[
  {
    "path": ".gitattributes",
    "content": "# Set default behaviour, in case users don't have core.autocrlf set.\n* text=auto\n\n# Explicitly declare text files we want to always be normalized and converted\n# to native line endings on checkout.\n*.c text\n*.h text\n*.java text\n*.xml text\n*.gradle text\n*.fodp text diff=xml\n*.properties text\n*.css text\n*.js text\n*.sql text\n*.txt text\n*.xsl text\n*.tex text\n*.markdown text\n*.md text\n\n# Declare files that will always have CRLF line endings on checkout.\n*.sln text eol=crlf\n\n# Denote all files that are truly binary and should not be modified.\n*.png binary\n*.jpg binary\n*.gif binary\n"
  },
  {
    "path": ".gitignore",
    "content": "*~\n*.bak\n*.class\n*.iml\n*.swp\n*-tmp.html\n.project\n.classpath\n.settings\ngit-modified-list.txt\nnbbuild.xml\nproperties/\n.METADATA\n.DS_Store\n.directory \n.gradle\n.nb-gradle\n.vscode\n\nnode_modules/\n\n/*.ipr\n/*.iws\n/*.patch\nbuild.*.properties\n\nbuild/\nclasses/\ndist/\n**nbproject**\n.ivy/\n.sass-cache/\n.DS_Store/\n*#\n/develop/tutorials/code/osgi/modules/form-nav-extension-portlet/.nb-gradle/\n/develop/tutorials/code/osgi/modules/com.liferay.docs.greetingimpl/.nb-gradle/\n/develop/tutorials/code/osgi/modules/com.liferay.docs.greetingapi/.nb-gradle/\n/develop/tutorials/code/osgi/modules/com.liferay.docs.greetingcommand/.nb-gradle/\n**/temp/**\n/develop/tutorials/code/com-liferay-docs-guestbook/.nb-gradle/\n"
  },
  {
    "path": "README.markdown",
    "content": "# LIFERAY-DOCS\n\n<a href=\"https://dev.liferay.com\" >\n<img src=\"guidelines/images/liferayDeveloperNetworkSmallerEdit7.jpg\" alt=\"Liferay Docs Icon\">\n</a>\n\n[![Slack Status](https://community-chat.liferay.com/badge.svg)](https://liferay-community.slack.com/messages/C5XE4BH3Q/)\n\nWelcome to Liferay's official documentation project, the home of\n[Liferay Developer Network](https://dev.liferay.com) articles. All articles are\nwritten in\n<a href=\"http://fletcher.github.com/peg-multimarkdown/mmd-manual.pdf\" target=\"_blank\">\nMarkdown</a>, making them easy to write and read. Approved articles are\nuploaded to the Liferay Developer Network (LDN) and converted automatically to\nHTML. In this project, you can contribute new articles, improve existing\narticles, or fix documentation bugs. To produce documentation that is\ncomprehensive and stylistically consistent, the liferay-docs project provides\n<a href=\"guidelines/03-writers-guidelines.markdown\" target=\"_blank\">writing guidelines</a>,\n<a href=\"guidelines/02-standards-and-customizations.markdown\" target=\"_blank\">standards & customizations</a>,\nand a\n<a href=\"develop/tutorial-template.markdown\" target=\"_blank\">tutorial template</a>.\n\nYou'll learn how quickly to submit a new article and its images next. \n\n## Quick Steps to Submit a New Article\n\nYou can follow these steps to create a new article and contribute it from \nGitHub. \n\n1.  Sign in to GitHub. If you don't already have a GitHub account, you must\n    [join](https://github.com/join) GitHub in order to contribute to\n    liferay-docs. \n\n2.  Click on a `new-articles` link folder below. These match the LDN sections\n    where you can add a articles:\n\n    - <a href=\"develop/new-articles\" target=\"_blank\">`develop/new-articles`</a>\n\n    - <a href=\"discover/new-articles\" target=\"_blank\">`discover/new-articles`</a>\n\n    - <a href=\"distribute/new-articles\" target=\"_blank\">`distribute/new-articles`</a>\n\n3.  Select the liferay-docs branch that matches the Liferay Portal version\n    you're writing about: \n\n    <table border=\"1\">\n    <tr>\n    <td><b>&nbsp;Branch&nbsp;</b></td>\n    <td><b>&nbsp;Portal Version&nbsp;</b></td>\n    </tr>\n    <tr>\n    <td>&nbsp;master&nbsp;</td>\n    <td>&nbsp;7.2</td>\n    </tr>\n    <tr>\n    <td>&nbsp;7.1.x&nbsp;</td>\n    <td>&nbsp;7.1</td>\n    </tr>\n    <tr>\n    <td>&nbsp;7.0.x&nbsp;</td>\n    <td>&nbsp;7.0</td>\n    </tr>\n    <tr>\n    <td>&nbsp;6.2.x</td>\n    <td>&nbsp;6.2</td>\n    </tr>\n    </table>\n\n4.  Click the **plus** sign after **new-articles/:**\n    ![new articles folder](guidelines/images/new-articles-folder.png)\n\n5.  Write your article and click the\n    ![Propose new file](guidelines/images/propose-new-file.png) button, to\n    prepare a [pull request](https://help.github.com/articles/using-pull-requests/). \n\n6.  Send the pull request to the default user `liferay`. \n\n7.  Drag your article's images into the pull request's comments to associate\n    the images with your article. \n\nYour new article is submitted! Liferay's Knowledge Management team will review\nyour contribution. Approved changes are merged into the liferay-docs repo and\npublished to the [Liferay Developer Network](https://dev.liferay.com). \n\nIf you want to clone our repository and do serious documentation work on your\nown machine, [click here](guidelines/01-creating-docs-for-liferay.markdown) to go\nto our guidelines section. \n\nThanks for helping us out with Liferay documentation! \n\n## Redirect instructions\n\n```\ncd liferay-docs\n./bin/update_liferay_learn_links.sh path/to/properties/file\n```\n\nFor example, \n\n```bash\ncd liferay-learn\ncat ./site/docs/redirects_keep.properties ./site/docs/redirects_new.properties > ~/redirects.properties\ncd ../liferay-docs\n./bin/update_liferay_learn_links.sh ~/redirects.properties\n```\n"
  },
  {
    "path": "bin/convert-markdown.sh",
    "content": "#!/bin/sh\n\nscriptPath=$(readlink -f \"$0\")\n\nclasspathDir=$(dirname $(dirname \"$scriptPath\"))/lib\nhtmlFile=$1\nhtmlFile=$(echo \"${htmlFile}\" | sed 's/[^.]*$//')\nhtmlFile=${htmlFile}html\n#echo \"java -cp \"${classpathDir}/*\" com.liferay.documentation.util.MarkdownToHtml $1 ${htmlFile}\"\n#java -cp \"${classpathDir}/*\" com.liferay.documentation.util.MarkdownToHtml $1 ${htmlFile}\n# echo \"java -cp \"${classpathDir}/*\" com.liferay.knowledge.base.markdown.converter.cli.MarkdownConverterCLI $1 > ${htmlFile}\"\njava -cp \"${classpathDir}/*\" com.liferay.knowledge.base.markdown.converter.cli.MarkdownConverterCLI /dev/stdin \nexit 0\n"
  },
  {
    "path": "bin/convert.bat",
    "content": "@echo off\n\nset CLASSPATH=%~dp0\\..\\lib\n\nif \"%1\"==\"\" goto usage\nif \"%2\"==\"\" goto convert\n\njava -cp .;%CLASSPATH%\\* com.liferay.knowledge.base.markdown.converter.cli.MarkdownConverterCLI %1 > %2\n\ngoto end\n\n:usage\n\necho Usage: convert.bat [Markdown file to convert] or convert.bat [Markdown file to convert] [HTML file to be written]\necho.\necho Run the convert.bat script from any directory.\necho.\necho The first argument is the path to the Markdown file to convert to HTML.\necho.\necho The second argument is optional. It specifies the path to the HTML file to be created. If this argument is omitted, the HTML file to be created will be created in the same directory as the Markdown file and will have the same filename as the Markdown file except that the .markdown file extension will be replaced by the .html file extension.\necho.\ngoto end\n\n:convert\n\nset htmlFile=%1\nset htmlFile=%htmlFile:markdown=html%\n\njava -cp .;%CLASSPATH%\\* com.liferay.knowledge.base.markdown.converter.cli.MarkdownConverterCLI %1 > %htmlFile%\n\n:end\n"
  },
  {
    "path": "bin/convert.sh",
    "content": "#!/bin/sh\n\nscriptPath=$(readlink -f \"$0\")\n\nclasspathDir=$(dirname $(dirname \"$scriptPath\"))/lib\n\nif [ $# -lt 1 ]; then\n\techo\n\techo Usage: ./convert.sh \\[Markdown file to convert\\] or ./convert.sh \\[Markdown file to convert\\] \\[HTML file to be written\\]\n\techo\n\techo Run the convert.sh script from the liferay-docs directory or any subdirectory.\n\techo\n\techo The first argument is the path to the Markdown file to convert to HTML.\n\techo\n\techo The second argument is optional. It specifies the path to the HTML file to be created. If this argument is omitted, the HTML file to be created is created in the same directory as the Markdown file and has the same filename as the Markdown file except that the .markdown file extension is replaced by the .html file extension.  \n\techo\n\texit 1\nelif [ $# -lt 2 ]; then\n\thtmlFile=$1\n\thtmlFile=$(echo \"${htmlFile}\" | sed 's/[^.]*$//')\n\thtmlFile=${htmlFile}html\n\t#echo \"java -cp \"${classpathDir}/*\" com.liferay.documentation.util.MarkdownToHtml $1 ${htmlFile}\"\n\t#java -cp \"${classpathDir}/*\" com.liferay.documentation.util.MarkdownToHtml $1 ${htmlFile}\n\techo \"java -cp \"${classpathDir}/*\" com.liferay.knowledge.base.markdown.converter.cli.MarkdownConverterCLI $1 > ${htmlFile}\"\n\tjava -cp \"${classpathDir}/*\" com.liferay.knowledge.base.markdown.converter.cli.MarkdownConverterCLI $1 > ${htmlFile}\n\texit 0\nelse\n\tjava -cp \"${classpathDir}/*\" com.liferay.documentation.util.MarkdownToHtml $1 > $2\n\texit 0\nfi\n"
  },
  {
    "path": "bin/migrate.py",
    "content": "import os\nimport re\nimport shutil\nimport sys\n\n\ndef append_to_line(line, new_line):\n    line = line.replace(\"\\r\",\"\")\n    line = line.replace(\"\\n\", \"\")\n    line = line + \" \" + new_line.lstrip()\n    return line\n\ndef end_sidebar(sidebar_line, newFile):\n    if sidebar_line not in \"\":\n        sidebar_line = sidebar_line.replace(\"| \", \"\")\n        newFile.write(sidebar_line)\n        newFile.write(\"```\\n\")\n        sidebar_line = \"\"\n    return sidebar_line\n\ndef write_and_reset_string(line, newFile):\n    if line not in \"\":\n        newFile.write(line)\n        line = \"\"\n\n    return line\n\n\nif __name__ == \"__main__\":\n\n    if (len(sys.argv) < 2):\n        print(\"Usage:\\n\\tpython migrate.py article [dest_folder]\\n\\nDescription:\\n\\tMigrates the article to an .md file and converts the content to liferay-learn Markdown/MyST syntax. The converted article destination folder [dest_folder] is the current folder by default but is typically set to a liferay-learn folder.\\n\\n\\tIMPORTANT: Run this script in the liferay-docs article's folder so that the script can use the image file paths found in the article to copy the image files to the [dest_folder]/[article_name]/images/ folder.\")\n        sys.exit()\n\n    article = sys.argv[1]\n\n    dest_folder = \".\"\n    if (len(sys.argv)) > 2:\n        dest_folder = sys.argv[2]\n\n    if not (os.path.isdir(dest_folder)):\n        os.mkdir(dest_folder)\n\n    article_name = article.split('.markdown')[0]\n\n    new_article_path = dest_folder + \"/\" + article_name + \".md\"\n\n    # Copy referenced image files to [article destination folder]/[article_name]/images\n\n    file = open(article)\n    content = file.read()\n    file.close()\n\n    png_split = content.split('.png)')\n\n    images = []\n\n    png_split_len = len(png_split) - 1\n    ii = 0;\n\n    while (ii < png_split_len):\n        paran_split = png_split[ii].split('(')\n\n        if (len(paran_split) > 1):\n            image = paran_split[len(paran_split) - 1] + '.png'\n            images.append(image)\n\n        ii = ii + 1\n\n    article_name_folder = dest_folder + \"/\" + article_name\n    images_folder = article_name_folder + \"/images\"\n\n    if (len(images) > 0):\n\n        if not os.path.isdir(article_name_folder):\n            os.mkdir(article_name_folder)\n\n        if not os.path.isdir(images_folder):\n            os.mkdir(images_folder)\n\n        print(\"Writing images to folder: \" + images_folder)\n\n    for ff in images:\n        shutil.copy(ff, images_folder)\n\n    # Process the text\n\n    file = open(article)\n    lines = file.readlines()\n    file.close()\n\n    # Open the destination file for writing to\n    newFile = open(new_article_path, 'w')\n\n    print(\"Migrating to article: \" + new_article_path)\n\n    # Track the last significant line's first non-space character column index\n    prev_index = -1\n\n    # Variables for storing text that belongs on the same line; hard returns are\n    # removed\n    list_item_line = \"\"\n    para_line = \"\"\n    sidebar_line = \"\"\n\n    # Variables that allow you to skip more detailed condition checks\n    done_header_id = False\n    done_title = False\n    done_toc = False\n    prev_sidebar_line_empty = False\n\n    # Variables for markup context\n    in_code = False\n    in_header_id = False\n\n    images_folder_path = \"](./\" + article_name + \"/images/\"\n\n    for line in lines:\n\n        # Set all image file locations to the ./[article_name]/images/ folder\n        line = re.sub(\"\\]\\((../)+images/\", images_folder_path, line)\n\n        # Replace legacy tokens\n        line = re.sub(\"@product@\", \"Liferay DXP\", line)\n        line = re.sub(\"@commerce@\", \"Liferay Commerce\", line)\n        line = re.sub(\"@ide@\", \"Dev Studio DXP\", line)\n        line = re.sub(\"@app-ref@\", \"https://docs.liferay.com/dxp/apps\", line)\n        line = re.sub(\"@platform-ref@\", \"https://docs.liferay.com/dxp/portal\", line)\n\n        # Convenience variable for working with the current line free of leading\n        # and trailing white space.\n        trimmed_line = line.lstrip()\n\n        if not done_header_id:\n            # Skip over all of the legacy header-id stuff\n\n            if trimmed_line.startswith(\"---\"):\n                if not in_header_id:\n                    in_header_id = True\n                else:\n                    done_header_id = True\n                    in_header_id = False\n\n            continue\n        if not done_title and trimmed_line in \"\":\n            # Skip empty lines until the title is written\n            continue\n        elif line.startswith(\"#\"):\n            # Handle heading\n            newFile.write(line)\n\n            if not done_title:\n                done_title = True\n        elif not done_toc and trimmed_line in \"\":\n            # Skip empty lines until TOC is done\n            continue\n        elif trimmed_line.startswith(\"```\"):\n            # Toggle whether we're in code\n\n            # Write any saved text\n            para_line = write_and_reset_string(para_line, newFile)\n            sidebar_line = end_sidebar(sidebar_line, newFile)\n            list_item_line = write_and_reset_string(list_item_line, newFile)\n\n            # Write the current line as is\n            newFile.write(line)\n\n            # Track whether code block has started or ended\n            if (in_code):\n                in_code = False\n            else:\n                in_code = True\n        elif re.search(\"^\\d.\", trimmed_line):\n            # Handle an ordered list item\n\n            # Write any saved text\n            para_line = write_and_reset_string(para_line, newFile)\n            sidebar_line = end_sidebar(sidebar_line, newFile)\n            list_item_line = write_and_reset_string(list_item_line, newFile)\n\n            # Start all ordered list items with 1.\n            list_item_line = re.sub(\"\\d+.\\s*\", \"1. \", line, 1)\n\n            prev_index = re.search(\"\\S\", line).start()\n        elif trimmed_line.startswith(\"-\"):\n            # Handle an unordered-list item starting with -\n\n            # Write any saved text\n            para_line = write_and_reset_string(para_line, newFile)\n            sidebar_line = end_sidebar(sidebar_line, newFile)\n            list_item_line = write_and_reset_string(list_item_line, newFile)\n\n            # Start list items with '*' instead of '-', followed by a single space\n            list_item_line = re.sub(\"-\\s*\", \"* \", line, 1)\n\n            prev_index = re.search(\"\\S\", line).start()\n        elif trimmed_line.startswith(\"* \"):\n            # Handle an unordered-list item starting with *\n\n            # Write any saved text\n            para_line = write_and_reset_string(para_line, newFile)\n            sidebar_line = end_sidebar(sidebar_line, newFile)\n            list_item_line = write_and_reset_string(list_item_line, newFile)\n\n            # Start list items with '*', followed by a single space\n            list_item_line = re.sub(\"\\*\\s*\", \"* \", line, 1)\n\n            prev_index = re.search(\"\\S\", line).start()\n        elif re.search(\"^\\|\", line.strip()):\n\n            if not re.search(\"^\\|.*\\|$\", line.strip()):\n\n                # Handle a sidebar line\n\n                # Write any saved text\n                para_line = write_and_reset_string(para_line, newFile)\n                list_item_line = write_and_reset_string(list_item_line, newFile)\n\n                pipe_index = re.search(\"|\", line).start()\n\n                tmp_line = line.replace(\"| \", \"\", 1).strip()\n                if tmp_line in \"\":\n                    prev_sidebar_line_empty = True\n                    sidebar_line += \"\\n\"\n                else:\n\n                    # Replace the leading pipe with a space\n                    stripped_line = line.replace(\"| \", \"\", 1)\n\n                    if sidebar_line in \"\":\n\n                        # Start the sidebar\n\n                        if \"**Note:**\" in stripped_line:\n                            newFile.write(\"```{note}\\n\")\n                            stripped_line = stripped_line.replace(\"**Note:**\", \"\", 1)\n                        elif \"**Tip:**\" in stripped_line:\n                            newFile.write(\"```{tip}\\n\")\n                            stripped_line = stripped_line.replace(\"**Tip:**\", \"\", 1)\n                        elif \"**Warning:**\" in stripped_line:\n                            newFile.write(\"```{warning}\\n\")\n                            stripped_line = stripped_line.replace(\"**Warning:**\", \"\", 1)\n                        elif \"**Important:**\" in stripped_line:\n                            newFile.write(\"```{important}\\n\")\n                            stripped_line = stripped_line.replace(\"**Important:**\", \"\", 1)\n                        elif \"**Note**:\" in stripped_line:\n                            newFile.write(\"```{note}\\n\")\n                            stripped_line = stripped_line.replace(\"**Note**:\", \"\", 1)\n                        elif \"**Tip**:\" in stripped_line:\n                            newFile.write(\"```{tip}\\n\")\n                            stripped_line = stripped_line.replace(\"**Tip**:\", \"\", 1)\n                        elif \"**Warning**:\" in stripped_line:\n                            newFile.write(\"```{warning}\\n\")\n                            stripped_line = stripped_line.replace(\"**Warning**:\", \"\", 1)\n                        elif \"**Important**:\" in stripped_line:\n                            newFile.write(\"```{important}\\n\")\n                            stripped_line = stripped_line.replace(\"**Important**:\", \"\", 1)\n                        else:\n                            newFile.write(\"```{note}\\n\")\n\n                    if sidebar_line in \"\":\n                        sidebar_line = stripped_line.lstrip()\n                    else:\n                        if prev_sidebar_line_empty:\n                            sidebar_line = sidebar_line + stripped_line\n                            prev_sidebar_line_empty = False\n                        else:\n                            sidebar_line = sidebar_line.rstrip() + \" \" + stripped_line.lstrip()\n\n                    prev_index = re.search(\"\\S\", line).start()\n            else:\n                # Handle table line\n\n                para_line = write_and_reset_string(para_line, newFile)\n                sidebar_line = end_sidebar(sidebar_line, newFile)\n                list_item_line = write_and_reset_string(list_item_line, newFile)\n\n                # Write the table line\n                newFile.write(line)\n        elif in_code:\n\n            # Write code line\n            newFile.write(line)\n        elif trimmed_line.startswith(\"[TOC\"):\n            # Don't write anything for the legacy TOC line\n            done_toc = True\n            continue\n        elif para_line != \"\":\n\n            if re.search(\"^[\\d\\-]\", trimmed_line):\n                # Write the existing paragraph and start a list item\n                list_item_line = line\n\n                para_line = write_and_reset_string(para_line, newFile)\n\n                prev_index = re.search(\"\\S\", line).start()\n            elif re.search(\"^[\\:]\", trimmed_line):\n\n                # Write definition term\n                newFile.write(para_line)\n\n                # Start the term definition\n                para_line = line\n\n                prev_index = re.search(\"\\S\", line).start()\n            elif re.search(\"^[\\w\\*\\@\\!\\(\\&\\.\\[\\`\\s(\\w\\*\\@\\!\\(\\&\\.\\[\\`)]\", trimmed_line):\n\n                para_line = append_to_line(para_line, trimmed_line)\n            else:\n\n                # Write the existing paragraph and the current line\n                para_line = write_and_reset_string(para_line, newFile)\n                newFile.write(line)\n\n                if re.search(\"\\S\", line):\n                    prev_index = re.search(\"\\S\", line).start()\n        elif re.search(\"^[\\w\\*\\@\\!\\(\\&\\.\\[\\`\\s\\:(\\w\\*\\@\\!\\(\\&\\.\\[\\`)]\", trimmed_line):\n\n            sidebar_line = end_sidebar(sidebar_line, newFile)\n\n            if re.search(\"\\S\", line):\n                index = re.search(\"\\S\", line).start()\n\n                if index > prev_index:\n\n                    if list_item_line != \"\":\n                        list_item_line = append_to_line(list_item_line, line)\n                    elif (para_line != \"\"):\n                        para_line = append_to_line(para_line, line)\n                    else:\n                        para_line = line\n                        prev_index = index\n                else:\n                    para_line = write_and_reset_string(para_line, newFile)\n                    sidebar_line = end_sidebar(sidebar_line, newFile)\n                    list_item_line = write_and_reset_string(list_item_line, newFile)\n\n                    para_line = line\n                    prev_index = index\n            else:\n                # Empty line\n\n                para_line = write_and_reset_string(para_line, newFile)\n                sidebar_line = end_sidebar(sidebar_line, newFile)\n                list_item_line = write_and_reset_string(list_item_line, newFile)\n\n                # Write the current line\n                newFile.write(line)\n        else:\n\n            if not done_header_id and trimmed_line in \"\":\n                # Skip writing empty lines before writing the title line\n                continue\n            else:\n                # Set index to first non-space character\n                if re.search(\"\\S\", line):\n                    prev_index = re.search(\"\\S\", line).start()\n\n\n                para_line = write_and_reset_string(para_line, newFile)\n                sidebar_line = end_sidebar(sidebar_line, newFile)\n                list_item_line = write_and_reset_string(list_item_line, newFile)\n\n                # Write the current line\n                newFile.write(line)\n\n    # Done looping through the lines\n\n    # Finish writing the current paragraph, list item, or sidebar\n\n    if para_line != \"\":\n        newFile.write(para_line)\n    elif list_item_line != \"\":\n        newFile.write(list_item_line)\n    elif sidebar_line != \"\":\n        newFile.write(sidebar_line)\n\n    newFile.close()"
  },
  {
    "path": "bin/update-liferay-learn-links.sh",
    "content": "#!/bin/bash\n\n# This script was inspired by the answers at\n# https://stackoverflow.com/questions/30724170/how-to-read-properties-file-using-shell-script\n\nredirects_file=~/redirects_all.properties\n\nif [ \"${#}\" -eq 0 ]\nthen\n    echo \"Must input a properties file\"\n    echo \"Usage: ./bin/update-liferay-learn_links.sh path/to/properties/file\"\n    exit 1\nfi\n\nif [ \"${#}\" -ge 1 ]\nthen\n    if [ ! -f ${1} ]\n    then\n        echo \"File ${1} does not exist.\"\n\n        exit 1\n    else\n        redirects_file=${1}\n    fi\nfi\n\necho \"Redirects file ${redirects_file}\"\n\n# Read configuration into an associative array\ndeclare -A REDIRECT\n\n# IFS is the 'internal field separator'. In this case, your file uses '='\nIFS=\"=\"\nwhile read -r key value\ndo\n    if [ -n $value ]; then\n        REDIRECT[$key]=$value\n    else\n        REDIRECT[$key]=$value\n    fi\ndone < ${redirects_file}\nunset IFS\n\nfor file in `find . -type f -name \"*.markdown\"`\ndo\n    if [ -n \"$(grep learn.liferay.com $(echo ${file}))\" ]\n    then\n        echo \"${file}\"\n\n        for key in \"${!REDIRECT[@]}\"\n        do\n            sed -i s@$key@${REDIRECT[$key]}@g \"${file}\"\n        done\n    fi\ndone"
  },
  {
    "path": "bin/update-markdown-sidebar-syntax-win.sh",
    "content": "#!/bin/sh\n\ndevelopdir=\"../develop\"\ndistributedir=\"../distribute\"\nusedir=\"../use\"\n\nfor file in $(find $developdir -name \"*.markdown\")\ndo\nperl -p0777i.bak -e 's/\\r\\n[ \\t]*---[ ]*(\\r\\n)+[ ]*!\\[.*\\]\\(.*\\)[\\s]*/\\r\\n\\+\\$\\$\\$\\r\\n\\r\\n/g' $file\nperl -p0777i.bak -e 's/\\r\\n[ \\t]*---[ ]*(\\r\\n)/\\r\\n\\$\\$\\$\\r\\n/g' $file\ndone\n\nfor file in $(find $distributedir -name \"*.markdown\")\ndo\nperl -p0777i.bak -e 's/\\r\\n[ \\t]*---[ ]*(\\r\\n)+[ ]*!\\[.*\\]\\(.*\\)[\\s]*/\\r\\n\\+\\$\\$\\$\\r\\n\\r\\n/g' $file\nperl -p0777i.bak -e 's/\\r\\n[ \\t]*---[ ]*(\\r\\n)/\\r\\n\\$\\$\\$\\r\\n/g' $file\ndone\n\nfor file in $(find $usedir -name \"*.markdown\")\ndo\nperl -p0777i.bak -e 's/\\r\\n[ \\t]*---[ ]*(\\r\\n)+[ ]*!\\[.*\\]\\(.*\\)[\\s]*/\\r\\n\\+\\$\\$\\$\\r\\n\\r\\n/g' $file\nperl -p0777i.bak -e 's/\\r\\n[ \\t]*---[ ]*(\\r\\n)/\\r\\n\\$\\$\\$\\r\\n/g' $file\ndone\n"
  },
  {
    "path": "bin/update-markdown-sidebar-syntax.sh",
    "content": "#!/bin/sh\n\ndevelopdir=\"../develop\"\ndistributedir=\"../distribute\"\nusedir=\"../use\"\n\nfor file in $(find $developdir -name \"*.markdown\")\ndo\nperl -p0777i -e 's/\\n[ \\t]*---[ ]*\\n+[ ]*!\\[.*\\]\\(.*\\)[\\s]*/\\n\\+\\$\\$\\$\\n\\n/g' $file\nperl -p0777i -e 's/\\n[ \\t]*---[ ]*\\n/\\n\\$\\$\\$\\n/g' $file\ndone\n\nfor file in $(find $distributedir -name \"*.markdown\")\ndo\nperl -p0777i -e 's/\\n[ \\t]*---[ ]*\\n+[ ]*!\\[.*\\]\\(.*\\)[\\s]*/\\n\\+\\$\\$\\$\\n\\n/g' $file\nperl -p0777i -e 's/\\n[ \\t]*---[ ]*\\n/\\n\\$\\$\\$\\n/g' $file\ndone\n\nfor file in $(find $usedir -name \"*.markdown\")\ndo\nperl -p0777i -e 's/\\n[ \\t]*---[ ]*\\n+[ ]*!\\[.*\\]\\(.*\\)[\\s]*/\\n\\+\\$\\$\\$\\n\\n/g' $file\nperl -p0777i -e 's/\\n[ \\t]*---[ ]*\\n/\\n\\$\\$\\$\\n/g' $file\ndone\n"
  },
  {
    "path": "book/developer/appdev.aux",
    "content": "\\relax \n\\providecommand{\\transparent@use}[1]{}\n\\providecommand\\hyper@newdestlabel[2]{}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {229}Application Development}{661}{chapter.229}\\protected@file@percent }\n\\newlabel{application-development}{{229}{661}{Application Development}{chapter.229}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {229.1}Getting Started with Liferay Development}{662}{section.229.1}\\protected@file@percent }\n\\newlabel{getting-started-with-liferay-development}{{229.1}{662}{Getting Started with Liferay Development}{section.229.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {229.2}Create Your Object Model and Database in One Shot}{662}{section.229.2}\\protected@file@percent }\n\\newlabel{create-your-object-model-and-database-in-one-shot}{{229.2}{662}{Create Your Object Model and Database in One Shot}{section.229.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {229.3}Create a REST Interface}{663}{section.229.3}\\protected@file@percent }\n\\newlabel{create-a-rest-interface}{{229.3}{663}{Create a REST Interface}{section.229.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {229.4}Create a Web Client}{663}{section.229.4}\\protected@file@percent }\n\\newlabel{create-a-web-client}{{229.4}{663}{Create a Web Client}{section.229.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {229.5}Use Liferay's Frameworks}{663}{section.229.5}\\protected@file@percent }\n\\newlabel{use-liferays-frameworks}{{229.5}{663}{Use Liferay's Frameworks}{section.229.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {229.6}Next Steps}{663}{section.229.6}\\protected@file@percent }\n\\newlabel{next-steps}{{229.6}{663}{Next Steps}{section.229.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {230}Developing Web Front-Ends}{665}{chapter.230}\\protected@file@percent }\n\\newlabel{developing-web-front-ends}{{230}{665}{Developing Web Front-Ends}{chapter.230}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {230.1}Using Popular Frameworks}{665}{section.230.1}\\protected@file@percent }\n\\newlabel{using-popular-frameworks}{{230.1}{665}{Using Popular Frameworks}{section.230.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {230.2}Getting Started}{666}{section.230.2}\\protected@file@percent }\n\\newlabel{getting-started}{{230.2}{666}{Getting Started}{section.230.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {231}Developing an Angular Application}{669}{chapter.231}\\protected@file@percent }\n\\newlabel{developing-an-angular-application}{{231}{669}{Developing an Angular Application}{chapter.231}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {231.1}{\\ignorespaces Apps like this Guestbook app are easy to migrate to Liferay DXP.}}{669}{figure.231.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {231.1}Related Topics}{673}{section.231.1}\\protected@file@percent }\n\\newlabel{related-topics}{{231.1}{673}{Related Topics}{section.231.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {232}Developing a React Application}{675}{chapter.232}\\protected@file@percent }\n\\newlabel{developing-a-react-application}{{232}{675}{Developing a React Application}{chapter.232}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {232.1}{\\ignorespaces Apps like this Guestbook app are easy to migrate to Liferay DXP.}}{675}{figure.232.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {232.1}Related Topics}{678}{section.232.1}\\protected@file@percent }\n\\newlabel{related-topics-1}{{232.1}{678}{Related Topics}{section.232.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {233}Developing a Vue Application}{679}{chapter.233}\\protected@file@percent }\n\\newlabel{developing-a-vue-application}{{233}{679}{Developing a Vue Application}{chapter.233}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {233.1}{\\ignorespaces Vue Apps like this Guestbook App are easy to deploy, and they look great in Liferay DXP.}}{680}{figure.233.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {233.1}Related Topics}{683}{section.233.1}\\protected@file@percent }\n\\newlabel{related-topics-2}{{233.1}{683}{Related Topics}{section.233.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {234}Liferay MVC Portlet}{685}{chapter.234}\\protected@file@percent }\n\\newlabel{liferay-mvc-portlet}{{234}{685}{Liferay MVC Portlet}{chapter.234}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {234.1}MVC Layers and Modularity}{685}{section.234.1}\\protected@file@percent }\n\\newlabel{mvc-layers-and-modularity}{{234.1}{685}{MVC Layers and Modularity}{section.234.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {234.2}Liferay MVC Command Classes}{686}{section.234.2}\\protected@file@percent }\n\\newlabel{liferay-mvc-command-classes}{{234.2}{686}{Liferay MVC Command Classes}{section.234.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {234.3}Liferay MVC Portlet Component}{686}{section.234.3}\\protected@file@percent }\n\\newlabel{liferay-mvc-portlet-component}{{234.3}{686}{Liferay MVC Portlet Component}{section.234.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {234.4}A Simpler MVC Portlet}{687}{section.234.4}\\protected@file@percent }\n\\newlabel{a-simpler-mvc-portlet}{{234.4}{687}{A Simpler MVC Portlet}{section.234.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {235}Creating an MVC Portlet}{689}{chapter.235}\\protected@file@percent }\n\\newlabel{creating-an-mvc-portlet}{{235}{689}{Creating an MVC Portlet}{chapter.235}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {235.1}{\\ignorespaces The example portlet shows a message defined by the language property \\texttt  {yourmvc.caption=Hello\\ from\\ YourMVC!} in the Language.properties file.}}{691}{figure.235.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {235.1}Related Topics}{691}{section.235.1}\\protected@file@percent }\n\\newlabel{related-topics-3}{{235.1}{691}{Related Topics}{section.235.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {236}Writing MVC Portlet Controller Code}{693}{chapter.236}\\protected@file@percent }\n\\newlabel{writing-mvc-portlet-controller-code}{{236}{693}{Writing MVC Portlet Controller Code}{chapter.236}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {236.1}Action Methods}{693}{section.236.1}\\protected@file@percent }\n\\newlabel{action-methods}{{236.1}{693}{Action Methods}{section.236.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {236.2}Render Logic}{694}{section.236.2}\\protected@file@percent }\n\\newlabel{render-logic}{{236.2}{694}{Render Logic}{section.236.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {236.3}Setting and Retrieving Request and Response Parameters and Attributes}{695}{section.236.3}\\protected@file@percent }\n\\newlabel{setting-and-retrieving-request-and-response-parameters-and-attributes}{{236.3}{695}{Setting and Retrieving Request and Response Parameters and Attributes}{section.236.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {236.4}Related Topics}{696}{section.236.4}\\protected@file@percent }\n\\newlabel{related-topics-4}{{236.4}{696}{Related Topics}{section.236.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {237}Configuring the View Layer}{697}{chapter.237}\\protected@file@percent }\n\\newlabel{configuring-the-view-layer}{{237}{697}{Configuring the View Layer}{chapter.237}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {237.1}Using the init.jsp}{697}{section.237.1}\\protected@file@percent }\n\\newlabel{using-the-init.jsp}{{237.1}{697}{Using the init.jsp}{section.237.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {237.2}Using Render URLs}{698}{section.237.2}\\protected@file@percent }\n\\newlabel{using-render-urls}{{237.2}{698}{Using Render URLs}{section.237.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {237.3}Using Action URLs}{699}{section.237.3}\\protected@file@percent }\n\\newlabel{using-action-urls}{{237.3}{699}{Using Action URLs}{section.237.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {237.4}Related Topics}{699}{section.237.4}\\protected@file@percent }\n\\newlabel{related-topics-5}{{237.4}{699}{Related Topics}{section.237.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {238}MVC Action Command}{701}{chapter.238}\\protected@file@percent }\n\\newlabel{mvc-action-command}{{238}{701}{MVC Action Command}{chapter.238}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {238.1}Related Topics}{703}{section.238.1}\\protected@file@percent }\n\\newlabel{related-topics-6}{{238.1}{703}{Related Topics}{section.238.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {239}MVC Render Command}{705}{chapter.239}\\protected@file@percent }\n\\newlabel{mvc-render-command}{{239}{705}{MVC Render Command}{chapter.239}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {239.1}Related Topics}{706}{section.239.1}\\protected@file@percent }\n\\newlabel{related-topics-7}{{239.1}{706}{Related Topics}{section.239.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {240}MVC Resource Command}{707}{chapter.240}\\protected@file@percent }\n\\newlabel{mvc-resource-command}{{240}{707}{MVC Resource Command}{chapter.240}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {240.1}Related Topics}{709}{section.240.1}\\protected@file@percent }\n\\newlabel{related-topics-8}{{240.1}{709}{Related Topics}{section.240.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {241}PortletMVC4Spring}{711}{chapter.241}\\protected@file@percent }\n\\newlabel{portletmvc4spring}{{241}{711}{PortletMVC4Spring}{chapter.241}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {241.1}{\\ignorespaces This PortletMVC4Spring portlet enables users to enter job applications. It uses the Spring features mentioned above and handles requests from multiple portlet phases.}}{712}{figure.241.1}\\protected@file@percent }\n\\gdef \\LT@xii {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {242}Developing a Portlet Using PortletMVC4Spring}{715}{chapter.242}\\protected@file@percent }\n\\newlabel{developing-a-portlet-using-portletmvc4spring}{{242}{715}{Developing a Portlet Using PortletMVC4Spring}{chapter.242}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {242.1}{\\ignorespaces The archetype's sample portlet prints a greeting (e.g., \\emph  {Hello, Joe Bloggs}) on submitting a first and last name.}}{715}{figure.242.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {242.1}Related Topics}{719}{section.242.1}\\protected@file@percent }\n\\newlabel{related-topics-9}{{242.1}{719}{Related Topics}{section.242.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {243}Migrating to PortletMVC4Spring}{721}{chapter.243}\\protected@file@percent }\n\\newlabel{migrating-to-portletmvc4spring}{{243}{721}{Migrating to PortletMVC4Spring}{chapter.243}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {243.1}Related Topics}{722}{section.243.1}\\protected@file@percent }\n\\newlabel{related-topics-10}{{243.1}{722}{Related Topics}{section.243.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {244}JSF Portlet}{723}{chapter.244}\\protected@file@percent }\n\\newlabel{jsf-portlet}{{244}{723}{JSF Portlet}{chapter.244}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {245}Developing a JSF Portlet Application}{725}{chapter.245}\\protected@file@percent }\n\\newlabel{developing-a-jsf-portlet-application}{{245}{725}{Developing a JSF Portlet Application}{chapter.245}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {246}Bean Portlet}{729}{chapter.246}\\protected@file@percent }\n\\newlabel{bean-portlet}{{246}{729}{Bean Portlet}{chapter.246}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {246.1}Portlet Configuration Annotations}{729}{section.246.1}\\protected@file@percent }\n\\newlabel{portlet-configuration-annotations}{{246.1}{729}{Portlet Configuration Annotations}{section.246.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {246.2}Dependency Injection}{730}{section.246.2}\\protected@file@percent }\n\\newlabel{dependency-injection}{{246.2}{730}{Dependency Injection}{section.246.2}{}}\n\\gdef \\LT@xiii {\\LT@entry \n    {1}{287.70345pt}\\LT@entry \n    {1}{182.05156pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {246.3}Portlet Phase Methods}{731}{section.246.3}\\protected@file@percent }\n\\newlabel{portlet-phase-methods}{{246.3}{731}{Portlet Phase Methods}{section.246.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {247}Creating a Bean Portlet}{733}{chapter.247}\\protected@file@percent }\n\\newlabel{creating-a-bean-portlet}{{247}{733}{Creating a Bean Portlet}{chapter.247}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {247.1}{\\ignorespaces The Foo portlet prints the message returned from \\texttt  {doView} method and shows the included JSP's contents.}}{735}{figure.247.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.1}Related Topics}{735}{section.247.1}\\protected@file@percent }\n\\newlabel{related-topics-11}{{247.1}{735}{Related Topics}{section.247.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {248}Service Builder}{737}{chapter.248}\\protected@file@percent }\n\\newlabel{service-builder}{{248}{737}{Service Builder}{chapter.248}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {248.1}Customization via Implementation Classes}{738}{section.248.1}\\protected@file@percent }\n\\newlabel{customization-via-implementation-classes}{{248.1}{738}{Customization via Implementation Classes}{section.248.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {248.2}Hibernate Configurations}{738}{section.248.2}\\protected@file@percent }\n\\newlabel{hibernate-configurations}{{248.2}{738}{Hibernate Configurations}{section.248.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {248.3}Caching}{738}{section.248.3}\\protected@file@percent }\n\\newlabel{caching}{{248.3}{738}{Caching}{section.248.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {248.4}Dynamic Query and Custom SQL Query}{738}{section.248.4}\\protected@file@percent }\n\\newlabel{dynamic-query-and-custom-sql-query}{{248.4}{738}{Dynamic Query and Custom SQL Query}{section.248.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {249}Creating a Service Builder Project}{741}{chapter.249}\\protected@file@percent }\n\\newlabel{creating-a-service-builder-project}{{249}{741}{Creating a Service Builder Project}{chapter.249}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {249.1}Related Topics}{742}{section.249.1}\\protected@file@percent }\n\\newlabel{related-topics-12}{{249.1}{742}{Related Topics}{section.249.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {250}Creating the service.xml File}{743}{chapter.250}\\protected@file@percent }\n\\newlabel{creating-the-service.xml-file}{{250}{743}{Creating the service.xml File}{chapter.250}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {251}Defining Global Service Information}{745}{chapter.251}\\protected@file@percent }\n\\newlabel{defining-global-service-information}{{251}{745}{Defining Global Service Information}{chapter.251}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {251.1}Dependency Injector}{745}{section.251.1}\\protected@file@percent }\n\\newlabel{dependency-injector}{{251.1}{745}{Dependency Injector}{section.251.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {251.2}Package Path}{746}{section.251.2}\\protected@file@percent }\n\\newlabel{package-path}{{251.2}{746}{Package Path}{section.251.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {251.3}Multiversion concurrency control (MVCC)}{746}{section.251.3}\\protected@file@percent }\n\\newlabel{multiversion-concurrency-control-mvcc}{{251.3}{746}{Multiversion concurrency control (MVCC)}{section.251.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {251.4}Namespace Options}{747}{section.251.4}\\protected@file@percent }\n\\newlabel{namespace-options}{{251.4}{747}{Namespace Options}{section.251.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {251.5}Author}{747}{section.251.5}\\protected@file@percent }\n\\newlabel{author}{{251.5}{747}{Author}{section.251.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {252}Defining Service Entities}{749}{chapter.252}\\protected@file@percent }\n\\newlabel{defining-service-entities}{{252}{749}{Defining Service Entities}{chapter.252}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {253}Defining the Columns (Attributes) for Each Service Entity}{751}{chapter.253}\\protected@file@percent }\n\\newlabel{defining-the-columns-attributes-for-each-service-entity}{{253}{751}{Defining the Columns (Attributes) for Each Service Entity}{chapter.253}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {253.1}Create Entity Columns}{751}{section.253.1}\\protected@file@percent }\n\\newlabel{create-entity-columns}{{253.1}{751}{Create Entity Columns}{section.253.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {253.2}Support Multi-tenancy}{752}{section.253.2}\\protected@file@percent }\n\\newlabel{support-multi-tenancy}{{253.2}{752}{Support Multi-tenancy}{section.253.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {253.3}Workflow Fields}{752}{section.253.3}\\protected@file@percent }\n\\newlabel{workflow-fields}{{253.3}{752}{Workflow Fields}{section.253.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {253.4}Audit Entities}{752}{section.253.4}\\protected@file@percent }\n\\newlabel{audit-entities}{{253.4}{752}{Audit Entities}{section.253.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {254}Defining Relationships Between Service Entities}{753}{chapter.254}\\protected@file@percent }\n\\newlabel{defining-relationships-between-service-entities}{{254}{753}{Defining Relationships Between Service Entities}{chapter.254}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {254.1}{\\ignorespaces Relating entities is a snap in Liferay Dev Studio DXP's \\emph  {Diagram} mode for \\texttt  {service.xml}.}}{754}{figure.254.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {255}Defining Ordering of Service Entity Instances}{755}{chapter.255}\\protected@file@percent }\n\\newlabel{defining-ordering-of-service-entity-instances}{{255}{755}{Defining Ordering of Service Entity Instances}{chapter.255}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {256}Defining Service Entity Finder Methods}{757}{chapter.256}\\protected@file@percent }\n\\newlabel{defining-service-entity-finder-methods}{{256}{757}{Defining Service Entity Finder Methods}{chapter.256}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {256.1}Creating Finders}{757}{section.256.1}\\protected@file@percent }\n\\newlabel{creating-finders}{{256.1}{757}{Creating Finders}{section.256.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {257}Running Service Builder}{759}{chapter.257}\\protected@file@percent }\n\\newlabel{running-service-builder}{{257}{759}{Running Service Builder}{chapter.257}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {257.1}Gradle}{759}{section.257.1}\\protected@file@percent }\n\\newlabel{gradle}{{257.1}{759}{Gradle}{section.257.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {257.2}Maven}{759}{section.257.2}\\protected@file@percent }\n\\newlabel{maven}{{257.2}{759}{Maven}{section.257.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {258}Understanding the Code Generated by Service Builder}{761}{chapter.258}\\protected@file@percent }\n\\newlabel{understanding-the-code-generated-by-service-builder}{{258}{761}{Understanding the Code Generated by Service Builder}{chapter.258}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {258.1}{\\ignorespaces Service Builder generates these persistence classes and interfaces for an example entity called \\emph  {Event}. You shouldn't (and you won't need to) customize any of these classes or interfaces.}}{762}{figure.258.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {258.2}{\\ignorespaces Service Builder generates these service classes and interfaces. Only the {[}ENTITY\\_NAME{]}LocalServiceImpl (e.g., EventLocalServiceImpl for the Event entity) allows custom methods to be added to the service layer.}}{763}{figure.258.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {258.3}{\\ignorespaces Service Builder generates these model classes and interfaces. Only \\texttt  {{[}ENTITY\\_NAME{]}Impl} (e.g., \\texttt  {EventImpl} for the Event entity) allows custom methods to be added to the service layer.}}{765}{figure.258.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {259}Iterative Development}{767}{chapter.259}\\protected@file@percent }\n\\newlabel{iterative-development}{{259}{767}{Iterative Development}{chapter.259}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {259.1}Related Topics}{767}{section.259.1}\\protected@file@percent }\n\\newlabel{related-topics-13}{{259.1}{767}{Related Topics}{section.259.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {260}Customizing Model Entities With Model Hints}{769}{chapter.260}\\protected@file@percent }\n\\newlabel{customizing-model-entities-with-model-hints}{{260}{769}{Customizing Model Entities With Model Hints}{chapter.260}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {260.1}Model Hint Types}{771}{section.260.1}\\protected@file@percent }\n\\newlabel{model-hint-types}{{260.1}{771}{Model Hint Types}{section.260.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {260.2}Default Hints}{771}{section.260.2}\\protected@file@percent }\n\\newlabel{default-hints}{{260.2}{771}{Default Hints}{section.260.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {260.3}Hint Collections}{772}{section.260.3}\\protected@file@percent }\n\\newlabel{hint-collections}{{260.3}{772}{Hint Collections}{section.260.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {261}Configuring service.properties}{775}{chapter.261}\\protected@file@percent }\n\\newlabel{configuring-service.properties}{{261}{775}{Configuring service.properties}{chapter.261}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {261.1}Related Topics}{775}{section.261.1}\\protected@file@percent }\n\\newlabel{related-topics-14}{{261.1}{775}{Related Topics}{section.261.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {262}Connecting Service Builder to an External Database}{777}{chapter.262}\\protected@file@percent }\n\\newlabel{connecting-service-builder-to-an-external-database}{{262}{777}{Connecting Service Builder to an External Database}{chapter.262}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {263}Connecting the Data Source Using a DataSourceProvider}{779}{chapter.263}\\protected@file@percent }\n\\newlabel{connecting-the-data-source-using-a-datasourceprovider}{{263}{779}{Connecting the Data Source Using a DataSourceProvider}{chapter.263}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {263.1}Related Topics}{781}{section.263.1}\\protected@file@percent }\n\\newlabel{related-topics-15}{{263.1}{781}{Related Topics}{section.263.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {264}Connecting the Data Source Using Spring Beans}{783}{chapter.264}\\protected@file@percent }\n\\newlabel{connecting-the-data-source-using-spring-beans}{{264}{783}{Connecting the Data Source Using Spring Beans}{chapter.264}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.1}Specify Your Database and a Data Source Name in Your \\texttt  {service.xml}}{784}{section.264.1}\\protected@file@percent }\n\\newlabel{specify-your-database-and-a-data-source-name-in-your-service.xml}{{264.1}{784}{\\texorpdfstring {Specify Your Database and a Data Source Name in Your \\texttt {service.xml}}{Specify Your Database and a Data Source Name in Your service.xml}}{section.264.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.2}Create the Database Manually}{784}{section.264.2}\\protected@file@percent }\n\\newlabel{create-the-database-manually}{{264.2}{784}{Create the Database Manually}{section.264.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.3}Define the Data Source}{784}{section.264.3}\\protected@file@percent }\n\\newlabel{define-the-data-source}{{264.3}{784}{Define the Data Source}{section.264.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.4}Connect Your Service Builder Module to the Data Source Via a Spring Bean}{784}{section.264.4}\\protected@file@percent }\n\\newlabel{connect-your-service-builder-module-to-the-data-source-via-a-spring-bean}{{264.4}{784}{Connect Your Service Builder Module to the Data Source Via a Spring Bean}{section.264.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.5}Run Service Builder}{786}{section.264.5}\\protected@file@percent }\n\\newlabel{run-service-builder}{{264.5}{786}{Run Service Builder}{section.264.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.6}Related Topics}{786}{section.264.6}\\protected@file@percent }\n\\newlabel{related-topics-16}{{264.6}{786}{Related Topics}{section.264.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {265}Migrating a Service Builder Module from Spring DI to OSGi DS}{787}{chapter.265}\\protected@file@percent }\n\\newlabel{migrating-a-service-builder-module-from-spring-di-to-osgi-ds}{{265}{787}{Migrating a Service Builder Module from Spring DI to OSGi DS}{chapter.265}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.1}Step 1: Prepare Your Project for DS}{787}{section.265.1}\\protected@file@percent }\n\\newlabel{step-1-prepare-your-project-for-ds}{{265.1}{787}{Step 1: Prepare Your Project for DS}{section.265.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.2}Step 2: Update Your Spring Bean Classes}{788}{section.265.2}\\protected@file@percent }\n\\newlabel{step-2-update-your-spring-bean-classes}{{265.2}{788}{Step 2: Update Your Spring Bean Classes}{section.265.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.3}Step 3: Resolve Any Circular Dependencies}{789}{section.265.3}\\protected@file@percent }\n\\newlabel{step-3-resolve-any-circular-dependencies}{{265.3}{789}{Step 3: Resolve Any Circular Dependencies}{section.265.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.4}Related Topics}{790}{section.265.4}\\protected@file@percent }\n\\newlabel{related-topics-17}{{265.4}{790}{Related Topics}{section.265.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {266}Business Logic with Service Builder}{791}{chapter.266}\\protected@file@percent }\n\\newlabel{business-logic-with-service-builder}{{266}{791}{Business Logic with Service Builder}{chapter.266}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {267}Implementing an Add Method}{793}{chapter.267}\\protected@file@percent }\n\\newlabel{implementing-an-add-method}{{267}{793}{Implementing an Add Method}{chapter.267}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {267.1}Step 1: Declare an add method with parameters for creating the entity}{794}{section.267.1}\\protected@file@percent }\n\\newlabel{step-1-declare-an-add-method-with-parameters-for-creating-the-entity}{{267.1}{794}{Step 1: Declare an add method with parameters for creating the entity}{section.267.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {267.2}Step 2: Validate the parameters}{795}{section.267.2}\\protected@file@percent }\n\\newlabel{step-2-validate-the-parameters}{{267.2}{795}{Step 2: Validate the parameters}{section.267.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {267.3}Step 3: Generate a primary key}{795}{section.267.3}\\protected@file@percent }\n\\newlabel{step-3-generate-a-primary-key}{{267.3}{795}{Step 3: Generate a primary key}{section.267.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {267.4}Step 4: Create an entity instance}{795}{section.267.4}\\protected@file@percent }\n\\newlabel{step-4-create-an-entity-instance}{{267.4}{795}{Step 4: Create an entity instance}{section.267.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {267.5}Step 5: Populate the entity attributes}{796}{section.267.5}\\protected@file@percent }\n\\newlabel{step-5-populate-the-entity-attributes}{{267.5}{796}{Step 5: Populate the entity attributes}{section.267.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {267.6}Step 6: Persist the entity}{796}{section.267.6}\\protected@file@percent }\n\\newlabel{step-6-persist-the-entity}{{267.6}{796}{Step 6: Persist the entity}{section.267.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {267.7}Step 7: Return the entity}{796}{section.267.7}\\protected@file@percent }\n\\newlabel{step-7-return-the-entity}{{267.7}{796}{Step 7: Return the entity}{section.267.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {268}Implementing Update and Delete Methods}{797}{chapter.268}\\protected@file@percent }\n\\newlabel{implementing-update-and-delete-methods}{{268}{797}{Implementing Update and Delete Methods}{chapter.268}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.1}Implementing an Update Method}{797}{section.268.1}\\protected@file@percent }\n\\newlabel{implementing-an-update-method}{{268.1}{797}{Implementing an Update Method}{section.268.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.2}Step 1: Declare an Update Method with Parameters for Updating the Entity}{798}{section.268.2}\\protected@file@percent }\n\\newlabel{step-1-declare-an-update-method-with-parameters-for-updating-the-entity}{{268.2}{798}{Step 1: Declare an Update Method with Parameters for Updating the Entity}{section.268.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.3}Step 2: Validate the Parameters}{798}{section.268.3}\\protected@file@percent }\n\\newlabel{step-2-validate-the-parameters-1}{{268.3}{798}{Step 2: Validate the Parameters}{section.268.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.4}Step 3: Retrieve the Entity Instance}{799}{section.268.4}\\protected@file@percent }\n\\newlabel{step-3-retrieve-the-entity-instance}{{268.4}{799}{Step 3: Retrieve the Entity Instance}{section.268.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.5}Step 4: Update the Entity Attributes}{799}{section.268.5}\\protected@file@percent }\n\\newlabel{step-4-update-the-entity-attributes}{{268.5}{799}{Step 4: Update the Entity Attributes}{section.268.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.6}Step 5: Persist and Return the Updated Entity Instance}{799}{section.268.6}\\protected@file@percent }\n\\newlabel{step-5-persist-and-return-the-updated-entity-instance}{{268.6}{799}{Step 5: Persist and Return the Updated Entity Instance}{section.268.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.7}Step 6: Run Service Builder}{799}{section.268.7}\\protected@file@percent }\n\\newlabel{step-6-run-service-builder}{{268.7}{799}{Step 6: Run Service Builder}{section.268.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.8}Implementing a Delete Method}{799}{section.268.8}\\protected@file@percent }\n\\newlabel{implementing-a-delete-method}{{268.8}{799}{Implementing a Delete Method}{section.268.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.9}Related Topics}{800}{section.268.9}\\protected@file@percent }\n\\newlabel{related-topics-18}{{268.9}{800}{Related Topics}{section.268.9}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {269}Implementing Methods to Get and Count Entities}{801}{chapter.269}\\protected@file@percent }\n\\newlabel{implementing-methods-to-get-and-count-entities}{{269}{801}{Implementing Methods to Get and Count Entities}{chapter.269}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {269.1}Getter Methods}{801}{section.269.1}\\protected@file@percent }\n\\newlabel{getter-methods}{{269.1}{801}{Getter Methods}{section.269.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {269.2}Counter Methods}{802}{section.269.2}\\protected@file@percent }\n\\newlabel{counter-methods}{{269.2}{802}{Counter Methods}{section.269.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {269.3}Service Method Prefixes and Transactional Aspects}{803}{section.269.3}\\protected@file@percent }\n\\newlabel{service-method-prefixes-and-transactional-aspects}{{269.3}{803}{Service Method Prefixes and Transactional Aspects}{section.269.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {269.4}Related Topics}{804}{section.269.4}\\protected@file@percent }\n\\newlabel{related-topics-19}{{269.4}{804}{Related Topics}{section.269.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {270}Implementing Any Other Business Logic}{805}{chapter.270}\\protected@file@percent }\n\\newlabel{implementing-any-other-business-logic}{{270}{805}{Implementing Any Other Business Logic}{chapter.270}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {271}Integrating with Liferay's Frameworks}{807}{chapter.271}\\protected@file@percent }\n\\newlabel{integrating-with-liferays-frameworks}{{271}{807}{Integrating with Liferay's Frameworks}{chapter.271}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {271.1}Related Topics}{808}{section.271.1}\\protected@file@percent }\n\\newlabel{related-topics-20}{{271.1}{808}{Related Topics}{section.271.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {272}Invoking Local Services}{809}{chapter.272}\\protected@file@percent }\n\\newlabel{invoking-local-services}{{272}{809}{Invoking Local Services}{chapter.272}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {272.1}Step 1: Reference the Local Service Component}{809}{section.272.1}\\protected@file@percent }\n\\newlabel{step-1-reference-the-local-service-component}{{272.1}{809}{Step 1: Reference the Local Service Component}{section.272.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {272.2}Step 2: Call the Component's Methods}{810}{section.272.2}\\protected@file@percent }\n\\newlabel{step-2-call-the-components-methods}{{272.2}{810}{Step 2: Call the Component's Methods}{section.272.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {272.3}Related Topics}{811}{section.272.3}\\protected@file@percent }\n\\newlabel{related-topics-21}{{272.3}{811}{Related Topics}{section.272.3}{}}\n\\gdef \\LT@xiv {\\LT@entry \n    {1}{194.50543pt}\\LT@entry \n    {1}{275.24957pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {273}Invoking Services from Spring Service Builder Code}{813}{chapter.273}\\protected@file@percent }\n\\newlabel{invoking-services-from-spring-service-builder-code}{{273}{813}{Invoking Services from Spring Service Builder Code}{chapter.273}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {273.1}Referencing a Spring Bean that is in the Application Context}{814}{section.273.1}\\protected@file@percent }\n\\newlabel{referencing-a-spring-bean-that-is-in-the-application-context}{{273.1}{814}{Referencing a Spring Bean that is in the Application Context}{section.273.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {273.2}Referencing OSGi Services}{814}{section.273.2}\\protected@file@percent }\n\\newlabel{referencing-osgi-services}{{273.2}{814}{Referencing OSGi Services}{section.273.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {273.3}Related Topics}{815}{section.273.3}\\protected@file@percent }\n\\newlabel{related-topics-22}{{273.3}{815}{Related Topics}{section.273.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {274}Advanced Queries}{817}{chapter.274}\\protected@file@percent }\n\\newlabel{advanced-queries}{{274}{817}{Advanced Queries}{chapter.274}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {275}Custom SQL}{819}{chapter.275}\\protected@file@percent }\n\\newlabel{custom-sql}{{275}{819}{Custom SQL}{chapter.275}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {275.1}Specify Your Custom SQL}{819}{section.275.1}\\protected@file@percent }\n\\newlabel{specify-your-custom-sql}{{275.1}{819}{Specify Your Custom SQL}{section.275.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {275.2}Related Topics}{820}{section.275.2}\\protected@file@percent }\n\\newlabel{related-topics-23}{{275.2}{820}{Related Topics}{section.275.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {276}Defining a Custom Finder Method}{821}{chapter.276}\\protected@file@percent }\n\\newlabel{defining-a-custom-finder-method}{{276}{821}{Defining a Custom Finder Method}{chapter.276}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {277}Dynamic Query}{823}{chapter.277}\\protected@file@percent }\n\\newlabel{dynamic-query}{{277}{823}{Dynamic Query}{chapter.277}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {277.1}Example Finder Method: findByGuestbookNameEntryName}{823}{section.277.1}\\protected@file@percent }\n\\newlabel{example-finder-method-findbyguestbooknameentryname}{{277.1}{823}{Example Finder Method: findByGuestbookNameEntryName}{section.277.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {277.2}Using a Hibernate Session}{825}{section.277.2}\\protected@file@percent }\n\\newlabel{using-a-hibernate-session}{{277.2}{825}{Using a Hibernate Session}{section.277.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {277.3}Creating Dynamic Queries}{826}{section.277.3}\\protected@file@percent }\n\\newlabel{creating-dynamic-queries}{{277.3}{826}{Creating Dynamic Queries}{section.277.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {277.4}Restriction Criteria}{826}{section.277.4}\\protected@file@percent }\n\\newlabel{restriction-criteria}{{277.4}{826}{Restriction Criteria}{section.277.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {277.5}Projection Criteria}{827}{section.277.5}\\protected@file@percent }\n\\newlabel{projection-criteria}{{277.5}{827}{Projection Criteria}{section.277.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {277.6}Order Criteria}{827}{section.277.6}\\protected@file@percent }\n\\newlabel{order-criteria}{{277.6}{827}{Order Criteria}{section.277.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {277.7}Executing the Dynamic Query}{828}{section.277.7}\\protected@file@percent }\n\\newlabel{executing-the-dynamic-query}{{277.7}{828}{Executing the Dynamic Query}{section.277.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {278}Accessing Your Custom Finder Method from the Service Layer}{829}{chapter.278}\\protected@file@percent }\n\\newlabel{accessing-your-custom-finder-method-from-the-service-layer}{{278}{829}{Accessing Your Custom Finder Method from the Service Layer}{chapter.278}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {278.1}Related Topics}{830}{section.278.1}\\protected@file@percent }\n\\newlabel{related-topics-24}{{278.1}{830}{Related Topics}{section.278.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {279}Actionable Dynamic Queries}{831}{chapter.279}\\protected@file@percent }\n\\newlabel{actionable-dynamic-queries}{{279}{831}{Actionable Dynamic Queries}{chapter.279}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {279.1}Related Topics}{833}{section.279.1}\\protected@file@percent }\n\\newlabel{related-topics-25}{{279.1}{833}{Related Topics}{section.279.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {280}REST Builder}{835}{chapter.280}\\protected@file@percent }\n\\newlabel{rest-builder}{{280}{835}{REST Builder}{chapter.280}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {281}Generating APIs with REST Builder}{837}{chapter.281}\\protected@file@percent }\n\\newlabel{generating-apis-with-rest-builder}{{281}{837}{Generating APIs with REST Builder}{chapter.281}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {281.1}Related Topics}{838}{section.281.1}\\protected@file@percent }\n\\newlabel{related-topics-26}{{281.1}{838}{Related Topics}{section.281.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {282}Troubleshooting Application Development Issues}{839}{chapter.282}\\protected@file@percent }\n\\newlabel{troubleshooting-application-development-issues}{{282}{839}{Troubleshooting Application Development Issues}{chapter.282}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {282.1}Modules}{839}{section.282.1}\\protected@file@percent }\n\\newlabel{modules}{{282.1}{839}{Modules}{section.282.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {282.2}Services and Components}{840}{section.282.2}\\protected@file@percent }\n\\newlabel{services-and-components}{{282.2}{840}{Services and Components}{section.282.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {283}Adjusting Module Logging}{841}{chapter.283}\\protected@file@percent }\n\\newlabel{adjusting-module-logging}{{283}{841}{Adjusting Module Logging}{chapter.283}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {283.1}Related Topics}{842}{section.283.1}\\protected@file@percent }\n\\newlabel{related-topics-27}{{283.1}{842}{Related Topics}{section.283.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {284}Identifying Liferay Artifact Versions for Dependencies}{843}{chapter.284}\\protected@file@percent }\n\\newlabel{identifying-liferay-artifact-versions-for-dependencies}{{284}{843}{Identifying Liferay Artifact Versions for Dependencies}{chapter.284}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {284.1}Related Topics}{843}{section.284.1}\\protected@file@percent }\n\\newlabel{related-topics-28}{{284.1}{843}{Related Topics}{section.284.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {285}Resolving Bundle-SymbolicName Syntax Issues}{845}{chapter.285}\\protected@file@percent }\n\\newlabel{resolving-bundle-symbolicname-syntax-issues}{{285}{845}{Resolving Bundle-SymbolicName Syntax Issues}{chapter.285}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {285.1}Related Topics}{845}{section.285.1}\\protected@file@percent }\n\\newlabel{related-topics-29}{{285.1}{845}{Related Topics}{section.285.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {286}Calling Non-OSGi Code that Uses OSGi Services}{847}{chapter.286}\\protected@file@percent }\n\\newlabel{calling-non-osgi-code-that-uses-osgi-services}{{286}{847}{Calling Non-OSGi Code that Uses OSGi Services}{chapter.286}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {286.1}Related Topics}{847}{section.286.1}\\protected@file@percent }\n\\newlabel{related-topics-30}{{286.1}{847}{Related Topics}{section.286.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {287}Connecting to JNDI Data Sources}{849}{chapter.287}\\protected@file@percent }\n\\newlabel{connecting-to-jndi-data-sources}{{287}{849}{Connecting to JNDI Data Sources}{chapter.287}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {288}Detecting Unresolved OSGi Components}{851}{chapter.288}\\protected@file@percent }\n\\newlabel{detecting-unresolved-osgi-components}{{288}{851}{Detecting Unresolved OSGi Components}{chapter.288}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {288.1}Declarative Services Components}{851}{section.288.1}\\protected@file@percent }\n\\newlabel{declarative-services-components}{{288.1}{851}{Declarative Services Components}{section.288.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {288.2}Declarative Services Unsatisfied Component Scanner}{852}{section.288.2}\\protected@file@percent }\n\\newlabel{declarative-services-unsatisfied-component-scanner}{{288.2}{852}{Declarative Services Unsatisfied Component Scanner}{section.288.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {288.3}ds:unsatisfied Command}{852}{section.288.3}\\protected@file@percent }\n\\newlabel{dsunsatisfied-command}{{288.3}{852}{ds:unsatisfied Command}{section.288.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {288.4}Service Builder Components}{853}{section.288.4}\\protected@file@percent }\n\\newlabel{service-builder-components}{{288.4}{853}{Service Builder Components}{section.288.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {288.5}Unavailable Component Scanner}{853}{section.288.5}\\protected@file@percent }\n\\newlabel{unavailable-component-scanner}{{288.5}{853}{Unavailable Component Scanner}{section.288.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {288.6}dm na Command}{854}{section.288.6}\\protected@file@percent }\n\\newlabel{dm-na-command}{{288.6}{854}{dm na Command}{section.288.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {288.7}ServiceProxyFactory}{854}{section.288.7}\\protected@file@percent }\n\\newlabel{serviceproxyfactory}{{288.7}{854}{ServiceProxyFactory}{section.288.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {288.8}Related Topics}{854}{section.288.8}\\protected@file@percent }\n\\newlabel{related-topics-31}{{288.8}{854}{Related Topics}{section.288.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {289}Disabling Cache for Table Mapper Tables}{855}{chapter.289}\\protected@file@percent }\n\\newlabel{disabling-cache-for-table-mapper-tables}{{289}{855}{Disabling Cache for Table Mapper Tables}{chapter.289}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {289.1}Why would I want to disable cache on a table mapper?}{855}{section.289.1}\\protected@file@percent }\n\\newlabel{why-would-i-want-to-disable-cache-on-a-table-mapper}{{289.1}{855}{Why would I want to disable cache on a table mapper?}{section.289.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {289.2}Disabling a Table Mapper Cache}{856}{section.289.2}\\protected@file@percent }\n\\newlabel{disabling-a-table-mapper-cache}{{289.2}{856}{Disabling a Table Mapper Cache}{section.289.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {290}Implementing Logging}{857}{chapter.290}\\protected@file@percent }\n\\newlabel{implementing-logging}{{290}{857}{Implementing Logging}{chapter.290}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {290.1}Related Topics}{858}{section.290.1}\\protected@file@percent }\n\\newlabel{related-topics-32}{{290.1}{858}{Related Topics}{section.290.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {291}Declaring Optional Import Package Requirements}{859}{chapter.291}\\protected@file@percent }\n\\newlabel{declaring-optional-import-package-requirements}{{291}{859}{Declaring Optional Import Package Requirements}{chapter.291}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {291.1}Related Topics}{860}{section.291.1}\\protected@file@percent }\n\\newlabel{related-topics-33}{{291.1}{860}{Related Topics}{section.291.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {292}Resolving Bundle Requirements}{861}{chapter.292}\\protected@file@percent }\n\\newlabel{resolving-bundle-requirements}{{292}{861}{Resolving Bundle Requirements}{chapter.292}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {292.1}Related Topics}{862}{section.292.1}\\protected@file@percent }\n\\newlabel{related-topics-34}{{292.1}{862}{Related Topics}{section.292.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {293}Resolving ClassNotFoundException and NoClassDefFoundError in OSGi Bundles}{863}{chapter.293}\\protected@file@percent }\n\\newlabel{resolving-classnotfoundexception-and-noclassdeffounderror-in-osgi-bundles}{{293}{863}{Resolving ClassNotFoundException and NoClassDefFoundError in OSGi Bundles}{chapter.293}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {293.1}Case 1: The Missing Class Belongs to an OSGi Module}{863}{section.293.1}\\protected@file@percent }\n\\newlabel{case-1-the-missing-class-belongs-to-an-osgi-module}{{293.1}{863}{Case 1: The Missing Class Belongs to an OSGi Module}{section.293.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {293.2}Case 2: The Missing Class Doesn't Belong to an OSGi Module}{864}{section.293.2}\\protected@file@percent }\n\\newlabel{case-2-the-missing-class-doesnt-belong-to-an-osgi-module}{{293.2}{864}{Case 2: The Missing Class Doesn't Belong to an OSGi Module}{section.293.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {293.3}Case 3: The Missing Class Belongs to a Global Library}{864}{section.293.3}\\protected@file@percent }\n\\newlabel{case-3-the-missing-class-belongs-to-a-global-library}{{293.3}{864}{Case 3: The Missing Class Belongs to a Global Library}{section.293.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {293.4}Case 4: The Missing Class Belongs to a Java Runtime Package}{865}{section.293.4}\\protected@file@percent }\n\\newlabel{case-4-the-missing-class-belongs-to-a-java-runtime-package}{{293.4}{865}{Case 4: The Missing Class Belongs to a Java Runtime Package}{section.293.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {293.5}Related Topics}{865}{section.293.5}\\protected@file@percent }\n\\newlabel{related-topics-35}{{293.5}{865}{Related Topics}{section.293.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {294}System Check}{867}{chapter.294}\\protected@file@percent }\n\\newlabel{system-check}{{294}{867}{System Check}{chapter.294}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {294.1}Related Topics}{867}{section.294.1}\\protected@file@percent }\n\\newlabel{related-topics-36}{{294.1}{867}{Related Topics}{section.294.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {295}Troubleshooting Front-End Development Issues}{869}{chapter.295}\\protected@file@percent }\n\\newlabel{troubleshooting-front-end-development-issues}{{295}{869}{Troubleshooting Front-End Development Issues}{chapter.295}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {295.1}CSS}{869}{section.295.1}\\protected@file@percent }\n\\newlabel{css}{{295.1}{869}{CSS}{section.295.1}{}}\n\\newlabel{broken-css-angular-app}{{295.1}{869}{CSS}{section*.7}{}}\n\\newlabel{portal-css-broken-ie}{{295.1}{869}{CSS}{section*.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {295.2}Modules}{869}{section.295.2}\\protected@file@percent }\n\\newlabel{modules-1}{{295.2}{869}{Modules}{section.295.2}{}}\n\\newlabel{jquery-anonymous-module-error}{{295.2}{869}{Modules}{section*.9}{}}\n\\newlabel{source-maps-not-showing}{{295.2}{870}{Modules}{section*.10}{}}\n\\newlabel{disable-bundler-analytics}{{295.2}{870}{Modules}{section*.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {295.3}Portlets}{870}{section.295.3}\\protected@file@percent }\n\\newlabel{portlets}{{295.3}{870}{Portlets}{section.295.3}{}}\n\\newlabel{angular-react-vue-portlet-disable-spa}{{295.3}{870}{Portlets}{section*.12}{}}\n\\@setckpt{developer/appdev}{\n\\setcounter{page}{871}\n\\setcounter{equation}{0}\n\\setcounter{enumi}{3}\n\\setcounter{enumii}{1}\n\\setcounter{enumiii}{3}\n\\setcounter{enumiv}{0}\n\\setcounter{footnote}{0}\n\\setcounter{mpfootnote}{0}\n\\setcounter{@memmarkcntra}{0}\n\\setcounter{storedpagenumber}{1}\n\\setcounter{book}{0}\n\\setcounter{part}{3}\n\\setcounter{chapter}{295}\n\\setcounter{section}{3}\n\\setcounter{subsection}{0}\n\\setcounter{subsubsection}{0}\n\\setcounter{paragraph}{0}\n\\setcounter{subparagraph}{0}\n\\setcounter{@ppsavesec}{0}\n\\setcounter{@ppsaveapp}{0}\n\\setcounter{vslineno}{0}\n\\setcounter{poemline}{0}\n\\setcounter{modulo@vs}{0}\n\\setcounter{memfvsline}{0}\n\\setcounter{verse}{0}\n\\setcounter{chrsinstr}{0}\n\\setcounter{poem}{0}\n\\setcounter{newflo@tctr}{4}\n\\setcounter{@contsubnum}{0}\n\\setcounter{section@level}{0}\n\\setcounter{maxsecnumdepth}{1}\n\\setcounter{sidefootnote}{0}\n\\setcounter{pagenote}{0}\n\\setcounter{pagenoteshadow}{0}\n\\setcounter{memfbvline}{0}\n\\setcounter{bvlinectr}{0}\n\\setcounter{cp@cntr}{0}\n\\setcounter{ism@mctr}{0}\n\\setcounter{xsm@mctr}{0}\n\\setcounter{csm@mctr}{0}\n\\setcounter{ksm@mctr}{0}\n\\setcounter{xksm@mctr}{0}\n\\setcounter{cksm@mctr}{0}\n\\setcounter{msm@mctr}{0}\n\\setcounter{xmsm@mctr}{0}\n\\setcounter{cmsm@mctr}{0}\n\\setcounter{bsm@mctr}{0}\n\\setcounter{workm@mctr}{0}\n\\setcounter{sheetsequence}{943}\n\\setcounter{lastsheet}{2851}\n\\setcounter{lastpage}{2779}\n\\setcounter{figure}{0}\n\\setcounter{lofdepth}{1}\n\\setcounter{table}{0}\n\\setcounter{lotdepth}{1}\n\\setcounter{Item}{1096}\n\\setcounter{Hfootnote}{5}\n\\setcounter{bookmark@seq@number}{0}\n\\setcounter{memhycontfloat}{0}\n\\setcounter{Hpagenote}{0}\n\\setcounter{r@tfl@t}{0}\n\\setcounter{float@type}{4}\n\\setcounter{LT@tables}{14}\n\\setcounter{LT@chunks}{3}\n\\setcounter{parentequation}{0}\n\\setcounter{FancyVerbLine}{0}\n}\n"
  },
  {
    "path": "book/developer/appdev.tex",
    "content": "\\chapter{Application Development}\\label{application-development}\n\nWriting applications on Liferay's standards-based platform makes your\nlife easier. Whether you create headless services for clients to access,\nfull-blown web applications with beautiful UIs, or anything in between,\nLiferay DXP streamlines the process to help you get your job done\nfaster.\n\nLiferay's framework embraces your existing tools and build environments\nlike \\href{https://maven.apache.org}{Maven} and\n\\href{https://gradle.org}{Gradle}. You can work with the standard\ntechnologies you know and leverage Liferay's APIs for Documents,\nPermissions, Search, or Content when you need them. Here's a high level\nview of what you can do:\n\n\\begin{itemize}\n\\item\n  \\textbf{Deployment of existing standards-based apps:} If you have an\n  existing app built outside of Liferay DXP, you can deploy it on\n  Liferay DXP. The Liferay Bundler Generator and Liferay npm Bundler\n  provide the project scaffolding and packaging to deploy\n  \\href{https://angular.io/}{Angular},\n  \\href{https://reactjs.org/}{React}, and \\href{https://vuejs.org/}{Vue}\n  web front-ends as Widgets. Spring Portlet MVC app conversion to\n  \\href{https://github.com/liferay/portletmvc4spring}{PortletMVC4Spring}\n  requires only a few steps. JSF applications work almost as-is. Portlet\n  3.0 or 2.0 compliant portlets deploy on Liferay DXP.\n\\item\n  \\textbf{Back-end Java services, web services, and REST services:}\n  Service Builder is an object-relational mapper where you describe your\n  data model in a single \\texttt{xml} file. From this, you can generate\n  the tables, a Java API for accessing your data model, and web\n  services. On top of these, REST Builder generates OpenAPI-based REST\n  services your client applications can call.\n\\item\n  \\textbf{Authentication and single-sign on (SSO):} OAuth 2.0, OpenID\n  Connect, and SAML are built-in and ready to go.\n\\item\n  \\textbf{Front-end web development using Java EE and/or JavaScript:}\n  Use Java EE standard Portlet technology (JSR 168, JSR 286, JSR 362)\n  with CDI and/or JSF. Prefer Spring?\n  \\href{https://github.com/liferay/portletmvc4spring}{PortletMVC4Spring}\n  brings the Spring MVC Framework to Liferay. Rather have a client-side\n  app? Write it in \\href{https://angular.io/}{Angular},\n  \\href{https://reactjs.org/}{React}, or \\href{https://vuejs.org/}{Vue}.\n  Been using Liferay DXP for a while? Liferay MVC Portlet is better than\n  ever.\n\\item\n  \\textbf{Frameworks and APIs for every need:} Be more productive by\n  using Liferay's built-in and well-tested APIs that cover often-used\n  features like file management(upload/download), permissions, comments,\n  out-of-process messaging, or UI elements such as data tables and item\n  selectors. Liferay DXP offers many APIs for every need, from an entire\n  workflow framework to a streamlined way of getting request parameters.\n\\item\n  \\textbf{Tool freedom:} Liferay provides Maven archetypes,\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\n  Workspace},\n  \\href{/docs/7-2/reference/-/knowledge_base/r/gradle-plugins}{Gradle}\n  and \\href{/docs/7-2/reference/-/knowledge_base/r/maven-plugins}{Maven}\n  plugins, a \\href{http://yeoman.io/}{Yeoman}-based\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-generator}{theme\n  generator}, and\n  \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI} to\n  integrate with any development workflow. On top of that, you can use\n  our \\href{/docs/7-2/reference/-/knowledge_base/r/intellij}{IntelliJ\n  plugin} or the Eclipse-based\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\n  Developer Studio} if you need a full-blown development environment.\n\\item\n  \\textbf{Developer community:} The \\href{https://liferay.dev}{Liferay\n  DXP community} is helpful and active.\n\\end{itemize}\n\n\\section{Getting Started with Liferay\nDevelopment}\\label{getting-started-with-liferay-development}\n\nWant to see what it's like to develop an app on Liferay DXP? Here's a\nquick tour.\n\n\\section{Create Your Object Model and Database in One\nShot}\\label{create-your-object-model-and-database-in-one-shot}\n\nYou don't need a database to work with Liferay, but if your app uses\none, you can design it and your object model at the same time with\nLiferay's object-relational mapper,\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}. You define your object model in a single \\texttt{xml} file:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_0_0.dtd\">\n<service-builder auto-namespace-tables=\"true\" package-path=\"com.liferay.docs.guestbook\">\n    <author>liferay</author>\n    <namespace>GB</namespace>\n    <entity name=\"Guestbook\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n\n        <column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n        <column name=\"name\" type=\"String\" />\n\n        <finder name=\"Name\" return-type=\"Collection\"/>\n            <finder-column name=\"name\" />\n        </finder>\n\n    </entity>\n\n    <entity name=\"Entry\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n    \n        <column name=\"entryId\" primary=\"true\" type=\"long\" />\n        <column name=\"name\" type=\"String\" />\n        <column name=\"email\" type=\"String\" />\n        <column name=\"message\" type=\"String\" />\n        <column name=\"guestbookId\" type=\"long\" />\n\n        <finder name=\"Email\" return-type=\"Collection\" />\n            <finder-column name=\"email\" />\n        </finder>\n\n    </entity>\n\n</service-builder>\n\\end{verbatim}\n\nService Builder generates your object model, database, SOAP, and JSON\nweb services automatically. Java classes are ready for you to implement\nyour business logic around generated CRUD operations. The web services\nare mapped to your business logic. If you want a REST interface, you can\ncreate one.\n\n\\section{Create a REST Interface}\\label{create-a-rest-interface}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/rest-builder}{REST Builder}\nhelps you define REST interfaces for your APIs, using\n\\href{https://swagger.io/docs/specification/about/}{OpenAPI/Swagger}.\nCreate your\n\\href{https://swagger.io/docs/specification/basic-structure/}{YAML\ndefinition} file for your REST interface along with a configuration file\ndefining where Java classes, a client, and tests should be generated,\nand you have REST endpoints ready to call your API.\n\nNext, you need a client. You can use Liferay DXP in headless mode and\nwrite your web and mobile clients any way you want. Or you can create\nyour web clients on Liferay's platform and take advantage of its many\ntools and APIs that speed up development.\n\n\\section{Create a Web Client}\\label{create-a-web-client}\n\nLiferay DXP is an ideal platform upon which to build a web client. Its\nJava EE-based technology means you can pick from the best it has to\noffer: Spring MVC using\n\\href{https://github.com/liferay/portletmvc4spring}{PortletMVC4Spring},\nthe new backwards-compatible Portlet 3, JSF using\n\\href{https://liferayfaces.org}{Liferay Faces}, or the venerable\nOSGi-based\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{Liferay\nMVC Portlet}. If you're a front-end developer, deploy your Angular,\nReact, or Vue-based front-end applications to run as widgets next to the\nrest of Liferay DXP's installed applications.\n\n\\section{Use Liferay's Frameworks}\\label{use-liferays-frameworks}\n\nYour apps need features. Liferay has implemented tons of common\nfunctionality you can use in your applications. The\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/liferay-ui/tld-summary.html}{Liferay-UI}\ntag library has tons of web components like Search Container (a sortable\ndata table), panels, buttons, and more. Liferay's\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{Asset\nFramework} can publish data from your application in context wherever\nusers need it---as a notification, a related asset, as tagged or\ncategorized data, or as relevant data based on a\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-user-segments}{user\nsegment}. Need to provide file upload/download? Use the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nAPI}. Need a robust permissions system? Use\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions}{Liferay\npermissions}. Want users to submit comments? Use Liferay's\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-comments-to-your-app}{comments}.\nNeed to process data outside the request/response? Use the Message Bus.\nShould users select items from a list? Use the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/item-selector}{Item\nSelector}.\n\n\\section{Next Steps}\\label{next-steps}\n\nSo what's next? \\href{/download}{Download} Liferay DXP and\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{create\nyour first project}! Have a look at our\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{back-end},\n\\href{/docs/7-2/appdev/-/knowledge_base/a/rest-builder}{REST Builder},\nand \\href{/docs/7-2/appdev/-/knowledge_base/a/web-front-ends}{front-end}\ndocs, examine what Liferay's\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/frameworks}{frameworks}\nhave to offer, and then go create the beautiful things that only you can\nmake.\n\n\\chapter{Developing Web Front-Ends}\\label{developing-web-front-ends}\n\nLiferay's open development framework removes barriers so developers can\nwrite applications faster. If you already have an application, you can\ndeploy it on Liferay DXP:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Java-based standards (CDI, JSF, Portlets, Spring)\n\\item\n  Front-end standards (Angular, React, Vue)\n\\end{itemize}\n\nIf you plan to write a new application and deploy it on Liferay DXP, you\ncan use the frameworks you know along with the build tools (Gradle,\nMaven) you know. Liferay also offers its own development framework\ncalled MVC Portlet that it uses to develop applications. When you want\nto integrate with\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Liferay\nservices} and frameworks such as permissions, assets, and indexers,\nyou'll find that these easily and seamlessly blend with your application\nto provide a great user experience.\n\nRegardless of your development strategy for applications, you'll find\nLiferay DXP to be a flexible platform that supports anything you need to\nwrite.\n\n\\section{Using Popular Frameworks}\\label{using-popular-frameworks}\n\nLiferay gives you a head start on developing and deploying apps that use\nthese popular Java and JavaScript-based technologies:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/developing-an-angular-application}{Angular\n  Widget}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/developing-a-react-application}{React\n  Widget}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/developing-a-vue-application}{Vue\n  Widget}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{Liferay\n  MVC Portlet}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/portletmvc4spring}{PortletMVC4Spring\n  Portlet}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/jsf-portlet}{JSF Portlet}\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Reference section describes\n\\href{/docs/7-2/reference/-/knowledge_base/r/sample-projects}{sample\nprojects} and\n\\href{/docs/7-2/reference/-/knowledge_base/r/project-templates}{project\ntemplates} for creating UIs using other technologies.\n\n\\noindent\\hrulefill\n\nAngular, React, and Vue applications are written the same as you would\noutside of Liferay DXP---using \\href{https://www.npmjs.com/}{npm} and\nthe webpack dev server. The Liferay JS Generator creates a portlet\nbundle (project) for developing and deploying each type of app. The\nbundle project comes with npm commands for building, testing, and\ndeploying the app. It packages the app's dependencies (including\nJavaScript packages), deploys the bundle as a JAR, and installs the\nbundle to Liferay DXP's run time environment, making your app available\nas a widget.\n\nYou can also develop web front-ends using Java EE standards. Liferay DXP\nsupports the \\href{https://jcp.org/en/jsr/detail?id=362}{JSR 362}\nPortlet 3.0 standard which is backwards-compatible with the\n\\href{http://jcp.org/en/jsr/detail?id=286}{JSR 286} Portlet 2.0 standard\nfrom the Java Community Process (JCP). Each portlet framework has\nbenefits you may wish to consider.\n\nBean Portlet is the only framework containing all of the Portlet 3\nfeatures:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Contexts and Dependency Injection (CDI)\n\\item\n  Extended method annotations\n\\item\n  Explicit render state\n\\item\n  Action, render, and resource parameters\n\\item\n  Asynchronous support\n\\end{itemize}\n\nIf you're a JavaServer Faces (JSF) developer, the\n\\href{/docs/7-1/reference/-/knowledge_base/r/understanding-liferay-faces-bridge}{Liferay\nFaces Bridge} supports deploying JSF web apps as portlets without\nwriting portlet-specific Java code. It also contains innovative features\nthat make it possible to leverage the power of JSF 2.x inside a portlet\napplication.\n\nIf Spring is your thing, Spring Portlet MVC portlets are easy to\nconfigure and deploy on Liferay DXP. You can continue using Spring\nfeatures, including Spring beans and Spring dependency injection.\n\nLast but not least, Liferay MVC Portlet continues to be a favorite with\nexperienced Liferay developers, and makes portlet development easy for\nLiferay newcomers. It leverages OSGi Declarative Services (DS) for\ninjecting dependencies and defining configurable extension points. Since\nLiferay DXP core and Liferay-written apps use DS, gaining experience\nwith DS helps you develop Liferay DXP extensions and customizations.\nLiferay MVC Portlet works seamlessly with many Liferay frameworks, such\nas MVC commands, Service Builder, and more.\n\nNo matter which development framework you choose, you'll be able to get\nan app up and running fast.\n\n\\section{Getting Started}\\label{getting-started}\n\nIf you have an existing app that uses one the frameworks described\nabove, your first step is to deploy it to Liferay DXP. Most deployments\ninvolve configuration steps that you can complete in an hour or less.\n\nYou can also build apps from scratch using the tools you like or\nleveraging Liferay's tool offering. Liferay provides templates for\ncreating all kinds of apps and samples that you can examine and modify\nto fit your needs.\n\nOnce your app is functional, you can improve your app by integrating it\nwith Liferay frameworks:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Localization\n\\item\n  Permissions\n\\item\n  Search and indexing\n\\item\n  Asset publishing\n\\item\n  Workflow\n\\item\n  Staging\n\\item\n  Data export and import\n\\end{itemize}\n\nLiferay provides frameworks that integrate these features fast. As you\ndevelop apps on Liferay DXP, you'll enjoy using what you know, discover\nframeworks and tools that boost your productivity, and have fun creating\nrich, full-featured applications.\n\nIf you're experienced with developing one of the listed app types, feel\nfree to jump ahead to it. Otherwise, Angular Widgets is next.\n\n\\chapter{Developing an Angular\nApplication}\\label{developing-an-angular-application}\n\nRunning an existing Angular app on Liferay DXP makes the app available\nas a widget for using on site pages. You can\n\\href{/docs/7-2/reference/-/knowledge_base/r/adapting-existing-apps-to-run-on-product}{adapt\nyour existing Angular app}, but this doesn't give you access to the\nbundler and its various loaders to develop your project further in\nLiferay DXP. To have access to all of Liferay DXP's features, you must\nuse the Liferay JS Generator and Liferay npm Bundler to merge your files\ninto a portlet bundle, adapt your routes and CSS, and deploy your\nbundle.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/appdev-angular-app-migrated.png}\n\\caption{Apps like this Guestbook app are easy to migrate to Liferay\nDXP.}\n\\end{figure}\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Using \\href{https://www.npmjs.com}{npm}, install the Liferay JS\n  Generator:\n\n\\begin{verbatim}\nnpm install -g yo generator-liferay-js\n\\end{verbatim}\n\\item\n  Generate an Angular-based portlet bundle project for deploying your\n  app to your \\href{/deployment/docs/installing-product}{Liferay DXP\n  installation}.\n\n\\begin{verbatim}\nyo liferay-js\n\\end{verbatim}\n\n  Select \\texttt{Angular\\ based\\ portlet} and opt for generating sample\n  code. Here's the bundle's structure:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{{[}my-angular-portlet-bundle{]}}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{assets/} → CSS, HTML templates, and resources\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{css/} → CSS files\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{styles.css} → Default CSS file\n        \\end{itemize}\n      \\item\n        \\texttt{app/} → HTML templates\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{app.component.html} → Root component template\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{features/} → Liferay DXP bundle features\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{localization/} → Resource bundles\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{Language.properties} → Default language keys\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{src/} → JavaScript an TypeScript files\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{app/} → Application modules and Components\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{app.component.ts} → Main component\n        \\item\n          \\texttt{app.module.ts} → Root module\n        \\item\n          \\texttt{dynamic.loader.ts} → Loads an Angular component\n          dynamically for the portlet to attach to\n        \\end{itemize}\n      \\item\n        \\texttt{types/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{LiferayParams.ts} → Parameters passed by Liferay DXP\n          to the JavaScript module\n        \\end{itemize}\n      \\item\n        \\texttt{index.ts} → Main module invoked by the ``bootstrap''\n        module to initialize the portlet\n      \\item\n        \\texttt{polyfills.ts} → Fills in browser JavaScript\n        implementation gaps\n      \\end{itemize}\n    \\item\n      \\texttt{package.json} → npm bundle configuration\n    \\item\n      \\texttt{README.md}\n    \\item\n      \\texttt{.npmbuildrc} → Build configuration\n    \\item\n      \\texttt{.npmbundlerrc} → Bundler configuration\n    \\item\n      \\texttt{tsconfig.json} → TypeScript configuration\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  Copy your app files, matching the types listed below, into your new\n  project.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n File type | Destination | Comments |\n --------- | ----------- | -------- |\n HTML | `assets/app/` | Merge your main component with the existing `app.component.html`. |\n CSS  | `assets/css/` | Overwrite `styles.css`. |\n TypeScript and JavaScript | `src/app/` |  Merge with all files **except** `app.module.ts`---the root module merge is explained in a later step. |\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  Update your component class \\texttt{templateUrl}s to use the\n  \\texttt{web-context} value declared in your project's\n  \\texttt{.npmbundlerrc} file. Here's the format:\n\n\\begin{verbatim}\ntemplateUrl: `/o/[web-context]/app/[template]`\n\\end{verbatim}\n\n  Here's an example:\n\n\\begin{verbatim}\ntemplateUrl: '/o/my-angular-guestbook/app/add-entry/add-entry.component.html'\n\\end{verbatim}\n\\item\n  Update your bundle to use portlet-level styling.\n\n  \\begin{itemize}\n  \\item\n    Import all component CSS files through the CSS file (default is\n    \\texttt{styles.css}) your bundle's \\texttt{package.json} file sets\n    for your portlet. Here's the default setting:\n\n\\begin{verbatim}\n\"portlet\": {\n    \"com.liferay.portlet.header-portlet-css\": \"/css/styles.css\",\n...\n}\n\\end{verbatim}\n  \\item\n    Remove \\texttt{selector} and \\texttt{styleUrls} properties from your\n    component classes.\n  \\end{itemize}\n\\item\n  In your routing module's \\texttt{@NgModule} decorator, configure the\n  router option \\texttt{useHash:\\ true}. This tells Angular to use\n  client-side routing in the form of \\texttt{.../\\#/{[}route{]}}, which\n  prevents client-side parameters (i.e., anything after \\texttt{\\#})\n  from being sent back to Liferay DXP.\n\n  For example, your routing module class \\texttt{@NgModule} decorator\n  might look like this:\n\n\\begin{verbatim}\n@NgModule({\n  imports: [RouterModule.forRoot(routes, {useHash: true})],\n  exports: [RouterModule]\n})\nexport class AppRoutingModule { }\n\\end{verbatim}\n\\item\n  Also in your routing module, export your view components for your root\n  module (discussed next) to use. For example,\n\n\\begin{verbatim}\nexport const routingComponents = [ViewComponent1, ViewComponent2]\n\\end{verbatim}\n\\item\n  Merge your root module with \\texttt{src/app/app.module.ts},\n  configuring it to dynamically load components.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Components must be loaded dynamically to attach to the portlet's\n DOM. The DOM is determined at run time when the portlet's page is\n rendered. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n-   Import the `routingComponents` constant and the app routing module class\n    from your app routing module. For example,\n\n    ```javascript\n    import { AppRoutingModule, routingComponents } from './app-routing.module';\n    ```\n\n-   Specify the base href for the router to use in the navigation URLs. \n\n    ```javascript\n    import { APP_BASE_HREF } from '@angular/common';\n    ...\n    \n    @NgModule({\n        ...\n        providers: [{provide: APP_BASE_HREF, useValue: '/'}]\n    })\n    ```\n\n-   Declare the `routingComponents` constant in your `@NgModule` decorator. \n\n    ```javascript\n    @NgModule({\n      declarations: [\n          routingComponents,\n          ...\n      ],\n      ...\n    })\n    ```\n\n-   Make sure your `@NgModule` `bootstrap` property has no components. All \n    components are loaded dynamically using the `entryComponents` array\n    property. The empty `ngDoBootstrap()` method nullifies the default\n    bootstrap implementation. \n\n    ```javascript\n    @NgModule({\n      ...\n        entryComponents: [AppComponent],\n        bootstrap: [],\n        ...\n    })\n    export class AppModule {\n        ngDoBootstrap() {}\n        ...\n    }\n    ```\n\nYour root module `app.module.ts` should look like this: \n\n```javascript\nimport { APP_BASE_HREF } from '@angular/common';\nimport { AppRoutingModule, routingComponents } from './app-routing.module';\n// more imports ...\n\n@NgModule({\n  declarations: [\n    AppComponent,\n    routingComponents, \n    // more declarations ...\n  ],\n  imports: [\n    AppRoutingModule,\n    // more imports ...\n  ],\n  entryComponents: [AppComponent],\n  providers: [{provide: APP_BASE_HREF, useValue: '/'}],\n  bootstrap: [],\n  // more properties ...\n})\nexport class AppModule {\n    ngDoBootstrap() {}\n\n    // ...\n}\n```\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{8}\n\\tightlist\n\\item\n  Merge your app \\texttt{package.json} file's \\texttt{dependencies} and\n  \\texttt{devDependencies} into the bundle's \\texttt{package.json}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** To work around build errors caused by the `rxjs` dependency, set\n the dependency to version `\"6.0.0\"`. See\n [LPS-92848](https://issues.liferay.com/browse/LPS-92848)\n for details. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{9}\n\\item\n  Finally, deploy your bundle:\n\n\\begin{verbatim}\nnpm run deploy\n\\end{verbatim}\n\\end{enumerate}\n\nCongratulations! Your Angular app is deployed and now available as a\nwidget that you can add to site pages.\n\nThe Liferay npm Bundler confirms the deployment:\n\n\\begin{verbatim}\nReport written to liferay-npm-bundler-report.html\nDeployed my-angular-guestbook-1.0.0.jar to c:\\git\\bundles\n\\end{verbatim}\n\nThe Liferay DXP console confirms your bundle started:\n\n\\begin{verbatim}\n2019-03-22 20:17:53.181 INFO  [fileinstall-C:/git/bundles/osgi/modules][BundleStartStopLogger:39] STARTED my-angular-guestbook_1.0.0 [1695]\n\\end{verbatim}\n\nTo find your widget, select the \\emph{Add} icon\n(\\includegraphics{./images/icon-add-app.png}), navigate to\n\\emph{Widgets} and then the category you specified to the Liferay Bundle\nGenerator (\\emph{Sample} is the default category).\n\n\\section{Related Topics}\\label{related-topics}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/web-services}{Web\nServices}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localization}{Localization}\n\n\\chapter{Developing a React\nApplication}\\label{developing-a-react-application}\n\nRunning an existing React app on Liferay DXP makes the app available as\na widget for using on site pages. You can\n\\href{/docs/7-2/reference/-/knowledge_base/r/adapting-existing-apps-to-run-on-product}{adapt\nyour existing React app}, but this doesn't give you access to the\nbundler and its various loaders to develop your project further in\nLiferay DXP. To have access to all of Liferay DXP's features, you must\nuse the Liferay JS Generator and Liferay npm Bundler to merge your files\ninto a portlet bundle, update your static resource paths, and deploy\nyour bundle.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/appdev-react-app-migrated.png}\n\\caption{Apps like this Guestbook app are easy to migrate to Liferay\nDXP.}\n\\end{figure}\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Using \\href{https://www.npmjs.com/}{npm}, install the Liferay JS\n  Generator:\n\n\\begin{verbatim}\nnpm install -g yo generator-liferay-js\n\\end{verbatim}\n\\item\n  Generate a React based portlet bundle project for deploying your app\n  to your \\href{/deployment/docs/installing-product}{Liferay DXP\n  installation}.\n\n\\begin{verbatim}\nyo liferay-js\n\\end{verbatim}\n\n  Select \\texttt{React\\ based\\ portlet} and opt for generating sample\n  code. Here's the bundle's structure:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{my-react-portlet-bundle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{assets/} → CSS and resources\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{css/} → CSS files\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{styles.css} → Default CSS file\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{features/} → Liferay DXP bundle features\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{localization} → Resource bundles\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{Language.properties} → Default language keys\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{src/} → JavaScript and React component files\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{AppComponent.js} → Sample React component that you can\n        remove\n      \\item\n        \\texttt{index.js} → Main module used to initialize the portlet\n      \\end{itemize}\n    \\item\n      \\texttt{.babelrc} → Babel configuration\n    \\item\n      \\texttt{.npmbuildrc} → Build configuration\n    \\item\n      \\texttt{.npmbundlerrc} → Bundler configuration\n    \\item\n      \\texttt{package.json} → npm bundle configuration\n    \\item\n      \\texttt{README.md}\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  Copy your app files, matching the types listed below, into your new\n  project.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n File type | Destination | Comments |\n --------- | ----------- | -------- |\n CSS  | `assets/css/` | Overwrite `styles.css`. |\n JavaScript | `src/` |  Merge with all files **except** `index.js`---the main module merge is explained in a later step. |\n Static resources | `assets/` |  Include resources such as image files here |\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  Update your bundle to use portlet-level styling.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Import all component CSS files through the CSS file (default is\n    \\texttt{styles.css}) your bundle's \\texttt{package.json} file sets\n    for your portlet. Here's the default setting:\n  \\end{itemize}\n\n\\begin{verbatim}\n \"portlet\": {\n     \"com.liferay.portlet.header-portlet-css\": \"/css/styles.css\",\n     ...\n }\n\\end{verbatim}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Remove any CSS imports you have in your JS files\n  \\end{itemize}\n\\item\n  Update any static resource references to use the \\texttt{web-context}\n  value declared in your project's \\texttt{.npmbundlerrc} file, and\n  remove any imports for the resource. For example, if you have an image\n  file called \\texttt{logo.png} in your \\texttt{assets} folder, you\n  would use the format below. Note that the \\texttt{assets} folder is\n  not included in the path.\n\n  Here is the format:\n\n\\begin{verbatim}\n/o/[web-context]/[resource]\n\\end{verbatim}\n\n  Here's an example image resource:\n\n\\begin{verbatim}\n<img alt=\"React logo\" src=\"/o/react-guestbook-migrated/logo.png\">\n\\end{verbatim}\n\\item\n  Merge your entry module with \\texttt{src/index.js}, configuring it to\n  dynamically load components.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Components must be loaded dynamically to attach to the portlet's\n DOM. The DOM is determined at run time when the portlet's page is\n rendered. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n-   Use the `HashRouter` for routing between component views, as Liferay DXP \n    requires hash routing for proper portal navigation:\n\n   ```javascript\n    import { HashRouter as Router } from 'react-router-dom';\n   ```\n\n-   Place your code inside the `main()` function.\n\n-   Render your app inside the `portletElementId` element that is passed in \n    the `main()` function. This is required to render the React app inside\n    the portlet.\n\nYour entry module `index.js` should look like this. \n\n```javascript\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n//import './index.css';//removed for Portal Migration\nimport App from './App';\nimport { HashRouter as Router } from 'react-router-dom';\n\nexport default function main({portletNamespace, contextPath, \nportletElementId}) {\n      ReactDOM.render((\n        <Router>\n          <App/>\n        </Router>\n      ), document.getElementById(portletElementId));\n}\n```\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{6}\n\\item\n  Merge your app \\texttt{package.json} file's \\texttt{dependencies} and\n  \\texttt{devDependencies} into the bundle's \\texttt{package.json}.\n\\item\n  Finally, deploy your bundle:\n\n\\begin{verbatim}\nnpm run deploy\n\\end{verbatim}\n\\end{enumerate}\n\nCongratulations! Your React app is deployed and now available as a\nwidget that you can add to site pages.\n\nThe Liferay npm Bundler confirms the deployment:\n\n\\begin{verbatim}\nReport written to liferay-npm-bundler-report.html\nDeployed my-react-guestbook-1.0.0.jar to c:\\git\\bundles\n\\end{verbatim}\n\nThe Liferay DXP console confirms your bundle started:\n\n\\begin{verbatim}\n2019-03-22 20:17:53.181 INFO  \n[fileinstall-C:/git/bundles/osgi/modules][BundleStartStopLogger:39] \nSTARTED my-react-guestbook_1.0.0 [1695]\n\\end{verbatim}\n\nTo Find your widget, click the \\emph{Add} icon\n(\\includegraphics{./images/icon-add-app.png}), navigate to\n\\emph{Widgets} and then the category you specified to the Liferay Bundle\nGenerator (\\emph{Sample} is the default category).\n\n\\section{Related Topics}\\label{related-topics-1}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/web-services}{Web\nServices}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localization}{Localization}\n\n\\chapter{Developing a Vue\nApplication}\\label{developing-a-vue-application}\n\nRunning an existing Vue app on Liferay DXP makes the app available as a\nwidget for using on site pages. You can\n\\href{/docs/7-2/reference/-/knowledge_base/r/adapting-existing-apps-to-run-on-product}{adapt\nyour existing Vue app}, but this doesn't give you access to the bundler\nand its various loaders to develop your project further in Liferay DXP.\nTo have access to all of Liferay DXP's features, you must use the\nLiferay JS Generator and Liferay npm Bundler to merge your files into a\nportlet bundle, update your static resource paths, and deploy your\nbundle. The steps below demonstrate how to prepare a Vue app that uses\nsingle file components (\\texttt{.vue} files) with multiple views.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/appdev-vue-migrated.png}\n\\caption{Vue Apps like this Guestbook App are easy to deploy, and they\nlook great in Liferay DXP.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} if you have a tree of components expressed as\n\\texttt{.vue} templates, only the root one will be available as a true\nAMD module.\n\n\\noindent\\hrulefill\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Using \\href{https://www.npmjs.com/}{npm}, install the Liferay JS\n  Generator:\n\n\\begin{verbatim}\nnpm install -g yo generator-liferay-js\n\\end{verbatim}\n\\item\n  Generate a Vue based portlet bundle project:\n\n\\begin{verbatim}\nyo liferay-js\n\\end{verbatim}\n\n  Select \\texttt{Vue\\ based\\ portlet} and opt for generating sample\n  code. Here's the bundle's structure:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{my-vue-portlet-bundle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{assets/} → CSS and resources\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{css/} → CSS not included in \\texttt{.vue} files.\n      \\end{itemize}\n    \\item\n      \\texttt{features/} → Liferay DXP bundle features\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{localization/} → Resource bundles\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{Language.properties} → Default language keys\n        \\end{itemize}\n      \\item\n        \\texttt{settings.json} → Placeholder System Settings\n      \\end{itemize}\n    \\item\n      \\texttt{src/} → JavaScript and Vue files\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{index.js} → Main module used to initialize the portlet\n      \\end{itemize}\n    \\item\n      \\texttt{.babelrc} → Babel configuration\n    \\item\n      \\texttt{.npmbuildrc} → Build configuration\n    \\item\n      \\texttt{.npmbundlerrc} → Bundler configuration\n    \\item\n      \\texttt{package.json} → npm bundle configuration\n    \\item\n      \\texttt{README.md}\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  Copy your app files, matching the types listed below, into your new\n  project.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n File type | Destination | Comments |\n --------- | ----------- | -------- |\n CSS  | `assets/css/` | Overwrite `styles.css`. |\n Static resources | `assets` |  Include resources such as image files here |\n VUE and JS| `src` | Merge your main component with the existing `index.js`. More info on that below. |\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  Update your bundle to use portlet-level styling.\n\n  \\begin{itemize}\n  \\item\n    If you have internal CSS included with\n    \\texttt{\\textless{}style\\textgreater{}} tags in your \\texttt{.vue}\n    files, import \\texttt{.index.css} in \\texttt{/assets/styles.css}.\n    This is generated by the modified build script further down:\n\n\\begin{verbatim}\n@import '../index.css';\n\\end{verbatim}\n  \\item\n    Import all custom CSS files (i.e.~CSS not included in \\texttt{.vue}\n    files) through the CSS file (default is \\texttt{styles.css}) your\n    bundle's \\texttt{package.json} file sets for your portlet. Here's\n    the default setting:\n\n\\begin{verbatim}\n\"portlet\": {\n    \"com.liferay.portlet.header-portlet-css\": \"/css/styles.css\",\n...\n}\n\\end{verbatim}\n  \\end{itemize}\n\\item\n  Update any static resource references to use the \\texttt{web-context}\n  value declared in your project's \\texttt{.npmbundlerrc} file. Here's\n  the format:\n\n\\begin{verbatim}\n/o/[web-context]/[resource]\n\\end{verbatim}\n\n  Here's an example image resource:\n\n\\begin{verbatim}\n<img alt=\"Vue logo\" src=\"/o/vue-guestbook-migrated/logo.png\">\n\\end{verbatim}\n\\item\n  Merge your entry module with \\texttt{src/index.js}, following these\n  steps to dynamically load components.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Components must be loaded dynamically to attach to the portlet's\n DOM. The DOM is determined at runtime when the portlet's page is\n rendered. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n-   Use Vue's runtime + compiler module \n    (`import Vue from 'vue/dist/vue.common';`) so you don't have to process \n    templates during build time. This is imported by default at the top of \n    the file.\n\n-   Remove the sample content from the `main()` function (i.e. the `node` \n    constant and its use), and replace it with your router code.\n\n-   Make these updates to the `new Vue` instance:\n\n    -   Remove the default data properties (the ones you just removed in the \n        sample content), and set the render element to `portletElementId`. \n        This is required and ensures that your app is rendered inside the \n        portlet.\n    -   Add the router.\n    -   Add a render function that mounts your component wrapper to the Vue \n        instance and displays it.\n        \n    Your updated configuration should look like this:\n\n    ```javascript\n    new Vue({\n      el: `#${portletElementId}`,\n      render: h => h(App),\n      router\n    })\n    ```\n\nYour entry module `index.js` should look like this. \n\n```javascript\nimport Vue from 'vue/dist/vue.common';\nimport App from './App.vue'\nimport VueRouter from 'vue-router'\n//Component imports\n\nexport default function main({portletNamespace, contextPath, portletElementId}) {\n\n  Vue.config.productionTip = false\n\n  Vue.use(VueRouter)\n\n  const router = new VueRouter({\n      routes: [\n          {\n            ...\n          }\n      ]\n  })\n  new Vue({\n    el: `#${portletElementId}`,\n    render: h => h(App),\n    router\n  })\n}\n```\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{6}\n\\item\n  Merge your app \\texttt{package.json} file's \\texttt{dependencies} and\n  \\texttt{devDependencies} into the project's \\texttt{package.json}, and\n  replace the \\texttt{babel-cli} and \\texttt{babel-preset-env} dev\n  dependencies with the newer \\texttt{\"@babel/cli\":\\ \"\\^{}7.0.0\"} and\n  \\texttt{\"@babel/preset-env\":\\ \"\\^{}7.4.2\"} packages instead. Also\n  include the \\texttt{\"vueify\":\\ \"9.4.1\"} dev dependency.\n\\item\n  Update the \\texttt{.babelrc} file to use \\texttt{@babel/preset-env}\n  instead of \\texttt{env}:\n\n\\begin{verbatim}\n\"presets\": [\"@babel/preset-env\"]\n\\end{verbatim}\n\\item\n  If you're using \\texttt{.vue} files, replace the build script in the\n  \\texttt{package.json} with the one below to use\n  \\texttt{vue-cli-service}. The updated build script uses vue-cli to\n  access the main entrypoint for the app (\\texttt{index.js} in the\n  example below) and combines all the Vue templates and JS files into\n  one single file named \\texttt{index.common.js} and generates an\n  \\texttt{index.css} file for any internal CSS included with\n  \\texttt{\\textless{}style\\textgreater{}} tags in \\texttt{.vue} files:\n\n\\begin{verbatim}\n\"scripts\": {\n  \"build\": \"babel --source-maps -d build src && vue-cli-service build --dest \n  build/ --formats commonjs --target lib --name index ./src/index.js && npm \n  run copy-assets && liferay-npm-bundler\",\n  \"copy-assets\": \"lnbs-copy-assets\",\n  \"deploy\": \"npm run build && lnbs-deploy\",\n  \"start\": \"lnbs-start\"\n}\n\\end{verbatim}\n\\item\n  Update the \\texttt{main} entry of the \\texttt{package.json} to match\n  the new \\href{http://www.commonjs.org/}{CommonJS} file name specified\n  in the previous step:\n\n\\begin{verbatim}\n\"main\": \"index.common\"\n\\end{verbatim}\n\\item\n  Finally, deploy your portlet bundle:\n\n\\begin{verbatim}\nnpm run deploy\n\\end{verbatim}\n\\end{enumerate}\n\nCongratulations! Your Vue app is deployed and now available as a widget\nthat you can add to site pages.\n\nThe liferay-npm-bundler confirms the deployment:\n\n\\begin{verbatim}\nReport written to liferay-npm-bundler-report.html\nDeployed my-vue-guestbook-1.0.0.jar to c:\\git\\bundles\n\\end{verbatim}\n\nThe Liferay DXP console confirms your bundle started:\n\n\\begin{verbatim}\n2019-03-22 20:17:53.181 INFO  \n[fileinstall-C:/git/bundles/osgi/modules][BundleStartStopLogger:39] \nSTARTED my-vue-guestbook_1.0.0 [1695]\n\\end{verbatim}\n\nFind your widget by selecting the \\emph{Add} icon\n(\\includegraphics{./images/icon-add-app.png}) and navigating to\n\\emph{Widgets} and the category you specified to the Liferay Bundle\nGenerator (\\emph{Sample} is the default category).\n\n\\section{Related Topics}\\label{related-topics-2}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/web-services}{Web\nServices}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localization}{Localization}\n\n\\chapter{Liferay MVC Portlet}\\label{liferay-mvc-portlet}\n\nIf you're an experienced developer, this is not the first time you've\nheard about Model View Controller. If there are so many implementations\nof MVC frameworks in Java, why did Liferay create yet another one? Stay\nwith us and you'll see that Liferay MVC Portlet provides these benefits:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  It's lightweight, as opposed to many other Java MVC frameworks.\n\\item\n  There are no special configuration files that need to be kept in sync\n  with your code.\n\\item\n  It's a simple extension of\n  \\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/GenericPortlet.html}{\\texttt{GenericPortlet}}.\n\\item\n  You avoid writing a bunch of boilerplate code, since Liferay's MVC\n  Portlet framework only looks for some pre-defined parameters when the\n  \\texttt{init()} method is called.\n\\item\n  The controller can be broken down into MVC command classes, each of\n  which handles the controller code for a particular\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{portlet phase}\n  (render, action, and resource serving phases).\n\\item\n  An MVC command class can serve multiple portlets.\n\\item\n  Liferay's portlets use it. That means there are plenty of robust\n  implementations to reference when you need to design or troubleshoot\n  your Liferay applications.\n\\end{itemize}\n\nThe Liferay MVC Portlet framework is light and easy to use. The default\n\\href{/docs/7-2/reference/-/knowledge_base/r/using-the-mvc-portlet-template}{\\texttt{MVCPortlet}\nproject} template generates a fully configured and working project.\n\nHere, you'll learn how MVCPortlet works by covering these topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[mvc-layers-and-modularity]{MVC layers and modularity}\n\\item\n  \\hyperref[liferay-mvc-command-classes]{Liferay MVC command classes}\n\\item\n  \\hyperref[liferay-mvc-portlet-component]{Liferay MVC portlet\n  component}\n\\item\n  \\hyperref[a-simpler-mvc-portlet]{Simple MVC portlets}\n\\end{itemize}\n\nReview how each layer of the Liferay MVC portlet framework helps you\nseparate the concerns of your application.\n\n\\section{MVC Layers and Modularity}\\label{mvc-layers-and-modularity}\n\nIn MVC, there are three layers, and you can probably guess what they\nare.\n\n\\textbf{Model:} The model layer holds the application data and logic for\nmanipulating it.\n\n\\textbf{View:} The view layer contains logic for displaying data.\n\n\\textbf{Controller:} The middle man in the MVC pattern, the Controller\ncontains logic for passing the data back and forth between the view and\nthe model layers.\n\nLiferay DXP's applications are divided into multiple discrete modules.\nWith \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}, the model layer is generated into a \\texttt{service} and an\n\\texttt{api} module. That accounts for the model in the MVC pattern. The\nview and the controller layers share a module, the \\texttt{web} module.\n\nGenerating the skeleton for a\n\\href{/docs/7-2/reference/-/knowledge_base/r/using-the-service-builder-template}{multi-module\nService Builder-driven MVC application} saves you lots of time and gets\nyou started on the more important (and interesting, if we're being\nhonest) development work.\n\n\\section{Liferay MVC Command Classes}\\label{liferay-mvc-command-classes}\n\nIn a larger application, your \\texttt{-Portlet} class can become\nmonstrous and unwieldy if it holds all of the controller logic. Liferay\nprovides MVC command classes to break up your controller functionality.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCActionCommand.html}{\\texttt{MVCActionCommand}}:}\n  Use \\texttt{-ActionCommand} classes to hold each of your portlet\n  actions, which are invoked by action URLs.\n\\item\n  \\textbf{\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html}{\\texttt{MVCRenderCommand}}:}\n  Use \\texttt{-RenderCommand} classes to hold a \\texttt{render} method\n  that dispatches to the appropriate JSP, by responding to render URLs.\n\\item\n  \\textbf{\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html}{\\texttt{MVCResourceCommand}}:}\n  Use \\texttt{-ResourceCommand} classes to serve resources based on\n  resource URLs.\n\\end{itemize}\n\nThere must be some confusing configuration files to keep everything\nwired together and working properly, right? Wrong: it's all easily\nmanaged in the \\texttt{-Portlet} class's\n\\href{https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html}{\\texttt{@Component}}\nannotation.\n\n\\section{Liferay MVC Portlet\nComponent}\\label{liferay-mvc-portlet-component}\n\nWhether or not you plan to split up the controller into MVC command\nclasses, the portlet \\texttt{@Component} annotation configures the\nportlet. Here's a simple portlet component as an example:\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"com.liferay.portlet.css-class-wrapper=portlet-hello-world\",\n        \"com.liferay.portlet.display-category=category.sample\",\n        \"com.liferay.portlet.icon=/icons/hello_world.png\",\n        \"com.liferay.portlet.preferences-owned-by-group=true\",\n        \"com.liferay.portlet.private-request-attributes=false\",\n        \"com.liferay.portlet.private-session-attributes=false\",\n        \"com.liferay.portlet.remoteable=true\",\n        \"com.liferay.portlet.render-weight=50\",\n        \"com.liferay.portlet.use-default-template=true\",\n        \"javax.portlet.display-name=Hello World\",\n        \"javax.portlet.expiration-cache=0\",\n        \"javax.portlet.init-param.always-display-default-configuration-icons=true\",\n        \"javax.portlet.name=\" + HelloWorldPortletKeys.HELLO_WORLD,\n        \"javax.portlet.resource-bundle=content.Language\",\n        \"javax.portlet.security-role-ref=guest,power-user,user\",\n        \"javax.portlet.supports.mime-type=text/html\"\n    },\n    service = Portlet.class\n)\npublic class HelloWorldPortlet extends MVCPortlet {\n}\n\\end{verbatim}\n\nThe \\texttt{javax.portlet.name} property is required. When using MVC\ncommands, the \\texttt{javax.portlet.name} property value links\nparticular portlet URL/command combinations to the correct portlet.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Make your portlet name unique, considering how\n\\href{/docs/7-2/reference/-/knowledge_base/r/portlet-descriptor-to-osgi-service-property-map\\#ten}{Liferay\nDXP uses the name to create the portlet's ID}.\n\n\\noindent\\hrulefill\n\nThere can be some confusion over exactly what kind of\n\\texttt{Portlet.class} implementation you're publishing with a\ncomponent. The service registry expects this to be the\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/Portlet.html}{\\texttt{javax.portlet.Portlet}}\ninterface. Import that, and not, for example,\n\\texttt{com.liferay.portal.kernel.model.Portlet}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The DTD\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/definitions/liferay-portlet-app_7_2_0.dtd.html}{liferay-portlet-app\\_7\\_2\\_0.dtd}\ndefines all the Liferay-specific attributes you can specify as\nproperties in your portlet components.\n\nConsider the \\texttt{\\textless{}css-class-wrapper\\textgreater{}} element\nfrom the above link as an example. To specify that property in your\ncomponent, use this syntax in your property list:\n\n\\texttt{\"com.liferay.portlet.css-class-wrapper=portlet-hello-world\",}\n\nThe properties namespaced with \\texttt{javax.portlet.} are elements of\nthe\n\\href{https://docs.liferay.com/portlet-api/3.0/portlet-app_3_0.xsd}{\\texttt{portlet.xml}\ndescriptor}.\n\n\\noindent\\hrulefill\n\n\\section{A Simpler MVC Portlet}\\label{a-simpler-mvc-portlet}\n\nIn simpler applications, you don't use MVC commands. Your portlet render\nURLs specify JSP paths in \\texttt{mvcPath} parameters.\n\n\\begin{verbatim}\n<portlet:renderURL var=\"addEntryURL\">\n    <portlet:param name=\"mvcPath\" value=\"/entry/edit_entry.jsp\" />\n    <portlet:param name=\"redirect\" value=\"<%= redirect %>\" />\n</portlet:renderURL>\n\\end{verbatim}\n\nAs you've seen, Liferay's MVC Portlet framework gives you a\nwell-structured controller layer that takes very little time to\nimplement. With all your free time, you could\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Learn a new language\n\\item\n  Take pottery classes\n\\item\n  Lift weights\n\\item\n  Work on your application's business logic\n\\end{itemize}\n\nIt's entirely up to you.\n\nTo get into the details of creating an MVC Portlet application, continue\nwith\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-an-mvc-portlet}{Creating\nan MVC Portlet}.\n\n\\chapter{Creating an MVC Portlet}\\label{creating-an-mvc-portlet}\n\nGenerating MVC portlet projects is a snap using Liferay's project\ntemplates. Here you'll generate an MVC Portlet project and deploy the\nportlet to Liferay DXP.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Generate an\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-mvc-portlet-template}{MVC\n  Portlet project} using a Gradle or Maven.\n\n  Here's the resulting folder structure for an MVC Portlet class named\n  \\texttt{MyMvcPortlet} in a base package\n  \\texttt{com.liferay.docs.mvcportlet}:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{my-mvc-portlet-project} → Arbitrary project name.\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{gradle}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{wrapper}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{gradle-wrapper.jar}\n        \\item\n          \\texttt{gradle-wrapper.properties}\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{src}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{main}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{java}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{com/liferay/docs/mvcportlet}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{constants}\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                \\texttt{MyMvcPortletKeys.java} → Declares portlet\n                constants.\n              \\end{itemize}\n            \\item\n              \\texttt{portlet}\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                \\texttt{MyMvcPortlet.java} → MVC Portlet class.\n              \\end{itemize}\n            \\end{itemize}\n          \\end{itemize}\n        \\item\n          \\texttt{resources}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{content}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{Language.properties} → Resource bundle\n            \\end{itemize}\n          \\item\n            \\texttt{META-INF}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{resources}\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                \\texttt{init.jsp} → Imports classes and taglibs and\n                defines commonly used objects from the theme and the\n                portlet.\n              \\item\n                \\texttt{view.jsp} → Default view template.\n              \\end{itemize}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{bnd.bnd} → OSGi bundle metadata.\n    \\item\n      \\texttt{build.gradle}\n    \\item\n      \\texttt{gradlew}\n    \\end{itemize}\n  \\end{itemize}\n\n  The Maven-generated project includes a \\texttt{pom.xml} file and does\n  not include the Gradle-specific files, but otherwise is exactly the\n  same.\n\n  Here's the resulting MVC Portlet class:\n\\end{enumerate}\n\n\\begin{verbatim}\npackage com.liferay.docs.mvcportlet.portlet;\n\nimport com.liferay.docs.mvcportlet.constants.MyMvcPortletKeys;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport javax.portlet.Portlet;\nimport org.osgi.service.component.annotations.Component;\n\n@Component(\n    immediate = true,\n    property = {\n        \"com.liferay.portlet.display-category=category.sample\",\n        \"com.liferay.portlet.instanceable=true\",\n        \"javax.portlet.display-name=my-mvc-portlet-project Portlet\",\n        \"javax.portlet.init-param.template-path=/\",\n        \"javax.portlet.init-param.view-template=/view.jsp\",\n        \"javax.portlet.name=\" + MyMvcPortletKeys.MyMvc,\n        \"javax.portlet.resource-bundle=content.Language\",\n        \"javax.portlet.security-role-ref=power-user,user\"\n    },\n    service = Portlet.class\n)\npublic class MyMvcPortlet extends MVCPortlet {\n}\n\\end{verbatim}\n\nThe class extends\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCPortlet.html}{\\texttt{MVCPortlet}}.\nThe\n\\href{https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html}{\\texttt{@Component}}\nannotation and \\texttt{service\\ =\\ Portlet.class} attribute makes the\nclass an OSGi Declarative Services component that provides the\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/Portlet.html}{\\texttt{javax.portlet.Portlet}}\nservice type. The \\texttt{immediate\\ =\\ true} attribute activates the\nservice immediately on the portlet's deployment.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Set any portlet configuration or Liferay portlet configuration values\n  using \\texttt{javax.portlet.*} and \\texttt{com.liferay.portlet.*}\n  \\texttt{@Component} annotation properties\n  \\href{/docs/7-2/reference/-/knowledge_base/r/portlet-descriptor-to-osgi-service-property-map}{\\texttt{javax.portlet.*}\n  and \\texttt{com.liferay.portlet.*} \\texttt{@Component} annotation\n  properties} respectively.\n\n  Here are the example component's properties:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{\"com.liferay.portlet.display-category=category.sample\"}:\n    Sets the Widget's category to ``Sample''.\n  \\item\n    \\texttt{\"com.liferay.portlet.instanceable=true\"}: Activates the\n    component immediately when its bundle installs.\n  \\item\n    \\texttt{\"javax.portlet.display-name=my-mvc-portlet-project\\ Portlet\"}:\n    Sets the portlet's Widget name.\n  \\item\n    \\texttt{\"javax.portlet.init-param.template-path=/\"}: The path under\n    \\texttt{src/main/resources/META-INF/resources/} where the templates\n    reside.\n  \\item\n    \\texttt{\"javax.portlet.init-param.view-template=/view.jsp\"}: Default\n    view template.\n  \\item\n    \\texttt{\"javax.portlet.name=\"\\ +\\ MyMvcPortletKeys.MyMvc}: The\n    portlet's unique identity.\n  \\item\n    \\texttt{\"javax.portlet.resource-bundle=content.Language\"}: Sets the\n    portlet's\n    \\href{/docs/7-2/frameworks/-/knowledge_base/f/localization}{resource\n    bundle} to the \\texttt{content/Language*.properties} file(s) in the\n    \\texttt{src/main/resources/} folder.\n  \\item\n    \\texttt{\"javax.portlet.security-role-ref=power-user,user\"}: Makes\n    the Liferay DXP virtual instance's power user and user Roles\n    available for defining the portlet's permissions.\n  \\end{itemize}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** To opt-in to Portlet 3.0 features, set the component property\n `\"javax.portlet.version=3.0\"`.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  The portlet renders content via the view template\n  \\texttt{src/main/resources/META-INF/resources/view.jsp} by default.\n\\item\n  Build your project.\n\n  \\emph{Gradle:}\n\n\\begin{verbatim}\ngradlew jar\n\\end{verbatim}\n\n  \\emph{Maven:}\n\n\\begin{verbatim}\nmvn clean package\n\\end{verbatim}\n\\item\n  Deploy the project\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{using\n  your build environment} or by building the project JAR and copying it\n  to the \\texttt{deploy/} folder in your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}.\n\\end{enumerate}\n\nThe MVC Portlet is now available in the Liferay DXP UI, in the Widget\ncategory you assigned it.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/default-mvc-portlet-on-page.png}\n\\caption{The example portlet shows a message defined by the language\nproperty \\texttt{yourmvc.caption=Hello\\ from\\ YourMVC!} in the\nLanguage.properties file.}\n\\end{figure}\n\nCongratulations on creating and deploying an MVC Portlet!\n\n\\section{Related Topics}\\label{related-topics-3}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/writing-mvc-portlet-controller-code}{Writing\nMVC Portlet Controller Code}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer}{Configuring\nthe View Layer}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-action-command}{MVC Action\nCommand}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-render-command}{MVC Render\nCommand}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-resource-command}{MVC\nResource Command}\n\n\\chapter{Writing MVC Portlet Controller\nCode}\\label{writing-mvc-portlet-controller-code}\n\nIn MVC, your controller is a traffic director: it provides data to the\nright front-end view for display to the user, and it takes data the user\nentered in the front-end and passes it to the right back-end service.\nFor this reason, the controller must process requests from the\nfront-end, and it must determine the right front-end view to pass data\nback to the user.\n\nIf you have a small application that's not heavy on controller logic,\nyou can put all your controller code in the \\texttt{-Portlet} class. If\nyou have more complex needs (lots of actions, complex render logic to\nimplement, or maybe even some resource serving code), consider breaking\nthe controller into\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-render-command}{MVC Render\nCommand classes},\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-action-command}{MVC Action\nCommand classes}, and\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-resource-command}{MVC\nResource Command classes}. Here you'll implement controller logic for\nsmall applications, where all the controller code is in the\n\\texttt{-Portlet} class. It involves these things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Action methods\n\\item\n  Render logic\n\\item\n  Setting and retrieving request parameters and attributes\n\\end{itemize}\n\nStart with creating action methods.\n\n\\section{Action Methods}\\label{action-methods}\n\nYour portlet class can act as your controller by itself and process\nrequests using action methods. Here's a sample action method:\n\n\\begin{verbatim}\npublic void addGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException, SystemException {\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n        Guestbook.class.getName(), request);\n\n    String name = ParamUtil.getString(request, \"name\");\n\n    try {\n        _guestbookService.addGuestbook(serviceContext.getUserId(),\n                name, serviceContext);\n\n        SessionMessages.add(request, \"guestbookAdded\");\n\n    } catch (Exception e) {\n        SessionErrors.add(request, e.getClass().getName());\n\n        response.setRenderParameter(\"mvcPath\",\n            \"/html/guestbook/edit_guestbook.jsp\");\n    }\n\n}\n\\end{verbatim}\n\nThis action method has one job: call a service to add a guestbook. If\nthe call succeeds, the message \\texttt{\"guestbookAdded\"} is associated\nwith the request and added to the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/servlet/SessionMessages.html}{\\texttt{SessionMessages}\nobject}. If an exception is thrown, it's caught, and the class name is\nassociated with the request and added to the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/servlet/SessionErrors.html}{\\texttt{SessionErrors}\nobject}, and the response is set to render \\texttt{edit\\_guestbook.jsp}.\nSetting the \\texttt{mvcPath} render parameter is a Liferay\n\\texttt{MVCPortlet} framework convention that denotes the next view to\nrender to the user.\n\nWhile action methods respond to user actions, render logic determines\nthe view to display to the user. Render logic is next.\n\n\\section{Render Logic}\\label{render-logic}\n\nHere's how MVC Portlet determines which view to render. Note the\n\\texttt{init-param} properties you set in your component:\n\n\\begin{verbatim}\n\"javax.portlet.init-param.template-path=/\",\n\"javax.portlet.init-param.view-template=/view.jsp\",\n\\end{verbatim}\n\nThe \\texttt{template-path} property tells the MVC framework where your\nJSP files live. In the above example, \\texttt{/} means that the JSP\nfiles are in your project's root \\texttt{resources} folder. That's why\nit's important to follow Liferay's standard folder structure. The\n\\texttt{view-template} property directs the default rendering to\n\\texttt{view.jsp}.\n\nHere's the path of a hypothetical Web module's resource folder:\n\n\\begin{verbatim}\ndocs.liferaymvc.web/src/main/resources/META-INF/resources\n\\end{verbatim}\n\nBased on that resource folder, the \\texttt{view.jsp} file is found at\n\n\\begin{verbatim}\ndocs.liferaymvc.web/src/main/resources/META-INF/resources/view.jsp\n\\end{verbatim}\n\nand that's the application's default view. When the portlet's\n\\texttt{init} method (e.g., your portlet's override of\n\\texttt{MVCPortlet.init()}) is called, Liferay reads the initialization\nparameters you specify and directs rendering to the default JSP.\nThroughout the controller, you can render different views (JSP files) by\nsetting the render parameter \\texttt{mvcPath} like this:\n\n\\begin{verbatim}\nactionResponse.setRenderParameter(\"mvcPath\", \"/error.jsp\");\n\\end{verbatim}\n\nYou can avoid render logic by using initialization parameters and render\nparameters, but most of the time you'll override the portlet's\n\\texttt{render} method. Here's an example:\n\n\\begin{verbatim}\n@Override\npublic void render(RenderRequest renderRequest,\n        RenderResponse renderResponse) throws PortletException, IOException {\n\n    try {\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n                Guestbook.class.getName(), renderRequest);\n\n        long groupId = serviceContext.getScopeGroupId();\n\n        long guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n        List<Guestbook> guestbooks = _guestbookService\n                .getGuestbooks(groupId);\n\n        if (guestbooks.size() == 0) {\n            Guestbook guestbook = _guestbookService.addGuestbook(\n                    serviceContext.getUserId(), \"Main\", serviceContext);\n\n            guestbookId = guestbook.getGuestbookId();\n\n        }\n\n        if (!(guestbookId > 0)) {\n            guestbookId = guestbooks.get(0).getGuestbookId();\n        }\n\n        renderRequest.setAttribute(\"guestbookId\", guestbookId);\n\n    } catch (Exception e) {\n\n        throw new PortletException(e);\n    }\n\n    super.render(renderRequest, renderResponse);\n\n}\n\\end{verbatim}\n\nThis render logic provides the view layer with data to display to the\nuser. The \\texttt{render} method above sets the render request attribute\n\\texttt{guestbookId} with the ID of a guestbook to display. If\nguestbooks exist, it chooses the first. Otherwise, it creates a\nguestbook and sets it to display. Lastly the method passes the render\nrequest and render response objects to the base class via its\n\\texttt{render} method.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Are you wondering how to call\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder services} in 7.0? In short, obtain a reference to the service by\nannotating one of your fields of that service type with the\n\\texttt{@Reference}\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{Declarative\nServices} annotation.\n\n\\texttt{@Reference\\ \\ private\\ GuestbookService\\ \\_guestbookService;}\n\nOnce done, you can call the service's methods.\n\n\\texttt{\\_guestbookService.addGuestbook(serviceContext.getUserId(),\\ \"Main\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ serviceContext);}\n\n\\noindent\\hrulefill\n\nBefore venturing into the view layer, the next section demonstrates ways\nto pass information between the controller and view layers.\n\n\\section{Setting and Retrieving Request and Response Parameters and\nAttributes}\\label{setting-and-retrieving-request-and-response-parameters-and-attributes}\n\nA handy utility class called\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}}\nfacilitates retrieving parameters from an\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/ActionRequest.html}{\\texttt{ActionRequest}}\nor a\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/RenderRequest.html}{\\texttt{RenderRequest}}.\n\nFor example, this JSP passes a parameter named \\texttt{guestbookId} in\nan action URL.\n\n\\begin{verbatim}\n<portlet:actionURL name=\"doSomething\" var=\"doSomethingURL\">\n    <portlet:param name=\"guestbookId\" \n            value=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n</portlet:actionURL>\n\\end{verbatim}\n\nThe \\texttt{\\textless{}portlet:actionURL\\textgreater{}} tag's\n\\texttt{name} attribute maps the action URL to a controller action\nmethod named \\texttt{doSomething}. Triggering an action URL invokes the\ncorresponding method in the controller.\n\nThe controller's \\texttt{doSomething} method referenced in this example\ngets the \\texttt{guestbookId} parameter value from the\n\\texttt{ActionRequest}.\n\n\\begin{verbatim}\nlong guestbookId = ParamUtil.getLong(actionRequest, \"guestbookId\");\n\\end{verbatim}\n\nTo pass information back to the view layer, the controller code can set\nrender parameters on response objects.\n\n\\begin{verbatim}\nactionResponse.setRenderParameter(\"mvcPath\", \"/error.jsp\");\n\\end{verbatim}\n\nThe code above sets a parameter called \\texttt{mvcPath} to JSP path\n\\texttt{/error.jsp}. This causes the controller's render method to\nredirect the user to that JSP.\n\nYour controller class can also set attributes into response objects\nusing the \\texttt{setAttribute} method.\n\n\\begin{verbatim}\nrenderResponse.setAttribute(\"guestbookId\", guestbookId);\n\\end{verbatim}\n\nJSPs can use Java code in scriptlets to interact with the request\nobject.\n\n\\begin{verbatim}\n<%\n    long guestbookId = Long.valueOf((Long) renderRequest\n            .getAttribute(\"guestbookId\"));\n%>\n\\end{verbatim}\n\nPassing information back and forth from your view and controller is\nimportant, but there's more to the view layer than that. The view layer\nis up next.\n\n\\section{Related Topics}\\label{related-topics-4}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-an-mvc-portlet}{Creating\nan MVC Portlet}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer}{Configuring\nthe View Layer}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-action-command}{MVC Action\nCommand}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-render-command}{MVC Render\nCommand}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-resource-command}{MVC\nResource Command}\n\n\\chapter{Configuring the View Layer}\\label{configuring-the-view-layer}\n\nThis section briefly covers how to get your view layer working, from\norganizing your imports in one JSP file, to creating URLs that direct\nprocessing to methods in your portlet class.\n\n\\noindent\\hrulefill\n\nNote: As you create JSPs, you can\n\\href{/docs/7-1/tutorials/-/knowledge_base/t/applying-clay-styles-to-your-app}{apply\nClay styles to your app to match Liferay's apps}.\n\n\\noindent\\hrulefill\n\n\\section{Using the init.jsp}\\label{using-the-init.jsp}\n\nLiferay's practice puts all Java imports, tag library declarations, and\nvariable initializations into a JSP called \\texttt{init.jsp}. If you use\n\\href{/docs/7-1/tutorials/-/knowledge_base/t/blade-cli}{Blade CLI} or\n\\href{/docs/7-1/tutorials/-/knowledge_base/t/liferay-ide}{Liferay Dev\nStudio DXP} to create a module based on the \\texttt{mvc-portlet} project\ntemplate, these taglib declarations and initializations are added\nautomatically to your \\texttt{init.jsp}:\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\" %>\n\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\" %>\n\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\" %>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\" %>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\" %>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\" %>\n\n<liferay-theme:defineObjects />\n\n<portlet:defineObjects />\n\\end{verbatim}\n\nHere are the tag libraries it gives you:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/c/tld-frame.html}{\\texttt{c}}:\n  JSTL core tags.\n\\item\n  \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/portlet/tld-frame.html}{\\texttt{portlet}}:\n  Standard portlet component tags.\n\\item\n  \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/tld-summary.html}{\\texttt{aui}}:\n  \\href{https://alloyui.com/}{AlloyUI} component tags.\n\\item\n  \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/liferay-portlet/tld-frame.html}{\\texttt{liferay-portlet}}:\n  Liferay portlet component tags.\n\\item\n  \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/liferay-theme/tld-frame.html}{\\texttt{liferay-theme}}:\n  Liferay theme component tags.\n\\item\n  \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/liferay-ui/tld-frame.html}{\\texttt{liferay-ui}}:\n  Liferay UI component tags.\n\\end{itemize}\n\nThese tags make portlet and Liferay objects available:\n\n\\begin{itemize}\n\\item\n  \\texttt{\\textless{}portlet:defineObjects\\ /\\textgreater{}}: Implicit\n  Java variables that reference Portlet API objects. The objects\n  available are limited to those available in the current portlet\n  request. For details, see the \\texttt{defineObjects} tag in\n  \\href{https://jcp.org/en/jsr/detail?id=286}{JSR-286}.\n\\item\n  \\texttt{\\textless{}liferay-theme:defineObjects\\ /\\textgreater{}}:\n  Implicit Java variables that reference Liferay objects.\n\\end{itemize}\n\nTo use all that the \\texttt{init.jsp} has, include it in your other\nJSPs:\n\n\\begin{verbatim}\n<%@include file=\"/html/init.jsp\"%>\n\\end{verbatim}\n\nA JSP uses render URLs to display other pages and action URLs to invoke\ncontroller methods.\n\n\\section{Using Render URLs}\\label{using-render-urls}\n\nA render URL attached to a UI component action displays another page.\nFor example, this render URL displays the JSP \\texttt{/path/to/foo.jsp}.\n\n\\begin{verbatim}\n<portlet:renderURL var=\"adminURL\">\n    <portlet:param name=\"mvcPath\" value=\"/path/to/foo.jsp\" />\n</portlet:renderURL>\n\\end{verbatim}\n\nHere's how to use a render URL:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/portlet/renderURL.html}{\\texttt{\\textless{}portlet:renderURL\\textgreater{}}}\n  to your JSP.\n\\item\n  Name the render URL via a \\texttt{var} attribute in the\n  \\texttt{\\textless{}portlet:renderURL\\textgreater{}} tag. The\n  \\texttt{\\textless{}portlet:renderURL\\textgreater{}} tag constructs the\n  URL and assigns it to the variable. For example, this render URL is\n  assigned to the variable named \\texttt{adminURL}:\n\n\\begin{verbatim}\n<portlet:renderURL var=\"adminURL\">\n   ...\n</portlet:renderURL>\n\\end{verbatim}\n\\item\n  As sub-element to the\n  \\texttt{\\textless{}portlet:renderURL\\textgreater{}} tag, add a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/portlet/param.html}{\\texttt{\\textless{}portlet:param\\textgreater{}}}\n  tag with the following attributes:\n\n  \\texttt{name=\"mvcPath\"}: Your controller's \\texttt{render} method\n  forwards processing to the JSP at the path specified in the\n  \\texttt{value}.\n\n  \\texttt{value=\"/path/to/foo.jsp\"}: The path to the JSP to render.\n  Replace the value \\texttt{/path/to/foo.jsp} with your JSP path.\n\n\\begin{verbatim}\n<portlet:renderURL var=\"adminURL\">\n    <portlet:param name=\"mvcPath\" value=\"/path/to/foo.jsp\" />\n</portlet:renderURL>\n\\end{verbatim}\n\\item\n  To invoke the render URL, assign its variable (\\texttt{var}) to a UI\n  component action, such as a button or navigation bar item action.\n\\end{enumerate}\n\nInvoking the UI component causes the controller's render method to\ndisplay the \\texttt{mvcPath} parameter's JSP.\n\n\\section{Using Action URLs}\\label{using-action-urls}\n\nAction methods are different because they invoke an action (i.e., code),\nrather than link to another page. For example, this action URL invokes a\ncontroller method called \\texttt{doSomething} and passes a parameter\ncalled \\texttt{redirect}. The \\texttt{redirect} parameter contains the\npath of the JSP to render after invoking the action:\n\n\\begin{verbatim}\n<portlet:actionURL name=\"doSomething\" var=\"doSomethingURL\">\n    <portlet:param name=\"redirect\" value=\"<%= redirect %>\" />\n</portlet:actionURL>\n\\end{verbatim}\n\nHere's how to use an action URL:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/portlet/actionURL.html}{\\texttt{\\textless{}portlet:actionURL\\textgreater{}}}\n  to your JSP.\n\\item\n  Add a \\texttt{name} and \\texttt{var} attribute to the\n  \\texttt{\\textless{}portlet:actionURL\\textgreater{}}. The\n  \\texttt{\\textless{}portlet:actionURL\\textgreater{}} tag constructs the\n  URL and assigns it to the \\texttt{var} variable.\n\n  \\texttt{name}: Controller action to invoke.\n\n  \\texttt{var}: Variable to assign the action URL to.\n\n\\begin{verbatim}\n<portlet:actionURL name=\"doSomething\" var=\"doSomethingURL\">\n   ...\n</portlet:actionURL>\n\\end{verbatim}\n\\item\n  As sub-element to the\n  \\texttt{\\textless{}portlet:actionURL\\textgreater{}} tag, add a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/portlet/param.html}{\\texttt{\\textless{}portlet:param\\textgreater{}}}\n  tag that has the following attributes:\n\n  \\texttt{name=\"redirect\"}: Tells the portlet to redirect to the JSP\n  associated with this parameter.\n\n  \\texttt{value=\"/path/to/foo.jsp\"}: Redirects the user to this JSP path\n  after invoking the action. Replace the value \\texttt{/path/to/bar.jsp}\n  with your JSP path.\n\n\\begin{verbatim}\n<portlet:actionURL name=\"doSomething\" var=\"doSomethingURL\">\n    <portlet:param name=\"redirect\" value=\"/path/to/bar.jsp\" />\n</portlet:actionURL>\n\\end{verbatim}\n\\item\n  To invoke the action URL, assign its variable (\\texttt{var}) to a UI\n  component action, such as a button or navigation bar item action.\n\\end{enumerate}\n\nCongratulations! Your portlet is ready for action.\n\nThese simple examples demonstrate how Liferay MVC Portlet facilitates\ncommunication between a smaller application's view layer and controller.\n\n\\section{Related Topics}\\label{related-topics-5}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/writing-mvc-portlet-controller-code}{Writing\nMVC Portlet Controller Code}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-action-command}{MVC Action\nCommand}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-render-command}{MVC Render\nCommand}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-resource-command}{MVC\nResource Command}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs}{Front-end\nTaglibs}\n\n\\href{https://portal.liferay.dev/docs/7-2/reference/-/knowledge_base/r/liferay-javascript-apis}{Liferay\nJavaScript APIs}\n\n\\chapter{MVC Action Command}\\label{mvc-action-command}\n\nLiferay's MVC Portlet framework enables you to handle\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{MVCPortlet}\n\\href{/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer\\#using-action-urls}{actions}\nin separate classes. This facilitates managing action logic in portlets\nthat have many actions. Each action URL in your portlet's JSPs invokes\nan appropriate action command class.\n\nHere are the steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer}{Configure\n  your JSPs} to use action URLs via\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/portlet/actionURL.html}{\\texttt{\\textless{}portlet:actionURL\\textgreater{}}}\n  tags. For example, the\n  \\href{https://github.com/liferay/liferay-blade-samples/blob/7.1/gradle/apps/action-command-portlet/src/main/resources/META-INF/resources/view.jsp}{action-command-portlet}\n  sample uses this action URL:\n\n\\begin{verbatim}\n<liferay-portlet:actionURL name=\"greet\" var=\"greetURL\" />\n\\end{verbatim}\n\n  Name the action URL via its \\texttt{name} attribute. Your\n  \\texttt{*MVCActionCommand} class maps to this name. Assign the\n  \\texttt{var} attribute a variable name.\n\\item\n  Assign the action URL variable (\\texttt{var}) to a UI component.\n  Acting on the UI component invokes the action. For example, the\n  sample's \\texttt{greetURL} action URL variable triggers on submitting\n  this form:\n\n\\begin{verbatim}\n<aui:form action=\"<%= greetURL %>\" method=\"post\" name=\"fm\">\n    <aui:input name=\"name\" type=\"text\" />\n\n    <aui:button-row>\n        <aui:button type=\"submit\"></aui:button>\n    </aui:button-row>\n</aui:form>\n\\end{verbatim}\n\\item\n  Create a class that implements the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCActionCommand.html}{\\texttt{MVCActionCommand}}\n  interface, or that extends the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/BaseMVCActionCommand.html}{\\texttt{BaseMVCActionCommand}}\n  class. The latter may save you time, since it already implements\n  \\texttt{MVCActionCommand}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Tip:** Naming your `*MVCActionCommand` class after the action it \n performs makes the action mappings more obvious for maintaining the code.\n For example, if your action class edits some kind of entry, you could name\n its class `EditEntryMVCActionCommand`. If your application has several MVC\n command classes, naming them this way helps differentiate them. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  Annotate your class with an\n  \\href{https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html}{\\texttt{@Component}}\n  annotation, like this one:\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"javax.portlet.name=your_portlet_name_YourPortlet\",\n        \"mvc.command.name=/your/jsp/action/url\"\n    },\n    service = MVCActionCommand.class\n)\n\\end{verbatim}\n\\item\n  Set a \\texttt{javax.portlet.name} property to your portlet's internal\n  ID.\n\n  Note, you can apply MVC Command classes to multiple portlets by\n  setting a \\texttt{javax.portlet.name} property for each portlet. For\n  example, the \\texttt{javax.portlet.name} properties in this component\n  apply it to three specific portlets.\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS,\n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_ADMIN,\n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_AGGREGATOR,\n        \"mvc.command.name=/blogs/edit_entry\"\n    },\n    service = MVCActionCommand.class\n)\npublic class EditEntryMVCActionCommand extends BaseMVCActionCommand {\n    ...\n}\n\\end{verbatim}\n\\item\n  Set the \\texttt{mvc.command.name} property to your\n  \\texttt{\\textless{}portlet:actionURL\\textgreater{}} tag's\n  \\texttt{name}. This maps your class to the action URL of the same\n  name.\n\\item\n  Register your class as an \\texttt{MVCActionCommand} service by setting\n  the \\texttt{service} attribute to \\texttt{MVCActionCommand.class}.\n\\item\n  Implement your action logic by overriding the appropriate method of\n  the class you're implementing or extending.\n\n  \\begin{itemize}\n  \\item\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCActionCommand.html}{\\texttt{MVCActionCommand}}\n    implementations override the \\texttt{processAction} method.\n  \\item\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/BaseMVCActionCommand.html}{\\texttt{BaseMVCActionCommand}}\n    extensions override the \\texttt{doProcessAction} method.\n  \\end{itemize}\n\n  Here's an example of overriding \\texttt{MVCActionCommand}'s\n  \\texttt{processAction} method. This action logic gets the name\n  parameter from the \\texttt{ActionRequest} and adds it to the session\n  messages and to an \\texttt{ActionRequest} attribute.\n\n\\begin{verbatim}\n@Override\npublic boolean processAction(\n        ActionRequest actionRequest, ActionResponse actionResponse)\n    throws PortletException {\n\n    _handleActionCommand(actionRequest);\n\n    return true;\n}\n\nprivate void _handleActionCommand(ActionRequest actionRequest) {\n    String name = ParamUtil.get(actionRequest, \"name\", StringPool.BLANK);\n\n    if (_log.isInfoEnabled()) {\n        _log.info(\"Hello \" + name);\n    }\n\n    String greetingMessage = \"Hello \" + name + \"! Welcome to OSGi\";\n\n    actionRequest.setAttribute(\"GREETER_MESSAGE\", greetingMessage);\n\n    SessionMessages.add(actionRequest, \"greetingMessage\", greetingMessage);\n}\n\nprivate static final Log _log = LogFactoryUtil.getLog(\n    GreeterActionCommand.class);\n\\end{verbatim}\n\\end{enumerate}\n\nCongratulations! You've created an \\texttt{MVCActionCommand} that\nhandles your portlet actions.\n\n\\section{Related Topics}\\label{related-topics-6}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-an-mvc-portlet}{Creating\nan MVC Portlet}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer}{Configuring\nthe View Layer}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-render-command}{MVC Render\nCommand}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-resource-command}{MVC\nResource Command}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-liferay-mvc-commands}{MVC\nCommand Overrides}\n\n\\chapter{MVC Render Command}\\label{mvc-render-command}\n\n\\texttt{MVCRenderCommand}s are classes that respond to\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{MVCPortlet}\n\\href{/docs/7-2/appdev/-/knowledge_base/a/writing-mvc-portlet-controller-code\\#render-logic}{render\nURLs}. If your render logic is simple and you want to implement all of\nyour render logic in your portlet class, see\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{Writing\nMVC Portlet Controller Code}. If your render logic is complex or you\nwant clean separation between render paths, use\n\\texttt{MVCRenderCommand}s. Each render URL in your portlet's JSPs\ninvokes an appropriate render command class.\n\nHere are the steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer}{Configure\n  your JSPs} to generate render URLs via\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/portlet/renderURL.html}{\\texttt{\\textless{}portlet:renderURL\\textgreater{}}}\n  tags.\n\n  For example, this\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.1/gradle/apps/render-command-portlet}{render-command-portlet}\n  sample render URL invokes an MVC render command named\n  \\texttt{/blade/render}.\n\n\\begin{verbatim}\n<portlet:renderURL var=\"bladeRender\">\n    <portlet:param name=\"mvcRenderCommandName\" value=\"/blade/render\" />\n</portlet:renderURL>\n\\end{verbatim}\n\\item\n  Name the render URL via its\n  \\texttt{\\textless{}portlet:param\\textgreater{}} named\n  \\texttt{mvcRenderCommandName}. The render URL and\n  \\texttt{*MVCRenderCommand} class (demonstrated later) map to the\n  \\texttt{mvcRenderCommandName} value.\n\\item\n  Assign the \\texttt{\\textless{}portlet:renderURL\\textgreater{}}'s\n  \\texttt{var} attribute a variable name to pass to a UI component.\n\\item\n  Assign the render URL variable (\\texttt{var}) to a UI component. When\n  the user triggers the UI component, the \\texttt{*MVCRenderCommand}\n  class that matches the render URL handles the render request.\n\n  For example, the render URL with the variable \\texttt{bladeRender}\n  triggers on users clicking this button.\n\n\\begin{verbatim}\n<aui:button href=\"<%= bladeRender %>\" value=\"goto page render\" />\n\\end{verbatim}\n\\item\n  Create a class that implements the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html}{\\texttt{MVCRenderCommand}}\n  interface.\n\\item\n  Annotate the class with an\n  \\href{https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html}{\\texttt{@Component}}\n  annotation, like this one:\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"javax.portlet.name=com_liferay_blade_samples_portlet_rendercommand_BladeRenderPortlet\",\n        \"mvc.command.name=/blade/render\"\n    },\n    service = MVCRenderCommand.class\n)\n\\end{verbatim}\n\\item\n  Set a \\texttt{javax.portlet.name} property to your portlet's internal\n  ID.\n\\item\n  Set a \\texttt{mvc.command.name} property to your\n  \\texttt{\\textless{}portlet:renderURL\\textgreater{}} tag\n  \\texttt{mvcRenderCommandName} portlet parameter value. This maps your\n  class to the render URL.\n\\item\n  Register your class as an \\texttt{MVCRenderCommand} service by setting\n  the \\texttt{service} attribute to \\texttt{MVCRenderCommand.class}.\n\n  Note, you can apply MVC Command classes to multiple portlets by\n  setting a \\texttt{javax.portlet.name} property for each portlet and\n  apply MVC Command classes to multiple command names by setting an\n  \\texttt{mvc.command.name} property for each command name. For example,\n  this component's \\texttt{javax.portlet.name} properties and\n  \\texttt{mvc.command.name} properties apply it to two specific portlets\n  and two specific command names.\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n       \"javax.portlet.name=\" + HelloWorldPortletKeys.HELLO_MY_WORLD,\n       \"javax.portlet.name=\" + HelloWorldPortletKeys.HELLO_WORLD,\n       \"mvc.command.name=/hello/edit_super_entry\",\n       \"mvc.command.name=/hello/edit_entry\"\n    },\n    service = MVCRenderCommand.class\n)\n\\end{verbatim}\n\\item\n  Implement your render logic in a method that overrides\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html}{\\texttt{MVCRenderCommand}}'s\n  \\texttt{render} method. Some \\texttt{*MVCRenderCommand}s, such as the\n  one below, always render the same JSP.\n\n\\begin{verbatim}\npublic class BlogsViewMVCRenderCommand implements MVCRenderCommand {\n\n    @Override\n    public String render(\n        RenderRequest renderRequest, RenderResponse renderResponse) {\n\n        return \"/blogs/view.jsp\";\n    }\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\nAs you can see, MVC render commands are easy to implement and can\nrespond to multiple command names for multiple portlets.\n\n\\section{Related Topics}\\label{related-topics-7}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-an-mvc-portlet}{Creating\nan MVC Portlet}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer}{Configuring\nthe View Layer}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-resource-command}{MVC\nResource Command}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-action-command}{MVC Action\nCommand}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-liferay-mvc-commands}{MVC\nCommand Overrides}\n\n\\chapter{MVC Resource Command}\\label{mvc-resource-command}\n\nWhen using Liferay's\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{MVCPortlet\nframework}, you can create resource URLs in your JSPs to retrieve\nimages, XML, or any other kind of resource from a Liferay DXP instance.\nThe resource URL then invokes the corresponding MVC resource command\nclass (\\texttt{*MVCResourceCommand}) that processes the resource request\nand response.\n\nHere how to create your own MVC Resource Command:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer}{Configure\n  your JSPs} to generate resource URLs via\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/portlet/resourceURL.html}{\\texttt{\\textless{}portlet:resourceURL\\textgreater{}}}\n  tags.\n\n  For example, this\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.1/gradle/apps/resource-command-portlet}{resource-command-portlet}\n  sample resource URL invokes an MVC resource command named\n  \\texttt{/blade/captcha}.\n\n\\begin{verbatim}\n<portlet:resourceURL id=\"/blade/captcha\" var=\"captchaURL\" />\n\\end{verbatim}\n\\item\n  Name the resource URL via its \\texttt{id} attribute.\n\\item\n  Assign the resource URL's \\texttt{var} attribute a variable name to\n  pass to a UI component.\n\\item\n  Assign the resource URL variable (\\texttt{var}) to a UI component,\n  such as a button or icon. When the user triggers the UI component, the\n  \\texttt{*MVCResourceCommand} class that matches the resource URL\n  handles the resource request.\n\n  For example, the sample's resource URL is triggered when the user\n  clicks on this\n  \\href{https://docs.liferay.com/dxp/apps/foundation/latest/taglibdocs/liferay-captcha/captcha.html}{\\texttt{liferay-captcha}}\n  component:\n\n\\begin{verbatim}\n<liferay-captcha:captcha url=\"<%= captchaURL %>\" />\n\\end{verbatim}\n\\item\n  Create a class that implements the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html}{\\texttt{MVCResourceCommand}}\n  interface, or that extends the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/BaseMVCResourceCommand.html}{\\texttt{BaseMVCResourceCommand}}\n  class. The latter may save you time, since it already implements\n  \\texttt{MVCResourceCommand}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Tip:** Naming your `*MVCResourceCommand` class after the resource it \n provides makes the resource mappings more obvious for maintaining the\n code. For example, if your resource URL serves a captcha, you could name\n its class `CaptchaMVCResourceCommand`. If your application has several MVC\n command classes, naming them this way helps differentiate them. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{5}\n\\item\n  Annotate your class with an\n  \\href{https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html}{\\texttt{@Component}}\n  annotation, like this one:\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"javax.portlet.name=your_portlet_name_YourPortlet\",\n        \"mvc.command.name=/your/jsp/resource/url\"\n    },\n    service = MVCResourceCommand.class\n)\npublic class YourMVCResourceCommand extends BaseMVCResourceCommand {\n    // your resource handling code\n}\n\\end{verbatim}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    Set a \\texttt{javax.portlet.name} property to your portlet's\n    internal ID.\n  \\item\n    Set the \\texttt{mvc.command.name} property to your\n    \\texttt{\\textless{}portlet:resourceURL\\textgreater{}} tag's\n    \\texttt{id}. This maps your class to the resource URL of the same\n    name.\n  \\item\n    Register your class as an \\texttt{MVCResourceCommand} service by\n    setting the \\texttt{service} attribute to\n    \\texttt{MVCResourceCommand.class}.\n  \\end{enumerate}\n\\item\n  Implement your resource logic by overriding the appropriate method of\n  the class you're implementing or extending.\n\n  \\begin{itemize}\n  \\item\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html}{\\texttt{MVCResourceCommand}}\n    implementations override the \\texttt{serveResource} method.\n  \\item\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/BaseMVCResourceCommand.html}{\\texttt{BaseMVCResourceCommand}}\n    extensions override the \\texttt{doServeResource} method.\n  \\end{itemize}\n\\end{enumerate}\n\nFor example, the\n\\href{https://github.com/liferay/liferay-blade-samples/tree/7.1/gradle/apps/resource-command-portlet}{resource-command-portlet}'s\n\\texttt{CaptchaMVCResourceCommand} class implements the\n\\texttt{MVCResourceCommand} interface with only a single method:\n\\texttt{serveResource}.\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"javax.portlet.name=com_liferay_blade_samples_portlet_resourcecommand_CaptchaPortlet\",\n        \"mvc.command.name=/blade/captcha\"\n    },\n    service = MVCResourceCommand.class\n)\npublic class CaptchaMVCResourceCommand implements MVCResourceCommand {\n\n    @Override\n    public boolean serveResource(\n            ResourceRequest resourceRequest, ResourceResponse resourceResponse)\n        throws PortletException {\n\n        if (_log.isInfoEnabled()) {\n            _log.info(\"get captcha resource \");\n        }\n\n        try {\n            CaptchaUtil.serveImage(resourceRequest, resourceResponse);\n\n            return false;\n        }\n        catch (Exception e) {\n            _log.error(e.getMessage(), e);\n\n            return true;\n        }\n    }\n\n    private static final Log _log = LogFactoryUtil.getLog(\n        CaptchaMVCResourceCommand.class);\n\n}\n\\end{verbatim}\n\nThis \\texttt{serveResource} method processes the resource request and\nresponse via the \\texttt{javax.portlet.ResourceRequest} and\n\\texttt{javax.portlet.ResourceResponse} parameters, respectively. Note\nthat the \\texttt{try} block uses the helper class\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/captcha/CaptchaUtil.html}{\\texttt{CaptchaUtil}}\nto serve the CAPTCHA image. Though you don't have to create such a\nhelper class, doing so often simplifies your code.\n\nGreat! Now you know how to use \\texttt{MVCResourceCommand} to process\nresources in your Liferay MVC Portlets.\n\n\\section{Related Topics}\\label{related-topics-8}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-an-mvc-portlet}{Creating\nan MVC Portlet}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer}{Configuring\nthe View Layer}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-render-command}{MVC Render\nCommand}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/mvc-action-command}{MVC Action\nCommand}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-liferay-mvc-commands}{MVC\nCommand Overrides}\n\n\\chapter{PortletMVC4Spring}\\label{portletmvc4spring}\n\nPortletMVC4Spring is a way to develop portlets using the Spring\nFramework and the Model View Controller (MVC) pattern. While the Spring\nFramework supports developing \\emph{servlet-based} web applications\nusing\n\\href{https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html}{Spring\nWeb MVC}, PortletMVC4Spring supports developing \\emph{portlet-based}\napplications using MVC. You can build\n\\href{https://spring.io/projects/spring-framework}{Spring Framework}\nLiferay DXP portlets with features like these:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Inversion of control (IoC) / dependency injection (DI)\n\\item\n  Annotations\n\\item\n  Security\n\\item\n  Binding and validation\n\\item\n  Multi-part file upload\n\\item\n  \\ldots{} and more\n\\end{itemize}\n\nYou'll learn these things about PortletMVC4Spring:\n\n\\begin{itemize}\n\\item\n  \\textbf{Developing a Portlet Using PortletMVC4Spring:} Demonstrates\n  creating and deploying a portlet using PortletMVC4Spring.\n\\item\n  \\textbf{Annotation-based Controller Development:} Shows how to\n  implement controllers using plain old Java objects (POJOs) and\n  annotations.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Background:} The PortletMVC4Spring project began as Spring\nPortlet MVC and was part of the\n\\href{https://spring.io/projects/spring-framework}{Spring Framework}.\nWhen the project was pruned from version 5.0.x of the Spring Framework\nunder\n\\href{https://github.com/spring-projects/spring-framework/issues/18701}{SPR-14129},\nit became necessary to fork and rename the project. This made it\npossible to improve and maintain the project for compatibility with the\nlatest versions of the Spring Framework and the Portlet API.\n\n\\href{http://www.liferay.com}{Liferay} adopted Spring Portlet MVC in\nMarch of 2019 and the project was renamed to \\textbf{PortletMVC4Spring}.\n\n\\noindent\\hrulefill\n\nIf you're familiar with Spring Web MVC, it's helpful to compare it with\nPortletMVC4Spring. Portlet workflow differs from servlet workflow\nbecause a request to the portlet can have two distinct phases: the\n\\texttt{ACTION\\_PHASE} and the \\texttt{RENDER\\_PHASE}. The\n\\texttt{ACTION\\_PHASE} is executed only once and is where any back-end\nchanges or actions occur, such as making changes in a database. The\n\\texttt{RENDER\\_PHASE} presents the portlet's content to the user each\ntime the display is refreshed. Thus for a single request, the\n\\texttt{ACTION\\_PHASE} is executed only once, but the\n\\texttt{RENDER\\_PHASE} may be executed multiple times. This provides\n(and requires) a clean separation between the activities that modify the\nsystem's persistent state and the activities that generate content. The\nPortlet 2.0 Specification added two more phases: The event phase and the\nresource phase, both of which are supported by annotation-driven\ndispatching.\n\nPortletMVC4Spring provides annotations that support requests from the\nrender, action, event, and resource serving portlet phases; Spring Web\nMVC provides only a \\texttt{@RequestMapping} annotation. Where a Spring\nWeb MVC controller might have a single handler method annotated with\n\\texttt{@RequestMapping}, an equivalent PortletMVC4Spring controller\nmight have multiple handler methods, each using one of the phase\nannotations: \\texttt{@ActionMapping}, \\texttt{@EventMapping},\n\\texttt{@RenderMapping}, or \\texttt{@ResourceMapping}.\n\nThe PortletMVC4Spring framework uses a \\texttt{DispatcherPortlet} that\ndispatches requests to handlers, with configurable handler mappings and\nview resolution, just as the \\texttt{DispatcherServlet} in the web\nframework does.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} For more information on portlets, portlet specifications,\nand how portlets differ from servlets, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{Portlets}.\n\n\\noindent\\hrulefill\n\nLiferay also provides full-featured sample portlets that demonstrate\nusing JSP and \\href{https://www.thymeleaf.org}{Thymeleaf} view\ntemplates. They exercise many features that form-based portlet\napplications typically require.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portletmvc4spring-applicant-jsp-app.png}\n\\caption{This PortletMVC4Spring portlet enables users to enter job\napplications. It uses the Spring features mentioned above and handles\nrequests from multiple portlet phases.}\n\\end{figure}\n\nThe samples are available here:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nSource Code\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nMaven Central\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\href{https://github.com/liferay/portletmvc4spring/tree/master/demo/applicant-jsp-portlet}{applicant-jsp-portlet}\n&\n\\href{https://search.maven.org/search?q=a:com.liferay.portletmvc4spring.demo.applicant.jsp.portlet}{com.liferay.portletmvc4spring.demo.applicant.jsp.portlet.war} \\\\\n\\href{https://github.com/liferay/portletmvc4spring/tree/master/demo/applicant-thymeleaf-portlet}{applicant-thymeleaf-portlet}\n&\n\\href{https://search.maven.org/search?q=a:com.liferay.portletmvc4spring.demo.applicant.thymeleaf.portlet}{com.liferay.portletmvc4spring.demo.applicant.thymeleaf.portlet.war} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nNow that you have a basic understanding of PortletMVC4Spring portlets\nand how they compare to Spring Web MVC applications, it's time to\ndevelop a PortletMVC4Spring portlet.\n\n\\chapter{Developing a Portlet Using\nPortletMVC4Spring}\\label{developing-a-portlet-using-portletmvc4spring}\n\nPortletMVC4Spring compliments the Spring Web framework and MVC design\npattern by providing annotations that map portlet requests to Controller\nclasses and methods. Here you'll develop and deploy a portlet\napplication that uses PortletMVC4Spring, Spring, and JSP/JSPX templates.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portletmvc4Spring-developing.png}\n\\caption{The archetype's sample portlet prints a greeting (e.g.,\n\\emph{Hello, Joe Bloggs}) on submitting a first and last name.}\n\\end{figure}\n\nFollow these steps to create and deploy your portlet application:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a PortletMVC4Spring project. See\n  \\href{/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-project-anatomy}{PortletMVC4Spring\n  Project Anatomy} for the project structure and commands for generating\n  PortletMVC4Spring projects. Here's the Maven command for generating\n  the JSP/JSPX project. This article lists sample code from the\n  generated project.\n\n\\begin{verbatim}\nmvn archetype:generate \\\n-DarchetypeGroupId=com.liferay.portletmvc4spring.archetype \\\n-DarchetypeArtifactId=com.liferay.portletmvc4spring.archetype.form.jsp.portlet \\\n-DarchetypeVersion=5.1.0 \\ \n-DgroupId=com.mycompany \\ \n-DartifactId=com.mycompany.my.form.jsp.portlet\n\\end{verbatim}\n\\item\n  In your project's Gradle build file or Maven POM, add your app's\n  dependencies. Here are the PortletMVC4Spring development dependencies:\n\n  \\textbf{Gradle:}\n\n\\begin{verbatim}\ncompile group: 'com.liferay.portletmvc4spring', name: 'com.liferay.portletmvc4spring.framework', version: '5.1.0'\ncompile group: 'com.liferay.portletmvc4spring', name: 'com.liferay.portletmvc4spring.security', version: '5.1.0'\nprovidedCompile group: 'javax.portlet', name: 'portlet-api', version: '3.0.0'\n\\end{verbatim}\n\n  \\textbf{Maven:}\n\n\\begin{verbatim}\n<dependency>\n    <groupId>com.liferay.portletmvc4spring</groupId>\n    <artifactId>com.liferay.portletmvc4spring.framework</artifactId>\n    <version>5.1.0</version>\n</dependency>\n<dependency>\n    <groupId>com.liferay.portletmvc4spring</groupId>\n    <artifactId>com.liferay.portletmvc4spring.security</artifactId>\n    <version>5.1.0</version>\n</dependency>\n<dependency>\n    <groupId>javax.portlet</groupId>\n    <artifactId>portlet-api</artifactId>\n    <version>3.0.0</version>\n    <scope>provided</scope>\n</dependency>\n\\end{verbatim}\n\n  At this point you can develop your Model, View, or Controller\n  components, in any order.\n\\item\n  Create your Model class(es) in a package for models (e.g.,\n  \\texttt{java/{[}my-package-path{]}/dto}). The sample Model class is\n  \\texttt{User}.\n\n\\begin{verbatim}\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1234273427623725552L;\n\n    @NotBlank\n    private String firstName;\n\n    @NotBlank\n    private String lastName;\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public void setFirstName(String firstName) {\n        this.firstName = firstName;\n    }\n\n    public String getLastName() {\n        return lastName;\n    }\n\n    public void setLastName(String lastName) {\n        this.lastName = lastName;\n    }\n}\n\\end{verbatim}\n\\item\n  Create your View using a Spring Web-supported template type. If you\n  didn't generate your project using the archetype mentioned above,\n  specify a View resolver for template type in your\n  \\texttt{spring-context/portlet-application-context.xml} portlet\n  application context. (See\n  \\href{/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-configuration-files}{PortletMVC4Spring\n  Configuration Files} for details).\n\n  The sample \\texttt{user.jspx} template renders a form for submitting a\n  user's first and last name.\n\n\\begin{verbatim}\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<jsp:root xmlns:jsp=\"http://java.sun.com/JSP/Page\"\n          xmlns:portlet=\"http://xmlns.jcp.org/portlet_3_0\"\n          xmlns:spring=\"http://www.springframework.org/tags\"\n          xmlns:form=\"http://www.springframework.org/tags/form\"\n          version=\"2.1\">\n    <jsp:directive.page contentType=\"text/html\" pageEncoding=\"UTF-8\" />\n    <portlet:defineObjects/>\n    <link href=\"${contextPath}/resources/css/main.css\" rel=\"stylesheet\" type=\"text/css\"/>\n    <portlet:actionURL var=\"mainFormActionURL\"/>\n    <form:form id=\"${namespace}mainForm\" action=\"${mainFormActionURL}\" class=\"user-form\" method=\"post\" modelAttribute=\"user\">\n        <p class=\"caption\">\n            <spring:message code=\"personal-information\" />\n        </p>\n        <fieldset>\n            <div class=\"form-group\">\n                <form:label for=\"${namespace}firstName\" path=\"firstName\">\n                    <spring:message code=\"first-name\" />\n                </form:label>\n                <form:input id=\"${namespace}firstName\" cssClass=\"form-control\" path=\"firstName\"/>\n                <form:errors path=\"firstName\" cssClass=\"portlet-msg-error\"/>\n            </div>\n            <div class=\"form-group\">\n                <form:label for=\"${namespace}lastName\" path=\"lastName\">\n                    <spring:message code=\"last-name\" />\n                </form:label>\n                <form:input id=\"${namespace}lastName\" cssClass=\"form-control\" path=\"lastName\"/>\n                <form:errors path=\"lastName\" cssClass=\"portlet-msg-error\"/>\n            </div>\n        </fieldset>\n        <hr />\n        <spring:message code=\"submit\" var=\"submit\" />\n        <input class=\"btn btn-primary\" value=\"${submit}\" type=\"submit\"/>\n    </form:form>\n</jsp:root>\n\\end{verbatim}\n\n  To invoke actions in your Controller, associate action URLs with your\n  templates. The sample template associates the action URL variable\n  \\texttt{mainFormActionURL} with its form element.\n\n\\begin{verbatim}\n<portlet:actionURL var=\"mainFormActionURL\"/>\n<form:form id=\"${namespace}mainForm\" action=\"${mainFormActionURL}\" class=\"user-form\" method=\"post\" modelAttribute=\"user\">\n    ...\n\\end{verbatim}\n\n  A \\texttt{\\textless{}form:form/\\textgreater{}} element's\n  \\texttt{modelAttribute} attribute targets an application Model. The\n  sample template targets the application's \\texttt{user} Model.\n\\item\n  Style your portlet by adding CSS to a stylesheet (e.g.,\n  \\texttt{webapp/resources/css/main.css}) and linking your template to\n  it.\n\n\\begin{verbatim}\n<link href=\"${contextPath}/resources/css/main.css\" rel=\"stylesheet\" type=\"text/css\"/>\n\\end{verbatim}\n\\item\n  Define your portlet's messages in a properties file (e.g.,\n  \\texttt{src/main/resources/content/{[}portlet{]}.properties}). The\n  sample \\texttt{user.jspx} template references some of these\n  properties:\n\n\\begin{verbatim}\nfirst-name=First Name\ngreetings=Greetings, {0} {1}!\njavax.portlet.display-name=com.mycompany.my.form.jsp.portlet\njavax.portlet.keywords=com.mycompany.my.form.jsp.portlet\njavax.portlet.short-title=com.mycompany.my.form.jsp.portlet\njavax.portlet.title=com.mycompany.my.form.jsp.portlet\nlast-name=Last Name\npersonal-information=Personal Information\nsubmit=Submit\ntodays-date-is=Today''s date is {0}\n\\end{verbatim}\n\\item\n  Create a Controller class to handle portlet requests. Here's an\n  example:\n\n\\begin{verbatim}\n@Controller\n@RequestMapping(\"VIEW\")\npublic class MyController {\n    ...\n}\n\\end{verbatim}\n\n  The\n  \\href{https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Controller.html}{\\texttt{@Controller}}\n  annotation applies the Spring Controller component stereotype. The\n  Spring Framework scans Controller classes for Controller annotations.\n\n  The\n  \\href{https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html}{\\texttt{@RequestMapping(\"VIEW\")}}\n  annotation marks the class's public methods as request handler methods\n  for the portlet's\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{VIEW mode}.\n\\item\n  In your Controller, apply \\texttt{@RenderMapping} annotations to\n  methods for handling portlet render requests. Import the annotation\n  \\texttt{com.liferay.portletmvc4spring.bind.annotation.RenderMapping}\n  and make sure each handler method returns a string that matches the\n  name of a template to render. Here are the sample's render request\n  handler methods:\n\n\\begin{verbatim}\n@RenderMapping\npublic String prepareView() {\n    return \"user\";\n}\n\n@RenderMapping(params = \"javax.portlet.action=success\")\npublic String showGreeting(ModelMap modelMap) {\n\n    DateFormat dateFormat = new SimpleDateFormat(\"EEEE, MMMM d, yyyy G\");\n\n    Calendar todayCalendar = Calendar.getInstance();\n\n    modelMap.put(\"todaysDate\", dateFormat.format(todayCalendar.getTime()));\n\n    return \"greeting\";\n}\n\\end{verbatim}\n\n  The \\texttt{@RenderMapping} annotation causes the \\texttt{prepareView}\n  method above to be invoked if no other handler methods match the\n  request. \\texttt{prepareView} renders the \\texttt{user} template\n  (i.e., \\texttt{user.jspx}).\n\n  The \\texttt{@RenderMapping(params\\ =\\ \"javax.portlet.action=success\")}\n  annotation causes the \\texttt{showGreeting} method to be invoked if\n  the render request has the parameter setting\n  \\texttt{javax.portlet.action=success}. \\texttt{showGreeting} renders\n  the \\texttt{greeting} template (i.e., \\texttt{greeting.jspx}).\n\\item\n  In your Controller, apply \\texttt{@ActionMapping} annotations to your\n  portlet action request handling methods. Import the annotation\n  \\texttt{com.liferay.portletmvc4spring.bind.annotation.ActionMapping}.\n\n  The sample Controller's action handler method below is annotated with\n  \\texttt{@ActionMapping}, making it the default action handler if no\n  other action handlers match the request. Since this portlet only has\n  one action handler, the \\texttt{submitApplicant} method handles all of\n  the portlet's action requests.\n\n\\begin{verbatim}\n@ActionMapping\npublic void submitApplicant(@ModelAttribute(\"user\") User user, BindingResult bindingResult, ModelMap modelMap,\n                            Locale locale, ActionResponse actionResponse, SessionStatus sessionStatus) {\n\n    localValidatorFactoryBean.validate(user, bindingResult);\n\n    if (!bindingResult.hasErrors()) {\n\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"firstName=\" + user.getFirstName());\n            logger.debug(\"lastName=\" + user.getLastName());\n        }\n\n        MutableRenderParameters mutableRenderParameters = actionResponse.getRenderParameters();\n\n        mutableRenderParameters.setValue(\"javax.portlet.action\", \"success\");\n\n        sessionStatus.setComplete();\n    }\n}\n\\end{verbatim}\n\n  The\n  \\href{https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html}{\\texttt{@ModelAttribute}}\n  annotation in method parameter\n  \\texttt{@ModelAttribute(\"user\")\\ User\\ user} associates the View's\n  \\texttt{user} Model (comprising a first name and last name) to the\n  \\texttt{User} object passed to this method.\n\n  Note, the \\texttt{submitApplicant} method sets the\n  \\texttt{javax.portlet.action} render parameter to\n  \\texttt{success}---the previous render handler method\n  \\texttt{showGreeting} matches this request parameter.\n\\item\n  Configure any additional resources and beans in the project's\n  descriptors and Spring context files respectively. (See\n  \\href{/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-configuration-files}{PortletMVC4Spring\n  Configuration Files} for details).\n\\item\n  Build the project WAR using Gradle or Maven.\n\\item\n  Deploy the WAR by copying it to your\n  \\texttt{{[}Liferay-Home{]}/deploy} folder.\n\\end{enumerate}\n\nLiferay DXP logs the deployment and the portlet is now available in the\nLiferay DXP UI. Find your portlet by selecting the \\emph{Add} icon\n(\\includegraphics{./images/icon-add-app.png}) and navigating to\n\\emph{Widgets} and the category you specified to the Liferay Bundle\nGenerator (\\emph{Sample} is the default category).\n\nCongratulations! You created and deployed a PortletMVC4Spring Portlet.\n\n\\section{Related Topics}\\label{related-topics-9}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-project-anatomy}{PortletMVC4Spring\nProject Anatomy}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-annotations}{PortletMVC4Spring\nAnnotations}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-configuration-files}{PortletMVC4Spring\nConfiguration Files}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/migrating-to-portletmvc4spring}{Migrating\nto PortletMVC4Spring}\n\n\\chapter{Migrating to\nPortletMVC4Spring}\\label{migrating-to-portletmvc4spring}\n\nTo continue developing a portlet to use Spring Framework version 5.0\nonward, migrate it from Spring Portlet MVC to\n\\href{/docs/7-2/appdev/-/knowledge_base/a/portletmvc4spring}{PortletMVC4Spring}.\nHere are the steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your \\texttt{pom.xml} or \\texttt{build.gradle} descriptor, use the\n  Spring Framework version 5.1.x artifacts by replacing dependencies on\n  the \\texttt{spring-webmvc-portlet} artifact with the\n  \\texttt{com.liferay.portletmvc4spring.framework} artifact.\n\n  Maven:\n\n\\begin{verbatim}\n<dependency>\n    <groupId>com.liferay.portletmvc4spring</groupId>\n    <artifactId>com.liferay.portletmvc4spring.framework</artifactId>\n    <version>5.1.0</version>    \n</dependency>\n<dependency>\n    <groupId>com.liferay.portletmvc4spring</groupId>\n    <artifactId>com.liferay.portletmvc4spring.security</artifactId>\n    <version>5.1.0</version>    \n</dependency>\n\\end{verbatim}\n\n  Gradle:\n\n\\begin{verbatim}\ncompile group: 'com.liferay.portletmvc4spring', name: 'com.liferay.portletmvc4spring.framework', version: '5.1.0'\ncompile group: 'com.liferay.portletmvc4spring', name: 'com.liferay.portletmvc4spring.security', version: '5.1.0'\n\\end{verbatim}\n\\item\n  In your \\texttt{WEB-INF/portlet.xml} descriptor, replace uses of\n  \\texttt{org.springframework.web.portlet.DispatcherPortlet} with\n  \\href{https://liferay.github.io/portletmvc4spring/apidocs/com/liferay/portletmvc4spring/DispatcherPortlet.html}{\\texttt{com.liferay.portletmvc4spring.DispatcherPortlet}}.\n\\item\n  Replace uses of the Spring Portlet MVC\n  \\href{https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.html}{\\texttt{AnnotationMethodHandlerAdapter}}\n  class with the PortletMVC4Spring\n  \\href{https://liferay.github.io/portletmvc4spring/apidocs/com/liferay/portletmvc4spring/mvc/method/annotation/PortletRequestMappingHandlerAdapter.html}{\\texttt{PortletRequestMappingHandlerAdapter}}\n  class. \\texttt{PortletRequestMappingHandlerAdapter} uses the\n  \\texttt{HandlerMethod} infrastructure that\n  \\href{https://docs.spring.io/spring/docs/5.1.x/spring-framework-reference/web.html\\#spring-web}{Spring\n  Web MVC 5.1.x} is based on.\n\\item\n  If you specified \\texttt{AnnotationMethodHandlerAdapter} as a\n  \\texttt{\\textless{}bean\\textgreater{}} in a Spring configuration\n  descriptor, replace its fully-qualified class name\n  \\texttt{org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter}\n  with\n  \\texttt{com.liferay.portletmvc4spring.mvc.method.annotation.PortletRequestMappingHandlerAdapter}.\n\n  Also address these bean property changes:\n\n  \\begin{itemize}\n  \\item\n    \\href{https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.html\\#setCustomModelAndViewResolver-org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver-}{\\texttt{customModelAndViewResolver}}\n    (no longer available)\n  \\item\n    \\href{https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.html\\#setCustomArgumentResolver-org.springframework.web.bind.support.WebArgumentResolver-}{\\texttt{customArgumentResolver}}\n    (no longer available)\n  \\item\n    \\href{https://liferay.github.io/portletmvc4spring/apidocs/com/liferay/portletmvc4spring/mvc/method/annotation/PortletRequestMappingHandlerAdapter.html\\#setCustomArgumentResolvers-java.util.List-}{\\texttt{customArgumentResolvers}}\n    (specify a list of\n    \\href{https://docs.spring.io/spring/docs/5.1.4.RELEASE/javadoc-api/org/springframework/web/method/support/HandlerMethodArgumentResolver.html}{\\texttt{HandlerMethodArgumentResolver}}\n    instead of a list of\n    \\href{https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/bind/support/WebArgumentResolver.html}{\\texttt{WebArgumentResolver}})\n  \\end{itemize}\n\\item\n  If you're using\n  \\href{https://commons.apache.org/proper/commons-fileupload/}{Apache\n  Commons Fileupload}, update your Spring configuration descriptor:\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    Replace this legacy bean:\n\n\\begin{verbatim}\n<bean id=\"portletMultipartResolver\"\n    class=\"org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver\" />\n\\end{verbatim}\n\n    With this new one from PortletMVC4Spring:\n\n\\begin{verbatim}\n<bean id=\"portletMultipartResolver\"\n    class=\"com.liferay.portletmvc4spring.multipart.CommonsPortletMultipartResolver\" />\n\\end{verbatim}\n  \\end{enumerate}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n     **Note:** Alternatively, you can use the native Portlet 3.0 file upload\n     support that PortletMVC4Spring provides by setting the\n     `portletMultipartResolver` `<bean>` element's `class` to\n     `com.liferay.portletmvc4spring.multipart.StandardPortletMultipartResolver`\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n2.  Remove these dependencies from your `pom.xml` or `build.gradle` \n    descriptor:\n\n    ```xml\n    <dependency>\n        <groupId>commons-fileupload</groupId>\n        <artifactId>commons-fileupload</artifactId>\n    </dependency>\n    <dependency>\n        <groupId>commons-io</groupId>\n        <artifactId>commons-io</artifactId>\n    </dependency>\n    ```\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{4}\n\\item\n  Throughout your project, replace all uses of the\n  \\texttt{org.springframework.web.portlet} package path with\n  \\texttt{com.liferay.portletmvc4spring}.\n\\item\n  Continue\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/developing-a-portlet-using-portletmvc4spring}{developing\n  your portlet using PortletMVC4Spring}.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Build\n  and deploy your project}.\n\\end{enumerate}\n\nCongratulations! You migrated your project from Spring Portlet MVC to\nPortletMVC4Spring.\n\n\\section{Related Topics}\\label{related-topics-10}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/portletmvc4spring}{PortletMVC4Spring}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/developing-a-portlet-using-portletmvc4spring}{Developing\na Portlet Using PortletMVC4Spring}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Configuring\nDependencies}\n\n\\chapter{JSF Portlet}\\label{jsf-portlet}\n\nDo you want to develop MVC-based portlets using the Java EE standard? Do\nyou want to use a portlet development framework with a UI component\nmodel that makes it easy to develop sophisticated, rich UIs? Or have you\nbeen writing web apps using JSF that you'd like to use in Liferay DXP?\nIf you answered \\emph{yes} to any of these questions, you're in luck!\nYou can use the JSF portlet technology in Liferay DXP by leveraging the\nLiferay Faces project, which provides all these capabilities and more.\n\nLiferay Faces is an umbrella project that provides support for the\nJavaServer™ Faces (JSF) standard in Liferay DXP. It encompasses the\nfollowing projects:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-bridge}{Liferay\n  Faces Bridge} lets you deploy JSF web apps as portlets without writing\n  portlet-specific Java code. It also contains innovative features that\n  make it possible to leverage the power of JSF 2.x inside a portlet\n  application. Liferay Faces Bridge implements the JSR 329/378 Portlet\n  Bridge Standard.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-alloy}{Liferay\n  Faces Alloy} lets you use AlloyUI components in a way that is\n  consistent with JSF development.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-portal}{Liferay\n  Faces Portal} lets you leverage Liferay-specific utilities and UI\n  components in JSF portlets.\n\\end{itemize}\n\nFor a comprehensive demo for the JSF component suite, visit the\n\\href{https://faces.liferay.dev}{Liferay Faces Developer site}.\n\nIf you're new to JSF, you may want to know its strengths, its\nweaknesses, and how it stacks up to developing portlets with\nCSS/JavaScript.\n\nHere are some good reasons to use JSF and Liferay Faces:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  JSF is the Java EE standard for developing web applications that use\n  the Model/View/Controller (MVC) design pattern. As a standard, the\n  specification is actively maintained by the Java Community Process\n  (JCP), and the Oracle reference implementation (Mojarra) has frequent\n  releases. Software Architects often choose standards like JSF because\n  they are supported by Java EE application server vendors and have a\n  guaranteed service life according to Service Level Agreements (SLAs).\n\\item\n  JSF was first introduced in 2003 and is a mature technology for\n  developing web applications that are (arguably) easy to maintain.\n\\item\n  JSF Portlet Bridges (like Liferay Faces Bridge) are also standardized\n  by the JCP and make it possible to deploy JSF web applications as\n  portlets without writing portlet-specific Java code.\n\\item\n  Support for JSF (via Liferay Faces) is included with Liferay DXP\n  support.\n\\item\n  JSF is a unique framework in that it provides a UI component model\n  that makes it easy to develop sophisticated, rich user interfaces.\n\\item\n  JSF has built-in Ajax functionality that provides automatic updates to\n  the browser by replacing elements in the DOM.\n\\item\n  JSF is designed with many extension points that make a variety of\n  integrations possible.\n\\item\n  There are several JSF component suites available including Liferay\n  Faces Alloy, Primefaces, ICEfaces, and RichFaces. Each of these\n  component suites fortify JSF with a variety of UI components and\n  complimentary technologies.\n\\item\n  JSF is a good choice for server-side developers that need to build web\n  user interfaces. This enables server-side developers to focus on their\n  core competencies rather than being experts in HTML/CSS/JavaScript.\n\\item\n  JSF provides the Facelets templating engine which makes it possible to\n  create reusable UI components that are encapsulated as markup.\n\\item\n  JSF provides good integration with HTML5 markup\n\\item\n  JSF provides the Faces Flows feature which makes it easy for\n  developers to create wizard-like applications that flow from\n  view-to-view.\n\\item\n  JSF has good integration with dependency injection frameworks such as\n  CDI and Spring that make it easy for developers to create beans that\n  are placed within a scope managed by a container:\n  \\texttt{@RequestScoped}, \\texttt{@ViewScoped},\n  \\texttt{@SessionScoped}, \\texttt{@FlowScoped}\n\\item\n  Since JSF is a stateful technology, the framework encapsulates the\n  complexities of managing application state so the developer doesn't\n  have to write state management code. It is also possible to use JSF in\n  a stateless manner, but some of the features of application state\n  management become effectively disabled.\n\\end{itemize}\n\nThere are some reasons not to use JSF. For example, if you are a\nfront-end developer who makes heavy use of HTML/CSS/JavaScript, you\nmight find that JSF UI components render HTML in a manner that gives you\nless control over the overall HTML document. Sticking with a JavaScript\nframework may be better for you. Or, perhaps standards aren't a major\nconsideration for you or you may simply prefer developing portlets using\nyour current framework.\n\nWhether you develop your next portlet application with JSF and Liferay\nFaces or with HTML/CSS/JavaScript is entirely up to you. But you\nprobably want to learn more about Liferay Faces and try it out for\nyourself.\n\n\\chapter{Developing a JSF Portlet\nApplication}\\label{developing-a-jsf-portlet-application}\n\nTo run an existing JSF web app on Liferay DXP, you must leverage the\nLiferay Faces project. The\n\\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-bridge}{Liferay\nFaces Bridge} enables you to deploy JSF web apps as portlets without\nwriting portlet-specific code. You must also provide portlet-specific\ndescriptor files to make it compatible with the Liferay DXP platform.\nThe easiest way to do this is by generating a new Liferay JSF Portlet\nproject and migrating your code to it. Then you can deploy your new JSF\nportlet project to Liferay DXP.\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new JSF portlet project. The following Maven archetypes are\n  available:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{com.liferay.faces.archetype.alloy.portlet} (Liferay Faces\n    Alloy portlet)\n  \\item\n    \\texttt{com.liferay.faces.archetype.bootsfaces.portlet} (Liferay\n    BootsFaces portlet)\n  \\item\n    \\texttt{com.liferay.faces.archetype.butterfaces.portlet} (Liferay\n    ButterFaces portlet)\n  \\item\n    \\texttt{com.liferay.faces.archetype.icefaces.portlet} (Liferay\n    ICEFaces portlet)\n  \\item\n    \\texttt{com.liferay.faces.archetype.jsf.portlet} (Liferay JSF\n    portlet)\n  \\item\n    \\texttt{com.liferay.faces.archetype.primefaces.portlet} (Liferay\n    PrimeFaces portlet)\n  \\item\n    \\texttt{com.liferay.faces.archetype.richfaces.portlet} (Liferay\n    RichFaces portlet)\n  \\end{itemize}\n\n  Choose the archetype that matches your web app's JSF component suite.\n  For example,\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay.faces.archetype \\\n    -DarchetypeArtifactId=com.liferay.faces.archetype.jsf.portlet \\\n    -DarchetypeVersion=5.0.6 \\\n    -DgroupId=com.mycompany \\\n    -DartifactId=com.mycompany.my.jsf.portlet\n\\end{verbatim}\n\n  The above archetypes support both Gradle and Maven development by\n  providing a \\texttt{build.gradle} and \\texttt{pom.xml}, respectively.\n  For more information, visit\n  \\href{https://faces.liferay.dev}{faces.liferay.dev}.\n\n  Here's the resulting project structure for a JSF Standard portlet:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    {[}liferay-jsf-portlet{]}/ → Arbitrary project name\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{src/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{main/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{java/{[}my-package-path{]}/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{bean/} → Sub-package for managed Java beans\n            (optional)\n          \\item\n            \\texttt{dto/} → Sub-package for model (data transfer object)\n            classes (optional)\n          \\end{itemize}\n        \\item\n          \\texttt{resources/} → Resources to include in the class path\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{i18n.properties} → Internationalization\n            configuration\n          \\item\n            \\texttt{log4j.properties} → Log4J logging configuration\n          \\end{itemize}\n        \\item\n          \\texttt{webapp/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{resources/}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{images/} → Images\n            \\end{itemize}\n          \\item\n            \\texttt{WEB-INF/}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{resources/} Frontend files (e.g., CSS, JS, XHTML,\n              etc.) that shouldn't be accessed directly by the browser\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                \\texttt{css/} → Stylesheets\n              \\end{itemize}\n            \\item\n              \\texttt{views/} → View templates\n            \\item\n              \\texttt{faces-config.xml} → JSF application configuration\n              file\n            \\item\n              \\texttt{liferay-display.xml} → Portlet display\n              configuration\n            \\item\n              \\texttt{liferay-plugin-package.properties} → Packaging\n              descriptor\n            \\item\n              \\texttt{liferay-portlet.xml} → Liferay-specific portlet\n              configuration\n            \\item\n              \\texttt{portlet.xml} → Portlet configuration\n            \\item\n              \\texttt{web.xml} → Web application configuration\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{test/java/} → Test source files\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  Update your dependencies as desired. The generated portlet already\n  includes the required artifacts required to deploy a simple JSF\n  portlet to Liferay DXP. For example, the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-bridge}{Liferay\n  Faces Bridge} artifacts look like this:\n\n  \\textbf{Maven:}\n\n\\begin{verbatim}\n<dependencies>\n    <dependency>\n        <groupId>com.liferay.faces</groupId>\n        <artifactId>com.liferay.faces.bridge.ext</artifactId>\n        <version>5.0.4</version>\n        <scope>runtime</scope>\n    </dependency>\n    <dependency>\n        <groupId>com.liferay.faces</groupId>\n        <artifactId>com.liferay.faces.bridge.impl</artifactId>\n        <version>4.1.3</version>\n        <scope>runtime</scope>\n    </dependency>\n</dependencies>\n\\end{verbatim}\n\n  \\textbf{Gradle:}\n\n\\begin{verbatim}\ndependencies {\n    runtime group: 'com.liferay.faces', name: 'com.liferay.faces.bridge.ext', version: '5.0.4'\n    runtime group: 'com.liferay.faces', name: 'com.liferay.faces.bridge.impl', version: '4.1.3'\n}\n\\end{verbatim}\n\\item\n  Copy your Java classes to the new \\texttt{java/{[}my-package-path{]}/}\n  folder.\n\\item\n  Copy your view templates to the new\n  \\texttt{src/main/webapp/WEBINF/views} folder.\n\\item\n  Add your frontend files (e.g., CSS, JS, etc.) that shouldn't be\n  accessed directly by the browser to the\n  \\texttt{webapp/WEB-INF/resources/} folder. For example, your web app's\n  CSS files would reside in the \\texttt{webapp/WEB-INF/resources/css}\n  folder.\n\\item\n  Add your image files to the \\texttt{webapp/resources/images} folder.\n\\item\n  Add localized messages to the \\texttt{resources/i18n.properties} file.\n  The messages in the \\texttt{i18n.properties} file can be accessed via\n  the Expression Language using the implicit \\texttt{i18n} object\n  provided by Liferay Faces Util. The \\texttt{i18n} object can access\n  messages both from a resource bundle defined in the portlet's\n  \\texttt{portlet.xml} file, and from Liferay DXP's\n  \\texttt{Language.properties} file.\n\\item\n  Configure your portlet's logging configuration as desired. The\n  \\texttt{log4j.properties} file in the \\texttt{src/main/resources}\n  folder sets properties for the Log4j logging utility defined in your\n  JSF portlet (i.e., \\texttt{faces-config.xml}).\n\\item\n  Replace your new JSF portlet's\n  \\texttt{webapp/WEB-INF/faces-config.xml} with your web app's\n  \\texttt{faces-config.xml} file. The \\texttt{faces-config.xml} file is\n  a JSF portlet's application configuration file, which is used to\n  register and configure objects and navigation rules.\n\\item\n  Replace your new JSF portlet's \\texttt{webapp/WEB-INF/web.xml} with\n  your web app's \\texttt{web.xml} file. The \\texttt{web.xml} file serves\n  as a deployment descriptor that provides necessary configurations for\n  your JSF portlet to deploy and function in Liferay DXP.\n\n  Make sure the Faces Servlet is configured in your \\texttt{web.xml}:\n\n\\begin{verbatim}\n<servlet>\n    <servlet-name>Faces Servlet</servlet-name>\n    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>\n    <load-on-startup>1</load-on-startup>\n</servlet>\n\\end{verbatim}\n\n  This is required to initialize JSF and should be defined in all JSF\n  portlets deployed to Liferay DXP.\n\\item\n  Modify your \\texttt{webapp/WEB-INF/portlet.xml} as desired. The\n  \\texttt{portlet.xml} descriptor describes the portlet to the portlet\n  container. For example, it describes portlet info, security settings,\n  etc. Also, the \\texttt{javax.portlet.faces.GenericFacesPortlet} is\n  defined here, which handles invocations to your JSF portlet and makes\n  your portlet, since it relies on Liferay Faces Bridge, easy to develop\n  by acting as a turnkey implementation.\n\n  The \\texttt{init-param} is also defined here, which ensures your\n  portlet is visible when deployed to Liferay DXP by pointing to your\n  default view template:\n\n\\begin{verbatim}\n<init-param>\n    <name>javax.portlet.faces.defaultViewId.view</name>\n    <value>/WEB-INF/views/view.xhtml</value>\n</init-param>\n\\end{verbatim}\n\\item\n  Modify your \\texttt{webapp/WEB-INF/liferay-portlet.xml} as desired. It\n  specifies additional information Liferay DXP uses to enhance your\n  portlet: supported security roles, portlet icon, CSS and JavaScript\n  locations, and more. The\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/definitions/liferay-portlet-app_7_2_0.dtd.html}{liferay-portlet-app\n  DTD} defines the \\texttt{liferay-portlet.xml} elements.\n\\item\n  Modify your \\texttt{webapp/WEB-INF/liferay-display.xml} as desired. It\n  configures characteristics for displaying your portlet. For example,\n  this \\texttt{liferay-display.xml} snippet specifies the Widget\n  category in the Add Widget menu:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE display PUBLIC \"-//Liferay//DTD Display 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-display_7_2_0.dtd\">\n\n<display>\n<category name=\"category.sample\">\n    <portlet id=\"jsf-portlet\" />\n</category>\n</display>\n\\end{verbatim}\n\\item\n  Modify your \\texttt{webapp/WEB-INF/liferay-plugin-package.properties}\n  as desired. It describes the portlet application's packaging and\n  version information and specifies any required OSGi metadata. For\n  example, this \\texttt{liferay-plugin-package.properties} snippet tells\n  the OSGi container not to scan for CDI annotations in Liferay DXP.\n\n\\begin{verbatim}\n-cdiannotations:\n\\end{verbatim}\n\n  This is required for JSF portlets leveraging CDI deployed to Liferay\n  DXP. They must reference their own included CDI implementation.\n\n  On deploying the WAR file, the\n  \\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{WAB\n  Generator} adds the specified OSGi metadata to the resulting web\n  application bundle (WAB) that's deployed to Liferay DXP's runtime\n  framework.\n\n  The\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/liferay-plugin-package_7_2_0.properties.html}{liferay-plugin-package\n  reference document} describes the\n  \\texttt{liferay-plugin-package.properties} file.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Build\n  and deploy your project}.\n\\end{enumerate}\n\nLiferay DXP logs the deployment.\n\n\\begin{verbatim}\n2019-05-30 14:10:59.405 INFO  [com.liferay.portal.kernel.deploy.auto.AutoDeployScanner][AutoDeployDir:261] Processing guestbook-jsf-portlet.war\n...\n2019-05-30 14:11:11.401 INFO  [fileinstall-C:/liferay-ce-portal-7.2.0-ga1/osgi/war][BaseDeployer:877] Deploying guestbook-jsf-portlet.war\n...\n2019-05-30 14:11:26.379 INFO  [fileinstall-C:/liferay-ce-portal-7.2.0-ga1/osgi/war][BundleStartStopLogger:39] STARTED guestbook-jsf-portlet_7.2.0.1 [2155]\n...\n2019-05-30 14:11:67.569 INFO  [fileinstall-C:/liferay-ce-portal-7.2.0-ga1/osgi/war][PortletHotDeployListener:288] 1 portlet for guestbook-jsf-portlet is available for use\n\\end{verbatim}\n\nThe portlet is now available in the Liferay DXP UI. Find your portlet by\nselecting the \\emph{Add} icon\n(\\includegraphics{./images/icon-add-app.png}) and navigating to\n\\emph{Widgets} and the category you specified (\\emph{Sample} is the\ndefault category).\n\nGreat! You've successfully developed a Liferay JSF portlet and migrated\nyour web app logic to it.\n\n\\chapter{Bean Portlet}\\label{bean-portlet}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Bean Portlet is in development and is not yet\navailable.\n\n\\noindent\\hrulefill\n\nPortlet 3.0, the \\href{https://jcp.org/en/jsr/detail?id=362}{JSR 362}\nstandard, features a new style of portlet development called Bean\nPortlets that use Contexts and Dependency Injection (CDI). Bean Portlets\nfully leverage\n\\href{https://portals.apache.org/pluto/v301/v3Features.html}{all the new\nPortlet 3.0 features} in compliant portals, and are fully supported in\nLiferay DXP.\n\nBean Portlets are plain old Java objects (POJOs): they don't need to\nextend anything. Portlet descriptors declare them to be portlets.\n\nConfiguration annotations, phase method annotations, and CDI are some of\nthe features you'll use in Portlet 3.0.\n\n\\section{Portlet Configuration\nAnnotations}\\label{portlet-configuration-annotations}\n\nThe\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/PortletConfiguration.html}{\\texttt{@PortletConfiguration}}\nannotation describes your portlet to the portlet container. You can use\nthe annotation instead of or in addition to the traditional\n\\texttt{portlet.xml} descriptor file. The \\texttt{@PortletConfiguration}\nannotation describes your portlet in the portlet code instead of a\nseparate file.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can configure Bean Portlets using configuration\nannotations, descriptors, or both. If using annotations and descriptors,\nthe descriptors take precedence.\n\n\\noindent\\hrulefill\n\nThis example portlet was generated using the\n\\texttt{com.liferay.project.templates.cdi.bean.portlet} archetype, and\nit uses \\texttt{@PortletConfiguration} and\n\\texttt{@LiferayPortletConfiguration} annotations:\n\n\\begin{verbatim}\nimport com.mycompany.constants.FooPortletKeys;\n\nimport com.liferay.bean.portlet.LiferayPortletConfiguration;\n\nimport javax.portlet.annotations.LocaleString;\nimport javax.portlet.annotations.PortletConfiguration;\n\n@PortletConfiguration(\n    portletName = FooPortletKeys.Foo,\n    title = @LocaleString(value = FooPortletKeys.Foo))\n@LiferayPortletConfiguration(\n    portletName = FooPortletKeys.Foo,\n    properties = {\n        \"com.liferay.portlet.display-category=category.sample\",\n        \"com.liferay.portlet.instanceable=true\"\n    }\n)\npublic class FooPortlet {\n    ...\n}\n\\end{verbatim}\n\n\\texttt{@PortletConfiguration}'s \\texttt{portletName} attribute names\nthe portlet. It's the only required attribute. The \\texttt{title}\nattribute typically uses a nicer looking name (e.g., uses spaces and\ncapitalization). The \\texttt{title} above is assigned the key constant\n\\texttt{FooPortletKeys.Foo}. You can also localize a title to one or\nmore languages using an array of\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/LocaleString.html}{\\texttt{@LocaleString}}\nannotations, each specifying a different value for the \\texttt{locale}\nelement.\n\nThe \\texttt{@LiferayPortletConfiguration} annotation specifies\nadditional Liferay-specific configuration properties. For example, the\n\\texttt{com.liferay.portlet.display-category} property lets you assign\nthe Widget category where users will find your portlet. Setting the\n\\texttt{com.liferay.portlet.instanceable=true} enables adding multiple\ninstances of the portlet to a page.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\texttt{@PortletConfiguration} and\n\\texttt{@LiferayPortletConfiguration} annotations are respectively\nsynonymous with the \\texttt{javax.portlet.*} and\n\\texttt{com.liferay.portlet.*} properties in the OSGi\n\\texttt{@Component} annotation (used in\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{Liferay\nMVC Portlets}, for example). If you're familiar with the\n\\texttt{portlet.xml} and \\texttt{liferay-portlet.xml} descriptors, the\n\\href{/docs/7-2/reference/-/knowledge_base/r/portlet-descriptor-to-osgi-service-property-map}{Portlet\nDescriptor to OSGi Service Property Map} shows you the OSGi\n\\texttt{@Component} property equivalent. There's an\n\\texttt{@PortletConfiguration} or \\texttt{@LiferayPortletConfiguration}\nequivalent setting for each \\texttt{@Component} property.\n\n\\noindent\\hrulefill\n\nTo opt-in to Portlet 3.0 features, add the following\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/PortletApplication.html}{\\texttt{@PortletApplication}}\nannotation to the class.\n\n\\begin{verbatim}\n@PortletApplication(version=\"3.0\")\n\\end{verbatim}\n\nOnce you've configured your portlet, you should declare the objects it\nuses (depends on).\n\n\\section{Dependency Injection}\\label{dependency-injection}\n\nBean Portlets use the \\texttt{@Inject} CDI annotation (by default) to\ninject dependencies. Apply the annotation to a field you want injected\nwith an object of the specified type. This example portlet injects the\nportlet's \\texttt{PortletConfig} object.\n\n\\begin{verbatim}\nimport javax.inject.Inject;\n\nimport javax.portlet.PortletConfig;\n\npublic class FooPortlet {\n\n    @Inject\n    PortletConfig portletConfig;\n\n    // Invoke methods on portletConfig ...\n\n}\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:}\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/osgi-cdi-integration}{OSGi\nIntegration} allows you to use OSGi services (e.g., Liferay's\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/service/UserLocalService.html}{\\texttt{UserLocalService}})\nin your Bean Portlets.\n\n\\noindent\\hrulefill\n\nPortlet 3.0 defines annotations for declaring methods that handle\nportlet phases.\n\n\\section{Portlet Phase Methods}\\label{portlet-phase-methods}\n\nPhase method annotations apply methods for handling a portlet's phases.\nYou can add them to methods in any class anywhere in the portlet WAR.\nThere's no mandatory method naming convention: assign a phase annotation\nto the methods you want to invoke to process the phase. Here are the\nannotations:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.6154}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.3846}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nPhase\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nAnnotation\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nHeader (new) &\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/HeaderMethod.html}{\\texttt{@HeaderMethod}} \\\\\nRender &\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/RenderMethod.html}{\\texttt{@RenderMethod}} \\\\\nAction &\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/ActionMethod.html}{\\texttt{@ActionMethod}} \\\\\nEvent &\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/EventMethod.html}{\\texttt{@EventMethod}} \\\\\nResource-serving &\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/ServeResourceMethod.html}{\\texttt{@ServeResourceMethod}} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nYou can specify resource dependencies, such as CSS, in the Header phase\nprior to the Render phase. It helps you avoid loading the same resources\nmultiple times.\n\nYou'll definitely want to define a Render method. For example, here's a\nmethod invoked during the Render phase:\n\n\\begin{verbatim}\nimport javax.portlet.annotations.RenderMethod;\n\n@RenderMethod(\n    include = \"/WEB-INF/jsp/view.jsp\",\n    portletNames = {FooPortletKeys.Foo})\npublic String doView() {\n    return \"Hello from \" + portletConfig.getPortletName();\n}\n\\end{verbatim}\n\nThe \\texttt{@RenderMethod} annotation sets the method to be invoked\nduring the Render phase of the WAR's portlets matching any of the names\nlisted for the \\texttt{portletNames} attribute.\n\nThe example Render method produces this content:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  A string greeting\n  \\texttt{\"Hello\\ from\\ \"\\ +\\ portletConfig.getPortletName()}\n\\item\n  The JSP template \\texttt{/WEB-INF/jsp/view.jsp}---the\n  \\texttt{@RenderMethod} annotation's \\texttt{include} attribute\n  references it.\n\\end{enumerate}\n\nThese are just a few of the Portlet 3.0 features that facilitate\ndeveloping applications. This section covers more Portlet 3.0 features\nand Bean Portlet demonstrations. Creating and deploying your own Bean\nPortlet is next.\n\n\\chapter{Creating a Bean Portlet}\\label{creating-a-bean-portlet}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Bean Portlet is in development and is not yet\navailable.\n\n\\noindent\\hrulefill\n\nYour first step in developing a Bean Portlet is to create one. Here\nyou'll generate a Bean Portlet project and deploy your Bean Portlet to\nLiferay DXP.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Generate a Bean Portlet project using a Maven command like this:\n\n\\begin{verbatim}\nmvn archetype:generate \\\n-DarchetypeGroupId=com.liferay \\\n-DarchetypeArtifactId=com.liferay.project.templates.cdi.bean.portlet \\\n-DarchetypeVersion=1.0.0 \\\n-DgroupId=com.mycompany \\\n-DartifactId=com.mycompany.demo.bean.portlet\n\\end{verbatim}\n\n  Here's the resulting folder structure for a Bean Portlet class named\n  \\texttt{Foo}:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{com.mycompany.demo.bean.portlet} → Arbitrary project name.\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{src/main/java/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{com.mycompany.constants.FooPortletKeys} → Declares\n        portlet constants.\n      \\item\n        \\texttt{com.mycompany.portlet.FooPortlet} → Bean Portlet class.\n      \\end{itemize}\n    \\item\n      \\texttt{src/main/webapp/WEB-INF/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{jsp/view.jsp} → Default view template.\n      \\item\n        \\texttt{beans.xml} → Signals CDI to scan the portlet for\n        annotations.\n      \\end{itemize}\n    \\item\n      \\texttt{pom.xml} → Specifies the project's dependencies and\n      packaging.\n    \\end{itemize}\n  \\end{itemize}\n\n  Here's the example Bean Portlet class:\n\n\\begin{verbatim}\npackage com.mycompany.portlet;\n\nimport com.mycompany.constants.FooPortletKeys;\n\nimport com.liferay.bean.portlet.LiferayPortletConfiguration;\n\nimport javax.inject.Inject;\n\nimport javax.portlet.PortletConfig;\nimport javax.portlet.annotations.LocaleString;\nimport javax.portlet.annotations.PortletConfiguration;\nimport javax.portlet.annotations.RenderMethod;\n\n@PortletConfiguration(\n    portletName = FooPortletKeys.Foo,\n    title = @LocaleString(value = FooPortletKeys.Foo))\n@LiferayPortletConfiguration(\n    portletName = FooPortletKeys.Foo,\n    properties = {\n        \"com.liferay.portlet.display-category=category.sample\",\n        \"com.liferay.portlet.instanceable=true\"\n    }\n)\npublic class FooPortlet {\n\n    @Inject\n    PortletConfig portletConfig;\n\n    @RenderMethod(\n        include = \"/WEB-INF/jsp/view.jsp\",\n        portletNames = {FooPortletKeys.Foo})\n    public String doView() {\n        return \"Hello from \" + portletConfig.getPortletName();\n    }\n\n}\n\\end{verbatim}\n\\item\n  Set any portlet configuration or Liferay portlet configuration values\n  using\n  \\href{/docs/7-2/reference/-/knowledge_base/r/portlet-descriptor-to-osgi-service-property-map}{\\texttt{@PortletConfiguration}\n  and \\texttt{Liferay@PortletConfiguration} attributes}.\n\\item\n  Inject any CDI beans using the \\texttt{@Inject} annotation.\n\\item\n  Update your render method \\texttt{doView} (it's annotated with\n  \\texttt{@RenderMethod}). It displays the template\n  \\texttt{WEB-INF/jsp/view.jsp} by default.\n\\item\n  Add any other logic you like to your portlet class.\n\\item\n  Build your portlet:\n\n\\begin{verbatim}\nmvn clean package\n\\end{verbatim}\n\\item\n  Deploy your portlet by copying the portlet WAR to your\n  \\texttt{{[}Liferay\\ \\ \\ \\ \\ Home{]}/deploy} folder. The\n  \\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{WAB\n  Generator} converts the WAR to an OSGi Web Application Bundle (WAB)\n  and installs it to Liferay's OSGi container.\n\\end{enumerate}\n\nLiferay DXP logs the deployment.\n\n\\begin{verbatim}\nINFO  [main][HotDeployImpl:226] Deploying com.mycompany.demo.bean.portlet from queue\nINFO  [main][PluginPackageUtil:1001] Reading plugin package for com.mycompany.demo.bean.portlet\n...\nINFO  [main][PortletHotDeployListener:181] 1 bean portlets for com.mycompany.demo.bean.portlet are available for use\n\\end{verbatim}\n\nThe Bean Portlet is now available in the Liferay DXP UI. The example\nportlet is in the Widget category you assigned it.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portlet-3-portlet.png}\n\\caption{The Foo portlet prints the message returned from\n\\texttt{doView} method and shows the included JSP's contents.}\n\\end{figure}\n\nCongratulations on creating and deploying a Bean Portlet!\n\n\\section{Related Topics}\\label{related-topics-11}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/osgi-cdi-integration}{OSGi\nCDI Integration}\n\n\\chapter{Service Builder}\\label{service-builder}\n\nAn application without reliable business logic or persistence isn't much\nof an application at all. Unfortunately, writing your own persistence\ncode often takes a great deal of time. Service Builder is an\nobject-relational mapping tool that can generate your model,\npersistence, and service layers from a single \\texttt{xml} file. Once\ngenerated, the code is completely customizable: you can write your own\npersistence code along with custom SQL if necessary. Regardless of how\nyou produce your persistence code, you can then use Service Builder to\nimplement your app's business logic.\n\nThis section demonstrates using Service Builder to\n\n\\begin{itemize}\n\\item\n  Generate and customize your persistence framework\n\\item\n  Implement your business logic\n\\end{itemize}\n\nWhen you configure your model and its relationships in your\n\\texttt{service.xml} file and run Service Builder, it generates these\nlayers of code:\n\n\\begin{itemize}\n\\item\n  \\textbf{Model layer:} defines objects to represent your project's\n  entities.\n\\item\n  \\textbf{Persistence layer:} saves entities to and retrieves entities\n  from the database and updates entities.\n\\item\n  \\textbf{Service layer:} a blank layer ready for you to create your API\n  and business logic. .\n\\end{itemize}\n\nHere are some key features these layers contain:\n\n\\begin{itemize}\n\\item\n  Stubbed-out classes for implementing custom business logic\n\\item\n  Hibernate configurations\n\\item\n  Configurable caching support\n\\item\n  Flexibility and support for adding custom SQL queries and dynamic\n  queries\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You don't have to use Service Builder for your back-end\nservices on @product. It's entirely possible to use your persistence\nframework of choice, such as JPA or Hibernate. Note that ``under the\nhood,'' Service Builder uses Hibernate.\n\n\\noindent\\hrulefill\n\n\\section{Customization via Implementation\nClasses}\\label{customization-via-implementation-classes}\n\nEach entity Service Builder generates contains these\n\\emph{implementation} classes:\n\n\\begin{itemize}\n\\item\n  \\textbf{Entity implementation} (\\texttt{*Impl.java}): Is responsible\n  for customizing the entity.\n\\item\n  \\textbf{Local service implementation}\n  (\\texttt{*LocalServiceImpl.java}): Is responsible for calling the\n  persistence layer to retrieve and store data entities. Local services\n  contain the business logic and access the persistence layer. They can\n  be invoked by client code running in the same Java Virtual Machine.\n\\item\n  \\textbf{Remote service implementation} (\\texttt{*ServiceImpl.java}):\n  Generated if the \\texttt{service.xml} is configured for remote\n  services. Remote services usually contain permission checking code and\n  are meant to be accessible from outside the JVM. Service Builder\n  automatically generates code that makes the remote services available\n  via JSON or SOAP, and you can also create your own remote APIs through\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/rest-builder}{REST Builder}\n  or \\href{/docs/7-2/frameworks/-/knowledge_base/f/jax-rs}{JAX-RS}.\n\\end{itemize}\n\nThese classes are where you implement custom business logic. They're the\nonly classes generated by Service Builder intended for customization.\n\n\\section{Hibernate Configurations}\\label{hibernate-configurations}\n\nService Builder uses the Hibernate persistence framework for\nobject-relational mapping. Service Builder hides the complexities of\nHibernate, while still giving you access to technology like dynamic\nqueries and custom SQL. You can take advantage of Object-Relational\nMapping (ORM) in your projects without having to manually set up a\nHibernate environment or make any configurations.\n\n\\section{Caching}\\label{caching}\n\nService Builder caches objects at three levels: \\emph{entity},\n\\emph{finder}, and \\emph{Hibernate}. By default, Liferay uses Ehcache as\nan underlying cache provider for each of these cache levels. However,\nthis is configurable via\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\nproperties}. All you must do to enable entity and finder caching in your\nproject is to set the \\texttt{cache-enabled=true} attribute of your\nentity's \\texttt{\\textless{}entity\\textgreater{}} element in your\n\\texttt{service.xml} file.\n\\href{/docs/7-2/deploy/-/knowledge_base/d/enabling-cluster-link}{Liferay\nClustering} describes Liferay caching in a cluster.\n\n\\section{Dynamic Query and Custom SQL\nQuery}\\label{dynamic-query-and-custom-sql-query}\n\nService Builder automates many of the common tasks associated with\ncreating database persistence code but it doesn't prevent you from\ncreating \\href{/docs/7-2/appdev/-/knowledge_base/a/custom-sql}{custom\nSQL queries}. You can define custom SQL queries in an XML file and\nimplement finder methods to run the queries. If you have some crazy join\nto do, Service Builder gets out of your way. You can also use\n\\href{/docs/7-2/appdev/-/knowledge_base/a/dynamic-query}{dynamic query}\nto access Hibernate's criteria API.\n\nService Builder is used exclusively throughout Liferay DXP and its\napplications, so it's well-tested and robust. It saves lots of\ndevelopment time, both initial development time and time that would have\nto be spent maintaining, extending, or customizing a project. Now\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-a-service-builder-project}{create\nyour own Service Builder project}.\n\n\\chapter{Creating a Service Builder\nProject}\\label{creating-a-service-builder-project}\n\nTo use\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}, you must generate the projects where you'll configure your\nobject-relational map. There's an API project and an implementation\nproject.\n\n\\begin{itemize}\n\\item\n  \\texttt{{[}project{]}/{[}project{]}-api/} → Service interfaces.\n\\item\n  \\texttt{{[}project{]}/{[}project{]}-service/} → Service\n  implementations and supporting files.\n\\end{itemize}\n\nHere's how to create a Service Builder project.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Decide on a project name. If the project is part of an application,\n  name the project after the application.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  a project} using\n  \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI} and\n  the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-service-builder-template}{\\texttt{service-builder}\n  project template}, passing your project name as a parameter. For\n  example, here are Gradle and Maven commands for creating a Service\n  Builder project called \\texttt{guestbook}.\n\n  Gradle:\n\n\\begin{verbatim}\nblade create -t service-builder -p com.liferay.docs.guestbook guestbook\n\\end{verbatim}\n\n  Maven:\n\n\\begin{verbatim}\nmvn archetype:generate \\\n-DarchetypeGroupId=com.liferay \\\n-DarchetypeArtifactId=com.liferay.project.templates.service.builder \\\n-DgroupId=com.liferay \\\n-DartifactId=guestbook \\\n-Dpackage=com.liferay.docs.guestbook \\\n-Dversion=1.0 \\\n-DapiPath=com.liferay.api.path \\\n-DliferayVersion=7.2\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To use the Spring dependency injector instead of the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{Declarative\nServices} dependency injector, use the\n\\texttt{-\\/-dependency-injector\\ spring} option (Blade CLI) or\n\\texttt{-DdependencyInjector=spring} (Maven).\n\n\\noindent\\hrulefill\n\nA message like this one reports project creation success:\n\n\\begin{verbatim}\nSuccessfully created project bookmarks in C:\\workspaces_liferay\\72-ws\\modules\n\\end{verbatim}\n\nBlade CLI generates the parent project folder and sub-folders for the\n\\texttt{*-api} and \\texttt{*-service} module projects.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{guestbook/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{guestbook-api/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{bnd.bnd}\n    \\item\n      \\texttt{build.gradle}\n    \\end{itemize}\n  \\item\n    \\texttt{guestbook-service/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{bnd.bnd}\n    \\item\n      \\texttt{build.gradle}\n    \\item\n      \\texttt{service.xml} → Service definition file.\n    \\end{itemize}\n  \\item\n    \\texttt{build.gradle}\n  \\end{itemize}\n\\end{itemize}\n\nCongratulations! You've created your Service Builder project. The\n\\texttt{service.xml} file is where you'll define your model objects\n(entities) and services.\n\n\\section{Related Topics}\\label{related-topics-12}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Service Builder\nSamples}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-gradle-plugin}{Service\nBuilder Gradle Plugin}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-plugin}{Service\nBuilder Maven Plugin}\n\n\\chapter{Creating the service.xml\nFile}\\label{creating-the-service.xml-file}\n\nTo define a service for your portlet project, you must create a\n\\texttt{service.xml} file. The DTD (Document Type Declaration) file\n\\href{https://docs.liferay.com/ce/portal/7.2-latest/definitions/liferay-service-builder_7_2_0.dtd.html}{liferay-service-builder\\_7\\_2\\_0.dtd}\nspecifies the format and requirements of the XML to use.\n\nA \\texttt{service.xml} was created for you when you\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-a-service-builder-project}{created\nyour Service Builder project}. It's in your \\texttt{*-service} module's\nroot folder with an \\texttt{entity} element named \\emph{Foo}. This is\n(obviously) an example entity, but you can use it as a pattern for\ncreating your own.\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\nDev Studio DXP} provides a Diagram mode and a Source mode to give you\ndifferent perspectives of the service information in your\n\\texttt{service.xml} file.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Diagram mode} facilitates creating and visualizing\n  relationships between service entities.\n\\item\n  \\textbf{Source mode} brings up the \\texttt{service.xml} file's raw XML\n  content in the editor.\n\\end{itemize}\n\nIf you use Liferay Dev Studio DXP, you can switch between these modes.\nOf course, you don't have to use Liferay Dev Studio DXP to work on\nLiferay projects.\n\nNext, you'll specify your service's global information.\n\n\\chapter{Defining Global Service\nInformation}\\label{defining-global-service-information}\n\nA service's global information applies to all its entities. It contains\nthe\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[dependency-injector]{Dependency Injector}\n\\item\n  \\hyperref[package-path]{Package path}\n\\item\n  \\hyperref[namespace-options]{Namespace options}\n\\item\n  \\hyperref[multiversion-concurrency-control-mvcc]{Multiversion\n  concurrency control}\n\\item\n  \\hyperref[author]{Author}\n\\end{itemize}\n\n\\section{Dependency Injector}\\label{dependency-injector}\n\nThe default dependency injector is\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{OSGi\nDeclarative Services}. This makes Service Builder work consistently the\nway other modules do. Prior versions of Liferay used Spring. The only\ndifference is how you inject the services when you\n\\href{/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services}{go to\nuse them later}.\n\nDeclarative Services Dependency Injector:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"ds\"\n         package-path=\"com.liferay.docs.guestbook\">\n\\end{verbatim}\n\nSpring Dependency Injector:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"spring\"\n         package-path=\"com.liferay.docs.guestbook\">\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} When a project is created using the\n\\href{/docs/7-2/reference/-/knowledge_base/r/using-the-service-builder-template}{Service\nBuilder template}, the Declarative Services dependency injector and its\ndependencies are configured for the project by default. To use the\nSpring dependency injector instead, create the project using the Service\nBuilder template and the \\texttt{-\\/-dependency-injector\\ spring} option\n(Blade CLI) or \\texttt{-DdependencyInjector=spring} (Maven).\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Prior to Liferay DXP 7.2, Spring was the sole dependency\ninjector. The services were Spring beans. Liferay's Spring bean\nframework accommodates Spring beans referencing each other: for example,\nSpring bean A has a Spring bean B field and vice versa. When Spring is\nthe dependency injector, the base services Service Builder generates\ninclude local service and persistence fields of all the\n\\texttt{service.xml}'s entities. This causes circular references. Since\nOSGi Declarative Services doesn't accommodate circular references,\nService Builder does not create these fields in the base classes when DS\nis the dependency injector. For more details, see\n\\href{understanding-the-code-generated-by-service-builder}{Understanding\nthe Code}.\n\n\\noindent\\hrulefill\n\n\\section{Package Path}\\label{package-path}\n\nThe package path specifies the package where the service and persistence\nclasses are generated. The package path for Guestbook ensures that the\n\\texttt{*-api} module's service classes are generated in the\n\\texttt{com.liferay.docs.guestbook} package. The persistence classes are\ngenerated in a package of the same name in the \\texttt{*-service}\nmodule. A later article\n\\href{/docs/7-2/appdev/-/knowledge_base/a/running-service-builder}{describes\nthe package content}.\n\n\\section{Multiversion concurrency control\n(MVCC)}\\label{multiversion-concurrency-control-mvcc}\n\nThe \\texttt{service-builder} element's \\texttt{mvcc-enabled} attribute\nis \\texttt{false} by default. Setting \\texttt{mvcc-enabled=\"true\"}\n(hint: edit \\texttt{service.xml} in \\emph{Source} view) enables\n\\href{https://en.wikipedia.org/wiki/Multiversion_concurrency_control}{multiversion\nconcurrency control} (MVCC) for all of the service's entities. In\nsystems, concurrent updates are common. Without MVCC people may read or\noverwrite data from an invalid state unknowingly. With MVCC, each\nmodification is made upon a given base version number. When Hibernate\nreceives the update, it generates an \\texttt{update} SQL statement that\nuses a \\texttt{where} clause to make sure the current data version is\nthe version you expect.\n\nIf the current data version\n\n\\begin{itemize}\n\\item\n  \\textbf{matches the expected version}, your data operation is based on\n  up-to-date data and is accepted.\n\\item\n  \\textbf{doesn't match the expected version}, the data you're operating\n  on is outdated. Liferay DXP rejects your data operation and throws an\n  exception, which you can catch to help the user handle the exception\n  (e.g., suggest retrying the operation).\n\\end{itemize}\n\n\\textbf{Important:} Enable MVCC for all your services by setting\n\\texttt{mvcc-enabled=\"true\"} in your\n\\texttt{\\textless{}service-builder/\\textgreater{}} element. When\ninvoking service entity updates (e.g.,\n\\texttt{fooService.update(object)}), make sure to do so in transactions.\nPropagate rejected transactions to the UI for the user to handle.\n\n\\begin{verbatim}\n<service-builder dependency-injector=\"ds\"\n         package-path=\"com.liferay.docs.guestbook\"\n         mvcc-enabled=\"true\">\n\\end{verbatim}\n\n\\section{Namespace Options}\\label{namespace-options}\n\nService Builder names the database tables using the service namespace.\nFor example, \\emph{GB} could serve as the namespace for a Guestbook\napplication service.\n\n\\begin{verbatim}\n<namespace>GB</namespace>\n\\end{verbatim}\n\nService Builder uses the namespace in the following SQL scripts it\ngenerates in your \\texttt{src/main/resources/sql} folder:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{indexes.sql}\n\\item\n  \\texttt{sequences.sql}\n\\item\n  \\texttt{tables.sql}\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The generated SQL script folder location is configurable.\nFor example, if you're using Gradle, you can define the \\texttt{sqlDir}\nsetting in the project's Gradle \\texttt{build.gradle} file or Maven\n\\texttt{pom.xml} file, the same way the \\texttt{databaseNameMaxLength}\nsetting is applied in the examples below.\n\n\\noindent\\hrulefill\n\nService Builder uses the SQL scripts to create database tables for all\nthe entities the \\texttt{service.xml} defines. The database table names\nhave the namespace prepended when they are created. Since the example\nnamespace value is \\texttt{GB}, the database table names created for the\nentities start with \\texttt{GB\\_\\_} as their prefix. Each Service\nBuilder project's namespace must be unique. Separate plugins should use\nseparate namespaces and should not use a namespace already used by\nLiferay entities (such as \\texttt{Users} or \\texttt{Groups}). Check the\ntable names in Liferay's database to see the namespaces already in use.\n\n\\textbf{Warning:} Use caution when assigning namespace values. Some\ndatabases have strong restrictions on database table and column name\nlengths. The Service Builder\n\\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-gradle-plugin\\#task-properties}{Gradle}\nand\n\\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-plugin\\#available-parameters}{Maven}\nplugin parameter \\texttt{databaseNameMaxLength} sets the maximum length\nyou can use for your table and column names. Here are paraphrased\nexamples of setting \\texttt{databaseNameMaxLength} in build files:\n\n\\textbf{Gradle \\texttt{build.gradle}}\n\n\\begin{verbatim}\nbuildService {\n    ...\n    databaseNameMaxLength = 64\n    ...\n}\n\\end{verbatim}\n\n\\textbf{Maven \\texttt{pom.xml}}\n\n\\begin{verbatim}\n<configuration>\n    ...\n    <databaseNameMaxLength>64</databaseNameMaxLength>\n    ...\n</configuration>\n\\end{verbatim}\n\n\\section{Author}\\label{author}\n\nAs the last piece of global information, enter your name as the\nservice's \\emph{author} in your \\texttt{service.xml} file. Service\nBuilder adds \\texttt{@author} annotations with the specified name to all\nthe Java classes and interfaces it generates. Save your\n\\texttt{service.xml} file. Next, you'll add entities for your services.\n\n\\begin{verbatim}\n<author>Liferay</author>\n\\end{verbatim}\n\n\\chapter{Defining Service Entities}\\label{defining-service-entities}\n\nEntities are the heart and soul of a service. They represent the map\nbetween the model objects in Java and your database fields and tables.\nService Builder maps your Java model to the entities you define\nautomatically, giving you a facility for taking Java objects and\npersisting them. For the Guestbook application, two entities are created\naccording to its \\texttt{service.xml}: one for Guestbooks and one for\nGuestbook Entries.\n\nHere's a summary of the \\texttt{Guestbook} entity information:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Name:} \\texttt{Guestbook}\n\\item\n  \\textbf{Local service:} \\emph{yes}\n\\item\n  \\textbf{Remote service:} \\emph{yes}\n\\end{itemize}\n\nAnd here's what is used for the \\texttt{GuestbookEntry} entity:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Name:} \\texttt{GuestbookEntry}\n\\item\n  \\textbf{Local service:} \\emph{yes}\n\\item\n  \\textbf{Remote service:} \\emph{yes}\n\\end{itemize}\n\nHere's how you define entities:\n\n\\begin{verbatim}\n<entity name=\"Guestbook\" uuid=\"true\" local-service=\"true\" remote-service=\"true\">\n</entity>\n\n<entity name=\"GuestbookEntry\" uuid=\"true\" local-service=\"true\" remote-service=\"true\">\n</entity>\n\\end{verbatim}\n\nThe entity's database table name includes the entity name prefixed with\nthe namespace. The Guestbook example creates one database table named\n\\texttt{GW\\_Guestbook} and another named \\texttt{GB\\_GuestbookEntry}.\n\nSetting \\emph{Local Service} (the \\texttt{local-service} attribute) to\n\\texttt{true} instructs Service Builder to generate local interfaces for\nthe entity's services. Local services can only be invoked from the\nLiferay server on which they're deployed.\n\nSetting \\emph{Remote Service} (the \\texttt{remote-service} attribute) to\n\\texttt{true} instructs Service Builder to generate remote interfaces\nfor the service. You can build a fully-functional application without\ngenerating remote services. In that case, you could set your entity\nlocal services to \\texttt{true} and remote services to \\texttt{false}.\nIf, however, you want to enable remote access to your application's\nservices, set both local service and remote service to \\texttt{true}.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} Suppose you have an existing Data Access Object (DAO)\nservice for an entity built using some other framework such as JPA. You\ncan set local service to \\texttt{false} and remote service to\n\\texttt{true} so that the methods of your remote \\texttt{-Impl} class\ncan call the methods of your existing DAO. This enables your entity to\nintegrate with Liferay's permission-checking system and provides access\nto the web service APIs generated by Service Builder. This is a very\nhandy, quite powerful, and often used feature of Liferay.\n\n\\noindent\\hrulefill\n\nNow that you've seen how to create your application's entities, you'll\nlearn how to describe their attributes using entity \\emph{columns}.\n\n\\chapter{Defining the Columns (Attributes) for Each Service\nEntity}\\label{defining-the-columns-attributes-for-each-service-entity}\n\nAn entity's columns represent its attributes. These attributes map table\nfields to Java object fields. To add attributes for your entity, add\n\\texttt{\\textless{}column\\ /\\textgreater{}} tags to your entity\ndefinition:\n\n\\begin{verbatim}\n<column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n\\end{verbatim}\n\nService Builder creates a database field for each column you add to the\n\\texttt{service.xml} file. It maps a database field type appropriate to\nthe Java type specified for each column, and it does this across all the\ndatabases Liferay supports. Once Service Builder runs, it generates a\nHibernate configuration that handles the object-relational mapping.\nService Builder automatically generates getter/setter methods in the\nmodel class for these attributes. The column's name specifies the name\nused in the getters and setters that are created for the entity's Java\nfield. The column's type indicates the Java type of this field for the\nentity. If a column's \\texttt{primary} (i.e., primary key) attribute is\nset to \\texttt{true}, the column becomes part of the primary key for the\nentity. If only one column has \\texttt{primary} set to \\texttt{true},\nthat column represents the entire primary key for the entity. This is\nthe case in the Guestbook application. If you define multiple columns\nwith the \\texttt{primary} attribute set to true, the combination of\ncolumns makes up a compound primary key for the entity.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The\n\\href{/docs/7-2/appdev/-/knowledge_base/a/implementing-an-add-method\\#step-3-generate-a-primary-key}{Implementing\nan Add Method} article demonstrates how to generate unique primary keys\nfor entity instances.\n\n\\noindent\\hrulefill\n\n\\section{Create Entity Columns}\\label{create-entity-columns}\n\nDefine the columns you need for your first entity. The Guestbook entity\nis simple: it has only two attributes; a primary key and a name:\n\n\\begin{verbatim}\n<column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n<column name=\"name\" type=\"String\" />\n\\end{verbatim}\n\n\\textbf{Note}: On deploying a \\texttt{*service} module, Service Builder\nautomatically generates indexes for all entity primary keys.\n\nCreate a column for each attribute of your entity or entities, using the\nJava type you'll use in your application. Service Builder handles\nmapping it to SQL for you.\n\n\\section{Support Multi-tenancy}\\label{support-multi-tenancy}\n\nIn addition to columns for your entity's primary key and attributes, add\nportal instance ID and site ID columns. Then you can support Liferay's\nmulti-tenancy features, so that each portal instance and each Site in a\nportal instance can have independent sets of your application's data. To\nhold the site's ID, add a column called \\texttt{groupId} of type\n\\texttt{long}. To hold the portal instance's ID, add a column called\n\\texttt{companyId} of type \\texttt{long}:\n\n\\begin{verbatim}\n<!-- Group instance -->\n\n<column name=\"groupId\" type=\"long\" />\n<column name=\"companyId\" type=\"long\" />\n\\end{verbatim}\n\n\\section{Workflow Fields}\\label{workflow-fields}\n\nYou can support Liferay's\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow}{workflow system} by\nadding the fields it needs to track an entity's progress:\n\n\\begin{verbatim}\n<!-- Status fields -->\n\n<column name=\"status\" type=\"int\" />\n<column name=\"statusByUserId\" type=\"long\" />\n<column name=\"statusByUserName\" type=\"String\" />\n<column name=\"statusDate\" type=\"Date\" />\n\\end{verbatim}\n\n\\section{Audit Entities}\\label{audit-entities}\n\nFinally, you can add columns to help audit your entities. To track each\nentity instance's owner, add a column called \\texttt{userId} of type\n\\texttt{long}. Create a column named \\texttt{createDate} of type\n\\texttt{Date} to note an entity instance's creation date. Add a column\nnamed \\texttt{modifiedDate} of type \\texttt{Date} to track the last time\nan entity instance was modified.\n\n\\begin{verbatim}\n<!-- Audit fields -->\n\n<column name=\"userId\" type=\"long\" />\n<column name=\"userName\" type=\"String\" />\n<column name=\"createDate\" type=\"Date\" />\n<column name=\"modifiedDate\" type=\"Date\" />\n\\end{verbatim}\n\nGreat! Your entities have columns that not only represent their\nattributes, but also support multi-tenancy, workflow, and auditing.\nNext, you'll learn how to specify the relationship service entities.\n\n\\chapter{Defining Relationships Between Service\nEntities}\\label{defining-relationships-between-service-entities}\n\nRelationships between database entities or Java objects are necessary\nfor most applications. The Guestbook application, therefore, defines a\nrelationship between a Guestbook and its entries.\n\nAs mentioned earlier, each entry must belong to a particular Guestbook.\nTherefore, each \\texttt{GuestbookEntry} entity must relate to a\n\\texttt{Guestbook} entity.\n\nCreate the \\texttt{GuestbookEntry} entity's fields:\n\n\\begin{verbatim}\n<entity name=\"GuestbookEntry\" local-service=\"true\" uuid=\"true\" remote-service=\"true\">\n\n    <column name=\"entryId\" primary=\"true\" type=\"long\" />\n    <column name=\"name\" type=\"String\" />\n    <column name=\"email\" type=\"String\" />\n    <column name=\"message\" type=\"String\" />\n    <column name=\"guestbookId\" type=\"long\" />\n\\end{verbatim}\n\nNote the last field in the list is the \\texttt{guestbookId} field. Since\nit's the same name as the \\texttt{Guestbook} object's primary key, a\nrelationship is created between the two objects. If you're using Liferay\nDev Studio DXP, you can see this relationship in its diagram mode.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/service-builder-relate-entities.png}\n\\caption{Relating entities is a snap in Liferay Dev Studio DXP's\n\\emph{Diagram} mode for \\texttt{service.xml}.}\n\\end{figure}\n\nCongratulations! You've related two entities.\n\nNext, add the instance, audit, and status fields mentioned from the\nprevious step to enable Liferay's multi-tenancy, audit, and workflow\nfeatures.\n\nNow that your entity columns are in place and entity relationships are\nestablished, you can specify the default order in which the entity\ninstances are retrieved from the database.\n\n\\chapter{Defining Ordering of Service Entity\nInstances}\\label{defining-ordering-of-service-entity-instances}\n\nOften, you want to retrieve multiple instances of a given entity and\nlist them in a particular order. The \\texttt{service.xml} file lets you\nspecify the default order of your entities.\n\nSuppose you want to return \\texttt{GuestbookEntry} entities by their\ncreation date. It's easy to specify these default orderings:\n\n\\begin{verbatim}\n<order>\n    <order-column name=\"createDate\" order-by=\"desc\" />\n</order>\n\\end{verbatim}\n\nYou can enter \\texttt{asc} or \\texttt{desc} for ascending or descending\norder.\n\nNow that you know how to order your service entities, the last thing to\ndo is to define the finder methods for retrieving entity instances from\nthe database.\n\n\\chapter{Defining Service Entity Finder\nMethods}\\label{defining-service-entity-finder-methods}\n\nFinder methods retrieve entity objects from the database based on\nspecified parameters. For each finder defined, Service Builder generates\nseveral methods to fetch, find, remove, and count entity instances based\non the finder's parameters.\n\nWhen supporting Liferay's multi-tenancy, it's important to be able to\nfind its entities per Site.\n\n\\section{Creating Finders}\\label{creating-finders}\n\nFinders are easy to create:\n\n\\begin{verbatim}\n<finder name=\"GroupId\" return-type=\"Collection\">\n   <finder-column name=\"groupId\" />\n</finder> \n\\end{verbatim}\n\nThe example above is among the simplest of finders, and is one you\nshould always add if you're supporting multi-tenancy. This finder\nreturns a collection of objects that belong to the Site on which your\napplication has been placed. Service Builder generates finder-related\nmethods (e.g., \\texttt{fetchByGroupId}, \\texttt{findByGroupId},\n\\texttt{removeByGroupId}, \\texttt{countByGroupId}) for the your entities\nin the \\texttt{*Persistence} and \\texttt{*PersistenceImpl} classes. The\nfirst of these classes is the interface; the second is its\nimplementation. For example, the Guestbook application generates its\nentity finder methods in the \\texttt{-Persistence} classes found in the\n\\texttt{/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence}\nfolder and the \\texttt{-PersistenceImpl} classes in the\n\\texttt{/guestbook/src/main/java/com/liferay/docs/service/persistence/impl}\nfolder.\n\nYou're not limited to finding by one column, however; you can create\nmulti-column finders:\n\n\\begin{verbatim}\n<finder name=\"G_S\" return-type=\"Collection\">\n    <finder-column name=\"groupId\" />\n    <finder-column name=\"status\" />\n</finder>\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Important}: DO NOT create finders that use entity primary key as\nparameters. They're unnecessary as Service Builder automatically\ngenerates \\texttt{findByPrimaryKey} and \\texttt{fetchByPrimaryKey}\nmethods for all entity primary keys. On deploying a \\texttt{*service}\nmodule, Service Builder creates indexes for all entity primary key\ncolumns and finder columns. Adding finders that use entity primary keys\nresults in attempts to create multiple indexes for the same\ncolumns---Oracle DB, for example, reports these attempts as errors.\n\n\\noindent\\hrulefill\n\nNow you know to configure Service Builder to create finder methods for\nyour entity. Terrific!\n\nNow that you've specified the service for your project, you're ready to\n\\emph{build} the service by running Service Builder. It's time to\n\\href{/docs/7-2/appdev/-/knowledge_base/a/running-service-builder}{run\nService Builder and examine the code it generates}.\n\n\\chapter{Running Service Builder}\\label{running-service-builder}\n\nHere you'll learn how to run Service Builder. If want to use Service\nBuilder in your application but haven't yet\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file}{created\na \\texttt{service.xml} file that defines an object-relational map for\nyou application}, make sure to do so before proceeding.\n\nOpen a command line and navigate to your application folder (the folder\nthat contains your \\texttt{*-api} and \\texttt{*-service} modules).\n\n\\section{Gradle}\\label{gradle}\n\nTo build your services using Gradle, enter the following command:\n\n\\begin{verbatim}\nblade gw buildService\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\ngradlew buildService\n\\end{verbatim}\n\nBlade's \\texttt{gw} command works in any project that has a Gradle\nWrapper available to it. Projects generated using Liferay project\ntemplates have a Gradle Wrapper.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Liferay Workspace's Gradle Wrapper script is in the\nworkspace root folder. If your application project folder is located at\n\\texttt{{[}workspace{]}/modules/{[}application{]}}, for example, the\nGradle Wrapper is available at \\texttt{../../gradlew}.\n\n\\noindent\\hrulefill\n\n\\section{Maven}\\label{maven}\n\nIf you're using Maven, build the services by running the following\ncommand:\n\n\\begin{verbatim}\nmvn service-builder:build\n\\end{verbatim}\n\n\\textbf{Important:} The \\texttt{mvn\\ service-builder:build} command only\nworks if you're using the\n\\texttt{com.liferay.portal.tools.service.builder} plugin version\n1.0.145+. Maven projects using an earlier version of the Service Builder\nplugin should update their POM accordingly. More information is\navailable on\n\\href{/docs/7-2/reference/-/knowledge_base/r/using-service-builder-in-a-maven-project}{using\nMaven to run Service Builder}.\n\nOn successfully building the services, Service Builder prints the\nmessage \\texttt{BUILD\\ SUCCESSFUL}. Many generated files appear in your\nproject. They represent a model layer, service layer, and persistence\nlayer for your entities. Don't worry about the number of generated\nfiles---they're explained in the next article, where you can review the\ncode Service Builder generates for your entities.\n\n\\chapter{Understanding the Code Generated by Service\nBuilder}\\label{understanding-the-code-generated-by-service-builder}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/running-service-builder}{Service\nBuilder generates code} to support your entities. The files listed under\nLocal Service and Remote Service below are only generated for an entity\nthat has both \\texttt{local-service} and \\texttt{remote-service}\nattributes set to \\texttt{true}. Service Builder generates services for\nthese entities in your application's \\texttt{*-api} and\n\\texttt{*-service} modules in the packages you specified in\n\\texttt{service.xml}. For example, here are the package paths for\nLiferay's Bookmarks application:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{/guestbook-api/src/main/java/com/liferay/docs/guestbook}\n\\item\n  \\texttt{/guestbook-service/src/main/java/com/liferay/docs/guestbook}\n\\end{itemize}\n\nThe \\texttt{guestbook-api} module's interfaces define the Guestbook\napplication API. The \\texttt{*-api} module interfaces define the\napplication's persistence layer, service layer, and model layer.\nWhenever you compile and deploy the \\texttt{*-api} module, all its\nclasses and interfaces are packaged in a \\texttt{.jar} file called\n\\texttt{PROJECT\\_NAME-api.jar} in the module's \\texttt{build/libs}\nfolder. Deploying this JAR to Liferay \\emph{defines} the API as OSGi\nservices.\n\nThe \\texttt{guestbook-service} module classes implement the\n\\texttt{guestbook-api} module interfaces. The \\texttt{*-service} module\nprovides the OSGi service implementations to deploy to Liferay's OSGi\nframework.\n\nNext, examine the classes and interfaces generated for the entities you\nspecified. Similar classes are generated for each entity, depending on\nhow each entity is specified in the \\texttt{service.xml}. Here are the\nthree types of customizable classes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{*LocalServiceImpl}\n\\item\n  \\texttt{*ServiceImpl}\n\\item\n  \\texttt{*Impl}\n\\end{itemize}\n\nThe \\texttt{*} represents the entity name in the classes listed above.\n\nHere are the persistence, service, and model classes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Persistence\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}Persistence}: Persistence interface that\n    defines CRUD methods for the entity such as \\texttt{create},\n    \\texttt{remove}, \\texttt{countAll}, \\texttt{find}, \\texttt{findAll},\n    etc.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}PersistenceImpl}: Persistence\n    implementation class that implements\n    \\texttt{{[}ENTITY\\_NAME{]}Persistence}.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}Util}: Persistence utility class that\n    wraps \\texttt{{[}ENTITY\\_NAME{]}PersistenceImpl} and provides direct\n    access to the database for CRUD operations. This utility should only\n    be used by the service layer; in your portlet classes, use the\n    \\texttt{{[}ENTITY\\_NAME{]}} class by referencing it with the\n    \\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{\\texttt{@Reference}\n    annotation}.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/service-builder-persistence-diagram.png}\n  \\caption{Service Builder generates these persistence classes and\n  interfaces for an example entity called \\emph{Event}. You shouldn't\n  (and you won't need to) customize any of these classes or interfaces.}\n  \\end{figure}\n\\item\n  Local Service (generated for an entity only if the entity's\n  \\texttt{local-service} attribute is set to \\texttt{true} in\n  \\texttt{service.xml})\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}LocalService}: Local service interface.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}LocalServiceImpl} (\\textbf{LOCAL SERVICE\n    IMPLEMENTATION}): Local service implementation. This is the only\n    class in the local service that you should modify: it's where you\n    add your business logic. For any methods added here, Service Builder\n    adds corresponding methods to the\n    \\texttt{{[}ENTITY\\_NAME{]}LocalService} interface the next time you\n    run it.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}LocalServiceBaseImpl}: Local service base\n    implementation. This is an abstract class. Service Builder injects a\n    number of instances of various service and persistence classes into\n    this class. \\texttt{@abstract}\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}LocalServiceUtil}: Local service utility\n    class which wraps \\texttt{{[}ENTITY\\_NAME{]}LocalServiceImpl}. This\n    class is generated for backwards compatibility purposes only. Use\n    the \\texttt{*LocalService} class by referencing it with the\n    \\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{\\texttt{@Reference}\n    annotation}.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}LocalServiceWrapper}: Local service\n    wrapper which implements \\texttt{{[}ENTITY\\_NAME{]}LocalService}.\n    This class is designed to be extended and it lets you\n    \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers}{customize\n    the entity's local services}.\n  \\end{itemize}\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/service-builder-service-diagram.png}\n\\caption{Service Builder generates these service classes and interfaces.\nOnly the {[}ENTITY\\_NAME{]}LocalServiceImpl (e.g., EventLocalServiceImpl\nfor the Event entity) allows custom methods to be added to the service\nlayer.}\n\\end{figure}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Remote Service (generated for an entity only if an entity's\n  \\texttt{remote-service} attribute is \\emph{not} set to \\texttt{false}\n  in \\texttt{service.xml})\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}Service}: Remote service interface.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}ServiceImpl} (\\textbf{REMOTE SERVICE\n    IMPLEMENTATION}): Remote service implementation. This is the only\n    class in the remote service that you should modify manually. Here,\n    you can write code that adds additional security checks and invokes\n    the local services. For any custom methods added here, Service\n    Builder adds corresponding methods to the\n    \\texttt{{[}ENTITY\\_NAME{]}Service} interface the next time you run\n    it.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}ServiceBaseImpl}: Remote service base\n    implementation. This is an abstract class. \\texttt{@abstract}\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}ServiceUtil}: Remote service utility class\n    which wraps \\texttt{{[}ENTITY\\_NAME{]}ServiceImpl}. This class is\n    generated for backwards compatibility purposes only. Use the\n    \\texttt{*Service} class by referencing it with the\n    \\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{\\texttt{@Reference}\n    annotation}.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}ServiceWrapper}: Remote service wrapper\n    which implements \\texttt{{[}ENTITY\\_NAME{]}Service}. This class is\n    designed to be extended and it lets you\n    \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers}{customize\n    the entity's remote services}.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}ServiceSoap}: SOAP utility which the\n    remote \\texttt{{[}ENTITY\\_NAME{]}ServiceUtil} remote service utility\n    can access.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}Soap}: SOAP model, similar to\n    \\texttt{{[}ENTITY\\_NAME{]}ModelImpl}.\n    \\texttt{{[}ENTITY\\_NAME{]}Soap} is serializable; it does not\n    implement \\texttt{{[}ENTITY\\_NAME{]}}.\n  \\end{itemize}\n\\item\n  Model\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}Model}: Base model interface. This\n    interface and its \\texttt{{[}ENTITY\\_NAME{]}ModelImpl}\n    implementation serve only as a container for the default property\n    accessors Service Builder generates. Any helper methods and all\n    application logic should be added to\n    \\texttt{{[}ENTITY\\_NAME{]}Impl}.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}ModelImpl}: Base model implementation.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}}: \\texttt{{[}ENTITY\\_NAME{]}} model\n    interface which extends \\texttt{{[}ENTITY\\_NAME{]}Model}.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}Impl}: (\\textbf{MODEL IMPLEMENTATION})\n    Model implementation. You can use this class to add helper methods\n    and application logic to your model. If you don't add any helper\n    methods or application logic, only the auto-generated field getters\n    and setters are available. Whenever you add custom methods to this\n    class, Service Builder adds corresponding methods to the\n    \\texttt{{[}ENTITY\\_NAME{]}} interface the next time you run it.\n  \\item\n    \\texttt{{[}ENTITY\\_NAME{]}Wrapper}: Wrapper, wraps\n    \\texttt{{[}ENTITY\\_NAME{]}}. This class is designed to be extended\n    and it lets you\n    \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers}{customize\n    the entity}.\n  \\end{itemize}\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/service-builder-model-diagram.png}\n\\caption{Service Builder generates these model classes and interfaces.\nOnly \\texttt{{[}ENTITY\\_NAME{]}Impl} (e.g., \\texttt{EventImpl} for the\nEvent entity) allows custom methods to be added to the service layer.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} \\texttt{*Util} classes are generated for backwards\ncompatibility purposes only. Your module applications should avoid\ncalling the util classes. Use the non-util classes instead--you can\nreference them using the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{\\texttt{@Reference}\nannotation}.\n\n\\noindent\\hrulefill\n\nEach file that Service Builder generates is assembled from an associated\nFreeMarker template. The FreeMarker templates are in the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-service-builder}{portal-tools-service-builder\nmodule's}\n\\texttt{src/main/resources/com/liferay/portal/tools/service/builder/dependencies/}\nfolder. For example, Service Builder uses the \\texttt{service\\_impl.ftl}\ntemplate to generate the \\texttt{*ServiceImpl.java} classes.\n\nYou can modify any \\texttt{*Impl} class Service Builder generates. The\nmost common are \\texttt{*LocalServiceImpl}, \\texttt{*ServiceImpl} and\n\\texttt{*Impl}. If you modify the other classes, Service Builder\noverwrites the changes the next time you run it. Whenever you add\nmethods to, remove methods from, or change a method signature of a\n\\texttt{*LocalServiceImpl} class, \\texttt{*ServiceImpl} class, or\n\\texttt{*Impl} class, you should run Service Builder again to regenerate\nthe affected interfaces and the service JAR.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Service Builder may generate code that requires adding\ndependencies to your \\texttt{*-service} module build file.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When \\texttt{spring} is the dependency injector (see\n\\emph{Dependency Injector} in\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information}{Defining\nGlobal Service Information}), the \\texttt{-LocalServiceBaseImpl} classes\nService Builder generates include \\texttt{-LocalService} and\n\\texttt{-Persistence} member fields of all the \\texttt{service.xml}'s\nentities. \\texttt{-LocalServiceImpl} classes inherit these fields and\nare Spring beans. The Spring beans can reference each other. For\nexample, Spring bean A can have a Spring bean B field and vice versa.\nLiferay's \\texttt{spring} dependency injector accommodates Spring bean\ncircular references. The \\texttt{ds} dependency injector does not\naccommodate circular references.\n\nWhen using \\texttt{ds} as the dependency injector,\n\\texttt{-LocalServiceImpl} classes are OSGi Declarative Services. Such\nservices start only after all the other services they reference have\nstarted. If declarative service A has a declarative service B member\nfield and vice versa, neither service can start. For this reason, the\n\\texttt{-LocalServiceBaseImpl} classes Service Builder generates don't\ninclude \\texttt{-LocalService} member fields of the\n\\texttt{service.xml}'s other entities. When using the \\texttt{ds}\ndependency injector, you must make sure member fields you add to service\nclasses don't create circular dependencies.\n\n\\noindent\\hrulefill\n\nCongratulations! You've generated your application's initial model,\npersistence, and service layers and you understand the generated code.\n\n\\textbf{Related Topics}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/running-service-builder}{Running\nService Builder}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/understanding-servicecontext}{Understanding\nService Context}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{Creating\nLocal Services}\n\n\\chapter{Iterative Development}\\label{iterative-development}\n\nAs you develop an application, you might need to add fields to your\ndatabase. This is a normal process of iterative development: you get an\nidea for a new feature, or it's suggested to you, and that feature\nrequires additional data in the database. \\textbf{New fields added to\n\\texttt{service.xml} are not automatically added to the database.} To\nadd the fields, you must do one of two things:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Write an\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes}{upgrade\n  process} to modify the tables and preserve the data, or\n\\item\n  Run the \\texttt{cleanServiceBuilder}\n  \\href{/docs/7-2/reference/-/knowledge_base/r/db-support-gradle-plugin}{Gradle\n  task} (also supported on Maven and Ant), which drops your tables so\n  they get re-created the next time your app is deployed. The\n  \\href{/docs/7-2/reference/-/knowledge_base/r/db-support-plugin}{Maven\n  DB Support Plugin} reference article explains how to run this command\n  from a Maven project.\n\\end{enumerate}\n\nUse the first option if you have a released application and you must\npreserve user data. Use the second option if you're adding new columns\nduring development.\n\n\\section{Related Topics}\\label{related-topics-13}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes}{Upgrade\nProcesses}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/db-support-gradle-plugin}{Gradle\nDB Support Plugin}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/db-support-plugin}{Maven DB\nSupport Plugin}\n\n\\chapter{Customizing Model Entities With Model\nHints}\\label{customizing-model-entities-with-model-hints}\n\nOnce you've used Service Builder to define model entities, you may want\nto further refine how users enter that data. For example, model hints\ncan define a calendar field with selectable dates only in the future.\nModel hints specify entity data restrictions and other formatting.\n\nYou define model hints in a file called\n\\texttt{portlet-model-hints.xml}. The \\texttt{portlet-model-hints.xml}\nfile goes in the service module's \\texttt{src/main/resources/META-INF}\nfolder.\n\nModel hints define two things:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  How entities are presented to users\n\\item\n  The size of database columns\n\\end{enumerate}\n\nAs Liferay renders your form fields, it customizes the form's input\nfields based on your configuration.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you chose Spring as the dependency injector, Service\nBuilder generates a number of XML configuration files in your service\nmodule's \\texttt{src/main/resources/META-INF} folder. Service Builder\nuses most of these files to manage Spring and Hibernate configurations.\nDon't modify the Spring or Hibernate configuration files; changes to\nthem are overwritten when Service Builder runs. You can, however, safely\nedit the \\texttt{portlet-model-hints.xml} file.\n\n\\noindent\\hrulefill\n\nSince the Guestbook doesn't have much of a model hints file, as an\nexample, consider the\n\\href{https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.bookmarks.service/}{Bookmarks\napp service module's} model hints file:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<model-hints>\n    <model name=\"com.liferay.bookmarks.model.BookmarksEntry\">\n        <field name=\"uuid\" type=\"String\" />\n        <field name=\"entryId\" type=\"long\" />\n        <field name=\"groupId\" type=\"long\" />\n        <field name=\"companyId\" type=\"long\" />\n        <field name=\"userId\" type=\"long\" />\n        <field name=\"userName\" type=\"String\" />\n        <field name=\"createDate\" type=\"Date\" />\n        <field name=\"modifiedDate\" type=\"Date\" />\n        <field name=\"folderId\" type=\"long\" />\n        <field name=\"treePath\" type=\"String\">\n            <hint name=\"max-length\">4000</hint>\n        </field>\n        <field name=\"name\" type=\"String\">\n            <hint name=\"max-length\">255</hint>\n        </field>\n        <field name=\"url\" type=\"String\">\n            <hint-collection name=\"URL\" />\n            <validator name=\"required\" />\n            <validator name=\"url\" />\n        </field>\n        <field name=\"description\" type=\"String\">\n            <hint-collection name=\"TEXTAREA\" />\n        </field>\n        <field name=\"visits\" type=\"int\" />\n        <field name=\"priority\" type=\"int\">\n            <hint name=\"display-width\">20</hint>\n        </field>\n        <field name=\"lastPublishDate\" type=\"Date\" />\n        <field name=\"status\" type=\"int\" />\n        <field name=\"statusByUserId\" type=\"long\" />\n        <field name=\"statusByUserName\" type=\"String\" />\n        <field name=\"statusDate\" type=\"Date\" />\n    </model>\n    <model name=\"com.liferay.bookmarks.model.BookmarksFolder\">\n        ...\n    </model>\n</model-hints>\n\\end{verbatim}\n\nThe root-level element is \\texttt{model-hints}. Model entities are\nrepresented by \\texttt{model} sub-elements of the \\texttt{model-hints}\nelement. Each \\texttt{model} element must have a \\texttt{name} attribute\nspecifying the fully-qualified class name. Models have \\texttt{field}\nelements representing their entity's columns. Lastly, \\texttt{field}\nelements must have a name and a type. Each \\texttt{field} element's name\nand type maps to the name and type specified for the entity's column in\nthe service module's \\texttt{service.xml} file. Service Builder\ngenerates all these elements for you, based on the \\texttt{service.xml}.\n\nTo add hints to a field, add a \\texttt{hint} child element. For example,\nyou can add a \\texttt{display-width\\ hint} to specify the pixel width to\nuse in displaying the field. The default pixel width is \\texttt{350}. To\nshow a \\texttt{String} field with 50 pixels, you could nest a\n\\texttt{hint} element named \\texttt{display-width} and give it a value\nof \\texttt{50}.\n\nTo see the effect of a hint on a field,\n\\href{/docs/7-2/appdev/-/knowledge_base/a/running-service-builder}{run\nService Builder} again and\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{redeploy\nyour module}. Note that changing \\texttt{display-width} doesn't limit\nthe number of characters a user can enter into the \\texttt{name} field;\nit only controls the field's width in the AlloyUI input form.\n\nTo configure the maximum size of a model field's database column (i.e.,\nthe maximum number of characters that can be saved for the field), use\nthe \\texttt{max-length} hint. The default \\texttt{max-length} value is\n\\texttt{75} characters. If you want the \\texttt{name} field to persist\nup to 100 characters, add a \\texttt{max-length} hint to that field:\n\n\\begin{verbatim}\n<field name=\"name\" type=\"String\">\n    <hint name=\"display-width\">50</hint>\n    <hint name=\"max-length\">100</hint>\n</field>\n\\end{verbatim}\n\nRemember to run Service Builder and redeploy your project after updating\nthe \\texttt{portlet-model-hints.xml} file.\n\n\\section{Model Hint Types}\\label{model-hint-types}\n\nSo far, you've seen a few different hints. The following table describes\nthe portlet model hints available for use.\n\n\\textbf{Model Hint Values and Descriptions}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n  Name       | Value Type | Description | Default |\n\\end{verbatim}\n\n\\texttt{auto-escape} \\textbar{} boolean \\textbar{} sets whether text\nvalues should be escaped via \\texttt{HtmlUtil.escape} \\textbar{} true\n\\textbar{} \\texttt{autoSize} \\textbar{} boolean \\textbar{} displays the\nfield in a for scrollable text area \\textbar{} false \\textbar{}\n\\texttt{day-nullable} \\textbar{} boolean \\textbar{} allows the day to be\nnull in a date field \\textbar{} false \\textbar{} \\texttt{default-value}\n\\textbar{} String \\textbar{} sets the default value of the form field\nrendered using the aui taglib \\textbar{} (empty String) \\textbar{}\n\\texttt{display-height} \\textbar{} integer \\textbar{} sets the display\nheight of the form field rendered using the aui taglib \\textbar{} 15\n\\textbar{} \\texttt{display-width} \\textbar{} integer \\textbar{} sets the\ndisplay width of the form field rendered using the aui taglib \\textbar{}\n350 \\textbar{} \\texttt{editor} \\textbar{} boolean \\textbar{} sets\nwhether to provide an editor for the input \\textbar{} false \\textbar{}\n\\texttt{max-length} \\textbar{} integer \\textbar{} sets the maximum\ncolumn size for SQL file generation \\textbar{} 75 \\textbar{}\n\\texttt{month-nullable} \\textbar{} boolean \\textbar{} allows the month\nto be null in a date field \\textbar{} false \\textbar{} \\texttt{secret}\n\\textbar{} boolean \\textbar{} sets whether to hide the characters input\nby the user \\textbar{} false \\textbar{} \\texttt{show-time} \\textbar{}\nboolean \\textbar{} sets whether to show the time along with the date\n\\textbar{} true \\textbar{} \\texttt{upper-case} \\textbar{} boolean\n\\textbar{} converts all characters to upper case \\textbar{} false\n\\textbar{} \\texttt{year-nullable} \\textbar{} boolean \\textbar{} allows a\ndate field's year to be null \\textbar{} false \\textbar{}\n\\texttt{year-range-delta} \\textbar{} integer \\textbar{} specifies the\nnumber of years to display from today's date in a date field rendered\nwith the aui taglib \\textbar{} 5 \\textbar{} \\texttt{year-range-future}\n\\textbar{} boolean \\textbar{} sets whether to include future dates\n\\textbar{} true \\textbar{} \\texttt{year-range-past} \\textbar{} boolean\n\\textbar{} sets whether to include past dates \\textbar{} true \\textbar{}\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: The aui taglib is fully supported and not related to\nAlloyUI (the JavaScript library) that's deprecated.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: You can use a mix of Clay and aui tags in a form. Model\nhints, however, affect aui tags only.\n\n\\noindent\\hrulefill\n\nNote that Liferay has its own model hints file\n(\\texttt{portal-model-hints.xml}). It's in \\texttt{portal-impl.jar}'s\n\\texttt{META-INF} folder. This file contains many hint examples, so you\ncan reference it when creating \\texttt{portlet-model-hints.xml} files.\n\n\\section{Default Hints}\\label{default-hints}\n\nYou can use the \\texttt{default-hints} element to define a list of hints\nto apply to every field of a model. For example, adding the following\nelement inside a model element applies a \\texttt{display-width} of 300\npixels to each field:\n\n\\begin{verbatim}\n<default-hints>\n    <hint name=\"display-width\">300</hint>\n</default-hints>\n\\end{verbatim}\n\n\\section{Hint Collections}\\label{hint-collections}\n\nYou can define \\texttt{hint-collection} elements inside the\n\\texttt{model-hints} root-level element to define a list of hints to\napply together. A hint collection must have a name. For example,\nLiferay's \\texttt{portal-model-hints.xml} defines the following hint\ncollections:\n\n\\begin{verbatim}\n<hint-collection name=\"CLOB\">\n    <hint name=\"max-length\">2000000</hint>\n</hint-collection>\n<hint-collection name=\"EDITOR\">\n    <hint name=\"editor\">true</hint>\n    <hint name=\"max-length\">2000000</hint>\n</hint-collection>\n<hint-collection name=\"EMAIL-ADDRESS\">\n    <hint name=\"max-length\">254</hint>\n</hint-collection>\n<hint-collection name=\"HOSTNAME\">\n    <hint name=\"max-length\">200</hint>\n</hint-collection>\n<hint-collection name=\"SEARCHABLE-DATE\">\n    <hint name=\"month-nullable\">true</hint>\n    <hint name=\"day-nullable\">true</hint>\n    <hint name=\"year-nullable\">true</hint>\n    <hint name=\"show-time\">false</hint>\n</hint-collection>\n<hint-collection name=\"TEXTAREA\">\n    <hint name=\"display-height\">105</hint>\n    <hint name=\"display-width\">500</hint>\n    <hint name=\"max-length\">4000</hint>\n</hint-collection>\n<hint-collection name=\"URL\">\n    <hint name=\"max-length\">4000</hint>\n</hint-collection>\n\\end{verbatim}\n\nYou can apply a hint collection to a model field by referencing the hint\ncollection's name. For example, if you define a \\texttt{SEARCHABLE-DATE}\ncollection like the one above in your \\texttt{model-hints} element, you\ncan apply it to your model's date field by using a\n\\texttt{hint-collection} element that references the collection by its\nname:\n\n\\begin{verbatim}\n<field name=\"date\" type=\"Date\">\n    <hint-collection name=\"SEARCHABLE-DATE\" />\n</field>\n\\end{verbatim}\n\nSuppose you want to use a couple of model hints in your project. Start\nby providing users with an editor for filling in their comment fields.\nTo apply the same hint to multiple entities, define it as a hint\ncollection. Then reference the hint collection in each entity.\n\nTo define a hint collection, add a \\texttt{hint-collection} element\ninside the \\texttt{model-hints} root element in your\n\\texttt{portlet-model-hints.xml} file. For example:\n\n\\begin{verbatim}\n<hint-collection name=\"COMMENT-TEXTAREA\">\n    <hint name=\"display-height\">105</hint>\n    <hint name=\"display-width\">500</hint>\n    <hint name=\"max-length\">4000</hint>\n</hint-collection>\n\\end{verbatim}\n\nTo reference a hint collection for a specific field, add the\n\\texttt{hint-collection} element inside the field's \\texttt{field}\nelement:\n\n\\begin{verbatim}\n<field name=\"comment\" type=\"String\">\n    <hint-collection name=\"COMMENT-TEXTAREA\" />\n</field>\n\\end{verbatim}\n\nAfter defining hint collections and adding hint collection references,\nrebuild your services, redeploy your project, and check that the hints\ndefined in your hint collection have taken effect.\n\nNice work! Now you can not only influence how your model's input fields\nare displayed, but you can also can set its database table column sizes.\nYou can organize hints, insert individual hints directly into your\nfields, apply a set of default hints to all of a model's fields, or\ndefine collections of hints to apply at either of those scopes.\n\n\\chapter{Configuring\nservice.properties}\\label{configuring-service.properties}\n\nService Builder generates a \\texttt{service.properties} file in your\n\\texttt{*-service} module's \\texttt{src/main/resources} folder. Liferay\nDXP uses this file's properties to alter your service's database schema.\nYou should not modify this file directly, but rather make any necessary\nproperty overrides in a \\texttt{service-ext.properties} file in that\nsame folder.\n\nHere are some of the properties the \\texttt{service.properties} file\nincludes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{build.namespace}: This is the\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file}{namespace\n  you defined in your \\texttt{service.xml}}. Liferay distinguishes\n  different modules from each other using their namespaces.\n\\item\n  \\texttt{build.number}: Liferay distinguishes your module's different\n  Service Builder builds. Each time you deploy a distinct Service\n  Builder build to Liferay, Liferay increments this number.\n\\item\n  \\texttt{build.date}: This is the time of your module's latest Service\n  Builder build.\n\\item\n  \\texttt{include-and-override}: The default value of this property\n  defines \\texttt{service-ext.properties} as an override file for\n  \\texttt{service.properties}.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: In Liferay Portal 6.x Service Builder portlets, the\n\\texttt{build.auto.upgrade} property in \\texttt{service.properties}\napplies Liferay Service schema changes upon rebuilding services and\nredeploying the portlets. This property was deprecated in Liferay 7.0.\n\nThe Build Auto Upgrade feature is now different and is set in a global\nproperty \\texttt{schema.module.build.auto.upgrade} in the file\n\\texttt{{[}Liferay\\_Home{]}/portal-developer.properties}.\n\n\\noindent\\hrulefill\n\nAwesome! You now have all the tools necessary to set up your own\n\\texttt{service-ext.properties} file.\n\n\\section{Related Topics}\\label{related-topics-14}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder?}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{Creating\nLocal Services}\n\n\\chapter{Connecting Service Builder to an External\nDatabase}\\label{connecting-service-builder-to-an-external-database}\n\nIf you want to use a database separate from Liferay DXP's, follow these\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Specify your database and a data source name in your\n  \\texttt{service.xml}.\n\\item\n  Create the database manually.\n\\item\n  Define the data source.\n\\item\n  Connect your Service Builder module to the data source.\n\\item\n  Run Service Builder.\n\\end{enumerate}\n\nThere are two different ways to create the connection:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{\\texttt{DataSourceProvider}:} This approach involves\n  implementing a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/jdbc/DataSourceProvider.html}{\\texttt{DataSourceProvider}}\n  \\href{https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html}{\\texttt{ServiceProviderInterface}}\n  (SPI). This way requires the fewest files and steps and works\n  regardless of whether your Service Builder module uses the \\texttt{ds}\n  or \\texttt{spring}\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information\\#dependency-injector}{dependency\n  injector}.\n\\item\n  \\textbf{Spring Beans:} Configure the connection using Spring XML\n  files. This approach only works with Service Builder modules that use\n  the \\texttt{spring}\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information\\#dependency-injector}{dependency\n  injection option}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} All entities defined in a Service Builder module's\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file}{\\texttt{service.xml}}\nfile are bound to the same data source. Binding different entities to\ndifferent data sources requires defining the entities in separate\nService Builder modules and configuring each of the modules to use a\ndifferent data source.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} If your Service Builder services require nested\ntransactions, using an external data source may not be appropriate.\nTransactions between separate data sources cannot be fully nested.\nRollbacks may not propagate between services that use an external data\nsource and Liferay DXP services (or another app's services) that use a\ndifferent data source.\n\n\\noindent\\hrulefill\n\nSince \\texttt{DataSourceProvider} is the easiest, most versatile\napproach, it's explained first.\n\n\\chapter{Connecting the Data Source Using a\nDataSourceProvider}\\label{connecting-the-data-source-using-a-datasourceprovider}\n\nConnecting to an external database by creating and registering a\n\\texttt{DataSourceProvider} as a JDK\n\\href{https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html}{\\texttt{ServiceProviderInterface}}\n(SPI) is the easiest way. This approach works regardless of whether your\nService Builder module uses the \\texttt{ds} or \\texttt{spring}\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information\\#dependency-injector}{dependency\ninjection option} and it requires the fewest files and steps.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} All entities defined in a Service Builder module's\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file}{\\texttt{service.xml}}\nfile are bound to the same data source. Binding different entities to\ndifferent data sources requires defining the entities in separate\nService Builder modules and configuring each of the modules to use a\ndifferent data source.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} If your Service Builder services require nested\ntransactions, using an external data source may not be appropriate for\nyou. Transactions between separate data sources cannot be fully nested.\nRollbacks may not propagate between a module that uses an external data\nsource and Liferay DXP services (or another app's services) that use a\ndifferent data source.\n\n\\noindent\\hrulefill\n\nHere are the steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your \\texttt{service.xml} file, specify the same arbitrary data\n  source name for all of the entities, a unique table name for each\n  entity, and a database column name for each column. Here's an example:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\"\n    \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"spring\" package-path=\"com.liferay.example\" >\n    <namespace>TestDB</namespace>\n    <entity local-service=\"true\" name=\"Foo\" table=\"testdata\" data-source=\"extDataSource\"\n            remote-service=\"false\" uuid=\"false\">\n           <column name=\"id\" db-name=\"id\" primary=\"true\" type=\"long\" />\n           <column name=\"foo\" db-name=\"foo\" type=\"String\" />\n           <column name=\"bar\" db-name=\"bar\" type=\"long\" />\n    </entity>\n</service-builder>\n\\end{verbatim}\n\n  Note the example's \\texttt{\\textless{}entity\\textgreater{}} tag\n  attributes:\n\n  \\emph{\\texttt{data-source}}: The \\texttt{liferayDataSource} alias\n  \\texttt{ext-spring.xml} specifies.\n\n  \\emph{\\texttt{table}}: Your entity's database table.\n\n  Also note that your entity's \\texttt{\\textless{}column\\textgreater{}}s\n  must have a \\emph{\\texttt{db-name}} attribute set to the column name.\n\\item\n  \\href{https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay/configuring-a-database.html}{Manually\n  create the database} you defined in your \\texttt{service.xml}.\n\\item\n  Define the data source. One way is to use\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\n  properties} in a \\texttt{portal-ext.properties} file. Distinguish your\n  data source from Liferay's default data source by giving it a prefix\n  other than \\texttt{jdbc.default.}. This example uses prefix\n  \\texttt{jdbc.ext.}.\n\n\\begin{verbatim}\njdbc.ext.driverClassName=org.mariadb.jdbc.Driver\njdbc.ext.password=userpassword\njdbc.ext.url=jdbc:mariadb://localhost/external?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\njdbc.ext.username=yourusername\n\\end{verbatim}\n\\item\n  Restart your server if you defined your data source using portal\n  properties.\n\\item\n  Connect your Service Builder module to the data source by implementing\n  the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/jdbc/DataSourceProvider.html}{\\texttt{DataSourceProvider}}\n  interface. Since the \\texttt{DataSourceProvider} must be visible to\n  your \\texttt{*-service} module class loader, it's common to put the\n  \\texttt{DataSourceProvider} in the \\texttt{*-service} module.\n\n  This example uses\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/jdbc/DataSourceFactoryUtil.html}{\\texttt{DataSourceFactoryUtil}}\n  to create a data source from\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\n  properties} that have the prefix \\texttt{jdbc.ext.}.\n\n\\begin{verbatim}\npackage com.liferay.external.data.source.test.internal;\n\nimport com.liferay.portal.kernel.dao.jdbc.DataSourceFactoryUtil;\nimport com.liferay.portal.kernel.dao.jdbc.DataSourceProvider;\nimport com.liferay.portal.kernel.util.PropsUtil;\n\nimport javax.sql.DataSource;\n\npublic class DataSourceProviderImpl implements DataSourceProvider {\n\n    @Override\n    public DataSource getDataSource() {\n        try {\n            return DataSourceFactoryUtil.initDataSource(\n                PropsUtil.getProperties(\"jdbc.ext.\", true));\n        }\n        catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n}\n\\end{verbatim}\n\\item\n  Register the implementation as a JDK\n  \\href{https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html}{\\texttt{ServiceProviderInterface}}\n  (SPI) in a\n  \\texttt{/META-INF/services/com.liferay.portal.kernel.dao.jdbc.DataSourceProvider}\n  file in your \\texttt{*-service} module. For example, this file\n  registers the \\texttt{DataSourceProvider} implementation from the\n  previous step.\n\n\\begin{verbatim}\ncom.liferay.external.data.source.test.internal.DataSourceProviderImpl\n\\end{verbatim}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/running-service-builder}{Run\n  Service Builder}.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy}\n  your \\texttt{-service} module. If your \\texttt{DataSourceProvider} is\n  in a different project, deploy it too.\n\\end{enumerate}\n\nCongratulations! Your module's Service Builder services are persisting\ndata to your external data source.\n\n\\section{Related Topics}\\label{related-topics-15}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/connecting-to-data-sources-using-jndi}{Connecting\nto JNDI Data Sources}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{Business\nLogic with Service Builder}\n\n\\chapter{Connecting the Data Source Using Spring\nBeans}\\label{connecting-the-data-source-using-spring-beans}\n\nSometimes you want to use a database other than Liferay DXP's. To do\nthis, its data source must be defined in \\texttt{portal-ext.properties}\nor configured as a JNDI data source on the app server. Here you'll\nconnect\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} to a data source using Spring XML files. This approach only\nworks with Service Builder modules that use the \\texttt{spring}\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information\\#dependency-injector}{dependency\ninjection option}. Here are the steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Specify your database and a data source name in your\n  \\texttt{service.xml}.\n\\item\n  Create the database manually.\n\\item\n  Define the data source.\n\\item\n  Create a Spring bean that points to the data source.\n\\item\n  Run Service Builder.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} All entities defined in a Service Builder module's\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file}{\\texttt{service.xml}}\nfile are bound to the same data source. Binding different entities to\ndifferent data sources requires defining the entities in separate\nService Builder modules and configuring each of the modules to use a\ndifferent data source.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} If your Service Builder services require nested\ntransactions, using an external data source may not be appropriate for\nyou. Transactions between separate data sources cannot be fully nested.\nRollbacks may not propagate between a module that uses an external data\nsource and Liferay DXP services (or another app's services) that use a\ndifferent data source.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Connecting to an external data source using JNDI is\nbroken in Portal CE 7.2 GA1 and GA2, and in DXP 7.2 releases prior to\nFP5/SP2. See\n\\href{https://issues.liferay.com/browse/LPS-107733}{LPS-107733} for\ndetails.\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{Specify Your Database and a Data Source Name in\nYour\n\\texttt{service.xml}}{Specify Your Database and a Data Source Name in Your service.xml}}\\label{specify-your-database-and-a-data-source-name-in-your-service.xml}\n\nIn your \\texttt{service.xml} file, specify the same arbitrary data\nsource name for all of the entities, a unique table name for each\nentity, and a database column name for each column. Here's an example:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\"\n    \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"spring\" package-path=\"com.liferay.example\" >\n    <namespace>TestDB</namespace>\n    <entity local-service=\"true\" name=\"Foo\" table=\"testdata\" data-source=\"extDataSource\"\n            remote-service=\"false\" uuid=\"false\">\n           <column name=\"id\" db-name=\"id\" primary=\"true\" type=\"long\" />\n           <column name=\"foo\" db-name=\"foo\" type=\"String\" />\n           <column name=\"bar\" db-name=\"bar\" type=\"long\" />\n    </entity>\n</service-builder>\n\\end{verbatim}\n\nNote the example's \\texttt{\\textless{}entity\\textgreater{}} tag\nattributes:\n\n\\emph{\\texttt{data-source}}: The \\texttt{liferayDataSource} alias\n\\texttt{ext-spring.xml} specifies.\n\n\\emph{\\texttt{table}}: Your entity's database table.\n\nAlso note that your entity's \\texttt{\\textless{}column\\textgreater{}}s\nmust have a \\emph{\\texttt{db-name}} attribute set to the column name.\n\n\\section{Create the Database\nManually}\\label{create-the-database-manually}\n\n\\href{https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay/configuring-a-database.html}{Create\nthe database} per the database specification in your\n\\texttt{service.xml}.\n\nNext, use portal properties to set your data source.\n\n\\section{Define the Data Source}\\label{define-the-data-source}\n\nIf the application server defines the data source using JNDI, skip this\nstep. Otherwise, specify the data source in a\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}\nfile}. Distinguish it from Liferay's default data source by giving it a\nprefix other than \\texttt{jdbc.default.}. This example uses prefix\n\\texttt{jdbc.ext.}:\n\n\\begin{verbatim}\njdbc.ext.driverClassName=org.mariadb.jdbc.Driver\njdbc.ext.password=userpassword\njdbc.ext.url=jdbc:mariadb://localhost/external?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\njdbc.ext.username=yourusername\n\\end{verbatim}\n\nRestart your server if you defined your data source using portal\nproperties.\n\n\\section{Connect Your Service Builder Module to the Data Source Via a\nSpring\nBean}\\label{connect-your-service-builder-module-to-the-data-source-via-a-spring-bean}\n\nTo do this, create a parent context extension\n(e.g.,\\texttt{ext-spring.xml}) in your \\texttt{*-service} module's\n\\texttt{src/main/resources/META-INF/spring} folder or in your\ntraditional portlet's \\texttt{WEB-INF/src/META-INF} folder. Create this\nfolder if it doesn't exist already.\n\nDefine the following elements:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  A data source factory Spring bean for the data source. It's different\n  based on the type.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{JNDI}: Specify an arbitrary property prefix and prepend the\n    prefix to a JNDI name property key. Here's an example:\n  \\end{itemize}\n\n\\begin{verbatim}\n<bean class=\"com.liferay.portal.dao.jdbc.spring.DataSourceFactoryBean\"\n    id=\"liferayDataSourceFactory\">\n    <property name=\"propertyPrefix\" value=\"custom.\" />\n    <property name=\"properties\">\n        <props>\n            <prop key=\"custom.jndi.name\">jdbc/externalDataSource</prop>\n        </props>\n    </property>\n</bean>\n\\end{verbatim}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Portal Properties}: Specify a property prefix that matches\n    the prefix (e.g., \\texttt{jdbc.ext.}) you used in\n    \\texttt{portal-ext.properties}.\n  \\end{itemize}\n\n\\begin{verbatim}\n<bean class=\"com.liferay.portal.dao.jdbc.spring.DataSourceFactoryBean\"\n    id=\"liferayDataSourceFactory\">\n    <property name=\"propertyPrefix\" value=\"jdbc.ext.\" />\n</bean>\n\\end{verbatim}\n\\item\n  A Liferay data source bean that refers to the data source factory\n  Spring bean.\n\\item\n  An alias for the Liferay data source bean. Name the alias after the\n  data source name you specified in the \\texttt{service.xml}.\n\n  Here's an example \\texttt{ext-spring.xml} that points to a JNDI data\n  source:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<beans default-destroy-method=\"destroy\" default-init-method=\"afterPropertiesSet\"\n   xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n   xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\">\n\n   <!-- To define an external data source, the liferayDataSource Spring bean\n       must be overridden. Other default Spring beans like liferaySessionFactory\n       and liferayTransactionManager may optionally be overridden.\n\n       liferayDataSourceFactory refers to the data source configured on the\n       application server. -->\n   <bean class=\"com.liferay.portal.dao.jdbc.spring.DataSourceFactoryBean\"\n       id=\"liferayDataSourceFactory\">\n       <property name=\"propertyPrefix\" value=\"custom.\" />\n       <property name=\"properties\">\n           <props>\n               <prop key=\"custom.jndi.name\">jdbc/externalDataSource</prop>\n           </props>\n       </property>\n   </bean>\n\n   <!-- The data source bean refers to the factory to access the data source.\n   -->\n   <bean\n       class=\"org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy\"\n       id=\"liferayDataSource\">\n       <property name=\"targetDataSource\" ref=\"liferayDataSourceFactory\" />\n   </bean>\n\n   <!-- In service.xml, we associated our entity with the extDataSource. To\n       associate the extDataSource with our overridden liferayDataSource, we define\n       this alias. -->\n   <alias alias=\"extDataSource\" name=\"liferayDataSource\" />\n</beans>\n\\end{verbatim}\n\\end{enumerate}\n\nThe \\texttt{liferayDataSourceFactory} above refers to a JNDI data source\nnamed \\texttt{jdbc/externalDataSource}. If the data source is in a\n\\texttt{portal-ext.properties} file, the bean requires only a\n\\texttt{propertyPrefix} property that matches the data source property\nprefix.\n\nThe data source bean \\texttt{liferayDataSource} is overridden with one\nthat refers to the \\texttt{liferayDataSourceFactory} bean. The override\naffects this bundle (module or\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{Web\nApplication Bundle}) only.\n\nThe alias \\texttt{extDataSource} refers to the\n\\texttt{liferayDataSource} data source bean.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} The \\texttt{alias} element's \\texttt{alias}\nattribute value must match the data source name specified in the\n\\texttt{service.xml}. For example, the alias attribute value above is\n\\texttt{extDataSource}.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: To use an external data source in multiple Service\nBuilder bundles, you must override the \\texttt{liferayDataSource} bean\nin each bundle.\n\n\\noindent\\hrulefill\n\n\\section{Run Service Builder}\\label{run-service-builder}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/running-service-builder}{Run\nService Builder} and\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{deploy}\nyour \\texttt{-service} module. Now your Service Builder services use the\ndata source. You can\n\\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{use\nthe services in your business logic} as you always have regardless of\nthe underlying data source.\n\nCongratulations! You've connected Service Builder to your external data\nsource.\n\n\\section{Related Topics}\\label{related-topics-16}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-application-using-external-database-via-jndi}{Sample\nService Builder Application Using External Database via JNDI}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-application-using-external-database-via-jdbc}{Sample\nService Builder Application Using External Database via JDBC}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{Business\nLogic with Service Builder}\n\n\\chapter{Migrating a Service Builder Module from Spring DI to OSGi\nDS}\\label{migrating-a-service-builder-module-from-spring-di-to-osgi-ds}\n\nPrior to Liferay DXP 7.2, Service Builder modules could only use Spring\nfor dependency injection (DI). Now\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{OSGi\nDeclarative Services} (DS) is the default dependency injection mechanism\nfor new Service Builder modules. It's easier to learn and fosters loose\ncoupling between services. If you have an existing Service Builder\nmodule that uses Spring DI, you can modify it to use DS.\n\nHere are the conversion steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Prepare your project for DS\n\\item\n  Update your Spring bean classes\n\\item\n  Resolve any circular dependencies\n\\end{enumerate}\n\nNow prepare your project.\n\n\\section{Step 1: Prepare Your Project for\nDS}\\label{step-1-prepare-your-project-for-ds}\n\nPrepare your project's metadata, dependencies, and \\texttt{service.xml}\nfor DS.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Enable the DS annotation option for your inherited dependencies by\n  adding this line to your \\texttt{bnd.bnd} file:\n\n\\begin{verbatim}\n-dsannotations-options: inherit\n\\end{verbatim}\n\\item\n  Since DS Service Builder modules use the AOP API, add it as a compile\n  dependency in \\texttt{build.gradle}:\n\n\\begin{verbatim}\ncompileOnly group: \"com.liferay:com.liferay.portal.aop.api\", version: \"1.0.0\"\n\\end{verbatim}\n\\item\n  Add the \\texttt{dependency-injector=\"ds\"} attribute to your\n  \\texttt{service.xml} file's\n  \\texttt{\\textless{}service-builder\\textgreater{}} element:\n\n\\begin{verbatim}\n<service-builder dependency-injector=\"ds\" >\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Step 2: Update Your Spring Bean\nClasses}\\label{step-2-update-your-spring-bean-classes}\n\nSome of your\n\\href{/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder}{non-generated\nSpring bean classes} must be updated to use DS.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the\n  \\href{https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html}{\\texttt{@Component}}\n  annotation to your \\texttt{*LocalServiceImpl}, \\texttt{*ServiceImpl},\n  and \\texttt{*FinderImpl} classes.\n\\item\n  If the class implements a \\texttt{*Finder} interface, declare the\n  component as that service type. Example:\n\n\\begin{verbatim}\n@Component(service = MyFinder.class)\n\\end{verbatim}\n\\item\n  If the class implements a remote or local service, declare the\n  component as the \\texttt{com.liferay.portal.aop.AopService} service\n  type. Example:\n\n\\begin{verbatim}\n@Component(service = AopService.class)\n\\end{verbatim}\n\\item\n  If it's a remote service (i.e., \\texttt{-ServiceImpl} instead of\n  \\texttt{-LocalServiceImpl}), enable JSON web services by setting these\n  properties in your \\texttt{@Component} annotation:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{json.web.service.context.name}\n  \\item\n    \\texttt{json.web.service.context.path}\n  \\end{itemize}\n\n  Set them to the same values as the properties in your remote service\n  interface's\n  \\href{https://docs.liferay.com/ce/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/spring/osgi/OSGiBeanProperties.html}{\\texttt{@OSGiBeanProperties}}\n  annotation.\n\\item\n  If it's a local service, enable\n  \\href{https://docs.liferay.com/ce/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/service/PersistedModelLocalService.html}{\\texttt{PersistedModelLocalService}}\n  service tracking by setting the \\texttt{@Component} property\n  \\texttt{model.class.name} to the service entity's fully qualified\n  class name.\n\\item\n  Replace all the \\texttt{@ServiceReference} and \\texttt{@BeanReference}\n  field annotations with the DS\n  \\href{https://osgi.org/javadoc/r6/cmpn/org/osgi/service/component/annotations/Reference.html}{\\texttt{@Reference}}\n  annotation.\n\\item\n  Use the \\texttt{@Reference} field annotation to access any other\n  services you need.\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/running-service-builder}{Run\n  Service Builder} to regenerate the interfaces based on your\n  implementation changes.\n\\item\n  Replace the following methods:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{afterPropertiesSet()\\ \\{...\\}} →\n    \\texttt{activate()\\ \\{...\\}} and annotate with\n    \\href{https://osgi.org/javadoc/r6/cmpn/org/osgi/service/component/annotations/Activate.html}{\\texttt{@Activate}}.\n  \\item\n    \\texttt{destroy()\\ \\{...\\}} → \\texttt{deactivate()\\ \\{...\\}} and\n    annotate with\n    \\href{https://osgi.org/javadoc/r6/cmpn/org/osgi/service/component/annotations/Deactivate.html}{\\texttt{@Deactivate}}.\n  \\end{itemize}\n\\end{enumerate}\n\nNext, you'll work out any remaining references you need.\n\n\\section{Step 3: Resolve Any Circular\nDependencies}\\label{step-3-resolve-any-circular-dependencies}\n\nCircular dependencies occur in a module if two or more of its DS\nservices refer to each another (either directly or indirectly). A direct\nreference occurs, for example, when service \\texttt{A} references\nservice \\texttt{B}, and \\texttt{B} references \\texttt{A}. Here's what\nthe service components might look like:\n\n\\texttt{AImpl.java}:\n\n\\begin{verbatim}\n@Component(service = A.class)\npublic class AImpl implements A {\n    @Reference\n    private B _b;\n}\n\\end{verbatim}\n\n\\texttt{BImpl.java}:\n\n\\begin{verbatim}\n@Component(service = B.class)\npublic class BImpl implements B {\n    @Reference\n    private A _a;\n} \n\\end{verbatim}\n\n\\texttt{AImpl} and \\texttt{BImpl} directly depend on each other. This\ncircular dependency prevents each service component from resolving. DS\nservice activation requires that all of a service's dependencies\n(references) be satisfied.\n\n\\textbf{Note:} Service resolution is independent and separate from\nmodule (OSGi bundle) resolution:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Module resolution is determined by the module's manifest.\n\\item\n  Modules resolve before any of their services become active.\n\\item\n  Services inside a module cannot activate if the module cannot resolve.\n\\item\n  A module can resolve even if none of its services activate.\n\\end{itemize}\n\nThe example above demonstrates a very small circle, composed of only two\nclasses, but a circle can compose more classes. For example, \\texttt{A}\nreferences \\texttt{B}, \\texttt{B} references \\texttt{C}, \\texttt{C}\nreferences \\texttt{A}. Detecting and resolving such a dependency can be\ncomplicated.\n\nThere is no general, correct way to detect and resolve circular\ndependencies; cases vary. However, Liferay provides tools that\nfacilitate detecting circular dependencies and examining the DS service\ncomponents involved.\n\n\\begin{itemize}\n\\item\n  \\texttt{system:check}: This\n  \\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\n  shell} command provides several checks, including one that detects\n  inactive service components whose required references are unresolved.\n\\item\n  \\texttt{scr:info\\ {[}component{]}}: Execute this\n  \\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\n  shell} command on an unresolved component to report its unresolved\n  references.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Service resolution in DS dependency injection (DI) is\ndifferent than in services that use Liferay's Spring DI. In the latter\ncase, all Spring beans in the same module act as a single bundle of\nservices that activate together and can bind together before activation.\nDS doesn't have this feature. With DS, each component in a module is its\nown service and must resolve on its own.\n\n\\noindent\\hrulefill\n\nCongratulations on converting your service module to use Declarative\nServices.\n\n\\section{Related Topics}\\label{related-topics-17}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder}{Understanding\nthe Code Service Builder Generates}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{Declarative\nServices}\n\n\\chapter{Business Logic with Service\nBuilder}\\label{business-logic-with-service-builder}\n\nOnce you've\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file}{defined\nyour application's entities} and\n\\href{/docs/7-2/appdev/-/knowledge_base/a/running-service-builder}{run\nService Builder} to generate your service and persistence layers, you\ncan begin adding business logic. Each entity generated by Service\nBuilder contains a model implementation, local service implementation,\nand optionally a remote service implementation class. Your application's\nbusiness logic can be implemented in these classes. The generated\nservice layer contains default methods that call CRUD operations from\nthe persistence layer. Once you've added your business logic, running\nService Builder again regenerates your application's interfaces and\nmakes your new logic available for invocation.\n\nThe heart of your service is its \\texttt{*LocalServiceImpl} class. This\nclass is your entity's local service extension point. Local services are\ninvoked from your application or by other applications running on the\nsame instance as your application.\n\nCreating services takes these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Deciding to Create Local and Remote Services.\n\\item\n  Implementing the \\texttt{add} Method.\n\\item\n  Implementing the \\texttt{update} and \\texttt{delete} Methods.\n\\item\n  Implementing \\texttt{get} and \\texttt{get*Count} Methods\n\\item\n  Implementing Other Business Logic\n\\item\n  Integrating with Liferay's Services.\n\\end{enumerate}\n\nStart with deciding the service types you need.\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file}{Defining\nyour object model} involves choosing whether to generate local and or\nremote service interfaces. Local services can only be invoked from the\nLiferay server on which they're deployed. Remote services are accessible\nto clients outside of the Liferay server. Before implementing local or\nremote services, consider the best practices described here:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If you plan to have remote services, enable local services too.\n\\item\n  Implement your business logic in \\texttt{*LocalServiceImpl}.\n\\item\n  Create corresponding remote services methods in your\n  \\texttt{*ServiceImpl}.\n\\item\n  Use the remote service methods to call the local service, wrapping the\n  calls in permission checks.\n\\item\n  In your application, call only the remote services. This ensures that\n  your service methods are secured and that you don't have to duplicate\n  permissions code.\n\\end{enumerate}\n\nIf you are turning on local or remote services in your\n\\texttt{service.xml} file just now, make sure to\n\\href{/docs/7-2/appdev/-/knowledge_base/a/running-service-builder}{run\nService Builder} again to generate the service interfaces.\n\nNow you're ready to implement your business logic.\n\n\\chapter{Implementing an Add Method}\\label{implementing-an-add-method}\n\nYour \\texttt{*LocalServiceImpl} represents your service layer, where you\ncreate the business logic that operates on your application's data and\nthen calls the persistence layer to persist, retrieve, or delete your\ndata, using the object model defined in \\texttt{service.xml}.\n\nOne of the first methods you'll likely implement is one that creates\nentities. Liferay's convention is to implement this in an \\texttt{add*}\nmethod, where the part after \\texttt{add} is the entity name (or a\nshortened version of it). Here are the steps for implementing an\n\\texttt{add*} method:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Declare an \\texttt{add*} method with parameters for creating the\n  entity.\n\\item\n  Validate the parameters.\n\\item\n  Generate a primary key.\n\\item\n  Create an entity instance.\n\\item\n  Populate the entity attributes.\n\\item\n  Persist the entity.\n\\item\n  Return the entity instance.\n\\end{enumerate}\n\nThis article refers to the Guestbook application's\n\\texttt{addGuestbookEntry} method from\n\\texttt{GuestbookEntryLocalServiceImpl}. To keep things simple, we have\nexcluded the code that integrates with Liferay services, such as assets,\nsocial bookmarks, and more.\n\nHere's the Guestbook application's \\texttt{addGuestbookEntry} method:\n\n\\begin{verbatim}\npublic GuestbookEntry addEntry(long userId, long guestbookId, String name, String email, String message,\n        ServiceContext serviceContext) throws PortalException {\n\n    long groupId = serviceContext.getScopeGroupId();\n\n    User user = userLocalService.getUserById(userId);\n\n    Date now = new Date();\n\n    validate(name, email, message);\n\n    long entryId = counterLocalService.increment();\n\n    GuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\n    entry.setUuid(serviceContext.getUuid());\n    entry.setUserId(userId);\n    entry.setGroupId(groupId);\n    entry.setCompanyId(user.getCompanyId());\n    entry.setUserName(user.getFullName());\n    entry.setCreateDate(serviceContext.getCreateDate(now));\n    entry.setModifiedDate(serviceContext.getModifiedDate(now));\n    entry.setExpandoBridgeAttributes(serviceContext);\n    entry.setGuestbookId(guestbookId);\n    entry.setName(name);\n    entry.setEmail(email);\n    entry.setMessage(message);\n\n    guestbookEntryPersistence.update(entry);\n\n    // Calls to other Liferay frameworks go here\n\n    return entry;\n}\n\\end{verbatim}\n\nThis method uses the parameters to create \\texttt{GuestbookEntry}. It\nvalidates the parameters, creates an entry with a generated entry ID\n(primary key), populates the entry, persists the entry, and returns it.\nYou can refer to this method as you create your own \\texttt{add*}\nmethod. Note that there's no real business logic here; it's a simple\napplication that takes data the user entered, validates it, and then\npersists it to the database.\n\n\\section{Step 1: Declare an add method with parameters for creating the\nentity}\\label{step-1-declare-an-add-method-with-parameters-for-creating-the-entity}\n\nCreate a public method for \\emph{adding} (creating) your application's\nentity. Make it a public method that returns the entity it creates.\n\n\\begin{verbatim}\npublic [ENTITY] add[ENTITY](...) {\n    \n} \n\\end{verbatim}\n\nFor example, here's the \\texttt{addEntry} method signature:\n\n\\begin{verbatim}\npublic GuestbookEntry addEntry(long userId, long guestbookId, \n    String name, String email, String message, \n    ServiceContext serviceContext) throws PortalException {\n    ...\n    }\n\\end{verbatim}\n\nThis method specifies all the parameters needed to create and populate a\n\\texttt{GuestbookEntry} as you specified them in your\n\\texttt{service.xml} file. It throws a \\texttt{PortalException} in case\nthe parameters are invalid or a processing exception occurs (more on\nthis in a later step).\n\nMake sure to account for primary keys of other related entities. For\nexample, the \\texttt{addEntry} method above includes a parameter\n\\texttt{long\\ guestbookId} to associate the new \\texttt{GuestbookEntry}\nto a \\texttt{Guestbook}.\n\n\\section{Step 2: Validate the\nparameters}\\label{step-2-validate-the-parameters}\n\nValidate the parameters as needed. You might need to make sure a\nparameter is not empty or \\texttt{null}, or that a parameter value is\nwithin a valid range. Throw a\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/exception/PortalException.html}{\\texttt{PortalException}}\nor an extension of \\texttt{PortalException} for any invalid parameters.\n\nFor example, the \\texttt{addEntry} method invokes the following\n\\texttt{validate} method to check if the URL parameter is \\texttt{null}.\n\n\\begin{verbatim}\nprotected void validate(String name, String email, String entry) throws PortalException {\n\n    if (Validator.isNull(name)) {\n        throw new GuestbookEntryNameException();\n    }\n\n    if (!Validator.isEmailAddress(email)) {\n        throw new GuestbookEntryEmailException();\n    }\n\n    if (Validator.isNull(entry)) {\n        throw new GuestbookEntryMessageException();\n    }\n}\n\\end{verbatim}\n\nNext, generate a primary key for the entity instance you're creating.\n\n\\section{Step 3: Generate a primary\nkey}\\label{step-3-generate-a-primary-key}\n\nEntities must each have a unique primary key. Liferay's\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/counter/kernel/service/CounterLocalService.html}{\\texttt{CounterLocalService}}\ngenerates them per entity. Every \\texttt{*BaseLocalServiceImpl} has a\n\\texttt{counterLocalService} field that references a\n\\texttt{CounterLocalService} object for the entity. Invoke the counter\nservice's \\texttt{increment} method to generate a primary key for your\nentity instance.\n\n\\begin{verbatim}\nlong id = counterLocalService.increment();\n\\end{verbatim}\n\nNow you have a unique ID for your entity instance. Always generate\nprimary keys in this way, as it ensures your code is compatible with all\nthe databases Liferay supports.\n\n\\section{Step 4: Create an entity\ninstance}\\label{step-4-create-an-entity-instance}\n\nThe \\texttt{*Peristence} instance associated with your entity has a\n\\texttt{create(long\\ id)} method that constructs an entity instance with\nthe given ID. Every \\texttt{*BaseLocalServiceImpl} has a\n\\texttt{*Persistence} field that references a \\texttt{*Persistence}\nobject for the entity. For example,\n\\texttt{GuestbookEntryLocalServiceImpl} as a child of\n\\texttt{GuestbookEntryLocalServiceBaseImpl} has a field\n\\texttt{guestbookEntryPersistence}, which is a reference to a\n\\texttt{GuestbookEntryPersistence} instance.\n\n\\begin{verbatim}\n@Reference\nprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\\end{verbatim}\n\n\\texttt{GuestbookEntryLocalServiceImpl}'s \\texttt{addEntry} method\ncreates a \\texttt{GuestbookEntry} instance using this call:\n\n\\begin{verbatim}\nGuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\\end{verbatim}\n\nTo create an instance of your entity, invoke the \\texttt{create} method\non the \\texttt{*Persistence} field associated with the entity, making\nsure to pass in the entity primary key you generated in the previous\nstep.\n\n\\begin{verbatim}\n [ENTITY_NAME] entity = [ENTITY_NAME]Persistence.create(id);\n\\end{verbatim}\n\nIt's time to populate the new entity instance.\n\n\\section{Step 5: Populate the entity\nattributes}\\label{step-5-populate-the-entity-attributes}\n\nUse the \\texttt{add*} method parameter values and the entity's setter\nmethods to populate your entity's attributes. For example, here are the\n\\texttt{GuestbookEntry} attribute assignments:\n\n\\begin{verbatim}\nentry.setUuid(serviceContext.getUuid());\nentry.setUserId(userId);\nentry.setGroupId(groupId);\nentry.setCompanyId(user.getCompanyId());\nentry.setUserName(user.getFullName());\nentry.setCreateDate(serviceContext.getCreateDate(now));\nentry.setModifiedDate(serviceContext.getModifiedDate(now));\nentry.setExpandoBridgeAttributes(serviceContext);\nentry.setGuestbookId(guestbookId);\nentry.setName(name);\nentry.setEmail(email);\nentry.setMessage(message);\n\\end{verbatim}\n\nNote that the \\texttt{ServiceContext} is commonly used to carry an\nentity's UUID and the \\texttt{User} is associated to a company.\n\n\\section{Step 6: Persist the entity}\\label{step-6-persist-the-entity}\n\nIt's time to store the entity. Invoke the \\texttt{*Persistence} field's\n\\texttt{update} method, passing in the entity object. For example,\nhere's how the new \\texttt{GuestbookEntry} is persisted:\n\n\\begin{verbatim}\nguestbookEntryPersistence.update(entry);\n\\end{verbatim}\n\nYour entity is persisted for the application.\n\n\\section{Step 7: Return the entity}\\label{step-7-return-the-entity}\n\nFinally, return the entity you just created so the caller can use it.\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/running-service-builder}{Run\nService Builder} to propagate your new service method to the\n\\texttt{*LocalService} interface.\n\nYou've implemented your local service's \\texttt{add*} method to create\nand persist your application's entities.\n\n\\chapter{Implementing Update and Delete\nMethods}\\label{implementing-update-and-delete-methods}\n\nAfter you've implementing an\n\\href{/docs/7-2/appdev/-/knowledge_base/a/implementing-an-add-method}{\\texttt{add*}\nmethod} for creating service entities, you'll want to create\n\\hyperref[implementing-an-update-method]{\\texttt{update*}} and\n\\hyperref[implementing-a-delete-method]{\\texttt{delete*}} methods for\nupdating and deleting them. The main difference between these and the\n\\texttt{add*} method is they must know which entity they're updating or\ndeleting.\n\n\\section{Implementing an Update\nMethod}\\label{implementing-an-update-method}\n\nAn \\texttt{update*} method for a local service resembles an\n\\href{/docs/7-2/appdev/-/knowledge_base/a/implementing-an-add-method}{\\texttt{add*}\nmethod} most because it has parameters for setting entity attribute\nvalues. Create an \\texttt{update*} method this way:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Declare an \\texttt{update*} method with parameters for updating the\n  entity.\n\\item\n  Validate the parameters.\n\\item\n  Retrieve the entity instance, if necessary.\n\\item\n  Update the entity attributes.\n\\item\n  Persist the updated entity.\n\\item\n  Run Service Builder.\n\\end{enumerate}\n\nThe following code snippets from\n\\texttt{GuestbookEntryLocalServiceImpl}'s \\texttt{updateEntry} method\nare helpful to examine.\n\n\\begin{verbatim}\npublic GuestbookEntry updateEntry(long userId, long guestbookId, long entryId, String name, String email, String message,\n        ServiceContext serviceContext) throws PortalException, SystemException {\n\n    Date now = new Date();\n\n    validate(name, email, message);\n\n    GuestbookEntry entry = getGuestbookEntry(entryId);\n\n    User user = userLocalService.getUserById(userId);\n\n    entry.setUserId(userId);\n    entry.setUserName(user.getFullName());\n    entry.setModifiedDate(serviceContext.getModifiedDate(now));\n    entry.setName(name);\n    entry.setEmail(email);\n    entry.setMessage(message);\n    entry.setExpandoBridgeAttributes(serviceContext);\n\n    guestbookEntryPersistence.update(entry);\n\n    // Integrate with Liferay frameworks here.\n\n    return entry;\n}\n\\end{verbatim}\n\nThis method has all the makings of a good \\texttt{update*} method:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  parameter for looking up the entity instance\n\\item\n  parameters for updating the entity attributes\n\\item\n  parameter validation\n\\item\n  entity attribute updates\n\\item\n  entity persistence\n\\item\n  returns the entity instance\n\\end{itemize}\n\nRefer to the example method above as you follow the steps to create your\nown \\texttt{update*} method.\n\n\\section{Step 1: Declare an Update Method with Parameters for Updating\nthe\nEntity}\\label{step-1-declare-an-update-method-with-parameters-for-updating-the-entity}\n\nCreate a public method for updating your application's entity.\n\n\\begin{verbatim}\npublic [ENTITY] update[ENTITY](...)\n    throws PortalException {\n    \n} \n\\end{verbatim}\n\nReplace \\texttt{{[}ENTITY{]}} with your entity's name or nickname.\nCreate a parameter list that satisfies the entity attributes you're\nupdating. Include an entity instance parameter or an ID parameter for\nfetching the entity instance.\n\nFor example, the \\texttt{GuestbookEntryLocalServiceImpl}'s\n\\texttt{updateEntry} method signature has an ID parameter\n(\\texttt{entryId}) for fetching the \\texttt{GuestbookEntry} entity\ninstance. Also it has parameters \\texttt{folderId}, \\texttt{name},\n\\texttt{url}, and \\texttt{description} for updating the\n\\texttt{GuestbookEntry}'s respective attributes.\n\n\\begin{verbatim}\npublic GuestbookEntry updateEntry(long userId, long guestbookId, long entryId, String name, String email, String message,\n        ServiceContext serviceContext) throws PortalException, SystemException {\n\\end{verbatim}\n\nNote, user ID, group ID, and service context parameters are useful for\nintegrating with Liferay's services. More on that later.\n\n\\section{Step 2: Validate the\nParameters}\\label{step-2-validate-the-parameters-1}\n\nSimilar to validating the\n\\href{/docs/7-2/appdev/-/knowledge_base/a/implementing-an-add-method}{\\texttt{add*}\nmethod} parameters, validate your \\texttt{update*} parameters. Your\n\\texttt{add*} and \\texttt{update*} methods might be able to use the same\nvalidation code. Throw a\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/exception/PortalException.html}{\\texttt{PortalException}}\nor an extension of \\texttt{PortalException} for any invalid parameters.\n\n\\section{Step 3: Retrieve the Entity\nInstance}\\label{step-3-retrieve-the-entity-instance}\n\nIf you're passing in an entity instance, you can update it directly.\nOtherwise, pass in the entity ID (the primary key). The\n\\texttt{*Persistence} class Service Builder injects into\n\\texttt{*BaseLocalServiceImpl} classes has a\n\\texttt{findByPrimaryKey(long)} method that retrieves instances by ID.\nFor example, the \\texttt{GuestbookEntryLocalServiceImpl} retrieves the\n\\texttt{GuestbookEntry} with the primary key \\texttt{entryId}.\n\n\\begin{verbatim}\nGuestbookEntry entry = guestbookEntryPersistence.findByPrimaryKey(\n    entryId);\n\\end{verbatim}\n\nInvoke the \\texttt{findByPrimaryKey(long\\ id)} method of your\n\\texttt{*Persistence} class to retrieve the entity instance that matches\nyour primary key parameter.\n\n\\begin{verbatim}\n [ENTITY] entity = [ENTITY]Persistence.findByPrimaryKey(id);\n\\end{verbatim}\n\nIt's time to update the entity attributes.\n\n\\section{Step 4: Update the Entity\nAttributes}\\label{step-4-update-the-entity-attributes}\n\nInvoke the entity's setter methods to replace its attribute values.\n\n\\section{Step 5: Persist and Return the Updated Entity\nInstance}\\label{step-5-persist-and-return-the-updated-entity-instance}\n\nPersist the updated entity to the database and return the instance to\nthe caller.\n\n\\begin{verbatim}\n[ENTITY]Persistence.update(entity);\n\n...\n\nreturn entity;\n\\end{verbatim}\n\n\\section{Step 6: Run Service Builder}\\label{step-6-run-service-builder}\n\nFinally, run Service Builder to propagate your new service method to the\n\\texttt{*LocalService} interface.\n\nYou've created a service method to update your entity. If you thought\nthat was easy, implementing a \\texttt{delete*} method is even easier.\n\n\\section{Implementing a Delete\nMethod}\\label{implementing-a-delete-method}\n\nThe \\texttt{remove} method of an entity's \\texttt{*Persistence} class\ndeletes an entity instance from the database. Use it in your local\nservice's \\texttt{delete*} method. Here's what a \\texttt{delete*} method\nlooks like:\n\n\\begin{verbatim}\npublic [ENTITY] delete[ENTITY](ENTITY entity) throws PortalException\n{\n    [ENTITY]Persistence.remove(entity);\n\n    // Clean up related to additional Liferay services goes here ... \n\n    return entity;\n}\n\\end{verbatim}\n\nMake sure to replace \\texttt{{[}ENTITY{]}} with your entity's name or\nnickname.\n\nFor example, here's paraphrased code from\n\\texttt{GuestbookEntryLocalServiceImpl}'s \\texttt{deleteEntry} method:\n\n\\begin{verbatim}\npublic GuestbookEntry deleteEntry(GuestbookEntry entry)\n    throws PortalException {\n\n    guestbookEntryPersistence.remove(entry);\n\n    // Clean up related to additional Liferay services goes here ...  \n\n    return entry;\n}\n\\end{verbatim}\n\nAfter implementing your \\texttt{delete*} method, run Service Builder to\npropagate your new service method to the \\texttt{*LocalService}\ninterface.\n\n\\section{Related Topics}\\label{related-topics-18}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/implementing-an-add-method}{Implementing\nan add method}\n\n\\chapter{Implementing Methods to Get and Count\nEntities}\\label{implementing-methods-to-get-and-count-entities}\n\nService Builder generates \\texttt{findBy*} methods and \\texttt{countBy*}\nmethods in your\n\\href{/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder}{\\texttt{*Persistence}\nclasses} based on your \\texttt{service.xml} file's\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-service-entity-finder-methods}{finders}.\nYou can leverage finder methods in your local services to get and count\nentities.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[getter-methods]{Getters}: \\texttt{get*} methods return\n  entity instances matching criteria.\n\\item\n  \\hyperref[counter-methods]{Counters}: \\texttt{get*Count} methods\n  return the number of instances matching criteria\n\\end{itemize}\n\nStart with getting entities that match criteria.\n\n\\section{Getter Methods}\\label{getter-methods}\n\nThe \\texttt{findByPrimaryKey} methods and \\texttt{findBy*} methods\nsearch for and return entity instances based on criteria. Your local\nservice implementation must only wrap calls to the finder methods that\nget what you want.\n\nHere's how to create a method that gets an entity based on an ID\n(primary key):\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a method using this format:\n\n\\begin{verbatim}\npublic [ENTITY] get[ENTITY_NAME](long id) {\n    return [ENTITY]Persistence.findByPrimaryKey(id);\n}\n\\end{verbatim}\n\\item\n  Replace \\texttt{{[}ENTITY{]}} and \\texttt{{[}ENTITY\\_NAME{]}} with the\n  respective entity type and entity name (or nickname).\n\\item\n  Run Service Builder to propagate the method to your local service\n  interface.\n\\end{enumerate}\n\nHere's how to get entities based on criteria:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Identify the criteria for finding the entity instance(s).\n\\item\n  If there is no\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/defining-service-entity-finder-methods}{\\texttt{finder}\n  element} for the criteria, create one for it and run Service Builder.\n\\item\n  Determine the\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder}{\\texttt{*Persistence}\n  class} \\texttt{findBy*} method you want to call. Depending on your\n  \\texttt{finder} element columns, Service Builder might overload the\n  method to include these parameters:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{int\\ start} and \\texttt{int\\ end} parameters for specifying\n    a range of entities.\n  \\item\n    \\texttt{com.liferay.portal.kernel.util.OrderByComparator\\ orderByComparator}\n    parameter for arranging the matching entities.\n  \\end{itemize}\n\\item\n  Specify your \\texttt{get*} method signature, making sure to account\n  for the \\texttt{*Persistence} class \\texttt{findBy*} method parameters\n  you must satisfy. Use this method format:\n\n\\begin{verbatim}\npublic List<[ENTITY]> get[DESCRIBE_THE_ENTITIES](...) {\n\n}\n\\end{verbatim}\n\n  Replace \\texttt{{[}ENTITY{]}} with the entity type. Replace\n  \\texttt{{[}DESCRIBE\\_THE\\_ENTITIES{]}} with a descriptive name for the\n  entities you're getting.\n\\item\n  Call the \\texttt{*Persistence} class \\texttt{findBy*} method and\n  return the list of matching entities.\n\\item\n  Run Service Builder.\n\\end{enumerate}\n\nFor example, \\texttt{getGuestbookEntries} from\n\\texttt{GuestbookEntryLocalServiceImpl} returns a range of\n\\texttt{GuestbookEntry}s associated with a \\texttt{Group} primary key:\n\n\\begin{verbatim}\npublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId) {\n    return guestbookEntryPersistence.findByG_G(groupId, guestbookId);\n}\n\\end{verbatim}\n\nNow you know how to leverage finder methods to get entities. Methods\nthat count entities are next.\n\n\\section{Counter Methods}\\label{counter-methods}\n\nCounting entities is just as easy as getting them. Your\n\\texttt{*Persistence} class \\texttt{countBy*} methods do all the work.\nService Builder generates \\texttt{countBy*} methods based on each finder\nand its columns.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Identify the criteria for entity instances you're counting and\n  determine the\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder}{\\texttt{*Persistence}\n  class} \\texttt{countBy*} method that satisfies the criteria.\n\\item\n  Create a \\texttt{get*Count} method signature following this format:\n\n\\begin{verbatim}\npublic int get[DESCRIBE_THE_ENTITIES]Count(...) {\n\n}\n\\end{verbatim}\n\n  Replace \\texttt{{[}DESCRIBE\\_THE\\_ENTITIES{]}} with a descriptive name\n  for the entities you're counting.\n\\item\n  Call the \\texttt{*Persistence} class' \\texttt{countBy} method and\n  return the value. For example, the method \\texttt{getEntriesCount}\n  from \\texttt{GuestbookEntryLocalServiceImpl} returns the number of\n  \\texttt{GuestbookEntry}s that are associated with a group (matching\n  \\texttt{groupId}) and a guestbook (matching \\texttt{guestbookId}).\n\\end{enumerate}\n\n\\begin{verbatim}\npublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n    return guestbookEntryPersistence.countByG_G(groupId, guestbookId);\n}\n\\end{verbatim}\n\nNow your local service can get entities matching your criteria and\nreturn quick entity counts.\n\n\\section{Service Method Prefixes and Transactional\nAspects}\\label{service-method-prefixes-and-transactional-aspects}\n\nService Builder applies transactions to services by adding\n\\texttt{@Transactional} annotations to the \\texttt{*LocalService} and\n\\texttt{*Service} interfaces and their methods. By default, Service\nBuilder applies read-only transactions (e.g.,\n\\texttt{@Transactional\\ (readOnly\\ =\\ true\\ ...)}) to service methods\nprefixed with any of these words:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{dynamicQuery}\n\\item\n  \\texttt{fetch}\n\\item\n  \\texttt{get}\n\\item\n  \\texttt{has}\n\\item\n  \\texttt{is}\n\\item\n  \\texttt{load}\n\\item\n  \\texttt{reindex}\n\\item\n  \\texttt{search}\n\\end{itemize}\n\nSince these methods operate in read-only transactions, Liferay DXP\noptimizes their performance. Transactional service methods that don't\nhave the read-only setting operate in regular transactions.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} A method implementation can override its interface's\n\\texttt{@Transactional} annotation attributes. For example, applying\n\\texttt{@Transactional\\ (readOnly\\ =\\ false\\ ...)} to a method\nimplementation makes it operate in a transaction that is not read only.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} In methods that operate in read-only transactions,\ninvoking a service method that persists data (adds, updates, or deletes\ndata) must be done via the service object. Using the service object\nensures that the defined transactional behavior is applied.\n\n\\begin{verbatim}\nsomeService.addSomething();\n\\end{verbatim}\n\nFor example, this \\texttt{*LocalServiceImpl}'s getter method adds\n(\\emph{persists}) a \\texttt{ClassName} object if no object with that\nvalue exists.\n\n\\begin{verbatim}\npublic ClassName getClassName(String value) {\n    if (Validator.isNull(value)) {\n        return _nullClassName;\n    }\n\n    ClassName className = _classNames.get(value);\n\n    if (className == null) {\n        try {\n            className = classNameLocalService.addClassName(value);\n            ...\n        }\n        ...\n    }\n    ...\n}\n\\end{verbatim}\n\nUsing the service object \\texttt{classNameLocalService} to invoke its\n\\texttt{addClassName} method applies the service method's transaction\n(the regular transaction specified for the method in the\n\\texttt{*Service} interface). If the \\texttt{addClassName} method was\ninvoked WITHOUT using the service object, the \\texttt{ClassName} object\nwould not persist because the method's regular transaction would not be\napplied.\n\n\\section{Related Topics}\\label{related-topics-19}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{Creating\nLocal Services}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/implementing-an-add-method}{Implementing\nan Add Method}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-service-entity-finder-methods}{Defining\nService Entity Finder Methods}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder}{Understanding\nthe Code Generated by Service Builder}\n\n\\chapter{Implementing Any Other Business\nLogic}\\label{implementing-any-other-business-logic}\n\nThis section's earlier local service articles focus on CRUD methods:\nmethods that \\textbf{c}reate (add), \\textbf{r}ead (get),\n\\textbf{u}pdate, and \\textbf{d}elete entities. But you might also need\nmethods that provide business logic.\n\nSince the Guestbook application doesn't have any business logic, the\nBookmarks application, which is extremely similar, (Bookmark Folders and\nBookmark entries instead of Guestbooks and Guestbook entries) is used\nhere to illustrate simple business logic. Bookmarks application users\n\\emph{open} bookmarks (navigate to a URLs) by clicking on them.\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/bookmarks/bookmarks-service/src/main/java/com/liferay/bookmarks/service/impl/BookmarksEntryLocalServiceImpl.java}{\\texttt{BookmarksEntryLocalServiceImpl}}'s\n\\texttt{openEntry} method supports this functionality:\n\n\\begin{verbatim}\npublic BookmarksEntry openEntry(long userId, BookmarksEntry entry) {\n    entry.setVisits(entry.getVisits() + 1);\n\n    bookmarksEntryPersistence.update(entry);\n\n    assetEntryLocalService.incrementViewCounter(\n        userId, BookmarksEntry.class.getName(), entry.getEntryId(), 1);\n\n    return entry;\n}\n\\end{verbatim}\n\nThe \\texttt{openEntry} method tracks and persists the number of visits\nto the \\texttt{BookmarksEntry}'s URL, increments the number of views for\nthe \\texttt{BookmarksEntry} as an asset, and returns the\n\\texttt{BookmarksEntry}. This method implements required business logic\nthat compliments the CRUD methods.\n\n\\emph{Convenience methods} might also be appropriate for your app.\nThey're easier to use because they typically have these characteristics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Shorter parameter list\n\\item\n  Intuitive name\n\\end{itemize}\n\nShort parameter lists are easier to satisfy, and methods that have\nintuitive names are easier to find in Javadoc.\n\nFor example, the Bookmarks application lets users move bookmarks to\ndifferent folders. Moving a bookmark can be done using the service's\n\\texttt{updateEntry(...)} method, but its long parameter list is\noverkill since all the operation requires is the bookmarks entry and the\nfolder where it's going. Compare the following \\texttt{update*} method\ncall to a convenience method call.\n\n\\textbf{Update method}:\n\n\\begin{verbatim}\nbookmarksEntryLocalService.updateEntry(userId, entryId, groupId, folderId, name, url, description, serviceContext);\n\\end{verbatim}\n\n\\textbf{Convenience method}:\n\n\\begin{verbatim}\nbookmarksEntryLocalService.moveEntry(entryId, folderId);\n\\end{verbatim}\n\nHere's the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/bookmarks/bookmarks-service/src/main/java/com/liferay/bookmarks/service/impl/BookmarksEntryLocalServiceImpl.java}{\\texttt{moveEntry}\nmethod}:\n\n\\begin{verbatim}\npublic BookmarksEntry moveEntry(long entryId, long parentFolderId)\n    throws PortalException {\n\n    BookmarksEntry entry = getBookmarksEntry(entryId);\n\n    entry.setFolderId(parentFolderId);\n    entry.setTreePath(entry.buildTreePath());\n\n    bookmarksEntryPersistence.update(entry);\n\n    return entry;\n}\n\\end{verbatim}\n\nThe \\texttt{moveEntry} method retrieves the \\texttt{BookmarksEntry}\nentity by its ID, assigns it a new parent folder, updates its tree path,\npersists all the entity's changes, and returns the entity. Convenience\nmethods like this one facilitate updating a subset of the entity's\nattributes.\n\nAfter implementing your custom business methods,\n\\href{/docs/7-2/appdev/-/knowledge_base/a/running-service-builder}{run\nService Builder} to propagate them to the interface.\n\nIn your local services, you can implement business logic methods that\nsuit your application.\n\n\\textbf{Related Topics}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{Creating\nLocal Services}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services}{Invoking\nLocal Services}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/invoking-services-from-spring-service-builder-code}{Invoking\nServices from Spring Service Builder Code}\n\n\\chapter{Integrating with Liferay's\nFrameworks}\\label{integrating-with-liferays-frameworks}\n\nNew car buyers expect certain standard features: power windows, cruise\ncontrol, floor mats (at least the cheap ones), and so on. Similarly,\nusers expect applications to have certain features, and those features\nshould behave consistently across applications.\n\nFor example, a user might expect the app's content can be shared on\nsocial networks, tagged and rated, and discussed in comments. Liferay's\nframeworks implement these features users expect to see. Integrating\nwith the frameworks is easy, and the frameworks provide intuitive,\nconsistent user experiences.\n\nHere are some of Liferay's most popular frameworks:\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions}{Permissions}:\nDefines resources and permissions for entities and actions that can be\nperformed on them.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/configurable-applications}{Configurable\nApplications}: Provide configuration screens in the Control Panel.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework}{Workflow}:\nEquips entities for reviewing in workflows before publishing.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/item-selector}{Item\nSelector}: Provides a consistent experience for browsing and selecting\nentities.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{Asset\nFramework}: Provides a way to make entity data generic, so common tasks\ncan be performed on them. This enables users to tag, categorize, rate,\nprioritize, and comment on anything that has been asset-enabled. Users\ncan relate entities to each other as assets, and entities can be\npublished in the Asset Publisher.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/implementing-asset-categorization-and-tagging}{Tags\n  and Categories}: Enables users to tag entities and categorize them\n  into logical hierarchies.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/implementing-asset-priority}{Priority}:\n  Users can ascribe numerical priorities to entities.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/relating-assets}{Related\n  Assets}: Users can associate one entity with another as an asset.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{Asset\n  Renderer}: Enables entities appearing in Asset Publisher queries.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-comments-to-your-app}{Comments}:\n  Lets users comment on entities.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/rating-assets}{Ratings}:\n  Enables rating systems, such as five stars or thumbs up/down, on\n  entities.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/flagging-inappropriate-asset-content}{Flags}:\n  Users can flag entity content as inappropriate.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/applying-social-bookmarks}{Social\n  Bookmarks}: Users can share entity content on Twitter, Facebook, and\n  more.\n\\end{itemize}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/export-import}{Export/Import}:\nExport entity data to and import entity data from files (\\texttt{.lpkg}\nfiles). Exported data can be imported to another portal instance or\nsaved for later use.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/staging}{Staging}: Modify\ncontent behind the scenes without affecting the live site, and then\npublish to the live site when the content is ready.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/search}{Search}: Index\nentities for searching.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-entities-to-the-recycle-bin}{Recycle\nBin}: Instead of deleting entities, put them into the Recycle Bin.\nEntities can be restored from the Recycle Bin or deleted permanently\n(manually or per a schedule).\n\n\\section{Related Topics}\\label{related-topics-20}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys}{Internationalization}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/javascript-module-loaders}{JavaScript\nModule Loaders}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs}{Java\nTaglibs}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes}{Upgrade\nProcesses}\n\n\\chapter{Invoking Local Services}\\label{invoking-local-services}\n\nOnce you deploy your services module, those services are available in\nthe container. Service Builder generates local and remote service\nclasses as OSGi Declarative Services (DS) components. These components\nare accessible to other DS components, so you can invoke them from other\ncomponents, such as your web application. Here's how:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a reference to the local service component.\n\\item\n  Call the component's methods.\n\\end{enumerate}\n\nThere's a Blade sample called\n\\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-samples}{Basic\nService Builder}. Its \\texttt{basic-web} module has a \\texttt{Portlet}\nservice component that demonstrates referencing a local service\ncomponent. This module also has JSPs that invoke the component's\nmethods. Your first step is to add a reference to the local service\ncomponent object.\n\n\\section{Step 1: Reference the Local Service\nComponent}\\label{step-1-reference-the-local-service-component}\n\nYour application's Service Builder-generated local services are DS\ncomponents that you can inject into your application's other DS\ncomponents (classes annotated with \\texttt{@Component})\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{using\nthe \\texttt{@Reference} annotation}. The \\texttt{basic-web} module's\n\\texttt{JSPPortlet} class is a \\texttt{Portlet} service component that\nreferences the \\texttt{FooLocalService} local service as a DS component.\n\n\\begin{verbatim}\n@Reference\nprivate volatile FooLocalService _fooLocalService;\n\\end{verbatim}\n\nThe OSGi service registry wires the service implementation object to\nyour class that references it. The \\texttt{JSPPortlet} sample class\ndeclares the \\texttt{\\_fooLocalService} field to be volatile, but making\na field volatile is completely optional.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: If you chose Spring as the dependency injector, Service\nBuilder generates \\texttt{*LocalServiceImpl}, \\texttt{*ServiceImpl},\n\\texttt{*PersistenceImpl}, and \\texttt{{[}ENTITY\\_NAME{]}Impl} classes\nfor your entities as Service Builder Spring Beans---not OSGi Declarative\nServices.\n\\href{/docs/7-2/appdev/-/knowledge_base/a/invoking-services-from-spring-service-builder-code}{Service\nBuilder Spring Beans must use means other than the \\texttt{@Reference}\nannotation to reference Liferay services and OSGi services}.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} You should never invoke \\texttt{*LocalServiceImpl}\nobjects directly. You should only invoke them indirectly through their\n\\texttt{*LocalService} service interface. The OSGi service registry\nwires the service implementation object to your class.\n\nYou can make a service object available to JSPs by associating it with a\n\\texttt{RenderRequest} attribute. For example, the \\texttt{JSPPortlet}'s\n\\texttt{render} method associates the \\texttt{FooLocalService} object\nwith an attribute called \\texttt{fooLocalService}.\n\n\\begin{verbatim}\n@Override\npublic void render(RenderRequest request, RenderResponse response)\n    throws IOException, PortletException {\n\n    //set service bean\n    request.setAttribute(\"fooLocalService\", getFooLocalService());\n\n    super.render(request, response);\n}\n\npublic FooLocalService getFooLocalService() {\n    return _fooLocalService;\n}\n\\end{verbatim}\n\nIf your JSP declares the\n\\texttt{\\textless{}portlet:defineObjects\\ /\\textgreater{}} tag, it can\nretrieve the service object from the \\texttt{RenderRequest} attribute.\nFor example, the \\texttt{JSPPortlet}'s \\texttt{init.jsp} file retrieves\nthe \\texttt{FooLocalService} object from the \\texttt{\"fooLocalService\"}\nattribute.\n\n\\begin{verbatim}\n...\n<%@\npage import=\"com.liferay.blade.samples.servicebuilder.service.FooLocalService\" %>\n...\n\n<liferay-theme:defineObjects />\n\n<portlet:defineObjects />\n\n<%\n...\n\n//get service bean\nFooLocalService fooLocalService = (FooLocalService)request.getAttribute(\"fooLocalService\");\n%>\n\\end{verbatim}\n\nAll JSPs that include the above \\texttt{init.jsp} can use the\n\\texttt{fooLocalService} variable to invoke the local service\ncomponent's methods.\n\n\\section{Step 2: Call the Component's\nMethods}\\label{step-2-call-the-components-methods}\n\nNow that you have the service component object, you can invoke its\nmethods as you would any Java object's methods.\n\nThe \\texttt{basic-web} sample module's \\texttt{view.jsp} and\n\\texttt{edit\\_foo.jsp} files include the \\texttt{init.jsp} shown in the\nprevious section. Therefore, they can access the\n\\texttt{fooLocalService} variable which references the service component\nobject. The \\texttt{view.jsp} file uses the component's\n\\texttt{getFoosCount} method and \\texttt{getFoos} method in a Liferay\nSearch Container that lists \\texttt{Foo} instances.\n\n\\begin{verbatim}\n<liferay-ui:search-container\n    total=\"<%= fooLocalService.getFoosCount() %>\"\n>\n    <liferay-ui:search-container-results\n        results=\"<%= fooLocalService.getFoos(searchContainer.getStart(), searchContainer.getEnd()) %>\"\n    />\n    ...\n</liferay-ui:search-container>\n\\end{verbatim}\n\nThe \\texttt{edit\\_foo.jsp} file calls \\texttt{getFoo(long\\ id)} to\nretrieve a \\texttt{Foo} entity based on the entity instance's ID.\n\n\\begin{verbatim}\nlong fooId = ParamUtil.getLong(request, \"fooId\");\nFoo foo = null;\nif (fooId > 0) {\n    foo = fooLocalService.getFoo(fooId);\n}\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} When invoking service entity updates (e.g.,\n\\texttt{fooService.update(object)}) for services that have MVCC enabled,\nmake sure to do so in transactions. Propagate rejected transactions to\nthe UI for the user to handle. For details, see\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information\\#multiversion-concurrency-control-mvcc}{Multiversion\nconcurrency control (MVCC)}.\n\n\\noindent\\hrulefill\n\nUsing the \\texttt{@Reference} annotation, you can inject your\napplication's OSGi DS components (such as a portlet DS component) with\ninstances of your application's Service Builder-generated local service\ncomponents. Also you can provide your JSPs access to the component\ninstances via \\texttt{RenderRequest} attributes.\n\n\\section{Related Topics}\\label{related-topics-21}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{Creating\nLocal Services}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services}{Invoking\nLocal Services}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/invoking-services-from-spring-service-builder-code}{Invoking\nLocal Services from Spring Service Builder Code}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{OSGi\nServices and Dependency Injection with Declarative Services}\n\n\\chapter{Invoking Services from Spring Service Builder\nCode}\\label{invoking-services-from-spring-service-builder-code}\n\nWhen using Spring as the dependency injector, all the services created\nwithin a Service Builder application are wired using an internal Spring\nApplication Context. This uses AOP proxies to adapt the services for\ntransactions, indexing, and security. In a module's\n\\texttt{module-spring.xml} Spring Application Context file, Service\nBuilder defines each entity's \\texttt{*LocalServiceImpl},\n\\texttt{*ServiceImpl}, and \\texttt{*PersistenceImpl} classes as Spring\nBeans. For example, Service Builder defines Spring Beans for the\n\\texttt{Foo} entity in the\n\\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-samples}{Liferay\nBlade Service Builder \\texttt{basic-service} sample module's}\n\\texttt{src/main/resources/META-INF/spring/module-spring.xml} file:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<beans\n    default-destroy-method=\"destroy\"\n    default-init-method=\"afterPropertiesSet\"\n    xmlns=\"http://www.springframework.org/schema/beans\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\n>\n    <bean class=\"com.liferay.blade.samples.servicebuilder.service.impl.FooLocalServiceImpl\" id=\"com.liferay.blade.samples.servicebuilder.service.FooLocalService\" />\n    <bean class=\"com.liferay.blade.samples.servicebuilder.service.impl.FooServiceImpl\" id=\"com.liferay.blade.samples.servicebuilder.service.FooService\" />\n    <bean class=\"com.liferay.blade.samples.servicebuilder.service.persistence.impl.FooPersistenceImpl\" id=\"com.liferay.blade.samples.servicebuilder.service.persistence.FooPersistence\" parent=\"basePersistence\" />\n</beans>\n\\end{verbatim}\n\nHere's a summary of the beans the example context defines:\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.4118}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5882}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\n\\textbf{Interface ID}\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n\\textbf{Implementation Class}\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{com.liferay.blade.samples.servicebuilder.service.FooLocalService}\n&\n\\texttt{com.liferay.blade.samples.servicebuilder.service.impl.FooLocalServiceImpl} \\\\\n\\texttt{com.liferay.blade.samples.servicebuilder.service.FooService} &\n\\texttt{com.liferay.blade.samples.servicebuilder.service.impl.FooServiceImpl} \\\\\n\\texttt{com.liferay.blade.samples.servicebuilder.service.persistence.FooPersistence}\n&\n\\texttt{com.liferay.blade.samples.servicebuilder.service.persistence.impl.FooPersistenceImpl} \\\\\n\\end{longtable}\n\nSince these classes are Spring Beans and NOT OSGi Declarative Services\ncomponents, they don't use the \\texttt{@Reference} Declarative Services\nannotation to inject themselves. Here are the recommended Liferay\nannotations a Service Builder Spring Bean can use.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Use \\texttt{@BeanReference} to reference a Spring Bean that is in the\n  Application Context.\n\\item\n  Use \\texttt{@ServiceReference} to reference an OSGi service.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} When invoking service entity updates (e.g.,\n\\texttt{fooService.update(object)}) for services that have MVCC enabled,\nmake sure to do so in transactions. Propagate rejected transactions to\nthe UI for the user to handle. For details, see\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information\\#multiversion-concurrency-control-mvcc}{Multiversion\nconcurrency control (MVCC)}.\n\n\\noindent\\hrulefill\n\nThe \\texttt{@BeanReference} annotation is explained first.\n\n\\section{Referencing a Spring Bean that is in the Application\nContext}\\label{referencing-a-spring-bean-that-is-in-the-application-context}\n\nA Service Builder Spring Bean class, such as a\n\\texttt{*LocalServiceImpl} class, should use Liferay's\n\\texttt{@BeanReference} annotation to access other Spring Beans the\nmodule's Spring Application Context defines.\n\nFor example, if your service module's \\texttt{service.xml} file defines\nlocal services for entities named \\texttt{Foo} and \\texttt{Bar}, Service\nBuilder generates a \\texttt{module-spring.xml} file that defines local\nservice Spring Beans for both entities. To inject the\n\\texttt{BarLocalService} Spring Bean into the\n\\texttt{FooLocalServiceImpl} class, for example, the\n\\texttt{FooLocalServiceImpl} class must declare a\n\\texttt{BarLocalService} field and apply an \\texttt{@BeanReference}\nannotation to it.\n\n\\begin{verbatim}\n@BeanReference\nprivate BarLocalService _barLocalService;\n\\end{verbatim}\n\nThe \\texttt{@BeanReference} tells Liferay's AOP to treat the bean\nreference for use in transactions, search indexing, or security, if\nneeded. The referencing class can invoke the Spring Bean class's\nmethods.\n\nBesides the services Service Builder makes available for your\napplication, Service Builder Spring Bean classes can also access any\nservice published in the OSGi Registry. This means the following\nservices are available:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Beans defined in Liferay's core\n\\item\n  Beans created in other module app contexts\n\\item\n  Services declared using OSGi Declarative Services\n\\item\n  Services registered using the OSGi low level API\n\\end{itemize}\n\nThese are all OSGi services. The next section demonstrates a Service\nBuilder Spring Bean referencing OSGi services.\n\n\\section{Referencing OSGi Services}\\label{referencing-osgi-services}\n\nIn many cases, your Service Builder code (Spring Beans) must use\nexternal services. Liferay's \\texttt{@ServiceReference} annotation lets\nLiferay Spring Beans reference OSGi services.\n\nSuppose you're building an application with a simple entity your service\nmodule defines in its \\texttt{service.xml} file. The application must\nsend an SMS every time a new entity is created, and the\n\\texttt{SMSService} is provided by a module installed in the system.\n\nYour \\texttt{*LocalServiceImpl} (Spring Bean) could use an\n\\texttt{@ServiceReference} annotation to reference the \\emph{external}\nservice.\n\n\\begin{verbatim}\n@ServiceReference\nprivate SMSService _smsService;\n\\end{verbatim}\n\nThis annotation retrieves a reference to the OSGi service and provides\nsome nice benefits. None of the Spring context is created until the\n\\texttt{SMSService} service is available. Likewise, if the\n\\texttt{SMSService} suddenly disappears, the whole Spring Application\nContext is destroyed. This makes Liferay Spring apps robust and\nversatile.\n\nFortunately, Service Builder generates this kind of code for every\nentity your \\texttt{service.xml} file references. For example, the\n\\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-samples}{Liferay\nBlade Service Builder sample project} \\texttt{basic-service} module's\n\\texttt{service.xml} file defines a \\texttt{Foo} entity that references\nan \\texttt{AssetEntry} entity:\n\n\\begin{verbatim}\n<reference entity=\"AssetEntry\" package-path=\"com.liferay.portlet.asset\" />\n\\end{verbatim}\n\nService Builder generated the \\texttt{FooLocalServiceBaseImpl} class\n(the base class is part of the \\texttt{FooLocalServiceImpl} class's\nhierarchy), which references the \\texttt{AssetEntry} entity's local\nservice \\texttt{AssetEntryLocalService} using a field annotated with\n\\texttt{@ServiceReference}:\n\n\\begin{verbatim}\n@ServiceReference(type = com.liferay.asset.kernel.service.AssetEntryLocalService.class)\nprotected com.liferay.asset.kernel.service.AssetEntryLocalService assetEntryLocalService;\n\\end{verbatim}\n\nGreat! You now know how to add a reference to any OSGi service to a\nService Builder Spring Bean. You also know how to add a reference to any\nother Spring Bean in the Application Context of your Service Builder\nSpring Bean.\n\n\\section{Related Topics}\\label{related-topics-22}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services}{Invoking\nLocal Services}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker}{Service\nTrackers}\n\n\\chapter{Advanced Queries}\\label{advanced-queries}\n\nService Builder doesn't limit you to what you can cook up with\n\\texttt{\\textless{}finder\\ /\\textgreater{}} elements in\n\\texttt{service.xml}. If simple finders aren't sufficient for getting\ndata out of your application, you can use Liferay's Dynamic Query API,\nwhich wraps Hibernate's Criteria API, or your own SQL to make exactly\nthe queries you need.\n\nThough you can use custom SQL queries with Service Builder to retrieve\ndata from the database, sometimes it's more convenient to build queries\ndynamically at runtime. You can do this with Liferay's Dynamic Query\nAPI, which wraps Hibernate's Criteria API. The Dynamic Query API lets\nyou build queries without writing any SQL. It helps you think in terms\nof objects and member variables instead of tables and columns. Complex\nqueries can be significantly easier to understand and maintain than the\nequivalent custom SQL (or HQL) queries. While you technically don't need\nto know SQL to construct Dynamic Queries, you still must take care to\nconstruct efficient queries. For information on Hibernate's Criteria\nAPI, please see\n\\href{http://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/chapters/query/criteria/Criteria.html}{Hibernate's\nmanual}.\n\nWhichever way you decide to implement your custom queries, this guide\nshows you how. Here are the steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If using SQL,\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/custom-sql}{create your SQL\n  query}.\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/defining-a-custom-finder-method}{Define\n  a custom finder method}.\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/dynamic-query}{Implement\n  your finder using Dynamic Query API or SQL}.\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/accessing-your-custom-finder-method-from-the-service-layer}{Add\n  a method to your \\texttt{*LocalServiceImpl} class that invokes your\n  finder}.\n\\end{enumerate}\n\nOnce you've taken these steps, you can access your custom finder as a\nservice method. Note: You can create multiple or overloaded\n\\texttt{findBy*} finder methods in your \\texttt{*FinderImpl} class.\nNext, you'll examine these steps in more detail.\n\n\\chapter{Custom SQL}\\label{custom-sql}\n\nService Builder creates finder methods that retrieve entities by their\nattributes: their column values. When you add a column as a parameter\nfor the finder in your \\texttt{service.xml} file and run Service\nBuilder, it generates the finder method in your persistence layer and\nadds methods to your service layer that invoke the finder. If your\nqueries are simple enough, consider using\n\\href{/docs/7-2/appdev/-/knowledge_base/a/dynamic-query}{Dynamic Query}\nto access Liferay's database. If you want to do something more\ncomplicated like JOINs, you can write your own custom SQL queries. Here,\nyou'll learn how.\n\nThe Guestbook application has two tables, one for guestbooks and one for\nguestbook entries. The entry entity's foreign key to its guestbook is\nthe guestbook's ID. That is, the entry entity table,\n\\texttt{GB\\_GuestbookEntry}, tracks an entry's guestbook by its long\ninteger ID in the table's \\texttt{guestbookId} column. If you want to\nfind a guestbook entry based on its name, message, and guestbook name,\nyou must access the \\emph{name} of the entry's guestbook. Of course,\nwith SQL you can join the entry and guestbook tables to include the\nguestbook name. Service Builder lets you do this by specifying the SQL\nas \\emph{Liferay custom SQL} and invoking it in your service via a\n\\emph{custom finder method}.\n\nUsing Custom SQL in Service Builder is the same as using dynamic\nqueries; it just takes an additional first step to place the SQL you\nwant to run in an XML file. If you plan to use dynamic queries instead,\nskip the rest of this tutorial and move on to the next one.\n\n\\section{Specify Your Custom SQL}\\label{specify-your-custom-sql}\n\nAfter you've tested your SQL, you must specify it in a particular file\nfor Liferay to access it. \\texttt{CustomSQL} class (from module\n\\href{https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.portal.dao.orm.custom.sql.api/}{\\texttt{com.liferay.portal.dao.orm.custom.sql.api}})\nretrieves SQL from a file called \\texttt{default.xml} in your service\nmodule's \\texttt{src/main/resources/META-INF/custom-sql/} folder. You\nmust create the \\texttt{custom-sql} folder and create the\n\\texttt{default.xml} file in that \\texttt{custom-sql} folder. The\n\\texttt{default.xml} file must adhere to the following format:\n\n\\begin{verbatim}\n<custom-sql>\n    <sql id=\"[fully-qualified class name + method]\">\n    SQL query wrapped in <![CDATA[...]]>\n    No terminating semi-colon\n    </sql>\n</custom-sql>\n\\end{verbatim}\n\nCreate a \\texttt{custom-sql} element for every SQL query you want in\nyour application, and give each query a unique ID. The recommended\nconvention to use for the ID value is the fully-qualified class name of\nthe finder followed by a dot (\\texttt{.}) character and the name of the\nfinder method. More detail on the finder class and finder methods is\nprovided in the next step.\n\nFor example, in the Guestbook application, you could use the following\nID value to specify a query:\n\n\\begin{verbatim}\ncom.liferay.docs.guestbook.service.persistence.EntryFinder.findByEntryNameEntryMessageGuestbookName\n\\end{verbatim}\n\nCustom SQL must be wrapped in character data (\\texttt{CDATA}) for the\n\\texttt{sql} element. Importantly, do not terminate the SQL with a\nsemi-colon. Following these rules, the \\texttt{default.xml} file of the\nGuestbook application specifies an SQL query that joins the\n\\texttt{GB\\_GuestbookEntry} and \\texttt{GB\\_Guestbook} tables:\n\n\\begin{verbatim}\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<custom-sql>\n    <sql id=\"com.liferay.docs.guestbook.service.persistence.EntryFinder.findByEntryNameEntryMessageGuestbookName\">\n        <![CDATA[\n            SELECT GB_GuestbookEntry.*\n            FROM GB_GuestbookEntry\n            INNER JOIN \n                GB_Guestbook ON GB_GuestbookEntry.guestbookId = GB_Guestbook.guestbookId\n            WHERE\n                (GB_GuestbookEntry.name LIKE ?) AND\n                (GB_GuestbookEntry.message LIKE ?) AND\n                (GB_Guestbook.name LIKE ?)\n        ]]>\n    </sql>\n</custom-sql>\n\\end{verbatim}\n\nNow that you've specified some custom SQL, the next step is to implement\na finder method to invoke it. The method name for the finder should\nmatch the ID you just specified for the \\texttt{sql} element.\n\nCongratulations on developing a custom SQL query and custom finder for\nyour application!\n\n\\section{Related Topics}\\label{related-topics-23}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers}{Customizing\nLiferay Services}\n\n\\chapter{Defining a Custom Finder\nMethod}\\label{defining-a-custom-finder-method}\n\nDynamic queries and custom SQL belong in finder methods. You implement\nthem and then make them available through an interface. This article\ndemonstrates defining the finder method in an implementation class,\ngenerating its interface and tying the implementation to the interface.\n\nAn example of this is a Guestbook application with two entities:\nguestbook and entry. Each entry belongs to a guestbook so the entry\nentity has a \\texttt{guestbookId} field as a foreign key. If you need a\nfinder to search for guestbook entries by entry name and guestbook name,\nyou'd add a finder method to \\texttt{GuestbookFinderImpl} and name it\n\\texttt{findByEntryNameGuestbookName}. The full method signature would\nbe\n\\texttt{findByEntryNameGuestbookName(String\\ entryName,\\ String\\ guestbookName)}.\nThe steps are below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a \\texttt{{[}Entity{]}FinderImpl} class in the\n  \\texttt{{[}package\\ \\ \\ \\ \\ \\ path{]}.service.persistence.impl}\n  package of your service module's \\texttt{src/main/java} folder. Recall\n  that you specify the \\texttt{{[}package\\ path{]}} in your\n  \\texttt{service.xml} file. Here's an example:\n\n\\begin{verbatim}\n<service-builder package-path=\"com.liferay.docs.guestbook\">\n    ...\n</service-builder>\n\\end{verbatim}\n\\item\n  Define a \\texttt{findBy*} finder method in the class you created. Make\n  sure to add any required arguments to your finder method signature.\n\\item\n  Run Service Builder to generate the appropriate interface in the\n  \\texttt{{[}package\\ \\ \\ \\ \\ \\ path{]}.service.persistence} package in\n  the \\texttt{service} folder of your API and service modules.\n\n  For example, after adding\n  \\texttt{findByEntryNameGuestbookName(String\\ entryName,\\ String\\ guestbookName)}\n  to \\texttt{GuestbookFinderImpl} and running Service Builder, the\n  interface\n  \\texttt{com.liferay.docs.guestbook.service.persistence.GuestbookFinder}\n  is generated.\n\\item\n  Make the finder class a component (annotated with\n  \\href{https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html}{\\texttt{@Component}})\n  that implements the \\texttt{GuestbookFinder} interface. For example,\n  the class declaration should look like this:\n\n\\begin{verbatim}\n@Component(service = GuestbookFinder.class)\npublic class GuestbookFinderImpl extends BasePersistenceImpl<Guestbook> implements GuestbookFinder\n\\end{verbatim}\n\\end{enumerate}\n\nYour next step is to implement the query in your finder. You can do this\nvia the Dynamic Query API or Custom SQL. The next tutorial covers\nDynamic Query. To simply call custom SQL you have written, create a\nfinder method to run your SQL:\n\n\\begin{verbatim}\npublic List<Entry> findByEntryNameEntryMessageGuestbookName(\n    String entryName, String entryMessage, String guestbookName,\n    int begin, int end) {\n\n    Session session = null;\n    try {\n        session = openSession();\n\n        String sql = _customSQL.get(\n            getClass(),\n            FIND_BY_ENTRYNAME_ENTRYMESSAGE_GUESTBOOKNAME);\n\n        SQLQuery q = session.createSQLQuery(sql);\n        q.setCacheable(false);\n        q.addEntity(\"GB_Entry\", EntryImpl.class);\n\n        QueryPos qPos = QueryPos.getInstance(q);\n        qPos.add(entryName);\n        qPos.add(entryMessage);\n        qPos.add(guestbookName);\n\n        return (List<Entry>) QueryUtil.list(q, getDialect(), begin, end);\n    }\n    catch (Exception e) {\n        try {\n            throw new SystemException(e);\n        }\n        catch (SystemException se) {\n            se.printStackTrace();\n        }\n    }\n    finally {\n        closeSession(session);\n    }\n\n    return null;\n}\n\npublic static final String FIND_BY_ENTRYNAME_ENTRYMESSAGE_GUESTBOOKNAME =\n    EntryFinder.class.getName() +\n        \".findByEntryNameEntryMessageGuestbookName\";\n\n@Reference\nprivate CustomSQL _customSQL;\n\\end{verbatim}\n\nThe custom finder method opens a new Hibernate session and uses\nLiferay's\n\\texttt{CustomSQL.get(Class\\textless{}?\\textgreater{}\\ clazz,\\ String\\ id)}\nmethod to get the custom SQL to use for the database query. The\n\\texttt{FIND\\_BY\\_ENTRYNAME\\_ENTRYMESSAGE\\_GUESTBOOKNAME} static field\ncontains the custom SQL query's ID. The\n\\texttt{FIND\\_BY\\_EVENTNAME\\_EVENTDESCRIPTON\\_LOCATIONNAME} string is\nbased on the fully-qualified class name of the \\texttt{*Finder}\ninterface (\\texttt{EventFinder}) and the name of the finder method\n(\\texttt{findByEntryNameEntryMessageGuestbookName}).\n\nAwesome! You've implemented your finder class, and if you're using\ncustom SQL, you've even implemented a method to call your finder. If\nyou're using Dynamic Query, the next tutorial shows you how to implement\na dynamic query finder method.\n\n\\chapter{Dynamic Query}\\label{dynamic-query}\n\nOnce you've\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-a-custom-finder-method}{defined\nyour custom finder method}, you can use the Dynamic Query API to\nimplement your query in it. Here's what you must do in your finder\nmethod:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\hyperref[using-a-hibernate-session]{Open a Hibernate Session}\n\\item\n  \\hyperref[creating-dynamic-queries]{Create a dynamic query using these\n  Hibernate features}:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\emph{Restrictions}: Similar to \\texttt{where} clauses of an SQL\n    query, restrictions limit results based on criteria.\n  \\item\n    \\emph{Projections}: Modify the kind of results the query returns.\n  \\item\n    \\emph{Orders}: Organize results.\n  \\end{itemize}\n\\item\n  \\hyperref[executing-the-dynamic-query]{Execute the Dynamic Query and\n  return the results}\n\\end{enumerate}\n\nBefore implementing a dynamic query in your own finder method, it can be\nhelpful to examine an example. The following example method uses\nmultiple dynamic queries and all the Hibernate features. Instructions\nfor implementing your own finder method follow the example.\n\n\\section{Example Finder Method:\nfindByGuestbookNameEntryName}\\label{example-finder-method-findbyguestbooknameentryname}\n\nThis finder method for the Guestbook application retrieves a list of\nGuestbook entries that have a specific name and that also belong to a\nGuestbook of a specific name:\n\n\\begin{verbatim}\npublic List<Entry> findByEntryNameGuestbookName(String entryName, String guestbookName) {\n\n    Session session = null;\n    try {\n        session = openSession();\n\n        ClassLoader classLoader = getClass().getClassLoader();\n\n        DynamicQuery guestbookQuery = DynamicQueryFactoryUtil.forClass(Guestbook.class, classLoader)\n            .add(RestrictionsFactoryUtil.eq(\"name\", guestbookName))\n            .setProjection(ProjectionFactoryUtil.property(\"guestbookId\"));\n\n        Order order = OrderFactoryUtil.desc(\"modifiedDate\");\n\n        DynamicQuery entryQuery = DynamicQueryFactoryUtil.forClass(Entry.class, classLoader))\n            .add(RestrictionsFactoryUtil.eq(\"name\", entryName))\n            .add(PropertyFactoryUtil.forName(\"guestbookId\").in(guestbookQuery))\n            .addOrder(order);\n\n        List<Entry> entries = _entryLocalService.dynamicQuery(entryQuery);\n\n        return entries;\n    }\n    catch (Exception e) {\n        try {\n            throw new SystemException(e);\n        }\n        catch (SystemException se) {\n            se.printStackTrace();\n        }\n    }\n    finally {\n        closeSession(session);\n    }\n}\n\\end{verbatim}\n\nThe method first opens a Hibernate session. While the session is open in\nthe \\texttt{try} block, it creates and executes a dynamic query, which\nreturns results (a list of guestbook \\texttt{Entry} objects) if all goes\nwell.\n\nThe finder method has two distinct dynamic queries.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The first query retrieves a list of guestbook IDs corresponding to\n  guestbook names that match the \\texttt{guestbookName} parameter of the\n  finder method.\n\\item\n  The second query retrieves a list of guestbook entries with entry\n  names that match the \\texttt{entryName} parameter and have\n  \\texttt{guestbookId} foreign keys belonging to the list returned by\n  the first query.\n\\end{enumerate}\n\nHere's the first query:\n\n\\begin{verbatim}\nDynamicQuery guestbookQuery = DynamicQueryFactoryUtil.forClass(Guestbook.class, classLoader))\n    .add(RestrictionsFactoryUtil.eq(\"name\", guestbookName))\n    .setProjection(ProjectionFactoryUtil.property(\"guestbookId\"));\n\\end{verbatim}\n\nBy default,\n\\texttt{DynamicQueryFactoryUtil.forClass(Guestbook.class,\\ classLoader))}\nreturns a query that retrieves a list of all guestbook entities. Adding\nthe \\texttt{.add(RestrictionsFactoryUtil.eq(\"name\",\\ guestbookName))}\nrestriction limits the results to only those guestbooks whose guestbook\nnames match the \\texttt{guestbookName} parameter. The\n\\texttt{.setProjection(ProjectionFactoryUtil.property(\"guestbookId\"))}\nprojection changes the result set from a list of guestbook entries to a\nlist of guestbook IDs. This is useful since guestbook IDs are much less\nexpensive to retrieve than full guestbook entities, and the entry query\nonly needs the guestbook IDs.\n\nNext appears an order:\n\n\\begin{verbatim}\nOrder order = OrderFactoryUtil.desc(\"modifiedDate\");\n\\end{verbatim}\n\nThis arranges the results list in descending order of the query entity's\n\\texttt{modifiedDate} attribute. Thus the most recently modified\nentities (guestbook entries, in our example) appear first and the least\nrecently modified entities appear last.\n\nHere's the second query:\n\n\\begin{verbatim}\nDynamicQuery entryQuery = DynamicQueryFactoryUtil.forClass(Entry.class, classLoader))\n    .add(RestrictionsFactoryUtil.eq(\"name\", entryName))\n    .add(PropertyFactoryUtil.forName(\"guestbookId\").in(guestbookQuery))\n    .addOrder(order);\n\\end{verbatim}\n\nBy default,\n\\texttt{DynamicQueryFactoryUtil.forClass(Entry.class,\\ classLoader))}\nreturns a list of all guestbook entry entities. The\n\\texttt{.add(RestrictionsFactoryUtil.eq(\"name\",\\ entryName))}\nrestriction limits the results to only those guestbook entries whose\nnames match the finder method's \\texttt{entryName} parameter.\n\\href{https://docs.liferay.com/dxp/portal/7.0-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/PropertyFactoryUtil.html}{\\texttt{PropertyFactoryUtil}}\nis a Liferay utility class whose method\n\\texttt{forName(String\\ propertyName)} returns the specified property.\nThis property can be passed to another Liferay dynamic query. This is\nexactly what happens in the following line of our example:\n\n\\begin{verbatim}\n.add(PropertyFactoryUtil.forName(\"guestbookId\").in(guestbookQuery))\n\\end{verbatim}\n\nHere, the code makes sure that the guestbook IDs (foreign keys) of the\nentry entities in the \\texttt{entityQuery} belong to the list of\nguestbook IDs returned by the \\texttt{guestbookQuery}. Declaring that an\nentity property in one query must belong to the result list of another\nquery is a way to use the dynamic query API to create complex queries,\nsimilar to SQL joins.\n\nLastly, the order defined earlier is applied to the entries returned by\nthe \\texttt{findByEntryNameGuestbookName} finder method:\n\n\\begin{verbatim}\n.addOrder(order);\n\\end{verbatim}\n\nThis orders the list of guestbook entities by the \\texttt{modifiedDate}\nattribute, from most recent to least recent.\n\nLastly, the dynamic query is invoked on the \\texttt{EntryLocalService}\ninstance. It returns a list of \\texttt{Entry} objects which are then\nreturned by the finder method.\n\n\\begin{verbatim}\nList<Entry> entries = _entryLocalService.dynamicQuery(entryQuery);\n\nreturn entries;\n\\end{verbatim}\n\nIt's time to implement your finder method to use Dynamic Query. Start\nwith opening and managing a Hibernate session.\n\n\\section{Using a Hibernate Session}\\label{using-a-hibernate-session}\n\nYour first step in implementing your custom finder method in your\n\\texttt{*FinderImpl} class is to open a new Hibernate session. Since\nyour \\texttt{*FinderImpl} class extends\n\\texttt{BasePersistenceImpl\\textless{}Entity\\textgreater{}}, and\n\\texttt{BasePersistenceImpl\\textless{}Entity\\textgreater{}} contains a\nsession factory object and an\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/service/persistence/impl/BasePersistenceImpl.html\\#openSession--}{\\texttt{openSession}}\nmethod, you can invoke the \\texttt{openSession} method of your\n\\texttt{*FinderImpl}'s parent class to open a new Hibernate session. The\nstructure of your finder method should look like this:\n\n\\begin{verbatim}\npublic List<Entity> findBy-(...) {\n\n    Session session = null;\n    try {\n            session = openSession();\n            \n            /*\n            create a dynamic\n            query to retrieve and return the desired list of entity\n            objects\n            */\n    }\n    catch (Exception e) {\n            // Exception handling\n    }\n    finally {\n            closeSession(session);\n    }\n\n    return null;\n    /*\n    Return null only if there was an error returning the\n    desired list of entity objects in the try block\n    */\n\n}\n\\end{verbatim}\n\nNext, in the try block, create your dynamic query objects.\n\n\\section{Creating Dynamic Queries}\\label{creating-dynamic-queries}\n\nIn Liferay, you don't create criteria objects directly from the\nHibernate session. Instead, you create dynamic query objects using\nLiferay's\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/DynamicQueryFactoryUtil.html}{\\texttt{DynamicQueryFactoryUtil}}\nservice. Thus, instead of\n\n\\begin{verbatim}\nCriteria entryCriteria = session.createCriteria(Entry.class);\n\\end{verbatim}\n\nyou use\n\n\\begin{verbatim}\nDynamicQuery entryQuery = DynamicQueryFactoryUtil.forClass(Entry.class, classLoader));\n\\end{verbatim}\n\nIn your finder method, initialize your dynamic query for your entity\nclass.\n\nMost features of Hibernate's Criteria API, including restrictions,\nprojections, and orders, can be used on Liferay dynamic query objects.\nEach criteria can be applied to your query. The restriction criteria\ntype is described first.\n\n\\section{Restriction Criteria}\\label{restriction-criteria}\n\nRestrictions in Hibernate's Criteria API roughly correspond to the\n\\texttt{where} clause of an SQL query: they offer a variety of ways to\nlimit the results returned by the query. You can use restrictions, for\nexample, to cause a query to return only results where a certain field\nhas a particular value, or a value in a certain range, or a non-null\nvalue, etc.\n\nWhen you need to add restrictions to a dynamic query, don't call\nHibernate's \\texttt{Restrictions} class directly. Instead, use the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/RestrictionsFactoryUtil.html}{\\texttt{RestrictionsFactoryUtil}}\nservice. \\texttt{RestrictionsFactoryUtil} has the same methods that\nyou're used to from Hibernate's \\texttt{Restrictions} class:\n\\texttt{in}, \\texttt{between}, \\texttt{like}, \\texttt{eq}, \\texttt{ne},\n\\texttt{gt}, \\texttt{ge}, \\texttt{lt}, \\texttt{le}, etc.\n\nThus, instead of using this call to specify that a guestbook must have a\ncertain name,\n\n\\begin{verbatim}\nentryCriteria.add(Restrictions.eq(\"name\", guestbookName));\n\\end{verbatim}\n\nyou use\n\n\\begin{verbatim}\nentryQuery.add(RestrictionsFactoryUtil.eq(\"name\", guestbookName));\n\\end{verbatim}\n\nThe restriction above limits the results to guestbook entries whose\n\\texttt{name} attribute matches the value of the variable\n\\texttt{guestbookName}. Add the restrictions you need to get the results\nyou want.\n\nProjections are the next criteria type. They let you transform the query\nresults to return the field type you desire.\n\n\\section{Projection Criteria}\\label{projection-criteria}\n\nProjections in Hibernate's Criteria API let you modify the kind of\nresults returned by a query. For example, if you don't want your query\nto return a list of entity objects (the default), you can set a\nprojection on a query to return only a list of the values of a certain\nentity field, or fields. You can also use projections on a query to\nreturn the maximum or minimum value of an entity field, or the sum of\nall the values of a field, or the average, etc. For more information on\nrestrictions and projections, please refer to Hibernate's\n\\href{http://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/querycriteria.html}{documentation}.\n\nSimilarly, to set projections, create properties via Liferay's\n\\href{https://docs.liferay.com/dxp/portal/7.0-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/PropertyFactoryUtil.html}{PropertyFactoryUtil}\nservice instead of through Hibernate's \\texttt{Property} class. Thus,\ninstead of\n\n\\begin{verbatim}\nentryCriteria.setProjection(Property.forName(\"guestbookId\"));\n\\end{verbatim}\n\nyou use\n\n\\begin{verbatim}\nentryQuery.setProjection(PropertyFactoryUtil.forName(\"guestbookId\"));\n\\end{verbatim}\n\nThe projection above specifies the \\texttt{guestbookId} entity field to\nchanges the result set to a list of those field values. If you want to\nreturn a specific field type from your entities, add a projection for\nit.\n\nThe last criteria type lets you organize results your way.\n\n\\section{Order Criteria}\\label{order-criteria}\n\nOrders in Hibernate's Criteria API let you control the order of the\nelements in the list a query returns. You can choose the property or\nproperties to which an order applies as well as whether they're in\nascending or descending order.\n\nThis code creates an order by the entity's \\texttt{modifiedDate}\nattribute:\n\n\\begin{verbatim}\nOrder order = OrderFactoryUtil.desc(\"modifiedDate\");\n\\end{verbatim}\n\nWhen you apply this order, the results are arranged in descending order\nof the query entity's \\texttt{modifiedDate} attribute. Thus the most\nrecently modified entities (guestbook entries, in our example) appear\nfirst and the least recently modified entities appear last.\n\nLike Hibernate criteria, Liferay's dynamic queries are\n\\emph{chain-able}: you can add criteria to, set projections on, and add\norders to Liferay's dynamic query objects just by appending the\nappropriate method calls to the query object. For example, the following\nsnippet demonstrates chaining a restriction criterion and a projection\nto a dynamic query object declaration:\n\n\\begin{verbatim}\nDynamicQuery guestbookQuery = DynamicQueryFactoryUtil.forClass(Guestbook.class)\n    .add(RestrictionsFactoryUtil.eq(\"name\", guestbookName))\n    .setProjection(ProjectionFactoryUtil.property(\"guestbookId\"));\n\\end{verbatim}\n\nIt's time to execute your dynamic query.\n\n\\section{Executing the Dynamic Query}\\label{executing-the-dynamic-query}\n\nIn the previous article, you ran Service Builder after\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-a-custom-finder-method}{defining\nyour custom finder}. Service Builder generated a\n\\texttt{dynamicQuery(DynamicQuery\\ dynamicQuery)} method in your\n\\texttt{*LocalServiceBaseImpl} class. Using a \\texttt{*LocalService}\ninstance, invoke \\texttt{dynamicQuery} method, passing it your dynamic\nquery. Here's an example dynamic query execution.\n\n\\begin{verbatim}\nList<Entity> entities = _someLocalService.dynamicQuery(entityQuery);\n\nreturn entities;\n\\end{verbatim}\n\nThe dynamic query execution returns a list of entities and the finder\nmethod returns that list.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Service Builder not only generates a\n\\texttt{public\\ List\\ \\ dynamicQuery(DynamicQuery\\ dynamicQuery)} method\nin \\texttt{*LocalServiceBaseImpl} but it also generates\n\\texttt{public\\ List\\ dynamicQuery(DynamicQuery\\ dynamicQuery,\\ int\\ \\ start,\\ int\\ end)}\nand\n\\texttt{public\\ List\\ dynamicQuery(DynamicQuery\\ dynamicQuery,\\ int\\ \\ start,\\ int\\ end,\\ OrderByComparator\\ orderByComparator)}\nmethods. You can go back to\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-a-custom-finder-method}{defining\ncustom finder methods} and either modify your finder method or create\noverloaded versions of it to take advantage of these extra methods and\ntheir parameters. The \\texttt{int\\ start} and \\texttt{int\\ \\ end}\nparameters are useful when paginating a result list. \\texttt{start} is\nthe lower bound of the range of model entity instances and \\texttt{end}\nis the upper bound. The \\texttt{OrderByComparator\\ orderByComparator} is\nthe comparator by which to order the results.\n\n\\noindent\\hrulefill\n\nTo use the overloaded \\texttt{dynamicQuery} methods of your\n\\texttt{*LocalServiceBaseImpl} class in the (optionally overloaded)\ncustom finders of your \\texttt{*FinderImpl} class, just choose the\nappropriate methods for running the dynamic queries:\n\\texttt{dynamicQuery(entryQuery)}, or\n\\texttt{dynamicQuery(entryQuery,\\ start,\\ end)} or\n\\texttt{dynamicQuery(entryQuery,\\ start,\\ end,\\ orderByComparator)}.\n\nGreat! You've now created a finder method using Liferay's Dynamic Query\nAPI. Your last step is to add a service method that calls your finder.\n\n\\chapter{Accessing Your Custom Finder Method from the Service\nLayer}\\label{accessing-your-custom-finder-method-from-the-service-layer}\n\nSo far, you've created a \\texttt{*FinderImpl} class, defined a\n\\texttt{findBy*} finder method in that class, and implemented the finder\nmethod using Dynamic Query or custom SQL. Now how do you call your\nfinder method from the service layer?\n\nWhen you ran Service Builder (if you haven't run it yet; run it now),\nthe \\texttt{*Finder} interface was generated (e.g.,\n\\texttt{GuestbookFinder}). For proper separation of concerns, only a\nlocal or remote service implementation (i.e., \\texttt{*LocalServiceImpl}\nor \\texttt{*ServiceImpl}) in your service module should invoke the\n\\texttt{*Finder} class. The portlet classes in your application's web\nmodule invoke the business logic of the services published from your\napplication's service module. The services, in turn, access the data\nmodel using the persistence layer's finder classes.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} In previous versions of Liferay DXP, your finder methods\nwere accessible via \\texttt{*FinderUtil} utility classes. Finder methods\nare now injected into your app's local services, removing the need to\ncall finder utilities.\n\n\\noindent\\hrulefill\n\nYou'll add a method in the \\texttt{*LocalServiceImpl} class that invokes\nthe finder method implementation via the \\texttt{*Finder} class. Then\nyou'll rebuild your application's service layer so that the portlet\nclasses and JSPs in your web module can access the services.\n\nFor example, for the Guestbook application, you'd add the following\nmethod to the \\texttt{GuestbookEntryLocalServiceImpl} class:\n\n\\begin{verbatim}\npublic List<GuestbookEntry> findByEntryNameGuestbookName(String entryName,\n    String guestbookName) throws SystemException {\n\n    return entryFinder.findByEntryNameGuestbookName(String entryName,\n        String guestbookName);\n}\n\\end{verbatim}\n\nAfter you've added your \\texttt{findBy*} method to your\n\\texttt{*LocalServiceImpl} class, run Service Builder to generate the\ninterface and make the finder method available in the\n\\texttt{EntryLocalService} class.\n\nNow you can indirectly call the finder method from your portlet class or\nfrom a JSP by calling\n\\texttt{\\_entryLocalService.findByEntryNameGuestbookName(...)}!\n\nCongratulations on following the process of developing custom queries in\na custom finder and exposing it as a service for your portlet!\n\n\\section{Related Topics}\\label{related-topics-24}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{Creating\nLocal Service}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services}{Invoking\nLocal Services}\n\n\\chapter{Actionable Dynamic Queries}\\label{actionable-dynamic-queries}\n\nSuppose you have over a million users, and you want to perform some kind\nof mass update to some of them. One approach might be to use a dynamic\nquery to retrieve the list of users in question. Once loaded into\nmemory, you could loop through the list and update each user. However,\nwith over a million users, the memory cost of such an operation would be\ntoo great. In general, retrieving large numbers of Service Builder\nentities using dynamic queries requires too much memory and time.\n\nLiferay actionable dynamic queries solve this problem. Actionable\ndynamic queries use a pagination strategy to load only small numbers of\nentities into memory at a time and perform processing (i.e., perform an\n\\emph{action}) on each entity. So instead of trying to use a dynamic\nquery to load a million users into memory and then perform some\nprocessing on each of them, a much better strategy is to use an\nactionable dynamic query. This way, you can still process your million\nusers, but only small numbers are loaded into memory at a time.\n\nHere's how to use actionable dynamic query:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get an\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/ActionableDynamicQuery.html}{\\texttt{ActionableDynamicQuery}}\n  from your \\texttt{*LocalService} by invoking its\n  \\texttt{getActionableDynamicQuery} method.\n\\item\n  Add query criteria and constraints, using the query's\n  \\texttt{setAddCriteriaMethod} and \\texttt{setAddOrderCriteriaMethod}\n  methods.\n\\item\n  Set an action to perform on the matching entities, using\n  \\texttt{setPerformActionMethod}.\n\\item\n  Execute the action on each matching entity, by invoking the query's\n  \\texttt{performActions} method.\n\\end{enumerate}\n\nThis method from a sample portlet creates an actionable dynamic query,\nadds a query restriction and an action, and executes the query:\n\n\\begin{verbatim}\nprotected void massUpdate() {\n    ActionableDynamicQuery adq = _barLocalService.getActionableDynamicQuery();\n    \n    adq.setAddCriteriaMethod(new ActionableDynamicQuery.AddCriteriaMethod() {\n        \n        @Override\n        public void addCriteria(DynamicQuery dynamicQuery) {\n            dynamicQuery.add(RestrictionsFactoryUtil.lt(\"field3\", 100));\n        }\n        \n    });\n    \n    adq.setPerformActionMethod(new ActionableDynamicQuery.PerformActionMethod<Bar>() {\n        \n        @Override\n        public void performAction(Bar bar) {\n            int field3 = bar.getField3();\n            field3++;\n            bar.setField3(field3);\n            _barLocalService.updateBar(bar);\n        }\n        \n    });\n    \n    try {\n        adq.performActions();\n    }\n    catch (Exception e) {\n        e.printStackTrace();\n    }\n}\n\\end{verbatim}\n\nThe example method demonstrates executing an actionable dynamic query on\n\\texttt{Bar} entities that match certain criteria.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Retrieve an\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/ActionableDynamicQuery.html}{\\texttt{ActionableDynamicQuery}}\n  from local service \\texttt{BarLocalService}.\n\n\\begin{verbatim}\nActionableDynamicQuery adq = _barLocalService.getActionableDynamicQuery();\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Service Builder generates method `getActionableDynamicQuery()` in\n each entity's `*LocalService` interface and implements it in each entity's\n `*BaseLocalServiceImpl` class.\n \n     @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n     public ActionableDynamicQuery getActionableDynamicQuery();\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Set query criteria to match \\texttt{field3} values less than\n  \\texttt{100}.\n\n\\begin{verbatim}\nadq.setAddCriteriaMethod(new ActionableDynamicQuery.AddCriteriaMethod() {\n\n     @Override\n     public void addCriteria(DynamicQuery dynamicQuery) {\n         dynamicQuery.add(RestrictionsFactoryUtil.lt(\"field3\", 100));\n     }\n\n });\n\\end{verbatim}\n\\item\n  Set an action to perform. The action increments the matching entity's\n  \\texttt{field3} value.\n\n\\begin{verbatim}\nadq.setPerformActionMethod(new ActionableDynamicQuery.PerformActionMethod<Bar>() {\n\n     @Override\n     public void performAction(Bar bar) {\n         int field3 = bar.getField3();\n         field3++;\n         bar.setField3(field3);\n         _barLocalService.updateBar(bar);\n     }\n\n });\n\\end{verbatim}\n\\item\n  Execute the action on each matching entity.\n\n\\begin{verbatim}\ntry {\n    adq.performActions();\n}\ncatch (Exception e) {\n    e.printStackTrace();\n}\n\\end{verbatim}\n\\end{enumerate}\n\nActionable dynamic queries let you act on large numbers of entities in\nsmaller groups. It's an efficient and high performing way to update\nentities.\n\n\\section{Related Topics}\\label{related-topics-25}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{Creating\nLocal Service}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services}{Invoking\nLocal Services}\n\n\\chapter{REST Builder}\\label{rest-builder}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} This documentation is in beta. Stay tuned for more to\ncome!\n\n\\noindent\\hrulefill\n\nLiferay DXP's headless REST APIs follow the\n\\href{https://swagger.io/docs/specification/about/}{OpenAPI}\nspecification and let your apps consume RESTful web services. These APIs\nare developed using a mixture of the Contract First and Contract Last\ndevelopment approaches. This presents a best-of-both-worlds approach to\nAPI development. For more detailed information, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/headless-rest-apis}{Headless\nREST APIs}.\n\nHere, you'll learn how to use Liferay's\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-rest-builder}{REST\nBuilder tool} to create headless REST APIs for your own apps. REST\nBuilder is an API generator that consumes OpenAPI profiles and generates\nthe API scaffolding: JAX-RS endpoints, parsing, XML generation, and\nadvanced features like filtering or multipart (binary file) support. The\ndeveloper only has to fill in the resource implementations, calling\nLiferay DXP's remote services.\n\nRead on to learn how to generate REST services with REST Builder!\n\n\\chapter{Generating APIs with REST\nBuilder}\\label{generating-apis-with-rest-builder}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} This documentation is in beta. Stay tuned for more to\ncome!\n\n\\noindent\\hrulefill\n\nFollow these steps to use REST Builder to create a headless REST API for\nyour app:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{project}.\n\\item\n  Install REST Builder. For instructions on this, see\n  \\href{/docs/7-2/reference/-/knowledge_base/r/rest-builder-gradle-plugin}{REST\n  Builder Gradle Plugin}.\n\\item\n  Run \\texttt{gradlew\\ clean\\ deploy}. Note that your Gradle wrapper may\n  not be in your app's project directory, so you may need to use\n  \\texttt{..} to locate it (e.g.,\n  \\texttt{../../../gradlew\\ clean\\ deploy}).\n\\item\n  Create the \\texttt{*-api} and \\texttt{*-impl} projects with the usual\n  files (\\texttt{build.gradle}, \\texttt{bnd.bnd}). Also create a\n  \\texttt{rest-config.yaml} with the author, paths, and packages. For\n  example, here's the \\texttt{rest-config.yaml} for Liferay's\n  \\texttt{headless-delivery} API:\n\n\\begin{verbatim}\napiDir: \"../headless-delivery-api/src/main/java\"\napiPackagePath: \"com.liferay.headless.delivery\"\napplication:\n    baseURI: \"/headless-delivery\"\n    className: \"HeadlessDeliveryApplication\"\n    name: \"Liferay.Headless.Delivery\"\nauthor: \"Javier Gamarra\"\nclientDir: \"../headless-delivery-client/src/main/java\"\ntestDir: \"../headless-delivery-test/src/testIntegration/java\"\n\\end{verbatim}\n\\item\n  In your \\texttt{*-impl} module's root folder, write your OpenAPI\n  profile in YAML. You can use the\n  \\href{https://editor.swagger.io/}{Swagger Editor} to validate syntax\n  and ensure compliance with the OpenAPI specification.\n\\item\n  In your \\texttt{*-impl} module folder, run \\texttt{gradlew\\ buildREST}\n  (make sure you locate your Gradle wrapper as instructed in step two\n  above).\n\\item\n  REST Builder generates the interfaces with the JAX-RS endpoints. It\n  also generates a \\texttt{*ResourceImpl} class where you must implement\n  the business logic for each service.\n\\item\n  After implementing the business logic for each service, deploy your\n  modules. Your APIs are then available at this URL:\n\n\\begin{verbatim}\nhttp://[host]:[port]/o/[APPLICATION_CLASSNAME]/[OPEN_API_VERSION]/\n\\end{verbatim}\n\n  You can also execute \\texttt{jaxrs:check} in the OSGi console to see\n  all the JAX-RS endpoints.\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-26}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/rest-builder}{REST Builder}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/headless-rest-apis}{Headless\nREST APIs}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/rest-builder-gradle-plugin}{REST\nBuilder Gradle Plugin}\n\n\\chapter{Troubleshooting Application Development\nIssues}\\label{troubleshooting-application-development-issues}\n\nWhen coding on any platform, you can sometimes run into issues that have\nno clear resolution. This can be particularly frustrating. If you have\nissues building, deploying, or running apps and modules, you want to\nresolve them fast. These frequently asked questions and answers help you\ntroubleshoot and correct problems.\n\nHere are the troubleshooting sections:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[modules]{Modules}\n\\item\n  \\hyperref[services-and-components]{Services and Components}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/troubleshooting-front-end-development-issues}{Front-end}\n\\end{itemize}\n\nClick a question to view the answer.\n\n\\section{Modules}\\label{modules}\n\n{How can I configure dependencies on Liferay artifacts?~{}}\n\n\\begin{verbatim}\n<p>See <a href=\"/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies\">Configuring Dependencies</a>. </p>\n\\end{verbatim}\n\n{What are optional package imports and how can I specify them?~{}}\n\n\\begin{verbatim}\n<p>When developing modules, you can declare <em>optional</em> package imports. An optional package import is one your module can use if it's available, but can still function without it. <a href=\"/docs/7-2/appdev/-/knowledge_base/a/declaring-optional-import-package-requirements\">Specifying optional package imports</a> is straightforward. </p>\n\\end{verbatim}\n\n{How can I connect to a JNDI data source from my module?~{}}\n\n\\begin{verbatim}\n<p>Connecting to an application server's JNDI data sources from Liferay's OSGi environment is almost the same as connecting to them from the Java EE environment. In an OSGi environment, the only difference is that you must <a href=\"/docs/7-2/appdev/-/knowledge_base/a/connecting-to-data-sources-using-jndi\">use Liferay DXP's class loader to load the application server's JNDI classes</a>. </p>\n\\end{verbatim}\n\n{My module has an unresolved requirement. What can I do?~{}}\n\n\\begin{verbatim}\n<p>If one of your bundles imports a package that no other bundle in the Liferay OSGi runtime exports, Liferay DXP reports an unresolved requirement:</p>\n<pre><code>! could not resolve the bundles: ...\nUnresolved requirement: Import-Package: ...\n...\nUnresolved requirement: Require-Capability ...\n</code></pre>\n<p>To satisfy the requirement, <a href=\"/docs/7-2/appdev/-/knowledge_base/a/resolving-bundle-requirements\">find a module that provides the capability, add it to your build file's dependencies, and deploy it</a>. </p>\n\\end{verbatim}\n\n{An IllegalContextNameException reports that my bundle's context name\ndoes not follow Bundle-SymbolicName syntax. How can I fix the context\nname?~{}}\n\n\\begin{verbatim}\n<p><a href=\"/docs/7-2/appdev/-/knowledge_base/a/resolving-bundle-symbolicname-syntax-issues\">Adjust the <code>Bundle-SymbolicName</code> to adhere to the syntax</a>. </p>\n\\end{verbatim}\n\n{How can I adjust my module's logging?~{}}\n\n\\begin{verbatim}\n<p>See <a href=\"/docs/7-2/appdev/-/knowledge_base/a/adjusting-module-logging\">Adjusting Module Logging</a>. </p>\n\\end{verbatim}\n\n{How can I implement logging in my module or plugin?~{}}\n\n\\begin{verbatim}\n<p><a href=\"/docs/7-2/appdev/-/knowledge_base/a/implementing-logging\">Use Simple Logging Facade for Java (SLF4J) to log messages</a>.</p>\n\\end{verbatim}\n\n{After creating a relational mapping between Service Builder entities,\nmy portlet is using too much memory. What can I do?~{}}\n\n\\begin{verbatim}\n<p><a href=\"/docs/7-2/appdev/-/knowledge_base/a/disabling-cache-for-table-mapper-tables\">Disabling the cache related to the entity mapping lowers memory usage.</a>.</p>\n\\end{verbatim}\n\n\\section{Services and Components}\\label{services-and-components}\n\n{How can I see what's happening in the OSGi container?~{}}\n\n\\begin{verbatim}\n<p><a href=\"/docs/7-2/appdev/-/knowledge_base/a/system-check\">Run a System Check.</a>. </p>\n\\end{verbatim}\n\n{How can I detect unresolved OSGi components?~{}}\n\n\\begin{verbatim}\n<p>module components that use Service Builder use Dependency Manager (DM) and most other module components use Declarative Services (DS). <a href=\"/docs/7-2/appdev/-/knowledge_base/a/detecting-unresolved-osgi-components\">Gogo shell commands and tools help you find and inspect unsatisfied component references for DM and DS components</a>. </p>\n\\end{verbatim}\n\n{What is the safest way to call OSGi services from non-OSGi code?~{}}\n\n\\begin{verbatim}\n<p>See <a href=\"/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker\n\\end{verbatim}\n\n``\\textgreater Calling Non-OSGi Code that Uses OSGi Services.\n\n\\chapter{Adjusting Module Logging}\\label{adjusting-module-logging}\n\nLiferay DXP uses \\href{http://logging.apache.org/log4j/1.2/}{Log4j}\nlogging services. Here are the ways to configure logging for module\nclasses and class hierarchies.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Log Levels} in\n  \\href{/docs/7-2/user/-/knowledge_base/u/server-administration}{Liferay\n  DXP's UI}\n\\item\n  Configure Log4j for multiple modules in a\n  \\texttt{{[}anyModule{]}/src/main/resources/META-INF/module-log4j.xml}\n  file.\n\\item\n  Configure Log4j for a specific module in a\n  \\texttt{{[}Liferay\\ Home{]}/osgi/log4j/{[}symbolicNameOfBundle{]}-log4j-ext.xml}\n  file.\n\\item\n  Configure Log4j for an OSGi fragment host module in a\n  \\texttt{/META-INF/module-log4j-ext.xml} file\n\\end{itemize}\n\nHere's an example Log4j XML configuration:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\">\n    <category name=\"org.foo\">\n        <priority value=\"DEBUG\" />\n    </category>\n</log4j:configuration>\n\\end{verbatim}\n\nUse \\texttt{category} elements to specify each class or class hierarchy\nto log messages for. Set the \\texttt{name} attribute to that class name\nor root package. The example category sets logging for the class\nhierarchy starting at package \\texttt{org.foo}. Log messages at or above\nthe \\texttt{DEBUG} log level are printed for classes in \\texttt{org.foo}\nand classes in packages starting with \\texttt{org.foo}.\n\nSet each category's \\texttt{priority} element to the log\n\\href{http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Level.html}{level}\n(priority) you want.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  ALL\n\\item\n  TRACE\n\\item\n  DEBUG\n\\item\n  INFO\n\\item\n  WARN\n\\item\n  ERROR\n\\item\n  FATAL\n\\item\n  OFF\n\\end{itemize}\n\nThe log messages are printed to Liferay log files in\n\\texttt{{[}Liferay\\_Home{]}/logs}.\n\nYou can see examples of module logging in several Liferay sample\nprojects. For example, the\n\\href{https://github.com/liferay/liferay-blade-samples/tree/master/gradle/apps/action-command-portlet}{action-command-portlet},\n\\href{https://github.com/liferay/liferay-blade-samples/tree/master/gradle/extensions/document-action}{document-action},\nand\n\\href{https://github.com/liferay/liferay-blade-samples/tree/master/gradle/apps/service-builder/jdbc}{service-builder/jdbc}\nsamples (among others) leverage module logging.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If the log level configuration isn't appearing (e.g., you\nset the log level to \\texttt{ERROR} but you're still getting\n\\texttt{WARN} messages), make sure the log configuration file name\nprefix matches the module's symbolic name. If you have bnd installed,\noutput from command \\texttt{bnd\\ print\\ {[}path-to-bundle{]}} includes\nthe module's symbolic name\n(\\href{https://github.com/bndtools/bnd/wiki/Install-bnd-on-the-command-line}{Here}\nare instructions for installing bnd for the command line).\n\n\\noindent\\hrulefill\n\nThat's it for module log configuration. You're all set to print the\ninformation you want.\n\n\\section{Related Topics}\\label{related-topics-27}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/implementing-logging}{Implementing\nLogging}\n\n\\chapter{Identifying Liferay Artifact Versions for\nDependencies}\\label{identifying-liferay-artifact-versions-for-dependencies}\n\nWhen you're developing an application using Liferay APIs or tools---for\nexample, you might create a Service Builder application or use Message\nBus or Asset Framework---you must determine which versions of Liferay\nartifacts (modules, apps, etc.) you application's modules must specify\nas dependencies. To learn how to find Liferay artifacts and configure\ndependencies on them, see\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Configuring\nDependencies}.\n\n\\section{Related Topics}\\label{related-topics-28}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Configuring\nDependencies}\n\n\\chapter{Resolving Bundle-SymbolicName Syntax\nIssues}\\label{resolving-bundle-symbolicname-syntax-issues}\n\nLiferay's OSGi Runtime framework sometimes throws an\n\\texttt{IllegalContextNameException}. Often, this is because an OSGi\nbundle's \\texttt{Bundle-SymbolicName} manifest header has a space in it.\n\nThe \\texttt{Bundle-SymbolicName} uniquely identifies the bundle---along\nwith the \\texttt{Bundle-Version} manifest header---and cannot contain\nspaces. To follow naming best practices, use a reverse-domain name in\nyour \\texttt{Bundle-SymbolicName}. For example, a module with the domain\n\\texttt{troubleshooting.liferay.com} would be reversed to\n\\texttt{com.liferay.troubleshooting.}.\n\nThere are three ways to specify a bundle's \\texttt{Bundle-SymbolicName}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\texttt{Bundle-SymbolicName} header in a bundle's \\texttt{bnd.bnd}\n  file.\n\\item\n  \\texttt{Bundle-SymbolicName} header in a plugin WAR's\n  \\texttt{liferay-plugin-package.properties} file.\n\\item\n  Plugin WAR file name, if the WAR's\n  \\texttt{liferay-plugin-package.properties} has no\n  \\texttt{Bundle-SymbolicName} header.\n\\end{enumerate}\n\nFor plugin WARs, specifying the \\texttt{Bundle-SymbolicName} in the\n\\texttt{liferay-plugin-package.properties} file is preferred.\n\nFor example, if you deploy a plugin WAR that has no\n\\texttt{Bundle-SymbolicName} header in its\n\\texttt{liferay-plugin-package.properties}, the\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{WAB\nGenerator} uses the WAR's name as the WAB's\n\\texttt{Bundle-SymbolicName}. If the WAR's name has a space in it (e.g.,\n\\texttt{space-program-theme\\ v1.war}) an\n\\texttt{IllegalContextNameException} occurs on deployment.\n\n\\begin{verbatim}\norg.apache.catalina.core.ApplicationContext.log The context name 'space-program-theme v1' does not follow Bundle-SymbolicName syntax.\norg.eclipse.equinox.http.servlet.internal.error.IllegalContextNameException: The context name 'space-program-theme v1' does not follow Bundle-SymbolicName syntax.\n\\end{verbatim}\n\nHowever you set your a \\texttt{Bundle-SymbolicName}, refrain from using\nspaces.\n\n\\section{Related Topics}\\label{related-topics-29}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{Using\nthe WAB Generator}\n\n\\chapter{Calling Non-OSGi Code that Uses OSGi\nServices}\\label{calling-non-osgi-code-that-uses-osgi-services}\n\nLiferay DXP's static service utilities (e.g., \\texttt{UserServiceUtil},\n\\texttt{CompanyServiceUtil}, \\texttt{GroupServiceUtil}, etc.) are\nexamples of non-OSGi code that use OSGi services. Service Builder\ngenerates them for backwards compatibility purposes only. If you're\ntempted to call a \\texttt{*ServiceUtil} class or your existing code\ncalls one, access the \\texttt{*Service} directly instead using one these\nalternatives:\n\n\\begin{itemize}\n\\item\n  If your class is a Declarative Services component, use an\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{\\texttt{@Reference}\n  annotation} to access the \\texttt{*Service} class.\n\\item\n  If your class isn't a Declarative Services component, use a\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/service-trackers-for-osgi-services}{\\texttt{ServiceTracker}}\n  to access the \\texttt{*Service} class.\n\\end{itemize}\n\nYou can check the state of Liferay DXP's services in\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{the\nGogo shell}. The \\texttt{scr:list} Gogo shell command shows all\nDeclarative Services components, including inactive ones from\nunsatisfied dependencies. To find unsatisfied dependencies for Service\nBuilder services, use the Dependency Manager's\n\\texttt{dependencymanager:dm\\ wtf} command. Note that these commands\nonly show components that haven't been activated because of unsatisfied\ndependencies. They don't show pure service trackers that are waiting for\na service because of unsatisfied dependencies.\n\n\\section{Related Topics}\\label{related-topics-30}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/detecting-unresolved-osgi-components}{Detecting\nUnresolved OSGi Components}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Felix\nGogo Shell}\n\n\\chapter{Connecting to JNDI Data\nSources}\\label{connecting-to-jndi-data-sources}\n\nConnecting to an application server's JNDI data sources from Liferay\nDXP's OSGi environment is almost the same as connecting to them from the\nJava EE environment. In an OSGi environment, the only difference is that\nyou must use Liferay DXP's class loader to load the application server's\nJNDI classes. The following code demonstrates this.\n\n\\begin{verbatim}\nThread thread = Thread.currentThread();\n\n// Get the thread's class loader. You'll reinstate it after using\n// the data source you look up using JNDI\n\nClassLoader origLoader = thread.getContextClassLoader();\n\n// Set Liferay's class loader on the thread\n\nthread.setContextClassLoader(PortalClassLoaderUtil.getClassLoader());\n\ntry {\n\n        // Look up the data source and connect to it\n\n        InitialContext ctx = new InitialContext();\n        DataSource datasource = (DataSource)\n            ctx.lookup(\"java:comp/env/jdbc/TestDB\");\n\n        Connection connection = datasource.getConnection();\n        Statement statement = connection.createStatement();\n\n        // Execute SQL statements here ...\n\n        connection.close();\n}\ncatch (NamingException ne) {\n\n    ne.printStackTrace();\n}\ncatch (SQLException sqle) {\n\n    sqle.printStackTrace();\n}\nfinally {\n       // Switch back to the original context class loader\n\n       thread.setContextClassLoader(origLoader);\n}\n\\end{verbatim}\n\nThe example code sets Liferay DXP's classloader on the thread to access\nthe JNDI API.\n\n\\begin{verbatim}\nthread.setContextClassLoader(PortalClassLoaderUtil.getClassLoader());\n\\end{verbatim}\n\nIt uses JNDI to look up the data source.\n\n\\begin{verbatim}\nInitialContext ctx = new InitialContext();\nDataSource datasource = (DataSource)\n    ctx.lookup(\"java:comp/env/jdbc/TestDB\"); \n\\end{verbatim}\n\nAfter working with the data source, the code reinstates the thread's\noriginal classloader.\n\n\\begin{verbatim}\nthread.setContextClassLoader(origLoader);\n\\end{verbatim}\n\nHere are the class imports for the example code:\n\n\\begin{verbatim}\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport javax.naming.InitialContext;\nimport javax.naming.NamingException;\nimport javax.sql.DataSource;\nimport com.liferay.portal.kernel.util.PortalClassLoaderUtil;\n\\end{verbatim}\n\nYour applications can use similar code to access a data source. Make\nsure to substitute \\texttt{jdbc/TestDB} with your data source name.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: An OSGi bundle's attempt to connect to a JNDI data source\nwithout using Liferay DXP's classloader results in a\n\\texttt{java.lang.ClassNotFoundException}. For example, here's an\nexception from attempting to use Apache Tomcat's JNDI API without using\nLiferay DXP's classloader:\n\n\\begin{verbatim}\n javax.naming.NoInitialContextException: Cannot instantiate class:\n org.apache.naming.java.javaURLContextFactory [Root exception is\n java.lang.ClassNotFoundException:\n org.apache.naming.java.javaURLContextFactory]\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nAn easier way to work with databases is to connect to them using Service\nBuilder.\n\n\\chapter{Detecting Unresolved OSGi\nComponents}\\label{detecting-unresolved-osgi-components}\n\nLiferay DXP includes\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nshell} commands that come in handy when trying to diagnose a problem due\nto an unresolved OSGi component. The specific tools to use depend on the\ncomponent framework of the unresolved component. Most Liferay DXP\ncomponents are developed using Declarative Services (DS), also known as\nSCR (Service Component Runtime). An exception to this is Liferay DXP's\nService Builder services, which are Dependency Manager (DM) components.\nBoth\n\\href{http://felix.apache.org/documentation/subprojects/apache-felix-service-component-runtime.html}{Declarative\nServices} and\n\\href{http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html}{Dependency\nManager} are Apache Felix projects.\n\nThe unresolved component troubleshooting instructions are divided into\nthese sections:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[declarative-services-components]{Declarative Services\n  Components}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\hyperref[declarative-services-unsatisfied-component-scanner]{Declarative\n    Services Unsatisfied Component Scanner}\n  \\item\n    \\hyperref[dsunsatisfied-command]{ds:unsatisfied Command}\n  \\end{itemize}\n\\item\n  \\hyperref[service-builder-components]{Service Builder Components}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\hyperref[unavailable-component-scanner]{Unavailable Component\n    Scanner}\n  \\item\n    \\hyperref[dm-na-command]{dm na Command}\n  \\item\n    \\hyperref[serviceproxyfactory]{\\texttt{ServiceProxyFactory}}\n  \\end{itemize}\n\\end{itemize}\n\n\\section{Declarative Services\nComponents}\\label{declarative-services-components}\n\nStart with DS, since most Liferay DXP components, apart from Service\nBuilder components, are DS components. Suppose one of your bundle's\ncomponents has an unsatisfied service reference. How can you detect\nthis? Two ways:\n\n\\begin{itemize}\n\\item\n  Enable a\n  \\hyperref[declarative-services-unsatisfied-component-scanner]{Declarative\n  Services Unsatisfied Component Scanner} to report unsatisfied\n  references automatically or\n\\item\n  Use the \\hyperref[dsunsatisfied-command]{Gogo shell command\n  \\texttt{ds:unsatisfied}} to check for them manually.\n\\end{itemize}\n\n\\section{Declarative Services Unsatisfied Component\nScanner}\\label{declarative-services-unsatisfied-component-scanner}\n\nHere's how to enable the unsatisfied component scanner:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a file\n  \\texttt{com.liferay.portal.osgi.debug.declarative.service.internal.configuration.UnsatisfiedComponentScannerConfiguration.cfg}.\n\\item\n  Add the following content:\n\n\\begin{verbatim}\nunsatisfiedComponentScanningInterval=5\n\\end{verbatim}\n\\item\n  Copy the file into \\texttt{{[}LIFERAY\\_HOME{]}/osgi/configs}.\n\\end{enumerate}\n\nThe scanner detects and logs unsatisfied service component references.\nThe log message describes the bundle, the referencing DS component\nclass, and the referenced component.\n\nHere's an example scanner message:\n\n\\begin{verbatim}\n11:18:28,881 WARN  [Declarative Service Unsatisfied Component Scanner][UnsatisfiedComponentScanner:91]\nBundle {id: 631, name: com.liferay.blogs.web, version: 2.0.0}\n    Declarative Service {id: 3333, name: com.liferay.blogs.web.internal.portlet.action.EditEntryMVCRenderCommand, unsatisfied references:\n        {name: ItemSelectorHelper, target: null}\n    }\n\\end{verbatim}\n\nThe message above warns that the \\texttt{com.liferay.blogs.web} bundle's\nDS component\n\\texttt{com.liferay.blogs.web.internal.portlet.action.EditEntryMVCRenderCommand}\nhas an unsatisfied reference to a component of type\n\\texttt{ItemSelectorHelper}. The referencing component's ID (SCR ID) is\n\\texttt{3333} and its bundle ID is \\texttt{631}.\n\n\\section{ds:unsatisfied Command}\\label{dsunsatisfied-command}\n\nAnother way to detect unsatisfied component references is to invoke the\nGogo shell command \\texttt{ds:unsatisfied}.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{ds:unsatisfied} shows all unsatisfied DS components\n\\item\n  \\texttt{ds:unsatisfied\\ {[}BUNDLE\\_ID{]}} shows the bundle's\n  unsatisfied DS components\n\\end{itemize}\n\nTo view more detailed information about the unsatisfied DS component,\npass the component's ID to the command\n\\texttt{scr:info\\ {[}component\\ ID{]}}. For example, the following\ncommand does this for a component with ID \\texttt{1701}:\n\n\\begin{verbatim}\ng! scr:info 1701\n*** Bundle: org.foo.bar.command (507)\nComponent Description:\n    Name: org.foo.bar.command\n    Implementation Class: org.foo.bar.command.FooBarCommand\n    Default State: enabled\n    Activation: delayed\n    Configuration Policy: optional\n    Activate Method: activate\n    Deactivate Method: deactivate\n    Modified Method: -\n    Configuration Pid: [org.foo.bar.command]\n    Services:\n        org.foo.bar.command.DuckQuackCommand\n    Service Scope: singleton\n    Reference: Duck\n        Interface Name: org.foo.bar.api.Foo\n        Cardinality: 1..1\n        Policy: static\n        Policy option: reluctant\n        Reference Scope: bundle\n    Component Description Properties:\n        osgi.command.function = foo\n        osgi.command.scope = bar\n    Component Configuration:\n        ComponentId: 1701\n        State: unsatisfied reference\n        UnsatisfiedReference: Foo\n        Target: null\n        (no target services)\n        Component Configuration Properties:\n        component.id = 1701\n        component.name = org.foo.bar.command\n        osgi.command.function = foo\n        osgi.command.scope = bar\n\\end{verbatim}\n\nIn the \\texttt{Component\\ Configuration} section,\n\\texttt{UnsatisfiedReference} lists the unsatisfied reference's type.\nThis bundle's component isn't working because it's missing a\n\\texttt{Foo} service. Now you can focus on why \\texttt{Foo} is\nunavailable. The solution may be as simple as starting or deploying a\nbundle that provides the \\texttt{Foo} service.\n\n\\section{Service Builder Components}\\label{service-builder-components}\n\nService Builder modules are implemented using Spring. Liferay DXP uses\n\\href{http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html}{the\nApache Felix Dependency Manager} to manage Service Builder module OSGi\ncomponents via the\n\\href{https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.portal.spring.extender/}{Portal\nSpring Extender} module.\n\nWhen developing a Liferay Service Builder application, you might\nsometimes have an unresolved Spring-related OSGi component. This can\noccur if you update your application's database schema but forget to\ntrigger an upgrade.\n\nThese features detect unresolved Service Builder related components.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[unavailable-component-scanner]{Unavailable Component\n  Scanner}\n\\item\n  \\hyperref[dm-na-command]{dm na Command}\n\\item\n  \\hyperref[serviceproxyfactory]{ServiceProxyFactory}\n\\end{itemize}\n\n\\section{Unavailable Component\nScanner}\\label{unavailable-component-scanner}\n\nThe\n\\href{https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.portal.osgi.debug.spring.extender/}{OSGi\nDebug Spring Extender} module's Unavailable Component Scanner reports\nmissing components in modules that use Service Builder. Here's how to\nenable the scanner:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create the configuration file\n  \\texttt{com.liferay.portal.osgi.debug.spring.extender.internal.configuration.UnavailableComponentScannerConfiguration.cfg}.\n\\item\n  In the configuration file, set the time interval (in seconds) between\n  scans:\n\n  \\texttt{unavailableComponentScanningInterval=5}\n\\item\n  Copy the file into \\texttt{{[}LIFERAY\\_HOME{]}/osgi/configs}.\n\\end{enumerate}\n\nThe scanner reports Spring extender dependency manager component status\non the set interval. If all components are registered, the scanner sends\na confirmation message.\n\n\\begin{verbatim}\n11:10:53,817 INFO  [Spring Extender Unavailable Component Scanner][UnavailableComponentScanner:166] All Spring extender dependency manager components are registered\n\\end{verbatim}\n\nIf a component is unavailable, it warns you:\n\n\\begin{verbatim}\n11:13:08,851 WARN  [Spring Extender Unavailable Component Scanner][UnavailableComponentScanner:173] Found unavailable component in bundle com.liferay.screens.service_1.0.28 [516].\nComponent ComponentImpl[null com.liferay.portal.spring.extender.internal.context.ModuleApplicationContextRegistrator@1541eee] is unavailable due to missing required dependencies: ServiceDependency[interface com.liferay.blogs.service.BlogsEntryService null].\n\\end{verbatim}\n\nComponent unavailability, such as what's reported above, can occur when\nDS components and Service Builder components are published and used in\nthe same module. Use separate modules to publish DS components and\nService Builder components.\n\n\\section{dm na Command}\\label{dm-na-command}\n\nDependency Manager's\n\\href{http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager/tutorials/leveraging-the-shell.html}{Gogo\nshell command \\texttt{dm}} lists all Service Builder components, their\nrequired services, and whether each required service is available.\n\nTo list unresolved components only execute this Gogo shell command:\n\n\\begin{verbatim}\ndm na\n\\end{verbatim}\n\nThe \\texttt{na} option stands for ``not available.''\n\n\\section{ServiceProxyFactory}\\label{serviceproxyfactory}\n\nLiferay DXP's logs report unresolved Service Builder components too. For\nexample, Liferay DXP logs an error when a Service Proxy Factory can't\ncreate a new instance of a Service Builder based entity because a\nservice component is unresolved.\n\nThe following code demonstrates using a\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ServiceProxyFactory.html}{\\texttt{ServiceProxyFactory}\nclass} to create a new entity instance:\n\n\\begin{verbatim}\nprivate static volatile MessageBus _messageBus =\n    ServiceProxyFactory.newServiceTrackedInstance(\n        MessageBus.class, MessageBusUtil.class, \"_messageBus\", true);\n\\end{verbatim}\n\nThis message alerts you to the unavailable service:\n\n\\begin{verbatim}\n11:07:35,139 ERROR [localhost-startStop-1][ServiceProxyFactory:265] Service \"com.liferay.portal.kernel.messaging.sender.SingleDestinationMessageSenderFactory\" is unavailable in 60000 milliseconds while setting field \"_singleDestinationMessageSenderFactory\" for class \"com.liferay.portal.kernel.messaging.sender.SingleDestinationMessageSenderFactoryUtil\", will retry...\n\\end{verbatim}\n\nBased on the message above, there's no bundle providing the service\n\\texttt{com.liferay.portal.kernel.messaging.sender.SingleDestinationMessageSenderFactory}.\n\nNow you can detect unresolved components, DS and DM components,\nautomatically using scanners, manually using Gogo shell commands, and\nprogrammatically using a \\texttt{ServiceProxyFactory}.\n\n\\section{Related Topics}\\label{related-topics-31}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/system-check}{System Check}\n\n\\chapter{Disabling Cache for Table Mapper\nTables}\\label{disabling-cache-for-table-mapper-tables}\n\nService Builder creates relational mappings between entities. It uses\nmapping tables to associate the entities. In your \\texttt{service.xml}\nfile, both entities have a \\texttt{mapping-table} column attribute of\nthe format \\texttt{mapping-table=\"table1\\_table2\"}. For example, a\n\\texttt{service.xml} that maps \\texttt{AssetEntry}s to\n\\texttt{AssetCategory}s has an \\texttt{AssetCategory} entity with this\ncolumn:\n\n\\begin{verbatim}\n<column entity=\"AssetEntry\" \nmapping-table=\"AssetEntries_AssetCategories\" \nname=\"entries\" type=\"Collection\" />\n\\end{verbatim}\n\nand an \\texttt{AssetEntry} entity element with this column:\n\n\\begin{verbatim}\n<column entity=\"AssetCategory\" \nmapping-table=\"AssetEntries_AssetCategories\" \nname=\"categories\" type=\"Collection\" />\n\\end{verbatim}\n\nBy default, a table mapper cache is associated with each mapping table.\nThe cache optimizes object retrieval. In some cases, however, it's best\nto disable a table mapper cache.\n\n\\section{Why would I want to disable cache on a table\nmapper?}\\label{why-would-i-want-to-disable-cache-on-a-table-mapper}\n\nSuper-large entity tables can result in a memory-hogging table mapper\ncache. For this reason, consider disabling cache on a table mapper.\n\nThe\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Table\\%20Mapper}{\\texttt{table.mapper.cacheless.mapping.table.names}\nPortal property} disables cache for table mappers associated with the\nspecified mapping tables. Here's the default property setting:\n\n\\begin{verbatim}\n##\n## Table Mapper\n##\n\n  #\n  # Set a list of comma delimited mapping table names that will not be using\n  # cache in their table mappers.\n  #\n  table.mapper.cacheless.mapping.table.names=\\\n    Users_Groups,\\\n    Users_Orgs,\\\n    Users_Roles,\\\n    Users_Teams,\\\n    Users_UserGroups\n\\end{verbatim}\n\nAll of the disabled caches above pertain to the \\texttt{User} object\nbecause the table mappers tend to be much too large to have a useful\ncache---each \\texttt{User} can have several entries in each related\ntable.\n\nPotential race conditions retrieving objects from the cache is another\nreason to disable a table mapper.\n\nFor example,\n\\href{https://issues.liferay.com/browse/LPS-84374}{LPS-84374} describes\na race condition in which a custom entity's table mapper cache can be\ncleared while in use, causing transactional rollbacks. Publishing\n\\texttt{AssetEntry}s clears all associated table mapper caches. If\nthey're published at the same time getter methods are retrieving objects\nfrom the \\texttt{AssetEntries\\_AssetCategories} mapping table,\ntransaction rollbacks occur.\n\n\\section{Disabling a Table Mapper\nCache}\\label{disabling-a-table-mapper-cache}\n\nAdding a mapping table name to the\n\\texttt{table.mapper.cacheless.mapping.table.names} Portal property\ndisables the associated table mapper cache.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your\n  \\href{/docs/7-1/deploy/-/knowledge_base/d/liferay-home}{\\texttt{{[}Liferay\\_Home{]}/portal-ext.properties}\n  file}, add the current\n  \\texttt{table.mapper.cacheless.mapping.table.names} property setting.\n  The setting is in your Liferay DXP installation's\n  \\texttt{portal-impl.jar/portal.properties} file.\n\\item\n  Append your mapping table name to the list. For example, to disable\n  the cache associated with a mapping table named\n  \\texttt{AssetEntries\\_AssetCategories}, add that name to the list.\n\n\\begin{verbatim}\ntable.mapper.cacheless.mapping.table.names=\\\n    Users_Groups,\\\n    Users_Orgs,\\\n    Users_Roles,\\\n    Users_Teams,\\\n    Users_UserGroups,\\\n    AssetEntries_AssetCategories\n\\end{verbatim}\n\\item\n  Restart the Liferay DXP instance to delete the table mapper cache.\n\\end{enumerate}\n\nYou've disabled an unwanted table mapper cache.\n\n\\chapter{Implementing Logging}\\label{implementing-logging}\n\n7.0 uses the Log4j logging framework, but it may be replaced in the\nfuture. It's a best practice to use \\href{https://www.slf4j.org/}{Simple\nLogging Facade for Java (SLF4J)} to log messages in your modules and\ntraditional plugins. SLF4J is already integrated into Liferay DXP, so\nyou can focus on logging messages.\n\nHere's how to use SLF4J to log messages in a class:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a private static SLF4J\n  \\href{https://www.slf4j.org/apidocs/org/slf4j/Logger.html}{\\texttt{Logger}\n  field}.\n\n\\begin{verbatim}\nprivate static Logger _logger;\n\\end{verbatim}\n\\item\n  Instantiate the logger.\n\n\\begin{verbatim}\n_logger = LoggerFactory.getLogger(this.getClass().getName());\n\\end{verbatim}\n\\item\n  Throughout your class, log messages where noteworthy things happen.\n\n  For example,\n\n\\begin{verbatim}\n_logger.debug(\"...\");\n_logger.warn(\"...\");\n_logger.error(\"...\");\n...\n\\end{verbatim}\n\n  Use \\texttt{Logger} methods appropriate for each message:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{trace}: Provides more information than debug. This is the\n    most verbose message level.\n  \\item\n    \\texttt{debug}: Event and application information helpful for\n    debugging.\n  \\item\n    \\texttt{info}: High level events.\n  \\item\n    \\texttt{warn}: Information that might, but does not necessarily,\n    indicate a problem.\n  \\item\n    \\texttt{error}: Normal errors. This is the least verbose message\n    level.\n  \\end{itemize}\n\\end{enumerate}\n\nLog verbosity should correlate with the log level set for the class or\npackage. Make sure you provide additional information at log levels\nexpected to be more verbose, such as \\texttt{info} and \\texttt{debug}.\n\nYou're all set to add logging to your modules and traditional plugins.\n\n\\section{Related Topics}\\label{related-topics-32}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/adjusting-module-logging}{Adjusting\nModule Logging}\n\n\\chapter{Declaring Optional Import Package\nRequirements}\\label{declaring-optional-import-package-requirements}\n\nWhen developing modules, you can declare \\emph{optional} dependencies.\nAn optional dependency is one your module can use if available, but can\nstill function without it.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Try to avoid optional dependencies. The best module\ndesigns rely on normal dependencies. If an optional dependency seems\ndesirable, your module may be trying to provide more than one distinct\ntype of functionality. In such a situation, it's best to split it into\nmultiple modules that provide smaller, more focused functionality.\n\n\\noindent\\hrulefill\n\nIf you decide that your module requires an optional dependency, follow\nthese steps to add it:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your module's \\texttt{bnd.bnd} file, declare the package your\n  module optionally depends on:\n\n\\begin{verbatim}\nImport-Package: com.liferay.demo.foo;resolution:=\"optional\"\n\\end{verbatim}\n\n  Note that you can use either an optional or dynamic import. The\n  differences are explained\n  \\href{https://osgi.org/specification/osgi.core/7.0.0/framework.module.html\\#i2548181}{here}.\n\\item\n  Create a component to use the optional package:\n\n\\begin{verbatim}\nimport com.liferay.demo.foo.Foo; // A class from the optional package\n\n@Component(\n    enabled = false // instruct declarative services to ignore this component by default\n)\npublic class OptionalPackageConsumer implements Foo {...}\n\\end{verbatim}\n\\item\n  Create a second component to be a controller for the first. The second\n  component checks the class loader for the optional class on the\n  classpath. If it's not there, this means you must catch any\n  \\texttt{ClassNotFoundException}. For example:\n\n\\begin{verbatim}\n@Component\npublic class OptionalPackageConsumerStarter {\n    @Activate\n    void activate(ComponentContext componentContext) {\n        try {\n            Class.forName(com.liferay.demo.foo.Foo.class.getName());\n\n            componentContext.enableComponent(OptionalPackageConsumer.class.getName());\n        }\n        catch (Throwable t) {\n            _log.warn(\"Could not find {}\", t.getMessage()); // Could use _log.info instead\n        }\n    }\n}\n\\end{verbatim}\n\\end{enumerate}\n\nIf the class loader check in the controller component is successful, the\nclient component is enabled. This check is automatically performed\nwhenever there are any wiring changes to the module containing these\ncomponents (Declarative Services components are always restarted when\nthere are wiring changes).\n\nIf you install the module when the optional dependency is missing from\nLiferay DXP's OSGi runtime, your controller component catches a\n\\texttt{ClassNotFoundException} and logs a warning or info message (or\ntakes whatever other action you implement to handle this case). If you\ninstall the optional dependency, refreshing your module triggers the\nOSGi bundle lifecycle events that trigger your controller's\n\\texttt{activate} method and the check for the optional dependency.\nSince the dependency exists, your client component uses it.\n\nNote that you can refresh a bundle from\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nshell} with this command:\n\n\\begin{verbatim}\nequinox:refresh [bundle ID] \n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-33}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Configuring\nDependencies}\n\n\\chapter{Resolving Bundle\nRequirements}\\label{resolving-bundle-requirements}\n\nIf one of your bundles needs a package that is not exported by any other\nbundle in the Liferay OSGi runtime, you get a bundle exception. Here's\nan example exception:\n\n\\begin{verbatim}\n! could not resolve the bundles: [com.liferay.messaging.client.command-1.0.0.201707261701 org.osgi.framework.BundleException: Could not resolve module: com.liferay.messaging.client.command [1]\nUnresolved requirement: Import-Package: com.liferay.messaging.client.api; version=\"[1.0.0,2.0.0)\"\n-> Export-Package: com.liferay.messaging.client.api; bundle-symbolic-name=\"com.liferay.messaging.client.provider\"; bundle-version=\"1.0.0.201707261701\"; version=\"1.0.0\"; uses:=\"org.osgi.framework\"\ncom.liferay.messaging.client.provider [2]\nUnresolved requirement: Import-Package: com.liferay.messaging; version=\"[1.0.0,2.0.0)\"\n-> Export-Package: com.liferay.messaging; bundle-symbolic-name=\"com.liferay.messaging.api\"; bundle-version=\"1.0.0\"; version=\"1.0.0\"; uses:=\"com.liferay.petra.concurrent\"\ncom.liferay.messaging.api [12]\nUnresolved requirement: Import-Package: com.liferay.petra.io; version=\"[1.0.0,2.0.0)\"\n-> Export-Package: com.liferay.petra.io; bundle-symbolic-name=\"com.liferay.petra.io\"; bundle-version=\"1.0.0\"; version=\"1.0.0\"\ncom.liferay.petra.io [16]\nUnresolved requirement: Require-Capability osgi.extender; filter:=\"(osgi.extender=osgi.serviceloader.processor)\"\n\\end{verbatim}\n\nThe first line states \\emph{could not resolve the bundles}. What follows\nis a string of requirements that Liferay's OSGi Runtime could not\nresolve.\n\nThe bundle exception message follows this general pattern:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Module A has an unresolved requirement (package or capability)\n  \\texttt{aaa.bbb}.\n\\item\n  Module B provides \\texttt{aaa.bbb} but has an unresolved requirement\n  \\texttt{ccc.ddd}.\n\\item\n  Module C provides \\texttt{ccc.ddd} but has an unresolved requirement\n  \\texttt{eee.fff}.\n\\item\n  etc.\n\\item\n  Module Z provides \\texttt{www.xxx} but has an unresolved requirement\n  \\texttt{yyy.zzz}.\n\\end{itemize}\n\nThe pattern stops at the final unsatisfied requirement. The last\nmodule's dependencies are key to resolving the bundle exception. There\nare two possible causes:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  A dependency that satisfies the final requirement might be missing\n  from the build file.\n\\item\n  A dependency that satisfies the final requirement might not be\n  deployed.\n\\end{enumerate}\n\nBoth cases require deploying a bundle that provides the missing\nrequirement.\n\nThe example bundle exception concludes that module\n\\texttt{com.liferay.petra.io} requires capability\n\\texttt{osgi.extender;\\ filter:=\"(osgi.extender=osgi.serviceloader.processor)\"}.\nTo resolve the requirement, make sure all of\n\\texttt{com.liferay.petra.io}'s dependencies are deployed.\n\nThe \\texttt{com.liferay.petra.io} module's \\texttt{build.gradle} file\nlists its dependencies:\n\n\\begin{verbatim}\ndependencies {\n    provided group: \"com.liferay\", name: \"com.liferay.petra.concurrent\", version: \"1.0.0\"\n    provided group: \"com.liferay\", name: \"com.liferay.petra.memory\", version: \"1.0.0\"\n    provided group: \"org.apache.aries.spifly\", name: \"org.apache.aries.spifly.dynamic.bundle\", version: \"1.0.8\"\n    provided group: \"org.slf4j\", name: \"slf4j-api\", version: \"1.7.2\"\n    testCompile group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"default\"\n}\n\\end{verbatim}\n\nThen use\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Felix\nGogo Shell's \\texttt{lb\\ command}} to verify the dependencies are in\nLiferay's OSGi Runtime:\n\n\\begin{verbatim}\nlb\nSTART LEVEL 1\n   ID|State      |Level|Name\n    0|Active     |    0|OSGi System Bundle (3.10.100.v20150529-1857)|3.10.100.v20150529-1857\n    1|Active     |    1|com.liferay.messaging.client.command (1.0.0.201707261923)|1.0.0.201707261923\n    2|Active     |    1|com.liferay.messaging.client.provider (1.0.0.201707261927)|1.0.0.201707261927\n    3|Active     |    1|Apache Felix Configuration Admin Service (1.8.8)|1.8.8\n    4|Active     |    1|Apache Felix Log Service (1.0.1)|1.0.1\n    5|Active     |    1|Apache Felix Declarative Services (2.0.2)|2.0.2\n    6|Active     |    1|Meta Type (1.4.100.v20150408-1437)|1.4.100.v20150408-1437\n    7|Active     |    1|org.osgi:org.osgi.service.metatype (1.3.0.201505202024)|1.3.0.201505202024\n    8|Active     |    1|Apache Felix Gogo Command (0.16.0)|0.16.0\n    9|Active     |    1|Apache Felix Gogo Runtime (0.16.2)|0.16.2\n   10|Active     |    1|Apache Felix Gogo Runtime (1.0.0)|1.0.0\n...\n\\end{verbatim}\n\nThe dependency module \\texttt{org.apache.aries.spifly.dynamic.bundle} is\nmissing from the runtime bundle list. The\n\\texttt{org.apache.aries.spifly.dynamic.bundle} module's\n\\texttt{MANIFEST.MF} file shows it provides the requirement capability\n\\texttt{osgi.extender;\\ filter:=\"(osgi.extender=osgi.serviceloader.processor)\"}:\n\n\\begin{verbatim}\nProvide-Capability: osgi.extender;osgi.extender=\"osgi.serviceloader.regi\n strar\";version:Version=\"1.0\",osgi.extender;osgi.extender=\"osgi.servicel\n oader.processor\";version:Version=\"1.0\"\n\\end{verbatim}\n\nThis capability\n\\texttt{osgi.extender;\\ filter:=\"(osgi.extender=osgi.serviceloader.processor)\"}\nis the unresolved requirement we identified earlier. Deploying this\nmissing bundle \\texttt{org.apache.aries.spifly.dynamic.bundle} satisfies\nthe example module's requirement and allows the module to resolve and\ninstall.\n\nYou can resolve your bundle exceptions by following steps similar to\nthese.\n\n\\noindent\\hrulefill\n\nNote: Bndtools's \\emph{Resolve} button can resolve bundle dependencies\nautomatically. You specify the bundles your application requires and\nBndtools adds transitive dependencies from your configured artifact\nrepository.\n\n\\noindent\\hrulefill\n\n\\section{Related Topics}\\label{related-topics-34}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Configuring\nDependencies}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{Adding\nThird Party Libraries to a Module}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Felix\nGogo Shell}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/finding-artifacts}{Finding\nArtifacts}\n\n\\chapter{Resolving ClassNotFoundException and NoClassDefFoundError in\nOSGi\nBundles}\\label{resolving-classnotfoundexception-and-noclassdeffounderror-in-osgi-bundles}\n\n\\texttt{ClassNotFoundException} and \\texttt{NoClassDefFoundError} are\ncommon, well known exceptions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{ClassNotFoundException} is thrown when looking up a class that\n  isn't on the classpath or using an invalid name to look up a class\n  that isn't on the runtime classpath.\n\\item\n  \\texttt{NoClassDefFoundError} occurs when a compiled class references\n  another class that isn't on the runtime classpath.\n\\end{itemize}\n\nIn OSGi environments, however, there are additional cases where a\n\\texttt{ClassNotFoundException} or \\texttt{NoClassDefFoundError} can\noccur:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  The missing class belongs to a module dependency that's an OSGi\n  module.\n\\item\n  The missing class belongs to a module dependency that's \\emph{not} an\n  OSGi module.\n\\item\n  The missing class belongs to a global library, either at the Liferay\n  DXP web app scope or the application server scope.\n\\item\n  The missing class belongs to a Java runtime package.\n\\end{enumerate}\n\nThis tutorial explains how to handle each case.\n\n\\section{Case 1: The Missing Class Belongs to an OSGi\nModule}\\label{case-1-the-missing-class-belongs-to-an-osgi-module}\n\nIn this case, there are two possible causes:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{The module doesn't import the class's package:} For a module\n  (or WAB) to consume another module's exported class, the consuming\n  module must import the exported package that contains the class. To do\n  this, you add an \\texttt{Import-Package} header in the consuming\n  module's \\texttt{bnd.bnd} file. If the consuming module tries to\n  access the class without importing the package, a\n  \\texttt{ClassNotFoundException} or \\texttt{NoClassDefFoundError}\n  occurs.\n\n  Check the package name and make sure the consuming module imports the\n  right package. If the import is correct but you still get the\n  exception or error, the class might no longer exist in the package.\n\\item\n  \\textbf{The class no longer exists in the imported package:} Modules\n  are changed frequently in OSGi runtime environments. If you reference\n  another module's class that its developer removed, a\n  \\texttt{NoClassDefFoundError} or \\texttt{ClassNotFoundException}\n  occurs. \\href{http://semver.org}{Semantic Versioning} guards against\n  this scenario: removing a class from an exported package constitutes a\n  new major version for that package. Neglecting to increment the\n  package's major version breaks dependent modules.\n\n  For example, say a module that consumes the class \\texttt{com.foo.Bar}\n  specifies the package import\n  \\texttt{com.foo;version={[}1.0.0,\\ 2.0.0)}. The module uses\n  \\texttt{com.foo} versions from \\texttt{1.0.0} up to (but not\n  including) \\texttt{2.0.0}. The first part of the version number (the\n  \\texttt{1} in \\texttt{1.0.0}) represents the \\emph{major} version. The\n  consuming module doesn't expect any \\emph{major} breaking changes,\n  like a class removal. Removing \\texttt{com.foo.Bar} from\n  \\texttt{com.foo} without incrementing the package to a new major\n  version (e.g., \\texttt{2.0.0}) causes a\n  \\texttt{ClassNotFoundException} or \\texttt{NoClassDefFoundError} when\n  other modules look up or reference that class.\n\n  You have limited options when the class no longer exists in the\n  package:\n\n  \\begin{itemize}\n  \\item\n    Adapt to the new API. To learn how to do this, read the\n    package's/module's Javadoc, release notes, and/or formal\n    documentation. You can also ask the author or search forums.\n  \\item\n    Revert to the module version you used previously. Deployed module\n    versions reside in \\texttt{{[}Liferay\\_Home{]}/osgi/}. For details,\n    see\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation}{Backing\n    up Liferay Installations}.\n  \\end{itemize}\n\n  Do what you think is best to get your module working properly.\n\\end{enumerate}\n\nNow you know how to resolve common situations involving\n\\texttt{ClassNotFoundException} or \\texttt{NoClassDefFoundError}. For\nadditional information on \\texttt{NoClassDefFoundError}, see OSGi\nEnroute's article\n\\href{http://enroute.osgi.org/faq/class-not-found-exception.html}{What\nis NoClassDefFoundError?}.\n\n\\section{Case 2: The Missing Class Doesn't Belong to an OSGi\nModule}\\label{case-2-the-missing-class-doesnt-belong-to-an-osgi-module}\n\nIn this case, you have two options:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Convert the dependency into an OSGi module so it can export the\n  missing class. Converting a non-OSGi \\texttt{JAR} file dependency into\n  an OSGi module that you can deploy alongside your application is the\n  ideal solution, so it should be your first choice.\n\\item\n  Embed the dependency in your module by embedding the dependency\n  \\texttt{JAR} file's packages as private packages in your module. If\n  you want to embed a non-OSGi \\texttt{JAR} file in your application,\n  see\n  \\href{https://portal.liferay.dev/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{Resolving\n  Third Party Library Package Dependencies}.\n\\end{enumerate}\n\n\\section{Case 3: The Missing Class Belongs to a Global\nLibrary}\\label{case-3-the-missing-class-belongs-to-a-global-library}\n\nIn this case, you can configure Liferay DXP so the OSGi system module\nexports the missing class's package. Then your module can import it. You\nshould \\textbf{NOT}, however, undertake this lightly. If Liferay\nintended to make a global library available for use by developers, the\nsystem module would already export this library! Proceed only if you\nhave no other solution, and watch out for unintended consequences. There\nare two ways to export the package:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your \\texttt{portal-ext.properties} file, use the property\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Module\\%20Framework}{\\texttt{module.framework.system.packages.extra}}\n  to specify the packages to export. Preserve the property's current\n  list.\n\\item\n  If the package you need is from a Liferay DXP JAR, you might be able\n  to add the module to the list of exported packages in\n  \\texttt{{[}LIFERAY\\_HOME{]}/osgi/core/com.liferay.portal.bootstrap.jar}'s\n  \\texttt{META-INF/system.packages.extra.bnd} file. Try this option only\n  if the first option doesn't work.\n\\end{enumerate}\n\nIf the package you need is from a Liferay DXP module, (i.e., it's\n\\textbf{NOT} from a global library), you can add the package to that\nmodule's \\texttt{bnd.bnd} exports. You should \\textbf{NOT}, however,\nundertake this lightly. The package would already be exported if Liferay\nintended for it to be available.\n\n\\section{Case 4: The Missing Class Belongs to a Java Runtime\nPackage}\\label{case-4-the-missing-class-belongs-to-a-java-runtime-package}\n\n\\texttt{rt.jar} (the JRE library) has non-public packages. If your\nmodule imports one of them, configure Liferay DXP's system bundle to\nexport the package to the module framework.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the current\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Module\\%20Framework}{\\texttt{module.framework.system.packages.extra}\n  property setting} to a\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}\n  file}. Your server's current setting is in the Liferay DXP web\n  application's \\texttt{/WEB-INF/lib/portal-impl.jar/portal.properties}\n  file.\n\\item\n  In your \\texttt{portal-ext.properties} file, append the required Java\n  runtime package to the end of the\n  \\texttt{module.framework.system.packages.extra} property's package\n  list.\n\\item\n  Restart your server.\n\\end{enumerate}\n\nYour module should resolve and install.\n\n\\section{Related Topics}\\label{related-topics-35}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation}{Backing\nup Liferay Installations}\n\n\\href{https://portal.liferay.dev/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{Resolving\nThird Party Library Package Dependencies}\n\n\\chapter{System Check}\\label{system-check}\n\nDuring development, all kinds of strange things can happen in the OSGi\ncontainer. Liferay's \\texttt{system:check}\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nshell} command can help you see what's happening. You can enable it to\nrun as the last Portal startup step and you can execute it any time in\nGogo shell.\n\n\\texttt{system:check} aggregates these commands:\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/detecting-unresolved-osgi-components\\#dsunsatisfied-command}{\\texttt{ds:unsatisfied}}:\n  Reports unsatisfied Declarative Service components.\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/detecting-unresolved-osgi-components\\#dm-na-command}{\\texttt{dm\\ na}}:\n  Reports unsatisfied Dependency Manager service components, including\n  Service Builder services.\n\\end{itemize}\n\nSystem checking functionality from future Liferay tools will be added to\n\\texttt{system:check}.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes}{Developer\nmode} runs \\texttt{system:check} automatically on every startup.\n\nYou can enable \\texttt{system:check} to run on startup outside of\ndeveloper mode by setting this property in your\n\\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\nmodule.framework.properties.initial.system.check.enabled=true\n\\end{verbatim}\n\nAs stated previously, you can run the \\texttt{system:check} command any\ntime in\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nshell}. Enjoy detecting unresolved components and other issues fast\nusing \\texttt{system:check}.\n\n\\section{Related Topics}\\label{related-topics-36}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/detecting-unresolved-osgi-components}{Detecting\nUnresolved OSGi Components}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nshell}\n\n\\chapter{Troubleshooting Front-End Development\nIssues}\\label{troubleshooting-front-end-development-issues}\n\nFront-end development involves many moving parts. Sometimes it's hard to\ntell what may be causing the issues you run into along the way. This can\nbe particularly frustrating. These frequently asked questions and\nanswers help you troubleshoot and correct problems arising during\nfront-end development.\n\nHere are the troubleshooting sections:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[css]{CSS}\n\\item\n  \\hyperref[modules]{Modules}\n\\item\n  \\hyperref[portlets]{Portlets}\n\\end{itemize}\n\nClick a question to view the answer.\n\n\\section{CSS}\\label{css}\n\n\\phantomsection\\label{broken-css-angular-app}\n{Why are my CSS templates not applied in my Angular app?~{}}\n\n\\begin{verbatim}\n<p>A known <a href=\"https://github.com/angular/angular/issues/4974\">bug</a> with Angular causes absolute URLs for CSS files not to be recognized.</p>\n<p>Due to the nature of portals, a relative URL is not an option either because the app can be placed on any page.</p>\n<p>To fix this, you can either provide the CSS with a theme or themelet, or you can specify the path to the CSS file with the <code>com.liferay.portlet.header-portlet-css</code> property in the portlet containing your Angular code.</p>\n\\end{verbatim}\n\n\\phantomsection\\label{portal-css-broken-ie}\n{Why is Liferay Portal's CSS broken in Internet Explorer?~{}}\n\n\\begin{verbatim}\n<p>By default CSS files are minified in the browser. This can cause issues in Internet Explorer. You can disable this behavior by including <code>theme.css.fast.load=false</code> and <code>minifier.enabled=false</code> in your <code>portal-ext.properties</code> file. </p>\n\\end{verbatim}\n\n\\section{Modules}\\label{modules-1}\n\n\\phantomsection\\label{jquery-anonymous-module-error}\n{Why does my JQuery module throw an anonymous module error when I try to\nload it?~{}}\n\n\\begin{verbatim}\n<p>If you're using an external library that you host, you must disable the <i>Expose Global</i> option as described in the <a href=\"/docs/7-2/frameworks/-/knowledge_base/f/using-external-javascript-libraries#using-libraries-that-you-host\">Using External JavaScript Libraries</a> tutorial.</p>\n\\end{verbatim}\n\n\\phantomsection\\label{source-maps-not-showing}\n{Why are my source maps not showing for my Angular or Typescript\nmodule?~{}}\n\n\\begin{verbatim}\n<p>This is due to <a href=\"https://issues.liferay.com/browse/LPS-83052\">LPS-83052</a>.</p>\n<p>To solve this, activate the <a href=\"https://www.typescriptlang.org/docs/handbook/compiler-options.html\"><code>inlineSources</code> compiler option</a> via argument or your <code>tsconfig.json</code> file.</p>\n\\end{verbatim}\n\n\\phantomsection\\label{disable-bundler-analytics}\n{I'm using the liferay-npm-bundler for multiple projects. How can I\ndisable analytics tracking for the liferay-npm-bundler in my\nprojects?~{}}\n\n\\begin{verbatim}\n<p>There are a couple options you can use to disable reporting:</p>\n<ul>\n  <li><p>Use the <code>--no-tracking</code> flag in your <code>package.json</code>'s build script to disable reporting:</p>\n  <p><pre><code>liferay-npm-bundler --no-tracking</code></pre></p>\n  </li>\n  <li>\n  <p>Create a <code>.liferay-npm-bundler-no-tracking</code> file in your project's root folder, or any of its ancestors, to disable reporting.</p>\n  <p>This equates to answering <code>No</code> to the <code>May liferay-npm-bundler anonymously report usage statistics to improve the tool over time?</code> question.</p>\n  </li>\n</ul>\n\\end{verbatim}\n\n\\section{Portlets}\\label{portlets}\n\n\\phantomsection\\label{angular-react-vue-portlet-disable-spa}\n{I want to use a custom router in my Angular/React/Vue portlet. How can\nI disable the default Senna JS SPA engine in my portlet?~{}}\n\n\\begin{verbatim}\n<p>By default, the <a href=\"/docs/7-2/frameworks/-/knowledge_base/f/automatic-single-page-applications#what-is-sennajs\">Senna JS SPA engine</a> is enabled in your portlets and sites. This disables full page reloads during portlet navigation.</p>\n<p>If you want to use a custom router in your portlet instead, follow the <a href=\"/docs/7-2/frameworks/-/knowledge_base/f/automatic-single-page-applications#disabling-spa\">instructions in the SPA documentation</a> to blacklist your portlet from SPA.</p>\n\\end{verbatim}\n"
  },
  {
    "path": "book/developer/customization.aux",
    "content": "\\relax \n\\providecommand{\\transparent@use}[1]{}\n\\providecommand\\hyper@newdestlabel[2]{}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {150}Liferay Customization}{399}{chapter.150}\\protected@file@percent }\n\\newlabel{liferay-customization}{{150}{399}{Liferay Customization}{chapter.150}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {151}Fundamentals}{401}{chapter.151}\\protected@file@percent }\n\\newlabel{fundamentals}{{151}{401}{Fundamentals}{chapter.151}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {152}Configuring Dependencies}{403}{chapter.152}\\protected@file@percent }\n\\newlabel{configuring-dependencies}{{152}{403}{Configuring Dependencies}{chapter.152}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {153}Finding Artifacts}{405}{chapter.153}\\protected@file@percent }\n\\newlabel{finding-artifacts}{{153}{405}{Finding Artifacts}{chapter.153}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {153.1}Finding Core Artifact Attributes}{405}{section.153.1}\\protected@file@percent }\n\\newlabel{finding-core-artifact-attributes}{{153.1}{405}{Finding Core Artifact Attributes}{section.153.1}{}}\n\\gdef \\LT@ii {\\LT@entry \n    {1}{105.70474pt}\\LT@entry \n    {1}{134.6858pt}\\LT@entry \n    {1}{81.00111pt}\\LT@entry \n    {1}{88.6722pt}\\LT@entry \n    {1}{59.69113pt}}\n\\gdef \\LT@iii {\\LT@entry \n    {1}{177.65813pt}\\LT@entry \n    {1}{292.09688pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {153.2}Finding Liferay App and Independent Artifacts}{407}{section.153.2}\\protected@file@percent }\n\\newlabel{finding-liferay-app-and-independent-artifacts}{{153.2}{407}{Finding Liferay App and Independent Artifacts}{section.153.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {153.3}App Manager}{407}{section.153.3}\\protected@file@percent }\n\\newlabel{app-manager}{{153.3}{407}{App Manager}{section.153.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {153.1}{\\ignorespaces You can inspect deployed module artifact IDs and version numbers.}}{408}{figure.153.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {153.2}{\\ignorespaces The App Manager aggregates Liferay and independent modules.}}{408}{figure.153.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {153.4}Reference Docs}{408}{section.153.4}\\protected@file@percent }\n\\newlabel{reference-docs}{{153.4}{408}{Reference Docs}{section.153.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {153.3}{\\ignorespaces Results from this Gogo command show that the module's number is \\texttt  {1173}.}}{409}{figure.153.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {153.4}{\\ignorespaces Results from running the \\texttt  {headers} command show the module's bundle vendor and bundle version.}}{409}{figure.153.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {153.5}{\\ignorespaces Liferay DXP app Javadoc overviews list each app module's display name, followed by its group ID, artifact ID, and version number in a colon-separated string. It's a Gradle artifact syntax.}}{410}{figure.153.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {153.5}Maven Central}{410}{section.153.5}\\protected@file@percent }\n\\newlabel{maven-central}{{153.5}{410}{Maven Central}{section.153.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {153.6}Related Topics}{411}{section.153.6}\\protected@file@percent }\n\\newlabel{related-topics}{{153.6}{411}{Related Topics}{section.153.6}{}}\n\\gdef \\LT@iv {\\LT@entry \n    {1}{62.14067pt}\\LT@entry \n    {1}{56.93881pt}\\LT@entry \n    {1}{63.67303pt}\\LT@entry \n    {1}{43.04385pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {154}Specifying Dependencies}{413}{chapter.154}\\protected@file@percent }\n\\newlabel{specifying-dependencies}{{154}{413}{Specifying Dependencies}{chapter.154}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {154.1}Related Topics}{414}{section.154.1}\\protected@file@percent }\n\\newlabel{related-topics-1}{{154.1}{414}{Related Topics}{section.154.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {155}Resolving Third Party Library Package Dependencies}{415}{chapter.155}\\protected@file@percent }\n\\newlabel{resolving-third-party-library-package-dependencies}{{155}{415}{Resolving Third Party Library Package Dependencies}{chapter.155}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {155.1}Library Package Resolution Workflow}{416}{section.155.1}\\protected@file@percent }\n\\newlabel{library-package-resolution-workflow}{{155.1}{416}{Library Package Resolution Workflow}{section.155.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {155.2}Embedding Libraries in a Project}{417}{section.155.2}\\protected@file@percent }\n\\newlabel{embedding-libraries-in-a-project}{{155.2}{417}{Embedding Libraries in a Project}{section.155.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {155.3}Embedding Libraries Using Gradle}{417}{section.155.3}\\protected@file@percent }\n\\newlabel{embedding-libraries-using-gradle}{{155.3}{417}{Embedding Libraries Using Gradle}{section.155.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {155.4}Embedding a Library Using Maven}{417}{section.155.4}\\protected@file@percent }\n\\newlabel{embedding-a-library-using-maven}{{155.4}{417}{Embedding a Library Using Maven}{section.155.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {155.5}Related Topics}{418}{section.155.5}\\protected@file@percent }\n\\newlabel{related-topics-2}{{155.5}{418}{Related Topics}{section.155.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {156}Understanding Excluded JARs}{419}{chapter.156}\\protected@file@percent }\n\\newlabel{understanding-excluded-jars}{{156}{419}{Understanding Excluded JARs}{chapter.156}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {156.1}Related Topics}{420}{section.156.1}\\protected@file@percent }\n\\newlabel{related-topics-3}{{156.1}{420}{Related Topics}{section.156.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {157}Using the Felix Gogo Shell}{421}{chapter.157}\\protected@file@percent }\n\\newlabel{using-the-felix-gogo-shell}{{157}{421}{Using the Felix Gogo Shell}{chapter.157}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {158}Importing Packages}{423}{chapter.158}\\protected@file@percent }\n\\newlabel{importing-packages}{{158}{423}{Importing Packages}{chapter.158}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {158.1}Automatic Package Import Generation}{423}{section.158.1}\\protected@file@percent }\n\\newlabel{automatic-package-import-generation}{{158.1}{423}{Automatic Package Import Generation}{section.158.1}{}}\n\\gdef \\LT@v {\\LT@entry \n    {1}{130.8321pt}\\LT@entry \n    {1}{338.9229pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {158.2}Manually Adding Package Imports}{425}{section.158.2}\\protected@file@percent }\n\\newlabel{manually-adding-package-imports}{{158.2}{425}{Manually Adding Package Imports}{section.158.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {158.3}Related Topics}{426}{section.158.3}\\protected@file@percent }\n\\newlabel{related-topics-4}{{158.3}{426}{Related Topics}{section.158.3}{}}\n\\gdef \\LT@vi {\\LT@entry \n    {3}{149.19315pt}\\LT@entry \n    {3}{211.04694pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {159}Exporting Packages}{427}{chapter.159}\\protected@file@percent }\n\\newlabel{exporting-packages}{{159}{427}{Exporting Packages}{chapter.159}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {159.1}Related Topics}{428}{section.159.1}\\protected@file@percent }\n\\newlabel{related-topics-5}{{159.1}{428}{Related Topics}{section.159.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {160}Semantic Versioning}{429}{chapter.160}\\protected@file@percent }\n\\newlabel{semantic-versioning}{{160}{429}{Semantic Versioning}{chapter.160}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {160.1}Baselining Your Project}{429}{section.160.1}\\protected@file@percent }\n\\newlabel{baselining-your-project}{{160.1}{429}{Baselining Your Project}{section.160.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {160.2}Managing Artifact and Dependency Versions}{430}{section.160.2}\\protected@file@percent }\n\\newlabel{managing-artifact-and-dependency-versions}{{160.2}{430}{Managing Artifact and Dependency Versions}{section.160.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {160.3}Related Topics}{431}{section.160.3}\\protected@file@percent }\n\\newlabel{related-topics-6}{{160.3}{431}{Related Topics}{section.160.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {161}Deploying WARs (WAB Generator)}{433}{chapter.161}\\protected@file@percent }\n\\newlabel{deploying-wars-wab-generator}{{161}{433}{Deploying WARs (WAB Generator)}{chapter.161}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {161.1}WAR versus WAB Structure}{434}{section.161.1}\\protected@file@percent }\n\\newlabel{war-versus-wab-structure}{{161.1}{434}{WAR versus WAB Structure}{section.161.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {161.2}Deploying a WAR}{435}{section.161.2}\\protected@file@percent }\n\\newlabel{deploying-a-war}{{161.2}{435}{Deploying a WAR}{section.161.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {161.3}Saving a Copy of the WAB}{435}{section.161.3}\\protected@file@percent }\n\\newlabel{saving-a-copy-of-the-wab}{{161.3}{435}{Saving a Copy of the WAB}{section.161.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {161.4}Related Topics}{435}{section.161.4}\\protected@file@percent }\n\\newlabel{related-topics-7}{{161.4}{435}{Related Topics}{section.161.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {162}Architecture}{437}{chapter.162}\\protected@file@percent }\n\\newlabel{architecture}{{162}{437}{Architecture}{chapter.162}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {162.1}Core}{437}{section.162.1}\\protected@file@percent }\n\\newlabel{core}{{162.1}{437}{Core}{section.162.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {162.1}{\\ignorespaces Liferay DXP portals and Sites contain content and widgets. Liferay DXP can also be used ``headless''---without the UI.}}{438}{figure.162.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {162.2}{\\ignorespaces The Core provides a runtime environment for components, such as the ones here. New component implementations can extend or replace existing implementations dynamically.}}{439}{figure.162.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {162.2}Services}{439}{section.162.2}\\protected@file@percent }\n\\newlabel{services}{{162.2}{439}{Services}{section.162.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {162.3}{\\ignorespaces Remote and Liferay DXP applications can invoke services via REST web APIs. Liferay DXP Java-based portlets can also invoke services via Java APIs.}}{440}{figure.162.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {162.3}UI}{440}{section.162.3}\\protected@file@percent }\n\\newlabel{ui}{{162.3}{440}{UI}{section.162.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {163}Liferay Portal Classloader Hierarchy}{443}{chapter.163}\\protected@file@percent }\n\\newlabel{liferay-portal-classloader-hierarchy}{{163}{443}{Liferay Portal Classloader Hierarchy}{chapter.163}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {163.1}{\\ignorespaces 0: Here is Liferay's classloader hierarchy.}}{444}{figure.163.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {163.1}Web Application Classloading Perspective}{445}{section.163.1}\\protected@file@percent }\n\\newlabel{web-application-classloading-perspective}{{163.1}{445}{Web Application Classloading Perspective}{section.163.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {163.2}Other Classloading Perspectives}{445}{section.163.2}\\protected@file@percent }\n\\newlabel{other-classloading-perspectives}{{163.2}{445}{Other Classloading Perspectives}{section.163.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {163.3}Related Topics}{445}{section.163.3}\\protected@file@percent }\n\\newlabel{related-topics-8}{{163.3}{445}{Related Topics}{section.163.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {164}Liferay DXP Startup Phases}{447}{chapter.164}\\protected@file@percent }\n\\newlabel{liferay-dxp-startup-phases}{{164}{447}{Liferay DXP Startup Phases}{chapter.164}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {164.1}Portal Context Initialization Phase}{447}{section.164.1}\\protected@file@percent }\n\\newlabel{portal-context-initialization-phase}{{164.1}{447}{Portal Context Initialization Phase}{section.164.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {164.2}Main Servlet Initialization Phase}{448}{section.164.2}\\protected@file@percent }\n\\newlabel{main-servlet-initialization-phase}{{164.2}{448}{Main Servlet Initialization Phase}{section.164.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {164.3}Acting on Events}{448}{section.164.3}\\protected@file@percent }\n\\newlabel{acting-on-events}{{164.3}{448}{Acting on Events}{section.164.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {164.4}ModuleServiceLifecycle Events}{448}{section.164.4}\\protected@file@percent }\n\\newlabel{moduleservicelifecycle-events}{{164.4}{448}{ModuleServiceLifecycle Events}{section.164.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {164.5}Portal Startup Events}{448}{section.164.5}\\protected@file@percent }\n\\newlabel{portal-startup-events}{{164.5}{448}{Portal Startup Events}{section.164.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {164.6}Related Topics}{449}{section.164.6}\\protected@file@percent }\n\\newlabel{related-topics-9}{{164.6}{449}{Related Topics}{section.164.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {165}The Benefits of Modularity}{451}{chapter.165}\\protected@file@percent }\n\\newlabel{the-benefits-of-modularity}{{165}{451}{The Benefits of Modularity}{chapter.165}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {165.1}{\\ignorespaces The Apollo spacecraft's modules collectively took astronauts to the moon's surface and back to Earth.}}{451}{figure.165.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {165.1}Modularity Benefits for Software}{452}{section.165.1}\\protected@file@percent }\n\\newlabel{modularity-benefits-for-software}{{165.1}{452}{Modularity Benefits for Software}{section.165.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {165.2}Distinct Functionality}{452}{section.165.2}\\protected@file@percent }\n\\newlabel{distinct-functionality}{{165.2}{452}{Distinct Functionality}{section.165.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {165.3}Encapsulation}{452}{section.165.3}\\protected@file@percent }\n\\newlabel{encapsulation}{{165.3}{452}{Encapsulation}{section.165.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {165.4}Dependencies}{453}{section.165.4}\\protected@file@percent }\n\\newlabel{dependencies}{{165.4}{453}{Dependencies}{section.165.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {165.5}Reusability}{453}{section.165.5}\\protected@file@percent }\n\\newlabel{reusability}{{165.5}{453}{Reusability}{section.165.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {165.6}Example: Designing a Modular Application}{453}{section.165.6}\\protected@file@percent }\n\\newlabel{example-designing-a-modular-application}{{165.6}{453}{Example: Designing a Modular Application}{section.165.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {165.2}{\\ignorespaces The speech recognition application can be implemented in a single monolithic code base or in modules, each focused on a particular function.}}{454}{figure.165.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {165.3}{\\ignorespaces The \\emph  {Instruction manager} and \\emph  {Computer voice} modules designed for the speech recognition app can be used (or \\emph  {reused}) by a navigation app.}}{454}{figure.165.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {166}OSGi and Modularity}{455}{chapter.166}\\protected@file@percent }\n\\newlabel{osgi-and-modularity}{{166}{455}{OSGi and Modularity}{chapter.166}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {166.1}Modules}{455}{section.166.1}\\protected@file@percent }\n\\newlabel{modules}{{166.1}{455}{Modules}{section.166.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {166.2}API}{456}{section.166.2}\\protected@file@percent }\n\\newlabel{api}{{166.2}{456}{API}{section.166.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {166.3}Provider}{457}{section.166.3}\\protected@file@percent }\n\\newlabel{provider}{{166.3}{457}{Provider}{section.166.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {166.4}Consumer}{458}{section.166.4}\\protected@file@percent }\n\\newlabel{consumer}{{166.4}{458}{Consumer}{section.166.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {166.5}A Typical Liferay Application}{460}{section.166.5}\\protected@file@percent }\n\\newlabel{a-typical-liferay-application}{{166.5}{460}{A Typical Liferay Application}{section.166.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {166.6}Related Topics}{460}{section.166.6}\\protected@file@percent }\n\\newlabel{related-topics-10}{{166.6}{460}{Related Topics}{section.166.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {167}Module Lifecycle}{461}{chapter.167}\\protected@file@percent }\n\\newlabel{module-lifecycle}{{167}{461}{Module Lifecycle}{chapter.167}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {167.1}{\\ignorespaces This state diagram illustrates the module lifecycle.}}{462}{figure.167.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {167.1}Related Topics}{463}{section.167.1}\\protected@file@percent }\n\\newlabel{related-topics-11}{{167.1}{463}{Related Topics}{section.167.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {168}UI Architecture}{465}{chapter.168}\\protected@file@percent }\n\\newlabel{ui-architecture}{{168}{465}{UI Architecture}{chapter.168}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {168.1}Content}{465}{section.168.1}\\protected@file@percent }\n\\newlabel{content}{{168.1}{465}{Content}{section.168.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {168.2}Applications}{465}{section.168.2}\\protected@file@percent }\n\\newlabel{applications}{{168.2}{465}{Applications}{section.168.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {168.1}{\\ignorespaces Widget pages offer users functionality. Widgets are organized into a page template's rows and columns. This template has two columns: a smaller left column and larger right column. On this page, users select tags in the Tag Cloud widget and the matching tagged images show the Asset Publisher widget.}}{466}{figure.168.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {168.3}Themes}{466}{section.168.3}\\protected@file@percent }\n\\newlabel{themes}{{168.3}{466}{Themes}{section.168.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {168.2}{\\ignorespaces You can select an attractive theme and apply it to your site.}}{467}{figure.168.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {168.3}{\\ignorespaces You can provide custom styling using the theme's \\texttt  {\\_custom.sccs} file.}}{468}{figure.168.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {168.4}Product Navigation Sidebars and Panels}{469}{section.168.4}\\protected@file@percent }\n\\newlabel{product-navigation\\noindent -and-panels}{{168.4}{469}{Product Navigation Sidebars and Panels}{section.168.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {168.4}{\\ignorespaces Liferay facilitates integrating custom administrative functionality through navigation menus and administrative applications.}}{469}{figure.168.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {169}Theme Components}{471}{chapter.169}\\protected@file@percent }\n\\newlabel{theme-components}{{169}{471}{Theme Components}{chapter.169}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {169.1}Theme Templates and Utilities}{471}{section.169.1}\\protected@file@percent }\n\\newlabel{theme-templates-and-utilities}{{169.1}{471}{Theme Templates and Utilities}{section.169.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {169.1}{\\ignorespaces The collapsed navbar provides simplified user-friendly navigation for mobile devices.}}{472}{figure.169.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {169.2}{\\ignorespaces Each theme template provides a portion of the page's markup and functionality.}}{472}{figure.169.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {169.2}CSS Frameworks and Extensions}{473}{section.169.2}\\protected@file@percent }\n\\newlabel{css-frameworks-and-extensions}{{169.2}{473}{CSS Frameworks and Extensions}{section.169.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {169.3}Theme Customizations and Extensions}{473}{section.169.3}\\protected@file@percent }\n\\newlabel{theme-customizations-and-extensions}{{169.3}{473}{Theme Customizations and Extensions}{section.169.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {169.4}Portlet Customizations and Extensions}{473}{section.169.4}\\protected@file@percent }\n\\newlabel{portlet-customizations-and-extensions}{{169.4}{473}{Portlet Customizations and Extensions}{section.169.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {169.3}{\\ignorespaces There are several extension points for customizing portlets}}{474}{figure.169.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {169.5}Related Topics}{474}{section.169.5}\\protected@file@percent }\n\\newlabel{related-topics-12}{{169.5}{474}{Related Topics}{section.169.5}{}}\n\\gdef \\LT@vii {\\LT@entry \n    {1}{154.56912pt}\\LT@entry \n    {1}{160.56912pt}\\LT@entry \n    {1}{154.56912pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {170}Understanding the Page Structure}{475}{chapter.170}\\protected@file@percent }\n\\newlabel{understanding-the-page-structure}{{170}{475}{Understanding the Page Structure}{chapter.170}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {170.1}Portlets or Fragments}{475}{section.170.1}\\protected@file@percent }\n\\newlabel{portlets-or-fragments}{{170.1}{475}{Portlets or Fragments}{section.170.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {170.1}{\\ignorespaces The page layout is broken into three key sections.}}{476}{figure.170.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {170.2}Layout Templates, Page Templates, and Site Templates}{476}{section.170.2}\\protected@file@percent }\n\\newlabel{layout-templates-page-templates-and-site-templates}{{170.2}{476}{Layout Templates, Page Templates, and Site Templates}{section.170.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {170.2}{\\ignorespaces Each section of the page has elements and IDs that you can target for styling.}}{477}{figure.170.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {170.3}Product Navigation Sidebars and Panels}{478}{section.170.3}\\protected@file@percent }\n\\newlabel{product-navigation-sidebars-and-panels-1}{{170.3}{478}{Product Navigation Sidebars and Panels}{section.170.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {170.3}{\\ignorespaces Remember to account for the product navigation sidebars and panels when styling your site.}}{478}{figure.170.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {170.4}{\\ignorespaces The Add Menu pushes the main contents to the left.}}{479}{figure.170.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {170.5}{\\ignorespaces The Product Menu pushes the main contents to the right.}}{479}{figure.170.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {170.6}{\\ignorespaces The Simulation Panel pushes the main contents to the left.}}{480}{figure.170.6}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {170.4}Related Topics}{480}{section.170.4}\\protected@file@percent }\n\\newlabel{related-topics-13}{{170.4}{480}{Related Topics}{section.170.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {171}Bundle Classloading Flow}{481}{chapter.171}\\protected@file@percent }\n\\newlabel{bundle-classloading-flow}{{171}{481}{Bundle Classloading Flow}{chapter.171}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {171.1}{\\ignorespaces This flow chart illustrates classloading in a bundle.}}{482}{figure.171.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {172}Finding Extension Points}{483}{chapter.172}\\protected@file@percent }\n\\newlabel{finding-extension-points}{{172}{483}{Finding Extension Points}{chapter.172}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {172.1}Locate the Related Module and Component}{483}{section.172.1}\\protected@file@percent }\n\\newlabel{locate-the-related-module-and-component}{{172.1}{483}{Locate the Related Module and Component}{section.172.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {172.1}{\\ignorespaces The module name can be found using the App Manager.}}{484}{figure.172.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {172.2}Finding Extension Points in a Component}{484}{section.172.2}\\protected@file@percent }\n\\newlabel{finding-extension-points-in-a-component}{{172.2}{484}{Finding Extension Points in a Component}{section.172.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {172.2}{\\ignorespaces The component name can be found using the App Manager.}}{485}{figure.172.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {173}Troubleshooting Customizations}{487}{chapter.173}\\protected@file@percent }\n\\newlabel{troubleshooting-customizations}{{173}{487}{Troubleshooting Customizations}{chapter.173}{}}\n\\newlabel{cacheable-web-content-taglibs}{{173}{487}{Troubleshooting Customizations}{section*.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {174}Why doesn't the package I use from the fragment host resolve?}{489}{chapter.174}\\protected@file@percent }\n\\newlabel{why-doesnt-the-package-i-use-from-the-fragment-host-resolve}{{174}{489}{Why doesn't the package I use from the fragment host resolve?}{chapter.174}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {175}Why Aren't JSP overrides I Made Using Fragments Showing?}{491}{chapter.175}\\protected@file@percent }\n\\newlabel{why-arent-jsp-overrides-i-made-using-fragments-showing}{{175}{491}{Why Aren't JSP overrides I Made Using Fragments Showing?}{chapter.175}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {175.1}Related Topics}{491}{section.175.1}\\protected@file@percent }\n\\newlabel{related-topics-14}{{175.1}{491}{Related Topics}{section.175.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {176}Using OSGi Services from EXT Plugins}{493}{chapter.176}\\protected@file@percent }\n\\newlabel{using-osgi-services-from-ext-plugins}{{176}{493}{Using OSGi Services from EXT Plugins}{chapter.176}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {176.1}Related Topics}{493}{section.176.1}\\protected@file@percent }\n\\newlabel{related-topics-15}{{176.1}{493}{Related Topics}{section.176.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {177}Contributing to Liferay Portal}{495}{chapter.177}\\protected@file@percent }\n\\newlabel{contributing-to-liferay-portal}{{177}{495}{Contributing to Liferay Portal}{chapter.177}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {177.1}Building Liferay Portal from source}{495}{section.177.1}\\protected@file@percent }\n\\newlabel{building-liferay-portal-from-source}{{177.1}{495}{Building Liferay Portal from source}{section.177.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {177.2}Tooling}{495}{section.177.2}\\protected@file@percent }\n\\newlabel{tooling}{{177.2}{495}{Tooling}{section.177.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {177.3}Additional Resources}{496}{section.177.3}\\protected@file@percent }\n\\newlabel{additional-resources}{{177.3}{496}{Additional Resources}{section.177.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {178}Model Listeners}{497}{chapter.178}\\protected@file@percent }\n\\newlabel{model-listeners}{{178}{497}{Model Listeners}{chapter.178}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {178.1}Creating a Model Listener Class}{498}{section.178.1}\\protected@file@percent }\n\\newlabel{creating-a-model-listener-class}{{178.1}{498}{Creating a Model Listener Class}{section.178.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {178.2}Register the Model Listener Service}{498}{section.178.2}\\protected@file@percent }\n\\newlabel{register-the-model-listener-service}{{178.2}{498}{Register the Model Listener Service}{section.178.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {178.3}Listening For Persistence Events}{498}{section.178.3}\\protected@file@percent }\n\\newlabel{listening-for-persistence-events}{{178.3}{498}{Listening For Persistence Events}{section.178.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {178.4}Related Topics}{499}{section.178.4}\\protected@file@percent }\n\\newlabel{related-topics-16}{{178.4}{499}{Related Topics}{section.178.4}{}}\n\\gdef \\LT@viii {\\LT@entry \n    {1}{133.35468pt}\\LT@entry \n    {1}{171.17975pt}\\LT@entry \n    {1}{165.17975pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {179}Customizing JSPs}{501}{chapter.179}\\protected@file@percent }\n\\newlabel{customizing-jsps}{{179}{501}{Customizing JSPs}{chapter.179}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {179.1}Using Liferay DXP's API to Override a JSP}{501}{section.179.1}\\protected@file@percent }\n\\newlabel{using-liferay-dxps-api-to-override-a-jsp}{{179.1}{501}{Using Liferay DXP's API to Override a JSP}{section.179.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {179.2}Overriding a JSP Without Using Liferay DXP's API}{501}{section.179.2}\\protected@file@percent }\n\\newlabel{overriding-a-jsp-without-using-liferay-dxps-api}{{179.2}{501}{Overriding a JSP Without Using Liferay DXP's API}{section.179.2}{}}\n\\gdef \\LT@ix {\\LT@entry \n    {1}{133.35468pt}\\LT@entry \n    {1}{171.17975pt}\\LT@entry \n    {1}{165.17975pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {180}Customizing JSPs with Dynamic Includes}{503}{chapter.180}\\protected@file@percent }\n\\newlabel{customizing-jsps-with-dynamic-includes}{{180}{503}{Customizing JSPs with Dynamic Includes}{chapter.180}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {181}JSP Overrides Using Portlet Filters}{507}{chapter.181}\\protected@file@percent }\n\\newlabel{jsp-overrides-using-portlet-filters}{{181}{507}{JSP Overrides Using Portlet Filters}{chapter.181}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {182}JSP Overrides Using OSGi Fragments}{511}{chapter.182}\\protected@file@percent }\n\\newlabel{jsp-overrides-using-osgi-fragments}{{182}{511}{JSP Overrides Using OSGi Fragments}{chapter.182}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {182.1}Declaring a Fragment Host}{511}{section.182.1}\\protected@file@percent }\n\\newlabel{declaring-a-fragment-host}{{182.1}{511}{Declaring a Fragment Host}{section.182.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {182.2}Provide the Overridden JSP}{512}{section.182.2}\\protected@file@percent }\n\\newlabel{provide-the-overridden-jsp}{{182.2}{512}{Provide the Overridden JSP}{section.182.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {182.3}Using Fragment Host Internal Packages}{513}{section.182.3}\\protected@file@percent }\n\\newlabel{using-fragment-host-internal-packages}{{182.3}{513}{Using Fragment Host Internal Packages}{section.182.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {182.4}Related Topics}{514}{section.182.4}\\protected@file@percent }\n\\newlabel{related-topics-17}{{182.4}{514}{Related Topics}{section.182.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {183}JSP Overrides Using Custom JSP Bag}{515}{chapter.183}\\protected@file@percent }\n\\newlabel{jsp-overrides-using-custom-jsp-bag}{{183}{515}{JSP Overrides Using Custom JSP Bag}{chapter.183}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {183.1}Providing a Custom JSP}{515}{section.183.1}\\protected@file@percent }\n\\newlabel{providing-a-custom-jsp}{{183.1}{515}{Providing a Custom JSP}{section.183.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {183.2}Implement a Custom JSP Bag}{516}{section.183.2}\\protected@file@percent }\n\\newlabel{implement-a-custom-jsp-bag}{{183.2}{516}{Implement a Custom JSP Bag}{section.183.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {183.3}Extend a JSP}{518}{section.183.3}\\protected@file@percent }\n\\newlabel{extend-a-jsp}{{183.3}{518}{Extend a JSP}{section.183.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {183.4}Site Scoped JSP Customization}{518}{section.183.4}\\protected@file@percent }\n\\newlabel{site-scoped-jsp-customization}{{183.4}{518}{Site Scoped JSP Customization}{section.183.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {183.5}Related Topics}{518}{section.183.5}\\protected@file@percent }\n\\newlabel{related-topics-18}{{183.5}{518}{Related Topics}{section.183.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {184}Overriding Inline Content Using JSPs}{519}{chapter.184}\\protected@file@percent }\n\\newlabel{overriding-inline-content-using-jsps}{{184}{519}{Overriding Inline Content Using JSPs}{chapter.184}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {184.1}Example: Overriding the fieldset Taglib Tag}{520}{section.184.1}\\protected@file@percent }\n\\newlabel{example-overriding-the-fieldset-taglib-tag}{{184.1}{520}{Example: Overriding the fieldset Taglib Tag}{section.184.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {184.1}{\\ignorespaces Liferay DXP's home page's search and sign in components are in a \\texttt  {fieldset}.}}{522}{figure.184.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {184.2}{\\ignorespaces Before the \\texttt  {fieldset}'s nested fields, it prints \\emph  {test} surrounded by asterisks.}}{523}{figure.184.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {184.2}Related Topics}{523}{section.184.2}\\protected@file@percent }\n\\newlabel{related-topics-19}{{184.2}{523}{Related Topics}{section.184.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {185}Customizing Widgets}{525}{chapter.185}\\protected@file@percent }\n\\newlabel{customizing-widgets}{{185}{525}{Customizing Widgets}{chapter.185}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {185.1}Implementing the TemplateHandler Interface}{526}{section.185.1}\\protected@file@percent }\n\\newlabel{implementing-the-templatehandler-interface}{{185.1}{526}{Implementing the TemplateHandler Interface}{section.185.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {185.2}Defining Display Template Definitions}{526}{section.185.2}\\protected@file@percent }\n\\newlabel{defining-display-template-definitions}{{185.2}{526}{Defining Display Template Definitions}{section.185.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {185.3}Defining Permissions}{526}{section.185.3}\\protected@file@percent }\n\\newlabel{defining-permissions}{{185.3}{526}{Defining Permissions}{section.185.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {185.4}Exposing the Widget Template Selection}{526}{section.185.4}\\protected@file@percent }\n\\newlabel{exposing-the-widget-template-selection}{{185.4}{526}{Exposing the Widget Template Selection}{section.185.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {185.5}Recommendations for Using Widget Templates}{527}{section.185.5}\\protected@file@percent }\n\\newlabel{recommendations-for-using-widget-templates}{{185.5}{527}{Recommendations for Using Widget Templates}{section.185.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {186}Implementing Widget Templates}{529}{chapter.186}\\protected@file@percent }\n\\newlabel{implementing-widget-templates}{{186}{529}{Implementing Widget Templates}{chapter.186}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {186.1}{\\ignorespaces By using a custom display template, your portlet's display can be customized.}}{529}{figure.186.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {186.2}{\\ignorespaces You can click a variable to add it to the template editor.}}{531}{figure.186.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {186.3}{\\ignorespaces You can choose the Widget Template you want to apply from the widget's Configuration menu.}}{532}{figure.186.3}\\protected@file@percent }\n\\gdef \\LT@x {\\LT@entry \n    {1}{192.48683pt}\\LT@entry \n    {1}{277.26817pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {187}Dynamic Includes}{537}{chapter.187}\\protected@file@percent }\n\\newlabel{dynamic-includes}{{187}{537}{Dynamic Includes}{chapter.187}{}}\n\\gdef \\LT@xi {\\LT@entry \n    {1}{59.00896pt}\\LT@entry \n    {3}{83.90862pt}\\LT@entry \n    {3}{99.2502pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {188}WYSIWYG Editor Dynamic Includes}{539}{chapter.188}\\protected@file@percent }\n\\newlabel{wysiwyg-editor-dynamic-includes}{{188}{539}{WYSIWYG Editor Dynamic Includes}{chapter.188}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {188.1}Related Topics}{540}{section.188.1}\\protected@file@percent }\n\\newlabel{related-topics-20}{{188.1}{540}{Related Topics}{section.188.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {189}Top Head JSP Dynamic Includes}{541}{chapter.189}\\protected@file@percent }\n\\newlabel{top-head-jsp-dynamic-includes}{{189}{541}{Top Head JSP Dynamic Includes}{chapter.189}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {189.1}Related Topics}{542}{section.189.1}\\protected@file@percent }\n\\newlabel{related-topics-21}{{189.1}{542}{Related Topics}{section.189.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {190}Top JS Dynamic Include}{543}{chapter.190}\\protected@file@percent }\n\\newlabel{top-js-dynamic-include}{{190}{543}{Top JS Dynamic Include}{chapter.190}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {190.1}Related Topics}{544}{section.190.1}\\protected@file@percent }\n\\newlabel{related-topics-22}{{190.1}{544}{Related Topics}{section.190.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {191}Bottom JSP Dynamic Includes}{545}{chapter.191}\\protected@file@percent }\n\\newlabel{bottom-jsp-dynamic-includes}{{191}{545}{Bottom JSP Dynamic Includes}{chapter.191}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {191.1}Related Topics}{546}{section.191.1}\\protected@file@percent }\n\\newlabel{related-topics-23}{{191.1}{546}{Related Topics}{section.191.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {192}Waiting on Lifecycle Events}{547}{chapter.192}\\protected@file@percent }\n\\newlabel{waiting-on-lifecycle-events}{{192}{547}{Waiting on Lifecycle Events}{chapter.192}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {192.1}Taking action from a component}{547}{section.192.1}\\protected@file@percent }\n\\newlabel{taking-action-from-a-component}{{192.1}{547}{Taking action from a component}{section.192.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {192.2}Taking action from a non-component class}{548}{section.192.2}\\protected@file@percent }\n\\newlabel{taking-action-from-a-non-component-class}{{192.2}{548}{Taking action from a non-component class}{section.192.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {192.3}Related Topics}{549}{section.192.3}\\protected@file@percent }\n\\newlabel{related-topics-24}{{192.3}{549}{Related Topics}{section.192.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {193}Liferay Forms}{551}{chapter.193}\\protected@file@percent }\n\\newlabel{liferay-forms}{{193}{551}{Liferay Forms}{chapter.193}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {193.1}Liferay Forms Extension Points}{551}{section.193.1}\\protected@file@percent }\n\\newlabel{liferay-forms-extension-points}{{193.1}{551}{Liferay Forms Extension Points}{section.193.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {194}Form Storage Adapters}{553}{chapter.194}\\protected@file@percent }\n\\newlabel{form-storage-adapters}{{194}{553}{Form Storage Adapters}{chapter.194}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {194.1}Storage Adapter Methods}{553}{section.194.1}\\protected@file@percent }\n\\newlabel{storage-adapter-methods}{{194.1}{553}{Storage Adapter Methods}{section.194.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {194.1}{\\ignorespaces Choose a Storage Type for your form records.}}{554}{figure.194.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {194.2}The CRUD Methods}{554}{section.194.2}\\protected@file@percent }\n\\newlabel{the-crud-methods}{{194.2}{554}{The CRUD Methods}{section.194.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {194.3}Validating Form Entries}{555}{section.194.3}\\protected@file@percent }\n\\newlabel{validating-form-entries}{{194.3}{555}{Validating Form Entries}{section.194.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {194.4}Enabling the Storage Adapter}{556}{section.194.4}\\protected@file@percent }\n\\newlabel{enabling-the-storage-adapter}{{194.4}{556}{Enabling the Storage Adapter}{section.194.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {195}Creating a Form Storage Adapter}{557}{chapter.195}\\protected@file@percent }\n\\newlabel{creating-a-form-storage-adapter}{{195}{557}{Creating a Form Storage Adapter}{chapter.195}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {195.1}Storage Adapter CRUD Operations}{557}{section.195.1}\\protected@file@percent }\n\\newlabel{storage-adapter-crud-operations}{{195.1}{557}{Storage Adapter CRUD Operations}{section.195.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {195.2}Create}{557}{section.195.2}\\protected@file@percent }\n\\newlabel{create}{{195.2}{557}{Create}{section.195.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {195.3}Read}{558}{section.195.3}\\protected@file@percent }\n\\newlabel{read}{{195.3}{558}{Read}{section.195.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {195.4}Update}{559}{section.195.4}\\protected@file@percent }\n\\newlabel{update}{{195.4}{559}{Update}{section.195.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {195.5}Delete}{559}{section.195.5}\\protected@file@percent }\n\\newlabel{delete}{{195.5}{559}{Delete}{section.195.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {195.6}Beyond CRUD: Validation}{560}{section.195.6}\\protected@file@percent }\n\\newlabel{beyond-crud-validation}{{195.6}{560}{Beyond CRUD: Validation}{section.195.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {196}Overriding Language Keys}{561}{chapter.196}\\protected@file@percent }\n\\newlabel{overriding-language-keys}{{196}{561}{Overriding Language Keys}{chapter.196}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {197}Overriding Global Language Keys}{563}{chapter.197}\\protected@file@percent }\n\\newlabel{overriding-global-language-keys}{{197}{563}{Overriding Global Language Keys}{chapter.197}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {197.1}Determine the language keys to override}{563}{section.197.1}\\protected@file@percent }\n\\newlabel{determine-the-language-keys-to-override}{{197.1}{563}{Determine the language keys to override}{section.197.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {197.1}{\\ignorespaces Messages displayed in Liferay's user interface can be customized.}}{564}{figure.197.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {197.2}Override the keys in a new language properties file}{564}{section.197.2}\\protected@file@percent }\n\\newlabel{override-the-keys-in-a-new-language-properties-file}{{197.2}{564}{Override the keys in a new language properties file}{section.197.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {197.3}Create a Resource Bundle service component}{564}{section.197.3}\\protected@file@percent }\n\\newlabel{create-a-resource-bundle-service-component}{{197.3}{564}{Create a Resource Bundle service component}{section.197.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {197.2}{\\ignorespaces This button uses the overridden \\texttt  {publish} key.}}{566}{figure.197.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {197.4}Related Topics}{566}{section.197.4}\\protected@file@percent }\n\\newlabel{related-topics-25}{{197.4}{566}{Related Topics}{section.197.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {198}Overriding a Module's Language Keys}{567}{chapter.198}\\protected@file@percent }\n\\newlabel{overriding-a-modules-language-keys}{{198}{567}{Overriding a Module's Language Keys}{chapter.198}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {198.1}Find the module and its metadata and language keys}{567}{section.198.1}\\protected@file@percent }\n\\newlabel{find-the-module-and-its-metadata-and-language-keys}{{198.1}{567}{Find the module and its metadata and language keys}{section.198.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {198.2}Write custom language key values}{568}{section.198.2}\\protected@file@percent }\n\\newlabel{write-custom-language-key-values}{{198.2}{568}{Write custom language key values}{section.198.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {198.3}Prioritize Your Module's Resource Bundle}{569}{section.198.3}\\protected@file@percent }\n\\newlabel{prioritize-your-modules-resource-bundle}{{198.3}{569}{Prioritize Your Module's Resource Bundle}{section.198.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {198.4}Related Topics}{570}{section.198.4}\\protected@file@percent }\n\\newlabel{related-topics-26}{{198.4}{570}{Related Topics}{section.198.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {199}Overriding Liferay Services (Service Wrappers)}{571}{chapter.199}\\protected@file@percent }\n\\newlabel{overriding-liferay-services-service-wrappers}{{199}{571}{Overriding Liferay Services (Service Wrappers)}{chapter.199}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {199.1}Related Topics}{572}{section.199.1}\\protected@file@percent }\n\\newlabel{related-topics-27}{{199.1}{572}{Related Topics}{section.199.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {200}Overriding lpkg Files}{573}{chapter.200}\\protected@file@percent }\n\\newlabel{overriding-lpkg-files}{{200}{573}{Overriding lpkg Files}{chapter.200}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {201}Overriding Liferay MVC Commands}{575}{chapter.201}\\protected@file@percent }\n\\newlabel{overriding-liferay-mvc-commands}{{201}{575}{Overriding Liferay MVC Commands}{chapter.201}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {202}Adding Logic to MVC Commands}{577}{chapter.202}\\protected@file@percent }\n\\newlabel{adding-logic-to-mvc-commands}{{202}{577}{Adding Logic to MVC Commands}{chapter.202}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {202.1}Step 1: Implement the interface}{577}{section.202.1}\\protected@file@percent }\n\\newlabel{step-1-implement-the-interface}{{202.1}{577}{Step 1: Implement the interface}{section.202.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {202.2}Step 2: Publish as a component}{578}{section.202.2}\\protected@file@percent }\n\\newlabel{step-2-publish-as-a-component}{{202.2}{578}{Step 2: Publish as a component}{section.202.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {202.3}Step 3: Refer to the original implementation}{578}{section.202.3}\\protected@file@percent }\n\\newlabel{step-3-refer-to-the-original-implementation}{{202.3}{578}{Step 3: Refer to the original implementation}{section.202.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {202.4}Step 4: Add the logic}{579}{section.202.4}\\protected@file@percent }\n\\newlabel{step-4-add-the-logic}{{202.4}{579}{Step 4: Add the logic}{section.202.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {203}Overriding MVCRenderCommands}{581}{chapter.203}\\protected@file@percent }\n\\newlabel{overriding-mvcrendercommands}{{203}{581}{Overriding MVCRenderCommands}{chapter.203}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {203.1}Adding Logic to an Existing MVC Render Command}{582}{section.203.1}\\protected@file@percent }\n\\newlabel{adding-logic-to-an-existing-mvc-render-command}{{203.1}{582}{Adding Logic to an Existing MVC Render Command}{section.203.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {203.2}Redirecting to a New JSP}{582}{section.203.2}\\protected@file@percent }\n\\newlabel{redirecting-to-a-new-jsp}{{203.2}{582}{Redirecting to a New JSP}{section.203.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {203.3}Related Topics}{584}{section.203.3}\\protected@file@percent }\n\\newlabel{related-topics-28}{{203.3}{584}{Related Topics}{section.203.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {204}Overriding MVCActionCommands}{585}{chapter.204}\\protected@file@percent }\n\\newlabel{overriding-mvcactioncommands}{{204}{585}{Overriding MVCActionCommands}{chapter.204}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {204.1}Related Topics}{586}{section.204.1}\\protected@file@percent }\n\\newlabel{related-topics-29}{{204.1}{586}{Related Topics}{section.204.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {205}Overriding MVCResourceCommands}{587}{chapter.205}\\protected@file@percent }\n\\newlabel{overriding-mvcresourcecommands}{{205}{587}{Overriding MVCResourceCommands}{chapter.205}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {205.1}Related Topics}{588}{section.205.1}\\protected@file@percent }\n\\newlabel{related-topics-30}{{205.1}{588}{Related Topics}{section.205.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {206}Overriding OSGi Services}{589}{chapter.206}\\protected@file@percent }\n\\newlabel{overriding-osgi-services}{{206}{589}{Overriding OSGi Services}{chapter.206}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {207}Examining an OSGi Service to Override}{591}{chapter.207}\\protected@file@percent }\n\\newlabel{examining-an-osgi-service-to-override}{{207}{591}{Examining an OSGi Service to Override}{chapter.207}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {207.1}Gathering Information on a Service}{591}{section.207.1}\\protected@file@percent }\n\\newlabel{gathering-information-on-a-service}{{207.1}{591}{Gathering Information on a Service}{section.207.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {207.2}Step 1: Copy the Service Interface Name}{592}{section.207.2}\\protected@file@percent }\n\\newlabel{step-1-copy-the-service-interface-name}{{207.2}{592}{Step 1: Copy the Service Interface Name}{section.207.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {207.3}Step 2: Copy the Existing Service Name}{592}{section.207.3}\\protected@file@percent }\n\\newlabel{step-2-copy-the-existing-service-name}{{207.3}{592}{Step 2: Copy the Existing Service Name}{section.207.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {207.4}Step 3: Gather Reference Configuration Details (if reconfiguration is needed)}{593}{section.207.4}\\protected@file@percent }\n\\newlabel{step-3-gather-reference-configuration-details-if-reconfiguration-is-needed}{{207.4}{593}{Step 3: Gather Reference Configuration Details (if reconfiguration is needed)}{section.207.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {207.5}Related Topics}{594}{section.207.5}\\protected@file@percent }\n\\newlabel{related-topics-31}{{207.5}{594}{Related Topics}{section.207.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {208}Creating a Custom OSGi Service}{595}{chapter.208}\\protected@file@percent }\n\\newlabel{creating-a-custom-osgi-service}{{208}{595}{Creating a Custom OSGi Service}{chapter.208}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {208.1}Related Topics}{596}{section.208.1}\\protected@file@percent }\n\\newlabel{related-topics-32}{{208.1}{596}{Related Topics}{section.208.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {209}Reconfiguring Components to Use Your OSGi Service}{597}{chapter.209}\\protected@file@percent }\n\\newlabel{reconfiguring-components-to-use-your-osgi-service}{{209}{597}{Reconfiguring Components to Use Your OSGi Service}{chapter.209}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {209.1}Reconfiguring the Service Reference}{598}{section.209.1}\\protected@file@percent }\n\\newlabel{reconfiguring-the-service-reference}{{209.1}{598}{Reconfiguring the Service Reference}{section.209.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {209.1}{\\ignorespaces Because the example component's service reference is overridden by the configuration file deployment, the portlet indicates it's calling the custom service.}}{599}{figure.209.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {209.2}Related Topics}{599}{section.209.2}\\protected@file@percent }\n\\newlabel{related-topics-33}{{209.2}{599}{Related Topics}{section.209.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {210}Portlet Filters}{601}{chapter.210}\\protected@file@percent }\n\\newlabel{portlet-filters}{{210}{601}{Portlet Filters}{chapter.210}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {210.1}Sample Portlet}{602}{section.210.1}\\protected@file@percent }\n\\newlabel{sample-portlet}{{210.1}{602}{Sample Portlet}{section.210.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {210.2}Render filter 1 hides parts of user email addresses}{602}{section.210.2}\\protected@file@percent }\n\\newlabel{render-filter-1-hides-parts-of-user-email-addresses}{{210.2}{602}{Render filter 1 hides parts of user email addresses}{section.210.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {210.3}RenderFilter 2 Logs Statistics}{604}{section.210.3}\\protected@file@percent }\n\\newlabel{renderfilter-2-logs-statistics}{{210.3}{604}{RenderFilter 2 Logs Statistics}{section.210.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {210.4}Related Topics}{605}{section.210.4}\\protected@file@percent }\n\\newlabel{related-topics-34}{{210.4}{605}{Related Topics}{section.210.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {211}Product Navigation}{607}{chapter.211}\\protected@file@percent }\n\\newlabel{product-navigation}{{211}{607}{Product Navigation}{chapter.211}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {211.1}{\\ignorespaces The main product navigation menus include the Product Menu, Control Menu, Simulation Menu and User Personal Menu.}}{607}{figure.211.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {211.1}Product Menu}{608}{section.211.1}\\protected@file@percent }\n\\newlabel{product-menu}{{211.1}{608}{Product Menu}{section.211.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {211.2}Control Menu}{608}{section.211.2}\\protected@file@percent }\n\\newlabel{control-menu}{{211.2}{608}{Control Menu}{section.211.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {211.2}{\\ignorespaces The Control Menu has three configurable areas: left, right, and middle. It also displays the title and type of page that you are currently viewing.}}{608}{figure.211.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {211.3}{\\ignorespaces When switching your context to web content, the Control Menu adapts to provide helpful options for that area.}}{608}{figure.211.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {211.3}Simulation Menu}{609}{section.211.3}\\protected@file@percent }\n\\newlabel{simulation-menu}{{211.3}{609}{Simulation Menu}{section.211.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {211.4}{\\ignorespaces The Simulation Menu offers a device preview application.}}{609}{figure.211.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {211.4}User Personal Menu}{610}{section.211.4}\\protected@file@percent }\n\\newlabel{user-personal-menu}{{211.4}{610}{User Personal Menu}{section.211.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {211.5}{\\ignorespaces By default, the User Personal Menu contains the signed-in user's avatar, which opens the user's settings when selected.}}{610}{figure.211.5}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {212}Customizing the Product Menu}{613}{chapter.212}\\protected@file@percent }\n\\newlabel{customizing-the-product-menu}{{212}{613}{Customizing the Product Menu}{chapter.212}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {212.1}PanelCategory Interface}{613}{section.212.1}\\protected@file@percent }\n\\newlabel{panelcategory-interface}{{212.1}{613}{PanelCategory Interface}{section.212.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {212.2}BasePanelCategory}{614}{section.212.2}\\protected@file@percent }\n\\newlabel{basepanelcategory}{{212.2}{614}{BasePanelCategory}{section.212.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {212.3}BaseJSPPanelCategory}{614}{section.212.3}\\protected@file@percent }\n\\newlabel{basejsppanelcategory}{{212.3}{614}{BaseJSPPanelCategory}{section.212.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {212.4}PanelApp Interface}{614}{section.212.4}\\protected@file@percent }\n\\newlabel{panelapp-interface}{{212.4}{614}{PanelApp Interface}{section.212.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {213}Adding Custom Panel Categories}{617}{chapter.213}\\protected@file@percent }\n\\newlabel{adding-custom-panel-categories}{{213}{617}{Adding Custom Panel Categories}{chapter.213}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {213.1}Creating the OSGi Module}{617}{section.213.1}\\protected@file@percent }\n\\newlabel{creating-the-osgi-module}{{213.1}{617}{Creating the OSGi Module}{section.213.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {213.2}Implementing Liferay's Frameworks}{617}{section.213.2}\\protected@file@percent }\n\\newlabel{implementing-liferays-frameworks}{{213.2}{617}{Implementing Liferay's Frameworks}{section.213.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {214}Adding Custom Panel Apps}{621}{chapter.214}\\protected@file@percent }\n\\newlabel{adding-custom-panel-apps}{{214}{621}{Adding Custom Panel Apps}{chapter.214}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {215}Customizing the Control Menu}{623}{chapter.215}\\protected@file@percent }\n\\newlabel{customizing-the-control-menu}{{215}{623}{Customizing the Control Menu}{chapter.215}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {215.1}{\\ignorespaces This image shows where your entry will reside depending on the category you select.}}{623}{figure.215.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {215.1}ProductNavigationControlMenuEntry Interface}{623}{section.215.1}\\protected@file@percent }\n\\newlabel{productnavigationcontrolmenuentry-interface}{{215.1}{623}{ProductNavigationControlMenuEntry Interface}{section.215.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {215.2}BaseProductNavigationControlMenuEntry}{624}{section.215.2}\\protected@file@percent }\n\\newlabel{baseproductnavigationcontrolmenuentry}{{215.2}{624}{BaseProductNavigationControlMenuEntry}{section.215.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {215.3}BaseJSPProductNavigationControlMenuEntry}{624}{section.215.3}\\protected@file@percent }\n\\newlabel{basejspproductnavigationcontrolmenuentry}{{215.3}{624}{BaseJSPProductNavigationControlMenuEntry}{section.215.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {216}Creating Control Menu Entries}{625}{chapter.216}\\protected@file@percent }\n\\newlabel{creating-control-menu-entries}{{216}{625}{Creating Control Menu Entries}{chapter.216}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {216.1}Creating the OSGi Module}{625}{section.216.1}\\protected@file@percent }\n\\newlabel{creating-the-osgi-module-1}{{216.1}{625}{Creating the OSGi Module}{section.216.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {216.2}Implementing Liferay's Frameworks}{625}{section.216.2}\\protected@file@percent }\n\\newlabel{implementing-liferays-frameworks-1}{{216.2}{625}{Implementing Liferay's Frameworks}{section.216.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {217}Defining Icons and Tooltips}{629}{chapter.217}\\protected@file@percent }\n\\newlabel{defining-icons-and-tooltips}{{217}{629}{Defining Icons and Tooltips}{chapter.217}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {217.1}Control Menu Entry Icons}{629}{section.217.1}\\protected@file@percent }\n\\newlabel{control-menu-entry-icons}{{217.1}{629}{Control Menu Entry Icons}{section.217.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {217.2}Control Menu Entry Tooltips}{630}{section.217.2}\\protected@file@percent }\n\\newlabel{control-menu-entry-tooltips}{{217.2}{630}{Control Menu Entry Tooltips}{section.217.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {218}Extending the Simulation Menu}{631}{chapter.218}\\protected@file@percent }\n\\newlabel{extending-the-simulation-menu}{{218}{631}{Extending the Simulation Menu}{chapter.218}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {218.1}{\\ignorespaces The Simulation Menu also displays Segments to help simulate different user experiences.}}{632}{figure.218.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {219}Customizing the User Personal Bar and Menu}{635}{chapter.219}\\protected@file@percent }\n\\newlabel{customizing-the-user-personal-bar-and-menu}{{219}{635}{Customizing the User Personal Bar and Menu}{chapter.219}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {219.1}{\\ignorespaces By default, the User Personal Bar contains the signed-in user's avatar, which opens the Personal Menu when selected.}}{635}{figure.219.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {219.1}Displaying the Personal Menu}{636}{section.219.1}\\protected@file@percent }\n\\newlabel{displaying-the-personal-menu}{{219.1}{636}{Displaying the Personal Menu}{section.219.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {220}Using a Custom Portlet in Place of the User Personal Bar}{637}{chapter.220}\\protected@file@percent }\n\\newlabel{using-a-custom-portlet-in-place-of-the-user-personal-bar}{{220}{637}{Using a Custom Portlet in Place of the User Personal Bar}{chapter.220}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {220.1}Related Topics}{638}{section.220.1}\\protected@file@percent }\n\\newlabel{related-topics-35}{{220.1}{638}{Related Topics}{section.220.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {221}Customizing the Personal Menu}{639}{chapter.221}\\protected@file@percent }\n\\newlabel{customizing-the-personal-menu}{{221}{639}{Customizing the Personal Menu}{chapter.221}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {221.1}Adding an Entry to the Personal Menu}{639}{section.221.1}\\protected@file@percent }\n\\newlabel{adding-an-entry-to-the-personal-menu}{{221.1}{639}{Adding an Entry to the Personal Menu}{section.221.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {221.1}{\\ignorespaces The Personal Menu is organized into four sections.}}{640}{figure.221.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {221.2}Adding a Portlet Entry to the Personal Menu}{641}{section.221.2}\\protected@file@percent }\n\\newlabel{adding-a-portlet-entry-to-the-personal-menu}{{221.2}{641}{Adding a Portlet Entry to the Personal Menu}{section.221.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {221.3}Related Topics}{642}{section.221.3}\\protected@file@percent }\n\\newlabel{related-topics-36}{{221.3}{642}{Related Topics}{section.221.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {222}Customizing Workflow}{643}{chapter.222}\\protected@file@percent }\n\\newlabel{customizing-workflow}{{222}{643}{Customizing Workflow}{chapter.222}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {223}Creating SLA Calendars}{645}{chapter.223}\\protected@file@percent }\n\\newlabel{creating-sla-calendars}{{223}{645}{Creating SLA Calendars}{chapter.223}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {223.1}{\\ignorespaces Write a Custom SLA Calendar if the default, 24/7 calendar isn't sufficient.}}{645}{figure.223.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {223.1}Dependencies}{646}{section.223.1}\\protected@file@percent }\n\\newlabel{dependencies-1}{{223.1}{646}{Dependencies}{section.223.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {223.2}Implementation Steps}{646}{section.223.2}\\protected@file@percent }\n\\newlabel{implementation-steps}{{223.2}{646}{Implementation Steps}{section.223.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {224}Customizing Core Functionality with Ext}{649}{chapter.224}\\protected@file@percent }\n\\newlabel{customizing-core-functionality-with-ext}{{224}{649}{Customizing Core Functionality with Ext}{chapter.224}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {225}Extending Core Classes Using Spring with Ext Plugins}{651}{chapter.225}\\protected@file@percent }\n\\newlabel{extending-core-classes-using-spring-with-ext-plugins}{{225}{651}{Extending Core Classes Using Spring with Ext Plugins}{chapter.225}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {226}Overriding Core Classes with Ext Plugins}{653}{chapter.226}\\protected@file@percent }\n\\newlabel{overriding-core-classes-with-ext-plugins}{{226}{653}{Overriding Core Classes with Ext Plugins}{chapter.226}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {227}Adding to the web.xml with Ext Plugins}{655}{chapter.227}\\protected@file@percent }\n\\newlabel{adding-to-the-web.xml-with-ext-plugins}{{227}{655}{Adding to the web.xml with Ext Plugins}{chapter.227}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {228}Modifying the web.xml with Ext Plugins}{657}{chapter.228}\\protected@file@percent }\n\\newlabel{modifying-the-web.xml-with-ext-plugins}{{228}{657}{Modifying the web.xml with Ext Plugins}{chapter.228}{}}\n\\@setckpt{developer/customization}{\n\\setcounter{page}{659}\n\\setcounter{equation}{0}\n\\setcounter{enumi}{3}\n\\setcounter{enumii}{2}\n\\setcounter{enumiii}{3}\n\\setcounter{enumiv}{0}\n\\setcounter{footnote}{0}\n\\setcounter{mpfootnote}{0}\n\\setcounter{@memmarkcntra}{-1}\n\\setcounter{storedpagenumber}{1}\n\\setcounter{book}{0}\n\\setcounter{part}{2}\n\\setcounter{chapter}{228}\n\\setcounter{section}{0}\n\\setcounter{subsection}{0}\n\\setcounter{subsubsection}{0}\n\\setcounter{paragraph}{0}\n\\setcounter{subparagraph}{0}\n\\setcounter{@ppsavesec}{0}\n\\setcounter{@ppsaveapp}{0}\n\\setcounter{vslineno}{0}\n\\setcounter{poemline}{0}\n\\setcounter{modulo@vs}{0}\n\\setcounter{memfvsline}{0}\n\\setcounter{verse}{0}\n\\setcounter{chrsinstr}{0}\n\\setcounter{poem}{0}\n\\setcounter{newflo@tctr}{4}\n\\setcounter{@contsubnum}{0}\n\\setcounter{section@level}{0}\n\\setcounter{maxsecnumdepth}{1}\n\\setcounter{sidefootnote}{0}\n\\setcounter{pagenote}{0}\n\\setcounter{pagenoteshadow}{0}\n\\setcounter{memfbvline}{0}\n\\setcounter{bvlinectr}{0}\n\\setcounter{cp@cntr}{0}\n\\setcounter{ism@mctr}{0}\n\\setcounter{xsm@mctr}{0}\n\\setcounter{csm@mctr}{0}\n\\setcounter{ksm@mctr}{0}\n\\setcounter{xksm@mctr}{0}\n\\setcounter{cksm@mctr}{0}\n\\setcounter{msm@mctr}{0}\n\\setcounter{xmsm@mctr}{0}\n\\setcounter{cmsm@mctr}{0}\n\\setcounter{bsm@mctr}{0}\n\\setcounter{workm@mctr}{0}\n\\setcounter{sheetsequence}{731}\n\\setcounter{lastsheet}{2851}\n\\setcounter{lastpage}{2779}\n\\setcounter{figure}{0}\n\\setcounter{lofdepth}{1}\n\\setcounter{table}{0}\n\\setcounter{lotdepth}{1}\n\\setcounter{Item}{837}\n\\setcounter{Hfootnote}{5}\n\\setcounter{bookmark@seq@number}{0}\n\\setcounter{memhycontfloat}{0}\n\\setcounter{Hpagenote}{0}\n\\setcounter{r@tfl@t}{0}\n\\setcounter{float@type}{4}\n\\setcounter{LT@tables}{11}\n\\setcounter{LT@chunks}{3}\n\\setcounter{parentequation}{0}\n\\setcounter{FancyVerbLine}{0}\n}\n"
  },
  {
    "path": "book/developer/customization.tex",
    "content": "\\chapter{Liferay Customization}\\label{liferay-customization}\n\nLiferay DXP is highly customizable. Its modular architecture contains\ncomponents you can extend and override dynamically. This section\nexplains Liferay DXP's architecture and customization fundamentals and\ndemonstrates overriding and extending Liferay DXP components and\napplications using APIs.\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/fundamentals}{Fundamentals}\n  include understanding and configuring dependencies, packaging, and\n  deployment. Here you'll work with module JARs, plugin WARs,\n  components, and Java packages in Liferay DXP.\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/architecture}{Architecture}\n  dives deep into how Liferay DXP uses modularity and OSGi to provide\n  the core, application modules, component services, and extension\n  points. Learning the architecture helps you develop better\n  customizations fast, and it empowers you to build extension points\n  into your own applications.\n\\item\n  Built-in customization features, including\n  \\href{/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates}{Widget\n  Templates} and\n  \\href{/docs/7-2/user/-/knowledge_base/u/web-experience-management}{Web\n  Experience Management} help you customize content and pages faster.\n  All this is done from within the Liferay DXP UI.\n\\item\n  Application customization articles (listed after the Architecture\n  articles) demonstrate modifying Liferay applications via their APIs\n  and extension points.\n\\end{itemize}\n\nStart with\n\\href{/docs/7-2/customization/-/knowledge_base/c/fundamentals}{Fundamentals}.\n\n\\chapter{Fundamentals}\\label{fundamentals}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe fundamentals of developing on Liferay DXP and customizing it are\nperhaps best learned in the context of projects. It's in projects that\nyou configure access to Liferay DXP's API, extend and override Liferay\nDXP features, and package your software for deployment. Projects are\ndeveloped as WARs or OSGi JARs, but are all installed to Liferay's OSGi\nframework as OSGi bundles. These bundles can depend on external Java\npackages, share Java packages, and be manipulated at run time via Apache\nGogo Shell. The fundamentals are explained in the context of projects so\nthat you understand them in a practical sense and can apply them right\naway. Here are the fundamental topics:\n\n\\begin{itemize}\n\\item\n  \\textbf{WARs Versus OSGi JAR} explains fundamental differences between\n  the WAR and OSGi JAR structures and how they're deployed in Liferay\n  DXP.\n\\item\n  \\textbf{Configuring Dependencies} demonstrates how to identify and\n  configure Liferay artifacts and third-party artifacts to use their\n  Java packages in your projects.\n\\item\n  \\textbf{Importing and Exporting Packages} shows how to import the\n  packages your projects need and export packages your projects provide.\n  Liferay's tooling detects package use and specifies package imports\n  automatically.\n\\item\n  \\textbf{Semantic Versioning} shows how Liferay DXP uses a standard for\n  ascribing meaning to major, minor, and micro versions of modules and\n  Java packages.\n\\item\n  \\textbf{Deploying WARs (WAB Generator)} explains how Liferay's WAB\n  Generator deploys WAR applications as OSGi Web Application Bundles\n  (WABs).\n\\item\n  \\textbf{Gogo Shell} enables you to examine components, debug issues,\n  and manage deployments.\n\\end{itemize}\n\nStart with understanding how WAR and OSGi JAR project structures are\nused in development.\n\n\\chapter{Configuring Dependencies}\\label{configuring-dependencies}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay DXP's modular environment lets modules provide and consume\ncapabilities via Java packages. To leverage packages from other modules\nor traditional libraries in your project, you must configure them as\ndependencies. Here you'll learn how to find artifacts (modules or\nlibraries) and configure dependencies on them.\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/finding-artifacts}{Finding\n  Artifacts} explains how to use the Application Manager, Gogo Shell,\n  and Liferay DXP reference documentation to find artifacts deployed on\n  Liferay DXP and available in repositories.\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/specifying-dependencies}{Specifying\n  Dependencies} demonstrates specifying artifacts to Maven and Gradle\n  build frameworks. It shows you how to determine whether Liferay DXP\n  already exports packages from an artifact and how to configure such\n  artifacts as compile-time dependencies.\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{Resolving\n  Third-Party Library Package Dependencies} provides a workflow for\n  using packages that are only available in traditional library JARs\n  (JARs that aren't OSGi modules). It involves minimizing transitive\n  dependencies so you can resolve dependencies quicker and prevent\n  bloating your project with unnecessary JARs.\n\\end{itemize}\n\nYour first step is to find the artifacts you need.\n\n\\chapter{Finding Artifacts}\\label{finding-artifacts}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nUsing external artifacts in your project requires configuring their\ndependencies. To do this, look up the artifact's attributes and plug\nthem into dependency entries for your build system (either\n\\href{https://gradle.org/}{Gradle} or\n\\href{https://maven.apache.org/}{Maven}). Your build system downloads\nthe dependency artifacts your project needs to compile successfully.\n\nBefore specifying an artifact as a dependency, you must first find its\nattributes. Artifacts have these attributes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Group ID}: Authoring organization\n\\item\n  \\emph{Artifact ID}: Name/identifier\n\\item\n  \\emph{Version}: Release number\n\\end{itemize}\n\nHere you'll learn how to find artifact attributes to specify artifact\ndependencies.\n\n\\section{Finding Core Artifact\nAttributes}\\label{finding-core-artifact-attributes}\n\nEach Liferay artifact is a JAR file whose \\texttt{META-INF/MANIFEST.MF}\nfile specifies OSGi bundle metadata the artifact's attributes. For\nexample, these two OSGi headers specify the artifact ID and version:\n\n\\begin{verbatim}\nBundle-SymbolicName:  [artifact ID]\nBundle-Version: [version]\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Artifacts in Liferay DXP fix packs override Liferay\nDXP installation artifacts. Subfolders of a fix pack ZIP file's\n\\texttt{binaries} folder hold the artifacts. If an installed fix pack\nprovides an artifact you depend \\textbar{} on, specify the version of\nthat fix pack artifact in your dependency.\n\n\\noindent\\hrulefill\n\nThis table lists each core Liferay DXP artifact's group ID and artifact\nID and where to find the artifact's manifest, which lists the artifact\nversion:\n\n\\emph{Core Liferay DXP Artifacts}:\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 8\\tabcolsep) * \\real{0.2364}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 8\\tabcolsep) * \\real{0.2909}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 8\\tabcolsep) * \\real{0.1636}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 8\\tabcolsep) * \\real{0.1818}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 8\\tabcolsep) * \\real{0.1273}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nFile\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nGroup ID\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nArtifact ID\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nVersion\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nOrigin\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{portal-kernel.jar} & \\texttt{com.liferay.portal} &\n\\texttt{com.liferay.\\ \nportal.kernel} & (see JAR's \\texttt{MANIFEST.MF}) &\nfix pack ZIP, Liferay DXP installation, or Liferay DXP dependencies\nZIP \\\\\n\\texttt{portal-impl.jar} & \\texttt{com.liferay.portal} &\n\\texttt{com.liferay.\\ \nportal.impl} & (see JAR's \\texttt{MANIFEST.MF}) &\nfix pack ZIP or Liferay DXP \\texttt{.war} \\\\\n\\texttt{util-bridges.jar} & \\texttt{com.liferay.portal} &\n\\texttt{com.liferay.\\ \nutil.bridges} & (see JAR's \\texttt{MANIFEST.MF}) &\nfix pack ZIP or Liferay DXP \\texttt{.war} \\\\\n\\texttt{util-java.jar} & \\texttt{com.liferay.portal} &\n\\texttt{com.liferay.\\ \nutil.java} & (see JAR's \\texttt{MANIFEST.MF}) & fix\npack ZIP or Liferay DXP \\texttt{.war} \\\\\n\\texttt{util-slf4j.jar} & \\texttt{com.liferay.portal} &\n\\texttt{com.liferay.\\ \nutil.slf4j} & (see JAR's \\texttt{MANIFEST.MF}) & fix\npack ZIP or Liferay DXP \\texttt{.war} \\\\\n\\texttt{util-taglibs.jar} & \\texttt{com.liferay.portal} &\n\\texttt{com.liferay.\\ \nutil.taglib} & (see JAR's \\texttt{MANIFEST.MF}) &\nfix pack ZIP or Liferay DXP \\texttt{.war} \\\\\n\\texttt{com.liferay.*} JAR files & \\texttt{com.liferay} & (see JAR's\n\\texttt{MANIFEST.MF}) & (see JAR's \\texttt{MANIFEST.MF}) & fix pack ZIP,\nLiferay DXP installation, Liferay DXP dependencies ZIP, or the OSGi\nZIP \\\\\n\\end{longtable}\n\nNext, you'll learn how to find Liferay DXP app and independent module\nartifact attributes.\n\n\\section{Finding Liferay App and Independent\nArtifacts}\\label{finding-liferay-app-and-independent-artifacts}\n\nIndependent modules and Liferay DXP app modules aren't part of the\nLiferay DXP core. You must still, however, find their artifact\nattributes if you depend on them. The resources below provide the\nartifact details for Liferay DXP's apps and independent modules:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.3750}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.6250}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nResource\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nArtifact Type\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\hyperref[app-manager]{App Manager} & Deployed modules \\\\\n\\hyperref[reference-docs]{Reference Docs} & Liferay DXP modules (per\nrelease) \\\\\n\\hyperref[maven-central]{Maven Central} & All artifact types: Liferay\nDXP and third party, module and non-module \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Important}: \\texttt{com.liferay} is the group ID for all of\nLiferay's apps and independent modules.\n\n\\noindent\\hrulefill\n\nThe App Manager is the best source for information on deployed modules.\nYou'll learn about it next.\n\n\\section{App Manager}\\label{app-manager}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-and-configuring-apps\\#using-the-app-manager}{The\nApp Manager} knows what's deployed on your Liferay instance. Use it to\nfind deployed module attributes.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In Liferay DXP, navigate to \\emph{Control Panel} → \\emph{Apps} →\n  \\emph{App Manager}.\n\\item\n  Search for the module by its display name, symbolic name, or related\n  keywords. You can also browse for the module in its app. Whether\n  browsing or searching, the App Manager shows the module's artifact ID\n  and version number.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/configuring-dependencies-search-app-manager-for-module.png}\n\\caption{You can inspect deployed module artifact IDs and version\nnumbers.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/configuring-dependencies-indep-modules-in-app-manager.png}\n\\caption{The App Manager aggregates Liferay and independent modules.}\n\\end{figure}\n\nIf you don't know a deployed module's group ID, use the\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Felix\nGogo Shell} to find it:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Gogo Shell portlet in the Control Panel →\n  \\emph{Configuration} → \\emph{Gogo Shell}. Enter commands in the Felix\n  Gogo Shell command prompt.\n\\item\n  Search for the module by its display name (e.g.,\n  \\texttt{Liferay\\ Blogs\\ API}) or a keyword. In the results, note the\n  module's number. You can use it in the next step. For example, Gogo\n  command results in the figure below show the Liferay Blogs API module\n  number.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/configuring-deps-gogo-grep-for-module.png}\n  \\caption{Results from this Gogo command show that the module's number\n  is \\texttt{1173}.}\n  \\end{figure}\n\\item\n  List the module's manifest headers by passing the module number to the\n  \\texttt{headers} command. In the results, note the\n  \\texttt{Bundle-Vendor} value: you'll match it with an artifact group\n  in a later step:\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/configuring-deps-gogo-module-info.png}\n  \\caption{Results from running the \\texttt{headers} command show the\n  module's bundle vendor and bundle version.}\n  \\end{figure}\n\\item\n  On \\href{https://search.maven.org/}{Maven Central} or\n  \\href{https://mvnrepository.com}{MVNRepository}, search for the module\n  by its artifact ID.\n\\item\n  Determine the group ID by matching the \\texttt{Bundle-Vendor} value\n  from step 3 with a group listed that provides the artifact.\n\\end{enumerate}\n\nNext, Liferay DXP's reference documentation provides Liferay DXP app\nartifact attributes.\n\n\\section{Reference Docs}\\label{reference-docs}\n\nLiferay DXP's app Javadoc lists each app module's artifact ID, version\nnumber, and display name. This is the best place to look up Liferay DXP\napp modules that aren't yet deployed to your Liferay DXP instance.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To find artifact information on a Core Liferay DXP\nartifact, refer to the previous section \\emph{Finding Core Liferay DXP\nArtifact Attributes}.\n\n\\noindent\\hrulefill\n\nFollow these steps to find a Liferay DXP app module's attributes in the\nJavadoc:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to Javadoc for an app module class. If you don't have a link\n  to the class's Javadoc, find it by browsing\n  {[}https://docs.liferay.com/dxp/apps{]}(\n\\item\n  Copy the class's package name.\n\\item\n  Navigate to the \\emph{Overview} page.\n\\item\n  On the \\emph{Overview} page, search for the package name you copied in\n  step 2.\n\\end{enumerate}\n\nThe heading above the package name shows the module's artifact ID,\nversion number, and display name. Remember, the group ID for all app\nmodules is \\texttt{com.liferay}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/intro-configuring-dependencies-module-info-in-javadoc-overview.png}\n\\caption{Liferay DXP app Javadoc overviews list each app module's\ndisplay name, followed by its group ID, artifact ID, and version number\nin a colon-separated string. It's a Gradle artifact syntax.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: Module version numbers aren't currently included in any\ntag library reference docs.\n\n\\noindent\\hrulefill\n\nNext, you'll learn how to look up artifacts on MVNRepository and Maven\nCentral.\n\n\\section{Maven Central}\\label{maven-central}\n\nMost artifacts, regardless of type or origin, are on\n\\href{https://mvnrepository.com/}{MVNRepository} and\n\\href{https://search.maven.org/}{Maven Central}. These sites can help\nyou find artifacts based on class packages. It's common to include an\nartifact's ID in the start of an artifact's package names. For example,\nif you depend on the class\n\\texttt{org.osgi.service.component.annotations.Component}, search for\nthe package name \\texttt{org.osgi.service.component.annotations} on one\nof the Maven sites.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Make sure to follow the instructions listed earlier to\ndetermine the version of Liferay artifacts you need.\n\n\\noindent\\hrulefill\n\nNow that you know the artifact's attributes, you can configure a\ndependency on it.\n\n\\section{Related Topics}\\label{related-topics}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/specifying-dependencies}{Specifying\nDependencies}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/importing-packages}{Importing\nPackages}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/exporting-packages}{Exporting\nPackages}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{Resolving\nThird Party Library Package Dependencies}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{Deploying\nWARs (WAB Generator)}\n\n\\chapter{Specifying Dependencies}\\label{specifying-dependencies}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nCompiling your project and deploying it to Liferay DXP requires\nsatisfying its dependencies on external artifacts. After\n\\href{/docs/7-2/customization/-/knowledge_base/c/finding-artifacts}{finding\nthe attributes of an artifact}, set a dependency for it in your build\nfile. Here's how:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Determine whether Liferay DXP provides the Java packages you use from\n  the artifact. These files list the packages Liferay DXP exports:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{modules/core/portal-bootstrap/system.packages.extra.bnd}\n    file in the\n    \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/core/portal-bootstrap/system.packages.extra.bnd}{GitHub\n    repository}. It lists exported packages on separate lines, making\n    them easy to read.\n  \\item\n    \\texttt{META-INF/system.packages.extra.mf} file in\n    \\texttt{{[}LIFERAY\\_HOME{]}/osgi/core/com.liferay.portal.bootstrap.jar}.\n    The file is available in Liferay DXP bundles. It lists exported\n    packages in a paragraph wrapped at 70 columns--they're harder to\n    read here than in the \\texttt{system.packages.extra.bnd} file.\n  \\end{itemize}\n\\item\n  If Liferay DXP exports all the packages you use from the artifact,\n  specify the artifact as a compile-only dependency. This prevents your\n  build framework from bundling the artifact with your project. Here's\n  how to make the dependency compile-only:\n\n  \\textbf{Gradle:} Add the \\texttt{compileOnly} directive to the\n  dependency\n\n  \\textbf{Maven:} Add the\n  \\texttt{\\textless{}scope\\textgreater{}provided\\textless{}/scope\\textgreater{}}\n  element to the dependency.\n\\item\n  Add a dependency entry for the artifact. Here's the artifact\n  terminology for the Gradle and Maven build frameworks:\n\\end{enumerate}\n\n\\emph{Artifact Terminology}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}llll@{}}\n\\toprule\\noalign{}\nFramework & Group ID & Artifact ID & Version \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nGradle & \\texttt{group} & \\texttt{name} & \\texttt{version} \\\\\nMaven & \\texttt{groupId} & \\texttt{artifactId} & \\texttt{version} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nHere is an example dependency on Liferay's Journal API module for\nGradle, and Maven:\n\n\\emph{Gradle (\\texttt{build.gradle} entry):}\n\n\\begin{verbatim}\ndependencies {\n    compileOnly group: \"com.liferay\", name: \"com.liferay.journal.api\", version: \"1.0.1\"\n    ...\n}\n\\end{verbatim}\n\n\\emph{Maven (\\texttt{pom.xml} entry):}\n\n\\begin{verbatim}\n<dependency>\n    <groupId>com.liferay</groupId>\n    <artifactId>com.liferay.journal.api</artifactId>\n    <version>1.0.1</version>\n    <scope>provided</scope>\n</dependency>\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:}\n\\href{/docs/7-2/reference/-/knowledge_base/r/third-party-packages-portal-exports}{Liferay\nDXP exports many third-party packages}. Deploy your module to check if\nLiferay DXP or another module in your Liferay instance's OSGi runtime\nframework provides the package you need. If it's provided already,\nspecify the corresponding dependency as being ``provided''. Here's how\nto specify a provided dependency:\n\nMaven:\n\\texttt{\\textless{}scope\\textgreater{}provided\\textless{}/scope\\textgreater{}}\n\nGradle: \\texttt{providedCompile}\n\nDon't deploy a provided package's JAR again or embed the JAR in your\nproject. Exporting the same package from different JARs leads to ``split\npackage'' issues, whose side affects differ from case to case. If the\npackage is in a third-party library (not an OSGi module), refer to\n{[}Resolving Third\n\n\\noindent\\hrulefill Party Library\nDependencies{]}(/docs/7-2/customization/-/knowledge\\_base/c/adding-third-party-libraries-to-a-module).\n\n\\noindent\\hrulefill\n\nIf you're developing a WAR that requires a different version of a\nthird-party package that\n\\href{/docs/7-2/reference/-/knowledge_base/r/third-party-packages-portal-exports}{Liferay\nDXP or another module exports}, specify that package in your\n\\href{/docs/7-2/customization/-/knowledge_base/c/importing-packages}{\\texttt{Import-Package:}\nlist}. If the package provider is an OSGi module, publish its exported\npackages by deploying that module. Otherwise, follow the instructions\nfor\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{adding\na third-party library (not an OSGi module)}.\n\n\\noindent\\hrulefill\n\nNice! You know how to specify artifact dependencies. Now that's a skill\nyou can depend on!\n\n\\section{Related Topics}\\label{related-topics-1}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/finding-artifacts}{Finding\nArtifacts}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/importing-packages}{Importing\nPackages}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/exporting-packages}{Exporting\nPackages}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{Resolving\nThird Party Library Package Dependencies}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{Deploying\nWARs (WAB Generator)}\n\n\\chapter{Resolving Third Party Library Package\nDependencies}\\label{resolving-third-party-library-package-dependencies}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay's OSGi framework lets you build applications composed of\nmultiple OSGi bundles (modules). For the framework to assemble the\nmodules into a working system, the modules must resolve their Java\npackage dependencies. In a perfect world, every Java library would be an\nOSGi module, but many libraries aren't. So how do you resolve the\npackages your project needs from non-OSGi third party libraries?\n\nHere is the main workflow for resolving third party Java library\npackages:\n\n\\textbf{Option 1 - Find an OSGi module of the library}: Projects, such\nas \\href{https://www.eclipse.org/orbit/}{Eclipse Orbit} and\n\\href{https://servicemix.apache.org/developers/source/bundles-source.html}{ServiceMix\nBundles}, convert hundreds of traditional Java libraries to OSGi\nmodules. Their artifacts are available at these locations:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://download.eclipse.org/tools/orbit/downloads/}{Eclipse\n  Orbit downloads (select a build)}\n\\item\n  \\href{https://mvnrepository.com/artifact/org.apache.servicemix.bundles}{ServiceMix\n  Bundles}\n\\end{itemize}\n\nDeploying the module to Liferay's OSGi framework lets you share it on\nthe system. If you find a module for the library you need,\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{deploy}\nit. Then\n\\href{/docs/7-2/customization/-/knowledge_base/c/specifying-dependencies}{add\na compile-only dependency} for it in your project. When you deploy your\nproject, the OSGi framework wires the dependency module to your\nproject's module or web application bundle (WAB). If you don't find an\nOSGi module based on the Java library, follow Option 2.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} Refrain from embedding library JARs that provide the same\n\\href{/docs/7-2/reference/-/knowledge_base/r/third-party-packages-portal-exports}{packages\nthat Liferay DXP or existing modules provide already}.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you're developing a WAR that requires a different\nversion of a third-party package that\n\\href{/docs/7-2/reference/-/knowledge_base/r/third-party-packages-portal-exports}{Liferay\nDXP or another module exports}, specify that package in your\n\\href{/docs/7-2/customization/-/knowledge_base/c/importing-packages}{\\texttt{Import-Package:}\nlist}. If the package provider is an OSGi module, publish its exported\npackages by deploying that module. Otherwise, rename the third-party\nlibrary (not an OSGi module) differently from the\n\\href{/docs/7-2/customization/-/knowledge_base/c/understanding-excluded-jars}{JAR\nthat the WAB generator excludes} and embed the JAR in your project.\n\n\\noindent\\hrulefill\n\n\\textbf{Option 2 - Resolve the Java packages privately in your project}:\nCopy \\emph{required packages} only from libraries into your project, if\nyou can or embed \\emph{libraries} wholesale, if you must. The rest of\nthis article shows you how to do these things.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Features for manipulating library packages are only\navailable to module projects that use bnd and the\n\\texttt{com.liferay.plugin} plugin, such as\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace} modules. WAR projects must embed libraries wholesale into\ntheir classpath.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: Liferay's Gradle plugin \\texttt{com.liferay.plugin}\nautomates several third party library configuration steps. The plugin is\nautomatically applied to\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace} Gradle module projects created using\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\nDev Studio DXP} or\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Liferay Blade\nCLI}.\n\nTo leverage the \\texttt{com.liferay.plugin} plugin outside of Liferay\nWorkspace, add code like the listing below to your Gradle project and\nupdate the version of the \\texttt{com.liferay.gradle.plugins} artifact\nto the latest version found in the repository:\n\n\\begin{verbatim}\n buildscript {\n     dependencies {\n         classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins\", version: \"4.0.4\"\n     }\n\n     repositories {\n         maven {\n             url \"https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/\"\n         }\n     }\n }\n\n apply plugin: \"com.liferay.plugin\"\n\\end{verbatim}\n\nIf you use Gradle without the \\texttt{com.liferay.plugin} plugin, you\nmust \\hyperref[embedding-libraries-using-gradle]{embed the third party\nlibraries wholesale}.\n\n\\noindent\\hrulefill\n\nThe recommended package resolution workflow is next.\n\n\\section{Library Package Resolution\nWorkflow}\\label{library-package-resolution-workflow}\n\nWhen you depend on a library JAR, much of the time you only need parts\nof it. Explicitly specifying only the Java packages you need makes your\nmodule more modular. This also keeps other modules that depend on your\nmodule from incorporating unneeded packages.\n\nHere's a configuration workflow for module projects that minimizes\ndependencies and Java package imports:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the library as a compile-only dependency (e.g.,\n  \\texttt{compileOnly} in Gradle,\n  \\texttt{\\textless{}scope\\textgreater{}provided\\textless{}/scope\\textgreater{}}\n  in Maven).\n\\item\n  Copy only the library packages you need by specifying them in a\n  conditional package instruction (\\texttt{Conditional-Package}) in your\n  \\texttt{bnd.bnd} file. Here are some examples:\n\n  \\texttt{Conditional-Package:\\ foo.common*} adds packages your module\n  uses such as \\texttt{foo.common}, \\texttt{foo.common-messages},\n  \\texttt{foo.common-web} to your module's class path.\n\n  \\texttt{Conditional-Package:\\ foo.bar.*} adds packages your module\n  uses such as \\texttt{foo.bar} and all its sub-packages (e.g.,\n  \\texttt{foo.bar.baz}, \\texttt{foo.bar.biz}, etc.) to your module's\n  class path.\n\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  your project}. If a class your module needs or class its dependencies\n  need isn't found, go back to main workflow \\textbf{Step 1 - Find an\n  OSGi module version of the library} to resolve it.\n\n  \\textbf{Important}: Resolving packages by using compile-only\n  dependencies and conditional package instructions assures you use only\n  the packages you need and avoids unnecessary transitive dependencies.\n  It's recommended to use the steps up to this point, as much as\n  possible, to resolve required packages.\n\\item\n  If a library package you depend on requires non-class files (e.g.,\n  DLLs, descriptors) from the library, then you might need to\n  \\hyperref[embedding-libraries-in-a-project]{embed the library\n  wholesale in your module}. This adds the entire library to your\n  module's classpath.\n\\end{enumerate}\n\nNext you'll learn how to embed libraries in your module project.\n\n\\section{Embedding Libraries in a\nProject}\\label{embedding-libraries-in-a-project}\n\nYou can use Gradle or Maven to embed libraries in your project. Below\nare examples for adding \\href{https://shiro.apache.org}{Apache Shiro}\nusing both build utilities.\n\n\\section{Embedding Libraries Using\nGradle}\\label{embedding-libraries-using-gradle}\n\nOpen your module's \\texttt{build.gradle} file and add the library as a\ndependency in the \\texttt{compileInclude} configuration:\n\n\\begin{verbatim}\ndependencies {\n    compileInclude group: 'org.apache.shiro', name: 'shiro-core', version: '1.1.0'\n}\n\\end{verbatim}\n\nThe \\texttt{com.liferay.plugin} plugin's \\texttt{compileInclude}\nconfiguration is transitive. The \\texttt{compileInclude} configuration\nembeds the artifact and all its dependencies in a \\texttt{lib} folder in\nthe module's JAR. Also, it adds the artifact JARs to the module's\n\\texttt{Bundle-ClassPath} manifest header.\n\n\\textbf{Note}: The \\texttt{compileInclude} configuration does not\ndownload transitive\n\\href{https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html}{optional\ndependencies}. If your module requires such artifacts, add them as you\nwould another third party library.\n\n\\textbf{Note:} If the library you've added as a dependency in your\n\\texttt{build.gradle} file has transitive dependencies, you can\nreference them by name in an \\texttt{-includeresource:} instruction\nwithout having to add them explicitly to the dependency list. See how\nit's used in the Maven section next.\n\n\\section{Embedding a Library Using\nMaven}\\label{embedding-a-library-using-maven}\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your project's \\texttt{pom.xml} file and add the library as a\n  dependency in the \\texttt{provided} scope:\n\n\\begin{verbatim}\n<dependency>\n  <groupId>org.apache.shiro</groupId>\n  <artifactId>shiro-core</artifactId>\n  <version>1.1.0</version>\n  <scope>provided</scope>\n</dependency>\n\\end{verbatim}\n\\item\n  Open your module's \\texttt{bnd.bnd} file and add the library to an\n  \\texttt{-includeresource} instruction:\n\n\\begin{verbatim}\n-includeresource: META-INF/lib/shiro-core.jar=shiro-core-[0-9]*.jar;lib:=true\n\\end{verbatim}\n\n  This instruction adds the \\texttt{shiro-core-{[}version{]}.jar} file\n  as an included resource in the module's \\texttt{META-INF/lib} folder.\n  The \\texttt{META-INF/lib/shiro-core.jar} is your module's embedded\n  library. The expression \\texttt{{[}0-9{]}*} helps the build tool match\n  the library version to make available on the module's class path. The\n  \\texttt{lib:=true} directive adds the embedded JAR to the module's\n  class path via the \\texttt{Bundle-Classpath} manifest header.\n\\end{enumerate}\n\nLastly, if after embedding a library you get unresolved imports when\ntrying to deploy to Liferay, you might need to blacklist some imports:\n\n\\begin{verbatim}\nImport-Package:\\\n    !foo.bar.baz,\\\n    *\n\\end{verbatim}\n\nThe \\texttt{*} character represents all packages that the module refers\nto explicitly. Bnd detects the referenced packages.\n\nCongratulations! Resolving all of your module's package dependencies,\nespecially those from traditional Java libraries, is a quite an\naccomplishment.\n\n\\section{Related Topics}\\label{related-topics-2}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/importing-packages}{Importing\nPackages}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/exporting-packages}{Exporting\nPackages}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Creating\na Project}\n\n\\chapter{Understanding Excluded JARs}\\label{understanding-excluded-jars}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Module\\%20Framework}{Portal\nproperty \\texttt{module.framework.web.generator.excluded.paths}}\ndeclares JARs that are stripped from all Liferay DXP\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{generated\nWABs}. These JARs are excluded from web application bundles (WABs)\nbecause Liferay DXP provides them already. All JARs listed for this\nproperty are excluded from a WAB, even if the WAB lists the JAR in a\n\\texttt{portal-dependency-jars} property in its\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/liferay-plugin-package_7_2_0.properties.html}{\\texttt{liferay-plugin-package.properties}}\nfile.\n\nIf your WAR requires different versions of the packages Liferay DXP\nexports, you must include them in JARs named differently from the ones\n\\texttt{module.framework.web.generator.excluded.paths} excludes.\n\nFor example, Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/core/portal-bootstrap/system.packages.extra.bnd}{\\texttt{system.packages.extra}}\nmodule exports Spring Framework version 4.1.9 packages:\n\n\\begin{verbatim}\nExport-Package:\\\n    ...\n    org.springframework.*;version='4.1.9',\\\n    ...\n\\end{verbatim}\n\nLiferay DXP uses the\n\\texttt{module.framework.web.generator.excluded.paths} portal property\nto exclude their JARs.\n\n\\begin{verbatim}\nmodule.framework.web.generator.excluded.paths=\\\n    ...\n    WEB-INF/lib/spring-aop.jar,\\\n    WEB-INF/lib/spring-aspects.jar,\\\n    WEB-INF/lib/spring-beans.jar,\\\n    WEB-INF/lib/spring-context.jar,\\\n    WEB-INF/lib/spring-context-support.jar,\\\n    WEB-INF/lib/spring-core.jar,\\\n    WEB-INF/lib/spring-expression.jar,\\\n    WEB-INF/lib/spring-jdbc.jar,\\\n    WEB-INF/lib/spring-jms.jar,\\\n    WEB-INF/lib/spring-orm.jar,\\\n    WEB-INF/lib/spring-oxm.jar,\\\n    WEB-INF/lib/spring-tx.jar,\\\n    WEB-INF/lib/spring-web.jar,\\\n    WEB-INF/lib/spring-webmvc.jar,\\\n    WEB-INF/lib/spring-webmvc-portlet.jar,\\\n    ...\n\\end{verbatim}\n\nTo use a different Spring Framework version in your WAR, you must name\nthe corresponding Spring Framework JARs differently from the\nglob-patterned JARs\n\\texttt{module.framework.web.generator.excluded.paths} lists.\n\nFor example, to use Spring Framework version 3.0.7's Spring AOP JAR,\ninclude it in your plugin's \\texttt{WEB-INF/lib} but name it something\nother than \\texttt{spring-aop.jar}. Adding the version to the JAR name\n(i.e., \\texttt{spring-aop-3.0.7.RELEASE.jar}) differentiates it from the\nexcluded JAR and prevents it from being stripped from the WAB (the\nbundled WAR).\n\n\\section{Related Topics}\\label{related-topics-3}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Configuring\nDependencies}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{Deploying\nWARs (WAB Generator)}\n\n\\chapter{Using the Felix Gogo Shell}\\label{using-the-felix-gogo-shell}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe Gogo shell provides a way to interact with Liferay DXP's module\nframework. You can\n\n\\begin{itemize}\n\\tightlist\n\\item\n  dynamically install/uninstall bundles\n\\item\n  examine package dependencies\n\\item\n  examine extension points\n\\item\n  list service references\n\\item\n  etc.\n\\end{itemize}\n\nThere are two ways you can access the Gogo shell.\n\nThe recommended way to access the Gogo shell for a production\nenvironment is through the Control Panel. Accessing it there is the most\nsecure way to use the Gogo shell. You can set permissions in your\nLiferay DXP instance to only give certain people access to it. The Gogo\nshell is extremely powerful and should only be given to trusted admins,\nas you can manipulate the platform's core functionality. You can access\nthe Gogo shell in the Control Panel by navigating to\n\\emph{Configuration} → \\emph{Gogo Shell}.\n\nYou can also interact with Liferay DXP's module framework via a local\ntelnet session. This is only recommended when you're developing your\nLiferay DXP instance. This is not recommended for production\nenvironments.\n\nTo open the Gogo shell via telnet, execute the following command:\n\n\\begin{verbatim}\ntelnet localhost 11311\n\\end{verbatim}\n\nRunning this command requires a local running instance of Liferay DXP\nand your machine's telnet command line utilities enabled. You must also\nhave\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes\\#enabling-developer-mode-manually}{Developer\nMode enabled}.\n\nTo disconnect the session, execute the \\texttt{disconnect} command.\nAvoid using the following commands, which stop the OSGi framework:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{close}\n\\item\n  \\texttt{exit}\n\\item\n  \\texttt{shutdown}\n\\end{itemize}\n\nIf you have\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}\ninstalled and the telnet capability enabled, you can run the Gogo shell\nvia Blade command too:\n\n\\begin{verbatim}\nblade sh <gogoShellCommand>\n\\end{verbatim}\n\nHere are some useful Gogo shell commands:\n\n\\texttt{b\\ {[}BUNDLE\\_ID{]}}: lists information about a specific bundle\nincluding the bundle's symbolic name, bundle ID, data root, registered\n(provided) and used services, imported and exported packages, and more\n\n\\texttt{diag\\ {[}BUNDLE\\_ID{]}}: lists information about why the\nspecified bundle is not working (e.g., unresolved dependencies, etc.)\n\n\\texttt{headers\\ {[}BUNDLE\\_ID{]}}: lists metadata about the bundle from\nthe bundle's \\texttt{MANIFEST.MF} file\n\n\\texttt{help}: lists all the available Gogo shell commands. Notice that\neach command has two parts to its name, separated by a colon. For\nexample, the full name of the \\texttt{help} command is\n\\texttt{felix:help}. The first part is the command scope while the\nsecond part is the command function. The scope allows commands with the\nsame name to be disambiguated. E.g., scope allows the\n\\texttt{felix:refresh} command to be distinguished from the\n\\texttt{equinox:refresh} command.\n\n\\texttt{help\\ {[}COMMAND\\_NAME{]}}: lists information about a specific\ncommand including a description of the command, the scope of the\ncommand, and information about any flags or parameters that can be\nsupplied when invoking the command.\n\n\\texttt{inspect\\ capability\\ service\\ {[}BUNDLE\\_ID{]}}: lists services\nexposed by a bundle\n\n\\texttt{install\\ {[}PATH\\_TO\\_JAR\\_FILE{]}}: installs the specified\nbundle into Liferay's module framework\n\n\\texttt{lb}: lists all of the bundles installed in Liferay's module\nframework. Use the \\texttt{-s} flag to list the bundles using the\nbundles' symbolic names.\n\n\\texttt{packages\\ {[}PACKAGE\\_NAME{]}}: lists all of the named package's\ndependencies\n\n\\texttt{scr:list}: lists all of the components registered in the module\nframework (\\emph{scr} stands for service component runtime)\n\n\\texttt{scr:info\\ {[}COMPONENT\\_NAME{]}}: lists information about a\nspecific component including the component's description, services,\nproperties, configuration, references, and more.\n\n\\texttt{services}: lists all of the services that have been registered\nin Liferay's module framework\n\n\\texttt{start\\ {[}BUNDLE\\_ID{]}}: starts the specified bundle\n\n\\texttt{stop\\ {[}BUNDLE\\_ID{]}}: stops the specified bundle\n\n\\texttt{uninstall\\ {[}BUNDLE\\_ID{]}}: uninstalls the specified bundle\nfrom Liferay's module framework. This does not remove the specified\nbundle from Liferay's module framework; it's hidden from Gogo's\n\\texttt{lb} command, but is still present. Adding a new version of the\nuninstalled bundle, therefore, will not reinstall it; it will update the\ncurrently hidden uninstalled version. To remove a bundle from Liferay's\nmodule framework permanently, manually delete it from the\n\\texttt{LIFERAY\\_HOME/osgi} folder. For more information on the\n\\texttt{uninstall} command, see OSGi's\n\\href{https://osgi.org/javadoc/r6/core/org/osgi/framework/Bundle.html\\#uninstall()}{uninstall}\ndocumentation.\n\nFor more information about the Gogo shell, visit\n\\href{http://felix.apache.org/documentation/subprojects/apache-felix-gogo.html}{Apache's\nofficial documentation}.\n\n\\chapter{Importing Packages}\\label{importing-packages}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nPlugins often must use Java classes from packages outside of themselves.\nAnother OSGi bundle (a module or an OSGi Web Application Bundle) in the\nOSGi framework must\n\\href{/docs/7-2/customization/-/knowledge_base/c/exporting-packages}{export}\na package for your plugin to import it.\n\nWhen an OSGi bundle (bundle) is set up to import packages, the OSGi\nframework finds other registered bundles that export the needed packages\nand wires them to the importing bundle. At run time, the importing\nbundle gets the class from the wired bundle that exports the class's\npackage.\n\nFor this to happen, a bundle's \\texttt{META-INF/MANIFEST.MF} file must\nspecify the\n\\href{https://bnd.bndtools.org/heads/import_package.html}{\\texttt{Import-Package}}\nOSGi manifest header with a comma-separated list of the Java packages it\nneeds. For example, if a bundle needs classes from the\n\\texttt{javax.portlet} and \\texttt{com.liferay.portal.kernel.util}\npackages, it must specify them like so:\n\n\\begin{verbatim}\nImport-Package: javax.portlet,com.liferay.portal.kernel.util,*\n\\end{verbatim}\n\nThe \\texttt{*} character represents all packages that the module refers\nto explicitly. Bnd detects the referenced packages.\n\nImport packages must sometimes be specified manually, but not always.\nConveniently, Liferay DXP\n\\href{/docs/7-2/reference/-/knowledge_base/r/project-templates}{project\ntemplates} and\n\\href{/docs/7-2/reference/-/knowledge_base/r/tooling}{tools}\nautomatically detect the packages a bundle uses and add them to the\npackage imports in the bundle's manifest. Here are the different package\nimport scenarios:\n\n\\begin{itemize}\n\\item\n  \\hyperref[automatic-package-import-generation]{Automatic Package\n  Import Generation}\n\\item\n  \\hyperref[manually-adding-package-imports]{Manually Adding Package\n  Imports}\n\\end{itemize}\n\nLet's explore how package imports are specified in these scenarios.\n\n\\section{Automatic Package Import\nGeneration}\\label{automatic-package-import-generation}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/project-templates}{Gradle\nand Maven module projects} created using\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI},\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven}{Liferay's Maven\narchetypes}, or\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\nDev Studio DXP} use \\href{http://bnd.bndtools.org/}{bnd}. On building\nsuch a project's module JAR, bnd detects the packages the module uses\nand generates a \\texttt{META-INF/MANIFEST.MF} file whose\n\\texttt{Import-Package} header specifies the packages.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Liferay's Maven module archetypes use the\n\\texttt{bnd-maven-plugin}. Liferay's Gradle module project templates use\n\\href{https://github.com/TomDmitriev/gradle-bundle-plugin}{a third-party\nGradle plugin} to invoke bnd.\n\n\\noindent\\hrulefill\n\nFor example, suppose you're developing a Liferay module using Maven or\nGradle. In most cases, you specify your module's dependencies in your\n\\texttt{pom.xml} or \\texttt{build.gradle} file. At build time, the Maven\nor Gradle module plugin reads your \\texttt{pom.xml} or\n\\texttt{build.gradle} file and bnd adds the required\n\\texttt{Import-Package} headers to your module JAR's\n\\texttt{META-INF/MANIFEST.MF}.\n\nHere's an example dependencies section from a module's\n\\texttt{build.gradle} file:\n\n\\begin{verbatim}\ndependencies {\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"2.0.0\"\n    compileOnly group: \"javax.portlet\", name: \"portlet-api\", version: \"2.0\"\n    compileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\", version: \"1.3.0\"\n}\n\\end{verbatim}\n\nAnd here's the \\texttt{Import-Package} header that's generated in the\nmodule JAR's \\texttt{META-INF/MANIFEST.MF} file:\n\n\\begin{verbatim}\nImport-Package: com.liferay.portal.kernel.portlet.bridges.mvc;version=\n\"[1.0,2)\",com.liferay.portal.kernel.util;version=\"[7.0,8)\",javax.nami\nng,javax.portlet;version=\"[2.0,3)\",javax.servlet,javax.servlet.http,j\navax.sql\n\\end{verbatim}\n\nNote that your build file need only specify artifact dependencies. bnd\nexamines your module's class path to determine which packages from those\nartifacts contain classes your application uses and imports the\npackages. The examination includes all classes found in the class\npath--even those from embedded\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{third\nparty library JARs}.\n\nRegarding classes used by a plugin WAR,\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{Liferay's\nWAB Generator} detects their use in the WAR's JSPs, descriptor files,\nand classes (in \\texttt{WEB-INF/classes} and embedded JARs). The WAB\nGenerator searches the \\texttt{web.xml}, \\texttt{liferay-web.xml},\n\\texttt{portlet.xml}, \\texttt{liferay-portlet.xml}, and\n\\texttt{liferay-hook.xml} descriptor files. It adds package imports for\nclasses that are neither found in the plugin's \\texttt{WEB-INF/classes}\nfolder nor in its embedded JARs.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Packages for Java APIs, such as Java Portlet, aren't\nsemantically versioned but have Portable Java Contracts. Each API's\ncontract specifies the JSR it satisfies. Bundles that use these APIs\nmust specify requirements on the API contracts. The contract requirement\nspecifies your bundle's relationship with the imported API packages. If\nthe system you're running does \\emph{not} provide the exact contract,\nyour bundle does not resolve. Resolving the missing package is better\nthan handling an incompatibility failure during execution.\n\n\\begin{itemize}\n\\item\n  \\textbf{Blade CLI and Liferay Dev Studio DXP module projects} specify\n  Portable Java Contracts automatically! For example, if your Blade CLI\n  or Liferay Dev Studio DXP module uses the Java Portlet API and you\n  compile against the Java Portlet 2.0 artifact, a contract requirement\n  for the package is added to your module's manifest.\n\\item\n  \\textbf{Module projects that use bnd but are not created using Blade\n  CLI or Liferay Dev Studio DXP} must specify contracts in their\n  \\texttt{bnd.bnd} file. For example, here are contract instructions for\n  Java Portlet and Java Servlet APIs:\n\n\\begin{verbatim}\n-contract: JavaPortlet,JavaServlet\n\\end{verbatim}\n\n  At build time, bnd adds the contract instructions to your module's\n  manifest. It adds a requirement for the first version of the API found\n  in your classpath and \\emph{removes} version range information from\n  \\texttt{Import-Package} entries for corresponding API packages---the\n  package version information isn't needed.\n\\item\n  \\textbf{Projects that don't use bnd} must specify contracts in their\n  OSGi bundle manifest. For example, here's the specified contract for\n  \\texttt{JavaPortlet} 2.0, which goes in your\n  \\texttt{META-INF/MANIFEST.MF} file:\n\n\\begin{verbatim}\nImport-Package: javax.portlet\nRequire-Capability: osgi.contract;filter:=(&(osgi.contract=JavaPortlet)(version=2.0))\n\\end{verbatim}\n\\end{itemize}\n\nFor Portable Java Contract details, see\n\\href{https://www.osgi.org/portable-java-contract-definitions/}{Portable\nJava Contract Definitions}.\n\n\\noindent\\hrulefill\n\n\\section{Manually Adding Package\nImports}\\label{manually-adding-package-imports}\n\nThe WAB Generator and bnd don't add package imports for classes\nreferenced in these places:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Unrecognized descriptor file\n\\item\n  Custom or unrecognized descriptor element or attribute\n\\item\n  Reflection code\n\\item\n  Class loader code\n\\end{itemize}\n\nIn such cases, you must manually determine these packages and specify an\n\\texttt{Import-Package} OSGi header that includes these packages and the\npackages that Bnd detects automatically. The \\texttt{Import-Package}\nheader belongs in the location appropriate to your project type:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.2727}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.7273}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nProject type\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n\\texttt{Import-Package} header location\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nModule (uses bnd) & \\texttt{{[}project{]}/bnd.bnd} \\\\\nModule (doesn't use bnd) &\n\\texttt{{[}module\\ JAR{]}/META-INF/MANIFEST.MF} \\\\\nTraditional Liferay plugin WAR &\n\\texttt{WEB-INF/liferay-plugin-package.properties} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nHere's an example of adding a package called\n\\texttt{com.liferay.docs.foo} to the list of referenced packages that\nBnd detects automatically:\n\n\\begin{verbatim}\nImport-Package:\\\n    com.liferay.docs.foo,\\\n    *\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{WAB\nGenerator} refrains from adding WAR project embedded third-party JARs to\na WAB if\n\\href{/docs/7-2/customization/-/knowledge_base/c/understanding-excluded-jars}{Liferay\nDXP already exports the JAR's packages}.\n\nIf your WAR requires a different version of a third-party package that\nLiferay DXP exports, specify that package in your\n\\texttt{Import-Package:} list. Then if the package provider is an OSGi\nmodule, publish its exported packages by deploying the module. If the\npackage provider is not an OSGi module, follow the instructions for\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{adding\nthird-party libraries}.\n\n\\noindent\\hrulefill\n\nPlease see the\n\\href{https://bnd.bndtools.org/heads/import_package.html}{\\texttt{Import-Package}}\nheader documentation for more information.\n\nCongratulations! Now you can import all kinds of packages for your\nmodules and plugins to use.\n\n\\section{Related Topics}\\label{related-topics-4}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Configuring\nDependencies}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{Deploying\nWARs (WAB Generator)}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/project-templates}{Project\nTemplates}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven}{Liferay's Maven\nArchetypes}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\nDev Studio DXP}\n\n\\chapter{Exporting Packages}\\label{exporting-packages}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAn OSGi bundle's Java packages are private by default. To expose a\npackage, you must explicitly export it. This way you share only the\nclasses you want to share. Exporting a package in your OSGi bundle\n(bundle) manifest makes all the package's classes available for other\nbundles to\n\\href{/docs/7-2/customization/-/knowledge_base/c/importing-packages}{import}.\n\nTo export a package, add it to your module's or plugin's\n\\texttt{Export-Package} OSGi header. A header exporting\n\\texttt{com.liferay.petra.io} and \\texttt{com.liferay.petra.io.unsync}\nwould look like this:\n\n\\begin{verbatim}\nExport-Package:\\\ncom.liferay.petra.io,\\\ncom.liferay.petra.io.unsync\n\\end{verbatim}\n\nThe correct location for the header depends on your project's type:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}ll@{}}\n\\toprule\\noalign{}\nProject Type & \\texttt{Export-Package} header location \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nModule JAR (uses bnd) & \\texttt{{[}project{]}/bnd.bnd} \\\\\nModule JAR (doesn't use bnd) &\n\\texttt{{[}module\\ JAR{]}/META-INF/MANIFEST.MF} \\\\\nPlugin WAR & \\texttt{WEB-INF/liferay-plugin-package.properties} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nModule projects created using\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI},\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven}{Liferay's Maven\narchetypes}, or\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\nDev Studio DXP} use \\href{http://bnd.bndtools.org/}{bnd}. On building\nsuch a project's module JAR, bnd propagates the OSGi headers from the\nproject's \\texttt{bnd.bnd} file to the JAR's\n\\texttt{META-INF/MANIFEST.MF}.\n\nIn module projects that don't use bnd, you must manually add package\nexports to an \\texttt{Export-Package} header in the module JAR's\n\\texttt{META-INF/MANIFEST.MF}.\n\nIn plugin WAR projects, you must add package exports to an\n\\texttt{Export-Package} header in the project's\n\\texttt{WEB-INF/liferay-plugin-package.properties}. On copying the WAR\ninto the \\texttt{{[}Liferay\\ Home{]}/deploy} folder, the\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{WAB\nGenerator} propagates the OSGi headers from the WAR's\n\\texttt{liferay-plugin-package.properties} file to the\n\\texttt{META-INF/MANIFEST.MF} file in the generated Web Application\nBundle (WAB).\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} bnd makes a module's exported packages\n\\emph{substitutable}. That is, the OSGi framework can substitute your\nmodule's exported package with a compatible package of the same name,\nbut potentially different version, that's exported from a different OSGi\nbundle. bnd enables this for your module by automatically making your\nmodule import every package it exports. In this way, your module can\nwork on its own, but can also work in conjunction with bundles that\nprovide a different (compatible) version, or even the same version, of\nthe package. A package from another bundle might provide better\n``wiring'' opportunities with other bundles.\n\\href{http://blog.osgi.org/2007/04/importance-of-exporting-nd-importing.html}{Peter\nKriens' blog post} provides more details on how substitutable exports\nworks.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Don't export the same package from different JARs.\nMultiple exports of the same package leads to ``split package'' issues,\nwhose side affects differ from case to case.\n\n\\noindent\\hrulefill\n\nNow you can share your module's or plugin's terrific {[}EDITOR: or\nterrible!{]} packages with other OSGi bundles!\n\n\\section{Related Topics}\\label{related-topics-5}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Configuring\nDependencies}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{Deploying\nWARs (WAB Generator)}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/project-templates}{Project\nTemplates}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven}{Liferay's Maven\nArchetypes}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\nDev Studio DXP}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/semantic-versioning}{Semantic\nVersioning}\n\n\\chapter{Semantic Versioning}\\label{semantic-versioning}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\n\\href{https://semver.org}{Semantic Versioning} is a three tiered\nversioning system that increments version numbers based on the type of\nAPI change introduced to a releasable software component. It's a\nstandard way of communicating programmatic compatibility of a package or\nmodule for dependent consumers and API implementations. If a package is\nprogrammatically (i.e., semantically) incompatible with a project,\n\\href{http://bnd.bndtools.org}{bnd} (used when building\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Liferay\ngenerated module projects}) fails that project's build immediately.\n\nThe semantic version format looks like this:\n\n\\begin{verbatim}\nMAJOR.MINOR.MICRO\n\\end{verbatim}\n\nCertain events force each tier to increment:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{MAJOR:} an incompatible, API-breaking change is made\n\\item\n  \\emph{MINOR:} a change that affects only providers of the API, or new\n  backwards- compatible functionality is added\n\\item\n  \\emph{MICRO:} a backwards-compatible bug fix is made\n\\end{itemize}\n\nFor more details on semantic versioning, see the official\n\\href{https://semver.org/}{Semantic Versioning} site and\n\\href{http://www.osgi.org/wp-content/uploads/SemanticVersioning1.pdf}{OSGi\nAlliance's Semantic Versioning} technical whitepaper.\n\nAll of Liferay DXP's modules use Semantic Versioning.\n\nFollowing Semantic Versioning is especially important because Liferay\nDXP is a modular platform containing hundreds of independent OSGi\nmodules. With many independent modules containing a slew of\ndependencies, releasing new package versions can quickly become\nterrifying. With this complex intertwined system of dependencies, you\nmust meticulously manage your own project's API versions to ensure\ncompatibility for those who leverage it. With Semantic Versioning's\nstraightforward system and the help of\n\\href{/docs/7-2/reference/-/knowledge_base/r/tooling}{Liferay tooling},\nmanaging your module project's versions is easy.\n\n\\section{Baselining Your Project}\\label{baselining-your-project}\n\nFollowing Semantic Versioning manually seems deceptively easy. There's a\nsad history of good-intentioned developers updating their projects'\nsemantic versions manually, only to find out later they made a mistake.\nThe truth is, it's hard to anticipate the ramifications of a simple\nupdate. To avoid this, you can \\emph{baseline} your project after it has\nbeen updated. Baselining verifies that the Semantic Versioning rules are\nobeyed by your project. This can catch many obvious API changes that are\nnot so obvious to humans. Care must always be taken, however, when\nmaking any kind of code change because this tool is not smart enough to\nidentify compatibility changes not represented in the signatures of Java\nclasses or interfaces, or in API \\emph{use} changes (e.g., assumptions\nabout method call order, or changes to input and/or output encoding).\nBaseline, as the name implies, does give you a certain measure of\n\\emph{baseline} comfort that a large class of compatibility issues won't\nsneak past you.\n\nYou can use Liferay's Baseline Gradle plugin to provide baselining\ncapabilities. Add it to your Gradle build configuration and execute the\nfollowing command:\n\n\\begin{verbatim}\n./gradlew baseline\n\\end{verbatim}\n\nSee the\n\\href{/docs/7-2/reference/-/knowledge_base/r/baseline-gradle-plugin}{Baseline\nGradle Plugin} article for configuration details. This plugin is not\nprovided in\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace} by default.\n\nWhen you run the \\texttt{baseline} command, the plugin baselines your\nnew module against the latest released non-snapshot module (i.e., the\nbaseline). That is, it compares the public exported API of your new\nmodule with the baseline. If there are any changes, it uses the OSGi\nSemantic Versioning rules to calculate the minimum new version. If your\nnew module has a lower version, errors are thrown.\n\nWith baselining, your project's Semantic Versioning is as accurate as\nits API expresses.\n\n\\section{Managing Artifact and Dependency\nVersions}\\label{managing-artifact-and-dependency-versions}\n\nThere are two ways to track your project's artifact and dependency\nversions with Semantic Versioning:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Range of versions\n\\item\n  Exact version (one-to-one)\n\\end{itemize}\n\nYou should track a range of versions if you intend to build your project\nfor multiple versions of Liferay DXP and maintain maximum compatibility.\nIn other words, if several versions of a package work for an app, you\ncan configure the app to use any of them. What's more, bnd automatically\ndetermines the semantically compatible range of each package a module\ndepends on and records the range to the module's manifest.\n\nFor help with version range syntax, see the\n\\href{https://osgi.org/specification/osgi.core/7.0.0/framework.module.html\\#i3189032}{OSGi\nSpecifications}.\n\nA version range for imported packages in an OSGi bundle's\n\\texttt{bnd.bnd} looks like this:\n\n\\begin{verbatim}\nImport-Package: com.liferay.docs.test; version=\"[1.0.0,2.0.0)\"\n\\end{verbatim}\n\nPopular build tools also follow this syntax. In Gradle, a version range\nfor a dependency looks like this:\n\n\\begin{verbatim}\ncompile group: \"com.liferay.portal\", name: \"com.liferay.portal.test\", version: \"[1.0.0,2.0.0)\"\n\\end{verbatim}\n\nIn Maven, it looks like this:\n\n\\begin{verbatim}\n<groupId>com.liferay.portal</groupId>\n<artifactId>com.liferay.portal.test</artifactId>\n<version>[1.0.0,2.0.0)</version>\n\\end{verbatim}\n\nSpecifying the latest release version can also be considered a range of\nversions with no upper limit. For example, in Gradle, it's specified as\n\\texttt{version:\\ \"latest.release\"}. This can be done in Maven 2.x with\nthe usage of the version marker \\texttt{RELEASE}. This is not possible\nif you're using Maven 3.x. See \\href{https://gradle.org/docs}{Gradle}\nand \\href{http://maven.apache.org/guides/}{Maven}'s respective docs for\nmore information.\n\nTracking a range of versions comes with a price. It's hard to reproduce\nold builds when you're debugging an issue. It also comes with the risk\nof differing behaviors depending on the version used. Also, relying on\nthe latest release could break compatibility with your project if a\nmajor change is introduced. You should proceed with caution when\nspecifying a range of versions and ensure your project is tested on all\nincluded versions.\n\nTracking a dependency's exact version is much safer, but is less\nflexible. This might limit you to a specific version of Liferay DXP. You\nwould also be locked in to APIs that only exist for that specific\nversion. This means your module is much easier to test and has less\nchance for unexpected failures.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When specifying package versions in your \\texttt{bnd.bnd}\nfile, exact versions are typically specified like this:\n\\texttt{version=\"1.1.2\"}. However, this syntax is technically a range;\nit is interpreted as {[}1.1.2, ∞). Therefore, if a higher version of the\npackage is available, it's used instead of the version you specified.\nFor these cases, it may be better to specify a version range for\ncompatible versions that have been tested. If you want to specify a true\nexact match, the syntax is like this: \\texttt{{[}1.1.2{]}}. See the\n\\href{https://osgi.org/specification/osgi.core/7.0.0/framework.module.html\\#i3189032}{Version\nRange} section in the OSGi specifications for more info.\n\nGradle and Maven use exact versions when only one version is specified.\n\n\\noindent\\hrulefill\n\nYou now know the pros and cons for tracking dependencies as a range and\nas an exact match.\n\n\\section{Related Topics}\\label{related-topics-6}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/importing-packages}{Importing\nPackages}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/exporting-packages}{Exporting\nPackages}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Configuring\nDependencies}\n\n\\chapter{Deploying WARs (WAB\nGenerator)}\\label{deploying-wars-wab-generator}\n\nYou can create applications for Liferay DXP as Java EE-style Web\nApplication ARchive (WAR) artifacts or as Java ARchive (JAR) OSGi bundle\nartifacts. Bean Portlets, PortletMVC4Spring Portlets, and JSF Portlets\nmust be packaged as WAR artifacts because their frameworks are designed\nfor Java EE. Therefore, they expect a WAR layout and require Java EE\nresources such as the \\texttt{WEB-INF/web.xml} descriptor.\n\nLiferay provides a way for these WAR-styled plugins to be deployed and\ntreated like OSGi modules by Liferay's OSGi runtime. They can be\nconverted to \\emph{WABs}.\n\nLiferay DXP supports the OSGi Web Application Bundle (WAB) standard for\ndeployment of Java EE style WARs. Simply put, a WAB is an archive that\nhas a WAR layout and contains a \\texttt{META-INF/MANIFEST.MF} file with\nthe \\texttt{Bundle-SymbolicName} OSGi directive. A WAB is an OSGi\nbundle. Although the project source has a WAR layout, the artifact\nfilename may end with either the \\texttt{.jar} or \\texttt{.war}\nextension.\n\nLiferay only supports the use of WABs that have been auto-generated by\nthe WAB Generator. The WAB Generator transforms a traditional WAR-style\nplugin into a WAB during deployment. So what exactly does the WAB\nGenerator do to a WAR file to transform it into a WAB?\n\nThe WAB Generator detects packages referenced in the plugin WAR's JSPs,\ndescriptor files, and classes (in \\texttt{WEB-INF/classes} and embedded\nJARs). The descriptor files include \\texttt{web.xml},\n\\texttt{liferay-web.xml}, \\texttt{portlet.xml},\n\\texttt{liferay-portlet.xml}, and \\texttt{liferay-hook.xml}. The WAB\nGenerator verifies whether the detected packages are in the plugin's\n\\texttt{WEB-INF/classes} folder or in an embedded JAR in the\n\\texttt{WEB-INF/lib} folder. Packages that aren't found in either\nlocation are added to an \\texttt{Import-Package} OSGi header in the\nWAB's \\texttt{META-INF/MANIFEST.MF} file.\n\nTo import a package that is only referenced in the following types of\nlocations, you must add an \\texttt{Import-Package} OSGi header to the\nplugin's \\texttt{WEB-INF/liferay-plugin-package.properties} file and add\nthe package to that header's list of values.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Unrecognized descriptor file\n\\item\n  Custom or unrecognized descriptor element or attribute\n\\item\n  Reflection code\n\\item\n  Class loader code\n\\end{itemize}\n\n\\section{WAR versus WAB Structure}\\label{war-versus-wab-structure}\n\nThe WAB folder structure and WAR folder structure differ. Consider the\nfollowing folder structure of a WAR-style portlet.\n\n\\textbf{WAR}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-war-portlet}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n      \\item\n        \\texttt{webapp}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{WEB-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{classes}\n          \\item\n            \\texttt{lib}\n          \\item\n            \\texttt{resources}\n          \\item\n            \\texttt{views}\n          \\item\n            \\texttt{liferay-display.xml}\n          \\item\n            \\texttt{liferay-plugin-package.properties}\n          \\item\n            \\texttt{liferay-portlet.xml}\n          \\item\n            \\texttt{portlet.xml}\n          \\item\n            \\texttt{web.xml}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\end{itemize}\n\nWhen a WAR-style portlet is deployed to Liferay DXP and processed by the\nWAB Generator, the portlet's folder structure is transformed.\n\n\\textbf{WAB}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-war-portlet-that-is-now-a-wab}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{META-INF}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{MANIFEST.MF}\n    \\end{itemize}\n  \\item\n    \\texttt{WEB-INF}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{classes}\n    \\item\n      \\texttt{lib}\n    \\item\n      \\texttt{resources}\n    \\item\n      \\texttt{views}\n    \\item\n      \\texttt{liferay-display.xml}\n    \\item\n      \\texttt{liferay-plugin-package.properties}\n    \\item\n      \\texttt{liferay-portlet.xml}\n    \\item\n      \\texttt{portlet.xml}\n    \\item\n      \\texttt{web.xml}\n    \\end{itemize}\n  \\end{itemize}\n\\end{itemize}\n\nThe major difference is the addition of the\n\\texttt{META-INF/MANIFEST.MF} file. The WAB Generator automatically\ngenerates an OSGi-ready manifest file. If you want to affect the content\nof the manifest file, you can place bnd directives and OSGi headers\ndirectly into your plugin's \\texttt{liferay-plugin-package.properties}\nfile.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Adding a \\texttt{bnd.bnd} file or a build-time plugin\n(e.g., \\texttt{bnd-maven-plugin}) to your WAR plugin is pointless,\nbecause the generated WAB cannot use them.\n\n\\noindent\\hrulefill\n\n\\section{Deploying a WAR}\\label{deploying-a-war}\n\nTo deploy a WAB based on your WAR plugin, copy your WAR plugin to your\nLiferay DXP instance's \\texttt{deploy/} folder in your\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{\\texttt{{[}Liferay\\ Home{]}}}.\n\n\\section{Saving a Copy of the WAB}\\label{saving-a-copy-of-the-wab}\n\nOptionally, save the WAB to a local folder. This gives you the\nopportunity to inspect the generated WAB. To store generated WABs, add\nthe following portal properties to a\n\\texttt{{[}Liferay\\ Home{]}/portal-ext.properties} file. Then restart\nLiferay DXP:\n\n\\begin{verbatim}\nmodule.framework.web.generator.generated.wabs.store=true\nmodule.framework.web.generator.generated.wabs.store.dir=${module.framework.base.dir}/wabs\n\\end{verbatim}\n\nThese properties instruct the WAB generator to store generated WABs in\nyour Liferay instance's \\texttt{osgi/wabs/} folder. The generated WABs\nhave the same structure as the example WAB structure listed above. The\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Module\\%20Framework\\%20Web\\%20Application\\%20Bundles}{Module\nFramework Web Application Bundles} properties section explains more\ndetails.\n\nAwesome! You have deployed your WAR plugin as a WAB and you know how to\nsave a copy of the WAB to examine it!\n\n\\section{Related Topics}\\label{related-topics-7}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Creating\na Project}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploying\na Project}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/web-front-ends}{Developing Web\nFront-Ends}\n\n\\chapter{Architecture}\\label{architecture}\n\nLiferay DXP architecture comprises these parts:\n\n\\textbf{Core:} Bootstraps Liferay DXP and its frameworks. The Core\nprovides a runtime environment for managing services, UI components, and\ncustomizations.\n\n\\textbf{Services:} Liferay and custom functionality is exposed via Java\nAPIs and web APIs.\n\n\\textbf{UI:} The optional web application UI for adding portals, sites,\npages, widgets, and content.\n\nYou can use the Liferay DXP UI and services together or focus solely on\nusing services via\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/headless-rest-apis}{REST\nweb APIs}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/architecture-options.png}\n\\caption{Liferay DXP portals and Sites contain content and widgets.\nLiferay DXP can also be used ``headless''---without the UI.}\n\\end{figure}\n\nThe architecture satisfies these requirements:\n\n\\begin{itemize}\n\\item\n  Supports using common development technologies\n\\item\n  Leverages development standards\n\\item\n  Facilitates swapping components\n\\item\n  Starts fast and performs well\n\\item\n  Its runtime is easy to configure and inspect\n\\end{itemize}\n\nThe Core supports UI and service deployments and orchestrates wiring\nthem together.\n\n\\section{Core}\\label{core}\n\nLiferay DXP is a web application that runs on your application server.\nThe Core bootstraps the application and\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/frameworks}{Liferay's\nbuilt-in frameworks}.\n\nThere are frameworks for these things and more:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media}{Adaptive\n  Media}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configurable-applications}{Application\n  Configuration}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/application-security}{Application\n  Security}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{Asset\n  Framework}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{File\n  Management}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/localization}{Localization}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/search}{Search}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/segmentation-personalization}{Segmentation\n  and Personalization}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes}{Upgrade\n  Processes}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/page-fragments}{Web\n  Fragments}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework}{Workflow}\n\\end{itemize}\n\nThe Core provides the component runtime environment for the frameworks,\nservices, and UI. Here are some component examples:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Services}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers}{Service\n  customizations}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{Portlets}\n  (templates, controllers, and resources)\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/web-front-ends}{JavaScript\n  applications} (templates, routers, and resources)\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters}{JSP\n  customization via Portlet Filters}\n\\item\n  \\href{(/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction)}{Theme}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-language-module}{Shared\n  Language Keys}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/screen-navigation-framework}{Navigation\n  components}\n\\end{itemize}\n\nThe following figure shows these component types in the runtime\nenvironment.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/component-runtime-environment.png}\n\\caption{The Core provides a runtime environment for components, such as\nthe ones here. New component implementations can extend or replace\nexisting implementations dynamically.}\n\\end{figure}\n\nThe runtime environment supports adding, replacing, and customizing\ncomponents on-the-fly. This makes the following scenarios possible:\n\n\\textbf{Replacement:} If the \\texttt{ServiceC\\ Impl\\ 2} component has a\nhigher ranking than existing component \\texttt{ServiceC\\ Impl\\ 1},\n\\texttt{ServiceC\\ Impl\\ 2} is used in its place.\n\n\\textbf{Customization:} The \\texttt{PortletA\\ Filter} intercepts and\nmodifies requests to and responses from \\texttt{PortletA}, affecting the\ncontent \\texttt{PortletA} displays.\n\nComponent WAR and module JAR projects install as\n\\href{https://www.osgi.org/}{OSGi bundles} (modules). Liferay DXP's OSGi\nframework defines the module lifecycle, enforces dependencies, defines\nthe class loading structure, and provides an API and CLI\n(\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Felix\nGogo Shell}) for managing modules and components. The Core is configured\nvia \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\nproperties files} and\n\\href{/docs/7-2/user/-/knowledge_base/u/server-administration}{Server\nAdministration panels}.\n\nThe service components provide business functionality.\n\n\\section{Services}\\label{services}\n\nBusiness logic is implemented in services deployed to the component\nruntime environment. Built-in Core services and framework services\noperate on Liferay models such as Users, Roles, Web Content, Documents\nand Media, and more. You can write and deploy custom services to\nintroduce new models and functionality. Service components can access\neach other in Liferay DXP via\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{dependency\ninjection}.\n\nFront-end applications invoke the services to do work. You can deploy\nJava-based applications that call services directly using the\n\\href{/docs/7-2/reference/-/knowledge_base/r/java-apis}{Java APIs}, and\nany web-based (Java and non-Java) application, whether deployed on\nLiferay DXP or not, can use the web APIs, which include\n\\href{/docs/7-2/appdev/-/knowledge_base/a/generating-apis-with-rest-builder}{headless\nREST APIs} that conform to the\n\\href{https://swagger.io/docs/specification/about/}{OpenAPI} standard\nand include\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/web-services}{plain\nweb/REST services}. The following figure shows Liferay DXP applications\nand external clients invoking Liferay services.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/apps-invoking-services.png}\n\\caption{Remote and Liferay DXP applications can invoke services via\nREST web APIs. Liferay DXP Java-based portlets can also invoke services\nvia Java APIs.}\n\\end{figure}\n\nLiferay services are built using\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} and made REST-ful using\n\\href{/docs/7-2/appdev/-/knowledge_base/a/rest-builder}{REST Builder}.\nThe services are easy to\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-osgi-services}{override\nand extend} too.\n\nLiferay DXP also provides a web-based UI, which makes content and\nservice functionality available in browsers.\n\n\\section{UI}\\label{ui}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/the-liferay-distinction}{Liferay\nDXP's UI} helps people do work,\n\\href{/docs/7-2/user/-/knowledge_base/u/collaboration}{collaborate}, and\n\\href{/docs/7-2/user/-/knowledge_base/u/web-experience-management}{enjoy\ncontent}. The UI consists of\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/the-liferay-distinction}{Liferay\n  DXP application}: The web application for managing Portals, Sites,\n  Users, Pages, Widgets, and more.\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/application-development}{Applications}:\n  Widgets that provide a user interface for services already deployed.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{Themes}:\n  Plugins for styling Sites with a unique look and feel.\n\\end{itemize}\n\nThe UI concepts article digs deeper into developing and customizing UI\ncomponents.\n\nAs you can see, the Liferay DXP architecture supports developing\nservices, UI components, and customizations. The architecture section\ncovers Core, service, and UI topics. Next, we dive into the Core to\ndescribe class loading, modularity, and more. But you can jump ahead to\nany service or UI architecture topics, if you like. Enjoy exploring the\nLiferay DXP architecture!\n\n\\chapter{Liferay Portal Classloader\nHierarchy}\\label{liferay-portal-classloader-hierarchy}\n\nAll Liferay DXP applications live in its OSGi container. Portal is a web\napplication deployed on your application server. Portal's Module\nFramework bundles (modules) live in the OSGi container and have\nclassloaders. All the classloaders from Java's Bootstrap classloader to\nclassloaders for bundle classes and JSPs are part of a hierarchy.\n\nThis article explains Liferay's classloader hierarchy and describes how\nit works in the following contexts:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Web application, such as Liferay Portal, deployed on the app server\n\\item\n  OSGi bundle deployed in the Module Framework\n\\end{itemize}\n\nThe following diagram shows Liferay DXP's classloader hierarchy.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portal-classloader-hierarchy.png}\n\\caption{0: Here is Liferay's classloader hierarchy.}\n\\end{figure}\n\nHere are the classloader descriptions:\n\n\\begin{itemize}\n\\item\n  \\textbf{Bootstrap}: The JRE's classes (from packages \\texttt{java.*})\n  and Java extension classes (from \\texttt{\\$JAVA\\_HOME/lib/ext}). No\n  matter the context, loading all \\texttt{java.*} classes is delegated\n  to the Bootstrap classloader.\n\\item\n  \\textbf{System}: Classes configured on the \\texttt{CLASSPATH} and or\n  passed in via the application server's Java classpath (\\texttt{-cp} or\n  \\texttt{-classpath}) parameter.\n\\item\n  \\textbf{Common}: Classes accessible globally to web applications on\n  the application server.\n\\item\n  \\textbf{Web Application}: Classes in the application's\n  \\texttt{WEB-INF/classes} folder and \\texttt{WEB-INF/lib/*.jar}.\n\\item\n  \\textbf{Module Framework}: Liferay's OSGi module framework classloader\n  which is used to provide controlled isolation for the module framework\n  bundles.\n\\item\n  \\textbf{bundle}: Classes from a bundle's packages or from packages\n  other bundles export.\n\\item\n  \\textbf{JSP}: A classloader that aggregates the following bundle and\n  classloaders:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Bundle that contains the JSPs' classloader\n  \\item\n    JSP servlet bundle's classloader\n  \\item\n    Javax Expression Language (EL) implementation bundle's classloader\n  \\item\n    Javax JSTL implementation bundle's classloader\n  \\end{itemize}\n\\item\n  \\textbf{Service Builder}: Service Builder classes\n\\end{itemize}\n\nThe classloader used depends on context. Classloading rules vary between\napplication servers. Classloading in web applications and OSGi bundles\ndiffers too. In all contexts, however, the Bootstrap classloader loads\nclasses from \\texttt{java.*} packages.\n\nClassloading from a web application perspective is up next.\n\n\\section{Web Application Classloading\nPerspective}\\label{web-application-classloading-perspective}\n\nApplication servers dictate where and in what order web applications,\nsuch as Liferay DXP, search for classes and resources. Application\nservers such as\n\\href{https://tomcat.apache.org/tomcat-9.0-doc/class-loader-howto.html}{Apache\nTomcat} enforce the following default search order:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Bootstrap classes\n\\item\n  Web app's \\texttt{WEB-INF/classes}\n\\item\n  web app's \\texttt{WEB-INF/lib/*.jar}\n\\item\n  System classloader\n\\item\n  Common classloader\n\\end{enumerate}\n\nFirst, the web application searches Bootstrap. If the class/resource\nisn't there, the web application searches its own classes and JARs. If\nthe class/resource still isn't found, it checks the System classloader\nand then Common classloader. Except for the web application checking its\nown classes and JARs, it searches the hierarchy in parent-first order.\n\nApplication servers such as\n\\href{https://docs.oracle.com/cd/E19501-01/819-3659/beadf/index.html}{Oracle\nWebLogic} and IBM WebSphere have additional classloaders. They may also\nhave a different classloader hierarchy and search order. Consult your\napplication server's documentation for classloading details.\n\n\\section{Other Classloading\nPerspectives}\\label{other-classloading-perspectives}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/bundle-classloading-flow}{Bundle\nClassloading Flow} explains classloading from an OSGi bundle\nperspective.\n\nClassloading for JSPs and Service Builder classes is similar to that of\nweb applications and OSGi bundle classes.\n\nYou now know Liferay DXP's classloading hierarchy, understand it in\ncontext of web applications, and have references to information on other\nclassloading perspectives.\n\n\\section{Related Topics}\\label{related-topics-8}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/bundle-classloading-flow}{Bundle\nClassloading Flow}\n\n\\chapter{Liferay DXP Startup Phases}\\label{liferay-dxp-startup-phases}\n\nKnowing Liferay's startup phases helps you troubleshoot startup\nfailures. By learning the phase triggered events, you can listen for\nphases and act on them. This article describes the startup phases and\nidentifies how to \\hyperref[acting-on-events]{implement actions for\nphase events}.\n\nStartup consists of these main phases:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{Portal Context Initialization Phase:} focuses on low level\n  tasks without a web context.\n\\item\n  \\textbf{Main Servlet Initialization Phase:} focuses on the portlet\n  container and the Liferay DXP web application's UI features such as\n  Struts, Themes, and more.\n\\end{enumerate}\n\nThe Portal Context Initialization Phase sets the stage for the Main\nServlet Initialization Phase.\n\n\\section{Portal Context Initialization\nPhase}\\label{portal-context-initialization-phase}\n\nThe Portal Context Initialization phase runs first with these tasks:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Set up low level utilities such as logging and those in\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/PortalUtil.html}{\\texttt{PortalUtil}}\n  and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-impl/com/liferay/portal/util/InitUtil.html}{\\texttt{InitUtil}}.\n\\item\n  OSGi framework is initialized.\n\\item\n  Spring Phase 1: INFRASTRUCTURE beans specified by the Spring context\n  files listed in Portal property\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Spring}{\\texttt{spring.infrastructure.configs}}\n  are loaded.\n\\item\n  INFRASTRUCTURE beans are published as\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{OSGi\n  services}.\n\\item\n  OSGi framework starts.\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\tightlist\n  \\item\n    Static bundles are installed and started.\n  \\item\n    Dynamic bundles are started.\n  \\end{enumerate}\n\\item\n  OSGi framework starts the runtime.\n\\item\n  Spring Phase 2: MAIN\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\tightlist\n  \\item\n    Load Spring beans specified by the Spring context files listed in\n    Portal property\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Spring}{\\texttt{spring.configs}}.\n  \\item\n    A\n    \\hyperref[moduleservicelifecycle-events]{\\texttt{ModuleServiceLifecycle}\n    event service} with a service property\n    \\texttt{module.service.lifecycle} value \\texttt{spring.initialized}\n    (i.e.,\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/constant-values.html\\#com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle.SPRING_INITIALIZED}{\\texttt{SPRING\\_INITIALIZED}})\n    registers.\n  \\end{enumerate}\n\\item\n  MAIN Spring beans are published as\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{OSGi\n  services}.\n\\end{enumerate}\n\n\\section{Main Servlet Initialization\nPhase}\\label{main-servlet-initialization-phase}\n\nHere's the phase's activity sequence:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The\n  \\hyperref[moduleservicelifecycle-events]{\\texttt{ModuleServiceLifecycle}\n  event service} is updated with the service property\n  \\texttt{module.service.lifecycle} value \\texttt{database.initialized}\n  (i.e.,\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/constant-values.html\\#com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle.DATABASE_INITIALIZED}{\\texttt{DATABASE\\_INITIALIZED}}).\n\\item\n  The \\hyperref[portal-startup-events]{Global Startup event} fires.\n\\item\n  For each portal instance, the\n  \\hyperref[portal-startup-events]{Application Startup events} fire.\n\\item\n  The\n  \\hyperref[moduleservicelifecycle-events]{\\texttt{ModuleServiceLifecycle}\n  event service} is updated with the service property\n  \\texttt{module.service.lifecycle} value \\texttt{portal.initialized}\n  (i.e.,\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/constant-values.html\\#com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle.PORTAL_INITIALIZED}{\\texttt{PORTAL\\_INITIALIZED}}).\n\\end{enumerate}\n\nNow that you're acquainted with the startup phases, you can concentrate\non the events they fire.\n\n\\section{Acting on Events}\\label{acting-on-events}\n\nThe ways to act on events depends on the event type. These subsections\ndescribe the event types.\n\n\\section{ModuleServiceLifecycle\nEvents}\\label{moduleservicelifecycle-events}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/waiting-on-lifecycle-events}{You\ncan wait for and act on \\texttt{ModuleServiceLifecycle} event services.}\n\n\\section{Portal Startup Events}\\label{portal-startup-events}\n\nIn your \\texttt{liferay-portal-ext.properties} file, you can override\nthe following properties and add your own\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/events/LifecycleAction.html}{\\texttt{LifecycleAction}}\nclasses to the list of action classes to invoke on the events.\n\n\\textbf{Global Startup Event} runs once when Liferay DXP initializes.\nThe\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Startup\\%20Events}{\\texttt{global.startup.events}\nproperty} defines the event's default actions.\n\n\\textbf{Application Startup Events} runs once for each Site instance\nLiferay DXP initializes. The\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Startup\\%20Events}{\\texttt{application.startup.events}\nproperty} defines the event's default actions.\n\n\\section{Related Topics}\\label{related-topics-9}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/waiting-on-lifecycle-events}{Waiting\non Lifecycle Events}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{OSGi\nServices and Dependency Injection with Declarative Services}\n\n\\chapter{The Benefits of Modularity}\\label{the-benefits-of-modularity}\n\nDictionary.com defines\n\\href{http://www.dictionary.com/browse/modularity}{modularity} as\n\\emph{the use of individually distinct functional units, as in\nassembling an electronic or mechanical system.} The distinct functional\nunits are called \\emph{modules}.\n\nNASA's Apollo spacecraft, for example, comprised three modules, each\nwith a distinct function:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Lunar Module}: Carried astronauts from the Apollo spacecraft to\n  the moon's surface and back.\n\\item\n  \\emph{Service Module}: Provided fuel for propulsion, air conditioning,\n  and water.\n\\item\n  \\emph{Command Module}: Housed the astronauts and communication and\n  navigation controls.\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/modularity_apollo_spacecraft_diagram.png}\n\\caption{The Apollo spacecraft's modules collectively took astronauts to\nthe moon's surface and back to Earth.}\n\\end{figure}\n\nThe spacecraft and its modules exemplified these modularity\ncharacteristics:\n\n\\begin{itemize}\n\\item\n  \\textbf{Distinct functionality}: Each module provides a distinct\n  function (purpose); modules can be combined to provide an entirely new\n  collective function.\n\n  The Apollo spacecraft's modules were grouped together for a distinct\n  collective function: take astronauts from the Earth's atmospheric rim,\n  to the moon's surface, and back to Earth. The previous list identifies\n  each module's distinct function.\n\\item\n  \\textbf{Dependencies}: Modules can require capabilities other modules\n  satisfy.\n\n  The Apollo modules had these dependencies:\n\n  \\begin{itemize}\n  \\item\n    Lunar Module depended on the Service Module to get near the moon.\n  \\item\n    Command Module depended on the Service Module for power and oxygen.\n  \\item\n    Service Module depended on the Command Module for instruction.\n  \\end{itemize}\n\\item\n  \\textbf{Encapsulation}: Modules hide their implementation details but\n  publicly define their capabilities and interfaces.\n\n  Each Apollo module was commissioned with a contract defining its\n  capabilities and interface, while each module's details were\n  encapsulated (hidden) from other modules. NASA integrated the modules\n  based on their interfaces.\n\\item\n  \\textbf{Reusability}: A module can be applied to different scenarios.\n\n  The Command Module's structure and design were reusable. NASA used\n  different versions of the Command Module, for example, throughout the\n  Apollo program, and in the Gemini Program, which focused on Earth\n  orbit.\n\\end{itemize}\n\nNASA used modularity to successfully complete over a dozen missions to\nthe moon. Can modularity benefit software too? Yes! The following\nsections show you how:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[modularity-benefits-for-software]{Modularity benefits for\n  software}\n\\item\n  \\hyperref[example-designing-a-modular-application]{Example: How to\n  design a modular application}\n\\end{itemize}\n\n\\section{Modularity Benefits for\nSoftware}\\label{modularity-benefits-for-software}\n\nJava applications have predominantly been monolithic: they're developed\nin large code bases. In a monolith, it's difficult to avoid tight\ncoupling of classes. Modular application design, conversely, facilitates\nloose coupling, making the code easier to maintain. It's much easier and\nmore fun to develop small amounts of cohesive code in modules. Here are\nsome key benefits of developing modular software.\n\n\\section{Distinct Functionality}\\label{distinct-functionality}\n\nIt's natural to focus on developing one piece of software at a time. In\na module, you work on a small set of classes to define and implement the\nmodule's function. Keeping scope small facilitates writing high quality,\nelegant code. The more cohesive the code, the easier it is to test,\ndebug, and maintain. Modules can be combined to provide a new function,\ndistinguishable from each module's function.\n\n\\section{Encapsulation}\\label{encapsulation}\n\nA module encapsulates a function (capability). Module implementations\nare hidden from consumers, so you can create and modify them as you\nlike. Throughout a module's lifetime, you can fix and improve the\nimplementation or swap in an entirely new one. You make the changes\nbehind the scenes, transparent to consumers. A module's contract defines\nits capability and interface, making the module easy to understand and\nuse.\n\n\\section{Dependencies}\\label{dependencies}\n\nModules have requirements and capabilities. The interaction between\nmodules is a function of the capability of one satisfying the\nrequirement of another and so on. Modules are published to artifact\nrepositories, such as Maven Central. Module versioning schemes let you\nspecify dependencies on particular module versions or version ranges.\n\n\\section{Reusability}\\label{reusability}\n\nModules that do their job well are hot commodities. They're reusable\nacross projects, for different purposes. As you discover helpful\nreliable modules, you'll use them again and again.\n\nIt's time to design a modular application.\n\n\\section{Example: Designing a Modular\nApplication}\\label{example-designing-a-modular-application}\n\nApplication design often starts out simple but gets more complex as you\ndetermine capabilities the application requires. If a third party\nlibrary already provides the capability, you can\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{deploy\nit with your app}. You can otherwise implement the capability yourself.\n\nAs you design various aspects of your app to support its function, you\nmust decide how those aspects fit into the code base. Putting them in a\nsingle monolithic code base often leads to tight coupling, while\ndesignating separate modules for each aspect fosters loose coupling.\nAdopting a modular approach to application design lets you reap the\nmodularity benefits.\n\nFor example, you can apply modular design to a speech recognition app.\nHere are the app's function and required capabilities:\n\n\\emph{Function}: interface with users to translate their speech into\ntext for the computer to understand.\n\n\\emph{Required capabilities}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Translates user words to text\n\\item\n  Uses a selected computer voice to speak to users.\n\\item\n  Interacts with users based on a script of instructions that include\n  questions, commands, requests, and confirmations.\n\\end{itemize}\n\nYou could create modules to provide the required capabilities:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Speech to text}: Translates spoken words to text the computer\n  understands.\n\\item\n  \\emph{Voice UI}: Interacts with users based on stored questions,\n  commands, and confirmations.\n\\item\n  \\emph{Instruction manager}: Stores and provides the application's\n  questions, commands, and confirmations.\n\\item\n  \\emph{Computer voice}: Stores and provides computer voices for users\n  to choose from.\n\\end{itemize}\n\nThe following diagram contrasts a monolithic design for the speech\nrecognition application with a modular design.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/modularity-benefits-application-design-example.png}\n\\caption{The speech recognition application can be implemented in a\nsingle monolithic code base or in modules, each focused on a particular\nfunction.}\n\\end{figure}\n\nDesigning the app as a monolith lumps everything together. There are no\ninitial boundaries between the application aspects, whereas the modular\ndesign distinguishes the aspects.\n\nDevelopers can create the modules in parallel, each one with its own\nparticular capability. Designing applications that comprise modules\nfosters writing cohesive pieces of code that represent capabilities.\nEach module's capability can potentially be \\emph{reused} in other\nscenarios too.\n\nFor example, the \\emph{Instruction manager} and \\emph{Computer voice}\nmodules can be \\emph{reused} by a navigation app.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/modularity-benefits-module-reuse.png}\n\\caption{The \\emph{Instruction manager} and \\emph{Computer voice}\nmodules designed for the speech recognition app can be used (or\n\\emph{reused}) by a navigation app.}\n\\end{figure}\n\nHere are the benefits of designing the speech recognition app as\nmodules:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Each module represents a capability that contributes to the app's\n  overall function.\n\\item\n  The app depends on modules, that are easy to develop, test, and\n  maintain.\n\\item\n  The modules can be reused in different applications.\n\\end{itemize}\n\nIn conclusion, modularity has literally taken us to the moon and back.\nIt benefits software development too. The example speech recognition\napplication demonstrated how to design an app that comprises modules.\n\nNext you'll learn how OSGi facilitates creating modules that provide and\nconsume services.\n\n\\chapter{OSGi and Modularity}\\label{osgi-and-modularity}\n\nModularity makes writing software, especially as a team, fun! Here are\nsome benefits to modular development on DXP:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay DXP's runtime framework is lightweight, fast, and secure.\n\\item\n  The framework uses the OSGi standard. If you have experience using\n  OSGi with other projects, you can apply your existing knowledge to\n  developing on DXP.\n\\item\n  Modules publish services to and consume services from a service\n  registry. Service contracts are loosely coupled from service providers\n  and consumers, and the registry manages the contracts automatically.\n\\item\n  Modules' dependencies are managed automatically by the container,\n  dynamically (no restart required).\n\\item\n  The container manages module life cycles dynamically. Modules can be\n  installed, started, updated, stopped, and uninstalled while Liferay is\n  running, making deployment a snap.\n\\item\n  Only a module's classes whose packages are explicitly exported are\n  publicly visible; OSGi hides all other classes by default.\n\\item\n  Modules and packages are semantically versioned and declare\n  dependencies on specific versions of other packages. This allows two\n  applications that depend on different versions of the same packages to\n  each depend on their own versions of the packages.\n\\item\n  Team members can develop, test, and improve modules in parallel.\n\\item\n  You can use your existing developer tools and environment to develop\n  modules.\n\\end{itemize}\n\nThere are many benefits to modular software development with OSGi, and\nwe can only scratch the surface here. Once you start developing modules,\nyou might find it hard to go back to developing any other way.\n\n\\section{Modules}\\label{modules}\n\nIt's time to see what module projects look like and see Liferay DXP's\nmodular development features in action. To keep things simple, only\nproject code and structure are shown: you can\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{create\nmodules} like these anytime.\n\nThese modules collectively provide a command that takes a String and\nuses it in a greeting. Consider it ``Hello World'' for modules.\n\n\\section{API}\\label{api}\n\nThe API module is first. It defines the contract that a provider\nimplements and a consumer uses. Here is its structure:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{greeting-api}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/greeting/api}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Greeting.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\end{itemize}\n\\end{itemize}\n\nVery simple, right? Beyond the Java source file, there are only two\nother files: a Gradle build script (though you can use any build system\nyou want), and a configuration file called \\texttt{bnd.bnd}. The\n\\texttt{bnd.bnd} file describes and configures the module:\n\n\\begin{verbatim}\nBundle-Name: Greeting API\nBundle-SymbolicName: com.liferay.docs.greeting.api\nBundle-Version: 1.0.0\nExport-Package: com.liferay.docs.greeting.api\n\\end{verbatim}\n\nThe module's name is \\emph{Greeting API}. Its symbolic name--a name that\nensures uniqueness--is \\texttt{com.liferay.docs.greeting.api}. Its\nsemantic version is declared next, and its package is \\emph{exported},\nwhich means it's made available to other modules. This module's package\nis just an API other modules can implement.\n\nFinally, there's the Java class, which in this case is an interface:\n\n\\begin{verbatim}\npackage com.liferay.docs.greeting.api;\n\nimport aQute.bnd.annotation.ProviderType;\n\n@ProviderType\npublic interface Greeting {\n\n        public void greet(String name);\n\n}\n\\end{verbatim}\n\nThe interface's \\texttt{@ProviderType} annotation tells the service\nregistry that anything implementing the interface is a provider. The\ninterface's one method asks for a \\texttt{String} and doesn't return\nanything.\n\nThat's it! As you can see, creating modules is not very different from\ncreating other Java projects.\n\n\\section{Provider}\\label{provider}\n\nAn interface only defines an API; to do something, it must be\nimplemented. This is what the provider module is for. Here's what a\nprovider module for the Greeting API looks like:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{greeting-impl}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/greeting/impl}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{GreetingImpl.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\end{itemize}\n\\end{itemize}\n\nIt has the same structure as the API module: a build script, a\n\\texttt{bnd.bnd} configuration file, and an implementation class. The\nonly differences are the file contents. The \\texttt{bnd.bnd} file is a\nlittle different:\n\n\\begin{verbatim}\nBundle-Name: Greeting Impl\nBundle-SymbolicName: com.liferay.docs.greeting.impl\nBundle-Version: 1.0.0\n\\end{verbatim}\n\nThe bundle name, symbolic name, and version are all set similarly to the\nAPI.\n\nFinally, there's no \\texttt{Export-Package} declaration. A client (which\nis the third module you'll create) just wants to use the API: it doesn't\ncare how its implementation works as long as the API returns what it's\nsupposed to return. The client, then, only needs to declare a dependency\non the API; the service registry injects the appropriate implementation\nat runtime.\n\nPretty cool, eh?\n\nAll that's left, then, is the class that provides the implementation:\n\n\\begin{verbatim}\npackage com.liferay.docs.greeting.impl;\n\nimport com.liferay.docs.greeting.api.Greeting;\n\nimport org.osgi.service.component.annotations.Component;\n\n@Component(\n    immediate = true,\n    property = {\n    },\n    service = Greeting.class\n)\npublic class GreetingImpl implements Greeting {\n\n    @Override\n    public void greet(String name) {\n        System.out.println(\"Hello \" + name + \"!\");\n    }\n\n}\n\\end{verbatim}\n\nThe implementation is simple. It uses the \\texttt{String} as a name and\nprints a hello message. A better implementation might be to use\nLiferay's API to collect all the names of all the users in the system\nand send each user a greeting notification, but the point here is to\nkeep things simple. You should understand, though, that there's nothing\nstopping you from replacing this implementation by deploying another\nmodule whose Greeting implementation's \\texttt{@Component} annotation\nspecifies a higher service ranking property (e.g.,\n\\texttt{\"service.ranking:Integer=100\"}).\n\nThis \\texttt{@Component} annotation defines three options:\n\\texttt{immediate\\ =\\ true}, an empty property list, and the service\nclass that it implements. The \\texttt{immediate\\ =\\ true} setting means\nthat this module should not be lazy-loaded; the service registry loads\nit as soon as it's deployed, instead of when it's first used. Using the\n\\texttt{@Component} annotation declares the class as a Declarative\nServices component, which is the most straightforward way to create\ncomponents for OSGi modules. A component is a POJO that the runtime\ncreates automatically when the module starts.\n\nTo compile this module, the API it's implementing must be on the\nclasspath. If you're using Gradle, you'd add the \\texttt{greetings-api}\nproject to your \\texttt{dependencies\\ \\{\\ ...\\ \\}} block. In a\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace} module, the dependency looks like this:\n\n\\begin{verbatim}\ncompileOnly project (':modules:greeting-api')\n\\end{verbatim}\n\nThat's all there is to a provider module.\n\n\\section{Consumer}\\label{consumer}\n\nThe consumer or client uses the API that the API module defines and the\nprovider module implements. DXP has many different kinds of consumer\nmodules.\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{Portlets} are\nthe most common consumer module type, but since they are a topic all by\nthemselves, this example stays simple by creating an command for the\nApache Felix Gogo shell. Note that consumers can, of course, consume\nmany different APIs to provide functionality.\n\nA consumer module has the same structure as the other module types:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{greeting-command}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/greeting/command}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{GreetingCommand.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\end{itemize}\n\\end{itemize}\n\nAgain, you have a build script, a \\texttt{bnd.bnd} file, and a Java\nclass. This module's \\texttt{bnd.bnd} file is almost the same as the\nprovider's:\n\n\\begin{verbatim}\nBundle-Name: Greeting Command\nBundle-SymbolicName: com.liferay.docs.greeting.command\nBundle-Version: 1.0.0\n\\end{verbatim}\n\nThere's nothing new here: you declare the same things you declared for\nthe provider.\n\nYour Java class has a little bit more going on:\n\n\\begin{verbatim}\npackage com.liferay.docs.greeting.command;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.greeting.api.Greeting;\n\n@Component(\n    immediate = true,\n    property = {\n        \"osgi.command.scope=greet\",\n        \"osgi.command.function=greet\"\n    },\n    service = Object.class\n)\npublic class GreetingCommand {\n\n    public void greet(String name) {\n        Greeting greeting = _greeting;\n\n        greeting.greet(name);\n    }\n\n    @Reference\n    private Greeting _greeting;\n\n}\n\\end{verbatim}\n\nThe \\texttt{@Component} annotation declares the same attributes, but\nspecifies different properties and a different service. As in Java,\nwhere every class is a subclass of \\texttt{java.lang.Object} (even\nthough you don't need to specify it by default), in Declarative\nServices, the runtime needs to know the type of class to register.\nBecause you're not implementing any particular type, your parent class\nis \\texttt{java.lang.Object}, so you must specify that class as the\nservice. While Java doesn't require you to specify \\texttt{Object} as\nthe parent when you're creating a class that doesn't inherit anything,\nDeclarative Services does.\n\nThe two properties define a command scope and a command function. All\ncommands have a scope to define their context, as it's common for\nmultiple APIs to have similar functions, such as \\texttt{copy} or\n\\texttt{delete}. These properties specify you're creating a command\ncalled \\texttt{greet} in a scope called \\texttt{greet}. While you get no\npoints for imagination, this sufficiently defines the command.\n\nSince you specified \\texttt{osgi.command.function=greet} in the\n\\texttt{@Component} annotation, your class must have a method named\n\\texttt{greet}, and you do. But how does this \\texttt{greet} method\nwork? It obtains an instance of the \\texttt{Greeting} OSGi service and\ninvokes its \\texttt{greet} method, passing in the \\texttt{name}\nparameter. How is an instance of the \\texttt{Greeting} OSGi service\nobtained? The \\texttt{GreetingCommand} class declares a private service\nbean, \\texttt{\\_greeting} of type \\texttt{Greeting}. This is the OSGi\nservice type that the provider module registers. The \\texttt{@Reference}\nannotation tells the OSGi runtime to instantiate the service bean with a\nservice from the service registry. The runtime binds the\n\\texttt{Greeting} object of type \\texttt{GreetingImpl} to the private\nfield \\texttt{\\_greeting}. The \\texttt{greet} method uses the\n\\texttt{\\_greeting} field value.\n\nJust like the provider, the consumer needs to have the API on its\nclasspath in order to compile, but at runtime, since you've declared all\nthe dependencies appropriately, the container knows about these\ndependencies, and provides them automatically.\n\nIf you were to\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{deploy\nthese modules to a DXP instance}, you'd be able to attach to the\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nShell} and execute a command like this:\n\n\\begin{verbatim}\ngreet:greet \"Captain\\ Kirk\"\n\\end{verbatim}\n\nThe shell would then return your greeting:\n\n\\begin{verbatim}\nHello Captain Kirk!\n\\end{verbatim}\n\nThis most basic of examples should make it clear that module-based\ndevelopment is easy and straightforward. The API-Provider-Consumer\ncontract fosters loose coupling, making your software easy to manage,\nenhance, and support.\n\n\\section{A Typical Liferay\nApplication}\\label{a-typical-liferay-application}\n\nIf you look at a typical application from Liferay's source, you'll\ngenerally find at least four modules:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  An API module\n\\item\n  A Service (provider) module\n\\item\n  A Test module\n\\item\n  A Web (consumer) module\n\\end{itemize}\n\nThis is exactly what you'll find for some smaller applications, like the\nMentions application that lets users mention other users with the\n\\texttt{@username} nomenclature in comments, blogs, or other\napplications. Larger applications like the Documents and Media library\nhave more modules. In the case of the Documents and Media library, there\nare separate modules for different document storage back-ends. In the\ncase of the Wiki, there are separate modules for different Wiki engines.\n\nEncapsulating capability variations as modules facilitates\nextensibility. If you have a document storage back-end that Liferay\ndoesn't yet support, you can implement Liferay's document storage API\nfor your solution by developing a module for it and thus extend\nLiferay's Documents and Media library. If there's a Wiki dialect that\nyou like better than what Liferay's wiki provides, you can write a\nmodule for it and extend Liferay's wiki.\n\nAre you excited yet? Are you ready to start developing? Here are some\nresources for you to learn more.\n\n\\section{Related Topics}\\label{related-topics-10}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\nDev Studio}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven}{Maven}\n\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver}{Upgrading\nCode to 7.2}\n\n\\chapter{Module Lifecycle}\\label{module-lifecycle}\n\nIn OSGi, all components, Java classes, resources, and descriptors are\ndeployed via modules. The \\texttt{MANIFEST.MF} file describes the\nmodule's physical characteristics, such as the packages it exports and\nimports. The module's component description files specify its functional\ncharacteristics (i.e., the services its components offer and consume).\nAlso modules and their components have their own lifecycles and\nadministrative APIs. Declarative Services and shell tools give you\nfine-grained control over module and component deployment.\n\nSince a module's contents depend on its activation, consider the\nactivation steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\emph{Installation}: Copying the module JAR into Liferay DXP's\n  \\texttt{{[}Liferay\\ Home{]}/deploy} folder installs the module to the\n  OSGi framework, marking the module \\texttt{INSTALLED}.\n\\item\n  \\emph{Resolution}: Once all the module's requirements are met (e.g.,\n  all packages it imports are available), the framework publishes the\n  module's exported packages and marks the module \\texttt{RESOLVED}.\n\\item\n  \\emph{Activation}: Modules are activated \\emph{eagerly} by default.\n  That is, they're started in the framework and marked \\texttt{ACTIVE}\n  on resolution. An active module's components are enabled. If a module\n  specifies a \\texttt{lazy} activation policy, as shown in the manifest\n  header below, it's activated only after another module requests one of\n  its classes.\n\n\\begin{verbatim}\nBundle-ActivationPolicy: lazy\n\\end{verbatim}\n\\end{enumerate}\n\nThe figure below illustrates the module lifecycle.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/module-state-diagram.png}\n\\caption{This state diagram illustrates the module lifecycle.}\n\\end{figure}\n\nThe\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Apache\nFelix Gogo Shell} lets you manage the module lifecycle. You can\ninstall/uninstall modules and start/stop them. You can update a module\nand notify dependent modules to use the update. Liferay's tools,\nincluding\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\nDev Studio DXP},\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace}, and\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI} offer\nsimilar shell commands that use the OSGi Admin API.\n\nOn activating a module, its components are enabled. But only\n\\emph{activated} components can be used. Component activation requires\nall its referenced services be satisfied. That is, all services it\nreferences must be registered. The highest ranked service that matches a\nreference is bound to the component. When the container finds and binds\nall the services the component references, it registers the component.\nIt's now ready for activation.\n\nComponents can use \\emph{delayed} (default) or \\emph{immediate}\nactivation policies. To specify immediate activation, the developer adds\nthe attribute \\texttt{immediate=true} to the \\texttt{@Component}\nannotation.\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    ...\n)\n\\end{verbatim}\n\nUnless immediate activation is specified, the component's activation is\ndelayed. That is, the component's object is created and its classes are\nloaded once the component is requested. In this way, delayed activation\ncan improve startup times and conserve resources.\n\nGogo Shell's\n\\href{http://felix.apache.org/documentation/subprojects/apache-felix-service-component-runtime.html\\#shell-command}{Service\nComponent Runtime commands} let you manage components:\n\n\\begin{itemize}\n\\item\n  \\texttt{scr:list\\ {[}bundleID{]}}: Lists the module's (bundle's)\n  components.\n\\item\n  \\texttt{scr:info\\ {[}componentID\\textbar{}fullClassName{]}}: Describes\n  the component, including its status and the services it provides.\n\\item\n  \\texttt{scr:enable\\ {[}componentID\\textbar{}fullClassName{]}}: Enables\n  the component.\n\\item\n  \\texttt{scr:disable\\ {[}componentID\\textbar{}fullClassName{]}}:\n  Disables the component. It's disabled on the server (or current server\n  node in a cluster) until the server is restarted.\n\\end{itemize}\n\nService references are static and reluctant by default. That is, an\ninjected service remains bound to the referencing component until the\nservice is disabled. Alternatively, you can specify \\emph{greedy}\nservice policies for references. Every time a higher ranked matching\nservice is registered, the framework unbinds the lower ranked service\nfrom the component (whose service policy is greedy) and binds the new\nservice in its place automatically. Here's a \\texttt{@Reference}\nannotation that uses a greedy policy:\n\n\\begin{verbatim}\n@Reference(policyOption = ReferencePolicyOption.GREEDY)\n\\end{verbatim}\n\nDeclarative Services annotations let you specify component activation\nand service policies. Gogo Shell commands let you control modules and\ncomponents.\n\n\\section{Related Topics}\\label{related-topics-11}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Creating\na Project}\n\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver}{Upgrading\nCode to 7.2}\n\n\\chapter{UI Architecture}\\label{ui-architecture}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/the-liferay-distinction}{Liferay\nDXP's UI} is a portal for adding sites, pages, widgets, and content. It\nhelps people do work,\n\\href{/docs/7-2/user/-/knowledge_base/u/collaboration}{collaborate}, and\n\\href{/docs/7-2/user/-/knowledge_base/u/web-experience-management}{share\ncontent}.\n\nThe UI comprises the following parts:\n\n\\begin{itemize}\n\\item\n  Content: images, videos, and text.\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/application-development}{Applications}:\n  Widgets and portlets that expose functionality for accomplishing\n  tasks.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{Themes}:\n  Plugins that use CSS, FreeMarker templates, HTML, and JavaScript to\n  provide a site's overall look and feel.\n\\item\n  Product navigation sidebars and panels: Use these for administering\n  sites.\n\\end{itemize}\n\n\\section{Content}\\label{content}\n\nLiferay DXP's built-in applications help you publish images, video,\nforms, markup text, and more to site pages.\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-documents-and-media}{Documents\nand Media} stores images, videos, and documents to use throughout your\nsite. The\n\\href{/docs/7-2/user/-/knowledge_base/u/web-experience-management}{Web\nExperience Management} suite helps you create, maintain, and organize\ncontent. \\href{/docs/7-2/user/-/knowledge_base/u/forms}{Liferay Forms}\ngives you robust form building capability.\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-forums-with-message-boards}{Message\nBoards} facilitate lively discussions and\n\\href{/docs/7-2/user/-/knowledge_base/u/publishing-blogs}{Blogs} let\nusers express themselves with markup text and images. These are just a\nfew of the built-in applications for adding site content.\n\n\\section{Applications}\\label{applications}\n\nLiferay DXP applications provide content and help users accomplish\ntasks. They're\n\\href{/7-2/appdev/-/knowledge_base/a/web-front-ends}{developed the same\nway} as other web applications, and Liferay DXP can combine multiple\napplications on one page.\n\nLiferay DXP supports developing JavaScript-based applications using\npopular front-end frameworks:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/developing-an-angular-application}{Angular}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/developing-a-react-application}{React}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/developing-a-vue-application}{Vue}\n\\end{itemize}\n\nJava-based portlet applications use the latest portlet standards and\nframeworks, including ones familiar to experienced Liferay portlet\ndevelopers:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{Liferay\n  MVC Portlet}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/portletmvc4spring}{PortletMVC4Spring}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/jsf-portlet}{JSF Portlet}\n\\end{itemize}\n\nIn the UI, applications are referred to as Widgets and categorized for\nusers to add to pages. Administrative applications are available in the\nproduct menu panels.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/architecture-ui-widgets.png}\n\\caption{Widget pages offer users functionality. Widgets are organized\ninto a page template's rows and columns. This template has two columns:\na smaller left column and larger right column. On this page, users\nselect tags in the Tag Cloud widget and the matching tagged images show\nthe Asset Publisher widget.}\n\\end{figure}\n\n\\section{Themes}\\label{themes}\n\nA\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{theme}\nstyles a site with a unique look and feel. It's developed as a WAR\nproject that includes CSS, JavaScript, and markup content. You can\ndevelop themes using whatever tools you prefer, but Liferay DXP offers\n\\href{https://getbootstrap.com/}{Bootstrap}-based components and\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-themes}{theme\ntooling} to create and deploy themes in no time.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/architecture-ui-themes.png}\n\\caption{You can select an attractive theme and apply it to your site.}\n\\end{figure}\n\nHere's a quick demonstration of developing a theme:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a theme using the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-generator}{Theme\n  Generator}. The theme extends the base theme you specified to the\n  Theme Generator---Liferay's\n  \\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-styled}{Styled\n  theme} is the default.\n\\item\n  Run\n  \\href{https://portal.liferay.dev/docs/7-2/frameworks/-/knowledge_base/f/building-your-themes-files}{\\texttt{gulp\\ build}}\n  to generate the base theme files to the \\texttt{build} folder\n  subfolders:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{templates}: FreeMarker templates specify site page markup.\n    \\texttt{portal\\_normal.ftl} is the central file; it includes\n    templates that define the page parts (e.g., header, navigation,\n    footer). The \\texttt{init.ftl} file defines default variables\n    available to the templates.\n  \\item\n    \\texttt{css}: SCCS files that provide styling.\n  \\item\n    \\texttt{font}: Font Awesome and Glyphicons fonts.\n  \\item\n    \\texttt{js}: JavaScript files; \\texttt{main.js} is the Styled\n    theme's JavaScript.\n  \\item\n    \\texttt{images}: Image files.\n  \\end{itemize}\n\\item\n  Override aspects of the base theme by copying relevant files from the\n  \\texttt{build} subfolders to folders of the same name in your\n  \\texttt{src} folder. The\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide}{Theme\n  Anatomy Guide} describes all the files. Here's an example of a\n  customized \\texttt{portal\\_normal.ftl}:\n\\end{enumerate}\n\n\\begin{verbatim}\n<html class=\"${root_css_class}\">\n<head></head>\n  <body class=\"${body_class}\">\n    <header class=\"${header_css_class}\">\n      <a class=\"${logo_css_class} href=\"${site_url}\"><img src=\"${site_logo}\"/></a>\n      <#include \"${full_templates_path}/navigation.ftl\" />\n    </header>\n    <section>\n      ${portlets}\n    </section>\n      <#include \"${full_templates_path}/footer.ftl\" />\n  </body>\n</html>\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\tightlist\n\\item\n  Add custom styling using your theme's \\texttt{\\_custom.scss} file\n  (i.e., \\texttt{src/css/\\_custom.scss}). Liferay DXP supports\n  \\href{https://getbootstrap.com/}{Bootstrap}, as well as\n  \\href{https://sass-lang.com/}{Sass}, so you can use Bootstrap\n  utilities in your markup and Sass nesting, variables, and more in your\n  CSS files. This snippet styles the logo:\n\\end{enumerate}\n\n\\begin{verbatim}\n.logo {\n  margin-left: 15px;\n\n  img {\n    height: auto;\n  }\n\n  @include media-breakpoint-down(md) {\n    text-align: center;\n    width: 100%;\n  }\n}\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/architecture-ui-portal-dev-logo.png}\n\\caption{You can provide custom styling using the theme's\n\\texttt{\\_custom.sccs} file.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{4}\n\\tightlist\n\\item\n  Deploy your theme by executing \\texttt{gulp\\ deploy}.\n\\end{enumerate}\n\nThe theme is available to\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes}{apply}\nto your site.\n\nFor details,\n\\href{/docs/7-2/customization/-/knowledge_base/c/theme-components}{Theme\nComponents} breaks down a theme's parts, and the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{Themes\nsection} provides theme development details.\n\n\\section{Product Navigation Sidebars and\nPanels}\\label{product-navigation\\noindent\\hrulefills-and-panels}\n\nThe product navigation sidebars and panels enable administrators to\nbuild sites, add pages, apply themes, and configure the portal. It's\nalso where you can provide administrative functionality for your custom\napplications. The navigation sidebars and panels are customizable.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/architecture-ui-menus-and-panel-app.png}\n\\caption{Liferay facilitates integrating custom administrative\nfunctionality through navigation menus and administrative applications.}\n\\end{figure}\n\nAs you can see, Liferay DXP's UI is highly flexible and customizable.\nHere's where to learn more:\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/theme-components}{Theme\n  Components}: Explains available mechanisms and extensions for\n  customizing and theming pages, content, and applications.\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/understanding-the-page-structure}{Understanding\n  the Page Structure}: Describes how the page's UI is organized and\n  introduces tools for populating and developing each section.\n\\end{itemize}\n\n\\chapter{Theme Components}\\label{theme-components}\n\nThis guide provides an overview of the following theme development and\ncustomization topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[theme-templates]{Theme Templates}\n\\item\n  \\hyperref[css-frameworks-and-extensions]{CSS Frameworks and\n  Extensions}\n\\item\n  \\hyperref[theme-customizations-and-extensions]{Theme Customizations\n  and Extensions}\n\\item\n  \\hyperref[portlet-customizations-and-extensions]{Portlet\n  Customizations and Extensions}\n\\end{itemize}\n\n\\section{Theme Templates and\nUtilities}\\label{theme-templates-and-utilities}\n\nThe default FreeMarker templates provide helpful utilities and handle\nkey pieces of page layout (page) functionality:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{portal\\_normal.ftl}: Similar to a static site's\n  \\texttt{index.html}, this file is the hub for all the theme templates\n  and provides the overall markup for the page.\n\\item\n  \\texttt{init.ftl}: Contains variables commonly used throughout the\n  theme templates. Refer to it to look up theme objects. For\n  convenience, the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/freemarker-variable-reference-guide}{FreeMarker\n  Variable Reference Guide} lists the objects. \\textbf{DO NOT override\n  this file}.\n\\item\n  \\texttt{init\\_custom.ftl}: Used to override FreeMarker variables in\n  \\texttt{init.ftl} and to define new variables, such as\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/making-configurable-theme-settings}{theme\n  settings}.\n\\item\n  \\texttt{portlet.ftl}: Controls the theme's portlets. If your theme\n  uses\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/theming-portlets\\#portlet-decorators}{Portlet\n  Decorators}, modify this file to create application decorator-specific\n  theme settings.\n\\item\n  \\texttt{navigation.ftl}: Contains the navigation markup. To customize\n  pages in the navigation, you must use the\n  \\texttt{liferay.navigation\\_menu} macro. Then you can leverage\n  \\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/site-navigation/site-navigation-menu-web/src/main/resources/com/liferay/site/navigation/menu/web/portlet/template/dependencies}{widget\n  templates} for the navigation menu. Note that \\texttt{navigation.ftl}\n  also defines the hamburger icon and \\texttt{navbar-collapse} class\n  that provides the simplified navigation toggle for mobile viewports,\n  as shown in the snippet below for the Classic theme:\n\\end{itemize}\n\n\\begin{verbatim}\n<#if has_navigation && is_setup_complete>\n  <button aria-controls=\"navigationCollapse\" aria-expanded=\"false\" \n  aria-label=\"Toggle navigation\" class=\"navbar-toggler navbar-toggler-right\" \n  data-target=\"#navigationCollapse\" data-toggle=\"collapse\" type=\"button\">\n    <span class=\"navbar-toggler-icon\"></span>\n  </button>\n\n  <div aria-expanded=\"false\" class=\"collapse navbar-collapse\" id=\"navigationCollapse\">\n    <@liferay.navigation_menu default_preferences=\"${preferences}\" />\n  </div>\n</#if>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portal-layout-mobile-nav.png}\n\\caption{The collapsed navbar provides simplified user-friendly\nnavigation for mobile devices.}\n\\end{figure}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{portal\\_pop\\_up.ftl}: Controls pop up dialogs for the theme's\n  portlets. Similar to \\texttt{portal\\_normal.ftl},\n  \\texttt{portal\\_pop\\_up.ftl} provides the markup template for all\n  pop-up dialogs, such as a portlet's Configuration menu. It also has\n  access to the FreeMarker variables defined in \\texttt{init.ftl} and\n  \\texttt{init\\_custom.ftl}.\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portal-layout-theme-templates.png}\n\\caption{Each theme template provides a portion of the page's markup and\nfunctionality.}\n\\end{figure}\n\n\\begin{itemize}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-template/portal-template-freemarker/src/main/resources/FTL_liferay.ftl}{\\texttt{FTL\\_Liferay.ftl}}:\n  Provides\n  \\href{/docs/7-2/reference/-/knowledge_base/r/product-freemarker-macros}{macros}\n  for commonly used portlets and theme resources.\n\\item\n  \\texttt{taglib-mappings.properties}: Maps the portal taglibs to\n  FreeMarker macros. Taglibs can quickly create common UI components.\n  This properties file is provided separately for each app taglib. For\n  convenience, these FreeMarker macros appear in the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/freemarker-taglib-macros}{FreeMarker\n  Taglib Mappings reference guide}. See the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs}{Taglib\n  reference} for more information on using each taglib in your theme\n  templates.\n\\end{itemize}\n\n\\section{CSS Frameworks and\nExtensions}\\label{css-frameworks-and-extensions}\n\nThemes are integrated with \\href{https://sass-lang.com/}{SASS}, so you\ncan take full advantage of Sass mixins, nesting, partials, and variables\nin your CSS.\n\nAlso important to note is \\href{https://clayui.com/}{Clay CSS}, the web\nimplementation of Liferay's \\href{https://lexicondesign.io/}{Lexicon\ndesign language}. An extension of Bootstrap, Clay CSS fills the gaps\nbetween Bootstrap and the needs of Liferay DXP, providing additional\ncomponents and CSS patterns that you can use in your themes. Clay base,\nLiferay's Bootstrap API extension, along with Atlas, a custom Bootstrap\ntheme, creates Liferay DXP's Classic theme. See\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/customizing-atlas-and-clay-base-themes}{Customizing\nAtlas and Clay Base Themes} for more information.\n\n\\section{Theme Customizations and\nExtensions}\\label{theme-customizations-and-extensions}\n\nThe theme templates, along with the CSS, provide much of the overall\nlook and feel for the page, but additional extension\npoints/customizations are available. The following extensions and\nmechanisms are available for themes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Color Schemes:} Specifies configurable color scheme settings\n  Administrators can configure via the Look and Feel menu. See the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-color-schemes-for-your-theme}{color\n  scheme tutorial} for more information.\n\\item\n  \\textbf{Configurable Theme Settings:} Administrators can configure\n  theme aspects that change frequently, such as the visibility of\n  certain elements, changing a daily quote, etc. See the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/making-configurable-theme-settings}{Configurable\n  Theme Settings tutorial} for more information.\n\\item\n  \\textbf{Context Contributor:} Exposes Java variables and functionality\n  for use in FreeMarker templates. This allows non-JSP templating\n  languages in themes, widget templates, and any other templates. See\n  the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/injecting-additional-context-variables-and-functionality-into-your-theme-templates}{Context\n  Contributors tutorial} or more information.\n\\item\n  \\textbf{Theme Contributor:} A package containing UI resources, not\n  attached to a theme, that you want to include on every page. See the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site}{Theme\n  Contributors tutorial} for more information.\n\\item\n  \\textbf{Themelet:} Small, extendable, and reusable pieces of code\n  containing CSS and JavaScript. They can be shared with other\n  developers to provide common components for themes. See\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator}{Generating\n  Themelets} for more information.\n\\end{itemize}\n\n\\section{Portlet Customizations and\nExtensions}\\label{portlet-customizations-and-extensions}\n\nYou can customize portlets with these mechanisms and extensions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Portlet FTL Customizations:} Customize the base template\n  markup for all portlets. See the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/theming-portlets}{Theming\n  Portlets} for more information.\n\\item\n  \\textbf{Widget Templates:} Provides an alternate display style for a\n  portlet. Note that not all portlets support widget templates. See the\n  \\href{/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates}{Widget\n  Templates User Guide} for more information.\n\\item\n  \\textbf{Portlet Decorator:} Customizes the exterior decoration for a\n  portlet. See\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/theming-portlets\\#portlet-decorators}{Portlet\n  Decorators} for more information.\n\\item\n  \\textbf{Web Content Template:} Defines how structures are displayed\n  for web content. See the\n  \\href{/docs/7-2/user/-/knowledge_base/u/designing-web-content-with-templates}{Web\n  Content Templates User Guide articles} for more information.\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portal-layout-portlet-customizations.png}\n\\caption{There are several extension points for customizing portlets}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-12}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/understanding-the-page-structure}{Understanding\n  the Page Structure}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Installing\n  the Theme Generator and Creating a Theme}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-themes}{Developing\n  Themes}\n\\end{itemize}\n\n\\chapter{Understanding the Page\nStructure}\\label{understanding-the-page-structure}\n\nUnderstanding the page's structure is crucial to targeting the correct\nmarkup for styling, organizing your content, and creating your site.\nYour page layout is unique to the requirements and design for your site.\nThe Unstyled theme's default page layout is organized into three key\nsections in its \\texttt{portal\\_normal.ftl} template:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Header:} Contains the navigation, site logo and title (if\n  shown), and sign-in link when the user isn't logged in.\n\\item\n  \\textbf{Main Content:} Contains the portlets or fragments for the\n  page.\n\\item\n  \\textbf{Footer:} contains additional information, such as the\n  copyright or author.\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portal-layout-sections.png}\n\\caption{The page layout is broken into three key sections.}\n\\end{figure}\n\n\\section{Portlets or Fragments}\\label{portlets-or-fragments}\n\nThe \\texttt{\\#content} \\texttt{Section} makes up the majority of the\npage. Portlets or fragments are contained inside the\n\\texttt{\\#main-content} \\texttt{div}. Liferay DXP ships with a default\nset of applications that provide common functionality, such as forums\nand Wikis, documents and media, blogs, and more. For more information on\nusing Liferay DXP and its native portlets, see the\n\\href{/documentation/user}{User \\& Admin documentation}. You can also\ncreate custom portlets for your site. Portlets can be added via the Add\nMenu (referred to as widget), included in a sitemap through the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/importing-resources-with-a-theme}{Resources\nImporter}, or they can be\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes}{embedded\nin the page's theme}. See the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{portlet\ntutorials section} for more information on creating and developing\nportlets.\n\nYou can target the elements and IDs shown in the table below to style\nthe page:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nElement\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nID\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{div} & \\texttt{\\#wrapper} & The container div for the page\ncontents \\\\\n\\texttt{header} & \\texttt{\\#banner} & The page's header \\\\\n\\texttt{section} & \\texttt{\\#content} \\textgreater{}\n\\texttt{\\#main-content} & The main contents of the page (portlets or\nfragments) \\\\\n\\texttt{footer} & \\texttt{\\#footer} & The page's footer \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portal-layout-elements.png}\n\\caption{Each section of the page has elements and IDs that you can\ntarget for styling.}\n\\end{figure}\n\nAs shown in the diagram above, you can also add\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/page-fragments}{fragments}\nto a page. Fragments are components---composed of CSS, JavaScript, and\nHTML---that provide key pieces of functionality for the page (i.e.~a\ncarousel or banner). Liferay DXP provides an editor for creating\ncollections of fragments that you can then add to the page. These\nfragments can be edited on the page to suit your vision.\n\n\\section{Layout Templates, Page Templates, and Site\nTemplates}\\label{layout-templates-page-templates-and-site-templates}\n\nThe page layout within the \\texttt{\\#content} Section is determined by\nthe\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/layout-templates-intro}{Layout\nTemplate}. Several layout templates are included out-of-the-box. You can\nalso\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/layout-templates-intro}{create\ncustom layout templates manually} or with the\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator}{Liferay\nTheme Generator's layout sub-generator}.\n\nLayout templates can be pre-configured depending on the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-pages}{page type} you\nchoose when the page is created. Along with setting the types of\nportlets to include on the page, the page template may also define the\ndefault layout template for the page. Climbing further up the scope\nchain, you can create\n\\href{/docs/7-2/user/-/knowledge_base/u/building-sites-from-templates}{Site\nTemplates}, which can define the pages, page templates, layout\ntemplates, and theme(s) to use for site pages.\n\n\\section{Product Navigation Sidebars and\nPanels}\\label{product-navigation-sidebars-and-panels-1}\n\nThe main page layout also contains a few notable sidebars an\nadministrative user can trigger through the Control Menu. These are\nlisted below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Add Menu:} For adding portlets (widgets) and fragments (if\n  applicable) to the page\n\\item\n  \\textbf{Control Menu:} Provides the main navigation for accessing the\n  Add Menu, Product Menu, and Simulation Panel\n\\item\n  \\textbf{Product Menu:} Contains administrative apps, configuration\n  settings, and user account settings, profile, and dashboard page\n\\item\n  \\textbf{Simulation Panel:} Simulates how the page appears on different\n  devices\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portal-layout-nav-control-menu.png}\n\\caption{Remember to account for the product navigation sidebars and\npanels when styling your site.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portal-layout-nav-add-menu.png}\n\\caption{The Add Menu pushes the main contents to the left.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portal-layout-nav-product-menu.png}\n\\caption{The Product Menu pushes the main contents to the right.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portal-layout-nav-simulation-panel.png}\n\\caption{The Simulation Panel pushes the main contents to the left.}\n\\end{figure}\n\nWhen styling the page, you must keep the navigation menus in mind,\nespecially for absolutely positioned elements, such as a fixed navbar.\nIf the user is logged in and can view the Control Menu, the fixed navbar\nmust have a top margin equal to the Control Menu's height.\n\nSee the\n\\href{/docs/7-2/customization/-/knowledge_base/c/product-navigation}{Product\nNavigation articles} for more information on customizing these menus.\n\n\\section{Related Topics}\\label{related-topics-13}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator}{Creating\n  Layout Templates with the Layouts Sub-generator}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/including-layout-templates-with-a-theme}{Bundling\n  Layout Templates with a Theme}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Installing\n  the Liferay Theme Generator and Creating a Theme}\n\\end{itemize}\n\n\\chapter{Bundle Classloading Flow}\\label{bundle-classloading-flow}\n\nThe OSGi container searches several places for imported classes. It's\nimportant to know where it looks and in what order. Liferay DXP's\nclassloading flow for OSGi bundles follows the OSGi Core specification.\nIt's straightforward, but complex. The figure below illustrates the flow\nand this article walks you through it.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/bundle-classloading-flow-chart.png}\n\\caption{This flow chart illustrates classloading in a bundle.}\n\\end{figure}\n\nHere is the algorithm for classloading in a bundle:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If the class is in a \\texttt{java.*} package, delegate loading to the\n  parent classloader. Otherwise, continue.\n\\item\n  If the class is in the OSGi Framework's boot delegation list, delegate\n  loading to the parent classloader. Otherwise, continue.\n\\item\n  If the class is in one of the packages the bundle imports from a wired\n  exporter, the exporting bundle's classloader loads it. A \\emph{wired\n  exporter} is another bundle's classloader that previously loaded the\n  package. If the class isn't found, continue.\n\\item\n  If the class is imported by one of the bundle's required bundles, the\n  required bundle's classloader loads it.\n\\item\n  If the class is in the bundle's classpath (manifest header\n  \\texttt{Bundle-ClassPath}), the bundle's classloader loads it.\n  Otherwise, continue.\n\\item\n  If the class is in the bundle's fragments classpath, the bundle's\n  classloader loads it.\n\\item\n  If the class is in a package that's dynamically imported using\n  \\texttt{DynamicImport-Package} and a wire is established with the\n  exporting bundle, the exporting bundle's classloader loads it.\n  Otherwise, the class isn't found.\n\\end{enumerate}\n\nCongratulations! Now you know how Liferay DXP finds and loads classes\nfor OSGi bundles.\n\n\\chapter{Finding Extension Points}\\label{finding-extension-points}\n\nLiferay DXP provides many features that help users accomplish their\ntasks. Sometimes, however, you may find it necessary to\n\\href{/docs/7-2/customization/-/knowledge_base/c/liferay-customization}{customize\na built-in feature}. It's easy to \\textbf{find} an area you want to\ncustomize, but it may seem like a daunting task to figure out\n\\textbf{how} to customize it. Liferay DXP was developed for easy\ncustomization, meaning it has many extension points you can use to add\nyour own flavor.\n\nThere's a process you can follow that makes finding an extension point a\nbreeze.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Locate the bundle (module) that provides the functionality you want to\n  change.\n\\item\n  Find the components available in the module.\n\\item\n  Discover the extension points for the components you want to\n  customize.\n\\end{enumerate}\n\nThis article demonstrates finding an extension point. It steps through a\nsimple example that locates an extension point for importing LDAP users.\nThe example includes using Liferay DXP's\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-and-configuring-apps\\#using-the-app-manager}{Application\nManager} and\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Felix\nGogo Shell}.\n\n\\section{Locate the Related Module and\nComponent}\\label{locate-the-related-module-and-component}\n\nFirst think of words that describe the application behavior you want to\nchange. The right keywords can help you easily track down the desired\nmodule and its component. Consider the example for importing LDAP users.\nSome candidate keywords for finding the component are \\emph{import},\n\\emph{user}, \\emph{security, }and \\emph{LDAP}.\n\nThe easiest way to discover the module responsible for a particular\nLiferay feature is to use the Application Manager. The Application\nManager lists apps and their included modules/components in an\neasy-to-use interface. It even lists third party apps! You'll use your\nkeywords to target the applicable component.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the App Manager by navigating to \\emph{Control Panel} →\n  \\emph{Apps} → \\emph{App Manager}. The top level lists independent apps\n  and independent modules.\n\\item\n  Navigate the apps and modules to find components that might provide\n  your desired extension point. Remember to check for your keywords in\n  element names. The keyword \\emph{security} is found in the Liferay CE\n  Portal Security app. Select it.\n\\item\n  The Security application has several modules to inspect. Select the\n  \\emph{Liferay Portal Security LDAP Implementation} module.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/ldapimplementation-module.png}\n  \\caption{The module name can be found using the App Manager.}\n  \\end{figure}\n\\item\n  Search through the components, applying your keywords as a guide. Copy\n  the component name you think best fits the functionality you want to\n  customize; you'll inspect it later using the Gogo shell.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/usermodellistener-component.png}\n  \\caption{The component name can be found using the App Manager.}\n  \\end{figure}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** When using the Gogo shell later, understand that it can take \n several tries to find the component you're looking for; Liferay's naming \n conventions facilitate finding extension points in a manageable time frame. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nNext, you'll use the Gogo shell to inspect the component for extension\npoints.\n\n\\section{Finding Extension Points in a\nComponent}\\label{finding-extension-points-in-a-component}\n\nOnce you have the component that relates to the functionality you want\nto extend, you can use the Gogo shell's Service Component Runtime (SCR)\ncommands to inspect it. You can execute SCR commands using\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Liferay Blade\nCLI} or in\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nshell}. This article assumes you're using the Gogo shell.\n\nExecute the following command:\n\n\\begin{verbatim}\nscr:info [COMPONENT_NAME]\n\\end{verbatim}\n\nFor the LDAP example component you copied previously, the command would\nlook like this:\n\n\\begin{verbatim}\nscr:info com.liferay.portal.security.ldap.internal.messaging.UserImportMessageListener\n\\end{verbatim}\n\nThe output includes a lot of information. For this exercise, you're\ninterested in services the component references. They are extension\npoints. For example, here's the reference for the service that imports\nLDAP users:\n\n\\begin{verbatim}\n- _ldapUserImporter: \n  com.liferay.portal.security.ldap.exportimport.LDAPUserImporter \n  SATISFIED \n  1..1 \n  dynamic+greedy\n    target=(*) scope=bundle (1 binding):\n    * Bound to [7764] from bundle 1754 (com.liferay.portal.security.ldap.impl:2.0.4)\n\\end{verbatim}\n\nThe \\texttt{LDAPUserImporter} is the extension point for customizing the\nLDAP user import process! If none of the references satisfy what you're\nlooking for, search other components from the App Manager.\n\nIf you plan on overriding the referenced service, you'll need to\nunderstand the reference's policy and policy option. In the example, the\npolicy is \\texttt{dynamic} and the policy option is \\texttt{greedy}. If\nthe policy is \\texttt{static} and the policy option is\n\\texttt{reluctant}, binding a new higher ranking service in place of a\nbound service requires reactivating the component or changing the\ntarget. For information on the other policies and policy options, visit\nthe \\href{https://osgi.org/download/r6/osgi.enterprise-6.0.0.pdf}{OSGi\nspecification}, in particular, sections 112.3.5 and 112.3.6. See\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-osgi-services}{Overriding\nOSGi Services} to learn how to override a component's service reference.\n\n\\textbf{Important} Not all Liferay extension points are available as\nreferenced services. Service references are common in Declarative\nServices (DS) components, but extension points can be exposed in other\nways too. Here's a brief list of other potential extension points in\nLiferay DXP:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Instances of\n  \\texttt{org.osgi.util.tracker.ServiceTracker\\textless{}S,\\ T\\textgreater{}}\n\\item\n  Uses of Liferay's \\texttt{Registry.getServiceTracker}\n\\item\n  Uses of Liferay's \\texttt{ServiceTrackerMap} or\n  \\texttt{ServiceTrackerCollection}\n\\item\n  Any other component framework or whiteboard implementation (e.g.,\n  HTTP, JAX-RS) that supports tracking services; Blueprint, Apache\n  Dependency Manager, etc. could also introduce extension points.\n\\end{itemize}\n\nThere you have it! In the App Manager, you used keywords to find the\nmodule component whose behavior you wanted to change. Then you used Gogo\nshell to find the component extension point for implementing your\ncustomization.\n\n\\chapter{Troubleshooting\nCustomizations}\\label{troubleshooting-customizations}\n\nWhen coding on any platform, you can sometimes run into issues that have\nno clear resolution. This can be particularly frustrating. If you have\nissues building, deploying, or running apps and modules, you want to\nresolve them fast. These frequently asked questions and answers help you\ntroubleshoot and correct problems.\n\nClick a question to view the answer.\n\n{Why aren't my fragment's JSP overrides showing?~{}}\n\n\\begin{verbatim}\n<p><a href=\"/docs/7-2/customization/-/knowledge_base/c/why-arent-jsp-overrides-i-made-using-fragments-showing\">Make sure your <code>Fragment-Host</code>'s bundle version is compatible with the host's bundle version</a>. </p>\n\\end{verbatim}\n\n{Why doesn't the package I use from the fragment host resolve?~{}}\n\n\\begin{verbatim}\n<p><a href=\"/docs/7-2/customization/-/knowledge_base/c/why-is-a-package-i-use-from-the-fragment-host-unresolved\">Refrain from importing (<code>Import-Package: ...</code>) host packages that the host doesn't export</a>. </p>\n\\end{verbatim}\n\n\\phantomsection\\label{cacheable-web-content-taglibs}\n{Why does my web content break when I refresh the page?~{}}\n\n\\begin{verbatim}\n<p>Some taglibs, such as the <code>liferay-map</code> taglib, have limitations when used in a cacheable template (e.g., FreeMarker and Velocity). For instance, when the <code>liferay-map</code> taglib is used in a cacheable template and the user refreshes the page, the map does not show. </p>\n<p>One possible workaround is to disable cache for the template by editing it and unchecking the cacheable option. Alternatively, you can disable cache for all templates by navigating to <code>System Settings</code>&rarr;<code>Template Engines</code> and setting <code>Resource Modification Check</code> to <code>0</code>. </p>\n<p>As best practice, however, we recommend that you don't use taglibs in cacheable web content. </p>\n\\end{verbatim}\n\n\\chapter{Why doesn't the package I use from the fragment host\nresolve?}\\label{why-doesnt-the-package-i-use-from-the-fragment-host-resolve}\n\nAn OSGi fragment can access all of the fragment host's packages---it\ndoesn't need to import them from another bundle. bnd adds external\npackages the fragment uses (even ones in the fragment host) to the\nfragment's \\texttt{Import-Package:\\ {[}package{]},...} OSGi manifest\nheader. That's fine for packages exported to the OSGi runtime. The\nproblem is, however, when bnd tries to import a host's internal package\n(a package the host doesn't export). The OSGi runtime can't activate the\nfragment because the internal package remains an\n\\texttt{Unresolved\\ requirement}---a fragment shouldn't import a\nfragment host's packages.\n\nResolve the issue by explicitly excluding host packages that the host\ndoesn't export.\n\nFor example, this fragment bundle's JSP uses classes from the fragment\nhost bundle's internal package\n\\texttt{com.liferay.portal.search.web.internal.custom.facet.display.context}:\n\n\\begin{verbatim}\n<%@\npage import=\"com.liferay.portal.search.web.internal.custom.facet.display.context.CustomFacetDisplayContext\" %><%@\npage import=\"com.liferay.portal.search.web.internal.custom.facet.display.context.CustomFacetTermDisplayContext\" %>\n\\end{verbatim}\n\nSince the example host bundle doesn't export the package, the fragment\nbundle can avoid importing the package by using an OSGi manifest header,\nlike the one below, to explicitly exclude the package from package\nimports:\n\n\\begin{verbatim}\nImport-Package: !com.liferay.portal.search.web.internal.*,*\n\\end{verbatim}\n\n\\chapter{Why Aren't JSP overrides I Made Using Fragments\nShowing?}\\label{why-arent-jsp-overrides-i-made-using-fragments-showing}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} It's strongly recommended to\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps}{customize\nJSPs using Liferay DXP's API}. Since overriding a JSP using an OSGi\nfragment is not based on APIs there's no way to guarantee that it will\nfail gracefully. Instead, if your customization is buggy (because of\nyour code or because of a change in Liferay), you are most likely to\nfind out at runtime, where functionality breaks and nasty log errors\ngreet you. Overriding a JSP using a fragment should only be used as a\nlast resort.\n\n\\noindent\\hrulefill\n\nThe fragment module must specify the exact version of the host module. A\nLiferay DXP upgrade might have changed some JSPs in the host module,\nprompting a version update. If this occurs, check that your JSP\ncustomizations are compatible with the updated host JSPs and then update\nyour fragment module's targeted version to match the host module.\n\nFor example, this \\texttt{bnd.bnd} file from a fragment module uses\n\\texttt{Fragment-Host} to specify the host module and host module\nversion:\n\n\\begin{verbatim}\nBundle-Name: custom-login-jsp\nBundle-SymbolicName: custom.login.jsp\nBundle-Version: 1.0.0\nFragment-Host: com.liferay.login.web;bundle-version=\"1.1.18\"\n\\end{verbatim}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/finding-artifacts}{Finding\nversions of deployed modules} is straightforward.\n\n\\section{Related Topics}\\label{related-topics-14}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters}{JSP\nOverrides using Portlet Filters}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps}{Customizing\nJSPs}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/finding-artifacts}{Finding\nArtifacts}\n\n\\chapter{Using OSGi Services from EXT\nPlugins}\\label{using-osgi-services-from-ext-plugins}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/service-trackers-for-osgi-services}{\\texttt{ServiceTrackers}}\nare the best way for Ext plugins to access OSGi services. They account\nfor the possibility of OSGi services coming and going.\n\n\\section{Related Topics}\\label{related-topics-15}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/detecting-unresolved-osgi-components}{Detecting\nUnresolved OSGi Components}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Felix\nGogo Shell}\n\n\\chapter{Contributing to Liferay\nPortal}\\label{contributing-to-liferay-portal}\n\nLiferay Portal is developed by its community consisting of users,\nenthusiasts, employees, customers, partners, and others. We strongly\nencourage you to contribute to Liferay's open source projects by\nimplementing new features, enhancing existing features, and fixing bugs.\nWe also welcome your participation in our forums, chat, writing\ndocumentation, and translating existing documentation.\n\nLiferay Portal is known for its innovative top quality features. To\nmaintain this reputation, all code changes are reviewed by a core set of\nproject maintainers. We encourage you to join our\n\\href{https://liferay-community.slack.com}{Slack Chat} and introduce\nyourself to the core maintainer(s) and engage them as you contribute to\nthe areas they maintain.\n\nDeveloping features and fixes requires cloning the source tree and\nbuilding Liferay Portal.\n\n\\section{Building Liferay Portal from\nsource}\\label{building-liferay-portal-from-source}\n\nThe first step to contributing to Liferay Portal is to clone the\n\\texttt{liferay-portal} repo from GitHub and build the platform from\nsource code.\n\nPlease follow the instructions for\n\\href{https://portal.liferay.dev/participate/fix-a-bug/building-liferay-source}{building\nLiferay Portal from source code}.\n\nTo better understand the code structure, please also read\n\\href{https://portal.liferay.dev/participate/fix-a-bug/how-the-source-is-organized}{How\nthe source is organized}.\n\n\\section{Tooling}\\label{tooling}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/tooling}{Liferay tooling}\nfacilitates creating customizations and debugging code. Consider using\nthese Liferay development tools:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}: a\n  command line interface used to build and manage Liferay Workspaces and\n  Liferay Portal projects. This CLI is intended for Gradle or Maven\n  development.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\n  Workspace}: a generated Gradle/Maven environment built to hold and\n  manage Liferay Portal projects.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\n  Dev Studio}: an Eclipse-based IDE supporting development for Liferay\n  Portal.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/intellij}{Liferay\n  IntelliJ Plugin}: a plugin providing support for Liferay Portal\n  development with IntelliJ IDEA.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-generator}{Liferay\n  Theme Generator}: a generator that creates themes, layouts templates,\n  and themelets for Liferay Portal development.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/js-generator}{Liferay JS\n  Generator}: a generator that creates JavaScript portlets with\n  JavaScript tooling.\n\\end{itemize}\n\nThe\n\\href{https://portal.liferay.dev/participate/fix-a-bug/ide-support}{Configure\nan IDE for use with the Liferay Source} page, explains how to set up the\nproject in your favorite IDE.\n\n\\section{Additional Resources}\\label{additional-resources}\n\n\\href{https://liferay.dev}{Liferay Community Site}\n\n\\href{https://liferay-community.slack.com/}{Liferay Community Slack\nChat}\n\n\\href{https://liferay.dev/chat}{Liferay Community Slack Chat Self\nInvite}\n\n\\href{https://www.liferay.com/legal/contributors-agreement}{Contributor\nLicense Agreement}\n\n\\href{http://help.github.com/}{General GitHub documentation}\n\n\\href{http://help.github.com/send-pull-requests/}{GitHub pull request\ndocumentation}\n\n\\chapter{Model Listeners}\\label{model-listeners}\n\nModel Listeners implement the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/ModelListener.html}{\\texttt{ModelListener}\ninterface}. They are used to listen for persistence events on models and\ndo something in response (either before or after the event).\n\nModel listeners are designed to perform lightweight actions in response\nto a \\texttt{create}, \\texttt{remove}, or \\texttt{update} attempt on an\nentity's database table or a mapping table (for example,\n\\texttt{users\\_roles}). Here are some supported use cases:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Audit Listener: In a separate database, record information about\n  updates to an entity's database table.\n\\item\n  Cache Clearing Listener: Clear caches that you've added to improve the\n  performance of custom code.\n\\item\n  Validation Listener: Perform additional validation on a model's\n  attribute values before they are persisted to the database.\n\\item\n  Entity Update Listener: Do some additional processing when an entity\n  table is updated. For example, notify users when changes are made to\n  their account.\n\\end{itemize}\n\nThere are also use cases that are not recommended, since they're likely\nto break unpredictably and give you headaches:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Setting a model's attributes in an \\texttt{onBeforeUpdate} call. If\n  some other database table has already been updated with the values\n  before your model listener is invoked, your database gets out of sync.\n  To change how an entity's attributes are set, consider using a\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers}{service\n  wrapper} instead.\n\\item\n  Wrapping a model. Model listeners are not called when fetching records\n  from the database.\n\\item\n  Creating worker threads to do parallel processing and querying data\n  you updated via your listener. Model listeners are called\n  \\emph{before} the database transaction is complete (even the\n  \\texttt{onAfter...} methods), so the queries could be executed before\n  the database transaction completes.\n\\end{itemize}\n\nIf there is no existing listener on the model, your model listener is\nthe only one that runs. However, there can be multiple listeners on a\nsingle model, and the order in which the listeners run cannot be\ncontrolled.\n\nYou can create a model listener in a module by doing two simple things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Implement \\texttt{ModelListener}\n\\item\n  Register the service in Liferay's OSGi runtime\n\\end{itemize}\n\n\\section{Creating a Model Listener\nClass}\\label{creating-a-model-listener-class}\n\nCreate a \\texttt{-ModelListener} class that extends the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/BaseModelListener.html}{\\texttt{BaseModelListener}\nclass}.\n\n\\begin{verbatim}\npackage ...;\n\nimport ...;\n\npublic class CustomEntityListener extends BaseModelListener<CustomEntity> {\n\n    // Override one or more methods from the ModelListener interface.\n    \n}\n\\end{verbatim}\n\nIn the body of the class, override any methods from the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/ModelListener.html}{\\texttt{ModelListener}\ninterface}. The available methods are listed and described at the end of\nthis article.\n\nIn your model listener class, the parameterized type (for example,\n\\texttt{CustomEntity} in the snippet above) tells the listener's\n\\texttt{ServiceTrackerCustomizer} which model class to register the\nlistener against.\n\n\\section{Register the Model Listener\nService}\\label{register-the-model-listener-service}\n\nRegister the service with Liferay's OSGi runtime for immediate\nactivation. If using Declarative Services, set\n\\texttt{service=\\ ModelListener.class} and \\texttt{immediate=true} in\nthe Component:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    service = ModelListener.class\n)\n\\end{verbatim}\n\nThat's all there is to preparing a model listener. Now learn what model\nevents you can respond to.\n\n\\section{Listening For Persistence\nEvents}\\label{listening-for-persistence-events}\n\nThe \\texttt{ModelListener} interface provides lots of opportunity to\nlisten for model events:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{\\texttt{onAfterAddAssociation}:} If there's an association\n  between two models (if they have a mapping table), use this method to\n  do something after an association record is added.\n\\item\n  \\textbf{\\texttt{onAfterCreate}:} Use this method to do something after\n  the persistence layer's \\texttt{create} method is called.\n\\item\n  \\textbf{\\texttt{onAfterRemove}:} Use this method to do something after\n  the persistence layer's \\texttt{remove} method is called.\n\\item\n  \\textbf{\\texttt{onAfterRemoveAssociation}:} If there's an association\n  between two models (if they have a mapping table), do something after\n  an association record is removed.\n\\item\n  \\textbf{\\texttt{onAfterUpdate}:} Use this method to do something after\n  the persistence layer's \\texttt{update} method is called.\n\\item\n  \\textbf{\\texttt{onBeforeAddAssociation}:} If there's an association\n  between two models (if they have a mapping table), do something before\n  an addition to the mapping table.\n\\item\n  \\textbf{\\texttt{onBeforeCreate}:} Use this method to do something\n  before the persistence layer's \\texttt{create} method is called.\n\\item\n  \\textbf{\\texttt{onBeforeRemove}:} Use this method to do something\n  before the persistence layer's \\texttt{remove} method is called.\n\\item\n  \\textbf{\\texttt{onBeforeRemoveAssociation}:} If there's an association\n  between two models (if they have a mapping table), do something before\n  a removal from the mapping table.\n\\item\n  \\textbf{\\texttt{onBeforeUpdate}:} Use this method to do something\n  before the persistence layer's \\texttt{update} method is called.\n\\end{itemize}\n\nLook in Liferay source file\n\\texttt{portal-kernel/src/com/liferay/portal/kernel/service/persistence/impl/BasePersistenceImpl.java},\nparticularly the \\texttt{remove} and \\texttt{update} methods, and you'll\nsee how model listeners are accounted for before (for the\n\\texttt{onBefore...} case) and after (for the \\texttt{onAfter...} case)\nthe model persistence event.\n\nNow that you know how to create model listeners, keep in mind that\nthey're useful as standalone projects or inside of your application. If\nyour application needs to do something (like add a custom entity) every\ntime a User is added in Liferay, you can include the model listener\ninside your application.\n\n\\section{Related Topics}\\label{related-topics-16}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-model-listener-hooks}{Upgrading\n  Model Listener Hooks}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\n  Builder}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\n  Builder Persistence}\n\\end{itemize}\n\n\\chapter{Customizing JSPs}\\label{customizing-jsps}\n\nThere are several different ways to customize JSPs in portlets and the\ncore. Liferay DXP's API provides the safest ways to customize them. If\nyou customize a JSP by other means, new versions of the JSP can render\nyour customization invalid and leave you with runtime errors. It's\nhighly recommended to use one of the API-based ways.\n\n\\section{Using Liferay DXP's API to Override a\nJSP}\\label{using-liferay-dxps-api-to-override-a-jsp}\n\nHere are API-based approaches to overriding JSPs in Liferay DXP:\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.2857}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3571}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3571}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\n\\textbf{Approach}\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n\\textbf{Description}\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n\\textbf{Cons/Limitations}\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps-with-dynamic-includes}{Dynamic\nincludes} & Adds content at dynamic include tags. & Limited to JSPs that\nhave \\texttt{dynamic-include} tags (or tags whose classes inherit from\n\\texttt{IncludeTag}). Only inserts content in the JSPs at the dynamic\ninclude tags. \\\\\n\\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters}{Portlet\nfilters} & Modifies portlet requests and/or responses to simulate a JSP\ncustomization. & Although this approach doesn't directly customize a\nJSP, it achieves the effect of a JSP customization. \\\\\n\\end{longtable}\n\n\\section{Overriding a JSP Without Using Liferay DXP's\nAPI}\\label{overriding-a-jsp-without-using-liferay-dxps-api}\n\nIt's strongly recommended to customize JSPs using Liferay DXP's API, as\nthe previous section describes. Since overriding a JSP using an OSGi\nfragment or a Custom JSP Bag is not based on APIs there's no way to\nguarantee that they'll fail gracefully. Instead, if your customization\nis buggy (because of your code or because of a change in Liferay), you\nare most likely to find out at runtime, where functionality breaks and\nnasty log errors greet you. These approaches should only be used as a\nlast resort.\n\nIf you're maintaining a JSP customization that uses one of these\napproaches, you should know how they work. This section describes them\nand links to their tutorials.\n\nHere are ways to customize JSPs without using Liferay DXP's API:\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.2857}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3571}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3571}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\n\\textbf{Approach}\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n\\textbf{Description}\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n\\textbf{Cons/Limitations}\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-osgi-fragments}{OSGi\nfragment} & Completely overrides a module's JSP using an OSGi fragment &\nChanges to the original JSP or module can cause runtime errors. \\\\\n\\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-custom-jsp-bag}{Custom\nJSP bag} & Completely override a Liferay DXP core JSP or one of its\ncorresponding \\texttt{-ext.jsp} files. & For Liferay DXP core JSPs only.\nChanges to the original JSP or module can cause runtime errors. \\\\\n\\end{longtable}\n\nAll the JSP customization approaches are available to you. It's time to\ncustomize some JSPs!\n\n\\chapter{Customizing JSPs with Dynamic\nIncludes}\\label{customizing-jsps-with-dynamic-includes}\n\nThe\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/liferay-util/dynamic-include.html}{\\texttt{liferay-util:dynamic-include}\ntag} is placeholder into which you can inject content. Every JSP's\ndynamic include tag is an extension point for inserting content (e.g.,\nJavaScript code, HTML, and more). To do this, create a module that has\ncontent you want to insert, register that content with the dynamic\ninclude tag, and deploy your module.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: If the JSP you want to customize has no\n\\texttt{liferay-util:dynamic-include} tags (or tags whose classes\ninherit from \\texttt{IncludeTag}), you must use a different\ncustomization approach, such as\n\\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters}{portlet\nfilters}.\n\n\\noindent\\hrulefill\n\nBlogs entries contain a good example of how dynamic includes work. For\nreference, you can download the\n\\href{https://portal.liferay.dev/documents/113763090/114000186/example-dynamic-include-blogs-master.zip}{example\nmodule}.\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Find the \\texttt{liferay-util:dynamic-include} tag where you want to\n  insert content and note the tag's key.\n\n  The Blogs app's \\texttt{view\\_entry.jsp} has a dynamic include tag at\n  the top and another at the very bottom.\n\n\\begin{verbatim}\n<%@ include file=\"/blogs/init.jsp\" %>\n\n<liferay-util:dynamic-include key=\"com.liferay.blogs.web#/blogs/view_entry.jsp#pre\" />\n\n    ... JSP content is here\n\n<liferay-util:dynamic-include key=\"com.liferay.blogs.web#/blogs/view_entry.jsp#post\" />\n\\end{verbatim}\n\n  Here are the Blogs view entry dynamic include keys:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{key=\"com.liferay.blogs.web\\#/blogs/view\\_entry.jsp\\#pre\"}\n  \\item\n    \\texttt{key=\"com.liferay.blogs.web\\#/blogs/view\\_entry.jsp\\#post\"}\n  \\end{itemize}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  a module} (e.g., \\texttt{blade\\ create\\ my-dynamic-include}). The\n  module will hold your dynamic include implementation.\n\\item\n  Specify compile-only dependencies, like these Gradle dependencies, in\n  your module build file:\n\n\\begin{verbatim}\ndependencies {\n    compileOnly group: \"javax.portlet\", name: \"portlet-api\", version: \"2.0\"\n    compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\", version: \"3.0.1\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\", version: \"1.0.0\"\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"2.0.0\"\n    compileOnly group: \"org.osgi\", name: \"osgi.cmpn\", version: \"6.0.0\"\n}\n\\end{verbatim}\n\\item\n  Create an OSGi component class that implements the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/servlet/taglib/DynamicInclude.html}{\\texttt{DynamicInclude}\n  interface}.\n\n  Here's an example dynamic include implementation for Blogs:\n\n\\begin{verbatim}\nimport java.io.IOException;\nimport java.io.PrintWriter;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.portal.kernel.servlet.taglib.DynamicInclude;\n\n@Component(\n    immediate = true,\n    service = DynamicInclude.class\n)\npublic class BlogsDynamicInclude implements DynamicInclude {\n\n    @Override\n    public void include(\n            HttpServletRequest request, HttpServletResponse response,\n            String key)\n        throws IOException {\n\n        PrintWriter printWriter = response.getWriter();\n\n        printWriter.println(\n            \"<h2>Added by Blogs Dynamic Include!</h2><br />\");\n    }\n\n    @Override\n    public void register(DynamicIncludeRegistry dynamicIncludeRegistry) {\n        dynamicIncludeRegistry.register(\n            \"com.liferay.blogs.web#/blogs/view_entry.jsp#pre\");\n    }\n\n}\n\\end{verbatim}\n\n  Giving the class an \\texttt{@Component} annotation that has the\n  service attribute \\texttt{service\\ =\\ DynamicInclude.class} makes the\n  class a \\texttt{DynamicInclude} service component.\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    service = DynamicInclude.class\n)\n\\end{verbatim}\n\n  In the \\texttt{include} method, add your content. The example\n  \\texttt{include} method writes a heading.\n\n\\begin{verbatim}\n@Override\npublic void include(\n        HttpServletRequest request, HttpServletResponse response,\n        String key)\n    throws IOException {\n\n    PrintWriter printWriter = response.getWriter();\n\n    printWriter.println(\n    \"<h2>Added by Blogs Dynamic Include!</h2><br />\");\n}\n\\end{verbatim}\n\n  In the \\texttt{register} method, specify the dynamic include tag to\n  use. The example register method targets the dynamic include at the\n  top of the Blogs \\texttt{view\\_entry.jsp}.\n\n\\begin{verbatim}\n@Override\npublic void register(DynamicIncludeRegistry dynamicIncludeRegistry) {\n    dynamicIncludeRegistry.register(\n        \"com.liferay.blogs.web#/blogs/view_entry.jsp#pre\");\n}\n\\end{verbatim}\n\\end{enumerate}\n\nOnce you've\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{deployed\nyour module}, the JSP dynamically includes your content. Congratulations\non injecting dynamic content into a JSP!\n\n\\chapter{JSP Overrides Using Portlet\nFilters}\\label{jsp-overrides-using-portlet-filters}\n\nPortlet filters let you intercept portlet requests before they're\nprocessed and portlet responses after they're processed but before\nthey're sent back to the client. You can operate on the request and / or\nresponse to modify the JSP content. Unlike dynamic includes, portlet\nfilters give you access to all the content sent back to the client.\n\nThis demonstration uses a portlet filter to modify content in Liferay's\nBlogs portlet. For reference, you can download the\n\\href{https://portal.liferay.dev/documents/113763090/114000186/example-portlet-filter-customize-jsp-master.zip}{example\nmodule}.\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new module and make sure it specifies these compile-only\n  dependencies, shown here in Gradle format:\n\n\\begin{verbatim}\ndependencies {\n    compileOnly group: \"javax.portlet\", name: \"portlet-api\", version: \"2.0\"\n    compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\", version: \"3.0.1\"\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"2.0.0\"\n    compileOnly group: \"org.osgi\", name: \"osgi.cmpn\", version: \"6.0.0\"\n}\n\\end{verbatim}\n\\item\n  Create an OSGi component class that implements the\n  \\texttt{javax.portlet.filter.RenderFilter} interface.\n\n  Here's an example portlet filter implementation for Blogs:\n\n\\begin{verbatim}\nimport java.io.IOException;\n\nimport javax.portlet.PortletException;\nimport javax.portlet.RenderRequest;\nimport javax.portlet.RenderResponse;\nimport javax.portlet.filter.FilterChain;\nimport javax.portlet.filter.FilterConfig;\nimport javax.portlet.filter.PortletFilter;\nimport javax.portlet.filter.RenderFilter;\nimport javax.portlet.filter.RenderResponseWrapper;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.portal.kernel.util.PortletKeys;\n\n@Component(\n    immediate = true,\n    property = {\n            \"javax.portlet.name=\" + PortletKeys.BLOGS\n    },\n    service = PortletFilter.class\n)\npublic class BlogsRenderFilter implements RenderFilter {\n\n    @Override\n    public void init(FilterConfig config) throws PortletException {\n\n    }\n\n    @Override\n    public void destroy() {\n\n    }\n\n    @Override\n    public void doFilter(RenderRequest request, RenderResponse response, FilterChain chain)\n            throws IOException, PortletException {\n\n        RenderResponseWrapper renderResponseWrapper = new BufferedRenderResponseWrapper(response);\n\n        chain.doFilter(request, renderResponseWrapper);\n\n        String text = renderResponseWrapper.toString();\n\n        if (text != null) {\n            String interestingText = \"<input  class=\\\"field form-control\\\"\";\n\n            int index = text.lastIndexOf(interestingText);\n\n            if (index >= 0) {\n                String newText1 = text.substring(0, index);\n                String newText2 = \"\\n<p>Added by Blogs Render Filter!</p>\\n\";\n                String newText3 = text.substring(index);\n\n                String newText = newText1 + newText2 + newText3;\n\n                response.getWriter().write(newText);\n            }\n        }\n    }\n\n}\n\\end{verbatim}\n\\item\n  Make your class a \\texttt{PortletFilter} service component by giving\n  it the \\texttt{@Component} annotation that has the service attribute\n  \\texttt{service\\ \\ \\ \\ \\ \\ =\\ PortletFilter.class}. Target the portlet\n  whose content you're overriding by assigning it a javax.portlet.name\n  property that's the same as your portlet's key. Here's the example\n  \\texttt{@Component} annotation:\n\n\\begin{verbatim}\n@Component(\n   immediate = true,\n   property = {\n           \"javax.portlet.name=\" + PortletKeys.BLOGS\n   },\n   service = PortletFilter.class\n)\n\\end{verbatim}\n\\item\n  Override the \\texttt{doFilterMethod} to operate on the request or\n  response to produce the content you want. The example appends a\n  paragraph stating \\texttt{Added\\ \\ \\ \\ \\ \\ by\\ Blogs\\ Render\\ Filter!}\n  to the portlet content:\n\n\\begin{verbatim}\n@Override\npublic void doFilter(RenderRequest request, RenderResponse response, FilterChain chain)\n        throws IOException, PortletException {\n\n    RenderResponseWrapper renderResponseWrapper = new BufferedRenderResponseWrapper(response);\n\n    chain.doFilter(request, renderResponseWrapper);\n\n    String text = renderResponseWrapper.toString();\n\n    if (text != null) {\n        String interestingText = \"<input  class=\\\"field form-control\\\"\";\n\n        int index = text.lastIndexOf(interestingText);\n\n        if (index >= 0) {\n            String newText1 = text.substring(0, index);\n            String newText2 = \"\\n<p>Added by Blogs Render Filter!</p>\\n\";\n            String newText3 = text.substring(index);\n\n            String newText = newText1 + newText2 + newText3;\n\n            response.getWriter().write(newText);\n        }\n    }\n}\n\\end{verbatim}\n\n  The example uses a \\texttt{RenderResponseWrapper} extension class\n  called \\texttt{BufferedRenderResponseWrapper}.\n  \\texttt{BufferedRenderResponseWrapper} is a helper class whose\n  \\texttt{toString} method returns the current response text and whose\n  \\texttt{getWriter} method lets you write data to the response before\n  it's sent back to the client.\n\n\\begin{verbatim}\nimport java.io.CharArrayWriter;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.PrintWriter;\n\nimport javax.portlet.RenderResponse;\nimport javax.portlet.filter.RenderResponseWrapper;\n\npublic class BufferedRenderResponseWrapper extends RenderResponseWrapper {\n\n    public BufferedRenderResponseWrapper(RenderResponse response) {\n        super(response);\n\n        charWriter = new CharArrayWriter();\n    }\n\n    public OutputStream getOutputStream() throws IOException {\n        if (getWriterCalled) {\n            throw new IllegalStateException(\"getWriter already called\");\n        }\n\n        getOutputStreamCalled = true;\n\n        return super.getPortletOutputStream();\n    }\n\n    public PrintWriter getWriter() throws IOException {\n        if (writer != null) {\n            return writer;\n        }\n\n        if (getOutputStreamCalled) {\n            throw new IllegalStateException(\"getOutputStream already called\");\n        }\n\n        getWriterCalled = true;\n\n        writer = new PrintWriter(charWriter);\n\n        return writer;\n    }\n\n    public String toString() {\n        String s = null;\n\n        if (writer != null) {\n            s = charWriter.toString();\n        }\n\n        return s;\n    }\n\n    protected CharArrayWriter charWriter;\n    protected PrintWriter writer;\n    protected boolean getOutputStreamCalled;\n    protected boolean getWriterCalled;\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\nOnce you've\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{deployed\nyour module}, the portlet's JSP shows your custom content.\n\nYour portlet filter operates directly on portlet response content.\nUnlike dynamic includes, portlet filters let you work with all of a\nJSP's content.\n\n\\chapter{JSP Overrides Using OSGi\nFragments}\\label{jsp-overrides-using-osgi-fragments}\n\nYou can completely override JSPs using OSGi fragments. This approach is\npowerful but can make things unstable when the host module is upgraded:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  By overriding an entire JSP, you might not account for new content or\n  new widgets essential to new host module versions.\n\\item\n  Fragments are tied to a specific host module version. If the host\n  module is upgraded, the fragment detaches from it. In this scenario,\n  the original JSPs are still available and the module is functional\n  (but lacks your JSP enhancements).\n\\item\n  Liferay cannot guarantee that JSPs overridden by fragments can be\n  upgraded.\n\\end{enumerate}\n\nUsing OSGi fragments to override JSPs is a bad practice, equivalent to\nusing Ext plugins to customize Liferay DXP. They should only be used as\na last resort. Liferay's API based approaches to overriding JSPs (i.e.,\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps-with-dynamic-includes}{Dynamic\nIncludes} and\n\\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters}{Portlet\nFilters}), on the other hand, provide more stability as they customize\nspecific parts of JSPs that are safe to override. Also, the API based\napproaches don't limit your override to a specific host module version.\nIf you are maintaining existing JSP overrides that use OSGi fragments,\nhowever, this tutorial explains how they work.\n\nAn OSGi fragment that overrides a JSP requires these two things:\n\n\\begin{itemize}\n\\item\n  The host module's symbolic name and version in the OSGi header\n  \\texttt{Fragment-Host} declaration.\n\\item\n  The original JSP with any modifications you need to make.\n\\end{itemize}\n\nFor more information about fragment modules, you can refer to section\n3.14 of the\n\\href{https://osgi.org/specification/osgi.core/7.0.0/framework.module.html}{OSGi\nAlliance's core specification document}.\n\n\\section{Declaring a Fragment Host}\\label{declaring-a-fragment-host}\n\nThere are two players in this game: the fragment and the host. The\nfragment is a parasitic module that attaches itself to a host. That\nsounds harsh, so let's compare the fragment-host relationship to the\nrelationship between a pilot fish and a huge, scary shark. It's\nsymbiotic, really. Your fragment module benefits by not doing much work\n(like the pilot fish who benefits from the shark's hunting prowess). In\nreturn, the host module gets whatever benefits you've conjured up in\nyour fragment's JSPs (for the shark, it gets free dental cleanings!). To\nthe OSGi runtime, your fragment is part of the host module.\n\nYour fragment must declare two things to the OSGi runtime regarding the\nhost module:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The Bundle Symbolic Name of the host module. This is the module\n  containing the original JSP.\n\\item\n  The exact version of the host module to which the fragment belongs.\n\\end{enumerate}\n\nBoth are declared using the OSGi manifest header \\texttt{Fragment-Host}.\n\n\\begin{verbatim}\nFragment-Host: com.liferay.login.web;bundle-version=\"[1.0.0,1.0.1)\"\n\\end{verbatim}\n\nSupplying a specific host module version is important. If that version\nof the module isn't present, your fragment won't attach itself to a\nhost, and that's a good thing. A new version of the host module might\nhave changed its JSPs, so if your now-incompatible version of the JSP is\napplied to the host module, you'll break the functionality of the host.\nIt's better to detach your fragment and leave it lonely in the OSGi\nruntime than it is to break the functionality of an entire application.\n\n\\section{Provide the Overridden JSP}\\label{provide-the-overridden-jsp}\n\nThere are two possible naming conventions for targeting the host\noriginal JSP: \\texttt{portal} or \\texttt{original}. For example, if the\noriginal JSP is in the folder \\texttt{/META-INF/resources/login.jsp},\nthen the fragment bundle should contain a JSP with the same path, using\nthe following pattern:\n\n\\begin{verbatim}\n<liferay-util:include \n    page=\"/login.original.jsp\" (or login.portal.jsp) \n    servletContext=\"<%= application %>\" \n/>\n\\end{verbatim}\n\nAfter that, make your modifications. Just make sure you mimic the host\nmodule's folder structure when overriding its JAR. If you're overriding\nLiferay's login application's \\texttt{login.jsp} for example, you'd put\nyour own \\texttt{login.jsp} in\n\n\\begin{verbatim}\nmy-jsp-fragment/src/main/resources/META-INF/resources/login.jsp\n\\end{verbatim}\n\nIf you must post-process the output, you can update the pattern to\ninclude Liferay DXP's buffering mechanism. Below is an example that\noverrides the original \\texttt{create\\_account.jsp}:\n\n\\begin{verbatim}\n<%@ include file=\"/init.jsp\" %>\n\n<liferay-util:buffer var=\"html\">\n    <liferay-util:include page=\"/create_account.portal.jsp\" \n    servletContext=\"<%= application %>\"/>\n</liferay-util:buffer>\n\n<liferay-util:buffer var=\"openIdFieldHtml\"><aui:input name=\"openId\" \ntype=\"hidden\" value=\"<%= ParamUtil.getString(request, \"openId\") %>\" />\n</liferay-util:buffer>\n\n<liferay-util:buffer var=\"userNameFieldsHtml\"><liferay-ui:user-name-fields />\n</liferay-util:buffer>\n\n<liferay-util:buffer var=\"errorMessageHtml\">\n    <liferay-ui:error \n    exception=\"<%= com.liferay.portal.kernel.exception.NoSuchOrganizationException.class %>\" message=\"no-such-registration-code\" />\n</liferay-util:buffer>\n\n<liferay-util:buffer var=\"registrationCodeFieldHtml\">\n            <aui:input name=\"registrationCode\" type=\"text\" value=\"\">\n                    <aui:validator name=\"required\" />\n            </aui:input>\n</liferay-util:buffer>\n\n<%\n    html = com.liferay.portal.kernel.util.StringUtil.replace(html, \n      openIdFieldHtml, openIdFieldHtml + errorMessageHtml);\n    html = com.liferay.portal.kernel.util.StringUtil.replace(html, \n      userNameFieldsHtml, userNameFieldsHtml + registrationCodeFieldHtml);\n%>\n\n<%=html %>\n\\end{verbatim}\n\n\\section{Using Fragment Host Internal\nPackages}\\label{using-fragment-host-internal-packages}\n\nTo use an internal (unexported) host package, the fragment must\nexplicitly exclude the package from its \\texttt{Import-Package:}\nmanifest header. For example, this \\texttt{Import-Package} header\nexcludes packages that match\n\\texttt{com.liferay.portal.search.web.internal.*}.\n\n\\begin{verbatim}\nImport-Package: !com.liferay.portal.search.web.internal.*,*\n\\end{verbatim}\n\nUnless you explicitly exclude the package, bnd adds the package to the\n\\texttt{Import-Package:} header. Attempting to start the fragment while\nrequiring an unexported package fails because the package is an\nunresolved requirement. For this reason, make sure to exclude such\npackages from your fragment's \\texttt{Import-Package:} header.\n\nEach fragment has full access to the host packages, including its\ninternal (unexported) packages already.\n\nNow you can easily modify the JSPs of any application in Liferay.\n\n\\includegraphics{./images/sharks.jpg}\n\n\\section{Related Topics}\\label{related-topics-17}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters}{JSP\n  Overrides Using Portlet Filters}\n\\end{itemize}\n\n\\chapter{JSP Overrides Using Custom JSP\nBag}\\label{jsp-overrides-using-custom-jsp-bag}\n\nLiferay's API based approaches to overriding JSPs (i.e.,\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps-with-dynamic-includes}{Dynamic\nIncludes} and\n\\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters}{Portlet\nFilters}) are the best way to override JSPs in apps and in the core. You\ncan also use Custom JSP Bags to override core JSPs. But the approach is\nnot as stable as the API based approaches. If your Custom JSP Bag's JSP\nis buggy (because of your code or because of a change in Liferay), you\nare most likely to find out at runtime, where functionality breaks and\nnasty log errors greet you. Using Custom JSP Bags to override JSPs is a\nbad practice, equivalent to using Ext plugins to customize Liferay DXP.\nIf you're maintaining existing Custom JSP Bags, however, this tutorial\nexplains how they work.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Liferay cannot guarantee that JSPs overridden using\nCustom JSP Bag can be upgraded.\n\n\\noindent\\hrulefill\n\nA Custom JSP Bag module must satisfy these criteria:\n\n\\begin{itemize}\n\\item\n  Provides and specifies a custom JSP for the JSP you're extending.\n\\item\n  Includes a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-impl/com/liferay/portal/deploy/hot/CustomJspBag.html}{\\texttt{CustomJspBag}}\n  implementation for serving the custom JSPs.\n\\end{itemize}\n\nThe module provides transportation for this code into Liferay's OSGi\nruntime. After you\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{create\nyour new module}, continue with providing your custom JSP.\n\n\\section{Providing a Custom JSP}\\label{providing-a-custom-jsp}\n\nCreate your JSPs to override Liferay DXP core JSPs. If you're using the\nMaven\n\\href{https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html}{Standard\nDirectory Layout}, place your JSPs under\n\\texttt{src/main/resources/META-INF/jsps}. For example, if you're\noverriding\n\n\\begin{verbatim}\nportal-web/docroot/html/common/themes/bottom-ext.jsp \n\\end{verbatim}\n\nplace your custom JSP at\n\n\\begin{verbatim}\n[your module]/src/main/resources/META-INF/jsps/html/common/themes/bottom-ext.jsp\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you place custom JSPs somewhere other than\n\\texttt{src/main/resources/META-INF/jsps} in your module, assign that\nlocation to a \\texttt{-includeresource:\\ META-INF/jsps=} directive in\nyour module's \\texttt{bnd.bnd} file. For example, if you place custom\nJSPs in a folder \\texttt{src/META-INF/custom\\_jsps} in your module,\nspecify this in your \\texttt{bnd.bnd}:\n\n\\begin{verbatim}\n -includeresource: META-INF/jsps=src/META-INF/custom_jsps\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\section{Implement a Custom JSP Bag}\\label{implement-a-custom-jsp-bag}\n\nLiferay DXP (specifically the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-impl/com/liferay/portal/deploy/hot/CustomJspBagRegistryUtil.html}{\\texttt{CustomJspBagRegistryUtil}\nclass}) loads JSPs from\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-impl/com/liferay/portal/deploy/hot/CustomJspBag.html}{\\texttt{CustomJspBag}}\nservices. Here are steps for implementing a custom JSP bag.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your module, create a class that implements\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-impl/com/liferay/portal/deploy/hot/CustomJspBag.html}{\\texttt{CustomJspBag}}.\n\\item\n  Register your class as an OSGi service by adding an\n  \\texttt{@Component} annotation to it, like this:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"context.id=BladeCustomJspBag\",\n      \"context.name=Test Custom JSP Bag\",\n        \"service.ranking:Integer=100\"\n    }\n)\n\\end{verbatim}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{\\texttt{immediate\\ =\\ true}:} Makes the service available on\n    module activation.\n  \\item\n    \\textbf{\\texttt{context.id}:} Your custom JSP bag class name.\n    Replace \\texttt{BladeCustomJspBag} with your class name.\n  \\item\n    \\textbf{\\texttt{context.name}:} A more human readable name for your\n    service. Replace it with a name of your own.\n  \\item\n    \\textbf{\\texttt{service.ranking:Integer}:} A priority for your\n    implementation. The container chooses the implementation with the\n    highest priority.\n  \\end{itemize}\n\\item\n  Implement the \\texttt{getCustomJspDir} method to return the folder\n  path in your module's JAR where the JSPs reside (for example,\n  \\texttt{META-INF/jsps}).\n\n\\begin{verbatim}\n@Override\npublic String getCustomJspDir() {\n    return \"META-INF/jsps/\";\n}\n\\end{verbatim}\n\\item\n  Create an \\texttt{activate} method and the following fields. The\n  method adds the URL paths of all your custom JSPs to a list when the\n  module is activated.\n\n\\begin{verbatim}\n@Activate\nprotected void activate(BundleContext bundleContext) {\n    _bundle = bundleContext.getBundle();\n\n    _customJsps = new ArrayList<>();\n\n    Enumeration<URL> entries = _bundle.findEntries(\n        getCustomJspDir(), \"*.jsp\", true);\n\n    while (entries.hasMoreElements()) {\n        URL url = entries.nextElement();\n\n        _customJsps.add(url.getPath());\n    }\n}\n\nprivate Bundle _bundle;\nprivate List<String> _customJsps;\n\\end{verbatim}\n\\item\n  Implement the \\texttt{getCustomJsps} method to return the list of this\n  module's custom JSP URL paths.\n\n\\begin{verbatim}\n@Override\npublic List<String> getCustomJsps() {\n    return _customJsps;\n}\n\\end{verbatim}\n\\item\n  Implement the \\texttt{getURLContainer} method to return a new\n  \\texttt{com.liferay.portal.kernel.url.URLContainer}. Instantiate the\n  URL container and override its \\texttt{getResources} and\n  \\texttt{getResource} methods. The \\texttt{getResources} method looks\n  up all the paths to resources in the container by a given path. It\n  returns a \\texttt{HashSet} of \\texttt{Strings} for the matching custom\n  JSP paths. The \\texttt{getResource} method returns one specific\n  resource by its name (the path included).\n\n\\begin{verbatim}\n@Override\npublic URLContainer getURLContainer() {\n    return _urlContainer;\n}\n\nprivate final URLContainer _urlContainer = new URLContainer() {\n\n    @Override\n    public URL getResource(String name) {\n        return _bundle.getEntry(name);\n    }\n\n    @Override\n    public Set<String> getResources(String path) {\n        Set<String> paths = new HashSet<>();\n\n        for (String entry : _customJsps) {\n            if (entry.startsWith(path)) {\n               paths.add(entry);\n            }\n        }\n\n        return paths;\n    }\n\n};\n\\end{verbatim}\n\\item\n  Implement the \\texttt{isCustomJspGlobal} method to return\n  \\texttt{true}.\n\n\\begin{verbatim}\n@Override\npublic boolean isCustomJspGlobal() {\n    return true;\n}\n\\end{verbatim}\n\\end{enumerate}\n\nNow your module provides custom JSPs and a custom JSP bag\nimplementation. When you deploy it, Liferay DXP uses its custom JSPs in\nplace of the core JSPs they override.\n\n\\section{Extend a JSP}\\label{extend-a-jsp}\n\nIf you want to add something to a core JSP, see if it has an empty\n\\texttt{-ext.jsp} and override that instead of the whole JSP. It keeps\nthings simpler and more stable, since the full JSP might change\nsignificantly, breaking your customization in the process. By overriding\nthe \\texttt{-ext.jsp}, you're only relying on the original JSP including\nthe \\texttt{-ext.jsp}. For an example, open\n\\texttt{portal-web/docroot/html/common/themes/bottom.jsp}, and scroll to\nthe end. You'll see this:\n\n\\begin{verbatim}\n<liferay-util:include page=\"/html/common/themes/bottom-ext.jsp\" />\n\\end{verbatim}\n\nIf you must add something to \\texttt{bottom.jsp}, override\n\\texttt{bottom-ext.jsp}.\n\nSince Liferay DXP 7.0, the content from the following JSP files formerly\nin \\texttt{html/common/themes} are inlined to improve performance.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{body\\_bottom-ext.jsp}\n\\item\n  \\texttt{body\\_top-ext.jsp}\n\\item\n  \\texttt{bottom-ext.jsp}\n\\item\n  \\texttt{bottom-test.jsp}\n\\end{itemize}\n\nThey're no longer explicit files in the code base. But you can still\ncreate them in your module to add functionality and content.\n\nRemember, this type of customization is a last resort. Your override may\nbreak due to the nature of this implementation, and core functionality\nin Liferay can go down with it. If the JSP you want to override is in\nanother module, refer to the API based approaches to overriding JSPs\nmentioned at the beginning of the article.\n\n\\section{Site Scoped JSP\nCustomization}\\label{site-scoped-jsp-customization}\n\nIn Liferay Portal 6.2, you could use\n\\href{/docs/6-2/tutorials/-/knowledge_base/t/customizing-sites-and-site-templates-with-application-adapters}{Application\nAdapters} to scope your core JSP customizations to a specific Site.\nSince the majority of JSPs were moved into modules for Liferay DXP 7.0,\nthe use case for this has shrunk considerably. If you must scope a core\nJSP customization to a Site, prepare an application adapter\n\\href{/docs/6-2/tutorials/-/knowledge_base/t/customizing-sites-and-site-templates-with-application-adapters}{as\nyou would have for Liferay Portal 6.2}, and deploy it to 7.0. It will\nstill work. However, note that this approach is deprecated in 7.0 and\nwon't be supported at all in Liferay 8.0.\n\n\\section{Related Topics}\\label{related-topics-18}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-1/tutorials/-/knowledge_base/t/upgrading-core-jsp-hooks}{Upgrading\n  Core JSP Hooks}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters}{JSP\n  Overrides Using Portlet Filters}\n\\end{itemize}\n\n\\chapter{Overriding Inline Content Using\nJSPs}\\label{overriding-inline-content-using-jsps}\n\nSome Liferay DXP core content, such as tag library tags, can only be\noverridden using JSPs ending in \\texttt{.readme}. The suffix\n\\texttt{.readme} facilitates finding them. The code from these JSPs is\nnow inlined (brought into Liferay DXP Java source files) to improve\nperformance. Liferay DXP ignores JSP files with the \\texttt{.readme}\nsuffix. If you add code to a JSP \\texttt{.readme} file and remove the\n\\texttt{.readme} suffix, Liferay DXP uses that JSP instead of the core\ninline content. This tutorial shows you how to make these\ncustomizations.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} This type of customization is a last resort. Your\noverride may break due to the nature of this implementation, and core\nfunctionality can go down with it. Liferay cannot guarantee that content\noverridden using JSP \\texttt{.readme} files can be upgraded.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Modifying a Liferay DXP tag library tag affects all\nuses of that tag in your Liferay DXP installation.\n\n\\noindent\\hrulefill\n\nHere's how to override inline content using JSPs:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Create a\n  \\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-custom-jsp-bag}{Custom\n  JSP Bag} for deploying your JSP. Note the module folder you're storing\n  the JSPs in: the default folder is\n  \\texttt{{[}your\\ module{]}/src/main/resources/META-INF/jsps/}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** you can develop your JSP anywhere, but a Custom JSP Bag module \n provides a straightforward way to build and deploy it.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Download the Liferay DXP source code or browse the source code on\n  \\href{https://github.com/liferay/liferay-portal/tree/7.2.x}{GitHub\n  (Liferay Portal CE)}.\n\\item\n  Search the source code for a \\texttt{.jsp.readme} file that overrides\n  the tag you're customizing.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Files ending in `-ext.jsp.readme` let you prepend or\n append new content to existing content. Examples include the\n `bottom-test.jsp.readme`, `bottom-ext.jsp.readme`,\n `body_top-ext.jsp.readme`, and `body_bottom-ext.jsp.readme` files in\n the Liferay DXP application's `portal-web/docroot/html/common/themes` folder.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  Copy the \\texttt{.jsp.readme} file into your project and drop the\n  \\texttt{.readme} suffix. Use the same relative file path Liferay DXP\n  uses for the \\texttt{.jsp.readme} file. For example, if the file in\n  Liferay DXP is\n\n\\begin{verbatim}\nportal-web/docroot/html/taglib/aui/fieldset/start.jsp.readme\n\\end{verbatim}\n\n  use file path\n\n\\begin{verbatim}\n[your module]/src/main/resources/META-INF/jsps/html/taglib/aui/fieldset/start.jsp\n\\end{verbatim}\n\\item\n  Familiarize yourself with the current UI content and logic, so you can\n  override it appropriately. Tag library tag content logic, for example,\n  is in the respective \\texttt{*Tag.java} file under\n  \\texttt{util-taglib/src/com/liferay/taglib/{[}tag\\ library{]}/}.\n\\item\n  Develop your new logic, keeping in mind the current inline logic\n  you're replacing.\n\\item\n  Deploy your JSP.\n\\end{enumerate}\n\nLiferay DXP uses your JSP in place of the current inline logic. If you\nwant to walk through an example override, continue with this tutorial.\nOtherwise, congratulations on a modified \\texttt{.jsp.readme} file to\noverride core inline content!\n\n\\section{Example: Overriding the fieldset Taglib\nTag}\\label{example-overriding-the-fieldset-taglib-tag}\n\nThis example demonstrates changing the \\texttt{liferay:aui} tag\nlibrary's \\texttt{fieldset} tag. Browsing the Liferay DXP web\napplication or the source code at\n\\texttt{portal-web/docroot/html/taglib/aui/fieldset} reveals these\nfiles:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{start.jsp.readme}\n\\item\n  \\texttt{end.jsp.readme}\n\\end{itemize}\n\nThey can override the logic that creates the start and end of the\n\\texttt{fieldset} tag. The \\texttt{FieldsetTag.java} class's\n\\texttt{processStart} and \\texttt{processEnd} methods implement the\ncurrent inline content. Here's the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/util-taglib/src/com/liferay/taglib/aui/FieldsetTag.java\\#L86-L141}{\\texttt{processStart}}\nmethod:\n\n\\begin{verbatim}\n@Override\nprotected int processStartTag() throws Exception {\n  JspWriter jspWriter = pageContext.getOut();\n\n  jspWriter.write(\"<fieldset class=\\\"fieldset \");\n  jspWriter.write(GetterUtil.getString(getCssClass()));\n  jspWriter.write(\"\\\" \");\n\n  String id = getId();\n\n  if (id != null) {\n    jspWriter.write(\"id=\\\"\");\n    jspWriter.write(id);\n    jspWriter.write(\"\\\" \");\n  }\n\n  jspWriter.write(\n    InlineUtil.buildDynamicAttributes(getDynamicAttributes()));\n\n  jspWriter.write(StringPool.GREATER_THAN);\n\n  String lable = getLabel();\n\n  if (lable != null) {\n    jspWriter.write(\n      \"<legend class=\\\"fieldset-legend\\\"><span class=\\\"legend\\\">\");\n\n    MessageTag messageTag = new MessageTag();\n\n    messageTag.setKey(lable);\n    messageTag.setLocalizeKey(getLocalizeLabel());\n\n    messageTag.doTag(pageContext);\n\n    String helpMessage = getHelpMessage();\n\n    if (helpMessage != null) {\n      IconHelpTag iconHelpTag = new IconHelpTag();\n\n      iconHelpTag.setMessage(helpMessage);\n\n      iconHelpTag.doTag(pageContext);\n    }\n\n    jspWriter.write(\"</span></legend>\");\n  }\n\n  if (getColumn()) {\n    jspWriter.write(\"<div class=\\\"row\\\">\");\n  }\n  else {\n    jspWriter.write(\"<div class=\\\"\\\">\");\n  }\n\n  return EVAL_BODY_INCLUDE;\n}\n\\end{verbatim}\n\nThe code above does this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Write\n  \\texttt{\\textless{}fieldset\\ class=\\textbackslash{}\"fieldset}starting\n  tag.\n\\item\n  Write the CSS class name attribute.\n\\item\n  If the tag has an ID, add the \\texttt{id} as an attribute.\n\\item\n  Write the tag's dynamic attribute (map).\n\\item\n  Close the starting \\texttt{fieldset} tag.\n\\item\n  Get the tag's \\texttt{label} attribute.\n\\item\n  Write the starting \\texttt{legend} element.\n\\item\n  Use \\texttt{getLocalizeLabel()} to add the localized label in the\n  \\texttt{legend}.\n\\item\n  If there's a help message (retrieved from \\texttt{getHelpMessage()}),\n  write it in an \\texttt{icon-help-tag}.\n\\item\n  Write the closing \\texttt{legend} tag.\n\\item\n  If there's a column attribute, write\n  \\texttt{\\textless{}div\\ class=\\textbackslash{}\"row\\textbackslash{}\"\\textgreater{}};\n  else write\n  \\texttt{\\textless{}div\\ class=\\textbackslash{}\"\\textbackslash{}\"\\textgreater{}}.\n\\end{enumerate}\n\nReplicating the current logic in your custom JSP helps you set up the\ntag properly for customizing. The \\texttt{init.jsp} for\n\\texttt{fieldset} initializes all the variables required to create the\nstarting tag. You can use the variables in the \\texttt{start.jsp}. The\nlogic from \\texttt{FieldsetTag}'s \\texttt{processStart} method converted\nto JSP code for \\texttt{start.jsp} (renamed from\n\\texttt{start.jsp.readme}) would look like this:\n\n\\begin{verbatim}\n<%@ include file=\"/html/taglib/aui/fieldset/init.jsp\" %>\n\n<fieldset class=\"fieldset <%= cssClass %>\" <%= Validator.isNotNull(id) ? \"id=\\\"\" + id + \"\\\"\" : StringPool.BLANK %> <%= InlineUtil.buildDynamicAttributes(dynamicAttributes) %>>\n    <c:if test=\"<%= Validator.isNotNull(label) %>\">\n        <legend class=\"fieldset-legend\">\n            <span class=\"legend\">\n                <liferay-ui:message key=\"<%= label %>\" localizeKey=\"<%= localizeLabel %>\" />\n\n                <c:if test=\"<%= Validator.isNotNull(helpMessage) %>\">\n                    <liferay-ui:icon-help message=\"<%= helpMessage %>\" />\n                </c:if>\n            </span>\n        </legend>\n    </c:if>\n\n    <div class=\"<%= column ? \"row\" : StringPool.BLANK %>\">\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} A \\texttt{*Tag.java} file's history might reveal original\nJSP code that was inlined. For example, the logic from \\texttt{fieldset}\ntag's\n\\href{https://github.com/liferay/liferay-portal/blob/df22ba66eff49b76404cfda908d3cd024efbebd9/portal-web/docroot/html/taglib/aui/fieldset/start.jsp}{\\texttt{start.jsp}}\nwas inlined in\n\\href{https://github.com/liferay/liferay-portal/commit/7fba0775bcc1d1a0bc4d107cabfb41a90f15937c\\#diff-2ad802b4c0d8f7a2da45b895e89d6e46}{this\ncommit}.\n\n\\noindent\\hrulefill\n\nOn deploying the \\texttt{start.jsp}, the \\texttt{fieldset} tags render\nthe same as they did before. This is expected because it uses the same\nlogic as \\texttt{FieldsetTag}'s \\texttt{processStart} method.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/jsp-readme-inline-fieldset.png}\n\\caption{Liferay DXP's home page's search and sign in components are in\na \\texttt{fieldset}.}\n\\end{figure}\n\nThe \\texttt{fieldset} starting logic is ready for customization. To test\nthat this works, you'll print the word \\emph{test} surrounded by\nasterisks before the end of the \\texttt{fieldset} tag's starting logic.\nInsert this line before the \\texttt{start.jsp}'s last \\texttt{div} tag:\n\n\\begin{verbatim}\n<c:out value=\"**********test**********\"/>\n\\end{verbatim}\n\nRedeploy the JSP and refresh the page to see the text printed above the\n\\texttt{fieldset}'s fields.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/jsp-readme-override-inline-fieldset.png}\n\\caption{Before the \\texttt{fieldset}'s nested fields, it prints\n\\emph{test} surrounded by asterisks.}\n\\end{figure}\n\nYou know how to override specific Liferay DXP core inline content using\nLiferay's \\texttt{.jsp.readme} files.\n\n\\section{Related Topics}\\label{related-topics-19}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps-with-dynamic-includes}{Customizing\n  JSPs with Dynamic Includes}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters}{JSP\n  Overrides Using Portlet Filters}\n\\end{itemize}\n\n\\chapter{Customizing Widgets}\\label{customizing-widgets}\n\nIt would be nice to apply display changes to specific widget instances\nwithout having to create a hook (e.g., HTML-related change) or change a\ntheme (e.g., CSS-related change). Ideally, you should be able to enable\nauthorized users to apply custom display interfaces to widgets.\n\nBe of good cheer! That's precisely what\n\\href{/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates}{Widget\nTemplates} provide. Now you can customize the way widgets appear on a\npage, removing limitations to the way content is displayed. With Widget\nTemplates, you can define display templates to render asset-centric\nwidgets. Some default widgets already have templating capabilities\n(e.g., \\emph{Web Content} and \\emph{Dynamic Data Lists}), in which you\ncan add as many display options (or templates) as you want. You can also\nadd them to your own applications.\n\nSome portlets that already support Widget Templates are\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Asset Publisher}\n\\item\n  \\emph{Blogs}\n\\item\n  \\emph{Breadcrumb}\n\\item\n  \\emph{Categories Navigation}\n\\item\n  \\emph{Language Selector}\n\\item\n  \\emph{Media Gallery}\n\\item\n  \\emph{Navigation Menu}\n\\item\n  \\emph{RSS Publisher}\n\\item\n  \\emph{Site Map}\n\\item\n  \\emph{Tags Navigation}\n\\item\n  \\emph{Wiki}\n\\end{itemize}\n\nTo leverage the Widget Template API, follow these steps:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  register your portlet to use Widget Templates\n\\item\n  define your display template definitions\n\\item\n  define permissions\n\\item\n  expose the Widget Template functionality to users\n\\end{itemize}\n\nThe detailed steps are in the\n\\href{/docs/7-2/customization/-/knowledge_base/c/implementing-widget-templates}{Implementing\nWidget Templates} article. Here's a high level overview of what you'll\ndo.\n\n\\section{Implementing the TemplateHandler\nInterface}\\label{implementing-the-templatehandler-interface}\n\nTo register your portlet to use Widget Templates, you must implement the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/template/TemplateHandler.html}{\\texttt{TemplateHandler}}\ninterface. Read the interface's Javadoc for more information on each\nmethod provided by the interface.\n\nEach of the methods in this class have a significant role in defining\nand implementing Widget Templates for your custom portlet. The list\nbelow highlights some of the methods defined specifically for Widget\nTemplates:\n\n\\texttt{getClassName()}: Defines the type of entry your portlet is\nrendering.\n\n\\texttt{getName()}: Declares the name of your Widget Template type\n(typically, the name of the portlet).\n\n\\texttt{getResourceName()}: Specifies which resource is using the Widget\nTemplate (e.g., a portlet) for permission checking. This method must\nreturn the portlet's fully qualified portlet ID (e.g.,\n\\texttt{com\\_liferay\\_wiki\\_web\\_portlet\\_WikiPortlet}).\n\n\\texttt{getTemplateVariableGroups()}: Defines the variables exposed in\nthe template editor.\n\n\\texttt{getTemplatesConfigPath()}: Defines the configuration file\ncontaining the display template definition.\n\nNext, you must define your display template definition(s).\n\n\\section{Defining Display Template\nDefinitions}\\label{defining-display-template-definitions}\n\nOnce you've registered your portlet to use Widget Templates, you should\ncreate the display template definitions. These are used to style the\ncontent displayed in the widget.\n\nYou must create a \\texttt{portlet-display-templates.xml} configuration\nfile to define the definitions and point to their styled templated\n(e.g., FreeMarker). Then you must create the templates. These template\ndefinitions are available to apply from a widget's Configuration menu.\n\nNext, you define permissions for your portlet's Widget Templates.\n\n\\section{Defining Permissions}\\label{defining-permissions}\n\nYou must define permissions for your Widget Templates; without\npermissions, anyone in the Site could access and change your widget's\ndisplay templates. Configuring permissions lets administrative users\ngrant permissions only to the Roles that should create and manage\ndisplay templates.\n\nThis is done by creating a \\texttt{default.xml} file in your portlet\ndefining the permissions you want to enforce, wiring it up with your\nportlet, and configuring them for use in Liferay DXP. You can visit\n\\href{/docs/7-2/customization/-/knowledge_base/c/implementing-widget-templates}{this\narticle} for step-by-step instructions on how to complete this.\n\nNext, you'll learn how to expose Widget Template selection for users.\n\n\\section{Exposing the Widget Template\nSelection}\\label{exposing-the-widget-template-selection}\n\nTo expose the Widget Template option to your users, use the\n\\texttt{\\textless{}liferay-ui:ddm-template-selector\\textgreater{}} tag\nin the JSP file that controls your portlet's configuration. This tag\nrequires the following parameters:\n\n\\texttt{className}: your entity's class name.\n\n\\texttt{contextObjects}: accepts a\n\\texttt{Map\\textless{}String,\\ Object\\textgreater{}} with any object you\nwant to the template context.\n\n\\texttt{displayStyle}: your portlet's display style.\n\n\\texttt{displayStyleGroupId}: your portlet's display style group ID.\n\n\\texttt{entries}: accepts a list of your entities (e.g.,\n\\texttt{List\\textless{}YourEntity\\textgreater{}}).\n\nThe variables \\texttt{displayStyle} and \\texttt{displayStyleGroupId} are\npreferences that your portlet stores when you use this taglib and your\nportlet uses the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BaseJSPSettingsConfigurationAction.html}{\\texttt{BaseJSPSettingsConfigurationAction}}\nor\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/DefaultConfigurationAction.html}{\\texttt{DefaultConfigurationAction}}.\nOtherwise, you must obtain the value of those parameters and store them\nmanually in your configuration class.\n\n\\section{Recommendations for Using Widget\nTemplates}\\label{recommendations-for-using-widget-templates}\n\nYou can harness a lot of power by leveraging the Widget Template API. Be\ncareful, for with great power, comes great responsibility! Here are some\npractices you can use to optimize your portlet's performance and\nsecurity.\n\nFirst let's talk about security. You may want to hide some classes or\npackages from the template context to limit the operations that Widget\nTemplates can perform. Liferay DXP provides some system settings, which\ncan be accessed by navigating to \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{System Settings} → \\emph{Template Engines}\n→ \\emph{FreeMarker Engine}, to define the restricted classes, packages,\nand variables. In particular, you may want to add\n\\texttt{serviceLocator} to the list of default values assigned to the\nFreeMarker Engine Restricted variables.\n\nWidget Templates introduce additional processing tasks when your portlet\nis rendered. To minimize negative effects on performance, make your\ntemplates as minimal as possible by focusing on their presentation,\nwhile using the existing API for complex operations. The best way to\nmake Widget Templates efficient is to know your template context well,\nand understand what you can use from it. Fortunately, you don't need to\nmemorize the context information, thanks to Liferay DXP's advanced\ntemplate editor!\n\nTo navigate to the template editor for Widget Templates, go to the Site\nAdmin menu and select \\emph{Configuration} → \\emph{Widget Templates} and\nthen click \\emph{Add} and select the specific portlet on which you\ndecide to create a custom template.\n\nThe template editor provides fields, general variables, and utility\nvariables customized for the portlet you chose. These variable\nreferences are on the left-side panel of the template editor. Place your\ncursor where you want the variable placed and click the desired variable\nto insert it. You can learn more about the template editor in\n\\href{/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates}{Styling\nWidgets with Widget Templates}.\n\nFinally, don't forget to run performance tests and tune the template\ncache options by modifying the \\emph{Resource modification check} field\nin \\emph{System Settings} → \\emph{Template Engines} → \\emph{FreeMarker\nEngine}.\n\nWidget Templates provide power to your portlets by providing infinite\nways of editing your portlet to create new interfaces for your users. Be\nsure to configure your FreeMarker templates appropriately for the most\nefficient customization process.\n\nContinue on to add support for Widget Templates in your portlet.\n\n\\chapter{Implementing Widget\nTemplates}\\label{implementing-widget-templates}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates}{Widget\nTemplates} are ways to customize how a widget looks. You can create\ntemplates for a widget's display and then choose which template is\nactive.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/widget-template-dropdown.png}\n\\caption{By using a custom display template, your portlet's display can\nbe customized.}\n\\end{figure}\n\nTo add Widget Template support to your portlet, follow the steps below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create and register a custom \\texttt{*PortletDisplayTemplateHandler}\n  component. Liferay provides the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portletdisplaytemplate/BasePortletDisplayTemplateHandler.html}{\\texttt{BasePortletDisplayTemplateHandler}}\n  as a base implementation for you to extend. You can check the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/template/TemplateHandler.html}{\\texttt{TemplateHandler}}\n  interface Javadoc to learn about each template handler method.\n\n  The \\texttt{@Component} annotation ties your handler to a specific\n  portlet by setting the property \\texttt{javax.portlet.name} to your\n  portlet's name. The same property should be found in your portlet\n  class. For example,\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"javax.portlet.name=\"+ AssetCategoriesNavigationPortletKeys.ASSET_CATEGORIES_NAVIGATION\n    },\n    service = TemplateHandler.class\n)\n\\end{verbatim}\n\n  The Site Map widget sets the \\texttt{@Component} annotation like this:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = \"javax.portlet.name=\" + SiteNavigationSiteMapPortletKeys.SITE_NAVIGATION_SITE_MAP,\n    service = TemplateHandler.class\n)\npublic class SiteNavigationSiteMapPortletDisplayTemplateHandler\n    extends BasePortletDisplayTemplateHandler {\n}\n\\end{verbatim}\n\n  You'll continue stepping through the Site map widget's\n  \\texttt{TemplateHandler} implementation next.\n\\item\n  Override the base class' \\texttt{getClassName()},\n  \\texttt{getName(...)}, and \\texttt{getResourceName()} methods:\n\n\\begin{verbatim}\n@Override\npublic String getClassName() {\n    return LayoutSet.class.getName();\n}\n\n@Override\npublic String getName(Locale locale) {\n    String portletTitle = _portal.getPortletTitle(\n        SiteNavigationSiteMapPortletKeys.SITE_NAVIGATION_SITE_MAP,\n        ResourceBundleUtil.getBundle(locale, getClass()));\n\n    return LanguageUtil.format(locale, \"x-template\", portletTitle, false);\n}\n\n@Override\npublic String getResourceName() {\n    return SiteNavigationSiteMapPortletKeys.SITE_NAVIGATION_SITE_MAP;\n}\n\\end{verbatim}\n\n  These methods return the template handler's class name, the template\n  handler's name (via\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/localization}{resource\n  bundle}), and the resource name associated with the Widget Template,\n  respectively.\n\\item\n  Override the \\texttt{getTemplateVariableGroups(...)} method to return\n  your widget template's script variable groups. These are used to\n  display hints in the template editor palette.\n\n\\begin{verbatim}\n@Override\npublic Map<String, TemplateVariableGroup> getTemplateVariableGroups(\n        long classPK, String language, Locale locale)\n    throws Exception {\n\n    Map<String, TemplateVariableGroup> templateVariableGroups =\n        super.getTemplateVariableGroups(classPK, language, locale);\n\n    TemplateVariableGroup templateVariableGroup =\n        templateVariableGroups.get(\"fields\");\n\n    templateVariableGroup.empty();\n\n    templateVariableGroup.addCollectionVariable(\n        \"pages\", List.class, PortletDisplayTemplateConstants.ENTRIES,\n        \"page\", Layout.class, \"curPage\", \"getName(locale)\");\n    templateVariableGroup.addVariable(\n        \"site-map-display-context\",\n        SiteNavigationSiteMapDisplayContext.class, \"siteMapDisplayContext\");\n\n    return templateVariableGroups;\n}\n\\end{verbatim}\n\n  For this example, the \\emph{Pages} and \\emph{Site Map Display Context}\n  fields are added to the default variables in the template editor\n  palette.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/widget-template-fields.png}\n  \\caption{You can click a variable to add it to the template editor.}\n  \\end{figure}\n\\item\n  Set your display template configuration file path:\n\n\\begin{verbatim}\n@Override\nprotected String getTemplatesConfigPath() {\n    return \"com/liferay/site/navigation/site/map/web/portlet/template\" +\n        \"/dependencies/portlet-display-templates.xml\";\n}\n\\end{verbatim}\n\n  This method returns the XML file containing the display template\n  definitions available for your portlet. You'll create this file next.\n\\item\n  Create your \\texttt{portlet-display-templates.xml} file to define your\n  display template definitions. For example,\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<root>\n    <template>\n        <template-key>site-map-multi-column-layout-ftl</template-key>\n        <name>portlet-display-template-name-multi-column-layout</name>\n        <description>portlet-display-template-description-multi-column-layout-sitemap</description>\n        <language>ftl</language>\n        <script-file>com/liferay/site/navigation/site/map/web/portlet/template/dependencies/portlet_display_template_multi_column_layout.ftl</script-file>\n        <cacheable>false</cacheable>\n    </template>\n</root>\n\\end{verbatim}\n\n  This defined template option is read and presented to the user through\n  the widget's Configuration menu. Navigate to the Site Map widget's\n  Configuration menu and you can confirm the \\emph{Multi Column Layout}\n  option is available.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/widget-config-display.png}\n  \\caption{You can choose the Widget Template you want to apply from the\n  widget's Configuration menu.}\n  \\end{figure}\n\n  This template is created using FreeMarker. You'll create this template\n  option next.\n\\item\n  Create your template script file that you specified in the previous\n  step. For the Site Map widget, its Multi Column Layout option is\n  configured in a FreeMarker template like this:\n\n\\begin{verbatim}\n<#if entries?has_content>\n    <@liferay_aui.row>\n        <#list entries as entry>\n            <#if layoutPermission.containsWithoutViewableGroup(permissionChecker, entry, \"VIEW\")>\n                <@liferay_aui.col width=25>\n                    <div class=\"results-header\">\n                        <h3>\n                            <a\n\n                            <#assign layoutType = entry.getLayoutType() />\n\n                            <#if layoutType.isBrowsable()>\n                                href=\"${portalUtil.getLayoutURL(entry, themeDisplay)}\"\n                            </#if>\n\n                            >${entry.getName(locale)}</a>\n                        </h3>\n                    </div>\n\n                    <@displayPages\n                        depth=1\n                        pages=entry.getChildren(permissionChecker)\n                    />\n                </@liferay_aui.col>\n            </#if>\n        </#list>\n    </@liferay_aui.row>\n</#if>\n\n<#macro displayPages\n    depth\n    pages\n>\n    <#if pages?has_content && ((depth < displayDepth?number) || (displayDepth?number == 0))>\n        <ul class=\"child-pages\">\n            <#list pages as page>\n                <li>\n                    <a\n\n                    <#assign pageType = page.getLayoutType() />\n\n                    <#if pageType.isBrowsable()>\n                        href=\"${portalUtil.getLayoutURL(page, themeDisplay)}\"\n                    </#if>\n\n                    >${page.getName(locale)}</a>\n\n                    <@displayPages\n                        depth=depth + 1\n                        pages=page.getChildren(permissionChecker)\n                    />\n                </li>\n            </#list>\n        </ul>\n    </#if>\n</#macro>\n\\end{verbatim}\n\n  This template definition enforces page permissions, formats how the\n  pages are displayed (multi column), and provides clickable links for\n  each page.\n\\item\n  Your widget must define permissions for creating and managing display\n  templates. Add the action key \\texttt{ADD\\_PORTLET\\_DISPLAY\\_TEMPLATE}\n  to your portlet's\n  \\texttt{/src/main/resources/resource-actions/default.xml} file:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action Mapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n<resource-action-mapping>\n    ...\n    <portlet-resource>\n        <portlet-name>yourportlet</portlet-name>\n        <permissions>\n            <supports>\n                <action-key>ADD_PORTLET_DISPLAY_TEMPLATE</action-key>\n                <action-key>ADD_TO_PAGE</action-key>\n                <action-key>CONFIGURATION</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            ...\n        </permissions>\n    </portlet-resource>\n    ...\n</resource-action-mapping>\n\\end{verbatim}\n\\item\n  If your widget hasn't defined Liferay permissions before, create a\n  file named \\texttt{portlet.properties} in the \\texttt{/resources}\n  folder and add the following contents providing the path to your\n  \\texttt{default.xml}:\n\n\\begin{verbatim}\ninclude-and-override=portlet-ext.properties\nresource.actions.configs=resource-actions/default.xml\n\\end{verbatim}\n\\item\n  Now expose the Widget Template selector to your users. Include the\n  \\texttt{\\textless{}liferay-ddm:template-selector\\textgreater{}} tag in\n  the JSP file you're using to control your portlet's configuration.\n\n  For example, it may be helpful for you to insert a\n  \\texttt{\\textless{}liferay-frontend:fieldset\\textgreater{}} in your\n  configuration JSP file like this:\n\n\\begin{verbatim}\n<liferay-frontend:fieldset\n    collapsible=\"<%= true %>\"\n    label=\"templates\"\n>\n    <div class=\"display-template\">\n        <liferay-ddm:template-selector\n            classNameId=\"<%= YourEntity.class.getName() %>\"\n            displayStyle=\"<%= displayStyle %>\"\n            displayStyleGroupId=\"<%= displayStyleGroupId %>\"\n            refreshURL=\"<%= PortalUtil.getCurrentURL(request) %>\"\n            showEmptyOption=\"<%= true %>\"\n        />\n    </div>\n</liferay-frontend:fieldset>\n\\end{verbatim}\n\n  In this JSP, the\n  \\texttt{\\textless{}liferay-ddm:template-selector\\textgreater{}} tag\n  specifies the Display Template drop-down menu to be used in the\n  widget's Configuration menu.\n\\item\n  You must now extend your view code to render your portlet using the\n  selected Widget Template.\n\n  First, initialize the Java variables needed for the Widget Template:\n\n\\begin{verbatim}\n<%\nString displayStyle = GetterUtil.getString(portletPreferences.getValue(\"displayStyle\", StringPool.BLANK));\nlong displayStyleGroupId = GetterUtil.getLong(portletPreferences.getValue(\"displayStyleGroupId\", null), scopeGroupId);\n%>\n\\end{verbatim}\n\n  Next, you can test if the Widget Template is configured, grab the\n  entities to be rendered, and render them using the Widget Template.\n  The tag\n  \\texttt{\\textless{}liferay-ddm:template-renderer\\textgreater{}} aids\n  with this process. It automatically uses the selected template or\n  renders its body if no template is selected.\n\n  Here's some example code that demonstrates implementing this:\n\n\\begin{verbatim}\n<liferay-ddm:template-renderer\n    className=\"<%= YourEntity.class.getName() %>\"\n    contextObjects=\"<%= contextObjects %>\"\n    displayStyle=\"<%= displayStyle %>\"\n    displayStyleGroupId=\"<%= displayStyleGroupId %>\"\n    entries=\"<%= yourEntities %>\"\n>\n\n    <%-- The code that renders the default view should be inserted here. --%>\n\n</liferay-ddm:template-renderer>\n\\end{verbatim}\n\n  In this step, you initialized variables dealing with the display\n  settings (\\texttt{displayStyle} and \\texttt{displayStyleGroupId}) and\n  passed them to the tag along with other parameters.\n\n  As an example, the Site Map widget implements the\n  \\texttt{\\textless{}liferay-ddm:template-renderer\\textgreater{}} tag in\n  its \\texttt{view.jsp} like this:\n\n\\begin{verbatim}\n<liferay-ddm:template-renderer\n    className=\"<%= LayoutSet.class.getName() %>\"\n    contextObjects=\"<%= contextObjects %>\"\n    displayStyle=\"<%= siteNavigationSiteMapPortletInstanceConfiguration.displayStyle() %>\"\n    displayStyleGroupId=\"<%= siteNavigationSiteMapDisplayContext.getDisplayStyleGroupId() %>\"\n    entries=\"<%= siteNavigationSiteMapDisplayContext.getRootLayouts() %>\"\n>\n    <%= siteNavigationSiteMapDisplayContext.buildSiteMap() %>\n</liferay-ddm:template-renderer>\n\\end{verbatim}\n\n  This logic builds the site's navigation map when the widget is added\n  to a page.\n\\end{enumerate}\n\nAwesome! Your portlet now supports Widget Templates! Once your script is\nuploaded and saved, Users with the specified Roles can select the\ntemplate when they're configuring the display settings of your portlet\non a page. You can visit the\n\\href{/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates}{Styling\nWidgets with Widget Templates} section for more details on using Widget\nTemplates.\n\n\\chapter{Dynamic Includes}\\label{dynamic-includes}\n\nDynamic includes expose extension points in JSPs for injecting\nadditional HTML, adding resources, modifying editors, and more. Several\ndynamic includes are available. Once you know the dynamic include's key,\nyou can use it to\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps-with-dynamic-includes}{create\na module to inject your content}.\n\nThis section of tutorials lists the available dynamic include keys,\nalong with a description of their use cases and a code example.\n\nThe following extension points are covered in this section:\n\n\\begin{longtable}[]{@{}\n  >{\\centering\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.4074}}\n  >{\\centering\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5926}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\centering\nExtension Point\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\centering\nPurpose\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\href{/docs/7-2/customization/-/knowledge_base/c/bottom-jsp-dynamic-includes}{bottom}\n& Load additional HTML or scripts in the bottom of the theme's body \\\\\n\\href{/docs/7-2/customization/-/knowledge_base/c/top-head-jsp-dynamic-includes}{top\\_head}\n& Load additional links in the theme's head \\\\\n\\href{/docs/7-2/customization/-/knowledge_base/c/top-js-dynamic-include}{top\\_js}\n& Load additional JS files in the theme's head \\\\\n\\href{/docs/7-2/customization/-/knowledge_base/c/wysiwyg-editor-dynamic-includes}{WYSIWYG}\n& Add resources to the editor, listen to events, update the\nconfiguration, etc. \\\\\n\\end{longtable}\n\n\\chapter{WYSIWYG Editor Dynamic\nIncludes}\\label{wysiwyg-editor-dynamic-includes}\n\nAll WYSIWYG editors share the same dynamic include extension points for\nthese things:\n\n\\begin{itemize}\n\\item\n  Adding resources, plugins, etc. to the editor:\n\n  com.liferay.frontend.editor.\\texttt{editorType}.web\\#\\texttt{editorName}\\#additionalResources\n\\item\n  Accessing the editor instance to listen to events, configure it, etc:\n\n  com.liferay.frontend.editor.\\texttt{editorType}.web\\#\\texttt{editorName}\\#onEditorCreate\n\\end{itemize}\n\nThe table below shows the \\texttt{editorType}, variable, and\n\\texttt{editorName}s for each editor:\n\n\\begin{longtable}[]{@{}ccc@{}}\n\\toprule\\noalign{}\neditorType & variable & editorName \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nalloyeditor & alloyEditor & alloyeditor \\\\\n~ & ~ & alloyeditor\\_bbcode \\\\\n~ & ~ & alloyeditor\\_creole \\\\\nckeditor & ckEditor & ckeditor \\\\\n~ & ~ & ckeditor\\_bbcode \\\\\n~ & ~ & ckeditor\\_creole \\\\\ntinymce & tinyMCEEditor & tinymce \\\\\n~ & ~ & tinymce\\_simple \\\\\n\\end{longtable}\n\nThe example below alerts the user when he/she pastes content into the\nCKEditor.\n\n\\texttt{*DynamicInclude} Java Class:\n\n\\begin{verbatim}\n@Component(immediate = true, service = DynamicInclude.class)\npublic class CKEditorOnEditorCreateDynamicInclude implements DynamicInclude {\n\n    @Override\n    public void include(\n            HttpServletRequest request, HttpServletResponse response,\n            String key)\n        throws IOException {\n\n        Bundle bundle = _bundleContext.getBundle();\n\n        URL entryURL = bundle.getEntry(\n            \"/META-INF/resources/ckeditor/extension/ckeditor_alert.js\");\n\n        StreamUtil.transfer(\n            entryURL.openStream(), response.getOutputStream(), false);\n    }\n\n    @Override\n    public void register(\n        DynamicInclude.DynamicIncludeRegistry dynamicIncludeRegistry) {\n\n        dynamicIncludeRegistry.register(\n            \"com.liferay.frontend.editor.ckeditor.web#ckeditor#onEditorCreate\");\n    }\n\n    @Activate\n    protected void activate(BundleContext bundleContext) {\n        _bundleContext = bundleContext;\n    }\n\n    private BundleContext _bundleContext;\n\n}\n\\end{verbatim}\n\nExample JavaScript:\n\n\\begin{verbatim}\n// ckEditor variable is already available in the execution context\nckEditor.on(\n    'paste',\n    function(event) {\n        event.stop();\n\n        alert('Please, do not paste code here!');\n    }\n);\n\\end{verbatim}\n\nNow you know how to use the WYSIWYG editor dynamic includes.\n\n\\section{Related Topics}\\label{related-topics-20}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/bottom-jsp-dynamic-includes}{Bottom\n  JSP Dynamic Includes}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/top-head-jsp-dynamic-includes}{Top\n  Head JSP Dynamic Includes}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/top-js-dynamic-include}{Top\n  JS Dynamic Include}\n\\end{itemize}\n\n\\chapter{Top Head JSP Dynamic\nIncludes}\\label{top-head-jsp-dynamic-includes}\n\nThe \\texttt{top\\_head.jsp} dynamic includes load additional links in the\ntheme's head. It uses the following keys:\n\nLoad additional links in the theme's head before the existing ones:\n\n\\begin{verbatim}\n/html/common/themes/top_head.jsp#pre\n\\end{verbatim}\n\nAlternatively, you can load additional links in the theme's head, after\nthe existing ones:\n\n\\begin{verbatim}\n/html/common/themes/top_head.jsp#post\n\\end{verbatim}\n\nThe example below injects a link into the top of the\n\\texttt{top\\_head.jsp}:\n\n\\begin{verbatim}\n@Component(immediate = true, service = DynamicInclude.class)\npublic class CssTopHeadDynamicInclude extends BaseDynamicInclude {\n\n    @Override\n    public void include(\n            HttpServletRequest request, HttpServletResponse response,\n            String key)\n        throws IOException {\n\n        PrintWriter printWriter = response.getWriter();\n\n        String content = \n    \"<link href=\\\"http://localhost:8080/o/my-custom-dynamic-include/css/mentions.css\\\" \n    rel=\\\"stylesheet\\\" \n    type = \\\"text/css\\\" />\";\n    \n        printWriter.println(content);\n    }\n\n    @Override\n    public void register(DynamicIncludeRegistry dynamicIncludeRegistry) {\n        dynamicIncludeRegistry.register(\"/html/common/themes/top_head.jsp#pre\");\n    }\n  \n}\n\\end{verbatim}\n\nPage Source:\n\n\\begin{verbatim}\n<head>\n  ...\n  <link href=\"http://localhost:8080/o/my-custom-dynamic-include/css/mentions.css\" rel=\"stylesheet\" type=\"text/css\">\n  ...\n</head>\n\\end{verbatim}\n\nNote that the link's \\texttt{href} attribute's value\n\\texttt{/o/my-custom-dynamic-include/} is provided by the OSGi module's\n\\texttt{Web-ContextPath} (\\texttt{/my-custom-dynamic-include} in the\nexample).\n\nNow you know how to use the \\texttt{top\\_head.jsp} dynamic includes.\n\n\\section{Related Topics}\\label{related-topics-21}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/bottom-jsp-dynamic-includes}{Bottom\n  JSP Dynamic Includes}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/top-js-dynamic-include}{Top\n  JS Dynamic Include}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/wysiwyg-editor-dynamic-includes}{WYSIWYG\n  Editor Dynamic Includes}\n\\end{itemize}\n\n\\chapter{Top JS Dynamic Include}\\label{top-js-dynamic-include}\n\nThe \\texttt{top\\_js.jspf} dynamic include adds additional JavaScript\nfiles to the theme's head. For example, you can use this extension point\nto include a JS library that you need present in the theme's head:\n\n\\begin{verbatim}\n/html/common/themes/top_js.jspf#resources\n\\end{verbatim}\n\nThe example below injects a JavaScript file into the top of the\n\\texttt{top\\_js.jspf}:\n\n\\texttt{*DynamicInclude} Java Class:\n\n\\begin{verbatim}\n@Component(immediate = true, service = DynamicInclude.class)\npublic class JSTopHeadDynamicInclude extends BaseDynamicInclude {\n\n  @Override\n    public void include(\n            HttpServletRequest request, HttpServletResponse response,\n            String key)\n        throws IOException {\n\n    PrintWriter printWriter = response.getWriter();\n\n    String content = \"<script charset=\\\"utf-8\\\" src=\\\"/o/my-custom-dynamic-include/my_example_javascript.js\\\" async />\";\n\n    printWriter.println(content);\n    }\n\n    @Override\n    public void register(\n        DynamicInclude.DynamicIncludeRegistry dynamicIncludeRegistry) {\n\n        dynamicIncludeRegistry.register(\n      \"/html/common/themes/top_js.jspf#resources\"\n    );\n    }\n}\n\\end{verbatim}\n\nPage Source:\n\n\\begin{verbatim}\n<head>\n  ...\n  <script charset=\"utf-8\" src=\"/o/my-custom-dynamic-include/my_example_javascript.js\" async>...</script>\n  ...\n</head>\n\\end{verbatim}\n\nNote that the JavaScript \\texttt{src} attribute's value\n\\texttt{/o/my-custom-dynamic-include/...} is provided by the OSGi\nmodule's \\texttt{Web-ContextPath} (\\texttt{/my-custom-dynamic-include}\nin the example).\n\nNow you know how to use the \\texttt{top\\_js.jspf} dynamic include.\n\n\\section{Related Topics}\\label{related-topics-22}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/bottom-jsp-dynamic-includes}{Bottom\n  JSP Dynamic Includes}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/top-head-jsp-dynamic-includes}{Top\n  Head JSP Dynamic Includes}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/wysiwyg-editor-dynamic-includes}{WYSIWYG\n  Editor Dynamic Includes}\n\\end{itemize}\n\n\\chapter{Bottom JSP Dynamic Includes}\\label{bottom-jsp-dynamic-includes}\n\nThe \\texttt{bottom.jsp} dynamic includes load additional HTML or scripts\nin the bottom of the theme's body. The following keys are available:\n\nLoad additional HTML or scripts in the bottom of the theme's body,\nbefore the existing ones:\n\n\\begin{verbatim}\n/html/common/themes/bottom.jsp#pre\n\\end{verbatim}\n\nAlternatively, load HTML or scripts in the bottom of the theme's body,\nafter the existing ones:\n\n\\begin{verbatim}\n/html/common/themes/bottom.jsp#post \n\\end{verbatim}\n\nThe example below includes an additional script for the Simulation panel\nin the bottom of the theme's body, after the existing ones.\n\n\\texttt{SimulationDeviceDynamicInclude} Java class:\n\n\\begin{verbatim}\n@Component(immediate = true, service = DynamicInclude.class)\npublic class SimulationDeviceDynamicInclude extends BaseDynamicInclude {\n\n    @Override\n    public void include(\n            HttpServletRequest request, HttpServletResponse response,\n            String key)\n        throws IOException {\n\n        PrintWriter printWriter = response.getWriter();\n\n        printWriter.print(_TMPL_CONTENT);\n    }\n\n    @Override\n    public void register(DynamicIncludeRegistry dynamicIncludeRegistry) {\n        dynamicIncludeRegistry.register(\"/html/common/themes/bottom.jsp#post\");\n    }\n\n    private static final String _TMPL_CONTENT = StringUtil.read(\n        SimulationDeviceDynamicInclude.class,\n        \"/META-INF/resources/simulation_device_dynamic_include.tmpl\");\n\n}\n\\end{verbatim}\n\n\\texttt{simulation\\_device\\_dynamic\\_include.tmpl}:\n\n\\begin{verbatim}\n<script type=\"text/javascript\">\n    // <![CDATA[\n        AUI().use(\n            'aui-base',\n            function(A) {\n                var frameElement = window.frameElement;\n\n                if (frameElement && frameElement.getAttribute('id') === 'simulationDeviceIframe') {\n                    A.getBody().addClass('lfr-has-simulation-panel');\n                }\n            }\n        );\n    // ]]>\n</script>\n\\end{verbatim}\n\nWhen the Simulation panel is open, the script adds the\n\\texttt{lfr-has-simulation-panel} class to the theme's body.\n\nPage Source:\n\n\\begin{verbatim}\n<body class=\"controls-visible has-control-menu closed  yui3-skin-sam guest-site signed-in public-page site lfr-has-simulation-panel\" id=\"senna_surface1\">\n\\end{verbatim}\n\nNow you know how to use the \\texttt{bottom.jsp} dynamic includes.\n\n\\section{Related Topics}\\label{related-topics-23}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/top-head-jsp-dynamic-includes}{Top\n  Head JSP Dynamic Includes}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/top-js-dynamic-include}{Top\n  JS Dynamic Include}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/wysiwyg-editor-dynamic-includes}{WYSIWYG\n  Editor Dynamic Includes}\n\\end{itemize}\n\n\\chapter{Waiting on Lifecycle Events}\\label{waiting-on-lifecycle-events}\n\nLiferay registers lifecycle events like portal and database\ninitialization into the OSGi service registry. Your OSGi Component or\nnon-component class can listen for these events by way of their service\nregistrations. The\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/module/framework/ModuleServiceLifecycle.html}{\\texttt{ModuleServiceLifecycle}\ninterface} defines these names for the lifecycle event services:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/constant-values.html\\#com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle.DATABASE_INITIALIZED}{DATABASE\\_INITIALIZED}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/constant-values.html\\#com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle.PORTAL_INITIALIZED}{PORTAL\\_INITIALIZED}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/constant-values.html\\#com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle.SPRING_INITIALIZED}{SPRING\\_INITIALIZED}\n\\end{itemize}\n\nHere you'll learn how to wait on lifecycle event services to act on them\nfrom within a component or non-component class.\n\n\\section{Taking action from a\ncomponent}\\label{taking-action-from-a-component}\n\n\\href{https://osgi.org/specification/osgi.cmpn/7.0.0/service.component.html}{Declarative\nServices (DS)} facilitates waiting for OSGi services and acting on them\nonce they're available.\n\nHere's a component whose \\texttt{doSomething} method is invoked once the\n\\texttt{ModuleServiceLifecycle.PORTAL\\_INITIALIZED} lifecycle event\nservice and other services are available.\n\n\\begin{verbatim}\n@Component\npublic class MyXyz implements XyzApi {\n\n    // Plain old OSGi service\n    @Reference\n    private SomeOsgiService _someOsgiService;\n\n    // Service Builder generated service\n    @Reference\n    private DDMStructureLocalService _ddmStructureLocalService;\n\n    // Liferay lifecycle service\n    @Reference(target = ModuleServiceLifecycle.PORTAL_INITIALIZED)\n    private ModuleServiceLifecycle _portalInitialized;\n\n    @Activate\n    public void doSomething() {\n        // `@Activate` method is only executed once all of\n        // `_someOsgiService`,\n        // `_ddmStructureLocalService` and\n        // `_portalInitialized`\n        // are set.\n    }\n}\n\\end{verbatim}\n\nHere's how to act on services in your component:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  For each lifecycle event service and OSGi service your component uses,\n  add a field of that service type and add an \\texttt{@Reference}\n  annotation to that field. The OSGi framework binds the services to\n  your fields. This field, for example, binds to a standard OSGi\n  service.\n\n\\begin{verbatim}\n@Reference\nSomeOsgiService _someOsgiService;\n\\end{verbatim}\n\\item\n  To bind to a particular lifecycle event service, target its name as\n  the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/module/framework/ModuleServiceLifecycle.html}{\\texttt{ModuleServiceLifecycle}\n  interface} defines. This field, for example, targets database\n  initialization.\n\n\\begin{verbatim}\n@Reference(target = ModuleServiceLifecycle.DATABASE_INITIALIZED)\nModuleServiceLifecycle _dataInitialized;\n\\end{verbatim}\n\\item\n  Create a method that's triggered on the event(s) and add the\n  \\texttt{@Activate} annotation to that method. It's invoked when all\n  the service objects are bound to the component's fields.\n\\end{enumerate}\n\nYour component fires (via its \\texttt{@Activate} method) after all its\nservice dependencies resolve. DS components are the easiest way to act\non lifecycle event services.\n\n\\section{Taking action from a non-component\nclass}\\label{taking-action-from-a-non-component-class}\n\nClasses that aren't DS components can use a\n\\texttt{org.osgi.util.tracker.ServiceTracker} or\n\\texttt{org.osgi.util.tracker.ServiceTrackerCustomizer} as a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker\\#creating-a-service-tracker-that-tracks-service-events-using-a-callback-handler}{service\ncallback handler} for the lifecycle event. If you depend on multiple\nservices, add logic to your \\texttt{ServiceTracker} or\n\\texttt{ServiceTrackerCustomizer} to coordinate taking action when all\nthe services are available.\n\nTo target a lifecycle event service, create a service tracker that\nfilters on that service. Use \\texttt{org.osgi.framework.FrameworkUtil}\nto create an \\texttt{org.osgi.framework.Filter} that specifies the\nservice. Then pass that filter as a parameter to the service tracker\nconstructor. For example, this service tracker filters on the lifecycle\nservice \\texttt{ModuleServiceLifecycle.PORTAL\\_INITIALIZED}.\n\n\\begin{verbatim}\nimport org.osgi.framework.Filter;\nimport org.osgi.framework.FrameworkUtil;\n\nFilter filter = FrameworkUtil.createFilter(\n    String.format(\n        \"(&(objectClass=%s)%s)\",\n        ModuleServiceLifecycle.class.getName(),\n        ModuleServiceLifecycle.PORTAL_INITIALIZED));\n\nnew ServiceTracker<>(bundleContext, filter, null);\n\\end{verbatim}\n\nActing on lifecycle event services in this way requires service callback\nhandling and some boilerplate code. Using DS components is easier and\nmore elegant, but at least service trackers provide a way to work with\nlifecycle events outside of DS components.\n\n\\section{Related Topics}\\label{related-topics-24}\n\n\\href{/docs/7-1/tutorials/-/knowledge_base/t/service-trackers}{Service\nTrackers}\n\n\\href{/docs/7-1/reference/-/knowledge_base/r/liferay-startup-phases}{Liferay\nDXP Startup Phases}\n\n\\chapter{Liferay Forms}\\label{liferay-forms}\n\nThe \\href{/docs/7-2/user/-/knowledge_base/u/forms}{Liferay Forms}\napplication is a full-featured form building tool for collecting data.\nThere's lots of built-in functionality. For the pieces you're missing,\nthere are extension points.\n\nThis section of articles shows developers how to\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Store form entry data in an alternative format. The default storage\n  type is JSON.\n\\item\n  {[}Coming Soon{]} Create new form field types.\n\\end{enumerate}\n\n\\section{Liferay Forms Extension\nPoints}\\label{liferay-forms-extension-points}\n\nHere's a compilation of the Liferay Forms application's extension points\nthat are ready for your customization:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Create a Form Storage Adapter by implementing a\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/storage/StorageAdapter.java}{\\texttt{StorageAdapter}}\n  or by extending the Abstract implementation,\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/storage/BaseStorageAdapter.java}{\\texttt{BaseStorageAdapter}}.\n\\item\n  Create a Form Field Type by implementing a\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/form/field/type/DDMFormFieldType.java}{\\texttt{DDMFormFieldType}},\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/form/field/type/DDMFormFieldTypeSettings.java}{\\texttt{DDMFormFieldTypeSettings}},\n  and a\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/form/field/type/DDMFormFieldTemplateContextContributor.java}{\\texttt{DDMFormFieldTemplateContextContributor}}.\n\\item\n  Create custom validation rules for form fields by implementing a\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/form/field/type/DDMFormFieldValueValidator.java}{DDMFormFieldValueValidator}.\n\\end{itemize}\n\n\\chapter{Form Storage Adapters}\\label{form-storage-adapters}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nWhen a User adds a form record, the Forms API routes the processing of\nthe request through the storage adapter API. The same is true for the\nother \\emph{CRUD} operations performed on form entries (read, update,\nand delete operations). The default implementation of the storage\nservice is called \\texttt{JSONStorageAdapter}, and as its name implies,\nit implements the \\texttt{StorageAdapter} interface to provide JSON\nstorage of form entry data.\n\nThe Dynamic Data Mapping (DDM) backend can \\emph{adapt} to other data\nstorage formats for form records. Want to store your data in XML? YAML?\nNo problem. Because the storage API is separated from the regular\nservice calls used to populate the database table for form entries, a\ndeveloper can even choose to store form data outside the Liferay\ndatabase.\n\nDefine your own format to save form entries by writing your own\nimplementation of the \\texttt{StorageAdapter} interface. The interface\nfollows the \\emph{CRUD} approach, so implementing it requires that you\nwrite methods to create, read, update and delete form values.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\texttt{StorageAdapter} interface and it's abstract\nimplementation, \\textbar{} \\texttt{BaseStorageAdapter}, are deprecated\nin 7.0. In the future your code should be migrated to implement the\n\\texttt{DDMStorageAdapter} interface. If you need a storage adapter, the\ncurrent extension of \\texttt{BaseStorageAdapter} (demonstrated in this\ndocumentation), is still the way to create one, but be aware that it\nwill not be available in a future version.\n\n\\noindent\\hrulefill\n\nA newly added storage adapter can only be used with new Forms. All\nexisting Forms continue to use the adapter selected (JSON by default) at\nthe time of their creation, and a different storage adapter cannot be\nselected.\n\nThe example storage adapter in this tutorial serializes form data to be\nstored in a simple file, stored on the file system.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-storage-type.png}\n\\caption{Choose a Storage Type for your form records.}\n\\end{figure}\n\n\\section{Storage Adapter Methods}\\label{storage-adapter-methods}\n\nBefore handling the CRUD logic, write a \\texttt{getStorageType} method.\n\n\\begin{description}\n\\tightlist\n\\item[\\texttt{getStorageType}]\nReturn a human readable String, as \\texttt{getStorageType} determines\nwhat appears in the UI when the form creator is selecting a storage type\nfor their form. The String value you return here is added to the\n\\texttt{StorageAdapterRegistry}'s Map of storage adapters.\n\\end{description}\n\n\\section{The CRUD Methods}\\label{the-crud-methods}\n\n\\texttt{doCreate}: Return a \\texttt{long} that identifies each form\nrecord with a unique file ID. Almost as important is to validate the\nform values being sent through the storage adapter API. This is as\nsimple as calling\n\\texttt{DDMFormValuesValidator.validate(ddmFormValues)}. In addition,\nyou'll interact with at least two other DDM services to get the form the\nvalues are associated with, and to make sure they're linked:\n\\texttt{DDMStructureVersionLocalService} and\n\\texttt{DDMStorageLinkLocalService}. Lastly, the form values in the\n\\texttt{DDMFormValues} object must be serialized (converted) into the\nright storage format. If JSON works for your use case, feel free to use\nthe \\texttt{DDMFormValuesJSONSerializer} service in the Liferay Forms\ncode, as demonstrated in the following article. Otherwise you'll need to\nprovide your own serialization service for the form values.\n\n\\begin{description}\n\\tightlist\n\\item[\\texttt{doGetDDMFormValues}]\nReturn the form values (\\texttt{DDMFormValues}) for a form. You'll call\nthe \\texttt{deserialize} method after retrieving them, to take them from\nthe storage format (e.g., JSON) to a proper \\texttt{DDMFormValues}\nobject. You can use the Liferay Forms\n\\texttt{DDMFormValuesJSONDeserializer} if you're retrieving JSON data.\n\\item[\\texttt{doUpdate}]\nA request to update the values comes from a User in the Liferay Forms\napplication, so call the validator again, serialize the values into the\nproper format, and save them.\n\\item[\\texttt{doDeleteByClass}]\nWhen a delete request is made on a form record directly, delete the form\nvalues in whatever format they're currently being stored in (this is\nentirely dependent on your own application of the storage adapter). In\naddition, retrieve and delete the DDM class storage link using\n\\texttt{DDMStorageLinkLocalService}.\n\\item[\\texttt{doDeleteByDDMStrcuture}]\nWhen a delete request is made on an entire form, delete all the form\nrecords associated with it. In addition, take the form's\n\\texttt{ddmStructureId} and delete all the DDM structure storage links\nthat were created for it.\n\\end{description}\n\n\\section{Validating Form Entries}\\label{validating-form-entries}\n\nBecause the Storage Adapter handles User entered data during the\n\\texttt{add} and \\texttt{update} operations, it's important to validate\nthat the entries include only appropriate data. Add a \\texttt{validate}\nmethod to the \\texttt{StorageAdapter}, calling the Liferay Forms'\n\\texttt{DDMFormValuesValidator} method to do the heavy lifting.\n\n\\begin{verbatim}\nprotected void validate(\n      DDMFormValues ddmFormValues, ServiceContext serviceContext)\n    throws Exception {\n\n    boolean validateDDMFormValues = GetterUtil.getBoolean(\n        serviceContext.getAttribute(\"validateDDMFormValues\"), true);\n\n    if (!validateDDMFormValues) {\n        return;\n    }\n\n    _ddmFormValuesValidator.validate(ddmFormValues);\n}\n\\end{verbatim}\n\nMake sure to do three things:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Retrieve the value of the \\texttt{boolean\\ validateDDMFormValues}\n  attribute from the service context.\n\\item\n  If \\texttt{validateDDMFormValues} is false, exit the validation\n  without doing anything.\n\n  When a User accesses a form at its dedicated link, there's a periodic\n  auto-save process of in-progress form values. There's no need to\n  validate this data until the User hits the \\emph{Submit} button on the\n  form, so the auto-save process sets the \\texttt{validateDDMFormValues}\n  attribute to \\texttt{false}.\n\\item\n  Otherwise, call the validate method from the\n  \\texttt{DDMFormValuesValidator} service.\n\\end{enumerate}\n\nAll the Java code for the logic discussed here is shown in the next\narticle,\n\\href{/docs/7-2/customization/-/knowledge_base/c/creating-a-form-storage-adapter}{Creating\nForm Storage Adapters}.\n\n\\section{Enabling the Storage\nAdapter}\\label{enabling-the-storage-adapter}\n\nThe storage adapter is enabled at the individual form level. Create a\nnew form, and select the Storage Adapter \\emph{before saving or\npublishing the form}. If you wait until first Saving the Form, the\ndefault Storage Adapter is already assigned to the Form, and this\nsetting is no longer editable.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the Site Menu → Content → Forms, and click the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}).\n\\item\n  In the Form Builder view, click the \\emph{Options} button\n  (\\includegraphics{./images/icon-options.png}) and open the\n  \\emph{Settings} window.\n\\item\n  From the select list field called \\emph{Select a Storage Type}, choose\n  the desired type and click \\emph{Done}.\n\\end{enumerate}\n\nNow all the form's entries are stored in the desired format.\n\n\\chapter{Creating a Form Storage\nAdapter}\\label{creating-a-form-storage-adapter}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThere's only one class to create when implementing a Form Storage\nAdapter, and it extends the base \\texttt{StorageAdapter} implementation.\n\n\\begin{verbatim}\n@Component(service = StorageAdapter.class)\npublic class FileSystemStorageAdapter extends BaseStorageAdapter {\n\\end{verbatim}\n\nThe only method without a base implementation in the abstract class is\n\\texttt{getStorageType}. For file system storage, it can return\n\\texttt{\"File\\ System\"}.\n\n\\begin{verbatim}\n@Override\npublic String getStorageType() {\n    return \"File System\";\n}\n\\end{verbatim}\n\n\\section{Storage Adapter CRUD\nOperations}\\label{storage-adapter-crud-operations}\n\nThe CRUD operations must be created to properly handle the Form Records.\n\n\\section{Create}\\label{create}\n\nNext override the \\texttt{doCreateMethod} to return a \\texttt{long} that\nidentifies each form record with a unique file ID:\n\n\\begin{verbatim}\n@Override\nprotected long doCreate(\n    long companyId, long ddmStructureId, DDMFormValues ddmFormValues, \n    ServiceContext serviceContext)\n    throws Exception {\n\n    validate(ddmFormValues, serviceContext);\n\n    long fileId = _counterLocalService.increment();\n\n    DDMStructureVersion ddmStructureVersion =\n        _ddmStructureVersionLocalService.getLatestStructureVersion(\n            ddmStructureId);\n\n    long classNameId = PortalUtil.getClassNameId(\n        FileSystemStorageAdapter.class.getName());\n\n    _ddmStorageLinkLocalService.addStorageLink(\n        classNameId, fileId, ddmStructureVersion.getStructureVersionId(),\n        serviceContext);\n\n    saveFile(\n        ddmStructureVersion.getStructureVersionId(), fileId, ddmFormValues);\n\n    return fileId;\n}\n\n@Reference\nprivate CounterLocalService _counterLocalService;\n\n@Reference\nprivate DDMStorageLinkLocalService _ddmStorageLinkLocalService;\n\n@Reference\nprivate DDMStructureVersionLocalService _ddmStructureVersionLocalService;\n\\end{verbatim}\n\nThese are the utility methods invoked in the create method:\n\n\\begin{verbatim}\nprivate File getFile(long structureId, long fileId) {\n    return new File(\n        getStructureFolder(structureId), String.valueOf(fileId));\n}\n\nprivate File getStructureFolder(long structureId) {\n    return new File(String.valueOf(structureId));\n}\n\nprivate void saveFile(\n        long structureVersionId, long fileId, DDMFormValues formValues)\n    throws IOException {\n\n    String serializedDDMFormValues = _ddmFormValuesJSONSerializer.serialize(\n        formValues);\n\n    File formEntryFile = getFile(structureVersionId, fileId);\n\n    FileUtil.write(formEntryFile, serializedDDMFormValues);\n}\n\n@Reference\nprivate DDMFormValuesJSONSerializer _ddmFormValuesJSONSerializer;\n\\end{verbatim}\n\n\\section{Read}\\label{read}\n\nTo retrieve the form record's values from the \\texttt{File} object where\nthey were written, override \\texttt{doGetDDMFormValues}:\n\n\\begin{verbatim}\n@Override\nprotected DDMFormValues doGetDDMFormValues(long classPK) throws Exception {\n    DDMStorageLink storageLink =\n        _ddmStorageLinkLocalService.getClassStorageLink(classPK);\n\n    DDMStructureVersion structureVersion =\n        _ddmStructureVersionLocalService.getStructureVersion(\n            storageLink.getStructureVersionId());\n\n    String serializedDDMFormValues = FileUtil.read(\n        getFile(structureVersion.getStructureVersionId(), classPK));\n\n    return _ddmFormValuesJSONDeserializer.deserialize(\n        structureVersion.getDDMForm(), serializedDDMFormValues);\n}\n\n@Reference\nprivate DDMFormValuesJSONDeserializer _ddmFormValuesJSONDeserializer;\n\\end{verbatim}\n\n\\section{Update}\\label{update}\n\nOverride the \\texttt{doUpdate} method so the record's values can be\noverwritten. This example calls the \\texttt{saveFile} utility method\nprovided earlier:\n\n\\begin{verbatim}\n@Override\nprotected void doUpdate(\n        long classPK, DDMFormValues ddmFormValues,\n        ServiceContext serviceContext)\n    throws Exception {\n\n    validate(ddmFormValues, serviceContext);\n\n    DDMStorageLink storageLink =\n        _ddmStorageLinkLocalService.getClassStorageLink(classPK);\n\n    saveFile(\n        storageLink.getStructureVersionId(), storageLink.getClassPK(),\n        ddmFormValues);\n}\n\\end{verbatim}\n\n\\section{Delete}\\label{delete}\n\nOverride the \\texttt{doDeleteByClass} method to delete the \\texttt{File}\nrepresenting the form record, using the \\texttt{classPK}, and to delete\nthe class storage links:\n\n\\begin{verbatim}\n@Override\nprotected void doDeleteByClass(long classPK) throws Exception {\n    DDMStorageLink storageLink =\n        _ddmStorageLinkLocalService.getClassStorageLink(classPK);\n\n    FileUtil.delete(getFile(storageLink.getStructureId(), classPK));\n\n    _ddmStorageLinkLocalService.deleteClassStorageLink(classPK);\n}\n\\end{verbatim}\n\nProvide form record deletion logic to be called when deleting all the\nrecords and storage links associated with a form (using its\n\\texttt{ddmStructureId}):\n\n\\begin{verbatim}\n@Override\nprotected void doDeleteByDDMStructure(long ddmStructureId)\n    throws Exception {\n\n    FileUtil.deltree(getStructureFolder(ddmStructureId));\n\n    _ddmStorageLinkLocalService.deleteStructureStorageLinks(ddmStructureId);\n}\n\\end{verbatim}\n\n\\section{Beyond CRUD: Validation}\\label{beyond-crud-validation}\n\nAdd a \\texttt{validate} method to the \\texttt{StorageAdapter}:\n\n\\begin{verbatim}\nprotected void validate(\n    DDMFormValues ddmFormValues, ServiceContext serviceContext)\n    throws Exception {\n\n    boolean validateDDMFormValues = GetterUtil.getBoolean(\n        serviceContext.getAttribute(\"validateDDMFormValues\"), true);\n\n    if (!validateDDMFormValues) {\n        return;\n    }\n\n    _ddmFormValuesValidator.validate(ddmFormValues);\n}\n\\end{verbatim}\n\nDeploy your storage adapter and it's ready to use.\n\n\\chapter{Overriding Language Keys}\\label{overriding-language-keys}\n\nCore and portlet module \\texttt{Language*.properties} files implement\nsite internationalization. They're fully customizable, too. This section\ndemonstrates this in the following topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys}{Overriding\n  Liferay's Language Keys}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys}{Overriding\n  a Module's Language Keys}\n\\end{itemize}\n\n\\chapter{Overriding Global Language\nKeys}\\label{overriding-global-language-keys}\n\nLanguage files contain\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application}{translations\nof your application's user interface messages}. But you can also\noverride the default language keys globally and in other applications\n(including your own). Here are the steps for overriding language keys:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  \\hyperref[determine-the-language-keys-to-override]{Determine the\n  language keys to override}\n\\item\n  \\hyperref[override-the-keys-in-a-new-language-properties-file]{Override\n  the keys in a new language properties file}\n\\item\n  \\hyperref[create-a-resource-bundle-service-component]{Create a\n  Resource Bundle service component}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Many applications that were once part of Liferay Portal\n6.2 are now modularized. Their language keys might have been moved out\nof Liferay's language properties files and into one of the application's\nmodules. The process for\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys}{overriding\na module's language keys} is different from the process for overriding\nLiferay's language keys.\n\n\\noindent\\hrulefill\n\n\\section{Determine the language keys to\noverride}\\label{determine-the-language-keys-to-override}\n\nSo how do you find global language keys? They're in the\n\\texttt{Language{[}xx\\_XX{]}.properties} files in the source code or\nyour bundle.\n\n\\begin{itemize}\n\\item\n  From the source:\n\n  \\texttt{/portal-impl/src/content/Language{[}xx\\_XX{]}.properties}\n\\item\n  From a bundle:\n\n  \\texttt{portal-impl.jar}\n\\end{itemize}\n\nAll language properties files contain properties you can override, like\nthe language settings properties:\n\n\\begin{verbatim}\n##\n## Language settings\n##\n\n...\nlang.user.name.field.names=prefix,first-name,middle-name,last-name,suffix\nlang.user.name.prefix.values=Dr,Mr,Ms,Mrs\nlang.user.name.required.field.names=last-name\nlang.user.name.suffix.values=II,III,IV,Jr,Phd,Sr\n...\n\\end{verbatim}\n\nThere are also many simple keys you can override to update default\nmessages and labels.\n\n\\begin{verbatim}\n##\n## Category titles\n##\n\ncategory.admin=Admin\ncategory.alfresco=Alfresco\ncategory.christianity=Christianity\ncategory.cms=Content Management\n...\n\\end{verbatim}\n\nFor example, Figure 1 shows a button that uses Liferay's\n\\texttt{publish} default language key.\n\n\\begin{verbatim}\n`publish=Publish`\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/standard-publish.png}\n\\caption{Messages displayed in Liferay's user interface can be\ncustomized.}\n\\end{figure}\n\nNext, you'll learn how to override this key.\n\n\\section{Override the keys in a new language properties\nfile}\\label{override-the-keys-in-a-new-language-properties-file}\n\nOnce you know the keys to override, create a language properties file\nfor the locale you want (or the default \\texttt{Language.properties}\nfile) in your module's \\texttt{src/main/resources/content} folder. In\nyour file, define the keys your way. For example, you could override the\n\\texttt{publish} key.\n\n\\begin{verbatim}\npublish=Publish Override\n\\end{verbatim}\n\nTo enable your change, you must create a resource bundle service\ncomponent to reference your language file.\n\n\\section{Create a Resource Bundle service\ncomponent}\\label{create-a-resource-bundle-service-component}\n\nIn your module, create a class that extends\n\\texttt{java.util.ResourceBundle} for the locale you're overriding.\nHere's an example resource bundle class for the \\texttt{en\\_US} locale:\n\n\\begin{verbatim}\n@Component(\n    property = { \"language.id=en_US\" }, \n    service = ResourceBundle.class\n)\npublic class MyEnUsResourceBundle extends ResourceBundle {\n\n    @Override\n    protected Object handleGetObject(String key) {\n        return _resourceBundle.getObject(key);\n    }\n\n    @Override\n    public Enumeration<String> getKeys() {\n        return _resourceBundle.getKeys();\n    }\n\n    private final ResourceBundle _resourceBundle = ResourceBundle.getBundle(\n        \"content.Language_en_US\", UTF8Control.INSTANCE);\n\n}\n\\end{verbatim}\n\nThe class's \\texttt{\\_resourceBundle} field is assigned a\n\\texttt{ResourceBundle}. The call to \\texttt{ResourceBundle.getBundle}\nneeds two parameters. The \\texttt{content.Language\\_en\\_US} parameter is\nthe language file's qualified name with respect to the module's\n\\texttt{src/main/resources} folder. The second parameter is a\n\\texttt{control} that sets the language syntax of the resource bundle.\nTo use language syntax identical to Liferay's syntax, import Liferay's\n\\texttt{com.liferay.portal.kernel.language.UTF8Control} class and set\nthe second parameter to \\texttt{UTF8Control.INSTANCE}.\n\nThe class's \\texttt{@Component} annotation declares it an OSGi\n\\texttt{ResourceBundle} service component. It's \\texttt{language.id}\nproperty designates it for the \\texttt{en\\_US} locale.\n\n\\begin{verbatim}\n@Component(\n    property = { \"language.id=en_US\" }, \n    service = ResourceBundle.class\n)\n\\end{verbatim}\n\nThe class overrides these methods:\n\n\\begin{itemize}\n\\item\n  \\textbf{\\texttt{handleGetObject}:} Looks up the key in the module's\n  resource bundle (which is based on the module's language properties\n  file) and returns the key's value as an \\texttt{Object}.\n\\item\n  \\textbf{\\texttt{getKeys}:} Returns an \\texttt{Enumeration} of the\n  resource bundle's keys.\n\\end{itemize}\n\nYour resource bundle service component redirects the default language\nkeys to your module's language key overrides.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: Global language key overrides for multiple locales\nrequire a separate module for each locale. Each module's\n\\texttt{ResourceBundle} extension class (like the\n\\texttt{MyEnUsResourceBundle} class above) must specify its locale in\nthe \\texttt{language.id} component property definition and in the\nlanguage file qualified name parameter. For example, here is what they\nlook like for the Spanish locale.\n\nComponent definition:\n\n\\begin{verbatim}\n@Component(\n    property = { \"language.id=es_ES\" },\n    service = ResourceBundle.class\n)\n\\end{verbatim}\n\nResource bundle assignment:\n\n\\begin{verbatim}\nprivate final ResourceBundle _resourceBundle = ResourceBundle.getBundle(\n    \"content.Language_es_ES\", UTF8Control.INSTANCE);\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Important}: If your module\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module}{uses\nlanguage keys from another module} and\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module}{overrides\nany of that other module's keys}, make sure to use OSGi headers to\nspecify the capabilities your module requires and provides. This lets\nyou prioritize resource bundles from the modules.\n\nTo see your Liferay language key overrides in action,\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{deploy\nyour module} and visit the portlets and pages that use the keys.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/localized-publish.png}\n\\caption{This button uses the overridden \\texttt{publish} key.}\n\\end{figure}\n\nThat's all there is to overriding Liferay's language keys.\n\n\\section{Related Topics}\\label{related-topics-25}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-core-language-key-hooks}{Upgrading\n  Core Language Key Hooks}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys}{Overriding\n  a Module's Language Keys}\n\\end{itemize}\n\n\\chapter{Overriding a Module's Language\nKeys}\\label{overriding-a-modules-language-keys}\n\nWhat do you do if the language keys you want to modify are in one of\nLiferay's applications or another module whose source code you don't\ncontrol? Since module language keys are in the respective module, the\nprocess for overriding a module's language keys is different from\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-global-language-keys}{the\nprocess of overriding Liferay's language keys}.\n\nHere is the process:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  \\hyperref[find-the-module-and-its-metadata-and-language-keys]{Find the\n  module and its metadata and language keys}\n\\item\n  \\hyperref[write-custom-language-key-values]{Write your custom language\n  key values}\n\\item\n  \\hyperref[prioritize-your-modules-resource-bundle]{Prioritize your\n  module's resource bundle}\n\\end{enumerate}\n\n\\section{Find the module and its metadata and language\nkeys}\\label{find-the-module-and-its-metadata-and-language-keys}\n\nIn\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nshell}, list the bundles and grep for keyword(s) that match the\nportlet's display name. Language keys are in the portlet's web module\n(bundle). When you find the bundle, note its ID number.\n\nTo find the Blogs portlet, for example, your Gogo commands and output\nmight look like this:\n\n\\begin{verbatim}\ng! lb | grep Blogs\n  152|Active     |    1|Liferay Blogs Service (1.0.2)\n  184|Active     |    1|Liferay Blogs Editor Config (2.0.1)\n  202|Active     |    1|Liferay Blogs Layout Prototype (2.0.2)\n  288|Active     |    1|Liferay Blogs Recent Bloggers Web (1.0.2)\n  297|Active     |    1|Liferay Blogs Item Selector Web (1.0.2)\n  374|Active     |    1|Liferay Blogs Item Selector API (2.0.1)\n  448|Active     |    1|Liferay Blogs API (3.0.1)\n  465|Active     |    1|Liferay Blogs Web (1.0.6)\ntrue\n\\end{verbatim}\n\nList the bundle's headers by passing its ID to the \\texttt{headers}\ncommand.\n\n\\begin{verbatim}\ng! headers 465\n\nLiferay Blogs Web (465)\n-----------------------\nManifest-Version = 1.0\nBnd-LastModified = 1459866186018\nBundle-ManifestVersion = 2\nBundle-Name = Liferay Blogs Web\nBundle-SymbolicName = com.liferay.blogs.web\nBundle-Version: 1.0.6\n... \nWeb-ContextPath = /blogs-web\ng! \n\\end{verbatim}\n\nNote the \\texttt{Bundle-SymbolicName}, \\texttt{Bundle-Version}, and\n\\texttt{Web-ContextPath}. The \\texttt{Web-ContextPath} value, following\nthe \\texttt{/}, is the servlet context name.\n\n\\textbf{Important}: Record the servlet context name, bundle symbolic\nname and version, as you'll use them to create the resource bundle\nloader later in the process.\n\nFor example, here are those values for Liferay Blogs Web module:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Bundle symbolic name: \\texttt{com.liferay.blogs.web}\n\\item\n  Bundle version: \\texttt{4.0.16}\n\\item\n  Servlet context name: \\texttt{blogs-web}\n\\end{itemize}\n\nNext find the module's JAR file so you can examine its language keys.\nLiferay follows this module JAR file naming convention:\n\n\\begin{verbatim}\n[bundle symbolic name]-[version].jar\n\\end{verbatim}\n\nFor example, the Blogs Web version 4.0.16 module is in\n\\texttt{com.liferay.blogs.web-4.0.16.jar}.\n\nHere's where to find the module JAR:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay's\n  \\href{https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/}{Nexus\n  repository}\n\\item\n  \\texttt{{[}Liferay\\ Home{]}/osgi/modules}\n\\item\n  Embedded in an application's or application suite's LPKG file in\n  \\texttt{{[}Liferay\\ \\ \\ \\ \\ Home{]}/osgi/marketplace}.\n\\end{itemize}\n\nThe language property files are in the module's\n\\texttt{src/main/resources/content} folder. Identify the language keys\nyou want to override in the \\texttt{Language{[}\\_xx{]}.properties}\nfiles.\n\nCheckpoint: Make sure you have the required information for overriding\nthe module's language keys:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Language keys\n\\item\n  Bundle symbolic name\n\\item\n  Servlet context name\n\\end{itemize}\n\nNext you'll write new values for the language keys.\n\n\\section{Write custom language key\nvalues}\\label{write-custom-language-key-values}\n\nCreate a new module to hold a resource bundle loader and your custom\nlanguage keys.\n\nIn your module's \\texttt{src/main/resources/content} folder, create\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application}{language\nproperties files} for each locale whose keys you want to override. In\neach language properties file, specify your language key overrides.\n\nNext you'll prioritize your module's language keys as a resource bundle\nfor the target module.\n\n\\section{Prioritize Your Module's Resource\nBundle}\\label{prioritize-your-modules-resource-bundle}\n\nNow that your language keys are in place, use OSGi manifest headers to\nspecify the language keys are for the target module. To compliment the\ntarget module's resource bundle, you'll aggregate your resource bundle\nwith the target module's resource bundle. You'll list your module first\nto prioritize its resource bundle over the target module resource\nbundle. Here's an example of module\n\\texttt{com.liferay.docs.l10n.myapp.lang} prioritizing its resource\nbundle over target module \\texttt{com.liferay.blogs.web}'s resource\nbundle:\n\n\\begin{verbatim}\nProvide-Capability:\\\nliferay.resource.bundle;resource.bundle.base.name=\"content.Language\",\\\nliferay.resource.bundle;resource.bundle.aggregate:String=\"(bundle.symbolic.name=com.liferay.docs.l10n.myapp.lang),(bundle.symbolic.name=com.liferay.blogs.web)\";bundle.symbolic.name=com.liferay.blogs.web;resource.bundle.base.name=\"content.Language\";service.ranking:Long=\"2\";\\\nservlet.context.name=blogs-web\n\\end{verbatim}\n\nThe example \\texttt{Provide-Capability} header has two parts:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\texttt{liferay.resource.bundle;resource.bundle.base.name=\"content.Language\"}\n  declares that the module provides a resource bundle with the base name\n  \\texttt{content.language}.\n\\item\n  The\n  \\texttt{liferay.resource.bundle;resource.bundle.aggregate:String=...}\n  directive specifies the list of bundles with resource bundles to\n  aggregate, the target bundle, the target bundle's resource bundle\n  name, and this service's ranking:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\"(bundle.symbolic.name=com.liferay.docs.l10n.myapp.lang),(bundle.symbolic.name=com.liferay.blogs.web)\"}:\n    The service aggregates resource bundles from bundles\n    \\texttt{com.liferay.docs.l10n.myapp.lang} and\n    \\texttt{com.liferay.blogs.web}. Aggregate as many bundles as\n    desired. Listed bundles are prioritized in descending order.\n  \\item\n    \\texttt{bundle.symbolic.name=com.liferay.blogs.web;resource.bundle.base.name=\"content.Language\"}:\n    Override the \\texttt{com.liferay.blogs.web} bundle's resource bundle\n    named \\texttt{content.Language}.\n  \\item\n    \\texttt{service.ranking:Long=\"2\"}: The resource bundle's service\n    ranking is \\texttt{2}. The OSGi framework applies this service if it\n    outranks all other resource bundle services that target\n    \\texttt{com.liferay.blogs.web}'s \\texttt{content.Language} resource\n    bundle.\n  \\item\n    \\texttt{servlet.context.name=blogs-web}: The target resource bundle\n    is in servlet context \\texttt{blogs-web}.\n  \\end{itemize}\n\\end{enumerate}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\nyour module} to see the language keys you've overridden.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} If your override isn't showing, use\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nShell} to check for competing resource bundle services. It may be that\nanother service outranks yours. To check for competing resource bundle\nservices whose aggregates include \\texttt{com.liferay.blogs.web}'s\nresource bundle, for example, execute this Gogo Shell command:\n\n\\begin{verbatim}\n services \"(bundle.symbolic.name=com.liferay.login.web)\"\n\\end{verbatim}\n\nSearch the results for resource bundle aggregate services whose ranking\nis higher.\n\n\\noindent\\hrulefill\n\nNow you can modify the language keys of modules in Liferay's OSGi\nruntime. Remember, language keys you want to override might actually be\nin Liferay's core. You can\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys}{override\nLiferay's language keys} too.\n\n\\section{Related Topics}\\label{related-topics-26}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-core-language-key-hooks}{Upgrading\n  Core Language Key Hooks}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-global-language-keys}{Overriding\n  Global Language Keys}\n\\end{itemize}\n\n\\chapter{Overriding Liferay Services (Service\nWrappers)}\\label{overriding-liferay-services-service-wrappers}\n\nWhy might you need to customize Liferay services? Perhaps you've added a\nnew field to Liferay's \\texttt{User} object and you want its value to be\nsaved whenever the \\texttt{addUser} or \\texttt{updateUser} methods of\nLiferay's API are called. Or maybe you want to add some additional\nlogging functionality to some Liferay APIs or other services built using\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}. Whatever your case may be, Liferay's service wrappers provide\neasy-to-use extension points for customizing Liferay's services.\n\nTo create a module that overrides one of Liferay's services, use\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI} to\ncreate a \\texttt{servicewrapper} project type with the command below\n(replace the class and package names with your own):\n\n\\begin{verbatim}\nblade create -t service-wrapper -p com.liferay.docs.serviceoverride \n-c UserLocalServiceOverride -s \ncom.liferay.portal.kernel.service.UserLocalServiceWrapper service-override\n\\end{verbatim}\n\nAs an example, here's the \\texttt{UserLocalServiceOverride} class that's\ngenerated with the Service Wrapper Template:\n\n\\begin{verbatim}\npackage com.liferay.docs.serviceoverride;\n\nimport com.liferay.portal.kernel.service.UserLocalServiceWrapper;\nimport com.liferay.portal.kernel.service.ServiceWrapper;\nimport org.osgi.service.component.annotations.Component;\n\n@Component(\n    immediate = true,\n    property = {\n    },\n    service = ServiceWrapper.class\n)\npublic class UserLocalServiceOverride extends UserLocalServiceWrapper {\n\n    public UserLocalServiceOverride() {\n        super(null);\n    }\n\n}\n\\end{verbatim}\n\nNotice that you must specify the fully qualified class name of the\nservice wrapper class that you want to extend. The \\texttt{service}\nargument was used in full in this import statement:\n\n\\begin{verbatim}\nimport com.liferay.portal.service.UserLocalServiceWrapper;\n\\end{verbatim}\n\nThis import statement, in turn, allowed the short form of the service\nwrapper class name to be used in the class declaration of your component\nclass:\n\n\\begin{verbatim}\npublic class UserLocalServiceOverride extends UserLocalServiceWrapper {...}\n\\end{verbatim}\n\nThe bottom line is that when using \\texttt{blade\\ create} to create a\nservice wrapper project, you must specify a fully qualified class name\nas the \\texttt{service} argument. (This is also true when using\n\\texttt{blade\\ create} to create a service project.) For information\nabout creating service projects, please see\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}.\n\nThe generated \\texttt{UserLocalServiceOverride} class does not actually\ncustomize any Liferay service. Before you can test that your service\nwrapper module actually works, you need to override at least one service\nmethod.\n\nOpen your \\texttt{UserLocalServiceOverride} class and add the following\nmethods:\n\n\\begin{verbatim}\n@Override\npublic int authenticateByEmailAddress(long companyId, String emailAddress,\n        String password, Map<String, String[]> headerMap,\n        Map<String, String[]> parameterMap, Map<String, Object> resultsMap)\n    throws PortalException {\n\n    System.out.println(\n        \"Authenticating user by email address \" + emailAddress);\n    return super.authenticateByEmailAddress(companyId, emailAddress, password,\n        headerMap, parameterMap, resultsMap);\n}\n\n@Override\npublic User getUser(long userId) throws PortalException {\n    System.out.println(\"Getting user by id \" + userId);\n    return super.getUser(userId);\n}\n\\end{verbatim}\n\nEach of these methods overrides a Liferay service method. These\nimplementations merely execute a few print statements that before\nexecuting the original service implementations.\n\nLastly, you must add the following method to the bottom of your service\nwrapper so it can find the appropriate service it's overriding on\ndeployment.\n\n\\begin{verbatim}\n@Reference(unbind = \"-\")\nprivate void serviceSetter(UserLocalService userLocalService) {\n    setWrappedService(userLocalService);\n}\n\\end{verbatim}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Build\nand deploy your module}. Congratulations! You've created and deployed a\nLiferay service wrapper!\n\n\\section{Related Topics}\\label{related-topics-27}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-service-wrapper-hooks}{Upgrading\n  Service Wrappers}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-blade-cli}{Installing\n  Blade CLI}\n\\item\n  \\href{/docs/7-1/tutorials/-/knowledge_base/t/creating-projects-with-blade-cli}{Creating\n  Projects with Blade CLI}\n\\end{itemize}\n\n\\chapter{Overriding lpkg Files}\\label{overriding-lpkg-files}\n\nApplications are delivered through Liferay Marketplace as \\emph{lpkg}\nfiles. This is a simple compressed file format that contains .jar files\nfor deploying to Liferay DXP. If you want to examine an application from\nMarketplace, all you have to do is unzip its .lpkg file to reveal its\n.jar files.\n\nAfter examining an application, you may want to\n\\href{/docs/7-2/customization/-/knowledge_base/c/liferay-customization}{customize}\none of its .jars. Make your customization in a copy of the .jar, but\ndon't deploy it the way you'd normally deploy an application. By\noverriding the .lpkg file, you can update application modules without\nmodifying the original .lpkg file. Here are the steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Shut down Liferay DXP.\n\\item\n  Create a folder called \\texttt{override} in the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{\\texttt{Liferay\\ Home{]}/osgi/marketplace}\n  folder}.\n\\item\n  Name your updated .jar the same as the .jar in the original .lpkg,\n  minus the version information. For example, if you're overriding the\n  \\texttt{com.liferay.amazon.rankings.web-1.0.5.jar} from the\n  \\texttt{Liferay\\ CE\\ Amazon\\ \\ \\ \\ \\ \\ Rankings.lpkg}, you'd name your\n  .jar \\texttt{com.liferay.amazon.rankings.web.jar}.\n\\item\n  Copy this .jar into the \\texttt{override} folder you created in step\n  one.\n\\end{enumerate}\n\nThis works for applications from Marketplace, but there's also the\nstatic .lpkg that contains core Liferay technology and third-party\nutilities (such as the servlet API, Apache utilities, etc.). To\ncustomize or patch any of these .jar files, follow this process:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Make your customization and package it in a .jar file.\n\\item\n  Name your .jar the same as the original .jar, minus the version\n  information. For example, a customized\n  \\texttt{com.liferay.portal.profile-1.0.4.jar} should be\n  \\texttt{com.liferay.portal.profile.jar}.\n\\item\n  Copy the .jar into the \\texttt{{[}Liferay\\ Home{]}/osgi/static}\n  folder.\n\\end{enumerate}\n\nNow start Liferay DXP. Note that any time you add and remove .jars this\nway, Liferay DXP must be shut down and then restarted for the changes to\ntake effect.\n\nIf you must roll back your customizations, delete the overriding .jar\nfiles: Liferay DXP uses the original .jar on its next startup.\n\n\\chapter{Overriding Liferay MVC\nCommands}\\label{overriding-liferay-mvc-commands}\n\nMVC Commands are used to break up the controller layer of\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{Liferay\nMVC applications} into smaller, more digestible code chunks.\n\nSometimes you'll want to override an MVC command, whether it's in a\nLiferay application or another Liferay MVC application whose source code\nyou don't own. Since MVC commands are components registered in the OSGi\nruntime, you can simply publish your own customization of the component,\ngive it a higher service ranking, and deploy it.\n\nAll existing components that reference the original MVC command service\ncomponent (using a greedy reference policy) switch to reference your new\none. Any existing\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-osgi-services}{reluctant\nreferences to the original command must be configured to reference the\nnew one}. Once they're configured with the new service component, their\nJSP's command URLs invoke the new custom MVC command.\n\nHere are the customization options available for each Liferay MVC\nCommand type:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  MVCActionCommand:\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-mvcactioncommand}{Add\n  logic}\n\\item\n  MVCRenderCommand:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-mvcrendercommand\\#adding-logic-to-an-existing-mvc-render-command}{Add\n    logic}\n  \\item\n    \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-mvcrendercommand\\#redirecting-to-a-new-jsp}{Redirect\n    to a different JSP}\n  \\end{itemize}\n\\item\n  MVCResourceCommand:\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-mvcresourcecommand}{Add\n  logic}\n\\end{itemize}\n\nThis section demonstrates each MVC command customization option. Since\nthe steps for adding logic are generally the same across MVC command\ntypes, start with\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands}{adding\nlogic}.\n\n\\chapter{Adding Logic to MVC\nCommands}\\label{adding-logic-to-mvc-commands}\n\nYou can completely override MVC commands, or any OSGi service for that\nmatter, but \\emph{adding logic} to the commands is the better option.\nDiscarding necessary logic is bad. Conversely any logic you copy from\nthe original might not work in new versions of the portlet. Adding\ncustom logic while continuing to invoke the original logic decouples the\ncustom class from the original implementation. Keeping the new logic\nseparate form the original logic keeps the code clean, maintainable, and\neasy to understand.\n\nHere are the steps for adding logic to MVC commands:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  \\hyperref[step-1-implement-the-interface]{Implement the interface}\n\\item\n  \\hyperref[step-2-publish-as-a-component]{Publish as a component}\n\\item\n  \\hyperref[step-3-refer-to-the-original-implementation]{Refer to the\n  original implementation}\n\\item\n  \\hyperref[step-4-add-the-logic]{Add the logic, and call the original}\n\\end{enumerate}\n\n\\section{Step 1: Implement the\ninterface}\\label{step-1-implement-the-interface}\n\nImplement the respective MVC Command interface either directly or by\nextending an existing base class that implements it. Extending a base\nclass for the interface relieves you from implementing logic that should\ntypically be a part of most command implementations. For example, to add\nlogic to the Blogs portlet's \\texttt{EditEntryMVCActionCommand}, you\nwould extend base class \\texttt{BaseMVCActionCommand}.\n\n\\begin{verbatim}\npublic class CustomBlogsMVCActionCommand extends BaseMVCActionCommand {...}\n\\end{verbatim}\n\nCheck the MVC command interfaces for existing base classes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCActionCommand.html}{\\texttt{MVCActionCommand}}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html}{\\texttt{MVCRenderCommand}}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html}{\\texttt{MVCResourceCommand}}\n\\end{itemize}\n\nNext make your class a service component.\n\n\\section{Step 2: Publish as a\ncomponent}\\label{step-2-publish-as-a-component}\n\nThe Declarative Services \\texttt{@Component} annotation facilitates\ncustomizing MVC commands. All the customization options require\npublishing your MVC command class as a component. For example, this\n\\texttt{@Component} annotation declares an \\texttt{MVCActionCommand}\nservice.\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = { \n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_ADMIN, \n        \"mvc.command.name=/blogs/edit_entry\",\n        \"service.ranking:Integer=100\" \n    }, \n    service = MVCActionCommand.class\n)\npublic class CustomBlogsMVCActionCommand extends BaseMVCActionCommand {\n    ...\n} \n\\end{verbatim}\n\nIt publishes \\texttt{CustomBlogsMVCActionCommand} as a service component\nfor the \\texttt{MVCActionCommand} class. Upon resolving, it's activated\nimmediately because \\texttt{immediate\\ =\\ true}. The component is\ninvoked in the Blogs Admin portlet by the command URL\n\\texttt{/blogs/edit\\_entry}. Its service ranking of \\texttt{100}\nprioritizes it ahead of the original service component, whose ranking is\n\\texttt{0}.\n\nHere's what you need to specify in an \\texttt{@Component} annotation for\nyour custom MVC command:\n\n\\begin{itemize}\n\\item\n  \\texttt{javax.portlet.name}: for each portlet you want the\n  customization to affect. JSPs in these portlets can invoke the MVC\n  command via applicable command URL tags. You can specify the same\n  portlets as the original MVC command or a subset of those portlets.\n\\item\n  \\texttt{mvc.command.name}: this property declares the command URL that\n  maps to this custom MVC command component.\n\\item\n  \\texttt{service.ranking:Integer}: set this property to a higher\n  integer than the original service implementation's ranking. The\n  ranking tells the OSGi runtime which service to use, in cases where\n  multiple components register the same service, with the same\n  properties. The higher the integer you specify here, the more weight\n  your component carries. Liferay's service implementations typically\n  have a \\texttt{0} ranking.\n\\item\n  \\texttt{service}: this attribute specifies the service (interface) to\n  override.\n\\item\n  \\texttt{immediate}: set this attribute to \\texttt{true} to activate\n  your component immediately upon resolution.\n\\end{itemize}\n\nYou can refer back to this list as you add \\texttt{@Component}\nannotations to your custom MVC commands.\n\nNext reference the original implementation.\n\n\\section{Step 3: Refer to the original\nimplementation}\\label{step-3-refer-to-the-original-implementation}\n\nUse a field annotated with \\texttt{@Reference} to fetch a reference to\nthe original MVC command component. If there are no additional\ncustomizations on the original component, this reference will be for the\noriginal MVC command type. For example, this field references the\noriginal MVC command component \\texttt{EditEntryMVCActionCommand}.\n\n\\begin{verbatim}\n@Reference(\n    target = \"(component.name=com.liferay.blogs.web.internal.portlet.action.EditEntryMVCActionCommand)\")\nprotected MVCActionCommand mvcActionCommand;\n\\end{verbatim}\n\nHere's how to add the reference:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Declare the field as the MVC command interface type that it is. For\n  example, the \\texttt{mvcActionCommand} field is type\n  \\texttt{MVCActionCommand}.\n\\item\n  Add the \\texttt{@Reference} annotation.\n\\item\n  In the annotation, define a \\texttt{target} attribute that filters on\n  a \\texttt{component.name} equal to the default service implementation\n  class's fully qualified name.\n\\end{enumerate}\n\nWhen your custom component resolves, the OSGi runtime assigns the\ntargeted service to your field. It's time to add your custom logic.\n\n\\section{Step 4: Add the logic}\\label{step-4-add-the-logic}\n\nAdding the logic involves overriding the primary method of the base\nclass you're extending or the interface you're implementing. In your\nmethod override, add your new logic AND then invoke the original\nimplementation. For example, the following method overrides\n\\texttt{BaseMVCActionCommand}'s method \\texttt{doProcessAction}.\n\n\\begin{verbatim}\n@Override\nprotected void doProcessAction(\n    ActionRequest actionRequest, ActionResponse actionResponse)\nthrows Exception {\n    // Add custom logic here \n    ...\n    \n    // Call the original service implementation \n    mvcActionCommand.processAction(actionRequest, actionResponse);\n}\n\\end{verbatim}\n\nThe method above defines custom logic and then invokes the original\nservice it referenced in the previous step.\n\nIf you use this approach, your extension will continue to work with new\nversions of the original portlet, because no coupling exists between the\noriginal portlet logic and your customization. The command\nimplementation class can change. Make sure to keep your reference\nupdated to the name of the current implementation class.\n\nCongratulations on adding logic to your existing MVC command.\n\n\\chapter{Overriding\nMVCRenderCommands}\\label{overriding-mvcrendercommands}\n\nYou can override\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html}{\\texttt{MVCRenderCommand}}\nfor any portlet that uses Liferay's MVC framework and publishes an\n\\texttt{MVCRenderCommand} component.\n\nFor example, Liferay's Blogs application has a class called\n\\texttt{EditEntryMVCRenderCommand}, with this component:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS,\n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_ADMIN,\n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_AGGREGATOR,\n        \"mvc.command.name=/blogs/edit_entry\"\n    },\n    service = MVCRenderCommand.class\n)\n\\end{verbatim}\n\nThis MVC render command can be invoked from any of the portlets\nspecified by the \\texttt{javax.portlet.name} parameter, by calling a\nrender URL that names the MVC command:\n\n\\begin{verbatim}\n<portlet:renderURL var=\"addEntryURL\">\n    <portlet:param name=\"mvcRenderCommandName\" value=\"/blogs/edit_entry\" />\n    <portlet:param name=\"redirect\" value=\"<%= viewEntriesURL %>\" />\n</portlet:renderURL>\n\\end{verbatim}\n\nWhat if you want to override the command, but not for all of the\nportlets listed in the original component? In your override component,\njust list the \\texttt{javax.portlet.name} of the portlets where you want\nthe override to take effect. For example, if you want to override the\n\\texttt{/blogs/edit\\_entry} MVC render command just for the Blogs Admin\nportlet (the Blogs Application accessed in the site administration\nsection of Liferay), your component could look like this:\n\n\\begin{verbatim}\n@Component(\n  immediate = true,\n  property = {\n     \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_ADMIN,\n     \"mvc.command.name=/blogs/edit_entry\",\n     \"service.ranking:Integer=100\"\n  },\n  service = MVCRenderCommand.class\n)\n\\end{verbatim}\n\nNote the last property listed, \\texttt{service.ranking}. It's used to\ntell the OSGi runtime which service to use, in cases where there are\nmultiple components registering the same service, with the same\nproperties. The higher the integer you specify here, the more weight\nyour component carries. In this case, the override component is used\ninstead of the original one, since the default value for this property\nis \\texttt{0}.\n\nAfter that, it's up to you to do whatever you'd like. MVC render\ncommands can be customized for these purposes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[adding-logic-to-an-existing-mvc-render-command]{Adding Logic\n  to an Existing MVC Render Command}\n\\item\n  \\hyperref[redirecting-to-a-new-jsp]{Redirecting to a new JSP}\n\\end{itemize}\n\nStart by exploring how to add logic to an existing MVC render command.\n\n\\section{Adding Logic to an Existing MVC Render\nCommand}\\label{adding-logic-to-an-existing-mvc-render-command}\n\nYou can add logic to an MVC render command following the\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands}{general\nsteps for MVC commands}. Specifically for MVC render commands, you must\ndirectly implement the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html}{\\texttt{MVCRenderCommand}\ninterface} and override its \\texttt{render} method.\n\nFor example, this custom MVC render command has a placeholder (i.e., at\ncomment \\texttt{//Do\\ something\\ here}) for adding logic to the\n\\texttt{render} method:\n\n\\begin{verbatim}\npublic CustomEditEntryRenderCommand implements MVCRenderCommand {\n    @Override\n    public String render(RenderRequest renderRequest, \n                        RenderResponse renderResponse)\n           throws PortletException {\n\n        //Do something here\n\n        return mvcRenderCommand.render(renderRequest, renderResponse);\n    }\n\n    @Reference(target = \n          \"(component.name=com.liferay.blogs.web.internal.portlet.action.EditEntryMVCRenderCommand)\")\n      protected MVCRenderCommand mvcRenderCommand;\n}\n\\end{verbatim}\n\nThe example references an \\texttt{EditEntryMVCRenderCommand}\nimplementation of \\texttt{MVCRenderCommand}. In the \\texttt{render}\nmethod, you'd replace the placeholder with new logic and then invoke the\noriginal implementation's logic by calling its \\texttt{render} method.\n\nSometimes, you might need to redirect the request to an entirely new\nJSP. You can do that from a custom MVC render command module too.\n\n\\section{Redirecting to a New JSP}\\label{redirecting-to-a-new-jsp}\n\n\\texttt{MVCRenderCommand}'s \\texttt{render} method returns a JSP path as\na String. By default, the JSP must live in the original module, so you\ncannot simply specify a path to a custom JSP in your override module. To\nredirect it to a JSP in your new module, you must make the method skip\ndispatching to the original JSP altogether, by using the constant\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderConstants.html}{\\texttt{MVCRenderConstants.MVC\\_PATH\\_VALUE\\_SKIP\\_DISPATCH}\nclass}. Then you need to initiate your own dispatching process,\ndirecting the request to your JSP path. Here's how that might look in\npractice:\n\n\\begin{verbatim}\npublic class CustomEditEntryMVCRenderCommand implements MVCRenderCommand {\n\n    @Override\n    public String render(\n        RenderRequest renderRequest, RenderResponse renderResponse) throws\n            PortletException {\n\n        System.out.println(\"Rendering custom_edit_entry.jsp\");\n\n        RequestDispatcher requestDispatcher =\n            servletContext.getRequestDispatcher(\"/custom_edit_entry.jsp\");\n\n        try {\n            HttpServletRequest httpServletRequest = \n                PortalUtil.getHttpServletRequest(renderRequest);\n            HttpServletResponse httpServletResponse = \n                PortalUtil.getHttpServletResponse(renderResponse);\n\n            requestDispatcher.include\n                (httpServletRequest, httpServletResponse);\n        } catch (Exception e) {\n            throw new PortletException\n                (\"Unable to include custom_edit_entry.jsp\", e);\n        }\n\n        return MVCRenderConstants.MVC_PATH_VALUE_SKIP_DISPATCH;\n    }\n\n    @Reference(target = \"(osgi.web.symbolicname=com.custom.code.web)\")\n    protected ServletContext servletContext;\n}\n\\end{verbatim}\n\nThe servlet context provides access to the request dispatcher. A servlet\ncontext is automatically created for portlets. It can be created for\nother modules by including the following line in your \\texttt{bnd.bnd}\nfile:\n\n\\begin{verbatim}\nWeb-ContextPath: /custom-code-web\n\\end{verbatim}\n\nFollow these steps to fetch the portlet's servlet context in your custom\nMVC render command:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a \\texttt{ServletContext} field.\n\n\\begin{verbatim}\nprotected ServletContext servletContext;\n\\end{verbatim}\n\\item\n  Add the \\texttt{@Reference} annotation to the field and set the\n  annotation to filter on the portlet's module. By convention, Liferay\n  puts portlets in modules whose symbolic names end in \\texttt{.web}.\n  For example, this servlet context reference filters on a module whose\n  symbolic name is \\texttt{com.custom.code.web}.\n\n\\begin{verbatim}\n@Reference(target = \"(osgi.web.symbolicname=com.custom.code.web)\")\nprotected ServletContext servletContext;\n\\end{verbatim}\n\\end{enumerate}\n\nImplement your \\texttt{render} method this way:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a request dispatcher to your module's custom JSP:\n\n\\begin{verbatim}\nRequestDispatcher requestDispatcher =\n    servletContext.getRequestDispatcher(\"/custom_edit_entry.jsp\");\n\\end{verbatim}\n\\item\n  Include the HTTP servlet request and response in the request\n  dispatcher.\n\n\\begin{verbatim}\ntry {\n    HttpServletRequest httpServletRequest = \n        PortalUtil.getHttpServletRequest(renderRequest);\n    HttpServletResponse httpServletResponse = \n        PortalUtil.getHttpServletResponse(renderResponse);\n\n    requestDispatcher.include\n        (httpServletRequest, httpServletResponse);\n} catch (Exception e) {\n    throw new PortletException\n        (\"Unable to include custom_edit_entry.jsp\", e);\n}\n\\end{verbatim}\n\\item\n  Return the request dispatcher via the constant\n  \\texttt{MVC\\_PATH\\_VALUE\\_SKIP\\_DISPATCH}.\n\n\\begin{verbatim}\nreturn MVCRenderConstants.MVC_PATH_VALUE_SKIP_DISPATCH;\n\\end{verbatim}\n\\end{enumerate}\n\nAfter deploying your module, the\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands\\#step-2-publish-as-a-component}{portlets\ntargeted by your custom \\texttt{MVCRenderCommand} component} render your\nnew JSP.\n\n\\section{Related Topics}\\label{related-topics-28}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands}{Adding\n  Logic to MVC Commands}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-struts-action-hooks}{Converting\n  StrutsActionWrappers to MVCCommands}\n\\end{itemize}\n\n\\chapter{Overriding\nMVCActionCommands}\\label{overriding-mvcactioncommands}\n\nIn case you want add to a Liferay MVC action command, you can. The OSGi\nframework lets you override MVC action commands if you follow the\ninstructions for\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands}{adding\nlogic to MVC commands}. It involves\n\\href{/docs/7-1/tutorials/-/knowledge_base/t/adding-logic-to-mvc-commands\\#publish-as-a-component}{registering\nyour custom MVC action command as an OSGi component} with the same\nproperties as the original, but with a higher service ranking.\n\nCustom MVC action commands typically extend the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/BaseMVCActionCommand.html}{\\texttt{BaseMVCActionCommand}\nclass}, and override its \\texttt{doProcessAction} method, which returns\n\\texttt{void}. Add your logic to the original behavior of the action\nmethod by getting a reference to the original service, and calling it\nafter your own logic.\n\nFor example, this \\texttt{MVCActionCommand} override checks whether the\n\\texttt{delete} action is invoked on a blog entry, and prints a message\nto the log, before continuing with the original processing:\n\n\\begin{verbatim}\n@Component(\n    property = { \n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_ADMIN, \n        \"mvc.command.name=/blogs/edit_entry\",\n        \"service.ranking:Integer=100\" \n    }, \n    service = MVCActionCommand.class\n)\npublic class CustomBlogsMVCActionCommand extends BaseMVCActionCommand {\n\n    @Override\n    protected void doProcessAction\n        (ActionRequest actionRequest, ActionResponse actionResponse) \n        throws Exception {\n\n        String cmd = ParamUtil.getString(actionRequest, Constants.CMD);\n\n        if (cmd.equals(Constants.DELETE)) {\n            System.out.println(\"Deleting a Blog Entry\");\n        }\n\n        mvcActionCommand.processAction(actionRequest, actionResponse);\n    }\n\n    @Reference(\n        target = \"(component.name=com.liferay.blogs.web.internal.portlet.action.EditEntryMVCActionCommand)\")\n    protected MVCActionCommand mvcActionCommand;\n\n}\n\\end{verbatim}\n\nAdding MVC action command logic before existing logic is straightforward\nand maintains loose coupling between new and old code.\n\n\\section{Related Topics}\\label{related-topics-29}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands}{Adding\n  Logic to MVC Commands}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-mvcrendercommand}{Overriding\n  MVCRenderCommands}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-struts-action-hooks}{Converting\n  StrutsActionWrappers to MVCCommands}\n\\end{itemize}\n\n\\chapter{Overriding\nMVCResourceCommands}\\label{overriding-mvcresourcecommands}\n\nIf you need to add functionality to a Liferay MVC resource command, you\ncan. The Liferay MVC command framework supports customizing MVC resource\ncommands. It follows the process for\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands}{adding\nlogic to MVC commands} and it is similar to the ones described for\n\\texttt{MVCRenderCommand} and \\texttt{MVCActionCommand}. There's a\ncouple things to keep in mind:\n\n\\begin{itemize}\n\\item\n  The service to specify in your component is\n  \\texttt{MVCResourceCommand.class}\n\\item\n  As with overriding \\texttt{MVCRenderCommand}, there's no base\n  implementation class to extend. Implement the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html}{\\texttt{MVCResourceCommand}\n  interface} yourself.\n\\item\n  Keep your code decoupled from the original code by adding your logic\n  to the original \\texttt{MVCResourceCommand}'s logic by getting a\n  reference to the original and returning a call to its\n  \\texttt{serveResource} method:\n\\end{itemize}\n\n\\begin{verbatim}\nreturn mvcResourceCommand.serveResource(resourceRequest, resourceResponse);\n\\end{verbatim}\n\nThe following example overrides the behavior of\n\\texttt{com.liferay.login.web.portlet.action.CaptchaMVCResourceCommand},\nfrom the Liferay's Login portlet's \\texttt{login-web} module. It simply\nprints a line in the console and then executes the original logic:\nreturning the Captcha image for the account creation screen.\n\n\\begin{verbatim}\n@Component(\n    property = { \n        \"javax.portlet.name=\" + LoginPortletKeys.LOGIN,\n        \"mvc.command.name=/login/captcha\"\n    }, \n    service = MVCResourceCommand.class\n)\npublic class CustomCaptchaMVCResourceCommand implements MVCResourceCommand {\n\n    @Override\n    public boolean serveResource\n        (ResourceRequest resourceRequest, ResourceResponse resourceResponse) {\n\n        System.out.println(\"Serving login captcha image\");\n\n        return mvcResourceCommand.serveResource(resourceRequest, resourceResponse);\n    }\n\n    @Reference(target = \n        \"(component.name=com.liferay.login.web.internal.portlet.action.CaptchaMVCResourceCommand)\")\n    protected MVCResourceCommand mvcResourceCommand;\n}\n\\end{verbatim}\n\nAnd that, as they say, is that. Even if you don't own the source code of\nan application, you can\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-liferay-mvc-commands}{override\nits MVC commands} just by knowing the component class name.\n\n\\section{Related Topics}\\label{related-topics-30}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands}{Adding\n  Logic to MVC Commands}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-mvcrendercommand}{Overriding\n  MVCRenderCommands}\n\\end{itemize}\n\n\\chapter{Overriding OSGi Services}\\label{overriding-osgi-services}\n\nComponents register as services with the OSGi service registry. A\nservice component's availability, ranking, and attributes determine\nwhether components referring to the service type bind to that particular\nservice. Liferay DXP's OSGI container is a dynamic environment in which\nservices come and go and can be overridden, which means that if there's\na service whose behavior you want to change, you can override it. Here\nare the steps for overriding a service:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/examining-an-osgi-service-to-override}{Get\n  the service and service reference details}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/creating-a-custom-osgi-service}{Create\n  a custom service}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/reconfiguring-components-to-use-your-service}{Configure\n  components to use your custom service}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} services in\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-impl/}{portal-impl}\nare Spring beans that Liferay makes available as OSGi services.\n\n\\noindent\\hrulefill\n\nStart with examining the service you want to override.\n\n\\chapter{Examining an OSGi Service to\nOverride}\\label{examining-an-osgi-service-to-override}\n\nCreating and injecting a custom service in place of an existing service\nrequires three things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Understanding the service interface\n\\item\n  The existing service\n\\item\n  The references to the service\n\\end{itemize}\n\nYour custom service must implement the service interface, match\nreferences you want, and might need to invoke the existing service.\n\nGetting components to adopt your custom service immediately can require\nreconfiguring their references to the service. Here you'll flesh out\nservice details to make these decisions.\n\n\\section{Gathering Information on a\nService}\\label{gathering-information-on-a-service}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Since component service references are extension points, start with\n  determining the service you want to override and components that use\n  that service.\n\\item\n  Once you know the service and components that use it, use Gogo Shell's\n  Service Component Runtime (SCR) to inspect the components and get the\n  service and reference details. The\n  \\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\n  Shell} command \\texttt{scr:info\\ {[}componentName{]}} lists the\n  component's attributes and service references.\n\\end{enumerate}\n\nHere's an example \\texttt{scr:info} command and results (abbreviated\nwith \\texttt{...}) that describe component\n\\texttt{override.my.service.reference.OverrideMyServiceReference} (from\nsample module\n\\href{https://portal.liferay.dev/documents/113763090/114000186/override-my-service-reference.zip}{override-my-service-reference})\nand its reference to a service of type\n\\texttt{override.my.service.reference.service.api.SomeService}:\n\n\\begin{verbatim}\n> scr:info override.my.service.reference.OverrideMyServiceReference \n\n...\nComponent Description:\n    Name: override.my.service.reference.portlet.OverrideMyServiceReferencePortlet\n...\nReference: _someService\n    Interface Name: override.my.service.reference.service.api.SomeService\n    Cardinality: 1..1\n    Policy: static\n    Policy option: reluctant\n    Reference Scope: bundle\n...\nComponent Configuration:\nComponentId: 2399\nState: active\nSatisfiedReference: _someService\n  Target: null\n  Bound to:        6840\n      Properties:\n        component.id = 2400\n        component.name = override.my.service.reference.service.impl.SomeServiceImpl\n        objectClass = [override.my.service.reference.service.api.SomeService]\n        service.bundleid = 524\n        service.id = 6840\n        service.scope = bundle\n...\n\\end{verbatim}\n\nThe \\texttt{scr:info} results, like the ones above, contain information\nrelevant to injecting a custom service. Here's what you'll do with the\ninformation:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\hyperref[step-1-copy-the-service-interface-name]{Copy the service\n  interface name}\n\\item\n  \\hyperref[step-2-copy-the-existing-service-name]{Copy the existing\n  service name}\n\\item\n  \\hyperref[step-3-gather-reference-configuration-details-if-reconfiguration-is-needed]{Gather\n  reference configuration details (if reconfiguration is necessary)}\n\\end{enumerate}\n\nStart with the service interface.\n\n\\section{Step 1: Copy the Service Interface\nName}\\label{step-1-copy-the-service-interface-name}\n\nThe reference's \\emph{Interface Name} is the service interface's fully\nqualified name.\n\n\\begin{verbatim}\n...\nReference: _someService\n    Interface Name: override.my.service.reference.service.api.SomeService\n    ...\n\\end{verbatim}\n\n\\textbf{Copy and save the interface name}, because it's the type your\ncustom service must implement.\n\n\\noindent\\hrulefill\n\nJavadocs for Liferay DXP service interfaces are at these locations:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/}{Liferay\n  DXP core Javadocs}\n\\item\n  \\href{https://docs.liferay.com/dxp/apps}{Liferay DXP app Javadocs}\n\\item\n  \\href{https://mvnrepository.com/}{MVNRepository} and\n  \\href{https://search.maven.org/}{Maven Central} (for Liferay and\n  non-Liferay artifact Javadocs).\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\section{Step 2: Copy the Existing Service\nName}\\label{step-2-copy-the-existing-service-name}\n\nIf you want to invoke the existing service along with your custom\nservice, get the existing service name.\n\nThe \\texttt{src:info} result's Component Configuration section lists the\nexisting service's fully qualified name. For example, the\n\\texttt{OverrideMyServiceReferencePortlet} component's references\n\\texttt{\\_someService} is bound to a service component whose fully\nqualified name is\n\\texttt{override.my.service.reference.service.impl.SomeServiceImpl}.\n\n\\begin{verbatim}\nComponent Configuration:\n...\nSatisfiedReference: _someService\n  ...\n  Bound to:        6840\n      Properties:\n        ...\n        component.name = override.my.service.reference.service.impl.SomeServiceImpl\n\\end{verbatim}\n\n\\textbf{Copy the \\texttt{component.name}} so you can reference the\nservice in your\n\\href{/docs/7-2/customization/-/knowledge_base/c/creating-a-custom-osgi-service}{custom\nservice}.\n\nHere's an example of referencing the service above.\n\n\\begin{verbatim}\n@Reference  (\n    target = \"(component.name=override.my.service.reference.service.impl.SomeServiceImpl)\"\n)\nprivate SomeService _defaultService;\n\\end{verbatim}\n\n\\section{Step 3: Gather Reference Configuration Details (if\nreconfiguration is\nneeded)}\\label{step-3-gather-reference-configuration-details-if-reconfiguration-is-needed}\n\nThe service reference's policy and policy option determine a component's\nconditions for adopting a particular service.\n\n\\begin{itemize}\n\\item\n  If the reference's policy option is \\texttt{greedy}, it binds to the\n  matching, highest ranking service right away. The reference need not\n  be reconfigured to adopt your service.\n\\item\n  If policy is \\texttt{static} and its policy option is\n  \\texttt{reluctant}, however, the component requires one of the\n  following conditions to switch from using the existing service it's\n  referencing to using the matching, highest ranking service (i.e.,\n  you'll rank your custom service highest):\n\n  \\begin{enumerate}\n  \\def\\labelenumi{\\arabic{enumi}.}\n  \\tightlist\n  \\item\n    The component is reactivated\n  \\item\n    The component's existing referenced service is unavailable\n  \\item\n    The component's reference is modified so that it does not match the\n    existing service but matches your service\n  \\end{enumerate}\n\\end{itemize}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/reconfiguring-components-to-use-your-service}{Reconfiguring\nthe reference} can be the quickest way for the component to adopt a new\nservice.\n\n\\textbf{Gather these details:}\n\n\\begin{itemize}\n\\item\n  \\emph{Component name:} Find this at \\emph{Component Description} →\n  \\emph{Name}. For example,\n\n\\begin{verbatim}\n  Component Description:\n      Name: override.my.service.reference.portlet.OverrideMyServiceReferencePortlet\n      ...\n\\end{verbatim}\n\\item\n  \\emph{Reference name:} The \\emph{Reference} value (e.g.,\n  \\texttt{Reference:\\ \\_someService}).\n\\item\n  \\emph{Cardinality:} Number of service instances the reference can bind\n  to.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: Declarative Services makes all components configurable\nthrough OSGi Configuration Admin. Each \\texttt{@Reference} annotation in\nthe source code has a name property, either \\emph{explicitly} set in the\nannotation or \\emph{implicitly} derived from the name of the member on\nwhich the annotation is used.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  If no reference name property is used and the \\texttt{@Reference} is\n  on a field, then the reference name is the field name. If\n  \\texttt{@Reference} is on a field called \\texttt{\\_someService}, for\n  example, then the reference name is \\texttt{\\_someService}.\n\\item\n  If the \\texttt{@Reference} is on a method, then heuristics derive the\n  reference name. Method name suffix is used and prefixes such as\n  \\texttt{set}, \\texttt{add}, and \\texttt{put} are ignored. If\n  \\texttt{@Reference} is on a method called\n  \\texttt{setSearchEngine(SearchEngine\\ se)}, for example, then the\n  reference name is \\texttt{SearchEngine}.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\nAfter\n\\href{/docs/7-2/customization/-/knowledge_base/c/creating-a-custom-osgi-service}{creating\nyour custom service} (next), you'll use the details you collected here\nto\n\\href{/docs/7-2/customization/-/knowledge_base/c/reconfiguring-components-to-use-your-service}{configure\nthe component to use your custom service}.\n\nCongratulations on getting the details required for overriding the OSGi\nservice!\n\n\\section{Related Topics}\\label{related-topics-31}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{OSGi\n  Services and Dependency Injection with Declarative Services}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\n  Shell}\n\\end{itemize}\n\n\\chapter{Creating a Custom OSGi\nService}\\label{creating-a-custom-osgi-service}\n\nIt's time to implement your OSGi service. Make sure to\n\\href{/docs/7-2/customization/-/knowledge_base/c/examining-an-osgi-service-to-override}{examine\nthe service and service reference details}, if you haven't done so\nalready. Here you'll create a custom service that implements the service\ninterface, declares it an OSGi service of that type, and makes it the\nbest match for binding with other components.\n\nThe example custom service \\texttt{CustomServiceImpl} implements service\ninterface (from sample module\n\\href{https://portal.liferay.dev/documents/113763090/114000186/overriding-service-reference.zip}{\\texttt{overriding-service-reference}})\n\\texttt{SomeService}, declares itself an OSGi service of the\n\\texttt{SomeService} service type, and even delegates work to the\nexisting service. Examine the example code below as you follow the steps\nfor creating your custom service:\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"service.ranking:Integer=100\"\n    },\n    service = SomeService.class\n)\npublic class CustomServiceImpl implements SomeService {\n\n    @Override\n    public String doSomething() {\n\n        StringBuilder sb = new StringBuilder();\n        sb.append(this.getClass().getName());\n        sb.append(\", which delegates to \");\n        sb.append(_defaultService.doSomething());\n\n        return sb.toString();\n    }\n\n    @Reference  (\n        target = \"(component.name=override.my.service.reference.service.impl.SomeServiceImpl)\"\n    )\n    private SomeService _defaultService;\n}\n\\end{verbatim}\n\nHere are the steps to create a custom OSGi service:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  a module}.\n\\item\n  Create your custom service class so that it \\texttt{implements} the\n  \\href{/docs/7-2/customization/-/knowledge_base/c/examining-an-osgi-service-to-override\\#step-1-copy-the-service-interface-name}{service\n  interface} you want. In the example above,\n  \\texttt{CustomServiceImpl\\ implements\\ SomeService}. Step 5 (later)\n  demonstrates implementing the interface methods.\n\\item\n  Make your class a Declarative Services component that is the best\n  match for references to the service interface:\n\n  \\begin{itemize}\n  \\item\n    Use an \\texttt{@Component} annotation and \\texttt{service} attribute\n    to make your classes a Declarative Services (DS) component. This\n    declares your class to be an OSGi service that can be made available\n    in the OSGi service registry. The example class above is a DS\n    service component of service type \\texttt{SomeService.class}.\n  \\item\n    Use a \\texttt{service.ranking:Integer} component property to rank\n    your service higher than existing services. The\n    \\texttt{\"service.ranking:Integer=100\"} property above sets the\n    example's ranking to \\texttt{100}.\n  \\end{itemize}\n\\item\n  If you want to invoke the existing service implementation, declare a\n  field that uses a Declarative Services reference to the existing\n  service. Use the\n  \\href{/docs/7-2/customization/-/knowledge_base/c/examining-an-osgi-service-to-override\\#step-2-copy-the-existing-service-name}{\\texttt{component.name}\n  you copied when you examined the service} to target the existing\n  service. The example above refers to an existing service like this:\n\n\\begin{verbatim}\n@Reference  (\n    target = \"(component.name=override.my.service.reference.service.impl.SomeServiceImpl)\"\n)\nprivate SomeService _defaultService;\n\\end{verbatim}\n\n  The field lets you invoke the existing service in your custom service.\n\\item\n  Override the interface's methods. Optionally, delegate work to the\n  existing service implementation (see previous step).\n\n  The example custom service's \\texttt{doSomething} method delegates\n  work to the original service implementation.\n\\item\n  Register your custom service with the OSGi runtime framework by\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{deploying\n  your module}.\n\\end{enumerate}\n\nComponents that reference the service type you implemented and whose\nreference policy option is \\texttt{greedy} bind to your custom service\nimmediately. Components bound to an existing service and whose reference\npolicy option is \\texttt{reluctant} can be dynamically reconfigured to\nuse your service. That's demonstrated next.\n\n\\section{Related Topics}\\label{related-topics-32}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{OSGi\nServices and Dependency Injection with Declarative Services}\n\n\\chapter{Reconfiguring Components to Use Your OSGi\nService}\\label{reconfiguring-components-to-use-your-osgi-service}\n\nIn many cases, assigning your\n\\href{/docs/7-2/customization/-/knowledge_base/c/creating-a-custom-osgi-service}{custom\nservice (service)} a higher ranking convinces components to unbind from\ntheir current service and bind to yours. In other cases, components keep\nusing their current service. Why is that? And how do you make components\nadopt your service? The component's\n\\href{/docs/7-2/customization/-/knowledge_base/c/examining-an-osgi-service-to-override\\#step-3-gather-reference-configuration-details-if-reconfiguration-is-needed}{service\nreference policy option} is the key to determining the service.\n\nHere are the policy options:\n\n\\texttt{greedy}: The component uses the matching, highest ranking\nservice as soon as it's available.\n\n\\texttt{reluctant}: The component uses the matching, highest ranking\nservice available in the following events:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  the component is (re)activated\n\\item\n  the component's existing referenced service becomes unavailable\n\\item\n  the component's reference is modified so that it no longer matches the\n  existing bound service\n\\end{itemize}\n\nIn short, references with greedy policy options adopt your higher\nranking service right away, while ones with reluctant policy options\nrequire particular events. What's great is that Liferay DXP's\nConfiguration Admin lets you use configuration files (config files) or\nthe API to swap in service reference changes on the fly. Here you'll use\na config file to reconfigure a service reference to use your custom\nservice immediately.\n\nThis article uses example modules \\texttt{override-my-service-reference}\nand \\texttt{overriding-service-reference} to demonstrate reconfiguring a\nservice reference, binding the component to a different service. you can\napply the steps below to configure your own customization.\n\n\\begin{itemize}\n\\item\n  \\texttt{override-my-service-reference}\n  (\\href{https://portal.liferay.dev/documents/113763090/114000186/override-my-service-reference.zip}{download}):\n  This module's portlet component\n  \\texttt{OverrideMyServiceReferencePortlet}'s field\n  \\texttt{\\_someService} references a service of type\n  \\texttt{SomeService}. The reference's policy is static and reluctant.\n  By default, it binds to an implementation called\n  \\texttt{SomeServiceImpl}.\n\\item\n  \\texttt{overriding-service-reference}\n  (\\href{https://portal.liferay.dev/documents/113763090/114000186/overriding-service-reference.zip}{download}):\n  Provides a custom \\texttt{SomeService} implementation called\n  \\texttt{CustomServiceImpl}. The module's configuration file overrides\n  \\texttt{OverrideMyServiceReferencePortlet}'s \\texttt{SomeService}\n  reference so that it binds to \\texttt{CustomServiceImpl}.\n\\end{itemize}\n\nYou're ready to reconfigure a component's service reference to target\nyour custom service.\n\n\\section{Reconfiguring the Service\nReference}\\label{reconfiguring-the-service-reference}\n\nLiferay DXP's Configuration Admin lets you use configuration files to\nswap in service references on the fly.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files}{Create\n  a system configuration file} named after the referencing component.\n  Follow the name convention \\texttt{{[}component{]}.config}, replacing\n  \\texttt{{[}component{]}} with the\n  \\href{/docs/7-2/customization/-/knowledge_base/c/examining-an-osgi-service-to-override\\#step-3-gather-reference-configuration-details-if-reconfiguration-is-needed}{component\n  name}. The configuration file name for the example component\n  \\texttt{override.my.service.reference.portlet.OverrideMyServiceReferencePortlet}\n  is:\n\n\\begin{verbatim}\noverride.my.service.reference.portlet.OverrideMyServiceReferencePortlet.config\n\\end{verbatim}\n\\item\n  In the configuration file, add a reference target entry that filters\n  on your custom service. Follow this format for the entry:\n\n\\begin{verbatim}\n[reference].target=[filter]\n\\end{verbatim}\n\n  Replace \\texttt{{[}reference{]}} with the name of the reference you're\n  overriding. Replace \\texttt{{[}filter{]}} with service properties that\n  filter on your custom service.\n\n  This example filters on the \\texttt{component.name} service property:\n\n\\begin{verbatim}\n_someService.target=\"(component.name\\=overriding.service.reference.service.CustomServiceImpl)\" \n\\end{verbatim}\n\n  This example filters on the \\texttt{service.vendor} service property:\n\n\\begin{verbatim}\n_someService.target=\"(service.vendor\\=Acme, Inc.)\"\n\\end{verbatim}\n\\item\n  Optionally, you can add a \\texttt{cardinality.minimum} entry to\n  specify the number of services the reference can use. Here's the\n  format:\n\n\\begin{verbatim}\n[reference].cardinality.minimum=[int]\n\\end{verbatim}\n\n  Here's an example cardinality minimum:\n\n\\begin{verbatim}\n_someService.cardinality.minimum=1\n\\end{verbatim}\n\\item\n  Deploy the configuration by copying the configuration file into the\n  folder \\texttt{{[}Liferay\\_Home{]}/osgi/configs}.\n\\end{enumerate}\n\nExecuting \\texttt{scr:info} on your component shows that the custom\nservice is now bound to the reference.\n\nFor example, executing\n\\texttt{scr:info\\ override.my.service.reference.portlet.OverrideMyServiceReferencePortlet}\nreports the following information:\n\n\\begin{verbatim}\n...\nComponent Description:\n  Name: override.my.service.reference.portlet.OverrideMyServiceReferencePortlet\n  ...\n  Reference: _someService\n    Interface Name: override.my.service.reference.service.api.SomeService\n    Cardinality: 1..1\n    Policy: static\n    Policy option: reluctant\n    Reference Scope: bundle\n    ...\n  Component Configuration:\n    ComponentId: 2399\n    State: active\n    SatisfiedReference: _someService\n      Target: (component.name=overriding.service.reference.CustomServiceImpl)\n      Bound to:        6841\n          Properties:\n            _defaultService.target = (component.name=overriding.service.reference.service.CustomServiceImpl)\n            component.id = 2398\n            component.name = overriding.service.reference.service.CustomServiceImpl\n            objectClass = [override.my.service.reference.service.api.SomeService]\n            service.bundleid = 525\n            service.id = 6841\n            service.scope = bundle\n      Component Configuration Properties:\n        _someService.target = (component.name=overriding.service.reference.service.CustomServiceImpl)\n        ...\n\\end{verbatim}\n\nThe example component's \\texttt{\\_someService} reference targets the\ncustom service component\n\\texttt{overriding.service.reference.service.CustomServiceImpl}.\n\\texttt{CustomServiceImpl} references default service\n\\texttt{SomeServiceImpl} to delegate work to it.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/overriding-service-refs-result.png}\n\\caption{Because the example component's service reference is overridden\nby the configuration file deployment, the portlet indicates it's calling\nthe custom service.}\n\\end{figure}\n\nLiferay DXP processed the configuration file and injected the service\nreference, which in turn bound the custom service to the referencing\ncomponent!\n\n\\section{Related Topics}\\label{related-topics-33}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{OSGi\n  Services and Dependency Injection with Declarative Services}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Using\n  Felix Gogo Shell}\n\\end{itemize}\n\n\\chapter{Portlet Filters}\\label{portlet-filters}\n\nPortlet filters intercept requests and responses at the start of the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{portlet request\nprocessing phase}. Portlet filters are commonly used for these things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Transform content\n\\item\n  Add or modify request and response attributes\n\\item\n  Suspend a portlet phase to get user input\n\\item\n  Audit portlet activity\n\\end{itemize}\n\nThe\n\\href{http://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/filter/package-frame.html}{\\texttt{javax.portlet.filter}}\npackage defines a portlet filter interface for each phase. Here are the\nsteps for developing a portlet filter:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Implement the\n  \\href{http://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/filter/package-frame.html}{portlet\n  filter interface} for the phase it's intercepting. Here are common\n  interface methods to override:\n\n  \\texttt{doFilter}: Here's where you take action. This method is\n  invoked at the start of the portlet request processing phase. The\n  request and response parameters provide access to portlet content and\n  attributes. The \\texttt{FilterChain} parameter can be used to invoke\n  the next filter in the phase.\n\n  \\texttt{init}: Initialize the filter. The \\texttt{FilterConfig}\n  parameter can be used to prepare the filter.\n\n  \\texttt{destroy}: Perform any filter cleanup.\n\\item\n  Target the desired portlet(s).\n\\item\n  Choose how to prioritize the filter among other filters in the phase:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    OSGi Declarative Service Component portlet filters use a service\n    ranking property. High ranking filters execute before lower ones.\n  \\item\n    \\texttt{\\textless{}filter-mapping\\textgreater{}} element order in a\n    portlet application's \\texttt{portlet.xml} file.\n  \\item\n    The \\texttt{ordinal} element value of a filter class annotated with\n    \\texttt{@PortletLifecycleFilter}. Low ordinal value filters execute\n    before higher ones.\n  \\end{itemize}\n\\end{enumerate}\n\nBelow is demonstrated applying multiple filters to a portlet's render\nphase. The filters are\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{OSGi\nDeclarative Service (DS) Components}, but filters can also be applied to\na portlet using a \\texttt{portlet.xml} descriptor or a\n\\texttt{@PortletLifecycleFilter} annotation. See the Portlet 3.0\nSpecification for details. The sample code is available\n\\href{https://portal.liferay.dev/learn/code-samples/-/cs/list/7.2/java8/workspace-gradle/modules/applications/portlets/render-filter-portlet}{here}.\n\n\\section{Sample Portlet}\\label{sample-portlet}\n\nThe sample portlet \\texttt{MembersListPortlet} is a\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{Liferay\nMVC Portlet} that lists names and email addresses when users click its\n\\emph{Load Users} button. The information is based on \\texttt{Person}\nobjects that the portlet class passes to the View template via a request\nattribute called \\texttt{MembersListPortlet.MEMBERLIST\\_ATTRIBUTE}.\n\n\\begin{verbatim}\npublic void loadUsers(ActionRequest actionRequest, ActionResponse actionResponse) {\n\n    actionRequest.setAttribute(MembersListPortlet.MEMBERLIST_ATTRIBUTE, createStaticUserList());\n}\n\\end{verbatim}\n\nTwo render filters are applied to the portlet:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Render filter 1 hides parts of the user email addresses (e.g., for\n  privacy) by modifying the request object.\n\\item\n  Render filter 2 logs portlet render phase statistics.\n\\end{enumerate}\n\nAdding the \\texttt{MemberList} portlet to a page and clicking the\n\\texttt{Load\\ Users} button renders each \\texttt{Person}'s name and\npartially hidden email address, thanks to the filter\n\\texttt{EncodingPersonEmailsRenderFilter}.\n\n\\begin{verbatim}\nSievert Shayne\n\nSievert.Sha...@...mple.com\n\nVida Jonas\n\nVida.Jo...@...mple.com\n...\n\\end{verbatim}\n\nIf you set the portlet's log level to \\texttt{debug}, it prints the\nrender phase statistics.\n\n\\begin{verbatim}\nPortlet com_liferay_code_samples_portal_modules_applications_portlets_render_filter_MembersListPortlet rendered in 7791 ms\nPortlet com_liferay_code_samples_portal_modules_applications_portlets_render_filter_MembersListPortlet rendered 2 times with an average 356135 ms render time\n\\end{verbatim}\n\nThe first filter modifies portlet content via the request object.\n\n\\section{Render filter 1 hides parts of user email\naddresses}\\label{render-filter-1-hides-parts-of-user-email-addresses}\n\n\\texttt{EncodingPersonEmailsRenderFilter} is a \\texttt{RenderFilter}\nthat hides parts of user email addresses by modifying a request\nattribute. Here is the class:\n\n\\begin{verbatim}\n@Component(\n        immediate = true,\n        property = {\n                \"javax.portlet.name=\" + MembersListPortlet.MEMBERSLIST_PORTLET_NAME,\n                \"service.ranking:Integer=1\" \n        },\n        service = PortletFilter.class\n)\npublic class EncodingPersonEmailsRenderFilter implements RenderFilter {\n\n    @Override\n    public void doFilter(RenderRequest request, RenderResponse response, FilterChain chain)\n            throws IOException, PortletException {\n\n        //This is executed before the portlet render\n        Optional.ofNullable((List<Person>)request.getAttribute(MembersListPortlet.MEMBERLIST_ATTRIBUTE))\n                .ifPresent(personList ->\n                        request.setAttribute(MembersListPortlet.MEMBERLIST_ATTRIBUTE, ofuscateEmails(personList)));\n\n        // Invoke the rest of the filters in the chain\n        //  (it also invokes the Portlet render method if this is the last filter in the chain\n        chain.doFilter(request, response);\n\n    }\n\n    private List<Person> ofuscateEmails(List<Person> list) {\n        return list.stream()\n                .map(this::ofuscatePersonEmail)\n                .collect(Collectors.toList());\n    }\n\n    private Person ofuscatePersonEmail(Person person) {\n        return new Person(person.getName(),\n                          person.getEmail().replaceFirst(\"(.+)(...)@(...)(.*)\", \"$1...@...$4\"));\n\n    }\n\n    @Override\n    public void init(FilterConfig filterConfig) throws PortletException {\n\n    }\n\n    @Override\n    public void destroy() {\n\n    }\n}\n\\end{verbatim}\n\nThe \\texttt{@Component} annotation declares the filter to be an OSGi DS\nComponent. Here are its elements and properties:\n\n\\texttt{immediate\\ =\\ true} sets the component ready to start upon being\ninstalled.\n\n\\texttt{service\\ =\\ PortletFilter.class} defines the component to be a\n\\texttt{PortletFilter} service.\n\n\\texttt{javax.portlet.name\\ =\\ +\\ MembersListPortlet.MEMBERSLIST\\_PORTLET\\_NAME}\nlinks the filter to the target portlet. Note, multiple portlets can be\nlisted.\n\n\\texttt{service.ranking:Integer=1} sets the filter to execute after\nfilters that are ranked higher than \\texttt{1}.\n\n\\texttt{EncodingPersonEmailsRenderFilter} \\emph{implements} the\n\\href{http://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/filter/RenderFilter.html}{\\texttt{RenderFilter}}\ninterface, overriding the \\texttt{doFilter}, \\texttt{init}, and\n\\texttt{destroy} methods.\n\n\\texttt{doFilter} modifies the attribute\n\\texttt{MembersListPortlet.MEMBERLIST\\_ATTRIBUTE}'s list of\n\\texttt{Person}s by replacing parts of their email addresses with\nellipses (\\texttt{...}). It delegates the \\texttt{ofuscatePersonEmail}\nmethod to do the modifications. Then \\texttt{doFilter} invokes\n\\texttt{chain.doFilter(request,\\ response)} to execute the next\n\\texttt{RenderFilter} or next portlet processing phase.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Filters can also intercept and block the execution of a\nportlet phase. In the \\texttt{doFilter} method, this is usually done by\nthrowing an exception or by not calling the next element in the filter\nchain.\n\n\\noindent\\hrulefill\n\n\\section{RenderFilter 2 Logs\nStatistics}\\label{renderfilter-2-logs-statistics}\n\n\\texttt{MembersListStatsRenderFilter} is a \\texttt{RenderFilter} that\nlogs the number of times the portlet is rendered and the average render\ntime. Here's the code:\n\n\\begin{verbatim}\n@Component(\n        immediate = true,\n        property = {\n                \"javax.portlet.name=\" + MembersListPortlet.MEMBERSLIST_PORTLET_NAME,\n                \"service.ranking:Integer=100\"\n        },\n        service = PortletFilter.class\n)\npublic class MembersListStatsRenderFilter implements RenderFilter {\n\n    //Thread safe - accumulator that keeps the number of times the portlet has been rendered\n    private final LongAdder hits = new LongAdder();\n\n    //Thread safe accumulator that keeps total time spent rendering the portlet.\n    private final LongAdder accumulatedTimeMs = new LongAdder();\n\n    @Override\n    public void doFilter(RenderRequest request, RenderResponse response, FilterChain chain) throws IOException, PortletException {\n\n        long startTime = System.nanoTime();\n\n        chain.doFilter(request, response);\n\n        long renderTime = (System.nanoTime() - startTime) / 1000;\n        hits.increment();\n        accumulatedTimeMs.add(renderTime);\n\n        if (LOG.isDebugEnabled()) {\n            long totalHits = hits.longValue();\n            long averageRenderTimeNs = accumulatedTimeMs.longValue() / totalHits;\n            LOG.debug(\"Portlet \" + MembersListPortlet.MEMBERSLIST_PORTLET_NAME + \" rendered in \" + renderTime + \" ms\");\n            LOG.debug(\"Portlet \" + MembersListPortlet.MEMBERSLIST_PORTLET_NAME + \" rendered \" + hits.longValue()\n                    + \" times with an average \" + averageRenderTimeNs + \" ms render time\");\n        }\n    }\n\n    ...\n\n    private static final Log LOG = LogFactoryUtil.getLog(MembersListStatsRenderFilter.class);\n}\n\\end{verbatim}\n\nAs with \\texttt{EncodingPersonEmailsRenderFilter}, it's an OSGi DS\nComponent that is a \\texttt{PortletFilter} service, starts upon\ninstallation, applies to the \\texttt{MembersListPortlet}, and has a\nservice ranking. Since its ranking is \\texttt{100}, it is executed\nbefore render filter \\texttt{EncodingPersonEmailsRenderFilter}.\n\n\\texttt{MembersListStatsRenderFilter}'s \\texttt{doFilter()} method\naudits the render phase in these ways:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Notes the render phase start time.\n\\item\n  Executes \\texttt{chain.doFilter(request,\\ response)} to invoke all of\n  the other \\texttt{RenderFilter}s in the \\texttt{FilterChain}.\n\\item\n  Increments the number of times the portlet renders.\n\\item\n  Calculates the average render time.\n\\item\n  Logs the times rendered and average render time.\n\\end{enumerate}\n\nConsider creating your own filters to intercept portlet processing\nphases.\n\n\\section{Related Topics}\\label{related-topics-34}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{Portlets}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters}{JSP\nOverrides Using Portlet Filters}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{Liferay\nMVC Portlet}\n\n\\chapter{Product Navigation}\\label{product-navigation}\n\nLiferay DXP's product navigation consists of the main menus you use to\ncustomize, configure, and navigate the system. When you edit a page,\nswitch to a different Site scope, access a User's credentials, etc.,\nyou're using the default navigation menus. Customizing a default menu\ncan help give your Liferay instance a unique touch. You can extend and\ncustomize the default product navigation to fit your need.\n\nThere are four product navigation sections that you can extend:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Product Menu\n\\item\n  Control Menu\n\\item\n  Simulation Menu\n\\item\n  User Personal Menu\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/product-navigation-summary.png}\n\\caption{The main product navigation menus include the Product Menu,\nControl Menu, Simulation Menu and User Personal Menu.}\n\\end{figure}\n\nThe Product Menu is on the left, and displays the Control Panel and Site\nAdministration functionality. The Control Menu is on top, offering\nnavigation to the Product Menu, Simulation Menu (the right menu), and\nthe \\emph{Add} button. When certain settings are enabled (e.g., Staging,\nPage Customization, etc.), more tools are offered. The Simulation Menu\noffers options to simulate your Site's look for different scenarios\n(devices, user segments, etc.). Finally, the User Personal Menu holds\nselectable items containing a user's own account settings.\n\nYou'll learn more about each of these product navigation sections next.\n\n\\section{Product Menu}\\label{product-menu}\n\nBy default, Liferay's Product Menu consists of two main sections:\nControl Panel and Site Administration. These sections are called\n\\emph{Panel Categories}. For instance, the Control Panel is a single\nPanel Category, and when clicking on it, you see six child Panel\nCategories: \\emph{Users}, \\emph{Sites}, \\emph{Apps},\n\\emph{Configuration}, and \\emph{Workflow}. Clicking a child Panel\nCategory shows \\emph{panel apps}.\n\nThe Product Menu is intuitive and easy to use---but you can still change\nit any way you want. You can reorganize the Panel Categories and apps,\nor add completely new categories and populate them with custom Panel\nApps. You'll learn how to provide new or modified Panel Categories and\nPanel Apps for the Product Menu. For more information, read the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu}{Customizing\nthe Product Menu} articles.\n\n\\section{Control Menu}\\label{control-menu}\n\nThe Control Menu is the most visible and accessible menu. For example,\non your home page, the Control Menu offers default options for accessing\nthe Product Menu, Simulation Menu, and Add Menu. You can think of this\nmenu as the gateway to configuring options in Liferay DXP.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/control-menu-home.png}\n\\caption{The Control Menu has three configurable areas: left, right, and\nmiddle. It also displays the title and type of page that you are\ncurrently viewing.}\n\\end{figure}\n\nIf you navigate away from the home page, the Control Menu adapts and\nprovides helpful functionality for whatever option you're using. For\nexample, if you navigate to Site Administration → \\emph{Content \\& Data}\n→ \\emph{Web Content}, you see a Control Menu with different\nfunctionality tailored for that option.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/control-menu-web-content.png}\n\\caption{When switching your context to web content, the Control Menu\nadapts to provide helpful options for that area.}\n\\end{figure}\n\nThe default Control Menu contains three categories representing the\nleft, middle, and right portions of the menu. You can create navigation\nentries for each category. For more information, read the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-control-menu}{Customizing\nthe Control Menu} articles.\n\n\\section{Simulation Menu}\\label{simulation-menu}\n\nWhen testing how pages and apps appear for users, it's important to\nsimulate their views in as many ways as possible. The Simulation Menu on\nthe right-side of the main page allows this, and you can extend the menu\nif you need to simulate something that it does not provide.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/simulation-menu-preview.png}\n\\caption{The Simulation Menu offers a device preview application.}\n\\end{figure}\n\nThere are few differences between the Simulation Menu and Product Menu,\nmostly because they extend the same base classes. The Simulation Menu,\nby default, is made up of only one Panel Category and one Panel App.\nLiferay provides the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/product-navigation/product-navigation-simulation-web/src/main/java/com/liferay/product/navigation/simulation/web/internal/application/list/SimulationPanelCategory.java}{\\texttt{SimulationPanelCategory}}\nclass, a hidden category needed to hold the\n\\texttt{DevicePreviewPanelApp}. This is the app and functionality you\nsee in the Simulation Menu by default.\n\nFor more information, read the\n\\href{/docs/7-2/customization/-/knowledge_base/c/extending-the-simulation-menu}{Extending\nthe Simulation Menu} article.\n\n\\section{User Personal Menu}\\label{user-personal-menu}\n\nThe User Personal Menu displays options unique to the current user. By\ndefault, this menu appears as an avatar button that expands the User\nSettings sub-menu just below the Control Menu. In a custom theme, the\nUser Personal Menu could appear anywhere in the interface.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/user-personal-menu.png}\n\\caption{By default, the User Personal Menu contains the signed-in\nuser's avatar, which opens the user's settings when selected.}\n\\end{figure}\n\nAlthough Liferay's default User Personal Menu is bare-bones, you can add\nmore functionality to fit your needs. Unlike other product navigation\nmenus (e.g., Product Menu), the User Personal Bar does not require the\nextension/creation of Panel Categories and Panel Apps. It uses another\ncommon Liferay framework for providing functionality:\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes}{Portlet\nProviders}.\n\nThe User Personal Menu can be seen as a placeholder in every Liferay\ntheme. By default, Liferay provides one sample \\emph{User Personal Bar}\nportlet that fills that placeholder, but the portlet Liferay provides\ncan be replaced by other portlets.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can add the User Personal Bar to your theme by adding\nthe following snippet into your \\texttt{portal\\_normal.ftl}:\n\n\\begin{verbatim}\n<@liferay.user_personal_bar />\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nFor more information, read the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-user-personal-bar-and-menu}{Customizing\nthe User Personal Bar and Menu} article.\n\n\\chapter{Customizing the Product\nMenu}\\label{customizing-the-product-menu}\n\nCustomizing the Product Menu can be completed by adding Panel Categories\nand Panel Apps.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Product Menu cannot be changed by applying a new\ntheme. To change the layout/style of the Product Menu, you must create\nand deploy a theme contributor. See the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site}{Theme\nContributors} article for more details.\n\n\\noindent\\hrulefill\n\nTo create these entities, you must implement the\n\\href{https://docs.liferay.com/dxp/apps/application-list/latest/javadocs/com/liferay/application/list/PanelCategory.html}{\\texttt{PanelCategory}}\nand\n\\href{https://docs.liferay.com/dxp/apps/application-list/latest/javadocs/com/liferay/application/list/PanelApp.html}{\\texttt{PanelApp}}\ninterfaces.\n\n\\section{PanelCategory Interface}\\label{panelcategory-interface}\n\nThe \\texttt{PanelCategory} interface requires you to implement the\nfollowing methods:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{getNotificationCount}: returns the number of notifications to\n  be shown in the Panel Category.\n\\item\n  \\texttt{include}: renders the body of the Panel Category.\n\\item\n  \\texttt{includeHeader}: renders the Panel Category header.\n\\item\n  \\texttt{isActive}: whether the panel is selected.\n\\item\n  \\texttt{isPersistState}: whether to persist the Panel Category's state\n  to the database. This saves the state of the Panel Category when\n  navigating away from the menu.\n\\end{itemize}\n\nYou can reduce the number of methods you must implement if you extend a\nbase class that already implements the \\texttt{PanelCategory} interface.\nThe recommended way to do this is by extending the\n\\href{https://docs.liferay.com/dxp/apps/application-list/latest/javadocs/com/liferay/application/list/BasePanelCategory.html}{\\texttt{BasePanelCategory}}\nor\n\\href{https://docs.liferay.com/dxp/apps/application-list/latest/javadocs/com/liferay/application/list/BaseJSPPanelCategory.html}{\\texttt{BaseJSPPanelCategory}}\nabstract classes. Typically, the \\texttt{BasePanelCategory} is extended\nfor basic categories (e.g., the Control Panel category) that only\ndisplay the category name. To add more complex functionality, you can\nthen provide a custom UI for your panel using any front-end technology\nby implementing the \\texttt{include()} or \\texttt{includeHeader()} from\nthe \\texttt{PanelCategory} interface.\n\nIf you plan to use JSPs as the front-end technology, extend a base class\ncalled \\texttt{BaseJSPPanelCategory} that already implements the methods\n\\texttt{include()} and \\texttt{includeHeader()} for you.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} In this article, example JSPs describe how to provide\nfunctionality to Panel Categories and Panel Apps. JSPs, however, are not\nthe only way to provide front-end functionality to your categories/apps.\nYou can create your own class implementing \\texttt{PanelCategory} to use\nother technologies such as FreeMarker.\n\n\\noindent\\hrulefill\n\nMore information on provided base classes for your\n\\texttt{PanelCategory} implementation are described next.\n\n\\section{BasePanelCategory}\\label{basepanelcategory}\n\nIf you need something simple for your Panel Category like a name,\nextending \\texttt{BasePanelCategory} is probably sufficient. For\nexample, the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/product-navigation/product-navigation-control-panel/src/main/java/com/liferay/product/navigation/control/panel/internal/application/list/ControlPanelCategory.java}{\\texttt{ControlPanelCategory}}\nextends \\texttt{BasePanelCategory} and specifies a \\texttt{getLabel}\nmethod to set and display the Panel Category name.\n\n\\begin{verbatim}\n@Override\npublic String getLabel(Locale locale) {\n    return LanguageUtil.get(locale, \"control-panel\");\n}\n\\end{verbatim}\n\n\\section{BaseJSPPanelCategory}\\label{basejsppanelcategory}\n\nIf you need more complex functionality, extend\n\\texttt{BaseJSPPanelCategory} and use JSPs to render the Panel Category.\nFor example, the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/product-navigation/product-navigation-site-administration/src/main/java/com/liferay/product/navigation/site/administration/internal/application/list/SiteAdministrationPanelCategory.java}{\\texttt{SiteAdministrationPanelCategory}}\nspecifies the \\texttt{getHeaderJspPath} and \\texttt{getJspPath} methods.\nYou could create a JSP with the UI you want to render and specify its\npath in methods like these:\n\n\\begin{verbatim}\n@Override\npublic String getHeaderJspPath() {\n    return \"/sites/site_administration_header.jsp\";\n}\n\n@Override\npublic String getJspPath() {\n    return \"/sites/site_administration_body.jsp\";\n}\n\\end{verbatim}\n\nOne JSP renders the Panel Category's header (displayed when panel is\ncollapsed) and the other its body (displayed when panel is expanded).\n\nNext, you'll learn about the \\texttt{PanelApp} interface.\n\n\\section{PanelApp Interface}\\label{panelapp-interface}\n\nThe \\texttt{PanelApp} interface requires you to implement the following\nmethods:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{getNotificationCount}: returns the number of notifications for\n  the user.\n\\item\n  \\texttt{getPortlet}: returns the portlet associated with the\n  application.\n\\item\n  \\texttt{getPortletId}: returns the portlet's ID associated with the\n  application.\n\\item\n  \\texttt{getPortletURL}: returns the URL used to render a portlet based\n  on the servlet request attributes.\n\\item\n  \\texttt{include}: Returns \\texttt{true} if the application\n  successfully renders.\n\\item\n  \\texttt{setGroupProvider}: sets the group provider associated with the\n  application.\n\\item\n  \\texttt{setPortlet}: sets the portlet associated with the application.\n\\end{itemize}\n\nYou can reduce the number of methods you must implement if you extend a\nbase class that already implements the \\texttt{PanelCategory} interface.\nThe recommended way to do this is by extending the\n\\href{https://docs.liferay.com/dxp/apps/application-list/latest/javadocs/com/liferay/application/list/BasePanelApp.html}{\\texttt{BasePanelApp}}\nor\n\\href{https://docs.liferay.com/dxp/apps/application-list/latest/javadocs/com/liferay/application/list/BaseJSPPanelApp.html}{\\texttt{BaseJSPPanelApp}}\nabstract classes. If you want to use JSPs to render that UI, extend\n\\texttt{BaseJSPPanelApp}. This provides additional methods you can use\nto incorporate JSP functionality into your app's listing in the Product\nMenu.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} JSPs are not the only way to provide front-end\nfunctionality to your Panel Apps. You can create your own class\nimplementing \\texttt{PanelApp} to use other technologies such as\nFreeMarker.\n\n\\noindent\\hrulefill\n\nThe \\texttt{BlogsPanelApp} is a simple example of how to specify your\nportlet as a Panel App. This class extends \\texttt{BasePanelApp},\noverriding the \\texttt{getPortletId} and \\texttt{setPortlet} methods.\nThese methods specify and set the Blogs portlet as a Panel App.\n\nThis is how those methods look for the Blogs portlet:\n\n\\begin{verbatim}\n@Override\npublic String getPortletId() {\n    return BlogsPortletKeys.BLOGS_ADMIN;\n}\n\n@Override\n@Reference(\n    target = \"(javax.portlet.name=\" + BlogsPortletKeys.BLOGS_ADMIN + \")\",\n    unbind = \"-\"\n)\npublic void setPortlet(Portlet portlet) {\n    super.setPortlet(portlet);\n}\n\\end{verbatim}\n\nEach Panel App must belong to a portlet and each portlet can have at\nmost one Panel App. If more than one Panel App is needed, another\nportlet must be created. By default, the Panel App only appears if the\nuser has permission to view the associated portlet.\n\nContinue on the learn about creating custom Panel Categories and Panel\nApps.\n\n\\chapter{Adding Custom Panel\nCategories}\\label{adding-custom-panel-categories}\n\nAs you navigate the Product Menu, you can see that Panel Apps like\n\\emph{Web Content} and \\emph{Settings} are organized into Panel\nCategories such as \\emph{Content \\& Data} and \\emph{Configuration}. This\narticle explains how to add new Panel Categories to the menu. Adding new\nPanel Apps is covered in the next section.\n\nThere are three steps to creating a new category:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create the OSGi structure and metadata.\n\\item\n  Implement Liferay's Frameworks.\n\\item\n  Define the Panel Category.\n\\end{enumerate}\n\n\\section{Creating the OSGi Module}\\label{creating-the-osgi-module}\n\nFirst you must create the project.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create an OSGi module using your favorite third party tool, or use\n  \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}.\n  Blade CLI offers a\n  \\href{/docs/7-2/reference/-/knowledge_base/r/panel-app-template}{Panel\n  App} template, which is for creating a Panel Category and Panel App.\n\\item\n  Create a unique package name in the module's \\texttt{src} directory\n  and create a new Java class in that package. To follow naming\n  conventions, give your class a unique name followed by\n  \\texttt{PanelCategory} (e.g., \\texttt{ControlPanelCategory}).\n\\end{enumerate}\n\n\\section{Implementing Liferay's\nFrameworks}\\label{implementing-liferays-frameworks}\n\nNext, you must connect your OSGi module to Liferay's frameworks and use\nthose to define information about your entry. This takes only two steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Insert the \\texttt{@Component} annotation declaring the panel category\n  keys directly above the class's declaration:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"panel.category.key=\" + [Panel Category Key],\n        \"panel.category.order:Integer=[int]\"\n    },\n    service = PanelCategory.class\n)\n\\end{verbatim}\n\n  You can view an example of a similar \\texttt{@Component} annotation\n  for the \\texttt{UserPanelCategory} class below:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"panel.category.key=\" + PanelCategoryKeys.ROOT,\n        \"panel.category.order:Integer=200\"\n    },\n    service = PanelCategory.class\n)\n\\end{verbatim}\n\n  The \\texttt{property} element designates two properties that should be\n  assigned for your category. The \\texttt{panel.category.key} specifies\n  the parent category for your custom category. You can find popular\n  parent categories to assign in the\n  \\href{https://docs.liferay.com/dxp/apps/application-list/latest/javadocs/com/liferay/application/list/PanelCategoryKeys.html}{\\texttt{PanelCategoryKeys}}\n  class. For instance, if you wanted to create a child category in the\n  Control Panel, you could assign\n  \\texttt{PanelCategoryKeys.CONTROL\\_PANEL}. Likewise, if you wanted to\n  create a root category, like the Control Panel or Site Administration,\n  you could assign \\texttt{PanelCategoryKeys.ROOT}.\n\n  The \\texttt{panel.category.order:Integer} property specifies the order\n  in which your category is displayed. The higher the number (integer),\n  the lower your category is listed among other sibling categories\n  assigned to a parent.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** To insert a Panel Category between existing categories in the\n default menu, you must know the `panel.category.order:Integer` property\n for the existing categories. For example, the Product Menu's two main\n sections---Control Panel and Site Administration---have\n `panel.category.order:Integer` properties of 100 and 200, respectively. A\n new panel inserted between Control Panel and Site Administration would\n need a `panel.category.key` of ROOT and a `panel.category.order:Integer`\n of 150.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nFinally, your `service` element should specify the `PanelCategory.class`\nservice.\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Implement the \\texttt{PanelCategory} interface. See the\n  \\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu\\#panelcategory-interface}{\\texttt{PanelCategory}\n  Interface} section for more details. Extending one of the provided\n  base classes\n  (\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu\\#basepanelcategory}{BasePanelCategory}\n  or\n  \\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu\\#basejsppanelcategory}{BaseJSPPanelCategory})\n  is a popular way to implement the \\texttt{PanelCategory} interface.\n\\item\n  If you elect to leverage JSPs, you must also specify the servlet\n  context from where you are loading the JSP files. If this is inside an\n  OSGi module, make sure your \\texttt{bnd.bnd} file has defined a web\n  context path:\n\n\\begin{verbatim}\nBundle-SymbolicName: com.sample.my.module.web\nWeb-ContextPath: /my-module-web\n\\end{verbatim}\n\n  Then reference the Servlet context using the symbolic name of your\n  module like this:\n\n\\begin{verbatim}\n@Override\n@Reference(\n    target = \"(osgi.web.symbolicname=com.sample.my.module.web)\",\n    unbind = \"-\"\n)\npublic void setServletContext(ServletContext servletContext) {\n    super.setServletContext(servletContext);\n}\n\\end{verbatim}\n\\end{enumerate}\n\nExcellent! You've successfully created a custom Panel Category to\ndisplay in the Product Menu. In many cases, a Panel Category holds Panel\nApps for users to access. You'll learn how to add a Panel App to a Panel\nCategory next.\n\n\\chapter{Adding Custom Panel Apps}\\label{adding-custom-panel-apps}\n\nAfter you have created a Panel Category, create a Panel App to go in it:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create an OSGi module using your favorite third party tool, or use\n  \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}.\n  Blade CLI offers a\n  \\href{/docs/7-2/reference/-/knowledge_base/r/panel-app-template}{Panel\n  App} template to help generate a basic Panel Category and Panel App.\n\\item\n  Create a unique package name in the module's \\texttt{src} directory,\n  and create a new Java class in that package. To follow naming\n  conventions, give your class a unique name followed by \\emph{PanelApp}\n  (e.g., \\texttt{JournalPanelApp}).\n\\item\n  Directly above the class's declaration, insert the following\n  annotation:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"panel.app.order:Integer=INTEGER\"\n        \"panel.category.key=\" + PANEL_CATEGORY_KEY,\n    },\n    service = PanelApp.class\n)\n\\end{verbatim}\n\n  You can view an example of a similar \\texttt{@Component} annotation\n  for the \\texttt{JournalPanelApp} class below.\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"panel.app.order:Integer=100\",\n        \"panel.category.key=\" + PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT\n    },\n    service = PanelApp.class\n)\n\\end{verbatim}\n\n  These properties and attributes are similar to those discussed in the\n  previous\n  \\href{/docs/7-2/customization/-/knowledge_base/c/adding-custom-panel-categories}{article}.\n  The \\texttt{panel.category.key} assigns your Panel App to a Panel\n  Category. The \\texttt{panel.app.order:Integer} property specifies the\n  order your Panel App appears among other Panel Apps in the same\n  category. For example, if you want to add a Panel App to Site\n  Administration → \\emph{Content \\& Data}, add the following property:\n\n\\begin{verbatim}\n\"panel.category.key=\" + PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT\n\\end{verbatim}\n\n  Visit the\n  \\href{https://docs.liferay.com/dxp/apps/application-list/latest/javadocs/com/liferay/application/list/constants/PanelCategoryKeys.html}{PanelCategoryKeys}\n  class for keys you can use to specify default Panel Categories in\n  Liferay.\n\n  Set the \\texttt{service} attribute to \\texttt{PanelApp.class}.\n\\item\n  Implement the \\texttt{PanelApp} interface. See the\n  \\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu\\#panelapp-interface}{\\texttt{PanelApp}\n  Interface} section for more details. Extending one of the provided\n  base classes\n  (\\href{https://docs.liferay.com/dxp/apps/application-list/latest/javadocs/com/liferay/application/list/BasePanelApp.html}{BasePanelApp}\n  or\n  \\href{https://docs.liferay.com/dxp/apps/application-list/latest/javadocs/com/liferay/application/list/BaseJSPPanelApp.html}{BaseJSPPanelApp})\n  is a popular way to implement the \\texttt{PanelApp} interface. See the\n  \\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu\\#panelapp-interface}{PanelApp\n  Interface} section for more information.\n\\item\n  If you elect to leverage JSPs, you must also specify the servlet\n  context from where you are loading the JSP files. If this is inside an\n  OSGi module, make sure your \\texttt{bnd.bnd} file has defined a web\n  context path:\n\n\\begin{verbatim}\nBundle-SymbolicName: com.sample.my.module.web\nWeb-ContextPath: /my-module-web\n\\end{verbatim}\n\n  Then reference the Servlet context using the symbolic name of your\n  module like this:\n\n\\begin{verbatim}\n@Override\n@Reference(\n    target = \"(osgi.web.symbolicname=com.sample.my.module.web)\",\n    unbind = \"-\"\n)\npublic void setServletContext(ServletContext servletContext) {\n    super.setServletContext(servletContext);\n}\n\\end{verbatim}\n\\end{enumerate}\n\nNow you know how to add or modify a Panel App in the Product Menu. Not\nonly does Liferay provide a simple solution to add new Panel Categories\nand Panel Apps, it also gives you the flexibility to add a more complex\nUI to the Product Menu using any technology.\n\n\\chapter{Customizing the Control\nMenu}\\label{customizing-the-control-menu}\n\nLiferay's Control Menu consists of three main sections: Sites (left\nportion), Tools (middle portion), and User (right portion).\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/control-menu-areas.png}\n\\caption{This image shows where your entry will reside depending on the\ncategory you select.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can add the Control Menu to a theme by adding the\nfollowing snippet into your \\texttt{portal\\_normal.ftl}:\n\n\\begin{verbatim}\n<@liferay.control_menu />\n\\end{verbatim}\n\nThe other product navigation menus (e.g., Product Menu, Simulation Menu)\nare included in this tag, so specifying the above snippet embeds all\nthree menus into your theme. Embedding the User Personal Menu is\nslightly different. Visit the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-user-personal-bar-and-menu}{Customizing\nthe User Personal Bar and Menu} article for more information.\n\n\\noindent\\hrulefill\n\nYou can reference a sample Control Menu Entry by visiting the\n\\href{/docs/7-2/reference/-/knowledge_base/r/control-menu-entry-template}{Control\nMenu Entry} article.\n\n\\section{ProductNavigationControlMenuEntry\nInterface}\\label{productnavigationcontrolmenuentry-interface}\n\nTo create a control menu entry, you must implement the\n\\href{https://docs.liferay.com/dxp/apps/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/ProductNavigationControlMenuEntry.html}{\\texttt{ProductNavigationControlMenuEntry}}\ninterface. It's recommended to implement this interface by extending the\n\\href{https://docs.liferay.com/dxp/apps/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/BaseProductNavigationControlMenuEntry.html}{\\texttt{BaseProductNavigationControlMenuEntry}}\nor\n\\href{https://docs.liferay.com/dxp/apps/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/BaseJSPProductNavigationControlMenuEntry.html}{\\texttt{BaseJSPProductNavigationControlMenuEntry}}\nabstract classes.\n\nThese base classes are covered in more detail next.\n\n\\section{BaseProductNavigationControlMenuEntry}\\label{baseproductnavigationcontrolmenuentry}\n\nTypically, the \\texttt{BaseProductNavigationControlMenuEntry} is\nextended for basic entries that only display a link with text or a\nsimple icon. If you want to provide a more complex UI with buttons or a\nsub-menu, you can override the \\texttt{include()} and\n\\texttt{includeBody()} methods.\n\nThe\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-web/src/main/java/com/liferay/portal/search/web/internal/product/navigation/control/menu/IndexingProductNavigationControlMenuEntry.java}{\\texttt{IndexingProductNavigationControlMenuEntry}}\nis a simple example for providing text and an icon. It extends the\n\\texttt{BaseProductNavigationControlMenuEntry} class and is used when\nLiferay is indexing. The indexing entry is displayed in the \\emph{Tools}\n(middle) area of the Control Menu with a \\emph{Refresh} icon and text\nstating \\emph{The Portal is currently indexing}.\n\n\\section{BaseJSPProductNavigationControlMenuEntry}\\label{basejspproductnavigationcontrolmenuentry}\n\nIf you use JSPs for generating the UI, you can extend\n\\texttt{BaseJSPProductNavigationControlMenuEntry} to save time when\ncreating/modifying a control menu entry.\n\nThe\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/product-navigation/product-navigation-product-menu-web/src/main/java/com/liferay/product/navigation/product/menu/web/internal/product/navigation/control/menu/ProductMenuProductNavigationControlMenuEntry.java}{\\texttt{ProductMenuProductNavigationControlMenuEntry}}\ncreates an entry that appears in the \\emph{Sites} (left) area of the\nControl Menu. This class extends the\n\\texttt{BaseJSPProductNavigationControlMenuEntry} class. This provides\nseveral more methods that use JSPs to define your entry's UI. There are\ntwo methods to notice:\n\n\\begin{verbatim}\n@Override\npublic String getBodyJspPath() {\nreturn \"/portlet/control_menu/product_menu_control_menu_entry_body.jsp\";\n}\n\n@Override\npublic String getIconJspPath() {\nreturn \"/portlet/control_menu/product_menu_control_menu_entry_icon.jsp\";\n}\n\\end{verbatim}\n\nThe \\texttt{getIconJspPath()} method provides the Product Menu icon\n(\\includegraphics{./images/icon-menu.png} → !{[}Menu Open{]}(../../..and\nthe \\texttt{getBodyJspPath()} method adds the UI body for the entry\noutside of the Control Menu. The latter method must be used when\nproviding a UI outside the Control Menu. You can test this by opening\nand closing the Product Menu on the home page.\n\nFinally, if you provide functionality that is exclusively inside the\nControl Menu, the \\texttt{StagingProductNavigationControlMenuEntry}\nclass calls its JSP like this:\n\n\\begin{verbatim}\n@Override\npublic String getIconJspPath() {\nreturn \"/control_menu/entry.jsp\";\n}\n\\end{verbatim}\n\nThe \\texttt{entry.jsp} is returned, which embeds the Staging Bar portlet\ninto the Control Menu.\n\nNext, you'll step through the process of customizing the Control Menu.\n\n\\chapter{Creating Control Menu\nEntries}\\label{creating-control-menu-entries}\n\nNow you'll create entries to customize the Control Menu. Make sure to\nread\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-custom-panel-categories}{Adding\nCustom Panel Categories} before beginning this article. This article\nassumes you know how to create a Panel Category. Creating a Control Menu\nEntry follows the same pattern as creating a Panel Category:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create the OSGi structure and metadata.\n\\item\n  Implement Liferay's Frameworks.\n\\item\n  Define the Control Menu Entry.\n\\end{enumerate}\n\n\\section{Creating the OSGi Module}\\label{creating-the-osgi-module-1}\n\nFirst you must create the project.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a generic OSGi module. Your module must contain a Java class,\n  \\texttt{bnd.bnd} file, and build file (e.g., \\texttt{build.gradle} or\n  \\texttt{pom.xml}). You'll create your Java class next if your project\n  does not already define one.\n\\item\n  Create a unique package name in the module's \\texttt{src} directory\n  and create a new Java class in that package. Give your class a unique\n  name followed by \\emph{ProductNavigationControlMenuEntry}\n  (e.g.,\\texttt{StagingProductNavigationControlMenuEntry}).\n\\end{enumerate}\n\n\\section{Implementing Liferay's\nFrameworks}\\label{implementing-liferays-frameworks-1}\n\nNext, you need to connect your OSGi module to Liferay's frameworks and\nuse those to define information about your entry.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Directly above the class's declaration, insert this code:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"product.navigation.control.menu.category.key=\" + [Control Menu Category],\n        \"product.navigation.control.menu.category.order:Integer=[int]\"\n    },\n    service = ProductNavigationControlMenuEntry.class\n)\n\\end{verbatim}\n\n  The \\texttt{product.navigation.control.menu.category.key} property\n  specifies your entry's category. The default Control Menu provides\n  three categories: Sites (left portion), Tools (middle portion), and\n  User (right portion).\n\n  To specify the category, reference the appropriate key in the\n  \\href{https://docs.liferay.com/dxp/apps/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/constants/ProductNavigationControlMenuCategoryKeys.html}{ProductNavigationControlMenuCategoryKeys}\n  class. For example, this property places your entry in the middle\n  portion of the Control Menu:\n\n\\begin{verbatim}\n\"product.navigation.control.menu.category.key=\" + ProductNavigationControlMenuCategoryKeys.TOOLS\n\\end{verbatim}\n\n  Like Panel Categories, you must specify an integer to place your entry\n  in the category. Entries are ordered from left to right: an entry with\n  order \\texttt{1} appears to the left of an entry with order\n  \\texttt{2}. If the order is not specified, it's chosen at random based\n  on which service was registered first in the OSGi container.\n\n  Finally, your \\texttt{service} element should specify the\n  \\texttt{ProductNavigationControlMenuEntry.class} service.\n\\item\n  Implement the\n  \\href{https://docs.liferay.com/dxp/apps/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/ProductNavigationControlMenuEntry.html}{\\texttt{ProductNavigationControlMenuEntry}}\n  interface. You can also extend the\n  \\href{https://docs.liferay.com/dxp/apps/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/BaseProductNavigationControlMenuEntry.html}{\\texttt{BaseProductNavigationControlMenuEntry}}\n  or\n  \\href{https://docs.liferay.com/dxp/apps/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/BaseJSPProductNavigationControlMenuEntry.html}{\\texttt{BaseJSPProductNavigationControlMenuEntry}}\n  abstract classes. See the\n  \\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-control-menu}{Customizing\n  the Control Menu} article for more information on these classes.\n\\item\n  If you elect to leverage JSPs, you must specify the servlet context\n  for the JSP files. If this is inside an OSGi module, make sure your\n  \\texttt{bnd.bnd} file defines a web context path:\n\n\\begin{verbatim}\nBundle-SymbolicName: com.sample.my.module.web\nWeb-ContextPath: /my-module-web\n\\end{verbatim}\n\n  And then reference the Servlet context using the symbolic name of your\n  module:\n\n\\begin{verbatim}\n@Override\n@Reference(\n    target = \"(osgi.web.symbolicname=com.sample.my.module.web)\",\n    unbind = \"-\"\n)\npublic void setServletContext(ServletContext servletContext) {\n    super.setServletContext(servletContext);\n}\n\\end{verbatim}\n\\item\n  Part of creating the entry is defining when it appears. The Control\n  Menu shows different entries depending on the displayed page. You can\n  specify when your entry appears with the\n  \\texttt{isShow(HttpServletRequest)} method.\n\n  For example, the \\texttt{IndexingProductNavigationControlMenuEntry}\n  class queries the number of indexing jobs when calling\n  \\texttt{isShow}. If the query count is \\texttt{0}, the indexing entry\n  doesn't appear in the Control Menu:\n\n\\begin{verbatim}\n@Override\npublic boolean isShow(HttpServletRequest request) throws PortalException {\n    int count = _indexWriterHelper.getReindexTaskCount(\n        CompanyConstants.SYSTEM, false);\n\n    if (count == 0) {\n        return false;\n    }\n\n    return super.isShow(request);\n}\n\\end{verbatim}\n\n  The \\texttt{StagingProductNavigationControlMenuEntry} class selects\n  the pages to appear. The staging entry never appears if the page is an\n  administration page (e.g., \\emph{Site Administration}, \\emph{Control\n  Panel}, etc.):\n\n\\begin{verbatim}\n@Override\npublic boolean isShow(HttpServletRequest request) throws PortalException {\n    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(\n        WebKeys.THEME_DISPLAY);\n\n    Layout layout = themeDisplay.getLayout();\n\n    // This controls if the page is an Administration Page\n\n    if (layout.isTypeControlPanel()) {\n        return false;\n    }\n\n    // This controls if Staging is enabled\n\n  if (!themeDisplay.isShowStagingIcon()) {\n        return false;\n    }\n\n    return true;\n}\n\\end{verbatim}\n\\end{enumerate}\n\nExcellent! You've created your entry in one of the three default\nsections in the Control Menu.\n\n\\chapter{Defining Icons and Tooltips}\\label{defining-icons-and-tooltips}\n\nWhen creating a Control Menu entry, you can use an icon in addition to\nor in place of text. You can also use tooltips to provide a more in\ndepth explanation.\n\n\\section{Control Menu Entry Icons}\\label{control-menu-entry-icons}\n\nYou can provide a Lexicon or CSS icon in your\n\\texttt{*ControlMenuEntry}. To use a Lexicon icon, you should override\nthe methods in \\texttt{ProductMenuProductNavigationControlMenuEntry}\nlike this one:\n\n\\begin{verbatim}\npublic String getIconCssClass(HttpServletRequest request) {\n    return \"\";\n}\n\npublic String getIcon(HttpServletRequest request) {\n    return \"lexicon-icon\";\n}\n\npublic String getMarkupView(HttpServletRequest request) {\n    return \"lexicon\";\n}\n\\end{verbatim}\n\nLikewise, you can use a CSS icon by overriding the\n\\texttt{ProductMenuProductNavigationControlMenuEntry} methods like this\none:\n\n\\begin{verbatim}\npublic String getIconCssClass(HttpServletRequest request) {\n    return \"icon-css\";\n}\n\npublic String getIcon(HttpServletRequest request) {\n    return \"\";\n}\n\npublic String getMarkupView(HttpServletRequest request) {\n    return \"\";\n}\n\\end{verbatim}\n\nYou can find these icons documented\n\\href{https://clayui.com/docs/components/icons.html}{here}.\n\n\\section{Control Menu Entry Tooltips}\\label{control-menu-entry-tooltips}\n\nTo provide a tooltip for the Control Menu entry, create a\n\\texttt{getLabel} method like this:\n\n\\begin{verbatim}\n@Override\npublic String getLabel(Locale locale) {\n    ResourceBundle resourceBundle = ResourceBundleUtil.getBundle(\n        \"content.Language\", locale, getClass());\n\n    return LanguageUtil.get(\n        resourceBundle, \"the-portal-is-currently-reindexing\");\n}\n\\end{verbatim}\n\nYou need to create a \\texttt{Language.properties} to store your labels.\nYou can learn more about resource bundles in the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localization}{Localization}\narticles.\n\n\\chapter{Extending the Simulation\nMenu}\\label{extending-the-simulation-menu}\n\nTo provide your own functionality in the Simulation Menu, you must\ncreate a Panel App in \\texttt{SimulationPanelCategory}. If you want to\nadd extensive functionality, you can even create additional Panel\nCategories in the menu to divide up your Panel Apps. This article covers\nthe simpler case of creating a Panel App for the already present hidden\ncategory.\n\nBefore beginning, make sure you're accustomed to using Panel Categories\nand Panel Apps. This is covered in detail in the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu}{Customizing\nthe Product Menu} articles. Once you know how to create Panel Categories\nand Panel Apps, continue with this article.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Follow the steps documented in\n  \\href{/docs/7-2/customization/-/knowledge_base/c/adding-custom-panel-apps}{Adding\n  Custom Panel Apps} for creating custom Panel Apps. Once you've created\n  the foundation of your Panel App, move on to learn how to tweak it so\n  it customizes the Simulation Menu.\n\n  You can generate a Simulation Panel App by using Blade CLI's\n  \\href{/docs/7-2/reference/-/knowledge_base/r/simulation-panel-entry-template}{Simulation\n  Panel Entry template}. You can also refer to the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/simulation-panel-app}{Simulation\n  Panel App sample} for a working example.\n\\item\n  Since this article assumes you're providing more functionality to the\n  existing simulation category, set the simulation category in the\n  \\texttt{panel.category.key} of the \\texttt{@Component} annotation:\n\n\\begin{verbatim}\n\"panel.category.key=\" + SimulationPanelCategory.SIMULATION\n\\end{verbatim}\n\n  To use this constant, you must add a dependency on\n  \\href{https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.product.navigation.simulation/}{\\texttt{com.liferay.product.navigation.simulation}}.\n\n  Be sure to also specify the order to display your new Panel App, which\n  was explained in\n  \\href{/docs/7-2/customization/-/knowledge_base/c/adding-custom-panel-apps}{Adding\n  Custom Panel Apps}.\n\\item\n  This article assumes you're using JSPs. Therefore, you should extend\n  the\n  \\href{https://docs.liferay.com/dxp/apps/application-list/latest/javadocs/com/liferay/application/list/BaseJSPPanelApp.html}{\\texttt{BaseJSPPanelApp}}\n  abstract class, which implements the\n  \\href{https://docs.liferay.com/dxp/apps/application-list/latest/javadocs/com/liferay/application/list/PanelApp.html}{\\texttt{PanelApp}}\n  interface and also provides additional methods necessary for\n  specifying JSPs to render your Panel App's UI. Remember that you can\n  also implement your own \\texttt{include()} method to use any front-end\n  technology you want, if you want to use a technology other than JSP\n  (e.g., FreeMarker).\n\\item\n  Define your simulation view. For instance, in\n  \\texttt{DevicePreviewPanelApp}, the \\texttt{getJspPath} method points\n  to the \\texttt{simulation-device.jsp} file in the\n  \\texttt{resources/META-INF/resources} folder, where the device\n  simulation interface is defined. Optionally, you can also add your own\n  language keys, CSS, or JavaScript resources in your simulation module.\n\n  The right servlet context is also provided by implementing this\n  method:\n\n\\begin{verbatim}\n@Override\n@Reference(\n    target = \"(osgi.web.symbolicname=com.liferay.product.navigation.simulation.device)\",\n    unbind = \"-\"\n)\npublic void setServletContext(ServletContext servletContext) {\n    super.setServletContext(servletContext);\n}\n\\end{verbatim}\n\n  As explained in\n  \\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu}{Customizing\n  The Product Menu}, a Panel App should be associated with a portlet.\n  This makes the Panel App visible only when the user has permission to\n  view the portlet. This Panel App is associated to the Simulation\n  Device portlet using these methods:\n\n\\begin{verbatim}\n@Override\npublic String getPortletId() {\n    return ProductNavigationSimulationPortletKeys.\n        PRODUCT_NAVIGATION_SIMULATION;\n}\n\n@Override\n@Reference(\n    target = \"(javax.portlet.name=\" + ProductNavigationSimulationPortletKeys.PRODUCT_NAVIGATION_SIMULATION + \")\",\n    unbind = \"-\"\n)\npublic void setPortlet(Portlet portlet) {\n    super.setPortlet(portlet);\n}\n\\end{verbatim}\n\n  Segments also provides a good example of how to extend the Simulation\n  Menu. When segments are available, the Simulation Menu is extended to\n  offer personalization options. You can simulate particular experiences\n  directly from the Simulation Menu. Its Panel App class is similar to\n  \\texttt{DevicePreviewPanelApp}, except it points to a different\n  portlet and JSP. For more information on Segments, see the\n  \\href{/docs/7-2/user/-/knowledge_base/u/segmentation-and-personalization}{Segmentation\n  and Personalization} section.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/segments-preview.png}\n  \\caption{The Simulation Menu also displays Segments to help simulate\n  different user experiences.}\n  \\end{figure}\n\\item\n  You can combine your simulation options with the device simulation\n  options by interacting with the device preview iFrame. To retrieve the\n  device preview frame in an \\texttt{aui:script} block of your custom\n  simulation view's JavaScript, you can use this code:\n\n\\begin{verbatim}\nvar iframe = A.one('#simulationDeviceIframe');\n\\end{verbatim}\n\n  Then you can modify the device preview frame URL like this:\n\n\\begin{verbatim}\niframe.setAttribute('src', newUrlWithCustomParameters);\n\\end{verbatim}\n\\end{enumerate}\n\nNow that you know how to extend the necessary Panel Categories and Panel\nApps to modify the Simulation Menu,\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{create\na module} of your own and customize the Simulation Menu so it's most\nhelpful for your needs.\n\n\\chapter{Customizing the User Personal Bar and\nMenu}\\label{customizing-the-user-personal-bar-and-menu}\n\nThe User Personal Bar is a portlet, but it's also an important concept\nin Liferay DXP. In a fresh bundle using the default theme, it's the\nsection of screen occupied by the User's avatar and the Personal Menu.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/user-personal-bar.png}\n\\caption{By default, the User Personal Bar contains the signed-in user's\navatar, which opens the Personal Menu when selected.}\n\\end{figure}\n\nThe User Personal Bar holds only the Personal Menu by default, but it\ncan also contain any functionality you want (even additional portlets).\nThe User Personal Bar is included by default in every Liferay theme, but\nyou can replace it with a\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-a-custom-portlet-in-place-of-the-user-personal-bar}{portlet}\nor customize it by adding entries to the existing portlet's menu.\n\nThis section covers these topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Replacing the default User Personal Bar portlet with a custom portlet.\n\\item\n  Customizing the default User Personal Bar.\n\\end{itemize}\n\n\\section{Displaying the Personal\nMenu}\\label{displaying-the-personal-menu}\n\nStarting with 7.0, the Personal Menu is no longer part of the Product\nMenu, but is instead included in the User Personal Bar. To display the\nexisting User Personal Bar in your own theme, embed the portlet into\nyour theme by adding the following snippet into\n\\texttt{portal\\_normal.ftl}:\n\n\\begin{verbatim}\n<@liferay.user_personal_bar />\n\\end{verbatim}\n\nYou'll use the same snippet even if you're replacing the default User\nPersonal Bar portlet with your own.\n\nIf you use a custom portlet to provide the User Personal Bar, but wish\nto include the default Personal Menu, make sure to render it by using\nthis tag in your portlet's JSP:\n\n\\begin{verbatim}\n<liferay-product-navigation:personal-menu\n    expanded=\"<%= true %>\"\n    label=\"<%= userAvatar %>\"\n/>\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The recommended way to display the Personal Menu is by\nembedding the User Personal Bar in a theme. If this is not practical, a\nworkaround exists: go to \\emph{Control Panel} → \\emph{Configuration} →\n\\emph{Instance Settings} → \\emph{Users} and select \\emph{Personal Menu}.\nEnable the \\emph{Show in Control Menu} toggle and click \\emph{Update}.\n\nThis places a button to expand the Personal Menu in the Control Menu. It\nappears on every site and page in your virtual instance, including sites\nthat have the User Personal Bar embedded in the theme. So, to avoid\nmultiple User Personal Bars appearing on the page, you should use only\n\\emph{one} of these mechanisms to display the User Personal Bar.\n\n\\noindent\\hrulefill\n\nUnlike the Product Menu, the Personal Menu can be customized without\ncreating panel categories and panel apps. See\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-personal-menu}{Customizing\nthe Personal Menu} for details.\n\n\\chapter{Using a Custom Portlet in Place of the User Personal\nBar}\\label{using-a-custom-portlet-in-place-of-the-user-personal-bar}\n\nIn this article, you'll learn how to write the single Java class\nrequired to replace the default User Personal Bar with a custom portlet.\nWriting the portlet itself is up to each developer's needs. See the\ndocumentation on\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{portlets} if you\nneed guidance.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  an OSGi module}.\n\\item\n  Create a unique package name in the module's \\texttt{src} directory\n  and create a new Java class in that package.\n\\item\n  Above the class declaration, insert the following annotation:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"model.class.name=\" + PortalUserPersonalBarApplicationType.UserPersonalBar.CLASS_NAME,\n        \"service.ranking:Integer=10\"\n    },\n    service = ViewPortletProvider.class\n)\n\\end{verbatim}\n\n  The \\texttt{model.class.name} property must be set to the class name\n  of the entity type you want the portlet to handle. In this case, you\n  want your portlet to be provided based on whether it can be displayed\n  in the User Personal Bar.\n\n  You may recall from the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes}{Portlet\n  Providers} articles that you can request portlets in several different\n  ways (e.g., \\emph{Edit}, \\emph{Browse}, etc.).\n\n  You should also specify the service rank for your new portlet so it\n  overrides the default. Make sure to set the\n  \\texttt{service.ranking:Integer} property to a number that is ranked\n  higher than the portlet being used by default.\n\n  Since you want to display your portlet instead of the User Personal\n  Bar, the \\texttt{service} element should be\n  \\texttt{ViewPortletProvider.class}.\n\\item\n  Update the class's declaration to extend the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BasePortletProvider.html}{\\texttt{BasePortletProvider}}\n  abstract class and implement \\texttt{ViewPortletProvider}:\n\n\\begin{verbatim}\npublic class ExampleViewPortletProvider extends BasePortletProvider implements ViewPortletProvider {\n\n}\n\\end{verbatim}\n\\item\n  Specify the portlet you want in the User Personal Bar by declaring the\n  following method in your class:\n\n\\begin{verbatim}\n@Override\npublic String getPortletName() {\n    return PORTLET_NAME;\n}\n\\end{verbatim}\n\n  Replace the \\texttt{PORTLET\\_NAME} text with the portlet to provide\n  when one is requested by the theme template. For example, the default\n  portlet uses\n  \\texttt{com\\_liferay\\_product\\_navigation\\_user\\_personal\\_bar\\_web\\_portlet\\_ProductNavigationPersonalBarPortlet}\n\\end{enumerate}\n\nIf you want to inspect the entire module used for Liferay's User\nPersonal Bar, see the\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.0-ga1/modules/apps/product-navigation/product-navigation-user-personal-bar-web}{product-navigation-user-personal-bar-web}\nmodule.\n\n\\section{Related Topics}\\label{related-topics-35}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu}{Customizing\n  the Product Menu}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-control-menu}{Customizing\n  the Control Menu}\n\\end{itemize}\n\n\\chapter{Customizing the Personal\nMenu}\\label{customizing-the-personal-menu}\n\nThe Personal Menu is a portlet in Liferay DXP, and is the only item\noccupying the User Personal Bar out of the box. You can add entries to\nthe Personal Menu by implementing the \\texttt{PersonalMenuEntry}\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/product-navigation/product-navigation-personal-menu-api/src/main/java/com/liferay/product/navigation/personal/menu/PersonalMenuEntry.java}{interface}.\nIf you're adding a portlet entry to the Personal Menu, the process is\nslightly different. Both approaches are covered below.\n\n\\section{Adding an Entry to the Personal\nMenu}\\label{adding-an-entry-to-the-personal-menu}\n\nFollow these steps. \\texttt{SignOutPersonalMenuEntry.java} is used as an\nexample throughout these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  an OSGi module} and place a new Java class into a package in its\n  \\texttt{src} folder.\n\\item\n  In the \\texttt{@Component} annotation, specify the two properties\n  shown below to place your new entry in the Personal Menu:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{product.navigation.personal.menu.group}: determines the\n    section where the entry will be placed.\n  \\item\n    \\texttt{product.navigation.personal.menu.entry.order}: determines\n    the order of entries within each section. Note that sections are not\n    labelled. To create a new section, assign the \\texttt{group}\n    property a value other than those for the four default sections\n    (100, 200, 300, and 400).\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/user-personal-menu-sections.png}\n  \\caption{The Personal Menu is organized into four sections.}\n  \\end{figure}\n\n  Here's an example:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n    \"product.navigation.personal.menu.group:Integer=400\",\n        \"product.navigation.personal.menu.entry.order:Integer=100\"\n    },\n    service = PersonalMenuEntry.class\n)\npublic class SignOutPersonalMenuEntry implements PersonalMenuEntry {\n\\end{verbatim}\n\\item\n  Include the interface's methods. \\texttt{SignoutPersonalMenuEntry}\n  uses \\texttt{getLabel} and \\texttt{getPortletURL}, which are the only\n  two that are mandatory. \\texttt{getLabel} retrieves a language key to\n  label the entry in the UI:\n\n\\begin{verbatim}\n    @Override\n    public String getLabel(Locale locale) {\n        return LanguageUtil.get(locale, \"sign-out\");\n    }\n\\end{verbatim}\n\n  \\texttt{getPortletURL} returns the URL for the portlet or page you\n  want to access with the entry:\n\n\\begin{verbatim}\n    public String getPortletURL(HttpServletRequest httpServletRequest)\n        throws PortalException {\n\n        ThemeDisplay themeDisplay =\n            (ThemeDisplay)httpServletRequest.getAttribute(\n                WebKeys.THEME_DISPLAY);\n\n        return themeDisplay.getURLSignOut();\n    }\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\nThat's all you need to implement the interface. However, the\n\\texttt{PersonalMenuEntry} interface includes a number of other methods\nthat you can use if you need them:\n\n\\texttt{getIcon}: identify an icon to display in the entry.\n\n\\texttt{isActive}: indicate whether the entry is currently active.\n\n\\texttt{isShow}: write logic to determine under what circumstances the\nentry is displayed.\n\nLearn how to add a portlet entry to the Personal Menu next.\n\n\\section{Adding a Portlet Entry to the Personal\nMenu}\\label{adding-a-portlet-entry-to-the-personal-menu}\n\nIf you're adding a portlet to the Personal Menu, you can extend the\n\\texttt{BasePersonalMenuEntry} class to save time. Follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  an OSGi module} and place a new Java class into a package in its\n  \\texttt{src} folder.\n\\item\n  In the \\texttt{@Component} annotation, specify the two properties\n  shown below to place your new entry in the Personal Menu:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{product.navigation.personal.menu.group}: determines the\n    section where the entry will be placed.\n  \\item\n    \\texttt{product.navigation.personal.menu.entry.order}: determines\n    the order of entries within each section. Note that sections are not\n    labelled. To create a new section, assign the \\texttt{group}\n    property a value other than those for the four default sections\n    (100, 200, 300, and 400).\n  \\end{itemize}\n\n  An example is shown below:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"product.navigation.personal.menu.entry.order:Integer=100\",\n        \"product.navigation.personal.menu.group:Integer=300\"\n    },\n    service = PersonalMenuEntry.class\n)\npublic class MyAccountPersonalMenuEntry extends BasePersonalMenuEntry {\n\\end{verbatim}\n\\item\n  Override the \\texttt{getPortletId()} method to provide the portlet's\n  ID, as shown in the example below:\n\n\\begin{verbatim}\npublic class MyAccountPersonalMenuEntry extends BasePersonalMenuEntry {\n\n    @Override\n    public String getPortletId() {\n        return UsersAdminPortletKeys.MY_ACCOUNT;\n    }\n\n}\n\\end{verbatim}\n\n  The \\texttt{BasePersonalMenuEntry} class automatically determines the\n  label, portlet URL, state, and visibility based on the portlet ID.\n\\end{enumerate}\n\nOnce you've completed your implementation and deployed your module, your\nnew entry is displayed in the personal menu.\n\n\\section{Related Topics}\\label{related-topics-36}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu}{Customizing\n  the Product Menu}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-control-menu}{Customizing\n  the Control Menu}\n\\end{itemize}\n\n\\chapter{Customizing Workflow}\\label{customizing-workflow}\n\nLiferay's workflow engine calls users to participate in a review process\ndesigned for them. Out of the box, workflow makes it possible to define\nsimple to complex business processes/workflows, deploy them, and manage\nthem through a portal interface.\n\nWorkflow is flexible, in that you can design workflow processes in XML\nto suit your business needs.\n\nIn Liferay DXP version 7.2, a new set of workflow features was\nintroduced around the concept of\n\\href{/docs/7-2/customization/-/knowledge_base/c/creating-sla-calendars}{Workflow\nMetrics}.\n\nThe embedded calendar that ships out of the box can be replaced by your\nown custom calendar service. More customization points will likely be\nadded in the future.\n\n\\chapter{Creating SLA Calendars}\\label{creating-sla-calendars}\n\nBy default, an internal calendar assumes the\n\\href{/docs/7-2/customization/-/knowledge_base/c/creating-sla-calendars}{SLA\ndeadline clock} should continue counting all the time: in other words,\n24 hours per day, seven days per week. If you need a different calendar\nformat, provide your own implementation of the\n\\texttt{WorkflowMetricsSLACalendar} interface. New implementations of\nthis service are picked up automatically by the Workflow Metrics\napplication, so they become available as soon as the module holding the\nservice implementation is deployed. The interface has three methods to\nimplement:\n\n\\begin{verbatim}\npublic interface WorkflowMetricsSLACalendar {\n\n    public Duration getDuration(\n        LocalDateTime startLocalDateTime, LocalDateTime endLocalDateTime);\n\n    public LocalDateTime getOverdueLocalDateTime(\n        LocalDateTime nowLocalDateTime, Duration remainingDuration);\n\n    public String getTitle(Locale locale);\n\n}\n\\end{verbatim}\n\nIf you define a new calendar, a new option becomes available in the Add\nSLA form, allowing you to choose from the default 24/7 calendar or any\ncustom ones you've provided. For example, you can make the timer run for\n8 hours per day, from 9-17 by a 24-hour clock, for 5 days per week. If\nyou need to, you can even stop the calendar from counting during lunch\nhours!\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-custom-sla-calendar.png}\n\\caption{Write a Custom SLA Calendar if the default, 24/7 calendar isn't\nsufficient.}\n\\end{figure}\n\n\\section{Dependencies}\\label{dependencies-1}\n\nAlong with some artifacts you're probably used to depending on (like\n\\texttt{com.liferay.portal.kernel}), you'll need the\n\\texttt{com.liferay.portal.workflow.metrics.sla.api-{[}version{]}.jar}\nartifact. For Liferay DXP version 7.2.10-GA1, here's an example Gradle\nbuild dependency declaration:\n\n\\begin{verbatim}\ncompileOnly group: \"com.liferay\", name: \"com.liferay.portal.workflow.metrics.sla.api\", version: \"1.1.0\"\ncompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"4.4.0\"\ncompileOnly group: \"javax.servlet\", name: \"javax.servlet-api\", version: \"3.0.1\"\ncompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\", version: \"1.3.0\"\n\\end{verbatim}\n\n\\section{Implementation Steps}\\label{implementation-steps}\n\nImplement a\n\\texttt{com.liferay.portal.workflow.metrics.sla.calendar.WorkflowMetricsSLACalendar}\nto define your own SLA calendar logic. When you're finished, use the\ncreated calendar when creating the\n\\href{/docs/7-2/customization/-/knowledge_base/c/creating-sla-calendars}{SLA\ndefinition}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Declare the component and the class:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.language.Language;\nimport com.liferay.portal.workflow.metrics.sla.calendar.WorkflowMetricsSLACalendar;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\n\n@Component(property = \"sla.calendar.key=default\")\npublic class DefaultWorkflowMetricsSLACalendar\n    implements WorkflowMetricsSLACalendar {\n\\end{verbatim}\n\n  The component property \\texttt{sla.calendar.key} is required to\n  identify this calendar.\n\\item\n  Override \\texttt{getDuration} to return the time \\texttt{Duration}\n  when elapsed SLA time should be computed. The start and end dates that\n  this method receives represent the time a workflow task has been\n  running. For example, given a task that started at\n  \\emph{2019-05-13T16:00:00} and finished at \\emph{2019-05-13T18:00:00},\n  then The 24/7 calendar returns 2 elapsed hours, while a 9-17 weekdays\n  calendar returns 1 hour as the elapsed time.\n\n\\begin{verbatim}\n@Override\npublic Duration getDuration(\n    LocalDateTime startLocalDateTime, LocalDateTime endLocalDateTime) {\n\n    return Duration.between(startLocalDateTime, endLocalDateTime);\n}\n\\end{verbatim}\n\\item\n  \\texttt{getOverdueLocalDateTime} must return the date (as a\n  \\texttt{LocalDateTime}) when this SLA is considered overdue given the\n  parameter values. For example, given that\n  \\texttt{nowLocalDateTime}=\\emph{2019-05-13T17:00:00} and\n  \\texttt{remainingDuration}=\\emph{24H}, The 24/7 calendar returns a\n  \\texttt{localDateTime} of \\emph{2019-05-14T17:00:00} as the overdue\n  date. Given the same parameters, the 9-17 weekdays calendar should\n  return \\emph{2019-05-17T09:00:00}. The remaining duration of time left\n  in the SLA is available in the method as a \\texttt{Duration} object;\n  your job is to write logic that considers your calendar and create a\n  \\texttt{LocalDateTime} with the proper overdue date/time.\n\n\\begin{verbatim}\n@Override\npublic LocalDateTime getOverdueLocalDateTime(\n    LocalDateTime nowLocalDateTime, Duration remainingDuration) {\n\n    return nowLocalDateTime.plus(remainingDuration);\n}\n\\end{verbatim}\n\\item\n  Use \\texttt{getTitle} to provide the title for the given locale. Make\n  sure you\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application}{properly\n  localize} this extension by providing a \\texttt{Language.properties}\n  file and any \\texttt{Language\\_xx.properties} files for translation of\n  the value. At runtime, the User's locale is used to return the correct\n  translation.\n\n\\begin{verbatim}\n@Override\npublic String getTitle(Locale locale) {\n    return _language.get(locale, \"default\");\n}\n\n@Reference\nprivate Language _language;\n\\end{verbatim}\n\\end{enumerate}\n\nIf the 24/7 default calendar works for you, use it. Otherwise create\nyour own \\texttt{WorkflowMetricsSLACalendar}s.\n\n\\chapter{Customizing Core Functionality with\nExt}\\label{customizing-core-functionality-with-ext}\n\n\\noindent\\hrulefill\n\n\\textbf{Ext plugins are deprecated for 7.0 and should only be used if\nabsolutely necessary.}\n\nThe following app servers should be used for Ext plugin development in\nLiferay DXP:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Tomcat 9.x\n\\end{itemize}\n\nIn most cases, Ext plugins are not necessary. There are, however,\ncertain cases that require the use of an Ext plugin. Liferay only\nsupports the following Ext plugin use cases:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Providing custom implementations for any beans declared in Liferay\n  DXP's Spring files (when possible, use\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers}{service\n  wrappers} instead of an Ext plugin). 7.0 removed many beans, so make\n  sure your overridden beans are still relevant if converting your\n  legacy Ext plugin\n  (\\href{/docs/7-2/customization/-/knowledge_base/c/extending-core-classes-using-spring-with-ext-plugins}{how\n  to}).\n\\item\n  Overwriting a class in a 7.0 core JAR. For a list of core JARs, see\n  the\n  \\href{/docs/7-2/customization/-/knowledge_base/c/finding-artifacts\\#finding-core-artifact-attributes}{Finding\n  Core Liferay DXP Artifacts} section\n  (\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-core-classes-with-ext-plugins}{how\n  to}).\n\\item\n  Modifying Liferay DXP's \\texttt{web.xml} file\n  (\\href{/docs/7-2/customization/-/knowledge_base/c/modifying-the-web-xml-with-ext-plugins}{how\n  to}).\n\\item\n  Adding to Liferay DXP's \\texttt{web.xml} file\n  (\\href{/docs/7-2/customization/-/knowledge_base/c/adding-to-the-web-xml-with-ext-plugins}{how\n  to}).\n\\end{itemize}\n\n\\textbf{Note:} In previous versions of Liferay Portal, you needed an Ext\nplugin to specify classes as portal property values (e.g.,\n\\texttt{global.starup.events.my.custom.MyStartupAction}), since the\ncustom class had to be added to the portal class loader. This is no\nlonger the case in 7.0 since all lifecycle events can use OSGi services\nwith no need to edit these legacy properties.\n\n\\noindent\\hrulefill\n\nExt plugins are used to customize Liferay DXP's core functionality. You\ncan learn more about what the core encompasses in the\n\\href{/docs/7-2/customization/-/knowledge_base/c/finding-artifacts\\#finding-core-artifact-attributes}{Finding\nCore Liferay DXP Artifacts} article section. In this section, you'll\nlearn how to\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/creating-an-ext-plugin}{Create\n  an Ext plugin}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/developing-an-ext-plugin}{Develop\n  an Ext plugin}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/deploying-an-ext-plugin}{Deploy\n  an Ext plugin}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/redeploying-an-ext-plugin}{Redeploy\n  an Ext plugin}\n\\end{itemize}\n\nYou can also dive into the\n\\href{/docs/7-2/customization/-/knowledge_base/c/anatomy-of-an-ext-plugin}{Anatomy\nof an Ext Plugin} to familiarize yourself with its structure.\n\nYou'll start by creating an Ext plugin.\n\n\\chapter{Extending Core Classes Using Spring with Ext\nPlugins}\\label{extending-core-classes-using-spring-with-ext-plugins}\n\nA supported use case for using Ext plugins in Liferay DXP is extending\nits core classes (e.g., \\texttt{portal-impl}, \\texttt{portal-kernel},\netc.) using Spring. You can reference the\n\\href{/docs/7-2/customization/-/knowledge_base/c/finding-artifacts\\#finding-core-artifact-attributes}{Finding\nCore Liferay Portal Artifacts} section for help distinguishing core\nclasses. Make sure you've reviewed the generalized\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-core-functionality-with-ext}{Customization\nwith Ext Plugins} section before creating an Ext plugin.\n\nAs an example, you'll create a sample Ext plugin that extends the\n\\href{https://docs.liferay.com/ce/portal/7.2-latest/javadocs/portal-impl/com/liferay/portal/util/PortalImpl.html}{PortalImpl}\ncore class residing in the \\texttt{portal-impl.jar}. You'll override the\n\\texttt{PortalImpl.getComputerName()} method via Spring bean, which\nreturns your server's node name. The Ext plugin will override this\nmethod and modify the server's returned node name.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to your Liferay Workspace's root folder and run the following\n  command:\n\n\\begin{verbatim}\nblade create -t war-core-ext portal-impl-extend-spring-ext\n\\end{verbatim}\n\n  Your Ext plugin is generated and now resides in the workspace's\n  \\texttt{/ext} folder with the name you assigned.\n\\item\n  Displaying the server node name in your Liferay DXP installation is\n  set to \\texttt{false} by default. You'll need to enable this property.\n  To do this, navigate into your Liferay bundle's root folder and create\n  a \\texttt{portal-ext.properties} file. In that file, insert the\n  following property:\n\n\\begin{verbatim}\nweb.server.display.node=true\n\\end{verbatim}\n\n  Now your server's node name will be displayed once your Liferay bundle\n  is restarted.\n\\item\n  In the \\texttt{/extImpl/java} folder, create the folder structure\n  representing the package name you want your new class to reside in\n  (e.g., \\texttt{com/liferay/portal/util}). Then create your new Java\n  class:\n\n\\begin{verbatim}\npackage com.liferay.portal.util;\n\npublic class SamplePortalImpl extends PortalImpl {\n\n    @Override\n    public String getComputerName() {\n        return \"SAMPLE_EXT_INSTALLED_\" + super.getComputerName();\n    }\n}\n\\end{verbatim}\n\\end{enumerate}\n\nThe method defined in the extension class overrides the\n\\texttt{PortalImpl.getComputerName()} method. The\n\\texttt{\"SAMPLE\\_EXT\\_INSTALLED\\_\"} String is now prefixed to your\nserver's node name.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  In your Ext plugin's \\texttt{/extImpl/resources} folder, create a\n  \\texttt{META-INF/ext-spring.xml} file. In this file, insert the\n  following code:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<beans\n    default-destroy-method=\"destroy\"\n    default-init-method=\"afterPropertiesSet\"\n    xmlns=\"http://www.springframework.org/schema/beans\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\"\n>\n\n    <bean class=\"com.liferay.portal.util.SamplePortalImpl\" id=\"com.liferay.portal.util.PortalImpl\" />\n</beans>\n\\end{verbatim}\n\\end{enumerate}\n\nSince you plan on modifying a core service class, you can inject its\nextension class via a Spring bean. This will ensure your new class is\nrecognized. Assign your extension class's fully defined class name\n(e.g., \\texttt{com.liferay.portal.util.SamplePortalImpl}) to the bean\ntag's \\texttt{class} attribute and the fully defined original class name\n(e.g., \\texttt{com.liferay.portal.util.PortalImpl}) to the bean tag's\n\\texttt{id} attribute.\n\nWhen your Ext plugin is deployed, your new service (e.g.,\n\\texttt{SamplePortalImpl}) will extend the core \\texttt{PortalImpl}\nclass.\n\nAwesome! You've created an Ext plugin that extends a core class in\nLiferay DXP! Follow the instructions in the\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-an-ext-plugin}{Deploy\nthe Plugin} article to deploy it to your server.\n\n\\chapter{Overriding Core Classes with Ext\nPlugins}\\label{overriding-core-classes-with-ext-plugins}\n\nA supported use case for using Ext plugins in Liferay DXP is overriding\nits core classes (e.g., \\texttt{portal-impl}, \\texttt{portal-kernel},\netc.). You can reference the\n\\href{/docs/7-2/customization/-/knowledge_base/c/finding-artifacts\\#finding-core-artifact-attributes}{Finding\nCore Liferay Portal Artifacts} section for help distinguishing core\nclasses. Make sure you've reviewed the generalized\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-core-functionality-with-ext}{Customization\nwith Ext Plugins} section before creating an Ext plugin.\n\nAs an example, you'll create a sample Ext plugin that overwrites the\n\\href{https://docs.liferay.com/ce/portal/7.2-latest/javadocs/portal-impl/com/liferay/portal/util/PortalImpl.html}{PortalImpl}\ncore class residing in the \\texttt{portal-impl.jar}. You'll edit the\n\\texttt{PortalImpl.getComputerName()} method, which returns your\nserver's node name. The Ext plugin will override the entire\n\\texttt{PortalImpl} class, adding the method modifying the server's\nreturned node name.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to your Liferay Workspace's root folder and run the following\n  command:\n\n\\begin{verbatim}\nblade create -t war-core-ext portal-impl-override\n\\end{verbatim}\n\n  Your Ext plugin is generated and now resides in the workspace's\n  \\texttt{/ext} folder with the name you assigned.\n\\item\n  Displaying the server node name in your Liferay DXP installation is\n  set to \\texttt{false} by default. You'll need to enable this property.\n  To do this, navigate into your Liferay bundle's root folder and create\n  a \\texttt{portal-ext.properties} file. In that file, insert the\n  following property:\n\n\\begin{verbatim}\nweb.server.display.node=true\n\\end{verbatim}\n\n  Now your server's node name will be displayed once your Liferay bundle\n  is restarted.\n\\item\n  In the \\texttt{/extImpl/java} folder, create the folder structure\n  matching the class's folder structure you'd like to override (e.g.,\n  \\texttt{com/liferay/portal/util}). Then create the new Java class that\n  will override the existing core class; your new class must have the\n  same name as the original.\n\\item\n  Copy all of the original class's (e.g., \\texttt{PortalImpl}) logic\n  into your new class. Then modify the method you want to customize. For\n  this example, you want to edit the \\texttt{getComputerName()} method.\n  Therefore, replace it with the method below:\n\n\\begin{verbatim}\n@Override\npublic String getComputerName() {\n    return \"sample_portalimpl_ext_installed_successfully_\" + _computerName;\n}\n\\end{verbatim}\n\n  The method defined in the new class overrides the\n  \\texttt{PortalImpl.getComputerName()} method. The\n  \\texttt{sample\\_portalimpl\\_ext\\_installed\\_successfully\\_} String is\n  now prefixed to your server's node name.\n\\end{enumerate}\n\nWhen your Ext plugin is deployed, your new Java class will override the\ncore \\texttt{PortalImpl} class.\n\nAwesome! You've created an Ext plugin that overrides a core class in\nLiferay DXP! Follow the instructions in the\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-an-ext-plugin}{Deploy\nthe Plugin} article to deploy it to your server.\n\n\\chapter{Adding to the web.xml with Ext\nPlugins}\\label{adding-to-the-web.xml-with-ext-plugins}\n\nA supported use case for using Ext Plugins in Liferay DXP is adding\nadditional functionality to its \\texttt{web.xml} file. Before beginning,\nmake sure you've reviewed the generalized\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-core-functionality-with-ext}{Customization\nwith Ext Plugins} section.\n\nAs an example, you'll create a sample Ext plugin that adds to your\nLiferay DXP's existing \\texttt{web.xml} file (e.g., in the\n\\texttt{/tomcat-{[}version{]}/webapps/ROOT/WEB-INF} folder). You'll add\na new printout in the console during startup.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to your Liferay Workspace's root folder and run the following\n  command:\n\n\\begin{verbatim}\nblade create -t war-core-ext add-printout\n\\end{verbatim}\n\n  Your Ext plugin is generated and now resides in the workspace's\n  \\texttt{/ext} folder with the name you assigned.\n\\item\n  For your Liferay DXP installation to recognize new functionality in\n  the \\texttt{web.xml}, you must create a class that implements the\n  \\href{https://javaee.github.io/javaee-spec/javadocs/javax/servlet/ServletContextListener.html}{ServletContextListener}\n  interface. This class will initialize a servlet context event for\n  which you'll add your new functionality. In the \\texttt{extImpl/java}\n  folder, create the folder structure representing the package name you\n  want your new class to reside in (e.g.,\n  \\texttt{com/liferay/portal/servlet/context}). Then create your new\n  Java class:\n\n\\begin{verbatim}\npackage com.liferay.portal.servlet.context;\n\nimport javax.servlet.ServletContextEvent;\nimport javax.servlet.ServletContextListener;\n\npublic class ExtAddEntryWebXmlPortalContextLoaderListener\n        implements ServletContextListener {\n\n    public void contextDestroyed(ServletContextEvent servletContextEvent) {\n    }\n\n    public void contextInitialized(ServletContextEvent servletContextEvent) {\n        System.out.println(\"EXT_ADD_ENTRY_WEBXML_INSTALLED_SUCCESSFULLY\");\n    }\n}\n\\end{verbatim}\n\n  The above class includes two methods that initialize and destroy your\n  servlet context event. Be sure to add the new \\texttt{web.xml}'s\n  functionality when the portal context is initializing. To add a\n  printout verifying the Ext plugins installation, a simple print\n  statement was defined in the \\texttt{contextInitialized(...)} method:\n\n\\begin{verbatim}\nSystem.out.println(\"EXT_ADD_ENTRY_WEBXML_INSTALLED_SUCCESSFULLY\");\n\\end{verbatim}\n\\item\n  Now that you've defined a servlet context event, you should add a\n  listener to your \\texttt{web.xml} that listens for it. In the\n  \\texttt{ext-web/docroot/WEB-INF} folder, open the \\texttt{web.xml}\n  file, which was generated for you by default.\n\\item\n  Add the following tag between the tags:\n\n\\begin{verbatim}\n<listener>\n    <listener-class>com.liferay.portal.servlet.context.ExtAddEntryWebXmlPortalContextLoaderListener</listener-class>\n</listener>\n\\end{verbatim}\n\\end{enumerate}\n\nExcellent! Now when your Ext plugin is deployed, your Liferay DXP\ninstallation will create a \\texttt{ServletContextListener} instance,\nwhich will initialize a custom servlet context event. This event will be\nrecognized by the \\texttt{web.xml} file, which will add the new\nfunctionality to your Liferay DXP installation. Follow the instructions\nin the\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-an-ext-plugin}{Deploy\nthe Plugin} article for help deploying the Ext plugin to your server.\n\n\\chapter{Modifying the web.xml with Ext\nPlugins}\\label{modifying-the-web.xml-with-ext-plugins}\n\nA supported use case for using Ext Plugins in Liferay DXP is modifying\nits \\texttt{web.xml} file. Before beginning, make sure you've reviewed\nthe generalized\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-core-functionality-with-ext}{Customization\nwith Ext Plugins} section.\n\nAs an example, you'll create a sample Ext plugin that modifies Liferay\nDXP's existing \\texttt{web.xml\\ file} (e.g., in the\n\\texttt{/tomcat-{[}version{]}/webapps/ROOT/WEB-INF} folder). You'll\nmodify the session timeout configuration, which is set to 30 (minutes)\nby default:\n\n\\begin{verbatim}\n<session-config>\n    <session-timeout>30</session-timeout>\n    <cookie-config>\n        <http-only>true</http-only>\n    </cookie-config>\n</session-config>\n\\end{verbatim}\n\nThe Ext plugin will update the session timeout to one minute.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate into your Liferay Workspace's \\texttt{/ext} folder and run\n  the following command:\n\n\\begin{verbatim}\nblade create -t war-core-ext modify-session-timeout\n\\end{verbatim}\n\n  Your Ext plugin is generated and now resides in the workspace's\n  \\texttt{/ext} folder with the name you assigned.\n\\item\n  In the \\texttt{ext-web/docroot/WEB-INF} folder, open the\n  \\texttt{web.xml} file, which was generated for you by default.\n\\item\n  Insert the following logic between the\n  \\texttt{\\textless{}web-app\\textgreater{}} tags:\n\n\\begin{verbatim}\n<session-config>\n    <session-timeout>1</session-timeout>\n    <cookie-config>\n        <http-only>true</http-only>\n    </cookie-config>\n</session-config>\n\\end{verbatim}\n\\end{enumerate}\n\nNotice that the \\texttt{\\textless{}session-timeout\\textgreater{}} tag\nhas been updated to \\texttt{1}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can configure an uninterrupted session by setting the\n\\texttt{\\textless{}session-timeout\\textgreater{}} tag to \\texttt{-1}.\nLeaving a session permanently active is a risk and is not recommended\nfor production environments, but is useful for testing.\n\n\\noindent\\hrulefill\n\nThat's it! Now when your Ext plugin is deployed, your Liferay DXP\ninstallation will timeout after one minute of inactivity. Follow the\ninstructions in the\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-an-ext-plugin}{Deploy\nthe Plugin} article for help deploying the Ext plugin to your server.\n"
  },
  {
    "path": "book/developer/frameworks.aux",
    "content": "\\relax \n\\providecommand{\\transparent@use}[1]{}\n\\providecommand\\hyper@newdestlabel[2]{}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {296}Application Security}{873}{chapter.296}\\protected@file@percent }\n\\newlabel{application-security}{{296}{873}{Application Security}{chapter.296}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {297}Defining Application Permissions}{875}{chapter.297}\\protected@file@percent }\n\\newlabel{defining-application-permissions}{{297}{875}{Defining Application Permissions}{chapter.297}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {298}Defining Resources and Permissions}{877}{chapter.298}\\protected@file@percent }\n\\newlabel{defining-resources-and-permissions}{{298}{877}{Defining Resources and Permissions}{chapter.298}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {298.1}Defining Portlet Resource Permissions}{877}{section.298.1}\\protected@file@percent }\n\\newlabel{defining-portlet-resource-permissions}{{298.1}{877}{Defining Portlet Resource Permissions}{section.298.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {298.2}Defining Model Resource Permissions}{879}{section.298.2}\\protected@file@percent }\n\\newlabel{defining-model-resource-permissions}{{298.2}{879}{Defining Model Resource Permissions}{section.298.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {298.3}Enabling Your Permissions Configuration}{880}{section.298.3}\\protected@file@percent }\n\\newlabel{enabling-your-permissions-configuration}{{298.3}{880}{Enabling Your Permissions Configuration}{section.298.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {299}Registering Permissions}{881}{chapter.299}\\protected@file@percent }\n\\newlabel{registering-permissions}{{299}{881}{Registering Permissions}{chapter.299}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {299.1}Registering Permissions Resources in the Database}{881}{section.299.1}\\protected@file@percent }\n\\newlabel{registering-permissions-resources-in-the-database}{{299.1}{881}{Registering Permissions Resources in the Database}{section.299.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {299.2}Registering Entities to the Permissions Service}{882}{section.299.2}\\protected@file@percent }\n\\newlabel{registering-entities-to-the-permissions-service}{{299.2}{882}{Registering Entities to the Permissions Service}{section.299.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {300}Associating Permissions with Resources}{885}{chapter.300}\\protected@file@percent }\n\\newlabel{associating-permissions-with-resources}{{300}{885}{Associating Permissions with Resources}{chapter.300}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {301}Checking Permissions}{887}{chapter.301}\\protected@file@percent }\n\\newlabel{checking-permissions}{{301}{887}{Checking Permissions}{chapter.301}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {301.1}Add Permission Checks to Your Service Calls}{887}{section.301.1}\\protected@file@percent }\n\\newlabel{add-permission-checks-to-your-service-calls}{{301.1}{887}{Add Permission Checks to Your Service Calls}{section.301.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {301.2}Create Permission Helper Classes in Your Web Module}{888}{section.301.2}\\protected@file@percent }\n\\newlabel{create-permission-helper-classes-in-your-web-module}{{301.2}{888}{Create Permission Helper Classes in Your Web Module}{section.301.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {301.3}Add Permission Checks to Your Web Application}{890}{section.301.3}\\protected@file@percent }\n\\newlabel{add-permission-checks-to-your-web-application}{{301.3}{890}{Add Permission Checks to Your Web Application}{section.301.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {302}Using JSR Roles in a Portlet}{893}{chapter.302}\\protected@file@percent }\n\\newlabel{using-jsr-roles-in-a-portlet}{{302}{893}{Using JSR Roles in a Portlet}{chapter.302}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {302.1}JSR Portlet Security}{893}{section.302.1}\\protected@file@percent }\n\\newlabel{jsr-portlet-security}{{302.1}{893}{JSR Portlet Security}{section.302.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {302.2}Mapping Portlet Roles to Portal Roles}{894}{section.302.2}\\protected@file@percent }\n\\newlabel{mapping-portlet-roles-to-portal-roles}{{302.2}{894}{Mapping Portlet Roles to Portal Roles}{section.302.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {302.3}Related Topics}{895}{section.302.3}\\protected@file@percent }\n\\newlabel{related-topics}{{302.3}{895}{Related Topics}{section.302.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {303}Authentication Pipelines}{897}{chapter.303}\\protected@file@percent }\n\\newlabel{authentication-pipelines}{{303}{897}{Authentication Pipelines}{chapter.303}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {304}Auto Login}{899}{chapter.304}\\protected@file@percent }\n\\newlabel{auto-login}{{304}{899}{Auto Login}{chapter.304}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {304.1}Creating an Auto Login Component}{899}{section.304.1}\\protected@file@percent }\n\\newlabel{creating-an-auto-login-component}{{304.1}{899}{Creating an Auto Login Component}{section.304.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {304.2}Related Topics}{900}{section.304.2}\\protected@file@percent }\n\\newlabel{related-topics-1}{{304.2}{900}{Related Topics}{section.304.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {305}Password-Based Authentication Pipelines}{901}{chapter.305}\\protected@file@percent }\n\\newlabel{password-based-authentication-pipelines}{{305}{901}{Password-Based Authentication Pipelines}{chapter.305}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {305.1}Anatomy of an Authenticator}{901}{section.305.1}\\protected@file@percent }\n\\newlabel{anatomy-of-an-authenticator}{{305.1}{901}{Anatomy of an Authenticator}{section.305.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {305.2}Creating an Authenticator}{902}{section.305.2}\\protected@file@percent }\n\\newlabel{creating-an-authenticator}{{305.2}{902}{Creating an Authenticator}{section.305.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {305.1}{\\ignorespaces The Authenticator module contains the validator's interface and the authenticator.}}{903}{figure.305.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {305.2}{\\ignorespaces The validator project implements the Validator Interface and depends on the authenticator module.}}{906}{figure.305.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {305.3}Related Topics}{907}{section.305.3}\\protected@file@percent }\n\\newlabel{related-topics-2}{{305.3}{907}{Related Topics}{section.305.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {306}Writing a Custom Login Portlet}{909}{chapter.306}\\protected@file@percent }\n\\newlabel{writing-a-custom-login-portlet}{{306}{909}{Writing a Custom Login Portlet}{chapter.306}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {306.1}Authenticating to Liferay DXP}{909}{section.306.1}\\protected@file@percent }\n\\newlabel{authenticating-to-liferay-dxp}{{306.1}{909}{Authenticating to Liferay DXP}{section.306.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {306.2}Related Topics}{911}{section.306.2}\\protected@file@percent }\n\\newlabel{related-topics-3}{{306.2}{911}{Related Topics}{section.306.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {307}Service Access Policies}{913}{chapter.307}\\protected@file@percent }\n\\newlabel{service-access-policies}{{307}{913}{Service Access Policies}{chapter.307}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {307.1}How Service Access Policies Work}{913}{section.307.1}\\protected@file@percent }\n\\newlabel{how-service-access-policies-work}{{307.1}{913}{How Service Access Policies Work}{section.307.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {307.1}{\\ignorespaces The authorization module maps the credentials or token to the proper Service Access Policy.}}{914}{figure.307.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {307.2}API Overview}{915}{section.307.2}\\protected@file@percent }\n\\newlabel{api-overview}{{307.2}{915}{API Overview}{section.307.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {307.3}Service Access Policy Example}{916}{section.307.3}\\protected@file@percent }\n\\newlabel{service-access-policy-example}{{307.3}{916}{Service Access Policy Example}{section.307.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {308}Frameworks}{919}{chapter.308}\\protected@file@percent }\n\\newlabel{frameworks}{{308}{919}{Frameworks}{chapter.308}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {309}Asset Framework}{921}{chapter.309}\\protected@file@percent }\n\\newlabel{asset-framework}{{309}{921}{Asset Framework}{chapter.309}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {309.1}Persistence Operations for Assets}{921}{section.309.1}\\protected@file@percent }\n\\newlabel{persistence-operations-for-assets}{{309.1}{921}{Persistence Operations for Assets}{section.309.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {309.2}Rendering an Asset}{922}{section.309.2}\\protected@file@percent }\n\\newlabel{rendering-an-asset}{{309.2}{922}{Rendering an Asset}{section.309.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {309.3}Asset Features}{922}{section.309.3}\\protected@file@percent }\n\\newlabel{asset-features}{{309.3}{922}{Asset Features}{section.309.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {309.4}Tags and Categories}{923}{section.309.4}\\protected@file@percent }\n\\newlabel{tags-and-categories}{{309.4}{923}{Tags and Categories}{section.309.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {309.1}{\\ignorespaces Adding category and tag input options lets authors aggregate and label custom entities.}}{923}{figure.309.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {309.5}Relating Assets}{923}{section.309.5}\\protected@file@percent }\n\\newlabel{relating-assets}{{309.5}{923}{Relating Assets}{section.309.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {309.2}{\\ignorespaces You and your users can find it helpful to relate assets to entities, such as this blogs entry.}}{923}{figure.309.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {309.6}Implementing Asset Priority}{924}{section.309.6}\\protected@file@percent }\n\\newlabel{implementing-asset-priority}{{309.6}{924}{Implementing Asset Priority}{section.309.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {309.3}{\\ignorespaces The Priority field lets users set an asset's priority.}}{924}{figure.309.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {310}Adding, Updating, and Deleting Assets}{925}{chapter.310}\\protected@file@percent }\n\\newlabel{adding-updating-and-deleting-assets}{{310}{925}{Adding, Updating, and Deleting Assets}{chapter.310}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {310.1}Preparing Your Project for the Asset Framework}{925}{section.310.1}\\protected@file@percent }\n\\newlabel{preparing-your-project-for-the-asset-framework}{{310.1}{925}{Preparing Your Project for the Asset Framework}{section.310.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {310.2}Adding and Updating Assets}{925}{section.310.2}\\protected@file@percent }\n\\newlabel{adding-and-updating-assets}{{310.2}{925}{Adding and Updating Assets}{section.310.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {310.3}Deleting Assets}{927}{section.310.3}\\protected@file@percent }\n\\newlabel{deleting-assets}{{310.3}{927}{Deleting Assets}{section.310.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {310.1}{\\ignorespaces It can be useful to show custom entities, like this wiki page entity, in a JSP or in an Asset Publisher.}}{928}{figure.310.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {311}Creating an Asset Renderer}{929}{chapter.311}\\protected@file@percent }\n\\newlabel{creating-an-asset-renderer}{{311}{929}{Creating an Asset Renderer}{chapter.311}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {311.1}{\\ignorespaces Enable printing in the Asset Publisher to display the Print icon for your asset.}}{933}{figure.311.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {312}Configuring JSP Templates for an Asset Renderer}{935}{chapter.312}\\protected@file@percent }\n\\newlabel{configuring-jsp-templates-for-an-asset-renderer}{{312}{935}{Configuring JSP Templates for an Asset Renderer}{chapter.312}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {312.1}{\\ignorespaces The abstract and full content views are rendered differently for blogs.}}{936}{figure.312.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {312.2}{\\ignorespaces The \\texttt  {preview} template displays a preview of the asset in the Content section of the Add menu.}}{938}{figure.312.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {313}Creating a Factory for the Asset Renderer}{939}{chapter.313}\\protected@file@percent }\n\\newlabel{creating-a-factory-for-the-asset-renderer}{{313}{939}{Creating a Factory for the Asset Renderer}{chapter.313}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {314}Implementing Asset Categorization and Tagging}{945}{chapter.314}\\protected@file@percent }\n\\newlabel{implementing-asset-categorization-and-tagging}{{314}{945}{Implementing Asset Categorization and Tagging}{chapter.314}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {314.1}Adding Tags and Categories}{945}{section.314.1}\\protected@file@percent }\n\\newlabel{adding-tags-and-categories}{{314.1}{945}{Adding Tags and Categories}{section.314.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {314.2}Displaying Tags and Categories}{945}{section.314.2}\\protected@file@percent }\n\\newlabel{displaying-tags-and-categories}{{314.2}{945}{Displaying Tags and Categories}{section.314.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {315}Relating Assets}{947}{chapter.315}\\protected@file@percent }\n\\newlabel{relating-assets-1}{{315}{947}{Relating Assets}{chapter.315}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {315.1}Relating Assets in the Service Layer}{947}{section.315.1}\\protected@file@percent }\n\\newlabel{relating-assets-in-the-service-layer}{{315.1}{947}{Relating Assets in the Service Layer}{section.315.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {315.2}Relating Assets in the UI}{948}{section.315.2}\\protected@file@percent }\n\\newlabel{relating-assets-in-the-ui}{{315.2}{948}{Relating Assets in the UI}{section.315.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {315.1}{\\ignorespaces Your portlet's entity is now available in the Related Assets \\emph  {Select} menu.}}{948}{figure.315.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {315.3}Showing Related Assets}{949}{section.315.3}\\protected@file@percent }\n\\newlabel{showing-related-assets}{{315.3}{949}{Showing Related Assets}{section.315.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {316}Implementing Asset Priority}{951}{chapter.316}\\protected@file@percent }\n\\newlabel{implementing-asset-priority-1}{{316}{951}{Implementing Asset Priority}{chapter.316}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {316.1}Add the Priority Field to Your JSP}{951}{section.316.1}\\protected@file@percent }\n\\newlabel{add-the-priority-field-to-your-jsp}{{316.1}{951}{Add the Priority Field to Your JSP}{section.316.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {316.2}Using the Priority Value in Your Service Layer}{951}{section.316.2}\\protected@file@percent }\n\\newlabel{using-the-priority-value-in-your-service-layer}{{316.2}{951}{Using the Priority Value in Your Service Layer}{section.316.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {317}Back-end Frameworks}{953}{chapter.317}\\protected@file@percent }\n\\newlabel{back-end-frameworks}{{317}{953}{Back-end Frameworks}{chapter.317}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {317.1}Portlet Providers}{953}{section.317.1}\\protected@file@percent }\n\\newlabel{portlet-providers}{{317.1}{953}{Portlet Providers}{section.317.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {317.2}Portlet Provider Classes}{953}{section.317.2}\\protected@file@percent }\n\\newlabel{portlet-provider-classes}{{317.2}{953}{Portlet Provider Classes}{section.317.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {317.3}Data Scopes}{954}{section.317.3}\\protected@file@percent }\n\\newlabel{data-scopes}{{317.3}{954}{Data Scopes}{section.317.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {317.4}Accessing the Site Scope Across Apps}{954}{section.317.4}\\protected@file@percent }\n\\newlabel{accessing-the-site-scope-across-apps}{{317.4}{954}{Accessing the Site Scope Across Apps}{section.317.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {317.5}Message Bus}{955}{section.317.5}\\protected@file@percent }\n\\newlabel{message-bus}{{317.5}{955}{Message Bus}{section.317.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {317.1}{\\ignorespaces JConsole shows statistics on Message Bus messages sent, messages pending, and more.}}{956}{figure.317.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {318}Creating Portlet Providers}{957}{chapter.318}\\protected@file@percent }\n\\newlabel{creating-portlet-providers}{{318}{957}{Creating Portlet Providers}{chapter.318}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {318.1}Related Topics}{958}{section.318.1}\\protected@file@percent }\n\\newlabel{related-topics-4}{{318.1}{958}{Related Topics}{section.318.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {319}Retrieving Portlets}{959}{chapter.319}\\protected@file@percent }\n\\newlabel{retrieving-portlets}{{319}{959}{Retrieving Portlets}{chapter.319}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {319.1}Fetching a Portlet ID}{959}{section.319.1}\\protected@file@percent }\n\\newlabel{fetching-a-portlet-id}{{319.1}{959}{Fetching a Portlet ID}{section.319.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {319.2}Fetching a Portlet URL}{960}{section.319.2}\\protected@file@percent }\n\\newlabel{fetching-a-portlet-url}{{319.2}{960}{Fetching a Portlet URL}{section.319.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {319.3}Related Topics}{961}{section.319.3}\\protected@file@percent }\n\\newlabel{related-topics-5}{{319.3}{961}{Related Topics}{section.319.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {320}Enabling and Accessing Data Scopes}{963}{chapter.320}\\protected@file@percent }\n\\newlabel{enabling-and-accessing-data-scopes}{{320}{963}{Enabling and Accessing Data Scopes}{chapter.320}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {320.1}Enabling Scoping}{963}{section.320.1}\\protected@file@percent }\n\\newlabel{enabling-scoping}{{320.1}{963}{Enabling Scoping}{section.320.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {320.2}Accessing Your App's Scope}{964}{section.320.2}\\protected@file@percent }\n\\newlabel{accessing-your-apps-scope}{{320.2}{964}{Accessing Your App's Scope}{section.320.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {320.3}Accessing the Site Scope}{964}{section.320.3}\\protected@file@percent }\n\\newlabel{accessing-the-site-scope}{{320.3}{964}{Accessing the Site Scope}{section.320.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {320.4}Related Topics}{965}{section.320.4}\\protected@file@percent }\n\\newlabel{related-topics-6}{{320.4}{965}{Related Topics}{section.320.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {321}Using the Message Bus}{967}{chapter.321}\\protected@file@percent }\n\\newlabel{using-the-message-bus}{{321}{967}{Using the Message Bus}{chapter.321}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {321.1}Messaging Destinations}{967}{section.321.1}\\protected@file@percent }\n\\newlabel{messaging-destinations}{{321.1}{967}{Messaging Destinations}{section.321.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {321.2}Destination Configuration}{967}{section.321.2}\\protected@file@percent }\n\\newlabel{destination-configuration}{{321.2}{967}{Destination Configuration}{section.321.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {321.3}Message Listeners}{968}{section.321.3}\\protected@file@percent }\n\\newlabel{message-listeners}{{321.3}{968}{Message Listeners}{section.321.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {321.4}Sending Messages}{969}{section.321.4}\\protected@file@percent }\n\\newlabel{sending-messages}{{321.4}{969}{Sending Messages}{section.321.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {322}Creating a Destination}{971}{chapter.322}\\protected@file@percent }\n\\newlabel{creating-a-destination}{{322}{971}{Creating a Destination}{chapter.322}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {322.1}Related Topics}{975}{section.322.1}\\protected@file@percent }\n\\newlabel{related-topics-7}{{322.1}{975}{Related Topics}{section.322.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {323}Message Bus Event Listeners}{977}{chapter.323}\\protected@file@percent }\n\\newlabel{message-bus-event-listeners}{{323}{977}{Message Bus Event Listeners}{chapter.323}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {323.1}Listening for Destinations}{977}{section.323.1}\\protected@file@percent }\n\\newlabel{listening-for-destinations}{{323.1}{977}{Listening for Destinations}{section.323.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {323.2}Listening for Message Listeners}{977}{section.323.2}\\protected@file@percent }\n\\newlabel{listening-for-message-listeners}{{323.2}{977}{Listening for Message Listeners}{section.323.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {323.3}Related Topics}{978}{section.323.3}\\protected@file@percent }\n\\newlabel{related-topics-8}{{323.3}{978}{Related Topics}{section.323.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {324}Registering Message Listeners}{979}{chapter.324}\\protected@file@percent }\n\\newlabel{registering-message-listeners}{{324}{979}{Registering Message Listeners}{chapter.324}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {324.1}Automatic Registration as a Component}{979}{section.324.1}\\protected@file@percent }\n\\newlabel{automatic-registration-as-a-component}{{324.1}{979}{Automatic Registration as a Component}{section.324.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {324.2}Registering via a MessageBus Reference}{980}{section.324.2}\\protected@file@percent }\n\\newlabel{registering-via-a-messagebus-reference}{{324.2}{980}{Registering via a MessageBus Reference}{section.324.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {324.3}Registering Directly to the Destination}{980}{section.324.3}\\protected@file@percent }\n\\newlabel{registering-directly-to-the-destination}{{324.3}{980}{Registering Directly to the Destination}{section.324.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {324.4}Related Topics}{981}{section.324.4}\\protected@file@percent }\n\\newlabel{related-topics-9}{{324.4}{981}{Related Topics}{section.324.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {325}Creating a Message}{983}{chapter.325}\\protected@file@percent }\n\\newlabel{creating-a-message}{{325}{983}{Creating a Message}{chapter.325}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {325.1}Related Topics}{983}{section.325.1}\\protected@file@percent }\n\\newlabel{related-topics-10}{{325.1}{983}{Related Topics}{section.325.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {326}Sending a Message}{985}{chapter.326}\\protected@file@percent }\n\\newlabel{sending-a-message}{{326}{985}{Sending a Message}{chapter.326}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {326.1}Directly with MessageBus}{985}{section.326.1}\\protected@file@percent }\n\\newlabel{directly-with-messagebus}{{326.1}{985}{Directly with MessageBus}{section.326.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {326.2}Asynchronously with SingleDestinationMessageSender}{986}{section.326.2}\\protected@file@percent }\n\\newlabel{asynchronously-with-singledestinationmessagesender}{{326.2}{986}{Asynchronously with SingleDestinationMessageSender}{section.326.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {326.3}Synchronously with SynchronousMessageSender}{987}{section.326.3}\\protected@file@percent }\n\\newlabel{synchronously-with-synchronousmessagesender}{{326.3}{987}{Synchronously with SynchronousMessageSender}{section.326.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {326.4}Related Topics}{988}{section.326.4}\\protected@file@percent }\n\\newlabel{related-topics-11}{{326.4}{988}{Related Topics}{section.326.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {327}Sending Messages Across a Cluster}{989}{chapter.327}\\protected@file@percent }\n\\newlabel{sending-messages-across-a-cluster}{{327}{989}{Sending Messages Across a Cluster}{chapter.327}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {327.1}Related Topics}{990}{section.327.1}\\protected@file@percent }\n\\newlabel{related-topics-12}{{327.1}{990}{Related Topics}{section.327.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {328}Cache Configuration}{991}{chapter.328}\\protected@file@percent }\n\\newlabel{cache-configuration}{{328}{991}{Cache Configuration}{chapter.328}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {328.1}Cache Types}{991}{section.328.1}\\protected@file@percent }\n\\newlabel{cache-types}{{328.1}{991}{Cache Types}{section.328.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {328.2}Cache Configuration}{992}{section.328.2}\\protected@file@percent }\n\\newlabel{cache-configuration-1}{{328.2}{992}{Cache Configuration}{section.328.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {328.3}Initial Global Cache Configuration}{992}{section.328.3}\\protected@file@percent }\n\\newlabel{initial-global-cache-configuration}{{328.3}{992}{Initial Global Cache Configuration}{section.328.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {328.4}Module Cache Configuration}{992}{section.328.4}\\protected@file@percent }\n\\newlabel{module-cache-configuration}{{328.4}{992}{Module Cache Configuration}{section.328.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {328.5}Portlet WAR Cache Configuration}{993}{section.328.5}\\protected@file@percent }\n\\newlabel{portlet-war-cache-configuration}{{328.5}{993}{Portlet WAR Cache Configuration}{section.328.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {328.6}Cache Names and Registration}{993}{section.328.6}\\protected@file@percent }\n\\newlabel{cache-names-and-registration}{{328.6}{993}{Cache Names and Registration}{section.328.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {328.7}EntityCache Names}{993}{section.328.7}\\protected@file@percent }\n\\newlabel{entitycache-names}{{328.7}{993}{EntityCache Names}{section.328.7}{}}\n\\gdef \\LT@xv {\\LT@entry \n    {1}{105.04608pt}\\LT@entry \n    {1}{185.35446pt}\\LT@entry \n    {1}{179.35446pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {328.8}FinderCache Names}{994}{section.328.8}\\protected@file@percent }\n\\newlabel{findercache-names}{{328.8}{994}{FinderCache Names}{section.328.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {329}Overriding Cache}{995}{chapter.329}\\protected@file@percent }\n\\newlabel{overriding-cache}{{329}{995}{Overriding Cache}{chapter.329}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {329.1}{\\ignorespaces Caches configured in Liferay DXP can be examined using JMX tools such as Zulu Mission Control (Portal Process → MBean server → MBean Browser)}}{996}{figure.329.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {329.1}Related Topics}{997}{section.329.1}\\protected@file@percent }\n\\newlabel{related-topics-13}{{329.1}{997}{Related Topics}{section.329.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {330}Caching Data}{999}{chapter.330}\\protected@file@percent }\n\\newlabel{caching-data}{{330}{999}{Caching Data}{chapter.330}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {330.1}Step 1: Determine Cache Pool Requirements}{999}{section.330.1}\\protected@file@percent }\n\\newlabel{step-1-determine-cache-pool-requirements}{{330.1}{999}{Step 1: Determine Cache Pool Requirements}{section.330.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {330.2}Step 2: Implement a Cache Key}{999}{section.330.2}\\protected@file@percent }\n\\newlabel{step-2-implement-a-cache-key}{{330.2}{999}{Step 2: Implement a Cache Key}{section.330.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {330.3}Step 3: Implement Cache Logic}{1001}{section.330.3}\\protected@file@percent }\n\\newlabel{step-3-implement-cache-logic}{{330.3}{1001}{Step 3: Implement Cache Logic}{section.330.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {330.4}Step 4: Configure the Cache}{1002}{section.330.4}\\protected@file@percent }\n\\newlabel{step-4-configure-the-cache}{{330.4}{1002}{Step 4: Configure the Cache}{section.330.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {330.5}Related Topics}{1003}{section.330.5}\\protected@file@percent }\n\\newlabel{related-topics-14}{{330.5}{1003}{Related Topics}{section.330.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {331}Collaboration}{1005}{chapter.331}\\protected@file@percent }\n\\newlabel{collaboration}{{331}{1005}{Collaboration}{chapter.331}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {332}Item Selector}{1007}{chapter.332}\\protected@file@percent }\n\\newlabel{item-selector}{{332}{1007}{Item Selector}{chapter.332}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {332.1}{\\ignorespaces Item Selectors select different kinds of entities.}}{1007}{figure.332.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {333}Adaptive Media}{1009}{chapter.333}\\protected@file@percent }\n\\newlabel{adaptive-media}{{333}{1009}{Adaptive Media}{chapter.333}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {334}Social API}{1011}{chapter.334}\\protected@file@percent }\n\\newlabel{social-api}{{334}{1011}{Social API}{chapter.334}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {335}Documents and Media API}{1013}{chapter.335}\\protected@file@percent }\n\\newlabel{documents-and-media-api}{{335}{1013}{Documents and Media API}{chapter.335}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {336}Item Selector}{1015}{chapter.336}\\protected@file@percent }\n\\newlabel{item-selector-1}{{336}{1015}{Item Selector}{chapter.336}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {336.1}{\\ignorespaces Item Selectors select entities.}}{1016}{figure.336.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {337}Understanding the Item Selector API's Components}{1017}{chapter.337}\\protected@file@percent }\n\\newlabel{understanding-the-item-selector-apis-components}{{337}{1017}{Understanding the Item Selector API's Components}{chapter.337}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {337.1}{\\ignorespaces Item Selector views (selection views) are determined by the return type and criterion, and rendered by the markup.}}{1018}{figure.337.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {338}Getting an Item Selector}{1019}{chapter.338}\\protected@file@percent }\n\\newlabel{getting-an-item-selector}{{338}{1019}{Getting an Item Selector}{chapter.338}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {339}Understanding Custom Selection Views}{1021}{chapter.339}\\protected@file@percent }\n\\newlabel{understanding-custom-selection-views}{{339}{1021}{Understanding Custom Selection Views}{chapter.339}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {339.1}{\\ignorespaces An entity type can have multiple selection views.}}{1021}{figure.339.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {339.1}The Selection View's Class}{1021}{section.339.1}\\protected@file@percent }\n\\newlabel{the-selection-views-class}{{339.1}{1021}{The Selection View's Class}{section.339.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {340}Selecting Entities with an Item Selector}{1023}{chapter.340}\\protected@file@percent }\n\\newlabel{selecting-entities-with-an-item-selector}{{340}{1023}{Selecting Entities with an Item Selector}{chapter.340}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {340.1}Get an Item Selector}{1023}{section.340.1}\\protected@file@percent }\n\\newlabel{get-an-item-selector}{{340.1}{1023}{Get an Item Selector}{section.340.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {340.2}Using the Item Selector Dialog}{1024}{section.340.2}\\protected@file@percent }\n\\newlabel{using-the-item-selector-dialog}{{340.2}{1024}{Using the Item Selector Dialog}{section.340.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {340.3}Related Topics}{1027}{section.340.3}\\protected@file@percent }\n\\newlabel{related-topics-15}{{340.3}{1027}{Related Topics}{section.340.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {341}Creating Custom Criterion and Return Types}{1029}{chapter.341}\\protected@file@percent }\n\\newlabel{creating-custom-criterion-and-return-types}{{341}{1029}{Creating Custom Criterion and Return Types}{chapter.341}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {341.1}Related Topics}{1030}{section.341.1}\\protected@file@percent }\n\\newlabel{related-topics-16}{{341.1}{1030}{Related Topics}{section.341.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {342}Creating Custom Item Selector Views}{1031}{chapter.342}\\protected@file@percent }\n\\newlabel{creating-custom-item-selector-views}{{342}{1031}{Creating Custom Item Selector Views}{chapter.342}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {342.1}Configuring Your Selection View's OSGi Module}{1031}{section.342.1}\\protected@file@percent }\n\\newlabel{configuring-your-selection-views-osgi-module}{{342.1}{1031}{Configuring Your Selection View's OSGi Module}{section.342.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {342.2}Implementing Your Selection View's Class}{1032}{section.342.2}\\protected@file@percent }\n\\newlabel{implementing-your-selection-views-class}{{342.2}{1032}{Implementing Your Selection View's Class}{section.342.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {342.3}Writing Your View Markup}{1034}{section.342.3}\\protected@file@percent }\n\\newlabel{writing-your-view-markup}{{342.3}{1034}{Writing Your View Markup}{section.342.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {342.1}{\\ignorespaces The Layouts Item Selector view uses Lexicon and Liferay Layout taglibs to create the UI.}}{1035}{figure.342.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {342.2}{\\ignorespaces The URL and UUID can be seen in the \\texttt  {data-url} and \\texttt  {data-uuid} attributes of the Layout Item Selector's HTML.}}{1037}{figure.342.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {342.4}Related Topics}{1037}{section.342.4}\\protected@file@percent }\n\\newlabel{related-topics-17}{{342.4}{1037}{Related Topics}{section.342.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {343}Documents and Media API}{1039}{chapter.343}\\protected@file@percent }\n\\newlabel{documents-and-media-api-1}{{343}{1039}{Documents and Media API}{chapter.343}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {344}Getting Started with the Documents and Media API}{1041}{chapter.344}\\protected@file@percent }\n\\newlabel{getting-started-with-the-documents-and-media-api}{{344}{1041}{Getting Started with the Documents and Media API}{chapter.344}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {345}Key Interfaces}{1043}{chapter.345}\\protected@file@percent }\n\\newlabel{key-interfaces}{{345}{1043}{Key Interfaces}{chapter.345}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {346}Getting a Service Reference}{1045}{chapter.346}\\protected@file@percent }\n\\newlabel{getting-a-service-reference}{{346}{1045}{Getting a Service Reference}{chapter.346}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {347}Specifying Repositories}{1047}{chapter.347}\\protected@file@percent }\n\\newlabel{specifying-repositories}{{347}{1047}{Specifying Repositories}{chapter.347}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {348}Specifying Folders}{1049}{chapter.348}\\protected@file@percent }\n\\newlabel{specifying-folders}{{348}{1049}{Specifying Folders}{chapter.348}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {349}Creating Files, Folders, and Shortcuts}{1051}{chapter.349}\\protected@file@percent }\n\\newlabel{creating-files-folders-and-shortcuts}{{349}{1051}{Creating Files, Folders, and Shortcuts}{chapter.349}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {350}Files}{1053}{chapter.350}\\protected@file@percent }\n\\newlabel{files}{{350}{1053}{Files}{chapter.350}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {351}Folders}{1055}{chapter.351}\\protected@file@percent }\n\\newlabel{folders}{{351}{1055}{Folders}{chapter.351}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {351.1}Folders and External Repositories}{1055}{section.351.1}\\protected@file@percent }\n\\newlabel{folders-and-external-repositories}{{351.1}{1055}{Folders and External Repositories}{section.351.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {352}File Shortcuts}{1057}{chapter.352}\\protected@file@percent }\n\\newlabel{file-shortcuts}{{352}{1057}{File Shortcuts}{chapter.352}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {353}Creating Files}{1059}{chapter.353}\\protected@file@percent }\n\\newlabel{creating-files}{{353}{1059}{Creating Files}{chapter.353}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {353.1}Related Topics}{1060}{section.353.1}\\protected@file@percent }\n\\newlabel{related-topics-18}{{353.1}{1060}{Related Topics}{section.353.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {354}Creating Folders}{1061}{chapter.354}\\protected@file@percent }\n\\newlabel{creating-folders}{{354}{1061}{Creating Folders}{chapter.354}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {354.1}Related Topics}{1062}{section.354.1}\\protected@file@percent }\n\\newlabel{related-topics-19}{{354.1}{1062}{Related Topics}{section.354.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {355}Creating File Shortcuts}{1063}{chapter.355}\\protected@file@percent }\n\\newlabel{creating-file-shortcuts}{{355}{1063}{Creating File Shortcuts}{chapter.355}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {355.1}Related Topics}{1064}{section.355.1}\\protected@file@percent }\n\\newlabel{related-topics-20}{{355.1}{1064}{Related Topics}{section.355.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {356}Deleting Entities}{1065}{chapter.356}\\protected@file@percent }\n\\newlabel{deleting-entities}{{356}{1065}{Deleting Entities}{chapter.356}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {357}Files}{1067}{chapter.357}\\protected@file@percent }\n\\newlabel{files-1}{{357}{1067}{Files}{chapter.357}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {358}File Versions}{1069}{chapter.358}\\protected@file@percent }\n\\newlabel{file-versions}{{358}{1069}{File Versions}{chapter.358}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {358.1}Identifying File Versions}{1069}{section.358.1}\\protected@file@percent }\n\\newlabel{identifying-file-versions}{{358.1}{1069}{Identifying File Versions}{section.358.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {359}File Shortcuts}{1071}{chapter.359}\\protected@file@percent }\n\\newlabel{file-shortcuts-1}{{359}{1071}{File Shortcuts}{chapter.359}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {360}Folders}{1073}{chapter.360}\\protected@file@percent }\n\\newlabel{folders-1}{{360}{1073}{Folders}{chapter.360}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {361}Recycle Bin}{1075}{chapter.361}\\protected@file@percent }\n\\newlabel{recycle-bin}{{361}{1075}{Recycle Bin}{chapter.361}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {362}Deleting Files}{1077}{chapter.362}\\protected@file@percent }\n\\newlabel{deleting-files}{{362}{1077}{Deleting Files}{chapter.362}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {362.1}Related Topics}{1078}{section.362.1}\\protected@file@percent }\n\\newlabel{related-topics-21}{{362.1}{1078}{Related Topics}{section.362.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {363}Deleting File Versions}{1079}{chapter.363}\\protected@file@percent }\n\\newlabel{deleting-file-versions}{{363}{1079}{Deleting File Versions}{chapter.363}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {363.1}Related Topics}{1080}{section.363.1}\\protected@file@percent }\n\\newlabel{related-topics-22}{{363.1}{1080}{Related Topics}{section.363.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {364}Deleting File Shortcuts}{1081}{chapter.364}\\protected@file@percent }\n\\newlabel{deleting-file-shortcuts}{{364}{1081}{Deleting File Shortcuts}{chapter.364}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {364.1}Related Topics}{1081}{section.364.1}\\protected@file@percent }\n\\newlabel{related-topics-23}{{364.1}{1081}{Related Topics}{section.364.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {365}Deleting Folders}{1083}{chapter.365}\\protected@file@percent }\n\\newlabel{deleting-folders}{{365}{1083}{Deleting Folders}{chapter.365}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {365.1}Related Topics}{1084}{section.365.1}\\protected@file@percent }\n\\newlabel{related-topics-24}{{365.1}{1084}{Related Topics}{section.365.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {366}Moving Entities to the Recycle Bin}{1085}{chapter.366}\\protected@file@percent }\n\\newlabel{moving-entities-to-the-recycle-bin}{{366}{1085}{Moving Entities to the Recycle Bin}{chapter.366}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {366.1}Related Topics}{1085}{section.366.1}\\protected@file@percent }\n\\newlabel{related-topics-25}{{366.1}{1085}{Related Topics}{section.366.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {367}Updating Entities}{1087}{chapter.367}\\protected@file@percent }\n\\newlabel{updating-entities}{{367}{1087}{Updating Entities}{chapter.367}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {368}Files}{1089}{chapter.368}\\protected@file@percent }\n\\newlabel{files-2}{{368}{1089}{Files}{chapter.368}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {369}Folders}{1091}{chapter.369}\\protected@file@percent }\n\\newlabel{folders-2}{{369}{1091}{Folders}{chapter.369}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {370}File Shortcuts}{1093}{chapter.370}\\protected@file@percent }\n\\newlabel{file-shortcuts-2}{{370}{1093}{File Shortcuts}{chapter.370}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {371}Updating Files}{1095}{chapter.371}\\protected@file@percent }\n\\newlabel{updating-files}{{371}{1095}{Updating Files}{chapter.371}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {371.1}Related Topics}{1096}{section.371.1}\\protected@file@percent }\n\\newlabel{related-topics-26}{{371.1}{1096}{Related Topics}{section.371.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {372}Updating Folders}{1097}{chapter.372}\\protected@file@percent }\n\\newlabel{updating-folders}{{372}{1097}{Updating Folders}{chapter.372}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {372.1}Related Topics}{1098}{section.372.1}\\protected@file@percent }\n\\newlabel{related-topics-27}{{372.1}{1098}{Related Topics}{section.372.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {373}Updating File Shortcuts}{1099}{chapter.373}\\protected@file@percent }\n\\newlabel{updating-file-shortcuts}{{373}{1099}{Updating File Shortcuts}{chapter.373}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {373.1}Related Topics}{1100}{section.373.1}\\protected@file@percent }\n\\newlabel{related-topics-28}{{373.1}{1100}{Related Topics}{section.373.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {374}File Checkout and Checkin}{1101}{chapter.374}\\protected@file@percent }\n\\newlabel{file-checkout-and-checkin}{{374}{1101}{File Checkout and Checkin}{chapter.374}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {375}File Checkout}{1103}{chapter.375}\\protected@file@percent }\n\\newlabel{file-checkout}{{375}{1103}{File Checkout}{chapter.375}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {375.1}Fine-tuning Checkout}{1103}{section.375.1}\\protected@file@percent }\n\\newlabel{fine-tuning-checkout}{{375.1}{1103}{Fine-tuning Checkout}{section.375.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {376}File Checkin}{1105}{chapter.376}\\protected@file@percent }\n\\newlabel{file-checkin}{{376}{1105}{File Checkin}{chapter.376}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {377}Canceling a Checkout}{1107}{chapter.377}\\protected@file@percent }\n\\newlabel{canceling-a-checkout}{{377}{1107}{Canceling a Checkout}{chapter.377}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {378}Checking Out Files}{1109}{chapter.378}\\protected@file@percent }\n\\newlabel{checking-out-files}{{378}{1109}{Checking Out Files}{chapter.378}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {378.1}Related Topics}{1109}{section.378.1}\\protected@file@percent }\n\\newlabel{related-topics-29}{{378.1}{1109}{Related Topics}{section.378.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {379}Checking In Files}{1111}{chapter.379}\\protected@file@percent }\n\\newlabel{checking-in-files}{{379}{1111}{Checking In Files}{chapter.379}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {379.1}Related Topics}{1112}{section.379.1}\\protected@file@percent }\n\\newlabel{related-topics-30}{{379.1}{1112}{Related Topics}{section.379.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {380}Canceling a Checkout}{1113}{chapter.380}\\protected@file@percent }\n\\newlabel{canceling-a-checkout-1}{{380}{1113}{Canceling a Checkout}{chapter.380}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {380.1}Related Topics}{1113}{section.380.1}\\protected@file@percent }\n\\newlabel{related-topics-31}{{380.1}{1113}{Related Topics}{section.380.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {381}Copying and Moving Entities}{1115}{chapter.381}\\protected@file@percent }\n\\newlabel{copying-and-moving-entities}{{381}{1115}{Copying and Moving Entities}{chapter.381}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {382}Copying Folders}{1117}{chapter.382}\\protected@file@percent }\n\\newlabel{copying-folders}{{382}{1117}{Copying Folders}{chapter.382}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {383}Moving Folders and Files}{1119}{chapter.383}\\protected@file@percent }\n\\newlabel{moving-folders-and-files}{{383}{1119}{Moving Folders and Files}{chapter.383}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {384}Copying Folders}{1121}{chapter.384}\\protected@file@percent }\n\\newlabel{copying-folders-1}{{384}{1121}{Copying Folders}{chapter.384}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {384.1}Related Topics}{1122}{section.384.1}\\protected@file@percent }\n\\newlabel{related-topics-32}{{384.1}{1122}{Related Topics}{section.384.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {385}Moving Folders and Files}{1123}{chapter.385}\\protected@file@percent }\n\\newlabel{moving-folders-and-files-1}{{385}{1123}{Moving Folders and Files}{chapter.385}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {385.1}Related Topics}{1124}{section.385.1}\\protected@file@percent }\n\\newlabel{related-topics-33}{{385.1}{1124}{Related Topics}{section.385.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {386}Getting Entities}{1125}{chapter.386}\\protected@file@percent }\n\\newlabel{getting-entities}{{386}{1125}{Getting Entities}{chapter.386}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {387}Files}{1127}{chapter.387}\\protected@file@percent }\n\\newlabel{files-3}{{387}{1127}{Files}{chapter.387}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {388}Folders}{1129}{chapter.388}\\protected@file@percent }\n\\newlabel{folders-3}{{388}{1129}{Folders}{chapter.388}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {389}Multiple Entity Types}{1131}{chapter.389}\\protected@file@percent }\n\\newlabel{multiple-entity-types}{{389}{1131}{Multiple Entity Types}{chapter.389}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {390}Getting Files}{1133}{chapter.390}\\protected@file@percent }\n\\newlabel{getting-files}{{390}{1133}{Getting Files}{chapter.390}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {390.1}Related Topics}{1134}{section.390.1}\\protected@file@percent }\n\\newlabel{related-topics-34}{{390.1}{1134}{Related Topics}{section.390.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {391}Getting Folders}{1135}{chapter.391}\\protected@file@percent }\n\\newlabel{getting-folders}{{391}{1135}{Getting Folders}{chapter.391}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {391.1}Related Topics}{1136}{section.391.1}\\protected@file@percent }\n\\newlabel{related-topics-35}{{391.1}{1136}{Related Topics}{section.391.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {392}Getting Multiple Entity Types}{1137}{chapter.392}\\protected@file@percent }\n\\newlabel{getting-multiple-entity-types}{{392}{1137}{Getting Multiple Entity Types}{chapter.392}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {392.1}Related Topics}{1138}{section.392.1}\\protected@file@percent }\n\\newlabel{related-topics-36}{{392.1}{1138}{Related Topics}{section.392.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {393}Adaptive Media}{1139}{chapter.393}\\protected@file@percent }\n\\newlabel{adaptive-media-1}{{393}{1139}{Adaptive Media}{chapter.393}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {394}The Adaptive Media Taglib}{1141}{chapter.394}\\protected@file@percent }\n\\newlabel{the-adaptive-media-taglib}{{394}{1141}{The Adaptive Media Taglib}{chapter.394}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {395}Adaptive Media's Finder API}{1143}{chapter.395}\\protected@file@percent }\n\\newlabel{adaptive-medias-finder-api}{{395}{1143}{Adaptive Media's Finder API}{chapter.395}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {395.1}Calling the API}{1143}{section.395.1}\\protected@file@percent }\n\\newlabel{calling-the-api}{{395.1}{1143}{Calling the API}{section.395.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {395.2}Adaptive Media API Constants}{1144}{section.395.2}\\protected@file@percent }\n\\newlabel{adaptive-media-api-constants}{{395.2}{1144}{Adaptive Media API Constants}{section.395.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {395.3}Approximate Attributes}{1144}{section.395.3}\\protected@file@percent }\n\\newlabel{approximate-attributes}{{395.3}{1144}{Approximate Attributes}{section.395.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {396}Image Scaling in Adaptive Media}{1145}{chapter.396}\\protected@file@percent }\n\\newlabel{image-scaling-in-adaptive-media}{{396}{1145}{Image Scaling in Adaptive Media}{chapter.396}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {397}Displaying Adapted Images in Your App}{1147}{chapter.397}\\protected@file@percent }\n\\newlabel{displaying-adapted-images-in-your-app}{{397}{1147}{Displaying Adapted Images in Your App}{chapter.397}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {397.1}{\\ignorespaces The Adaptive Media Samples app shows all the site's adapted images.}}{1148}{figure.397.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {397.1}Related Topics}{1148}{section.397.1}\\protected@file@percent }\n\\newlabel{related-topics-37}{{397.1}{1148}{Related Topics}{section.397.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {398}Finding Adapted Images}{1151}{chapter.398}\\protected@file@percent }\n\\newlabel{finding-adapted-images}{{398}{1151}{Finding Adapted Images}{chapter.398}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.1}Getting Adapted Images for File Versions}{1151}{section.398.1}\\protected@file@percent }\n\\newlabel{getting-adapted-images-for-file-versions}{{398.1}{1151}{Getting Adapted Images for File Versions}{section.398.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.2}Getting the Adapted Images for a Specific Image Resolution}{1152}{section.398.2}\\protected@file@percent }\n\\newlabel{getting-the-adapted-images-for-a-specific-image-resolution}{{398.2}{1152}{Getting the Adapted Images for a Specific Image Resolution}{section.398.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.3}Getting Adapted Images in a Specific Order}{1152}{section.398.3}\\protected@file@percent }\n\\newlabel{getting-adapted-images-in-a-specific-order}{{398.3}{1152}{Getting Adapted Images in a Specific Order}{section.398.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.4}Using Approximate Attributes}{1153}{section.398.4}\\protected@file@percent }\n\\newlabel{using-approximate-attributes}{{398.4}{1153}{Using Approximate Attributes}{section.398.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.5}Using the Adaptive Media Stream}{1153}{section.398.5}\\protected@file@percent }\n\\newlabel{using-the-adaptive-media-stream}{{398.5}{1153}{Using the Adaptive Media Stream}{section.398.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.6}Related Topics}{1154}{section.398.6}\\protected@file@percent }\n\\newlabel{related-topics-38}{{398.6}{1154}{Related Topics}{section.398.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {399}Creating an Image Scaler}{1155}{chapter.399}\\protected@file@percent }\n\\newlabel{creating-an-image-scaler}{{399}{1155}{Creating an Image Scaler}{chapter.399}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {399.1}Related Topics}{1157}{section.399.1}\\protected@file@percent }\n\\newlabel{related-topics-39}{{399.1}{1157}{Related Topics}{section.399.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {400}Social API}{1159}{chapter.400}\\protected@file@percent }\n\\newlabel{social-api-1}{{400}{1159}{Social API}{chapter.400}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {401}Social Bookmarks}{1161}{chapter.401}\\protected@file@percent }\n\\newlabel{social-bookmarks}{{401}{1161}{Social Bookmarks}{chapter.401}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {401.1}{\\ignorespaces With \\texttt  {displayStyle} set to \\texttt  {inline}, the first three social bookmarks appear in a row and the rest appear in a menu.}}{1161}{figure.401.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {401.2}{\\ignorespaces With \\texttt  {displayStyle} set to \\texttt  {menu}, all social bookmarks appear in the \\emph  {Share} menu.}}{1162}{figure.401.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {402}Ratings}{1163}{chapter.402}\\protected@file@percent }\n\\newlabel{ratings}{{402}{1163}{Ratings}{chapter.402}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {402.1}Rating Type Selection}{1163}{section.402.1}\\protected@file@percent }\n\\newlabel{rating-type-selection}{{402.1}{1163}{Rating Type Selection}{section.402.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {402.2}Rating Value Transformation}{1164}{section.402.2}\\protected@file@percent }\n\\newlabel{rating-value-transformation}{{402.2}{1164}{Rating Value Transformation}{section.402.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {403}Applying Social Bookmarks}{1165}{chapter.403}\\protected@file@percent }\n\\newlabel{applying-social-bookmarks}{{403}{1165}{Applying Social Bookmarks}{chapter.403}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {403.1}{\\ignorespaces These social bookmarks are in the inline display style.}}{1165}{figure.403.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {403.1}Related Topics}{1166}{section.403.1}\\protected@file@percent }\n\\newlabel{related-topics-40}{{403.1}{1166}{Related Topics}{section.403.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {404}Creating Social Bookmarks}{1167}{chapter.404}\\protected@file@percent }\n\\newlabel{creating-social-bookmarks}{{404}{1167}{Creating Social Bookmarks}{chapter.404}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {404.1}Implementing the SocialBookmark Interface}{1167}{section.404.1}\\protected@file@percent }\n\\newlabel{implementing-the-socialbookmark-interface}{{404.1}{1167}{Implementing the SocialBookmark Interface}{section.404.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {404.2}Creating Your JSP}{1168}{section.404.2}\\protected@file@percent }\n\\newlabel{creating-your-jsp}{{404.2}{1168}{Creating Your JSP}{section.404.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {404.3}Related Topics}{1169}{section.404.3}\\protected@file@percent }\n\\newlabel{related-topics-41}{{404.3}{1169}{Related Topics}{section.404.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {405}Adding Comments to Your App}{1171}{chapter.405}\\protected@file@percent }\n\\newlabel{adding-comments-to-your-app}{{405}{1171}{Adding Comments to Your App}{chapter.405}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {405.1}Related Topics}{1172}{section.405.1}\\protected@file@percent }\n\\newlabel{related-topics-42}{{405.1}{1172}{Related Topics}{section.405.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {406}Rating Assets}{1173}{chapter.406}\\protected@file@percent }\n\\newlabel{rating-assets}{{406}{1173}{Rating Assets}{chapter.406}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {406.1}{\\ignorespaces Users can rate content to let others know how they really feel about it.}}{1173}{figure.406.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {406.1}Related Topics}{1174}{section.406.1}\\protected@file@percent }\n\\newlabel{related-topics-43}{{406.1}{1174}{Related Topics}{section.406.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {407}Implementing Rating Type Selection}{1175}{chapter.407}\\protected@file@percent }\n\\newlabel{implementing-rating-type-selection}{{407}{1175}{Implementing Rating Type Selection}{chapter.407}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {407.1}Related Topics}{1175}{section.407.1}\\protected@file@percent }\n\\newlabel{related-topics-44}{{407.1}{1175}{Related Topics}{section.407.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {408}Customizing Rating Value Transformation}{1177}{chapter.408}\\protected@file@percent }\n\\newlabel{customizing-rating-value-transformation}{{408}{1177}{Customizing Rating Value Transformation}{chapter.408}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {408.1}Related Topics}{1178}{section.408.1}\\protected@file@percent }\n\\newlabel{related-topics-45}{{408.1}{1178}{Related Topics}{section.408.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {409}Flagging Inappropriate Asset Content}{1179}{chapter.409}\\protected@file@percent }\n\\newlabel{flagging-inappropriate-asset-content}{{409}{1179}{Flagging Inappropriate Asset Content}{chapter.409}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {409.1}{\\ignorespaces Users can flag objectionable content.}}{1179}{figure.409.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {409.1}Related Topics}{1180}{section.409.1}\\protected@file@percent }\n\\newlabel{related-topics-46}{{409.1}{1180}{Related Topics}{section.409.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {410}Configurable Applications}{1181}{chapter.410}\\protected@file@percent }\n\\newlabel{configurable-applications}{{410}{1181}{Configurable Applications}{chapter.410}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {410.1}Using a Configuration Interface}{1181}{section.410.1}\\protected@file@percent }\n\\newlabel{using-a-configuration-interface}{{410.1}{1181}{Using a Configuration Interface}{section.410.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {410.2}Reading Configuration Values}{1183}{section.410.2}\\protected@file@percent }\n\\newlabel{reading-configuration-values}{{410.2}{1183}{Reading Configuration Values}{section.410.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {410.3}Further Customization}{1183}{section.410.3}\\protected@file@percent }\n\\newlabel{further-customization}{{410.3}{1183}{Further Customization}{section.410.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {411}Creating A Configuration Interface}{1185}{chapter.411}\\protected@file@percent }\n\\newlabel{creating-a-configuration-interface}{{411}{1185}{Creating A Configuration Interface}{chapter.411}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {412}Categorizing the Configuration}{1187}{chapter.412}\\protected@file@percent }\n\\newlabel{categorizing-the-configuration}{{412}{1187}{Categorizing the Configuration}{chapter.412}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {412.1}Specifying a Configuration Category}{1187}{section.412.1}\\protected@file@percent }\n\\newlabel{specifying-a-configuration-category}{{412.1}{1187}{Specifying a Configuration Category}{section.412.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {412.2}Creating New Sections and Categories}{1188}{section.412.2}\\protected@file@percent }\n\\newlabel{creating-new-sections-and-categories}{{412.2}{1188}{Creating New Sections and Categories}{section.412.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {413}Scoping Configurations}{1191}{chapter.413}\\protected@file@percent }\n\\newlabel{scoping-configurations}{{413}{1191}{Scoping Configurations}{chapter.413}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {413.1}Step 1: Setting the Configuration Scope}{1191}{section.413.1}\\protected@file@percent }\n\\newlabel{step-1-setting-the-configuration-scope}{{413.1}{1191}{Step 1: Setting the Configuration Scope}{section.413.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {413.2}Step 2: Enabling the Configuration for Scoped Retrieval}{1191}{section.413.2}\\protected@file@percent }\n\\newlabel{step-2-enabling-the-configuration-for-scoped-retrieval}{{413.2}{1191}{Step 2: Enabling the Configuration for Scoped Retrieval}{section.413.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {414}Reading Scoped Configuration Values}{1193}{chapter.414}\\protected@file@percent }\n\\newlabel{reading-scoped-configuration-values}{{414}{1193}{Reading Scoped Configuration Values}{chapter.414}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {414.1}Using the Configuration Provider}{1193}{section.414.1}\\protected@file@percent }\n\\newlabel{using-the-configuration-provider}{{414.1}{1193}{Using the Configuration Provider}{section.414.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {414.2}Accessing the Portlet Instance Configuration Through the \\texttt  {PortletDisplay}}{1194}{section.414.2}\\protected@file@percent }\n\\newlabel{accessing-the-portlet-instance-configuration-through-the-portletdisplay}{{414.2}{1194}{\\texorpdfstring {Accessing the Portlet Instance Configuration Through the \\texttt {PortletDisplay}}{Accessing the Portlet Instance Configuration Through the PortletDisplay}}{section.414.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {415}Reading Unscoped Configuration Values from an MVC Portlet}{1197}{chapter.415}\\protected@file@percent }\n\\newlabel{reading-unscoped-configuration-values-from-an-mvc-portlet}{{415}{1197}{Reading Unscoped Configuration Values from an MVC Portlet}{chapter.415}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {415.1}Accessing the Configuration Object in the Portlet Class}{1197}{section.415.1}\\protected@file@percent }\n\\newlabel{accessing-the-configuration-object-in-the-portlet-class}{{415.1}{1197}{Accessing the Configuration Object in the Portlet Class}{section.415.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {415.2}Accessing the Configuration from a JSP}{1198}{section.415.2}\\protected@file@percent }\n\\newlabel{accessing-the-configuration-from-a-jsp}{{415.2}{1198}{Accessing the Configuration from a JSP}{section.415.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {415.3}Accessing the Configuration from the Portlet Class}{1199}{section.415.3}\\protected@file@percent }\n\\newlabel{accessing-the-configuration-from-the-portlet-class}{{415.3}{1199}{Accessing the Configuration from the Portlet Class}{section.415.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {416}Reading Unscoped Configuration Values from a Component}{1201}{chapter.416}\\protected@file@percent }\n\\newlabel{reading-unscoped-configuration-values-from-a-component}{{416}{1201}{Reading Unscoped Configuration Values from a Component}{chapter.416}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {417}Customizing the Configuration User Interface}{1203}{chapter.417}\\protected@file@percent }\n\\newlabel{customizing-the-configuration-user-interface}{{417}{1203}{Customizing the Configuration User Interface}{chapter.417}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.1}Providing Custom Configuration Forms}{1203}{section.417.1}\\protected@file@percent }\n\\newlabel{providing-custom-configuration-forms}{{417.1}{1203}{Providing Custom Configuration Forms}{section.417.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.2}Creating a Completely Custom Configuration UI}{1204}{section.417.2}\\protected@file@percent }\n\\newlabel{creating-a-completely-custom-configuration-ui}{{417.2}{1204}{Creating a Completely Custom Configuration UI}{section.417.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.3}Excluding a Configuration UI}{1206}{section.417.3}\\protected@file@percent }\n\\newlabel{excluding-a-configuration-ui}{{417.3}{1206}{Excluding a Configuration UI}{section.417.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.4}Using \\texttt  {generateUI}}{1206}{section.417.4}\\protected@file@percent }\n\\newlabel{using-generateui}{{417.4}{1206}{\\texorpdfstring {Using \\texttt {generateUI}}{Using generateUI}}{section.417.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.5}Using the Configuration Visibility SPI}{1206}{section.417.5}\\protected@file@percent }\n\\newlabel{using-the-configuration-visibility-spi}{{417.5}{1206}{Using the Configuration Visibility SPI}{section.417.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {418}Configuration Form Renderer}{1209}{chapter.418}\\protected@file@percent }\n\\newlabel{configuration-form-renderer}{{418}{1209}{Configuration Form Renderer}{chapter.418}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {418.1}Creating a \\texttt  {DisplayContext}}{1209}{section.418.1}\\protected@file@percent }\n\\newlabel{creating-a-displaycontext}{{418.1}{1209}{\\texorpdfstring {Creating a \\texttt {DisplayContext}}{Creating a DisplayContext}}{section.418.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {418.1}{\\ignorespaces The auto-generated UI for the Language Template configuration screen is sub-optimal. A select list with more human readable options is preferable.}}{1210}{figure.418.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {418.2}Implementing a \\texttt  {ConfigurationFormRenderer}}{1210}{section.418.2}\\protected@file@percent }\n\\newlabel{implementing-a-configurationformrenderer}{{418.2}{1210}{\\texorpdfstring {Implementing a \\texttt {ConfigurationFormRenderer}}{Implementing a ConfigurationFormRenderer}}{section.418.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {418.3}Writing the JSP Markup}{1213}{section.418.3}\\protected@file@percent }\n\\newlabel{writing-the-jsp-markup}{{418.3}{1213}{Writing the JSP Markup}{section.418.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {418.2}{\\ignorespaces A select list provides a more user friendly configuration experience than a text field.}}{1213}{figure.418.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {419}Using DDM Form Annotations in Configuration Forms}{1215}{chapter.419}\\protected@file@percent }\n\\newlabel{using-ddm-form-annotations-in-configuration-forms}{{419}{1215}{Using DDM Form Annotations in Configuration Forms}{chapter.419}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {419.1}Step 1: Declare the Dependencies}{1215}{section.419.1}\\protected@file@percent }\n\\newlabel{step-1-declare-the-dependencies}{{419.1}{1215}{Step 1: Declare the Dependencies}{section.419.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {419.2}Step 2: Write the Configuration Form}{1216}{section.419.2}\\protected@file@percent }\n\\newlabel{step-2-write-the-configuration-form}{{419.2}{1216}{Step 2: Write the Configuration Form}{section.419.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {419.1}{\\ignorespaces The DDM annotations are used to lay out this configuration form.}}{1217}{figure.419.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {419.3}Step 3: Write the Form Declaration}{1218}{section.419.3}\\protected@file@percent }\n\\newlabel{step-3-write-the-form-declaration}{{419.3}{1218}{Step 3: Write the Form Declaration}{section.419.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {420}Upgrading a Legacy App}{1219}{chapter.420}\\protected@file@percent }\n\\newlabel{upgrading-a-legacy-app}{{420}{1219}{Upgrading a Legacy App}{chapter.420}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {421}Dynamically Populating Select List Fields in the Configuration UI}{1221}{chapter.421}\\protected@file@percent }\n\\newlabel{dynamically-populating-select-list-fields-in-the-configuration-ui}{{421}{1221}{Dynamically Populating Select List Fields in the Configuration UI}{chapter.421}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {421.1}{\\ignorespaces The select list in the Google Cloud Natural Language Text Auto Tagging entry is populated programmatically, using the \\texttt  {ConfigurationFieldOptionsProvider}.}}{1223}{figure.421.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {422}Content Publication Management}{1225}{chapter.422}\\protected@file@percent }\n\\newlabel{content-publication-management}{{422}{1225}{Content Publication Management}{chapter.422}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {422.1}Export/Import}{1225}{section.422.1}\\protected@file@percent }\n\\newlabel{exportimport}{{422.1}{1225}{Export/Import}{section.422.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {422.1}{\\ignorespaces Leveraging the Export/Import feature in your app is useful for sharing content.}}{1225}{figure.422.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {422.2}Staging}{1226}{section.422.2}\\protected@file@percent }\n\\newlabel{staging}{{422.2}{1226}{Staging}{section.422.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {423}Export/Import}{1227}{chapter.423}\\protected@file@percent }\n\\newlabel{exportimport-1}{{423}{1227}{Export/Import}{chapter.423}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {423.1}Staged Models}{1227}{section.423.1}\\protected@file@percent }\n\\newlabel{staged-models}{{423.1}{1227}{Staged Models}{section.423.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {423.2}Data Handlers}{1228}{section.423.2}\\protected@file@percent }\n\\newlabel{data-handlers}{{423.2}{1228}{Data Handlers}{section.423.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {423.3}Provide Entity Specific Local Services}{1228}{section.423.3}\\protected@file@percent }\n\\newlabel{provide-entity-specific-local-services}{{423.3}{1228}{Provide Entity Specific Local Services}{section.423.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {423.4}Export/Import Event Listeners}{1228}{section.423.4}\\protected@file@percent }\n\\newlabel{exportimport-event-listeners}{{423.4}{1228}{Export/Import Event Listeners}{section.423.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {423.1}{\\ignorespaces Staged Model Repositories provide a Staging-specific layer of functionality for your local services.}}{1229}{figure.423.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {423.5}Export/Import Processes}{1230}{section.423.5}\\protected@file@percent }\n\\newlabel{exportimport-processes}{{423.5}{1230}{Export/Import Processes}{section.423.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {424}Developing Staged Models}{1233}{chapter.424}\\protected@file@percent }\n\\newlabel{developing-staged-models}{{424}{1233}{Developing Staged Models}{chapter.424}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {424.1}Staged Model Interfaces}{1233}{section.424.1}\\protected@file@percent }\n\\newlabel{staged-model-interfaces}{{424.1}{1233}{Staged Model Interfaces}{section.424.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {424.2}Staged Model Attributes}{1234}{section.424.2}\\protected@file@percent }\n\\newlabel{staged-model-attributes}{{424.2}{1234}{Staged Model Attributes}{section.424.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {424.3}Adapting Your Business Logic to Build Staged Models}{1234}{section.424.3}\\protected@file@percent }\n\\newlabel{adapting-your-business-logic-to-build-staged-models}{{424.3}{1234}{Adapting Your Business Logic to Build Staged Models}{section.424.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {424.1}{\\ignorespaces The Staged Model Adapter class extends your entity and staged model interfaces.}}{1235}{figure.424.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {424.2}{\\ignorespaces The Model Adapter Builder gets an instance of the model and outputs a staged model.}}{1236}{figure.424.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {425}Generating Staged Models Using Service Builder}{1237}{chapter.425}\\protected@file@percent }\n\\newlabel{generating-staged-models-using-service-builder}{{425}{1237}{Generating Staged Models Using Service Builder}{chapter.425}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {426}Creating Staged Models Manually}{1239}{chapter.426}\\protected@file@percent }\n\\newlabel{creating-staged-models-manually}{{426}{1239}{Creating Staged Models Manually}{chapter.426}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {427}Developing Data Handlers}{1243}{chapter.427}\\protected@file@percent }\n\\newlabel{developing-data-handlers}{{427}{1243}{Developing Data Handlers}{chapter.427}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {427.1}{\\ignorespaces The Data Handler framework uses portlet data handlers and staged model data handlers to track and export/import portlet and staged model information, respectively.}}{1244}{figure.427.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {427.1}Understanding the \\texttt  {PortletDataHandler} Interface}{1244}{section.427.1}\\protected@file@percent }\n\\newlabel{understanding-the-portletdatahandler-interface}{{427.1}{1244}{\\texorpdfstring {Understanding the \\texttt {PortletDataHandler} Interface}{Understanding the PortletDataHandler Interface}}{section.427.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {427.2}Understanding the \\texttt  {StagedModelDataHandler} Interface}{1245}{section.427.2}\\protected@file@percent }\n\\newlabel{understanding-the-stagedmodeldatahandler-interface}{{427.2}{1245}{\\texorpdfstring {Understanding the \\texttt {StagedModelDataHandler} Interface}{Understanding the StagedModelDataHandler Interface}}{section.427.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {428}Creating Portlet Data Handlers}{1247}{chapter.428}\\protected@file@percent }\n\\newlabel{creating-portlet-data-handlers}{{428}{1247}{Creating Portlet Data Handlers}{chapter.428}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {428.1}{\\ignorespaces You can select the content types you'd like to export/import in the UI.}}{1248}{figure.428.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {428.2}{\\ignorespaces The number of modified Bookmarks entities are displayed in the Export UI.}}{1251}{figure.428.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {429}Creating Staged Model Data Handlers}{1253}{chapter.429}\\protected@file@percent }\n\\newlabel{creating-staged-model-data-handlers}{{429}{1253}{Creating Staged Model Data Handlers}{chapter.429}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {429.1}{\\ignorespaces Your staged model data handler provides the display name in the Export/Import UI.}}{1254}{figure.429.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {430}Providing Entity-Specific Local Services for Export/Import}{1257}{chapter.430}\\protected@file@percent }\n\\newlabel{providing-entity-specific-local-services-for-exportimport}{{430}{1257}{Providing Entity-Specific Local Services for Export/Import}{chapter.430}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {430.1}Understanding the \\texttt  {StagedModelRepository} Interface}{1257}{section.430.1}\\protected@file@percent }\n\\newlabel{understanding-the-stagedmodelrepository-interface}{{430.1}{1257}{\\texorpdfstring {Understanding the \\texttt {StagedModelRepository} Interface}{Understanding the StagedModelRepository Interface}}{section.430.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {430.2}Using a Staged Model Repository}{1258}{section.430.2}\\protected@file@percent }\n\\newlabel{using-a-staged-model-repository}{{430.2}{1258}{Using a Staged Model Repository}{section.430.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {431}Implementing the Staged Model Repository Framework}{1261}{chapter.431}\\protected@file@percent }\n\\newlabel{implementing-the-staged-model-repository-framework}{{431}{1261}{Implementing the Staged Model Repository Framework}{chapter.431}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {432}Using the Staged Model Repository Framework}{1263}{chapter.432}\\protected@file@percent }\n\\newlabel{using-the-staged-model-repository-framework}{{432}{1263}{Using the Staged Model Repository Framework}{chapter.432}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {433}Using the Export/Import Lifecycle Listener Framework}{1265}{chapter.433}\\protected@file@percent }\n\\newlabel{using-the-exportimport-lifecycle-listener-framework}{{433}{1265}{Using the Export/Import Lifecycle Listener Framework}{chapter.433}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {434}Initiating New Export/Import Processes}{1269}{chapter.434}\\protected@file@percent }\n\\newlabel{initiating-new-exportimport-processes}{{434}{1269}{Initiating New Export/Import Processes}{chapter.434}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {435}Staging}{1271}{chapter.435}\\protected@file@percent }\n\\newlabel{staging-1}{{435}{1271}{Staging}{chapter.435}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {435.1}Controlling Staging's UI Settings}{1271}{section.435.1}\\protected@file@percent }\n\\newlabel{controlling-stagings-ui-settings}{{435.1}{1271}{Controlling Staging's UI Settings}{section.435.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {435.2}Filtering Staging-Specific Processes and States}{1272}{section.435.2}\\protected@file@percent }\n\\newlabel{filtering-staging-specific-processes-and-states}{{435.2}{1272}{Filtering Staging-Specific Processes and States}{section.435.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {435.1}{\\ignorespaces There are many apps available to select from the Staged Content screen.}}{1273}{figure.435.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {436}Dependency Injection}{1275}{chapter.436}\\protected@file@percent }\n\\newlabel{dependency-injection}{{436}{1275}{Dependency Injection}{chapter.436}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {437}CDI Dependency Injection}{1277}{chapter.437}\\protected@file@percent }\n\\newlabel{cdi-dependency-injection}{{437}{1277}{CDI Dependency Injection}{chapter.437}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {437.1}Related Topics}{1279}{section.437.1}\\protected@file@percent }\n\\newlabel{related-topics-47}{{437.1}{1279}{Related Topics}{section.437.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {438}OSGi CDI Integration}{1281}{chapter.438}\\protected@file@percent }\n\\newlabel{osgi-cdi-integration}{{438}{1281}{OSGi CDI Integration}{chapter.438}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {438.1}Use Case: Registering a CDI bean as an OSGi service}{1281}{section.438.1}\\protected@file@percent }\n\\newlabel{use-case-registering-a-cdi-bean-as-an-osgi-service}{{438.1}{1281}{Use Case: Registering a CDI bean as an OSGi service}{section.438.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {438.1}{\\ignorespaces OSGi Service Component Runtime (SCR) finds \\texttt  {MyBean} as the best (highest ranked) \\texttt  {S1} service provider and binds it to consumer component \\texttt  {C1}.}}{1282}{figure.438.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {438.2}Use Case: Using an OSGi service in a bean}{1282}{section.438.2}\\protected@file@percent }\n\\newlabel{use-case-using-an-osgi-service-in-a-bean}{{438.2}{1282}{Use Case: Using an OSGi service in a bean}{section.438.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {438.2}{\\ignorespaces Here how Liferay's \\texttt  {UserLocalService} is injected into a bean.}}{1283}{figure.438.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {439}Publishing CDI Beans as OSGi Services}{1285}{chapter.439}\\protected@file@percent }\n\\newlabel{publishing-cdi-beans-as-osgi-services}{{439}{1285}{Publishing CDI Beans as OSGi Services}{chapter.439}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {440}Using OSGi Services in a Bean}{1287}{chapter.440}\\protected@file@percent }\n\\newlabel{using-osgi-services-in-a-bean}{{440}{1287}{Using OSGi Services in a Bean}{chapter.440}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {441}Declarative Services}{1289}{chapter.441}\\protected@file@percent }\n\\newlabel{declarative-services}{{441}{1289}{Declarative Services}{chapter.441}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {442}Service Trackers for OSGi Services}{1291}{chapter.442}\\protected@file@percent }\n\\newlabel{service-trackers-for-osgi-services}{{442}{1291}{Service Trackers for OSGi Services}{chapter.442}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {443}Using a Service Tracker}{1293}{chapter.443}\\protected@file@percent }\n\\newlabel{using-a-service-tracker}{{443}{1293}{Using a Service Tracker}{chapter.443}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {443.1}Creating a New Service Tracker Where You Need It}{1293}{section.443.1}\\protected@file@percent }\n\\newlabel{creating-a-new-service-tracker-where-you-need-it}{{443.1}{1293}{Creating a New Service Tracker Where You Need It}{section.443.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {443.2}Create a Class That Extends ServiceTracker}{1294}{section.443.2}\\protected@file@percent }\n\\newlabel{create-a-class-that-extends-servicetracker}{{443.2}{1294}{Create a Class That Extends ServiceTracker}{section.443.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {443.3}Creating a Service Tracker that Tracks Service Events Using a Callback Handler}{1295}{section.443.3}\\protected@file@percent }\n\\newlabel{creating-a-service-tracker-that-tracks-service-events-using-a-callback-handler}{{443.3}{1295}{Creating a Service Tracker that Tracks Service Events Using a Callback Handler}{section.443.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {444}Friendly URLs}{1297}{chapter.444}\\protected@file@percent }\n\\newlabel{friendly-urls}{{444}{1297}{Friendly URLs}{chapter.444}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {445}Friendly URLs}{1299}{chapter.445}\\protected@file@percent }\n\\newlabel{friendly-urls-1}{{445}{1299}{Friendly URLs}{chapter.445}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {445.1}Related Topics}{1302}{section.445.1}\\protected@file@percent }\n\\newlabel{related-topics-48}{{445.1}{1302}{Related Topics}{section.445.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {446}Front-End Development}{1303}{chapter.446}\\protected@file@percent }\n\\newlabel{front-end-development}{{446}{1303}{Front-End Development}{chapter.446}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.1}Lexicon and Clay}{1303}{section.446.1}\\protected@file@percent }\n\\newlabel{lexicon-and-clay}{{446.1}{1303}{Lexicon and Clay}{section.446.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.2}Templates}{1304}{section.446.2}\\protected@file@percent }\n\\newlabel{templates}{{446.2}{1304}{Templates}{section.446.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.3}Themes}{1304}{section.446.3}\\protected@file@percent }\n\\newlabel{themes}{{446.3}{1304}{Themes}{section.446.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.4}Front-End Extensions}{1304}{section.446.4}\\protected@file@percent }\n\\newlabel{front-end-extensions}{{446.4}{1304}{Front-End Extensions}{section.446.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {447}Themes}{1305}{chapter.447}\\protected@file@percent }\n\\newlabel{themes-1}{{447}{1305}{Themes}{chapter.447}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {448}Theme Workflow}{1307}{chapter.448}\\protected@file@percent }\n\\newlabel{theme-workflow}{{448}{1307}{Theme Workflow}{chapter.448}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {449}Developing Themes}{1309}{chapter.449}\\protected@file@percent }\n\\newlabel{developing-themes}{{449}{1309}{Developing Themes}{chapter.449}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {450}Using Developer Mode with Themes}{1311}{chapter.450}\\protected@file@percent }\n\\newlabel{using-developer-mode-with-themes}{{450}{1311}{Using Developer Mode with Themes}{chapter.450}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {450.1}Enabling Developer Mode Manually}{1311}{section.450.1}\\protected@file@percent }\n\\newlabel{enabling-developer-mode-manually}{{450.1}{1311}{Enabling Developer Mode Manually}{section.450.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {450.2}Setting Developer Mode in Dev Studio DXP}{1311}{section.450.2}\\protected@file@percent }\n\\newlabel{setting-developer-mode-in-dev-studio-dxp}{{450.2}{1311}{Setting Developer Mode in Dev Studio DXP}{section.450.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {450.1}{\\ignorespaces The \\emph  {Use developer mode} option lets you enable Developer Mode for your server in Dev Studio DXP.}}{1312}{figure.450.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {450.3}Configuring FreeMarker System Settings}{1312}{section.450.3}\\protected@file@percent }\n\\newlabel{configuring-freemarker-system-settings}{{450.3}{1312}{Configuring FreeMarker System Settings}{section.450.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {450.4}JavaScript Fast Loading}{1313}{section.450.4}\\protected@file@percent }\n\\newlabel{javascript-fast-loading}{{450.4}{1313}{JavaScript Fast Loading}{section.450.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {450.5}Related Topics}{1313}{section.450.5}\\protected@file@percent }\n\\newlabel{related-topics-49}{{450.5}{1313}{Related Topics}{section.450.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {451}Building Your Theme's Files}{1315}{chapter.451}\\protected@file@percent }\n\\newlabel{building-your-themes-files}{{451}{1315}{Building Your Theme's Files}{chapter.451}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {451.1}{\\ignorespaces Run the \\texttt  {gulp\\ build} task to build your theme's files.}}{1315}{figure.451.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {451.1}Related Topics}{1315}{section.451.1}\\protected@file@percent }\n\\newlabel{related-topics-50}{{451.1}{1315}{Related Topics}{section.451.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {452}Deploying and Applying Themes}{1317}{chapter.452}\\protected@file@percent }\n\\newlabel{deploying-and-applying-themes}{{452}{1317}{Deploying and Applying Themes}{chapter.452}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {452.1}{\\ignorespaces Your server's log notifies you when the theme's bundle has started.}}{1317}{figure.452.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {452.1}Related Topics}{1318}{section.452.1}\\protected@file@percent }\n\\newlabel{related-topics-51}{{452.1}{1318}{Related Topics}{section.452.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {453}Updating Your Theme's App Server}{1319}{chapter.453}\\protected@file@percent }\n\\newlabel{updating-your-themes-app-server}{{453}{1319}{Updating Your Theme's App Server}{chapter.453}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {453.1}{\\ignorespaces Run the \\texttt  {gulp\\ init} task to update your app server configuration.}}{1319}{figure.453.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {453.1}Related Topics}{1320}{section.453.1}\\protected@file@percent }\n\\newlabel{related-topics-52}{{453.1}{1320}{Related Topics}{section.453.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {454}Automatically Deploying Theme Changes}{1321}{chapter.454}\\protected@file@percent }\n\\newlabel{automatically-deploying-theme-changes}{{454}{1321}{Automatically Deploying Theme Changes}{chapter.454}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {454.1}Related Topics}{1321}{section.454.1}\\protected@file@percent }\n\\newlabel{related-topics-53}{{454.1}{1321}{Related Topics}{section.454.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {454.1}{\\ignorespaces The watch task notifies you that the changes are deployed.}}{1322}{figure.454.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {455}Creating a Thumbnail Preview for Your Theme}{1323}{chapter.455}\\protected@file@percent }\n\\newlabel{creating-a-thumbnail-preview-for-your-theme}{{455}{1323}{Creating a Thumbnail Preview for Your Theme}{chapter.455}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {455.1}{\\ignorespaces Your theme thumbnail is displayed with the rest of the available themes.}}{1324}{figure.455.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {455.2}{\\ignorespaces Your theme thumbnail is displayed with the rest of the available themes.}}{1324}{figure.455.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {455.1}Related Topics}{1325}{section.455.1}\\protected@file@percent }\n\\newlabel{related-topics-54}{{455.1}{1325}{Related Topics}{section.455.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {456}Creating Color Schemes for Your Theme}{1327}{chapter.456}\\protected@file@percent }\n\\newlabel{creating-color-schemes-for-your-theme}{{456}{1327}{Creating Color Schemes for Your Theme}{chapter.456}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {456.1}{\\ignorespaces Color schemes give administrators some choices for your theme's look.}}{1327}{figure.456.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {456.1}Related Topics}{1329}{section.456.1}\\protected@file@percent }\n\\newlabel{related-topics-55}{{456.1}{1329}{Related Topics}{section.456.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {457}Making Configurable Theme Settings}{1331}{chapter.457}\\protected@file@percent }\n\\newlabel{making-configurable-theme-settings}{{457}{1331}{Making Configurable Theme Settings}{chapter.457}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {457.1}{\\ignorespaces Here are examples of configurable settings for the site Admin.}}{1332}{figure.457.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {457.1}Related Topics}{1333}{section.457.1}\\protected@file@percent }\n\\newlabel{related-topics-56}{{457.1}{1333}{Related Topics}{section.457.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {458}Using Font Awesome and Glyph Icons in Your Theme}{1335}{chapter.458}\\protected@file@percent }\n\\newlabel{using-font-awesome-and-glyph-icons-in-your-theme}{{458}{1335}{Using Font Awesome and Glyph Icons in Your Theme}{chapter.458}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {458.1}Disabling Enabling Global Font Awesome and Glyphicons in Portal}{1335}{section.458.1}\\protected@file@percent }\n\\newlabel{disabling-enabling-global-font-awesome-and-glyphicons-in-portal}{{458.1}{1335}{Disabling Enabling Global Font Awesome and Glyphicons in Portal}{section.458.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {458.2}Including Font Awesome and Glyphicons in Your Theme}{1335}{section.458.2}\\protected@file@percent }\n\\newlabel{including-font-awesome-and-glyphicons-in-your-theme}{{458.2}{1335}{Including Font Awesome and Glyphicons in Your Theme}{section.458.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {458.3}Related Topics}{1336}{section.458.3}\\protected@file@percent }\n\\newlabel{related-topics-57}{{458.3}{1336}{Related Topics}{section.458.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {459}Extending Themes}{1337}{chapter.459}\\protected@file@percent }\n\\newlabel{extending-themes}{{459}{1337}{Extending Themes}{chapter.459}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {460}Installing a Themelet in Your Theme}{1339}{chapter.460}\\protected@file@percent }\n\\newlabel{installing-a-themelet-in-your-theme}{{460}{1339}{Installing a Themelet in Your Theme}{chapter.460}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {460.1}{\\ignorespaces You can extend your theme using globally installed npm modules or published npm modules.}}{1339}{figure.460.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {460.1}Related Topics}{1340}{section.460.1}\\protected@file@percent }\n\\newlabel{related-topics-58}{{460.1}{1340}{Related Topics}{section.460.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {461}Injecting Additional Context Variables and Functionality into Your Theme Templates}{1341}{chapter.461}\\protected@file@percent }\n\\newlabel{injecting-additional-context-variables-and-functionality-into-your-theme-templates}{{461}{1341}{Injecting Additional Context Variables and Functionality into Your Theme Templates}{chapter.461}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {461.1}Related Topics}{1342}{section.461.1}\\protected@file@percent }\n\\newlabel{related-topics-59}{{461.1}{1342}{Related Topics}{section.461.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {462}Packaging Independent UI Resources for Your Site}{1343}{chapter.462}\\protected@file@percent }\n\\newlabel{packaging-independent-ui-resources-for-your-site}{{462}{1343}{Packaging Independent UI Resources for Your Site}{chapter.462}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {462.1}{\\ignorespaces The Control Menu, Product Menu, and Simulation Panel are packaged as Theme Contributor modules.}}{1344}{figure.462.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {462.1}Related Topics}{1344}{section.462.1}\\protected@file@percent }\n\\newlabel{related-topics-60}{{462.1}{1344}{Related Topics}{section.462.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {463}Changing Your Base Theme}{1345}{chapter.463}\\protected@file@percent }\n\\newlabel{changing-your-base-theme}{{463}{1345}{Changing Your Base Theme}{chapter.463}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {463.1}{\\ignorespaces Run the \\texttt  {gulp\\ extend} task to change your base theme.}}{1345}{figure.463.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {463.1}Related Topics}{1346}{section.463.1}\\protected@file@percent }\n\\newlabel{related-topics-61}{{463.1}{1346}{Related Topics}{section.463.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {464}Copying an Existing Theme's Files}{1347}{chapter.464}\\protected@file@percent }\n\\newlabel{copying-an-existing-themes-files}{{464}{1347}{Copying an Existing Theme's Files}{chapter.464}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {464.1}{\\ignorespaces Run the \\texttt  {gulp\\ kickstart} task to copy a theme's files into your own theme.}}{1347}{figure.464.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {464.1}Related Topics}{1348}{section.464.1}\\protected@file@percent }\n\\newlabel{related-topics-62}{{464.1}{1348}{Related Topics}{section.464.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {465}Listing Your Theme's Extensions}{1349}{chapter.465}\\protected@file@percent }\n\\newlabel{listing-your-themes-extensions}{{465}{1349}{Listing Your Theme's Extensions}{chapter.465}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {465.1}{\\ignorespaces Run the \\texttt  {gulp\\ status} task to list your theme's current extensions.}}{1349}{figure.465.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {465.1}Related Topics}{1349}{section.465.1}\\protected@file@percent }\n\\newlabel{related-topics-63}{{465.1}{1349}{Related Topics}{section.465.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {466}Overwriting and Extending Liferay Theme Tasks}{1351}{chapter.466}\\protected@file@percent }\n\\newlabel{overwriting-and-extending-liferay-theme-tasks}{{466}{1351}{Overwriting and Extending Liferay Theme Tasks}{chapter.466}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {466.1}Related Topics}{1353}{section.466.1}\\protected@file@percent }\n\\newlabel{related-topics-64}{{466.1}{1353}{Related Topics}{section.466.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {467}Clay CSS and Themes}{1355}{chapter.467}\\protected@file@percent }\n\\newlabel{clay-css-and-themes}{{467}{1355}{Clay CSS and Themes}{chapter.467}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {468}Customizing Atlas and Clay Base Themes in Liferay DXP}{1357}{chapter.468}\\protected@file@percent }\n\\newlabel{customizing-atlas-and-clay-base-themes-in-liferay-dxp}{{468}{1357}{Customizing Atlas and Clay Base Themes in Liferay DXP}{chapter.468}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {468.1}Related Topics}{1358}{section.468.1}\\protected@file@percent }\n\\newlabel{related-topics-65}{{468.1}{1358}{Related Topics}{section.468.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {469}Integrating Third Party Themes with Clay}{1359}{chapter.469}\\protected@file@percent }\n\\newlabel{integrating-third-party-themes-with-clay}{{469}{1359}{Integrating Third Party Themes with Clay}{chapter.469}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {469.1}Related Topics}{1360}{section.469.1}\\protected@file@percent }\n\\newlabel{related-topics-66}{{469.1}{1360}{Related Topics}{section.469.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {470}Using Clay Icons in a Theme}{1361}{chapter.470}\\protected@file@percent }\n\\newlabel{using-clay-icons-in-a-theme}{{470}{1361}{Using Clay Icons in a Theme}{chapter.470}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {470.1}Related Topics}{1361}{section.470.1}\\protected@file@percent }\n\\newlabel{related-topics-67}{{470.1}{1361}{Related Topics}{section.470.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {471}Using Clay Mixins in Your Theme}{1363}{chapter.471}\\protected@file@percent }\n\\newlabel{using-clay-mixins-in-your-theme}{{471}{1363}{Using Clay Mixins in Your Theme}{chapter.471}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {471.1}Related Topics}{1363}{section.471.1}\\protected@file@percent }\n\\newlabel{related-topics-68}{{471.1}{1363}{Related Topics}{section.471.1}{}}\n\\gdef \\LT@xvi {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {472}Theming Portlets}{1365}{chapter.472}\\protected@file@percent }\n\\newlabel{theming-portlets}{{472}{1365}{Theming Portlets}{chapter.472}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {472.1}Portlet Decorators}{1366}{section.472.1}\\protected@file@percent }\n\\newlabel{portlet-decorators}{{472.1}{1366}{Portlet Decorators}{section.472.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {472.1}{\\ignorespaces The Classic theme's Decorate Application Decorator wraps the portlet in a white box.}}{1367}{figure.472.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {472.2}{\\ignorespaces The Classic theme's Borderless Application Decorator displays the application's custom title.}}{1368}{figure.472.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {472.3}{\\ignorespaces The Classic theme's Barebone Application Decorator displays only the application's content.}}{1369}{figure.472.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {473}Embedding Portlets in Themes}{1371}{chapter.473}\\protected@file@percent }\n\\newlabel{embedding-portlets-in-themes}{{473}{1371}{Embedding Portlets in Themes}{chapter.473}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {474}Embedding Portlets in Themes by Entity Type and Action}{1373}{chapter.474}\\protected@file@percent }\n\\newlabel{embedding-portlets-in-themes-by-entity-type-and-action}{{474}{1373}{Embedding Portlets in Themes by Entity Type and Action}{chapter.474}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {474.1}Related Topics}{1375}{section.474.1}\\protected@file@percent }\n\\newlabel{related-topics-69}{{474.1}{1375}{Related Topics}{section.474.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {475}Embedding a Portlet by Portlet Name}{1377}{chapter.475}\\protected@file@percent }\n\\newlabel{embedding-a-portlet-by-portlet-name}{{475}{1377}{Embedding a Portlet by Portlet Name}{chapter.475}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {475.1}Related Topics}{1377}{section.475.1}\\protected@file@percent }\n\\newlabel{related-topics-70}{{475.1}{1377}{Related Topics}{section.475.1}{}}\n\\gdef \\LT@xvii {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {476}Setting Default Preferences for an Embedded Portlet}{1379}{chapter.476}\\protected@file@percent }\n\\newlabel{setting-default-preferences-for-an-embedded-portlet}{{476}{1379}{Setting Default Preferences for an Embedded Portlet}{chapter.476}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {476.1}Related Topics}{1380}{section.476.1}\\protected@file@percent }\n\\newlabel{related-topics-71}{{476.1}{1380}{Related Topics}{section.476.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {477}Importing Resources with a Theme}{1381}{chapter.477}\\protected@file@percent }\n\\newlabel{importing-resources-with-a-theme}{{477}{1381}{Importing Resources with a Theme}{chapter.477}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {477.1}Organizing Your Resources}{1381}{section.477.1}\\protected@file@percent }\n\\newlabel{organizing-your-resources}{{477.1}{1381}{Organizing Your Resources}{section.477.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {478}Creating a Sitemap for the Resources Importer}{1383}{chapter.478}\\protected@file@percent }\n\\newlabel{creating-a-sitemap-for-the-resources-importer}{{478}{1383}{Creating a Sitemap for the Resources Importer}{chapter.478}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {479}Defining Layout Templates and Pages in a Sitemap}{1387}{chapter.479}\\protected@file@percent }\n\\newlabel{defining-layout-templates-and-pages-in-a-sitemap}{{479}{1387}{Defining Layout Templates and Pages in a Sitemap}{chapter.479}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {479.1}Related Topics}{1389}{section.479.1}\\protected@file@percent }\n\\newlabel{related-topics-72}{{479.1}{1389}{Related Topics}{section.479.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {480}Defining Portlets in a Sitemap}{1391}{chapter.480}\\protected@file@percent }\n\\newlabel{defining-portlets-in-a-sitemap}{{480}{1391}{Defining Portlets in a Sitemap}{chapter.480}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {480.1}Related Topics}{1393}{section.480.1}\\protected@file@percent }\n\\newlabel{related-topics-73}{{480.1}{1393}{Related Topics}{section.480.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {481}Retrieving Portlet IDs with the Gogo Shell}{1395}{chapter.481}\\protected@file@percent }\n\\newlabel{retrieving-portlet-ids-with-the-gogo-shell}{{481}{1395}{Retrieving Portlet IDs with the Gogo Shell}{chapter.481}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {481.1}Related Topics}{1395}{section.481.1}\\protected@file@percent }\n\\newlabel{related-topics-74}{{481.1}{1395}{Related Topics}{section.481.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {481.1}{\\ignorespaces Portlet IDs can be found via the Gogo Shell.}}{1396}{figure.481.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {482}Preparing and Organizing Web Content for the Resources Importer}{1397}{chapter.482}\\protected@file@percent }\n\\newlabel{preparing-and-organizing-web-content-for-the-resources-importer}{{482}{1397}{Preparing and Organizing Web Content for the Resources Importer}{chapter.482}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {482.1}Related Topics}{1399}{section.482.1}\\protected@file@percent }\n\\newlabel{related-topics-75}{{482.1}{1399}{Related Topics}{section.482.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {483}Defining Assets for the Resources Importer}{1401}{chapter.483}\\protected@file@percent }\n\\newlabel{defining-assets-for-the-resources-importer}{{483}{1401}{Defining Assets for the Resources Importer}{chapter.483}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {483.1}Related Topics}{1402}{section.483.1}\\protected@file@percent }\n\\newlabel{related-topics-76}{{483.1}{1402}{Related Topics}{section.483.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {484}Specifying Where to Import Your Theme's Resources}{1403}{chapter.484}\\protected@file@percent }\n\\newlabel{specifying-where-to-import-your-themes-resources}{{484}{1403}{Specifying Where to Import Your Theme's Resources}{chapter.484}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {484.1}Related Topics}{1404}{section.484.1}\\protected@file@percent }\n\\newlabel{related-topics-77}{{484.1}{1404}{Related Topics}{section.484.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {485}Archiving Site Resources}{1405}{chapter.485}\\protected@file@percent }\n\\newlabel{archiving-site-resources}{{485}{1405}{Archiving Site Resources}{chapter.485}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {485.1}Related Topics}{1405}{section.485.1}\\protected@file@percent }\n\\newlabel{related-topics-78}{{485.1}{1405}{Related Topics}{section.485.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {486}Troubleshooting Themes}{1407}{chapter.486}\\protected@file@percent }\n\\newlabel{troubleshooting-themes}{{486}{1407}{Troubleshooting Themes}{chapter.486}{}}\n\\newlabel{osgi-headers-in-themes}{{486}{1407}{Troubleshooting Themes}{section*.14}{}}\n\\newlabel{developer-mode}{{486}{1407}{Troubleshooting Themes}{section*.15}{}}\n\\newlabel{default-theme-returned}{{486}{1407}{Troubleshooting Themes}{section*.16}{}}\n\\newlabel{rtl-no-flip}{{486}{1407}{Troubleshooting Themes}{section*.17}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {487}Layout Templates}{1409}{chapter.487}\\protected@file@percent }\n\\newlabel{layout-templates}{{487}{1409}{Layout Templates}{chapter.487}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {487.1}{\\ignorespaces There are many default layout templates to choose from.}}{1409}{figure.487.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {488}Creating Custom Layout Template Thumbnail Previews}{1411}{chapter.488}\\protected@file@percent }\n\\newlabel{creating-custom-layout-template-thumbnail-previews}{{488}{1411}{Creating Custom Layout Template Thumbnail Previews}{chapter.488}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {488.1}{\\ignorespaces A thumbnail preview displays the layout's design to the user.}}{1411}{figure.488.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {488.1}Related topics}{1412}{section.488.1}\\protected@file@percent }\n\\newlabel{related-topics-79}{{488.1}{1412}{Related topics}{section.488.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {489}Including Layout Templates with a Theme}{1413}{chapter.489}\\protected@file@percent }\n\\newlabel{including-layout-templates-with-a-theme}{{489}{1413}{Including Layout Templates with a Theme}{chapter.489}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {489.1}Related topics}{1414}{section.489.1}\\protected@file@percent }\n\\newlabel{related-topics-80}{{489.1}{1414}{Related topics}{section.489.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {490}Creating and Bundling JavaScript Widgets with JavaScript Tooling}{1415}{chapter.490}\\protected@file@percent }\n\\newlabel{creating-and-bundling-javascript-widgets-with-javascript-tooling}{{490}{1415}{Creating and Bundling JavaScript Widgets with JavaScript Tooling}{chapter.490}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {490.1}{\\ignorespaces The JS Portlet Extender lets you use pure JavaScript tooling to write widgets.}}{1416}{figure.490.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {491}Configuring System Settings and Instance Settings for Your JavaScript Widget}{1417}{chapter.491}\\protected@file@percent }\n\\newlabel{configuring-system-settings-and-instance-settings-for-your-javascript-widget}{{491}{1417}{Configuring System Settings and Instance Settings for Your JavaScript Widget}{chapter.491}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {491.1}Related Topics}{1418}{section.491.1}\\protected@file@percent }\n\\newlabel{related-topics-81}{{491.1}{1418}{Related Topics}{section.491.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {492}Localizing Your Widget}{1419}{chapter.492}\\protected@file@percent }\n\\newlabel{localizing-your-widget}{{492}{1419}{Localizing Your Widget}{chapter.492}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {492.1}Related Topics}{1420}{section.492.1}\\protected@file@percent }\n\\newlabel{related-topics-82}{{492.1}{1420}{Related Topics}{section.492.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {493}Using Translation Features in Your Widget}{1421}{chapter.493}\\protected@file@percent }\n\\newlabel{using-translation-features-in-your-widget}{{493}{1421}{Using Translation Features in Your Widget}{chapter.493}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {493.1}Related Topics}{1421}{section.493.1}\\protected@file@percent }\n\\newlabel{related-topics-83}{{493.1}{1421}{Related Topics}{section.493.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {494}Configuring Portlet Properties for Your Widget}{1423}{chapter.494}\\protected@file@percent }\n\\newlabel{configuring-portlet-properties-for-your-widget}{{494}{1423}{Configuring Portlet Properties for Your Widget}{chapter.494}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {494.1}Related Topics}{1423}{section.494.1}\\protected@file@percent }\n\\newlabel{related-topics-84}{{494.1}{1423}{Related Topics}{section.494.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {495}JavaScript Module Loaders}{1425}{chapter.495}\\protected@file@percent }\n\\newlabel{javascript-module-loaders}{{495}{1425}{JavaScript Module Loaders}{chapter.495}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {496}Loading AMD Modules in Liferay}{1427}{chapter.496}\\protected@file@percent }\n\\newlabel{loading-amd-modules-in-liferay}{{496}{1427}{Loading AMD Modules in Liferay}{chapter.496}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {496.1}Related Topics}{1428}{section.496.1}\\protected@file@percent }\n\\newlabel{related-topics-85}{{496.1}{1428}{Related Topics}{section.496.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {497}Using External JavaScript Libraries}{1429}{chapter.497}\\protected@file@percent }\n\\newlabel{using-external-javascript-libraries}{{497}{1429}{Using External JavaScript Libraries}{chapter.497}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {497.1}Related Topics}{1430}{section.497.1}\\protected@file@percent }\n\\newlabel{related-topics-86}{{497.1}{1430}{Related Topics}{section.497.1}{}}\n\\gdef \\LT@xviii {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {498}Loading Modules with AUI Script}{1431}{chapter.498}\\protected@file@percent }\n\\newlabel{loading-modules-with-aui-script}{{498}{1431}{Loading Modules with AUI Script}{chapter.498}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {499}Loading AlloyUI Modules with AUI Script}{1433}{chapter.499}\\protected@file@percent }\n\\newlabel{loading-alloyui-modules-with-aui-script}{{499}{1433}{Loading AlloyUI Modules with AUI Script}{chapter.499}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {499.1}Related Topics}{1434}{section.499.1}\\protected@file@percent }\n\\newlabel{related-topics-87}{{499.1}{1434}{Related Topics}{section.499.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {500}Loading ES2015 and Metal.js Modules with AUI Script}{1435}{chapter.500}\\protected@file@percent }\n\\newlabel{loading-es2015-and-metal.js-modules-with-aui-script}{{500}{1435}{Loading ES2015 and Metal.js Modules with AUI Script}{chapter.500}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {500.1}Related Topics}{1436}{section.500.1}\\protected@file@percent }\n\\newlabel{related-topics-88}{{500.1}{1436}{Related Topics}{section.500.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {501}Loading AUI, ES2015, and Metal.js Modules Together with AUI Script}{1437}{chapter.501}\\protected@file@percent }\n\\newlabel{loading-aui-es2015-and-metal.js-modules-together-with-aui-script}{{501}{1437}{Loading AUI, ES2015, and Metal.js Modules Together with AUI Script}{chapter.501}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {501.1}Related Topics}{1437}{section.501.1}\\protected@file@percent }\n\\newlabel{related-topics-89}{{501.1}{1437}{Related Topics}{section.501.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {502}Loading Bundled npm Modules in Your Portlets}{1439}{chapter.502}\\protected@file@percent }\n\\newlabel{loading-bundled-npm-modules-in-your-portlets}{{502}{1439}{Loading Bundled npm Modules in Your Portlets}{chapter.502}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {502.1}Related Topics}{1440}{section.502.1}\\protected@file@percent }\n\\newlabel{related-topics-90}{{502.1}{1440}{Related Topics}{section.502.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {503}The Info Framework}{1441}{chapter.503}\\protected@file@percent }\n\\newlabel{the-info-framework}{{503}{1441}{The Info Framework}{chapter.503}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {503.1}Using the Info Framework}{1441}{section.503.1}\\protected@file@percent }\n\\newlabel{using-the-info-framework}{{503.1}{1441}{Using the Info Framework}{section.503.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {503.2}List Providers}{1442}{section.503.2}\\protected@file@percent }\n\\newlabel{list-providers}{{503.2}{1442}{List Providers}{section.503.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {503.3}Item Renderers}{1442}{section.503.3}\\protected@file@percent }\n\\newlabel{item-renderers}{{503.3}{1442}{Item Renderers}{section.503.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {504}Creating an Information List Provider}{1443}{chapter.504}\\protected@file@percent }\n\\newlabel{creating-an-information-list-provider}{{504}{1443}{Creating an Information List Provider}{chapter.504}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {504.1}Next steps}{1445}{section.504.1}\\protected@file@percent }\n\\newlabel{next-steps}{{504.1}{1445}{Next steps}{section.504.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {505}Custom rendering of information with \\texttt  {InfoItemRenderer}}{1447}{chapter.505}\\protected@file@percent }\n\\newlabel{custom-rendering-of-information-with-infoitemrenderer}{{505}{1447}{\\texorpdfstring {Custom rendering of information with \\texttt {InfoItemRenderer}}{Custom rendering of information with InfoItemRenderer}}{chapter.505}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {506}Using Providers with Custom Applications}{1449}{chapter.506}\\protected@file@percent }\n\\newlabel{using-providers-with-custom-applications}{{506}{1449}{Using Providers with Custom Applications}{chapter.506}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {506.1}Leveraging renderers from a custom application}{1449}{section.506.1}\\protected@file@percent }\n\\newlabel{leveraging-renderers-from-a-custom-application}{{506.1}{1449}{Leveraging renderers from a custom application}{section.506.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {507}Liferay Forms}{1451}{chapter.507}\\protected@file@percent }\n\\newlabel{liferay-forms}{{507}{1451}{Liferay Forms}{chapter.507}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {508}Form Serialization with the DDM IO API}{1453}{chapter.508}\\protected@file@percent }\n\\newlabel{form-serialization-with-the-ddm-io-api}{{508}{1453}{Form Serialization with the DDM IO API}{chapter.508}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {509}Serializing Forms}{1455}{chapter.509}\\protected@file@percent }\n\\newlabel{serializing-forms}{{509}{1455}{Serializing Forms}{chapter.509}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {509.1}Calling the Serializer}{1456}{section.509.1}\\protected@file@percent }\n\\newlabel{calling-the-serializer}{{509.1}{1456}{Calling the Serializer}{section.509.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {510}Localization}{1457}{chapter.510}\\protected@file@percent }\n\\newlabel{localization}{{510}{1457}{Localization}{chapter.510}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {511}Localizing Your Application}{1459}{chapter.511}\\protected@file@percent }\n\\newlabel{localizing-your-application}{{511}{1459}{Localizing Your Application}{chapter.511}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {511.1}Related Topics}{1461}{section.511.1}\\protected@file@percent }\n\\newlabel{related-topics-91}{{511.1}{1461}{Related Topics}{section.511.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {512}Using Liferay's Localization Settings}{1463}{chapter.512}\\protected@file@percent }\n\\newlabel{using-liferays-localization-settings}{{512}{1463}{Using Liferay's Localization Settings}{chapter.512}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {512.1}Localizing User Names}{1464}{section.512.1}\\protected@file@percent }\n\\newlabel{localizing-user-names}{{512.1}{1464}{Localizing User Names}{section.512.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {512.1}{\\ignorespaces The user name settings impact the appearance of user information and forms.}}{1465}{figure.512.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {512.2}{\\ignorespaces The Spanish user name settings omit the suffix and middle name fields.}}{1466}{figure.512.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {512.2}Identifying User Initials}{1466}{section.512.2}\\protected@file@percent }\n\\newlabel{identifying-user-initials}{{512.2}{1466}{Identifying User Initials}{section.512.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {512.3}{\\ignorespaces The user's initials are displayed for their avatar by default.}}{1466}{figure.512.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {512.3}Right to Left or Left to Right?}{1466}{section.512.3}\\protected@file@percent }\n\\newlabel{right-to-left-or-left-to-right}{{512.3}{1466}{Right to Left or Left to Right?}{section.512.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {512.4}Related Topics}{1467}{section.512.4}\\protected@file@percent }\n\\newlabel{related-topics-92}{{512.4}{1467}{Related Topics}{section.512.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {513}Creating a Language Module}{1469}{chapter.513}\\protected@file@percent }\n\\newlabel{creating-a-language-module}{{513}{1469}{Creating a Language Module}{chapter.513}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {513.1}Related Topics}{1470}{section.513.1}\\protected@file@percent }\n\\newlabel{related-topics-93}{{513.1}{1470}{Related Topics}{section.513.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {514}Using a Language Module}{1471}{chapter.514}\\protected@file@percent }\n\\newlabel{using-a-language-module}{{514}{1471}{Using a Language Module}{chapter.514}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {514.1}Using a Language Module from a Module}{1471}{section.514.1}\\protected@file@percent }\n\\newlabel{using-a-language-module-from-a-module}{{514.1}{1471}{Using a Language Module from a Module}{section.514.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {514.2}Using a Language Module from a Traditional Plugin}{1472}{section.514.2}\\protected@file@percent }\n\\newlabel{using-a-language-module-from-a-traditional-plugin}{{514.2}{1472}{Using a Language Module from a Traditional Plugin}{section.514.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {514.3}Related Topics}{1473}{section.514.3}\\protected@file@percent }\n\\newlabel{related-topics-94}{{514.3}{1473}{Related Topics}{section.514.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {515}Automatically Generating Translations}{1475}{chapter.515}\\protected@file@percent }\n\\newlabel{automatically-generating-translations}{{515}{1475}{Automatically Generating Translations}{chapter.515}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {515.1}Configuring the Language Builder Plugin}{1475}{section.515.1}\\protected@file@percent }\n\\newlabel{configuring-the-language-builder-plugin}{{515.1}{1475}{Configuring the Language Builder Plugin}{section.515.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {515.2}Running Language Builder}{1476}{section.515.2}\\protected@file@percent }\n\\newlabel{running-language-builder}{{515.2}{1476}{Running Language Builder}{section.515.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {515.3}Translating Language Keys Automatically}{1477}{section.515.3}\\protected@file@percent }\n\\newlabel{translating-language-keys-automatically}{{515.3}{1477}{Translating Language Keys Automatically}{section.515.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {515.4}Related Topics}{1478}{section.515.4}\\protected@file@percent }\n\\newlabel{related-topics-95}{{515.4}{1478}{Related Topics}{section.515.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {516}Portlets}{1479}{chapter.516}\\protected@file@percent }\n\\newlabel{portlets}{{516}{1479}{Portlets}{chapter.516}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {516.1}{\\ignorespaces You can place multiple portlets on a single page.}}{1480}{figure.516.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {516.1}Related Topics}{1481}{section.516.1}\\protected@file@percent }\n\\newlabel{related-topics-96}{{516.1}{1481}{Related Topics}{section.516.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {517}Using JavaScript in Your Portlets}{1483}{chapter.517}\\protected@file@percent }\n\\newlabel{using-javascript-in-your-portlets}{{517}{1483}{Using JavaScript in Your Portlets}{chapter.517}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {518}Using ES2015 Modules in your Portlet}{1485}{chapter.518}\\protected@file@percent }\n\\newlabel{using-es2015-modules-in-your-portlet}{{518}{1485}{Using ES2015 Modules in your Portlet}{chapter.518}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {518.1}Related Topics}{1486}{section.518.1}\\protected@file@percent }\n\\newlabel{related-topics-97}{{518.1}{1486}{Related Topics}{section.518.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {519}Using npm in Your Portlets}{1487}{chapter.519}\\protected@file@percent }\n\\newlabel{using-npm-in-your-portlets}{{519}{1487}{Using npm in Your Portlets}{chapter.519}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {520}Formatting Your npm Modules for AMD}{1489}{chapter.520}\\protected@file@percent }\n\\newlabel{formatting-your-npm-modules-for-amd}{{520}{1489}{Formatting Your npm Modules for AMD}{chapter.520}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {520.1}Related Topics}{1490}{section.520.1}\\protected@file@percent }\n\\newlabel{related-topics-98}{{520.1}{1490}{Related Topics}{section.520.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {521}Migrating a liferay-npm-bundler Project from 1.x to 2.x}{1491}{chapter.521}\\protected@file@percent }\n\\newlabel{migrating-a-liferay-npm-bundler-project-from-1.x-to-2.x}{{521}{1491}{Migrating a liferay-npm-bundler Project from 1.x to 2.x}{chapter.521}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {522}Migrating a Plain JavaScript, Billboard JS, JQuery, Metal JS, React, or Vue JS Project to Use Bundler 2.x}{1493}{chapter.522}\\protected@file@percent }\n\\newlabel{migrating-a-plain-javascript-billboard-js-jquery-metal-js-react-or-vue-js-project-to-use-bundler-2.x}{{522}{1493}{Migrating a Plain JavaScript, Billboard JS, JQuery, Metal JS, React, or Vue JS Project to Use Bundler 2.x}{chapter.522}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {522.1}Related Topics}{1494}{section.522.1}\\protected@file@percent }\n\\newlabel{related-topics-99}{{522.1}{1494}{Related Topics}{section.522.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {523}Migrating an Angular Project to Use Bundler 2.x}{1495}{chapter.523}\\protected@file@percent }\n\\newlabel{migrating-an-angular-project-to-use-bundler-2.x}{{523}{1495}{Migrating an Angular Project to Use Bundler 2.x}{chapter.523}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {523.1}Related Topics}{1496}{section.523.1}\\protected@file@percent }\n\\newlabel{related-topics-100}{{523.1}{1496}{Related Topics}{section.523.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {524}Migrating Your Project to Use liferay-npm-bundler's New Mode}{1497}{chapter.524}\\protected@file@percent }\n\\newlabel{migrating-your-project-to-use-liferay-npm-bundlers-new-mode}{{524}{1497}{Migrating Your Project to Use liferay-npm-bundler's New Mode}{chapter.524}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {524.1}Related Topics}{1498}{section.524.1}\\protected@file@percent }\n\\newlabel{related-topics-101}{{524.1}{1498}{Related Topics}{section.524.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {525}Creating Custom Loaders for the liferay-npm-bundler}{1499}{chapter.525}\\protected@file@percent }\n\\newlabel{creating-custom-loaders-for-the-liferay-npm-bundler}{{525}{1499}{Creating Custom Loaders for the liferay-npm-bundler}{chapter.525}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {525.1}Related Topics}{1501}{section.525.1}\\protected@file@percent }\n\\newlabel{related-topics-102}{{525.1}{1501}{Related Topics}{section.525.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {526}Using the NPMResolver API in Your Portlets}{1503}{chapter.526}\\protected@file@percent }\n\\newlabel{using-the-npmresolver-api-in-your-portlets}{{526}{1503}{Using the NPMResolver API in Your Portlets}{chapter.526}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {527}Referencing an npm Module's Package to Improve Code Maintenance}{1505}{chapter.527}\\protected@file@percent }\n\\newlabel{referencing-an-npm-modules-package-to-improve-code-maintenance}{{527}{1505}{Referencing an npm Module's Package to Improve Code Maintenance}{chapter.527}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {527.1}Related Topics}{1506}{section.527.1}\\protected@file@percent }\n\\newlabel{related-topics-103}{{527.1}{1506}{Related Topics}{section.527.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {528}Obtaining an OSGi bundle's Dependency npm Package Descriptors}{1507}{chapter.528}\\protected@file@percent }\n\\newlabel{obtaining-an-osgi-bundles-dependency-npm-package-descriptors}{{528}{1507}{Obtaining an OSGi bundle's Dependency npm Package Descriptors}{chapter.528}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {528.1}Related Topics}{1508}{section.528.1}\\protected@file@percent }\n\\newlabel{related-topics-104}{{528.1}{1508}{Related Topics}{section.528.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {529}Automatic Single Page Applications}{1509}{chapter.529}\\protected@file@percent }\n\\newlabel{automatic-single-page-applications}{{529}{1509}{Automatic Single Page Applications}{chapter.529}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {529.1}The Benefits of SPAs}{1509}{section.529.1}\\protected@file@percent }\n\\newlabel{the-benefits-of-spas}{{529.1}{1509}{The Benefits of SPAs}{section.529.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {529.2}What is SennaJS?}{1510}{section.529.2}\\protected@file@percent }\n\\newlabel{what-is-sennajs}{{529.2}{1510}{What is SennaJS?}{section.529.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {530}Configuring SPA System Settings}{1511}{chapter.530}\\protected@file@percent }\n\\newlabel{configuring-spa-system-settings}{{530}{1511}{Configuring SPA System Settings}{chapter.530}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {530.1}Related Topics}{1511}{section.530.1}\\protected@file@percent }\n\\newlabel{related-topics-105}{{530.1}{1511}{Related Topics}{section.530.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {531}Disabling SPA}{1513}{chapter.531}\\protected@file@percent }\n\\newlabel{disabling-spa}{{531}{1513}{Disabling SPA}{chapter.531}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {531.1}Disabling SPA across an Instance}{1513}{section.531.1}\\protected@file@percent }\n\\newlabel{disabling-spa-across-an-instance}{{531.1}{1513}{Disabling SPA across an Instance}{section.531.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {531.2}Disabling SPA for a Portlet}{1513}{section.531.2}\\protected@file@percent }\n\\newlabel{disabling-spa-for-a-portlet}{{531.2}{1513}{Disabling SPA for a Portlet}{section.531.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {531.3}Disabling SPA for an Individual Element}{1514}{section.531.3}\\protected@file@percent }\n\\newlabel{disabling-spa-for-an-individual-element}{{531.3}{1514}{Disabling SPA for an Individual Element}{section.531.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {531.4}Related Topics}{1514}{section.531.4}\\protected@file@percent }\n\\newlabel{related-topics-106}{{531.4}{1514}{Related Topics}{section.531.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {532}Specifying How Resources Are Loaded During Navigation}{1515}{chapter.532}\\protected@file@percent }\n\\newlabel{specifying-how-resources-are-loaded-during-navigation}{{532}{1515}{Specifying How Resources Are Loaded During Navigation}{chapter.532}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {532.1}Related Topics}{1515}{section.532.1}\\protected@file@percent }\n\\newlabel{related-topics-107}{{532.1}{1515}{Related Topics}{section.532.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {533}Detaching Global Listeners}{1517}{chapter.533}\\protected@file@percent }\n\\newlabel{detaching-global-listeners}{{533}{1517}{Detaching Global Listeners}{chapter.533}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {533.1}Related Topics}{1518}{section.533.1}\\protected@file@percent }\n\\newlabel{related-topics-108}{{533.1}{1518}{Related Topics}{section.533.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {534}Applying Clay Styles to your App}{1519}{chapter.534}\\protected@file@percent }\n\\newlabel{applying-clay-styles-to-your-app}{{534}{1519}{Applying Clay Styles to your App}{chapter.534}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {535}Applying Clay Patterns to Navigation}{1521}{chapter.535}\\protected@file@percent }\n\\newlabel{applying-clay-patterns-to-navigation}{{535}{1521}{Applying Clay Patterns to Navigation}{chapter.535}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {535.1}{\\ignorespaces The navigation bar should be light for apps on the live site.}}{1522}{figure.535.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {535.2}{\\ignorespaces The navigation bar should be dark (inverted) in admin apps.}}{1522}{figure.535.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {535.1}Related topics}{1523}{section.535.1}\\protected@file@percent }\n\\newlabel{related-topics-109}{{535.1}{1523}{Related topics}{section.535.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {536}Implementing the Management Toolbar}{1525}{chapter.536}\\protected@file@percent }\n\\newlabel{implementing-the-management-toolbar}{{536}{1525}{Implementing the Management Toolbar}{chapter.536}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {537}Implementing the View Types}{1527}{chapter.537}\\protected@file@percent }\n\\newlabel{implementing-the-view-types}{{537}{1527}{Implementing the View Types}{chapter.537}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {538}Implementing the Card View}{1529}{chapter.538}\\protected@file@percent }\n\\newlabel{implementing-the-card-view}{{538}{1529}{Implementing the Card View}{chapter.538}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {538.1}{\\ignorespaces The Management Toolbar's card view gives a quick summary of the content's description and status.}}{1529}{figure.538.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {538.1}Related Topics}{1530}{section.538.1}\\protected@file@percent }\n\\newlabel{related-topics-110}{{538.1}{1530}{Related Topics}{section.538.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {539}Implementing the List View}{1531}{chapter.539}\\protected@file@percent }\n\\newlabel{implementing-the-list-view}{{539}{1531}{Implementing the List View}{chapter.539}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {539.1}{\\ignorespaces The Management Toolbar's list view gives the content's full description.}}{1531}{figure.539.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {539.1}Related Topics}{1532}{section.539.1}\\protected@file@percent }\n\\newlabel{related-topics-111}{{539.1}{1532}{Related Topics}{section.539.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {540}Implementing the Table View}{1533}{chapter.540}\\protected@file@percent }\n\\newlabel{implementing-the-table-view}{{540}{1533}{Implementing the Table View}{chapter.540}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {540.1}{\\ignorespaces The Management Toolbar's table view list the content's information in individual columns.}}{1533}{figure.540.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {540.1}Related Topics}{1534}{section.540.1}\\protected@file@percent }\n\\newlabel{related-topics-112}{{540.1}{1534}{Related Topics}{section.540.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {541}Updating the Search Iterator}{1535}{chapter.541}\\protected@file@percent }\n\\newlabel{updating-the-search-iterator}{{541}{1535}{Updating the Search Iterator}{chapter.541}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {541.1}Related Topics}{1535}{section.541.1}\\protected@file@percent }\n\\newlabel{related-topics-113}{{541.1}{1535}{Related Topics}{section.541.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {542}Filtering and Sorting Items with the Management Toolbar}{1537}{chapter.542}\\protected@file@percent }\n\\newlabel{filtering-and-sorting-items-with-the-management-toolbar}{{542}{1537}{Filtering and Sorting Items with the Management Toolbar}{chapter.542}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {542.1}Related Topics}{1538}{section.542.1}\\protected@file@percent }\n\\newlabel{related-topics-114}{{542.1}{1538}{Related Topics}{section.542.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {543}Configuring Your Application's Title and Back Link}{1539}{chapter.543}\\protected@file@percent }\n\\newlabel{configuring-your-applications-title-and-back-link}{{543}{1539}{Configuring Your Application's Title and Back Link}{chapter.543}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {543.1}{\\ignorespaces Adding a new blog entry displays the portlet title at the top, along with a back link.}}{1539}{figure.543.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {543.1}Related Topics}{1540}{section.543.1}\\protected@file@percent }\n\\newlabel{related-topics-115}{{543.1}{1540}{Related Topics}{section.543.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {544}Applying the Add Button Pattern}{1541}{chapter.544}\\protected@file@percent }\n\\newlabel{applying-the-add-button-pattern}{{544}{1541}{Applying the Add Button Pattern}{chapter.544}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {544.1}{\\ignorespaces The add button pattern consists of an \\texttt  {add-menu} tag and at least one \\texttt  {add-menu-item} tag.}}{1542}{figure.544.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {544.1}Related Topics}{1542}{section.544.1}\\protected@file@percent }\n\\newlabel{related-topics-116}{{544.1}{1542}{Related Topics}{section.544.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {545}Configuring Your Admin App's Actions Menu}{1543}{chapter.545}\\protected@file@percent }\n\\newlabel{configuring-your-admin-apps-actions-menu}{{545}{1543}{Configuring Your Admin App's Actions Menu}{chapter.545}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {545.1}{\\ignorespaces The upper right ellipsis menu contains most of the actions for the app.}}{1543}{figure.545.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {545.1}Related Topics}{1546}{section.545.1}\\protected@file@percent }\n\\newlabel{related-topics-117}{{545.1}{1546}{Related Topics}{section.545.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {546}Setting Empty Results Messages}{1547}{chapter.546}\\protected@file@percent }\n\\newlabel{setting-empty-results-messages}{{546}{1547}{Setting Empty Results Messages}{chapter.546}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {546.1}{\\ignorespaces This is a still frame from the Web Content portlet's empty results animation.}}{1547}{figure.546.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {546.2}{\\ignorespaces If you can use the add button to add entities to the app, use the empty state animation.}}{1548}{figure.546.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {546.3}{\\ignorespaces If you can use the add button to add entities to the app, use the search state animation.}}{1548}{figure.546.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {546.4}{\\ignorespaces If you can use the add button to add entities to the app, use the success state animation.}}{1549}{figure.546.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {546.1}Related Topics}{1549}{section.546.1}\\protected@file@percent }\n\\newlabel{related-topics-118}{{546.1}{1549}{Related Topics}{section.546.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {547}Search}{1551}{chapter.547}\\protected@file@percent }\n\\newlabel{search}{{547}{1551}{Search}{chapter.547}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {547.1}Basic Search Concepts}{1551}{section.547.1}\\protected@file@percent }\n\\newlabel{basic-search-concepts}{{547.1}{1551}{Basic Search Concepts}{section.547.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {547.2}Mapping Definitions}{1551}{section.547.2}\\protected@file@percent }\n\\newlabel{mapping-definitions}{{547.2}{1551}{Mapping Definitions}{section.547.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {547.3}Liferay Search Infrastructure}{1552}{section.547.3}\\protected@file@percent }\n\\newlabel{liferay-search-infrastructure}{{547.3}{1552}{Liferay Search Infrastructure}{section.547.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {548}Aggregations}{1553}{chapter.548}\\protected@file@percent }\n\\newlabel{aggregations}{{548}{1553}{Aggregations}{chapter.548}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {548.1}Using Aggregations}{1554}{section.548.1}\\protected@file@percent }\n\\newlabel{using-aggregations}{{548.1}{1554}{Using Aggregations}{section.548.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {548.2}External References}{1554}{section.548.2}\\protected@file@percent }\n\\newlabel{external-references}{{548.2}{1554}{External References}{section.548.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {548.3}Search Engine Connector Support}{1554}{section.548.3}\\protected@file@percent }\n\\newlabel{search-engine-connector-support}{{548.3}{1554}{Search Engine Connector Support}{section.548.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {548.4}New/Related APIs}{1554}{section.548.4}\\protected@file@percent }\n\\newlabel{newrelated-apis}{{548.4}{1554}{New/Related APIs}{section.548.4}{}}\n\\gdef \\LT@xix {\\LT@entry \n    {1}{120.02339pt}\\LT@entry \n    {1}{240.04678pt}\\LT@entry \n    {1}{109.68483pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {549}Creating Aggregations}{1557}{chapter.549}\\protected@file@percent }\n\\newlabel{creating-aggregations}{{549}{1557}{Creating Aggregations}{chapter.549}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {549.1}Instantiate and Construct the Aggregation}{1557}{section.549.1}\\protected@file@percent }\n\\newlabel{instantiate-and-construct-the-aggregation}{{549.1}{1557}{Instantiate and Construct the Aggregation}{section.549.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {549.2}Build the Search Query}{1557}{section.549.2}\\protected@file@percent }\n\\newlabel{build-the-search-query}{{549.2}{1557}{Build the Search Query}{section.549.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {549.3}Execute the Search Query}{1558}{section.549.3}\\protected@file@percent }\n\\newlabel{execute-the-search-query}{{549.3}{1558}{Execute the Search Query}{section.549.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {549.4}Process the response}{1558}{section.549.4}\\protected@file@percent }\n\\newlabel{process-the-response}{{549.4}{1558}{Process the response}{section.549.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {550}Statistical Aggregations}{1559}{chapter.550}\\protected@file@percent }\n\\newlabel{statistical-aggregations}{{550}{1559}{Statistical Aggregations}{chapter.550}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {550.1}\\texttt  {StatsRequest}}{1559}{section.550.1}\\protected@file@percent }\n\\newlabel{statsrequest}{{550.1}{1559}{\\texorpdfstring {\\texttt {StatsRequest}}{StatsRequest}}{section.550.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {550.2}\\texttt  {StatsResponse}}{1560}{section.550.2}\\protected@file@percent }\n\\newlabel{statsresponse}{{550.2}{1560}{\\texorpdfstring {\\texttt {StatsResponse}}{StatsResponse}}{section.550.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {550.3}Using the Legacy \\texttt  {Stats} Object}{1561}{section.550.3}\\protected@file@percent }\n\\newlabel{using-the-legacy-stats-object}{{550.3}{1561}{\\texorpdfstring {Using the Legacy \\texttt {Stats} Object}{Using the Legacy Stats Object}}{section.550.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {550.4}External References}{1561}{section.550.4}\\protected@file@percent }\n\\newlabel{external-references-1}{{550.4}{1561}{External References}{section.550.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {550.5}Search Engine Connector Support}{1561}{section.550.5}\\protected@file@percent }\n\\newlabel{search-engine-connector-support-1}{{550.5}{1561}{Search Engine Connector Support}{section.550.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {550.6}New/Related APIs}{1561}{section.550.6}\\protected@file@percent }\n\\newlabel{newrelated-apis-1}{{550.6}{1561}{New/Related APIs}{section.550.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {550.7}Deprecated APIs}{1562}{section.550.7}\\protected@file@percent }\n\\newlabel{deprecated-apis}{{550.7}{1562}{Deprecated APIs}{section.550.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {551}Model Entity Indexing Framework}{1563}{chapter.551}\\protected@file@percent }\n\\newlabel{model-entity-indexing-framework}{{551}{1563}{Model Entity Indexing Framework}{chapter.551}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {551.1}Search and Indexing Overview}{1563}{section.551.1}\\protected@file@percent }\n\\newlabel{search-and-indexing-overview}{{551.1}{1563}{Search and Indexing Overview}{section.551.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {551.2}Mapping the Composite Search and Indexing Framework to \\texttt  {Indexer}/\\texttt  {BaseIndexer} Code}{1563}{section.551.2}\\protected@file@percent }\n\\newlabel{mapping-the-composite-search-and-indexing-framework-to-indexerbaseindexer-code}{{551.2}{1563}{\\texorpdfstring {Mapping the Composite Search and Indexing Framework to \\texttt {Indexer}/\\texttt {BaseIndexer} Code}{Mapping the Composite Search and Indexing Framework to Indexer/BaseIndexer Code}}{section.551.2}{}}\n\\gdef \\LT@xx {\\LT@entry \n    {1}{177.9261pt}\\LT@entry \n    {1}{183.9261pt}\\LT@entry \n    {1}{107.90279pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {551.3}Permissions-Aware Searching and Indexing}{1564}{section.551.3}\\protected@file@percent }\n\\newlabel{permissions-aware-searching-and-indexing}{{551.3}{1564}{Permissions-Aware Searching and Indexing}{section.551.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {551.4}Annotating Service Methods to Trigger Indexing}{1564}{section.551.4}\\protected@file@percent }\n\\newlabel{annotating-service-methods-to-trigger-indexing}{{551.4}{1564}{Annotating Service Methods to Trigger Indexing}{section.551.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {551.5}Search and Localization: a Cheat Sheet}{1565}{section.551.5}\\protected@file@percent }\n\\newlabel{search-and-localization-a-cheat-sheet}{{551.5}{1565}{Search and Localization: a Cheat Sheet}{section.551.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {552}Indexing Model Entities}{1567}{chapter.552}\\protected@file@percent }\n\\newlabel{indexing-model-entities}{{552}{1567}{Indexing Model Entities}{chapter.552}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {552.1}Contributing Model Entity Fields to the Index}{1567}{section.552.1}\\protected@file@percent }\n\\newlabel{contributing-model-entity-fields-to-the-index}{{552.1}{1567}{Contributing Model Entity Fields to the Index}{section.552.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {552.2}Configure Re-Indexing and Batch Indexing Behavior}{1568}{section.552.2}\\protected@file@percent }\n\\newlabel{configure-re-indexing-and-batch-indexing-behavior}{{552.2}{1568}{Configure Re-Indexing and Batch Indexing Behavior}{section.552.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {552.3}Contribute Fields to Every Document}{1570}{section.552.3}\\protected@file@percent }\n\\newlabel{contribute-fields-to-every-document}{{552.3}{1570}{Contribute Fields to Every Document}{section.552.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {553}Searching the Index for Model Entities}{1571}{chapter.553}\\protected@file@percent }\n\\newlabel{searching-the-index-for-model-entities}{{553}{1571}{Searching the Index for Model Entities}{chapter.553}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {553.1}Adding your Model Entity's Terms to the Query}{1571}{section.553.1}\\protected@file@percent }\n\\newlabel{adding-your-model-entitys-terms-to-the-query}{{553.1}{1571}{Adding your Model Entity's Terms to the Query}{section.553.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {553.2}Contributing Query Clauses to Every Search}{1572}{section.553.2}\\protected@file@percent }\n\\newlabel{contributing-query-clauses-to-every-search}{{553.2}{1572}{Contributing Query Clauses to Every Search}{section.553.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {553.3}Pre-Filtering}{1572}{section.553.3}\\protected@file@percent }\n\\newlabel{pre-filtering}{{553.3}{1572}{Pre-Filtering}{section.553.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {554}Returning Results}{1575}{chapter.554}\\protected@file@percent }\n\\newlabel{returning-results}{{554}{1575}{Returning Results}{chapter.554}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {554.1}Creating a Results Summary}{1575}{section.554.1}\\protected@file@percent }\n\\newlabel{creating-a-results-summary}{{554.1}{1575}{Creating a Results Summary}{section.554.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {554.2}Controlling the Visibility of Model Entities}{1576}{section.554.2}\\protected@file@percent }\n\\newlabel{controlling-the-visibility-of-model-entities}{{554.2}{1576}{Controlling the Visibility of Model Entities}{section.554.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {555}Search Service Registration}{1577}{chapter.555}\\protected@file@percent }\n\\newlabel{search-service-registration}{{555}{1577}{Search Service Registration}{chapter.555}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {556}Search Queries and Filters}{1579}{chapter.556}\\protected@file@percent }\n\\newlabel{search-queries-and-filters}{{556}{1579}{Search Queries and Filters}{chapter.556}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.1}Queries and Filters in Liferay's Search API}{1579}{section.556.1}\\protected@file@percent }\n\\newlabel{queries-and-filters-in-liferays-search-api}{{556.1}{1579}{Queries and Filters in Liferay's Search API}{section.556.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.2}Supported Query Types}{1580}{section.556.2}\\protected@file@percent }\n\\newlabel{supported-query-types}{{556.2}{1580}{Supported Query Types}{section.556.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.3}Full Text Queries}{1580}{section.556.3}\\protected@file@percent }\n\\newlabel{full-text-queries}{{556.3}{1580}{Full Text Queries}{section.556.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.4}Term Queries}{1580}{section.556.4}\\protected@file@percent }\n\\newlabel{term-queries}{{556.4}{1580}{Term Queries}{section.556.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.5}Compound Queries}{1581}{section.556.5}\\protected@file@percent }\n\\newlabel{compound-queries}{{556.5}{1581}{Compound Queries}{section.556.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.6}Joining Queries}{1581}{section.556.6}\\protected@file@percent }\n\\newlabel{joining-queries}{{556.6}{1581}{Joining Queries}{section.556.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.7}Geo Queries}{1581}{section.556.7}\\protected@file@percent }\n\\newlabel{geo-queries}{{556.7}{1581}{Geo Queries}{section.556.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.8}Specialized Queries}{1581}{section.556.8}\\protected@file@percent }\n\\newlabel{specialized-queries}{{556.8}{1581}{Specialized Queries}{section.556.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.9}Other Queries}{1582}{section.556.9}\\protected@file@percent }\n\\newlabel{other-queries}{{556.9}{1582}{Other Queries}{section.556.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.10}Using Queries}{1582}{section.556.10}\\protected@file@percent }\n\\newlabel{using-queries}{{556.10}{1582}{Using Queries}{section.556.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.11}Search Queries in Liferay's Code}{1582}{section.556.11}\\protected@file@percent }\n\\newlabel{search-queries-in-liferays-code}{{556.11}{1582}{Search Queries in Liferay's Code}{section.556.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.12}External References}{1582}{section.556.12}\\protected@file@percent }\n\\newlabel{external-references-2}{{556.12}{1582}{External References}{section.556.12}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.13}Search Engine Connector Support}{1583}{section.556.13}\\protected@file@percent }\n\\newlabel{search-engine-connector-support-2}{{556.13}{1583}{Search Engine Connector Support}{section.556.13}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {556.14}New/Related APIs}{1583}{section.556.14}\\protected@file@percent }\n\\newlabel{newrelated-apis-2}{{556.14}{1583}{New/Related APIs}{section.556.14}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {557}Building Search Queries and Filters}{1585}{chapter.557}\\protected@file@percent }\n\\newlabel{building-search-queries-and-filters}{{557}{1585}{Building Search Queries and Filters}{chapter.557}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {557.1}Queries}{1585}{section.557.1}\\protected@file@percent }\n\\newlabel{queries}{{557.1}{1585}{Queries}{section.557.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {557.2}Declare Gradle Dependencies}{1585}{section.557.2}\\protected@file@percent }\n\\newlabel{declare-gradle-dependencies}{{557.2}{1585}{Declare Gradle Dependencies}{section.557.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {557.3}Reference the Search Services}{1585}{section.557.3}\\protected@file@percent }\n\\newlabel{reference-the-search-services}{{557.3}{1585}{Reference the Search Services}{section.557.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {557.4}Build the Search Query}{1586}{section.557.4}\\protected@file@percent }\n\\newlabel{build-the-search-query-1}{{557.4}{1586}{Build the Search Query}{section.557.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {557.5}Build the Search Request}{1586}{section.557.5}\\protected@file@percent }\n\\newlabel{build-the-search-request}{{557.5}{1586}{Build the Search Request}{section.557.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {557.6}Execute the Search Request}{1587}{section.557.6}\\protected@file@percent }\n\\newlabel{execute-the-search-request}{{557.6}{1587}{Execute the Search Request}{section.557.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {557.7}Process the Search Response}{1587}{section.557.7}\\protected@file@percent }\n\\newlabel{process-the-search-response}{{557.7}{1587}{Process the Search Response}{section.557.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {557.8}Search Insights: Request and Response Strings}{1588}{section.557.8}\\protected@file@percent }\n\\newlabel{search-insights-request-and-response-strings}{{557.8}{1588}{Search Insights: Request and Response Strings}{section.557.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {557.9}Queries Example}{1589}{section.557.9}\\protected@file@percent }\n\\newlabel{queries-example}{{557.9}{1589}{Queries Example}{section.557.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {557.10}Filters}{1590}{section.557.10}\\protected@file@percent }\n\\newlabel{filters}{{557.10}{1590}{Filters}{section.557.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {557.11}Legacy Filters in Legacy Search Calls}{1591}{section.557.11}\\protected@file@percent }\n\\newlabel{legacy-filters-in-legacy-search-calls}{{557.11}{1591}{Legacy Filters in Legacy Search Calls}{section.557.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {557.12}Discovering Indexed Fields}{1591}{section.557.12}\\protected@file@percent }\n\\newlabel{discovering-indexed-fields}{{557.12}{1591}{Discovering Indexed Fields}{section.557.12}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {558}Segmentation and Personalization}{1593}{chapter.558}\\protected@file@percent }\n\\newlabel{segmentation-and-personalization}{{558}{1593}{Segmentation and Personalization}{chapter.558}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {558.1}Managing segments}{1593}{section.558.1}\\protected@file@percent }\n\\newlabel{managing-segments}{{558.1}{1593}{Managing segments}{section.558.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {558.2}Extending Segment Criteria}{1594}{section.558.2}\\protected@file@percent }\n\\newlabel{extending-segment-criteria}{{558.2}{1594}{Extending Segment Criteria}{section.558.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {558.3}\\texttt  {RequestContextContributor}}{1594}{section.558.3}\\protected@file@percent }\n\\newlabel{requestcontextcontributor}{{558.3}{1594}{\\texorpdfstring {\\texttt {RequestContextContributor}}{RequestContextContributor}}{section.558.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {558.4}\\texttt  {SegmentsCriteriaContributor}}{1594}{section.558.4}\\protected@file@percent }\n\\newlabel{segmentscriteriacontributor}{{558.4}{1594}{\\texorpdfstring {\\texttt {SegmentsCriteriaContributor}}{SegmentsCriteriaContributor}}{section.558.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {558.1}{\\ignorespaces Learn more about a \\texttt  {RequestContextContributor} by viewing how it's used.}}{1595}{figure.558.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {558.2}{\\ignorespaces Learn more about a \\texttt  {SegmentsCriteriaContributor} by viewing how it's used.}}{1596}{figure.558.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {559}Segment Management}{1597}{chapter.559}\\protected@file@percent }\n\\newlabel{segment-management}{{559}{1597}{Segment Management}{chapter.559}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {559.1}Defining a Segment}{1597}{section.559.1}\\protected@file@percent }\n\\newlabel{defining-a-segment}{{559.1}{1597}{Defining a Segment}{section.559.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {559.2}Manual Segment Member Assignments}{1597}{section.559.2}\\protected@file@percent }\n\\newlabel{manual-segment-member-assignments}{{559.2}{1597}{Manual Segment Member Assignments}{section.559.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {559.3}Retrieving Segments}{1598}{section.559.3}\\protected@file@percent }\n\\newlabel{retrieving-segments}{{559.3}{1598}{Retrieving Segments}{section.559.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {559.4}Retrieving segment members}{1598}{section.559.4}\\protected@file@percent }\n\\newlabel{retrieving-segment-members}{{559.4}{1598}{Retrieving segment members}{section.559.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {560}Creating a Request Context Contributor}{1599}{chapter.560}\\protected@file@percent }\n\\newlabel{creating-a-request-context-contributor}{{560}{1599}{Creating a Request Context Contributor}{chapter.560}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {560.1}{\\ignorespaces The sample field appears.}}{1601}{figure.560.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {561}Creating a Segment Criteria Contributor}{1603}{chapter.561}\\protected@file@percent }\n\\newlabel{creating-a-segment-criteria-contributor}{{561}{1603}{Creating a Segment Criteria Contributor}{chapter.561}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {561.1}Creating the Entity Model}{1603}{section.561.1}\\protected@file@percent }\n\\newlabel{creating-the-entity-model}{{561.1}{1603}{Creating the Entity Model}{section.561.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {561.2}Creating the \\texttt  {ODataRetriever}}{1604}{section.561.2}\\protected@file@percent }\n\\newlabel{creating-the-odataretriever}{{561.2}{1604}{\\texorpdfstring {Creating the \\texttt {ODataRetriever}}{Creating the ODataRetriever}}{section.561.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {561.3}Creating the \\texttt  {SegmentsCriteriaContributor}}{1606}{section.561.3}\\protected@file@percent }\n\\newlabel{creating-the-segmentscriteriacontributor}{{561.3}{1606}{\\texorpdfstring {Creating the \\texttt {SegmentsCriteriaContributor}}{Creating the SegmentsCriteriaContributor}}{section.561.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {561.1}{\\ignorespaces The sample field appears.}}{1608}{figure.561.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {562}ServiceContext}{1609}{chapter.562}\\protected@file@percent }\n\\newlabel{servicecontext}{{562}{1609}{ServiceContext}{chapter.562}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {562.1}Service Context Fields}{1609}{section.562.1}\\protected@file@percent }\n\\newlabel{service-context-fields}{{562.1}{1609}{Service Context Fields}{section.562.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {562.2}Creating and Populating a Service Context}{1611}{section.562.2}\\protected@file@percent }\n\\newlabel{creating-and-populating-a-service-context}{{562.2}{1611}{Creating and Populating a Service Context}{section.562.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {562.3}Creating and Populating a Service Context in JavaScript}{1611}{section.562.3}\\protected@file@percent }\n\\newlabel{creating-and-populating-a-service-context-in-javascript}{{562.3}{1611}{Creating and Populating a Service Context in JavaScript}{section.562.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {562.4}Accessing Service Context Data}{1612}{section.562.4}\\protected@file@percent }\n\\newlabel{accessing-service-context-data}{{562.4}{1612}{Accessing Service Context Data}{section.562.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {562.1}{\\ignorespaces When you invoke a service from Liferay's JSON web services page, you can view the result of your service invocation as well as example code for invoking the service via JavaScript, curl, or URL.}}{1613}{figure.562.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {562.5}Related Topics}{1615}{section.562.5}\\protected@file@percent }\n\\newlabel{related-topics-119}{{562.5}{1615}{Related Topics}{section.562.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {563}Injecting Service Components into Integration Tests}{1617}{chapter.563}\\protected@file@percent }\n\\newlabel{injecting-service-components-into-integration-tests}{{563}{1617}{Injecting Service Components into Integration Tests}{chapter.563}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {563.1}Related Topics}{1618}{section.563.1}\\protected@file@percent }\n\\newlabel{related-topics-120}{{563.1}{1618}{Related Topics}{section.563.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {564}Upgrade Processes}{1619}{chapter.564}\\protected@file@percent }\n\\newlabel{upgrade-processes}{{564}{1619}{Upgrade Processes}{chapter.564}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {564.1}{\\ignorespaces In a registrator class, the developer specifies a registration for each schema version upgrade. The upgrade steps handle the database updates.}}{1621}{figure.564.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {565}Creating Upgrade Processes for Modules}{1623}{chapter.565}\\protected@file@percent }\n\\newlabel{creating-upgrade-processes-for-modules}{{565}{1623}{Creating Upgrade Processes for Modules}{chapter.565}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {565.1}Related Topics}{1627}{section.565.1}\\protected@file@percent }\n\\newlabel{related-topics-121}{{565.1}{1627}{Related Topics}{section.565.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {566}Upgrade Processes for Former Service Builder Plugins}{1629}{chapter.566}\\protected@file@percent }\n\\newlabel{upgrade-processes-for-former-service-builder-plugins}{{566}{1629}{Upgrade Processes for Former Service Builder Plugins}{chapter.566}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {566.1}Related Topics}{1632}{section.566.1}\\protected@file@percent }\n\\newlabel{related-topics-122}{{566.1}{1632}{Related Topics}{section.566.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {567}Upgrading Data Schemas in Development}{1633}{chapter.567}\\protected@file@percent }\n\\newlabel{upgrading-data-schemas-in-development}{{567}{1633}{Upgrading Data Schemas in Development}{chapter.567}{}}\n\\gdef \\LT@xxi {\\LT@entry \n    {1}{244.03456pt}\\LT@entry \n    {1}{225.72044pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {567.1}Related Topics}{1634}{section.567.1}\\protected@file@percent }\n\\newlabel{related-topics-123}{{567.1}{1634}{Related Topics}{section.567.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {568}Managing User-Associated Data Stored by Custom Applications}{1635}{chapter.568}\\protected@file@percent }\n\\newlabel{managing-user-associated-data-stored-by-custom-applications}{{568}{1635}{Managing User-Associated Data Stored by Custom Applications}{chapter.568}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {569}Adding the UAD Framework to a Service Builder Application}{1637}{chapter.569}\\protected@file@percent }\n\\newlabel{adding-the-uad-framework-to-a-service-builder-application}{{569}{1637}{Adding the UAD Framework to a Service Builder Application}{chapter.569}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {569.1}Update the Service Module}{1637}{section.569.1}\\protected@file@percent }\n\\newlabel{update-the-service-module}{{569.1}{1637}{Update the Service Module}{section.569.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {569.2}Include Dependencies}{1637}{section.569.2}\\protected@file@percent }\n\\newlabel{include-dependencies}{{569.2}{1637}{Include Dependencies}{section.569.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {569.3}Choose the Fields to Anonymize}{1637}{section.569.3}\\protected@file@percent }\n\\newlabel{choose-the-fields-to-anonymize}{{569.3}{1637}{Choose the Fields to Anonymize}{section.569.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {569.4}Update the UAD Module}{1638}{section.569.4}\\protected@file@percent }\n\\newlabel{update-the-uad-module}{{569.4}{1638}{Update the UAD Module}{section.569.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {569.5}Include Dependencies}{1638}{section.569.5}\\protected@file@percent }\n\\newlabel{include-dependencies-1}{{569.5}{1638}{Include Dependencies}{section.569.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {569.6}Provide Your App's Name to the UI}{1638}{section.569.6}\\protected@file@percent }\n\\newlabel{provide-your-apps-name-to-the-ui}{{569.6}{1638}{Provide Your App's Name to the UI}{section.569.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {570}Enhancing the Data Erasure UI}{1639}{chapter.570}\\protected@file@percent }\n\\newlabel{enhancing-the-data-erasure-ui}{{570}{1639}{Enhancing the Data Erasure UI}{chapter.570}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {570.1}Filtering and Searching in the Data Erasure UI}{1639}{section.570.1}\\protected@file@percent }\n\\newlabel{filtering-and-searching-in-the-data-erasure-ui}{{570.1}{1639}{Filtering and Searching in the Data Erasure UI}{section.570.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {570.1}{\\ignorespaces Items in the Personal Data Erasure screen can be filtered by scope.}}{1640}{figure.570.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {570.2}Hierarchy Display}{1640}{section.570.2}\\protected@file@percent }\n\\newlabel{hierarchy-display}{{570.2}{1640}{Hierarchy Display}{section.570.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {570.3}UAD Hierarchy Declaration}{1640}{section.570.3}\\protected@file@percent }\n\\newlabel{uad-hierarchy-declaration}{{570.3}{1640}{UAD Hierarchy Declaration}{section.570.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {570.2}{\\ignorespaces Hierarchical representation of nested entities is useful for administrators reviewing User data for possible deletion.}}{1641}{figure.570.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {570.4}Add methods to UADDisplay}{1641}{section.570.4}\\protected@file@percent }\n\\newlabel{add-methods-to-uaddisplay}{{570.4}{1641}{Add methods to UADDisplay}{section.570.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {571}Filtering and Searching UAD-Marked Entities}{1645}{chapter.571}\\protected@file@percent }\n\\newlabel{filtering-and-searching-uad-marked-entities}{{571}{1645}{Filtering and Searching UAD-Marked Entities}{chapter.571}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {571.1}Filtering}{1645}{section.571.1}\\protected@file@percent }\n\\newlabel{filtering}{{571.1}{1645}{Filtering}{section.571.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {571.2}Search}{1645}{section.571.2}\\protected@file@percent }\n\\newlabel{search-1}{{571.2}{1645}{Search}{section.571.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {572}Web Experience Management}{1649}{chapter.572}\\protected@file@percent }\n\\newlabel{web-experience-management}{{572}{1649}{Web Experience Management}{chapter.572}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {573}Page Fragments}{1651}{chapter.573}\\protected@file@percent }\n\\newlabel{page-fragments}{{573}{1651}{Page Fragments}{chapter.573}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {573.1}Developing Page Fragments}{1651}{section.573.1}\\protected@file@percent }\n\\newlabel{developing-page-fragments}{{573.1}{1651}{Developing Page Fragments}{section.573.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {573.2}Making a Fragment Configurable}{1651}{section.573.2}\\protected@file@percent }\n\\newlabel{making-a-fragment-configurable}{{573.2}{1651}{Making a Fragment Configurable}{section.573.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {573.3}Fragments CLI}{1652}{section.573.3}\\protected@file@percent }\n\\newlabel{fragments-cli}{{573.3}{1652}{Fragments CLI}{section.573.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {573.4}Contributed Collections}{1652}{section.573.4}\\protected@file@percent }\n\\newlabel{contributed-collections}{{573.4}{1652}{Contributed Collections}{section.573.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {573.5}Fragment Specific Tags}{1652}{section.573.5}\\protected@file@percent }\n\\newlabel{fragment-specific-tags}{{573.5}{1652}{Fragment Specific Tags}{section.573.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {573.6}Recommendations and Best Practices}{1653}{section.573.6}\\protected@file@percent }\n\\newlabel{recommendations-and-best-practices}{{573.6}{1653}{Recommendations and Best Practices}{section.573.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {573.7}CSS}{1653}{section.573.7}\\protected@file@percent }\n\\newlabel{css}{{573.7}{1653}{CSS}{section.573.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {573.8}JavaScript}{1653}{section.573.8}\\protected@file@percent }\n\\newlabel{javascript}{{573.8}{1653}{JavaScript}{section.573.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {574}Developing Fragments}{1655}{chapter.574}\\protected@file@percent }\n\\newlabel{developing-fragments}{{574}{1655}{Developing Fragments}{chapter.574}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {574.1}Creating a Section}{1655}{section.574.1}\\protected@file@percent }\n\\newlabel{creating-a-section}{{574.1}{1655}{Creating a Section}{section.574.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {574.1}{\\ignorespaces The Fragment editor provides autocomplete for Liferay Fragment specific tags.}}{1656}{figure.574.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {574.2}{\\ignorespaces The Fragment editor with HTML and CSS code and a live preview.}}{1657}{figure.574.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {574.2}Creating a Component}{1657}{section.574.2}\\protected@file@percent }\n\\newlabel{creating-a-component}{{574.2}{1657}{Creating a Component}{section.574.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {575}Making a Fragment Configurable}{1659}{chapter.575}\\protected@file@percent }\n\\newlabel{making-a-fragment-configurable-1}{{575}{1659}{Making a Fragment Configurable}{chapter.575}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {575.1}{\\ignorespaces Switch from the Code tab to the Configuration tab to create your configuration logic.}}{1659}{figure.575.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {575.2}{\\ignorespaces You can click your Fragment to view its configuration options.}}{1661}{figure.575.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {576}Managing Fragments and Collections}{1663}{chapter.576}\\protected@file@percent }\n\\newlabel{managing-fragments-and-collections}{{576}{1663}{Managing Fragments and Collections}{chapter.576}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {576.1}Collections Management Menu}{1663}{section.576.1}\\protected@file@percent }\n\\newlabel{collections-management-menu}{{576.1}{1663}{Collections Management Menu}{section.576.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {576.2}Fragment Management Menu}{1663}{section.576.2}\\protected@file@percent }\n\\newlabel{fragment-management-menu}{{576.2}{1663}{Fragment Management Menu}{section.576.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {576.3}Propagating Fragment Changes Automatically}{1664}{section.576.3}\\protected@file@percent }\n\\newlabel{propagating-fragment-changes-automatically}{{576.3}{1664}{Propagating Fragment Changes Automatically}{section.576.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {576.1}{\\ignorespaces Once Fragment propagation is enabled, developers can automatically propagate Fragment changes to all pages using them.}}{1664}{figure.576.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {576.2}{\\ignorespaces You're notified when automatic propagation is enabled.}}{1665}{figure.576.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {577}Developing A Fragment Using Desktop Tools}{1667}{chapter.577}\\protected@file@percent }\n\\newlabel{developing-a-fragment-using-desktop-tools}{{577}{1667}{Developing A Fragment Using Desktop Tools}{chapter.577}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {577.1}Collection Format}{1667}{section.577.1}\\protected@file@percent }\n\\newlabel{collection-format}{{577.1}{1667}{Collection Format}{section.577.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {577.2}Fragment CLI}{1668}{section.577.2}\\protected@file@percent }\n\\newlabel{fragment-cli}{{577.2}{1668}{Fragment CLI}{section.577.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {577.3}Creating Collections}{1668}{section.577.3}\\protected@file@percent }\n\\newlabel{creating-collections}{{577.3}{1668}{Creating Collections}{section.577.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {577.4}Creating Fragments}{1669}{section.577.4}\\protected@file@percent }\n\\newlabel{creating-fragments}{{577.4}{1669}{Creating Fragments}{section.577.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {577.5}Importing and Exporting Fragments}{1669}{section.577.5}\\protected@file@percent }\n\\newlabel{importing-and-exporting-fragments}{{577.5}{1669}{Importing and Exporting Fragments}{section.577.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {578}Creating a Contributed Fragment Collection}{1671}{chapter.578}\\protected@file@percent }\n\\newlabel{creating-a-contributed-fragment-collection}{{578}{1671}{Creating a Contributed Fragment Collection}{chapter.578}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {578.1}Create a Module}{1671}{section.578.1}\\protected@file@percent }\n\\newlabel{create-a-module}{{578.1}{1671}{Create a Module}{section.578.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {578.2}Create the Java Class}{1671}{section.578.2}\\protected@file@percent }\n\\newlabel{create-the-java-class}{{578.2}{1671}{Create the Java Class}{section.578.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {578.3}Create the Resources}{1672}{section.578.3}\\protected@file@percent }\n\\newlabel{create-the-resources}{{578.3}{1672}{Create the Resources}{section.578.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {578.4}Configuring the Metadata}{1673}{section.578.4}\\protected@file@percent }\n\\newlabel{configuring-the-metadata}{{578.4}{1673}{Configuring the Metadata}{section.578.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {578.5}Providing Thumbnail Images}{1673}{section.578.5}\\protected@file@percent }\n\\newlabel{providing-thumbnail-images}{{578.5}{1673}{Providing Thumbnail Images}{section.578.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {578.6}Providing Language Keys}{1673}{section.578.6}\\protected@file@percent }\n\\newlabel{providing-language-keys}{{578.6}{1673}{Providing Language Keys}{section.578.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {578.7}Deploy the Contributed Fragment Collection}{1674}{section.578.7}\\protected@file@percent }\n\\newlabel{deploy-the-contributed-fragment-collection}{{578.7}{1674}{Deploy the Contributed Fragment Collection}{section.578.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {579}Including Default Resources in Fragments}{1675}{chapter.579}\\protected@file@percent }\n\\newlabel{including-default-resources-in-fragments}{{579}{1675}{Including Default Resources in Fragments}{chapter.579}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {579.1}{\\ignorespaces Any Fragment from the Fragment Collection has access to the uploaded resources.}}{1676}{figure.579.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {580}Supporting Custom Content Types in Content and Display Pages}{1677}{chapter.580}\\protected@file@percent }\n\\newlabel{supporting-custom-content-types-in-content-and-display-pages}{{580}{1677}{Supporting Custom Content Types in Content and Display Pages}{chapter.580}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {581}Mapping a Content Type to a Page}{1679}{chapter.581}\\protected@file@percent }\n\\newlabel{mapping-a-content-type-to-a-page}{{581}{1679}{Mapping a Content Type to a Page}{chapter.581}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {581.1}{\\ignorespaces After creating the \\texttt  {*InfoDisplayContributor} class, you can create Display Page Templates and map them to your custom model.}}{1680}{figure.581.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {582}Specifying the Fields of a Custom Content Type}{1681}{chapter.582}\\protected@file@percent }\n\\newlabel{specifying-the-fields-of-a-custom-content-type}{{582}{1681}{Specifying the Fields of a Custom Content Type}{chapter.582}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {582.1}{\\ignorespaces After creating the \\texttt  {*InfoDisplayContributorField} class, your custom content type has a new field to map.}}{1683}{figure.582.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {583}Providing Friendly URLs for a Custom Content Type}{1685}{chapter.583}\\protected@file@percent }\n\\newlabel{providing-friendly-urls-for-a-custom-content-type}{{583}{1685}{Providing Friendly URLs for a Custom Content Type}{chapter.583}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {584}Integrating Display Pages into Content Creation}{1691}{chapter.584}\\protected@file@percent }\n\\newlabel{integrating-display-pages-into-content-creation}{{584}{1691}{Integrating Display Pages into Content Creation}{chapter.584}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {584.1}Display Page Taglib Example}{1691}{section.584.1}\\protected@file@percent }\n\\newlabel{display-page-taglib-example}{{584.1}{1691}{Display Page Taglib Example}{section.584.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {584.1}{\\ignorespaces You need to add the Display Page selection to your content type's create/edit page to define the Display Page for each instance of that asset.}}{1691}{figure.584.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {585}Screen Navigation Framework}{1693}{chapter.585}\\protected@file@percent }\n\\newlabel{screen-navigation-framework}{{585}{1693}{Screen Navigation Framework}{chapter.585}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {585.1}Using the Framework for Your Application}{1693}{section.585.1}\\protected@file@percent }\n\\newlabel{using-the-framework-for-your-application}{{585.1}{1693}{Using the Framework for Your Application}{section.585.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {585.1}{\\ignorespaces The User Management application has three Screen Navigation Categories: General, Contact, and Preference; and each of those have a number of Screen Navigation Entries}}{1694}{figure.585.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {585.2}{\\ignorespaces Many application only use Screen Navigation Categories for their functionality, and don't have Screen Navigation Entries. For Blogs, Entries and Images are Categories with no Entries.}}{1694}{figure.585.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {585.2}Adding Custom Screens to Liferay Applications}{1695}{section.585.2}\\protected@file@percent }\n\\newlabel{adding-custom-screens-to-liferay-applications}{{585.2}{1695}{Adding Custom Screens to Liferay Applications}{section.585.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {586}Using Screen Navigation for Your Application}{1697}{chapter.586}\\protected@file@percent }\n\\newlabel{using-screen-navigation-for-your-application}{{586}{1697}{Using Screen Navigation for Your Application}{chapter.586}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {586.1}Adding Screens to Your Application's Back-end}{1697}{section.586.1}\\protected@file@percent }\n\\newlabel{adding-screens-to-your-applications-back-end}{{586.1}{1697}{Adding Screens to Your Application's Back-end}{section.586.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {586.2}Adding Screens to Your Application's Front-end}{1700}{section.586.2}\\protected@file@percent }\n\\newlabel{adding-screens-to-your-applications-front-end}{{586.2}{1700}{Adding Screens to Your Application's Front-end}{section.586.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {587}Extending Categories Administration}{1701}{chapter.587}\\protected@file@percent }\n\\newlabel{extending-categories-administration}{{587}{1701}{Extending Categories Administration}{chapter.587}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {588}Developing a Fragment Renderer}{1703}{chapter.588}\\protected@file@percent }\n\\newlabel{developing-a-fragment-renderer}{{588}{1703}{Developing a Fragment Renderer}{chapter.588}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {588.1}Implementing the FragmentRenderer Interface}{1703}{section.588.1}\\protected@file@percent }\n\\newlabel{implementing-the-fragmentrenderer-interface}{{588.1}{1703}{Implementing the FragmentRenderer Interface}{section.588.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {588.2}Leveraging the FragmentRendererContext}{1704}{section.588.2}\\protected@file@percent }\n\\newlabel{leveraging-the-fragmentrenderercontext}{{588.2}{1704}{Leveraging the FragmentRendererContext}{section.588.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {588.3}Rendering JSPs}{1704}{section.588.3}\\protected@file@percent }\n\\newlabel{rendering-jsps}{{588.3}{1704}{Rendering JSPs}{section.588.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {588.4}Choosing When to Display a Component}{1705}{section.588.4}\\protected@file@percent }\n\\newlabel{choosing-when-to-display-a-component}{{588.4}{1705}{Choosing When to Display a Component}{section.588.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {588.5}Translating the Collection Language Key}{1706}{section.588.5}\\protected@file@percent }\n\\newlabel{translating-the-collection-language-key}{{588.5}{1706}{Translating the Collection Language Key}{section.588.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {589}Creating a Fragment Renderer}{1707}{chapter.589}\\protected@file@percent }\n\\newlabel{creating-a-fragment-renderer}{{589}{1707}{Creating a Fragment Renderer}{chapter.589}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {589.1}{\\ignorespaces The new Fragment Renderer appears in its defined component collection.}}{1708}{figure.589.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {589.2}{\\ignorespaces When adding the new Fragment Renderer to a page, the context information is displayed.}}{1709}{figure.589.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {590}Web Services}{1711}{chapter.590}\\protected@file@percent }\n\\newlabel{web-services}{{590}{1711}{Web Services}{chapter.590}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {591}Headless REST APIs}{1713}{chapter.591}\\protected@file@percent }\n\\newlabel{headless-rest-apis}{{591}{1713}{Headless REST APIs}{chapter.591}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {591.1}OpenAPI}{1713}{section.591.1}\\protected@file@percent }\n\\newlabel{openapi}{{591.1}{1713}{OpenAPI}{section.591.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {591.2}API Vocabulary}{1713}{section.591.2}\\protected@file@percent }\n\\newlabel{api-vocabulary}{{591.2}{1713}{API Vocabulary}{section.591.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {592}Get Started: Find the API}{1715}{chapter.592}\\protected@file@percent }\n\\newlabel{get-started-find-the-api}{{592}{1715}{Get Started: Find the API}{chapter.592}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {592.1}Related Topics}{1716}{section.592.1}\\protected@file@percent }\n\\newlabel{related-topics-124}{{592.1}{1716}{Related Topics}{section.592.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {593}How To Invoke a Service}{1717}{chapter.593}\\protected@file@percent }\n\\newlabel{how-to-invoke-a-service}{{593}{1717}{How To Invoke a Service}{chapter.593}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {593.1}Related Topics}{1719}{section.593.1}\\protected@file@percent }\n\\newlabel{related-topics-125}{{593.1}{1719}{Related Topics}{section.593.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {594}Making Authenticated Requests}{1721}{chapter.594}\\protected@file@percent }\n\\newlabel{making-authenticated-requests}{{594}{1721}{Making Authenticated Requests}{chapter.594}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {594.1}Basic Authentication}{1721}{section.594.1}\\protected@file@percent }\n\\newlabel{basic-authentication}{{594.1}{1721}{Basic Authentication}{section.594.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {594.2}OAuth 2.0 Authorization}{1723}{section.594.2}\\protected@file@percent }\n\\newlabel{oauth-2.0-authorization}{{594.2}{1723}{OAuth 2.0 Authorization}{section.594.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {594.3}Obtaining the OAuth 2.0 Token}{1723}{section.594.3}\\protected@file@percent }\n\\newlabel{obtaining-the-oauth-2.0-token}{{594.3}{1723}{Obtaining the OAuth 2.0 Token}{section.594.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {594.4}Invoking the Service with an OAuth 2.0 Token}{1723}{section.594.4}\\protected@file@percent }\n\\newlabel{invoking-the-service-with-an-oauth-2.0-token}{{594.4}{1723}{Invoking the Service with an OAuth 2.0 Token}{section.594.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {594.5}Using Cookie Authentication or Making Requests from the UI}{1723}{section.594.5}\\protected@file@percent }\n\\newlabel{using-cookie-authentication-or-making-requests-from-the-ui}{{594.5}{1723}{Using Cookie Authentication or Making Requests from the UI}{section.594.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {594.6}Making Unauthenticated Requests}{1724}{section.594.6}\\protected@file@percent }\n\\newlabel{making-unauthenticated-requests}{{594.6}{1724}{Making Unauthenticated Requests}{section.594.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {594.7}Cross-Origin Resource Sharing (CORS)}{1724}{section.594.7}\\protected@file@percent }\n\\newlabel{cross-origin-resource-sharing-cors}{{594.7}{1724}{Cross-Origin Resource Sharing (CORS)}{section.594.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {594.8}Related Topics}{1724}{section.594.8}\\protected@file@percent }\n\\newlabel{related-topics-126}{{594.8}{1724}{Related Topics}{section.594.8}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {594.1}{\\ignorespaces Configure Cross-Origin Resource Sharing in Liferay}}{1725}{figure.594.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {595}Working with Collections of Data}{1727}{chapter.595}\\protected@file@percent }\n\\newlabel{working-with-collections-of-data}{{595}{1727}{Working with Collections of Data}{chapter.595}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {595.1}Pagination}{1727}{section.595.1}\\protected@file@percent }\n\\newlabel{pagination}{{595.1}{1727}{Pagination}{section.595.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {596}Getting Collections}{1729}{chapter.596}\\protected@file@percent }\n\\newlabel{getting-collections}{{596}{1729}{Getting Collections}{chapter.596}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {596.1}Related Topics}{1730}{section.596.1}\\protected@file@percent }\n\\newlabel{related-topics-127}{{596.1}{1730}{Related Topics}{section.596.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {597}Pagination}{1731}{chapter.597}\\protected@file@percent }\n\\newlabel{pagination-1}{{597}{1731}{Pagination}{chapter.597}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {597.1}Related Topics}{1732}{section.597.1}\\protected@file@percent }\n\\newlabel{related-topics-128}{{597.1}{1732}{Related Topics}{section.597.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {598}Navigating from a Collection to its Elements}{1733}{chapter.598}\\protected@file@percent }\n\\newlabel{navigating-from-a-collection-to-its-elements}{{598}{1733}{Navigating from a Collection to its Elements}{chapter.598}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {598.1}Related Topics}{1734}{section.598.1}\\protected@file@percent }\n\\newlabel{related-topics-129}{{598.1}{1734}{Related Topics}{section.598.1}{}}\n\\gdef \\LT@xxii {\\LT@entry \n    {1}{134.72264pt}\\LT@entry \n    {1}{335.03236pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {599}API Formats and Content Negotiation}{1735}{chapter.599}\\protected@file@percent }\n\\newlabel{api-formats-and-content-negotiation}{{599}{1735}{API Formats and Content Negotiation}{chapter.599}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {599.1}Language Negotiation}{1737}{section.599.1}\\protected@file@percent }\n\\newlabel{language-negotiation}{{599.1}{1737}{Language Negotiation}{section.599.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {599.2}Creating Content with Different Languages}{1738}{section.599.2}\\protected@file@percent }\n\\newlabel{creating-content-with-different-languages}{{599.2}{1738}{Creating Content with Different Languages}{section.599.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {599.3}Related Topics}{1738}{section.599.3}\\protected@file@percent }\n\\newlabel{related-topics-130}{{599.3}{1738}{Related Topics}{section.599.3}{}}\n\\gdef \\LT@xxiii {\\LT@entry \n    {1}{134.72264pt}\\LT@entry \n    {1}{335.03236pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {600}OpenAPI Profiles}{1739}{chapter.600}\\protected@file@percent }\n\\newlabel{openapi-profiles}{{600}{1739}{OpenAPI Profiles}{chapter.600}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {600.1}Headless Delivery}{1739}{section.600.1}\\protected@file@percent }\n\\newlabel{headless-delivery}{{600.1}{1739}{Headless Delivery}{section.600.1}{}}\n\\gdef \\LT@xxiv {\\LT@entry \n    {3}{71.0149pt}\\LT@entry \n    {1}{81.60974pt}}\n\\gdef \\LT@xxv {\\LT@entry \n    {3}{96.02061pt}\\LT@entry \n    {1}{81.60974pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {600.2}Headless Administration}{1740}{section.600.2}\\protected@file@percent }\n\\newlabel{headless-administration}{{600.2}{1740}{Headless Administration}{section.600.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {600.3}Related Topics}{1740}{section.600.3}\\protected@file@percent }\n\\newlabel{related-topics-131}{{600.3}{1740}{Related Topics}{section.600.3}{}}\n\\gdef \\LT@xxvi {\\LT@entry \n    {1}{49.99712pt}\\LT@entry \n    {1}{68.9838pt}\\LT@entry \n    {3}{151.0332pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {601}Filter, Sort, and Search}{1741}{chapter.601}\\protected@file@percent }\n\\newlabel{filter-sort-and-search}{{601}{1741}{Filter, Sort, and Search}{chapter.601}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.1}Filter}{1741}{section.601.1}\\protected@file@percent }\n\\newlabel{filter}{{601.1}{1741}{Filter}{section.601.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.2}Comparison Operators}{1741}{section.601.2}\\protected@file@percent }\n\\newlabel{comparison-operators}{{601.2}{1741}{Comparison Operators}{section.601.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.3}Logical Operators}{1741}{section.601.3}\\protected@file@percent }\n\\newlabel{logical-operators}{{601.3}{1741}{Logical Operators}{section.601.3}{}}\n\\gdef \\LT@xxvii {\\LT@entry \n    {1}{49.94234pt}\\LT@entry \n    {1}{68.9838pt}\\LT@entry \n    {3}{121.02634pt}}\n\\gdef \\LT@xxviii {\\LT@entry \n    {1}{104.33191pt}\\LT@entry \n    {1}{84.12503pt}\\LT@entry \n    {1}{281.29807pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.4}Grouping Operators}{1742}{section.601.4}\\protected@file@percent }\n\\newlabel{grouping-operators}{{601.4}{1742}{Grouping Operators}{section.601.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.5}String Functions}{1742}{section.601.5}\\protected@file@percent }\n\\newlabel{string-functions}{{601.5}{1742}{String Functions}{section.601.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.6}Lambda Operators}{1742}{section.601.6}\\protected@file@percent }\n\\newlabel{lambda-operators}{{601.6}{1742}{Lambda Operators}{section.601.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.7}Operator combinations and OData syntax}{1742}{section.601.7}\\protected@file@percent }\n\\newlabel{operator-combinations-and-odata-syntax}{{601.7}{1742}{Operator combinations and OData syntax}{section.601.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.8}Escaping in Queries}{1743}{section.601.8}\\protected@file@percent }\n\\newlabel{escaping-in-queries}{{601.8}{1743}{Escaping in Queries}{section.601.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.9}Filtering in Structured Content Fields (ContentField)}{1743}{section.601.9}\\protected@file@percent }\n\\newlabel{filtering-in-structured-content-fields-contentfield}{{601.9}{1743}{Filtering in Structured Content Fields (ContentField)}{section.601.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.10}Search}{1743}{section.601.10}\\protected@file@percent }\n\\newlabel{search-2}{{601.10}{1743}{Search}{section.601.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.11}Sorting}{1744}{section.601.11}\\protected@file@percent }\n\\newlabel{sorting}{{601.11}{1744}{Sorting}{section.601.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.12}Flatten}{1744}{section.601.12}\\protected@file@percent }\n\\newlabel{flatten}{{601.12}{1744}{Flatten}{section.601.12}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {601.13}Related Topics}{1745}{section.601.13}\\protected@file@percent }\n\\newlabel{related-topics-132}{{601.13}{1745}{Related Topics}{section.601.13}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {602}Restrict Properties}{1747}{chapter.602}\\protected@file@percent }\n\\newlabel{restrict-properties}{{602}{1747}{Restrict Properties}{chapter.602}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {602.1}Related Topics}{1748}{section.602.1}\\protected@file@percent }\n\\newlabel{related-topics-133}{{602.1}{1748}{Related Topics}{section.602.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {603}Multipart Requests}{1749}{chapter.603}\\protected@file@percent }\n\\newlabel{multipart-requests}{{603}{1749}{Multipart Requests}{chapter.603}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {603.1}Related Topics}{1750}{section.603.1}\\protected@file@percent }\n\\newlabel{related-topics-134}{{603.1}{1750}{Related Topics}{section.603.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {604}How to get siteId}{1751}{chapter.604}\\protected@file@percent }\n\\newlabel{how-to-get-siteid}{{604}{1751}{How to get siteId}{chapter.604}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {604.1}Using siteId or siteKey}{1751}{section.604.1}\\protected@file@percent }\n\\newlabel{using-siteid-or-sitekey}{{604.1}{1751}{Using siteId or siteKey}{section.604.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {604.2}Obtain siteId}{1751}{section.604.2}\\protected@file@percent }\n\\newlabel{obtain-siteid}{{604.2}{1751}{Obtain siteId}{section.604.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {604.1}{\\ignorespaces GraphQL BlogPostings definition}}{1752}{figure.604.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {605}Filterable properties}{1753}{chapter.605}\\protected@file@percent }\n\\newlabel{filterable-properties}{{605}{1753}{Filterable properties}{chapter.605}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.1}Headless Delivery API}{1753}{section.605.1}\\protected@file@percent }\n\\newlabel{headless-delivery-api}{{605.1}{1753}{Headless Delivery API}{section.605.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.2}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/BlogPosting}{BlogPosting}}{1753}{section.605.2}\\protected@file@percent }\n\\newlabel{blogposting}{{605.2}{1753}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/BlogPosting}{BlogPosting}}{BlogPosting}}{section.605.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.3}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/BlogPostingImage}{BlogPostingImage}}{1753}{section.605.3}\\protected@file@percent }\n\\newlabel{blogpostingimage}{{605.3}{1753}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/BlogPostingImage}{BlogPostingImage}}{BlogPostingImage}}{section.605.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.4}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/Comment}{Comment}}{1753}{section.605.4}\\protected@file@percent }\n\\newlabel{comment}{{605.4}{1753}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/Comment}{Comment}}{Comment}}{section.605.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.5}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/ContentStructure}{ContentStructure}}{1754}{section.605.5}\\protected@file@percent }\n\\newlabel{contentstructure}{{605.5}{1754}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/ContentStructure}{ContentStructure}}{ContentStructure}}{section.605.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.6}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/Document}{Document}}{1754}{section.605.6}\\protected@file@percent }\n\\newlabel{document}{{605.6}{1754}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/Document}{Document}}{Document}}{section.605.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.7}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/DocumentFolder}{DocumentFolder}}{1754}{section.605.7}\\protected@file@percent }\n\\newlabel{documentfolder}{{605.7}{1754}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/DocumentFolder}{DocumentFolder}}{DocumentFolder}}{section.605.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.8}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/KnowledgeBaseArticle}{KnowledgeBaseArticle}}{1754}{section.605.8}\\protected@file@percent }\n\\newlabel{knowledgebasearticle}{{605.8}{1754}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/KnowledgeBaseArticle}{KnowledgeBaseArticle}}{KnowledgeBaseArticle}}{section.605.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.9}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/MessageBoardMessage}{MessageBoardMessage}}{1754}{section.605.9}\\protected@file@percent }\n\\newlabel{messageboardmessage}{{605.9}{1754}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/MessageBoardMessage}{MessageBoardMessage}}{MessageBoardMessage}}{section.605.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.10}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/MessageBoardSection}{MessageBoardSection}}{1755}{section.605.10}\\protected@file@percent }\n\\newlabel{messageboardsection}{{605.10}{1755}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/MessageBoardSection}{MessageBoardSection}}{MessageBoardSection}}{section.605.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.11}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/StructuredContent}{StructuredContent}}{1755}{section.605.11}\\protected@file@percent }\n\\newlabel{structuredcontent}{{605.11}{1755}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/StructuredContent}{StructuredContent}}{StructuredContent}}{section.605.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.12}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/StructuredContentFolder}{StructuredContentFolder}}{1755}{section.605.12}\\protected@file@percent }\n\\newlabel{structuredcontentfolder}{{605.12}{1755}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/StructuredContentFolder}{StructuredContentFolder}}{StructuredContentFolder}}{section.605.12}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.13}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/WikiNode}{WikiNode}}{1755}{section.605.13}\\protected@file@percent }\n\\newlabel{wikinode}{{605.13}{1755}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/WikiNode}{WikiNode}}{WikiNode}}{section.605.13}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.14}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/WikiPage}{WikiPage}}{1756}{section.605.14}\\protected@file@percent }\n\\newlabel{wikipage}{{605.14}{1756}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/WikiPage}{WikiPage}}{WikiPage}}{section.605.14}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.15}Headless Admin User API}{1756}{section.605.15}\\protected@file@percent }\n\\newlabel{headless-admin-user-api}{{605.15}{1756}{Headless Admin User API}{section.605.15}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.16}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-admin-user/v1.0\\#/Organization}{Organization}}{1756}{section.605.16}\\protected@file@percent }\n\\newlabel{organization}{{605.16}{1756}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-admin-user/v1.0\\#/Organization}{Organization}}{Organization}}{section.605.16}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.17}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-admin-user/v1.0\\#/User}{User}}{1756}{section.605.17}\\protected@file@percent }\n\\newlabel{user}{{605.17}{1756}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-admin-user/v1.0\\#/User}{User}}{User}}{section.605.17}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.18}Headless Admin Taxonomy API}{1756}{section.605.18}\\protected@file@percent }\n\\newlabel{headless-admin-taxonomy-api}{{605.18}{1756}{Headless Admin Taxonomy API}{section.605.18}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.19}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/v1.0\\#/Category}{Category}}{1756}{section.605.19}\\protected@file@percent }\n\\newlabel{category}{{605.19}{1756}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/v1.0\\#/Category}{Category}}{Category}}{section.605.19}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.20}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/v1.0\\#/Keyword}{Keyword}}{1757}{section.605.20}\\protected@file@percent }\n\\newlabel{keyword}{{605.20}{1757}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/v1.0\\#/Keyword}{Keyword}}{Keyword}}{section.605.20}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {605.21}\\href  {https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/v1.0\\#/Vocabulary}{Vocabulary}}{1757}{section.605.21}\\protected@file@percent }\n\\newlabel{vocabulary}{{605.21}{1757}{\\texorpdfstring {\\href {https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/v1.0\\#/Vocabulary}{Vocabulary}}{Vocabulary}}{section.605.21}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {606}Using REST APIs}{1759}{chapter.606}\\protected@file@percent }\n\\newlabel{using-rest-apis}{{606}{1759}{Using REST APIs}{chapter.606}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {607}JAX-RS}{1761}{chapter.607}\\protected@file@percent }\n\\newlabel{jax-rs}{{607}{1761}{JAX-RS}{chapter.607}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {607.1}Authenticating to JAX-RS Web Services}{1762}{section.607.1}\\protected@file@percent }\n\\newlabel{authenticating-to-jax-rs-web-services}{{607.1}{1762}{Authenticating to JAX-RS Web Services}{section.607.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {607.2}During Development: Basic Auth}{1762}{section.607.2}\\protected@file@percent }\n\\newlabel{during-development-basic-auth}{{607.2}{1762}{During Development: Basic Auth}{section.607.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {607.3}Using OAuth 2.0 to Invoke a JAX-RS Web Service}{1762}{section.607.3}\\protected@file@percent }\n\\newlabel{using-oauth-2.0-to-invoke-a-jax-rs-web-service}{{607.3}{1762}{Using OAuth 2.0 to Invoke a JAX-RS Web Service}{section.607.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {607.1}{\\ignorespaces Enable the scope to grant access to the service.}}{1763}{figure.607.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{OAuth2 Scopes}{1764}{figure.607.1}\\protected@file@percent }\n\\newlabel{oauth2-scopes}{{607.3}{1764}{OAuth2 Scopes}{figure.607.1}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Requiring OAuth2}{1764}{figure.607.1}\\protected@file@percent }\n\\newlabel{requiring-oauth2}{{607.3}{1764}{Requiring OAuth2}{figure.607.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {607.4}JAX-RS and Service Access Policies}{1764}{section.607.4}\\protected@file@percent }\n\\newlabel{jax-rs-and-service-access-policies}{{607.4}{1764}{JAX-RS and Service Access Policies}{section.607.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {607.5}Public JAX-RS Services}{1764}{section.607.5}\\protected@file@percent }\n\\newlabel{public-jax-rs-services}{{607.5}{1764}{Public JAX-RS Services}{section.607.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {607.6}Using JAX-RS with CORS}{1765}{section.607.6}\\protected@file@percent }\n\\newlabel{using-jax-rs-with-cors}{{607.6}{1765}{Using JAX-RS with CORS}{section.607.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {607.7}Related Topics}{1766}{section.607.7}\\protected@file@percent }\n\\newlabel{related-topics-135}{{607.7}{1766}{Related Topics}{section.607.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {608}JAX-WS}{1767}{chapter.608}\\protected@file@percent }\n\\newlabel{jax-ws}{{608}{1767}{JAX-WS}{chapter.608}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {608.1}Configuring Endpoints and Extenders with the Control Panel}{1767}{section.608.1}\\protected@file@percent }\n\\newlabel{configuring-endpoints-and-extenders-with-the-control-panel}{{608.1}{1767}{Configuring Endpoints and Extenders with the Control Panel}{section.608.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {608.1}{\\ignorespaces Fill out this form to create a CXF endpoint.}}{1768}{figure.608.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {608.2}Configuring Endpoints and Extenders Programmatically}{1769}{section.608.2}\\protected@file@percent }\n\\newlabel{configuring-endpoints-and-extenders-programmatically}{{608.2}{1769}{Configuring Endpoints and Extenders Programmatically}{section.608.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {608.2}{\\ignorespaces Fill out this form to create a SOAP extender.}}{1770}{figure.608.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {608.3}Publishing JAX-WS Web Services}{1771}{section.608.3}\\protected@file@percent }\n\\newlabel{publishing-jax-ws-web-services}{{608.3}{1771}{Publishing JAX-WS Web Services}{section.608.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {609}GraphQL APIs}{1773}{chapter.609}\\protected@file@percent }\n\\newlabel{graphql-apis}{{609}{1773}{GraphQL APIs}{chapter.609}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {610}Get Started: Discover the API}{1775}{chapter.610}\\protected@file@percent }\n\\newlabel{get-started-discover-the-api}{{610}{1775}{Get Started: Discover the API}{chapter.610}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {610.1}Unique endpoint and versioning}{1775}{section.610.1}\\protected@file@percent }\n\\newlabel{unique-endpoint-and-versioning}{{610.1}{1775}{Unique endpoint and versioning}{section.610.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {610.1}{\\ignorespaces GraphQL APIs can be browsed in Altair.}}{1776}{figure.610.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {611}Get Started: Invoke a Service}{1777}{chapter.611}\\protected@file@percent }\n\\newlabel{get-started-invoke-a-service}{{611}{1777}{Get Started: Invoke a Service}{chapter.611}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {611.1}{\\ignorespaces GraphQL exposes a definition for BlogPostings.}}{1777}{figure.611.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {611.1}GraphQL Clients}{1779}{section.611.1}\\protected@file@percent }\n\\newlabel{graphql-clients}{{611.1}{1779}{GraphQL Clients}{section.611.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {612}Making Authenticated Requests}{1781}{chapter.612}\\protected@file@percent }\n\\newlabel{making-authenticated-requests-1}{{612}{1781}{Making Authenticated Requests}{chapter.612}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {612.1}Basic Authentication}{1781}{section.612.1}\\protected@file@percent }\n\\newlabel{basic-authentication-1}{{612.1}{1781}{Basic Authentication}{section.612.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {612.2}OAuth 2.0 Authorization}{1782}{section.612.2}\\protected@file@percent }\n\\newlabel{oauth-2.0-authorization-1}{{612.2}{1782}{OAuth 2.0 Authorization}{section.612.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {612.3}Obtaining the OAuth 2.0 Token}{1782}{section.612.3}\\protected@file@percent }\n\\newlabel{obtaining-the-oauth-2.0-token-1}{{612.3}{1782}{Obtaining the OAuth 2.0 Token}{section.612.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {612.4}Invoking the Service with an OAuth 2.0 Token}{1782}{section.612.4}\\protected@file@percent }\n\\newlabel{invoking-the-service-with-an-oauth-2.0-token-1}{{612.4}{1782}{Invoking the Service with an OAuth 2.0 Token}{section.612.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {612.5}Using Cookie Authentication or doing a request from the portal}{1783}{section.612.5}\\protected@file@percent }\n\\newlabel{using-cookie-authentication-or-doing-a-request-from-the-portal}{{612.5}{1783}{Using Cookie Authentication or doing a request from the portal}{section.612.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {612.6}Making Unauthenticated Requests}{1783}{section.612.6}\\protected@file@percent }\n\\newlabel{making-unauthenticated-requests-1}{{612.6}{1783}{Making Unauthenticated Requests}{section.612.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {612.7}Related Topics}{1783}{section.612.7}\\protected@file@percent }\n\\newlabel{related-topics-136}{{612.7}{1783}{Related Topics}{section.612.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {613}Working with Collections of Data}{1785}{chapter.613}\\protected@file@percent }\n\\newlabel{working-with-collections-of-data-1}{{613}{1785}{Working with Collections of Data}{chapter.613}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {613.1}Pagination}{1785}{section.613.1}\\protected@file@percent }\n\\newlabel{pagination-2}{{613.1}{1785}{Pagination}{section.613.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {614}Mutations}{1787}{chapter.614}\\protected@file@percent }\n\\newlabel{mutations}{{614}{1787}{Mutations}{chapter.614}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {614.1}{\\ignorespaces The GraphQL Mutations list for Blog postings shows the possible operations.}}{1787}{figure.614.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {615}Fragments and Node Patterns}{1789}{chapter.615}\\protected@file@percent }\n\\newlabel{fragments-and-node-patterns}{{615}{1789}{Fragments and Node Patterns}{chapter.615}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {615.1}Node pattern}{1789}{section.615.1}\\protected@file@percent }\n\\newlabel{node-pattern}{{615.1}{1789}{Node pattern}{section.615.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {616}Language Negotiation}{1791}{chapter.616}\\protected@file@percent }\n\\newlabel{language-negotiation-1}{{616}{1791}{Language Negotiation}{chapter.616}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {616.1}Creating Content with Different Languages}{1792}{section.616.1}\\protected@file@percent }\n\\newlabel{creating-content-with-different-languages-1}{{616.1}{1792}{Creating Content with Different Languages}{section.616.1}{}}\n\\gdef \\LT@xxix {\\LT@entry \n    {1}{49.99712pt}\\LT@entry \n    {1}{68.9838pt}\\LT@entry \n    {3}{151.0332pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {617}Filter, Sort, and Search}{1793}{chapter.617}\\protected@file@percent }\n\\newlabel{filter-sort-and-search-1}{{617}{1793}{Filter, Sort, and Search}{chapter.617}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {617.1}Filter}{1793}{section.617.1}\\protected@file@percent }\n\\newlabel{filter-1}{{617.1}{1793}{Filter}{section.617.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {617.2}Comparison Operators}{1793}{section.617.2}\\protected@file@percent }\n\\newlabel{comparison-operators-1}{{617.2}{1793}{Comparison Operators}{section.617.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {617.3}Logical Operators}{1793}{section.617.3}\\protected@file@percent }\n\\newlabel{logical-operators-1}{{617.3}{1793}{Logical Operators}{section.617.3}{}}\n\\gdef \\LT@xxx {\\LT@entry \n    {1}{49.94234pt}\\LT@entry \n    {1}{68.9838pt}\\LT@entry \n    {3}{121.02634pt}}\n\\gdef \\LT@xxxi {\\LT@entry \n    {1}{104.33191pt}\\LT@entry \n    {1}{84.12503pt}\\LT@entry \n    {1}{281.29807pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {617.4}Grouping Operators}{1794}{section.617.4}\\protected@file@percent }\n\\newlabel{grouping-operators-1}{{617.4}{1794}{Grouping Operators}{section.617.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {617.5}String Functions}{1794}{section.617.5}\\protected@file@percent }\n\\newlabel{string-functions-1}{{617.5}{1794}{String Functions}{section.617.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {617.6}Lambda Operators}{1794}{section.617.6}\\protected@file@percent }\n\\newlabel{lambda-operators-1}{{617.6}{1794}{Lambda Operators}{section.617.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {617.7}Escaping in Queries}{1794}{section.617.7}\\protected@file@percent }\n\\newlabel{escaping-in-queries-1}{{617.7}{1794}{Escaping in Queries}{section.617.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {617.8}Filtering in Structured Content Fields (ContentField)}{1795}{section.617.8}\\protected@file@percent }\n\\newlabel{filtering-in-structured-content-fields-contentfield-1}{{617.8}{1795}{Filtering in Structured Content Fields (ContentField)}{section.617.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {617.9}Search}{1795}{section.617.9}\\protected@file@percent }\n\\newlabel{search-3}{{617.9}{1795}{Search}{section.617.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {617.10}Sorting}{1796}{section.617.10}\\protected@file@percent }\n\\newlabel{sorting-1}{{617.10}{1796}{Sorting}{section.617.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {617.11}Flatten}{1797}{section.617.11}\\protected@file@percent }\n\\newlabel{flatten-1}{{617.11}{1797}{Flatten}{section.617.11}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {618}Multipart Requests}{1799}{chapter.618}\\protected@file@percent }\n\\newlabel{multipart-requests-1}{{618}{1799}{Multipart Requests}{chapter.618}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {618.1}{\\ignorespaces Create Document accepts a \\texttt  {multipartBody}.}}{1799}{figure.618.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {618.2}{\\ignorespaces Creating a Document in Altair is easy with the selector.}}{1800}{figure.618.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {619}Using GraphQL APIs}{1803}{chapter.619}\\protected@file@percent }\n\\newlabel{using-graphql-apis}{{619}{1803}{Using GraphQL APIs}{chapter.619}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {620}REST Builder}{1805}{chapter.620}\\protected@file@percent }\n\\newlabel{rest-builder}{{620}{1805}{REST Builder}{chapter.620}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {620.1}Why we should use REST Builder}{1805}{section.620.1}\\protected@file@percent }\n\\newlabel{why-we-should-use-rest-builder}{{620.1}{1805}{Why we should use REST Builder}{section.620.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {621}How to install REST Builder}{1807}{chapter.621}\\protected@file@percent }\n\\newlabel{how-to-install-rest-builder}{{621}{1807}{How to install REST Builder}{chapter.621}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {622}REST Builder \\& OpenAPI}{1809}{chapter.622}\\protected@file@percent }\n\\newlabel{rest-builder-openapi}{{622}{1809}{REST Builder \\& OpenAPI}{chapter.622}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {622.1}OpenAPI profile}{1809}{section.622.1}\\protected@file@percent }\n\\newlabel{openapi-profile}{{622.1}{1809}{OpenAPI profile}{section.622.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {622.2}Generation}{1811}{section.622.2}\\protected@file@percent }\n\\newlabel{generation}{{622.2}{1811}{Generation}{section.622.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {622.3}Examples}{1811}{section.622.3}\\protected@file@percent }\n\\newlabel{examples}{{622.3}{1811}{Examples}{section.622.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {622.4}GET Collection}{1811}{section.622.4}\\protected@file@percent }\n\\newlabel{get-collection}{{622.4}{1811}{GET Collection}{section.622.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {622.5}DELETE}{1812}{section.622.5}\\protected@file@percent }\n\\newlabel{delete}{{622.5}{1812}{DELETE}{section.622.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {622.6}POST}{1812}{section.622.6}\\protected@file@percent }\n\\newlabel{post}{{622.6}{1812}{POST}{section.622.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {622.7}PUT}{1812}{section.622.7}\\protected@file@percent }\n\\newlabel{put}{{622.7}{1812}{PUT}{section.622.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {622.8}Summary}{1813}{section.622.8}\\protected@file@percent }\n\\newlabel{summary}{{622.8}{1813}{Summary}{section.622.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {623}Developing an API with REST Builder}{1815}{chapter.623}\\protected@file@percent }\n\\newlabel{developing-an-api-with-rest-builder}{{623}{1815}{Developing an API with REST Builder}{chapter.623}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {623.1}Development Cycle}{1816}{section.623.1}\\protected@file@percent }\n\\newlabel{development-cycle}{{623.1}{1816}{Development Cycle}{section.623.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {623.2}Wrapping Up}{1817}{section.623.2}\\protected@file@percent }\n\\newlabel{wrapping-up}{{623.2}{1817}{Wrapping Up}{section.623.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {624}Managing Collections in REST Builder}{1819}{chapter.624}\\protected@file@percent }\n\\newlabel{managing-collections-in-rest-builder}{{624}{1819}{Managing Collections in REST Builder}{chapter.624}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {624.1}Pagination}{1819}{section.624.1}\\protected@file@percent }\n\\newlabel{pagination-3}{{624.1}{1819}{Pagination}{section.624.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {624.2}Filtering, sorting and searching}{1819}{section.624.2}\\protected@file@percent }\n\\newlabel{filtering-sorting-and-searching}{{624.2}{1819}{Filtering, sorting and searching}{section.624.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {624.3}Add an EntityModel}{1820}{section.624.3}\\protected@file@percent }\n\\newlabel{add-an-entitymodel}{{624.3}{1820}{Add an EntityModel}{section.624.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {624.4}Inject Your EntityModel}{1820}{section.624.4}\\protected@file@percent }\n\\newlabel{inject-your-entitymodel}{{624.4}{1820}{Inject Your EntityModel}{section.624.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {624.5}Call search utilities}{1821}{section.624.5}\\protected@file@percent }\n\\newlabel{call-search-utilities}{{624.5}{1821}{Call search utilities}{section.624.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {624.6}Using Your filter, search, and sort}{1821}{section.624.6}\\protected@file@percent }\n\\newlabel{using-your-filter-search-and-sort}{{624.6}{1821}{Using Your filter, search, and sort}{section.624.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {625}REST Builder Scaffolding}{1823}{chapter.625}\\protected@file@percent }\n\\newlabel{rest-builder-scaffolding}{{625}{1823}{REST Builder Scaffolding}{chapter.625}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {625.1}Context fields}{1823}{section.625.1}\\protected@file@percent }\n\\newlabel{context-fields}{{625.1}{1823}{Context fields}{section.625.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {625.2}Automatic transactions}{1823}{section.625.2}\\protected@file@percent }\n\\newlabel{automatic-transactions}{{625.2}{1823}{Automatic transactions}{section.625.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {625.3}Test generation}{1824}{section.625.3}\\protected@file@percent }\n\\newlabel{test-generation}{{625.3}{1824}{Test generation}{section.625.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {625.4}Client generation}{1824}{section.625.4}\\protected@file@percent }\n\\newlabel{client-generation}{{625.4}{1824}{Client generation}{section.625.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {625.5}Common utilities}{1824}{section.625.5}\\protected@file@percent }\n\\newlabel{common-utilities}{{625.5}{1824}{Common utilities}{section.625.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {626}Support for oneOf, anyOf and allOf}{1825}{chapter.626}\\protected@file@percent }\n\\newlabel{support-for-oneof-anyof-and-allof}{{626}{1825}{Support for oneOf, anyOf and allOf}{chapter.626}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {626.1}allOf}{1825}{section.626.1}\\protected@file@percent }\n\\newlabel{allof}{{626.1}{1825}{allOf}{section.626.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {626.2}oneOf}{1826}{section.626.2}\\protected@file@percent }\n\\newlabel{oneof}{{626.2}{1826}{oneOf}{section.626.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {626.3}anyOf}{1826}{section.626.3}\\protected@file@percent }\n\\newlabel{anyof}{{626.3}{1826}{anyOf}{section.626.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {627}REST Builder Liferay Conventions}{1829}{chapter.627}\\protected@file@percent }\n\\newlabel{rest-builder-liferay-conventions}{{627}{1829}{REST Builder Liferay Conventions}{chapter.627}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {627.1}YAML \\& OpenAPI restrictions}{1829}{section.627.1}\\protected@file@percent }\n\\newlabel{yaml-openapi-restrictions}{{627.1}{1829}{YAML \\& OpenAPI restrictions}{section.627.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {627.2}Conventions}{1829}{section.627.2}\\protected@file@percent }\n\\newlabel{conventions}{{627.2}{1829}{Conventions}{section.627.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {628}The Workflow Framework}{1831}{chapter.628}\\protected@file@percent }\n\\newlabel{the-workflow-framework}{{628}{1831}{The Workflow Framework}{chapter.628}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {628.1}Supporting Workflow in the Database}{1831}{section.628.1}\\protected@file@percent }\n\\newlabel{supporting-workflow-in-the-database}{{628.1}{1831}{Supporting Workflow in the Database}{section.628.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {628.2}Setting the Status Fields}{1831}{section.628.2}\\protected@file@percent }\n\\newlabel{setting-the-status-fields}{{628.2}{1831}{Setting the Status Fields}{section.628.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {628.1}{\\ignorespaces Enable workflow on your custom Asset, and it can be sent through a workflow process just like a native Asset.}}{1832}{figure.628.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {628.3}Sending the Entity to the Workflow Framework}{1833}{section.628.3}\\protected@file@percent }\n\\newlabel{sending-the-entity-to-the-workflow-framework}{{628.3}{1833}{Sending the Entity to the Workflow Framework}{section.628.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {628.4}Allowing the Workflow Framework to Handle the Entity}{1833}{section.628.4}\\protected@file@percent }\n\\newlabel{allowing-the-workflow-framework-to-handle-the-entity}{{628.4}{1833}{Allowing the Workflow Framework to Handle the Entity}{section.628.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {628.5}Supporting Workflow in the Service Layer}{1833}{section.628.5}\\protected@file@percent }\n\\newlabel{supporting-workflow-in-the-service-layer}{{628.5}{1833}{Supporting Workflow in the Service Layer}{section.628.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {628.6}Database Cleanup: Delete the Workflow Instance Links}{1834}{section.628.6}\\protected@file@percent }\n\\newlabel{database-cleanup-delete-the-workflow-instance-links}{{628.6}{1834}{Database Cleanup: Delete the Workflow Instance Links}{section.628.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {628.7}Updating the User Interface}{1834}{section.628.7}\\protected@file@percent }\n\\newlabel{updating-the-user-interface}{{628.7}{1834}{Updating the User Interface}{section.628.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {629}Liferay's Workflow Framework}{1835}{chapter.629}\\protected@file@percent }\n\\newlabel{liferays-workflow-framework}{{629}{1835}{Liferay's Workflow Framework}{chapter.629}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {629.1}Creating a Workflow Handler}{1835}{section.629.1}\\protected@file@percent }\n\\newlabel{creating-a-workflow-handler}{{629.1}{1835}{Creating a Workflow Handler}{section.629.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {629.2}Updating the Service Layer}{1836}{section.629.2}\\protected@file@percent }\n\\newlabel{updating-the-service-layer}{{629.2}{1836}{Updating the Service Layer}{section.629.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {629.3}Workflow Status and the View Layer}{1838}{section.629.3}\\protected@file@percent }\n\\newlabel{workflow-status-and-the-view-layer}{{629.3}{1838}{Workflow Status and the View Layer}{section.629.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {630}WYSIWYG Editors}{1839}{chapter.630}\\protected@file@percent }\n\\newlabel{wysiwyg-editors}{{630}{1839}{WYSIWYG Editors}{chapter.630}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {631}Adding a WYSIWYG Editor to a Portlet}{1841}{chapter.631}\\protected@file@percent }\n\\newlabel{adding-a-wysiwyg-editor-to-a-portlet}{{631}{1841}{Adding a WYSIWYG Editor to a Portlet}{chapter.631}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {631.1}Related Topics}{1842}{section.631.1}\\protected@file@percent }\n\\newlabel{related-topics-137}{{631.1}{1842}{Related Topics}{section.631.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {632}Modifying an Editor's Configuration}{1843}{chapter.632}\\protected@file@percent }\n\\newlabel{modifying-an-editors-configuration}{{632}{1843}{Modifying an Editor's Configuration}{chapter.632}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {632.1}Related Topics}{1846}{section.632.1}\\protected@file@percent }\n\\newlabel{related-topics-138}{{632.1}{1846}{Related Topics}{section.632.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {633}AlloyEditor}{1847}{chapter.633}\\protected@file@percent }\n\\newlabel{alloyeditor}{{633}{1847}{AlloyEditor}{chapter.633}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {633.1}{\\ignorespaces AlloyEditor is the default WYSIWYG editor built on top of CKEditor.}}{1847}{figure.633.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {634}Adding Buttons to AlloyEditor's Toolbars}{1849}{chapter.634}\\protected@file@percent }\n\\newlabel{adding-buttons-to-alloyeditors-toolbars}{{634}{1849}{Adding Buttons to AlloyEditor's Toolbars}{chapter.634}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {635}Creating the OSGi Module and Configuring the EditorConfigContributor Class}{1851}{chapter.635}\\protected@file@percent }\n\\newlabel{creating-the-osgi-module-and-configuring-the-editorconfigcontributor-class}{{635}{1851}{Creating the OSGi Module and Configuring the EditorConfigContributor Class}{chapter.635}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {635.1}Related Topics}{1852}{section.635.1}\\protected@file@percent }\n\\newlabel{related-topics-139}{{635.1}{1852}{Related Topics}{section.635.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {636}Adding a Button to the Add Toolbar}{1853}{chapter.636}\\protected@file@percent }\n\\newlabel{adding-a-button-to-the-add-toolbar}{{636}{1853}{Adding a Button to the Add Toolbar}{chapter.636}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {636.1}{\\ignorespaces The Add toolbar lets you add content to the editor.}}{1853}{figure.636.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {636.2}{\\ignorespaces The Updated Add toolbar lets you add pictures from a camera directly to the editor.}}{1854}{figure.636.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {636.1}Related Topics}{1855}{section.636.1}\\protected@file@percent }\n\\newlabel{related-topics-140}{{636.1}{1855}{Related Topics}{section.636.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {637}Adding a Button to a Styles Toolbar}{1857}{chapter.637}\\protected@file@percent }\n\\newlabel{adding-a-button-to-a-styles-toolbar}{{637}{1857}{Adding a Button to a Styles Toolbar}{chapter.637}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {637.1}{\\ignorespaces The embed URL Styles toolbar lets you format embedded content in the editor.}}{1857}{figure.637.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {637.2}{\\ignorespaces The image Styles toolbar lets you format images in the editor.}}{1858}{figure.637.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {637.3}{\\ignorespaces The link Styles toolbar lets you format hyperlinks in the editor.}}{1859}{figure.637.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {637.4}{\\ignorespaces The table Styles toolbar lets you format tables in the editor.}}{1859}{figure.637.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {637.5}{\\ignorespaces The text Styles toolbar lets you format highlighted text in the editor.}}{1860}{figure.637.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {637.6}{\\ignorespaces The Updated text styles toolbar lets you copy, cut, and paste text in the editor.}}{1861}{figure.637.6}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {637.1}Related Topics}{1862}{section.637.1}\\protected@file@percent }\n\\newlabel{related-topics-141}{{637.1}{1862}{Related Topics}{section.637.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {638}Embedding Content in the AlloyEditor}{1863}{chapter.638}\\protected@file@percent }\n\\newlabel{embedding-content-in-the-alloyeditor}{{638}{1863}{Embedding Content in the AlloyEditor}{chapter.638}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {638.1}Related Topics}{1865}{section.638.1}\\protected@file@percent }\n\\newlabel{related-topics-142}{{638.1}{1865}{Related Topics}{section.638.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {639}Adding New Behavior to an Editor}{1867}{chapter.639}\\protected@file@percent }\n\\newlabel{adding-new-behavior-to-an-editor}{{639}{1867}{Adding New Behavior to an Editor}{chapter.639}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {639.1}Related Topics}{1869}{section.639.1}\\protected@file@percent }\n\\newlabel{related-topics-143}{{639.1}{1869}{Related Topics}{section.639.1}{}}\n\\@setckpt{developer/frameworks}{\n\\setcounter{page}{1870}\n\\setcounter{equation}{0}\n\\setcounter{enumi}{8}\n\\setcounter{enumii}{4}\n\\setcounter{enumiii}{3}\n\\setcounter{enumiv}{0}\n\\setcounter{footnote}{0}\n\\setcounter{mpfootnote}{0}\n\\setcounter{@memmarkcntra}{0}\n\\setcounter{storedpagenumber}{1}\n\\setcounter{book}{0}\n\\setcounter{part}{4}\n\\setcounter{chapter}{639}\n\\setcounter{section}{1}\n\\setcounter{subsection}{0}\n\\setcounter{subsubsection}{0}\n\\setcounter{paragraph}{0}\n\\setcounter{subparagraph}{0}\n\\setcounter{@ppsavesec}{0}\n\\setcounter{@ppsaveapp}{0}\n\\setcounter{vslineno}{0}\n\\setcounter{poemline}{0}\n\\setcounter{modulo@vs}{0}\n\\setcounter{memfvsline}{0}\n\\setcounter{verse}{0}\n\\setcounter{chrsinstr}{0}\n\\setcounter{poem}{0}\n\\setcounter{newflo@tctr}{4}\n\\setcounter{@contsubnum}{0}\n\\setcounter{section@level}{0}\n\\setcounter{maxsecnumdepth}{1}\n\\setcounter{sidefootnote}{0}\n\\setcounter{pagenote}{0}\n\\setcounter{pagenoteshadow}{0}\n\\setcounter{memfbvline}{0}\n\\setcounter{bvlinectr}{0}\n\\setcounter{cp@cntr}{0}\n\\setcounter{ism@mctr}{0}\n\\setcounter{xsm@mctr}{0}\n\\setcounter{csm@mctr}{0}\n\\setcounter{ksm@mctr}{0}\n\\setcounter{xksm@mctr}{0}\n\\setcounter{cksm@mctr}{0}\n\\setcounter{msm@mctr}{0}\n\\setcounter{xmsm@mctr}{0}\n\\setcounter{cmsm@mctr}{0}\n\\setcounter{bsm@mctr}{0}\n\\setcounter{workm@mctr}{0}\n\\setcounter{sheetsequence}{1942}\n\\setcounter{lastsheet}{2851}\n\\setcounter{lastpage}{2779}\n\\setcounter{figure}{0}\n\\setcounter{lofdepth}{1}\n\\setcounter{table}{0}\n\\setcounter{lotdepth}{1}\n\\setcounter{Item}{2116}\n\\setcounter{Hfootnote}{5}\n\\setcounter{bookmark@seq@number}{0}\n\\setcounter{memhycontfloat}{0}\n\\setcounter{Hpagenote}{0}\n\\setcounter{r@tfl@t}{0}\n\\setcounter{float@type}{4}\n\\setcounter{LT@tables}{31}\n\\setcounter{LT@chunks}{3}\n\\setcounter{parentequation}{0}\n\\setcounter{FancyVerbLine}{0}\n}\n"
  },
  {
    "path": "book/developer/frameworks.tex",
    "content": "\\chapter{Application Security}\\label{application-security}\n\nLiferay's development framework provides an application security\nplatform with years of experience behind it. You don't need to roll your\nown security for your applications. Instead, you can specify security\nfor your applications using Liferay's framework.\n\nBeyond security for applications, there are many ways to extend the\ndefault security model by customizing the authentication process. This\ngroup of tutorials teaches you about them:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Resources, Roles, and Permissions\n\\item\n  Custom SSO Providers\n\\item\n  Authentication Pipelines\n\\item\n  Service Access Policies\n\\item\n  Authentication Verifiers\n\\end{itemize}\n\nRead on to learn about implementing Liferay's security framework!\n\n\\chapter{Defining Application\nPermissions}\\label{defining-application-permissions}\n\nWhen you're writing an application, there are almost always parts of the\napplication or its data that should be protected by permissions. Some\nusers should access all the functions or data, but most users shouldn't.\n\nOn many platforms, developers have to create the security model\nthemselves. On Liferay DXP, an application security model has been\nprovided for you; you only need to make use of it.\n\nFortunately, no matter what your application does, access to it and to\nits content can be controlled with permissions. Read on to learn about\nLiferay's permissions system and how add permissions to your\napplication.\n\nThe permissions system has three parts: \\emph{Resources},\n\\emph{Actions}, and \\emph{Permissions}.\n\n\\textbf{Action}: An operation that can be performed by a user. For\nexample, users can perform these actions on the Bookmarks application:\n\\texttt{ADD\\_TO\\_PAGE}, \\texttt{CONFIGURATION}, and \\texttt{VIEW}. Users\ncan perform these actions on Bookmarks entry entities:\n\\texttt{ADD\\_ENTRY}, \\texttt{DELETE}, \\texttt{PERMISSIONS},\n\\texttt{UPDATE}, and \\texttt{VIEW}.\n\n\\textbf{Resource}: A generic representation of any application or entity\non which an action can be performed. Resources are used for permission\nchecking. For example, resources could include the RSS application with\ninstance ID \\texttt{hF5f}, a globally scoped Wiki page, a Bookmarks\nentry of the site X, and a Message Boards post with the ID\n\\texttt{5052}.\n\n\\textbf{Permission}: A flag that determines whether an action can be\nperformed on a resource. In the database, resources and actions are\nsaved in pairs. Each entry in the \\texttt{ResourceAction} table contains\nboth the name of a portlet or entity and the name of an action. For\nexample, the \\texttt{VIEW} action with respect to \\emph{viewing the\nBookmarks application} is associated with the\n\\texttt{com\\_liferay\\_bookmarks\\_web\\_portlet\\_BookmarksPortlet} portlet\nID. The \\texttt{VIEW} actions with respect to \\emph{viewing a Bookmarks\nFolder} or \\emph{viewing a Bookmarks entry} are associated with the\n\\texttt{com.liferay.bookmarks.model.BookmarksFolder} and\n\\texttt{com.liferay.bookmarks.model.BookmarksEntry} entities,\nrespectively.\n\nTo do permissions, therefore, you define \\emph{Users} (Roles) who have\n\\emph{Permission} to perform \\emph{Actions} on \\emph{Resources}. User\ndefinition is done by administrators once your application is deployed;\ndevelopers define resources, actions, and default permissions.\n\nYou can implement permissions in your applications in four steps that\nspell the acronym \\emph{DRAC}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Define all resources and their permissions.\n\\item\n  Register all defined resources in the permissions system.\n\\item\n  Associate the necessary permissions with resources.\n\\item\n  Check permission before returning resources.\n\\end{enumerate}\n\nThe next four tutorials show these steps in detail.\n\n\\chapter{Defining Resources and\nPermissions}\\label{defining-resources-and-permissions}\n\nYour first step in implementing permissions is to define the resources\nand the permissions that protect them. There are two different kinds of\nresources: \\emph{portlet resources} and \\emph{model resources}.\n\nPortlet resources represent portlets. The names of portlet resources are\nthe portlet IDs from the portlets' \\texttt{@Component} properties or if\nyou're using a WAR file, \\texttt{portlet.xml} files. Model resources\nrefer to model objects, usually persisted as entities to a database. The\nnames of model resources are their fully qualified class names. In the\nXML displayed below, permission implementations are first defined for\nthe \\emph{portlet} resource and then for the \\emph{model} resources.\n\nModel resources represent models, such as blog entries. Resources are\nnamed using the fully qualified class names of the entities they\nrepresent.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} For each resource, there are four scopes to which the\npermissions can be applied: company, group, group-template, or\nindividual. Because these are called \\emph{portlet resources} here and\nin the code, this can be confusing. The other scopes are mostly used\ninternally for various Liferay constructs (such as Sites or Categories).\n\n\\noindent\\hrulefill\n\nYou define resources and their permissions using an XML file. By\nconvention, this file is called \\texttt{default.xml} and exists in a\nmodule's \\texttt{src/main/resources/resource-actions} folder.\n\nBecause of the two different types of resources, you'll have two of\nthese files: one in your portlet module to define the portlet resources\nand one in your service module to define the model resources.\n\n\\section{Defining Portlet Resource\nPermissions}\\label{defining-portlet-resource-permissions}\n\nDefine the portlet resources first; here's an example using Liferay's\nBlogs application.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Start with the DTD declaration:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action Mapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_0_0.dtd\">\n\\end{verbatim}\n\\item\n  The root tag contains all the resources to be declared:\n\n\\begin{verbatim}\n<resource-action-mapping>\n\n</resource-action-mapping> \n\\end{verbatim}\n\\item\n  Inside these tags, define your resources. The Blogs application\n  defines two portlet resources:\n\n\\begin{verbatim}\n<portlet-resource>\n    <portlet-name>com_liferay_blogs_web_portlet_BlogsAdminPortlet</portlet-name>\n    <permissions>\n        <supports>\n            <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n            <action-key>CONFIGURATION</action-key>\n            <action-key>VIEW</action-key>\n        </supports>\n        <site-member-defaults>\n            <action-key>VIEW</action-key>\n        </site-member-defaults>\n        <guest-defaults>\n            <action-key>VIEW</action-key>\n        </guest-defaults>\n        <guest-unsupported>\n            <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n            <action-key>CONFIGURATION</action-key>\n        </guest-unsupported>\n    </permissions>\n</portlet-resource>\n<portlet-resource>\n    <portlet-name>com_liferay_blogs_web_portlet_BlogsPortlet</portlet-name>\n    <permissions>\n        <supports>\n            <action-key>ADD_PORTLET_DISPLAY_TEMPLATE</action-key>\n            <action-key>ADD_TO_PAGE</action-key>\n            <action-key>CONFIGURATION</action-key>\n            <action-key>VIEW</action-key>\n        </supports>\n        <site-member-defaults>\n            <action-key>VIEW</action-key>\n        </site-member-defaults>\n        <guest-defaults>\n            <action-key>VIEW</action-key>\n        </guest-defaults>\n        <guest-unsupported>\n            <action-key>ADD_PORTLET_DISPLAY_TEMPLATE</action-key>\n            <action-key>CONFIGURATION</action-key>\n        </guest-unsupported>\n    </permissions>\n</portlet-resource>\n\\end{verbatim}\n\\end{enumerate}\n\nThe Blogs application comprises two portlets: the Blogs portlet itself\nand the Blogs Admin portlet that appears in the Site menu for\nadministrators. Define your portlets by their names, and then list the\npermissions for the portlet. The Blogs portlet, for example, supports\nfour permissions: \\texttt{ADD\\_PORTLET\\_DISPLAY\\_TEMPLATE},\n\\texttt{ADD\\_TO\\_PAGE}, \\texttt{CONFIGURATION}, and \\texttt{VIEW}. The\nBlogs Admin portlet has an additional permission:\n\\texttt{ACCESS\\_IN\\_CONTROL\\_PANEL}, which defines who can see the entry\nin the Site menu.\n\nOnce you've defined permissions at the portlet level, you can set\ndefault permissions for different types of users. The DTD allows for\nsite member and guest defaults. Since guests are users that aren't\nlogged in, there's also a \\texttt{guest-unsupported} tag for defining\npermissions guests can \\emph{never} have (in other words, the user must\nbe logged in and identifiable).\n\nThat's all there is to it! Your next task is to define permissions for\nyour model resources.\n\n\\section{Defining Model Resource\nPermissions}\\label{defining-model-resource-permissions}\n\nDefining permissions for models is a similar process. Create a\n\\texttt{default.xml} file in your service module's\n\\texttt{src/main/resources/resource-actions} folder. In this file, you\nmust define top-level function permissions and individual entity\npermissions using the same\n\\texttt{\\textless{}model-resource\\textgreater{}} tag.\n\nThis can be confusing, so some explanation is in order. Model\npermissions for what Liferay calls the \\emph{root model} are defined\nseparately from permissions on stored entities, which Liferay calls the\n\\emph{model}. This makes sense when you think about the functions users\ncan perform:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Creating something new\n\\item\n  Editing something that exists\n\\end{itemize}\n\nCreating something new (like adding a new Blog entry) is different from\naccessing something that exists. A Blog owner should be able to create\nor edit a Blog entry, but a User or guest should have read permission\nfor existing entries and no permission to create them.\n\nPermission to create something new that doesn't yet exist is a\n\\emph{root model} permission, whether that functionality is exposed in a\nportlet or not. Permission on an existing resource is a \\emph{model}\npermission.\n\nNow you're ready to define both your root model and model permissions.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  First, create the skeleton for your file:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action Mapping 7.1.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_0_0.dtd\">\n\n<resource-action-mapping>\n\n</resource-action-mapping> \n\\end{verbatim}\n\\item\n  Inside the \\texttt{\\textless{}resource-action-mapping\\textgreater{}}\n  tags, use a \\texttt{\\textless{}model-resource\\textgreater{}} tag to\n  define permissions for the root model:\n\n\\begin{verbatim}\n<model-resource>\n    <model-name>com.liferay.blogs</model-name>\n    <portlet-ref>\n        <portlet-name>com_liferay_blogs_web_portlet_BlogsAdminPortlet</portlet-name>\n        <portlet-name>com_liferay_blogs_web_portlet_BlogsPortlet</portlet-name>\n    </portlet-ref>\n    <root>true</root>\n    <weight>1</weight>\n    <permissions>\n        <supports>\n            <action-key>ADD_ENTRY</action-key>\n            <action-key>PERMISSIONS</action-key>\n            <action-key>SUBSCRIBE</action-key>\n        </supports>\n        <site-member-defaults>\n            <action-key>SUBSCRIBE</action-key>\n        </site-member-defaults>\n        <guest-defaults />\n        <guest-unsupported>\n            <action-key>ADD_ENTRY</action-key>\n            <action-key>PERMISSIONS</action-key>\n            <action-key>SUBSCRIBE</action-key>\n        </guest-unsupported>\n    </permissions>\n</model-resource>\n\\end{verbatim}\n\n  The model name (\\texttt{com.liferay.blogs}) is just a package name.\n  The\n  \\texttt{\\textless{}root\\textgreater{}true\\textless{}/root\\textgreater{}}\n  tag defines this as a root model. The\n  \\texttt{\\textless{}weight\\textgreater{}} tag defines the order of\n  these permissions in the GUI. The permissions defined are ADD\\_ENTRY\n  (add a Blog entry), PERMISSIONS (set permissions on Blog entries), and\n  SUBSCRIBE (receive notifications when Blog entries are created). These\n  are all root model permissions, because no primary key in the database\n  can be assigned to any of these functions.\n\\item\n  Finally, define your model permissions:\n\n\\begin{verbatim}\n<model-resource>\n    <model-name>com.liferay.blogs.model.BlogsEntry</model-name>\n    <portlet-ref>\n        <portlet-name>com_liferay_blogs_web_portlet_BlogsAdminPortlet</portlet-name>\n        <portlet-name>com_liferay_blogs_web_portlet_BlogsPortlet</portlet-name>\n    </portlet-ref>\n    <weight>2</weight>\n    <permissions>\n        <supports>\n            <action-key>ADD_DISCUSSION</action-key>\n            <action-key>DELETE</action-key>\n            <action-key>DELETE_DISCUSSION</action-key>\n            <action-key>PERMISSIONS</action-key>\n            <action-key>UPDATE</action-key>\n            <action-key>UPDATE_DISCUSSION</action-key>\n            <action-key>VIEW</action-key>\n        </supports>\n        <site-member-defaults>\n            <action-key>ADD_DISCUSSION</action-key>\n            <action-key>VIEW</action-key>\n        </site-member-defaults>\n        <guest-defaults>\n            <action-key>ADD_DISCUSSION</action-key>\n            <action-key>VIEW</action-key>\n        </guest-defaults>\n        <guest-unsupported>\n            <action-key>DELETE</action-key>\n            <action-key>DELETE_DISCUSSION</action-key>\n            <action-key>PERMISSIONS</action-key>\n            <action-key>UPDATE</action-key>\n            <action-key>UPDATE_DISCUSSION</action-key>\n        </guest-unsupported>\n    </permissions>\n</model-resource>\n\\end{verbatim}\n\\end{enumerate}\n\nNote the lack of a \\texttt{\\textless{}root\\textgreater{}} tag, the fully\nqualified class name for the model, and the permissions that operate on\nan entity with a primary key.\n\n\\section{Enabling Your Permissions\nConfiguration}\\label{enabling-your-permissions-configuration}\n\nYour last step is to enable your permission definitions. Each module\nthat contains a \\texttt{default.xml} permissions definition file must\nalso have a \\texttt{portlet.properties} file with a property that\ndefines where to find the permissions definition file. For your service\nand your web modules, create a \\texttt{portlet.properties} file in\n\\texttt{src/main/resources} and make sure it has this property:\n\n\\begin{verbatim}\nresource.actions.configs=resource-actions/default.xml\n\\end{verbatim}\n\nOnce you've defined portlet permissions, root model permissions, and\nmodel permissions, you've completed step 1 (the \\emph{D} in DRAC).\nCongratulations! You're now ready to \\emph{register} the resources\nyou've now defined in the permissions system.\n\n\\chapter{Registering Permissions}\\label{registering-permissions}\n\nDefining permissions was your first step; now you're ready to register\nthe permissions you've defined. You must register your entities both in\nthe database and in the permissions service running in the OSGi\ncontainer.\n\n\\section{Registering Permissions Resources in the\nDatabase}\\label{registering-permissions-resources-in-the-database}\n\nAll this takes is a call to Liferay's resource service in your service\nlayer. If you're using Service Builder, this is very easy to do.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your \\texttt{-LocalServiceImpl} class.\n\\item\n  In your method that adds an entity, add a call to add a resource with\n  the entity. For example, Liferay's Blogs application adds resources\n  this way:\n\n\\begin{verbatim}\nresourceLocalService.addResources(\n    entry.getCompanyId(), entry.getGroupId(), entry.getUserId(),\n    BlogsEntry.class.getName(), entry.getEntryId(), false,\n    addGroupPermissions, addGuestPermissions);\n\\end{verbatim}\n\n  This method requires passing in the company ID, the group ID, the user\n  ID, the entity's class name, the entity's primary key, and some\n  boolean settings. In order, these settings define\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Whether the permission is a portlet resource\n  \\item\n    Whether the default group permissions defined in\n    \\texttt{default.xml} should be added\n  \\item\n    Whether the default guest permissions defined in\n    \\texttt{default.xml} should be added\n  \\end{itemize}\n\\end{enumerate}\n\nNote that the resource local service is injected automatically into your\nService Builder-generated service.\n\nIf you're not using Service Builder, but you are using OSGi modules for\nyour application, you should be able to inject the resource service with\nan \\texttt{@Reference} annotation. If you're building a WAR-style\nplugin, you need a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/service-trackers-for-osgi-services}{service\ntracker} to gain access to the service. Note that your model classes\nmust also implement Liferay's \\texttt{ClassedModel} interface.\n\nSimilarly, when you delete an entity, you should also delete its\nassociated resource. Here's how the Blogs application does it in its\n\\texttt{deleteEntry()} method:\n\n\\begin{verbatim}\nresourceLocalService.deleteResource(\n    entry.getCompanyId(), BlogsEntry.class.getName(),\n    ResourceConstants.SCOPE_INDIVIDUAL, entry.getEntryId());\n\\end{verbatim}\n\nAs with adding resources, the method needs to know the entity's company\nID, class, and primary key. Most of the time, its scope is an individual\nentity of your own choosing. Other scopes available as constants are for\ncompany, group, or group template (site template). These are used\ninternally for those objects, so you'd only use them if you were\ncustomizing functionality for creating and deleting them.\n\nNow you're ready to register your entities with the permissions service.\n\n\\section{Registering Entities to the Permissions\nService}\\label{registering-entities-to-the-permissions-service}\n\nThe permissions service that's running must know about your entities and\nhow to check permissions for them. This requires creating a permissions\nregistrar class.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your service bundle, create a package that by convention ends in\n  \\texttt{internal.security.permission.resource}. For example, the Blogs\n  application's package is named\n  \\texttt{com.liferay.blogs.internal.security.permission.resource}.\n\\item\n  Create a class in this package called\n  \\texttt{{[}Entity\\ \\ \\ \\ \\ Name{]}ModelResourcePermissionRegistrar}.\n  For example, the Blogs application's class is named\n  \\texttt{BlogsEntryModelResourcePermissionRegistrar}.\n\\item\n  This class is a component class that requires overriding the\n  \\texttt{activate} method to register the permissions logic you want\n  for your entities. For example, this is how the Blogs application\n  registers its permissions:\n\n\\begin{verbatim}\n@Component(immediate = true)\npublic class BlogsEntryModelResourcePermissionRegistrar {\n\n    @Activate\n    public void activate(BundleContext bundleContext) {\n        Dictionary<String, Object> properties = new HashMapDictionary<>();\n\n        properties.put(\"model.class.name\", BlogsEntry.class.getName());\n\n        _serviceRegistration = bundleContext.registerService(\n            ModelResourcePermission.class,\n            ModelResourcePermissionFactory.create(\n                BlogsEntry.class, BlogsEntry::getEntryId,\n                _blogsEntryLocalService::getEntry, _portletResourcePermission,\n                (modelResourcePermission, consumer) -> {\n                    consumer.accept(\n                        new StagedModelPermissionLogic<>(\n                            _stagingPermission, BlogsPortletKeys.BLOGS,\n                            BlogsEntry::getEntryId));\n                    consumer.accept(\n                        new WorkflowedModelPermissionLogic<>(\n                            _workflowPermission, modelResourcePermission,\n                            BlogsEntry::getEntryId));\n                }),\n            properties);\n    }\n\n    @Deactivate\n    public void deactivate() {\n        _serviceRegistration.unregister();\n    }\n\n    @Reference\n    private BlogsEntryLocalService _blogsEntryLocalService;\n\n    @Reference(target = \"(resource.name=\" + BlogsConstants.RESOURCE_NAME + \")\")\n    private PortletResourcePermission _portletResourcePermission;\n\n    private ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n    @Reference\n    private StagingPermission _stagingPermission;\n\n    @Reference\n    private WorkflowPermission _workflowPermission;\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\nWe call these types of classes Registrars because the classes' job is to\nconfigure, register and unregister the \\texttt{ModelResourcePermission}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The \\texttt{model.class.name} is set in the properties so that other\n  modules' service trackers can find this model resource permission by\n  its type when it's needed. Liferay has several service trackers\n  checking for model resource permissions. The \\texttt{service.ranking}\n  property can also be set to a value greater than zero to override\n  other module's model resource permissions.\n\\item\n  This registrar uses two portal-kernel permission logic classes for\n  Staging and Workflow. Custom logic classes can be reused or composed\n  inline since \\texttt{ModelResourcePermissionLogic} is a\n  \\texttt{@FunctionalInterface}. Permission logic classes are executed\n  in order of when they are accepted in the \\texttt{Consumer}.\n\\item\n  \\texttt{ModelResourcePermissionLogic} classes return \\texttt{true}\n  when users have permission for the action, \\texttt{false} when they\n  are denied permission for the action, and \\texttt{null} when wanting\n  to delegate responsibility to the next permission logic. If all\n  permission logics return null then the\n  \\texttt{PermissionChecker.hasPermission} method is called to determine\n  if the action is allowed for the user.\n\\end{enumerate}\n\nThis class uses an \\texttt{@Reference} with the target filter to inject\nthe appropriate \\texttt{PortletResourcePermission}.\n\\texttt{BlogsConstants.RESOURCE\\_NAME} evaluates to\n\\texttt{com.liferay.blogs}, which is defined in the \\texttt{default.xml}\nyou created earlier. If you were to reference this\n\\texttt{ModelResourcePermission}, you'd use a target filter matching the\n\\texttt{model.class.name} property set in the \\texttt{activate} method.\n\nNote that you specify your entity's class, primary key, and the entity\nitself for the factory so it can create permission objects specific to\nyour entity.\n\nGreat! You've now completed step 2 in \\emph{DRAC} by registering your\npermissions. Now you're ready to provide users the interface to\nassociate permissions with resources.\n\n\\chapter{Associating Permissions with\nResources}\\label{associating-permissions-with-resources}\n\nNow that you've defined and registered permissions, you must expose the\npermissions interface so users can set permissions.\n\nTo allow permissions to be configured for model resources, you must add\nthe permissions interface to the UI. Add these two Liferay UI tags to\nyour JSP:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\texttt{\\textless{}liferay-security:permissionsURL\\textgreater{}}:\n  Returns a URL to the permission settings configuration page.\n\\item\n  \\texttt{\\textless{}liferay-ui:icon\\textgreater{}}: Shows an icon to\n  the user. These are defined in the theme and one of them (see below)\n  is used for permissions.\n\\end{enumerate}\n\nThe Blogs application uses these tags like this:\n\n\\begin{verbatim}\n<liferay-security:permissionsURL\n            modelResource=\"<%= BlogsEntry.class.getName() %>\"\n            modelResourceDescription=\"<%= BlogsEntryUtil.getDisplayTitle(resourceBundle, entry) %>\"\n            resourceGroupId=\"<%= String.valueOf(entry.getGroupId()) %>\"\n            resourcePrimKey=\"<%= String.valueOf(entry.getEntryId()) %>\"\n            var=\"permissionsEntryURL\"\n            windowState=\"<%= LiferayWindowState.POP_UP.toString() %>\"\n        />\n\n<liferay-ui:icon\n    label=\"<%= true %>\"\n    message=\"permissions\"\n    method=\"get\"\n    url=\"<%= permissionsEntryURL %>\"\n    useDialog=\"<%= true %>\"\n/>\n\\end{verbatim}\n\nFor the\n\\texttt{\\textless{}liferay-security:permissionsURL\\ /\\textgreater{}}\ntag, specify these attributes:\n\n\\texttt{modelResource}: The fully qualified class name of the entity\nclass. This class name gets translated into a more readable name as\nspecified in \\texttt{Language.properties}.\n\n\\texttt{Language.properties}: The entity class in the example above is\nthe Blogs entry class for which the fully qualified class name is\n\\texttt{com.liferay.blogs.model.BlogsEntry}.\n\n\\texttt{modelResourceDescription}: You can enter anything that best\ndescribes this model instance. In the example above, the Blog title is\nused for the model resource description.\n\n\\texttt{resourcePrimKey}: Your entity's primary key.\n\n\\texttt{var}: The name of the variable to which the resulting URL string\nis assigned. The variable is then passed to the\n\\texttt{\\textless{}liferay-ui:icon\\textgreater{}} tag so the permission\nicon has the proper URL link.\n\nThere's an optional attribute called \\texttt{redirect} that's available\nif you want to override the default behavior of the upper right arrow\nlink. That's it; now your users can configure the permission settings\nfor model resources!\n\nYou've completed step 3 in \\emph{DRAC}. Your next step is to check for\npermissions in the appropriate areas of your application.\n\n\\chapter{Checking Permissions}\\label{checking-permissions}\n\nNow that you've defined your permissions, registered resources in the\ndatabase and with the OSGi container, and enabled users to associate\npermissions with resources, you're ready to add permission checks in the\nappropriate places in your application. This takes three steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add permission checks to your service calls.\n\\item\n  Create permission helper classes in your web module.\n\\item\n  Add permission checks to your web application.\n\\end{enumerate}\n\nThese things are covered next.\n\n\\section{Add Permission Checks to Your Service\nCalls}\\label{add-permission-checks-to-your-service-calls}\n\nA best practice is to create methods in your \\texttt{-ServiceImpl}\nclasses that call the same methods in your \\texttt{-LocalServiceImpl}\nclasses, but wrap those calls in permission checks. If you expose your\nservices as web services, then any client calling those services must\nhave permission to call the service. In this way, you separate your\nbusiness logic (contained in the \\texttt{-LocalServiceImpl} class) from\nyour permissions logic (contained in the \\texttt{-ServiceImpl} class).\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your entity's \\texttt{-ServiceImpl} class.\n\\item\n  Use the \\texttt{ModelResourcePermissionFactory} and the\n  \\texttt{PortletResourcePermissionFactory} to reference permission\n  checkers that can check permissions as you've defined them in\n  \\texttt{default.xml}. Here's how the Blogs portlet does this:\n\n\\begin{verbatim}\nprivate static volatile ModelResourcePermission<BlogsEntry>\n    _blogsEntryFolderModelResourcePermission =\n        ModelResourcePermissionFactory.getInstance(\n            BlogsEntryServiceImpl.class,\n            \"_blogsEntryFolderModelResourcePermission\", BlogsEntry.class);\nprivate static volatile PortletResourcePermission\n    _portletResourcePermission =\n        PortletResourcePermissionFactory.getInstance(\n            BlogsEntryServiceImpl.class, \"_portletResourcePermission\",\n            BlogsConstants.RESOURCE_NAME);\n\\end{verbatim}\n\n  You declare the class, the variable, and for the portlet resource, the\n  resource name from \\texttt{default.xml}. In the Blogs application,\n  \\texttt{BlogsConstants.RESOURCE\\_NAME} is a \\texttt{String} with the\n  value \\texttt{com.liferay.blogs}.\n\n  You must use \\texttt{ModelResourcePermissionFactory.getInstance()} in\n  the service because Service Builder is wired with Spring, so\n  \\texttt{@Reference} can't be used. Make sure to use the correct\n  service class and the name of the field that's being set (in this case\n  \\texttt{\"\\_blogsEntryFolderModelResourcePermission\"}), because it's\n  set with reflection when the service is registered. If you get the\n  field wrong, it'll be set wrong. The field must be \\texttt{static} and\n  \\texttt{volatile}, and should never be used outside of\n  \\texttt{-ServiceImpl} classes.\n\\item\n  Check permissions in the appropriate places. For example, adding a\n  blog entry requires the \\texttt{ADD\\_ENTRY} permission, so the Blogs\n  application does this:\n\n\\begin{verbatim}\n@Override\npublic BlogsEntry addEntry(\n        String title, String subtitle, String description, String content,\n        int displayDateMonth, int displayDateDay, int displayDateYear,\n        int displayDateHour, int displayDateMinute, boolean allowPingbacks,\n        boolean allowTrackbacks, String[] trackbacks,\n        String coverImageCaption, ImageSelector coverImageImageSelector,\n        ImageSelector smallImageImageSelector,\n        ServiceContext serviceContext)\n    throws PortalException {\n\n    _portletResourcePermission.check(\n        getPermissionChecker(), serviceContext.getScopeGroupId(),\n        ActionKeys.ADD_ENTRY);\n\n    return blogsEntryLocalService.addEntry(\n        getUserId(), title, subtitle, description, content,\n        displayDateMonth, displayDateDay, displayDateYear, displayDateHour,\n        displayDateMinute, allowPingbacks, allowTrackbacks, trackbacks,\n        coverImageCaption, coverImageImageSelector, smallImageImageSelector,\n        serviceContext);\n}\n\\end{verbatim}\n\n  The check throws an exception if it fails, preventing the local\n  service call that adds the entry. A convention Liferay uses is to\n  place the action keys from \\texttt{default.xml} as constants in an\n  \\texttt{ActionKeys} class. If \\texttt{ActionKeys} doesn't have an\n  action key appropriate for your application, extend Liferay's class\n  and add your own keys.\n\\end{enumerate}\n\nAdd permission checks where necessary to protect your application's\nfunctions at the service level. Next, you'll learn how to create\npermission helper classes for your web module.\n\n\\section{Create Permission Helper Classes in Your Web\nModule}\\label{create-permission-helper-classes-in-your-web-module}\n\nA helper class can make it easier to check permissions in your portlet\napplication. You can create helper classes for both portlet permissions\nand model permissions. Here's how to create a portlet permission helper:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a package with the suffix\n  \\texttt{web.internal.security.permission.resource}. For example, the\n  Blogs application has the package\n  \\texttt{com.liferay.blogs.web.internal.security.permission.resource}.\n\\item\n  Create a component class with at least one static method for checking\n  permissions. For example, here's the \\texttt{BlogsPermission} class:\n\n\\begin{verbatim}\n@Component(immediate = true)\npublic class BlogsPermission {\n\n    public static boolean contains(\n        PermissionChecker permissionChecker, long groupId, String actionId) {\n\n        return _portletResourcePermission.contains(\n            permissionChecker, groupId, actionId);\n    }\n\n    @Reference(\n        target = \"(resource.name=\" + BlogsConstants.RESOURCE_NAME + \")\",\n        unbind = \"-\"\n    )\n    protected void setPortletResourcePermission(\n        PortletResourcePermission portletResourcePermission) {\n\n        _portletResourcePermission = portletResourcePermission;\n    }\n\n    private static PortletResourcePermission _portletResourcePermission;\n\n}\n\\end{verbatim}\n\n  Note the \\texttt{@Reference} annotation that tells the OSGi container\n  to supply an object via the permission registrar you created\n  previously. The \\texttt{\\_portletResourcePermission} field is static,\n  while the setter method is an instance method: this is how Liferay\n  avoids having service references in JSPs.\n\\end{enumerate}\n\nThe procedure for creating a model permission helper is similar:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the same package, create a component class with at least one static\n  method for checking permissions. For example, here's the\n  \\texttt{BlogsEntryPermission} class:\n\n\\begin{verbatim}\n@Component(immediate = true)\npublic class BlogsEntryPermission {\n\n    public static boolean contains(\n            PermissionChecker permissionChecker, BlogsEntry entry,\n            String actionId)\n        throws PortalException {\n\n        return _blogsEntryFolderModelResourcePermission.contains(\n            permissionChecker, entry, actionId);\n    }\n\n    public static boolean contains(\n            PermissionChecker permissionChecker, long entryId, String actionId)\n        throws PortalException {\n\n        return _blogsEntryFolderModelResourcePermission.contains(\n            permissionChecker, entryId, actionId);\n    }\n\n    @Reference(\n        target = \"(model.class.name=com.liferay.blogs.model.BlogsEntry)\",\n        unbind = \"-\"\n    )\n    protected void setEntryModelPermission(\n        ModelResourcePermission<BlogsEntry> modelResourcePermission) {\n\n        _blogsEntryFolderModelResourcePermission = modelResourcePermission;\n    }\n\n    private static ModelResourcePermission<BlogsEntry>\n        _blogsEntryFolderModelResourcePermission;\n\n}\n\\end{verbatim}\n\n  As you can see, this class is almost the same as the portlet\n  permission class. The real difference is in the \\texttt{@Reference}\n  annotation that specifies the fully qualified class name of the model,\n  rather than the resource name from \\texttt{default.xml}.\n\\item\n  Save both files.\n\\end{enumerate}\n\nNow you're ready to use these helper classes to check permissions in\nyour web module.\n\n\\section{Add Permission Checks to Your Web\nApplication}\\label{add-permission-checks-to-your-web-application}\n\nYou can use the permission helper classes to check for permissions\nbefore displaying UI elements. If the element never appears, a user\ncan't access it (though you should also protect your services as\ndescribed above). Here's how to do that:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  When you have a function you want to protect, wrap it in an\n  \\texttt{if} statement that uses the permission helper class. For\n  example, the Blogs application has many functions protected by\n  permissions, including \\texttt{ADD\\_ENTRY} and \\texttt{SUBSCRIBE}.\n  Clearly, only blog owners should be able to add blog entries. The\n  button for this, therefore, should only appear if a user has\n  permission to add entries:\n\n\\begin{verbatim}\n<c:if test=\"<%= BlogsPermission.contains(permissionChecker, scopeGroupId, ActionKeys.ADD_ENTRY) %>\">\n    <div class=\"button-holder\">\n        <portlet:renderURL var=\"editEntryURL\" windowState=\"<%= WindowState.MAXIMIZED.toString() %>\">\n            <portlet:param name=\"mvcRenderCommandName\" value=\"/blogs/edit_entry\" />\n            <portlet:param name=\"redirect\" value=\"<%= currentURL %>\" />\n        </portlet:renderURL>\n\n        <aui:button href=\"<%= editEntryURL %>\" icon=\"icon-plus\" value=\"add-blog-entry\" />\n    </div>\n</c:if>\n\\end{verbatim}\n\\item\n  Do this for any function. For example, the Permissions function you\n  added in\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/associating-permissions-with-resources}{step\n  3} should definitely be protected by permissions:\n\n\\begin{verbatim}\n<c:if test=\"<%= BlogsEntryPermission.contains(permissionChecker, entry, ActionKeys.PERMISSIONS) %>\">\n    <liferay-security:permissionsURL\n        modelResource=\"<%= BlogsEntry.class.getName() %>\"\n        modelResourceDescription=\"<%= BlogsEntryUtil.getDisplayTitle(resourceBundle, entry) %>\"\n        resourceGroupId=\"<%= String.valueOf(entry.getGroupId()) %>\"\n        resourcePrimKey=\"<%= String.valueOf(entry.getEntryId()) %>\"\n        var=\"permissionsEntryURL\"\n        windowState=\"<%= LiferayWindowState.POP_UP.toString() %>\"\n    />\n\n    <liferay-ui:icon\n        label=\"<%= true %>\"\n        message=\"permissions\"\n        method=\"get\"\n        url=\"<%= permissionsEntryURL %>\"\n        useDialog=\"<%= true %>\"\n    />\n</c:if>\n\\end{verbatim}\n\n  This prevents anyone without the permission to set permissions from\n  seeing the permissions button. Say that three times fast!\n\\end{enumerate}\n\nThat's all there is to it! You've now learned all the steps in\n\\emph{DRAC}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Define permissions\n\\item\n  Register permissions\n\\item\n  Associate permissions with resources\n\\item\n  Check permissions\n\\end{enumerate}\n\nFollow these steps, and your applications can take advantage of\nLiferay's integrated and well-tested permissions system.\n\n\\chapter{Using JSR Roles in a\nPortlet}\\label{using-jsr-roles-in-a-portlet}\n\nRoles in Liferay DXP are the primary means for granting or restricting\naccess to content. If you've decided \\emph{not} to use Liferay's\npermissions system, you can use the basic system offered by the JSR 168,\n286, and 362 specifications that map Roles in a portlet to Roles\nprovided by the portal.\n\n\\section{JSR Portlet Security}\\label{jsr-portlet-security}\n\nThe portlet specification defines a means to specify Roles used by\nportlets in their \\texttt{docroot/WEB-INF/portlet.xml} descriptors. The\nRole names themselves, however, are not standardized. When these\nportlets run in Liferay DXP, the Role names defined in the portlet must\nbe mapped to Roles that exist in the Portal.\n\nFor example, consider a Guestbook project that contains two portlets:\nThe Guestbook portlet and the Guestbook Admin portlet. The WAR version\nof the Guestbook project's \\texttt{portlet.xml} file references the\n\\emph{administrator}, \\emph{guest}, \\emph{power-user}, and \\emph{user}\nRoles:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<portlet-app xmlns=\"http://xmlns.jcp.org/xml/ns/portlet\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/portlet http://xmlns.jcp.org/xml/ns/portlet/portlet-app_3_0.xsd\" version=\"3.0\">\n    <portlet>\n        <portlet-name>guestbook-war</portlet-name>\n        <display-name>guestbook-war</display-name>\n        <portlet-class>com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet</portlet-class>\n        <init-param>\n            <name>template-path</name>\n            <value>/</value>\n        </init-param>\n        <init-param>\n            <name>view-template</name>\n            <value>/view.jsp</value>\n        </init-param>\n        <expiration-cache>0</expiration-cache>\n        <supports>\n            <mime-type>text/html</mime-type>\n        </supports>\n        <resource-bundle>content.Language</resource-bundle>\n        <portlet-info>\n            <title>guestbook-war</title>\n            <short-title>guestbook-war</short-title>\n            <keywords>guestbook-war</keywords>\n        </portlet-info>\n        <security-role-ref>\n            <role-name>administrator</role-name>\n        </security-role-ref>\n        <security-role-ref>\n            <role-name>guest</role-name>\n        </security-role-ref>\n        <security-role-ref>\n            <role-name>power-user</role-name>\n        </security-role-ref>\n        <security-role-ref>\n            <role-name>user</role-name>\n        </security-role-ref>\n    </portlet>\n</portlet-app>\n\\end{verbatim}\n\nAn OSGi-based \\texttt{guestbook-web} module project defines Roles\nwithout an XML file, in the portlet class's \\texttt{@Component}\nannotation:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"com.liferay.portlet.display-category=category.sample\",\n        \"com.liferay.portlet.instanceable=true\",\n        \"javax.portlet.init-param.template-path=/\",\n        \"javax.portlet.init-param.view-template=/view.jsp\",\n        \"javax.portlet.name=\" + GuestbookPortletKeys.Guestbook,\n        \"javax.portlet.resource-bundle=content.Language\",\n        \"javax.portlet.security-role-ref=power-user,user\"\n    },\n    service = Portlet.class\n)\n\\end{verbatim}\n\nIf you are using an OSGi-based MVC Portlet, you must use Liferay's\npermissions system, as the only way to map JSR-362 Roles to Liferay\nRoles is to place them in the Liferay WAR file's \\texttt{portlet.xml}.\n\nYour \\texttt{portlet.xml} Roles must be mapped to specific Roles that\nhave been created. These mappings allow Liferay DXP to resolve conflicts\nbetween Roles with the same name that are from different portlets\n(e.g.~portlets from different developers).\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Each Role named in a portlet's\n\\texttt{\\textless{}security-role-ref\\textgreater{}} element is given\npermission to add the portlet to a page.\n\n\\noindent\\hrulefill\n\n\\section{Mapping Portlet Roles to Portal\nRoles}\\label{mapping-portlet-roles-to-portal-roles}\n\nTo map the Roles to Liferay DXP, you must use the\n\\texttt{docroot/WEB-INF/liferay-portlet.xml} Liferay-specific\nconfiguration file. For an example, see the mapping defined in the\nGuestbook project's \\texttt{liferay-portlet.xml} file.\n\n\\begin{verbatim}\n<role-mapper>\n    <role-name>administrator</role-name>\n    <role-link>Administrator</role-link>\n</role-mapper>\n<role-mapper>\n    <role-name>guest</role-name>\n    <role-link>Guest</role-link>\n</role-mapper>\n<role-mapper>\n    <role-name>power-user</role-name>\n    <role-link>Power User</role-link>\n</role-mapper>\n<role-mapper>\n    <role-name>user</role-name>\n    <role-link>User</role-link>\n</role-mapper>\n\\end{verbatim}\n\nIf a portlet definition references the Role \\texttt{power-user}, that\nportlet is mapped to the Liferay Role called \\emph{Power User} that's\nalready in Liferay's database.\n\nAs stated above, there is no standardization with portal Role names. If\nyou deploy a portlet with Role names different from the above default\nLiferay names, you must add the names to the \\texttt{system.roles}\nproperty in your \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\nsystem.roles=my-role,your-role,our-role\n\\end{verbatim}\n\nThis prevents Roles from being created accidentally.\n\nOnce Roles are mapped to the portal, you can use methods as defined in\nthe portlet specification:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{getRemoteUser()}\n\\item\n  \\texttt{isUserInRole()}\n\\item\n  \\texttt{getUserPrincipal()}\n\\end{itemize}\n\nFor example, you can use the following code to check if the current User\nhas the \\texttt{power-user} Role:\n\n\\begin{verbatim}\nif (renderRequest.isUserInRole(\"power-user\")) {\n    // ...\n}\n\\end{verbatim}\n\nBy default, Liferay doesn't use the \\texttt{isUserInRole()} method in\nany built-in portlets. Liferay uses its own permission system directly\nto achieve more fine-grained security. If you don't intend on deploying\nyour portlets to other portal servers, we recommend using Liferay's\npermission system, because it offers a much more robust way of tailoring\nyour application's permissions.\n\n\\section{Related Topics}\\label{related-topics}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions}{Liferay\nPermissions}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{Asset\nFramework}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{Portlets}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/understanding-servicecontext}{Understanding\nServiceContext}\n\n\\chapter{Authentication Pipelines}\\label{authentication-pipelines}\n\nThe authentication process is a pipeline through which users can be\nvalidated by one or several systems. As a developer, you can\nauthenticate users to anything you wish, rather than be limited by what\nLiferay DXP supports out of the box.\n\nHere's how authentication works under most circumstances:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Users provide their credentials to the Login Portlet to begin an\n  authenticated session in a browser.\n\\item\n  Alternatively, credentials are provided to Liferay DXP's API\n  endpoints, where they are sent in an HTTP BASIC Auth header.\n\\item\n  Alternatively, credentials can be provided by another system. These\n  are managed by \\texttt{AutoLogin} components.\n\\item\n  Credentials are checked by default against the database, but they can\n  be delegated to other systems instead of or in addition to it. This is\n  called an \\emph{Authentication Pipeline}. You can add\n  \\texttt{Authenticator}s to the pipeline to support any system.\n\\item\n  You can also customize the Login Portlet to support whatever user\n  interface any of these systems need. This gives you full flexibility\n  over the entire authentication process.\n\\end{enumerate}\n\nThis structure lets you support an authentication mechanism and/or\naccept credentials from a system that Liferay DXP doesn't yet support.\nIf you don't like the user interface for signing in, you can replace it\nwith your own.\n\nThese tutorials guide you through these customizations. You'll discover\nthree kinds of customizations:\n\n\\begin{itemize}\n\\item\n  \\textbf{Auto Login:} the easiest of the three, this enables\n  authentication to Liferay DXP using credentials provided in the HTTP\n  header from another system.\n\\item\n  \\textbf{Authentication Pipelines:} if you must check credentials\n  against other systems instead of or in addition to Liferay DXP's\n  database, you can create a pipeline.\n\\item\n  \\textbf{Custom Login Portlet:} if you want to change the user's\n  sign-in experience completely, you can implement your own Login\n  portlet.\n\\end{itemize}\n\nRead on to discover how to customize your users' sign-in experience.\n\n\\chapter{Auto Login}\\label{auto-login}\n\nWhile Liferay DXP supports a wide variety of\n\\href{/docs/7-2/deploy/-/knowledge_base/d/securing-product}{authentication\nmechanisms}, you may use a home-grown system or some other product to\nauthenticate users. To do so, you can write an Auto Login component to\nsupport your authentication system.\n\nAuto Login components can check if the request contains something (a\ncookie, an attribute) that can be associated with a user in any way. If\nthe component can make that association, it can authenticate that user.\n\n\\section{Creating an Auto Login\nComponent}\\label{creating-an-auto-login-component}\n\nCreate a\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Declarative\nServices component}. The component should implement the\n\\texttt{com.liferay.portal.kernel.security.auto.login.AutoLogin}\ninterface. Here's an example template:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.security.auto.login.AutoLogin;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.osgi.service.component.annotations.Component;\n\n@Component(immediate = true)\npublic class MyAutoLogin implements Autologin {\n\n    public String[] handleException(\n            HttpServletRequest request, HttpServletResponse response,\n            Exception e)\n        throws AutoLoginException {\n\n        /* This method is no longer used in the interface and can be \n      left empty */\n\n    }\n\n    public String[] login(\n            HttpServletRequest request, HttpServletResponse response)\n        throws AutoLoginException {\n\n        /* Your Code Goes Here */\n\n    }\n\n}\n\\end{verbatim}\n\nAs you can see, you have access to the \\texttt{HttpServletRequest} and\nthe \\texttt{HttpServletResponse} objects. If your sign-on solution\nplaces anything here that identifies a user such as a cookie, an\nattribute, or a parameter, you can retrieve it and take whatever action\nyou need to retrieve the user information and authenticate that user.\n\nFor example, say that there's a request attribute that contains the\nencrypted value of a user key. This can only be there if the user has\nauthenticated with a third party system that knew the value of the user\nkey, encrypted it, and added it as a request attribute. You could write\ncode that reads the value, decrypts it using the same pre-shared key,\nand uses the value to look up and authenticate the user.\n\nThe \\texttt{login} method is where this all happens. This method must\nreturn a \\texttt{String} array with three items in this order:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  The user ID\n\\item\n  The user password\n\\item\n  A boolean flag that's \\texttt{true} if the password is encrypted and\n  \\texttt{false} if it's not (\\texttt{Boolean.TRUE.toString()} or\n  \\texttt{Boolean.FALSE.toString()}).\n\\end{itemize}\n\nSending redirects is an optional \\texttt{AutoLogin} feature. Since\n\\texttt{AutoLogin}s are part of the servlet filter chain, you have two\noptions. Both are implemented by setting attributes in the request. Here\nare the attributes:\n\n\\begin{itemize}\n\\item\n  \\texttt{AutoLogin.AUTO\\_LOGIN\\_REDIRECT}: This key causes\n  \\texttt{AutoLoginFilter} to stop the filter chain's execution and\n  redirect immediately to the location specified in the attribute's\n  value.\n\\item\n  \\texttt{AutoLogin.AUTO\\_LOGIN\\_REDIRECT\\_AND\\_CONTINUE}: This key\n  causes \\texttt{AutoLoginFilter} to set the redirect and continue\n  executing the remaining filters in the chain.\n\\end{itemize}\n\nAuto Login components are useful ways of providing an authentication\nmechanism to a system that Liferay DXP doesn't yet support. You can\nwrite them fairly quickly to provide the integration you need.\n\n\\section{Related Topics}\\label{related-topics-1}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/password-based-authentication-pipelines}{Password-Based\nAuthentication Pipelines}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/writing-a-custom-login-portlet}{Writing\na Custom Login Portlet}\n\n\\chapter{Password-Based Authentication\nPipelines}\\label{password-based-authentication-pipelines}\n\nBy default, once a user submits credentials, those credentials are\nchecked against Liferay DXP's database, though you can also delegate\nauthentication to an LDAP server. To use some other system in your\nenvironment instead of or in addition to checking credentials against\nthe database, you can write an \\texttt{Authenticator} and insert it as a\nstep in the authentication pipeline.\n\nBecause the \\texttt{Authenticator} is checked by the Login Portlet, you\ncan't use this approach if the user must be redirected to the external\nsystem or needs a token to authenticate. In those cases, you should use\nan \\href{/docs/7-2/frameworks/-/knowledge_base/f/auto-login}{Auto Login}\nor an\n\\href{/docs/7-2/deploy/-/knowledge_base/d/authentication-verifiers}{Auth\nVerifier}.\n\n\\texttt{Authenticator}s let you do these things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Log into Liferay DXP with a user name and password maintained in an\n  external system\n\\item\n  Make secondary user authentication checks\n\\item\n  Perform additional processing when user authentication fails\n\\end{itemize}\n\nRead on to learn how to create an \\texttt{Authenticator}.\n\n\\section{Anatomy of an Authenticator}\\label{anatomy-of-an-authenticator}\n\n\\texttt{Authenticator}s are implemented for various steps in the\nauthentication pipeline. Here are the steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\texttt{auth.pipeline.pre}: Comes before default authentication to the\n  database. In this step, you can skip credential validation against the\n  database. Implemented by \\texttt{Authenticator}.\n\\item\n  Default (optional) authentication to the database.\n\\item\n  \\texttt{auth.pipeline.post}: Further (secondary, tertiary)\n  authentication checks. Implemented by \\texttt{Authenticator}.\n\\item\n  \\texttt{auth.failure}: Perform additional processing after\n  authentication fails. Implemented by \\texttt{AuthFailure}.\n\\end{enumerate}\n\nTo create an \\texttt{Authenticator}, create a module and add a component\nthat implements the interface:\n\n\\begin{verbatim}\n@Component(\n    immediate = true, property = {\"key=auth.pipeline.post\"},\n    service = Authenticator.class\n)\npublic class MyCustomAuth implements Authenticator {\n\n    public int authenticateByEmailAddress(\n            long companyId, String emailAddress, String password,\n            Map<String, String[]> headerMap, Map<String, String[]> parameterMap)\n        throws AuthException {\n\nreturn Authenticator.SUCCESS;\n}\n\n    public int authenticateByScreenName(\n            long companyId, String screenName, String password,\n            Map<String, String[]> headerMap, Map<String, String[]> parameterMap)\n        throws AuthException {\n\nreturn Authenticator.SUCCESS;\n    }\n\n    public int authenticateByUserId(\n            long companyId, long userId, String password,\n            Map<String, String[]> headerMap, Map<String, String[]> parameterMap)\n        throws AuthException {\n\nreturn Authenticator.SUCCESS;\n    }\n}\n\\end{verbatim}\n\nThis example has been stripped down so you can see its structure. First,\nnote the \\texttt{@Component} annotation's contents:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{immediate\\ =\\ true}: sets the component to start immediately\n\\item\n  \\texttt{key=auth.pipeline.post}: sets the \\texttt{Authenticator} to\n  run in the \\texttt{auth.pipeline.post} phase. To run the\n  \\texttt{auth.pipeline.pre} phase, substitute\n  \\texttt{auth.pipeline.pre}.\n\\item\n  \\texttt{service\\ =\\ Authenticator.class}: implements the\n  \\texttt{Authenticator} service. All \\texttt{Authenticator}s must do\n  this.\n\\end{itemize}\n\nThe three methods below the annotation run based on how you've\nconfigured authentication: by email address (the default), by screen\nname, or by user ID. All the methods throw an \\texttt{AuthException} in\ncase the \\texttt{Authenticator} can't perform its task: if the system\nit's authenticating against is unavailable or if some dependency can't\nbe found. The methods in this barebones example return success in all\ncases. If you deploy its module, it has no effect. Naturally, you'll\nwant to provide more functionality. Next is an example that shows you\nhow to do that.\n\n\\section{Creating an Authenticator}\\label{creating-an-authenticator}\n\nThis example is an \\texttt{Authenticator} that only allows users whose\nemail addresses end with \\emph{@liferay.com} or \\emph{@example.com}. You\ncan implement this using one module that does everything. If you think\nother modules might use the functionality that validates the email\naddresses, you should create two modules: one to implement the\n\\texttt{Authenticator} and one to validate email addresses. This example\nshows the two module approach.\n\nTo create an \\texttt{Authenticator}, create a module for your\nimplementation. The most appropriate Blade template for this is the\n\\href{/docs/7-2/reference/-/knowledge_base/r/using-the-service-template}{service\ntemplate}. Once you have the module, creating the \\texttt{Activator} is\nstraightforward:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the \\texttt{@Component} annotation to bind your \\texttt{Activator}\n  to the appropriate authentication pipeline phase.\n\\item\n  Implement the \\texttt{Authenticator} interface and provide the\n  functionality you need.\n\\item\n  Deploy your module. If you're using\n  \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}, do\n  this via \\texttt{blade\\ deploy}.\n\\end{enumerate}\n\nFor this example, you'll do this twice: once for the email address\nvalidator module and once for the \\texttt{Authenticator} itself. The\n\\texttt{Authenticator} project contains the interface for the validator,\nand the validator project contains the implementation. Here's what the\n\\texttt{Authenticator} module structure looks like:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auth-pipeline-authenticator-project.png}\n\\caption{The Authenticator module contains the validator's interface and\nthe authenticator.}\n\\end{figure}\n\nSince the \\texttt{Authenticator} is the most relevant, examine it first:\n\n\\begin{verbatim}\npackage com.liferay.docs.emailaddressauthenticator;\n\nimport java.util.Map;\n\nimport com.liferay.docs.emailaddressauthenticator.validator.EmailAddressValidator;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.security.auth.AuthException;\nimport com.liferay.portal.kernel.security.auth.Authenticator;\nimport com.liferay.portal.kernel.service.UserLocalService;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\nimport org.osgi.service.component.annotations.ReferenceCardinality;\nimport org.osgi.service.component.annotations.ReferencePolicy;\n\n@Component(\n    immediate = true,\n    property = {\"key=auth.pipeline.post\"},\n    service = Authenticator.class\n)\npublic class EmailAddressAuthenticator implements Authenticator {\n\n    @Override\n    public int authenticateByEmailAddress(long companyId, String emailAddress,\n            String password, Map<String, String[]> headerMap,\n            Map<String, String[]> parameterMap) throws AuthException {\n        \n        return validateDomain(emailAddress);\n    }\n\n    @Override\n    public int authenticateByScreenName(long companyId, String screenName,\n            String password, Map<String, String[]> headerMap,\n            Map<String, String[]> parameterMap) throws AuthException {\n        \n        String emailAddress = \n            _userLocalService.fetchUserByScreenName(companyId, screenName).getEmailAddress();\n        \n        return validateDomain(emailAddress);\n    }\n\n    @Override\n    public int authenticateByUserId(long companyId, long userId,\n            String password, Map<String, String[]> headerMap,\n            Map<String, String[]> parameterMap) throws AuthException {\n        \n        String emailAddress = \n            _userLocalService.fetchUserById(userId).getEmailAddress();\n        \n        return validateDomain(emailAddress);\n    }\n    \n    private int validateDomain(String emailAddress) throws AuthException {\n        \n        if (_emailValidator == null) {\n            \n            String msg = \"Email address validator is unavailable, cannot authenticate user\";            \n            _log.error(msg);\n            \n            throw new AuthException(msg);\n        }\n        \n        if (_emailValidator.isValidEmailAddress(emailAddress)) {        \n            return Authenticator.SUCCESS;\n        }\n        return Authenticator.FAILURE;\n    }\n    \n    @Reference\n    private volatile UserLocalService _userLocalService;\n    \n    @Reference(\n        policy = ReferencePolicy.DYNAMIC,\n        cardinality = ReferenceCardinality.OPTIONAL\n    )\n    private volatile EmailAddressValidator _emailValidator;\n    \n    private static final Log _log = LogFactoryUtil.getLog(EmailAddressAuthenticator.class);\n}\n\\end{verbatim}\n\nThis time, rather than stubs, the three authentication methods contain\nfunctionality. The \\texttt{authenticateByEmailAddress} method directly\nchecks the email address provided by the Login Portlet. The other two\nmethods, \\texttt{authenticateByScreenName} and\n\\texttt{authenticateByUserId} call \\texttt{UserLocalService} to look up\nthe user's email address before checking it. The OSGi container injects\nthis service because of the \\texttt{@Reference} annotation. Note that\nthe validator is also injected in this same manner, though it's\nconfigured not to fail if the implementation can't be found. This allows\nthis module to start regardless of its dependency on the validator\nimplementation. In this case, this is safe because the error is handled\nby throwing an \\texttt{AuthException} and logging the error.\n\nWhy would you want to do it this way? To err gracefully. Because this is\nan \\texttt{auth.pipeline.post} \\texttt{Authenticator}, you presumably\nhave other \\texttt{Authenticator}s checking credentials before this one.\nIf this one isn't working, you want to inform administrators with an\nerror message rather than catastrophically failing and preventing users\nfrom logging in.\n\nThe only other Java code in this module is the Interface for the\nvalidator:\n\n\\begin{verbatim}\npackage com.liferay.docs.emailaddressauthenticator.validator;\n\nimport aQute.bnd.annotation.ProviderType;\n\n@ProviderType\npublic interface EmailAddressValidator {\n\n    public boolean isValidEmailAddress(String emailAddress);\n}\n\\end{verbatim}\n\nThis defines a single method for checking the email address.\n\nNext, you'll address the validator module.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auth-pipeline-validator-project.png}\n\\caption{The validator project implements the Validator Interface and\ndepends on the authenticator module.}\n\\end{figure}\n\nThis module contains only one class. It implements the Validator\ninterface:\n\n\\begin{verbatim}\npackage com.liferay.docs.emailaddressvalidator.impl;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\nimport org.osgi.service.component.annotations.Component;\nimport com.liferay.docs.emailaddressauthenticator.validator.EmailAddressValidator;\n\n@Component(\n    immediate = true,\n    property = {\n    },\n    service = EmailAddressValidator.class\n)\npublic class EmailAddressValidatorImpl implements EmailAddressValidator {\n\n    @Override\n    public boolean isValidEmailAddress(String emailAddress) {\n\n        if (_validEmailDomains.contains(\n            emailAddress.substring(emailAddress.indexOf('@')))) {\n\n            return true;\n        }\n        return false;\n    }\n\n    private Set<String> _validEmailDomains = \n        new HashSet<String>(Arrays.asList(new String[] {\"@liferay.com\", \"@example.com\"}));\n}\n\\end{verbatim}\n\nThis code checks to make sure that the email address is from the\n\\emph{@liferay.com} or \\emph{@example.com} domains. The only other\ninteresting part of this module is the Gradle build script, because it\ndefines a compile-only dependency between the two projects. This is\ndivided into two files: a \\texttt{settings.gradle} and a\n\\texttt{build.gradle}.\n\nThe \\texttt{settings.gradle} file defines the location of the project\n(the \\texttt{Authenticator}) the validator depends on:\n\n\\begin{verbatim}\ninclude ':emailAddressAuthenticator'\nproject(':emailAddressAuthenticator').projectDir = new File(settingsDir, '../com.liferay.docs.emailAddressAuthenticator')\n\\end{verbatim}\n\nSince this project contains the interface, it must be on the classpath\nat compile time, which is when \\texttt{build.gradle} is running:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins\", version: \"3.0.23\"\n    }\n\n    repositories {\n        mavenLocal()\n\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.plugin\"\n\ndependencies {\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"2.0.0\"\n    compileOnly group: \"org.osgi\", name: \"org.osgi.compendium\", version: \"5.0.0\"\n\n    compileOnly project(\":emailAddressAuthenticator\")\n}\n\nrepositories {\n    mavenLocal()\n\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\nNote the line in the dependencies section that refers to the\n\\texttt{Authenticator} project defined in \\texttt{settings.gradle}.\n\nWhen these projects are deployed, the \\texttt{Authenticator} you defined\nruns, enforcing logins for the two domains specified in the validator.\n\n\\section{Related Topics}\\label{related-topics-2}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/auto-login}{Auto Login}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/writing-a-custom-login-portlet}{Writing\na Custom Login Portlet}\n\n\\chapter{Writing a Custom Login\nPortlet}\\label{writing-a-custom-login-portlet}\n\nIf you need to customize your users' authentication experience\ncompletely, you can write your own Login Portlet. The mechanics of this\non the macro level are no different from writing any other portlet, so\nif you need to familiarize yourself with that, please see the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{portlets}.\n\nThis tutorial shows only the relevant parts of a\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{Liferay\nMVC Portlet} that authenticates the user. You'll learn how to call the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/password-based-authentication-pipelines}{authentication\npipeline} and then redirect the user to a location of your choice.\n\n\\section{Authenticating to Liferay\nDXP}\\label{authenticating-to-liferay-dxp}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When developing a login portlet, set the session timeout\nportal property like this:\n\n\\begin{verbatim}\n session.timeout.auto.extend.offset=45\n\\end{verbatim}\n\nThis is needed because the default (as of\n\\href{https://issues.liferay.com/browse/LPS-68543}{LPS-68543}) setting\nis \\texttt{0}, causing the browser to execute an\n\\texttt{extend\\_session} call. This may force users attempting to log in\nto make the attempt twice.\n\n\\noindent\\hrulefill\n\nIt has only one view, which is used for logging in or showing the user\nwho is already logged in:\n\n\\begin{verbatim}\n<%@ include file=\"/init.jsp\" %>\n\n<p>\n    <b><liferay-ui:message key=\"myloginportlet_MyLogin.caption\"/></b>\n</p>\n\n<c:choose>\n    <c:when test=\"<%= themeDisplay.isSignedIn() %>\">\n\n        <%\n        String signedInAs = HtmlUtil.escape(user.getFullName());\n\n        if (themeDisplay.isShowMyAccountIcon() && (themeDisplay.getURLMyAccount() != null)) {\n            String myAccountURL = String.valueOf(themeDisplay.getURLMyAccount());\n\n            signedInAs = \"<a class=\\\"signed-in\\\" href=\\\"\" + HtmlUtil.escape(myAccountURL) + \"\\\">\" + signedInAs + \"</a>\";\n        }\n        %>\n\n        <liferay-ui:message arguments=\"<%= signedInAs %>\" key=\"you-are-signed-in-as-x\" translateArguments=\"<%= false %>\" />\n    </c:when>\n    <c:otherwise>\n    \n        <%\n        String redirect = ParamUtil.getString(request, \"redirect\");\n        %>\n    \n        <portlet:actionURL name=\"/login/login\" var=\"loginURL\">\n            <portlet:param name=\"mvcRenderCommandName\" value=\"/login/login\" />\n        </portlet:actionURL>\n        \n        <aui:form action=\"<%= loginURL %>\" autocomplete='on' cssClass=\"sign-in-form\" method=\"post\" name=\"loginForm\">\n        \n            <aui:input name=\"saveLastPath\" type=\"hidden\" value=\"<%= false %>\" />\n            <aui:input name=\"redirect\" type=\"hidden\" value=\"<%= redirect %>\" />\n                    \n            <aui:input autoFocus=\"true\" cssClass=\"clearable\" label=\"email-address\" name=\"login\" showRequiredLabel=\"<%= false %>\" type=\"text\" value=\"\">\n                <aui:validator name=\"required\" />\n            </aui:input>\n\n            <aui:input name=\"password\" showRequiredLabel=\"<%= false %>\" type=\"password\">\n                <aui:validator name=\"required\" />\n            </aui:input>\n            \n            <aui:button-row>\n                <aui:button cssClass=\"btn-lg\" type=\"submit\" value=\"sign-in\" />\n            </aui:button-row>\n                \n        </aui:form>\n    </c:otherwise>\n</c:choose>\n\\end{verbatim}\n\nNote that in the form, authentication by email address (the default\nsetting) is hard-coded, as this is an example project. The current page\nis sent as a hidden field on the form so the portlet can redirect the\nuser to it, but you can of course set this to any value you want.\n\nThe portlet handles all processing of this form using a single Action\nCommand (imports left out for brevity):\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"javax.portlet.name=MyLoginPortlet\",\n        \"mvc.command.name=/login/login\"\n    },\n    service = MVCActionCommand.class\n)\npublic class MyLoginMVCActionCommand extends BaseMVCActionCommand {\n\n    @Override\n    protected void doProcessAction(ActionRequest actionRequest,\n            ActionResponse actionResponse) throws Exception {\n\n        ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(\n            WebKeys.THEME_DISPLAY);\n        \n        HttpServletRequest request = PortalUtil.getOriginalServletRequest(\n            PortalUtil.getHttpServletRequest(actionRequest));\n        \n        HttpServletResponse response = PortalUtil.getHttpServletResponse(\n            actionResponse);\n\n        String login = ParamUtil.getString(actionRequest, \"login\");\n        String password = actionRequest.getParameter(\"password\");\n        boolean rememberMe = ParamUtil.getBoolean(actionRequest, \"rememberMe\");\n        String authType = CompanyConstants.AUTH_TYPE_EA;\n        \n        AuthenticatedSessionManagerUtil.login(\n            request, response, login, password, rememberMe, authType);\n        \n        actionResponse.sendRedirect(themeDisplay.getPathMain());\n    }\n\n}\n\\end{verbatim}\n\nThe only tricky/unusual code here is the need to grab the\n\\texttt{HttpServletRequest} and the \\texttt{HttpServletResponse}. This\nis necessary to call Liferay DXP's API for authentication. At the end of\nthe Action Command, the portlet sends a redirect that sends the user to\nthe same page. You can of course make this any page you want.\n\nImplementing your own login portlet gives you complete control over the\nauthentication process.\n\n\\section{Related Topics}\\label{related-topics-3}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/password-based-authentication-pipelines}{Password-Based\nAuthentication Pipelines}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/auto-login}{Auto Login}\n\n\\chapter{Service Access Policies}\\label{service-access-policies}\n\nService access policies provide web service security beyond user\nauthentication to remote services. Together with\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions}{permissions},\nservice access policies limit remote service access by remote client\napplications. This forms an additional security layer that protects user\ndata from unauthorized access and modification.\n\nTo connect to a web service, remote clients must authenticate using\ncredentials in that instance. This grants the remote client the\npermissions assigned to those credentials in the Liferay DXP\ninstallation. Service access policies further limit the remote client's\naccess to the services specified in the policy. Without such policies,\nauthenticated remote clients are treated like users: they can call any\nremote API and read or modify data on behalf of the authenticated user.\nSince remote clients are often intended for a specific use case,\ngranting them access to everything the user has permissions for poses a\nsecurity risk.\n\nFor example, consider a mobile app (client) that displays a user's\nappointments from the Liferay Calendar app. This client app doesn't need\naccess to the API that updates the user profile, even though the user\nhas such permissions on the server. The client app doesn't even need\naccess to the Calendar API methods that create, update, and delete\nappointments. It only needs access to the remote service methods for\nfinding and retrieving appointments. A service access policy on the\nserver can restrict the client's access to only these service methods.\nSince the client doesn't perform other operations, having access to them\nis a security risk if the mobile device is lost or stolen or the client\napp is compromised by an attacker.\n\n\\section{How Service Access Policies\nWork}\\label{how-service-access-policies-work}\n\nA remote client's request to a web service contains the user's\ncredentials or an authorization token. An authentication module\nrecognizes the client based on the credentials/token and grants the\nappropriate service access policy to the request. The service access\npolicy authorization layer then processes all granted policies and lets\nthe request access the remote service(s) permitted by the policy.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/service-access-policies-arch.png}\n\\caption{The authorization module maps the credentials or token to the\nproper Service Access Policy.}\n\\end{figure}\n\nService Access policies are created in the Control Panel by\nadministrators. If you want to start creating policies yourself, see\n\\href{/docs/7-2/deploy/-/knowledge_base/d/service-access-policies}{this\narticle on service access policies} that documents creating them in the\nUI.\n\nThere may be cases, however, when your server-side Liferay app must use\nthe service access policies API:\n\n\\begin{itemize}\n\\item\n  It uses\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/auto-login}{custom\n  remote API authentication} (tokens) and require certain services to be\n  available for clients using the tokens.\n\\item\n  It requires its services be made available to guest users, with no\n  authentication necessary.\n\\item\n  It contains a\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/password-based-authentication-pipelines}{remote\n  service authorization layer} that needs to drive access to remote\n  services based on granted privileges.\n\\end{itemize}\n\n\\section{API Overview}\\label{api-overview}\n\nLiferay provides an Interface and a \\texttt{ThreadLocal} if you don't\nwant to roll your own policies. If you want to get low level, an API is\nprovided that Liferay itself has used to implement\n\\href{/docs/7-2/user/-/knowledge_base/u/administering-liferay-sync}{Liferay\nSync}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The Interface and \\texttt{ThreadLocal} are available in the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/security/service/access/policy/package-summary.html}{package\n  \\texttt{com.liferay.portal.kernel.security.service.access.policy}}.\n  This package provides classes for basic access to policies. For\n  example, you can use the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/security/service/access/policy/ServiceAccessPolicyManagerUtil.html}{singleton\n  \\texttt{ServiceAccessPolicyManagerUtil}} to obtain Service Access\n  Policies configured in the system. You can also use the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/security/service/access/policy/ServiceAccessPolicyThreadLocal.html}{\\texttt{ServiceAccessPolicyThreadLocal}\n  class} to set and obtain Service Access Policies granted to the\n  current request thread.\n\n  At this level, you can get a list of the configured policies to let\n  your app/client choose a policy for accessing services. Also, apps\n  like OAuth can offer a list of available policies during the\n  authorization step in the OAuth workflow and allow the user to choose\n  the policy to assign to the remote application. You can also grant a\n  policy to a current request thread. When a remote client accesses an\n  API, something must tell the Liferay instance which policies are\n  assigned to this call. This something is in most cases an\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/security/auth/verifier/AuthVerifier.html}{\\texttt{AuthVerifier}\n  implementation}. For example, in the case of the OAuth app, an\n  \\texttt{AuthVerifier} implementation assigns the policy chosen by the\n  user in the authorization step.\n\\item\n  The API ships with the product as OSGi modules:\n\\end{enumerate}\n\n\\begin{itemize}\n\\item\n  \\texttt{com.liferay.portal.security.service.access.policy.api.jar}\n\\item\n  \\texttt{com.liferay.portal.security.service.access.policy.service.jar}\n\\item\n  \\texttt{com.liferay.portal.security.service.access.policy.web.jar}\n\n  These OSGi modules are active by default, and you can use them to\n  manage Service Access Policies programmatically. Each module publishes\n  a list of packages and services that can be consumed by other OSGi\n  modules.\n\\end{itemize}\n\nYou can use both tools to develop a token verification module (a module\nthat implements custom security token verification for use in\nauthorizing remote clients) for your app to use. For example, this\nmodule may contain a JSON Web Token implementation for Liferay DXP's\nremote API. A custom token verification module must use the Service\nAccess Policies API during the remote API/web service call to grant the\nassociated policy during the request. The module\n\n\\begin{itemize}\n\\item\n  can use\n  \\texttt{com.liferay.portal.security.service.access.policy.api.jar} and\n  \\texttt{com.liferay.portal.security.service.access.policy.service.jar}\n  to create policies programmatically.\n\\item\n  should use the method\n  \\texttt{ServiceAccessPolicyThreadLocal.addActiveServiceAccessPolicyName()}\n  to grant the associated policy during a web service request.\n\\item\n  can use \\texttt{ServiceAccessPolicyManagerUtil} to display list of\n  supported policies when authorizing the remote application, to\n  associate the token with an existing policy.\n\\end{itemize}\n\n\\section{Service Access Policy\nExample}\\label{service-access-policy-example}\n\n\\href{https://www.liferay.com/supporting-products/liferay-sync}{Liferay\nSync's} \\texttt{sync-security} module is a service access policy module.\nIt uses\n\\texttt{com.liferay.portal.security.service.access.policy.service} to\ncreate the \\texttt{SYNC\\_DEFAULT} and \\texttt{SYNC\\_TOKEN} policies\nprogrammatically. For service calls to Sync's remote API, these policies\ngrant access to Sync's\n\\texttt{com.liferay.sync.service.SyncDLObjectService\\#getSyncContext}\nand \\texttt{com.liferay.sync.service.*}, respectively. Here's the code\nin the \\texttt{sync-security} module that defines and creates these\npolicies:\n\n\\begin{verbatim}\n@Component(immediate = true)\npublic class SyncSAPEntryActivator {\n\n    // Define the policies\n    public static final Object[][] SAP_ENTRY_OBJECT_ARRAYS = new Object[][] {\n        {\n            \"SYNC_DEFAULT\",\n            \"com.liferay.sync.service.SyncDLObjectService#getSyncContext\", true\n        },\n        {\"SYNC_TOKEN\", \"com.liferay.sync.service.*\", false}\n    };\n\n    ...\n\n    // Create the policies\n    protected void addSAPEntry(long companyId) throws PortalException {\n            for (Object[] sapEntryObjectArray : SAP_ENTRY_OBJECT_ARRAYS) {\n                String name = String.valueOf(sapEntryObjectArray[0]);\n                String allowedServiceSignatures = String.valueOf(\n                    sapEntryObjectArray[1]);\n                boolean defaultSAPEntry = GetterUtil.getBoolean(\n                    sapEntryObjectArray[2]);\n\n                SAPEntry sapEntry = _sapEntryLocalService.fetchSAPEntry(\n                    companyId, name);\n\n                if (sapEntry != null) {\n                    continue;\n                }\n\n                Map<Locale, String> map = new HashMap<>();\n\n                map.put(LocaleUtil.getDefault(), name);\n\n                _sapEntryLocalService.addSAPEntry(\n                    _userLocalService.getDefaultUserId(companyId),\n                    allowedServiceSignatures, defaultSAPEntry, true, name, map,\n                    new ServiceContext());\n            }\n    }\n\n    ...\n\n}\n\\end{verbatim}\n\nThis class creates the policies when the module starts. Note that this\nmodule is included and enabled by default. You can access these and\nother policies in \\emph{Control Panel} → \\emph{Configuration} →\n\\emph{Service Access Policy}.\n\nThe \\texttt{sync-security} module must then grant the appropriate policy\nwhen needed. Since every authenticated call to Liferay Sync's remote API\nrequires access to \\texttt{com.liferay.sync.service.*}, the module must\ngrant the \\texttt{SYNC\\_TOKEN} policy to such calls. The module does\nthis with the method\n\\texttt{ServiceAccessPolicyThreadLocal.addActiveServiceAccessPolicyName},\nas shown in this code snippet:\n\n\\begin{verbatim}\nif ((permissionChecker != null) && permissionChecker.isSignedIn()) {\n    ServiceAccessPolicyThreadLocal.addActiveServiceAccessPolicyName(\n        String.valueOf(\n            SyncSAPEntryActivator.SAP_ENTRY_OBJECT_ARRAYS[1][0]));\n}\n\\end{verbatim}\n\nNow every authenticated call to Sync's remote API, regardless of\nauthentication method, has access to\n\\texttt{com.liferay.sync.service.*}. To see the full code example,\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/sync/sync-security/src/main/java/com/liferay/sync/security/servlet/filter/SyncAuthFilter.java}{click\nhere}.\n\nNice! Now you know how to integrate your apps with the Service Access\nPolicies.\n\n\\chapter{Frameworks}\\label{frameworks}\n\nTo make your applications more fully featured and to develop them\nfaster, you can make use of Liferay's development frameworks. These help\nyou create commonly used features---like search, tagging, and\ncomments---without having to develop them from scratch. And since these\nfeatures are tried and tested, you can rest assured knowing they're bug\nfree.\n\nHere are just a few frameworks you'll find here:\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions}{\\textbf{A\nfully-fledged permissions system:}} Implement permissions the way\nthey're implemented with the applications that ship with Liferay DXP for\na consistent, seamless, and robust experience.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{\\textbf{Assets:}}\nPublish data from your application across the system, making it\navailable to those who need it, and enabling other features like\ntagging, categorizing, and comments.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/search}{\\textbf{Search:}}\nIf you have a data-driven application, you can add search capabilities\nby integrating with Liferay's search indexer.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/configurable-applications}{\\textbf{A\nconfiguration system with auto-generated or custom UI:}} Are you\nproviding user-configurable options in your application? Make use of\nLiferay's configuration system and provide a clean and consistent\nexperience for your users.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{\\textbf{File\nmanagement:}} Will your applications work with files? Use Liferay's\nDocuments and Media API to manage them.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/content-publication-management}{\\textbf{Import/Export:}}\nUse Liferay's import/export system to make your application's data\nportable or to stage it for publication to production systems.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/page-fragments}{\\textbf{Web\nFragments:}} Provide your content managers with dynamic chunks of\nfunctionality they can use as building blocks for web pages.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework}{\\textbf{Workflow:}}\nRun the data from your application through an approval process.\n\nThis really just scratches the surface. From\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/item-selector}{pop-up list\nselectors} to a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/social-api}{social\nnetworking API}, as a Liferay developer, you have access to tons of\nframeworks that make your life easier.\n\n\\chapter{Asset Framework}\\label{asset-framework}\n\nThe asset framework is behind many of Liferay's most powerful features.\nIt provides tools for displaying and interacting with various types of\ncontent and data. For example, if you build an event management\napplication that displays a list of upcoming events, you can use the\nasset framework to let users add tags, categories, or comments to make\nentries more self-descriptive. Using the asset framework is also the\nfirst step for integrating other important frameworks like Segmentation\nand Personalization or Workflow.\n\nAs background, the term \\emph{asset} refers to any type of content:\ntext, a file, a URL, an image, documents, blog entries, bookmarks, wiki\npages, or anything you create in your applications.\n\nThe asset framework tutorials assume that you've used Liferay's Service\nBuilder to generate your persistence layer, that you've implemented\npermissions on the entities that you're persisting, and that you've\nenabled them for search and indexing. You can learn more about Liferay's\nService Builder and how to use it in the\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} tutorial section. After that is completed, you can get started\nasset enabling your application.\n\nThis section explores how to leverage the asset framework's various\nfeatures. Here are some features that you'll give your users as you\nimplement them in your app:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Extensively render your assets.\n\\item\n  Associate tags to custom content types. Users can create and assign\n  new tags or use existing tags.\n\\item\n  Associate categories to custom content types.\n\\item\n  Manage tags from the Control Panel. Administrators can even merge\n  tags.\n\\item\n  Manage categories from the Control Panel. This includes the ability to\n  create category hierarchies.\n\\item\n  Relate assets to one another.\n\\end{itemize}\n\nThere are several steps to creating an asset and taking full advantage\nof the asset framework.\n\n\\section{Persistence Operations for\nAssets}\\label{persistence-operations-for-assets}\n\nTo use Liferay's asset framework with an entity, you must inform the\nasset framework about each entity instance you create, modify, and\ndelete. In this sense, it's similar to informing\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions}{Liferay's\npermissions framework} about a new resource. All you have to do is\ninvoke a method of the asset framework that associates an\n\\texttt{AssetEntry} with the entity so Liferay can keep track of the\nentity as an asset. When it's time to update the entity, you update the\nasset at the same time.\n\nTo leverage assets, you must also implement indexers for your portlet's\nentities. Liferay's asset framework uses indexers to manage assets.\n\n\\section{Rendering an Asset}\\label{rendering-an-asset}\n\nOnce you add your asset to the framework, you can render the asset using\nthe Asset Publisher application. The default render, however, only\ndisplays the asset's title and description text. Anything else requires\nadditional coding. For instance, you might want these additional things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  An edit feature for modifying an asset.\n\\item\n  View an asset in its original context (e.g., a blog in the Blogs\n  application; a post in the Message Boards application).\n\\item\n  Embed images, videos, and audio.\n\\item\n  Restrict access to users who do not have permissions to interact with\n  the asset.\n\\item\n  Allow users to comment on the asset.\n\\end{itemize}\n\nYou can dictate your asset's rendering capabilities by providing the\n\\emph{Asset Renderer} framework. There are two prerequisites for asset\nenabling an application:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The application must store asset data. Applications that store a data\n  model meet this requirement.\n\\item\n  The application must contain at least one non-instanceable portlet.\n  \\texttt{Edit} links for the asset cannot be generated without a\n  non-instanceable portlet.\n\\end{enumerate}\n\nSome applications may consist of only one non-instanceable portlet,\nwhile others may consist of a both instanceable and non-instanceable\nportlets. If your application does not currently include a\nnon-instanceable portlet, adding a configuration interface through a\npanel app both enhances the usability of the application, and meets the\nrequirement for adding a non-instanceable portlet to the application.\n\nAfter you have met all the prerequisites, there are two things you must\ndo to get your asset renderer functioning properly for your asset:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create an asset renderer for your custom asset.\n\\item\n  Create an asset renderer factory to create an instance of the asset\n  renderer for each asset entity.\n\\end{enumerate}\n\n\\section{Asset Features}\\label{asset-features}\n\nOnce you have done the necessary work to persist your assets and render\nthem, you can enable Tags, Categories, and Related Assets.\n\n\\section{Tags and Categories}\\label{tags-and-categories}\n\nTags and Categories are two ways that you can organize and connect\nassets. Tags are simple \\emph{ad hoc} groups. Any two assets with the\nsame tag are connected by that tag. Categories are a form of\nhierarchical organization where an administrator can define a number of\ncategories for organization content, images, or other types of assets\nand use those categories to help users find what they're looking for.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/asset-fw-categories-and-tags-options.png}\n\\caption{Adding category and tag input options lets authors aggregate\nand label custom entities.}\n\\end{figure}\n\n\\section{Relating Assets}\\label{relating-assets}\n\nRelating assets connects individual pieces of content across your site\nor portal. This helps users discover related content, particularly when\nthere's an abundance of other available content. For example, assets\nrelated to a web content article appear alongside that entry in the\nAsset Publisher application.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/asset-related-content-asset-publisher.png}\n\\caption{You and your users can find it helpful to relate assets to\nentities, such as this blogs entry.}\n\\end{figure}\n\n\\section{Implementing Asset Priority}\\label{implementing-asset-priority}\n\nThe \\href{/docs/7-2/user/-/knowledge_base/u/publishing-assets}{Asset\nPublisher} lets you order assets by priority. For this to work, however,\nusers must be able to set the asset's priority when creating or editing\nthe asset. For example, when creating or editing web content, users can\nassign a priority in the Metadata section's Priority field.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-categorization.png}\n\\caption{The Priority field lets users set an asset's priority.}\n\\end{figure}\n\nReady to implement assets? The rest of the tutorials show you how.\n\n\\chapter{Adding, Updating, and Deleting\nAssets}\\label{adding-updating-and-deleting-assets}\n\nThis section shows you how to enable assets for your custom entities and\nimplement indexes for them. It's time to get started!\n\n\\section{Preparing Your Project for the Asset\nFramework}\\label{preparing-your-project-for-the-asset-framework}\n\nIn your project's \\texttt{service.xml} file, add an asset entry entity\nreference for your custom entity. Add the following \\texttt{reference}\ntag before your custom entity's closing\n\\texttt{\\textless{}/entity\\textgreater{}} tag.\n\n\\begin{verbatim}\n<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetEntry\" />\n\\end{verbatim}\n\nThen \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{run\nService Builder.}\n\nNow you're ready to implement adding and updating assets!\n\n\\section{Adding and Updating Assets}\\label{adding-and-updating-assets}\n\nYour \\texttt{-LocalServiceImpl} Java class inherits from its parent base\nclass an \\texttt{AssetEntryLocalService} instance; it's assigned to the\nvariable \\texttt{assetEntryLocalService}. To add your custom entity as a\nLiferay asset, you must invoke the \\texttt{assetEntryLocalService}'s\n\\texttt{updateEntry} method.\n\nHere's what the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-impl/com/liferay/portlet/asset/service/impl/AssetEntryLocalServiceImpl.html\\#updateEntry-long-long-java.util.Date-java.util.Date-java.lang.String-long-java.lang.String-long-long:A-java.lang.String:A-boolean-boolean-java.util.Date-java.util.Date-java.util.Date-java.util.Date-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-int-int-java.lang.Double-}{\\texttt{updateEntry}}\nmethod's signature looks like:\n\n\\begin{verbatim}\nAssetEntry updateEntry(\n    long userId, long groupId, Date createDate, Date modifiedDate,\n    String className, long classPK, String classUuid, long classTypeId,\n    long[] categoryIds, String[] tagNames, boolean listable,\n    boolean visible, Date startDate, Date endDate, Date publishDate,\n    Date expirationDate, String mimeType, String title,\n    String description, String summary, String url, String layoutUuid,\n    int height, int width, Double priority)\nthrows PortalException\n\\end{verbatim}\n\nHere are descriptions of each of the \\texttt{updateEntry} method's\nparameters:\n\n\\texttt{userId}: identifies the user updating the content.\n\n\\texttt{groupId}: identifies the scope of the created content. If your\ncontent doesn't support scopes (extremely rare), pass \\texttt{0} as the\nvalue.\n\n\\texttt{createDate}: the date the entity was created.\n\n\\texttt{modifiedDate}: the date of this change to the entity.\n\n\\texttt{className}: identifies the entity's class. The recommended\nconvention is to use the name of the Java class that represents your\ncontent type. For example, you can pass in the value returned from\n\\texttt{{[}YourClassName{]}.class.getName()}.\n\n\\texttt{classPK}: identifies the specific entity instance,\ndistinguishing it from other instances of the same type. It's usually\nthe primary key of the table where the entity is stored.\n\n\\texttt{classUuid}: serves as a secondary identifier that's guaranteed\nto be universally unique. It correlates entity instances across scopes.\nIt's especially useful if your content is exported and imported across\nseparate portals.\n\n\\texttt{classTypeId}: identifies the particular variation of this class,\nif it has any variations. Otherwise, use \\texttt{0}.\n\n\\texttt{categoryIds}: represent the categories selected for the entity.\nThe asset framework stores them for you.\n\n\\texttt{tagNames}: represent the tags selected for the entity. The asset\nframework stores them for you.\n\n\\texttt{listable}: specifies whether the entity can be shown in dynamic\nlists of content (such as asset publisher configured dynamically).\n\n\\texttt{visible}: specifies whether the entity is approved.\n\n\\texttt{startDate}: the entity's publish date. You can use it to specify\nwhen an Asset Publisher should show the entity's content.\n\n\\texttt{endDate}: the date the entity is taken down. You can use it to\nspecify when an Asset Publisher should stop showing the entity's\ncontent.\n\n\\texttt{publishDate}: the date the entity will start to be shown.\n\n\\texttt{expirationDate}: the date the entity will no longer be shown.\n\n\\texttt{mimetype}: the Multi-Purpose Internet Mail Extensions type, such\nas\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ContentTypes.html\\#TEXT_HTML}{ContentTypes.TEXT\\_HTML},\nused for the content.\n\n\\texttt{title}: the entity's name.\n\n\\texttt{description}: a \\texttt{String}-based textual description of the\nentity.\n\n\\texttt{summary}: a shortened or truncated sample of the entity's\ncontent.\n\n\\texttt{url}: a URL to optionally associate with the entity.\n\n\\texttt{layoutUuid}: the universally unique ID of the layout of the\nentry's default display page.\n\n\\texttt{height}: this can be set to \\texttt{0}.\n\n\\texttt{width}: this can be set to \\texttt{0}.\n\n\\texttt{priority}: specifies how the entity is ranked among peer entity\ninstances. Low numbers take priority over higher numbers.\n\nThe following code from Liferay's Wiki application's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/wiki/wiki-service/src/main/java/com/liferay/wiki/service/impl/WikiPageLocalServiceImpl.java}{\\texttt{WikiPageLocalServiceImpl}}\nJava class demonstrates invoking the \\texttt{updateEntry} method on the\nwiki page entity called \\texttt{WikiPage}. In your \\texttt{add-} method,\nyou could invoke \\texttt{updateEntry} after adding your entity's\nresources. Likewise, in your \\texttt{update-} method, you could invoke\n\\texttt{updateEntry} after calling the \\texttt{super.update-} method.\nThe code below is called in the \\texttt{WikiPageLocalServiceImpl}\nclass's \\texttt{updateStatus(...)} method.\n\n\\begin{verbatim}\nAssetEntry assetEntry = assetEntryLocalService.updateEntry(\n    userId, page.getGroupId(), page.getCreateDate(),\n    page.getModifiedDate(), WikiPage.class.getName(),\n    page.getResourcePrimKey(), page.getUuid(), 0,\n    assetCategoryIds, assetTagNames, true, true, null, null,\n    page.getCreateDate(), null, ContentTypes.TEXT_HTML,\n    page.getTitle(), null, null, null, null, 0, 0, null);\n\nIndexer<JournalArticle> indexer = IndexerRegistryUtil.nullSafeGetIndexer(\n    WikiPage.class);\n\nindexer.reindex(page);\n\\end{verbatim}\n\nImmediately after invoking the \\texttt{updateEntry} method, you must\nupdate the respective asset and index the entity instance. The above\ncode calls the indexer to index (or re-index, if updating) the entity.\nThat's all there is to it.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} The current user's ID and the scope group ID are commonly\nmade available in service context parameters. If the service context you\nuse contains them, then you can access them in calls like these:\n\nlong userId = serviceContext.getUserId(); long groupId =\nserviceContext.getScopeGroupId();\n\n\\noindent\\hrulefill\n\nNext, you'll learn what's needed to delete an entity that's associated\nwith an asset.\n\n\\section{Deleting Assets}\\label{deleting-assets}\n\nWhen deleting your entities, you should delete the associated assets and\nindexes at the same time. This cleans up stored asset and index\ninformation, which keeps the Asset Publisher from showing information\nfor the entities you've deleted.\n\nIn your \\texttt{-LocalServiceImpl} Java class, open your\n\\texttt{delete-} method. After the code that deletes the entity's\nresource, delete the entity instance's asset entry and index.\n\nHere's some code which deletes an asset entry and an index associated\nwith a portlet's entity.\n\n\\begin{verbatim}\nassetEntryLocalService.deleteEntry(\n    ENTITY.class.getName(), assetEntry.getEntityId());\n\nIndexer<ENTITY> indexer = IndexerRegistryUtil.nullSafeGetIndexer(ENTITY.class);\nindexer.delete(assetEntry);\n\\end{verbatim}\n\nIn your \\texttt{-LocalServiceImpl} class, you can write similar code.\nReplace the \\emph{ENTITY} class name and variable with your entity's\nname.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} For Liferay's Asset Publisher application to show\nyour entity, the entity must have an Asset Renderer.\n\nNote also that an Asset Renderer is how you show a user the components\nof your entity in the Asset Publisher. On deploying your portlet with\nasset, indexer, and asset rendering implementations in place, an Asset\nPublisher can show your custom entities!\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/basic-asset-in-asset-publisher.png}\n\\caption{It can be useful to show custom entities, like this wiki page\nentity, in a JSP or in an Asset Publisher.}\n\\end{figure}\n\nGreat! Now you know how to add, update, and delete assets in your apps!\n\n\\chapter{Creating an Asset Renderer}\\label{creating-an-asset-renderer}\n\nIn this tutorial, you'll learn how to create an \\texttt{Asset\\ Renderer}\nand associate your JSP templates with it, along with configuring several\nother options by studying a Liferay asset: Blogs.\n\nThe Blogs application offers many different ways to access and render a\nblogs asset. You'll learn how a blogs asset provides an edit feature,\ncomment section, original context viewing (i.e., viewing an asset from\nthe Blogs application), workflow, and more. You'll also learn how it\nuses JSP templates to display various blog views. The Blogs application\nis an extensive example of how an asset renderer can be customized to\nfit your needs.\n\nTo learn how an asset renderer is created, you'll create the\npre-existing\n\\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/blogs/web/asset/BlogsEntryAssetRenderer.html}{\\texttt{BlogsEntryAssetRenderer}}\nclass, which configures the asset renderer framework for the Blogs\napplication.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new package in your existing project for your asset-related\n  classes. For instance, the \\texttt{BlogsEntryAssetRenderer} class\n  resides in the \\texttt{com.liferay.blogs.web} module's\n  \\texttt{com.liferay.blogs.web.asset} package.\n\\item\n  Create your \\texttt{-AssetEntry} class for your application in the new\n  \\texttt{-.asset} package and have it implement the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/asset/kernel/model/AssetEntry.html}{\\texttt{AssetEntry}}\n  interface. Consider the \\texttt{BlogsEntryAssetRenderer} class as an\n  example:\n\n\\begin{verbatim}\npublic class BlogsEntryAssetRenderer\n    extends BaseJSPAssetRenderer<BlogsEntry> implements TrashRenderer {\n\\end{verbatim}\n\n  The \\texttt{BlogsEntryAssetRenderer} class extends the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/asset/kernel/model/BaseJSPAssetRenderer.html}{\\texttt{BaseJSPAssetRenderer}},\n  which is an extension class intended for those who plan on using JSP\n  templates to generate their asset's HTML. The\n  \\texttt{BaseJSPAssetRenderer} class implements the\n  \\texttt{AssetRenderer} interface. You'll notice the asset renderer\n  also implements the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/trash/TrashRenderer.html}{\\texttt{TrashRenderer}}\n  interface. This is a common practice for many applications, so they\n  can use Liferay DXP's Recycle Bin.\n\\item\n  Define the asset renderer class's constructor, which typically sets\n  the asset object to use in the asset renderer class.\n\n\\begin{verbatim}\npublic BlogsEntryAssetRenderer(\n    BlogsEntry entry, ResourceBundleLoader resourceBundleLoader) {\n\n    _entry = entry;\n    _resourceBundleLoader = resourceBundleLoader;\n}\n\\end{verbatim}\n\n  The \\texttt{BlogsEntryAssetRenderer} also sets the resource bundle\n  loader, which loads the language keys for a module. You can learn more\n  about the resource bundle loader in the\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys}{Overriding\n  Language Keys} tutorial.\n\n  Also, make sure to define the \\texttt{\\_entry} and\n  \\texttt{\\_resourceBundleLoader} fields in the class:\n\n\\begin{verbatim}\nprivate final BlogsEntry _entry;\nprivate final ResourceBundleLoader _resourceBundleLoader;\n\\end{verbatim}\n\\item\n  Now that your class declaration and constructor are defined for the\n  blogs asset renderer, you must begin connecting your asset renderer to\n  your asset. The following getter methods accomplish this:\n\n\\begin{verbatim}\n@Override\npublic BlogsEntry getAssetObject() {\n    return _entry;\n}\n\n@Override\npublic String getClassName() {\n    return BlogsEntry.class.getName();\n}\n\n@Override\npublic long getClassPK() {\n    return _entry.getEntryId();\n}\n\n@Override\npublic long getGroupId() {\n    return _entry.getGroupId();\n}\n\n@Override\npublic String getType() {\n    return BlogsEntryAssetRendererFactory.TYPE;\n}\n\n@Override\npublic String getUuid() {\n    return _entry.getUuid();\n}\n\\end{verbatim}\n\n  The \\texttt{getAssetObject()} method sets the \\texttt{BlogsEntry} that\n  was set in the constructor as your asset to track. Likewise, the\n  \\texttt{getType()} method references the blogs asset renderer factory\n  for the type of asset your asset renderer renders. Of course, the\n  asset renderer type is \\texttt{blog}, which you'll set in the factory\n  later.\n\\item\n  Your asset renderer must link to the portlet that owns the entity. In\n  the case of a blogs asset, its portlet ID should be linked to the\n  Blogs application.\n\n\\begin{verbatim}\n@Override\npublic String getPortletId() {\n    AssetRendererFactory<BlogsEntry> assetRendererFactory =\n        getAssetRendererFactory();\n\n    return assetRendererFactory.getPortletId();\n}\n\\end{verbatim}\n\n  The \\texttt{getPortletId()} method instantiates an asset renderer\n  factory for a \\texttt{BlogsEntry} and retrieves the portlet ID for the\n  portlet used to display blogs entries.\n\\item\n  If you want to enable workflow for your asset, add the following\n  method similar to what was done for the Blogs application:\n\n\\begin{verbatim}\n@Override\npublic int getStatus() {\n    return _entry.getStatus();\n}\n\\end{verbatim}\n\n  This method retrieves the workflow status for the asset.\n\\item\n  Another feature many developers want for their asset is comments. This\n  is enabled for the Blogs application with the following method:\n\n\\begin{verbatim}\n@Override\npublic String getDiscussionPath() {\n    if (PropsValues.BLOGS_ENTRY_COMMENTS_ENABLED) {\n        return \"edit_entry_discussion\";\n    }\n    else {\n        return null;\n    }\n}\n\\end{verbatim}\n\n  A comments section is an available option if it returns a non-null\n  value. For the comments section to display for your asset, you must\n  enable it in the Asset Publisher's \\emph{Options}\n  (\\includegraphics{./images/icon-options.png}) → \\emph{Configuration} →\n  \\emph{Setup} → \\emph{Display Settings} section.\n\\item\n  At a minimum, you should create a title and summary for your asset.\n  Here's how the \\texttt{BlogsEntryAssetRenderer} does it:\n\n\\begin{verbatim}\n@Override\npublic String getSummary(\n    PortletRequest portletRequest, PortletResponse portletResponse) {\n\n    int abstractLength = AssetUtil.ASSET_ENTRY_ABSTRACT_LENGTH;\n\n    if (portletRequest != null) {\n        abstractLength = GetterUtil.getInteger(\n            portletRequest.getAttribute(\n                WebKeys.ASSET_ENTRY_ABSTRACT_LENGTH),\n            AssetUtil.ASSET_ENTRY_ABSTRACT_LENGTH);\n    }\n\n    String summary = _entry.getDescription();\n\n    if (Validator.isNull(summary)) {\n        summary = HtmlUtil.stripHtml(\n            StringUtil.shorten(_entry.getContent(), abstractLength));\n    }\n\n    return summary;\n}\n\n@Override\npublic String getTitle(Locale locale) {\n    ResourceBundle resourceBundle =\n        _resourceBundleLoader.loadResourceBundle(\n            LanguageUtil.getLanguageId(locale));\n\n    return BlogsEntryUtil.getDisplayTitle(resourceBundle, _entry);\n}\n\\end{verbatim}\n\n  These two methods return information about your asset, so the asset\n  publisher can display it. The title and summary can be anything.\n\n  The \\texttt{getSummary(...)} method for Blogs returns the abstract\n  description for a blog asset. If the abstract description does not\n  exist, the content of the blog is used as an abstract. You'll learn\n  more about abstracts and other content specifications later.\n\n  The \\texttt{getTitle(...)} method for Blogs uses the resource bundle\n  loader you configured in the constructor to load your module's\n  resource bundle and return the display title for your asset.\n\\item\n  If you want to provide a unique URL for your asset, you can specify a\n  URL title. A URL title is the URL used to access your asset directly\n  (e.g., localhost:8080/-/this-is-my-blog-asset). You can do this by\n  providing the following method:\n\n\\begin{verbatim}\n@Override\npublic String getUrlTitle() {\n    return _entry.getUrlTitle();\n}\n\\end{verbatim}\n\\item\n  Insert the \\texttt{isPrintable()} method, which enables the Asset\n  Publisher's printing capability for your asset.\n\n\\begin{verbatim}\n@Override\npublic boolean isPrintable() {\n    return true;\n}\n\\end{verbatim}\n\n  This displays a Print icon when your asset is displayed in the Asset\n  Publisher. For the icon to appear, you must enable it in the Asset\n  Publisher's \\emph{Options} → \\emph{Configuration} → \\emph{Setup} →\n  \\emph{Display Settings} section.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/asset-publisher-printing.png}\n  \\caption{Enable printing in the Asset Publisher to display the Print\n  icon for your asset.}\n  \\end{figure}\n\\item\n  If your asset is protected by permissions, you can set permissions for\n  the asset via the asset renderer. See the logic below for an example\n  used in the \\texttt{BlogsEntryAssetRenderer} class:\n\n\\begin{verbatim}\n@Override\npublic long getUserId() {\n    return _entry.getUserId();\n}\n\n@Override\npublic String getUserName() {\n    return _entry.getUserName();\n}\n\npublic boolean hasDeletePermission(PermissionChecker permissionChecker) {\n    return BlogsEntryPermission.contains(\n        permissionChecker, _entry, ActionKeys.DELETE);\n}\n\n@Override\npublic boolean hasEditPermission(PermissionChecker permissionChecker) {\n    return BlogsEntryPermission.contains(\n        permissionChecker, _entry, ActionKeys.UPDATE);\n}\n\n@Override\npublic boolean hasViewPermission(PermissionChecker permissionChecker) {\n    return BlogsEntryPermission.contains(\n        permissionChecker, _entry, ActionKeys.VIEW);\n}\n\\end{verbatim}\n\n  Before you can check if a user has permission to view your asset, you\n  must use the \\texttt{getUserId()} and \\texttt{getUserName()} to\n  retrieve the entry's user ID and username, respectively. Then there\n  are three boolean permission methods that check if the user can view,\n  edit, or delete your blogs entry. These permissions are for specific\n  entity instances. Global permissions for blog entries are implemented\n  in the factory, which you'll do later.\n\\end{enumerate}\n\nAwesome! You've learned how to set up the blogs asset renderer to\n\n\\begin{itemize}\n\\tightlist\n\\item\n  connect to an asset\n\\item\n  connect to the asset's portlet\n\\item\n  use workflow management\n\\item\n  use a comments section\n\\item\n  retrieve the asset's title and summary\n\\item\n  generate the asset's unique URL\n\\item\n  display a print icon\n\\item\n  check permissions for the asset\n\\end{itemize}\n\nNow you need to create the templates to render the HTML. The\n\\texttt{BlogsEntryAssetRenderer} is configured to use JSP templates to\ngenerate HTML for the Asset Publisher. You'll learn more about how to do\nthis next.\n\n\\chapter{Configuring JSP Templates for an Asset\nRenderer}\\label{configuring-jsp-templates-for-an-asset-renderer}\n\nAn asset can be displayed in several different ways in the Asset\nPublisher. There are three templates to implement provided by the\n\\texttt{AssetRenderer} interface:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{abstract}\n\\item\n  \\texttt{full\\_content}\n\\item\n  \\texttt{preview}\n\\end{itemize}\n\nBesides these supported templates, you can also create JSPs for buttons\nfor direct access and manipulation of the asset. For example,\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Edit\n\\item\n  View\n\\item\n  View in Context\n\\end{itemize}\n\nThe \\texttt{BlogsEntryAssetRenderer} customizes the\n\\texttt{AssetRenderer}'s provided JSP templates and adds a few other\nfeatures using JSPs. You'll inspect how the blogs asset renderer is put\ntogether to satisfy JSP template development requirements.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the \\texttt{getJspPath(...)} method to your asset renderer. This\n  method should return the path to your JSP, which is rendered inside\n  the Asset Publisher. This is how the \\texttt{BlogsEntryAssetRenderer}\n  uses this method:\n\n\\begin{verbatim}\n@Override\npublic String getJspPath(HttpServletRequest request, String template) {\n    if (template.equals(TEMPLATE_ABSTRACT) ||\n        template.equals(TEMPLATE_FULL_CONTENT)) {\n\n        return \"/blogs/asset/\" + template + \".jsp\";\n    }\n    else {\n        return null;\n    }\n}\n\\end{verbatim}\n\n  Blogs assets provide \\texttt{abstract.jsp} and\n  \\texttt{full\\_content.jsp} templates. This means that a blogs asset\n  can render a blog's abstract description or the blog's full content in\n  the Asset Publisher. Those templates are located in the\n  \\texttt{com.liferay.blogs.web} module's\n  \\texttt{src/main/resources/META-INF/resources/blogs/asset} folder. You\n  could create a similar folder for your JSP templates used for this\n  method. The other template provided by the \\texttt{AssetRenderer}\n  interface, \\texttt{preview.jsp}, is not customized by the blogs asset\n  renderer, so its default template is implemented.\n\n  You must create a link to display the full content of the asset.\n  You'll do this later.\n\\item\n  Now that you've added the path to your JSP, you must include that JSP.\n  Since the \\texttt{BlogsEntryAssetRenderer} class extends the\n  \\texttt{BaseJSPAssetRenderer}, it already has an \\texttt{include(...)}\n  method to render a specific JSP. You must override this method to set\n  an attribute in the request to use in the blog's views:\n\n\\begin{verbatim}\n@Override\npublic boolean include(\n        HttpServletRequest request, HttpServletResponse response,\n        String template)\n    throws Exception {\n\n    request.setAttribute(WebKeys.BLOGS_ENTRY, _entry);\n\n    return super.include(request, response, template);\n}\n\\end{verbatim}\n\n  The attribute includes the blogs entry object. Adding the blog object\n  this way is not mandatory; you could obtain the blog entry directly\n  from the view. Using the \\texttt{include(...)} method, however,\n  follows the best practice for MVC portlets.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/blogs-asset-views.png}\n  \\caption{The abstract and full content views are rendered differently\n  for blogs.}\n  \\end{figure}\n\\end{enumerate}\n\nTerrific! You've learned how to apply JSPs supported by the Asset\nPublisher for your asset. That's not all you can do with JSP templates,\nhowever! The asset renderer framework provides several other methods\nthat let you render convenient buttons for your asset.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Blogs assets provide an Edit button for editing the asset. Provide\n  this by adding the following method to the\n  \\texttt{BlogsEntryAssetRenderer} class:\n\n\\begin{verbatim}\n@Override\npublic PortletURL getURLEdit(\n        LiferayPortletRequest liferayPortletRequest,\n        LiferayPortletResponse liferayPortletResponse)\n    throws Exception {\n\n    Group group = GroupLocalServiceUtil.fetchGroup(_entry.getGroupId());\n\n    PortletURL portletURL = PortalUtil.getControlPanelPortletURL(\n        liferayPortletRequest, group, BlogsPortletKeys.BLOGS, 0, 0,\n        PortletRequest.RENDER_PHASE);\n\n    portletURL.setParameter(\"mvcRenderCommandName\", \"/blogs/edit_entry\");\n    portletURL.setParameter(\"entryId\", String.valueOf(_entry.getEntryId()));\n\n    return portletURL;\n}\n\\end{verbatim}\n\n  The Asset Publisher loads the blogs asset using the Blogs application.\n  Then the \\texttt{edit\\_entry.jsp} template generates the HTML for an\n  editing UI. Once the necessary edits are made to the asset, it can be\n  saved from the Asset Publisher. Pretty cool, right?\n\\item\n  You can specify how to view your asset by providing methods similar to\n  the methods outlined below in the \\texttt{BlogsEntryAssetRenderer}\n  class:\n\n\\begin{verbatim}\n@Override\npublic String getURLView(\n        LiferayPortletResponse liferayPortletResponse,\n        WindowState windowState)\n    throws Exception {\n\n    AssetRendererFactory<BlogsEntry> assetRendererFactory =\n        getAssetRendererFactory();\n\n    PortletURL portletURL = assetRendererFactory.getURLView(\n        liferayPortletResponse, windowState);\n\n    portletURL.setParameter(\"mvcRenderCommandName\", \"/blogs/view_entry\");\n    portletURL.setParameter(\"entryId\", String.valueOf(_entry.getEntryId()));\n    portletURL.setWindowState(windowState);\n\n    return portletURL.toString();\n}\n\n@Override\npublic String getURLViewInContext(\n    LiferayPortletRequest liferayPortletRequest,\n    LiferayPortletResponse liferayPortletResponse,\n    String noSuchEntryRedirect) {\n\n    return getURLViewInContext(\n        liferayPortletRequest, noSuchEntryRedirect, \"/blogs/find_entry\",\n        \"entryId\", _entry.getEntryId());\n}\n\\end{verbatim}\n\n  The \\texttt{getURLView(...)} method generates a URL that displays the\n  full content of the asset in the Asset Publisher. This is assigned to\n  the clickable asset name. The \\texttt{getURLViewInContext(...)} method\n  provides a similar URL assigned to the asset name, but the URL\n  redirects to the original context of the asset (e.g., viewing a blogs\n  asset in the Blogs application). Deciding which view to render is\n  configurable by navigating to the Asset Publisher's \\emph{Options} →\n  \\emph{Configuration} → \\emph{Setup} → \\emph{Display Settings} section\n  and choosing between \\emph{Show Full Content} and \\emph{View in\n  Context} for the Asset Link Behavior drop-down menu.\n\\end{enumerate}\n\nThe Blogs application provides \\texttt{abstract} and\n\\texttt{full\\_content} JSP templates that override the ones provided by\nthe \\texttt{AssetRenderer} interface. The third template,\n\\texttt{preview}, could also be customized. You can view the default\n\\texttt{preview.jsp} template rendered in the \\emph{Add} →\n\\emph{Content} menu.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/preview-template-asset-renderer.png}\n\\caption{The \\texttt{preview} template displays a preview of the asset\nin the Content section of the Add menu.}\n\\end{figure}\n\nYou've learned all about implementing the \\texttt{AssetRenderer}'s\nprovided templates and customizing them to fit your needs. Next, you'll\nput your asset renderer into action by creating a factory.\n\n\\chapter{Creating a Factory for the Asset\nRenderer}\\label{creating-a-factory-for-the-asset-renderer}\n\nYou've successfully created an asset renderer, but you must create a\nfactory class to generate asset renderers for each asset instance. For\nexample, the blogs asset renderer factory instantiates\n\\texttt{BlogsEntryAssetRenderer} for each blogs asset displayed in an\nAsset Publisher.\n\nYou'll continue the blogs asset renderer example by creating the blogs\nasset renderer factory.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create an \\texttt{-AssetRenderFactory} class in the same folder as its\n  asset renderer class. For blogs, the\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/blogs/web/asset/BlogsEntryAssetRendererFactory.html}{\\texttt{BlogsEntryAssetRendererFactory}}\n  class resides in the \\texttt{com.liferay.blogs.web} module's\n  \\texttt{com.liferay.blogs.web.asset} package. The factory class should\n  extend the \\texttt{BaseAssetRendererFactory} class and the asset type\n  should be specified as its parameter. You can see how this was done in\n  the \\texttt{BlogsEntryAssetRendererFactory} class below\n\n\\begin{verbatim}\npublic class BlogsEntryAssetRendererFactory\n    extends BaseAssetRendererFactory<BlogsEntry> {\n\\end{verbatim}\n\\item\n  Create an \\texttt{@Component} annotation section above the class\n  declaration. This annotation is responsible for registering the\n  factory instance for the asset.\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\"javax.portlet.name=\" + BlogsPortletKeys.BLOGS},\n    service = AssetRendererFactory.class\n)\npublic class BlogsEntryAssetRendererFactory\n    extends BaseAssetRendererFactory<BlogsEntry> {\n\\end{verbatim}\n\n  There are a few annotation elements you should set:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    The \\texttt{immediate} element directs the factory to start in\n    Liferay DXP when its module starts.\n  \\item\n    The \\texttt{property} element sets the portlet that is associated\n    with the asset. The Blogs portlet is specified, since this is the\n    Blogs asset renderer factory.\n  \\item\n    The \\texttt{service} element should point to the\n    \\texttt{AssetRendererFactory.class} interface.\n  \\end{itemize}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** If you're using a Java EE portlet WAR, you must register the asset\n renderer factory in the portlet's `liferay-portlet.xml` file. In an \n OSGi-based Liferay MVC portlet, the registration\n process is completed automatically by OSGi using the `@Component`\n annotation.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Create a constructor for the factory class that presets private\n  attributes of the factory.\n\n\\begin{verbatim}\npublic BlogsEntryAssetRendererFactory() {\n    setClassName(BlogsEntry.class.getName());\n    setLinkable(true);\n    setPortletId(BlogsPortletKeys.BLOGS);\n    setSearchable(true);\n}\n\\end{verbatim}\n\n  \\texttt{linkable}: other assets can select blogs assets as their\n  related assets.\n\n  \\texttt{searchable}: blogs can be found when searching for assets.\n\n  Setting the class name and portlet ID links the asset renderer factory\n  to the entity.\n\\item\n  Create the asset renderer for your asset. This is done by calling its\n  constructor.\n\n\\begin{verbatim}\n@Override\npublic AssetRenderer<BlogsEntry> getAssetRenderer(long classPK, int type)\n    throws PortalException {\n\n    BlogsEntry entry = _blogsEntryLocalService.getEntry(classPK);\n\n    BlogsEntryAssetRenderer blogsEntryAssetRenderer =\n        new BlogsEntryAssetRenderer(entry, _resourceBundleLoader);\n\n    blogsEntryAssetRenderer.setAssetRendererType(type);\n    blogsEntryAssetRenderer.setServletContext(_servletContext);\n\n    return blogsEntryAssetRenderer;\n}\n\\end{verbatim}\n\n  For blogs, the asset is retrieved by calling the Blogs application's\n  local service. Then the asset renderer is instantiated using the blogs\n  asset and resource bundle loader. Next, the type and servlet context\n  is set for the asset renderer. Finally, the configured asset renderer\n  is returned.\n\n  There are a few variables in the \\texttt{getAssetRenderer(...)} method\n  you must create. You'll set those variables and learn what they're\n  doing next.\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\alph{enumii}.}\n  \\tightlist\n  \\item\n    You must get the entry by calling the Blogs application's local\n    service. You can instantiate this service by creating a private\n    field and setting it using a setter method:\n  \\end{enumerate}\n\n\\begin{verbatim}\n@Reference(unbind = \"-\")\nprotected void setBlogsEntryLocalService(\n    BlogsEntryLocalService blogsEntryLocalService) {\n\n    _blogsEntryLocalService = blogsEntryLocalService;\n}\n\nprivate BlogsEntryLocalService _blogsEntryLocalService;\n\\end{verbatim}\n\n  The setter method is annotated with the \\texttt{@Reference} tag.\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\alph{enumii}.}\n  \\setcounter{enumii}{1}\n  \\tightlist\n  \\item\n    You must specify the resource bundle loader since it was specified\n    in the \\texttt{BlogsEntryAssetRenderer}'s constructor:\n  \\end{enumerate}\n\n\\begin{verbatim}\n@Reference(\n    target = \"(bundle.symbolic.name=com.liferay.blogs.web)\", unbind = \"-\"\n)\npublic void setResourceBundleLoader(\n    ResourceBundleLoader resourceBundleLoader) {\n\n    _resourceBundleLoader = resourceBundleLoader;\n}\n\nprivate ResourceBundleLoader _resourceBundleLoader;\n\\end{verbatim}\n\n  Make sure the \\texttt{osgi.web.symbolicname} in the \\texttt{target}\n  property of the \\texttt{@Reference} annotation is set to the same\n  value as the \\texttt{Bundle-SymbolicName} defined in the\n  \\texttt{bnd.bnd} file of the module the factory resides in.\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\alph{enumii}.}\n  \\setcounter{enumii}{2}\n  \\item\n    The asset renderer \\texttt{type} integer is set for the asset\n    renderer, but why an integer? Liferay DXP needs to differentiate\n    when it should display the latest \\emph{approved} version of the\n    asset, or the latest version, even if it's unapproved (e.g.,\n    unapproved versions would be displayed for reviewers of the asset in\n    a workflow). For these situations, the asset renderer factory should\n    receive either\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{0} for the latest version of the asset\n    \\item\n      \\texttt{1} for the latest approved version of the asset\n    \\end{itemize}\n  \\item\n    Since the Blogs application provides its own JSPs, it must pass a\n    reference of the servlet context to the asset renderer. This is\n    always required when using custom JSPs in an asset renderer:\n  \\end{enumerate}\n\n\\begin{verbatim}\n@Reference(\n    target = \"(osgi.web.symbolicname=com.liferay.blogs.web)\", unbind = \"-\"\n)\npublic void setServletContext(ServletContext servletContext) {\n    _servletContext = servletContext;\n}\n\nprivate ServletContext _servletContext;\n\\end{verbatim}\n\\item\n  Set the type of asset that the asset factory associates with and\n  provide a getter method to retrieve that type. Also, provide another\n  getter to retrieve the blogs entry class name, which is required:\n\n\\begin{verbatim}\npublic static final String TYPE = \"blog\";\n\n@Override\npublic String getType() {\n    return TYPE;\n}\n\n@Override\npublic String getClassName() {\n    return BlogsEntry.class.getName();\n}\n\\end{verbatim}\n\\item\n  Set the Lexicon icon for the asset:\n\n\\begin{verbatim}\n@Override\npublic String getIconCssClass() {\n    return \"blogs\";\n}\n\\end{verbatim}\n\n  You can find a list of all available Lexicon icons\n  \\href{https://liferay.design/lexicon/core-components/icons/}{here}.\n\\item\n  Add methods that generate URLs to add and view the asset.\n\n\\begin{verbatim}\n@Override\npublic PortletURL getURLAdd(\n    LiferayPortletRequest liferayPortletRequest,\n    LiferayPortletResponse liferayPortletResponse, long classTypeId) {\n\n    PortletURL portletURL = PortalUtil.getControlPanelPortletURL(\n        liferayPortletRequest, getGroup(liferayPortletRequest),\n        BlogsPortletKeys.BLOGS, 0, 0, PortletRequest.RENDER_PHASE);\n\n    portletURL.setParameter(\"mvcRenderCommandName\", \"/blogs/edit_entry\");\n\n    return portletURL;\n}\n\n@Override\npublic PortletURL getURLView(\n    LiferayPortletResponse liferayPortletResponse,\n    WindowState windowState) {\n\n    LiferayPortletURL liferayPortletURL =\n        liferayPortletResponse.createLiferayPortletURL(\n            BlogsPortletKeys.BLOGS, PortletRequest.RENDER_PHASE);\n\n    try {\n        liferayPortletURL.setWindowState(windowState);\n    }\n    catch (WindowStateException wse) {\n    }\n\n    return liferayPortletURL;\n}\n\\end{verbatim}\n\n  If you're paying close attention, you may have noticed the\n  \\texttt{getURLView(...)} method was also implemented in the\n  \\texttt{BlogsEntryAssetRenderer} class. The asset renderer's\n  \\texttt{getURLView(...)} method creates a URL for the specific asset\n  instance, whereas the factory uses the method to create a generic URL\n  that only points to the application managing the assets (e.g., Blogs\n  application).\n\\item\n  Set the global permissions for all blogs assets:\n\n\\begin{verbatim}\n@Override\npublic boolean hasAddPermission(\n        PermissionChecker permissionChecker, long groupId, long classTypeId)\n    throws Exception {\n\n    return BlogsPermission.contains(\n        permissionChecker, groupId, ActionKeys.ADD_ENTRY);\n}\n\n@Override\npublic boolean hasPermission(\n        PermissionChecker permissionChecker, long classPK, String actionId)\n    throws Exception {\n\n    return BlogsEntryPermission.contains(\n        permissionChecker, classPK, actionId);\n}\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! You've finished creating the Blogs application's asset renderer\nfactory! Now you have the knowledge to implement an asset renderer and\nproduce an asset renderer for each asset instance using a factory!\n\n\\chapter{Implementing Asset Categorization and\nTagging}\\label{implementing-asset-categorization-and-tagging}\n\nNow it's time to get started with Tags and Categories.\n\n\\section{Adding Tags and Categories}\\label{adding-tags-and-categories}\n\nYou can use the following tags in the JSPs you provide for\nadding/editing custom entities. Here's what the tags look like in the\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/blogs/blogs-web/src/main/resources/META-INF/resources/blogs/edit_entry.jsp}{edit\\_entry.jsp}\nfor the Blogs portlet:\n\n\\begin{verbatim}\n<liferay-ui:asset-categories-error />\n<liferay-ui:asset-tags-error />\n...\n<aui:fieldset-group markupView=\"lexicon\">\n    ...\n    <aui:fieldset collapsed=\"<%= true %>\" collapsible=\"<%= true %>\" label=\"categorization\">\n        <liferay-asset:asset-categories-selector name=\"categories\" type=\"assetCategories\" />\n\n        <liferay-asset:asset-tags-selector name=\"tags\" type=\"assetTags\" />\n    </aui:fieldset>\n    ...\n</aui:fieldset-group>\n\\end{verbatim}\n\nThe \\texttt{liferay-asset:asset-categories-selector} and\n\\texttt{liferay-asset:asset-tags-selector} tags generate form controls\nthat let users browse/select categories for the entity, browse/select\ntags, and/or create new tags to associate with the entity.\n\nThe \\texttt{liferay-ui:asset-categories-error} and\n\\texttt{liferay-ui:asset-tags-error} tags show messages for errors\noccurring during the asset category or tag input process. The\n\\texttt{aui:fieldset} tag uses a container that lets users hide or show\nthe category and tag input options.\n\nFor styling purposes, the \\texttt{aui:fieldset-group} tag is given the\n\\texttt{lexicon} markup view.\n\n\\section{Displaying Tags and\nCategories}\\label{displaying-tags-and-categories}\n\nTags and categories should be displayed with the content of the asset.\nHere's how to display the tags and categories:\n\n\\begin{verbatim}\n<liferay-asset:asset-categories-available\n    className=\"<%= [AssetEntry].class.getName() %>\"\n    classPK=\"<%= entry.getEntryId() %>\"\n>\n    <div class=\"entry-categories\">\n        <liferay-asset:asset-categories-summary\n            className=\"<%= [AssetEntry].class.getName() %>\"\n            classPK=\"<%= entry.getEntryId() %>\"\n            portletURL=\"<%= renderResponse.createRenderURL() %>\"\n        />\n    </div>\n</liferay-asset:asset-categories-available>\n\n...\n\n<liferay-asset:asset-tags-available\n    className=\"<%= [AssetEntry].class.getName() %>\"\n    classPK=\"<%= entry.getEntryId() %>\"\n>\n    <div class=\"entry-tags\">\n        <liferay-asset:asset-tags-summary\n            className=\"<%= [AssetEntry].class.getName() %>\"\n            classPK=\"<%= entry.getEntryId() %>\"\n            portletURL=\"<%= renderResponse.createRenderURL() %>\"\n        />\n    </div>\n</liferay-asset:asset-tags-available>\n\\end{verbatim}\n\nThe \\texttt{portletURL} parameter is used for both tags and categories.\nEach tag that uses this parameter becomes a link containing the\n\\texttt{portletURL} \\emph{and} \\texttt{tag} or \\texttt{categoryId}\nparameter value. To implement this, you must implement the look-up\nfunctionality in your portlet code. Do this by reading the values of\nthose two parameters and using \\texttt{AssetEntryService} to query the\ndatabase for entries based on the specified tag or category.\n\nDeploy your changes and add/edit a custom entity in your UI. Your form\nshows the categorization and tag input options in a panel that the user\ncan hide/show.\n\nGreat! Now you know how to make category and tag input options available\nto your app's content authors.\n\n\\chapter{Relating Assets}\\label{relating-assets-1}\n\nAfter you complete\n\\href{/docs/frameworks/7-2/-/knowledge_base/frameworks/adding-updating-and-deleting-assets}{Adding,\nUpdating, and Deleting Assets} for your application you can go ahead and\nbegin relating your assets!\n\n\\section{Relating Assets in the Service\nLayer}\\label{relating-assets-in-the-service-layer}\n\nFirst, you must make some modifications to your portlet's service layer.\nYou must implement persisting your entity's asset relationships.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your portlet's \\texttt{service.xml}, put the following line of code\n  below any finder method elements and then run Service Builder:\n\n\\begin{verbatim}\n<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetLink\" />\n\\end{verbatim}\n\\item\n  Modify the \\texttt{add-}, \\texttt{delete-}, and \\texttt{update-}\n  methods in your \\texttt{-LocalServiceImpl} to persist the asset\n  relationships. You'll use your \\texttt{-LocalServiceImpl}'s\n  \\texttt{assetLinkLocalService} instance variable to execute\n  persistence actions.\n\n  For example, consider the Wiki application. When you update wiki\n  assets and statuses, both methods utilize the \\texttt{updateLinks} via\n  your instance variable \\texttt{assetLinkLocalService}. Here's the\n  \\texttt{updateLinks} invocation in the Wiki application's\n  \\texttt{WikiPageLocalServiceImpl.updateStatus(...)} method:\n\n\\begin{verbatim}\nassetLinkLocalService.updateLinks(\n    userId, assetEntry.getEntryId(), assetLinkEntryIds,\n    AssetLinkConstants.TYPE_RELATED);\n\\end{verbatim}\n\n  To call the \\texttt{updateLinks} method, you must pass in the current\n  user's ID, the asset entry's ID, the asset link entries' IDs, and the\n  link type. Invoke this method after creating the asset entry. If you\n  assign to an \\texttt{AssetEntry} variable (e.g., one called\n  \\texttt{assetEntry}) the value returned from invoking\n  \\texttt{assetEntryLocalService.updateEntry}, you can get the asset\n  entry's ID for updating its asset links. Lastly, in order to specify\n  the link type parameter, make sure to import\n  \\texttt{com.liferay.portlet.asset.model.AssetLinkConstants}.\n\\item\n  In your \\texttt{-LocalServiceImpl} class' \\texttt{delete-} method, you\n  must delete the asset's relationships before deleting the asset. For\n  example, you could delete your existing asset link relationships by\n  using the following code:\n\n\\begin{verbatim}\nAssetEntry assetEntry = assetEntryLocalService.fetchEntry(\n    ENTITY.class.getName(), ENTITYId);\n\nassetLinkLocalService.deleteLinks(assetEntry.getEntryId());\n\\end{verbatim}\n\\end{enumerate}\n\nMake sure to replace the \\emph{ENTITY} place holders for your custom\n\\texttt{-delete} method.\n\nSuper! Now your portlet's service layer can handle related assets. Even\nso, there's still nothing in your portlet's UI that lets your users\nrelate assets. You'll take care of that in the next step.\n\n\\section{Relating Assets in the UI}\\label{relating-assets-in-the-ui}\n\nThe UI for linking assets should be in the JSP where users create and\nedit your entity. This way only content creators can relate other assets\nto the entity. Related assets are implemented in the JSP by using the\nLiferay UI tag \\texttt{liferay-ui:input-asset-links} inside a\ncollapsible panel. This code is placed inside the \\texttt{aui:fieldset}\ntags of the JSP.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the \\texttt{liferay-asset:input-asset-links} tag to your form.\n  Here's how it's added in the Blogs application:\n\n\\begin{verbatim}\n<aui:fieldset collapsed=\"<%= true %>\" collapsible=\"<%= true %>\" label=\"related-assets\">\n        <liferay-asset:input-asset-links\n            className=\"<%= [AssetEntry].class.getName() %>\"\n            classPK=\"<%= entryId %>\"\n        />\n</aui:fieldset>\n\\end{verbatim}\n\n  The following screenshot shows the Related Assets menu for an\n  application. Note that it is contained in a collapsible panel titled\n  Related Assets.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/related-assets-select-menu.png}\n  \\caption{Your portlet's entity is now available in the Related Assets\n  \\emph{Select} menu.}\n  \\end{figure}\n\\item\n  Unfortunately, the Related Assets menu shows your entity's fully\n  qualified class name. To replace it with a simplified name for your\n  entity, add a language key with the fully qualified class name for the\n  key and the name you want for the value. Put the language key in file\n  \\texttt{docroot/WEB-INF/src/content/Language.properties} in your\n  portlet. You can refer to the\n  \\href{/docs/frameworks/7-2/-/knowledge_base/frameworks/overriding-language-keys}{Overriding\n  Language Keys} tutorial for more documentation on using language\n  properties.\n\n  Upon redeploying your portlet, the value you assigned to the fully\n  qualified class name in your \\texttt{Language.properties} file shows\n  in the Related Assets menu.\n\\end{enumerate}\n\nAwesome! Now content creators and editors can relate the assets of your\napplication. The next thing you need to do is reveal any such related\nassets to the rest of your application's users. After all, you don't\nwant to give everyone edit access just so they can view related assets!\n\n\\section{Showing Related Assets}\\label{showing-related-assets}\n\nYou can show related assets in your application's view of that entity\nor, if you've implemented asset rendering for your custom entity, you\ncan show related assets in the full content view of your entity for\nusers to view in an Asset Publisher portlet.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  You must get the \\texttt{AssetEntry} object associated with your\n  entity:\n\n\\begin{verbatim}\n<%\nlong insultId = ParamUtil.getLong(renderRequest, \"insultId\");\nInsult ins = InsultLocalServiceUtil.getInsult(insultId);\nAssetEntry assetEntry = AssetEntryLocalServiceUtil.getEntry(Insult.class.getName(), ins.getInsultId());\n%>\n\\end{verbatim}\n\\item\n  Use the \\texttt{liferay-asset:asset-links} tag to show the entity's\n  related assets. For this tag, you retrieve the \\texttt{assetEntryId}\n  from the \\texttt{assetEntry} object, retrieve your asset's\n  \\texttt{className}, and get the entity's primary key\n  (\\texttt{classPK}) from the specific \\texttt{entry}. The tag then\n  retrieves any other assets linked to your asset.\n\n\\begin{verbatim}\n<liferay-asset:asset-links\n    assetEntryId=\"<%= (assetEntry != null) ? assetEntry.getEntryId() : 0 %>\"\n    className=\"<%= [myAssetEntry].class.getName() %>\"\n    classPK=\"<%= entry.getEntryId() %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! Now you have the JSP that lets your users view related assets.\nRelated assets, if you've created any yet, should be visible near the\nbottom of the page.\n\nExcellent! Now you know how to implement related assets in your apps.\n\n\\chapter{Implementing Asset\nPriority}\\label{implementing-asset-priority-1}\n\nThis asset priority field isn't enabled when you create an asset. You\nmust manually add support for it. You'll learn how below.\n\n\\section{Add the Priority Field to Your\nJSP}\\label{add-the-priority-field-to-your-jsp}\n\nIn the JSP for adding and editing your asset, add the following input\nfield that lets users set the asset's priority. This example also\nvalidates the input to make sure the value the user sets is a number\nhigher than zero:\n\n\\begin{verbatim}\n<aui:input label=\"priority\" name=\"assetPriority\" type=\"text\" value=\"<%= priority %>\">\n    <aui:validator name=\"number\" />\n\n    <aui:validator name=\"min\">[0]</aui:validator>\n</aui:input>\n\\end{verbatim}\n\nThat's it for the view layer! Now when users create or edit your asset,\nthey can enter its priority. Next, you'll learn how to use that value in\nyour service layer.\n\n\\section{Using the Priority Value in Your Service\nLayer}\\label{using-the-priority-value-in-your-service-layer}\n\nTo make the priority value functional, you must retrieve it from the\nview and add it to the asset in your database. The priority value is\nautomatically available in your service layer via the\n\\texttt{ServiceContext} variable \\texttt{serviceContext}. Retrieve it\nwith \\texttt{serviceContext.getAssetPriority()}, and then pass it as the\nlast argument to the \\texttt{assetEntryLocalService.updateEntry} call in\nyour \\texttt{-LocalServiceImpl}. You can see an example of this in\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/service/impl/BlogsEntryLocalServiceImpl.java}{the\n\\texttt{BlogsEntryLocalServiceImpl} class} of Liferay DXP's Blogs app.\nThe \\texttt{updateAsset} method takes a \\texttt{priority} argument,\nwhich it passes as the last argument to its\n\\texttt{assetEntryLocalService.updateEntry} call:\n\n\\begin{verbatim}\n@Override\npublic void updateAsset(\n        long userId, BlogsEntry entry, long[] assetCategoryIds,\n        String[] assetTagNames, long[] assetLinkEntryIds, Double priority)\n    throws PortalException {\n\n    ...\n\n    AssetEntry assetEntry = assetEntryLocalService.updateEntry(\n        userId, entry.getGroupId(), entry.getCreateDate(),\n        entry.getModifiedDate(), BlogsEntry.class.getName(),\n        entry.getEntryId(), entry.getUuid(), 0, assetCategoryIds,\n        assetTagNames, true, visible, null, null, null, null,\n        ContentTypes.TEXT_HTML, entry.getTitle(), entry.getDescription(),\n        summary, null, null, 0, 0, priority);\n\n    ...\n}\n\\end{verbatim}\n\nThe \\texttt{BlogsEntryLocalServiceImpl} class calls this\n\\texttt{updateAsset} method when adding or updating a blog entry. Note\nthat \\texttt{serviceContext.getAssetPriority()} retrieves the priority:\n\n\\begin{verbatim}\nupdateAsset(\n        userId, entry, serviceContext.getAssetCategoryIds(),\n        serviceContext.getAssetTagNames(),\n        serviceContext.getAssetLinkEntryIds(),\n        serviceContext.getAssetPriority());\n\\end{verbatim}\n\nSweet! Now you know how to enable priorities for your app's assets.\n\n\\chapter{Back-end Frameworks}\\label{back-end-frameworks}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay's powerful back-end frameworks provide essential services behind\nthe scenes. Here are some of the frameworks:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[portlet-providers]{Portlet Providers}\n\\item\n  \\hyperref[data-scopes]{Data Scopes}\n\\item\n  \\hyperref[message-bus]{Message Bus}\n\\end{itemize}\n\nYou can use these frameworks to provide important functionality to your\napplications.\n\n\\section{Portlet Providers}\\label{portlet-providers}\n\nSome apps perform the same operations on different entity types. For\nexample, the Asset Publisher lets users browse, add, preview, and view\nvarious entities as assets including documents, web content, blogs, and\nmore. The entities vary, but the operations and surrounding business\nlogic stay the same. Apps like the Asset Publisher rely on the Portlet\nProviders framework to fetch portlets to operate on the entities. In\nthis way, the framework lets you focus on entity operations and frees\nyou from concern about portlets that carry out those operations.\n\n\\section{Portlet Provider Classes}\\label{portlet-provider-classes}\n\nPortlet Provider classes are components that implement the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProvider.html}{\\texttt{PortletProvider}}\ninterface, and are associated with an entity type. Once you've\nregistered a Portlet Provider, you can invoke the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProviderUtil.html}{\\texttt{PortletProviderUtil}}\nclass to retrieve the portlet ID or portlet URL from that Portlet\nProvider.\n\nAs an example, examine the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/wiki/wiki-web/src/main/java/com/liferay/wiki/web/internal/portlet/WikiEditPortletProvider.java}{\\texttt{WikiEditPortletProvider}}\nclass:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"model.class.name=com.liferay.wiki.model.WikiPage\",\n        \"service.ranking:Integer=100\"\n    },\n    service = EditPortletProvider.class\n)\npublic class WikiEditPortletProvider\n    extends BasePortletProvider implements EditPortletProvider {\n\n    @Override\n    public String getPortletName() {\n        return WikiPortletKeys.WIKI;\n    }\n\n}\n\\end{verbatim}\n\n\\texttt{WikiEditPortletProvider} extends\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BasePortletProvider.html}{\\texttt{BasePortletProvider}},\ninheriting its \\texttt{getPortletURL} methods.\n\\texttt{WikiEditPortletProvider} must, however, implement\n\\texttt{PortletProvider}'s \\texttt{getPortletName} method, which returns\nthe portlet's name \\texttt{WikiPortletKeys.WIKI}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you're creating a Portlet Provider for one of\nLiferay's portlets, your \\texttt{getPortletName} method should return\nthe portlet name from that portlet's \\texttt{*PortletKeys} class, if\nsuch a class exists.\n\n\\noindent\\hrulefill\n\nThe \\texttt{@Component} annotation for \\texttt{WikiEditPortletProvider}\nspecifies these elements and properties:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{immediate\\ =\\ true} activates the component immediately upon\n  installation.\n\\item\n  \\texttt{\"model.class.name=com.liferay.wiki.model.WikiPage\"} specifies\n  the entity type the portlet operates on.\n\\item\n  \\texttt{\"service.ranking:Integer=100\"} sets the component's rank to\n  \\texttt{100}, prioritizing it above all Portlet Providers that specify\n  the same \\texttt{model.class.name} value but have a lower rank.\n\\item\n  \\texttt{service\\ =\\ EditPortletProvider.class} reflects the\n  subinterface \\texttt{PortletProvider} class this class implements\n  (\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/EditPortletProvider.html}{\\texttt{EditPortletProvider}}).\n\\end{itemize}\n\nFor step-by-step instructions on creating a Portlet Provider class, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-portlet-providers}{Creating\nPortlet Providers}. For instructions on using Portlet Providers to\nretrieve a portlet, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/retrieving-portlets}{Retrieving\nPortlets}.\n\n\\section{Data Scopes}\\label{data-scopes}\n\nApps can restrict their data to specific \\emph{scopes}. Scopes provide a\ncontext for the application's data.\n\n\\textbf{Global:} One data set throughout a portal instance.\n\n\\textbf{Site:} One data set for each Site.\n\n\\textbf{Page:} One data set for each Page on a Site.\n\nFor example, a Site-scoped app has one set of data on one Site and a\ncompletely different set of data for another Site. For a detailed\nexplanation of scopes, see the user guide article\n\\href{/docs/7-2/user/-/knowledge_base/u/widget-scope}{Widget Scope}. To\ngive your applications scope, you must manually add support for it. For\ninstructions on this, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/enabling-and-accessing-data-scopes}{Enabling\nand Accessing Data Scopes}.\n\n\\section{Accessing the Site Scope Across\nApps}\\label{accessing-the-site-scope-across-apps}\n\nThere may be times when you must access a different app's Site-scoped\ndata from your app that is scoped to a page or the portal. For example,\nweb content articles can be created in the page, Site, or portal scope.\n\\href{/docs/7-2/user/-/knowledge_base/u/designing-uniform-content}{Structures\nand Templates} for such articles, however, exist only in the Site scope.\nOther techniques return your app's scope, which might not be the Site\nscope. What a pickle! Never fear, the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/theme/ThemeDisplay.html}{\\texttt{ThemeDisplay}}\nmethod \\texttt{getSiteGroupId()} is here! This method always gets the\nSite scope, no matter your app's current scope. For an example of using\nthis method, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/enabling-and-accessing-data-scopes}{Enabling\nand Accessing Data Scopes}.\n\n\\section{Message Bus}\\label{message-bus}\n\nIf you must ever do data processing outside the scope of the web's\nrequest/response, look no further than the Message Bus. It's\nconceptually similar to Java Messaging Service (JMS) Topics, but\nsacrifices transactional, reliable delivery capabilities, making it much\nlighter-weight. Liferay DXP uses Message Bus in many places:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Auditing\n\\item\n  Search engine integration\n\\item\n  Email subscriptions\n\\item\n  Monitoring\n\\item\n  Document Library processing\n\\item\n  Background tasks\n\\item\n  Cluster-wide request execution\n\\item\n  Clustered cache replication\n\\end{itemize}\n\nYou can use it too! Here are some of Message Bus's most important\nfeatures:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  publish/subscribe messaging\n\\item\n  request queuing and throttling\n\\item\n  flow control\n\\item\n  multi-thread message processing\n\\end{itemize}\n\nThere are also tools, such as the Java SE's JConsole, that can monitor\nMessage Bus activities.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/message-bus-jconsole.png}\n\\caption{JConsole shows statistics on Message Bus messages sent,\nmessages pending, and more.}\n\\end{figure}\n\n\\chapter{Creating Portlet Providers}\\label{creating-portlet-providers}\n\nFollow these steps to create your own\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks\\#portlet-providers}{Portlet\nProvider}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  an OSGi module}.\n\\item\n  Create a \\texttt{PortletProvider} class in your module. Use the\n  recommended class naming convention:\n\n  \\texttt{{[}Entity{]}\\ +\\ {[}Action{]}\\ +\\ PortletProvider}\n\n  For example, here's a Portlet Provider class for viewing a\n  \\texttt{LanguageEntry}:\n\n  \\texttt{LanguageEntryViewPortletProvider}\n\\item\n  Extend\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BasePortletProvider.html}{\\texttt{BasePortletProvider}}\n  if you want to use its \\texttt{getPortletURL} method implementations.\n\\item\n  Implement one or more\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProvider.html}{\\texttt{PortletProvider}}\n  subinterfaces that match your action(s):\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/AddPortletProvider.html}{\\texttt{AddPortletProvider}}\n  \\item\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BrowsePortletProvider.html}{\\texttt{BrowsePortletProvider}}\n  \\item\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/EditPortletProvider.html}{\\texttt{EditPortletProvider}}\n  \\item\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/ManagePortletProvider.html}{\\texttt{ManagePortletProvider}}\n  \\item\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PreviewPortletProvider.html}{\\texttt{PreviewPortletProvider}}\n  \\item\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/ViewPortletProvider.html}{\\texttt{ViewPortletProvider}}\n  \\end{itemize}\n\\item\n  Make the class an OSGi Component by adding an annotation like this\n  one:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"model.class.name=CLASS_NAME\",\n        \"service.ranking:Integer=10\"\n    },\n    service = {INTERFACE_1.class, ...}\n)\n\\end{verbatim}\n\n  The \\texttt{immediate\\ =\\ true} element specifies that the component\n  should be activated immediately upon installation.\n\n  Assign to the \\texttt{model.class.name} property the fully qualified\n  class name of the entity the portlet operates on. Here's an example\n  \\texttt{model.class.name} property for the \\texttt{WikiPage} entity:\n\n\\begin{verbatim}\n\"model.class.name=com.liferay.wiki.model.WikiPage\"\n\\end{verbatim}\n\n  Assign the \\texttt{service} element to the \\texttt{PortletProvider}\n  subinterface(s) you're implementing (e.g.,\n  \\texttt{ViewPortletProvider.class},\n  \\texttt{BrowsePortletProvider.class}, etc.). This example sets\n  \\texttt{service} to \\texttt{EditPortletProvider.class}:\n\n\\begin{verbatim}\nservice = EditPortletProvider.class\n\\end{verbatim}\n\\item\n  If you're overriding an existing Portlet Provider, rank your Portlet\n  Provider higher by specifying a \\texttt{service.ranking:Integer}\n  property with a higher integer value:\n\n\\begin{verbatim}\nproperty = {\n    ...,\n    \"service.ranking:Integer=10\"\n}\n\\end{verbatim}\n\\item\n  Implement the provider methods you want. Be sure to implement the\n  \\texttt{PortletProvider} method \\texttt{getPortletName}. If you didn't\n  extend \\texttt{BasePortletProvider}, implement\n  \\texttt{PortletProvider}'s \\texttt{getPortletURL} methods too.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  your module}.\n\\end{enumerate}\n\nNow your Portlet Provider is available to return the ID and URL of the\nportlet that provides the desired behaviors. For more information on\nthis, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/retrieving-portlets}{Retrieving\nPortlets}.\n\n\\section{Related Topics}\\label{related-topics-4}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks\\#portlet-providers}{Portlet\nProviders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/retrieving-portlets}{Retrieving\nPortlets}\n\n\\chapter{Retrieving Portlets}\\label{retrieving-portlets}\n\nWhen a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks\\#portlet-providers}{Portlet\nProvider} exists for an entity, you can use the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProviderUtil.html}{\\texttt{PortletProviderUtil}}\nclass to retrieve the ID or URL of the portlet that performs the entity\naction you want.\n\nThe Portlet Provider framework's\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProvider.Action.html}{\\texttt{PortletProvider.Action}}\nEnum defines these action types:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{ADD}\n\\item\n  \\texttt{BROWSE}\n\\item\n  \\texttt{EDIT}\n\\item\n  \\texttt{MANAGE}\n\\item\n  \\texttt{PREVIEW}\n\\item\n  \\texttt{VIEW}\n\\end{itemize}\n\nThe action type and entity type are key parameters in fetching a\nportlet's ID or URL.\n\n\\section{Fetching a Portlet ID}\\label{fetching-a-portlet-id}\n\nTo get the ID of the portlet that performs an action on an entity, pass\nthat entity and action as arguments to the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProviderUtil.html}{\\texttt{PortletProviderUtil}}\nmethod \\texttt{getPortletId}. For example, this call gets the ID of a\nportlet for viewing Recycle Bin entries:\n\n\\begin{verbatim}\nString portletId = PortletProviderUtil.getPortletId(\n    \"com.liferay.portlet.trash.model.TrashEntry\", \n    PortletProvider.Action.VIEW);\n\\end{verbatim}\n\nThe \\texttt{com.liferay.portlet.trash.model.TrashEntry} entity specifies\nRecycle Bin entries, and \\texttt{PortletProvider.Action.VIEW} specifies\nthe view action.\n\nHow and where you use the portlet ID depends on your needs---there's no\ntypical use case or set of steps to follow. One example is how the Asset\nPublisher uses the Portlet Provider framework to add a previewed asset\nto a page; it adds the asset to a portlet and adds that portlet to the\npage. The Asset Publisher uses the \\texttt{liferay-asset:asset\\_display}\ntag library tag whose \\texttt{asset\\_display/preview.jsp} shows an\n\\emph{Add} button for adding the portlet. If the previewed asset is a\nBlogs entry, for example, the framework returns a blogs portlet ID or\nURL for adding the portlet to the current page. Here's the relevant code\nfrom the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/asset/asset-taglib/src/main/resources/META-INF/resources/asset_display/preview.jsp\\#L75-L91}{\\texttt{asset\\_display/preview.jsp}}:\n\n\\begin{verbatim}\n<%\nMap<String, Object> data = new HashMap<String, Object>();\n\n<!-- populate the data map -->\n\nString portletId = PortletProviderUtil.getPortletId(assetEntry.getClassName(), PortletProvider.Action.ADD);\n\ndata.put(\"portlet-id\", portletId);\n\n<!-- add more to the data map -->\n%>\n\n<c:if test=\"<%= PortletPermissionUtil.contains(permissionChecker, layout, portletId, ActionKeys.ADD_TO_PAGE) %>\">\n    <aui:button cssClass=\"add-button-preview\" data=\"<%= data %>\" value=\"add\" />\n</c:if>\n\\end{verbatim}\n\nThis code invokes\n\\texttt{PortletProviderUtil.getPortletId(assetEntry.getClassName(),\\ PortletProvider.Action.ADD)}\nto get the ID of a portlet that adds and displays the asset of the\nunderlying entity class.\n\nThe JSP puts the portlet ID into the \\texttt{data} map:\n\n\\begin{verbatim}\ndata.put(\"portlet-id\", portletId);\n\\end{verbatim}\n\nThen it passes the \\texttt{data} map to a new \\emph{Add} button that\nadds the portlet to the page:\n\n\\begin{verbatim}\n<aui:button cssClass=\"add-button-preview\" data=\"<%= data %>\" value=\"add\" />\n\\end{verbatim}\n\n\\section{Fetching a Portlet URL}\\label{fetching-a-portlet-url}\n\nTo get the URL of the portlet that performs an action on an entity, call\none of\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProviderUtil.html}{\\texttt{PortletProviderUtil}'s}\n\\texttt{getPortletURL} methods. These methods return a\n\\texttt{javax.portlet.PortletURL} based on an\n\\texttt{HttpServletRequest} or \\texttt{PortletRequest}. You can also\nspecify a \\texttt{Group}, the entity's class name, and the action.\n\nHow you call these methods depends on your use case---there's no typical\nset of steps to follow. As an example, when the Asset Publisher is\nconfigured in Manual mode, the user can use an Asset Browser to select\nasset entries. The \\texttt{asset-publisher-web} module's\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/asset/asset-publisher-web/src/main/resources/META-INF/resources/configuration/asset_entries.jsp\\#L123}{\\texttt{configuration/asset\\_entries.jsp}}\nfile uses \\texttt{PortletProviderUtil}'s \\texttt{getPortletURL} method\n(at the end of the code below) to generate a corresponding Asset Browser\nURL:\n\n\\begin{verbatim}\nList<AssetRendererFactory<?>> assetRendererFactories = \n    ListUtil.sort(\n        AssetRendererFactoryRegistryUtil.getAssetRendererFactories(\n            company.getCompanyId()),\n            new AssetRendererFactoryTypeNameComparator(locale));\n\nfor (AssetRendererFactory<?> curRendererFactory : assetRendererFactories) {\n        long curGroupId = groupId;\n\n        if (!curRendererFactory.isSelectable()) {\n            continue;\n        }\n\n        PortletURL assetBrowserURL = PortletProviderUtil.getPortletURL(\n            request, curRendererFactory.getClassName(),\n            PortletProvider.Action.BROWSE);\n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-5}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks\\#portlet-providers}{Portlet\nProviders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-portlet-providers}{Creating\nPortlet Providers}\n\n\\chapter{Enabling and Accessing Data\nScopes}\\label{enabling-and-accessing-data-scopes}\n\nApps can restrict their data to specific scopes (e.g., Global, Site,\nPage). Here, you'll learn how to\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[enabling-scoping]{Enable Scoping}\n\\item\n  \\hyperref[accessing-your-apps-scope]{Access Your App's Scope}\n\\item\n  \\hyperref[accessing-the-site-scope]{Access the Site Scope}\n\\end{itemize}\n\nFor more detailed information about scoping, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks\\#data-scopes}{Data\nScopes}.\n\n\\section{Enabling Scoping}\\label{enabling-scoping}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Scope your app's entities. In your service layer, your entities must\n  have a \\texttt{companyId} attribute of type \\texttt{long} to enable\n  scoping by portal instance, and a \\texttt{groupId} attribute of type\n  \\texttt{long} to enable scoping by Site. Using\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\n  Builder} is the simplest way to do this. For instructions on this, see\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/creating-a-service-builder-project}{Service\n  Builder Persistence} and\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{Business\n  Logic with Service Builder}.\n\\item\n  To enable scoping in your app, set the property\n  \\texttt{\"com.liferay.portlet.scopeable=true\"} in your portlet class's\n  \\texttt{@Component} annotation. For example, the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/journal/journal-content-web/src/main/java/com/liferay/journal/content/web/internal/portlet/JournalContentPortlet.java}{Web\n  Content Display Portlet's portlet class} sets this component property:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        ...\n        \"com.liferay.portlet.scopeable=true\",\n        ...,\n    },\n    service = Portlet.class\n)\npublic class JournalContentPortlet extends MVCPortlet {\n    ...\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Accessing Your App's Scope}\\label{accessing-your-apps-scope}\n\nUsers can typically set an app's scope to a page, a Site, or the entire\nportal. To handle your app's data, you must access it in its current\nscope. Your app's scope is available in these ways:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Via the \\texttt{scopeGroupId} variable injected in JSPs that use the\n  \\texttt{\\textless{}liferay-theme:defineObjects\\ /\\textgreater{}} tag.\n  This variable contains your app's current scope. For example, the\n  Liferay Bookmarks app's\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/bookmarks/bookmarks-web/src/main/resources/META-INF/resources/bookmarks/view.jsp\\#L122-L125}{\\texttt{view.jsp}}\n  uses its \\texttt{scopeGroupId} to retrieve the bookmarks and total\n  number of bookmarks in the current scope:\n\n\\begin{verbatim}\n...\ntotal = BookmarksEntryServiceUtil.getGroupEntriesCount(scopeGroupId, groupEntriesUserId);\n\nbookmarksSearchContainer.setTotal(total);\nbookmarksSearchContainer.setResults(BookmarksEntryServiceUtil.getGroupEntries(scopeGroupId, groupEntriesUserId, bookmarksSearchContainer.getStart(), bookmarksSearchContainer.getEnd()));\n...\n\\end{verbatim}\n\\item\n  By calling the \\texttt{getScopeGroupId()} method on the request's\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/theme/ThemeDisplay.html}{\\texttt{ThemeDisplay}}.\n  This method returns your app's current scope. For example, the Liferay\n  Blogs app's\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-web/src/main/java/com/liferay/blogs/web/internal/portlet/action/EditEntryMVCActionCommand.java\\#L350-L362}{\\texttt{EditEntryMVCActionCommand}}\n  class does this in its \\texttt{subscribe} and \\texttt{unsubscribe}\n  methods:\n\n\\begin{verbatim}\nprotected void subscribe(ActionRequest actionRequest) throws Exception {\n    ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(\n        WebKeys.THEME_DISPLAY);\n\n    _blogsEntryService.subscribe(themeDisplay.getScopeGroupId());\n}\n\nprotected void unsubscribe(ActionRequest actionRequest) throws Exception {\n    ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(\n        WebKeys.THEME_DISPLAY);\n\n    _blogsEntryService.unsubscribe(themeDisplay.getScopeGroupId());\n}\n\\end{verbatim}\n\n  If you know your app always needs the portal instance ID, use\n  \\texttt{themeDisplay.getCompanyId()}.\n\\item\n  By calling the \\texttt{getScopeGroupId()} method on a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/service/ServiceContext.html}{\\texttt{ServiceContext}}\n  object. See\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/understanding-servicecontext}{Understanding\n  Service Context} for an example and more details. If you know your app\n  always needs the portal instance ID, use the \\texttt{ServiceContext}\n  object's \\texttt{getCompanyId()} method.\n\\end{enumerate}\n\n\\section{Accessing the Site Scope}\\label{accessing-the-site-scope}\n\nTo access the Site scope regardless of your app's current scope, use the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/theme/ThemeDisplay.html}{\\texttt{ThemeDisplay}}\nmethod \\texttt{getSiteGroupId()}. For more information on this use case,\nsee\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks\\#accessing-the-site-scope-across-apps}{Accessing\nthe Site Scope Across Apps}.\n\nFor example, the Web Content app's\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/journal/journal-web/src/main/resources/META-INF/resources/edit_feed.jsp\\#L40}{\\texttt{edit\\_feed.jsp}}\nuses the \\texttt{getSiteGroupId()} method to get the Site ID, which is\nrequired to retrieve Structures:\n\n\\begin{verbatim}\nddmStructure = DDMStructureLocalServiceUtil.fetchStructure(themeDisplay.getSiteGroupId(), \n    PortalUtil.getClassNameId(JournalArticle.class), ddmStructureKey, true);\n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-6}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks\\#data-scopes}{Data\nScopes}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/creating-a-service-builder-project}{Service\nBuilder Project}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{Business\nLogic with Service Builder}\n\n\\chapter{Using the Message Bus}\\label{using-the-message-bus}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nHere, you'll learn how to use the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks\\#message-bus}{Message\nBus} to send and receive messages in the portal. The following topics\nare covered:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[messaging-destinations]{Messaging Destinations}\n\\item\n  \\hyperref[message-listeners]{Message Listeners}\n\\item\n  \\hyperref[sending-messages]{Sending Messages}\n\\end{itemize}\n\n\\section{Messaging Destinations}\\label{messaging-destinations}\n\nIn Message Bus, you send messages to destinations. A destination is a\nnamed logical (not physical) location. Sender classes send messages to\ndestinations, while listener classes wait to receive messages at the\ndestinations. In this way, the sender and recipient don't need to know\neach other---they're loosely coupled.\n\n\\section{Destination Configuration}\\label{destination-configuration}\n\nEach destination has a name and type and can have several other\nattributes. The destination type determines these things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Whether there's a message queue.\n\\item\n  The kinds of threads involved with a destination.\n\\item\n  The message delivery behavior to expect at the destination.\n\\end{itemize}\n\nHere are the primary destination types:\n\n\\textbf{Parallel Destination}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Messages sent here are queued.\n\\item\n  Multiple worker threads from a thread pool deliver each message to a\n  registered message listener. There's one worker thread per message per\n  message listener.\n\\end{itemize}\n\n\\textbf{Serial Destination}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Messages sent here are queued.\n\\item\n  Worker threads from a thread pool deliver the messages to each\n  registered message listener, one worker thread per message.\n\\end{itemize}\n\n\\textbf{Synchronous Destination}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Messages sent here are directly delivered to message listeners.\n\\item\n  The thread sending the message here also delivers the message to all\n  message listeners.\n\\end{itemize}\n\nPreconfigured destinations exist for various purposes. The\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationNames.html}{\\texttt{DestinationNames}}\nclass defines \\texttt{String} constants for each. For example,\n\\texttt{DestinationNames.HOT\\_DEPLOY} (value is\n\\texttt{\"liferay/hot\\_deploy\"}) is for deployment event messages. Since\ndestinations are tuned for specific purposes, don't modify them.\n\nDestinations are based on\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationConfiguration.html}{\\texttt{DestinationConfiguration}}\ninstances. The configuration specifies the destination type, name, and\nthese destination-related attributes:\n\n\\textbf{Maximum Queue Size}: Limits the number of the destination's\nqueued messages.\n\n\\textbf{Rejected Execution Handler}: A\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/concurrent/RejectedExecutionHandler.html}{\\texttt{RejectedExecutionHandler}}\ninstance can take action (e.g., log warnings) regarding rejected\nmessages when the destination queue is full.\n\n\\textbf{Workers Core Size}: Initial number of worker threads for\nprocessing messages.\n\n\\textbf{Workers Max Size}: Limits the number of worker threads for\nprocessing messages.\n\nThe \\texttt{DestinationConfiguration} class provides these static\nmethods for creating the various types of configurations.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{createParallelDestinationConfiguration(String\\ destinationName)}\n\\item\n  \\texttt{createSerialDestinationConfiguration(String\\ destinationName)}\n\\item\n  \\texttt{createSynchronousDestinationConfiguration(String\\ destinationName)}\n\\end{itemize}\n\nYou can also use the \\texttt{DestinationConfiguration}\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationConfiguration.html\\#DestinationConfiguration-java.lang.String-java.lang.String-}{constructor}\nto create a configuration for any destination type, even your own.\n\nFor instructions on creating your own destination, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-destination}{Creating\na Destination}.\n\n\\section{Message Listeners}\\label{message-listeners}\n\nIf you're interested in messages sent to a destination, you need to\n\\emph{listen} for them. That is, you must create and register a message\nlistener for the destination.\n\nTo create a message listener, implement the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageListener.html}{\\texttt{MessageListener}}\ninterface and override its \\texttt{receive(Message)} method to process\nmessages your way.\n\n\\begin{verbatim}\npublic void receive(Message message) {\n    // Process messages your way\n}\n\\end{verbatim}\n\nHere are the ways to register your listener with Message Bus:\n\n\\textbf{Automatic Registration as a Component}: Publish the listener to\nthe OSGi registry as a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{Declarative\nServices} component that specifies a destination. Message Bus\nautomatically wires the listener to the destination.\n\n\\textbf{Registering via MessageBus}: Obtain and use a\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageBus.html}{\\texttt{MessageBus}}\nreference to directly register the listener to a destination.\n\n\\textbf{Registering Directly to a Destination}: Obtain a reference to a\nspecific destination and use it to directly register the listener with\nthat destination.\n\nFor instructions on these topics, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/registering-message-listeners}{Registering\nMessage Listeners}.\n\n\\section{Sending Messages}\\label{sending-messages}\n\nMessage Bus lets you send messages to destinations that have any number\nof listening classes. As a message sender you don't need to know the\nmessage recipients. Instead, you focus on creating message content\n(payload) and sending messages to destinations.\n\nYou can also send messages in a synchronous or asynchronous manner. The\nsynchronous option waits for a response that the message was received or\nthat it timed out. The asynchronous option gives you the ``fire and\nforget'' behavior; send the message and continue processing without\nwaiting for a response.\n\nSee these topics for instructions on creating and sending messages:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-message}{Creating\n  a Message}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/sending-a-message}{Sending\n  a Message}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/sending-messages-across-a-cluster}{Sending\n  Messages Across a Cluster}\n\\end{itemize}\n\n\\chapter{Creating a Destination}\\label{creating-a-destination}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus\\#messaging-destinations}{Message\nBus destinations} are based on destination configurations and registered\nas OSGi services. Message Bus detects the destination services and\nmanages their associated destinations.\n\nHere are the steps for creating a destination. The example configurator\nclass that follows demonstrates these steps.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create an \\texttt{activate(BundleContext)} method in your component.\n  Then create a\n  \\href{https://osgi.org/javadoc/r4v43/core/org/osgi/framework/BundleContext.html}{\\texttt{BundleContext}}\n  instance variable and set it to the \\texttt{activate} method's\n  \\texttt{BundleContext}:\n\n\\begin{verbatim}\n@Activate\nprotected void activate(BundleContext bundleContext) {\n\n    _bundleContext = bundleContext;\n\n}\n\nprivate final BundleContext _bundleContext;\n\\end{verbatim}\n\n  You'll create and register your destination inside this\n  \\texttt{activate} method. This ensures that the destination is\n  available upon service activation. Once the destination is registered,\n  Message Bus detects its service and manages the destination.\n\\item\n  Create a destination configuration by using one of\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationConfiguration.html}{\\texttt{DestinationConfiguration}'s}\n  static \\texttt{create*} methods or its constructor. Set any attributes\n  that apply to the destinations you'll create with the destination\n  configuration.\n\n  For example, this code uses the \\texttt{DestinationConfiguration}\n  constructor to create a destination configuration for parallel\n  destinations. It then sets the destination configuration's maximum\n  queue size and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/concurrent/RejectedExecutionHandler.html}{\\texttt{RejectedExecutionHandler}}:\n\n\\begin{verbatim}\n@Activate\nprotected void activate(BundleContext bundleContext) {\n    ...\n\n    // Create a DestinationConfiguration for parallel destinations.\n\n    DestinationConfiguration destinationConfiguration =\n        new DestinationConfiguration(\n            DestinationConfiguration.DESTINATION_TYPE_PARALLEL,\n                \"myDestinationName\");\n\n    // Set the DestinationConfiguration's max queue size and\n    // rejected execution handler.\n\n    destinationConfiguration.setMaximumQueueSize(_MAXIMUM_QUEUE_SIZE);\n\n    RejectedExecutionHandler rejectedExecutionHandler =\n        new CallerRunsPolicy() {\n\n            @Override\n            public void rejectedExecution(\n                Runnable runnable, ThreadPoolExecutor threadPoolExecutor) {\n\n                if (_log.isWarnEnabled()) {\n                    _log.warn(\n                        \"The current thread will handle the request \" +\n                            \"because the graph walker's task queue is at \" +\n                                \"its maximum capacity\");\n                }\n\n                super.rejectedExecution(runnable, threadPoolExecutor);\n            }\n\n    };\n\n    destinationConfiguration.setRejectedExecutionHandler(\n        rejectedExecutionHandler);\n\n}\n\\end{verbatim}\n\\item\n  Create the destination by invoking the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationFactory.html}{\\texttt{DestinationFactory}}\n  method \\texttt{createDestination(DestinationConfiguration)}, passing\n  in the destination configuration from the previous step.\n\n  For example, this code does so via a \\texttt{DestinationFactory}\n  reference:\n\n\\begin{verbatim}\n@Activate\nprotected void activate(BundleContext bundleContext) {\n    ...\n\n    Destination destination = _destinationFactory.createDestination(\n        destinationConfiguration);\n\n}\n...\n\n@Reference\nprivate DestinationFactory _destinationFactory;\n\\end{verbatim}\n\\item\n  Register the destination as an OSGi service by invoking the\n  \\texttt{BundleContext} method \\texttt{registerService} with these\n  parameters:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    The destination class \\texttt{Destination.class}.\n  \\item\n    Your \\texttt{Destination} object.\n  \\item\n    A \\texttt{Dictionary} of properties defining the destination,\n    including the \\texttt{destination.name}.\n  \\end{itemize}\n\n\\begin{verbatim}\n@Activate\nprotected void activate(BundleContext bundleContext) {\n    ...\n\n    Dictionary<String, Object> properties = new HashMapDictionary<>();\n\n    properties.put(\"destination.name\", destination.getName());\n\n    ServiceRegistration<Destination> serviceRegistration =\n        _bundleContext.registerService(\n            Destination.class, destination, properties);\n}\n\\end{verbatim}\n\\item\n  Manage the destination object and service registration resources using\n  a collection such as a\n  \\texttt{Map\\textless{}String,\\ ServiceRegistration\\textless{}Destination\\textgreater{}\\textgreater{}}.\n  Keeping references to these resources is helpful for when you're ready\n  to unregister and destroy them.\n\n\\begin{verbatim}\n@Activate\nprotected void activate(BundleContext bundleContext) {\n    ...\n\n    _serviceRegistrations.put(destination.getName(), \n        serviceRegistration);\n\n}\n...\n\nprivate final Map<String, ServiceRegistration<Destination>>\n    _serviceRegistrations = new HashMap<>();\n\\end{verbatim}\n\\item\n  Add a \\texttt{deactivate} method that unregisters and destroys any\n  destinations for this component. This ensures there aren't any active\n  destinations for this component when the service deactivates:\n\n\\begin{verbatim}\n@Deactivate\nprotected void deactivate() {\n\n    // Unregister and destroy destinations\n\n    for (ServiceRegistration<Destination> serviceRegistration : \n        _serviceRegistrations.values()) {\n\n        Destination destination = _bundleContext.getService(\n            serviceRegistration.getReference());\n\n        serviceRegistration.unregister();\n\n        destination.destroy();\n\n    }\n\n    _serviceRegistrations.clear();\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\nHere's the full messaging configurator component class that contains the\ncode in the above steps:\n\n\\begin{verbatim}\n@Component (\n    immediate = true,\n    service = MyMessagingConfigurator.class\n)\npublic class MyMessagingConfigurator {\n\n    @Activate\n    protected void activate(BundleContext bundleContext) {\n\n        _bundleContext = bundleContext;\n\n        // Create a DestinationConfiguration for parallel destinations.\n\n        DestinationConfiguration destinationConfiguration =\n            new DestinationConfiguration(\n                DestinationConfiguration.DESTINATION_TYPE_PARALLEL,\n                    \"myDestinationName\");\n\n        // Set the DestinationConfiguration's max queue size and\n        // rejected execution handler.\n\n        destinationConfiguration.setMaximumQueueSize(_MAXIMUM_QUEUE_SIZE);\n\n        RejectedExecutionHandler rejectedExecutionHandler =\n            new CallerRunsPolicy() {\n\n                @Override\n                public void rejectedExecution(\n                    Runnable runnable, ThreadPoolExecutor threadPoolExecutor) {\n\n                    if (_log.isWarnEnabled()) {\n                        _log.warn(\n                            \"The current thread will handle the request \" +\n                                \"because the graph walker's task queue is at \" +\n                                    \"its maximum capacity\");\n                    }\n\n                    super.rejectedExecution(runnable, threadPoolExecutor);\n                }\n\n            };\n\n        destinationConfiguration.setRejectedExecutionHandler(\n            rejectedExecutionHandler);\n\n        // Create the destination\n\n        Destination destination = _destinationFactory.createDestination(\n            destinationConfiguration);\n\n        // Add the destination to the OSGi service registry\n\n        Dictionary<String, Object> properties = new HashMapDictionary<>();\n\n        properties.put(\"destination.name\", destination.getName());\n\n        ServiceRegistration<Destination> serviceRegistration =\n            _bundleContext.registerService(\n                Destination.class, destination, properties);\n\n        // Track references to the destination service registrations \n\n        _serviceRegistrations.put(destination.getName(),    \n            serviceRegistration);\n    }\n\n    @Deactivate\n    protected void deactivate() {\n\n        // Unregister and destroy destinations this component unregistered\n\n        for (ServiceRegistration<Destination> serviceRegistration : \n            _serviceRegistrations.values()) {\n\n            Destination destination = _bundleContext.getService(\n                serviceRegistration.getReference());\n\n            serviceRegistration.unregister();\n\n            destination.destroy();\n\n        }\n\n        _serviceRegistrations.clear();\n\n    }\n\n    private final BundleContext _bundleContext;\n\n    @Reference\n    private DestinationFactory _destinationFactory;\n\n    private final Map<String, ServiceRegistration<Destination>>\n        _serviceRegistrations = new HashMap<>();\n}\n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-7}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus\\#messaging-destinations}{Message\nBus Destinations}\n\n\\chapter{Message Bus Event Listeners}\\label{message-bus-event-listeners}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nWhen\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus}{using\nMessage Bus}, you may wish to listen for events that take place within\nthe Message Bus framework itself, independent of messages. For example,\nyou can listen for when\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus\\#messaging-destinations}{destinations}\nand\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus\\#message-listeners}{message\nlisteners} are added or removed. Here, you'll learn how.\n\n\\section{Listening for Destinations}\\label{listening-for-destinations}\n\nMessage Bus notifies event listeners when destinations are added and\nremoved. To register these listeners, publish a\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageBusEventListener.html}{\\texttt{MessageBusEventListener}}\ninstance to the OSGi service registry (e.g., via an \\texttt{@Component}\nannotation).\n\nHere's an example implementation of \\texttt{MessageBusEventListener}.\nUse the \\texttt{destinationAdded} and \\texttt{destinationDestroyed}\nmethods to implement any logic that you want to run when a destination\nis added or removed, respectively:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    service = MessageBusEventListener.class\n)\npublic class MyMessageBusEventListener implements MessageBusEventListener {\n\n    void destinationAdded(Destination destination) {\n        ...\n    }\n\n    void destinationDestroyed(Destination destination) {\n        ...\n    }\n}\n\\end{verbatim}\n\n\\section{Listening for Message\nListeners}\\label{listening-for-message-listeners}\n\nMessage Bus notifies\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationEventListener.html}{\\texttt{DestinationEventListener}}\ninstances when message listeners for destinations are either registered\nor unregistered. To register an event listener to a destination, publish\na \\texttt{DestinationEventListener} service to the OSGi service\nregistry, making sure to specify the destination's\n\\texttt{destination.name} property.\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\"destination.name=myCustom/Destination\"},\n    service = DestinationEventListener.class\n)\npublic class MyDestinationEventListener implements DestinationEventListener {\n\n    void messageListenerRegistered(String destinationName,\n                                   MessageListener messageListener) {\n        ...\n    }\n\n    void messageListenerUnregistered(String destinationName,\n                                   MessageListener messageListener) {\n        ...\n    }\n}\n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-8}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus}{Using\nthe Message Bus}\n\n\\chapter{Registering Message\nListeners}\\label{registering-message-listeners}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThere are three ways to register a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus\\#message-listeners}{message\nlistener} with the Message Bus:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  \\hyperref[automatic-registration-as-a-component]{Automatic\n  Registration as a Component}\n\\item\n  \\hyperref[registering-via-a-messagebus-reference]{Registering via a\n  MessageBus Reference}\n\\item\n  \\hyperref[registering-directly-to-the-destination]{Registering\n  Directly to the Destination}\n\\end{enumerate}\n\nAutomatic registration as a component is the preferred way to register\nmessage listeners to destinations. You might want to use the other two\nways if, for example, you want to create some special proxy wrappers.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: The\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationNames.html}{\\texttt{DestinationNames}}\nclass defines \\texttt{String} constants for Liferay DXP's preconfigured\ndestinations.\n\n\\noindent\\hrulefill\n\n\\section{Automatic Registration as a\nComponent}\\label{automatic-registration-as-a-component}\n\nYou can specify a message listener in the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{Declarative\nServices} \\texttt{@Component} annotation:\n\n\\begin{verbatim}\n@Component (\n    immediate = true,\n    property = {\"destination.name=myCustom/Destination\"},\n    service = MessageListener.class\n)\npublic class MyMessageListener implements MessageListener {\n    ...\n\n   public void receive(Message message) {\n       // Handle the message\n   }\n}\n\\end{verbatim}\n\nThe Message Bus listens for\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageListener.html}{\\texttt{MessageListener}}\nservice components like this one to publish themselves to the OSGi\nservice registry. The attribute \\texttt{immediate\\ =\\ true} tells the\nOSGi framework to activate the component as soon as its dependencies\nresolve. Message Bus wires each registered listener to the destination\nits \\texttt{destination.name} property specifies. If the destination is\nnot yet registered, Message Bus queues the listener until the\ndestination registers.\n\n\\section{Registering via a MessageBus\nReference}\\label{registering-via-a-messagebus-reference}\n\nYou can use a\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageBus.html}{\\texttt{MessageBus}}\nreference to directly register message listeners to destinations. Here's\na registrator that demonstrates this:\n\n\\begin{verbatim}\n@Component (\n    immediate = true,\n    service = MyMessageListenerRegistrator.class\n)\npublic class MyMessageListenerRegistrator {\n    ...\n\n    @Activate\n    protected void activate() {\n\n        _messageListener = new MessageListener() {\n\n            public void receive(Message message) {\n                // Handle the message\n            }\n        };\n\n        _messageBus.registerMessageListener(\"myDestinationName\",  \n            _messageListener);\n    }\n\n    @Deactivate\n    protected void deactivate() {\n        _messageBus.unregisterMessageListener(\"myDestinationName\",  \n            _messageListener);\n    }\n\n    @Reference\n    private MessageBus _messageBus;\n\n    private MessageListener _messageListener;\n}\n\\end{verbatim}\n\nThe \\texttt{\\_messageBus} field's \\texttt{@Reference} annotation binds\nit to the \\texttt{MessageBus} instance. The \\texttt{activate} method\ncreates the listener and uses the Message Bus to register the listener\nto a destination named \\texttt{\"myDestination\"}. When this registrator\ncomponent is destroyed, the \\texttt{deactivate} method unregisters the\nlistener.\n\n\\section{Registering Directly to the\nDestination}\\label{registering-directly-to-the-destination}\n\nYou can use a\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/Destination.html}{\\texttt{Destination}}\nreference to register a listener to that destination. Here's a\nregistrator that demonstrates this:\n\n\\begin{verbatim}\n@Component (\n    immediate = true,\n    service = MyMessageListenerRegistrator.class\n)\npublic class MyMessageListenerRegistrator {\n    ...\n\n    @Activate\n    protected void activate() {\n\n        _messageListener = new MessageListener() {\n\n            public void receive(Message message) {\n                // Handle the message\n            }\n        };\n\n        _destination.register(_messageListener);\n    }\n\n    @Deactivate\n    protected void deactivate() {\n\n        _destination.unregister(_messageListener);\n    }\n\n    @Reference(target = \"(destination.name=someDestination)\")\n    private Destination _destination;\n\n    private MessageListener _messageListener;\n}\n\\end{verbatim}\n\nThe \\texttt{\\_destination} field's \\texttt{@Reference} annotation binds\nit to a destination named \\texttt{someDestination}. The\n\\texttt{activate} method creates the listener and registers it to the\ndestination. When this registrator component is destroyed, the\n\\texttt{deactivate} method unregisters the listener.\n\n\\section{Related Topics}\\label{related-topics-9}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus}{Using\nthe Message Bus}\n\n\\chapter{Creating a Message}\\label{creating-a-message}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nBefore you can\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus\\#sending-messages}{send\na message} via the Message Bus, you must first create it. Here's how to\ncreate a message:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Call the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/Message.html}{\\texttt{Message}}\n  constructor to create a new \\texttt{Message}:\n\n\\begin{verbatim}\nMessage message = new Message();\n\\end{verbatim}\n\\item\n  Populate the message with a \\texttt{String} or \\texttt{Object}\n  payload:\n\n  \\begin{itemize}\n  \\item\n    String payload:\n    \\texttt{message.setPayload(\"Message\\ Bus\\ is\\ great!\")}\n  \\item\n    Object payload: \\texttt{message.put(\"firstName\",\\ \"Joe\")}\n  \\end{itemize}\n\\item\n  To receive responses at a particular location, set both of these\n  attributes:\n\n  \\begin{itemize}\n  \\item\n    Response destination name:\n    \\texttt{setResponseDestinationName(String)}\n  \\item\n    Response ID: \\texttt{setResponseId(String)}\n  \\end{itemize}\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-10}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/sending-a-message}{Sending\na Message}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/sending-messages-across-a-cluster}{Sending\nMessages Across a Cluster}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus}{Using\nthe Message Bus}\n\n\\chapter{Sending a Message}\\label{sending-a-message}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nOnce you've\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-message}{created\na message}, there are three ways to send it with the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus}{Message\nBus}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[directly-with-messagebus]{Directly with \\texttt{MessageBus}}\n\\item\n  \\hyperref[asynchronously-with-singledestinationmessagesender]{Asynchronously\n  with \\texttt{SingleDestinationMessageSender}}\n\\item\n  \\hyperref[synchronously-with-synchronousmessagesender]{Synchronously\n  with \\texttt{SynchronousMessageSender}}\n\\end{itemize}\n\n\\section{Directly with MessageBus}\\label{directly-with-messagebus}\n\nTo send a message directly with\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageBus.html}{\\texttt{MessageBus}},\nfollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a \\texttt{MessageBus} reference:\n\n\\begin{verbatim}\n@Reference\nprivate MessageBus _messageBus;\n\\end{verbatim}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-message}{Create\n  a message}. For example:\n\n\\begin{verbatim}\nMessage message = new Message();\nmessage.put(\"myId\", 12345);\nmessage.put(\"someAttribute\", \"abcdef\");\n\\end{verbatim}\n\\item\n  Call the \\texttt{MessageBus} reference's \\texttt{sendMessage} method\n  with the destination and message:\n\n\\begin{verbatim}\n_messageBus.sendMessage(\"myDestinationName\", message);\n\\end{verbatim}\n\\end{enumerate}\n\nHere's a class that contains this example:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    service = SomeServiceImpl.class\n)\npublic class SomeServiceImpl {\n    ...\n\n    public void sendSomeMessage() {\n\n        Message message = new Message();\n        message.put(\"myId\", 12345);\n        message.put(\"someAttribute\", \"abcdef\");\n        _messageBus.sendMessage(\"myDestinationName\", message);\n    }\n\n    @Reference\n    private MessageBus _messageBus;\n}\n\\end{verbatim}\n\n\\section{Asynchronously with\nSingleDestinationMessageSender}\\label{asynchronously-with-singledestinationmessagesender}\n\nThe\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/sender/SingleDestinationMessageSender.html}{\\texttt{SingleDestinationMessageSender}}\ninterface wraps the Message Bus to send messages asynchronously. Follow\nthese steps to use this interface to send asynchronous messages:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/sender/SingleDestinationMessageSenderFactory.html}{\\texttt{SingleDestinationMessageSenderFactory}}\n  reference:\n\n\\begin{verbatim}\n@Reference\nprivate SingleDestinationMessageSenderFactory _messageSenderFactory;\n\\end{verbatim}\n\\item\n  Create a \\texttt{SingleDestinationMessageSender} by calling the\n  \\texttt{SingleDestinationMessageSenderFactory} reference's\n  \\texttt{createSingleDestinationMessageSender} method with the\n  message's destination:\n\n\\begin{verbatim}\nSingleDestinationMessageSender messageSender = \n   _messageSenderFactory.createSingleDestinationMessageSender(\"myDestinationName\");\n\\end{verbatim}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-message}{Create\n  a message}. For example:\n\n\\begin{verbatim}\nMessage message = new Message();\nmessage.put(\"myId\", 12345);\nmessage.put(\"someValue\", \"abcdef\");\n\\end{verbatim}\n\\item\n  Send the message by calling the\n  \\texttt{SingleDestinationMessageSender} instance's \\texttt{send}\n  method with the message:\n\n\\begin{verbatim}\nmessageSender.send(message);\n\\end{verbatim}\n\\end{enumerate}\n\nHere's a class that contains this example:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    service = SomeServiceImpl.class\n)\npublic class SomeServiceImpl {\n    ...\n\n    public void sendSomeMessage() {\n\n        SingleDestinationMessageSender messageSender = \n           _messageSenderFactory.createSingleDestinationMessageSender(\"myDestinationName\");\n\n        Message message = new Message();\n        message.put(\"myId\", 12345);\n        message.put(\"someValue\", \"abcdef\");\n\n        messageSender.send(message);\n    }\n\n    @Reference\n    private SingleDestinationMessageSenderFactory _messageSenderFactory;\n}\n\\end{verbatim}\n\n\\section{Synchronously with\nSynchronousMessageSender}\\label{synchronously-with-synchronousmessagesender}\n\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/sender/SynchronousMessageSender.html}{\\texttt{SynchronousMessageSender}}\nsends a message to the Message Bus and blocks until receiving a response\nor the response times out. A \\texttt{SynchronousMessageSender} has these\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/sender/SynchronousMessageSender.Mode.html}{operating\nmodes}:\n\n\\texttt{DEFAULT}: Delivers the message in a separate thread and also\nprovides timeouts, in case the message is not delivered properly.\n\n\\texttt{DIRECT}: Delivers the message in the same thread of execution\nand blocks until it receives a response.\n\nFollow these steps to send a synchronous message with\n\\texttt{SynchronousMessageSender}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/sender/SingleDestinationMessageSenderFactory.html}{\\texttt{SingleDestinationMessageSenderFactory}}\n  reference:\n\n\\begin{verbatim}\n@Reference\nprivate SingleDestinationMessageSenderFactory _messageSenderFactory;\n\\end{verbatim}\n\\item\n  Create a \\texttt{SingleDestinationSynchronousMessageSender} by calling\n  the \\texttt{SingleDestinationMessageSenderFactory} reference's\n  \\texttt{createSingleDestinationSynchronousMessageSender} method with\n  the destination and operating mode. Note that this example uses the\n  \\texttt{DEFAULT} mode:\n\n\\begin{verbatim}\nSingleDestinationSynchronousMessageSender messageSender = \n    _messageSenderFactory.createSingleDestinationSynchronousMessageSender(\n        \"myDestinationName\", SynchronousMessageSender.Mode.DEFAULT);\n\\end{verbatim}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-message}{Create\n  a message}. For example:\n\n\\begin{verbatim}\nMessage message = new Message();\nmessage.put(\"myId\", 12345);\nmessage.put(\"someValue\", \"abcdef\");\n\\end{verbatim}\n\\item\n  Send the message by calling the\n  \\texttt{SingleDestinationSynchronousMessageSender} instance's\n  \\texttt{send} method with the message:\n\n\\begin{verbatim}\nmessageSender.send(message);\n\\end{verbatim}\n\\end{enumerate}\n\nHere's a class that contains this example:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    service = SomeServiceImpl.class\n)\npublic class SomeServiceImpl {\n    ...\n\n    public void sendSomeMessage() {\n\n        Message message = new Message();\n        message.put(\"myId\", 12345);\n        message.put(\"someAttribute\", \"abcdef\");\n\n        SingleDestinationSynchronousMessageSender messageSender = \n            _messageSenderFactory.createSingleDestinationSynchronousMessageSender(\n                \"myDestinationName\", SynchronousMessageSender.Mode.DEFAULT);\n\n        messageSender.send(message);\n\n    }\n\n    @Reference\n    private SingleDestinationMessageSenderFactory _messageSenderFactory;\n}\n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-11}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-message}{Creating\na Message}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/sending-messages-across-a-cluster}{Sending\nMessages Across a Cluster}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus}{Using\nthe Message Bus}\n\n\\chapter{Sending Messages Across a\nCluster}\\label{sending-messages-across-a-cluster}\n\nTo ensure a message sent to a destination is received by all cluster\nnodes, you must register a\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cluster/messaging/ClusterBridgeMessageListener.html}{\\texttt{ClusterBridgeMessageListener}}\nat that destination. This bridges the local destination to the cluster\nand ensures that messages sent to the destination are distributed across\nthe cluster's JVMs. You should do this in a registrator class, like\nthose shown in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/registering-message-listeners}{Registering\nMessage Listeners}.\n\nFollow these steps to create a registrator class that registers a\n\\texttt{ClusterBridgeMessageListener} to a destination:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create the registrator class as an OSGi component:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    service = MyMessageListenerRegistrator.class\n)\npublic class MyMessageListenerRegistrator {\n    ...\n}\n\\end{verbatim}\n\\item\n  Create a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageListener.html}{\\texttt{MessageListener}}\n  variable:\n\n\\begin{verbatim}\nprivate MessageListener _clusterBridgeMessageListener;\n\\end{verbatim}\n\\item\n  Create a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/Destination.html}{\\texttt{Destination}}\n  reference and set its \\texttt{destination.name} property to your\n  destination. For example, this reference is for the destination\n  \\texttt{liferay/live\\_users}:\n\n\\begin{verbatim}\n@Reference(target = \"(destination.name=liferay/live_users)\")\nprivate Destination _destination;\n\\end{verbatim}\n\\item\n  In the registrator's \\texttt{activate} method, create a new\n  \\texttt{ClusterBridgeMessageListener} and set it to the\n  \\texttt{MessageListener} variable you created earlier. Then set the\n  \\texttt{ClusterBridgeMessageListener}'s priority and register the\n  \\texttt{ClusterBridgeMessageListener} to the destination:\n\n\\begin{verbatim}\n@Activate\nprotected void activate() {\n\n    _clusterBridgeMessageListener = new ClusterBridgeMessageListener();\n    _clusterBridgeMessageListener.setPriority(Priority.LEVEL5)\n    _destination.register(_clusterBridgeMessageListener);\n}\n\\end{verbatim}\n\n  The\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cluster/Priority.html}{\\texttt{Priority}}\n  enum has ten levels (\\texttt{Level1} through \\texttt{Level10}, with\n  \\texttt{Level10} being the most important). Each level is a priority\n  queue for sending messages through the cluster. This is similar in\n  concept to thread priorities: \\texttt{Thread.MIN\\_PRIORITY},\n  \\texttt{Thread.MAX\\_PRIORITY}, and \\texttt{Thread.NORM\\_PRIORITY}.\n\\item\n  In the registrator's \\texttt{deactivate} method, unregister the\n  \\texttt{ClusterBridgeMessageListener} from the destination:\n\n\\begin{verbatim}\n@Deactivate\nprotected void deactivate() {\n\n    _destination.unregister(_clusterBridgeMessageListener);\n}\n\\end{verbatim}\n\\end{enumerate}\n\nHere's the full registrator class for this example:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    service = MyMessageListenerRegistrator.class\n)\npublic class MyMessageListenerRegistrator {\n    ...\n\n    @Activate\n    protected void activate() {\n\n        _clusterBridgeMessageListener = new ClusterBridgeMessageListener();\n        _clusterBridgeMessageListener.setPriority(Priority.LEVEL5)\n        _destination.register(_clusterBridgeMessageListener);\n    }\n\n    @Deactivate\n    protected void deactivate() {\n\n        _destination.unregister(_clusterBridgeMessageListener);\n    }\n\n    @Reference(target = \"(destination.name=liferay/live_users)\")\n    private Destination _destination;\n\n    private MessageListener _clusterBridgeMessageListener;\n}\n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-12}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/registering-message-listeners}{Registering\nMessage Listeners}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/sending-a-message}{Sending\na Message}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus}{Using\nthe Message Bus}\n\n\\chapter{Cache Configuration}\\label{cache-configuration}\n\nCaching makes specified data readily available in memory. It costs\nmemory but improves performance. You can experiment with cache to\ndetermine what's good for your system. If your site serves lots of web\ncontent articles, for example, you may want to increase the limit on how\nmany you can cache.\n\nLiferay's cache configuration framework uses\n\\href{https://www.ehcache.org/}{Ehcache}. It's an independent framework\nused by Liferay DXP's data access and template engine components. It\nmanages two pools:\n\n\\textbf{Multi-VM:} Cache is replicated among cluster nodes.\n\\texttt{EntityCache} and \\texttt{FinderCache} (described next) are in\nthis pool because they must synchronize with data on all nodes.\n\n\\textbf{Single-VM:} Cache is managed uniquely per VM and isn't\nreplicated among nodes. Single-VM cache is for objects and references\nthat you don't need/want replicated among nodes.\n\nHere are ways you can configure the Ehcache:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/overriding-cache}{Overriding\n  Cache}: Tuning existing cache.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/caching-data}{Caching\n  Data}: Implementing cache for custom data.\n\\end{itemize}\n\nStart learning the Liferay cache configuration basics here.\n\n\\section{Cache Types}\\label{cache-types}\n\nYou can cache any classes you like. Conveniently, Liferay DXP caches\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-service-entities}{service\nentities} and\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-service-entity-finder-methods}{service\nentity finder results} automatically by default.\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} generates their caching code in the\n\\href{/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder}{service\npersistence layer}. The code operates on these cache types:\n\n\\textbf{\\texttt{EntityCache}:} Holds service entities by primary keys.\nThe caching code maps entity primary keys to implementation objects. An\nentity's \\texttt{*PersistenceImpl.fetchByPrimaryKey} method uses\n\\texttt{EntityCache}.\n\n\\textbf{\\texttt{FinderCache}:} Holds parameterized service entity search\nresults. The caching code associates\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-service-entity-finder-methods}{service\nentity finder} query parameter values with matching entity results.\nThere's code for caching entities, paginated entity lists, and\nnon-paginated entity lists that match your finder parameters. An\nentity's \\texttt{fetchByValue}, \\texttt{findByValue},\n\\texttt{countByValue}, \\texttt{findAll}, and \\texttt{countAll} methods\nuse the FinderCache.\n\n\\section{Cache Configuration}\\label{cache-configuration-1}\n\nLiferay DXP designates separate cache configurations for multi-VM and\nsingle-VM environments. Default \\texttt{EntityCache} and\n\\texttt{FinderCache} are specified programmatically, while Liferay's\nglobal cache configuration and custom cache configurations are specified\nvia files. All configurations adhere to the\n\\href{http://www.ehcache.org/ehcache.xsd}{Ehcache XSD}.\n\nLiferay's global cache configuration is processed first on startup.\nCache configurations in modules and WARs are processed as they're\ndeployed after the initial global cache configuration.\n\n\\section{Initial Global Cache\nConfiguration}\\label{initial-global-cache-configuration}\n\nLiferay's portal cache implementation LPKG file\n(\\texttt{Liferay\\ {[}version{]}\\ Foundation\\ -\\ Liferay\\ {[}version{]}\\ Portal\\ Cache\\ -\\ Impl.lpkg})\nfound in the \\texttt{{[}Liferay\\_Home{]}/osgi/marketplace} folder\ncontains the initial global cache configuration. The LPKG file's\n\\texttt{com.liferay.portal.cache.ehcache.impl-{[}version{]}.jar} holds\nthe configuration files:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{liferay-multi-vm.xml}: Maps to the multi-VM pool.\n\\item\n  \\texttt{liferay-single-vm.xml}: Maps to the single-VM pool.\n\\end{itemize}\n\n\\section{Module Cache Configuration}\\label{module-cache-configuration}\n\nModules can configure (add or override) cache using configuration files\nin their \\texttt{src/main/resources/META-INF} folder:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{module-multi-vm.xml}: Maps to the multi-VM cache manager.\n\\item\n  \\texttt{module-single-vm.xml}: Maps to the single-VM cache manager.\n\\end{itemize}\n\nFor example, the Liferay DXP Web Experience suite's\n\\texttt{com.liferay.journal.service} module uses the following\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/journal/journal-service/src/main/resources/META-INF/module-multi-vm.xml}{\\texttt{module-multi-vm.xml}}\nto create a cache named \\texttt{com.liferay.journal.util.JournalContent}\nin the multi-VM pool.\n\n\\begin{verbatim}\n<ehcache\n    dynamicConfig=\"true\"\n    monitoring=\"off\"\n    name=\"module-multi-vm\"\n    updateCheck=\"false\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"http://www.ehcache.org/ehcache.xsd\"\n>\n    <cache\n        eternal=\"false\"\n        maxElementsInMemory=\"10000\"\n        name=\"com.liferay.journal.util.JournalContent\"\n        overflowToDisk=\"false\"\n        timeToIdleSeconds=\"600\"\n    >\n    </cache>\n</ehcache>\n\\end{verbatim}\n\nPortlet WARs can configure cache too.\n\n\\section{Portlet WAR Cache\nConfiguration}\\label{portlet-war-cache-configuration}\n\nEhcache configuration in a portlet WAR has these requirements:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The Ehcache configuration XML file must be in the application context\n  (e.g., any path under \\texttt{WEB-INF/src}).\n\\item\n  The \\texttt{portlet.properties} file must specify the cache file\n  location. Either of the two properties is used and is assigned the\n  cache file path, relative to the application context root (e.g.,\n  \\texttt{WEB-INF/src}).\n\\end{enumerate}\n\n\\begin{verbatim}\nehcache.single.vm.config.location=path/to/single/vm/config/file\nehcache.multi.vm.config.location=path/to/multi/vm/config/file \n\\end{verbatim}\n\nFor example, here's the\n\\href{https://github.com/liferay/liferay-plugins/blob/7.0.x/portlets/test-cache-configuration-portlet}{\\texttt{test-cache-configuration-portlet}}\nWAR's structure:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{docroot/WEB-INF/src/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{ehcache/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\href{https://github.com/liferay/liferay-plugins/blob/7.0.x/portlets/test-cache-configuration-portlet/docroot/WEB-INF/src/ehcache/liferay-single-vm-ext.xml}{\\texttt{liferay-single-vm-ext.xml}}\n    \\item\n      \\href{https://github.com/liferay/liferay-plugins/blob/7.0.x/portlets/test-cache-configuration-portlet/docroot/WEB-INF/src/ehcache/liferay-multi-vm-clustered-ext.xml}{\\texttt{liferay-multi-vm-clustered-ext.xml}}\n    \\end{itemize}\n  \\item\n    \\texttt{portlet.properties}\n  \\end{itemize}\n\\end{itemize}\n\nThe \\texttt{portlet.properties} file specifies these properties:\n\n\\begin{verbatim}\nehcache.single.vm.config.location=ehcache/liferay-single-vm-ext.xml\nehcache.multi.vm.config.location=ehcache/liferay-multi-vm-clustered-ext.xml\n\\end{verbatim}\n\n\\section{Cache Names and\nRegistration}\\label{cache-names-and-registration}\n\nA cache is identified by its name (e.g.,\n\\texttt{\\textless{}cache\\ name=\"com.liferay.docs.MyClass\"\\ ...\\ /\\textgreater{}}).\nIf a module provides a cache configuration with the name of an existing\ncache, the existing cache is overridden. If a module provides a cache\nconfiguration with a new name, a new cache is added.\n\nHere's what happens behind the scenes: Liferay's cache manager checks\nthe configurations. If a cache with the name already exists, the cache\nmanager removes it from Ehcache's cache registry and registers a new\nEhcache into Ehcache's cache registry. If the name is new, the Liferay\ncache manager just registers a new Ehcache.\n\nCache names are arbitrary except for \\texttt{EntityCache} and\n\\texttt{FinderCache}.\n\n\\section{EntityCache Names}\\label{entitycache-names}\n\n\\texttt{EntityCache} uses this naming convention:\n\n\\texttt{PREFIX\\ +\\ ENTITY\\_IMPL\\_CLASS\\_NAME}\n\nwhere the \\texttt{PREFIX} is always this:\n\n\\begin{verbatim}\ncom.liferay.portal.kernel.dao.orm.EntityCache.\n\\end{verbatim}\n\nFor example, the cache name for the\n\\texttt{com.liferay.portal.kernel.model.User} entity starts with the\n\\texttt{PREFIX} and ends with the implementation class name\n\\texttt{com.liferay.portal.model.impl.UserImpl}:\n\n\\begin{verbatim}\ncom.liferay.portal.kernel.dao.orm.EntityCache.com.liferay.portal.model.impl.UserImpl\n\\end{verbatim}\n\n\\section{FinderCache Names}\\label{findercache-names}\n\n\\texttt{FinderCache} uses this naming convention:\n\n\\texttt{PREFIX\\ +\\ ENTITY\\_IMPL\\_CLASS\\_NAME\\ +\\ {[}\".LIST1\"\\textbar{}\".LIST2\"{]}}\n\nwhere the \\texttt{PREFIX} is always this:\n\n\\begin{verbatim}\ncom.liferay.portal.kernel.dao.orm.FinderCache.\n\\end{verbatim}\n\nHere are the \\texttt{FinderCache} types and their name patterns.\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.2222}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3889}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3889}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nType\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nPattern\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nExample\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nEntity instances matching query parameters. &\n\\texttt{PREFIX\\ +\\ ENTITY\\_IMPL\\_CLASS\\_NAME} &\n\\texttt{com.liferay.portal.kernel.dao.orm.FinderCache.com.liferay.portal.model.impl.ClassNameImpl} \\\\\nPaginated lists of entity instances matching query parameters. &\n\\texttt{PREFIX\\ +\\ ENTITY\\_IMPL\\_CLASS\\_NAME\\ +\\ \".List1\"} &\n\\texttt{com.liferay.portal.kernel.dao.orm.FinderCache.com.liferay.portal.model.impl.ClassNameImpl.List1} \\\\\nNon-paginated lists of entity instances matching query parameters. &\n\\texttt{PREFIX\\ +\\ ENTITY\\_IMPL\\_CLASS\\_NAME\\ +\\ \".List2\"} &\n\\texttt{com.liferay.portal.kernel.dao.orm.FinderCache.com.liferay.portal.model.impl.ClassNameImpl.List2} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nNow that you have a basic understanding of cache in Liferay, continue\nwith overriding an existing cache configuration or caching custom data.\n\n\\chapter{Overriding Cache}\\label{overriding-cache}\n\nLiferay DXP pre-configures cache for service entities, service entity\nfinder results, and cache for several other classes. You can tune\nexisting cache to meet your needs. For example, it may help to write\ncache overflow elements to disk, increase the maximum number of cached\nelements, or make other adjustments. Using a module and only one XML\nfile, you can override cache configurations dynamically.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Modifying an Ehcache element flushes its cache.\n\n\\noindent\\hrulefill\n\nHere is how to override a cache configuration:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Identify the name of the cache you want to override. Existing cache\n  configurations and statistics (hit/miss counts and percentages) can be\n  examined at runtime through JMX. Using a tool that supports JMX\n  analysis, you can examine Liferay DXP's cache configurations in the\n  MBean of \\texttt{net.sf.ehcache}. Please note that the caches listed\n  in the MBean are more than what Liferay DXP's cache configuration\n  files specify because some caches are created purely through Java\n  code.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/zulu-mission-control.png}\n  \\caption{Caches configured in Liferay DXP can be examined using JMX\n  tools such as Zulu Mission Control (Portal Process → MBean server →\n  MBean Browser)}\n  \\end{figure}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** See\n [Cache Names and Registration](/docs/7-2/frameworks/-/knowledge_base/f/cache-configuration#cache-names-and-registration)\n to identify `EntityCache` and the different kinds of `FinderCache` instances \n associated with service entities. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nSome cache configurations can also be viewed statically in their deployment\nartifacts or source code.\n\n-   `liferay-*-vm.xml` files in the\n    `Liferay [version] Foundation - Liferay [version] Portal Cache - Impl.lpkg` file.\n\n-   `module-*-vm.xml` files in modules or Liferay LPKG files.\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\tightlist\n\\item\n  If you don't own the existing project that specifies the cache or you\n  want to use a different project to configure the cache, create a\n  module project. Otherwise, edit the cache in the existing project.\n  These instructions demonstrate adding the cache configuration to a new\n  module project.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Tip:** create new projects using the\n [API project template](/docs/7-2/reference/-/knowledge_base/r/api-template)\n and remove the Java class generated in the `src/main/java/` folder.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  In the \\texttt{src/main/resources/META-INF} folder, add an XML file\n  for the type of cache (multi-VM or single-VM) you're overriding.\n\n  \\texttt{module-multi-vm.xml} file:\n\n\\begin{verbatim}\n<ehcache\n    dynamicConfig=\"true\"\n    monitoring=\"off\"\n    name=\"module-multi-vm\"\n    updateCheck=\"false\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"http://www.ehcache.org/ehcache.xsd\"\n>\n    <!-- cache elements go here -->\n</ehcache>\n\\end{verbatim}\n\n  \\texttt{module-single-vm.xml} file:\n\n\\begin{verbatim}\n<ehcache\n    dynamicConfig=\"true\"\n    monitoring=\"off\"\n    name=\"module-single-vm\"\n    updateCheck=\"false\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"http://www.ehcache.org/ehcache.xsd\"\n>\n    <!-- cache elements go here -->\n</ehcache>\n\\end{verbatim}\n\\item\n  In the \\texttt{\\textless{}ehcache/\\textgreater{}} element, add a\n  \\texttt{\\textless{}cache/\\textgreater{}} element and set its\n  \\texttt{name} attribute to the name of the cache you're overriding.\n\\item\n  Specify all existing \\texttt{\\textless{}cache/\\textgreater{}} element\n  attributes you want to preserve. Hint: view the attributes in an MBean\n  browser, as mentioned earlier.\n\\item\n  Add or modify attributes to meet your needs. The\n  \\texttt{\\textless{}cache/\\textgreater{}} element attributes are\n  described in the\n  \\href{http://www.ehcache.org/ehcache.xsd}{ehcache.xsd} and\n  \\href{http://www.ehcache.org/documentation/2.8/configuration/index.html}{Ehcache\n  documentation}.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  the project}.\n\\end{enumerate}\n\nCongratulations! Your cache modification is in effect.\n\n\\section{Related Topics}\\label{related-topics-13}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/caching-data}{Caching\nData}\n\n\\chapter{Caching Data}\\label{caching-data}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/cache-configuration}{Liferay's\ncaching framework} helps you use Ehcache to cache any data. The\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/SingleVMPool.html}{\\texttt{SingleVMPool}}\nand\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/MultiVMPool.html}{\\texttt{MultiVMPool}}\nclasses use Liferay's\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/PortalCache.html}{\\texttt{PortalCache}}\nutility. Storing and retrieving cached data objects is as easy as using\na hash map: you associate a key with every cache value. The following\nsteps demonstrate implementing data caching.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you want to modify cache for Service Builder Service\nEntities or Entity Finder results, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/overriding-cache}{Overriding\nCache}.\n\n\\noindent\\hrulefill\n\n\\section{Step 1: Determine Cache Pool\nRequirements}\\label{step-1-determine-cache-pool-requirements}\n\nThere are cache pools for single-VM and multi-VM environments. The pool\ntypes and some Ehcache features require using \\texttt{Serializable}\nvalues.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Determine whether to create a cache\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/cache-configuration}{in\n  a single VM or across multiple VMs} (e.g., in a clustered\n  environment).\n\\item\n  Determine if it's necessary to serialize the data you're caching.\n\n  \\begin{itemize}\n  \\item\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/MultiVMPool.html}{\\texttt{MultiVMPool}}\n    requires both the cache key and cache value to be\n    \\href{https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html}{\\texttt{Serializable}}.\n  \\item\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/SingleVMPool.html}{\\texttt{SingleVMPool}}\n    typically requires only cache keys to be\n    \\href{https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html}{\\texttt{Serializable}}.\n    Note that some Ehache features, such as \\texttt{overflowToDisk},\n    require \\texttt{Serializable} values too.\n  \\end{itemize}\n\\end{enumerate}\n\n\\section{Step 2: Implement a Cache\nKey}\\label{step-2-implement-a-cache-key}\n\nCache keys must be unique,\n\\href{https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html}{\\texttt{Serializable}}\nobjects. They should relate to the values being cached. For example, in\nLiferay DXP's \\texttt{JournalContentImpl}, a \\texttt{JournalContentKey}\ninstance relates to each cached \\texttt{JournalArticleDisplay} object.\nHere's the \\texttt{JournalContentKey} class:\n\n\\begin{verbatim}\nprivate static class JournalContentKey implements Serializable {\n\n    @Override\n    public boolean equals(Object obj) {\n        JournalContentKey journalContentKey = (JournalContentKey)obj;\n\n        if ((journalContentKey._groupId == _groupId) &&\n            Objects.equals(journalContentKey._articleId, _articleId) &&\n            (journalContentKey._version == _version) &&\n            Objects.equals(\n                journalContentKey._ddmTemplateKey, _ddmTemplateKey) &&\n            (journalContentKey._layoutSetId == _layoutSetId) &&\n            Objects.equals(journalContentKey._viewMode, _viewMode) &&\n            Objects.equals(journalContentKey._languageId, _languageId) &&\n            (journalContentKey._page == _page) &&\n            (journalContentKey._secure == _secure)) {\n\n            return true;\n        }\n\n        return false;\n    }\n\n    @Override\n    public int hashCode() {\n        int hashCode = HashUtil.hash(0, _groupId);\n\n        hashCode = HashUtil.hash(hashCode, _articleId);\n        hashCode = HashUtil.hash(hashCode, _version);\n        hashCode = HashUtil.hash(hashCode, _ddmTemplateKey);\n        hashCode = HashUtil.hash(hashCode, _layoutSetId);\n        hashCode = HashUtil.hash(hashCode, _viewMode);\n        hashCode = HashUtil.hash(hashCode, _languageId);\n        hashCode = HashUtil.hash(hashCode, _page);\n\n        return HashUtil.hash(hashCode, _secure);\n    }\n\n    private JournalContentKey(\n        long groupId, String articleId, double version,\n        String ddmTemplateKey, long layoutSetId, String viewMode,\n        String languageId, int page, boolean secure) {\n\n        _groupId = groupId;\n        _articleId = articleId;\n        _version = version;\n        _ddmTemplateKey = ddmTemplateKey;\n        _layoutSetId = layoutSetId;\n        _viewMode = viewMode;\n        _languageId = languageId;\n        _page = page;\n        _secure = secure;\n    }\n\n    private static final long serialVersionUID = 1L;\n\n    private final String _articleId;\n    private final String _ddmTemplateKey;\n    private final long _groupId;\n    private final String _languageId;\n    private final long _layoutSetId;\n    private final int _page;\n    private final boolean _secure;\n    private final double _version;\n    private final String _viewMode;\n\n}\n\\end{verbatim}\n\n\\texttt{JournalContentKey}s constructor populates fields that\ncollectively define unique keys for each piece of journal content.\n\nNote a cache key's characteristics:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  A key instance's field values relate to the cached data and\n  distinguish it from other data instances.\n\\item\n  A key follows \\texttt{Serializable} class best practices.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Overrides \\texttt{Object}'s \\texttt{equals} and \\texttt{hashcode}\n    methods.\n  \\item\n    Includes a private static final long \\texttt{serialVersionUID}\n    field. It is to be incremented when a new version of the class is\n    incompatible with previous versions.\n  \\end{itemize}\n\\end{enumerate}\n\nYour cache key class is ready for caching data values.\n\n\\section{Step 3: Implement Cache\nLogic}\\label{step-3-implement-cache-logic}\n\nWhen your application creates or requests the data type you're caching,\nyou must handle getting existing data from cache and putting new/updated\ndata into the cache. Liferay DXP's caching classes are easy to inject\ninto a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{Declarative\nServices (DS) Component}, but you can access them using\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker}{\\texttt{ServiceTracker}}s\ntoo. These steps use fictitious key and value classes: \\texttt{SomeKey}\nand \\texttt{SomeValue}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Name your cache. Cache names are arbitrary, but they must be unique in\n  the cache pool, and typically identify the data type being cached.\n\\end{enumerate}\n\n\\begin{verbatim}\nprotected static final String CACHE_NAME = SomeValue.class.getName();\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\tightlist\n\\item\n  Access the VM pool you're using.\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/MultiVMPool.html}{\\texttt{MultiVMPool}}\n  and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/SingleVMPool.html}{\\texttt{SingleVMPool}}\n  are Declarative Service (DS) components. To access a pool from a DS\n  component, apply the\n  \\href{https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Reference.html}{\\texttt{@Reference}}\n  annotation to a pool field (see below). Otherwise, use a\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker}{\\texttt{ServiceTracker}}\n  to access the pool.\n\\end{enumerate}\n\n\\begin{verbatim}\n@Reference\nprivate MultiVMPool _multiVMPool;\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\tightlist\n\\item\n  Declare a private static\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/PortalCache.html}{\\texttt{PortalCache}}\n  instance.\n\\end{enumerate}\n\n\\begin{verbatim}\nprivate static PortalCache<SomeKey, SomeValue> _portalCache;\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\tightlist\n\\item\n  Initialize your \\texttt{PortalCache} when your class is being\n  activated or initialized. If you're using a DS component, initialize\n  the cache in your component's activation method (annotated with\n  \\href{https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Activate.html}{\\texttt{@Activate}}).\n  Get the cache from your VM pool using your cache name. For example,\n  this DS component's activation method gets a cache from the multi-VM\n  pool.\n\\end{enumerate}\n\n\\begin{verbatim}\n@Activate\npublic void activate() {\n    _portalCache =\n        (PortalCache<SomeKey, SomeValue>)\n            _multiVMPool.getPortalCache(CACHE_NAME);\n    ...\n}\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{4}\n\\tightlist\n\\item\n  Similarly, remove your cache when your class instance is deactivated\n  or destroyed. If you're using a DS component, remove the cache in your\n  deactivation method (annotated with\n  \\href{https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Deactivate.html}{\\texttt{@Deactivate}}).\n  Use the VM pool to remove the cache.\n\\end{enumerate}\n\n\\begin{verbatim}\n@Deactivate\npublic void deactivate() {\n    _multiVMPool.removePortalCache(CACHE_NAME);\n}\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{5}\n\\tightlist\n\\item\n  In your code that uses the cached data, implement your caching logic.\n  Here's some example code:\n\\end{enumerate}\n\n\\begin{verbatim}\nSomeKey key = new SomeKey(...); \n\nSomeValue value = _portalCache.get(\n    key);\n\nif (value == null) {\n    value = createSomeValue(...);\n\n    _portalCache.put(key, value);\n}\n\n// continue using the data \n...\n\\end{verbatim}\n\nThe code above constructs a key based on the data being used. Then, the\nkey is used to check the \\texttt{PortalCache} for the data. If the cache\ndoesn't have data associated with the key, data is created and put it\ninto the cache. The code continues using the cached data. Use similar\nlogic for the data you are caching.\n\nConfiguring the cache and deploying your project is next.\n\n\\section{Step 4: Configure the Cache}\\label{step-4-configure-the-cache}\n\nIt's time to specify your Ehcache configuration.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Depending on the VM pool you're using, start your XML file in one of\n  the following ways.\n\\end{enumerate}\n\nMulti VM file:\n\n\\begin{verbatim}\n<ehcache\n    dynamicConfig=\"true\"\n    monitoring=\"off\"\n    name=\"module-multi-vm\"\n    updateCheck=\"false\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"http://www.ehcache.org/ehcache.xsd\"\n>\n    <!-- cache elements go here -->\n</ehcache>\n\\end{verbatim}\n\nSingle VM file:\n\n\\begin{verbatim}\n<ehcache\n    dynamicConfig=\"true\"\n    monitoring=\"off\"\n    name=\"module-single-vm\"\n    updateCheck=\"false\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"http://www.ehcache.org/ehcache.xsd\"\n>\n    <!-- cache elements go here -->\n</ehcache>\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Add a \\texttt{\\textless{}cache\\textgreater{}} element for the cache\n  you're creating. Although the cache name is arbitrary, using a\n  name-spaced name such as a fully qualified class name is a best\n  practice.\n\n  Configure your \\texttt{\\textless{}cache\\textgreater{}} element to fit\n  your caching requirements. The\n  \\href{http://www.ehcache.org/ehcache.xsd}{ehcache.xsd} and\n  \\href{http://www.ehcache.org/documentation/2.8/configuration/index.html}{Ehcache\n  documentation} describe the \\texttt{\\textless{}cache\\textgreater{}}\n  attributes.\n\n  For example, the Liferay Web Experience suite's\n  \\texttt{com.liferay.journal.service} module uses this\n  \\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/journal/journal-service/src/main/resources/META-INF/module-multi-vm.xml}{\\texttt{module-multi-vm.xml}}\n  file to configure its cache named\n  \\texttt{com.liferay.journal.util.JournalContent}.\n\\end{enumerate}\n\n\\begin{verbatim}\n<ehcache\n    dynamicConfig=\"true\"\n    monitoring=\"off\"\n    name=\"module-multi-vm\"\n    updateCheck=\"false\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"http://www.ehcache.org/ehcache.xsd\"\n>\n    <cache\n        eternal=\"false\"\n        maxElementsInMemory=\"10000\"\n        name=\"com.liferay.journal.util.JournalContent\"\n        overflowToDisk=\"false\"\n        timeToIdleSeconds=\"600\"\n    >\n    </cache>\n</ehcache>\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\tightlist\n\\item\n  Deploy your project.\n\\end{enumerate}\n\nCongratulations! Your data cache is in effect.\n\n\\section{Related Topics}\\label{related-topics-14}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/overriding-cache}{Overriding\nCache}\n\n\\chapter{Collaboration}\\label{collaboration}\n\nUnderlying the\n\\href{/docs/7-2/user/-/knowledge_base/u/collaboration}{collaboration\nsuite} is a set of powerful APIs that add collaboration features to your\napps. For example, if your app contains a custom content type, you can\nuse the collaboration suite's social API to enable comments and ratings\nfor that content. You can also integrate your app with the Documents and\nMedia Library, and much more.\n\nHere are a few of the things you can do with the collaboration suite's\nAPIs.\n\n\\chapter{Item Selector}\\label{item-selector}\n\nAn \\emph{Item Selector} is a UI component for selecting entities in a\nuser-friendly manner. Many Liferay apps use Item Selectors to select\nitems such as images, videos, audio files, documents, and pages. For\nexample, the Documents and Media Item Selector selects files.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/item-selector-dialog-02.png}\n\\caption{Item Selectors select different kinds of entities.}\n\\end{figure}\n\nThe Item Selector API provides a framework for you to use, extend, and\ncreate Item Selectors in your apps.\n\nHere are some use cases for the Item Selector API:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Selecting entities with an Item Selector.\n\\item\n  Configuring an Item Selector to select your app's custom entity.\n\\item\n  Adding a new \\emph{selection view} to customize the selection\n  experience.\n\\end{enumerate}\n\n\\chapter{Adaptive Media}\\label{adaptive-media}\n\nThe\n\\href{/docs/7-2/user/-/knowledge_base/u/adapting-your-media-across-multiple-devices}{Adaptive\nMedia} app tailors the size and quality of images to the device\ndisplaying them. For example, you can configure Adaptive Media to send\nlarge, high-resolution images only to devices that can display them.\nOther devices get images that consume less bandwidth and processing\npower.\n\nBy default, Adaptive Media integrates with Documents and Media, Blogs,\nand Web Content. You can also integrate it with your apps. Adaptive\nMedia contains a taglib that displays the adapted image matching the\nfile version you supply. You can also use Adaptive Media's finder API if\nyou need to get adapted images that match other criteria (e.g., a\nspecific resolution, a range of attributes, etc.). You can even\ncustomize the image scaling that Adaptive Media uses to produce adapted\nimages.\n\n\\chapter{Social API}\\label{social-api}\n\nUsers interact with content via Liferay DXP's social features. For\nexample, users can provide feedback on content, share that content with\nothers, subscribe to receive notifications, and more. Use the social API\nto enable such functionality in your apps.\n\nHere's an example of some functionality you can add to your apps via the\nsocial API:\n\n\\textbf{Social Bookmarks:} Share content on social media. You can also\ncreate new social bookmarks if one doesn't exist for your social network\nof choice.\n\n\\textbf{Comments:} Comment on content.\n\n\\textbf{Ratings:} Rate content. Administrators can also change the\nrating type (e.g., likes, stars, thumbs, etc.).\n\n\\textbf{Flags:} Flag inappropriate content.\n\n\\chapter{Documents and Media API}\\label{documents-and-media-api}\n\nUsers can use, manage, and share files in the Documents and Media\nLibrary. For example, users can embed files in content, organize them in\nfolders, edit and collaborate on them with other users, and more. See\nthe\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-documents-and-media}{user\nguide} for more information on the Documents and Media Library's\nfeatures.\n\nA powerful API underlies the Documents and Media Library's\nfunctionality. You can leverage this API in your apps. For example, you\ncould create an app that uploads files to the Documents and Media\nLibrary. Your app could even update, delete, and copy files.\n\nHere's an example of some things you can do with the Documents and Media\nAPI:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Create files, folders, and shortcuts.\n\\item\n  Delete entities.\n\\item\n  Update entities.\n\\item\n  Check out files for editing, and check them back in.\n\\item\n  Copy and move entities.\n\\item\n  Get entities.\n\\end{itemize}\n\n\\chapter{Item Selector}\\label{item-selector-1}\n\nAn \\emph{Item Selector} is a UI component for selecting entities in a\nuser-friendly manner.\n\nHere's what you'll learn to do with Item Selectors:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select Entities.\n\\item\n  Create Custom Item Selector Criteria.\n\\item\n  Create Custom Item Selector Views.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/item-selector-dialog-02.png}\n\\caption{Item Selectors select entities.}\n\\end{figure}\n\n\\chapter{Understanding the Item Selector API's\nComponents}\\label{understanding-the-item-selector-apis-components}\n\nBefore working with the Item Selector API, you should learn about its\ncomponents. You'll work with these components as you leverage the API in\nyour apps:\n\n\\textbf{Selection View:} A class that shows entities of particular types\nfrom different sources. For example, an Item Selector configured to show\nimages might show selection views from Documents and Media, a\nthird-party image provider, or a drag-and-drop UI. Selection views are\nthe framework's key components.\n\n\\textbf{Markup:} A markup file that renders the selection view. You can\nchoose from JSP, FreeMarker, or even pure HTML and JavaScript.\n\n\\textbf{Return Type:} A class that represents the data type that entity\nselections return. For example, if users select images and you want to\nreturn the selected image's URL, then you need a URL return type. Each\nreturn type class must implement\n\\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorReturnType.html}{\\texttt{ItemSelectorReturnType}}.\nSuch classes are named after the return type's data and suffixed with\n\\texttt{ItemSelectorReturnType}. For example, the URL return type class\nis \\texttt{URLItemSelectorReturnType}. The return type class is an API\nthat connects the return type to the Item Selector's views. The Item\nSelector uses the return type class, which is empty and returns no\ninformation, as an identifier. The view ensures that the proper\ninformation is returned. If you create your own return type, you should\nspecify its data type and format in Javadoc.\n\n\\textbf{Criterion:} A class that represents the selected entity. For\nexample, if users select images, you need an image criterion class. Each\ncriterion class must implement\n\\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorCriterion.html}{\\texttt{ItemSelectorCriterion}}.\nSuch classes are named for the entity they represent and suffixed with\n\\texttt{ItemSelectorCriterion}. For example, the criterion class for\nimages is \\texttt{ImageItemSelectorCriterion}. If you create your own\ncriterion class, extend\n\\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/BaseItemSelectorCriterion.html}{\\texttt{BaseItemSelectorCriterion}}.\nThis base class implements \\texttt{ItemSelectorCriterion} and provides\nmethods that handle the Item Selector's return types. Your criterion\nclass can therefore be empty, unless you also want to use it to pass\ninformation to the view.\n\nNote that criterion and return types together form an Item Selector's\n\\emph{criteria}. The Item Selector uses its criteria to decide which\nselection views to show.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} For a list of the criterion classes and return types that\nLiferay DXP provides, see\n\\href{/docs/7-2/reference/-/knowledge_base/r/item-selector-criterion-and-return-types}{Item\nSelector Criterion and Return Types}.\n\n\\noindent\\hrulefill\n\n\\textbf{Criterion Handler:} A class that gets the appropriate selection\nview. Each criterion requires a criterion handler. Criterion handler\nclasses extend\n\\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/BaseItemSelectorCriterionHandler.html}{\\texttt{BaseItemSelectorCriterionHandler}}\nwith the criterion's entity as a type argument. Criterion handler\nclasses are named after the criterion's entity and suffixed by\n\\texttt{ItemSelectorCriterionHandler}. For example, the image criterion\nhandler class is \\texttt{ImageItemSelectorCriterionHandler} and extends\n\\texttt{BaseItemSelectorCriterionHandler\\textless{}ImageItemSelectorCriterion\\textgreater{}}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/item-selector-architecture.png}\n\\caption{Item Selector views (selection views) are determined by the\nreturn type and criterion, and rendered by the markup.}\n\\end{figure}\n\n\\chapter{Getting an Item Selector}\\label{getting-an-item-selector}\n\nTo use an Item Selector with your criteria, you must get that Item\nSelector's URL. The URL is needed to open the Item Selector dialog in\nyour UI. To get this URL, you must get an \\texttt{ItemSelector}\nreference and call its\n\\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelector.html\\#getItemSelectorURL-com.liferay.portal.kernel.portlet.RequestBackedPortletURLFactory-java.lang.String-com.liferay.item.selector.ItemSelectorCriterion...-}{\\texttt{getItemSelectorURL}}\nmethod with the following parameters:\n\n\\texttt{RequestBackedPortletURLFactory}: A factory that creates portlet\nURLs.\n\n\\texttt{ItemSelectedEventName}: A unique, arbitrary JavaScript event\nname that the Item Selector triggers when the entity is selected.\n\n\\texttt{ItemSelectorCriterion}: The criterion (or an array of criterion\nobjects) that specifies the type of entities to make available in the\nItem Selector.\n\nKeep these points in mind when getting an Item Selector's URL:\n\n\\begin{itemize}\n\\item\n  You can invoke the URL object's \\texttt{toString} method to get its\n  value.\n\\item\n  You can configure an Item Selector to use any number of criterion. The\n  criterion can use any number of return types.\n\\item\n  The order of the Item Selector's criteria determines the selection\n  view order. For example, if you pass the Item Selector an\n  \\texttt{ImageItemSelectorCriterion} followed by a\n  \\texttt{VideoItemSelectorCriterion}, the Item Selector displays the\n  image selection views first.\n\\item\n  The return type order is also significant. A view uses the first\n  return type it supports from each criterion's return type list.\n\\end{itemize}\n\n\\chapter{Understanding Custom Selection\nViews}\\label{understanding-custom-selection-views}\n\nThe default selection views may provide everything you need for your\napp. Custom selection views are required, however, for certain\nsituations. For example, you must create a custom selection view for\nyour users to select images from an external image provider.\n\nThe selected entity type determines the view the Item Selector presents.\nThe Item Selector can also render multiple views for the same entity\ntype. For example, several selection views are available for images.\nEach selection view is a tab in the UI that corresponds to the image's\nlocation. An \\texttt{*ItemSelectorCriterion} class represents each\nselection view.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/item-selector-tabs.png}\n\\caption{An entity type can have multiple selection views.}\n\\end{figure}\n\n\\section{The Selection View's Class}\\label{the-selection-views-class}\n\nThe criterion and return types determine the selection view's class.\nThis class is an \\texttt{ItemSelectorView} component class that\nimplements\n\\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html}{\\texttt{ItemSelectorView}}\nparameterized with the view's criterion. Remember these things when\ncreating this class:\n\n\\begin{itemize}\n\\item\n  Configure the title by implementing the\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html\\#getTitle-java.util.Locale-}{\\texttt{getTitle}}\n  method to return the localized title of the tab to display in the Item\n  Selector dialog.\n\\item\n  Configure the search options by implementing the\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html\\#isShowSearch--}{\\texttt{isShowSearch()}}\n  method to return whether your view should show the search field. To\n  implement search, this method must return \\texttt{true}. The\n  \\texttt{renderHTML} method indicates whether a user performed a search\n  based on the value of the \\texttt{search} parameter. You can get the\n  user's search keywords as follows:\n\n\\begin{verbatim}\nString keywords = ParamUtil.getString(request, \"keywords\");\n\\end{verbatim}\n\\item\n  Make your view visible by implementing the\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html\\#isVisible-com.liferay.portal.kernel.theme.ThemeDisplay-}{\\texttt{isVisible()}}\n  method to return \\texttt{true}. Note that you can use this method to\n  add conditional logic to disable the view.\n\\end{itemize}\n\n\\chapter{Selecting Entities with an Item\nSelector}\\label{selecting-entities-with-an-item-selector}\n\nThe steps here show you how to get and use an Item Selector to select\nentities in your app. For an explanation of the Item Selector API and\nmore information on these steps, see the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/item-selector}{Item\nSelector introduction}.\n\n\\section{Get an Item Selector}\\label{get-an-item-selector}\n\nFirst, you must get an Item Selector for your use case. Follow these\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Determine the criterion and return types for the Item Selector. The\n  criterion corresponds to the selected entity type, and the return\n  types correspond to the data you expect to receive from those\n  selections. For a list of the criterion and return types that Liferay\n  DXP provides, see\n  \\href{/docs/7-2/reference/-/knowledge_base/r/item-selector-criterion-and-return-types}{Item\n  Selector Criterion and Return Types}. For example, if you need an Item\n  Selector that selects images and returns their URLs, use\n  \\texttt{ImageItemSelectorCriterion} and\n  \\texttt{URLItemSelectorReturnType}. You can\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-criterion-and-return-types}{create}\n  criterion and/or return types if there aren't existing ones for your\n  use case.\n\\item\n  Use Declarative Services to get an \\texttt{ItemSelector} OSGi Service\n  Component:\n\n\\begin{verbatim}\nimport com.liferay.item.selector.ItemSelector;\nimport org.osgi.service.component.annotations.Reference;\n\n...\n\n@Reference\nprivate ItemSelector _itemSelector\n\\end{verbatim}\n\n  The component annotations are available in the module\n  \\href{http://mvnrepository.com/artifact/org.osgi/org.osgi.service.component.annotations}{\\texttt{org.osgi.service.component.annotations}}.\n\\item\n  Create the factory you'll use to create the Item Selector's URL. To do\n  this, invoke the \\texttt{RequestBackedPortletURLFactoryUtil.create}\n  method with the current request object. The request can be an\n  \\texttt{HttpServletRequest} or \\texttt{PortletRequest}:\n\n\\begin{verbatim}\nRequestBackedPortletURLFactory requestBackedPortletURLFactory =\n    RequestBackedPortletURLFactoryUtil.create(request);\n\\end{verbatim}\n\\item\n  Create a list of return types expected for the entity. For example,\n  the return types list here consists of\n  \\texttt{URLItemSelectorReturnType}:\n\n\\begin{verbatim}\nList<ItemSelectorReturnType> desiredItemSelectorReturnTypes =\n    new ArrayList<>();\ndesiredItemSelectorReturnTypes.add(new URLItemSelectorReturnType());\n\\end{verbatim}\n\\item\n  Create an object for the criterion. This example creates a new\n  \\texttt{ImageItemSelectorCriterion}:\n\n\\begin{verbatim}\nImageItemSelectorCriterion imageItemSelectorCriterion =\n    new ImageItemSelectorCriterion();\n\\end{verbatim}\n\\item\n  Use the criterion's \\texttt{setDesiredItemSelectorReturnTypes} method\n  to set the return types list to the criterion:\n\n\\begin{verbatim}\nimageItemSelectorCriterion.setDesiredItemSelectorReturnTypes(\n    desiredItemSelectorReturnTypes);\n\\end{verbatim}\n\\item\n  Call the Item Selector's \\texttt{getItemSelectorURL} method to get an\n  Item Selector URL for the criterion. The method requires the URL\n  factory, an arbitrary event name, and a series of criterion instances\n  (one, in this case):\n\n\\begin{verbatim}\nPortletURL itemSelectorURL = _itemSelector.getItemSelectorURL(\n    requestBackedPortletURLFactory, \"sampleTestSelectItem\",\n    imageItemSelectorCriterion);\n\\end{verbatim}\n\\item\n  Add the \\texttt{itemSelectorURL} to the request to be able to retrieve\n  it from the JSP:\n  \\texttt{\\textless{}code/\\textgreater{}request.setAttribute(\"itemSelectorURL\",\\ itemSelectorURL.toString())\\textless{}/code\\textgreater{}\"}\n\\end{enumerate}\n\n\\section{Using the Item Selector\nDialog}\\label{using-the-item-selector-dialog}\n\nTo open the Item Selector in your UI, you must use the JavaScript\ncomponent \\texttt{LiferayItemSelectorDialog} from\n\\href{http://alloyui.com}{AlloyUI's}\n\\texttt{liferay-item-selector-dialog} module. The component listens for\nthe item selected event that you specified for the Item Selector URL.\nThe event returns the selected element's information according to its\nreturn type.\n\nFollow these steps to use the Item Selector's dialog in a JSP:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Declare the AUI tag library:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n\\end{verbatim}\n\\item\n  Define the UI element you'll use to open the Item Selector dialog. For\n  example, this creates a \\emph{Choose} button with the ID\n  \\texttt{chooseImage}:\n\n\\begin{verbatim}\n<aui:button name=\"chooseImage\" value=\"Choose\" />\n\\end{verbatim}\n\\item\n  Get the Item Selector's URL:\n\n\\begin{verbatim}\n<%\nString itemSelectorURL = GetterUtil.getString(request.getAttribute(\"itemSelectorURL\"));\n%>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}aui:script\\textgreater{}} tag and set it to\n  use the \\texttt{liferay-item-selector-dialog} module:\n\n\\begin{verbatim}\n<aui:script use=\"liferay-item-selector-dialog\">\n\n</aui:script>\n\\end{verbatim}\n\\item\n  Inside the \\texttt{\\textless{}aui:script\\textgreater{}} tag, attach an\n  event handler to the UI element you created in step two. For example,\n  this attaches a click event and a function to the \\emph{Choose}\n  button:\n\n\\begin{verbatim}\n<aui:script use=\"liferay-item-selector-dialog\">\n\n    $('#<portlet:namespace />chooseImage').on(\n    'click',\n      function(event) {\n        <!-- function logic goes here -->\n      }\n    );\n\n</aui:script>\n\\end{verbatim}\n\n  Inside the function, you must create a new instance of the\n  \\texttt{LiferayItemSelectorDialog} AlloyUI component and configure it\n  to use the Item Selector. The next steps walk you through this.\n\\item\n  Create the function's logic. First, create a new instance of the\n  Liferay Item Selector dialog:\n\n\\begin{verbatim}\nvar itemSelectorDialog = new A.LiferayItemSelectorDialog(\n    {\n        ...\n    }\n);\n\\end{verbatim}\n\\item\n  Inside the braces of the \\texttt{LiferayItemSelectorDialog}\n  constructor, first set set the \\texttt{eventName} attribute. This\n  makes the dialog listen for the item selected event. The event name is\n  the Item Selector's event name that you specified in your Java code\n  (the code that gets the Item Selector URL):\n\n\\begin{verbatim}\neventName: 'ItemSelectedEventName',\n\\end{verbatim}\n\\item\n  Immediately after the \\texttt{eventName} setting, set the \\texttt{on}\n  attribute to implement a function that operates on the selected item\n  change. For example, this function sets its variables for the newly\n  selected item. The information available to parse depends on the\n  return type(s). As the comment below indicates, you must add the logic\n  for using the selected element:\n\n\\begin{verbatim}\non: {\n        selectedItemChange: function(event) {\n            var selectedItem = event.newVal;\n\n            if (selectedItem) {\n                var itemValue = JSON.parse(\n                selectedItem.value\n                );\n                itemSrc = itemValue.url;\n\n                <!-- use item as needed -->\n            }\n        }\n},\n\\end{verbatim}\n\\item\n  Immediately after the \\texttt{on} setting, set the \\texttt{title}\n  attribute to the dialog's title:\n\n\\begin{verbatim}\ntitle: '<liferay-ui:message key=\"select-image\" />',\n\\end{verbatim}\n\\item\n  Immediately after the \\texttt{title} setting, set the \\texttt{url}\n  attribute to the previously retrieved Item Selector URL. This\n  concludes the attribute settings inside the\n  \\texttt{LiferayItemSelectorDialog} constructor:\n\n\\begin{verbatim}\nurl: '<%= itemSelectorURL.toString() %>'\n\\end{verbatim}\n\\item\n  To conclude the logic of the function from step four, open the Item\n  Selector dialog by calling its \\texttt{open} method:\n\n\\begin{verbatim}\nitemSelectorDialog.open();\n\\end{verbatim}\n\\end{enumerate}\n\nWhen the user clicks the \\emph{Choose} button, a new dialog opens,\nrendering the Item Selector with the views that support the criterion\nand return type(s).\n\nHere's the complete example code for these steps:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n\n<aui:button name=\"chooseImage\" value=\"Choose\" />\n\n<%\nString itemSelectorURL = GetterUtil.getString(request.getAttribute(\"itemSelectorURL\"));\n%>\n\n<aui:script use=\"liferay-item-selector-dialog\">\n\n    $('#<portlet:namespace />chooseImage').on(\n        'click',\n        function(event) {\n            var itemSelectorDialog = new A.LiferayItemSelectorDialog(\n                {\n                    eventName: 'ItemSelectedEventName',\n                    on: {\n                            selectedItemChange: function(event) {\n                                var selectedItem = event.newVal;\n\n                                if (selectedItem) {\n                                    var itemValue = JSON.parse(\n                                    selectedItem.value\n                                    );\n                                    itemSrc = itemValue.url;\n\n                                    <!-- use item as needed -->\n                                }\n                            }\n                    },\n                    title: '<liferay-ui:message key=\"select-image\" />',\n                    url: '<%= itemSelectorURL.toString() %>'\n                }\n            );\n            itemSelectorDialog.open();\n        }\n    );\n</aui:script>\n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-15}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/item-selector}{Item\nSelector}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-criterion-and-return-types}{Creating\nCustom Criterion and Return Types}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-item-selector-views}{Creating\nCustom Item Selector Views}\n\n\\chapter{Creating Custom Criterion and Return\nTypes}\\label{creating-custom-criterion-and-return-types}\n\nIf an existing criterion or return type doesn't fit your use case, you\ncan create them. The steps here show you how. For more detailed\ninformation on Item Selector criterion and return types, see the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/item-selector}{Item\nSelector introduction}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create your criterion class by extending\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/BaseItemSelectorCriterion.html}{\\texttt{BaseItemSelectorCriterion}}.\n  Name the class after the entity it represents and suffix it with\n  \\texttt{ItemSelectorCriterion}. You can use the class to pass\n  information to the view if needed. Otherwise, your criterion class can\n  be empty. If you pass information to the view, any fields in your\n  criterion class should be serializable and you should expose an empty\n  public constructor.\n\n  For example,\n  \\href{https://docs.liferay.com/dxp/apps/web-experience/latest/javadocs/com/liferay/journal/item/selector/criterion/JournalItemSelectorCriterion.html}{\\texttt{JournalItemSelectorCriterion}}\n  is the criterion class for \\texttt{Journal} entities (Web Content) and\n  passes primary key information to the view:\n\n\\begin{verbatim}\npublic class JournalItemSelectorCriterion extends BaseItemSelectorCriterion {\n\n        public JournalItemSelectorCriterion() {\n        }\n\n        public JournalItemSelectorCriterion(long resourcePrimKey) {\n                _resourcePrimKey = resourcePrimKey;\n        }\n\n        public long getResourcePrimKey() {\n                return _resourcePrimKey;\n        }\n\n        public void setResourcePrimKey(long resourcePrimKey) {\n                _resourcePrimKey = resourcePrimKey;\n        }\n\n        private long _resourcePrimKey;\n\n}\n\\end{verbatim}\n\\item\n  Create a criterion handler by creating an OSGi component class that\n  implements\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/BaseItemSelectorCriterionHandler.html}{\\texttt{BaseItemSelectorCriterionHandler}}.\n  This example creates a criterion handler for the\n  \\texttt{TaskItemSelectorCriterion} class. The \\texttt{@Activate} and\n  \\texttt{@Override} tokens are required to activate this OSGi\n  component:\n\n\\begin{verbatim}\n@Component(service = ItemSelectorCriterionHandler.class)\npublic class TaskItemSelectorCriterionHandler extends \n    BaseItemSelectorCriterionHandler<TaskItemSelectorCriterion> {\n\n    public Class <TaskItemSelectorCriterion> getItemSelectorCriterionClass() {\n        return TasksItemSelectorCriterionHandler.class;\n    }\n\n    @Activate\n    @Override\n    protected void activate(BundleContext bundleContext) {\n            super.activate(bundleContext);\n    }\n\n}\n\\end{verbatim}\n\\item\n  If you need a new return type, create it by implementing\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorReturnType.html}{\\texttt{ItemSelectorReturnType}}.\n  Name your return type class after the return type's data and suffix it\n  with \\texttt{ItemSelectorReturnType}. Specify the data and its format\n  in Javadoc. Return type classes need no content. For example, here's a\n  return type for a task:\n\n\\begin{verbatim}\n/**\n* This return type should return the task ID and the user who\n* created the task as a string.\n*\n* @author Joe Bloggs\n*/\npublic class TaskItemSelectorReturnType implements ItemSelectorReturnType{\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-16}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/item-selector}{Item\nSelector}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-item-selector-views}{Creating\nCustom Item Selector Views}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/selecting-entities-with-an-item-selector}{Selecting\nEntities with an Item Selector}\n\n\\chapter{Creating Custom Item Selector\nViews}\\label{creating-custom-item-selector-views}\n\nYou can create your own selection view if an Item Selector doesn't\ncontain the one you need. The steps here show you how. For more\ninformation on custom selection views and the Item Selector API, see the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/item-selector}{Item\nSelector introduction}.\n\n\\section{Configuring Your Selection View's OSGi\nModule}\\label{configuring-your-selection-views-osgi-module}\n\nFirst, you must configure your selection view's OSGi module:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add these dependencies to your module's \\texttt{build.gradle}:\n\n\\begin{verbatim}\ndependencies {\n        compileOnly group: \"com.liferay\", name: \"com.liferay.item.selector.api\", version: \"2.0.0\"\n        compileOnly group: \"com.liferay\", name: \"com.liferay.item.selector.criteria.api\", version: \"2.0.0\"\n        compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.impl\", version: \"2.0.0\"\n        compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"2.0.0\"\n        compileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\", version: \"2.0.0\"\n        compileOnly group: \"javax.portlet\", name: \"portlet-api\", version: \"2.0\"\n        compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\", version: \"3.0.1\"\n        compileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\", version: \"1.3.0\"\n}\n\\end{verbatim}\n\\item\n  Add your module's information to the \\texttt{bnd.bnd} file. For\n  example, this configuration adds the information for a module called\n  \\texttt{My\\ Custom\\ View}:\n\n\\begin{verbatim}\nBundle-Name: My Custom View\nBundle-SymbolicName: com.liferay.docs.my.custom.view\nBundle-Version: 1.0.0\n\\end{verbatim}\n\\item\n  Add a \\texttt{Web-ContextPath} to your \\texttt{bnd.bnd} to point to\n  your module's resources:\n\n\\begin{verbatim}\nInclude-Resource:\\\n        META-INF/resources=src/main/resources/META-INF/resources\nWeb-ContextPath: /my-custom-view\n\\end{verbatim}\n\n  If you don't have a \\texttt{Web-ContextPath}, your module won't know\n  where your resources are. The \\texttt{Include-Resource} header points\n  to the relative path for the module's resources.\n\\end{enumerate}\n\n\\section{Implementing Your Selection View's\nClass}\\label{implementing-your-selection-views-class}\n\nFollow these steps to implement your selection view's class:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create an \\texttt{ItemSelectorView} component class that implements\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html}{\\texttt{ItemSelectorView}}\n  with the criterion as a type argument. In the \\texttt{@Component}\n  annotation, set the \\texttt{item.selector.view.order} property to the\n  order you want the view to appear in when displayed alongside other\n  selector views (lower values get higher priority).\n\n  This example selector view class is for images, so it implements\n  \\texttt{ItemSelectorView} with\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/criteria/image/criterion/ImageItemSelectorCriterion.html}{\\texttt{ImageItemSelectorCriterion}}\n  as a type argument. The \\texttt{@Component} annotation sets the\n  \\texttt{item.selector.view.order} property to \\texttt{200} and\n  registers the class as an \\texttt{ItemSelectorView} service:\n\n\\begin{verbatim}\n@Component(\n    property = {\"item.selector.view.order:Integer=200\"},\n    service = ItemSelectorView.class\n)\npublic class SampleItemSelectorView\n    implements ItemSelectorView<ImageItemSelectorCriterion> {...\n\\end{verbatim}\n\\item\n  Create getter methods for the criterion class, servlet context, and\n  return types. Do this by implementing the methods\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html\\#getItemSelectorCriterionClass--}{\\texttt{getItemSelectorCriterionClass()}},\n  \\texttt{getServletContext()}, and\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html\\#getSupportedItemSelectorReturnTypes--}{\\texttt{getSupportedItemSelectorReturnTypes()}},\n  respectively:\n\n\\begin{verbatim}\n@Override\npublic Class<ImageItemSelectorCriterion> getItemSelectorCriterionClass() \n{\n    return ImageItemSelectorCriterion.class;\n}\n\n@Override            \npublic ServletContext getServletContext() {\n    return _servletContext;\n}\n\n@Override            \npublic List<ItemSelectorReturnType> getSupportedItemSelectorReturnTypes() {\n    return _supportedItemSelectorReturnTypes;\n}\n\\end{verbatim}\n\\item\n  Configure the selection view's title, search options, and visibility\n  settings. Here's an example configuration for the\n  \\texttt{Sample\\ Selector} selection view:\n\n\\begin{verbatim}\n@Override\npublic String getTitle(Locale locale) {\n    return \"Sample Selector\";\n}\n\n@Override\npublic boolean isShowSearch() {\n    return false;\n}\n\n@Override\npublic boolean isVisible(ThemeDisplay themeDisplay) {\n    return true;\n}\n\\end{verbatim}\n\n  See\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/item-selector\\#the-selection-views-class}{The\n  Selection View's Class} for more information on these methods.\n\\item\n  Implement the\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html\\#renderHTML-javax.servlet.ServletRequest-javax.servlet.ServletResponse-T-javax.portlet.PortletURL-java.lang.String-boolean-}{\\texttt{renderHTML}}\n  method to set your view's render settings and render its markup.\n\n  Here's an example implementation of a \\texttt{renderHTML} method that\n  points to a JSP file (\\texttt{sample.jsp}) to render the view. Note\n  that \\texttt{itemSelectedEventName} is passed as a request attribute\n  so it can be used in the view markup. The view markup is specified via\n  the \\texttt{ServletContext} method \\texttt{getRequestDispatcher}.\n  Although this example uses a JSP, you can render the markup in another\n  language such as FreeMarker.\n\n\\begin{verbatim}\n@Override\npublic void renderHTML(\n    ServletRequest request, ServletResponse response,\n    ImageItemSelectorCriterion itemSelectorCriterion,\n    PortletURL portletURL, String itemSelectedEventName,\n    boolean search\n)\nthrows IOException, ServletException {\n\n    request.setAttribute(_ITEM_SELECTED_EVENT_NAME,\n        itemSelectedEventName);\n\n    ServletContext servletContext = getServletContext();\n\n    RequestDispatcher requestDispatcher =\n        servletContext.getRequestDispatcher(\"/sample.jsp\");\n\n    requestDispatcher.include(request, response);\n}\n\\end{verbatim}\n\\item\n  Use the \\texttt{@Reference} annotation to reference your module's\n  class for the \\texttt{setServletContext} method. In the annotation,\n  use the \\texttt{target} parameter to specify the available services\n  for the servlet context. This example uses the\n  \\texttt{osgi.web.symbolicname} property to specify the\n  \\texttt{com.liferay.selector.sample.web} class as the default value.\n  You should also use the \\texttt{unbind\\ =\\ \\_} parameter to specify\n  that there's no unbind method for this module. In the method body, set\n  the servlet context variable:\n\n\\begin{verbatim}\n@Reference(\n    target =\n    \"(osgi.web.symbolicname=com.liferay.item.selector.sample.web)\",\n    unbind = \"-\"\n)\npublic void setServletContext(ServletContext servletContext) {\n    _servletContext = servletContext;\n}\n\\end{verbatim}\n\\item\n  Define the \\texttt{\\_supportedItemSelectorReturnTypes} list with the\n  return types that this view supports (you referenced this list in step\n  two). This example adds\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/criteria/URLItemSelectorReturnType.html}{\\texttt{URLItemSelectorReturnType}}\n  and\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/item/selector/criteria/FileEntryItemSelectorReturnType.html}{\\texttt{FileEntryItemSelectorReturnType}}\n  to the list of supported return types (you can use more if needed).\n  More return types means that the view is more reusable. Also note that\n  this example defines its servlet context variable at the bottom of the\n  file:\n\n\\begin{verbatim}\nprivate static final List<ItemSelectorReturnType>\n    _supportedItemSelectorReturnTypes =\n    Collections.unmodifiableList(\n        ListUtil.fromArray(\n            new ItemSelectorReturnType[] {\n                new FileEntryItemSelectorReturnType(),\n                new URLItemSelectorReturnType()\n            }));\n\n private ServletContext _servletContext;\n\\end{verbatim}\n\\end{enumerate}\n\nFor a real-world example of a view class, see\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/site-navigation/site-navigation-item-selector-web/src/main/java/com/liferay/site/navigation/item/selector/web/internal/SiteNavigationMenuItemItemSelectorView.java}{\\texttt{SiteNavigationMenuItemItemSelectorView}}.\n\n\\section{Writing Your View Markup}\\label{writing-your-view-markup}\n\nYou can write your view markup however you wish---there's no typical or\naverage case. You can write it with taglibs, AUI components, or even\npure HTML and JavaScript. The markup must do two key things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Render the entities for the user to select.\n\\item\n  When an entity is selected, pass the return type information via a\n  JavaScript event.\n\\end{itemize}\n\nThe example view class in the previous section passes the JavaScript\nevent name as a request attribute in the \\texttt{renderHTML} method. You\ncan therefore use this event name in the markup:\n\n\\begin{verbatim}\nLiferay.fire(\n        `<%= {ITEM_SELECTED_EVENT_NAME} %>',\n\n        {\n            data:{\n                the-data-your-client-needs-according-to-the-return-type\n            }\n        }\n);\n\\end{verbatim}\n\nFor a complete, real-world example, see\n\\href{https://github.com/liferay/liferay-portal/blob/7.0.x/modules/apps/web-experience/layout/layout-item-selector-web/src/main/resources/META-INF/resources/layouts.jsp}{\\texttt{layouts.jsp}}\nfor the module\n\\href{https://github.com/liferay/liferay-portal/tree/7.0.x/modules/apps/web-experience/layout/layout-item-selector-web}{\\texttt{com.liferay.layout.item.selector.web}}.\nEven though this example is for a previous version of Liferay DXP, it\nstill applies to 7.0. Here's a walkthrough of this \\texttt{layouts.jsp}\nfile:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  First, some variables are defined. Note that\n  \\texttt{LayoutItemSelectorViewDisplayContext} is an optional class\n  that contains additional information about the criteria and view:\n\n\\begin{verbatim}\n<%\nLayoutItemSelectorViewDisplayContext layoutItemSelectorViewDisplayContext = \n    (LayoutItemSelectorViewDisplayContext)request.getAttribute(\n    BaseLayoutsItemSelectorView.LAYOUT_ITEM_SELECTOR_VIEW_DISPLAY_CONTEXT);\n\nLayoutItemSelectorCriterion layoutItemSelectorCriterion = \n    layoutItemSelectorViewDisplayContext.getLayoutItemSelectorCriterion();\n\nPortlet portlet = PortletLocalServiceUtil.getPortletById(company.getCompanyId(), \n    portletDisplay.getId());\n%>\n\\end{verbatim}\n\\item\n  This snippet imports a CSS file for styling and places it in the\n  \\texttt{\\textless{}head\\textgreater{}} of the page:\n\n\\begin{verbatim}\n<liferay-util:html-top>\n        <link href=\"<%= PortalUtil.getStaticResourceURL(\n        request, application.getContextPath() + \"/css/main.css\", \n        portlet.getTimestamp()) \n        %>\" rel=\"stylesheet\" type=\"text/css\" />\n</liferay-util:html-top>\n\\end{verbatim}\n\n  You can learn more about using the \\texttt{liferay-util} taglibs in\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-liferay-util-taglib}{Using\n  the Liferay Util Taglib}.\n\\item\n  This snippet creates the UI to display the layout entities. It uses\n  the\n  \\href{https://docs.liferay.com/dxp/apps/layout/latest/taglibdocs/liferay-layout/layouts-tree.html}{\\texttt{liferay-layout:layouts-tree}}\n  taglib along with the \\href{https://lexicondesign.io/}{Lexicon} design\n  language to create\n  \\href{https://clayui.com/docs/components/cards.html}{cards}:\n\n\\begin{verbatim}\n<div class=\"container-fluid-1280 layouts-selector\">\n    <div class=\"card-horizontal main-content-card\">\n            <div class=\"card-row card-row-padded\">\n                    <liferay-layout:layouts-tree\n                            checkContentDisplayPage=\"<%= layoutItemSelectorCriterion.isCheckDisplayPage() %>\"\n                            draggableTree=\"<%= false %>\"\n                            expandFirstNode=\"<%= true %>\"\n                            groupId=\"<%= scopeGroupId %>\"\n                            portletURL=\"<%= layoutItemSelectorViewDisplayContext.getEditLayoutURL() %>\"\n                            privateLayout=\"<%= layoutItemSelectorViewDisplayContext.isPrivateLayout() %>\"\n                            rootNodeName=\"<%= layoutItemSelectorViewDisplayContext.getRootNodeName() %>\"\n                            saveState=\"<%= false %>\"\n                            selectedLayoutIds=\"<%= layoutItemSelectorViewDisplayContext.getSelectedLayoutIds() %>\"\n                            selPlid=\"<%= layoutItemSelectorViewDisplayContext.getSelPlid() %>\"\n                            treeId=\"treeContainer\"\n                    />\n            </div>\n    </div>\n</div>\n\\end{verbatim}\n\n  This renders the following UI:\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/layouts-item-selector-view.png}\n  \\caption{The Layouts Item Selector view uses Lexicon and Liferay\n  Layout taglibs to create the UI.}\n  \\end{figure}\n\\item\n  This portion of the \\texttt{aui:script} returns the path for the page:\n\n\\begin{verbatim}\n<aui:script use=\"aui-base\">\n    var LString = A.Lang.String;\n\n    var getChosenPagePath = function(node) {\n            var buffer = [];\n\n            if (A.instanceOf(node, A.TreeNode)) {\n                    var labelText = LString.escapeHTML(node.get('labelEl').text());\n\n                    buffer.push(labelText);\n\n                    node.eachParent(\n                            function(treeNode) {\n                                    var labelEl = treeNode.get('labelEl');\n\n                                    if (labelEl) {\n                                            labelText = LString.escapeHTML(labelEl.text());\n\n                                            buffer.unshift(labelText);\n                                    }\n                            }\n                    );\n            }\n\n            return buffer.join(' > ');\n    };\n\\end{verbatim}\n\\item\n  The following snippet passes the return type data when the layout\n  (entity) is selected. Note the \\texttt{url} and \\texttt{uuid}\n  variables retrieve the URL or UUID for the layout:\n\n\\begin{verbatim}\nvar setSelectedPage = function(event) {\n        var disabled = true;\n\n        var messageText = '<%= UnicodeLanguageUtil.get(request, \"there-is-no-selected-page\") %>';\n\n        var lastSelectedNode = event.newVal;\n\n        var labelEl = lastSelectedNode.get('labelEl');\n\n        var link = labelEl.one('a');\n\n        var url = link.attr('data-url');\n        var uuid = link.attr('data-uuid');\n\n        var data = {};\n\n        if (link && url) {\n                disabled = false;\n\n                data.layoutpath = getChosenPagePath(lastSelectedNode);\n\\end{verbatim}\n\\item\n  This checks if the return type information is a URL or a UUID. It then\n  sets the value for the JSON object's \\texttt{data} attribute\n  accordingly. The last line adds the \\texttt{CKEditorFuncNum} for the\n  editor to the JSON object's \\texttt{data} attribute:\n\n\\begin{verbatim}\n        <c:choose>\n                <c:when test=\"<%= Objects.equals(layoutItemSelectorViewDisplayContext.getItemSelectorReturnTypeName(), URLItemSelectorReturnType.class.getName()) %>\">\n                        data.value = url;\n                </c:when>\n                <c:when test=\"<%= Objects.equals(layoutItemSelectorViewDisplayContext.getItemSelectorReturnTypeName(), UUIDItemSelectorReturnType.class.getName()) %>\">\n                        data.value = uuid;\n                </c:when>\n        </c:choose>\n}\n\n<c:if test=\"<%= Validator.isNotNull(layoutItemSelectorViewDisplayContext.getCkEditorFuncNum()) %>\">\n        data.ckeditorfuncnum: <%= layoutItemSelectorViewDisplayContext.getCkEditorFuncNum() %>;\n</c:if>\n\\end{verbatim}\n\n  The \\texttt{data-url} and \\texttt{data-uuid} attributes are in the\n  HTML for the Layouts Item Selector. The HTML for an instance of the\n  Layouts Item Selector is shown here:\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/layouts-item-selector-html.png}\n  \\caption{The URL and UUID can be seen in the \\texttt{data-url} and\n  \\texttt{data-uuid} attributes of the Layout Item Selector's HTML.}\n  \\end{figure}\n\\item\n  The JavaScript trigger event specified in the Item Selector return\n  type is fired, passing the data JSON object with the required return\n  type information:\n\n\\begin{verbatim}\n        Liferay.Util.getOpener().Liferay.fire(\n                '<%= layoutItemSelectorViewDisplayContext.getItemSelectedEventName() %>',\n                {\n                        data: data\n                }\n        );\n};\n\\end{verbatim}\n\\item\n  Finally, the layout is set to the selected page:\n\n\\begin{verbatim}\n    var container = A.one('#<portlet:namespace />treeContainerOutput');\n\n    if (container) {\n            container.swallowEvent('click', true);\n\n            var tree = container.getData('tree-view');\n\n            tree.after('lastSelectedChange', setSelectedPage);\n    }\n</aui:script>\n\\end{verbatim}\n\\end{enumerate}\n\nYour new selection view is automatically rendered by the Item Selector\nin every app that uses the criterion and return types you defined,\nwithout modifying anything in those apps.\n\n\\section{Related Topics}\\label{related-topics-17}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/item-selector}{Item\nSelector}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-criterion-and-return-types}{Creating\nCustom Criterion and Return Types}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/selecting-entities-with-an-item-selector}{Selecting\nEntities with an Item Selector}\n\n\\chapter{Documents and Media API}\\label{documents-and-media-api-1}\n\nA powerful API underlies the\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-documents-and-media}{Documents\nand Media library}. You can leverage this API in your own apps. For\nexample, you could create an app that lets users upload files to the\nDocuments and Media library. Your app could even let users update,\ndelete, and copy files.\n\nHere, you'll learn how to use the Documents and Media library's API.\nNote that this is a large API and it may seem daunting at first. To keep\nbackwards compatibility, the API has different entry points and multiple\nmethods or classes with similar functionality. Fortunately, you don't\nneed to learn all of them. The content here focuses on the API's most\nuseful classes and methods.\n\nAlso note that the Documents and Media app is itself a consumer of this\nAPI---Liferay's developers used the API to implement the app's\nfunctionality. Therefore, code from this app is used as an example of\nhow to use the API.\n\n\\chapter{Getting Started with the Documents and Media\nAPI}\\label{getting-started-with-the-documents-and-media-api}\n\nBefore you start using the Documents and Media API, you must learn these\nthings:\n\n\\hyperref[key-interfaces]{\\textbf{Key Interfaces:}} The interfaces\nyou'll use most while using the API.\n\n\\hyperref[getting-a-service-reference]{\\textbf{Getting a Service\nReference:}} A service reference is required for calling the API's\nservices.\n\n\\hyperref[specifying-repositories]{\\textbf{Specifying Repositories:}}\nHow to specify which Documents and Media repository to work with.\n\n\\hyperref[specifying-folders]{\\textbf{Specifying Folders:}} How to\nspecify which Documents and Media folder to work with.\n\n\\chapter{Key Interfaces}\\label{key-interfaces}\n\nThe Documents and Media API contains several key interfaces:\n\n\\textbf{Documents and Media Services:} These interfaces expose all the\navailable Documents and Media functionality:\n\n\\begin{itemize}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppLocalService.html}{\\texttt{DLAppLocalService}}:\n  The local service.\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{\\texttt{DLAppService}}:\n  The remote service. This service wraps the local service methods in\n  permission checks.\n\n  Note that Liferay used\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\n  Builder} to create these services. Because the remote service contains\n  permission checks, it's a best practice to call it instead of the\n  local service. See below for instructions on getting a service\n  reference.\n\\end{itemize}\n\n\\textbf{Entity Interfaces:} These interfaces represent entities in the\nDocuments and Media library. Here are the primary ones you'll use:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{FileEntry}: Represents a file.\n\\item\n  \\texttt{Folder}: Represents a folder.\n\\item\n  \\texttt{FileShortcut}: Represents a shortcut to a file.\n\\end{itemize}\n\n\\chapter{Getting a Service Reference}\\label{getting-a-service-reference}\n\nBefore you can do anything with the Documents and Media API, you must\nget a service reference. If you're using OSGi modules, use the\n\\texttt{@Reference} annotation to\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{get\na service reference in an OSGi component via Declarative Services}. For\nexample, this code gets a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\nIf you're using a standard web module (WAR file), use a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker}{Service\nTracker} to get a reference to the service instead.\n\nGetting the reference this way ensures that you leverage OSGi's\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{dependency\nmanagement} features. If you must use the Documents and Media services\noutside of an OSGi component (e.g., in a JSP), then you can use the\nservices' static \\texttt{*Util} classes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppServiceUtil.html}{\\texttt{DLAppServiceUtil}}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppLocalServiceUtil.html}{\\texttt{DLAppLocalServiceUtil}}\n\\end{itemize}\n\n\\chapter{Specifying Repositories}\\label{specifying-repositories}\n\nMany methods in the Documents and Media API contain a\n\\texttt{repositoryId} parameter that identifies the Documents and Media\nrepository where the operation is performed. A Site (group) can have\nmultiple repositories, but only one can be accessed via the portal UI.\nThis is called the Site repository, which is effectively a Site's\ndefault repository. To access this repository via the API, provide the\ngroup ID as the \\texttt{repositoryId}.\n\nYou can also get the \\texttt{repositoryId} via file\n(\\texttt{FileEntry}), folder (\\texttt{Folder}), and file shortcut\n(\\texttt{FileShortcut}) entities. Each of these entities has a\n\\texttt{getRepositoryId} method that gets its repository's ID. For\nexample, this code gets the repository ID of the \\texttt{FileEntry}\nobject \\texttt{fileEntry}:\n\n\\begin{verbatim}\nlong repositoryId = fileEntry.getRepositoryId();\n\\end{verbatim}\n\nThere may also be cases that require a \\texttt{Repository} object. You\ncan get one by creating a \\texttt{RepositoryProvider} reference and\npassing the repository ID to its \\texttt{getRepository} method:\n\n\\begin{verbatim}\n@Reference\nprivate RepositoryProvider repositoryProvider;\n\nRepository repository = repositoryProvider.getRepository(repositoryId);\n\\end{verbatim}\n\nEven if you only have an entity ID (e.g., a file or folder ID), you can\nstill use \\texttt{RepositoryProvider} to get a \\texttt{Repository}\nobject. To do so, call the \\texttt{RepositoryProvider} method for the\nentity type with the entity ID as its argument. For example, this code\ngets a folder's \\texttt{Repository} by calling the\n\\texttt{RepositoryProvider} method \\texttt{getFolderRepository} with the\nfolder's ID:\n\n\\begin{verbatim}\nRepository repository = repositoryProvider.getFolderRepository(folderId);\n\\end{verbatim}\n\nSee the \\texttt{RepositoryProvider}\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/repository/RepositoryProvider.html}{Javadoc}\nfor a list of the methods for other entity types.\n\nNote that there are ways to create repositories programmatically,\nincluding repositories private to specific apps. For simplicity,\nhowever, the examples here access the default site repository.\n\n\\chapter{Specifying Folders}\\label{specifying-folders}\n\nMany API methods require the ID of a folder that they perform operations\nin or on. For example, such methods may contain parameters like\n\\texttt{folderId} or \\texttt{parentFolderId}. Also note that you can use\nthe constant \\texttt{DLFolderConstants.DEFAULT\\_PARENT\\_FOLDER\\_ID} to\nspecify the root folder of your current repository.\n\n\\chapter{Creating Files, Folders, and\nShortcuts}\\label{creating-files-folders-and-shortcuts}\n\nA primary use case for the Docs \\& Media API is to create files,\nfolders, and file shortcuts in the Documents and Media library.\n\nIf you've used other Liferay APIs, the Docs \\& Media API follows the\nsame conventions. In general, methods that do similar things have\nsimilar names. When you must create an entity (whatever it is), look for\nmethods that follow the pattern \\texttt{add{[}ModelName{]}}, where\n\\texttt{{[}ModelName{]}} is the name of the entity's data model object.\nAs the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{intro}\nexplains, you'll use\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{\\texttt{DLAppService}}\nto access the API. This service object contains the methods for adding\nthese entities:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[files]{Files}\n\\item\n  \\hyperref[folders]{Folders}\n\\item\n  \\hyperref[file-shortcuts]{File Shortcuts}\n\\end{itemize}\n\n\\chapter{Files}\\label{files}\n\nTo create files (\\texttt{FileEntry} entities) in the Documents and Media\nlibrary, you must use the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{\\texttt{DLAppService}}\ninterface's \\texttt{addFileEntry} methods. There are three such methods,\nand they differ by the data type used to create the file. Click each\nmethod to see a full description of the method and its parameters:\n\n\\begin{itemize}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#addFileEntry-long-long-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-byte:A-com.liferay.portal.kernel.service.ServiceContext-}{\\texttt{addFileEntry(...,\\ byte{[}{]}\\ bytes,\\ ...)}}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#addFileEntry-long-long-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.io.File-com.liferay.portal.kernel.service.ServiceContext-}{\\texttt{addFileEntry(...,\\ File\\ file,\\ ...)}}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#addFileEntry-long-long-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.io.InputStream-long-com.liferay.portal.kernel.service.ServiceContext-}{\\texttt{addFileEntry(...,\\ InputStream\\ is,\\ long\\ size,\\ ...)}}\n\\end{itemize}\n\nNote that the following arguments are optional:\n\n\\texttt{sourceFileName}: This keeps track of the uploaded file. It\ninfers the content type if that file has an extension.\n\n\\texttt{mimeType}: Defaults to a binary stream. If omitted, Documents\nand Media tries to infer the type from the file extension.\n\n\\texttt{description}: The file's description to display in the portal.\n\n\\texttt{changeLog}: Descriptions for file versions.\n\n\\texttt{is} and \\texttt{size}: In the method that takes an\n\\texttt{InputStream}, you can use \\texttt{null} for the \\texttt{is}\nparameter. If you do this, however, you must use \\texttt{0} for the\n\\texttt{size} parameter.\n\nFor step-by-step instructions on creating files with\n\\texttt{addFileEntry}, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-files}{Creating\nFiles}.\n\n\\chapter{Folders}\\label{folders}\n\nTo create folders (\\texttt{Folder} entities) in the Documents and Media\nlibrary, you must use the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{\\texttt{DLAppService}}\ninterface's \\texttt{addFolder} method:\n\n\\begin{verbatim}\naddFolder(long repositoryId, \n        long parentFolderId, \n        String name, \n        String description, \n        ServiceContext serviceContext)\n\\end{verbatim}\n\nSee this method's\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#addFolder-long-long-java.lang.String-java.lang.String-com.liferay.portal.kernel.service.ServiceContext-}{Javadoc}\nfor a description of the parameters. Note that the \\texttt{description}\nparameter is optional.\n\nFor step-by-step instructions on creating folders with\n\\texttt{addFolder}, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-folders}{Creating\nFolders}.\n\n\\section{Folders and External\nRepositories}\\label{folders-and-external-repositories}\n\nBy creating a folder that acts as a proxy for an external repository\n(e.g., SharePoint), you can effectively mount that repository inside a\nSite's default repository. When users enter this special folder, they\nsee the external repository. These folders are called \\emph{mount\npoints}. You can create one via the API by setting the Service Context's\n\\texttt{mountPoint} attribute to \\texttt{true}, and then using that\nService Context in the \\texttt{addFolder} method:\n\n\\begin{verbatim}\nserviceContext.setAttribute(\"mountPoint\", true);\n\\end{verbatim}\n\nNote that the \\texttt{repositoryId} of such a folder indicates the\nexternal repository the folder points to---not the repository the folder\nexists in. Also, mount point folders can only exist in the default Site\nrepository.\n\n\\chapter{File Shortcuts}\\label{file-shortcuts}\n\nTo create file shortcuts (\\texttt{FileShortcut} entities) in the\nDocuments and Media library, you must use the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{\\texttt{DLAppService}}\ninterface's \\texttt{addFileShortcut} method:\n\n\\begin{verbatim}\naddFileShortcut(long repositoryId, \n                long folderId, \n                long toFileEntryId, \n                ServiceContext serviceContext)\n\\end{verbatim}\n\nSee this method's\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#addFileShortcut-long-long-long-com.liferay.portal.kernel.service.ServiceContext-}{Javadoc}\nfor a description of the parameters. Note that all this method's\nparameters are mandatory.\n\nKeep these things in mind when creating shortcuts:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  You can create a shortcut to a file in a different Site, if that file\n  and its resulting shortcut are in the same portal instance.\n\\item\n  You can't create folder shortcuts.\n\\item\n  Shortcuts can only exist in the default Site repository. If you try to\n  invoke \\texttt{addFileShortcut} with an external repository's ID\n  (e.g., a SharePoint repository), the operation fails. Because not all\n  repositories have the same features, the Documents and Media API only\n  supports the common denominators for all repositories: files and\n  folders.\n\\end{itemize}\n\nFor step-by-step instructions on creating file shortcuts with\n\\texttt{addFileShortcut}, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-file-shortcuts}{Creating\nFile Shortcuts}.\n\n\\chapter{Creating Files}\\label{creating-files}\n\nTo create a file via the Documents and Media API, use one of the\noverloaded \\texttt{addFileEntry} methods in\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{\\texttt{DLAppService}}.\nThe steps here show you how to do this, using the method that contains\n\\texttt{InputStream} as an example. For detailed information on this and\nother \\texttt{addFileEntry} methods, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-files-folders-and-shortcuts}{Creating\nFiles, Folders, and Shortcuts}. For general information on using the\nAPI, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to create a file via the Documents and Media API:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the \\texttt{addFileEntry} method's\n  arguments. Since it's common to create a file with data submitted by\n  the end user, you can extract the data from the request. This example\n  does so via\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/upload/UploadPortletRequest.html}{\\texttt{UploadPortletRequest}}\n  and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can get the data any way you wish:\n\n\\begin{verbatim}\nlong repositoryId = ParamUtil.getLong(uploadPortletRequest, \"repositoryId\");\nlong folderId = ParamUtil.getLong(uploadPortletRequest, \"folderId\");\nString sourceFileName = uploadPortletRequest.getFileName(\"file\");\nString title = ParamUtil.getString(uploadPortletRequest, \"title\");\nString description = ParamUtil.getString(uploadPortletRequest, \"description\");\nString changeLog = ParamUtil.getString(uploadPortletRequest, \"changeLog\");\nboolean majorVersion = ParamUtil.getBoolean(uploadPortletRequest, \"majorVersion\");\n\ntry (InputStream inputStream = uploadPortletRequest.getFileAsStream(\"file\")) {\n\n    String contentType = uploadPortletRequest.getContentType(\"file\");\n    long size = uploadPortletRequest.getSize(\"file\");\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            DLFileEntry.class.getName(), uploadPortletRequest);\n}\n\\end{verbatim}\n\\item\n  Call the service reference's \\texttt{addFileEntry} method with the\n  data from the previous step. Note that this example does so inside the\n  previous step's try-with-resources statement:\n\n\\begin{verbatim}\ntry (InputStream inputStream = uploadPortletRequest.getFileAsStream(\"file\")) {\n\n    ...\n\n    FileEntry fileEntry = _dlAppService.addFileEntry(\n                        repositoryId, folderId, sourceFileName, contentType, title, \n                        description, changeLog, inputStream, size, serviceContext);\n}\n\\end{verbatim}\n\n  The method returns a \\texttt{FileEntry} object, which this example\n  sets to a variable for later use. Note, however, that you don't have\n  to do this.\n\\end{enumerate}\n\nYou can find the full code for this example in the\n\\texttt{updateFileEntry} method of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java}{\\texttt{EditFileEntryMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{FileEntry} actions that the Documents and Media app\nsupports. Also note that this \\texttt{updateFileEntry} method, as well\nas the rest of \\texttt{EditFileEntryMVCActionCommand}, contains\nadditional logic to suit the specific needs of the Documents and Media\napp.\n\n\\section{Related Topics}\\label{related-topics-18}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-files}{Updating\nFiles}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-files}{Deleting\nFiles}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files}{Moving\nFolders and Files}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-folders}{Creating\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-file-shortcuts}{Creating\nFile Shortcuts}\n\n\\chapter{Creating Folders}\\label{creating-folders}\n\nTo create folders (\\texttt{Folder} entities) in the Documents and Media\nlibrary, you must use the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{\\texttt{DLAppService}}\ninterface's \\texttt{addFolder} method. The steps here show you how to do\nthis. For more detailed information, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-files-folders-and-shortcuts}{Creating\nFiles, Folders, and Shortcuts}. For general information on using the\nAPI, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to create a folder with the \\texttt{DLAppService}\nmethod \\texttt{addFolder}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the \\texttt{addFolder} method's\n  arguments. Since it's common to create a folder with data submitted by\n  the end user, you can extract the data from the request. This example\n  does so via \\texttt{javax.portlet.ActionRequest} and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}}:\n\n\\begin{verbatim}\nlong repositoryId = ParamUtil.getLong(actionRequest, \"repositoryId\");\nlong parentFolderId = ParamUtil.getLong(actionRequest, \"parentFolderId\");\nString name = ParamUtil.getString(actionRequest, \"name\");\nString description = ParamUtil.getString(actionRequest, \"description\");\n\nServiceContext serviceContext = ServiceContextFactory.getInstance(\n            DLFolder.class.getName(), actionRequest);\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Call the service reference's \\texttt{addFolder} method with the data\n  from the previous step:\n\n\\begin{verbatim}\nFolder folder = _dlAppService.addFolder(\n                        repositoryId, parentFolderId, name, description, \n                        serviceContext);\n\\end{verbatim}\n\n  The method returns a \\texttt{Folder} object, which this example sets\n  to a variable for later use. Note, however, that you don't have to do\n  this.\n\\end{enumerate}\n\nYou can find the full code for this example in the \\texttt{updateFolder}\nmethod of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFolderMVCActionCommand.java}{\\texttt{EditFolderMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{Folder} actions that the Documents and Media app\nsupports. Also note that this \\texttt{updateFolder} method, as well as\nthe rest of \\texttt{EditFolderMVCActionCommand}, contains additional\nlogic to suit the specific needs of the Documents and Media app.\n\n\\section{Related Topics}\\label{related-topics-19}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-folders}{Updating\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-folders}{Deleting\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/copying-folders}{Copying\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files}{Moving\nFolders and Files}\n\n\\chapter{Creating File Shortcuts}\\label{creating-file-shortcuts}\n\nTo create file shortcuts (\\texttt{FileShortcut} entities) in the\nDocuments and Media library, you must use the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{\\texttt{DLAppService}}\ninterface's \\texttt{addFileShortcut} method. The steps here show you how\nto do this. For more detailed information, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-files-folders-and-shortcuts}{Creating\nFiles, Folders, and Shortcuts}. For general information on using the\nAPI, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to create a file shortcut with the\n\\texttt{DLAppService} method \\texttt{addFileShortcut}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the \\texttt{addFileShortcut} method's\n  arguments. Since it's common to create a file shortcut with data\n  submitted by the end user, you can extract the data from the request.\n  This example does so via \\texttt{javax.portlet.ActionRequest} and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can get the data any way you wish:\n\n\\begin{verbatim}\nlong repositoryId = ParamUtil.getLong(actionRequest, \"repositoryId\");\nlong folderId = ParamUtil.getLong(actionRequest, \"folderId\");\nlong toFileEntryId = ParamUtil.getLong(actionRequest, \"toFileEntryId\");\n\nServiceContext serviceContext = ServiceContextFactory.getInstance(\n                    DLFileShortcutConstants.getClassName(), actionRequest);\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Call the service reference's \\texttt{addFileShortcut} method with the\n  data from the previous step:\n\n\\begin{verbatim}\nFileShortcut fileShortcut = _dlAppService.addFileShortcut(\n                                    repositoryId, folderId, toFileEntryId, \n                                    serviceContext);\n\\end{verbatim}\n\n  The method returns a \\texttt{FileShortcut} object, which this example\n  sets to a variable for later use. Note, however, that you don't have\n  to do this.\n\\end{enumerate}\n\nYou can find the full code for this example in the\n\\texttt{updateFileShortcut} method of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileShortcutMVCActionCommand.java}{\\texttt{EditFileShortcutMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{FileShortcut} actions that the Documents and Media app\nsupports. Also note that this \\texttt{updateFileShortcut} method, as\nwell as the rest of \\texttt{EditFileShortcutMVCActionCommand}, contains\nadditional logic to suit the specific needs of the Documents and Media\napp.\n\n\\section{Related Topics}\\label{related-topics-20}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-file-shortcuts}{Deleting\nFile Shortcuts}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-file-shortcuts}{Updating\nFile Shortcuts}\n\n\\chapter{Deleting Entities}\\label{deleting-entities}\n\nYou can delete entities with the Documents and Media API. Note that the\nexact meaning of \\emph{delete} depends on the portal configuration and\nthe delete operation you choose. This is because the\n\\href{/docs/7-2/user/-/knowledge_base/u/restoring-deleted-assets}{Recycle\nBin}, which is enabled by default, can be used to recover deleted items.\nDeletions via\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{\\texttt{DLAppService}},\nhowever, are permanent. To send items to the Recycle Bin, you must use\nthe Capabilities API.\n\nHere, you'll learn about deleting these entities:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[files]{Files}\n\\item\n  \\hyperref[file-versions]{File Versions}\n\\item\n  \\hyperref[file-shortcuts]{File Shortcuts}\n\\item\n  \\hyperref[folders]{Folders}\n\\end{itemize}\n\nYou'll also learn about using the \\hyperref[recycle-bin]{Recycle Bin}.\n\n\\chapter{Files}\\label{files-1}\n\nThere are two \\texttt{DLAppService} methods you can use to delete files:\n\n\\begin{itemize}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#deleteFileEntry-long-}{\\texttt{deleteFileEntry(long\\ fileEntryId)}}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#deleteFileEntryByTitle-long-long-java.lang.String-}{\\texttt{deleteFileEntryByTitle(long\\ repositoryId,\\ long\\ folderId,\\ String\\ title)}}\n\\end{itemize}\n\nThese methods differ only in how they identify a file for deletion. The\ncombination of the \\texttt{folderId} and \\texttt{title} parameters in\n\\texttt{deleteFileEntryByTitle} uniquely identify a file because it's\nimpossible for two files in the same folder to share a name. For\nstep-by-step instructions on using these methods, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-files}{Deleting\nFiles}.\n\n\\chapter{File Versions}\\label{file-versions}\n\nWhen a file is modified, Documents and Media creates a new file version\nand leaves the previous version intact. Over time, old file versions can\naccumulate and consume storage space. Fortunately, you can use the\nDocuments and Media API to delete them. Note, however, that there's no\nway to send file versions to the Recycle Bin---once you delete them,\nthey're gone forever.\n\nYou can delete file versions with the \\texttt{DLAppService} method\n\\texttt{deleteFileVersion}:\n\n\\begin{verbatim}\ndeleteFileVersion(long fileEntryId, String version)\n\\end{verbatim}\n\nSee this method's\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#deleteFileVersion-long-java.lang.String-}{Javadoc}\nfor a description of the parameters. For step-by-step instructions on\nusing this method, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-file-versions}{Deleting\nFile Versions}.\n\n\\section{Identifying File Versions}\\label{identifying-file-versions}\n\nSince there may be many versions of a file, it's useful to\nprogrammatically identify old versions for deletion. You can do this\nwith\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/util/comparator/FileVersionVersionComparator.html}{\\texttt{FileVersionVersionComparator}}.\n\nThe following example creates such a comparator and uses its\n\\texttt{compare} method to identify old file versions. The code does so\nby iterating through each\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow}{approved} version of\nthe file (\\texttt{fileVersion}). Each iteration uses the\n\\texttt{compare} method to test that file version\n(\\texttt{fileVersion.getVersion()}) against the same file's current\nversion (\\texttt{fileEntry.getVersion()}). If this comparison is greater\nthan \\texttt{0}, then the iteration's file version\n(\\texttt{fileVersion}) is old and is deleted by\n\\texttt{deleteFileVersion}:\n\n\\begin{verbatim}\nFileVersionVersionComparator comparator = new FileVersionVersionComparator();\n\nfor (FileVersion fileVersion: fileEntry.getVersions(WorkflowConstants.STATUS_APPROVED)) {\n\n    if (comparator.compare(fileEntry.getVersion(), fileVersion.getVersion()) > 0) {\n\n        dlAppService.deleteFileVersion(fileVersion.getFileEntryId(), fileVersion.getVersion());\n    }\n}\n\\end{verbatim}\n\n\\chapter{File Shortcuts}\\label{file-shortcuts-1}\n\nTo delete file shortcuts, use the \\texttt{DLAppService} method\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#deleteFileShortcut-long-}{\\texttt{deleteFileShortcut}}\nwith the ID of the shortcut you want to delete:\n\n\\begin{verbatim}\ndeleteFileShortcut(long fileShortcutId)\n\\end{verbatim}\n\nFor step-by-step instructions on using this method, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-file-shortcuts}{Deleting\nFile Shortcuts}.\n\n\\chapter{Folders}\\label{folders-1}\n\nDeleting folders is similar to deleting files. There are two methods you\ncan use to delete a folder. Click each method to see its Javadoc:\n\n\\begin{itemize}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#deleteFolder-long-}{\\texttt{deleteFolder(long\\ folderId)}}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#deleteFolder-long-long-java.lang.String-}{\\texttt{deleteFolder(long\\ repositoryId,\\ long\\ parentFolderId,\\ String\\ name)}}\n\\end{itemize}\n\nWhich method you use is up to you---they both delete a folder. For\nstep-by-step instructions on using these methods, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-folders}{Deleting\nFolders}.\n\n\\chapter{Recycle Bin}\\label{recycle-bin}\n\nInstead of deleting entities, you can move them to the\n\\href{/docs/7-2/user/-/knowledge_base/u/restoring-deleted-assets}{Recycle\nBin}. Note that the Recycle Bin isn't part of the Documents and Media\nAPI. Although you can use the Recycle Bin API directly, in the case of\nDocuments and Media it's better to use the Capabilities API. This is\nbecause some third-party repositories (e.g., SharePoint) don't support\nRecycle Bin functionality. The Capabilities API lets you verify that the\nrepository you're working in supports the Recycle Bin. It's therefore a\nbest practice to always use the Capabilities API when moving entities to\nthe Recycle Bin.\n\nFor step-by-step instructions on this, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-entities-to-the-recycle-bin}{Moving\nEntities to the Recycle Bin}.\n\n\\chapter{Deleting Files}\\label{deleting-files}\n\nTo delete a file with the Documents and Media API, you must use one of\nthe two \\texttt{deleteFileEntry*} methods discussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-entities}{Deleting\nEntities}. The steps here show you how. For general information on using\nthe API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to delete a file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the arguments of the\n  \\texttt{deleteFileEntry*} method you wish to use. Since it's common to\n  delete a file specified by the end user, you can extract the data you\n  need from the request. This example does so via\n  \\texttt{javax.portlet.ActionRequest} and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can get the data any way you wish. Also note that this example\n  gets only the file entry ID because it uses \\texttt{deleteFileEntry}:\n\n\\begin{verbatim}\nlong fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\n\\end{verbatim}\n\n  If you want to use \\texttt{deleteFileEntryByTitle} instead, you can\n  also get the repository ID, folder ID, and title from the request.\n\\item\n  Call the service reference's \\texttt{deleteFileEntry*} method you wish\n  to use with the data from the previous step. This example calls\n  \\texttt{deleteFileEntry} with the file entry's ID:\n\n\\begin{verbatim}\n_dlAppService.deleteFileEntry(fileEntryId);\n\\end{verbatim}\n\\end{enumerate}\n\nYou can find the full code for this example in the\n\\texttt{deleteFileEntry} method of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java}{\\texttt{EditFileEntryMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{FileEntry} actions that the Documents and Media app\nsupports. Also note that this \\texttt{deleteFileEntry} method, as well\nas the rest of \\texttt{EditFileEntryMVCActionCommand}, contains\nadditional logic to suit the specific needs of the Documents and Media\napp.\n\n\\section{Related Topics}\\label{related-topics-21}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-entities-to-the-recycle-bin}{Moving\nEntities to the Recycle Bin}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-files}{Creating\nFiles}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-files}{Updating\nFiles}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files}{Moving\nFolders and Files}\n\n\\chapter{Deleting File Versions}\\label{deleting-file-versions}\n\nTo delete a file version with the Documents and Media API, you must use\nthe \\texttt{deleteFileVersion} method discussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-entities}{Deleting\nEntities}. The steps here show you how. For general information on using\nthe API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to use \\texttt{deleteFileVersion} to delete a file\nversion:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the file entry ID and version for the file you want to delete.\n  Since it's common to delete a file version specified by the end user,\n  you can extract these parameters from the request. This example does\n  so via \\texttt{javax.portlet.ActionRequest} and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can do this any way you wish:\n\n\\begin{verbatim}\nlong fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\nString version = ParamUtil.getString(actionRequest, \"version\");\n\\end{verbatim}\n\\item\n  Use the service reference to call the \\texttt{deleteFileVersion}\n  method with the file entry ID and version from the previous step:\n\n\\begin{verbatim}\n_dlAppService.deleteFileVersion(fileEntryId, version);\n\\end{verbatim}\n\\end{enumerate}\n\nYou can find the full code for this example in the\n\\texttt{deleteFileEntry} method of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java}{\\texttt{EditFileEntryMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{FileEntry} actions that the Documents and Media app\nsupports. Also note that this \\texttt{deleteFileEntry} method, as well\nas the rest of \\texttt{EditFileEntryMVCActionCommand}, contains\nadditional logic to suit the specific needs of the Documents and Media\napp.\n\n\\section{Related Topics}\\label{related-topics-22}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-files}{Deleting\nFiles}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-file-shortcuts}{Deleting\nFile Shortcuts}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-folders}{Deleting\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-entities-to-the-recycle-bin}{Moving\nEntities to the Recycle Bin}\n\n\\chapter{Deleting File Shortcuts}\\label{deleting-file-shortcuts}\n\nTo delete a file shortcut with the Documents and Media API, you must use\nthe \\texttt{deleteFileShortcut} method discussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-entities}{Deleting\nEntities}. The steps here show you how. For general information on using\nthe API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to delete a file shortcut:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the file shortcut's ID. Since it's common to delete a file\n  shortcut specified by the end user, you can extract its ID from the\n  request. This example does so via \\texttt{javax.portlet.ActionRequest}\n  and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can do this any way you wish:\n\n\\begin{verbatim}\nlong fileShortcutId = ParamUtil.getLong(actionRequest, \"fileShortcutId\");\n\\end{verbatim}\n\\item\n  Use the service reference to call the \\texttt{deleteFileShortcut}\n  method with the file shortcut ID from the previous step:\n\n\\begin{verbatim}\n_dlAppService.deleteFileShortcut(fileShortcutId);\n\\end{verbatim}\n\\end{enumerate}\n\nYou can find the full code for this example in the\n\\texttt{deleteFileShortcut} method of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileShortcutMVCActionCommand.java}{\\texttt{EditFileShortcutMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{FileShortcut} actions that the Documents and Media app\nsupports. Also note that this \\texttt{deleteFileShortcut} method, as\nwell as the rest of \\texttt{EditFileShortcutMVCActionCommand}, contains\nadditional logic to suit the specific needs of the Documents and Media\napp.\n\n\\section{Related Topics}\\label{related-topics-23}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-entities-to-the-recycle-bin}{Moving\nEntities to the Recycle Bin}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-file-shortcuts}{Creating\nFile Shortcuts}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-file-shortcuts}{Updating\nFile Shortcuts}\n\n\\chapter{Deleting Folders}\\label{deleting-folders}\n\nTo delete a folder with the Documents and Media API, you must use one of\nthe two \\texttt{deleteFolder} methods discussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-entities}{Deleting\nEntities}. The steps here show you how. For general information on using\nthe API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to delete a folder:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the arguments of the\n  \\texttt{deleteFolder} method you wish to use. Since it's common to\n  delete a folder specified by the end user, you can extract the data\n  you need from the request. This example does so via\n  \\texttt{javax.portlet.ActionRequest} and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can get the data any way you wish. Also note that this example\n  gets only the folder ID because the next step deletes the folder with\n  \\texttt{deleteFolder(folderId)}:\n\n\\begin{verbatim}\nlong folderId = ParamUtil.getLong(actionRequest, \"folderId\");\n\\end{verbatim}\n\n  If you want to use the other \\texttt{deleteFolder} method, you can\n  also get the repository ID, parent folder ID, and folder name from the\n  request.\n\\item\n  Call the service reference's \\texttt{deleteFolder} method you wish to\n  use with the data from the previous step. This example calls\n  \\texttt{deleteFolder} with the folder's ID:\n\n\\begin{verbatim}\n_dlAppService.deleteFolder(folderId);\n\\end{verbatim}\n\\end{enumerate}\n\nYou can find the full code for this example in the\n\\texttt{deleteFolders} method of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFolderMVCActionCommand.java}{\\texttt{EditFolderMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{Folder} actions that the Documents and Media app\nsupports. Also note that this \\texttt{deleteFolders} method, as well as\nthe rest of \\texttt{EditFolderMVCActionCommand}, contains additional\nlogic to suit the specific needs of the Documents and Media app.\n\n\\section{Related Topics}\\label{related-topics-24}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-entities-to-the-recycle-bin}{Moving\nEntities to the Recycle Bin}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-folders}{Creating\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-folders}{Updating\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/copying-folders}{Copying\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files}{Moving\nFolders and Files}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-files}{Deleting\nFiles}\n\n\\chapter{Moving Entities to the Recycle\nBin}\\label{moving-entities-to-the-recycle-bin}\n\nFollow these steps to use the Capabilities API to move an entity to the\nRecycle Bin. For an explanation of why you should use the Capabilities\nAPI for this, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-entities}{Deleting\nEntities}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Verify that the repository supports the Recycle Bin. Do this by\n  calling the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api\\#specifying-repositories}{repository\n  object's} \\texttt{isCapabilityProvided} method with\n  \\texttt{TrashCapability.class} as its argument. This example does so\n  in \\texttt{if} statement's condition:\n\n\\begin{verbatim}\nif (repository.isCapabilityProvided(TrashCapability.class)) {\n\n    // The code to move the entity to the Recycle Bin\n    // You'll write this in the next step\n}\n\\end{verbatim}\n\\item\n  Move the entity to the Recycle Bin if the repository supports it. To\n  do this, first get a \\texttt{TrashCapability} reference by calling the\n  repository object's \\texttt{getCapability} method with\n  \\texttt{TrashCapability.class} as its argument. Then call the\n  \\texttt{TrashCapability} method that moves the entity to the Recycle\n  Bin. For example, this code calls \\texttt{moveFileEntryToTrash} to\n  move a file to the Recycle Bin:\n\n\\begin{verbatim}\nif (repository.isCapabilityProvided(TrashCapability.class)) {\n\n    TrashCapability trashCapability = repository.getCapability(TrashCapability.class);\n    trashCapability.moveFileEntryToTrash(user.getUserId(), fileEntry);\n}\n\\end{verbatim}\n\n  See the \\texttt{TrashCapability}\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/repository/capabilities/TrashCapability.html}{Javadoc}\n  for information on the methods you can use to move other types of\n  entities to the Recycle Bin.\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-25}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-files}{Deleting\nFiles}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-folders}{Deleting\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-file-shortcuts}{Deleting\nFile Shortcuts}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files}{Moving\nFolders and Files}\n\n\\chapter{Updating Entities}\\label{updating-entities}\n\nLike\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-files-folders-and-shortcuts}{creating}\nand\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-entities}{deleting}\nentities, updating entities is a key task when working with Documents\nand Media. The methods in the Documents and Media API for creating and\nupdating entities are similar. There are, however, a few important\ndifferences.\n\nHere, you'll learn about updating these entities:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[files]{Files}\n\\item\n  \\hyperref[folders]{Folders}\n\\item\n  \\hyperref[file-shortcuts]{File Shortcuts}\n\\end{itemize}\n\n\\chapter{Files}\\label{files-2}\n\nUpdating a file is a bit more complicated than\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-files}{creating\none}. This is due to the way the update operation handles a file's\nmetadata and content. To update only a file's content, you must also\nsupply the file's existing metadata. Otherwise, the update operation\ncould lose the metadata. The opposite, however, isn't true. You can\nmodify a file's metadata without re-supplying the content. In such an\nupdate, the file's content is automatically copied to the new version of\nthe file. To make this easier to remember, follow these rules when\nupdating files:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Always provide all metadata.\n\\item\n  Only provide the file's content when you want to change it.\n\\end{itemize}\n\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{\\texttt{DLAppService}}\nhas three \\texttt{updateFileEntry} methods that you can use to update a\nfile. These methods differ only in the file content's type. Click each\nmethod to see its Javadoc, which contains a full description of its\nparameters:\n\n\\begin{itemize}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#updateFileEntry-long-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-boolean-byte:A-com.liferay.portal.kernel.service.ServiceContext-}{\\texttt{updateFileEntry(...,\\ byte{[}{]}\\ bytes,\\ ...)}}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#updateFileEntry-long-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-boolean-java.io.File-com.liferay.portal.kernel.service.ServiceContext-}{\\texttt{updateFileEntry(...,\\ File\\ file,\\ ...)}}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#updateFileEntry-long-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-boolean-java.io.InputStream-long-com.liferay.portal.kernel.service.ServiceContext-}{\\texttt{updateFileEntry(...,\\ InputStream\\ is,\\ long\\ size,\\ ...)}}\n\\end{itemize}\n\nKeep these things in mind when using these methods:\n\n\\begin{itemize}\n\\item\n  To retain the original file's title and description, you must provide\n  those parameters to \\texttt{updateFileEntry}. Omitting them deletes\n  any existing title and description.\n\\item\n  If you supply \\texttt{null} in place of the file's content (e.g.,\n  \\texttt{bytes}, \\texttt{file}, or \\texttt{is}), the update\n  automatically uses the file's existing content. Do this only if you\n  want to update the file's metadata.\n\\item\n  If you use \\texttt{false} for the \\texttt{majorVersion} parameter, the\n  update increments the file version by \\texttt{0.1} (e.g., from\n  \\texttt{1.0} to \\texttt{1.1}). If you use \\texttt{true} for this\n  parameter, the update increments the file version to the next\n  \\texttt{.0} value (e.g., from \\texttt{1.0} to \\texttt{2.0},\n  \\texttt{1.1} to \\texttt{2.0}, etc.).\n\\end{itemize}\n\nFor a step-by-step guide on using these \\texttt{updateFileEntry}\nmethods, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-files}{Updating\nFiles}.\n\n\\chapter{Folders}\\label{folders-2}\n\nYou can use the Documents and Media API to\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/copying-and-moving-entities}{copy\nor move} folders to a different location. Options for in-place folder\nupdates, however, are limited. You can only update a folder's name and\ndescription. You can do this with the \\texttt{DLAppService} method\n\\texttt{updateFolder}:\n\n\\begin{verbatim}\nupdateFolder(long folderId, String name, String description, ServiceContext serviceContext)\n\\end{verbatim}\n\nAll parameters except the description are mandatory. For a full\ndescription of this method and its parameters, see its\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#updateFolder-long-java.lang.String-java.lang.String-com.liferay.portal.kernel.service.ServiceContext-}{Javadoc}.\nFor step-by-step instructions on using this method, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-folders}{Updating\nFolders}.\n\n\\chapter{File Shortcuts}\\label{file-shortcuts-2}\n\nYou can update a file shortcut (\\texttt{FileShortcut} entities) to\nchange the file it points to or the folder it resides in. Do this via\nthe \\texttt{DLAppService} method \\texttt{updateFileShortcut}:\n\n\\begin{verbatim}\nupdateFileShortcut(long fileShortcutId, long folderId, long toFileEntryId, ServiceContext serviceContext)\n\\end{verbatim}\n\nAll of this method's parameters are mandatory. To retain any of the\nshortcut's original values, you must provide them to this method. For a\nfull description of the parameters, see the method's\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#updateFileShortcut-long-long-long-com.liferay.portal.kernel.service.ServiceContext-}{Javadoc}.\nFor step-by-step instructions on using this method, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-file-shortcuts}{Updating\nFile Shortcuts}.\n\n\\chapter{Updating Files}\\label{updating-files}\n\nTo update a file with the Documents and Media API, you must use one of\nthe three \\texttt{updateFileEntry} methods discussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-entities}{Updating\nEntities}. The steps here show you how. For general information on using\nthe API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nNote that the example in these steps uses the \\texttt{updateFileEntry}\nmethod that contains \\texttt{InputStream}, but you can adapt the example\nto the other methods if you wish:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the \\texttt{updateFileEntry} method's\n  arguments. Since it's common to update a file with data submitted by\n  the end user, you can extract the data from the request. This example\n  does so via\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/upload/UploadPortletRequest.html}{\\texttt{UploadPortletRequest}}\n  and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can get the data any way you wish:\n\n\\begin{verbatim}\nlong repositoryId = ParamUtil.getLong(uploadPortletRequest, \"repositoryId\");\nlong folderId = ParamUtil.getLong(uploadPortletRequest, \"folderId\");\nString sourceFileName = uploadPortletRequest.getFileName(\"file\");\nString title = ParamUtil.getString(uploadPortletRequest, \"title\");\nString description = ParamUtil.getString(uploadPortletRequest, \"description\");\nString changeLog = ParamUtil.getString(uploadPortletRequest, \"changeLog\");\nboolean majorVersion = ParamUtil.getBoolean(uploadPortletRequest, \"majorVersion\");\n\ntry (InputStream inputStream = uploadPortletRequest.getFileAsStream(\"file\")) {\n\n    String contentType = uploadPortletRequest.getContentType(\"file\");\n    long size = uploadPortletRequest.getSize(\"file\");\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            DLFileEntry.class.getName(), uploadPortletRequest);\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Call the service reference's \\texttt{updateFileEntry} method with the\n  data from the previous step. Note that this example does so inside the\n  previous step's try-with-resources statement:\n\n\\begin{verbatim}\ntry (InputStream inputStream = uploadPortletRequest.getFileAsStream(\"file\")) {\n\n    ...\n\n    FileEntry fileEntry = _dlAppService.updateFileEntry(\n                            fileEntryId, sourceFileName, contentType, title,\n                            description, changeLog, majorVersion, inputStream, size,\n                            serviceContext);\n}\n\\end{verbatim}\n\n  The method returns a \\texttt{FileEntry} object, which this example\n  sets to a variable for later use. Note, however, that you don't have\n  to do this.\n\\end{enumerate}\n\nYou can find the full code for this example in the\n\\texttt{updateFileEntry} method of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java}{\\texttt{EditFileEntryMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{FileEntry} actions that the Documents and Media app\nsupports. Also note that this \\texttt{updateFileEntry} method, as well\nas the rest of \\texttt{EditFileEntryMVCActionCommand}, contains\nadditional logic to suit the specific needs of the Documents and Media\napp.\n\n\\section{Related Topics}\\label{related-topics-26}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-files}{Creating\nFiles}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-files}{Deleting\nFiles}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files}{Moving\nFolders and Files}\n\n\\chapter{Updating Folders}\\label{updating-folders}\n\nTo update a folder with the Documents and Media API, you must use the\n\\texttt{updateFolder} method discussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-entities}{Updating\nEntities}. The steps here show you how. For general information on using\nthe API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to update a folder:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the \\texttt{updateFolder} method's\n  arguments. Since it's common to update a folder with data submitted by\n  the end user, you can extract the data from the request. This example\n  does so via \\texttt{javax.portlet.ActionRequest} and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can get the data any way you wish:\n\n\\begin{verbatim}\nlong folderId = ParamUtil.getLong(actionRequest, \"folderId\");\nString name = ParamUtil.getString(actionRequest, \"name\");\nString description = ParamUtil.getString(actionRequest, \"description\");\n\nServiceContext serviceContext = ServiceContextFactory.getInstance(\n            DLFolder.class.getName(), actionRequest);\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Call the service reference's \\texttt{updateFolder} method with the\n  data from the previous step:\n\n\\begin{verbatim}\n_dlAppService.updateFolder(folderId, name, description, serviceContext);\n\\end{verbatim}\n\\end{enumerate}\n\nYou can find the full code for this example in the \\texttt{updateFolder}\nmethod of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFolderMVCActionCommand.java}{\\texttt{EditFolderMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{Folder} actions that the Documents and Media app\nsupports. Also note that this \\texttt{updateFolder} method, as well as\nthe rest of \\texttt{EditFolderMVCActionCommand}, contains additional\nlogic to suit the specific needs of the Documents and Media app.\n\n\\section{Related Topics}\\label{related-topics-27}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-folders}{Creating\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-folders}{Deleting\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/copying-folders}{Copying\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files}{Moving\nFolders and Files}\n\n\\chapter{Updating File Shortcuts}\\label{updating-file-shortcuts}\n\nTo update a file shortcut with the Documents and Media API, you must use\nthe \\texttt{updateFileShortcut} method discussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-entities}{Updating\nEntities}. The steps here show you how. For general information on using\nthe API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to update a file shortcut:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the \\texttt{updateFileShortcut}\n  method's arguments. Since it's common to update a file shortcut with\n  data submitted by the end user, you can extract the data from the\n  request. This example does so via \\texttt{javax.portlet.ActionRequest}\n  and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can get the data any way you wish:\n\n\\begin{verbatim}\nlong fileShortcutId = ParamUtil.getLong(actionRequest, \"fileShortcutId\");\nlong folderId = ParamUtil.getLong(actionRequest, \"folderId\");\nlong toFileEntryId = ParamUtil.getLong(actionRequest, \"toFileEntryId\");\n\nServiceContext serviceContext = ServiceContextFactory.getInstance(\n            DLFileShortcutConstants.getClassName(), actionRequest);\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Call the service reference's \\texttt{updateFileShortcut} method with\n  the data from the previous step:\n\n\\begin{verbatim}\n_dlAppService.updateFileShortcut(\n        fileShortcutId, folderId, toFileEntryId, serviceContext);\n\\end{verbatim}\n\\end{enumerate}\n\nYou can find the full code for this example in the\n\\texttt{updateFileShortcut} method of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileShortcutMVCActionCommand.java}{\\texttt{EditFileShortcutMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{FileShortcut} actions that the Documents and Media app\nsupports. Also note that this \\texttt{updateFileShortcut} method, as\nwell as the rest of \\texttt{EditFileShortcutMVCActionCommand}, contains\nadditional logic to suit the specific needs of the Documents and Media\napp.\n\n\\section{Related Topics}\\label{related-topics-28}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-file-shortcuts}{Creating\nFile Shortcuts}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-file-shortcuts}{Deleting\nFile Shortcuts}\n\n\\chapter{File Checkout and Checkin}\\label{file-checkout-and-checkin}\n\nUsers can\n\\href{/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files}{check\nout files} from the Document Library for editing. Only the user who\nchecked out the file can edit it. This prevents conflicting edits on the\nsame file from multiple users. The Documents and Media API allows these\ncheckin/checkout operations:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{file-checkout}{File Checkout}\n\\item\n  \\href{file-checkin}{File Checkin}\n\\item\n  \\href{canceling-a-checkout}{Canceling a Checkout}\n\\end{itemize}\n\n\\chapter{File Checkout}\\label{file-checkout}\n\nHere's what happens when you check out a file:\n\n\\begin{itemize}\n\\item\n  A private working copy of the file is created that only you and\n  administrators can access. Until you check the file back in or cancel\n  your changes, any edits you make are stored in the private working\n  copy.\n\\item\n  Other users can't change or edit any version of the file. This state\n  remains until you cancel or check in your changes.\n\\end{itemize}\n\nThe main \\texttt{DLAppService} method for checking out a file is this\n\\texttt{checkOutFileEntry} method:\n\n\\begin{verbatim}\ncheckOutFileEntry(long fileEntryId, ServiceContext serviceContext)\n\\end{verbatim}\n\nIf this method throws an exception, then you should assume the checkout\nfailed and repeat the operation. For a full description of the method\nand its parameters, see its\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#checkOutFileEntry-long-com.liferay.portal.kernel.service.ServiceContext-}{Javadoc}.\nFor step-by-step instructions on using this method, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/checking-out-files}{Checking\nOut Files}.\n\n\\section{Fine-tuning Checkout}\\label{fine-tuning-checkout}\n\nYou can control how the checkout is performed by setting the following\nattributes in the \\texttt{checkOutFileEntry} method's\n\\texttt{ServiceContext} parameter:\n\n\\begin{itemize}\n\\item\n  \\texttt{manualCheckInRequired}: By default, the system automatically\n  checks out/in a file when a user edits it. Setting this attribute to\n  \\texttt{true} prevents this, therefore requiring manual checkout and\n  checkin.\n\\item\n  \\texttt{existingDLFileVersionId}: The system typically reuses the\n  private working copy across different checkout/checkin sequences.\n  There's little chance for conflicting edits because only one user at a\n  time can access the private working copy. To force the system to\n  create a new private working copy each time, omit this attribute or\n  set it to \\texttt{0}.\n\\item\n  \\texttt{fileVersionUuid}: This is used by\n  \\href{/docs/7-2/user/-/knowledge_base/u/staging}{staging}, but can be\n  ignored for normal use. Setting this attribute causes the system to\n  create the new private working copy version with the given UUID.\n\\end{itemize}\n\nTo set these attributes, use the \\texttt{ServiceContext} method\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/service/ServiceContext.html\\#setAttribute-java.lang.String-java.io.Serializable-}{\\texttt{setAttribute(String\\ name,\\ Serializable\\ value)}}.\nHere's an example of setting the \\texttt{manualCheckInRequired}\nattribute to \\texttt{true}:\n\n\\begin{verbatim}\nserviceContext.setAttribute(\"manualCheckInRequired\", Boolean.TRUE)\n\\end{verbatim}\n\n\\chapter{File Checkin}\\label{file-checkin}\n\nAfter checking out and editing a file, you must check it back in for\nother users to see the new version. Once you do so, you can't access the\nprivate working copy. The next time the file is checked out, the private\nworking copy's contents are overwritten.\n\nThe \\texttt{DLAppService} method for checking in a file is\n\\texttt{checkInFileEntry}:\n\n\\begin{verbatim}\ncheckInFileEntry(long fileEntryId, boolean majorVersion, String changeLog, \n                ServiceContext serviceContext)\n\\end{verbatim}\n\nFor a full description of the method and its parameters, see its\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#checkInFileEntry-long-boolean-java.lang.String-com.liferay.portal.kernel.service.ServiceContext-}{Javadoc}.\nThis method uses the private working copy to create a new version of the\nfile. As\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-files}{Updating\nFiles} explains, the \\texttt{majorVersion} parameter's setting\ndetermines how the file's version number is incremented.\n\nFor step-by-step instructions on using this method, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/checking-in-files}{Checking\nIn Files}.\n\n\\chapter{Canceling a Checkout}\\label{canceling-a-checkout}\n\nYou can also cancel a checkout. Use caution with this operation---it\ndiscards any edits made since checkout. If you're sure you want to\ncancel a checkout, do so with the \\texttt{DLAppService} method\n\\texttt{cancelCheckOut}:\n\n\\begin{verbatim}\ncancelCheckOut(long fileEntryId)\n\\end{verbatim}\n\nFor a full description of this method and its parameter, see its\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#cancelCheckOut-long-}{Javadoc}.\nIf you invoke this method without error, you can safely assume that it\ndiscarded the private working copy and unlocked the file. Other users\nshould now be able to check out and edit the file.\n\nFor step-by-step instructions on using this method, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/canceling-a-checkout}{Canceling\na Checkout}.\n\n\\chapter{Checking Out Files}\\label{checking-out-files}\n\nTo check out a file with the Documents and Media API, use the\n\\texttt{checkOutFileEntry} method discussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/file-checkout-and-checkin}{File\nCheckout and Checkin}. The steps here show you how. For general\ninformation on using the API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to check out a file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the \\texttt{checkOutFileEntry}\n  method's arguments. Since it's common to check out a file in response\n  to an action by the end user, you can extract the data from the\n  request. This example does so via \\texttt{javax.portlet.ActionRequest}\n  and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can get the data any way you wish:\n\n\\begin{verbatim}\nlong fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\n\nServiceContext serviceContext = ServiceContextFactory.getInstance(actionRequest);\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Call the service reference's \\texttt{checkOutFileEntry} method with\n  the data from the previous step:\n\n\\begin{verbatim}\n_dlAppService.checkOutFileEntry(fileEntryId, serviceContext);\n\\end{verbatim}\n\\end{enumerate}\n\nYou can find the full code for this example in the\n\\texttt{checkOutFileEntries} method of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java}{\\texttt{EditFileEntryMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{FileEntry} actions that the Documents and Media app\nsupports. Also note that this \\texttt{checkOutFileEntries} method, as\nwell as the rest of \\texttt{EditFileEntryMVCActionCommand}, contains\nadditional logic to suit the specific needs of the Documents and Media\napp.\n\n\\section{Related Topics}\\label{related-topics-29}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/checking-in-files}{Checking\nIn Files}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/canceling-a-checkout}{Canceling\na Checkout}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-files}{Updating\nFiles}\n\n\\chapter{Checking In Files}\\label{checking-in-files}\n\nTo check in a file with the Documents and Media API, use the\n\\texttt{checkInFileEntry} method discussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/file-checkout-and-checkin}{File\nCheckout and Checkin}. The steps here show you how. For general\ninformation on using the API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to use \\texttt{checkInFileEntry} to check in a file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the \\texttt{checkInFileEntry} method's\n  arguments. Since it's common to check in a file in response to an\n  action by the end user, you can extract the data from the request.\n  This example does so via \\texttt{javax.portlet.ActionRequest} and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can get the data any way you wish:\n\n\\begin{verbatim}\nlong fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\nboolean majorVersion = ParamUtil.getBoolean(actionRequest, \"majorVersion\");\nString changeLog = ParamUtil.getString(actionRequest, \"changeLog\");\n\nServiceContext serviceContext = ServiceContextFactory.getInstance(actionRequest);\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Call the service reference's \\texttt{checkInFileEntry} method with the\n  data from the previous step:\n\n\\begin{verbatim}\n_dlAppService.checkInFileEntry(\n        fileEntryId, majorVersion, changeLog, serviceContext);\n\\end{verbatim}\n\\end{enumerate}\n\nYou can find the full code for this example in the\n\\texttt{checkInFileEntries} method of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java}{\\texttt{EditFileEntryMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{FileEntry} actions that the Documents and Media app\nsupports. Also note that this \\texttt{checkInFileEntries} method, as\nwell as the rest of \\texttt{EditFileEntryMVCActionCommand}, contains\nadditional logic to suit the specific needs of the Documents and Media\napp.\n\n\\section{Related Topics}\\label{related-topics-30}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/checking-out-files}{Checking\nOut Files}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/canceling-a-checkout}{Canceling\na Checkout}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-files}{Updating\nFiles}\n\n\\chapter{Canceling a Checkout}\\label{canceling-a-checkout-1}\n\nTo cancel a checkout with the Documents and Media API, use the\n\\texttt{cancelCheckOut} method discussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/file-checkout-and-checkin}{File\nCheckout and Checkin}. The steps here show you how. For general\ninformation on using the API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to cancel a checkout:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the ID of the file whose checkout you want to cancel. Since it's\n  common to cancel a checkout in response to a user action, you can\n  extract the file ID from the request. This example does so via\n  \\texttt{javax.portlet.ActionRequest} and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can get it any way you wish:\n\n\\begin{verbatim}\nlong fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\n\\end{verbatim}\n\\item\n  Call the service reference's \\texttt{cancelCheckOut} method with the\n  file's ID:\n\n\\begin{verbatim}\n_dlAppService.cancelCheckOut(fileEntryId);\n\\end{verbatim}\n\\end{enumerate}\n\nYou can find the full code for this example in the\n\\texttt{cancelFileEntriesCheckOut} method of Liferay DXP's\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java}{\\texttt{EditFileEntryMVCActionCommand}}\nclass. This class uses the Documents and Media API to implement almost\nall the \\texttt{FileEntry} actions that the Documents and Media app\nsupports. Also note that this \\texttt{cancelFileEntriesCheckOut} method,\nas well as the rest of \\texttt{EditFileEntryMVCActionCommand}, contains\nadditional logic to suit the specific needs of the Documents and Media\napp.\n\n\\section{Related Topics}\\label{related-topics-31}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/checking-out-files}{Checking\nOut Files}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/checking-in-files}{Checking\nIn Files}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-files}{Updating\nFiles}\n\n\\chapter{Copying and Moving Entities}\\label{copying-and-moving-entities}\n\nAlthough the Documents and Media API can copy and move entities, these\noperations have some important caveats and limitations. Keep these\nthings in mind when copying entities:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  There's no way to copy files---you can only copy folders. However,\n  copying a folder also copies its contents, which can include files.\n\\item\n  Folders can only be copied within their current repository.\n\\end{itemize}\n\nThe move operation doesn't have these restrictions. It's possible to\nmove files and folders between different repositories. In general,\nhowever, the move operation is a bit more complicated than the copy\noperation. For example, the API's behavior changes depending on whether\nyou move entities to a different repository or within the same one.\n\nHere, you'll learn about the following:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[copying-folders]{Copying Folders}\n\\item\n  \\hyperref[moving-folders-and-files]{Moving Folders and Files}\n\\end{itemize}\n\n\\chapter{Copying Folders}\\label{copying-folders}\n\nThe Documents and Media API can copy folders within a repository. You\ncan't, however, copy a folder between different repositories. Note that\ncopying a folder also copies its contents.\n\nTo copy a folder, use the \\texttt{DLAppService} method\n\\texttt{copyFolder}:\n\n\\begin{verbatim}\ncopyFolder(long repositoryId, long sourceFolderId, long parentFolderId, String name, \n        String description, ServiceContext serviceContext)\n\\end{verbatim}\n\nFor a full description of the method and its parameters, see its\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#copyFolder-long-long-long-java.lang.String-java.lang.String-com.liferay.portal.kernel.service.ServiceContext-}{Javadoc}.\n\nFor step-by-step instructions on using this method, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/copying-folders}{Copying\nFolders}.\n\n\\chapter{Moving Folders and Files}\\label{moving-folders-and-files}\n\nThe move operation is more flexible than the copy operation. Copying\nonly works with folders, and you can't copy between repositories. The\nmove operation, however, works with files and folders within or between\nrepositories.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Depending on the repository implementation, you may get\nunexpected behavior when moving folders between repositories. Moving a\nfolder also moves its contents via separate move operations for each\nitem in the folder. In some repository implementations, if any move\nsub-operation fails, the parent move operation also fails. In other\nrepository implementations, the results of successful sub-operations\nremain even if others fail, which leaves a partially complete move of\nthe whole folder.\n\n\\noindent\\hrulefill\n\nTo move a folder, use the \\texttt{DLAppService} method\n\\texttt{moveFolder}:\n\n\\begin{verbatim}\nmoveFolder(long folderId, long parentFolderId, ServiceContext serviceContext)\n\\end{verbatim}\n\nFor a full description of this method and its parameters, see its\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#moveFolder-long-long-com.liferay.portal.kernel.service.ServiceContext-}{Javadoc}.\nThis method is similar to \\texttt{copyFolder}, but it can't change the\nfolder's name or description, and it can move folders between\nrepositories. Folder contents are moved with the folder.\n\nThe operation for moving a file is almost identical to moving a folder.\nTo move a file, use the \\texttt{DLAppService} method\n\\texttt{moveFileEntry}:\n\n\\begin{verbatim}\nmoveFileEntry(long fileEntryId, long newFolderId, ServiceContext serviceContext)\n\\end{verbatim}\n\nFor a full description of this method and its parameters, see its\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#moveFileEntry-long-long-com.liferay.portal.kernel.service.ServiceContext-}{Javadoc}.\n\nFor step-by-step instructions on using \\texttt{moveFolder} and\n\\texttt{moveFileEntry}, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files}{Moving\nFolders and Files}.\n\n\\chapter{Copying Folders}\\label{copying-folders-1}\n\nTo copy a folder with the Documents and Media API, use the\n\\texttt{copyFolder} method discussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/copying-and-moving-entities}{Copying\nand Moving Entities}. The steps here show you how. For general\ninformation on using the API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to use \\texttt{copyFolder} to copy a folder:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the \\texttt{copyFolder} method's\n  arguments. How you do this depends on your use case. The copy\n  operation in this example takes place in the default Site repository\n  and retains the folder's existing name and description. It therefore\n  needs the folder's group ID (to specify the default site repository),\n  name, and description. Also note that because the destination folder\n  in this example is the repository's root folder, the parent folder ID\n  isn't needed---Liferay DXP supplies a constant for specifying a\n  repository's root folder.\n\n  In the following code,\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}}\n  gets the folder's ID from the request\n  (\\texttt{javax.portlet.ActionRequest}), and the service reference's\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#getFolder-long-}{\\texttt{getFolder}}\n  method gets the corresponding folder object. The folder's\n  \\texttt{getGroupId()}, \\texttt{getName()}, and\n  \\texttt{getDescription()} methods then get the folder's group ID,\n  name, and description, respectively:\n\n\\begin{verbatim}\nlong folderId = ParamUtil.getLong(actionRequest, \"folderId\");\n\nFolder folder = _dlAppService.getFolder(folderId);\nlong groupId = folder.getGroupId();\nString folderName = folder.getName();\nString folderDescription = folder.getDescription();\n\nServiceContext serviceContext = ServiceContextFactory.getInstance(\n            DLFolder.class.getName(), actionRequest);\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Call the service reference's \\texttt{copyFolder} method with the data\n  from the previous step. Note that this example uses the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/model/DLFolderConstants.html}{\\texttt{DLFolderConstants}}\n  constant \\texttt{DEFAULT\\_PARENT\\_FOLDER\\_ID} to specify the\n  repository's root folder as the destination folder:\n\n\\begin{verbatim}\n_dlAppService.copyFolder(\n        groupId, folderId, DLFolderConstants.DEFAULT_PARENT_FOLDER_ID, \n        folderName, folderDescription, serviceContext);\n\\end{verbatim}\n\\end{enumerate}\n\nNote that you can change any of these values to suit your copy\noperation. For example, if your copy takes place in a repository other\nthan the default Site repository, you would specify that repository's ID\nin place of the group ID. You could also specify a different destination\nfolder, and/or change the new folder's name and/or description.\n\n\\section{Related Topics}\\label{related-topics-32}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-folders}{Creating\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-folders}{Updating\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deleting-folders}{Deleting\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files}{Moving\nFolders and Files}\n\n\\chapter{Moving Folders and Files}\\label{moving-folders-and-files-1}\n\nTo move folders and files with the Documents and Media API, use the\n\\texttt{moveFolder} and \\texttt{moveFileEntry} methods discussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/copying-and-moving-entities}{Copying\nand Moving Entities}. The steps here show you how. For general\ninformation on using the API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to use \\texttt{moveFolder} and \\texttt{moveFileEntry}\nto move a folder and a file, respectively. This example does both to\ndemonstrate the procedures:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n    @Reference\n    private DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the method arguments. Since moving\n  folders and files is typically done in response to a user action, you\n  can get the data from the request. This example does so via\n  \\texttt{javax.portlet.ActionRequest} and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}},\n  but you can get the data any way you wish:\n\n\\begin{verbatim}\n// Get the folder IDs\nlong folderId = ParamUtil.getLong(actionRequest, \"folderId\");\nlong newFolderId = ParamUtil.getLong(actionRequest, \"newFolderId\");\n\n// Get the file ID\nlong fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\n\nServiceContext serviceContext = ServiceContextFactory.getInstance(\n        DLFileEntry.class.getName(), actionRequest);\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Call the service reference's method(s). This example calls\n  \\texttt{moveFolder} to move a folder (\\texttt{folderId}) to a\n  different folder (\\texttt{newFolderId}). It then calls\n  \\texttt{moveFileEntry} to move a file (\\texttt{fileEntryId}) to the\n  same destination folder:\n\n\\begin{verbatim}\n_dlAppService.moveFolder(folderId, newFolderId, serviceContext);\n\n_dlAppService.moveFileEntry(fileEntryId, newFolderId, serviceContext);\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-33}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/copying-folders}{Copying\nFolders}\n\n\\chapter{Getting Entities}\\label{getting-entities}\n\nThe Documents and Media API contains many methods for getting entities\nfrom a repository. Most methods in \\texttt{DLAppService} get single\nentities (e.g., a file or folder), a collection of entities that match\ncertain characteristics, or the number of such entities. Because there\nare so many similar methods for getting entities, they aren't all\ndescribed here. You can find full descriptions for all\n\\texttt{DLAppService} methods in its\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{reference\ndocumentation}.\n\nHere, you'll learn about getting these entities:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[files]{Files}\n\\item\n  \\hyperref[folders]{Folders}\n\\item\n  \\hyperref[multiple-entity-types]{Multiple Entity Types}\n\\end{itemize}\n\n\\chapter{Files}\\label{files-3}\n\nGetting files is one of the most common tasks you'll perform with the\nDocuments and Media API. There are two main method families for getting\nfiles:\n\n\\texttt{getFileEntries}: Gets files from a specific repository.\n\n\\texttt{getGroupFileEntries}: Gets files from a Site (group), regardless\nof repository.\n\nSince these method families are common, their methods share many\nparameters:\n\n\\texttt{repositoryId}: The ID of the repository to get files from. To\nspecify the default Site repository, use the \\texttt{groupId} (Site ID).\n\n\\texttt{folderId}: The ID of the folder to get files from. Note that\nthese methods don't traverse the folder structure---they only get files\ndirectly from the specified folder. To specify the repository's root\nfolder, use the constant\n\\texttt{DLFolderConstants.DEFAULT\\_PARENT\\_FOLDER\\_ID}.\n\n\\texttt{start} and \\texttt{end}: Integers that specify the lower and\nupper bounds, respectively, of collection items to include in a page of\nresults. If you don't want to use pagination, use\n\\texttt{QueryUtil.ALL\\_POS} for these parameters.\n\n\\texttt{obc}: The comparator to use to order collection items.\nComparators are\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/OrderByComparator.html}{\\texttt{OrderByComparator}}\nimplementations that sort collection items.\n\n\\texttt{fileEntryTypeId}: The ID of the file type to retrieve. Use this\nto retrieve files of a specific type.\n\n\\texttt{mimeTypes}: The MIME types of the files to retrieve. Use this to\nretrieve files of the specified MIME types. You can specify MIME types\nvia the constants in\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ContentTypes.html}{\\texttt{ContentTypes}}.\n\nNote that the \\texttt{obc} parameter must be an implementation of\n\\texttt{OrderByComparator}. Although you can implement your own\ncomparators, Liferay DXP already contains a few useful implementations\nin the package\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/util/comparator/package-summary.html}{\\texttt{com.liferay.document.library.kernel.util.comparator}}:\n\n\\texttt{RepositoryModelCreateDateComparator}: Sorts by creation date.\n\n\\texttt{RepositoryModelModifiedDateComparator}: Sorts by modification\ndate.\n\n\\texttt{RepositoryModelReadCountComparator}: Sorts by number of views.\n\n\\texttt{RepositoryModelSizeComparator}: Sorts by file size.\n\n\\texttt{RepositoryModelTitleComparator}: Sorts by title.\n\nSee \\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-files}{Getting\nFiles} for step-by-step instructions on using the above method families.\n\n\\chapter{Folders}\\label{folders-3}\n\nThe Documents and Media API can get folders in a similar way to getting\nfiles. The main difference is that folder retrieval methods may have an\nadditional argument to tell the system whether to include \\emph{mount\nfolders}. Mount folders are mount points for external repositories\n(e.g.~Alfresco or SharePoint) that appear as regular folders in a Site's\ndefault repository. They let users navigate seamlessly between\nrepositories. To account for this, some folder retrieval methods include\nthe boolean parameter \\texttt{includeMountFolders}. Setting this\nparameter to \\texttt{true} includes mount folders in the results, while\nomitting it or setting it to \\texttt{false} excludes them.\n\nFor example, to get a list of a parent folder's subfolders from a\nrepository, including any mount folders, use this\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#getFolders-long-long-boolean-}{\\texttt{getFolders}}\nmethod:\n\n\\begin{verbatim}\ngetFolders(long repositoryId, long parentFolderId, boolean includeMountFolders)\n\\end{verbatim}\n\nNote that there are several other \\texttt{getFolders} methods in\n\\texttt{DLAppService}. Use the one that best matches your use case. See\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-folders}{Getting\nFolders} for step-by-step instructions on using these\n\\texttt{getFolders} methods.\n\n\\chapter{Multiple Entity Types}\\label{multiple-entity-types}\n\nThere are also methods in the Documents and Media API that retrieve\nlists containing several entity types. These methods use many of the\nsame parameters as those already described for retrieving files and\nfolders. For example, the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#getFileEntriesAndFileShortcuts-long-long-int-int-int-}{\\texttt{getFileEntriesAndFileShortcuts}}\nmethod gets files and shortcuts from a given repository and folder. Its\n\\texttt{status} parameter specifies a\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow}{workflow} status. As\nbefore, the \\texttt{start} and \\texttt{end} parameters control\npagination of the entities:\n\n\\begin{verbatim}\ngetFileEntriesAndFileShortcuts(long repositoryId, long folderId, int status, int start, int end)\n\\end{verbatim}\n\nFor step-by-step instructions on calling this method and others like it,\nsee\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-multiple-entity-types}{Getting\nMultiple Entity Types}. To see all such methods, see the\n\\texttt{DLAppService}\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{Javadoc}.\n\n\\chapter{Getting Files}\\label{getting-files}\n\nTo get files with the Documents and Media API, use a method from the\n\\texttt{getFileEntries} or \\texttt{getGroupFileEntries} method families\ndiscussed in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-entities}{Getting\nEntities}. The steps here show you how, using this\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#getFileEntries-long-long-java.lang.String:A-int-int-com.liferay.portal.kernel.util.OrderByComparator-}{\\texttt{getFileEntries}}\nmethod as an example:\n\n\\begin{verbatim}\nList<FileEntry> getFileEntries(\n        long repositoryId, \n        long folderId, \n        String[] mimeTypes, \n        int start, \n        int end, \n        OrderByComparator<FileEntry> obc\n)\n\\end{verbatim}\n\nFor general information on using the Documents and Media API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to get a list of files. This example uses the above\n\\texttt{getFileEntries} method to get all the PNG images from the root\nfolder of a Site's default repository, sorted by title:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the method's arguments. You can do\n  this any way you wish. As the next step describes, Liferay DXP\n  provides constants and a comparator for all the arguments this example\n  needs besides the group ID. This example gets the group ID by using\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}}\n  with the request (\\texttt{javax.portlet.ActionRequest}):\n\n\\begin{verbatim}\nlong groupId = ParamUtil.getLong(actionRequest, \"groupId\");\n\\end{verbatim}\n\n  It's also possible to get the group ID via the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/theme/ThemeDisplay.html}{\\texttt{ThemeDisplay}}.\n  Calling the \\texttt{ThemeDisplay} method \\texttt{getScopeGroupId()}\n  gets the ID of your app's current site (group):\n\n\\begin{verbatim}\nThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);\nlong groupId = themeDisplay.getScopeGroupId();\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Use the data from the previous step to call the service reference\n  method you want to use to get the files. This example calls the above\n  \\texttt{getFileEntries} method with the group ID from the previous\n  step, and constants and a comparator for the remaining arguments:\n\n\\begin{verbatim}\nList<FileEntry> fileEntries = \n        _dlAppService.getFileEntries(\n                groupId, \n                DLFolderConstants.DEFAULT_PARENT_FOLDER_ID, \n                new String[] {ContentTypes.IMAGE_PNG}, \n                QueryUtil.ALL_POS, \n                QueryUtil.ALL_POS, \n                new RepositoryModelTitleComparator<>()\n        );\n\\end{verbatim}\n\n  Here's a description of the arguments used in this example:\n\n  \\texttt{groupId}: Using the group ID as the repository ID specifies\n  that the operation takes place in the default site repository.\n\n  \\texttt{DLFolderConstants.DEFAULT\\_PARENT\\_FOLDER\\_ID}: Uses the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/model/DLFolderConstants.html}{\\texttt{DLFolderConstants}}\n  constant \\texttt{DEFAULT\\_PARENT\\_FOLDER\\_ID} to specify the\n  repository's root folder.\n\n  \\texttt{new\\ String{[}{]}\\ \\{ContentTypes.IMAGE\\_PNG\\}}: Uses the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ContentTypes.html}{\\texttt{ContentTypes}}\n  constant \\texttt{IMAGE\\_PNG} to specify PNG images.\n\n  \\texttt{QueryUtil.ALL\\_POS}: Uses the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/QueryUtil.html}{\\texttt{QueryUtil}}\n  constant \\texttt{ALL\\_POS} for the start and end positions in the\n  results. This specifies all results, bypassing pagination.\n\n  \\texttt{new\\ RepositoryModelTitleComparator\\textless{}\\textgreater{}()}:\n  Creates a new\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/util/comparator/RepositoryModelTitleComparator.html}{\\texttt{RepositoryModelTitleComparator}},\n  which sorts the results by title.\n\\end{enumerate}\n\nRemember, this is just one of many \\texttt{getFileEntries} and\n\\texttt{getGroupFileEntries} methods. To see all such methods, see the\n\\texttt{DLAppService}\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{Javadoc}.\n\n\\section{Related Topics}\\label{related-topics-34}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-folders}{Getting\nFolders}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-multiple-entity-types}{Getting\nMultiple Entity Types}\n\n\\chapter{Getting Folders}\\label{getting-folders}\n\nTo get folders with the Documents and Media API, use one of the\n\\texttt{getFolders} methods in \\texttt{DLAppService}. This is discussed\nin more detail in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-entities}{Getting\nEntities}. The steps here show you how to call these \\texttt{getFolders}\nmethods. As an example, this method is used to get a parent folder's\nsubfolders:\n\n\\begin{verbatim}\ngetFolders(long repositoryId, long parentFolderId, boolean includeMountFolders)\n\\end{verbatim}\n\nFor general information on using the Documents and Media API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nFollow these steps to call a \\texttt{getFolders} method:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the method's arguments any way you\n  wish. This \\texttt{getFolders} method needs a repository ID, a parent\n  folder ID, and a boolean value that indicates whether to include mount\n  folders in the results. To specify the default site repository, you\n  can use the group ID as the repository ID. This example gets the group\n  ID from the request (\\texttt{javax.portlet.ActionRequest}) via\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}}:\n\n\\begin{verbatim}\nlong groupId = ParamUtil.getLong(actionRequest, \"groupId\");\n\\end{verbatim}\n\n  It's also possible to get the group ID via the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/theme/ThemeDisplay.html}{\\texttt{ThemeDisplay}}.\n  Calling the \\texttt{ThemeDisplay} method \\texttt{getScopeGroupId()}\n  gets the ID of your app's current Site (group).\n\n\\begin{verbatim}\nThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);\nlong groupId = themeDisplay.getScopeGroupId();\n\\end{verbatim}\n\n  Note that getting the parent folder ID isn't necessary because this\n  example uses the root folder, for which Liferay DXP provides a\n  constant. Also, the boolean value can be provided directly---it\n  doesn't need to be retrieved from somewhere.\n\\item\n  Call the service reference's \\texttt{getFolders} method with the data\n  from the previous step and any other values you want to provide. Note\n  that this example uses\n  \\texttt{DLFolderConstants.DEFAULT\\_PARENT\\_FOLDER\\_ID} to specify the\n  repository's root folder as the parent folder. It also uses\n  \\texttt{true} to include any mount folders in the results:\n\n\\begin{verbatim}\n_dlAppService.getFolders(groupId, DLFolderConstants.DEFAULT_PARENT_FOLDER_ID, true)\n\\end{verbatim}\n\\end{enumerate}\n\nThis is one of many methods you can use to get folders. The rest are\nlisted in the \\texttt{DLAppService}\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{Javadoc}.\n\n\\section{Related Topics}\\label{related-topics-35}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-files}{Getting\nFiles}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-multiple-entity-types}{Getting\nMultiple Entity Types}\n\n\\chapter{Getting Multiple Entity\nTypes}\\label{getting-multiple-entity-types}\n\nThere are several methods in\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html}{\\texttt{DLAppService}}\nthat get lists containing multiple entity types. This is discussed in\nmore detail in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-entities}{Getting\nEntities}. The steps here show you how to use the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html\\#getFileEntriesAndFileShortcuts-long-long-int-int-int-}{\\texttt{getFileEntriesAndFileShortcuts}}\nmethod, but you can apply them to other such methods as well. For\ngeneral information on using the Documents and Media API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api}{Documents\nand Media API}.\n\nNote that the example in these steps gets all the files and shortcuts in\nthe default Site repository's root folder:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to \\texttt{DLAppService}:\n\n\\begin{verbatim}\n@Reference\nprivate DLAppService _dlAppService;\n\\end{verbatim}\n\\item\n  Get the data needed to populate the method's arguments any way you\n  wish. To specify the default Site repository, you can use the group ID\n  as the repository ID. This example gets the group ID from the request\n  (\\texttt{javax.portlet.ActionRequest}) via\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}}:\n\n\\begin{verbatim}\nlong groupId = ParamUtil.getLong(actionRequest, \"groupId\");\n\\end{verbatim}\n\n  Getting the parent folder ID, workflow status, and start and end\n  parameters isn't necessary because Liferay DXP provides constants for\n  them. The next step shows this in detail.\n\\item\n  Call the service reference method with the data from the previous step\n  and any other values you want to provide. This example calls\n  \\texttt{getFileEntriesAndFileShortcuts} with the group ID from the\n  previous step and constants for the remaining arguments:\n\n\\begin{verbatim}\n_dlAppService.getFileEntriesAndFileShortcuts(\n        groupId, \n        DLFolderConstants.DEFAULT_PARENT_FOLDER_ID, \n        WorkflowConstants.STATUS_APPROVED, \n        QueryUtil.ALL_POS, \n        QueryUtil.ALL_POS\n)\n\\end{verbatim}\n\n  Here's a description of the arguments used in this example:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{groupId}: Using the group ID as the repository ID specifies\n    that the operation takes place in the default site repository.\n  \\item\n    \\texttt{DLFolderConstants.DEFAULT\\_PARENT\\_FOLDER\\_ID}: Uses the\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/model/DLFolderConstants.html}{\\texttt{DLFolderConstants}}\n    constant \\texttt{DEFAULT\\_PARENT\\_FOLDER\\_ID} to specify the\n    repository's root folder.\n  \\item\n    \\texttt{WorkflowConstants.STATUS\\_APPROVED}: Uses the\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/workflow/WorkflowConstants.html}{\\texttt{WorkflowConstants}}\n    constant \\texttt{STATUS\\_APPROVED} to specify only files/folders\n    that have been approved via workflow.\n  \\item\n    \\texttt{QueryUtil.ALL\\_POS}: Uses the\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/QueryUtil.html}{\\texttt{QueryUtil}}\n    constant \\texttt{ALL\\_POS} for the start and end positions in the\n    results. This specifies all results, bypassing pagination.\n  \\end{itemize}\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-36}\n\n\\href{/docs/7-1/frameworks/-/knowledge_base/frameworks/getting-files}{Getting\nFiles}\n\n\\href{/docs/7-1/frameworks/-/knowledge_base/frameworks/getting-folders}{Getting\nFolders}\n\n\\chapter{Adaptive Media}\\label{adaptive-media-1}\n\nThe\n\\href{/docs/7-2/user/-/knowledge_base/u/adapting-your-media-across-multiple-devices}{Adaptive\nMedia} app tailors the size and quality of images to the device\ndisplaying them. Here, you'll learn about these things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[the-adaptive-media-taglib]{The Adaptive Media Taglib}\n\\item\n  \\hyperref[adaptive-medias-finder-api]{Adaptive Media's Finder API}\n\\item\n  \\hyperref[image-scaling-in-adaptive-media]{Image Scaling in Adaptive\n  Media}\n\\end{itemize}\n\n\\chapter{The Adaptive Media Taglib}\\label{the-adaptive-media-taglib}\n\nTo display adapted images in your apps, Adaptive Media offers a\nconvenient tag library in the module\n\\href{https://github.com/liferay/com-liferay-adaptive-media/tree/master/adaptive-media-image-taglib}{\\texttt{com.liferay.adaptive.media.image.taglib}}.\nThe only mandatory attribute for the taglib is \\texttt{fileVersion}. It\nindicates the file version of the adapted image to display. The taglib\nuses this file version to query Adaptive Media's finder API and display\nthe adapted image appropriate for the device making the request. You can\nalso add as many attributes as needed, such as \\texttt{class},\n\\texttt{style}, \\texttt{data-sample}, and so on. Any attributes you add\nare then added to the adapted images in the markup the taglib renders.\n\nFor step-by-step instructions on using this taglib, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/displaying-adapted-images-in-your-app}{Displaying\nAdapted Images in Your App}.\n\n\\chapter{Adaptive Media's Finder API}\\label{adaptive-medias-finder-api}\n\nIf you need more control than the taglib offers for finding adapted\nimages, you can query Adaptive Media's finder API directly. For example,\nif you have an app that needs a specific image in a specific dimension,\nit's best to query Adaptive Media's finder API directly. You can then\ndisplay the image however you like (e.g., with an HTML\n\\texttt{\\textless{}img\\textgreater{}} tag).\n\nAdaptive Media's finder API lets you write queries that get adapted\nimages based on certain search criteria and filters. For example, you\ncan get adapted images that match a file version or resolution, or are\nordered by an attribute like image width. You can even get adapted\nimages that match approximate attribute values.\n\n\\section{Calling the API}\\label{calling-the-api}\n\nThe entry point to Adaptive Media's API is\n\\href{https://docs.liferay.com/dxp/apps/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/finder/AMImageFinder.html}{\\texttt{AMImageFinder}}.\nTo use it, you must first inject the OSGi component in your class (which\nmust also be an OSGi component) as follows:\n\n\\begin{verbatim}\n@Reference\nprivate AMImageFinder _amImageFinder;\n\\end{verbatim}\n\nThis makes an \\texttt{AMImageFinder} instance available. It has one\nmethod, \\texttt{getAdaptiveMediaStream}, that returns a stream of\n\\href{https://docs.liferay.com/dxp/apps/adaptive-media/latest/javadocs/com/liferay/adaptive/media/AdaptiveMedia.html}{\\texttt{AdaptiveMedia}}\nobjects. This method takes a \\texttt{Function} that creates an\n\\href{https://docs.liferay.com/dxp/apps/adaptive-media/latest/javadocs/com/liferay/adaptive/media/finder/AMQuery.html}{\\texttt{AMQuery}}\n(the query for adapted images) via\n\\href{https://docs.liferay.com/dxp/apps/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/finder/AMImageQueryBuilder.html}{\\texttt{AMImageQueryBuilder}},\nwhich can search adapted images based on different attributes (e.g.,\nwidth, height, order, etc.). The \\texttt{AMImageQueryBuilder} methods\nyou call depend on the exact query you want to construct.\n\nFor example, here's a general \\texttt{getAdaptiveMediaStream} call:\n\n\\begin{verbatim}\nStream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n    _amImageFinder.getAdaptiveMediaStream(\n        amImageQueryBuilder -> amImageQueryBuilder.methodToCall(arg).done());\n\\end{verbatim}\n\nThe argument to \\texttt{getAdaptiveMediaStream} is a lambda expression\nthat returns an \\texttt{AMQuery} constructed via\n\\texttt{AMImageQueryBuilder}. Note that \\texttt{methodToCall(arg)} is a\nplaceholder for the \\texttt{AMImageQueryBuilder} method you want to call\nand its argument. The exact call depends on the criteria you want to use\nto select adapted images. The \\texttt{done()} call that follows this,\nhowever, isn't a placeholder--it creates and returns the\n\\texttt{AMQuery} regardless of which \\texttt{AMImageQueryBuilder}\nmethods you call.\n\nFor more information on creating \\texttt{AMQuery} instances, see the\n\\texttt{AMImageQueryBuilder}\n\\href{https://docs.liferay.com/dxp/apps/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/finder/AMImageQueryBuilder.html}{Javadoc}.\n\nFor step-by-step instructions on calling Adaptive Media's API, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/finding-adapted-images}{Finding\nAdapted Images}.\n\n\\section{Adaptive Media API\nConstants}\\label{adaptive-media-api-constants}\n\nWhen calling the Adaptive Media API, there are some constants you can\nuse for specifying common attributes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{AMImageAttribute.AM\\_IMAGE\\_ATTRIBUTE\\_WIDTH}: image width\n\\item\n  \\texttt{AMImageAttribute.AM\\_IMAGE\\_ATTRIBUTE\\_HEIGHT}: image height\n\\item\n  \\texttt{AMImageQueryBuilder.SortOrder.ASC}: ascending sort\n\\item\n  \\texttt{AMImageQueryBuilder.SortOrder.DESC}: descending sort\n\\end{itemize}\n\n\\section{Approximate Attributes}\\label{approximate-attributes}\n\nAdaptive Media also lets you get adapted images that match approximate\nattribute values. For example, you can ask for adapted images whose\nheight is around 200px, or whose size is around 100kb. The API returns a\nstream with elements ordered by how close they are to the specified\nattribute. For example, imagine that there are four image resolutions\nthat have adapted images with the heights 150px, 350px, 600px, and\n900px. Searching for adapted images whose height is approximately 400px\nreturns this order in the stream: 350px, 600px, 150px, 900px.\n\nSo how close, exactly, is \\emph{close}? It depends on the attribute. In\nthe case of width, height, and length, a numeric comparison orders the\nimages. In the case of content type, file name, or UUID, the comparison\nis more tricky because these attributes are strings and thus delegated\nto Java's\n\\href{https://docs.oracle.com/javase/8/docs/api/java/lang/String.html\\#compareTo-java.lang.String-}{\\texttt{String.compareTo}}\nmethod.\n\n\\chapter{Image Scaling in Adaptive\nMedia}\\label{image-scaling-in-adaptive-media}\n\nAs described in\n\\href{/docs/7-2/user/-/knowledge_base/u/adapting-your-media-across-multiple-devices}{Adaptive\nMedia's user guide}, Adaptive Media scales images to match the image\nresolutions defined by the Liferay DXP administrator. The default\nscaling is usually suitable, but Adaptive Media contains an extension\npoint that lets you replace the way it scales images. The\n\\href{https://docs.liferay.com/dxp/apps/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/scaler/AMImageScaler.html}{\\texttt{AMImageScaler}}\ninterface defines Adaptive Media's image scaling logic. Out of the box,\nAdaptive Media provides two implementations of this interface:\n\n\\href{https://github.com/liferay/com-liferay-adaptive-media/blob/master/adaptive-media-image-impl/src/main/java/com/liferay/adaptive/media/image/internal/scaler/AMDefaultImageScaler.java}{\\texttt{AMDefaultImageScaler}}:\nThe default image scaler. It's always enabled and uses \\texttt{java.awt}\nfor its image processing and scaling.\n\n\\href{https://github.com/liferay/com-liferay-adaptive-media/blob/master/adaptive-media-image-impl/src/main/java/com/liferay/adaptive/media/image/internal/scaler/AMGIFImageScaler.java}{\\texttt{AMGIFImageScaler}}:\nA scaler that works only with GIF images. It depends on the installation\nof the external tool \\href{https://www.lcdf.org/gifsicle/}{gifsicle} in\nthe Liferay DXP instance. This scaler is disabled by default.\nAdministrators can enable it in \\emph{Control Panel} → \\emph{System\nSettings}.\n\nYou must register image scalers in Liferay DXP's OSGi container using\nthe \\texttt{AMImageScaler} interface. Each scaler must also set the\n\\texttt{mime.type} property to the MIME type it handles. For example, if\nyou set a scaler's MIME type to \\texttt{image/jpeg}, then that scaler\ncan only handle \\texttt{image/jpeg} images. If you specify the special\nMIME type \\texttt{*}, the scaler can process any image. Note that\n\\texttt{AMDefaultImageScaler} is registered using \\texttt{mime.type=*},\nwhile \\texttt{AMGIFImageScaler} is registered using\n\\texttt{mime.type=image/gif}. Both scalers, like all scalers, implement\n\\texttt{AMImageScaler}.\n\nYou can add as many image scalers as you need, even for the same MIME\ntype. However, Adaptive Media uses only one scaler per image, using this\nprocess to determine the best one:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select only the image scalers registered with the same MIME type as\n  the image.\n\\item\n  Select the enabled scalers from those selected in the first step (the\n  \\texttt{AMImageScaler} method \\texttt{isEnabled()} returns\n  \\texttt{true} for enabled scalers).\n\\item\n  Of the scalers selected in the second step, select the one with the\n  highest \\texttt{service.ranking}.\n\\end{enumerate}\n\nIf these steps return no results, they're repeated with the special MIME\ntype \\texttt{*}. Also note that if an image scaler is registered for\nspecific MIME types and has a higher \\texttt{service.ranking}, it's more\nlikely to be chosen than if it's registered for the special MIME type\n\\texttt{*} or has a lower \\texttt{service.ranking}.\n\nFor step-by-step instructions on creating your own image scaler, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-image-scaler}{Creating\nan Image Scaler}.\n\n\\chapter{Displaying Adapted Images in Your\nApp}\\label{displaying-adapted-images-in-your-app}\n\nFollow these steps to display adapted images in your app with the\nAdaptive Media\n\\href{https://github.com/liferay/com-liferay-adaptive-media/tree/master/adaptive-media-image-taglib}{taglib}.\nFor more information, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media\\#the-adaptive-media-taglib}{The\nAdaptive Media Taglib}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Include the taglib dependency in your project. For example, if you're\n  using Gradle you must add the following line in your project's\n  \\texttt{build.gradle} file:\n\n\\begin{verbatim}\nprovided group: \"com.liferay\", name: \"com.liferay.adaptive.media.image.taglib\", version: \"1.0.0\"\n\\end{verbatim}\n\\item\n  Declare the taglib in your JSP:\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://liferay.com/tld/adaptive-media-image\" prefix=\"liferay-adaptive-media\" %>\n\\end{verbatim}\n\\item\n  Use the taglib wherever you want the adapted image to appear in your\n  app's JSP files:\n\n\\begin{verbatim}\n<liferay-adaptive-media:img class=\"img-fluid\" fileVersion=\"<%= fileEntry.getFileVersion() %>\" />\n\\end{verbatim}\n\n  For example, this \\texttt{view.jsp} uses the taglib to display the\n  adapted images in a grid with the \\texttt{col-md-6}\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/layout-templates-intro}{column\n  container class}:\n\n\\begin{verbatim}\n<%@ include file=\"/init.jsp\" %>\n\n<div class=\"container\">\n\n<%\nString[] mimeTypes = {\"image/bmp\", \"image/gif\", \"image/jpeg\", \"image/pjpeg\", \"image/png\", \"image/tiff\", \"image/x-citrix-jpeg\", \"image/x-citrix-png\", \"image/x-ms-bmp\", \"image/x-png\", \"image/x-tiff\"};\n\nList<FileEntry> fileEntries = DLAppServiceUtil.getFileEntries(scopeGroupId, DLFolderConstants.DEFAULT_PARENT_FOLDER_ID, mimeTypes);\n\nint columns = 0;\n\nfor (FileEntry fileEntry : fileEntries) {\n        boolean row = ((columns % 2) == 0);\n%>\n\n        <c:if test=\"<%= row %>\">\n                <c:if test=\"<%= columns != 0 %>\">\n                        </div>\n                </c:if>\n\n                <div class=\"row\">\n        </c:if>\n\n        <div class=\"col-md-6\">\n                <liferay-adaptive-media:img class=\"img-fluid\" fileVersion=\"<%= fileEntry.getFileVersion() %>\" />\n        </div>\n\n        <%\n        columns++;\n}\n%>\n\n</div>\n\\end{verbatim}\n\\end{enumerate}\n\nLooking at the generated markup, you can see that it uses the\n\\texttt{\\textless{}picture\\textgreater{}} tag as described in\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-with-adapted-images}{Creating\nContent with Adapted Images}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adaptive-media-sample.png}\n\\caption{The Adaptive Media Samples app shows all the site's adapted\nimages.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-37}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media}{Adaptive\nMedia}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/finding-adapted-images}{Finding\nAdapted Images}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-image-scaler}{Creating\nan Image Scaler}\n\n\\chapter{Finding Adapted Images}\\label{finding-adapted-images}\n\nIf you need more control than the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/displaying-adapted-images-in-your-app}{Adaptive\nMedia taglib} offers for finding adapted images to display in your app,\nyou can query Adaptive Media's finder API directly. The steps here show\nyou how for these scenarios:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[getting-adapted-images-for-file-versions]{Getting Adapted\n  Images for File Versions}\n\\item\n  \\hyperref[getting-the-adapted-images-for-a-specific-image-resolution]{Getting\n  the Adapted Images for a Specific Image Resolution}\n\\item\n  \\hyperref[getting-adapted-images-in-a-specific-order]{Getting Adapted\n  Images in a Specific Order}\n\\item\n  \\hyperref[using-approximate-attributes]{Using Approximate Attributes}\n\\item\n  \\hyperref[using-the-adaptive-media-stream]{Using the Adaptive Media\n  Stream}\n\\end{itemize}\n\nFor background information on these topics, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media\\#adaptive-medias-finder-api}{Adaptive\nMedia's Finder API}.\n\n\\section{Getting Adapted Images for File\nVersions}\\label{getting-adapted-images-for-file-versions}\n\nFollow these steps to get adapted images for file versions. Note that\nthe method calls here only return adapted images for\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-image-resolutions}{enabled\nimage resolutions}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get an \\texttt{AMImageFinder} reference:\n\n\\begin{verbatim}\n@Reference\nprivate AMImageFinder _amImageFinder;\n\\end{verbatim}\n\\item\n  To get adapted images for a specific file version, call the\n  \\href{https://docs.liferay.com/dxp/apps/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/finder/AMImageQueryBuilder.html}{\\texttt{AMImageQueryBuilder}}\n  method \\texttt{forFileVersion} with a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/repository/model/FileVersion.html}{\\texttt{FileVersion}}\n  object as an argument:\n\n\\begin{verbatim}\nStream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n    _amImageFinder.getAdaptiveMediaStream(\n        amImageQueryBuilder -> amImageQueryBuilder.forFileVersion(fileVersion).done());\n\\end{verbatim}\n\\item\n  To get the adapted images for the latest approved file version, use\n  the \\texttt{forFileEntry} method with a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/repository/model/FileEntry.html}{\\texttt{FileEntry}}\n  object:\n\n\\begin{verbatim}\nStream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n    _amImageFinder.getAdaptiveMediaStream(\n        amImageQueryBuilder -> amImageQueryBuilder.forFileEntry(fileEntry).done());\n\\end{verbatim}\n\\end{enumerate}\n\nTo get adapted images regardless of status (enabled/disabled image\nresolutions), invoke the \\texttt{withConfigurationStatus} method with\nthe constant \\texttt{AMImageQueryBuilder.ConfigurationStatus.ANY}:\n\n\\begin{verbatim}\nStream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n    _amImageFinder.getAdaptiveMediaStream(\n        amImageQueryBuilder -> amImageQueryBuilder.forFileVersion(fileVersion)\n            .withConfigurationStatus(AMImageQueryBuilder.ConfigurationStatus.ANY).done());\n\\end{verbatim}\n\nUse the constant\n\\texttt{AMImageQueryBuilder.ConfigurationStatus.DISABLED} to get adapted\nimages for only disabled image resolutions.\n\n\\section{Getting the Adapted Images for a Specific Image\nResolution}\\label{getting-the-adapted-images-for-a-specific-image-resolution}\n\nBy providing an image resolution's UUID to \\texttt{AMImageFinder}, you\ncan get that resolution's adapted images. This UUID is defined when\n\\href{/docs/7-2/user/-/knowledge_base/u/adding-image-resolutions}{adding\nthe resolution} in the Adaptive Media app. To get a resolution's adapted\nimages, you must pass that resolution's UUID to the\n\\texttt{forConfiguration} method.\n\nFollow these steps to get adapted images for an image resolution:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get an \\texttt{AMImageFinder} reference:\n\n\\begin{verbatim}\n@Reference\nprivate AMImageFinder _amImageFinder;\n\\end{verbatim}\n\\item\n  Call the\n  \\href{https://docs.liferay.com/dxp/apps/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/finder/AMImageQueryBuilder.ConfigurationStep.html}{\\texttt{AMImageQueryBuilder.ConfigurationStep}}\n  method \\texttt{forConfiguration} with the image resolution's UUID. For\n  example, this code gets the adapted images that match a file version,\n  and belong to an image resolution with the UUID\n  \\texttt{hd-resolution}. It returns the adapted images regardless of\n  whether the resolution is enabled or disabled:\n\n\\begin{verbatim}\nStream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n    _amImageFinder.getAdaptiveMediaStream(\n        amImageQueryBuilder -> amImageQueryBuilder.forFileVersion(fileVersion)\n            .forConfiguration(\"hd-resolution\").done());\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Getting Adapted Images in a Specific\nOrder}\\label{getting-adapted-images-in-a-specific-order}\n\nIt's also possible to define the order in which\n\\texttt{getAdaptiveMediaStream} returns adapted images. Follow these\nsteps to do so:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get an \\texttt{AMImageFinder} reference:\n\n\\begin{verbatim}\n@Reference\nprivate AMImageFinder _amImageFinder;\n\\end{verbatim}\n\\item\n  Call the \\texttt{orderBy} method with your sort criteria just before\n  calling the \\texttt{done()} method. The \\texttt{orderBy} method takes\n  two arguments: the first specifies the image attribute to sort by\n  (e.g., width/height), while the second specifies the sort order (e.g.,\n  ascending/descending). The Adaptive Media API provides\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media\\#adaptive-media-api-constants}{constants}\n  that you can use for these arguments.\n\n  For example, this code gets all the adapted images regardless of\n  whether the image resolution is enabled, and puts them in ascending\n  order by image width:\n\n\\begin{verbatim}\nStream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n    _amImageFinder.getAdaptiveMediaStream(\n        amImageQueryBuilder -> amImageQueryBuilder.forFileVersion(_fileVersion)\n            .withConfigurationStatus(AMImageQueryBuilder.ConfigurationStatus.ANY)\n            .orderBy(AMImageAttribute.AM_IMAGE_ATTRIBUTE_WIDTH, AMImageQueryBuilder.SortOrder.ASC)\n            .done());\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Using Approximate\nAttributes}\\label{using-approximate-attributes}\n\nYou can use the API to get adapted images that match approximate\nattribute values. Follow these steps to do so:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get an \\texttt{AMImageFinder} reference:\n\n\\begin{verbatim}\n@Reference\nprivate AMImageFinder _amImageFinder;\n\\end{verbatim}\n\\item\n  Call the \\texttt{with} method with your search criteria just before\n  calling the \\texttt{done()} method. The \\texttt{with} method takes two\n  arguments: the image attribute and that attribute's approximate value.\n  For example, this code gets adapted images whose height is\n  approximately 400px:\n\n\\begin{verbatim}\nStream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n    _amImageFinder.getAdaptiveMediaStream(\n        amImageQueryBuilder -> amImageQueryBuilder.forFileVersion(_fileVersion)\n            .with(AMImageAttribute.AM_IMAGE_ATTRIBUTE_HEIGHT, 400).done());\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Using the Adaptive Media\nStream}\\label{using-the-adaptive-media-stream}\n\nThe Adaptive Media stream flows like a babbling brook through the sands\nof time. Just kidding; it's not like that at all. Once you have the\n\\href{https://docs.liferay.com/dxp/apps/adaptive-media/latest/javadocs/com/liferay/adaptive/media/AdaptiveMedia.html}{\\texttt{AdaptiveMedia}}\nstream, you can get the information you need from it. For example, this\ncode prints the URI for each adapted image:\n\n\\begin{verbatim}\nadaptiveMediaStream.forEach(\n    adaptiveMedia -> {\n        System.out.println(adaptiveMedia.getURI());\n    }\n);\n\\end{verbatim}\n\nYou can also get other values and attributes from the\n\\texttt{AdaptiveMedia} stream. Here are a few examples:\n\n\\begin{verbatim}\n// Get the InputStream \nadaptiveMedia.getInputStream()\n\n// Get the content length\nadaptiveMedia.getValueOptional(AMAttribute.getContentLengthAMAttribute())\n\n// Get the image height\nadaptiveMedia.getValueOptional(AMImageAttribute.AM_IMAGE_ATTRIBUTE_HEIGHT)\n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-38}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media}{Adaptive\nMedia}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/displaying-adapted-images-in-your-app}{Displaying\nAdapted Images in Your App}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-image-scaler}{Creating\nan Image Scaler}\n\n\\chapter{Creating an Image Scaler}\\label{creating-an-image-scaler}\n\nAdaptive Media scales images to match the image resolutions defined by\nthe Liferay DXP administrator. The default scaling is usually suitable,\nbut you can customize it by creating an image scaler. The steps here\nshow you how. For detailed information on these steps, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media\\#image-scaling-in-adaptive-media}{Image\nScaling in Adaptive Media}.\n\nFollow these steps to create a custom image scaler. The example scaler\nin these steps customizes the scaling of PNG images:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Create your scaler class to implement \\texttt{AMImageScaler}. You must\n  also annotate your scaler class with \\texttt{@Component}, setting\n  \\texttt{mime.type} properties for each of the scaler's MIME types, and\n  registering an \\texttt{AMImageScaler} service. If there's more than\n  one scaler for the same MIME type, you must also set the\n  \\texttt{@Component} annotation's \\texttt{service.ranking} property.\n  For your scaler to take precedence over other scalers of the same MIME\n  type, its service ranking property must be higher than that of the\n  other scalers. If \\texttt{service.ranking} isn't set, it defaults to\n  \\texttt{0}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The `service.ranking` property isn't set for the image scalers \n included with Adaptive Media (`AMDefaultImageScaler` and \n `AMGIFImageScaler`). Their service ranking therefore defaults to `0`. To \n replace either scaler, you must set your scaler to the same MIME type and \n give it a service ranking higher than `0`. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nThis example image scaler scales PNG and x-PNG images and has a service \nranking of `100`: \n\n```java\n@Component(\n    immediate = true,\n    property = {\"mime.type=image/png\", \"mime.type=image/x-png\", \"service.ranking:Integer=100\"},\n    service = {AMImageScaler.class}\n)\npublic class SampleAMPNGImageScaler implements AMImageScaler {...\n```\n\nThis requires these imports: \n\n```java\nimport com.liferay.adaptive.media.image.scaler.AMImageScaler;\nimport org.osgi.service.component.annotations.Component;\n```\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Implement the \\texttt{isEnabled()} method to return \\texttt{true} when\n  you want to enable the scaler. In many cases, you always want the\n  scaler enabled, so you can simply return \\texttt{true} in this method.\n  This is the case with the image scaler in this example:\n\n\\begin{verbatim}\n@Override\npublic boolean isEnabled() {\n    return true;\n}\n\\end{verbatim}\n\n  This method gets more interesting when the scaler depends on other\n  tools or features. For example, the \\texttt{isEnabled()} method in\n  \\texttt{AMGIFImageScaler} determines whether gifsicle is enabled. This\n  scaler must only be enabled when the tool it depends on, gifsicle, is\n  also enabled:\n\n\\begin{verbatim}\n@Override\npublic boolean isEnabled() {\n    return _amImageConfiguration.gifsicleEnabled();\n}\n\\end{verbatim}\n\\item\n  Implement the \\texttt{scaleImage} method. This method contains the\n  scaler's business logic and must return an \\texttt{AMImageScaledImage}\n  instance. For example, the \\texttt{scaleImage} implementation in this\n  example uses \\texttt{AMImageConfigurationEntry} to get the maximum\n  height and width values for the scaled image, and \\texttt{FileVersion}\n  to get the image to scale. The scaling is done via a private inner\n  class, assuming that the methods \\texttt{\\_scalePNG},\n  \\texttt{\\_getScalePNGHeight}, \\texttt{\\_getScalePNGWidth}, and\n  \\texttt{\\_getScalePNGSize} implement the actual scaling:\n\n\\begin{verbatim}\n@Override\npublic AMImageScaledImage scaleImage(FileVersion fileVersion,\n    AMImageConfigurationEntry amImageConfigurationEntry) {\n\n    Map<String, String> properties = amImageConfigurationEntry.getProperties();\n\n    int maxHeight = GetterUtil.getInteger(properties.get(\"max-height\"));\n    int maxWidth = GetterUtil.getInteger(properties.get(\"max-width\"));\n\n    try {\n        InputStream inputStream = \n            _scalePNG(fileVersion.getContentStream(false), maxHeight, maxWidth);\n\n        int height = _getScalePNGHeight();\n        int width = _getScalePNGWidth();\n        long size = _getScalePNGSize();\n\n        return new AMImageScaledImageImpl(inputStream, height, width, size);\n    }\n    catch (PortalException pe) {\n        throw new AMRuntimeException.IOException(pe);\n    }\n}\n\nprivate class AMImageScaledImageImpl implements AMImageScaledImage {\n\n    @Override\n    public int getHeight() {\n        return _height;\n    }\n\n    @Override\n    public InputStream getInputStream() {\n        return _inputStream;\n    }\n\n    @Override\n    public long getSize() {\n        return _size;\n    }\n\n    @Override\n    public int getWidth() {\n        return _width;\n    }\n\n    private AMImageScaledImageImpl(InputStream inputStream, int height, \n        int width, long size) {\n\n        _inputStream = inputStream;\n        _height = height;\n        _width = width;\n        _size = size;\n    }\n\n    private final int _height;\n    private final InputStream _inputStream;\n    private final long _size;\n    private final int _width;\n\n}\n\\end{verbatim}\n\n  This requires these imports:\n\n\\begin{verbatim}\nimport com.liferay.adaptive.media.exception.AMRuntimeException;\nimport com.liferay.adaptive.media.image.configuration.AMImageConfigurationEntry;\nimport com.liferay.adaptive.media.image.scaler.AMImageScaledImage;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.repository.model.FileVersion;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport java.io.InputStream;\nimport java.util.Map;\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-39}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media}{Adaptive\nMedia}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/displaying-adapted-images-in-your-app}{Displaying\nAdapted Images in Your App}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/finding-adapted-images}{Finding\nAdapted Images}\n\n\\chapter{Social API}\\label{social-api-1}\n\nYou can use the social API to integrate Liferay DXP's social features\nwith your apps. Here, you'll learn about the following topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[social-bookmarks]{Social Bookmarks}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-comments-to-your-app}{Adding\n  Comments to Your App}\n\\item\n  \\hyperref[ratings]{Ratings}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/flagging-inappropriate-asset-content}{Flagging\n  Inappropriate Asset Content}\n\\end{itemize}\n\n\\chapter{Social Bookmarks}\\label{social-bookmarks}\n\nTo apply social bookmarks to your app's content, you must use the\n\\texttt{liferay-social-bookmarks} taglib. This taglib contains the\n\\texttt{liferay-social-bookmarks:bookmarks} tag, which adds the social\nbookmarks component. This tag contains these attributes:\n\n\\texttt{className}: The entity's class name.\n\n\\texttt{classPK}: The entity's primary key.\n\n\\texttt{displayStyle}: The social bookmarks' display style. Possible\nvalues are \\texttt{inline}, which displays them in a row, and\n\\texttt{menu}, which hides them in a menu.\n\n\\texttt{title}: A title for the content being shared. This attribute is\noften populated by calling the entity's \\texttt{getTitle()} method (or\nother method that retrieves the title).\n\n\\texttt{types}: A comma-delimited list of the social media services to\nuse (e.g., \\texttt{facebook,twitter}). To use every social media service\navailable in the portal, omit this attribute or use\n\\texttt{\\textless{}\\%=\\ null\\ \\%\\textgreater{}} for its value.\n\n\\texttt{url}: A URL to the portal content being shared. The\n\\texttt{PortalUtil} method \\texttt{getCanonicalURL} is often called to\npopulate this attribute. This method constructs an SEO-friendly URL from\nthe page's full URL. For more information, see the method's\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/PortalUtil.html\\#getCanonicalURL-java.lang.String-com.liferay.portal.kernel.theme.ThemeDisplay-com.liferay.portal.kernel.model.Layout-}{Javadoc}.\n\nFor instructions on using this tag, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/applying-social-bookmarks}{Applying\nSocial Bookmarks}. For instructions on creating your own social\nbookmarks, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-social-bookmarks}{Creating\nSocial Bookmarks}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/social-bookmarks-inline.png}\n\\caption{With \\texttt{displayStyle} set to \\texttt{inline}, the first\nthree social bookmarks appear in a row and the rest appear in a menu.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/social-bookmarks-menu.png}\n\\caption{With \\texttt{displayStyle} set to \\texttt{menu}, all social\nbookmarks appear in the \\emph{Share} menu.}\n\\end{figure}\n\n\\chapter{Ratings}\\label{ratings}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{The asset\nframework} supports a content rating system. This feature appears in\nmany of Liferay DXP's built-in apps. For example, users can rate\narticles published in the Blogs app. There are three different rating\ntypes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Likes\n\\item\n  Stars (five, by default)\n\\item\n  Thumbs (up/down)\n\\end{itemize}\n\nTo enable ratings in your app, you must use the\n\\texttt{liferay-ui:ratings} tag and set its \\texttt{type} attribute to\nthe rating type (\\texttt{like}, \\texttt{stars}, or \\texttt{thumbs}). For\ninstructions on this, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/rating-assets}{Rating\nAssets}.\n\n\\section{Rating Type Selection}\\label{rating-type-selection}\n\nAdmins can select the rating type for an app's entities via the Control\nPanel and Site Administration. Portal admins can set the default rating\ntype for the portal, while Site admins can override the default rating\ntype for their Site.\n\nA ratings-enabled app must define its rating type in an OSGi component\nthat implements the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/definition/PortletRatingsDefinition.html}{\\texttt{PortletRatingsDefinition}}\ninterface. This class declares the usage of ratings (specifying the\nportlet and the entity) and the default rating type (that can be\noverridden by portal and site admins). This interface has two methods\nthat you must implement:\n\n\\texttt{getDefaultRatingsType}: Returns the entity's default rating\ntype, which portal and site admins can override. You can do this via the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/RatingsType.html}{\\texttt{RatingsType}}\nenum, which contains \\texttt{LIKE}, \\texttt{STARS}, or \\texttt{THUMBS}.\n\n\\texttt{getPortletId}: Returns the portlet ID of the main portlet that\nuses the entity. You can do this via the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/PortletKeys.html}{\\texttt{PortletKeys}}\nenum, which defines many constants that correspond to the portlet IDs of\nthe built-in portlets.\n\nTo add support for rating type selection in your app, follow the\ninstructions in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/implementing-rating-type-selection}{Implementing\nRating Type Selection}. Once you've done so, you can configure the\ndefault rating type via the Control Panel at \\emph{Configuration} →\n\\emph{Instance Settings} → \\emph{Social}. To override the default values\nfor a site, go to Site Administration (your Site's menu) →\n\\emph{Configuration} → \\emph{Site Settings} → \\emph{Social}.\n\n\\section{Rating Value Transformation}\\label{rating-value-transformation}\n\nThe database stores normalized rating values. This permits switching\nbetween rating types without modifying the underlying data. When\nadministrators change an entity's rating type, its best match is\ncomputed. Here's a list of the default transformations between rating\ntypes:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  When changing from stars to:\n\n  \\textbf{Like:} A value of 3, 4, or 5 stars is considered a like; a\n  value of 1 or 2 stars is omitted.\n\n  \\textbf{Thumbs up/down:} A value of 3, 4, or 5 stars is considered a\n  thumbs up; a value of 1 or 2 stars is considered a thumbs down.\n\\item\n  When changing from thumbs up/down to:\n\n  \\textbf{Like:} A like is considered a thumbs up.\n\n  \\textbf{Stars:} A thumbs down is considered 1 star; a thumbs up is\n  considered 5 stars.\n\\item\n  When changing from like to:\n\n  \\textbf{Stars:} A like is considered 5 stars.\n\n  \\textbf{Thumbs up/down:} A like is considered a thumbs up.\n\\end{enumerate}\n\nThere may be some cases, however, where you want to apply different\ncriteria to determine the new rating values. A mechanism exists that\npermits this, but it modifies the stored rating values. To define such\ntransformations, create an OSGi component that implements\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/transformer/RatingsDataTransformer.html}{\\texttt{RatingsDataTransformer}}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The portal doesn't provide a default\n\\texttt{RatingsDataTransformer} implementation. Unless you provide such\nan implementation, the stored rating values always remain the same while\nthe portal interprets existing values for the selected rating type.\n\n\\noindent\\hrulefill\n\nWhen implementing \\texttt{RatingsDataTransformer}, implement the\n\\texttt{transformRatingsData} method to transform the data. This\nmethod's arguments include the \\texttt{RatingsType} variables\n\\texttt{fromRatingsType} and \\texttt{toRatingsType}, which contain the\nrating type to transform from and to, respectively. These values let you\nwrite your custom transformation's logic. You can write this logic by\nimplementing the interface\n\\texttt{ActionableDynamicQuery.PerformActionMethod} as an anonymous\ninner class in the \\texttt{transformRatingsData} method, implementing\nthe \\texttt{performAction} method with your transformation's logic.\n\nFor instructions on implementing \\texttt{RatingsDataTransformer}, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/customizing-rating-value-transformation}{Customizing\nRating Value Transformation}.\n\n\\chapter{Applying Social Bookmarks}\\label{applying-social-bookmarks}\n\nWhen you enable social bookmarks, icons for sharing on Twitter,\nFacebook, and LinkedIn appear below your app's content. Taglibs provide\nthe markup you need to add this feature to your app.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/social-bookmarks-inline.png}\n\\caption{These social bookmarks are in the inline display style.}\n\\end{figure}\n\nFollow these steps to add social bookmarks to your app:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Make sure your entity is\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{asset\n  enabled}.\n\\item\n  In your project's \\texttt{build.gradle} file, add a dependency to the\n  module\n  \\href{https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.social.bookmarks.taglib/}{\\texttt{com.liferay.social.bookmarks.taglib}}:\n\n\\begin{verbatim}\ncompileOnly group: \"com.liferay\", name: \"com.liferay.social.bookmarks.taglib\", version: \"1.0.0\"\n\\end{verbatim}\n\\item\n  Choose a view in which to show the social bookmarks. For example, you\n  can display them in one of your app's views. However, note that you\n  don't need to implement social bookmarks in your app's\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-asset-renderer}{asset\n  renderers}. The Asset Publisher displays social bookmarks in asset\n  renderers by default.\n\\item\n  In your view's JSP, include the \\texttt{liferay-social-bookmarks}\n  taglib declaration:\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://liferay.com/tld/social-bookmarks\" prefix=\"liferay-social-bookmarks\" %>\n\\end{verbatim}\n\\item\n  Get an instance of your entity. You can do this however you wish. This\n  example uses\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}}\n  to get the entity's ID from the render request, then uses the entity's\n  \\texttt{-LocalServiceUtil} class to create an entity object:\n\n\\begin{verbatim}\n<%\nlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\nentry = EntryLocalServiceUtil.getEntry(entryId);\n%>\n\\end{verbatim}\n\\item\n  Use the \\texttt{liferay-social-bookmarks:bookmarks} tag to add the\n  social bookmarks component. See\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/social-api\\#social-bookmarks}{Social\n  Bookmarks} for information on this tag's attributes. Here's an example\n  of using this tag to add social bookmarks for a blog entry in the\n  Blogs app:\n\n\\begin{verbatim}\n<liferay-social-bookmarks:bookmarks\n        className=\"<%= BlogsEntry.class.getName() %>\"\n        classPK=\"<%= entry.getEntryId() %>\"\n        displayStyle=\"inline\"\n        title=\"<%= entry.getTitle() %>\"\n        types=\"facebook,twitter\"\n        url=\"<%= PortalUtil.getCanonicalURL(bookmarkURL.toString(), themeDisplay, layout) %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-40}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/social-api\\#social-bookmarks}{Social\nBookmarks}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-social-bookmarks}{Creating\nSocial Bookmarks}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{Asset\nFramework}\n\n\\chapter{Creating Social Bookmarks}\\label{creating-social-bookmarks}\n\nBy default, Liferay DXP contains social bookmarks for Twitter, Facebook,\nand LinkedIn. You can also create your own social bookmark by\nregistering a component that implements the\n\\href{https://docs.liferay.com/dxp/apps/social/latest/javadocs/com/liferay/social/bookmarks/SocialBookmark.html}{\\texttt{SocialBookmark}}\ninterface from the module \\texttt{com.liferay.social.bookmarks.api}. The\nsteps here show you how to do this.\n\n\\section{Implementing the SocialBookmark\nInterface}\\label{implementing-the-socialbookmark-interface}\n\nFollow these steps to implement the \\texttt{SocialBookmark} interface:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create your \\texttt{*SocialBookmark} class and register a component\n  that defines the \\texttt{social.bookmarks.type} property. This\n  property's value is what you enter for the\n  \\texttt{liferay-social-bookmarks:bookmarks} tag's \\texttt{type}\n  attribute when you use your social bookmark.\n\n  For example, here's the definition for a Twitter social bookmark\n  class:\n\n\\begin{verbatim}\n@Component(immediate = true, property = \"social.bookmarks.type=twitter\")\npublic class TwitterSocialBookmark implements SocialBookmark {...\n\\end{verbatim}\n\\item\n  Create a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ResourceBundleLoader.html}{\\texttt{ResourceBundleLoader}}\n  reference to help localize the social bookmark's name.\n\n\\begin{verbatim}\n@Reference(\n        target = \"(bundle.symbolic.name=com.liferay.social.bookmark.twitter)\"\n)\nprivate ResourceBundleLoader _resourceBundleLoader;\n\\end{verbatim}\n\\item\n  Implement the \\texttt{getName} method to return the social bookmark's\n  name as a string. This method takes a\n  \\href{https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html}{\\texttt{Locale}}\n  object that you can use for localization via\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/language/LanguageUtil.html}{\\texttt{LanguageUtil}}\n  and\n  \\href{https://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html}{\\texttt{ResourceBundle}}:\n\n\\begin{verbatim}\n@Override\npublic String getName(Locale locale) {\n    ResourceBundle resourceBundle = _resourceBundleLoader.loadResourceBundle(locale);\n\n    return LanguageUtil.get(resourceBundle, \"twitter\");\n}\n\\end{verbatim}\n\\item\n  Implement the \\texttt{getPostURL} method to return the share URL. This\n  method constructs the share URL from a title and URL, and uses\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/URLCodec.html}{\\texttt{URLCodec}}\n  to encode the title in the URL:\n\n\\begin{verbatim}\n@Override\npublic String getPostURL(String title, String url) {\n    return String.format(\n        \"https://twitter.com/intent/tweet?text=%s&tw_p=tweetbutton&url=%s\", \n        URLCodec.encodeURL(title), url);\n}\n\\end{verbatim}\n\\item\n  Create a \\texttt{ServletContext} reference:\n\n\\begin{verbatim}\n@Reference(\n        target = \"(osgi.web.symbolicname=com.liferay.social.bookmark.twitter)\"\n)\nprivate ServletContext _servletContext;\n\\end{verbatim}\n\\item\n  Implement the \\texttt{render} method, which is called when the inline\n  display style is selected. Typically, this method renders a link to\n  the share URL (e.g., a share button), but you can use it for whatever\n  you need. To keep a consistent look and feel with the default social\n  bookmarks, you can use a\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Clay icon}.\n\n  This example gets a \\texttt{RequestDispatcher} for the JSP that\n  contains a Clay icon (\\texttt{page.jsp}), and then includes that JSP\n  in the response:\n\n\\begin{verbatim}\n@Override\npublic void render(\n                String target, String title, String url, HttpServletRequest request,\n                HttpServletResponse response)\n        throws IOException, ServletException {\n\n        RequestDispatcher requestDispatcher =\n                _servletContext.getRequestDispatcher(\"/page.jsp\");\n\n        requestDispatcher.include(request, response);\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Creating Your JSP}\\label{creating-your-jsp}\n\nThe \\texttt{page.jsp} file referenced in the above\n\\texttt{SocialBookmark} implementation uses\n\\href{/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links}{a\nClay link} (\\texttt{clay:link}) to specify and style the Twitter icon\nincluded with Clay. Follow these steps to create a JSP for your own\nsocial bookmark:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the \\texttt{clay} and \\texttt{liferay-theme} taglib declarations:\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://liferay.com/tld/clay\" prefix=\"clay\" %>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\" %>\n\\end{verbatim}\n\\item\n  Import\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/GetterUtil.html}{\\texttt{GetterUtil}}\n  and \\texttt{SocialBookmark}:\n\n\\begin{verbatim}\n<%@ page import=\"com.liferay.portal.kernel.util.GetterUtil\" %>\n<%@ page import=\"com.liferay.social.bookmarks.SocialBookmark\" %>\n\\end{verbatim}\n\\item\n  From the request, get a \\texttt{SocialBookmark} instance and the\n  social bookmark's title and URL:\n\n\\begin{verbatim}\n<%\nSocialBookmark socialBookmark = (SocialBookmark)request.getAttribute(\"liferay-social-bookmarks:bookmark:socialBookmark\");\nString title = GetterUtil.getString((String)request.getAttribute(\"liferay-social-bookmarks:bookmark:title\"));\nString url = GetterUtil.getString((String)request.getAttribute(\"liferay-social-bookmarks:bookmark:url\"));\n%>\n\\end{verbatim}\n\n  The title and URL are set via the \\texttt{liferay-social-bookmarks}\n  taglib when\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/applying-social-bookmarks}{applying\n  the social bookmark}.\n\\item\n  Add the Clay link. See the \\texttt{clay:link}\n  \\href{https://clayui.com/docs/components/link.html}{documentation} for\n  a full description of its attributes.\n\n\\begin{verbatim}\n<clay:link\n        buttonStyle=\"secondary\"\n        elementClasses=\"btn-outline-borderless btn-sm lfr-portal-tooltip\"\n        href=\"<%= socialBookmark.getPostURL(title, url) %>\"\n        icon=\"twitter\"\n        title=\"<%= socialBookmark.getName(locale) %>\"\n/>\n\\end{verbatim}\n\n  This example sets the following \\texttt{clay:link} attributes:\n\n  \\texttt{buttonStyle}: This example renders the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-buttons}{button's\n  type} as a secondary button.\n\n  \\texttt{elementClasses}: The custom CSS to use for styling the button\n  (optional).\n\n  \\texttt{href}: The button's URL. You should specify this by calling\n  your \\texttt{SocialBookmark} instance's \\texttt{getPostURL} method.\n\n  \\texttt{icon}: The button's icon. This example specifies the Twitter\n  icon included in Clay (\\texttt{twitter}).\n\n  \\texttt{title}: The button's title. This example uses the\n  \\texttt{SocialBookmark} instance's \\texttt{getName} method.\n\\end{enumerate}\n\nTo see a complete, real-world example of a social bookmark\nimplementation, see\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/social/social-bookmark-twitter}{Liferay's\nTwitter social bookmark code}.\n\n\\section{Related Topics}\\label{related-topics-41}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/applying-social-bookmarks}{Applying\nSocial Bookmarks}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/using-the-clay-taglib-in-your-portlets}{Using\nthe Clay Taglib in Your Portlets}\n\n\\chapter{Adding Comments to Your App}\\label{adding-comments-to-your-app}\n\nLiferay provides taglibs that enable comments on your app's content.\nHere, you'll learn how to use these taglibs, using a sample Guestbook\napp as an example.\n\nFollow these steps to enable commenting on your app's content:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Make sure your entity is\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{asset\n  enabled}.\n\\item\n  Choose a read-only view of the entity you want to enable comments on.\n  You can display the comments component in your app's view, or if\n  you've\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-asset-renderer}{implemented\n  asset rendering} you can display it in the full content view in the\n  Asset Publisher app.\n\\item\n  Include the \\texttt{liferay-ui}, \\texttt{liferay-comment}, and\n  \\texttt{portlet} taglib declarations in your JSP:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"liferay-ui\" uri=\"http://liferay.com/tld/ui\" %>\n<%@ taglib prefix=\"liferay-comment\" uri=\"http://liferay.com/tld/comment\" %>\n<%@ taglib prefix=\"portlet\" uri=\"http://java.sun.com/portlet_2_0\" %>\n\\end{verbatim}\n\\item\n  Use\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}}\n  to get the entity's ID from the render request. Then create an entity\n  object using the \\texttt{-LocalServiceUtil} class. Here's an example\n  that does this for a guestbook entry in the example Guestbook app:\n\n\\begin{verbatim}\n<%\nlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\nentry = EntryLocalServiceUtil.getEntry(entryId);\n%>\n\\end{verbatim}\n\\item\n  Create a collapsible panel for the comments using the\n  \\texttt{liferay-ui:panel-container} and \\texttt{liferay-ui:panel}\n  tags. This lets users hide the discussion area:\n\n\\begin{verbatim}\n<liferay-ui:panel-container extended=\"<%=false%>\"\n  id=\"guestbookCollaborationPanelContainer\" persistState=\"<%=true%>\">\n  <liferay-ui:panel collapsible=\"<%=true%>\" extended=\"<%=true%>\"\n    id=\"guestbookCollaborationPanel\" persistState=\"<%=true%>\"\n    title=\"Collaboration\">\n\\end{verbatim}\n\\item\n  Create a URL for the discussion using the \\texttt{portlet:actionURL}\n  tag:\n\n\\begin{verbatim}\n<portlet:actionURL name=\"invokeTaglibDiscussion\" var=\"discussionURL\" />\n\\end{verbatim}\n\\item\n  Use the \\texttt{liferay-comment:discussion} tag to add the discussion.\n  To let the user return to the JSP after making a comment, set the\n  tag's \\texttt{redirect} attribute to the current URL. You can use\n  \\texttt{PortalUtil.getCurrentURL((renderRequest))} to get the current\n  URL from the \\texttt{request} object. In this example, the current URL\n  was earlier set to the \\texttt{currentURL} variable:\n\n\\begin{verbatim}\n    <liferay-comment:discussion className=\"<%=Entry.class.getName()%>\"\n      classPK=\"<%=entry.getEntryId()%>\"\n      formAction=\"<%=discussionURL%>\" formName=\"fm2\"\n      ratingsEnabled=\"<%=true%>\" redirect=\"<%=currentURL%>\"\n      userId=\"<%=entry.getUserId()%>\" />\n\n  </liferay-ui:panel>\n</liferay-ui:panel-container>\n\\end{verbatim}\n\\end{enumerate}\n\nIf you haven't already connected your portlet's view to the JSP for your\nentity, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/configuring-jsp-templates-for-an-asset-renderer}{Configuring\nJSP Templates for an Asset Renderer}.\n\n\\section{Related Topics}\\label{related-topics-42}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{Asset\nFramework}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/rating-assets}{Rating\nAssets}\n\n\\chapter{Rating Assets}\\label{rating-assets}\n\nIn only a few lines of code, you can use a taglib to enable ratings for\nyour app's content. The steps here show you how. For more information on\nthis taglib and ratings in general, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/social-api\\#ratings}{Ratings}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/social-ratings-thumbs.png}\n\\caption{Users can rate content to let others know how they really feel\nabout it.}\n\\end{figure}\n\nFollow these steps to enable ratings in your app. Note that these steps\nuse a sample Guestbook app as an example. This app lets users leave\nsimple messages in a guestbook.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Make sure your entity is\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{asset\n  enabled}.\n\\item\n  Choose a read-only view of the entity for which you want to enable\n  ratings. You can display ratings in one of your portlet's views, or if\n  you've\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-asset-renderer}{implemented\n  asset rendering} you can display them in the full content view in the\n  Asset Publisher app.\n\\item\n  In the JSP, include the \\texttt{liferay-ui} taglib declaration:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"liferay-ui\" uri=\"http://liferay.com/tld/ui\" %>\n\\end{verbatim}\n\\item\n  Use\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}}\n  to get the entity's ID from the render request. Then create an entity\n  object using the \\texttt{-LocalServiceUtil} class. Here's an example\n  that does this for a guestbook entry in the example Guestbook app:\n\n\\begin{verbatim}\n<%\nlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\nentry = EntryLocalServiceUtil.getEntry(entryId);\n%>\n\\end{verbatim}\n\\item\n  Use the \\texttt{liferay-ui:ratings} tag to add the ratings component\n  for the entity. This example uses the stars rating type:\n\n\\begin{verbatim}\n<liferay-ui:ratings className=\"<%=Entry.class.getName()%>\"\n    classPK=\"<%=entry.getEntryId()%>\" type=\"stars\" />\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-43}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/social-api\\#ratings}{Ratings}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/implementing-rating-type-selection}{Implementing\nRating Type Selection}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/customizing-rating-value-transformation}{Customizing\nRating Value Transformation}\n\n\\chapter{Implementing Rating Type\nSelection}\\label{implementing-rating-type-selection}\n\nFor administrators to change your app's rating type (e.g.~likes, stars,\nthumbs), you must implement rating type selection. The steps here show\nyou how. For a detailed explanation of these steps and rating type\nselection, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/social-api\\#rating-type-selection}{Rating\nType Selection}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Implement the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/definition/PortletRatingsDefinition.html}{\\texttt{PortletRatingsDefinition}}\n  interface, registering the class as an OSGi component. In the\n  \\texttt{@Component} annotation, set the \\texttt{model.class.name}\n  property to the fully qualified name of the class that will use this\n  rating definition. This example rating definition is for a blog entry,\n  so the \\texttt{model.class.name} property is set to\n  \\texttt{com.liferay.portlet.blogs.model.BlogsEntry}:\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"model.class.name=com.liferay.portlet.blogs.model.BlogsEntry\"\n    }\n)\npublic class BlogsPortletRatingsDefinition implements PortletRatingsDefinition {...\n\\end{verbatim}\n\\item\n  Implement the \\texttt{PortletRatingsDefinition} methods\n  \\texttt{getDefaultRatingsType} and \\texttt{getPortletId} to return the\n  entity's default rating type and the portlet ID of the main portlet\n  that uses the entity, respectively. In this example, the rating type\n  is thumbs and the portlet ID is for the Blogs portlet:\n\n\\begin{verbatim}\n@Override\npublic RatingsType getDefaultRatingsType() {\n    return RatingsType.THUMBS;\n}\n\n@Override\npublic String getPortletId() {\n    return PortletKeys.BLOGS;\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-44}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/social-api\\#rating-type-selection}{Rating\nType Selection}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/rating-assets}{Rating\nAssets}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/customizing-rating-value-transformation}{Customizing\nRating Value Transformation}\n\n\\chapter{Customizing Rating Value\nTransformation}\\label{customizing-rating-value-transformation}\n\nTo customize rating value transformation, you must create an OSGi\ncomponent that implements\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/transformer/RatingsDataTransformer.html}{\\texttt{RatingsDataTransformer}}.\nThe steps here show you how. For a detailed explanation of these steps\nand rating value transformation, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/social-api\\#rating-value-transformation}{Rating\nValue Transformation}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create an OSGi component class that implements\n  \\texttt{RatingsDataTransformer}:\n\n\\begin{verbatim}\n@Component\npublic class DummyRatingsDataTransformer implements RatingsDataTransformer {...\n\\end{verbatim}\n\\item\n  In this class, implement the \\texttt{transformRatingsData} method.\n  Note that it contains the \\texttt{RatingsType} variables\n  \\texttt{fromRatingsType} and \\texttt{toRatingsType}:\n\n\\begin{verbatim}\n@Override\npublic ActionableDynamicQuery.PerformActionMethod transformRatingsData(\n        final RatingsType fromRatingsType, final RatingsType toRatingsType)\n    throws PortalException {\n\n}\n\\end{verbatim}\n\\item\n  In the \\texttt{transformRatingsData} method, implement the interface\n  \\texttt{ActionableDynamicQuery.PerformActionMethod} as an anonymous\n  inner class:\n\n\\begin{verbatim}\nreturn new ActionableDynamicQuery.PerformActionMethod() {\n\n};\n\\end{verbatim}\n\\item\n  In the anonymous \\texttt{ActionableDynamicQuery.PerformActionMethod}\n  implementation, implement the \\texttt{performAction} method to perform\n  your transformation:\n\n\\begin{verbatim}\n@Override\npublic void performAction(Object object)\n    throws PortalException {\n\n    if (fromRatingsType.getValue().equals(RatingsType.LIKE) &&\n        toRatingsType.getValue().equals(RatingsType.STARS)) {\n\n        RatingsEntry ratingsEntry = (RatingsEntry) object;\n\n        ratingsEntry.setScore(0);\n\n        RatingsEntryLocalServiceUtil.updateRatingsEntry(\n            ratingsEntry);\n    }\n}\n\\end{verbatim}\n\n  This example irreversibly transforms the rating type from likes to\n  stars, resetting the value to \\texttt{0}. The \\texttt{if} statement\n  uses the \\texttt{fromRatingsType} and \\texttt{toRatingsType} values to\n  specify that the transformation only occurs when going from likes to\n  stars. The transformation is performed via \\texttt{RatingsEntry} and\n  its \\texttt{-LocalServiceUtil}. After getting a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/model/RatingsEntry.html}{\\texttt{RatingsEntry}}\n  object, its \\texttt{setScore} method sets the rating score to\n  \\texttt{0}. The\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/service/RatingsEntryLocalServiceUtil.html}{\\texttt{RatingsEntryLocalServiceUtil}}\n  method \\texttt{updateRatingsEntry} then updates the\n  \\texttt{RatingsEntry} in the database.\n\\end{enumerate}\n\nHere's the complete class for this example:\n\n\\begin{verbatim}\n@Component\npublic class DummyRatingsDataTransformer implements RatingsDataTransformer {\n    @Override\n    public ActionableDynamicQuery.PerformActionMethod transformRatingsData(\n            final RatingsType fromRatingsType, final RatingsType toRatingsType)\n        throws PortalException {\n\n        return new ActionableDynamicQuery.PerformActionMethod() {\n\n            @Override\n            public void performAction(Object object)\n                throws PortalException {\n\n                if (fromRatingsType.getValue().equals(RatingsType.LIKE) &&\n                    toRatingsType.getValue().equals(RatingsType.STARS)) {\n\n                    RatingsEntry ratingsEntry = (RatingsEntry) object;\n\n                    ratingsEntry.setScore(0);\n\n                    RatingsEntryLocalServiceUtil.updateRatingsEntry(\n                        ratingsEntry);\n                }\n            }\n        };\n    }\n\n}\n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-45}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/social-api\\#rating-value-transformation}{Rating\nValue Transformation}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/implementing-rating-type-selection}{Implementing\nRating Type Selection}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/rating-assets}{Rating\nAssets}\n\n\\chapter{Flagging Inappropriate Asset\nContent}\\label{flagging-inappropriate-asset-content}\n\nThe asset framework supports a system for flagging inappropriate content\nin apps. The steps here show you how to enable it in your app.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/social-flags.png}\n\\caption{Users can flag objectionable content.}\n\\end{figure}\n\nFollow these steps to enable content flagging in your app:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Make sure your entity is\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{asset\n  enabled}.\n\\item\n  Choose a read-only view of the entity you want to enable flags on. You\n  can display flags in one of your app's views, or if you've\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-asset-renderer}{implemented\n  asset rendering} you can display it in the full content view in the\n  Asset Publisher app.\n\\item\n  In your JSP, include the \\texttt{liferay-flags} taglib declaration:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"liferay-flags\" uri=\"http://liferay.com/tld/flags\" %>\n\\end{verbatim}\n\\item\n  Use\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html}{\\texttt{ParamUtil}}\n  to get the entity's ID from the render request. Then use your\n  \\texttt{-LocalServiceUtil} class to create an entity object:\n\n\\begin{verbatim}\n<%\nlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\nentry = EntryLocalServiceUtil.getEntry(entryId);\n%>\n\\end{verbatim}\n\\item\n  Use the\n  \\href{https://docs.liferay.com/dxp/apps/collaboration/latest/taglibdocs/liferay-flags/flags.html}{\\texttt{liferay-flags:flags}}\n  tag to add the flags component:\n\n\\begin{verbatim}\n<liferay-flags:flags\n    className=\"<%= Entry.class.getName() %>\"\n    classPK=\"<%= entry.getEntryId() %>\"\n    contentTitle=\"<%= title %>\"\n    message=\"flag-this-content\"\n    reportedUserId=\"<%= reportedUserId %>\"\n/>\n\\end{verbatim}\n\n  The \\texttt{reportedUserId} attribute specifies the ID of the user who\n  flagged the asset.\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-46}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/rating-assets}{Rating\nAssets}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/social-api}{Social API}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{Asset\nFramework}\n\n\\chapter{Configurable Applications}\\label{configurable-applications}\n\nMany applications must be configurable, whether by end users or\nadministrators. A configuration solution must support use cases ranging\nfrom setting a location for a weather display to more complex cases like\nsettings for a mail or time sheet application.\n\nThe Portlet standard's portlet preferences API can be used for portlet\nconfiguration, but it's intended for storing user preferences. This\nlimits its usefulness for enabling administrator configuration; plus it\ncan only be used with portlets. Instead, application developers tend to\ncreate ad hoc configuration methods. But this isn't necessary.\n\nLiferay DXP's configuration API is easy to use and is not limited to\nportlets. When you define configuration options in a Java interface,\nLiferay's configuration framework auto-generates a UI, sparing you the\ntrouble of developing an interface for your users to select\nconfiguration options.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To see a working application configuration, deploy the\n\\texttt{configuration-action}\n\\href{https://github.com/liferay/liferay-blade-samples/tree/master/gradle/apps/configuration-action}{Blade\nsample} and navigate to System Settings (\\emph{Control Panel} →\n\\emph{Configuration} → \\emph{System Settings}). In the Platorm section's\nThird Party category, click the \\emph{Message display configuration}\nentry.\n\nAdd the \\emph{Blade Message Portlet} to a page to test your\nconfiguration choices.\n\n\\noindent\\hrulefill\n\nComplete these three high level tasks to integrate your application with\nthe configuration framework:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Provide a way to set configurations in the UI.\n\\item\n  Set the scope where the application is configured.\n\\item\n  Read configuration values in your business logic.\n\\end{enumerate}\n\n\\section{Using a Configuration\nInterface}\\label{using-a-configuration-interface}\n\nYou can take care of the first two steps by\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-configuration-interface}{Creating\nA Configuration Interface}. This Java interface does a number of things:\n\n\\begin{itemize}\n\\item\n  Just by existing, it gives you a UI in \\emph{System Settings}, so you\n  don't have to write one yourself. Score!\n\\item\n  It defines the configuration options that will appear in the UI.\n\\item\n  It defines the type \\{\\texttt{int}, \\texttt{String}, etc.) of values\n  each configuration takes.\n\\item\n  It defines the scope of your configuration. Bonus in 7.0: if your\n  configuration is scoped to anything other than \\texttt{SYSTEM}, you\n  get an additional UI generated for you in \\emph{Instance Settings}.\n  More on scope in a minute.\n\\item\n  It categorizes your configuration screen so that it can be easily\n  found in \\emph{System Settings} and \\emph{Instance Settings}. If you\n  skip this the screen will be put in a default location.\n\\end{itemize}\n\nA few things you need to know:\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Typed Configuration}]\nThe method described here uses \\emph{typed} configuration. The\napplication configuration isn't just a list of key-value pairs. Values\ncan have types, like \\texttt{Integer}, a list of \\texttt{Strings}, a\nURL, etc. You can even use your own types, although that's beyond the\nscope of this tutorial. Typed configurations are easier to use than\nuntyped configurations, and they prevent many programmatic errors.\nConfiguration options should be programmatically explicit, so developers\ncan use autocomplete in modern IDEs to find out all configuration\noptions of a given application or one of its components.\n\\item[\\textbf{Configuration Scope}]\nScope defines where a configuration value applies. Here are the most\ncommon configuration scopes:\n\\end{description}\n\n\\begin{itemize}\n\\item\n  \\texttt{SYSTEM}: Configuration values apply throughout the system.\n\\item\n  \\texttt{COMPANY}: One set of configuration values is stored for each\n  virtual instance, so each instance can be configured individually.\n\\item\n  \\texttt{GROUP}: Each group can be configured individually.\n\\item\n  \\texttt{PORTLET\\_INSTANCE}: this refers to apps that can be placed on\n  a page as a widget. Each widget can be configured individually.\n\\end{itemize}\n\n\\textbf{Configuration UIs} : When you create a configuration interface\nof any sort, a UI is generated for you in \\emph{System Settings}. If\nyour configuration is scoped to \\texttt{COMPANY}, \\texttt{GROUP}, or\n\\texttt{PORTLET\\_INSTANCE}, an additional UI is generated in\n\\emph{Instance Settings}. Note that while \\texttt{GROUP} and\n\\texttt{PORTLET\\_INSTANCE} configurations appear in the Instance\nSettings UI, they can only be used to set defaults for the current\ninstance. No corresponding UI is auto-generated to configure the app at\nthe Site or Portlet level.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} An Instance Settings UI is not currently generated for\nfactory configurations. You can track the progress of this issue\n\\href{https://issues.liferay.com/browse/LPS-94490}{here}.\n\n\\noindent\\hrulefill\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Default Configurations}]\nDefault values for any scoped configuration can be set at any wider\nscope. For example, if your configuration is scoped to the\n\\texttt{GROUP}, you can set a system-wide default in \\emph{System\nSettings, an instance-wide default in }Instance Settings*, or both. Any\nconfiguration at a narrower scope will always override a configuration\nat a wider scope.\n\\end{description}\n\nRead more about configuration scope\n\\href{/docs/7-2/user/-/knowledge_base/u/system-settings\\#configuration-scope}{here}.\n\nWhen you complete your configuration interface, you're done with steps 1\nand 2 above.\n\n\\section{Reading Configuration\nValues}\\label{reading-configuration-values}\n\nThe final step is to make your app read the configuration values that\nusers enter. There are a number of ways to do that:\n\nIf your configuration is scoped to \\texttt{COMPANY} or \\texttt{GROUP}\nyou must use\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/reading-scoped-configuration-values}{\\texttt{ConfigurationProvider}}\nThis allows your app to read different configuration values from each\nsite, virtual instance, or whatever the configuration is scoped to.\n\nIf your configuration is scoped to \\texttt{PORTLET\\_INSTANCE}, you can\nstill use \\texttt{ConfigurationProvider}, but using\n\\texttt{PortletDisplay} is simpler and more convenient. See\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/reading-scoped-configuration-values\\#accessing-the-portlet-instance-configuration-through-the-portletdisplay}{\\texttt{PortletDisplay}}.\n\nIf you only want your app to be configurable at the \\texttt{SYSTEM}\nscope, you have a few options. \\texttt{ConfigurationProvider} will work\nfine, but there are alternatives that---since they don't need to query\nmultiple sources---can yield modest performance benefits. Which one you\nuse depends on what kind of class you're using to read configuration\nvalues. Here are your options:\n\n\\begin{itemize}\n\\item\n  Read with an\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/reading-unscoped-configuration-values-from-an-mvc-portlet\\#accessing-the-configuration-from-a-jsp}{MVC\n  portlet's JSP}\n\\item\n  With an\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/reading-unscoped-configuration-values-from-an-mvc-portlet\\#accessing-the-configuration-from-the-portlet-class}{MVC\n  Portlet's Portlet Class}\n\\item\n  With any other\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/reading-unscoped-configuration-values-from-a-component}{Component\n  Class}\n\\end{itemize}\n\n\\section{Further Customization}\\label{further-customization}\n\nAt this point you may be asking, ``But what if I don't \\emph{like} the\nauto-generated UI?'' Relax. There are a number of ways you can customize\nit, or even suppress it entirely so you can put your own UI in its\nplace.\n\n\\begin{itemize}\n\\item\n  Implement the \\texttt{ConfigurationFormRenderer}\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configuration-form-renderer}{interface}\n  to customize the auto-generated UI in system settings.\n\\item\n  If you need more flexibility---perhaps your app needs multiple\n  configuration screens, or maybe you've already written a configuration\n  UI and just want to insert it without bothering to write a\n  configuration interface---implement the \\texttt{ConfigurationScreen}\n  interface to implement your own.\n\\item\n  If you're using a configuration interface but you don't want a UI to\n  be generated---maybe you're using a \\texttt{ConfigurationScreen}\n  implementation instead, or maybe you just want configuration to be\n  handled programatically or by\n  \\href{/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files}{.config\n  file} ---you can\n  \\href{/docs/frameworks/-/knowledge_base/7-2/customizing-the-system-settings-user-interface\\#excluding-a-configuration-ui-from-system-settings}{just\n  leave it out}.\n\\item\n  If you want the UI to render only under certain circumstances, you can\n  write logic to\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/customizing-the-configuration-user-interface\\#excluding-a-configuration-ui}{do\n  that, too}.\n\\end{itemize}\n\nEnough conceptual stuff. You're ready to get started with some code. If\nyou already have an app that was configurable under an earlier version\nof Liferay DXP, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/upgrading-a-legacy-app}{Upgrading\na Legacy App}.\n\n\\chapter{Creating A Configuration\nInterface}\\label{creating-a-configuration-interface}\n\nFirst, you'll learn how to create a configuration with no scope\ndeclaration. This automatically scopes your configuration to\n\\texttt{SYSTEM}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a Java interface to represent the configuration and its default\n  values. Using a Java interface allows for an advanced type system for\n  each configuration option. Here is the configuration interface for the\n  Liferay Forms application:\n\n\\begin{verbatim}\n@Meta.OCD(\n    id = \"com.liferay.dynamic.data.mapping.form.web.configuration.DDMFormWebConfiguration\",\n    localization = \"content/Language\", name = \"ddm-form-web-configuration-name\"\n)\npublic interface DDMFormWebConfiguration {\n\n    @Meta.AD(\n        deflt = \"1\", description = \"autosave-interval-description\",\n        name = \"autosave-interval-name\", required = false\n    )\n    public int autosaveInterval();\n\n    @Meta.AD(\n        deflt = \"descriptive\", name = \"default-display-view\",\n        optionLabels = {\"Descriptive\", \"List\"},\n        optionValues = {\"descriptive\", \"list\"}, required = false\n    )\n    public String defaultDisplayView();\n\n\n}\n\\end{verbatim}\n\n  This defines two configuration options, the autosave interval (with a\n  default of one minute) and the default display view, which can be\n  descriptive or list, but defaults to descriptive. Here's what the two\n  Java annotations in the above snippet do:\n\n  \\textbf{Meta.OCD:} Registers this class as a configuration with a\n  specific id. \\textbf{The ID must be the fully qualified configuration\n  class name.}\n\n  \\textbf{Meta.AD:} Specifies\n  \\href{http://bnd.bndtools.org/chapters/210-metatype.html}{optional\n  metadata} about the field, such as whether it's a required field or if\n  it has a default value. Note that if you set a field as required and\n  don't specify a default value, the system administrator must specify a\n  value in order for your application to work properly. Use the\n  \\texttt{deflt} property to specify a default value.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You can dynamically populate select field options with the \n [`ConfigurationFieldsOptionProvider` interface](/docs/7-2/frameworks/-/knowledge_base/f/dynamically-populating-select-list-fields-in-the-configuration-ui)\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nThe fully-qualified name of the `Meta` class above is\n`aQute.bnd.annotation.metatype.Meta`. For more information about this class and\nthe `Meta.OCD` and `Meta.AD` annotations, please refer to the \n[bndtools documentation](http://bnd.bndtools.org/chapters/210-metatype.html).\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  To use the \\texttt{Meta.OCD} and \\texttt{Meta.AD} annotations in your\n  modules, you must\n  \\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{specify\n  a dependency} on the bnd library. We recommend using bnd version 3.\n  Here's an example of how to include this dependency in a Gradle\n  project:\n\n\\begin{verbatim}\ndependencies {\n    compile group: \"biz.aQute.bnd\", name: \"biz.aQute.bndlib\", version: \"3.1.0\"\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The annotations \\texttt{@Meta.OCD} and \\texttt{@Meta.AD}\nare part of the bnd library, but as of OSGi standard version R6, they're\nincluded in the OSGi core under the names\n\\texttt{@ObjectClassDefinition} and \\texttt{@AttributeDefinition}. The\nOSGi annotations can be used for simple cases like the one described in\nthis tutorial. However, a key difference between the two libraries is\nthat the bnd annotations are available at runtime, while the OSGi\nannotations are not. Because runtime availability is necessary for some\nof the Liferay-specific features described below, we recommend\ndefaulting to the bnd annotations.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Also Note:} Your project depends on a \\texttt{-metatype:\\ *}\ndeclaration in its metadata. If you're in a\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace}(or otherwise applying the\n\\href{/docs/7-2/reference/-/knowledge_base/r/gradle-plugins}{workspace\nplugin to your build}), it's added automatically at build time.\nOtherwise, add it manually in your module's \\texttt{bnd.bnd}. It's\nrequired to provide information about your app's configuration options\nso that a configuration UI can be generated.\n\n\\noindent\\hrulefill\n\nWhen you register a configuration interface, a UI is auto-generated for\nit in \\emph{System Settings} → \\emph{Platform} → \\emph{Third Party}.\nThat's the default location; read the next section to learn how to move\nit somewhere more intuitive.\n\n\\chapter{Categorizing the\nConfiguration}\\label{categorizing-the-configuration}\n\nBy default, the configuration UI for your app is generated in\n\\emph{System Settings} → \\emph{Platform} → \\emph{Third Party}. You\nprobably don't really want it there; by categorizing your configuration\nyou can place it somewhere intuitive and easy to find.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/scoping-configurations}{scope}\nyour configuration so that a UI is generated in Instance Settings as\nwell, your categorization will apply to that UI also.\n\n\\noindent\\hrulefill\n\nYou have two options: 1) locate your configuration UI in an existing\ncategory and section, or 2) create your own.\n\nHere are the default System Settings sections. All available categories\nare nested beneath these sections:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Content and Data\n\\item\n  Platform\n\\item\n  Security\n\\item\n  Commerce\n\\item\n  Other\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Sections appear if they contain at least one\nconfiguration category. Categories appear if they contain at least one\nconfiguration. The visible sections and categories depend on the\ndeployed modules.\n\n\\noindent\\hrulefill\n\n\\section{Specifying a Configuration\nCategory}\\label{specifying-a-configuration-category}\n\nSpecify the category for your UI by placing an\n\\texttt{@ExtendedObjectClassDefinition} annotation in your configuration\ninterface. This example, which appears right before the interface's\n\\texttt{@Meta.OCD} annotation, places the UI in the\n\\texttt{dynamic-data-mapping} category in the Content management\nsection:\n\n\\begin{verbatim}\n@ExtendedObjectClassDefinition(\n    category = \"dynamic-data-mapping\",\n    scope = ExtendedObjectClassDefinition.Scope.GROUP\n)\n\\end{verbatim}\n\nThis annotation does two things:\n\n\\begin{itemize}\n\\item\n  Specifies the \\texttt{dynamic-data-mapping} category in the Content\n  Management section.\n\\item\n  Sets the scope of the configuration. You'll learn more about this\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/scoping-configurations}{next}.\n\\end{itemize}\n\nThe fully qualified class name of the\n\\texttt{@ExtendedObjectClassDefinition} class is\n\\texttt{com.liferay.portal.configuration.metatype.annotations.ExtendedObjectClassDefinition}.\n\nNote: The infrastructure used by System Settings assumes the\n\\texttt{configurationPid} is the same as the fully qualified class name\nof the interface. If they don't match, it can't provide any information\nthrough \\texttt{ExtendedObjectClassConfiguration}.\n\nThe \\texttt{@ExtendedObjectClassDefinition} annotation is distributed\nthrough the \\texttt{com.liferay.portal.configuration.metatype} module,\nwhich you can\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{configure\nas a dependency}.\n\n\\section{Creating New Sections and\nCategories}\\label{creating-new-sections-and-categories}\n\nIf you don't like the default sections and categories, you can create\nyour own by implementing the \\texttt{ConfigurationCategory} interface.\n\nHere's code that creates the \\emph{Content and Data} section and the\n\\emph{Dynamic Data Mapping} category:\n\n\\begin{verbatim}\n@Component(service = ConfigurationCategory.class)\npublic class DynamicDataMappingConfigurationCategory\n    implements ConfigurationCategory {\n\n    @Override\n    public String getCategoryIcon() {\n        return _CATEGORY_ICON;\n    }\n\n    @Override\n    public String getCategoryKey() {\n        return _CATEGORY_KEY;\n    }\n\n    @Override\n    public String getCategorySection() {\n        return _CATEGORY_SECTION;\n    }\n\n    private static final String _CATEGORY_ICON = \"dynamic-data-mapping\";\n\n    private static final String _CATEGORY_KEY = \"dynamic-data-mapping\";\n\n    private static final String _CATEGORY_SECTION = \"content-and-data\";\n\n}\n\\end{verbatim}\n\nThe \\texttt{getCategorySection} method returns the String with the new\nsection's key. Similarly, \\texttt{getCategoryKey} returns the key for\nthe new category. Provide localized values for these keys in your\nmodule's \\texttt{src/main/resources/content/Language.properties} file.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} the language keys for categories and sections must follow\na specific format. Prefix each section language key with\n\\texttt{category-section.} and each category language key with\n\\texttt{category.} For example:\n\n\\texttt{category-section.content-and-data=Content\\ and\\ Data}\n\\texttt{category.dynamic-data-mapping=Dynamic\\ Data\\ Mapping}\n\n\\noindent\\hrulefill\n\nNext you'll specify the scope of your application's configuration.\n\n\\chapter{Scoping Configurations}\\label{scoping-configurations}\n\nHere's how to scope a configuration:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Set the scope in the configuration interface.\n\\item\n  Enable the configuration for scoped retrieval by creating a\n  configuration bean declaration.\n\\end{enumerate}\n\n\\section{Step 1: Setting the Configuration\nScope}\\label{step-1-setting-the-configuration-scope}\n\nUse the \\texttt{@ExtendedObjectClassDefinition} annotation to specify\nthe configuration's scope. The scope you choose must match how the\nconfiguration object is retrieved through the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/reading-scoped-configuration-values}{configuration\nprovider}. Pass one of these valid scope options to\n\\texttt{@ExtendedObjectClassDefinition}:\n\n\\texttt{Scope.SYSTEM}: for system scope \\texttt{Scope.COMPANY}: for\nvirtual instance scope \\texttt{Scope.GROUP}: for site scope\n\\texttt{Scope.PORTLET\\_INSTANCE}: for the portlet instance scope\n\nHere is an example:\n\n\\begin{verbatim}\n@ExtendedObjectClassDefinition(\n    category = \"dynamic-data-mapping\",\n    scope = ExtendedObjectClassDefinition.Scope.GROUP\n)\n@Meta.OCD(\n    id = \"com.liferay.dynamic.data.mapping.form.web.configuration.\n        DDMFormWebConfiguration\",\n    localization = \"content/Language\", \n    name = \"ddm-form-web-configuration-name\"\n)\n\npublic interface DDMFormWebConfiguration {\n\\end{verbatim}\n\n\\section{Step 2: Enabling the Configuration for Scoped\nRetrieval}\\label{step-2-enabling-the-configuration-for-scoped-retrieval}\n\nTo create a configuration bean declaration:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Register the configuration class by implementing\n  \\texttt{ConfigurationBeanDeclaration}.\n\n\\begin{verbatim}\n@Component\npublic class JournalGroupServiceConfigurationBeanDeclaration\n    implements ConfigurationBeanDeclaration {\n\\end{verbatim}\n\\item\n  This class has one method that returns the class of the configuration\n  interface you created. It enables the system to keep track of\n  configuration changes as they happen, making requests for the\n  configuration very fast.\n\n\\begin{verbatim}\n@Override\npublic Class<?> getConfigurationBeanClass() {\n    return JournalGroupServiceConfiguration.class;\n}\n\\end{verbatim}\n\\end{enumerate}\n\nThat's all there is to it. Now the configuration is scoped and supports\nscoped retrieval via \\texttt{ConfigurationProvider}. See the next\nsection for details on retrieval.\n\n\\chapter{Reading Scoped Configuration\nValues}\\label{reading-scoped-configuration-values}\n\nIf your configuration is scoped to anything other than \\texttt{SYSTEM},\nyou have two options for reading configuration values.\n\n\\begin{itemize}\n\\item\n  Use \\texttt{ConfigurationProvider}. This works for any kind of\n  configuration, and is the only way to read configuration values at the\n  \\texttt{COMPANY} and \\texttt{GROUP} scopes.\n\\item\n  Use \\texttt{PortletDisplay}. This is the recommended approach for\n  configurations at the \\texttt{PORTLET\\_INSTANCE} scope, but only works\n  at that scope.\n\\end{itemize}\n\n\\section{Using the Configuration\nProvider}\\label{using-the-configuration-provider}\n\nWhen using the Configuration Provider, instead of receiving the\nconfiguration directly, the class that wants to access it must\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Receive a \\texttt{ConfigurationProvider} to obtain the configuration.\n\\item\n  Be registered with a \\texttt{ConfigurationBeanDeclaration}.\n\\end{enumerate}\n\nThe tutorial on\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/scoping-configurations}{scoping\nconfigurations} demonstrates how to register the configuration with a\n\\texttt{ConfigurationBeanDeclaration}.\n\nAfter registering with a \\texttt{ConfigurationBeanDeclaration}, you're\nready to use a \\texttt{ConfigurationProvider} to retrieve the scoped\nconfiguration. Here's how you obtain a reference to it:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Here's the approach for components:\n\n\\begin{verbatim}\n@Reference(unbind = \"-\")\nprotected void setConfigurationProvider(ConfigurationProvider configurationProvider) {\n    _configurationProvider = configurationProvider;\n}\n\\end{verbatim}\n\\item\n  Here's the approach for Service Builder services:\n\n\\begin{verbatim}\n@ServiceReference(type = ConfigurationProvider.class)\nprotected ConfigurationProvider configurationProvider;\n\\end{verbatim}\n\\item\n  For Spring beans, it is possible to use the same mechanism as for\n  Service Builder services (\\texttt{@ServiceReference}).\n\\item\n  For anything else, call the same methods from the utility class,\n  \\texttt{ConfigurationProviderUtil}. Be sure you call the utility\n  methods in contexts where the portal is guaranteed to be initialized\n  prior to the method call. This class is useful in the\n  \\href{/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console}{scripting\n  console}, for example. Here's an example method that uses the utility\n  class. It comes from the export-import service, which is only called\n  during the import and export of content from a running portal:\n\n\\begin{verbatim}\nprotected boolean isValidateLayoutReferences() throws PortalException {\n    long companyId = CompanyThreadLocal.getCompanyId();\n\n    ExportImportServiceConfiguration exportImportServiceConfiguration =\n        ConfigurationProviderUtil.getCompanyConfiguration(\n            ExportImportServiceConfiguration.class, companyId);\n\n    return exportImportServiceConfiguration.validateLayoutReferences();\n}\n\\end{verbatim}\n\\end{enumerate}\n\nTo retrieve the configuration, use one of the following methods of the\nprovider:\n\n\\begin{description}\n\\tightlist\n\\item[\\texttt{getCompanyConfiguration()}]\nUsed when you want to support different configurations per virtual\ninstance. In this case, the configuration is usually entered by an admin\nthrough \\emph{Control Panel} → \\emph{Configuration} → \\emph{Instance\nSettings}.\n\\item[\\texttt{getGroupConfiguration()}]\nUsed when you want to support different configurations per site (or, if\ndesired, per page scope). Usually this configuration is specified by an\nadmin through the Configuration menu option in an app accessing through\nthe site administration menu. That UI is developed as a portlet\nconfiguration view.\n\\item[\\texttt{getPortletInstanceConfiguration()}]\nUsed to obtain the configuration for a specific portlet instance. Most\noften you should not be using this directly. Use the convenience method\nin \\texttt{PortletDisplay} instead as shown below.\n\\item[\\texttt{getSystemConfiguration}]\nUsed to obtain the configuration for the system scope. These settings\nare specified by an admin via the System Settings application or with an\nOSGi configuration file.\n\\end{description}\n\nHere are a couple real world examples from Liferay's source code:\n\n\\begin{verbatim}\nJournalGroupServiceConfiguration configuration =\n    configurationProvider.getGroupConfiguration(\n        JournalGroupServiceConfiguration.class, groupId);\n\nMentionsGroupServiceConfiguration configuration =\n  _configurationProvider.getCompanyConfiguration(\n     MentionsGroupServiceConfiguration.class, entry.getCompanyId());\n\\end{verbatim}\n\nNext, you'll learn a nifty way to to access a portlet instance\nconfiguration from a JSP.\n\n\\section{\\texorpdfstring{Accessing the Portlet Instance Configuration\nThrough the\n\\texttt{PortletDisplay}}{Accessing the Portlet Instance Configuration Through the PortletDisplay}}\\label{accessing-the-portlet-instance-configuration-through-the-portletdisplay}\n\nOften you must access portlet instance settings from a JSP or from a\nJava class that isn't an OSGi component. To read the settings in these\ncases, a method was added to \\texttt{PortletDisplay}, which is available\nas a request object. Here is an example of how to use it:\n\n\\begin{verbatim}\nRSSPortletInstanceConfiguration rssPortletInstanceConfiguration =\n    portletDisplay.getPortletInstanceConfiguration(\n        RSSPortletInstanceConfiguration.class);\n\\end{verbatim}\n\nAs you can see, it knows how to find the values and returns a typed bean\ncontaining them just by passing the configuration class.\n\n\\chapter{Reading Unscoped Configuration Values from an MVC\nPortlet}\\label{reading-unscoped-configuration-values-from-an-mvc-portlet}\n\nIf your configuration is scoped to \\texttt{SYSTEM} or is unscoped (which\namounts to the same thing), you have a couple of options for reading\nconfiguration values. There are two ways to do this:\n\n\\begin{itemize}\n\\item\n  Add a configuration to the request and read it from the view layer\n  (commonly a JSP).\n\\item\n  Read values directly from the portlet class.\n\\end{itemize}\n\nThis tutorial uses dummy code from a portlet we'll call the Example\nConfiguration Portlet. The import statements are included in the code\nsnippets so that you can see the fully qualified class names (FQCNs) of\nall the classes that are used.\n\n\\section{Accessing the Configuration Object in the Portlet\nClass}\\label{accessing-the-configuration-object-in-the-portlet-class}\n\nWhether you need the configuration values in the portlet class or the\nJSPs, the first step is to get access to the configuration object in the\n\\texttt{*Portlet} class.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Imports first:\n\n\\begin{verbatim}\npackage com.liferay.docs.exampleconfig;\n\nimport java.io.IOException;\nimport java.util.Map;\n\nimport javax.portlet.Portlet;\nimport javax.portlet.PortletException;\nimport javax.portlet.RenderRequest;\nimport javax.portlet.RenderResponse;\n\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Modified;\n\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\n\nimport com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil;\n\\end{verbatim}\n\\item\n  MVC Portlet classes are Component classes. If you have a Bean Portlet\n  or PortletMVC4Spring class, the configuration below goes in\n  \\texttt{portlet.xml} and \\texttt{liferay-portlet.xml}. To mate the\n  configuration with the Component, provide the\n  \\texttt{configurationPid} property with the FQCN of the configuration\n  class.\n\n\\begin{verbatim}\n@Component(\n    configurationPid = \"com.liferay.docs.exampleconfig.ExampleConfiguration\",\n    immediate = true,\n    property = {\n        \"com.liferay.portlet.display-category=category.sample\",\n        \"com.liferay.portlet.instanceable=true\",\n        \"javax.portlet.security-role-ref=power-user,user\",\n        \"javax.portlet.init-param.template-path=/\",\n        \"javax.portlet.init-param.view-template=/view.jsp\",\n        \"javax.portlet.resource-bundle=content.Language\"\n    },\n    service = Portlet.class\n)\npublic class ExampleConfigPortlet extends MVCPortlet {\n\\end{verbatim}\n\n  Note that you can specify more than one configuration PID here by\n  enclosing the values in curly braces (\\texttt{\\{\\}}) and placing\n  commas between each PID.\n\\item\n  Write an \\texttt{activate} method annotated with \\texttt{@Activate}\n  and \\texttt{@Modified}. This ensures that the method is invoked when\n  the Component is started, and again whenever the configuration is\n  changed.\n\n\\begin{verbatim}\n@Activate\n@Modified\nprotected void activate(Map<String, Object> properties) {\n    _configuration = ConfigurableUtil.createConfigurable(\n    ExampleConfiguration.class, properties);\n}\n\nprivate volatile ExampleConfiguration _configuration;\n\\end{verbatim}\n\\end{enumerate}\n\nA volatile field \\texttt{\\_configuration} is created by the\n\\texttt{createConfigurable} method. Now the field can be used to\nretrieve configuration values or to set the values in the request, so\nthey can be retrieved in the application's JSPs.\n\n\\section{Accessing the Configuration from a\nJSP}\\label{accessing-the-configuration-from-a-jsp}\n\nIn the case of reading from a JSP, add the configuration object to the\nrequest object so its values can be read from the JSPs that comprise the\napplication's view layer.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the configuration object to the request. Here's what it looks like\n  in a simple portlet's \\texttt{doView} method:\n\n\\begin{verbatim}\n@Override\npublic void doView(RenderRequest renderRequest,\n    RenderResponse renderResponse) throws IOException, PortletException {\n\n    renderRequest.setAttribute(\n        ExampleConfiguration.class.getName(), _configuration);\n\n    super.doView(renderRequest, renderResponse);\n}\n\\end{verbatim}\n\n  The main difference between this example and the component class\n  covered in the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/reading-unscoped-configuration-values-from-a-component}{next\n  section} is that this class is a portlet class and it sets the\n  configuration object as a request attribute in its \\texttt{doView()}\n  method.\n\\item\n  Read configuration values from a JSP. First add these imports to the\n  top of your \\texttt{view.jsp} file:\n\n\\begin{verbatim}\n<%@ page import=\"com.liferay.docs.exampleconfig.ExampleConfiguration\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.GetterUtil\" %>\n\\end{verbatim}\n\\item\n  In the JSP, obtain the configuration object from the request object\n  and read the desired configuration value from it. Here's a\n  \\texttt{view.jsp} file that does this:\n\n\\begin{verbatim}\n<%@ include file=\"/init.jsp\" %>\n\n<p>\n    <b>Hello from the Example Configuration portlet!</b>\n</p>\n\n<%\nExampleConfiguration configuration = (ExampleConfiguration) GetterUtil.getObject(\n    renderRequest.getAttribute(ExampleConfiguration.class.getName()));\n\nString favoriteColor = configuration.favoriteColor();\n%>\n\n<p>Favorite color: <span style=\"color: <%= favoriteColor %>;\"><%= favoriteColor %></span></p\n\\end{verbatim}\n\\end{enumerate}\n\nThe example code here would make the application display a message like\nthis:\n\n\\begin{verbatim}\nFavorite color: blue\n\\end{verbatim}\n\nThe word \\emph{blue} is written in blue text. Note that \\emph{blue} is\ndisplayed by default since you specified it as the default in your\n\\texttt{ExampleConfiguration} interface. If you go to \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{System Settings} → \\emph{Platform}\n→ \\emph{Third Party} and click on the \\emph{Example configuration} link,\nyou can find the \\texttt{Favorite\\ color} setting and change its value.\nThe JSP reads the configuration, and refreshing the UI reflects this\nupdate.\n\n\\section{Accessing the Configuration from the Portlet\nClass}\\label{accessing-the-configuration-from-the-portlet-class}\n\nNow that you've seen a detailed example of accessing the configuration\nvalues in a JSP, there's not much more to cover when accessing the\nconfiguration directly in the \\texttt{-Portlet} class. Wherever you\nrequire the value of a configuration property, call\n\\texttt{\\_configuration.propertyName} and you have access to the\ncurrently configured value. For example, this code compares the\n\\texttt{favoriteColor} configuration value with a\n\\texttt{userFavoriteColor} that's fetched from the request object:\n\n\\begin{verbatim}\npublic boolean isFavoriteColorMatched {\n\n    String userFavoriteColor = ParamUtil.getString(request, \"userFavoriteColor\");\n\n    if (_configuration.favoriteColor() == userFavoriteColor) {\n\n        SessionMessages.add(request, \"congratulateUser\");\n\n        return true;\n    }\n\n    return false;\n}\n\\end{verbatim}\n\nIt returns true and adds a success message if the two Strings match each\nother, but you can do anything that makes sense for your application's\ncontroller logic.\n\nThat's all there is to reading configuration values in a Portlet. The\nnext section covers reading configuration values from an OSGi Component\nclass that is not part of a portlet.\n\n\\chapter{Reading Unscoped Configuration Values from a\nComponent}\\label{reading-unscoped-configuration-values-from-a-component}\n\nFollow these steps to read \\texttt{SYSTEM} scoped or unscoped\nconfiguration values from a Component that isn't part of a portlet:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  First set the \\texttt{configurationPid} Component property as the\n  fully qualified class name of the configuration class:\n\n\\begin{verbatim}\n@Component(configurationPid = \"com.liferay.dynamic.data.mapping.form.web.configuration.DDMFormWebConfiguration\")\n\\end{verbatim}\n\\item\n  Then provide an \\texttt{activate} method, annotated with\n  \\texttt{@Activate} to ensure the method is invoked as soon as the\n  Component is started, and \\texttt{@Modified} so it's invoked whenever\n  the configuration is modified.\n\n\\begin{verbatim}\n@Activate\n@Modified\nprotected void activate(Map<String, Object> properties) {\n    _formWebConfiguration = ConfigurableUtil.createConfigurable(\n        DDMFormWebConfiguration.class, properties);\n}\n\nprivate volatile DDMFormWebConfiguration _formWebConfiguration;\n\\end{verbatim}\n\n  The \\texttt{activate()} method calls the method\n  \\texttt{ConfigurableUtil.createConfigurable()} to convert a map of the\n  configuration's properties to a typed class, which is easier to\n  handle. The configuration is stored in a \\texttt{volatile} field.\n  Don't forget to make it \\texttt{volatile} to prevent thread safety\n  problems.\n\\item\n  Once the activate method is set up, retrieve particular properties\n  from the configuration wherever they're needed:\n\n\\begin{verbatim}\npublic void orderCar(String model) {\n    order(\"car\", model, _configuration.favoriteColor());\n}\n\\end{verbatim}\n\n  This is dummy code: don't try to find it in the Liferay source code.\n  The String configuration value of \\texttt{favoriteColor} is passed to\n  the \\texttt{order} method call, presumably so that whatever model car\n  is ordered gets ordered in the configured favorite color.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The bnd library also provides a class called\n\\texttt{aQute.bnd.annotation.metatype.Configurable} with a\n\\texttt{createConfigurable()} method. You can use that instead of\nLiferay's\n\\texttt{com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil}\nwithout any problems. Liferay's developers created the\n\\texttt{ConfigurableUtil} class to improve the performance of bnd's\nimplementation, and it's used in internal code. Feel free to use\nwhichever method you prefer.\n\n\\noindent\\hrulefill\n\nWith very few lines of code, you have a configurable application that\ndynamically changes its configuration, has an auto-generated UI, and\nuses a simple API to access the configuration.\n\n\\chapter{Customizing the Configuration User\nInterface}\\label{customizing-the-configuration-user-interface}\n\nThere are three ways to customize a configuration UI.\n\n\\begin{itemize}\n\\item\n  Provide a custom form for a configuration object. This modifies the\n  auto-generated UI.\n\\item\n  Write a completely custom configuration UI. This is useful especially\n  if you aren't using the Configuration Admin service or any of\n  Liferay's Configuration APIs.\n\\item\n  Exclude a configuration object. You'll want this option if you're\n  using a configuration interface but don't wan't a UI generated for\n  you.\n\\end{itemize}\n\n\\section{Providing Custom Configuration\nForms}\\label{providing-custom-configuration-forms}\n\nCustomize your auto-generated UI by implementing the\n\\texttt{ConfigurationFormRender} interface. To write this interface, you\nmust refer to your configuration interface. For this example, refer to\nthis configuration interface from Liferay's Currency Converter\napplication:\n\n\\begin{verbatim}\n@ExtendedObjectClassDefinition(category = \"localization\")\n@Meta.OCD(\n    id = \"com.liferay.currency.converter.web.configuration.CurrencyConverterConfiguration\",\n    localization = \"content/Language\",\n    name = \"currency-converter-configuration-name\"\n)\npublic interface CurrencyConverterConfiguration {\n\n    @Meta.AD(deflt = \"GBP|CNY|EUR|JPY|USD\", name = \"symbols\", required = false)\n    public String[] symbols();\n}\n\\end{verbatim}\n\nThis example defines one configuration option, \\texttt{symbols}, which\ntakes an array of values.\n\nImplement \\texttt{ConfigurationFormRenderer}'s three methods:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\texttt{getPid}: Return the configuration object's ID. This is defined\n  in the \\texttt{id} property in the \\texttt{*Configuration} class's\n  \\texttt{@Meta.OCD} annotation.\n\\item\n  \\texttt{getRequestParameters}: Read the parameters sent by the custom\n  form and put them in a Map whose keys should be the method names of\n  the Configuration interface.\n\\item\n  \\texttt{render}: Render the custom form's fields, using your desired\n  method (for example, JSPs or another template mechanism). The\n  \\texttt{\\textless{}form\\textgreater{}} tag itself is provided\n  automatically and shouldn't be included in the\n  \\texttt{ConfigurationFormRenderer}.\n\\end{enumerate}\n\nHere's a complete \\texttt{ConfigurationFormRenderer} implementation:\n\n\\begin{verbatim}\n@Component(immediate = true, service = ConfigurationFormRenderer.class)\npublic class CurrencyConverterConfigurationFormRenderer\n    implements ConfigurationFormRenderer {\n\n    @Override\n    public String getPid() {\n        return \"com.liferay.currency.converter.web.configuration.CurrencyConverterConfiguration\";\n    }\n\n    @Override\n    public void render(HttpServletRequest request, HttpServletResponse response)\n            throws IOException {\n            \n        String formHtml = \"<input name=\\\"mysymbols\\\" />\";\n\n        PrintWriter writer = response.getWriter();\n\n        writer.print(formHtml);\n\n    }\n\n    @Override\n    public Map<String, Object> getRequestParameters(\n            HttpServletRequest request) {\n\n        Map<String, Object> params = new HashMap<>();\n\n        String[] mysymbols = ParamUtil.getParameterValues(request, \"mysymbols\");\n\n        params.put(\"symbols\", mysymbols);\n\n        return params;\n    }\n}\n\\end{verbatim}\n\nThe above example generates a custom rendering (HTML) for the form in\nthe \\texttt{render()} method and reads the information entered in the\ncustom form in the \\texttt{getRequestParameters()} method.\n\nTo see a complete demonstration, including JSP markup, read the\ndedicated tutorial on creating a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/configuration-form-renderer}{configuration\nform renderer}.\n\n\\section{Creating a Completely Custom Configuration\nUI}\\label{creating-a-completely-custom-configuration-ui}\n\nYou get more flexibility if you create a completely custom UI using a\n\\texttt{ConfigurationScreen} implementation.\n\nAt a high level you must\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Write a Component that declares itself an implementation of the\n  \\texttt{ConfigurationScreen} interface.\n\\item\n  Implement \\texttt{ConfigurationScreen}'s methods.\n\\item\n  Create the UI by hand.\n\\end{enumerate}\n\nHere's an example implementation:\n\n\\begin{verbatim}\n@Component(immediate = true, service = ConfigurationScreen.class) \npublic class SampleConfigurationScreen implements ConfigurationScreen {\n\\end{verbatim}\n\nFirst declare the class an implementation of\n\\texttt{ConfigurationScreen}.\n\n\\begin{verbatim}\n@Override \npublic String getCategoryKey() { \n\n    return \"third-party\"; \n\n}\n\n@Override \npublic String getKey() { \n\n    return \"sample-configuration-screen\"; \n\n}\n\n@Override \npublic String getName(Locale locale) { \n\n    return \"Sample Configuration Screen\"; \n\n}\n\\end{verbatim}\n\nSecond, set the category key, the configuration entry's key, and its\nlocalized name. This example puts the configuration entry, keyed\n\\texttt{sample-configuration-screen}, into the \\texttt{third-party}\nSystem Settings section. The String that appears in System Settings is\n\\emph{Sample Configuration Screen}.\n\n\\begin{verbatim}\n@Override \npublic String getScope() { \n\n    return \"system\"; \n\n}\n\\end{verbatim}\n\nThird, set the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/scoping-configurations}{configuration\nscope}.\n\n\\begin{verbatim}\n@Override \npublic void render(HttpServletRequest request, HttpServletResponse response) \n        throws IOException {\n\n    _jspRenderer.renderJSP( _servletContext, request, response,\n    \"/sample_configuration_screen.jsp\"); \n\n}\n\n@Reference private JSPRenderer _jspRenderer;\n\n@Reference(\n    target =\"(osgi.web.symbolicname=com.liferay.currency.converter.web)\", \n    unbind = \"-\")\nprivate ServletContext _servletContext;\n\\end{verbatim}\n\nThe most important step is to write the \\texttt{render} method. This\nexample relies on the \\texttt{JSPRenderer} service to delegate rendering\nto a JSP.\n\nIt's beyond the scope of this tutorial to write the JSP markup. A\nseparate tutorial will provide a complete demonstration of the\n\\texttt{ConfigurationScreen} and implementation and the JSP markup to\ndemonstrate its usage.\n\n\\section{Excluding a Configuration\nUI}\\label{excluding-a-configuration-ui}\n\nIf you don't want a UI to be generated for you, you have two options.\n\n\\begin{itemize}\n\\item\n  If you don't want a UI generated no matter what, use the\n  \\texttt{generateUI} property.\n\\item\n  If you only want the UI to render under specific circumstances\n  (defined by logic you'll write yourself), use the configuration\n  visibility SPI.\n\\end{itemize}\n\n\\section{\\texorpdfstring{Using\n\\texttt{generateUI}}{Using generateUI}}\\label{using-generateui}\n\nTo turn off auto-generating at all scopes, include the\n\\texttt{ExtendedObjectClassDefinition} annotation property\n\\texttt{generateUI} in your configuration interface. The property\ndefaults to \\texttt{true}; here is an example setting it to\n\\texttt{false}:\n\n\\begin{verbatim}\n@ExtendedObjectClassDefinition(generateUI=false)\n@Meta.OCD(\n  id = \"com.foo.bar.LowLevelConfiguration\",\n)\npublic interface LowLevelConfiguration {\n\n  public String[] foo();\n  public String bar();\n\n}\n\\end{verbatim}\n\nNow no UI is auto-generated for this configuration. You can still manage\nthe configuration via a \\texttt{ConfigurationScreen} implementation, a\n\\href{/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files}{.config\nfile}, or programmatically.\n\n\\section{Using the Configuration Visibility\nSPI}\\label{using-the-configuration-visibility-spi}\n\nThe configuration visibility SPI involves implementing a single\ninterface, \\texttt{ConfigurationVisibilityController}. You can see the\nwhole interface\n\\href{https://github.com/liferay/liferay-portal/blob/48cd71b35a2d3b66e88f47685be7186cb7c52075/modules/apps/configuration-admin/configuration-admin-api/src/main/java/com/liferay/configuration/admin/display/ConfigurationVisibilityController.java}{here}.\n\nTo implement the interface, you must identify your configuration\ninterface using an \\texttt{@Component} property, then write your own\nlogic for the interface's only method, \\texttt{isVisible}. Here is a\nsample implementation from Liferay's source code:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = \"configuration.pid=com.liferay.sharing.internal.configuration.SharingCompanyConfiguration\",\n    service = ConfigurationVisibilityController.class\n)\npublic class SharingCompanyConfigurationVisibilityController\n    implements ConfigurationVisibilityController {\n\n    @Override\n    public boolean isVisible(\n        ExtendedObjectClassDefinition.Scope scope, Serializable scopePK) {\n\n        SharingConfiguration systemSharingConfiguration =\n            _sharingConfigurationFactory.getSystemSharingConfiguration();\n\n        return systemSharingConfiguration.isEnabled();\n    }\n\n    @Reference\n    private SharingConfigurationFactory _sharingConfigurationFactory;\n\n}\n\\end{verbatim}\n\nNote that the property \\texttt{configuration.pid} identifies the\nconfiguration interface of the UI to be hidden. In this example, the\nconfiguration UI only renders when\n\\texttt{systemSharingConfiguration.isEnabled} returns \\texttt{true}.\n\n\\chapter{Configuration Form Renderer}\\label{configuration-form-renderer}\n\nTo replace an application's auto-generated configuration screen with a\nform built from scratch, you follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Use a \\texttt{DisplayContext} class to transfer data between back-end\n  code and the desired JSP markup.\n\\item\n  Implement the \\texttt{ConfigurationFormRenderer} interface.\n\\item\n  Render the configuration form. This tutorial demonstrates the use of a\n  JSP and the previously created \\texttt{DisplayContext} class.\n\\end{enumerate}\n\nA generalized discussion on System Settings UI customization is found in\na\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/customizing-the-configuration-user-interface}{separate\nsection}.\n\nThis article demonstrates replacing the configuration UI for the\n\\emph{Language Template} System Settings entry, found in \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{System Settings} →\n\\emph{Localization} → \\emph{Language Template}. The same steps apply\nwhen replacing your custom application's auto-generated UI.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sys-settings-lang-template-default.png}\n\\caption{The auto-generated UI for the Language Template configuration\nscreen is sub-optimal. A select list with more human readable options is\npreferable.}\n\\end{figure}\n\nSpecifically, the text input field labeled \\emph{DDM Template Key} in\nthe auto-generated UI is replaced with a select list field type called\n\\emph{Language Selection Style}, populated with all possible DDM\nTemplate Keys.\n\n\\section{\\texorpdfstring{Creating a\n\\texttt{DisplayContext}}{Creating a DisplayContext}}\\label{creating-a-displaycontext}\n\nA \\texttt{DisplayContext} class is a POJO that simplifies and minimizes\nthe use of Java logic in JSPs. Display context usage isn't required, but\nit's a nice convention to follow. It's a kind of data transfer object,\nwhere the \\texttt{DisplayContext}'s setters are called from the Java\nclass providing the render logic (in this case the\n\\texttt{ConfigurationFormRenderer}'s \\texttt{render} method), and the\ngetters are called from the JSP, removing the need for Java logic to be\nwritten inside the JSP itself.\n\nFor this example, create a\n\\texttt{LanguageTemplateConfigurationDisplayContext} class with these\ncontents:\n\n\\begin{verbatim}\npublic class LanguageTemplateConfigurationDisplayContext {\n\n    public void addTemplateValue(\n        String templateKey, String templateDisplayName) {\n\n        _templateValues.add(new String[] {templateKey, templateDisplayName});\n    }\n\n    public String getCurrentTemplateName() {\n        return _currentTemplateName;\n    }\n\n    public String getFieldLabel() {\n        return _fieldLabel;\n    }\n\n    public List<String[]> getTemplateValues() {\n        return _templateValues;\n    }\n\n    public void setCurrentTemplateName(String currentTemplateName) {\n        _currentTemplateName = currentTemplateName;\n    }\n\n    public void setFieldLabel(String fieldLabel) {\n        _fieldLabel = fieldLabel;\n    }\n\n    private String _currentTemplateName;\n    private String _fieldLabel;\n    private final List<String[]> _templateValues = new ArrayList<>();\n\n}\n\\end{verbatim}\n\nNext implement the \\texttt{ConfigurationFormRenderer}.\n\n\\section{\\texorpdfstring{Implementing a\n\\texttt{ConfigurationFormRenderer}}{Implementing a ConfigurationFormRenderer}}\\label{implementing-a-configurationformrenderer}\n\nFirst create the component and class declarations. Set the\n\\texttt{service} property to \\texttt{ConfigurationFormRenderer.class}:\n\n\\begin{verbatim}\n@Component(\n    configurationPid = \"com.liferay.site.navigation.language.web.configuration.SiteNavigationLanguageWebTemplateConfiguration\",\n    immediate = true, service = ConfigurationFormRenderer.class\n)\npublic class LanguageTemplateConfigurationFormRenderer\n    implements ConfigurationFormRenderer {\n\\end{verbatim}\n\nNext, write an \\texttt{activate} method (decorated with\n\\texttt{@Activate} and \\texttt{@Modified}) to convert a map of the\nconfiguration's properties to a typed class. The configuration is stored\nin a volatile field. Don't forget to make it volatile to prevent thread\nsafety problems. See the article on\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/reading-unscoped-configuration-values-from-a-component}{reading\nconfiguration values from a component class} for more information.\n\n\\begin{verbatim}\n@Activate\n@Modified\npublic void activate(Map<String, Object> properties) {\n    _siteNavigationLanguageWebTemplateConfiguration =\n        ConfigurableUtil.createConfigurable(\n            SiteNavigationLanguageWebTemplateConfiguration.class,\n            properties);\n}\n\nprivate volatile SiteNavigationLanguageWebTemplateConfiguration\n    _siteNavigationLanguageWebTemplateConfiguration;\n\\end{verbatim}\n\nNext override the \\texttt{getPid} and \\texttt{getRequestParameters}\nmethods:\n\n\\begin{verbatim}\n@Override\npublic String getPid() {\n    return \"com.liferay.site.navigation.language.web.configuration.\" +\n        \"SiteNavigationLanguageWebTemplateConfiguration\";\n}\n\\end{verbatim}\n\nReturn the full configuration ID, as specified in the\n\\texttt{*Configuration} class's \\texttt{@Meta.OCD} annotation.\n\n\\begin{verbatim}\n@Override\npublic Map<String, Object> getRequestParameters(\n    HttpServletRequest request) {\n\n    Map<String, Object> params = new HashMap<>();\n\n    String ddmTemplateKey = ParamUtil.getString(request, \"ddmTemplateKey\");\n\n    params.put(\"ddmTemplateKey\", ddmTemplateKey);\n\n    return params;\n}\n\\end{verbatim}\n\nIn the \\texttt{getRequestParameters} method, map the parameters sent by\nthe custom form (obtained from the request) to the keys of the fields in\nthe Configuration interface.\n\nProvide the render logic via the overridden \\texttt{render} method. The\nrendering approach demonstrated here uses a JSP. Recall that it's backed\nby a \\texttt{DisplayContext} class set into the request object. The\nvalues set from this \\texttt{render} method are available in the JSP via\nthe \\texttt{DisplayContext} object's getters.\n\nLoop through the DDM Template Keys for the given \\texttt{groupId} and\nset them into the display context with the \\texttt{addTemplateKey}\nmethod. Then set the other necessary values that the JSP needs. In this\ncase, set the title, the field label, and the redirect URL. Finally,\ncall \\texttt{renderJSP} and pass in the \\texttt{servletContext},\nrequest, response, and the path to the JSP:\n\n\\begin{verbatim}\n@Override\npublic void render(HttpServletRequest request, HttpServletResponse response)\n    throws IOException {\n\n    Locale locale = request.getLocale();\n\n    LanguageTemplateConfigurationDisplayContext\n        languageTemplateConfigurationDisplayContext =\n            new LanguageTemplateConfigurationDisplayContext();\n\n    languageTemplateConfigurationDisplayContext.setCurrentTemplateName(\n        _siteNavigationLanguageWebTemplateConfiguration.ddmTemplateKey());\n\n    long groupId = 0;\n\n    long companyId = _portal.getCompanyId(actionRequest);\n\n    Group group = _groupLocalService.fetchCompanyGroup(companyId);\n\n    if (group != null) {\n        groupId = group.getGroupId();\n    }\n\n    List<DDMTemplate> ddmTemplates = _ddmTemplateLocalService.getTemplates(\n        groupId, _portal.getClassNameId(LanguageEntry.class));\n\n    for (DDMTemplate ddmTemplate : ddmTemplates) {\n        languageTemplateConfigurationDisplayContext.addTemplateValue(\n            ddmTemplate.getTemplateKey(), ddmTemplate.getName(locale));\n    }\n\n    languageTemplateConfigurationDisplayContext.setFieldLabel(\n        LanguageUtil.get(\n            ResourceBundleUtil.getBundle(\n                locale, LanguageTemplateConfigurationFormRenderer.class),\n            \"language-selection-style\"));\n\n    request.setAttribute(\n        LanguageTemplateConfigurationDisplayContext.class.getName(),\n        languageTemplateConfigurationDisplayContext);\n\n    _jspRenderer.renderJSP(\n        _servletContext, request, response,\n        \"/configuration/site_navigation_language_web_template.jsp\");\n}\n\\end{verbatim}\n\nSpecify the required service references at the bottom of the class. Be\ncareful to target the proper servlet context, passing the\n\\texttt{bundle-SymbolicName} of the module (found in its\n\\texttt{bnd.bnd} file) into the \\texttt{osgi.web.symbolicname} property\nof the reference target:\n\n\\begin{verbatim}\n@Reference\nprivate DDMTemplateLocalService _ddmTemplateLocalService;\n\n@Reference\nprivate GroupLocalService _groupLocalService;\n\n@Reference\nprivate JSPRenderer _jspRenderer;\n\n@Reference\nprivate Portal _portal;\n\n@Reference(\n    target = \"(osgi.web.symbolicname=com.liferay.site.navigation.language.web)\",\n    unbind = \"-\"\n)\nprivate ServletContext _servletContext;\n\\end{verbatim}\n\nOnce the configuration form renderer is implemented, you can write the\nJSP markup for the form.\n\n\\section{Writing the JSP Markup}\\label{writing-the-jsp-markup}\n\nNow write the JSP:\n\n\\begin{verbatim}\n<%@ include file=\"/init.jsp\" %>\n\n<%\nLanguageTemplateConfigurationDisplayContext\n    languageTemplateConfigurationDisplayContext = (LanguageTemplateConfigurationDisplayContext)request.getAttribute(LanguageTemplateConfigurationDisplayContext.class.getName());\n\nAdmin: Instance Settings    String currentTemplateName = languageTemplateConfigurationDisplayContext.getCurrentTemplateName();\n%>\n\n<aui:select label=\"<%= HtmlUtil.escape(languageTemplateConfigurationDisplayContext.getFieldLabel()) %>\" name=\"ddmTemplateKey\" value=\"<%= currentTemplateName %>\">\n\n    <%\n    for (String[] templateValue : languageTemplateConfigurationDisplayContext.getTemplateValues()) {\n    %>\n\n        <aui:option label=\"<%= templateValue[1] %>\" selected=\"<%= currentTemplateName.equals(templateValue[0]) %>\" value=\"<%= templateValue[0] %>\" />\n\n    <%\n    }\n    %>\n\n</aui:select>\n\\end{verbatim}\n\nThe opening scriptlet gets the display context object from the request\nso that all its getters are invoked whenever information from the\nback-end is required. Right away, the \\texttt{getCurrentTemplateName}\nmethod is called, since the current template name is needed for the\nfirst option's \\texttt{ddmTemplateKey} display value as soon as the form\nis rendered. This happens in the\n\\texttt{\\textless{}aui:select\\textgreater{}} tag. There's just a bit of\nlogic used to create an option for each of the available DDM templates\nthat can be chosen.\n\nSo what does this example look like when all is said and done?\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sys-settings-lang-template-custom.png}\n\\caption{A select list provides a more user friendly configuration\nexperience than a text field.}\n\\end{figure}\n\nNow, administrators encountering the Language Template entry in System\nSettings won't be handicapped by not knowing the available DDM Template\nKeys. Providing the available values in a select field wildly enhances\nthe user experience.\n\n\\chapter{Using DDM Form Annotations in Configuration\nForms}\\label{using-ddm-form-annotations-in-configuration-forms}\n\nThe auto-generated configuration form you get by just creating a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-configuration-interface}{configuration\ninterface} can be too simplistic for some configurations. To enhance it,\nuse the Dynamic Data Mapping (DDM) Form Annotations.\n\nTo use DDM Annotations in configuration forms,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Configure the module dependencies.\n\\item\n  Write a \\texttt{ConfigurationForm} class, including just the fields\n  that you want to leverage the enhanced forms capability. This is\n  similar to the configuration interface, but with field annotations\n  from the Liferay\n  \\href{https://github.com/liferay/liferay-portal/tree/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/annotations}{Dynamic\n  Data Mapping API} rather than the bndtools metatype specification. The\n  fields here must match fields defined in the configuration interface.\n\\item\n  Implement a\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/configuration-admin/configuration-admin-api/src/main/java/com/liferay/configuration/admin/definition/ConfigurationDDMFormDeclaration.java}{\\texttt{ConfigurationDDMFormDeclaration}}\n  to mark your configuration as having a \\texttt{ConfigurationForm}.\n\\end{enumerate}\n\nThis article assumes you already have an auto-generated configuration\nUI.\n\nNote that the example code here splits up the Configuration interface\nand the Configuration Form interface. If you'd rather mash these\ntogether into one class, you can. This approach might make sense if you\nhave a small number of fields in your configuration and a simple form to\ncreate. If you have numerous configuration fields and/or a complex form\nto create, or if you're taking an existing configuration and extending\nit to use the DDM Form annotations, you can consider separating the\nclasses, as shown here.\n\n\\section{Step 1: Declare the\nDependencies}\\label{step-1-declare-the-dependencies}\n\nIn the \\texttt{build.gradle} file, add \\texttt{compileOnly} dependencies\non the \\texttt{dynamic-data-mapping-api} and\n\\texttt{configuration-admin-api} module artifacts:\n\n\\begin{verbatim}\ncompileOnly group: \"com.liferay\", name: \"com.liferay.dynamic.data.mapping.api\", version: \"5.2.0\"\ncompileOnly group: \"com.liferay\", name: \"com.liferay.configuration.admin.api\", version: \"2.0.2\"\n\\end{verbatim}\n\n\\section{Step 2: Write the Configuration\nForm}\\label{step-2-write-the-configuration-form}\n\nThis step requires annotating the class with \\texttt{@DDMForm} to set up\nthe form, and annotating each method with \\texttt{@DDMFormField}. Begin\nby creating the class body, annotating each configuration field\n(interface method) with \\texttt{@DDMFormField}:\n\n\\begin{verbatim}\npublic interface MyFooConfigurationForm {\n\n    @DDMFormField(\n        label = \"%label-key-for-field-1\",\n        tip = \"%description-key-for-field-1\",\n        properties = {\n\n            \"placeholder=%enter-a-value\",\n            \"tooltip=%some-tooltip-text\"\n        }\n    )\n    public String[] textArrayValues();\n\n    @DDMFormField(\n        label = \"%date\",\n        tip = \"%date-description\",\n        type = \"date\")\n    public String date();\n\n    @DDMFormField(\n        label = \"%select\",\n        optionLabels = {\"%foo\", \"%bar\"},\n        optionValues = {\"foo\", \"bar\"},\n        type = \"select\")\n    public String select();\n\n    @DDMFormField(\n        label = \"%numeric\",\n        properties = {\n            \"placeholder=%milliseconds\",\n            \"tooltip=%enter-an-integer-between-1000-and-30000\"\n        },\n        validationErrorMessage = \"%please-enter-an-integer-between-1000-and-30000-milliseconds\",\n        validationExpression = \"(numeric >= 1000) && (numeric <= 30000)\",\n        type = \"numeric\")\n    public String numeric();\n\n    @DDMFormField(\n        label = \"%checkbox\",\n        properties = \"showAsSwitcher=true\")\n    public boolean checkbox();\n\n}\n\\end{verbatim}\n\nOnce the field annotations are in place, lay out the form itself, right\nabove the class declaration. This example shows the layout of the\n\\texttt{UserFileUploadsConfigurationForm}, so that you can see the\nresulting form via the below screenshot:\n\n\\begin{verbatim}\n@DDMForm\n@DDMFormLayout(\n    paginationMode = com.liferay.dynamic.data.mapping.model.DDMFormLayout.SINGLE_PAGE_MODE,\n    value = {\n        @DDMFormLayoutPage(\n            {\n                @DDMFormLayoutRow(\n                    {\n                        @DDMFormLayoutColumn(\n                            size = 12,\n                            value = {\n                                \"imageCheckToken\", \"imageDefaultUseInitials\",\n                                \"imageMaxSize\"\n                            }\n                        )\n                    }\n                ),\n                @DDMFormLayoutRow(\n                    {\n                        @DDMFormLayoutColumn(\n                            size = 6, value = \"imageMaxHeight\"\n                        ),\n                        @DDMFormLayoutColumn(size = 6, value = \"imageMaxWidth\")\n                    }\n                )\n            }\n        )\n    }\n)\npublic interface MyFooConfigurationForm {\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/configuration-ddm-form.png}\n\\caption{The DDM annotations are used to lay out this configuration\nform.}\n\\end{figure}\n\nNext, you must make sure the configuration framework knows about your\nslick form.\n\n\\section{Step 3: Write the Form\nDeclaration}\\label{step-3-write-the-form-declaration}\n\nCreate a new implementation of \\texttt{ConfigurationDDMFormDeclaration}\nto register your new configuration form class:\n\n\\begin{verbatim}\npackage com.liferay.docs.my.foo.configuration.definition;\n\nimport com.liferay.configuration.admin.definition.ConfigurationDDMFormDeclaration;\nimport org.osgi.service.component.annotations.Component;\n...\n\n@Component(\n    immediate = true,\n    property = \"configurationPid=com.liferay.docs.my.foo.configuration.MyFooConfiguration\",\n    service = ConfigurationDDMFormDeclaration.class\n)\npublic class MyFooConfigurationDDMFormDeclaration\n    implements ConfigurationDDMFormDeclaration {\n\n    @Override\n    public Class<?> getDDMFormClass() {\n        return MyFooConfigurationForm.class;\n    }\n\n}\n\\end{verbatim}\n\nThe \\texttt{configurationPid} must match the fully qualified class name\nof the configuration interface.\n\nNow your configuration class is backed by the form-building power of\nLiferay's native \\href{/docs/7-2/user/-/knowledge_base/u/forms}{Forms\napplication}.\n\nTo see how this is done for one of Liferay's own configurations, check\nout all of the configuration classes for the User Images configuration\n(Control Panel → Configuration → System Settings → User Images):\n\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/users-admin/users-admin-api/src/main/java/com/liferay/users/admin/configuration/definition/UserFileUploadsConfigurationForm.java}{\\texttt{UserFileUploadsConfigurationForm}}\n\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/users-admin/users-admin-api/src/main/java/com/liferay/users/admin/configuration/UserFileUploadsConfiguration.java}{\\texttt{UserFileUploadsConfiguration.java}}\n\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/users-admin/users-admin-web/src/main/java/com/liferay/users/admin/web/internal/configuration/definition/UserFileUploadsConfigurationBeanDeclaration.java}{\\texttt{UserFileUploadsConfigurationBeanDeclaration.java}}\n\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/users-admin/users-admin-web/src/main/java/com/liferay/users/admin/web/internal/configuration/definition/UserFileUploadsConfigurationDDMFormDeclaration.java}{\\texttt{UserFileUploadsConfigurationDDMFormDeclaration.java}}\n\n\\chapter{Upgrading a Legacy App}\\label{upgrading-a-legacy-app}\n\nIf you have an app that was made configurable under an earlier version\nof Liferay DXP, you can upgrade without having to reconfigure any of\nyour app's instances.\n\nIf you have an app that was configurable using the mechanisms of Liferay\nPortal 6.2 and before, refer to\n\\href{/docs/7-0/tutorials/-/knowledge_base/t/transitioning-from-portlet-preferences-to-the-configuration-api}{Transitioning\nfrom Portlet Preferences to the Configuration API}.\n\nIf you have an app with a configuration interface scoped to anything\nother than \\texttt{SYSTEM} and a custom UI for saving configuration\nvalues to \\texttt{PortletPreferences}, you have two options:\n\n\\begin{itemize}\n\\item\n  Keep using your custom UI. Deactivate the auto-generated UI in\n  \\emph{Instance Settings} by setting the scope in your configuration\n  interface to \\texttt{SYSTEM}. This is quick and easy, but won't make\n  your code easier to maintain in the long term.\n\n  For other ways to disable the auto-generated UI, see\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/customizing-the-configuration-user-interface\\#excluding-a-configuration-ui}{Excluding\n  a Configuration UI}\n\\item\n  Write an Upgrade Process to convert your configuration values in\n  \\texttt{PortletPreferences} to an instance-scoped OSGi configuration,\n  using the \\texttt{saveCompanyConfiguration} method in the\n  \\texttt{ConfigurationProvider} interface.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\nYou don't have to use \\texttt{saveCompanyConfiguration}, but doing so\nmeets all the necessary requirements for an upgrade process: it must be\na factory instance with a factory PID of\n\\texttt{Unknown\\ macro:{[}base-pid{]}.scoped}, and it must contain a\n\\texttt{companyId} property.\n\n\\noindent\\hrulefill\n\nThen remove your custom UI. If you're reading configuration values using\n\\texttt{ConfigurationProvider}'s \\texttt{getCompanyConfiguration}\nmethod, the auto-generated UI picks up where you left off, with no need\nto reconfigure anything.\n\n\\chapter{Dynamically Populating Select List Fields in the Configuration\nUI}\\label{dynamically-populating-select-list-fields-in-the-configuration-ui}\n\nYou've always been able to provide a select list for your configuration\noptions by entering each label and value directly in the\n\\texttt{@Meta.AD} annotation of the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-configuration-interface}{Configuration\ninterface}.\n\n\\begin{verbatim}\n@Meta.AD(\n    deflt = \"enabled-with-warning\", name = \"csv-export\",\n    optionLabels = {\"enabled\", \"enabled-with-warning\", \"disabled\"},\n    optionValues = {\"enabled\", \"enabled-with-warning\", \"disabled\"},\n    required = false\n)\npublic String csvExport();\n\\end{verbatim}\n\nNow, thanks to the\n\\href{https://docs.liferay.com/dxp/apps/configuration-admin/latest/javadocs/com/liferay/configuration/admin/definition/ConfigurationFieldOptionsProvider.html}{\\texttt{ConfigurationFieldOptionsProvider}\ninterface}, you can populate select list configurations dynamically,\nusing custom logic.\n\nFollow these steps to populate the select list fields dynamically in\nyour configuration UI:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Use an \\texttt{@Component} annotation to register the\n  \\texttt{ConfigurationFieldOptionsProvider.class} service and include\n  two properties:\n\n  \\texttt{configuration.field.name}: The name of the attribute in the\n  configuration interface\n\n  \\texttt{configuration.pid}: The ID of the corresponding configuration\n  interface (usually the fully qualified class name)\n\n  For example,\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"configuration.field.name=enabledClassNames\",\n        \"configuration.pid=com.liferay.asset.auto.tagger.google.cloud.natural.language.internal.configuration.GCloudNaturalLanguageAssetAutoTaggerCompanyConfiguration\",\n        \"configuration.pid=com.liferay.asset.auto.tagger.opennlp.internal.configuration.OpenNLPDocumentAssetAutoTaggerCompanyConfiguration\"\n    },\n    service = ConfigurationFieldOptionsProvider.class\n)\n\\end{verbatim}\n\\item\n  Implement the \\texttt{ConfigurationFieldOptionsProvider} interface:\n\n\\begin{verbatim}\npublic class MyConfigurationFieldOptionsProvider implements \nConfigurationFieldOptionsProvider {\n    ..\n}\n\\end{verbatim}\n\\item\n  The \\texttt{getOptions} method returns a list of \\texttt{Option}s\n  consisting of the label and value fields. The labels provided here are\n  translated to \\texttt{optionLabels}, and the values as\n  \\texttt{optionValues}, in the configuration interface.\n\n\\begin{verbatim}\npublic List<Option> getOptions() {\n    List<AssetRendererFactory<?>> assetRendererFactories =\n        AssetRendererFactoryRegistryUtil.getAssetRendererFactories(\n            CompanyThreadLocal.getCompanyId());\n\n    Stream<AssetRendererFactory<?>> stream =\n        assetRendererFactories.stream();\n\n    return stream.filter(\n        assetRendererFactory -> {\n            TextExtractor textExtractor =\n                _textExtractorTracker.getTextExtractor(\n                    assetRendererFactory.getClassName());\n\n            return textExtractor != null;\n        }\n    ).map(\n        assetRendererFactory -> new Option() {\n\n            @Override\n            public String getLabel(Locale locale) {\n                return assetRendererFactory.getTypeName(locale);\n            }\n\n            @Override\n            public String getValue() {\n                return assetRendererFactory.getClassName();\n            }\n\n        }\n    ).collect(\n        Collectors.toList()\n    );\n}\n\\end{verbatim}\n\n  This code gets a list of \\texttt{AssetRendererFactory} objects and\n  iterates through the list, populating a new list of \\texttt{Option}s,\n  using the asset's type name as the label and the class name as the\n  value. It comes from the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/asset/asset-auto-tagger-service/src/main/java/com/liferay/asset/auto/tagger/internal/configuration/admin/definition/EnabledClassNamesConfigurationFieldOptionsProvider.java}{\\texttt{EnabledClassNamesConfigurationFieldOptionsProvider}},\n  which populates the configuration field labeled \\emph{Enable Google\n  Cloud Natural Language Text Auto Tagging For} with all the asset types\n  that have registered a \\texttt{TextExtractor}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/configuration-field-options-provider.png}\n  \\caption{The select list in the Google Cloud Natural Language Text\n  Auto Tagging entry is populated programmatically, using the\n  \\texttt{ConfigurationFieldOptionsProvider}.}\n  \\end{figure}\n\\end{enumerate}\n\nThe \\texttt{ConfigurationFieldOptionsProvider} allows you to populate\nselect lists with configuration options defined by your custom logic.\n\n\\chapter{Content Publication\nManagement}\\label{content-publication-management}\n\nManaging content publication is primarily controlled by two frameworks:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[exportimport]{Export/Import}\n\\item\n  \\hyperref[staging]{Staging}\n\\end{itemize}\n\nThese features give you the power to plan page publication and manage\ncontent. You can leverage the APIs offered by these frameworks to manage\nyour app's publication process.\n\nThe Export/Import and Staging frameworks are closely tied together. They\nboth implement the same interfaces and share the same extension points.\nBy implementing one of these frameworks in your app, you automatically\nleverage the other. There are a few simple configurations that can be\nset to customize them separately. You'll learn about this later.\n\nExport/Import can be viewed as the base feature with Staging built on\ntop of it (although they're implemented together). You can visit the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/export-import}{Export/Import}\nframework's articles for the base APIs that both it and the Staging\nframeworks share. You must implement these to implement Staging.\nReference the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/staging}{Staging}\nframework's articles for additional configuration pertaining only to it.\n\nHere are a few of the things you can do with the Export/Import and\nStaging APIs.\n\n\\section{Export/Import}\\label{exportimport}\n\nThe Export/Import feature adds another dimension to your application by\nproviding a framework for producing reusable content and importing\ncontent from other places. By creating\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-archive-lar-file}{LAR\nfiles (Liferay ARchive)}, you can export your data, import it to another\nsystem, or even use this feature to back up your content.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/export-import-preview.png}\n\\caption{Leveraging the Export/Import feature in your app is useful for\nsharing content.}\n\\end{figure}\n\nExport/Import is a default feature for many of Liferay DXP's\nout-of-the-box apps. It offers an intuitive GUI for managing\nexport/import processes and tracking the history of previous\nexport/imports. You can also easily pick subsets of data to export based\non content type, date range, and configurations. Importing content is\ndone using a modern drag-and-drop interface or by selecting the LAR file\nfrom your file system.\n\n\\section{Staging}\\label{staging}\n\nStaging gives you a test environment where you can modify your site and\ntest different configurations without changing your live site. When you\nimplement Staging in your app, its content can be tracked by the staged\nenvironment, allowing users to stage your app's data before releasing it\nto the world.\n\nHere's an example of some functionality you can add to your app with\nStaging's APIs:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Local Staging environment tracking\n\\item\n  Remote Staging environment tracking\n\\item\n  Single asset publishing\n\\item\n  Content tracking on page variations\n\\end{itemize}\n\nContinue on to learn more about the frameworks that bring content\npublication management to life!\n\n\\chapter{Export/Import}\\label{exportimport-1}\n\nThe Export/Import feature exports content from the portal and imports\nexternal content into the portal. Your application is much more site\nadministrator-friendly if users can export/import your application's\nassets. For example, if you want to export your application's assets to\nuse on another installation or you must clear its data but save a copy,\nyou can implement the export feature. Implementing the import feature\nlets you bring your assets/data back into your application.\n\nHere's what you'll learn to do with the Export/Import framework:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Create Staged Models\n\\item\n  Develop Portlet Data Handlers\n\\item\n  Develop Staged Model Data Handlers\n\\item\n  Provide entity-specific local services for Export/Import framework\n\\item\n  Listen to export/import events\n\\item\n  Initiate new export/import processes programmatically\n\\end{itemize}\n\n\\section{Staged Models}\\label{staged-models}\n\nTo track an entity of an application with the Export/Import framework,\nyou must implement the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/StagedModel.html}{\\texttt{StagedModel}}\ninterface in the app's model classes. It provides the behavior contract\nfor entities during the Export/Import and Staging processes. There are\ntwo ways to create staged models for your application's entities:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/generating-staged-models-using-service-builder}{Generate\n  them using Service Builder}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-staged-models-manually}{Implement\n  the appropriate interfaces manually}\n\\end{itemize}\n\nUsing Service Builder to generate your staged models is the easiest way\nto create staged models for your app. You define the necessary columns\nin your \\texttt{service.xml} file and set the \\texttt{uuid} attribute to\n\\texttt{true}. Then you run Service Builder, which generates the\nrequired code for your new staged models.\n\nImplementing the necessary staged model logic \\emph{manually} should be\ndone if you \\textbf{don't} want to extend your model with special\nattributes only required to generate Staging logic (i.e., not needed by\nyour business logic). In this case, you should adapt your business logic\nto meet the Staging framework's needs.\n\nSee the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models}{Developing\nStaged Models} section for more information on the Staged Model\narchitecture.\n\n\\section{Data Handlers}\\label{data-handlers}\n\nYou must implement Data Handlers to use the Export/Import framework to\nprocess\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-archive-lar-file}{LAR\nfiles} in your application. There are two types of data handlers:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Portlet Data Handlers\n\\item\n  Staged Model Data Handlers\n\\end{itemize}\n\nA Portlet Data Handler imports/exports portlet specific data to a LAR\nfile. These classes query and coordinate between staged model data\nhandlers. They also configure the Export/Import and Staging UI options.\n\nA Staged Model Data Handler supplies information about a staged model\n(entity) to the Export/Import framework, defining a display name for the\nUI, deleting an entity, etc. It also exports referenced content.\n\nVisit the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-data-handlers}{Developing\nData Handlers} section for more information.\n\n\\section{Provide Entity Specific Local\nServices}\\label{provide-entity-specific-local-services}\n\nWhen creating data handlers, you must leverage your app's local services\nto perform Export/Import and Staging related tasks for its entities.\nWhen these frameworks operate on entities (i.e., staged models), it\noften cannot manage important information from the entity's local\nservices alone; instead, you're forced to reinvent basic functionality\nso the framework can access it. This is caused by services not sharing a\ncommon ancestor (i.e., interface or base class).\n\nThe \\emph{Staged Model Repository} framework removes this barrier by\nlinking an app's staged model to a local service.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/staged-model-repository.png}\n\\caption{Staged Model Repositories provide a Staging-specific layer of\nfunctionality for your local services.}\n\\end{figure}\n\nThis lets the Staging framework call a staged model repository\nindependently based on the entity being processed. This gives you access\nto entity-specific methods tailored specifically for the staged model\ndata you're handling.\n\n\\section{Export/Import Event\nListeners}\\label{exportimport-event-listeners}\n\nThe \\texttt{ExportImportLifecycleListener} framework is for listening\nfor certain staging or export/import events (like export successes and\nimport failures) during the publication process so you can take some\naction. You can also listen for processes comprised of many events and\ntake action when these processes are initiated. For example, you can\nlisten for when\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Staging has started\n\\item\n  A portlet export has failed\n\\item\n  An entity export has succeeded\n\\end{itemize}\n\nAfter an event is triggered, you can take an action like these:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Print information about the event to your console\n\\item\n  When an import process has completed, clear the cache.\n\\end{itemize}\n\nFor a complete list of events you can listen for, see\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lifecycle/ExportImportLifecycleConstants.html}{\\texttt{ExportImportLifecycleConstants}}.\n\nYou must extend one of the two Base listener classes:\n\n\\begin{itemize}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lifecycle/BaseExportImportLifecycleListener.html}{\\texttt{BaseExportImportLifecycleListener}}:\n  listens for specific \\emph{events} during a lifecycle. For example, if\n  a layout export fails, you might take some action.\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lifecycle/BaseProcessExportImportLifecycleListener.html}{\\texttt{BaseProcessExportImportLifecycleListener}}:\n  listens for \\emph{processes} during a lifecycle. A process usually\n  consists of many individual events. For example, if a site publication\n  fails, you might take some action. Methods provided by this base class\n  are only run once when the desired process action occurs.\n\\end{itemize}\n\nWhat's the difference between events and processes?\n\n\\textbf{Events:} particular actions that occur during processing\n(example event listener:\n\\href{https://docs.liferay.com/dxp/apps/web-experience/latest/javadocs/com/liferay/exportimport/lifecycle/CacheExportImportLifecycleListener.html}{\\texttt{CacheExportImportLifecycleListener}}).\n\n\\textbf{Processes:} longer running groups of events (example process\nlistener:\n\\href{https://docs.liferay.com/dxp/apps/web-experience/latest/javadocs/com/liferay/exportimport/lifecycle/ExportImportProcessCallbackLifecycleListener.html}{\\texttt{ExportImportProcessCallbackLifecycleListener}}).\n\nUse the listener type that is most appropriate for your use case.\n\n\\section{Export/Import Processes}\\label{exportimport-processes}\n\nYou can start the process programmatically instead of through the UI.\nThis lets you provide new interfaces or mimic the functionality of these\nfeatures in your own application.\n\nTo initiate an export/import or staging process, you must pass in an\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/model/ExportImportConfiguration.html}{\\texttt{ExportImportConfiguration}}\nobject. This object encapsulates many parameters and settings that are\nrequired while the export/import is running. Having one single object\nwith all your necessary data makes executing these frameworks quick and\neasy.\n\nFor example, when implementing export, you must call services offered by\nthe\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/service/ExportImportService.html}{\\texttt{ExportImportService}}\ninterface. All the methods in this interface require an\n\\texttt{ExportImportConfiguration} object. You can generate these\nconfiguration objects, so you can easily pass them in your service\nmethods.\n\nThere are three factory classes that are useful to create an\n\\texttt{ExportImportConfiguration} object:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/configuration/ExportImportConfigurationSettingsMapFactory.html}{\\texttt{ExportImportConfigurationSettingsMapFactory}}:\n  provides many \\texttt{build} methods to create settings maps for\n  various scenarios like importing, exporting, and publishing layouts\n  and portlets. For example, you can reference\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-impl/com/liferay/portal/service/impl/UserGroupLocalServiceImpl.html\\#exportLayouts-long-java.util.Map-}{\\texttt{UserGroupLocalServiceImpl.exportLayouts(...)}}\n  and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-impl/com/liferay/portal/service/impl/GroupLocalServiceImpl.html\\#addDefaultGuestPublicLayoutsByLAR-com.liferay.portal.kernel.model.Group-java.io.File-}{\\texttt{GroupLocalServiceImpl.addDefaultGuestPublicLayoutsByLAR(...)}}.\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/configuration/ExportImportConfigurationFactory.html}{\\texttt{ExportImportConfigurationFactory}}:\n  This factory builds \\texttt{ExportImportConfiguration} objects used\n  for default local/remote publishing.\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/configuration/ExportImportConfigurationParameterMapFactory.html}{\\texttt{ExportImportConfigurationParameterMapFactory}}:\n  This factory builds parameter maps, which are required during\n  export/import and publishing.\n\\end{itemize}\n\nThere are two important service interfaces that primarily use\n\\texttt{ExportImportConfiguration} objects for exporting, importing, and\nstaging:\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/service/ExportImportLocalService.html}{\\texttt{ExportImportLocalService}}\nand\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/service/StagingLocalService.html}{\\texttt{StagingLocalService}}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you're not calling the export/import or staging\nservice methods from an OSGi module, you should not use the interface.\nThe Liferay OSGi container automatically handles interface referencing,\nwhich is why using the interface is permitted for modules. If you're\ncalling export/import or staging service methods outside of a module,\nyou should use their service Util classes (e.g.,\n\\texttt{ExportImportLocalServiceUtil}).\n\n\\noindent\\hrulefill\n\nIt's also important to know that \\texttt{ExportImportConfiguration} is a\nLiferay DXP entity, similar to \\texttt{User} or \\texttt{Group}. This\nmeans that the \\texttt{ExportImportConfiguration} framework offers local\nand remote services, models, persistence classes, and more.\n\n\\chapter{Developing Staged Models}\\label{developing-staged-models}\n\nTo track an entity of an application with the Export/Import framework,\nyou must implement the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/StagedModel.html}{\\texttt{StagedModel}}\ninterface in the app's model classes. It provides the behavior contract\nfor entities during the Staging process. For example, the Bookmarks\napplication manages\n\\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/bookmarks/model/BookmarksEntry.html}{\\texttt{BookmarksEntry}}s\nand\n\\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/bookmarks/model/BookmarksFolder.html}{\\texttt{BookmarksFolder}}s,\nand both implement the \\texttt{StagedModel} interface. Once you've\nconfigured your staged models, you can create staged model data\nhandlers, which supply information about a staged model (entity) and its\nreferenced content to the Export/Import and Staging frameworks. See the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-data-handlers}{Developing\nData Handlers} section for more information.\n\nThere are two ways to create staged models for your application's\nentities:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/generating-staged-models-using-service-builder}{Using\n  Service Builder to generate the required Staging implementations}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-staged-models-manually}{Implementing\n  the required Staging interfaces manually}\n\\end{itemize}\n\nYou can follow step-by-step procedures for creating staged models for\nyour entities by visiting their respective articles.\n\nContinue on to learn more about Staged Models!\n\n\\section{Staged Model Interfaces}\\label{staged-model-interfaces}\n\nThe\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/StagedModel.html}{\\texttt{StagedModel}}\ninterface must be implemented by your app's model classes, but this is\ntypically done through inheritance by implementing one of the interfaces\nthat extend the base interface:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/StagedAuditedModel.html}{\\texttt{StagedAuditedModel}}\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/StagedGroupedModel.html}{\\texttt{StagedGroupedModel}}\n\\end{itemize}\n\nYou must implement these when you want to use certain features of the\nStaging framework like automatic group mapping or entity level\n\\emph{Last Publish Date} handling. So how do you choose which is right\nfor you?\n\nThe \\texttt{StagedAuditedModel} interface provides all the audit fields\nto the model that implements it. You can check the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/AuditedModel.html}{\\texttt{AuditedModel}}\ninterface for the specific audit fields provided. The\n\\texttt{StagedAuditedModel} interface is for models that function\nindependently from the group concept (sometimes referred to as company\nmodels). If your model is a group model, you should not implement the\n\\texttt{StagedAuditedModel} interface.\n\nThe \\texttt{StagedGroupedModel} interface must be implemented for group\nmodels. For example, if your application requires the \\texttt{groupId}\ncolumn, your model is a group model. If your model satisfies both the\n\\texttt{StagedGroupModel} and \\texttt{StagedAuditedModel} requirements,\nit should implement \\texttt{StagedGroupedModel}. Your model should only\nimplement the \\texttt{StagedAuditedModel} if it doesn't fulfill the\ngrouped model needs, but does fulfill the audited model needs. If your\nmodel does not fulfill either the \\texttt{StagedAuditedModel} or\n\\texttt{StagedGroupedModel} requirements, you should implement the base\n\\texttt{StagedModel} interface.\n\nAs an example for extending your model class, you can visit the\n\\href{https://docs.liferay.com/dxp/apps/collaboration/latest/javadocs/com/liferay/bookmarks/model/BookmarksEntryModel.html}{\\texttt{BookmarksEntryModel}}\nclass, which extends the \\texttt{StagedGroupedModel} interface; this is\ndone because bookmark entries are group models.\n\n\\begin{verbatim}\npublic interface BookmarksEntryModel extends BaseModel<BookmarksEntry>,\n    ShardedModel, StagedGroupedModel, TrashedModel, WorkflowedModel {\n\\end{verbatim}\n\nThose are the differences between staged model interfaces.\n\n\\section{Staged Model Attributes}\\label{staged-model-attributes}\n\nOne of the most important attributes used by the Staging framework is\nthe UUID (Universally Unique Identifier). This attribute must be set to\n\\texttt{true} in your \\texttt{service.xml} file for Service Builder to\nrecognize your model as an eligible staged model. The UUID\ndifferentiates entities between environments. Because the UUID always\nremains the same, it's unique across multiple systems. Why is this so\nimportant?\n\nSuppose you're using\n\\href{/docs/7-2/user/-/knowledge_base/u/enabling-remote-live-staging}{remote\nstaging} and you create a new entity on your local staging site and\npublish it to your remote live site. When you go back to modify the\nentity on your local site and want to publish those changes, the UUID\nshows that the local and remote entities are the same. The Staging\nframework can thus recognize the original entity on the remote site and\nupdate it. The UUID provides that.\n\nIn addition to the UUID, there are several columns that must be defined\nin your \\texttt{service.xml} file for Service Builder to define your\nmodel as a staged model:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{companyId}\n\\item\n  \\texttt{createDate}\n\\item\n  \\texttt{modifiedDate}\n\\end{itemize}\n\nIf you want a staged grouped model, also include the \\texttt{groupId}\nand \\texttt{lastPublishDate} columns. If you want a staged audited\nmodel, include the \\texttt{userId} and \\texttt{userName} columns.\n\n\\section{Adapting Your Business Logic to Build Staged\nModels}\\label{adapting-your-business-logic-to-build-staged-models}\n\nWhat if you don't want to extend your model with special attributes your\nbusiness logic doesn't need (removing the ability to leverage Service\nBuilder's auto-generation of staged models)? In this case, you should\nadapt your business logic to meet the Staging framework's needs. Liferay\nprovides the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/adapter/builder/ModelAdapterBuilder.html}{\\texttt{ModelAdapterBuilder}}\nframework, which lets you adapt your model classes to staged models.\n\nAs an example, assume you have a completed app and you want it to work\nwith Staging. Your app, however, does not require a UUID for any of its\nentities, and therefore, does not provide them. Instead of configuring\nyour app to handle UUIDs just for the sake of generating staged models,\nyou can leverage the Model Adapter Builder to build your staged models.\n\nAnother example for building staged models from scratch is for\napplications that use REST services instead of the database to access\ntheir attributes. Since this kind of app pulls its attributes from a\nremote system, it is more convenient to build your staged models\nyourself instead of relying on Service Builder, which is database\ndriven.\n\nTo adapt your model classes to staged models, follow the steps outlined\nbelow:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a \\texttt{Staged{[}Entity{]}} interface that extends the\n  model-specific interface (e.g., \\texttt{{[}Entity{]}}) and the\n  appropriate staged model interface (e.g., \\texttt{StagedModel}). This\n  class serves as the Staged Model Adapter.\n\\item\n  Create a \\texttt{Staged{[}Entity{]}Impl} class that implements the\n  \\texttt{Staged{[}Entity{]}} interface and provides necessary logic for\n  your entity model to be recognized as a staged model.\n\\item\n  Create a \\texttt{Staged{[}Entity{]}ModelAdapterBuilder} class that\n  implements\n  \\texttt{ModelAdapterBuilder\\textless{}{[}Entity{]},\\ Staged{[}Entity{]}\\textgreater{}}.\n  This class adapts the original model to the newly created Staged Model\n  Adapter.\n\\item\n  Adapt your existing model and call one of the provided APIs to export\n  or import the entity automatically.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/staged-model-adapter-diagram.png}\n\\caption{The Staged Model Adapter class extends your entity and staged\nmodel interfaces.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/model-adapter-builder-diagram.png}\n\\caption{The Model Adapter Builder gets an instance of the model and\noutputs a staged model.}\n\\end{figure}\n\nTo step through the process for leveraging the Model Adapter Builder for\nan existing app, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-staged-models-manually}{Creating\nStaged Models Manually}.\n\n\\chapter{Generating Staged Models Using Service\nBuilder}\\label{generating-staged-models-using-service-builder}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nInstead of having to create staged models for your app manually, you can\nleverage\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} to generate the necessary staged model logic for you. If your\napp doesn't use Liferay's Service Builder, you must configure it in your\nproject. This tutorial assumes you have a Service Builder project with\n\\texttt{*api} and \\texttt{*service} modules. If you want to follow along\nwith this tutorial, download the staged-model-example Service Builder\nproject (Gradle-based).\n\nYou'll track the Service Builder-generated changes applied to an entity\nmodel file to observe how staged models are assigned to your entity.\nKeep in mind the specific\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models\\#staged-model-attributes}{staged\nattributes} necessary for each staged model. Depending on the attributes\ndefined in your \\texttt{service.xml} file, Service Builder assigns your\nentity model to a specific staged model type.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to your project's \\texttt{*service} module at the command\n  line. Run Service Builder (e.g., \\texttt{gradlew\\ buildService}) to\n  generate your project's models based on the current\n  \\texttt{service.xml} configuration.\n\\item\n  Open your project's \\texttt{{[}Entity{]}Model.java} interface and\n  observe the inherited interfaces.\n\n\\begin{verbatim}\npublic interface FooModel extends BaseModel<Foo>, ShardedModel, StagedModel {\n\\end{verbatim}\n\n  Your model was generated as a staged model! This is because the\n  \\texttt{service.xml} file sets the UUID to \\texttt{true} and defines\n  the \\texttt{companyId}, \\texttt{createDate}, and \\texttt{modifiedDate}\n  columns. There is much more logic generated for your app behind the\n  scenes, but this shows that Service Builder deemed your entity\n  eligible for the Staging and Export/Import frameworks.\n\\item\n  Add the \\texttt{userId} and \\texttt{userName} columns to your\n  \\texttt{service.xml} file:\n\n\\begin{verbatim}\n<column name=\"userId\" type=\"long\" />\n<column name=\"userName\" type=\"String\" />\n\\end{verbatim}\n\\item\n  Rerun Service Builder and observe your \\texttt{{[}Entity{]}Model.java}\n  interface again:\n\n\\begin{verbatim}\npublic interface FooModel extends BaseModel<Foo>, GroupedModel, ShardedModel,\n    StagedAuditedModel {\n\\end{verbatim}\n\n  Your model is now a staged audited model!\n\\item\n  Add the \\texttt{lastPublishDate} column to your \\texttt{service.xml}\n  file:\n\n\\begin{verbatim}\n<column name=\"lastPublishDate\" type=\"Date\" />\n\\end{verbatim}\n\\item\n  Rerun Service Builder and observe your \\texttt{{[}Entity{]}Model.java}\n  interface again:\n\n\\begin{verbatim}\npublic interface FooModel extends BaseModel<Foo>, ShardedModel,\n    StagedGroupedModel {\n\\end{verbatim}\n\n  Your model is now a staged grouped model! The \\texttt{groupId} column\n  is also required to extend the \\texttt{StagedGroupedModel} interface,\n  but it was already defined in the original \\texttt{service.xml} file.\n\\end{enumerate}\n\nFantastic! You've witnessed firsthand how easy it is to generate staged\nmodels using Service Builder.\n\n\\chapter{Creating Staged Models\nManually}\\label{creating-staged-models-manually}\n\nThere are times when using\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/generating-staged-models-using-service-builder}{Service\nBuilder to generate your staged models} is not practical. In these\ncases, you should create your staged models manually. Make sure to read\nthe\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models\\#adapting-your-business-logic-to-build-staged-models}{Adapting\nYour Business Logic to Build Staged Models} section to determine if\ncreating staged models manually is beneficial for your use case.\n\nIn this tutorial, you'll explore how the Asset Link framework (a Liferay\nDXP framework used for\n\\href{/docs/7-2/user/-/knowledge_base/u/defining-content-relationships}{relating\nassets}) manually creates staged models. This framework is separate from\nExport/Import and is referenced solely as an example for how to leverage\nthe\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/adapter/builder/ModelAdapterBuilder.html}{ModelAdapterBuilder}\nframework, which lets you adapt your model classes to staged models.\n\nFollow the steps below to leverage the Model Adapter Builder in your\napp.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new interface that extends one of the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models\\#staged-model-interfaces}{staged\n  model interfaces} and your model specific interface. For example,\n\n\\begin{verbatim}\npublic interface StagedAssetLink extends AssetLink, StagedModel {\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Staged model interfaces typically follow the `Staged[Entity]`\n naming convention. The Asset Link framework uses a generic entity called\n `AssetLink`.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Define methods required for your model to qualify as a staged model.\n  For asset links, methods for retrieving entry UUIDs (among others) are\n  defined:\n\n\\begin{verbatim}\npublic String getEntry1Uuid();\n\npublic String getEntry2Uuid();\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Asset links do not provide UUIDs by default; however, they still\n need to be tracked in the Staging and Export/Import frameworks. Therefore,\n they require staged models. Since they don't provide a UUID, Service\n Builder cannot generate staged models for asset links. The Asset Link\n framework has to create staged models differently using the Model Adapter\n Builder.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nThese will be implemented by a new implementation class later. \n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Create an implementation class that implements your new\n  \\texttt{Staged{[}Entity{]}}. For example, the Asset Link framework\n  does this:\n\n\\begin{verbatim}\npublic class StagedAssetLinkImpl implements StagedAssetLink {\n\n}\n\\end{verbatim}\n\n  This class provides necessary logic for your entity model to be\n  recognized as a staged model. Below is a subset of logic in the\n  example \\texttt{StagedAssetLinkImpl} class used to populate UUIDs for\n  asset link entries:\n\n\\begin{verbatim}\npublic StagedAssetLinkImpl(AssetLink assetLink) {\n     _assetLink = assetLink;\n\n    ...\n\n     populateUuid();\n}\n\n@Override\npublic String getEntry1Uuid() {\n    if (Validator.isNotNull(_entry1Uuid)) {\n        return _entry1Uuid;\n    }\n\n    populateEntry1Attributes();\n\n    return _entry1Uuid;\n}\n\n@Override\npublic String getEntry2Uuid() {\n    if (Validator.isNotNull(_entry2Uuid)) {\n            return _entry2Uuid;\n    }\n\n    populateEntry2Attributes();\n\n    return _entry2Uuid;\n}\n\nprotected void populateEntry1Attributes() {\n\n    ...\n\n    AssetEntry entry1 = AssetEntryLocalServiceUtil.fetchAssetEntry(\n        _assetLink.getEntryId1());\n\n    ...\n\n    _entry1Uuid = entry1.getClassUuid();\n}\n\nprotected void populateEntry2Attributes() {\n\n    ...\n\n    AssetEntry entry2 = AssetEntryLocalServiceUtil.fetchAssetEntry(\n        _assetLink.getEntryId2());\n\n    ...\n\n    _entry2Uuid = entry2.getClassUuid();\n}\n\nprotected void populateUuid() {\n\n    ...\n\n    String entry1Uuid = getEntry1Uuid();\n    String entry2Uuid = getEntry2Uuid();\n\n    ...\n\n    _uuid = entry1Uuid + StringPool.POUND + entry2Uuid;\n        }\n}\n\nprivate AssetLink _assetLink;\nprivate String _entry1Uuid;\nprivate String _entry2Uuid;\nprivate String _uuid;\n\\end{verbatim}\n\n  This logic retrieves asset link entries and populates UUIDs for them\n  usable by the Staging and Export/Import frameworks. With the newly\n  generated UUIDs, asset link model classes can be converted to staged\n  models.\n\\item\n  Create a Model Adapter Builder class and implement the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/adapter/builder/ModelAdapterBuilder.html}{ModelAdapterBuilder}\n  interface. You should define the entity type and your Staged Model\n  Adapter class when implementing the interface:\n\n\\begin{verbatim}\npublic class StagedAssetLinkModelAdapterBuilder\n    implements ModelAdapterBuilder<AssetLink, StagedAssetLink> {\n\n    @Override\n    public StagedAssetLink build(AssetLink assetLink) {\n        return new StagedAssetLinkImpl(assetLink);\n    }\n\n}\n\\end{verbatim}\n\n  For the \\texttt{StagedAssetLinkModelAdapterBuilder}, the entity type\n  is \\texttt{AssetLink} and the Staged Model Adapter is\n  \\texttt{StagedAssetLink}. Your app should follow a similar design. The\n  Model Adapter Builder outputs a new instance of the\n  \\texttt{Staged{[}Entity{]}Impl} object.\n\\item\n  Now you need to adapt your existing business logic to call the\n  provided APIs. You can call the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/adapter/ModelAdapterUtil.html}{ModelAdapterUtil}\n  class to create an instance of your Staged Model Adapter:\n\n\\begin{verbatim}\nStagedAssetLink stagedAssetLink = ModelAdapterUtil.adapt(\n    assetLink, AssetLink.class, StagedAssetLink.class);\n\\end{verbatim}\n\n  Once you've created\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-staged-model-data-handlers}{Staged\n  Model Data Handlers}, you can begin exporting/importing your now\n  Staging-compatible entities:\n\n\\begin{verbatim}\nStagedModelDataHandlerUtil.exportStagedModel(\n    portletDataContext, stagedAssetLink);\n\\end{verbatim}\n\\end{enumerate}\n\nAwesome! You've successfully adapted your business logic to build staged\nmodels!\n\n\\chapter{Developing Data Handlers}\\label{developing-data-handlers}\n\nA common requirement for data driven applications is to import and\nexport data. This \\emph{could} be accomplished by accessing your\ndatabase directly and running SQL queries to export/import data;\nhowever, this has several drawbacks:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Working with different database vendors might require customized SQL\n  scripts.\n\\item\n  Access to the database may be tightly controlled, restricting the\n  ability to export/import on demand.\n\\item\n  You'd have to come up with your own means of storing and parsing the\n  data.\n\\end{itemize}\n\nLiferay provides data handlers as a convenient and reliable way to\nexport/import your data (as a LAR file).\n\nThere are two types of data handlers:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Portlet Data Handlers\n\\item\n  Staged Model Data Handlers\n\\end{itemize}\n\nA Portlet Data Handler imports/exports portlet specific data to a LAR\nfile. These classes only have the role of querying and coordinating\nbetween staged model data handlers. For example, the Bookmarks\napplication's portlet data handler tracks system events dealing with\nBookmarks entities. It also configures the Export/Import UI options for\nthe Bookmarks application.\n\nA Staged Model Data Handler supplies information about a staged model\n(entity) to the Export/Import framework, defining a display name for the\nUI, deleting an entity, and exporting referenced content. For example,\nif a Bookmarks entry resides in a Bookmarks folder, the\n\\texttt{BookmarksEntry} staged model data handler invokes the export of\nthe \\texttt{BookmarksFolder}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/data-handler-diagram.png}\n\\caption{The Data Handler framework uses portlet data handlers and\nstaged model data handlers to track and export/import portlet and staged\nmodel information, respectively.}\n\\end{figure}\n\nYou're not required to implement a staged model data handler for every\nentity in your application, but they're necessary for any entity you\nwant to export/import or have the staging framework track.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Creating data handlers for your app means it's\nautomatically tracked by the Staging framework. You can further\ncustomize how Staging handles your app, but creating staged models and\ndata handlers is what registers your app for Staging.\n\n\\noindent\\hrulefill\n\nBefore implementing data handlers, make sure your application is ready\nfor the Export/Import and Staging frameworks by creating\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models}{staged\nmodels}.\n\n\\section{\\texorpdfstring{Understanding the \\texttt{PortletDataHandler}\nInterface}{Understanding the PortletDataHandler Interface}}\\label{understanding-the-portletdatahandler-interface}\n\nA Portlet Data Handler imports/exports portlet specific data to a LAR\nfile. These classes query and coordinate between\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-staged-model-data-handlers}{staged\nmodel data handlers}.\n\nTo create a portlet data handler for your staged model, you must\nimplement the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/PortletDataHandler.html}{\\texttt{PortletDataHandler}}\ninterface by extending the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/BasePortletDataHandler.html}{\\texttt{BasePortletDataHandler}}\nclass. Visit the API reference documentation for this interface/class\nfor useful information on the methods provided.\n\nSome guidelines for implementing the \\texttt{PortletDataHandler}\ninterface are provided below:\n\nThe \\texttt{@Component} annotation section above the implementation\nclass's declaration registers the class as a portlet data handler in the\nOSGi service registry. There are a few annotation attributes you should\nset:\n\n\\texttt{immediate}: activates the component immediately once its\nprovided module has started.\n\n\\texttt{property}: sets various properties for the component service.\nYou must associate the portlet you wish to handle with this service so\nthey function properly in the export/import environment. You should have\none portlet data handler for each portlet (e.g., Bookmarks and Bookmarks\nAdmin).\n\n\\texttt{service}: points to the \\texttt{PortletDataHandler.class}\ninterface.\n\nThe \\texttt{activate} method sets what the portlet data handler\ncontrols. It also configures the portlet's Export/Import and Staging UI.\nThis method is called during initialization of the component by the\n\\href{https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Activate.html}{\\texttt{@Activate}}\nannotation; it's invoked after dependencies are set and before services\nare registered. Five callable \\texttt{set} methods are described below:\n\n\\texttt{setDataPortletPreferences}: sets portlet preferences your app\nshould handle.\n\n\\texttt{setDeletionSystemEventStagedModelTypes}: sets the staged model\ndeletions that the portlet data handler should track. For example, the\nBookmarks app tracks Bookmark entries and folders.\n\n\\texttt{setPublishToLiveByDefault}: controls whether your app is\nselected to publish on the Publication screen by default.\n\n\\texttt{setExportControls}: adds fine grained controls over\nexport/import behavior rendered in the Export/Import UI. This also sets\nthe \\texttt{setImportControls} method. For example, the Bookmarks app\nadds a checkbox to select Bookmarks content (entries) to export.\n\n\\texttt{setStagingControls}: adds fine-grained controls over staging\nbehavior rendered in the Staging UI. For example, this enables your\napp's checkboxes in the Content section displayed during publication.\n\nThe \\texttt{doExportData} method checks if anything should be exported.\nFor example, the Bookmarks app uses this method to check if the user\nselected Bookmarks entries for export by leveraging the\n\\texttt{portletDataContext}. Later, the\n\\texttt{ExportImportActionableDynamicQuery} framework runs a query\nagainst bookmarks folders and entries to find ones which should be\nexported to the LAR file.\n\nThe \\texttt{-ActionableDynamicQuery} classes are generated automatically\nby Service Builder and are available in an app's local services. It\nqueries the database searching for certain Export/Import-specific\nparameters (e.g., \\texttt{createDate} and \\texttt{modifiedDate}), and\nbased on those parameters, finds a list of exportable records from the\nstaged model data handler.\n\nThe \\texttt{doImportData} method queries for entity data in the imported\nLAR file that should be added to the database. This is done by\nextracting XML elements from the LAR file by using utility methods in\nthe\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/StagedModelDataHandlerUtil.html}{\\texttt{StagedModelDataHandlerUtil}}\nclass. The extracted elements tell Liferay DXP what data to import from\nthe LAR file.\n\nThe \\texttt{doPrepareManifestSummary} method calculates the number of\naffected entities based on the current export or staging process.\n\nYou must retrieve and manage the schema version. This is done with the\n\\texttt{getSchemaVersion} and \\texttt{validateSchemaVersion} methods.\nThe schema version is used to perform component related validation\nbefore importing data. It's added to the\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-archive-lar-file}{LAR\nfile} for each application being processed. During import, the\nenvironment's schema version is compared to the LAR file's schema\nversion. Validating the schema version avoids broken data when\nimporting. See the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/PortletDataHandler.html\\#getSchemaVersion--}{\\texttt{PortletDataHandler.getSchemaVersion()}}\nmethod's Javadoc for more information.\n\nNext you'll learn about the \\texttt{StagedModelDataHandler} interface.\n\n\\section{\\texorpdfstring{Understanding the\n\\texttt{StagedModelDataHandler}\nInterface}{Understanding the StagedModelDataHandler Interface}}\\label{understanding-the-stagedmodeldatahandler-interface}\n\nA Staged Model Data Handler supplies information about a staged model\n(entity) to the Export/Import framework, defines a display name for the\nUI, deletes entities, etc. It's also responsible for exporting\nreferenced content. For example, if a Bookmarks entry resides in a\nBookmarks folder, the \\texttt{BookmarksEntry} staged model data handler\ninvokes the export of the \\texttt{BookmarksFolder}.\n\nTo create a staged model data handler for your staged model, you must\nimplement the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/StagedModelDataHandler.html}{\\texttt{StagedModelDataHandler}}\ninterface. This is typically done by extending the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/BaseStagedModelDataHandler.html}{\\texttt{BaseStagedModelDataHandler}}\nclass. Visit the API reference documentation for this interface/class\nfor useful information on the methods provided.\n\nAdditional implementation details for the\n\\texttt{StagedModelDataHandler} interface is provided below:\n\nThe \\texttt{@Component} annotation section above the implementation\nclass's declaration registers the class as a staged model data handler\nin the OSGi service registry. There are two annotation attributes you\nshould set:\n\n\\texttt{immediate}: activates the component immediately once its\nprovided module has started.\n\n\\texttt{service}: points to the \\texttt{StagedModelDataHandler.class}\ninterface.\n\nThe \\texttt{getClassNames} method provides the class names of the models\nthe data handler tracks. As a best practice, you should have one staged\nmodel data handler per staged model. It's possible to use multiple class\ntypes, but this is not recommended.\n\nThe \\texttt{getDisplayName} method retrieves the staged model's display\nname. This is used in the Export/Import UI.\n\nThe \\texttt{doExportStagedModel} method retrieves your app entity's data\nelement from the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/PortletDataContext.html}{\\texttt{PortletDataContext}}\nand then adds the class model characterized by that data element to the\n\\texttt{PortletDataContext}. The \\texttt{PortletDataContext} data\npopulates the LAR file with your application's data during the export\nprocess.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} A staged model data handler should ensure everything\nrequired for its operation is also exported. For example, in the\nBookmarks application, an entry requires its folder to keep the folder\nstructure intact. Therefore, the folder should be exported first\nfollowed by the entry. Note that once an entity has been exported,\nsubsequent calls to the export method don't repeat the export process\nmultiple times, ensuring optimal performance.\n\n\\noindent\\hrulefill\n\nThe \\texttt{doImportStagedModel} method imports the staged model data.\nAn important feature of the import process is that all exported\nreference elements are automatically imported when needed. The method\nmust therefore only find the new assigned ID for the folder before\nimporting the entry.\n\nThe \\texttt{PortletDataContext} keeps the data up-to-date during the\nimport process. The old ID and new ID mapping can be reached by using\nthe \\texttt{portletDataContext.getNewPrimaryKeysMap()} method. This\nmethod also checks the import mode (e.g., \\emph{Copy As New} or\n\\emph{Mirror}) and, depending on the process configuration and existing\nenvironment, adds or updates the entry.\n\nThe \\texttt{doImportMissingReference} method maps the existing staged\nmodel to the old ID in the reference element. When a reference is\nexported as missing, the Data Handler framework calls this method during\nthe import process and updates the new primary key map in the portlet\ndata context.\n\nWhen importing a LAR (i.e., publishing to the live Site), the import\nprocess expects all of an entity's references to be available and\nvalidates their existence.\n\nFor example, if you republish an updated bookmarks folder to the live\nSite and did not include some of its existing entries in the\npublication, these entries are considered missing references.\n\nSince you have references from two separate Sites with differing IDs,\nthe system can't match them during publication. Suppose you export a\nbookmark entry as a missing reference with a primary key (ID) of\n\\texttt{1}. When importing that information, the LAR only provides the\nID but not the entry itself. Therefore, during the import process, the\nData Handler framework searches for the entry to replace by its UUID,\nbut the entry to replace has a different ID (primary key) of \\texttt{2}.\nYou must provide a way to handle these missing references.\n\nTo do this, you must add a method that maps the missing reference's\nprimary key from the export to the existing primary key during import.\nSince the reference's UUID is consistent across systems, it's used to\ncomplete the mapping of differing primary keys. Note that a reference\ncan only be missing on the live Site if it has already been published\npreviously. Therefore, when publishing a bookmarks folder for the first\ntime, the system doesn't check for missing references.\n\nContinue in the section to learn how to develop data handlers for your\napp.\n\n\\chapter{Creating Portlet Data\nHandlers}\\label{creating-portlet-data-handlers}\n\nIn this tutorial, you'll create the\n\\texttt{BookmarksPortletModelDataHandler} class used for the Bookmarks\napplication. The Bookmarks application's portlet data handler tracks\nsystem events dealing with Bookmarks entities. It also configures the\nExport/Import and Staging UI options for the Bookmarks application.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new package in your existing Service Builder project for your\n  data handler classes. For instance, the Bookmarks application's data\n  handler classes reside in the \\texttt{bookmarks-service} module's\n  \\texttt{com.liferay.bookmarks.internal.exportimport.data.handler}\n  package.\n\\item\n  Create your \\texttt{-PortletDataHandler} class for your application in\n  the new \\texttt{-exportimport.data.handler} package and have it\n  implement the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/PortletDataHandler.html}{PortletDataHandler}\n  interface by extending the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/BasePortletDataHandler.html}{BasePortletDataHandler}\n  class. For example,\n\n\\begin{verbatim}\npublic class BookmarksPortletDataHandler extends BasePortletDataHandler {\n\\end{verbatim}\n\\item\n  Create an \\texttt{@Component} annotation section above the class\n  declaration:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"javax.portlet.name=\" + BookmarksPortletKeys.BOOKMARKS\n    },\n    service = PortletDataHandler.class\n)\n\\end{verbatim}\n\\item\n  Set what the portlet data handler controls and the portlet's\n  Export/Import and Staging UIs by adding an \\texttt{activate} method:\n\n\\begin{verbatim}\n@Activate\nprotected void activate() {\n    setDataPortletPreferences(\"rootFolderId\");\n    setDeletionSystemEventStagedModelTypes(\n        new StagedModelType(BookmarksEntry.class),\n        new StagedModelType(BookmarksFolder.class));\n    setExportControls(\n        new PortletDataHandlerBoolean(\n            NAMESPACE, \"entries\", true, false, null,\n            BookmarksEntry.class.getName()));\n    setStagingControls(getExportControls());\n}\n\\end{verbatim}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/export-import-controls.png}\n  \\caption{You can select the content types you'd like to export/import\n  in the UI.}\n  \\end{figure}\n\\item\n  For the Bookmarks portlet data handler to reference its entry and\n  folder staged models successfully, you must set them in your class:\n\n\\begin{verbatim}\n@Reference(unbind = \"-\")\nprotected void setBookmarksEntryLocalService(\n    BookmarksEntryLocalService bookmarksEntryLocalService) {\n\n    _bookmarksEntryLocalService = bookmarksEntryLocalService;\n}\n\n@Reference(unbind = \"-\")\nprotected void setBookmarksFolderLocalService(\n    BookmarksFolderLocalService bookmarksFolderLocalService) {\n\n    _bookmarksFolderLocalService = bookmarksFolderLocalService;\n}\n\nprivate BookmarksEntryLocalService _bookmarksEntryLocalService;\nprivate BookmarksFolderLocalService _bookmarksFolderLocalService;\n\\end{verbatim}\n\n  The \\texttt{set} methods must be annotated with the\n  \\href{https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Reference.html}{@Reference}\n  annotation.\n\n  \\textbf{Important:} Liferay DXP's official Bookmarks app does not use\n  local services in its portlet data handler; instead, it uses the\n  \\href{https://docs.liferay.com/dxp/apps/web-experience/latest/javadocs/com/liferay/exportimport/staged/model/repository/StagedModelRepository.html}{\\texttt{StagedModelRepository}}\n  framework. This is a new framework, but is a viable option when\n  setting up your portlet data handlers. For more information on this,\n  see the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/providing-entity-specific-local-services-for-export-import}{Providing\n  Entity-Specific Local Services for Staging} tutorial section. Since\n  local services are more widely used in custom apps, this tutorial\n  covers those instead.\n\\item\n  You must create a namespace for your entities so the Export/Import\n  framework can identify your application's entities from other entities\n  in Liferay DXP. The Bookmarks application's namespace declaration\n  looks like this:\n\n\\begin{verbatim}\npublic static final String NAMESPACE = \"bookmarks\";\n\\end{verbatim}\n\n  You'll see how this namespace is used later.\n\\item\n  Your portlet data handler should retrieve the data related to its\n  staged model entities so it can properly export/import it. Add this\n  functionality by inserting the following methods:\n\n\\begin{verbatim}\n@Override\nprotected String doExportData(\n        final PortletDataContext portletDataContext, String portletId,\n        PortletPreferences portletPreferences)\n    throws Exception {\n\n    Element rootElement = addExportDataRootElement(portletDataContext);\n\n    if (!portletDataContext.getBooleanParameter(NAMESPACE, \"entries\")) {\n        return getExportDataRootElementString(rootElement);\n    }\n\n    portletDataContext.addPortletPermissions(\n        BookmarksConstants.RESOURCE_NAME);\n\n    rootElement.addAttribute(\n        \"group-id\", String.valueOf(portletDataContext.getScopeGroupId()));\n\n    ExportActionableDynamicQuery folderActionableDynamicQuery =\n        _bookmarksFolderLocalService.\n            getExportActionableDynamicQuery(portletDataContext);\n\n    folderActionableDynamicQuery.performActions();\n\n    ActionableDynamicQuery entryActionableDynamicQuery =\n        _bookmarksEntryLocalService.\n            getExportActionableDynamicQuery(portletDataContext);\n\n    entryActionableDynamicQuery.performActions();\n\n    return getExportDataRootElementString(rootElement);\n}\n\n@Override\nprotected PortletPreferences doImportData(\n        PortletDataContext portletDataContext, String portletId,\n        PortletPreferences portletPreferences, String data)\n    throws Exception {\n\n    if (!portletDataContext.getBooleanParameter(NAMESPACE, \"entries\")) {\n        return null;\n    }\n\n    portletDataContext.importPortletPermissions(\n        BookmarksConstants.RESOURCE_NAME);\n\n    Element foldersElement = portletDataContext.getImportDataGroupElement(\n        BookmarksFolder.class);\n\n    List<Element> folderElements = foldersElement.elements();\n\n    for (Element folderElement : folderElements) {\n        StagedModelDataHandlerUtil.importStagedModel(\n            portletDataContext, folderElement);\n    }\n\n    Element entriesElement = portletDataContext.getImportDataGroupElement(\n            BookmarksEntry.class);\n\n    List<Element> entryElements = entriesElement.elements();\n\n    for (Element entryElement : entryElements) {\n        StagedModelDataHandlerUtil.importStagedModel(\n            portletDataContext, entryElement);\n    }\n\n    return null;\n}\n\\end{verbatim}\n\\item\n  Add a method that counts the number of affected entities based on the\n  current export or staging process:\n\n\\begin{verbatim}\n@Override\nprotected void doPrepareManifestSummary(\n        PortletDataContext portletDataContext,\n        PortletPreferences portletPreferences)\n    throws Exception {\n\n    if (ExportImportDateUtil.isRangeFromLastPublishDate(\n            portletDataContext)) {\n\n        _staging.populateLastPublishDateCounts(\n            portletDataContext,\n            new StagedModelType[] {\n                new StagedModelType(BookmarksEntry.class.getName()),\n                new StagedModelType(BookmarksFolder.class.getName())\n            });\n\n            return;\n        }\n\n    ActionableDynamicQuery entryExportActionableDynamicQuery =\n        _bookmarksEntryLocalService.\n            getExportActionableDynamicQuery(portletDataContext);\n\n    entryExportActionableDynamicQuery.performCount();\n\n    ActionableDynamicQuery folderExportActionableDynamicQuery =\n        _bookmarksFolderLocalService.\n            getExportActionableDynamicQuery(portletDataContext);\n\n    folderExportActionableDynamicQuery.performCount();\n}\n\\end{verbatim}\n\n  This number is displayed in the Export and Staging UI. Note that since\n  the Staging framework traverses the entity graph during export, the\n  built-in components provide an approximate value in some cases.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/manifest-summary-count.png}\n  \\caption{The number of modified Bookmarks entities are displayed in\n  the Export UI.}\n  \\end{figure}\n\\item\n  Set the XML schema version for the XML files included in your exported\n  LAR file:\n\n\\begin{verbatim}\npublic static final String SCHEMA_VERSION = \"1.0.0\";\n\n@Override\npublic String getSchemaVersion() {\n    return SCHEMA_VERSION;\n}\n\n@Override\npublic boolean validateSchemaVersion(String schemaVersion) {\n    return _portletDataHandlerHelper.validateSchemaVersion(\n        schemaVersion, getSchemaVersion());\n}\n\\end{verbatim}\n\\end{enumerate}\n\nAwesome! You've set up your portlet data handler and your application\ncan now support the Export/Import framework and display a UI for it. Be\nsure to also implement staged model data handlers for your staged\nmodels. See the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-staged-model-data-handlers}{Creating\nStaged Model Data Handlers} for more information.\n\n\\chapter{Creating Staged Model Data\nHandlers}\\label{creating-staged-model-data-handlers}\n\nIn this tutorial, you'll create the\n\\texttt{BookmarksStagedModelDataHandler} class used for the Bookmarks\napplication. The Bookmarks application has two staged models: entries\nand folders. Creating data handlers for these two entities is similar,\nso you'll examine how this is done for Bookmark entries. This tutorial\nassumes you've already created\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models}{staged\nmodels}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new package in your existing Service Builder project for your\n  data handler classes. For instance, the Bookmarks application's data\n  handler classes reside in the \\texttt{bookmarks-service} module's\n  \\texttt{com.liferay.bookmarks.internal.exportimport.data.handler}\n  package.\n\\item\n  Create a \\texttt{-StagedModelDataHandler} class in the\n  \\texttt{-exportimport.data.handler} package. The staged model data\n  handler class should extend the\n  \\href{https://docs.liferay.com/dxp/portal/7.1-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/BaseStagedModelDataHandler.html}{\\texttt{BaseStagedModelDataHandler}}\n  class and the entity type should be specified as its parameter. You\n  can see how this was done for the\n  \\texttt{BookmarksEntryStagedModelDataHandler} class below:\n\n\\begin{verbatim}\npublic class BookmarksEntryStagedModelDataHandler\n    extends BaseStagedModelDataHandler<BookmarksEntry> {\n\\end{verbatim}\n\\item\n  Create an \\texttt{@Component} annotation section above the class\n  declaration.\n\n\\begin{verbatim}\n@Component(immediate = true, service = StagedModelDataHandler.class)\n\\end{verbatim}\n\\item\n  Create a getter and setter method for the local service of the staged\n  model for which you want to provide a data handler:\n\n\\begin{verbatim}\n@Override\nprotected BookmarksEntryLocalService getBookmarksEntryLocalService() {\n    return _bookmarksEntryLocalService;\n}\n\n@Reference(unbind = \"-\")\nprotected void setBookmarksEntryLocalService(\n    BookmarksEntryLocalService bookmarksEntryLocalService) {\n\n    _bookmarksEntryLocalService = bookmarksEntryLocalService;\n}\n\nprivate BookmarksEntryLocalService _bookmarksEntryLocalService;\n\\end{verbatim}\n\n  These methods are used to link this data handler with the staged model\n  for bookmark entries.\n\n  \\textbf{Important:} Liferay DXP's official Bookmarks app does not use\n  local services in its staged model data handlers; instead, it uses the\n  \\href{https://docs.liferay.com/dxp/apps/web-experience/latest/javadocs/com/liferay/exportimport/staged/model/repository/StagedModelRepository.html}{\\texttt{StagedModelRepository}}\n  framework. This is a new framework, but is a viable option when\n  setting up your staged model data handlers. For more information on\n  this, see the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/providing-entity-specific-local-services-for-export-import}{Providing\n  Entity-Specific Local Services for Staging} tutorial section. Since\n  local services are more widely used in custom apps, this tutorial\n  covers those instead.\n\\item\n  Provide the class names of the models the data handler tracks. You can\n  do this by overriding the\n  \\href{https://docs.liferay.com/dxp/portal/7.1-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/StagedModelDataHandler.html}{\\texttt{StagedModelDataHandler}}'s\n  \\texttt{getClassnames()} method:\n\n\\begin{verbatim}\npublic static final String[] CLASS_NAMES = {BookmarksEntry.class.getName()};\n\n@Override\npublic String[] getClassNames() {\n    return CLASS_NAMES;\n}\n\\end{verbatim}\n\\item\n  Add a method that retrieves the staged model's display name:\n\n\\begin{verbatim}\n@Override\npublic String getDisplayName(BookmarksEntry entry) {\n    return entry.getName();\n}\n\\end{verbatim}\n\n  The display name is presented with the progress bar during the\n  export/import process.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/staged-model-display-name.png}\n  \\caption{Your staged model data handler provides the display name in\n  the Export/Import UI.}\n  \\end{figure}\n\\item\n  Add methods that import and export your staged model and its\n  references.\n\n\\begin{verbatim}\n@Override\nprotected void doExportStagedModel(\n        PortletDataContext portletDataContext, BookmarksEntry entry)\n    throws Exception {\n\n    if (entry.getFolderId() !=\n            BookmarksFolderConstants.DEFAULT_PARENT_FOLDER_ID) {\n\n        StagedModelDataHandlerUtil.exportReferenceStagedModel(\n            portletDataContext, entry, entry.getFolder(),\n            PortletDataContext.REFERENCE_TYPE_PARENT);\n    }\n\n    Element entryElement = portletDataContext.getExportDataElement(entry);\n\n    portletDataContext.addClassedModel(\n        entryElement, ExportImportPathUtil.getModelPath(entry), entry);\n}\n\n@Override\nprotected void doImportStagedModel(\n        PortletDataContext portletDataContext, BookmarksEntry entry)\n    throws Exception {\n\n    Map<Long, Long> folderIds =\n        (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(\n            BookmarksFolder.class);\n\n    long folderId = MapUtil.getLong(\n        folderIds, entry.getFolderId(), entry.getFolderId());\n\n    ServiceContext serviceContext =\n        portletDataContext.createServiceContext(entry);\n\n    BookmarksEntry importedEntry = null;\n\n    if (portletDataContext.isDataStrategyMirror()) {\n\n        BookmarksEntry existingEntry =\n            _bookmarksEntryLocalService. fetchBookmarksEntryByUuidAndGroupId(\n                entry.getUuid(), portletDataContext.getScopeGroupId());\n\n        if (existingEntry == null) {\n\n            serviceContext.setUuid(entry.getUuid());\n            importedEntry = _bookmarksEntryLocalService.addEntry(                   \n              userId, portletDataContext.getScopeGroupId(), folderId, entry.getName(), entry.getUrl(), entry.getDescription(), serviceContext);\n        }\n        else {\n            importedEntry = _bookmarksEntryLocalService.updateEntry(\n                userId, existingEntry.getEntryId(), portletDataContext.getScopeGroupId(), folderId, entry.getName(), entry.getUrl(), entry.getDescription(),    serviceContext);\n        }\n    }\n    else {\n        importedEntry = _bookmarksEntryLocalService.addEntry(userId, portletDataContext.getScopeGroupId(), folderId,entry.getName(), entry.getUrl(), entry.getDescription(),    serviceContext);\n    }\n\n    portletDataContext.importClassedModel(entry, importedEntry);\n}\n\\end{verbatim}\n\\item\n  Add the \\texttt{doImportMissingReference} method to your class:\n\n\\begin{verbatim}\n@Override\nprotected void doImportMissingReference(\n        PortletDataContext portletDataContext, String uuid, long groupId,\n        long entryId)\n    throws Exception {\n\n    BookmarksEntry existingEntry = fetchMissingReference(uuid, groupId);\n\n    if (existingEntry == null) {\n        return;\n    }\n\n    Map<Long, Long> entryIds =\n        (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(\n            BookmarksEntry.class);\n\n    entryIds.put(entryId, existingEntry.getEntryId());\n}\n\\end{verbatim}\n\\end{enumerate}\n\nFantastic! You've created a data handler for your staged model. The\nExport/Import framework can now track your entity's behavior and data.\nBe sure to also implement a portlet data handler to manage portlet\nspecific data. See the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-portlet-data-handlers}{Creating\nPortlet Data Handlers} article to do this for the Bookmarks app.\n\n\\chapter{Providing Entity-Specific Local Services for\nExport/Import}\\label{providing-entity-specific-local-services-for-exportimport}\n\nData handlers must often call your app's local services to perform\nExport/Import-related tasks for its entities. When the Export/Import\nframework operates on entities (i.e., staged models), it often cannot\nmanage important information from the entity's local services alone. The\n\\emph{Staged Model Repository} framework removes this barrier by linking\nan app's staged model to a local service. This gives you access to\nentity-specific methods tailored specifically for the staged model data\nyou're handling.\n\nWhat kind of \\emph{entity-specific} methods are we talking about here?\nYour data handlers only expose a specific set of actions, like export\nand import methods. The Staged Model Repository framework provides CRUD\noperations for a specific staged model that local services don't expose.\n\nThe staged model repository does not avoid using your app's local\nservices. It only provides an additional layer that provides\nExport/Import-specific functionality. Here's how this works:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{*StagedModelDataHandler}: de-serializes the provided\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-archive-lar-file}{LAR\n  file's} XML into a model.\n\\item\n  \\texttt{*StagedModelRepository}: updates the model based on the\n  environment and business logic, providing entity-specific CRUD\n  operations for Staging purposes (e.g., UUID manipulation).\n\\item\n  Local services are called from the \\texttt{*StagedModelRepository} and\n  handle the remainder of the process.\n\\end{itemize}\n\nPretty cool, right? The first thing you must do is implement the\n\\texttt{StagedModelRepository} interface. You'll explore this next.\n\n\\section{\\texorpdfstring{Understanding the\n\\texttt{StagedModelRepository}\nInterface}{Understanding the StagedModelRepository Interface}}\\label{understanding-the-stagedmodelrepository-interface}\n\nProviding specialized local services for your app's staging\nfunctionality lets you abstract the additional staging-specific\ninformation away from your data handlers. Before you can begin using the\nStaged Model Repository framework in your app, you must implement it.\n\nThe first important task is adding an \\texttt{@Component} annotation\nsection above the implementation class's declaration. This registers the\nclass as a staged model repository in the OSGi service registry. There\nare a few annotation attributes you should set:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{immediate}: activates the component immediately once its\n  provided module has started.\n\\item\n  \\texttt{property}: sets various properties for the component service.\n  You must associate the model class you wish to handle with this\n  service so it's recognized by the data handlers leveraging it. This\n  property must set the fully qualified model class name like this:\n  \\texttt{property\\ =\\ \\ \\ \"model.class.name=FULLY\\_QUALIFIED\\_MODEL\\_CLASS\"}.\n\\item\n  \\texttt{service}: points to the \\texttt{StagedModelRepository.class}\n  interface.\n\\end{itemize}\n\nNext, you must implement the \\texttt{StagedModelRepository} interface.\nImplementations vary greatly based on the app you're implementing it\nfor. There are two common cases you'll experience when implementing its\nmethods:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Methods that require additional Export/Import information injected\n  before calling the local service.\n\\item\n  Methods that can call the local service directly.\n\\end{itemize}\n\nThe\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-a1/modules/apps/bookmarks/bookmarks-service/src/main/java/com/liferay/bookmarks/internal/exportimport/staged/model/repository/BookmarksEntryStagedModelRepository.java\\#L51-L71}{\\texttt{BookmarksEntryStagedModelRepository.addStagedModel(...)}}\nmethod is an example where only calling the local service would not\nsatisfy the staged model data handler's needs (i.e., its UUID\nrequirement). With the staged model repository layer, however, you can\nadd export/import specific requirements on top of the present local\nservices to serve your data handlers' needs.\n\nThe method does this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Sets the user ID and service context based on the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/PortletDataContext.html}{\\texttt{PortletDataContext}}\n  (used to populate the LAR file with your application's data during the\n  export process).\n\\item\n  Sets the UUID, which is required to differentiate staged content\n  between Sites.\n\\item\n  Calls the entity's local service.\n\\end{itemize}\n\nNot every method implementation requires additional export/import\ninformation. For example, deleting Bookmarks Entries and deleting\nBookmarks Entry staged models are functionally the same, so your staged\nmodel repository's method would call the local service directly (e.g.,\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-a1/modules/apps/bookmarks/bookmarks-service/src/main/java/com/liferay/bookmarks/internal/exportimport/staged/model/repository/BookmarksEntryStagedModelRepository.java\\#L73-L78}{\\texttt{BookmarksEntryStagedModelRepository.deleteStagedModel(...)}}).\n\nNext you'll learn about using a Staged Model Repository.\n\n\\section{Using a Staged Model\nRepository}\\label{using-a-staged-model-repository}\n\nYou can leverage a staged model repository by\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Creating a getter and setter method to make a\n  \\texttt{StagedModelRepository} object available to your entity.\n\\item\n  Calling the \\texttt{StagedModelRepository} object to leverage its\n  specialized export/import logic.\n\\end{enumerate}\n\nThe getter and setter methods instantiate a\n\\texttt{StagedModelRepository} object that the staged model data handler\ncan use to access your entity's CRUD operations. The setter method\nshould have an \\texttt{@Reference} annotation listed above its method\nsignature. This injects the component service of the\n\\texttt{*StagedModelRepository} into the staged model repository object.\nThe component service was created when you set the \\texttt{@Component}\nannotation in the implementation class.\n\nOnce you have access to the \\texttt{StagedModelRepository} object, call\nit to use its specialized export/import logic. Now that you have access\nto CRUD operations via the \\texttt{StagedModelRepository} object, you\ncan skip the headache of providing a slew of parameters and additional\nfunctionality in the local service to do simple things like add a\nBookmarks entry. The staged model repository abstracts these\nrequirements away from the data handler.\n\nContinue in the section to learn how to develop staged model\nrepositories for your app.\n\n\\chapter{Implementing the Staged Model Repository\nFramework}\\label{implementing-the-staged-model-repository-framework}\n\nIn this article, you'll step through a quick example that demonstrates\nimplementing the \\texttt{StagedModelRepository} interface to use for a\nstaged model. This example references Liferay's Bookmarks app and\nBookmarks Entry entities.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your app's \\texttt{-service} bundle, create a package that holds\n  your Staged Model Repository classes (e.g.,\n  \\texttt{com.liferay.bookmarks.exportimport.staged.model.repository}).\n  If you do not have a \\texttt{-service} bundle, visit the\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\n  Builder} articles for info on generating an app's services. You must\n  have them to leverage most Export/Import and Staging features.\n\\item\n  Create your \\texttt{-StagedModelRepository} class in the new package\n  and implement the \\texttt{StagedModelRepository} interface in the\n  class' declaration. For example,\n\n\\begin{verbatim}\npublic class BookmarksEntryStagedModelRepository\n    implements StagedModelRepository<BookmarksEntry> {\n\\end{verbatim}\n\n  Be sure also to include the staged model type parameter for this\n  repository (e.g., \\texttt{BookmarksEntry}).\n\\item\n  Add an \\texttt{@Component} annotation for your staged model repository\n  class that looks like this:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = \"model.class.name=com.liferay.bookmarks.model.BookmarksEntry\",\n    service = StagedModelRepository.class\n)\n\\end{verbatim}\n\\item\n  Implement the \\texttt{StagedModelRepository} interface's methods in\n  your staged model repository. You can reference the\n  \\href{https://docs.liferay.com/dxp/apps/web-experience/latest/javadocs/com/liferay/exportimport/staged/model/repository/StagedModelRepository.html}{Javadoc}\n  for this interface to learn what each method is intended for.\n\n  As an example, you'll set a couple method implementations to get a\n  taste for how it works.\n\\item\n  Implement the \\texttt{addStagedModel(...)} method. The Bookmarks entry\n  example looks like this:\n\n\\begin{verbatim}\n@Override\npublic BookmarksEntry addStagedModel(\n        PortletDataContext portletDataContext,\n        BookmarksEntry bookmarksEntry)\n    throws PortalException {\n\n    long userId = portletDataContext.getUserId(\n        bookmarksEntry.getUserUuid());\n\n    ServiceContext serviceContext = portletDataContext.createServiceContext(\n        bookmarksEntry);\n\n    if (portletDataContext.isDataStrategyMirror()) {\n        serviceContext.setUuid(bookmarksEntry.getUuid());\n    }\n\n    return _bookmarksEntryLocalService.addEntry(\n        userId, bookmarksEntry.getGroupId(), bookmarksEntry.getFolderId(),\n        bookmarksEntry.getName(), bookmarksEntry.getUrl(),\n        bookmarksEntry.getDescription(), serviceContext);\n}\n\\end{verbatim}\n\n  This provides the UUID for the local service.\n\\item\n  Not every method implementation requires additional staging\n  information. Implementing the \\texttt{deleteStagedModels} method calls\n  the local service directly.\n\n\\begin{verbatim}\n@Override\npublic void deleteStagedModels(PortletDataContext portletDataContext)\n    throws PortalException {\n\n    _bookmarksEntryLocalService.deleteEntries(\n        portletDataContext.getScopeGroupId(),\n        BookmarksFolderConstants.DEFAULT_PARENT_FOLDER_ID);\n}\n\\end{verbatim}\n\\item\n  Finish implementing the \\texttt{StagedModelRepository} so it's usable\n  in your data handlers.\n\\end{enumerate}\n\nAwesome! You've implemented the Staged Model Repository framework for\nyour app! If you're interested in leveraging this framework after the\nimplementation process, see the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-staged-model-repository-framework}{Using\nthe Staged Model Repository Framework} article.\n\n\\chapter{Using the Staged Model Repository\nFramework}\\label{using-the-staged-model-repository-framework}\n\nLeveraging the Staged Model Repository framework in your app is easy\nonce you've\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/implementing-the-staged-model-repository-framework}{created\nstaged model repository implementation classes}.\n\nYou'll step through a quick example to demonstrate leveraging the\n\\texttt{StagedModelRepository} interface in a staged model data handler.\nThe code snippets originate from Liferay's Bookmarks app and Bookmarks\nEntries.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a getter and setter method to make a\n  \\texttt{StagedModelRepository} object available for the\n  \\texttt{BookmarksEntry} entity:\n\n\\begin{verbatim}\n@Override\nprotected StagedModelRepository<BookmarksEntry> getStagedModelRepository() {\n    return _stagedModelRepository;\n}\n\n@Reference(\n    target = \"(model.class.name=com.liferay.bookmarks.model.BookmarksEntry)\",\n    unbind = \"-\"\n)\nprotected void setStagedModelRepository(\n    StagedModelRepository<BookmarksEntry> stagedModelRepository) {\n\n    _stagedModelRepository = stagedModelRepository;\n}\n\nprivate StagedModelRepository<BookmarksEntry> _stagedModelRepository;\n\\end{verbatim}\n\\item\n  Call your \\texttt{\\_stagedModelRepository} object to leverage its\n  specialized export/import logic. For example,\n\n\\begin{verbatim}\nnewEntry = _stagedModelRepository.updateStagedModel(portletDataContext, importedEntry);\n\\end{verbatim}\n\n  Without the staged model repository logic, you would've called your\n  local service like this:\n\n\\begin{verbatim}\nserviceContext.setUuid(entry.getUuid());\n\nnewEntry = _bookmarksEntryLocalService.addEntry(\n    userId, portletDataContext.getScopeGroupId(), folderId, entry.getName(), entry.getUrl(), entry.getDescription(), serviceContext);\n\\end{verbatim}\n\n  The large number of parameters and UUID setter the local service\n  method requires aren't needed when leveraging the staged model\n  repository.\n\\end{enumerate}\n\nGreat! You've successfully leveraged your staged model repository from a\ndata handler!\n\n\\chapter{Using the Export/Import Lifecycle Listener\nFramework}\\label{using-the-exportimport-lifecycle-listener-framework}\n\nIn this tutorial, you'll learn how to use the\n\\texttt{ExportImportLifecycleListener} framework to listen for\nprocesses/events during the staging and export/import lifecycles.\n\nTo begin creating your lifecycle listener, you must create a module.\nFollow the steps below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  an OSGi module}.\n\\item\n  Create a unique package name in the module's \\texttt{src} directory\n  and create a new Java class in that package. To follow naming\n  conventions, begin the class name with the entity or action name\n  you're processing, followed by \\emph{ExportImportLifecycleListener}\n  (e.g., \\texttt{LoggerExportImportLifecycleListener}).\n\\item\n  You must extend one of the two Base classes provided with the\n  Export/Import Lifecycle Listener framework:\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lifecycle/BaseExportImportLifecycleListener.html}{BaseExportImportLifecycleListener}\n  (event listener) or\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lifecycle/BaseProcessExportImportLifecycleListener.html}{BaseProcessExportImportLifecycleListener}\n  (process listener). To choose, you'll need to consider what parts of a\n  lifecycle you want to listen for (event or process).\n\\item\n  Directly above the class's declaration, insert the following\n  annotation:\n\n\\begin{verbatim}\n@Component(immediate = true, service = ExportImportLifecycleListener.class)\n\\end{verbatim}\n\n  This annotation declares the implementation class of the component and\n  specifies that the portal should start the module immediately.\n\\item\n  Specify the methods you want to implement in your class. As an\n  example, you'll step through the\n  \\href{https://docs.liferay.com/dxp/apps/web-experience/latest/javadocs/com/liferay/exportimport/lifecycle/LoggerExportImportLifecycleListener.html}{LoggerExportImportLifecycleListener}.\n  This listener extends the \\texttt{BaseExportImportLifecycleListener},\n  so you immediately know that it deals with lifecycle events.\n\\item\n  Add the \\texttt{getStagedModelLogFragment(...)} method:\n\n\\begin{verbatim}\nprotected String getStagedModelLogFragment(StagedModel stagedModel) {\n    StringBundler sb = new StringBundler(8);\n\n    sb.append(StringPool.OPEN_CURLY_BRACE);\n    sb.append(\"class: \");\n    sb.append(ExportImportClassedModelUtil.getClassName(stagedModel));\n\n    if (stagedModel instanceof StagedGroupedModel) {\n        StagedGroupedModel stagedGroupedModel =\n            (StagedGroupedModel)stagedModel;\n\n        sb.append(\", groupId: \");\n        sb.append(stagedGroupedModel.getGroupId());\n    }\n\n    sb.append(\", uuid: \");\n    sb.append(stagedModel.getUuid());\n    sb.append(StringPool.CLOSE_CURLY_BRACE);\n\n    return sb.toString();\n}\n\\end{verbatim}\n\n  This retrieves the staged model's log fragment, which is the lifecycle\n  listener's logging information on events.\n\\item\n  Add the \\texttt{isParallel()} method:\n\n\\begin{verbatim}\n@Override\npublic boolean isParallel() {\n    return false;\n}\n\\end{verbatim}\n\n  This determines whether your listener should run in parallel with the\n  import/export process, or if the calling method should stop, execute\n  the listener, and return to where the event was fired after the\n  listener has finished.\n\\item\n  Add the \\texttt{onExportImportLifecycleEvent(...)} method:\n\n\\begin{verbatim}\n@Override\npublic void onExportImportLifecycleEvent(\n        ExportImportLifecycleEvent exportImportLifecycleEvent)\n    throws Exception {\n\n    if (!_log.isDebugEnabled()) {\n        return;\n    }\n\n    super.onExportImportLifecycleEvent(exportImportLifecycleEvent);\n}\n\\end{verbatim}\n\n  This consumes the lifecycle event and passes it through the base\n  class's method (as long as Debug mode is not enabled).\n\\item\n  Each remaining method is called to print logging information for the\n  user. For example, when a layout export fails, logging information\n  directly related to that event is printed:\n\n\\begin{verbatim}\n@Override\nprotected void onLayoutExportFailed(\n        PortletDataContext portletDataContext, Throwable throwable)\n    throws Exception {\n\n    if (!_log.isDebugEnabled()) {\n        return;\n    }\n\n    _log.debug(\n        \"Layout export failed for group \" + portletDataContext.getGroupId(),\n        throwable);\n}\n\\end{verbatim}\n\n  In summary, the \\texttt{LoggerExportImportLifecycleListener} uses the\n  lifecycle listener framework to print messages to the log when an\n  export/import event occurs. You can view the other logging methods\n  implemented for this class\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.0-m2/modules/apps/export-import/export-import-service/src/main/java/com/liferay/exportimport/lifecycle/LoggerExportImportLifecycleListener.java}{here}.\n\\item\n  Once you've successfully created your export/import lifecycle listener\n  module, generate the module's JAR file and copy it to Liferay DXP's\n  \\texttt{osgi/modules} folder.\n\n  Once your module is installed and activated in your instance's service\n  registry, your lifecycle listener is ready for use in your Portal\n  instance.\n\\end{enumerate}\n\nTerrific! You learned about the Export/Import Lifecycle Listener\nframework, and you've learned how to create your own listener for\nevents/processes that occur during export/import of your portal's\ncontent.\n\n\\chapter{Initiating New Export/Import\nProcesses}\\label{initiating-new-exportimport-processes}\n\nIn this tutorial, you'll learn about the\n\\texttt{ExportImportConfiguration} framework and how you can take\nadvantage of provided services and factories to create these controller\nobjects. Once they're created, you can easily implement whatever\nimport/export functionality you need.\n\nYour first step is to create an \\texttt{ExportImportConfiguration}\nobject and use it to initiate your custom export/import or staging\nprocess.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Use the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/configuration/ExportImportConfigurationSettingsMapFactory.html}{\\texttt{ExportImportConfigurationSettingsMapFactory}}\n  class to create a layout export settings map:\n\n\\begin{verbatim}\nMap<String, Serializable> exportLayoutSettingsMap =\n    ExportImportConfigurationSettingsMapFactory.\n        buildExportLayoutSettingsMap(...);\n\\end{verbatim}\n\\item\n  Create the \\texttt{ExportImportConfiguration} object by using an\n  \\emph{add} method in the entity's local service. The map created\n  previously is used as a parameter to create the\n  \\texttt{ExportImportConfiguration} object.\n\n\\begin{verbatim}\nExportImportConfiguration exportImportConfiguration =\n    exportImportConfigurationLocalService.\n        addDraftExportImportConfiguration(\n            user.getUserId(),\n            ExportImportConfigurationConstants.TYPE_EXPORT_LAYOUT,\n            exportLayoutSettingsMap);\n\\end{verbatim}\n\n  The\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/service/ExportImportConfigurationLocalService.html}{ExportImportConfigurationLocalService}\n  provides several useful methods to create and modify your custom\n  \\texttt{ExportImportConfiguration}.\n\\item\n  Call the appropriate service using your newly created\n  \\texttt{ExportImportConfiguration} object to initiate an export/import\n  or staging process. For example,\n\n\\begin{verbatim}\nfiles[0] = exportImportLocalService.exportLayoutsAsFile(\n    exportImportConfiguration);\n\\end{verbatim}\n\n  Notice that your \\texttt{ExportImportConfiguration} object is the only\n  needed parameter in the method. Your configuration object holds all\n  the required parameters and settings necessary to export your layouts\n  from Liferay DXP.\n\\end{enumerate}\n\nIt's that easy! To start your own export/import or staging process, you\nmust create an \\texttt{ExportImportConfiguration} object using a\ncombination of the three provided \\texttt{ExportImportConfiguration}\nfactories (outlined\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/export-import\\#exportimport-processes}{here}).\nOnce you have your configuration object, provide it as a parameter in\none of the many service methods available to you by the Export/Import or\nStaging interfaces to begin your desired process.\n\n\\chapter{Staging}\\label{staging-1}\n\n\\href{docs/7-2/user/-/knowledge_base/u/staging-content-for-publication}{Staging}\nlets users change a Site without affecting the live Site and then\npublish all the changes in one fell swoop. If you include staging\nsupport in your application, your users can stage its content until it's\nready.\n\nFor example, if your application uses the Staging framework and provides\ninformation intended only during a specific holiday, users can save your\napplication's assets specific for that holiday. They reside in the\nStaging environment until they're ready for publishing.\n\nStaging and Export/Import share the same base framework. When publishing\nyour staged content to the live Site, you're essentially importing\ncontent from the staged Site and exporting it to the live Site. This\nmeans that implementing Staging in your app is \\emph{almost} the same as\nimplementing the Export/Import framework. You can visit the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/export-import}{Export/Import}\nframework's articles for the base APIs that both it and the Staging\nframeworks share.\n\nIf your app supports Export/Import, its entities\n(\\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models}{staged\nmodels}) are automatically tracked by Staging with the use of\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-data-handlers}{data\nhandlers}. There are some Staging-specific configurations you can add\nthat are not shared by Export/Import. Some Staging-specific actions you\ncan complete include\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Control Staging UI settings\n\\item\n  Filter Staging-specific processes\n\\item\n  Check for Staging-specific states\n\\end{itemize}\n\nYou'll learn about these next.\n\n\\section{Controlling Staging's UI\nSettings}\\label{controlling-stagings-ui-settings}\n\nYou can control most of Staging's UI from your portlet data handler.\nThis can be done several ways; first, you can configure predefined\nsetter methods in the portlet data handler's \\texttt{activate()} method:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{setStagingControls}: adds fine grained controls over staging\n  behavior that is rendered in the Staging UI. For example, this enables\n  your app's checkboxes in the Content section of the Publication\n  screen. This is usually set like this:\n  \\texttt{setStagingControls(getExportControls());}. The staging UI\n  typically provides the same content as the export UI (i.e., the\n  Content section for selecting what to publish/export), so it leverages\n  its UI. You can set the Staging UI differently by configuring the\n  \\texttt{setStagingControls} method differently. See the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/asset/asset-tags-service/src/main/java/com/liferay/asset/tags/internal/exportimport/data/handler/AssetTagsPortletDataHandler.java\\#L82-L84}{\\texttt{AssetTagsPortletDataHandler}}\n  class for an example of not copying the Export UI for the Staging UI.\n\\item\n  \\texttt{setDataAlwaysStaged}: defines whether you can enable/disable\n  your app's content staging (i.e., selectable from the Publication\n  screen). For example, setting this method to \\texttt{true}\n  automatically stages your app's content. Users can no longer choose\n  whether its content should be staged.\n\\end{itemize}\n\nOther setter methods are available that control both Export/Import and\nStaging settings. You can reference them by visiting the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-data-handlers\\#understanding-the-portletdatahandler-interface}{Understanding\nthe \\texttt{PortletDataHandler} Interface} section.\n\nYou can also control whether your app is enabled on the Staged Content\nscreen by adding this method to your portlet data handler:\n\n\\begin{verbatim}\n@Override\npublic boolean isConfigurationEnabled() {\n    return false;\n}\n\\end{verbatim}\n\nWhen this is set to \\texttt{false}, your app is disabled on the Staged\nContent screen. This is set to \\texttt{true} by default.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/staged-content-screen.png}\n\\caption{There are many apps available to select from the Staged Content\nscreen.}\n\\end{figure}\n\nThe majority of Staging-specific configurations are completed in a\nportlet data handler. The staged model data handler does come into play\nwhen you want to filter for certain staging processes/states. You'll\nlearn about this next.\n\n\\section{Filtering Staging-Specific Processes and\nStates}\\label{filtering-staging-specific-processes-and-states}\n\nYou can filter for certain staging-specific processes/states and\ncomplete actions based on the returned status. You can do this by\nleveraging the following classes from a staged model data handler:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/ExportImportThreadLocal.html}{\\texttt{ExportImportThreadLocal}}\n\\item\n  \\href{https://docs.liferay.com/dxp/apps/staging/latest/javadocs/com/liferay/staging/StagingGroupHelper.html}{\\texttt{StagingGroupHelper}}\n\\end{itemize}\n\nThe \\texttt{ExportImportThreadLocal} class provides boolean methods that\nreturn whether a specific process is in progress. Use this to check for\nevents affecting the entire site. For example, you can check if the\nfollowing processes are in progress:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Local Staging\n\\item\n  Remote Staging\n\\item\n  Layout Validation\n\\item\n  Portlet Staging\n\\item\n  etc.\n\\end{itemize}\n\nThe \\texttt{StagingGroupHelper} interface provides utility methods that\nreturn the staging state in your app. This is intended to check for\nevents only affecting your app. For example, you can check if your app\nis in these states:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Resides in Local Staging group\n\\item\n  Resides in Remote Live group\n\\item\n  Is a staged portlet\n\\item\n  etc.\n\\end{itemize}\n\nA real example filtering for a staging process and state can be found in\nthe\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-b2/modules/apps/asset/asset-list-service/src/main/java/com/liferay/asset/list/internal/exportimport/data/handler/AssetListEntryStagedModelDataHandler.java\\#L215-L222}{\\texttt{AssetListEntryStagedModelDataHandler}}\nclass:\n\n\\begin{verbatim}\nif ((assetRendererFactory != null) &&\n    ExportImportThreadLocal.isStagingInProcess() &&\n    !_stagingGroupHelper.isStagedPortlet(\n        assetEntry.getGroupId(),\n        assetRendererFactory.getPortletId())) {\n\n    continue;\n}\n\\end{verbatim}\n\nThe staged model data handler uses the\n\\texttt{ExportImportThreadLocal.isStagingInProcess()} method to verify\nthat a staging process is running. It also checks whether the app is\nstaged by executing \\texttt{!\\_stagingGroupHelper.isStagedPortlet(...)}.\n\nExcellent! You can now filter for staging-specific processes and states.\n\n\\chapter{Dependency Injection}\\label{dependency-injection}\n\nWhen you're using a object based on its interface, you don't have to\nconcern yourself with the implementation because it's abstracted from\nyou. At runtime, the implementation used depends on your environment and\nyour app's configuration. Liferay DXP offers several standard ways to\nregister implementations and inject them into your applications.\n\n\\textbf{Contexts and Dependency Injection (CDI):} Is the Java EE\nstandard dependency injection mechanism. Liferay DXP's CDI bean\ncontainer makes an application's concrete classes available as beans.\nBean classes can user other beans by way of injecting them into their\nfields that have the \\texttt{@Inject} annotation.\n\n\\textbf{OSGi Declarative Services:} Liferay DXP's OSGi runtime framework\nallows components to register as service provides and other components\ncan call on the registry to bind the services to their fields that have\nthe \\texttt{@Reference} annotation. Liferay DXP's services and services\nyou create using\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} are available as OSGi Declarative Services.\n\n\\textbf{Spring DI:} The Spring framework includes inversion of control\n(IoC) and dependency injection. It's available to applications that\nconfigure the Spring framework.\n\nAs an added bonus, Liferay DXP provides\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/osgi-cdi-integration}{OSGi\nCDI integration}. It lets you publish CDI beans as OSGi services and\nconsume OSGi services in your CDI beans. Which dependency injection\nmechanism will you use? Read on to learn more about them.\n\n\\chapter{CDI Dependency Injection}\\label{cdi-dependency-injection}\n\nPortlet 3.0 (see \\href{https://jcp.org/en/jsr/detail?id=362}{JSR 362})\nsupports Contexts and Dependency Injection (CDI) so you can create and\nuse injectable classes (CDI beans) in your portlet. It also provides\ninjectable portlet artifacts called\n\\href{/docs/7-2/reference/-/knowledge_base/r/cdi-portlet-predefined-beans}{Portlet\nPredefined Beans}. They give a portlet's CDI beans access to the portlet\nconfiguration, preferences, requests, responses, and more. Here's how to\ncreate and use CDI beans and Portlet Predefined Beans:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a portlet WAR project, if you haven't created one already.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Any project that has a class that implements the\n    \\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/Portlet.html}{\\texttt{javax.portlet.Portlet}}\n    interface, either directly or indirectly.\n  \\end{itemize}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** If you're developing a portlet JAR, such as a\n [Liferay MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet),\n use CDI via\n [OSGi CDI Integration](/docs/7-2/frameworks/-/knowledge_base/f/osgi-cdi-integration).\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:**\n Liferay DXP exports the packages provided by the Portlet API and CDI API.\n Liferay project templates typically include them as transitive\n dependencies. If you must explicitly depend on the portlet API and CDI\n artifacts, add them as `compileOnly` (Gradle) or `provided` (Maven)\n dependencies.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  If your portlet WAR project isn't a Bean Portlet, add this\n  \\texttt{src/main/webapp/WEB-INF/beans.xml} file to it. This file tells\n  CDI to scan the project for CDI annotations.\n\n\\begin{verbatim}\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://xmlns.jcp.org/xml/ns/javaee\" bean-discovery-mode=\"all\" version=\"1.2\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd\">\n    <!-- This file is necessary in order to inform CDI that scanning should occur for CDI annotations. -->\n</beans>\n\\end{verbatim}\n\\item\n  Add the\n  \\href{https://docs.oracle.com/javaee/7/api/javax/enterprise/context/ApplicationScoped.html}{\\texttt{@ApplicationScoped}}\n  annotation to your portlet class.\n\n\\begin{verbatim}\nimport javax.enterprise.context.ApplicationScoped;\n\n@ApplicationScoped\npublic class MyPortlet ... {\n    ...\n}\n\\end{verbatim}\n\\item\n  Make sure all concrete classes you want to make injectable have the\n  default constructor. These classes are now CDI beans.\n\\item\n  Add a scope to each CDI bean.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n Bean Scope              | Description      |\n ----------------------- | ---------------- |\n [`@ApplicationScoped`](https://docs.oracle.com/javaee/7/api/javax/enterprise/context/ApplicationScoped.html) | Shares the bean's state across all users' interactions with the portlet. |\n [`@Dependent`](https://docs.oracle.com/javaee/7/api/javax/enterprise/context/Dependent.html) | (default scope) Designates the bean to be for the client bean and share the client bean's lifecycle. |\n [`@PortletRequestScoped`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/PortletRequestScoped.html) | Associates the bean with the portlet request. |\n [`@PortletSessionScoped`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/PortletSessionScoped.html) | Places the bean in the portlet session. |\n [`@RenderStateScoped`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/RenderStateScoped.html) | Stores the bean as part of the portlet's render state. **Important:** The bean must implement the `PortletSerializable` interface. |\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{5}\n\\item\n  Use the \\href{https://jcp.org/en/jsr/detail?id=330}{JSR 330}\n  \\href{https://docs.oracle.com/javaee/7/api/javax/inject/Inject.html}{\\texttt{@Inject}}\n  annotation in a CDI bean to inject another CDI bean into it. For\n  example, this code informs Liferay DXP's CDI bean container to inject\n  a \\texttt{GuestBook} CDI bean into this \\texttt{guestbook} field.\n\n\\begin{verbatim}\n@Inject\nprivate GuestBook guestbook;\n\\end{verbatim}\n\\item\n  Inject any\n  \\href{/docs/7-2/reference/-/knowledge_base/r/cdi-portlet-predefined-beans}{Portlet\n  Predefined Beans} (portlet request scoped or dependent scoped beans)\n  into your \\texttt{@PortletRequestScoped} CDI beans.\n\n\\begin{verbatim}\n@PortletRequestScoped\npublic class RequestProcessor ... {\n\n    @Inject\n    private PortletRequest portletRequest;\n    ...\n}\n\\end{verbatim}\n\\item\n  Inject any\n  \\href{/docs/7-2/reference/-/knowledge_base/r/cdi-portlet-predefined-beans}{dependent\n  scoped Portlet Predefined Beans} into your \\texttt{ApplicationScoped}\n  or \\texttt{@Dependent} scoped CDI beans. For example,\n\n\\begin{verbatim}\n@ApplicationScoped\npublic class MyPortlet ... {\n\n    @Inject\n    private PortletConfig portletConfig;\n    ...\n}\n\\end{verbatim}\n\\item\n  Use bean EL names to reference any\n  \\href{/docs/7-2/reference/-/knowledge_base/r/cdi-portlet-predefined-beans}{portlet\n  redefined \\emph{named beans}} in your JSP or JSF pages.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy}\n  your project.\n\\end{enumerate}\n\nCongratulations! You have created and used CDI beans and Portlet\nPredefined Beans in your portlet.\n\n\\section{Related Topics}\\label{related-topics-47}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/cdi-portlet-predefined-beans}{CDI\nPortlet Predefined Beans}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{Portlets}\n\n\\chapter{OSGi CDI Integration}\\label{osgi-cdi-integration}\n\nLiferay DXP's runtime environment consists of services (OSGi services).\nThe OSGi service registry and Service Component Runtime (SCR) facilitate\nproviding and consuming services.\n\\href{http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html}{Contexts and\nDependency Injection (CDI)} is a Java SE and EE standard for lifecycle\nevents, stateful objects, and dependency injection.\n\\href{https://osgi.org/specification/osgi.enterprise/7.0.0/service.cdi.html}{OSGi\nCDI Integration} brings features and capabilities of CDI to OSGi and\nmakes OSGi services available to CDI beans. Here you'll learn how to\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/publishing-cdi-beans-as-osgi-services}{Publish\n  CDI beans as OSGi services}: Register CDI beans as services you can\n  use to customize Liferay DXP components.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-osgi-services-in-a-bean}{Use\n  OSGi services in beans}: Leverage any OSGi service published on\n  Liferay DXP in any bean.\n\\end{itemize}\n\nThe following use cases provide more detail.\n\n\\section{Use Case: Registering a CDI bean as an OSGi\nservice}\\label{use-case-registering-a-cdi-bean-as-an-osgi-service}\n\nLiferay DXP extension points are implemented as OSGi services. If\nthere's a piece of functionality you must customize, you don't have to\nlearn OSGi to do it: you can write your extension/override as a CDI bean\ninstead and use OSGi CDI integration to publish your bean as an OSGi\nservice.\n\nBy implementing the service in your CDI bean class and adding the\nintegration's \\texttt{@org.osgi.service.cdi.annotations.Service}\nannotation to it, your bean registers as providing that OSGi service. In\nthis way, service consumers can use your service implementation (i.e.,\nyour CDI bean).\n\nFor example, the Service Registry in figure 1 shows two implementations\nof an OSGi service called \\texttt{S1}:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/injecting-bean-osgi-service.png}\n\\caption{OSGi Service Component Runtime (SCR) finds \\texttt{MyBean} as\nthe best (highest ranked) \\texttt{S1} service provider and binds it to\nconsumer component \\texttt{C1}.}\n\\end{figure}\n\n\\begin{itemize}\n\\item\n  \\texttt{MyBean} is a CDI bean whose service rank is \\texttt{1000}.\n\\item\n  \\texttt{MySvcImpl} has a service rank of \\texttt{0}.\n\\end{itemize}\n\nThe Service Component Runtime (\\texttt{SCR}) finds the matching, highest\nranked \\texttt{S1} service provider and binds it to consumer\n\\texttt{C1}. The fact that \\texttt{MyBean} is a CDI bean is transparent\nto the SCR.\n\nOnce a CDI bean is registered as a service, components can use it as\nthey would any other OSGi service.\n\n\\section{Use Case: Using an OSGi service in a\nbean}\\label{use-case-using-an-osgi-service-in-a-bean}\n\nLiferay DXP contains many development frameworks for all of its\nconstructs, such as Users, Sites, Documents, Comments, and the APIs for\nthese assets are all implemented as OSGi services. When developers write\nnew applications using Liferay's development frameworks, new assets\nbecome available and integrated with the rest of the system. OSGi CDI\nintegration enables your beans to access these OSGi services.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/using-a-service-in-a-bean.png}\n\\caption{Here how Liferay's \\texttt{UserLocalService} is injected into a\nbean.}\n\\end{figure}\n\nIn figure 2, for example, CDI bean \\texttt{SomeBean} uses the OSGi CDI\nintegration annotation\n\\texttt{@org.osgi.service.cdi.annotations.Reference} (along with CDI\nannotation \\texttt{@Inject}) to inject the OSGi service\n\\texttt{UserLocalService}. The Service Component Runtime (\\texttt{SCR})\nfinds the \\texttt{UserLocalService} in the Service Registry and binds it\nto \\texttt{SomeBean}'s field \\texttt{userSvc}.\n\nThese are the most common use cases, but you might have more. Now you\ncan get started using OSGi CDI integration to\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/publishing-cdi-beans-as-osgi-services}{publish\nCDI beans as OSGi services}!\n\n\\chapter{Publishing CDI Beans as OSGi\nServices}\\label{publishing-cdi-beans-as-osgi-services}\n\nYou can publish CDI beans as OSGi services, making them accessible via\nthe Liferay's OSGi service registry. Here's how:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a project dependency on the OSGi CDI Integration artifact. For\n  example, here's the dependency to use in a Maven \\texttt{pom.xml}\n  file:\n\n\\begin{verbatim}\n<dependency>\n    <groupId>org.osgi</groupId>\n    <artifactId>org.osgi.service.cdi</artifactId>\n    <version>1.0.0</version>\n</dependency>\n\\end{verbatim}\n\\item\n  Make your CDI bean implement the service interface you're providing.\n  For example, \\texttt{ShopImpl} provides the \\texttt{Shop} service by\n  implementing that interface.\n\n\\begin{verbatim}\npackage my.package;\n\npublic class ShopImpl implements Shop {\n    ...\n}\n\\end{verbatim}\n\\item\n  Annotate your CDI bean class with\n  \\texttt{@org.osgi.service.cdi.annotations.Service}.\n\n\\begin{verbatim}\npackage my.package;\n\nimport org.osgi.service.cdi.annotations.Service;\n\n@Service \npublic class ShopImpl implements Shop {\n    ...\n}\n\\end{verbatim}\n\\item\n  Deploy the API that defines the service interface, if you haven't\n  deployed it already.\n\\item\n  Build and deploy your service project bundle.\n\\end{enumerate}\n\nOnce your bundle installs and activates, your bean's service\nimplementation is available. You can use Gogo Shell commands can verify\nthat the service registered.\n\nFor example, here are steps for verifying that a bundle\n\\texttt{com.liferay.portal.samples.cdi.jar.portlet} registers a service\ncalled \\texttt{org.apache.portals.samples.Users}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Control Panel} → \\emph{Configuration} → \\emph{Gogo\n  Shell}.\n\\item\n  Use the \\texttt{lb} Gogo command and \\texttt{grep} (pass in the\n  bundle's symbolic name) to find your bundle (and its ID).\n\n  Example command:\n\n\\begin{verbatim}\ng!: lb | grep com.liferay.portal.samples.cdi.jar.portlet\n\\end{verbatim}\n\n  Results:\n\n\\begin{verbatim}\n924|Active     |   10|com.liferay.portal.samples.cdi.jar.portlet (0.0.1.201901252134)|0.0.1.201901252134\n\\end{verbatim}\n\n  The first column contains the bundle ID.\n\\item\n  Use the \\texttt{b} Gogo command with your bundle ID to list your\n  bundle's details and verify the bundle includes your service as one of\n  its registered services.\n\n  Example command:\n\n\\begin{verbatim}\ng!: b 924\n\\end{verbatim}\n\n  Results:\n\n\\begin{verbatim}\ncom.liferay.portal.samples.cdi.jar.portlet_0.0.1.201901252134 [924]\nId=924, Status=ACTIVE      Data Root=C:\\git\\bundles\\osgi\\state\\org.eclipse.osgi\\924\\data\n\"Registered Services\"\n  ...\n  {org.apache.portals.samples.Users}={osgi.command.scope=cdiportlet, service.id=4232, service.bundleid=924, service.scope=singleton, osgi.command.function=[getUsersCount], component.name=com.liferay.portal.samples.cdi.jar.portlet, component.id=1}\n  ...\n\\end{verbatim}\n\\end{enumerate}\n\nCongratulations on publishing your CDI bean as an OSGi service!\n\n\\chapter{Using OSGi Services in a\nBean}\\label{using-osgi-services-in-a-bean}\n\nAny bean can use the\n\\texttt{@org.osgi.service.cdi.annotations.Reference} annotation to\ninject OSGi services. It's the easiest way for a bean to access an OSGi\nservice. Here's how:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a project dependency on the OSGi CDI Integration artifact. For\n  example, here's the dependency to use in a Maven \\texttt{pom.xml}\n  file:\n\n\\begin{verbatim}\n<dependency>\n    <groupId>org.osgi</groupId>\n    <artifactId>org.osgi.service.cdi</artifactId>\n    <version>1.0.0</version>\n</dependency>\n\\end{verbatim}\n\\item\n  Obtain and inject the OSGi service by using the\n  \\texttt{@org.osgi.service.cdi.annotations.Reference} and\n  \\texttt{@javax.inject.Inject} annotations respectively. Here's an\n  example of injecting a service of type \\texttt{ProductStore}.\n\n\\begin{verbatim}\nimport javax.inject.Inject;\n\nimport org.osgi.service.cdi.annotations.Reference;\n\nimport package.path.ProductStore;\n\npublic class MyBean {\n\n    @Inject\n    @Reference\n    ProductStore productStore;\n\n    ...\n}\n\\end{verbatim}\n\\item\n  Deploy your bean project to Liferay DXP.\n\\end{enumerate}\n\nCongratulations on injecting an OSGi service into your bean! Now your\nbean uses the OSGi service you injected.\n\n\\chapter{Declarative Services}\\label{declarative-services}\n\nLiferay DXP's OSGi framework registers objects as \\emph{services}. Each\nservice offers functionality and can leverage functionality other\nservices provide. The OSGi Services model supports a collaborative\nenvironment for objects.\n\nDeclarative Services (DS) provides a service component model on top of\nOSGi Services. DS service components are marked with the\n\\href{https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html}{\\texttt{@Component}}\nannotation and implement or extend a service class. Service components\ncan refer to and use each other's services. The Service Component\nRuntime (SCR) registers component services and handles binding them to\nother components that reference them.\n\nHere's how the ``magic'' happens:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{Service registration:} On installing a module that contains a\n  service component, the SCR creates a component configuration that\n  associates the component with its specified service type and stores it\n  in a service registry.\n\\item\n  \\textbf{Service reference handling:} On installing a module whose\n  service component references another service type, the SCR searches\n  the registry for a component configuration that matches the service\n  type and on finding a match binds an instance of that service to the\n  referring component.\n\\end{enumerate}\n\nIt's publish, find, and bind at its best!\n\nHow do you use DS to register and bind services? Does it involve\ncreating XML files? No, it's much easier than that. You use two\nannotations: \\texttt{@Component} and \\texttt{@Reference}.\n\n\\begin{itemize}\n\\item\n  \\href{https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html}{\\texttt{@Component}}:\n  Add this annotation to a class definition to make the class a\n  component--a service provider.\n\\item\n  \\href{https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Reference.html}{\\texttt{@Reference}}:\n  Add this annotation to a field to inject it with a service that\n  matches the field's type.\n\\end{itemize}\n\nThe\n\\href{https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html}{\\texttt{@Component}}\nannotation makes the class an OSGi component. Setting the annotation's\n\\texttt{service} attribute to a particular service type allows other\ncomponents to reference the service component by that service type.\n\nFor example, the following class is a service component of type\n\\texttt{SomeApi.class}.\n\n\\begin{verbatim}\n@Component(\n    service = SomeApi.class\n)\npublic class Service1 implements SomeApi {\n   ...\n}\n\\end{verbatim}\n\nOn deploying this class's module, the SCR creates a component\nconfiguration that associates the class with the service type\n\\texttt{SomeApi}.\n\nSpecifying a service reference is easy too. Applying the\n\\href{https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Reference.html}{\\texttt{@Reference}}\nannotation to a field marks it to be injected with a service matching\nthe field's type.\n\n\\begin{verbatim}\n@Reference\nSomeApi _someApi;\n\\end{verbatim}\n\nOn deploying this class's module, the SCR finds a component\nconfiguration of the class type \\texttt{SomeApi} and binds the service\nto this referencing component class's field \\texttt{SomeApi\\ \\_someApi}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\texttt{@Reference} annotation can only be used in a\nclass that is annotated with \\texttt{@Component} (i.e, a Declarative\nServices component ) or a bean class that uses OSGi CDI integration.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} At build time in modules created from\n\\href{/docs/7-1/reference/-/knowledge_base/r/project-templates}{Liferay\nproject templates}, bnd creates a \\emph{component description} file for\neach module's components automatically. The file specifies the\ncomponent's services, dependencies, and activation characteristics. On\nmodule deployment, the OSGi framework reads the component description to\ncreate the component and manage its dependency on other components.\n\n\\noindent\\hrulefill\n\nThe SCR stands ready to pair service components with each other. For\neach referencing component, the SCR binds an instance of the targeted\nservice to it.\n\nAs an improvement over dependency injection with Spring, OSGi\nDeclarative Services supports dynamic dependency injection. You can\ncreate and publish service components for other classes to use. You can\nupdate the components and even publish alternative component\nimplementations for a service. This kind of dynamism is a powerful part\nof Liferay DXP.\n\n\\chapter{Service Trackers for OSGi\nServices}\\label{service-trackers-for-osgi-services}\n\nIn an OSGi runtime ecosystem, you must consider how your apps can rely\non OSGi services in other modules for functionality. It's possible for\nservice implementations to be swapped out or removed entirely, and your\napp must not just survive but thrive in this environment.\n\nIf you want to\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{call\nOSGi services from an OSGi Declarative Services \\texttt{@Component}}\nclasses, it's easy: you just use a\n\\href{https://osgi.org/specification/osgi.cmpn/7.0.0/service.component.html}{Declarative\nServices (DS)} annotation,\n\\href{ttps://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Reference.html}{\\texttt{@Reference}},\nto inject the service. The component activates when the referenced\nservice is available.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\texttt{@Reference} annotation can only be used in a\nclass that is annotated with\n\\href{https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html}{\\texttt{@Component}}.\nThat is, only a Declarative Services component can use\n\\texttt{@Reference} to bind to an OSGi service.\n\n\\noindent\\hrulefill\n\nIf you want to call an OSGi service from a bean, use\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-osgi-services-in-a-bean}{OSGi\nCDI integration}.\n\nDS \\texttt{@Reference} with \\texttt{@Component}s and OSGi CDI\nintegration with beans manage much of the complexity of service dynamism\nfor you transparently. If you can use either of them, you should.\nOtherwise, read\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker}{implement\na Service Tracker} to look up services in the service registry.\n\n\\chapter{Using a Service Tracker}\\label{using-a-service-tracker}\n\nYour non-OSGi and non-bean classes can access any service registered in\nthe OSGi runtime using a Service Tracker. It lets you access any OSGi\nservices including your own\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder services} and the services published by Liferay's modules (like\nthe popular \\texttt{UserLocalService}).\n\nYou can create a service tracker in two ways:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a service tracker where you need it.\n\\item\n  Create a class that extends\n  \\texttt{org.osgi.util.tracker.ServiceTracker}.\n\\item\n  Create a service tracker that tracks service events using a callback\n  handler.\n\\end{enumerate}\n\nBoth ways depend on \\texttt{org.osgi.core}, whose packages Liferay DXP\nexports by default. Configure it as \\texttt{compileOnly} (Gradle) or\n\\texttt{provided} (Maven). See the\n\\href{/docs/7-1/reference/-/knowledge_base/r/third-party-packages-portal-exports}{Third\nParty Packages Portal Exports} for more information.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The static utility classes (e.g.,\n\\texttt{UserLocalServiceUtil}) that were useful in Liferay Portal 6.2\n(and earlier) exist for compatibility but should not be called, if\npossible. Static utility classes cannot account for the OSGi runtime's\ndynamic environment. If you use a static class, you might attempt\ncalling a stopped service or one that hasn't been deployed or started.\nThis could cause unrecoverable runtime errors. Service Trackers,\nhowever, help you make OSGi-friendly service calls.\n\n\\noindent\\hrulefill\n\n\\section{Creating a New Service Tracker Where You Need\nIt}\\label{creating-a-new-service-tracker-where-you-need-it}\n\nTo create it directly, do this:\n\n\\begin{verbatim}\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\nBundle bundle = FrameworkUtil.getBundle(this.getClass());\nBundleContext bundleContext = bundle.getBundleContext();\nServiceTracker<SomeService, SomeService> serviceTracker =\n    new ServiceTracker(bundleContext, SomeService.class, null);\nserviceTracker.open();\nSomeService someService = serviceTracker.waitForService(500);\n\\end{verbatim}\n\n\\section{Create a Class That Extends\nServiceTracker}\\label{create-a-class-that-extends-servicetracker}\n\nA better way is to create a class that extends\n\\texttt{org.osgi.util.tracker.ServiceTracker}, because this simplifies\nyour code.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a class like this one that extends \\texttt{ServiceTracker}:\n\n\\begin{verbatim}\npublic class SomeServiceTracker\n    extends ServiceTracker<SomeService, SomeService> {\n\n    public SomeServiceTracker(Object host) {\n        super(\n            FrameworkUtil.getBundle(host.getClass()).getBundleContext(),\n            SomeService.class, null);\n    }\n}\n\\end{verbatim}\n\\item\n  Construct a new instance of your service tracker where you need it.\n  The \\texttt{Object\\ host} parameter obtains your own bundle context\n  and must be an object from your own bundle in order to give accurate\n  results.\n\n\\begin{verbatim}\nServiceTracker<SomeService, SomeService> someServiceTracker =\n    new SomeServiceTracker(this);\n\\end{verbatim}\n\\item\n  When you want to use the service tracker, open it, typically as early\n  as you can.\n\n\\begin{verbatim}\nsomeServiceTracker.open();\n\\end{verbatim}\n\\item\n  Before attempting to use a service, use the Service Tracker to\n  interrogate the service's state. For example, check whether the\n  service is \\texttt{null}:\n\n\\begin{verbatim}\nSomeService someService = someServiceTracker.getService();\n\nif (someService == null) {\n    _log.warn(\"The required service 'SomeService' is not available.\");\n}\nelse {\n    someService.doSomethingCool();\n}\n\\end{verbatim}\n\\end{enumerate}\n\nNote, service trackers have several other utility functions for\nintrospecting tracked services.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{4}\n\\item\n  Later when your application is being destroyed or undeployed, close\n  the service tracker.\n\n\\begin{verbatim}\nsomeServiceTracker.close();\n\\end{verbatim}\n\\end{enumerate}\n\nIf you need to track multiple services or their events, implement a\nservice tracker that uses callback handlers.\n\n\\section{Creating a Service Tracker that Tracks Service Events Using a\nCallback\nHandler}\\label{creating-a-service-tracker-that-tracks-service-events-using-a-callback-handler}\n\nIf there's a strong possibility the service might not be available or if\nyou need to track multiple services, the Service Tracker API provides a\ncallback mechanism that operates on service \\emph{events}. To use this,\noverride \\texttt{ServiceTracker}'s \\texttt{addingService} and\n\\texttt{removedService} methods. Their \\texttt{ServiceReference}\nparameter references an active service object.\n\nHere's an example \\texttt{ServiceTracker} implementation from the\n\\href{https://osgi.org/specification/osgi.core/7.0.0/util.tracker.html\\#d0e51991}{OSGi\nAlliance's OSGi Core Release 7 specification}:\n\n\\begin{verbatim}\nnew ServiceTracker<HttpService, MyServlet>(context, HttpService.class, null) {\n\n    public MyServlet addingService(ServiceReference<HttpService> reference) {\n        HttpService httpService = context.getService(reference);\n        MyServlet myServlet = new MyServlet(httpService);\n        return myServlet;\n    }\n\n    public void removedService(\n        ServiceReference<HttpService> reference, MyServlet myServlet) {\n        myServlet.close();\n        context.ungetService(reference);\n    }\n}\n\\end{verbatim}\n\nWhen the \\texttt{HttpService} is added to the OSGi registry, this\n\\texttt{ServiceTracker} creates a new wrapper class, \\texttt{MyServlet},\nwhich uses the newly added service. When the service is removed from the\nregistry, the \\texttt{removedService} method cleans up related\nresources.\n\nAs an alternative to directly overloading \\texttt{ServiceTracker}\nmethods, create a\n\\texttt{org.osgi.util.tracker.ServiceTrackerCustomizer}:\n\n\\begin{verbatim}\nclass MyServiceTrackerCustomizer \n    implements ServiceTrackerCustomizer<SomeService, MyWrapper> {\n    \n    private final BundleContext bundleContext;\n    \n    MyServiceTrackerCustomizer(BundleContext bundleContext) {\n        this.bundleContext = bundleContext;\n    }\n    \n    @Override\n    public MyWrapper addedService(\n        ServiceReference<SomeService> serviceReference) {\n        \n        // Determine if the service is one that's interesting to you.\n        // The return type of this method is the `tracked` type. Its type \n        // is what is returned from `getService*` methods; useful for wrapping \n        // the service with your own type (e.g., MyWrapper).\n        if (isInteresting(serviceReference)) {\n            MyWrapper myWrapper = new MyWrapper(\n                serviceReference, bundleContext.getService());\n            \n            // trigger the logic that requires the available service(s)\n            triggerServiceAddedLogic(myWrapper);\n            \n            return myWrapper;\n        }\n        \n        // If the return is null, the tracker is effectively ignoring any further\n        // events for the service reference\n        return null;\n    }\n\n    @Override\n    public void modifiedService(\n        ServiceReference<SomeService> serviceReference, MyWrapper myWrapper) {\n        // handle the modified service\n    }\n\n    @Override\n    public void removedService(\n        ServiceReference<SomeService> serviceReference, MyWrapper myWrapper) {\n\n        // finally, trigger logic when the service is going away\n        triggerServiceRemovedLogic(myWrapper);\n    }\n\n}\n\\end{verbatim}\n\nRegister the \\texttt{ServiceTrackerCustomizer} by passing it as the\n\\texttt{ServiceTracker} constructor's third parameter.\n\n\\begin{verbatim}\nServiceTrackerCustomizer<SomeService, MyWrapper> serviceTrackerCustomizer =\n    new MyServiceTrackerCustomizer();\n\nServiceTracker<SomeService, MyWrapper> serviceTracker = \n    new ServiceTracker<>(\n        bundleContext, SomeService.class, serviceTrackerCustomizer);\n\\end{verbatim}\n\nUsing service trackers requires producing some boilerplate code, but now\nyou can look up services in the service registry, even if your plugins\ncan't take advantage of the Declarative Services component model.\n\n\\chapter{Friendly URLs}\\label{friendly-urls}\n\nThis is a story of two URLs who couldn't be more different. One was full\nof himself and always wanted to show everyone (users and SEO services\nalike) just how smart he was by openly displaying all the parameters he\ncarried. He was happiest when he could tell people he met were\nintimidated and confused by him.\n\n\\begin{verbatim}\nhttp://localhost:8080/group/guest/~/control_panel/manage?p_p_id=com_liferay_blogs_web_portlet_BlogsAdminPortlet&p_p_lifecycle=0&p_p_state=maximized&p_p_mode=view&_com_liferay_blogs_web_portlet_BlogsAdminPortlet_mvcRenderCommandName=%2Fblogs%2Fedit_entry&_com_liferay_blogs_web_portlet_BlogsAdminPortlet_redirect=http%3A%2F%2Flocalhost%3A8080%2Fgroup%2Fguest%2F~%2Fcontrol_panel%2Fmanage%3Fp_p_id%3Dcom_liferay_blogs_web_portlet_BlogsAdminPortlet%26p_p_lifecycle%3D0%26p_p_state%3Dmaximized%26p_p_mode%3Dview%26_com_liferay_blogs_web_portlet_BlogsAdminPortlet_mvcRenderCommandName%3D%252Fblogs%252Fview%26_com_liferay_blogs_web_portlet_BlogsAdminPortlet_orderBycol%3Dtitle%26_com_liferay_blogs_web_portlet_BlogsAdminPortlet_orderByType%3Dasc%26_com_liferay_blogs_web_portlet_BlogsAdminPortlet_entriesNavigation%3D%26_com_liferay_blogs_web_portlet_BlogsAdminPortlet_cur%3D1%26_com_liferay_blogs_web_portlet_BlogsAdminPortlet_delta%3D20&_com_liferay_blogs_web_portlet_BlogsAdminPortlet_entryId=30836\n\\end{verbatim}\n\nThe other was just, well, friendly. She was less concerned about looking\nsmart and more concerned about those she interacted with, so she shared\nonly the important things about her. She didn't need to look fancy and\ncomplicated. She aspired to be simple and kind to all the users and SEO\nservices she encountered.\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/home/-/blogs/lunar-scavenger-hunt\n\\end{verbatim}\n\nIf you want your application to be friendly to your users and to SEO\nservices, make your URLs friendlier. It only takes a couple steps, after\nall.\n\n\\chapter{Friendly URLs}\\label{friendly-urls-1}\n\nFollow these steps to create friendly URLs:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create friendly URL routes. Create a \\texttt{routes.xml} file in your\n  application's web module. Liferay's pattern puts it in a\n  \\texttt{src/main/resources/META-INF/friendly-url-routes/} folder.\n\\item\n  Add friendly URL routes, using as many\n  \\texttt{\\textless{}route\\textgreater{}} tags as you need friendly\n  URLs, like this:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE routes PUBLIC \"-//Liferay//DTD Friendly URL Routes 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-friendly-url-routes_7_2_0.dtd\">\n\n<routes>\n    <route>\n        <pattern></pattern>\n        <implicit-parameter name=\"mvcRenderCommandName\">/blogs/view</implicit-parameter>\n        <implicit-parameter name=\"p_p_lifecycle\">0</implicit-parameter>\n        <implicit-parameter name=\"p_p_state\">normal</implicit-parameter>\n    </route>\n    <route>\n        <pattern>/maximized</pattern>\n        <implicit-parameter name=\"mvcRenderCommandName\">/blogs/view</implicit-parameter>\n        <implicit-parameter name=\"p_p_lifecycle\">0</implicit-parameter>\n        <implicit-parameter name=\"p_p_state\">maximized</implicit-parameter>\n    </route>\n    <route>\n        <pattern>/{entryId:\\d+}</pattern>\n        <implicit-parameter name=\"categoryId\"></implicit-parameter>\n        <implicit-parameter name=\"mvcRenderCommandName\">/blogs/view_entry</implicit-parameter>\n        <implicit-parameter name=\"p_p_lifecycle\">0</implicit-parameter>\n        <implicit-parameter name=\"p_p_state\">normal</implicit-parameter>\n        <implicit-parameter name=\"tag\"></implicit-parameter>\n    </route>\n    ...\n</routes>\n\\end{verbatim}\n\n  Use \\texttt{\\textless{}pattern\\textgreater{}} tags to define\n  placeholder values for the parameters that normally appear in the\n  generated URL. This is just a mask. The beastly URL\\\\\n  still lurks beneath it.\n\n  The \\texttt{pattern} value \\texttt{/\\{entryId:\\textbackslash{}d+\\}}\n  matches a \\texttt{/} followed by an \\texttt{entryId} variable that\n  matches the\n  \\href{https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html}{Java\n  regular expression} \\texttt{\\textbackslash{}d+}---one or more numeric\n  digits. For example, a URL \\texttt{/entryId}, where the\n  \\texttt{entryId} value is \\texttt{123} results in a URL value\n  \\texttt{/123}, which matches the pattern.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Warning:** Make sure your `pattern` values don't end in a slash `/`. A\n trailing slash character prevents the request from identifying the correct\n route.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n**Important:** If your portlet is instanceable, you must use a variant of \nthe `instanceId` in the `pattern` value. If the starting value is \n`render-it`, for example, use one of these patterns:\n\n```xml\n<pattern>/{userIdAndInstanceId}/render-it</pattern>\n```\n\nor\n\n```xml\n<pattern>/{instanceId}/render-it</pattern>\n```\n\nor\n\n```xml\n<pattern>/{p_p_id}/render-it</pattern>\n```\n\nUse `<implicit-parameter>` tags to define parameters that are always the \nsame for the URL. For example, for a render URL, you can be certain that the \n`p_p_lifecycle` parameter is always `0`. You don't have to define these \ntypes of implicit parameters, but it's a best practice because if you don't, \nthey still appear in your URL. \n\nThe implicit parameters with the name `mvcRenderCommandName` are very \nimportant. If you're using an `MVCPortlet` with `MVCRenderCommand` classes, \nthat parameter comes from the `mvc.command.name` property in the \n`@Component` of your `MVCRenderCommand` implementation. This determines the \npage that's rendered (for example, `view.jsp`). \n\n<!--Add link back for 'using an `MVCPortlet` with `MVCRenderCommand` classes' \nonce mvc-render-command article is available-->\n\n```java\n@Component(\n    immediate = true,\n    property = {\n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS, \"mvc.command.name=/\",\n        \"mvc.command.name=/blogs/view\"\n    },\n    service = MVCRenderCommand.class\n)\n```\n\nThe [DTD file](https://docs.liferay.com/dxp/portal/7.2-latest/definitions/liferay-friendly-url-routes_7_2_0.dtd.html)\ncompletely defines the `routes.xml` file. \n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Provide an implementation of the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/FriendlyURLMapper.html}{\\texttt{FriendlyURLMapper}\n  service}. Create a component that specifies a\n  \\texttt{FriendlyURLMapper} service, with two properties:\n\n  \\begin{itemize}\n  \\item\n    A \\texttt{com.liferay.portlet.friendly-url-routes} property sets the\n    path to your \\texttt{routes.xml} file.\n  \\item\n    A \\texttt{javax.portlet.name} property, which you probably have\n    already, specifies your portlet's name.\n  \\end{itemize}\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"com.liferay.portlet.friendly-url-routes=META-INF/friendly-url-routes/routes.xml\",\n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS\n    },\n    service = FriendlyURLMapper.class\n)\n\\end{verbatim}\n\\item\n  Implement the \\texttt{FriendlyURLMapper} service. For your\n  convenience, the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/DefaultFriendlyURLMapper.html}{\\texttt{DefaultFriendlyURLMapper}\n  class} provides a default implementation. If you extend\n  \\texttt{DefaultFriendlyURLMapper} you must only override one method,\n  \\texttt{getMapping()}. Return a String that defines the first part of\n  your Friendly URLs. It's smart to name it after your application.\n  Here's what it looks like for Liferay's Blogs application:\n\n\\begin{verbatim}\npublic class BlogsFriendlyURLMapper extends DefaultFriendlyURLMapper {\n\n    @Override\n    public String getMapping() {\n        return _MAPPING;\n    }\n\n    private static final String _MAPPING = \"blogs\";\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\nAll friendly URLs in Blogs begin with the String set here\n(\\texttt{blogs}). Let's look at one of these Friendly URLs in action.\nAdd a blog entry and then click on the entry's title. Look at the URL:\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/home/-/blogs/lunar-scavenger-hunt\n\\end{verbatim}\n\nAs specified in the friendly URL mapper class, \\texttt{blogs} is the\nfirst part of the friendly URL that comes after the Liferay part of the\nURL. The next part is determined by a specific URL route in\n\\texttt{routes.xml}:\n\n\\begin{verbatim}\n<route>\n    <pattern>/{urlTitle}</pattern>\n    <implicit-parameter name=\"categoryId\"></implicit-parameter>\n    <implicit-parameter name=\"mvcRenderCommandName\">/blogs/view_entry</implicit-parameter>\n    <implicit-parameter name=\"p_p_lifecycle\">0</implicit-parameter>\n    <implicit-parameter name=\"p_p_state\">normal</implicit-parameter>\n    <implicit-parameter name=\"tag\"></implicit-parameter>\n</route>\n\\end{verbatim}\n\nThe \\texttt{urlTitle} is generated from the blog post's title. Since\nit's already a parameter in the URL (see below), it's available for use\nin the friendly URL.\n\n\\begin{verbatim}\n<portlet:renderURL var=\"viewEntryURL\">\n    <portlet:param name=\"mvcRenderCommandName\" value=\"/blogs/view_entry\" />\n    <portlet:param name=\"urlTitle\" value=\"<%= entry.getUrlTitle() %>\" />\n</portlet:renderURL>\n\\end{verbatim}\n\nWhen the render URL is invoked, the String defined in the friendly URL\nmapper teams up with the \\texttt{pattern} tag in your friendly URL\nroutes file, and you get a very friendly URL indeed, instead of some\nnasty, conceited, unfriendly URL that's despised by users and SEO\nservices alike.\n\nGreat! Now you know how to make your URLS friendly.\n\n\\section{Related Topics}\\label{related-topics-48}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/dependency-injection}{Dependency\n  Injection}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/localization}{Localization}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{Asset\n  Framework}\n\\end{itemize}\n\n\\chapter{Front-End Development}\\label{front-end-development}\n\nYou have complete front-end development freedom. You can use Liferay\nDXP's front-end frameworks, along with the front-end technologies you\nlove the most:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  EcmaScript ES2015+\n\\item\n  React, Angular, Vue, etc.\n\\item\n  \\href{https://metaljs.com/}{Metal.js} (developed by Liferay)\n\\item\n  \\href{https://alloyui.com/}{AlloyUI} (developed by Liferay)\n\\item\n  jQuery (included)\n\\item\n  Lodash (included,\n  \\href{https://github.com/liferay/liferay-portal/blob/master/readme/BREAKING_CHANGES.markdown\\#lodash-is-no-longer-included-by-default}{but\n  disabled by default})\n\\end{itemize}\n\nTo load modules, you must know when they are needed, where they are at\nbuild time, whether they should be bundled together or loaded\nindependently, and you must assemble them at runtime. Liferay's Loaders\n(YUI/AUI, AMD, and npm in AMD format) handle loading for you. All you\nmust do is provide a small bit of information about your module.\n\nThe Liferay JS Bundle Toolkit(the\n\\href{https://web.liferay.com/marketplace/-/mp/application/115542926}{JS\nPortlet Extender},\n\\href{https://www.npmjs.com/package/generator-liferay-bundle}{Liferay\nBundle Generator}, and\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-npm-bundler}{\\texttt{liferay-npm-bundler}}\n) has the tools you need to create and develop JavaScript portlets with\npure JavaScript tooling. You can use the\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-npm-bundler}{\\texttt{liferay-npm-bundler}}\nto bundle npm packages in your applications. It even has several presets\nfor common module types (AMD, React, Angular JS, etc.) to save you time.\nIt creates an OSGi bundle for you, extracts all npm dependencies, and\ntranspiles your code for the Liferay AMD Loader.\n\nWhile developing JavaScript applications, you may need to access Liferay\nDXP-specific information or web services. The \\texttt{Liferay} global\nJavaScript Object\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-javascript-apis}{exposes\nthis information for you}, to use in your JavaScript applications.\n\n\\section{Lexicon and Clay}\\label{lexicon-and-clay}\n\nLiferay uses its own design language, called\n\\href{https://liferay.design/lexicon}{Lexicon}, to provide a common\nframework for building consistent UIs and user experiences across the\nLiferay product ecosystem. The web implementation of Lexicon (CSS, JS,\nand HTML) is called \\href{https://clayui.com/}{Clay}. It is\nautomatically available to application developers through a set of CSS\nclasses or our\n\\href{/docs/7-2/reference/-/knowledge_base/r/using-the-clay-taglib-in-your-portlets}{tag\nlibrary}.\n\n\\section{Templates}\\label{templates}\n\nFor templating, you can use Java EE's JSP, FreeMarker, or whatever else\nyou like.\n\n\\section{Themes}\\label{themes}\n\nThemes use the standard components (CSS, JavaScript, and HTML) along\nwith FreeMarker templates. Although the default themes are nice, you may\nwish to create your own look and feel for your site. The\n\\href{https://github.com/liferay/liferay-themes-sdk/tree/master/packages}{Liferay\nJS Theme Toolkit} has all the tools you need to create and develop\nthemes, but you can use the tools you prefer.\n\nFrom the\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin}{Theme\nBuilder Gradle Plugin}, to the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Liferay\nTheme Generator}, to\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}'s\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-template}{Theme\nTemplate}, you can choose the development tools you like best, so you\ncan focus on creating a well designed theme.\n\n\\section{Front-End Extensions}\\label{front-end-extensions}\n\nLiferay DXP's modularity has many benefits for the front-end developer,\nin the form of development customizations and extension points. These\nextensions assure the stability, conformity, and future evolution of\nyour applications.\n\nBelow are some of the available front-end extensions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site}{Theme\n  Contributors}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/injecting-additional-context-variables-and-functionality-into-your-theme-templates}{Context\n  Contributors}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/dynamic-includes}{Dynamic\n  Includes}\n\\end{itemize}\n\nSee\n\\href{/docs/7-2/customization/-/knowledge_base/c/theme-components}{Theme\nComponents} and\n\\href{/docs/7-2/customization/-/knowledge_base/c/understanding-the-page-structure}{Understanding\nthe Page Structure} for more information.\n\n\\chapter{Themes}\\label{themes-1}\n\nThemes customize the default look and feel of your site. You can inject\nyour own flavor and personality and represent the visual identity of\nyour brand or company.\n\nYou'll learn these things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-themes}{Developing\n  Themes}: Learn how to use Liferay DXP's tools and features to develop\n  your theme.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/extending-themes}{Extending\n  Themes}: Learn how to use Liferay DXP's theme extension mechanisms and\n  features to add to your theme.\n\\end{itemize}\n\nThemes use the standard components (CSS, JS, and HTML) along with\nFreeMarker templates for rendering. There are several\n\\href{/docs/7-2/customization/-/knowledge_base/c/theme-components\\#theme-templates}{default\nFreeMarker templates} that each handle a key piece of functionality for\nthe page. There are also\n\\href{/docs/7-2/customization/-/knowledge_base/c/theme-components\\#theme-template-utilities}{theme\ntemplate utilities} that let you use portlets, taglibs, theme objects,\nand more in your theme templates.\n\\href{/docs/7-2/customization/-/knowledge_base/c/theme-components\\#css-frameworks-and-extensions}{CSS\nextensions and patterns} come out-of-the-box, and support SASS, and\nmultiple JavaScript frameworks. Several mechanisms are available for\ncustomizing, developing, and extending themes.\n\n\\chapter{Theme Workflow}\\label{theme-workflow}\n\nThemes are built on top of one of the following base themes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Unstyled:} provides basic markup, functions, and images\n\\item\n  \\textbf{Styled:} inherits from the Unstyled base theme and adds some\n  styling on top\n\\end{itemize}\n\nThemes can be built with your choice of tooling. Liferay also offers its\nown set of tools to get your themes up and running quickly.\n\nThe following Liferay tools help you build themes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin}{Theme\n  Builder Gradle Plugin}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Liferay\n  Theme Generator}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}'s\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-template}{Theme\n  Template}.\n\\end{itemize}\n\nDepending on the tool you choose (\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-generator}{Theme\nGenerator},\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin}{Gradle},\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-template}{Blade CLI},\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-template}{Maven}, or\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-template}{Dev Studio}\n), the theme anatomy can be different. The overall development process\nis the same:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Mirror the structure of the files you want to modify. Most of the\n  time, you'll modify these files:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{portal\\_normal.ftl}: main theme markup\n  \\item\n    \\texttt{\\_custom.scss}: custom CSS styling\n  \\item\n    \\texttt{main.js}: the theme's JavaScript\n  \\end{itemize}\n\\item\n  Build and deploy the theme.\n\\item\n  Apply the theme\n  \\href{/docs/7-2/user/-/knowledge_base/u/page-set-look-and-feel}{through\n  the Look and Feel menu} by selecting your\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-thumbnail-preview-for-your-theme}{theme's\n  thumbnail}.\n\\end{enumerate}\n\nThe finished theme is bundled as a WAR (Web application ARchive) file.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} While developing your theme, you should enable\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes}{Developer\nMode}. This disables the JavaScript minifier and caching for CSS and\nFreeMarker template files, which makes debugging easier.\n\n\\noindent\\hrulefill\n\nIf you've built your theme with the Liferay Theme Generator, you can use\nsome helpful Gulp tasks to streamline the process:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{build:} builds your theme's files based on the specified base\n  theme. See the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/building-your-themes-files}{gulp\n  build tutorial} for more information.\n\\item\n  \\textbf{extend:} sets the base theme or themelet to extend. See the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/changing-your-base-theme}{gulp\n  extend tutorial} for more information.\n\\item\n  \\textbf{init:} specifies the app server to deploy your theme to\n  (automatically run during the initial creation of the theme). See the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-your-themes-app-server}{gulp\n  init tutorial} for more information.\n\\item\n  \\textbf{kickstart:} copies files from an existing theme into your\n  theme to help kickstart it. See the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/copying-an-existing-themes-files}{gulp\n  kickstart tutorial} for more information.\n\\item\n  \\textbf{status:} lists the base theme/themelets that your theme\n  extends. See the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/listing-your-themes-extensions}{gulp\n  status tutorial} for more information.\n\\item\n  \\textbf{watch:} watches for changes to your theme's files and\n  automatically deploys them to the server when a change is made. See\n  the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/automatically-deploying-theme-changes}{gulp\n  watch tutorial} for more information.\n\\end{itemize}\n\nSee\n\\href{/docs/7-2/customization/-/knowledge_base/c/theme-components}{Theme\nComponents} and\n\\href{/docs/7-2/customization/-/knowledge_base/c/understanding-the-page-structure}{Understanding\nthe Page Structure} to get a top-level overview of how themes work.\n\n\\chapter{Developing Themes}\\label{developing-themes}\n\nTheme projects created using the\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-generator}{Liferay\nTheme Generator} have access to several\n\\href{https://www.npmjs.com/package/gulp}{gulp} tasks you can execute to\nmanage and develop your theme. This section covers the available actions\nthat these tasks provide, as well as other information you may find\nuseful while developing your theme.\n\nThis section covers these topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Using liferay theme tasks (build, deploy, extend, init, kickstart,\n  status, and watch)\n\\item\n  Using Developer Mode\n\\item\n  Creating thumbnail previews for your theme\n\\item\n  Creating color schemes for your theme\n\\item\n  Making configurable theme settings\n\\end{itemize}\n\nWhile developing your theme, you may notice that your theme's CSS and JS\nfiles are minified. This optimizes performance, but it can make\ndebugging difficult during development. Developer Mode, disabled by\ndefault, optimizes development instead. For instance, it loads CSS and\nJS files individually for easier debugging. Also, you don't have to\nreboot the server as often in Developer Mode. Here is a list of\nDeveloper Mode's key behavior changes and the\n\\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html}{Portal\nProperty} override settings that trigger them (if applicable):\n\n\\begin{itemize}\n\\tightlist\n\\item\n  CSS files are loaded individually rather than being combined and\n  loaded as a single CSS file (\\texttt{theme.css.fast.load=false}).\n\\item\n  Layout template caching is disabled\n  (\\texttt{layout.template.cache.enabled=false}).\n\\item\n  The server does not launch a browser when starting\n  (\\texttt{browser.launcher.url=}).\n\\item\n  FreeMarker Templates for themes and web content are not cached, so\n  changes are applied immediately (via the system setting in your\n  Liferay DXP instance).\n\\item\n  Minification of CSS and JavaScript resources is disabled\n  (\\texttt{minifier.enabled=false}).\n\\end{itemize}\n\nIndividual file loading of your styling and behaviors, combined with\ndisabled caching for layout and FreeMarker templates, lets you see your\nchanges more quickly. These developer settings are defined in the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/portal-impl/src/portal-developer.properties}{\\texttt{portal-developer.properties}\nfile}. See\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes}{Using\nDeveloper Mode with Themes} to learn how to enable Developer Mode in\nyour app server. You can also use the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/automatically-deploying-theme-changes}{Gulp\nWatch task} to test theme changes on a proxy port before deploying your\ntheme to your server.\n\n\\chapter{Using Developer Mode with\nThemes}\\label{using-developer-mode-with-themes}\n\nThis article shows how to enable Developer Mode in your app server\nmanually and through Dev Studio DXP, as well as how to configure other\nsettings that may benefit you during development. Each topic is\nexplained in the relevant section below.\n\n\\section{Enabling Developer Mode\nManually}\\label{enabling-developer-mode-manually}\n\nFollow these steps to enabled Developer Mode in your app server\nmanually:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a \\texttt{portal-ext.properties} file in your server's root\n  folder if it doesn't exist.\n\\item\n  Add the line below to it:\n\n\\begin{verbatim}\ninclude-and-override=portal-developer.properties\n\\end{verbatim}\n\n  Alternatively, add the properties from the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/portal-impl/src/portal-developer.properties}{portal-developer.properties}\n  file to your \\texttt{portal-ext.properties} file that you want to use.\n\\item\n  Start your app server to apply the changes.\n\\end{enumerate}\n\nRead the next section to learn how to enable Developer Mode in Dev\nStudio DXP.\n\n\\section{Setting Developer Mode in Dev Studio\nDXP}\\label{setting-developer-mode-in-dev-studio-dxp}\n\nFollow these steps to enable Developer Mode for your app server in Dev\nStudio DXP:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Double-click on your server in the \\emph{Servers} window and open the\n  \\emph{Liferay Launch} section.\n\\item\n  Select \\emph{Custom Launch Settings} and check the \\emph{Use developer\n  mode} option.\n\\item\n  Save the changes and start your server.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/developer-mode-ide.png}\n\\caption{The \\emph{Use developer mode} option lets you enable Developer\nMode for your server in Dev Studio DXP.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Only change the Server settings from the runtime\nenvironment's Liferay Launch section.\n\n\\noindent\\hrulefill\n\n\\section{Configuring FreeMarker System\nSettings}\\label{configuring-freemarker-system-settings}\n\nBy default, FreeMarker theme templates and web content templates are\ncached. You can change this behavior through System Settings with the\nsteps below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Control Panel and go to \\emph{Configuration} → \\emph{System\n  Settings}.\n\\item\n  Select \\emph{Template Engines} under the \\emph{PLATFORM} heading.\n\\item\n  By default, the \\emph{Resource modification check} (the time in\n  milliseconds that the template is cached) is set to \\texttt{60000}.\n  Set this value to \\texttt{0} to disable caching.\n\\end{enumerate}\n\nYour FreeMarker templates are ready for development. Next you can learn\nhow to improve JavaScript file loading for development.\n\n\\section{JavaScript Fast Loading}\\label{javascript-fast-loading}\n\nBy default, JavaScript fast loading is enabled in Developer Mode\n(\\texttt{javascript.fast.load=true}). This loads the packed version of\nfiles listed in the\n\\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#JavaScript}{Portal\nProperties} \\texttt{javascript.barebone.files} or\n\\texttt{javascript.everything.files}. You can, however, disable\nJavaScript fast loading for easier debugging for development. Just set\n\\texttt{javascript.fast.load} to \\texttt{false} in your\n\\texttt{portal.properties}, or you can disable fast loading by setting\nthe URL parameter \\texttt{js\\_fast\\_load} to \\texttt{0}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} JavaScript fast loading is retrieved from one of three\nplaces: the request (determined by the current URL:\n\\texttt{http://localhost:8080/web/guest/home?js\\_fast\\_load=1}(on) or\n\\texttt{...?js\\_fast\\_load=0}(off)), the Session, or the Portal Property\n(\\texttt{javascript.fast.load=true}). Preference is given in the order\nof request, session, and then Portal Properties. This lets you change\n\\texttt{js\\_fast\\_load}'s value from the default in\n\\texttt{portal.properties} without having to manually re-enter\n\\texttt{js\\_fast\\_load} into the URL upon every new page load.\n\n\\noindent\\hrulefill\n\nGreat! You've set up your server for Developer Mode. Now, when you\nmodify your theme's file directly in your bundle, you can see your\nchanges applied immediately on redeploying your theme!\n\n\\section{Related Topics}\\label{related-topics-49}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator}{Generating\n  Layout Templates with the Theme Generator}\n\\end{itemize}\n\n\\chapter{Building Your Theme's Files}\\label{building-your-themes-files}\n\nFollow these steps to build your theme's files with the Build task. Note\nthat this task only works for themes that use the\n\\href{https://github.com/liferay/liferay-themes-sdk/tree/master/packages}{liferay\nJS Theme Toolkit}, such as those created with the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Liferay\nTheme Generator}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Gulp is included as a local dependency in generated\nthemes, so you are not required to install it. It can be accessed by\nrunning \\texttt{node\\_modules\\textbackslash{}.bin\\textbackslash{}gulp}\nfollowed by the Gulp task from a generated theme's root folder.\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to your theme's root folder and run \\texttt{gulp\\ build}.\n\\item\n  The \\texttt{gulp\\ build} task generates the base theme files (in the\n  \\texttt{build} folder), compiles Sass into CSS, and compresses all\n  theme files into a \\texttt{.war} file (in the \\texttt{dist} folder),\n  that you can deploy to your server. Copy any of these files and\n  folders to your theme's \\texttt{src} folder to modify them.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes}{Deploy}\n  the \\texttt{war} file to your app server to make it available.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-dev-building-themes-gulp-build.png}\n\\caption{Run the \\texttt{gulp\\ build} task to build your theme's files.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-50}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/automatically-deploying-theme-changes}{Automatically\n  Deploying Theme Changes}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/copying-an-existing-themes-files}{Copying\n  an Existing Theme's Files}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes}{Deploying\n  and Applying Themes}\n\\end{itemize}\n\n\\chapter{Deploying and Applying\nThemes}\\label{deploying-and-applying-themes}\n\nFollow these steps to deploy your theme with the Deploy task. Note that\nthis task only works for themes that use the\n\\href{https://github.com/liferay/liferay-themes-sdk/tree/master/packages}{liferay\nJS Theme Toolkit}, such as those created with the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Liferay\nTheme Generator}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Gulp is included as a local dependency in generated\nthemes, so you are not required to install it. It can be accessed by\nrunning \\texttt{node\\_modules\\textbackslash{}.bin\\textbackslash{}gulp}\nfollowed by the Gulp task from a generated theme's root folder.\n\n\\noindent\\hrulefill\n\nFollow these steps to deploy your theme:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Navigate to your theme's root folder and run \\texttt{gulp\\ deploy}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** If you're running the \n [Felix Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell), \n you can also deploy your theme using the `gulp deploy:gogo` command.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Your server's log displays that the OSGi bundle is started.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/theme-dev-deploying-themes-server-log.png}\n  \\caption{Your server's log notifies you when the theme's bundle has\n  started.}\n  \\end{figure}\n\\item\n  Apply your theme through the \\emph{Build} → \\emph{Pages} menu in the\n  Control Menu. Select the \\emph{Configure} option for your site pages,\n  and click the \\emph{Change Current Theme} button to apply your theme.\n\\end{enumerate}\n\nWonderful! Your theme is deployed to your server and applied to your\nsite.\n\n\\section{Related Topics}\\label{related-topics-51}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/automatically-deploying-theme-changes}{Automatically\n  Deploying Theme Changes}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/copying-an-existing-themes-files}{Copying\n  an Existing Theme's Files}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator}{Creating\n  Themelets with the Theme Generator}\n\\end{itemize}\n\n\\chapter{Updating Your Theme's App\nServer}\\label{updating-your-themes-app-server}\n\nFollow these steps to update the configuration for your theme's app\nserver with the Init task. Note that this task only works for themes\nthat use the\n\\href{https://github.com/liferay/liferay-themes-sdk/tree/master/packages}{liferay\nJS Theme Toolkit}, such as those created with the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Liferay\nTheme Generator}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Gulp is included as a local dependency in generated\nthemes, so you are not required to install it. It can be accessed by\nrunning \\texttt{node\\_modules\\textbackslash{}.bin\\textbackslash{}gulp}\nfollowed by the Gulp task from a generated theme's root folder.\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to your theme's root folder and run \\texttt{gulp\\ init}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/theme-dev-server-configuration-gulp-init.png}\n  \\caption{Run the \\texttt{gulp\\ init} task to update your app server\n  configuration.}\n  \\end{figure}\n\\item\n  Enter the updated path to your app server and site.\n\\item\n  Your theme's \\texttt{liferay-theme.json} file contains the updated\n  server configuration information:\n\n\\begin{verbatim}\n{\n  \"LiferayTheme\": {\n    \"deploymentStrategy\": \"LocalAppServer\",\n    \"appServerPath\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0\\\\liferay-ce-portal-7.2.0\\\\tomcat-9.0.10\",\n    \"deployPath\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0\\\\liferay-ce-portal-7.2.0\\\\deploy\",\n    \"url\": \"http://localhost:8080\",\n    \"appServerPathPlugin\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0\\\\liferay-ce-portal-7.2.0\\\\tomcat-9.0.10\\\\webapps\\\\my-new72theme-theme\",\n    \"deployed\": true,\n    \"pluginName\": \"my-new72theme-theme\"\n  }\n}\n\\end{verbatim}\n\\end{enumerate}\n\nAwesome! Now you can\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes}{deploy\nyour theme} to the proper server.\n\n\\section{Related Topics}\\label{related-topics-52}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/automatically-deploying-theme-changes}{Automatically\n  Deploying Theme Changes}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/changing-your-base-theme}{Changing\n  Your Base Theme}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/listing-your-themes-extensions}{Listing\n  Your Theme's Extensions}\n\\end{itemize}\n\n\\chapter{Automatically Deploying Theme\nChanges}\\label{automatically-deploying-theme-changes}\n\nFollow these steps to automatically preview your theme's changes with\nthe Watch task. Note that this task only works for themes that use the\n\\href{https://github.com/liferay/liferay-themes-sdk/tree/master/packages}{liferay\nJS Theme Toolkit}, such as those created with the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Liferay\nTheme Generator}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Gulp is included as a local dependency in generated\nthemes, so you are not required to install it. It can be accessed by\nrunning \\texttt{node\\_modules\\textbackslash{}.bin\\textbackslash{}gulp}\nfollowed by the Gulp task from a generated theme's root folder.\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Navigate to your theme's root folder and run \\texttt{gulp\\ watch}.\n  This sets up a proxy for your app server and opens it in a new window\n  in the browser.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Live changes are only viewable on port `9080` \n (`http://localhost:9080`). Live changes **are not viewable** on your app \n server (e.g. `http://localhost:8080`).\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n![ Run the `gulp watch` task to automatically deploy any changes to your theme.](./images/theme-dev-watching-themes-gulp-watch-startup.png)\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Make a change to your theme and save the file. The updated files are\n  built, compiled, and copied directly to the proxy port\n  (e.g.~\\texttt{9080}). CSS changes are deployed live, so no page reload\n  is needed.\n\\item\n  Once you're happy with the changes,\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes}{re-deploy}\n  your theme to apply the changes to your site on your app server.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/theme-dev-watching-themes-gulp-watch-auto-deploy.png}\n  \\caption{The watch task notifies you that the changes are deployed.}\n  \\end{figure}\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-53}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-your-themes-app-server}{Configuring\n  Your Theme's App Server}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/copying-an-existing-themes-files}{Copying\n  an Existing Theme's Files}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes}{Deploying\n  and Applying Themes}\n\\end{itemize}\n\n\\chapter{Creating a Thumbnail Preview for Your\nTheme}\\label{creating-a-thumbnail-preview-for-your-theme}\n\nWhen you apply a theme to your site pages, you have to choose from the\nlist of available themes in the site selector. The only identification\nfor each theme is the theme's name, along with a small thumbnail preview\nimage that gives a brief impression of the theme. This is even more\nimportant when developing color schemes for a theme, since names are not\ndisplayed for color schemes.\n\nThis article shows how to create a thumbnail preview for your theme so\nusers can identify it.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-dev-theme-thumbnail-default.png}\n\\caption{Your theme thumbnail is displayed with the rest of the\navailable themes.}\n\\end{figure}\n\nYour first step in creating a thumbnail preview for your theme is taking\na screenshot of your theme. Once you have a screenshot that you like,\nfollow the steps below to create a thumbnail preview for your theme:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Resize the screenshot to 270 pixels high by 480 pixels wide. Your\n  thumbnail \\emph{must be} these exact dimensions to display properly.\n\\item\n  Save the image as a \\texttt{.png} file named \\texttt{thumbnail.png}\n  and place it in the theme's \\texttt{src/images} folder (create this\n  folder if it doesn't already exist).\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The\n [Theme Builder Gradle plugin](/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin)\n doesn't recognize a `thumbnail.png` file. If you're using this plugin to\n build your theme instead, you must create a `screenshot.png` file in your\n theme's `images` folder that is 1080 pixels high by 864 pixels wide. The\n thumbnail is automatically generated from the screenshot for you when the\n theme is built.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\tightlist\n\\item\n  Redeploy the theme. The file is displayed as the theme's thumbnail.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-dev-theme-thumbnail-custom.png}\n\\caption{Your theme thumbnail is displayed with the rest of the\navailable themes.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-54}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Installing\n  the Theme Generator and Creating a Theme}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator}{Creating\n  Themelets with the Theme Generator}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-color-schemes-for-your-theme}{Creating\n  Color Schemes for Your Theme}\n\\end{itemize}\n\n\\chapter{Creating Color Schemes for Your\nTheme}\\label{creating-color-schemes-for-your-theme}\n\nColor schemes give your theme additional color palettes. With just a\nsmall amount of changes to your theme's CSS, you can subtly change the\nlook of your theme, while maintaining the same design and feel to it.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-dev-color-schemes.png}\n\\caption{Color schemes give administrators some choices for your theme's\nlook.}\n\\end{figure}\n\nFollow these steps to create color schemes for your theme:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the theme's \\texttt{WEB-INF/liferay-look-and-feel.xml} file and\n  follow the pattern below to add the default color scheme. If your\n  default styles are in \\texttt{\\_custom.scss}, use the \\texttt{default}\n  \\texttt{\\textless{}css-class\\textgreater{}} as shown in the example\n  below. See the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/definitions/liferay-look-and-feel_7_2_0.dtd.html\\#color-scheme}{liferay-look-and-feel\n  DTD} for an explanation of each of the elements used below:\n\n\\begin{verbatim}\n<theme id=\"my-theme-id\" name=\"My Theme Name\">\n   <color-scheme id=\"01\" name=\"My Default Color Scheme Name\">\n       <default-cs>true</default-cs>\n       <css-class>default</css-class>\n\n       <color-scheme-images-path>\n           ${images-path}/my_color_schemes_folder_name/${css-class}\n       </color-scheme-images-path>\n   </color-scheme>\n   ...\n</theme>\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Color schemes are sorted alphabetically by `name` rather than \n `id`. For example, a color scheme named `Clouds` and `id` `02` would be \n selected by default over a color scheme named `Day` with `id` `01`. The \n `<default-cs>` element overrides the alphabetical sorting and sets the \n color scheme that is selected by default when the theme is chosen.  \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Add the remaining color schemes below the default color scheme, using\n  the pattern below. Note that the IDs, names, and CSS classes must be\n  unique for each color scheme.\n\n\\begin{verbatim}\n<color-scheme id=\"id-number\" name=\"Color Scheme Name\">\n   <css-class>color-scheme-css-class</css-class>\n</color-scheme>\n\\end{verbatim}\n\n  An example \\texttt{liferay-look-and-feel.xml} configuration is shown\n  below:\n\n\\begin{verbatim}\n<look-and-feel>\n    <compatibility>\n        <version>7.2.0+</version>\n    </compatibility>\n    <theme id=\"my-great-theme\" name=\"My Great Theme\">\n        <template-extension>ftl</template-extension>\n        <color-scheme id=\"01\" name=\"Default\">\n                <default-cs>true</default-cs>\n                <css-class>default</css-class>\n                <color-scheme-images-path>\n                        ${images-path}/color_schemes/${css-class}\n                </color-scheme-images-path>\n        </color-scheme>\n        <color-scheme id=\"02\" name=\"Dark\">\n            <css-class>dark</css-class>\n        </color-scheme>\n        <color-scheme id=\"03\" name=\"Light\">\n            <css-class>light</css-class>\n        </color-scheme>\n        <portlet-decorator ...>\n            ...\n    </theme>\n</look-and-feel>\n\\end{verbatim}\n\\item\n  Create a folder for your color schemes (\\texttt{color\\_schemes} for\n  example) in the theme's \\texttt{css} folder, and add a \\texttt{.scss}\n  file to it for each color scheme your theme supports, excluding the\n  default color scheme since those styles are included in\n  \\texttt{\\_custom.scss}.\n\\item\n  The color scheme class is added to the theme's\n  \\texttt{\\textless{}body\\textgreater{}} element when the color scheme\n  is applied, so add the class to the color scheme's styles to target\n  the proper color scheme. The example below specifies styles for a\n  color scheme with the class \\texttt{day}:\n\n\\begin{verbatim}\nbody.day { background-color: #DDF; }\n.day a { color: #66A; }\n\\end{verbatim}\n\\item\n  Import the color scheme \\texttt{.scss} files into the theme's\n  \\texttt{\\_custom.scss} file. The example below imports\n  \\texttt{\\_day.scss} and \\texttt{\\_night.scss} files:\n\n\\begin{verbatim}\n@import \"color_schemes/day\";\n@import \"color_schemes/night\";\n\\end{verbatim}\n\\item\n  Create a folder for each color scheme in your theme's \\texttt{images}\n  folder, and add\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-thumbnail-preview-for-your-theme}{a\n  thumbnail preview} for them. The folder name \\emph{must match} the\n  color scheme's CSS class name.\n\\end{enumerate}\n\nThere you have it. Now you can go color scheme crazy with your themes!\n\n\\section{Related Topics}\\label{related-topics-55}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator}{Generating\n  Layout Templates}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-thumbnail-preview-for-your-theme}{Creating\n  a Thumbnail Preview for Your Theme}\n\\end{itemize}\n\n\\chapter{Making Configurable Theme\nSettings}\\label{making-configurable-theme-settings}\n\nIf you have an aspect of a theme that you want an Administrator to\nconfigure without having to manually update and redeploy the theme, you\ncan create a \\emph{theme setting} for it. Theme settings are very\nversatile and can be customized to meet your needs.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-dev-configurable-theme-settings.png}\n\\caption{Here are examples of configurable settings for the site Admin.}\n\\end{figure}\n\nFollow the steps below to create theme settings:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your theme's \\texttt{WEB-INF/liferay-look-and-feel.xml} file, and\n  follow the pattern below to nest a\n  \\texttt{\\textless{}setting/\\textgreater{}} element inside the parent\n  \\texttt{\\textless{}settings\\textgreater{}} element for each setting\n  you want to add:\n\n\\begin{verbatim}\n<look-and-feel>\n    <compatibility>\n        <version>7.2.0+</version>\n    </compatibility>\n    <theme id=\"your-theme-name\" name=\"Your Theme Name\">\n        <template-extension>ftl</template-extension>\n    <settings>\n      <setting configurable=\"true\" key=\"theme-setting-key\"\n      options=\"true,false\" type=\"select\" value=\"true\" />\n      <setting configurable=\"true\" key=\"theme-setting-key\"\n      type=\"text\" value=\"My placeholder text\" />\n    </settings>\n    <portlet-decorator>\n      portlet decorators...\n        </portlet-decorator>\n    </theme>\n</look-and-feel>\n\\end{verbatim}\n\n  The example below adds a text input setting for a custom hex code:\n\n\\begin{verbatim}\n<settings>\n  <setting configurable=\"true\" key=\"my-hex-code\" type=\"text\" value=\"blue\" />\n</settings>\n\\end{verbatim}\n\n  See the \\texttt{liferay-look-and-feel.xml}'s\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/definitions/liferay-look-and-feel_7_2_0.dtd.html\\#settings}{DTD\n  docs} for an explanation of the setting's configuration options.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You can modify theme settings with JavaScript to provide a more \n custom experience. The example below modifies the theme setting, changing \n its `type` to `color`, to provide a color picker for the user:  \n\n ```xml\n <setting configurable=\"true\" key=\"user-color\"\n type=\"text\" value=\"#993300\"  \n >\n <![CDATA[  \n      AUI().ready('node',function(A) {\n           A.one(\"#[@NAMESPACE@]user-color\").setAttribute(\"type\", \"color\");    \n           A.one(\"#[@NAMESPACE@]user-color\").setAttribute(\"style\", \"height: 35px; width: 200px\");    \n       });\n ]]>\n </setting>\n ```\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\tightlist\n\\item\n  Create a file called \\texttt{init\\_custom.ftl} in your theme's\n  \\texttt{templates} folder if it doesn't already exist, and follow the\n  patterns in the table below to define your theme setting variables in\n  it:\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n Return Type | Description | Pattern |\n --- | --- | --- |\n Boolean | a select box with the options `true` and `false` or a checkbox with values `yes` and `no` | `<#assign my_variable_name = getterUtil.getBoolean(themeDisplay.getThemeSetting(\"theme-setting-key\"))/>` |\n String | a text input or text area input | `<#assign my_variable_name = getterUtil.getString(themeDisplay.getThemeSetting(\"theme-setting-key\"))/>` |\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nThe example below adds a custom hex code setting:\n\n    <#assign my_hex_code = \n    getterUtil.getString(themeDisplay.getThemeSetting(\"my-hex-code\"))/>\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Add your theme setting variables to the theme template. The example\n  below prints \\texttt{my\\_hex-code}'s value as the value of the\n  header's \\texttt{style} attribute:\n\n  \\texttt{portal\\_normal.ftl}:\n\n\\begin{verbatim}\n<header style=\"background-color:${my_hex_code}\">\n\\end{verbatim}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes}{Deploy\n  the theme} to apply the changes. To set the theme setting for a Public\n  or Private page set, click the \\emph{Gear icon} next to the page set\n  you want to configure and update the setting under the \\emph{Look and\n  Feel} tab. Alternatively, you can set the theme setting for an\n  individual page by opening the \\emph{Actions menu} next to the page\n  and selecting \\emph{Configure} and choosing the \\emph{Define a\n  Specific look and feel for this page} option.\n\\end{enumerate}\n\nGreat! You've created configurable settings for your theme.\n\n\\section{Related Topics}\\label{related-topics-56}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator}{Creating\n  Themelets with the Theme Generator}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/listing-your-themes-extensions}{Listing\n  Your Theme's Extensions}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/importing-resources-with-a-theme}{Importing\n  Resources with a Theme}\n\\end{itemize}\n\n\\chapter{Using Font Awesome and Glyph Icons in Your\nTheme}\\label{using-font-awesome-and-glyph-icons-in-your-theme}\n\nBy default, \\href{https://fontawesome.com/v3.2.1/}{Font Awesome v3.2.1}\nand \\href{https://getbootstrap.com/docs/3.3/components/}{Bootstrap 3\nGlyphicons} are enabled globally in Liferay DXP via a system setting.\nThis means that you can use them in your themes to create social media\nlinks, for example. A Site Administrator can disable this to improve\nperformance, if they choose.\n\n\\section{Disabling Enabling Global Font Awesome and Glyphicons in\nPortal}\\label{disabling-enabling-global-font-awesome-and-glyphicons-in-portal}\n\nSince Liferay DXP Fix Pack 2 and Liferay Portal 7.2 CE GA2, Font Awesome\nis available globally as a system setting, which is enabled by default.\nYou can disable this setting to improve performance. To update the\nsetting, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Control Menu and navigate to \\emph{Control Panel} →\n  \\emph{Configuration} → \\emph{System Settings} and select \\emph{Third\n  Party} under the \\emph{PLATFORM} heading.\n\\item\n  Select \\emph{Font Awesome} under the \\emph{System Scope} and\n  check/uncheck the \\emph{Enable Font Awesome} checkbox to\n  enable/disable Font Awesome icons and Glyphicons across the site.\n\\item\n  Click \\emph{Save} to save the configuration.\n\\end{enumerate}\n\n\\section{Including Font Awesome and Glyphicons in Your\nTheme}\\label{including-font-awesome-and-glyphicons-in-your-theme}\n\nAs a safeguard, you should include Font Awesome and Glyphicons with your\ntheme if you want to use them. This ensures that your icons won't break\nif the global system setting is disabled. If you created the theme with\nthe Liferay Theme Generator and answered yes (y) to the Font Awesome\nprompt, the Font Awesome dependency is added which includes Font Awesome\nand Glyphicons for you. If you didn't include Font Awesome and\nGlyphicons when you initially created the theme, follow these steps to\ninclude them in your theme now:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Use the Font Awesome v3.2.1 or Bootstrap 3 Glyphicons in your theme's\n  template. The example below uses Font Awesome icons:\n\n\\begin{verbatim}\n<div id=\"social-media-links\">\n  <ul class=\"nav flex-row mx-auto\">\n      <li class=\"mx-2\">\n          <div id=\"facebook\">\n            <a class=\"icon-facebook icon-3x text-white\"\n            href=\"http://www.facebook.com/pages/Liferay/45119213107\" \n            target=\"_blank\"><span class=\"hide\">Facebook</span>\n            </a>\n          </div>\n      </li>\n      <li class=\"mx-2\">\n          <div id=\"twitter\">\n            <a class=\"icon-twitter icon-3x text-white\" \n            href=\"http://www.twitter.com/liferay\" \n            target=\"_blank\"><span class=\"hide\">Twitter</span>\n            </a>\n          </div>\n      </li>\n      <li class=\"mx-2\">\n          <div id=\"linked-in\">\n            <a class=\"icon-linkedin icon-3x text-white\"\n            href=\"http://www.linkedin.com/company/83609\" \n            target=\"_blank\"><span class=\"hide\">LinkedIn</span>\n            </a>\n          </div>\n      </li>\n      <li class=\"mx-2\">\n          <div id=\"youtube\">\n            <a class=\"icon-youtube icon-3x text-white\" \n            href=\"http://www.youtube.com/user/liferayinc\" \n            target=\"_blank\"><span class=\"hide\">YouTube</span>\n            </a>\n          </div>\n      </li>\n      <li class=\"mx-2\">\n          <div id=\"google-plus\">\n            <a class=\"icon-google-plus icon-3x text-white\"\n            href=\"https://plus.google.com/+liferay/posts\" \n            target=\"_blank\"><span class=\"hide\">Google</span>\n            </a>\n          </div>\n      </li>\n  </ul>\n</div>\n\\end{verbatim}\n\\item\n  Open the theme's \\texttt{package.json} and include the\n  \\texttt{liferay-font-awesome} dev dependency:\n\n\\begin{verbatim}\n\"liferay-font-awesome\": \"3.4.0\"\n\\end{verbatim}\n\\item\n  Run \\texttt{gulp\\ deploy} from the theme's root folder to build and\n  deploy the theme's files. This adds a \\texttt{/css/font/} folder to\n  the theme's \\texttt{build} folder that contains the Font Awesome and\n  Glyphicon fonts.\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-57}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Installing\n  the Theme Generator and Creating a Theme}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-theme-to-7-2}{Upgrading\n  Themes}\n\\end{itemize}\n\n\\chapter{Extending Themes}\\label{extending-themes}\n\nLiferay DXP has additional features that you can use to extend your\ntheme and modify your site. This section covers these topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Installing Themelets\n\\item\n  Injecting additional context variables into your theme templates\n\\item\n  Packaging independent UI resources for your site\n\\item\n  Copying an existing theme's files\n\\item\n  Listing your theme's extensions\n\\item\n  Overwriting and extending liferay theme tasks\n\\end{itemize}\n\n\\chapter{Installing a Themelet in Your\nTheme}\\label{installing-a-themelet-in-your-theme}\n\nAfter you've created your\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator}{themelet},\nfollow the steps below to install it into your theme.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Gulp is included as a local dependency in generated\nthemes, so you are not required to install it. It can be accessed by\nrunning \\texttt{node\\_modules\\textbackslash{}.bin\\textbackslash{}gulp}\nfollowed by the Gulp task from a generated theme's root folder.\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to your theme's root folder and run \\texttt{gulp\\ extend}.\n\\item\n  Choose \\emph{Themelet} as the theme asset to extend.\n\\item\n  Select \\emph{Search globally installed npm modules}, \\emph{Search npm\n  registry}, or \\emph{Specify a package URL} to locate the themelet.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/install-themelet.png}\n  \\caption{You can extend your theme using globally installed npm\n  modules or published npm modules.}\n  \\end{figure}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You can retrieve the URL for a package by running \n `npm show package-name dist.tarball`. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\tightlist\n\\item\n  Highlight your themelet, press spacebar to activate it, and press\n  \\emph{Enter} to install it.\n\\end{enumerate}\n\nGreat, now you know how to install a themelet in your theme! The next\ntime you\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes}{deploy}\nyour theme, the themelet will be bundled along with it.\n\n\\section{Related Topics}\\label{related-topics-58}\n\n-\\href{/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator}{Generating\nThemelets with the Theme Generator}\n-\\href{/docs/7-2/frameworks/-/knowledge_base/f/injecting-additional-context-variables-and-functionality-into-your-theme-templates}{Injecting\nAdditional Context Variables and Functionality into Your Theme\nTemplates}\n-\\href{/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site}{Packaging\nIndependent UI Resources for Your Site}\n\n\\chapter{Injecting Additional Context Variables and Functionality into\nYour Theme\nTemplates}\\label{injecting-additional-context-variables-and-functionality-into-your-theme-templates}\n\nJSPs are native to Java EE and therefore have access to all the\ncontextual objects inherit to the platform, like the request and\nsession. Through these objects, developers can obtain Liferay\nDXP-specific context information by accessing container objects like\n\\texttt{themeDisplay} or \\texttt{serviceContext}. This, however, is not\nthe case for FreeMarker templates. To access this information in\nFreeMarker templates, you must inject it yourself into the template's\ncontext. Liferay DXP gives you a head start by injecting several common\nobjects into the template's context and exposing them as\n\\href{/docs/7-2/reference/-/knowledge_base/r/product-freemarker-macros}{FreeMarker\nmacros}. To inject other objects into the FreeMarker template's context,\nyou must create a \\emph{Context Contributor}.\n\nYou can create a Context Contributor to use non-JSP templating languages\nfor themes, widget templates, and any other templates used in Liferay\nDXP. For example, suppose you want your theme to change color based on\nthe user's organization. You could create a Context Contributor to\ninject the user's organization into your theme's context, and then\ndetermine the theme's color based on that information.\n\nFollow the steps below to create a context contributor:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create an OSGi module using your favorite third party tool, or use\n  \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}.\n\\item\n  Create a component class that implements the\n  \\texttt{TemplateContextContributor} service, and set the \\texttt{type}\n  property to the type of context you're injecting into. Set it to\n  \\texttt{TYPE\\_THEME} to inject context-specific variables for your\n  theme, or set it to \\texttt{TYPE\\_GLOBAL} to inject it into every\n  context execution in Liferay DXP, like themes, widget templates, DDM\n  templates, etc, as defined in\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/template/TemplateContextContributor.html}{TemplateContextContributor}.\n  To follow naming conventions, begin the class name with the entity you\n  want to inject context-specific variables for, followed by\n  \\emph{TemplateContextContributor} (e.g.,\n  \\texttt{ProductMenuTemplateContextContributor}):\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\"type=\" + TemplateContextContributor.TYPE_THEME},\n    service = TemplateContextContributor.class\n)\n\\end{verbatim}\n\\item\n  Implement the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/template/TemplateContextContributor.html}{TemplateContextContributor}\n  interface in your \\texttt{*TemplateContextContributor} class, and\n  overwrite the\n  \\texttt{prepare(Map\\textless{}String,Object\\textgreater{},\\ HttpServletRequest)}\n  method to inject new or modified variables into the\n  \\texttt{contextObjects} map. This is your template's context that was\n  described earlier.\n\\end{enumerate}\n\nThe \\texttt{ProductMenuTemplateContextContributor}'s class is shown as\nan example below. It overwrites the \\texttt{prepare(...)} method to\ninject a modified \\texttt{bodyCssClass} variable and a new\n\\texttt{liferay\\_product\\_menu\\_state} variable into the theme context\nfor the Product Menu. Specifically, the \\texttt{cssClass} variable\nprovides styling for the Product Menu and the \\texttt{productMenuState}\nvariable determines whether the visible Product Menu should be open or\nclosed:\n\n\\begin{verbatim}\n@Override\npublic void prepare(\n    Map<String, Object> contextObjects, HttpServletRequest request) {\n\n    if (!isShowProductMenu(request)) {\n        return;\n    }\n\n    String cssClass = GetterUtil.getString(\n        contextObjects.get(\"bodyCssClass\"));\n    String productMenuState = SessionClicks.get(\n        request,\n        ProductNavigationProductMenuWebKeys.\n            PRODUCT_NAVIGATION_PRODUCT_MENU_STATE,\n        \"closed\");\n\n    contextObjects.put(\n        \"bodyCssClass\", cssClass + StringPool.SPACE + productMenuState);\n\n    contextObjects.put(\"liferay_product_menu_state\", productMenuState);\n}\n\\end{verbatim}\n\nThe \\texttt{ProductMenuTemplateContextContributor} provides an easy way\nto inject variables into Liferay DXP's theme directly related to the\nProduct Menu. You can do the same with your custom context contributor.\nWith the power to inject additional variables into any context in\nLiferay DXP, you're free to fully harness the power of your chosen\ntemplating language.\n\n\\section{Related Topics}\\label{related-topics-59}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-themes}{Developing\n  Themes}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site}{Theme\n  Contributors}\n\\end{itemize}\n\n\\chapter{Packaging Independent UI Resources for Your\nSite}\\label{packaging-independent-ui-resources-for-your-site}\n\nIf you want to package UI resources independent of a specific theme and\ninclude them on every page, a \\emph{Theme Contributor} is your best\noption. If, instead, you want to include separate UI resources on a page\nthat are attached to a theme, use\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator}{themelets}.\n\nA Theme Contributor is a module that contains CSS and JS resources to\napply to the page. The Control Menu, Product Menu, and Simulation Panel\nare packaged as Theme Contributors.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-contributor-menus-diagram.png}\n\\caption{The Control Menu, Product Menu, and Simulation Panel are\npackaged as Theme Contributor modules.}\n\\end{figure}\n\nIf you want to edit or style these standard UI components, you must\ncreate a Theme Contributor and add your modifications on top. You can\nalso add new UI components to Liferay DXP by creating a Theme\nContributor. This article shows how to create a Theme Contributor\nmodule.\n\nFollow these steps to create a Theme Contributor:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a generic OSGi module using your favorite third party tool, or\n  use \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade\n  CLI}. You can also use the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-contributor-template}{Blade\n  Template} to create your module, in which case you can skip step 2.\n\\item\n  Add the \\texttt{Liferay-Theme-Contributor-Type} header to your\n  module's \\texttt{bnd.bnd} file to identify your module as a Theme\n  Contributor, and add the \\texttt{Web-ContextPath} header to set the\n  context from which the Theme Contributor's resources are hosted. See\n  the\n  \\href{https://search.maven.org/search?q=a:com.liferay.product.navigation.control.menu.theme.contributor}{Control\n  Menu module's} \\texttt{bnd.bnd} below as an example:\n\n\\begin{verbatim}\nBundle-Name: Liferay Product Navigation Product Menu Theme Contributor\nBundle-SymbolicName: com.liferay.product.navigation.product.menu.theme.contributor\nBundle-Version: 3.0.4\nLiferay-Theme-Contributor-Type: product-navigation-product-menu\nWeb-ContextPath: /product-navigation-product-menu-theme-contributor\n\\end{verbatim}\n\n  The Theme Contributor type helps Liferay DXP better identify your\n  module. If you're creating a Theme Contributor to override an existing\n  Theme Contributor, you should try to use the same type to maximize\n  compatibility with future developments.\n\\item\n  Add the \\texttt{Liferay-Theme-Contributor-Weight} to your\n  \\texttt{bnd.bnd} file to set a priority for your Theme Contributor. To\n  override another Theme Contributor's styles, such as those for the\n  Control Menu, set a higher weight. The higher the value, the higher\n  the priority. If your Theme Contributor has a weight of 100, it will\n  be loaded after one with a weight of 99, allowing your CSS to override\n  theirs:\n\n\\begin{verbatim}\nLiferay-Theme-Contributor-Weight: [value]\n\\end{verbatim}\n\\item\n  Create a \\texttt{src/main/resources/META-INF/resources} folder in your\n  module and place your resources (CSS and JS) in that folder.\n\\item\n  Build and deploy your module to see your modifications applied to\n  Liferay DXP pages and themes.\n\\end{enumerate}\n\nThat's all you need to do to create a Theme Contributor for your site.\nRemember, with great power comes great responsibility, so use Theme\nContributors wisely. The UI contributions affect every page and aren't\naffected by theme deployments.\n\n\\section{Related Topics}\\label{related-topics-60}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-themes}{Developing\n  Themes}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator}{Generating\n  Themelets}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/installing-a-themelet-in-your-theme}{Installing\n  a Themelet}\n\\end{itemize}\n\n\\chapter{Changing Your Base Theme}\\label{changing-your-base-theme}\n\nFollow these steps to change your theme's base theme with the Extend\ntask. Note that this task only works for themes that use the\n\\href{https://github.com/liferay/liferay-themes-sdk/tree/master/packages}{liferay\nJS Theme Toolkit}, such as those created with the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Liferay\nTheme Generator}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Gulp is included as a local dependency in generated\nthemes, so you are not required to install it. It can be accessed by\nrunning \\texttt{node\\_modules\\textbackslash{}.bin\\textbackslash{}gulp}\nfollowed by the Gulp task from a generated theme's root folder.\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to your theme's root folder and run \\texttt{gulp\\ extend}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/theme-ext-changing-base-themes-gulp-extend-base-theme.png}\n  \\caption{Run the \\texttt{gulp\\ extend} task to change your base\n  theme.}\n  \\end{figure}\n\\item\n  Enter 1 to select a new base theme to extend.\n\\item\n  By default, themes created with the\n  \\href{https://github.com/liferay/generator-liferay-theme}{Liferay\n  Theme Generator} are based off of the\n  \\href{https://www.npmjs.com/package/liferay-theme-styled}{styled\n  theme}. You can extend the styled or unstyled base theme, a globally\n  installed theme, a theme published on the npm registry, or you can\n  specify a package URL. Enter the number for the option you wish to\n  select.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You can retrieve the URL for a package by running \n `npm show package-name dist.tarball`. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n![ You can extend the styled or unstyled base theme, a globally installed theme, a theme published to the npm registry, or you can specify a package URL.](./images/theme-ext-changing-base-themes-gulp-extend-base-theme-choice.png)\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The Classic theme is an implementation of an existing base theme \n and is therefore not meant to be extended. Extending Liferay's Classic \n theme is strongly discouraged.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nYour theme's \\texttt{package.json} contains the updated base theme\nconfiguration:\n\n\\begin{verbatim}\n\"liferayTheme\": {\n  \"baseTheme\": \"styled\",\n  \"screenshot\": \"\",\n  \"templateLanguage\": \"ftl\",\n  \"version\": \"7.2\"\n},\n\\end{verbatim}\n\nGreat! You've updated your base theme. When you\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/building-your-themes-files}{build\nyour theme's files} or\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes}{deploy\nit}, your theme will inherit the updated base theme's files.\n\n\\section{Related Topics}\\label{related-topics-61}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-your-themes-app-server}{Configuring\n  Your Theme's App Server}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes}{Deploying\n  and Applying Themes}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/listing-your-themes-extensions}{Listing\n  Your Theme's Extensions}\n\\end{itemize}\n\n\\chapter{Copying an Existing Theme's\nFiles}\\label{copying-an-existing-themes-files}\n\nFollow these steps to copy an existing theme's files with the Kickstart\ntask. Unlike extending a base theme, which is a dynamic inheritance that\napplies your \\texttt{src} files on top of the base theme on every build,\nthe Kickstart task is a one time inheritance.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} The gulp kickstart task copies an existing theme's\nfiles into your own, which can potentially overwrite files with the same\nname. Proceed with caution.\n\n\\noindent\\hrulefill\n\nNote that this task only works for themes that use the\n\\href{https://github.com/liferay/liferay-themes-sdk/tree/master/packages}{liferay\nJS Theme Toolkit}, such as those created with the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Liferay\nTheme Generator}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Gulp is included as a local dependency in generated\nthemes, so you are not required to install it. It can be accessed by\nrunning \\texttt{node\\_modules\\textbackslash{}.bin\\textbackslash{}gulp}\nfollowed by the Gulp task from a generated theme's root folder.\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to your theme's root folder and run \\texttt{gulp\\ kickstart}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/theme-ext-kickstarting-themes-gulp-kickstart.png}\n  \\caption{Run the \\texttt{gulp\\ kickstart} task to copy a theme's files\n  into your own theme.}\n  \\end{figure}\n\\item\n  Select where to search for the theme to copy. You can copy files from\n  globally installed themes or themes published on the npm registry.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** **You can't kickstart the Classic Theme.**\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** To globally install a theme, run the `npm link` command from the \n theme's root folder.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n![ You can copy files from  globally installed themes.](./images/theme-ext-kickstarting-themes-global-theme.png)\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\tightlist\n\\item\n  The theme's files are copied into your own theme, jump starting\n  development. Add your changes on top of these files.\n\\end{enumerate}\n\nCongrats! Now you have a head start to developing your theme.\n\n\\section{Related Topics}\\label{related-topics-62}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/building-your-themes-files}{Building\n  Your Theme's files}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator}{Generating\n  Themelets with the Theme Generator}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes}{Deploying\n  and Applying Themes}\n\\end{itemize}\n\n\\chapter{Listing Your Theme's\nExtensions}\\label{listing-your-themes-extensions}\n\nDo you need to know what base theme/themelet(s) your theme extends?\nFollow these steps to list your theme's extensions with the Status task.\nNote that this task only works for themes that use the\n\\href{https://github.com/liferay/liferay-themes-sdk/tree/master/packages}{liferay\nJS Theme Toolkit}, such as those created with the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Liferay\nTheme Generator}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Gulp is included as a local dependency in generated\nthemes, so you are not required to install it. It can be accessed by\nrunning \\texttt{node\\_modules\\textbackslash{}.bin\\textbackslash{}gulp}\nfollowed by the Gulp task from a generated theme's root folder.\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to your theme's root folder.\n\\item\n  Run \\texttt{gulp\\ status} to print your theme's current extensions to\n  the command line.\n\\end{enumerate}\n\nYour theme's current extensions are also found under the\n\\texttt{baseTheme} and \\texttt{themeletDependencies} headings in your\ntheme's \\texttt{package.json}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-ext-listing-theme-extensions.png}\n\\caption{Run the \\texttt{gulp\\ status} task to list your theme's current\nextensions.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-63}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/changing-your-base-theme}{Changing\n  Your Base Theme}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-your-themes-app-server}{Configuring\n  Your Theme's App Server}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator}{Generating\n  Themelets with the Theme Generator}\n\\end{itemize}\n\n\\chapter{Overwriting and Extending Liferay Theme\nTasks}\\label{overwriting-and-extending-liferay-theme-tasks}\n\nThemes created with the Liferay Theme Generator have access to several\ndefault gulp theme tasks that provide the standard features required to\ndevelop and build your theme (build, deploy, watch, etc.). You may,\nhowever, want to run additional processes on your theme's files prior to\ndeploying the theme to the server---such as minifying your JavaScript\nfiles. The Liferay Theme Generator's APIs expose a \\texttt{hookFn}\nproperty that lets you hook into the default gulp theme tasks to inject\nyour own logic.\n\nFollow these steps to hook into the default Liferay theme tasks:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Identify the gulp task or sub task that you want to hook into or\n  overwrite. The tasks and their sub tasks are listed in their\n  \\texttt{{[}task-name{]}.js} file in the\n  \\href{https://github.com/liferay/liferay-js-themes-toolkit/tree/master/packages/liferay-theme-tasks/tasks}{\\texttt{tasks}\n  folder} of the\n  \\href{https://github.com/liferay/liferay-js-themes-toolkit/tree/master/packages/liferay-theme-tasks}{\\texttt{liferay-theme-tasks}}\n  package. For example, the gulp \\texttt{build} task and sub tasks are\n  defined in the\n  \\href{https://github.com/liferay/liferay-js-themes-toolkit/blob/master/packages/liferay-theme-tasks/tasks/build.js\\#L73-L92}{\\texttt{build.js}\n  file}:\n\n\\begin{verbatim}\ngulp.task('build', function(cb) {\n    runSequence(\n        'build:clean',\n        'build:base',\n        'build:src',\n        'build:web-inf',\n        'build:liferay-look-and-feel',\n        'build:hook',\n        'build:themelets',\n        'build:rename-css-dir',\n        'build:compile-css',\n        'build:fix-url-functions',\n        'build:move-compiled-css',\n        'build:remove-old-css-dir',\n        'build:fix-at-directives',\n        'build:r2',\n        'build:war',\n        cb\n    );\n});\n\\end{verbatim}\n\\item\n  Open your theme's \\texttt{gulpfile.js} file and locate the\n  \\texttt{liferayThemeTasks.registerTasks()} method. This method\n  registers the default gulp theme tasks. Add the \\texttt{hookFn}\n  property to the \\texttt{registerTasks()} method's configuration\n  object, making sure to pass in the \\texttt{gulp} instance:\n\n\\begin{verbatim}\nliferayThemeTasks.registerTasks({\n  gulp: gulp,\n  hookFn: function(gulp) {\n\n  }\n});\n\\end{verbatim}\n\\item\n  Inside the \\texttt{hookFn()} function, use the \\texttt{gulp.hook()}\n  method to specify the theme task or sub task that you want to hook\n  into. You can inject your code before or after a task by prefixing it\n  with the \\texttt{before:} or \\texttt{after:} keywords. Alternatively,\n  you can use the \\texttt{gulp.task()} method to overwrite a gulp task.\n  Both methods have two parameters: the task or sub task you want to\n  hook into and a callback function that invokes \\texttt{done} or\n  returns a stream with the logic that you want to inject. A few example\n  configuration patterns are shown below:\n\n\\begin{verbatim}\nliferayThemeTasks.registerTasks({\n  gulp: gulp,\n  hookFn: function(gulp) {\n    gulp.hook('before:build:src', function(done) {\n      // Fires before build:src task\n    });\n\n    gulp.hook('after:build', function(done) {\n      // Fires after build task\n    });\n\n    gulp.task('build:base', function(done) {\n      // Overwrites build:base task\n    });\n  }\n});\n\\end{verbatim}\n\\end{enumerate}\n\nThe example below fires before the \\texttt{build:war} sub-task and reads\nthe JavaScript files in the theme's \\texttt{build} folder, minifies them\nwith the \\texttt{gulp-uglify} module, places them back in the\n\\texttt{./build/js} folder, invokes \\texttt{done}, and finally logs that\nthe JavaScript was minified. To follow along, replace your theme's\n\\texttt{gulpfile.js} with the contents shown below, install the\n\\href{https://www.npmjs.com/package/gulp-uglify}{gulp-uglify} module and\nthe \\href{https://www.npmjs.com/package/fancy-log}{fancy-log} module,\nand run \\texttt{gulp\\ deploy}:\n\n\\begin{verbatim}\n'use strict';\n\nvar gulp = require('gulp');\nvar log = require('fancy-log');\nvar uglify = require('gulp-uglify');\nvar liferayThemeTasks = require('liferay-theme-tasks');\n\nliferayThemeTasks.registerTasks({\n   gulp: gulp,\n   hookFn: function(gulp) {\n     gulp.hook('before:build:war', function(done) {\n      // Fires before build `war` task\n      gulp.src('./build/js/*.js')\n      .pipe(uglify())\n      .pipe(gulp.dest('./build/js'))\n      .on('end', done);\n      log('Your JS is now minified...');\n      });\n   }\n});\n\\end{verbatim}\n\nYou should see something similar to the output shown below:\n\n\\begin{verbatim}\n[15:58:07] Finished 'build:r2' after 198 ms\n[15:58:07] Starting 'build:war'...\n[15:58:07] Your JS is now minified...\n[15:58:07] Starting 'plugin:version'...\n[15:58:07] Finished 'plugin:version' after 2.52 ms\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\texttt{hook} callback function must invoke the\n\\texttt{done} argument or return a stream.\n\n\\noindent\\hrulefill\n\nNow you know how to hook into and overwrite the default Liferay theme\ntasks!\n\n\\section{Related Topics}\\label{related-topics-64}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Installing\n  the Theme Generator and Creating a Theme}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator}{Generating\n  Themelets}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes}{Using\n  Developer Mode with Themes}\n\\end{itemize}\n\n\\chapter{Clay CSS and Themes}\\label{clay-css-and-themes}\n\n\\href{https://liferay.design/lexicon/}{Lexicon} is a design language\nthat provides a common framework for building consistent UIs.\n\\href{https://clayui.com/docs/css-framework/scss.html}{Clay}, the web\nimplementation of Lexicon, is an extension of Bootstrap's open source\nCSS Framework. Bootstrap is by far the most popular CSS framework on the\nweb. Built with Sass, Clay CSS fills the front-end gaps between\nBootstrap and the specific needs of Liferay DXP.\n\nBootstrap features have been extended to cover more use cases. Here are\nsome of the new components added by Clay CSS:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Aspect Ratio\n\\item\n  Cards\n\\item\n  Dropdown Wide and Dropdown Full\n\\item\n  Figures\n\\item\n  Nameplates\n\\item\n  Sidebar / Sidenav\n\\item\n  Stickers\n\\item\n  SVG Icons\n\\item\n  Timelines\n\\item\n  Toggles\n\\end{itemize}\n\nSeveral reusable CSS patterns have also been added to help accomplish\ntime consuming tasks such as these:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  truncating text\n\\item\n  content filling the remaining container width\n\\item\n  truncating text inside table cells\n\\item\n  table cells filling remaining container width and table cells only\n  being as wide as their content\n\\item\n  open and close icons inside collapsible panels\n\\item\n  nested vertical navigations\n\\item\n  slide out panels\n\\item\n  notification icons/messages\n\\item\n  vertical alignment of content\n\\end{itemize}\n\n\\href{https://clayui.com/}{Clay CSS} is bundled with two sub-themes:\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-styled/src/main/resources/META-INF/resources/_styled/css/clay}{Clay\nBase} and\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-styled/src/main/resources/META-INF/resources/_styled/css/clay/atlas}{Atlas}.\nClay Base is Liferay DXP's Bootstrap API extension. It adds all the\nfeatures and components you need and inherits Bootstrap's styles. As a\nresult, Clay Base is fully compatible with\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/integrating-third-party-themes-with-clay}{third\nparty themes} that leverage Bootstrap's Sass variable API.\n\nAtlas is Liferay DXP's custom Bootstrap theme that is used in the\nClassic Theme. Its purpose is to overwrite and manipulate Bootstrap and\nClay Base to create its classic look and feel. Atlas is equivalent to\ninstalling a Bootstrap third party theme.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} It is not recommended to integrate third party themes\nwith Atlas, as it adds variables and styles that are outside the scope\nof Bootstrap's API.\n\n\\noindent\\hrulefill\n\nThis section covers these topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Customizing the Atlas and Clay base themes\n\\item\n  Integrating third party themes with Clay\n\\end{itemize}\n\n\\chapter{Customizing Atlas and Clay Base Themes in Liferay\nDXP}\\label{customizing-atlas-and-clay-base-themes-in-liferay-dxp}\n\nWhether you're customizing the Atlas or Clay base theme, the process is,\nfor the most part, the same. Follow these steps. If you're customizing\nthe Clay base theme, skip to step 3.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  By default, Clay base is imported into the theme. If you're\n  overwriting Atlas, add a file named \\texttt{clay.scss} to your theme's\n  \\texttt{/src/css/} folder and import \\texttt{clay/atlas} instead:\n\n\\begin{verbatim}\n@import \"clay/atlas\";\n\\end{verbatim}\n\\item\n  By default, Clay base variables are imported into the theme. If you're\n  overwriting Atlas, add an \\texttt{\\_imports.scss} file to your theme's\n  \\texttt{/src/css/} folder and import Atlas variables instead:\n\n\\begin{verbatim}\n@import \"bourbon\";\n\n@import \"mixins\";\n\n@import \"compat/mixins\";\n\n@import \"clay/atlas-variables\";\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Bourbon mixins are deprecated as of 7.0 and will be \n removed in the next major release. We recommend you use Clay mixins \n instead. To use Clay mixins, follow the instructions in \n [Using Clay Mixins in Your Theme](/docs/7-2/frameworks/-/knowledge_base/f/using-clay-mixins-in-your-theme)\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\tightlist\n\\item\n  Add a file named \\texttt{\\_clay\\_variables.scss}. Place your Atlas,\n  Bootstrap, and Clay Base variable modifications in this file.\n\\end{enumerate}\n\nGreat! Now you know how to customize the Atlas and Clay base themes.\n\n\\section{Related Topics}\\label{related-topics-65}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/integrating-third-party-themes-with-clay}{Integrating\n  Third Party Themes with Clay}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-clay-mixins-in-your-theme}{Using\n  Clay Mixins in Your Theme}\n\\end{itemize}\n\n\\chapter{Integrating Third Party Themes with\nClay}\\label{integrating-third-party-themes-with-clay}\n\n\\href{https://github.com/liferay/liferay-portal/tree/7.1.x/modules/apps/frontend-theme/frontend-theme-styled/src/main/resources/META-INF/resources/_styled/css/clay}{Clay\nBase} provides all the features and components your theme needs and\ninherits Bootstrap's styles. As a result, Clay Base is fully compatible\nwith third party themes that leverage Bootstrap's Sass variable API.\n\nThe\n\\href{https://github.com/liferay/liferay-portal/tree/7.1.x/modules/apps/frontend-theme/frontend-theme-styled}{Styled\nTheme} uses Clay Base to provide its styles and components. Therefore,\nas a best practice, you should use the Styled base theme to integrate\nthird party themes.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can purchase third party themes from the\n\\href{https://web.liferay.com/marketplace}{Liferay Marketplace}. Third\nparty themes must be built with Sass to be compatible. \\textbf{Make\nsure} Sass files are included before making any theme purchase.\n\n\\noindent\\hrulefill\n\nFollow these steps to integrate a third party theme with Clay Base:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new theme with the Styled Theme as its base. This is the\n  default base theme for newly created themes, so no further action is\n  required. This provides the Clay Base files you need.\n\\item\n  In the theme's \\texttt{/src/css/} folder, add a file named\n  \\texttt{\\_clay\\_variables.scss}. Place your Atlas, Bootstrap, and Clay\n  Base variable modifications in this file.\n\\item\n  Create a folder inside \\texttt{/src/css/} to house your third party\n  theme (e.g.~\\texttt{/src/css/my-third-party-theme/})\n\\item\n  Copy the CSS contents of the theme to the folder you just created.\n\\item\n  In \\texttt{\\_clay\\_variables.scss}, import the file containing the\n  theme variables. For example,\n  \\texttt{@import\\ \"my-third-party-theme/variables.scss\";}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You may omit the leading underscore when importing Sass files.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{5}\n\\tightlist\n\\item\n  In \\texttt{\\_custom.scss}, import the file containing the CSS. For\n  example, \\texttt{@import\\ \"my-third-party-theme/main.scss\";}\n\\end{enumerate}\n\nNow you know how to integrate third party themes with Clay Base!\n\n\\section{Related Topics}\\label{related-topics-66}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/customizing-atlas-and-clay-base-themes}{Customizing\n  Atlas and Clay Base Themes}\n\\end{itemize}\n\n\\chapter{Using Clay Icons in a Theme}\\label{using-clay-icons-in-a-theme}\n\nTo use Clay icons in your themes, you must use the\n\\href{/docs/7-2/reference/-/knowledge_base/r/freemarker-taglib-macros}{clay\ntaglib macro}. If you want to use Clay icons in your portlets, follow\nthe steps in the\n\\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Clay taglib\nicons} article. To use Clay icons in your theme, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the FreeMarker theme template you want to use the Clay icon in.\n\\item\n  Use the \\texttt{@clay{[}\"icon\"{]}} macro and specify the icon with the\n  \\texttt{symbol} attribute, as shown in the pattern below:\n\n\\begin{verbatim}\n<@clay[\"icon\"] symbol=\"icon-name\" />\n\\end{verbatim}\n\n  The full list of icons can be found on\n  \\href{https://clayui.com/docs/components/icon.html}{ClayUI's site}\n  (CSS/Markup tab). Here is an example configuration for a Facebook\n  social media icon:\n\n\\begin{verbatim}\n    <a class=\"text-white\"\n    href=\"http://www.facebook.com/pages/Liferay/45119213107\" \n    target=\"_blank\">\n      <span class=\"hide\">Facebook</span>\n      <@clay[\"icon\"] symbol=\"social-facebook\" />\n    </a>\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! Now you know how to use Clay icons in your theme.\n\n\\section{Related Topics}\\label{related-topics-67}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/freemarker-taglib-macros}{FreeMarker\n  Taglib Macros}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Clay Icons}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/customizing-atlas-and-clay-base-themes}{Customizing\n  Atlas and Clay Base Themes}\n\\end{itemize}\n\n\\chapter{Using Clay Mixins in Your\nTheme}\\label{using-clay-mixins-in-your-theme}\n\nBourbon mixins are deprecated as of 7.0 and will be removed in the next\nmajor release. We recommend you use Clay mixins instead. Follow these\nsteps to use Clay mixins in your theme:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the \\texttt{clay-css} dependency to the theme's\n  \\texttt{package.json}:\n\n\\begin{verbatim}\n\"dependencies\":{\n  \"clay-css\": \"^2.18.0\",\n}\n\\end{verbatim}\n\\item\n  Delete \\texttt{\\_imports.scss} if you modified it.\n\\item\n  Import the library into the theme's \\texttt{main.scss} file:\n\n\\begin{verbatim}\n@import 'node_modules/clay-css/src/scss/atlas-variables'\n\\end{verbatim}\n\n  or import the base-variables if you want to use Clay Base instead:\n\n\\begin{verbatim}\n@import 'node_modules/clay-css/src/scss/base-variables'\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! Now you know how to use Clay mixins in your theme!\n\n\\section{Related Topics}\\label{related-topics-68}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/customizing-atlas-and-clay-base-themes}{Customizing\n  Atlas and Clay Base Theme}\n\\end{itemize}\n\n\\chapter{Theming Portlets}\\label{theming-portlets}\n\nAlthough you can individually style a portlet via the theme's CSS or the\nportlet's\n\\href{/docs/7-2/user/-/knowledge_base/u/look-and-feel-configuration}{Look\nand Feel Configuration} menu, you may want to modify the default look\nand feel for all portlets in your site. A portlet's template--its\ncontainer, CSS classes, and overall HTML Markup--is defined via the\ntheme's \\texttt{portlet.ftl} file. To provide a custom style for all\nportlets, use the CSS classes in this file for the various container\nelements along with the portlet decorators to achieve the desired look\nand feel. Be cautious: changes to \\texttt{portlet.ftl} affect all the\nportlets in your site when the theme is applied.\n\nTo help you with your bearings as you modify your portlet's template,\nbelow is a quick look at the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-theme/frontend-theme-classic/src/templates/portlet.ftl}{\\texttt{portlet.ftl}}\nfile that's included in 7.0's Classic theme.\n\n\\begin{verbatim}\n<#assign\n    portlet_display = portletDisplay\n    portlet_back_url = htmlUtil.escapeHREF(portlet_display.getURLBack())\n    portlet_content_css_class = \"portlet-content\"\n    portlet_display_name = htmlUtil.escape(portlet_display.getPortletDisplayName())\n    portlet_display_root_portlet_id = htmlUtil.escapeAttribute(portlet_display.getRootPortletId())\n    portlet_id = htmlUtil.escapeAttribute(portlet_display.getId())\n    portlet_title = htmlUtil.escape(portlet_display.getTitle())\n/>\n\\end{verbatim}\n\nThese variables are described in the table below:\n\n\\textbf{Portlet FTL Variables}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nVariable\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{portletDisplay} & Fetched from the \\texttt{themeDisplay} object,\ncontains information about the portlet \\\\\n\\texttt{portlet\\_back\\_url} & URL to return to the previous page when\nthe portlet \\texttt{WindowState} is maximized \\\\\n\\texttt{portlet\\_display\\_name} & The ``friendly'' name of the portlet\nas displayed in the GUI \\\\\n\\texttt{portlet\\_display\\_root\\_portlet\\_id} & The root portlet ID of\nthe portlet \\\\\n\\texttt{portlet\\_id} & The ID of the portlet (not the same as the\nportlet namespace) \\\\\n\\texttt{portlet\\_title} & The portlet name set in the portlet Java class\n(usually from a \\texttt{Keys.java} class) \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nNext, a condition checks if the portlet header should be displayed. If\nthe portlet has a portlet toolbar (Configuration, Permissions, Look and\nFeel), the condition is true and the header is displayed:\n\n\\begin{verbatim}\n<#if portlet_display.isPortletDecorate() && !portlet_display.isStateMax() \n&& portlet_display.getPortletConfigurationIconMenu()?? \n&& portlet_display.getPortletToolbar()??>\n\\end{verbatim}\n\nYou can use a similar pattern if you want to dynamically show portions\nof the portlet's UI.\n\nNext, the portlet title menus are defined. These are used in portlets\nthat let you add resources (Web Content Display, Media Gallery,\nDocuments and Media):\n\n\\begin{verbatim}\nportlet_title_menus = portlet_toolbar.getPortletTitleMenus(portlet_display_root_portlet_id, renderRequest, renderResponse)\n\\end{verbatim}\n\nThe configuration below contains the information for the configuration\nmenu (Configuration, Permissions, Look and Feel):\n\n\\begin{verbatim}\nportlet_configuration_icons = portlet_configuration_icon_menu.getPortletConfigurationIcons(portlet_display_root_portlet_id, renderRequest, renderResponse)\n\\end{verbatim}\n\nThe rest of the file contains the HTML markup for the portlet topper and\nthe portlet content. This section barely scratches the surface of the\n\\texttt{portlet.ftl} file. You must examine the \\texttt{portlet.ftl}\nfile yourself and determine what elements and classes need updated for\nyour theme and site.\n\nNow that you are more familiar with your theme's \\texttt{portlet.ftl}\nfile, you can learn the role Portlet Decorators play in the overall look\nand feel of your portlets.\n\n\\section{Portlet Decorators}\\label{portlet-decorators}\n\nPortlet Decorators modify the style of the application wrapper. Themes\ncome bundled with three default portlet decorators, defined in\n\\texttt{liferay-look-and-feel.xml}:\n\n\\begin{itemize}\n\\item\n  Decorate: this is the default Application Decorator when using the\n  Classic theme. It wraps the application in a white box with a border,\n  and displays the title at the top.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/application-decorator-decorate.png}\n  \\caption{The Classic theme's Decorate Application Decorator wraps the\n  portlet in a white box.}\n  \\end{figure}\n\\item\n  Borderless: this decorator shows the title at the top, but does not\n  display a wrapping box.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/application-decorator-borderless.png}\n  \\caption{The Classic theme's Borderless Application Decorator displays\n  the application's custom title.}\n  \\end{figure}\n\\item\n  Barebone: this decorator displays the bare application content,\n  showing neither the wrapping box nor the custom application title.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/application-decorator-barebone.png}\n  \\caption{The Classic theme's Barebone Application Decorator displays\n  only the application's content.}\n  \\end{figure}\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Upgrading to Liferay DXP assigns the \\emph{borderless}\ndecorator automatically to those portlets that had the \\emph{Show\nBorders} option set to false in previous versions of Liferay.\n\n\\noindent\\hrulefill\n\nThis section covers these topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Embedding Portlets in Themes\n\\end{itemize}\n\n\\chapter{Embedding Portlets in\nThemes}\\label{embedding-portlets-in-themes}\n\nYou may occasionally want to embed a portlet in a theme, making the\nportlet visible on all pages where the theme is used. Since there are\nnumerous drawbacks to hard-coding a specific portlet into place, the\n\\emph{Portlet Providers} framework offers an alternative that displays\nthe appropriate portlet based on a given entity type and action.\n\nThe first thing you should do is open the template file for which you\nwant to declare an embedded portlet. For example, the\n\\texttt{portal\\_normal.ftl} template file is a popular place to declare\nembedded portlets. To avoid problems, it's usually best to embed\nportlets with an entity type and action, but you may encounter\ncircumstances where you'll want to hard code it by portlet name. Both\nmethods are covered in this section. These topics are covered:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Embedding a portlet by entity type and action\n\\item\n  Embedding a portlet by instance name and ID\n\\end{itemize}\n\n\\chapter{Embedding Portlets in Themes by Entity Type and\nAction}\\label{embedding-portlets-in-themes-by-entity-type-and-action}\n\nIn this article, you'll learn how to declare an entity type and action\nin a custom theme, and you'll create a module that finds the correct\nportlet to use based on those given parameters. Follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Insert a declaration where you want the portlet embedded. This\n  declaration expects two parameters: the type of action and the class\n  name of the entity type the portlet should handle. An example\n  configuration is shown below:\n\n\\begin{verbatim}\n<@liferay_portlet[\"runtime\"]\n    portletProviderAction=portletProviderAction.VIEW\n    portletProviderClassName=\"com.liferay.portal.kernel.servlet.taglib.ui.LanguageEntry\"\n/>\n\\end{verbatim}\n\n  This example declares that the theme is requesting to view language\n  entries. Liferay DXP determines which deployed portlet to use in this\n  case by providing the portlet with the highest service ranking.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** In some cases, a default portlet is already provided to fulfill\n certain requests. You can override the default portlet with your custom\n portlet by specifying a higher service rank. To do this, set the following\n property in your class' `@Component` declaration:\n \n     property= {\"service.ranking:Integer=20\"}\n \n Make sure you set the service ranking higher than the default portlet being\n used.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  The Portal is not yet configured to handle this request. You must\n  create a module that can find the portlet that fits the theme's\n  request.\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  a module}.\n\\item\n  Create a unique package name in the module's \\texttt{src} directory,\n  and create a new Java class in that package. To follow naming\n  conventions, name the class based on the entity type and action type,\n  followed by \\emph{PortletProvider} (e.g.,\n  \\texttt{SiteNavigationLanguageEntryViewPortletProvider}). The class\n  should extend the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BasePortletProvider.html}{\\texttt{BasePortletProvider}}\n  class and implement the appropriate portlet provider interface based\n  on the action you chose in your theme (e.g.,\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/ViewPortletProvider.html}{\\texttt{ViewPortletProvider}},\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BrowsePortletProvider.html}{\\texttt{BrowsePortletProvider}},\n  etc.).\n\\item\n  Directly above the class's declaration, insert the following\n  annotation:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\"model.class.name=CLASS_NAME\"},\n    service = INTERFACE.class\n)\n\\end{verbatim}\n\n  The \\texttt{property} element must match the entity type you specified\n  in your theme declaration (e.g.,\n  \\texttt{com.liferay.portal.kernel.servlet.taglib.ui.LanguageEntry}).\n  Also, your \\texttt{service} element should match the interface you're\n  implementing (e.g., \\texttt{ViewPortletProvider.class}).\n\\item\n  Specify the methods you want to implement. Make sure to retrieve the\n  portlet ID and page ID that should be provided when this service is\n  called by your theme.\n\n  A common use case is to implement the \\texttt{getPortletId()} and\n  \\texttt{getPlid(ThemeDisplay)} methods. Below is an example\n  configuration:\n\n\\begin{verbatim}\n@Override\npublic String getPortletName() {\n  return SiteNavigationLanguagePortletKeys.SITE_NAVIGATION_LANGUAGE;\n}\n\n@Override\npublic PortletURL getPortletURL(\n    HttpServletRequest httpServletRequest, Group group)\n  throws PortalException {\n\n  return PortletURLFactoryUtil.create(\n    httpServletRequest, getPortletName(), PortletRequest.RENDER_PHASE);\n}\n\n/**\n * @deprecated As of Judson (7.1.x)\n */\n@Deprecated\n@Override\nprotected long getPlid(ThemeDisplay themeDisplay) throws PortalException {\n  return themeDisplay.getPlid();\n}\n\\end{verbatim}\n\n  This returns the portlet ID and the PLID, which is the ID that\n  uniquely identifies a page used by your theme. By retrieving these,\n  your theme will know which portlet to use, and which page to use it\n  on.\n\\item\n  Generate the module's JAR file and copy it to your app server's\n  \\texttt{osgi/modules/} folder. Once the module is installed and\n  activated in your Portal's service registry, your embedded portlet is\n  available for use wherever your theme is used.\n\\end{enumerate}\n\nAwesome! You successfully requested a portlet based on the entity and\naction types required, and created and deployed a module that retrieves\nthe portlet and embeds it in your theme.\n\n\\section{Related Topics}\\label{related-topics-69}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes}{Embedding\n  Portlets in Themes}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{Portlets}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\n  Builder}\n\\end{itemize}\n\n\\chapter{Embedding a Portlet by Portlet\nName}\\label{embedding-a-portlet-by-portlet-name}\n\nIf you'd like to embed a specific portlet in the theme, you can hard\ncode it by providing its instance ID and name. Follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the FreeMarker theme template that you want to embed the portlet\n  in. (\\texttt{portal\\_normal.ftl} is a good choice.\n\\item\n  Add the \\texttt{liferay\\_portlet{[}\"runtime\"{]}} macro to the\n  template, as shown below. The portlet name \\textbf{must} be the same\n  as \\texttt{javax.portlet.name}'s value.\n\n\\begin{verbatim}\n<@liferay_portlet[\"runtime\"]\n    instanceId=\"INSTANCE_ID\"\n    portletName=\"PORTLET_NAME\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If your portlet is instanceable, an instance ID must be\nprovided; otherwise, you can remove this line. To set your portlet to\nnon-instanceable, set the property\n\\texttt{com.liferay.portlet.instanceable} in the component annotation of\nyour portlet to \\texttt{false}.\n\n\\noindent\\hrulefill\n\nHere's an example of an embedded portlet declaration that uses the\nportlet name to embed a web content portlet:\n\n\\begin{verbatim}\n<@liferay_portlet[\"runtime\"]\n    portletName=\"com_liferay_journal_content_web_portlet_JournalContentPortlet\"\n/>\n\\end{verbatim}\n\nGreat! Now you know how to embed a portlet in your theme's by their name\nand ID.\n\n\\section{Related Topics}\\label{related-topics-70}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes-by-entity-type-and-action}{Embedding\n  Portlets in Themes by Entity Type and Action}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{Portlets}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\n  Builder}\n\\end{itemize}\n\n\\chapter{Setting Default Preferences for an Embedded\nPortlet}\\label{setting-default-preferences-for-an-embedded-portlet}\n\nFollow these steps to set default portlet preferences for an embedded\nportlet:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Retrieve portlet preferences using the\n  \\texttt{freeMarkerPortletPreferences} object. The example below\n  retrieves the \\texttt{barebone}:\n\n\\begin{verbatim}\n<#assign preferences = freeMarkerPortletPreferences.getPreferences(\n  \"portletSetupPortletDecoratorId\", \"barebone\"\n) />\n\\end{verbatim}\n\\item\n  Set the \\texttt{defaultPreferences} attribute of the embedded portlet\n  to the \\texttt{freeMarkerPortletPreferences} object you just\n  configured:\n\n\\begin{verbatim}\n<@liferay_portlet[\"runtime\"]\n    defaultPreferences=\"${preferences}\"\n    portletName=\"com_liferay_login_web_portlet_LoginPortlet\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\nBelow are some additional attributes you can define for embedded\nportlets:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nPreference\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\textbf{defaultPreferences} & A string of Portlet Preferences for the\napplication. This includes look and feel configurations. \\\\\n\\textbf{instanceId} & The instance ID for the app, if the application is\ninstanceable. \\\\\n\\textbf{persistSettings} & Whether to have an application use its\ndefault settings, which will persist across layouts. The default value\nis \\emph{true}. \\\\\n\\textbf{settingsScope} & Specifies which settings to use for the\napplication. The default value is \\texttt{portletInstance}, but it can\nbe set to \\texttt{group} or \\texttt{company}. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nNow you know how to set default preferences for embedded portlets!\n\n\\section{Related Topics}\\label{related-topics-71}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/embedding-a-portlet-by-portlet-name}{Embedding\n  Portlets by Name}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{Portlets}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\n  Builder}\n\\end{itemize}\n\n\\chapter{Importing Resources with a\nTheme}\\label{importing-resources-with-a-theme}\n\nTo truly appreciate a theme, you must view it with content. Showcasing a\ntheme in the proper context is key to communicating the true intentions\nof its design. Who better to do this than the theme's designer?\nDesigners can provide a sample context that optimizes the design of\ntheir themes. The Resources Importer does this for you.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} The Resources Importer is deprecated as of 7.0 7.1.\n\n\\noindent\\hrulefill\n\nThe Resources Importer module lets theme developers import files and web\ncontent with a theme. Administrators can use the site or site template\ncreated by the Resources Importer to showcase the theme. In fact, all\nstandalone themes that are uploaded to Liferay Marketplace \\textbf{must\nuse} the Resources Importer. This ensures a uniform experience for\nMarketplace users: a user can download a theme from Marketplace, install\nit, go to Sites or Site Templates in the Control Panel and immediately\nsee their new theme in action.\n\n\\section{Organizing Your Resources}\\label{organizing-your-resources}\n\nAdd your resources to the theme's\n\\texttt{/src/WEB-INF/src/resources-importer} folder as outlined below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{{[}theme-name{]}/src/WEB-INF/src/resources-importer/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{sitemap.json} - defines the pages, layout templates, and\n    portlets\n  \\item\n    \\texttt{assets.json} - (optional) specifies details on the assets\n  \\item\n    \\texttt{document\\_library/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{documents/} - contains documents and media files (assets)\n    \\end{itemize}\n  \\item\n    \\texttt{journal/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{articles/} - contains web content (HTML) and folders\n      grouping web content articles (XML) by template. Each folder name\n      must match the file name of the corresponding template. For\n      example, create folder \\texttt{Template\\ 1/} to hold an article\n      based on template file \\texttt{Template\\ 1.ftl}.\n    \\item\n      \\texttt{structures/} - contains structures (JSON) and folders of\n      child structures. Each folder name must match the file name of the\n      corresponding parent structure. For example, create folder\n      \\texttt{Structure\\ 1/} to hold a child of structure file\n      \\texttt{Structure\\ 1.json}.\n    \\item\n      \\texttt{templates/} - groups templates (VM or FTL) into folders by\n      structure. Each folder name must match the file name of the\n      corresponding structure. For example, create folder\n      \\texttt{Structure\\ 1/} to hold a template for structure file\n      \\texttt{Structure\\ 1.json}.\n    \\end{itemize}\n  \\end{itemize}\n\\end{itemize}\n\nUsing the Resources Importer involves the following steps:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer}{Preparing\n  and Organizing Resources}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-sitemap-for-the-resources-importer}{Creating\n  a Sitemap for the Resources Importer}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-assets-for-the-resources-importer}{Defining\n  Assets for the Resources Importer} (optional)\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/specifying-where-to-import-your-themes-resources}{Specifying\n  Where to Import Your Theme's Resources}\n\\end{itemize}\n\nThis section explains how to use the Resources Importer to import\nresources with your theme.\n\n\\chapter{Creating a Sitemap for the Resources\nImporter}\\label{creating-a-sitemap-for-the-resources-importer}\n\nYou have two options for specifying resources to be imported with your\ntheme: a sitemap or an\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/archiving-your-sites-resources}{archive\nLAR file}. Using a \\texttt{sitemap.json} file is the most flexible\napproach, so we recommend it; unlike LAR files, a \\texttt{sitemap.json}\ncan be created in one version of Liferay DXP and used in another. LAR\nfiles are version-specific, and can only be imported in the same version\nin which they were created.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} The Resources Importer is deprecated as of 7.0 7.1.\n\n\\noindent\\hrulefill\n\nThe \\texttt{sitemap.json} specifies the site pages, layout templates,\nweb content, assets, and portlet configurations provided with the theme.\nThis file describes the contents and hierarchy of the site to import as\na site or site template. If you're developing themes for Liferay\nMarketplace, you must use the \\texttt{sitemap.json} to specify resources\nto be imported with your theme. Even if you're not familiar with JSON,\nthe \\texttt{sitemap.json} file is easy to understand. An example\n\\texttt{sitemap.json} file is shown below:\n\n\\begin{verbatim}\n{\n    \"layoutTemplateId\": \"2_columns_ii\",\n    \"privatePages\": [\n        {\n            \"friendlyURL\": \"/private-page\",\n            \"name\": \"Private Page\",\n            \"title\": \"Private Page\"\n        }\n    ],\n    \"publicPages\": [\n        {\n            \"columns\": [\n                [\n                    {\n                        \"portletId\": \"com_liferay_login_web_portlet_LoginPortlet\"\n                    },\n                    {\n                        \"portletId\": \n                        \"com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet\"\n                    },\n                    {\n                        \"portletId\": \n                        \"com_liferay_journal_content_web_portlet_JournalContentPortlet\",\n                        \"portletPreferences\": {\n                            \"articleId\": \"Without Border.html\",\n                            \"groupId\": \"${groupId}\",\n                            \"portletSetupPortletDecoratorId\": \"borderless\"\n                        }\n                    },\n                    {\n                        \"portletId\": \"com_liferay_journal_content_web_portlet_JournalContentPortlet\",\n                        \"portletPreferences\": {\n                            \"articleId\": \"Custom Title.html\",\n                            \"groupId\": \"${groupId}\",\n                            \"portletSetupPortletDecoratorId\": \"decorate\",\n                            \"portletSetupTitle_en_US\": \"Web Content Display with Custom Title\",\n                            \"portletSetupUseCustomTitle\": \"true\"\n                        }\n                    }\n                ],\n                [\n                    {\n                        \"portletId\": \"com_liferay_hello_world_web_portlet_HelloWorldPortlet\"\n                    },\n                    {\n                        \"portletId\": \n                        \"com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet_INSTANCE_${groupId}\",\n                        \"portletPreferences\": {\n                            \"displayStyle\": \"[custom]\",\n                            \"headerType\": \"root-layout\",\n                            \"includedLayouts\": \"all\",\n                            \"nestedChildren\": \"1\",\n                            \"rootLayoutLevel\": \"3\",\n                            \"rootLayoutType\": \"relative\"\n                        }\n                    },\n                        \"Web Content with Image.html\",\n                    {\n                        \"portletId\": \"com_liferay_nested_portlets_web_portlet_NestedPortletsPortlet\",\n                        \"portletPreferences\": {\n                            \"columns\": [\n                                [\n                                    {\n                                        \"portletId\": \n                                        \"com_liferay_journal_content_web_portlet_JournalContentPortlet\",\n                                        \"portletPreferences\": {\n                                            \"articleId\": \"Child Web Content 1.xml\",\n                                            \"groupId\": \"${groupId}\",\n                                            \"portletSetupPortletDecoratorId\": \"decorate\",\n                                            \"portletSetupTitle_en_US\": \n                                            \"Web Content Display with Child Structure 1\",\n                                            \"portletSetupUseCustomTitle\": \"true\"\n                                        }\n                                    }\n                                ],\n                                [\n                                    {\n                                        \"portletId\": \n                                        \"com_liferay_journal_content_web_portlet_JournalContentPortlet\",\n                                        \"portletPreferences\": {\n                                            \"articleId\": \"Child Web Content 2.xml\",\n                                            \"groupId\": \"${groupId}\",\n                                            \"portletSetupPortletDecoratorId\": \"decorate\",\n                                            \"portletSetupTitle_en_US\": \n                                            \"Web Content Display with Child Structure 2\",\n                                            \"portletSetupUseCustomTitle\": \"true\"\n                                        }\n                                    }\n                                ]\n                            ],\n                            \"layoutTemplateId\": \"2_columns_i\"\n                        }\n                    }\n                ]\n            ],\n            \"friendlyURL\": \"/home\",\n            \"nameMap\": {\n                \"en_US\": \"Welcome\",\n                \"fr_FR\": \"Bienvenue\"\n            },\n            \"title\": \"Welcome\"\n        },\n        {\n            \"columns\": [\n                [\n                    {\n                        \"portletId\": \"com_liferay_login_web_portlet_LoginPortlet\"\n                    }\n                ],\n                [\n                    {\n                        \"portletId\": \"com_liferay_hello_world_web_portlet_HelloWorldPortlet\"\n                    }\n                ]\n            ],\n            \"friendlyURL\": \"/layout-prototypes-parent-page\", \n            \"layouts\": [\n                {\n                    \"friendlyURL\": \"/layout-prototypes-page-1\",\n                    \"layoutPrototypeLinkEnabled\": \"true\",\n                    \"layoutPrototypeUuid\": \"371647ba-3649-4039-bfe6-ae32cf404737\",\n                    \"name\": \"Layout Prototypes Page 1\",\n                    \"title\": \"Layout Prototypes Page 1\"\n                },\n                {\n                    \"friendlyURL\": \"/layout-prototypes-page-2\",\n                    \"layoutPrototypeUuid\": \"c98067d0-fc10-9556-7364-238d39693bc4\",\n                    \"name\": \"Layout Prototypes Page 2\",\n                    \"title\": \"Layout Prototypes Page 2\"\n                }\n            ],\n            \"name\": \"Layout Prototypes\",\n            \"title\": \"Layout Prototypes\"\n        },\n        {\n            \"columns\": [\n                [\n                    {\n                        \"portletId\": \"com_liferay_login_web_portlet_LoginPortlet\"\n                    }\n                ],\n                [\n                    {\n                        \"portletId\": \"com_liferay_hello_world_web_portlet_HelloWorldPortlet\"\n                    }\n                ]\n            ],\n            \"friendlyURL\": \"/parent-page\",\n            \"layouts\": [\n                {\n                    \"friendlyURL\": \"/child-page-1\",\n                    \"name\": \"Child Page 1\",\n                    \"title\": \"Child Page 1\"\n                },\n                {\n                    \"friendlyURL\": \"/child-page-2\",\n                    \"name\": \"Child Page 2\",\n                    \"title\": \"Child Page 2\"\n                }\n            ],\n            \"name\": \"Parent Page\",\n            \"title\": \"Parent Page\"\n        },\n        {\n            \"friendlyURL\": \"/url-page\",\n            \"name\": \"URL Page\",\n            \"title\": \"URL Page\",\n            \"type\": \"url\"\n        },\n        {\n            \"friendlyURL\": \"/link-page\",\n            \"name\": \"Link to another Page\",\n            \"title\": \"Link to another Page\",\n            \"type\": \"link_to_layout\",\n            \"typeSettings\": \"linkToLayoutId=1\"\n        },\n        {\n            \"friendlyURL\": \"/hidden-page\",\n            \"name\": \"Hidden Page\",\n            \"title\": \"Hidden Page\",\n            \"hidden\": \"true\"\n        }\n    ]\n}\n\\end{verbatim}\n\nIf you don't understand the sitemap at this point, don't worry. This\nsection covers how to create a sitemap for your theme, from\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-layout-templates-and-pages-in-a-sitemap}{defining\npages} to\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-portlets-in-a-sitemap}{defining\nportlets}.\n\n\\chapter{Defining Layout Templates and Pages in a\nSitemap}\\label{defining-layout-templates-and-pages-in-a-sitemap}\n\nA sitemap defines the layouts---pages---and layout templates that your\nsite or site template uses. Follow these steps to define pages and\nlayout templates in your theme's \\texttt{sitemap.json}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Define a default layout template ID so the target site or site\n  template can reference the layout template to use for its pages. When\n  defined outside the scope of a page, the \\texttt{layoutTemplateId}\n  sets the default layout template for the theme's pages:\n\n\\begin{verbatim}\n{\n  \"layoutTemplateId\": \"2_columns_ii\",\n  \"publicPages\": [\n    {\n      \"friendlyURL\": \"/my-page\",\n      \"name\": \"My Page\",\n      \"title\": \"My Page\"\n    }\n  ]  \n}\n\\end{verbatim}\n\n  You can override this by defining a layout template inside a page\n  configuration. In the example below, the Hidden Page and the Welcome\n  page both use the default \\texttt{2\\_columns\\_ii} layout template,\n  while the Custom Layout Page overrides the default layout template and\n  uses the \\texttt{2\\_columns\\_i} layout template instead:\n\n\\begin{verbatim}\n{\n  \"layoutTemplateId\":\"2_columns_ii\",\n  \"publicPages\": [\n      {\n        \"friendlyURL\": \"/welcome-page\",\n        \"name\": \"Welcome\",\n        \"title\": \"Welcome\"\n      },\n      {\n        \"friendlyURL\": \"/custom-layout-page\",\n        \"name\": \"Custom Layout Page\",\n        \"title\": \"Custom Layout Page\",\n        \"layoutTemplateId\": \"2_columns_i\"\n      },\n      {\n        \"friendlyURL\": \"/hidden-page\",\n        \"name\": \"Hidden Page\",\n        \"title\": \"Hidden Page\",\n        \"hidden\": \"true\"\n      }\n  ]\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Pages are imported into a site template by default. Site templates\n only support the importing of either public page sets or private page sets, not\n both.\n \n If you want to import both public and private page sets, as shown in the example\n `sitemap.json` below, you must\n [import your resources into a site](/docs/7-2/frameworks/-/knowledge_base/f/specifying-where-to-import-your-themes-resources).\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Follow the pattern below to specify the public and (optionally)\n  private pages for your theme. You can specify a name for a page,\n  title, friendly URL, whether it is hidden, and much more. The example\n  below defines a default layout template and both public and private\n  page sets to import into a site. See\n  \\href{/docs/7-2/reference/-/knowledge_base/r/sitemap-page-configuration-options}{Sitemap\n  Page Configuration Options} for a full list of the available options.\n\n\\begin{verbatim}\n        {\n          \"layoutTemplateId\": \"2_columns_ii\",\n          \"privatePages\": [\n              {\n                \"friendlyURL\": \"/private-page\",\n                    \"name\": \"Private Page\",\n                    \"title\": \"Private Page\"\n              }\n          ],\n          \"publicPages\": [\n              {\n                \"friendlyURL\": \"/welcome-page\",\n                \"nameMap\": {\n                    \"en_US\": \"Welcome\",\n                    \"fr_FR\": \"Bienvenue\"\n                },\n                \"title\": \"Welcome\"\n              },\n              {\n                \"friendlyURL\": \"/custom-layout-page\",\n                \"name\": \"Custom Layout Page\",\n                \"title\": \"Custom Layout Page\",\n                \"layoutTemplateId\": \"2_columns_i\"\n              },\n              {\n                \"friendlyURL\": \"/hidden-page\",\n                \"name\": \"Hidden Page\",\n                \"title\": \"Hidden Page\",\n                \"hidden\": \"true\"\n              }\n          ]\n        }\n\\end{verbatim}\n\n  You can create child pages by configuring the \\texttt{layouts} element\n  for a page configuration:\n\n\\begin{verbatim}\n{      \n\"friendlyURL\": \"/parent-page\",\n\"layouts\": [\n    {\n        \"friendlyURL\": \"/child-page-1\",\n        \"name\": \"Child Page 1\",\n        \"title\": \"Child Page 1\"\n    },\n    {\n        \"friendlyURL\": \"/child-page-2\",\n        \"name\": \"Child Page 2\",\n        \"title\": \"Child Page 2\"\n    }\n],\n\"name\": \"Parent Page\",\n\"title\": \"Parent Page\"\n}\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! Now you know how to configure pages for the Resources Importer.\n\n\\section{Related Topics}\\label{related-topics-72}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer}{Preparing\n  and Organizing Web Content for the Resources Importer}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-portlets-in-a-sitemap}{Defining\n  Portlets in a Sitemap}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/specifying-where-to-import-your-themes-resources}{Specifying\n  Where to Import Your Theme's Resources}\n\\end{itemize}\n\n\\chapter{Defining Portlets in a\nSitemap}\\label{defining-portlets-in-a-sitemap}\n\nYou can embed portlets in a sitemap for the pages you define. You can\nembed them with the default settings or provide portlet preferences for\na more custom look and feel. This tutorial covers both these options.\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Note the portlet's ID. This is the \\texttt{javax.portlet.name}\n  attribute of the portlet spec. For convenience, The IDs for portlets\n  available out-of-the-box are listed in the\n  \\href{/docs/7-1/reference/-/knowledge_base/r/fully-qualified-portlet-ids}{Fully\n  Qualified Portlet IDs} reference guide. For custom portlets, this\n  property is listed in the portlet class as the\n  \\texttt{javax.portlet.name=} service property.\n\\item\n  List the portlet ID in the column of the layout you want to display\n  the portlet on. An example configuration is shown below:\n\n\\begin{verbatim}\n{\n    \"layoutTemplateId\": \"2_columns_ii\",\n    \"publicPages\": [\n        {\n            \"columns\": [\n                [\n                    {\n                        \"portletId\": \"com_liferay_login_web_portlet_LoginPortlet\"\n                    },\n                    {\n                        \"portletId\": \n                        \"com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet\"\n                    }\n                ],\n                [\n                    {\n                        \"portletId\": \"com_liferay_hello_world_web_portlet_HelloWorldPortlet\"\n                    }\n                ]\n            ],\n            \"friendlyURL\": \"/home\",\n            \"nameMap\": {\n                \"en_US\": \"Welcome\",\n                \"fr_FR\": \"Bienvenue\"\n            },\n            \"title\": \"Welcome\"\n        }\n    ]\n}\n\\end{verbatim}\n\n  This approach embeds the portlet with its default settings. To\n  customize the portlet, you must configure the portlet's preferences,\n  as described in the next step.\n\\item\n  Optionally specify portlet preferences for a portlet with the\n  \\texttt{portletPreferences} key. Below is an example for the Web\n  Content Display portlet:\n\n\\begin{verbatim}\n{\n    \"portletId\": \"com_liferay_journal_content_web_portlet_JournalContentPortlet\",\n    \"portletPreferences\": {\n        \"articleId\": \"Custom Title.xml\",\n        \"groupId\": \"${groupId}\",\n        \"portletSetupPortletDecoratorId\": \"decorate\",\n        \"portletSetupTitle_en_US\": \"Web Content Display with Custom Title\",\n        \"portletSetupUseCustomTitle\": \"true\"\n    }\n}\n\\end{verbatim}\n\n  \\textbf{portletSetupPortletDecoratorId:} Specifies the \\href{}{portlet\n  decorator} to use for the portlet (\\texttt{borderless}\n  \\textbar\\textbar{} \\texttt{barebone} \\textbar\\textbar{}\n  \\texttt{decorate}). See\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/setting-default-preferences-for-an-embedded-portlet}{Setting\n  Default Preference for An Embedded Portlet} for more information.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} You can specify an\n\\href{/docs/7-1/user/-/knowledge_base/u/styling-apps-and-assets}{application\ndisplay template} (ADT) for a portlet in the \\texttt{sitemap.json} file\nby setting the \\texttt{displayStyle} and \\texttt{displayStyleGroupId}\nportlet preferences, as shown in the example below:\n\n\\begin{verbatim}\n \"portletId\": \"com_liferay_asset_publisher_web_portlet_AssetPublisherPortlet\",\n     \"portletPreferences\": {\n         \"displayStyleGroupId\": \"10197\",\n         \"displayStyle\": \"ddmTemplate_6fe4851b-53bc-4ca7-868a-c836982836f4\"\n }\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nPortlet preferences are unique to each portlet, so first you must\ndetermine which preferences you want to configure. There are two ways to\ndetermine the proper key/value pair for a portlet preference. The first\nis to set the portlet preference manually, and then check the values in\nthe \\texttt{portletPreferences.preferences} column of the database as a\nhint for what to configure in your \\texttt{sitemap.json}. For example,\nyou can configure the Asset Publisher to display assets that match the\nspecified asset tags, using the following configuration:\n\n\\begin{verbatim}\n\"queryName0\": \"assetTags\",\n\"queryValues0\": \"MyAssetTagName\"\n\\end{verbatim}\n\nAnother approach is to search each app in your bundle for the keyword\n\\texttt{preferences-\\/-}. This returns some of the app's JSPs that have\nthe portlet preferences defined for the portlet.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Portlet preferences that require an existing\nconfiguration, such as a tag or category, may require you to create the\nconfiguration on the Global site first, so that the Resources Importer\nfinds a match when deployed with the theme.\n\n\\noindent\\hrulefill\n\n\\section{Related Topics}\\label{related-topics-73}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer}{Preparing\n  and Organizing Web Content for the Resources Importer}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-layout-templates-and-pages-in-a-sitemap}{Defining\n  Layout Templates and Pages in a Sitemap}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/specifying-where-to-import-your-themes-resources}{Specifying\n  Where to Import Your Theme's Resources}\n\\end{itemize}\n\n\\chapter{Retrieving Portlet IDs with the Gogo\nShell}\\label{retrieving-portlet-ids-with-the-gogo-shell}\n\nTo\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-portlets-in-a-sitemap}{include\na portlet in a sitemap}, you must have its ID. For convenience, IDs for\nout-of-the-box portlets appear in the\n\\href{/docs/7-2/reference/-/knowledge_base/r/fully-qualified-portlet-ids}{Fully\nQualified Portlet IDs} reference guide. If you've installed purchased or\ndeveloped portlets, you can retrieve their IDs using the Gogo Shell, as\nthis tutorial instructs.\n\nFollow these steps to use the Gogo Shell to retrieve a portlet ID:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Control Panel and go to Configuration → Gogo Shell.\n\\item\n  Run the command \\texttt{lb\\ {[}app\\ prefix{]}}, and locate the app's\n  web bundle. For example, run \\texttt{lb\\ blogs} to find the blogs web\n  bundle.\n\n\\begin{verbatim}\n100|Active     |   10|Liferay Blogs Web (3.0.7)\n\\end{verbatim}\n\\item\n  Run the command \\texttt{scr:list\\ {[}bundle\\ ID{]}}, and locate the\n  app's component ID. The blogs portlet entry is shown below. The last\n  number preceding the bundle's state is the component ID:\n\n\\begin{verbatim}\n[ 100] com.liferay.blogs.web.internal.portlet.BlogsPortlet enabled \n[ 196] [active] \n\\end{verbatim}\n\\item\n  Run the command \\texttt{scr:info\\ {[}component\\ ID{]}} to list the\n  portlet's information. For example, to list the info for the blogs\n  portlet component, run \\texttt{scr:info\\ 196}. Note that the bundle\n  and/or component ID may be different for your instance.\n\\item\n  Search for \\texttt{javax.portlet.name} in the results.\n  \\texttt{javax.portlet.name}'s value is the portlet ID required for the\n  sitemap. The blogs portlet's ID is shown below:\n\n\\begin{verbatim}\njavax.portlet.name = com_liferay_blogs_web_portlet_BlogsPortlet\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/resources-importer-gogo-shell.png}\n\\caption{Portlet IDs can be found via the Gogo Shell.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-74}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-layout-templates-and-pages-in-a-sitemap}{Defining\n  Layout Templates and Pages in a Sitemap}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-portlets-in-a-sitemap}{Defining\n  Portlets in a Sitemap}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer}{Preparing\n  and Organizing Web Content for the Resources Importer}\n\\end{itemize}\n\n\\chapter{Preparing and Organizing Web Content for the Resources\nImporter}\\label{preparing-and-organizing-web-content-for-the-resources-importer}\n\nYou must create the resources to import with your theme. You can create\nresources from scratch and/or bring in resources that you've already\ncreated. You can leverage your HTML (basic web content), JSON\n(structures), or VM or FTL (templates) files with the Resource Importer.\nAll web content articles require a structure and optionally a template.\nNote that some articles may share the same structure and perhaps even\nthe same template---this is the case for all basic web content articles.\nFollow these steps to prepare your web content articles:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select \\emph{Edit} from the article's options menu, click the\n  \\emph{Options} icon at the top right of the page and select \\emph{View\n  Source}. Copy the article's raw XML into an XML file locally. Create a\n  folder for the article under\n  \\texttt{resources-importer/journal/articles/} and rename it as\n  desired. The web content article's XML fills in the data required by\n  the structure. An example web content article's XML is shown below:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<root available-locales=\"en_US\" default-locale=\"en_US\">\n  <dynamic-element name=\"content\" type=\"text_area\" index-type=\"keyword\" index=\"0\">\n    <dynamic-content language-id=\"en_US\">\n        <![CDATA[\n            <center>\n            <p><img alt=\"\" src=\"[$FILE=space-program-history.jpg$]\" /></p>\n            </center>\n\n            <p>In the mid-20th century, after two of the \n            most violent wars in history, mankind turned \n            its gaze upwards to the stars. Instead of \n            continuing to strive against one another, \n            man choose instead to strive against the \n            limits that we had bound ourselves to. And \n            so the Great Space Race began.</p>\n\n            <p>At first the race was to reach space--get \n            outside the earth's atmosphere, and when \n            that had been reached, we shot for the moon. \n            After sending men to the moon, robots to \n            Mars, and probes beyond the reaches of our \n            solar system, it seemed that there was \n            nowhere left to go.</p>\n\n            <p>The Space Program aims to change that. \n            Beyond national boundaries, beyond what \n            anyone can imagine that we can do. The sky \n            is not the limit.</p>\n        ]]>\n    </dynamic-content>\n  </dynamic-element>\n</root>\n\\end{verbatim}\n\\item\n  Download the web content article's structure. Open the structure and\n  click the \\emph{Source} tab to view the structure's file. Copy and\n  paste its contents into a new JSON file in the\n  \\texttt{resources-importer/journal/structures/} folder. The structure\n  JSON sets a wireframe, or blueprint, for an article's data. If you're\n  saving a basic web content article, you can copy the structure below\n  (replace \\texttt{en\\_US} with your language):\n\n\\begin{verbatim}\n{\n    \"availableLanguageIds\": [\n        \"en_US\"\n    ],\n    \"defaultLanguageId\": \"en_US\",\n    \"fields\": [\n        {\n            \"label\": {\n                \"en_US\": \"Content\"\n            },\n            \"predefinedValue\": {\n                \"en_US\": \"\"\n            },\n            \"style\": {\n                \"en_US\": \"\"\n            },\n            \"tip\": {\n                \"en_US\": \"\"\n            },\n            \"dataType\": \"html\",\n            \"fieldNamespace\": \"ddm\",\n            \"indexType\": \"text\",\n            \"localizable\": true,\n            \"name\": \"content\",\n            \"readOnly\": false,\n            \"repeatable\": false,\n            \"required\": false,\n            \"showLabel\": true,\n            \"type\": \"ddm-text-html\"\n        }\n    ]\n}\n\\end{verbatim}\n\\item\n  Download the structure's matching template if it has one. Open the\n  Actions menu for the structure and select \\emph{Manage Templates} to\n  view the templates that use it. Create a folder for the template under\n  \\texttt{resources-importer/journal/templates/} and copy and paste its\n  contents into a new FTL file. The template defines how the data should\n  be displayed. If you're saving a basic web content article, you can\n  copy the FreeMarker template below:\n\n\\begin{verbatim}\n${content.getData()}\n\\end{verbatim}\n\\end{enumerate}\n\nRepeat the steps above for each web content article you have. Note that\nsome web content articles may share the same structure and template; In\nthese cases, only one copy of the structure and template is required for\nall web content articles that use them. Once your web content articles\nare saved, you can place them in their proper folder structure.\n\n\\section{Related Topics}\\label{related-topics-75}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-sitemap-for-the-resources-importer}{Creating\n  a Sitemap for the Resources Importer}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-assets-for-the-resources-importer}{Defining\n  Assets for the Resources Importer}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/specifying-where-to-import-your-themes-resources}{Specifying\n  Where to Import Your Theme's Resources}\n\\end{itemize}\n\n\\chapter{Defining Assets for the Resources\nImporter}\\label{defining-assets-for-the-resources-importer}\n\nThe \\texttt{sitemap.json} file defines the pages of the site or site\ntemplate to import---along with the layout templates, portlets, and\nportlet preferences of these pages. You may also want to provide details\nabout the assets you include with the theme. An \\texttt{assets.json}\nfile lets you provide this information.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create the \\texttt{assets.json} in your theme's\n  \\texttt{{[}theme-name{]}/src/WEB-INF/src/resources-importer} folder.\n\\item\n  Follow the pattern below to configure your \\texttt{assets.json} file.\n  Tags can be applied to any asset. Abstract summaries and small images\n  can be applied to web content articles. For example, the following\n  \\texttt{assets.json} file specifies two tags for the\n  \\texttt{company\\_logo.png} image, one tag for the\n  \\texttt{Custom\\ Title.xml} web content article, and an abstract\n  summary and small image for the \\texttt{Child\\ Web\\ Content\\ 1.json}\n  article structure:\n\n\\begin{verbatim}\n{\n    \"assets\": [\n        {\n            \"name\": \"company_logo.png\",\n            \"tags\": [\n                \"logo\",\n                \"company\"\n            ]\n        },\n        {\n            \"name\": \"Custom Title.xml\",\n            \"tags\": [\n                \"web content\"\n            ]\n        },\n        {\n            \"abstractSummary\": \"This is an abstract summary.\",\n            \"name\": \"Child Web Content 1.json\",\n            \"smallImage\": \"company_logo.png\"\n        }\n    ]\n}\n\\end{verbatim}\n\\end{enumerate}\n\nNow you know how to configure assets for your web content!\n\n\\section{Related Topics}\\label{related-topics-76}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer}{Preparing\n  and Organizing Web Content for the Resources Importer}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-sitemap-for-the-resources-importer}{Creating\n  a Sitemap for the Resources Importer}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/specifying-where-to-import-your-themes-resources}{Specifying\n  Where to Import Your Theme's Resources}\n\\end{itemize}\n\n\\chapter{Specifying Where to Import Your Theme's\nResources}\\label{specifying-where-to-import-your-themes-resources}\n\nBy default, resources are imported into a new site template named after\nthe theme, but you can also import resources into a new site or existing\nsites or site templates. These options are covered below. Follow these\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Before specifying where to import your resources, you must enable\n  Developer Mode in your theme. To do this, add the following property\n  to your theme's \\texttt{liferay-plugin-package.properties} file. This\n  is enabled by default for themes generated with the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-generator}{Liferay\n  Theme Generator}. Without this property enabled, you must manually\n  delete the sites or site templates built by the Resources Importer\n  each time you want to apply changes from your theme's\n  \\texttt{src/WEB-INF/src/resources-importer} folder:\n\n\\begin{verbatim}\nresources-importer-developer-mode-enabled=true\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Warning:** the `resources-importer-developer-mode-enabled=true` setting can be\n dangerous since it involves *deleting* (and re-creating) the affected site or\n site template. It's only intended to be used during development. **Never use it\n in production.**\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Specify where to import your resources. By default, resources are\n  imported into a new site template named after the theme. If that's\n  what you want, you can skip this step. If instead you want your\n  resources to be imported into an existing site template, you must\n  specify a value for the \\texttt{resources-importer-target-value}\n  property in your theme's \\texttt{liferay-plugin-package.properties}\n  file:\n\n\\begin{verbatim}\n#resources-importer-target-class-name\n\nresources-importer-target-value=[site-template-name]\n\\end{verbatim}\n\n  Alternatively, you can import resources into an existing site.\n  \\textbf{You must} import your resources into a site if you define both\n  public and private page sets in your \\texttt{sitemap.json}. To import\n  resources into an existing site, uncomment the\n  \\texttt{resources-importer-target-class-name} property and set it to\n  \\texttt{com.liferay.portal.kernel.model.Group}:\n\n\\begin{verbatim}\nresources-importer-target-class-name=com.liferay.portal.kernel.model.Group\n\nresources-importer-target-value=[site-name] \n\\end{verbatim}\n\n  Double check the name that you're specifying. If you specify the wrong\n  value, you could end up deleting (and re-creating) the wrong site or\n  site template!\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Warning:** It's safer to import theme resources into a site template than into\n an actual site. The\n `resources-importer-target-class-name=com.liferay.portal.kernel.model.Group`\n setting can be handy for development and testing but should be used cautiously.\n Don't use this setting in a theme deployed to a production Liferay instance or\n a theme submitted to Liferay Marketplace. To prepare a theme for deployment to\n a production Liferay instance, use the default setting so that the resources are\n imported into a site template. You can do this explicitly by setting\n `resources-importer-target-class-name=com.liferay.portal.kernel.model.LayoutSetPrototype`\n or implicitly by commenting out or removing the\n `resources-importer-target-class-name` property.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Deploy the theme. To view your theme and its resources, log in as an\n  administrator, and check the Sites or Site Templates section of the\n  Control Panel to make sure your resources were deployed correctly.\n  From the Control Panel you can easily view your theme and its\n  resources:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    If you imported into a site template, open its actions menu and\n    select \\emph{View Pages} to see it.\n  \\item\n    If you imported directly into a site, open its actions menu and\n    select \\emph{Go to Public Pages} to see it.\n  \\end{itemize}\n\\end{enumerate}\n\nGreat! Now you know how to specify where to import your theme's\nresources.\n\n\\section{Related Topics}\\label{related-topics-77}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer}{Preparing\n  and Organizing Web Content for the Resources Importer}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-sitemap-for-the-resources-importer}{Creating\n  a Sitemap for the Resources Importer}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-assets-for-the-resources-importer}{Defining\n  Assets for the Resources Importer}\n\\end{itemize}\n\n\\chapter{Archiving Site Resources}\\label{archiving-site-resources}\n\nAlthough a \\texttt{sitemap.json} is the recommended approach for\nincluding resources with a theme, you can also export your site's data\nin a LAR (Liferay Archive) file. A LAR file is version-specific; it\nwon't work on any version of Liferay DXP other than the one from which\nit was exported. This approach does, however, require less\nconfiguration, since it does not require a sitemap or other files. So,\nif you're using the exported resources in the same version of Liferay\nDXP and it's not for a theme on Liferay Marketplace, you may prefer a\nLAR file.\n\nFollow these steps to archive your site's resources:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Export the contents of a site using the site scope.\n\\item\n  Place the \\texttt{archive.lar} file in your theme's\n  \\texttt{/src/WEB-INF/src/resources-importer} folder.\n\\end{enumerate}\n\nGreat! Now you know how to archive your site's resources.\n\n\\section{Related Topics}\\label{related-topics-78}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer}{Preparing\n  and Organizing Web Content for the Resources Importer}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-sitemap-for-the-resources-importer}{Creating\n  a Sitemap for the Resources Importer}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-assets-for-the-resources-importer}{Defining\n  Assets for the Resources Importer}\n\\end{itemize}\n\n\\chapter{Troubleshooting Themes}\\label{troubleshooting-themes}\n\nThese frequently asked questions and answers help you troubleshoot and\ncorrect problems in theme development.\n\nClick a question to view the answer.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[osgi-headers-in-themes]{How can I include OSGi headers in my\n  theme?}\n\\item\n  \\hyperref[developer-mode]{Why aren't my changes showing up after I\n  redeploy my theme?}\n\\item\n  \\hyperref[default-theme-returned]{Why is my theme not loading? It\n  returns the default theme instead.}\n\\item\n  \\hyperref[rtl-no-flip]{How can I prevent specific CSS rules from\n  transforming for RTL Languages?}\n\\end{itemize}\n\n\\phantomsection\\label{osgi-headers-in-themes}\n{How can I include OSGi headers in my theme?~{}}\n\n\\begin{verbatim}\n<p>Specify the headers you want to use in your theme's <code>liferay-plugin-package.properties</code> file. Any headers placed in this file are included automatically in your MANIFEST and the OSGi bundle.</p>\n<p>For example, you can add OSGi dependencies in your theme by importing the exported package with the <code>Import-Package</code> header:</p>\n<pre><code>Import-Package:com.liferay.docs.portlet</code></pre>\n\\end{verbatim}\n\n\\phantomsection\\label{developer-mode}\n{Why aren't my changes showing up after I redeploy my theme?~{}}\n\n\\begin{verbatim}\n<p>By default CSS, JS, and theme template files are cached in the browser. During development, you can enable <a href=\"/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes\">Devloper Mode</a> to prevent your theme's files from caching. </p>\n\\end{verbatim}\n\n\\phantomsection\\label{default-theme-returned}\n{Why is my theme not loading? It returns the default theme instead.~{}}\n\n\\begin{verbatim}\n<p>If you receive the warning \"No theme found for specified theme id...\", you may be referencing an outdated theme ID in your Site. Verify that the theme ID in your theme's <code>liferay-look-and-feel.xml</code> matches the theme ID in the warning message: \"mytheme_WAR_mytheme\". If the theme IDs match, there may be pages using the outdated theme instead of the Site theme. You can verify this by checking the pages manually or searching the database for layouts with values for <code>themeId -</code>. </p>\n\\end{verbatim}\n\n\\phantomsection\\label{rtl-no-flip}\n{How can I prevent specific CSS rules from transforming for RTL\nLanguages?~{}}\n\n\\begin{verbatim}\n<p>You can prevent specific CSS rules from transforming (flipping) with the <code>/* @noflip */</code> decoration. Place the decoration to the left of the CSS rule to apply it. For example, this rule gives a left margin of <code>20em</code> to the <code>body</code> no matter if the selected language is LTR or RTL:</p>\n  <pre><code>\n  /* @noflip */ body {\n   margin-left: 20em;\n  }\n  </pre></code>\n<p>You can also use the <code>.rtl</code> CSS selector for rules that exclusively apply to RTL languages.</p>\n\\end{verbatim}\n\n\\chapter{Layout Templates}\\label{layout-templates}\n\nLayout templates dictate where content and apps can be placed on a page.\nThere are several default layout templates for organizing content on\nyour pages:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/page-select-layout.png}\n\\caption{There are many default layout templates to choose from.}\n\\end{figure}\n\nIf the default layouts don't work for your site, you can create your own\nlayout template by following the articles listed below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator}{Create\n  the layout template}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-layout-template-thumbnail-previews}{Create\n  the layout template thumbnail preview}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/including-layout-templates-with-a-theme}{Bundle\n  the layout template with a theme}\n\\end{itemize}\n\nLayout Templates specify a number of rows and columns for the page. The\nrows and columns dictate where apps (and fragments) can be placed on the\npage. Layout templates are written in\n\\href{https://freemarker.apache.org/}{FreeMarker}. An example row's HTML\nmarkup is shown below:\n\n\\begin{verbatim}\n<div class=\"portlet-layout row\">\n        <div class=\"col-md-4 col-sm-6 portlet-column portlet-column-first\" \n        id=\"column-1\">\n                ${processor.processColumn(\"column-1\", \n                \"portlet-column-content portlet-column-content-first\")}\n        </div>\n        <div class=\"col-md-8 col-sm-6 portlet-column portlet-column-last\" \n        id=\"column-2\">\n                ${processor.processColumn(\"column-2\", \n                \"portlet-column-content portlet-column-content-last\")}\n        </div>\n</div>\n\\end{verbatim}\n\nColumns use the\n\\href{https://getbootstrap.com/docs/4.0/layout/grid/}{Bootstrap grid\nsystem}. Every row consists of twelve sections, so columns can range in\nsize from \\texttt{1} to \\texttt{12}. Sizes are indicated with the number\nthat follows the \\texttt{col-{[}breakpoint{]}} class prefix\n(e.g.~\\texttt{col-md-6}). These specify two things: the percentage-based\nwidth of the element and the media query breakpoint (\\texttt{xs},\n\\texttt{sm}, \\texttt{md}, or \\texttt{lg}), which specifies when the\nelement expands to 100\\% width. For example, \\texttt{col-md-6} indicates\n\\texttt{6/12} width, or \\texttt{50\\%}. These classes can also be mixed\nto achieve more advanced layouts, as shown above. In the example, medium\nsized viewports display \\texttt{column-1} at 33.33\\% width and\n\\texttt{column-2} at 66.66\\% width, while both \\texttt{column-1} and\n\\texttt{column-2} are 50\\% width on small sized view ports.\n\nThe processor (\\texttt{\\$\\{processor.processColumn()\\}}) processes each\ncolumn's content, taking two arguments: the column's \\texttt{id}, and\nthe classes \\texttt{portlet-column-content} and\n\\texttt{portlet-column-content-{[}case{]}} (if applicable), where\n\\texttt{{[}case{]}} refers to the \\texttt{first}, \\texttt{last}, or\n\\texttt{only} column in the row.\n\n\\chapter{Creating Custom Layout Template Thumbnail\nPreviews}\\label{creating-custom-layout-template-thumbnail-previews}\n\nTo showcase your layout template properly, you must provide a thumbnail\npreview for it. Without this, no one will know the design of the layout.\nFollow these steps to provide a thumbnail preview for your layout\ntemplate:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the layout template's \\texttt{docroot/} folder. If you\n  bundled the layout template with a theme created with the Liferay\n  Theme Generator, the thumbnail is located in your theme's\n  \\texttt{src/layouttpl/custom/my-layoutttpl} folder.\n\\item\n  Create a custom thumbnail PNG inside the folder specified in step 1\n  with the same name as the layout template that is 120 px x 120 px .\n  Delete the temporary thumbnail PNG file if it exist.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/porygon_50_50_width_limited.png}\n  \\caption{A thumbnail preview displays the layout's design to the\n  user.}\n  \\end{figure}\n\\item\n  Deploy your layout template to your app server to use it. If your\n  layout template is\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/including-layout-templates-with-a-theme}{bundled\n  with a theme}, it deploys when the theme is deployed. Now you know how\n  to create a custom thumbnail preview for your Liferay DXP layout\n  templates!\n\\end{enumerate}\n\n\\section{Related topics}\\label{related-topics-79}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator}{Layout\n  Templates with the Liferay Theme Generator}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/including-layout-templates-with-a-theme}{Bundling\n  Layout Templates with a Theme}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{Themes}\n\\end{itemize}\n\n\\chapter{Including Layout Templates with a\nTheme}\\label{including-layout-templates-with-a-theme}\n\nAlthough you can deploy a layout template by itself, you can also bundle\nit with a theme. If you generated a layout template with the\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator}{Layouts\nsub-generator} from inside a generated theme project, the layout\ntemplate is bundled with the theme automatically. If, however, you\ngenerated a layout template and want to bundle it with a theme\nafterwards, follow these steps to include the layout template with a\ntheme:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Copy the layout template's \\texttt{liferay-layout-templates.xml} file\n  to the theme's \\texttt{src/WEB-INF/} folder.\n\\item\n  Create a \\texttt{layouttpl/custom/my-layouttpl/} folder inside the\n  theme's \\texttt{src/} folder.\n\\item\n  Copy the layout template's FreeMarker (.ftl) file, and\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-layout-template-thumbnail-previews}{thumbnail\n  preview} (.png) if it exist, over to the\n  \\texttt{layouttpl/custom/my-layouttpl/} folder.\n\\item\n  Copy the theme's \\texttt{liferay-theme.json} file into the\n  \\texttt{src/layouttpl/custom/my-layouttpl/} folder and rename it\n  \\texttt{liferay-plugin.json}.\n\\item\n  Open \\texttt{liferay-plugin.json}, rename the \\texttt{LiferayTheme}\n  entry \\texttt{LiferayPlugin}, and replace the \\texttt{pluginName}\n  entry's value with the name of the layout template. Below is an\n  example configuration:\n\\end{enumerate}\n\n\\begin{verbatim}\n{\n  \"LiferayPlugin\": {\n    \"deploymentStrategy\": \"LocalAppServer\",\n    \"appServerPath\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0\\\\tomcat-9.0.10\",\n    \"deployPath\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0\\\\tomcat-9.0.10\\\\deploy\",\n    \"url\": \"http://localhost:8080\",\n    \"appServerPathPlugin\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0\\\\tomcat-9.0.10\\\\webapps\\\\my-layouttpl\",\n    \"deployed\": false,\n    \"pluginName\": \"my-layouttpl\"\n  }\n}\n\\end{verbatim}\n\nNow you know how to include layout templates with your Liferay DXP\nthemes!\n\n\\section{Related topics}\\label{related-topics-80}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator}{Layout\n  Templates with the Liferay Theme Generator}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-layout-template-thumbnail-previews}{Creating\n  Custom Layout Template Thumbnail Previews}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{Themes}\n\\end{itemize}\n\n\\chapter{Creating and Bundling JavaScript Widgets with JavaScript\nTooling}\\label{creating-and-bundling-javascript-widgets-with-javascript-tooling}\n\nPortlets are a Java standard, so you must have a knowledge and\nunderstanding of how Java works to write one. This can be quite the\nhurdle for front-end developers who want to use JavaScript frameworks in\ntheir widgets. Thanks to the Liferay JS Generator and\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-npm-bundler}{liferay-npm-bundler},\ndevelopers can easily create and develop JavaScript widgets in Liferay\nDXP using pure JavaScript tooling. The Liferay JS Generator generates\nJavaScript widgets for Liferay DXP. It is just one of Liferay JS\nToolkit's\n\\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages}{tools}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To use the Liferay JS Generator, you must have the\nLiferay JS Portlet Extender activated in your Liferay DXP instance. It's\nactivated by default. You can confirm this by opening the Control Menu,\nnavigating to the \\emph{App Manager}, and searching for\n\\texttt{com.liferay.frontend.js.portlet.extender}.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/extender-lifecycle.png}\n\\caption{The JS Portlet Extender lets you use pure JavaScript tooling to\nwrite widgets.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} JavaScript Server-Side-Rendering is not supported\nout-of-the-box. To use JS frameworks for site rendering, you\n\\textbf{must} set up your server-side (or search-crawler) rendering\ngeneration to support them.\n\n\\noindent\\hrulefill\n\nOnce you've\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-js-generator-and-generating-a-bundle}{installed\nthe Liferay JS Generator and generated a widget}, you can configure\ninstance settings, system settings, and even provide localization for\nyour widget. This section explains how to configure these options for\nyour generated JS widget.\n\n\\chapter{Configuring System Settings and Instance Settings for Your\nJavaScript\nWidget}\\label{configuring-system-settings-and-instance-settings-for-your-javascript-widget}\n\nAs of v1.1.0 of the JS Portlet Extender, you can define configuration\noptions for your widget. These options are passed to the widget's\nJavaScript entry point as the \\texttt{configuration} parameter. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/understanding-the-js-portlet-extender-configuration\\#main-entry-point}{main\nentry point's reference} for more information on the entry point. Follow\nthese steps to set system and/or portlet instance settings for your\nwidget:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Add a \\texttt{/features} folder in your project's root folder if it\n  doesn't already exist.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** This location can be overridden with the \n `create-jar.features.configuration` option in your project's `.npmbundlerrc` \n file. See [OSGi bundle configuration options](/docs/7-2/reference/-/knowledge_base/r/understanding-the-npmbundlerrcs-structure#osgi-bundle-creation-options) \n for all the available options for the bundle.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Create a \\texttt{configuration.json} file in the \\texttt{/features}\n  folder and follow the pattern below. See the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/configuration-json-available-options}{Configuration\n  JSON} reference for an explanation of each of the available options:\n\n\\begin{verbatim}\n{\n  \"system\": {\n    \"category\": \"{category identifier}\",\n    \"name\": \"{name of configuration}\",\n    \"fields\": {\n      \"{field id 1}\": {\n        \"type\": \"{field type}\",\n        \"name\": \"{field name}\",\n        \"description\": \"{field description}\",\n        \"default\": \"{default value}\",\n        \"options\": {\n          \"{option id 1}\": \"{option name 1}\",\n          \"{option id 2}\": \"{option name 2}\",\n\n          \"{option id n}\": \"{option name n}\"\n        }\n      },\n      \"{field id 2}\": {},\n\n      \"{field id n}\": {}\n    }\n  },\n  \"portletInstance\": {\n    \"name\": \"{name of configuration}\",\n    \"fields\": {\n      \"{field id 1}\": {\n        \"type\": \"{field type}\",\n        \"name\": \"{field name}\",\n        \"description\": \"{field description}\",\n        \"default\": \"{default value}\",\n        \"options\": {\n          \"{option id 1}\": \"{option name 1}\",\n          \"{option id 2}\": \"{option name 2}\",\n\n          \"{option id n}\": \"{option name n}\"\n        }\n      },\n      \"{field id 2}\": {},\n\n      \"{field id n}\": {}\n    }\n  }\n}\n\\end{verbatim}\n\\item\n  Access a system setting's value or a portlet instance setting's value\n  with the syntax \\texttt{configuration.system} or\n  \\texttt{configuration.portletInstance} respectively. For instance, to\n  retrieve the \\texttt{\\{field\\ id\\ 1\\}} system settings value, you\n  would use \\texttt{configuration.system.\\{field\\ id\\ 1\\}}. Note that\n  all fields are passed as strings no matter what type they declare in\n  their descriptor.\n\\end{enumerate}\n\nAwesome! Now you know how to configure system settings and portlet\ninstance settings for your widget.\n\n\\section{Related Topics}\\label{related-topics-81}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-widget}{Localizing\n  Your Widget}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-translation-features-in-your-widget}{Using\n  Translation Features in Your JavaScript Widget}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configuring-portlet-properties-for-your-widget}{Setting\n  Portlet Properties for Your JavaScript Widget}\n\\end{itemize}\n\n\\chapter{Localizing Your Widget}\\label{localizing-your-widget}\n\nFollow the steps below to learn how to localize your widget:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If you chose not to use localization when you generated the bundle,\n  follow this step to enable it in your bundle. Create a\n  \\texttt{/features/localization} folder in your project and add a\n  \\texttt{Language.properties} file to it. Add a\n  \\texttt{create-jar.features.localization} key to your\n  \\texttt{.npmbuildrc} file that points to the\n  \\texttt{Language.properties} file. An example configuration is shown\n  below:\n\n\\begin{verbatim}\n{\n    \"create-jar\": {\n        \"output-dir\": \"dist\",\n        \"features\": {\n            \"js-extender\": true,\n            \"web-context\": \"/my-test-js-widget\",\n            \"localization\": \"features/localization/Language\",\n            \"settings\": \"features/settings.json\"\n        }\n    },\n    ...\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The default file path is shown above. You can update this value, \n if you want to place your `Language.properties` file in a different \n location.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Configure the \\texttt{Language.properties} file and provide the\n  localized property files\n  (e.g.~\\texttt{Language\\_{[}locale{]}.properties}) with the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application}{language\n  keys} for each\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Languages\\%20and\\%20Time\\%20Zones}{available\n  translation}. The \\emph{JavaScript based widget} configuration is\n  shown below:\n\n\\begin{verbatim}\njavax.portlet.title.my_js_portlet_project=My JS Widget Project\nportlet-namespace=Portlet Namespace\ncontext-path=Context Path\nportlet-element-id=Portlet Element Id\nconfiguration=Configuration\nfruit=Favourite fruit\nfruit-help=Choose the fruit you like the most\nan-orange=An orange\na-pear=A pear\nan-apple=An apple\n\\end{verbatim}\n\\item\n  Retrieve a language key's localized value in JavaScript with the\n  \\texttt{Liferay.Language.get(\\textquotesingle{}key\\textquotesingle{})}\n  method.\n\\end{enumerate}\n\nGreat! Now you know how to localize your widget!\n\n\\section{Related Topics}\\label{related-topics-82}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configuring-system-settings-and-instance-settings-for-your-js-widget}{Configuring\n  System Settings and Instance Settings for Your JavaScript Widget}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-translation-features-in-your-widget}{Using\n  Translation Features in Your JavaScript Widget}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configuring-portlet-properties-for-your-widget}{Setting\n  Portlet Properties for Your JavaScript Widget}\n\\end{itemize}\n\n\\chapter{Using Translation Features in Your\nWidget}\\label{using-translation-features-in-your-widget}\n\nBy default, the Liferay JS Generator creates an empty configuration for\ntranslation. The translate script instructs the user how to add new\nsupported locales or configure the credentials when it's run. The\ntranslate target reads the supported locales you have defined in the\n\\texttt{supportedLocales} key of your \\texttt{.npmbuildrc} file and\nchecks your \\texttt{*language.properties} files to make sure they match.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To use the translation features, you must have a\nMicrosoft Translator key. Provide your credentials through either the\n\\texttt{translatorTextKey} variable in your \\texttt{.npmbuildrc} file,\nor provide them in the \\texttt{TRANSLATOR\\_TEXT\\_KEY} environment\nvariable.\n\n\\noindent\\hrulefill\n\nFollow these steps to add a new supported locale and automatically\ncreate a language properties file for it with translations:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the locale to the \\texttt{supportedLocales} array in your\n  \\texttt{.npmbuildrc} file.\n\\item\n  Run the translate target with the command below:\n\n\\begin{verbatim}\nnpm run translate\n\\end{verbatim}\n\\item\n  The translate target automatically creates a language properties file\n  for each new \\textbf{supported} locale with translations for your\n  language keys. It also warns about locales that are not supported, but\n  have a \\texttt{*language.properties} file.\n\\end{enumerate}\n\nGreat! Now you know how to use the Liferay JS Generator's translation\nfeatures in your app.\n\n\\section{Related Topics}\\label{related-topics-83}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configuring-system-settings-and-instance-settings-for-your-js-widget}{Configuring\n  System Settings and Instance Settings for Your JavaScript Widget}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-widget}{Localizing\n  Your Widget}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configuring-portlet-properties-for-your-widget}{Setting\n  Portlet Properties for Your JavaScript Widget}\n\\end{itemize}\n\n\\chapter{Configuring Portlet Properties for Your\nWidget}\\label{configuring-portlet-properties-for-your-widget}\n\nFollow these steps to configure your widget's properties:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your generated JavaScript widget's \\texttt{package.json} file.\n\\item\n  Set the properties under the \\texttt{portlet} entry. Note that these\n  are the same properties you would define in the Java\n  \\texttt{@Component} annotation of a portlet, as defined in the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/definitions/liferay-portlet-app_7_2_0.dtd.html}{liferay-portlet-app\\_7\\_2\\_0.dtd}.\n  An example configuration is shown below:\n\n\\begin{verbatim}\n\"portlet\": {\n    \"com.liferay.portlet.display-category\": \"category.sample\",\n    \"com.liferay.portlet.header-portlet-css\": \"/css/styles.css\",\n    \"com.liferay.portlet.instanceable\": true,\n    \"javax.portlet.name\": \"my_js_portlet_project\",\n    \"javax.portlet.security-role-ref\": \"power-user,user\",\n    \"javax.portlet.resource-bundle\": \"content.Language\"\n},\n\\end{verbatim}\n\\item\n  Deploy your bundle to apply the changes.\n\\end{enumerate}\n\nGreat! Now you know how to configure your JavaScript widget's\nproperties.\n\n\\section{Related Topics}\\label{related-topics-84}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configuring-system-settings-and-instance-settings-for-your-js-widget}{Configuring\n  System Settings and Instance Settings for Your JavaScript Widget}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-widget}{Localizing\n  Your Widget}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-translation-features-in-your-widget}{Using\n  Translation Features in Your JavaScript Widget}\n\\end{itemize}\n\n\\chapter{JavaScript Module Loaders}\\label{javascript-module-loaders}\n\nA JavaScript module encapsulates code into a useful unit that exports\nits capability/value. This makes it easier to see the broader scope,\neasier to find what you're looking for, and keeps related code close\ntogether. A normal web page usually loads JavaScript files via HTML\n\\texttt{script} tags. That's fine for small websites, but when\ndeveloping large scale web applications, a more robust organization and\nloader is needed. A module loader lets an application load dependencies\neasily by specifying a string that identifies the JavaScript module's\nname.\n\nThis section shows how to load JavaScript modules in Liferay DXP.\n\n\\chapter{Loading AMD Modules in\nLiferay}\\label{loading-amd-modules-in-liferay}\n\nModularized JavaScript code is a specification for the JavaScript\nlanguage called Asynchronous Module Definition, or AMD. The\n\\href{https://github.com/liferay/liferay-amd-loader\\#amd-module-loader}{Liferay\nAMD Module Loader} is the native loader that you can use to load your\nAMD modules. The steps below cover how to use the Liferay AMD Module\nLoader.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} While you can manually configure the AMD Loader, we\nrecommend that you use the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-npm-in-your-portlets}{liferay-npm-bundler}\ninstead.\n\n\\noindent\\hrulefill\n\nFollow these steps to prepare your module for the Liferay AMD Module\nLoader:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Wrap your AMD module code with the \\texttt{Liferay.Loader.define()}\n  method, such as the one shown below:\n\n\\begin{verbatim}\nLiferay.Loader.define('my-dialog', ['my-node', 'my-plugin-base'], \nfunction(myNode, myPluginBase) {\n   return {\n       log: function(text) {\n           console.log('module my-dialog: ' + text);\n       }\n   };\n});\n\\end{verbatim}\n\\item\n  You can modify the configuration to load the module when another\n  module is triggered or when a condition is met. The configuration\n  below specifies that this module should be loaded if another module\n  requests the \\texttt{my-test} module:\n\n\\begin{verbatim}\nLiferay.Loader.define('my-dialog', ['my-node', 'my-plugin-base'], \nfunction(myNode, myPluginBase) {\n   return {\n       log: function(text) {\n           console.log('module my-dialog: ' + text);\n       }\n   };\n}, {\n   condition: {\n       trigger: 'my-test',\n       test: function() {\n           var el = document.createElement('input');\n\n           return ('placeholder' in el);\n       }\n   },\n   path: 'my-dialog.js'\n});\n\\end{verbatim}\n\n  The Liferay AMD Loader uses the definition, along with the listed\n  dependencies, as well as any other configurations specified, to create\n  a \\texttt{config.json} file. This configuration object tells the\n  loader which modules are available, where they are located, and what\n  dependencies they require. Below is an example of a generated\n  \\texttt{config.json} file:\n\n\\begin{verbatim}\n{\n    \"frontend-js-web@1.0.0/html/js/parser\": {\n        \"dependencies\": []\n    },\n    \"frontend-js-web@1.0.0/html/js/list-display\": {\n        \"dependencies\": [\"exports\"]\n    },\n    \"frontend-js-web@1.0.0/html/js/autocomplete\": {\n        \"dependencies\": [\"exports\", \"./parser\", \"./list-display\"]\n    }\n}\n\\end{verbatim}\n\\item\n  Load your module in your scripts. Pass the module name to the\n  \\texttt{Liferay.Loader.require} method. The example below loads a\n  module called \\texttt{my-dialog}:\n\n\\begin{verbatim}\nLiferay.Loader.require('my-dialog', function(myDialog) {\n    // your code here\n}, function(error) {\n    console.error(error);\n});\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** By default, the AMD Loader times out in seven seconds. You can \n configure this value through System Settings. Open the Control Panel and \n navigate to *Configuration* &rarr; *System Settings* &rarr; *PLATFORM* &rarr; \n *Infrastructure*, and select *JavaScript Loader*. Set the *Module Definition \n Timeout* configuration to the time you want and click *Save*.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\section{Related Topics}\\label{related-topics-85}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/loading-modules-with-aui-script}{Loading\n  Modules with AUI Script}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-npm-in-your-portlets}{Using\n  npm in Your Portlets}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/loading-modules-with-aui-script}{Loading\n  Modules with AUI Script}\n\\end{itemize}\n\n\\chapter{Using External JavaScript\nLibraries}\\label{using-external-javascript-libraries}\n\nYou can use external JavaScript libraries in your portlets (i.e.,\nanything but Metal.js or jQuery, which are included by default). If\nyou're the owner or hosting the external library, there are a few more\nrequirements to load them with the JavaScript Loaders.\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If you're the owner of the library, you should make sure that it\n  supports \\href{https://github.com/umdjs/umd}{UMD} (Universal Module\n  Definition). You can configure your code to support UMD with the\n  template shown below:\n\n\\begin{verbatim}\n// Assuming your \"module\" will be exported as \"mylibrary\"\n(function (root, factory) {\n    if (typeof Liferay.Loader.define === 'function' && Liferay.Loader.define.amd) {\n        // AMD. Register as a \"named\" module.\n        Liferay.Loader.define('mylibrary', [], factory);\n    } else if (typeof module === 'object' && module.exports) {\n        // Node. Does not work with strict CommonJS, but\n        // only CommonJS-like environments that support module.exports,\n        // like Node.\n        module.exports = factory();\n    } else {\n        // Browser globals (root is window)\n        root.mylibrary = factory();\n  }\n}(this, function () {\n\n    // Your library code goes here\n    return {};\n}));\n\\end{verbatim}\n\\item\n  If you're hosting the library (and not loading it from a CDN), you\n  must hide the Liferay AMD Loader to use your Library. Open the Control\n  Panel, navigate to \\emph{Configuration} → \\emph{System Settings}.\n\\item\n  Click \\emph{JavaScript Loader} under \\emph{Platform} →\n  \\emph{Infrastructure}.\n\\item\n  Uncheck the \\texttt{expose\\ global} option.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Once this option is unchecked, you can no longer use the\n\\texttt{Liferay.Loader.define} or \\texttt{Liferay.Loader.require}\nfunctions in your app. Also, if you're using third party libraries that\nare AMD compatible, they could stop working after unchecking this option\nbecause they usually use global functions like \\texttt{require()} or\n\\texttt{define()}.\n\n\\noindent\\hrulefill\n\nGreat! Now you know how to adapt external libraries for Liferay's\nJavaScript Loaders.\n\n\\section{Related Topics}\\label{related-topics-86}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/loading-amd-modules-in-liferay}{Liferay\n  AMD Module Loader}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-esplus-modules-in-your-portlet}{Using\n  ES2015+ Modules in Your Portlet}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/loading-modules-with-aui-script}{Loading\n  Modules with AUI Script}\n\\end{itemize}\n\n\\chapter{Loading Modules with AUI\nScript}\\label{loading-modules-with-aui-script}\n\nThe \\texttt{aui:script} tag is a JSP tag that loads JavaScript on the\npage, while ensuring that certain resources are loaded before executing.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} AUI is deprecated and no longer in active development in\n7.0, but all the tags will remain fully functional in Liferay DXP 7.2.\nEventually, these tags will be replaced with\n\\href{https://clayui.com/}{Clay} tag counterparts.\n\n\\noindent\\hrulefill\n\nThe \\texttt{aui:script} tag supports the following options:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nOption\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{require} & Requires an AMD module to load with the\n\\href{https://github.com/liferay/liferay-amd-loader\\#amd-module-loader}{Liferay\nAMD Module Loader}. \\\\\n\\texttt{use} & Uses an AlloyUI/YUI module that is loaded via the YUI\nloader. \\\\\n\\texttt{position} & The position the script tag is put on the page.\nPossible options are \\texttt{inline} or \\texttt{auto}. \\\\\n\\texttt{sandbox} & Whether to wrap the script tag in an anonymous\nfunction. If set to \\texttt{true}, in addition to the wrapping,\n\\texttt{\\$} and \\texttt{\\_} are defined for jQuery and underscore. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nThis section covers how to load ES2015, Metal.js, and AUI modules with\nthe AUI script tag.\n\n\\chapter{Loading AlloyUI Modules with AUI\nScript}\\label{loading-alloyui-modules-with-aui-script}\n\nFollow these steps to load modules with\n\\texttt{\\textless{}aui:script\\textgreater{}}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the following declaration to your portlet's JSP:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}aui:script\\textgreater{}} tag and use the\n  \\texttt{use} attribute to load AlloyUI/YUI modules:\n\n\\begin{verbatim}\n<aui:script use=\"aui-base\">\n    A.one('#someNodeId').on(\n        'click',\n        function(event) {\n            alert('Thank you for clicking.')\n        }\n    );\n</aui:script>\n\\end{verbatim}\n\\end{enumerate}\n\nThis loads the \\texttt{aui-base} AlloyUI component and makes it\navailable to the code inside the \\texttt{aui:script}.\n\nIn the browser, the \\texttt{aui:script} translates to the full\nJavaScript shown below:\n\n\\begin{verbatim}\n<script type=\"text/javascript\">\n    AUI().use(\"aui-base\",\n        function(A){\n            A.one('#someNodeId').on(\n                'click',\n                function(event) {\n                    alert('Thank you for clicking.')\n                }\n            );\n        }\n    );\n</script>\n\\end{verbatim}\n\nWonderful! Now you know how to load AUI/YUI modules in AUI scripts.\n\n\\section{Related Topics}\\label{related-topics-87}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-external-javascript-libraries}{Using\n  External JavaScript Libraries}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/loading-amd-modules-in-liferay}{Loading\n  AMD Modules}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/loading-es2015-and-metal-modules-with-aui-script}{Loading\n  ES2015 and Metal.js Modules with AUI Script}\n\\end{itemize}\n\n\\chapter{Loading ES2015 and Metal.js Modules with AUI\nScript}\\label{loading-es2015-and-metal.js-modules-with-aui-script}\n\nFollow these steps to load your ES2015 and Metal.js modules with\n\\texttt{\\textless{}aui:script\\textgreater{}}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the following declaration to your portlet's JSP:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}aui:script\\textgreater{}} tag and use the\n  \\texttt{require} attribute to load ES2015 and Metal.js modules:\n\n\\begin{verbatim}\n<aui:script require=\"metal-clipboard/src/Clipboard\">\n    new metalClipboardSrcClipboard.default();\n</aui:script>\n\\end{verbatim}\n\\end{enumerate}\n\nalternatively, you can specify a variable for your module by adding\n\\texttt{as\\ variableName} after the module name, as shown in the example\nbelow:\n\n\\begin{verbatim}\n<aui:script require=\"metal-clipboard/src/Clipboard as myModule\">\n    new myModule.default();\n</aui:script>\n\\end{verbatim}\n\nThis resolves the dependencies of the registered module and loads them\nin order until all of them are satisfied and the requested module can be\nsafely executed.\n\nIn the browser, the \\texttt{aui:script} translates to the full\nJavaScript shown below:\n\n\\begin{verbatim}\n<script type=\"text/javascript\">\n    Liferay.Loader.require(\"metal-clipboard/src/Clipboard\", \n    function(metalClipboardSrcClipboard) {\n        (function() {\n            new metalClipboardSrcClipboard.default();\n        })()\n    }, function(error) {\n        console.error(error)\n    });\n</script>\n\\end{verbatim}\n\nGreat! Now you know how to load ES2015 and Metal.js modules in AUI\nscripts.\n\n\\section{Related Topics}\\label{related-topics-88}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-external-javascript-libraries}{Using\n  External JavaScript Libraries}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/loading-amd-modules-in-liferay}{Loading\n  AMD Modules}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/loading-aui-es2015-and-metal-modules-with-aui-script}{Loading\n  AUI, ES2015, and Metal.js Modules Together with AUI Script}\n\\end{itemize}\n\n\\chapter{Loading AUI, ES2015, and Metal.js Modules Together with AUI\nScript}\\label{loading-aui-es2015-and-metal.js-modules-together-with-aui-script}\n\nYou may want to load an AUI module along with an ES2015 module or\nMetal.js module in an \\texttt{aui:script}. The \\texttt{aui:script} tag\ndoesn't support both the \\texttt{require} and \\texttt{use} attributes in\nthe same configuration. Not to worry though. You can use the\n\\texttt{aui:script}'s \\texttt{require} attribute to load the ES2015 and\nMetal.js modules, while loading the AUI module(s) with the\n\\texttt{AUI().use()} function within the script.\n\nFollow these steps to load your ES2015, Metal.js, and AUI modules\ntogether with \\texttt{\\textless{}aui:script\\textgreater{}}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the following declaration to your portlet's JSP:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}aui:script\\textgreater{}} tag and use the\n  \\texttt{require} attribute to load ES2015 and Metal.js modules, while\n  using the \\texttt{AUI().use()} function to load AUI modules, as shown\n  in the example below:\n\n\\begin{verbatim}\n<aui:script require=\"path-to/metal/module\">\n AUI().use(\n    'liferay-aui-module', \n    function(A) {\n        let var = pathToMetalModule.default;\n    }\n);\n</aui:script>\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! Now you know how to load all your modules with\n\\texttt{aui:script}.\n\n\\section{Related Topics}\\label{related-topics-89}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-external-javascript-libraries}{Using\n  External JavaScript Libraries}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/loading-amd-modules-in-liferay}{Loading\n  AMD Modules}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/loading-es2015-and-metal-modules-with-aui-script}{Loading\n  ES2015 and Metal.js Modules with AUI Script}\n\\end{itemize}\n\n\\chapter{Loading Bundled npm Modules in Your\nPortlets}\\label{loading-bundled-npm-modules-in-your-portlets}\n\nOnce you've\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-javascript-in-your-portlets}{exposed\nyour modules}, you can use them in your portlet via the\n\\texttt{aui:script} tag's \\texttt{require} attribute. You can load the\nnpm module in your portlet using the \\texttt{npmResolvedPackageName}\nvariable, which is available by default since 7.0. You can then create\nan alias to reference it in your portlet.\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Provide a \\texttt{Web-ContextPath} in your bundle's \\texttt{bnd.bnd}\n  file:\n\n\\begin{verbatim}\nWeb-ContextPath: /my-module-web\n\\end{verbatim}\n\\item\n  Make sure the\n  \\texttt{\\textless{}liferay-frontend:defineObjects\\ /\\textgreater{}}\n  tag is included in your portlet's \\texttt{init.jsp}. This makes the\n  \\texttt{npmResolvedPackageName} variable available, setting it to your\n  project module's resolved name. For instance, if your module is called\n  \\texttt{my-module} and is at version \\texttt{2.3.0}, the implicit\n  variable \\texttt{npmResolvedPackageName} is set to\n  \\texttt{my-module@2.3.0}. This lets you prefix any JS module\n  \\texttt{require} or soy component rendering with this variable.\n\\item\n  Use the \\texttt{npmResolvedPackageName} variable along with the\n  relative path to your JavaScript module file to create an alias in the\n  \\texttt{\\textless{}aui:script\\textgreater{}}'s \\texttt{require}\n  attribute. An example configuration is shown below:\n\n\\begin{verbatim}\n<aui:script \n  require='<%= npmResolvedPackageName + \n  \"/js/my-module.es as myModule\" %>'>\n</aui:script>\n\\end{verbatim}\n\\item\n  Use the alias inside the \\texttt{aui:script} to refer to your module:\n\n\\begin{verbatim}\n<aui:script \n  require='<%= npmResolvedPackageName + \n  \"/js/my-module.es as myModule\" %>'>\n\n    myModule.default();\n</aui:script>\n\\end{verbatim}\n\\end{enumerate}\n\nNow you know how to use an npm module's package!\n\n\\section{Related Topics}\\label{related-topics-90}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/obtaining-dependency-npm-package-descriptors}{Obtaining\n  an OSGi bundle's Dependency npm Package Descriptors}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-npm-bundler}{liferay-npm-bundler}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/how-the-liferay-npm-bundler-publishes-npm-packages}{How\n  liferay-npm-bundler Publishes npm Packages}\n\\end{itemize}\n\n\\chapter{The Info Framework}\\label{the-info-framework}\n\n7.0 introduces the Info Framework to provide a greater degree of\nextensibility for the most common needs of retrieving, processing and\ndisplaying any type of information. A key aspect of the Info Framework\nis that it makes no assumptions about the source of the data or how it\nis represented in memory (like which Java class the information is\nfrom). It can work with information stored in the database, created\nthrough some process in memory or retrieved from an external source.\n\nIn 7.0, the Info Framework still has limited functionality, but it sets\nthe foundation for obtaining and displaying information from external\nsystems or custom data models in Liferay. It also provides flexibility\nin customizing how any piece of information is displayed.\n\nThe Info Framework is lightweight. By design, it does not require all\nthe information to implement any specific interface. This means that you\ncan use it with any existing Java class, even if you don't have access\nto modify it. It comprises a collection of loosely coupled\nmicro-frameworks, so that developers can choose which features to use\nand ignore the others. This lowers the learning curve and minimizes work\nfor developers.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Liferay veterans may notice similarities between the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{Asset\nFramework} and Info Frameworks. The Info Framework can be considered a\ngeneralization of the Asset Framework and its design has considered lots\nof lessons learned from the Asset Framework. In particular, the Info\nFramework provides many similar characteristics (such as rendering of\ninformation) but with fewer requirements (such as having an\n\\texttt{AssetEntry} in the database). We have also made sure the two\nframeworks play well together so that when the Asset Framework is used,\nfor rendering an asset, the renderer is also available through the Info\nframework. The Info framework is not meant to be a full replacement for\nthe Asset Framework, but if the Asset Framework seems too complex for\nyour needs, the Info Framework might be a better fit for you.\n\n\\noindent\\hrulefill\n\n\\section{Using the Info Framework}\\label{using-the-info-framework}\n\nThe Info Framework helps generalize information handling. Custom\napplications can use it to make them more generic and extensible.\n\nSome of the out-of-the-box Liferay features use it to achieve that same\ngoal. In particular, Liferay DXP 7.2 uses it in two ways:\n\n\\begin{itemize}\n\\item\n  The Asset Publisher can display Assets from Information Lists defined\n  by the Info Framework.\n\\item\n  Display Pages, which previously only worked for an\n  \\texttt{AssetEntry}, can now leverage the \\texttt{InfoFramework} to\n  create display pages for any type of information that can be\n  represented by a Java class. Developers can add support for display\n  pages for various entities like Orders, Categories, and Events that\n  are not Assets.\n\\end{itemize}\n\nThere are currently two tools provided by the Info Framework:\nInformation Item Renderers and Information List Providers. You can\ncreate an Information List Provider to obtain a list of information\nitems from a source, or create an Information Item Renderer to provide a\ncustom renderer for any type of information. These two features can be\nused together or separately.\n\n\\section{List Providers}\\label{list-providers}\n\nInformation List Providers obtain a list of information items from a\nsource. To do this, a developer must implement the\n\\texttt{InfoListProvider} interface and provide the necessary logic for\nretrieving the information from its source. By providing an\nimplementation of the \\texttt{InfoListProvider} interface, developers\ncan provide programmatic retrieval of information of any type, as long\nas it can be represented through a Java class.\n\n\\texttt{InfoListProvider} has four methods to implement:\n\n\\texttt{getLabel()} provides the label that is displayed for this\nprovider in the UI of applications like the Asset Publisher.\n\n\\texttt{getInfoList()} provides the information list. This method has\ntwo variants: a plain list or a list with pagination and sorting.\n\n\\texttt{getInfoListCount()} provides total number of items. This is\nneeded for the paginated variant of \\texttt{getInfoList}.\n\nFor an example of how to create Information List providers, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-information-list-provider}{Creating\nInformation List Providers}.\n\n\\section{Item Renderers}\\label{item-renderers}\n\nDevelopers can create custom renderers for any type of information. To\ndo this, a developer must provide an implementation of the\n\\texttt{InfoItemRenderer} interface to provide programmatic rendering of\ninformation. It can be any kind of information as long as it can be\nrepresented through a Java class. You can create multiple renderers for\na single type of information.\n\nInternally, Liferay's Display Pages use this from the Content component.\nWhen it is added to a display page template, this component renders\nwhatever piece of information is shown through that template (whether it\nis Content in the strict sense or some other entity type). It is\nrendered by the first \\texttt{InfoItemRenderer} class registered that\nentity. Information Item Renderers will be leveraged further in future\nLiferay versions.\n\nTo create an Information Item Renderer you must create a class that\nimplements \\texttt{InfoItemRenderer} and registers it as a component.\nInside that class, you need a \\texttt{render()} method that contains\nyour logic. To learn about Information Item Renderers, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/custom-rendering-of-information-with-infoitemrenderer}{Creating\nInformation Item Renderers}.\n\n\\chapter{Creating an Information List\nProvider}\\label{creating-an-information-list-provider}\n\nTo demonstrate Information List Providers, follow the instructions below\nto implement an \\texttt{InfoListProvider} for the most viewed asset\nentries. In this case the list shows a list of \\texttt{AssetEntry}\ninstances. Since they already have their own renderer, they can appear\nin the Asset Publisher with no additional changes. If you create a\nprovider for a custom class, you must also render it.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{module}\n  named asset-entry-info-list-provider.\n\\item\n  Create a package inside the module named\n  \\texttt{com.liferay.docs.info.provider}.\n\\item\n  Inside the package, create a class named\n  \\texttt{AssetEntryInfoListProvider} that implements\n  \\texttt{InfoListProvider} and registers it as a component:\n\n\\begin{verbatim}\n@Component(service = InfoListProvider.class)\n  public class AssetEntryInfoListProvider implements InfoListProvider<AssetEntry> {\n\n}\n\\end{verbatim}\n\\item\n  Next, add the necessary \\texttt{@Reference} that you need for the\n  logic of retrieving assets to the bottom of the class.\n\n\\begin{verbatim}\n@Reference\nAssetEntryLocalService _assetEntryLocalService;\n\\end{verbatim}\n\\item\n  Then implement \\texttt{getInfoList} which returns just the list.\n\n\\begin{verbatim}\n@Override\npublic List<AssetEntry> getInfoList(\n InfoListProviderContext infoListProviderContext) {\n\n return _assetEntryLocalService.getTopViewedEntries(\n    new String[0], false, 0, 20);\n}\n\\end{verbatim}\n\n  Descending order and a maximum of 20 items to return is hardcoded.\n\\item\n  Now implement the second method, which provides greater control over\n  how items are returned to the provider.\n\n\\begin{verbatim}\n@Override\npublic List<AssetEntry> getInfoList(\n  InfoListProviderContext infoListProviderContext, Pagination pagination,\n  Sort sort) {\n\n  return _assetEntryLocalService.getTopViewedEntries(\n     new String[0], !sort.isReverse(), pagination.getStart(),\n     pagination.getEnd());\n}\n\\end{verbatim}\n\\item\n  Provide a method to get a full count of info list items.\n\n\\begin{verbatim}\n@Override\npublic int getInfoListCount(\nInfoListProviderContext infoListProviderContext) {\n\n    Company company = infoListProviderContext.getCompany();\n\n    return _assetEntryLocalService.getCompanyEntriesCount(\n    company.getCompanyId());\n}\n\\end{verbatim}\n\\item\n  Finally, add a method that provides a display label for the list.\n\n\\begin{verbatim}\n@Override\npublic String getLabel(Locale locale) {\n return \"Most Viewed Content\";\n }\n\\end{verbatim}\n\\end{enumerate}\n\nThe completed class should look like this:\n\n\\begin{verbatim}\n@Component(service = InfoListProvider.class)\n  public class AssetEntryInfoListProvider implements InfoListProvider<AssetEntry> {\n\n           @Override\n           public List<AssetEntry> getInfoList(\n             InfoListProviderContext infoListProviderContext) {\n\n             return _assetEntryLocalService.getTopViewedEntries(\n                new String[0], false, 0, 20);\n           }\n\n         @Override\n         public List<AssetEntry> getInfoList(\n           InfoListProviderContext infoListProviderContext, Pagination pagination,\n           Sort sort) {\n\n           return _assetEntryLocalService.getTopViewedEntries(\n              new String[0], !sort.isReverse(), pagination.getStart(),\n              pagination.getEnd());\n         }\n\n         @Override\n         public int getInfoListCount(\n         InfoListProviderContext infoListProviderContext) {\n\n             Company company = infoListProviderContext.getCompany();\n\n             return _assetEntryLocalService.getCompanyEntriesCount(\n             company.getCompanyId());\n         }\n\n           @Override\n           public String getLabel(Locale locale) {\n             return \"Most Viewed Content\";\n             }\n\n@Reference\nAssetEntryLocalService _assetEntryLocalService;\n}\n\\end{verbatim}\n\nThis class is now ready to go! If you deploy it, it shows the ``Most\nViewed Content'' in any Asset Publisher.\n\n\\section{Next steps}\\label{next-steps}\n\nThis example is pretty simplistic and probably not useful in real world\ncases. To begin with, you may want to scope the search to the current\nsite. You can also add more advanced filter criteria or provide a\nconfiguration for the provider using Liferay's configuration framework.\n\nAs mentioned, it is also possible to implement providers for custom\ntypes. The following code shows a partial example of a provider for a\ncustom \\texttt{MyOrder} class:\n\n\\begin{verbatim}\n@Component(service = InfoListProvider.class)\npublic class MyOrderProvider implements InfoListProvider<MyOrder> {\n\n    @Override\n    public List<MyOrder> getInfoList(\n        InfoListProviderContext infoListProviderContext, Pagination pagination,\n        Sort sort) {\n\n        return _myOrderLocalService.getOrders(\n            [...], !sort.isReverse(), pagination.getStart(),\n            pagination.getEnd());\n        }\n\n    [..]\n\n    @Reference\n    MyOrderLocalService _myOrderLocalService;\n\\end{verbatim}\n\n\\chapter{\\texorpdfstring{Custom rendering of information with\n\\texttt{InfoItemRenderer}}{Custom rendering of information with InfoItemRenderer}}\\label{custom-rendering-of-information-with-infoitemrenderer}\n\nTo demonstrate the \\texttt{InfoItemRenderer}, implement a class that can\nrender information provided through a custom class called\n\\texttt{MyOrder}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{module}\n  named \\texttt{my-order}.\n\\item\n  In \\texttt{my-order}, create a package named\n  \\texttt{com.liferay.docs.info.myorder}\n\\item\n  In the package, create a class that implements\n  \\texttt{InfoItemRenderer} and register it as a component.\n\n\\begin{verbatim}\n@Component(service = InfoItemRenderer.class)\npublic class MyOrderRenderer implements InfoItemRenderer<MyOrder> {\n    @Override\n    public void render(\n       MyOrder myOrder, HttpServletRequest httpServletRequest,\n       HttpServletResponse httpServletResponse) {\n\n    }\n}\n\\end{verbatim}\n\\item\n  Next you must add the logic for the \\texttt{render()} method.\n\n\\begin{verbatim}\n@Override\npublic void render(\n   MyOrder myOrder, HttpServletRequest httpServletRequest,\n   HttpServletResponse httpServletResponse) {\n\n   StringBundler sb = new StringBundler(3);\n\n   sb.append(\"<ul>\");\n   sb.append(\"<li>By: \" + myOrder.getBy());\n   sb.append(\"<li>When: \" + myOrder.getWhen());\n   sb.append(\"<li>Items: \" + myOrder.getItems());\n   sb.append(\"</ul>\");\n\n   try {\n      PrintWriter printWriter = httpServletResponse.getWriter();\n\n      printWriter.write(sb.toString());\n   }\n   catch (IOException ioe) {\n      throw new RuntimeException(ioe);\n   }\n}\n\\end{verbatim}\n\\end{enumerate}\n\nFor this example you rendered everything through a\n\\texttt{StringBundler}. In more complex cases, you would use JSPs or\nanother templating technology.\n\nThe renderer is ready for use! In 7.0, Info Item Renderers are not\nwidely used, but the usages and application will grow in future\nreleases.\n\n\\chapter{Using Providers with Custom\nApplications}\\label{using-providers-with-custom-applications}\n\nImagine a widget that can display lists of orders. You can use the Info\nFramework so that it shows any list of orders provided through\n\\texttt{InfoListProvider}.\n\nFirst you must obtain a list of all available providers for the desired\ntype, and then you would obtain a specific provider through that list.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The list of all available providers for \\texttt{MyOrder}, can be\n  obtained done by using the \\texttt{InfoListProviderTracker}:\n\n\\begin{verbatim}\n@Reference\nInfoListProviderTracker _infoListProviderTracker;\n\\end{verbatim}\n\n  Once a tracker is available, obtaining the list is as simple as\n  invoking \\texttt{getInfoListProviders()}:\n\n\\begin{verbatim}\n_infoListProviderTracker.getInfoListProviders(MyOrder.class);\n\\end{verbatim}\n\n  When the user selects an item from this list, you can store the\n  class's name.\n\\item\n  When a specific provider is desired it can be obtained through its\n  class name as follows:\n\n\\begin{verbatim}\n_infoListProviderTracker.getInfoListProvider(infoListProviderClassName);\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Leveraging renderers from a custom\napplication}\\label{leveraging-renderers-from-a-custom-application}\n\nUsing renderers from a custom application is almost identical to using\nproviders. Here is the equivalent code to what you've seen previously:\n\n\\begin{verbatim}\n_infoItemRendererTracker.getInfoItemRenderers(MyOrder.class.getName());\n\nString infoItemRendererClassName = MyOrderRenderer.class.getName();\n_infoItemRendererTracker.getInfoItemRenderer(infoItemRendererClassName);\n\\end{verbatim}\n\n\\chapter{Liferay Forms}\\label{liferay-forms}\n\nThe Liferay Forms application is a full-featured form building tool for\ncollecting data. There's lots of built-in functionality, and for the\npieces you're missing, there's lots of extensibility.\n\n\\chapter{Form Serialization with the DDM IO\nAPI}\\label{form-serialization-with-the-ddm-io-api}\n\nWhen a form creator saves a form in the Liferay Forms application, the\nForm object is \\emph{serialized} (converted) into JSON for storage in\nthe Liferay DXP database. That's the default behavior; if you need the\nform in a different format, you must write your own serialization and\ndeserialization code. Why would you want to do that? Maybe you think\nJSON storage is not secure, or you have another tool that can consume\nthe form if it's in YAML. Whatever your reasons, the form can be stored\nin any format as long as you write a \\texttt{DDMFormSerializer} and its\ncorresponding \\texttt{DDMFormDeserializer} with the proper logic.\n\nFirst consider what form data looks like by default, in JSON. A simple\nform, \\emph{My Form}, with one text field, \\emph{Full Name}, is first\ncreated as a \\texttt{DDMForm} Java object, then \\emph{serialized} into\nJSON for storage in the Liferay DXP database when saved.\n\n\\begin{verbatim}\n{\n    \"availableLanguageIds\":[\"en_US\"],\n    \"successPage\":{\"body\":{},\n    \"title\":{},\n    \"enabled\":false},\n    \"defaultLanguageId\":\"en_US\",\n    \"fields\":[{\n        \"autocomplete\":false,\n        \"ddmDataProviderInstanceId\":\"[]\",\n        \"dataType\":\"string\",\n        \"predefinedValue\":{\"en_US\":\"\"},\n        \"tooltip\":{\"en_US\":\"\"},\n        \"readOnly\":false,\n        \"label\":{\"en_US\":\"Full Name\"},\n        \"type\":\"text\",\n        \"required\":false,\n        \"showLabel\":true,\n        \"displayStyle\":\"singleline\",\n        \"fieldNamespace\":\"\",\n        \"indexType\":\"keyword\",\n        \"visibilityExpression\":\"\",\n        \"ddmDataProviderInstanceOutput\":\"[]\",\n        \"repeatable\":false,\n        \"name\":\"FullName\",\n        \"options\":[{\"label\":{\"en_US\":\"Option\"},\"value\":\"Option\"}],\n        \"localizable\":true,\n        \"tip\":{\"en_US\":\"\"},\n        \"placeholder\":{\"en_US\":\"\"},\n        \"dataSourceType\":\"\",\n        \"validation\":{\"expression\":\"\",\"errorMessage\":\"\"}\n    }]\n}\n\\end{verbatim}\n\nFrom its initial state as a \\texttt{DDMForm} Java object, the form is\n\\emph{serialized} into JSON format, and upon retrieval from the\ndatabase, it's \\emph{deserialized}: the JSON object representing the\nform is translated back into a \\texttt{DDMForm} Java object, with all\nits requisite fields. For example, the JSON for the above example holds\neach form field in the \\texttt{fields} attribute. To translate this back\ninto the necessary \\texttt{DDMForm} object, first parse the data\ncontained in the JSON object into an actual form field using your\ndeserialization logic. Here's the logic from\n\\texttt{DDMFormJsonDeserializer} that parses the JSON \\texttt{\"fields\"}\nelement into a list of \\texttt{DDMFormFields}:\n\n\\begin{verbatim}\nprotected List<DDMFormField> getDDMFormFields(JSONArray jsonArray)\n    throws PortalException {\n\n    List<DDMFormField> ddmFormFields = new ArrayList<>();\n\n    for (int i = 0; i < jsonArray.length(); i++) {\n        DDMFormField ddmFormField = getDDMFormField(\n            jsonArray.getJSONObject(i));\n\n        ddmFormFields.add(ddmFormField);\n    }\n\n    return ddmFormFields;\n}\n\\end{verbatim}\n\nNow calling \\texttt{DDMForm.setDDMFormFields(ddmFormFields)} in the\ndeserializer completes the translation process from the JSON array back\nto a \\texttt{DDMFormField} object that the \\texttt{DDMForm} needs.\n\nIf you'd like to store forms in a different format, provide custom\n\\emph{serialization} and \\emph{deserialization} functionality.\n\n\\chapter{Serializing Forms}\\label{serializing-forms}\n\nThe DDM IO API serializes and deserializes forms using a\nrequest/response structure. The example here creates a serializer for\nsaving form data in \\href{https://yaml.org}{YAML} format. The same\nprinciples shown here apply to writing a deserializer.\n\nTo serialize form data into YAML:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a class that implements \\texttt{DDMFormSerializer}:\n\n\\begin{verbatim}\n@Component(immediate = true, property = \"ddm.form.serializer.type=yaml\") public\nclass DDMFormYamlSerializer implements DDMFormSerializer { .....  }\n\\end{verbatim}\n\n  The property \\texttt{ddm.form.serializer.type=yaml} marks the\n  Component so that \\texttt{DDMFormSerializerTracker} can find the YAML\n  serializer.\n\\item\n  Add the serializing logic to the overridden \\texttt{serialize} method.\n  It takes a \\texttt{DDMFormSerializerSerializeRequest} and returns a\n  \\texttt{DDMFormSerializerSerializeResponse} with the serialized string\n  in it.\n\n\\begin{verbatim}\n@Override public DDMFormSerializerSerializeResponse serialize(\nDDMFormSerializerSerializeRequest ddmFormSerializerSerializeRequest) {\n\n        DDMForm ddmForm = ddmFormSerializerSerializeRequest.getDDMForm(); \n\n        ...YOUR CODE FOR BUILDING A YAML OBJECT GOES HERE ...  \n\n        DDMFormSerializerSerializeResponse.Builder builder = \n            DDMFormSerializerSerializeResponse.Builder.newBuilder(yamlObject.toString());\n\n        return builder.build(); }\n\\end{verbatim}\n\\end{enumerate}\n\nThis is what you need to create your serializer. Of course,\n\\texttt{YOUR\\ CODE\\ FOR\\ BUILDING\\ A\\ YAML\\ OBJECT\\ GOES\\ HERE} requires\nsome explanation. While you can do whatever you want here, there are\nseveral things you really ought to do:\n\n\\textbf{Add the available Language IDs:} Since you have the\n\\texttt{DDMForm} object from the request, call\n\\texttt{ddmForm.getAvailableLocales()}.\n\n\\textbf{Add the default Language ID:} Get this from the \\texttt{DDMForm}\nobject by calling \\texttt{ddmForm.getDefaultLocale()}.\n\n\\textbf{Add the Form Fields:} Get these from the \\texttt{DDMForm} object\nby calling \\texttt{ddmForm.getDDMFormFields()}.\n\n\\textbf{Add any Form Rules:} Get them form the \\texttt{DDMForm} object\nwith \\texttt{ddmForm.getDDMFormRules()}.\n\n\\textbf{Add Success Page Settings:} Get these from the \\texttt{DDMForm}\nwith \\texttt{ddmForm.getDDMFormSuccessPageSettings()}.\n\nAll these are done in the default form serializer,\n\\texttt{DDMFormJSONSerializer}.\n\nIf you have the Liferay DXP source code, you can find the default\nserializer in\n\n\\begin{verbatim}\nmodules/apps/dynamic-data-mapping/dynamic-data-mapping-io/src/main/java/com/liferay/dynamic/data/mapping/io/internal/DDMFormJSONSerializer.java\n\\end{verbatim}\n\nYou didn't create serialization code for no reason. You'll want to call\nit from somewhere.\n\n\\section{Calling the Serializer}\\label{calling-the-serializer}\n\nTo get properly serialized form content, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get the serializer from the \\texttt{DDMFormSerializerTracker}, passing\n  in the value of the \\texttt{ddm.form.serializer.type} property.\n\\item\n  Construct a \\texttt{DDMFormSerializerSerializeRequest} object using\n  its nested static \\texttt{Builder} class.\n\\item\n  Call the \\texttt{serialize} method you wrote in the last section to\n  create the \\texttt{DDMFormSerializerSerializeResponse}, passing the\n  \\texttt{DDMFormSerializerSerializeRequest} object, via a call to the\n  \\texttt{Builder}'s \\texttt{build} method.\n\\item\n  Get the serialized form content from the\n  \\texttt{DDMFormSerializerSerializeResponse} by calling its\n  \\texttt{getContent} method.\n\\end{enumerate}\n\nHere's a code example:\n\n\\begin{verbatim}\nDDMFormSerializer ddmFormSerializer =\nddmFormSerializerTracker.getDDMFormSerializer(\"yaml\");\n\nDDMFormSerializerSerializeRequest.Builder builder =\nDDMFormSerializerSerializeRequest.Builder.newBuilder(ddmForm);\n\nDDMFormSerializerSerializeResponse ddmFormSerializerSerializeResponse =\nddmFormSerializer.serialize(builder.build());\n\nddmFormSerializerSerializeResponse.getContent();\n\\end{verbatim}\n\nYou can create a serializer for any format that can be saved in the\ndatabase as a String. Once you create the serializer, make it the\ndefault by changing the storage format in the Form's Settings menu.\n\n\\chapter{Localization}\\label{localization}\n\nIf you're writing a Liferay application, you're probably a genius who is\nalso really cool, which means your application will be used throughout\nthe entire world. At least, if its messages can be translated into their\nlanguage, it will. Thankfully, Liferay facilitates creating and using\nmessage translations and adapting to cultural conventions for user names\nand initials.\n\nYou can leverage Liferay's localization framework or use standard\nresource bundles to localize your app. The localization framework uses\nproperties files (the same as any resource bundle) but leverages a\ndefault properties file called \\texttt{Language.properties} to propagate\nmessages (language keys) to properties files for all your locales. For\nexample, when you add a new message to the \\texttt{Language.properties}\nfile and run Language Builder, it propagates the message to your locale\nfiles. All you must do is translate the message in each locale file,\nmanually or automatically using Language Builder.\n\nLanguage Builder integrates the Microsoft Text Translator API to\ntranslate each locale file's messages from your default locale to the\nrespective locale. A machine's translation is no substitute for a\nhuman's, of course, but the automatic translation gives you a base to\nwork from.\n\nIt's common to use the same messages in multiple apps. Liferay DXP\nprovides these message sharing features:\n\n\\begin{itemize}\n\\item\n  Liferay DXP's messages (and their translations) are available for all\n  your apps to use. JSP tags such as\n  \\texttt{\\textless{}liferay-ui:message\\ ...\\ /\\textgreater{}} let you\n  use all Liferay DXP messages.\n\\item\n  Language modules are easy to use in all your apps. They're great for\n  centralizing messages in all your locales.\n\\end{itemize}\n\nLastly, Liferay DXP provides settings for adapting your app to cultures:\n\n\\begin{itemize}\n\\item\n  Naming conventions for users\n\\item\n  Initial conventions for user avatars\n\\item\n  Text direction settings (left-to-right or right-to-left)\n\\end{itemize}\n\nLocalization is important to all site users. Liferay helps you get it\nright! Start with localizing your application using Liferay's\nlocalization framework.\n\n\\chapter{Localizing Your Application}\\label{localizing-your-application}\n\nLiferay's localization framework helps you create and use localized\nmessages in minutes. You create your messages in a default properties\nfile called \\texttt{Language.properties} and localize them in properties\nfiles that use the convention \\texttt{Language\\_xx.properties}, where\n\\texttt{xx} is the locale code. After deploying your app, the messages\nare available to your templates. Liferay's JSP tags, such as\n\\texttt{\\textless{}liferay-ui:message\\ .../\\textgreater{}} display them\nin the user's current locale automatically, without requiring you to\naccess \\texttt{ResourceBundle} or \\texttt{Locale} objects explicitly.\nHere are the steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Create a default language properties file called\n  \\texttt{Language.properties} in your project's resource bundle folder.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n Project Type              | Resource Bundle Folder |\n ------------------------- | ---------------------- |\n Bean Portlet              | `src/main/resources/content/` |\n JSF Portlet               | `src/main/resources/` |\n Liferay MVC Portlet       | `src/main/resources/content/` |\n PortletMVC4Spring Portlet | `src/main/java/resources/content/` |\n Angular Widget            | `features/localization/` |\n React Widget              | `features/localization/` |\n Vue.js Widget             | `features/localization/` |\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Specify your language properties (language keys) using key/value\n  pairs. For example, create a friendly greeting.\n\n\\begin{verbatim}\nhowdy-partner=Howdy, Partner!\n\\end{verbatim}\n\\item\n  Configure your app's resource bundle and the locales you're\n  supporting. The locales your Liferay DXP instance supports are\n  specified in its\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\n  properties} file (here are Liferay DXP's\n  \\href{(https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Languages\\%20and\\%20Time\\%20Zones)}{default\n  locales}. For example, these configurations support translations for\n  English and Spanish locales:\n\n  \\textbf{\\texttt{@PortletConfiguration} class annotation:} Can be used\n  in Portlet 3.0 portlets such as Bean Portlets.\n\n\\begin{verbatim}\n@PortletConfiguration (\n    ...\n    resourceBundle=\"content.Language\",\n    supportedLocales = {\"en\", \"es\"}\n)\n\\end{verbatim}\n\n  \\textbf{Portlet descriptor \\texttt{portlet.xml}:} Can be used in any\n  portlet WAR.\n\n\\begin{verbatim}\n<portlet>\n...\n<supported-locale>en</supported-locale>\n<supported-locale>es</supported-locale>\n<resource-bundle>content.Language</resource-bundle>\n...\n</portlet>\n\\end{verbatim}\n\n  \\textbf{\\texttt{@Component} class annotation:} Can be used in a\n  portlet module such as a Liferay MVC Portlet.\n\n\\begin{verbatim}\n@Component (\n    ...\n    property = {\n        ...\n        \"javax.portlet.supported-locale=en\",\n        \"javax.portlet.supported-locale=es\",\n        \"javax.portlet.resource-bundle=content.Language\"\n    }\n)\n\\end{verbatim}\n\\item\n  Create language properties for a locale. For demonstration purposes,\n  create one manually.\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/automatically-generating-translations}{Automatically\n  generating translations} is discussed later.\n\n  For example, create a Spanish translation by copying\n  \\texttt{Language.properties} to \\texttt{Language\\_es.properties} and\n  translating the property values to Spanish.\n\n\\begin{verbatim}\nhowdy-partner=Hola, Compañero!\n\\end{verbatim}\n\\item\n  In your front-end template code, use the language property. For\n  example, a JSP could use the \\texttt{howdy-partner} property via the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/liferay-ui/message.html}{\\texttt{\\textless{}liferay-ui:message\\ /\\textgreater{}}}\n  tag.\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\" %> \n...\n<liferay-ui:message key=\"howdy-partner\" />\n...\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Tip:** The\n [`liferay-ui`](https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/liferay-ui/tld-summary.html)\n tag library has several tags (e.g., `alert`, `error`, and `message`) that\n accept language keys. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{5}\n\\tightlist\n\\item\n  Deploy your application and view it in different locales. For example,\n  you could view the app locally in Spanish by specifying the\n  \\texttt{es} locale code in the URL (e.g.,\n  \\texttt{http://localhost:8080/es/...}).\n\\end{enumerate}\n\nCongratulations on a great start to localizing your application!\n\nNext, you can explore\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/automatically-generating-translations}{generating\ntranslations automatically} or\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-language-module}{create\na language module} for using language keys across applications.\n\n\\section{Related Topics}\\label{related-topics-91}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/automatically-generating-translations}{Automatically\nGenerating Translations}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module}{Using\nLanguage Modules}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f//docs/7-1/tutorials/-/knowledge_base/t/using-liferays-language-settings}{Using\nLiferay DXP's Language Settings}\n\n\\chapter{Using Liferay's Localization\nSettings}\\label{using-liferays-localization-settings}\n\nYou can customize a given locale's default language settings by\noverriding the properties that control those settings. For instructions\non this, see\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys}{Overriding\nLanguage Keys}. Here, you'll learn which properties correspond to common\nlanguage settings.\n\nSo what all can be customized? This is an excellent question! Consider\nthese examples:\n\n\\begin{itemize}\n\\item\n  In the add and edit user forms, you can configure the name fields that\n  are displayed and the field values available in select fields. For\n  example, you can leave out the middle name field or alter the prefix\n  selections.\n\\item\n  You can also control the directionality of content and messages (left\n  to right or right to left).\n\\end{itemize}\n\nLanguage properties exist in \\texttt{Language\\_xx.properties} files,\nwhere \\texttt{xx} represents the locale. For example,\n\\texttt{Language\\_es.properties} contains the properties for Spanish,\n\\texttt{Language\\_en.properties} contains the properties for English,\nand so on.\n\nThe default (core) language properties are in\n\\texttt{Language.properties}. \\textbf{Do not edit this file.} You can,\nhowever, open it to view the default language settings. There are two\nways to do so:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From Liferay Portal's source code. Navigate to\n\n\\begin{verbatim}\nliferay-portal/portal-impl/src/content/Language.properties\n\\end{verbatim}\n\\item\n  From a bundle's \\texttt{portal-impl.jar}.\n\n\\begin{verbatim}\n[Liferay Home]/tomcat-[version]/webapps/ROOT/WEB-INF/lib/portal-impl.jar\n\\end{verbatim}\n\n  Open the \\texttt{content} folder in the JAR to find the language\n  files.\n\\end{enumerate}\n\nThe first section in the core \\texttt{Language.properties} file is\nlabeled \\emph{Language Settings} and contains language properties that\nbegin with \\texttt{lang}:\n\n\\begin{verbatim}\n##\n## Language Settings\n##\n\nlang.dir=ltr\nlang.line.begin=left\nlang.line.end=right\nlang.user.default.portrait=initials\nlang.user.initials.field.names=first-name,last-name\nlang.user.name.field.names=prefix,first-name,middle-name,last-name,suffix\nlang.user.name.prefix.values=Dr,Mr,Ms,Mrs\nlang.user.name.required.field.names=last-name\nlang.user.name.suffix.values=II,III,IV,Jr,Phd,Sr\n\\end{verbatim}\n\nTo use the language settings mentioned here, you must have a module. See\nthe articles on\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys}{overriding\nlanguage keys} to set up a module with the following characteristics:\n\n\\begin{itemize}\n\\item\n  Contains an implementation of \\texttt{ResourceBundle} that is\n  registered in the OSGi runtime.\n\\item\n  Contains a \\texttt{Language\\_xx.properties} file for the locale whose\n  properties you want to override.\n\\end{itemize}\n\n\\section{Localizing User Names}\\label{localizing-user-names}\n\nNaming conventions can differ between locales. For example, users in\nsome locales have more than one last name. You can therefore change the\nuser name properties to fit the given locale. The properties for\nchanging user name settings begin with \\texttt{lang.user.name}.\n\nUser name fields are configurable in the following ways:\n\n\\begin{itemize}\n\\item\n  Remove certain name fields and make others appear more than once. Some\n  locales need more than one last name, for example.\n\n\\begin{verbatim}\nlang.user.name.field.names=prefix,first-name,middle-name,last-name,suffix\n\\end{verbatim}\n\\item\n  Change the prefix and suffix values for a locale.\n\n\\begin{verbatim}\nlang.user.name.prefix.values=Dr,Mr,Ms,Mrs\nlang.user.name.suffix.values=II,III,IV,Jr,Phd,Sr\n\\end{verbatim}\n\\item\n  Specify which fields are required.\n\n\\begin{verbatim}\nlang.user.name.required.field.names=last-name\n\\end{verbatim}\n\\end{itemize}\n\nA user's first name is mandatory. Because of this, take these two points\ninto consideration when configuring a locale's user name settings:\n\n\\begin{itemize}\n\\item\n  The \\texttt{first-name} field can't be removed from the field names\n  list.\n\n\\begin{verbatim}\nlang.user.name.field.names=prefix,first-name,middle-name,last-name,suffix\n\\end{verbatim}\n\\item\n  Because a first name is required, it's always implicitly included in\n  the \\texttt{required\\ field\\ names} property:\n\n\\begin{verbatim}\nlang.user.name.required.field.names=last-name\n\\end{verbatim}\n\n  Therefore, any fields you enter here are \\emph{in addition to} the\n  first name field. Last name is required by default, but you can\n  disable it by deleting its value from the property:\n\n\\begin{verbatim}\nlang.user.name.required.field.names=\n\\end{verbatim}\n\n  In that case, only a first name would be required.\n\\end{itemize}\n\nFor most of the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Languages\\%20and\\%20Time\\%20Zones}{locales\nenabled by default}, the user name properties are tailored to each\nlocale. The default locales are specified by the\n\\texttt{locales.enabled} property:\n\n\\begin{verbatim}\nlocales.enabled=ar_SA,ca_ES,zh_CN,nl_NL,en_US,fi_FI,fr_FR,de_DE,hu_HU,ja_JP,pt_BR,es_ES,sv_SE\n\\end{verbatim}\n\nFor example, here are the English (\\texttt{Language\\_en.properties})\nproperties for setting user name fields:\n\n\\begin{verbatim}\nlang.user.name.field.names=prefix,first-name,middle-name,last-name,suffix\nlang.user.name.prefix.values=Dr,Mr,Ms,Mrs\nlang.user.name.required.field.names=last-name\nlang.user.name.suffix.values=II,III,IV,Jr,Phd,Sr\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/english-user-name-fields.png}\n\\caption{The user name settings impact the appearance of user\ninformation and forms.}\n\\end{figure}\n\nCompare those to the Spanish (\\texttt{Language\\_es.properties})\nsettings:\n\n\\begin{verbatim}\nlang.user.name.field.names=prefix,first-name,last-name\nlang.user.name.prefix.values=Sr,Sra,Sta,Dr,Dra\nlang.user.name.required.field.names=last-name\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/spanish-user-name-fields.png}\n\\caption{The Spanish user name settings omit the suffix and middle name\nfields.}\n\\end{figure}\n\nThe biggest difference between the English and Spanish form fields is\nthat the middle name and suffix fields are omitted in the Spanish\nconfiguration. Other differences include the specific prefix values.\n¡Muy excelente!\n\n\\section{Identifying User Initials}\\label{identifying-user-initials}\n\nThe default avatar displays a user's initials. Some cultures use\ninitials differently, so there's a way to configure them in the\nappropriate \\texttt{Language\\_xx.properties} file.\n\n\\begin{verbatim}\nlang.user.default.portrait=initials\nlang.user.initials.field.names=first-name,last-name\n\\end{verbatim}\n\nThe \\texttt{lang.user.default.portrait} property sets the type of\nportrait to use for users. This can be set to \\texttt{initials} or\n\\texttt{image}. If set to \\texttt{image}, the default images defined by\nthe \\texttt{image.default.user.female.portrait} or\n\\texttt{image.default.user.male.portrait} properties residing in the\n\\texttt{portal.properties} file are used. Therefore, the\n\\texttt{lang.user.initials.field.names} property is ignored.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/initials-avatar.png}\n\\caption{The user's initials are displayed for their avatar by default.}\n\\end{figure}\n\nIf you're leveraging the user's initials for the default avatar, you can\nuse the \\texttt{lang.user.initials.field.names} property to organize how\nthe initials are displayed. Valid values for this property include\n\\texttt{first-name}, \\texttt{middle-name}, and \\texttt{last-name}, in\nany order.\n\n\\section{Right to Left or Left to\nRight?}\\label{right-to-left-or-left-to-right}\n\nThe first three properties in the core \\texttt{Language.properties}\nfile's Language Settings section change the display direction of a\nlanguage's characters. Most languages read from left to right, but some\nlanguages read from right to left (e.g., Arabic, Hebrew, and Persian).\n\nHere are the relevant language properties for a right-to-left language:\n\n\\begin{verbatim}\nlang.dir=rtl\nlang.line.begin=right\nlang.line.end=left\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can prevent specific CSS rules from transforming\n(flipping) with the \\texttt{/*\\ @noflip\\ */} decoration. Place the\ndecoration to the left of the CSS rule to apply it. For example, this\nrule gives a left margin of \\texttt{20em} to the \\texttt{body} no matter\nif the selected language is LTR or RTL:\n\n\\begin{verbatim}\n/* @noflip */ body {\n margin-left: 20em;\n}\n\\end{verbatim}\n\nYou can also use the \\texttt{.rtl} CSS selector for rules that\nexclusively apply to RTL languages.\n\n\\noindent\\hrulefill\n\n\\section{Related Topics}\\label{related-topics-92}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys}{Overriding\nLanguage Keys}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application}{Localizing\nYour Application}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-language-module}{Creating\na Language Module}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module}{Using\na Language Module}\n\n\\chapter{Creating a Language Module}\\label{creating-a-language-module}\n\nYou might have an application with multiple modules that provide the\nview layer. These modules are often called web modules. For example,\nthis application contains three such modules:\n\n\\begin{verbatim}\nmy-application/\nmy-application-web/\nmy-admin-application-web/\nmy-application-content-web/\nmy-application-api/\nmy-application-service/\n\\end{verbatim}\n\nEach of these modules can have language keys and translations to\nmaintain, likely resulting in duplicate keys. You don't want to end up\nwith different values for the same key, and you don't want to maintain\nlanguage keys in multiple places. In this case, you should create a\nsingle module---a language module---for housing all your app's language\nkeys.\n\nFollow these steps to create a language module:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the root project folder (the one that holds your service, API, and\n  web modules),\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{create\n  a new module} to hold your app's language keys.\n\\item\n  In the language module, create a \\texttt{src/main/resources/content}\n  folder. In this folder, create a \\texttt{Language.properties} file for\n  your app's default language keys. For example, such a file might look\n  like this:\n\n\\begin{verbatim}\nmy-app-title=My Application\nadd-entity=Add Entity\n\\end{verbatim}\n\\item\n  Create any translations you want in additional language properties\n  files, appending the locale's ID to the file name. For example, a file\n  \\texttt{Language\\_es.properties} holds Spanish (\\texttt{es})\n  translations and could contain something like this:\n\n\\begin{verbatim}\nmy-app-title=Mi Aplicación\nadd-entity=Añadir Entity\n\\end{verbatim}\n\n  Here's the folder structure of an example language module called\n  \\texttt{my-application-lang}. This module contains the app's default\n  language keys (\\texttt{Language.properties}) and a Spanish translation\n  (\\texttt{Language\\_es.properties}):\n\n\\begin{verbatim}\nmy-application-lang/\n    bnd.bnd\n    src/\n        main/\n            resources/\n                content/\n                    Language.properties\n                    Language_es.properties\n                    ...\n\\end{verbatim}\n\\end{enumerate}\n\nOn building the language module, Liferay DXP's\n\\texttt{ResourceBundleLoaderAnalyzerPlugin} detects the module's\n\\texttt{Language.properties} file and adds a resource bundle\n\\href{http://blog.osgi.org/2015/12/using-requirements-and-capabilities.html}{capability}\nto the module. A capability is a contract a module declares to the OSGi\nframework. Capabilities let you associate services with modules that\nprovide them. In this case, Liferay DXP registers a\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ResourceBundleLoader.html}{ResourceBundleLoader}\nservice for the resource bundle capability.\n\n\\section{Related Topics}\\label{related-topics-93}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys}{Overriding\nLanguage Keys}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application}{Localizing\nYour Application}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-liferays-localization-settings}{Using\nLiferay's Localization Settings}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module}{Using\na Language Module}\n\n\\chapter{Using a Language Module}\\label{using-a-language-module}\n\nA module or traditional Liferay plugin can use a resource bundle from\nanother module and optionally include its own resource bundle. OSGi\nmanifest headers \\texttt{Require-Capability} and\n\\texttt{Provide-Capability} make this possible, and it's especially easy\nin modules generated from Liferay project templates. Instructions for\nusing a language module are divided into these environments:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[using-a-language-module-from-a-module]{Using a Language\n  Module from a Module}\n\\item\n  \\hyperref[using-a-language-module-from-a-traditional-plugin]{Using a\n  Language Module from a Traditional Plugin}\n\\end{itemize}\n\nIf you're using bnd with Maven or Gradle, you need only specify\nLiferay's \\texttt{-liferay-aggregate-resource-bundle:} bnd\ninstruction---at build time, Liferay's bnd plugin converts the\ninstruction to \\texttt{Require-Capability} and\n\\texttt{Provide-Capability} parameters automatically. Both approaches\nare demonstrated here.\n\n\\section{Using a Language Module from a\nModule}\\label{using-a-language-module-from-a-module}\n\nModules generated from Liferay project templates have a Liferay bnd\nbuild time instruction called\n\\texttt{-liferay-aggregate-resource-bundles}. It lets you use other\nresource bundles (including their language keys) along with your own.\n\nHere's how to use this bnd instruction:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your module's \\texttt{bnd.bnd} file.\n\\item\n  Add the \\texttt{-liferay-aggregate-resource-bundles:} bnd instruction\n  and assign it the bundle symbolic names of modules whose resource\n  bundles you wish to aggregate with the current module's resource\n  bundle:\n\n\\begin{verbatim}\n-liferay-aggregate-resource-bundles: \\\n    [bundle.symbolic.name1],\\\n    [bundle.symbolic.name2]\n\\end{verbatim}\n\\end{enumerate}\n\nFor example, a module that uses resource bundles from modules\n\\texttt{com.liferay.docs.l10n.myapp1.lang} and\n\\texttt{com.liferay.docs.l10n.myapp2.lang} would set this in its\n\\texttt{bnd.bnd} file:\n\n\\begin{verbatim}\n-liferay-aggregate-resource-bundles: \\\n    com.liferay.docs.l10n.myapp1.lang,\\\n    com.liferay.docs.l10n.myapp2.lang\n\\end{verbatim}\n\nThe current module's resource bundle is prioritized over those of the\nlisted modules.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Shared Language Key\n\\href{/docs/7-2/reference/-/knowledge_base/r/shared-language-keys}{sample\nproject} is a working example that demonstrates aggregating resource\nbundles. You can deploy it in Gradle, Maven, and Liferay Workspace build\nenvironments.\n\n\\noindent\\hrulefill\n\nAt build time, Liferay's bnd plugin converts the bnd instruction to\n\\texttt{Require-Capability} and \\texttt{Provide-Capability} parameters\nautomatically. In traditional Liferay plugins, you must specify the\nparameters manually.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can always specify the \\texttt{Require-Capability}\nand \\texttt{Provide-\\ \\ Capability} OSGi manifest headers manually, as\nthe next section demonstrates.\n\n\\noindent\\hrulefill\n\n\\section{Using a Language Module from a Traditional\nPlugin}\\label{using-a-language-module-from-a-traditional-plugin}\n\nTo use a language module from a traditional Liferay plugin you must\nspecify the language module via the \\texttt{Require-Capability} and\n\\texttt{Provide-Capability} OSGi manifest headers in the plugin's\n\\texttt{liferay-plugin-package.properties} file.\n\nFollow these steps to configure your traditional plugin to use a\nlanguage module:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the plugin's \\texttt{liferay-plugin-package.properties} file and\n  add a \\texttt{Require-Capability} header that filters on the language\n  module's resource bundle capability. For example, if the language\n  module's symbolic name is \\texttt{myapp.lang}, specify the requirement\n  like this:\n\n\\begin{verbatim}\nRequire-Capability: liferay.resource.bundle;filter:=\"(bundle.symbolic.name=myapp.lang)\"\n\\end{verbatim}\n\\item\n  In the same \\texttt{liferay-plugin-package.properties} file, add a\n  \\texttt{Provide-Capability} header that adds the language module's\n  resource bundle as this plugin's (the \\texttt{myapp.web} plugin) own\n  resource bundle:\n\n\\begin{verbatim}\nProvide-Capability:\\\nliferay.resource.bundle;resource.bundle.base.name=\"content.Language\",\\\nliferay.resource.bundle;resource.bundle.aggregate:String=\"(bundle.symbolic.name=myapp.lang)\";bundle.symbolic.name=myapp.web;resource.bundle.base.name=\"content.Language\";service.ranking:Long=\"4\";\\\nservlet.context.name=myapp-web\n\\end{verbatim}\n\\end{enumerate}\n\nIn this case, the \\texttt{myapp.web} plugin solely uses the language\nmodule's resource bundle---the resource bundle aggregate only includes\nlanguage module \\texttt{myapp.lang}.\n\nAggregating resource bundles comes into play when you want to use a\nlanguage module's resource bundle in addition to your plugin's resource\nbundle. These instructions show you how to do this, while prioritizing\nyour current plugin's resource bundle over the language module resource\nbundle. In this way, the language module's language keys compliment your\nplugin's language keys.\n\nFor example, a portlet whose bundle symbolic name is \\texttt{myapp.web}\nuses keys from the language module \\texttt{myapp.lang} in addition to\nits own. The portlet's \\texttt{Provide-Capability} and\n\\texttt{Web-ContextPath} OSGi headers accomplish this.\n\n\\begin{verbatim}\nProvide-Capability:\\\nliferay.resource.bundle;resource.bundle.base.name=\"content.Language\",\\\nliferay.resource.bundle;resource.bundle.aggregate:String=\"(bundle.symbolic.name=myapp.web),(bundle.symbolic.name=myapp.lang)\";bundle.symbolic.name=myapp.web;resource.bundle.base.name=\"content.Language\";service.ranking:Long=\"4\";\\\nservlet.context.name=myapp-web\n\\end{verbatim}\n\nThe example \\texttt{Provide-Capability} header has two parts:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\texttt{liferay.resource.bundle;resource.bundle.base.name=\"content.Language\"}\n  declares that the module provides a resource bundle whose base name is\n  \\texttt{content.language}.\n\\item\n  The\n  \\texttt{liferay.resource.bundle;resource.bundle.aggregate:String=...}\n  directive specifies the list of bundles whose resource bundles are\n  aggregated, the target bundle, the target bundle's resource bundle\n  name, and this service's ranking:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\"(bundle.symbolic.name=myapp.web),(bundle.symbolic.name=myapp.lang)\"}:\n    The service aggregates resource bundles from bundles\n    \\texttt{bundle.symbolic.name=myapp.web} (the current module) and\n    \\texttt{bundle.symbolic.name=myapp.lang}. Aggregate as many bundles\n    as desired. Listed bundles are prioritized in descending order.\n  \\item\n    \\texttt{bundle.symbolic.name=myapp.web;resource.bundle.base.name=\"content.Language\"}:\n    Override the \\texttt{myapp.web} bundle's resource bundle named\n    \\texttt{content.Language}.\n  \\item\n    \\texttt{service.ranking:Long=\"4\"}: The resource bundle's service\n    ranking is \\texttt{4}. The OSGi framework applies this service if it\n    outranks all other resource bundle services that target\n    \\texttt{myapp.web}'s \\texttt{content.Language} resource bundle.\n  \\item\n    \\texttt{servlet.context.name=myapp-web}: The target resource bundle\n    is in servlet context \\texttt{myapp-web}.\n  \\end{itemize}\n\\end{enumerate}\n\nNow the language keys from the aggregated resource bundles compliment\nyour plugin's language keys.\n\n\\section{Related Topics}\\label{related-topics-94}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application}{Localizing\nYour Application}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys}{Overriding\nLanguage Keys}\n\n\\chapter{Automatically Generating\nTranslations}\\label{automatically-generating-translations}\n\nIf your app uses a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module}{language\nmodule} or\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application}{\\texttt{Language.properties}\nfile} for its user interface messages, you're in the right place.\nLanguage Builder provides these localization capabilities:\n\n\\begin{itemize}\n\\item\n  Generating language files for each supported locale with a single\n  command. It also propagates new default language file keys to all\n  language files, while keeping their translated values intact.\n\\item\n  Generating translations automatically using Microsoft's Translator\n  Text API. This gives you a jump start on creating translations.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Language Builder is available as a plugin for projects\nthat use Gradle or Maven.\n\n\\noindent\\hrulefill\n\nStart with Configuring the Language Builder plugin.\n\n\\section{Configuring the Language Builder\nPlugin}\\label{configuring-the-language-builder-plugin}\n\nConfigure the Language Builder plugin for\n\\href{/docs/7-2/reference/-/knowledge_base/r/lang-builder-gradle-plugin}{Gradle}\nor\n\\href{/docs/7-2/reference/-/knowledge_base/r/lang-builder-plugin}{Maven}.\n\n\\textbf{Gradle:}\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath 'com.liferay:com.liferay.gradle.plugins.lang.builder:latest.release'\n    }\n\n    repositories {\n        maven {\n            url \"https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.lang.builder\"\n\nrepositories {\n    maven {\n        url \"https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/\"\n    }\n}\n\\end{verbatim}\n\n\\textbf{Maven:}\n\n\\begin{verbatim}\n<project>\n    ...\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.liferay</groupId>\n                <artifactId>com.liferay.lang.builder</artifactId>\n                <version>1.0.30</version>\n                <configuration>\n                    <langDirName>.</langDirName>\n                    <translateClientId>${microsoft.translator.client.id}</translateClientId>\n                    <translateClientSecret>${microsoft.translator.client.secret}</translateClientSecret>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n    ...\n</project>\n\\end{verbatim}\n\nNow you can invoke Language Builder in your project.\n\n\\section{Running Language Builder}\\label{running-language-builder}\n\nWhen you run Language Builder, it generates properties files for all\nyour locales and propagates all new language properties from\n\\texttt{Language.properties} to your locale language files (newly\ngenerated and existing). Additionally if you configured the Microsoft\nTranslator Text API (discussed next), Language Builder translates your\nlocale language properties.\n\nHere's the command:\n\n\\textbf{Gradle:}\n\n\\begin{verbatim}\ngradlew buildLang\n\\end{verbatim}\n\n\\textbf{Maven:}\n\n\\begin{verbatim}\nmvn lang-builder:build\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} Run Language Builder to update your locale files each time\nyou change your \\texttt{Language.properties} file.\n\n\\noindent\\hrulefill\n\nNote, until you configure translation credentials (discussed next),\nLanguage Builder prints this message:\n\n\\begin{verbatim}\nTranslation is disabled because credentials are not specified\n\\end{verbatim}\n\nIf you want to configure your app to generate automatic translations\nusing the Microsoft Translator Text API, keep reading.\n\n\\section{Translating Language Keys\nAutomatically}\\label{translating-language-keys-automatically}\n\nIf you've configured the Language Builder plugin (above) in your\nproject, you're well on your way to translating language keys\nautomatically. Now you have to configure\n\\href{https://azure.microsoft.com/en-us/services/cognitive-services/translator-text-api/}{Microsoft's\nTranslator Text API} so you can translate language keys automatically.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Lang Builder does not translate language keys\ncontaining HTML (e.g., \\texttt{\\textless{}em\\textgreater{}},\n\\texttt{\\textless{}b\\textgreater{}},\n\\texttt{\\textless{}code\\textgreater{}}, etc.). Default language keys\nthat contain HTML are only \\emph{copied} to your locale language files.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} These translations are best used as a starting point. A\nmachine translation can't match the accuracy of a real person who is\nfluent in the language. Then again, if you only speak English and you\nneed a Hungarian translation, this is better and faster than your\nattempts at a manual translation.\n\n\\noindent\\hrulefill\n\nHere's how to set up the translator and generate translations.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Generate a translation subscription key for the Microsoft Translator\n  Text API. Follow the instructions\n  \\href{https://www.microsoft.com/en-us/translator/business/}{here}.\n\\item\n  Add your client credentials to the Language Builder plugin\n  configuration. For security reasons, pass the credentials to a\n  property that's stored in your local build environment (e.g., see the\n  \\href{https://docs.gradle.org/current/userguide/build_environment.html}{Gradle\n  environment guide}).\n\n  \\textbf{Gradle:}\n\n  Make sure the \\texttt{buildLang} task knows to use your subscription\n  key for translation by setting the \\texttt{translateSubscriptionKey}\n  property:\n\n\\begin{verbatim}\nbuildLang {\n   translateSubscriptionKey = langTranslateSubscriptionKey\n}\n\\end{verbatim}\n\n  Here's the entire \\texttt{build.gradle} example code,\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath 'com.liferay:com.liferay.gradle.plugins.lang.builder:latest.release'\n    }\n\n    repositories {\n        maven {\n            url \"https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.lang.builder\"\n\nbuildLang {\n   translateSubscriptionKey = langTranslateSubscriptionKey\n}\n\nrepositories {\n    maven {\n        url \"https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/\"\n    }\n}\n\\end{verbatim}\n\n  \\textbf{Maven:}\n\n  Set the following Language Builder plugin\n  \\texttt{\\textless{}translateClientId\\ /\\textgreater{}} and\n  \\texttt{\\textless{}translateClientSecret\\ /\\textgreater{}}\n  configuration elements using Maven build environment properties:\n\n\\begin{verbatim}\n<configuration>\n    <langDirName>.</langDirName>\n    <translateClientId>${microsoft.translator.client.id}</translateClientId>\n    <translateClientSecret>${microsoft.translator.client.secret}</translateClientSecret>\n</configuration>\n...\n\\end{verbatim}\n\n  Here's the entire \\texttt{pom.xml} example code,\n\n\\begin{verbatim}\n<project>\n    ...\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.liferay</groupId>\n                <artifactId>com.liferay.lang.builder</artifactId>\n                <version>1.0.30</version>\n                <configuration>\n                    <langDirName>.</langDirName>\n                    <translateClientId>${microsoft.translator.client.id}</translateClientId>\n                    <translateClientSecret>${microsoft.translator.client.secret}</translateClientSecret>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n    ...\n</project>\n\\end{verbatim}\n\\item\n  Run Language Builder.\n\n  \\textbf{Gradle:}\n\n\\begin{verbatim}\ngradlew buildLang\n\\end{verbatim}\n\n  \\textbf{Maven:}\n\n\\begin{verbatim}\nmvn lang-builder:build\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! You can now generate language files and provide automatic\ntranslations of your language keys.\n\n\\section{Related Topics}\\label{related-topics-95}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application}{Localizing\nYour Application}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module}{Using\nLanguage Modules}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/lang-builder-gradle-plugin}{Gradle\nLanguage Builder Plugin}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/lang-builder-plugin}{Maven\nLanguage Builder Plugin}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/tooling}{Tooling}\n\n\\chapter{Portlets}\\label{portlets}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay DXP started off as a portal server, designed to serve Java-based\nweb applications called \\emph{portlets} (see\n\\href{https://jcp.org/en/jsr/detail?id=168}{JSR 168},\n\\href{https://jcp.org/en/jsr/detail?id=286}{JSR-286}, and\n\\href{https://jcp.org/en/jsr/detail?id=362}{JSR-362}). Portlets process\nrequests and generate responses like any other web application. One key\ndifference, however, between portlets and other web apps is that\nportlets run in a portion of the web page. When you're writing a portlet\napplication, you need only worry about that application: the rest of the\npage---the navigation, the top banner, and any other global components\nof the interface---is handled by other components. Portlets run only in\na portal server. They use the portal's existing support for user\nmanagement, authentication, permissions, page management, and more. This\nfrees you to focus on developing the portlet's core functionality. In\nmany ways, writing your application as a portlet is easier than writing\na standalone application.\n\nMany portlets can be placed on a single page by users (if they have\npermission) or portal administrators. For example, a page in a community\nsite could have a calendar portlet for community events, an\nannouncements portlet for important announcements, and a bookmarks\nportlet for links of interest to the community. You can drag and drop to\nreposition and resize portlets on a page without altering any portlet\ncode. Alternatively, a single portlet can take up an entire page if it's\nthe only app you need on that page. For example, message boards or Wikis\nwith complex user interfaces are best suited on their own pages. In\nshort, portlets alleviate many of the traditional pain points associated\nwith developing Java-based web apps.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portlet-applications.png}\n\\caption{You can place multiple portlets on a single page.}\n\\end{figure}\n\nPortlets handle requests in multiple phases. This makes portlets much\nmore flexible than servlets. Each portlet phase executes different\noperations:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Render:} Generates the portlet's content based on its current\n  state. When this phase runs on one portlet, it also runs on all other\n  portlets on the page. The Render phase runs when any portlets on the\n  page complete the Action or Event phases.\n\\item\n  \\textbf{Action:} In response to a user action, the Action phase\n  performs operations that change the portlet's state. The Action phase\n  can also trigger events that are processed by the Event phase.\n  Following the Action phase and optional Event phase, the Render phase\n  then regenerates the portlet's contents.\n\\item\n  \\textbf{Event:} Processes events triggered in the Action phase. Events\n  are used for inter-portlet communication (IPC). Once the portlet\n  processes all events, the portal calls the Render phase on all\n  portlets on the page.\n\\item\n  \\textbf{Resource-serving:} Serves a resource independently from the\n  rest of the lifecycle. This lets a portlet serve dynamic content\n  without running the Render phase on all portlets on a page. The\n  Resource-serving phase handles AJAX requests.\n\\item\n  \\textbf{Header:} Lets you specify resource dependencies, such as CSS,\n  prior to the Render phase.\n\\end{itemize}\n\nCompared to servlets, portlets also have some other key differences.\nSince portlets only render a portion of a page, tags like\n\\texttt{\\textless{}html\\textgreater{}},\n\\texttt{\\textless{}head\\textgreater{}}, and\n\\texttt{\\textless{}body\\textgreater{}} aren't allowed. And because you\ndon't know the portlet's page ahead of time, you can't create portlet\nURLs directly. Instead, the portlet API gives you methods to create\nportlet URLs programmatically. Also, because portlets don't have direct\naccess to the \\texttt{javax.servlet.ServletRequest}, they can't read\nquery parameters directly from a URL. Portlets instead access a\n\\texttt{javax.portlet.PortletRequest} object. The portlet specification\nprovides a mechanism for a portlet to read only its own URL parameters\nor those declared as public render parameters. Liferay DXP does,\nhowever, provide utility methods that can access the\n\\texttt{ServletRequest} and query parameters. Portlets also have a\n\\emph{portlet filter} available for each phase in the portlet lifecycle.\nPortlet filters are similar to servlet filters in that they allow\nrequest and response modification on the fly.\n\nPortlets also differ from servlets by having distinct modes and window\nstates. Modes distinguish the portlet's current function:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{View mode:} The portlet's standard mode. Use this mode to\n  access the portlet's main functionality.\n\\item\n  \\textbf{Edit mode:} The portlet's configuration mode. Use this mode to\n  configure a custom view or behavior. For example, the Edit mode of a\n  weather portlet might let you choose a location to retrieve weather\n  data from.\n\\item\n  \\textbf{Help mode:} A mode that displays the portlet's help\n  information.\n\\end{itemize}\n\nMost modern applications use View Mode only.\n\nPortlet window states control the amount of space a portlet takes on a\npage. Window states mimic window behavior in a traditional desktop\nenvironment:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Normal:} The portlet can be on a page that contains other\n  portlets. This is the default window state.\n\\item\n  \\textbf{Maximized:} The portlet takes up an entire page.\n\\item\n  \\textbf{Minimized:} Only the portlet's title bar shows.\n\\end{itemize}\n\nAll of the\n\\href{/docs/7-2/appdev/-/knowledge_base/a/web-front-ends}{ways to\ndevelop web front-ends} on Liferay DXP involve portlets. The\nJavaScript-based widgets use Liferay's JS Portlet Extender behind the\nscenes and the Java-based web front-ends are explicitly portlets. All of\nthe web front-end types vary in their support of Portlet 3.0,\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/dependency-injection}{dependency\ninjection (DI)}, Model View Controller (MVC), and modularity, giving you\nplenty of good options for developing portlets.\n\n\\section{Related Topics}\\label{related-topics-96}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/portletmvc4spring}{Spring\nPortlet MVC: PortletMVC4Spring}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{Liferay\nMVC Portlet}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/jsf-portlet}{JSF Portlet}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/portlet-3-0-api-opt-in}{Portlet\n3.0 API Opt In}\n\n\\chapter{Using JavaScript in Your\nPortlets}\\label{using-javascript-in-your-portlets}\n\nWould you like to use the latest ECMAScript features in your JavaScript\nfiles and portlets? Do you wish you could use npm and npm packages in\nyour portlets?\n\nTo use the ES2015+ syntax in a JavaScript file, add the extension\n\\texttt{.es} to its name. For example, you rename file\n\\texttt{filename.js} to \\texttt{filename.es.js}. The extension indicates\nit uses ES2015+ syntax and must therefore be transpiled by\n\\href{https://babeljs.io/}{Babel} before deployment.\n\nES2015+ advanced features, such as\n\\href{https://babeljs.io/docs/learn-es2015/\\#generators}{generators},\nare available to you if you import the \\texttt{polyfillBabel} class from\nthe \\texttt{polyfill-babel} module:\n\n\\begin{verbatim}\nimport polyfillBabel from 'polyfill-babel'\n\\end{verbatim}\n\nThe \\href{http://babeljs.io/docs/usage/polyfill/}{Babel Polyfill}\nemulates a complete ES6 environment. Use it at your own discretion, as\nit loads a large amount of code. You can inspect\n\\url{https://github.com/zloirock/core-js\\#core-js} to see what's\npolyfilled.\n\nOnce you've completed writing your module, you can expose it by creating\na \\texttt{package.json} file that specifies your bundle's name and\nversion. Make sure to create this in your module's root folder. Below is\nan example \\texttt{package.json} file for a \\texttt{js-logger} module:\n\n\\begin{verbatim}\n{\n    \"name\": \"js-logger\",\n    \"version\": \"1.0.0\"\n}\n\\end{verbatim}\n\nThe Module Config Generator creates the module based on this\ninformation. In this section, you'll learn how to prepare your\nJavaScript files to leverage ECMAScript and npm features in your\nportlets.\n\n\\chapter{Using ES2015 Modules in your\nPortlet}\\label{using-es2015-modules-in-your-portlet}\n\nOnce you've\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-javascript-in-your-portlets}{exposed\nyour modules} via your \\texttt{package.json} file, you can use them in\nyour portlets. The \\texttt{aui:script} tag's \\texttt{require} attribute\nmakes it easy.\n\nFollow the steps below to use your exposed modules in your portlets:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Declare the \\texttt{aui} taglib in your view JSP:\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\" %>\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** if you created the portlet using Blade, the `aui` taglib is \n already provided for you in the `init.jsp`. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Add an \\texttt{aui:script} tag to the JSP and set the \\texttt{require}\n  attribute to the relative path for your module.\n\n  The \\texttt{require} attribute lets you include your exposed modules\n  in your JSP. The AMD Loader fetches the specified module and its\n  dependencies. An example faux Console Logger Portlet's\n  \\texttt{view.jsp} shown below includes the module \\texttt{logger.es}:\n\n\\begin{verbatim}\n<aui:script require=\"js-logger/logger.es\">\n    var Logger = jsLoggerLoggerEs.default;\n\n    var loggerOne = new Logger('*** -> ');\n    loggerOne.log('Hello');\n\n    var loggerDefault = new Logger();\n    loggerDefault.log('World');\n</aui:script>\n\\end{verbatim}\n\n  References to the module within the script tag are named after the\n  \\texttt{require} value, in camel-case and with all invalid characters\n  removed. The \\texttt{logger.es} module's reference\n  \\texttt{jsLoggerLoggerEs} is derived from the module's relative path\n  value \\texttt{js-logger/logger.es}. The value is stripped of its dash\n  and slash characters and converted to camel case.\n\\end{enumerate}\n\nThanks to the \\texttt{aui:script} tag and its \\texttt{require}\nattribute, using your modules in your portlet is a piece of cake!\n\n\\section{Related Topics}\\label{related-topics-97}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps}{Customizing\n  JSPs}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/web-services}{Web\n  Services}\n\\end{itemize}\n\n\\chapter{Using npm in Your Portlets}\\label{using-npm-in-your-portlets}\n\nnpm is a powerful tool, and almost a necessity for Front-End\ndevelopment. You can use npm as your JavaScript package manager\ntool---including npm and npm packages---while developing portlets in\nyour normal, everyday workflow.\n\nDeployed portlets leverage\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/loading-amd-modules-in-liferay}{Liferay\nAMD Loader} to share JavaScript modules and take advantage of semantic\nversioning when resolving modules among portlets on the same page. The\nliferay-npm-bundler helps prepare your npm modules for the Liferay AMD\nLoader.\n\nThe bundler copies the project and \\texttt{node\\_modules}' JS files to\nthe output and wraps them inside a \\texttt{Liferay.Loader.define()} call\nso that the Liferay AMD Loader knows how to handle them. It also\nnamespaces the module names in \\texttt{require()} calls and inside the\n\\texttt{Liferay.Loader.define()} call with the project's name prefix to\nachieve\n\\href{/docs/7-2/reference/-/knowledge_base/r/how-the-liferay-npm-bundler-publishes-npm-packages\\#isolated-package-dependencies}{dependency\nisolation}. The bundler injects the dependencies in the\n\\texttt{package.json} pertaining to the module to make them available at\nruntime.\n\nThis section covers how to set up npm-based portlet projects.\n\n\\chapter{Formatting Your npm Modules for\nAMD}\\label{formatting-your-npm-modules-for-amd}\n\nFor Liferay DXP to recognize your npm modules, they must be formatted\nfor the Liferay AMD Loader. Luckily, the liferay-npm-bundler handles\nthis for you, you just have to provide the proper configuration and add\nit to your build script. This article shows how to use the\nliferay-npm-bundler to set up npm-based portlet projects.\n\nFollow these steps to configure your project to use the\nliferay-npm-bundler:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Install NodeJS \\textgreater=\n  \\href{http://nodejs.org/dist/v6.11.0/}{v6.11.0} if you don't have it\n  installed.\n\\item\n  Navigate to your portlet's project folder and initialize a\n  \\texttt{package.json} file if it's not present yet.\n\n  If you don't have a portlet already, create an empty MVC portlet\n  project. For convenience, you can use\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-blade-cli}{Blade\n  CLI} to create an empty portlet with the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-mvc-portlet-template}{mvc\n  portlet blade template}.\n\n  If you don't have a \\texttt{package.json} file, you can run\n  \\texttt{npm\\ init\\ -y} to create an empty one based on the project\n  directory's name.\n\\item\n  Run this command to install the liferay-npm-bundler:\n\n\\begin{verbatim}\nnpm install --save-dev liferay-npm-bundler\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Use npm from within your portlet project's root folder (where the\n `package.json` file lives), as you normally do on a typical web project.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  Add the \\texttt{liferay-npm-bundler} to your \\texttt{package.json}'s\n  build script to pack the needed npm packages and transform them to\n  AMD:\n\n\\begin{verbatim}\n\"scripts\": {\n      \"build\": \"liferay-npm-bundler\"\n}\n\\end{verbatim}\n\\item\n  Configure your project for the bundler, using the\n  \\texttt{.npmbundlerrc} file (create this file in your project's root\n  folder if it doesn't exist). See the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/understanding-the-npmbundlerrcs-structure}{liferay-npm-bundler's\n  \\texttt{.npmbundlerrc} structure reference} for more information on\n  the available options. Specify the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-npm-bundlers-loaders}{loaders\n  and rules} to use for your project's source files. The example below\n  processes the JavaScript files in the project's \\texttt{/src/} and\n  \\texttt{/assets/} folders with Babel via the\n  \\href{https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-babel-loader}{\\texttt{babel-loader}}:\n\n\\begin{verbatim}\n{\n  \"sources\": [\"src\", \"assets\"],\n  \"rules\": [\n    {\n      \"test\": \"\\\\.js$\",\n      \"exclude\": \"node_modules\",\n      \"use\": [\n        {\n          \"loader\": \"babel-loader\",\n          \"options\": {\n            \"presets\": [\"env\"]\n          }\n        }\n      ]\n    }\n  ]\n}\n\\end{verbatim}\n\\item\n  Run \\texttt{npm\\ install} to install the required dependencies.\n\\item\n  Run the build script to bundle your dependencies with the\n  liferay-npm-bundler:\n\n\\begin{verbatim}\nnpm run-script build\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} By default, the AMD Loader times out in seven seconds.\nYou can configure this value through System Settings. Open the Control\nPanel and navigate to \\emph{Configuration} → \\emph{System Settings} →\n\\emph{PLATFORM} → \\emph{Infrastructure}, and select \\emph{JavaScript\nLoader}. Set the \\emph{Module Definition Timeout} configuration to the\ntime you want and click \\emph{Save}.\n\n\\noindent\\hrulefill\n\nGreat! Now you know how to use the liferay-npm-bundler to bundle your\nnpm-based portlets for the Liferay AMD Loader.\n\n\\section{Related Topics}\\label{related-topics-98}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-javascript-in-your-portlets}{Preparing\n  Your JavaScript Files for ES2015+}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-npm-bundlers-loaders}{Understanding\n  liferay-npm-bundler's Loaders and Rules}\n\\end{itemize}\n\n\\chapter{Migrating a liferay-npm-bundler Project from 1.x to\n2.x}\\label{migrating-a-liferay-npm-bundler-project-from-1.x-to-2.x}\n\nYou should use the latest 2.x version of the liferay-npm-bundler. It\n\\href{/docs/7-2/reference/-/knowledge_base/r/what-changed-between-liferay-npm-bundler-1-x-and-2-x}{offers\nmore stability and includes more features out-of-the-box}. If you\nalready created a project using the 1.x version, don't worry. Follow\nthese steps to migrate your project to 2.x:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Update the \\texttt{liferay-npm-bundler} dependency in your\n  \\texttt{package.json} to version 2.x:\n\n\\begin{verbatim}\n{\n  \"devDependencies\": {\n    ...\n    \"liferay-npm-bundler\": \"^2.0.0\",\n    ...\n  },\n  ...\n}\n\\end{verbatim}\n\\item\n  Remove all \\texttt{liferay-npm-bundler-preset-*} dependencies from\n  your \\texttt{package.json} because liferay-npm-bundler 2.x includes\n  these by default.\n\\item\n  Remove any bundler presets you configured in your\n  \\texttt{.npmbundlerrc} file. liferay-npm-bundler 2.x includes one\n  smart preset that handles all frameworks automatically.\n\\end{enumerate}\n\nThese are the standard requirements that all projects have in common.\nThe remaining steps depend on your project's framework. Follow the\ninstructions in the corresponding section to finish migrating your\nproject.\n\n\\chapter{Migrating a Plain JavaScript, Billboard JS, JQuery, Metal JS,\nReact, or Vue JS Project to Use Bundler\n2.x}\\label{migrating-a-plain-javascript-billboard-js-jquery-metal-js-react-or-vue-js-project-to-use-bundler-2.x}\n\nAfter following the steps covered in the intro to this section, follow\nthese remaining steps to migrate the framework projects shown below to\n2.x:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  plain JS project\n\\item\n  Billboard.js project\n\\item\n  JQuery project\n\\item\n  Metal.js project\n\\item\n  React project\n\\item\n  Vue.js project\n\\end{itemize}\n\nWhile Babel is required to transpile your source files, you must remove\nany Babel preset used for transformations from your project that bundler\n1.x imposed. liferay-npm-bundler 2.x handles these transformations by\ndefault:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Remove the \\emph{liferay-project} preset from your project's\n  \\texttt{.babelrc} file. All that should remain is the \\texttt{es2015}\n  preset shown below:\n\n\\begin{verbatim}\n{\n  \"presets\": [\"es2015\"]\n}\n\\end{verbatim}\n\n  If your project uses React, make sure the \\texttt{react} preset\n  remains as well:\n\n\\begin{verbatim}\n{\n  \"presets\": [\"es2015\", \"react\"]\n}\n\\end{verbatim}\n\\item\n  Remove the \\texttt{babel-preset-liferay-project} dependency from your\n  \\texttt{package.json}.\n\\end{enumerate}\n\nAwesome! Your project is migrated to use the new version of the\nliferay-npm-bundler.\n\n\\section{Related Topics}\\label{related-topics-99}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/formatting-your-npm-modules-for-amd}{Formatting\n  Your npm Modules for AMD}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-npmresolver-api-in-your-portlets}{Using\n  the NPMResolver API in Your Portlets}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/what-changed-between-liferay-npm-bundler-1-x-and-2-x}{What\n  Changed between liferay-npm-bundler 1.x and 2.x}\n\\end{itemize}\n\n\\chapter{Migrating an Angular Project to Use Bundler\n2.x}\\label{migrating-an-angular-project-to-use-bundler-2.x}\n\nAfter following the steps covered in the intro to this section, follow\nthese remaining steps to migrate your Angular project to 2.x. While\nliferay-npm-bundler 1.x relied on Babel to perform some transformation\nsteps, these transformations are now automatically applied in version\n2.x. Therefore, you should remove Babel from your project:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your \\texttt{tsconfig.json} file and replace the\n  \\texttt{\"module\":\\ \"amd\"} compiler option with the configuration shown\n  below to produce CommonJS modules:\n\n\\begin{verbatim}\n{\n  \"compilerOptions\": {\n    ...\n    \"module\": \"commonjs\",\n    ...\n  }\n}\n\\end{verbatim}\n\\item\n  Delete the \\texttt{.babelrc} file to remove the Babel configuration.\n\\item\n  Remove Babel from your \\texttt{package.json} build process so it\n  matches the configuration below:\n\n\\begin{verbatim}\n{\n  \"scripts\": {\n    \"build\": \"tsc && liferay-npm-bundler\"\n  },\n  ...\n}\n\\end{verbatim}\n\\item\n  Remove the following Babel dependencies from your\n  \\texttt{package.json} \\emph{devDependencies}:\n\n\\begin{verbatim}\n\"babel-cli\": \"6.26.0\",\n\"babel-preset-liferay-amd\": \"1.2.2\"\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! Your project is migrated to use the new version of the\nliferay-npm-bundler.\n\n\\section{Related Topics}\\label{related-topics-100}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/formatting-your-npm-modules-for-amd}{Formatting\n  Your npm Modules for AMD}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-the-npmresolver-api-in-your-portlets}{Using\n  the NPMResolver API in Your Portlets}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/what-changed-between-liferay-npm-bundler-1-x-and-2-x}{What\n  Changed between liferay-npm-bundler 1.x and 2.x}\n\\end{itemize}\n\n\\chapter{Migrating Your Project to Use liferay-npm-bundler's New\nMode}\\label{migrating-your-project-to-use-liferay-npm-bundlers-new-mode}\n\nIn the previous version of the liferay-npm-bundler, before the bundler\nran, the build did some preprocessing, then the bundler modified the\noutput from the preprocessed files, as shown in the example build script\nbelow:\n\n\\begin{verbatim}\n{\n  \"scripts\":{\n    \"build\": \"babel --source-maps -d build src && liferay-npm-bundler\"\n  }\n}\n\\end{verbatim}\n\nIn the new mode, the liferay-npm-bundler runs the whole process, like\nwebpack, and is configured via a set of rules. The build script is\ncondensed, as shown below:\n\n\\begin{verbatim}\n{\n  \"scripts\":{\n    \"build\": \"liferay-npm-bundler\"\n  }\n}\n\\end{verbatim}\n\nFollow these steps to migrate your project to use the new configuration\nmode:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the project's \\texttt{package.json} file and update the\n  \\texttt{build} script to use only the liferay-npm-bundler:\n\n\\begin{verbatim}\n{\n  \"scripts\":{\n    \"build\": \"liferay-npm-bundler\"\n  }\n}\n\\end{verbatim}\n\\item\n  Define the rules for the bundler to use (e.g.~running babel to\n  transpile files) in the project's \\texttt{.npmbundlerrc} file. The\n  example configuration below defines rules for using the\n  \\texttt{babel-loader} to transpile JavaScript files. See the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/default-liferay-npm-bundler-loaders}{Default\n  Loaders reference} for the full list of default loaders. Follow the\n  steps in\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-loaders-for-the-liferay-npm-bundler}{Creating\n  Custom Loaders for the Bundler} to create a custom loader. The\n  liferay-npm-bundler processes the \\texttt{*.js} files in\n  \\texttt{/src/} with babel and writes the results in the default\n  \\texttt{/build/} folder:\n\n\\begin{verbatim}\n{\n  \"sources\": [\"src\"],\n  \"rules\": [\n    {\n      \"test\": \"\\\\.js$\",\n      \"exclude\": \"node_modules\",\n      \"use\": [\n        {\n          \"loader\": \"babel-loader\",\n          \"options\": {\n            \"presets\": [\"env\"]\n          }\n        }\n      ]\n    }\n  ]\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n**Note:** The new mode of the liferay-npm-bundler acts very much \n like webpack, but because webpack creates a single JS bundle file and \n liferay-npm-bundler targets AMD loader, they are not compatible.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\section{Related Topics}\\label{related-topics-101}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/default-liferay-npm-bundler-loaders}{Default\n  liferay-npm-bundler Loaders}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-npm-bundlers-loaders}{Understanding\n  liferay-npm-bundler's Loaders}\n\\end{itemize}\n\n\\chapter{Creating Custom Loaders for the\nliferay-npm-bundler}\\label{creating-custom-loaders-for-the-liferay-npm-bundler}\n\nSince webpack creates JavaScript bundles and the liferay-npm-bundler\ntargets AMD loader, webpack's loaders aren't compatible with the\nliferay-npm-bundler. So, if you want to use a loader that isn't\n\\href{/docs/7-2/reference/-/knowledge_base/r/default-liferay-npm-bundler-loaders}{available\nby default}, you must create a custom loader.\n\nA loader, in terms of the liferay-npm-bundler, is defined as an npm\npackage that has a main module which exports a default function with\nthis signature:\n\n\\begin{verbatim}\nfunction(context, options){\n}\n\\end{verbatim}\n\nThe arguments are defined as follows:\n\n\\texttt{context}: an object containing these fields:\n\n\\begin{quote}\n\\texttt{content}: a string with the contents of the processed file (the\nmain input of the loader)\n\\end{quote}\n\n\\begin{quote}\n\\texttt{filepath}: the project-relative path to the file to process with\nthe loader\n\\end{quote}\n\n\\begin{quote}\n\\texttt{extraArtifacts}: an object with project-relative paths as keys\nand strings as values of properties that may be used to output extra\nfiles along with the one being processed (for example, you can use it to\ngenerate source maps).\n\\end{quote}\n\n\\begin{quote}\n\\texttt{log}: a logger that writes execution information to the\nbundler's report file (see the\n\\href{https://github.com/liferay/liferay-js-toolkit/blob/master/packages/liferay-npm-build-tools-common/src/plugin-logger.js}{\\texttt{PluginLogger}\nclass} for information on its structure and API).\n\\end{quote}\n\n\\texttt{options}: an object taken from the \\texttt{options} field of the\nloader's configuration (See\n\\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-npm-bundlers-loaders}{Understanding\nliferay-npm-bundler's loaders and rules} for more information).\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} the function may return nothing or modified content. If\nsomething is returned, it is copied on top of the\n\\texttt{context.content} field and used to feed the next loader or write\nthe output file. This is the equivalent to\n\\texttt{context.content\\ =\\ \\textquotesingle{}something\\textquotesingle{}}.\nIf your loader does not return a file, but instead it only filters files\nto prevent them from being generated, you must explicitly set\n\\texttt{context.content\\ =\\ \\textquotesingle{}undefined\\textquotesingle{}}.\n\n\\noindent\\hrulefill\n\nFollow these steps to write a new loader. These steps use the Babel\nloader as an example:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If your loader requires configuration, like Babel, you may define a\n  rule configuration like the one shown below so you can specify options\n  for the loader:\n\n\\begin{verbatim}\n{\n  \"rules\": [\n    {\n      \"test\": \"\\\\.js$\",\n      \"exclude\": \"node_modules\",\n      \"use\": [\n        {\n          \"loader\": \"babel-loader\",\n          \"options\": {\n            \"presets\": [\"env\", \"react\"]\n          }\n        }\n      ]\n    }\n  ]\n}\n\\end{verbatim}\n\\item\n  Create an \\texttt{index.js} file and write a function that takes the\n  input content, passes it through the loader, and writes the result and\n  the source map file to the output folder. The loader function below\n  takes the passed content (JS files), run it through babel, and writes\n  the result and source map to the default \\texttt{/build/} output\n  folder:\n\n\\begin{verbatim}\nexport default function(context, options) {\n  // Get input parameters\n  const { content, filePath, log, sourceMap } = context;\n\n  // Run babel on content\n  const result = babel.transform(content, options);\n\n  // Create an extra .map file with source map next to source .js file\n  context.extraArtifacts[`${filePath}.map`] = JSON.stringify(result.map);\n\n  // Tell the user what we have done\n  log.info(\"babel-loader\", \"Transpiled file\");\n\n  // Return the modified content\n  return result.code;\n}\n\\end{verbatim}\n\\item\n  Place the \\texttt{index.js} file in an npm package and publish it.\n\\item\n  Include the npm package you just created as a \\texttt{devDependency}\n  in the project's \\texttt{package.json}:\n\n\\begin{verbatim}\n\"devDependencies\": {\n  \"liferay-npm-bundler\": \"2.12.0\",\n  \"liferay-npm-build-support\": \"2.12.0\",\n  \"liferay-npm-bundler-loader-babel-loader\": \"2.12.0\",\n  ...\n}\n\\end{verbatim}\n\\item\n  Configure the loader's name in the \\texttt{rules} section of the\n  project's \\texttt{.npmbundlerrc} file:\n\n\\begin{verbatim}\n{\n  \"sources\": [\"src\"],\n  ...\n  \"rules\": [\n    {\n      \"test\": \"\\\\.js$\",\n      \"exclude\": \"node_modules\",\n      \"use\": [\n        {\n          \"loader\": \"babel-loader\",\n          \"options\": {\n            \"presets\": [\"env\", \"react\"]\n          }\n        }\n      ]\n    }\n  ],\n  ...\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-102}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/default-liferay-npm-bundler-loaders}{Default\n  liferay-npm-bundler Loaders}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-npm-bundlers-loaders}{Understanding\n  liferay-npm-bundler's Loaders}\n\\end{itemize}\n\n\\chapter{Using the NPMResolver API in Your\nPortlets}\\label{using-the-npmresolver-api-in-your-portlets}\n\nIf you're developing an npm-based portlet, your OSGi bundle's\n\\texttt{package.json} is a treasure-trove of information. It contains\neverything that's stored in the npm registry about your bundle: default\nentry point, dependencies, modules, package names, versions, and more.\nThe\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/NPMResolver.html}{\\texttt{NPMResolver}\nAPIs} expose this information so you can access it in your portlet. If\nit's defined in the OSGi bundle's \\texttt{package.json}, you can\nretrieve the information in your portlet with the \\texttt{NPMResolver}\nAPI. For instance, you can use this API to\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/obtaining-dependency-npm-package-descriptors}{reference\nan npm package's static resources} (such as CSS files) and even to\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/referencing-an-npm-modules-package}{make\nyour code more maintainable}.\n\nTo enable the \\texttt{NPMResolver} in your portlet, use the\n\\texttt{@Reference} annotation to inject the \\texttt{NPMResolver} OSGi\ncomponent into your portlet's Component class, as shown below:\n\n\\begin{verbatim}\nimport com.liferay.frontend.js.loader.modules.extender.npm.NPMResolver;\n\npublic class MyPortlet extends MVCPortlet {\n  \n  @Reference\n  private NPMResolver `_npmResolver`;\n  \n}\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Because the \\texttt{NPMResolver} reference is tied\ndirectly to the OSGi bundle's \\texttt{package.json} file, it can only be\nused to retrieve npm module and package information from that file. You\ncan't use the \\texttt{NPMResolver} to retrieve npm package information\nfor other OSGi bundles.\n\n\\noindent\\hrulefill\n\nNow that the \\texttt{NPMResolver} is added to your portlet, read the\ntopics in this section to learn how to retrieve your OSGi bundle's npm\npackage and module information.\n\n\\chapter{Referencing an npm Module's Package to Improve Code\nMaintenance}\\label{referencing-an-npm-modules-package-to-improve-code-maintenance}\n\nOnce you've\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-javascript-in-your-portlets}{exposed\nyour modules}, you can use them in your portlet via the\n\\texttt{aui:script} tag's \\texttt{require} attribute. By default,\nLiferay DXP automatically composes an npm module's JavaScript variable\nbased on its name. For example, the module \\texttt{my-package@1.0.0}\ntranslates to the variable \\texttt{myPackage100} for the\n\\texttt{\\textless{}aui:script\\textgreater{}} tag's \\texttt{require}\nattribute. This means that each time a new version of the OSGi bundle's\nnpm package is released, you must update your code's variable to reflect\nthe new version. You can use the\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/JSPackage.html}{\\texttt{JSPackage}\ninterface} to obtain the module's package name and create an alias to\nreference it, so the variable name always reflects the latest version\nnumber!\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Retrieve a reference to the OSGi bundle's npm package using the\n  \\href{https://docs.liferay.com/dxp/apps/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/NPMResolver.html\\#getJSPackage}{\\texttt{getJSPackage()}\n  method}:\n\n\\begin{verbatim}\nJSPackage jsPackage = _npmResolver.getJSPackage();\n\\end{verbatim}\n\\item\n  Grab the npm package's resolved ID (the current package version, in\n  the format\n  \\texttt{\\textless{}package\\ name\\textgreater{}@\\textless{}version\\textgreater{}},\n  defined in the OSGi module's \\texttt{package.json}) using the\n  \\href{https://docs.liferay.com/dxp/apps/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/JSPackage.html\\#getResolvedId}{\\texttt{getResolvedId()}\n  method} and alias it with the \\texttt{as\\ myVariableName} pattern. The\n  example below retrieves the npm module's resolved ID, sets it to the\n  \\texttt{bootstrapRequire} variable, and assigns the entire value to\n  the attribute \\texttt{bootstrapRequire}. This ensures that the package\n  version is always up to date:\n\n\\begin{verbatim}\nrenderRequest.setAttribute(\n  \"bootstrapRequire\",\n  jsPackage.getResolvedId() + \" as bootstrapRequire\");\n\\end{verbatim}\n\\item\n  Include the reference to the\n  \\href{https://docs.liferay.com/dxp/apps/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/NPMResolver.html}{\\texttt{NPMResolver}}:\n\n\\begin{verbatim}\n@Reference\nprivate NPMResolver _npmResolver;\n\\end{verbatim}\n\\item\n  Resolve the \\texttt{JSPackage} and \\texttt{NPMResolver} imports:\n\n\\begin{verbatim}\nimport com.liferay.frontend.js.loader.modules.extender.npm.JSPackage;\nimport com.liferay.frontend.js.loader.modules.extender.npm.NPMResolver;\n\\end{verbatim}\n\\item\n  In the portlet's JSP, retrieve the aliased attribute\n  (\\texttt{bootstrapRequire} in the example):\n\n\\begin{verbatim}\n<%\nString bootstrapRequire =\n    (String)renderRequest.getAttribute(\"bootstrapRequire\");\n%>\n\\end{verbatim}\n\\item\n  Finally, use the attribute as the\n  \\texttt{\\textless{}aui:script\\textgreater{}} require attribute's\n  value:\n\n\\begin{verbatim}\n<aui:script require=\"<%= bootstrapRequire %>\">\n    bootstrapRequire.default();\n</aui:script>\n\\end{verbatim}\n\n  Below is the full example \\texttt{*Portlet} class:\n\n\\begin{verbatim}\npublic class MyPortlet extends MVCPortlet {\n\n    @Override\n    public void doView(\n            RenderRequest renderRequest, RenderResponse renderResponse)\n        throws IOException, PortletException {\n\n        JSPackage jsPackage = _npmResolver.getJSPackage();\n\n        renderRequest.setAttribute(\n            \"bootstrapRequire\",\n            jsPackage.getResolvedId() + \" as bootstrapRequire\");\n\n        super.doView(renderRequest, renderResponse);\n    }\n\n    @Reference\n    private NPMResolver _npmResolver;\n\n}\n\\end{verbatim}\n\n  And here is the corresponding example \\texttt{view.jsp}:\n\n\\begin{verbatim}\n<%\nString bootstrapRequire =\n  (String)renderRequest.getAttribute(\"bootstrapRequire\");\n%>\n\n<aui:script require=\"<%= bootstrapRequire %>\">\n  bootstrapRequire.default();\n</aui:script>\n\\end{verbatim}\n\\end{enumerate}\n\nNow you know how to reference an npm module's package!\n\n\\section{Related Topics}\\label{related-topics-103}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/obtaining-dependency-npm-package-descriptors}{Obtaining\n  an OSGi bundle's Dependency npm Package Descriptors}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-npm-bundler}{liferay-npm-bundler}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/how-the-liferay-npm-bundler-publishes-npm-packages}{How\n  Liferay DXP Publishes npm Packages}\n\\end{itemize}\n\n\\chapter{Obtaining an OSGi bundle's Dependency npm Package\nDescriptors}\\label{obtaining-an-osgi-bundles-dependency-npm-package-descriptors}\n\nWhile writing your npm portlet, you may need to reference a dependency\npackage or its modules. For instance, you can retrieve an npm dependency\npackage module's CSS file and use it in your portlet. The\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/NPMResolver.html}{\\texttt{NPMResolver}\nOSGi component} provides two methods for retrieving an OSGi bundle's\ndependency npm package descriptors:\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/NPMResolver.html\\#getDependencyJSPackage}{\\texttt{getDependencyJSPackage()}}\nto retrieve dependency npm packages and\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/NPMResolver.html\\#resolveModuleName}{\\texttt{resolveModuleName()}}\nto retrieve dependency npm modules. This article references the\n\\texttt{package.json} below to help demonstrate these methods:\n\n\\begin{verbatim}\n{\n    \"dependencies\": {\n        \"react\": \"15.6.2\",\n        \"react-dom\": \"15.6.2\"\n    },\n    .\n    .\n    .\n}\n\\end{verbatim}\n\nTo obtain an OSGi bundle's npm dependency package, pass the package's\nname in as the \\texttt{getDependencyJSPackage()} method's argument. The\nexample below resolves the \\texttt{react} dependency package:\n\n\\begin{verbatim}\nString reactResolvedId = npmResolver.getDependencyJSPackage(\"react\");\n\\end{verbatim}\n\n\\texttt{reactResolvedId}'s resulting value is \\texttt{react@15.6.2}.\n\nYou can use the \\texttt{resolveModuleName()} method To obtain a module\nin an npm dependency package. To do this, pass the module's relative\npath in as the \\texttt{resolveModuleName()} method's argument. The\nexample below resolves a module named \\texttt{react-with-addons} for the\n\\texttt{react} dependency package:\n\n\\begin{verbatim}\nString resolvedModule = \nnpmResolver.resolveModuleName(\"react/dist/react-with-addons\");\n\\end{verbatim}\n\nThe \\texttt{resolvedModule} variable evaluates to\n\\texttt{react@15.6.2/dist/react-with-addons}. You can also use this to\nreference static resources inside npm packages (like CSS or image\nfiles), as shown in the example below:\n\n\\begin{verbatim}\nString cssPath = npmResolver.resolveModuleName(\n      \"react/lib/css/main.css\"); \n\\end{verbatim}\n\nNow you know how to obtain an OSGi bundle's dependency npm packages\ndescriptors!\n\n\\section{Related Topics}\\label{related-topics-104}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/referencing-an-npm-modules-package}{Referencing\n  an npm Module's Package}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/the-structure-of-osgi-bundles-containing-npm-packages}{The\n  Structure of OSGi Bundles Containing npm Packages}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/how-the-liferay-npm-bundler-publishes-npm-packages}{How\n  Liferay DXP Publishes npm Packages}\n\\end{itemize}\n\n\\chapter{Automatic Single Page\nApplications}\\label{automatic-single-page-applications}\n\nA good user experience is the measure of a well-designed site. A user's\ntime is highly valuable. The last thing you want is for someone to grow\nfrustrated with your site because of constant page reloads. A Single\nPage Application (SPA) avoids this issue. Single Page Applications\ndrastically cut down on load times by loading only a single HTML page\nthat's dynamically updated as the user interacts and navigates through\nthe site. This provides a more seamless app experience by eliminating\npage reloads. \\textbf{SPA is enabled by default in your apps and sites\nand requires no changes to your workflow or code!} If Spa is disabled,\nensure that the \\texttt{com.liferay.frontend.js.spa.web-{[}version{]}}\nmodule is deployed and active.\n\n\\section{The Benefits of SPAs}\\label{the-benefits-of-spas}\n\nLet's say you're surfing the web and you find a really rad site that\nhappens to be SPA enabled. All right! Page load times are blazin' fast.\nYou're deep into the site, scrolling along, when you find this great\npost that just speaks to you. You copy the URL from the address bar and\nemail it to all of your friends with the subject: `Your Life Will Change\nForever.' They must experience this awe-inspiring work!\n\nYou get a response back almost immediately. ``This is a rad site, but\nwhat post are you talking about?'' it reads.\n\n``What!? Do my eyes deceive me?'' you exclaim. You were in so much of a\nhurry to share this life-changing content that you neglected to notice\nthat the URL never updated when you clicked the post. You click the back\nbutton, hoping to get back to the post, but it takes you to the site you\nwere on before you ever visited this one. The page history didn't update\nas you navigated through the app; Only the main app URL was saved.\n\nWhat a bummer! ``Why? Why have you failed me, site?'' you cry.\n\nIf only there was a way to have a Single Page Application, but also be\nable to link to the content you want. Well, don't despair my friend. You\ncan have your cake and eat it too, thanks to SennaJS.\n\n\\section{What is SennaJS?}\\label{what-is-sennajs}\n\nSennaJS is Liferay DXP's SPA engine. SennaJS handles the client-side\ndata, and AJAX loads the page's content dynamically. While there are\nother JavaScript frameworks out there that may provide some of the same\nfeatures, Senna's only focus is SPA, ensuring that your site provides\nthe best user experience possible.\n\nSennaJS provides the following key enhancements to SPA:\n\n\\textbf{SEO \\& Bookmarkability}: Sharing or bookmarking a link displays\nthe same content you are viewing. Search engines are able to index this\ncontent.\n\n\\textbf{Hybrid rendering}: Ajax + server-side rendering lets you disable\n\\texttt{pushState} at any time, allowing progressive enhancement. You\ncan use your preferred method to render the server side (e.g.~HTML\nfragments or template views).\n\n\\textbf{State retention}: Scrolling, reloading, or navigating through\nthe history of the page takes you back to where you were. SennaJS\nexposes lifecycle events that represent state changes in the\napplication. See \\href{available-spa-lifecycle-events}{Available SPA\nLifecycle Events} for more information.\n\n\\textbf{UI feedback}: The UI indicates to the user when some content is\nrequested.\n\n\\textbf{Pending navigations}: UI rendering is blocked until data is\nloaded, and the content is displayed all at once.\n\n\\textbf{Timeout detection}: If the request takes too long to load or the\nuser tries to navigate to a different link while another request is\npending, the request times out.\n\n\\textbf{History navigation}: The browser history is manipulated via the\n\\href{https://developer.mozilla.org/en-US/docs/Web/API/History}{History\nAPI}, so you can use the back and forward history buttons to navigate\nthrough the history of the page.\n\n\\textbf{Cacheable screens}: Once a surface is loaded, the content is\ncached in memory and is retrieved later without any additional request,\nspeeding up your application.\n\n\\textbf{Page resources management}: Scripts and stylesheets are\nevaluated from dynamically loaded resources. Additional content can be\nappended to the DOM using \\texttt{XMLHttpRequest}. For security reasons,\nsome browsers won't evaluate \\texttt{\\textless{}script\\textgreater{}}\ntags from content fragments. Therefore, SennaJS extracts scripts from\nthe content and parses them to ensure that they meet the browser loading\nrequirements.\n\nYou can see examples and read more about SennaJS at its\n\\href{http://sennajs.com/}{website}.\n\nThis section covers these topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Configuring SPA System Settings\n\\item\n  Disabling SPA\n\\item\n  Specifying how resources are loaded during SPA navigation\n\\item\n  Detaching Global Listeners\n\\end{itemize}\n\n\\chapter{Configuring SPA System\nSettings}\\label{configuring-spa-system-settings}\n\nDepending on what behaviors you need to customize, you can configure SPA\noptions in one of two places. SPA caching and SPA timeout settings are\nconfigured in System Settings. If you wish to disable SPA for a certain\nlink, page, or portlet in your site, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/disabling-spa}{Disabling\nSPA}.\n\nFollow these steps to configure system settings for SPA:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the Control Panel, navigate to \\emph{Configuration} → \\emph{System\n  Settings}.\n\\item\n  Select \\emph{Infrastructure} under the \\emph{PLATFORM} heading.\n\\item\n  Click \\emph{Frontend SPA Infrastructure}.\n\\end{enumerate}\n\nThe following configuration options are available:\n\n\\textbf{Cache Expiration Time}: The time, in minutes, in which the SPA\ncache is cleared. A negative value means the cache should be disabled.\n\n\\textbf{Navigation Exception Selectors}: Defines a CSS selector that SPA\nshould ignore.\n\n\\textbf{Request Timeout Time}: The time, in milliseconds, in which a SPA\nrequest times out. A zero value means the request should never timeout.\n\n\\textbf{User Notification Timeout}: The time, in milliseconds, in which\na notification is shown to the user stating that the request is taking\nlonger than expected. A zero value means no notification should be\nshown.\n\nGreat! Now you know how to configure system settings for SPA.\n\n\\section{Related Topics}\\label{related-topics-105}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/disabling-spa}{Disabling\n  SPA}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/specifying-how-resources-are-loaded-during-navigation}{Specifying\n  How Resources Are Loaded During SPA Navigation}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/detaching-global-listeners}{Detaching\n  Global Listeners}\n\\end{itemize}\n\n\\chapter{Disabling SPA}\\label{disabling-spa}\n\nCertain elements of your page may require a regular navigation to work\nproperly. For example, you may have downloadable content that you want\nto share with the user. In these cases, SPA must be disabled for those\nspecific elements. You can disable SPA at these scopes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  disable SPA across an entire Liferay DXP instance\n\\item\n  disable SPA in a portlet\n\\item\n  Disable SPA in individual elements\n\\end{itemize}\n\nFollow the steps in the corresponding section to disable SPA for that\nscope.\n\n\\section{Disabling SPA across an\nInstance}\\label{disabling-spa-across-an-instance}\n\nTo disable SPA across an entire Liferay DXP instance, add the following\nline to your \\texttt{portal-ext.properties}:\n\n\\begin{verbatim}\njavascript.single.page.application.enabled = false\n\\end{verbatim}\n\n\\section{Disabling SPA for a Portlet}\\label{disabling-spa-for-a-portlet}\n\nTo disable SPA for a portlet, you must blacklist it. To blacklist a\nportlet from SPA, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your portlet class.\n\\item\n  Set the \\texttt{com.liferay.portlet.single-page-application} property\n  to false:\n\n\\begin{verbatim}\ncom.liferay.portlet.single-page-application=false\n\\end{verbatim}\n\n  If you prefer, you can set this property to false in your\n  \\texttt{liferay-portlet.xml} instead by adding the following property\n  to the \\texttt{\\textless{}portlet\\textgreater{}} section:\n\n\\begin{verbatim}\n<single-page-application>false</single-page-application>\n\\end{verbatim}\n\\item\n  Alternatively, you can override the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-impl/com/liferay/portal/model/impl/PortletImpl.html\\#isSinglePageApplication--}{\\texttt{isSinglePageApplication}\n  method} of the portlet to return \\texttt{false}.\n\\end{enumerate}\n\n\\section{Disabling SPA for an Individual\nElement}\\label{disabling-spa-for-an-individual-element}\n\nTo disable SPA for a form or link follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the \\texttt{data-senna-off} attribute to the element.\n\\item\n  Set the value to \\texttt{true}. See the example below:\n\n\\begin{verbatim}\n<a data-senna-off=\"true\" href=\"/pages/page2.html\">Page 2</a>\n\\end{verbatim}\n\\end{enumerate}\n\nNice! Now you know how to disable SPA in your app.\n\n\\section{Related Topics}\\label{related-topics-106}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configuring-spa-system-settings}{Configuring\n  SPA System Settings}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/specifying-how-resources-are-loaded-during-navigation}{Specifying\n  How Resources Are Loaded During SPA Navigation}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/detaching-global-listeners}{Detaching\n  Global Listeners}\n\\end{itemize}\n\n\\chapter{Specifying How Resources Are Loaded During\nNavigation}\\label{specifying-how-resources-are-loaded-during-navigation}\n\nBy default, Liferay DXP unloads CSS resources from the\n\\texttt{\\textless{}head\\textgreater{}} element on navigation. JavaScript\nresources in the \\texttt{\\textless{}head\\textgreater{}}, however, are\nnot removed on navigation. This functionality can be customized by\nsetting the resource's \\texttt{data-senna-track} attribute. Follow these\nsteps to customize your resources:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select the resource you want to modify the default behavior for.\n\\item\n  Add the \\texttt{data-senna-track} attribute to the resource.\n\\item\n  Set the \\texttt{data-senna-track} attribute to \\texttt{permanent} to\n  prevent a resource from unloading on navigation.\n\n  Alternatively, set the \\texttt{data-senna-track} attribute to\n  \\texttt{temporary} to unload the resource on navigation.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** the `data-senna-track` attribute can be added to resources loaded\n outside of the `<head>` element as well to specify navigation behavior.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nThe example below ensures that the JS resource isn't unloaded during\nnavigation:\n\n\\begin{verbatim}\n<script src=\"myscript.js\" data-senna-track=\"permanent\" />\n\\end{verbatim}\n\nGreat! Now you know how to specify how resources are loaded during SPA\nnavigation.\n\n\\section{Related Topics}\\label{related-topics-107}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configuring-spa-system-settings}{Configuring\n  SPA System Settings}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/disabling-spa}{Disabling\n  SPA}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/detaching-global-listeners}{Detaching\n  Global Listeners}\n\\end{itemize}\n\n\\chapter{Detaching Global Listeners}\\label{detaching-global-listeners}\n\nSPA provides several improvements that highly benefit your site and\nusers, but with great power comes great re-SPA-nsibility. I apologize\nfor that last sentence, but the fact remains that there is potentially\nsome additional maintenance as a consequence of SPA. In a traditional\nnavigation scenario, every page refresh resets everything, so you don't\nhave to worry about what's left behind. In a SPA scenario, however,\nglobal listeners such as \\texttt{Liferay.on}, \\texttt{Liferay.after}, or\nbody delegates can become problematic. Every time you execute these\nglobal listeners, you add yet another listener to the globally persisted\n\\texttt{Liferay} object. The result is multiple invocations of those\nlisteners. This can obviously cause problems if not handled.\n\nFollow these steps to prevent potential problems:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Listen to the navigation event in order to detach your listeners. For\n  example, you would use the following code to listen to a global\n  \\texttt{category} event and \\texttt{destroyPortlet} event:\n\n\\begin{verbatim}\nLiferay.on('category', function(event){...});\nLiferay.on('destroyPortlet', function(event){...});\n\\end{verbatim}\n\\item\n  Detach the event listeners when the portlet is destroyed, as shown in\n  the example below:\n\n\\begin{verbatim}\nvar onCategory = function(event) {...};\n\nvar clearPortletHandlers = function(event) {\n    if (event.portletId === '<%= portletDisplay.getRootPortletId() %>') {\n        Liferay.detach('onCategoryHandler', onCategory);\n        Liferay.detach('destroyPortlet', clearPortletHandlers);\n    }\n};\n\n\nLiferay.on('category', onCategory);\nLiferay.on('destroyPortlet', clearPortletHandlers);\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! Now you know how to properly maintain your global listeners for\nSPA.\n\n\\section{Related Topics}\\label{related-topics-108}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/available-spa-lifecycle-events}{Available\n  SPA Lifecycle Events}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/disabling-spa}{Disabling\n  SPA}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configuring-spa-system-settings}{Configuring\n  SPA System Settings}\n\\end{itemize}\n\n\\chapter{Applying Clay Styles to your\nApp}\\label{applying-clay-styles-to-your-app}\n\nIt's important to have a consistent user experience across your apps.\nPortal's built-in apps achieve this through Liferay's\n\\href{https://liferay.design/lexicon/}{Lexicon Experience Language} and\nits web implementation,\n\\href{https://clayui.com/docs/getting-started/clay.html}{Clay}.\n\nClay provides a consistent, user-friendly UI and is included in all\nthemes that are based on the \\texttt{\\_styled} base theme, making all\nthe components documented on the\n\\href{https://clayui.com/docs/components/alerts.html}{Clay site}\naccessible.\n\nThis means you can use Clay markup and components in your apps. This\nsection explains how to apply Clay's design patterns to achieve the same\nlook and feel as Portal's built-in apps.\n\nThis section covers these topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Applying Clay to navigation\n\\item\n  Implementing the Management Toolbar\n\\end{itemize}\n\n\\chapter{Applying Clay Patterns to\nNavigation}\\label{applying-clay-patterns-to-navigation}\n\nThis article covers how to leverage Clay patterns in your app's\nnavigation to make it more user-friendly. Updating your app's navigation\nbar to use Clay is easy, thanks to the\n\\texttt{\\textless{}clay:navigation-bar\\ /\\textgreater{}} tag. Follow\nthese steps to update your app:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the required imports to your app's \\texttt{init.jsp}:\n\n\\begin{verbatim}\n// Import the clay tld file to be able to use the new tag\n<%@ taglib uri=\"http://liferay.com/tld/clay\" prefix=\"clay\" %>\n\n// Import the NavigationItem utility class to create the items model\n<%@ page import=\"com.liferay.frontend.taglib.clay.servlet.taglib.util.JSPNavigationItemList\" %>\n\\end{verbatim}\n\\item\n  Add the \\texttt{frontend-taglib-clay} and \\texttt{frontend.taglib.soy}\n  module dependencies to your app's \\texttt{build.gradle} file:\n\n\\begin{verbatim}\ncompileOnly group: \"com.liferay\", name: \"com.liferay.frontend.taglib.soy\", \nversion: \"1.0.10\"\n\ncompileOnly group: \"com.liferay\", name: \"com.liferay.frontend.taglib.clay\", \nversion: \"1.0.0\"\n\\end{verbatim}\n\\item\n  Inside your JSP view, add a java scriplet to retrieve the navigation\n  variable and portlet URL. An example configuration is shown below:\n\n\\begin{verbatim}\n<%\nfinal String navigation = ParamUtil.getString(request, \"navigation\", \n\"entries\");\n\nPortletURL portletURL = renderResponse.createRenderURL();\n\nportletURL.setParameter(\"mvcRenderCommandName\", \"/blogs/view\");\nportletURL.setParameter(\"navigation\", navigation);\n%>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}clay:navigation-bar\\ /\\textgreater{}} tag\n  to your app, and use the \\texttt{items} attribute to specify the\n  navigation items. The navigation bar should be dark if your app is\n  intended for Admin use. To do this, set the \\texttt{inverted}\n  attribute to \\texttt{true}. If your app is intended for an instance on\n  a live site, keep the navigation bar light by setting the\n  \\texttt{inverted} attribute to \\texttt{false}. An example\n  configuration for an admin app is shown below:\n\n\\begin{verbatim}\n<clay:navigation-bar\n    inverted=\"<%= true %>\"\n    navigationItems=\"<%=\n        new JSPNavigationItemList(pageContext) {\n            {\n                add(\n                navigationItem -> {\n                    navigationItem.setActive(navigation.equals(\"entries\"));\n                    navigationItem.setHref(renderResponse.createRenderURL());\n                    navigationItem.setLabel(LanguageUtil.get(request, \"entries\"));\n                });\n\n                add(\n                navigationItem -> {\n                    navigationItem.setActive(navigation.equals(\"images\"));\n                    navigationItem.setHref(renderResponse.createRenderURL(), \n          \"navigation\", \"images\");\n                    navigationItem.setLabel(LanguageUtil.get(request, \"images\"));\n                });\n            }\n        }\n    %>\"\n/>\n\\end{verbatim}\n\\item\n  Add a conditional block to display the proper JSP for the selected\n  navigation item. An example configuration for the Blogs Admin portlet\n  is shown below:\n\n\\begin{verbatim}\n<c:choose>\n    <c:when test='<%= navigation.equals(\"entries\") %>'>\n        <liferay-util:include page=\"/blogs_admin/view_entries.jsp\" \n    servletContext=\"<%= application %>\" />\n    </c:when>\n    <c:otherwise>\n        <liferay-util:include page=\"/blogs_admin/view_images.jsp\" \n    servletContext=\"<%= application %>\" />\n    </c:otherwise>\n</c:choose>\n\\end{verbatim}\n\\end{enumerate}\n\nLive site navigation bar:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-patterns-navbar.png}\n\\caption{The navigation bar should be light for apps on the live site.}\n\\end{figure}\n\nAdmin app navigation bar:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-patterns-navbar-inverted.png}\n\\caption{The navigation bar should be dark (inverted) in admin apps.}\n\\end{figure}\n\nSweet! Now you know how to style a navigation bar with Clay.\n\n\\section{Related topics}\\label{related-topics-109}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/implementing-the-management-toolbar}{Implementing\nthe Management Toolbar}\n\n\\chapter{Implementing the Management\nToolbar}\\label{implementing-the-management-toolbar}\n\nThe Management Toolbar is a combination of search, filters, sorting\noptions, and display options that let you manage data. For admin apps,\nwe recommend that you add a management toolbar to manage your search\ncontainer results. The\n\\href{/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar}{Clay\nManagement Toolbar} taglib reference covers how to use the Clay taglibs\nto create the Management Toolbar. This section covers how to create the\nfeatures below for the Management Toolbar:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Implementing View Types\n\\item\n  Sorting and Filtering Items\n\\end{itemize}\n\n\\chapter{Implementing the View Types}\\label{implementing-the-view-types}\n\nThe Management Toolbar has three predefined view types for your app's\nsearch container results. Each style offers a slightly different look\nand feel. To provide these view types in your app, you must make some\nupdates to your search result columns. Start by defining the view types\nyou want to provide.\n\n\\begin{itemize}\n\\item\n  \\textbf{Cards:} displays search result columns on a horizontal or\n  vertical card\n\\item\n  \\textbf{List:} displays a detailed description along with summarized\n  details for the search result columns\n\\item\n  \\textbf{Table:} the default view, which list the search result columns\n  from left to right\n\\end{itemize}\n\nFollow these steps to define the view types for your management toolbar:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the \\texttt{ViewTypeItemList} utility class to create the\n  action items model:\n\n\\begin{verbatim}\n<%@ page import=\"com.liferay.frontend.taglib.clay.servlet.taglib.util.JSPViewTypeItemList\" %>\n\\end{verbatim}\n\\item\n  Add the \\texttt{frontend.taglib.clay} and \\texttt{frontend.taglib.soy}\n  module dependencies to your app's \\texttt{build.gradle} file:\n\n\\begin{verbatim}\ncompileOnly group: \"com.liferay\", name: \"com.liferay.frontend.taglib.soy\", \nversion: \"1.0.10\"\n\ncompileOnly group: \"com.liferay\", name: \"com.liferay.frontend.taglib.clay\", \nversion: \"1.0.0\"\n\\end{verbatim}\n\\item\n  In your app's main view, retrieve the \\texttt{displayStyle} for\n  reference. Each view type corresponds to a display style. this is used\n  to determine the proper content configuration to display for the\n  selected view type:\n\n\\begin{verbatim}\n<%\nString displayStyle = ParamUtil.getString(request, \"displayStyle\");\n%>\n\\end{verbatim}\n\\item\n  Add the management toolbar to your app's main view and configure the\n  display buttons as shown below. Note that while this example\n  implements all three view types, only one view type is required. The\n  default or active view type is set by adding\n  \\texttt{viewTypeItem.setActive(true)} to the view type:\n\n\\begin{verbatim}\n<clay:management-toolbar\n    disabled=<%= assetTagsDisplayContext.isDisabledTagsManagementBar() %>\n    namespace=\"<%= renderResponse.getNamespace() %>\"\n    searchContainerId=\"assetTags\"\n    selectable=\"<%= true %>\"\n    viewTypeItems=\"<%=\n        new JSPViewTypeItemList(pageContext, baseURL, selectedType) {\n            {\n                addCardViewTypeItem(\n                    viewTypeItem -> {\n                        viewTypeItem.setActive(true);\n                        viewTypeItem.setLabel(\"Card\");\n                    });\n\n                addListViewTypeItem(\n                    viewTypeItem -> {\n                        viewTypeItem.setLabel(\"List\");\n                    });\n\n                addTableViewTypeItem(\n                    viewTypeItem -> {\n                        viewTypeItem.setLabel(\"Table\");\n                    });\n            }\n        }\n    %>\"\n/>\n\\end{verbatim}\n\n  \\texttt{viewTypeItems}: The available view types\n\\item\n  Create a conditional block to check for the view types. If you only\n  have one view type, you can skip this step.\n\n\\begin{verbatim}\n<c:choose>\n    <%-- view type configuration goes here --%>\n</c:choose>\n\\end{verbatim}\n\\end{enumerate}\n\nNow that the view types are defined, you can follow the instructions in\nthe rest of this section to configure them.\n\n\\chapter{Implementing the Card View}\\label{implementing-the-card-view}\n\nThe card view displays the entry's information in a vertical or\nhorizontal card with an image, user profile, or an icon representing the\ncontent's type, along with details about the content, such as its name,\nworkflow status, and a condensed description.\n\nSee the\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-cards}{Liferay\nFrontend Cards} reference for examples and use cases of each card.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-view-type-card.png}\n\\caption{The Management Toolbar's card view gives a quick summary of the\ncontent's description and status.}\n\\end{figure}\n\nFollow the steps below to create your card view:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Inside the \\texttt{\\textless{}c:choose\\textgreater{}} conditional\n  block, add a condition for the icon display style (Card view type):\n\n\\begin{verbatim}\n<c:when test='<%= Objects.equals(displayStyle, \"icon\") %>'>\n    <%-- card view type configuration goes here --%>\n</c:when>\n\\end{verbatim}\n\\item\n  Add the proper java scriplet to make the card view responsive to\n  different devices:\n\n  Use the pattern below for vertical cards:\n\n\\begin{verbatim}\n<%\nrow.setCssClass(\"col-md-2 col-sm-4 col-xs-6\");\n%>\n\\end{verbatim}\n\n  For horizontal cards use the following pattern:\n\n\\begin{verbatim}\n<%\nrow.setCssClass(\"col-md-3 col-sm-4 col-xs-12\");\n%>\n\\end{verbatim}\n\\item\n  Add the search container column text containing your card. The card\n  should include the actions for the entry(if applicable), as well as an\n  image, icon or user profile, and the entry's title. An example\n  configuration is shown below:\n\n\\begin{verbatim}\n<liferay-frontend:icon-vertical-card\n  actionJsp='<%= dlPortletInstanceSettingsHelper.isShowActions() ? \n  \"/image_gallery_display/image_action.jsp\" : StringPool.BLANK %>'\n  actionJspServletContext=\"<%= application %>\"\n  cssClass=\"entry-display-style\"\n  icon=\"documents-and-media\"\n  resultRow=\"<%= row %>\"\n  title=\"<%= dlPortletInstanceSettingsHelper.isShowActions() ? \n  fileEntry.getTitle() : StringPool.BLANK %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! Now you know how to implement the Card view!\n\n\\section{Related Topics}\\label{related-topics-110}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar}{Configuring\n  the Clay Management Toolbar Taglib}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/filtering-and-sorting-items-with-the-management-toolbar}{Filtering\n  and Sorting Items with the Management Toolbar}\n\\end{itemize}\n\n\\chapter{Implementing the List View}\\label{implementing-the-list-view}\n\nThe list view displays the entry's complete description, along with a\nsmall icon for the content type, and its name.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-view-type-list.png}\n\\caption{The Management Toolbar's list view gives the content's full\ndescription.}\n\\end{figure}\n\nFollow these steps to configure the List view:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Inside the \\texttt{\\textless{}c:choose\\textgreater{}} conditional\n  block, add a condition for the descriptive display style (list view\n  type):\n\n\\begin{verbatim}\n<c:when test='<%= Objects.equals(displayStyle, \"descriptive\") %>'>\n    <%-- list view type configuration goes here --%>\n</c:when>\n\\end{verbatim}\n\\item\n  Follow the example below to add the three columns to the conditional\n  block:\n\\end{enumerate}\n\nColumn \\textbar{} Content Options \\textbar{} Example 1 \\textbar{} icon\n\\textbar{}\n\\textless liferay-ui:search-container-column-icon/\\textgreater{} ~\n\\textbar{} image \\textbar{}\n\\textless liferay-ui:search-container-column-image/\\textgreater{} ~\n\\textbar{} user portrait \\textbar{}\n\\textless liferay-ui:search-container-column-text\\textgreater{}~~\\textless liferay-ui:user-portrait/\\textgreater{}\\textless/liferay-ui:search-container-column-text\\textgreater{}\n2 \\textbar{} description \\textbar{}\n\\textless liferay-ui:search-container-column-text\n~~colspan=``\\textless\\%=2\\%\\textgreater{}''\n\\textgreater{}~~\\textless h5\\textgreater\\textless\\%= userGroup.getName()\n\\%\\textgreater\\textless/h5\\textgreater{} ~~\\textless h6\nclass=``text-default''\\textgreater{}\n~~~~\\textless span\\textgreater\\textless\\%= userGroup.getDescription()\n\\%\\textgreater\\textless/span\\textgreater{} ~~\\textless/h6\\textgreater{}\n~~\\textless h6 class=``text-default''\\textgreater{}\n~~~~\\textless span\\textgreater{} ~~~~~~~~''\nkey=``x-users''/\\textgreater{} ~~~~\\textless/span\\textgreater{}\n~~\\textless/h6\\textgreater{} 3 \\textbar{} actions \\textbar{}\n\\textless liferay-ui:search-container-column-jsp\n~~path=``/edit\\_team\\_assignments\\_user\\_groups\\_action.jsp''/\\textgreater{}\n\nGreat! Now you know how to implement the List view!\n\n\\section{Related Topics}\\label{related-topics-111}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar}{Configuring\n  the Clay Management Toolbar Taglib}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/filtering-and-sorting-items-with-the-management-toolbar}{Filtering\n  and Sorting Items with the Management Toolbar}\n\\end{itemize}\n\n\\chapter{Implementing the Table View}\\label{implementing-the-table-view}\n\nThe table view list the search container columns from left to right.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-view-type-table.png}\n\\caption{The Management Toolbar's table view list the content's\ninformation in individual columns.}\n\\end{figure}\n\nFollow these steps to configure the Table view:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Inside the \\texttt{\\textless{}c:choose\\textgreater{}} conditional\n  block, add a condition for the list display style (table view type):\n\n\\begin{verbatim}\n<c:when test='<%= Objects.equals(displayStyle, \"list\") %>'>\n  <%-- table view type configuration goes here --%>\n</c:when>\n\\end{verbatim}\n\\item\n  Follow the example below to add the required columns to the\n  conditional block:\n\\end{enumerate}\n\nColumn \\textbar{} Content Options \\textbar{} Example 1 \\textbar{} name\n\\textbar{} \\textless liferay-ui:search-container-column-text\n~~cssClass=``content-column name-column title-column'' ~~name=``name''\n~~truncate=``\\textless\\%= true \\%\\textgreater{}'' ~~value=``\\textless\\%=\nrule.getName(locale) \\%\\textgreater{}'' /\\textgreater{} 2 \\textbar{}\ndescription \\textbar{} \\textless liferay-ui:search-container-column-text\n~~cssClass=``content-column description-column'' ~~name=``description''\n~~truncate=``\\textless\\%= true \\%\\textgreater{}'' ~~value=``\\textless\\%=\nrule.getDescription(locale) \\%\\textgreater{}'' /\\textgreater{} 3\n\\textbar{} create date \\textbar{}\n\\textless liferay-ui:search-container-column-date\n~~cssClass=``create-date-column text-column'' ~~name=``create-date''\n~~property=``createDate'' /\\textgreater{} 4 \\textbar{} actions\n\\textbar{} \\textless liferay-ui:search-container-column-jsp ~~\ncssClass=``entry-action-column'' ~~path=``/rule\\_actions.jsp''\n/\\textgreater{}\n\nGreat! Now you know how to implement the Table view!\n\n\\section{Related Topics}\\label{related-topics-112}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar}{Configuring\n  the Clay Management Toolbar Taglib}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/filtering-and-sorting-items-with-the-management-toolbar}{Filtering\n  and Sorting Items with the Management Toolbar}\n\\end{itemize}\n\n\\chapter{Updating the Search\nIterator}\\label{updating-the-search-iterator}\n\nOnce the view type's display styles are defined, you must update the\nsearch iterator to show the selected view type. Follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If your management toolbar only has one view type, you can set the\n  \\texttt{displayStyle} attribute to the style you defined, otherwise\n  follow the pattern below:\n\n\\begin{verbatim}\n<liferay-ui:search-iterator\n    displayStyle=\"<%= displayStyle %>\"\n    markupView=\"lexicon\"\n    searchContainer=\"<%= searchContainer %>\"\n/>\n\\end{verbatim}\n\n  The \\texttt{displayStyle}'s value is set to the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/implementing-the-view-types}{current\n  view type}.\n\\item\n  Save the changes.\n\\end{enumerate}\n\nGreat! You've updated your search iterator to use your display styles.\n\n\\section{Related Topics}\\label{related-topics-113}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar}{Configuring\n  the Clay Management Toolbar Taglib}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/filtering-and-sorting-items-with-the-management-toolbar}{Filtering\n  and Sorting Items with the Management Toolbar}\n\\end{itemize}\n\n\\chapter{Filtering and Sorting Items with the Management\nToolbar}\\label{filtering-and-sorting-items-with-the-management-toolbar}\n\nThe Management Toolbar lets you filter and sort your search container\nresults. While you can configure the toolbar's filters in the JSP, this\ncan quickly crowd the JSP. We recommend instead that you move this\nfunctionality to a separate java class, which we refer to as a Display\nContext throughout this tutorial.\n\nThere are two main types of filters: navigation and order. Both of these\nare contained within the same dropdown menu. Follow the steps below to\ncreate your filters.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Depending on your needs, there are two classes that you can extend for\n  your management toolbar Display Context. These base classes provide\n  the required methods to create your navigation and order filters:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-taglib/frontend-taglib-clay/src/main/java/com/liferay/frontend/taglib/clay/servlet/taglib/display/context/BaseManagementToolbarDisplayContext.java}{\\texttt{BaseManagementToolbarDisplayContext}}:\n    for apps without a search container\n  \\item\n    \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-taglib/frontend-taglib-clay/src/main/java/com/liferay/frontend/taglib/clay/servlet/taglib/display/context/SearchContainerManagementToolbarDisplayContext.java}{\\texttt{SearchContainerManagementToolbarDisplayContext}}:\n    for apps with a search container (extends\n    \\texttt{BaseManagementToolbarDisplayContext} and provides additional\n    logic for search containers)\n  \\end{itemize}\n\n  An example configuration for each is shown below:\n\n  \\texttt{BaseManagementToolbarDisplayContext} example:\n\n\\begin{verbatim}\npublic class MyManagementToolbarDisplayContext\n  extends BaseManagementToolbarDisplayContext {\n\n  public MyManagementToolbarDisplayContext(\n    LiferayPortletRequest liferayPortletRequest,\n    LiferayPortletResponse liferayPortletResponse,\n    HttpServletRequest request) {\n\n    super(liferayPortletRequest, liferayPortletResponse, request);\n  }\n  ...\n}\n\\end{verbatim}\n\n  \\texttt{SearchContainerManagementToolbarDisplayContext} example:\n\n\\begin{verbatim}\npublic class MyManagementToolbarDisplayContext\n  extends SearchContainerManagementToolbarDisplayContext {\n\n  public MyManagementToolbarDisplayContext(\n    LiferayPortletRequest liferayPortletRequest,\n    LiferayPortletResponse liferayPortletResponse,\n    HttpServletRequest request, SearchContainer searchContainer) {\n\n    super(\n      liferayPortletRequest, liferayPortletResponse, request,\n      searchContainer);\n  }\n}\n\\end{verbatim}\n\\item\n  Override the \\texttt{getNavigationKeys()} method to return the\n  navigation filter dropdown item(s). If your app doesn't require any\n  navigation filters, you can just provide the \\emph{all} filter to\n  display everything. An example configuration is shown below:\n\n\\begin{verbatim}\n@Override\nprotected String[] getNavigationKeys() {\n  return new String[] {\"all\", \"pending\", \"done\"};\n}\n\\end{verbatim}\n\\item\n  override the \\texttt{getOrderByKeys()} method to return the columns to\n  order. An example configuration is shown below:\n\n\\begin{verbatim}\n@Override\nprotected String[] getOrderByKeys() {\n  return new String[] {\"name\", \"items\", \"status\"};\n}\n\\end{verbatim}\n\\item\n  Open the JSP view that contains the Clay Management Toolbar and set\n  its \\texttt{displayContext} attribute to the Display Context you\n  created. An example configuration is shown below:\n\n\\begin{verbatim}\n<clay:management-toolbar\n    displayContext=\"<%= myManagementToolbarDisplayContext %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\nNow you know how to configure the Management Toolbar's filters via a\nDisplay Context.\n\n\\section{Related Topics}\\label{related-topics-114}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar\\#filtering-and-sorting-search-results}{Configuring\n  Filtering and Sorting Management Toolbar Attributes}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/implementing-the-view-types}{Implementing\n  the View Types}\n\\end{itemize}\n\n\\chapter{Configuring Your Application's Title and Back\nLink}\\label{configuring-your-applications-title-and-back-link}\n\nFor administration applications, the title should be moved to the inner\nviews of the app and the associated back link should be moved to the\nportlet titles. If you open the Blogs Admin application in the Control\nPanel and add a new blog entry, you'll see this behavior in action:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/new-blog-entry-title.png}\n\\caption{Adding a new blog entry displays the portlet title at the top,\nalong with a back link.}\n\\end{figure}\n\nThe Blogs Admin application is used as an example throughout this\narticle. Follow these steps to configure your app's title and back URL:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Use \\texttt{ParamUtil} to retrieve the redirect for the URL:\n\n\\begin{verbatim}\nString redirect = ParamUtil.getString(request, \"redirect\");\n\\end{verbatim}\n\\item\n  Display the back icon and set the back URL to the \\texttt{redirect}:\n\n\\begin{verbatim}\nportletDisplay.setShowBackIcon(true);\nportletDisplay.setURLBack(redirect);\n\\end{verbatim}\n\\item\n  Finally, set the title using the \\texttt{renderResponse.setTitle()}\n  method. The example below provides a title for two scenarios:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    If an existing blog entry is being updated, the blog's title is\n    displayed.\n  \\item\n    Otherwise it defaults to \\emph{New Blog Entry} since a new blog\n    entry is being created.\n  \\end{itemize}\n\n\\begin{verbatim}\nrenderResponse.setTitle((entry != null) ? entry.getTitle() : \nLanguageUtil.get(request, \"new-blog-entry\"));\n%>\n\\end{verbatim}\n\\item\n  Update any back links in the view to use the \\texttt{redirect}. The\n  Blog Admin app's \\texttt{edit\\_entry.jsp} form's cancel button is\n  shown below as an example:\n\n\\begin{verbatim}\n<aui:button cssClass=\"btn-lg\" href=\"<%= redirect %>\" name=\"cancelButton\" \ntype=\"cancel\" />\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! Now you know how to configure your app's title and back URL.\n\n\\section{Related Topics}\\label{related-topics-115}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/applying-clay-patterns-to-navigation}{Applying\n  Clay Patterns to Your Navigation Bar}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/setting-empty-results-messages}{Setting\n  Empty Results Messages}\n\\end{itemize}\n\n\\chapter{Applying the Add Button\nPattern}\\label{applying-the-add-button-pattern}\n\nClay's add button pattern is for actions that add entities (for example\na new blog entry button). It gives you a clean, minimal UI. You can use\nit in any of your app's screens. Follow these steps to add a plus button\nto your app:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the \\texttt{liferay-frontend} taglib declaration to your portlet's\n  \\texttt{init.jsp}:\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n\\end{verbatim}\n\\item\n  Add an\n  \\href{https://docs.liferay.com/dxp/apps/frontend-taglib/latest/taglibdocs/liferay-frontend/add-menu.html}{\\texttt{add-menu}\n  tag} to your portlet's view:\n\n\\begin{verbatim}\n<liferay-frontend:add-menu>\n</liferay-frontend:add-menu>\n\\end{verbatim}\n\\item\n  Nest a\n  \\href{https://docs.liferay.com/dxp/apps/frontend-taglib/latest/taglibdocs/liferay-frontend/add-menu-item.html}{\\texttt{\\textless{}liferay-frontend:add-menu-item\\textgreater{}}}\n  tag for every menu item you have. Here's an example of the add button\n  pattern with a single item:\n\n\\begin{verbatim}\n<liferay-frontend:add-menu>\n    <liferay-frontend:add-menu-item \n      title='<%= LanguageUtil.get(request,\"titleName\") %>' \n      url=\"<%= nameURL.toString() %>\" \n    />\n</liferay-frontend:add-menu>\n\\end{verbatim}\n\n  If there's only one item, the plus icon acts as a button that triggers\n  the item. If there's multiple items, clicking the plus icon displays a\n  menu containing them.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/add-button-diagram.png}\n  \\caption{The add button pattern consists of an \\texttt{add-menu} tag\n  and at least one \\texttt{add-menu-item} tag.}\n  \\end{figure}\n\\end{enumerate}\n\nThe \\texttt{com.liferay.mobile.device.rules.web} module's add menu is\nshown below:\n\n\\begin{verbatim}\n<liferay-frontend:add-menu\n  inline=\"<%= true %>\"\n>\n  <liferay-frontend:add-menu-item\n    title='<%= LanguageUtil.get(resourceBundle, \"add-device-family\") %>'\n    url=\"<%= addRuleGroupURL %>\"\n  />\n</liferay-frontend:add-menu>\n\\end{verbatim}\n\nThere you have it! Now you know how to use the add button pattern.\n\n\\section{Related Topics}\\label{related-topics-116}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/setting-empty-results-messages}{Setting\n  Empty Results Messages}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/implementing-the-management-toolbar}{Implementing\n  the Management Toolbar}\n\\end{itemize}\n\n\\chapter{Configuring Your Admin App's Actions\nMenu}\\label{configuring-your-admin-apps-actions-menu}\n\nRather then have a series of buttons or menus with actions in the\ndifferent views of the app, you can move all of these actions to the\nupper right menu of the administrative portlet, leaving the primary\naction (often an\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/applying-the-add-button-pattern}{``Add''\noperation}) visible in the add menu. For example, the web content\napplication has the actions menu shown below:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/actions-menu.png}\n\\caption{The upper right ellipsis menu contains most of the actions for\nthe app.}\n\\end{figure}\n\nFollow these steps to configure the actions menu in your admin app:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a \\texttt{*ConfigurationIcon} Component class for the action\n  that extends the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/configuration/icon/BasePortletConfigurationIcon.html}{\\texttt{BasePortletConfigurationIcon}\n  class} and implements the \\texttt{PortletConfigurationIcon} service:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    service = PortletConfigurationIcon.class\n)\npublic class MyConfigurationIcon extends BasePortletConfigurationIcon {\n  ...\n}\n\\end{verbatim}\n\\item\n  Override the \\texttt{getMessage()} method to specify the action's\n  label:\n\n\\begin{verbatim}\n@Override\npublic String getMessage(PortletRequest portletRequest) {\n  ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(\n    WebKeys.THEME_DISPLAY);\n\n  ResourceBundle resourceBundle = ResourceBundleUtil.getBundle(\n    themeDisplay.getLocale(), ExportAllConfigurationIcon.class);\n\n  return LanguageUtil.get(resourceBundle, \"export-all-settings\");\n}\n\\end{verbatim}\n\\item\n  Override the \\texttt{get()} method to specify whether the action is\n  invoked with the \\texttt{GET} or \\texttt{POST} method:\n\n\\begin{verbatim}\n@Override\npublic String getMethod() {\n    return \"GET\";\n}\n\\end{verbatim}\n\\item\n  Override the \\texttt{getURL()} method to specify the URL (or\n  \\texttt{onClick} JavaScript method) to invoke when the action is\n  clicked:\n\n\\begin{verbatim}\n@Override\npublic String getURL(\n  PortletRequest portletRequest, PortletResponse portletResponse) {\n\n  LiferayPortletURL liferayPortletURL =\n    (LiferayPortletURL)_portal.getControlPanelPortletURL(\n      portletRequest, ConfigurationAdminPortletKeys.SYSTEM_SETTINGS,\n      PortletRequest.RESOURCE_PHASE);\n\n  liferayPortletURL.setResourceID(\"export\");\n\n  return liferayPortletURL.toString();\n}\n\\end{verbatim}\n\\item\n  Override the \\texttt{getWeight()} method to specify the order that the\n  action should appear in the list:\n\n\\begin{verbatim}\n@Override\npublic double getWeight() {\n  return 1;\n}\n\\end{verbatim}\n\\item\n  Override the \\texttt{isShow()} method to specify the context in which\n  the action should be displayed:\n\n\\begin{verbatim}\n@Override\npublic boolean isShow(PortletRequest portletRequest) {\n    return true;\n}\n\\end{verbatim}\n\\item\n  Define the view where you want to display the configuration options.\n  By default, if the portlet uses \\texttt{mvcPath}, the global actions\n  (such as configuration, export/import, maximized, etc.) are displayed\n  for the JSP indicated in the initialization parameter of the portlet\n  \\texttt{javax.portlet.init-param.view-template=/view.jsp}. The value\n  indicates the JSP where the global actions should be displayed.\n  However, if the portlet uses MVC Command, the views for the global\n  actions must be indicated with the initialization parameter\n  \\texttt{javax.portlet.init-param.mvc-command-names-default-views=/wiki\\_admin/view}\n  and the value must contain the \\texttt{mvcRenderCommandName} where the\n  global actions should be displayed.\n\\item\n  If the portlet can be added to a page and you want to always include\n  the configuration options, add this initialization parameter to the\n  portlet's properties:\n\n\\begin{verbatim}\njavax.portlet.init-param.always-display-default-configuration-icons=true\n\\end{verbatim}\n\n  In this example, the action appears in the System Settings portlet. To\n  make it appear in a secondary screen, you can use the \\texttt{path}\n  property as shown below. The value of the \\texttt{path} property\n  depends on the MVC framework used to develop the app. For the\n  MVCPortlet framework, provide the path (often a JSP) from the\n  \\texttt{mvcPath} parameter. For MVCPortlet with MVC Commands, the path\n  should contain the \\texttt{mvcRenderCommandName} where the actions\n  should be displayed (such as \\texttt{/document\\_library/edit\\_folder}\n  for example):\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"javax.portlet.name=\" + ConfigurationAdminPortletKeys.SYSTEM_SETTINGS,\n        \"path=/view_factory_instances\"\n    },\n    service = PortletConfigurationIcon.class\n)\npublic class ExportFactoryInstancesIcon extends BasePortletConfigurationIcon {\n\n    @Override\n    public String getMessage(PortletRequest portletRequest) {\n        ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(\n            WebKeys.THEME_DISPLAY);\n\n        ResourceBundle resourceBundle = ResourceBundleUtil.getBundle(\n            themeDisplay.getLocale(), ExportFactoryInstancesIcon.class);\n\n        return LanguageUtil.get(resourceBundle, \"export-entries\");\n    }\n\n    @Override\n    public String getMethod() {\n        return \"GET\";\n    }\n\n    @Override\n    public String getURL(\n        PortletRequest portletRequest, PortletResponse portletResponse) {\n\n        LiferayPortletURL liferayPortletURL =\n            (LiferayPortletURL)_portal.getControlPanelPortletURL(\n                portletRequest, ConfigurationAdminPortletKeys.SYSTEM_SETTINGS,\n                PortletRequest.RESOURCE_PHASE);\n\n        ConfigurationModel factoryConfigurationModel =\n            (ConfigurationModel)portletRequest.getAttribute(\n                ConfigurationAdminWebKeys.FACTORY_CONFIGURATION_MODEL);\n\n        liferayPortletURL.setParameter(\n            \"factoryPid\", factoryConfigurationModel.getFactoryPid());\n\n        liferayPortletURL.setResourceID(\"export\");\n\n        return liferayPortletURL.toString();\n    }\n\n    @Override\n    public double getWeight() {\n        return 1;\n    }\n\n    @Override\n    public boolean isShow(PortletRequest portletRequest) {\n        ConfigurationModelIterator configurationModelIterator =\n            (ConfigurationModelIterator)portletRequest.getAttribute(\n                ConfigurationAdminWebKeys.CONFIGURATION_MODEL_ITERATOR);\n\n        if (configurationModelIterator.getTotal() > 0) {\n            return true;\n        }\n\n        return false;\n    }\n\n    @Reference\n    private Portal _portal;\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\nThis covers some of the available methods. See the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/configuration/icon/BasePortletConfigurationIcon.html}{Javadoc}\nfor a complete list of the available methods.\n\nGreat! Now you know how to configure your admin app's actions.\n\n\\section{Related Topics}\\label{related-topics-117}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/applying-clay-patterns-to-navigation}{Applying\n  Clay Patterns to Your Navigation Bar}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configuring-your-applications-title-and-back-link}{Configuring\n  Your Application's Title and Back Link}\n\\end{itemize}\n\n\\chapter{Setting Empty Results\nMessages}\\label{setting-empty-results-messages}\n\nIf you've toured the UI, you've probably noticed messages or possibly\neven animations in the search containers when no results are found.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/no-web-content-found.png}\n\\caption{This is a still frame from the Web Content portlet's empty\nresults animation.}\n\\end{figure}\n\nYou can configure your apps to use empty results messages and animations\ntoo, thanks to the \\texttt{liferay-frontend:empty-results-message} tag.\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the \\texttt{liferay-frontend} taglib declaration into your\n  portlet's \\texttt{init.jsp}:\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n\\end{verbatim}\n\\item\n  Add an\n  \\href{https://docs.liferay.com/dxp/apps/frontend-taglib/latest/taglibdocs/liferay-frontend/empty-result-message.html}{\\texttt{empty-result-message}\n  tag} to your portlet's view:\n\n\\begin{verbatim}\n<liferay-frontend:empty-result-message />\n\\end{verbatim}\n\\item\n  Configure the tag's attributes to define your search container's empty\n  results message, with or without an animation or image. The attributes\n  are described in the table below:\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n Attribute | Description |\n --- | --- |\n `actionDropdownItems` | Specifies the action or actions to display for the empty results in either a dropdown menu, a link, or a button, depending on the number of available actions. |\n `animationType` | The CSS class for the animation. Four values are available by default with these CSS classes: `EmptyResultMessageKeys.AnimationType.EMPTY` (`taglib-empty-state`), `EmptyResultMessageKeys.AnimationType.SEARCH` (`taglib-search-state`), `EmptyResultMessageKeys.AnimationType.SUCCESS` (`taglib-success-state`), and `EmptyResultMessageKeys.AnimationType.NONE`. You can also specify a custom CSS class if you prefer. |\n `componentId` | Specifies the ID for the `actionDropdownItems` component (dropdown menu, link, or button)|\n `description` | The descriptive text to display beneath the main message. |\n `elementType` | The type of element to replace the `x` parameter in the main message's language key `no-x-yet`. |\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nAn example configuration is shown below:\n\n```markup\n<liferay-frontend:empty-result-message\n    actionDropdownItems=\"<%= (availableSegmentsEntries.size() > 0) ? \n    editAssetListDisplayContext.getAssetListEntryVariationActionDropdownItems() : null %>\"\n    animationType=\"<%= EmptyResultMessageKeys.AnimationType.NONE %>\"\n    componentId='<%= renderResponse.getNamespace() + \"emptyResultMessageComponent\" %>'\n    description='<%= LanguageUtil.get(request, \"no-personalized-variations-were-found\") %>'\n    elementType='<%= LanguageUtil.get(request, \"personalized-variations\") %>'\n/>\n```\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You can replace the available default animations with your own \n by overriding the `taglib-empty-state`, `taglib-search-state`, and \n `taglib-success-state` CSS classes provided by \n [_empty_result_message.scss](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-css/frontend-css-web/src/main/resources/META-INF/resources/taglib/_empty_result_message.scss), \n or by replacing the existing animations in the `@theme_image_path@/states/` \n folder. Alternatively, you can also provide a new CSS class that defines \n the animation and use it for the `animationType` attribute's value. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nempty\\_state.gif:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/empty_state.gif}\n\\caption{If you can use the add button to add entities to the app, use\nthe empty state animation.}\n\\end{figure}\n\nsearch\\_state.gif:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search_state.gif}\n\\caption{If you can use the add button to add entities to the app, use\nthe search state animation.}\n\\end{figure}\n\nsuccess\\_state.gif:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/success_state.gif}\n\\caption{If you can use the add button to add entities to the app, use\nthe success state animation.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Empty results messages can also contain static images if\nyou prefer. Just use a valid image type instead. All animations must be\nof type \\texttt{GIF} though.\n\n\\noindent\\hrulefill\n\nGreat! Now you know how to configure your app to display an empty\nresults message.\n\n\\section{Related Topics}\\label{related-topics-118}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-front-end-taglibs-in-your-portlet}{Using\n  the Liferay Front-End Taglib}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/applying-the-add-button-pattern}{Applying\n  the Add Button Pattern}\n\\end{itemize}\n\n\\chapter{Search}\\label{search}\n\nLiferay DXP contains a search engine based on\n\\href{https://www.elastic.co/products/elasticsearch}{Elasticsearch}. You\ncan extend it, implement search for your own applications, and it's\nhighly configurable.\n\n\\section{Basic Search Concepts}\\label{basic-search-concepts}\n\n\\textbf{Indexing}: During indexing, a document is sent to the search\nengine. This document contains fields of various types (string, etc.).\nThe search engine processes each field and determines whether to store\nthe field or analyze it. Index time analysis can be configured for each\nfield (see Mapping Definitions).\n\nFor fields requiring analysis, the search engine first tokenizes the\nvalue to obtain individual words or tokens. Then it passes each token\nthrough a series of analyzers, which perform different functions. Some\nremove common words or stop words (e.g., ``the'', ``and'', ``or'') while\nothers perform operations like lowercasing all characters.\n\n\\textbf{Searching}: Searching involves sending a search query and\nobtaining results (a.k.a. hits) from the search engine. The search query\nmight contain both queries and filters (more on this later). Queries and\nfilters specify a field to search within and the value to match against.\nThe search engine iterates through each field within the nested queries\nand filters and may perform special analysis prior to executing the\nquery (search time analysis). Search time analysis can be configured for\neach field (see Mapping Definitions).\n\n\\section{Mapping Definitions}\\label{mapping-definitions}\n\n\\emph{Mappings} control how a search engine processes a given field. For\ninstance, if a field name ends in ``es\\_ES'', it should be processed as\nSpanish, removing any common Spanish words like ``si''.\n\nIn Elasticsearch and Solr, the two supported search engines for Liferay\nDXP, mappings are defined in \\texttt{liferay-type-mappings.json} and\n\\texttt{schema.xml}, respectively.\n\nThe Elasticsearch mapping JSON file is in the Liferay DXP\n\\href{https://github.com/liferay/liferay-portal}{source code}, in the\n\\texttt{portal-search-elasticsearch6} module:\n\n\\begin{verbatim}\nportal-search-elasticsearch6-impl/src/main/resources/META-INF/mappings/liferay-type-mappings.json\n\\end{verbatim}\n\nThe Solr \\texttt{schema.xml} is in the \\texttt{portal-search-solr7}\nmodule's source code:\n\n\\begin{verbatim}\nportal-search-solr7-impl/src/main/resources/META-INF/resources/schema.xml\n\\end{verbatim}\n\nAccess the Solr 7 module's source code from the \\texttt{liferay-portal}\nrepository\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-search-solr7/portal-search-solr7-impl/src/main/resources/META-INF/resources/schema.xml}{here}.\n\nYou can customize these mappings to fit your needs. For example, you\nmight want to use a special analyzer for a custom inventory number\nfield.\n\n\\section{Liferay Search\nInfrastructure}\\label{liferay-search-infrastructure}\n\nSearch engines already provide native APIs, but Liferay wraps the engine\nwith a search infrastructure that does several things:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Index documents with the fields Liferay needs\n  (\\texttt{entryClassName}, \\texttt{entryClassPK},\n  \\texttt{assetTagNames}, \\texttt{assetCategories}, \\texttt{companyId},\n  \\texttt{groupId}, staging status, etc.).\n\\item\n  Apply the right filters to search queries (e.g., for scoping results).\n\\item\n  Apply permission checking on the results.\n\\item\n  Summarize returned results.\n\\end{enumerate}\n\nThat's just a taste of Liferay's Search Infrastructure. Continue reading\nto learn more.\n\n\\chapter{Aggregations}\\label{aggregations}\n\nAggregations take the results of a query and group the data into logical\nsets. Aggregations can be composed to provide complex data summaries.\n\n7.0 has a new API that exposes\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations.html}{Elasticsearch's\nnative Aggregation functionality}.\n\nCurrently, these aggregation types are supported:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-bucket.html}{Bucketing\n  aggregations} create buckets of documents based on some criterion.\n  They support sub-aggregations.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Supported bucket aggregations include children aggregations, date\n    histogram aggregations, date range aggregations, diversified sampler\n    aggregations, filter aggregations, filters aggregations, geo\n    distance aggregations, geo hash grid aggregations, global\n    aggregations, histogram aggregations, missing aggregations, nested\n    aggregations, range aggregations, reverse nested aggregations,\n    sample aggregations, significant terms aggregations, significant\n    text aggregations, and terms aggregations.\n  \\end{itemize}\n\\item\n  \\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-metrics.html}{Metrics\n  aggregations} compute metrics over a set of documents.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Supported metrics aggregations include average aggregations,\n    cardinality aggregations, extended stats aggregations, geo bounds\n    aggregations, geo centroid aggregations, max aggregations, min\n    aggregations, percentile ranks aggregations, percentiles\n    aggregations, scripted metric aggregations, stats aggregations, sum\n    aggregations, top hits aggregations, value count aggregations, and\n    weighted average aggregations.\n  \\end{itemize}\n\\item\n  \\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-pipeline.html}{Pipeline\n  aggregations} aggregate the output of other aggregations and their\n  associated metrics.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Supported pipeline aggregations include average bucket pipeline\n    aggregations, bucket metrics pipeline aggregations, bucket script\n    pipeline aggregations, bucket selector pipeline aggregations, bucket\n    sort pipeline aggregations, cumulative sum pipeline aggregations,\n    derivative pipeline aggregations, extended stats bucket pipeline\n    aggregations, max bucket pipeline aggregations, min bucket pipeline\n    aggregations, moving function pipeline aggregations, percentiles\n    bucket pipeline aggregations, pipeline aggregations, serial diff\n    pipeline aggregations, stats bucket pipeline aggregations, and sum\n    bucket pipeline aggregations.\n  \\end{itemize}\n\\end{itemize}\n\nAll the supported aggregations are found in the\n\\texttt{portal-search-api} module's\n\\texttt{com.liferay.portal.search.aggregation} package.\n\nIn addition to these aggregations, other aggregation-like features are\npresent in the Liferay DXP search API:\n\n\\textbf{Group By} collects search results (documents) based on a\nparticular field. For example, if you want to group the search results\nbased on the asset type (e.g., web content article, document, blog post,\netc.), you can create a search query that contains a\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/portal-kernel/src/com/liferay/portal/kernel/search/GroupBy.java}{com.liferay.portal.kernel.search.GroupBy}\naggregation with the field \\texttt{entryClassName}.\n\nYou can specify these attributes for returned groups:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  The maximum number of results in each group\n\\item\n  Special sorting for the grouped results\n\\end{itemize}\n\n\\textbf{Facets} act like bucket aggregations, holding results that share\na certain characteristic.\n\n\\section{Using Aggregations}\\label{using-aggregations}\n\nThe generalized approach for using aggregations in your own search code\nis like this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Instantiate and construct the aggregation object\n\\item\n  Add the aggregation information to the search request\n\\item\n  Process the search response\n\\end{enumerate}\n\nThese steps are covered in more detail (with examples)\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-aggregations-in-low-level-search-calls}{here}.\n\n\\section{External References}\\label{external-references}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\url{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations.html}\n\\item\n  \\url{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations.html\\#_structuring_aggregations}\n\\end{itemize}\n\n\\section{Search Engine Connector\nSupport}\\label{search-engine-connector-support}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Elasticsearch 6: Yes\n\\item\n  Solr 7: No\n\\end{itemize}\n\n\\section{New/Related APIs}\\label{newrelated-apis}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.2558}}\n  >{\\centering\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.5116}}\n  >{\\centering\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.2326}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nAPI (FQCN)\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\centering\nProvided by Artifact\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\centering\nNotes\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{com.liferay.portal.search.aggregation.*} &\ncom.liferay.portal.search.api & The whole\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/aggregation}{``aggregation''\npackage} is new as of 7.0 \\\\\n\\end{longtable}\n\n\\chapter{Creating Aggregations}\\label{creating-aggregations}\n\nEach aggregation has a different purpose and should be processed\ndifferently once returned from the search engine, but you add the\naggregation information to the search request in a similar way between\nall aggregations.\n\n\\section{Instantiate and Construct the\nAggregation}\\label{instantiate-and-construct-the-aggregation}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Use the \\texttt{com.liferay.portal.search.aggregation.Aggregations} to\n  instantiate the aggregation you'll construct. For example,\n\n\\begin{verbatim}\nPercentilesAggregation percentilesAggregation =\n    aggregations.percentiles(\"percentiles\", Field.PRIORITY);\n\\end{verbatim}\n\n  To discover what fields each aggregation must have (e.g.,\n  \\texttt{Sting\\ name,\\ String\\ field} in the case of the above\n  \\texttt{PercentilesAggregation}), see the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/aggregation/Aggregations.java}{\\texttt{Aggregations}\n  interface}.\n\\item\n  Build out the aggregation to get the desired response. This looks\n  different for each aggregation type, but Elasticsearch's documentation\n  on the aggregation type explains it well (such as\n  \\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-metrics-percentile-aggregation.html}{Percentiles\n  Aggregations}) combined with the setters in Liferay's corresponding\n  interface.\n\n  For example, use the\n  \\texttt{setPercentilesMethod(PercentilesMethod.HDR)} method to use the\n  \\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-metrics-percentile-aggregation.html\\#_hdr_histogram}{High\n  Dynamic Range Histogram} for calculating the percentiles.\n\n\\begin{verbatim}\npercentilesAggregation.setPercentilesMethod(PercentilesMethod.HDR);\n\\end{verbatim}\n\\end{enumerate}\n\nOnce the aggregation itself is in good shape, feed it to the search\nquery.\n\n\\section{Build the Search Query}\\label{build-the-search-query}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get an instance of\n  \\texttt{com.liferay.portal.search.searcher.SearchRequestBuilder} from\n  the \\texttt{SearchRequestBuilderFactory} service:\n\n\\begin{verbatim}\nSearchRequestBuilder searchRequestBuilder = \n    searchRequestBuilderFactory.builder();\n\\end{verbatim}\n\\item\n  Get a \\texttt{com.liferay.portal.search.searcher.SearchRequest}\n  instance from the builder, then add the aggregation to it and run its\n  \\texttt{build} method:\n\n\\begin{verbatim}\nSearchRequest searchRequest =\n    searchRequestBuilder.addAggregation(percentilesAggregation).build();\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Execute the Search Query}\\label{execute-the-search-query}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Perform a search using the \\texttt{Searcher} service and the\n  \\texttt{SearchRequest} to get a\n  \\texttt{com.liferay.portal.search.searcher.SearchResponse}:\n\n\\begin{verbatim}\nSearchResponse searcher.search(searchRequest);\n\\end{verbatim}\n\\item\n  To satisfy the dependencies of the example code here, get a reference\n  to\n  \\texttt{com.liferay.portal.search.searcher.SearchRequestBuilderFactory}\n  and \\texttt{com.liferay.portal.search.searcher.Searcher}:\n\n\\begin{verbatim}\n@Reference\nprotected Searcher searcher;\n\n@Reference\nSearchRequestBuilderFactory searchRequestBuilderFactory;\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Process the response}\\label{process-the-response}\n\nWhat you'll do with the \\texttt{SearchResponse} returned by the\n\\texttt{searcher.search} call depends on the type of aggregation and\nyour specific use case. A separate article will be written to\ndemonstrate how to process the response.\n\n\\chapter{Statistical Aggregations}\\label{statistical-aggregations}\n\nSupport for\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/portal-kernel/src/com/liferay/portal/kernel/search/GroupBy.java}{GroupBy}\nand\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/portal-kernel/src/com/liferay/portal/kernel/search/Stats.java}{Stats}\naggregations were introduced in 7.0.\n\nCardinality Aggregations extend Liferay DXP's metrics aggregation\ncapabilities, providing an approximate (i.e., statistical) count of\ndistinct values returned by a search query. For example, you could\ncompute a count of distinct values of the \\emph{tag} field. Refer to the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-metrics-cardinality-aggregation.html}{Elasticsearch\ndocumentation} for more details.\n\nWhile this functionality was available in the past directly in the\nportal kernel code, it's been extracted and re-implemented in\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/stats/StatsRequest.java}{\\texttt{StatsRequest}}\nand\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/stats/StatsResponse.java}{\\texttt{StatsResponse}},\nboth introduced in the \\texttt{com.liferay.portal.search.api} module to\navoid modifying \\texttt{portal-kernel}. \\texttt{StatsRequest} provides\nthe same statistical features that the legacy\n\\texttt{com.liferay.portal.kernel.search.Stats} does, and adds the new\ncardinality option.\n\n\\section{\\texorpdfstring{\\texttt{StatsRequest}}{StatsRequest}}\\label{statsrequest}\n\nThe \\texttt{StatsRequest} Provides a map of field names and the metric\naggregations that are to be computed for each field.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a reference to\n  \\texttt{com.liferay.portal.search.searcher.SearchRequestBuilderFactory}:\n\n\\begin{verbatim}\n@Reference\nSearchRequestBuilderFactory searchRequestBuilderFactory;\n\\end{verbatim}\n\\item\n  Get an instance of\n  \\texttt{com.liferay.portal.search.searcher.SearchRequestBuilder}:\n\n\\begin{verbatim}\nSearchRequestBuilder searchRequestBuilder = searchRequestBuilderFactory.builder();\n\\end{verbatim}\n\\item\n  Get a \\texttt{com.liferay.portal.search.searcher.SearchRequest}\n  instance from the builder:\n\n\\begin{verbatim}\nSearchRequest searchRequest = searchRequestBuilder.build();\n\\end{verbatim}\n\\item\n  Get a reference to\n  \\texttt{com.liferay.portal.search.stats.StatsRequestBuilderFactory}:\n\n\\begin{verbatim}\n@Reference\nStatsRequestBuilderFactory statsRequestBuilderFactory;\n\\end{verbatim}\n\\item\n  Get a \\texttt{com.liferay.portal.search.stats.StatsRequestBuilder}\n  instance and build\n  \\texttt{com.liferay.portal.search.stats.StatsRequest} with the desired\n  metrics:\n\n\\begin{verbatim}\nStatsRequestBuilder statsRequestBuilder = \n    statsRequestBuilderFactory.getStatsRequestBuilder();\nStatsRequest statsRequest = statsRequestBuilder\n    .cardinality(true)\n    .count(true)\n    .field(field)\n    .max(true)\n    .mean(true)\n    .min(true)\n    .missing(true)\n    .sum(true)\n    .sumOfSquares(true)\n    .build();\n\\end{verbatim}\n\\item\n  Set \\texttt{StatsRequest} on the \\texttt{SearchRequest}:\n\n\\begin{verbatim}\nsearchRequest.statsRequests(statsRequest);\n\\end{verbatim}\n\\item\n  Get a reference to\n  \\texttt{com.liferay.portal.search.searcher.Searcher}:\n\n\\begin{verbatim}\n@Reference\nprotected Searcher searcher;\n\\end{verbatim}\n\\item\n  Perform a search using \\texttt{Searcher} and \\texttt{SearchRequest} to\n  get \\texttt{com.liferay.portal.search.searcher.SearchResponse}:\n\n\\begin{verbatim}\nSearchResponse searcher.search(searchRequest);\n\\end{verbatim}\n\\end{enumerate}\n\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-test-util/src/main/java/com/liferay/portal/search/test/util/stats/BaseStatisticsTestCase.java\\#L128}{\\textbf{Click\nhere to see an example from Liferay's codebase}}\n\n\\section{\\texorpdfstring{\\texttt{StatsResponse}}{StatsResponse}}\\label{statsresponse}\n\nThe stats response contains the metrics aggregations computed by the\nsearch engine for a given field.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get the map containing the metrics aggregations computed by the search\n  engine:\n\n\\begin{verbatim}\nMap<String, StatsResponse> map = searchResponse.getStatsResponseMap();\n\\end{verbatim}\n\\item\n  Get the \\texttt{StatsResponse} for a given field:\n\n\\begin{verbatim}\nStatsResponse statsResponse = map.get(field);\n\\end{verbatim}\n\\item\n  Get the desired metric, for example \\emph{cardinality}:\n\n\\begin{verbatim}\nstatsResponse.getCardinality();\n\\end{verbatim}\n\\end{enumerate}\n\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-test-util/src/main/java/com/liferay/portal/search/test/util/stats/BaseStatisticsTestCase.java\\#L128}{\\textbf{Click\nhere to see an example from Liferay's codebase}}\n\n\\section{\\texorpdfstring{Using the Legacy \\texttt{Stats}\nObject}{Using the Legacy Stats Object}}\\label{using-the-legacy-stats-object}\n\nThe legacy \\texttt{com.liferay.portal.kernel.search.Stats} object\ncontinues to be fully supported:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a \\texttt{Stats} instance with the desired metrics:\n\n\\begin{verbatim}\nStats stats = new Stats() {\n    {\n        setCount(true);\n        setField(field);\n        setMax(true);\n        setMean(true);\n        setMin(true);\n        setSum(true);\n        setSumOfSquares(true);\n    }\n};\n\\end{verbatim}\n\\item\n  Set \\texttt{Stats} on the \\texttt{SearchContext}:\n\n\\begin{verbatim}\nsearchRequestBuilder.withSearchContext(searchContext -> searchContext.addStats(stats));\n\\end{verbatim}\n\\end{enumerate}\n\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-test-util/src/main/java/com/liferay/portal/search/test/util/stats/BaseStatisticsTestCase.java\\#L42}{\\textbf{Click\nhere to see an example from Liferay's codebase}}\n\n\\section{External References}\\label{external-references-1}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-metrics.html\n\\item\n  https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-metrics-cardinality-aggregation.html\n\\item\n  https://lucene.apache.org/solr/guide/7\\_5/the-stats-component.html\n\\end{itemize}\n\n\\section{Search Engine Connector\nSupport}\\label{search-engine-connector-support-1}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Elasticsearch 6: Yes\n\\item\n  Solr 7: Yes\n\\end{itemize}\n\n\\section{New/Related APIs}\\label{newrelated-apis-1}\n\nThese are the relevant APIs for building Statistics Aggregations:\n\nAPI (FQCN) \\textbar{} Provided by Artifact \\textbar{}\n\\texttt{com.liferay.portal.search.searcher.SearchRequestBuilder\\#statsRequests(StatsRequest...\\ statsRequests)}\n\\textbar{} \\texttt{com.liferay.portal.search.api}\n\\texttt{com.liferay.portal.search.searcher.SearchResponse\\#getStatsResponseMap()}\n\\textbar{} \\texttt{com.liferay.portal.search.api}\n\\textbf{\\texttt{com.liferay.portal.search.stats.StatsRequest}}\n\\textbar{} \\texttt{com.liferay.portal.search.api}\n\\texttt{com.liferay.portal.search.stats.StatsRequestBuilder} \\textbar{}\n\\texttt{com.liferay.portal.search.api}\n\\texttt{com.liferay.portal.search.stats.StatsRequestBuilderFactory}\n\\textbar{} \\texttt{com.liferay.portal.search.api}\n\\textbf{\\texttt{com.liferay.portal.search.stats.StatsResponse}}\n\\textbar{} \\texttt{com.liferay.portal.search.api}\n\\texttt{com.liferay.portal.kernel.search.Stats} \\textbar{}\n\\texttt{portal-kernel}\n\n\\section{Deprecated APIs}\\label{deprecated-apis}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  SearchSearchRequest\\#getStats()\n\\item\n  SearchSearchRequest\\#setStats(Map\\textless String, Stats\\textgreater{}\n  stats)\n\\end{itemize}\n\n\\chapter{Model Entity Indexing\nFramework}\\label{model-entity-indexing-framework}\n\nUnless you're searching for model entities using database queries (not\nrecommended in most cases), each asset in Liferay DXP must be indexed in\nthe search engine. The indexing code is specific to each asset, as the\nasset's developers know what fields to index and what filters to apply\nto the search query. This paradigm applies to Liferay's own developers\nand anyone developing model entities for use with Liferay DXP.\n\nIn past versions of Liferay DXP, when your asset required indexing, you\nwould implement a new Indexer by extending\n\\texttt{com.liferay.portal.kernel.search.BaseIndexer\\textless{}T\\textgreater{}}.\nLiferay DXP version 7.1 introduced a new pattern that relies on\n\\href{https://stackoverflow.com/questions/2399544/difference-between-inheritance-and-composition}{composition\ninstead of inheritance}. That said, if you want to use the old approach,\nfeel free to extend \\texttt{BaseIndexer}. It's still supported.\n\n\\section{Search and Indexing\nOverview}\\label{search-and-indexing-overview}\n\nStarting with the 7.0 version of Liferay DXP, the Search API has become\nless tied to Lucene. Elasticsearch support was added (in addition to\nSolr), and most of the newer searching and indexing APIs aim to\nleverage/map Elasticsearch APIs. This means that in many cases (for\nexample the \\texttt{Query} types) there is a one-to-one mapping between\nthe Liferay and Elasticsearch APIs.\n\nIn addition to the Elasticsearch-centered APIs, Liferay's Search\nInfrastructure includes additional APIs serving these purposes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Ensure all indexed documents include some required fields (e.g.,\n  \\texttt{entryClassName}, \\texttt{entryClassPK},\n  \\texttt{assetTagNames}, \\texttt{assetCategories}, \\texttt{companyId},\n  \\texttt{groupId}, staging status).\n\\item\n  Ensure the scope of returned search results is appropriate by applying\n  the right filters to search requests.\n\\item\n  Provide permission checking and hit summaries for display in the\n  built-in \\href{/docs/7-2/user/-/knowledge_base/u/search}{search\n  application}.\n\\end{itemize}\n\n\\section{\\texorpdfstring{Mapping the Composite Search and Indexing\nFramework to \\texttt{Indexer}/\\texttt{BaseIndexer}\nCode}{Mapping the Composite Search and Indexing Framework to Indexer/BaseIndexer Code}}\\label{mapping-the-composite-search-and-indexing-framework-to-indexerbaseindexer-code}\n\nIf you're used to the old way of indexing custom entities (extending\n\\texttt{BaseIndexer}, the abstract implementation of \\texttt{Indexer}),\nthe table below provides a quick overview about how the methods of the\n\\texttt{Indexer} interface were decomposed into several new classes and\nmethods.\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3857}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3857}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.2286}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nIndexer/BaseIndexer method\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nComposite Indexer Equivalent\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nExample\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nClass Constructor & \\texttt{SearchRegistrar} &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/BlogsEntrySearchRegistrar.java}{\\texttt{BlogsEntrySearchRegistrar}} \\\\\n\\texttt{setDefaultSelectedFieldNames} &\n\\texttt{SearchRegistrar.activate} &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/BlogsEntrySearchRegistrar.java}{\\texttt{BlogsEntrySearchRegistrar}} \\\\\n\\texttt{setDefaultSelectedLocalizedFieldNames} &\n\\texttt{SearchRegistrar.activate} &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/BlogsEntrySearchRegistrar.java}{\\texttt{BlogsEntrySearchRegistrar}} \\\\\n\\texttt{setPermissionAware} & \\texttt{ModelResourcePermissionRegistrar}\n&\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/document-library/document-library-service/src/main/java/com/liferay/document/library/internal/security/permission/resource/DLFileEntryModelResourcePermissionRegistrar.java}{\\texttt{DLFileEntryModelResourcePermissionRegistrar}} \\\\\n\\texttt{setFilterSearch} & \\texttt{ModelResourcePermissionRegistrar} &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/document-library/document-library-service/src/main/java/com/liferay/document/library/internal/security/permission/resource/DLFileEntryModelResourcePermissionRegistrar.java}{\\texttt{DLFileEntryModelResourcePermissionRegistrar}} \\\\\n\\texttt{getDocument}/\\texttt{doGetDocument} &\n\\texttt{ModelDocumentContributor} &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/spi/model/index/contributor/BlogsEntryModelDocumentContributor.java}{\\texttt{BlogsEntryModelDocumentContributor}} \\\\\n\\texttt{reindex}/\\texttt{doReindex} &\n\\texttt{ModelIndexerWriterContributor} &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/spi/model/index/contributor/BlogsEntryModelIndexerWriterContributor.java}{\\texttt{BlogsEntryModelIndexerWriterContributor}} \\\\\n\\texttt{addRelatedEntryFields} & \\texttt{RelatedEntryIndexer} &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/document-library/document-library-service/src/main/java/com/liferay/document/library/internal/search/DLFileEntryRelatedEntryIndexer.java}{\\texttt{DLFileEntryRelatedEntryIndexer}} \\\\\n\\texttt{postProcessContextBooleanFilter}/\\texttt{PostProcessContextQuery}\n& \\texttt{ModelPreFilterContributor} &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/spi/model/query/contributor/BlogsEntryModelPreFilterContributor.java}{\\texttt{BlogsEntryModelPreFilterContributor}} \\\\\n\\texttt{postProcessSearchQuery} & \\texttt{KeywordQueryContributor} &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/spi/model/query/contributor/BlogsEntryKeywordQueryContributor.java}{\\texttt{BlogsEntryKeywordQueryContributor}} \\\\\n\\texttt{getFullQuery} & \\texttt{SearchContextContributor} &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/document-library/document-library-service/src/main/java/com/liferay/document/library/internal/search/DLFileEntryModelSearchContextContributor.java}{\\texttt{DLFileEntryModelSearchContextContributor}} \\\\\n\\texttt{isVisible}/\\texttt{isVisibleRelatedEntry} &\n\\texttt{ModelVisibilityContributor} &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/spi/model/result/contributor/BlogsEntryModelVisibilityContributor.java}{\\texttt{BlogsEntryModelVisibilityContributor}} \\\\\n\\texttt{getSummary}/\\texttt{createSummary}/\\texttt{doGetSummary} &\n\\texttt{ModelSummaryContributor} &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/spi/model/result/contributor/BlogsEntryModelSummaryContributor.java}{\\texttt{BlogsEntryModelSummaryContributor}} \\\\\n\\texttt{Indexer.search}/\\texttt{searchCount} & No change &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-web/src/main/java/com/liferay/blogs/web/internal/display/context/BlogEntriesDisplayContext.java}{\\texttt{BlogEntriesDisplayContext}} \\\\\n\\texttt{Indexer.delete}/\\texttt{doDelete} & No change &\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/message-boards/message-boards-service/src/main/java/com/liferay/message/boards/service/impl/MBMessageLocalServiceImpl.java\\#L703}{\\texttt{MBMessageLocalServiceImpl.deleteMessage}} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nIn addition, you can index \\texttt{ExpandoBridge} attributes. This was\npreviously accomplished in \\texttt{BaseIndexer}'s\n\\texttt{getBaseModelDocument}. Now you implement an\n\\texttt{ExpandoBridgeRetriever}. See\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/document-library/document-library-service/src/main/java/com/liferay/document/library/internal/search/DLFileEntryExpandoBridgeRetriever.java}{\\texttt{DLFileEntryExpandoBridgeRetriever}}\nfor an example implementation.\n\n\\section{Permissions-Aware Searching and\nIndexing}\\label{permissions-aware-searching-and-indexing}\n\nIn previous versions of Liferay DXP, search was only \\emph{permissions\naware} (indexed with the entity's permissions and searched with those\npermissions intact) if the application developer specified this line in\nthe \\texttt{Indexer} class's constructor:\n\n\\begin{verbatim}\nsetPermissionAware(true);\n\\end{verbatim}\n\nNow, search is permissions aware by default \\emph{if the new permissions\napproach}, as described in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions}{these\ntutorials}, is implemented for an application.\n\n\\section{Annotating Service Methods to Trigger\nIndexing}\\label{annotating-service-methods-to-trigger-indexing}\n\nHaving objects translated into database entities \\emph{and} search\nengine documents means that there's a possibility for a state mismatch\nbetween the database and search engine. For example, when a Blogs Entry\nis added, updated, or removed from the database, corresponding changes\nmust be made in the search engine. To do this, intervention must be made\nin the service layer. For Service Builder entities, this occurs in the\n\\texttt{LocalServiceImpl} classes. An annotation simplifies this:\n\\texttt{@Indexable}. It takes a \\texttt{type} property that can have two\nvalues: \\texttt{REINDEX} or \\texttt{DELETE}. Commonly, a\n\\texttt{deleteEntity} method in the service layer is annotated like\nthis:\n\n\\begin{verbatim}\n@Indexable(type = IndexableType.DELETE)\n@Override\n@SystemEvent(type = SystemEventConstants.TYPE_DELETE)\npublic BlogsEntry deleteEntry(BlogsEntry entry) throws PortalException {\n    ...\n}\n\\end{verbatim}\n\nThe \\texttt{@Indexable} annotation is executed by Liferay's AOP\ninfrastructure, so if you have a method with that annotation, you must\ncall it using a service instance variable injected by your dependency\ninjector, and not using the \\texttt{this} keyword. Whether using OSGi's\nDeclarative Services (DS) or Spring for dependency injection, there's a\nprotected variable declared in the superclass\n(\\texttt{*LocalServiceBaseImpl}) that can be used in the\n\\texttt{*LocalServiceImpl}, like this.\n\n\\begin{verbatim}\nblogsEntryLocalService.deleteEntry(entry);\n\\end{verbatim}\n\nSince you're using the injected service variable, that means you must\nnot call\n\n\\begin{verbatim}\nthis.deleteEntry(...) \n\\end{verbatim}\n\nin your \\texttt{*LocalServiceImpl} methods. The annotation won't be\nexecuted and you'll be left with a state mismatch between the search\nengine document and the database column.\n\n\\section{Search and Localization: a Cheat\nSheet}\\label{search-and-localization-a-cheat-sheet}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localization}{Localization}\nis important. Search and localization can play nicely together, if you\ntake some precautions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  For each field that should be localized (e.g., \\texttt{content}),\n  index a separate field for each of the site's languages (e.g.,\n  \\texttt{content\\_en\\_US}, \\texttt{content\\_ja\\_JP},\n  \\texttt{content\\_es\\_ES}, \\ldots).\n\\item\n  Search the localized fields. Whatever you index, that's what you\n  should be querying for.\n\\item\n  Don't index content in plain (unlocalized) fields if you expect the\n  content to be present in multiple locales.\n\\item\n  Don't index both the plain and the localized field.\n\\end{itemize}\n\nThe indexing and searching articles included in this section demonstrate\nhow to handle localized fields in the search code properly.\n\n\\chapter{Indexing Model Entities}\\label{indexing-model-entities}\n\nModel entities are searchable when their data fields are indexed by the\nsearch engine. Search and indexing code relies on Search APIs and SPIs.\n\nThe extension points (i.e., the interfaces to implement) in this section\nare provided by the \\texttt{com.liferay.portal.search.spi} bundle. Calls\nare also made to the \\texttt{com.liferay.portal.search.api} bundle's\nmethods.\n\nHere are the Gradle dependencies for Liferay DXP 7.2.0 GA1:\n\n\\begin{verbatim}\ndependencies {\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.spi\", version: \"3.2.1\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.api\", version: \"3.7.0\"\n}\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{APIs and SPIs:} SPIs are a special type of API. Generally, code\ninside a SPI module (e.g., \\texttt{portal-search-spi}) is used to\ncustomize existing behavior, while API modules contain behavior you want\nto use. Put simply, implement interfaces from an SPI, and consume the\ncode from the API.\n\nSPI example: \\texttt{ModelDocumentContributor} lives in an SPI module\nbecause you're supposed to implement it directly, defining your own\nindexing behavior.\n\nAPI example: \\texttt{SearchRequest} lives in an API module because its\nbehavior is leveraged inside your code to build a search request.\n\n\\noindent\\hrulefill\n\n\\section{Contributing Model Entity Fields to the\nIndex}\\label{contributing-model-entity-fields-to-the-index}\n\nWrite a \\texttt{ModelDocumentContributor} class to control how model\nentity fields are indexed in search engine documents.\n\n\\textbf{Extension Point (SPI):}\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/index/contributor/ModelDocumentContributor.java}{\\texttt{com.liferay.portal.search.spi.model.index.contributor.ModelDocumentContributor\\textless{}T\\textgreater{}}}\n\nDeclare the Component's \\texttt{indexer.class.name} and its service type\nas a \\texttt{ModelDocumentContributor} class:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = \"indexer.class.name=com.liferay.foo.model.FooEntry\",\n    service = ModelDocumentContributor.class\n)\npublic class FooEntryModelDocumentContributor\n    implements ModelDocumentContributor<FooEntry> {\n\\end{verbatim}\n\nImplement the \\texttt{contribute} method, calling the\n\\texttt{com.liferay.portal.kernel.Document.add()} method appropriate for\nthe data type the index should use (e.g., \\texttt{addText} for fields\nthat should be searched using a\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/text.html}{full\ntext search strategy}).\n\n\\begin{verbatim}\n@Override\npublic void contribute(Document document, FooEntry fooEntry) {\n\n    document.addDate(Field.DISPLAY_DATE, fooEntry.getDisplayDate());\n    document.addDate(Field.MODIFIED_DATE, fooEntry.getModifiedDate());\n    document.addText(Field.SUBTITLE, fooEntry.getSubtitle());\n\\end{verbatim}\n\nFor fields that should be\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localization}{localized},\nindex a field for each locale in the Site. Many times you'll want to\nlocalize the title and content fields, for example:\n\n\\begin{verbatim}\nfor (Locale locale :\n        LanguageUtil.getAvailableLocales(fooEntry.getGroupId())) {\n\n    String languageId = LocaleUtil.toLanguageId(locale);\n\n    document.addText(\n        LocalizationUtil.getLocalizedName(Field.CONTENT, languageId),\n        content);\n    document.addText(\n        LocalizationUtil.getLocalizedName(Field.TITLE, languageId),\n        fooEntry.getTitle());\n}\n\\end{verbatim}\n\nThe above strategy is a good one: loop through the available locales in\nthe site, then use\n\\texttt{com.liferay.portal.kernel.util.LocalizationUtil} to add the\nlocalized field name to the document.\n\nThe \\texttt{contribute} method is called each time the \\texttt{add} and\n\\texttt{update} methods in the entity's service layer are called.\n\n\\section{Configure Re-Indexing and Batch Indexing\nBehavior}\\label{configure-re-indexing-and-batch-indexing-behavior}\n\n\\texttt{ModelIndexerWriterContributor} classes configure the re-indexing\nand batch re-indexing behavior for the model entity. This class's\n\\texttt{customize} method is called when a re-index is triggered from\nthe Search administrative application found in Control Panel →\nConfiguration → Search, or when a CRUD operation is made on the entity,\n\\emph{if} the \\texttt{modelIndexed} method is implemented in the\ncontributor.\n\n\\textbf{Extension Point (SPI):}\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/index/contributor/ModelIndexerWriterContributor.java}{\\texttt{com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor}}\n\nThe bulk of the work is in the \\texttt{customize} method. This code uses\nthe actionable dynamic query helper method to retrieve all existing Foo\nentities in the virtual instance (identified by the Company ID). If\nyou're using Service Builder, this query method was generated for you\nwhen you built the services. Each Foo Entry document is then retrieved\nand added to a collection.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  First set up the component and class declarations:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = \"indexer.class.name=com.liferay.foo.model.FooEntry\",\n    service = ModelIndexerWriterContributor.class\n)\npublic class FooEntryModelIndexerWriterContributor\n    implements ModelIndexerWriterContributor<FooEntry> {\n\\end{verbatim}\n\\item\n  Write the \\texttt{customize} method:\n\n\\begin{verbatim}\n@Override\npublic void customize(\n    BatchIndexingActionable batchIndexingActionable,\n    ModelIndexerWriterDocumentHelper modelIndexerWriterDocumentHelper) {\n\n    batchIndexingActionable.setAddCriteriaMethod(\n        dynamicQuery -> {\n            Property displayDateProperty = PropertyFactoryUtil.forName(\n                \"displayDate\");\n\n            dynamicQuery.add(displayDateProperty.lt(new Date()));\n\n            Property statusProperty = PropertyFactoryUtil.forName(\"status\");\n\n            Integer[] statuses = {\n                WorkflowConstants.STATUS_APPROVED,\n                WorkflowConstants.STATUS_IN_TRASH\n            };\n\n            dynamicQuery.add(statusProperty.in(statuses));\n        });\n    batchIndexingActionable.setPerformActionMethod(\n        (FooEntry fooEntry) -> {\n            Document document =\n                modelIndexerWriterDocumentHelper.getDocument(fooEntry);\n\n            batchIndexingActionable.addDocuments(document);\n        });\n}\n\\end{verbatim}\n\\item\n  Override \\texttt{getBatchIndexingActionable}:\n\n\\begin{verbatim}\n@Override\npublic BatchIndexingActionable getBatchIndexingActionable() {\n    return _dynamicQueryBatchIndexingActionableFactory.\n        getBatchIndexingActionable(\n            _fooEntryLocalService.getIndexableActionableDynamicQuery());\n}\n\\end{verbatim}\n\\item\n  Override \\texttt{getcompanyId}, getting the ID from your entity:\n\n\\begin{verbatim}\n@Override\npublic long getCompanyId(FooEntry fooEntry) {\n    return fooEntry.getCompanyId();\n}\n\\end{verbatim}\n\\item\n  Override \\texttt{getIndexerWriterMode}:\n\n\\begin{verbatim}\n@Override\npublic IndexerWriterMode getIndexerWriterMode(FooEntry fooEntry) {\n    int status = fooEntry.getStatus();\n\n    if ((status == WorkflowConstants.STATUS_APPROVED) ||\n        (status == WorkflowConstants.STATUS_IN_TRASH) ||\n        (status == WorkflowConstants.STATUS_DRAFT)) {\n\n        return IndexerWriterMode.UPDATE;\n    }\n\n    return IndexerWriterMode.DELETE;\n}\n\\end{verbatim}\n\n  \\texttt{com.liferay.portal.search.spi.model.index.contributor.helper.IndexerWriterMode}\n  defines the following index-writing options:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{IndexerWriterMode.DELETE}: instructs the search framework to\n    delete the given document in the search index without re-creating it\n  \\item\n    \\texttt{IndexerWriterMode.PARTIAL\\_UPDATE},\n    \\texttt{IndexerWriterMode.UPDATE}: instructs the search framework to\n    update the given document in the search index.\n  \\item\n    \\texttt{IndexerWriterMode.SKIP}: instructs the search framework to\n    not write anything to the search index.\n  \\end{itemize}\n\n  The default is \\texttt{IndexerWriterMode.UPDATE}.\n\\item\n  Add the services referenced:\n\n\\begin{verbatim}\n@Reference\nprivate FooEntryLocalService _fooEntryLocalService;\n\n@Reference\nprivate DynamicQueryBatchIndexingActionableFactory\n    _dynamicQueryBatchIndexingActionableFactory;\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Contribute Fields to Every\nDocument}\\label{contribute-fields-to-every-document}\n\n\\texttt{DocumentContributor} classes (without any\n\\texttt{indexer.class.name} component property or type parameter)\ncontribute certain fields to every index document, regardless of what\nbase entity is being indexed. For example, the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search/src/main/java/com/liferay/portal/search/internal/contributor/document/GroupedModelDocumentContributor.java}{\\texttt{GroupedModelDocumentContributor}}\ncontains logic to contribute \\texttt{GROUP\\_ID} and\n\\texttt{SCOPE\\_GROUP\\_ID} fields for all documents with a backing entity\nthat's also a \\texttt{GroupedModel}.\n\n\\chapter{Searching the Index for Model\nEntities}\\label{searching-the-index-for-model-entities}\n\nThe heart of searching for your model entity documents is querying for\nwhat you indexed. To do this, contribute search terms to the Liferay DXP\nsearch query.\n\nThe extension points (i.e., the interfaces to implement) on this page\nare provided by the \\texttt{com.liferay.portal.search.spi} bundle.\n\nHere's the Gradle dependency for Liferay DXP 7.2.0 GA1:\n\n\\begin{verbatim}\ndependencies {\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.spi\", version: \"3.2.1\"\n}\n\\end{verbatim}\n\n\\section{Adding your Model Entity's Terms to the\nQuery}\\label{adding-your-model-entitys-terms-to-the-query}\n\n\\texttt{KeywordQueryContributor} classes contribute clauses to the\nongoing search query, to control the way model entities are searched. If\nyou're storing localized fields in the index (a good idea, as covered in\nthe example code for your \\texttt{ModelDocumentContributor}), query the\nlocalized fields at search time.\n\n\\textbf{Extension Point (SPI):}\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/query/contributor/KeywordQueryContributor.java}{com.liferay.portal.search.spi.model.query.contributor.KeywordQueryContributor}\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = \"indexer.class.name=com.liferay.foo.model.FooEntry\",\n    service = KeywordQueryContributor.class\n)\npublic class FooEntryKeywordQueryContributor\n    implements KeywordQueryContributor {\n\n    @Override\n    public void contribute(\n        String keywords, BooleanQuery booleanQuery,\n        KeywordQueryContributorHelper keywordQueryContributorHelper) {\n\n        SearchContext searchContext =\n            keywordQueryContributorHelper.getSearchContext();\n\n        queryHelper.addSearchLocalizedTerm(\n            booleanQuery, searchContext, Field.CONTENT, false);\n        queryHelper.addSearchTerm(\n            booleanQuery, searchContext, Field.SUBTITLE, false);\n        queryHelper.addSearchLocalizedTerm(\n            booleanQuery, searchContext, Field.TITLE, false);\n    }\n\n    @Reference\n    protected QueryHelper queryHelper;\n\n}\n\\end{verbatim}\n\nThe entity in this example has a title, subtitle, and content field. The\nsubtitle field wasn't stored as a localized field, so it's not searched\nthat way, either.\n\n\\section{Contributing Query Clauses to Every\nSearch}\\label{contributing-query-clauses-to-every-search}\n\nIt's a less common need, but to contribute query clauses to every\nsearch, regardless of what base entity is being searched, implement a\n\\texttt{KeywordQueryContributor} class registered without an\n\\texttt{indexer.class.name} component property. For example, see\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search/src/main/java/com/liferay/portal/search/internal/contributor/query/AlwaysPresentFieldsKeywordQueryContributor.java}{\\texttt{AlwaysPresentFieldsKeywordQueryContributor}}.\n\nIt includes a String array that includes the fields that are always\nsearched:\n\n\\begin{verbatim}\nprivate static final String[] _ALWAYS_PRESENT_FIELDS = {\n    Field.COMMENTS, Field.CONTENT, Field.DESCRIPTION, Field.PROPERTIES,\n    Field.TITLE, Field.URL, Field.USER_NAME\n};\n\\end{verbatim}\n\n\\section{Pre-Filtering}\\label{pre-filtering}\n\n\\texttt{*PreFilterContributor} classes control how search results are\nfiltered before they're returned from the search engine. For example,\nadding the workflow status to the query ensures that an entity in the\ntrash isn't returned in the search results. They're constructed each\ntime a query for the model entity is made.\n\n\\textbf{Extension Pointi (SPI):}\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/query/contributor/ModelPreFilterContributor.java}{\\texttt{ModelPreFilterContributor}s}\n\nTo contribute filter clauses specific to a model entity, use a\n\\texttt{ModelPreFilterContributor}. This one adds a filter for workflow\nstatus:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = \"indexer.class.name=com.liferay.foo.model.FooEntry\",\n    service = ModelPreFilterContributor.class\n)\npublic class FooEntryModelPreFilterContributor\n    implements ModelPreFilterContributor {\n\n    @Override\n    public void contribute(\n        BooleanFilter booleanFilter, ModelSearchSettings modelSearchSettings,\n        SearchContext searchContext) {\n\n        addWorkflowStatusFilter(\n            booleanFilter, modelSearchSettings, searchContext);\n    }\n\n    protected void addWorkflowStatusFilter(\n        BooleanFilter booleanFilter, ModelSearchSettings modelSearchSettings,\n        SearchContext searchContext) {\n\n        workflowStatusModelPreFilterContributor.contribute(\n            booleanFilter, modelSearchSettings, searchContext);\n    }\n\n    @Reference(target = \"(model.pre.filter.contributor.id=WorkflowStatus)\")\n    protected ModelPreFilterContributor workflowStatusModelPreFilterContributor;\n\n}\n\\end{verbatim}\n\n\\textbf{Extension Point (SPI):}\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/query/contributor/QueryPreFilterContributor.java}{\\texttt{com.liferay.portal.search.spi.model.query.contributor.QueryPreFilterContributor}}\n\nTo contribute Filter clauses to every search, regardless of what base\nentity is being searched, implement a\n\\texttt{QueryPreFilterContributor}. \\texttt{QueryPreFilterContributor}\nis constructed only once under the root filter during a search. For\nexample, see\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search/src/main/java/com/liferay/portal/search/internal/contributor/query/AssetCategoryTitlesKeywordQueryContributor.java}{\\texttt{AssetCategoryTitlesKeywordQueryContributor}}.\n\nWhat's the difference between \\texttt{QueryPreFilterContributor} and\n\\texttt{ModelPreFilterContributor}? \\texttt{QueryPreFilterContributor}\nis constructed only once under the root filter during a search, while\n\\texttt{ModelPreFilterContributor} is constructed once per model entity\nand added under each specific entity's sub-filter.\n\n\\chapter{Returning Results}\\label{returning-results}\n\nWhen a model entity's indexed search document is obtained during a\nsearch request, it's converted into a summary of the model entity. You\ncan exert control over your model entity's summary.\n\n\\section{Creating a Results Summary}\\label{creating-a-results-summary}\n\n\\texttt{ModelSummaryContributor} classes get the \\texttt{Summary} object\ncreated for each search document, so you can manipulate it by adding\nspecific fields or setting the length of the displayed content.\n\n\\textbf{Extension Pointi (SPI):}\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/result/contributor/ModelSummaryContributor.java}{\\texttt{com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor}}\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = \"indexer.class.name=com.liferay.foo.model.FooEntry\",\n    service = ModelSummaryContributor.class\n)\npublic class FooEntryModelSummaryContributor\n    implements ModelSummaryContributor {\n\n    @Override\n    public Summary getSummary(\n        Document document, Locale locale, String snippet) {\n\n        String languageId = LocaleUtil.toLanguageId(locale);\n\n        return _createSummary(\n            document,\n            LocalizationUtil.getLocalizedName(Field.CONTENT, languageId),\n            LocalizationUtil.getLocalizedName(Field.TITLE, languageId));\n    }\n\n    private Summary _createSummary(\n        Document document, String contentField, String titleField) {\n\n        String prefix = Field.SNIPPET + StringPool.UNDERLINE;\n\n        Summary summary = new Summary(\n            document.get(prefix + titleField, titleField),\n            document.get(prefix + contentField, contentField));\n\n        summary.setMaxContentLength(200);\n\n        return summary;\n    }\n\n}\n\\end{verbatim}\n\n\\section{Controlling the Visibility of Model\nEntities}\\label{controlling-the-visibility-of-model-entities}\n\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/result/contributor/ModelVisibilityContributor.java}{\\texttt{ModelVisibilityContributor}}\nclasses control the visibility of model entities that can be attached to\nother asset types (for example, File Entries can be attached to Wiki\nPages), in the search context.\n\n\\textbf{Extension Point (SPI):}\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/result/contributor/ModelVisibilityContributor.java}{\\texttt{com.liferay.portal.search.spi.model.result.contributor.ModelVisibilityContributor}}\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = \"indexer.class.name=com.liferay.foo.model.FooEntry\",\n    service = ModelVisibilityContributor.class\n)\npublic class FooEntryModelVisibilityContributor\n    implements ModelVisibilityContributor {\n\n    @Override\n    public boolean isVisible(long classPK, int status) {\n        try {\n            FooEntry entry = fooEntryLocalService.getEntry(classPK);\n\n            return isVisible(entry.getStatus(), status);\n        }\n        catch (PortalException pe) {\n            if (_log.isWarnEnabled()) {\n                _log.warn(\"Unable to check visibility for foo entry \", pe);\n            }\n        }\n\n        return false;\n    }\n\n    protected boolean isVisible(int entryStatus, int queryStatus) {\n        if (((queryStatus != WorkflowConstants.STATUS_ANY) &&\n             (entryStatus == queryStatus)) ||\n            (entryStatus != WorkflowConstants.STATUS_IN_TRASH)) {\n\n            return true;\n        }\n\n        return false;\n    }\n\n    @Reference\n    protected FooEntryLocalService fooEntryLocalService;\n\n    private static final Log _log = LogFactoryUtil.getLog(\n        FooEntryModelVisibilityContributor.class);\n\n}\n\\end{verbatim}\n\nOnce you index model entities, add their terms to the Liferay DXP search\nquery, and get the summary just right, your model entity is ready to be\nsearched.\n\n\\chapter{Search Service Registration}\\label{search-service-registration}\n\nThe search framework must know about your entity and how to handle it\nduring a search request. To register model entities with Liferay's\nsearch framework, \\texttt{SearchRegistrar}s use the\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/registrar}{search\nframework's registry} to define certain things about your model entity's\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/registrar/ModelSearchDefinition.java}{\\texttt{ModelSearchDefinition}}:\nthe default fields used to retrieve documents from the search engine,\nand the optional search services registered for your entity (for\nexample, the \\texttt{ModelIndexWriterContributor} for you entity).\nRegistration occurs as soon as the Component is activated (during portal\nstartup or deployment of the bundle).\n\nA Registrar is required so the container knows about your\nimplementation.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  First, declare the class a component and create the class declaration:\n\n\\begin{verbatim}\n@Component(immediate = true, service = {})\npublic class FooEntrySearchRegistrar {\n\\end{verbatim}\n\\item\n  Next write the \\texttt{activate} method, annotated with the OSGi\n  annotation \\texttt{@Activate}. On activation of this component, call\n  the \\texttt{ModelSearchRegistrarHelper.register} method and use the\n  call to build out a \\texttt{ModelSearchDefinition}:\n\n\\begin{verbatim}\n@Activate\nprotected void activate(BundleContext bundleContext) {\n    _serviceRegistration = modelSearchRegistrarHelper.register(\n        FooEntry.class, bundleContext,\n        modelSearchDefinition -> {\n            modelSearchDefinition.setDefaultSelectedFieldNames(\n                Field.ASSET_TAG_NAMES, Field.COMPANY_ID, \n                Field.ENTRY_CLASS_NAME, Field.ENTRY_CLASS_PK,\n                Field.GROUP_ID, Field.MODIFIED_DATE, Field.SCOPE_GROUP_ID,\n                Field.UID);\n            modelSearchDefinition.setDefaultSelectedLocalizedFieldNames(\n                Field.CONTENT, Field.TITLE);\n            modelSearchDefinition.setModelIndexWriteContributor(\n                modelIndexWriterContributor);\n            modelSearchDefinition.setModelSummaryContributor(\n                modelSummaryContributor);\n            modelSearchDefinition.setModelVisibilityContributor(\n                modelVisibilityContributor);\n        });\n}\n\\end{verbatim}\n\n  On activation of the Component, a chain of search and indexing classes\n  is registered for the Foo entity. In addition, set the default\n  selected field names used to retrieve results documents from the\n  search engine. Then set the contributors used to build a model search\n  definition.\n\n  In addition to the \\texttt{ModelSearchDefinition} setter methods used\n  in the above code, there's another to be aware of:\n\n  To select all locales all the time when searching for your model\n  entity, pass \\texttt{true} to \\texttt{setSelectAllLocales}:\n\n\\begin{verbatim}\nmodelSearchDefinition.setSelectAllLocales(true);\n\\end{verbatim}\n\n  Technically, there's another setter in \\texttt{ModelSearchDefinition}\n  that takes a boolean,\n  \\texttt{setSearchResultPermissionFilterSuppressed}. However, this is\n  intended for internal consumption.\n\\item\n  Write a corresponding \\texttt{deactivate} method:\n\n\\begin{verbatim}\n@Deactivate\nprotected void deactivate() {\n    _serviceRegistration.unregister();\n}\n\\end{verbatim}\n\\item\n  Get references to the services needed in the class. For the search\n  services you're providing, specify them by entering the FQCN of your\n  model entity in the reference target's \\texttt{indexer.class.name}\n  property:\n\n\\begin{verbatim}\n@Reference(\n    target = \"(indexer.class.name=com.liferay.foo.model.FooEntry)\"\n)\nprotected ModelIndexerWriterContributor<FooEntry>\n    modelIndexWriterContributor;\n\n@Reference\nprotected ModelSearchRegistrarHelper modelSearchRegistrarHelper;\n\n@Reference(\n    target = \"(indexer.class.name=com.liferay.foo.model.FooEntry)\"\n)\nprotected ModelSummaryContributor modelSummaryContributor;\n\n@Reference(\n    target = \"(indexer.class.name=com.liferay.foo.model.FooEntry)\"\n)\nprotected ModelVisibilityContributor modelVisibilityContributor;\n\nprivate ServiceRegistration<?> _serviceRegistration;\n\\end{verbatim}\n\\end{enumerate}\n\nIt's quite possible you'll want to write this class after first getting\nall the search and indexing logic into place. How can you register a\n\\texttt{ModelIndexerWriterContributor} if you haven't written one yet?\n\n\\chapter{Search Queries and Filters}\\label{search-queries-and-filters}\n\nTo get sensible results from the search engine, you must provide a\nsensible query.\n\n\\section{Queries and Filters in Liferay's Search\nAPI}\\label{queries-and-filters-in-liferays-search-api}\n\nElasticsearch and Solr do not make API level distinctions between\nqueries and filters. In the past, Liferay's API explicitly provided two\nsets of APIs, one for queries and one for filters. Both APIs lived in\n\\texttt{portal-kernel} (the 7.1 source code for filters is\n\\href{https://github.com/liferay/liferay-portal/tree/7.1.x/portal-kernel/src/com/liferay/portal/kernel/search/filter}{here}).\n\nIn 7.0, there's a new way of querying and filtering via Liferay's Search\nAPI, and the APIs for it live in the \\texttt{portal-search-api} module.\nInstead of calling specific filter APIs, you'll now construct a query\nand add it to the search request, specifying it as a filter using the\n\\texttt{SearchRequestBuilder.postFilterQuery(Query)} method. See the\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/query}{7.2\nQuery APIs}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: Support for the legacy\n\\texttt{com.liferay.portal.kernel.search.Query.getPreBooleanFilter()} is\nonly present in the new search request builder and assembler\nimplementation to allow for backwards compatibility with the\n\\texttt{Indexer} framework's handling of queries. The older approach\nencourages some practices that are not ideal:\n\n\\begin{itemize}\n\\item\n  Wrapping a \\texttt{BooleanQuery} with another \\texttt{BooleanQuery}.\n\\item\n  Some queries shouldn't have filters according to Elasticsearch's API.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\nDespite the more unified filtering and querying code, you should\nunderstand the functional difference between filtering and querying:\n\n\\emph{Filters} ask a yes or no question for every document. A filter\nmight ask \\emph{is the status field equal to staging or live?}\n\n\\emph{Queries} ask the same yes or no question AND how well a document\nmatches the specified criteria. This is the concept of\n\\href{https://www.elastic.co/guide/en/elasticsearch/guide/current/scoring-theory.html}{relevance\nscoring}. A query might ask \\emph{Does the document's content field\nfield contain the words ``Liferay'', ``Content'', or ``Management'', and\nhow relevant is the content of the document to the searched keywords?}\n\n\\noindent\\hrulefill\n\n\\textbf{Hint:} Filtering is faster than querying, since the documents\nmatching a filter can be cached. Queries not only match documents but\nalso calculate scores. We recommend using filtering and querying\ntogether: filters to reduce the number of matched documents, queries for\nthe final examination.\n\n\\noindent\\hrulefill\n\n\\section{Supported Query Types}\\label{supported-query-types}\n\nLiferay's Search API supports the following types of queries:\n\n\\section{Full Text Queries}\\label{full-text-queries}\n\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/full-text-queries.html}{Full\ntext queries} are high-level queries usually used for querying full text\nfields like the \\texttt{content} field of a Blogs Entry. How terms are\nmatched depends on the query type.\n\n\\emph{Supported Full Text Queries}\n\n\\begin{verbatim}\nCommonTermsQuery\nMatchPhraseQuery\nMatchPhrasePrefixQuery\nMatchQuery\nMultiMatchQuery\nSimpleStringQuery\nStringQuery\n\\end{verbatim}\n\nHere are some common full text queries:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Match Query: A full text query, scored by relevance.\n\\item\n  Multi Match Query: Execute a \\texttt{MatchQuery} over several fields.\n\\item\n  String Query: Use Lucene query syntax.\n\\end{itemize}\n\n\\section{Term Queries}\\label{term-queries}\n\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/term-level-queries.html}{Term\nqueries} look for exact matching on keyword fields and indexed terms.\n\n\\begin{verbatim}\nExistsQuery\nFuzzyQuery\nIdsQuery\nPrefixQuery\nRangeQuery    \nRegexpQuery\nTermQuery\nTermsQuery\nTermRangeQuery\nTermsSetQuery\nWildcardQuery\n\\end{verbatim}\n\nHere are some common term queries:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Wildcard Query: Wildcard (\\texttt{*} and \\texttt{?}) matching on\n  keyword fields and indexed terms\n\\item\n  Fuzzy Query: Scrambles characters in input before matching\n\\end{itemize}\n\n\\section{Compound Queries}\\label{compound-queries}\n\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/compound-queries.html}{Compound\nqueries} are often used to wrap other queries. They're commonly used to\nswitch from query to filter context.\n\n\\begin{verbatim}\nBooleanQuery\nBoostingQuery\nConstantScoreQuery\nDisMaxQuery\nFunctionScoreQuery\n\\end{verbatim}\n\nHere are some common compound queries:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Boolean Query: Allows a combo of several query types. Individual\n  queries are as clauses with \\texttt{SHOULD} \\textbar{} \\texttt{MUST}\n  \\textbar{} \\texttt{MUST\\_NOT} \\textbar{} \\texttt{FILTER}\n\\item\n  Constant Score Query: Wraps another query, switches it to filter mode,\n  and gives all returned documents a constant relevance score.\n\\end{itemize}\n\n\\section{Joining Queries}\\label{joining-queries}\n\nThe concept of a join doesn't work well in a distributed index.\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/joining-queries.html}{Joining\nqueries} allow similar behavior in the search index, such as using the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/nested.html}{\\texttt{nested}\ndatatype} to index an array of objects that can be queried\nindependently, using the \\texttt{NestedQuery}.\n\n\\begin{verbatim}\nNestedQuery\n\\end{verbatim}\n\nNested Query: Query nested objects as if they each had a separate\ndocument in the index.\n\n\\section{Geo Queries}\\label{geo-queries}\n\nIn Elasticsearch, you can index latitude/longitude pairs and geographic\nshapes.\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/geo-queries.html}{Geo\nqueries} let you query for these points and shapes.\n\n\\begin{verbatim}\nGeoBoundingBoxQuery\nGeoDistanceQuery\nGeoDistanceRangeQuery\nGeoPolygonQuery\nGeoShapeQuery\n\\end{verbatim}\n\nA common Geo Query is the \\texttt{GeoDistanceQuery}, used to find\ndocuments within a certain distance of a geographic point\n(latitude/longitude).\n\n\\section{Specialized Queries}\\label{specialized-queries}\n\nThese\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/specialized-queries.html}{queries}\ndon't fit into another group:\n\n\\begin{verbatim}\nMoreLikeThisQuery\nPercolateQuery\nScriptQuery\n\\end{verbatim}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{More Like This:} Use a document to query for similar\n  documents.\n\\item\n  \\textbf{Percolate:} Match individual documents against indexed queries\n  (for alerting to new documents of interest, or automatically\n  categorizing documents).\n\\item\n  \\textbf{Script:} Filter based on a script.\n\\end{itemize}\n\n\\section{Other Queries}\\label{other-queries}\n\n\\texttt{MatchAllQuery} Matches all documents in the index.\n\nThe proper search query is entirely context- and search engine-specific,\nso you should read the Query documentation straight from\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl.html}{Elasticsearch}\nor\n\\href{https://lucene.apache.org/solr/guide/7_1/query-syntax-and-parsing.html}{Solr}\nto determine which queries are available and what they do.\n\nAll the recommended and supported queries and filters are found in the\n\\texttt{portal-search-api} module's\n\\texttt{com.liferay.portal.search.query} and\n\\texttt{com.liferay.portal.search.filter} packages.\n\nLegacy queries and filters, which are still supported but moving towards\ndeprecation, are found in the\n\\texttt{com.liferay.portal.kernel.search.*} packages provided by\n\\texttt{portal-kernel}.\n\n\\section{Using Queries}\\label{using-queries}\n\nHere's the generalized approach for querying and filtering search\ndocuments in your own search code:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Instantiate and construct the query object.\n\\item\n  Add the query to the search request---the method you use determines\n  whether the context is filtering or querying (or both in the same\n  request).\n\\item\n  Execute the search request.\n\\item\n  Process the search response.\n\\end{enumerate}\n\nThese steps are covered in more detail (with examples)\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/building-search-queries-and-filters}{in\nthe next article}.\n\n\\section{Search Queries in Liferay's\nCode}\\label{search-queries-in-liferays-code}\n\nThe APIs for creating queries are best exemplified in Liferay's own test\ncases. For example,\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-search/portal-search-test-util/src/main/java/com/liferay/portal/search/test/util/query/BaseTermsQueryTestCase.java}{BaseTermsQueryTestCase}\nconstructs a search request containing a \\texttt{TermsQuery} using the\n\\texttt{Queries} API:\n\n\\begin{verbatim}\nTermsQuery termsQuery = queries.terms(Field.USER_NAME);\n\\end{verbatim}\n\nThis code executes the search request with the terms query constructed\nabove in a query context.\n\nOther query test cases are also available to reference in the\n\\texttt{portal-search} module's\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/portal-search/portal-search-test-util/src/main/java/com/liferay/portal/search/test/util/query}{source\ncode}.\n\n\\section{External References}\\label{external-references-2}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\url{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl.html}\n\\item\n  \\url{https://lucene.apache.org/solr/guide/7_1/query-syntax-and-parsing.html}\n\\end{itemize}\n\n\\section{Search Engine Connector\nSupport}\\label{search-engine-connector-support-2}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Elasticsearch 6: Yes\n\\item\n  Solr 7: No* (Only the ``legacy'' query types from\n  \\texttt{com.liferay.portal.kernel.search.*} are supported as of\n  Liferay DXP Beta 2.)\n\\end{itemize}\n\n\\section{New/Related APIs}\\label{newrelated-apis-2}\n\nPackage \\textbar{} Provided by Artifact \\textbar{} Notes \\textbar{}\n\\texttt{com.liferay.portal.search.query.*} \\textbar{}\ncom.liferay.portal.search.api \\textbar{} Most of the provided\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/query}{query\ntypes} are new as of 7.2 \\texttt{com.liferay.portal.search.filter.*}\n\\textbar{} com.liferay.portal.search.api \\textbar{} Some of the provided\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/filter}{filter\ntypes} are new as of 7.2\n\n\\chapter{Building Search Queries and\nFilters}\\label{building-search-queries-and-filters}\n\nEach filter and query has a\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl.html}{different\npurpose}, but the way you'll add the information to the search request\nis similar between all queries and filters.\n\n\\section{Queries}\\label{queries}\n\nA mostly-complete code snippet for building Queries is provided for your\ncopying and pasting convenience \\hyperref[example]{below}.\n\n\\section{Declare Gradle Dependencies}\\label{declare-gradle-dependencies}\n\nAdd the following to your \\texttt{build.gradle} file:\n\n\\begin{verbatim}\ndependencies {\n    compileOnly group: \"biz.aQute.bnd\", name: \"biz.aQute.bndlib\", version: \"3.5.0\"\n    compileOnly group: \"com.liferay.portal\", name: \"release.portal.api\", version: \"7.2.0\"\n    compileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\", version: \"1.3.0\"\n}\n\\end{verbatim}\n\nWith this you can import all the Search APIs.\n\n\\section{Reference the Search\nServices}\\label{reference-the-search-services}\n\nTo satisfy the dependencies of the example code presented here, get\nreferences to\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.liferay.portal.search.searcher.SearchRequestBuilderFactory}\n\\item\n  \\texttt{com.liferay.portal.search.searcher.Searcher}\n\\item\n  \\texttt{com.liferay.portal.search.query.Queries}\n\\end{itemize}\n\n\\begin{verbatim}\n@Reference\nprotected Queries queries;\n\n@Reference\nprotected Searcher searcher;\n\n@Reference\nprotected SearchRequestBuilderFactory searchRequestBuilderFactory;\n\\end{verbatim}\n\n\\section{Build the Search Query}\\label{build-the-search-query-1}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Use the \\texttt{com.liferay.portal.search.query.Queries} interface to\n  instantiate the queries you'll construct. For example,\n\n\\begin{verbatim}\nTermsQuery termsQuery = queries.terms(\"fieldName\");\nMatchQuery matchQuery = queries.match(\"fieldName\", \"value\");\nBooleanQuery booleanQuery = queries.booleanQuery();\n\\end{verbatim}\n\n  To discover what parameters each query must have (e.g.,\n  \\texttt{String\\ field} in the case of the above\n  \\texttt{com.liferay.portal.search.query.TermsQuery}), see the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/query/Queries.java}{\\texttt{Queries}}\n  interface.\n\\item\n  Build out the queries to get the desired response. This looks\n  different for each query type, as explained in\n  \\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl.html}{Elasticsearch's\n  Query documentation}.\n\n\\begin{verbatim}\ntermsQuery.addValues(\"value1\", \"value2\");\n\\end{verbatim}\n\\item\n  You might want to wrap queries. For example, use the queries\n  constructed above as MUST clauses in a \\texttt{BooleanQuery} wrapper:\n\n\\begin{verbatim}\nbooleanQuery.addMustQueryClauses(termsQuery, matchQuery);\n\\end{verbatim}\n\\end{enumerate}\n\nOnce the query itself is in good shape, feed it to the search request.\n\n\\section{Build the Search Request}\\label{build-the-search-request}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Use\n  \\texttt{com.liferay.portal.search.searcher.SearchRequestBuilderFactory}\n  to get an instance of\n  \\texttt{com.liferay.portal.search.searcher.SearchRequestBuilder}:\n\n\\begin{verbatim}\nSearchRequestBuilder searchRequestBuilder =\n    searchRequestBuilderFactory.builder();\n\\end{verbatim}\n\n  If not setting search keywords into the \\texttt{SearchContext}\n  (covered below), make sure the request builder enables empty search.\n\n\\begin{verbatim}\nsearchRequestBuilder.emptySearchEnabled(true);\n\\end{verbatim}\n\n  Set the \\texttt{long\\ companyId} and, optionally,\n  \\texttt{String\\ keywords} into the\n  \\texttt{com.liferay.portal.kernel.search.SearchContext}:\n\n\\begin{verbatim}\nsearchRequestBuilder.withSearchContext(\n    searchContext -> {\n        searchContext.setCompanyId(companyId);\n        searchContext.setKeywords(keywords);\n    });\n\\end{verbatim}\n\n  Setting the Company ID into the \\texttt{SearchContext} is required to\n  ensure the correct index is searched.\n\n  Setting ``keywords'' on the \\texttt{SearchContext} is necessary if you\n  want to search via user input. For example, if providing a Search bar\n  in an application's view layer, pass its input into the search\n  context. Liferay's search framework adds the user input keywords and\n  any other data in the \\texttt{SearchContext} object to its own\n  queries, searching the appropriate fields of each indexed entity, as\n  defined by its\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/searching-the-index-for-model-entities\\#adding-your-model-entitys-terms-to-the-query}{\\texttt{KeywordQueryContributor}}\n  or by the \\texttt{postProcessSearchQuery} method of its\n  \\texttt{Indexer}.\n\\item\n  To execute the query, get a\n  \\texttt{com.liferay.portal.search.searcher.SearchRequest} instance\n  from the builder by adding the query to it and running its\n  \\texttt{build} method:\n\n\\begin{verbatim}\nSearchRequest searchRequest = \n    searchRequestBuilder.query(booleanQuery).build();\n\\end{verbatim}\n\\item\n  To use a constructed query in a filter context, call the\n  \\texttt{postFilterQuery} method while building the request:\n\n\\begin{verbatim}\nSearchRequest searchRequest = \n    searchRequestBuilder.postFilterQuery(termsQuery).build();\n\\end{verbatim}\n\\item\n  When constructing a search request, you'll often find it necessary to\n  chain the builder methods together:\n\n\\begin{verbatim}\nSearchRequest searchRequest = \n    searchRequestBuilder.postFilterQuery(myQuery1).query(myQuery2).build();\n\\end{verbatim}\n\n  Chaining allows you to add filters and queries (and anything else from\n  the builder API) to the same request in one fell swoop.\n\\end{enumerate}\n\n\\section{Execute the Search Request}\\label{execute-the-search-request}\n\nPerform a search using the\n\\texttt{com.liferay.portal.search.searcher.Searcher} service and the\n\\texttt{SearchRequest} to get a\n\\texttt{com.liferay.portal.search.searcher.SearchResponse}:\n\n\\begin{verbatim}\nSearchResponse searchResponse = searcher.search(searchRequest);\n\\end{verbatim}\n\n\\section{Process the Search Response}\\label{process-the-search-response}\n\nWhat you'll do with the \\texttt{SearchResponse} returned by the\n\\texttt{searcher.search} call is dependent on the type of query and your\nspecific use case. Much of the time you'll want to loop through the\n\\texttt{com.liferay.portal.search.hits.SearchHit} and\n\\texttt{com.liferay.portal.search.document.Document} objects, so that's\nwhat's shown here, with a simple message printed in the log for each\none.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get the \\texttt{SearchHits} from the response:\n\n\\begin{verbatim}\nSearchHits searchHits = searchResponse.getSearchHits();\n\\end{verbatim}\n\\item\n  Get a List of the \\texttt{SearchHit} objects:\n\n\\begin{verbatim}\nList<SearchHit> searchHitsList = searchHits.getSearchHits();\n\\end{verbatim}\n\\item\n  Loop through the \\texttt{SearchHit} objects in the List, get the\n  \\texttt{Document} associated with each one, printing its score and UID\n  to the console:\n\n\\begin{verbatim}\nsearchHitsList.forEach(\n    searchHit -> {\n        float hitScore = searchHit.getScore();\n\n        Document doc = searchHit.getDocument();\n\n        String uid = doc.getString(Field.UID);\n\n        System.out.println(\n            StringBundler.concat(\n                \"Document \", uid, \" had a score of \", hitScore));\n    });\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Search Insights: Request and Response\nStrings}\\label{search-insights-request-and-response-strings}\n\nWhen building a search application, it can be useful to inspect the\nrequest string (translated into the search engine's dialect), and\nsubsequently see the response string returned by the search server.\n\nRetrieve these from the \\texttt{SearchResponse} as\n\n\\begin{verbatim}\nsearchResponse.getRequestString();\nsearchResponse.getResponseString();\n\\end{verbatim}\n\nThe format depends on your search engine: with Elasticsearch, both are\nJSON.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The JSON returned as a request string is pruned from\nseveral Elasticsearch query defaults for clarity. To see the full\nrequest JSON that Elasticsearch processed, adjust the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/6.x/logging.html}{Elasticsearch\nserver's logging}.\n\n\\noindent\\hrulefill\n\nInspecting the request string produced by the code example included\n\\hyperref[example]{here} reveals two main \\texttt{\"bool\":\"must\"} query\nclauses in the JSON being sent to the search engine:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The \\texttt{BooleanQuery} explicitly declared in the code example.\n\\item\n  A (very long) query determined by the logic embedded in the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-search/portal-search/src/main/java/com/liferay/portal/search/internal/searcher/SearcherImpl.java\\#L137}{\\texttt{SearcherImpl\\#doSearch}}\n  method.\n\\end{enumerate}\n\nHow you construct the \\texttt{SearchRequest} determines how the\n\\texttt{Searcher} API processes it, which in turn influences the request\nString sent to Elasticsearch. For example, sending \\texttt{keywords}\ninto the \\texttt{SearchContext} object passed to the\n\\texttt{SearchRequest} ensures that queries for certain fields are\nexecuted on all searchable documents.\n\n\\section{Queries Example}\\label{queries-example}\n\nThe code below performs a \\texttt{MatchQuery} on the\n\\texttt{title\\_en\\_US} field for the value provided via the\n\\texttt{keywords} String. In addition, a \\texttt{TermsQuery} on the\n\\texttt{folderId} field is executed to match a value of \\texttt{0} (root\n\\texttt{JournalFolder}s are identified by\n\\texttt{JournalFolderConstants.DEFAULT\\_PARENT\\_FOLDER\\_ID}, which\nevaluates to \\texttt{0}). Both queries are wrapped in a\n\\texttt{BooleanQuery} must clause.\n\nBecause this example passes \\texttt{keywords} to the\n\\texttt{SearchContext}, \\texttt{emptySearchEnabled(true)} is not called\non the \\texttt{SearchRequestBuilder}. The \\texttt{keywords} variable is\nnot explicitly declared because this should come from user input.\nTherefore the example \\texttt{search} method receives \\texttt{keywords}\nas a parameter, along with the \\texttt{companyId}:\n\n\\begin{verbatim}\npackage com.liferay.docs.search;\n\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.util.LocaleUtil;\nimport com.liferay.portal.search.document.Document;\nimport com.liferay.portal.search.hits.SearchHit;\nimport com.liferay.portal.search.hits.SearchHits;\nimport com.liferay.portal.search.query.BooleanQuery;\nimport com.liferay.portal.search.query.MatchQuery;\nimport com.liferay.portal.search.query.Queries;\nimport com.liferay.portal.search.query.TermsQuery;\nimport com.liferay.portal.search.searcher.SearchRequest;\nimport com.liferay.portal.search.searcher.SearchRequestBuilder;\nimport com.liferay.portal.search.searcher.SearchRequestBuilderFactory;\nimport com.liferay.portal.search.searcher.SearchResponse;\nimport com.liferay.portal.search.searcher.Searcher;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\n@Component(\n    service = MySearchComponent.class\n)\npublic class MySearchComponent {\n\n    public List<String> search(long companyId, String keywords)\n        throws PortalException {\n\n        MatchQuery titleQuery = queries.match(\n            Field.getLocalizedName(LocaleUtil.US, Field.TITLE), keywords);\n\n        TermsQuery rootFolderQuery = queries.terms(Field.FOLDER_ID);\n\n        rootFolderQuery.addValues(String.valueOf(\n                JournalFolderConstants.DEFAULT_PARENT_FOLDER_ID));\n\n        BooleanQuery booleanQuery = queries.booleanQuery();\n\n        booleanQuery.addMustQueryClauses(rootFolderQuery, titleQuery);\n\n        SearchRequestBuilder searchRequestBuilder =\n            searchRequestBuilderFactory.builder();\n\n        // Uncomment this line below if you aren't setting \"keywords\"\n        // on the SearchContext\n        //      searchRequestBuilder.emptySearchEnabled(true);\n\n        searchRequestBuilder.withSearchContext(\n            searchContext -> {\n                searchContext.setCompanyId(companyId);\n                searchContext.setKeywords(keywords);\n            });\n\n        SearchRequest searchRequest = searchRequestBuilder.query(\n            booleanQuery\n        ).build();\n\n        SearchResponse searchResponse = searcher.search(searchRequest);\n\n        SearchHits searchHits = searchResponse.getSearchHits();\n\n        List<SearchHit> searchHitsList = searchHits.getSearchHits();\n\n        List<String> resultsList = new ArrayList<>(searchHitsList.size());\n\n        searchHitsList.forEach(\n            searchHit -> {\n                float hitScore = searchHit.getScore();\n\n                Document doc = searchHit.getDocument();\n\n                String uid = doc.getString(Field.UID);\n\n                System.out.println(\n                    StringBundler.concat(\n                        \"Document \", uid, \" had a score of \", hitScore));\n\n                resultsList.add(uid);\n            });\n\n        System.out.println(StringPool.EIGHT_STARS);\n\n        /*\n         *  // Uncomment to inspect the Request and Response Strings\n         * System.out.println( \"Request String:\\n\" + searchResponse.getRequestString() +\n         * \"\\n\" + StringPool.EIGHT_STARS);\n         * System.out.println( \"Response String:\\n\" +\n         * searchResponse.getResponseString() + \"\\n\" + StringPool.EIGHT_STARS);\n         */\n\n        return resultsList;\n    }\n\n    @Reference\n    protected Queries queries;\n\n    @Reference\n    protected Searcher searcher;\n\n    @Reference\n    protected SearchRequestBuilderFactory searchRequestBuilderFactory;\n\n}\n\\end{verbatim}\n\n\\section{Filters}\\label{filters}\n\nFilters as a distinct API-level object in Liferay DXP are going away.\nIt's best to mirror the APIs of the search engine, and neither supported\nsearch engine makes an API level distinction between queries and\nfilters. In recognition of this, there's a new way to perform\npost-filtering, which is filtering the returned search documents at the\nend of the search request (after calculating any aggregations). Add the\nfilter to the query using the \\texttt{postFilterQuery} method in the\nrequest builder:\n\n\\begin{verbatim}\nSearchRequestBuilder.postFilterQuery(Query);\n\\end{verbatim}\n\nAs you can see, this takes a \\texttt{Query} object, not a\n\\texttt{Filter}. Therefore, construct the \\texttt{Query} as in the\nprevious section, and specify it as a post-filter while building the\nrequest. All of the legacy \\texttt{Filter} objects from\n\\texttt{portal-kernel} can now be constructed as queries, per the above\nquery-building documentation.\n\n\\section{Legacy Filters in Legacy Search\nCalls}\\label{legacy-filters-in-legacy-search-calls}\n\nConstructing the filters found in \\texttt{portal-kernel}'s\n\\texttt{com.liferay.portal.kernel.search.filter} package is demonstrated\nby this \\texttt{new} term filter:\n\n\\begin{verbatim}\nTermFilter termFilter = new TermFilter(\"fieldName\", \"filterValue\");\n\\end{verbatim}\n\nFilters are added in legacy search calls by going through the\n\\texttt{Indexer} framework's \\texttt{postProcessContextBooleanFilter}\nmethod, which is invoked while the search framework is constructing the\nmain search query. See the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/users-admin/users-admin-impl/src/main/java/com/liferay/users/admin/internal/search/UserIndexer.java}{\\texttt{UserIndexer}'s\n\\texttt{addContextQueryParams} method}, which is called in the\noverridden \\texttt{postProcessContextBooleanFilter} to add the filter\nlogic.\n\n\\section{Discovering Indexed Fields}\\label{discovering-indexed-fields}\n\nTo find the fields to use in your Queries, navigate to \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{Search} in a running portal. From\nthere, open the Field Mappings tab and browse the mappings for the\n\\texttt{liferay-{[}companyId{]}} index. Scroll to the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/current/properties.html}{\\texttt{properties}}\nsection of the mapping.\n\nA summary of the text fields that are localized can be found\n\\href{/docs/7-2/user/-/knowledge_base/u/searching-for-localized-content}{here}.\n\n\\chapter{Segmentation and\nPersonalization}\\label{segmentation-and-personalization}\n\nSegments are groups of users that are defined by a specific criteria.\nYou can use the metadata from the user or organization profile, context\ninformation derived from the user's behavior, or some combination of the\ntwo to define that criteria. Alternatively, segments can be a static set\nof manually selected members.\n\nIn 7.0, the creation of user segments and experience personalization are\nnow part of the product's core functionality. Up to Liferay DXP 7.1,\nthis functionality was provided through the Audience Targeting\napplication. In addition to the administration features of Segmentation\nand Personalization, developers can integrate and extend it.\n\n\\section{Managing segments}\\label{managing-segments}\n\nThe API to manage segments is provided by the\n\\texttt{com.liferay.segments.api\\ module}. The\n\\texttt{SegmentsEntryService} provides the methods to perform permission\naware operations on segments. You can use the provided tools to assign\nmembers to segments and to extend segment criteria.\n\nThe \\texttt{segmentsEntry} criteria field determines the conditions that\na user must meet to be assigned to the segment. A condition represents a\ncombination of properties, operations, target values, and conjunctions.\nFor example, a condition identifying Liferay Engineers for a Segment\nmight look like this:\n\n\\begin{verbatim}\norganization name EQUALS Liferay AND Job Title EQUALS Engineer\n\\end{verbatim}\n\nIn the Segments UI, the segments criteria is built using the\n\\href{/docs/7-2/user/-/knowledge_base/u/the-segment-editor}{Segments\nEditor}. The available properties are grouped by topic (e.g.~User,\nOrganization, Session). Technically, they are called a\n\\texttt{SegmentsCriteriaContributor}, because they \\emph{contribute}\nconditions to the segments criteria.\n\nYou can see a number of common Segment management operations with\nexample code in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/segment-management}{Segment\nManagement}.\n\n\\section{Extending Segment Criteria}\\label{extending-segment-criteria}\n\nThe default segment capabilities are robust enough to cover most use\ncases, and many types of third party integration can be performed\nwithout developing a code extension. Some cases, like retrieving an\nexternal segment or list can be be handled by using the REST API.\n\nOn the other hand, if you want to segment users based on a field\nprovided from an external source (for example, the number of followers a\nuser has on Instagram), you can contribute an indexable\n\\href{/docs/7-2/user/-/knowledge_base/u/custom-fields}{custom field} to\nthe User entity and query the value using the Expando API. Your new\nfield is automatically available for its use as a profile-based\ncriteria.\n\nYou must only develop a code extension if you must:\n\n\\begin{itemize}\n\\item\n  Add a custom session property. This is done through the\n  \\texttt{RequestContextContributor}.\n\\item\n  Extend the criteria query with new queries, based either on existing\n  model entities or in custom model entities. This is done through the\n  \\texttt{SegmentsCriteriaContributor}.\n\\end{itemize}\n\n\\section{\\texorpdfstring{\\texttt{RequestContextContributor}}{RequestContextContributor}}\\label{requestcontextcontributor}\n\nUser and Organization properties are model-based properties. That means\nthat the available criteria for users and organizations are based on the\nattributes for users and organizations defined by the entities\n\\texttt{model}. Criteria for model-based entities can be extended by\ncreating a Custom Field for the corresponding model. Session properties\nare context-based properties and can't be extended through custom\nfields. To allow for user segmentation based on new context-based\nproperties, like custom HTTP headers or attributes, you must develop an\nextension and deploy it in your Liferay DXP instance.\n\nThe default fields available for context-based segmentation can be found\nin the Context interface. Liferay generates a context instance with\nreal-time information for every request. These are mostly obtained from\nthe \\texttt{HttpServletRequest}. The \\texttt{RequestContextContributor}\ninterface provides an extension point for adding a new context-based\nproperty to the Session panel in the Segments criteria editor and\npopulating the segmentation context with the right value for that field.\n\nThe following service properties define a\n\\texttt{RequestContextContributor}:\n\n\\texttt{request.context.contributor.key}: the unique key of the\ncontributed field.\n\n\\texttt{request.context.contributor.type}: the contributor field type\n(boolean, date, double, integer, or string{[}default{]}).\n\nThe \\texttt{contribute} method of the \\texttt{RequestContextContributor}\nadds the custom field key-value pair to the context.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/request-context-contributor.png}\n\\caption{Learn more about a \\texttt{RequestContextContributor} by\nviewing how it's used.}\n\\end{figure}\n\nTo create a \\texttt{RequestContextContributor} through the step by step\nprocess, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-request-context-contributor}{Creating\na Request Context Contributor}.\n\n\\section{\\texorpdfstring{\\texttt{SegmentsCriteriaContributor}}{SegmentsCriteriaContributor}}\\label{segmentscriteriacontributor}\n\nThe \\texttt{SegmentsCriteriaContributor} interface provides a mechanism\nto extend the segment criteria query. Each\n\\texttt{SegmentsCriteriaContributor} contributes a sub-query (or\ncriterion) and the conjunction (AND, OR) to build the complete criteria\nquery that defines the segment. They also provide a list of Field\nelements to be shown in the Segment Editor UI, as depicted in the\nfigure:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/segment-field-contributor.png}\n\\caption{Learn more about a \\texttt{SegmentsCriteriaContributor} by\nviewing how it's used.}\n\\end{figure}\n\nThe following service properties describe a\n\\texttt{SegmentsCriteriaContributor}:\n\n\\texttt{segments.criteria.contributor.key}: the unique key that\nidentifies the contributor.\n\n\\texttt{segments.criteria.contributor.model.class.name}: the entity type\nthe contributor targets.\n\n\\texttt{segments.criteria.contributor.priority}: the order in which the\nfields and queries are contributed.\n\nThe \\texttt{UserOrganizationSegmentsCriteriaContributor} is a good\nexample of how a \\texttt{SegmentsCriteriaContributor} works. It\ncontributes new organization-related fields (\\emph{Organization\nProperties}) to the segments criteria editor, executes a query on the\nOrganization based model, and finally contributes a subquery to the\nglobal user query (AND/OR the user belongs to the organizations found in\nthe Organization model query). In summary, you can filter users based on\naspects of a different but related entity, such as the organization.\n\nTo create a \\texttt{SegmentsCriteriaContributor} through the step by\nstep process, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-segment-criteria-contributor}{Creating\nSegment Criteria Contributors}.\n\n\\chapter{Segment Management}\\label{segment-management}\n\nThere are a broad array of uses for Segments and ways that you can\nintegrate them with your application. You'll learn more about how to\nmanage segments next.\n\n\\section{Defining a Segment}\\label{defining-a-segment}\n\nThis snippet defines a segment by retrieving \\texttt{@Reference} objects\nfrom \\texttt{SegmentsCriteriaContributor} and instantiating a new\n\\texttt{Criteria} object. It then adds user criteria using the\n\\texttt{segmentsEntryService}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  the user's \\texttt{jobTitle} is \\texttt{Developer} \\textbf{AND}\n\\item\n  the user belongs to an Organization with a name that contains\n  \\texttt{America}\n\\end{itemize}\n\n\\begin{verbatim}\nprivate void addSegmentWithCriteria() {\n    Criteria criteria = new Criteria();\n\n    _userSegmentsCriteriaContributor.contribute(\n        criteria, \"(jobTitle eq 'Developer')\", Criteria.Conjunction.AND);\n    _organizationCriteriaContributor.contribute(\n        criteria, \"contains(name,'America')\", Criteria.Conjunction.OR);\n\n    segmentsEntryService.addSegmentsEntry(\n        \"segment-key\", nameMap, descriptionMap, true, CriteriaSerializer.serialize(criteria),\n                    User.class.getName(), serviceContext);\n}\n\n@Reference(target = \"(segments.criteria.contributor.key=organization)\")\nprivate SegmentsCriteriaContributor _organizationSegmentsCriteriaContributor;\n\n@Reference(target = \"(segments.criteria.contributor.key=user)\")\nprivate SegmentsCriteriaContributor _userSegmentsCriteriaContributor;\n\\end{verbatim}\n\n\\section{Manual Segment Member\nAssignments}\\label{manual-segment-member-assignments}\n\nTo define manual user-segment member assignments with the\n\\texttt{SegmentsEntryRelService}, use a snippet like this:\n\n\\begin{verbatim}\nsegmentsEntryRelService.addSegmentsEntryRel(\n    segmentsEntryId, _portal.getClassNameId(User.class), userId, serviceContext)\n\\end{verbatim}\n\nThis assigns a user identified by a \\texttt{userId} to a segment\nidentified by a \\texttt{segmentsEntryId}:\n\n\\section{Retrieving Segments}\\label{retrieving-segments}\n\nSegments and Segment Members can be retrieved programmatically. The code\nsnippet below retrieves an ordered range of active segments for the\n\\texttt{User}, within a site identified by a \\texttt{groupId}.\n\n\\begin{verbatim}\nList<SegmentsEntry> segmentsEntries = segmentsEntryService.getSegmentsEntries(groupId, true, User.class.getName(), 0, 10, orderByComparator);\n\\end{verbatim}\n\n\\section{Retrieving segment members}\\label{retrieving-segment-members}\n\nThe local API to query computed segment-member associations is available\nin the \\texttt{com.liferay.segments.api\\ module}. The\n\\texttt{SegmentsEntryProvider} service provides methods to obtain the\nentities associated to a segment, and the segments associated to an\nentity.\n\nThis snippet retrieves a range of primary keys of the entities\nassociated to a segment identified by a \\texttt{segmentsEntryId}:\n\n\\begin{verbatim}\nlong[] segmentsEntryClassPKs = segmentsEntryProvider.getSegmentsEntryClassPKs(segmentsEntryId, 0, 10);\n\\end{verbatim}\n\nTo obtain the total count of entities associated to a segment, use the\n\\texttt{getSegmentsEntryClassPKsCount} method, as shown in the following\nsnippet:\n\n\\begin{verbatim}\nint segmentsEntryClassPksCount =\n    segmentsEntryProvider.getSegmentsEntryClassPKsCount(segmentsEntryId);\n\\end{verbatim}\n\nThe method \\texttt{getSegmentsEntryIds} obtains the reverse association\n--- the segments associated to a specific entity. For example, this\nsnippet returns the segments associated to a user identified by a\n\\texttt{userId}:\n\n\\begin{verbatim}\nint segmentsEntryClassPksCount =\n    segmentsEntryProvider.getSegmentsEntryIds(User.class.getName(), userId);\n\\end{verbatim}\n\nGreat! You now know how to manage segments!\n\n\\chapter{Creating a Request Context\nContributor}\\label{creating-a-request-context-contributor}\n\nTo better understand the Request Context Contributor, you'll explore how\nto create one. First, you'll create the\n\\texttt{SampleRequestContentContributor} class file, which contains the\n\\texttt{contribute} method that contributes a new field to the context\nwith a custom attribute. You can view the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/apps/segments/segments-context-extension-sample}{full\nproject on Github}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  a new module}.\n\\item\n  Inside the module, create a package named\n  \\texttt{com.liferay.segments.context.extension.sample.internal.context.contributor}\n\\item\n  Create a Java class named \\texttt{SampleRequestContentContributor}.\n\\item\n  Inside the file, insert the \\texttt{@Component} declaration:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"request.context.contributor.key=\" + SampleRequestContextContributor.KEY,\n        \"request.context.contributor.type=boolean\"\n    },\n    service = RequestContextContributor.class\n)\n\\end{verbatim}\n\\item\n  Add the class declaration:\n\n\\begin{verbatim}\npublic class SampleRequestContextContributor\n    implements RequestContextContributor {\n\n}\n\\end{verbatim}\n\\item\n  Create the attribute that you're adding. In this case, it's just a\n  static string.\n\n\\begin{verbatim}\npublic static final String KEY = \"sample\";\n\\end{verbatim}\n\\item\n  Create the \\texttt{contribute} method:\n\n\\begin{verbatim}\n@Override\npublic void contribute(\n    Context context, HttpServletRequest httpServletRequest) {\n\n        context.put(KEY,\n        GetterUtil.getBoolean(httpServletRequest.getAttribute(\"sample.attribute\")));\n    }\n\\end{verbatim}\n\\end{enumerate}\n\nTo customize your field label or add a set of selectable options, you\ncan add an optional \\texttt{SegmentsFieldCustomizer} service associated\nto your contributed field by its key. Create one now.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Inside the module, create a package named\n  \\texttt{com.liferay.context.extension.sample.internal.field.customizer}\n\\item\n  Create a Java class named \\texttt{SampleSegmentsFieldCustomizer}.\n\\item\n  Inside the file, insert the \\texttt{@Component} declaration:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"segments.field.customizer.entity.name=Context\",\n        \"segments.field.customizer.key=\" + SampleSegmentsFieldCustomizer.KEY,\n        \"segments.field.customizer.priority:Integer=50\"\n    },\n    service = SegmentsFieldCustomizer.class\n)\n\\end{verbatim}\n\\item\n  Create the class declaration:\n\n\\begin{verbatim}\npublic class SampleSegmentsFieldCustomizer implements SegmentsFieldCustomizer {\n\n}\n\\end{verbatim}\n\\item\n  Create the \\texttt{KEY} value:\n\n\\begin{verbatim}\npublic static final String KEY = \"sample\";\n\\end{verbatim}\n\\item\n  Create the methods to provide a list of fields to be displayed when\n  configuring the criteria.\n\n\\begin{verbatim}\n@Override\npublic List<String> getFieldNames() {\n    return _fieldNames;\n}\n\n@Override\npublic String getKey() {\n    return KEY;\n}\n\n@Override\npublic String getLabel(String fieldName, Locale locale) {\n        ResourceBundle resourceBundle = ResourceBundleUtil.getBundle(\n        \"content.Language\", locale, getClass());\n\n        return LanguageUtil.get(resourceBundle, \"sample-field-label\");\n}\n\nprivate static final List<String> _fieldNames = ListUtil.fromArray(\n        new String[] {\"sample\"});\n\\end{verbatim}\n\\end{enumerate}\n\nOnce you deploy your extensions, the session section of the segment\ncriteria editor includes your new context-based field.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/context-based-field.png}\n\\caption{The sample field appears.}\n\\end{figure}\n\nGreat! You've created a Request Context Contributor!\n\n\\chapter{Creating a Segment Criteria\nContributor}\\label{creating-a-segment-criteria-contributor}\n\nTo demonstrate the Segment Criteria Contributor, you'll create a\ncontributor that segments users based on the title of Knowledge Base\narticles they have authored.\n\nThe first step is to make your related entity searchable through OData\nqueries. For this purpose, you must have classes:\n\n\\begin{itemize}\n\\item\n  \\texttt{EntityModel}: represents your associated entity (in this case,\n  the \\texttt{KBArticle}) with its fields of interest.\n\\item\n  \\texttt{ODataRetriever}: obtains the \\texttt{KBArticles} that match a\n  given OData query.\n\\end{itemize}\n\nYou can view the\n\\href{https://github.com/epgarcia/liferay-portal/tree/LPS-86249.criteria.extension.sample.2/modules/apps/segments/segments-criteria-extension-sample}{full\nproject on Github}.\n\nFollow the instructions below to get started.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  a module}.\n\\item\n  Create the following packages within the module:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{com.liferay.segments.criteria.extension.sample.internal.odata.retreiver}\n  \\item\n    \\texttt{com.liferay.segments.criteria.extension.sample.internal.odata.entity}\n  \\item\n    \\texttt{com.liferay.segments.criteria.extension.sample.internal.criteria.contributor}\n  \\end{itemize}\n\\end{enumerate}\n\nExcellent! You have your module ready. Next, you'll create the entity\nmodel.\n\n\\section{Creating the Entity Model}\\label{creating-the-entity-model}\n\nFirst, create the Entity Model for \\texttt{KBArticle}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Inside the \\texttt{...internal.odata.entity} package, create the\n  \\texttt{KBArticleEntityModel} class which implements\n  \\texttt{EntityModel}:\n\n\\begin{verbatim}\npublic class KBArticleEntityModel implements EntityModel {\n\n}\n\\end{verbatim}\n\\item\n  Create the key for the entity name:\n\n\\begin{verbatim}\npublic static final String NAME = \"KBArticle\";\n\\end{verbatim}\n\\item\n  Create the variable for the entity field map:\n\n\\begin{verbatim}\nprivate final Map<String, EntityField> _entityFieldsMap;\n\\end{verbatim}\n\\item\n  Create the methods to retrieve the \\texttt{KBArticleEntity}, entity\n  map, and entity name key:\n\n\\begin{verbatim}\npublic KBArticleEntityModel() {\n    _entityFieldsMap = Stream.of(\n        new StringEntityField(\"title\", locale -> \"titleKeyword\")\n        ).collect(\n            Collectors.toMap(EntityField::getName, Function.identity())\n        );\n}\n\n@Override\npublic Map<String, EntityField> getEntityFieldsMap() {\n    return _entityFieldsMap;\n}\n\n@Override\npublic String getName() {\n    return NAME;\n}\n\\end{verbatim}\n\\end{enumerate}\n\nNext, you'll create the OData Retriever.\n\n\\section{\\texorpdfstring{Creating the\n\\texttt{ODataRetriever}}{Creating the ODataRetriever}}\\label{creating-the-odataretriever}\n\nNext, create the \\texttt{ODataRetriever} which gets the data using the\nrelevant filter.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Inside the \\texttt{...internal.odata.retreiver} package, create\n  \\texttt{KBArticleODataRetriever.java} which implements\n  \\texttt{ODataRetriever}:\n\n\\begin{verbatim}\npublic class KBArticleODataRetriever implements ODataRetriever<KBArticle> {\n\n}\n\\end{verbatim}\n\\item\n  Add the \\texttt{@Component} declaration above the class declaration:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = \"model.class.name=com.liferay.knowledge.base.model.KBArticle\",\n    service = ODataRetriever.class\n)\n\\end{verbatim}\n\\item\n  Create the \\texttt{@Reference} objects that you need for the Filter\n  Parser, Knowledge Base Article Service, and OData Search Adapter:\n\n\\begin{verbatim}\n@Reference\nprivate FilterParserProvider _filterParserProvider;\n\n @Reference\nprivate KBArticleLocalService _kbArticleLocalService;\n\n@Reference\nprivate ODataSearchAdapter _oDataSearchAdapter;\n\\end{verbatim}\n\\item\n  Create and instantiate the \\texttt{\\_entityModel} object for the\n  \\texttt{KBArticle} model:\n\n\\begin{verbatim}\nprivate static final EntityModel _entityModel = new KBArticleEntityModel();\n\\end{verbatim}\n\\item\n  Create the public methods to retrieve the results and the results\n  count from the OData filter:\n\n\\begin{verbatim}\n@Override\npublic List<KBArticle> getResults(\n        long companyId, String filterString, Locale locale, int start,  int end)\n    throws PortalException {\n\n    Hits hits = _oDataSearchAdapter.search(\n        companyId, filterString, KBArticle.class.getName(), _entityModel,\n            _getFilterParser(), locale, start, end);\n\n    return _getKBArticles(hits);\n}\n\n@Override\npublic int getResultsCount(\n        long companyId, String filterString, Locale locale)\n    throws PortalException {\n\n    return _oDataSearchAdapter.searchCount(\n        companyId, filterString, KBArticle.class.getName(), _entityModel,\n            _getFilterParser(), locale);\n}\n\\end{verbatim}\n\\item\n  Create the private methods for instantiating the \\texttt{FilterParser}\n  and retrieving the Knowledge Base article(s) that meet the criteria:\n\n\\begin{verbatim}\nprivate FilterParser _getFilterParser() {\n    return _filterParserProvider.provide(_entityModel);\n}\n\nprivate KBArticle _getKBArticle(Document document) throws PortalException {\n    long resourcePrimKey = GetterUtil.getLong(\n        document.get(Field.ENTRY_CLASS_PK));\n\n    return _kbArticleLocalService.getLatestKBArticle(resourcePrimKey, 0);\n}\n\nprivate List<KBArticle> _getKBArticles(Hits hits) throws PortalException {\n    Document[] documents = hits.getDocs();\n\n    List<KBArticle> kbArticles = new ArrayList<>(documents.length);\n\n    for (Document document : documents) {\n        kbArticles.add(_getKBArticle(document));\n    }\n\n    return kbArticles;\n}\n\\end{verbatim}\n\\end{enumerate}\n\nYou're all set to create the Segments Criteria Contributor!\n\n\\section{\\texorpdfstring{Creating the\n\\texttt{SegmentsCriteriaContributor}}{Creating the SegmentsCriteriaContributor}}\\label{creating-the-segmentscriteriacontributor}\n\nNow create the \\texttt{SegmentsCriteriaContributor} class that consumes\nthe previous classes to retrieve the articles that match the query\ngenerated by the criteria editor, and contributes a query to filter\nusers based on the articles they authored.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\texttt{...internal.criteria.contributor} package, create a\n  \\texttt{UserKBArticleSegmentCritieriaContributor} class that\n  implements \\texttt{SegmentsCriteriaContributor}.\n\n\\begin{verbatim}\npublic class UserKBArticleSegmentsCriteriaContributor\n    implements SegmentsCriteriaContributor {\n\n}\n\\end{verbatim}\n\\item\n  Create the \\texttt{@Component} declaration to set properties and\n  declare the service class.\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"segments.criteria.contributor.key=\" + UserKBArticleSegmentsCriteriaContributor.KEY,\n        \"segments.criteria.contributor.model.class.name=com.liferay.portal.kernel.model.User\",\n        \"segments.criteria.contributor.priority:Integer=70\"\n    },\n    service = SegmentsCriteriaContributor.class\n)\n\\end{verbatim}\n\\item\n  Create the variables to enable logging, retrieve the entity model, and\n  entity key.\n\n\\begin{verbatim}\nprivate static final Log _log = LogFactoryUtil.getLog(\n    UserKBArticleSegmentsCriteriaContributor.class);\n\n    private static final EntityModel _entityModel = new KBArticleEntityModel();\n\n    public static final String KEY = \"user-kb-article\";\n\\end{verbatim}\n\\item\n  Create the reference variables for the OData retriever and Portal\n  instance.\n\n\\begin{verbatim}\n@Reference(\n    target = \"(model.class.name=com.liferay.knowledge.base.model.KBArticle)\"\n)\n\nprivate ODataRetriever<KBArticle> _oDataRetriever;\n\n@Reference\nprivate Portal _portal;\n\\end{verbatim}\n\\item\n  Create the methods to define the implementation of\n  \\texttt{SegmentsCriteriaContributor}.\n\n\\begin{verbatim}\n@Override\npublic void contribute(\n    Criteria criteria, String filterString,\n        Criteria.Conjunction conjunction) {\n\n    criteria.addCriterion(getKey(), getType(), filterString, conjunction);\n\n    long companyId = CompanyThreadLocal.getCompanyId();\n    String newFilterString = null;\n\n    try {\n        StringBundler sb = new StringBundler();\n\n        List<KBArticle> kbArticles = _oDataRetriever.getResults(\n            companyId, filterString, LocaleUtil.getDefault(),\n            QueryUtil.ALL_POS, QueryUtil.ALL_POS);\n\n        for (int i = 0; i < kbArticles.size(); i++) {\n            KBArticle kbArticle = kbArticles.get(i);\n\n            sb.append(\"(userId eq '\");\n            sb.append(kbArticle.getUserId());\n            sb.append(\"')\");\n\n            if (i < (kbArticles.size() - 1)) {\n                sb.append(\" or \");\n            }\n        }\n\n        newFilterString = sb.toString();\n    }\n    catch (PortalException pe) {\n        _log.error(\n            com.liferay.petra.string.StringBundler.concat(\n                \"Unable to evaluate criteria \", criteria, \" with filter \",\n                filterString, \" and conjunction \", conjunction.getValue()),\n            pe);\n    }\n\n    if (Validator.isNull(newFilterString)) {\n        newFilterString = \"(userId eq '0')\";\n    }\n\n    criteria.addFilter(getType(), newFilterString, conjunction);\n}\n\n@Override\npublic EntityModel getEntityModel() {\n    return _entityModel;\n}\n\n@Override\npublic String getEntityName() {\n    return KBArticleEntityModel.NAME;\n}\n\n@Override\npublic List<Field> getFields(PortletRequest portletRequest) {\n    return Collections.singletonList(\n        new Field(\n            \"title\",\n            LanguageUtil.get(_portal.getLocale(portletRequest), \"title\"),\n            \"string\"));\n}\n\n@Override\npublic String getKey() {\n    return KEY;\n}\n\n@Override\npublic Criteria.Type getType() {\n    return Criteria.Type.MODEL;\n}\n\\end{verbatim}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  your module}.\n\\end{enumerate}\n\nAfter deploying your extension, the segment criteria editor includes a\nnew section containing Knowledge Base properties. Notice that the\nsection's UI, the properties, and their associated input fields and\noperations have been automatically generated based on the information\nprovided by the extension services. For instance, the Knowledge Base\narticle title supports \\emph{equals}, \\emph{not equals},\n\\emph{contains}, and \\emph{not contains} operations because it was\ndefined as a \\texttt{StringEntityField}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/segment-new-category.png}\n\\caption{The sample field appears.}\n\\end{figure}\n\nAwesome! You've created a Segment Criteria Contributor!\n\n\\chapter{ServiceContext}\\label{servicecontext}\n\nThe \\texttt{ServiceContext} class holds contextual information for a\nservice. It aggregates information necessary for features used\nthroughout Liferay's portlets, such as permissions, tagging,\ncategorization, and more. This article covers the following\n\\texttt{ServiceContext} class topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[service-context-fields]{Service Context Fields}\n\\item\n  \\hyperref[creating-and-populating-a-service-context]{Creating and\n  Populating a Service Context in Java}\n\\item\n  \\hyperref[creating-and-populating-a-service-context-in-javascript]{Creating\n  and Populating a Service Context in JavaScript}\n\\item\n  \\hyperref[accessing-service-context-data]{Accessing Service Context\n  Data}\n\\end{itemize}\n\nThe \\texttt{ServiceContext} fields are first.\n\n\\section{Service Context Fields}\\label{service-context-fields}\n\nThe \\texttt{ServiceContext} class has many fields. The\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/service/ServiceContext.html}{\\texttt{ServiceContext}\nclass Javadoc} describes them.\n\nHere's a categorical listing of some commonly used Service Context\nfields:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Actions:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_command}\n  \\item\n    \\texttt{\\_workflowAction}\n  \\end{itemize}\n\\item\n  Attributes:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_attributes}\n  \\item\n    \\texttt{\\_expandoBridgeAttributes}\n  \\end{itemize}\n\\item\n  Classification:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_assetCategoryIds}\n  \\item\n    \\texttt{\\_assetTagNames}\n  \\end{itemize}\n\\item\n  Exception\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_failOnPortalException}\n  \\end{itemize}\n\\item\n  IDs and Scope:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_companyId}\n  \\item\n    \\texttt{\\_portletPreferencesIds}\n  \\item\n    \\texttt{\\_plid}\n  \\item\n    \\texttt{\\_scopeGroupId}\n  \\item\n    \\texttt{\\_userId}\n  \\item\n    \\texttt{\\_uuid}\n  \\end{itemize}\n\\item\n  Language:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_languageId}\n  \\end{itemize}\n\\item\n  Miscellaneous:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_headers}\n  \\item\n    \\texttt{\\_signedIn}\n  \\end{itemize}\n\\item\n  Permissions:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_addGroupPermissions}\n  \\item\n    \\texttt{\\_addGuestPermissions}\n  \\item\n    \\texttt{\\_deriveDefaultPermissions}\n  \\item\n    \\texttt{\\_modelPermissions}\n  \\end{itemize}\n\\item\n  Request\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_request}\n  \\end{itemize}\n\\item\n  Resources:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_assetEntryVisible}\n  \\item\n    \\texttt{\\_assetLinkEntryIds}\n  \\item\n    \\texttt{\\_assetPriority}\n  \\item\n    \\texttt{\\_createDate}\n  \\item\n    \\texttt{\\_formDate}\n  \\item\n    \\texttt{\\_indexingEnabled}\n  \\item\n    \\texttt{\\_modifiedDate}\n  \\item\n    \\texttt{\\_timeZone}\n  \\end{itemize}\n\\item\n  URLs, paths and addresses:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_currentURL}\n  \\item\n    \\texttt{\\_layoutFullURL}\n  \\item\n    \\texttt{\\_layoutURL}\n  \\item\n    \\texttt{\\_pathMain}\n  \\item\n    \\texttt{\\_pathFriendlyURLPrivateGroup}\n  \\item\n    \\texttt{\\_pathFriendlyURLPrivateUser}\n  \\item\n    \\texttt{\\_pathFriendlyURLPublic}\n  \\item\n    \\texttt{\\_portalURL}\n  \\item\n    \\texttt{\\_remoteAddr}\n  \\item\n    \\texttt{\\_remoteHost}\n  \\item\n    \\texttt{\\_userDisplayURL}\n  \\end{itemize}\n\\end{itemize}\n\nAre you wondering how the \\texttt{ServiceContext} fields get populated?\nGood! You'll learn about that next.\n\n\\section{Creating and Populating a Service\nContext}\\label{creating-and-populating-a-service-context}\n\nAlthough all the \\texttt{ServiceContext} class fields are optional,\nservices that store data with scope must at least specify the scope\ngroup ID. Here's an example of creating a \\texttt{ServiceContext}\ninstance and passing it as a parameter to a Liferay service API:\n\n\\begin{verbatim}\nServiceContext serviceContext = new ServiceContext();\nserviceContext.setScopeGroupId(myGroupId);\n\n...\n\n_blogsEntryService.addEntry(..., serviceContext);\n\\end{verbatim}\n\nIf you invoke the service from a servlet, a Struts action, or any other\nfront-end class with access to the \\texttt{PortletRequest}, use one of\nthe \\texttt{ServiceContextFactory.getInstance(...)} methods. These\nmethods create a \\texttt{ServiceContext} object from the request and\nautomatically populate its fields with all the values specified in the\nrequest. The above example looks different if you invoke the service\nfrom a servlet:\n\n\\begin{verbatim}\nServiceContext serviceContext =\n    ServiceContextFactory.getInstance(BlogsEntry.class.getName(), portletRequest);\n\n...\n\n_blogsEntryService.addEntry(..., serviceContext);\n\\end{verbatim}\n\nYou can see an example of populating a \\texttt{ServiceContext} with\ninformation from a request object in the code of the\n\\texttt{ServiceContextFactory.getInstance(...)} methods. The methods\ndemonstrate how to set parameters like \\emph{scope group ID},\n\\emph{company ID}, \\emph{language ID}, and more. They also demonstrate\nhow to access and populate more complex context parameters like\n\\emph{tags}, \\emph{categories}, \\emph{asset links}, \\emph{headers}, and\nthe \\emph{attributes} parameter. By calling\n\\texttt{ServiceContextFactory.getInstance(String\\ className,\\ PortletRequest\\ portletRequest)},\nyou can assure that your Expando bridge attributes are set on the\n\\texttt{ServiceContext}. Expandos are the back-end implementation of\ncustom fields for entities in Liferay.\n\n\\section{Creating and Populating a Service Context in\nJavaScript}\\label{creating-and-populating-a-service-context-in-javascript}\n\nLiferay's API can be invoked in languages other than Java. Some methods\nrequire or allow a \\texttt{ServiceContext} parameter. If you're invoking\nsuch a method via Liferay's JSON web services, you might want to create\nand populate a \\texttt{ServiceContext} object in JavaScript. Creating a\n\\texttt{ServiceContext} object in JavaScript is no different from\ncreating any other object in JavaScript.\n\nBefore examining a JSON web service invocation that uses a\n\\texttt{ServiceContext} object, it helps to see a simple JSON web\nservice example in JavaScript:\n\n\\begin{verbatim}\nLiferay.Service(\n    '/user/get-user-by-email-address`,\n    {\n        companyId: 20101,\n        emailAddress: 'test@example.com`\n    },\n    function(obj) {\n        console.log(obj);\n    }\n);\n\\end{verbatim}\n\nIf you run this code, the \\emph{test@example.com} user (JSON object) is\nlogged to the JavaScript console.\n\nThe \\texttt{Liferay.Service(...)} function takes three arguments:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  A string representing the service being invoked\n\\item\n  A parameters object\n\\item\n  A callback function\n\\end{enumerate}\n\nThe callback function takes the result of the service invocation as an\nargument.\n\nThe Liferay JSON web services page (its URL is\n\\url{localhost:8080/api/jsonws} if you're running Liferay locally on\nport 8080) generates example code for invoking web services. To see the\ngenerated code for a particular service, click on the name of the\nservice, enter the required parameters, and click \\emph{Invoke}. The\nJSON result of your service invocation appears. There are multiple ways\nto invoke Liferay's JSON web services: click on \\emph{JavaScript\nExample} to see how to invoke the web service via JavaScript, click on\n\\emph{curl Example} to see how to invoke the web service via curl, or\nclick on \\emph{URL example} to see how to invoke the web service via a\nURL.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/jsonws-simple-example.png}\n\\caption{When you invoke a service from Liferay's JSON web services\npage, you can view the result of your service invocation as well as\nexample code for invoking the service via JavaScript, curl, or URL.}\n\\end{figure}\n\nNext, you'll learn how to access information from a\n\\texttt{ServiceContext} object.\n\n\\section{Accessing Service Context\nData}\\label{accessing-service-context-data}\n\nIn this section, you'll find code snippets from\n\\texttt{BlogsEntryLocalServiceImpl.addEntry(...,\\ ServiceContext)}. This\ncode demonstrates how to access information from a\n\\texttt{ServiceContext} and provides an example of how the context\ninformation can be used.\n\nAs mentioned above, services for entities with scope must get a scope\ngroup ID from the \\texttt{ServiceContext} object. This is true for the\nBlogs entry service because the scope group ID provides the scope of the\nBlogs entry (the entity being persisted). For the Blogs entry, the scope\ngroup ID is used in the following way:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  It's used as the \\texttt{groupId} for the \\texttt{BlogsEntry} entity.\n\\item\n  It's used to generate a unique URL for the blog entry.\n\\item\n  It's used to set the scope for comments on the blog entry.\n\\end{itemize}\n\nHere are the corresponding code snippets:\n\n\\begin{verbatim}\nlong groupId = serviceContext.getScopeGroupId();\n\n...\n\nentry.setGroupId(groupId);\n\n...\n\nentry.setUrlTitle(getUniqueUrlTitle(entryId, groupId, title));\n\n...\n\n// Message boards\n\nif (PropsValues.BLOGS_ENTRY_COMMENTS_ENABLED) {\n    mbMessageLocalService.addDiscussionMessage(\n        userId, entry.getUserName(), groupId,\n        BlogsEntry.class.getName(), entryId,\n        WorkflowConstants.ACTION_PUBLISH);\n}\n\\end{verbatim}\n\nCan \\texttt{ServiceContext} be used to access the UUID of the blog\nentry? Absolutely! Can you use \\texttt{ServiceContext} to set the time\nthe blog entry was added? You sure can. See here:\n\n\\begin{verbatim}\nentry.setUuid(serviceContext.getUuid());\n...\nentry.setCreateDate(serviceContext.getCreateDate(now));\n\\end{verbatim}\n\nCan \\texttt{ServiceContext} be used in setting permissions on resources?\nYou bet! When adding a blog entry, you can add new permissions or apply\nexisting permissions for the entry, like this:\n\n\\begin{verbatim}\n// Resources\n\nif (serviceContext.isAddGroupPermissions() ||\n    serviceContext.isAddGuestPermissions()) {\n\n    addEntryResources(\n        entry, serviceContext.isAddGroupPermissions(),\n        serviceContext.isAddGuestPermissions());\n}\nelse {\n    addEntryResources(\n        entry, serviceContext.getGroupPermissions(),\n        serviceContext.getGuestPermissions());\n}\n\\end{verbatim}\n\n\\texttt{ServiceContext} helps apply categories, tag names, and the link\nentry IDs to asset entries too. Asset links are the back-end term for\nrelated assets in Liferay.\n\n\\begin{verbatim}\n// Asset\n\nupdateAsset(\n    userId, entry, serviceContext.getAssetCategoryIds(),\n    serviceContext.getAssetTagNames(),\n    serviceContext.getAssetLinkEntryIds());\n\\end{verbatim}\n\nDoes \\texttt{ServiceContext} also play a role in starting a workflow\ninstance for the blogs entry? Must you ask?\n\n\\begin{verbatim}\n// Workflow\n\nif ((trackbacks != null) && (trackbacks.length > 0)) {\n    serviceContext.setAttribute(\"trackbacks\", trackbacks);\n}\nelse {\n    serviceContext.setAttribute(\"trackbacks\", null);\n}\n\n_workflowHandlerRegistry.startWorkflowInstance(\n    user.getCompanyId(), groupId, userId, BlogsEntry.class.getName(),\n    entry.getEntryId(), entry, serviceContext);\n\\end{verbatim}\n\nThe snippet above also demonstrates the \\texttt{trackbacks} attribute, a\nstandard attribute for the blogs entry service. There may be cases where\nyou need to pass in custom attributes to your blogs entry service. Use\nExpando attributes to carry custom attributes along in your\n\\texttt{ServiceContext}. Expando attributes are set on the added blogs\nentry like this:\n\n\\begin{verbatim}\nentry.setExpandoBridgeAttributes(serviceContext);\n\\end{verbatim}\n\nYou can see that the \\texttt{ServiceContext} can be used to transfer\nlots of useful information for your services. Understanding how\n\\texttt{ServiceContext} is used in Liferay helps you determine when and\nhow to use \\texttt{ServiceContext} in your own Liferay application\ndevelopment.\n\n\\section{Related Topics}\\label{related-topics-119}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder}{Business\nLogic with Service Builder}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services}{Invoking\nLocal Services}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/web-services}{Web\nServices}\n\n\\chapter{Injecting Service Components into Integration\nTests}\\label{injecting-service-components-into-integration-tests}\n\nTest driven development plays a key role in quality assurance. Liferay's\ntooling and integration with standard test frameworks support test\ndriven development and help you reach quality milestones. You can use\nLiferay DXP's \\texttt{@Inject} annotation to inject service components\ninto an integration test, like you use the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{\\texttt{@Reference}\nannotation to inject service components} into an OSGi component.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} \\href{http://arquillian.org/}{Arquillian} plus\n\\href{https://junit.org}{JUnit} annotations is one way to develop\nintegration tests. Liferay lets you use whatever testing framework you\nwant.\n\n\\noindent\\hrulefill\n\nFollow these steps to inject a service component into a test class:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your test class, add a rule field of\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-test-integration/com/liferay/portal/test/rule/LiferayIntegrationTestRule.html}{type\n  \\texttt{com.liferay.portal.test.rule.LiferayIntegrationTestRule}}. For\n  example,\n\n\\begin{verbatim}\n@ClassRule\n@Rule\npublic static final AggregateTestRule aggregateTestRule = \n    new LiferayIntegrationTestRule();\n\\end{verbatim}\n\\item\n  Add a field to hold a service component. Making the field static\n  improves efficiency because the container injects static fields once\n  before test runs and nulls them after all tests run. Non-static fields\n  are injected before each test run but stay in memory till all tests\n  finish.\n\\item\n  Annotate the field with an \\texttt{@Inject} annotation. By default,\n  the container injects the field with a service component object\n  matching the field's type.\n\n  \\texttt{@Inject} uses reflection to inject a field with a service\n  component object matching the field's interface.\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-test-integration/com/liferay/portal/test/rule/LiferayIntegrationTestRule.html}{Test\n  rule \\texttt{LiferayIntegrationTestRule}} provides the annotation.\n\\item\n  Optionally add a \\texttt{filter} string or \\texttt{type} parameter to\n  further specify the service component object to inject. They can be\n  used separately or together.\n\n  To fill a field with a particular implementation or sub-class object,\n  set the \\texttt{type} with it.\n\n\\begin{verbatim}\n@Inject(type = SubClass.class)\n\\end{verbatim}\n\n  Replace \\texttt{SubClass} with the name of the service interface to\n  inject.\n\\end{enumerate}\n\nAt runtime, the \\texttt{@Inject} annotation blocks the test until a\nmatching service component is available. The block has a timeout and\nmessages are logged regarding the test's unavailable dependencies.\n\n\\noindent\\hrulefill\n\n\\textbf{Important}: If you're publishing the service component you are\ninjecting, the test might never run. If you must publish the service\ncomponent from the test class, use\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker}{Service\nTrackers} to access service components.\n\n\\noindent\\hrulefill\n\nHere's an example test class that injects a \\texttt{DDLServiceUpgrade}\nobject into an \\texttt{UpgradeStepRegistrator} interface field:\n\n\\begin{verbatim}\npublic class Test {\n\n    @ClassRule\n    @Rule\n    public static final AggregateTestRule aggregateTestRule = \n        new LiferayIntegrationTestRule();\n\n    @Test\n    public void testSomething() {\n        // your test code here\n    }\n\n    @Inject(\n        filter = \"(&(objectClass=com.liferay.dynamic.data.lists.internal.upgrade.DDLServiceUpgrade))\"\n    )\n    private static UpgradeStepRegistrator _upgradeStepRegistrator;\n\n}\n\\end{verbatim}\n\nGreat! Now you can inject service components into your tests.\n\n\\section{Related Topics}\\label{related-topics-120}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker}{Service\n  Trackers}\n\\end{itemize}\n\n\\chapter{Upgrade Processes}\\label{upgrade-processes}\n\nThe development process doesn't end when you first release your\napplication. Through your own planning, feature requests, and bug\nreports, developers improve their applications on a regular basis.\nSometimes, those changes result in changes to the data structure and\nunderlying database. When users upgrade, they need a process that\ntransitions them to improved versions of your application. For this, you\nmust create an upgrade process.\n\nHere's what's involved in creating an upgrade process for your app:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Specifying the schema version\n\\item\n  Declaring dependencies\n\\item\n  Writing upgrade steps\n\\item\n  Writing the registrator\n\\item\n  Waiting for upgrade completion\n\\end{itemize}\n\nLiferay has an Upgrade framework you can use to make this easier to do.\nIt's a feature-rich framework that makes upgrades safe: the system\nrecords the current state of the schema so that if the upgrade fails,\nthe process can revert the module back to its previous version.\n\\href{/docs/7-2/reference/-/knowledge_base/r/meaningful-schema-versioning}{Meaningful\nschema versioning} is important to clearly communicate the updates to\nyour users.\n\nLiferay DXP's Upgrade framework executes your module's upgrades\nautomatically when the new version starts for the first time. You\nimplement concrete data schema changes in upgrade step classes and then\nregister them with the upgrade framework using an \\emph{upgrade step}\nregistrator. An upgrade step is a class that adapts module data to the\nmodule's target database schema. It can execute SQL commands and DDL\nfiles to upgrade the data. The Upgrade framework lets you encapsulate\nupgrade logic in multiple upgrade step classes per schema version.\n\nThe Upgrade framework executes the upgrade steps to update the current\nmodule data to the latest schema. The registrator's \\texttt{register}\nmethod informs the Upgrade framework about each new schema and\nassociated upgrade steps to adapt data to it. Each schema upgrade is\nrepresented by a \\emph{registration}. A registration is an abstraction\nfor all the changes you need to apply to the database from one schema\nversion to the next one.\n\nUpgrade registrations are defined by the following values:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Module's bundle symbolic name}\n\\item\n  \\textbf{Schema version to upgrade from} (as a \\texttt{String})\n\\item\n  \\textbf{Schema version to upgrade to} (as a \\texttt{String})\n\\item\n  \\textbf{List of upgrade steps}\n\\end{itemize}\n\nA registration's upgrade step list can consist of as many upgrade steps\nas needed. How you name and organize upgrade steps is up to you.\nLiferay's upgrade classes are organized using a package structure\nsimilar to this one:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{some.package.structure}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{upgrade}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{v1\\_1\\_0}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{UpgradeFoo.java} ← Upgrade Step\n      \\end{itemize}\n    \\item\n      \\texttt{v2\\_0\\_0}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{UpgradeFoo.java} ← Upgrade Step\n      \\item\n        \\texttt{UpgradeBar.java} ← Upgrade Step\n      \\end{itemize}\n    \\item\n      \\texttt{MyCustomModuleUpgrade.java} ← Registrator\n    \\end{itemize}\n  \\end{itemize}\n\\end{itemize}\n\nThe example upgrade structure shown above is for a module that has two\ndatabase schema versions: \\texttt{1.1.0} and \\texttt{2.0.0}. They're\nrepresented by packages \\texttt{v1\\_1\\_0} and \\texttt{v2\\_0\\_0}. Each\nversion package contains upgrade step classes that update the database.\nThe example upgrade steps focus on fictitious data elements \\texttt{Foo}\nand \\texttt{Bar}. The registrator class (\\texttt{MyCustomModuleUpgrade},\nin this example) is responsible for registering the applicable upgrade\nsteps for each schema version.\n\nHere are some organizational tips:\n\n\\begin{itemize}\n\\item\n  Put all upgrade classes in a sub-package called \\texttt{upgrade}.\n\\item\n  Group together similar database updates (ones that operate on a data\n  element or related data elements) in the same upgrade step class.\n\\item\n  Create upgrade steps in sub-packages named after each data schema\n  version.\n\\end{itemize}\n\nThe diagram below illustrates the relationship between the registrator\nand the upgrade steps.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/data-upgrade-module-upgrade-architecture.png}\n\\caption{In a registrator class, the developer specifies a registration\nfor each schema version upgrade. The upgrade steps handle the database\nupdates.}\n\\end{figure}\n\nThis section covers these topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-upgrade-process-for-your-app}{Creating\n  an upgrade process for your app}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes-for-former-service-builder-plugins}{Creating\n  upgrade processes for former service builder plugins}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/upgrading-data-schemas-in-development}{Upgrading\n  data schemas in development}\n\\end{itemize}\n\n\\chapter{Creating Upgrade Processes for\nModules}\\label{creating-upgrade-processes-for-modules}\n\nFollow these steps to create an upgrade process for your module:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your module's \\texttt{bnd.bnd} file, and specify a\n  \\texttt{Liferay-Require-SchemaVersion} header with the new schema\n  version value. Here's an example schema version header for a module\n  whose new schema is version \\texttt{1.1}:\n\n\\begin{verbatim}\nLiferay-Require-SchemaVersion: 1.1\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Important**: If no `Liferay-Require-SchemaVersion` header is specified, \n Liferay DXP considers the `Bundle-Version` header value to be the database \n schema version. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Add\n  a dependency} on the\n  \\href{https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.portal.upgrade/}{\\texttt{com.liferay.portal.upgrade}\n  module}, along with any other modules your upgrade process requires,\n  in your your module's dependency management file (e.g., Maven POM,\n  Gradle build file, or Ivy \\texttt{ivy.xml} file). An example\n  configuration for a \\texttt{build.gradle} file is shown below:\n\n\\begin{verbatim}\ncompile group: \"com.liferay\", name: \"com.liferay.portal.upgrade.api\", version: \"2.0.3\"\n\\end{verbatim}\n\\item\n  Create an \\texttt{UpgradeProcess} class that extends the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/upgrade/UpgradeProcess.html}{\\texttt{UpgradeProcess}\n  base class} ( which implements the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/upgrade/UpgradeStep.html}{\\texttt{UpgradeStep}\n  interface}):\n\n\\begin{verbatim}\npublic class MyUpgradeSchemaClass extends UpgradeProcess {\n  ...\n}\n\\end{verbatim}\n\\item\n  Override the \\texttt{UpgradeProcess} class's \\texttt{doUpgrade()}\n  method with instructions for modifying the database. Use the\n  \\texttt{runSQL} and \\texttt{runSQLTemplate*} methods (inherited from\n  the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/db/BaseDBProcess.html}{\\texttt{BaseDBProcess}\n  class}) to execute your SQL commands and SQL DDL, respectively. If you\n  want to create, modify, or drop tables or indexes by executing DDL\n  sentences from an SQL file, make sure to use ANSI SQL only. Doing this\n  assures the commands work on different databases. If you need to use\n  non-ANSI SQL, it's best to write it in the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/upgrade/UpgradeProcess.html}{\\texttt{UpgradeProcess}\n  class's} \\texttt{runSQL} or \\texttt{alter} methods, along with tokens\n  that allow porting the sentences to different databases, as shown in\n  journal-service module's\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/journal/journal-service/src/main/java/com/liferay/journal/internal/upgrade/v0_0_4/UpgradeSchema.java}{\\texttt{UpgradeSchema}\n  upgrade step class} below which uses the \\texttt{runSQLTemplateString}\n  method to execute ANSI SQL DDL from an SQL file and the \\texttt{alter}\n  method and\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/upgrade/UpgradeProcess.html}{\\texttt{UpgradeProcess}'s}\n  \\texttt{UpgradeProcess.AlterColumnName} and\n  \\texttt{UpgradeProcess.AlterColumnType} inner classes as token classes\n  to modify column names and column types:\n\n\\begin{verbatim}\npublic class UpgradeSchema extends UpgradeProcess {\n\n  @Override\n  protected void doUpgrade() throws Exception {\n      String template = StringUtil.read(\n          UpgradeSchema.class.getResourceAsStream(\"dependencies/update.sql\"));\n\n      runSQLTemplateString(template, false, false);\n\n      upgrade(UpgradeMVCCVersion.class);\n\n      alter(\n          JournalArticleTable.class,\n          new AlterColumnName(\n              \"structureId\", \"DDMStructureKey VARCHAR(75) null\"),\n          new AlterColumnName(\n              \"templateId\", \"DDMTemplateKey VARCHAR(75) null\"),\n          new AlterColumnType(\"description\", \"TEXT null\"));\n\n      alter(\n          JournalFeedTable.class,\n          new AlterColumnName(\"structureId\", \"DDMStructureKey TEXT null\"),\n          new AlterColumnName(\"templateId\", \"DDMTemplateKey TEXT null\"),\n          new AlterColumnName(\n              \"rendererTemplateId\", \"DDMRendererTemplateKey TEXT null\"),\n          new AlterColumnType(\"targetPortletId\", \"VARCHAR(200) null\"));\n  }\n}\n\\end{verbatim}\n\n  Here's a simpler example upgrade step from the\n  \\texttt{com.liferay.calendar.service} module. It uses the\n  \\texttt{alter} method to modify a column type in the calendar booking\n  table:\n\n\\begin{verbatim}\npublic class UpgradeCalendarBooking extends UpgradeProcess {\n\n        @Override\n        protected void doUpgrade() throws Exception {\n                alter(\n                        CalendarBookingTable.class,\n                        new AlterColumnType(\"description\", \"TEXT null\"));\n        }\n\n}\n\\end{verbatim}\n\\item\n  If your application was modularized from a former traditional Liferay\n  plugin application (application WAR) and it uses Service Builder,\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes-for-former-service-builder-plugins}{create\n  and register a Bundle Activator} to register it in Liferay DXP's\n  \\texttt{Release\\_} table.\n\\item\n  Create an \\texttt{UpgradeStepRegistrator} OSGi Component class of\n  service type \\texttt{UpgradeStepRegistrator.class} that implements the\n  \\href{https://docs.liferay.com/dxp/apps/foundation/latest/javadocs/com/liferay/portal/upgrade/registry/UpgradeStepRegistrator.html}{\\texttt{UpgradeStepRegistrator}\n  interface}:\n\n\\begin{verbatim}\npackage com.liferay.mycustommodule.upgrade;\n\nimport com.liferay.portal.upgrade.registry.UpgradeStepRegistrator;\n\nimport org.osgi.service.component.annotations.Component;\n\n@Component(immediate = true, service = UpgradeStepRegistrator.class)\npublic class MyCustomModuleUpgrade implements UpgradeStepRegistrator {\n\n\n}\n\\end{verbatim}\n\\item\n  Override the\n  \\href{https://docs.liferay.com/dxp/apps/foundation/latest/javadocs/com/liferay/portal/upgrade/registry/UpgradeStepRegistrator.html\\#register-com.liferay.portal.upgrade.registry.UpgradeStepRegistrator.Registry-}{\\texttt{register}\n  method} to implement the module's upgrade registrations---abstractions\n  for the upgrade steps required to update the database from one schema\n  version to the next. For example, the upgrade step registrator class\n  \\texttt{MyCustomModuleUpgrade} (below) registers three upgrade steps\n  incrementally for each schema version (past and present,\n  \\texttt{0.0.0} to \\texttt{2.0.0}, \\texttt{1.0.0} to \\texttt{1.1.0},\n  and \\texttt{1.1.0} to \\texttt{2.0.0}).\n\n  The first registration is applied if the module hasn't been installed\n  previously. It contains only one empty upgrade step:\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/portal-kernel/src/com/liferay/portal/kernel/upgrade/DummyUpgradeStep.java}{new\n  \\texttt{DummyUpgradeStep}}(). This registration records the module's\n  latest schema version (i.e., \\texttt{2.0.0}) in Liferay DXP's\n  \\texttt{Release\\_} table. Note that if the same class name is used in\n  multiple packages, you must provide the fully qualified class name for\n  the class, as shown in the second registration (\\texttt{1.0.0} to\n  \\texttt{1.1.0}) below for the \\texttt{UpgradeFoo} class:\n\n\\begin{verbatim}\npackage com.liferay.mycustommodule.upgrade;\n\nimport com.liferay.portal.upgrade.registry.UpgradeStepRegistrator;\n\nimport org.osgi.service.component.annotations.Component;\n\n@Component(immediate = true, service = UpgradeStepRegistrator.class)\npublic class MyCustomModuleUpgrade implements UpgradeStepRegistrator {\n\n    @Override\n    public void register(Registry registry) {\n        registry.register(\n            \"com.liferay.mycustommodule\", \"0.0.0\", \"2.0.0\",\n            new DummyUpgradeStep());\n\n        registry.register(\n            \"com.liferay.mycustommodule\", \"1.0.0\", \"1.1.0\",\n            new com.liferay.mycustommodule.upgrade.v1_1_0.UpgradeFoo());\n\n        registry.register(\n            \"com.liferay.mycustommodule\", \"1.1.0\", \"2.0.0\",\n            new com.liferay.mycustommodule.upgrade.v2_0_0.UpgradeFoo(),\n            new UpgradeBar());\n    }\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Important**: Modules that use Service Builder *should not* define a\n registration for their initial database schema version, as Service Builder\n already records their schema versions to Liferay DXP's `Release_` table. Modules\n that don't use Service Builder, however, *should* define a registration for\n their initial schema. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{7}\n\\item\n  If your upgrade step uses an OSGi service, \\textbf{your upgrade must\n  wait for that service's availability}. Use the \\texttt{@Reference}\n  annotation to declare any classes that the registrator class depends\n  on. For example, the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/wiki/wiki-service/src/main/java/com/liferay/wiki/internal/upgrade/WikiServiceUpgrade.java}{\\texttt{WikiServiceUpgrade}\n  registrator class} below references the \\texttt{SettingsFactory} class\n  for the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/wiki/wiki-service/src/main/java/com/liferay/wiki/internal/upgrade/v1_0_0/UpgradePortletSettings.java}{\\texttt{UpgradePortletSettings}\n  upgrade step}. The \\texttt{setSettingsFactory} method's\n  \\texttt{@Reference} annotation declares that the registrator class\n  depends on and must wait for the \\texttt{SettingsFactory} service to\n  be available in the run time environment:\n\n\\begin{verbatim}\n@Component(immediate = true, service = UpgradeStepRegistrator.class)\npublic class WikiServiceUpgrade implements UpgradeStepRegistrator {\n\n    @Override\n    public void register(Registry registry) {\n        registry.register(\"0.0.1\", \"0.0.2\", new UpgradeSchema());\n\n        registry.register(\"0.0.2\", \"0.0.3\", new UpgradeKernelPackage());\n\n        registry.register(\n            \"0.0.3\", \"1.0.0\", new UpgradeCompanyId(),\n            new UpgradeLastPublishDate(), new UpgradePortletPreferences(),\n            new UpgradePortletSettings(_settingsFactory), new UpgradeWikiPage(),\n            new UpgradeWikiPageResource());\n\n        registry.register(\"1.0.0\", \"1.1.0\", new UpgradeWikiNode());\n\n        registry.register(\n            \"1.1.0\", \"1.1.1\",\n            new UpgradeDiscussionSubscriptionClassName(\n                _subscriptionLocalService, WikiPage.class.getName(),\n                UpgradeDiscussionSubscriptionClassName.DeletionMode.ADD_NEW));\n\n        registry.register(\n            \"1.1.1\", \"2.0.0\",\n            new BaseUpgradeSQLServerDatetime(\n                new Class<?>[] {WikiNodeTable.class, WikiPageTable.class}));\n    }\n\n    @Reference\n    private SettingsFactory _settingsFactory;\n\n    @Reference\n    private SubscriptionLocalService _subscriptionLocalService;\n\n}\n\\end{verbatim}\n\\item\n  Upgrade the database to the latest database schema version before\n  making its services available. To do this, configure the Bnd header\n  \\texttt{Liferay-Require-SchemaVersion} to the latest schema version\n  for \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\n  Builder} services. For all other services, specify an\n  \\texttt{@Reference} annotation that targets the containing module and\n  its latest schema version.\n\n  Here are the target's required attributes:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{release.bundle.symbolic.name}: module's bundle symbolic name\n  \\item\n    \\texttt{release.schema.version}: module's current schema version\n  \\end{itemize}\n\n  For example, the \\texttt{com.liferay.comment.page.comments.web}\n  module's\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/comment/comment-page-comments-web/src/main/java/com/liferay/comment/page/comments/web/internal/portlet/PageCommentsPortlet.java}{\\texttt{PageCommentsPortlet}\n  class} upgrades to schema version \\texttt{2.0.0} by defining the\n  reference below:\n\n\\begin{verbatim}\n@Reference(\n    target = \"(&(release.bundle.symbolic.name=com.liferay.comment.page.comments.web)(release.schema.version=2.0.0))\"\n)\nprivate Release _release;\n\\end{verbatim}\n\n  Dependencies between OSGi services can reduce the number of service\n  classes in which upgrade reference annotations are needed. For\n  example, there's no need to add an upgrade reference in a dependent\n  service, if the dependency already refers to the upgrade.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note**: Data verifications using the class `VerifyProcess` are deprecated.\n Verifications should be tied to schema versions. Upgrade processes are associated\n with schema versions but `VerifyProcess` instances are not.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nGreat! Now you know how to create data upgrades for all your modules.\n\n\\section{Related Topics}\\label{related-topics-121}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes-for-former-service-builder-plugins}{Upgrade\n  Processes for Former Service Builder Plugins}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver}{Upgrading\n  Code to 7.0}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configurable-applications}{Configurable\n  Applications}\n\\end{itemize}\n\n\\chapter{Upgrade Processes for Former Service Builder\nPlugins}\\label{upgrade-processes-for-former-service-builder-plugins}\n\nIf you modularized a traditional Liferay plugin application that\nimplements Service Builder services, your new modular application must\nregister itself in the Liferay DXP's \\texttt{Release\\_} table. This is\nrequired regardless of whether release records already exist for\nprevious versions of the app. A Bundle Activator is the recommended way\nto add a release record for the first modular version of your converted\napplication.\n\n\\textbf{Important}: The steps covered in this article only apply to\nmodular applications that use Service Builder and were modularized from\ntraditional Liferay plugin applications. They do not apply to you if\nyour application doesn't use Service Builder or has never been a\ntraditional Liferay plugin application (a WAR application).\n\nBundle Activator class code is dense but straightforward. Referring to\nan example Bundle Activator can be helpful. Here's the Liferay Knowledge\nBase application's Bundle Activator:\n\n\\begin{verbatim}\npublic class KnowledgeBaseServiceBundleActivator implements BundleActivator {\n\n    @Override\n    public void start(BundleContext bundleContext) throws Exception {\n        Filter filter = bundleContext.createFilter(\n            StringBundler.concat(\n                \"(&(objectClass=\", ModuleServiceLifecycle.class.getName(), \")\",\n                ModuleServiceLifecycle.DATABASE_INITIALIZED, \")\"));\n\n        _serviceTracker = new ServiceTracker<Object, Object>(\n            bundleContext, filter, null) {\n\n            @Override\n            public Object addingService(\n                ServiceReference<Object> serviceReference) {\n\n                try {\n                    BaseUpgradeServiceModuleRelease\n                        upgradeServiceModuleRelease =\n                            new BaseUpgradeServiceModuleRelease() {\n\n                                @Override\n                                protected String getNamespace() {\n                                    return \"KB\";\n                                }\n\n                                @Override\n                                protected String getNewBundleSymbolicName() {\n                                    return \"com.liferay.knowledge.base.service\";\n                                }\n\n                                @Override\n                                protected String getOldBundleSymbolicName() {\n                                    return \"knowledge-base-portlet\";\n                                }\n\n                            };\n\n                    upgradeServiceModuleRelease.upgrade();\n\n                    return null;\n                }\n                catch (UpgradeException ue) {\n                    throw new RuntimeException(ue);\n                }\n            }\n\n        };\n\n        _serviceTracker.open();\n    }\n\n    @Override\n    public void stop(BundleContext bundleContext) {\n        _serviceTracker.close();\n    }\n\n    private ServiceTracker<Object, Object> _serviceTracker;\n\n}\n\\end{verbatim}\n\nFollow these steps to create a Bundle Activator:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a class that implements the\n  \\texttt{org.osgi.framework.BundleActivator} interface:\n\n\\begin{verbatim}\npublic class KnowledgeBaseServiceBundleActivator implements BundleActivator {\n\n}\n\\end{verbatim}\n\\item\n  Add a service tracker field:\n\n\\begin{verbatim}\n`private ServiceTracker<Object, Object> _serviceTracker;`\n\\end{verbatim}\n\\item\n  Override BundleActivator's \\texttt{stop} method to close the service\n  tracker:\n\n\\begin{verbatim}\n@Override\npublic void stop(BundleContext bundleContext) throws Exception {\n    _serviceTracker.close();\n}\n\\end{verbatim}\n\\item\n  Override BundleActivator's \\texttt{start} method to instantiate a\n  service tracker that creates a filter to listen for the app's database\n  initialization event and initializes the service tracker to use that\n  filter. You'll add the service tracker initialization code in the next\n  steps. At the end of the \\texttt{start} method, open the service\n  tracker.\n\n\\begin{verbatim}\n@Override\npublic void start(BundleContext bundleContext) throws Exception {\n    Filter filter = bundleContext.createFilter(\n        StringBundler.concat(\n            \"(&(objectClass=\", ModuleServiceLifecycle.class.getName(), \")\",\n            ModuleServiceLifecycle.DATABASE_INITIALIZED, \")\"));\n\n    _serviceTracker = new ServiceTracker<Object, Object>(\n        bundleContext, filter, null) {\n        // See the next step for this code ...\n    };\n\n    _serviceTracker.open();\n}\n\\end{verbatim}\n\\item\n  In the service tracker initialization block\n  \\texttt{\\{\\ //\\ See\\ the\\ next\\ step\\ for\\ this\\ \\ \\ \\ \\ \\ code\\ ...\\ \\}}\n  from the previous step, add an \\texttt{addingService} method that\n  instantiates a \\texttt{BaseUpgradeServiceModuleRelease} for describing\n  your app. The example \\texttt{BaseUpgradeServiceModuleRelease}\n  instance below describes Liferay's Knowledge Base app:\n\n\\begin{verbatim}\n@Override\npublic Object addingService(\n    ServiceReference<Object> serviceReference) {\n\n    try {\n        BaseUpgradeServiceModuleRelease\n                upgradeServiceModuleRelease =\n            new BaseUpgradeServiceModuleRelease() {\n\n                @Override\n                protected String getNamespace() {\n                    return \"KB\";\n                }\n\n                @Override\n                protected String getNewBundleSymbolicName() {\n                    return \"com.liferay.knowledge.base.service\";\n                }\n\n                @Override\n                protected String getOldBundleSymbolicName() {\n                    return \"knowledge-base-portlet\";\n                }\n\n            };\n\n        upgradeServiceModuleRelease.upgrade();\n\n        return null;\n    }\n    catch (UpgradeException ue) {\n        throw new RuntimeException(ue);\n    }\n}\n\\end{verbatim}\n\n  The \\texttt{BaseUpgradeServiceModuleRelease} implements the following\n  methods:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{getNamespace}: Returns the namespace value as specified in\n    the former plugin's \\texttt{service.xml} file. This value is also in\n    the \\texttt{buildNamespace} field in the plugin's\n    \\texttt{ServiceComponent} table record.\n  \\item\n    \\texttt{getOldBundleSymbolicName}: Returns the former plugin's name.\n  \\item\n    \\texttt{getNewBundleSymbolicName}: Returns the module's symbolic\n    name. In the module's \\texttt{bnd.bnd} file, it's the\n    \\texttt{Bundle-SymbolicName} value.\n  \\item\n    \\texttt{upgrade}: Invokes the app's upgrade processes.\n  \\end{itemize}\n\\item\n  In the module's \\texttt{bnd.bnd} file, reference the Bundle Activator\n  class you created. Here's the example's Bundle Activator reference:\n\n\\begin{verbatim}\nBundle-Activator: com.liferay.knowledge.base.internal.activator.KnowledgeBaseServiceBundleActivator\n\\end{verbatim}\n\\end{enumerate}\n\nThe Bundle Activator uses one of the following values to initialize the\n\\texttt{schemaVersion} field in the application's \\texttt{Release\\_}\ntable record:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Current \\texttt{buildNumber}: if there is an existing\n  \\texttt{Release\\_} table record for the previous plugin.\n\\item\n  \\texttt{0.0.1}: if there is no existing \\texttt{Release\\_} table\n  record.\n\\end{itemize}\n\nWonderful! You've set your service module's data upgrade process.\n\n\\section{Related Topics}\\label{related-topics-122}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-upgrade-process-for-your-app}{Creating\n  Upgrade Processes for Modules}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver}{Upgrading\n  Code to 7.0}\n\\end{itemize}\n\n\\chapter{Upgrading Data Schemas in\nDevelopment}\\label{upgrading-data-schemas-in-development}\n\nAs you develop modules, you might need to iterate through several\ndatabase schema changes. Before you release new module versions with\nyour finalized schema changes, you must create a formal\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-upgrade-process-for-your-app}{data\nupgrade process}. Until then, you can use the Build Auto Upgrade feature\nto test schema changes on the fly.\n\nFollow these steps to use the Build Auto Upgrade feature to test schema\nchanges in development:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a \\texttt{portal-ext.properties} file in your app server's\n  \\texttt{{[}Liferay\\_Home{]}/} folder if it doesn't already exist.\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes}{Enable\n  Developer Mode in your app server} by adding the following line to the\n  properties file:\n\n\\begin{verbatim}\ninclude-and-override=portal-developer.properties;\n\\end{verbatim}\n\n  The Build Auto Upgrade feature is a global property\n  \\texttt{schema.module.build.auto.upgrade} in the file\n  \\texttt{{[}Liferay\\_Home{]}/portal-developer.properties}, so enabling\n  Developer Mode automatically enables this property as well.\n\n  Alternatively, if you prefer not to enable all the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/portal-impl/src/portal-developer.properties}{other\n  properties included in Developer Mode}, you can just add the\n  \\texttt{schema.module.build.auto.upgrade} property to your\n  \\texttt{portal-ext.properties} file and set it to \\texttt{true}:\n\n\\begin{verbatim}\nschema.module.build.auto.upgrade = true;\n\\end{verbatim}\n\\end{enumerate}\n\nSetting the global property \\texttt{schema.module.build.auto.upgrade} to\n\\texttt{true} applies module schema changes for redeployed modules whose\nservice build numbers have incremented. The \\texttt{build.number}\nproperty in the module's \\texttt{service.properties} file indicates the\nservice build number. Build Auto Upgrade executes schema changes without\nmassaging existing data. It leaves data empty for created columns, drops\ndata from deleted and renamed columns, and orphans data from deleted and\nrenamed tables.\n\nAlthough Build Auto Upgrade updates databases quickly and automatically,\nit doesn't guarantee a proper data upgrade--you implement that via\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-upgrade-process-for-your-app}{data\nupgrade processes}. Build Auto Upgrade is for development purposes only.\n\n\\noindent\\hrulefill\n\n\\textbf{WARNING}: DO NOT USE the Build Auto Upgrade feature in\nproduction. Liferay DXP DOES NOT support Build Auto Upgrade in\nproduction. Build Auto Upgrade is for development purposes only.\nEnabling it in production can result in data loss and improper data\nupgrade. In production environments, leave the property\n\\texttt{schema.module.build.auto.upgrade} in\n\\texttt{portal-developer.properties} set to \\texttt{false}.\n\n\\noindent\\hrulefill\n\nBy default, \\texttt{schema.module.build.auto.upgrade} is set to\n\\texttt{false}. On any module's first deployment, the module's tables\nare generated regardless of the\n\\texttt{schema.module.build.auto.upgrade} value.\n\nThe table below summarizes Build Auto Upgrade's handling of schema\nchanges:\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5200}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.4800}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nSchema Change\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nResult\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nAdd column & Create a new empty column. \\\\\nRename column & Drop the existing column and delete all its data. Create\na new empty column. \\\\\nDelete column & Drop the existing column and delete all its data. \\\\\nCreate or rename a table in Liferay DXP's built-in data source. & Orphan\nthe existing table and all its data. Create the new table. \\\\\n\\end{longtable}\n\nGreat! Now you know how to use the Build Auto Upgrade developer feature.\n\n\\section{Related Topics}\\label{related-topics-123}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-upgrade-process-for-your-app}{Creating\nData Upgrade Process for Modules}\n\n\\chapter{Managing User-Associated Data Stored by Custom\nApplications}\\label{managing-user-associated-data-stored-by-custom-applications}\n\nAdministrators can\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-user-data}{delete or\nanonymize} User Associated Data (UAD) using management tools that aid\ncompliance efforts with the EU's General Data Protection Regulation\n(GDPR). Out of the box, the UAD management tool supports Liferay DXP\napplications (Blogs, Documents and Media, etc.), and you can also\nanonymize data stored by your custom applications.\n\nThis task is made easier for\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder applications}. At the core of the anonymization effort, you must\nidentify the model entity's fields to anonymize. With Service Builder,\nattach anonymization attributes to elements in the \\texttt{-service}\nmodule's \\texttt{service.xml} file. For the entire DTD for Service\nBuilder, see\n\\href{https://docs.liferay.com/portal/7.2-ga1/definitions/}{here}. These\ntwo are the most important attributes for the UAD framework:\n\n\\begin{itemize}\n\\item\n  The \\texttt{uad-anonymize-field-name=fieldName} attribute indicates a\n  field whose value is replaced by that of the anonymous user in the UAD\n  deletion process.\n\\item\n  The \\texttt{uad-nonanonymizable=true} attribute indicates data that\n  cannot be anonymized automatically and must be reviewed by an\n  administrator.\n\\end{itemize}\n\nOnce your application uses the UAD framework to manage User data, there\nare more features in 7.0 that make searching and deleting User\nAssociated Data even easier.\n\n\\chapter{Adding the UAD Framework to a Service Builder\nApplication}\\label{adding-the-uad-framework-to-a-service-builder-application}\n\nYou'll touch two modules of a Service Builder application to implement\nthe UAD features: the \\texttt{-service} module and a brand new module\nthat Service Builder generates for you, the \\texttt{-uad} module.\n\n\\section{Update the Service Module}\\label{update-the-service-module}\n\nBefore you specify your model entity's fields to manage with the UAD\nframework, make sure you have the right dependencies.\n\n\\section{Include Dependencies}\\label{include-dependencies}\n\nTo compile the code that Service Builder generates, you need\ndependencies on \\texttt{com.liferay.petra.String} and\n\\texttt{com.liferay.portal\\ kernel}. Make sure your service module's\n\\texttt{build.gradle} includes both:\n\n\\begin{verbatim}\ndependencies {\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"4.4.0\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\", version: \"3.0.0\"\n    ...\n}\n\\end{verbatim}\n\n\\section{Choose the Fields to\nAnonymize}\\label{choose-the-fields-to-anonymize}\n\nIn the \\texttt{service.xml} file, choose the fields to be handled by the\nframework by adding a\n\\texttt{uad-anonymize-field-name=\"{[}fieldName{]}\"} or\n\\texttt{uad-nonanonymizable=true}.\n\nFor example, to replace the \\texttt{userName} field of your custom\nentity with the \\texttt{fullName} of the anonymous user,\n\n\\begin{verbatim}\n<column name=\"userName\" type=\"String\" uad-anonymize-field-name=\"fullName\" />\n\\end{verbatim}\n\nThis declaration specifies that the \\texttt{content} field cannot be\nauto-anonymized by the framework but should be reviewed manually.\n\n\\begin{verbatim}\n<column name=\"content\" type=\"String\" uad-nonanonymizable=\"true\" />\n\\end{verbatim}\n\nRun Service Builder. A new module is generated alongside your other\nmodules, called \\texttt{my-app-uad}. It requires a little massaging.\n\n\\section{Update the UAD Module}\\label{update-the-uad-module}\n\nFirst, include your dependencies, and then provide your application's\nname to the user interface.\n\n\\section{Include Dependencies}\\label{include-dependencies-1}\n\nThe new module is generated without a build script, so you must provide\none. It should include dependencies on\n\\texttt{osgi.service.component.annotations},\n\\texttt{com.liferay.portal.kernel}, \\texttt{com.liferay.petra.string},\nthe \\texttt{com.liferay.user.associated.data.api}, and your own\napplication's \\texttt{-api} module:\n\n\\begin{verbatim}\ndependencies {\n  compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"4.4.0\"\n  compileOnly group: \"com.liferay\", name: \"com.liferay.user.associated.data.api\", version: \"4.1.1\"\n  compileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\", version: \"3.0.0\"\n  compileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\", version: \"1.3.0\"\n  compileOnly project(\":modules:custom:custom-api\")\n  }\n\\end{verbatim}\n\n\\section{Provide Your App's Name to the\nUI}\\label{provide-your-apps-name-to-the-ui}\n\nThe simplest way to provide your app's name to the anonymization UI is\nto include a language key in your \\texttt{Language.properties} file:\n\n\\begin{verbatim}\napplication.name.[Bundle-SymbolicName]=Custom App\n\\end{verbatim}\n\nThe bracketed text is the \\texttt{Bundle-SymbolicName} from your\n\\texttt{-uad} module's \\texttt{bnd.bnd} file.\n\nWhile this approach is recommended, it has one downside: multiple\nlanguage keys are used to label a single application. Liferay DXP\napplications use the \\texttt{com.liferay.lang.merger} plugin to avoid\nthis. Here's the relevant part of the Blogs application's\n\\texttt{blogs-uad/build.gradle}:\n\n\\begin{verbatim}\napply plugin: \"com.liferay.lang.merger\"\n\ndependencies {\n\n...\n\n}\n\nmergeLang {\n    setting(\"../blogs-web/src/main/resources/content\") {\n        transformKey \"javax.portlet.title.com_liferay_blogs_web_portlet_BlogsPortlet\", \"application.name.com.liferay.blogs.uad\"\n    }\n\n    sourceDirs = [\"../blogs-web/src/main/resources/content\"]\n}\n\\end{verbatim}\n\n\\chapter{Enhancing the Data Erasure\nUI}\\label{enhancing-the-data-erasure-ui}\n\nAs of 7.0, there are new features that enhance an administrator's\nexperience finding data in the Personal Data Erasure UI:\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Filtering}]\nUser content can now be viewed and acted upon based on whether it is\npart of the User's personal Site, some other Site, or the overall\ncompany.\n\\item[\\textbf{Search}]\nA search bar now allows for filtering content based on a search term.\n\\item[\\textbf{Hierarchy Display}]\nIf there are multiple types of content that are related in a hierarchy,\nyou can define that relationship using a\n\\texttt{UADHierarchyDeclaration}, and the user interface shows that\nhierarchy (e.g.~files and folders).\n\\end{description}\n\n\\section{Filtering and Searching in the Data Erasure\nUI}\\label{filtering-and-searching-in-the-data-erasure-ui}\n\nTo support filtering and searching in your custom entities, implement\nthree methods in the \\texttt{UADDisplay} class (found in your\n\\texttt{-uad} module):\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{isSiteScoped}\n\\item\n  \\texttt{search}\n\\item\n  \\texttt{searchCount}\n\\end{itemize}\n\nThe \\texttt{isSiteScoped} method returns a boolean, determining if the\nentity can be associated with a particular Site. This is used to\ndetermine which filter they are associated with (``instance'',\n``personal-site'', or ``regular-sites'').\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/uad-scope-filter.png}\n\\caption{Items in the Personal Data Erasure screen can be filtered by\nscope.}\n\\end{figure}\n\nThe \\texttt{search} method takes the following parameters:\n\n\\texttt{userId}: The \\texttt{userId} of the selected User.\n\n\\texttt{groupIds}: An array of \\texttt{groupId}s used to filter which\ndata is shown by which groups it is associated with. In the case that no\n\\texttt{groupId}s are given (it can be null), the search method should\nreturn data that is not scoped to any given group.\n\n\\texttt{keywords}: The contents of the search bar. The search method\nshould filter by whatever fields are relevant for the given entity.\n\n\\texttt{orderByField}: The name of the field used to sort the results.\nThis is one of the names returned by \\texttt{getSortingFieldNames}.\n\n\\texttt{orderByType}: Sort the results in ascending order or descending\norder (\\texttt{asc} or \\texttt{desc}), for pagination.\n\n\\texttt{start}: The starting index of the result set. For pagination.\n\n\\texttt{end}: The ending index of the result set. For pagination.\n\nThe \\texttt{searchCount} method takes the following parameters, which\nare treated identically to the ones in \\texttt{search}:\n\n\\begin{itemize}\n\\item\n  \\texttt{userId}\n\\item\n  \\texttt{groupIds}\n\\item\n  \\texttt{keywords}\n\\end{itemize}\n\nRead\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/filtering-and-searching-uad-marked-entities}{here}\nfor instructions on how to implement search and filtering for your\nentity.\n\n\\section{Hierarchy Display}\\label{hierarchy-display}\n\nHierarchical UAD display optionally shows entities with a natural\nparent-child relationship (for example, Document Library Folders and\nDocument Library File Entries). Viewing these entities in a hierarchy\nhelps administrators make sense of the data they're reviewing for\npossible erasure.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/uad-hierarchy.png}\n\\caption{Hierarchical representation of nested entities is useful for\nadministrators reviewing User data for possible deletion.}\n\\end{figure}\n\nTo implement a hierarchy display, you must do two things:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Implement a\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/user-associated-data/user-associated-data-api/src/main/java/com/liferay/user/associated/data/display/UADHierarchyDeclaration.java}{\\texttt{UADHierarchyDeclaration}}\n  class.\n\\item\n  Add a method to the \\texttt{*UADDisplay} class for each type involved\n  in the hierarchy.\n\\end{enumerate}\n\nOnce implemented, a hierarchy view is displayed for any of the types\nreturned in the \\texttt{UADHierarchyDeclaration}. For container\nentities, a count of all child entities is calculated and displayed\nusing the hierarchy-related methods in \\texttt{UADDisplay}.\n\n\\section{UAD Hierarchy Declaration}\\label{uad-hierarchy-declaration}\n\nThe \\texttt{UADHierarchyDeclaration} defines the types in the\nhierarchical relationship. There are two classifications for a type in a\nhierarchy: a \\emph{container}, and a \\emph{non-container}. These are\ndefined by \\texttt{getContainerUADDisplays} and\n\\texttt{getNoncontainerUADDisplays}.\n\nEach returns an array of one or more \\texttt{UADDisplay} classes.\nContainers can be parents or children in the hierarchy. An example is a\nfolder in a file system, which can contain both files and other folders.\nThe non-container entities can only be children in the hierarchy. An\nexample is a file in a file system.\n\nThe \\texttt{UADHierarchyDeclaration} provides some other methods for\ndisplay purposes.\n\n\\begin{itemize}\n\\item\n  A label for the hierarchy is provided through the\n  \\texttt{getEntitiesTypeLabel} method.\n\\item\n  If any additional information is to be displayed in the table for any\n  of the entity types in addition to the count, those column names\n  should be returned by \\texttt{getExtraColumnNames}. This is optional.\n\\end{itemize}\n\nSee\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-uad/src/main/java/com/liferay/document/library/uad/display/DLUADHierarchyDeclaration.java}{\\texttt{DLUADHierarchyDeclaration}}\nfor an example.\n\n\\section{Add methods to UADDisplay}\\label{add-methods-to-uaddisplay}\n\nEach type involved in the hierarchy should implement some additional\nmethods in \\texttt{UADDisplay}.\n\nThese methods must be implemented by containers and non-containers\nalike, in the \\texttt{*UADDisplay} class:\n\n\\texttt{getName} returns a display name for the given entity.\n\n\\texttt{getParentContainerClass} returns the class of the type that\ncontains this type. It can return itself (for example, a folder can\ncontain a folder).\n\n\\texttt{getParentContainerId} returns the primary key of the container\nthat contains the entity passed to this method.\n\n\\texttt{isUserOwned} returns whether or not the given entity is owned by\nthe user.\n\nAdditionally, implement \\texttt{getTopLevelContainer} in the\n\\texttt{*UADDisplay} class for all types classified as\n\\emph{containers}. It's used to derive the count of how many user-owned\nentities are contained inside a given container's tree. It answers the\nquestion ``which type \\texttt{T} ancestor of \\texttt{childObject} is an\nimmediate child of the container identified by\n\\texttt{parentContainerClass} and \\texttt{parentContainerId}?'' The\nmethod may return \\texttt{null} if \\texttt{childObject} is not a child\nof the parent container. This method is the most complicated to\nimplement and requires some consideration for each case. Refer to the\ntest case for examples of the requirements used for \\texttt{DLFolder}:\n\\href{https://github.com/liferay/liferay-portal/blob/c8f78609353d6a83a0b755b0bbf93764959821ee/modules/apps/document-library/document-library-uad-test/src/testIntegration/java/com/liferay/document/library/uad/display/test/DLFolderUADDisplayTest.java\\#L67}{DLFolderUADDisplayTest\\#testGetTopLevelContainer}\n\nSee the actual implementation for \\texttt{DLFolder} in\n\\href{https://github.com/liferay/liferay-portal/blob/c8f78609353d6a83a0b755b0bbf93764959821ee/modules/apps/document-library/document-library-uad/src/main/java/com/liferay/document/library/uad/display/DLFolderUADDisplay.java\\#L105}{DLFolderUADDisplay\\#getTopLevelContainer}.\n\nThe method returns either \\texttt{null} or the container object of type\nT that is the top level container of the \\texttt{childObject} (which\ncould be any type of object that is a part of the hierarchy). This\ncontainer does not necessarily have to be owned by the user, but is\nunderstood to contain data related to the user. This information is used\nto count how much user data is inside the container designated by\n\\texttt{parentContainerClass} and \\texttt{parentContainerId}.\n\n\\begin{verbatim}\n@Override\npublic DLFolder getTopLevelContainer(\n    Class<?> parentContainerClass, Serializable parentContainerId,\n    Object childObject) {\n\n    try {\n        DLFolder childFolder = null;\n\n        if (childObject instanceof DLFileEntry) {\n            DLFileEntry dlFileEntry = (DLFileEntry)childObject;\n\n            childFolder = dlFileEntry.getFolder();\n        }\n        else {\n            childFolder = (DLFolder)childObject;\n        }\n\n        long parentFolderId = (long)parentContainerId;\n\n        if ((childFolder.getFolderId() == parentFolderId) ||\n            ((parentFolderId !=\n                DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) &&\n             !StringUtil.contains(\n                 childFolder.getTreePath(), String.valueOf(parentFolderId),\n                 \"/\"))) {\n\n            return null;\n        }\n\n        if (childFolder.getParentFolderId() == parentFolderId) {\n            return childFolder;\n        }\n\n        List<Long> ancestorFolderIds = childFolder.getAncestorFolderIds();\n\n        if (parentFolderId == DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {\n            return get(ancestorFolderIds.get(ancestorFolderIds.size() - 1));\n        }\n\n        if (ancestorFolderIds.contains(parentFolderId)) {\n            return get(\n                ancestorFolderIds.get(\n                    ancestorFolderIds.indexOf(parentFolderId) - 1));\n        }\n    }\n    catch (PortalException pe) {\n        _log.error(pe, pe);\n    }\n\n    return null;\n}\n\\end{verbatim}\n\nThe exact implementation details vary for each entity type.\n\n\\chapter{Filtering and Searching UAD-Marked\nEntities}\\label{filtering-and-searching-uad-marked-entities}\n\nIn the data erasure UI, it's important that administrators can find what\nthey're looking for. The native Liferay DXP entities support filtering\nand search, and when you follow the steps here, your entities will, too.\n\nTo add filtering and searching for your custom entities, implement three\nmethods in the \\texttt{UADDisplay} class (in your application's\n\\texttt{-uad} module):\n\n\\section{Filtering}\\label{filtering}\n\nThe \\texttt{isSiteScoped} method returns a boolean denoting if the\nentities can be associated with a particular Site: \\texttt{false} if\nnot, and \\texttt{true} if the entities are scoped to a Site. This\ndetermines which filter they are associated with (``instance'',\n``personal-site'', or ``regular-sites'').\n\n\\begin{verbatim}\n@Override\npublic boolean isSiteScoped() {\n\n    return false;\n}\n\\end{verbatim}\n\n\\section{Search}\\label{search-1}\n\nImplement the \\texttt{search} and \\texttt{searchCount} methods to enable\nsearch in the UAD interface:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The \\texttt{search} method must return a \\texttt{List} of entities\n  associated with the \\texttt{userId}. For example, you could search the\n  database for records associated with the \\texttt{userId}:\n\n\\begin{verbatim}\n@Override\npublic List<T> search(\n    long userId, long[] groupIds, String keywords, String orderByField,\n    String orderByType, int start, int end) {\n\n    FooService<T> fooService = getFooService();\n\n    return dummyService.getEntities(userId);\n}\n\\end{verbatim}\n\n  But if you've gone through the trouble of indexing your model entity's\n  fields in a search engine, it's more likely you'll want to do the\n  initial search, querying for documents matching the \\texttt{userId},\n  at the search engine level. After the search, retrieve the matching\n  entities from the database.\n\n\\begin{verbatim}\n@Override\npublic List<T> search(\n    long userId, long[] groupIds, String keywords, String orderByField,\n    String orderByType, int start, int end) {\n\n    SearchContext searchContext = new SearchContext();\n\n    searchContext.setStart(start);\n    searchContext.setEnd(end);\n    searchContext.setGroupIds(groupIds);\n    searchContext.setKeywords(keywords);\n\n    BooleanQuery booleanQuery = BooleanQueryFactoryUtil.create(\n        searchContext);\n\n    booleanQuery.addExactTerm(\"userId\", userId);\n\n    BooleanClause booleanClause = BooleanClauseFactoryUtil.create(\n        booleanQuery, BooleanClauseOccur.MUST.getName());\n\n    searchContext.setBooleanClauses(new BooleanClause[] {booleanClause});\n\n    Indexer indexer = IndexerRegistryUtil.getIndexer(FooEntry.class);\n\n    Hits hits = indexer.search(searchContext);\n\n    List<FooEntry> fooEntries = new ArrayList<FooEntry>();\n\n    for (int i = 0; i < hits.getDocs().length; i++) {\n            Document doc = hits.doc(i);\n\n            long entryId = GetterUtil\n            .getLong(doc.get(Field.ENTRY_CLASS_PK));\n\n            Entry entry = null;\n\n            try {\n                    entry = _fooEntryLocalService.getFooEntry(fooEntryId);\n            } catch (PortalException pe) {\n                    _log.error(pe.getLocalizedMessage());\n            } catch (SystemException se) {\n                    _log.error(se.getLocalizedMessage());\n            }\n\n            fooEntries.add(fooEntry);\n    }\n\n    return fooEntries;\n}\n\\end{verbatim}\n\n  It largely boils down to instantiating and populating the search\n  context, which gets passed to the \\texttt{indexer.search} call to\n  retrieve the \\texttt{Hits}. Subsequently, populate the \\texttt{List}\n  by iterating through the \\texttt{Hits}, using each one's\n  \\texttt{ENTRY\\_CLASS\\_PK} field as the primary key of the entity in\n  the call to the entity's getter. The \\texttt{BooleanClause}\n  construction and inclusion in the search context ensures that all the\n  results returned correspond to the \\texttt{userId} that's passed to\n  this method.\n\\item\n  The \\texttt{searchCount} method returns a long of the result\n  \\texttt{List}'s \\texttt{size} method. You could just invoke the\n  class's \\texttt{search} method, then call the \\texttt{List} object's\n  \\texttt{size} method.\n\n\\begin{verbatim}\n@Override\npublic long searchCount(long userId, long[] groupIds, String keywords) {\n    List<T> results = search(\n        userId, groupIds, keywords, null, null, QueryUtil.ALL_POS,\n        QueryUtil.ALL_POS);\n\n    return results.size();\n}\n\\end{verbatim}\n\n  But, again, if the model entity is being indexed in a search engine,\n  you can use it to get a count without ever hitting the database. Using\n  the \\texttt{Hits} object returned from a search (see the code from\n  step 1, but don't include \\texttt{start} and \\texttt{end} parameters\n  in the \\texttt{SearchContext}), call \\texttt{hits.getLegnth()} and you\n  get the count, as an \\texttt{int}.\n\\end{enumerate}\n\nNow administrators responsible for complying with GDPR or other data\nerasure concerns can search and filter your entity from the Liferay DXP\nUAD interface.\n\n\\chapter{Web Experience Management}\\label{web-experience-management}\n\nWeb Experience Management encompasses Liferay's features and tools for\nbuilding Sites and creating content. Many of these, like Web Content\nManagement and Page Templates, are graphical tools used by\nadministrators and marketers. Others, like Page Fragments, let web\ndevelopers flex their muscles in content creation. These articles cover\nwhere web development intersects with user experience and how to use\nLiferay's Web Experience frameworks to integrate custom applications\ninto Liferay DXP.\n\nSpecifically, you'll learn about\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/page-fragments}{Developing\n  Fragments}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/supporting-custom-content-types-in-content-and-display-pages}{Supporting\n  Custom Content Types}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/screen-navigation-framework}{Screen\n  Navigation}\n\\end{itemize}\n\nFor more information on applying these frameworks for users, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/web-experience-management}{Web\nExperience Management} user articles.\n\n\\chapter{Page Fragments}\\label{page-fragments}\n\nYou can use Page Fragments to take your design vision and accurately\nrealize it on a web page. You start with a ``blank slate.'' You then\nhave three tools at your disposal to accomplish your vision:\n\n\\textbf{HTML}: The markup of the fragment. Fragments use standard HTML\nwith special tags to add dynamic behavior.\n\n\\textbf{CSS}: Styles and positions the fragment's markup.\n\n\\textbf{JavaScript}: Provides dynamic behavior to the fragment.\n\nThe HTML, CSS, and JavaScript are all completely standard, but can be\nenhanced with Liferay-specific features. You can specify text, images,\nand links as editable and provide for ``rich'' text with formatting.\n\nYou can also access the FreeMarker templates engine from your HTML using\nthe\n\\href{https://freemarker.apache.org/docs/dgui_misc_alternativesyntax.html}{alternative\n(square bracket) syntax}. Learn more about available FreeMarker objects\nin\n\\href{/docs/7-2/reference/-/knowledge_base/r/front-end-reference}{Front-end\nReference}.\n\nLiferay portlets can also be embedded in Fragments as widgets, making\npages with Fragments more dynamic than regular web content.\n\nNow you'll step through some Page Fragment basics.\n\n\\section{Developing Page Fragments}\\label{developing-page-fragments}\n\nThere are two types of Page Fragments: \\emph{Sections} and\n\\emph{Components}. A Section defines columns, padding, and spacing on\nthe page. A Component contains content that is added to a Section.\n\nFragments are created inside of Collections. Collections provide an easy\nway to manage and share groups of related Fragments. Users navigate\nCollections when selecting Fragments to add to a page. To see examples,\nthe admin page shows all the out-of-the-box Fragments (and their code).\n\nYou can create and manage Fragments and Collections without using any\nexternal tools, but you can also use your preferred web development\ntools. For an explanation of Fragment creation using Liferay's built in\ntools, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-fragments}{Creating\na Fragment}.\n\n\\section{Making a Fragment\nConfigurable}\\label{making-a-fragment-configurable}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Defining configurations for Page Fragments is available\nin Liferay DXP 7.2 SP1+ and Liferay Portal GA2+.\n\n\\noindent\\hrulefill\n\nPage Fragments are also configurable: defining configuration options for\nyour fragment eliminates having to maintain multiple other fragments\nsimilar in style. For example, if you want a dark background banner and\na light background banner, you can create one banner with a\nconfiguration option for background type.\n\nThe following field types are supported for Fragment configurations:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{checkbox}\n\\item\n  \\texttt{colorPalette}\n\\item\n  \\texttt{itemSelector}\n\\item\n  \\texttt{select}\n\\item\n  \\texttt{text}\n\\end{itemize}\n\nThis is available for all Fragment types (e.g., Fragment Renderer,\netc.).\n\nFor more information, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/making-a-fragment-configurable}{Making\na Fragment Configurable}.\n\n\\section{Fragments CLI}\\label{fragments-cli}\n\nTo streamline fragment development, 7.0 provides command line tools for\ngenerating, importing, and exporting fragments and fragment collections.\nFor more information about the CLI, see the\n\\href{https://github.com/liferay/generator-liferay-fragments/blob/master/README.md}{official\nLiferay Fragments CLI project} reference. Using this CLI is also covered\nin\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/page-fragments-desktop-tools}{Developing\na Fragment using Desktop Tools}.\n\n\\section{Contributed Collections}\\label{contributed-collections}\n\nMost of the time, Page Fragments are created and imported through the\nPage Fragments interface or created directly using the built-in tools.\nAny user with the right permissions can update or edit Page Fragments\ncreated like this. You may have certain situations, however, where you\nwant 100\\% static fragments that cannot be modified. In this case you\ncan create a Contributed Fragment Collection.\n\nContributed Fragment Collections are deployable modules containing Page\nFragments. Those fragments can be used just like regular fragments, but\nare not contained in the database, and cannot be modified except by\nupdating the module they came from. Use the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-contributed-fragment-collection}{Creating\nContributed Collections guide} to learn to create your own Contributed\nCollections.\n\n\\section{Fragment Specific Tags}\\label{fragment-specific-tags}\n\nIn addition to standard HTML, CSS, and JavaScript you can use\nLiferay-specific tags to make editable sections or embed widgets in your\nFragment.\n\nEditable elements can be modified before publication. This means that\nweb developers can create simple, reusable fragments that have identical\nformatting, but contain elements that are adaptable to the specific\ncontext.\n\nYou can make text, images, and links in a fragment editable by using an\n\\texttt{\\textless{}lfr-editable\\textgreater{}} tag. The\n\\texttt{\\textless{}lfr-editable\\textgreater{}} tag requires a unique\n\\texttt{id}, a type, and some content of the specified type inside.\n\nThe following four \\texttt{type} options are available in an\n\\texttt{lfr-editable} tag:\n\n\\texttt{text}: Creates a space for editable plain text.\n\n\\texttt{image}: Must contain a valid\n\\texttt{\\textless{}img\\textgreater{}} tag which can then be replaced\nwith an image before publishing---including those from Documents and\nMedia.\n\n\\texttt{rich-text}: Provides rich text formatting, such as bold,\nitalics, underline, links, and predefined styles.\n\n\\texttt{link}: Must contain a valid anchor tag for which the style,\ntarget URL, and link text can be edited before publishing.\n\nThe text or images you provide here are the default values for the\nfields. You may want to display them in the final version of the page,\nor you may want filler text that should be replaced before the page is\npublished.\n\nAll of these work together to help you create dynamic, reusable elements\nfor building a site. For example, if you need a small text box with an\nimage and link to provide a product description, you can create a\nfragment containing editable filler text, space for an editable image,\nthe appropriate formatting, and an editable link. That fragment can be\nadded to multiple pages, and marketers can define the image, text, and\nlink for each product they need to describe.\n\nYou can make a Fragment even more dynamic by including a widget.\nCurrently, portlets are the only embeddable types of widgets, but other\noptions are planned.\n\nYou can find a complete list and usage examples of these in the\n\\href{/docs/7-2/reference/-/knowledge_base/r/fragment-specific-tags}{Page\nFragments Reference}.\n\n\\section{Recommendations and Best\nPractices}\\label{recommendations-and-best-practices}\n\nIn general all your code should be semantic and highly reusable. A main\nconcern is making sure that everything is namespaced properly so it\nwon't interfere with other elements on the page outside of the Fragment.\n\n\\section{CSS}\\label{css}\n\nWhile you can write any CSS in a fragment, it's recommended to prefix it\nwith a class specific to the fragment to avoid impacting other\nfragments. To facilitate this, when creating a new fragment, the HTML\nincludes a \\texttt{div} with an automatically generated class name and\nthe CSS shows a sample selector using that class. Use it as the basis\nfor all selectors you add.\n\n\\section{JavaScript}\\label{javascript}\n\nAvoid adding a lot of JavaScript code, since it isn't easily reusable.\nInstead, reference external JS libraries.\n\n\\chapter{Developing Fragments}\\label{developing-fragments}\n\nThis tutorial assumes you understand Fragments and Collections. If you\ndon't, read the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-page-fragments}{Creating\nPage Fragments} article first. Once, you're ready, start by creating a\nCollection:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the menu for your selected site, click \\emph{Site Builder} →\n  \\emph{Page Fragments}.\n\\item\n  Create a new Collection named \\emph{Developing Fragments}.\n\\end{enumerate}\n\nFirst, you'll create a \\emph{Section}.\n\n\\section{Creating a Section}\\label{creating-a-section}\n\nThe list of Collections appears on the left in the Page Fragments page.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Ensure that you are in the \\emph{Developing Fragments} collection.\n\\item\n  Click the \\emph{New} button \\includegraphics{./images/icon-add.png}\n  and select \\emph{Section}.\n\\item\n  Name your Section \\emph{Basic Section}\n\\end{enumerate}\n\nYou're now on the Fragment editing page. There are four panes on this\nscreen. You enter HTML in the top left pane, CSS in the top right,\nJavaScript in the bottom left, and preview the results in the bottom\nright. The Fragment Editor even comes with autocomplete functionality!\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-editor-autocomplete.png}\n\\caption{The Fragment editor provides autocomplete for Liferay Fragment\nspecific tags.}\n\\end{figure}\n\nYou can look at the three editing panes as if each were writing to a\nseparate file. Everything in the HTML pane goes to \\texttt{index.html},\nthe CSS pane goes to \\texttt{index.css}, and the JavaScript pane goes to\n\\texttt{index.js}. The preview pane renders everything as it looks on\nthe page.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Including images inline in base64 in your Page\nFragments can increase publishing, import, and export times for pages\nusing those Fragments. Use\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/including-default-resources-in-fragments}{references\nto resources} in your Page Fragments instead.\n\n\\noindent\\hrulefill\n\nA Section defines a work space. Now create a section with an editable\nrich text area where content can be entered:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the following code inside the HTML pane:\n\n\\begin{verbatim}\n<div class=\"banner py-6 py-md-8 text-white\" data-lfr-background-image-id=\"banner\">\n    <div class=\"container my-lg-6\">\n        <div class=\"row\">\n            <div class=\"col-12 col-md-8 col-xl-6\">\n                <h1>\n                    <lfr-editable id=\"01-title\" type=\"rich-text\">\n                        Banner Title Example\n                    </lfr-editable>\n                </h1>\n\n                <div class=\"mb-4 lead\">\n                    <p>\n                        <lfr-editable id=\"02-subtitle\" type=\"rich-text\">\n                            This is a simple banner component that you can use must provide extra information.\n                        </lfr-editable>\n                    </p>\n                </div>\n\n                <lfr-editable id=\"03-link\" type=\"link\">\n                    <a href=\"#\" class=\"btn btn-primary\">Go Somewhere</a>\n                </lfr-editable>\n            </div>\n        </div>\n    </div>\n</div>\n\\end{verbatim}\n\\item\n  Replace the code in the CSS pane with the following:\n\n\\begin{verbatim}\n.banner {\n    background-color:#415fa9;\n    background-position: center;\n    background-size: cover;\n}\n\\end{verbatim}\n\\item\n  Click \\emph{Publish} to save your work and make it available to add to\n  a content page.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When you start typing the name of a tag, the HTML editor\nprovides auto-completion for \\texttt{lfr} tags like editable elements\nand embeddable widgets.\n\n\\noindent\\hrulefill\n\nAs you work, you can observe your changes in the preview pane.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-editor-basic.png}\n\\caption{The Fragment editor with HTML and CSS code and a live preview.}\n\\end{figure}\n\n\\section{Creating a Component}\\label{creating-a-component}\n\nComponents are simple, reusable elements for building parts of a page.\nNext create a button with a link as a Component:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go back to the \\emph{Site Builder} → \\emph{Page Fragments} page and\n  select the \\emph{Developing Fragments} Collection.\n\\item\n  Click the \\emph{New} button \\includegraphics{./images/icon-add.png}\n  and select \\emph{Component}.\n\\item\n  Name it \\emph{Basic Component}.\n\\item\n  Back in the editor, add the following code inside the HTML pane:\n\n\\begin{verbatim}\n<div class=\"basic-link-button\">\n    <lfr-editable id=\"btn00\" type=\"link\">\n        <a href=\"#\" class=\"btn btn-primary\">Read More</a>\n    </lfr-editable>\n</div>\n\\end{verbatim}\n\\item\n  Click \\emph{Publish} to save your work and make it available to add to\n  a content page.\n\\end{enumerate}\n\nThis fragment did not require any CSS. For the button link, no target is\nprovided by default, so the link must be configured when it is added to\nthe page.\n\nFrom here, the Fragment can be added to a Page. To see this process in\naction, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/building-content-pages}{Building\nContent Pages} article.\n\n\\chapter{Making a Fragment\nConfigurable}\\label{making-a-fragment-configurable-1}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Defining configurations for Page Fragments is available\nin Liferay DXP 7.2 SP1+ and Liferay Portal GA2+.\n\n\\noindent\\hrulefill\n\nDefining configuration options for a Fragment gives it more flexibility,\nreducing the number of Fragments you must maintain. To make a Fragment\nconfigurable,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the \\emph{Site Builder} → \\emph{Page Fragments} page.\n\\item\n  Click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) → \\emph{Edit} for the\n  Fragment (Section or Component) you want to make configurable.\n\\item\n  Select the \\emph{Configuration} tab at the top of the page.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/fragment-config-tab.png}\n  \\caption{Switch from the Code tab to the Configuration tab to create\n  your configuration logic.}\n  \\end{figure}\n\\item\n  In the editor, add your JSON code. This code is added to your\n  fragment's \\texttt{index.json} file. For example, the code below\n  provides the \\texttt{select} option to choose \\emph{dark} or\n  \\emph{light} for a Fragment's heading:\n\n\\begin{verbatim}\n{\n    \"fieldSets\": [\n        {\n            \"label\": \"heading\",\n            \"fields\": [\n                {\n                    \"name\": \"headingAppliedStyle\",\n                    \"label\": \"applied-style\",\n                    \"description\": \"this-is-the-style-that-will-be-applied\",\n                    \"type\": \"select\",\n                    \"dataType\": \"string\",\n                    \"typeOptions\": {\n                        \"validValues\": [\n                            {\n                                \"value\": \"dark\"\n                            },\n                            {\n                                \"value\": \"light\"\n                            }\n                        ]\n                    },\n                    \"defaultValue\": \"light\"\n                }\n            ]\n        }\n    ]\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The `label` property is optional. If it's left out, your\n configuration option has no title.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** If your configuration is invalid, you can't save the\n code. Be sure to always have a valid JSON configuration before previewing\n or saving it.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nThe configuration values selected by the user are made available to the\nFragment developer through the FreeMarker context. A configuration value can\nbe referenced using the notation `${configuration.[fieldName]}`. For the\nexample snippet above, `${configuration.headingAppliedStyle}` returns\n`dark` or `light` depending on the configuration value selected by the user.\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{4}\n\\item\n  You can refer to your Fragment's configuration values in its HTML file\n  (e.g., \\texttt{index.html}). Navigate back to the \\emph{Code} tab at\n  the top of the page and add your HTML. For example,\n\n\\begin{verbatim}\n[#if configuration.headingAppliedStyle == 'dark']\n...\n[#else]\n...\n[/#if]\n\\end{verbatim}\n\n  Configuration values inserted into the FreeMarker context honor the\n  defined \\texttt{datatype} value specified in the JSON file. Therefore,\n  for this example,\n  \\texttt{configuration.headingAppliedStyle?is\\_string} is\n  \\texttt{true}.\n\\item\n  Click \\emph{Publish} to save your work and make it available to add to\n  a Content Page.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/fragment-lang-keys.png}\n  \\caption{You can click your Fragment to view its configuration\n  options.}\n  \\end{figure}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can also make a Fragment configurable by leveraging\nthe Fragments Toolkit. You can create/modify the Fragment's\nconfiguration JSON file and then reimport the Fragment to your Liferay\ninstance. For more information, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/page-fragments-desktop-tools}{Page\nFragment Desktop Tools}.\n\n\\noindent\\hrulefill\n\nAlthough this example highlights accessing configuration values in HTML\nvia the FreeMarker context, you can also access these values via\nJavaScript. JavaScript configuration objects are named the same as their\nFreeMarker counterparts.\n\nFor example, a configuration object could be built like this:\n\n\\begin{verbatim}\nvar configuration = {\n    field1: value1,\n    field2: value2\n}\n\\end{verbatim}\n\nAnother example of setting the configuration object and using it is\nshown below:\n\n\\begin{verbatim}\nconst configurationValue = configuration.field1\n\nconsole.log(configurationValue);\n\\end{verbatim}\n\nFor more examples of Fragment configuration, see\n\\href{/docs/7-2/reference/-/knowledge_base/r/fragment-configuration-types}{Fragment\nConfiguration Types}.\n\nAwesome! You now have a configurable Fragment!\n\n\\chapter{Managing Fragments and\nCollections}\\label{managing-fragments-and-collections}\n\nAfter you create Collections and Fragments, you have a handful of\noptions for managing them. You'll explore several management options\nnext.\n\n\\section{Collections Management Menu}\\label{collections-management-menu}\n\nTo access the collections management menu,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select the Collection you want to manage from the \\emph{Collections}\n  list.\n\\item\n  Click on the \\includegraphics{./images/icon-actions.png} menu next to\n  the collection name.\n\\item\n  Select whether you want to \\emph{Edit}, \\emph{Export}, \\emph{Import},\n  or \\emph{Delete} the collection.\n\n  \\textbf{Edit}: change the name or description for the collection.\n\n  \\textbf{Export}: download a \\texttt{.zip} file containing the full\n  collection.\n\n  \\textbf{Import}: select a \\texttt{.zip} file to upload with additional\n  Fragments.\n\n  \\textbf{Delete}: remove the current collection and all of its\n  contents.\n\\end{enumerate}\n\nNext, you'll learn about the Fragment Management Menu.\n\n\\section{Fragment Management Menu}\\label{fragment-management-menu}\n\nTo access the fragment management menu,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select the Collection containing the Fragment you want to manage from\n  the \\emph{Collections} list.\n\\item\n  Click on the \\includegraphics{./images/icon-actions.png} menu next to\n  the Fragment name.\n\\item\n  Select whether you want to \\emph{Edit}, \\emph{Rename}, \\emph{Move},\n  \\emph{Make a Copy}, \\emph{Change Thumbnail} \\emph{Export}, or\n  \\emph{Delete}.\n\\end{enumerate}\n\nDid you know you can enable automatic propagation for Fragments? You'll\ndo this next.\n\n\\section{Propagating Fragment Changes\nAutomatically}\\label{propagating-fragment-changes-automatically}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Propagating Fragment changes is available in Liferay DXP\n7.2 SP1+ and Liferay Portal GA2+.\n\n\\noindent\\hrulefill\n\nBy default, when a Fragment developer makes a change to an existing\nfragment, the change is not automatically propagated to the pages that\nwere using it. This gives marketers and page authors more control over\nthe pages they own, avoiding unexpected changes. For example, if three\npages were using the same Fragment, an update to the Fragment could\nintroduce unintended changes to some of the pages using it. While this\nis a safeguard for the production environment, developers must\n\\href{/docs/7-2/user/-/knowledge_base/u/propagation-of-changes}{manually\npropagate Fragment changes} during testing, which can be tedious. To\ngive developers more freedom, you can enable automatic propagation for\nFragment changes:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Control Panel → \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Page Fragments}.\n\\item\n  Enable the checkbox \\emph{Propagate Fragment Changes Automatically}.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-propagation.png}\n\\caption{Once Fragment propagation is enabled, developers can\nautomatically propagate Fragment changes to all pages using them.}\n\\end{figure}\n\nGreat! You've enabled Fragment propagation system wide! Now when a\ndeveloper publishes a Fragment, the changes apply immediately to all\nContent Pages, Content Page Templates, and Display Page Templates using\nit, overwriting existing Fragment code. Automatic propagation works only\nfor HTML, CSS, and JS Fragment code, not the editable values.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} It's recommended to only leverage this functionality\nduring testing, as automatic propagation on the production environment\ncan cause unintended consequences.\n\n\\noindent\\hrulefill\n\nWhen using the Fragment Editor, you're now notified that automatic\nFragment propagation is enabled.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-propagation-info.png}\n\\caption{You're notified when automatic propagation is enabled.}\n\\end{figure}\n\nNow that you've seen how to use Liferay's built-in tools to manage\nFragments, you can see how to do it using your own tools of choice and\nthe Fragments Toolkit.\n\n\\chapter{Developing A Fragment Using Desktop\nTools}\\label{developing-a-fragment-using-desktop-tools}\n\nYou can develop a fragment using any preferred desktop tools. Since the\nFragment is HTML, CSS, and JavaScript, you could use a text editor or a\nspecialized tool with its own built in previews.\n\n\\section{Collection Format}\\label{collection-format}\n\nTo import a Collection into Liferay DXP, it must be archived in a\n\\texttt{.zip} with the contents in the following format:\n\n\\begin{itemize}\n\\item\n  \\texttt{collection.json}: a text file which describes your collection\n  with the format\n  \\texttt{\\{\"name\":\"\\textless{}collection-name\\textgreater{}\",\"description\":\"\\textless{}collection-description\\textgreater{}\"\\}}.\n\\item\n  \\texttt{language.properties}: the language keys defined for the\n  collection.\n\n  \\begin{itemize}\n  \\item\n    \\texttt{{[}fragment-name{]}/}: a folder containing all of the files\n    for a single Page Fragment.\n\n    \\begin{itemize}\n    \\item\n      \\texttt{fragment.json}: a text file that describes a Page Fragment\n      with the format\n\n\\begin{verbatim}\n{\n    \"cssPath\": \"index.css\",\n    \"configurationPath\": \"index.json\",\n    \"htmlPath\": \"index.html\",\n    \"jsPath\": \"index.js\",\n    \"name\": \"<fragment-name>\",\n    \"type\": \"<fragment-type>\"\n }\n\\end{verbatim}\n\n      Update the \\texttt{*Path} properties in your\n      \\texttt{fragment.json} file if you change the names of your\n      \\texttt{index.*} files.\n    \\item\n      \\texttt{index.css}: the CSS source for the fragment.\n    \\item\n      \\texttt{index.html}: the HTML source for the fragment.\n    \\item\n      \\texttt{index.json}: a JSON file that defines the fragment's\n      configuration.\n    \\item\n      \\texttt{index.js}: the JavaScript source for the fragment.\n    \\item\n      \\texttt{thumbnail.png}: the thumbnail that will display when the\n      Fragment is displayed in a list.\n    \\end{itemize}\n  \\item\n    \\texttt{{[}resources{]}/}: a folder containing any additional images\n    or other external files needed for the fragment.\n  \\end{itemize}\n\\end{itemize}\n\nA collection can contain any number of fragments, so you can have\nmultiple subfolders in the collection. This format is the same as what's\nexported from within Liferay. If you import a \\texttt{.zip} file that is\nnot organized like this, any fragments that are found will be imported\ninto special collection called \\emph{Imported} which is created for\norphaned fragments.\n\n\\section{Fragment CLI}\\label{fragment-cli}\n\nYou can manage Fragment creation and deployment manually, or you can use\nLiferay's Fragment CLI:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Follow the project instructions to\n  \\href{https://github.com/liferay/generator-liferay-fragments/blob/master/README.md}{set\n  up the Fragments Toolkit}.\n\\item\n  Run \\texttt{yo\\ liferay-fragments}.\n\\item\n  Follow the prompts to create a fragment.\n\\end{enumerate}\n\nNow you will have the basic structure created, but there's still more\nthat the Fragments Toolkit can help you with.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can see all of the available tasks inside the\n\\texttt{scripts} section in the Fragment CLI \\texttt{package.json}.\n\n\\noindent\\hrulefill\n\n\\section{Creating Collections}\\label{creating-collections}\n\nBefore you can create any Page Fragments, you must create a Collection.\nYou can learn more about Collections in the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-pages\\#creating-page-fragments}{Creating\nPage Fragments} section. Creating a Collection will create the base\nfolder structure and some information about your Collection. To do this,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From inside of your project, run \\texttt{npm\\ run\\ add-collection}.\n\\item\n  Follow the prompts to name your Collection.\n\\end{enumerate}\n\nYou can now create Page Fragments inside of this Collection.\n\n\\section{Creating Fragments}\\label{creating-fragments}\n\nA Page Fragment is made up of three primary files, \\texttt{index.html},\n\\texttt{index.css}, and \\texttt{index.js}. However, the files must be\nproperly arranged in the folder structure and have the appropriate\nmetadata to be imported onto your server. The Fragments Toolkit will\ncreate the files in the correct hierarchy with all of the necessary\ninformation.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From inside of the Collection you created, run\n  \\texttt{npm\\ run\\ add-fragment}.\n\\item\n  Follow the prompts to add the necessary information about your Page\n  Fragment.\n\\end{enumerate}\n\nNow the files are all created and you can edit them using your editor of\nchoice.\n\n\\section{Importing and Exporting\nFragments}\\label{importing-and-exporting-fragments}\n\nThe Fragments Toolkit can connect to your currently running Liferay DXP\nto import and export fragments. You can even have fragments that you\ncreate with the toolkit imported into Liferay DXP automatically.\n\n\\begin{itemize}\n\\item\n  To get collections and fragments from a running server, run\n  \\texttt{npm\\ run\\ export}.\n\\item\n  To send the collections and fragments from your current project to a\n  running server, run \\texttt{npm\\ run\\ import}. If your Fragment's\n  configuration JSON (if available) is invalid, the import fails and\n  provides an error message.\n\\item\n  To have collections and fragments automatically imported into Liferay\n  DXP as they are created or modified, run\n  \\texttt{npm\\ run\\ import:watch}.\n\\item\n  To preview how a fragment will look when it's imported, run\n  \\texttt{npm\\ run\\ preview}. This renders a fragment on a specified\n  Liferay server without importing it. When changes are made to the\n  fragment while it's previewed, changes are auto reloaded to rapidly\n  display updates. Note, this is available for Liferay DXP 7.2 SP1+ and\n  Liferay Portal 7.2 GA2+. You must install the\n  \\href{https://web.liferay.com/marketplace/-/mp/application/109571986}{OAuth\n  2} plugin in your portal instance for this command to work properly.\n\\item\n  To create a \\texttt{.zip} file that can be manually imported into\n  Liferay DXP, run \\texttt{npm\\ run\\ compress}.\n\\end{itemize}\n\nWith these tools at your disposal, you can more efficiently manage\ncreating and editing Page Fragments with whatever tools and environments\nwork best for you.\n\n\\chapter{Creating a Contributed Fragment\nCollection}\\label{creating-a-contributed-fragment-collection}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nTo create a Contributed Fragment Collection, a developer must,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a module which will contain the necessary logic and the\n  fragments.\n\\item\n  Extend the class \\texttt{BaseFragmentCollectionContributor} with all\n  the logic for reading the contributed fragments.\n\\item\n  Add fragments as resources in the module.\n\\end{enumerate}\n\nOnce you deploy the module, any fragments contained in it will be\navailable for use.\n\nTo better understand Contributed Fragment Collections, create one called\n\\texttt{DemoFragmentCollectionContributor}.\n\n\\section{Create a Module}\\label{create-a-module}\n\nFirst you must create the module in your development environment. Follow\nthe instructions in\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Creating\na Project}.\n\n\\section{Create the Java Class}\\label{create-the-java-class}\n\nNext, you must create the Java package and class to handle the logic for\nthe contributed collection:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a package in your module named\n  \\texttt{com.liferay.fragment.collection.contributor.demo}\n\\item\n  Inside of that package, create a Java class named\n  \\texttt{DemoFragmentCollectionContributor} that extends\n  \\texttt{BaseFragmentCollectionContributor}.\n\\item\n  Above the class declaration, add the \\texttt{@Component} annotation to\n  set the service class:\n\n\\begin{verbatim}\n@Component(service = FragmentCollectionContributor.class)\n\\end{verbatim}\n\\item\n  Create the variable for the servlet context:\n\n\\begin{verbatim}\nprivate ServletContext _servletContext;\n\\end{verbatim}\n\\item\n  Define the \\texttt{getFragmentCollectionKey()} and\n  \\texttt{getServletContext()} methods:\n\n\\begin{verbatim}\n@Override\npublic String getFragmentCollectionKey() {\n    return \"DEMO\";\n}\n\n@Override\npublic ServletContext getServletContext() {\n    return _servletContext;\n}\n\\end{verbatim}\n\\item\n  Below that use the \\texttt{@Reference} annotation to define your\n  module's symbolic name:\n\n\\begin{verbatim}\n@Reference(\n    target = \"(osgi.web.symbolicname=com.liferay.fragment.collection.contributor.demo)\"\n)\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** `osgi.web.symbolicname` must match `Bundle-SymbolicName` from `bnd.bnd`\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{6}\n\\tightlist\n\\item\n  Organize your imports and save.\n\\end{enumerate}\n\n\\section{Create the Resources}\\label{create-the-resources}\n\nNext you need to include the fragments that you want to contribute in\nyour module:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  In your module's \\texttt{resources/} folder, create the folder\n  structure\n  \\texttt{/com/liferay/fragment/collection/contributor/demo/dependencies}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The class package name and resources package name must match \n (e.g. `[my.class.package.structure].dependencies`).\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Copy the Fragments you want to distribute into the folder. You can\n  learn how to create a Fragment in the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-fragments}{Creating\n  Fragments section}.\n\\item\n  Create a file named \\texttt{collection.json} in the same folder with\n  this format:\n\n\\begin{verbatim}\n{\n    \"fragments\": [\n        \"[fragment-1]\",\n        \"[fragment-2]\",\n        \"[fragment-3]\",\n            ...\n    ],\n    \"name\": \"[collection-name]\"\n}\n\\end{verbatim}\n\n  If a fragment is not listed in \\texttt{collection.json}, it will not\n  be available in the Contributed Collection, even if the files are\n  included in the module.\n\\end{enumerate}\n\nNext, you'll configure the module's metadata so the fragments are\nimported.\n\n\\section{Configuring the Metadata}\\label{configuring-the-metadata}\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your bundle's \\texttt{bnd.bnd} file and add the\n  \\texttt{Web-ContextPath} header to point to your bundle's folder so\n  the fragment resources are loaded properly:\n\n\\begin{verbatim}\nWeb-ContextPath: /my-fragment-collection-contributor\n\\end{verbatim}\n\\item\n  Add the \\texttt{-dsannotations-options} instruction and set it to use\n  the \\texttt{inherit} option. This specifies to use DS annotations\n  found in the class hierarchy of the component class:\n\n\\begin{verbatim}\n-dsannotations-options: inherit\n\\end{verbatim}\n\\end{enumerate}\n\nNext, you'll dive into providing thumbnail images and language\nkeys/translations.\n\n\\section{Providing Thumbnail Images}\\label{providing-thumbnail-images}\n\nYou can also provide thumbnail images for reference for your fragments:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Under \\texttt{resources/META-INF/resources} create a folder named\n  \\texttt{thumbnails}.\n\\item\n  Copy thumbnail images into the folder with the format\n  \\texttt{{[}fragment-name{]}.png} for each fragment.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} All fragments added through a Contributed Fragment\nCollection will be available globally to all Sites.\n\n\\noindent\\hrulefill\n\n\\section{Providing Language Keys}\\label{providing-language-keys}\n\nProviding language keys in your Fragment gives you the option for\ntranslating the text you display. Here's how to do it:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  You must define your language keys in the Fragment's collection\n  folder. Create the\n  \\texttt{{[}COLLECTION{]}/src/main/resources/content/Language.properties}\n  file.\n\\item\n  Add your language keys. For example,\n\n\\begin{verbatim}\napplied-style=Applied Style\nthis-is-the-style-that-will-be-applied=This is the style that will be applied.\ndark=Dark\nlight=Light\n\\end{verbatim}\n\\end{enumerate}\n\nYou can learn more about providing translations in the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application}{Localizing\nYour Application} article.\n\n\\section{Deploy the Contributed Fragment\nCollection}\\label{deploy-the-contributed-fragment-collection}\n\nNow that you have created the necessary pieces of the module, you can\nbuild it and\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{deploy\nit} to Liferay DXP. After it's deployed, the Fragments will be available\nfor use. This can also be done by using the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/page-fragments-desktop-tools\\#importing-and-exporting-fragments}{Fragments\nToolkit}. Contributed Fragments cannot be edited with Liferay, and can\nonly be updated by editing the fragments in your module and the building\nand redeploying them.\n\n\\chapter{Including Default Resources in\nFragments}\\label{including-default-resources-in-fragments}\n\nWhen creating Page Fragments, you can upload resources (e.g., images,\ndocuments, etc.) to your Fragment Collection to make them always\navailable from the Collection, rather than relying on resources uploaded\nin other areas of your Site (e.g., Documents and Media). For more\ninformation on how to include resources in your Fragment Collection from\nthe Page Fragments interface, see\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-page-fragments}{Creating\nPage Fragments}.\n\nOnce you've uploaded your resource to a Fragment Collection, you can\nspecify it in your Fragment:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Fragment editing page by clicking your Fragment's\n  \\emph{Actions} \\includegraphics{./images/icon-actions.png} →\n  \\emph{Edit} button.\n\\item\n  Specify the image by using this syntax:\n  \\texttt{{[}resources:IMAGE\\_NAME{]}}. For example, you could include\n  an image \\texttt{building.png} within an HTML image tag like this:\n\n\\begin{verbatim}\n<img src=\"[resources:building.png]\">\n\\end{verbatim}\n\n  You can view a full example snippet below:\n\n\\begin{verbatim}\n<div class=\"fragment_38314\">\n    <lfr-editable id=\"img\" type=\"image\">\n        <img src=\"[resources:building.png]\">\n    </lfr-editable>\n</div>\n\\end{verbatim}\n\\item\n  Add any additional HTML, CSS, or JavaScript to your Fragment and then\n  click \\emph{Publish}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You can also reference your Fragment Collection's resources\n in your CSS code too. It follows the same syntax as its HTML.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-resources.png}\n\\caption{Any Fragment from the Fragment Collection has access to the\nuploaded resources.}\n\\end{figure}\n\nGreat! You've successfully referenced a default resource from your\nFragment Collection!\n\n\\chapter{Supporting Custom Content Types in Content and Display\nPages}\\label{supporting-custom-content-types-in-content-and-display-pages}\n\nContent Pages and Display Page Templates can display several types of\ncontent out-of-the-box:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Web Content Article\n\\item\n  Document\n\\item\n  Blogs Entry\n\\end{itemize}\n\nYou can publish these content types in highly customizable ways using\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/page-fragments}{Page\nFragments}. You can use these page types to map fields of certain\ncontent (e.g., Web Content) to fields defined in a Page Fragment. Then\nyou can publish the content on a page using the Page Fragment as a\ntemplate. To see an example of how Display Page Templates work, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/display-page-template-example}{Display\nPage Template Example}. For more info on creating Content Pages, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/building-content-pages}{Building\nContent Pages} article.\n\nIf you want to extend the Content Page or Display Page Template\nframework to support other content types, you must use the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/the-info-framework}{Info\nframework}.\n\nYou must complete the following steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Provide basic information about your custom content type.\n\\item\n  Provide your content type's fields so they're configurable in the Page\n  Editor.\n\\item\n  Provide friendly URLs for your page type.\n\\item\n  Handle the information that the user is requesting.\n\\end{enumerate}\n\nAs an example, you'll step through how to provide this information to\nthe Content Page and Display Page Template frameworks.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} This section assumes you're customizing Display Page\nTemplates' available content types. The same process outlined in these\narticles also applies to Content Pages, although it's not explicitly\nstated.\n\n\\noindent\\hrulefill\n\nContinue on to begin!\n\n\\chapter{Mapping a Content Type to a\nPage}\\label{mapping-a-content-type-to-a-page}\n\nYou must allow the mapping of your custom content type to the page type.\nTo do this, implement the\n\\href{https://docs.liferay.com/dxp/apps/info/2.0.0/javadocs/com/liferay/info/display/contributor/InfoDisplayContributor.html}{\\texttt{InfoDisplayContributor}}\ninterface. Follow the steps below to complete this for the custom User\ncontent type.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Inside your custom model project, create a new Java package and add a\n  class named \\texttt{UserInfoDisplayContributor}.\n\\item\n  Implement the \\texttt{InfoDisplayContributor} interface and pass the\n  \\texttt{User} model as the type parameter. Then add the\n  \\texttt{@Component} annotation:\n\n\\begin{verbatim}\n@Component(immediate = true, service = InfoDisplayContributor.class)\npublic class UserInfoDisplayContributor\n    implements InfoDisplayContributor<User> {\n}\n\\end{verbatim}\n\n  The \\texttt{@Component} annotation registers the class as an info\n  display contributor in the OSGi service registry. Set the\n  \\texttt{service} property to the interface you're implementing.\n\\item\n  Implement the methods. For the example User content type, three\n  methods are crucial to mapping its model to the Display Page Template\n  framework:\n\n\\begin{verbatim}\n@Override\npublic String getClassName() {\n    return User.class.getName();\n}\n\n@Override\npublic String getInfoURLSeparator() {\n    return \"/user/\";\n}\n\n@Override\npublic String getLabel(Locale locale) {\n    return \"Users\";\n}\n\\end{verbatim}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    The class name is used to link the Display Page Template to the User\n    model.\n  \\item\n    The URL separator is used to generate friendly URLs for the Display\n    Page Template.\n  \\item\n    The label is the display name for the new content type.\n  \\end{itemize}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/custom-model-selectable.png}\n\\caption{After creating the \\texttt{*InfoDisplayContributor} class, you\ncan create Display Page Templates and map them to your custom model.}\n\\end{figure}\n\nGreat! You've mapped your custom content type to the Display Page\nTemplate framework. Next, you'll provide your content type's fields.\n\n\\chapter{Specifying the Fields of a Custom Content\nType}\\label{specifying-the-fields-of-a-custom-content-type}\n\nNow that your custom content type is selectable for a Display Page\nTemplate, you must specify the fields you want the user to map to the\nfragment's editable fields in the Display Page Template. To do this,\nimplement the\n\\href{https://docs.liferay.com/dxp/apps/info/2.0.0/javadocs/com/liferay/info/display/contributor/field/InfoDisplayContributorField.html}{\\texttt{InfoDisplayContributorField}}\ninterface.\n\nFollow the steps below to create a user name field for the User content\ntype:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Inside your custom model project, add a class named\n  \\texttt{UserNameInfoDisplayContributorField}.\n\\item\n  Implement the \\texttt{InfoDisplayContributorField} interface and pass\n  the \\texttt{User} model as the type parameter. Then add the\n  \\texttt{@Component} annotation:\n\n\\begin{verbatim}\n@Component(\n    property = \"model.class.name=com.liferay.portal.kernel.model.User\",\n    service = InfoDisplayContributorField.class\n)\npublic class UserNameInfoDisplayContributorField\n    implements InfoDisplayContributorField<User> {\n}\n\\end{verbatim}\n\n  The \\texttt{@Component} annotation declares the class as an info\n  display contributor field in the OSGi service registry. You also set\n  the property \\texttt{model.class.name}, which associates the content\n  type you wish to configure with this service.\n\\item\n  Implement the methods.\n\n\\begin{verbatim}\n@Override\npublic String getKey() {\n    return \"userName\";\n}\n\n@Override\npublic String getLabel(Locale locale) {\n    return \"User Name\";\n}\n\n@Override\npublic InfoDisplayContributorFieldType getType() {\n    return InfoDisplayContributorFieldType.TEXT;\n}\n\n@Override\npublic String getValue(User user, Locale locale) {\n    return user.getFullName();\n}\n\\end{verbatim}\n\n  The above methods\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    set the content type field key to \\texttt{username}.\n  \\item\n    set the field label to \\texttt{User\\ Name}.\n  \\item\n    set the field type to text.\n  \\item\n    set the field value to the user's full name.\n  \\end{itemize}\n\\item\n  Now you must override the \\texttt{getInfoDisplayFields} method in your\n  \\texttt{*DisplayContributor} class, so the mappable fields are\n  displayed. Open the \\texttt{UserInfoDisplayContributor} class and add\n  the following method:\n\n\\begin{verbatim}\n@Override\npublic Set<InfoDisplayField> getInfoDisplayFields(\n        long classTypeId, Locale locale)\n    throws PortalException {\n\n    Set<InfoDisplayField> infoDisplayFields = new LinkedHashSet<>();\n\n    List<InfoDisplayContributorField> infoDisplayContributorFields =\n        _infoDisplayContributorFieldTracker.getInfoDisplayContributorFields(\n        getClassName());\n\n    for (InfoDisplayContributorField infoDisplayContributorField :\n    infoDisplayContributorFields) {\n\n        InfoDisplayContributorFieldType infoDisplayContributorFieldType =\n            infoDisplayContributorField.getType();\n\n        infoDisplayFields.add(\n            new InfoDisplayField(\n            infoDisplayContributorField.getKey(),\n            infoDisplayContributorField.getLabel(locale),\n            infoDisplayContributorFieldType.getValue()));\n    }\n\n    return infoDisplayFields;\n}\n\n@Reference\nprivate InfoDisplayContributorFieldTracker _infoDisplayContributorFieldTracker;\n\\end{verbatim}\n\n  This method references your new \\texttt{*InfoDisplayContributorField}\n  class to specify your content type's fields.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-type-custom-fields.png}\n\\caption{After creating the \\texttt{*InfoDisplayContributorField} class,\nyour custom content type has a new field to map.}\n\\end{figure}\n\nAwesome! You've mapped the content type's fields to the editable fields\nof the provided fragments. Next, you'll provide the friendly URLs for\nthe Display Page Template.\n\n\\chapter{Providing Friendly URLs for a Custom Content\nType}\\label{providing-friendly-urls-for-a-custom-content-type}\n\nTo provide a friendly URL for your custom content type, you must\nimplement a friendly URL resolver to retrieve the desired information.\nFor the User content type example, you'll want to retrieve profiles by a\nuser's screen name.\n\nTo do this, follow the steps below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\texttt{*InfoDisplayContributor} class you created previously\n  (covered\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/mapping-a-content-type-to-a-page}{here}).\n  Implement the \\texttt{getInfoDisplayObjectProvider} method. For the\n  User example, it looks like this:\n\n\\begin{verbatim}\n@Override\npublic InfoDisplayObjectProvider<User> getInfoDisplayObjectProvider(\n        long groupId, String urlTitle)\n    throws PortalException {\n\n    Group group = _groupLocalService.getGroup(groupId);\n\n    User user = _userLocalService.getUserByScreenName(\n        group.getCompanyId(), urlTitle);\n\n    return new InfoDisplayObjectProvider<User>() {\n\n        @Override\n        public long getClassNameId() {\n            return _classNameLocalService.getClassNameId(getClassName());\n        }\n\n        @Override\n        public long getClassPK() {\n            return user.getUserId();\n        }\n\n        @Override\n        public long getClassTypeId() {\n            return 0;\n        }\n\n        @Override\n        public String getDescription(Locale locale) {\n            return StringPool.BLANK;\n        }\n\n        @Override\n        public User getDisplayObject() {\n            return user;\n        }\n\n        @Override\n        public long getGroupId() {\n            return groupId;\n        }\n\n        @Override\n        public String getKeywords(Locale locale) {\n            return StringPool.BLANK;\n        }\n\n        @Override\n        public String getTitle(Locale locale) {\n            return user.getFullName();\n        }\n\n        @Override\n        public String getURLTitle(Locale locale) {\n            return user.getScreenName();\n        }\n\n    };\n}\n\\end{verbatim}\n\n  This method returns a new \\texttt{InfoDisplayObjectProvider} for the\n  User type. This is the specific model instance used to retrieve the\n  mapped values and check for the display page. This is required by the\n  friendly URL resolver. Now you'll implement the friendly URL resolver\n  for the User content type.\n\\item\n  Inside your custom model project, add a class named\n  \\texttt{UserDisplayPageFriendlyURLResolver}.\n\\item\n  Implement the\n  \\href{https://docs.liferay.com/dxp/portal//7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/FriendlyURLResolver.html}{\\texttt{FriendlyURLResolver}}\n  interface. Then add the \\texttt{@Component} annotation:\n\n\\begin{verbatim}\n@Component(service = FriendlyURLResolver.class)\npublic class UserDisplayPageFriendlyURLResolver implements FriendlyURLResolver {\n\n}\n\\end{verbatim}\n\n  The \\texttt{@Component} annotation declares the class as a friendly\n  URL resolver in the OSGi service registry.\n\\item\n  Implement the methods. The User type's implementation looks like this:\n\n\\begin{verbatim}\n@Override\npublic String getActualURL(\n    long companyId, long groupId, boolean privateLayout,\n      String mainPath, String friendlyURL, Map<String, String[]> params,\n    Map<String, Object> requestContext)\nthrows PortalException {\n\n    HttpServletRequest request = (HttpServletRequest)requestContext.get(\n        \"request\");\n\n    InfoDisplayContributor infoDisplayContributor =\n        _getInfoDisplayContributor();\n\n    List<String> paths = StringUtil.split(friendlyURL, CharPool.SLASH);\n\n    InfoDisplayObjectProvider infoDisplayObjectProvider =\n        infoDisplayContributor.getInfoDisplayObjectProvider(\n            groupId, paths.get(1));\n\n    request.setAttribute(\n        AssetDisplayPageWebKeys.INFO_DISPLAY_OBJECT_PROVIDER,\n            infoDisplayObjectProvider);\n\n    request.setAttribute(\n        InfoDisplayWebKeys.INFO_DISPLAY_CONTRIBUTOR,\n            infoDisplayContributor);\n\n    Layout layout = _getInfoDisplayObjectProviderLayout(\n        infoDisplayObjectProvider);\n\n    return _portal.getLayoutActualURL(layout, mainPath);\n}\n\n@Override\npublic LayoutFriendlyURLComposite getLayoutFriendlyURLComposite(\n    long companyId, long groupId, boolean privateLayout,\n    String friendlyURL, Map<String, String[]> params,\n    Map<String, Object> requestContext)\nthrows PortalException {\n\n    Layout layout = _getInfoDisplayObjectProviderLayout(\n        _getInfoDisplayObjectProvider(groupId, friendlyURL));\n\n    return new LayoutFriendlyURLComposite(layout, friendlyURL);\n}\n\n@Override\npublic String getURLSeparator() {\n    return \"/user/\";\n}\n\nprivate InfoDisplayContributor _getInfoDisplayContributor()\n    throws PortalException {\n\n    InfoDisplayContributor infoDisplayContributor =\n        _infoDisplayContributorTracker.\n            getInfoDisplayContributorByURLSeparator(getURLSeparator());\n\n    if (infoDisplayContributor == null) {\n        throw new PortalException(\n            \"Info display contributor is not available for \" +\n            getURLSeparator());\n    }\n\n    return infoDisplayContributor;\n}\n\nprivate InfoDisplayObjectProvider _getInfoDisplayObjectProvider(\n        long groupId, String friendlyURL)\n    throws PortalException {\n\n    List<String> paths = StringUtil.split(friendlyURL, CharPool.SLASH);\n\n    InfoDisplayContributor infoDisplayContributor =\n        _infoDisplayContributorTracker.getInfoDisplayContributor(\n            User.class.getName());\n\n    return infoDisplayContributor.getInfoDisplayObjectProvider(\n        groupId, paths.get(1));\n}\n\nprivate Layout _getInfoDisplayObjectProviderLayout(\n    InfoDisplayObjectProvider infoDisplayObjectProvider) {\n\n    LayoutPageTemplateEntry layoutPageTemplateEntry =\n        _layoutPageTemplateEntryService.fetchDefaultLayoutPageTemplateEntry(\n            infoDisplayObjectProvider.getGroupId(),\n            infoDisplayObjectProvider.getClassNameId(),\n            infoDisplayObjectProvider.getClassTypeId());\n\n    if (layoutPageTemplateEntry != null) {\n        return _layoutLocalService.fetchLayout(\n            layoutPageTemplateEntry.getPlid());\n    }\n\n    return null;\n}\n\n@Reference\nprivate InfoDisplayContributorTracker _infoDisplayContributorTracker;\n\n@Reference\nprivate LayoutLocalService _layoutLocalService;\n\n@Reference\nprivate LayoutPageTemplateEntryService _layoutPageTemplateEntryService;\n\n@Reference\nprivate Portal _portal;\n\\end{verbatim}\n\n  Notice you're finding the \\texttt{InfoDisplayObjectProvider}\n  corresponding to the current user. This serves as the\n  representation/descriptor of the mapped object.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** This `FriendlyURLResolver` implementation uses the default\n display page template for the User model.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nWhen this implementation is deployed, you'll receive an empty page when\ncalling the URL `[host]/web/guest/user/[screenName]`. You must return the\nvalues from the users that are mapped to the display page. You'll do this\nnext.\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{4}\n\\item\n  Implement the \\texttt{getInfoDisplayFieldsValue} method in the\n  previously created \\texttt{*InfoDisplayContributor} class.\n\n\\begin{verbatim}\n@Override\npublic Map<String, Object> getInfoDisplayFieldsValues(\n        User user, Locale locale)\n    throws PortalException {\n\n    Map<String, Object> infoDisplayFieldsValues = new HashMap<>();\n\n    List<InfoDisplayContributorField> infoDisplayContributorFields =\n        _infoDisplayContributorFieldTracker.getInfoDisplayContributorFields(\n            getClassName());\n\n    for (InfoDisplayContributorField infoDisplayContributorField :\n        infoDisplayContributorFields) {\n\n        Object fieldValue = infoDisplayContributorField.getValue(\n            user, locale);\n\n        infoDisplayFieldsValues.putIfAbsent(\n            infoDisplayContributorField.getKey(), fieldValue);\n    }\n\n    return infoDisplayFieldsValues;\n}\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! Now you have a friendly URL that maps to your display page\ntemplate's custom content type.\n\n\\chapter{Integrating Display Pages into Content\nCreation}\\label{integrating-display-pages-into-content-creation}\n\nAfter you add support for Display Pages in your custom entities, you can\nintegrate display page configuration into your entity's creation form.\n\n\\section{Display Page Taglib Example}\\label{display-page-taglib-example}\n\nTo provide the Display Page selector for the User type after you\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/specifying-the-fields-of-a-custom-content-type}{created\nfields for it},\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your JSP used for displaying the editing interface (e.g.,\n  \\texttt{.../META-INF/resources/.../edit\\_entry.jsp}).\n\\item\n  Add this code in the appropriate place in the layout to add the\n  Display Page selector:\n\n\\begin{verbatim}\n<liferay-asset:select-asset-display-page\n    classNameId=\"<%= PortalUtil.getClassNameId(User.class) %>\"\n    classPK=\"<%= userId %>\"\n    groupId=\"<%= scopeGroupId %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\nNow, a selector is available to define a default Display Page when\nediting or creating a User.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/display-pages-select-default-display-page.png}\n\\caption{You need to add the Display Page selection to your content\ntype's create/edit page to define the Display Page for each instance of\nthat asset.}\n\\end{figure}\n\nAwesome! Your custom content type is now available for Content Pages\nand/or Display Page Templates.\n\n\\chapter{Screen Navigation Framework}\\label{screen-navigation-framework}\n\nThe Screen Navigation Framework is for customizing and extending\napplication UIs. You can use it to make Liferay's applications your own\nand to make your applications customizable by others.\n\nThe framework uses a specific structure for screens and supports one or\ntwo levels of navigation. Each item in the top level navigation is a\n\\texttt{ScreenNavigationCategory}. Each item in the second level is a\n\\texttt{ScreenNavigationEntry}. Categories are usually represented by\ntabs, while entries use a second level of navigation. You need not have\nany Entries in your application, but you must have at least one\nCategory.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/screen-nav-sample-screen-1.png}\n\\caption{The User Management application has three Screen Navigation\nCategories: General, Contact, and Preference; and each of those have a\nnumber of Screen Navigation Entries}\n\\end{figure}\n\nThe Screen structure normally renders Navigation Categories as\nhorizontal tabs at the top of the page and Navigation Entries as a\nvertical list of items along the left side of the page. The screen box\ncontaining the content uses the rest of the screen. You can customize\nthis default layout for your needs.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/screen-nav-one-level.png}\n\\caption{Many application only use Screen Navigation Categories for\ntheir functionality, and don't have Screen Navigation Entries. For\nBlogs, Entries and Images are Categories with no Entries.}\n\\end{figure}\n\n\\section{Using the Framework for Your\nApplication}\\label{using-the-framework-for-your-application}\n\nThe Screen Navigation Framework comprises two parts: Java classes for\nyour screens and a tag library for your front-end. To use Screen\nNavigation for your application, first you'll create the necessary Java\nclasses and then add the front-end support through JSPs.\n\nYour \\texttt{ScreenNavigationCategory} class must be a a component that\nimplements the \\texttt{ScreenNavigationCategory} interface with these\nmethods:\n\n\\textbf{\\texttt{getCategoryKey()}}: returns the category's primary key.\n\n\\textbf{\\texttt{getLabel(Locale\\ locale)}}: returns the label of the\nkey.\n\n\\textbf{\\texttt{getScreenNavigationKey()}}: returns the navigation key\nthat the category belongs in, as defined in your application.\n\nYour \\texttt{ScreenNavigationEntry} class, similarly must be a component\nwhich implements \\texttt{ScreenNavigationEntry} with the following\nmethods:\n\n\\textbf{\\texttt{getCategoryKey()}}: returns the category's primary key.\n\n\\textbf{\\texttt{getEntryKey()}}: returns the entry's primary key.\n\n\\textbf{\\texttt{getLabel()}}: returns the entries label.\n\n\\textbf{\\texttt{getScreenNavigationKey()}}: returns the navigation key\nfor the category of the current entry.\n\n\\textbf{\\texttt{isVisible(User\\ user,\\ T\\ screenModelBean)}}: boolean\nfor whether or not the entry should be visible for the current user.\n\n\\textbf{\\texttt{render(HttpServletRequest\\ request,\\ HttpServletResponse\\ response)}}:\nrenders the entry. The \\texttt{render} method is also where any logic\nfor creating the configuration goes.\n\n\\section{Adding Custom Screens to Liferay\nApplications}\\label{adding-custom-screens-to-liferay-applications}\n\nYou can extend certain Liferay Applications with custom screens. Custom\nscreens can add configuration for features you've developed and added to\na Liferay application, integrating them seamlessly with the original\napplication.\n\nThe parameters it needs are \\texttt{key}, \\texttt{modelBean}, and\n\\texttt{portletURL}.\n\n\\begin{itemize}\n\\item\n  \\textbf{Key}: a unique name for the navigation in this application.\n\\item\n  \\textbf{modelBean}: the model that is being rendered\n\\item\n  \\textbf{portletURL}: the portlet URL used to build the titles for each\n  link.\n\\end{itemize}\n\nIn addition to these parameters, you must build the page that users will\nuse for configuration, and connect it to your business logic through the\nrender command.\n\n\\chapter{Using Screen Navigation for Your\nApplication}\\label{using-screen-navigation-for-your-application}\n\nTo use the Screen Navigation framework with your application you must\ncreate a Screen Navigation Category and Entry, and then create the JSP\nto provide the layout for the Screen Navigation Entry.\n\n\\section{Adding Screens to Your Application's\nBack-end}\\label{adding-screens-to-your-applications-back-end}\n\nTo add screens to your application, first you must add at least one\nNavigation Category for the top level navigation. Then you can add\nadditional Navigation Entries for each page that you need. To\ndemonstrate this, follow the instructions for integrating Screen\nNavigation for a sample application.\n\nFirst, create the Navigation Category:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your existing application, create a component named\n  \\texttt{SampleScreenNavigationCategory} that implements the\n  \\texttt{ScreenNavigationCategory} interface.\n\\item\n  In the \\texttt{@Component} declaration, set your priority property\n  which determines what order items appear in in the side navigation:\n\n\\begin{verbatim}\nproperty = \"screen.navigation.category.order:Integer=10\",\n\\end{verbatim}\n\\item\n  Add the following methods to the class body:\n\n\\begin{verbatim}\n@Override\npublic String getCategoryKey() {\n    return SampleScreenNavigationConstants.\n        CATEGORY_KEY_SAMPLE_CONFIGURATION;\n}\n\n@Override\npublic String getLabel(Locale locale) {\n    return LanguageUtil.get(locale, \"general\");\n}\n\n@Override\npublic String getScreenNavigationKey() {\n    return SampleScreenNavigationConstants.\n        SAMPLE_KEY_METHOD;\n}\n\\end{verbatim}\n\n  When you're finished, your \\texttt{ScreenNavigationCategory} class\n  looks like this:\n\n\\begin{verbatim}\n@Component(\nproperty = \"screen.navigation.category.order:Integer=10\",\nservice = ScreenNavigationCategory.class\n)\npublic class SampleScreenNavigationCategory\n  implements ScreenNavigationCategory {\n\n  @Override\n  public String getCategoryKey() {\n    return SampleScreenNavigationConstants.\n        CATEGORY_KEY_SAMPLE_CONFIGURATION;\n  }\n\n  @Override\n  public String getLabel(Locale locale) {\n    return LanguageUtil.get(locale, \"general\");\n  }\n\n  @Override\n  public String getScreenNavigationKey() {\n    return SampleScreenNavigationConstants.\n        SAMPLE_KEY_METHOD;\n  }\n}\n\\end{verbatim}\n\\end{enumerate}\n\nNext, add a Navigation Entry:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a component named \\texttt{SampleScreenNavigationEntry} which\n  implements \\texttt{ScreenNavigationEntry}.\n\\item\n  Create the the \\texttt{@Reference} variables you need for the render\n  logic:\n\n\\begin{verbatim}\n@Reference\nprivate ConfigurationProvider _configurationProvider;\n\n@Reference\nprivate JSPRenderer _jspRenderer;\n\n@Reference\nprivate Portal _portal;\n\n@Reference(\n    target = \"(osgi.web.symbolicname=com.liferay.commerce.payment.method.sample)\"\n)\nprivate ServletContext _servletContext;\n\\end{verbatim}\n\\item\n  Implement the following methods in your component:\n\n\\begin{verbatim}\n@Override\npublic String getCategoryKey() {\n    return SampleScreenNavigationConstants.\n        CATEGORY_KEY_SAMPLE_CONFIGURATION;\n}\n\n@Override\npublic String getEntryKey() {\n    return SampleScreenNavigationConstants.\n        ENTRY_KEY_SAMPLE_CONFIGURATION;\n}\n\n@Override\npublic String getLabel(Locale locale) {\n    return LanguageUtil.get(\n        locale,\n        SampleScreenNavigationConstants.\n            CATEGORY_KEY_SAMPLE_CONFIGURATION);\n}\n\n@Override\npublic String getScreenNavigationKey() {\n    return SpaceShipScreenNavigationConstants.\n        SAMPLE_KEY_METHOD;\n}\n\n@Override\npublic boolean isVisible(\n    User user, SamplePermissions spaceShipPermissions) {\n\n    if (samplePermissions.criteriaMethod()) \n    {\n\n        return true;\n    }\n\n    return false;\n}\n\n@Override\npublic void render(HttpServletRequest request, HttpServletResponse response)\nthrows IOException {\n\n    _jspRenderer.renderJSP(request, response, \"/my-category/view-category.jsp\");\n}\n\\end{verbatim}\n\n  Here is what the \\texttt{SampleScreenNavigationEntry} class looks\n  like:\n\n\\begin{verbatim}\n@Component(\nproperty = \"screen.navigation.entry.order:Integer=20\",\nservice = ScreenNavigationEntry.class\n)\npublic class\n    SampleScreenNavigationEntry\n    implements ScreenNavigationEntry<SampleApplication> {\n\n    public static final String\n        ENTRY_KEY_SAMPLE_CONFIGURATION =\n            \"sample-configuration\";\n\n    @Override\n    public String getCategoryKey() {\n        return SpaceShipScreenNavigationConstants.\n            CATEGORY_KEY_SAMPLE_CONFIGURATION;\n    }\n\n    @Override\n    public String getEntryKey() {\n        return ENTRY_KEY_SAMPLE_CONFIGURATION;\n    }\n\n    @Override\n    public String getLabel(Locale locale) {\n        return LanguageUtil.get(\n            locale,\n            SpaceShipScreenNavigationConstants.\n                CATEGORY_KEY_SAMPLE_CONFIGURATION);\n    }\n\n    @Override\n    public String getScreenNavigationKey() {\n        return SpaceShipScreenNavigationConstants.\n            SAMPLE_KEY_METHOD;\n    }\n\n    @Override\n    public boolean isVisible(\n        User user, SamplePermissions spaceShipPermissions) {\n\n        if (samplePermissions.criteriaMethod()) \n        {\n\n            return true;\n        }\n\n            return false;\n        }\n\n    @Override\n    public void render(HttpServletRequest request, HttpServletResponse response)\n    throws IOException {\n\n        _jspRenderer.renderJSP(request, response, \"/my-category/view-category.jsp\");\n    }\n\n    @Reference\n    private JSPRenderer _jspRenderer;\n\n    @Reference(\n        target = \"(osgi.web.symbolicname=com.liferay.commerce.payment.method.sample);\n\n    }\n\\end{verbatim}\n\\end{enumerate}\n\nYou can implement your render method any way that you want as long as it\nprovides a way to render HTML. Liferay developers typically use JSPs,\nshown below.\n\n\\section{Adding Screens to Your Application's\nFront-end}\\label{adding-screens-to-your-applications-front-end}\n\nThe \\texttt{render} method that you created in your last step references\n\\texttt{/my-category/view-category.jsp}. Create the JSP now:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In \\texttt{/src/resources/META-INF/resources} create the\n  \\texttt{my-category} folder.\n\\item\n  Inside of that folder, create \\texttt{view-category.jsp}.\n\\item\n  Inside the JSP add the \\texttt{liferay-frontend:screen-navigation}\n  taglib with the required parameters:\n\n\\begin{verbatim}\n<liferay-frontend:screen-navigation key=\"<%= AssetCategoriesConstants.CATEGORY_KEY_GENERAL %>\"\n    modelBean=\"<%= category %>\"\n    portletURL=\"<%= portletURL %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\nAfter that tag, add the rest of the content of the JSP file to handle\nuser interactions and communication with the back-end for configuration.\n\n\\chapter{Extending Categories\nAdministration}\\label{extending-categories-administration}\n\nThe Categories Administration application supports adding Custom Screens\nto provide additional options for editing a category. To demonstrate\nadding a new Screen Navigation Entry and Category, you'll add one to\nCategories Administration.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new Java class in the \\texttt{asset-categories-admin-web}\n  module named \\texttt{CategoryCustomScreenNavigationEntry} that\n  implements \\texttt{ScreenNavigationCategory} and\n  \\texttt{ScreenNavigationEntry}.\n\\item\n  Add the following Component annotation above the class declaration:\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"screen.navigation.category.order:Integer=1\",\n        \"screen.navigation.entry.order:Integer=1\"\n        },\n         service = {ScreenNavigationCategory.class, ScreenNavigationEntry.class}\n    )\n\\end{verbatim}\n\n  The \\texttt{screen.navigation.category.order} and\n  \\texttt{screen.navigation.entry.order} determine where in the\n  navigation the items appear. Higher is first in the navigation.\n\n  In the \\texttt{service} declaration, declare it as defining a\n  \\texttt{ScreenNavigationCategory}, \\texttt{ScreenNavigationEntry}, or\n  both.\n\\item\n  For the class body, insert this code:\n\n\\begin{verbatim}\n@Override\npublic String getCategoryKey() {\n    return \"custom-screen\";\n}\n\n@Override\npublic String getEntryKey() {\n    return \"custom-screen\";\n}\n\n@Override\npublic String getLabel(Locale locale) {\n    return LanguageUtil.get(locale, \"custom-screen\");\n}\n\n@Override\npublic String getScreenNavigationKey() {\n    return AssetCategoriesConstants.CATEGORY_KEY_GENERAL;\n}\n\n@Override\npublic void render(HttpServletRequest request, HttpServletResponse response)\n    throws IOException {\n\n         _jspRenderer.renderJSP(request, response, \"/category/custom-screen.jsp\");\n}\n\n@Reference\nprivate JSPRenderer _jspRenderer;\n\\end{verbatim}\n\\item\n  Create a \\texttt{custom-screen.jsp} in the\n  \\texttt{/resources/META-INF/resources/category/} folder.\n\\item\n  At the top of your JSP class, insert the following scriptlet to use\n  the Screen Navigation UI:\n\n\\begin{verbatim}\n<%\nString redirect = ParamUtil.getString(request, \"redirect\", assetCategoriesDisplayContext.getEditCategoryRedirect());\n\nlong categoryId = ParamUtil.getLong(request, \"categoryId\");\n\nAssetCategory category = AssetCategoryLocalServiceUtil.fetchCategory(categoryId);\n\nlong parentCategoryId = BeanParamUtil.getLong(category, request, \"parentCategoryId\");\n\nlong vocabularyId = ParamUtil.getLong(request, \"vocabularyId\");\n\nportletDisplay.setShowBackIcon(true);\nportletDisplay.setURLBack(redirect);\n\nrenderResponse.setTitle(((category == null) ? LanguageUtil.get(request, \"add-new-category\") : category.getTitle(locale)));\n%>\n\\end{verbatim}\n\\item\n  Below that, insert the following tag:\n\n\\begin{verbatim}\n<liferay-frontend:screen-navigation key=\n\"<%= AssetCategoriesConstants.CATEGORY_KEY_GENERAL %>\"\nmodelBean=\"<%= category %>\"\nportletURL=\"<%= portletURL %>\"\n/>\n\\end{verbatim}\n\\item\n  For the rest of the JSP, create your custom screen.\n\\end{enumerate}\n\nNow you can use that pattern to create additional screens for whatever\nyou need.\n\n\\chapter{Developing a Fragment\nRenderer}\\label{developing-a-fragment-renderer}\n\nWhen creating Fragments through Liferay DXP's provided UI, you're given\nthree front-end languages to leverage: CSS, HTML, and JavaScript.\nAlthough you can harness a lot of power with these languages alone, they\ndo not provide an easy way to retrieve and process information from the\ndatabase or third party systems. A common solution for this issue is\ncreating a full-fledged portlet to complete common back-end necessities,\nbut this is sometimes overkill for what you need.\n\nFor a lightweight alternative, you can develop a \\emph{Fragment\nRenderer} to use Liferay's provided Java APIs for back-end tasks related\nto your Fragment. To do this, you must\n\\hyperref[implementing-the-fragmentrenderer-interface]{implement the\n\\texttt{FragmentRenderer} interface}.\n\nOptionally, you can\n\n\\begin{itemize}\n\\item\n  \\hyperref[leveraging-the-fragmentrenderercontext]{Leverage the\n  \\texttt{FragmentRendererContext}}.\n\\item\n  \\hyperref[rendering-jsps]{Use JSPs for your Fragment's display}.\n\\item\n  \\hyperref[choosing-when-to-display-a-component]{Choose when to display\n  the component}.\n\\item\n  \\hyperref[translating-the-collection-language-key]{Translate the\n  Collection language key}.\n\\end{itemize}\n\nYou'll explore each step next.\n\n\\section{Implementing the FragmentRenderer\nInterface}\\label{implementing-the-fragmentrenderer-interface}\n\nThe\n\\href{https://docs.liferay.com/dxp/apps/fragment/latest/javadocs/com/liferay/fragment/renderer/FragmentRenderer.html}{\\texttt{FragmentRenderer}}\ninterface requires the implementation of two methods:\n\n\\texttt{getCollectionKey}: returns the unique key for the component's\nCollection. Define this key in several components to group them under a\ncollapsible panel in the Page Editor.\n\n\\texttt{getLabel}: provides the Fragment name.\n\nThe remaining methods are optional, but can be useful in many scenarios:\n\n\\texttt{getImagePreviewURL}: returns the URL for previewing the\nFragment's image.\n\n\\texttt{getKey}: returns the Fragment's key.\n\n\\texttt{getType}: returns the Fragment's type. Type values include\n\\texttt{FragmentConstants.TYPE\\_COMPONENT} and\n\\texttt{FragmentConstants.TYPE\\_SECTION}.\n\n\\texttt{isSelectable}: defines whether page authors can select the\nFragment Renderer. You'll learn more about this in the\n\\hyperref[choosing-when-to-display-a-component]{Choosing When to Display\na Component} section.\n\n\\texttt{render} (highly recommended): defines how to render the Fragment\nRenderer (e.g., JSP or FreeMarker). You can leverage the\n\\texttt{FragmentRendererContext} in this method to facilitate the\nrendering process.\n\nNext, you'll learn about leveraging the\n\\texttt{FragmentRendererContext}.\n\n\\section{Leveraging the\nFragmentRendererContext}\\label{leveraging-the-fragmentrenderercontext}\n\nThe \\texttt{render} method receives a read-only instance of the\ninterface\n\\href{https://docs.liferay.com/dxp/apps/fragment/latest/javadocs/com/liferay/fragment/renderer/FragmentRendererContext.html}{\\texttt{FragmentRendererContext}}.\nThis provides information about the context in which the Fragment is\nbeing rendered. The fields of information that are accessible through it\ninclude\n\n\\textbf{Fragment Entry Link}: The specific instance of the Fragment\nbeing rendered. This information can be used to identify the specific\nsite or page to which the Fragment was added, when it was added, the\nuser who added it, etc.\n\n\\textbf{Locale}: The current locale to be used for any multi-locale\ntext.\n\n\\textbf{Mode}: There are three available modes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{VIEW}: The component is being rendered within a page being\n  viewed (not edited).\n\\item\n  \\textbf{ASSET\\_DISPLAY\\_PAGE}: The component is being edited on a\n  Display Page.\n\\item\n  \\textbf{EDIT}: The component is being edited on a Content Page.\n\\end{itemize}\n\nThere are other fields which should only be necessary for advanced use\ncases:\n\n\\textbf{Preview Class PK}: If the Fragment supports displaying content,\nthis field supports previewing an \\emph{In progress} version of the\ncontent before it's ready to publish. In this case, the \\texttt{render}\nmethod returns the content's primary key.\n\n\\textbf{Preview Type}: Represents the preview type you want to show. The\naccepted values include\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{TYPE\\_LATEST\\_APPROVED}: The latest approved version of the\n  content.\n\\item\n  \\textbf{TYPE\\_LATEST}: The latest version of the content.\n\\end{itemize}\n\n\\textbf{Field Values}: Fragments can have editable elements through\n\\texttt{\\textless{}lfr-editable\\textgreater{}} tags; this also applies\nto those created with \\texttt{FragmentRenderer}. The\n\\texttt{getFieldValuesOptional()} method retrieves the field values the\nuser may have introduced in them. This only applies in the context of a\nDisplay Page with the values of the mapped structure.\n\n\\textbf{Segment Experience IDs}: A list of identifiers for experiences\nthat have been configured for the current page.\n\n\\section{Rendering JSPs}\\label{rendering-jsps}\n\nUsually you'll want to avoid writing HTML in your Java code.\nFortunately, you can use the \\texttt{render} method to use any\ntemplating mechanism of your choice. JSP integration is provided\nout-of-the box.\n\nFor example, rendering a JSP for your Fragment Renderer would look like\nthis:\n\n\\begin{verbatim}\n@Override\npublic void render(\n    FragmentRendererContext fragmentRendererContext,\n    HttpServletRequest httpServletRequest,\n    HttpServletResponse httpServletResponse) throws IOException {\n\n    httpServletRequest.setAttribute(\n        \"fragmentRendererContext\", fragmentRendererContext);\n\n    _jspRenderer.renderJSP(\n        httpServletRequest, httpServletResponse, \"/my-component.jsp\");\n}\n\n@Reference\nprivate JSPRenderer _jspRenderer;\n\n@Reference(\n    target = \"(osgi.web.symbolicname=com.liferay.fragment.renderer.docs)\",\n    unbind = \"-\"\n)\nprivate ServletContext _servletContext;\n\\end{verbatim}\n\nThis sets the\n\\hyperref[leveraging-the-fragmentrenderercontext]{\\texttt{FragmentRendererContext}}\nin the HTTP servlet request, which is then used to render the included\nJSP file (e.g., \\texttt{my-component.jsp}).\n\nTo leverage JSPs, you must specify the servlet context for the JSP\nfiles. Since your Fragment Renderer is an OSGi module, your\n\\texttt{bnd.bnd} file must define a web context path:\n\n\\begin{verbatim}\nBundle-SymbolicName: com.liferay.fragment.renderer.docs\nWeb-ContextPath: /my-fragment-renderer\n\\end{verbatim}\n\nThen you must reference the Servlet context using the symbolic name of\nyour module, as was shown above:\n\n\\begin{verbatim}\n@Reference(\n    target = \"(osgi.web.symbolicname=com.liferay.fragment.renderer.docs)\",\n    unbind = \"-\"\n)\nprivate ServletContext _servletContext;\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To use the JSP Renderer, your module must set the\n\\texttt{com.liferay.frontend.taglib} dependency in its build file.\n\n\\noindent\\hrulefill\n\nNext, you'll learn about controlling when your Fragment Renderer is\ndisplayed.\n\n\\section{Choosing When to Display a\nComponent}\\label{choosing-when-to-display-a-component}\n\nSometimes offering Fragment components only makes sense in specific\ncases. You can implement the \\texttt{isSelectable(...)} method to\nspecify under which conditions the Fragment Renderer is available to\npage authors.\n\nFor example, if you wanted to make your Fragment Renderer only available\nin Display Pages, you could implement the \\texttt{isSelectable} method\nlike this:\n\n\\begin{verbatim}\n@Override\npublic boolean isSelectable(HttpServletRequest httpServletRequest) {\n    Layout layout = (Layout)httpServletRequest.getAttribute(WebKeys.LAYOUT);\n\n    if (Objects.equals(\n        layout.getType(), LayoutConstants.TYPE_ASSET_DISPLAY)) {\n\n        return true;\n    }\n\n    return false;\n}\n\\end{verbatim}\n\nThis determines the Fragment Renderer's page type and returns\n\\texttt{true} when the page type is a Display Page or \\texttt{false} if\nit's not.\n\n\\section{Translating the Collection Language\nKey}\\label{translating-the-collection-language-key}\n\nWhen setting your Fragment Renderer's collection name via the\n\\texttt{getCollectionKey} method, you should specify it as a language\nkey and then define it in a resource bundle.\n\nFor example, a \\texttt{getCollectionKey} method could look like this:\n\n\\begin{verbatim}\n@Override\npublic String getCollectionKey() {\n    return \"sample-components\";\n}\n\\end{verbatim}\n\nTo specify \\texttt{sample-components} in a resource bundle, create the\n\\texttt{src/main/resources/content/Language.properties} file within the\nFragment Renderer module and define it using the language key\n\\texttt{fragment.collection.label.\\{collection-key\\}}. For example,\n\n\\begin{verbatim}\nfragment.collection.label.sample-components=Sample Components\n\\end{verbatim}\n\nTo learn more about resource bundles, see the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localization}{Localization}\nsection.\n\nNext, you'll step through creating a Fragment Renderer.\n\n\\chapter{Creating a Fragment\nRenderer}\\label{creating-a-fragment-renderer}\n\nCreating a Fragment Renderer lets you call Liferay's provided Java APIs\nfor back-end tasks related to your Fragment. In this article, you'll\ncreate a sample Fragment Renderer that displays values stored in the\ncurrent Liferay DXP instance's database.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  a default module project} in your development environment.\n\\item\n  Create a unique package name in the module's \\texttt{src} directory\n  and create a new Java class in that package. To follow naming\n  conventions, give your class a unique name followed by\n  \\texttt{FragmentRenderer} (e.g.,\n  \\texttt{ShowContextFragmentRenderer}).\n\\item\n  Configure your new class to implement the\n  \\href{https://docs.liferay.com/dxp/apps/fragment/latest/javadocs/com/liferay/fragment/renderer/FragmentRenderer.html}{\\texttt{FragmentRenderer}}\n  interface:\n\n\\begin{verbatim}\npublic class ShowContextFragmentRenderer implements FragmentRenderer {\n}\n\\end{verbatim}\n\\item\n  Insert the following \\texttt{@Component} annotation above the class\n  declaration:\n\n\\begin{verbatim}\n@Component(service = FragmentRenderer.class)\n\\end{verbatim}\n\n  This sets the OSGi service type to \\texttt{FragmentRenderer}.\n\\item\n  Implement the two required \\texttt{FragmentRenderer} methods:\n\n\\begin{verbatim}\n@Override\npublic String getCollectionKey() {\n    return \"sample-components\";\n}\n\n@Override\npublic String getLabel(Locale locale) {\n    return \"Show Context Component\";\n}\n\\end{verbatim}\n\n  The \\texttt{getCollectionKey()} method returns a language key, which\n  you'll define later. The name displayed for this Fragment is defined\n  as \\emph{Show Context Component}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/show-context-fragment-renderer.png}\n  \\caption{The new Fragment Renderer appears in its defined component\n  collection.}\n  \\end{figure}\n\\item\n  Implement the \\texttt{render} method:\n\n\\begin{verbatim}\n@Override\npublic void render(\n    FragmentRendererContext fragmentRendererContext,\n    HttpServletRequest httpServletRequest,\n    HttpServletResponse httpServletResponse) throws IOException {\n\n        PrintWriter printWriter = httpServletResponse.getWriter();\n\n        printWriter.write(\"<h3>Context</h3>\");\n        printWriter.write(\"<ul>\");\n\n        FragmentEntryLink fragmentEntryLink =\n        fragmentRendererContext.getFragmentEntryLink();\n\n        printWriter.write(\"<li>Added by: \" + fragmentEntryLink.getUserName());\n        printWriter.write(\"<li>Added in: \" + fragmentEntryLink.getCreateDate());\n\n        printWriter.write(\"<li>Locale: \" + fragmentRendererContext.getLocale());\n        printWriter.write(\"<li>Mode: \" + fragmentRendererContext.getMode());\n        printWriter.write(\"<li>PreviewClassPK: \" + fragmentRendererContext.getPreviewClassPK());\n        printWriter.write(\"<li>PreviewType: \" + fragmentRendererContext.getPreviewType());\n        printWriter.write(\"<li>Segment experiences: \" + StringUtil.merge(fragmentRendererContext.getSegmentsExperienceIds(), \", \"));\n        printWriter.write(\"</ul>\");\n}\n\\end{verbatim}\n\n  This method leverages the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/developing-a-fragment-renderer\\#leveraging-the-fragmentrenderercontext}{\\texttt{FragmentRendererContext}},\n  which provides the Fragment's context information stored in the\n  database. This information is displayed in the Fragment Renderer when\n  it's placed on a page.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/show-context-fragment-renderer-page.png}\n  \\caption{When adding the new Fragment Renderer to a page, the context\n  information is displayed.}\n  \\end{figure}\n\\item\n  Define the language key \\texttt{sample-components} that you used in\n  the \\texttt{getCollectionKey()} method. To do this, create the\n  \\texttt{src/main/resources/content/Language.properties} file and add\n  the following language key:\n\n\\begin{verbatim}\nfragment.collection.label.sample-components=Sample Components\n\\end{verbatim}\n\\item\n  Provide the appropriate dependencies to compile your Fragment Renderer\n  project. For example, the following dependencies are defined for the\n  Show Context Component Fragment Renderer sample (Gradle build)\n  deployed to Liferay Portal 7.2 GA1:\n\n\\begin{verbatim}\ndependencies {\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"4.13.0\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.fragment.api\", version: \"2.7.2\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.fragment.service\", version: \"2.0.10\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.frontend.taglib\", version: \"4.0.15\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\", version: \"3.0.0\"\n    compileOnly group: \"javax.portlet\", name: \"portlet-api\", version: \"3.0.0\"\n    compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\", version: \"3.0.1\"\n    compileOnly group: \"jstl\", name: \"jstl\", version: \"1.2\"\n    compileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\", version: \"1.3.0\"\n}\n\\end{verbatim}\n\n  To stay in sync with the appropriate versions of your project's\n  dependencies, consider using the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform}{Target\n  Platform} framework.\n\\end{enumerate}\n\nThat's it! You can compile the sample \\emph{Show Context Component}\nFragment Renderer and\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{deploy\nit}! It'll be available to add for a Fragment-enabled page under the\n\\emph{Sample Components} collection.\n\n\\chapter{Web Services}\\label{web-services}\n\nIt's important for apps on different machines to communicate. To enable\nthis, an app can expose APIs so remote components (other apps or\ndevices) can access the app's features. For example, one service could\nhave a client app presenting information to users, a server app\nprocessing data in B2B setting, and an IoT device requesting data to do\nits work. Exposing web APIs lets external applications or devices\ncommunicate with yours.\n\nBecause Liferay DXP contains so many apps and features, it's prudent for\nLiferay to let developers access those apps and features from external\napps and devices by exposing their APIs. Additionally, Liferay's\ndevelopment platform makes it easy to extend them and create new ones.\n\nThere are three different approaches for clients to connect to Liferay\nDXP's web APIs:\n\n\\textbf{Headless REST APIs:} You can consume RESTful web services\nindependent of Liferay DXP's front end (hence \\emph{headless}). These\nAPIs conform to the\n\\href{https://swagger.io/docs/specification/about/}{OpenAPI}\nspecification. This is the modern, preferred way to work with web\nservices in Liferay DXP.\n\n\\textbf{GraphQL:} All the power of doing multiple queries in a unique\nrequest following\n\\href{https://graphql.github.io/graphql-spec/June2018/}{GraphQL\nspecification}.\n\n\\textbf{Plain Web/REST Services:} This is the old way to build and\nconsume web services in Liferay DXP, but is still supported.\n\nYou can also create your own Headless REST and GraphQL APIs through the\n\\textbf{REST builder}.\n\n\\chapter{Headless REST APIs}\\label{headless-rest-apis}\n\nLiferay DXP's headless REST APIs follow the\n\\href{https://swagger.io/docs/specification/about/}{OpenAPI}\nspecification and let your apps consume RESTful web services. What's\nmore, you can consume these APIs without being tied to Liferay DXP's UI\n(hence the term \\emph{headless}). This gives you a great deal of freedom\nwhen designing and developing your apps.\n\nThe articles in this section show you how to navigate and consume\nLiferay DXP's headless REST APIs. But first, you'll learn the design\napproach for these APIs.\n\n\\section{OpenAPI}\\label{openapi}\n\n\\href{https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md}{OpenAPI}\n(originally called Swagger) is a Linux Foundation project specification\nthat defines machine-readable files that describe REST APIs and how to\nconsume them.\n\nOpenAPI has become a widely adopted standard for defining REST APIs and\nis supported by major players in the API ecosystem such as Google,\nAmazon, and Microsoft. As a spec, it is language-agnostic, and many\nlibraries implement it or provide code generation to help validate,\nconsume, or produce APIs.\n\nLiferay DXP leverages existing knowledge of OpenAPI to define, create\nand consume REST APIs.\n\n\\section{API Vocabulary}\\label{api-vocabulary}\n\nWhen defining an API, the developer must decide how to expose the\nrepresentation of its resources. This determines its ease of use and how\nit can evolve. Traditionally, there are two approaches:\n\n\\textbf{Contract Last:} The code is written first and features are\nexposed as web or REST services. This approach is typically easier for\ndevelopers, as they must only implement and expose the business logic.\nService Builder is an example of this.\n\n\\textbf{Contract First:} The structure for client-server messages is\nwritten before the code that implements the services. Such messages are\ndefined independent of the code. This avoids tight coupling and is less\nlikely to break clients as APIs evolve.\n\nLiferay DXP's headless web APIs use a mixture of both approaches. An\nOpenAPI profile uses a contract first approach by defining the paths and\nschemas before writing any code. It then generates an API automatically\nbased on that profile, using the contract-last characteristic of code\ngeneration (like Service Builder). This allows fast development for\ndevelopers.\n\nThis mixed approach delivers the best of both worlds, allowing a step of\nconscious API design and then simplifying the developer experience by\nexposing only the business logic to implement.\n\nWhen writing the OpenAPI profile, the main focus should be on defining\nhow client-server messages represent the APIs' resources. In other\nwords, the APIs' schemas are defined first and the attributes,\nresources, and operations are named to clearly define what they\nrepresent and how they should be used.\n\n\\chapter{Get Started: Find the API}\\label{get-started-find-the-api}\n\nTo begin consuming web services, you must first know where they are\n(e.g., a service catalog), what operations you can invoke, and how to\ninvoke them. Because Liferay DXP's headless REST APIs leverage\n\\href{https://en.wikipedia.org/wiki/OpenAPI_Specification}{OpenAPI}\n(originally known as Swagger), you don't need a service catalog. You\nonly need to know the OpenAPI profile from which to discover the rest of\nthe API.\n\nLiferay DXP's headless APIs are available in SwaggerHub at\n\\href{https://app.swaggerhub.com/organizations/liferayinc}{\\texttt{https://app.swaggerhub.com/organizations/liferayinc}}.\nEach API has its own URL in SwaggerHub. For example, you can access the\ndelivery API definition at\n\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0}{\\texttt{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0}}.\n\nEach OpenAPI profile is also deployed dynamically in your portal\ninstance under this schema:\n\n\\begin{verbatim}\nhttp://[host]:[port]/o/[insert-headless-api]/[version]/openapi.yaml\n\\end{verbatim}\n\nFor example, if you're running Liferay DXP locally on port\n\\texttt{8080}, the home URL for discovering the headless delivery API\nis:\n\n\\begin{verbatim}\nhttp://localhost:8080/o/headless-delivery/v1.0/openapi.yaml\n\\end{verbatim}\n\nYou must be logged in to access this URL, or use basic authentication\nand a browser or other tool like\n\\href{https://www.getpostman.com}{Postman},\n\\href{https://install.advancedrestclient.com/install}{Advanced REST\nClient}, or even the \\texttt{curl} command in your system console.\n\nFor simplicity, the examples in this documentation use the \\texttt{curl}\ncommand and send requests to a Liferay DXP instance running locally on\nport \\texttt{8080}.\n\nRun this \\texttt{curl} command to access the home URL:\n\n\\begin{verbatim}\ncurl http://localhost:8080/o/headless-delivery/v1.0/openapi.yaml -u test@example.com:test\n\\end{verbatim}\n\nYou should get a response like this:\n\n\\begin{verbatim}\nopenapi: 3.0.1\ninfo:\n  title: Headless Delivery\n  version: v1.0\npaths:\n  /v1.0/blog-posting-images/{blogPostingImageId}:\n    get:\n      tags:\n      - BlogPostingImage\n      operationId: getBlogPostingImage\n      parameters:\n      - name: blogPostingImageId\n        in: path\n        required: true\n        schema:\n          type: integer\n          format: int64\n      responses:\n        default:\n          description: default response\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/BlogPostingImage'\n(...)\n\\end{verbatim}\n\nThis response follows the OpenAPI version 3.0 syntax to specify the\nendpoints (URLs) of the API and schemas returned. You can also open the\nOpenAPI profile in an OpenAPI editor like the\n\\href{https://editor.swagger.io}{Swagger Editor}. You can use this\neditor to inspect the documentation and parameters and make requests to\nthe API.\n\nThere are also many other tools that support OpenAPI, such as client\ngenerators, validators, parsers, and more. See\n\\href{https://openapi.tools/}{OpenAPI.Tools} for a comprehensive list.\nLeveraging OpenAPI provides standards support, extensive\n\\href{https://swagger.io/docs/}{documentation}, and industry-wide\nconventions.\n\n\\section{Related Topics}\\label{related-topics-124}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/get-started-invoke-a-service}{Get\nStarted: Invoke a Service}\n\n\\chapter{How To Invoke a Service}\\label{how-to-invoke-a-service}\n\nOnce you know which API you want to call via the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/get-started-discover-the-api}{OpenAPI\nprofile}, you can send a request to that resource's URL. For example,\nsuppose you want to retrieve all the blog entries from a Site. If you\nconsult the OpenAPI profile for Liferay DXP's delivery API, you can find\nthis endpoint:\n\n\\begin{verbatim}\n\"/sites/{siteId}/blog-postings\":\n        get:\n            operationId: getSiteBlogPostingsPage\n            parameters:\n                - in: path\n                  name: siteId\n                  required: true\n                  schema:\n                      format: int64\n                      type: integer\n                - in: query\n                  name: filter\n                  schema:\n                      type: string\n                - in: query\n                  name: page\n                  schema:\n                      type: integer\n                - in: query\n                  name: pageSize\n                  schema:\n                      type: integer\n                - in: query\n                  name: search\n                  schema:\n                      type: string\n                - in: query\n                  name: sort\n                  schema:\n                      type: string\n            responses:\n                200:\n                    content:\n                        application/json:\n                            schema:\n                                items:\n                                    $ref: \"#/components/schemas/BlogPosting\"\n                                type: array\n                    description: \"\"\n            tags: [\"BlogPosting\"]\n\\end{verbatim}\n\nThe only required parameter is \\texttt{siteId}, the ID of the blog\npostings' Site. Internally, the \\texttt{siteId} is a \\texttt{groupId}\nthat you can retrieve from the database, a URL, or Liferay DXP's UI via\nthe Site Administration menu. The following GET request gets the site's\nblog postings by providing the site ID (\\texttt{20124}) in the URL:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/blog-postings/\" -u 'test@example.com:test'\n\\end{verbatim}\n\nIf you send such a request to a site that contains some blog entries,\nthe response should look like this:\n\n\\begin{verbatim}\n{\n  \"items\": [\n    {\n      \"alternativeHeadline\": \"The power of OpenAPI & Liferay\",\n      \"articleBody\": \"<p>We are happy to announce...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T07:04:47Z\",\n      \"dateModified\": \"2019-04-22T07:04:51Z\",\n      \"datePublished\": \"2019-04-22T07:02:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"new-headless-apis\",\n      \"headline\": \"New Headless APIs\",\n      \"id\": 59301,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 1\n}\n\\end{verbatim}\n\nThis response is a JSON object with information about the collection of\nblogs. The response's attributes contain information about the resource\n(blogs, in this case). Also note that the results are paginated. The\n\\texttt{*page*} attributes refer to pages of results. Here's a\ndescription of some common attributes:\n\n\\texttt{id}: Each item has an ID. You can use the ID to retrieve more\ninformation about that item. For example, there are two \\texttt{id}\nattributes in the above response: one for the blog posting\n(\\texttt{59301}) and one for the blog post's creator (\\texttt{20130}).\n\n\\texttt{lastPage}: The page number of the final page of results. The\nabove response only contains a single page, so its last page is\n\\texttt{1}.\n\n\\texttt{page}: The page number of the current page. The page in the\nabove response is \\texttt{1}.\n\n\\texttt{pageSize}: The possible number of this resource's items to be\nincluded in a single page. In the above response this is \\texttt{20}.\n\n\\texttt{totalCount}: The total number of this resource's existing items\n(independent of pagination). The above response lists the total number\nof blog postings (\\texttt{1}) in a Site.\n\nTo get information on a specific blog posting, send a GET request to the\n\\texttt{blogPostingId} resource's URL with the blog posting's ID\n(\\texttt{/blog-postings/\\{blogPostingId\\}}). For example, the URL for\nsuch a request to the blog posting in the above response is\n\\texttt{/blog-postings/59301}. Here's an example response:\n\n\\begin{verbatim}\n{\n  \"alternativeHeadline\": \"The power of OpenAPI & Liferay\",\n  \"articleBody\": \"<p>We are happy to announce...</p>\",\n  \"creator\": {\n    \"familyName\": \"Test\",\n    \"givenName\": \"Test\",\n    \"id\": 20130,\n    \"name\": \"Test Test\",\n    \"profileURL\": \"/web/test\"\n  },\n  \"dateCreated\": \"2019-04-22T07:04:47Z\",\n  \"dateModified\": \"2019-04-22T07:04:51Z\",\n  \"datePublished\": \"2019-04-22T07:02:00Z\",\n  \"encodingFormat\": \"text/html\",\n  \"friendlyUrlPath\": \"new-headless-apis\",\n  \"headline\": \"New Headless APIs\",\n  \"id\": 59301,\n  \"numberOfComments\": 0,\n  \"siteId\": 20124\n}\n\\end{verbatim}\n\nAlthough this response is JSON, the API's consumer can select other\nformats to use (like XML). For more information, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation}{API\nFormats and Content Negotiation}.\n\n\\section{Related Topics}\\label{related-topics-125}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/get-started-discover-the-api}{Get\nStarted: Discover the API}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation}{API\nFormats and Content Negotiation}\n\n\\chapter{Making Authenticated\nRequests}\\label{making-authenticated-requests}\n\nTo make an authenticated request, you must authenticate as a specific\nUser.\n\nThere are three authentication mechanisms available when invoking web\nAPIs:\n\n\\textbf{Basic Authentication:} Sends the user credentials as an encoded\nuser name and password pair. This is the simplest authentication\nprotocol (available since HTTP/1.0).\n\n\\textbf{OAuth 2.0:} In 7.0, you can use OAuth 2.0 for authentication.\nSee the \\href{/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0}{OAuth 2.0\ndocumentation} for more information.\n\n\\textbf{Cookie/Session authentication:} From inside the portal you can\nmake direct requests to the APIs by sending the session token.\n\nFirst, you'll learn how send requests with basic authentication.\n\n\\section{Basic Authentication}\\label{basic-authentication}\n\nBasic authentication requires that you send an HTTP\n\\texttt{Authorization} header containing the encoded user name and\npassword. You must first get that encoded value. To do so, you can use\n\\texttt{openssl} or a \\texttt{Base64} encoder. Either way, you must\nencode the \\texttt{user:password} string. Here's an example of the\n\\texttt{openssl} command for encoding the \\texttt{user:password} string\nfor a user \\texttt{test@example.com} with the password \\texttt{Liferay}:\n\n\\begin{verbatim}\nopenssl base64 <<< test@example.com:Liferay\n\\end{verbatim}\n\nThis returns the encoded value:\n\n\\begin{verbatim}\ndGVzdEBleGFtcGxlLmNvbTpMaWZlcmF5Cg==\n\\end{verbatim}\n\nIf you don't have \\texttt{openssl} installed, try the \\texttt{base64}\ncommand:\n\n\\begin{verbatim}\nbase64 <<< test@example.com:Liferay\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Encoding a string as shown here does not encrypt the\nresulting string. An encoded string can easily be decoded by executing\n\\texttt{base64\\ \\textless{}\\textless{}\\textless{}\\ the-encoded-string},\nwhich returns the original string.\n\nAnyone listening to your request could therefore decode the\n\\texttt{Authorization} header and reveal your user name and password. To\nprevent this, ensure that all communication is made through HTTPS, which\nencrypts the entire message (including headers).\n\n\\noindent\\hrulefill\n\nUse the encoded value for the HTTP Authorization header when sending the\nrequest:\n\n\\begin{verbatim}\ncurl -H \"Authorization: Basic dGVzdEBleGFtcGxlLmNvbTpMaWZlcmF5Cg==\" http://localhost:8080/o/headless-delivery/v1.0/sites/{siteId}/blog-postings/\n\\end{verbatim}\n\nThe response contains data instead of the 403 error that an\nunauthenticated request receives. For more information on the response's\nstructure, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data}{Working\nwith Collections of Data}.\n\n\\begin{verbatim}\n{\n  \"items\": [\n    {\n      \"alternativeHeadline\": \"The power of OpenAPI & Liferay\",\n      \"articleBody\": \"<p>We are happy to announce...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T07:04:47Z\",\n      \"dateModified\": \"2019-04-22T07:04:51Z\",\n      \"datePublished\": \"2019-04-22T07:02:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"new-headless-apis\",\n      \"headline\": \"New Headless APIs\",\n      \"id\": 59301,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    },\n    {\n      \"alternativeHeadline\": \"How to work with OAuth\",\n      \"articleBody\": \"<p>To configure OAuth...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T09:35:09Z\",\n      \"dateModified\": \"2019-04-22T09:35:09Z\",\n      \"datePublished\": \"2019-04-22T09:34:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"authenticated-requests\",\n      \"headline\": \"Authenticated requests\",\n      \"id\": 59309,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 2\n}\n\\end{verbatim}\n\n\\section{OAuth 2.0 Authorization}\\label{oauth-2.0-authorization}\n\n7.0 supports authorization via OAuth 2.0, which is a token-based\nauthorization mechanism. For more details, see\n\\href{/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0}{Liferay DXP's OAuth\n2.0 documentation}. The following sections show you how to use OAuth 2.0\nto authenticate web API requests.\n\n\\section{Obtaining the OAuth 2.0\nToken}\\label{obtaining-the-oauth-2.0-token}\n\nBefore using OAuth 2.0 to invoke a web API, you must register your\napplication (your web API's consumer) as an authorized OAuth client. To\ndo this, follow the instructions in the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0\\#creating-an-application}{Creating\nan Application} section of the OAuth 2.0 documentation. When creating\nthe application, fill in the form as follows:\n\n\\textbf{Application Name:} Your application's name.\n\n\\textbf{Client Profile:} Headless Server.\n\n\\textbf{Allowed Authorization Types:} Check \\emph{Client Credentials}.\n\nAfter clicking \\emph{Save} to finish creating the application, write\ndown the Client ID and Client Secret values that appear at the top of\nthe form.\n\nNext, you must get an OAuth 2.0 access token. To do this, see the\ntutorial\n\\href{/docs/7-2/deploy/-/knowledge_base/d/authorizing-account-access-with-oauth2}{Authorizing\nAccount Access with OAuth 2}.\n\n\\section{Invoking the Service with an OAuth 2.0\nToken}\\label{invoking-the-service-with-an-oauth-2.0-token}\n\nOnce you have a valid OAuth 2.0 token, include it in the request's\n\\texttt{Authorization} header, specifying that the authentication type\nis a \\href{https://tools.ietf.org/html/rfc6750}{bearer token}. For\nexample:\n\n\\begin{verbatim}\ncurl -H \"Authorization: Bearer d5571ff781dc555415c478872f0755c773fa159\" http://localhost:8080/o/headless-delivery/v1.0/sites/{siteId}/blog-postings/\n\\end{verbatim}\n\nThe response contains the resources that the authenticated user has\npermission to access, just like the response from Basic authentication.\n\n\\section{Using Cookie Authentication or Making Requests from the\nUI}\\label{using-cookie-authentication-or-making-requests-from-the-ui}\n\nYou can call the REST APIs using the existing session from outside\nLiferay DXP by passing the session identifier (the cookie reference) and\nthe Liferay Auth Token (a Cross-Site Request Forgery---CSRF---token).\n\nTo do a request from outside Liferay DXP you must provide the\n\\texttt{Cookie} identifier in the header. In CURL, pass the \\texttt{-H}\nparameter:\n\n\\begin{verbatim}\n -H 'Cookie: JSESSIONID=27D7C95648D7CDBE3347601FC4543F5D'\n\\end{verbatim}\n\nYou must also provide the CSRF token by passing it in the\n\\texttt{p\\_auth} query parameter, or by adding the URL to the whitelist\nof CSRF allowed URLs or disabling CSRF checks altogether with the\n\\texttt{auth.verifier.auth.verifier.PortalSessionAuthVerifier.check.csrf.token}\nproperty (application level).\n\nHere's a sample CURL request with the cookie and CSRF token:\n\n\\begin{verbatim}\ncurl -H 'Cookie: JSESSIONID=27D7C95648D7CDBE3347601FC4543F5D' http://localhost:8080/o/headless-delivery/v1.0/sites/{siteId}/blog-postings/?p_auth=O4dCU1Mj\n\\end{verbatim}\n\nTo do a cookie request from inside Liferay DXP, from JavaScript code or\na Java method, the session identifier is not needed and you must only\nprovide the CRSF token or add the API to the whitelist of CSRF allowed\nURLs.\n\n\\section{Making Unauthenticated\nRequests}\\label{making-unauthenticated-requests}\n\nUnauthenticated requests are disabled by default in Liferay DXP's\nheadless REST APIs. You can, however, enable them manually by defining\nan exception in the Service Access Policy to allow unauthenticated\nrequests.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Control Panel → Configuration → Service Access Policy.\n\\item\n  Add a new Service Access Policy.\n\\item\n  Enable both \\emph{Enabled} and \\emph{Default} options.\n\\item\n  Use\n  \\texttt{com.liferay.headless.delivery.internal.resource.v1\\_0.OpenAPIResourceImpl}\n  for the Service Class and \\texttt{getOpenAPI} for the Method Name (or\n  the method/class you want to expose).\n\\item\n  Test the APIs by making a request to an OpenAPI profile URL:\n\\end{enumerate}\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/openapi.yaml\"\n\\end{verbatim}\n\nYou should get the OpenAPI profile for the API you sent the request to.\n\n\\section{Cross-Origin Resource Sharing\n(CORS)}\\label{cross-origin-resource-sharing-cors}\n\nModern web browsers block access to content from domains other than the\none currently being visited. For example, browsers block fetch/ajax\nrequests from a local JavaScript application (being executed in\nlocalhost:4000) that tries to access a Tomcat server (running in\nlocalhost:8080).\n\nCross Origin Resource Sharing allows the configuration of safe resource\nsharing between sites. A web application using APIs can only request\nendpoints that have the same origin/domain unless some special CORS\nheaders are set that explicitly allow querying from different domains.\n\nFor development purposes, it's common to enable CORS headers to allow\nscripts to call APIs served by a different server.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/cors.png}\n\\caption{Configure Cross-Origin Resource Sharing in Liferay}\n\\end{figure}\n\nFollow these instructions to configure\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-cors}{Cross-Origin\nResource Sharing (CORS)} in Liferay DXP.\n\n\\section{Related Topics}\\label{related-topics-126}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/get-started-invoke-a-service}{Get\nStarted: Invoke a Service}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data}{Working\nwith Collections of Data}\n\n\\chapter{Working with Collections of\nData}\\label{working-with-collections-of-data}\n\nCollection resources are common in Liferay DXP web APIs. If you followed\nalong with the previous examples that sent requests to the portal's\n\\texttt{blog-postings} resource URL, you've already seen collections in\naction: the \\texttt{BlogPosting} resource is a collection.\n\nHere, you'll learn more detailed information about working with\ncollection resources. But first you should learn about how collections\nare returned in pages.\n\n\\section{Pagination}\\label{pagination}\n\nA small collection can be transmitted in a single response without\ndifficulty. Transmitting a large collection all at once, however, can\nconsume too much bandwidth, time, and memory. It can also overwhelm the\nuser with too much data. It's therefore best to get and display the\nelements of a large collection in discrete chunks, or pages.\n\nLiferay DXP's headless REST APIs return paginated collections by\ndefault. The following attributes in the responses also contain the\ninformation needed to navigate between those pages:\n\n\\texttt{totalCount}: The total number of this resource's items.\n\n\\texttt{pageSize}: The number of this resource's items to be included in\nthis response.\n\n\\texttt{page}: The current page's number.\n\n\\texttt{lastPage}: The last page's number.\n\n\\texttt{items}: The collection elements present in this page. Each\nelement also contains the data of the object it represents, so there's\nno need for additional requests for individual elements.\n\n\\texttt{id}: Each item's identifier. You can use this, if necessary, to\nget more information on a specific item.\n\nFor examples of working with collection pages, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/pagination}{Pagination}.\n\n\\chapter{Getting Collections}\\label{getting-collections}\n\nRequests for collection resources are the same as those for\nnon-collection resources. For example, an\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/making-authenticated-requests}{authenticated\nrequest} to the \\texttt{UserAccount} endpoint returns a collection\ncontaining the portal's users. When sending this request, use the\ncredentials of an administrative user who has permission to view other\nportal users:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-admin-user/v1.0/user-accounts\"  -u 'test@example.com:test'\n\\end{verbatim}\n\nThe response (below) has two main parts:\n\n\\begin{itemize}\n\\item\n  The list of collection elements, inside the \\texttt{items} attribute.\n  This example contains data on two users: an administrator (Test), and\n  a user named Javier Gamarra.\n\\item\n  A set of metadata about the collection. This is the rest of the data\n  in the response. This lets clients know how to use the collection.\n\\end{itemize}\n\nThis response is in JSON, which is the default response format for web\nAPIs in Liferay DXP. For information on specifying other response\nformats, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation}{API\nFormats and Content Negotiation}.\n\n\\begin{verbatim}\n{\n  \"items\": [\n    {\n      \"alternateName\": \"test\",\n      \"birthDate\": \"1970-01-01T00:00:00Z\",\n      \"contactInformation\": {},\n      \"dashboardURL\": \"/user/test\",\n      \"dateCreated\": \"2019-04-17T20:37:19Z\",\n      \"dateModified\": \"2019-04-22T09:56:35Z\",\n      \"emailAddress\": \"test@example.com\",\n      \"familyName\": \"Test\",\n      \"givenName\": \"Test\",\n      \"id\": 20130,\n      \"name\": \"Test Test\",\n      \"profileURL\": \"/web/test\",\n      ...\n    },\n    {\n      \"alternateName\": \"nhpatt\",\n      \"birthDate\": \"1970-01-01T00:00:00Z\",\n      \"contactInformation\": {},\n      \"dateCreated\": \"2019-04-22T10:38:36Z\",\n      \"dateModified\": \"2019-04-22T10:38:37Z\",\n      \"emailAddress\": \"nhpatt@gmail.com\",\n      \"familyName\": \"Gamarra\",\n      \"givenName\": \"Javier\",\n      \"id\": 59347,\n      \"name\": \"Javier Gamarra\",\n      ...\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 2\n}\n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-127}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/pagination}{Pagination}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/making-authenticated-requests}{Making\nAuthenticated Requests}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation}{API\nFormats and Content Negotiation}\n\n\\chapter{Pagination}\\label{pagination-1}\n\nCollection resources are returned in pages of information.\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data}{Working\nwith Collections of Data} explains this in more detail. Here, you'll\nlearn how to work with collection pages.\n\nFor example, suppose that there are 123 users your portal and you want\nto get information on them. To do this, send an\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/making-authenticated-requests}{authenticated\nrequest} to the UserAccount URL:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-admin-user/v1.0/user-accounts\"  -u 'test@example.com:test'\n\\end{verbatim}\n\nThe response contains the first 30 users and IDs for navigating the rest\nof the collection. Note that most of the contents of the \\texttt{items}\nattribute, which contains the users, are omitted here so you can focus\non the metadata for navigating the collection:\n\n\\begin{verbatim}\n{\n  \"items\": [\n    {\n      \"id\": 20130,\n      ...\n    },\n    {\n      \"id\": 59347,\n      ...\n    }\n  ],\n  \"lastPage\": 5,\n  \"page\": 1,\n  \"pageSize\": 30,\n  \"totalCount\": 123\n}\n\\end{verbatim}\n\nThe attributes \\texttt{page} and \\texttt{pageSize} allow client\napplications to navigate through the results. For example, such a client\ncould send a request for a specific page. This example gets the second\npage (\\texttt{?page=2}) of documents that exist on the site with the ID\n\\texttt{20124}:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/documents?page=2\"  -u 'test@example.com:test'\n\\end{verbatim}\n\nSimilarly, you can customize the number of elements per page via the\noptional parameter \\texttt{pageSize} (e.g., \\texttt{?pageSize=20}).\n\n\\section{Related Topics}\\label{related-topics-128}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data}{Working\nwith Collections of Data}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/making-authenticated-requests}{Making\nAuthenticated Requests}\n\n\\chapter{Navigating from a Collection to its\nElements}\\label{navigating-from-a-collection-to-its-elements}\n\nWhen you\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-collections}{get a\ncollection}, you can use the response to get an element of that\ncollection. Follow these steps to do so:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get a collection. This example gets a list of users by sending\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/making-authenticated-requests}{an\n  authenticated request} to the \\texttt{user-accounts} collection:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-admin-user/v1.0/user-accounts\"  -u 'test@example.com:test'\n\\end{verbatim}\n\n  Recall from\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-collections}{Getting\n  Collections} that the response's \\texttt{items} attribute contains the\n  collection elements. In this case, the collection contains two users:\n  Test Test and Javier Gamarra:\n\n  \\texttt{json\\ \\{\\ \\ \\ \\ \\ \"totalItems\":\\ 2,\\ \\ \\ \\ \\ \"numberOfItems\":\\ 2,\\ \\ \\ \\ \\ \"view\":\\ \\{\\ \\ \\ \\ \\ \\ \\ \\ \\ \\{\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"items\":\\ {[}\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\{\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"alternateName\":\\ \"test\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"birthDate\":\\ \"1970-01-01T00:00:00Z\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"contactInformation\":\\ \\{\\},\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"dashboardURL\":\\ \"/user/test\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"dateCreated\":\\ \"2019-04-17T20:37:19Z\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"dateModified\":\\ \"2019-04-22T09:56:35Z\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"emailAddress\":\\ \"test@example.com\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"familyName\":\\ \"Test\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"givenName\":\\ \"Test\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"id\":\\ 20130,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"name\":\\ \"Test\\ Test\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"profileURL\":\\ \"/web/test\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"roleBriefs\":\\ {[}\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\{\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"id\":\\ 20108,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"name\":\\ \"Administrator\"\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\},\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\{\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"id\":\\ 20111,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"name\":\\ \"Power\\ User\"\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\},\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\{\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"id\":\\ 20112,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"name\":\\ \"User\"\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\}\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ {]},\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"siteBriefs\":\\ {[}\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\{\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"id\":\\ 20128,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"name\":\\ \"Global\"\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\},\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\{\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"id\":\\ 20124,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"name\":\\ \"Guest\"\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\}\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ {]}\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\},\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\{\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"alternateName\":\\ \"nhpatt\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"birthDate\":\\ \"1970-01-01T00:00:00Z\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"contactInformation\":\\ \\{\\},\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"dateCreated\":\\ \"2019-04-22T10:38:36Z\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"dateModified\":\\ \"2019-04-22T10:38:37Z\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"emailAddress\":\\ \"nhpatt@gmail.com\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"familyName\":\\ \"Gamarra\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"givenName\":\\ \"Javier\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"id\":\\ 59347,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"name\":\\ \"Javier\\ Gamarra\",\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"roleBriefs\":\\ {[}\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\{\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"id\":\\ 20112,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"name\":\\ \"User\"\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\}\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ {]},\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"siteBriefs\":\\ {[}\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\{\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"id\":\\ 20128,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"name\":\\ \"Global\"\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\},\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\{\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"id\":\\ 20124,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"name\":\\ \"Guest\"\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\}\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ {]}\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\}\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ {]},\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"lastPage\":\\ 1,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"page\":\\ 1,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"pageSize\":\\ 20,\\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \"totalCount\":\\ 2\\ \\ \\ \\ \\ \\ \\ \\ \\ \\}}\n\\item\n  In the response, locate the ID of the element you want and look in the\n  OpenAPI profile for the appropriate GET item endpoint. For example,\n  the \\texttt{user-accounts} GET item endpoint is\n  \\texttt{/user-accounts/\\{userAccountId\\}}.\n\\item\n  Send a GET request to that endpoint. For example, this request gets\n  information for the user with the ID \\texttt{59347} (Javier Gamarra):\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-admin-user/v1.0/user-accounts/59347\"  -u 'test@example.com:test'\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Related Topics}\\label{related-topics-129}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/getting-collections}{Getting\nCollections}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/pagination}{Pagination}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/making-authenticated-requests}{Making\nAuthenticated Requests}\n\n\\chapter{API Formats and Content\nNegotiation}\\label{api-formats-and-content-negotiation}\n\nThe responses in the preceding examples use a standard JSON format,\nwhich is the default response format for Liferay DXP's headless REST\nAPIs. You can also use other formats like XML. Formats typically differ\nin the resource metadata's structure or semantics. There's no best\nformat; use the one that best fits your use case.\n\nYou use \\emph{content negotiation} to specify different formats for use.\nContent negotiation is how the client and server establish the format\nthey use to exchange messages. The client tells the server its preferred\nformat via the HTTP headers \\texttt{Accept} and \\texttt{Content-Type}.\nEach format has a string identifier (its MIME type) that you can use in\nthe HTTP headers to specify the format. The following table lists the\nMIME type for each supported format.\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.2812}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.7188}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nAPI Format\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n~MIME Type\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\napplication/json &\n\\href{https://www.iana.org/assignments/media-types/application/json}{application/json} \\\\\napplication/xml &\n\\href{https://www.iana.org/assignments/media-types/application/xml}{application/xml} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nWhen you send a request without specifying the API format, the server\nresponds with the default JSON. For example, here's such a request for a\nlist of folders from the Site with the ID \\texttt{20124}:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/document-folders\" -u 'test@example.com:test'\n\\end{verbatim}\n\n\\begin{verbatim}\n{\n  \"items\": [\n    {\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T10:21:20Z\",\n      \"dateModified\": \"2019-04-22T10:21:20Z\",\n      \"id\": 59319,\n      \"name\": \"REST APIs Documentation\",\n      \"numberOfDocumentFolders\": 0,\n      \"numberOfDocuments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 1\n}\n\\end{verbatim}\n\nIf you request the headers, the \\texttt{Content-Type} response attribute\nlists the content type's format (JSON, in this case):\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/document-folders\" -u 'test@example.com:test' --head\n\\end{verbatim}\n\n\\begin{verbatim}\nHTTP/1.1 200 \nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nX-XSS-Protection: 1\nSet-Cookie: JSESSIONID=9F61AEB8721DD9149BD577ECBC31AE3F; Path=/; HttpOnly\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nCache-Control: private, no-cache, no-store, must-revalidate\nPragma: no-cache\nSet-Cookie: COOKIE_SUPPORT=true; Max-Age=31536000; Expires=Tue, 21-Apr-2020 10:23:57 GMT; Path=/; HttpOnly\nSet-Cookie: GUEST_LANGUAGE_ID=en_US; Max-Age=31536000; Expires=Tue, 21-Apr-2020 10:23:57 GMT; Path=/; HttpOnly\nDate: Mon, 22 Apr 2019 10:23:57 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\n\\end{verbatim}\n\nTo get the response in XML instead, specify \\texttt{application/xml} in\nthe request's \\texttt{Accept} header. Note that the XML response\nincludes the same information as JSON, but is structured differently:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/documents/59203\"  -H 'Accept: application/xml'  -u 'test@example.com:test'\n\\end{verbatim}\n\n\\begin{verbatim}\n    <Page>\n        <items>\n            <items>\n                <creator>\n                    <familyName>Test</familyName>\n                    <givenName>Test</givenName>\n                    <id>20130</id>\n                    <name>Test Test</name>\n                    <profileURL>/web/test</profileURL>\n                    </creator>\n                <dateCreated>2019-04-22T10:21:20Z</dateCreated>\n                <dateModified>2019-04-22T10:21:20Z</dateModified>\n                <id>59319</id>\n                <name>REST APIs Documentation</name>\n                <numberOfDocumentFolders>0</numberOfDocumentFolders>\n                <numberOfDocuments>0</numberOfDocuments>\n                <siteId>20124</siteId>\n            </items>\n        </items>\n        <lastPage>1</lastPage>\n        <page>1</page>\n        <pageSize>20</pageSize>\n        <totalCount>1</totalCount>\n    </Page>\n\\end{verbatim}\n\nRequesting the headers, you can see that the response is in XML\n(\\texttt{application/xml}):\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/documents/59203\"  -H 'Accept: application/xml'  -u 'test@example.com:test' --head\n\\end{verbatim}\n\n\\begin{verbatim}\nHTTP/1.1 200 \nX-Content-Type-Options: nosniff\nX-Frame-Options: SAMEORIGIN\nX-XSS-Protection: 1\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nCache-Control: private, no-cache, no-store, must-revalidate\nPragma: no-cache\nDate: Mon, 22 Apr 2019 10:26:21 GMT\nContent-Type: application/xml\nTransfer-Encoding: chunked\n\\end{verbatim}\n\n\\section{Language Negotiation}\\label{language-negotiation}\n\nThe same mechanism used for requesting another response format (content\nnegotiation) is used for requesting content in another language.\n\nAPIs that are available in different languages return the options in a\nblock called \\texttt{availableLanguages}. For example, this block in the\nfollowing response lists U.S. English (\\texttt{en-US}) and\nSpain/Castilian Spanish (\\texttt{es-ES}):\n\n\\begin{verbatim}\n{\n  \"availableLanguages\": [\n    \"en-US\",\n    \"es-ES\"\n  ],\n  \"contentFields\": [\n    {\n      \"dataType\": \"html\",\n      \"name\": \"content\",\n      \"repeatable\": false,\n      \"value\": {\n        \"data\": \"<p>The main reason is because Headless APIs have been designed with real use cases in mind...</p>\"\n      }\n    }\n  ],\n  \"contentStructureId\": 36801,\n  \"creator\": {\n    \"familyName\": \"Test\",\n    \"givenName\": \"Test\",\n    \"id\": 20130,\n    \"name\": \"Test Test\",\n    \"profileURL\": \"/web/test\"\n  },\n  \"dateCreated\": \"2019-04-22T10:29:40Z\",\n  \"dateModified\": \"2019-04-22T10:30:31Z\",\n  \"datePublished\": \"2019-04-22T10:28:00Z\",\n  \"friendlyUrlPath\": \"why-headless-apis-are-better-than-json-ws-services-\",\n  \"id\": 59325,\n  \"key\": \"59323\",\n  \"numberOfComments\": 0,\n  \"renderedContents\": [\n    {\n      \"renderedContentURL\": \"http://localhost:8080/o/headless-delivery/v1.0/structured-contents/59325/rendered-content/36804\",\n      \"templateName\": \"Basic Web Content\"\n    }\n  ],\n  \"siteId\": 20124,\n  \"title\": \"Why Headless APIs are better than JSON-WS services?\",\n  \"uuid\": \"e1c4c152-e47c-313f-2d16-2ee4eba5cd26\"\n}\n\\end{verbatim}\n\nTo request the content in another language, specify your desired locale\nin the request's \\texttt{Accept-Language} header:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/structured-contents/59325\"  -H 'Accept-Language: es-ES'  -u 'test@example.com:test'\n\\end{verbatim}\n\n\\begin{verbatim}\n    {\n      \"availableLanguages\": [\n        \"en-US\",\n        \"es-ES\"\n      ],\n      \"contentFields\": [\n        {\n          \"dataType\": \"html\",\n          \"name\": \"content\",\n          \"repeatable\": false,\n          \"value\": {\n            \"data\": \"<p>La principal razón es porque las APIs Headless se han diseñado pensando en casos de uso reales...</p>\"\n          }\n        }\n      ],\n      \"contentStructureId\": 36801,\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T10:29:40Z\",\n      \"dateModified\": \"2019-04-22T10:30:31Z\",\n      \"datePublished\": \"2019-04-22T10:28:00Z\",\n      \"friendlyUrlPath\": \"%C2%BFpor-qu%C3%A9-las-apis-headless-son-mejores-que-json-ws-\",\n      \"id\": 59325,\n      \"key\": \"59323\",\n      \"numberOfComments\": 0,\n      \"renderedContents\": [\n        {\n          \"renderedContentURL\": \"http://localhost:8080/o/headless-delivery/v1.0/structured-contents/59325/rendered-content/36804\",\n          \"templateName\": \"Contenido web básico\"\n        }\n      ],\n      \"siteId\": 20124,\n      \"title\": \"¿Por qué las APIs Headless son mejores que JSON-WS?\",\n      \"uuid\": \"e1c4c152-e47c-313f-2d16-2ee4eba5cd26\"\n    }\n\\end{verbatim}\n\n\\section{Creating Content with Different\nLanguages}\\label{creating-content-with-different-languages}\n\nBy default, when sending a POST/PUT request, the\n\\texttt{Accept-Language} header is used as the content's language.\nHowever, there is one exception. Some entities require the first POST to\nbe in the Site's default language. In such cases, a POST request for a\ndifferent language results in an error.\n\nAfter creating a new resource, PUT requests in a different language adds\nthat translation. PATCH requests return an error (you are expected to\nupdate, not create, in a PATCH request).\n\n\\section{Related Topics}\\label{related-topics-130}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/get-started-discover-the-api}{Get\nStarted: Discover the API}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/get-started-invoke-a-service}{Get\nStarted: Invoke a Service}\n\n\\chapter{OpenAPI Profiles}\\label{openapi-profiles}\n\nAll the APIs exposed by Liferay DXP are available under the\n\\href{https://app.swaggerhub.com/organizations/liferayinc}{liferayinc\nSwaggerHub organization}.\n\nLiferay DXP's headless APIs are categorized in two different use cases:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Delivering content (delivery APIs)\n\\item\n  Managing and administering content (admin APIs)\n\\end{itemize}\n\nThe available APIs demonstrate this categorization.\n\n\\section{Headless Delivery}\\label{headless-delivery}\n\nThe following table lists the APIs that\n\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0}{Headless\nDelivery} contains. Note that the second column shows which internal\nmodel in Liferay DXP that the API maps to.\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.2812}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.7188}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nAPI\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n~Internal Model\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{BlogPosting} & \\texttt{BlogsEntry} \\\\\n\\texttt{BlogPostingImage} & \\texttt{DLFileEntry} (associated with a\n\\texttt{BlogsEntry}) \\\\\n\\texttt{Comment} & \\texttt{DiscussionComment} \\\\\n\\texttt{ContentDocument} & \\texttt{DLFileEntry} (associated with a\n\\texttt{JournalArticle}) \\\\\n\\texttt{ContentSet} & \\texttt{AssetListEntry} \\\\\n\\texttt{ContentStructure} & \\texttt{DDMStructure} \\\\\n\\texttt{Document} & \\texttt{DLFileEntry} \\\\\n\\texttt{DocumentFolder} & \\texttt{Folder} \\\\\n\\texttt{KnowledgeBaseArticle} & \\texttt{KBArticle} \\\\\n\\texttt{KnowledgeBaseAttachment} & \\texttt{FileEntry} (associated with a\n\\texttt{KBArticle}) \\\\\n\\texttt{KnowledgeBaseFolder} & \\texttt{KBFolder} \\\\\n\\texttt{MessageBoardAttachment} & \\texttt{FileEntry} (associated with a\n\\texttt{MBMessage}) \\\\\n\\texttt{MessageBoardMessage} & \\texttt{MBMessage} \\\\\n\\texttt{MessageBoardSection} & \\texttt{MBCategory} \\\\\n\\texttt{MessageBoardThread} & \\texttt{MBThread} \\\\\n\\texttt{Rating} & \\texttt{RatingsEntry} \\\\\n\\texttt{StructuredContent} & \\texttt{JournalArticle} \\\\\n\\texttt{StructuredContentFolder} & \\texttt{JournalFolder} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Headless Administration}\\label{headless-administration}\n\nThere are several headless admin APIs, each containing its own set of\nAPIs. The following tables list these, as well as any internal models in\nLiferay DXP that each API maps to.\n\n\\href{https://app.swaggerhub.com/apis/liferayinc/headless-admin-user/1.0}{Headless\nAdmin User} contains the following APIs for retrieving and managing\ninformation about users and organizations.\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}ll@{}}\n\\toprule\\noalign{}\nAPI & ~Internal Model \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{EmailAddress} & N/A \\\\\n\\texttt{Organization} & N/A \\\\\n\\texttt{Phone} & N/A \\\\\n\\texttt{PostalAddress} & \\texttt{Address} \\\\\n\\texttt{Role} & N/A \\\\\n\\texttt{Segment} & \\texttt{SegmentEntry} \\\\\n\\texttt{SegmentUser} & N/A \\\\\n\\texttt{SiteBrief} & N/A \\\\\n\\texttt{UserAccount} & \\texttt{User} \\\\\n\\texttt{WebUrl} & \\texttt{WebSite} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\href{https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/1.0}{Headless\nAdmin Taxonomy} contains the following APIs for managing asset\ncategories, asset vocabularies, and asset tags.\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}ll@{}}\n\\toprule\\noalign{}\nAPI & ~Internal Model \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{Keyword} & \\texttt{AssetTag} \\\\\n\\texttt{TaxonomyCategory} & \\texttt{AssetCategory} \\\\\n\\texttt{TaxonomyVocabulary} & \\texttt{AssetVocabulary} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\href{https://app.swaggerhub.com/apis/liferayinc/headless-admin-workflow/1.0}{Headless\nAdmin Workflow} contains APIs for transitioning workflows.\n\n\\section{Related Topics}\\label{related-topics-131}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation}{API\nFormats and Content Negotiation}\n\n\\chapter{Filter, Sort, and Search}\\label{filter-sort-and-search}\n\nYou can use Liferay DXP's headless REST APIs to search for content\nyou're interested in. You can also sort and filter content. Here, you'll\nlearn how.\n\n\\section{Filter}\\label{filter}\n\nIt's often useful to filter large collections for the exact data that\nyou need. Not all collections, however, allow filtering. The ones that\nsupport it contain the optional parameter \\texttt{filter} in their\nOpenAPI profile. To filter a collection based on the value of one or\nmore fields, use the \\texttt{filter} parameter following a subset of the\n\\href{https://docs.oasis-open.org/odata/odata/v4.01/csprd06/part1-protocol/odata-v4.01-csprd06-part1-protocol.html\\#sec_BuiltinFilterOperations}{oData\nstandard}.\n\nFiltering mainly applies to fields indexed as keywords in Liferay DXP's\nsearch. To find content by terms contained in fields indexed as text,\nyou should instead use \\hyperref[search]{search}.\n\n\\section{Comparison Operators}\\label{comparison-operators}\n\n\\noindent\\hrulefill\n\nOperator \\textbar{} Description \\textbar{} Example \\textbar{}\n\\texttt{eq} \\textbar{} Equal \\textbar{}\n\\texttt{addressLocality\\ eq\\ \\textquotesingle{}Redmond\\textquotesingle{}}\n\\textbar{} \\textbar{} Equal null \\textbar{}\n\\texttt{addressLocality\\ eq\\ null} \\textbar{} \\texttt{ne} \\textbar{} Not\nequal \\textbar{}\n\\texttt{addressLocality\\ ne\\ \\textquotesingle{}London\\textquotesingle{}}\n\\textbar{} \\textbar{} Not null \\textbar{}\n\\texttt{addressLocality\\ ne\\ null} \\textbar{} \\texttt{gt} \\textbar{}\nGreater than \\textbar{} \\texttt{price\\ gt\\ 20} \\textbar{} \\texttt{ge}\n\\textbar{} Greater than or equal \\textbar{} \\texttt{price\\ ge\\ 10}\n\\textbar{} \\texttt{lt} \\textbar{} Less than \\textbar{}\n\\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} \\texttt{le}\n\\textbar{} Less than or equal \\textbar{}\n\\texttt{dateCreated\\ le\\ 2012-05-29T09:13:28Z} \\textbar{}\n\\texttt{startswith} \\textbar{} Starts with \\textbar{}\n\\texttt{startswith(addressLocality,\\ \\textquotesingle{}Lond\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{Logical Operators}\\label{logical-operators}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}lll@{}}\n\\toprule\\noalign{}\nOperator & Description & Example \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{and} & Logical and &\n\\texttt{price\\ le\\ 200\\ and\\ price\\ gt\\ 3.5} \\\\\n\\texttt{or} & Logical or &\n\\texttt{price\\ le\\ 3.5\\ or\\ price\\ gt\\ 200} \\\\\n\\texttt{not} & Logical not & \\texttt{not\\ (price\\ le\\ 3.5)} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nNote that the \\texttt{not} operator needs a space character after it.\n\n\\section{Grouping Operators}\\label{grouping-operators}\n\n\\noindent\\hrulefill\n\nOperator \\textbar{} Description \\textbar{} Example \\textbar{}\n\\texttt{(\\ )} \\textbar{} Precedence grouping \\textbar{}\n\\texttt{(price\\ eq\\ 5)\\ or\\ (addressLocality\\ eq\\ \\textquotesingle{}London\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{String Functions}\\label{string-functions}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}lll@{}}\n\\toprule\\noalign{}\nFunction & Description & Example \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{contains} & Contains &\n\\texttt{contains(title,\\textquotesingle{}edmon\\textquotesingle{})} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Lambda Operators}\\label{lambda-operators}\n\nLambda operators evaluate a boolean expression on a collection. They\nmust be prepended with a navigation path that identifies a collection.\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.2206}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.1618}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.6176}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nLambda Operator\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nExample\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{any} & Any &\n\\texttt{keywords/any(k:contains(k,\\textquotesingle{}substring1\\textquotesingle{}))} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nThe \\texttt{any} operator applies a boolean expression to each\ncollection element and evaluates to \\texttt{true} if the expression is\ntrue for any element.\n\n\\section{Operator combinations and OData\nsyntax}\\label{operator-combinations-and-odata-syntax}\n\nSyntax examples and other operator combinations are covered in the\n\\href{https://docs.oasis-open.org/odata/odata/v4.01/csprd06/part1-protocol/odata-v4.01-csprd06-part1-protocol.html\\#sec_BuiltinFilterOperations}{OData\nstandard reference}.\n\n\\section{Escaping in Queries}\\label{escaping-in-queries}\n\nYou can escape a single quote in a value by adding another single quote.\nFor example, to filter for a blog posting whose headline is\n\\texttt{New\\ Headless\\ APIs}, append this filter string to the request\nURL:\n\n\\begin{verbatim}\n?filter=headline eq 'New Headless APIs'\n\\end{verbatim}\n\nHere's an example of the full request:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/blog-postings/?filter=headline%20eq%20%27New%20Headless%20APIs%27\"  -u 'test@example.com:test'\n\\end{verbatim}\n\n\\begin{verbatim}\n{\n  \"items\": [\n    {\n      \"alternativeHeadline\": \"The power of OpenAPI & Liferay\",\n      \"articleBody\": \"<p>We are happy to announce...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T07:04:47Z\",\n      \"dateModified\": \"2019-04-22T07:04:51Z\",\n      \"datePublished\": \"2019-04-22T07:02:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"new-headless-apis\",\n      \"headline\": \"New Headless APIs\",\n      \"id\": 59301,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 1\n}\n\\end{verbatim}\n\n\\section{Filtering in Structured Content Fields\n(ContentField)}\\label{filtering-in-structured-content-fields-contentfield}\n\nTo filter for a \\texttt{ContentField} value (dynamic values created by\nthe end user), you must use the endpoints that are scoped to an\nindividual \\texttt{ContentStructure}. To do so, find the ID of the\n\\texttt{ContentStructure} and use it in place of\n\\texttt{\\{contentStructureId\\}} in this URL:\n\n\\begin{verbatim}\n\"/content-structures/{contentStructureId}/structured-contents\"\n\\end{verbatim}\n\n\\section{Search}\\label{search-2}\n\nIt's often useful to search large collections with keywords. Use search\nwhen you want results from any field, rather than specific ones. To\nperform a search, use the optional parameter \\texttt{search} followed by\nthe search terms. For example, this request searches for all the\n\\texttt{BlogEntry} fields containing OAuth:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/blog-postings/?search=OAuth\"  -u 'test@example.com:test'\n\\end{verbatim}\n\n\\begin{verbatim}\n{\n  \"items\": [\n    {\n      \"alternativeHeadline\": \"How to work with OAuth\",\n      \"articleBody\": \"<p>To configure OAuth...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T09:35:09Z\",\n      \"dateModified\": \"2019-04-22T09:35:09Z\",\n      \"datePublished\": \"2019-04-22T09:34:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"authenticated-requests\",\n      \"headline\": \"Authenticated requests\",\n      \"id\": 59309,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 1\n}\n\\end{verbatim}\n\n\\section{Sorting}\\label{sorting}\n\nSorting collection results is another common task. Note, however, that\nnot all collections allow sorting. The ones that support it contain the\noptional parameter \\texttt{\\{lb\\}?sort\\{rb\\}} in their OpenAPI profile.\n\nTo get sorted collection results, append\n\\texttt{?sort=\\textless{}param-name\\textgreater{}} to the request URL.\nFor example, appending \\texttt{?sort=title} to the request URL sorts the\nresults by title.\n\nThe default sort order is ascending (0-1, A-Z). To perform a descending\nsort, append \\texttt{:desc} to the parameter name. For example, to\nperform a descending sort by title, append \\texttt{?sort=title:desc} to\nthe request URL.\n\nTo sort by more than one parameter, separate the parameter names by\ncommas and put them in order of priority. For example, to sort first by\ntitle and then by creation date, append \\texttt{?sort=title,dateCreated}\nto the request URL.\n\nTo specify a descending sort for only one parameter, you must explicitly\nspecify ascending sort order (\\texttt{:asc}) for the other parameters.\nFor example:\n\n\\begin{verbatim}\n?sort=headline:desc,dateCreated:asc\n\\end{verbatim}\n\n\\section{Flatten}\\label{flatten}\n\nSome collections (as defined in their OpenAPI profile) allow the query\nparameter \\texttt{flatten}, which returns all resources and disregards\nfolders or other hierarchical classifications. This parameter's default\nvalue is \\texttt{false}, so a document query to the root folder returns\nonly the documents in that folder. With \\texttt{flatten} set to\n\\texttt{true}, the same query also returns documents in any subfolders,\nregardless of how deeply those folders are nested. In other words,\nsetting \\texttt{flatten} set to \\texttt{true} and querying for documents\nin a Site's root folder returns all the documents in the Site.\n\n\\section{Related Topics}\\label{related-topics-132}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/filter-sort-and-search}{Making\nAuthenticated Requests}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation}{API\nFormats and Content Negotiation}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data}{Working\nwith Collections of Data}\n\n\\chapter{Restrict Properties}\\label{restrict-properties}\n\nRetrieving large entities or collections increases the response's size\nand uses more bandwidth. You can alleviate this by telling the server\nvia the request which fields it should include in the response. This is\nknown as \\emph{sparse fieldsets}. To make a request with sparse\nfieldsets, include the \\texttt{fields} parameter in the URL with the\nname of each field's attribute.\n\nFor example, this request doesn't use sparse fieldsets and therefore\nreturns all the fields of a blog posting:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/blog-postings/59301\"  -u 'test@example.com:test'\n\\end{verbatim}\n\n\\begin{verbatim}\n{\n  \"alternativeHeadline\": \"The power of OpenAPI & Liferay\",\n  \"articleBody\": \"<p>We are happy to announce...</p>\",\n  \"creator\": {\n    \"familyName\": \"Test\",\n    \"givenName\": \"Test\",\n    \"id\": 20130,\n    \"name\": \"Test Test\",\n    \"profileURL\": \"/web/test\"\n  },\n  \"dateCreated\": \"2019-04-22T07:04:47Z\",\n  \"dateModified\": \"2019-04-22T07:04:51Z\",\n  \"datePublished\": \"2019-04-22T07:02:00Z\",\n  \"encodingFormat\": \"text/html\",\n  \"friendlyUrlPath\": \"new-headless-apis\",\n  \"headline\": \"New Headless APIs\",\n  \"id\": 59301,\n  \"numberOfComments\": 0,\n  \"siteId\": 20124\n}\n\\end{verbatim}\n\nTo get only the headline, creation date, and creator, append the\n\\texttt{fields} parameter to the URL with the fields \\texttt{headline},\n\\texttt{dateCreated}, and \\texttt{creator}:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/blog-postings/59301?fields=headline,dateCreated,creator\"  -u 'test@example.com:test'\n\\end{verbatim}\n\n\\begin{verbatim}\n{\n  \"creator\": {\n    \"familyName\": \"Test\",\n    \"givenName\": \"Test\",\n    \"id\": 20130,\n    \"name\": \"Test Test\",\n    \"profileURL\": \"/web/test\"\n  },\n  \"dateCreated\": \"2019-04-22T07:04:47Z\",\n  \"headline\": \"New Headless APIs\"\n}\n\\end{verbatim}\n\nIn the response, the \\texttt{creator} attribute is a nested JSON object.\nTo return only the creator's name, specify that nested field via dot\nnotation (\\texttt{creator.name}):\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/blog-postings/59301?fields=headline,dateCreated,creator.name\"  -u 'test@example.com:test'\n\\end{verbatim}\n\n\\begin{verbatim}\n{\n  \"creator\": {\n    \"name\": \"Test Test\"\n  },\n  \"dateCreated\": \"2019-04-22T07:04:47Z\",\n  \"headline\": \"New Headless APIs\"\n}\n\\end{verbatim}\n\nThe \\texttt{fields} parameter also works with collection resources to\nreturn the specified attributes for every collection item. For example,\nthis request gets the headlines for all the blog postings in the Site\nwith the ID \\texttt{20124}:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/blog-postings/?fields=headline\"  -u 'test@example.com:test'\n\\end{verbatim}\n\n\\begin{verbatim}\n{\n  \"items\": [\n    {\n      \"headline\": \"New Headless APIs\"\n    },\n    {\n      \"headline\": \"Authenticated requests\"\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 2\n}\n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-133}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/filter-sort-and-search}{Making\nAuthenticated Requests}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation}{API\nFormats and Content Negotiation}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data}{Working\nwith Collections of Data}\n\n\\chapter{Multipart Requests}\\label{multipart-requests}\n\nSeveral operations accept a binary file via a multipart request. For\nexample, the definition for posting a file to a \\texttt{DocumentFolder}\nspecifies a multipart request:\n\n\\begin{verbatim}\npost:\n    operationId: postDocumentFolderDocument\n    parameters:\n        - in: path\n          name: documentFolderId\n          required: true\n          schema:\n              format: int64\n              type: integer\n    requestBody:\n        content:\n            multipart/form-data:\n                schema:\n                    properties:\n                        document:\n                            $ref: \"#/components/schemas/Document\"\n                        file:\n                            format: binary\n                            type: string\n                    type: object\n    responses:\n        200:\n            content:\n                application/json:\n                    schema:\n                        $ref: \"#/components/schemas/Document\"\n                application/xml:\n                    schema:\n                        $ref: \"#/components/schemas/Document\"\n            description: \"\"\n    tags: [\"Document\"]\n\\end{verbatim}\n\nThis operation returns a \\texttt{Document} (in JSON or XML). To create\nthis \\texttt{Document}, you must supply the operation's multipart\nrequest with 2 components:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  A binary file (bytes) via the \\texttt{file} property\n\\item\n  A JSON string with the binary file's metadata, via the\n  \\texttt{document} property\n\\end{itemize}\n\nTo send this request, the \\texttt{Content-Type} must be\n\\texttt{multipart/form-data}, and you must also specify a boundary name\n(the boundary name can be arbitrary).\n\nHere's an example request (without the file's bytes) that creates a\ndocument in the folder with the ID \\texttt{38549}:\n\n\\begin{verbatim}\ncurl -X \"POST\" \"http://localhost:8080/o/headless-delivery/v1.0/document-folders/38549/documents\" \\\n     -H 'Accept: application/json' \\\n     -H 'Content-Type: multipart/form-data; boundary=PART' \\\n     -u 'test@example.com:test' \\\n     -F \"file=\" \\\n     -F \"document={\\\"title\\\": \\\"podcast\\\"}\"\n\\end{verbatim}\n\nAnd here's the response:\n\n\\begin{verbatim}\n{\n  \"contentUrl\": \"/documents/20123/38549/podcast.mp3/e978e316-620c-df9f-e0bd-7cc0447cca49?version=1.0&t=1556100111417\",\n  \"creator\": {\n    \"familyName\": \"Test\",\n    \"givenName\": \"Test\",\n    \"id\": 20129,\n    \"name\": \"Test Test\",\n    \"profileURL\": \"/web/test\"\n  },\n  \"dateCreated\": \"2019-04-24T10:01:51Z\",\n  \"dateModified\": \"2019-04-24T10:01:51Z\",\n  \"documentFolderId\": 38549,\n  \"encodingFormat\": \"audio/mpeg\",\n  \"fileExtension\": \"mp3\",\n  \"id\": 38553,\n  \"numberOfComments\": 0,\n  \"sizeInBytes\": 28482097,\n  \"title\": \"podcast\"\n}\n\\end{verbatim}\n\n\\section{Related Topics}\\label{related-topics-134}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/filter-sort-and-search}{Making\nAuthenticated Requests}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation}{API\nFormats and Content Negotiation}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data}{Working\nwith Collections of Data}\n\n\\chapter{How to get siteId}\\label{how-to-get-siteid}\n\nSeveral APIs (generally all collection APIs) need the \\texttt{siteId}\nparameter to to execute requests. The \\texttt{siteId} is the internal\nidentifier of the Site where that content was created.\n\n\\section{Using siteId or siteKey}\\label{using-siteid-or-sitekey}\n\nIn all the APIs available from 7.2 GA2+, the \\texttt{siteKey} is also\naccepted as a valid parameter. The \\texttt{siteKey} is the external name\nof the Site (for example \\texttt{Guest}).\n\nIn the REST APIs, you can use \\texttt{siteKey} in all the places that\nexpect a \\texttt{siteId}; in GraphQL APIs, there are two different\nparameters: \\texttt{siteId} and \\texttt{siteKey}.\n\nUsing the \\texttt{siteKey} is recommended over \\texttt{siteId} in all\nsituations because it's more recognizable, doesn't expose an internal\nparameter, and doesn't change in import/export processes.\n\n\\section{Obtain siteId}\\label{obtain-siteid}\n\nThere are several ways to retrieve the \\texttt{siteId}:\n\n\\begin{itemize}\n\\item\n  Use the Site API to query it by name, friendly URL or in the list of a\n  User's Sites.\n\\item\n  From Liferay DXP's UI in the Site Administration menu (not\n  recommended).\n\\item\n  From the \\texttt{Group} table in the database (not recommended).\n\\item\n  From the \\texttt{ThemeDisplay} object in JavaScript or Java:\n\n\\begin{verbatim}\n  themeDisplay.getSiteGroupId()\n\\end{verbatim}\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/rest-site-id.png}\n\\caption{GraphQL BlogPostings definition}\n\\end{figure}\n\n\\chapter{Filterable properties}\\label{filterable-properties}\n\nSome APIs return data that can be filtered and sorted by several\nproperties. This is a non-comprehensive list of the properties that can\nbe used to filter or sort.\n\n\\section{Headless Delivery API}\\label{headless-delivery-api}\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/BlogPosting}{BlogPosting}}{BlogPosting}}\\label{blogposting}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} taxonomyCategoryIds\n\\textbar{} list \\textbar{} \\texttt{taxonomyCategoryIds/any(t:t\\ eq\\ 1)}\n\\textbar{} keywords \\textbar{} list \\textbar{}\n\\texttt{keywords/any(k:contains(k,\\textquotesingle{}substring1\\textquotesingle{}))}\\textbar{}\ncustomFields \\textbar{} complex \\textbar{}\n\\texttt{customFields/Name\\ eq\\ \\textquotesingle{}Article1\\textquotesingle{}}\n\\textbar{} dateCreated \\textbar{} date \\textbar{}\n\\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} dateModified\n\\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} creatorId\n\\textbar{} integer \\textbar{} \\texttt{creatorId\\ eq\\ 1} \\textbar{}\nheadline \\textbar{} string \\textbar{}\n\\texttt{contains(headline,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/BlogPostingImage}{BlogPostingImage}}{BlogPostingImage}}\\label{blogpostingimage}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} encodingFormat\n\\textbar{} id \\textbar{} \\texttt{encodingFormat\\ eq\\ 1} \\textbar{}\nsizeInBytes \\textbar{} integer \\textbar{} \\texttt{sizeInBytes\\ eq\\ 1}\n\\textbar{} fileExtension \\textbar{} string \\textbar{}\n\\texttt{contains(fileExtension,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{} title \\textbar{} string \\textbar{}\n\\texttt{contains(title,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/Comment}{Comment}}{Comment}}\\label{comment}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} dateCreated \\textbar{}\ndate \\textbar{} \\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z}\n\\textbar{} dateModified \\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z}\\textbar{} creatorId\n\\textbar{} integer \\textbar{} \\texttt{creatorId\\ eq\\ 1} \\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/ContentStructure}{ContentStructure}}{ContentStructure}}\\label{contentstructure}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} dateCreated \\textbar{}\ndate \\textbar{} \\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z}\n\\textbar{} dateModified \\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} name\n\\textbar{} string \\textbar{}\n\\texttt{contains(name,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/Document}{Document}}{Document}}\\label{document}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} taxonomyCategoryIds\n\\textbar{} list \\textbar{} \\texttt{taxonomyCategoryIds/any(t:t\\ eq\\ 1)}\n\\textbar{} keywords \\textbar{} list \\textbar{}\n\\texttt{keywords/any(k:contains(k,\\textquotesingle{}substring1\\textquotesingle{}))}\\textbar{}\ncustomFields \\textbar{} complex \\textbar{}\n\\texttt{customFields/Name\\ eq\\ \\textquotesingle{}Article1\\textquotesingle{}}\n\\textbar{} dateCreated \\textbar{} date \\textbar{}\n\\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} dateModified\n\\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{}\nencodingFormat \\textbar{} id \\textbar{} \\texttt{encodingFormat\\ eq\\ 1}\n\\textbar{} creatorId \\textbar{} integer \\textbar{}\n\\texttt{creatorId\\ eq\\ 1} \\textbar{} sizeInBytes \\textbar{} integer\n\\textbar{} \\texttt{sizeInBytes\\ eq\\ 1} \\textbar{} fileExtension\n\\textbar{} string \\textbar{}\n\\texttt{contains(fileExtension,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{} title \\textbar{} string \\textbar{}\n\\texttt{contains(title,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/DocumentFolder}{DocumentFolder}}{DocumentFolder}}\\label{documentfolder}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} customFields\n\\textbar{} complex \\textbar{}\n\\texttt{customFields/Name\\ eq\\ \\textquotesingle{}Article1\\textquotesingle{}}\n\\textbar{} dateCreated \\textbar{} date \\textbar{}\n\\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} dateModified\n\\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} creatorId\n\\textbar{} integer \\textbar{} \\texttt{creatorId\\ eq\\ 1} \\textbar{} name\n\\textbar{} string \\textbar{}\n\\texttt{contains(name,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/KnowledgeBaseArticle}{KnowledgeBaseArticle}}{KnowledgeBaseArticle}}\\label{knowledgebasearticle}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} taxonomyCategoryIds\n\\textbar{} list \\textbar{} \\texttt{taxonomyCategoryIds/any(t:t\\ eq\\ 1)}\n\\textbar{} keywords \\textbar{} list \\textbar{}\n\\texttt{keywords/any(k:contains(k,\\textquotesingle{}substring1\\textquotesingle{}))}\\textbar{}\ncustomFields \\textbar{} complex \\textbar{}\n\\texttt{customFields/Name\\ eq\\ \\textquotesingle{}Article1\\textquotesingle{}}\n\\textbar{} dateCreated \\textbar{} date \\textbar{}\n\\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} dateModified\n\\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} title\n\\textbar{} string \\textbar{}\n\\texttt{contains(title,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/MessageBoardMessage}{MessageBoardMessage}}{MessageBoardMessage}}\\label{messageboardmessage}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} showAsAnswer\n\\textbar{} boolean \\textbar{} \\texttt{showAsAnswer\\ eq\\ true} \\textbar{}\nshowAsQuestion \\textbar{} boolean \\textbar{}\n\\texttt{showAsQuestion\\ eq\\ true} \\textbar{} taxonomyCategoryIds\n\\textbar{} list \\textbar{} \\texttt{taxonomyCategoryIds/any(t:t\\ eq\\ 1)}\n\\textbar{} keywords \\textbar{} list \\textbar{}\n\\texttt{keywords/any(k:contains(k,\\textquotesingle{}substring1\\textquotesingle{}))}\\textbar{}\ncustomFields \\textbar{} complex \\textbar{}\n\\texttt{customFields/Name\\ eq\\ \\textquotesingle{}Article1\\textquotesingle{}}\n\\textbar{} dateCreated \\textbar{} date \\textbar{}\n\\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} dateModified\n\\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} creatorId\n\\textbar{} integer \\textbar{} \\texttt{creatorId\\ eq\\ 1} \\textbar{}\nmessageBoardSectionId \\textbar{} integer \\textbar{}\n\\texttt{messageBoardSectionId\\ eq\\ 1} \\textbar{} headline \\textbar{}\nstring \\textbar{}\n\\texttt{contains(headline,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/MessageBoardSection}{MessageBoardSection}}{MessageBoardSection}}\\label{messageboardsection}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} customFields\n\\textbar{} complex \\textbar{}\n\\texttt{customFields/Name\\ eq\\ \\textquotesingle{}Article1\\textquotesingle{}}\n\\textbar{} dateCreated \\textbar{} date \\textbar{}\n\\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} dateModified\n\\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} creatorId\n\\textbar{} integer \\textbar{} \\texttt{creatorId\\ eq\\ 1} \\textbar{} title\n\\textbar{} string \\textbar{}\n\\texttt{contains(title,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/StructuredContent}{StructuredContent}}{StructuredContent}}\\label{structuredcontent}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} taxonomyCategoryIds\n\\textbar{} list \\textbar{} \\texttt{taxonomyCategoryIds/any(t:t\\ eq\\ 1)}\n\\textbar{} keywords \\textbar{} list \\textbar{}\n\\texttt{keywords/any(k:contains(k,\\textquotesingle{}substring1\\textquotesingle{}))}\\textbar{}\ncontentFields \\textbar{} complex \\textbar{}\n\\texttt{contentFields/Name\\ eq\\ \\textquotesingle{}Article1\\textquotesingle{}}\n\\textbar{} customFields \\textbar{} complex \\textbar{}\n\\texttt{customFields/Name\\ eq\\ \\textquotesingle{}Article1\\textquotesingle{}}\n\\textbar{} dateCreated \\textbar{} date \\textbar{}\n\\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} dateModified\n\\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} datePublished\n\\textbar{} date \\textbar{}\n\\texttt{datePublished\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{}\ncontentStructureId \\textbar{} integer \\textbar{}\n\\texttt{contentStructureId\\ eq\\ 1} \\textbar{} title \\textbar{} string\n\\textbar{}\n\\texttt{contains(title,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/StructuredContentFolder}{StructuredContentFolder}}{StructuredContentFolder}}\\label{structuredcontentfolder}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} customFields\n\\textbar{} complex \\textbar{}\n\\texttt{customFields/Name\\ eq\\ \\textquotesingle{}Article1\\textquotesingle{}}\n\\textbar{} dateCreated \\textbar{} date \\textbar{}\n\\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} dateModified\n\\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} creatorId\n\\textbar{} integer \\textbar{} \\texttt{creatorId\\ eq\\ 1} \\textbar{} name\n\\textbar{} string \\textbar{}\n\\texttt{contains(name,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/WikiNode}{WikiNode}}{WikiNode}}\\label{wikinode}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} dateCreated \\textbar{}\ndate \\textbar{} \\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z}\n\\textbar{} dateModified \\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} creatorId\n\\textbar{} integer \\textbar{} \\texttt{creatorId\\ eq\\ 1} \\textbar{} name\n\\textbar{} string \\textbar{}\n\\texttt{contains(name,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0\\#/WikiPage}{WikiPage}}{WikiPage}}\\label{wikipage}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} customFields\n\\textbar{} complex \\textbar{}\n\\texttt{customFields/Name\\ eq\\ \\textquotesingle{}Article1\\textquotesingle{}}\n\\textbar{} dateCreated \\textbar{} date \\textbar{}\n\\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} dateModified\n\\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} headline\n\\textbar{} string \\textbar{}\n\\texttt{contains(headline,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{Headless Admin User API}\\label{headless-admin-user-api}\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-admin-user/v1.0\\#/Organization}{Organization}}{Organization}}\\label{organization}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} keywords \\textbar{}\nlist \\textbar{}\n\\texttt{keywords/any(k:contains(k,\\textquotesingle{}substring1\\textquotesingle{}))}\\textbar{}\ndateModified \\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{}\nparentOrganizationId \\textbar{} id \\textbar{}\n\\texttt{parentOrganizationId\\ eq\\ 1} \\textbar{} name \\textbar{} string\n\\textbar{}\n\\texttt{contains(name,\\textquotesingle{}category\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-admin-user/v1.0\\#/User}{User}}{User}}\\label{user}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} keywords \\textbar{}\nlist \\textbar{}\n\\texttt{keywords/any(k:contains(k,\\textquotesingle{}substring1\\textquotesingle{}))}\\textbar{}\ndateModified \\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} id \\textbar{}\nid \\textbar{} \\texttt{id\\ eq\\ 1} \\textbar{} organizationIds \\textbar{}\nid \\textbar{} \\texttt{organizationIds\\ eq\\ 1} \\textbar{} roleIds\n\\textbar{} id \\textbar{} \\texttt{roleIds\\ eq\\ 1} \\textbar{} userGroupIds\n\\textbar{} id \\textbar{} \\texttt{userGroupIds\\ eq\\ 1} \\textbar{}\nalternateName \\textbar{} string \\textbar{}\n\\texttt{contains(alternateName,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{} emailAddress \\textbar{} string \\textbar{}\n\\texttt{contains(emailAddress,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{} familyName \\textbar{} string \\textbar{}\n\\texttt{contains(familyName,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{} givenName \\textbar{} string \\textbar{}\n\\texttt{contains(givenName,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{} jobTitle \\textbar{} string \\textbar{}\n\\texttt{contains(jobTitle,\\textquotesingle{}substring1\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{Headless Admin Taxonomy API}\\label{headless-admin-taxonomy-api}\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/v1.0\\#/Category}{Category}}{Category}}\\label{category}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} dateCreated \\textbar{}\ndate \\textbar{} \\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z}\n\\textbar{} dateModified \\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z}\\textbar{} name\n\\textbar{} string \\textbar{}\n\\texttt{contains(name,\\textquotesingle{}category\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/v1.0\\#/Keyword}{Keyword}}{Keyword}}\\label{keyword}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} dateCreated \\textbar{}\ndate \\textbar{} \\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z}\n\\textbar{} dateModified \\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z}\\textbar{} name\n\\textbar{} string \\textbar{}\n\\texttt{contains(name,\\textquotesingle{}category\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\href{https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/v1.0\\#/Vocabulary}{Vocabulary}}{Vocabulary}}\\label{vocabulary}\n\n\\noindent\\hrulefill\n\nKey \\textbar{} Type \\textbar{} Example \\textbar{} dateCreated \\textbar{}\ndate \\textbar{} \\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z}\n\\textbar{} dateModified \\textbar{} date \\textbar{}\n\\texttt{dateModified\\ lt\\ 2018-02-13T12:33:12Z}\\textbar{} name\n\\textbar{} string \\textbar{}\n\\texttt{contains(name,\\textquotesingle{}category\\textquotesingle{})}\n\\textbar{}\n\n\\chapter{Using REST APIs}\\label{using-rest-apis}\n\nLiferay DXP's headless REST APIs can be used with any REST client you\nprefer. The only usual requirements are setting up the\n\\texttt{Authentication} header (either OAuth, Cookie, Basic\\ldots) and\nthe \\texttt{Content-Type} header if you are creating content.\n\nOur recommendation for JavaScript applications is to use \\texttt{fetch}\ndirectly, like this:\n\n\\begin{verbatim}\nfetch(`http://localhost:8080/o/headless-delivery/v1.0/sites/${SITE_ID}/structured-contents/'`, \n    {\n        method: 'GET',\n        headers: {\n            'Authorization': `Basic ${BASIC_AUTH}`\n        }\n    }\n);\n\\end{verbatim}\n\nOr for a \\texttt{POST} request:\n\n\\begin{verbatim}\nfetch(`http://localhost:8080/o/headless-delivery/v1.0/sites/${SITE_ID}/structured-contents/`, \n    {\n        method: 'POST', \n        headers: {\n            'Authorization': `Basic ${BASIC_AUTH}`,\n            'Content-Type': 'application/json'\n        }, \n        body: JSON.stringify(\n            {\n                \"title\": \"New appointment\",\n                \"contentStructureId\": STRUCTURE_ID,\n                \"contentFields\": [\n                    {\n                        \"name\": \"User\",\n                        \"value\": {\n                            \"data\": USER,\n                        }\n                    },\n                ]\n            }\n        )\n    }\n)\n\\end{verbatim}\n\nHere are two examples of JavaScript applications using the Headless REST\nAPIs:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/dgomezg/liferay-frontend-samples/tree/master/riuvo-alexa-skill}{Alexa\n  skill using Headless REST APIs with node-fetch}.\n\\item\n  \\href{https://liferay.dev/blogs/-/blogs/creating-headless-apis-part-1}{Example\n  API from scratch using REST Builder}.\n\\end{itemize}\n\n\\chapter{JAX-RS}\\label{jax-rs}\n\nJAX-RS web services work in Liferay modules the same way they work\noutside of Liferay. The only difference is that you must register the\nclass in the OSGi framework. Liferay makes this easy by providing a\ntemplate.\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\na project} and choose the \\emph{rest} template.\n\nThe class that's generated contains a working JAX-RS web service. You\ncan deploy it and use it immediately.\n\nWhile it's beyond the scope of this article to cover\n\\href{https://blog.osgi.org/2018/03/osgi-r7-highlights-jax-rs-whiteboard.html}{JAX-RS\nWhiteboard} in its entirety, essentially it's JAX-RS unchanged except\nfor configuration properties in the \\texttt{@Component} annotation.\nThese properties declare three things:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The endpoint for the service\n\\item\n  The service name as it appears in the OAuth 2.0 configuration\n\\item\n  (Optional) Properties you may want to set for further configuration.\n\\end{enumerate}\n\nThe generated class contains this configuration:\n\n\\begin{verbatim}\n@Component( \n        property = { \n            JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE + \"=/greetings\", \n            JaxrsWhiteboardConstants.JAX_RS_NAME + \"=Greetings.Rest\"\n        }, \n        service = Application.class)\n\\end{verbatim}\n\nThis configuration registers the service at this endpoint:\n\n\\begin{verbatim}\nhttps://[server-name]:[port]/o/greetings\n\\end{verbatim}\n\nIf you're testing this locally on Tomcat, the URL is\n\n\\begin{verbatim}\nhttps://localhost:8080/o/greetings\n\\end{verbatim}\n\nAs you might guess, you don't have access to the service by just calling\nthe URL above. You must authenticate first, which you'll learn how to do\nnext.\n\n\\section{Authenticating to JAX-RS Web\nServices}\\label{authenticating-to-jax-rs-web-services}\n\nAuthentication during development can be done through Basic\nAuthentication or portal sessions, but you don't want to leave that\nenabled for production. For production, you want OAuth 2.0. Here's how\nto configure JAX-RS authentication.\n\n\\section{During Development: Basic\nAuth}\\label{during-development-basic-auth}\n\nWhen you deploy a JAX-RS application, an\n\\href{/docs/7-2/deploy/-/knowledge_base/d/authentication-verifiers}{Auth\nVerifier} filter is registered for it. You can set its properties in\nyour \\texttt{@Component} annotation by prefixing the properties with\n\\texttt{auth.verifier}. For example, to disable guest access to the\nservice, configure it like this:\n\n\\begin{verbatim}\n@Component( \n        property = { \n            JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE + \"=/greetings\", \n            JaxrsWhiteboardConstants.JAX_RS_NAME + \"=Greetings.Rest\",\n            \"auth.verifier.guest.allowed=false\"\n        }, \n        service = Application.class)\n\\end{verbatim}\n\nBasic Auth is great during development, but credentials passed on the\nURL appear in server logs, so when you're done developing, you should\ndisable Basic Auth and use OAuth2 instead. To disable Basic Auth, create\nand deploy a configuration file called\n\\texttt{com.liferay.portal.security.auth.verifier.internal.tracker.AuthVerifierFilterTracker.config}\nthat contains this property:\n\n\\begin{verbatim}\ndefault.registration.property=[\"filter.init.auth.verifier.OAuth2RESTAuthVerifier.urls.includes=*\",\"filter.init.auth.verifier.PortalSessionAuthVerifier.urls.includes=*\"]\n\\end{verbatim}\n\nThis disables Basic Auth for all JAX-RS applications, but keeps Portal\nSession and OAuth2 enabled.\n\n\\section{Using OAuth 2.0 to Invoke a JAX-RS Web\nService}\\label{using-oauth-2.0-to-invoke-a-jax-rs-web-service}\n\nYour JAX-RS web service requires authorization by default. To enable\nthis, you must create an\n\\href{/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0\\#creating-an-application}{OAuth\n2.0 application} to provide a way to grant access to your service:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\emph{Control Panel} → \\emph{Configuration} → \\emph{OAuth2\n  Administration} and click the \\includegraphics{./images/icon-add.png}\n  button to add an application.\n\\item\n  Give your application a descriptive name.\n\\item\n  Choose the Client Profile appropriate for this service. These are\n  templates that auto-select the appropriate authorization types or\n  ``flows'' from the OAuth 2 standard. For this example choose the\n  \\emph{Headless Server} profile, which auto-selects the \\emph{Client\n  Credentials} authorization type.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nThe form now reappears with two additional generated fields: Client ID\nand Client Secret. You'll use these to authenticate to your web service.\n\nTo make your service accessible,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Scopes} tab.\n\\item\n  You'll see an entry for your deployed \\texttt{Greetings.Rest} service.\n  Expand it by clicking the arrow.\n\\item\n  Check the box labeled \\emph{read data on your behalf}.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/jax-rs-oauth2-scope.png}\n\\caption{Enable the scope to grant access to the service.}\n\\end{figure}\n\nFor simplicity, the examples below use \\href{https://curl.haxx.se}{Curl}\nto authenticate. You need the two pieces of information generated for\nyour application: the Client ID and the Client Secret. For example, say\nthose fields contain these values:\n\n\\textbf{Client ID:} \\texttt{id-12e14a84-e558-35a7-cf9a-c64aafc7f}\n\n\\textbf{Client Secret:}\n\\texttt{secret-93f14320-dc39-d67f-9dec-97717b814f}\n\nFirst, you must request an OAuth token. If you're testing locally, you'd\nmake a request like this:\n\n\\begin{verbatim}\ncurl http://localhost:8080/o/oauth2/token -d 'grant_type=client_credentials&client_id=id-12e14a84-e558-35a7-cf9a-c64aafc7f&client_secret=secret-93f14320-dc39-d67f-9dec-97717b814f'\n\\end{verbatim}\n\nThe response is JSON:\n\n\\begin{verbatim}\n{\"access_token\":\"a7f12bef7f2e578cf64bce4085db8f17b6a3c2963f865a65b374e89784bbca5\",\"token_type\":\"Bearer\",\"expires_in\":600,\"scope\":\"GET POST PUT\"}\n\\end{verbatim}\n\nIt contains a token, generated for this client. It expires in 600\nseconds, and it grants GET, POST, and PUT for this web service.\n\nWhen you want to call the service, you must supply the token in the HTTP\nheader, like this:\n\n\\begin{verbatim}\ncurl --header \"Authorization: Bearer a7f12bef7f2e578cf64bce4085db8f17b6a3c2963f865a65b374e89784bbca5\" http://localhost:8080/o/greetings/morning\n\\end{verbatim}\n\nWith authorization, your web service can be called and responds to the\nrequest:\n\n\\begin{verbatim}\nGood morning!\n\\end{verbatim}\n\nOf course, this is only one of the authorization flows for OAuth 2.0. If\nyou're creating a web-based client whose back-end is a JAX-RS web\nservice hosted on Liferay DXP, you'd want one of the other flows. See\nthe \\href{/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0}{OAuth 2.0\ndocumentation} for further information. Additionally, OAuth 2.0 assumes\nthe use of HTTPS for its security: the above URLs are only for local\ntesting purposes. You certainly would not want to pass OAuth tokens\nbetween clients and servers in the clear. Make sure that in production\nyour server uses HTTPS.\n\n\\subsection{OAuth2 Scopes}\\label{oauth2-scopes}\n\nWithout any special Liferay OAuth2 annotations or properties, a standard\nOSGi JAX-RS application is inspected by the Liferay OAuth2 runtime, and\nscopes are derived by default based on the HTTP verbs supported by the\napplication.\n\nWhen developers want more control, they can use the property\n\\texttt{oauth2.scopechecker.type=annotations} and the annotation\n\\texttt{com.liferay.oauth2.provider.scope.RequiresScope} exported from\nthe \\texttt{Liferay\\ OAuth2\\ Provider\\ Scope\\ API} bundle to annotate\nendpoint resource methods or whole classes like this:\n\n\\begin{verbatim}\n@RequiresScope(\"scopeName\")\n\\end{verbatim}\n\nOnce deployed, this becomes a scope in the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/oauth2-scopes}{OAuth 2.0\nconfiguration}. You can disable scope checking (not recommended) by\nsetting the scope checker to a non-existent type:\n\n\\begin{verbatim}\noauth2.scope.checker.type=none\n\\end{verbatim}\n\n\\subsection{Requiring OAuth2}\\label{requiring-oauth2}\n\nYou can specify OAuth2 authorization as required for your JAX-RS\napplication by using this property:\n\n\\begin{verbatim}\nosgi.jaxrs.extension.select=(osgi.jaxrs.name=Liferay.OAuth2)\n\\end{verbatim}\n\n\\section{JAX-RS and Service Access\nPolicies}\\label{jax-rs-and-service-access-policies}\n\nWhen authenticating via Basic Auth, the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/service-access-policies}{Service\nAccess Policy} \\texttt{SYSTEM\\_USER\\_PASSWORD} is enforced. When\nauthenticating via OAuth 2.0, the \\texttt{AUTHORIZED\\_OAUTH2\\_SAP}\npolicy is enforced. Configure them appropriately for your environment,\nas by default, they allow invoking all remote services. To disable\nService Access Policy enforcement for JAX-RS endpoints (not\nrecommended), set this property:\n\n\\begin{verbatim}\nliferay.access.control.disable=true\n\\end{verbatim}\n\nWith this configured, guests can call these endpoints without\nadministrators having to define a default Service Access Policy.\n\n\\section{Public JAX-RS Services}\\label{public-jax-rs-services}\n\nTo create a public endpoint for development purposes, all you must do is\nset two properties:\n\n\\begin{verbatim}\n@Component(\n    property={\n        \"auth.verifier.guest.allowed=true\",\n        \"liferay.access.control.disable=true\"\n    },\n    service = Application.class\n)\n\\end{verbatim}\n\nDon't keep this configuration for production. For public services, it's\nbest to leave the security in place and whitelist the particular\nendpoints you're making public. See\n\\href{/docs/7-2/deploy/-/knowledge_base/d/service-access-policies}{Service\nAccess Policies} for further information.\n\n\\section{Using JAX-RS with CORS}\\label{using-jax-rs-with-cors}\n\nIf you foresee that JavaScript in a browser might access your JAX-RS web\nservice from a different domain, you might want to use the CORS\nannotation. You can use the \\texttt{@CORS} annotation to define\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-cors}{CORS\npolicies} on your deployed JAX-RS applications. Note that the\nannotations\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-cors}{can be\noverridden by an administrator}. It only takes three steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Add the Portal Remote CORS API dependency to your module:\n\\end{enumerate}\n\n\\begin{verbatim}\ncompileOnly project(\":apps:portal-remote:portal-remote-cors-api\")\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\tightlist\n\\item\n  Activate the CORS annotation feature in your application properties:\n\\end{enumerate}\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"osgi.jaxrs.application.base=/my-application\",\n        \"osgi.jaxrs.name=My.Application.Name\",\n        \"liferay.cors.annotation=true\"\n    },\n    service = Application.class\n    )\n    public class MyApplication extends Application {\n    ...\n    }\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\tightlist\n\\item\n  Use the \\texttt{@CORS} annotation throughout your application globally\n  or by method.\n\\end{enumerate}\n\nGlobally:\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"osgi.jaxrs.application.base=/my-application\",\n        \"osgi.jaxrs.name=My.Application.Name\",\n        \"liferay.cors.annotation=true\"\n    },\n    service = Application.class\n)\n@CORS(allowMethods=\"GET\")\npublic class MyApplication extends Application {\n...\n}\n\\end{verbatim}\n\nBy method:\n\n\\begin{verbatim}\n@CORS\n    @GET\n    @Path(\"/users\")\n    public List<User> getUserList() throws Exception {\n        return _users;\n    }\n\\end{verbatim}\n\nYou can use the annotation to provide a configuration for any of the\nCORS headers. Here are some examples:\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n  Header      |    Annotation Example    | \n\\end{verbatim}\n\nAccess-Control-Allow-Credentials\\textbar{}\\texttt{@CORS(allowCredentials\\ =\\ false)}\\textbar{}\nAccess-Control-Allow-Headers\\textbar{}\\texttt{@CORS(allowHeaders\\ =\\ \"X-PINGOTHER\")}\\textbar{}\nAccess-Control-Allow-Methods\\textbar{}\\texttt{@CORS(allowMethods\\ =\\ \"OPTIONS,POST\")}\\textbar{}\nAccess-Control-Allow-Origin\\textbar{}\\texttt{@CORS(allowOrigin\\ =\\ \"http://www.liferay.com\")}\\textbar{}\n\n\\noindent\\hrulefill\n\nIf for some reason you want to disable the \\texttt{@CORS} annotations in\nyour application, you can do it globally by disabling it in your\n\\texttt{@Component} annotation:\n\n\\begin{verbatim}\n@Component(\n    property = {\n        \"osgi.jaxrs.application.base=/no-cors-application\",\n        \"osgi.jaxrs.name=NoCors.Application.Name\",\n        \"liferay.cors.annotation=false\"\n    },\n    service = Application.class\n)\n\\end{verbatim}\n\nGreat! Now you know how to create, deploy, and invoke JAX-RS web\nservices on Liferay DXP's platform!\n\n\\section{Related Topics}\\label{related-topics-135}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/rest-builder}{REST Builder}\n\n\\chapter{JAX-WS}\\label{jax-ws}\n\nLiferay supports\n\\href{https://en.wikipedia.org/wiki/Java_API_for_XML_Web_Services}{JAX-WS}\nvia the \\href{http://cxf.apache.org/}{Apache CXF} implementation. Apps\ncan publish JAX-WS web services to the CXF endpoints defined in your\nLiferay instance. CXF endpoints are effectively context paths the JAX-WS\nweb services are deployed to and accessible from. To publish any kind of\nJAX-WS web service, one or more CXF endpoints must be defined. To access\nJAX-WS web services, an \\emph{extender} must also be configured in your\nLiferay instance. Extenders specify where the services are deployed and\nwhether they are augmented with handlers, providers, and so on.\n\n\\textbf{SOAP Extenders:} Required to publish JAX-WS web services. Each\nSOAP extender can deploy the services to one or more CXF endpoints and\ncan use a set of\n\\href{https://jax-ws.java.net/articles/handlers_introduction.html}{JAX-WS\nhandlers} to augment the services.\n\nSOAP extenders are subsystems that track the services the app developer\nregisters in OSGi (those matching the provided\n\\href{https://osgi.org/javadoc/r6/core/org/osgi/framework/Filter.html}{OSGi\nfilters}), and deploy them under the specified CXF endpoints. For\nexample, if you create the CXF endpoint \\texttt{/soap}, you could later\ncreate a SOAP extender for \\texttt{/soap} that publishes SOAP services.\nOf course, this is only a rough example: you can fine tune things to\nyour liking.\n\nCXF endpoints and extenders can be created programmatically or with\nLiferay's Control Panel. This tutorial shows you how to do both, and\nthen shows you how to publish JAX-WS web services. The following topics\nare covered:\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/jax-ws\\#configuring-endpoints-and-extenders-with-the-control-panel}{Configuring\n  Endpoints and Extenders with the Control Panel}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/jax-ws\\#configuring-endpoints-and-extenders-programmatically}{Configuring\n  Endpoints and Extenders Programmatically}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/jax-ws\\#publishing-jax-ws-web-services}{Publishing\n  JAX-WS Web Services}\n\\end{itemize}\n\n\\section{Configuring Endpoints and Extenders with the Control\nPanel}\\label{configuring-endpoints-and-extenders-with-the-control-panel}\n\nLiferay's Control Panel lets administrators configure endpoints and\nextenders for JAX-WS web services. Note that you must be an\nadministrator in your Liferay instance to access the settings here.\nFirst, you'll learn how to create CXF endpoints.\n\nTo configure a CXF endpoint with the Control Panel, first go to\n\\emph{Control Panel} → \\emph{Configuration} → \\emph{System Settings} →\n\\emph{Web API}. Then select \\emph{CXF Endpoints} from the list. If there\nare any existing CXF endpoints, they're shown here. To add a new one,\nclick the \\emph{Add} button. The form that appears lets you configure a\nnew CXF endpoint by filling out these fields:\n\n\\textbf{Context Path:} The path the JAX-WS web services are deployed to\non the Liferay server. For example, if you define the context path\n\\texttt{/web-services}, any services deployed there are available at\n\\texttt{http://your-server:your-port/o/web-services}.\n\n\\textbf{\\texttt{AuthVerifier} properties:} Any properties defined here\nare passed as-is to the \\texttt{AuthVerifier} filter. See the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/authentication-verifiers}{\\texttt{AuthVerifier}\ndocumentation} for more details.\n\n\\textbf{Required Extensions:} CXF normally loads its default extension\nclasses, but in some cases you can override them to replace the default\nbehavior. In most cases, you can leave this field blank: overriding\nextensions isn't common. By specifying custom extensions here via\n\\href{https://osgi.org/javadoc/r6/core/org/osgi/framework/Filter.html}{OSGi\nfilters}, Liferay waits until those extensions are registered in the\nOSGi framework before creating the CXF servlet and passing the\nextensions to the servlet.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/cxf-endpoint-form.png}\n\\caption{Fill out this form to create a CXF endpoint.}\n\\end{figure}\n\nFor an app to deploy JAX-WS web services, you must configure a SOAP\nextender. To configure a SOAP extender with the Control Panel, first go\nto \\emph{Control Panel} → \\emph{Configuration} → \\emph{System Settings}\n→ \\emph{Web API}. Then select \\emph{SOAP Extenders} from the list. If\nthere are any existing SOAP extenders, they're shown here. To add a new\none, click on the \\emph{Add} button. The form that appears lets you\nconfigure a new SOAP extender by filling out these fields:\n\n\\textbf{Context paths:} Specify at least one CXF endpoint here. This is\nwhere the services affected by this extender are deployed. In the\npreceding CXF endpoint example, this would be \\texttt{/web-services}.\nNote that you can specify more than one CXF endpoint here.\n\n\\textbf{jax.ws.handler.filters:} Here you can specify a set of\n\\href{https://osgi.org/javadoc/r6/core/org/osgi/framework/Filter.html}{OSGi\nfilters} that select certain services registered in the OSGi framework.\nThe selected services should implement JAX-WS handlers and augment the\nJAX-WS services specified in the \\emph{jax.ws.service.filters} property.\nThese JAX-WS handlers apply to each service selected in this extender.\n\n\\textbf{jax.ws.service.filters:} Here you can specify a set of OSGi\nfilters that select the services registered in the OSGi framework that\nare deployed to the CXF endpoints. These OSGi services must be\n\\href{https://docs.oracle.com/javaee/7/tutorial/jaxws001.htm}{proper\nJAX-WS services}.\n\n\\textbf{soap.descriptor.builder:} Leave this option empty to use JAX-WS\nannotations to describe the SOAP service. To use a different way to\ndescribe the SOAP service, you can provide an OSGi filter here that\nselects an implementation of\n\\texttt{com.liferay.portal.remote.soap.extender.SoapDescriptorBuilder}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/soap-extenders-form.png}\n\\caption{Fill out this form to create a SOAP extender.}\n\\end{figure}\n\nNext, you'll learn how to create endpoints and extenders\nprogrammatically.\n\n\\section{Configuring Endpoints and Extenders\nProgrammatically}\\label{configuring-endpoints-and-extenders-programmatically}\n\nTo configure endpoints or extenders programmatically, you must use\nLiferay's configurator extender. The configurator extender provides a\nway for OSGi modules to deploy default configuration values. Modules\nthat use the configurator extender must provide a\n\\texttt{ConfigurationPath} header that points to the configuration\nfiles' location inside the module. For example, the following\nconfiguration sets the \\texttt{ConfigurationPath} to\n\\texttt{src/main/resources/configuration}:\n\n\\begin{verbatim}\nBundle-Name: Liferay Export Import Service JAX-WS\nBundle-SymbolicName: com.liferay.exportimport.service.jaxws\nBundle-Version: 1.0.0\nLiferay-Configuration-Path: /configuration\nInclude-Resource: configuration=src/main/resources/configuration\nLiferay-Releng-Module-Group-Description:\nLiferay-Releng-Module-Group-Title: Data Management\n\\end{verbatim}\n\nNote that Liferay-specific Bnd instructions are prefixed with\n\\texttt{Liferay} to avoid conflicts.\n\nThere are two different configuration types in\n\\href{https://osgi.org/javadoc/r4v42/org/osgi/service/cm/ConfigurationAdmin.html}{OSGi's\n\\texttt{ConfigurationAdmin}}: single, and factory. Factory\nconfigurations can have several configuration instances per factory\nname. Liferay DXP uses factory configurations. You must provide a\nfactory configuration's default values in a \\texttt{*.properties} file.\nIn this properties file, use a suffix on the end of the PID (persistent\nidentifier) and then provide your settings. For example, the following\ncode uses the \\texttt{-staging} suffix on the PID and creates a CXF\nendpoint at the context path \\texttt{/staging-ws}:\n\n\\texttt{com.liferay.portal.remote.cxf.common.configuration.CXFEndpointPublisherConfiguration-staging.properties}:\n\n\\begin{verbatim}\ncontextPath=/staging-ws\n\\end{verbatim}\n\nAs another example, the following code uses the suffix\n\\texttt{-stagingjaxws} on the PID and creates a SOAP extender at the\ncontext path \\texttt{/staging-ws}. This code also includes settings for\nthe configuration fields \\texttt{jaxWsHandlerFilterStrings} and\n\\texttt{jaxWsServiceFilterStrings}:\n\n\\texttt{com.liferay.portal.remote.soap.extender.internal.configuration.SoapExtenderConfiguration-stagingjaxws.properties}:\n\n\\begin{verbatim}\ncontextPaths=/staging-ws\njaxWsHandlerFilterStrings=(staging.jax.ws.handler=true)\njaxWsServiceFilterStrings=(staging.jax.ws.service=true)\n\\end{verbatim}\n\nYou must then use these configuration fields in the configuration class.\nFor example, the \\texttt{SoapExtenderConfiguration} interface below\ncontains the configuration fields \\texttt{contextPaths},\n\\texttt{jaxWsHandlerFilterStrings}, and\n\\texttt{jaxWsServiceFilterStrings}:\n\n\\begin{verbatim}\n@ExtendedObjectClassDefinition(\n    category = \"foundation\", factoryInstanceLabelAttribute = \"contextPaths\"\n)\n@Meta.OCD(\n    factory = true,\n    id = \"com.liferay.portal.remote.soap.extender.internal.configuration.SoapExtenderConfiguration\",\n    localization = \"content/Language\", name = \"soap.extender.internal.configuration.name\"\n)\npublic interface SoapExtenderConfiguration {\n\n    @Meta.AD(required = false)\n    public String[] contextPaths();\n\n    @Meta.AD(name = \"jax.ws.handler.filters\", required = false)\n    public String[] jaxWsHandlerFilterStrings();\n\n    @Meta.AD(name = \"jax.ws.service.filters\", required = false)\n    public String[] jaxWsServiceFilterStrings();\n\n    @Meta.AD(name = \"soap.descriptor.builder\", required = false)\n    public String soapDescriptorBuilderFilter();\n\n}\n\\end{verbatim}\n\nNext, you'll learn how to publish JAX-WS web services.\n\n\\section{Publishing JAX-WS Web\nServices}\\label{publishing-jax-ws-web-services}\n\nTo publish JAX-WS web services via SOAP in a module, annotate the class\nand its methods with standard JAX-WS annotations, and then register it\nas a service in the OSGi framework. For example, the following class\nuses the \\texttt{@WebService} annotation for the class and\n\\texttt{@WebMethod} annotations for its methods. You must also set the\n\\texttt{jaxws} property to \\texttt{true} in the OSGi \\texttt{@Component}\nannotation:\n\n\\begin{verbatim}\nimport javax.jws.WebMethod;\nimport javax.jws.WebService;\n\nimport org.osgi.service.component.annotations.Component;\n\n@Component(\n    immediate = true, property = \"jaxws=true\", service = Calculator.class\n)\n@WebService\npublic class Calculator {\n\n    @WebMethod\n    public int divide(int a, int b) {\n        return a / b;\n    }\n\n    @WebMethod\n    public int multiply(int a, int b) {\n        return a * b;\n    }\n\n    @WebMethod\n    public int subtract(int a, int b) {\n        return a - b;\n    }\n\n    @WebMethod\n    public int sum(int a, int b) {\n        return a + b;\n    }\n\n}\n\\end{verbatim}\n\nYou should also make sure that you include \\texttt{org.osgi.core} and\n\\texttt{org.osgi.service.component.annotations} as dependencies to your\nproject.\n\n\\chapter{GraphQL APIs}\\label{graphql-apis}\n\nLiferay DXP exposes a full\n\\href{https://graphql.github.io/graphql-spec/June2018/}{GraphQL API}\nimplementation, and lets your apps fetch several entities from the same\nrequest.\n\nHere you'll learn how to navigate and consume Liferay DXP's GraphQL\nAPIs. Since GraphQL APIs are discoverable, you'll start with that.\n\n\\chapter{Get Started: Discover the\nAPI}\\label{get-started-discover-the-api}\n\nTo begin consuming the GraphQL APIs, you must first know where they are,\nwhat operations you can invoke, and how to invoke them.\n\nBecause Liferay DXP's GraphQL APIs leverage the\n\\href{https://graphql.github.io/graphql-spec/June2018/}{official\nspecification}, you don't need a service catalog. You only need to know\nthe URL from which to discover the rest of the API.\n\nLiferay DXP's GraphQL APIs are available here:\n\n\\begin{verbatim}\nhttp://[host]:[port]/o/graphql\n\\end{verbatim}\n\nFor example, if you're running Liferay DXP locally on port\n\\texttt{8080}, the URL for discovering the GraphQL API is\n\n\\begin{verbatim}\nhttp://localhost:8080/o/graphql\n\\end{verbatim}\n\nTo inspect the GraphQL endpoint, use a GraphQL client, such as\n\\href{https://chrome.google.com/webstore/detail/altair-graphql-client/flnheeellpciglgpaodhkhmapeljopja?hl=en}{Altair}\n(a Chrome extension) or\n\\href{https://github.com/graphql/graphiql}{GraphiQL}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/graphql-altair.png}\n\\caption{GraphQL APIs can be browsed in Altair.}\n\\end{figure}\n\nYou don't have to be authenticated to inspect the live documentation,\nbut you must to be able to make requests. There are several ways of\nauthenticating in GraphQL APIs (explained\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/authenticated-requests}{here})\nbut the simplest way to test APIs locally is to use Basic\nAuthentication, setting an \\texttt{Authorization} header in Altair\n(first icon on the left). Remember that Basic Auth is a BASE64\ntransformation of \\texttt{user}:\\texttt{password}. This means it's\ninsecure, and should never be used in production.\n\nMost tools that introspect the GraphQL schema can autocomplete your\nquery or fill all the fields in for you.\n\nFor a list of tools such as client generators, validators, and parsers\nsupporting GraphQL, see\n\\href{https://github.com/chentsulin/awesome-graphql}{Awesome GraphQL}.\nLeveraging GraphQL provides standards support, extensive automatic\ndocumentation, and industry-wide conventions.\n\n\\section{Unique endpoint and\nversioning}\\label{unique-endpoint-and-versioning}\n\nIn contrast with the REST APIs, where endpoints are deployed by suite\n(headless-delivery, headless-admin-user\\ldots), GraphQL APIs are\ndeployed under the same endpoint (/o/graphql). That way we can easily\nadd relationships between entities to leverage GraphQL's powerful\nrequest characteristics.\n\nLiferay DXP's GraphQL APIs also expose the latest published version of\nall entities available. If several versions of the same entity are\ndeployed, only the latest one is exposed under the \\texttt{/o/graphql}\nendpoint (REST APIs use different endpoints for different versions).\nThis strategy follows GraphQL standards to avoid breaking versions by\nmarking deprecated fields and always adding properties to an entity.\n\n\\chapter{Get Started: Invoke a\nService}\\label{get-started-invoke-a-service}\n\nOnce you know which API you want to call via the GraphQL-introspected\ndocumentation, you can send a request using a POST body. For example,\nsuppose you want to retrieve all the blog entries from a Site. If you\nconsult the GraphQL documentation you can find this endpoint:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/graphql-blog-postings.png}\n\\caption{GraphQL exposes a definition for BlogPostings.}\n\\end{figure}\n\nIf you add the full query with Altair/GraphiQL, you'll see a result like\nthis:\n\n\\begin{verbatim}\nquery{\n  blogPostings(filter: ______, page: ______, pageSize: ______, search: ______, siteId: ______, siteKey: ______, sort: ______){\n    items\n    page\n    pageSize\n    totalCount\n  }\n}\n\\end{verbatim}\n\nThe only required parameter is \\texttt{siteId} or \\texttt{siteKey} (as\nof 7.2 FP4), the ID, or the internal name (like \\emph{guest}) of the\nblog posting's Site. Internally, the \\texttt{siteId} is a\n\\texttt{groupId} that you can retrieve from the database, a URL, or\nLiferay DXP's UI via the Site Administration menu. For more information,\nsee\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/how-to-get-site-id}{How to\nget SiteId}.\n\nA regular query would ignore optional parameters and return more\nelements of the list, identified by the property \\texttt{items}:\n\n\\begin{verbatim}\nquery {\n  blogPostings(siteKey: \"guest\") {\n    items {\n      alternativeHeadline\n      articleBody\n      creator {\n        name\n      }\n      dateCreated\n      dateModified\n      datePublished\n      description\n      encodingFormat\n      friendlyUrlPath\n      headline\n      id\n      keywords\n      numberOfComments\n      relatedContents {\n        title\n      }\n      siteId\n      taxonomyCategoryIds\n      viewableBy\n    }\n    page\n    pageSize\n    totalCount\n  }\n}\n\\end{verbatim}\n\nIn GraphQL, you must list explicitly every field you want to return in\nthe request. Complex objects, like \\texttt{items}, \\texttt{creator}, or\n\\texttt{relatedContents} can not be returned fully: you must specify at\nleast one field (or none).\n\nTo execute the query, make a \\texttt{POST} request with an\n\\texttt{Authentication} header and the query in a JSON object under the\nkey \\texttt{query}. Don't forget to escape strings!\n\nThe following request gets the Site's blog postings by providing the\nsite key (\\texttt{guest}):\n\n\\begin{verbatim}\ncurl -X \"POST\" \"http://localhost:8080/o/graphql\" \\\n     -H 'Content-Type: text/plain; charset=utf-8' \\\n     -u 'test@liferay.com:test' \\\n     -d $'{\n  \"query\": \"query { blogPostings(siteKey: \\\\\"guest\\\\\") { items { alternativeHeadline articleBody creator { id name } dateCreated dateModified datePublished description encodingFormat friendlyUrlPath headline id keywords numberOfComments relatedContents { title } siteId taxonomyCategoryIds viewableBy } page pageSize totalCount } }\"\n  }'\n\\end{verbatim}\n\nIf you send this request to a Site that contains some Blog entries, the\nresponse may look like this:\n\n\\begin{verbatim}\n{\n  \"data\": {\n    \"blogPostings\": {\n      \"items\": [\n        {\n          \"alternativeHeadline\": \"\",\n          \"articleBody\": \"<p>Content</p>\",\n          \"creator\": {\n            \"id\": 20124,\n            \"name\": \"Test Test\"\n          },\n          \"dateCreated\": \"2019-10-29T17:48:03Z\",\n          \"dateModified\": \"2019-10-29T17:48:03Z\",\n          \"datePublished\": \"2019-10-29T17:47:00Z\",\n          \"description\": \"\",\n          \"encodingFormat\": \"text/html\",\n          \"friendlyUrlPath\": \"title\",\n          \"headline\": \"Title\",\n          \"id\": 37644,\n          \"keywords\": [],\n          \"numberOfComments\": 0,\n          \"relatedContents\": [],\n          \"siteId\": 20118,\n          \"taxonomyCategoryIds\": null,\n          \"viewableBy\": null\n        }\n      ],\n      \"page\": 1,\n      \"pageSize\": 20,\n      \"totalCount\": 1\n    }\n  }\n}\n\\end{verbatim}\n\nThis response is a JSON object with information about the collection of\nblogs. The attributes contain information about the resource (blogs, in\nthis case). Also, note that the results are paginated. The\n\\texttt{*page*} attributes refer to pages of results. Here's a\ndescription of some common attributes:\n\n\\texttt{id}: Each item has an ID. You can use the ID to retrieve more\ninformation about that item. For example, there are two \\texttt{id}\nattributes in the above response: one for the blog posting\n(\\texttt{37644}) and one for the blog post's creator (\\texttt{20124}).\n\n\\texttt{page}: The current page's page number. The page in the above\nresponse is \\texttt{1}.\n\n\\texttt{pageSize}: The possible number of this resource's items to be\nincluded in a single page. In the above response this is \\texttt{20}.\n\n\\texttt{totalCount}: The total number of this resource's existing items\n(independent of pagination). The above response lists the total number\nof blog postings (\\texttt{1}) in a Site.\n\nTo get information on a specific blog posting, send a POST request with\nthe \\texttt{blogPostingId} to this query:\n\n\\begin{verbatim}\nquery {\n  blogPosting(blogPostingId: 37644) {\n    headline\n    id\n  }\n}\n\\end{verbatim}\n\n\\section{GraphQL Clients}\\label{graphql-clients}\n\nThe examples above show the GraphQL requests as cURL operations but you\ncan use any GraphQL clients available. We have made heavy use of\n\\href{https://www.apollographql.com/docs/}{Apollo}, the official React\nclient and the \\href{https://github.com/vuejs/vue-apollo}{Vue\nintegration} without any issues.\n\n\\chapter{Making Authenticated\nRequests}\\label{making-authenticated-requests-1}\n\nTo make an authenticated request, you must authenticate as a specific\nUser.\n\nThere are three authentication mechanisms available when invoking web\nAPIs:\n\n\\textbf{Basic Authentication:} Sends the user credentials as an encoded\nuser name and password pair. This is the simplest authentication\nprotocol (available since HTTP/1.0), but should be used only for\ndevelopment purposes, as it's insecure.\n\n\\textbf{OAuth 2.0:} In 7.0, you can use OAuth 2.0 for authorization. See\nthe \\href{/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0}{OAuth 2.0\ndocumentation} for more information.\n\n\\textbf{Cookie/Session authentication:} From inside the portal you can\ndo direct requests to the APIs by sending the session token.\n\nFirst, you'll learn how to send requests with basic authentication.\n\n\\section{Basic Authentication}\\label{basic-authentication-1}\n\nBasic authentication requires that you send an HTTP\n\\texttt{Authorization} header containing the encoded user name and\npassword. You must first get that encoded value. To do so, you can use\n\\texttt{openssl} or a \\texttt{Base64} encoder. Either way, you must\nencode the \\texttt{user:password} string. Here's an example of the\n\\texttt{openssl} command for encoding the \\texttt{user:password} string\nfor a user \\texttt{test@liferay.com} with the password \\texttt{Liferay}:\n\n\\begin{verbatim}\nopenssl base64 <<< test@liferay.com:Liferay\n\\end{verbatim}\n\nThis returns the encoded value:\n\n\\begin{verbatim}\ndGVzdEBsaWZlcmF5LmNvbTpMaWZlcmF5Cg==\n\\end{verbatim}\n\nIf you don't have \\texttt{openssl} installed, try the \\texttt{base64}\ncommand:\n\n\\begin{verbatim}\nbase64 <<< test@liferay.com:Liferay\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Encoding a string as shown here does not encrypt the\nresulting string. The encoded string can easily be decoded by executing\n\\texttt{base64\\ \\textless{}\\textless{}\\textless{}\\ the-encoded-string},\nwhich returns the original string.\n\nAnyone listening to your request could therefore decode the\n\\texttt{Authorization} header and reveal your user name and password. To\nprevent this, ensure that all communication is made through HTTPS, which\nencrypts the entire message (including headers).\n\n\\noindent\\hrulefill\n\nUse the encoded value for the HTTP Authorization header when sending the\nrequest:\n\n\\begin{verbatim}\ncurl -H \"Authorization: Basic dGVzdEBsaWZlcmF5LmNvbTpMaWZlcmF5Cg==\" http://localhost:8080/o/graphql ...\n\\end{verbatim}\n\nThe response contains data instead of the 403 error that an\nunauthenticated request receives. For more information on the response's\nstructure, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data}{Working\nwith Collections of Data}.\n\n\\section{OAuth 2.0 Authorization}\\label{oauth-2.0-authorization-1}\n\n7.0 supports authorization via OAuth 2.0, which is a token-based\nauthorization mechanism. For more details, see\n\\href{/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0}{Liferay DXP's OAuth\n2.0 documentation}. The following sections show you how to use OAuth 2.0\nto authenticate web API requests.\n\n\\section{Obtaining the OAuth 2.0\nToken}\\label{obtaining-the-oauth-2.0-token-1}\n\nBefore using OAuth 2.0 to invoke a web API, you must register your\napplication (your web API's consumer) as an authorized OAuth client. To\ndo this, follow the instructions in\n\\href{/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0\\#creating-an-application}{Creating\nan Application}. When creating the application, fill in the form as\nfollows:\n\n\\textbf{Application Name:} Your application's name.\n\n\\textbf{Client Profile:} Headless Server.\n\n\\textbf{Allowed Authorization Types:} Check \\emph{Client Credentials}.\n\nAfter clicking \\emph{Save} to finish creating the application, write\ndown the Client ID and Client Secret values that appear at the top of\nthe form.\n\nNext, you must get an OAuth 2.0 access token. To do this, see\n\\href{/docs/7-2/deploy/-/knowledge_base/d/authorizing-account-access-with-oauth2}{Authorizing\nAccount Access with OAuth 2}.\n\n\\section{Invoking the Service with an OAuth 2.0\nToken}\\label{invoking-the-service-with-an-oauth-2.0-token-1}\n\nOnce you have a valid OAuth 2.0 token, include it in the request's\n\\texttt{Authorization} header, specifying that the authentication type\nis a \\href{https://tools.ietf.org/html/rfc6750}{bearer token}:\n\n\\begin{verbatim}\ncurl -H \"Authorization: Bearer d5571ff781dc555415c478872f0755c773fa159\" http://localhost:8080/o/graphql\n\\end{verbatim}\n\nThe response contains the resources that the authenticated user has\npermission to access, just like the response from Basic authentication.\nThe request could be prevented depending on the scopes defined. If POST\na GraphQL query and there is scope disabling all request except\n\\texttt{GET}, you see a 403.\n\n\\section{Using Cookie Authentication or doing a request from the\nportal}\\label{using-cookie-authentication-or-doing-a-request-from-the-portal}\n\nYou can call the GraphQL APIs using the existing session from outside\nthe Liferay DXP by passing the session identifier (the cookie reference)\nand the Liferay Auth Token (a CSRF---Cross-Site Request\nForgery---token).\n\nTo make an unauthenticated request from outside the Liferay DXP you must\nprovide the \\texttt{Cookie} identifier in the header:\n\n\\begin{verbatim}\ncurl -H 'Cookie: JSESSIONID=27D7C95648D7CDBE3347601FC4543F5D'\n\\end{verbatim}\n\nYou must also provide the CSRF token by passing it as a query parameter\ncalled \\texttt{p\\_auth} or by adding the URL to the whitelist of CSRF\nallowed URLs or disabling CSRF checks altogether with the\n\\texttt{auth.verifier.auth.verifier.PortalSessionAuthVerifier.check.csrf.token}\nproperty (application level).\n\nHere's a sample cURL request with the cookie and CSRF token:\n\n\\begin{verbatim}\ncurl -H 'Cookie: JSESSIONID=27D7C95648D7CDBE3347601FC4543F5D' http://localhost:8080/o/graphql?p_auth=O4dCU1Mj\n\\end{verbatim}\n\nTo do an unauthenticated request from inside the Liferay DXP, from\nJavaScript code or a Java method, you don't need the session identifier.\nYou must only provide the CRSF token or add the API to the whitelist of\nCSRF allowed URLs.\n\n\\section{Making Unauthenticated\nRequests}\\label{making-unauthenticated-requests-1}\n\nUnauthenticated requests are disabled by default in Liferay DXP's\nGraphQL APIs. As all GraphQL APIs share the same endpoint, you cannot\nhave the same level of granularity with Service Access Policies as in\nREST APIs. For that reason, we do not recommend disabling the security\nof the GraphQL APIs.\n\n\\section{Related Topics}\\label{related-topics-136}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/get-started-invoke-a-service}{Get\nStarted: Invoke a Service}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data}{Working\nwith Collections of Data}\n\n\\chapter{Working with Collections of\nData}\\label{working-with-collections-of-data-1}\n\nCollection resources are common in Liferay DXP web APIs. If you followed\nalong with the previous examples that sent requests to the portal's\n\\texttt{blog-postings} resource URL, you've already seen collections in\naction: the \\texttt{BlogPosting} resource is a collection.\n\nHere, you'll learn more detailed information about working with\ncollection resources. But first, you should learn about collection\npagination.\n\n\\section{Pagination}\\label{pagination-2}\n\nA small collection can be transmitted in a single response without\ndifficulty. Transmitting a large collection all at once, however, can\nconsume too much bandwidth, time, and memory. It can also overwhelm the\nuser with too much data.\n\nIt's therefore best to get and display the elements of a large\ncollection in discrete chunks, or pages.\n\nLiferay DXP's GraphQL APIs return paginated collections by default. The\nfollowing attributes in the responses also contain the information\nneeded to navigate between those pages:\n\n\\texttt{totalCount}: The total number of this resource's items.\n\n\\texttt{pageSize}: The number of this resource's items to be included in\nthis response.\n\n\\texttt{page}: The current page's number.\n\n\\texttt{items}: The collection elements present on this page. Each\nelement also contains the data of the object it represents, so there's\nno need for additional requests for individual elements.\n\n\\texttt{id}: Each item's identifier. You can use this, if necessary, to\nget more information on a specific item.\n\nThe attributes \\texttt{page} and \\texttt{pageSize} allow client\napplications to navigate through the results. For example, such a client\ncould send a request for a specific page.\n\nThis example gets the second page (\\texttt{page:2}) of blog postings\nthat exist on the content set with the ID \\texttt{42345}:\n\n\\begin{verbatim}\nquery {\n  contentSetContentSetElements(contentSetId: 42345, page: 2) {\n    items {\n      id\n      title\n      content {\n        ... on BlogPosting {\n          headline\n        }\n      }\n    }\n    page\n    pageSize\n    totalCount\n  }\n}\n\\end{verbatim}\n\n\\chapter{Mutations}\\label{mutations}\n\nThe GraphQL spec differentiates between retrieve operations\n(\\texttt{query}) and create/update/delete operations\n(\\texttt{mutations}).\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/graphql-mutation.png}\n\\caption{The GraphQL Mutations list for Blog postings shows the possible\noperations.}\n\\end{figure}\n\nTo perform a mutation, do a \\texttt{POST} request as you did with\n\\texttt{query} operations, using cURL, a REST client, or a GraphQL\nClient like Apollo. The only difference is that create/update\n\\texttt{mutations} require an Input type, a JSON object to create or\nupdate the content.\n\nA create \\texttt{mutation} to insert a new blog posting looks like this:\n\n\\begin{verbatim}\nmutation {\n  createSiteBlogPosting(\n    blogPosting: {\n      headline: \"New GraphQL APIs!\"\n      articleBody: \"WoW! This is cool!\"\n    }\n    siteKey: \"guest\"\n  ) {\n    id\n    headline\n  }\n}\n\\end{verbatim}\n\nAuto-complete also works as expected filling the \\texttt{blogPosting}\nobject. Here's a cURL request to create the same entry:\n\n\\begin{verbatim}\ncurl 'http://localhost:8080/o/graphql' -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic dGVzdEBsaWZlcmF5LmNvbTp0ZXN0' --data-binary '{\"query\":\"mutation {createSiteBlogPosting(blogPosting: {headline: \\\"New GraphQL APIs!\\\" articleBody: \\\"WoW! This is cool!\\\"} siteKey: \\\"guest\\\") {id headline}}\",\"variables\":{}}'\n\\end{verbatim}\n\n\\chapter{Fragments and Node Patterns}\\label{fragments-and-node-patterns}\n\nLiferay DXP's GraphQL APIs also supports\n\\href{https://graphql.org/learn/queries/\\#fragments}{GraphQL fragments},\nreusable sets of fields that are needed in different requests. A special\ntype of fragments are\n\\href{https://graphql.org/learn/queries/\\#inline-fragments}{inline\nfragments}, which access the underlying concrete type when querying\ngeneric types or interfaces.\n\nYou'll use inline fragments to query objects that inherit from a common\ninterface, like the kind of objects returned from a \\texttt{ContentSet}.\n\\texttt{ContentSet}s allow defining lists of assets that comply with a\nset of rules, a segment, or are manually selected. \\texttt{ContentSet}s\ncan return any type of asset. This makes them a perfect fit for inline\nfragments.\n\nHere's an example of GraphQL querying \\texttt{ContentSet}s:\n\n\\begin{verbatim}\nquery {\n  contentSetContentSetElements(contentSetId: 42345) {\n    items {\n      id\n      title\n      content {\n        ... on BlogPosting {\n          headline\n        }\n        ... on StructuredContent {\n          relatedContents {\n            id\n            title\n          }\n        }\n      }\n    }\n    page\n    pageSize\n    totalCount\n  }\n}\n\\end{verbatim}\n\nThis query returns a set of objects, each of a different type.\n\n\\section{Node pattern}\\label{node-pattern}\n\n\\texttt{graphQLNode} is a special query that leverages the power of\ninline fragments. This query accepts a \\texttt{dataType} and an ID and\nreturns any kind of entity that has a query of the type\n\\texttt{\\{dataType\\}} and receives an \\texttt{id} as a parameter. Inline\nfragments can specify the fields you want to return in this special\ncase:\n\n\\begin{verbatim}\nquery{\n  graphQLNode(dataType: ______, id: ______){\n    id\n    ... on BlogPosting {\n      headline\n    }\n  }\n}\n\\end{verbatim}\n\nYou can also use \\texttt{graphQLNode} as a field in entities that\ncontain \\texttt{contentType} and \\texttt{id} properties. Those entities\nhave a generated property called \\texttt{GraphQLNode} that can return\nany type, queried by using inline fragments. A common use case is\nreturning an asset linked as \\texttt{relatedContents} (asset links).\n\n\\chapter{Language Negotiation}\\label{language-negotiation-1}\n\nThe same mechanism for requesting content in another language in the\nheadless REST APIs is used in GraphQL.\n\nAPIs available in different languages return the options in a block\ncalled \\texttt{availableLanguages}. For example, this block lists U.S.\nEnglish (\\texttt{en-US}) and Spain/Castilian Spanish (\\texttt{es-ES}):\n\n\\begin{verbatim}\n{\n  \"availableLanguages\": [\n    \"en-US\",\n    \"es-ES\"\n  ],\n  \"contentFields\": [\n    {\n      \"dataType\": \"html\",\n      \"name\": \"content\",\n      \"repeatable\": false,\n      \"value\": {\n        \"data\": \"<p>The main reason is because Headless APIs have been designed with real use cases in mind...</p>\"\n      }\n    }\n  ],\n  \"contentStructureId\": 36801,\n  \"creator\": {\n    \"familyName\": \"Test\",\n    \"givenName\": \"Test\",\n    \"id\": 20130,\n    \"name\": \"Test Test\",\n    \"profileURL\": \"/web/test\"\n  },\n  \"dateCreated\": \"2019-04-22T10:29:40Z\",\n  \"dateModified\": \"2019-04-22T10:30:31Z\",\n  \"datePublished\": \"2019-04-22T10:28:00Z\",\n  \"friendlyUrlPath\": \"why-headless-apis-are-better-than-json-ws-services-\",\n  \"id\": 59325,\n  \"key\": \"59323\",\n  \"numberOfComments\": 0,\n  \"renderedContents\": [\n    {\n      \"renderedContentURL\": \"http://localhost:8080/o/headless-delivery/v1.0/structured-contents/59325/rendered-content/36804\",\n      \"templateName\": \"Basic Web Content\"\n    }\n  ],\n  \"siteId\": 20124,\n  \"title\": \"Why Headless APIs are better than JSON-WS services?\",\n  \"uuid\": \"e1c4c152-e47c-313f-2d16-2ee4eba5cd26\"\n}\n\\end{verbatim}\n\nTo request the content in another language, specify your desired locale\nin the request's \\texttt{Accept-Language} header:\n\n\\begin{verbatim}\ncurl \"http://localhost:8080/o/graphql\"  -H 'Accept-Language: es-ES'  -u 'test@liferay.com:test' ...\n\\end{verbatim}\n\n\\begin{verbatim}\n    {\n      \"availableLanguages\": [\n        \"en-US\",\n        \"es-ES\"\n      ],\n      \"contentFields\": [\n        {\n          \"dataType\": \"html\",\n          \"name\": \"content\",\n          \"repeatable\": false,\n          \"value\": {\n            \"data\": \"<p>La principal razón es porque las APIs Headless se han diseñado pensando en casos de uso reales...</p>\"\n          }\n        }\n      ],\n      \"contentStructureId\": 36801,\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T10:29:40Z\",\n      \"dateModified\": \"2019-04-22T10:30:31Z\",\n      \"datePublished\": \"2019-04-22T10:28:00Z\",\n      \"friendlyUrlPath\": \"%C2%BFpor-qu%C3%A9-las-apis-headless-son-mejores-que-json-ws-\",\n      \"id\": 59325,\n      \"key\": \"59323\",\n      \"numberOfComments\": 0,\n      \"renderedContents\": [\n        {\n          \"renderedContentURL\": \"http://localhost:8080/o/headless-delivery/v1.0/structured-contents/59325/rendered-content/36804\",\n          \"templateName\": \"Contenido web básico\"\n        }\n      ],\n      \"siteId\": 20124,\n      \"title\": \"¿Por qué las APIs Headless son mejores que JSON-WS?\",\n      \"uuid\": \"e1c4c152-e47c-313f-2d16-2ee4eba5cd26\"\n    }\n\\end{verbatim}\n\n\\section{Creating Content with Different\nLanguages}\\label{creating-content-with-different-languages-1}\n\nBy default, when sending a mutation request, the\n\\texttt{Accept-Language} header is used as the content's language. There\nis one exception, however. Some entities require the first request to be\nin the Site's default language. In such cases, the first request for a\ndifferent language results in an error.\n\nAfter creating a new resource, a new request in a different language\nadds that translation.\n\n\\chapter{Filter, Sort, and Search}\\label{filter-sort-and-search-1}\n\nYou can use Liferay DXP's headless GraphQL APIs to search for the\ncontent you want. You can also sort and filter content.\n\n\\section{Filter}\\label{filter-1}\n\nIt's often useful to filter large collections for the exact data that\nyou need. Not all collections, however, allow filtering. The ones that\nsupport it contain the optional parameter \\texttt{filter}. To filter a\ncollection based on the value of one or more fields, use the\n\\texttt{filter} parameter following a subset of the\n\\href{https://www.odata.org}{OData} standard.\n\nFiltering mainly applies to fields indexed as keywords in Liferay DXP's\nsearch. To find content by terms contained in fields indexed as text,\nyou should instead use \\hyperref[search]{search}.\n\n\\section{Comparison Operators}\\label{comparison-operators-1}\n\n\\noindent\\hrulefill\n\nOperator \\textbar{} Description \\textbar{} Example \\textbar{}\n\\texttt{eq} \\textbar{} Equal \\textbar{}\n\\texttt{addressLocality\\ eq\\ \\textquotesingle{}Redmond\\textquotesingle{}}\n\\textbar{} \\textbar{} Equal null \\textbar{}\n\\texttt{addressLocality\\ eq\\ null} \\textbar{} \\texttt{ne} \\textbar{} Not\nequal \\textbar{}\n\\texttt{addressLocality\\ ne\\ \\textquotesingle{}London\\textquotesingle{}}\n\\textbar{} \\textbar{} Not null \\textbar{}\n\\texttt{addressLocality\\ ne\\ null} \\textbar{} \\texttt{gt} \\textbar{}\nGreater than \\textbar{} \\texttt{price\\ gt\\ 20} \\textbar{} \\texttt{ge}\n\\textbar{} Greater than or equal \\textbar{} \\texttt{price\\ ge\\ 10}\n\\textbar{} \\texttt{lt} \\textbar{} Less than \\textbar{}\n\\texttt{dateCreated\\ lt\\ 2018-02-13T12:33:12Z} \\textbar{} \\texttt{le}\n\\textbar{} Less than or equal \\textbar{}\n\\texttt{dateCreated\\ le\\ 2012-05-29T09:13:28Z} \\textbar{}\n\\texttt{startswith} \\textbar{} Starts with \\textbar{}\n\\texttt{startswith(addressLocality,\\ \\textquotesingle{}Lond\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{Logical Operators}\\label{logical-operators-1}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}lll@{}}\n\\toprule\\noalign{}\nOperator & Description & Example \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{and} & Logical and &\n\\texttt{price\\ le\\ 200\\ and\\ price\\ gt\\ 3.5} \\\\\n\\texttt{or} & Logical or &\n\\texttt{price\\ le\\ 3.5\\ or\\ price\\ gt\\ 200} \\\\\n\\texttt{not} & Logical not & \\texttt{not\\ (price\\ le\\ 3.5)} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nNote that the \\texttt{not} operator requires a trailing space.\n\n\\section{Grouping Operators}\\label{grouping-operators-1}\n\n\\noindent\\hrulefill\n\nOperator \\textbar{} Description \\textbar{} Example \\textbar{}\n\\texttt{(\\ )} \\textbar{} Precedence grouping \\textbar{}\n\\texttt{(price\\ eq\\ 5)\\ or\\ (addressLocality\\ eq\\ \\textquotesingle{}London\\textquotesingle{})}\n\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{String Functions}\\label{string-functions-1}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}lll@{}}\n\\toprule\\noalign{}\nFunction & Description & Example \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{contains} & Contains &\n\\texttt{contains(title,\\textquotesingle{}edmon\\textquotesingle{})} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Lambda Operators}\\label{lambda-operators-1}\n\nLambda operators evaluate a boolean expression on a collection. They\nmust be prepended with a navigation path that identifies a collection.\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.2206}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.1618}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.6176}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nLambda Operator\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nExample\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{any} & Any &\n\\texttt{keywords/any(k:contains(k,\\textquotesingle{}substring1\\textquotesingle{}))} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nThe \\texttt{any} operator applies a boolean expression to each\ncollection element and evaluates to \\texttt{true} if the expression is\ntrue for any element.\n\n\\section{Escaping in Queries}\\label{escaping-in-queries-1}\n\nYou can escape a single quote in a value by escaping it with a\nbackslash. For example, to filter for a blog posting whose headline is\n\\texttt{New\\ Headless\\ APIs}, send this filter string to the filter\nparameter.\n\n\\begin{verbatim}\nfilter: \\\\\"headline eq \\'Title\\'\\\\\"\n\\end{verbatim}\n\nHere's an example of the full request:\n\n\\begin{verbatim}\ncurl -X \"POST\" \"http://localhost:8080/o/graphql\" \\\n     -H 'Content-Type: text/plain; charset=utf-8' \\\n     -H 'Cookie: COOKIE_SUPPORT=true; GUEST_LANGUAGE_ID=en_US; JSESSIONID=EFEEC1617529C7C85E8CCCE510B0F6CF' \\\n     -u 'test@liferay.com:test' \\\n     -d $'{\n  \"query\": \"query { blogPostings(siteKey: \\\\\"guest\\\\\", filter: \\\\\"headline eq \\'Title\\'\\\\\") { items {headline} page pageSize totalCount } }\"\n}'\n\\end{verbatim}\n\nAnd here's a possible response:\n\n\\begin{verbatim}\n{\n  \"items\": [\n    {\n      \"alternativeHeadline\": \"The power of OpenAPI & Liferay\",\n      \"articleBody\": \"<p>We are happy to announce...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T07:04:47Z\",\n      \"dateModified\": \"2019-04-22T07:04:51Z\",\n      \"datePublished\": \"2019-04-22T07:02:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"new-headless-apis\",\n      \"headline\": \"New Headless APIs\",\n      \"id\": 59301,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 1\n}\n\\end{verbatim}\n\n\\section{Filtering in Structured Content Fields\n(ContentField)}\\label{filtering-in-structured-content-fields-contentfield-1}\n\nTo filter for a \\texttt{ContentField} value (dynamic values created by\nthe end user), you must use the paths that are scoped to an individual\n\\texttt{ContentStructure}.\n\nFind the ID of the \\texttt{ContentStructure} and use it in place of\n\\texttt{\\{contentStructureId\\}} in this query:\n\n\\begin{verbatim}\n\"contentStructureStructuredContents\"\n\\end{verbatim}\n\n\\section{Search}\\label{search-3}\n\nYou can search large collections with keywords. Use search when you want\nresults from any field, rather than specific ones. To perform a search,\nuse the optional parameter \\texttt{search} followed by the search terms.\nFor example, this request searches for all the \\texttt{BlogEntry} fields\ncontaining Title:\n\n\\begin{verbatim}\ncurl -X \"POST\" \"http://localhost:8080/o/graphql\" \\\n     -H 'Content-Type: text/plain; charset=utf-8' \\\n     -u 'test@liferay.com:test' \\\n     -d $'{\n  \"query\": \"query { blogPostings(siteKey: \\\\\"guest\\\\\", search: \\\\\"Title\\\\\") { items {headline} page pageSize totalCount } }\"\n}'\n\\end{verbatim}\n\n\\begin{verbatim}\n{\n  \"items\": [\n    {\n      \"alternativeHeadline\": \"How to work with OAuth\",\n      \"articleBody\": \"<p>To configure OAuth...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T09:35:09Z\",\n      \"dateModified\": \"2019-04-22T09:35:09Z\",\n      \"datePublished\": \"2019-04-22T09:34:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"authenticated-requests\",\n      \"headline\": \"Authenticated requests\",\n      \"id\": 59309,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 1\n}\n\\end{verbatim}\n\n\\section{Sorting}\\label{sorting-1}\n\nCollection results can be sorted. Note, however, that not all\ncollections allow sorting. The ones that support it contain the optional\nparameter \\texttt{\\{lb\\}?sort\\{rb\\}} in their GraphQL definition.\n\nTo get sorted collection results, append\n\\texttt{sort:\\textbackslash{}\"\\textless{}param-name\\textgreater{}\\textbackslash{}\"}\nto the request URL. For example, appending\n\\texttt{sort:\\textbackslash{}\"title\\textbackslash{}\"} to the request URL\nsorts the results by title.\n\nThe default sort order is ascending (0-1, A-Z). To perform a descending\nsort, append \\texttt{:desc} to the parameter name. For example, to\nperform a descending sort by title, append\n\\texttt{sort:\\textbackslash{}\"title:desc\\textbackslash{}\"} to the\nrequest URL.\n\nTo sort by more than one parameter, separate the parameter names by\ncommas and put them in order of priority. For example, to sort first by\ntitle and then by creation date, append\n\\texttt{sort:\\textbackslash{}\"title,dateCreated\\textbackslash{}\"} to the\nrequest URL.\n\nTo specify a descending sort for only one parameter, you must explicitly\nspecify ascending sort order (\\texttt{:asc}) for the other parameters:\n\n\\begin{verbatim}\nsort:\\\"headline:desc,dateCreated:asc\\\"\n\\end{verbatim}\n\n\\section{Flatten}\\label{flatten-1}\n\nThe \\texttt{flatten} query parameter returns all resources and\ndisregards folders or other hierarchical classifications. Collection\nGraphQL specifications define if \\texttt{flatten} is available. Its\ndefault value is \\texttt{false}, so a document query to the root folder\nreturns only the documents in that folder.\n\nWith \\texttt{flatten} set to \\texttt{true}, the same query returns\ndocuments in any subfolders, regardless of how deeply those folders are\nnested. Setting \\texttt{flatten} set to \\texttt{true} and querying for\ndocuments in a Site's root folder returns all the documents in the Site.\n\n\\chapter{Multipart Requests}\\label{multipart-requests-1}\n\nSeveral mutations accept a binary file via a multipart request. For\nexample, the definition for posting a file to a \\texttt{DocumentFolder}\nspecifies a multipart request, \\texttt{Upload} type in GraphQL:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/graphql-mutation-upload.png}\n\\caption{Create Document accepts a \\texttt{multipartBody}.}\n\\end{figure}\n\nThe GraphQL specification doesn't support natively multipart uploads,\nbut an\n\\href{https://github.com/jaydenseric/graphql-multipart-request-spec}{extension}\ncontributed by the community covers that use case.\n\nLiferay's implementation includes that extension and allows uploading\nfiles.\n\nMultipart support in GraphQL is disabled by default. To enable it, add\nthe configuration to upload multipart files in the Liferay application's\n\\texttt{web.xml} file:\n\n\\begin{verbatim}\n<servlet>\n        <servlet-name>Module Framework Servlet</servlet-name>\n        <servlet-class>\n            com.liferay.portal.module.framework.ModuleFrameworkServletAdapter\n        </servlet-class>\n        <load-on-startup>1</load-on-startup>\n        <async-supported>true</async-supported>\n        <multipart-config>\n            <location>/tmp</location>\n            <max-file-size>20848820</max-file-size>\n            <max-request-size>418018841</max-request-size>\n            <file-size-threshold>1048576</file-size-threshold>\n        </multipart-config>\n</servlet>\n\\end{verbatim}\n\nTo test, use the Altair configuration to upload files. Use the selector\nto upload one or multiple files and define a variable in the query.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/graphql-mutation-upload-altair.png}\n\\caption{Creating a Document in Altair is easy with the selector.}\n\\end{figure}\n\n\\begin{verbatim}\nmutation($file: [Upload]) {\n  createSiteDocument(multipartBody: $file, siteKey: \"guest\") {\n    id\n    title\n  }\n}\n\\end{verbatim}\n\nThe variable above is \\texttt{file} because there's only one. If you\nwanted to upload several files, name the variable \\texttt{\\$files} and\neach file should have a numeric sequence: \\texttt{files.0},\n\\texttt{files.1}, \\texttt{files.2}, etc.\n\nAll multipart APIs allow sending a JSON file containing the file's\nmetadata (title, description, etc.). That parameter should be the second\nfile uploaded (defined using the \\texttt{file.0}, \\texttt{file.1}\nsyntax).\n\nFor example,\n\n\\begin{verbatim}\ndocument={\\\"title\\\": \\\"Alternative name\\\"}\"\n\\end{verbatim}\n\nAnd here's the response:\n\n\\begin{verbatim}\n{\n  \"data\": {\n    \"createSiteDocument\": {\n      \"id\": 37701,\n      \"title\": \"99-rest-generator.markdown\"\n    }\n  }\n}\n\\end{verbatim}\n\nThe cURL request is slightly different (Altair fills out the variables):\n\n\\begin{verbatim}\ncurl 'http://localhost:8080/o/graphql' -H 'Authorization: Basic dGVzdEBsaWZlcmF5LmNvbTp0ZXN0' \\\n-F operations='{\"query\":\"mutation($files: [Upload]) {createSiteDocument(multipartBody: $files, siteId: 20122) {id}}\",\"variables\": { \"files\": [null] } }' \\\n-F map='{ \"0\": [\"variables.files.0\"]}' \\\n-F 0=@\"99-rest-generator.markdown\"\n\\end{verbatim}\n\n\\chapter{Using GraphQL APIs}\\label{using-graphql-apis}\n\nLiferay DXP's GraphQL APIs are independent of clients and can be used\nwith any GraphQL client you want. The only usual requirements are\nsetting up the \\texttt{Authentication} header using OAuth, Cookie,\nBasic, etc.\n\nFor JavaScript applications, we recommend using\n\\href{https://www.apollographql.com/}{Apollo Client} or\n\\href{https://github.com/prisma-labs/graphql-request}{graphql-request},\nlike this:\n\n\\begin{verbatim}\nconst { GraphQLClient } = require('graphql-request');\n\nconst graphQLClient = new GraphQLClient('http://localhost:8080/o/graphql', {\n    headers: {\n        authorization: `Basic ${AUTHORIZATION_TOKEN}`\n    }\n});\n\nconst getDestinationsQuery = ` {\n  destinations: contentSetContentSetElements(contentSetId: ${DESTINATION_CONTENTSET_ID}) {\n    items {\n        id\n        title\n    }\n    page\n    pageSize\n    totalCount\n  }\n}`;\n\n...\n\nconst response = await graphQLClient.request(getDestinationsQuery);\n\\end{verbatim}\n\nHere are several examples of JavaScript applications using GraphQL APIs:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/dgomezg/liferay-frontend-samples/blob/master/lifeair-alexa-skill/}{Alexa\n  skill using GraphQL APIs}\n\\item\n  \\href{https://github.com/dgomezg/liferay-frontend-samples/tree/master/lifeair-react-showroom}{React\n  application using Apollo Client for React}\n\\item\n  \\href{https://github.com/dgomezg/liferay-frontend-samples/tree/master/lifeair-vue-showroom}{Vue\n  application using Apollo Client for Vue}\n\\end{itemize}\n\n\\chapter{REST Builder}\\label{rest-builder}\n\nREST Builder is Liferay DXP's tool to build REST and GraphQL APIs. It's\nbased on OpenAPI, following an\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/headless-rest-apis}{API-first\napproach}.\n\nUsing REST Builder takes only three steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Write the OpenAPI profile.\n\\item\n  Use REST builder to generate the scaffolding.\n\\item\n  Fill out the generated classes with your logic.\n\\end{enumerate}\n\nA good overview of the process is detailed\n\\href{https://help.liferay.com/hc/es/articles/360028748872-Generating-APIs-with-REST-Builder}{here}.\n\nWe'll see each step in detail but first, let's talk about why we want to\nuse REST Builder.\n\n\\section{Why we should use REST\nBuilder}\\label{why-we-should-use-rest-builder}\n\nThere are several reasons to prefer REST Builder over rolling our own\n\\href{https://help.liferay.com/hc/en-us/articles/360031902292-JAX-RS}{JAX-RS\nservices}. Some of them are the following:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Development speed: you avoid writing JAX-RS annotations, converters,\n  adding support for multipart or layers to organize your code.\n  Everything is generated.\n\\item\n  API scaffolding: pagination, filtering, searching, JSON writers, XML\n  generation, even unit, and integration tests are generated.\n\\item\n  GraphQL support out of the box: write your REST API and get a GraphQL\n  endpoint for free.\n\\item\n  Integration with Liferay's authentication pipelines: Basic, OAuth,\n  Cookie, CORS handling. You don't have to search manually for the user\n  or the company; everything is already there.\n\\item\n  JSON \\& XML support: APIs return whichever format the consumer\n  prefers.\n\\item\n  Consistency: all APIs follow the same rules and conventions, enforced\n  by REST builder.\n\\end{itemize}\n\n\\chapter{How to install REST Builder}\\label{how-to-install-rest-builder}\n\nUse the\n\\href{https://portal.liferay.dev/docs/7-2/reference/-/knowledge_base/r/rest-builder-gradle-plugin}{Gradle\nplugin} to install REST builder by adding this gradle configuration to\nyour project:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.rest.builder\", version: \"1.0.21\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.portal.tools.rest.builder\"\n\\end{verbatim}\n\nTo use it, run \\texttt{gradlew\\ buildREST}. Note that your Gradle\nwrapper may not be in your app's project directory, so you may need to\nuse \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade} to\nlocate it:\n\n\\begin{verbatim}\nblade gw buildREST\n\\end{verbatim}\n\nIf you want to use a specific version of REST builder, you can specify\nit explicitly:\n\n\\begin{verbatim}\ndependencies {\n    restBuilder group: \"com.liferay\", name: \"com.liferay.portal.tools.rest.builder\", version: \"1.0.30\"\n}\n\\end{verbatim}\n\n\\chapter{REST Builder \\& OpenAPI}\\label{rest-builder-openapi}\n\nREST Builder is based on\n\\href{https://swagger.io/specification/}{OpenAPI}, and its philosophy is\n``OpenAPI first'': first you write the profile and then you use it as\nthe base of your implementation.\n\nBut first you must create a project with two empty bundles (a Blade\ntemplate will follow soon). The bundles (-api and -impl) should have the\nfiles you are already used to: a \\texttt{build.gradle} and a\n\\texttt{bnd.bnd}. The novelty is two YAML files, a configuration file\n(\\texttt{rest-config.yaml}) and the OpenAPI profile\n(\\texttt{rest-openapi.yaml}). An example project is\n\\href{https://github.com/nhpatt/liferay-devcon-appointment}{here}.\n\nLet's see the configuration file in detail. In the root of the -impl\nproject we have to create a YAML file to specify paths and the basic\nconfiguration of our new API. A sample implementation would be:\n\n\\begin{verbatim}\napiDir: \"../headless-test-api/src/main/java\"\napiPackagePath: \"com.liferay.headless.test\"\napplication:\n    baseURI: \"/headless-test\"\n    className: \"HeadlessTestApplication\"\n    name: \"Liferay.Headless.Test\"\nauthor: \"Javier Gamarra\"\n\\end{verbatim}\n\nThis file specifies the path of the -api bundle, the java package that\nwe will use across all the bundles and the information of the JAX-RS\napplication: the path of our application, the name of the class and the\nJAX-RS name of our API.\n\nI've skipped two advanced features, generating a client and automated\ntests, will see them later.\n\nJust one step left, writing our OpenAPI profile.\n\n\\section{OpenAPI profile}\\label{openapi-profile}\n\nThe OpenAPI profile will be the source of all our APIs, in this file, we\nwill add the paths and entities of our API. First, we'll create a\n\\href{https://en.wikipedia.org/wiki/YAML}{YAML} file called\nrest-openapi.yaml. Writing YAML files is tricky so we recommend using\nthe \\href{http://editor.swagger.io/}{swagger editor} to do it, which\nvalidates the YAML file against YAML syntax and the\n\\href{https://github.com/OAI/OpenAPI-Specification}{OpenAPI\nspecification}.\n\nA simple OpenAPI profile that retrieves a fictitious entity might look\nlike this:\n\n\\begin{verbatim}\ncomponents:\n  schemas:\n    Entity:\n      description: A very simple entity\n      properties:\n        name:\n          description: The entity name.\n          type: string\n        id:\n          description: The entity ID.\n          type: integer\n      type: object\ninfo:\n  description: \"\"\n  title: \"My API\"\n  version: v1.0\nopenapi: 3.0.1\npaths:\n  \"/entities/{entityId}\":\n    get:\n      parameters:\n        - in: path\n          name: entityId\n          required: true\n          schema:\n            type: integer\n      responses:\n        200:\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Entity\"\n          description: \"\"\n      tags: [\"Entity\"]\n\\end{verbatim}\n\nAll OpenAPI profiles have three different sections: components, info,\nand paths. The easiest one is the information block. It contains the\nOpenAPI version, the title and the version of your API:\n\n\\begin{verbatim}\ninfo:\n  description: \"\"\n  title: \"My API\"\n  version: v1.0\nopenapi: 3.0.1\n\\end{verbatim}\n\nIndentations should be spaces. The\n\\href{http://editor.swagger.io/}{swagger editor} helps with formatting.\n\nThe components section specifies the schemas/entities to return or\naccept on your APIs. In this case, you define a schema called\n\\emph{Entity} that has two string fields: a name and an id.\n\n\\begin{verbatim}\ncomponents:\n  schemas:\n    Entity:\n      description: A very simple entity\n      properties:\n        name:\n          description: The entity name.\n          type: string\n        id:\n          description: The entity ID.\n          type: integer\n      type: object\n\\end{verbatim}\n\nThe OpenAPI specification defines\n\\href{https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md\\#schemaObject}{many\ntypes and fields} you can use in your schemas.\n\nThe other common type is \\texttt{\\$ref}, a\n\\href{https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md\\#referenceObject}{reference\ntype} that allows you to refer to an existing type like this:\n\n\\begin{verbatim}\n$ref: '#/components/schemas/Entity'\n\\end{verbatim}\n\nThe last block, called paths, defines the URLs that you'll expose in\nyour APIs, with the type of HTTP verbs, list of parameters, status\ncodes, etc.\n\n\\begin{verbatim}\npaths:\n  \"/entities/{entityId}\":\n    get:\n      parameters:\n        - in: path\n          name: entityId\n          required: true\n          schema:\n            type: integer\n      responses:\n        200:\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Entity\"\n          description: \"\"\n      tags: [\"Entity\"]\n\\end{verbatim}\n\nThe pattern above, \\texttt{\"/entities/\\{entity\\}\"}, follows a common\npattern in REST APIs. This is the endpoint that retrieves one element,\n\\texttt{\"/entities\"}. It returns a list of elements, and a POST request\ncreates one.\n\nFor every path, it is mandatory to add a tag that points to an existing\nschema to indicate where to generate your code. REST Builder creates a\nmethod inside the class \\texttt{{[}TAG{]}ResourceImpl.java}.\n\n\\section{Generation}\\label{generation}\n\nOnce you've written your OpenAPI configuration and profile, it's time to\ngenerate your scaffolding for REST and GraphQL.\n\nIn the -impl or in the root module folder, execute this command:\n\n\\begin{verbatim}\ngw buildREST\n\\end{verbatim}\n\nYou can use \\texttt{gw\\ bR} if you want to save a few keystrokes.\n\nIf everything's indented properly and the OpenAPI profile validates,\nREST Builder generates your JAX-RS resources and the GraphQL endpoint.\nNext, you'll see what has been generated and how to implement our\nbusiness logic.\n\n\\section{Examples}\\label{examples}\n\nHere's a complete example that defines all CRUD operations in OpenAPI.\n\n\\section{GET Collection}\\label{get-collection}\n\n\\begin{verbatim}\npaths:\n    \"/entities\":\n        get:\n            responses:\n                200:\n                    content:\n                        application/json:\n                            schema:\n                                items:\n                                    $ref: \"#/components/schemas/Entity\"\n                                type: array\n                    description: \"\"\n            tags: [\"Entity\"]\n\\end{verbatim}\n\n\\section{DELETE}\\label{delete}\n\n\\begin{verbatim}\npaths:\n    \"/entities/{entityId}\":\n        delete:\n            parameters:\n                - in: path\n                  name: entityId\n                  required: true\n                  schema:\n                      type: integer\n            responses:\n                204:\n                    content:\n                        application/json: {}\n                    description: \"\"\n            tags: [\"Entity\"]\n\\end{verbatim}\n\n\\section{POST}\\label{post}\n\n\\begin{verbatim}\npaths:\n    \"/entities\":\n        post:\n            requestBody:\n                content:\n                    application/json:\n                        schema:\n                            $ref: \"#/components/schemas/Entity\"\n            responses:\n                200:\n                    content:\n                        application/json:\n                            schema:\n                                $ref: \"#/components/schemas/Entity\"\n                    description: \"\"\n            tags: [\"Entity\"]\n\\end{verbatim}\n\n\\section{PUT}\\label{put}\n\n\\begin{verbatim}\npaths:\n    \"/entities/{entityId}\":\n        put:\n            parameters:\n                - in: path\n                  name: entityId\n                  required: true\n                  schema:\n                      type: integer\n            requestBody:\n                content:\n                    application/json:\n                        schema:\n                            $ref: \"#/components/schemas/Entity\"\n            responses:\n                200:\n                    content:\n                        application/json:\n                            schema:\n                                $ref: \"#/components/schemas/Entity\"\n                    description: \"\"\n            tags: [\"Entity\"]\n\\end{verbatim}\n\n\\section{Summary}\\label{summary}\n\nThere are more examples showcasing all the supported OpenAPI syntax\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/headless/headless-delivery/headless-delivery-impl/rest-openapi.yaml}{here}\nand\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/apps/headless/headless-admin-taxonomy/headless-admin-taxonomy-impl/rest-openapi.yaml}{here}.\nYour next step is to\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/rest-builder-develop}{create\nyour API}.\n\n\\chapter{Developing an API with REST\nBuilder}\\label{developing-an-api-with-rest-builder}\n\nAfter executing \\texttt{gw\\ buildREST}, you have two modules:\n\\emph{headless-test-api} and \\emph{headless-test-impl}.\n\n\\begin{itemize}\n\\item\n  Headless Test API contains the interfaces for your resources and the\n  POJOs of your schemas.\n\\item\n  Headless Test Impl contains your implementation and the JAX-RS\n  application.\n\\end{itemize}\n\nYour generated \\texttt{EntityResource} looks like this:\n\n\\begin{verbatim}\npublic interface EntityResource {\n\n    public Page<Entity> getEntitiesPage() throws Exception;\n\n    public Entity postEntity(Entity entity) throws Exception;\n\n    public void deleteEntity(Integer entityId) throws Exception;\n\n    public Entity getEntity(Integer entityId) throws Exception;\n\n    public Entity putEntity(Integer entityId, Entity entity) throws Exception;\n\n    //Context methods\n\n    public default void setContextAcceptLanguage(AcceptLanguage contextAcceptLanguage) {}\n\n    public void setContextCompany(Company contextCompany);\n\n    public default void setContextHttpServletRequest(HttpServletRequest contextHttpServletRequest) {}\n\n    public default void setContextHttpServletResponse(HttpServletResponse contextHttpServletResponse) {}\n\n    public default void setContextUriInfo(UriInfo contextUriInfo) {}\n\n    public void setContextUser(User contextUser);\n}\n\\end{verbatim}\n\nThese are generated methods you defined in the OpenAPI profile (the full\nset as displayed in the examples).\n\nREST builder also generates two implementation files, a base class, with\nall the JAX-RS, GraphQL and OpenAPI annotations and an empty\nimplementation, \\texttt{EntityResourceImpl}:\n\n\\begin{verbatim}\n@Component(\n    properties = \"OSGI-INF/liferay/rest/v1_0/entity.properties\",\n    scope = ServiceScope.PROTOTYPE, service = EntityResource.class\n)\npublic class EntityResourceImpl extends BaseEntityResourceImpl {\n}\n\\end{verbatim}\n\nThis is where you implement new methods, by overriding the base class\nimplementation and returning your code. For example, here's a prototype\nimplementation storing entities in a Map:\n\n\\begin{verbatim}\nMap<Integer, Entity> entities = new HashMap<>();\n\n@Override\npublic Entity getEntity(Integer entityId) throws Exception {\n    return entities.get(entityId);\n}\n\n@Override\npublic Page<Entity> getEntitiesPage() throws Exception {\n    return Page.of(entities.values());\n}\n\n@Override\npublic void deleteEntity(Integer entityId) throws Exception {\n    entities.remove(entityId);\n}\n\n@Override\npublic Entity postEntity(Entity entity) throws Exception {\n    entities.put(entity.getId(), entity);\n    return entity;\n}\n\n@Override\npublic Entity putEntity(Integer entityId, Entity entity) throws Exception {\n    entities.put(entity.getId(), entity);\n    return entity;\n}\n\\end{verbatim}\n\nFor the collection, you return a \\texttt{Page} object based on a list\nbut there are also utility methods that return the pagination\ninformation:\n\n\\begin{verbatim}\nPage.of(list, pagination, totalCount)\n\\end{verbatim}\n\nDon't touch the interfaces or the base classes (those are regenerated\nevery time you run REST Builder). Like\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}, you only have to maintain the implementation classes, and if\nyou change the API, run REST Builder again and the interfaces are\nupdated. Your business logic could call other REST APIs, use Service\nBuilder or another persistence mechanism.\n\n\\section{Development Cycle}\\label{development-cycle}\n\nWhile implementing your API's business logic, you'll typically improve\nyour API by adding parameters or other paths. For that, you'll modify\nthe OpenAPI profile and regenerate the API again calling the\n\\texttt{buildREST} command.\n\nThe cycle starts anew until you get to the final state and deploy your\nAPIs. They become available at this URL pattern:\n\n\\begin{verbatim}\nhttp://localhost:8080/o/[application class name]/[OpenAPI version]/\n\\end{verbatim}\n\nYou can also execute \\texttt{jaxrs:check} in the OSGi console to see all\nthe JAX-RS endpoints.\n\nGraphQL paths and entities are added automatically to the default\nGraphQL endpoint:\n\n\\begin{verbatim}\nlocalhost:8080/o/graphql \n\\end{verbatim}\n\nYou can disable GraphQL generation by adding\n\\texttt{generateGraphQL:\\ false} to your \\texttt{rest-config.yaml}\n(\\texttt{generateREST} controls the generation of the REST endpoints).\n\n\\section{Wrapping Up}\\label{wrapping-up}\n\nSo\\ldots{} that's all!\n\nWhen everything is ready, you might want to consider publishing your\nHeadless API to Swaggerhub so others can consume it. You can use the\nfollowing URL pattern for that:\n\n\\begin{verbatim}\nhttp://localhost:8080/o/[application name]/[application version]/openapi.yaml\n\\end{verbatim}\n\nThe URL for the example above, therefore, would be\n\n\\begin{verbatim}\nhttp://localhost:8080/o/headless-test/v1.0/openapi.yaml\n\\end{verbatim}\n\nThis URL has the content of \\texttt{rest-openapi.yaml} plus the classes\nthat REST Builder generated for you.\n\n\\chapter{Managing Collections in REST\nBuilder}\\label{managing-collections-in-rest-builder}\n\n\\section{Pagination}\\label{pagination-3}\n\nTo add pagination to your endpoints, add \\texttt{page} and\n\\texttt{pageSize} as query parameters to your OpenAPI profile, like\nthis:\n\n\\begin{verbatim}\n- in: query\n  name: page\n  schema:\n      type: integer\n- in: query\n  name: pageSize\n  schema:\n      type: integer\n\\end{verbatim}\n\nThose two parameters add a \\texttt{Pagination\\ pagination} parameter in\nthe method signature to restrict the number of entries to return in the\n\\texttt{Page.of} constructor.\n\nPagination is highly recommended for entities that can have many\nelements, to avoid very large requests.\n\n\\section{Filtering, sorting and\nsearching}\\label{filtering-sorting-and-searching}\n\nAdding support for filtering, sorting, and searching is trickier. The\nfirst step is to add the query parameters to the OpenAPI profile, like\nthis:\n\n\\begin{verbatim}\n- in: query\n  name: filter\n  schema:\n      type: string\n- in: query\n  name: search\n  schema:\n      type: string\n- in: query\n  name: sort\n  schema:\n      type: string\n\\end{verbatim}\n\nThe method signature then receives a Sort object, a Filter object, and\nstring with the search request. Those objects use\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/model-entity-indexing-framework}{Liferay's\nindexing framework}. This gives you many benefits, like support for\npermissions out-of-the-box and having to write very little code to\nachieve sort, filter, and search.\n\nSo first, you must make sure your entity is indexed and uses the\nindexing framework.\n\nOnce that's done you have three things to do:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Add an \\texttt{EntityModel} to translate between our indexing\n  framework and your code\n\\item\n  Inject your \\texttt{entityModel} into your resource implementation.\n\\item\n  Call Search utilities to avoid boilerplate code.\n\\end{enumerate}\n\n\\section{Add an EntityModel}\\label{add-an-entitymodel}\n\nThe \\texttt{EntityModel} is a class that translates the name the\nproperty has in your API to the name used to index it.\n\n\\begin{verbatim}\npublic class EntityEntityModel implements EntityModel {\n    public EntityEntityModel() {\n        _entityFieldsMap = EntityModel.toEntityFieldsMap(\n            new StringEntityField(\n                \"name\", locale -> Field.getSortableFieldName(Field.NAME))\n        );\n    }\n\n    @Override\n    public Map<String, EntityField> getEntityFieldsMap() {\n        return _entityFieldsMap;\n    }\n\n    private final Map<String, EntityField> _entityFieldsMap;\n}\n\\end{verbatim}\n\nThe \\texttt{EntityModel} decouples the way you filter/sort from the way\nyou index the information. You could use one field to sort, backed\ninternally by several indexed fields or vice-versa.\n\n\\section{Inject Your EntityModel}\\label{inject-your-entitymodel}\n\nInjecting your \\texttt{EntityModel} is really easy, our resource\nimplementation just has to implement the \\texttt{EntityModelResource}\ninterface.\n\nThis entity model is simple and doesn't have any dynamic fields, so you\ncan instantiate it directly and return it in the \\texttt{getEntityModel}\nmethod, like this:\n\n\\begin{verbatim}\n@Component(\n    properties = \"OSGI-INF/liferay/rest/v1_0/entity.properties\",\n    scope = ServiceScope.PROTOTYPE, service = EntityResource.class\n)\npublic class EntityResourceImpl extends BaseEntityResourceImpl implements\n    EntityModelResource {\n\n    ...\n\n    @Override\n    public EntityModel getEntityModel(MultivaluedMap multivaluedMap) {\n        return _entityEntityModel;\n    }\n\n    private EntityEntityModel _entityEntityModel = new EntityEntityModel();\n}\n\\end{verbatim}\n\n\\section{Call search utilities}\\label{call-search-utilities}\n\nFinally, you must call \\texttt{SearchUtil.search()} that links\neverything together. It requires these parameters:\n\n\\texttt{booleanQueryUnsafeConsumer}: a boolean query to restrict the\ninformation we want to retrieve.\n\n\\texttt{filter}: pass-through of the filter object.\n\n\\texttt{indexerClass}: the class of the entity that to filter/search.\n\n\\texttt{keywords}: pass-through of the search string.\n\n\\texttt{pagination}: pass-through of the pagination object (to read the\nrow requested).\n\n\\texttt{queryConfigUnsafeConsumer}: the configuration for the fields\nthat you want to return, typically the id to do a query later, in the\n\\texttt{transformUnsafeFunction}.\n\n\\texttt{searchContextUnsafeConsumer}: global configuration of the query.\n\n\\texttt{transformUnsafeFunction}: the function that transforms from\n\\texttt{Document} (of the indexing framework) to your entity, either\nsearching in the database, your persistence, another API, etc.\n\n\\texttt{sorts}: pass-through of the sorts object.\n\nThe code would be similar to this:\n\n\\begin{verbatim}\n@Override\npublic Page<Entity> getEntitiesPage(\n    String search, Filter filter, Pagination pagination, Sort[] sorts)\n    throws Exception {\n\n    return SearchUtil.search(\n        booleanQuery -> {},\n        filter, Entity.class, search, pagination,\n        queryConfig -> queryConfig.setSelectedFieldNames(\n            Field.ENTRY_CLASS_PK),\n        searchContext -> searchContext.setCompanyId(contextCompany.getCompanyId()),\n        document -> new Entity(), //FILL with your implementation\n        sorts);\n}\n\\end{verbatim}\n\n\\section{Using Your filter, search, and\nsort}\\label{using-your-filter-search-and-sort}\n\nLifeay uses OData to express our filter queries, following this\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/filter-sort-and-search\\#filter}{syntax}.\n\nAnd that's it! Now you can can filter/search and sort by the fields you\ndefined in your \\texttt{EntityModel}.\n\n\\chapter{REST Builder Scaffolding}\\label{rest-builder-scaffolding}\n\nApart from the JAX-RS annotations, basic structure and GraphQL support,\nthere are some other things the REST Builder provides:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Context Fields\n\\item\n  Automatic Transactions\n\\item\n  Test Generation\n\\item\n  Client Generation\n\\item\n  Common Utilities\n\\end{itemize}\n\nThese are useful and time saving additions to your development cycle.\n\n\\section{Context fields}\\label{context-fields}\n\nThe \\texttt{ResourceImpl} classes are JAX-RS-compliant resources. You\ncan add \\texttt{@Context} injections, mix JAX-RS endpoints, and use full\npower of the JAX-RS standard.\n\nREST Builder injects several fields (as protected fields in the base\nclass) to help you implement new APIs, like these:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{contextAcceptLanguage}, containing the current\n  \\texttt{Locale}.\n\\item\n  \\texttt{contextCompany}, containing the current \\texttt{Company}.\n\\item\n  \\texttt{contextHttpServletRequest}, containing the\n  \\texttt{HttpServletRequest}.\n\\item\n  \\texttt{contextHttpServletResponse}, containing the\n  \\texttt{HttpServletResponse}.\n\\item\n  \\texttt{contextUser}, containing the current logged-in \\texttt{User}.\n\\item\n  \\texttt{contextUriInfo}, containing the \\texttt{UriInfo} information\n  (paths, endpoints).\n\\end{itemize}\n\n\\section{Automatic transactions}\\label{automatic-transactions}\n\nOne little-known feature of REST Builder is that it wraps your API calls\nin transactions if the HTTP verb used is POST, PUT, PATCH or DELETE\n(doesn't apply for GET operations).\n\nIf you need to do several Service Builder calls in a sequence, you don't\nhave to wrap them in a transaction; it happens automatically. The\ntransaction commits if no exception is thrown and rolls back if an\nexception bubbles up to the resource implementation method.\n\n\\section{Test generation}\\label{test-generation}\n\nYou can generate integration tests if you specify a \\texttt{testDir}\nproperty:\n\n\\begin{verbatim}\ntestDir: \"../headless-admin-taxonomy-test/src/testIntegration/java\"\n\\end{verbatim}\n\nREST Builder generates integration tests under the -test module. Those\ntests check the API using a REST client. They check the response, doing\nan end-to-end test involving all steps from parsing the request to\nreturning JSON.\n\nThe generated tests are ignored by default and, depending on the path,\nmay force you to implement a creation method (to be able to add content\nto either update/delete or retrieve it).\n\nThe\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/apps/headless/headless-delivery/headless-delivery-test}{headless-delivery-test}\nproject contains many examples.\n\n\\section{Client generation}\\label{client-generation}\n\nYou can generate a Java client if you specify a \\texttt{clientDir}\nproperty:\n\n\\begin{verbatim}\nclientDir: \"../headless-admin-taxonomy-client/src/main/java\"\n\\end{verbatim}\n\nThis is a Java, typed, client that interacts with the APIs using static\nmethods. The client project contains all the methods to call your paths\nand parses the requests and responses.\n\n\\section{Common utilities}\\label{common-utilities}\n\nREST Builder provides several JAX-RS utilities, from exception mappers\nfor the most common exceptions in the portal, XML and JSON Body\nReaders/Writers, Site validation (that works with \\texttt{siteId} and\n\\texttt{siteKey}), and support for Bean Validation.\n\nThere are also utility libraries that can be useful when developing new\nAPIs:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{ContentLanguageUtil}, to deal with the\n  \\texttt{ContentLanguage} header.\n\\item\n  \\texttt{JaxRsLinkUtil}, to create links between APIs.\n\\item\n  \\texttt{LocalDateTimeUtil}, to transform between date formats.\n\\item\n  \\texttt{LocalizedMapUtil}, to create maps with locales as keys.\n\\item\n  \\texttt{SearchUtil}, to use the search framework to return Pages of\n  content.\n\\item\n  \\texttt{TransformUtil}, to deal with lambdas and conversions.\n\\end{itemize}\n\n\\chapter{Support for oneOf, anyOf and\nallOf}\\label{support-for-oneof-anyof-and-allof}\n\nOpenAPI 3.0 added several ways of using inheritance and composition to\ncreate complex schemas. Specifically, it added support for\n\\href{https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/}{\\emph{allOf},\n\\emph{anyOf,} and \\emph{oneOf}}, with these semantics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  allOf -- the value validates against all the subschemas\n\\item\n  anyOf -- the value validates against any of the subschemas\n\\item\n  oneOf -- the value validates against exactly one of the subschemas\n\\end{itemize}\n\nNext, you'll learn each option's syntax and how its code is generated.\n\n\\section{allOf}\\label{allof}\n\nWith \\texttt{allOf} you can use the power of composition to create an\nentity that combines several others. It's the most potent way of reusing\ncode without losing expressiveness and granularity: you can define small\nentities that can be reused by composing several to create a larger\nentity.\n\nTo use \\texttt{allOf} you must follow this syntax:\n\n\\begin{verbatim}\nEntityA:\n    properties:\n        nameA:\n            type: string\nEntityB:\n    properties:\n        nameB:\n            type: string\nEntityC:\n    allOf:\n        - $ref: '#/components/schemas/EntityA'\n        - $ref: '#/components/schemas/EntityB'\n\\end{verbatim}\n\nThis OpenAPI syntax generates the following Java code inside the\n\\texttt{EntityC} class:\n\n\\begin{verbatim}\n@Schema\n@Valid\npublic EntityA getEntityA() {\n    return entityA;\n}\n\n@Schema\n@Valid\npublic EntityB getEntityB() {\n    return entityB;\n}\n\\end{verbatim}\n\n\\section{oneOf}\\label{oneof}\n\n\\texttt{OneOf} is the simplest of the generics properties. It defines a\nproperty that can have different types. Since Java doesn't support Union\nTypes, use an Object to model the property:\n\n\\begin{verbatim}\nEntityA:\n    properties:\n        nameA:\n            type: string\nEntityB:\n    properties:\n        nameB:\n            anyOf:\n                - $ref: \"#/components/schemas/EntityA\"\n                - type: object\n                  properties:\n                      name:\n                          type: string\n\\end{verbatim}\n\nThis syntax generates the following Java code:\n\n\\begin{verbatim}\n@Schema\n@Valid\npublic Object getNameB() {\n    return nameB;\n}\n\\end{verbatim}\n\n\\section{anyOf}\\label{anyof}\n\nThe final generic keyword, \\texttt{anyOf} leverages\n\\texttt{JsonSubTypes} to extend entities with properties using\ninheritance. You can define parent relationships (in this example,\n\\texttt{EntityC}) with two children containing the properties of the\nparent and their own properties. Here's how to define YAML to use\ninheritance:\n\n\\begin{verbatim}\nEntityC:\n    oneOf:\n        - properties:\n            nameA:\n                type: string\n        - properties:\n            nameB:\n                type: string\n    properties:\n        nameC:\n            type: string\n\\end{verbatim}\n\nThis generates a parent class and two children:\n\n\\begin{verbatim}\n@JsonSubTypes(\n    {\n        @JsonSubTypes.Type(name = \"nameA\", value = NameA.class),\n        @JsonSubTypes.Type(name = \"nameB\", value = NameB.class)\n    }\n)\n@JsonTypeInfo(\n    include = JsonTypeInfo.As.PROPERTY, property = \"childType\",\n    use = JsonTypeInfo.Id.NAME\n)\n@Generated(\"\")\n@GraphQLName(\"EntityC\")\n@JsonFilter(\"Liferay.Vulcan\")\n@XmlRootElement(name = \"EntityC\")\npublic class EntityC {\n\n    @Schema\n    public String getNameC() {\n        return nameC;\n    }\n\n    public void setNameC(String nameC) {\n        this.nameC = nameC;\n    }\n\\end{verbatim}\n\nAnd two children classes look like this:\n\n\\begin{verbatim}\n@JsonTypeInfo(\n    defaultImpl = NameA.class, include = JsonTypeInfo.As.PROPERTY,\n    property = \"childType\", use = JsonTypeInfo.Id.NAME\n)\n@Generated(\"\")\n@GraphQLName(\"NameA\")\n@JsonFilter(\"Liferay.Vulcan\")\n@XmlRootElement(name = \"NameA\")\npublic class NameA extends EntityC {\n\n    @Schema\n    public String getNameA() {\n        return nameA;\n    }\n\n    public void setNameA(String nameA) {\n        this.nameA = nameA;\n    }\n\\end{verbatim}\n\n\\chapter{REST Builder Liferay\nConventions}\\label{rest-builder-liferay-conventions}\n\nLiferay's headless APIs follow several patterns and conventions to\nprovide consistency and uniformity in the APIs.\n\nBelow is a list the most important ones. It's a living list.\nImprovements are being made all the time, so check back to stay up to\ndate on the changes.\n\n\\section{YAML \\& OpenAPI restrictions}\\label{yaml-openapi-restrictions}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Tags are required}. We can't assign a class to a DELETE\n  operation (doesn't return anything, doesn't receive an entity) so we\n  need the tag to assign the method to a Java class.\n\\item\n  Responses must return a status code (default is not supported).\n\\item\n  Paths must be quoted.\n\\item\n  Paths only contain the method path (application and version are\n  inherited from the JAX-RS application).\n\\end{itemize}\n\n\\section{Conventions}\\label{conventions}\n\n\\begin{itemize}\n\\item\n  We use path parameters for information that is required (like the id\n  in a DELETE operation) and query parameters for optional information\n  (filtering, sorting\\ldots)\n\\item\n  We don't expose \\texttt{className}. If you need to return information\n  like the \\texttt{className}, use \\texttt{contentType} keyword.\n\\end{itemize}\n\n\\chapter{The Workflow Framework}\\label{the-workflow-framework}\n\nBlogs Entries, Journal Articles, and Forms Entries are just a few Assets\nsupporting workflow. There's nothing stopping you from likewise enabling\nworkflow for your custom Assets. Discover here how the workflow\nframework works, and find the steps and code samples for enabling your\ncustom entities to use the workflow capabilities in subsequent articles.\n\nA workflow process is a set of steps that an Asset must proceed through\nbefore it's marked with the workflow status \\emph{Approved}. The steps\nare defined in an XML file called a\n\\href{/7-2/reference/-/knowledge_base/r/crafting-xml-workflow-definitions}{workflow\ndefinition}. Each Asset is configured to run through a specific workflow\ndefinition via the \\href{/7-2/user/-/knowledge_base/u/workflow}{Control\nPanel}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-configuration.png}\n\\caption{Enable workflow on your custom Asset, and it can be sent\nthrough a workflow process just like a native Asset.}\n\\end{figure}\n\nThe workflow status is a database field that must be present for an\nentity to support workflow. If a database has the status field, but no\nworkflow code has been written, it's auto-marked \\emph{Approved} by\nLiferay's Service Builder infrastructure, to assure that everything\nworks smoothly by default.\n\n\\section{Supporting Workflow in the\nDatabase}\\label{supporting-workflow-in-the-database}\n\nThere are several database fields that must be present for an Asset to\nsupport workflow:\n\n\\texttt{int\\ status} represents the workflow status of each Asset.\n\n\\texttt{long\\ statusByUserId} is the ID of the user that set the status\n(for example, the initial User that hit the Submit for Publication\nbutton to add a new Asset.\n\n\\texttt{String\\ statusByUserName} is the User Name of the User that set\nthe status of the Asset.\n\n\\texttt{Date\\ statusDate} is the date/time when the status was set.\n\nFor Service Builder applications, add these as entity columns in the\n\\texttt{service.xml} file, run Service Builder, and you're good to go.\n\n\\section{Setting the Status Fields}\\label{setting-the-status-fields}\n\nOnce the database table has the proper status fields, set them in your\nEntity's \\texttt{addEntity} service method. Initially, set the status as\na DRAFT. It's what the workflow framework expects of an entity as it\nenters the workflow process. The status is an \\texttt{int}, but you\ndon't have to remember which number corresponds to the DRAFT status.\nInstead, use the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/portal-kernel/src/com/liferay/portal/kernel/workflow/WorkflowConstants.java}{\\texttt{WorkflowConstants}}\nin \\texttt{portal-kernel}. For a draft, pass in\n\n\\begin{verbatim}\nWorkflowConstants.STATUS_DRAFT\n\\end{verbatim}\n\nIf you're curious, the \\texttt{int} represented by this constant is\n\\texttt{2}. Another important status, APPROVED, is represented by the\n\\texttt{int} value \\texttt{0} and the constant\n\n\\begin{verbatim}\nWorkflowConstants.STATUS_APPROVED\n\\end{verbatim}\n\nThe User fields (\\texttt{statusByUserID} and \\texttt{statusByUserName})\nare easy, since the \\texttt{userId} of the User making the\n\\texttt{addEntity} request is part of the request itself, and passed\ninto the \\texttt{addEntity} method for most assets. Use the ID directly\nas the \\texttt{statusByUserId}, and get the full name associated with\nthe User by using the ID to retrieve the \\texttt{User} object.\n\n\\begin{verbatim}\nentity.setStatusByUserId(userId);\nentity.setStatusByUserName(user.getFullName());\n\\end{verbatim}\n\nThe \\texttt{statusDate} is usually best set as the date the entity was\nmodified, and is part of the Service Context in the request:\n\n\\begin{verbatim}\nentity.setStatusDate(serviceContext.getModifiedDate(null));\n\\end{verbatim}\n\nOnce the status dates are set, the entity is ready to be sent into the\nworkflow framework.\n\n\\section{Sending the Entity to the Workflow\nFramework}\\label{sending-the-entity-to-the-workflow-framework}\n\nWhen an entity is added to the database, the application must detect\nwhether workflow is enabled. If not, it automatically marks the entity\nas approved so it appears in the UI. Otherwise, it's left in draft\nstatus and the workflow back-end handles it. Thankfully, this whole\nprocess is easily done with a call to\n\\texttt{WorkflowHandlerRegistryUtil.startWorkflowInstance} in your\npersistence code.\n\n\\section{Allowing the Workflow Framework to Handle the\nEntity}\\label{allowing-the-workflow-framework-to-handle-the-entity}\n\nOnce the entity is sent to the Workflow Framework, much of the process\nis automated, and you need not worry about the details. Write one class\nthat gives the framework some information on how to process the entity.\nIt's called a workflow handler\n(\\texttt{WorkflowHandler\\textless{}T\\textgreater{}}), and you can create\nit by extending the handy abstract implementation,\n\\texttt{BaseWorkflowHandler\\textless{}T\\textgreater{}}.\n\nThe workflow handler usually goes in the module containing service\nimplementations. It's nice to keep your back-end code separate from your\nview layer and controller (ala the MVC pattern).\n\nMake your workflow handler a Component class so it can be registered\nproperly with OSGi runtime. It requires one Component property,\n\\texttt{model.class.name}, which is the fully qualified class name for\nclass you pass as the type parameter in the class declaration.\n\nIn addition to the property, declare the type of service you're\nproviding in the Component: \\texttt{WorkflowHandler.class}.\n\nWorkflow handlers extending the \\texttt{BaseWorkflowHandler} must\noverride three methods:\n\n\\texttt{getClassName} returns the model class's fully qualified class\nname (\\texttt{com.my.app.package.model.FooEntity}, for example).\n\n\\texttt{getType} returns the model resource name\n(\\texttt{model.resource.com.my.app.package.model.FooEntity}, for\nexample).\n\n\\texttt{updateStatus} does most of the heavy lifting here. It returns a\ncall to a local service method of the same name (for example,\n\\texttt{FooEntityLocalService.updateStatus}), so the status returned\nfrom the workflow back-end can be persisted to the entity table in the\ndatabase. The \\texttt{updateStatus} method needs a user ID, the primary\nkey for the class (for example, \\texttt{fooEntityId}), the workflow\nstatus, the service context, and the workflow context. The status and\nthe workflow context can be obtained from the workflow back-end. The\nother parameters can be obtained from the workflow context.\n\n\\section{Supporting Workflow in the Service\nLayer}\\label{supporting-workflow-in-the-service-layer}\n\nThe service layer must update the status of the entity when it returns\nfrom the Workflow Framework. Make an \\texttt{updateStatus} method for\nthis purpose, and make sure, at a minimum, to set the status fields\nagain as the Asset comes out of the Workflow Framework, and call the\npersistence layer's \\texttt{update} method.\n\nAfter that, provide any additional logic you might want, like checking\nthe status and updating the Asset's visibility (using the\n\\texttt{assetEntryLocalService}) based on the condition (visible if\n\\emph{Approved}, not visible is any other status).\n\nReturn the entry once you're through here.\n\n\\section{Database Cleanup: Delete the Workflow Instance\nLinks}\\label{database-cleanup-delete-the-workflow-instance-links}\n\nWhen you send an entity to the workflow framework via the\n\\texttt{startWorkflowInstance} call, it creates an entry in the\n\\texttt{workflowinstancelink} database table. In your service layer's\ndeletion logic, you must delete the workflow instance links. This\n\\texttt{delete} call ensures there are no orphaned entries in the\n\\texttt{workflowinstancelinks} table.\n\nTo get the \\texttt{WorkflowInstanceLocalService} injected into your\n\\texttt{*LocalServiceBaseImpl} so you can call its methods in the\n\\texttt{LocalServiceImpl}, add a \\texttt{reference\\ entity} to your\nentity declaration in \\texttt{service.xml}, specifying\n\\texttt{WorkflowInstancelink}.\n\n\\section{Updating the User Interface}\\label{updating-the-user-interface}\n\nAfter you finish all the backend work, update your UI. Some common tasks\nhere include:\n\n\\begin{itemize}\n\\item\n  In any public facing portion of the application (accessible to guest\n  Users), don't display the entity if the status is anything except\n  \\emph{Approved}. This task requires the creation of an additional\n  \\texttt{finder} method that accounts for workflow status, and a\n  corresponding \\texttt{getter} to expose it in the service layer.\n\\item\n  In administrative portions of the application, display the entities,\n  but also display their workflow status. There's a tag library for\n  this.\n\\end{itemize}\n\nSee the next article for more concrete steps and code snippets.\n\n\\chapter{Liferay's Workflow\nFramework}\\label{liferays-workflow-framework}\n\nTo workflow-enable your entities,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Create a Workflow Handler\n\\item\n  Update the Service Layer\n\\item\n  Update the User Interface\n\\end{enumerate}\n\nTime to get started.\n\n\\section{Creating a Workflow Handler}\\label{creating-a-workflow-handler}\n\nIf you're in a Service Builder application, the workflow handler goes in\nyour \\texttt{-service} module.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a Component class that extends\n  \\texttt{BaseWorkflowHandler\\textless{}T\\textgreater{}}.\n\n\\begin{verbatim}\n@Component(immediate = true, service = WorkflowHandler.class)\npublic class FooEntityWorkflowHandler extends BaseWorkflowHandler<FooEntity>\n\\end{verbatim}\n\\item\n  Override three methods in the workflow handler.\n\n\\begin{verbatim}\n@Override\npublic String getClassName() {\n    return FooEntity.class.getName();\n}\n\n@Override\npublic String getType(Locale locale) {\n    return ResourceActionsUtil.getModelResource(locale, getClassName());\n}\n\n@Override\npublic FooEntity updateStatus(int status, Map<String, Serializable> workflowContext) throws PortalException {\n... }\n\\end{verbatim}\n\n  Most of the heavy lifting is in the \\texttt{updateStatus} method. It\n  returns a call to a local service method of the same name (for\n  example, \\texttt{FooEntityLocalService.updateStatus}), so the status\n  returned from the workflow back-end can be persisted to the entity\n  table in the database.\n\n  The \\texttt{updateStatus} method needs a user ID, the primary key for\n  the class (for example, \\texttt{fooEntityId}), the workflow status,\n  the service context, and the workflow context. The status and the\n  workflow context can be obtained from the workflow back-end. The other\n  parameters can be obtained from the workflow context. Here's an\n  example \\texttt{updateStatus} method:\n\n\\begin{verbatim}\n@Override\npublic FooEntity updateStatus(\n        int status, Map<String, Serializable> workflowContext)\n    throws PortalException {\n\n    long userId = GetterUtil.getLong(\n        (String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));\n    long classPK = GetterUtil.getLong(\n        (String)workflowContext.get(\n            WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n    ServiceContext serviceContext = (ServiceContext)workflowContext.get(\n        WorkflowConstants.CONTEXT_SERVICE_CONTEXT);\n\n    return _fooEntityLocalService.updateStatus(\n        userId, classPK, status, serviceContext);\n}\n\\end{verbatim}\n\\end{enumerate}\n\nNow your entity can be handled by Liferay's workflow framework. Next,\nupdate the service methods to account for workflow status and add a new\nmethod to update the status of an entity in the database.\n\n\\section{Updating the Service Layer}\\label{updating-the-service-layer}\n\nIn most Liferay applications,\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} is used to create database fields. First, you must update the\nservice layer:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Make sure your entity database table has \\texttt{status},\n  \\texttt{statusByUserId}, \\texttt{statusByUserName}, and\n  \\texttt{statusDate} fields.\n\n\\begin{verbatim}\n<column name=\"status\" type=\"int\" />\n<column name=\"statusByUserId\" type=\"long\" />\n<column name=\"statusByUserName\" type=\"String\" />\n<column name=\"statusDate\" type=\"Date\" />\n\\end{verbatim}\n\\item\n  Wherever you're setting the other database fields in your persistence\n  code, set the workflow status as a draft and set the other fields.\n\n\\begin{verbatim}\nfooEntity.setStatus(WorkflowConstants.STATUS_DRAFT);\nfooEntity.setStatusByUserId(userId);\nfooEntity.setStatusByUserName(user.getFullName());\nfooEntity.setStatusDate(serviceContext.getModifiedDate(null));\n\\end{verbatim}\n\n  With Service Builder driven Liferay applications, this is in the local\n  service implementation class (\\texttt{-LocalServiceImpl}).\n\\item\n  At the end of any method that adds a new entity to your database, call\n  the workflow service to enable sending the entity into the workflow\n  backend:\n\n\\begin{verbatim}\nWorkflowHandlerRegistryUtil.startWorkflowInstance(\n    fooEntity.getCompanyId(), fooEntity.getGroupId(), fooEntity.getUserId(),\n    FooEntity.class.getName(), fooEntity.getPrimaryKey(), fooEntity,\n    serviceContext);\n\\end{verbatim}\n\\item\n  Implement the \\texttt{updateStatus} method that must be called in the\n  workflow handler. In the end, persist the updated entity to the\n  database.\n\n\\begin{verbatim}\nfooEntity.setStatus(status);\nfooEntity.setStatusByUserId(user.getUserId());\nfooEntity.setStatusByUserName(user.getFullName());\nfooEntity.setStatusDate(serviceContext.getModifiedDate(now));\n\nfooEntityPersistence.update(fooEntity);\n\\end{verbatim}\n\\item\n  Do anything else that might make sense here, like changing the\n  visibility of the asset depending on its workflow status:\n\n\\begin{verbatim}\nif (status == WorkflowConstants.STATUS_APPROVED) {\n    assetEntryLocalService.updateVisible(\n        FooEntity.class.getName(), fooEntityId, true);\n}\nelse {\n    assetEntryLocalService.updateVisible(\n        FooEntity.class.getName(), fooEntityId, false);\n}\n\\end{verbatim}\n\n  Here's what a full \\texttt{updateStatus} method might look like:\n\n\\begin{verbatim}\n@Indexable(type = IndexableType.REINDEX)\npublic FooEntity updateStatus(\n    long userId, long fooEntityId, int status, ServiceContext serviceContext\n) throws PortalException, SystemException {\n\n        User user = userLocalService.getUser(userId);\n        FooEntity fooEntity = getFooEntity(fooEntityId);\n\n        fooEntity.setStatus(status);\n        fooEntity.setStatusByUserId(userId);\n        fooEntity.setStatusByUserName(user.getFullName());\n        fooEntity.setStatusDate(new Date());\n\n        fooEntityPersistence.update(fooEntity);\n\n        if (status == WorkflowConstants.STATUS_APPROVED) {\n            assetEntryLocalService.updateVisible(\n          FooEntity.class.getName(), fooEntityId, true);\n        }\n    else {\n            assetEntryLocalService.updateVisible(\n          FooEntity.class.getName(), fooEntityId, false);\n        }\n\n        return fooEntity;\n  }\n\\end{verbatim}\n\\item\n  Add a call to \\texttt{deleteWorkflowInstanceLinks} in the\n  \\texttt{deleteEntity} method:\n\n\\begin{verbatim}\nworkflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(\n    fooEntity.getCompanyId(), fooEntity.getGroupId(),\n    FooEntity.class.getName(), fooEntity.getFooEntityId());\n\\end{verbatim}\n\n  To get the \\texttt{WorkflowInstanceLocalService} injected into your\n  \\texttt{*LocalServiceBaseImpl} so you can call its methods in the\n  \\texttt{LocalServiceImpl}, add this to your entity declaration in\n  \\texttt{service.xml}:\n\n\\begin{verbatim}\n<reference entity=\"WorkflowInstanceLink\" package-path=\"com.liferay.portal\" />\n\\end{verbatim}\n\\end{enumerate}\n\nFor an example of a fully implemented \\texttt{updateStatus} method, see\nthe\n\\texttt{com.liferay.portlet.blogs.service.impl.BlogsEntryLocalServiceImpl}\nclass in \\texttt{portal-impl}.\n\nOnce you've accounted for workflow status in your database and service\nlayer, there's only one thing left to do: update the user interface.\n\n\\section{Workflow Status and the View\nLayer}\\label{workflow-status-and-the-view-layer}\n\nThis is dependent on the needs of your application, but often involves\nthe following these steps:\n\n\\textbf{Display only approved entities:}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If you're using Service Builder, define your finder in your\n  application's \\texttt{service.xml} and let Service Builder generate it\n  for you.\n\n\\begin{verbatim}\n<finder name=\"G_S\" return-type=\"Collection\">\n    <finder-column name=\"groupId\"></finder-column>\n    <finder-column name=\"status\"></finder-column>\n</finder>\n\\end{verbatim}\n\\item\n  Make sure you have a getter in your service layer that uses the new\n  finder.\n\n\\begin{verbatim}\npublic List<FooEntity> getFooEntities(long groupId, int status)\n    throws SystemException {\n\n    return fooEntityPersistence.findByG_S(\n        groupId, WorkflowConstants.STATUS_APPROVED);\n}\n\\end{verbatim}\n\\item\n  Finally, update your JSP to use the appropriate getter.\n\n\\begin{verbatim}\n<liferay-ui:search-container-results\n    results=\"<%= FooEntityLocalServiceUtil.getFooEntities(\n        scopeGroupId, fooEntityId(), Workflowconstants.STATUS_APPROVED,\n        searchContainer.getStart(), searchContainer.getEnd()) %>\"\n/>\n    ...\n\\end{verbatim}\n\\end{enumerate}\n\n\\textbf{Display the workflow status:}\n\nWhen you want to display the workflow status, use the\n\\texttt{\\textless{}aui:worklfow-status\\textgreater{}} tag.\n\n\\begin{verbatim}\n<aui:workflow-status markupView=\"lexicon\" showIcon=\"<%= false %>\" showLabel=\"<%= false %>\" status=\"<%= fooEntity.getStatus() %>\" />\n\\end{verbatim}\n\nOnce your user interface accounts for workflow, your Liferay application\nis fully workflow enabled.\n\n\\chapter{WYSIWYG Editors}\\label{wysiwyg-editors}\n\nWYSIWYG editors are an important part of content creation. Liferay's\nplatform supports several different editors, including CKEditor,\nTinyMCE, and our flagship, AlloyEditor. This section shows how to\ncustomize these WYSIWYG editors for your apps and sites.\n\n\\chapter{Adding a WYSIWYG Editor to a\nPortlet}\\label{adding-a-wysiwyg-editor-to-a-portlet}\n\nIt's easy to include WYSIWYG editors in your portlet, thanks to the\n\\texttt{\\textless{}liferay-editor:editor\\ /\\textgreater{}} tag. Follow\nthese steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the liferay-editor taglib declaration to your portlet's JSP:\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://liferay.com/tld/editor\" prefix=\"liferay-editor\" %>\n\\end{verbatim}\n\\item\n  Add the editor to your JSP with the\n  \\texttt{\\textless{}liferay-editor:editor\\ /\\textgreater{}} tag.\n  Configure it using the attributes shown in the table below:\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n Attribute | Type | Description |\n --- | --- | --- |\n `autoCreate` | `java.lang.String` | Whether to show the HTML edit view of the editor initially |\n `contents` | `java.lang.String` | Sets the initial contents of the editor |\n `contentsLanguageId` | `java.lang.String` | Sets the language ID for the input editor's text |\n `cssClass` | `java.lang.String` | A CSS class for styling the component. |\n `data` | `java.util.Map` | Data that can be used as the editorConfig |\n `editorName` | `java.lang.String` | The editor you want to use (alloyeditor, ckeditor, tinymce, simple) |\n `name` | `java.lang.String` | A name for the input editor. The default value is `editor`. |\n `onBlurMethod` | `java.lang.String` | A function to be called when the input editor loses focus. |\n `onChangeMethod` | `java.lang.String` | A function to be called on a change in the input editor. |\n `onFocusMethod` | `java.lang.String` | A function to be called when the input editor gets focus. |\n `onInitMethod` | `java.lang.String` | A function to be called when the input editor initializes. |\n `placeholder` | `java.lang.String` | Placeholder text to display in the input editor. |\n `showSource` | `java.lang.String` | Whether to enable editing the HTML source code of the content. The default value  is `true`. |\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nSee the [taglibdocs](https://docs.liferay.com/dxp/apps/frontend-editor/latest/taglibdocs/liferay-editor/editor.html) \nfor the complete list of supported attributes. \n\nBelow is an example configuration:\n\n```html    \n<div class=\"alloy-editor-container\">\n    <liferay-editor:editor\n            contents=\"Default Content\"\n            cssClass=\"my-alloy-editor\"\n            editorName=\"alloyeditor\"\n            name=\"myAlloyEditor\"\n            placeholder=\"description\"\n            showSource=\"true\" \n    /> \n</div>\n```\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Optionally pass JavaScript functions through the\n  \\texttt{onBlurMethod}, \\texttt{onChangeMethod},\n  \\texttt{onFocusMethod}, and \\texttt{onInitMethod} attributes. Here is\n  an example configuration that uses the \\texttt{onInitMethod} attribute\n  to pass a JavaScript function called \\texttt{OnDescriptionEditorInit}:\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://liferay.com/tld/editor\" prefix=\"liferay-editor\" %>\n\n<div class=\"alloy-editor-container\">\n    <liferay-editor:editor\n        contents=\"Default Content\"\n        cssClass=\"my-alloy-editor\"\n        editorName=\"alloyeditor\"\n        name=\"myAlloyEditor\"\n        onInitMethod=\"OnDescriptionEditorInit\"\n        placeholder=\"description\"\n        showSource=\"true\" />\n</div>\n\\end{verbatim}\n\n\\begin{verbatim}\n<aui:script>\n    function <portlet:namespace />OnDescriptionEditorInit() {\n        <c:if test=\"<%= !customAbstract %>\">\n            document.getElementById(\n              '<portlet:namespace />myAlloyEditor'\n            ).setAttribute('contenteditable', false);\n        </c:if>\n    }\n</aui:script>\n\\end{verbatim}\n\\end{enumerate}\n\nAs you can see, it's easy to include WYSIWYG editors in your portlets!\n\n\\section{Related Topics}\\label{related-topics-137}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-new-behavior-to-an-editor}{Adding\n  New Behavior to an Editor}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/modifying-an-editors-configuration}{Modifying\n  an Editor's Configuration}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/alloyeditor}{Modifying\n  the AlloyEditor}\n\\end{itemize}\n\n\\chapter{Modifying an Editor's\nConfiguration}\\label{modifying-an-editors-configuration}\n\nYou can use many different kinds of WYSIWYG editors to edit content in\nportlets. Depending on the content you're editing, you may want to\nmodify the editor to provide a customized configuration for your needs.\nIn this article, you'll learn how to modify the default configuration\nfor Liferay DXP's supported WYSIWYG editors to meet your requirements.\n\nTo modify the editor's configuration, create a module with a component\nthat implements the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/editor/configuration/EditorConfigContributor.html}{\\texttt{EditorConfigContributor}}\ninterface. Follow these steps to modify one of Liferay DXP's WYSIWYG\neditors:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  an OSGi module}.\n\\item\n  Open the portlet's \\texttt{build.gradle} file and update the\n  \\texttt{com.liferay.portal.kernel} \\texttt{version} to\n  \\texttt{4.13.1}. This is the version bundled with the Liferay DXP\n  release.\n\\item\n  Create a unique package name in the module's \\texttt{src} directory,\n  and create a new Java class in that package that extends the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/editor/configuration/BaseEditorConfigContributor.html}{\\texttt{BaseEditorConfigContributor}}\n  class:\n\\item\n  Create a component class that implements the\n  \\texttt{EditorConfigContributor} service:\n\n\\begin{verbatim}\n@Component(\n    property = {\n\n    },\n\n    service = EditorConfigContributor.class\n)\n\\end{verbatim}\n\\item\n  Add the following imports:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.editor.configuration.BaseEditorConfigContributor;\nimport com.liferay.portal.kernel.editor.configuration.EditorConfigContributor;\nimport com.liferay.portal.kernel.json.JSONArray;\nimport com.liferay.portal.kernel.json.JSONFactoryUtil;\nimport com.liferay.portal.kernel.json.JSONObject;\nimport com.liferay.portal.kernel.portlet.RequestBackedPortletURLFactory;\nimport com.liferay.portal.kernel.theme.ThemeDisplay;\n\\end{verbatim}\n\\item\n  Specify the editor's name, editor's configuration key, and/or the\n  portlet name(s) where the editor resides. These three properties can\n  be specified independently, or together, in any order. See the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/editor/configuration/EditorConfigContributor.html}{\\texttt{EditorConfigContributor}}\n  interface's Javadoc for more information about the available\n  properties and how to use them. The example configuration below\n  modifies the AlloyEditor's Content Editor, identified by the\n  \\texttt{contentEditor} configuration key and \\texttt{alloyeditor} name\n  key.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** If you're targeting all editors for a portlet, the\n `editor.config.key` is not required. For example, if you just want to target\n the Web Content portlet's editors, you can provide the configuration below:\n \n ```java\n @Component(\n property = {\"editor.name=ckeditor\",\n \"javax.portlet.name=com_liferay_journal_web_portlet_JournalPortlet\",\n \"service.ranking:Integer=100\"\n }\n ```\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nTwo portlet names are declared (Blogs and Blogs Admin), specifying that the \nservice applies to the content editors in those portlets. Lastly, the \nconfiguration overrides the default one by providing a higher \n[service ranking](/docs/7-2/customization/-/knowledge_base/c/creating-a-custom-osgi-service):\n\n```java\n@Component(\n    property = {\n        \"editor.config.key=contentEditor\", \"editor.name=alloyeditor\",\n        \"javax.portlet.name=com_liferay_blogs_web_portlet_BlogsPortlet\",\n        \"javax.portlet.name=com_liferay_blogs_web_portlet_BlogsAdminPortlet\", \n        \"service.ranking:Integer=100\"\n    },\n\n    service = EditorConfigContributor.class\n)\n```\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **NOTE:** If you want to create a global configuration that applies to an\n editor everywhere it's used, you must create two separate configurations:\n one configuration that targets just the editor and a second configuration\n that targets the Blogs and Blogs Admin portlets. For example, the two\n separate configurations below apply the updates to AlloyEditor everywhere\n it's used:\n \n Configuration one:\n ```java\n @Component(\n     immediate = true,\n     property = {\n         \"editor.name=alloyeditor\",\n         \"service.ranking:Integer=100\"\n     },\n \n     service = EditorConfigContributor.class\n )\n ```\n \n Configuration two:\n ```java\n @Component(\n     immediate = true,\n     property = {\n         \"editor.name=alloyeditor\",\n         \"javax.portlet.name=com_liferay_blogs_web_portlet_BlogsPortlet\",\n         \"javax.portlet.name=com_liferay_blogs_web_portlet_BlogsAdminPortlet\",\n         \"service.ranking:Integer=100\"\n     },\n \n     service = EditorConfigContributor.class\n )\n ```\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{6}\n\\item\n  Override the \\texttt{populateConfigJSONObject()} method to provide the\n  custom configuration for the editor. This method updates the original\n  configuration JSON object. It can also Update or delete existing\n  configurations, or any other configuration introduced by another\n  \\texttt{*EditorConfigContributor}.\n\n\\begin{verbatim}\n@Override\npublic void populateConfigJSONObject(\n    JSONObject jsonObject, Map<String, Object> inputEditorTaglibAttributes,\n    ThemeDisplay themeDisplay,\n    RequestBackedPortletURLFactory requestBackedPortletURLFactory) {\n\n}\n\\end{verbatim}\n\\item\n  In the \\texttt{populateConfigJSONObject} method, you must instantiate\n  a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/json/JSONObject.html}{\\texttt{JSONObject}}\n  to hold the current configuration of the editor. For instance, you\n  could use the code snippet below to retrieve the available toolbars\n  for the editor:\n\n\\begin{verbatim}\nJSONObject toolbars = jsonObject.getJSONObject(\"toolbars\");\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** This toolbar configuration is only applicable for the AlloyEditor.\n If you choose a configuration that is supported by multiple editors, you\n could apply it to them all. To do this, you could specify all the editors\n (e.g., `\"editor.name=alloyeditor\"`, `\"editor.name=ckeditor\"`,\n `ckeditor_bbcode` etc.) in the `@Component` annotation  of your\n `EditorConfigContributor` implementation, as you did in step six. Use the\n links the bottom of this article to view each editor's configuration\n options and requirements.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{8}\n\\item\n  Now that you've retrieved the toolbar, you can modify it. The example\n  below adds a camera button to the AlloyEditor's Add toolbar. It\n  extracts the \\emph{Add} buttons out of the toolbar configuration\n  object as a\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/json/JSONArray.html}{\\texttt{JSONArray}},\n  and then adds the button to that \\texttt{JSONArray}:\n\n\\begin{verbatim}\nif (toolbars != null) {\n    JSONObject toolbarAdd = toolbars.getJSONObject(\"add\");\n\n    if (toolbarAdd != null) {\n        JSONArray addButtons = toolbarAdd.getJSONArray(\"buttons\");\n\n        addButtons.put(\"camera\");\n    }\n}\n\\end{verbatim}\n\n  The configuration JSON object is passed to the editor with the\n  modifications you've implemented in the\n  \\texttt{populateConfigJSONObject} method.\n\\item\n  Finally, generate the module's JAR file and copy it to your\n  \\texttt{deploy} folder. Once the module is installed and activated in\n  the service registry, your new editor configuration is available for\n  use.\n\\end{enumerate}\n\nLiferay DXP supports several different types of WYSIWYG editors, which\ninclude (among others):\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://alloyeditor.com/api/1.5.0/Core.html}{AlloyEditor}\n\\item\n  \\href{http://docs.ckeditor.com/\\#!/api/CKEDITOR.config}{CKEditor}\n\\item\n  \\href{http://www.tinymce.com/wiki.php/Configuration}{TinyMCE}\n\\end{itemize}\n\nMake sure to visit each editor's configuration API to learn what each\neditor offers for configuration settings.\n\n\\section{Related Topics}\\label{related-topics-138}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-new-behavior-to-an-editor}{Adding\n  New Behavior to an Editor}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/alloyeditor}{Modifying\n  the AlloyEditor}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-a-wysiwyg-editor-to-a-portlet}{Adding\n  a WYSIWYG Editor to a Portlet}\n\\end{itemize}\n\n\\chapter{AlloyEditor}\\label{alloyeditor}\n\nAlloyEditor is a modern WYSIWYG editor built on top of CKEDITOR,\ndesigned to create modern and gorgeous web content. AlloyEditor is the\ndefault WYSIWYG editor.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/alloyeditor-website.png}\n\\caption{AlloyEditor is the default WYSIWYG editor built on top of\nCKEditor.}\n\\end{figure}\n\nThis section shows how to modify the default AlloyEditor configuration\nto meet your requirements.\n\n\\chapter{Adding Buttons to AlloyEditor's\nToolbars}\\label{adding-buttons-to-alloyeditors-toolbars}\n\nAlloyEditor's toolbars contain several useful functions out-of-the-box.\nYou may, however, want to customize the default configuration to include\na button you've created, to add an existing button to a toolbar, or to\nadd an\n\\href{/docs/7-2/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide}{existing\nCKEditor button that's bundled with Liferay DXP's AlloyEditor}. The\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/editor/configuration/EditorConfigContributor.html}{\\texttt{EditorConfigContributor}\ninterface}, provides everything you need to modify an editor's\nconfiguration, including adding buttons to AlloyEditor's toolbars.\n\\href{https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_config.html}{CKEditor\nConfiguration settings} that modify the editor's behavior (excluding UI\nmodifications) can also be passed down through this configuration\nobject.\n\nThe \\texttt{com.liferay.docs.my.button} module is used as an example\nthroughout this section. If you want to use it as a starting point for\nyour own configuration or follow along with the articles, you can\ndownload the module's zip file from the\n\\href{https://github.com/liferay/liferay-docs/tree/7.1.x/develop/tutorials/code/osgi/modules/com.liferay.docs.my.button}{Github\nrepo}.\n\n\\chapter{Creating the OSGi Module and Configuring the\nEditorConfigContributor\nClass}\\label{creating-the-osgi-module-and-configuring-the-editorconfigcontributor-class}\n\nTo add a button to the AlloyEditor's toolbars, you must first create an\nOSGi component class of service type\n\\texttt{EditorConfigContributor.class}. Follow these steps to create and\nconfigure the OSGi module:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  an OSGi module}, using\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-mvc-portlet-template}{Blade's\n  portlet template}:\n\n\\begin{verbatim}\nblade create -t portlet -p com.liferay.docs.my.button -c \nMyEditorConfigContributor my-new-button\n\\end{verbatim}\n\\item\n  Open the portlet's \\texttt{build.gradle} file and update the\n  \\texttt{com.liferay.portal.kernel} \\texttt{version} to\n  \\texttt{4.13.1}. This is the version bundled with the Liferay DXP\n  release.\n\\item\n  Open the portlet class you created in step one\n  (\\texttt{MyEditorConfigContributor}) and add the following imports:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.editor.configuration.BaseEditorConfigContributor;\nimport com.liferay.portal.kernel.editor.configuration.EditorConfigContributor;\nimport com.liferay.portal.kernel.json.JSONArray;\nimport com.liferay.portal.kernel.json.JSONFactoryUtil;\nimport com.liferay.portal.kernel.json.JSONObject;\nimport com.liferay.portal.kernel.portlet.RequestBackedPortletURLFactory;\nimport com.liferay.portal.kernel.theme.ThemeDisplay;\n\\end{verbatim}\n\\item\n  Replace the \\texttt{@Component} and properties with the properties\n  below:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n      \"editor.name=alloyeditor\",\n      \"service.ranking:Integer=100\"\n    },\n    service = EditorConfigContributor.class  \n)\n\\end{verbatim}\n\n  This targets AlloyEditor for the configuration and overrides the\n  default service by providing a higher\n  \\href{/docs/7-2/customization/-/knowledge_base/c/creating-a-custom-osgi-service}{service\n  ranking}. If you want to target a more specific configuration, you can\n  find the available properties in the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/editor/configuration/EditorConfigContributor.html}{\\texttt{EditorConfigContributor}\n  interface's Javadoc}.\n\\item\n  Extend \\texttt{BaseEditorConfigContributor} instead of\n  \\texttt{GenericPortlet}.\n\\item\n  Replace the \\texttt{doView()} method and contents with the\n  \\texttt{populateConfigJSONObject()} method shown below:\n\n\\begin{verbatim}\n@Override\npublic void populateConfigJSONObject(\nJSONObject jsonObject, Map<String, Object> inputEditorTaglibAttributes,\nThemeDisplay themeDisplay,\nRequestBackedPortletURLFactory requestBackedPortletURLFactory) {\n\n}\n\\end{verbatim}\n\\item\n  Inside the \\texttt{populateConfigJSONObject()} method, retrieve the\n  AlloyEditor's toolbars:\n\n\\begin{verbatim}\nJSONObject toolbarsJSONObject = jsonObject.getJSONObject(\"toolbars\");\n\nif (toolbarsJSONObject == null) {\n        toolbarsJSONObject = JSONFactoryUtil.createJSONObject();\n}\n\\end{verbatim}\n\\item\n  If you're adding a button for one of the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide}{CKEditor\n  plugins bundled with the AlloyEditor}, add the code below to retrieve\n  the extra plugins and add the plugin to the AlloyEditor's\n  configuration. The example below adds the \\texttt{clipboard} CKEditor\n  plugin:\n\n\\begin{verbatim}\nString extraPlugins = jsonObject.getString(\"extraPlugins\");\n\nif (Validator.isNotNull(extraPlugins)) {\n  extraPlugins = extraPlugins + \",ae_uibridge,ae_autolink,\n  ae_buttonbridge,ae_menubridge,ae_panelmenubuttonbridge,ae_placeholder,\n  ae_richcombobridge,clipboard\";\n}\nelse {\n  extraPlugins = \"ae_uibridge,ae_autolink,ae_buttonbridge,ae_menubridge,\n  ae_panelmenubuttonbridge,ae_placeholder,ae_richcombobridge,clipboard\";\n}\n\njsonObject.put(\"extraPlugins\", extraPlugins);\n\\end{verbatim}\n\n  AlloyEditor also comes with several plugins to bridge the gap between\n  the CKEditor's UI and the AlloyEditor's UI. These are prefixed with\n  the \\texttt{ae\\_} you see above. We recommend that you include them\n  all to ensure compatibility.\n\\end{enumerate}\n\nThe \\texttt{*EditorConfigContributor} class is prepared. Now you must\nchoose which toolbar you want to add the button(s) to: the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-a-button-to-the-add-toolbar}{Add\nToolbar} or one of the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-a-button-to-a-styles-toolbar}{Styles\nToolbars}.\n\n\\section{Related Topics}\\label{related-topics-139}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-new-behavior-to-an-editor}{Adding\n  New Behavior to an Editor}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide}{CKEditor\n  Plugin Reference Guide}\n\\end{itemize}\n\n\\chapter{Adding a Button to the Add\nToolbar}\\label{adding-a-button-to-the-add-toolbar}\n\nThe Add Toolbar appears in the AlloyEditor when your cursor is in the\neditor and you click the Add button:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/alloyeditor-add-toolbar.png}\n\\caption{The Add toolbar lets you add content to the editor.}\n\\end{figure}\n\nFollow these steps to add a button to the AlloyEditor's Add Toolbar:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Inside the \\texttt{populateConfigJSONObject()} method, retrieve the\n  Add Toolbar:\n\n\\begin{verbatim}\nJSONObject addToolbar = toolbarsJSONObject.getJSONObject(\"add\");\n\\end{verbatim}\n\\item\n  Retrieve the existing Add Toolbar buttons:\n\n\\begin{verbatim}\nJSONArray addToolbarButtons = addToolbar.getJSONArray(\"buttons\");\n\\end{verbatim}\n\\item\n  Add the button to the existing buttons. Note that the button's name is\n  case sensitive. The example below adds the \\texttt{camera} button to\n  the Add Toolbar:\n\n\\begin{verbatim}\naddToolbarButtons.put(\"camera\");\n\\end{verbatim}\n\n  The camera button is just one of the buttons available by default with\n  AlloyEditor, but they are not all enabled. Here's the full list of\n  available buttons you can add to the Add Toolbar:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    camera\n  \\item\n    embed\n  \\item\n    hline\n  \\item\n    image\n  \\item\n    table\n  \\end{itemize}\n\n  See \\href{https://alloyeditor.com/docs/features/camera.html}{here} for\n  an explanation of each button's features.\n\\item\n  Update the AlloyEditor's configuration with the changes you made:\n\n\\begin{verbatim}\naddToolbar.put(\"buttons\", addToolbarButtons);\n\ntoolbarsJSONObject.put(\"add\", addToolbar);\n\njsonObject.put(\"toolbars\", toolbarsJSONObject);\n\\end{verbatim}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  your module} and create new content that uses the AlloyEditor---like a\n  blog entry or web content article---to see your new configuration in\n  action!\n\\end{enumerate}\n\nThe \\texttt{com.liferay.docs.my.button} module's updated Add Toolbar is\nshown in the figure below:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/alloyeditor-updated-add-toolbar.png}\n\\caption{The Updated Add toolbar lets you add pictures from a camera\ndirectly to the editor.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-140}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-new-behavior-to-an-editor}{Adding\n  New Behavior to an Editor}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-a-button-to-a-styles-toolbar}{Adding\n  a Button to a Styles Toolbar}\n\\end{itemize}\n\n\\chapter{Adding a Button to a Styles\nToolbar}\\label{adding-a-button-to-a-styles-toolbar}\n\nA Styles Toolbar appears when content is selected or highlighted in\nAlloyEditor. There are five Styles toolbars to choose from:\n\n\\texttt{embedurl}: Appears when embedded content is selected.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/alloyeditor-embedurl-toolbar.png}\n\\caption{The embed URL Styles toolbar lets you format embedded content\nin the editor.}\n\\end{figure}\n\n\\texttt{image}: Appears when an image is selected.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/alloyeditor-image-toolbar.png}\n\\caption{The image Styles toolbar lets you format images in the editor.}\n\\end{figure}\n\n\\texttt{link}: Appears when a hyperlink is selected.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/alloyeditor-link-toolbar.png}\n\\caption{The link Styles toolbar lets you format hyperlinks in the\neditor.}\n\\end{figure}\n\n\\texttt{table}: Appears when a table is selected.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/alloyeditor-table-toolbar.png}\n\\caption{The table Styles toolbar lets you format tables in the editor.}\n\\end{figure}\n\n\\texttt{text}: Appears when text is highlighted.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/alloyeditor-text-toolbar.png}\n\\caption{The text Styles toolbar lets you format highlighted text in the\neditor.}\n\\end{figure}\n\nFollow these steps to add a button to one of the Styles toolbars:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Inside the \\texttt{populateConfigJSONObject()} method, retrieve the\n  Styles toolbar:\n\n\\begin{verbatim}\nJSONObject stylesToolbar = toolbarsJSONObject.getJSONObject(\"styles\");\n\nif (stylesToolbar == null) {\n        stylesToolbar = JSONFactoryUtil.createJSONObject();\n}\n\\end{verbatim}\n\\item\n  Retrieve the available selection toolbars:\n\n\\begin{verbatim}\nJSONArray selectionsJSONArray = stylesToolbar.getJSONArray(\n\"selections\");\n\\end{verbatim}\n\\item\n  Iterate through the selection toolbars, select the one you want to add\n  the button(s) to (\\texttt{embedurl}, \\texttt{image}, \\texttt{link},\n  \\texttt{table}, or \\texttt{text}), retrieve the existing buttons, and\n  add your button. The example below adds the \\texttt{clipboard}\n  plugin's \\texttt{Copy}, \\texttt{Cut}, and \\texttt{Paste} buttons to\n  the \\texttt{text} selection toolbar. Note that buttons are case\n  sensitive and may be aliased or not match the name of the plugin.\n  Search the plugin's\n  \\href{/docs/7-1/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide}{\\texttt{plugin.js}\n  file} for \\texttt{editor.ui.addButton} to find the button's name:\n\n\\begin{verbatim}\nfor (int i = 0; i < selectionsJSONArray.length(); i++) {\n        JSONObject selection = selectionsJSONArray.getJSONObject(i);\n\n        if (Objects.equals(selection.get(\"name\"), \"text\")) {\n                JSONArray buttons = selection.getJSONArray(\"buttons\");\n\n                buttons.put(\"Copy\");\n                buttons.put(\"Cut\");\n                buttons.put(\"Paste\");\n        }\n}\n\\end{verbatim}\n\n  The example above adds one of the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide}{CKEditor\n  plugins bundled with Liferay DXP's AlloyEditor}. There are also\n  several buttons available by default with the AlloyEditor, but they\n  are not all enabled. The full list of existing buttons you can add to\n  the Styles toolbars is shown in the table below, ordered by Toolbar:\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n text | table | image | link |\n ---- | ----- | ----- | ---- |\n bold | tableHeading | imageCenter | linkEdit |\n code | tableRow | imageLeft | |\n h1 | tableColumn | imageRight | |\n h2 | tableCell | | |\n indentBlock | tableRemove | | |\n italic | | | |\n link | | | |\n ol | | | |\n outdentBlock | | | |\n paragraphLeft | | | |\n paragraphRight | | | |\n paragraphCenter | | | |\n paragraphJustify | | | |\n quote | | | |\n removeFormat | | | |\n strike | | | |\n styles | | | |\n subscript | | | |\n superscript | | | |\n twitter | | | |\n ul | | | |\n underline | | | |\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nSee [here](https://alloyeditor.com/docs/features/camera.html) for an \nexplanation of each button's features. \n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  Update the AlloyEditor's configuration with the changes you made:\n\n\\begin{verbatim}\nstylesToolbar.put(\"selections\", selectionsJSONArray);\n\ntoolbarsJSONObject.put(\"styles\", stylesToolbar);\n\njsonObject.put(\"toolbars\", toolbarsJSONObject);\n\\end{verbatim}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  your module} and create a new piece of content that uses the\n  AlloyEditor---such as a blog entry or web content article---to see\n  your new configuration in action!\n\\end{enumerate}\n\nThe \\texttt{com.liferay.docs.my.button} module's updated text styles\ntoolbar is shown in the figure below:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/alloyeditor-updated-styles-toolbar.png}\n\\caption{The Updated text styles toolbar lets you copy, cut, and paste\ntext in the editor.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-141}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-a-button-to-the-add-toolbar}{Adding\n  a Button to the Add Toolbar}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide}{CKEditor\n  Plugin Reference Guide}\n\\end{itemize}\n\n\\chapter{Embedding Content in the\nAlloyEditor}\\label{embedding-content-in-the-alloyeditor}\n\nWhether it's a video from a popular streaming service, or an\nentertaining podcast, embedded content is commonplace on the web.\nSharing content from a third party is sometimes required to properly\ncover a topic. The \\texttt{EmbedProvider} mechanism lets you embed third\nparty content in the AlloyEditor, while writing blog posts, web content\narticles, etc. By default, the \\texttt{EmbedProvider} mechanism is only\nconfigured for embedding video content (Facebook, Twitch, Vimeo, and\nYouTube) into the AlloyEditor. This tutorial shows how to include\nadditional video providers, and even add support for additional content\ntypes.\n\nAn \\texttt{EmbedProvider} requires four pieces of information:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  An ID: The content's ID\n\\item\n  A Template: The required embed code for the provider\n\\item\n  A URL Schemes: URL patterns that are supported for the provider\n  template\n\\item\n  A Type (optional): The provider category\n\\end{itemize}\n\nWhen you add a supported URL to the editor, the \\texttt{EmbedProvider}\ntransforms the URL into the embed code.\n\nFollow these steps to create an \\texttt{*EmbedProvider}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  a module} for the Embed Provider.\n\\item\n  Add the following dependencies to the \\texttt{build.gradle} file:\n\n\\begin{verbatim}\ncompileOnly group: \"com.liferay\", name:\n\"com.liferay.frontend.editor.api\", version: \"1.0.1\"\n\ncompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\",\nversion: \"2.0.0\"\n\\end{verbatim}\n\\item\n  Create a component class that implements the\n  \\texttt{EditorEmbedProvider} service:\n\n\\begin{verbatim}\n@Component(\n  immediate = true,\n  service = EditorEmbedProvider.class\n)\n\\end{verbatim}\n\\item\n  Optionally set the \\texttt{type} property to the content's type. If\n  creating a provider for a content type other than video, you can\n  create a new type constant and add a new button for the content type.\n  If you do create your own button, we recommend that you use the\n  existing\n  \\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-editor/frontend-editor-api/src/main/java/com/liferay/frontend/editor/embed}{embed\n  video button's JSX files} as an example to write your own files. By\n  default, the provider is categorized as \\texttt{UNKNOWN}. The example\n  configuration below specifies the \\texttt{VIDEO} type, using a\n  constant provided by the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-api/src/main/java/com/liferay/frontend/editor/embed/EditorEmbedProviderTypeConstants.java}{\\texttt{EditorEmbedProviderTypeConstants}\n  class}:\n\n\\begin{verbatim}\n@Component(\n  immediate = true,\n  property = \"type=\" + EditorEmbedProviderTypeConstants.VIDEO,\n  service = EditorEmbedProvider.class\n)\n\\end{verbatim}\n\\item\n  Implement the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-api/src/main/java/com/liferay/frontend/editor/embed/EditorEmbedProvider.java}{\\texttt{EditorEmbedProvider}\n  interface}. An example configuration is shown below:\n\n\\begin{verbatim}\npublic class MyEditorEmbedProvider implements EditorEmbedProvider {\n\n}\n\\end{verbatim}\n\\item\n  Add the required imports:\n\n\\begin{verbatim}\nimport com.liferay.frontend.editor.api.embed.EditorEmbedProvider;\nimport com.liferay.frontend.editor.api.embed.EditorEmbedProviderTypeConstants;\nimport com.liferay.petra.string.StringBundler;\n\\end{verbatim}\n\n  Note the \\texttt{*TypeConstants} import is only needed if you're\n  adding a Video type provider.\n\\item\n  Override the \\texttt{*EmbedProvider}'s \\texttt{getId()} method to\n  return the ID for the provider. An example configuration is shown\n  below:\n\n\\begin{verbatim}\n@Override\npublic String getId() {\n    return \"providerName\";\n}\n\\end{verbatim}\n\\item\n  Override the \\texttt{*EmbedProvider}'s \\texttt{getTpl()} method to\n  provide the embed template code (usually an iframe for the provider).\n  The example below defines the template for a streaming video service.\n  Note that \\texttt{\\{embedId\\}} is a placeholder for the unique\n  identifier for the embedded content:\n\n\\begin{verbatim}\n@Override\npublic String getTpl() {\n    return StringBundler.concat(\n        \"<iframe allow=\\\"autoplay; encrypted-media\\\" allowfullscreen \",\n        \"height=\\\"315\\\" frameborder=\\\"0\\\" \",\n        \"src=\\\"https://www.liferaylunarresortstreaming.com/embed/{embedId}?rel=0\\\" \",\n        \"width=\\\"560\\\"></iframe>\");\n}\n\\end{verbatim}\n\\item\n  Override the \\texttt{*EmbedProvider}'s \\texttt{getURLSchemes()} method\n  to return an array of supported URL schemes that have an embedded\n  representation for the provider. URL schemes are defined using a\n  JavaScript regular expression that indicates whether a URL matches the\n  provider. Every URL scheme should contain a single matching group.\n  Matches replace the \\texttt{\\{embedId\\}} placeholder defined in the\n  previous step:\n\n\\begin{verbatim}\n@Override\npublic String[] getURLSchemes() {\n    return new String[] {\n        \"https?:\\\\/\\\\/(?:www\\\\.)?liferaylunarresortstreaming.com\\\\/watch\\\\?v=(\\\\S*)$\"\n    };\n}\n\\end{verbatim}\n\\item\n  Deploy your module and open an app that uses the AlloyEditor, such as\n  Blogs, and create a new entry. Click the \\emph{add button} and select\n  the video button---or your new content type button---and paste the\n  content's URL. Click the \\emph{checkmark} to confirm that the URL\n  scheme is supported. The content is embedded into the editor.\n\\end{enumerate}\n\nNow you know how to embed content in the AlloyEditor. Create a new\ncontent entry, such as a blog post, and click the embed video\nbutton---or the one you created---and paste the content's URL.\n\n\\section{Related Topics}\\label{related-topics-142}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-buttons-to-alloyeditor-toolbars}{Adding\n  Buttons to AlloyEditor's Toolbars}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-new-behavior-to-an-editor}{Adding\n  New Behavior to an Editor}\n\\end{itemize}\n\n\\chapter{Adding New Behavior to an\nEditor}\\label{adding-new-behavior-to-an-editor}\n\nYou can select from several different WYSIWYG editors for your users,\nand each is configurable and has its strengths and weaknesses.\nConfiguration alone, however, doesn't always expose the features you\nwant. In these cases, you can programmatically access the editor\ninstance to create the editor experience you want, using the\n\\texttt{liferay-util:dynamic-include} JavaScript extension point. It\ninjects JavaScript code right after the editor instantiation to\nconfigure/change the editor.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} By default, the CKEditor strips empty\n\\texttt{\\textless{}i\\textgreater{}} tags, such as those used for Font\nAwesome icons, from published content, when switching between the Code\nView and the Source View of the editor. You can disable this behavior by\nusing the \\texttt{ckeditor\\#additionalResources} or\n\\texttt{alloyeditor\\#additionalResources}\n\\href{/docs/7-2/customization/-/knowledge_base/c/wysiwyg-editor-dynamic-includes}{extension\npoints} to add the code shown below to the editor:\n\n\\begin{verbatim}\n CKEDITOR.dtd.$removeEmpty.i = 0\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nThe \\texttt{liferay-util:dynamic-include} extension point is in\nconfigurable editors' JSP files: it's the gateway for injecting\nJavaScript into your editor instance. In this article, you'll learn how\nto use this JavaScript extension point. Follow these steps to inject\nJavaScript into the WYSIWYG editor to modify its behavior:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a JS file containing your editor functionality in a folder that\n  makes sense to reference, since you must register the file in your\n  module. The extension point injects the JavaScript code right after\n  editor initialization.\n\n  Liferay injects JavaScript code for some applications:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/extension/creole_dialog_definition.js}{creole\\_dialog\\_definition.js}\n    for the wiki\n  \\item\n    \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/extension/creole_dialog_show.js}{creole\\_dialog\\_show.js}\n    also for the wiki\n  \\item\n    \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/extension/dialog_definition.js}{dialog\\_definition.js}\n    for various applications\n  \\end{itemize}\n\n  These JS files redefine the fields that show in dialogs, depending on\n  what the selected language (HTML, BBCode, Creole) supports. For\n  example, Creole doesn't support background color in table cells, so\n  the table cells are removed from the options displayed to the user\n  when running in Creole mode.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  a module} that can register your new JS file and inject it into your\n  editor instance.\n\\item\n  Create a unique package name in the module's \\texttt{src} directory,\n  and create a new Java class in that package. To follow naming\n  conventions, your class name should begin with the editor you're\n  modifying, followed by custom attributes, and ending with\n  \\emph{DynamicInclude} (e.g.,\n  \\texttt{CKEditorCreoleOnEditorCreateDynamicInclude.java}). Your Java\n  class should implement the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/servlet/taglib/DynamicInclude.html}{\\texttt{DynamicInclude}}\n  interface.\n\\item\n  Directly above the class's declaration, insert the following\n  annotation:\n\n\\begin{verbatim}\n@Component(immediate = true, service = DynamicInclude.class)\n\\end{verbatim}\n\n  This declares the component's implementation class and starts the\n  module once deployed to Portal.\n\\item\n  If you have not yet overridden the abstract methods from\n  \\texttt{DynamicInclude}, do that now. There are two implemented\n  methods to edit: \\texttt{include(...)} and \\texttt{register(...)}.\n\\item\n  In the \\texttt{include(...)} method, retrieve the bundle containing\n  your custom JS file. Retrieve the JS file as a URL and inject its\n  contents into the editor. Here's the code that does this for the\n  \\texttt{creole\\_dialog\\_definition.js} file:\n\n\\begin{verbatim}\nBundle bundle = _bundleContext.getBundle();\n\nURL entryURL = bundle.getEntry(\n    \"/META-INF/resources/html/editors/ckeditor/extension\" +\n        \"/creole_dialog_definition.js\");\n\nStreamUtil.transfer(entryURL.openStream(), response.getOutputStream());\n\\end{verbatim}\n\n  In the \\texttt{include(...)} method, you can also retrieve editor\n  configurations and choose the JS file to inject based on the\n  configuration selected by the user. For example, this would be\n  applicable for the use case that was suggested previously dealing with\n  Creole's deficiency with displaying background colors in table cells.\n  Liferay implemented this in the \\texttt{include(...)} method in the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/java/com/liferay/frontend/editor/ckeditor/web/internal/servlet/taglib/CKEditorCreoleOnEditorCreateDynamicInclude.java}{\\texttt{CKEditorCreoleOnEditorCreateDynamicInclude}}\n  class.\n\\item\n  Make sure you've instantiated your bundle's context so you can\n  successfully retrieve your bundle. As a best practice, do this by\n  creating an activation method and then setting the\n  \\texttt{BundleContext} as a private field. Here's an example:\n\n\\begin{verbatim}\n@Activate\nprotected void activate(BundleContext bundleContext) {\n    _bundleContext = bundleContext;\n}\n\nprivate BundleContext _bundleContext;\n\\end{verbatim}\n\n  This method uses the \\texttt{@Activate} annotation, which specifies\n  that it should be invoked once the service component has satisfied its\n  requirements. For this default example, the \\texttt{\\_bundleContext}\n  was used in the \\texttt{include(...)} method.\n\\item\n  Now register the editor you're customizing. For example, if you were\n  injecting JS code into the CKEditor's JSP file, the code would look\n  like this:\n\n\\begin{verbatim}\ndynamicIncludeRegistry.register(\n    \"com.liferay.frontend.editor.ckeditor.web#ckeditor#onEditorCreate\");\n\\end{verbatim}\n\n  This registers the CKEditor into the Dynamic Include registry and\n  specifies that JS code will be injected into the editor once it's\n  created.\n\n  Just as you can configure individual JSP pages to use a specific\n  implementation of the available WYSIWYG editors, you can use those\n  same implementation options for the registration process. Visit the\n  \\href{https://docs.liferay.com/dxp/portal/7.1-latest/propertiesdoc/portal.properties.html\\#Editors}{Editors}\n  section of \\texttt{portal.properties} for more details. For example,\n  to configure the Creole implementation of the CKEditor, you could use\n  the following key:\n\n\\begin{verbatim}\n\"com.liferay.frontend.editor.ckeditor.web#ckeditor_creole#onEditorCreate\"\n\\end{verbatim}\n\\end{enumerate}\n\nThat's it! The JS code that you created is now injected into the editor\ninstance you've specified. You're now able to use JavaScript to add new\nbehavior to your Liferay DXP supported WYSIWYG editor!\n\n\\section{Related Topics}\\label{related-topics-143}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-new-behavior-to-an-editor}{Adding\n  New Behavior to an Editor}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes}{Embedding\n  Portlets in Themes}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{Portlets}\n\\end{itemize}\n"
  },
  {
    "path": "book/developer/publish.tex",
    "content": "\\chapter{Publishing Your App}\\label{publishing-your-app}\n\nAs you develop apps, you may want to sell them to or share them with\nconsumers. You can distribute your apps on the\n\\href{http://marketplace.liferay.com}{Liferay Marketplace}. The process\nis straightforward, but must make some decisions along the way and\nprepare your app for publishing on the Marketplace.\n\nThis section's first tutorial explains Liferay Marketplace-related\nconcepts and informs you of decisions to make before starting the\npublishing process from your portal. Then you'll learn how to get\nresources, such as your app's icon, screenshots to show off your app,\nand a comprehensive description of your app, ready for the publishing\nprocess. After that, you'll learn the step-by-step process of submitting\nyour app to the Marketplace. Lastly, you'll learn how to monitor your\napp's success on Liferay Marketplace and modify or add new versions of\nyour app to the Marketplace.\n\n\\chapter{Planning Your App's\nDistribution}\\label{planning-your-apps-distribution}\n\nWhen you start the formal process of submitting your app to the\nMarketplace, in addition to uploading your app's files you must answer a\nhost of important questions. For example, you must clarify who owns the\napp, specify pricing for the app, define its licensing scheme (if it's a\npaid app), associate a person or company as its owner and maintainer,\nand specify the versions of Liferay that the app supports. Your answers\nto these questions help you determine if you must package multiple\nversions of the app. This tutorial prepares you by explaining the\nquestions and ways you might answer them. Here's what's covered:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/how-to-publish/-/knowledge_base/publish/selling-your-app-or-making-it-free}{Selling\n  your app or making it free}\n\\item\n  \\href{/how-to-publish/-/knowledge_base/publish/publishing-as-an-individual-or-on-behalf-of-a-company}{Publishing\n  as an individual or on behalf of a company}\n\\item\n  \\href{/how-to-publish/-/knowledge_base/publish/licensing-and-pricing-your-app}{Licensing\n  and pricing your app}\n\\item\n  \\href{/how-to-publish/-/knowledge_base/publish/targeting-liferay-editions-and-versions}{App\n  versioning and target Liferay editions and versions}\n\\end{itemize}\n\nThe first tutorial answers this question: Should I sell my app or make\nit free?\n\n\\section{Selling Your App or Making it\nFree}\\label{selling-your-app-or-making-it-free}\n\nDo you want to sell your app on the Marketplace? Or do you want to share\nit freely with anyone on the Marketplace? It's up to you. Most of the\ncontent that follows describes options for paid apps (apps you sell).\n\nIf you're selling your app, you must publish using a \\emph{Paid App\nAccount}. You must also specify licensing, a price structure, and\nregional availability for your paid apps.\n\nImportantly, you can't change the app from free to paid or from paid to\nfree once the app is published to the Marketplace. Offering the app in\nin both license types requires submitting another app under a different\nname (title). If you want both free and paid licenses for your app, you\nmust submit the app under one name for free licenses and under another\nname for paid licenses. Make sure you select the license type (i.e.,\nfree or paid) that's best for your app.\n\nHave you decided who to list as the app's author/owner? Have you decided\nmanages the app once it's on the Marketplace? App ownership options are\nexplained next.\n\n\\section{Publishing as an Individual or on Behalf of a\nCompany}\\label{publishing-as-an-individual-or-on-behalf-of-a-company}\n\nYou can publish an app as yourself (an individual) or on behalf of a\n\\emph{company}. This determines who is shown as the app's author and\nowner. Your selection also determines who can access the app behind the\nscenes, once it's published.\n\nThe default option is publishing on behalf of yourself. If you go with\nthis option, your name shows as the app's author/owner in the\nMarketplace. The term \\emph{personal app} refers to an app published by\nan individual. That individual is the only one who can manage the\npersonal app. Managing an app includes such duties as adding new\nreleases to it, adding new versions of it, and editing its details.\n\nPublishing on behalf of a company effectively hands the keys over to the\ncompany's administrators. The app shows on the company's Marketplace app\ndevelopment page and in the company's list of apps on the company's\npublic profile page. Company admins have the same permission that an\nindividual author has to manage the app (add new releases, new versions,\nedit details, etc.). The company's name alone is shown as the app's\nauthor/owner.\n\nYou can\n\\href{https://www.liferay.com/marketplace/become-a-developer}{register}\nyourself as a Marketplace Developer or one of your company's\nadministrators can register the company as a Marketplace Developer. You\ncan either register for a Free Basic Account or register for a Paid App\nDeveloper Account. A Paid App Developer Account lets you submit paid\napps to the Liferay Marketplace for sale to customers globally. It\nenables you to monetize the benefits of the Marketplace and the benefits\nof the Liferay distribution channel. The best part is, you can upgrade\nto a Paid App Developer Account if and when you want to submit a paid\napp to the Marketplace! Here's a comparison of the Marketplace developer\naccount options:\n\nFree Basic Account\n\nNo cost to register as a Marketplace developer\n\nDistribute free apps on the Marketplace\n\nAccess to developer-only resources (e.g., Liferay Dev Studio, Liferay\nDeveloper License)\n\nParticipate in Marketplace promotions\n\nConverting from Basic-to-Paid\n\nWhen you're ready to submit a paid app to the Marketplace:\n\nAgree to the Developer Agreement\n\nPay \\$99 annual fee\n\nSubmit relevant tax documents (eg., W-9, W-8BEN) to receive payments\n\nEnter PayPal info to receive payments (PayPal account must be a Verified\nBusiness Account)\n\nPaid App Developer Account\n\nUpgrade when you're ready: only pay annual fee at time of paid app\nsubmission\n\nOffer paid licensing/support for your apps\n\nReceive 80\\% of app sales proceeds\n\nAccess to detailed transaction history\n\nPaid apps customer management\n\nAll the benefits of a Basic Developer Account\n\nNow that you've determined your app's owner and you've registered an\naccount to manage the app, you can learn about licensing options for\npaid apps.\n\n\\section{Licensing and Pricing Your\nApp}\\label{licensing-and-pricing-your-app}\n\nYou have significant control over how to price your app. You choose the\nlicense term (perpetual vs.~annual), choose the license type (standard\nvs.~developer), define a pricing structure (pricing and bundled\ndiscounting based on a license unit), and specify regional availability.\nEven after your app is on the Marketplace, you can tweak general pricing\nor modify regional pricing. Here you'll learn how to do all these\nthings.\n\n\\subsection{Determining a License\nTerm}\\label{determining-a-license-term}\n\nHere are the license term options:\n\n\\begin{itemize}\n\\item\n  Perpetual license: does not expire.\n\\item\n  Non-perpetual license: must be renewed annually.\n\\end{itemize}\n\nImportantly, you can't change your app's license terms once the app is\napproved. Releasing an approved app under a different license term\nrequires submitting another app under a new name (title). So make sure\nto think through the license term that makes the most sense for your\napp.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you are a foreign developer based outside of the\nUnited States, non-perpetual license sales (considered to be royalty\nincome) to US customers are subject to a 30\\% withholding tax. This tax\ndoes not apply to perpetual licenses. For more information on licensing\nterms and fees, please refer to the \\href{/faq}{FAQ}.\n\n\\noindent\\hrulefill\n\n\\subsection{Determining License Type and License Unit\nPricing}\\label{determining-license-type-and-license-unit-pricing}\n\nLicenses are set to run on a permitted number of Instance Units (defined\nas a single installation of the Liferay, which corresponds to one (1)\nLiferay \\texttt{.war} file). You can create tiers of bundled options to\naccommodate the number of Instance Units to offer to customers. You can\nalso offer a discounted price for a bundle of multiple Instance Units.\n\nThere are two types of licenses that you can offer for your app:\nstandard licenses and developer licenses. Standard licenses are intended\nfor production server environments. Developer licenses are limited to 10\nunique IP addresses and, therefore, should not be used for full-scale\nproduction deployments.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/licenseview.png}\n\\caption{Liferay Marketplace lets you specify different types of\nlicenses and license quantities for pricing.}\n\\end{figure}\n\nYou can also offer subscription services or 30-day trials. Support,\nmaintenance, and updates should comprise subscription services.\n\nDepending on how you decide to license your app, you have a few options\nto consider with regard to subscription services:\n\n\\textbf{If you're offering a perpetual license\\ldots{}}\n\nYou can offer annually renewable subscription services. During the app\nsubmission process if you choose to \\emph{offer subscription services},\nyou're asked to price subscription services on a \\emph{per Instance Unit\nper year} basis. The first year of subscription services is included\nwith a perpetual license. If a customer wants to continue receiving\nservices after the first year, the customer must renew subscription\nservices at the price you designate. Customers are entitled to support,\nmaintenance, and updates as long as they continue to annually renew\nsubscription services.\n\nIf you choose not to offer subscription services, customers are entitled\nto only app updates, if and when updates become available. This type of\none-time, upfront payment model may work well for less complex apps and\nthemes/templates.\n\n\\textbf{If you're offering a non-perpetual (annual/renewable)\nlicense\\ldots{}}\n\nYou can offer annually renewable subscription services. During the app\nsubmission process if you choose to \\emph{offer subscription services},\nyou should build the price of subscription services into the annual\nprice of the non-perpetual license; please take this into account as you\nprice non-perpetual licenses. In effect, the customer pays one price\nannually for both the app license and subscription services. The\ncustomer is entitled to support, maintenance, and updates as long as\nthey continue to renew their non-perpetual license.\n\nIf you choose not to offer subscription services, customers are entitled\nto only app updates if and when updates become available. They can\nreceive updates as long as they continue to have valid non-perpetual\nlicenses.\n\n\\subsection{Setting Prices for License Options by\nRegion}\\label{setting-prices-for-license-options-by-region}\n\nYou can specify countries your app will be available in and the app's\nprice in in each of those countries. You can make it as simple (single\nprice offered globally) or as granular (different price in each country\noffered) as you want.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/pricing_screenshot.png}\n\\caption{Liferay Marketplace lets you specify prices for all of your\nlicense options and lets you specify them by region.}\n\\end{figure}\n\nChoose the currency to use with your pricing options. Decide on the\nrenewal cost for any support services you offer. The support services\nprice is per instance, so if you specify \\$100 USD and the customer is\nrunning 10 instances, their annual support services renewal cost will be\n\\$1000. Note: This only applies to perpetual licenses. For non-perpetual\nlicenses, include any support services cost in the annual license price.\n\nEven after an app has been approved, you can change the currency type\nand currency price of its license bundles, and you can modify regional\navailability of license bundles.\n\nAlthough Liferay Marketplace supports major currencies and a broad list\nof countries, not all currencies and countries are currently available.\nAdditional currencies and countries may become available at a later\ntime.\n\n\\subsection{Considering the Liferay Marketplace\nFee}\\label{considering-the-liferay-marketplace-fee}\n\nBy selling your paid apps on the Liferay Marketplace, you're agreeing to\nshare app sales revenue with Liferay. For each app sale, you receive\n80\\% of the sales proceeds and Liferay receives 20\\% of the sales\nproceeds. We believe this type of fee structure is extremely competitive\ncompared to other app marketplaces. Liferay uses its share of app sale\nproceeds to:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Operate and improve the Liferay Marketplace.\n\\item\n  Provide developer services, such as payment processing, license\n  tracking, and performance metrics.\n\\item\n  Continue investing in and growing the Liferay ecosystem.\n\\end{itemize}\n\n\\textbf{Comparison of Liferay's App Revenue Sharing with that of Other\nApp Marketplaces}\n\nShare to Developer\n\nLiferay Marketplace\n\n80\\%\n\nAtlassian Marketplace\n\n75\\%\n\nDNN Store\n\n75\\%\n\nConcrete 5\n\n75\\%\n\nApple App Store\n\n70\\%\n\nAs you can see, Liferay's fee is reasonable.\n\nYou need a valid PayPal account to receive payment; you're asked to\nprovide this info when registering for or converting to a Paid App\nDeveloper Account. Payments are issued no later than 90 days after the\ntransaction.\n\nNow that you've decided on licensing options and pricing, you can\nconcentrate on the Liferay versions your app runs on.\n\n\\section{Targeting Liferay Editions and Versions and Versioning your\nApp}\\label{targeting-liferay-editions-and-versions-and-versioning-your-app}\n\nThere are multiple versions of Liferay and multiple editions (e.g.,\ncommunity and enterprise) for each version. You must decide the versions\nand editions to build your app on. And lastly, you must decide how to\nversion your app. This tutorial covers these topics.\n\n\\subsection{Determining Editions and Versions of Liferay to\nTarget}\\label{determining-editions-and-versions-of-liferay-to-target}\n\nOf course, targeting the widest possible range of Liferay editions and\nversions in an app typically draws larger audiences to the app. And\nthere may be certain features in these editions and versions that you\nwant to take advantage of. In your app's plugin\n\\href{/how-to-publish/-/knowledge_base/publish/preparing-your-app}{packaging\nproperties}, specify packaging directives to indicate the editions the\napp supports and the version that the app supports. To ensure the widest\naudience for your app, make your app compatible with both Liferay\nDigital Experience Platform (DXP) and Liferay Portal Community Edition\n(CE).\n\nYou can prepare a set of app files (including its\n\\texttt{liferay-plugin-package.properties} file) for each version of\nLiferay you want to support. When uploading your app, you can specify\nwhich versions of Liferay your app is compatible with and you can\nappropriately upload the sets of app files that are designed for those\ndifferent versions. The next article in this guide explains how to go\nabout\n\\href{/how-to-publish/-/knowledge_base/publish/preparing-your-app}{specifying\npackaging directives}.\n\nNote that apps on Liferay Marketplace must be designed for Liferay\nPortal 6.1 GA3 or later. That doesn't mean they can't work with prior\nversions. However, only Liferay Portal 6.1 GA3 and later versions\nsupport installing apps directly from Marketplace and provide safeguards\nagainst malicious apps. If you want to use an app with an earlier\nversion of Liferay DXP, make sure that version provides the dependencies\nyour app needs.\n\nLastly, you should determine a versioning scheme for your app. How will\nyou refer to the first version of your app, the second version, and so\non.\n\n\\subsection{Decide on a Versioning\nScheme}\\label{decide-on-a-versioning-scheme}\n\nA version of an app represents the functionality of the app at a given\npoint in time. When you first create an app, you give it an initial\nversion (e.g., \\texttt{1.0}). On updating the app, you increment its\nversion (e.g., from \\texttt{1.0} to \\texttt{1.1}), and you upload new\nfiles representing that version of the app. In some cases, you specify\nadditional qualifiers in order to convey a special meaning. For example,\nyou may declare that the version of your app is always in x.y.z format\n(where you've clearly defined the significance of x, y, and z). Liferay\nversions and official Liferay app versions use this format.\n\nIn any case, you're free to assign your app's version designators any\nway you like. We recommend that you stick to a well known and easily\nunderstandable format, such as \\texttt{1.0}, \\texttt{1.1}, \\texttt{1.2},\nand so on. Although you may want to include alphabetical characters\n(e.g., \\texttt{1.0\\ Beta\\ 2} or \\texttt{6.3\\ Patch\\ 123235-01}), we\ndiscourage it, as such characters may confuse customers as to how your\napp's versions relate to one another.\n\nKeep in mind that the releases of Liferay with which your app works must\nbe specified using Liferay's versioning scheme, as explained in\n\\href{/docs/7-2/deploy/-/knowledge_base/d/understanding-liferays-releases}{Understanding\nLiferay's Releases}. See the later section \\emph{Specify App Packaging\nDirectives} for details on specifying the releases of Liferay for which\nyour app is designed.\n\nCongratulations on coming up with a sound game plan for your app! The\nnext articles explain how to prepare apps for publishing.\n\n\\chapter{Preparing Your App}\\label{preparing-your-app}\n\nAs a Liferay developer, you're undoubtedly already familiar with the\nconcept of plugins (portlets, themes, etc.). If you're not familiar with\nLiferay plugins, see \\href{/docs/7-2/appdev}{Introduction to Liferay\nDevelopment}. A \\emph{Liferay App} (sometimes just called an \\emph{app})\nis a collection of one or more of these plugins, packaged together to\nrepresent the full functionality of an application on the Liferay\nplatform. In addition to the plugins contained within an app, apps have\nmetadata such as names, descriptions, versions, and other information\nthat describe and track the app throughout its life cycle.\n\nMuch like standard Liferay plugins, Liferay apps are also\nauto-deployable. Liferay Marketplace apps are distributed via a special\nfile type with an \\texttt{.lpkg} extension. To deploy these files, drop\nthem into a running Liferay instance's auto-deploy folder\n(\\texttt{{[}Liferay\\_Home{]}/deploy}), like any other plugin.\n\nAs an app developer, you're not required to create the \\texttt{.lpkg}\nfiles. Instead, your app's individual plugins (WAR files for traditional\nplugins or JAR files for OSGi modules) are uploaded as part of the\npublication process, along with information (name, description, version,\nicon, etc.) that identifies the app. The publication process is\ndescribed in detail later.\n\nAt this point in preparing to publish your app, you've developed your\napp. But before you start the formal publishing process, you must\nprepare your app's files and app metadata.\n\n\\section{Marketplace App Metadata\nGuidelines}\\label{marketplace-app-metadata-guidelines}\n\nThe following app metadata guidelines ensure that apps are submitted\nwith important and necessary supporting information. The metadata that\nyou submit with your app serves both as necessary information for your\napp's buyers (e.g., your contact info) and as promotional assets (e.g.,\ndescription, screenshots, etc.) that can help drive traffic to and\ndownloads of your app!\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dev-portal-app-metadata-guidelines.png}\n\\caption{Check out how good your app can look on the Marketplace.}\n\\end{figure}\n\nThink of a good name and description for your app. If you haven't\nalready done so, take some screenshots, design an icon, and create a\nwebsite for your app. The table below helps you address the Marketplace\napp metadata requirements and produce an appealing app advertisement.\n\n\\textbf{Marketplace App Metadata Guidelines:}\n\nRequired Metadata\n\nDescription\n\nApp Name\n\nThis is probably the most important branding element of your app, so be\ncreative! Some important things to keep in mind:\n\nIn some views within the Marketplace, app titles longer than 18\ncharacters are shortened with an ellipsis.\n\nTitles must not be longer than 50 characters.\n\nApp title may contain the word ``Liferay'' to describe its use or intent\nas long as the name does not imply official certification or validation\nfrom Liferay, Inc. (For example, names such as ``Exchange Connector for\nLiferay'' or ``Integration Connector Kit for Liferay'' would be allowed,\nwhile ``Liferay Mail Portlet,'' ``Liferay Management Console,'' or\n``Liferay UI Kit'' would not be permissible without explicit approval).\nPlease refer to our trademark policy for details.\n\nPlease try to conform the app name as closely as possible to the actual\nplugin (portlet, module, theme, etc.) name.\n\nSupport Information\n\nPlease include an email address, contact information, and/or website URL\nfor the ``Support'' field. If there's an issue with your app, they\nshould be a way to contact you.\n\nIf you choose not to offer Support Services (by unchecking ``Offer\nSupport'' during the app submission process), you must provide support\ncontact information so that buyers can ask you general questions about\nyour app.\n\nDescription\n\nUsing English as the default language, you must provide a description\nfor your app. After providing the description in English, you can\nprovide other translations of the text.\n\nAt a minimum, the description should provide a concise overview of what\nthe app does. Great descriptions also list key functionality and what\ncustomers can expect to gain by deploying your app. For a good\ndescription example, see the Audience Targeting app on the Marketplace.\n\nSpecify any plugin dependencies (plugins that must be installed prior to\nrunning your app) and environment compatibilities (compatibility with\nspecific app servers) here, so that potential buyers and the Liferay app\nreview team are aware of these requirements.\n\nWhat's New?*\n\nUsing English as the default language, describe what's new and improved\nin your app. After this, you can provide other translations of the text.\nThis field is shown on updating an app that you've already submitted,\nregardless of whether the app has been published.\n\nCompatibility with Future Minor Releases of Liferay\n\nPlease include a ``+'' at the end of the latest version when specifying\nversion constraints in your liferay-plugin-package.properties file\n(e.g., ``liferay-versions=7.2.1+, 7.2.20+''). This ensures that the app\ncontinues to be deployable to future Liferay DXP versions within a minor\nrelease. If, in the future, you discover your app does NOT work with a\nparticular version, you can modify the list to exclude that version.\n\nIncrease Your Potential User Base\n\nIn most cases, an app compatible with Liferay Portal CE also runs on\nLiferay Digital Experience Platform (DXP) (or Liferay Portal EE), and\nvice versa. Specifying compatibility with both DXP/EE and CE versions of\nLiferay ensures a wider audience for your app!\n\nYou can request a Developer License to support testing and confirm\ncompatibility.\n\nIcon\n\nApp icons must be exactly 90 pixels in both height and width in PNG,\nJPG, or GIF format. The image size cannot exceed 512kb. Animated images\nare prohibited.\n\nThe use of the Liferay logo, including alternate versions of the Liferay\nlogo, is permitted only with Liferay's explicit approval. Please refer\nto our trademark policy for details.\n\nScreenshots\n\nApp screenshots must not exceed 1080 pixels in width x 678 pixels in\nheight and must be in the JPG format. Each screenshot's file size must\nnot exceed 384KB. Each screenshot should be the same size (each is\nautomatically scaled to match the aspect ratio of the above dimensions),\nand it is preferable to name screenshots sequentially, for example\nfluffy-puppies-01.jpg, fluffy-puppies-02.jpg, and so on.\n\nMake sure your icons, images, descriptions, and tags are free of\nprofanity or other offensive material.\n\nDuring the publication process, you upload your app's individual plugin\nWAR files and module JAR files along with the app's metadata (name,\ndescription, version, icon, etc.).\n\n\\subsection{Additional Requirements for Themes/Site\nTemplates}\\label{additional-requirements-for-themessite-templates}\n\nThemes and Site Templates must include sample content and optionally\nlink to a demo website that provides context. This ensures a uniform\nexperience for Marketplace users: they download a Theme or Site Template\nfrom Marketplace, install it, go to Sites or Site Templates in the\nControl Panel, and immediately see the Theme or Site Template in action.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Demo sites must be valid. Demo website, Theme, and\nSite Template sample content must not plagiarize or infringe on\ncopyrights.\n\n\\noindent\\hrulefill\n\nThe\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/importing-resources-with-a-theme}{Resources\nImporter} includes files and web content that imports automatically into\nLiferay on Theme and Site Template deployment. Use the Resources\nImporter to include files/web content to provide a sample context for\nyour Theme or Site Template. The\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{Themes\ntutorials} provide details.\n\n\\section{Packaging Your Marketplace\nApp}\\label{packaging-your-marketplace-app}\n\nLiferay apps are ``normal'' Liferay plugins with additional information\nabout them. Therefore, most of the requirements are the same as those\nthat exist for other Liferay plugins, as explained in the tutorials on\ncreating\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{Liferay\nMVC Portlets}.\n\nIn addition to those requirements, there are some Marketplace-specific\nones to keep in mind:\n\n\\begin{itemize}\n\\item\n  \\textbf{Target the Appropriate Java JRE}: Regardless of the tools you\n  use to develop your app, your app's bytecode must be compatible with\n  the target Java JRE for your version of Liferay. Apps are rejected if\n  their bytecode is not compatible with the Java JRE for the intended\n  version of Liferay. In the Compatibility Matrix for your Liferay\n  version, make sure your app's bytecode is compatible with the latest\n  certified Java JDK version.\n\n  If you use any Gradle-based build environment like\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\n  Workspace}, make sure your \\texttt{targetCompatibility} is set to the\n  latest certified Java JDK version.\n\n  Similarly, if you use Maven, you can set the bytecode compatibility\n  like this:\n\\end{itemize}\n\n\\begin{verbatim}\n<properties>\n    <maven.compiler.source>1.8</maven.compiler.source>\n    <maven.compiler.target>1.8</maven.compiler.target>  \n</properties>\n\\end{verbatim}\n\nIf you use the\n\\href{/docs/6-2/tutorials/-/knowledge_base/t/plugins-sdk}{Liferay\nPlugins SDK} to develop your app for Liferay Portal 6.2 or 6.1, you can\nset the Java version by overriding the \\texttt{ant.build.javac.target}\nproperty in the Plugins SDK's \\texttt{build.properties} file.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{WAR (\\texttt{.war}) files}:\n\n  \\begin{itemize}\n  \\item\n    WAR files must contain a\n    \\texttt{WEB-INF/liferay-plugin-package.properties} file.\n  \\item\n    WAR files must not contain any\n    \\texttt{WEB-INF/liferay-plugin-package.xml} file.\n  \\item\n    WAR file names must not contain any commas.\n  \\item\n    WAR file names must conform to the following naming convention:\n\n    \\emph{context\\_name}\\texttt{-}\\emph{plugin\\_type}\\texttt{-A.B.C.D.war}\n\n    Where:\n  \\item\n    \\emph{context\\_name}: Alpha-numeric (including \\texttt{-} and\n    \\texttt{\\_}) short name of your app. This name is used as the\n    deployment context, and must not duplicate any other app's context\n    (you'll be warned if you use a context name of any other app on the\n    Marketplace).\n  \\item\n    \\emph{plugin\\_type}: one of the following: \\texttt{hook},\n    \\texttt{layouttpl}, \\texttt{portlet}, \\texttt{theme}, or\n    \\texttt{web}.\n  \\item\n    \\texttt{A.B.C.D}: The four digit version of your WAR file. Four\n    digits must be used.\n\n    Example: \\texttt{myapp-portlet-1.0.0.0.war}\n  \\end{itemize}\n\\item\n  \\textbf{\\texttt{WEB-INF/}\\href{https://docs.liferay.com/ce/portal/7.1-latest/propertiesdoc/liferay-plugin-package_7_1_0.properties.html}{\\texttt{liferay-plugin-package.properties}}\n  file:}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Property \\texttt{recommended.deployment.context} must not be set.\n  \\item\n    Setting property \\texttt{security-manager-enabled} to \\texttt{true}\n    is mandatory for all paid apps that contain WAR-style plugins and\n    that are for 6.1 EE/CE GA3 and 6.2 EE/CE; the setting is optional\n    for free apps. Setting this property to \\texttt{true} enables\n    Liferay's Plugin Security Manager. If you're enabling the security\n    manager, you must also define your Portal Access Control List (PACL)\n    in this file. Read\n    \\href{/docs/6-2/tutorials/-/knowledge_base/t/plugin-security-and-pacl}{Plugins\n    Security and PACL} for information on developing secure apps.\n  \\end{itemize}\n\\item\n  \\textbf{Deployment contexts}:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Liferay reserves the right to deny an application if any of its\n    plugin deployment contexts is the same as a context of another\n    plugin in the Marketplace.\n  \\item\n    Liferay reserves the right to replace app plugin WAR files that have\n    the same deployment context as plugins built by Liferay.\n  \\end{itemize}\n\\end{itemize}\n\nThere are some additional requirements for uploading Liferay DXP 7.x and\nLiferay Portal CE 7.x apps:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{OSGi modules in JAR (\\texttt{.jar}) files}:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    The manifest file must at minimum contain the following manifest\n    headers:\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{Bundle-SymbolicName}: a unique name for the module\n    \\item\n      \\texttt{Bundle-Version}: the module's version\n    \\item\n      \\texttt{Web-ContextPath}: the servlet context path\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  \\textbf{Apps containing multiple file types:}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Liferay DXP 7.x and Liferay Portal CE 7.x apps can contain a mix of\n    WAR-based plugins and OSGi JAR-based plugins. Regardless of file\n    type, each plugin must be able to run on Liferay DXP 7.x and Liferay\n    Portal CE 7.x.\n  \\end{itemize}\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} If you're developing a paid app or want your free\napp to satisfy Plugin Security Manager on Liferay 6.2 or 6.1 GA3, make\nsure to specify PACLs for your traditional WAR-style plugins. See the\narticle\n\\href{/docs/6-2/tutorials/-/knowledge_base/t/plugin-security-and-pacl}{Plugin\nSecurity and PACL} for details. Give yourself adequate time to develop\nyour app's permission descriptors and time to test your app thoroughly\nwith the security manager enabled.\n\n\\noindent\\hrulefill\n\nApps usually consist of multiple plugins (e.g., multiple WAR or JAR\nfiles) and plugin types. In addition, you may want to consider how to\npackage your app for running on different Liferay versions.\n\n\\subsection{Considering Package Variations to Target Different Versions\nof\nLiferay}\\label{considering-package-variations-to-target-different-versions-of-liferay}\n\nApps can be written to work across many different versions of Liferay.\nFor example, suppose you want to publish version 1.0 of your app, which\nyou're supporting on Liferay Portal 6.1 and 6.2. Due to\nincompatibilities between these Liferay versions, it may be impossible\nto create a single binary WAR or JAR file that works across both Liferay\nversions. In this case, you must compile your app twice: once against\nLiferay Portal 6.1 and once against 6.2, producing two different\n\\emph{packages} (also called variations) of your version 1.0 app. Each\npackage has the same functionality, but they're different files. You can\nupload such packages to support your app on different Liferay versions.\nPackages are sometimes referred to as files that make up your app.\n\nNow that you've prepared your app's files and specified its metadata,\nit's time to submit it to Liferay for publishing on the Marketplace!\n\n\\chapter{Submitting Your App}\\label{submitting-your-app}\n\nNow that you have everything in order for publishing your app, you can\nstart the Marketplace app submission process.\n\nGo to your \\emph{Account Home} on\n\\href{http://www.liferay.com}{liferay.com}. In the left side navigation\npanel of your profile page, there are links to pages related to using\napps and developing apps. Links to \\emph{Apps} and \\emph{Metrics} appear\nin the \\emph{Development} section of the navigation panel. You'll use\nthese links heavily during development, so you may want to bookmark this\npage too. Click \\emph{Apps} from the \\emph{Development} section to\naccess your app development page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/marketplace-my-app-manager.png}\n\\caption{Your app development page lists the apps you've developed and\nenables you to add new apps for publishing to the Marketplace.}\n\\end{figure}\n\nNow that you know how to get to your app development page, you can\nsubmit your app! It involves,\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/how-to-publish/-/knowledge_base/publish/specify-your-apps-initial-details}{Specifying\n  your app's initial details}\n\\item\n  \\href{/how-to-publish/-/knowledge_base/publish/uploading-your-app}{Uploading\n  your app}\n\\item\n  \\href{/how-to-publish/-/knowledge_base/publish/creating-your-licensing-and-pricing-model}{Creating\n  your licensing and pricing model}\n\\item\n  \\href{/how-to-publish/-/knowledge_base/publish/submitting-your-app-for-review}{Submitting\n  your app for review}\n\\end{itemize}\n\nTo begin the process of publishing your app, click \\emph{Add New App}.\nThe app wizard appears, so that you can fill in your app's details.\n\n\\section{Specifying Your App's Initial\nDetails}\\label{specifying-your-apps-initial-details}\n\nFrom this screen you enter your app's basic details. Previous articles\n\\href{/how-to-publish/-/knowledge_base/publish/planning-your-apps-distribution}{Planning\nYour App's Distribution} and\n\\href{/how-to-publish/-/knowledge_base/publish/preparing-your-app}{Preparing\nYour App} helped prepare you for filling in these details.\n\nThe top portion of the app wizard's initial screen is shown in the\nfigure below.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/marketplace-add-app-details.png}\n\\caption{The app wizard lets you add details about your app, an icon,\nand screen shots. Scroll down further to see options for specifying\nrelevant URLs, adding tags, and specifying the editions of Liferay that\nyour app supports.}\n\\end{figure}\n\nHere are descriptions of this screen's fields:\n\n\\textbf{App Owner:} Choose to whom the app ``belongs'' once it is\nuploaded--either yourself (personal) or a company. If you'd like to\nsubmit on behalf of a company, click \\emph{Create a company} to go to\nthe \\emph{Join A Company} page. From this page, search to see if your\ncompany already exists in the Liferay Marketplace. If you wish to\npublish your app on behalf of your company, but your company does not\nyet have a Marketplace profile, register your company by clicking\n\\emph{Register My Company}, filling out the registration form, and\nsubmitting the form.\n\n\\textbf{App Pricing:} Choose whether you want the app to be free or\npaid. If you choose paid, you'll have the option to specify pricing and\nlicensing details later in the submission process.\n\nImportantly, you can't change the app from free to paid or from paid to\nfree once the app is published to the Marketplace. In order to offer the\napp in the other license type, you must submit another app under a\ndifferent name (title). If you wish to have both free and paid licenses\nfor your app, you must submit the app under one name for free licenses\nand submit it under another name for paid licenses. Make sure to select\nthe license type (i.e., free or paid) that's best for your app.\n\n\\textbf{Title:} Name of your application.\n\n\\textbf{Description:} Like the name says, this is a description of your\napp. You can put anything you want here, but a good guideline is no more\nthan 4-5 paragraphs. This field does not allow any markup tags or other\ntextual adornments--it's just text.\n\n\\textbf{Localized:} Selecting this displays an \\emph{Add Translation}\nbutton that lets you denote translations your app provides.\n\n\\textbf{Icon:} Upload an icon image to represent your app.\n\n\\textbf{Screen Captures:} Upload one or more screenshots of your app.\nThe screenshots you upload here are displayed in a carousel of rotating\nimages when your app is viewed on the Marketplace.\n\n\\textbf{Category:} Choose the Marketplace category that most accurately\ndescribes what your app does. Users looking for specific types of apps\noften browse categories by clicking on a specific category name in the\nmain Marketplace home page. Having your app listed under the appropriate\ncategory helps them find your app.\n\n\\textbf{Developer Website URL:} This is a URL that should reference the\nweb site associated with the development of the app. For open source\napps, this typically points at the project site where the source is\nmaintained. For others, links to a home page of information about this\napp would be appropriate.\n\n\\textbf{Support URL:} This is a URL that should reference a location\nwhere your app's purchasers or users can get support.\n\n\\textbf{Documentation URL:} What better way to showcase the amazing\ncapabilities of your app than to have a live, running version of it for\npotential buyers to see and use? This field can house a URL pointing to\nsomething as exciting as that and/or documentation for using your app.\n\n\\textbf{API Reference URL:} This is a URL that should reference your\napp's API documentation (e.g., Javadoc).\n\n\\textbf{Source Code URL:} If you'd to provide a link to the source code\nof your app, do so here.\n\n\\textbf{Labs:} You can denote an app as experimental by checking the\nappropriate box.\n\n\\textbf{Security:} If your app is only for Liferay 7.0 or later, or does\nnot contain WAR-style plugins, or does \\emph{not} use Liferay's PACL\nSecurity Manager, check the appropriate box. Otherwise, enable the\nsecurity manager in your app by including the setting\n\\texttt{security-manager-enabled=true} in the\n\\href{http://docs.liferay.com/portal/6.2/propertiesdoc/liferay-plugin-package_6_2_0.properties.html}{\\texttt{liferay-plugin-package.properties}}\nfile in your plugin WAR files.\n\n\\textbf{Tags:} A set of descriptive words that categorize your app.\nThese tags are free-form and can help potential purchasers find your app\nthrough keyword searches, tag clouds, and other search mechanisms. You\ncan click on \\emph{Suggestions} to let Marketplace suggest some tags for\nyou based on the data you've already entered. Click \\emph{Select} to\nselect from existing tags, or you can manually type in new tags.\n\n\\textbf{EULA:} You can use the default end user license agreement (EULA)\nor provide your own. There's a link to the minimum terms which custom\nEULAs must satisfy.\n\nRemember to visit the articles\n\\href{/how-to-publish/-/knowledge_base/publish/planning-your-apps-distribution}{Planning\nYour App's Distribution} and\n\\href{/how-to-publish/-/knowledge_base/publish/preparing-your-app}{Preparing\nYour App} for information to help you decide how best to fill in many of\nthese fields.\n\nOnce you've entered all your app's details, click \\emph{Next} to get to\nthe screen for uploading your app's files.\n\n\\section{Uploading Your App}\\label{uploading-your-app}\n\nOn this screen, you must specify your app's version and upload your\napp's files. Note that the article\n\\href{/how-to-publish/-/knowledge_base/publish/planning-your-apps-distribution}{Planning\nYour App's Distribution} helps you plan your app's versioning scheme.\nLikewise, the article\n\\href{/how-to-publish/-/knowledge_base/publish/preparing-your-app}{Preparing\nYour App} helps you determine which files to upload.\n\nTo start uploading, click \\emph{Browse} and select the files that make\nup your app. Each time you add a file, it automatically begins to upload\nand its compatibility information is scanned. You must upload at least\none file before advancing beyond this screen. Once the files are\nsuccessfully uploaded, a check mark appears next to each plugin, and the\nplugins are displayed based on their compatibility information.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/marketplace-app-version-and-upload-files.png}\n\\caption{Specify a set of files for each Liferay version you wish to\nsupport.}\n\\end{figure}\n\nIf you selected \\emph{Free} for your app pricing, click \\emph{Next} to\nadvance to the final screen. If you selected \\emph{Paid}, you'll be\npresented with additional options for licensing and pricing your app.\n\n\\section{Creating Your Licensing and Pricing\nModel}\\label{creating-your-licensing-and-pricing-model}\n\nCarefully consider which licensing structure best meets your needs. Once\nyour app has been approved, these options, except for price updates,\ncannot be changed.\n\n\\textbf{Choose a license term:}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/marketplace-configure-app-license.png}\n\\caption{Choosing license terms for Marketplace apps is easy.}\n\\end{figure}\n\nChoosing \\emph{Perpetual} allows the app to continue running without\nexpiration. Choosing \\emph{Non-Perpetual} expires the app's license one\nyear from the purchase date. Perpetual License also allows you to offer\nSupport Services, which the customer must renew annually to maintain\naccess to app updates and support. If you choose not to offer Support\nServices with a Perpetual License, customers will be provided with app\nupdates only, whenever updates are available.\n\nImportantly, you can't change your app's license terms (perpetual or\nnon-perpetual) once the app is approved. In order to release an approved\napp under a different license term you must submit another app under a\nnew name (title). So make sure you think through the license term that\nmakes the most sense for your app.\n\n\\textbf{Creating license options:}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/marketplace-create-license-types.png}\n\\caption{You can create multiple license options for your Marketplace\napps.}\n\\end{figure}\n\nCreating license options allows you to design license bundles and to\nspecify discounts for customers who purchase more Liferay Instances for\nyour app (a Liferay Instance or Instance refers to a single installation\nof the Liferay Portal). Also you can designate different pricing for\nStandard Licenses vs. Developer Licenses. You must specify at least one\nlicense option, but no more than 10 options per type. You'll price these\noptions on the next page.\n\nYou can add or remove bundles (quantities) of your app's license type\neven after the app is approved. You must however, always honor any\nsupport agreements you have with current customers that are using\nbundles you've removed.\n\n\\textbf{Paid support:} You can offer additional paid support services\nfor your app. If you select this option, customers can contact you with\nsupport requests and are entitled to regular updates.\n\nOnce an app that offers a support subscription offering is approved, you\ncan't remove that support subscription offering. You can however, add a\nsupport subscription offering to an approved app.\n\n\\textbf{Offer a trial:} You can offer a free 30-day trial of your app,\nrestricted to one Instance and 25 users.\n\nWhen you're finished selecting all the options for your license, proceed\nto the next page to determine the app's pricing and availability.\n\n\\textbf{Pricing:}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/marketplace-app-pricing.png}\n\\caption{Liferay makes it easy to price your app's license types and\nspecify their availability to countries around the world.}\n\\end{figure}\n\nBased on your selections from the previous page, you'll have price\nfields for each license option and for any support option you offered.\n\nWhen you have completed your app's pricing and availability, click\n\\emph{Next} to advance to the app preview screen.\n\n\\section{Submitting Your App For\nReview}\\label{submitting-your-app-for-review}\n\nThe \\emph{App Preview} screen lets you preview your app as it will\nappear on the Marketplace. If you want to change things before finally\nsubmitting your app, click \\emph{Edit} to go back and continue making\nchanges until you're satisfied.\n\nBefore finalizing your app's submission, make sure that you're pleased\nwith the values you've set for the following app details, as these\ndetails can't be modified once the app is approved:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Free vs.~paid\n\\item\n  License term (perpetual vs.~non-perpetual)\n\\item\n  Support subscription offering (cannot be removed)\n\\end{itemize}\n\nOnce you are satisfied, click \\emph{Submit for Review}.\n\nAt this point, the Liferay Marketplace staff starts to review your app\nand test it. If you need to make changes to your app during its review\nor if you're curious about the rigors of the review process, make sure\nto read the next section of this guide.\n\n\\chapter{Understanding the App Review\nProcess}\\label{understanding-the-app-review-process}\n\nThe Liferay Marketplace app QA/review process begins as soon as you\nsubmit your app for review. Every third-party app submitted to the\nLiferay Marketplace is reviewed by our team to ensure that certain\nstandards for information are upheld and the app installs as expected.\n\\emph{Liferay cannot, however, be a substitute for your own testing and\ndebugging team}. Ultimately, you must test, refine, and ensure that your\napp functions as promised and performs as expected.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Liferay is not responsible for the behavior (or\nmisbehavior) of apps on the Marketplace. For details regarding this,\nconsult the \\emph{Liferay Marketplace User Agreement}, \\emph{Liferay\nMarketplace Developer Agreement}, and the individual \\emph{End User\nLicense Agreements} associated with each app.\n\n\\noindent\\hrulefill\n\nOnce you've submitted your app for review, your app's status changes as\nit moves through the review process. We email you on status changes and\nprovide as much detail as we can if we discover potential issues with\nyour app. Overall, we don't want our app review process to feel like a\nbarrier or a black box. We love having new apps in the Marketplace, and\nwe try to be as helpful as we can throughout the approval process!\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/app_review_process.png}\n\\caption{Liferay informs you at every step during the QA/review\nprocess.}\n\\end{figure}\n\nIf you submit an updated version of a previously approved app, you'll\nsee these statuses: Approved (Version Unsubmitted), Approved (Version\nPending), Approved (Version Pending QA), and Approved (Version Denied).\n\nThe app review process consists of \\textbf{Two Major Phases}:\n\n\\phantomsection\\label{article-33460874}\nReview Phase\n\nEst. Time Frame\n\nApp metadata review\n\nOur team reviews your app's metadata to confirm that titles,\ndescriptions, images, etc. are appropriate.\n\n\\textasciitilde1 week\n\nApp QA test\n\nLiferay ensures that apps meet a minimal set of requirements:\n\nPasses anti-virus scan\n\nDeploys successfully on standard Liferay supported\nenvironments/platforms without errors.\n\nBasic functionality ``smoke'' test.\n\nLiferay does not do source code review and does not ask for your source\ncode. Further, Liferay is not responsible for the behavior (or\nmisbehavior) of apps on the Marketplace. Please consult the Liferay\nMarketplace User Agreement, Liferay Marketplace Developer Agreement, and\nthe individual End User License Agreements associated with each app.\n\n\\textasciitilde1-2 weeks\n\n\\textbf{Our QA Test Environments} are summarized below. At a minimum,\ntest your app against these environments prior to submission. If\ntechnical reasons prevent your app from running on certain platforms\n(e.g., app server-specific issues), specify compatibility in the app\ndescription and documentation so that our review team can exclude\ncertain test conditions, if necessary.\n\n\\phantomsection\\label{article-33460919}\nLiferay Version\n\n6.x\n\n7.0.x\n\n7.1.x\n\n7.2.x\n\n7.3.x\n\nOperating Systems\n\nUbuntu 11x and Windows 10 x64\n\nDatabase\n\nMySQL 5.5.x\n\nMySQL 5.6.x\n\nMySQL 5.7.x\n\nApplication Server *\n\nTomcat 7\n\nTomcat 8\n\nTomcat 9\n\nJDK\n\nOracle JDK 6, 7\n\nOracle JDK 8 / JDK 11 (7.1 and higher)\n\nBrowser\n\nChrome\n\n\\textbf{*} You can request certification on additional, optional\napplication servers. For Liferay 7.x, Wildfly 10 is available. For\nLiferay 6.x, Glassfish 3.1 and an appropriate JBoss version are\navailable. Please add the request in the app submission panel's\n\\emph{Note to testers} section.\n\nOnce your app is approved by Marketplace staff, you'll get email\nnotification. When your app is approved, it is made available on\nMarketplace. The app also appears on your public Profile page, which\nlists all apps that you or your company developed and published.\n\nIf your app is rejected, you receive an email explaining the reasons for\nrejection. You can then make the requested changes and re-submit the app\nfor approval.\n\nAfter you've successfully published your app, you might get all kinds of\nfeedback from users and yourself about what's right and wrong with it.\nThe next article explores how to make changes once you have published\nyour app.\n\n\\chapter{Managing Your Published App}\\label{managing-your-published-app}\n\nYou've launched your new app on the Marketplace. Congratulations! As you\nsettle down from your launch celebration, you might wonder how your app\nis performing in the Marketplace. Is there a way to monitor how many\npeople are viewing it and downloading it? And what about those features\nthat didn't make this release? How difficult is it to upload your app's\nnext version with those features? Don't sweat it. The Liferay\nMarketplace and app wizard are here to help.\n\nYou can track app performance. Marketplace shows app trends like views,\ndownloads, and purchases. You can upload new versions of your app and\nmake changes to some of its details with the App Wizard. Read on to\nlearn how all of this works.\n\n\\section{Tracking App Performance}\\label{tracking-app-performance}\n\nOne of the main reasons for developing and publishing apps on\nMarketplace is to drive app downloads and adoption. Marketplace gives\nyou detailed reports on views, downloads, and purchases of your app(s).\nTo access these metrics, navigate to \\emph{Account Home} →\n\\emph{Metrics} (under \\emph{Development}).\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/marketplace-app-metrics-over-time.png}\n\\caption{The App Performance view in Marketplace lets you see how many\ntimes your apps have been viewed, downloaded, and purchased over a time\ninterval.}\n\\end{figure}\n\nThe view shown above is the default metrics view for a developer's apps.\nAcross the bottom is a list of data series options (\\emph{Views},\n\\emph{Downloads}, or \\emph{Purchases}). At the top, you set a date\nrange. In the middle, a graph shows the data within the date range.\nFinally, the graphed data is also shown in tabular format, in case you\nwant to know the exact values making up the graph. The different types\nof data available to view are described below.\n\n\\subsection{Views}\\label{views}\n\nWhen someone searches or browses the Marketplace, they click on apps to\nsee their details. When this occurs for an app, Marketplace records a\n\\emph{View} for it. When you select \\emph{Views} in the App Metrics\nscreen, the app view count over time appears. The App Metrics screen\nshows \\emph{Views} by default. The number of possible views per day per\nuser is unlimited.\n\n\\subsection{Downloads}\\label{downloads}\n\nMarketplace records a \\emph{Download} for your app when someone\ndownloads any package of any version of your app. The number of possible\ndownloads per day per user is unlimited.\n\n\\subsection{Purchases}\\label{purchases}\n\nThe Marketplace counts app purchases too.\n\nAs you see areas to improve your app or when necessary changes come up,\nyou can make changes to your published app. The next article describes\nchanges you can make.\n\n\\section{Making Changes to Published\nApps}\\label{making-changes-to-published-apps}\n\nYou can make these kinds of changes during the life of your published\napp:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Edit your app details (e.g., description, icon, etc.)\n\\item\n  Add new license bundles (quantities)\n\\item\n  Edit app prices of bundles\n\\item\n  Modify regional availability\n\\item\n  Add support for a new version of Liferay\n\\item\n  Release a new version of your app to fix bugs or offer new\n  functionality\n\\item\n  Disable your app\n\\end{itemize}\n\nYour currently approved app remains available while you're submitting\nchanges and while Liferay reviews and tests your app changes.\n\nThe rest of this article describes each kind of change.\n\n\\subsection{Editing Your App's\nMetadata}\\label{editing-your-apps-metadata}\n\nApp metadata includes the name, description, icon, screenshots, and\nother information you supplied during the app creation process. Here's\nhow to edit your app's metadata:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Account Home} → \\emph{Development} → \\emph{Apps}. A\n  listing of your published apps appears.\n\\item\n  Select the app you wish to edit. This screen shows you what the app\n  looks like on Marketplace.\n\\item\n  Click the \\emph{Edit} button at the bottom of the preview. Now you can\n  edit details and add new files to the your existing version of your\n  app. The current values as they appear in your app pre-populate the\n  form.\n\\item\n  Make any changes as needed on this screen.\n\\item\n  Click \\emph{Next}. If you don't need to edit any more variations, you\n  can continue clicking \\emph{Next} until you reach the final preview\n  screen.\n\\item\n  Click \\emph{Submit for Review} to submit your changes for review.\n\\end{enumerate}\n\nAll changes must go through the submission process. If your app revision\nonly consists of metadata changes, it need only go through the\n\\href{/how-to-publish/-/knowledge_base/publish/understanding-the-app-review-process}{\\emph{App\nmetadata review}} phase of the review process.\n\nOnce approved, the changes you request appear for your app on the\nMarketplace.\n\n\\subsection{Editing App Prices}\\label{editing-app-prices}\n\nYou can change your app's prices, add or remove bundles, and modify\nregional availability for a variety of reasons, whether it's to run a\npromotional offer, or to adjust your pricing model to better account for\napp demand. Here's how to edit app prices:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Company Profile Home} → \\emph{Apps}. A listing of\n  your published apps appears.\n\\item\n  Select the app you wish to edit.\n\\item\n  At the bottom, click \\emph{Edit} → \\emph{Pricing}.\n\\item\n  Edit your app's prices.\n\\item\n  When you've finished editing your app's prices, click \\emph{Next} and\n  save your app.\n\\end{enumerate}\n\nThese changes do not require Liferay verification process to approve.\nThe changes show immediately.\n\nYou cannot however, modify the following attributes of an approved app:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Free vs.~paid\n\\item\n  License term (perpetual vs.~non-perpetual)\n\\item\n  Support subscription offering (cannot be removed)\n\\end{itemize}\n\nNext, you can consider adding support for new versions of Liferay.\n\n\\subsection{Adding Support for New Versions of\nLiferay}\\label{adding-support-for-new-versions-of-liferay}\n\nIf you must add files to support another Liferay release, the process is\nsimilar to editing an app.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Account Home} → \\emph{Apps}. A listing of your\n  published apps appears.\n\\item\n  Select the app you wish to edit.\n\\item\n  Click the \\emph{Edit} button to edit that app.\n\\item\n  Click \\emph{Next} to advance past the details screen (make any\n  necessary changes). The version edit screen appears.\n\\item\n  Click \\emph{Next} to advance past the version edit screen (you can't\n  actually edit the version number of an already-approved version, but\n  you can edit the ``What's New'' information). The File Upload screen\n  appears.\n\n  This screen should look familiar---it's the same workflow used when\n  you initially created your app! The difference is that you can't edit\n  pre-approved files for specific Liferay releases. You can only add\n  \\emph{new} files for a different Liferay release (if you must update\n  existing files, you must create a new version of the app---see the\n  later section on adding versions for details on how to do this).\n\\item\n  Upload your new files (ensuring that your new plugins have updated\n  compatibility information: see the\n  \\href{/how-to-publish/-/knowledge_base/publish/preparing-your-app}{Marketplace\n  App Metadata Guidelines} for details on versions).\n\\item\n  Click \\emph{Next}, and observe the newly-added files listed at the\n  bottom of the preview screen.\n\\item\n  Click \\emph{Submit for Review} to submit your requested change (adding\n  files).\n\\end{enumerate}\n\nLiferay reviews the files, and once approved, the new package becomes\navailable for download in the Marketplace.\n\n\\subsection{Releasing a New Version of your\nApp}\\label{releasing-a-new-version-of-your-app}\n\nAs time passes, you may wish to add new functionality to your app or fix\na batch of bugs. This can be accomplished by releasing a new version of\nyour app. New versions offer your users new functionality and bug fixes,\nand users are generally encouraged to use the latest version. In\naddition, when a new version of your app becomes available, Marketplace\nnotifies existing users.\n\nNew versions of your apps are created in a similar fashion to creating\nnew apps.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Account Home} → \\emph{Apps}. A listing of your\n  published apps appears.\n\\item\n  Select the app you wish to edit.\n\\item\n  Click the \\emph{Edit} button to edit that app. The Details screen\n  appears.\n\\item\n  At the bottom of the Details screen, click the \\emph{Add New Version}\n  button. The process of adding a new version begins, starting with the\n  App Details screen. Data from the current version of the app\n  pre-populates the screen.\n\n  You can modify the screen's pre-populated data. Since this is a new\n  version of an existing app, making major changes (such as completely\n  changing the name or description) might unsettle existing users.\n  Uploading new screenshots and refreshing app icons is common. Note\n  that you cannot change the app owner (such as moving from a\n  personally-developed app to a company-developed app).\n\\item\n  Click \\emph{Next} until you get to the \\emph{Add App Version} screen.\n\\item\n  Specify a new version name for this version of your app.\n\\item\n  Add \\emph{What's New} text (optional). Common changes to mention are\n  new features and bug fix information.\n\\item\n  Click \\emph{Next}. The file upload screen appears.\n\\item\n  Upload the files associated with the new version of the app. You must\n  upload all files for all supported Liferay versions again, even if\n  they have not changed since the last version of your app.\n\\item\n  Continue clicking \\emph{Next} until you reach the final preview\n  screen.\n\\item\n  Click \\emph{Submit for Review} to submit the new version of your app\n  for review.\n\\end{enumerate}\n\nThe Liferay Marketplace staff starts to review your app's new version\nand test it.\n\n\\subsection{Deactivating Your App}\\label{deactivating-your-app}\n\nWhen the time comes to retire your app, you can \\emph{Deactivate} it.\nDeactivating an app makes it unavailable to new customers, and it\ndoesn't appear in any public Marketplace listings. Existing customers\nthat have already downloaded your app can continue downloading the\nlegacy versions of the app they have already acquired, but they can't\ndownload any versions they've not already received. The app remains in\nyour inventory, with all of its history, in case you choose to\nre-activate or reference it.\n\nHere's how to deactivate your app:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Account Home} → \\emph{Apps}. A listing of your\n  published apps appears.\n\\item\n  Select the app you wish to deactivate.\n\\item\n  Click the \\emph{Deactivate} button.\n\\end{enumerate}\n\nIn this set of articles, we looked at how to create, publish, maintain,\nand track Liferay Marketplace apps. You can do this through the App\nManager that's available on your \\emph{Account Home} page on\n\\href{http://liferay.com}{liferay.com} (liferay.com) account required!).\nWe covered requirements for publishing variations of your apps for\ndifferent versions of Liferay. Next, we showed how you can publish an\napp on the Marketplace and how you can modify the app as it evolves.\nFinally, we looked at tracking app adoption using view, download, and\ninstall metrics.\n"
  },
  {
    "path": "book/developer/reference.aux",
    "content": "\\relax \n\\providecommand{\\transparent@use}[1]{}\n\\providecommand\\hyper@newdestlabel[2]{}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {640}Developer Reference}{1873}{chapter.640}\\protected@file@percent }\n\\newlabel{developer-reference}{{640}{1873}{Developer Reference}{chapter.640}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {641}Classes Moved From portal-service.jar}{1875}{chapter.641}\\protected@file@percent }\n\\newlabel{classes-moved-from-portal-service.jar}{{641}{1875}{Classes Moved From portal-service.jar}{chapter.641}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {642}Export/Import and Staging}{1929}{chapter.642}\\protected@file@percent }\n\\newlabel{exportimport-and-staging}{{642}{1929}{Export/Import and Staging}{chapter.642}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {643}Decision to Implement Staging}{1931}{chapter.643}\\protected@file@percent }\n\\newlabel{decision-to-implement-staging}{{643}{1931}{Decision to Implement Staging}{chapter.643}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {644}Liferay Archive (LAR) File}{1933}{chapter.644}\\protected@file@percent }\n\\newlabel{liferay-archive-lar-file}{{644}{1933}{Liferay Archive (LAR) File}{chapter.644}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {644.1}LAR File Anatomy}{1933}{section.644.1}\\protected@file@percent }\n\\newlabel{lar-file-anatomy}{{644.1}{1933}{LAR File Anatomy}{section.644.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {644.2}LAR Manifest}{1934}{section.644.2}\\protected@file@percent }\n\\newlabel{lar-manifest}{{644.2}{1934}{LAR Manifest}{section.644.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {644.3}LAR Folders}{1935}{section.644.3}\\protected@file@percent }\n\\newlabel{lar-folders}{{644.3}{1935}{LAR Folders}{section.644.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {644.1}{\\ignorespaces Entities, Portlets, and Pages are defined in a LAR in different places.}}{1936}{figure.644.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {645}Front-End Reference}{1937}{chapter.645}\\protected@file@percent }\n\\newlabel{front-end-reference}{{645}{1937}{Front-End Reference}{chapter.645}{}}\n\\gdef \\LT@xxxii {\\LT@entry \n    {1}{114.43875pt}\\LT@entry \n    {1}{120.43875pt}\\LT@entry \n    {1}{120.43875pt}\\LT@entry \n    {1}{114.43875pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {646}Liferay DXP FreeMarker Macros}{1939}{chapter.646}\\protected@file@percent }\n\\newlabel{liferay-dxp-freemarker-macros}{{646}{1939}{Liferay DXP FreeMarker Macros}{chapter.646}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {646.1}Reference Examples}{1940}{section.646.1}\\protected@file@percent }\n\\newlabel{reference-examples}{{646.1}{1940}{Reference Examples}{section.646.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {647}Front-End Taglibs}{1943}{chapter.647}\\protected@file@percent }\n\\newlabel{front-end-taglibs}{{647}{1943}{Front-End Taglibs}{chapter.647}{}}\n\\gdef \\LT@xxxiii {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {648}Liferay Theme Objects Available in JSPs}{1945}{chapter.648}\\protected@file@percent }\n\\newlabel{liferay-theme-objects-available-in-jsps}{{648}{1945}{Liferay Theme Objects Available in JSPs}{chapter.648}{}}\n\\gdef \\LT@xxxiv {\\LT@entry \n    {1}{167.54416pt}\\LT@entry \n    {1}{302.21085pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {649}Liferay Portlet Objects Available in JSPs}{1947}{chapter.649}\\protected@file@percent }\n\\newlabel{liferay-portlet-objects-available-in-jsps}{{649}{1947}{Liferay Portlet Objects Available in JSPs}{chapter.649}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {650}Using the Liferay UI Taglib}{1949}{chapter.650}\\protected@file@percent }\n\\newlabel{using-the-liferay-ui-taglib}{{650}{1949}{Using the Liferay UI Taglib}{chapter.650}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {651}Liferay UI Icons}{1951}{chapter.651}\\protected@file@percent }\n\\newlabel{liferay-ui-icons}{{651}{1951}{Liferay UI Icons}{chapter.651}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {651.1}{\\ignorespaces Use the image attribute to use a theme icon.}}{1951}{figure.651.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {651.2}{\\ignorespaces The Liferay UI taglib offers multiple icons for use in your app.}}{1952}{figure.651.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {651.3}{\\ignorespaces Liferay UI icons can be configured based on language.}}{1953}{figure.651.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {651.4}{\\ignorespaces You can use the icon attribute to include Font Awesome icons in your app.}}{1953}{figure.651.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {651.5}{\\ignorespaces You can use Font Awesome icons in your app.}}{1953}{figure.651.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {651.1}Related Topics}{1953}{section.651.1}\\protected@file@percent }\n\\newlabel{related-topics}{{651.1}{1953}{Related Topics}{section.651.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {652}Liferay UI Icon Lists}{1955}{chapter.652}\\protected@file@percent }\n\\newlabel{liferay-ui-icon-lists}{{652}{1955}{Liferay UI Icon Lists}{chapter.652}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {652.1}{\\ignorespaces Icon lists display an app's actions at all times.}}{1955}{figure.652.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {652.1}Related Topics}{1956}{section.652.1}\\protected@file@percent }\n\\newlabel{related-topics-1}{{652.1}{1956}{Related Topics}{section.652.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {653}Liferay UI Icon Menus}{1957}{chapter.653}\\protected@file@percent }\n\\newlabel{liferay-ui-icon-menus}{{653}{1957}{Liferay UI Icon Menus}{chapter.653}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {653.1}{\\ignorespaces Setting up an icon menu is a piece of cake.}}{1957}{figure.653.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {653.1}Related Topics}{1958}{section.653.1}\\protected@file@percent }\n\\newlabel{related-topics-2}{{653.1}{1958}{Related Topics}{section.653.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {654}Liferay UI Tabs}{1959}{chapter.654}\\protected@file@percent }\n\\newlabel{liferay-ui-tabs}{{654}{1959}{Liferay UI Tabs}{chapter.654}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {654.1}{\\ignorespaces Tabs are a useful way to organize configuration options into individual sections within the same UI.}}{1960}{figure.654.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {654.1}Related Topics}{1960}{section.654.1}\\protected@file@percent }\n\\newlabel{related-topics-3}{{654.1}{1960}{Related Topics}{section.654.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {655}Liferay UI Icon Help}{1961}{chapter.655}\\protected@file@percent }\n\\newlabel{liferay-ui-icon-help}{{655}{1961}{Liferay UI Icon Help}{chapter.655}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {655.1}{\\ignorespaces Here's an example of the icon help tag.}}{1961}{figure.655.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {655.1}Related Topics}{1962}{section.655.1}\\protected@file@percent }\n\\newlabel{related-topics-4}{{655.1}{1962}{Related Topics}{section.655.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {655.2}{\\ignorespaces help icons are used throughout the Control Panel.}}{1963}{figure.655.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {656}Using Liferay Front-end Taglibs in Your Portlet}{1965}{chapter.656}\\protected@file@percent }\n\\newlabel{using-liferay-front-end-taglibs-in-your-portlet}{{656}{1965}{Using Liferay Front-end Taglibs in Your Portlet}{chapter.656}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {657}Liferay Front-end Add Menu}{1967}{chapter.657}\\protected@file@percent }\n\\newlabel{liferay-front-end-add-menu}{{657}{1967}{Liferay Front-end Add Menu}{chapter.657}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {657.1}{\\ignorespaces The add button pattern consists of an \\texttt  {add-menu} tag and at least one \\texttt  {add-menu-item} tag.}}{1967}{figure.657.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {657.2}{\\ignorespaces The add button pattern consists of an \\texttt  {add-menu} tag and at least one \\texttt  {add-menu-item} tag.}}{1968}{figure.657.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {657.1}Related Topics}{1968}{section.657.1}\\protected@file@percent }\n\\newlabel{related-topics-5}{{657.1}{1968}{Related Topics}{section.657.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {658}Liferay Front-end Cards}{1969}{chapter.658}\\protected@file@percent }\n\\newlabel{liferay-front-end-cards}{{658}{1969}{Liferay Front-end Cards}{chapter.658}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {658.1}Horizontal Card}{1969}{section.658.1}\\protected@file@percent }\n\\newlabel{horizontal-card}{{658.1}{1969}{Horizontal Card}{section.658.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {658.2}Icon Vertical Card}{1969}{section.658.2}\\protected@file@percent }\n\\newlabel{icon-vertical-card}{{658.2}{1969}{Icon Vertical Card}{section.658.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {658.1}{\\ignorespaces Horizontal cards are perfect to display files and documents.}}{1970}{figure.658.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {658.3}Vertical Card}{1970}{section.658.3}\\protected@file@percent }\n\\newlabel{vertical-card}{{658.3}{1970}{Vertical Card}{section.658.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {658.2}{\\ignorespaces Vertical icon cards are perfect to display an entity selection, such as a web content article.}}{1971}{figure.658.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {658.4}HTML Vertical Card}{1971}{section.658.4}\\protected@file@percent }\n\\newlabel{html-vertical-card}{{658.4}{1971}{HTML Vertical Card}{section.658.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {658.3}{\\ignorespaces Vertical cards are perfect to display files and documents.}}{1972}{figure.658.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {658.4}{\\ignorespaces Html vertical cards let you display custom HTML in the card's header.}}{1973}{figure.658.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {658.5}User Vertical Card}{1973}{section.658.5}\\protected@file@percent }\n\\newlabel{user-vertical-card}{{658.5}{1973}{User Vertical Card}{section.658.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {658.5}{\\ignorespaces User vertical cards are perfect to display files and documents.}}{1974}{figure.658.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {658.6}Related Topics}{1974}{section.658.6}\\protected@file@percent }\n\\newlabel{related-topics-6}{{658.6}{1974}{Related Topics}{section.658.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {659}Liferay Front-end Info Bar}{1977}{chapter.659}\\protected@file@percent }\n\\newlabel{liferay-front-end-info-bar}{{659}{1977}{Liferay Front-end Info Bar}{chapter.659}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {659.1}{\\ignorespaces The info bar tags create a sidebar panel toggler that reveals additional info.}}{1978}{figure.659.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {659.2}{\\ignorespaces The info bar tags create a sidebar panel toggler that reveals additional info.}}{1979}{figure.659.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {659.1}Related Topics}{1979}{section.659.1}\\protected@file@percent }\n\\newlabel{related-topics-7}{{659.1}{1979}{Related Topics}{section.659.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {660}Liferay Front-end Management Bar}{1981}{chapter.660}\\protected@file@percent }\n\\newlabel{liferay-front-end-management-bar}{{660}{1981}{Liferay Front-end Management Bar}{chapter.660}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {660.1}{\\ignorespaces The Management Bar lets the user customize how the app displays content.}}{1981}{figure.660.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {660.2}{\\ignorespaces The \\texttt  {management-bar-buttons} tag contains the Management Bar's main buttons.}}{1982}{figure.660.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {660.3}{\\ignorespaces The \\texttt  {management-bar-display-buttons} tag contains the content's display options.}}{1982}{figure.660.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {660.4}{\\ignorespaces The \\texttt  {management-bar-filters} tag contains the content filtering options.}}{1982}{figure.660.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {660.5}{\\ignorespaces The management bar keeps track of the items selected and displays the actions to execute on them.}}{1982}{figure.660.5}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {661}Including Actions in the Management Bar}{1985}{chapter.661}\\protected@file@percent }\n\\newlabel{including-actions-in-the-management-bar}{{661}{1985}{Including Actions in the Management Bar}{chapter.661}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {661.1}{\\ignorespaces You can select individual results or all results at once.}}{1985}{figure.661.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {661.2}{\\ignorespaces You can have as many actions as your app requires.}}{1986}{figure.661.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {661.1}Related Topics}{1986}{section.661.1}\\protected@file@percent }\n\\newlabel{related-topics-8}{{661.1}{1986}{Related Topics}{section.661.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {662}Disabling All or Portions of the Management Bar}{1987}{chapter.662}\\protected@file@percent }\n\\newlabel{disabling-all-or-portions-of-the-management-bar}{{662}{1987}{Disabling All or Portions of the Management Bar}{chapter.662}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {662.1}Related Topics}{1987}{section.662.1}\\protected@file@percent }\n\\newlabel{related-topics-9}{{662.1}{1987}{Related Topics}{section.662.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {662.1}{\\ignorespaces You can disable all or portions of the Management Bar.}}{1988}{figure.662.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {663}Using the Liferay Util Taglib}{1989}{chapter.663}\\protected@file@percent }\n\\newlabel{using-the-liferay-util-taglib}{{663}{1989}{Using the Liferay Util Taglib}{chapter.663}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {664}Using Liferay Util Body Bottom}{1991}{chapter.664}\\protected@file@percent }\n\\newlabel{using-liferay-util-body-bottom}{{664}{1991}{Using Liferay Util Body Bottom}{chapter.664}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {664.1}Related Topics}{1991}{section.664.1}\\protected@file@percent }\n\\newlabel{related-topics-10}{{664.1}{1991}{Related Topics}{section.664.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {665}Using Liferay Util Body Top}{1993}{chapter.665}\\protected@file@percent }\n\\newlabel{using-liferay-util-body-top}{{665}{1993}{Using Liferay Util Body Top}{chapter.665}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {665.1}Related Topics}{1993}{section.665.1}\\protected@file@percent }\n\\newlabel{related-topics-11}{{665.1}{1993}{Related Topics}{section.665.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {666}Using Liferay Util Buffer}{1995}{chapter.666}\\protected@file@percent }\n\\newlabel{using-liferay-util-buffer}{{666}{1995}{Using Liferay Util Buffer}{chapter.666}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {666.1}{\\ignorespaces You can use the Liferay Util Buffer tag to save pieces of markup to reuse in your JSP.}}{1995}{figure.666.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {666.1}Related Topics}{1995}{section.666.1}\\protected@file@percent }\n\\newlabel{related-topics-12}{{666.1}{1995}{Related Topics}{section.666.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {667}Using Liferay Util Dynamic Include}{1997}{chapter.667}\\protected@file@percent }\n\\newlabel{using-liferay-util-dynamic-include}{{667}{1997}{Using Liferay Util Dynamic Include}{chapter.667}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {667.1}Related Topics}{1997}{section.667.1}\\protected@file@percent }\n\\newlabel{related-topics-13}{{667.1}{1997}{Related Topics}{section.667.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {668}Using Liferay Util Get URL}{1999}{chapter.668}\\protected@file@percent }\n\\newlabel{using-liferay-util-get-url}{{668}{1999}{Using Liferay Util Get URL}{chapter.668}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {668.1}Related Topics}{1999}{section.668.1}\\protected@file@percent }\n\\newlabel{related-topics-14}{{668.1}{1999}{Related Topics}{section.668.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {668.1}{\\ignorespaces You can use the Liferay Util Get URL tag to scrape URLs.}}{2000}{figure.668.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {669}Using Liferay Util HTML Bottom}{2001}{chapter.669}\\protected@file@percent }\n\\newlabel{using-liferay-util-html-bottom}{{669}{2001}{Using Liferay Util HTML Bottom}{chapter.669}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {669.1}Related Topics}{2001}{section.669.1}\\protected@file@percent }\n\\newlabel{related-topics-15}{{669.1}{2001}{Related Topics}{section.669.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {670}Using Liferay Util HTML Top}{2003}{chapter.670}\\protected@file@percent }\n\\newlabel{using-liferay-util-html-top}{{670}{2003}{Using Liferay Util HTML Top}{chapter.670}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {670.1}Related Topics}{2003}{section.670.1}\\protected@file@percent }\n\\newlabel{related-topics-16}{{670.1}{2003}{Related Topics}{section.670.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {671}Using Liferay Util Include}{2005}{chapter.671}\\protected@file@percent }\n\\newlabel{using-liferay-util-include}{{671}{2005}{Using Liferay Util Include}{chapter.671}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {671.1}Related Topics}{2005}{section.671.1}\\protected@file@percent }\n\\newlabel{related-topics-17}{{671.1}{2005}{Related Topics}{section.671.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {672}Using Liferay Util Param}{2007}{chapter.672}\\protected@file@percent }\n\\newlabel{using-liferay-util-param}{{672}{2007}{Using Liferay Util Param}{chapter.672}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {672.1}Related Topics}{2007}{section.672.1}\\protected@file@percent }\n\\newlabel{related-topics-18}{{672.1}{2007}{Related Topics}{section.672.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {673}Using Liferay Util Whitespace Remover}{2009}{chapter.673}\\protected@file@percent }\n\\newlabel{using-liferay-util-whitespace-remover}{{673}{2009}{Using Liferay Util Whitespace Remover}{chapter.673}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {673.1}Related Topics}{2009}{section.673.1}\\protected@file@percent }\n\\newlabel{related-topics-19}{{673.1}{2009}{Related Topics}{section.673.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {674}Using the Clay Taglib in Your portlets}{2011}{chapter.674}\\protected@file@percent }\n\\newlabel{using-the-clay-taglib-in-your-portlets}{{674}{2011}{Using the Clay Taglib in Your portlets}{chapter.674}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {675}Clay Alerts}{2013}{chapter.675}\\protected@file@percent }\n\\newlabel{clay-alerts}{{675}{2013}{Clay Alerts}{chapter.675}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {675.1}Embedded Alerts}{2013}{section.675.1}\\protected@file@percent }\n\\newlabel{embedded-alerts}{{675.1}{2013}{Embedded Alerts}{section.675.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {675.1}{\\ignorespaces The danger alert notifies the user of an error or issue.}}{2013}{figure.675.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {675.2}{\\ignorespaces The success alert notifies the user when an action is successful.}}{2014}{figure.675.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {675.3}{\\ignorespaces The info alert displays general information to the user.}}{2014}{figure.675.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {675.4}{\\ignorespaces The warning alert displays a warning message to the user.}}{2014}{figure.675.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {675.2}Stripe Alerts}{2014}{section.675.2}\\protected@file@percent }\n\\newlabel{stripe-alerts}{{675.2}{2014}{Stripe Alerts}{section.675.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {675.5}{\\ignorespaces The danger striped alert notifies the user that an action has failed.}}{2014}{figure.675.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {675.6}{\\ignorespaces The success striped alert notifies the user that an action has completed successfully.}}{2015}{figure.675.6}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {675.7}{\\ignorespaces The info striped alert displays general information about an action to the user.}}{2015}{figure.675.7}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {675.8}{\\ignorespaces The warning striped alert warns the user about an action.}}{2015}{figure.675.8}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {675.3}Related Topics}{2015}{section.675.3}\\protected@file@percent }\n\\newlabel{related-topics-20}{{675.3}{2015}{Related Topics}{section.675.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {676}Clay Badges}{2017}{chapter.676}\\protected@file@percent }\n\\newlabel{clay-badges}{{676}{2017}{Clay Badges}{chapter.676}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {676.1}Badge Types}{2017}{section.676.1}\\protected@file@percent }\n\\newlabel{badge-types}{{676.1}{2017}{Badge Types}{section.676.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {676.1}{\\ignorespaces A primary badge is bright blue, commanding attention like the primary button of a form.}}{2017}{figure.676.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {676.2}{\\ignorespaces A secondary badge is light-grey and draws less focus than a primary button.}}{2018}{figure.676.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {676.3}{\\ignorespaces A info badge is dark blue and meant for numbers related to general information.}}{2018}{figure.676.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {676.4}{\\ignorespaces An error badge displays numbers related to an error.}}{2019}{figure.676.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {676.5}{\\ignorespaces A success badge displays numbers related to a successful action.}}{2019}{figure.676.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {676.6}{\\ignorespaces A warning badge displays numbers related to warnings that should be addressed.}}{2019}{figure.676.6}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {676.2}Related Topics}{2020}{section.676.2}\\protected@file@percent }\n\\newlabel{related-topics-21}{{676.2}{2020}{Related Topics}{section.676.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {677}Clay Buttons}{2021}{chapter.677}\\protected@file@percent }\n\\newlabel{clay-buttons}{{677}{2021}{Clay Buttons}{chapter.677}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {677.1}Types}{2021}{section.677.1}\\protected@file@percent }\n\\newlabel{types}{{677.1}{2021}{Types}{section.677.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {677.1}{\\ignorespaces A primary button is bright blue, grabbing the user's attention.}}{2021}{figure.677.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {677.2}{\\ignorespaces A secondary button draws less attention than a primary button and is meant for secondary actions.}}{2022}{figure.677.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {677.3}{\\ignorespaces Borderless buttons remove the dark outline from the button.}}{2022}{figure.677.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {677.4}{\\ignorespaces You can also turn buttons into links.}}{2022}{figure.677.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {677.5}{\\ignorespaces Buttons can also display icons.}}{2022}{figure.677.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {677.6}{\\ignorespaces Buttons can be disabled if you don't want the user to interact with them.}}{2023}{figure.677.6}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {677.2}Variations}{2023}{section.677.2}\\protected@file@percent }\n\\newlabel{variations}{{677.2}{2023}{Variations}{section.677.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {677.7}{\\ignorespaces Buttons can display both icons and text.}}{2023}{figure.677.7}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {677.8}{\\ignorespaces Buttons can display monospaced text.}}{2023}{figure.677.8}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {677.9}{\\ignorespaces Block level buttons span the entire width of the container.}}{2024}{figure.677.9}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {677.10}{\\ignorespaces : A plus button is used for add actions in an app.}}{2024}{figure.677.10}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {677.11}{\\ignorespaces : An action button is used to display actions menus.}}{2024}{figure.677.11}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {677.3}Related Topics}{2024}{section.677.3}\\protected@file@percent }\n\\newlabel{related-topics-22}{{677.3}{2024}{Related Topics}{section.677.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {678}Clay Cards}{2025}{chapter.678}\\protected@file@percent }\n\\newlabel{clay-cards}{{678}{2025}{Clay Cards}{chapter.678}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {678.1}Image Cards}{2025}{section.678.1}\\protected@file@percent }\n\\newlabel{image-cards}{{678.1}{2025}{Image Cards}{section.678.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {678.1}{\\ignorespaces Image Cards display images and documents.}}{2026}{figure.678.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {678.2}{\\ignorespaces Image Cards can also display icons instead of images.}}{2027}{figure.678.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {678.2}File Cards}{2027}{section.678.2}\\protected@file@percent }\n\\newlabel{file-cards}{{678.2}{2027}{File Cards}{section.678.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {678.3}{\\ignorespaces Cards can also display nothing.}}{2028}{figure.678.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {678.4}{\\ignorespaces Cards can also contain file types.}}{2029}{figure.678.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {678.3}User Cards}{2029}{section.678.3}\\protected@file@percent }\n\\newlabel{user-cards}{{678.3}{2029}{User Cards}{section.678.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {678.5}{\\ignorespaces You can include labels in Cards.}}{2030}{figure.678.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {678.4}Horizontal Cards}{2030}{section.678.4}\\protected@file@percent }\n\\newlabel{horizontal-cards}{{678.4}{2030}{Horizontal Cards}{section.678.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {678.5}Related Topics}{2030}{section.678.5}\\protected@file@percent }\n\\newlabel{related-topics-23}{{678.5}{2030}{Related Topics}{section.678.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {678.6}{\\ignorespaces Cards can be selectable.}}{2031}{figure.678.6}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {678.7}{\\ignorespaces File Cards display file type icons.}}{2032}{figure.678.7}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {678.8}{\\ignorespaces User Cards can display a user's initials.}}{2033}{figure.678.8}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {678.9}{\\ignorespaces A User Card can also display a profile image.}}{2033}{figure.678.9}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {678.10}{\\ignorespaces : Horizontal Cards are good for displaying folders.}}{2034}{figure.678.10}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {679}Clay Dropdown Menus and Action Menus}{2035}{chapter.679}\\protected@file@percent }\n\\newlabel{clay-dropdown-menus-and-action-menus}{{679}{2035}{Clay Dropdown Menus and Action Menus}{chapter.679}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {679.1}Dropdown Menus}{2035}{section.679.1}\\protected@file@percent }\n\\newlabel{dropdown-menus}{{679.1}{2035}{Dropdown Menus}{section.679.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {679.1}{\\ignorespaces Clay taglibs provide everything you need to add dropdown menus to your app.}}{2036}{figure.679.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {679.2}{\\ignorespaces You can organize dropdown menu items into groups.}}{2037}{figure.679.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {679.2}Actions Menus}{2037}{section.679.2}\\protected@file@percent }\n\\newlabel{actions-menus}{{679.2}{2037}{Actions Menus}{section.679.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {679.3}{\\ignorespaces Inputs can be included in dropdown menus.}}{2038}{figure.679.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {679.4}{\\ignorespaces Icons can be included in dropdown menus.}}{2039}{figure.679.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {679.5}{\\ignorespaces You can also create Actions menus with Clay taglibs.}}{2039}{figure.679.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {679.6}{\\ignorespaces You can provide help text in Actions menus.}}{2040}{figure.679.6}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {679.3}Related Topics}{2041}{section.679.3}\\protected@file@percent }\n\\newlabel{related-topics-24}{{679.3}{2041}{Related Topics}{section.679.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {680}Clay Form Elements}{2043}{chapter.680}\\protected@file@percent }\n\\newlabel{clay-form-elements}{{680}{2043}{Clay Form Elements}{chapter.680}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {680.1}Checkbox}{2043}{section.680.1}\\protected@file@percent }\n\\newlabel{checkbox}{{680.1}{2043}{Checkbox}{section.680.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {680.1}{\\ignorespaces Clay taglibs provide checkboxes.}}{2043}{figure.680.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {680.2}Radio}{2043}{section.680.2}\\protected@file@percent }\n\\newlabel{radio}{{680.2}{2043}{Radio}{section.680.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {680.2}{\\ignorespaces Clay taglibs provide radio buttons.}}{2044}{figure.680.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {680.3}Selector}{2044}{section.680.3}\\protected@file@percent }\n\\newlabel{selector}{{680.3}{2044}{Selector}{section.680.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {680.3}{\\ignorespaces Clay taglibs provide select boxes.}}{2045}{figure.680.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {680.4}{\\ignorespaces You can let users select multiple options from the select menu.}}{2046}{figure.680.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {680.4}Related Topics}{2046}{section.680.4}\\protected@file@percent }\n\\newlabel{related-topics-25}{{680.4}{2046}{Related Topics}{section.680.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {681}Clay Icons}{2047}{chapter.681}\\protected@file@percent }\n\\newlabel{clay-icons}{{681}{2047}{Clay Icons}{chapter.681}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {681.1}{\\ignorespaces You can include icons in your app with the Clay taglib.}}{2047}{figure.681.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {681.1}Related Topics}{2047}{section.681.1}\\protected@file@percent }\n\\newlabel{related-topics-26}{{681.1}{2047}{Related Topics}{section.681.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {681.2}{\\ignorespaces The Clay taglib gives you access to several Liferay DXP icons.}}{2048}{figure.681.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {681.3}{\\ignorespaces You can include language flags in your apps.}}{2048}{figure.681.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {682}Clay Labels and Links}{2049}{chapter.682}\\protected@file@percent }\n\\newlabel{clay-labels-and-links}{{682}{2049}{Clay Labels and Links}{chapter.682}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {682.1}Labels}{2049}{section.682.1}\\protected@file@percent }\n\\newlabel{labels}{{682.1}{2049}{Labels}{section.682.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {682.2}Color-coded Labels}{2049}{section.682.2}\\protected@file@percent }\n\\newlabel{color-coded-labels}{{682.2}{2049}{Color-coded Labels}{section.682.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {682.1}{\\ignorespaces Info labels convey general information.}}{2049}{figure.682.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {682.2}{\\ignorespaces Status labels are the least flashy and best for displaying basic information.}}{2050}{figure.682.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {682.3}{\\ignorespaces Warning labels notify the user of issues, but nothing app breaking.}}{2050}{figure.682.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {682.4}{\\ignorespaces Danger labels convey a sense of urgency that must be addressed.}}{2050}{figure.682.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {682.5}{\\ignorespaces Success labels indicate a successful action.}}{2050}{figure.682.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {682.3}Removable Labels}{2051}{section.682.3}\\protected@file@percent }\n\\newlabel{removable-labels}{{682.3}{2051}{Removable Labels}{section.682.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {682.6}{\\ignorespaces Labels can be removable.}}{2051}{figure.682.6}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {682.4}Labels with Links}{2051}{section.682.4}\\protected@file@percent }\n\\newlabel{labels-with-links}{{682.4}{2051}{Labels with Links}{section.682.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {682.7}{\\ignorespaces Labels can also be links.}}{2051}{figure.682.7}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {682.5}Links}{2051}{section.682.5}\\protected@file@percent }\n\\newlabel{links}{{682.5}{2051}{Links}{section.682.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {682.8}{\\ignorespaces Clay taglibs also provide link elements.}}{2051}{figure.682.8}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {682.6}Related Topics}{2052}{section.682.6}\\protected@file@percent }\n\\newlabel{related-topics-27}{{682.6}{2052}{Related Topics}{section.682.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {683}Clay Management Toolbar}{2053}{chapter.683}\\protected@file@percent }\n\\newlabel{clay-management-toolbar}{{683}{2053}{Clay Management Toolbar}{chapter.683}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {683.1}{\\ignorespaces The Management ToolBar lets the user customize how the app displays content.}}{2053}{figure.683.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {683.1}Using a Display Context to Configure the Management Toolbar}{2053}{section.683.1}\\protected@file@percent }\n\\newlabel{using-a-display-context-to-configure-the-management-toolbar}{{683.1}{2053}{Using a Display Context to Configure the Management Toolbar}{section.683.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {683.2}Checkbox and Actions}{2054}{section.683.2}\\protected@file@percent }\n\\newlabel{checkbox-and-actions}{{683.2}{2054}{Checkbox and Actions}{section.683.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {683.2}{\\ignorespaces Actions are also listed in the Management Toolbar's dropdown menu when an item, multiple items, or the master checkbox is checked.}}{2054}{figure.683.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {683.3}{\\ignorespaces The Management Toolbar keeps track of the results selected and displays the actions to execute on them.}}{2055}{figure.683.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {683.3}Filtering and Sorting Search Results}{2055}{section.683.3}\\protected@file@percent }\n\\newlabel{filtering-and-sorting-search-results}{{683.3}{2055}{Filtering and Sorting Search Results}{section.683.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {683.4}{\\ignorespaces You can also sort and filter search container results.}}{2056}{figure.683.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {683.5}{\\ignorespaces You can also sort and filter search container results.}}{2056}{figure.683.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {683.4}Search Form}{2057}{section.683.4}\\protected@file@percent }\n\\newlabel{search-form}{{683.4}{2057}{Search Form}{section.683.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {683.6}{\\ignorespaces The search form comprises most of the Management Toolbar, letting users search through the search container results.}}{2057}{figure.683.6}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {683.5}Info Panel}{2057}{section.683.5}\\protected@file@percent }\n\\newlabel{info-panel}{{683.5}{2057}{Info Panel}{section.683.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {683.7}{\\ignorespaces The info panel keeps your UI clutter-free.}}{2058}{figure.683.7}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {683.6}View Types}{2058}{section.683.6}\\protected@file@percent }\n\\newlabel{view-types}{{683.6}{2058}{View Types}{section.683.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {683.8}{\\ignorespaces The Management Toolbar's icon display view gives a quick summary of the content's description and status.}}{2059}{figure.683.8}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {683.9}{\\ignorespaces The Management Toolbar's List view type gives the content's full description.}}{2059}{figure.683.9}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {683.10}{\\ignorespaces : The Management Toolbar's Table view type list the content's information in individual columns.}}{2059}{figure.683.10}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {683.11}{\\ignorespaces : The Management Toolbar offers three view type options.}}{2060}{figure.683.11}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {683.7}Creation Menu}{2060}{section.683.7}\\protected@file@percent }\n\\newlabel{creation-menu}{{683.7}{2060}{Creation Menu}{section.683.7}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {683.12}{\\ignorespaces : The Management Toolbar lets you optionally add a Creation Menu for creating new entities.}}{2061}{figure.683.12}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {683.8}Related Topics}{2061}{section.683.8}\\protected@file@percent }\n\\newlabel{related-topics-28}{{683.8}{2061}{Related Topics}{section.683.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {684}Clay Navigation Bars}{2063}{chapter.684}\\protected@file@percent }\n\\newlabel{clay-navigation-bars}{{684}{2063}{Clay Navigation Bars}{chapter.684}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {684.1}{\\ignorespaces You can include navigation bars in your apps.}}{2063}{figure.684.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {684.2}{\\ignorespaces Navigation bars can be inverted if you prefer.}}{2063}{figure.684.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {684.1}Related Topics}{2064}{section.684.1}\\protected@file@percent }\n\\newlabel{related-topics-29}{{684.1}{2064}{Related Topics}{section.684.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {685}Clay Progress Bars}{2065}{chapter.685}\\protected@file@percent }\n\\newlabel{clay-progress-bars}{{685}{2065}{Clay Progress Bars}{chapter.685}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {685.1}{\\ignorespaces You can include progress bars in your apps.}}{2065}{figure.685.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {685.2}{\\ignorespaces warning progress bars indicate that the progress has not completed due to an error.}}{2065}{figure.685.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {685.3}{\\ignorespaces The complete progress bar indicates the progress is complete.}}{2066}{figure.685.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {685.1}Related Topics}{2066}{section.685.1}\\protected@file@percent }\n\\newlabel{related-topics-30}{{685.1}{2066}{Related Topics}{section.685.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {686}Clay Stickers}{2067}{chapter.686}\\protected@file@percent }\n\\newlabel{clay-stickers}{{686}{2067}{Clay Stickers}{chapter.686}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {686.1}{\\ignorespaces You can include stickers in your apps.}}{2067}{figure.686.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {686.2}{\\ignorespaces Stickers can include icons.}}{2067}{figure.686.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {686.3}{\\ignorespaces You can also have circle stickers.}}{2068}{figure.686.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {686.4}{\\ignorespaces You can specify the position of the sticker within a container.}}{2068}{figure.686.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {686.1}Related Topics}{2068}{section.686.1}\\protected@file@percent }\n\\newlabel{related-topics-31}{{686.1}{2068}{Related Topics}{section.686.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {687}Using the Chart Taglib in Your Portlets}{2069}{chapter.687}\\protected@file@percent }\n\\newlabel{using-the-chart-taglib-in-your-portlets}{{687}{2069}{Using the Chart Taglib in Your Portlets}{chapter.687}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {687.1}{\\ignorespaces You can create many different types of charts with the chart taglibs.}}{2070}{figure.687.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {688}Bar Charts}{2071}{chapter.688}\\protected@file@percent }\n\\newlabel{bar-charts}{{688}{2071}{Bar Charts}{chapter.688}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {688.1}Related Topics}{2071}{section.688.1}\\protected@file@percent }\n\\newlabel{related-topics-32}{{688.1}{2071}{Related Topics}{section.688.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {688.1}{\\ignorespaces A bar chart models the data in bars.}}{2072}{figure.688.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {689}Line Charts}{2073}{chapter.689}\\protected@file@percent }\n\\newlabel{line-charts}{{689}{2073}{Line Charts}{chapter.689}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {689.1}Related Topics}{2073}{section.689.1}\\protected@file@percent }\n\\newlabel{related-topics-33}{{689.1}{2073}{Related Topics}{section.689.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {689.1}{\\ignorespaces A Line chart displays the data linearly.}}{2074}{figure.689.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {690}Scatter Charts}{2075}{chapter.690}\\protected@file@percent }\n\\newlabel{scatter-charts}{{690}{2075}{Scatter Charts}{chapter.690}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {690.1}Related Topics}{2075}{section.690.1}\\protected@file@percent }\n\\newlabel{related-topics-34}{{690.1}{2075}{Related Topics}{section.690.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {690.1}{\\ignorespaces A scatter chart models the data as individual points.}}{2076}{figure.690.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {691}Spline Charts}{2077}{chapter.691}\\protected@file@percent }\n\\newlabel{spline-charts}{{691}{2077}{Spline Charts}{chapter.691}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {691.1}{\\ignorespaces A spline chart connects points of data with a smooth curve.}}{2078}{figure.691.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {691.1}Related Topics}{2078}{section.691.1}\\protected@file@percent }\n\\newlabel{related-topics-35}{{691.1}{2078}{Related Topics}{section.691.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {691.2}{\\ignorespaces An area spline chart highlights the area under the spline curve.}}{2079}{figure.691.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {692}Step Charts}{2081}{chapter.692}\\protected@file@percent }\n\\newlabel{step-charts}{{692}{2081}{Step Charts}{chapter.692}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {692.1}{\\ignorespaces A step chart steps between the points of data, resembling steps.}}{2082}{figure.692.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {692.1}Related Topics}{2082}{section.692.1}\\protected@file@percent }\n\\newlabel{related-topics-36}{{692.1}{2082}{Related Topics}{section.692.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {692.2}{\\ignorespaces An area step chart highlights the area covered by a step graph.}}{2083}{figure.692.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {693}Combination Charts}{2085}{chapter.693}\\protected@file@percent }\n\\newlabel{combination-charts}{{693}{2085}{Combination Charts}{chapter.693}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {693.1}{\\ignorespaces A combination chart displays a variety of data set types.}}{2086}{figure.693.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {693.1}Related Topics}{2086}{section.693.1}\\protected@file@percent }\n\\newlabel{related-topics-37}{{693.1}{2086}{Related Topics}{section.693.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {694}Donut Charts}{2087}{chapter.694}\\protected@file@percent }\n\\newlabel{donut-charts}{{694}{2087}{Donut Charts}{chapter.694}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {694.1}Related Topics}{2087}{section.694.1}\\protected@file@percent }\n\\newlabel{related-topics-38}{{694.1}{2087}{Related Topics}{section.694.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {694.1}{\\ignorespaces A donut chart is similar to a pie chart, but it has a hole in the center.}}{2088}{figure.694.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {695}Gauge Charts}{2089}{chapter.695}\\protected@file@percent }\n\\newlabel{gauge-charts}{{695}{2089}{Gauge Charts}{chapter.695}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {695.1}Related Topics}{2089}{section.695.1}\\protected@file@percent }\n\\newlabel{related-topics-39}{{695.1}{2089}{Related Topics}{section.695.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {695.1}{\\ignorespaces A gauge chart shows where percentage-based data falls over a given range.}}{2090}{figure.695.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {696}Pie Charts}{2091}{chapter.696}\\protected@file@percent }\n\\newlabel{pie-charts}{{696}{2091}{Pie Charts}{chapter.696}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {696.1}Related Topics}{2091}{section.696.1}\\protected@file@percent }\n\\newlabel{related-topics-40}{{696.1}{2091}{Related Topics}{section.696.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {696.1}{\\ignorespaces A pie chart models percentage-based data as individual slices of pie.}}{2092}{figure.696.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {697}Geomap Charts}{2093}{chapter.697}\\protected@file@percent }\n\\newlabel{geomap-charts}{{697}{2093}{Geomap Charts}{chapter.697}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {697.1}{\\ignorespaces A Geomap chart displays a heatmap representing the data.}}{2094}{figure.697.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {697.2}{\\ignorespaces Geomap charts can be customized to fit the look and feel you desire.}}{2095}{figure.697.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {697.1}Related Topics}{2096}{section.697.1}\\protected@file@percent }\n\\newlabel{related-topics-41}{{697.1}{2096}{Related Topics}{section.697.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {698}Predictive Charts}{2097}{chapter.698}\\protected@file@percent }\n\\newlabel{predictive-charts}{{698}{2097}{Predictive Charts}{chapter.698}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {698.1}{\\ignorespaces Predicted/forecasted data is surrounded by a highlighted area of possible values.}}{2097}{figure.698.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {698.2}{\\ignorespaces A predictive chart lets you visualize estimated future data alongside existing data.}}{2099}{figure.698.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {698.1}Related Topics}{2099}{section.698.1}\\protected@file@percent }\n\\newlabel{related-topics-42}{{698.1}{2099}{Related Topics}{section.698.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {699}Refreshing Charts to Reflect Real Time Data}{2101}{chapter.699}\\protected@file@percent }\n\\newlabel{refreshing-charts-to-reflect-real-time-data}{{699}{2101}{Refreshing Charts to Reflect Real Time Data}{chapter.699}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {699.1}Related Topics}{2101}{section.699.1}\\protected@file@percent }\n\\newlabel{related-topics-43}{{699.1}{2101}{Related Topics}{section.699.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {699.1}{\\ignorespaces The polling interval property lets you refresh charts at a given interval to reflect real time data.}}{2102}{figure.699.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {700}Using AUI Taglibs}{2103}{chapter.700}\\protected@file@percent }\n\\newlabel{using-aui-taglibs}{{700}{2103}{Using AUI Taglibs}{chapter.700}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {701}Building Forms with AUI Tags}{2105}{chapter.701}\\protected@file@percent }\n\\newlabel{building-forms-with-aui-tags}{{701}{2105}{Building Forms with AUI Tags}{chapter.701}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {701.1}{\\ignorespaces The AUI tags provide everything you need to build forms for your applications.}}{2106}{figure.701.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {701.2}{\\ignorespaces The AUI tags also provide validation for form fields.}}{2107}{figure.701.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {701.1}Related Topics}{2108}{section.701.1}\\protected@file@percent }\n\\newlabel{related-topics-44}{{701.1}{2108}{Related Topics}{section.701.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {702}liferay-npm-bundler}{2109}{chapter.702}\\protected@file@percent }\n\\newlabel{liferay-npm-bundler}{{702}{2109}{liferay-npm-bundler}{chapter.702}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {702.1}How the Liferay npm Bundler Works Internally}{2109}{section.702.1}\\protected@file@percent }\n\\newlabel{how-the-liferay-npm-bundler-works-internally}{{702.1}{2109}{How the Liferay npm Bundler Works Internally}{section.702.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {703}Understanding the \\texttt  {.npmbundlerrc}'s Structure}{2111}{chapter.703}\\protected@file@percent }\n\\newlabel{understanding-the-.npmbundlerrcs-structure}{{703}{2111}{\\texorpdfstring {Understanding the \\texttt {.npmbundlerrc}'s Structure}{Understanding the .npmbundlerrc's Structure}}{chapter.703}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {703.1}The Structure}{2111}{section.703.1}\\protected@file@percent }\n\\newlabel{the-structure}{{703.1}{2111}{The Structure}{section.703.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {703.2}Standard Configuration Options}{2113}{section.703.2}\\protected@file@percent }\n\\newlabel{standard-configuration-options}{{703.2}{2113}{Standard Configuration Options}{section.703.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {703.3}Package Processing Options}{2114}{section.703.3}\\protected@file@percent }\n\\newlabel{package-processing-options}{{703.3}{2114}{Package Processing Options}{section.703.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {703.4}OSGi Bundle Creation Options}{2115}{section.703.4}\\protected@file@percent }\n\\newlabel{osgi-bundle-creation-options}{{703.4}{2115}{OSGi Bundle Creation Options}{section.703.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {704}How the Default Preset Configures the liferay-npm-bundler}{2119}{chapter.704}\\protected@file@percent }\n\\newlabel{how-the-default-preset-configures-the-liferay-npm-bundler}{{704}{2119}{How the Default Preset Configures the liferay-npm-bundler}{chapter.704}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {705}The Structure of OSGi Bundles Containing npm Packages}{2121}{chapter.705}\\protected@file@percent }\n\\newlabel{the-structure-of-osgi-bundles-containing-npm-packages}{{705}{2121}{The Structure of OSGi Bundles Containing npm Packages}{chapter.705}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {705.1}Inline JavaScript packages}{2122}{section.705.1}\\protected@file@percent }\n\\newlabel{inline-javascript-packages}{{705.1}{2122}{Inline JavaScript packages}{section.705.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {706}How the Liferay npm Bundler Publishes npm Packages}{2123}{chapter.706}\\protected@file@percent }\n\\newlabel{how-the-liferay-npm-bundler-publishes-npm-packages}{{706}{2123}{How the Liferay npm Bundler Publishes npm Packages}{chapter.706}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {706.1}Package De-duplication}{2124}{section.706.1}\\protected@file@percent }\n\\newlabel{package-de-duplication}{{706.1}{2124}{Package De-duplication}{section.706.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {706.2}Isolated Package Dependencies}{2125}{section.706.2}\\protected@file@percent }\n\\newlabel{isolated-package-dependencies}{{706.2}{2125}{Isolated Package Dependencies}{section.706.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {706.3}De-duplication through Importing}{2127}{section.706.3}\\protected@file@percent }\n\\newlabel{de-duplication-through-importing}{{706.3}{2127}{De-duplication through Importing}{section.706.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {706.4}Strategies When Importing Packages}{2129}{section.706.4}\\protected@file@percent }\n\\newlabel{strategies-when-importing-packages}{{706.4}{2129}{Strategies When Importing Packages}{section.706.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {707}Understanding How liferay-npm-bundler Formats JavaScript Modules for AMD}{2131}{chapter.707}\\protected@file@percent }\n\\newlabel{understanding-how-liferay-npm-bundler-formats-javascript-modules-for-amd}{{707}{2131}{Understanding How liferay-npm-bundler Formats JavaScript Modules for AMD}{chapter.707}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {708}Understanding How Liferay AMD Loader Configuration is Exported}{2135}{chapter.708}\\protected@file@percent }\n\\newlabel{understanding-how-liferay-amd-loader-configuration-is-exported}{{708}{2135}{Understanding How Liferay AMD Loader Configuration is Exported}{chapter.708}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {709}What Changed Between Liferay npm Bundler 1.x and 2.x}{2139}{chapter.709}\\protected@file@percent }\n\\newlabel{what-changed-between-liferay-npm-bundler-1.x-and-2.x}{{709}{2139}{What Changed Between Liferay npm Bundler 1.x and 2.x}{chapter.709}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {709.1}Automatically Formatting Modules for AMD}{2139}{section.709.1}\\protected@file@percent }\n\\newlabel{automatically-formatting-modules-for-amd}{{709.1}{2139}{Automatically Formatting Modules for AMD}{section.709.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {709.2}Isolating Project Dependencies}{2139}{section.709.2}\\protected@file@percent }\n\\newlabel{isolating-project-dependencies}{{709.2}{2139}{Isolating Project Dependencies}{section.709.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {709.3}Improved Peer Dependency Support}{2139}{section.709.3}\\protected@file@percent }\n\\newlabel{improved-peer-dependency-support}{{709.3}{2139}{Improved Peer Dependency Support}{section.709.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {709.4}Manually De-duplicating Through Importing}{2140}{section.709.4}\\protected@file@percent }\n\\newlabel{manually-de-duplicating-through-importing}{{709.4}{2140}{Manually De-duplicating Through Importing}{section.709.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {710}Understanding liferay-npm-bundler's Loaders}{2141}{chapter.710}\\protected@file@percent }\n\\newlabel{understanding-liferay-npm-bundlers-loaders}{{710}{2141}{Understanding liferay-npm-bundler's Loaders}{chapter.710}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {711}Default liferay-npm-bundler Loaders}{2143}{chapter.711}\\protected@file@percent }\n\\newlabel{default-liferay-npm-bundler-loaders}{{711}{2143}{Default liferay-npm-bundler Loaders}{chapter.711}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {712}Liferay JavaScript APIs}{2145}{chapter.712}\\protected@file@percent }\n\\newlabel{liferay-javascript-apis}{{712}{2145}{Liferay JavaScript APIs}{chapter.712}{}}\n\\gdef \\LT@xxxv {\\LT@entry \n    {1}{154.56912pt}\\LT@entry \n    {1}{160.56912pt}\\LT@entry \n    {1}{154.56912pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {713}Accessing ThemeDisplay Information}{2147}{chapter.713}\\protected@file@percent }\n\\newlabel{accessing-themedisplay-information}{{713}{2147}{Accessing ThemeDisplay Information}{chapter.713}{}}\n\\gdef \\LT@xxxvi {\\LT@entry \n    {1}{154.56912pt}\\LT@entry \n    {1}{160.56912pt}\\LT@entry \n    {1}{154.56912pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {714}Working with URLs in JavaScript}{2151}{chapter.714}\\protected@file@percent }\n\\newlabel{working-with-urls-in-javascript}{{714}{2151}{Working with URLs in JavaScript}{chapter.714}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {714.1}Portlet URL Methods}{2151}{section.714.1}\\protected@file@percent }\n\\newlabel{portlet-url-methods}{{714.1}{2151}{Portlet URL Methods}{section.714.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {714.2}Liferay Util PortletURL}{2152}{section.714.2}\\protected@file@percent }\n\\newlabel{liferay-util-portleturl}{{714.2}{2152}{Liferay Util PortletURL}{section.714.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {714.3}Liferay AuthToken}{2152}{section.714.3}\\protected@file@percent }\n\\newlabel{liferay-authtoken}{{714.3}{2152}{Liferay AuthToken}{section.714.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {714.4}Liferay CurrentURL}{2152}{section.714.4}\\protected@file@percent }\n\\newlabel{liferay-currenturl}{{714.4}{2152}{Liferay CurrentURL}{section.714.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {714.5}Liferay CurrentURLEncoded}{2153}{section.714.5}\\protected@file@percent }\n\\newlabel{liferay-currenturlencoded}{{714.5}{2153}{Liferay CurrentURLEncoded}{section.714.5}{}}\n\\gdef \\LT@xxxvii {\\LT@entry \n    {1}{154.56912pt}\\LT@entry \n    {1}{160.56912pt}\\LT@entry \n    {1}{154.56912pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {715}Liferay DXP JavaScript Utilities}{2155}{chapter.715}\\protected@file@percent }\n\\newlabel{liferay-dxp-javascript-utilities}{{715}{2155}{Liferay DXP JavaScript Utilities}{chapter.715}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {715.1}Retrieve Browser Information}{2155}{section.715.1}\\protected@file@percent }\n\\newlabel{retrieve-browser-information}{{715.1}{2155}{Retrieve Browser Information}{section.715.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {715.2}Format XML}{2156}{section.715.2}\\protected@file@percent }\n\\newlabel{format-xml}{{715.2}{2156}{Format XML}{section.715.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {715.3}Format Storage Size}{2157}{section.715.3}\\protected@file@percent }\n\\newlabel{format-storage-size}{{715.3}{2157}{Format Storage Size}{section.715.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {715.4}Store and Retrieve Session Form data}{2157}{section.715.4}\\protected@file@percent }\n\\newlabel{store-and-retrieve-session-form-data}{{715.4}{2157}{Store and Retrieve Session Form data}{section.715.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {716}Invoking Liferay Services}{2159}{chapter.716}\\protected@file@percent }\n\\newlabel{invoking-liferay-services}{{716}{2159}{Invoking Liferay Services}{chapter.716}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {716.1}Invoking Web Services via JavaScript}{2159}{section.716.1}\\protected@file@percent }\n\\newlabel{invoking-web-services-via-javascript}{{716.1}{2159}{Invoking Web Services via JavaScript}{section.716.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {716.2}Batching Requests}{2160}{section.716.2}\\protected@file@percent }\n\\newlabel{batching-requests}{{716.2}{2160}{Batching Requests}{section.716.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {716.3}Nesting Requests}{2161}{section.716.3}\\protected@file@percent }\n\\newlabel{nesting-requests}{{716.3}{2161}{Nesting Requests}{section.716.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {716.4}Filtering Results}{2162}{section.716.4}\\protected@file@percent }\n\\newlabel{filtering-results}{{716.4}{2162}{Filtering Results}{section.716.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {716.5}Inner Parameters}{2163}{section.716.5}\\protected@file@percent }\n\\newlabel{inner-parameters}{{716.5}{2163}{Inner Parameters}{section.716.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {717}Handling AJAX Requests with \\texttt  {Liferay.Util.fetch}}{2165}{chapter.717}\\protected@file@percent }\n\\newlabel{handling-ajax-requests-with-liferay.util.fetch}{{717}{2165}{\\texorpdfstring {Handling AJAX Requests with \\texttt {Liferay.Util.fetch}}{Handling AJAX Requests with Liferay.Util.fetch}}{chapter.717}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {718}Working with Addresses}{2167}{chapter.718}\\protected@file@percent }\n\\newlabel{working-with-addresses}{{718}{2167}{Working with Addresses}{chapter.718}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {719}FreeMarker Taglib Macros}{2169}{chapter.719}\\protected@file@percent }\n\\newlabel{freemarker-taglib-macros}{{719}{2169}{FreeMarker Taglib Macros}{chapter.719}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {720}Setting up Your npm Environment}{2173}{chapter.720}\\protected@file@percent }\n\\newlabel{setting-up-your-npm-environment}{{720}{2173}{Setting up Your npm Environment}{chapter.720}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {721}Sitemap Page Configuration Options}{2175}{chapter.721}\\protected@file@percent }\n\\newlabel{sitemap-page-configuration-options}{{721}{2175}{Sitemap Page Configuration Options}{chapter.721}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {722}CKEditor Plugin Reference Guide}{2177}{chapter.722}\\protected@file@percent }\n\\newlabel{ckeditor-plugin-reference-guide}{{722}{2177}{CKEditor Plugin Reference Guide}{chapter.722}{}}\n\\gdef \\LT@xxxviii {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@xxxix {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {723}Fully Qualified Portlet IDs}{2181}{chapter.723}\\protected@file@percent }\n\\newlabel{fully-qualified-portlet-ids}{{723}{2181}{Fully Qualified Portlet IDs}{chapter.723}{}}\n\\gdef \\LT@xl {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@xli {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@xlii {\\LT@entry \n    {3}{64.40727pt}\\LT@entry \n    {3}{271.06067pt}}\n\\gdef \\LT@xliii {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@xliv {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@xlv {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@xlvi {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@xlvii {\\LT@entry \n    {1}{154.56912pt}\\LT@entry \n    {1}{160.56912pt}\\LT@entry \n    {1}{154.56912pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {724}Available SPA Lifecycle Events}{2185}{chapter.724}\\protected@file@percent }\n\\newlabel{available-spa-lifecycle-events}{{724}{2185}{Available SPA Lifecycle Events}{chapter.724}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {724.1}{\\ignorespaces You can leverage SPA lifecycle events in your apps.}}{2186}{figure.724.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {725}Theme Anatomy Reference Guide}{2187}{chapter.725}\\protected@file@percent }\n\\newlabel{theme-anatomy-reference-guide}{{725}{2187}{Theme Anatomy Reference Guide}{chapter.725}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.1}Theme Files}{2188}{section.725.1}\\protected@file@percent }\n\\newlabel{theme-files}{{725.1}{2188}{Theme Files}{section.725.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.2}\\_clay\\_custom.scss}{2188}{section.725.2}\\protected@file@percent }\n\\newlabel{clay_custom.scss}{{725.2}{2188}{\\_clay\\_custom.scss}{section.725.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.3}\\_clay\\_variables.scss}{2188}{section.725.3}\\protected@file@percent }\n\\newlabel{clay_variables.scss}{{725.3}{2188}{\\_clay\\_variables.scss}{section.725.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.4}\\_custom.scss}{2188}{section.725.4}\\protected@file@percent }\n\\newlabel{custom.scss}{{725.4}{2188}{\\_custom.scss}{section.725.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.5}\\_liferay\\_variables\\_custom.scss}{2188}{section.725.5}\\protected@file@percent }\n\\newlabel{liferay_variables_custom.scss}{{725.5}{2188}{\\_liferay\\_variables\\_custom.scss}{section.725.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.6}init\\_custom.ftl}{2188}{section.725.6}\\protected@file@percent }\n\\newlabel{init_custom.ftl}{{725.6}{2188}{init\\_custom.ftl}{section.725.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.7}navigation.ftl}{2189}{section.725.7}\\protected@file@percent }\n\\newlabel{navigation.ftl}{{725.7}{2189}{navigation.ftl}{section.725.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.8}portal\\_normal.ftl}{2189}{section.725.8}\\protected@file@percent }\n\\newlabel{portal_normal.ftl}{{725.8}{2189}{portal\\_normal.ftl}{section.725.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.9}portal\\_pop\\_up.ftl}{2189}{section.725.9}\\protected@file@percent }\n\\newlabel{portal_pop_up.ftl}{{725.9}{2189}{portal\\_pop\\_up.ftl}{section.725.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.10}portlet.ftl}{2189}{section.725.10}\\protected@file@percent }\n\\newlabel{portlet.ftl}{{725.10}{2189}{portlet.ftl}{section.725.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.11}liferay-theme.json}{2189}{section.725.11}\\protected@file@percent }\n\\newlabel{liferay-theme.json}{{725.11}{2189}{liferay-theme.json}{section.725.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.12}package.json}{2189}{section.725.12}\\protected@file@percent }\n\\newlabel{package.json}{{725.12}{2189}{package.json}{section.725.12}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.13}main.js}{2189}{section.725.13}\\protected@file@percent }\n\\newlabel{main.js}{{725.13}{2189}{main.js}{section.725.13}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.14}liferay-look-and-feel.xml}{2189}{section.725.14}\\protected@file@percent }\n\\newlabel{liferay-look-and-feel.xml}{{725.14}{2189}{liferay-look-and-feel.xml}{section.725.14}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {725.15}liferay-plugin-package.properties}{2190}{section.725.15}\\protected@file@percent }\n\\newlabel{liferay-plugin-package.properties}{{725.15}{2190}{liferay-plugin-package.properties}{section.725.15}{}}\n\\gdef \\LT@xlviii {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {726}Freemarker Variable Reference Guide}{2191}{chapter.726}\\protected@file@percent }\n\\newlabel{freemarker-variable-reference-guide}{{726}{2191}{Freemarker Variable Reference Guide}{chapter.726}{}}\n\\gdef \\LT@xlix {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@l {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@li {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@lii {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@liii {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@liv {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\gdef \\LT@lv {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {727}Gradle Plugins}{2197}{chapter.727}\\protected@file@percent }\n\\newlabel{gradle-plugins}{{727}{2197}{Gradle Plugins}{chapter.727}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {728}App Javadoc Builder Gradle Plugin}{2199}{chapter.728}\\protected@file@percent }\n\\newlabel{app-javadoc-builder-gradle-plugin}{{728}{2199}{App Javadoc Builder Gradle Plugin}{chapter.728}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {728.1}Usage}{2199}{section.728.1}\\protected@file@percent }\n\\newlabel{usage}{{728.1}{2199}{Usage}{section.728.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {728.2}Project Extension}{2199}{section.728.2}\\protected@file@percent }\n\\newlabel{project-extension}{{728.2}{2199}{Project Extension}{section.728.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {728.3}Tasks}{2200}{section.728.3}\\protected@file@percent }\n\\newlabel{tasks}{{728.3}{2200}{Tasks}{section.728.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {729}Baseline Gradle Plugin}{2201}{chapter.729}\\protected@file@percent }\n\\newlabel{baseline-gradle-plugin}{{729}{2201}{Baseline Gradle Plugin}{chapter.729}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {729.1}Usage}{2201}{section.729.1}\\protected@file@percent }\n\\newlabel{usage-1}{{729.1}{2201}{Usage}{section.729.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {729.2}Project Extension}{2202}{section.729.2}\\protected@file@percent }\n\\newlabel{project-extension-1}{{729.2}{2202}{Project Extension}{section.729.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {729.3}Tasks}{2202}{section.729.3}\\protected@file@percent }\n\\newlabel{tasks-1}{{729.3}{2202}{Tasks}{section.729.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {729.4}BaselineTask}{2202}{section.729.4}\\protected@file@percent }\n\\newlabel{baselinetask}{{729.4}{2202}{BaselineTask}{section.729.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2202}{section.729.4}\\protected@file@percent }\n\\newlabel{task-properties}{{729.4}{2202}{Task Properties}{section.729.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {729.5}Helper Tasks}{2203}{section.729.5}\\protected@file@percent }\n\\newlabel{helper-tasks}{{729.5}{2203}{Helper Tasks}{section.729.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {729.6}Additional Configuration}{2203}{section.729.6}\\protected@file@percent }\n\\newlabel{additional-configuration}{{729.6}{2203}{Additional Configuration}{section.729.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {729.7}Baseline Dependency}{2204}{section.729.7}\\protected@file@percent }\n\\newlabel{baseline-dependency}{{729.7}{2204}{Baseline Dependency}{section.729.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {729.8}System Properties}{2204}{section.729.8}\\protected@file@percent }\n\\newlabel{system-properties}{{729.8}{2204}{System Properties}{section.729.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {730}Change Log Builder Gradle Plugin}{2205}{chapter.730}\\protected@file@percent }\n\\newlabel{change-log-builder-gradle-plugin}{{730}{2205}{Change Log Builder Gradle Plugin}{chapter.730}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {730.1}Usage}{2205}{section.730.1}\\protected@file@percent }\n\\newlabel{usage-2}{{730.1}{2205}{Usage}{section.730.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {730.2}Tasks}{2206}{section.730.2}\\protected@file@percent }\n\\newlabel{tasks-2}{{730.2}{2206}{Tasks}{section.730.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {730.3}BuildChangeLogTask}{2206}{section.730.3}\\protected@file@percent }\n\\newlabel{buildchangelogtask}{{730.3}{2206}{BuildChangeLogTask}{section.730.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2206}{section.730.3}\\protected@file@percent }\n\\newlabel{task-properties-1}{{730.3}{2206}{Task Properties}{section.730.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Methods}{2206}{section.730.3}\\protected@file@percent }\n\\newlabel{task-methods}{{730.3}{2206}{Task Methods}{section.730.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {731}CSS Builder Gradle Plugin}{2207}{chapter.731}\\protected@file@percent }\n\\newlabel{css-builder-gradle-plugin}{{731}{2207}{CSS Builder Gradle Plugin}{chapter.731}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {731.1}Usage}{2207}{section.731.1}\\protected@file@percent }\n\\newlabel{usage-3}{{731.1}{2207}{Usage}{section.731.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {731.2}Tasks}{2207}{section.731.2}\\protected@file@percent }\n\\newlabel{tasks-3}{{731.2}{2207}{Tasks}{section.731.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {731.3}BuildCSSTask}{2208}{section.731.3}\\protected@file@percent }\n\\newlabel{buildcsstask}{{731.3}{2208}{BuildCSSTask}{section.731.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2208}{section.731.3}\\protected@file@percent }\n\\newlabel{task-properties-2}{{731.3}{2208}{Task Properties}{section.731.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Methods}{2209}{section.731.3}\\protected@file@percent }\n\\newlabel{task-methods-1}{{731.3}{2209}{Task Methods}{section.731.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {731.4}Additional Configuration}{2209}{section.731.4}\\protected@file@percent }\n\\newlabel{additional-configuration-1}{{731.4}{2209}{Additional Configuration}{section.731.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {731.5}Liferay CSS Builder Dependency}{2209}{section.731.5}\\protected@file@percent }\n\\newlabel{liferay-css-builder-dependency}{{731.5}{2209}{Liferay CSS Builder Dependency}{section.731.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {731.6}Liferay Frontend Common CSS Dependency}{2209}{section.731.6}\\protected@file@percent }\n\\newlabel{liferay-frontend-common-css-dependency}{{731.6}{2209}{Liferay Frontend Common CSS Dependency}{section.731.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {732}DB Support Gradle Plugin}{2211}{chapter.732}\\protected@file@percent }\n\\newlabel{db-support-gradle-plugin}{{732}{2211}{DB Support Gradle Plugin}{chapter.732}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {732.1}Usage}{2211}{section.732.1}\\protected@file@percent }\n\\newlabel{usage-4}{{732.1}{2211}{Usage}{section.732.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {732.2}Tasks}{2212}{section.732.2}\\protected@file@percent }\n\\newlabel{tasks-4}{{732.2}{2212}{Tasks}{section.732.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {732.3}CleanServiceBuilderTask}{2212}{section.732.3}\\protected@file@percent }\n\\newlabel{cleanservicebuildertask}{{732.3}{2212}{CleanServiceBuilderTask}{section.732.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2212}{section.732.3}\\protected@file@percent }\n\\newlabel{task-properties-3}{{732.3}{2212}{Task Properties}{section.732.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {732.4}Additional Configuration}{2212}{section.732.4}\\protected@file@percent }\n\\newlabel{additional-configuration-2}{{732.4}{2212}{Additional Configuration}{section.732.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {732.5}JDBC Drivers Dependency}{2213}{section.732.5}\\protected@file@percent }\n\\newlabel{jdbc-drivers-dependency}{{732.5}{2213}{JDBC Drivers Dependency}{section.732.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {732.6}Liferay DB Support Dependency}{2213}{section.732.6}\\protected@file@percent }\n\\newlabel{liferay-db-support-dependency}{{732.6}{2213}{Liferay DB Support Dependency}{section.732.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {733}Dependency Checker Gradle Plugin}{2215}{chapter.733}\\protected@file@percent }\n\\newlabel{dependency-checker-gradle-plugin}{{733}{2215}{Dependency Checker Gradle Plugin}{chapter.733}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {733.1}Usage}{2215}{section.733.1}\\protected@file@percent }\n\\newlabel{usage-5}{{733.1}{2215}{Usage}{section.733.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {733.2}Project Extension}{2215}{section.733.2}\\protected@file@percent }\n\\newlabel{project-extension-2}{{733.2}{2215}{Project Extension}{section.733.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {733.3}Additional Configuration}{2216}{section.733.3}\\protected@file@percent }\n\\newlabel{additional-configuration-3}{{733.3}{2216}{Additional Configuration}{section.733.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {733.4}Project Properties}{2216}{section.733.4}\\protected@file@percent }\n\\newlabel{project-properties}{{733.4}{2216}{Project Properties}{section.733.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {734}Deployment Helper Gradle Plugin}{2217}{chapter.734}\\protected@file@percent }\n\\newlabel{deployment-helper-gradle-plugin}{{734}{2217}{Deployment Helper Gradle Plugin}{chapter.734}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {734.1}Usage}{2217}{section.734.1}\\protected@file@percent }\n\\newlabel{usage-6}{{734.1}{2217}{Usage}{section.734.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {734.2}Tasks}{2217}{section.734.2}\\protected@file@percent }\n\\newlabel{tasks-5}{{734.2}{2217}{Tasks}{section.734.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {734.3}BuildDeploymentHelperTask}{2218}{section.734.3}\\protected@file@percent }\n\\newlabel{builddeploymenthelpertask}{{734.3}{2218}{BuildDeploymentHelperTask}{section.734.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2218}{section.734.3}\\protected@file@percent }\n\\newlabel{task-properties-4}{{734.3}{2218}{Task Properties}{section.734.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Methods}{2218}{section.734.3}\\protected@file@percent }\n\\newlabel{task-methods-2}{{734.3}{2218}{Task Methods}{section.734.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {734.4}Additional Configuration}{2218}{section.734.4}\\protected@file@percent }\n\\newlabel{additional-configuration-4}{{734.4}{2218}{Additional Configuration}{section.734.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {734.5}Liferay Deployment Helper Dependency}{2218}{section.734.5}\\protected@file@percent }\n\\newlabel{liferay-deployment-helper-dependency}{{734.5}{2218}{Liferay Deployment Helper Dependency}{section.734.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {735}Go Gradle Plugin}{2219}{chapter.735}\\protected@file@percent }\n\\newlabel{go-gradle-plugin}{{735}{2219}{Go Gradle Plugin}{chapter.735}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {735.1}Usage}{2219}{section.735.1}\\protected@file@percent }\n\\newlabel{usage-7}{{735.1}{2219}{Usage}{section.735.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {735.2}Project Extension}{2219}{section.735.2}\\protected@file@percent }\n\\newlabel{project-extension-3}{{735.2}{2219}{Project Extension}{section.735.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {735.3}Tasks}{2219}{section.735.3}\\protected@file@percent }\n\\newlabel{tasks-6}{{735.3}{2219}{Tasks}{section.735.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {735.4}DownloadGoTask}{2220}{section.735.4}\\protected@file@percent }\n\\newlabel{downloadgotask}{{735.4}{2220}{DownloadGoTask}{section.735.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2220}{section.735.4}\\protected@file@percent }\n\\newlabel{task-properties-5}{{735.4}{2220}{Task Properties}{section.735.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {735.5}ExecuteGoTask}{2220}{section.735.5}\\protected@file@percent }\n\\newlabel{executegotask}{{735.5}{2220}{ExecuteGoTask}{section.735.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2220}{section.735.5}\\protected@file@percent }\n\\newlabel{task-properties-6}{{735.5}{2220}{Task Properties}{section.735.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Methods}{2221}{section.735.5}\\protected@file@percent }\n\\newlabel{task-methods-3}{{735.5}{2221}{Task Methods}{section.735.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {735.6}go\\({command}\\)\\{programName\\} Task}{2221}{section.735.6}\\protected@file@percent }\n\\newlabel{gocommandprogramname-task}{{735.6}{2221}{\\texorpdfstring {go\\({command}\\)\\{programName\\} Task}{go\\{command\\}\\{programName\\} Task}}{section.735.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {736}Gulp Gradle Plugin}{2223}{chapter.736}\\protected@file@percent }\n\\newlabel{gulp-gradle-plugin}{{736}{2223}{Gulp Gradle Plugin}{chapter.736}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {736.1}Usage}{2223}{section.736.1}\\protected@file@percent }\n\\newlabel{usage-8}{{736.1}{2223}{Usage}{section.736.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {736.2}Tasks}{2223}{section.736.2}\\protected@file@percent }\n\\newlabel{tasks-7}{{736.2}{2223}{Tasks}{section.736.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {736.3}ExecuteGulpTask}{2223}{section.736.3}\\protected@file@percent }\n\\newlabel{executegulptask}{{736.3}{2223}{ExecuteGulpTask}{section.736.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2224}{section.736.3}\\protected@file@percent }\n\\newlabel{task-properties-7}{{736.3}{2224}{Task Properties}{section.736.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {737}Jasper JSPC Gradle Plugin}{2225}{chapter.737}\\protected@file@percent }\n\\newlabel{jasper-jspc-gradle-plugin}{{737}{2225}{Jasper JSPC Gradle Plugin}{chapter.737}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {737.1}Usage}{2225}{section.737.1}\\protected@file@percent }\n\\newlabel{usage-9}{{737.1}{2225}{Usage}{section.737.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {737.2}Tasks}{2226}{section.737.2}\\protected@file@percent }\n\\newlabel{tasks-8}{{737.2}{2226}{Tasks}{section.737.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {737.3}CompileJSPTask}{2226}{section.737.3}\\protected@file@percent }\n\\newlabel{compilejsptask}{{737.3}{2226}{CompileJSPTask}{section.737.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2226}{section.737.3}\\protected@file@percent }\n\\newlabel{task-properties-8}{{737.3}{2226}{Task Properties}{section.737.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {737.4}Additional Configuration}{2226}{section.737.4}\\protected@file@percent }\n\\newlabel{additional-configuration-5}{{737.4}{2226}{Additional Configuration}{section.737.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {737.5}JSP Compilation Classpath}{2226}{section.737.5}\\protected@file@percent }\n\\newlabel{jsp-compilation-classpath}{{737.5}{2226}{JSP Compilation Classpath}{section.737.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {737.6}Liferay Jasper JSPC Dependency}{2227}{section.737.6}\\protected@file@percent }\n\\newlabel{liferay-jasper-jspc-dependency}{{737.6}{2227}{Liferay Jasper JSPC Dependency}{section.737.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {738}Javadoc Formatter Gradle Plugin}{2229}{chapter.738}\\protected@file@percent }\n\\newlabel{javadoc-formatter-gradle-plugin}{{738}{2229}{Javadoc Formatter Gradle Plugin}{chapter.738}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {738.1}Usage}{2229}{section.738.1}\\protected@file@percent }\n\\newlabel{usage-10}{{738.1}{2229}{Usage}{section.738.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {738.2}Tasks}{2230}{section.738.2}\\protected@file@percent }\n\\newlabel{tasks-9}{{738.2}{2230}{Tasks}{section.738.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {738.3}FormatJavadocTask}{2230}{section.738.3}\\protected@file@percent }\n\\newlabel{formatjavadoctask}{{738.3}{2230}{FormatJavadocTask}{section.738.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2230}{section.738.3}\\protected@file@percent }\n\\newlabel{task-properties-9}{{738.3}{2230}{Task Properties}{section.738.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Methods}{2230}{section.738.3}\\protected@file@percent }\n\\newlabel{task-methods-4}{{738.3}{2230}{Task Methods}{section.738.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {738.4}Additional Configuration}{2230}{section.738.4}\\protected@file@percent }\n\\newlabel{additional-configuration-6}{{738.4}{2230}{Additional Configuration}{section.738.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {738.5}Liferay Javadoc Formatter Dependency}{2231}{section.738.5}\\protected@file@percent }\n\\newlabel{liferay-javadoc-formatter-dependency}{{738.5}{2231}{Liferay Javadoc Formatter Dependency}{section.738.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {738.6}System Properties}{2231}{section.738.6}\\protected@file@percent }\n\\newlabel{system-properties-1}{{738.6}{2231}{System Properties}{section.738.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {739}JS Module Config Generator Gradle Plugin}{2233}{chapter.739}\\protected@file@percent }\n\\newlabel{js-module-config-generator-gradle-plugin}{{739}{2233}{JS Module Config Generator Gradle Plugin}{chapter.739}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {739.1}Usage}{2233}{section.739.1}\\protected@file@percent }\n\\newlabel{usage-11}{{739.1}{2233}{Usage}{section.739.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {739.2}Project Extension}{2233}{section.739.2}\\protected@file@percent }\n\\newlabel{project-extension-4}{{739.2}{2233}{Project Extension}{section.739.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {739.3}Tasks}{2234}{section.739.3}\\protected@file@percent }\n\\newlabel{tasks-10}{{739.3}{2234}{Tasks}{section.739.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {739.4}ConfigJSModulesTask}{2234}{section.739.4}\\protected@file@percent }\n\\newlabel{configjsmodulestask}{{739.4}{2234}{ConfigJSModulesTask}{section.739.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2234}{section.739.4}\\protected@file@percent }\n\\newlabel{task-properties-10}{{739.4}{2234}{Task Properties}{section.739.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {740}JS Transpiler Gradle Plugin}{2237}{chapter.740}\\protected@file@percent }\n\\newlabel{js-transpiler-gradle-plugin}{{740}{2237}{JS Transpiler Gradle Plugin}{chapter.740}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {740.1}Usage}{2237}{section.740.1}\\protected@file@percent }\n\\newlabel{usage-12}{{740.1}{2237}{Usage}{section.740.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {740.2}JS Transpiler Plugin}{2238}{section.740.2}\\protected@file@percent }\n\\newlabel{js-transpiler-plugin}{{740.2}{2238}{JS Transpiler Plugin}{section.740.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {740.3}JS Transpiler Base Plugin}{2238}{section.740.3}\\protected@file@percent }\n\\newlabel{js-transpiler-base-plugin}{{740.3}{2238}{JS Transpiler Base Plugin}{section.740.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {740.4}Tasks}{2238}{section.740.4}\\protected@file@percent }\n\\newlabel{tasks-11}{{740.4}{2238}{Tasks}{section.740.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {740.5}TranspileJSTask}{2239}{section.740.5}\\protected@file@percent }\n\\newlabel{transpilejstask}{{740.5}{2239}{TranspileJSTask}{section.740.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2239}{section.740.5}\\protected@file@percent }\n\\newlabel{task-properties-11}{{740.5}{2239}{Task Properties}{section.740.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Methods}{2239}{section.740.5}\\protected@file@percent }\n\\newlabel{task-methods-5}{{740.5}{2239}{Task Methods}{section.740.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {741}JSDoc Gradle Plugin}{2241}{chapter.741}\\protected@file@percent }\n\\newlabel{jsdoc-gradle-plugin}{{741}{2241}{JSDoc Gradle Plugin}{chapter.741}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {741.1}Usage}{2241}{section.741.1}\\protected@file@percent }\n\\newlabel{usage-13}{{741.1}{2241}{Usage}{section.741.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {741.2}JSDoc Plugin}{2242}{section.741.2}\\protected@file@percent }\n\\newlabel{jsdoc-plugin}{{741.2}{2242}{JSDoc Plugin}{section.741.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {741.3}AppJSDoc Plugin}{2242}{section.741.3}\\protected@file@percent }\n\\newlabel{appjsdoc-plugin}{{741.3}{2242}{AppJSDoc Plugin}{section.741.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {741.4}Project Extension}{2242}{section.741.4}\\protected@file@percent }\n\\newlabel{project-extension-5}{{741.4}{2242}{Project Extension}{section.741.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {741.5}Tasks}{2243}{section.741.5}\\protected@file@percent }\n\\newlabel{tasks-12}{{741.5}{2243}{Tasks}{section.741.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {741.6}JSDocTask}{2243}{section.741.6}\\protected@file@percent }\n\\newlabel{jsdoctask}{{741.6}{2243}{JSDocTask}{section.741.6}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2243}{section.741.6}\\protected@file@percent }\n\\newlabel{task-properties-12}{{741.6}{2243}{Task Properties}{section.741.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {742}Lang Builder Gradle Plugin}{2245}{chapter.742}\\protected@file@percent }\n\\newlabel{lang-builder-gradle-plugin}{{742}{2245}{Lang Builder Gradle Plugin}{chapter.742}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {742.1}Usage}{2245}{section.742.1}\\protected@file@percent }\n\\newlabel{usage-14}{{742.1}{2245}{Usage}{section.742.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {742.2}Tasks}{2246}{section.742.2}\\protected@file@percent }\n\\newlabel{tasks-13}{{742.2}{2246}{Tasks}{section.742.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {742.3}BuildLangTask}{2246}{section.742.3}\\protected@file@percent }\n\\newlabel{buildlangtask}{{742.3}{2246}{BuildLangTask}{section.742.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2246}{section.742.3}\\protected@file@percent }\n\\newlabel{task-properties-13}{{742.3}{2246}{Task Properties}{section.742.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Methods}{2246}{section.742.3}\\protected@file@percent }\n\\newlabel{task-methods-6}{{742.3}{2246}{Task Methods}{section.742.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {742.4}Additional Configuration}{2247}{section.742.4}\\protected@file@percent }\n\\newlabel{additional-configuration-7}{{742.4}{2247}{Additional Configuration}{section.742.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {742.5}Liferay Lang Builder Dependency}{2247}{section.742.5}\\protected@file@percent }\n\\newlabel{liferay-lang-builder-dependency}{{742.5}{2247}{Liferay Lang Builder Dependency}{section.742.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {743}Maven Plugin Builder Gradle Plugin}{2249}{chapter.743}\\protected@file@percent }\n\\newlabel{maven-plugin-builder-gradle-plugin}{{743}{2249}{Maven Plugin Builder Gradle Plugin}{chapter.743}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {743.1}Usage}{2249}{section.743.1}\\protected@file@percent }\n\\newlabel{usage-15}{{743.1}{2249}{Usage}{section.743.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {743.2}Tasks}{2249}{section.743.2}\\protected@file@percent }\n\\newlabel{tasks-14}{{743.2}{2249}{Tasks}{section.743.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {743.3}BuildPluginDescriptorTask}{2250}{section.743.3}\\protected@file@percent }\n\\newlabel{buildplugindescriptortask}{{743.3}{2250}{BuildPluginDescriptorTask}{section.743.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2250}{section.743.3}\\protected@file@percent }\n\\newlabel{task-properties-14}{{743.3}{2250}{Task Properties}{section.743.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {743.4}Task Methods}{2251}{section.743.4}\\protected@file@percent }\n\\newlabel{task-methods-7}{{743.4}{2251}{Task Methods}{section.743.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {743.5}WriteMavenSettingsTask}{2251}{section.743.5}\\protected@file@percent }\n\\newlabel{writemavensettingstask}{{743.5}{2251}{WriteMavenSettingsTask}{section.743.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2251}{section.743.5}\\protected@file@percent }\n\\newlabel{task-properties-15}{{743.5}{2251}{Task Properties}{section.743.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {743.6}Additional Configuration}{2252}{section.743.6}\\protected@file@percent }\n\\newlabel{additional-configuration-8}{{743.6}{2252}{Additional Configuration}{section.743.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {743.7}Maven Embedder Dependency}{2252}{section.743.7}\\protected@file@percent }\n\\newlabel{maven-embedder-dependency}{{743.7}{2252}{Maven Embedder Dependency}{section.743.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {743.8}System Properties}{2252}{section.743.8}\\protected@file@percent }\n\\newlabel{system-properties-2}{{743.8}{2252}{System Properties}{section.743.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {744}Node Gradle Plugin}{2253}{chapter.744}\\protected@file@percent }\n\\newlabel{node-gradle-plugin}{{744}{2253}{Node Gradle Plugin}{chapter.744}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {744.1}Usage}{2253}{section.744.1}\\protected@file@percent }\n\\newlabel{usage-16}{{744.1}{2253}{Usage}{section.744.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {744.2}Project Extension}{2253}{section.744.2}\\protected@file@percent }\n\\newlabel{project-extension-6}{{744.2}{2253}{Project Extension}{section.744.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {744.3}Tasks}{2254}{section.744.3}\\protected@file@percent }\n\\newlabel{tasks-15}{{744.3}{2254}{Tasks}{section.744.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {744.4}DownloadNodeTask}{2254}{section.744.4}\\protected@file@percent }\n\\newlabel{downloadnodetask}{{744.4}{2254}{DownloadNodeTask}{section.744.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2255}{section.744.4}\\protected@file@percent }\n\\newlabel{task-properties-16}{{744.4}{2255}{Task Properties}{section.744.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {744.5}ExecuteNodeTask}{2255}{section.744.5}\\protected@file@percent }\n\\newlabel{executenodetask}{{744.5}{2255}{ExecuteNodeTask}{section.744.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2255}{section.744.5}\\protected@file@percent }\n\\newlabel{task-properties-17}{{744.5}{2255}{Task Properties}{section.744.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Methods}{2255}{section.744.5}\\protected@file@percent }\n\\newlabel{task-methods-8}{{744.5}{2255}{Task Methods}{section.744.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {744.6}ExecuteNodeScriptTask}{2256}{section.744.6}\\protected@file@percent }\n\\newlabel{executenodescripttask}{{744.6}{2256}{ExecuteNodeScriptTask}{section.744.6}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2256}{section.744.6}\\protected@file@percent }\n\\newlabel{task-properties-18}{{744.6}{2256}{Task Properties}{section.744.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {744.7}ExecuteNpmTask}{2256}{section.744.7}\\protected@file@percent }\n\\newlabel{executenpmtask}{{744.7}{2256}{ExecuteNpmTask}{section.744.7}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2256}{section.744.7}\\protected@file@percent }\n\\newlabel{task-properties-19}{{744.7}{2256}{Task Properties}{section.744.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {744.8}DownloadNodeModuleTask}{2257}{section.744.8}\\protected@file@percent }\n\\newlabel{downloadnodemoduletask}{{744.8}{2257}{DownloadNodeModuleTask}{section.744.8}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2257}{section.744.8}\\protected@file@percent }\n\\newlabel{task-properties-20}{{744.8}{2257}{Task Properties}{section.744.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {744.9}NpmInstallTask}{2257}{section.744.9}\\protected@file@percent }\n\\newlabel{npminstalltask}{{744.9}{2257}{NpmInstallTask}{section.744.9}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2257}{section.744.9}\\protected@file@percent }\n\\newlabel{task-properties-21}{{744.9}{2257}{Task Properties}{section.744.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {744.10}NpmShrinkwrapTask}{2258}{section.744.10}\\protected@file@percent }\n\\newlabel{npmshrinkwraptask}{{744.10}{2258}{NpmShrinkwrapTask}{section.744.10}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2258}{section.744.10}\\protected@file@percent }\n\\newlabel{task-properties-22}{{744.10}{2258}{Task Properties}{section.744.10}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Methods}{2258}{section.744.10}\\protected@file@percent }\n\\newlabel{task-methods-9}{{744.10}{2258}{Task Methods}{section.744.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {744.11}PublishNodeModuleTask}{2258}{section.744.11}\\protected@file@percent }\n\\newlabel{publishnodemoduletask}{{744.11}{2258}{PublishNodeModuleTask}{section.744.11}{}}\n\\gdef \\LT@lvi {\\LT@entry \n    {1}{167.54416pt}\\LT@entry \n    {1}{302.21085pt}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2259}{section.744.11}\\protected@file@percent }\n\\newlabel{task-properties-23}{{744.11}{2259}{Task Properties}{section.744.11}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Methods}{2259}{section.744.11}\\protected@file@percent }\n\\newlabel{task-methods-10}{{744.11}{2259}{Task Methods}{section.744.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {744.12}npmRun\\$\\{script\\} Task}{2259}{section.744.12}\\protected@file@percent }\n\\newlabel{npmrunscript-task}{{744.12}{2259}{npmRun\\$\\{script\\} Task}{section.744.12}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {745}REST Builder Gradle Plugin}{2261}{chapter.745}\\protected@file@percent }\n\\newlabel{rest-builder-gradle-plugin}{{745}{2261}{REST Builder Gradle Plugin}{chapter.745}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {745.1}Usage}{2261}{section.745.1}\\protected@file@percent }\n\\newlabel{usage-17}{{745.1}{2261}{Usage}{section.745.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {745.2}Tasks}{2261}{section.745.2}\\protected@file@percent }\n\\newlabel{tasks-16}{{745.2}{2261}{Tasks}{section.745.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {745.3}BuildRESTTask}{2262}{section.745.3}\\protected@file@percent }\n\\newlabel{buildresttask}{{745.3}{2262}{BuildRESTTask}{section.745.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2262}{section.745.3}\\protected@file@percent }\n\\newlabel{task-properties-24}{{745.3}{2262}{Task Properties}{section.745.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {745.4}Additional Configuration}{2262}{section.745.4}\\protected@file@percent }\n\\newlabel{additional-configuration-9}{{745.4}{2262}{Additional Configuration}{section.745.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {745.5}Liferay REST Builder Dependency}{2262}{section.745.5}\\protected@file@percent }\n\\newlabel{liferay-rest-builder-dependency}{{745.5}{2262}{Liferay REST Builder Dependency}{section.745.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {746}Service Builder Gradle Plugin}{2263}{chapter.746}\\protected@file@percent }\n\\newlabel{service-builder-gradle-plugin}{{746}{2263}{Service Builder Gradle Plugin}{chapter.746}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {746.1}Usage}{2263}{section.746.1}\\protected@file@percent }\n\\newlabel{usage-18}{{746.1}{2263}{Usage}{section.746.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {746.2}Tasks}{2263}{section.746.2}\\protected@file@percent }\n\\newlabel{tasks-17}{{746.2}{2263}{Tasks}{section.746.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {746.3}BuildServiceTask}{2265}{section.746.3}\\protected@file@percent }\n\\newlabel{buildservicetask}{{746.3}{2265}{BuildServiceTask}{section.746.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2265}{section.746.3}\\protected@file@percent }\n\\newlabel{task-properties-25}{{746.3}{2265}{Task Properties}{section.746.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {746.4}Additional Configuration}{2266}{section.746.4}\\protected@file@percent }\n\\newlabel{additional-configuration-10}{{746.4}{2266}{Additional Configuration}{section.746.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {746.5}Liferay Service Builder Dependency}{2266}{section.746.5}\\protected@file@percent }\n\\newlabel{liferay-service-builder-dependency}{{746.5}{2266}{Liferay Service Builder Dependency}{section.746.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {747}Source Formatter Gradle Plugin}{2267}{chapter.747}\\protected@file@percent }\n\\newlabel{source-formatter-gradle-plugin}{{747}{2267}{Source Formatter Gradle Plugin}{chapter.747}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {747.1}Usage}{2267}{section.747.1}\\protected@file@percent }\n\\newlabel{usage-19}{{747.1}{2267}{Usage}{section.747.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {747.2}Tasks}{2267}{section.747.2}\\protected@file@percent }\n\\newlabel{tasks-18}{{747.2}{2267}{Tasks}{section.747.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {747.3}FormatSourceTask}{2268}{section.747.3}\\protected@file@percent }\n\\newlabel{formatsourcetask}{{747.3}{2268}{FormatSourceTask}{section.747.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2268}{section.747.3}\\protected@file@percent }\n\\newlabel{task-properties-26}{{747.3}{2268}{Task Properties}{section.747.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {747.4}Additional Configuration}{2269}{section.747.4}\\protected@file@percent }\n\\newlabel{additional-configuration-11}{{747.4}{2269}{Additional Configuration}{section.747.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {747.5}Liferay Source Formatter Dependency}{2269}{section.747.5}\\protected@file@percent }\n\\newlabel{liferay-source-formatter-dependency}{{747.5}{2269}{Liferay Source Formatter Dependency}{section.747.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {747.6}System Properties}{2269}{section.747.6}\\protected@file@percent }\n\\newlabel{system-properties-3}{{747.6}{2269}{System Properties}{section.747.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {748}Soy Gradle Plugin}{2271}{chapter.748}\\protected@file@percent }\n\\newlabel{soy-gradle-plugin}{{748}{2271}{Soy Gradle Plugin}{chapter.748}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {748.1}Usage}{2271}{section.748.1}\\protected@file@percent }\n\\newlabel{usage-20}{{748.1}{2271}{Usage}{section.748.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {748.2}Soy Plugin}{2272}{section.748.2}\\protected@file@percent }\n\\newlabel{soy-plugin}{{748.2}{2272}{Soy Plugin}{section.748.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {748.3}Additional Configuration}{2272}{section.748.3}\\protected@file@percent }\n\\newlabel{additional-configuration-12}{{748.3}{2272}{Additional Configuration}{section.748.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Soy Dependency}{2272}{section.748.3}\\protected@file@percent }\n\\newlabel{soy-dependency}{{748.3}{2272}{Soy Dependency}{section.748.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {748.4}Soy Translation Plugin}{2272}{section.748.4}\\protected@file@percent }\n\\newlabel{soy-translation-plugin}{{748.4}{2272}{Soy Translation Plugin}{section.748.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {748.5}Tasks}{2273}{section.748.5}\\protected@file@percent }\n\\newlabel{tasks-19}{{748.5}{2273}{Tasks}{section.748.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {748.6}BuildSoyTask}{2273}{section.748.6}\\protected@file@percent }\n\\newlabel{buildsoytask}{{748.6}{2273}{BuildSoyTask}{section.748.6}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2273}{section.748.6}\\protected@file@percent }\n\\newlabel{task-properties-27}{{748.6}{2273}{Task Properties}{section.748.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {748.7}WrapSoyAlloyTemplateTask}{2273}{section.748.7}\\protected@file@percent }\n\\newlabel{wrapsoyalloytemplatetask}{{748.7}{2273}{WrapSoyAlloyTemplateTask}{section.748.7}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2273}{section.748.7}\\protected@file@percent }\n\\newlabel{task-properties-28}{{748.7}{2273}{Task Properties}{section.748.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {748.8}ReplaceSoyTranslationTask}{2273}{section.748.8}\\protected@file@percent }\n\\newlabel{replacesoytranslationtask}{{748.8}{2273}{ReplaceSoyTranslationTask}{section.748.8}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2274}{section.748.8}\\protected@file@percent }\n\\newlabel{task-properties-29}{{748.8}{2274}{Task Properties}{section.748.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {749}Target Platform Gradle Plugin}{2275}{chapter.749}\\protected@file@percent }\n\\newlabel{target-platform-gradle-plugin}{{749}{2275}{Target Platform Gradle Plugin}{chapter.749}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {749.1}Usage}{2275}{section.749.1}\\protected@file@percent }\n\\newlabel{usage-21}{{749.1}{2275}{Usage}{section.749.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {749.2}Target Platform Plugin}{2276}{section.749.2}\\protected@file@percent }\n\\newlabel{target-platform-plugin}{{749.2}{2276}{Target Platform Plugin}{section.749.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {749.3}Target Platform IDE Plugin}{2276}{section.749.3}\\protected@file@percent }\n\\newlabel{target-platform-ide-plugin}{{749.3}{2276}{Target Platform IDE Plugin}{section.749.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {749.4}Project Extension}{2276}{section.749.4}\\protected@file@percent }\n\\newlabel{project-extension-7}{{749.4}{2276}{Project Extension}{section.749.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {749.5}Tasks}{2277}{section.749.5}\\protected@file@percent }\n\\newlabel{tasks-20}{{749.5}{2277}{Tasks}{section.749.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {749.6}ResolveTask}{2277}{section.749.6}\\protected@file@percent }\n\\newlabel{resolvetask}{{749.6}{2277}{ResolveTask}{section.749.6}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2277}{section.749.6}\\protected@file@percent }\n\\newlabel{task-properties-30}{{749.6}{2277}{Task Properties}{section.749.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {749.7}Additional Configuration}{2278}{section.749.7}\\protected@file@percent }\n\\newlabel{additional-configuration-13}{{749.7}{2278}{Additional Configuration}{section.749.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {749.8}Target Platform BOMs Dependency}{2278}{section.749.8}\\protected@file@percent }\n\\newlabel{target-platform-boms-dependency}{{749.8}{2278}{Target Platform BOMs Dependency}{section.749.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {749.9}Target Platform Bundles Dependency}{2278}{section.749.9}\\protected@file@percent }\n\\newlabel{target-platform-bundles-dependency}{{749.9}{2278}{Target Platform Bundles Dependency}{section.749.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {749.10}Target Platform Distro Dependency}{2278}{section.749.10}\\protected@file@percent }\n\\newlabel{target-platform-distro-dependency}{{749.10}{2278}{Target Platform Distro Dependency}{section.749.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {749.11}Target Platform Requirements Dependency}{2279}{section.749.11}\\protected@file@percent }\n\\newlabel{target-platform-requirements-dependency}{{749.11}{2279}{Target Platform Requirements Dependency}{section.749.11}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {750}Theme Builder Gradle Plugin}{2281}{chapter.750}\\protected@file@percent }\n\\newlabel{theme-builder-gradle-plugin}{{750}{2281}{Theme Builder Gradle Plugin}{chapter.750}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {750.1}Usage}{2281}{section.750.1}\\protected@file@percent }\n\\newlabel{usage-22}{{750.1}{2281}{Usage}{section.750.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {750.2}Tasks}{2282}{section.750.2}\\protected@file@percent }\n\\newlabel{tasks-21}{{750.2}{2282}{Tasks}{section.750.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {750.3}BuildThemeTask}{2282}{section.750.3}\\protected@file@percent }\n\\newlabel{buildthemetask}{{750.3}{2282}{BuildThemeTask}{section.750.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2282}{section.750.3}\\protected@file@percent }\n\\newlabel{task-properties-31}{{750.3}{2282}{Task Properties}{section.750.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {750.4}Additional Configuration}{2283}{section.750.4}\\protected@file@percent }\n\\newlabel{additional-configuration-14}{{750.4}{2283}{Additional Configuration}{section.750.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {750.5}Liferay Theme Builder Dependency}{2283}{section.750.5}\\protected@file@percent }\n\\newlabel{liferay-theme-builder-dependency}{{750.5}{2283}{Liferay Theme Builder Dependency}{section.750.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {750.6}Parent Theme Dependencies}{2283}{section.750.6}\\protected@file@percent }\n\\newlabel{parent-theme-dependencies}{{750.6}{2283}{Parent Theme Dependencies}{section.750.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {751}TLD Formatter Gradle Plugin}{2285}{chapter.751}\\protected@file@percent }\n\\newlabel{tld-formatter-gradle-plugin}{{751}{2285}{TLD Formatter Gradle Plugin}{chapter.751}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {751.1}Usage}{2285}{section.751.1}\\protected@file@percent }\n\\newlabel{usage-23}{{751.1}{2285}{Usage}{section.751.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {751.2}Tasks}{2285}{section.751.2}\\protected@file@percent }\n\\newlabel{tasks-22}{{751.2}{2285}{Tasks}{section.751.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {751.3}FormatTLDTask}{2286}{section.751.3}\\protected@file@percent }\n\\newlabel{formattldtask}{{751.3}{2286}{FormatTLDTask}{section.751.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2286}{section.751.3}\\protected@file@percent }\n\\newlabel{task-properties-32}{{751.3}{2286}{Task Properties}{section.751.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {751.4}Additional Configuration}{2286}{section.751.4}\\protected@file@percent }\n\\newlabel{additional-configuration-15}{{751.4}{2286}{Additional Configuration}{section.751.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {751.5}Liferay TLD Formatter Dependency}{2286}{section.751.5}\\protected@file@percent }\n\\newlabel{liferay-tld-formatter-dependency}{{751.5}{2286}{Liferay TLD Formatter Dependency}{section.751.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {752}TLDDoc Builder Gradle Plugin}{2287}{chapter.752}\\protected@file@percent }\n\\newlabel{tlddoc-builder-gradle-plugin}{{752}{2287}{TLDDoc Builder Gradle Plugin}{chapter.752}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {752.1}Usage}{2287}{section.752.1}\\protected@file@percent }\n\\newlabel{usage-24}{{752.1}{2287}{Usage}{section.752.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {752.2}TLDDoc Builder Plugin}{2288}{section.752.2}\\protected@file@percent }\n\\newlabel{tlddoc-builder-plugin}{{752.2}{2288}{TLDDoc Builder Plugin}{section.752.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {752.3}App TLDDoc Builder Plugin}{2288}{section.752.3}\\protected@file@percent }\n\\newlabel{app-tlddoc-builder-plugin}{{752.3}{2288}{App TLDDoc Builder Plugin}{section.752.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {752.4}Project Extension}{2289}{section.752.4}\\protected@file@percent }\n\\newlabel{project-extension-8}{{752.4}{2289}{Project Extension}{section.752.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {752.5}Tasks}{2289}{section.752.5}\\protected@file@percent }\n\\newlabel{tasks-23}{{752.5}{2289}{Tasks}{section.752.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {752.6}TLDDocTask}{2289}{section.752.6}\\protected@file@percent }\n\\newlabel{tlddoctask}{{752.6}{2289}{TLDDocTask}{section.752.6}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2289}{section.752.6}\\protected@file@percent }\n\\newlabel{task-properties-33}{{752.6}{2289}{Task Properties}{section.752.6}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Methods}{2289}{section.752.6}\\protected@file@percent }\n\\newlabel{task-methods-11}{{752.6}{2289}{Task Methods}{section.752.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {752.7}ValidateSchemaTask}{2290}{section.752.7}\\protected@file@percent }\n\\newlabel{validateschematask}{{752.7}{2290}{ValidateSchemaTask}{section.752.7}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2290}{section.752.7}\\protected@file@percent }\n\\newlabel{task-properties-34}{{752.7}{2290}{Task Properties}{section.752.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {752.8}Additional Configuration}{2290}{section.752.8}\\protected@file@percent }\n\\newlabel{additional-configuration-16}{{752.8}{2290}{Additional Configuration}{section.752.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {752.9}Tag Library Documentation Generator Dependency}{2290}{section.752.9}\\protected@file@percent }\n\\newlabel{tag-library-documentation-generator-dependency}{{752.9}{2290}{Tag Library Documentation Generator Dependency}{section.752.9}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {753}Whip Gradle Plugin}{2291}{chapter.753}\\protected@file@percent }\n\\newlabel{whip-gradle-plugin}{{753}{2291}{Whip Gradle Plugin}{chapter.753}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {753.1}Usage}{2291}{section.753.1}\\protected@file@percent }\n\\newlabel{usage-25}{{753.1}{2291}{Usage}{section.753.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {753.2}Project Extension}{2292}{section.753.2}\\protected@file@percent }\n\\newlabel{project-extension-9}{{753.2}{2292}{Project Extension}{section.753.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {753.3}Task Extension}{2292}{section.753.3}\\protected@file@percent }\n\\newlabel{task-extension}{{753.3}{2292}{Task Extension}{section.753.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {753.4}Additional Configuration}{2292}{section.753.4}\\protected@file@percent }\n\\newlabel{additional-configuration-17}{{753.4}{2292}{Additional Configuration}{section.753.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {753.5}Liferay Whip Dependency}{2292}{section.753.5}\\protected@file@percent }\n\\newlabel{liferay-whip-dependency}{{753.5}{2292}{Liferay Whip Dependency}{section.753.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {754}WSDD Builder Gradle Plugin}{2293}{chapter.754}\\protected@file@percent }\n\\newlabel{wsdd-builder-gradle-plugin}{{754}{2293}{WSDD Builder Gradle Plugin}{chapter.754}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {754.1}Usage}{2293}{section.754.1}\\protected@file@percent }\n\\newlabel{usage-26}{{754.1}{2293}{Usage}{section.754.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {754.2}Tasks}{2293}{section.754.2}\\protected@file@percent }\n\\newlabel{tasks-24}{{754.2}{2293}{Tasks}{section.754.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {754.3}BuildWSDDTask}{2294}{section.754.3}\\protected@file@percent }\n\\newlabel{buildwsddtask}{{754.3}{2294}{BuildWSDDTask}{section.754.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2294}{section.754.3}\\protected@file@percent }\n\\newlabel{task-properties-35}{{754.3}{2294}{Task Properties}{section.754.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {754.4}Additional Configuration}{2294}{section.754.4}\\protected@file@percent }\n\\newlabel{additional-configuration-18}{{754.4}{2294}{Additional Configuration}{section.754.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {754.5}Liferay WSDD Builder Dependency}{2294}{section.754.5}\\protected@file@percent }\n\\newlabel{liferay-wsdd-builder-dependency}{{754.5}{2294}{Liferay WSDD Builder Dependency}{section.754.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {755}WSDL Builder Gradle Plugin}{2297}{chapter.755}\\protected@file@percent }\n\\newlabel{wsdl-builder-gradle-plugin}{{755}{2297}{WSDL Builder Gradle Plugin}{chapter.755}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {755.1}Usage}{2297}{section.755.1}\\protected@file@percent }\n\\newlabel{usage-27}{{755.1}{2297}{Usage}{section.755.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {755.2}Tasks}{2297}{section.755.2}\\protected@file@percent }\n\\newlabel{tasks-25}{{755.2}{2297}{Tasks}{section.755.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {755.3}BuildWSDLTask}{2298}{section.755.3}\\protected@file@percent }\n\\newlabel{buildwsdltask}{{755.3}{2298}{BuildWSDLTask}{section.755.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2298}{section.755.3}\\protected@file@percent }\n\\newlabel{task-properties-36}{{755.3}{2298}{Task Properties}{section.755.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Methods}{2298}{section.755.3}\\protected@file@percent }\n\\newlabel{task-methods-12}{{755.3}{2298}{Task Methods}{section.755.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Helper Tasks}{2298}{section.755.3}\\protected@file@percent }\n\\newlabel{helper-tasks-1}{{755.3}{2298}{Helper Tasks}{section.755.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {755.4}Additional Configuration}{2299}{section.755.4}\\protected@file@percent }\n\\newlabel{additional-configuration-19}{{755.4}{2299}{Additional Configuration}{section.755.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {755.5}Apache Axis Dependency}{2299}{section.755.5}\\protected@file@percent }\n\\newlabel{apache-axis-dependency}{{755.5}{2299}{Apache Axis Dependency}{section.755.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {756}XML Formatter Gradle Plugin}{2301}{chapter.756}\\protected@file@percent }\n\\newlabel{xml-formatter-gradle-plugin}{{756}{2301}{XML Formatter Gradle Plugin}{chapter.756}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {756.1}Usage}{2301}{section.756.1}\\protected@file@percent }\n\\newlabel{usage-28}{{756.1}{2301}{Usage}{section.756.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {756.2}Tasks}{2301}{section.756.2}\\protected@file@percent }\n\\newlabel{tasks-26}{{756.2}{2301}{Tasks}{section.756.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {756.3}FormatXMLTask}{2302}{section.756.3}\\protected@file@percent }\n\\newlabel{formatxmltask}{{756.3}{2302}{FormatXMLTask}{section.756.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2302}{section.756.3}\\protected@file@percent }\n\\newlabel{task-properties-37}{{756.3}{2302}{Task Properties}{section.756.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {756.4}Additional Configuration}{2302}{section.756.4}\\protected@file@percent }\n\\newlabel{additional-configuration-20}{{756.4}{2302}{Additional Configuration}{section.756.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {756.5}Liferay XML Formatter Dependency}{2302}{section.756.5}\\protected@file@percent }\n\\newlabel{liferay-xml-formatter-dependency}{{756.5}{2302}{Liferay XML Formatter Dependency}{section.756.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {757}XSD Builder Gradle Plugin}{2303}{chapter.757}\\protected@file@percent }\n\\newlabel{xsd-builder-gradle-plugin}{{757}{2303}{XSD Builder Gradle Plugin}{chapter.757}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {757.1}Usage}{2303}{section.757.1}\\protected@file@percent }\n\\newlabel{usage-29}{{757.1}{2303}{Usage}{section.757.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {757.2}Tasks}{2303}{section.757.2}\\protected@file@percent }\n\\newlabel{tasks-27}{{757.2}{2303}{Tasks}{section.757.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {757.3}BuildXSDTask}{2304}{section.757.3}\\protected@file@percent }\n\\newlabel{buildxsdtask}{{757.3}{2304}{BuildXSDTask}{section.757.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Task Properties}{2304}{section.757.3}\\protected@file@percent }\n\\newlabel{task-properties-38}{{757.3}{2304}{Task Properties}{section.757.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {757.4}Additional Configuration}{2304}{section.757.4}\\protected@file@percent }\n\\newlabel{additional-configuration-21}{{757.4}{2304}{Additional Configuration}{section.757.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {757.5}Apache XMLBeans Dependency}{2304}{section.757.5}\\protected@file@percent }\n\\newlabel{apache-xmlbeans-dependency}{{757.5}{2304}{Apache XMLBeans Dependency}{section.757.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {758}Liferay Faces}{2305}{chapter.758}\\protected@file@percent }\n\\newlabel{liferay-faces}{{758}{2305}{Liferay Faces}{chapter.758}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {759}Liferay Faces Version Scheme}{2307}{chapter.759}\\protected@file@percent }\n\\newlabel{liferay-faces-version-scheme}{{759}{2307}{Liferay Faces Version Scheme}{chapter.759}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {759.1}Using The Liferay Faces Archetype Portlet}{2307}{section.759.1}\\protected@file@percent }\n\\newlabel{using-the-liferay-faces-archetype-portlet}{{759.1}{2307}{Using The Liferay Faces Archetype Portlet}{section.759.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {759.2}Liferay Faces Alloy}{2307}{section.759.2}\\protected@file@percent }\n\\newlabel{liferay-faces-alloy}{{759.2}{2307}{Liferay Faces Alloy}{section.759.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {759.3}Liferay Faces Bridge}{2307}{section.759.3}\\protected@file@percent }\n\\newlabel{liferay-faces-bridge}{{759.3}{2307}{Liferay Faces Bridge}{section.759.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {759.4}Liferay Faces Bridge Ext}{2308}{section.759.4}\\protected@file@percent }\n\\newlabel{liferay-faces-bridge-ext}{{759.4}{2308}{Liferay Faces Bridge Ext}{section.759.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {759.5}Liferay Faces Portal}{2308}{section.759.5}\\protected@file@percent }\n\\newlabel{liferay-faces-portal}{{759.5}{2308}{Liferay Faces Portal}{section.759.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {759.6}Liferay Faces Util}{2308}{section.759.6}\\protected@file@percent }\n\\newlabel{liferay-faces-util}{{759.6}{2308}{Liferay Faces Util}{section.759.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {759.1}{\\ignorespaces The Liferay Faces dependency diagram helps visualize how components interact and depend on each other.}}{2309}{figure.759.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {760}Understanding Liferay Faces Bridge}{2311}{chapter.760}\\protected@file@percent }\n\\newlabel{understanding-liferay-faces-bridge}{{760}{2311}{Understanding Liferay Faces Bridge}{chapter.760}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {760.1}{\\ignorespaces The different phases of the JSF Lifecycle are executed depending on which phase of the Portlet lifecycle is being executed.}}{2312}{figure.760.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {760.1}Related Topics}{2313}{section.760.1}\\protected@file@percent }\n\\newlabel{related-topics-45}{{760.1}{2313}{Related Topics}{section.760.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {761}Understanding Liferay Faces Alloy}{2315}{chapter.761}\\protected@file@percent }\n\\newlabel{understanding-liferay-faces-alloy}{{761}{2315}{Understanding Liferay Faces Alloy}{chapter.761}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {761.1}Related Topics}{2315}{section.761.1}\\protected@file@percent }\n\\newlabel{related-topics-46}{{761.1}{2315}{Related Topics}{section.761.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {762}Understanding Liferay Faces Portal}{2317}{chapter.762}\\protected@file@percent }\n\\newlabel{understanding-liferay-faces-portal}{{762}{2317}{Understanding Liferay Faces Portal}{chapter.762}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {762.1}{\\ignorespaces The required \\texttt  {.jar} files are downloaded for your JSF portlet based on the JSF UI Component Suite you configured.}}{2317}{figure.762.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {762.1}Related Topics}{2318}{section.762.1}\\protected@file@percent }\n\\newlabel{related-topics-47}{{762.1}{2318}{Related Topics}{section.762.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {763}Maven Plugins}{2319}{chapter.763}\\protected@file@percent }\n\\newlabel{maven-plugins}{{763}{2319}{Maven Plugins}{chapter.763}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {764}Bundle Support Plugin}{2321}{chapter.764}\\protected@file@percent }\n\\newlabel{bundle-support-plugin}{{764}{2321}{Bundle Support Plugin}{chapter.764}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {764.1}Usage}{2321}{section.764.1}\\protected@file@percent }\n\\newlabel{usage-30}{{764.1}{2321}{Usage}{section.764.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {764.2}Goals}{2322}{section.764.2}\\protected@file@percent }\n\\newlabel{goals}{{764.2}{2322}{Goals}{section.764.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {764.3}clean Goal's Available Parameters}{2322}{section.764.3}\\protected@file@percent }\n\\newlabel{clean-goals-available-parameters}{{764.3}{2322}{clean Goal's Available Parameters}{section.764.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {764.4}create-token Goal's Available Parameters}{2322}{section.764.4}\\protected@file@percent }\n\\newlabel{create-token-goals-available-parameters}{{764.4}{2322}{create-token Goal's Available Parameters}{section.764.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {764.5}deploy Goal's Available Parameters}{2323}{section.764.5}\\protected@file@percent }\n\\newlabel{deploy-goals-available-parameters}{{764.5}{2323}{deploy Goal's Available Parameters}{section.764.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {764.6}dist Goal's Available Parameters}{2323}{section.764.6}\\protected@file@percent }\n\\newlabel{dist-goals-available-parameters}{{764.6}{2323}{dist Goal's Available Parameters}{section.764.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {764.7}init Goal's Available Parameters}{2323}{section.764.7}\\protected@file@percent }\n\\newlabel{init-goals-available-parameters}{{764.7}{2323}{init Goal's Available Parameters}{section.764.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {765}CSS Builder Plugin}{2325}{chapter.765}\\protected@file@percent }\n\\newlabel{css-builder-plugin}{{765}{2325}{CSS Builder Plugin}{chapter.765}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {765.1}Usage}{2325}{section.765.1}\\protected@file@percent }\n\\newlabel{usage-31}{{765.1}{2325}{Usage}{section.765.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {765.2}Goals}{2325}{section.765.2}\\protected@file@percent }\n\\newlabel{goals-1}{{765.2}{2325}{Goals}{section.765.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {765.3}Available Parameters}{2326}{section.765.3}\\protected@file@percent }\n\\newlabel{available-parameters}{{765.3}{2326}{Available Parameters}{section.765.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {766}DB Support Plugin}{2327}{chapter.766}\\protected@file@percent }\n\\newlabel{db-support-plugin}{{766}{2327}{DB Support Plugin}{chapter.766}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {766.1}Usage}{2327}{section.766.1}\\protected@file@percent }\n\\newlabel{usage-32}{{766.1}{2327}{Usage}{section.766.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {766.2}Goals}{2327}{section.766.2}\\protected@file@percent }\n\\newlabel{goals-2}{{766.2}{2327}{Goals}{section.766.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {766.3}Available Parameters}{2328}{section.766.3}\\protected@file@percent }\n\\newlabel{available-parameters-1}{{766.3}{2328}{Available Parameters}{section.766.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {767}Deployment Helper Plugin}{2329}{chapter.767}\\protected@file@percent }\n\\newlabel{deployment-helper-plugin}{{767}{2329}{Deployment Helper Plugin}{chapter.767}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {767.1}Usage}{2329}{section.767.1}\\protected@file@percent }\n\\newlabel{usage-33}{{767.1}{2329}{Usage}{section.767.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {767.2}Goals}{2329}{section.767.2}\\protected@file@percent }\n\\newlabel{goals-3}{{767.2}{2329}{Goals}{section.767.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {767.3}Available Parameters}{2329}{section.767.3}\\protected@file@percent }\n\\newlabel{available-parameters-2}{{767.3}{2329}{Available Parameters}{section.767.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {768}Javadoc Formatter Plugin}{2331}{chapter.768}\\protected@file@percent }\n\\newlabel{javadoc-formatter-plugin}{{768}{2331}{Javadoc Formatter Plugin}{chapter.768}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {768.1}Usage}{2331}{section.768.1}\\protected@file@percent }\n\\newlabel{usage-34}{{768.1}{2331}{Usage}{section.768.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {768.2}Goals}{2331}{section.768.2}\\protected@file@percent }\n\\newlabel{goals-4}{{768.2}{2331}{Goals}{section.768.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {768.3}Available Parameters}{2332}{section.768.3}\\protected@file@percent }\n\\newlabel{available-parameters-3}{{768.3}{2332}{Available Parameters}{section.768.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {769}Lang Builder Plugin}{2333}{chapter.769}\\protected@file@percent }\n\\newlabel{lang-builder-plugin}{{769}{2333}{Lang Builder Plugin}{chapter.769}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {769.1}Usage}{2333}{section.769.1}\\protected@file@percent }\n\\newlabel{usage-35}{{769.1}{2333}{Usage}{section.769.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {769.2}Goals}{2333}{section.769.2}\\protected@file@percent }\n\\newlabel{goals-5}{{769.2}{2333}{Goals}{section.769.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {769.3}Available Parameters}{2333}{section.769.3}\\protected@file@percent }\n\\newlabel{available-parameters-4}{{769.3}{2333}{Available Parameters}{section.769.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {770}REST Builder Plugin}{2335}{chapter.770}\\protected@file@percent }\n\\newlabel{rest-builder-plugin}{{770}{2335}{REST Builder Plugin}{chapter.770}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {770.1}Usage}{2335}{section.770.1}\\protected@file@percent }\n\\newlabel{usage-36}{{770.1}{2335}{Usage}{section.770.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {770.2}Goals}{2335}{section.770.2}\\protected@file@percent }\n\\newlabel{goals-6}{{770.2}{2335}{Goals}{section.770.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {770.3}Available Parameters}{2335}{section.770.3}\\protected@file@percent }\n\\newlabel{available-parameters-5}{{770.3}{2335}{Available Parameters}{section.770.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {771}Service Builder Plugin}{2337}{chapter.771}\\protected@file@percent }\n\\newlabel{service-builder-plugin}{{771}{2337}{Service Builder Plugin}{chapter.771}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {771.1}Usage}{2337}{section.771.1}\\protected@file@percent }\n\\newlabel{usage-37}{{771.1}{2337}{Usage}{section.771.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {771.2}Goals}{2337}{section.771.2}\\protected@file@percent }\n\\newlabel{goals-7}{{771.2}{2337}{Goals}{section.771.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {771.3}Available Parameters}{2337}{section.771.3}\\protected@file@percent }\n\\newlabel{available-parameters-6}{{771.3}{2337}{Available Parameters}{section.771.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {772}Source Formatter Plugin}{2339}{chapter.772}\\protected@file@percent }\n\\newlabel{source-formatter-plugin}{{772}{2339}{Source Formatter Plugin}{chapter.772}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {772.1}Usage}{2339}{section.772.1}\\protected@file@percent }\n\\newlabel{usage-38}{{772.1}{2339}{Usage}{section.772.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {772.2}Goals}{2339}{section.772.2}\\protected@file@percent }\n\\newlabel{goals-8}{{772.2}{2339}{Goals}{section.772.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {772.3}Available Parameters}{2340}{section.772.3}\\protected@file@percent }\n\\newlabel{available-parameters-7}{{772.3}{2340}{Available Parameters}{section.772.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {773}Theme Builder Plugin}{2341}{chapter.773}\\protected@file@percent }\n\\newlabel{theme-builder-plugin}{{773}{2341}{Theme Builder Plugin}{chapter.773}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {773.1}Usage}{2341}{section.773.1}\\protected@file@percent }\n\\newlabel{usage-39}{{773.1}{2341}{Usage}{section.773.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {773.2}Goals}{2341}{section.773.2}\\protected@file@percent }\n\\newlabel{goals-9}{{773.2}{2341}{Goals}{section.773.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {773.3}Available Parameters}{2342}{section.773.3}\\protected@file@percent }\n\\newlabel{available-parameters-8}{{773.3}{2342}{Available Parameters}{section.773.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {774}TLD Formatter Plugin}{2343}{chapter.774}\\protected@file@percent }\n\\newlabel{tld-formatter-plugin}{{774}{2343}{TLD Formatter Plugin}{chapter.774}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {774.1}Usage}{2343}{section.774.1}\\protected@file@percent }\n\\newlabel{usage-40}{{774.1}{2343}{Usage}{section.774.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {774.2}Goals}{2343}{section.774.2}\\protected@file@percent }\n\\newlabel{goals-10}{{774.2}{2343}{Goals}{section.774.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {774.3}Available Parameters}{2343}{section.774.3}\\protected@file@percent }\n\\newlabel{available-parameters-9}{{774.3}{2343}{Available Parameters}{section.774.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {775}WSDD Builder Plugin}{2345}{chapter.775}\\protected@file@percent }\n\\newlabel{wsdd-builder-plugin}{{775}{2345}{WSDD Builder Plugin}{chapter.775}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {775.1}Usage}{2345}{section.775.1}\\protected@file@percent }\n\\newlabel{usage-41}{{775.1}{2345}{Usage}{section.775.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {775.2}Goals}{2345}{section.775.2}\\protected@file@percent }\n\\newlabel{goals-11}{{775.2}{2345}{Goals}{section.775.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {775.3}Available Parameters}{2345}{section.775.3}\\protected@file@percent }\n\\newlabel{available-parameters-10}{{775.3}{2345}{Available Parameters}{section.775.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {776}XML Formatter Plugin}{2347}{chapter.776}\\protected@file@percent }\n\\newlabel{xml-formatter-plugin}{{776}{2347}{XML Formatter Plugin}{chapter.776}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {776.1}Usage}{2347}{section.776.1}\\protected@file@percent }\n\\newlabel{usage-42}{{776.1}{2347}{Usage}{section.776.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {776.2}Goals}{2347}{section.776.2}\\protected@file@percent }\n\\newlabel{goals-12}{{776.2}{2347}{Goals}{section.776.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {776.3}Available Parameters}{2347}{section.776.3}\\protected@file@percent }\n\\newlabel{available-parameters-11}{{776.3}{2347}{Available Parameters}{section.776.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {777}PortletMVC4Spring}{2349}{chapter.777}\\protected@file@percent }\n\\newlabel{portletmvc4spring}{{777}{2349}{PortletMVC4Spring}{chapter.777}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {778}PortletMVC4Spring Project Anatomy}{2351}{chapter.778}\\protected@file@percent }\n\\newlabel{portletmvc4spring-project-anatomy}{{778}{2351}{PortletMVC4Spring Project Anatomy}{chapter.778}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {778.1}Maven Commands for Generating PortletMVC4Spring Projects}{2351}{section.778.1}\\protected@file@percent }\n\\newlabel{maven-commands-for-generating-portletmvc4spring-projects}{{778.1}{2351}{Maven Commands for Generating PortletMVC4Spring Projects}{section.778.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {778.2}SP/JSPX Form Portlet}{2351}{section.778.2}\\protected@file@percent }\n\\newlabel{spjspx-form-portlet}{{778.2}{2351}{SP/JSPX Form Portlet}{section.778.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {778.3}Thymeleaf Form Portlet}{2351}{section.778.3}\\protected@file@percent }\n\\newlabel{thymeleaf-form-portlet}{{778.3}{2351}{Thymeleaf Form Portlet}{section.778.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {778.4}Project Structure}{2352}{section.778.4}\\protected@file@percent }\n\\newlabel{project-structure}{{778.4}{2352}{Project Structure}{section.778.4}{}}\n\\gdef \\LT@lvii {\\LT@entry \n    {1}{270.08083pt}\\LT@entry \n    {1}{199.67418pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {779}PortletMVC4Spring Annotations}{2353}{chapter.779}\\protected@file@percent }\n\\newlabel{portletmvc4spring-annotations}{{779}{2353}{PortletMVC4Spring Annotations}{chapter.779}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {779.1}\\texttt  {@RenderMapping} Annotation Examples}{2353}{section.779.1}\\protected@file@percent }\n\\newlabel{rendermapping-annotation-examples}{{779.1}{2353}{\\texorpdfstring {\\texttt {@RenderMapping} Annotation Examples}{@RenderMapping Annotation Examples}}{section.779.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {779.2}\\texttt  {@ActionMapping} Annotation Examples}{2353}{section.779.2}\\protected@file@percent }\n\\newlabel{actionmapping-annotation-examples}{{779.2}{2353}{\\texorpdfstring {\\texttt {@ActionMapping} Annotation Examples}{@ActionMapping Annotation Examples}}{section.779.2}{}}\n\\gdef \\LT@lviii {\\LT@entry \n    {1}{270.08083pt}\\LT@entry \n    {1}{199.67418pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {780}PortletMVC4Spring Configuration Files}{2355}{chapter.780}\\protected@file@percent }\n\\newlabel{portletmvc4spring-configuration-files}{{780}{2355}{PortletMVC4Spring Configuration Files}{chapter.780}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {780.1}web.xml}{2355}{section.780.1}\\protected@file@percent }\n\\newlabel{web.xml}{{780.1}{2355}{web.xml}{section.780.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {780.2}portlet.xml}{2357}{section.780.2}\\protected@file@percent }\n\\newlabel{portlet.xml}{{780.2}{2357}{portlet.xml}{section.780.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {780.3}liferay-portlet.xml}{2358}{section.780.3}\\protected@file@percent }\n\\newlabel{liferay-portlet.xml}{{780.3}{2358}{liferay-portlet.xml}{section.780.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {780.4}liferay-display.xml}{2359}{section.780.4}\\protected@file@percent }\n\\newlabel{liferay-display.xml}{{780.4}{2359}{liferay-display.xml}{section.780.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {780.5}Portlet Application Context}{2359}{section.780.5}\\protected@file@percent }\n\\newlabel{portlet-application-context}{{780.5}{2359}{Portlet Application Context}{section.780.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {780.6}Portlet Contexts}{2360}{section.780.6}\\protected@file@percent }\n\\newlabel{portlet-contexts}{{780.6}{2360}{Portlet Contexts}{section.780.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {780.7}liferay-plugin-package.properties}{2361}{section.780.7}\\protected@file@percent }\n\\newlabel{liferay-plugin-package.properties-1}{{780.7}{2361}{liferay-plugin-package.properties}{section.780.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {780.8}Related Topics}{2361}{section.780.8}\\protected@file@percent }\n\\newlabel{related-topics-48}{{780.8}{2361}{Related Topics}{section.780.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {781}Project Templates}{2363}{chapter.781}\\protected@file@percent }\n\\newlabel{project-templates}{{781}{2363}{Project Templates}{chapter.781}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {782}Activator Template}{2365}{chapter.782}\\protected@file@percent }\n\\newlabel{activator-template}{{782}{2365}{Activator Template}{chapter.782}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {783}API Template}{2367}{chapter.783}\\protected@file@percent }\n\\newlabel{api-template}{{783}{2367}{API Template}{chapter.783}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {784}Control Menu Entry Template}{2369}{chapter.784}\\protected@file@percent }\n\\newlabel{control-menu-entry-template}{{784}{2369}{Control Menu Entry Template}{chapter.784}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {785}Form Field Template}{2371}{chapter.785}\\protected@file@percent }\n\\newlabel{form-field-template}{{785}{2371}{Form Field Template}{chapter.785}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {786}Fragment Template}{2373}{chapter.786}\\protected@file@percent }\n\\newlabel{fragment-template}{{786}{2373}{Fragment Template}{chapter.786}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {787}FreeMarker Portlet Template}{2375}{chapter.787}\\protected@file@percent }\n\\newlabel{freemarker-portlet-template}{{787}{2375}{FreeMarker Portlet Template}{chapter.787}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {788}Layout Template}{2377}{chapter.788}\\protected@file@percent }\n\\newlabel{layout-template}{{788}{2377}{Layout Template}{chapter.788}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {789}Modules Ext Template}{2379}{chapter.789}\\protected@file@percent }\n\\newlabel{modules-ext-template}{{789}{2379}{Modules Ext Template}{chapter.789}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {790}MVC Portlet Template}{2381}{chapter.790}\\protected@file@percent }\n\\newlabel{mvc-portlet-template}{{790}{2381}{MVC Portlet Template}{chapter.790}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {791}Panel App Template}{2383}{chapter.791}\\protected@file@percent }\n\\newlabel{panel-app-template}{{791}{2383}{Panel App Template}{chapter.791}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {792}Portlet Configuration Icon}{2385}{chapter.792}\\protected@file@percent }\n\\newlabel{portlet-configuration-icon}{{792}{2385}{Portlet Configuration Icon}{chapter.792}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {793}Portlet Provider Template}{2387}{chapter.793}\\protected@file@percent }\n\\newlabel{portlet-provider-template}{{793}{2387}{Portlet Provider Template}{chapter.793}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {794}Portlet Toolbar Contributor Template}{2389}{chapter.794}\\protected@file@percent }\n\\newlabel{portlet-toolbar-contributor-template}{{794}{2389}{Portlet Toolbar Contributor Template}{chapter.794}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {795}REST Template}{2391}{chapter.795}\\protected@file@percent }\n\\newlabel{rest-template}{{795}{2391}{REST Template}{chapter.795}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {796}Service Builder Template}{2393}{chapter.796}\\protected@file@percent }\n\\newlabel{service-builder-template}{{796}{2393}{Service Builder Template}{chapter.796}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {797}Service Template}{2395}{chapter.797}\\protected@file@percent }\n\\newlabel{service-template}{{797}{2395}{Service Template}{chapter.797}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {798}Service Wrapper Template}{2397}{chapter.798}\\protected@file@percent }\n\\newlabel{service-wrapper-template}{{798}{2397}{Service Wrapper Template}{chapter.798}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {799}Simulation Panel Entry Template}{2399}{chapter.799}\\protected@file@percent }\n\\newlabel{simulation-panel-entry-template}{{799}{2399}{Simulation Panel Entry Template}{chapter.799}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {800}Social Bookmark Template}{2401}{chapter.800}\\protected@file@percent }\n\\newlabel{social-bookmark-template}{{800}{2401}{Social Bookmark Template}{chapter.800}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {800.1}{\\ignorespaces Click the magnifying glass icon to search the current URL using Google Search.}}{2402}{figure.800.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {801}Spring MVC Portlet Template}{2403}{chapter.801}\\protected@file@percent }\n\\newlabel{spring-mvc-portlet-template}{{801}{2403}{Spring MVC Portlet Template}{chapter.801}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {802}Template Context Contributor Template}{2407}{chapter.802}\\protected@file@percent }\n\\newlabel{template-context-contributor-template}{{802}{2407}{Template Context Contributor Template}{chapter.802}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {803}Theme Contributor Template}{2409}{chapter.803}\\protected@file@percent }\n\\newlabel{theme-contributor-template}{{803}{2409}{Theme Contributor Template}{chapter.803}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {804}Theme Template}{2411}{chapter.804}\\protected@file@percent }\n\\newlabel{theme-template}{{804}{2411}{Theme Template}{chapter.804}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {805}WAR Core Ext}{2413}{chapter.805}\\protected@file@percent }\n\\newlabel{war-core-ext}{{805}{2413}{WAR Core Ext}{chapter.805}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {806}WAR Hook Template}{2417}{chapter.806}\\protected@file@percent }\n\\newlabel{war-hook-template}{{806}{2417}{WAR Hook Template}{chapter.806}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {807}WAR MVC Portlet Template}{2419}{chapter.807}\\protected@file@percent }\n\\newlabel{war-mvc-portlet-template}{{807}{2419}{WAR MVC Portlet Template}{chapter.807}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {808}Sample Projects}{2421}{chapter.808}\\protected@file@percent }\n\\newlabel{sample-projects}{{808}{2421}{Sample Projects}{chapter.808}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {809}Apps}{2423}{chapter.809}\\protected@file@percent }\n\\newlabel{apps}{{809}{2423}{Apps}{chapter.809}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {810}Service Builder Samples}{2425}{chapter.810}\\protected@file@percent }\n\\newlabel{service-builder-samples}{{810}{2425}{Service Builder Samples}{chapter.810}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {811}Service Builder Application Demonstrating Actionable Dynamic Query}{2427}{chapter.811}\\protected@file@percent }\n\\newlabel{service-builder-application-demonstrating-actionable-dynamic-query}{{811}{2427}{Service Builder Application Demonstrating Actionable Dynamic Query}{chapter.811}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {811.1}{\\ignorespaces This sample provides options to add entities and perform a mass update.}}{2427}{figure.811.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {811.1}What API(s) and/or code components does this sample highlight?}{2428}{section.811.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight}{{811.1}{2428}{What API(s) and/or code components does this sample highlight?}{section.811.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {811.2}How does this sample leverage the API(s) and/or code component?}{2428}{section.811.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component}{{811.2}{2428}{How does this sample leverage the API(s) and/or code component?}{section.811.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {812}Service Builder Application Using External Database via JDBC}{2429}{chapter.812}\\protected@file@percent }\n\\newlabel{service-builder-application-using-external-database-via-jdbc}{{812}{2429}{Service Builder Application Using External Database via JDBC}{chapter.812}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {812.1}{\\ignorespaces This sample prints out the values previously inputted into the database.}}{2430}{figure.812.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {812.1}What API(s) and/or code components does this sample highlight?}{2431}{section.812.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-1}{{812.1}{2431}{What API(s) and/or code components does this sample highlight?}{section.812.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {812.2}How does this sample leverage the API(s) and/or code component?}{2431}{section.812.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-1}{{812.2}{2431}{How does this sample leverage the API(s) and/or code component?}{section.812.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {812.3}Configuring the Data Source}{2431}{section.812.3}\\protected@file@percent }\n\\newlabel{configuring-the-data-source}{{812.3}{2431}{Configuring the Data Source}{section.812.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {812.4}Accessing Data}{2431}{section.812.4}\\protected@file@percent }\n\\newlabel{accessing-data}{{812.4}{2431}{Accessing Data}{section.812.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {813}Service Builder Application Using External Database via JNDI}{2433}{chapter.813}\\protected@file@percent }\n\\newlabel{service-builder-application-using-external-database-via-jndi}{{813}{2433}{Service Builder Application Using External Database via JNDI}{chapter.813}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {813.1}{\\ignorespaces This sample prints out the values previously inputted into the database.}}{2435}{figure.813.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {813.1}What API(s) and/or code components does this sample highlight?}{2436}{section.813.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-2}{{813.1}{2436}{What API(s) and/or code components does this sample highlight?}{section.813.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {813.2}How does this sample leverage the API(s) and/or code component?}{2436}{section.813.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-2}{{813.2}{2436}{How does this sample leverage the API(s) and/or code component?}{section.813.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {813.3}Additional Information}{2436}{section.813.3}\\protected@file@percent }\n\\newlabel{additional-information}{{813.3}{2436}{Additional Information}{section.813.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {814}Workflow Samples}{2437}{chapter.814}\\protected@file@percent }\n\\newlabel{workflow-samples}{{814}{2437}{Workflow Samples}{chapter.814}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {815}Workflow Asset Application}{2439}{chapter.815}\\protected@file@percent }\n\\newlabel{workflow-asset-application}{{815}{2439}{Workflow Asset Application}{chapter.815}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {815.1}What API(s) and/or code components does this sample highlight?}{2439}{section.815.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-3}{{815.1}{2439}{What API(s) and/or code components does this sample highlight?}{section.815.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {815.2}How does this sample leverage the API(s) and/or code component?}{2439}{section.815.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-3}{{815.2}{2439}{How does this sample leverage the API(s) and/or code component?}{section.815.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {816}Workflow Application}{2441}{chapter.816}\\protected@file@percent }\n\\newlabel{workflow-application}{{816}{2441}{Workflow Application}{chapter.816}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {816.1}What API(s) and/or code components does this sample highlight?}{2441}{section.816.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-4}{{816.1}{2441}{What API(s) and/or code components does this sample highlight?}{section.816.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {816.2}How does this sample leverage the API(s) and/or code component?}{2441}{section.816.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-4}{{816.2}{2441}{How does this sample leverage the API(s) and/or code component?}{section.816.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {817}Greedy Policy Option Application}{2443}{chapter.817}\\protected@file@percent }\n\\newlabel{greedy-policy-option-application}{{817}{2443}{Greedy Policy Option Application}{chapter.817}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {817.1}{\\ignorespaces The Greedy Policy Option app provides two portlets that only print text. You'll dive deeper later to discover their interesting capabilities involving services.}}{2443}{figure.817.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {817.1}What API(s) and/or code components does this sample highlight?}{2443}{section.817.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-5}{{817.1}{2443}{What API(s) and/or code components does this sample highlight?}{section.817.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {817.2}How does this sample leverage the API(s) and/or code component?}{2444}{section.817.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-5}{{817.2}{2444}{How does this sample leverage the API(s) and/or code component?}{section.817.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {817.3}Binding a newly deployed component's service reference to the highest ranking service instance that's available initially}{2445}{section.817.3}\\protected@file@percent }\n\\newlabel{binding-a-newly-deployed-components-service-reference-to-the-highest-ranking-service-instance-thats-available-initially}{{817.3}{2445}{Binding a newly deployed component's service reference to the highest ranking service instance that's available initially}{section.817.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {817.2}{\\ignorespaces \\emph  {Reluctant Portlet} displays the message ``SomeService says I am Default!''}}{2446}{figure.817.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {817.3}{\\ignorespaces \\emph  {Greedy Portlet} displays the message ``SomeService says I am better, use me!''}}{2447}{figure.817.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {817.4}Deploying a module with a higher ranked service instance for binding to greedy references immediately}{2447}{section.817.4}\\protected@file@percent }\n\\newlabel{deploying-a-module-with-a-higher-ranked-service-instance-for-binding-to-greedy-references-immediately}{{817.4}{2447}{Deploying a module with a higher ranked service instance for binding to greedy references immediately}{section.817.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {817.4}{\\ignorespaces The \\emph  {Greedy Portlet} is using a \\texttt  {HigherRankedService} instance}}{2447}{figure.817.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {817.5}Configuring a component to reference a different service instance dynamically}{2447}{section.817.5}\\protected@file@percent }\n\\newlabel{configuring-a-component-to-reference-a-different-service-instance-dynamically}{{817.5}{2447}{Configuring a component to reference a different service instance dynamically}{section.817.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {817.5}{\\ignorespaces \\emph  {Reluctant Portlet} is using the \\texttt  {HigherRankedService} instance instead of a \\texttt  {DefaultService} instance.}}{2448}{figure.817.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {817.6}Where Is This Sample?}{2448}{section.817.6}\\protected@file@percent }\n\\newlabel{where-is-this-sample}{{817.6}{2448}{Where Is This Sample?}{section.817.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {818}Kotlin Portlet}{2449}{chapter.818}\\protected@file@percent }\n\\newlabel{kotlin-portlet}{{818}{2449}{Kotlin Portlet}{chapter.818}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {818.1}{\\ignorespaces After saving the inputted name, it's displayed as a greeting on the portlet page.}}{2449}{figure.818.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {818.1}What API(s) and/or code components does this sample highlight?}{2449}{section.818.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-6}{{818.1}{2449}{What API(s) and/or code components does this sample highlight?}{section.818.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {818.2}How does this sample leverage the API(s) and/or code component?}{2449}{section.818.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-6}{{818.2}{2449}{How does this sample leverage the API(s) and/or code component?}{section.818.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {818.3}Where Is This Sample?}{2450}{section.818.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-1}{{818.3}{2450}{Where Is This Sample?}{section.818.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {819}Shared Language Keys}{2451}{chapter.819}\\protected@file@percent }\n\\newlabel{shared-language-keys}{{819}{2451}{Shared Language Keys}{chapter.819}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {819.1}{\\ignorespaces The sample JSP portlet displays three language keys.}}{2451}{figure.819.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {819.1}What API(s) and/or code components does this sample highlight?}{2451}{section.819.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-7}{{819.1}{2451}{What API(s) and/or code components does this sample highlight?}{section.819.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {819.2}How does this sample leverage the API(s) and/or code component?}{2452}{section.819.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-7}{{819.2}{2452}{How does this sample leverage the API(s) and/or code component?}{section.819.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {819.2}{\\ignorespaces The Language Web portlet displays three phrases, two of which are shared from a different module.}}{2452}{figure.819.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {819.3}Where Is This Sample?}{2453}{section.819.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-2}{{819.3}{2453}{Where Is This Sample?}{section.819.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {820}Simulation Panel App}{2455}{chapter.820}\\protected@file@percent }\n\\newlabel{simulation-panel-app}{{820}{2455}{Simulation Panel App}{chapter.820}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {820.1}What API(s) and/or code components does this sample highlight?}{2455}{section.820.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-8}{{820.1}{2455}{What API(s) and/or code components does this sample highlight?}{section.820.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {820.2}How does this sample leverage the API(s) and/or code component?}{2455}{section.820.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-8}{{820.2}{2455}{How does this sample leverage the API(s) and/or code component?}{section.820.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {820.3}Where Is This Sample?}{2456}{section.820.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-3}{{820.3}{2456}{Where Is This Sample?}{section.820.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {821}Extensions}{2457}{chapter.821}\\protected@file@percent }\n\\newlabel{extensions}{{821}{2457}{Extensions}{chapter.821}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {822}Control Menu Entry}{2459}{chapter.822}\\protected@file@percent }\n\\newlabel{control-menu-entry}{{822}{2459}{Control Menu Entry}{chapter.822}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {822.1}{\\ignorespaces The User area of the Control Menu is provided an additional link button when the Control Menu Entry sample is deployed to Liferay DXP.}}{2459}{figure.822.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {822.1}What API(s) and/or code components does this sample highlight?}{2459}{section.822.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-9}{{822.1}{2459}{What API(s) and/or code components does this sample highlight?}{section.822.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {822.2}How does this sample leverage the API(s) and/or code component?}{2459}{section.822.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-9}{{822.2}{2459}{How does this sample leverage the API(s) and/or code component?}{section.822.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {822.3}Where Is This Sample?}{2460}{section.822.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-4}{{822.3}{2460}{Where Is This Sample?}{section.822.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {823}Document Action}{2461}{chapter.823}\\protected@file@percent }\n\\newlabel{document-action}{{823}{2461}{Document Action}{chapter.823}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {823.1}What API(s) and/or code components does this sample highlight?}{2461}{section.823.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-10}{{823.1}{2461}{What API(s) and/or code components does this sample highlight?}{section.823.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {823.2}How does this sample leverage the API(s) and/or code component?}{2461}{section.823.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-10}{{823.2}{2461}{How does this sample leverage the API(s) and/or code component?}{section.823.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {823.1}{\\ignorespaces The new \\emph  {Blade Basic Info} option is available from the entry's Options menu.}}{2462}{figure.823.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {823.3}Where Is This Sample?}{2463}{section.823.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-5}{{823.3}{2463}{Where Is This Sample?}{section.823.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {824}Gogo Shell Command}{2465}{chapter.824}\\protected@file@percent }\n\\newlabel{gogo-shell-command}{{824}{2465}{Gogo Shell Command}{chapter.824}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {824.1}What API(s) and/or code components does this sample highlight?}{2465}{section.824.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-11}{{824.1}{2465}{What API(s) and/or code components does this sample highlight?}{section.824.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {824.2}How does this sample leverage the API(s) and/or code component?}{2465}{section.824.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-11}{{824.2}{2465}{How does this sample leverage the API(s) and/or code component?}{section.824.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {824.1}{\\ignorespaces The sample Gogo shell command is listed with all the available commands.}}{2466}{figure.824.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {824.2}{\\ignorespaces The outcome of executing the \\texttt  {usercount} command.}}{2466}{figure.824.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {824.3}Where Is This Sample?}{2467}{section.824.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-6}{{824.3}{2467}{Where Is This Sample?}{section.824.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {825}Index Settings Contributor}{2469}{chapter.825}\\protected@file@percent }\n\\newlabel{index-settings-contributor}{{825}{2469}{Index Settings Contributor}{chapter.825}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {825.1}What API(s) and/or code components does this sample highlight?}{2469}{section.825.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-12}{{825.1}{2469}{What API(s) and/or code components does this sample highlight?}{section.825.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {825.1}{\\ignorespaces This sample added four new index properties.}}{2470}{figure.825.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {825.2}How does this sample leverage the API(s) and/or code component?}{2470}{section.825.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-12}{{825.2}{2470}{How does this sample leverage the API(s) and/or code component?}{section.825.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {825.3}Where Is This Sample?}{2471}{section.825.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-7}{{825.3}{2471}{Where Is This Sample?}{section.825.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {826}Indexer Post Processor}{2473}{chapter.826}\\protected@file@percent }\n\\newlabel{indexer-post-processor}{{826}{2473}{Indexer Post Processor}{chapter.826}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {826.1}What API(s) and/or code components does this sample highlight?}{2473}{section.826.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-13}{{826.1}{2473}{What API(s) and/or code components does this sample highlight?}{section.826.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {826.2}How does this sample leverage the API(s) and/or code component?}{2473}{section.826.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-13}{{826.2}{2473}{How does this sample leverage the API(s) and/or code component?}{section.826.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {826.3}Where Is This Sample?}{2474}{section.826.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-8}{{826.3}{2474}{Where Is This Sample?}{section.826.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {827}Model Listener}{2475}{chapter.827}\\protected@file@percent }\n\\newlabel{model-listener}{{827}{2475}{Model Listener}{chapter.827}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {827.1}{\\ignorespaces The sample model listener's message in the console.}}{2475}{figure.827.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {827.1}What API(s) and/or code components does this sample highlight?}{2475}{section.827.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-14}{{827.1}{2475}{What API(s) and/or code components does this sample highlight?}{section.827.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {827.2}How does this sample leverage the API(s) and/or code component?}{2475}{section.827.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-14}{{827.2}{2475}{How does this sample leverage the API(s) and/or code component?}{section.827.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {827.2}{\\ignorespaces The page's HTML title updated by the model listener sample.}}{2476}{figure.827.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {827.3}Where Is This Sample?}{2477}{section.827.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-9}{{827.3}{2477}{Where Is This Sample?}{section.827.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {828}Screen Name Validator}{2479}{chapter.828}\\protected@file@percent }\n\\newlabel{screen-name-validator}{{828}{2479}{Screen Name Validator}{chapter.828}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {828.1}{\\ignorespaces Enter reserved words for the screen name validator.}}{2479}{figure.828.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {828.2}{\\ignorespaces The error message displays when inputting a reserved word for the screen name.}}{2480}{figure.828.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {828.1}What API(s) and/or code components does this sample highlight?}{2480}{section.828.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-15}{{828.1}{2480}{What API(s) and/or code components does this sample highlight?}{section.828.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {828.2}How does this sample leverage the API(s) and/or code component?}{2480}{section.828.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-15}{{828.2}{2480}{How does this sample leverage the API(s) and/or code component?}{section.828.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {828.3}Where Is This Sample?}{2480}{section.828.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-10}{{828.3}{2480}{Where Is This Sample?}{section.828.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {829}Servlet}{2481}{chapter.829}\\protected@file@percent }\n\\newlabel{servlet}{{829}{2481}{Servlet}{chapter.829}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {829.1}{\\ignorespaces The servlet displays \\emph  {Hello World} from the configured servlet page URL.}}{2481}{figure.829.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {829.2}{\\ignorespaces The servlet also logs info in the console.}}{2481}{figure.829.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {829.1}What API(s) and/or code components does this sample highlight?}{2482}{section.829.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-16}{{829.1}{2482}{What API(s) and/or code components does this sample highlight?}{section.829.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {829.2}How does this sample leverage the API(s) and/or code component?}{2482}{section.829.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-16}{{829.2}{2482}{How does this sample leverage the API(s) and/or code component?}{section.829.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {829.3}Where Is This Sample?}{2482}{section.829.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-11}{{829.3}{2482}{Where Is This Sample?}{section.829.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {830}Overrides}{2483}{chapter.830}\\protected@file@percent }\n\\newlabel{overrides}{{830}{2483}{Overrides}{chapter.830}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {831}Module JSP Override}{2485}{chapter.831}\\protected@file@percent }\n\\newlabel{module-jsp-override}{{831}{2485}{Module JSP Override}{chapter.831}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {831.1}{\\ignorespaces The customized Sign In form with the new \\emph  {changed} text.}}{2485}{figure.831.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {831.1}What API(s) and/or code components does this sample highlight?}{2486}{section.831.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-17}{{831.1}{2486}{What API(s) and/or code components does this sample highlight?}{section.831.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {831.2}How does this sample leverage the API(s) and/or code component?}{2486}{section.831.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-17}{{831.2}{2486}{How does this sample leverage the API(s) and/or code component?}{section.831.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {831.3}Where Is This Sample?}{2486}{section.831.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-12}{{831.3}{2486}{Where Is This Sample?}{section.831.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {832}Resource Bundle Override}{2487}{chapter.832}\\protected@file@percent }\n\\newlabel{resource-bundle-override}{{832}{2487}{Resource Bundle Override}{chapter.832}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {832.1}{\\ignorespaces The customized Login portlet displays the new language key.}}{2487}{figure.832.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {832.1}What API(s) and/or code components does this sample highlight?}{2487}{section.832.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-18}{{832.1}{2487}{What API(s) and/or code components does this sample highlight?}{section.832.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {832.2}How does this sample leverage the API(s) and/or code component?}{2487}{section.832.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-18}{{832.2}{2487}{How does this sample leverage the API(s) and/or code component?}{section.832.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {832.3}Where Is This Sample?}{2488}{section.832.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-13}{{832.3}{2488}{Where Is This Sample?}{section.832.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {833}Themes}{2489}{chapter.833}\\protected@file@percent }\n\\newlabel{themes}{{833}{2489}{Themes}{chapter.833}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {834}Simple Theme}{2491}{chapter.834}\\protected@file@percent }\n\\newlabel{simple-theme}{{834}{2491}{Simple Theme}{chapter.834}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {834.1}{\\ignorespaces A theme based off of the Styled base theme is created when the Theme Blade sample is deployed to Liferay Portal.}}{2491}{figure.834.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {834.1}What API(s) and/or code components does this sample highlight?}{2491}{section.834.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-19}{{834.1}{2491}{What API(s) and/or code components does this sample highlight?}{section.834.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {834.2}How does this sample leverage the API(s) and/or code component?}{2492}{section.834.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-19}{{834.2}{2492}{How does this sample leverage the API(s) and/or code component?}{section.834.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {834.3}Where Is This Sample?}{2492}{section.834.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-14}{{834.3}{2492}{Where Is This Sample?}{section.834.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {835}Template Context Contributor}{2493}{chapter.835}\\protected@file@percent }\n\\newlabel{template-context-contributor}{{835}{2493}{Template Context Contributor}{chapter.835}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {835.1}What API(s) and/or code components does this sample highlight?}{2493}{section.835.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-20}{{835.1}{2493}{What API(s) and/or code components does this sample highlight?}{section.835.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {835.2}How does this sample leverage the API(s) and/or code component?}{2493}{section.835.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-20}{{835.2}{2493}{How does this sample leverage the API(s) and/or code component?}{section.835.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {835.3}Where Is This Sample?}{2494}{section.835.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-15}{{835.3}{2494}{Where Is This Sample?}{section.835.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {836}Theme Contributor}{2495}{chapter.836}\\protected@file@percent }\n\\newlabel{theme-contributor}{{836}{2495}{Theme Contributor}{chapter.836}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {836.1}{\\ignorespaces Your Liferay DXP pages and menu fonts now have a yellow tint.}}{2495}{figure.836.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {836.2}{\\ignorespaces The message is printed to your browser's console window using JavaScript.}}{2495}{figure.836.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {836.1}What API(s) and/or code components does this sample highlight?}{2496}{section.836.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-21}{{836.1}{2496}{What API(s) and/or code components does this sample highlight?}{section.836.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {836.2}How does this sample leverage the API(s) and/or code component?}{2496}{section.836.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-21}{{836.2}{2496}{How does this sample leverage the API(s) and/or code component?}{section.836.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {836.3}Where Is This Sample?}{2496}{section.836.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-16}{{836.3}{2496}{Where Is This Sample?}{section.836.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {837}Ext}{2497}{chapter.837}\\protected@file@percent }\n\\newlabel{ext}{{837}{2497}{Ext}{chapter.837}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {838}Login Web Ext}{2499}{chapter.838}\\protected@file@percent }\n\\newlabel{login-web-ext}{{838}{2499}{Login Web Ext}{chapter.838}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {838.1}{\\ignorespaces The Login Ext module customizes the original Login module.}}{2499}{figure.838.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {838.1}What API(s) and/or code components does this sample highlight?}{2500}{section.838.1}\\protected@file@percent }\n\\newlabel{what-apis-andor-code-components-does-this-sample-highlight-22}{{838.1}{2500}{What API(s) and/or code components does this sample highlight?}{section.838.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {838.2}How does this sample leverage the API(s) and/or code component?}{2500}{section.838.2}\\protected@file@percent }\n\\newlabel{how-does-this-sample-leverage-the-apis-andor-code-component-22}{{838.2}{2500}{How does this sample leverage the API(s) and/or code component?}{section.838.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {838.3}Where Is This Sample?}{2501}{section.838.3}\\protected@file@percent }\n\\newlabel{where-is-this-sample-17}{{838.3}{2501}{Where Is This Sample?}{section.838.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {839}Segmentation and Personalization Reference}{2503}{chapter.839}\\protected@file@percent }\n\\newlabel{segmentation-and-personalization-reference}{{839}{2503}{Segmentation and Personalization Reference}{chapter.839}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {840}Defining Segmentation Criteria}{2505}{chapter.840}\\protected@file@percent }\n\\newlabel{defining-segmentation-criteria}{{840}{2505}{Defining Segmentation Criteria}{chapter.840}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {840.1}User Properties}{2506}{section.840.1}\\protected@file@percent }\n\\newlabel{user-properties}{{840.1}{2506}{User Properties}{section.840.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {840.2}Organization Properties}{2506}{section.840.2}\\protected@file@percent }\n\\newlabel{organization-properties}{{840.2}{2506}{Organization Properties}{section.840.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {840.3}Session Properties}{2506}{section.840.3}\\protected@file@percent }\n\\newlabel{session-properties}{{840.3}{2506}{Session Properties}{section.840.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {841}Tooling}{2507}{chapter.841}\\protected@file@percent }\n\\newlabel{tooling}{{841}{2507}{Tooling}{chapter.841}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {842}Creating a Project}{2509}{chapter.842}\\protected@file@percent }\n\\newlabel{creating-a-project}{{842}{2509}{Creating a Project}{chapter.842}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {842.1}Blade CLI}{2509}{section.842.1}\\protected@file@percent }\n\\newlabel{blade-cli}{{842.1}{2509}{Blade CLI}{section.842.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {842.2}Liferay Dev Studio}{2510}{section.842.2}\\protected@file@percent }\n\\newlabel{liferay-dev-studio}{{842.2}{2510}{Liferay Dev Studio}{section.842.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {842.1}{\\ignorespaces The New Liferay Module Project wizard offers project templates for JAR and WAR-based projects.}}{2510}{figure.842.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {842.2}{\\ignorespaces Specify your component class's details in the Portlet Component Class Wizard.}}{2511}{figure.842.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {842.3}Liferay IntelliJ Plugin}{2511}{section.842.3}\\protected@file@percent }\n\\newlabel{liferay-intellij-plugin}{{842.3}{2511}{Liferay IntelliJ Plugin}{section.842.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {842.3}{\\ignorespaces Selecting \\emph  {Liferay Module} opens the New Liferay Modules wizard.}}{2511}{figure.842.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {842.4}{\\ignorespaces Choose the project template to create your module.}}{2512}{figure.842.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {842.4}Maven}{2512}{section.842.4}\\protected@file@percent }\n\\newlabel{maven}{{842.4}{2512}{Maven}{section.842.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {843}Deploying a Project}{2513}{chapter.843}\\protected@file@percent }\n\\newlabel{deploying-a-project}{{843}{2513}{Deploying a Project}{chapter.843}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {843.1}Blade CLI}{2513}{section.843.1}\\protected@file@percent }\n\\newlabel{blade-cli-1}{{843.1}{2513}{Blade CLI}{section.843.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {843.2}Gradle}{2514}{section.843.2}\\protected@file@percent }\n\\newlabel{gradle}{{843.2}{2514}{Gradle}{section.843.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {843.3}Liferay Dev Studio}{2514}{section.843.3}\\protected@file@percent }\n\\newlabel{liferay-dev-studio-1}{{843.3}{2514}{Liferay Dev Studio}{section.843.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {843.4}Liferay IntelliJ Plugin}{2514}{section.843.4}\\protected@file@percent }\n\\newlabel{liferay-intellij-plugin-1}{{843.4}{2514}{Liferay IntelliJ Plugin}{section.843.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {843.1}{\\ignorespaces Using the this deployment method is convenient when deploying multiple projects.}}{2515}{figure.843.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {843.2}{\\ignorespaces Verify that your project built successfully.}}{2515}{figure.843.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {843.5}Maven}{2516}{section.843.5}\\protected@file@percent }\n\\newlabel{maven-1}{{843.5}{2516}{Maven}{section.843.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {844}Blade CLI}{2517}{chapter.844}\\protected@file@percent }\n\\newlabel{blade-cli-2}{{844}{2517}{Blade CLI}{chapter.844}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {845}Installing Blade CLI}{2519}{chapter.845}\\protected@file@percent }\n\\newlabel{installing-blade-cli}{{845}{2519}{Installing Blade CLI}{chapter.845}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {845.1}{\\ignorespaces Determine where your Liferay Workspace should reside, if you want one.}}{2520}{figure.845.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {845.2}{\\ignorespaces Select the product version you'll use with your Liferay Workspace.}}{2520}{figure.845.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {846}Installing Blade CLI with Proxy Requirements}{2521}{chapter.846}\\protected@file@percent }\n\\newlabel{installing-blade-cli-with-proxy-requirements}{{846}{2521}{Installing Blade CLI with Proxy Requirements}{chapter.846}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {847}Managing Your Liferay Server with Blade CLI}{2523}{chapter.847}\\protected@file@percent }\n\\newlabel{managing-your-liferay-server-with-blade-cli}{{847}{2523}{Managing Your Liferay Server with Blade CLI}{chapter.847}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {847.1}{\\ignorespaces Blade CLI accesses the Gogo shell script to run the \\texttt  {lb} command.}}{2524}{figure.847.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {848}Generating Project Samples with Blade CLI}{2527}{chapter.848}\\protected@file@percent }\n\\newlabel{generating-project-samples-with-blade-cli}{{848}{2527}{Generating Project Samples with Blade CLI}{chapter.848}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {849}Updating Blade CLI}{2529}{chapter.849}\\protected@file@percent }\n\\newlabel{updating-blade-cli}{{849}{2529}{Updating Blade CLI}{chapter.849}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {850}Converting Plugins SDK Projects with Blade CLI}{2531}{chapter.850}\\protected@file@percent }\n\\newlabel{converting-plugins-sdk-projects-with-blade-cli}{{850}{2531}{Converting Plugins SDK Projects with Blade CLI}{chapter.850}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {851}Extending Blade CLI}{2533}{chapter.851}\\protected@file@percent }\n\\newlabel{extending-blade-cli}{{851}{2533}{Extending Blade CLI}{chapter.851}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {852}Creating Custom Commands for Blade CLI}{2535}{chapter.852}\\protected@file@percent }\n\\newlabel{creating-custom-commands-for-blade-cli}{{852}{2535}{Creating Custom Commands for Blade CLI}{chapter.852}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {853}Creating Custom Project Templates for Blade CLI}{2537}{chapter.853}\\protected@file@percent }\n\\newlabel{creating-custom-project-templates-for-blade-cli}{{853}{2537}{Creating Custom Project Templates for Blade CLI}{chapter.853}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {854}Installing New Extensions for Blade CLI}{2539}{chapter.854}\\protected@file@percent }\n\\newlabel{installing-new-extensions-for-blade-cli}{{854}{2539}{Installing New Extensions for Blade CLI}{chapter.854}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {854.1}Installing a New Extension}{2539}{section.854.1}\\protected@file@percent }\n\\newlabel{installing-a-new-extension}{{854.1}{2539}{Installing a New Extension}{section.854.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {854.2}Uninstalling an Extension}{2539}{section.854.2}\\protected@file@percent }\n\\newlabel{uninstalling-an-extension}{{854.2}{2539}{Uninstalling an Extension}{section.854.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {855}Creating a Blade Profile}{2541}{chapter.855}\\protected@file@percent }\n\\newlabel{creating-a-blade-profile}{{855}{2541}{Creating a Blade Profile}{chapter.855}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {855.1}Creating a New Profile}{2541}{section.855.1}\\protected@file@percent }\n\\newlabel{creating-a-new-profile}{{855.1}{2541}{Creating a New Profile}{section.855.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {855.2}Setting a Profile}{2542}{section.855.2}\\protected@file@percent }\n\\newlabel{setting-a-profile}{{855.2}{2542}{Setting a Profile}{section.855.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {856}Common Errors with Blade CLI}{2545}{chapter.856}\\protected@file@percent }\n\\newlabel{common-errors-with-blade-cli}{{856}{2545}{Common Errors with Blade CLI}{chapter.856}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {856.1}The blade command is not available in my CLI}{2545}{section.856.1}\\protected@file@percent }\n\\newlabel{the-blade-command-is-not-available-in-my-cli}{{856.1}{2545}{The blade command is not available in my CLI}{section.856.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {856.2}I can't update my Blade CLI version}{2546}{section.856.2}\\protected@file@percent }\n\\newlabel{i-cant-update-my-blade-cli-version}{{856.2}{2546}{I can't update my Blade CLI version}{section.856.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {857}Liferay Dev Studio}{2547}{chapter.857}\\protected@file@percent }\n\\newlabel{liferay-dev-studio-2}{{857}{2547}{Liferay Dev Studio}{chapter.857}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {858}Installing Liferay Dev Studio}{2549}{chapter.858}\\protected@file@percent }\n\\newlabel{installing-liferay-dev-studio}{{858}{2549}{Installing Liferay Dev Studio}{chapter.858}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {858.1}Install the Dev Studio Bundle}{2549}{section.858.1}\\protected@file@percent }\n\\newlabel{install-the-dev-studio-bundle}{{858.1}{2549}{Install the Dev Studio Bundle}{section.858.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {858.2}Install Dev Studio into Eclipse}{2550}{section.858.2}\\protected@file@percent }\n\\newlabel{install-dev-studio-into-eclipse}{{858.2}{2550}{Install Dev Studio into Eclipse}{section.858.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {858.3}Install Dev Studio into Eclipse from a ZIP File}{2550}{section.858.3}\\protected@file@percent }\n\\newlabel{install-dev-studio-into-eclipse-from-a-zip-file}{{858.3}{2550}{Install Dev Studio into Eclipse from a ZIP File}{section.858.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {859}Setting Proxy Requirements for Dev Studio}{2551}{chapter.859}\\protected@file@percent }\n\\newlabel{setting-proxy-requirements-for-dev-studio}{{859}{2551}{Setting Proxy Requirements for Dev Studio}{chapter.859}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {859.1}Additional Proxy Settings}{2551}{section.859.1}\\protected@file@percent }\n\\newlabel{additional-proxy-settings}{{859.1}{2551}{Additional Proxy Settings}{section.859.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {859.1}{\\ignorespaces You can configure your proxy settings in Dev Studio's Network Connections menu.}}{2552}{figure.859.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {860}Installing a Liferay Server in Dev Studio}{2553}{chapter.860}\\protected@file@percent }\n\\newlabel{installing-a-liferay-server-in-dev-studio}{{860}{2553}{Installing a Liferay Server in Dev Studio}{chapter.860}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {860.1}{\\ignorespaces Choose the type of server you want to create.}}{2554}{figure.860.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {860.2}{\\ignorespaces Specify the installation folder of the bundle.}}{2555}{figure.860.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {860.3}{\\ignorespaces Your new server appears under the \\emph  {Servers} view.}}{2555}{figure.860.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {861}Importing Projects in Dev Studio}{2557}{chapter.861}\\protected@file@percent }\n\\newlabel{importing-projects-in-dev-studio}{{861}{2557}{Importing Projects in Dev Studio}{chapter.861}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {861.1}{\\ignorespaces You can import a single project or folder of projects.}}{2557}{figure.861.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {862}Using the Gogo Shell in Dev Studio}{2559}{chapter.862}\\protected@file@percent }\n\\newlabel{using-the-gogo-shell-in-dev-studio}{{862}{2559}{Using the Gogo Shell in Dev Studio}{chapter.862}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {862.1}{\\ignorespaces Select \\emph  {Open Gogo Shell} to open a terminal window in Dev Studio using Gogo shell.}}{2559}{figure.862.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {862.2}{\\ignorespaces You can check to see if your project deployed successfully to Liferay using the Gogo shell.}}{2560}{figure.862.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {863}Searching Liferay DXP Source in Dev Studio}{2561}{chapter.863}\\protected@file@percent }\n\\newlabel{searching-liferay-dxp-source-in-dev-studio}{{863}{2561}{Searching Liferay DXP Source in Dev Studio}{chapter.863}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {863.1}Search Class Hierarchy}{2561}{section.863.1}\\protected@file@percent }\n\\newlabel{search-class-hierarchy}{{863.1}{2561}{Search Class Hierarchy}{section.863.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {863.1}{\\ignorespaces Browse the Type Hierarchy window and open the provided classes for examples on how to extend a class.}}{2562}{figure.863.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {863.2}Search Method Declarations}{2562}{section.863.2}\\protected@file@percent }\n\\newlabel{search-method-declarations}{{863.2}{2562}{Search Method Declarations}{section.863.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {863.2}{\\ignorespaces All declarations of the method are returned in the Search window.}}{2563}{figure.863.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {863.3}Search Annotation References}{2563}{section.863.3}\\protected@file@percent }\n\\newlabel{search-annotation-references}{{863.3}{2563}{Search Annotation References}{section.863.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {863.3}{\\ignorespaces All matching annotations are displayed in the Search window.}}{2564}{figure.863.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {864}Debugging Liferay DXP Source in Dev Studio}{2565}{chapter.864}\\protected@file@percent }\n\\newlabel{debugging-liferay-dxp-source-in-dev-studio}{{864}{2565}{Debugging Liferay DXP Source in Dev Studio}{chapter.864}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {864.1}Configure Your Target Platform}{2565}{section.864.1}\\protected@file@percent }\n\\newlabel{configure-your-target-platform}{{864.1}{2565}{Configure Your Target Platform}{section.864.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {864.2}Configure a Liferay Server and Start It in Debug Mode}{2566}{section.864.2}\\protected@file@percent }\n\\newlabel{configure-a-liferay-server-and-start-it-in-debug-mode}{{864.2}{2566}{Configure a Liferay Server and Start It in Debug Mode}{section.864.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {864.1}{\\ignorespaces The red box in this screenshot highlights the debug button. Click this button to start the server in debug mode.}}{2566}{figure.864.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {865}Updating Liferay Dev Studio}{2567}{chapter.865}\\protected@file@percent }\n\\newlabel{updating-liferay-dev-studio}{{865}{2567}{Updating Liferay Dev Studio}{chapter.865}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {865.1}{\\ignorespaces Make sure to check all the Dev Studio components you wish to install.}}{2567}{figure.865.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {866}Gradle in Dev Studio}{2569}{chapter.866}\\protected@file@percent }\n\\newlabel{gradle-in-dev-studio}{{866}{2569}{Gradle in Dev Studio}{chapter.866}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {866.1}{\\ignorespaces Navigate to \\emph  {Help} → \\emph  {Installation Details} to view plugins included in Dev Studio.}}{2569}{figure.866.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {866.1}Creating Pure Gradle Projects}{2570}{section.866.1}\\protected@file@percent }\n\\newlabel{creating-pure-gradle-projects}{{866.1}{2570}{Creating Pure Gradle Projects}{section.866.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {866.2}Importing Pure Gradle Projects}{2570}{section.866.2}\\protected@file@percent }\n\\newlabel{importing-pure-gradle-projects}{{866.2}{2570}{Importing Pure Gradle Projects}{section.866.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {866.3}Gradle Tasks and Executions}{2570}{section.866.3}\\protected@file@percent }\n\\newlabel{gradle-tasks-and-executions}{{866.3}{2570}{Gradle Tasks and Executions}{section.866.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {866.2}{\\ignorespaces You can specify your Gradle distribution and advanced options such as home directories, JVM options, and program arguments.}}{2571}{figure.866.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {866.3}{\\ignorespaces You can specify what Gradle project to import from the \\emph  {Import Gradle Project} wizard.}}{2572}{figure.866.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {866.4}{\\ignorespaces Navigate into your preferred Gradle project to view its available Gradle tasks.}}{2572}{figure.866.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {866.5}{\\ignorespaces The Gradle Executions view helps you visualize the Gradle build process.}}{2573}{figure.866.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {866.6}{\\ignorespaces Make sure to always refresh your Gradle project in Dev Studio after build script edits.}}{2573}{figure.866.6}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {867}Maven in Dev Studio}{2575}{chapter.867}\\protected@file@percent }\n\\newlabel{maven-in-dev-studio}{{867}{2575}{Maven in Dev Studio}{chapter.867}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {867.1}Installing Maven Plugins for Dev Studio}{2575}{section.867.1}\\protected@file@percent }\n\\newlabel{installing-maven-plugins-for-dev-studio}{{867.1}{2575}{Installing Maven Plugins for Dev Studio}{section.867.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {867.1}{\\ignorespaces You can install all the necessary Maven plugins for Dev Studio by installing the \\emph  {Liferay IDE Maven Support} option.}}{2576}{figure.867.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {867.2}Importing Maven Projects}{2577}{section.867.2}\\protected@file@percent }\n\\newlabel{importing-maven-projects}{{867.2}{2577}{Importing Maven Projects}{section.867.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {867.2}{\\ignorespaces Dev Studio offers the Maven folder in the Import wizard.}}{2577}{figure.867.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {867.3}Using the POM Graphic Editor}{2577}{section.867.3}\\protected@file@percent }\n\\newlabel{using-the-pom-graphic-editor}{{867.3}{2577}{Using the POM Graphic Editor}{section.867.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {867.3}{\\ignorespaces Use the Import Maven Projects wizard to import your pre-existing project.}}{2578}{figure.867.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {867.4}{\\ignorespaces Liferay Dev Studio provides five interactive modes to help you edit and organize your POM..}}{2579}{figure.867.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {868}IntelliJ}{2581}{chapter.868}\\protected@file@percent }\n\\newlabel{intellij}{{868}{2581}{IntelliJ}{chapter.868}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {869}Installing the Liferay IntelliJ Plugin}{2583}{chapter.869}\\protected@file@percent }\n\\newlabel{installing-the-liferay-intellij-plugin}{{869}{2583}{Installing the Liferay IntelliJ Plugin}{chapter.869}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {869.1}Installing Via IntelliJ Marketplace}{2583}{section.869.1}\\protected@file@percent }\n\\newlabel{installing-via-intellij-marketplace}{{869.1}{2583}{Installing Via IntelliJ Marketplace}{section.869.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {869.2}Installing Via Zip File}{2583}{section.869.2}\\protected@file@percent }\n\\newlabel{installing-via-zip-file}{{869.2}{2583}{Installing Via Zip File}{section.869.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {869.1}{\\ignorespaces IntelliJ Marketplace offers a streamlined way to install plugins.}}{2584}{figure.869.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {870}Installing a Server in IntelliJ}{2585}{chapter.870}\\protected@file@percent }\n\\newlabel{installing-a-server-in-intellij}{{870}{2585}{Installing a Server in IntelliJ}{chapter.870}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {870.1}{\\ignorespaces You have several options to choose from the server dropdown menu.}}{2585}{figure.870.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {870.2}{\\ignorespaces Set your Liferay server's configurations in the Run/Debug Configurations menu.}}{2586}{figure.870.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {871}Updating Liferay IntelliJ Plugin}{2587}{chapter.871}\\protected@file@percent }\n\\newlabel{updating-liferay-intellij-plugin}{{871}{2587}{Updating Liferay IntelliJ Plugin}{chapter.871}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {871.1}{\\ignorespaces Check for updates periodically to ensure you're leveraging the latest features.}}{2587}{figure.871.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {871.2}{\\ignorespaces The Available Updates prompt also prints the plugin version to which you're updating.}}{2588}{figure.871.2}\\protected@file@percent }\n\\gdef \\LT@lix {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {872}Liferay JS Generator}{2589}{chapter.872}\\protected@file@percent }\n\\newlabel{liferay-js-generator}{{872}{2589}{Liferay JS Generator}{chapter.872}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {873}Installing the JS Generator and Generating a Bundle}{2591}{chapter.873}\\protected@file@percent }\n\\newlabel{installing-the-js-generator-and-generating-a-bundle}{{873}{2591}{Installing the JS Generator and Generating a Bundle}{chapter.873}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {873.1}{\\ignorespaces The liferay-js generator prompts you for widget options.}}{2592}{figure.873.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {874}Understanding the JS Portlet Extender Configuration}{2593}{chapter.874}\\protected@file@percent }\n\\newlabel{understanding-the-js-portlet-extender-configuration}{{874}{2593}{Understanding the JS Portlet Extender Configuration}{chapter.874}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {874.1}Manifest Header}{2593}{section.874.1}\\protected@file@percent }\n\\newlabel{manifest-header}{{874.1}{2593}{Manifest Header}{section.874.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {874.2}Main Entry Point}{2593}{section.874.2}\\protected@file@percent }\n\\newlabel{main-entry-point}{{874.2}{2593}{Main Entry Point}{section.874.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {875}Configuration JSON Available Options}{2595}{chapter.875}\\protected@file@percent }\n\\newlabel{configuration-json-available-options}{{875}{2595}{Configuration JSON Available Options}{chapter.875}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {875.1}JSON Format}{2595}{section.875.1}\\protected@file@percent }\n\\newlabel{json-format}{{875.1}{2595}{JSON Format}{section.875.1}{}}\n\\gdef \\LT@lx {\\LT@entry \n    {1}{234.8775pt}\\LT@entry \n    {1}{234.8775pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {876}Adapting Existing Apps to Run on Liferay DXP}{2599}{chapter.876}\\protected@file@percent }\n\\newlabel{adapting-existing-apps-to-run-on-liferay-dxp}{{876}{2599}{Adapting Existing Apps to Run on Liferay DXP}{chapter.876}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {876.1}{\\ignorespaces You can run the adapt subtarget of the Liferay JS Generator to adapt your existing apps for Liferay.}}{2600}{figure.876.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {876.2}{\\ignorespaces You can run the adapt subtarget of the Liferay JS Generator to adapt your existing apps for Liferay.}}{2600}{figure.876.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {876.3}{\\ignorespaces Your adapted app runs in Liferay in no time.}}{2601}{figure.876.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {877}Liferay Workspace}{2603}{chapter.877}\\protected@file@percent }\n\\newlabel{liferay-workspace}{{877}{2603}{Liferay Workspace}{chapter.877}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {877.1}Workspace Anatomy}{2604}{section.877.1}\\protected@file@percent }\n\\newlabel{workspace-anatomy}{{877.1}{2604}{Workspace Anatomy}{section.877.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {877.2}Development Lifecycle}{2604}{section.877.2}\\protected@file@percent }\n\\newlabel{development-lifecycle}{{877.2}{2604}{Development Lifecycle}{section.877.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {877.3}Creating Projects}{2605}{section.877.3}\\protected@file@percent }\n\\newlabel{creating-projects}{{877.3}{2605}{Creating Projects}{section.877.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {877.4}Building Projects}{2605}{section.877.4}\\protected@file@percent }\n\\newlabel{building-projects}{{877.4}{2605}{Building Projects}{section.877.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {877.5}Deploying Projects}{2606}{section.877.5}\\protected@file@percent }\n\\newlabel{deploying-projects}{{877.5}{2606}{Deploying Projects}{section.877.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {877.6}Testing Projects}{2606}{section.877.6}\\protected@file@percent }\n\\newlabel{testing-projects}{{877.6}{2606}{Testing Projects}{section.877.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {877.1}{\\ignorespaces The \\texttt  {configs/common} and \\texttt  {configs/{[}environment{]}} overlay you Liferay DXP bundle when it's generated.}}{2606}{figure.877.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {877.7}Releasing Projects}{2607}{section.877.7}\\protected@file@percent }\n\\newlabel{releasing-projects}{{877.7}{2607}{Releasing Projects}{section.877.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {878}Installing Liferay Workspace}{2609}{chapter.878}\\protected@file@percent }\n\\newlabel{installing-liferay-workspace}{{878}{2609}{Installing Liferay Workspace}{chapter.878}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {878.1}{\\ignorespaces Determine where your Liferay Workspace should reside.}}{2610}{figure.878.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {878.2}{\\ignorespaces Select the product version you'll use with your Liferay Workspace.}}{2610}{figure.878.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {879}Creating a Liferay Workspace}{2611}{chapter.879}\\protected@file@percent }\n\\newlabel{creating-a-liferay-workspace}{{879}{2611}{Creating a Liferay Workspace}{chapter.879}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {879.1}Blade CLI}{2611}{section.879.1}\\protected@file@percent }\n\\newlabel{blade-cli-3}{{879.1}{2611}{Blade CLI}{section.879.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {879.2}Dev Studio}{2612}{section.879.2}\\protected@file@percent }\n\\newlabel{dev-studio}{{879.2}{2612}{Dev Studio}{section.879.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {879.1}{\\ignorespaces By selecting \\emph  {Liferay Workspace Project}, you begin the process of creating a new workspace for your Liferay projects.}}{2612}{figure.879.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {879.2}{\\ignorespaces Dev Studio provides an easy-to-follow menu to create your Liferay Workspace.}}{2613}{figure.879.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {879.3}IntelliJ}{2614}{section.879.3}\\protected@file@percent }\n\\newlabel{intellij-1}{{879.3}{2614}{IntelliJ}{section.879.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {879.3}{\\ignorespaces Choose \\emph  {Liferay Gradle Workspace} or \\emph  {Liferay Maven Workspace}, depending on the build you prefer.}}{2614}{figure.879.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {879.4}Maven}{2614}{section.879.4}\\protected@file@percent }\n\\newlabel{maven-2}{{879.4}{2614}{Maven}{section.879.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {879.4}{\\ignorespaces Specify your workspace's configurations.}}{2615}{figure.879.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {880}Importing a Liferay Workspace into an IDE}{2617}{chapter.880}\\protected@file@percent }\n\\newlabel{importing-a-liferay-workspace-into-an-ide}{{880}{2617}{Importing a Liferay Workspace into an IDE}{chapter.880}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {880.1}Dev Studio}{2617}{section.880.1}\\protected@file@percent }\n\\newlabel{dev-studio-1}{{880.1}{2617}{Dev Studio}{section.880.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {880.2}IntelliJ}{2617}{section.880.2}\\protected@file@percent }\n\\newlabel{intellij-2}{{880.2}{2617}{IntelliJ}{section.880.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {880.1}{\\ignorespaces You can import an existing Liferay Workspace into your current Dev Studio session.}}{2618}{figure.880.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {880.2}{\\ignorespaces Specify your workspace's configurations.}}{2618}{figure.880.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {881}Setting Proxy Requirements for Liferay Workspace}{2619}{chapter.881}\\protected@file@percent }\n\\newlabel{setting-proxy-requirements-for-liferay-workspace}{{881}{2619}{Setting Proxy Requirements for Liferay Workspace}{chapter.881}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {881.1}Gradle}{2619}{section.881.1}\\protected@file@percent }\n\\newlabel{gradle-1}{{881.1}{2619}{Gradle}{section.881.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {881.2}Maven}{2620}{section.881.2}\\protected@file@percent }\n\\newlabel{maven-3}{{881.2}{2620}{Maven}{section.881.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {882}Adding a Liferay Bundle to Liferay Workspace}{2621}{chapter.882}\\protected@file@percent }\n\\newlabel{adding-a-liferay-bundle-to-liferay-workspace}{{882}{2621}{Adding a Liferay Bundle to Liferay Workspace}{chapter.882}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {883}Setting Environment Configurations for Liferay Workspace}{2623}{chapter.883}\\protected@file@percent }\n\\newlabel{setting-environment-configurations-for-liferay-workspace}{{883}{2623}{Setting Environment Configurations for Liferay Workspace}{chapter.883}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {884}Building Node.js Themes in Liferay Workspace}{2625}{chapter.884}\\protected@file@percent }\n\\newlabel{building-node.js-themes-in-liferay-workspace}{{884}{2625}{Building Node.js Themes in Liferay Workspace}{chapter.884}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {885}Building Gradle/Maven Themes in Liferay Workspace}{2627}{chapter.885}\\protected@file@percent }\n\\newlabel{building-gradlemaven-themes-in-liferay-workspace}{{885}{2627}{Building Gradle/Maven Themes in Liferay Workspace}{chapter.885}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {886}Managing the Target Platform}{2629}{chapter.886}\\protected@file@percent }\n\\newlabel{managing-the-target-platform}{{886}{2629}{Managing the Target Platform}{chapter.886}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {886.1}Dependency Management with BOMs}{2629}{section.886.1}\\protected@file@percent }\n\\newlabel{dependency-management-with-boms}{{886.1}{2629}{Dependency Management with BOMs}{section.886.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {886.2}Leveraging Target Platform in Dev Studio}{2630}{section.886.2}\\protected@file@percent }\n\\newlabel{leveraging-target-platform-in-dev-studio}{{886.2}{2630}{Leveraging Target Platform in Dev Studio}{section.886.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {887}Setting the Target Platform}{2631}{chapter.887}\\protected@file@percent }\n\\newlabel{setting-the-target-platform}{{887}{2631}{Setting the Target Platform}{chapter.887}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {888}Targeting a Platform Outside of Workspace}{2633}{chapter.888}\\protected@file@percent }\n\\newlabel{targeting-a-platform-outside-of-workspace}{{888}{2633}{Targeting a Platform Outside of Workspace}{chapter.888}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {889}Targeting a Platform with Maven}{2635}{chapter.889}\\protected@file@percent }\n\\newlabel{targeting-a-platform-with-maven}{{889}{2635}{Targeting a Platform with Maven}{chapter.889}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {890}Validating Modules Against the Target Platform}{2637}{chapter.890}\\protected@file@percent }\n\\newlabel{validating-modules-against-the-target-platform}{{890}{2637}{Validating Modules Against the Target Platform}{chapter.890}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {890.1}Resolving Your Modules}{2637}{section.890.1}\\protected@file@percent }\n\\newlabel{resolving-your-modules}{{890.1}{2637}{Resolving Your Modules}{section.890.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {890.2}Modifying the Target Platform's Capabilities}{2639}{section.890.2}\\protected@file@percent }\n\\newlabel{modifying-the-target-platforms-capabilities}{{890.2}{2639}{Modifying the Target Platform's Capabilities}{section.890.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {890.3}Depending on Third Party Libraries Not Included in Liferay DXP}{2639}{section.890.3}\\protected@file@percent }\n\\newlabel{depending-on-third-party-libraries-not-included-in-liferay-dxp}{{890.3}{2639}{Depending on Third Party Libraries Not Included in Liferay DXP}{section.890.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {890.4}Depending on a Customized Distribution of Liferay DXP}{2639}{section.890.4}\\protected@file@percent }\n\\newlabel{depending-on-a-customized-distribution-of-liferay-dxp}{{890.4}{2639}{Depending on a Customized Distribution of Liferay DXP}{section.890.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {890.5}Including the Resolver in Your Gradle Build}{2640}{section.890.5}\\protected@file@percent }\n\\newlabel{including-the-resolver-in-your-gradle-build}{{890.5}{2640}{Including the Resolver in Your Gradle Build}{section.890.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {891}Adding a Third Party Library's Capabilities to the Resolver's Capabilities}{2641}{chapter.891}\\protected@file@percent }\n\\newlabel{adding-a-third-party-librarys-capabilities-to-the-resolvers-capabilities}{{891}{2641}{Adding a Third Party Library's Capabilities to the Resolver's Capabilities}{chapter.891}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {892}Skipping the Resolving Process for a Module}{2643}{chapter.892}\\protected@file@percent }\n\\newlabel{skipping-the-resolving-process-for-a-module}{{892}{2643}{Skipping the Resolving Process for a Module}{chapter.892}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {893}Depending on a Customized Distribution of Liferay DXP}{2645}{chapter.893}\\protected@file@percent }\n\\newlabel{depending-on-a-customized-distribution-of-liferay-dxp-1}{{893}{2645}{Depending on a Customized Distribution of Liferay DXP}{chapter.893}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {894}Including the Resolver in Your Gradle Build}{2647}{chapter.894}\\protected@file@percent }\n\\newlabel{including-the-resolver-in-your-gradle-build-1}{{894}{2647}{Including the Resolver in Your Gradle Build}{chapter.894}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {895}How to Resolve Common Output Errors Reported by the Resolve Task}{2649}{chapter.895}\\protected@file@percent }\n\\newlabel{how-to-resolve-common-output-errors-reported-by-the-resolve-task}{{895}{2649}{How to Resolve Common Output Errors Reported by the Resolve Task}{chapter.895}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {895.1}Missing Import Error}{2649}{section.895.1}\\protected@file@percent }\n\\newlabel{missing-import-error}{{895.1}{2649}{Missing Import Error}{section.895.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {895.2}Missing Service Reference}{2650}{section.895.2}\\protected@file@percent }\n\\newlabel{missing-service-reference}{{895.2}{2650}{Missing Service Reference}{section.895.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {895.3}Missing Fragment Host}{2650}{section.895.3}\\protected@file@percent }\n\\newlabel{missing-fragment-host}{{895.3}{2650}{Missing Fragment Host}{section.895.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {896}Validating Modules Outside of Workspace}{2653}{chapter.896}\\protected@file@percent }\n\\newlabel{validating-modules-outside-of-workspace}{{896}{2653}{Validating Modules Outside of Workspace}{chapter.896}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {897}Leveraging Docker}{2655}{chapter.897}\\protected@file@percent }\n\\newlabel{leveraging-docker}{{897}{2655}{Leveraging Docker}{chapter.897}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {898}Creating a Liferay DXP Docker Container}{2657}{chapter.898}\\protected@file@percent }\n\\newlabel{creating-a-liferay-dxp-docker-container}{{898}{2657}{Creating a Liferay DXP Docker Container}{chapter.898}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {899}Configuring a Docker Container}{2659}{chapter.899}\\protected@file@percent }\n\\newlabel{configuring-a-docker-container}{{899}{2659}{Configuring a Docker Container}{chapter.899}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {900}Building a Custom Docker Image}{2661}{chapter.900}\\protected@file@percent }\n\\newlabel{building-a-custom-docker-image}{{900}{2661}{Building a Custom Docker Image}{chapter.900}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {901}Updating Liferay Workspace}{2663}{chapter.901}\\protected@file@percent }\n\\newlabel{updating-liferay-workspace}{{901}{2663}{Updating Liferay Workspace}{chapter.901}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {901.1}Gradle}{2663}{section.901.1}\\protected@file@percent }\n\\newlabel{gradle-2}{{901.1}{2663}{Gradle}{section.901.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {901.2}Maven}{2664}{section.901.2}\\protected@file@percent }\n\\newlabel{maven-4}{{901.2}{2664}{Maven}{section.901.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {902}Updating Default Plugins Provided by Liferay Workspace}{2665}{chapter.902}\\protected@file@percent }\n\\newlabel{updating-default-plugins-provided-by-liferay-workspace}{{902}{2665}{Updating Default Plugins Provided by Liferay Workspace}{chapter.902}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {903}Maven}{2667}{chapter.903}\\protected@file@percent }\n\\newlabel{maven-5}{{903}{2667}{Maven}{chapter.903}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {903.1}Installing Liferay Maven Artifacts}{2667}{section.903.1}\\protected@file@percent }\n\\newlabel{installing-liferay-maven-artifacts}{{903.1}{2667}{Installing Liferay Maven Artifacts}{section.903.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {903.2}Managing Maven Artifacts in a Repository}{2668}{section.903.2}\\protected@file@percent }\n\\newlabel{managing-maven-artifacts-in-a-repository}{{903.2}{2668}{Managing Maven Artifacts in a Repository}{section.903.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {903.3}Applying Maven Plugins}{2668}{section.903.3}\\protected@file@percent }\n\\newlabel{applying-maven-plugins}{{903.3}{2668}{Applying Maven Plugins}{section.903.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {904}Installing Liferay Maven Artifacts}{2671}{chapter.904}\\protected@file@percent }\n\\newlabel{installing-liferay-maven-artifacts-1}{{904}{2671}{Installing Liferay Maven Artifacts}{chapter.904}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {905}Creating a Maven Repository}{2673}{chapter.905}\\protected@file@percent }\n\\newlabel{creating-a-maven-repository}{{905}{2673}{Creating a Maven Repository}{chapter.905}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {905.1}{\\ignorespaces Adding a repository to hold your Liferay artifacts is easy with Nexus.}}{2673}{figure.905.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {906}Configuring Local Maven Settings to Access Repositories}{2675}{chapter.906}\\protected@file@percent }\n\\newlabel{configuring-local-maven-settings-to-access-repositories}{{906}{2675}{Configuring Local Maven Settings to Access Repositories}{chapter.906}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {907}Deploying Liferay Maven Artifacts to a Repository}{2677}{chapter.907}\\protected@file@percent }\n\\newlabel{deploying-liferay-maven-artifacts-to-a-repository}{{907}{2677}{Deploying Liferay Maven Artifacts to a Repository}{chapter.907}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {907.1}{\\ignorespaces Your repository server now provides access to your Liferay Maven artifacts.}}{2679}{figure.907.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {908}Building an OSGi Module JAR with Maven}{2681}{chapter.908}\\protected@file@percent }\n\\newlabel{building-an-osgi-module-jar-with-maven}{{908}{2681}{Building an OSGi Module JAR with Maven}{chapter.908}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {909}Building a Theme with Maven}{2683}{chapter.909}\\protected@file@percent }\n\\newlabel{building-a-theme-with-maven}{{909}{2683}{Building a Theme with Maven}{chapter.909}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {910}Compiling Sass Files in a Maven Project}{2687}{chapter.910}\\protected@file@percent }\n\\newlabel{compiling-sass-files-in-a-maven-project}{{910}{2687}{Compiling Sass Files in a Maven Project}{chapter.910}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {911}Using Service Builder in a Maven Project}{2689}{chapter.911}\\protected@file@percent }\n\\newlabel{using-service-builder-in-a-maven-project}{{911}{2689}{Using Service Builder in a Maven Project}{chapter.911}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {912}Upgrading Your Maven Build Environment}{2691}{chapter.912}\\protected@file@percent }\n\\newlabel{upgrading-your-maven-build-environment}{{912}{2691}{Upgrading Your Maven Build Environment}{chapter.912}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {912.1}Upgrading to New 7.0 Maven Plugins}{2691}{section.912.1}\\protected@file@percent }\n\\newlabel{upgrading-to-new-7.0-maven-plugins}{{912.1}{2691}{Upgrading to New 7.0 Maven Plugins}{section.912.1}{}}\n\\gdef \\LT@lxi {\\LT@entry \n    {1}{98.84975pt}\\LT@entry \n    {1}{216.28851pt}\\LT@entry \n    {1}{154.56912pt}}\n\\gdef \\LT@lxii {\\LT@entry \n    {1}{143.70718pt}\\LT@entry \n    {3}{131.02863pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {912.2}Updating Liferay Maven Artifact Dependencies}{2693}{section.912.2}\\protected@file@percent }\n\\newlabel{updating-liferay-maven-artifact-dependencies}{{912.2}{2693}{Updating Liferay Maven Artifact Dependencies}{section.912.2}{}}\n\\gdef \\LT@lxiii {\\LT@entry \n    {1}{82.53932pt}\\LT@entry \n    {1}{160.56912pt}\\LT@entry \n    {1}{226.60574pt}}\n\\gdef \\LT@lxiv {\\LT@entry \n    {1}{67.33755pt}\\LT@entry \n    {1}{130.61452pt}\\LT@entry \n    {1}{271.80292pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {913}Theme Generator}{2695}{chapter.913}\\protected@file@percent }\n\\newlabel{theme-generator}{{913}{2695}{Theme Generator}{chapter.913}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {913.1}Sub-generators}{2695}{section.913.1}\\protected@file@percent }\n\\newlabel{sub-generators}{{913.1}{2695}{Sub-generators}{section.913.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {913.2}Layouts Sub-generator}{2696}{section.913.2}\\protected@file@percent }\n\\newlabel{layouts-sub-generator}{{913.2}{2696}{Layouts Sub-generator}{section.913.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {913.3}Themelets Sub-generator}{2696}{section.913.3}\\protected@file@percent }\n\\newlabel{themelets-sub-generator}{{913.3}{2696}{Themelets Sub-generator}{section.913.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {914}Installing the Theme Generator and Creating a Theme}{2697}{chapter.914}\\protected@file@percent }\n\\newlabel{installing-the-theme-generator-and-creating-a-theme}{{914}{2697}{Installing the Theme Generator and Creating a Theme}{chapter.914}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {914.1}{\\ignorespaces The tools are in your hands to create any theme you can imagine.}}{2697}{figure.914.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {914.2}{\\ignorespaces You can generate a theme by answering just a few configuration questions.}}{2698}{figure.914.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {915}Generating Layout Templates with the Theme Generator}{2701}{chapter.915}\\protected@file@percent }\n\\newlabel{generating-layout-templates-with-the-theme-generator}{{915}{2701}{Generating Layout Templates with the Theme Generator}{chapter.915}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {915.1}{\\ignorespaces The \\emph  {1-2-1 Columns} page layout creates a nice flow for your content.}}{2701}{figure.915.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {915.2}{\\ignorespaces You must specify the width for each column in the row.}}{2702}{figure.915.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {915.3}{\\ignorespaces The Layouts sub-generator automates the layout creation process.}}{2703}{figure.915.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {915.4}{\\ignorespaces Rows can be inserted using the layout vi.}}{2703}{figure.915.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {915.5}{\\ignorespaces Rows are removed using the layout vi.}}{2703}{figure.915.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {915.6}{\\ignorespaces Select the \\emph  {Finish layout} option to complete your design.}}{2704}{figure.915.6}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {916}Generating Themelets with the Theme Generator}{2705}{chapter.916}\\protected@file@percent }\n\\newlabel{generating-themelets-with-the-theme-generator}{{916}{2705}{Generating Themelets with the Theme Generator}{chapter.916}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {916.1}{\\ignorespaces Themelets can be used to modify one aspect of the UI, that you can then reuse in your other themes.}}{2705}{figure.916.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {916.2}{\\ignorespaces The Themelet sub-generator automates the themelet creation process, making it quick and easy.}}{2706}{figure.916.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {917}Liferay Upgrade Planner}{2707}{chapter.917}\\protected@file@percent }\n\\newlabel{liferay-upgrade-planner}{{917}{2707}{Liferay Upgrade Planner}{chapter.917}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {917.1}{\\ignorespaces Configure your upgrade plan before beginning the upgrade process.}}{2708}{figure.917.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {917.2}{\\ignorespaces You can preview the Upgrade Planner's automated updates before you perform them.}}{2709}{figure.917.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {918}Using the Upgrade Planner with Proxy Requirements}{2711}{chapter.918}\\protected@file@percent }\n\\newlabel{using-the-upgrade-planner-with-proxy-requirements}{{918}{2711}{Using the Upgrade Planner with Proxy Requirements}{chapter.918}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {918.1}{\\ignorespaces You can configure your proxy settings in Dev Studio's Network Connections menu.}}{2712}{figure.918.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {919}Web Experience Management Reference}{2713}{chapter.919}\\protected@file@percent }\n\\newlabel{web-experience-management-reference}{{919}{2713}{Web Experience Management Reference}{chapter.919}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {920}Fragment Specific Tags}{2715}{chapter.920}\\protected@file@percent }\n\\newlabel{fragment-specific-tags}{{920}{2715}{Fragment Specific Tags}{chapter.920}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {920.1}Making Text Editable}{2715}{section.920.1}\\protected@file@percent }\n\\newlabel{making-text-editable}{{920.1}{2715}{Making Text Editable}{section.920.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {920.2}Making Images Editable}{2715}{section.920.2}\\protected@file@percent }\n\\newlabel{making-images-editable}{{920.2}{2715}{Making Images Editable}{section.920.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {920.1}{\\ignorespaces You have several options for defining an image on a Content Page.}}{2716}{figure.920.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {920.3}Creating Editable Links}{2716}{section.920.3}\\protected@file@percent }\n\\newlabel{creating-editable-links}{{920.3}{2716}{Creating Editable Links}{section.920.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {920.4}Including Widgets Within A Fragment}{2716}{section.920.4}\\protected@file@percent }\n\\newlabel{including-widgets-within-a-fragment}{{920.4}{2716}{Including Widgets Within A Fragment}{section.920.4}{}}\n\\gdef \\LT@lxv {\\LT@entry \n    {3}{139.77615pt}\\LT@entry \n    {3}{156.03435pt}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {920.2}{\\ignorespaces You have several options for defining a link's appearance and behavior.}}{2717}{figure.920.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {920.5}Enabling Embedding for Your Widget}{2718}{section.920.5}\\protected@file@percent }\n\\newlabel{enabling-embedding-for-your-widget}{{920.5}{2718}{Enabling Embedding for Your Widget}{section.920.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {921}Fragment Configuration Types}{2719}{chapter.921}\\protected@file@percent }\n\\newlabel{fragment-configuration-types}{{921}{2719}{Fragment Configuration Types}{chapter.921}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {921.1}Checkbox Configuration}{2719}{section.921.1}\\protected@file@percent }\n\\newlabel{checkbox-configuration}{{921.1}{2719}{Checkbox Configuration}{section.921.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {921.1}{\\ignorespaces The checkbox configuration is useful when a boolean selection is necessary.}}{2720}{figure.921.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {921.2}Color Palette Configuration}{2720}{section.921.2}\\protected@file@percent }\n\\newlabel{color-palette-configuration}{{921.2}{2720}{Color Palette Configuration}{section.921.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {921.2}{\\ignorespaces The \\texttt  {colorPalette} configuration is useful when a color selection is necessary.}}{2720}{figure.921.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {921.3}Select Configuration}{2721}{section.921.3}\\protected@file@percent }\n\\newlabel{select-configuration}{{921.3}{2721}{Select Configuration}{section.921.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {921.3}{\\ignorespaces The \\texttt  {select} configuration is useful when an option choice is necessary.}}{2721}{figure.921.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {921.4}Text Configuration}{2721}{section.921.4}\\protected@file@percent }\n\\newlabel{text-configuration}{{921.4}{2721}{Text Configuration}{section.921.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {921.4}{\\ignorespaces The \\texttt  {text} configuration is useful when an input text option is necessary.}}{2722}{figure.921.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {922}Escaping Fragment Configuration Text Values}{2723}{chapter.922}\\protected@file@percent }\n\\newlabel{escaping-fragment-configuration-text-values}{{922}{2723}{Escaping Fragment Configuration Text Values}{chapter.922}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {922.1}Escaping Values in HTML/FreeMarker}{2723}{section.922.1}\\protected@file@percent }\n\\newlabel{escaping-values-in-htmlfreemarker}{{922.1}{2723}{Escaping Values in HTML/FreeMarker}{section.922.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {922.2}Escaping Values in JavaScript}{2723}{section.922.2}\\protected@file@percent }\n\\newlabel{escaping-values-in-javascript}{{922.2}{2723}{Escaping Values in JavaScript}{section.922.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {923}Asset Display Page Taglib}{2725}{chapter.923}\\protected@file@percent }\n\\newlabel{asset-display-page-taglib}{{923}{2725}{Asset Display Page Taglib}{chapter.923}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {923.1}Display Page Attributes}{2725}{section.923.1}\\protected@file@percent }\n\\newlabel{display-page-attributes}{{923.1}{2725}{Display Page Attributes}{section.923.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {924}Crafting XML Workflow Definitions}{2727}{chapter.924}\\protected@file@percent }\n\\newlabel{crafting-xml-workflow-definitions}{{924}{2727}{Crafting XML Workflow Definitions}{chapter.924}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {924.1}Existing Workflow Definitions}{2727}{section.924.1}\\protected@file@percent }\n\\newlabel{existing-workflow-definitions}{{924.1}{2727}{Existing Workflow Definitions}{section.924.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {924.2}Schema}{2728}{section.924.2}\\protected@file@percent }\n\\newlabel{schema}{{924.2}{2728}{Schema}{section.924.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {924.3}Metadata}{2728}{section.924.3}\\protected@file@percent }\n\\newlabel{metadata}{{924.3}{2728}{Metadata}{section.924.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {925}Workflow Definition Nodes}{2729}{chapter.925}\\protected@file@percent }\n\\newlabel{workflow-definition-nodes}{{925}{2729}{Workflow Definition Nodes}{chapter.925}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {925.1}State Nodes}{2729}{section.925.1}\\protected@file@percent }\n\\newlabel{state-nodes}{{925.1}{2729}{State Nodes}{section.925.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {925.2}Conditions}{2730}{section.925.2}\\protected@file@percent }\n\\newlabel{conditions}{{925.2}{2730}{Conditions}{section.925.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {925.3}Forks and Joins}{2731}{section.925.3}\\protected@file@percent }\n\\newlabel{forks-and-joins}{{925.3}{2731}{Forks and Joins}{section.925.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {925.4}Task Nodes}{2732}{section.925.4}\\protected@file@percent }\n\\newlabel{task-nodes}{{925.4}{2732}{Task Nodes}{section.925.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {926}Workflow Task Nodes}{2733}{chapter.926}\\protected@file@percent }\n\\newlabel{workflow-task-nodes}{{926}{2733}{Workflow Task Nodes}{chapter.926}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {926.1}Assignments}{2734}{section.926.1}\\protected@file@percent }\n\\newlabel{assignments}{{926.1}{2734}{Assignments}{section.926.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {926.2}Resource Action Assignments}{2735}{section.926.2}\\protected@file@percent }\n\\newlabel{resource-action-assignments}{{926.2}{2735}{Resource Action Assignments}{section.926.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {926.3}Task Timers}{2736}{section.926.3}\\protected@file@percent }\n\\newlabel{task-timers}{{926.3}{2736}{Task Timers}{section.926.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {927}Workflow Notifications}{2739}{chapter.927}\\protected@file@percent }\n\\newlabel{workflow-notifications}{{927}{2739}{Workflow Notifications}{chapter.927}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {927.1}Notification Options}{2739}{section.927.1}\\protected@file@percent }\n\\newlabel{notification-options}{{927.1}{2739}{Notification Options}{section.927.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {928}Breaking Changes}{2743}{chapter.928}\\protected@file@percent }\n\\newlabel{breaking-changes}{{928}{2743}{Breaking Changes}{chapter.928}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.1}Breaking Changes List}{2743}{section.928.1}\\protected@file@percent }\n\\newlabel{breaking-changes-list}{{928.1}{2743}{Breaking Changes List}{section.928.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.2}Removed Support for JSP Templates in Themes}{2743}{section.928.2}\\protected@file@percent }\n\\newlabel{removed-support-for-jsp-templates-in-themes}{{928.2}{2743}{Removed Support for JSP Templates in Themes}{section.928.2}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2743}{section.928.2}\\protected@file@percent }\n\\newlabel{what-changed}{{928.2}{2743}{What changed?}{section.928.2}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2744}{section.928.2}\\protected@file@percent }\n\\newlabel{who-is-affected}{{928.2}{2744}{Who is affected?}{section.928.2}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2744}{section.928.2}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code}{{928.2}{2744}{How should I update my code?}{section.928.2}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2744}{section.928.2}\\protected@file@percent }\n\\newlabel{why-was-this-change-made}{{928.2}{2744}{Why was this change made?}{section.928.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.3}Lodash Is No Longer Included by Default}{2744}{section.928.3}\\protected@file@percent }\n\\newlabel{lodash-is-no-longer-included-by-default}{{928.3}{2744}{Lodash Is No Longer Included by Default}{section.928.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2744}{section.928.3}\\protected@file@percent }\n\\newlabel{what-changed-1}{{928.3}{2744}{What changed?}{section.928.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2744}{section.928.3}\\protected@file@percent }\n\\newlabel{who-is-affected-1}{{928.3}{2744}{Who is affected?}{section.928.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2744}{section.928.3}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-1}{{928.3}{2744}{How should I update my code?}{section.928.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2744}{section.928.3}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-1}{{928.3}{2744}{Why was this change made?}{section.928.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.4}Moved Two Staging Properties to OSGi Configuration}{2744}{section.928.4}\\protected@file@percent }\n\\newlabel{moved-two-staging-properties-to-osgi-configuration}{{928.4}{2744}{Moved Two Staging Properties to OSGi Configuration}{section.928.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2745}{section.928.4}\\protected@file@percent }\n\\newlabel{what-changed-2}{{928.4}{2745}{What changed?}{section.928.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2745}{section.928.4}\\protected@file@percent }\n\\newlabel{who-is-affected-2}{{928.4}{2745}{Who is affected?}{section.928.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2745}{section.928.4}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-2}{{928.4}{2745}{How should I update my code?}{section.928.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2745}{section.928.4}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-2}{{928.4}{2745}{Why was this change made?}{section.928.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.5}Remove Link Application URLs to Page Functionality}{2745}{section.928.5}\\protected@file@percent }\n\\newlabel{remove-link-application-urls-to-page-functionality}{{928.5}{2745}{Remove Link Application URLs to Page Functionality}{section.928.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2745}{section.928.5}\\protected@file@percent }\n\\newlabel{what-changed-3}{{928.5}{2745}{What changed?}{section.928.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2745}{section.928.5}\\protected@file@percent }\n\\newlabel{who-is-affected-3}{{928.5}{2745}{Who is affected?}{section.928.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2745}{section.928.5}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-3}{{928.5}{2745}{How should I update my code?}{section.928.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2745}{section.928.5}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-3}{{928.5}{2745}{Why was this change made?}{section.928.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.6}Moved TermsOfUseContentProvider out of kernel.util}{2746}{section.928.6}\\protected@file@percent }\n\\newlabel{moved-termsofusecontentprovider-out-of-kernel.util}{{928.6}{2746}{Moved TermsOfUseContentProvider out of kernel.util}{section.928.6}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2746}{section.928.6}\\protected@file@percent }\n\\newlabel{what-changed-4}{{928.6}{2746}{What changed?}{section.928.6}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2746}{section.928.6}\\protected@file@percent }\n\\newlabel{who-is-affected-4}{{928.6}{2746}{Who is affected?}{section.928.6}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2746}{section.928.6}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-4}{{928.6}{2746}{How should I update my code?}{section.928.6}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2746}{section.928.6}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-4}{{928.6}{2746}{Why was this change made?}{section.928.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.7}Removed HibernateConfigurationConverter and Converter}{2746}{section.928.7}\\protected@file@percent }\n\\newlabel{removed-hibernateconfigurationconverter-and-converter}{{928.7}{2746}{Removed HibernateConfigurationConverter and Converter}{section.928.7}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2746}{section.928.7}\\protected@file@percent }\n\\newlabel{what-changed-5}{{928.7}{2746}{What changed?}{section.928.7}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2747}{section.928.7}\\protected@file@percent }\n\\newlabel{who-is-affected-5}{{928.7}{2747}{Who is affected?}{section.928.7}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2747}{section.928.7}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-5}{{928.7}{2747}{How should I update my code?}{section.928.7}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2747}{section.928.7}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-5}{{928.7}{2747}{Why was this change made?}{section.928.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.8}Switched to Use JDK Function and Supplier}{2747}{section.928.8}\\protected@file@percent }\n\\newlabel{switched-to-use-jdk-function-and-supplier}{{928.8}{2747}{Switched to Use JDK Function and Supplier}{section.928.8}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2747}{section.928.8}\\protected@file@percent }\n\\newlabel{what-changed-6}{{928.8}{2747}{What changed?}{section.928.8}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2747}{section.928.8}\\protected@file@percent }\n\\newlabel{who-is-affected-6}{{928.8}{2747}{Who is affected?}{section.928.8}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2747}{section.928.8}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-6}{{928.8}{2747}{How should I update my code?}{section.928.8}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2747}{section.928.8}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-6}{{928.8}{2747}{Why was this change made?}{section.928.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.9}Deprecated com.liferay.portal.service.InvokableService Interface}{2747}{section.928.9}\\protected@file@percent }\n\\newlabel{deprecated-com.liferay.portal.service.invokableservice-interface}{{928.9}{2747}{Deprecated com.liferay.portal.service.InvokableService Interface}{section.928.9}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2747}{section.928.9}\\protected@file@percent }\n\\newlabel{what-changed-7}{{928.9}{2747}{What changed?}{section.928.9}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2748}{section.928.9}\\protected@file@percent }\n\\newlabel{who-is-affected-7}{{928.9}{2748}{Who is affected?}{section.928.9}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2748}{section.928.9}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-7}{{928.9}{2748}{How should I update my code?}{section.928.9}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2748}{section.928.9}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-7}{{928.9}{2748}{Why was this change made?}{section.928.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.10}Dropped Support of ServiceLoaderCondition}{2748}{section.928.10}\\protected@file@percent }\n\\newlabel{dropped-support-of-serviceloadercondition}{{928.10}{2748}{Dropped Support of ServiceLoaderCondition}{section.928.10}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2748}{section.928.10}\\protected@file@percent }\n\\newlabel{what-changed-8}{{928.10}{2748}{What changed?}{section.928.10}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2748}{section.928.10}\\protected@file@percent }\n\\newlabel{who-is-affected-8}{{928.10}{2748}{Who is affected?}{section.928.10}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2748}{section.928.10}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-8}{{928.10}{2748}{How should I update my code?}{section.928.10}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2748}{section.928.10}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-8}{{928.10}{2748}{Why was this change made?}{section.928.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.11}Switched to Use JDK Predicate}{2748}{section.928.11}\\protected@file@percent }\n\\newlabel{switched-to-use-jdk-predicate}{{928.11}{2748}{Switched to Use JDK Predicate}{section.928.11}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2749}{section.928.11}\\protected@file@percent }\n\\newlabel{what-changed-9}{{928.11}{2749}{What changed?}{section.928.11}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2749}{section.928.11}\\protected@file@percent }\n\\newlabel{who-is-affected-9}{{928.11}{2749}{Who is affected?}{section.928.11}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2749}{section.928.11}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-9}{{928.11}{2749}{How should I update my code?}{section.928.11}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2749}{section.928.11}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-9}{{928.11}{2749}{Why was this change made?}{section.928.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.12}Removed Unsafe Functional Interfaces in Package com.liferay.portal.kernel.util}{2749}{section.928.12}\\protected@file@percent }\n\\newlabel{removed-unsafe-functional-interfaces-in-package-com.liferay.portal.kernel.util}{{928.12}{2749}{Removed Unsafe Functional Interfaces in Package com.liferay.portal.kernel.util}{section.928.12}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2749}{section.928.12}\\protected@file@percent }\n\\newlabel{what-changed-10}{{928.12}{2749}{What changed?}{section.928.12}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2749}{section.928.12}\\protected@file@percent }\n\\newlabel{who-is-affected-10}{{928.12}{2749}{Who is affected?}{section.928.12}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2750}{section.928.12}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-10}{{928.12}{2750}{How should I update my code?}{section.928.12}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2750}{section.928.12}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-10}{{928.12}{2750}{Why was this change made?}{section.928.12}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.13}Deprecated NTLM in Portal Distribution}{2750}{section.928.13}\\protected@file@percent }\n\\newlabel{deprecated-ntlm-in-portal-distribution}{{928.13}{2750}{Deprecated NTLM in Portal Distribution}{section.928.13}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2750}{section.928.13}\\protected@file@percent }\n\\newlabel{what-changed-11}{{928.13}{2750}{What changed?}{section.928.13}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2750}{section.928.13}\\protected@file@percent }\n\\newlabel{who-is-affected-11}{{928.13}{2750}{Who is affected?}{section.928.13}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2750}{section.928.13}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-11}{{928.13}{2750}{How should I update my code?}{section.928.13}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2750}{section.928.13}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-11}{{928.13}{2750}{Why was this change made?}{section.928.13}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.14}Deprecated OpenID in Portal Distribution}{2750}{section.928.14}\\protected@file@percent }\n\\newlabel{deprecated-openid-in-portal-distribution}{{928.14}{2750}{Deprecated OpenID in Portal Distribution}{section.928.14}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2750}{section.928.14}\\protected@file@percent }\n\\newlabel{what-changed-12}{{928.14}{2750}{What changed?}{section.928.14}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2751}{section.928.14}\\protected@file@percent }\n\\newlabel{who-is-affected-12}{{928.14}{2751}{Who is affected?}{section.928.14}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2751}{section.928.14}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-12}{{928.14}{2751}{How should I update my code?}{section.928.14}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2751}{section.928.14}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-12}{{928.14}{2751}{Why was this change made?}{section.928.14}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.15}Deprecated Google SSO in Portal Distribution}{2751}{section.928.15}\\protected@file@percent }\n\\newlabel{deprecated-google-sso-in-portal-distribution}{{928.15}{2751}{Deprecated Google SSO in Portal Distribution}{section.928.15}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2751}{section.928.15}\\protected@file@percent }\n\\newlabel{what-changed-13}{{928.15}{2751}{What changed?}{section.928.15}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2751}{section.928.15}\\protected@file@percent }\n\\newlabel{who-is-affected-13}{{928.15}{2751}{Who is affected?}{section.928.15}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2751}{section.928.15}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-13}{{928.15}{2751}{How should I update my code?}{section.928.15}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2751}{section.928.15}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-13}{{928.15}{2751}{Why was this change made?}{section.928.15}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.16}Updated AlloyEditor v2.0 Includes New Major Version of React}{2751}{section.928.16}\\protected@file@percent }\n\\newlabel{updated-alloyeditor-v2.0-includes-new-major-version-of-react}{{928.16}{2751}{Updated AlloyEditor v2.0 Includes New Major Version of React}{section.928.16}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2752}{section.928.16}\\protected@file@percent }\n\\newlabel{what-changed-14}{{928.16}{2752}{What changed?}{section.928.16}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2752}{section.928.16}\\protected@file@percent }\n\\newlabel{who-is-affected-14}{{928.16}{2752}{Who is affected?}{section.928.16}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2752}{section.928.16}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-14}{{928.16}{2752}{How should I update my code?}{section.928.16}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2752}{section.928.16}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-14}{{928.16}{2752}{Why was this change made?}{section.928.16}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.17}Deprecated dl.tabs.visible property}{2752}{section.928.17}\\protected@file@percent }\n\\newlabel{deprecated-dl.tabs.visible-property}{{928.17}{2752}{Deprecated dl.tabs.visible property}{section.928.17}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2752}{section.928.17}\\protected@file@percent }\n\\newlabel{what-changed-15}{{928.17}{2752}{What changed?}{section.928.17}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2752}{section.928.17}\\protected@file@percent }\n\\newlabel{who-is-affected-15}{{928.17}{2752}{Who is affected?}{section.928.17}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2752}{section.928.17}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-15}{{928.17}{2752}{How should I update my code?}{section.928.17}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2752}{section.928.17}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-15}{{928.17}{2752}{Why was this change made?}{section.928.17}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.18}Move the User Menu out of the Product Menu}{2753}{section.928.18}\\protected@file@percent }\n\\newlabel{move-the-user-menu-out-of-the-product-menu}{{928.18}{2753}{Move the User Menu out of the Product Menu}{section.928.18}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2753}{section.928.18}\\protected@file@percent }\n\\newlabel{what-changed-16}{{928.18}{2753}{What changed?}{section.928.18}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2753}{section.928.18}\\protected@file@percent }\n\\newlabel{who-is-affected-16}{{928.18}{2753}{Who is affected?}{section.928.18}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2753}{section.928.18}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-16}{{928.18}{2753}{How should I update my code?}{section.928.18}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2753}{section.928.18}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-16}{{928.18}{2753}{Why was this change made?}{section.928.18}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.19}Removed Hong Kong and Macau from the List of Countries}{2753}{section.928.19}\\protected@file@percent }\n\\newlabel{removed-hong-kong-and-macau-from-the-list-of-countries}{{928.19}{2753}{Removed Hong Kong and Macau from the List of Countries}{section.928.19}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2753}{section.928.19}\\protected@file@percent }\n\\newlabel{what-changed-17}{{928.19}{2753}{What changed?}{section.928.19}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2753}{section.928.19}\\protected@file@percent }\n\\newlabel{who-is-affected-17}{{928.19}{2753}{Who is affected?}{section.928.19}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2753}{section.928.19}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-17}{{928.19}{2753}{How should I update my code?}{section.928.19}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2754}{section.928.19}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-17}{{928.19}{2754}{Why was this change made?}{section.928.19}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.20}JGroups Was Upgraded From 3.6.16 to 4.1.1}{2754}{section.928.20}\\protected@file@percent }\n\\newlabel{jgroups-was-upgraded-from-3.6.16-to-4.1.1}{{928.20}{2754}{JGroups Was Upgraded From 3.6.16 to 4.1.1}{section.928.20}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2754}{section.928.20}\\protected@file@percent }\n\\newlabel{what-changed-18}{{928.20}{2754}{What changed?}{section.928.20}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2754}{section.928.20}\\protected@file@percent }\n\\newlabel{who-is-affected-18}{{928.20}{2754}{Who is affected?}{section.928.20}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2754}{section.928.20}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-18}{{928.20}{2754}{How should I update my code?}{section.928.20}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2754}{section.928.20}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-18}{{928.20}{2754}{Why was this change made?}{section.928.20}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.21}Liferay \\texttt  {AssetEntries\\_AssetCategories} Is No Longer Used}{2754}{section.928.21}\\protected@file@percent }\n\\newlabel{liferay-assetentries_assetcategories-is-no-longer-used}{{928.21}{2754}{\\texorpdfstring {Liferay \\texttt {AssetEntries\\_AssetCategories} Is No Longer Used}{Liferay AssetEntries\\_AssetCategories Is No Longer Used}}{section.928.21}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2754}{section.928.21}\\protected@file@percent }\n\\newlabel{what-changed-19}{{928.21}{2754}{What changed?}{section.928.21}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2754}{section.928.21}\\protected@file@percent }\n\\newlabel{who-is-affected-19}{{928.21}{2754}{Who is affected?}{section.928.21}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2755}{section.928.21}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-19}{{928.21}{2755}{How should I update my code?}{section.928.21}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2755}{section.928.21}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-19}{{928.21}{2755}{Why was this change made?}{section.928.21}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.22}Auto Tagging Must Be Reconfigured Manually}{2755}{section.928.22}\\protected@file@percent }\n\\newlabel{auto-tagging-must-be-reconfigured-manually}{{928.22}{2755}{Auto Tagging Must Be Reconfigured Manually}{section.928.22}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2755}{section.928.22}\\protected@file@percent }\n\\newlabel{what-changed-20}{{928.22}{2755}{What changed?}{section.928.22}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2755}{section.928.22}\\protected@file@percent }\n\\newlabel{who-is-affected-20}{{928.22}{2755}{Who is affected?}{section.928.22}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2756}{section.928.22}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-20}{{928.22}{2756}{How should I update my code?}{section.928.22}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2756}{section.928.22}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-20}{{928.22}{2756}{Why was this change made?}{section.928.22}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.23}Blogs Image Properties Were Moved to System Settings}{2756}{section.928.23}\\protected@file@percent }\n\\newlabel{blogs-image-properties-were-moved-to-system-settings}{{928.23}{2756}{Blogs Image Properties Were Moved to System Settings}{section.928.23}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2756}{section.928.23}\\protected@file@percent }\n\\newlabel{what-changed-21}{{928.23}{2756}{What changed?}{section.928.23}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2756}{section.928.23}\\protected@file@percent }\n\\newlabel{who-is-affected-21}{{928.23}{2756}{Who is affected?}{section.928.23}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2756}{section.928.23}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-21}{{928.23}{2756}{How should I update my code?}{section.928.23}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2756}{section.928.23}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-21}{{928.23}{2756}{Why was this change made?}{section.928.23}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {928.24}Removed Cache Bootstrap Feature}{2756}{section.928.24}\\protected@file@percent }\n\\newlabel{removed-cache-bootstrap-feature}{{928.24}{2756}{Removed Cache Bootstrap Feature}{section.928.24}{}}\n\\@writefile{toc}{\\contentsline {subsection}{What changed?}{2756}{section.928.24}\\protected@file@percent }\n\\newlabel{what-changed-22}{{928.24}{2756}{What changed?}{section.928.24}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Who is affected?}{2757}{section.928.24}\\protected@file@percent }\n\\newlabel{who-is-affected-22}{{928.24}{2757}{Who is affected?}{section.928.24}{}}\n\\@writefile{toc}{\\contentsline {subsection}{How should I update my code?}{2757}{section.928.24}\\protected@file@percent }\n\\newlabel{how-should-i-update-my-code-22}{{928.24}{2757}{How should I update my code?}{section.928.24}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Why was this change made?}{2757}{section.928.24}\\protected@file@percent }\n\\newlabel{why-was-this-change-made-22}{{928.24}{2757}{Why was this change made?}{section.928.24}{}}\n\\gdef \\LT@lxvi {\\LT@entry \n    {3}{121.02634pt}\\LT@entry \n    {3}{107.02176pt}\\LT@entry \n    {1}{55.19775pt}\\LT@entry \n    {3}{125.4974pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {929}CDI Portlet Predefined Beans}{2759}{chapter.929}\\protected@file@percent }\n\\newlabel{cdi-portlet-predefined-beans}{{929}{2759}{CDI Portlet Predefined Beans}{chapter.929}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {929.1}Portlet Request Scoped Beans}{2759}{section.929.1}\\protected@file@percent }\n\\newlabel{portlet-request-scoped-beans}{{929.1}{2759}{Portlet Request Scoped Beans}{section.929.1}{}}\n\\gdef \\LT@lxvii {\\LT@entry \n    {3}{105.13809pt}\\LT@entry \n    {1}{83.6349pt}\\LT@entry \n    {3}{72.01375pt}\\LT@entry \n    {1}{65.72127pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {929.2}Dependent Scoped Beans}{2760}{section.929.2}\\protected@file@percent }\n\\newlabel{dependent-scoped-beans}{{929.2}{2760}{Dependent Scoped Beans}{section.929.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {929.3}Related Topics}{2760}{section.929.3}\\protected@file@percent }\n\\newlabel{related-topics-49}{{929.3}{2760}{Related Topics}{section.929.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {930}Item Selector Criterion and Return Types}{2761}{chapter.930}\\protected@file@percent }\n\\newlabel{item-selector-criterion-and-return-types}{{930}{2761}{Item Selector Criterion and Return Types}{chapter.930}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {930.1}Item Selector Criterion Classes}{2761}{section.930.1}\\protected@file@percent }\n\\newlabel{item-selector-criterion-classes}{{930.1}{2761}{Item Selector Criterion Classes}{section.930.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {930.2}Item Selector Return Type Classes}{2762}{section.930.2}\\protected@file@percent }\n\\newlabel{item-selector-return-type-classes}{{930.2}{2762}{Item Selector Return Type Classes}{section.930.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {931}Java APIs}{2763}{chapter.931}\\protected@file@percent }\n\\newlabel{java-apis}{{931}{2763}{Java APIs}{chapter.931}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {931.1}7.0 Java APIs}{2763}{section.931.1}\\protected@file@percent }\n\\newlabel{java-apis-1}{{931.1}{2763}{7.0 Java APIs}{section.931.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {931.2}Liferay DXP App Java APIs}{2763}{section.931.2}\\protected@file@percent }\n\\newlabel{liferay-dxp-app-java-apis}{{931.2}{2763}{Liferay DXP App Java APIs}{section.931.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {931.3}Forms and Workflow}{2764}{section.931.3}\\protected@file@percent }\n\\newlabel{forms-and-workflow}{{931.3}{2764}{Forms and Workflow}{section.931.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {931.4}Foundation}{2765}{section.931.4}\\protected@file@percent }\n\\newlabel{foundation}{{931.4}{2765}{Foundation}{section.931.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {931.5}Web Experience}{2767}{section.931.5}\\protected@file@percent }\n\\newlabel{web-experience}{{931.5}{2767}{Web Experience}{section.931.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {931.6}JavaScript and CSS}{2768}{section.931.6}\\protected@file@percent }\n\\newlabel{javascript-and-css}{{931.6}{2768}{JavaScript and CSS}{section.931.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {931.7}Descriptor Definitions}{2768}{section.931.7}\\protected@file@percent }\n\\newlabel{descriptor-definitions}{{931.7}{2768}{Descriptor Definitions}{section.931.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {932}Meaningful Schema Versioning}{2769}{chapter.932}\\protected@file@percent }\n\\newlabel{meaningful-schema-versioning}{{932}{2769}{Meaningful Schema Versioning}{chapter.932}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {932.1}Micro change examples}{2770}{section.932.1}\\protected@file@percent }\n\\newlabel{micro-change-examples}{{932.1}{2770}{Micro change examples}{section.932.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {932.2}Minor change examples}{2770}{section.932.2}\\protected@file@percent }\n\\newlabel{minor-change-examples}{{932.2}{2770}{Minor change examples}{section.932.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {932.3}Major change examples}{2770}{section.932.3}\\protected@file@percent }\n\\newlabel{major-change-examples}{{932.3}{2770}{Major change examples}{section.932.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {933}Portlet 3.0 API Opt In}{2771}{chapter.933}\\protected@file@percent }\n\\newlabel{portlet-3.0-api-opt-in}{{933}{2771}{Portlet 3.0 API Opt In}{chapter.933}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {933.1}Standard Portlet \\texttt  {@PortletApplication} Annotation}{2771}{section.933.1}\\protected@file@percent }\n\\newlabel{standard-portlet-portletapplication-annotation}{{933.1}{2771}{\\texorpdfstring {Standard Portlet \\texttt {@PortletApplication} Annotation}{Standard Portlet @PortletApplication Annotation}}{section.933.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {933.2}Liferay MVC Portlet \\texttt  {@Component} Annotation}{2771}{section.933.2}\\protected@file@percent }\n\\newlabel{liferay-mvc-portlet-component-annotation}{{933.2}{2771}{\\texorpdfstring {Liferay MVC Portlet \\texttt {@Component} Annotation}{Liferay MVC Portlet @Component Annotation}}{section.933.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {933.3}\\texttt  {portlet.xml} Descriptor}{2771}{section.933.3}\\protected@file@percent }\n\\newlabel{portlet.xml-descriptor}{{933.3}{2771}{\\texorpdfstring {\\texttt {portlet.xml} Descriptor}{portlet.xml Descriptor}}{section.933.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {934}Portlet Descriptor to OSGi Service Property Map}{2773}{chapter.934}\\protected@file@percent }\n\\newlabel{portlet-descriptor-to-osgi-service-property-map}{{934}{2773}{Portlet Descriptor to OSGi Service Property Map}{chapter.934}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {934.1}Portlet Descriptor Mappings}{2774}{section.934.1}\\protected@file@percent }\n\\newlabel{portlet-descriptor-mappings}{{934.1}{2774}{Portlet Descriptor Mappings}{section.934.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {934.2}Liferay Descriptor Mappings}{2774}{section.934.2}\\protected@file@percent }\n\\newlabel{liferay-descriptor-mappings}{{934.2}{2774}{Liferay Descriptor Mappings}{section.934.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {934.3}Liferay Display}{2775}{section.934.3}\\protected@file@percent }\n\\newlabel{liferay-display}{{934.3}{2775}{Liferay Display}{section.934.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {934.4}Liferay Portlet}{2775}{section.934.4}\\protected@file@percent }\n\\newlabel{liferay-portlet}{{934.4}{2775}{Liferay Portlet}{section.934.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {935}Third Party Packages Portal Exports}{2779}{chapter.935}\\protected@file@percent }\n\\newlabel{third-party-packages-portal-exports}{{935}{2779}{Third Party Packages Portal Exports}{chapter.935}{}}\n\\@setckpt{developer/reference}{\n\\setcounter{page}{2780}\n\\setcounter{equation}{0}\n\\setcounter{enumi}{2}\n\\setcounter{enumii}{5}\n\\setcounter{enumiii}{3}\n\\setcounter{enumiv}{0}\n\\setcounter{footnote}{0}\n\\setcounter{mpfootnote}{0}\n\\setcounter{@memmarkcntra}{-1}\n\\setcounter{storedpagenumber}{1}\n\\setcounter{book}{0}\n\\setcounter{part}{5}\n\\setcounter{chapter}{935}\n\\setcounter{section}{0}\n\\setcounter{subsection}{0}\n\\setcounter{subsubsection}{0}\n\\setcounter{paragraph}{0}\n\\setcounter{subparagraph}{0}\n\\setcounter{@ppsavesec}{0}\n\\setcounter{@ppsaveapp}{0}\n\\setcounter{vslineno}{0}\n\\setcounter{poemline}{0}\n\\setcounter{modulo@vs}{0}\n\\setcounter{memfvsline}{0}\n\\setcounter{verse}{0}\n\\setcounter{chrsinstr}{0}\n\\setcounter{poem}{0}\n\\setcounter{newflo@tctr}{4}\n\\setcounter{@contsubnum}{0}\n\\setcounter{section@level}{0}\n\\setcounter{maxsecnumdepth}{1}\n\\setcounter{sidefootnote}{0}\n\\setcounter{pagenote}{0}\n\\setcounter{pagenoteshadow}{0}\n\\setcounter{memfbvline}{0}\n\\setcounter{bvlinectr}{0}\n\\setcounter{cp@cntr}{0}\n\\setcounter{ism@mctr}{0}\n\\setcounter{xsm@mctr}{0}\n\\setcounter{csm@mctr}{0}\n\\setcounter{ksm@mctr}{0}\n\\setcounter{xksm@mctr}{0}\n\\setcounter{cksm@mctr}{0}\n\\setcounter{msm@mctr}{0}\n\\setcounter{xmsm@mctr}{0}\n\\setcounter{cmsm@mctr}{0}\n\\setcounter{bsm@mctr}{0}\n\\setcounter{workm@mctr}{0}\n\\setcounter{sheetsequence}{2852}\n\\setcounter{lastsheet}{2851}\n\\setcounter{lastpage}{2779}\n\\setcounter{figure}{0}\n\\setcounter{lofdepth}{1}\n\\setcounter{table}{0}\n\\setcounter{lotdepth}{1}\n\\setcounter{Item}{2549}\n\\setcounter{Hfootnote}{7}\n\\setcounter{bookmark@seq@number}{0}\n\\setcounter{memhycontfloat}{0}\n\\setcounter{Hpagenote}{0}\n\\setcounter{r@tfl@t}{0}\n\\setcounter{float@type}{4}\n\\setcounter{LT@tables}{67}\n\\setcounter{LT@chunks}{3}\n\\setcounter{parentequation}{0}\n\\setcounter{FancyVerbLine}{0}\n}\n"
  },
  {
    "path": "book/developer/reference.tex",
    "content": "\\chapter{Developer Reference}\\label{developer-reference}\n\nThis developer reference contains lists of options for various APIs. The\nactual API reference is stored on\n\\href{https://docs.liferay.com}{docs.liferay.com}. Here, you'll find\nhigher level descriptions of those APIs, lists of tag libraries,\ndescriptions of Gradle and Maven plugins, and much more.\n\nOne highlight here is a complete description of Liferay's development\ntooling. This includes not only our Blade CLI (a tool that bridges the\ngap between Gradle and Maven, bringing archetype-like functionality to\nLiferay Gradle projects), but also our plugins for IntelliJ and Eclipse,\nnot to mention our Maven or Gradle-based Liferay Workspace. It also\nincludes a complete description of our JS Generator, which helps\nfront-end developers create pure JavaScript widgets.\n\n\\chapter{Classes Moved From\nportal-service.jar}\\label{classes-moved-from-portal-service.jar}\n\nTo leverage the benefits of modularization, many classes from\nportal-service.jar have been moved into application and framework API\nmodules. The table below provides details about these classes and the\nmodules they've moved to. Package changes and each module's group,\nartifact ID, and version are listed, to facilitate configuring\ndependencies.\n\nClasses Moved from portal-service.jar to Modules\n\nThis information was generated based on comparing classes in\nliferay-portal-src-6.2-ee-sp20 to classes in liferay-dxp-src-7.2.10-ga1.\n\nClass\n\nPackage\n\nGroup ID, Artifact ID, and Version\n\nActionHandler\n\nOld: com.liferay.portal.kernel.mobile.device.rulegroup.action New:\ncom.liferay.mobile.device.rules.action\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nActionHandlerManager\n\nOld: com.liferay.portal.kernel.mobile.device.rulegroup New:\ncom.liferay.mobile.device.rules.action\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nActionHandlerManagerUtil\n\nOld: com.liferay.portal.kernel.mobile.device.rulegroup New:\ncom.liferay.mobile.device.rules.action\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nActionTypeException\n\nOld: com.liferay.portlet.mobiledevicerules New:\ncom.liferay.mobile.device.rules.exception\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nAlternateKeywordQueryHitsProcessor\n\nOld: com.liferay.portal.kernel.search New:\ncom.liferay.portal.search.internal.hits\n\ncom.liferay com.liferay.portal.search 6.0.14\n\nArticleContentException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleContentSizeException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleCreateDateComparator\n\nOld: com.liferay.portlet.journal.util.comparator New:\ncom.liferay.journal.util.comparator\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleDisplayDateComparator\n\nOld: com.liferay.portlet.journal.util.comparator New:\ncom.liferay.journal.util.comparator\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleDisplayDateException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleExpirationDateException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleIDComparator\n\nOld: com.liferay.portlet.journal.util.comparator New:\ncom.liferay.journal.util.comparator\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleIdException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleModifiedDateComparator\n\nOld: com.liferay.portlet.journal.util.comparator New:\ncom.liferay.journal.util.comparator\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleResourcePKComparator\n\nOld: com.liferay.portlet.journal.util.comparator New:\ncom.liferay.journal.util.comparator\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleReviewDateComparator\n\nOld: com.liferay.portlet.journal.util.comparator New:\ncom.liferay.journal.util.comparator\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleReviewDateException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleSmallImageNameException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleSmallImageSizeException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleTitleComparator\n\nOld: com.liferay.portlet.journal.util.comparator New:\ncom.liferay.journal.util.comparator\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleTitleException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleVersionComparator\n\nOld: com.liferay.portlet.journal.util.comparator New:\ncom.liferay.journal.util.comparator\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nArticleVersionException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nAuditMessageProcessor\n\nOld: com.liferay.portal.kernel.audit New:\ncom.liferay.portal.security.audit\n\ncom.liferay com.liferay.portal.security.audit.api 4.0.5\n\nAutoDeleteFileInputStream\n\nOld: com.liferay.portal.kernel.io New: com.liferay.petra.io\n\ncom.liferay com.liferay.petra.io 3.0.2\n\nAverageStatistics\n\nOld: com.liferay.portal.kernel.monitoring.statistics New:\ncom.liferay.portal.monitoring.internal.statistics\n\ncom.liferay com.liferay.portal.monitoring 7.0.7\n\nBackgroundTaskLocalService\n\nOld: com.liferay.portal.service New:\ncom.liferay.portal.background.task.service\n\ncom.liferay com.liferay.portal.background.task.api 4.1.3\n\nBackgroundTaskLocalServiceUtil\n\nOld: com.liferay.portal.service New:\ncom.liferay.portal.background.task.service\n\ncom.liferay com.liferay.portal.background.task.api 4.1.3\n\nBackgroundTaskLocalServiceWrapper\n\nOld: com.liferay.portal.service New:\ncom.liferay.portal.background.task.service\n\ncom.liferay com.liferay.portal.background.task.api 4.1.3\n\nBackgroundTaskModel\n\nOld: com.liferay.portal.model New:\ncom.liferay.portal.background.task.model\n\ncom.liferay com.liferay.portal.background.task.api 4.1.3\n\nBackgroundTaskPersistence\n\nOld: com.liferay.portal.service.persistence New:\ncom.liferay.portal.background.task.service.persistence\n\ncom.liferay com.liferay.portal.background.task.api 4.1.3\n\nBackgroundTaskService\n\nOld: com.liferay.portal.service New:\ncom.liferay.portal.background.task.service\n\ncom.liferay com.liferay.portal.background.task.api 4.1.3\n\nBackgroundTaskServiceUtil\n\nOld: com.liferay.portal.service New:\ncom.liferay.portal.background.task.service\n\ncom.liferay com.liferay.portal.background.task.api 4.1.3\n\nBackgroundTaskServiceWrapper\n\nOld: com.liferay.portal.service New:\ncom.liferay.portal.background.task.service\n\ncom.liferay com.liferay.portal.background.task.api 4.1.3\n\nBackgroundTaskSoap\n\nOld: com.liferay.portal.model New:\ncom.liferay.portal.background.task.model\n\ncom.liferay com.liferay.portal.background.task.api 4.1.3\n\nBackgroundTaskUtil\n\nOld: com.liferay.portal.service.persistence New:\ncom.liferay.portal.background.task.service.persistence\n\ncom.liferay com.liferay.portal.background.task.api 4.1.3\n\nBackgroundTaskWrapper\n\nOld: com.liferay.portal.model New:\ncom.liferay.portal.background.task.model\n\ncom.liferay com.liferay.portal.background.task.api 4.1.3\n\nBannedUserException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nBaseCmisRepository\n\nOld: com.liferay.portal.kernel.repository.cmis New:\ncom.liferay.document.library.repository.cmis\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nBaseCmisSearchQueryBuilder\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nBaseDDLExporter\n\nOld: com.liferay.portlet.dynamicdatalists.util New:\ncom.liferay.dynamic.data.lists.internal.exporter\n\ncom.liferay com.liferay.dynamic.data.lists.service 3.0.12\n\nBaseDDMDisplay\n\nOld: com.liferay.portlet.dynamicdatamapping.util New:\ncom.liferay.dynamic.data.mapping.util\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nBaseFieldRenderer\n\nOld: com.liferay.portlet.dynamicdatamapping.storage New:\ncom.liferay.dynamic.data.mapping.storage\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nBaseScriptingExecutor\n\nOld: com.liferay.portal.kernel.scripting New:\ncom.liferay.portal.scripting\n\ncom.liferay com.liferay.portal.scripting.api 3.0.3\n\nBaseStatistics\n\nOld: com.liferay.portal.kernel.monitoring.statistics New:\ncom.liferay.portal.monitoring.internal.statistics\n\ncom.liferay com.liferay.portal.monitoring 7.0.7\n\nBaseStorageAdapter\n\nOld: com.liferay.portlet.dynamicdatamapping.storage New:\ncom.liferay.dynamic.data.mapping.storage\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nBlockingPortalCache\n\nOld: com.liferay.portal.kernel.cache New: com.liferay.portal.cache\n\ncom.liferay com.liferay.portal.cache.api 2.0.1\n\nBlogsEntry\n\nOld: com.liferay.portlet.blogs.model New: com.liferay.blogs.model\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsEntryFinder\n\nOld: com.liferay.portlet.blogs.service.persistence New:\ncom.liferay.blogs.service.persistence\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsEntryLocalService\n\nOld: com.liferay.portlet.blogs.service New: com.liferay.blogs.service\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsEntryLocalServiceUtil\n\nOld: com.liferay.portlet.blogs.service New: com.liferay.blogs.service\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsEntryLocalServiceWrapper\n\nOld: com.liferay.portlet.blogs.service New: com.liferay.blogs.service\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsEntryModel\n\nOld: com.liferay.portlet.blogs.model New: com.liferay.blogs.model\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsEntryPersistence\n\nOld: com.liferay.portlet.blogs.service.persistence New:\ncom.liferay.blogs.service.persistence\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsEntryService\n\nOld: com.liferay.portlet.blogs.service New: com.liferay.blogs.service\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsEntryServiceUtil\n\nOld: com.liferay.portlet.blogs.service New: com.liferay.blogs.service\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsEntryServiceWrapper\n\nOld: com.liferay.portlet.blogs.service New: com.liferay.blogs.service\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsEntrySoap\n\nOld: com.liferay.portlet.blogs.model New: com.liferay.blogs.model\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsEntryUtil\n\nOld: com.liferay.portlet.blogs.service.persistence New:\ncom.liferay.blogs.service.persistence\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsEntryWrapper\n\nOld: com.liferay.portlet.blogs.model New: com.liferay.blogs.model\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsStatsUser\n\nOld: com.liferay.portlet.blogs.model New: com.liferay.blogs.model\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsStatsUserFinder\n\nOld: com.liferay.portlet.blogs.service.persistence New:\ncom.liferay.blogs.service.persistence\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsStatsUserLocalService\n\nOld: com.liferay.portlet.blogs.service New: com.liferay.blogs.service\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsStatsUserLocalServiceUtil\n\nOld: com.liferay.portlet.blogs.service New: com.liferay.blogs.service\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsStatsUserLocalServiceWrapper\n\nOld: com.liferay.portlet.blogs.service New: com.liferay.blogs.service\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsStatsUserModel\n\nOld: com.liferay.portlet.blogs.model New: com.liferay.blogs.model\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsStatsUserPersistence\n\nOld: com.liferay.portlet.blogs.service.persistence New:\ncom.liferay.blogs.service.persistence\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsStatsUserSoap\n\nOld: com.liferay.portlet.blogs.model New: com.liferay.blogs.model\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsStatsUserUtil\n\nOld: com.liferay.portlet.blogs.service.persistence New:\ncom.liferay.blogs.service.persistence\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBlogsStatsUserWrapper\n\nOld: com.liferay.portlet.blogs.model New: com.liferay.blogs.model\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nBookmarksEntry\n\nOld: com.liferay.portlet.bookmarks.model New:\ncom.liferay.bookmarks.model\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksEntryFinder\n\nOld: com.liferay.portlet.bookmarks.service.persistence New:\ncom.liferay.bookmarks.service.persistence\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksEntryLocalService\n\nOld: com.liferay.portlet.bookmarks.service New:\ncom.liferay.bookmarks.service\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksEntryLocalServiceUtil\n\nOld: com.liferay.portlet.bookmarks.service New:\ncom.liferay.bookmarks.service\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksEntryLocalServiceWrapper\n\nOld: com.liferay.portlet.bookmarks.service New:\ncom.liferay.bookmarks.service\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksEntryModel\n\nOld: com.liferay.portlet.bookmarks.model New:\ncom.liferay.bookmarks.model\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksEntryPersistence\n\nOld: com.liferay.portlet.bookmarks.service.persistence New:\ncom.liferay.bookmarks.service.persistence\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksEntryService\n\nOld: com.liferay.portlet.bookmarks.service New:\ncom.liferay.bookmarks.service\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksEntryServiceUtil\n\nOld: com.liferay.portlet.bookmarks.service New:\ncom.liferay.bookmarks.service\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksEntryServiceWrapper\n\nOld: com.liferay.portlet.bookmarks.service New:\ncom.liferay.bookmarks.service\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksEntrySoap\n\nOld: com.liferay.portlet.bookmarks.model New:\ncom.liferay.bookmarks.model\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksEntryUtil\n\nOld: com.liferay.portlet.bookmarks.service.persistence New:\ncom.liferay.bookmarks.service.persistence\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksEntryWrapper\n\nOld: com.liferay.portlet.bookmarks.model New:\ncom.liferay.bookmarks.model\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolder\n\nOld: com.liferay.portlet.bookmarks.model New:\ncom.liferay.bookmarks.model\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderConstants\n\nOld: com.liferay.portlet.bookmarks.model New:\ncom.liferay.bookmarks.model\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderFinder\n\nOld: com.liferay.portlet.bookmarks.service.persistence New:\ncom.liferay.bookmarks.service.persistence\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderLocalService\n\nOld: com.liferay.portlet.bookmarks.service New:\ncom.liferay.bookmarks.service\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderLocalServiceUtil\n\nOld: com.liferay.portlet.bookmarks.service New:\ncom.liferay.bookmarks.service\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderLocalServiceWrapper\n\nOld: com.liferay.portlet.bookmarks.service New:\ncom.liferay.bookmarks.service\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderModel\n\nOld: com.liferay.portlet.bookmarks.model New:\ncom.liferay.bookmarks.model\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderPersistence\n\nOld: com.liferay.portlet.bookmarks.service.persistence New:\ncom.liferay.bookmarks.service.persistence\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderService\n\nOld: com.liferay.portlet.bookmarks.service New:\ncom.liferay.bookmarks.service\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderServiceUtil\n\nOld: com.liferay.portlet.bookmarks.service New:\ncom.liferay.bookmarks.service\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderServiceWrapper\n\nOld: com.liferay.portlet.bookmarks.service New:\ncom.liferay.bookmarks.service\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderSoap\n\nOld: com.liferay.portlet.bookmarks.model New:\ncom.liferay.bookmarks.model\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderUtil\n\nOld: com.liferay.portlet.bookmarks.service.persistence New:\ncom.liferay.bookmarks.service.persistence\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nBookmarksFolderWrapper\n\nOld: com.liferay.portlet.bookmarks.model New:\ncom.liferay.bookmarks.model\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nByteArrayReportResultContainer\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nCMISBetweenExpression\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISConjunction\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISContainsExpression\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISContainsNotExpression\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISContainsValueExpression\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISCriterion\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISDisjunction\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISFullTextConjunction\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISInFolderExpression\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISInTreeExpression\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISJunction\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISNotExpression\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISParameterValueUtil\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISRepositoryHandler\n\nOld: com.liferay.portal.kernel.repository.cmis New:\ncom.liferay.document.library.repository.cmis\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISRepositoryUtil\n\nOld: com.liferay.portal.kernel.repository.cmis New:\ncom.liferay.document.library.repository.cmis.internal\n\ncom.liferay com.liferay.document.library.repository.cmis.impl 4.0.8\n\nCMISSearchQueryBuilder\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISSimpleExpression\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCMISSimpleExpressionOperator\n\nOld: com.liferay.portal.kernel.repository.cmis.search New:\ncom.liferay.document.library.repository.cmis.search\n\ncom.liferay com.liferay.document.library.repository.cmis.api 3.0.7\n\nCharPool\n\nOld: com.liferay.portal.kernel.util New: com.liferay.petra.string\n\ncom.liferay com.liferay.petra.string 3.0.1\n\nCharsetDecoderUtil\n\nOld: com.liferay.portal.kernel.nio.charset New: com.liferay.petra.nio\n\ncom.liferay com.liferay.petra.nio 3.0.1\n\nCharsetEncoderUtil\n\nOld: com.liferay.portal.kernel.nio.charset New: com.liferay.petra.nio\n\ncom.liferay com.liferay.petra.nio 3.0.1\n\nClassLoaderPool\n\nOld: com.liferay.portal.kernel.util New: com.liferay.petra.lang\n\ncom.liferay com.liferay.petra.lang 3.0.1\n\nClassPathUtil\n\nOld: com.liferay.portal.kernel.process New: com.liferay.petra.process\n\ncom.liferay com.liferay.petra.process 3.0.4\n\nClassResolverUtil\n\nOld: com.liferay.portal.kernel.util New: com.liferay.petra.lang\n\ncom.liferay com.liferay.petra.lang 3.0.1\n\nCollatedSpellCheckHitsProcessor\n\nOld: com.liferay.portal.kernel.search New:\ncom.liferay.portal.search.internal.hits\n\ncom.liferay com.liferay.portal.search 6.0.14\n\nCompoundSessionIdServletRequest\n\nOld: com.liferay.portal.kernel.servlet.filters.compoundsessionid New:\ncom.liferay.portal.compound.session.id.internal\n\ncom.liferay com.liferay.portal.compound.session.id 4.0.5\n\nCondition\n\nOld: com.liferay.portlet.dynamicdatamapping.storage.query New:\ncom.liferay.adaptive.media.image.media.query\n\ncom.liferay com.liferay.adaptive.media.image.api 3.0.3\n\nConsumerOutputProcessor\n\nOld: com.liferay.portal.kernel.process New: com.liferay.petra.process\n\ncom.liferay com.liferay.petra.process 3.0.4\n\nContactConverterKeys\n\nOld: com.liferay.portal.security.ldap New:\ncom.liferay.portal.security.ldap\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nContentException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nContentNameException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nContextClassloaderReportDesignRetriever\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nCountStatistics\n\nOld: com.liferay.portal.kernel.monitoring.statistics New:\ncom.liferay.portal.monitoring.internal.statistics\n\ncom.liferay com.liferay.portal.monitoring 7.0.7\n\nDDL\n\nOld: com.liferay.portlet.dynamicdatalists.util New:\ncom.liferay.dynamic.data.lists.util\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLExporter\n\nOld: com.liferay.portlet.dynamicdatalists.util New:\ncom.liferay.dynamic.data.lists.exporter\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLExporterFactory\n\nOld: com.liferay.portlet.dynamicdatalists.util New:\ncom.liferay.dynamic.data.lists.exporter\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecord\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordConstants\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordFinder\n\nOld: com.liferay.portlet.dynamicdatalists.service.persistence New:\ncom.liferay.dynamic.data.lists.service.persistence\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordLocalService\n\nOld: com.liferay.portlet.dynamicdatalists.service New:\ncom.liferay.dynamic.data.lists.service\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordLocalServiceUtil\n\nOld: com.liferay.portlet.dynamicdatalists.service New:\ncom.liferay.dynamic.data.lists.service\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordLocalServiceWrapper\n\nOld: com.liferay.portlet.dynamicdatalists.service New:\ncom.liferay.dynamic.data.lists.service\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordModel\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordPersistence\n\nOld: com.liferay.portlet.dynamicdatalists.service.persistence New:\ncom.liferay.dynamic.data.lists.service.persistence\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordService\n\nOld: com.liferay.portlet.dynamicdatalists.service New:\ncom.liferay.dynamic.data.lists.service\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordServiceUtil\n\nOld: com.liferay.portlet.dynamicdatalists.service New:\ncom.liferay.dynamic.data.lists.service\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordServiceWrapper\n\nOld: com.liferay.portlet.dynamicdatalists.service New:\ncom.liferay.dynamic.data.lists.service\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSet\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetConstants\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetFinder\n\nOld: com.liferay.portlet.dynamicdatalists.service.persistence New:\ncom.liferay.dynamic.data.lists.service.persistence\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetLocalService\n\nOld: com.liferay.portlet.dynamicdatalists.service New:\ncom.liferay.dynamic.data.lists.service\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetLocalServiceUtil\n\nOld: com.liferay.portlet.dynamicdatalists.service New:\ncom.liferay.dynamic.data.lists.service\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetLocalServiceWrapper\n\nOld: com.liferay.portlet.dynamicdatalists.service New:\ncom.liferay.dynamic.data.lists.service\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetModel\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetPersistence\n\nOld: com.liferay.portlet.dynamicdatalists.service.persistence New:\ncom.liferay.dynamic.data.lists.service.persistence\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetService\n\nOld: com.liferay.portlet.dynamicdatalists.service New:\ncom.liferay.dynamic.data.lists.service\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetServiceUtil\n\nOld: com.liferay.portlet.dynamicdatalists.service New:\ncom.liferay.dynamic.data.lists.service\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetServiceWrapper\n\nOld: com.liferay.portlet.dynamicdatalists.service New:\ncom.liferay.dynamic.data.lists.service\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetSoap\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetUtil\n\nOld: com.liferay.portlet.dynamicdatalists.service.persistence New:\ncom.liferay.dynamic.data.lists.service.persistence\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSetWrapper\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordSoap\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordUtil\n\nOld: com.liferay.portlet.dynamicdatalists.service.persistence New:\ncom.liferay.dynamic.data.lists.service.persistence\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordVersion\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordVersionModel\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordVersionPersistence\n\nOld: com.liferay.portlet.dynamicdatalists.service.persistence New:\ncom.liferay.dynamic.data.lists.service.persistence\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordVersionSoap\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordVersionUtil\n\nOld: com.liferay.portlet.dynamicdatalists.service.persistence New:\ncom.liferay.dynamic.data.lists.service.persistence\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordVersionVersionComparator\n\nOld: com.liferay.portlet.dynamicdatalists.util.comparator New:\ncom.liferay.dynamic.data.lists.util.comparator\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordVersionWrapper\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDLRecordWrapper\n\nOld: com.liferay.portlet.dynamicdatalists.model New:\ncom.liferay.dynamic.data.lists.model\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nDDM\n\nOld: com.liferay.portlet.dynamicdatamapping.util New:\ncom.liferay.dynamic.data.mapping.util\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMContent\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMContentLocalService\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMContentLocalServiceUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMContentLocalServiceWrapper\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMContentModel\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMContentPersistence\n\nOld: com.liferay.portlet.dynamicdatamapping.service.persistence New:\ncom.liferay.dynamic.data.mapping.service.persistence\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMContentSoap\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMContentUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.service.persistence New:\ncom.liferay.dynamic.data.mapping.service.persistence\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMContentWrapper\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMDisplay\n\nOld: com.liferay.portlet.dynamicdatamapping.util New:\ncom.liferay.dynamic.data.mapping.util\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMDisplayRegistry\n\nOld: com.liferay.portlet.dynamicdatamapping.util New:\ncom.liferay.dynamic.data.mapping.util\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMIndexer\n\nOld: com.liferay.portlet.dynamicdatamapping.util New:\ncom.liferay.dynamic.data.mapping.util\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMIndexerUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.util New:\ncom.liferay.asset.list.internal.dynamic.data.mapping.util\n\ncom.liferay com.liferay.asset.list.service 1.0.11\n\nDDMStorageLink\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStorageLinkLocalService\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStorageLinkLocalServiceUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStorageLinkLocalServiceWrapper\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStorageLinkModel\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStorageLinkPersistence\n\nOld: com.liferay.portlet.dynamicdatamapping.service.persistence New:\ncom.liferay.dynamic.data.mapping.service.persistence\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStorageLinkSoap\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStorageLinkUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.service.persistence New:\ncom.liferay.dynamic.data.mapping.service.persistence\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStorageLinkWrapper\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureConstants\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureFinder\n\nOld: com.liferay.portlet.dynamicdatamapping.service.persistence New:\ncom.liferay.dynamic.data.mapping.service.persistence\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureLinkLocalService\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureLinkLocalServiceUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureLinkLocalServiceWrapper\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureLinkModel\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureLinkPersistence\n\nOld: com.liferay.portlet.dynamicdatamapping.service.persistence New:\ncom.liferay.dynamic.data.mapping.service.persistence\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureLinkSoap\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureLinkUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.service.persistence New:\ncom.liferay.dynamic.data.mapping.service.persistence\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureLinkWrapper\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureLocalService\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureLocalServiceUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureLocalServiceWrapper\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureModel\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructurePersistence\n\nOld: com.liferay.portlet.dynamicdatamapping.service.persistence New:\ncom.liferay.dynamic.data.mapping.service.persistence\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureService\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureServiceUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureServiceWrapper\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureSoap\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.service.persistence New:\ncom.liferay.dynamic.data.mapping.service.persistence\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMStructureWrapper\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateConstants\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateFinder\n\nOld: com.liferay.portlet.dynamicdatamapping.service.persistence New:\ncom.liferay.dynamic.data.mapping.service.persistence\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateHelper\n\nOld: com.liferay.portlet.dynamicdatamapping.util New:\ncom.liferay.dynamic.data.mapping.util\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateLocalService\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateLocalServiceUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateLocalServiceWrapper\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateModel\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplatePersistence\n\nOld: com.liferay.portlet.dynamicdatamapping.service.persistence New:\ncom.liferay.dynamic.data.mapping.service.persistence\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateService\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateServiceUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateServiceWrapper\n\nOld: com.liferay.portlet.dynamicdatamapping.service New:\ncom.liferay.dynamic.data.mapping.service\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateSoap\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.service.persistence New:\ncom.liferay.dynamic.data.mapping.service.persistence\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMTemplateWrapper\n\nOld: com.liferay.portlet.dynamicdatamapping.model New:\ncom.liferay.dynamic.data.mapping.model\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMUtil\n\nOld: com.liferay.portlet.dynamicdatamapping.util New:\ncom.liferay.dynamic.data.mapping.util\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDDMXML\n\nOld: com.liferay.portlet.dynamicdatamapping.util New:\ncom.liferay.dynamic.data.mapping.util\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nDLContent\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.content.model\n\ncom.liferay com.liferay.document.library.content.api 2.0.3\n\nDLContentDataBlobModel\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.content.model\n\ncom.liferay com.liferay.document.library.content.api 2.0.3\n\nDLContentLocalService\n\nOld: com.liferay.portlet.documentlibrary.service New:\ncom.liferay.document.library.content.service\n\ncom.liferay com.liferay.document.library.content.api 2.0.3\n\nDLContentLocalServiceUtil\n\nOld: com.liferay.portlet.documentlibrary.service New:\ncom.liferay.document.library.content.service\n\ncom.liferay com.liferay.document.library.content.api 2.0.3\n\nDLContentLocalServiceWrapper\n\nOld: com.liferay.portlet.documentlibrary.service New:\ncom.liferay.document.library.content.service\n\ncom.liferay com.liferay.document.library.content.api 2.0.3\n\nDLContentModel\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.content.model\n\ncom.liferay com.liferay.document.library.content.api 2.0.3\n\nDLContentPersistence\n\nOld: com.liferay.portlet.documentlibrary.service.persistence New:\ncom.liferay.document.library.content.service.persistence\n\ncom.liferay com.liferay.document.library.content.api 2.0.3\n\nDLContentSoap\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.content.model\n\ncom.liferay com.liferay.document.library.content.api 2.0.3\n\nDLContentUtil\n\nOld: com.liferay.portlet.documentlibrary.service.persistence New:\ncom.liferay.document.library.content.service.persistence\n\ncom.liferay com.liferay.document.library.content.api 2.0.3\n\nDLContentVersionComparator\n\nOld: com.liferay.portlet.documentlibrary.util.comparator New:\ncom.liferay.document.library.content.service.util.comparator\n\ncom.liferay com.liferay.document.library.content.service 2.0.3\n\nDLContentWrapper\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.content.model\n\ncom.liferay com.liferay.document.library.content.api 2.0.3\n\nDLFileRank\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.file.rank.model\n\ncom.liferay com.liferay.document.library.file.rank.api 2.0.3\n\nDLFileRankFinder\n\nOld: com.liferay.portlet.documentlibrary.service.persistence New:\ncom.liferay.document.library.file.rank.service.persistence\n\ncom.liferay com.liferay.document.library.file.rank.api 2.0.3\n\nDLFileRankLocalService\n\nOld: com.liferay.portlet.documentlibrary.service New:\ncom.liferay.document.library.file.rank.service\n\ncom.liferay com.liferay.document.library.file.rank.api 2.0.3\n\nDLFileRankLocalServiceUtil\n\nOld: com.liferay.portlet.documentlibrary.service New:\ncom.liferay.document.library.file.rank.service\n\ncom.liferay com.liferay.document.library.file.rank.api 2.0.3\n\nDLFileRankLocalServiceWrapper\n\nOld: com.liferay.portlet.documentlibrary.service New:\ncom.liferay.document.library.file.rank.service\n\ncom.liferay com.liferay.document.library.file.rank.api 2.0.3\n\nDLFileRankModel\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.file.rank.model\n\ncom.liferay com.liferay.document.library.file.rank.api 2.0.3\n\nDLFileRankPersistence\n\nOld: com.liferay.portlet.documentlibrary.service.persistence New:\ncom.liferay.document.library.file.rank.service.persistence\n\ncom.liferay com.liferay.document.library.file.rank.api 2.0.3\n\nDLFileRankSoap\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.file.rank.model\n\ncom.liferay com.liferay.document.library.file.rank.api 2.0.3\n\nDLFileRankUtil\n\nOld: com.liferay.portlet.documentlibrary.service.persistence New:\ncom.liferay.document.library.file.rank.service.persistence\n\ncom.liferay com.liferay.document.library.file.rank.api 2.0.3\n\nDLFileRankWrapper\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.file.rank.model\n\ncom.liferay com.liferay.document.library.file.rank.api 2.0.3\n\nDLSyncConstants\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.sync.constants\n\ncom.liferay com.liferay.document.library.sync.api 2.0.3\n\nDLSyncEvent\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.sync.model\n\ncom.liferay com.liferay.document.library.sync.api 2.0.3\n\nDLSyncEventLocalService\n\nOld: com.liferay.portlet.documentlibrary.service New:\ncom.liferay.document.library.sync.service\n\ncom.liferay com.liferay.document.library.sync.api 2.0.3\n\nDLSyncEventLocalServiceUtil\n\nOld: com.liferay.portlet.documentlibrary.service New:\ncom.liferay.document.library.sync.service\n\ncom.liferay com.liferay.document.library.sync.api 2.0.3\n\nDLSyncEventLocalServiceWrapper\n\nOld: com.liferay.portlet.documentlibrary.service New:\ncom.liferay.document.library.sync.service\n\ncom.liferay com.liferay.document.library.sync.api 2.0.3\n\nDLSyncEventModel\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.sync.model\n\ncom.liferay com.liferay.document.library.sync.api 2.0.3\n\nDLSyncEventPersistence\n\nOld: com.liferay.portlet.documentlibrary.service.persistence New:\ncom.liferay.document.library.sync.service.persistence\n\ncom.liferay com.liferay.document.library.sync.api 2.0.3\n\nDLSyncEventSoap\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.sync.model\n\ncom.liferay com.liferay.document.library.sync.api 2.0.3\n\nDLSyncEventUtil\n\nOld: com.liferay.portlet.documentlibrary.service.persistence New:\ncom.liferay.document.library.sync.service.persistence\n\ncom.liferay com.liferay.document.library.sync.api 2.0.3\n\nDLSyncEventWrapper\n\nOld: com.liferay.portlet.documentlibrary.model New:\ncom.liferay.document.library.sync.model\n\ncom.liferay com.liferay.document.library.sync.api 2.0.3\n\nDatabase\n\nOld: com.liferay.portal.kernel.util New:\ncom.liferay.portal.tools.db.upgrade.client\n\ncom.liferay com.liferay.portal.tools.db.upgrade.client 3.0.0\n\nDefaultAttributesTransformer\n\nOld: com.liferay.portal.security.ldap New:\ncom.liferay.portal.security.ldap.internal\n\ncom.liferay com.liferay.portal.security.ldap.impl 2.0.5\n\nDefaultMessageBus\n\nOld: com.liferay.portal.kernel.messaging New:\ncom.liferay.portal.messaging.internal\n\ncom.liferay com.liferay.portal.messaging 6.0.5\n\nDefaultSingleDestinationMessageSender\n\nOld: com.liferay.portal.kernel.messaging.sender New:\ncom.liferay.portal.messaging.internal.sender\n\ncom.liferay com.liferay.portal.messaging 6.0.5\n\nDefaultSingleDestinationSynchronousMessageSender\n\nOld: com.liferay.portal.kernel.messaging.sender New:\ncom.liferay.portal.messaging.internal.sender\n\ncom.liferay com.liferay.portal.messaging 6.0.5\n\nDefaultSynchronousMessageSender\n\nOld: com.liferay.portal.kernel.messaging.sender New:\ncom.liferay.portal.messaging.internal.sender\n\ncom.liferay com.liferay.portal.messaging 6.0.5\n\nDeleteFileFinalizeAction\n\nOld: com.liferay.portal.kernel.memory New: com.liferay.petra.memory\n\ncom.liferay com.liferay.petra.memory 3.0.1\n\nDestinationStatisticsManager\n\nOld: com.liferay.portal.kernel.messaging.jmx New:\ncom.liferay.portal.messaging.internal.jmx\n\ncom.liferay com.liferay.portal.messaging 6.0.5\n\nDestinationStatisticsManagerMBean\n\nOld: com.liferay.portal.kernel.messaging.jmx New:\ncom.liferay.portal.messaging.internal.jmx\n\ncom.liferay com.liferay.portal.messaging 6.0.5\n\nDirectSynchronousMessageSender\n\nOld: com.liferay.portal.kernel.messaging.sender New:\ncom.liferay.portal.messaging.internal.sender\n\ncom.liferay com.liferay.portal.messaging 6.0.5\n\nDummy\n\nOld: com.liferay.portal.model New:\ncom.liferay.exportimport.test.util.model\n\ncom.liferay com.liferay.exportimport.test.util 2.0.6\n\nDummyContext\n\nOld: com.liferay.portal.kernel.ldap New:\ncom.liferay.portal.security.ldap.dummy\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nDummyDirContext\n\nOld: com.liferay.portal.kernel.ldap New:\ncom.liferay.portal.security.ldap.dummy\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nDummyFinalizeAction\n\nOld: com.liferay.portal.kernel.memory New: com.liferay.petra.memory\n\ncom.liferay com.liferay.petra.memory 3.0.1\n\nDuplicateArticleIdException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nDuplicateFeedIdException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nDuplicateLDAPServerNameException\n\nOld: com.liferay.portal.kernel.ldap New:\ncom.liferay.portal.security.ldap\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nDuplicateNodeNameException\n\nOld: com.liferay.portlet.wiki New: com.liferay.wiki.exception\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nDuplicatePageException\n\nOld: com.liferay.portlet.wiki New: com.liferay.wiki.exception\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nDuplicateRuleGroupInstanceException\n\nOld: com.liferay.portlet.mobiledevicerules New:\ncom.liferay.mobile.device.rules.exception\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nDuplicateVoteException\n\nOld: com.liferay.portlet.polls New: com.liferay.polls.exception\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nEntryDisplayDateComparator\n\nOld: com.liferay.portlet.blogs.util.comparator New:\ncom.liferay.blogs.util.comparator\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nEntryModifiedDateComparator\n\nOld: com.liferay.portlet.bookmarks.util.comparator New:\ncom.liferay.bookmarks.util.comparator\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nEntryNameComparator\n\nOld: com.liferay.portlet.bookmarks.util.comparator New:\ncom.liferay.bookmarks.util.comparator\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nEntryPriorityComparator\n\nOld: com.liferay.portlet.bookmarks.util.comparator New:\ncom.liferay.bookmarks.util.comparator\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nEntrySmallImageNameException\n\nOld: com.liferay.portlet.blogs New: com.liferay.blogs.exception\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nEntryURLComparator\n\nOld: com.liferay.portlet.bookmarks.util.comparator New:\ncom.liferay.bookmarks.util.comparator\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nEntryVisitsComparator\n\nOld: com.liferay.portlet.bookmarks.util.comparator New:\ncom.liferay.bookmarks.util.comparator\n\ncom.liferay com.liferay.bookmarks.api 4.0.5\n\nEqualityWeakReference\n\nOld: com.liferay.portal.kernel.memory New: com.liferay.petra.memory\n\ncom.liferay com.liferay.petra.memory 3.0.1\n\nFact\n\nOld: com.liferay.portal.kernel.bi.rules New:\ncom.liferay.portal.rules.engine\n\ncom.liferay com.liferay.portal.rules.engine.api 4.0.4\n\nFeedContentFieldException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nFeedIdException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nFeedNameException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nFeedTargetLayoutFriendlyUrlException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nFeedTargetPortletIdException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nFieldConstants\n\nOld: com.liferay.portlet.dynamicdatamapping.storage New:\ncom.liferay.dynamic.data.mapping.storage\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nFieldRenderer\n\nOld: com.liferay.portlet.dynamicdatamapping.storage New:\ncom.liferay.dynamic.data.mapping.storage\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nFieldRendererFactory\n\nOld: com.liferay.portlet.dynamicdatamapping.storage New:\ncom.liferay.dynamic.data.mapping.storage\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nFields\n\nOld: com.liferay.portlet.dynamicdatamapping.storage New:\ncom.liferay.dynamic.data.mapping.storage\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nFileRankCreateDateComparator\n\nOld: com.liferay.portlet.documentlibrary.util.comparator New:\ncom.liferay.document.library.file.rank.util.comparator\n\ncom.liferay com.liferay.document.library.file.rank.service 2.0.6\n\nFinalizeAction\n\nOld: com.liferay.portal.kernel.memory New: com.liferay.petra.memory\n\ncom.liferay com.liferay.petra.memory 3.0.1\n\nFinalizeManager\n\nOld: com.liferay.portal.kernel.memory New: com.liferay.petra.memory\n\ncom.liferay com.liferay.petra.memory 3.0.1\n\nFlagsEntryService\n\nOld: com.liferay.portlet.flags.service New: com.liferay.flags.service\n\ncom.liferay com.liferay.flags.api 4.0.6\n\nFlagsEntryServiceUtil\n\nOld: com.liferay.portlet.flags.service New: com.liferay.flags.service\n\ncom.liferay com.liferay.flags.api 4.0.6\n\nFlagsEntryServiceWrapper\n\nOld: com.liferay.portlet.flags.service New: com.liferay.flags.service\n\ncom.liferay com.liferay.flags.api 4.0.6\n\nFlagsRequest\n\nOld: com.liferay.portlet.flags.messaging New:\ncom.liferay.flags.internal.messaging\n\ncom.liferay com.liferay.flags.service 4.0.2\n\nGroupConverterKeys\n\nOld: com.liferay.portal.security.ldap New:\ncom.liferay.portal.security.ldap\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nImportFilesException\n\nOld: com.liferay.portlet.wiki New: com.liferay.wiki.exception\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nJournalArticle\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleConstants\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleDisplay\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleFinder\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleLocalService\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleLocalServiceUtil\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleLocalServiceWrapper\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleModel\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticlePersistence\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleResource\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleResourceLocalService\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleResourceLocalServiceUtil\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleResourceLocalServiceWrapper\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleResourceModel\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleResourcePersistence\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleResourceSoap\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleResourceUtil\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleResourceWrapper\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleService\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleServiceUtil\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleServiceWrapper\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleSoap\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleUtil\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalArticleWrapper\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalContent\n\nOld: com.liferay.portlet.journalcontent.util New:\ncom.liferay.journal.util\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalContentSearch\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalContentSearchLocalService\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalContentSearchLocalServiceUtil\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalContentSearchLocalServiceWrapper\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalContentSearchModel\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalContentSearchPersistence\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalContentSearchSoap\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalContentSearchUtil\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalContentSearchWrapper\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalConverter\n\nOld: com.liferay.portlet.journal.util New: com.liferay.journal.util\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeed\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedConstants\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedFinder\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedLocalService\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedLocalServiceUtil\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedLocalServiceWrapper\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedModel\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedPersistence\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedService\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedServiceUtil\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedServiceWrapper\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedSoap\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedUtil\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFeedWrapper\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolder\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolderFinder\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolderLocalService\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolderLocalServiceUtil\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolderLocalServiceWrapper\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolderModel\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolderPersistence\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolderService\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolderServiceUtil\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolderServiceWrapper\n\nOld: com.liferay.portlet.journal.service New:\ncom.liferay.journal.service\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolderSoap\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolderUtil\n\nOld: com.liferay.portlet.journal.service.persistence New:\ncom.liferay.journal.service.persistence\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalFolderWrapper\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalSearchConstants\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nJournalStructureConstants\n\nOld: com.liferay.portlet.journal.model New: com.liferay.journal.model\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nLDAPFilterException\n\nOld: com.liferay.portal.kernel.ldap New:\ncom.liferay.portal.security.ldap.validator\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nLDAPGroup\n\nOld: com.liferay.portal.security.ldap New:\ncom.liferay.portal.security.ldap.exportimport\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nLDAPServerNameException\n\nOld: com.liferay.portal.kernel.ldap New:\ncom.liferay.portal.security.ldap\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nLDAPToPortalConverter\n\nOld: com.liferay.portal.security.ldap New:\ncom.liferay.portal.security.ldap.exportimport\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nLDAPUser\n\nOld: com.liferay.portal.security.ldap New:\ncom.liferay.portal.security.ldap.exportimport\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nLDAPUtil\n\nOld: com.liferay.portal.kernel.ldap New:\ncom.liferay.portal.security.ldap.util\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nLockLocalService\n\nOld: com.liferay.portal.service New: com.liferay.portal.lock.service\n\ncom.liferay com.liferay.portal.lock.api 4.1.1\n\nLockLocalServiceUtil\n\nOld: com.liferay.portal.service New: com.liferay.portal.lock.service\n\ncom.liferay com.liferay.portal.lock.api 4.1.1\n\nLockLocalServiceWrapper\n\nOld: com.liferay.portal.service New: com.liferay.portal.lock.service\n\ncom.liferay com.liferay.portal.lock.api 4.1.1\n\nLockModel\n\nOld: com.liferay.portal.model New: com.liferay.portal.lock.model\n\ncom.liferay com.liferay.portal.lock.api 4.1.1\n\nLockPersistence\n\nOld: com.liferay.portal.service.persistence New:\ncom.liferay.portal.lock.service.persistence\n\ncom.liferay com.liferay.portal.lock.api 4.1.1\n\nLockSoap\n\nOld: com.liferay.portal.model New: com.liferay.portal.lock.model\n\ncom.liferay com.liferay.portal.lock.api 4.1.1\n\nLockUtil\n\nOld: com.liferay.portal.service.persistence New:\ncom.liferay.portal.lock.service.persistence\n\ncom.liferay com.liferay.portal.lock.api 4.1.1\n\nLockWrapper\n\nOld: com.liferay.portal.model New: com.liferay.portal.lock.model\n\ncom.liferay com.liferay.portal.lock.api 4.1.1\n\nLockedThreadException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nLoggingOutputProcessor\n\nOld: com.liferay.portal.kernel.process New: com.liferay.petra.process\n\ncom.liferay com.liferay.petra.process 3.0.4\n\nMBBan\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBBanLocalService\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBBanLocalServiceUtil\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBBanLocalServiceWrapper\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBBanModel\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBBanPersistence\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBBanService\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBBanServiceUtil\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBBanServiceWrapper\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBBanSoap\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBBanUtil\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBBanWrapper\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategory\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategoryConstants\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.constants\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategoryDisplay\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.web.internal.display\n\ncom.liferay com.liferay.message.boards.web 3.0.17\n\nMBCategoryFinder\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategoryLocalService\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategoryLocalServiceUtil\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategoryLocalServiceWrapper\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategoryModel\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategoryPersistence\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategoryService\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategoryServiceUtil\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategoryServiceWrapper\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategorySoap\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategoryUtil\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBCategoryWrapper\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBDiscussion\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBDiscussionLocalService\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBDiscussionLocalServiceUtil\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBDiscussionLocalServiceWrapper\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBDiscussionModel\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBDiscussionPersistence\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBDiscussionSoap\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBDiscussionUtil\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBDiscussionWrapper\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMailingList\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMailingListLocalService\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMailingListLocalServiceUtil\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMailingListLocalServiceWrapper\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMailingListModel\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMailingListPersistence\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMailingListSoap\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMailingListUtil\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMailingListWrapper\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessage\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageConstants\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.constants\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageDisplay\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageFinder\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageLocalService\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageLocalServiceUtil\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageLocalServiceWrapper\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageModel\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessagePersistence\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageService\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageServiceUtil\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageServiceWrapper\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageSoap\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageUtil\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBMessageWrapper\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBStatsUser\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBStatsUserLocalService\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBStatsUserLocalServiceUtil\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBStatsUserLocalServiceWrapper\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBStatsUserModel\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBStatsUserPersistence\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBStatsUserSoap\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBStatsUserUtil\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBStatsUserWrapper\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThread\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadConstants\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.constants\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadFinder\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadFlag\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadFlagLocalService\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadFlagLocalServiceUtil\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadFlagLocalServiceWrapper\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadFlagModel\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadFlagPersistence\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadFlagSoap\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadFlagUtil\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadFlagWrapper\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadLocalService\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadLocalServiceUtil\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadLocalServiceWrapper\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadModel\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadPersistence\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadService\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadServiceUtil\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadServiceWrapper\n\nOld: com.liferay.portlet.messageboards.service New:\ncom.liferay.message.boards.service\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadSoap\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadUtil\n\nOld: com.liferay.portlet.messageboards.service.persistence New:\ncom.liferay.message.boards.service.persistence\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBThreadWrapper\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBTreeWalker\n\nOld: com.liferay.portlet.messageboards.model New:\ncom.liferay.message.boards.model\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMBeanRegistry\n\nOld: com.liferay.portal.kernel.jmx New: com.liferay.portal.jmx\n\ncom.liferay com.liferay.portal.jmx.api 3.0.1\n\nMDRAction\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRActionLocalService\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRActionLocalServiceUtil\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRActionLocalServiceWrapper\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRActionModel\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRActionPersistence\n\nOld: com.liferay.portlet.mobiledevicerules.service.persistence New:\ncom.liferay.mobile.device.rules.service.persistence\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRActionService\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRActionServiceUtil\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRActionServiceWrapper\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRActionSoap\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRActionUtil\n\nOld: com.liferay.portlet.mobiledevicerules.service.persistence New:\ncom.liferay.mobile.device.rules.service.persistence\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRActionWrapper\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRPermission\n\nOld: com.liferay.portlet.mobiledevicerules.service.permission New:\ncom.liferay.mobile.device.rules.web.internal.security.permission.resource\n\ncom.liferay com.liferay.mobile.device.rules.web 3.0.6\n\nMDRRule\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroup\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupFinder\n\nOld: com.liferay.portlet.mobiledevicerules.service.persistence New:\ncom.liferay.mobile.device.rules.service.persistence\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupInstanceLocalService\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupInstanceLocalServiceUtil\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupInstanceLocalServiceWrapper\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupInstanceModel\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupInstancePermission\n\nOld: com.liferay.portlet.mobiledevicerules.service.permission New:\ncom.liferay.mobile.device.rules.web.internal.security.permission.resource\n\ncom.liferay com.liferay.mobile.device.rules.web 3.0.6\n\nMDRRuleGroupInstancePersistence\n\nOld: com.liferay.portlet.mobiledevicerules.service.persistence New:\ncom.liferay.mobile.device.rules.service.persistence\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupInstanceService\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupInstanceServiceUtil\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupInstanceServiceWrapper\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupInstanceSoap\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupInstanceUtil\n\nOld: com.liferay.portlet.mobiledevicerules.service.persistence New:\ncom.liferay.mobile.device.rules.service.persistence\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupInstanceWrapper\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupLocalService\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupLocalServiceUtil\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupLocalServiceWrapper\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupModel\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupPermission\n\nOld: com.liferay.portlet.mobiledevicerules.service.permission New:\ncom.liferay.mobile.device.rules.web.internal.security.permission.resource\n\ncom.liferay com.liferay.mobile.device.rules.web 3.0.6\n\nMDRRuleGroupPersistence\n\nOld: com.liferay.portlet.mobiledevicerules.service.persistence New:\ncom.liferay.mobile.device.rules.service.persistence\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupService\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupServiceUtil\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupServiceWrapper\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupSoap\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupUtil\n\nOld: com.liferay.portlet.mobiledevicerules.service.persistence New:\ncom.liferay.mobile.device.rules.service.persistence\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleGroupWrapper\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleLocalService\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleLocalServiceUtil\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleLocalServiceWrapper\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleModel\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRulePersistence\n\nOld: com.liferay.portlet.mobiledevicerules.service.persistence New:\ncom.liferay.mobile.device.rules.service.persistence\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleService\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleServiceUtil\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleServiceWrapper\n\nOld: com.liferay.portlet.mobiledevicerules.service New:\ncom.liferay.mobile.device.rules.service\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleSoap\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleUtil\n\nOld: com.liferay.portlet.mobiledevicerules.service.persistence New:\ncom.liferay.mobile.device.rules.service.persistence\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMDRRuleWrapper\n\nOld: com.liferay.portlet.mobiledevicerules.model New:\ncom.liferay.mobile.device.rules.model\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nMailingListEmailAddressException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMailingListInServerNameException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMailingListInUserNameException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMailingListOutEmailAddressException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMailingListOutServerNameException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMailingListOutUserNameException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMemoryReportDesignRetriever\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nMessageBodyException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMessageBusManager\n\nOld: com.liferay.portal.kernel.messaging.jmx New:\ncom.liferay.portal.messaging.internal.jmx\n\ncom.liferay com.liferay.portal.messaging 6.0.5\n\nMessageBusManagerMBean\n\nOld: com.liferay.portal.kernel.messaging.jmx New:\ncom.liferay.portal.messaging.internal.jmx\n\ncom.liferay com.liferay.portal.messaging 6.0.5\n\nMessageCreateDateComparator\n\nOld: com.liferay.portlet.messageboards.util.comparator New:\ncom.liferay.message.boards.util.comparator\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMessageSubjectException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nMessageThreadComparator\n\nOld: com.liferay.portlet.messageboards.util.comparator New:\ncom.liferay.message.boards.util.comparator\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nModifications\n\nOld: com.liferay.portal.security.ldap New:\ncom.liferay.portal.security.ldap.exportimport\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nNoSuchArticleException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nNoSuchArticleImageException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nNoSuchArticleResourceException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nNoSuchBanException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nNoSuchChoiceException\n\nOld: com.liferay.portlet.polls New: com.liferay.polls.exception\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nNoSuchContentException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nNoSuchContentSearchException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nNoSuchDiscussionException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nNoSuchFeedException\n\nOld: com.liferay.portlet.journal New: com.liferay.journal.exception\n\ncom.liferay com.liferay.journal.api 4.2.1\n\nNoSuchFileRankException\n\nOld: com.liferay.portlet.documentlibrary New:\ncom.liferay.document.library.file.rank.exception\n\ncom.liferay com.liferay.document.library.file.rank.api 2.0.3\n\nNoSuchMailingListException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nNoSuchNodeException\n\nOld: com.liferay.portlet.wiki New: com.liferay.wiki.exception\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nNoSuchPageException\n\nOld: com.liferay.portlet.wiki New: com.liferay.wiki.exception\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nNoSuchPageResourceException\n\nOld: com.liferay.portlet.wiki New: com.liferay.wiki.exception\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nNoSuchQuestionException\n\nOld: com.liferay.portlet.polls New: com.liferay.polls.exception\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nNoSuchRecordException\n\nOld: com.liferay.portlet.dynamicdatalists New:\ncom.liferay.dynamic.data.lists.exception\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nNoSuchRecordSetException\n\nOld: com.liferay.portlet.dynamicdatalists New:\ncom.liferay.dynamic.data.lists.exception\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nNoSuchRecordVersionException\n\nOld: com.liferay.portlet.dynamicdatalists New:\ncom.liferay.dynamic.data.lists.exception\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nNoSuchRuleException\n\nOld: com.liferay.portlet.mobiledevicerules New:\ncom.liferay.mobile.device.rules.exception\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nNoSuchRuleGroupException\n\nOld: com.liferay.portlet.mobiledevicerules New:\ncom.liferay.mobile.device.rules.exception\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nNoSuchRuleGroupInstanceException\n\nOld: com.liferay.portlet.mobiledevicerules New:\ncom.liferay.mobile.device.rules.exception\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nNoSuchStatsUserException\n\nOld: com.liferay.portlet.blogs New: com.liferay.blogs.exception\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nNoSuchStorageLinkException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nNoSuchStructureLinkException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nNoSuchTemplateException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nNoSuchThreadException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nNoSuchThreadFlagException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nNoSuchVoteException\n\nOld: com.liferay.portlet.polls New: com.liferay.polls.exception\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nNodeNameException\n\nOld: com.liferay.portlet.wiki New: com.liferay.wiki.exception\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nOutputProcessor\n\nOld: com.liferay.portal.kernel.process New: com.liferay.petra.process\n\ncom.liferay com.liferay.petra.process 3.0.4\n\nPageContentException\n\nOld: com.liferay.portlet.wiki New: com.liferay.wiki.exception\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nPageCreateDateComparator\n\nOld: com.liferay.portlet.wiki.util.comparator New:\ncom.liferay.wiki.util.comparator\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nPageTitleComparator\n\nOld: com.liferay.portlet.wiki.util.comparator New:\ncom.liferay.wiki.util.comparator\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nPageTitleException\n\nOld: com.liferay.portlet.wiki New: com.liferay.wiki.exception\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nPageVersionComparator\n\nOld: com.liferay.portlet.wiki.util.comparator New:\ncom.liferay.wiki.util.comparator\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nPageVersionException\n\nOld: com.liferay.portlet.wiki New: com.liferay.wiki.exception\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nPollsChoice\n\nOld: com.liferay.portlet.polls.model New: com.liferay.polls.model\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsChoiceLocalService\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsChoiceLocalServiceUtil\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsChoiceLocalServiceWrapper\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsChoiceModel\n\nOld: com.liferay.portlet.polls.model New: com.liferay.polls.model\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsChoicePersistence\n\nOld: com.liferay.portlet.polls.service.persistence New:\ncom.liferay.polls.service.persistence\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsChoiceService\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsChoiceServiceUtil\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsChoiceServiceWrapper\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsChoiceSoap\n\nOld: com.liferay.portlet.polls.model New: com.liferay.polls.model\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsChoiceUtil\n\nOld: com.liferay.portlet.polls.service.persistence New:\ncom.liferay.polls.service.persistence\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsChoiceWrapper\n\nOld: com.liferay.portlet.polls.model New: com.liferay.polls.model\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsQuestion\n\nOld: com.liferay.portlet.polls.model New: com.liferay.polls.model\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsQuestionLocalService\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsQuestionLocalServiceUtil\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsQuestionLocalServiceWrapper\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsQuestionModel\n\nOld: com.liferay.portlet.polls.model New: com.liferay.polls.model\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsQuestionPersistence\n\nOld: com.liferay.portlet.polls.service.persistence New:\ncom.liferay.polls.service.persistence\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsQuestionService\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsQuestionServiceUtil\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsQuestionServiceWrapper\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsQuestionSoap\n\nOld: com.liferay.portlet.polls.model New: com.liferay.polls.model\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsQuestionUtil\n\nOld: com.liferay.portlet.polls.service.persistence New:\ncom.liferay.polls.service.persistence\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsQuestionWrapper\n\nOld: com.liferay.portlet.polls.model New: com.liferay.polls.model\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsVote\n\nOld: com.liferay.portlet.polls.model New: com.liferay.polls.model\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsVoteLocalService\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsVoteLocalServiceUtil\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsVoteLocalServiceWrapper\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsVoteModel\n\nOld: com.liferay.portlet.polls.model New: com.liferay.polls.model\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsVotePersistence\n\nOld: com.liferay.portlet.polls.service.persistence New:\ncom.liferay.polls.service.persistence\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsVoteService\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsVoteServiceUtil\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsVoteServiceWrapper\n\nOld: com.liferay.portlet.polls.service New: com.liferay.polls.service\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsVoteSoap\n\nOld: com.liferay.portlet.polls.model New: com.liferay.polls.model\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsVoteUtil\n\nOld: com.liferay.portlet.polls.service.persistence New:\ncom.liferay.polls.service.persistence\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPollsVoteWrapper\n\nOld: com.liferay.portlet.polls.model New: com.liferay.polls.model\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nPoolAction\n\nOld: com.liferay.portal.kernel.memory New: com.liferay.petra.memory\n\ncom.liferay com.liferay.petra.memory 3.0.1\n\nPortalCacheClusterChannel\n\nOld: com.liferay.portal.kernel.cache.cluster New:\ncom.liferay.portal.cache.multiple.internal.cluster.link\n\ncom.liferay com.liferay.portal.cache.multiple 3.0.6\n\nPortalCacheClusterChannelFactory\n\nOld: com.liferay.portal.kernel.cache.cluster New:\ncom.liferay.portal.cache.multiple.internal.cluster.link\n\ncom.liferay com.liferay.portal.cache.multiple 3.0.6\n\nPortalCacheClusterChannelSelector\n\nOld: com.liferay.portal.kernel.cache.cluster New:\ncom.liferay.portal.cache.multiple.internal.cluster.link\n\ncom.liferay com.liferay.portal.cache.multiple 3.0.6\n\nPortalCacheClusterEvent\n\nOld: com.liferay.portal.kernel.cache.cluster New:\ncom.liferay.portal.cache.multiple.internal\n\ncom.liferay com.liferay.portal.cache.multiple 3.0.6\n\nPortalCacheClusterEventCoalesceComparator\n\nOld: com.liferay.portal.kernel.cache.cluster New:\ncom.liferay.portal.cache.multiple.internal\n\ncom.liferay com.liferay.portal.cache.multiple 3.0.6\n\nPortalCacheClusterEventType\n\nOld: com.liferay.portal.kernel.cache.cluster New:\ncom.liferay.portal.cache.multiple.internal\n\ncom.liferay com.liferay.portal.cache.multiple 3.0.6\n\nPortalCacheClusterException\n\nOld: com.liferay.portal.kernel.cache.cluster New:\ncom.liferay.portal.cache.multiple.internal\n\ncom.liferay com.liferay.portal.cache.multiple 3.0.6\n\nPortalCacheClusterLink\n\nOld: com.liferay.portal.kernel.cache.cluster New:\ncom.liferay.portal.cache.multiple.internal.cluster.link\n\ncom.liferay com.liferay.portal.cache.multiple 3.0.6\n\nPortalExecutorFactory\n\nOld: com.liferay.portal.kernel.executor New:\ncom.liferay.portal.executor.internal\n\ncom.liferay com.liferay.portal.executor 4.0.2\n\nPortalToLDAPConverter\n\nOld: com.liferay.portal.security.ldap New:\ncom.liferay.portal.security.ldap.exportimport\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nPortletDisplayTemplate\n\nOld: com.liferay.portlet.portletdisplaytemplate.util New:\ncom.liferay.portlet.display.template\n\ncom.liferay com.liferay.portlet.display.template.api 2.0.2\n\nPortletDisplayTemplateConstants\n\nOld: com.liferay.portlet.portletdisplaytemplate.util New:\ncom.liferay.portlet.display.template.constants\n\ncom.liferay com.liferay.portlet.display.template.api 2.0.2\n\nPortletDisplayTemplateUtil\n\nOld: com.liferay.portlet.portletdisplaytemplate.util New:\ncom.liferay.roles.admin.web.internal.util\n\ncom.liferay com.liferay.roles.admin.web 3.0.6\n\nPortletDisplayTemplateUtil\n\nOld: com.liferay.portlet.portletdisplaytemplate.util New:\ncom.liferay.roles.admin.web.internal.util\n\ncom.liferay com.liferay.roles.admin.web 3.0.6\n\nPortletDisplayTemplateUtil\n\nOld: com.liferay.portlet.portletdisplaytemplate.util New:\ncom.liferay.roles.admin.web.internal.util\n\ncom.liferay com.liferay.roles.admin.web 3.0.6\n\nProcessUtil\n\nOld: com.liferay.portal.kernel.process New: com.liferay.petra.process\n\ncom.liferay com.liferay.petra.process 3.0.4\n\nQueryIndexingHitsProcessor\n\nOld: com.liferay.portal.kernel.search New:\ncom.liferay.portal.search.internal.hits\n\ncom.liferay com.liferay.portal.search 6.0.14\n\nQuerySuggestionHitsProcessor\n\nOld: com.liferay.portal.kernel.search New:\ncom.liferay.portal.search.internal.hits\n\ncom.liferay com.liferay.portal.search 6.0.14\n\nQueryType\n\nOld: com.liferay.portal.kernel.bi.rules New:\ncom.liferay.portal.rules.engine\n\ncom.liferay com.liferay.portal.rules.engine.api 4.0.4\n\nQuestionChoiceException\n\nOld: com.liferay.portlet.polls New: com.liferay.polls.exception\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nQuestionDescriptionException\n\nOld: com.liferay.portlet.polls New: com.liferay.polls.exception\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nQuestionExpirationDateException\n\nOld: com.liferay.portlet.polls New: com.liferay.polls.exception\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nQuestionExpiredException\n\nOld: com.liferay.portlet.polls New: com.liferay.polls.exception\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nQuestionTitleException\n\nOld: com.liferay.portlet.polls New: com.liferay.polls.exception\n\ncom.liferay com.liferay.polls.api 6.0.3\n\nRecordSetDDMStructureIdException\n\nOld: com.liferay.portlet.dynamicdatalists New:\ncom.liferay.dynamic.data.lists.exception\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nRecordSetDuplicateRecordSetKeyException\n\nOld: com.liferay.portlet.dynamicdatalists New:\ncom.liferay.dynamic.data.lists.exception\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nRecordSetNameException\n\nOld: com.liferay.portlet.dynamicdatalists New:\ncom.liferay.dynamic.data.lists.exception\n\ncom.liferay com.liferay.dynamic.data.lists.api 4.0.5\n\nRegistryAwareMBeanServer\n\nOld: com.liferay.portal.kernel.jmx New: com.liferay.portal.jmx.internal\n\ncom.liferay com.liferay.portal.jmx 6.0.2\n\nReportCompilerRequestMessageListener\n\nOld: com.liferay.portal.kernel.bi.reporting.messaging New:\ncom.liferay.portal.reports.engine.messaging\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nReportDataSourceType\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nReportDesignRetriever\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nReportEngine\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nReportExportException\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nReportFormat\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nReportFormatExporter\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nReportFormatExporterRegistry\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nReportGenerationException\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nReportRequest\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nReportRequestContext\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nReportRequestMessageListener\n\nOld: com.liferay.portal.kernel.bi.reporting.messaging New:\ncom.liferay.portal.reports.engine.messaging\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nReportResultContainer\n\nOld: com.liferay.portal.kernel.bi.reporting New:\ncom.liferay.portal.reports.engine\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nRequestStatistics\n\nOld: com.liferay.portal.kernel.monitoring.statistics New:\ncom.liferay.portal.monitoring.internal.statistics\n\ncom.liferay com.liferay.portal.monitoring 7.0.7\n\nRequiredMessageException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nRequiredNodeException\n\nOld: com.liferay.portlet.wiki New: com.liferay.wiki.exception\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nRequiredTemplateException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nRequiredTemplateException\n\nOld: com.liferay.portlet.journal New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nRuleGroupInstancePriorityComparator\n\nOld: com.liferay.portlet.mobiledevicerules.util New:\ncom.liferay.mobile.device.rules.util.comparator\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nRuleGroupProcessor\n\nOld: com.liferay.portal.kernel.mobile.device.rulegroup New:\ncom.liferay.mobile.device.rules.rule\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nRuleGroupProcessorUtil\n\nOld: com.liferay.portal.kernel.mobile.device.rulegroup New:\ncom.liferay.mobile.device.rules.rule\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nRuleHandler\n\nOld: com.liferay.portal.kernel.mobile.device.rulegroup.rule New:\ncom.liferay.mobile.device.rules.rule\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nRulesEngine\n\nOld: com.liferay.portal.kernel.bi.rules New:\ncom.liferay.portal.rules.engine\n\ncom.liferay com.liferay.portal.rules.engine.api 4.0.4\n\nRulesEngineException\n\nOld: com.liferay.portal.kernel.bi.rules New:\ncom.liferay.portal.rules.engine\n\ncom.liferay com.liferay.portal.rules.engine.api 4.0.4\n\nRulesEngineUtil\n\nOld: com.liferay.portal.kernel.bi.rules New:\ncom.liferay.portal.rules.engine\n\ncom.liferay com.liferay.portal.rules.engine.api 4.0.4\n\nRulesLanguage\n\nOld: com.liferay.portal.kernel.bi.rules New:\ncom.liferay.portal.rules.engine\n\ncom.liferay com.liferay.portal.rules.engine.api 4.0.4\n\nRulesResourceRetriever\n\nOld: com.liferay.portal.kernel.bi.rules New:\ncom.liferay.portal.rules.engine\n\ncom.liferay com.liferay.portal.rules.engine.api 4.0.4\n\nSearchUtil\n\nOld: com.liferay.portal.kernel.search.util New:\ncom.liferay.portal.vulcan.util\n\ncom.liferay com.liferay.portal.vulcan.api 3.2.2\n\nSearchUtil\n\nOld: com.liferay.portal.kernel.search.util New:\ncom.liferay.portal.vulcan.util\n\ncom.liferay com.liferay.portal.vulcan.api 3.2.2\n\nServletContextReportDesignRetriever\n\nOld: com.liferay.portal.kernel.bi.reporting.servlet New:\ncom.liferay.portal.reports.engine.servlet\n\ncom.liferay com.liferay.portal.reports.engine.api 5.0.1\n\nSoftReferencePool\n\nOld: com.liferay.portal.kernel.memory New: com.liferay.petra.memory\n\ncom.liferay com.liferay.petra.memory 3.0.1\n\nSortFactoryImpl\n\nOld: com.liferay.portal.kernel.search New:\ncom.liferay.portal.search.internal\n\ncom.liferay com.liferay.portal.search 6.0.14\n\nSplitThreadException\n\nOld: com.liferay.portlet.messageboards New:\ncom.liferay.message.boards.exception\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nStatistics\n\nOld: com.liferay.portal.kernel.monitoring.statistics New:\ncom.liferay.portal.monitoring.internal.statistics\n\ncom.liferay com.liferay.portal.monitoring 7.0.7\n\nStatsUserLastPostDateComparator\n\nOld: com.liferay.portlet.blogs.util.comparator New:\ncom.liferay.blogs.util.comparator\n\ncom.liferay com.liferay.blogs.api 5.0.5\n\nStorageAdapter\n\nOld: com.liferay.portlet.dynamicdatamapping.storage New:\ncom.liferay.dynamic.data.mapping.storage\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nStorageEngine\n\nOld: com.liferay.portlet.dynamicdatamapping.storage New:\ncom.liferay.dynamic.data.mapping.storage\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nStorageException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nStorageFieldNameException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nStringQueryImpl\n\nOld: com.liferay.portal.kernel.search New:\ncom.liferay.portal.search.internal.query\n\ncom.liferay com.liferay.portal.search 6.0.14\n\nStructureDuplicateStructureKeyException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nStructureFieldException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nStructureIdComparator\n\nOld: com.liferay.portlet.dynamicdatamapping.util.comparator New:\ncom.liferay.dynamic.data.mapping.util.comparator\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nStructureModifiedDateComparator\n\nOld: com.liferay.portlet.dynamicdatamapping.util.comparator New:\ncom.liferay.dynamic.data.mapping.util.comparator\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nStructureStructureKeyComparator\n\nOld: com.liferay.portlet.dynamicdatamapping.util.comparator New:\ncom.liferay.dynamic.data.mapping.util.comparator\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nSummaryStatistics\n\nOld: com.liferay.portal.kernel.monitoring.statistics New:\ncom.liferay.portal.monitoring.internal.statistics\n\ncom.liferay com.liferay.portal.monitoring 7.0.7\n\nSynchronousMessageListener\n\nOld: com.liferay.portal.kernel.messaging.sender New:\ncom.liferay.portal.messaging.internal.sender\n\ncom.liferay com.liferay.portal.messaging 6.0.5\n\nTemplateDuplicateTemplateKeyException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nTemplateIdComparator\n\nOld: com.liferay.portlet.dynamicdatamapping.util.comparator New:\ncom.liferay.dynamic.data.mapping.util.comparator\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nTemplateModifiedDateComparator\n\nOld: com.liferay.portlet.dynamicdatamapping.util.comparator New:\ncom.liferay.dynamic.data.mapping.util.comparator\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nTemplateNameException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nTemplateNameException\n\nOld: com.liferay.portlet.journal New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nTemplateScriptException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nTemplateSmallImageNameException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nTemplateSmallImageNameException\n\nOld: com.liferay.portlet.journal New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nTemplateSmallImageSizeException\n\nOld: com.liferay.portlet.dynamicdatamapping New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nTemplateSmallImageSizeException\n\nOld: com.liferay.portlet.journal New:\ncom.liferay.dynamic.data.mapping.exception\n\ncom.liferay com.liferay.dynamic.data.mapping.api 5.2.1\n\nThreadLastPostDateComparator\n\nOld: com.liferay.portlet.messageboards.util.comparator New:\ncom.liferay.message.boards.util.comparator\n\ncom.liferay com.liferay.message.boards.api 5.1.4\n\nUniformPortalCacheClusterChannelSelector\n\nOld: com.liferay.portal.kernel.cache.cluster New:\ncom.liferay.portal.cache.multiple.internal.cluster.link\n\ncom.liferay com.liferay.portal.cache.multiple 3.0.6\n\nUnknownRuleHandlerException\n\nOld: com.liferay.portal.kernel.mobile.device.rulegroup.rule New:\ncom.liferay.mobile.device.rules.rule\n\ncom.liferay com.liferay.mobile.device.rules.api 4.0.4\n\nUserConverterKeys\n\nOld: com.liferay.portal.security.ldap New:\ncom.liferay.portal.security.ldap\n\ncom.liferay com.liferay.portal.security.ldap.api 2.0.8\n\nWikiFormatException\n\nOld: com.liferay.portlet.wiki New: com.liferay.wiki.exception\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiNode\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiNodeLocalService\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiNodeLocalServiceUtil\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiNodeLocalServiceWrapper\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiNodeModel\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiNodePersistence\n\nOld: com.liferay.portlet.wiki.service.persistence New:\ncom.liferay.wiki.service.persistence\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiNodeService\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiNodeServiceUtil\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiNodeServiceWrapper\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiNodeSoap\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiNodeUtil\n\nOld: com.liferay.portlet.wiki.service.persistence New:\ncom.liferay.wiki.service.persistence\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiNodeWrapper\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPage\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageConstants\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageDisplay\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageFinder\n\nOld: com.liferay.portlet.wiki.service.persistence New:\ncom.liferay.wiki.service.persistence\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageLocalService\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageLocalServiceUtil\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageLocalServiceWrapper\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageModel\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPagePersistence\n\nOld: com.liferay.portlet.wiki.service.persistence New:\ncom.liferay.wiki.service.persistence\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageResource\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageResourceLocalService\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageResourceLocalServiceUtil\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageResourceLocalServiceWrapper\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageResourceModel\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageResourcePersistence\n\nOld: com.liferay.portlet.wiki.service.persistence New:\ncom.liferay.wiki.service.persistence\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageResourceSoap\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageResourceUtil\n\nOld: com.liferay.portlet.wiki.service.persistence New:\ncom.liferay.wiki.service.persistence\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageResourceWrapper\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageService\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageServiceUtil\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageServiceWrapper\n\nOld: com.liferay.portlet.wiki.service New: com.liferay.wiki.service\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageSoap\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageUtil\n\nOld: com.liferay.portlet.wiki.service.persistence New:\ncom.liferay.wiki.service.persistence\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\nWikiPageWrapper\n\nOld: com.liferay.portlet.wiki.model New: com.liferay.wiki.model\n\ncom.liferay com.liferay.wiki.api 4.0.7\n\n\\chapter{Export/Import and Staging}\\label{exportimport-and-staging}\n\nExport/Import and Staging are frameworks that help you manage your\ncontent publication. This section provides reference documentation\ncomplementing the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/content-publication-management}{Content\nPublication Management} section.\n\n\\chapter{Decision to Implement\nStaging}\\label{decision-to-implement-staging}\n\nStaging is an advanced publication tool that lets you create or modify\nyour site before releasing it to the public. Most of Liferay DXP's\nincluded applications (e.g., Web Content, Bookmarks, etc.) support\nStaging. Implementing Staging in your own application can be beneficial,\nbut how do you know if it's the right move?\n\nNot every application needs to support Staging and Export/Import. The\nmost important question to consider during the decision process is\n\n\\emph{What part of your application are you primarily focused on using\nStaging for?}\n\nWhen Staging is enabled, all pages and applications are staged\nautomatically. Liferay DXP's architecture separates the application and\nits configuration from the actual content, meaning that content can\nexist without any application to display it and vice versa. Although\nStaging supports all applications and their configurations by default,\nnot all applications' content is supported by Staging.\n\nImplementing Staging for your application means you're defining the\nlogic for how the Staging framework should process, serialize, and\nde-serialize your app's content, and how to insert it into a database.\n\nTherefore, if you want to track your application's content, you should\nimplement Staging in your application. Here are a few other scenarios\nwhere you should implement Staging in your application:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  You're using remote staging. When publishing to a remote live site,\n  your content must be transferred to a different Liferay DXP\n  installation. Therefore, Staging must be able to recognize the content\n  to facilitate the transfer.\n\\item\n  You want a space where you can freely edit and test your content\n  before publishing it to a live audience.\n\\item\n  Your content is being referenced from another content type that\n  supports Staging.\n\\item\n  You want to process your portlet's preferences during publication\n  (i.e., you might want to publish some content with it or complete\n  extra steps).\n\\item\n  You want to process the content during publication (e.g., writing\n  validation for your content during the import process).\n\\end{itemize}\n\nIf none of these options are beneficial for you, implementing Staging in\nyour application is unnecessary.\n\nWhen content supports Staging and Staging is enabled, it is created in a\nStaging group and is only published to a live site when that site is\npublished. When content is \\textbf{not} supported by Staging, it is\nnever added to a Staging group and is not reviewable during the Staging\npublication process; it's added and removed from the live site only.\n\nFrom a technical standpoint, publishing an entity or content follows the\nprocess below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  The entity's possible references are discovered and processed.\n\\item\n  The entity's fields are processed.\n\\item\n  The entity is serialized into a LAR file.\n\\item\n  The LAR is transferred to the live site (local or remote live).\n\\item\n  After de-serialization, the entity's fields are processed.\n\\item\n  The entity is added to the database.\n\\end{enumerate}\n\nAwesome! You should now have a good idea about whether you should\nimplement Staging for your application.\n\n\\chapter{Liferay Archive (LAR) File}\\label{liferay-archive-lar-file}\n\nAn easier way to export/import your application's data is to use a\nLiferay ARchive (LAR) file. Liferay provides the LAR feature to address\nthe need to export/import data in a database agnostic manner. So what\nexactly is a LAR file?\n\nA LAR file is a compressed file (ZIP archive) Liferay DXP uses to\nexport/import data. LAR files can be created for single portlets, pages,\nor sets of pages. Portlets that are LAR-capable provide an interface to\nlet you control how their data is imported/exported. There are several\nLiferay DXP use cases that require the use of LAR files:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Backing up and restoring portlet-specific data without requiring a\n  full database backup.\n\\item\n  Cloning sites.\n\\item\n  Specifying a template to be used for users' public or private pages.\n\\item\n  Using Local Live or Remote Live staging.\n\\end{itemize}\n\nThe data handler framework is available so developers don't have to\ncreate/modify a LAR file manually. \\textbf{It is strongly recommended\nnever to modify a LAR file.} You should always use Liferay's provided\ndata handler APIs to construct it.\n\nKnowing how a LAR file is constructed, however, is beneficial to\nunderstand the overall purpose of your application's data handlers.\nNext, you'll explore a LAR file's anatomy.\n\n\\section{LAR File Anatomy}\\label{lar-file-anatomy}\n\nWhat is a LAR file? You know the general concept for \\emph{why} it's\nused, but you may want to know what lives inside to make your\nexport/import processes work. With a fundamental understanding for how a\nLAR file is constructed, you can better understand what your data\nhandlers generate behind the scenes.\n\nBelow is the structure of a simple LAR file. It illustrates the\nexportation of a single Bookmarks entry and the portlet's configuration:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{Bookmarks\\_Admin-201701091904.portlet.lar}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{group}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{20143}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{com.liferay.bookmarks.model.BookmarksEntry}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{35005.xml}\n        \\end{itemize}\n      \\item\n        \\texttt{portlet}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com\\_liferay\\_bookmarks\\_web\\_portlet\\_BookmarksAdminPortlet}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{20137}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{portlet.xml}\n            \\end{itemize}\n          \\item\n            \\texttt{20143}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{portlet-data.xml}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{manifest.xml}\n  \\end{itemize}\n\\end{itemize}\n\nYou'll dissect the anatomy structure next.\n\n\\section{LAR Manifest}\\label{lar-manifest}\n\nYou can tell from the LAR's generated name what information is contained\nin the LAR: the Bookmarks Admin app's data. The \\texttt{manifest.xml}\nfile sits at the root of the LAR file. It provides essential information\nabout the export process. The \\texttt{manifest.xml} for the sample\nBookmarks LAR is pretty bare since it's not exporting much content, but\nthis file can become large when exporting pages of content. There are\nfour main parts (tags) to a \\texttt{manifest.xml} file.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{header}: contains information about the LAR file, current\n  process, and site you're exporting (if necessary). For example, it can\n  include locales, build information, export date, company ID, group ID,\n  layouts, themes, etc.\n\\item\n  \\texttt{missing-references}: lists entities that must be validated\n  during import. For example, suppose you're exporting a web content\n  article that references an image (e.g., an embedded image residing in\n  the document library). If the image was not selected for export, the\n  image must already exist in the site where the article is imported.\n  Therefore, the image would be flagged as a missing reference in the\n  LAR file. If the missing reference does not exist in the site when the\n  LAR is imported, the import process fails. If your import fails, the\n  Import UI shows you the missing references that weren't validated.\n\\item\n  \\texttt{portlets}: defines the portlets (i.e., portlet data) exported\n  in the LAR. Each portlet definition has basic information on the\n  exported portlet and points to the generated \\texttt{portlet.xml} for\n  more specialized portlet information.\n\\item\n  \\texttt{manifest-summary}: contains information on what has been\n  exported. The Staging and Export frameworks export or publish some\n  entities even though they weren't marked for it, because the process\n  respects data integrity. This section holds information for all the\n  entities that have been processed. The entities defining a non-zero\n  \\texttt{addition-count} attribute are displayed in the Export/Import\n  UI.\n\\end{itemize}\n\nThe \\texttt{manifest.xml} file also defines layout information if you've\nexported pages in your LAR. For example, your manifest could have\n\\texttt{LayoutSet}, \\texttt{Layout}, and \\texttt{LayoutFriendlyURL} tags\nspecifying staged models and their various references in an exported\npage.\n\nNow that you've learned about the LAR's \\texttt{manifest.xml} and how\nit's used to store high-level data about your export process, you can\ndive deeper into the LAR file's folders.\n\n\\section{LAR Folders}\\label{lar-folders}\n\nThe \\texttt{group} folder has two main parts:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Entities\n\\item\n  Portlets\n\\end{itemize}\n\nIf you look at the anatomy of the sample Bookmarks LAR, you'll notice\nthat \\texttt{group/{[}groupId{]}} folder holds a folder named after the\nentity you're exporting (e.g.,\n\\texttt{com.liferay.bookmarks.model.BookmarksEntry}) and a\n\\texttt{portlet} folder holding a folder named after the portlet from\nwhich you're exporting (e.g.,\n\\texttt{com\\_liferay\\_bookmarks\\_web\\_portlet\\_BookmarksAdminPortlet}).\nFor each entity/portlet you export, there are subsequent folders holding\ndata about them. Entities and portlets can also be stored in a\n\\texttt{company} folder. Although the majority of entities belong to a\ngroup, some exist outside of a group scope (e.g., users).\n\nIf you open the\n\\texttt{/group/20143/com.liferay.bookmarks.model.BookmarksEntry/35005.xml}\nfile, you'll find serialized data about the entity, which is similar to\nwhat is stored in the database.\n\nThe \\texttt{portlet} folder holds all the portlets you exported. Each\nportlet has its own folder that holds various XML files with data\ndescribing the exported content. There are three main XML files that can\nbe generated for a single portlet:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{portlet.xml}: provides essential information about the\n  portlet, similar to a manifest file. For example, this can include the\n  portlet ID, high-level entity information stored in the portlet (e.g.,\n  web content articles in a web content portlet), permissioning, etc.\n\\item\n  \\texttt{portlet-data.xml}: describes specific entity data stored in\n  the portlet. For example, for the web content portlet, articles stored\n  in the portlet are defined in \\texttt{staged-model} tags and are\n  linked to their serialized entity XML files.\n\\item\n  \\texttt{portlet-preferences.xml}: defines the settings of the portlet.\n  For example, this can include portlet preferences like the portlet\n  owner, default user, article IDs, etc.\n\\end{itemize}\n\nNote that when you import a LAR, it only includes the portlet data. You\nhave to deploy the portlet to be able to use it.\n\nYou now know how exported entities, portlets, and pages are defined in a\nLAR file. For a summarized outline of what you've learned about LAR file\nconstruction, see the diagram below.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/lar-diagram.png}\n\\caption{Entities, Portlets, and Pages are defined in a LAR in different\nplaces.}\n\\end{figure}\n\nExcellent! You now have a fundamental understanding for how a LAR file\nis generated and how it's structured.\n\n\\chapter{Front-End Reference}\\label{front-end-reference}\n\nThis section contains resources that you might find useful for Front-End\ndevelopment.\n\nThe topics below are covered in this section:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/product-freemarker-macros}{Liferay\n  DXP FreeMarker Macros}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/freemarker-taglib-macros}{FreeMarker\n  Taglib Macros}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs}{Front-end\n  Taglibs}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-npm-bundler}{Liferay\n  npm Bundler}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-javascript-apis}{Liferay\n  JS APIs}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/setting-up-your-npm-environment}{Setting\n  up Your npm Environment}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/sitemap-page-configuration-options}{Sitemap.json\n  Page Configuration Options}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide}{CKEditor\n  Plugin Reference Guide}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/fully-qualified-portlet-ids}{Fully\n  Qualified Portlet IDs List}\n\\end{itemize}\n\n\\chapter{Liferay DXP FreeMarker\nMacros}\\label{liferay-dxp-freemarker-macros}\n\nLiferay DXP defines several\n\\href{https://freemarker.apache.org/docs/ref_directive_macro.html}{macros}\nin\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-template/portal-template-freemarker/src/main/resources/FTL_liferay.ftl}{\\texttt{FTL\\_Liferay.ftl}\ntemplate} that you can use in your theme templates to include theme\nresources, standard portlets, and more. Liferay DXP also exposes its\ntaglibs as FreeMarker macros. See each\n\\href{/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs}{taglib's\ndocumentation} for more information on using the taglib in your\nFreeMarker templates. This reference guide lists the available\nFreeMarker macros that Liferay DXP offers.\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 6\\tabcolsep) * \\real{0.2500}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 6\\tabcolsep) * \\real{0.2500}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 6\\tabcolsep) * \\real{0.2500}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 6\\tabcolsep) * \\real{0.2500}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nMacro\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nParameters\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nExample\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nbreadcrumbs & default\\_preferences & Adds the Breadcrumbs portlet with\noptional preferences &\n\\texttt{\\textless{}@liferay.breadcrumbs\\ /\\textgreater{}} \\\\\ncontrol\\_menu & N/A & Adds the Control Menu portlet &\n\\texttt{\\textless{}@liferay.control\\_menu\\ /\\textgreater{}} \\\\\ncss & file\\_name & Adds an external stylesheet with the specified file\nname location &\n\\texttt{\\textless{}@liferay.css\\ file\\_name=\"\\$\\{css\\_folder\\}/mycss.css\"/\\textgreater{}} \\\\\ndate & format & Prints the date in the current locale with the given\nformat &\n\\texttt{\\textless{}@liferay.date\\ format=\"/yyyy/MM/dd/HH/\"\\ /\\textgreater{}} \\\\\njs & file\\_name & Adds an external JavaScript file with the specified\nfile name source &\n\\texttt{\\textless{}@liferay.js\\ file\\_name=\"\\$\\{javascript\\_folder\\}/myJs.js\"/\\textgreater{}} \\\\\nlanguage & key & Prints the specified language key in the current locale\n&\n\\texttt{\\textless{}@liferay.language\\ key=\"last-modified\"\\ /\\textgreater{}} \\\\\nlanguage\\_format & argumentskey & Formats the given language key with\nthe specified arguments. For example, passing \\texttt{go-to-x} as the\nkey and \\texttt{Mars} as the arguments prints \\emph{Go to Mars}. &\n\\texttt{\\textless{}@liferay.language\\_format\\ arguments=\"\\$\\{site\\_name\\}\"\\ key=\"go-to-x\"\\ /\\textgreater{}} \\\\\nlanguages & default\\_preferences & Adds the Languages portlet with\noptional preferences &\n\\texttt{\\textless{}@liferay.languages\\ /\\textgreater{}} \\\\\nnavigation\\_menu & default\\_preferencesinstance\\_id & Adds the\nNavigation Menu portlet with optional preferences and instance ID. &\n\\texttt{\\textless{}@liferay.navigation\\_menu\\ /\\textgreater{}} \\\\\nsearch & default\\_preferences & Adds the Search portlet with optional\npreferences & \\texttt{\\textless{}@liferay.search\\ /\\textgreater{}} \\\\\nsearch\\_bar & default\\_preferences & Adds the Search Bar portlet with\noptional preferences &\n\\texttt{\\textless{}@liferay.search\\_bar\\ /\\textgreater{}} \\\\\nuser\\_personal\\_bar & N/A & Adds the User Personal Bar portlet &\n\\texttt{\\textless{}@liferay.user\\_personal\\_bar\\ /\\textgreater{}} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nA few reference examples are shown below.\n\n\\section{Reference Examples}\\label{reference-examples}\n\nThe example below includes a language key with the \\texttt{language}\nmacro directive along with its language \\texttt{key} parameter:\n\n\\begin{verbatim}\n<@liferay.language key=\"powered-by\" />\n\\end{verbatim}\n\nThis example includes the Search portlet with its\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/theming-portlets\\#portlet-decorators}{Portlet\nDecorator} portlet preference set to barebone:\n\n\\begin{verbatim}\n<@liferay.search default_preferences=\n  freeMarkerPortletPreferences.getPreferences(\n    \"portletSetupPortletDecoratorId\", \"barebone\"\n  ) \n/>\n\\end{verbatim}\n\nYou can also pass multiple portlet preferences in an object, as shown in\nthe example below for the Navigation Menu portlet:\n\n\\begin{verbatim}\n<#assign secondaryNavigationPreferencesMap = \n  {\n    \"displayStyle\": \"ddmTemplate_NAVBAR-BLANK-JUSTIFIED-FTL\", \n    \"portletSetupPortletDecoratorId\": \"barebone\", \n    \"rootLayoutType\": \"relative\", \n    \"siteNavigationMenuId\": \"0\", \n    \"siteNavigationMenuType\": \"1\"\n  } \n/>\n\n<@liferay.navigation_menu\n  default_preferences=\n  freeMarkerPortletPreferences.getPreferences(secondaryNavigationPreferencesMap)\n  instance_id=\"main_navigation_menu\"\n/>\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Portlet preferences are unique to each portlet, so first\nyou must determine which preferences you want to configure. There are\ntwo ways to determine the proper key/value pair for a portlet\npreference. The first is to set the portlet preference manually, and\nthen check the values in the \\texttt{portletPreferences.preferences}\ncolumn of the database as a hint for what to configure.\n\nAnother approach is to search each app in your bundle for the keyword\n\\texttt{preferences-\\/-}. This returns app JSPs that have the portlet\npreferences defined for the portlet.\n\n\\chapter{Front-End Taglibs}\\label{front-end-taglibs}\n\nYou have access to a powerful set of taglibs for creating commonly used\nUI components in your apps, themes, and web content. The following\ntaglibs are covered in this section:\n\n\\begin{itemize}\n\\item\n  AUI: create common UI components such as forms, buttons, and more.\n\\item\n  Chart: visualize data. Create bar charts, line charts, scatter charts,\n  spline charts, and much more.\n\\item\n  Clay: create\n  \\href{https://clayui.com/docs/components/alerts.html}{Clay\n  components}, such as alerts, buttons, drop-down menus, form elements,\n  and more for your apps.\n\\item\n  Frontend: create UI components commonly used throughout Portal's apps,\n  such as add menus, cards, management bars, and more.\n\\item\n  Liferay UI: create common UI components such as icons, tabs, and more.\n\\item\n  Liferay Util: load additional resources, define parameters, buffer\n  content, and more.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Each taglib is available as a FreeMarker macro, except\nfor the Chart taglib. The Chart taglib is \\textbf{not} available as a\nFreeMarker macro. The articles in this section provide the proper syntax\nto use for each macro. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/product-freemarker-macros}{FreeMarker\nTaglib Mappings reference} for a complete list of the available\nFreeMarker taglib macros.\n\n\\noindent\\hrulefill\n\nIn this section, you'll learn how to use taglibs to build awesome user\ninterfaces for your apps!\n\n\\chapter{Liferay Theme Objects Available in\nJSPs}\\label{liferay-theme-objects-available-in-jsps}\n\nWhen you include the\n\\texttt{\\textless{}liferay-theme:defineObjects\\textgreater{}} tag in\nyour JSP, you gain access to several Liferay theme objects via\nvariables. These objects are described in the table below:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nObject\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{account} & The user's Account object. This object maps to the\nAccount table in the Liferay database. \\\\\n\\texttt{colorScheme} & An object representing the current color scheme\nin the theme that is being rendered by the portal \\\\\n\\texttt{company} & The current Company object. This represents the\nportal instance on which the user is currently navigating. \\\\\n\\texttt{contact} & The user's Contact object. This object maps to the\nContact table in the Liferay database. \\\\\n\\texttt{layout} & The page to which the user has currently navigated \\\\\n\\texttt{layoutTypePortlet} & This object can be used to programmatically\nadd or remove portlets from a page. \\\\\n\\texttt{locale} & The current user's locale, as defined by Java \\\\\n\\texttt{permissionChecker} & An object that can determine---given a\nparticular resource---whether the current user has a particular\npermission for that resource \\\\\n\\texttt{plid} & A portal layout ID. This is a unique identifier for any\npage that exists in the portal, across all portal instances. \\\\\n\\texttt{portletDisplay} & An object that gives the programmer access to\nmany attributes of the current portlet, including the portlet name, the\nportlet mode, the ID of the column on the layout in which it resides,\nand more \\\\\n\\texttt{realUser} & When an administrator is impersonating a user, this\nvariable tracks the administrator's User object. \\\\\n\\texttt{scopeGroupId} & By default, contains the groupId for the\ncommunity or organization in which this portlet resides. If the\nscopeable attribute is set to true, this may contain a unique scope\nidentifier for custom scopes, such as the page scope, if the portlet has\nbeen configured to use a custom scope. \\\\\n\\texttt{theme} & An object representing the current theme that is being\nrendered by the portal \\\\\n\\texttt{themeDisplay} & A runtime object that contains many useful\nitems, such as the logged-in user, the layout, logo information, paths,\nand much more \\\\\n\\texttt{timeZone} & The current user's time zone, as defined by Java \\\\\n\\texttt{user} & The User object representing the current user \\\\\n\\end{longtable}\n\n\\chapter{Liferay Portlet Objects Available in\nJSPs}\\label{liferay-portlet-objects-available-in-jsps}\n\nYou may have noticed the\n\\texttt{\\textless{}liferay-portlet:defineObjects\\textgreater{}} tag in\nyour JSPs. Similar to the\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-theme-objects-available-in-jsps}{theme:defineObjects}\ntag, when you include this tag in your JSP, you gain access to several\nvariables that, in this case, return useful information about your\nportlet. Note that the JSR-286 specification defines four lifecycle\nmethods for a portlet: processAction, processEvent, render, and\nserveResource. Some of the variables defined by the\n\\texttt{\\textless{}portlet:defineObjects/\\textgreater{}} tag are only\navailable to a JSP if the JSP was included during the appropriate phase\nof the portlet lifecycle. These objects are described in the table\nbelow:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.3529}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.6471}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nObject\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{ActionRequest\\ actionRequest} & Represents the request sent to\nthe portlet to handle an action. \\texttt{actionRequest} is only\navailable to a JSP if the JSP was included during the action-processing\nphase. \\\\\n\\texttt{ActionResponse\\ actionResponse} & Represents the portlet\nresponse to an action request. \\texttt{actionResponse} is only available\nto a JSP if the JSP was included in the action-processing phase. \\\\\n\\texttt{EventRequest\\ eventRequest} & Represents the request sent to the\nportlet to handle an event. \\texttt{eventRequest} is only available to a\nJSP if the JSP was included during the event-processing phase. \\\\\n\\texttt{EventResponse\\ eventResponse} & Represents the portlet response\nto an event request. \\texttt{eventResponse} is only available to a JSP\nif the JSP was included in the event-processing phase. \\\\\n\\texttt{HeaderRequest\\ headerRequest} & Represents the request sent to\nthe portlet to handle its HTML header or HEAD section.\n\\texttt{headerRequest} is only available to a JSP if the JSP was\nincluded during the header-processing phase. \\\\\n\\texttt{HeaderResponse\\ headerResponse} & Represents the portlet\nresponse to a header request. \\texttt{headerResponse} is only available\nto a JSP if the JSP was included in the header-processing phase. \\\\\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/LiferayPortletRequest.html}{\\texttt{LiferayPortletRequest\\ liferayPortletRequest}}\n& Provides access to the \\texttt{HttpServletRequest}, the\n\\texttt{Portlet}, and the portlet name and lifecycle value.\n\\texttt{liferayPortletRequest} is available in all portlet phases. \\\\\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/LiferayPortletResponse.html}{\\texttt{LiferayPortletResponse\\ liferayPortletResponse}}\n& Includes the properties returned to the portal and provides a means to\nadd or change properties. \\texttt{liferayPortletResponse} is available\nin all portlet phases. \\\\\n\\texttt{RenderRequest\\ renderRequest} & Represents the request sent to\nthe portlet to render the portlet. \\texttt{renderRequest} is only\navailable to a JSP if the JSP was included during the render request\nphase. \\\\\n\\texttt{RenderResponse\\ renderResponse} & Represents an object that\nassists the portlet in sending a response to the portal.\n\\texttt{renderResponse} is only available to a JSP if the JSP was\nincluded during the render request phase. \\\\\n\\texttt{ResourceRequest\\ resourceRequest} & Represents the request sent\nto the portlet for rendering resources. \\texttt{resourceRequest} is only\navailable to a JSP if the JSP was included during the resource-serving\nphase. \\\\\n\\texttt{ResourceResponse\\ resourceResponse} & Represents an object that\nassists the portlet in rendering a resource. \\texttt{resourceResponse}\nis only available to a JSP if the JSP was included in the\nresource-serving phase. \\\\\n\\texttt{PortletConfig\\ portletConfig} & Represents the portlet's\nconfiguration including, the portlet's name, initialization parameters,\nresource bundle, and application context. \\texttt{portletConfig} is\nalways available to a portlet JSP, regardless of the request-processing\nphase in which it was included. \\\\\n\\texttt{PortletPreferences\\ portletPreferences} & Provides access to a\nportlet's preferences. \\texttt{portletPreferences} is always available\nto a portlet JSP, regardless of the request-processing phase in which it\nwas included. \\\\\n\\texttt{Map\\textless{}String,\\ String{[}{]}\\textgreater{}\\ portletPreferencesValues}\n& Provides a Map equivalent to the \\texttt{portletPreferences.getMap()}\ncall or an empty Map if no portlet preferences exist. \\\\\n\\texttt{PortletSession\\ portletSession} & Provides a way to identify a\nuser across more than one request and to store transient information\nabout a user. A \\texttt{portletSession} is created for each user client.\n\\texttt{portletSession} is always available to a portlet JSP, regardless\nof the request-processing phase in which it was included.\n\\texttt{portletSession} is \\texttt{null} if no session exists. \\\\\n\\texttt{Map\\textless{}String,\\ Object\\textgreater{}\\ portletSessionScope}\n& Provides a Map equivalent to the\n\\texttt{PortletSession.getAtrributeMap()} call or an empty Map if no\nsession attributes exist. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nFor more details, visit the\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/}{Portlet 3.0\nAPI Javadoc}.\n\n\\chapter{Using the Liferay UI Taglib}\\label{using-the-liferay-ui-taglib}\n\nThe Liferay UI tag library provides tags that implement commonly used UI\ncomponents. These tags make your markup consistent, responsive, and\naccessible.\n\nYou can find a list of the available Liferay UI taglibs in the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/liferay-ui/tld-summary.html}{Liferay\nUI taglibdocs}. Each taglib has a list of attributes that can be passed\nto the tag. Some of these are required and some are optional. See the\ntaglibdocs to view the requirements for each tag. You'll find the full\nmarkup generated by the tags in their JSPs in their\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/portal-web/docroot/html/taglib/ui}{Liferay\nGithub Repo} folders.\n\nTo use the Liferay-UI taglib library in your apps, you must add the\nfollowing declaration to your JSP:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"liferay-ui\" uri=\"http://liferay.com/tld/ui\" %>\n\\end{verbatim}\n\nThe Liferay-UI taglib is also available via a macro for your FreeMarker\ntheme and web content templates. Follow this syntax:\n\n\\begin{verbatim}\n<@liferay_ui[\"tag-name\"] attribute=\"string value\" attribute=10 />\n\\end{verbatim}\n\nThis section covers how to create UI components with the Liferay UI\ntaglibs. Each article contains code examples along with a screenshot of\nthe resulting UI.\n\n\\chapter{Liferay UI Icons}\\label{liferay-ui-icons}\n\nThe Liferay UI taglibs provide several icons you can include in your\napps. To add an icon to your app, use the \\texttt{liferay-ui:icon} tag\nand specify the icon with either the \\texttt{icon},\n\\texttt{iconCssClass}, or \\texttt{image} attribute. An example of each\nuse case is shown below.\n\nThe \\texttt{image} attribute specifies\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-unstyled/src/main/resources/META-INF/resources/_unstyled/images}{Liferay\nUI icons} to use (as defined in the Unstyled theme's\n\\texttt{images/common} folder). Here's an example configuration for a\nJSP:\n\n\\begin{verbatim}\n<div class=\"col-md-3\">\n    <liferay-ui:icon image=\"subscribe\" />\n\n    <span class=\"ml-2\">Subscribe</span>\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-ui-taglib-icon-subscribe.png}\n\\caption{Use the image attribute to use a theme icon.}\n\\end{figure}\n\nThe Liferay UI taglib also exposes language flag icons. To use a\nlanguage flag icon, provide the \\texttt{../language/} relative path\nbefore the icon's name. Below is an example snippet from the Web Content\nSearch portlet that displays the current language's flag along with a\nlocalized message:\n\n\\begin{verbatim}\n<liferay-ui:icon\n    image='<%= \"../language/\" + languageId %>'\n    message='<%= LanguageUtil.format(\n        request,\n        \"this-result-comes-from-the-x-version-of-this-content\",\n        snippetLocale.getDisplayLanguage(locale),\n        false\n    ) %>'\n/>\n\\end{verbatim}\n\nYou can achieve the same result in FreeMarker with the following code\nthat uses the available\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-theme/frontend-theme-unstyled/src/main/resources/META-INF/resources/_unstyled/templates/init.ftl}{\\texttt{init.ftl}\nvariables} and\n\\href{/docs/7-2/reference/-/knowledge_base/r/product-freemarker-macros}{Liferay\nDXP macros}:\n\n\\begin{verbatim}\n<#assign flag_message>\n    <@liferay.language_format \n        arguments=language \n        key=\"this-result-comes-from-the-x-version-of-this-content\" \n    />\n</#assign>\n\n<@liferay_ui[\"icon\"]\n    image=\"../language/${language_id}\"\n    message=flag_message\n/>\n\\end{verbatim}\n\nThe full list of available icons is shown in the figures below:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-ui-taglib-icons.png}\n\\caption{The Liferay UI taglib offers multiple icons for use in your\napp.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-ui-taglib-icon-flags.png}\n\\caption{Liferay UI icons can be configured based on language.}\n\\end{figure}\n\nThe \\texttt{icon} attribute specifies\n\\href{https://fontawesome.com/v3.2.1/icons/}{Font Awesome icons} to use:\n\n\\begin{verbatim}\n<liferay-ui:icon icon=\"angle-down\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-ui-taglib-icon-angle-down.png}\n\\caption{You can use the icon attribute to include Font Awesome icons in\nyour app.}\n\\end{figure}\n\nThe \\texttt{iconCssClass} attribute specifies a\n\\href{http://marcoceppi.github.io/bootstrap-glyphicons/}{glyphicon} to\nuse:\n\n\\begin{verbatim}\n<liferay-ui:icon\n    iconCssClass=\"icon-remove-sign\"\n    label=\"<%= true %>\"\n    message=\"unsubscribe\"\n    url=\"<%= unsubscribeURL %>\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-ui-taglib-icon-css-class.png}\n\\caption{You can use Font Awesome icons in your app.}\n\\end{figure}\n\nThe examples above use some of the icon's available attributes. See the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/liferay-ui/icon.html}{Icon\ntaglibdocs} for the full list.\n\n\\section{Related Topics}\\label{related-topics}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Clay Icons}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-lists}{Liferay\n  UI Icon Lists}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-menus}{Liferay\n  UI Icon Menus}\n\\end{itemize}\n\n\\chapter{Liferay UI Icon Lists}\\label{liferay-ui-icon-lists}\n\nAn icon list displays icons in a horizontal list, instead of in a pop-up\nnavigation menu like an\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-menus}{icon\nmenu}. You can see an example of an icon list menu in a message board\nthread. The thread's actions are visible at all times for\nadministrators:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-ui-taglib-icon-list.png}\n\\caption{Icon lists display an app's actions at all times.}\n\\end{figure}\n\nCreate the list menu with the \\texttt{liferay-ui:icon-list} tag and nest\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icons}{icons}\nfor each list item, as shown below:\n\n\\begin{verbatim}\n<div class=\"thread-actions\">\n    <liferay-ui:icon-list>\n\n        <liferay-ui:icon\n        iconCssClass=\"icon-lock\"\n        message=\"permissions\"\n        method=\"get\"\n        url=\"<%= permissionsURL %>\"\n        useDialog=\"<%= true %>\"\n        />\n\n        <liferay-rss:rss\n        delta=\"<%= rssDelta %>\"\n        displayStyle=\"<%= rssDisplayStyle %>\"\n        feedType=\"<%= rssFeedType %>\"\n        url=\"<%= MBRSSUtil.getRSSURL(plid, 0, message.getThreadId(), 0, themeDisplay) %>\"\n        />\n\n        <liferay-ui:icon\n        iconCssClass=\"icon-remove-sign\"\n        message=\"unsubscribe\"\n        url=\"<%= unsubscribeURL %>\"\n        />\n\n        <liferay-ui:icon\n        iconCssClass=\"icon-lock\"\n        message=\"lock\"\n        url=\"<%= lockThreadURL %>\"\n        />\n\n        <liferay-ui:icon\n        iconCssClass=\"icon-move\"\n        message=\"move\"\n        url=\"<%= editThreadURL %>\"\n        />\n\n        <liferay-ui:icon-delete\n        showIcon=\"<%= true %>\"\n        trash=\"<%= trashHelper.isTrashEnabled(themeDisplay.getScopeGroupId()) %>\"\n        url=\"<%= deleteURL %>\"\n        />\n    </liferay-ui:icon-list>\n</div>\n\\end{verbatim}\n\nSee the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/liferay-ui/icon-list.html}{Icon\nList taglibdocs} for the full list of available attributes.\n\n\\section{Related Topics}\\label{related-topics-1}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Clay Icons}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-menus}{Liferay\n  UI Icon Menus}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icons}{Liferay\n  UI Icons}\n\\end{itemize}\n\n\\chapter{Liferay UI Icon Menus}\\label{liferay-ui-icon-menus}\n\nYou can add a pop-up navigation menu to your app with the\n\\texttt{liferay-ui:icon-menu} tag. Icon menus display menu options when\nneeded, storing them away in a collapsed menu when they're not. This\nkeeps the UI clean and uncluttered. Just as with an icon list, you nest\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icons}{icons}\nfor each navigation item. You can see an example of a icon menu in a\nsite's actions menu in the My Sites portlet:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-ui-taglib-icon-menu.png}\n\\caption{Setting up an icon menu is a piece of cake.}\n\\end{figure}\n\nExample JSP configuration:\n\n\\begin{verbatim}\n<liferay-ui:icon-menu\n    direction=\"left-side\"\n    icon=\"<%= StringPool.BLANK %>\"\n    markupView=\"lexicon\"\n    message=\"<%= StringPool.BLANK %>\"\n    showWhenSingleIcon=\"<%= true %>\"\n>\n\n                <liferay-ui:icon\n                    message=\"go-to-public-pages\"\n                    target=\"_blank\"\n                    url=\"<%= group.getDisplayURL(themeDisplay, false) %>\"\n                />\n\n                <liferay-ui:icon\n                    message=\"leave\"\n                    url=\"<%= leaveURL %>\"\n                />\n\n</liferay-ui:icon-menu>\n\\end{verbatim}\n\nNote that the \\texttt{url} attribute is required for icons to render\nproperly. See the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/liferay-ui/icon-menu.html}{Icon\nMenu taglibdocs} for the full list of attributes.\n\n\\section{Related Topics}\\label{related-topics-2}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Clay Icons}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-lists}{Liferay\n  UI Icon Lists}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icons}{Liferay\n  UI Icons}\n\\end{itemize}\n\n\\chapter{Liferay UI Tabs}\\label{liferay-ui-tabs}\n\nTabs create dividers that organize content into individual sections.\nContent can be embedded or included from another JSP.\n\nTo add tabs to your app, use the\n\\texttt{\\textless{}liferay-ui:tabs\\textgreater{}} tag and specify each\ntab's name as a comma-separated list for the \\texttt{names} attribute.\nFor example, three tabs named \\texttt{tab1}, \\texttt{tab2}, and\n\\texttt{tab3}, look like this in the JSP:\n\n\\begin{verbatim}\n<liferay-ui:tabs names=\"tab1,tab2,tab3\">\n\n</liferay-ui:tabs>\n\\end{verbatim}\n\nEach tab requires a corresponding section to display content. Nest\n\\texttt{liferay-ui:section} tags for each of the tabs. Within each\nsection, you can add HTML content or add content indirectly by including\ncontent from another JSP (via the\n\\texttt{\\textless{}\\%@\\ includefile=\"filepath\"\\%\\textgreater{}}\ndirective). The example snippet below is from the Calendar portlet's\n\\texttt{configuration.jsp}:\n\n\\begin{verbatim}\n<liferay-ui:tabs\n    names='<%= \"user-settings,display-settings,rss\" %>'\n    param=\"tabs2\"\n    refresh=\"<%= false %>\"\n    type=\"tabs nav-tabs-default\"\n>\n    <liferay-ui:section>\n        <%@ include file=\"/configuration/user_settings.jspf\" %>\n    </liferay-ui:section>\n\n    <liferay-ui:section>\n        <%@ include file=\"/configuration/display_settings.jspf\" %>\n    </liferay-ui:section>\n\n    <liferay-ui:section>\n        <%@ include file=\"/configuration/rss.jspf\" %>\n    </liferay-ui:section>\n</liferay-ui:tabs>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-ui-taglib-tabs.png}\n\\caption{Tabs are a useful way to organize configuration options into\nindividual sections within the same UI.}\n\\end{figure}\n\nThe example above uses some of the tab's available attributes. See the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/taglibs/util-taglib/liferay-ui/tabs.html}{Tabs\ntaglibdocs} for the full list of attributes.\n\n\\section{Related Topics}\\label{related-topics-3}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-navigation-bars}{Clay\n  Navigation Bars}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-dropdown-menus-and-action-menus}{Clay\n  Dropdown Menus and Action Menus}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-help}{Liferay\n  UI Icon Help}\n\\end{itemize}\n\n\\chapter{Liferay UI Icon Help}\\label{liferay-ui-icon-help}\n\nThe icon help tag lets you communicate additional information to your\nusers in an unobtrusive way. It renders as an iconic question mark that\nprovides more information through a pop-up tooltip on mouse over. You\ncan see an example of this in the Control Panel:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-ui-taglib-tooltip.png}\n\\caption{Here's an example of the icon help tag.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you have installed a custom theme you may also need to\nadd the following imports to your \\texttt{view.jsp} to make\n\\texttt{liferay-ui:icon-help} tag work:\n\n\\begin{verbatim}\n <%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\"%>\n <liferay-theme:defineObjects />\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nAdd the \\texttt{\\textless{}liferay-ui:icon-help/\\textgreater{}} tag next\nto the UI that needs tooltip information. Define the informational text\nwith the required \\texttt{message} attribute. Below is an example\nsnippet for one of the Server Administration's clean up actions:\n\n\\begin{verbatim}\n<h5>\n    <liferay-ui:message key=\"clean-up-permissions\" />\n    <liferay-ui:icon-help message=\"clean-up-permissions-help\" />\n</h5>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-ui-taglib-tooltip-02.png}\n\\caption{help icons are used throughout the Control Panel.}\n\\end{figure}\n\nNote that the message is supplied via a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application}{language\nkey}. While you can use a string for the tooltip's message for testing\npurposes, a language key is considered best practice and should be used\nin production.\n\n\\section{Related Topics}\\label{related-topics-4}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-badges}{Clay Badges}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-stickers}{Clay\n  Stickers}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-menus}{Liferay\n  UI Icon Menus}\n\\end{itemize}\n\n\\chapter{Using Liferay Front-end Taglibs in Your\nPortlet}\\label{using-liferay-front-end-taglibs-in-your-portlet}\n\nThe Liferay Front-end tag library provides a set of tags for creating\ncommon front-end UI components in your app.\n\nTo use the Front-end taglib in you apps, add the following declaration\nto your JSP:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"liferay-frontend\" uri=\"http://liferay.com/tld/frontend\" %>\n\\end{verbatim}\n\nThe Liferay Front-end taglib is also available via a macro for your\nFreeMarker theme templates and web content templates. Follow this\nsyntax:\n\n\\begin{verbatim}\n<@liferay_frontend[\"tag-name\"] attribute=\"string value\" attribute=10 />\n\\end{verbatim}\n\nThe following Front-end UI components are covered in this section:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-add-menu}{Add\n  Menu}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-cards}{Cards}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-info-bar}{Info\n  Bar}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-management-bar}{Management\n  Bar}\n\\end{itemize}\n\nEach article contains a set of examples along with a screenshot of the\nresulting UI.\n\n\\chapter{Liferay Front-end Add Menu}\\label{liferay-front-end-add-menu}\n\nThe add menu tag creates an add menu button for one or multiple items.\nIt's used for actions that add entities (e.g.~a new blog entry), and is\npart of the Management Bar. Use the\n\\texttt{\\textless{}liferay-frontend:add-menu\\textgreater{}} tag to\ncreate the add menu and nest a\n\\texttt{\\textless{}liferay-frontend:add-menu-item\\textgreater{}} tag for\neach item.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} This pattern is deprecated as of 7.0. We recommend that\nyou use the Clay Management Toolbar's\n\\href{/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar\\#creation-menu}{creation\nmenu pattern} instead.\n\n\\noindent\\hrulefill\n\nWhen the menu has one item, the button triggers the item's action as\nshown in the example below for the Blogs Admin App:\n\n\\begin{verbatim}\n<liferay-frontend:management-bar>\n  <liferay-frontend:management-bar-buttons>\n      ...\n      <liferay-frontend:add-menu\n        inline=\"<%= true %>\"\n      >\n        <liferay-frontend:add-menu-item\n          title='<%= LanguageUtil.get(request, \"add-blog-entry\") %>'\n          url=\"<%= addEntryURL %>\"\n        />\n      </liferay-frontend:add-menu>\n\n  </liferay-frontend:management-bar-buttons>\n</liferay-frontend:management-bar>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-add-menu-one-item.png}\n\\caption{The add button pattern consists of an \\texttt{add-menu} tag and\nat least one \\texttt{add-menu-item} tag.}\n\\end{figure}\n\nWhen the menu has multiple items, they display in a pop-up menu. For\nexample, the Message Boards Admin application has the configuration\nbelow:\n\n\\begin{verbatim}\n<liferay-frontend:add-menu>\n    ...\n    <liferay-frontend:add-menu-item title='<%= LanguageUtil.get(request,\n    \"thread\") %>' url=\"<%= addMessageURL.toString() %>\" />\n    ...\n    <liferay-frontend:add-menu-item title='<%= LanguageUtil.get(request,\n    (categoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) ?\n    \"category[message-board]\" : \"subcategory[message-board]\") %>'\n    url=\"<%= addCategoryURL.toString() %>\" />\n    ...\n</liferay-frontend:add-menu>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-add-menu-items.png}\n\\caption{The add button pattern consists of an \\texttt{add-menu} tag and\nat least one \\texttt{add-menu-item} tag.}\n\\end{figure}\n\nThe examples above use some of the available attributes. See the\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/taglibdocs/liferay-frontend/add-menu.html}{add\nmenu} and\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/taglibdocs/liferay-frontend/add-menu-item.html}{add\nmenu item} taglibdocs for the full list of available attributes for the\ntags.\n\n\\section{Related Topics}\\label{related-topics-5}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-cards}{Liferay\n  Frontend Cards}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-info-bar}{Liferay\n  Frontend Info Bar}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-management-bar}{Liferay\n  Frontend Management Bar}\n\\end{itemize}\n\n\\chapter{Liferay Front-end Cards}\\label{liferay-front-end-cards}\n\nIf you have data you want to compare that's heavy on image usage, cards\nare the component for the job. Cards visually represent data in a\nminimal and compact format. Use them for images, document libraries,\nuser profiles, and more. There are four main types of Cards:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Horizontal Cards\n\\item\n  Icon Cards\n\\item\n  Vertical Cards\n\\item\n  User Cards\n\\end{itemize}\n\nExamples of each card are shown below.\n\n\\section{Horizontal Card}\\label{horizontal-card}\n\nHorizontal cards are used primarily to display documents, such as files\nand folders. An example configuration is shown below:\n\n\\begin{verbatim}\n<liferay-frontend:horizontal-card\n    text=\"Documents\"\n  url=\"https://portal.liferay.dev/docs/7-1/tutorials/-/knowledge_base/t/clay-icons\"\n>\n    <liferay-frontend:horizontal-card-col>\n                <liferay-frontend:horizontal-card-icon\n                    icon=\"folder\"\n                />\n    </liferay-frontend:horizontal-card-col>\n</liferay-frontend:horizontal-card>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-cards-horizontal.png}\n\\caption{Horizontal cards are perfect to display files and documents.}\n\\end{figure}\n\nThe\n\\texttt{\\textless{}liferay-frontend:horizontal-card-icon\\textgreater{}}\ntag uses \\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Clay\nIcons} for its \\texttt{icon} attribute.\n\n\\section{Icon Vertical Card}\\label{icon-vertical-card}\n\nIcon vertical cards, as the name suggests, are cards that display\ninformation in a vertical format that emphasizes an icon. These cards\nshow content that doesn't have an associated image. Instead, an icon\nrepresenting the type of content is displayed. The example snippet below\ndisplays information for a web content article:\n\n\\begin{verbatim}\n<liferay-frontend:icon-vertical-card\n  cssClass=\"article-preview-content\"\n  icon=\"web-content\"\n  title=\"<%= title %>\"\n>\n  <liferay-frontend:vertical-card-sticker-bottom>\n    <liferay-ui:user-portrait\n      cssClass=\"sticker sticker-bottom\"\n      userId=\"<%= assetRenderer.getUserId() %>\"\n    />\n  </liferay-frontend:vertical-card-sticker-bottom>\n\n  <liferay-frontend:vertical-card-footer>\n    <aui:workflow-status \n      markupView=\"lexicon\" \n      showIcon=\"<%= false %>\" \n      showLabel=\"<%= false %>\" \n      status=\"<%= article.getStatus() %>\" \n    />\n  </liferay-frontend:vertical-card-footer>\n</liferay-frontend:icon-vertical-card>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-cards-icon-vertical.png}\n\\caption{Vertical icon cards are perfect to display an entity selection,\nsuch as a web content article.}\n\\end{figure}\n\n\\section{Vertical Card}\\label{vertical-card}\n\nVertical cards display information in a vertical card format, as opposed\nto a horizontal format. If the content has an associated image (like a\nblog header image) you can use a vertical card to display the image. If\nthere is no associated image, you can use an icon vertical card to\nrepresent the content's type instead (e.g.~a PDF file). The example\nbelow displays a vertical card for a web content article when an image\npreview is available:\n\n\\begin{verbatim}\n<liferay-frontend:vertical-card\n  cssClass=\"article-preview-content\"\n  imageUrl=\"<%= articleImageURL %>\"\n  title=\"<%= title %>\"\n>\n  <liferay-frontend:vertical-card-sticker-bottom>\n    <liferay-ui:user-portrait\n      cssClass=\"sticker sticker-bottom\"\n      userId=\"<%= assetRenderer.getUserId() %>\"\n    />\n  </liferay-frontend:vertical-card-sticker-bottom>\n\n  <liferay-frontend:vertical-card-footer>\n    <aui:workflow-status \n      markupView=\"lexicon\" \n      showIcon=\"<%= false %>\" \n      showLabel=\"<%= false %>\" \n      status=\"<%= article.getStatus() %>\" \n    />\n  </liferay-frontend:vertical-card-footer>\n</liferay-frontend:vertical-card>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-cards-vertical.png}\n\\caption{Vertical cards are perfect to display files and documents.}\n\\end{figure}\n\n\\section{HTML Vertical Card}\\label{html-vertical-card}\n\nThe HTML Vertical card lets you display custom HTML in the header of the\nvertical card. The example below embeds a video:\n\n\\begin{verbatim}\n<liferay-util:buffer var = \"customThumbnailHtml\">\n    <div class=\"embed-responsive embed-responsive-16by9\">\n      <iframe class=\"embed-responsive-item\" \n    src=\"https://www.youtube.com/embed/8Bg9jPJpGOM?rel=0\" \n    allowfullscreen></iframe>\n    </div>\n</liferay-util:buffer>\n\n<div class=\"container\">\n  <div class=\"row\">\n    <div class=\"col-md-4\">\n      <liferay-frontend:html-vertical-card\n        html=\"<%= customThumbnailHtml %>\"\n        title=\"My Video\"\n      >\n      </liferay-frontend:html-vertical-card>\n    </div>\n  </div>\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-cards-html-vertical.png}\n\\caption{Html vertical cards let you display custom HTML in the card's\nheader.}\n\\end{figure}\n\n\\section{User Vertical Card}\\label{user-vertical-card}\n\nThe User Vertical card displays user profile selections in the icon view\nof the Management Bar. Below is an example snippet from the User Admin\nportlet:\n\n\\begin{verbatim}\n<liferay-frontend:user-vertical-card\n  actionJsp=\"/membership_request_action.jsp\"\n  actionJspServletContext=\"<%= application %>\"\n  resultRow=\"<%= row %>\"\n  subtitle=\"<%= membershipRequestUser.getEmailAddress() %>\"\n  title=\"<%= HtmlUtil.escape(membershipRequestUser.getFullName()) %>\"\n  userId=\"<%= membershipRequest.getUserId() %>\"\n>\n  <liferay-frontend:vertical-card-header>\n    <liferay-ui:message \n      arguments=\"<%= LanguageUtil.getTimeDescription(\n      request, \n      System.currentTimeMillis() - membershipRequest.getCreateDate().getTime(), \n      true) %>\" \n      key=\"x-ago\" \n      translateArguments=\"<%= false %>\" \n    />\n  </liferay-frontend:vertical-card-header>\n</liferay-frontend:user-vertical-card>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-cards-user-vertical.png}\n\\caption{User vertical cards are perfect to display files and\ndocuments.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-6}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-add-menu}{Liferay\n  Front-end Add Menu}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-info-bar}{Liferay\n  Front-end Info Bar}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-management-bar}{Liferay\n  Front-end Management Bar}\n\\end{itemize}\n\n\\chapter{Liferay Front-end Info Bar}\\label{liferay-front-end-info-bar}\n\nAn info bar provides a button that toggles the visibility of additional\nsidebar information. This is perfect for providing more detailed\nmetadata for a search result, such as the file size, type, URL, etc.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-info-bar-article.png}\n\\caption{The info bar tags create a sidebar panel toggler that reveals\nadditional info.}\n\\end{figure}\n\nThe configuration has two key parts: the info bar---and buttons---and\nthe sidebar panel.\n\nInfo bar:\n\n\\begin{verbatim}\n<liferay-frontend:info-bar>\n  <liferay-frontend:info-bar-buttons>\n    <liferay-frontend:info-bar-sidenav-toggler-button\n      icon=\"info-circle\"\n      label=\"my info\"\n    />\n  </liferay-frontend:info-bar-buttons>\n</liferay-frontend:info-bar>\n\\end{verbatim}\n\nThe\n\\texttt{\\textless{}liferay-frontend:info-bar-sidenav-toggler-button\\textgreater{}}\ntag uses \\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Clay\nIcons} for the \\texttt{icon} attribute.\n\nSidebar panel:\n\n\\begin{verbatim}\n<div class=\"closed container-fluid-1280 sidenav-container sidenav-right\" id=\"<portlet:namespace />infoPanelId\">\n    <liferay-frontend:sidebar-panel>\n      <div>\n      <h2>sidebar content</h2>\n      <p>Here is some content</p>\n      </div>\n    </liferay-frontend:sidebar-panel>\n</div>\n\\end{verbatim}\n\nNote that the sidebar panel's wrapper\n\\texttt{\\textless{}div\\textgreater{}} has the classes \\texttt{closed}\nand \\texttt{sidenav-right}. The info button toggles the classes\n\\texttt{open} and \\texttt{closed}, showing and hiding the sidebar panel.\nThe \\texttt{sidenav-right} class specifies that the panel should open on\nthe right.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-info-bar.png}\n\\caption{The info bar tags create a sidebar panel toggler that reveals\nadditional info.}\n\\end{figure}\n\nThe examples above use some of the available attributes. See the\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/taglibdocs/liferay-frontend/info-bar.html}{info\nbar},\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/taglibdocs/liferay-frontend/info-bar-buttons.html}{info\nbar buttons},\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/taglibdocs/liferay-frontend/info-bar-sidenav-toggler-button.html}{info\nbar sidenav toggler button}, and\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/taglibdocs/liferay-frontend/sidebar-panel.html}{sidebar\npanel} taglibdocs for the full list of available attributes for the\ntags.\n\n\\section{Related Topics}\\label{related-topics-7}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-add-menu}{Liferay\n  Front-end Add Menu}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-cards}{Liferay\n  Front-end Cards}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-management-bar}{Liferay\n  Front-end Management Bar}\n\\end{itemize}\n\n\\chapter{Liferay Front-end Management\nBar}\\label{liferay-front-end-management-bar}\n\nThe Management Bar gives administrators control over search container\nresults. It lets you filter, sort, and choose a display style for search\nresults, so you can quickly identify the document, web content, asset\nentry, or whatever you're looking for in your app. The Management Bar is\nfully customizable, so you can implement all the controls, or just the\nones your app requires.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-management-bar-message-boards.png}\n\\caption{The Management Bar lets the user customize how the app displays\ncontent.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Liferay Front-end Management Bar is deprecated as of\n7.0. We recommend that you use the\n\\href{/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar}{Clay\nManagement Toolbar} instead.\n\n\\noindent\\hrulefill\n\nThe Management Bar has a few key sections. Each section is grouped and\nconfigured using different taglibs:\n\nThe\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/taglibdocs/liferay-frontend/management-bar-buttons.html}{\\texttt{\\textless{}liferay-frontend:management-bar-buttons\\textgreater{}}\ntag} wraps the Management Bar's button elements:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-management-bar-buttons.png}\n\\caption{The \\texttt{management-bar-buttons} tag contains the Management\nBar's main buttons.}\n\\end{figure}\n\nThe\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/taglibdocs/liferay-frontend/management-bar-sidenav-toggler-button.html}{\\texttt{\\textless{}liferay-frontend:management-bar-sidenav-toggler-button\\textgreater{}}\ntag} implements slide-out navigation for the info button.\n\nThe\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/taglibdocs/liferay-frontend/management-bar-display-buttons.html}{\\texttt{\\textless{}liferay-frontend:management-bar-display-buttons\\textgreater{}}\ntag} renders the app's display style options.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-management-bar-display-buttons.png}\n\\caption{The \\texttt{management-bar-display-buttons} tag contains the\ncontent's display options.}\n\\end{figure}\n\nThe\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/taglibdocs/liferay-frontend/management-bar-filters.html}{\\texttt{\\textless{}liferay-frontend:management-bar-filters\\textgreater{}}\ntag} wraps the app's filtering options. This filter should be included\nin all control panel applications. Filtering options can include sort\ncriteria, sort ordering, and more.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-management-bar-filters.png}\n\\caption{The \\texttt{management-bar-filters} tag contains the content\nfiltering options.}\n\\end{figure}\n\nFinally, the\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/taglibdocs/liferay-frontend/management-bar-action-buttons.html}{\\texttt{\\textless{}liferay-frontend:management-bar-action-buttons\\textgreater{}}\ntag} wraps the actions that you can execute over selected items. You can\nselect multiple items between pages. The management bar keeps track of\nthe number of selected items for you.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-management-bar-action-buttons.png}\n\\caption{The management bar keeps track of the items selected and\ndisplays the actions to execute on them.}\n\\end{figure}\n\nFor example, here's the Management Bar configuration in the Trash app:\n\n\\begin{verbatim}\n<liferay-frontend:management-bar\n   includeCheckBox=\"<%= true %>\"\n   searchContainerId=\"trash\"\n>\n   <liferay-frontend:management-bar-buttons>\n       <liferay-frontend:management-bar-sidenav-toggler-button />\n\n       <liferay-portlet:actionURL name=\"changeDisplayStyle\"\n       varImpl=\"changeDisplayStyleURL\">\n           <portlet:param name=\"redirect\" value=\"<%= currentURL %>\" />\n       </liferay-portlet:actionURL>\n\n       <liferay-frontend:management-bar-display-buttons\n           displayViews='<%= new String[] {\"descriptive\", \"icon\",\n           \"list\"} %>'\n           portletURL=\"<%= changeDisplayStyleURL %>\"\n           selectedDisplayStyle=\"<%= trashDisplayContext.getDisplayStyle()\n           %>\"\n       />\n   </liferay-frontend:management-bar-buttons>\n\n   <liferay-frontend:management-bar-filters>\n       <liferay-frontend:management-bar-navigation\n           navigationKeys='<%= new String[] {\"all\"} %>'\n           portletURL=\"<%= trashDisplayContext.getPortletURL() %>\"\n       />\n\n       <liferay-frontend:management-bar-sort\n           orderByCol=\"<%= trashDisplayContext.getOrderByCol() %>\"\n           orderByType=\"<%= trashDisplayContext.getOrderByType() %>\"\n           orderColumns='<%= new String[] {\"removed-date\"} %>'\n           portletURL=\"<%= trashDisplayContext.getPortletURL() %>\"\n       />\n   </liferay-frontend:management-bar-filters>\n\n   <liferay-frontend:management-bar-action-buttons>\n       <liferay-frontend:management-bar-sidenav-toggler-button />\n\n       <liferay-frontend:management-bar-button href=\"javascript:;\"\n       icon=\"trash\" id=\"deleteSelectedEntries\" label=\"delete\" />\n   </liferay-frontend:management-bar-action-buttons>\n</liferay-frontend:management-bar>\n\\end{verbatim}\n\n\\chapter{Including Actions in the Management\nBar}\\label{including-actions-in-the-management-bar}\n\nWhile an actions menu is typically included with each search container\nresult, you can also include these actions in the management bar. This\nkeeps everything organized within the same UI. This update adds a\ncheckbox next to each search container result, as well as adds one in\nthe management bar itself to select all results. The actions are\ndisplayed when a checkbox is checked---individual or select all---and\nhidden from view otherwise.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-management-bar-include-checkbox.png}\n\\caption{You can select individual results or all results at once.}\n\\end{figure}\n\nFollow these steps to include actions in your management bar:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Update the\n  \\texttt{\\textless{}liferay-frontend:management-bar\\textgreater{}} tag\n  to include the checkbox and provide the search container's ID:\n\n\\begin{verbatim}\n<liferay-frontend:management-bar\n    includeCheckBox=\"<%= true %>\"\n    searchContainerId=\"mySearchContainerId\"\n>\n\\end{verbatim}\n\\item\n  After the closing\n  \\texttt{\\textless{}/liferay-frontend:management-bar-filters\\textgreater{}}\n  tag, add the\n  \\texttt{\\textless{}liferay-frontend:management-bar-action-buttons\\textgreater{}}\n  tags:\n\n\\begin{verbatim}\n<liferay-frontend:management-bar-action-buttons>\n\n</liferay-frontend:management-bar-action-buttons>\n\\end{verbatim}\n\\item\n  Use the available management bar button taglibs\n  (e.g.~\\texttt{management-bar-button}) to build the action buttons for\n  your app's management bar. A code snippet from the Site admin portlet\n  is shown below:\n\n\\begin{verbatim}\n<liferay-frontend:management-bar-action-buttons>\n    <liferay-frontend:management-bar-sidenav-toggler-button\n        icon=\"info-circle\"\n        label=\"info\"\n    />\n\n    <liferay-frontend:management-bar-button\n        href=\"javascript:deleteEntries();\"\n        icon=\"trash\"\n        id=\"deleteSites\"\n        label=\"delete\"\n    />\n</liferay-frontend:management-bar-action-buttons>\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-management-bar-actions.png}\n\\caption{You can have as many actions as your app requires.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-8}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/disabling-all-or-portions-of-the-management-bar}{Disabling\n  All or Portions of the Management Bar}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar}{Clay\n  Management Toolbar}\n\\end{itemize}\n\n\\chapter{Disabling All or Portions of the Management\nBar}\\label{disabling-all-or-portions-of-the-management-bar}\n\nWhen there are no search results to display, you should disable all the\nManagement Bar's buttons, except the sidenav toggler button.\n\nYou can disable the Management Bar by adding the \\texttt{disabled}\nattribute to the \\texttt{liferay-frontend:management-bar} tag:\n\n\\begin{verbatim}\n<liferay-frontend:management-bar\n        disabled=\"<%= total == 0 %>\"\n        includeCheckBox=\"<%= true %>\"\n        searchContainerId=\"<%= searchContainerId %>\"\n>\n\\end{verbatim}\n\nYou can also disable individual components by adding the\n\\texttt{disabled} attribute to the corresponding tag. The example below\ndisables the display buttons when the search container displays 0\nresults, since changing the display style has no effect when there\naren't any results to view:\n\n\\begin{verbatim}\n<liferay-frontend:management-bar-display-buttons\n        disabled=\"<%= total == 0 %>\"\n        displayViews='<%= new String[] {\"descriptive\", \"icon\", \"list\"} %>'\n        portletURL=\"<%= changeDisplayStyleURL %>\"\n        selectedDisplayStyle=\"<%= displayStyle %>\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-frontend-taglib-management-bar-disabled.png}\n\\caption{You can disable all or portions of the Management Bar.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-9}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/including-actions-in-the-management-bar}{Including\n  Actions in the Management Bar}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar}{Clay\n  Management Toolbar}\n\\end{itemize}\n\n\\chapter{Using the Liferay Util\nTaglib}\\label{using-the-liferay-util-taglib}\n\nThe Liferay Util taglib is used to pull other resources into a portlet\nor theme. You can use it to specify which resources to insert at the\nbottom or top of the page's HTML.\n\nTo use the Liferay-Util taglib, add the following declaration to your\nJSP:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"liferay-util\" uri=\"http://liferay.com/tld/util\" %>\n\\end{verbatim}\n\nThe Liferay-Util taglib is also available via a macro for your\nFreeMarker theme templates and web content templates. Follow this\nsyntax:\n\n\\begin{verbatim}\n<@liferay_util[\"tag-name\"] attribute=\"string value\" attribute=10 />\n\\end{verbatim}\n\nThis section covers the available Liferay Util tags you can use in your\napp to inject content into portlets and themes.\n\n\\chapter{Using Liferay Util Body\nBottom}\\label{using-liferay-util-body-bottom}\n\nThe body bottom tag is not a self-closing tag. It lets you add\nadditional HTML or scripts to the bottom of the \\texttt{body} tag.\ncontent placed between the opening and closing of this tag is passed to\nthe\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/portal-web/docroot/html/common/themes/body_bottom.jsp\\#L26-L31}{body\\_bottom.jsp}\nand outputs in this JSP.\n\nThis tag also has an optional \\texttt{outputKey} attribute. If several\nportlets on the page include the same resource with this tag, you can\nspecify the same \\texttt{outputKey} value for each tag so the resource\nis only loaded once.\n\nThe example configuration below uses the\n\\texttt{\\textless{}liferay-util:body-bottom\\textgreater{}} tag to\ninclude JavaScript provided by the portlet's bundle:\n\n\\begin{verbatim}\n<liferay-util:body-bottom outputKey=\"bodybottom\" >\n    <script \n  src=\"/o/my-liferay-util-portlet/js/my_custom_javascript_body_bottom.js\" \n  type=\"text/javascript\"></script>\n</liferay-util:body-bottom>\n\\end{verbatim}\n\nNow you know how to use the\n\\texttt{\\textless{}liferay-util:body-bottom\\textgreater{}} tag to\ninclude additional resources in the bottom of the page's body.\n\n\\section{Related Topics}\\label{related-topics-10}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-body-top}{Using\n  the Liferay Util HTML Body Top Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-html-top}{Using\n  the Liferay Util HTML Top Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-liferay-ui-taglib}{Using\n  the Liferay UI Taglib}\n\\end{itemize}\n\n\\chapter{Using Liferay Util Body Top}\\label{using-liferay-util-body-top}\n\nThe body top tag is not a self-closing tag. The content placed between\nthe opening and closing of this tag is moved to the top of the\n\\texttt{body} tag. When something is passed using this taglib, the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/portal-web/docroot/html/common/themes/body_top.jsp\\#L25-L31}{body\\_top.jsp}\nis passed markup and outputs in this JSP.\n\nThis tag also has an optional \\texttt{outputKey} attribute. If several\nportlets on the page include the same resource with this tag, you can\nspecify the same \\texttt{outputKey} value for each tag so the resource\nis only loaded once.\n\nThe example configuration below uses the\n\\texttt{\\textless{}liferay-util:body-top\\textgreater{}} tag to include\nJavaScript provided by the portlet's bundle:\n\n\\begin{verbatim}\n<liferay-util:body-top outputKey=\"bodytop\" >\n    <script \n  src=\"/o/my-liferay-util-portlet/js/my_custom_javascript_body_top.js\" \n  type=\"text/javascript\"></script>\n</liferay-util:body-top>\n\\end{verbatim}\n\nNow you know how to use the\n\\texttt{\\textless{}liferay-util:body-top\\textgreater{}} tag to include\nadditional resources in the top of the page's body.\n\n\\section{Related Topics}\\label{related-topics-11}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-body-bottom}{Using\n  the Liferay Util HTML Body Bottom Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-html-bottom}{Using\n  the Liferay Util HTML Bottom Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-clay-taglib-in-your-portlets}{Using\n  the Clay Taglib}\n\\end{itemize}\n\n\\chapter{Using Liferay Util Buffer}\\label{using-liferay-util-buffer}\n\nThe buffer tag is not a self-closing tag. The content placed between the\nopening and closing of this tag is saved to a buffer and its output is\nassigned to the Java variable declared with the tag's \\texttt{var}\nattribute. The output is returned as a String, letting you post-process\nit. For example, you can use this to\n\\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-osgi-fragments\\#provide-the-overridden-jsp}{override\na JSP's existing contents}.\n\nThe example below saves the link's generated markup to a buffer and then\nuses the returned string as the argument for a\n\\texttt{liferay-ui:message} key:\n\n\\begin{verbatim}\n<liferay-util:buffer\n        var=\"linkContent\"\n>\n        <aui:a \n            href=\"https://www.liferay.com/\" \n            target=\"_blank\">Liferay\n        </aui:a>\n</liferay-util:buffer>\n\n<liferay-ui:message \n        arguments=\"<%= linkContent %>\" \n        key=\"see-x-for-more-information\" \n        translateArguments=\"<%= false %>\" \n/>\n\\end{verbatim}\n\nNow you know how to use the\n\\texttt{\\textless{}liferay-util:buffer\\textgreater{}} tag to save\ncontent to a buffer.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-util-buffer.png}\n\\caption{You can use the Liferay Util Buffer tag to save pieces of\nmarkup to reuse in your JSP.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-12}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-osgi-fragments\\#provide-the-overridden-jsp}{JSP\n  Overrides Using OSGi Fragments}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-param}{Using\n  the Liferay Util Param Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-front-end-taglibs-in-your-portlet}{Using\n  the Liferay Front-End Taglibs}\n\\end{itemize}\n\n\\chapter{Using Liferay Util Dynamic\nInclude}\\label{using-liferay-util-dynamic-include}\n\nThe dynamic include tag lets you specify a point or points in a JSP or\ntheme where a developer can inject additional HTML, resources, or\nfunctionality, using the \\texttt{DynamicIncludeRegistry}. You can read\nmore about the OSGi Service Registry\n\\href{http://docs.spring.io/osgi/docs/current/reference/html/service-registry.html}{here}.\nThe \\texttt{key} attribute identifies the extension point. See\n\\href{/docs/7-2/customization/-/knowledge_base/c/dynamic-includes}{Dynamic\nIncludes} for example configurations that use dynamic include extension\npoints to inject additional functionality.\n\nThe example configuration below uses the\n\\texttt{\\textless{}liferay-util:dynamic-include\\textgreater{}} tag to\ninclude an extension point before the primary code and an extension\npoint after the primary code:\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://liferay.com/tld/util\" prefix=\"liferay-util\" %>\n\n<liferay-util:dynamic-include key=\"/path/to/jsp#pre\" />\n\n<div>\n        <p>And here we have our content</p>\n</div>\n\n<liferay-util:dynamic-include key=\"/path/to/jsp#post\" />\n\\end{verbatim}\n\nNow you know how to use the\n\\texttt{\\textless{}liferay-util:dynamic-include\\textgreater{}} tag to\nadd extension points to your app.\n\n\\section{Related Topics}\\label{related-topics-13}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/dynamic-includes}{Dynamic\n  Includes}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-body-top}{Using\n  the Liferay Util Body Top Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-chart-taglib-in-your-portlets}{Using\n  the Chart Taglib}\n\\end{itemize}\n\n\\chapter{Using Liferay Util Get URL}\\label{using-liferay-util-get-url}\n\nThe get URL tag scrapes the URL provided by the \\texttt{url} attribute.\nIf a value is provided for the \\texttt{var} attribute, the content from\nthe screen scrape is scoped to that variable. Otherwise, the scraped\ncontent is displayed where the taglib is used.\n\nA basic configuration for the\n\\texttt{\\textless{}liferay-util:get-url\\textgreater{}} tag is shown\nbelow:\n\n\\begin{verbatim}\n<liferay-util:get-url url=\"https://www.liferay.com/\" />\n\\end{verbatim}\n\nHere is an example that uses the \\texttt{var} attribute:\n\n\\begin{verbatim}\n<liferay-util:get-url url=\"https://www.liferay.com/\" var=\"Liferay\" />\n\n<div>\n                <h2>We borrowed <a href=\"https://www.liferay.com/\">Liferay</a>. Here it is.</h2>\n\n                <div class=\"Liferay\">\n                                <%= Liferay %>\n                </div>\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-util-get-url-ldn.png}\n\\caption{You can use the Liferay Util Get URL tag to scrape URLs.}\n\\end{figure}\n\nNow you know how to use the\n\\texttt{\\textless{}liferay-util:get-url\\textgreater{}} tag to scrape\nURLs.\n\n\\section{Related Topics}\\label{related-topics-14}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-param}{Using\n  the Liferay Util Param Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-include}{Using\n  the Liferay Util Include Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-aui-taglibs}{Using\n  the AUI Taglib}\n\\end{itemize}\n\n\\chapter{Using Liferay Util HTML\nBottom}\\label{using-liferay-util-html-bottom}\n\nThe HTML bottom tag is not a self-closing tag. Content placed between\nthe opening and closing of this tag is moved to the bottom of the\n\\texttt{\\textless{}html\\textgreater{}} tag. When something is passed\nusing this taglib, the\n\\href{https://github.com/liferay/liferay-portal/blob/master/portal-web/docroot/html/common/themes/bottom.jsp\\#L53-L59}{bottom.jsp}\nis passed markup and outputs in this JSP.\n\nThis tag also has an optional \\texttt{outputKey} attribute. If several\nportlets on the page include the same resource with this tag, you can\nspecify the same \\texttt{outputKey} value for each tag so the resource\nis only loaded once.\n\nThe example configuration below uses the\n\\texttt{\\textless{}liferay-util:html-bottom\\textgreater{}} tag to\ninclude JavaScript (a common use case) provided by the portlet's bundle:\n\n\\begin{verbatim}\n<liferay-util:html-bottom outputKey=\"htmlbottom\">\n\n    <script src=\"/o/my-liferay-util-portlet/js/my_custom_javascript.js\" \n    type=\"text/javascript\"></script>\n\n</liferay-util:html-bottom>\n\\end{verbatim}\n\nNow you know how to use the\n\\texttt{\\textless{}liferay-util:html-bottom\\textgreater{}} tag to\ninclude additional resources in the bottom of the page's HTML tag.\n\n\\section{Related Topics}\\label{related-topics-15}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-body-bottom}{Using\n  the Liferay Util HTML Body Bottom Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-html-top}{Using\n  the Liferay Util HTML Top Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-liferay-ui-taglib}{Using\n  the Liferay UI Taglib}\n\\end{itemize}\n\n\\chapter{Using Liferay Util HTML Top}\\label{using-liferay-util-html-top}\n\nThe HTML top tag is not a self-closing tag. The content placed between\nthe opening and closing of this tag is moved to the\n\\texttt{\\textless{}head\\textgreater{}} tag. When something is passed\nusing this taglib, the\n\\href{https://github.com/liferay/liferay-portal/blob/master/portal-web/docroot/html/common/themes/top_head.jsp\\#L147-L153}{top\\_head.jsp}\nis passed markup and outputs in this JSP.\n\nThis tag also has an optional \\texttt{outputKey} attribute. If several\nportlets on the page include the same resource with this tag, you can\nspecify the same \\texttt{outputKey} value for each tag so the resource\nis only loaded once.\n\nThe example configuration below uses the\n\\texttt{\\textless{}liferay-util:html-top\\textgreater{}} tag to include\nadditional CSS styles provided by the portlet's bundle:\n\n\\begin{verbatim}\n<liferay-util:html-top outputKey=\"htmltop\">\n                <link data-senna-track=\"permanent\" \n        href=\"/o/my-liferay-util-portlet/css/my-custom-styles.css\" \n        rel=\"stylesheet\" type=\"text/css\" />\n</liferay-util:html-top>\n\\end{verbatim}\n\nNow you know how to use the\n\\texttt{\\textless{}liferay-util:html-top\\textgreater{}} tag to include\nadditional resources in the top of the page's HTML tag.\n\n\\section{Related Topics}\\label{related-topics-16}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-html-bottom}{Using\n  the Liferay Util HTML Bottom Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-body-top}{Using\n  the Liferay Util Body Top Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-clay-taglib-in-your-portlets}{Using\n  the Clay Taglib}\n\\end{itemize}\n\n\\chapter{Using Liferay Util Include}\\label{using-liferay-util-include}\n\nThe include tag lets you include other JSP files in your portlet's JSP,\ntheme, or web content. This can increase readability as well as provide\nseparation of concerns for JSP files.\n\nThe \\texttt{page} attribute is required and specifies the path to the\nJSP or JSPF to include. The \\texttt{servletContext} refers to the\nrequest context that the included JSP should use. Passing\n\\texttt{\\textless{}\\%=\\ application\\ \\%\\textgreater{}} to this attribute\nlets the included JSP use the same \\texttt{request} object as other\nobjects that might be set in the prior JSP.\n\nBelow is an example configuration for the\n\\texttt{\\textless{}liferay-util:include\\textgreater{}} tag:\n\n\\begin{verbatim}\n<liferay-util:include \n  page=\"/relative/path/to/file.jsp\" \n  servletContext=\"<%= application %>\"\n/>\n\\end{verbatim}\n\nNow you know how to use the\n\\texttt{\\textless{}liferay-util:include\\textgreater{}} tag to include\nother JSPs in your portlets, themes, and web content.\n\n\\section{Related Topics}\\label{related-topics-17}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-param}{Using\n  the Liferay Util Param Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-dynamic-include}{Using\n  the Liferay Util Dynamic Include Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-front-end-taglibs-in-your-portlet}{Using\n  the Liferay Front-End Taglibs}\n\\end{itemize}\n\n\\chapter{Using Liferay Util Param}\\label{using-liferay-util-param}\n\nThe param tag lets you set a parameter for an\n\\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-include}{included\nJSP page}. This configuration requires two JSPs. JSP A, the main view of\nthe app, includes JSP B and sets its parameter value. This lets you\ndynamically set content when you include the JSP.\n\nFor example, say you have your main functionality in\n\\texttt{my-app.jsp}, and you have additional functionality provided by\n\\texttt{more-content.jsp}. You could have the example configuration\nshown below:\n\n\\texttt{more-content.jsp}:\n\n\\begin{verbatim}\n<%@ page import=\"com.liferay.portal.kernel.util.ParamUtil\" %>\n\n<%\nString answer = ParamUtil.getString(request, \"answer\");\n%>\n\n<div>\n  <p>The answer to life, the universe and everything is <%= answer %>.</p>\n</div>\n\\end{verbatim}\n\nThen in \\texttt{my-app.jsp}, you can include \\texttt{more-content.jsp}\nand set the value of the \\texttt{answer} parameter:\n\n\\begin{verbatim}\n<liferay-util:include page=\"/path/to/more-content.jsp\" servletContext=\"<%= application %>\">\n  <liferay-util:param name=\"answer\" value=\"42\" />\n</liferay-util:include>\n\\end{verbatim}\n\nThis results in the following output in \\texttt{my-app.jsp}:\n\n\\begin{verbatim}\nThe answer to life, the universe and everything is 42.\n\\end{verbatim}\n\nNow you know how to use the\n\\texttt{\\textless{}liferay-util:param\\textgreater{}} tag to set\nparameters for included JSPs. You can use this approach to include\ncommon reusable pieces of code in your apps.\n\n\\section{Related Topics}\\label{related-topics-18}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-include}{Using\n  the Liferay Util Include Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-body-top}{Using\n  the Liferay Util Body Top Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-chart-taglib-in-your-portlets}{Using\n  the Chart Taglib}\n\\end{itemize}\n\n\\chapter{Using Liferay Util Whitespace\nRemover}\\label{using-liferay-util-whitespace-remover}\n\nThe whitespace remover tag removes line breaks and tabs from code blocks\nincluded between the opening and closing of the tag. Below is an example\nconfiguration for the\n\\texttt{\\textless{}liferay-util:whitespace-remover\\textgreater{}} tag:\n\nwith remover:\n\n\\begin{verbatim}\n<liferay-util:whitespace-remover>\n    <p>Here is some text with        tabs.</p>\n</liferay-util:whitespace-remover>\n\\end{verbatim}\n\nresult:\n\n\\begin{verbatim}\nHere is some text withtabs.\n\\end{verbatim}\n\nNow you know how to use the\n\\texttt{\\textless{}liferay-util:whitespace-remover\\textgreater{}} tag to\nensure that your code formatting is consistent.\n\n\\section{Related Topics}\\label{related-topics-19}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-param}{Using\n  the Liferay Util Param Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-buffer}{Using\n  the Liferay Util Buffer Tag}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-aui-taglibs}{Using\n  the AUI Taglib}\n\\end{itemize}\n\n\\chapter{Using the Clay Taglib in Your\nportlets}\\label{using-the-clay-taglib-in-your-portlets}\n\nThe Liferay Clay tag library provides a set of tags for creating\n\\href{https://clayui.com/}{Clay} UI components in your app.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} AUI taglibs are deprecated as of 7.0. We recommend that\nyou use Clay taglibs to avoid future compatibility issues.\n\n\\noindent\\hrulefill\n\nTo use the Clay taglib in your apps, add the following declaration to\nyour JSP:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"clay\" uri=\"http://liferay.com/tld/clay\" %>\n\\end{verbatim}\n\nThe Liferay Clay taglib is also available via a macro for your\nFreeMarker theme templates and web content templates. Follow this\nsyntax:\n\n\\begin{verbatim}\n<@clay[\"tag-name\"] attribute=\"string value\" attribute=10 />\n\\end{verbatim}\n\nClay taglibs provide the following UI components for your apps:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-alerts}{Alerts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-badges}{Badges}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-buttons}{Buttons}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-cards}{Cards}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-dropdown-menus-and-action-menus}{Dropdown\n  Menus and Action Menus}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-form-elements}{Form\n  Elements}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Icons}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links}{Labels\n  and links}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar}{Management\n  Toolbar}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-navigation-bars}{Navigation\n  Bars}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-progress-bars}{Progress\n  Bars}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-stickers}{Stickers}\n\\end{itemize}\n\nThis section covers how to create these components with the Clay\ntaglibs. Each article contains a set of clay component examples along\nwith a screenshot of the resulting UI.\n\n\\chapter{Clay Alerts}\\label{clay-alerts}\n\nClay alerts come in two types: embedded and stripe. Both types, along\nwith several examples of each, are shown below.\n\n\\section{Embedded Alerts}\\label{embedded-alerts}\n\nEmbedded alerts are usually used inside forms. The element that contains\nit determines an embedded alert's width. The close action is not\nrequired for embedded alerts. The following embedded alerts can be\ncreated with Clay taglibs:\n\nDanger alert (embedded):\n\n\\begin{verbatim}\n<clay:alert\n    message=\"This is an error message.\"\n    style=\"danger\"\n    title=\"Error\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-alert-danger.png}\n\\caption{The danger alert notifies the user of an error or issue.}\n\\end{figure}\n\nSuccess alert (embedded):\n\n\\begin{verbatim}\n<clay:alert\n    message=\"This is a success message.\"\n    style=\"success\"\n    title=\"Success\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-alert-success.png}\n\\caption{The success alert notifies the user when an action is\nsuccessful.}\n\\end{figure}\n\nInfo alert (embedded):\n\n\\begin{verbatim}\n<clay:alert\n    message=\"This is an info message.\"\n    title=\"Info\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-alert-info.png}\n\\caption{The info alert displays general information to the user.}\n\\end{figure}\n\nWarning alert (embedded):\n\n\\begin{verbatim}\n<clay:alert\n    message=\"This is a warning message.\"\n    style=\"warning\"\n    title=\"Warning\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-alert-warning.png}\n\\caption{The warning alert displays a warning message to the user.}\n\\end{figure}\n\n\\section{Stripe Alerts}\\label{stripe-alerts}\n\nStripe alerts are placed below the last navigation element (either the\nheader or the navigation bar), and they usually appear on \\emph{Save}\naction, communicating the status of the action once received from the\nserver. Unlike embedded alerts, stripe alerts require the close action.\nA stripe alert is always the full width of the container and pushes all\nthe content below it. The following stripe alerts can be created with\nClay taglibs:\n\nDanger alert (stripe):\n\n\\begin{verbatim}\n<clay:stripe\n    message=\"This is an error message.\"\n    style=\"danger\"\n    title=\"Error\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-alert-danger-stripe.png}\n\\caption{The danger striped alert notifies the user that an action has\nfailed.}\n\\end{figure}\n\nSuccess alert (stripe):\n\n\\begin{verbatim}\n<clay:stripe\n    message=\"This is a success message.\"\n    style=\"success\"\n    title=\"Success\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-alert-success-stripe.png}\n\\caption{The success striped alert notifies the user that an action has\ncompleted successfully.}\n\\end{figure}\n\nInfo alert (stripe):\n\n\\begin{verbatim}\n<clay:stripe\n    message=\"This is an info message.\"\n    title=\"Info\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-alert-info-stripe.png}\n\\caption{The info striped alert displays general information about an\naction to the user.}\n\\end{figure}\n\nWarning alert (stripe):\n\n\\begin{verbatim}\n<clay:stripe\n    message=\"This is a warning message.\"\n    style=\"warning\"\n    title=\"Warning\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-alert-warning-stripe.png}\n\\caption{The warning striped alert warns the user about an action.}\n\\end{figure}\n\nNow you know how to alert users!\n\n\\section{Related Topics}\\label{related-topics-20}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-buttons}{Clay\n  Buttons}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-form-elements}{Clay\n  Form Elements}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links}{Clay\n  Labels and Links}\n\\end{itemize}\n\n\\chapter{Clay Badges}\\label{clay-badges}\n\nBadges help highlight important information such as notifications or new\nand unread messages. Badges have circular borders and are only used to\nspecify a number. This covers the different types of Clay badges you can\nadd to your app.\n\n\\section{Badge Types}\\label{badge-types}\n\nThe following badge styles are available:\n\nPrimary badge:\n\n\\begin{verbatim}\n<div class=\"col-md-1\">\n    <clay:badge label=\"8\" />\n\n    <div>Primary</div>\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-badge-primary.png}\n\\caption{A primary badge is bright blue, commanding attention like the\nprimary button of a form.}\n\\end{figure}\n\nSecondary badge:\n\n\\begin{verbatim}\n<div class=\"col-md-1\">\n    <clay:badge label=\"87\" style=\"secondary\" />\n\n    <div>Secondary</div>\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-badge-secondary.png}\n\\caption{A secondary badge is light-grey and draws less focus than a\nprimary button.}\n\\end{figure}\n\nInfo badge:\n\n\\begin{verbatim}\n<div class=\"col-md-1\">\n    <clay:badge label=\"91\" style=\"info\" />\n\n    <div>Info</div>\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-badge-info.png}\n\\caption{A info badge is dark blue and meant for numbers related to\ngeneral information.}\n\\end{figure}\n\nError badge:\n\n\\begin{verbatim}\n<div class=\"col-md-1\">\n    <clay:badge label=\"130\" style=\"danger\" />\n\n    <div>Error</div>\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-badge-error.png}\n\\caption{An error badge displays numbers related to an error.}\n\\end{figure}\n\nSuccess badge:\n\n\\begin{verbatim}\n<div class=\"col-md-1\">\n    <clay:badge label=\"1111\" style=\"success\" />\n\n    <div>Success</div>\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-badge-success.png}\n\\caption{A success badge displays numbers related to a successful\naction.}\n\\end{figure}\n\nWarning badge:\n\n\\begin{verbatim}\n<div class=\"col-md-1\">\n    <clay:badge label=\"21\" style=\"warning\" />\n\n    <div>Warning</div>\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-badge-warning.png}\n\\caption{A warning badge displays numbers related to warnings that\nshould be addressed.}\n\\end{figure}\n\nNow you know how to use badges to keep track of values in your app.\n\n\\section{Related Topics}\\label{related-topics-21}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links}{Clay\n  Labels and Links}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-cards}{Clay Cards}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-stickers}{Clay\n  Stickers}\n\\end{itemize}\n\n\\chapter{Clay Buttons}\\label{clay-buttons}\n\nButtons come in several types and variations. This tutorial covers the\ndifferent styles and variations of buttons you can create with the Clay\ntaglibs.\n\n\\section{Types}\\label{types}\n\n\\textbf{Primary button:} Used for the most important actions. Two\nprimary buttons should not be together or near each other.\n\nPrimary button with label:\n\n\\begin{verbatim}\n<clay:button label=\"Primary\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-button-primary.png}\n\\caption{A primary button is bright blue, grabbing the user's\nattention.}\n\\end{figure}\n\n\\textbf{Secondary button:} Used for secondary actions. There can be\nmultiple secondary buttons together or near each other.\n\n\\begin{verbatim}\n<div class=\"col\">\n    <clay:button label=\"Secondary\" style=\"secondary\" />\n</div>\n<div class=\"col\">\n    <clay:button ariaLabel=\"Wiki\" icon=\"wiki\" style=\"secondary\" />\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-button-secondary.png}\n\\caption{A secondary button draws less attention than a primary button\nand is meant for secondary actions.}\n\\end{figure}\n\n\\textbf{Borderless button:} Used in cases such as toolbars where the\nsecondary button would be too heavy for the design. This keeps the\ndesign clean.\n\n\\begin{verbatim}\n<div class=\"col\">\n    <clay:button label=\"Borderless\" style=\"borderless\" />\n</div>\n<div class=\"col\">\n    <clay:button ariaLabel=\"Page Template\" icon=\"page-template\" style=\"borderless\" />\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-button-borderless.png}\n\\caption{Borderless buttons remove the dark outline from the button.}\n\\end{figure}\n\n\\textbf{Link button:} Used for Cancel actions.\n\n\\begin{verbatim}\n<div class=\"col\">\n    <clay:button label=\"Link\" style=\"link\" />\n</div>\n<div class=\"col\">\n    <clay:button ariaLabel=\"Add Role\" icon=\"add-role\" style=\"link\" />\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-button-link.png}\n\\caption{You can also turn buttons into links.}\n\\end{figure}\n\nYou can use labels or icons for your buttons. Below is an example of a\nPrimary button with an icon:\n\n\\begin{verbatim}\n<clay:button ariaLabel=\"Workflow\" icon=\"workflow\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-button-primary-icon.png}\n\\caption{Buttons can also display icons.}\n\\end{figure}\n\nYou can disable a button by adding the \\texttt{disabled} attribute:\n\n\\begin{verbatim}\n<div class=\"col\">\n    <clay:button disabled=\"<%= true %>\" label=\"Primary\" />\n</div>\n<div class=\"col\">\n    <clay:button ariaLabel=\"Workflow\" disabled=\"<%= true %>\" icon=\"workflow\" />\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-button-primary-disabled.png}\n\\caption{Buttons can be disabled if you don't want the user to interact\nwith them.}\n\\end{figure}\n\n\\section{Variations}\\label{variations}\n\nButton with icon and text:\n\n\\begin{verbatim}\n<clay:button icon=\"share\" label=\"Share\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-button-icon-text.png}\n\\caption{Buttons can display both icons and text.}\n\\end{figure}\n\nButton with monospaced text:\n\n\\begin{verbatim}\n<clay:button icon=\"indent-less\" monospaced=\"<%= true %>\" style=\"secondary\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-button-monospaced.png}\n\\caption{Buttons can display monospaced text.}\n\\end{figure}\n\nBlock level button:\n\n\\begin{verbatim}\n<clay:button block=\"<%= true %>\" label=\"Button\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-button-block-level.png}\n\\caption{Block level buttons span the entire width of the container.}\n\\end{figure}\n\nPlus button:\n\n\\begin{verbatim}\n<clay:button icon=\"plus\" monospaced=\"<%= true %>\" style=\"secondary\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-button-plus.png}\n\\caption{: A plus button is used for add actions in an app.}\n\\end{figure}\n\nAction button:\n\n\\begin{verbatim}\n<clay:button icon=\"ellipsis-v\" monospaced=\"<%= true %>\" style=\"borderless\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-button-action.png}\n\\caption{: An action button is used to display actions menus.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-22}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-alerts}{Clay Alerts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-buttons}{Clay\n  Buttons}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links}{Clay\n  Labels and Links}\n\\end{itemize}\n\n\\chapter{Clay Cards}\\label{clay-cards}\n\nCards visually represent data. Use them for images, document libraries,\nuser profiles and more. There are four main types of Cards:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Image Cards\n\\item\n  File Cards\n\\item\n  User Cards\n\\item\n  Horizontal Cards\n\\end{itemize}\n\nEach of these types is covered below.\n\n\\section{Image Cards}\\label{image-cards}\n\nImage Cards are used for image/document galleries.\n\nImage Card:\n\n\\begin{verbatim}\n<clay:image-card\n    actionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n    href=\"#1\"\n    imageAlt=\"thumbnail\"\n    imageSrc=\"https://images.unsplash.com/photo-1506976773555-b3da30a63b57\"\n    subtitle=\"Author Action\"\n    title=\"Madrid\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-image-card.png}\n\\caption{Image Cards display images and documents.}\n\\end{figure}\n\nImage Card with icon:\n\n\\begin{verbatim}\n<clay:image-card\n    actionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n    icon=\"camera\"\n    subtitle=\"Author Action\"\n    title=\"<%= SVG_FILE_TITLE %>\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-image-card-icon.png}\n\\caption{Image Cards can also display icons instead of images.}\n\\end{figure}\n\nImage Card empty:\n\n\\begin{verbatim}\n<clay:image-card \n  actionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n    subtitle=\"Author Action\"\n    title=\"<%= SVG_FILE_TITLE %>\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-image-card-empty.png}\n\\caption{Cards can also display nothing.}\n\\end{figure}\n\nCards can also contain file types. Specify the file type with the\n\\texttt{filetype} attribute:\n\n\\begin{verbatim}\n<clay:image-card\n    actionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n    fileType=\"JPG\"\n    fileTypeStyle=\"danger\"\n    href=\"#1\"\n    imageAlt=\"thumbnail\"\n    imageSrc=\"https://images.unsplash.com/photo-1499310226026-b9d598980b90\"\n    subtitle=\"Author Action\"\n    title=\"California\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-image-card-file-type.png}\n\\caption{Cards can also contain file types.}\n\\end{figure}\n\nInclude the \\texttt{labels} attribute to add a label to a Card:\n\n\\begin{verbatim}\n<clay:image-card\n    actionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n    fileType=\"JPG\"\n    fileTypeStyle=\"danger\"\n    href=\"#1\"\n    imageAlt=\"thumbnail\"\n    imageSrc=\"https://images.unsplash.com/photo-1503703294279-c83bdf7b4bf4\"\n    labels=\"<%= cardsDisplayContext.getLabels() %>\"\n    subtitle=\"Author Action\"\n    title=\"Beetle\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-image-card-icon-label.png}\n\\caption{You can include labels in Cards.}\n\\end{figure}\n\nInclude the \\texttt{selectable} attribute to make cards selectable\n(include a checkbox):\n\n\\begin{verbatim}\n<clay:image-card\n    actionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n    fileType=\"JPG\"\n    fileTypeStyle=\"danger\"\n    href=\"#1\"\n    imageAlt=\"thumbnail\"\n    imageSrc=\"https://images.unsplash.com/photo-1506020647804-b04ee956dc04\"\n    labels=\"<%= cardsDisplayContext.getLabels() %>\"\n    selectable=\"<%= true %>\"\n    selected=\"<%= true %>\"\n    subtitle=\"Author Action\"\n    title=\"Beetle\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-image-card-icon-selectable.png}\n\\caption{Cards can be selectable.}\n\\end{figure}\n\n\\section{File Cards}\\label{file-cards}\n\nFile Cards display an icon of the file's type. They represent file types\nother than image files (i.e.~PDF, MP3, DOC, etc.).\n\n\\begin{verbatim}\n<clay:file-card\n    actionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n    fileType=\"MP3\"\n    fileTypeStyle=\"warning\"\n    labels=\"<%= cardsDisplayContext.getLabels() %>\"\n    labelStylesMap=\"<%= cardsDisplayContext.getLabelStylesMap() %>\"\n    selectable=\"<%= true %>\"\n    selected=\"<%= true %>\"\n    subtitle=\"Jimi Hendrix\"\n    title=\"<%= MP3_FILE_TITLE %>\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-file-card.png}\n\\caption{File Cards display file type icons.}\n\\end{figure}\n\nYou can optionally use the \\texttt{labelStylesMap} attribute to pass a\n\\texttt{HashMap} of multiple labels, as shown above.\n\nThe example below specifies a list \\texttt{icon} instead of the default\nfile icon:\n\n\\begin{verbatim}\n<clay:file-card\n    actionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n    fileType=\"DOC\"\n    fileTypeStyle=\"info\"\n    icon=\"list\"\n    labels=\"<%= cardsDisplayContext.getLabels() %>\"\n    selectable=\"<%= true %>\"\n    selected=\"<%= true %>\"\n    subtitle=\"Paco de Lucia\"\n    title=\"<%= DOC_FILE_TITLE %>\"\n/>\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The full list of available Liferay icons can be found on\nthe \\href{https://clayui.com/docs/components/icon.html}{Clay CSS\nwebsite}.\n\n\\noindent\\hrulefill\n\n\\section{User Cards}\\label{user-cards}\n\nUser Cards display user profile images or the initials of the user's\nname or name+surname.\n\nUser Card with initials:\n\n\\begin{verbatim}\n<clay:user-card\n    actionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n    initials=\"HS\"\n    name=\"User Name\"\n    subtitle=\"Latest Action\"\n    userColor=\"danger\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-user-card-initial.png}\n\\caption{User Cards can display a user's initials.}\n\\end{figure}\n\nUser Card with profile image:\n\n\\begin{verbatim}\n<clay:user-card\n    actionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n    disabled=\"<%= true %>\"\n    imageAlt=\"thumbnail\"\n    imageSrc=\"https://images.unsplash.com/photo-1502290822284-9538ef1f1291\"\n    name=\"User name\"\n    selectable=\"<%= true %>\"\n    selected=\"<%= true %>\"\n    subtitle=\"Latest Action\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-user-card-profile-image.png}\n\\caption{A User Card can also display a profile image.}\n\\end{figure}\n\n\\section{Horizontal Cards}\\label{horizontal-cards}\n\nHorizontal Cards represent folders and can have the same amount of\ninformation as other Cards. The key difference is that horizontal Cards\nlet you remove the image portion of the Card, since only the folder icon\nis required.\n\n\\begin{verbatim}\n<clay:horizontal-card\n    actionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n    selectable=\"<%= true %>\"\n    selected=\"<%= true %>\"\n    title=\"ReallySuperInsanelyJustIncrediblyLongAndTotallyNotPossibleWordButWeAreReallyTryingToCoverAllOurBasesHereJustInCaseSomeoneIsNutsAsPerUsual\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-horizontal-card.png}\n\\caption{: Horizontal Cards are good for displaying folders.}\n\\end{figure}\n\nNow you know how to use Cards in your UI to display information in your\napps.\n\n\\section{Related Topics}\\label{related-topics-23}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-badges}{Clay Badges}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links}{Clay\n  Labels and Links}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-stickers}{Clay\n  Stickers}\n\\end{itemize}\n\n\\chapter{Clay Dropdown Menus and Action\nMenus}\\label{clay-dropdown-menus-and-action-menus}\n\nYou can add dropdown menus to your app via the\n\\texttt{clay:dropdown-menu} and \\texttt{clay:actions-menu} taglibs. The\nClay taglibs provide several menu variations for you to choose. Both\ntaglibs with several examples are shown below.\n\n\\section{Dropdown Menus}\\label{dropdown-menus}\n\nBasic dropdown menu:\n\n\\begin{verbatim}\n<clay:dropdown-menu\n    items=\"<%= dropdownsDisplayContext.getDefaultDropdownItems() %>\"\n    label=\"Default\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-dropdown-basic.png}\n\\caption{Clay taglibs provide everything you need to add dropdown menus\nto your app.}\n\\end{figure}\n\nThe dropdown menu's items are defined in its Java\nclass--\\texttt{dropdownDisplayContext} in this case. Menu items are\n\\texttt{NavigationItem} objects. You can disable menu items with the\n\\texttt{setDisabled(true)} method and make a menu item active with the\n\\texttt{setActive(true)} method. The \\texttt{href} attribute is set with\nthe \\texttt{setHref()} method, and labels are defined with the\n\\texttt{setLabel()} method. Here's an example implementation of the\n\\texttt{dropdownDisplayContext} class:\n\n\\begin{verbatim}\nif (_defaultDropdownItems != null) {\n  return _defaultDropdownItems;\n}\n\n_defaultDropdownItems = new ArrayList<>();\n\nfor (int i = 0; i < 4; i++) {\n  NavigationItem navigationItem = new NavigationItem();\n\n  if (i == 1) {\n    navigationItem.setDisabled(true);\n  }\n  else if (i == 2) {\n    navigationItem.setActive(true);\n  }\n\n  navigationItem.setHref(\"#\" + i);\n  navigationItem.setLabel(\"Option \" + i);\n\n  _defaultDropdownItems.add(navigationItem);\n}\n\nreturn _defaultDropdownItems;\n}\n\\end{verbatim}\n\nYou can organize menu items into groups by setting the\n\\texttt{NavigationItem}'s type to \\texttt{TYPE\\_GROUP} and nesting the\nitems in separate \\texttt{ArrayList}s. You can add a horizontal\nseparator to separate the groups visually with the\n\\texttt{setSeparator(true)} method. Below is a code snippet from the\n\\texttt{dropdownsDisplayContext} class:\n\n\\begin{verbatim}\ngroup1NavigationItem.setSeparator(true);\ngroup1NavigationItem.setType(NavigationItem.TYPE_GROUP);\n\\end{verbatim}\n\nCorresponding taglib:\n\n\\begin{verbatim}\n<clay:dropdown-menu\n    items=\"<%= dropdownsDisplayContext.getGroupDropdownItems() %>\"\n    label=\"Dividers\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-dropdown-group.png}\n\\caption{You can organize dropdown menu items into groups.}\n\\end{figure}\n\nYou can also add inputs to dropdown menus. To add an input to a dropdown\nmenu, set the input's type with the \\texttt{setType()} method\n(e.g.~\\texttt{NavigationItem.TYPE\\_CHECKBOX}), its name with the\n\\texttt{setInputName()} method, and its value with the\n\\texttt{setInputValue()} method. Here's an example implementation:\n\n\\begin{verbatim}\nnavigationItem.setInputName(\"checkbox\" + i);\nnavigationItem.setInputValue(\"checkboxValue\" + i);\nnavigationItem.setLabel(\"Group 1 - Option \" + i);\nnavigationItem.setType(NavigationItem.TYPE_CHECKBOX);\n\\end{verbatim}\n\nCorresponding taglib:\n\n\\begin{verbatim}\n<clay:dropdown-menu\n    buttonLabel=\"Done\"\n    items=\"<%= dropdownsDisplayContext.getInputDropdownItems() %>\"\n    label=\"Inputs\"\n    searchable=\"<%= true %>\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-dropdown-input.png}\n\\caption{Inputs can be included in dropdown menus.}\n\\end{figure}\n\nMenu items can also contain icons. To add an icon to a menu item, use\nthe \\texttt{setIcon()} method. Below is an example:\n\n\\begin{verbatim}\nnavigationItem.setIcon(\"check-circle-full\")\n\\end{verbatim}\n\nCorresponding taglib:\n\n\\begin{verbatim}\n<clay:dropdown-menu\n    items=\"<%= dropdownsDisplayContext.getIconDropdownItems() %>\"\n    itemsIconAlignment=\"left\"\n    label=\"Icons\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-dropdown-icons.png}\n\\caption{Icons can be included in dropdown menus.}\n\\end{figure}\n\n\\section{Actions Menus}\\label{actions-menus}\n\nBasic actions menu:\n\n\\begin{verbatim}\n<clay:dropdown-actions\n    items=\"<%= dropdownsDisplayContext.getDefaultDropdownItems() %>\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-dropdown-actions.png}\n\\caption{You can also create Actions menus with Clay taglibs.}\n\\end{figure}\n\nAn actions menu can also display help text to the user:\n\n\\begin{verbatim}\n<clay:dropdown-actions\n    buttonLabel=\"More\"\n    buttonStyle=\"secondary\"\n    caption=\"Showing 4 of 32 Options\"\n    helpText=\"You can customize this menu or see all you have by pressing \\\"more\\\".\"\n    items=\"<%= dropdownsDisplayContext.getDefaultDropdownItems() %>\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-dropdown-actions-help.png}\n\\caption{You can provide help text in Actions menus.}\n\\end{figure}\n\nClay taglibs make it easy to add dropdown menus and action menus to your\napps.\n\n\\section{Related Topics}\\label{related-topics-24}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-form-elements}{Clay\n  Form Elements}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-navigation-bars}{Clay\n  Navigation Bars}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-progress-bars}{Clay\n  Progress Bars}\n\\end{itemize}\n\n\\chapter{Clay Form Elements}\\label{clay-form-elements}\n\nThe Liferay Clay tag library provides several tags for creating form\nelements. An example of each tag is shown below.\n\n\\section{Checkbox}\\label{checkbox}\n\nCheckboxes give the user a true or false input.\n\n\\begin{verbatim}\n<clay:checkbox \n        checked=\"<%= true %>\" \n        hideLabel=\"<%= true %>\" \n        label=\"My Input\" \n        name=\"name\" \n/>\n\\end{verbatim}\n\nAttributes:\n\n\\textbf{checked:} Whether the checkbox is checked\n\n\\textbf{disabled:} Whether the checkbox is enabled\n\n\\textbf{hideLabel:} Whether to display the checkbox label\n\n\\textbf{indeterminate:} Checkbox variable for multiple selection\n\n\\textbf{label:} The checkbox's label\n\n\\textbf{name:} The checkbox's name\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-form-checkbox.png}\n\\caption{Clay taglibs provide checkboxes.}\n\\end{figure}\n\n\\section{Radio}\\label{radio}\n\nA radio button lets the user select one choice from a set of options in\na form.\n\n\\begin{verbatim}\n<clay:radio \n        checked=\"<%= true %>\" \n        hideLabel=\"<%= true %>\" \n        label=\"My Input\" \n        name=\"name\" \n/>\n\\end{verbatim}\n\nAttributes:\n\n\\textbf{checked:} Whether the radio button is checked\n\n\\textbf{hideLabel:} Whether to display the radio button label\n\n\\textbf{disabled:} Whether the radio button is enabled\n\n\\textbf{label:} The radio button's label\n\n\\textbf{name:} The radio button's name\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-form-radio-button.png}\n\\caption{Clay taglibs provide radio buttons.}\n\\end{figure}\n\n\\section{Selector}\\label{selector}\n\nA selector gives the user a select box with a set of options to choose\nfrom.\n\nThe Java scriplet below creates eight dummy options for the selector:\n\n\\begin{verbatim}\n<%\nList<Map<String, Object>> options = new ArrayList<>();\n\nfor (int i = 0; i < 8; i++) {\n    Map<String, Object> option = new HashMap<>();\n\n    option.put(\"label\", \"Sample \" + i);\n    option.put(\"value\", i);\n\n    options.add(option);\n}\n%>\n\\end{verbatim}\n\n\\begin{verbatim}\n<clay:select \n        label=\"Regular Select Element\" \n        name=\"name\" \n        options=\"<%= options %>\" \n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-form-selector.png}\n\\caption{Clay taglibs provide select boxes.}\n\\end{figure}\n\nIf you want let users select multiple options at once, set the\n\\texttt{multiple} attribute to \\texttt{true}:\n\n\\begin{verbatim}\n<clay:select \n        label=\"Multiple Select Element\" \n        multiple=\"<%= true %>\" \n        name=\"name\" \n        options=\"<%= options %>\" \n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-form-selector-multiple.png}\n\\caption{You can let users select multiple options from the select\nmenu.}\n\\end{figure}\n\nAttributes:\n\n\\textbf{disabled:} Whether the selector is enabled \\textbf{label:} The\nselector's label \\textbf{multiple:} Whether multiple options can be\nselected \\textbf{name:} The selector's name\n\nNow you know how to use Clay taglibs to add common form elements to your\napp!\n\n\\section{Related Topics}\\label{related-topics-25}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-buttons}{Clay\n  Buttons}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Clay Icons}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links}{Clay\n  Labels and Links}\n\\end{itemize}\n\n\\chapter{Clay Icons}\\label{clay-icons}\n\nThe Liferay Clay taglibs provide several icons that you can use in your\napps. Use the \\texttt{clay:icon} tag and specify the icon with the\n\\texttt{symbol} attribute:\n\n\\begin{verbatim}\n<clay:icon symbol=\"folder\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-icon-folder.png}\n\\caption{You can include icons in your app with the Clay taglib.}\n\\end{figure}\n\nThe full list of icons is shown below:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-icon-library.png}\n\\caption{The Clay taglib gives you access to several Liferay DXP icons.}\n\\end{figure}\n\nThe Liferay Clay taglibs also provide a set of language flag icons that\nyou can use in your app. The full list of language flags is shown below:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-icon-language-flags.png}\n\\caption{You can include language flags in your apps.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-26}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-badges}{Clay Badges}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-stickers}{Clay\n  Stickers}\n\\item\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-clay-icons-in-a-theme}{Using\n  Clay Icons in a Theme}\n\\end{itemize}\n\n\\chapter{Clay Labels and Links}\\label{clay-labels-and-links}\n\nLiferay Clay taglibs provide tags for creating labels and links in your\napp. Both of these elements are covered below.\n\n\\section{Labels}\\label{labels}\n\nThe Liferay Clay taglibs provide a few different labels for your app.\nUse the \\texttt{clay:label} tag to add a label to your app. You can\ncreate color-coded labels, removable labels, and labels that contain\nlinks. The sections below demonstrate all of these options.\n\n\\section{Color-coded Labels}\\label{color-coded-labels}\n\nThe Liferay Clay labels come in four different colors: dark-blue for\ninfo, light-gray for status, orange for pending, red for rejected, and\ngreen for approved.\n\nInfo labels are dark-blue, and since they stand out a bit more than\nstatus labels, they are best for conveying general information. To use\nan info label, set the \\texttt{style} attribute to \\texttt{info}:\n\n\\begin{verbatim}\n<clay:label label=\"Label text\" style=\"info\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-label-info.png}\n\\caption{Info labels convey general information.}\n\\end{figure}\n\nStatus labels are light-gray, and due to their neutral color, they are\nbest for conveying basic information. Status labels are the default\nlabel and therefore require no \\texttt{style} attribute:\n\n\\begin{verbatim}\n<clay:label label=\"Status\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-label-status.png}\n\\caption{Status labels are the least flashy and best for displaying\nbasic information.}\n\\end{figure}\n\nWarning labels are orange, and due to their color, they are best for\nconveying a warning message. To use a warning label, set the\n\\texttt{style} attribute to \\texttt{warning}:\n\n\\begin{verbatim}\n<clay:label label=\"Pending\" style=\"warning\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-label-warning.png}\n\\caption{Warning labels notify the user of issues, but nothing app\nbreaking.}\n\\end{figure}\n\nDanger labels are red and indicate that something is wrong or has\nfailed. To use a danger label, set the \\texttt{style} attribute to\n\\texttt{danger}:\n\n\\begin{verbatim}\n<clay:label label=\"Rejected\" style=\"danger\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-label-danger.png}\n\\caption{Danger labels convey a sense of urgency that must be\naddressed.}\n\\end{figure}\n\nSuccess labels are green and indicate that something has completed\nsuccessfully. To use a success label, set the \\texttt{style} attribute\nto \\texttt{success}:\n\n\\begin{verbatim}\n<clay:label label=\"Approved\" style=\"success\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-label-success.png}\n\\caption{Success labels indicate a successful action.}\n\\end{figure}\n\nLabels can also be bigger. Set the \\texttt{size} attribute to\n\\texttt{lg} to display large labels:\n\n\\begin{verbatim}\n<clay:label label=\"Approved\" size=\"lg\" style=\"success\" />\n\\end{verbatim}\n\n\\section{Removable Labels}\\label{removable-labels}\n\nIf you want to let a user close a label (e.g.~a temporary notification),\nyou can make the label removable by setting the \\texttt{closeable}\nattribute to \\texttt{true}.\n\n\\begin{verbatim}\n<clay:label closeable=\"<%= true %>\" label=\"Normal Label\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-label-removable.png}\n\\caption{Labels can be removable.}\n\\end{figure}\n\n\\section{Labels with Links}\\label{labels-with-links}\n\nYou can make a label a link by adding the \\texttt{href} attribute to it\njust as you would an anchor tag:\n\n\\begin{verbatim}\n<clay:label href=\"#\" label=\"Label Text\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-label-link.png}\n\\caption{Labels can also be links.}\n\\end{figure}\n\n\\section{Links}\\label{links}\n\nYou can add traditional hyperlinks to your app with the\n\\texttt{\\textless{}clay:link\\textgreater{}} tag:\n\n\\begin{verbatim}\n<clay:link href=\"#\" label=\"link text\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-link.png}\n\\caption{Clay taglibs also provide link elements.}\n\\end{figure}\n\nNow you know how to add links and labels to your apps!\n\n\\section{Related Topics}\\label{related-topics-27}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-badges}{Clay Badges}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-cards}{Clay Cards}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-form-elements}{Clay\n  Form Elements}\n\\end{itemize}\n\n\\chapter{Clay Management Toolbar}\\label{clay-management-toolbar}\n\nThe Management Toolbar gives administrators control over search\ncontainer results in their apps. It lets you filter, sort, and choose a\nview type for search results, so you can quickly identify the document,\nweb content, asset entry, or whatever you're looking for. The Management\nToolbar is fully customizable, so you can implement all the controls or\njust the ones your app requires.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar.png}\n\\caption{The Management ToolBar lets the user customize how the app\ndisplays content.}\n\\end{figure}\n\nTo create a management toolbar, use the \\texttt{clay:management-toolbar}\ntaglib. The toolbar contains a few key sections. Each section is grouped\nand configured using different attributes. These attributes are\ndescribed in more detail below.\n\n\\section{Using a Display Context to Configure the Management\nToolbar}\\label{using-a-display-context-to-configure-the-management-toolbar}\n\nIf you're using a Display Context---a separate class to configure your\ndisplay options for your management toolbar---to define all or some of\nthe configuration options for the toolbar, you can specify the Display\nContext with the \\texttt{displayContext} attribute. An example is shown\nbelow:\n\n\\begin{verbatim}\n<clay:management-toolbar \n    displayContext=\"<%= viewUADEntitiesManagementToolbarDisplayContext %>\" \n/>\n\\end{verbatim}\n\nYou can see an example use case of a Display Context in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/filtering-and-sorting-items-with-the-management-toolbar}{Filtering\nand Sorting Items with the Management Toolbar}. A Display Context is not\nrequired for a management toolbar's configuration. You can provide as\nmuch or as little of the configuration options for your management\ntoolbar through the Display Context as you like.\n\n\\section{Checkbox and Actions}\\label{checkbox-and-actions}\n\nThe \\texttt{actionItems}, \\texttt{searchContainerId},\n\\texttt{selectable}, and \\texttt{totalItems} attributes let you include\na checkbox in the toolbar to select all search container results and run\nbulk actions on them. Actions and total items display when an individual\nresult is checked, or when the master checkbox is checked in the\ntoolbar.\n\n\\texttt{actionItems}: The list of dropdown items to display when a\nresult is checked or the master checkbox in the Management Toolbar is\nchecked. You can select multiple results between pages. The Management\nToolbar keeps track of the number of selected results for you.\n\n\\texttt{searchContainerId}: The ID of the search container connected to\nthe Management Toolbar\n\n\\texttt{selectable}: Whether to include a checkbox in the Management\nToolbar\n\n\\texttt{totalItems}: The total number of items across pagination. This\nnumber displays when one or multiple items are selected.\n\nAn example configuration is shown below:\n\n\\begin{verbatim}\nactionItems=\"<%=\n    new JSPDropdownItemList(pageContext) {\n        {\n          add(\n            dropdownItem -> {\n              dropdownItem.setHref(\"#edit\");\n              dropdownItem.setLabel(\"Edit\");\n            });\n  \n          add(\n            dropdownItem -> {\n              dropdownItem.setHref(\"#download\");\n              dropdownItem.setIcon(\"download\");\n              dropdownItem.setLabel(\"Download\");\n              dropdownItem.setQuickAction(true);\n            });\n  \n          add(\n            dropdownItem -> {\n              dropdownItem.setHref(\"#delete\");\n              dropdownItem.setLabel(\"Delete\");\n              dropdownItem.setIcon(\"trash\");\n              dropdownItem.setQuickAction(true);\n            });\n        }\n    }\n%>\"\n\\end{verbatim}\n\nAction items are listed in the Actions menu, along with the number of\nitems selected across pagination.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-actions.png}\n\\caption{Actions are also listed in the Management Toolbar's dropdown\nmenu when an item, multiple items, or the master checkbox is checked.}\n\\end{figure}\n\nIf an action has an icon specified, such as the Delete and Download\nactions in the example above, the icon is displayed next to the action\nmenu as well.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-selectable.png}\n\\caption{The Management Toolbar keeps track of the results selected and\ndisplays the actions to execute on them.}\n\\end{figure}\n\n\\section{Filtering and Sorting Search\nResults}\\label{filtering-and-sorting-search-results}\n\nThe \\texttt{filterItems}, \\texttt{sortingOrder}, and \\texttt{sortingURL}\nattributes let you filter and sort search container results. Filtering\nand sorting are grouped together in one convenient dropdown menu.\n\n\\texttt{filterItems}: Sets the search container's filtering options.\nThis filter should be included in all control panel applications.\nFiltering options can include sort criteria, sort ordering, and more.\n\n\\texttt{filterLabelItems}: Sets the search container's filter labels to\ndisplay. This lets the user know which filters are currently applied.\n\n\\texttt{sortingOrder}: The current sorting order: ascending or\ndescending.\n\n\\texttt{sortingURL}: The URL to change the sorting order\n\nThe example below adds two filter options and two sorting options:\n\n\\begin{verbatim}\nfilterItems=\"<%=\n    new DropdownItemList(_request) {\n        {\n            addGroup(\n                dropdownGroupItem -> {\n                    dropdownGroupItem.setDropdownItemList(\n                        new DropdownItemList(_request) {\n                            {\n                                add(\n                                    dropdownItem -> {\n                                        dropdownItem.setHref(\"#1\");\n                                        dropdownItem.setLabel(\"Filter 1\");\n                                    });\n\n                                add(\n                                    dropdownItem -> {\n                                        dropdownItem.setHref(\"#2\");\n                                        dropdownItem.setLabel(\"Filter 2\");\n                                    });\n                            }\n                        }\n                    );\n                    dropdownGroupItem.setLabel(\"Filter By\");\n                });\n                \n            addGroup(\n                dropdownGroupItem -> {\n                    dropdownGroupItem.setDropdownItemList(\n                        new DropdownItemList(_request) {\n                            {\n                                add(\n                                    dropdownItem -> {\n                                        dropdownItem.setHref(\"#3\");\n                                        dropdownItem.setLabel(\"Order 1\");\n                                    });\n\n                                add(\n                                    dropdownItem -> {\n                                        dropdownItem.setHref(\"#4\");\n                                        dropdownItem.setLabel(\"Order 2\");\n                                    });\n                            }\n                        }\n                    );\n                    dropdownGroupItem.setLabel(\"Order By\");\n                });\n        }\n    }\n%>\"\n\\end{verbatim}\n\n\\begin{verbatim}\nfilterLabelItems=\"<%=\n  new LabelItemList() {\n    {\n      add(\n        labelItem -> {\n          labelItem.setLabel(\"Filter 1\");\n        });\n\n      add(\n        labelItem -> {\n          labelItem.setLabel(\"Filter 2\");\n        });\n    }\n  };\n%>\"\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-filter-and-sort.png}\n\\caption{You can also sort and filter search container results.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-filter-label-items.jpg}\n\\caption{You can also sort and filter search container results.}\n\\end{figure}\n\n\\section{Search Form}\\label{search-form}\n\nThe \\texttt{clearResultsURL}, \\texttt{searchActionURL},\n\\texttt{searchFormName}, \\texttt{searchInputName}, and\n\\texttt{searchValue} attributes let you configure the search form. The\nmain portion of the Management Toolbar is reserved for the search form.\n\n\\texttt{clearResultsURL}: The URL to reset the search\n\n\\texttt{searchActionURL}: The action URL to send the search form\n\n\\texttt{searchFormName}: The search form's name\n\n\\texttt{searchInputName}: The search input's name\n\n\\texttt{searchValue}: The search input's value\n\nAn example configuration is shown below:\n\n\\begin{verbatim}\n<clay:management-toolbar\n    clearResultsURL=\"<%= searchURL %>\"\n    disabled=\"<%= isDisabled %>\"\n    namespace=\"<%= renderResponse.getNamespace() %>\"\n    searchActionURL=\"<%= searchURL %>\"\n    searchFormName=\"fm\"\n    searchInputName=\"<%= DisplayTerms.KEYWORDS %>\"\n    searchValue=\"<%= ParamUtil.getString(request, searchInputName) %>\"\n    selectable=\"<%= false %>\"\n    totalItems=\"<%= totalItems %>\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-search-form.png}\n\\caption{The search form comprises most of the Management Toolbar,\nletting users search through the search container results.}\n\\end{figure}\n\n\\section{Info Panel}\\label{info-panel}\n\nThe \\texttt{infoPanelId} and \\texttt{showInfoButton} attributes let you\nadd a retractable sidebar panel that displays additional information\nrelated to a search container result.\n\n\\texttt{infoPanelId}: The ID of the info panel to toggle\n\n\\texttt{showInfoButton}: Whether to show the info button\n\nIn the example configuration below, the \\texttt{showInfoButton}\nattribute is provided in the Display Context---specified with the\n\\texttt{displayContext} attribute---and the \\texttt{infoPanelId} is\nexplicitly set in the JSP:\n\n\\begin{verbatim}\n<clay:management-toolbar\n    displayContext=\"<%= journalDisplayContext %>\"\n    infoPanelId=\"infoPanelId\"\n    namespace=\"<%= renderResponse.getNamespace() %>\"\n    searchContainerId=\"<=% searchContainerId %>\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-info-panel.png}\n\\caption{The info panel keeps your UI clutter-free.}\n\\end{figure}\n\n\\section{View Types}\\label{view-types}\n\nThe \\texttt{viewTypes} attribute specifies the display options for the\nsearch container results. There are three display options to choose\nfrom:\n\n\\textbf{Cards:} Displays search result columns on a horizontal or\nvertical card.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-view-type-card.png}\n\\caption{The Management Toolbar's icon display view gives a quick\nsummary of the content's description and status.}\n\\end{figure}\n\n\\textbf{List:} Displays a detailed description along with summarized\ndetails for the search result columns.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-view-type-list.png}\n\\caption{The Management Toolbar's List view type gives the content's\nfull description.}\n\\end{figure}\n\n\\textbf{Table:} The default view. Lists the search result columns from\nleft to right.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-view-type-table.png}\n\\caption{: The Management Toolbar's Table view type list the content's\ninformation in individual columns.}\n\\end{figure}\n\nAn example configuration is shown below:\n\n\\begin{verbatim}\nviewTypes=\"<%=\n    new JSPViewTypeItemList(pageContext, baseURL, selectedType) {\n        {\n            addCardViewTypeItem(\n                viewTypeItem -> {\n                    viewTypeItem.setActive(true);\n                    viewTypeItem.setLabel(\"Card\");\n                });\n\n            addListViewTypeItem(\n                viewTypeItem -> {\n                    viewTypeItem.setLabel(\"List\");\n                });\n\n            addTableViewTypeItem(\n                viewTypeItem -> {\n                    viewTypeItem.setLabel(\"Table\");\n                });\n        }\n    }\n%>\"\n\\end{verbatim}\n\nWhile the example above shows how to configure the view types in the\nJSP, you must also\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/implementing-the-view-types}{specify\nwhen to use each view type}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-view-types.png}\n\\caption{: The Management Toolbar offers three view type options.}\n\\end{figure}\n\n\\section{Creation Menu}\\label{creation-menu}\n\nThe \\texttt{creationMenu} attribute creates an add menu button for one\nor multiple items. It's used for creating new entities (e.g.~a new blog\nentry).\n\nUse the \\texttt{addPrimaryDropdownItem()} method to add the top level\nitems to the dropdown menu, or use the\n\\texttt{addFavoriteDropdownItem()} method to add secondary items to the\ndropdown menu.\n\nThe example configuration below adds two primary creation menu items and\ntwo secondary creation menu items:\n\n\\begin{verbatim}\ncreationMenu=\"<%= \n    new JSPCreationMenu(pageContext) {\n            {\n                addPrimaryDropdownItem(\n                    dropdownItem -> {\n                        dropdownItem.setHref(\"#1\");\n                        dropdownItem.setLabel(\"Sample 1\");\n                    });\n  \n                addPrimaryDropdownItem(\n                    dropdownItem -> {\n                        dropdownItem.setHref(\"#2\");\n                        dropdownItem.setLabel(\"Sample 2\");\n                    });\n  \n                addFavoriteDropdownItem(\n                    dropdownItem -> {\n                        dropdownItem.setHref(\"#3\");\n                        dropdownItem.setLabel(\"Favorite 1\");\n                    });\n  \n                addFavoriteDropdownItem(\n                    dropdownItem -> {\n                        dropdownItem.setHref(\"#4\");\n                        dropdownItem.setLabel(\"Other item\");\n                    });\n            }\n    };\n%>\"\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-management-toolbar-creation-menu.png}\n\\caption{: The Management Toolbar lets you optionally add a Creation\nMenu for creating new entities.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-28}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-dropdown-menus-and-action-menus}{Clay\n  Dropdown Menus and Action Menus}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Clay Icons}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-navigation-bars}{Clay\n  Navigation Bars}\n\\end{itemize}\n\n\\chapter{Clay Navigation Bars}\\label{clay-navigation-bars}\n\nSimilar to dropdown menus, navigation bars display a list of navigation\nitems. The key difference is navigation bars are displayed in a\nhorizontal bar with all navigation items visible at all times. The\nnavigation bar also indicates the active navigation item with an\nunderline. Navigation bars come in two styles: white background with\ndark-grey text (default) and dark-grey background with white text\n(inverted).\n\nDefault navigation bar:\n\n\\begin{verbatim}\n<clay:navigation-bar \n    navigationItems=\"<%= navigationBarsDisplayContext.getNavigationItems() %>\" \n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-nav-bars.png}\n\\caption{You can include navigation bars in your apps.}\n\\end{figure}\n\nInverted navigation bar (set \\texttt{inverted} attribute to\n\\texttt{true}):\n\n\\begin{verbatim}\n<clay:navigation-bar \n    inverted=\"<%= true %>\" \n    navigationItems=\"<%= navigationBarsDisplayContext.getNavigationItems() %>\" \n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-nav-bars-inverted.png}\n\\caption{Navigation bars can be inverted if you prefer.}\n\\end{figure}\n\n\\section{Related Topics}\\label{related-topics-29}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-dropdown-menus-and-action-menus}{Clay\n  Dropdown Menus and Action Menus}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-form-elements}{Clay\n  Form Elements}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-progress-bars}{Clay\n  Progress Bars}\n\\end{itemize}\n\n\\chapter{Clay Progress Bars}\\label{clay-progress-bars}\n\nYou can add progress bars to your app with the \\texttt{clay:progressbar}\ntag. These indicate the completion percentage of a task and come in\nthree status styles: \\texttt{default} (blue), \\texttt{warning} (red),\nand \\texttt{complete} (green with checkmark). You can provide a minimum\nvalue (\\texttt{minValue}) and a maximum value (\\texttt{maxValue}).\n\nDefault progress bar:\n\n\\begin{verbatim}\n<clay:progressbar \n    maxValue=\"<%= 100 %>\" \n    minValue=\"<%= 0 %>\" \n    value=\"<%= 30 %>\" \n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-progress-bar.png}\n\\caption{You can include progress bars in your apps.}\n\\end{figure}\n\nWarning progress bar:\n\n\\begin{verbatim}\n<clay:progressbar \n    maxValue=\"<%= 100 %>\" \n    minValue=\"<%= 0 %>\" \n    status=\"warning\" \n    value=\"<%= 70 %>\" \n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-progress-bar-warning.png}\n\\caption{warning progress bars indicate that the progress has not\ncompleted due to an error.}\n\\end{figure}\n\nComplete progress bar:\n\n\\begin{verbatim}\n<clay:progressbar \n    status=\"complete\" \n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-progress-bar-complete.png}\n\\caption{The complete progress bar indicates the progress is complete.}\n\\end{figure}\n\nClay taglibs make it easy to track progress in your apps.\n\n\\section{Related Topics}\\label{related-topics-30}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-dropdown-menus-and-action-menus}{Clay\n  Dropdown Menus and Action Menus}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Clay Icons}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-navigation-bars}{Clay\n  Navigation Bars}\n\\end{itemize}\n\n\\chapter{Clay Stickers}\\label{clay-stickers}\n\nWhereas badges display numbers and labels display short information,\nstickers are small visual indicators of the content (usually the content\ntype). They can include a small label or a Liferay icon, and they come\nin two shapes: circle and square.\n\nSquare sticker with label:\n\n\\begin{verbatim}\n<clay:sticker label=\"JPG\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-sticker-square-label.png}\n\\caption{You can include stickers in your apps.}\n\\end{figure}\n\nSquare sticker with icon:\n\n\\begin{verbatim}\n<clay:sticker icon=\"picture\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-sticker-square-icon.png}\n\\caption{Stickers can include icons.}\n\\end{figure}\n\nCircle sticker:\n\n\\begin{verbatim}\n<clay:sticker label=\"JPG\" shape=\"circle\" />\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-sticker-round.png}\n\\caption{You can also have circle stickers.}\n\\end{figure}\n\nStickers can be positioned in any corner of a div. Indicate their\nposition with the \\texttt{position} attribute: \\texttt{top-left},\n\\texttt{bottom-left}, \\texttt{top-right}, or \\texttt{bottom-right}:\n\n\\begin{verbatim}\n<div class=\"aspect-ratio\">\n\n    <img class=\"aspect-ratio-item-fluid\" src=\"](./images/thumbnail_hot_air_ballon.jpg\" />\n\n    <clay:sticker label=\"PDF\" position=\"top-left\" style=\"danger\" />\n</div>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/clay-taglib-sticker-position.png}\n\\caption{You can specify the position of the sticker within a\ncontainer.}\n\\end{figure}\n\nNow you know how to use Clay stickers in your app!\n\n\\section{Related Topics}\\label{related-topics-31}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-badges}{Clay Badges}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-cards}{Clay Cards}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/clay-icons}{Clay Icons}\n\\end{itemize}\n\n\\chapter{Using the Chart Taglib in Your\nPortlets}\\label{using-the-chart-taglib-in-your-portlets}\n\nLines, splines, bars, pies and more, the Chart tag Library provides\neverything you need to model data. Each taglib gives you access to the\ncorresponding\n\\href{https://github.com/liferay/clay/tree/2.x-stable/packages/clay-charts/src}{Clay\ncomponent}. These components contain the default configuration for the\nUI.\n\nTo use the Chart taglib in your apps, add the following declaration to\nyour JSP:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n\\end{verbatim}\n\nThis section covers the types of charts you can create with the Chart\ntaglibs. Each article contains a set of chart examples along with sample\nJava data and a figure displaying the rendered results.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-sample-portlet.png}\n\\caption{You can create many different types of charts with the chart\ntaglibs.}\n\\end{figure}\n\n\\chapter{Bar Charts}\\label{bar-charts}\n\nBar charts contain multiple sets of data. A bar chart models the data in\nbars. Each data series (created with the \\texttt{addColumns()} method)\nis defined with a new instance of the\n\\href{https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/MultiValueColumn.html}{\\texttt{MultiValueColumn}\nobject}, which takes an ID and a set of values. Follow these steps to\nconfigure your portlet to use bar charts.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the chart taglib along with the \\texttt{BarChartConfig} and\n  \\texttt{MultiValueColumn} classes into your bundle's \\texttt{init.jsp}\n  file:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.point.bar.BarChartConfig\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.MultiValueColumn\" %>\n\\end{verbatim}\n\\item\n  Add the following Java scriptlet to the top of your \\texttt{view.jsp}:\n\n\\begin{verbatim}\n<%\nBarChartConfig _barChartConfig = new BarChartConfig();\n\n_barChartConfig.addColumns(\n    new MultiValueColumn(\"data1\", 100, 20, 30),\n    new MultiValueColumn(\"data2\", 20, 70, 100)\n);\n%>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}chart\\textgreater{}} taglib to the\n  \\texttt{view.jsp}, passing the \\texttt{\\_barChartConfig} as the\n  \\texttt{config} attribute's value:\n\n\\begin{verbatim}\n<chart:bar\n  config=\"<%= _barChartConfig %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-bar.png}\n\\caption{A bar chart models the data in bars.}\n\\end{figure}\n\nAwesome! Now you know how to create bar charts for your apps.\n\n\\section{Related Topics}\\label{related-topics-32}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/line-charts}{Line Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/donut-charts}{Donut\n  Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/combination-charts}{Combination\n  Charts}\n\\end{itemize}\n\n\\chapter{Line Charts}\\label{line-charts}\n\nLine charts contain multiple sets of data. A Line chart displays the\ndata linearly. Each data series (created with the \\texttt{addColumns()}\nmethod) is defined with a new instance of the\n\\href{https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/MultiValueColumn.html}{\\texttt{MultiValueColumn}\nobject}, which takes an ID and a set of values. Follow these steps to\nconfigure your portlet to use line charts.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the chart taglib along with the \\texttt{LineChartConfig} and\n  \\texttt{MultiValueColumn} classes into your bundle's \\texttt{init.jsp}\n  file:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.point.line.LineChartConfig\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.MultiValueColumn\" %>\n\\end{verbatim}\n\\item\n  Add the following Java scriptlet to the top of your \\texttt{view.jsp}:\n\n\\begin{verbatim}\n<%\nLineChartConfig _lineChartConfig = new LineChartConfig();\n\n_lineChartConfig.addColumns(\n  new MultiValueColumn(\"data1\", 100, 20, 30),\n  new MultiValueColumn(\"data2\", 20, 70, 100)\n);\n%>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}chart\\textgreater{}} taglib to the\n  \\texttt{view.jsp}, passing the \\texttt{\\_lineChartConfig} as the\n  \\texttt{config} attribute's value:\n\n\\begin{verbatim}\n<chart:line\n  config=\"<%= _lineChartConfig %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-line.png}\n\\caption{A Line chart displays the data linearly.}\n\\end{figure}\n\nAwesome! Now you know how to create line charts for your apps.\n\n\\section{Related Topics}\\label{related-topics-33}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/spline-charts}{Spline\n  Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/step-charts}{Step Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/predictive-charts}{Predictive\n  Charts}\n\\end{itemize}\n\n\\chapter{Scatter Charts}\\label{scatter-charts}\n\nScatter charts contain multiple sets of data. A scatter chart models the\ndata as individual points. Each data series (created with the\n\\texttt{addColumns()} method) is defined with a new instance of the\n\\href{https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/MultiValueColumn.html}{\\texttt{MultiValueColumn}\nobject}, which takes an ID and a set of values. Follow these steps to\nconfigure your portlet to use scatter charts.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the chart taglib along with the \\texttt{ScatterChartConfig} and\n  \\texttt{MultiValueColumn} classes into your bundle's \\texttt{init.jsp}\n  file:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.point.scatter.ScatterChartConfig\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.MultiValueColumn\" %>\n\\end{verbatim}\n\\item\n  Add the following Java scriptlet to the top of your \\texttt{view.jsp}:\n\n\\begin{verbatim}\n<%\nScatterChartConfig _scatterChartConfig = new ScatterChartConfig();\n\n_scatterChartConfig.addColumns(\n  new MultiValueColumn(\"data1\", 100, 20, 30),\n  new MultiValueColumn(\"data2\", 20, 70, 100));\n%>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}chart\\textgreater{}} taglib to the\n  \\texttt{view.jsp}, passing the \\texttt{\\_scatterChartConfig} as the\n  \\texttt{config} attribute's value:\n\n\\begin{verbatim}\n<chart:scatter\n  config=\"<%= _scatterChartConfig %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-scatter.png}\n\\caption{A scatter chart models the data as individual points.}\n\\end{figure}\n\nAwesome! Now you know how to create scatter charts for your apps.\n\n\\section{Related Topics}\\label{related-topics-34}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/line-charts}{Line Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/step-charts}{Step Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/predictive-charts}{Predictive\n  Charts}\n\\end{itemize}\n\n\\chapter{Spline Charts}\\label{spline-charts}\n\nSpline charts contain multiple sets of data. A spline chart connects\npoints of data with a smooth curve. Each data series (created with the\n\\texttt{addColumns()} method) is defined with a new instance of the\n\\href{https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/MultiValueColumn.html}{\\texttt{MultiValueColumn}\nobject}, which takes an ID and a set of values. Follow these steps to\nconfigure your portlet to use spline charts.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the chart taglib along with the \\texttt{SplineChartConfig} and\n  \\texttt{MultiValueColumn} classes into your bundle's \\texttt{init.jsp}\n  file:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.point.spline.SplineChartConfig\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.MultiValueColumn\" %>\n\\end{verbatim}\n\\item\n  Add the following Java scriptlet to the top of your \\texttt{view.jsp}:\n\n\\begin{verbatim}\n<%\nSplineChartConfig _splineChartConfig = new SplineChartConfig();\n\n_splineChartConfig.addColumns(\n  new MultiValueColumn(\"data1\", 100, 20, 30),\n  new MultiValueColumn(\"data2\", 20, 70, 100)\n);\n%>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}chart\\textgreater{}} taglib to the\n  \\texttt{view.jsp}, passing the \\texttt{\\_splineChartConfig} as the\n  \\texttt{config} attribute's value:\n\n\\begin{verbatim}\n<chart:spline\n  config=\"<%= _splineChartConfig %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-spline.png}\n\\caption{A spline chart connects points of data with a smooth curve.}\n\\end{figure}\n\nYou can also use an area spline chart if you prefer. An area spline\nchart highlights the area under the spline curve.\n\n\\begin{verbatim}\n<chart:area-spline\n  config=\"<%= _splineChartConfig %>\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-area-spline.png}\n\\caption{An area spline chart highlights the area under the spline\ncurve.}\n\\end{figure}\n\nAwesome! Now you know how to create spline charts for your apps.\n\n\\section{Related Topics}\\label{related-topics-35}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/line-charts}{Line Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/step-charts}{Step Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/scatter-charts}{Scatter\n  Charts}\n\\end{itemize}\n\n\\chapter{Step Charts}\\label{step-charts}\n\nStep charts contain multiple sets of data. A step chart steps between\nthe points of data, resembling steps. Each data series (created with the\n\\texttt{addColumns()} method) is defined with a new instance of the\n\\href{https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/MultiValueColumn.html}{\\texttt{MultiValueColumn}\nobject}, which takes an ID and a set of values. Follow these steps to\nconfigure your portlet to use step charts.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the chart taglib along with the \\texttt{StepChartConfig} and\n  \\texttt{MultiValueColumn} classes into your bundle's \\texttt{init.jsp}\n  file:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.point.step.StepChartConfig\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.MultiValueColumn\" %>\n\\end{verbatim}\n\\item\n  Add the following Java scriptlet to the top of your \\texttt{view.jsp}:\n\n\\begin{verbatim}\n<%\nStepChartConfig _stepChartConfig = new StepChartConfig();\n\n_stepChartConfig.addColumns(\n  new MultiValueColumn(\"data1\", 100, 20, 30),\n  new MultiValueColumn(\"data2\", 20, 70, 100)\n);\n%>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}chart\\textgreater{}} taglib to the\n  \\texttt{view.jsp}, passing the \\texttt{\\_stepChartConfig} as the\n  \\texttt{config} attribute's value:\n\n\\begin{verbatim}\n<chart:step\n  config=\"<%= _stepChartConfig %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-step.png}\n\\caption{A step chart steps between the points of data, resembling\nsteps.}\n\\end{figure}\n\nYou can also use an area step chart if you prefer. An area step chart\nhighlights the area covered by a step graph.\n\n\\begin{verbatim}\n<chart:area-step\n  config=\"<%= _stepChartConfig %>\"\n/>\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-area-step.png}\n\\caption{An area step chart highlights the area covered by a step\ngraph.}\n\\end{figure}\n\nAwesome! Now you know how to create step charts for your apps.\n\n\\section{Related Topics}\\label{related-topics-36}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/line-charts}{Line Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/spline-charts}{Spline\n  Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/scatter-charts}{Scatter\n  Charts}\n\\end{itemize}\n\n\\chapter{Combination Charts}\\label{combination-charts}\n\nCombination charts have minor differences from other charts. In a\ncombination chart, you must define the representation type of each data\nset: \\texttt{AREA}, \\texttt{AREA\\_SPLINE}, \\texttt{AREA\\_STEP},\n\\texttt{BAR}, \\texttt{BUBBLE}, \\texttt{DONUT}, \\texttt{GAUGE},\n\\texttt{LINE}, \\texttt{PIE}, \\texttt{SCATTER}, \\texttt{SPLINE}, or\n\\texttt{STEP}. Each data set in a combination chart is an instance of\nthe\n\\href{https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/TypedMultiValueColumn.html}{\\texttt{TypedMultiValueColumn}\nobject}. Each object receives an ID, the representation type, and values\nfor the data. Follow these steps to configure your portlet to use\ncombination charts.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the chart taglib along with the\n  \\texttt{CombinationChartConfig}, \\texttt{MultiValueColumn}, and\n  \\texttt{MultiValueColumn.Type} classes into your bundle's\n  \\texttt{init.jsp} file:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.combination.CombinationChartConfig\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.MultiValueColumn\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.TypedMultiValueColumn.Type\" %>\n\\end{verbatim}\n\\item\n  Add the following Java scriptlet to the top of your \\texttt{view.jsp}:\n\n\\begin{verbatim}\n<%\nCombinationChartConfig _combinationChartConfig =\nnew CombinationChartConfig();\n\n_combinationChartConfig.addColumns(\n  new TypedMultiValueColumn(\n    \"data1\", Type.BAR, 30, 20, 50, 40, 60, 50),\n  new TypedMultiValueColumn(\n    \"data2\", Type.BAR, 200, 130, 90, 240, 130, 220),\n  new TypedMultiValueColumn(\n    \"data3\", Type.SPLINE, 300, 200, 160, 400, 250, 250),\n  new TypedMultiValueColumn(\n    \"data4\", Type.LINE, 200, 130, 90, 240, 130, 220),\n  new TypedMultiValueColumn(\n    \"data5\", Type.BAR, 130, 120, 150, 140, 160, 150),\n  new TypedMultiValueColumn(\n    \"data6\", Type.AREA, 90, 70, 20, 50, 60, 120)\n  );\n\n_combinationChartConfig.addGroup(\"data1\", \"data2\");\n%>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}chart\\textgreater{}} taglib to the\n  \\texttt{view.jsp}, passing the \\texttt{\\_combinationChartConfig} as\n  the \\texttt{config} attribute's value:\n\n\\begin{verbatim}\n<chart:combination\n  config=\"<%= _combinationChartConfig %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-combination.png}\n\\caption{A combination chart displays a variety of data set types.}\n\\end{figure}\n\nAwesome! Now you know how to create combination charts for your apps.\n\n\\section{Related Topics}\\label{related-topics-37}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/bar-charts}{Bar Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/line-charts}{Line Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/geomap-charts}{Geomap\n  Charts}\n\\end{itemize}\n\n\\chapter{Donut Charts}\\label{donut-charts}\n\nDonut charts are percentage-based. A donut chart is similar to a pie\nchart, but it has a hole in the center. Each data set must be defined as\na new instance of the\n\\href{https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/SingleValueColumn.html}{\\texttt{SingleValueColumn}\nobject}. Follow these steps to configure your portlet to use donut\ncharts.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the chart taglib along with the \\texttt{DonutChartConfig} and\n  \\texttt{SingleValueColumn} classes into your bundle's\n  \\texttt{init.jsp} file:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.percentage.donut.DonutChartConfig\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.SingleValueColumn\" %>\n\\end{verbatim}\n\\item\n  Add the following Java scriptlet to the top of your \\texttt{view.jsp}:\n\n\\begin{verbatim}\n<%\nDonutChartConfig _donutChartConfig = new DonutChartConfig();\n\n_donutChartConfig.addColumns(\n  new SingleValueColumn(\"data1\", 30),\n  new SingleValueColumn(\"data2\", 70)\n);\n%>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}chart\\textgreater{}} taglib to the\n  \\texttt{view.jsp}, passing the \\texttt{\\_donutChartConfig} as the\n  \\texttt{config} attribute's value:\n\n\\begin{verbatim}\n<chart:donut\n  config=\"<%= _donutChartConfig %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-donut.png}\n\\caption{A donut chart is similar to a pie chart, but it has a hole in\nthe center.}\n\\end{figure}\n\nAwesome! Now you know how to create donut charts for your apps.\n\n\\section{Related Topics}\\label{related-topics-38}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/pie-charts}{Pie Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/gauge-charts}{Gauge\n  Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/bar-charts}{Bar Charts}\n\\end{itemize}\n\n\\chapter{Gauge Charts}\\label{gauge-charts}\n\nGauge charts are percentage-based. A gauge chart shows where\npercentage-based data falls over a given range. Each data set must be\ndefined as a new instance of the\n\\href{https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/SingleValueColumn.html}{\\texttt{SingleValueColumn}\nobject}. Follow these steps to configure your portlet to use gauge\ncharts.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the chart taglib along with the \\texttt{GaugeChartConfig} and\n  \\texttt{SingleValueColumn} classes into your bundle's\n  \\texttt{init.jsp} file:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.gauge.GaugeChartConfig\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.SingleValueColumn\" %>\n\\end{verbatim}\n\\item\n  Add the following Java scriptlet to the top of your \\texttt{view.jsp}:\n\n\\begin{verbatim}\n<%\nGaugeChartConfig _gaugeChartConfig = new GaugeChartConfig();\n\n_gaugeChartConfig.addColumn(\n  new SingleValueColumn(\"data1\", 85.4)\n);\n%>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}chart\\textgreater{}} taglib to the\n  \\texttt{view.jsp}, passing the \\texttt{\\_gaugeChartConfig} as the\n  \\texttt{config} attribute's value:\n\n\\begin{verbatim}\n<chart:gauge\n  config=\"<%= _gaugeChartConfig %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-gauge.png}\n\\caption{A gauge chart shows where percentage-based data falls over a\ngiven range.}\n\\end{figure}\n\nAwesome! Now you know how to create gauge charts for your apps.\n\n\\section{Related Topics}\\label{related-topics-39}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/pie-charts}{Pie Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/donut-charts}{Donut\n  Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/bar-charts}{Bar Charts}\n\\end{itemize}\n\n\\chapter{Pie Charts}\\label{pie-charts}\n\nPie charts are percentage-based. A pie chart models percentage-based\ndata as individual slices of pie. Each data set must be defined as a new\ninstance of the\n\\href{https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/SingleValueColumn.html}{\\texttt{SingleValueColumn}\nobject}. Follow these steps to configure your portlet to use pie charts.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the chart taglib along with the \\texttt{PieChartConfig} and\n  \\texttt{SingleValueColumn} classes into your bundle's\n  \\texttt{init.jsp} file:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.percentage.pie.PieChartConfig\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.SingleValueColumn\" %>\n\\end{verbatim}\n\\item\n  Add the following Java scriptlet to the top of your \\texttt{view.jsp}:\n\n\\begin{verbatim}\n<%\nPieChartConfig _pieChartConfig = new PieChartConfig();\n\n_pieChartConfig.addColumn(\n  new SingleValueColumn(\"data1\", 85.4)\n);\n%>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}chart\\textgreater{}} taglib to the\n  \\texttt{view.jsp}, passing the \\texttt{\\_pieChartConfig} as the\n  \\texttt{config} attribute's value:\n\n\\begin{verbatim}\n<chart:pie\n  config=\"<%= _pieChartConfig %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-pie.png}\n\\caption{A pie chart models percentage-based data as individual slices\nof pie.}\n\\end{figure}\n\nAwesome! Now you know how to create pie charts for your apps.\n\n\\section{Related Topics}\\label{related-topics-40}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/donut-charts}{Donut\n  Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/gauge-charts}{Gauge\n  Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/spine-charts}{Spline\n  Charts}\n\\end{itemize}\n\n\\chapter{Geomap Charts}\\label{geomap-charts}\n\nA Geomap Chart lets you visualize data based on geography, given a\nspecified color range---a lighter color representing a lower rank and a\ndarker a higher rank usually. The default configuration comes from the\nClay charts\n\\href{https://github.com/liferay/clay/blob/2.x-stable/packages/clay-charts/src/Geomap.js\\#L90-L104}{geomap\ncomponent}: which ranges from light-blue (\\#b1d4ff) to dark-blue\n(\\#0065e4) and ranks the geography based on the location's\n\\texttt{pop\\_est} value (specified in the geomap's JSON file).\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-geomap-default.png}\n\\caption{A Geomap chart displays a heatmap representing the data.}\n\\end{figure}\n\nFollow these steps to configure your portlet to use geomap charts.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the chart taglib along with the \\texttt{GeomapConfig},\n  \\texttt{GeomapColor}, and \\texttt{GeomapColorRange} classes into your\n  bundle's \\texttt{init.jsp} file:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.geomap.GeomapConfig\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.geomap.GeomapColor\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.geomap.GeomapColorRange\" %>\n\\end{verbatim}\n\\item\n  Add the following Java scriptlet to the top of your \\texttt{view.jsp}.\n  The colors---a color for minimum and a color for maximum---are\n  completely configurable, as shown in the second example configuration\n  below: \\texttt{\\_geomapConfig2}. Create a new\n  \\texttt{GeomapColorRange} and set the minimum and maximum color values\n  with the \\texttt{setMax()} and \\texttt{setMin()} methods. Specify the\n  highlight color---the color displayed when you mouse over an\n  area---with the \\texttt{setSelected()} method. use the\n  \\texttt{geomapColor.setValue()} method to specify the JSON property to\n  determine the geomap's ranking. Specify the JSON filepath with the\n  \\texttt{setDataHREF()} method. The example below displays a geomap\n  based on the length of each location's name:\n\n\\begin{verbatim}\n<%\nGeomapConfig _geomapConfig1 = new GeomapConfig();\nGeomapConfig _geomapConfig2 = new GeomapConfig();\n\nGeomapColor geomapColor = new GeomapColor();\nGeomapColorRange geomapColorRange = new GeomapColorRange();\n\ngeomapColorRange.setMax(\"#b2150a\");\ngeomapColorRange.setMin(\"#ee3e32\");\n\ngeomapColor.setGeomapColorRange(geomapColorRange);\n\ngeomapColor.setSelected(\"#a9615c\");\n\ngeomapColor.setValue(\"name_len\");\n\n_geomapConfig2.setColor(geomapColor);\n\nStringBuilder sb = new StringBuilder();\n\nsb.append(_portletRequest.getScheme());\nsb.append(StringPool.COLON);\nsb.append(StringPool.SLASH);\nsb.append(StringPool.SLASH);\nsb.append(_portletRequest.getServerName());\nsb.append(StringPool.COLON);\nsb.append(_portletRequest.getServerPort());\nsb.append(_portletRequest.getContextPath());\nsb.append(StringPool.SLASH);\nsb.append(\"geomap.geo.json\");\n\n_geomapConfig1.setDataHREF(sb.toString());\n_geomapConfig2.setDataHREF(sb.toString());\n%>\n\\end{verbatim}\n\\item\n  Add the \\texttt{\\textless{}chart\\textgreater{}} taglib to the\n  \\texttt{view.jsp} along with any styling information for the geomap,\n  such as the size and margins as shown below:\n\n\\begin{verbatim}\n<style type=\"text/css\">\n    .geomap {\n        margin: 10px 0 10px 0;\n    }\n    .geomap svg {\n        width: 100%;\n        height: 500px !important;\n    }\n</style>\n\n<chart:geomap\n    config=\"<%= _geomapConfig1 %>\"\n    id=\"geomap-default-colors\"\n/>\n\n<chart:geomap\n    config=\"<%= _geomapConfig2 %>\"\n    id=\"geomap-custom-colors\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-geomap-custom.png}\n\\caption{Geomap charts can be customized to fit the look and feel you\ndesire.}\n\\end{figure}\n\nAwesome! Now you know how to create geomap charts for your apps.\n\n\\section{Related Topics}\\label{related-topics-41}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/bar-charts}{Bar Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/pie-charts}{Pie Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/combination-charts}{Combination\n  Charts}\n\\end{itemize}\n\n\\chapter{Predictive Charts}\\label{predictive-charts}\n\nPredictive charts let you visualize current data along with\npredicted/forecasted data within a given value range.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-predictive-value-range.png}\n\\caption{Predicted/forecasted data is surrounded by a highlighted area\nof possible values.}\n\\end{figure}\n\nFollow these steps to use predictive charts.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the chart taglib along with the \\texttt{PredictiveChartConfig}\n  and \\texttt{MixedDataColumn} classes into your bundle's\n  \\texttt{init.jsp} file:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.predictive.PredictiveChartConfig\" %>\n<%@ page import=\"com.liferay.frontend.taglib.chart.model.MixedDataColumn\" %>\n\\end{verbatim}\n\\item\n  Add the following Java scriptlet to the top of your \\texttt{view.jsp}.\n  Add a\n  \\href{https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/MixedDataColumn.html}{\\texttt{MixedDataColumn}\n  object} ---a column that supports both single number values and arrays\n  of three numbers---for each data series. Single number values define\n  existing data. Arrays of numbers are used as the prediction/forecast\n  data and contain three numbers: a minimum value, an estimated value,\n  and a maximum value. The estimated value is rendered solid and\n  surrounded by a highlighted area with borders specified by the minimum\n  and maximum values. This lets you visualize your estimated values,\n  while also giving you an idea of the possible value ranges. Use the\n  \\texttt{addDataColumn()} method to add each data series:\n\n\\begin{verbatim}\n<%\nprivate PredictiveChartConfig _predictiveChartConfig = new\nPredictiveChartConfig();\n\nMixedDataColumn mixedDataColumn1 = new MixedDataColumn(\n  \"data1\", 130, 340, 200, 500, 80, 240, 40,\n  new Number[] {370, 400, 450}, new Number[] {210, 240, 270},\n  new Number[] {150, 180, 210}, new Number[] {60, 90, 120},\n  new Number[] {310, 340, 370}\n);\n\n_predictiveChartConfig.addDataColumn(mixedDataColumn1);\n\nMixedDataColumn mixedDataColumn2 = new MixedDataColumn(\n  \"data2\", 210, 160, 50, 125, 230, 110, 90,\n  Arrays.asList(170, 200, 230), Arrays.asList(10, 40, 70),\n  Arrays.asList(350, 380, 410), Arrays.asList(260, 290, 320),\n  Arrays.asList(30, 70, 150)\n);\n\n_predictiveChartConfig.addDataColumn(mixedDataColumn2);\n\n_predictiveChartConfig.setAxisXTickFormat(\"%b\");\n\n_predictiveChartConfig.setPredictionDate(\"2018-07-01\");\n\nList<String> timeseries = new ArrayList<>();\n\ntimeseries.add(\"2018-01-01\");\ntimeseries.add(\"2018-02-01\");\ntimeseries.add(\"2018-03-01\");\ntimeseries.add(\"2018-04-01\");\ntimeseries.add(\"2018-05-01\");\ntimeseries.add(\"2018-06-01\");\ntimeseries.add(\"2018-07-01\");\ntimeseries.add(\"2018-08-01\");\ntimeseries.add(\"2018-09-01\");\ntimeseries.add(\"2018-10-01\");\ntimeseries.add(\"2018-11-01\");\ntimeseries.add(\"2018-12-01\");\n\n_predictiveChartConfig.setTimeseries(timeseries);\n%>\n\\end{verbatim}\n\n  Predictive charts have these properties:\n\n  \\textbf{axisXTickFormat:} An optional string which specfies the time\n  formatting on the X axis. For more information on which formats can be\n  specified please refer to\n  \\href{https://github.com/d3/d3-time-format/blob/master/README.md\\#locale_format}{d3's\n  time format README}. This value is set using the\n  \\texttt{setAxisXTickFormat()} method.\n\n  \\textbf{Prediction Date:} A date as a string that represents the point\n  in the timeline from when the forecast/prediction is shown. This value\n  is parsed as a Date object in JavaScript and set using the\n  \\texttt{setPredictionDate()} method.\n\n  \\textbf{Time Series:} A timeline for the data which is displayed on\n  the X axis of the chart. This value is set as an array of dates\n  (\\texttt{2018-01-01} for example).\n\\item\n  Add the \\texttt{\\textless{}chart\\textgreater{}} taglib to the\n  \\texttt{view.jsp}, passing the \\texttt{\\_predictiveChartConfig} as the\n  \\texttt{config} attribute's value:\n\n\\begin{verbatim}\n<chart:predictive\n  config=\"<%= _predictiveChartConfig %>\"\n/>\n\\end{verbatim}\n\\end{enumerate}\n\nThe area contained within the light-blue rectangle is the point from\nwhich the predicted/forecasted values are shown:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-taglib-predictive.png}\n\\caption{A predictive chart lets you visualize estimated future data\nalongside existing data.}\n\\end{figure}\n\nAwesome! Now you know how to create predictive charts for your apps.\n\n\\section{Related Topics}\\label{related-topics-42}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/Line-charts}{Line Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/combination-charts}{Combination\n  Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/geomap-charts}{Geomap\n  Charts}\n\\end{itemize}\n\n\\chapter{Refreshing Charts to Reflect Real Time\nData}\\label{refreshing-charts-to-reflect-real-time-data}\n\nThe polling interval property is an optional property for all charts. It\nspecifies the time in milliseconds for the chart's data to refresh. You\ncan use this for charts that receive any kind of real time data, such as\na JSON file that changes periodically. This ensures that the chart is up\nto date, reflecting the most recent data. Follow these steps to\nconfigure your chart to use real time data.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a new java scriptlet and create a new instance of the chart's\n  object, and put the data into the \\texttt{data} attribute. Finally,\n  set the chart's polling interval with the\n  \\texttt{setPollingInterval()} method. An example \\texttt{view.jsp}\n  configuration is shown below:\n\n\\begin{verbatim}\n```java\n<%\nLineChartConfig _pollingIntervalLineChartConfig = new LineChartConfig();\n\n_pollingIntervalLineChartConfig.put(\"data\", \"/foo.json\");\n\n_pollingIntervalLineChartConfig.setPollingInterval(2000);\n%>\n```\n\\end{verbatim}\n\\item\n  Set the chart taglib's \\texttt{config} attribute to the updated\n  configuration object that you created in the last step, as shown in\n  the example below:\n\n\\begin{verbatim}\n```markup\n<chart:line\n    componentId=\"polling-interval-line-chart\"\n    config=\"<%= _pollingIntervalLineChartConfig %>\"\n/>\n```\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/chart-polling-interval.png}\n\\caption{The polling interval property lets you refresh charts at a\ngiven interval to reflect real time data.}\n\\end{figure}\n\nNow you know how to reflect real time data in your charts!\n\n\\section{Related Topics}\\label{related-topics-43}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/bar-charts}{Bar Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/scatter-charts}{Scatter\n  Charts}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/donut-charts}{Donut\n  Charts}\n\\end{itemize}\n\n\\chapter{Using AUI Taglibs}\\label{using-aui-taglibs}\n\nThe AUI tag library provides tags that implement commonly used UI\ncomponents. These tags make your markup consistent, responsive, and\naccessible.\n\nYou can find a list of the available\n\\texttt{\\textless{}aui\\textgreater{}} taglibs in the\n\\href{https://docs.liferay.com/portal/7.2-latest/taglibs/util-taglib/aui/tld-summary.html}{AUI\ntaglibdocs}. Each taglib has a list of attributes that can be passed to\nthe tag. Some of these are required, and some are optional. See the\ntaglibdocs to view the requirements for each tag. You'll find the full\nmarkup generated by the tags in their JSPs in their\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/portal-web/docroot/html/taglib/aui}{Liferay\nGithub Repo} folders.\n\nTo use the AUI taglib library in your apps, you must add the following\ndeclaration to your JSP:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n\\end{verbatim}\n\nThe AUI taglib is also available via a macro for your FreeMarker theme\ntemplates and web content templates. Follow this syntax:\n\n\\begin{verbatim}\n<@liferay_aui[\"tag-name\"] attribute=\"string value\" attribute=10 />\n\\end{verbatim}\n\nThis section covers how to create UI components with the AUI taglibs.\nEach article contains code examples along with a screenshot of the\nresulting UI.\n\n\\chapter{Building Forms with AUI\nTags}\\label{building-forms-with-aui-tags}\n\nThe\n\\href{https://docs.liferay.com/portal/7.2-latest/taglibs/util-taglib/aui/tld-summary.html}{AUI\ntag library} provides all the components you need to build forms for\nyour applications. AUI tags provide many benefits to standard form\nelements, such as custom namespacing, localization, and even validation.\nThey provide multiple attributes that let you create the experience you\nwant for your users.\n\nFollow these steps to build a form using AUI tags:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the \\texttt{aui} taglib declaration to your portlet's\n  \\texttt{view.jsp} if you haven't already:\n\n\\begin{verbatim}\n<%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n\\end{verbatim}\n\\item\n  Build your form using the tags shown below. Each tag links to the\n  corresponding taglibdoc that list the available attributes:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/input.html}{\\texttt{\\textless{}aui:input\\textgreater{}}}\n  \\item\n    \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/button.html}{\\texttt{\\textless{}aui:button\\textgreater{}}}\n  \\item\n    \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/button-row.html}{\\texttt{\\textless{}aui:button-row\\textgreater{}}}\n  \\item\n    \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/container.html}{\\texttt{\\textless{}aui:container\\textgreater{}}}\n  \\item\n    \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/col.html}{\\texttt{\\textless{}aui:col\\textgreater{}}}\n  \\item\n    \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/row.html}{\\texttt{\\textless{}aui:row\\textgreater{}}}\n  \\item\n    \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/field-wrapper.html}{\\texttt{\\textless{}aui:field-wrapper\\textgreater{}}}\n  \\item\n    \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/fieldset.html}{\\texttt{\\textless{}aui:fieldset\\textgreater{}}}\n  \\item\n    \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/fieldset-group.html}{\\texttt{\\textless{}aui:fieldset-group\\textgreater{}}}\n  \\item\n    \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/form.html}{\\texttt{\\textless{}aui:form\\textgreater{}}}\n  \\item\n    \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/select.html}{\\texttt{\\textless{}aui:select\\textgreater{}}}\n  \\item\n    \\href{https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/option.html}{\\texttt{\\textless{}aui:option\\textgreater{}}}\n  \\end{itemize}\n\n  An example form is shown below:\n\n\\begin{verbatim}\n<aui:form name=\"fm\">\n    <aui:fieldset-group markupView=\"lexicon\">\n        <aui:fieldset label=\"Personal Information\">\n            <aui:row>\n                <aui:col width=\"50\">\n                    <aui:input label=\"First Name\" name=\"firstName\" type=\"text\" />\n                </aui:col>\n                <aui:col width=\"50\">\n                    <aui:input label=\"Last Name\" name=\"lastName\" type=\"text\" />\n                </aui:col>\n            </aui:row>\n            <aui:row>\n                <aui:col width=\"50\">\n                    <aui:input label=\"Username\" name=\"username\" type=\"text\" />\n                </aui:col>\n                <aui:col width=\"50\">\n                    <aui:input label=\"Email\" name=\"email\" type=\"email\" />\n                </aui:col>\n            </aui:row>\n        </aui:fieldset>\n    </aui:fieldset-group>\n    <aui:fieldset-group markupView=\"lexicon\">\n        <aui:fieldset label=\"Miscellaneous\">\n            <aui:input label=\"Hobbies\" name=\"hobbies\" type=\"textarea\" />\n            <aui:input label=\"Receive email updates\" name=\"emailUpdates\" type=\"checkbox\" />\n        </aui:fieldset>\n    </aui:fieldset-group>\n    <aui:button-row>\n        <aui:button name=\"submitButton\" type=\"submit\" value=\"Submit\" />\n    </aui:button-row>\n</aui:form>\n\\end{verbatim}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/aui-taglib-basic-form.png}\n  \\caption{The AUI tags provide everything you need to build forms for\n  your applications.}\n  \\end{figure}\n\\item\n  Optionally add validation to your form fields. Nest a\n  \\texttt{\\textless{}aui:validator\\textgreater{}} tag inside each form\n  field that you want to validate. Specify the validation rule with the\n  \\texttt{\\textless{}aui:validator\\textgreater{}} tag's \\texttt{name}\n  attribute (The available validation rules are shown in the table\n  below). You can override a field's default validation error message\n  with the \\texttt{errorMessage} attribute. An example configuration is\n  shown below:\n\n\\begin{verbatim}\n<aui:form name=\"myForm\">\n    <aui:input name=\"password\" id=\"password\" label=\"Password\" \n    required=\"true\" />\n    <aui:input name=\"confirmPassword\" id=\"password\" \n    label=\"Confirm Password\" required=\"true\">\n        <aui:validator name=\"equalTo\" \n        errorMessage=\"The passwords much match. Please try again.\" >\n        '#<portlet:namespace>password'\n        </aui:validator>\n    </aui:input>\n</aui:form>\n\\end{verbatim}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/aui-taglib-form-validation.png}\n  \\caption{The AUI tags also provide validation for form fields.}\n  \\end{figure}\n\n  The full list of available validation rules is shown in the table\n  below:\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n Rule | Description | Default Error Message |\n --- | --- | --- |\n `acceptFiles` | Specifies that the field can only contain the file types given. Each file extension must be separated by a comma. For example </br> `<aui:validator name=\"acceptFiles\">'jpg,png,tif,gif'</aui:validator>` | 'Please enter a file with a valid extension ([supported extensions]).' |\n `alpha` | Permits alphabetic characters | 'Please enter only alpha characters.' |\n `alphanum` | Permits alphanumeric characters | 'Please enter only alphanumeric characters.' |\n `date` | Permits dates | 'Please enter a valid date.' |\n `digits` | Permits digits | 'Please enter only digits.' |\n `email` | Permits an email address | 'Please enter a valid email address.' |\n `equalTo` | Permits contents equal to another field with the specified field ID. For example, </br> `<aui:validator name=\"equalTo\">'#<portlet:namespace/>password'</aui:validator>` | 'Please enter the same value again.' |\n `max` | Permits an integer value less than the specified value. For example, a max value of 20 is specified with </br> `<aui:validator name=\"max\">20</aui:validator>` | 'Please enter a value less than or equal to [max value].' |\n `maxLength` | Permits a maximum field length of the specified size (follows the same syntax as `max`) | 'Please enter no more than [max] characters.' |\n `min` | Permits an integer value greater than the specified minimum value (follows the same syntax as `max`) | 'Please enter a value greater than or equal to [min value].' |\n `minLength` | Permits a field length longer than the specified size (follows the same syntax as `max`). | 'Please enter at least [min] characters.' |\n `number` | Permits numerical values | 'Please enter a valid number.' |\n `range` | Permits a number between the specified range. For example, a range between 1.23 and 10 is specified here </br> `<aui:validator name=\"range\">[1.23,10]</aui:validator>` | 'Please enter a value between [0] and [1].' |\n `rangeLength` | Permits a field length between the specified range (follows the same syntax as `range`)  | 'Please enter a value between [0] and [1] characters long.' |\n `required` | Prevents a blank field  | 'This field is required.' |\n `url` | Permits a URL value | 'Please enter a valid URL.' |\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nNow you know how to build user-friendly forms for your applications.\n\n\\section{Related Topics}\\label{related-topics-44}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-chart-taglib-in-your-portlets}{Using\n  the Chart Taglib in Your Portlets}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-liferay-front-end-taglibs-in-your-portlet}{Using\n  Liferay Front-end Taglibs in Your Portlet}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-clay-taglib-in-your-portlets}{Using\n  the Clay Taglib in Your portlets}\n\\end{itemize}\n\n\\chapter{liferay-npm-bundler}\\label{liferay-npm-bundler}\n\nThe liferay-npm-bundler is a bundler (like\n\\href{https://webpack.github.io/}{Webpack} or\n\\href{http://browserify.org/}{Browserify} ) that targets Liferay DXP as\na platform and assumes you're using your npm packages from widgets (as\nopposed to typical web applications).\n\nThe workflow for running npm packages inside widgets is slightly\ndifferent from standard bundlers. Instead of bundling the JavaScript in\na single file, you must \\emph{link} all packages together in the browser\nwhen the full web page is assembled. This lets widgets share common\nversions of modules instead of each one loading its own copy. The\nliferay-npm-bundler handles this for you.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can also find information for the liferay-npm-bundler\nin the project's\n\\href{https://github.com/liferay/liferay-npm-build-tools/wiki}{Wiki}.\n\n\\noindent\\hrulefill\n\n\\section{How the Liferay npm Bundler Works\nInternally}\\label{how-the-liferay-npm-bundler-works-internally}\n\nThe liferay-npm-bundler takes a widget project and outputs its files\n(including npm packages) to a build folder, so the standard widget build\n(Gradle) can produce an OSGi bundle. You can learn more about the build\nfolder's structure in\n\\href{/docs/7-2/reference/-/knowledge_base/r/the-structure-of-osgi-bundles-containing-npm-packages}{The\nStructure of OSGi Bundles Containing NPM Packages} reference.\n\nThe liferay-npm-bundler uses the process below to create the OSGi\nbundle:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Copy the project's \\texttt{package.json} file to the output directory.\n\\item\n  Traverse the project's dependency tree to determine its dependencies.\n\\item\n  For the project,\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\alph{enumii}.}\n  \\item\n    Run the source files, specified in the \\texttt{.npmbundlerrc}\n    configuration, through the rules.\n  \\item\n    Pre-process the project's package with any configured plugins.\n  \\item\n    Run \\href{https://babeljs.io/}{Babel} with configured plugins for\n    each \\texttt{.js} file inside the project.\n  \\item\n    Post-process the project package with any configured plugins.\n  \\end{enumerate}\n\\item\n  For each npm package dependency:\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\alph{enumii}.}\n  \\item\n    Copy the npm package to the output folder and prefix the bundle's\n    name to it. Note that the bundler stores packages in a plain\n    \\emph{bundle-name\\$package}@\\emph{version} format, rather than the\n    standard node\\_modules tree format. To determine what is copied, the\n    bundler invokes a plugin to filter the package file list.\n  \\item\n    Run rules on the package files.\n  \\item\n    Pre-process the npm package with any configured plugins.\n  \\item\n    Run \\href{https://babeljs.io/}{Babel} with configured plugins for\n    each \\texttt{.js} file inside the npm package.\n  \\item\n    Post-process the npm package with any configured plugins.\n  \\end{enumerate}\n\\end{enumerate}\n\nThe only difference between the pre-process and post-process steps are\nwhen they are run (before or after Babel is run, respectively). During\nthis workflow, liferay-npm-bundler calls all the configured plugins so\nthey can perform transformations on the npm packages (for instance,\nmodifying their \\texttt{package.json} files, or deleting or moving\nfiles).\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} that the pre, post, and Babel phases were designed for\nthe old mode of operation (See the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/migrating-your-project-to-use-the-new-mode}{Migrating\nYour Project to Use the New Mode} for more information) and they will\ngradually be replaced with rules for the new mode.\n\n\\noindent\\hrulefill\n\nIn this reference section, you'll learn more about the\nliferay-npm-bundler's configuration, default presets, format, and more.\n\n\\chapter{\\texorpdfstring{Understanding the \\texttt{.npmbundlerrc}'s\nStructure}{Understanding the .npmbundlerrc's Structure}}\\label{understanding-the-.npmbundlerrcs-structure}\n\nThe liferay-npm-bundler is configured via a \\texttt{.npmbundlerrc} file\nplaced in the widget project's root folder. You can create a complete\nconfiguration manually or extend a configuration preset (via Babel).\n\nThis article explains the \\texttt{.npmbundlerrc} file's structure. See\nthe\n\\href{/docs/7-2/reference/-/knowledge_base/r/how-the-default-preset-configures-the-liferay-npm-bundler}{default\npreset reference} to learn how the default preset configures the\nliferay-npm-bundler. See\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-and-bundling-javascript-widgets-with-javascript-tooling}{Creating\nJavaScript Widgets with JavaScript Tooling} to learn how to use the\nliferay-npm-bundler along with the Liferay JS Generator to create\nJavaScript widgets.\n\n\\section{The Structure}\\label{the-structure}\n\nThe \\texttt{.npmbundlerrc} file has four possible phase definitions:\n\\emph{copy-process}, \\emph{pre-process}, \\emph{post-process}, and\n\\emph{babel}. These phase definitions are explained in more detail\nbelow:\n\n\\textbf{Copy-Process:} Defined with the \\texttt{copy-plugins} property\n(only available for dependency packages). Specifies which files should\nbe copied or excluded from each given package.\n\n\\textbf{Pre-Process:} Defined with the \\texttt{plugins} property.\nSpecifies plugins to run before the Babel phase is run.\n\n\\textbf{Babel:} Defined with the \\texttt{.babelrc} definition. Specifies\nthe \\texttt{.babelrc} file to use when running Babel through the\npackage's \\texttt{.js} files.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} During this phase, Babel transforms package files (for\nexample, to convert them to AMD format, if necessary), but doesn't\ntranspile them. In theory, you could also transpile them by configuring\nthe proper plugins. We recommend transpiling before running the bundler,\nto avoid mixing both unrelated processes.\n\n\\noindent\\hrulefill\n\n\\textbf{Post-Process:} Defined with the \\texttt{post-plugins} property.\nAn alternative to using the \\emph{pre-process} phase, this specifies\nplugins to run after the Babel phase has completed.\n\nHere's an example of a \\texttt{.npmbundlerrc} configuration:\n\n\\begin{verbatim}\n{\n    \"exclude\": {\n        \"*\": [\n            \"test/**/*\"\n        ],\n        \"some-package-name\": [\n            \"test/**/*\",\n            \"bin/**/*\"\n        ],\n        \"another-package-name@1.0.10\": [\n            \"test/**/*\",\n            \"bin/**/*\",\n            \"lib/extras-1.0.10.js\"\n        ]\n    },\n    \"include-dependencies\": [\n        \"isobject\", \"isarray\"\n    ],\n    \"output\": \"build\",\n    \"verbose\": false,\n    \"dump-report\": true,\n    \"config\": {\n        \"imports\": {\n            \"npm-angular5-provider\": {\n                \"@angular/common\": \"^5.0.0\",\n            \"@angular/core\": \"^5.0.0\"\n            }\n        }\n    },\n    \"/\": {\n    \"plugins\": [\"resolve-linked-dependencies\"],\n    \".babelrc\": {\n      \"presets\": [\"liferay-standard\"]\n    },\n    \"post-plugins\": [\n            \"namespace-packages\",\n            \"inject-imports-dependencies\"\n        ]\n    },\n    \"*\": {\n      \"copy-plugins\": [\"exclude-imports\"],\n      \"plugins\": [\"replace-browser-modules\"],\n      \".babelrc\": {\n        \"presets\": [\"liferay-standard\"]\n      },\n      \"post-plugins\": [\n        \"namespace-packages\",\n        \"inject-imports-dependencies\",\n        \"inject-peer-dependencies\"\n      ]\n    },\n    \"packages\": {\n        \"a-package-name\": [\n        \"copy-plugins\": [\"exclude-imports\"],\n        \"plugins\": [\"replace-browser-modules\"],\n        \".babelrc\": {\n          \"presets\": [\"liferay-standard\"]\n        },\n        \"post-plugins\": [\n          \"namespace-packages\",\n          \"inject-imports-dependencies\",\n          \"inject-peer-dependencies\"\n        ]\n        ],\n        \"other-package-name@1.0.10\": [\n          \"copy-plugins\": [\"exclude-imports\"],\n          \"plugins\": [\"replace-browser-modules\"],\n          \".babelrc\": {\n            \"presets\": [\"liferay-standard\"]\n          },\n          \"post-plugins\": [\n            \"namespace-packages\",\n            \"inject-imports-dependencies\",\n            \"inject-peer-dependencies\"\n          ]\n        ]\n    }\n}\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Not all definition formats (\\texttt{*},\n\\texttt{some-package-name}, and \\texttt{some-package-name@version})\nshown above are required. In most cases, the wildcard definition\n(\\texttt{*}) is enough. The non-wildcard formats\n(\\texttt{some-package-name} and \\texttt{some-package-name@version}) are\nrare exceptions for packages that require a more specific configuration\nthan the wildcard definition provides.\n\n\\noindent\\hrulefill\n\n\\section{Standard Configuration\nOptions}\\label{standard-configuration-options}\n\nBelow are the standard configuration options for the\n\\texttt{.npmbundlerrc} file:\n\n\\texttt{config}: Defines the global configuration that is made available\nto all liferay-npm-bundler and Babel plugins. Please refer to each\nplugin's documentation to find the available options for each specific\nplugin.\n\n\\begin{verbatim}\n{\n  \"config\": {\n    \"imports\": {\n      \"vuejs-provider\": {\n        \"vue\": \"^2.0.0\"\n      }\n    }\n  }\n}\n\\end{verbatim}\n\n\\texttt{dump-report:} Sets whether to generate a debugging report. If\n\\texttt{true}, a \\texttt{liferay-npm-bundler-report.html} file is\ngenerated in the project directory that describes all actions and\ndecisions taken when processing project and npm modules. Note that you\ncan also pass this as the build flag\n\\texttt{\\$\\ liferay-npm-bundler\\ -\\/-dump-report} or\n\\texttt{\\$\\ liferay-npm-bundler\\ -r}. The default value is\n\\texttt{false}.\n\n\\texttt{no-tracking:} whether to send usage analytics to our servers.\nNote that you can also pass this as a build flag with the CLI argument\n\\texttt{\\$\\ liferay-npm-bundler\\ -\\/-no-tracking}, or by creating a\nmarker file called \\texttt{.liferay-npm-bundler-no-tracking} in the\nproject's root folder or any of its ancestors, or by setting the\nenvironment variable\n\\texttt{LIFERAY\\_NPM\\_BUNDLER\\_NO\\_TRACKING=\\textquotesingle{}\\textquotesingle{}}.\nThe default value is \\texttt{false}.\n\n\\texttt{output:} by default the bundler writes packages to the standard\nGradle resources folder:\n\\texttt{build/resources/main/META-INF/resources}. Set this value to\noverride the default output folder. Note that the dependency npm\npackages are placed in a \\texttt{node\\_modules} folder inside the build\nfolder. Note if \\texttt{create-jar} is set, the default output folder is\n\\texttt{build}.\n\n\\texttt{preset:} specifies the \\texttt{liferay-npm-bundler} preset to\nuse as a base configuration. Note that if a \\texttt{.npmbundlerrc} file\nis not provided, the default\n\\texttt{liferay-npm-bundler-preset-standard} preset is used. All\nsettings provided by the preset are inherited, but they can be\noverridden.\n\n\\texttt{verbose:} Sets whether to output detailed information about what\nthe tool is doing to the console. The default value is \\texttt{false}.\n\n\\section{Package Processing Options}\\label{package-processing-options}\n\n\\texttt{\"/\"}: plugins' configuration for the project's package.\n\n\\texttt{\"\\textbackslash{}\"}: plugins' configuration for dependency\npackages.\n\n\\emph{(asterisk)}: Defines the default plugin configuration for all npm\npackages. It contains four values identified by a corresponding key.\nKeys \\texttt{copy-plugins}, \\texttt{plugins} and \\texttt{post-plugins}\nidentify arrays of \\texttt{liferay-npm-bundler} plugins to apply in the\ncopy, pre and post process steps. Key \\texttt{.babelrc} identifies an\nobject specifying the configuration to use in the Babel step and has the\nsame structure of a standard \\texttt{.babelrc} file.\n\n\\texttt{exclude:} defines glob expressions of files to exclude from\nbundling from all or specific packages. Each list is an array identified\nby one of the following keys: \\texttt{*} (any package),\n\\texttt{\\{package\\ name\\}} (any version of the package), or\n\\texttt{\\{package\\ name\\}@\\{version\\}} (a specific version of a\npackage). Below is an example configuration:\n\n\\begin{verbatim}\n{\n  \"exclude\": {\n    \"*\": [\"__tests__/**/*\"],\n    \"is-object\": [\"test/**/*\"],\n    \"is-array@1.0.1\": [\"test/**/*\", \"Makefile\"]\n  }\n}\n\\end{verbatim}\n\n\\texttt{ignore:} skips processing the specified JavaScript files with\nBabel for the project. An example configuration is shown below:\n\n\\begin{verbatim}\n{\n  \"ignore\": [\"lib/legacy/**/*.js\"]\n}\n\\end{verbatim}\n\n\\texttt{include-dependencies:} defines packages to include in bundling,\neven if they are not listed under the \\texttt{dependencies} section of\n\\texttt{package.json}. These packages must be available in the\n\\texttt{node\\_modules} folder (i.e.~installed manually, without saving\nthem to \\texttt{package.json}, or listed in the \\texttt{devDependencies}\nsection).\n\n\\texttt{packages:} defines plugin configuration for npm packages, per\npackage.\n\n\\texttt{max-parallel-files:} Defines the maximum number of files to\nprocess in parallel to avoid EMFILE errors (especially on Windows). The\ndefault value is \\texttt{128}.\n\n\\texttt{process-serially:} \\textbf{Note}: removed since v 2.7.0.\nReplaced with \\texttt{max-parallel-files}.\n\n\\texttt{rules:} defines rules to apply to the projects source files with\nthe loader. Rules must have a \\texttt{use} array property that defines\nthe loader to use, which can be specified using a package name or an\nobject with \\texttt{loader} and \\texttt{options} properties if\napplicable, and one or more of the properties below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{test}: defines a regular expression to filter files in the\n  \\texttt{sources} folders to determine whether to apply rules to them.\n  The project-relative path of each eligible file is compared against\n  the regular expression and files that match are processed by the\n  loaders.\n\\item\n  \\texttt{exclude}: refines the \\texttt{test} expression by specifying\n  files to exclude.\n\\item\n  \\texttt{include}: refines the \\texttt{test} expression by specifying\n  files to include.\n\\end{itemize}\n\nHere's an example configuration:\n\n\\begin{verbatim}\n{\n  \"rules\": [\n    {\n      \"test\": \"\\\\.js$\",\n      \"exclude\": \"node_modules\",\n      \"use\": [\n        {\n          \"loader\": \"babel-loader\",\n          \"options\": {\n            \"presets\": [\"env\", \"react\"]\n          }\n        }\n      ]\n    },\n    {\n      \"test\": \"\\\\.css$\",\n      \"use\": [\"style-loader\"]\n    },\n    {\n      \"test\": \"\\\\.json$\",\n      \"use\": [\"json-loader\"]\n    }\n  ]\n}\n\\end{verbatim}\n\n\\texttt{sources:} rules apply to files in these project folders. Folders\ncan be nested (e.g.~\\texttt{/src/main/resources/}) and must be written\nusing POSIX path separators (i.e.~use \\texttt{/} instead of\n\\texttt{\\textbackslash{}} on Win32 systems). Note that rules are\nautomatically applied to package dependency files of the project.\n\nAn example configuration is shown below:\n\n\\begin{verbatim}\n{\n  \"sources\": [\"src\", \"assets\"]\n}\n\\end{verbatim}\n\n\\section{OSGi Bundle Creation\nOptions}\\label{osgi-bundle-creation-options}\n\nSince version 2.2.0, the liferay-npm-bundler can create widget OSGi\nbundles for you. See\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-and-bundling-javascript-widgets-with-javascript-tooling}{Creating\nand Bundling JavaScript Widgets with JavaScript Tooling} for complete\ninstructions. The configuration options for OSGi bundle creation are\nshown below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{create-jar}: Creates an OSGi bundle when set to a truthy\n  value. When set to \\texttt{true}, all sub-options take default values.\n  When an object is passed, each sub-option can be configured\n  individually. Note that you can also pass this as a build flag:\n  \\texttt{\\$\\ liferay-npm-bundler\\ -\\/-create-} or\n  \\texttt{\\$\\ liferay-npm-bundler\\ -j}. The default value is\n  \\texttt{false}.\n\\end{itemize}\n\n\\begin{verbatim}\n{\n  \"create-jar\": true\n}\n\\end{verbatim}\n\n\\begin{itemize}\n\\item\n  \\textbf{create-jar.auto-deploy-portlet}: \\textbf{Note} that this\n  option is deprecated. Use the \\texttt{create-jar.features.js-extender}\n  option instead.\n\\item\n  \\textbf{create-jar.features.configuration}: specifies the file\n  describing the system (OSGi) and widget instance (widget preferences,\n  as defined in the Portlet spec) configuration to use. (see\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/configuring-system-settings-and-instance-settings-for-your-js-widget}{Configuring\n  System Settings and Instance Settings for Your JavaScript Widgets} for\n  more information on the required settings configuration). The default\n  value is \\texttt{features/configuration.json} if that file exists,\n  otherwise the default is \\texttt{undefined}.\n\\end{itemize}\n\n\\begin{verbatim}\n{\n  \"create-jar\": {\n    \"features\": {\n      \"configuration\": \"features/configuration.json\"\n    }\n  }\n}\n\\end{verbatim}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{create-jar.output-dir:} specifies where to place the final JAR\n\\end{itemize}\n\n\\begin{verbatim}\n{\n  \"create-jar\": {\n    \"output-dir\": \"dist\"\n  }\n}\n\\end{verbatim}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{create-jar.features.js-extender:} controls whether to process\n  the OSGi bundle with the JS Portlet Extender. You can also specify the\n  minimum required version of the Extender to use for the bundle. This\n  can be useful if you want to use advanced features in your bundle, but\n  you want it to be deployable in older versions of the Extender. Pass\n  the string \\texttt{\"any\"} to let the bundle deploy in any version of\n  the Extender. If \\texttt{true}, the liferay-npm-bundler automatically\n  determines the minimum version of the Extender required for the\n  features used in the bundle. the default value is \\texttt{true}. An\n  example configuration is shown below:\n\\end{itemize}\n\n\\begin{verbatim}\n{\n  \"create-jar\": {\n    \"features\": {\n      \"js-extender\": \"1.1.0\"\n    }\n  }\n}\n\\end{verbatim}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{create-jar.features.web-context:} specifies the context path\n  to use for publishing bundle's static resources. The default value is\n  \\texttt{/\\{project\\ name\\}-\\{project\\ version\\}}.\n\\end{itemize}\n\n\\begin{verbatim}\n{\n  \"create-jar\": {\n    \"features\": {\n      \"web-context\": \"/my-project\"\n    }\n  }\n}\n\\end{verbatim}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{create-jar.features.localization:} specifies the L10N file to\n  use for the bundle (see\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-widget}{Providing\n  Localization in Your JavaScript Widgets} for more information on using\n  localization in your widget. The default value is\n  \\texttt{features/localization/Language} if a properties file with that\n  base name exists, otherwise the default is \\texttt{undefined}.\n\\end{itemize}\n\n\\begin{verbatim}\n{\n  \"create-jar\": {\n    \"features\": {\n      \"localization\": \"features/localization/Language\"\n    }\n  }\n}\n\\end{verbatim}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{create-jar.features.settings:} \\textbf{Note} that this option\n  is deprecated. Use the \\texttt{create-jar.features.configuration}\n  option instead.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Plugins' configuration specifies the options for\nconfiguring plugins in all the possible phases, as well as the\n\\texttt{.babelrc} file to use when running Babel (see\n\\href{https://babeljs.io/docs/usage/babelrc/}{Babel's documentation} for\nmore information on that file format).\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Prior to version 1.4.0 of the liferay-npm-bundler,\npackage configurations were placed next to the tools options\n(\\texttt{*}, \\texttt{output}, \\texttt{exclude}, etc.) To prevent package\nname collisions, package configurations are now namespaced and placed\nunder the \\texttt{packages} section. To maintain backwards\ncompatibility, the liferay-npm-bundler falls back to the root section\noutside \\texttt{packages} for package configuration, if no package\nconfigurations (\\texttt{package-name@version}, \\texttt{package-name}, or\n\\texttt{*}) are found in the \\texttt{packages} section.\n\n\\noindent\\hrulefill\n\nNow you know the structure of the \\texttt{.npmbundlerrc} file!\n\n\\chapter{How the Default Preset Configures the\nliferay-npm-bundler}\\label{how-the-default-preset-configures-the-liferay-npm-bundler}\n\nThe liferay-npm-bundler comes with a default configuration preset:\n\\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-preset-standard}{\\texttt{liferay-npm-bundler-preset-standard}}\nin your \\texttt{.npmbundlerrc} file. This preset configures several\nplugins for the build process and is automatically used (even if the\n\\texttt{.npmbundlerrc} is missing), unless you override it with one of\nyour own. Running the liferay-npm-bundler with this preset applies the\n\\href{https://github.com/liferay/liferay-npm-build-tools/blob/master/packages/liferay-npm-bundler-preset-standard/config.json}{config\nfile} from \\texttt{liferay-npm-bundler-preset-standard}:\n\n\\begin{verbatim}\n{\n  \"/\": {\n    \"plugins\": [\"resolve-linked-dependencies\"],\n    \".babelrc\": {\n      \"presets\": [\"liferay-standard\"]\n    },\n    \"post-plugins\": [\"namespace-packages\", \"inject-imports-dependencies\"]\n  },\n  \"*\": {\n    \"copy-plugins\": [\"exclude-imports\"],\n    \"plugins\": [\"replace-browser-modules\"],\n    \".babelrc\": {\n      \"presets\": [\"liferay-standard\"]\n    },\n    \"post-plugins\": [\n      \"namespace-packages\",\n      \"inject-imports-dependencies\",\n      \"inject-peer-dependencies\"\n    ]\n  }\n}\n\\end{verbatim}\n\nThe configuration above states that for all npm packages (\\texttt{*})\nthe pre-process phase (\\texttt{plugins}) must run the\n\\texttt{replace-browser-modules} plugin. Setting this to\n\\texttt{post-plugins} would run it during the post phase instead.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can override configuration preset values by adding\nyour own configuration to your project's \\texttt{.npmbundlerrc} file.\nFor instance, using the configuration preset example above, you can\ndefine your own \\texttt{.babelrc} value in \\texttt{.npmbundlerrc} file\nto override the defined ``liferay-standard'' babelrc preset.\n\n\\noindent\\hrulefill\n\nThe\n\\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-preset-liferay-standard}{\\texttt{liferay-standard}\npreset} applies the following plugins to packages:\n\n\\begin{itemize}\n\\item\n  \\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-exclude-imports}{exclude-imports}:\n  Exclude packages declared in the \\texttt{imports} section from the\n  build.\n\\item\n  \\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-inject-imports-dependencies}{inject-imports-dependencies}:\n  Inject dependencies declared in the \\texttt{imports} section in the\n  dependencies' \\texttt{package.json} files.\n\\item\n  \\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-inject-peer-dependencies}{inject-peer-dependencies}:\n  Inject declared peer dependencies (as they are resolved in the\n  project's \\texttt{node\\_modules} folder) in the dependencies'\n  \\texttt{package.json} files.\n\\item\n  \\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-namespace-packages}{namespace-packages}:\n  Namespace package names based on the root project's package name to\n  isolate packages per project and avoid collisions. This prepends\n  \\texttt{\\textless{}project-package-name\\textgreater{}\\$} to each\n  package name appearance in \\texttt{package.json} files.\n\\item\n  \\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-replace-browser-modules}{replace-browser-modules}:\n  Replaces the server side files for modules listed under\n  \\texttt{browser}/\\texttt{unpkg}/\\texttt{jsdelivr} section of\n  \\texttt{package.json} with their browser counterparts.\n\\item\n  \\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-resolve-linked-dependencies}{resolve-linked-dependencies}:\n  Replace linked dependencies versions appearing in\n  \\texttt{package.json} files (those obtained from local file system or\n  GitHub, for example) by their real version number, as resolved in the\n  project's \\texttt{node\\_modules} directory.\n\\end{itemize}\n\nIn addition, the bundler runs Babel with the\n\\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-preset-liferay-standard}{babel-preset-liferay-standard}\npreset, that invokes the following plugins:\n\n\\begin{itemize}\n\\item\n  \\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-plugin-normalize-requires}{babel-plugin-normalize-requires}:\n  Normalize AMD \\texttt{require()} calls.\n\\item\n  \\href{https://github.com/babel/minify/tree/master/packages/babel-plugin-transform-node-env-inline}{babel-plugin-transform-node-env-inline}:\n  Inline the \\texttt{NODE\\_ENV} environment variable, and if it's part\n  of a binary expression (eg.\n  \\texttt{process.env.NODE\\_ENV\\ ===\\ \"development\"}), then statically\n  evaluate and replace it.\n\\item\n  \\href{https://www.npmjs.com/package/babel-plugin-minify-dead-code-elimination}{babel-plugin-minify-dead-code-elimination}:\n  Inline bindings when possible. Tries to evaluate expressions and\n  prunes unreachable as a result.\n\\item\n  \\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-plugin-wrap-modules-amd}{babel-plugin-wrap-modules-amd}:\n  Wrap modules inside an AMD \\texttt{define()} module.\n\\item\n  \\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-plugin-name-amd-modules}{babel-plugin-name-amd-modules}:\n  Name AMD modules based on package name, version, and module path.\n\\item\n  \\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-plugin-namespace-modules}{babel-plugin-namespace-modules}:\n  Namespace modules based on the root project's package name, prepending\n  \\texttt{\\textless{}project-package-name\\textgreater{}\\$}. Wrap modules\n  inside an AMD \\texttt{define()} module for each module name appearance\n  (in \\texttt{define()} or \\texttt{require()} calls) so that the\n  packages are localized per project and don't clash.\n\\item\n  \\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-plugin-namespace-amd-define}{babel-plugin-namespace-amd-define}:\n  Add a prefix to AMD \\texttt{define()} calls (by default\n  \\texttt{Liferay.Loader.}).\n\\end{itemize}\n\nNow you know the available configuration presets for\n\\texttt{.npmbundlerrc} and how they work.\n\n\\chapter{The Structure of OSGi Bundles Containing npm\nPackages}\\label{the-structure-of-osgi-bundles-containing-npm-packages}\n\nTo deploy JavaScript modules, you must create an OSGi bundle with the\nnpm dependencies extracted from the project's \\texttt{node\\_modules}\nfolder and modify them to work with the\n\\href{https://github.com/liferay/liferay-amd-loader}{Liferay AMD\nLoader}. The liferay-npm-bundler automates this process for you,\ncreating a bundle similar to the one below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-bundle/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{META-INF/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{resources/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{package.json}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          name: my-bundle-package\n        \\item\n          version: 1.0.0\n        \\item\n          main: lib/index\n        \\item\n          dependencies:\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            my-bundle-package\\$isarray: 2.0.0\n          \\item\n            my-bundle-package\\$isobject: 2.1.0\n          \\end{itemize}\n        \\item\n          \\ldots{}\n        \\end{itemize}\n      \\item\n        \\texttt{lib/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{index.js}\n        \\item\n          \\ldots{}\n        \\end{itemize}\n      \\item\n        \\ldots{}\n      \\item\n        \\texttt{node\\_modules/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{my-bundle-package\\$isobject@2.1.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: my-bundle-package\\$isobject\n            \\item\n              version: 2.1.0\n            \\item\n              main: lib/index\n            \\item\n              dependencies:\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                my-bundle-package\\$isarray: 1.0.0\n              \\end{itemize}\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\item\n          \\texttt{my-bundle-package\\$isarray@1.0.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: my-bundle-package\\$isarray\n            \\item\n              version: 1.0.0\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\item\n          \\texttt{my-bundle-package\\$isarray@2.0.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: my-bundle-package\\$isarray\n            \\item\n              version: 2.0.0\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\end{itemize}\n\nThe packages inside \\texttt{node\\_modules} are the same format as the\nnpm tool and can be copied (after a little processing for things like\nconverting to AMD, for example) from a standard \\texttt{node\\_modules}\nfolder. The \\texttt{node\\_modules} folder can hold any number of npm\npackages (even different versions of the same package), or no npm\npackages at all.\n\nNow that you know the structure for OSGi bundles containing npm\npackages, you can learn how the liferay-npm-bundler handles inline\nJavaScript packages.\n\n\\section{Inline JavaScript packages}\\label{inline-javascript-packages}\n\nThe resulting OSGi bundle that the liferay-npm-bundler creates lets you\ndeploy one inline JavaScript package (named \\texttt{my-bundle-package}\nin the example) with several npm packages that are placed inside the\n\\texttt{node\\_modules} folder, one package per folder.\n\nThe inline package is nested in the OSGi standard\n\\texttt{META-INF/resources} folder and is defined by a standard npm\n\\texttt{package.json} file.\n\nThe inline package is optional, but only one inline package is allowed\nper OSGi bundle. The inline package usually provides the JavaScript code\nfor a widget, when the OSGi bundle contains one. Note that the\narchitecture does not differentiate between inline and npm packages once\nthey are published. The inline package is only used for organizational\npurposes.\n\nNow you know how the liferay-npm-bundler creates OSGi bundles for npm\npackages!\n\n\\chapter{How the Liferay npm Bundler Publishes npm\nPackages}\\label{how-the-liferay-npm-bundler-publishes-npm-packages}\n\nWhen you deploy an OSGi bundle with the specified structure, as\nexplained in\n\\href{/docs/7-2/reference/-/knowledge_base/r/the-structure-of-osgi-bundles-containing-npm-packages}{The\nStructure of OSGi Bundles Containing NPM Packages} reference, its\nmodules are made available for consumption through canonical URLs. To\nbetter illustrate resolved modules, the example structure below is the\nstandard structure that the liferay-npm-bundler 1.x generates, and\ntherefore doesn't have the namespaced packages that the 2.x version\ngenerates. Please refer to the last sections of this article to know how\nliferay-npm-bundler 2.0 overrides this de-duplication mechanism to\nimplement isolated dependencies and imports.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-bundle/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{META-INF/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{resources/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{package.json}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          name: my-bundle-package\n        \\item\n          version: 1.0.0\n        \\item\n          main: lib/index\n        \\item\n          dependencies:\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            isarray: 2.0.0\n          \\item\n            isobject: 2.1.0\n          \\end{itemize}\n        \\item\n          \\ldots{}\n        \\end{itemize}\n      \\item\n        \\texttt{lib/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{index.js}\n        \\item\n          \\ldots{}\n        \\end{itemize}\n      \\item\n        \\ldots{}\n      \\item\n        \\texttt{node\\_modules/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{isobject@2.1.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: isobject\n            \\item\n              version: 2.1.0\n            \\item\n              main: lib/index\n            \\item\n              dependencies:\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                isarray: 1.0.0\n              \\end{itemize}\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\item\n          \\texttt{isarray@1.0.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: isarray\n            \\item\n              version: 1.0.0\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\item\n          \\texttt{isarray@2.0.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: isarray\n            \\item\n              version: 2.0.0\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\end{itemize}\n\nIf you deploy the example OSGi bundle shown above, the following URLs\nare made available (one for each module):\n\n\\begin{itemize}\n\\item\n  \\url{http://localhost/o/js/module/598/my-bundle-package@1.0.0/lib/index.js}\n\\item\n  \\url{http://localhost/o/js/module/598/isobject@2.1.0/index.js}\n\\item\n  \\url{http://localhost/o/js/module/598/isarray@1.0.0/index.js}\n\\item\n  \\url{http://localhost/o/js/module/598/isarray@2.0.0/index.js}\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{NOTE:} The OSGi bundle ID (598) may vary.\n\n\\noindent\\hrulefill\n\nYou can learn about package de-duplication next.\n\n\\section{Package De-duplication}\\label{package-de-duplication}\n\nSince two or more OSGi modules may export multiple copies of the same\npackage and version, Liferay Portal must de-duplicate such collisions.\nTo accomplish de-duplication, a new concept called \\emph{resolved\nmodule} was created.\n\nA resolved module is the reference package exported to Liferay Portal's\nfront-end, when multiple copies of the same package and version exist.\nIt's randomly referenced from one of the several bundles exporting the\nsame copies of the package.\n\nUsing the example from the previous section, for each group of canonical\nURLs referring to the same module inside different OSGi bundles, there's\nanother canonical URL for the resolved module. The example structure has\nthe resolved module URLs shown below:\n\n\\begin{itemize}\n\\item\n  \\url{http://localhost/o/js/resolved-module/my-bundle-package@1.0.0/lib/index.js}\n\\item\n  {[}http://localhost/o/js/resolved-module/my-bundle-package\\(isobject@2.1.0/index.js](http://localhost/o/js/resolved-module/my-bundle-package\\)isobject@2.1.0/index.js)\n\\item\n  {[}http://localhost/o/js/resolved-module/my-bundle-package\\(isarray@1.0.0/index.js](http://localhost/o/js/resolved-module/my-bundle-package\\)isarray@1.0.0/index.js)\n\\item\n  {[}http://localhost/o/js/resolved-module/my-bundle-package\\(isarray@2.0.0/index.js](http://localhost/o/js/resolved-module/my-bundle-package\\)isarray@2.0.0/index.js)\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{NOTE:} The OSGi bundle ID (598 in the example) is removed and\nmodule is replaced by \\texttt{resolved-module}.\n\n\\noindent\\hrulefill\n\nNext you can learn how the bundler (since version 2.0.0) isolates\npackage dependencies. See\n\\href{/docs/7-2/reference/-/knowledge_base/r/what-changed-between-liferay-npm-bundler-1-x-and-2-x}{What\nChanged Between liferay-npm-bundler 1.x and 2.x} for more information on\nwhy this change was made.\n\n\\section{Isolated Package\nDependencies}\\label{isolated-package-dependencies}\n\nA typical OSGi bundle structure generated with liferay-npm-bundler 2.x\nis shown below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-bundle/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{META-INF/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{resources/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{package.json}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          name: my-bundle-package\n        \\item\n          version: 1.0.0\n        \\item\n          main: lib/index\n        \\item\n          dependencies:\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            my-bundle-package\\$isarray: 2.0.0\n          \\item\n            my-bundle-package\\$isobject: 2.1.0\n          \\end{itemize}\n        \\item\n          \\ldots{}\n        \\end{itemize}\n      \\item\n        \\texttt{lib/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{index.js}\n        \\item\n          \\ldots{}\n        \\end{itemize}\n      \\item\n        \\ldots{}\n      \\item\n        \\texttt{node\\_modules/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{my-bundle-package\\$isobject@2.1.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: my-bundle-package\\$isobject\n            \\item\n              version: 2.1.0\n            \\item\n              main: lib/index\n            \\item\n              dependencies:\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                my-bundle-package\\$isarray: 1.0.0\n              \\end{itemize}\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\item\n          \\texttt{my-bundle-package\\$isarray@1.0.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: my-bundle-package\\$isarray\n            \\item\n              version: 1.0.0\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\item\n          \\texttt{my-bundle-package\\$isarray@2.0.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: my-bundle-package\\$isarray\n            \\item\n              version: 2.0.0\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\end{itemize}\n\nNote that each package dependency is namespaced with the bundle's name\n(\\texttt{my-bundle-package\\$} in the example structure). This lets each\nproject load its own dependencies and avoid potential collisions with\nprojects that export the same package. For example, consider the two\nwidget projects below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-widget}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    package.json\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      dependencies:\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        a-library 1.0.0\n      \\item\n        a-helper 1.0.0\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    node\\_modules\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      a-library\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        version: 1.0.0\n      \\item\n        dependencies:\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          a-helper \\^{}1.0.0\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      a-helper\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        version: 1.0.0\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  \\texttt{another-widget}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    package.json\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      dependencies:\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        a-library 1.0.0\n      \\item\n        a-helper 1.2.0\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    node\\_modules\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      a-library\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        version: 1.0.0\n      \\item\n        dependencies:\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          a-helper \\^{}1.0.0\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      a-helper\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        version: 1.2.0\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\end{itemize}\n\nIn this example, \\texttt{a-library} depends on \\texttt{a-helper} at\nversion 1.0.0 or higher (note the caret \\^{} expression in the\ndependencies). The bundler implements isolated dependencies by prefixing\nthe name of the bundle to the modules, so that \\texttt{my-widget} gets\nits \\texttt{a-helper} at 1.0.0, while \\texttt{another-widget} gets its\n\\texttt{a-helper} at 1.2.0.\n\nThe dependencies isolation not only avoids collisions between bundles,\nbut also makes peer dependencies behave deterministically as each widget\ngets what it had in its \\texttt{node\\_modules} folder when it was\ndeveloped.\n\nNow that you understand how namespacing modules isolates bundle\ndependencies, avoiding collisions, you can learn about de-duplication\nnext.\n\n\\section{De-duplication through\nImporting}\\label{de-duplication-through-importing}\n\nIsolated dependencies are very useful, but there are times when sharing\nthe same package between modules would be more beneficial. To do this,\nthe liferay-npm-bundler lets you import packages from an external OSGi\nbundle, instead of using your own. This lets you put shared dependencies\nin one project and reference them from the rest.\n\nImagine that you have three widgets that compose the homepage of your\nsite: \\texttt{my-toolbar}, \\texttt{my-menu}, and \\texttt{my-content}.\nThese widgets depend on the fake, but awesome, Wonderful UI Components\n(WUI) framework. This quite limited framework is composed of only three\npackages:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  \\texttt{component-core}\n\\item\n  \\texttt{button}\n\\item\n  \\texttt{textfield}\n\\end{enumerate}\n\nSince the bundler namespaces each dependency package with the widget's\nname by default, you would end up with three namespaced copies of the\nWUI package on the page. This is not what you want. Since they share the\nsame package, instead you can create a fourth bundle that contains the\nWUI package, and import the WUI package in the three widgets. This\nresults in the structure below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-toolbar/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{.npmbundlerrc}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      config:\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        imports:\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          wui-provider:\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            component-core: \\^{}1.0.0\n          \\item\n            button: \\^{}1.0.0\n          \\item\n            textfield: \\^{}1.0.0\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  \\texttt{my-menu/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{.npmbundlerrc}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      config:\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        imports:\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          wui-provider:\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            component-core: \\^{}1.0.0\n          \\item\n            button: \\^{}1.0.0\n          \\item\n            textfield: \\^{}1.0.0\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  \\texttt{my-content/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{.npmbundlerrc}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      config:\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        imports:\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          wui-provider:\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            component-core: \\^{}1.0.0\n          \\item\n            button: \\^{}1.0.0\n          \\item\n            textfield: \\^{}1.0.0\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  \\texttt{wui-provider/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{.package.json}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      name: wui-provider\n    \\item\n      dependencies:\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        component-core: 1.0.0\n      \\item\n        button: 1.0.0\n      \\item\n        textfield: 1.0.0\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\end{itemize}\n\nThe bundler switches the namespace of certain packages, thus pointing\nthem to an external bundle. Say that you have the following code in\n\\texttt{my-toolbar} widget:\n\n\\begin{verbatim}\nvar Button = require('button');\n\\end{verbatim}\n\nBy default, the bundler 2.x transforms this into the following when not\nimported from another bundle:\n\n\\begin{verbatim}\nvar Button = require('my-toolbar$button');\n\\end{verbatim}\n\nBut, because \\texttt{button} is imported from \\texttt{wui-provider}, it\nis instead changed to the value below:\n\n\\begin{verbatim}\nvar Button = require('wui-provider$button');\n\\end{verbatim}\n\nAlso, a dependency on \\texttt{wui-provider\\$button} at version\n\\texttt{\\^{}1.0.0} is included in \\texttt{my-toolbar}'s\n\\texttt{package.json} file so that the loader finds the correct version.\nThat's all you need. Once \\texttt{wui-provider\\$button} is required at\nruntime, it jumps to \\texttt{wui-provider}'s context and loads the\nsubdependencies from there on, even if code is executed from\n\\texttt{my-toolbar}. This works because, as you can imagine,\n\\texttt{wui-provider}'s modules are namespaced too, and once you load a\nmodule from it, it keeps requiring \\texttt{wui-provider\\$} prefixed\nmodules all the way down.\n\nNext, you will learn possible strategies for importing.\n\n\\section{Strategies When Importing\nPackages}\\label{strategies-when-importing-packages}\n\nDe-duplication by importing is a powerful tool, but you must design a\nversioning strategy suitable for you so that you don't run into errors.\n\nFirst of all, you must decide if you want to declare imported\ndependencies only in the \\texttt{.npmbundlerrc} file or in the\n\\texttt{package.json} too. Listing an imported dependency in\n\\texttt{.npmbundlerrc} is enough, even if it isn't present in your\n\\texttt{node\\_modules} folder because during runtime the loader will\nfind it. Listing an imported dependency in \\texttt{.npmbundlerrc} is\nenough, even if it isn't present in your \\texttt{node\\_modules} folder,\nbecause during runtime the loader finds it. If you have previous\nexperience with dynamic linking support in standard operating systems,\nthink of it as a DLL or shared object.\n\nYou may need to install your dependencies in \\texttt{node\\_modules} too\nif you use them for tests, or if they contain types needed to compile\n(like in Typescript), etc. If that is the case, then you can place them\nin the \\texttt{dependencies} or \\texttt{devDependencies} section of your\n\\texttt{package.json}. If you list them under the latter, they are\nautomatically excluded from the output bundle by the\nliferay-npm-bundler. Otherwise, you need to exclude them in the\n\\texttt{.npmbundlerrc} file so they don't redundantly appear in the\noutput.\n\nIf you list dependencies both in \\texttt{package.json} and\n\\texttt{.npmbundlerrc}, decide how to keep versions in sync. The best\nadvice is to use the same version constraints in both files, but you may\ndecide not to do so if it is necessary. For example, imagine that you\nimport one of your dependencies from another bundle during runtime to\nrun tests. Say you are using version constraint \\^{}1.5.1. It would be\ndesirable that if you have tested your code with a version\n\\textgreater=1.5.1 and \\textless2.0.0 (that's what \\^{}1.5.1 means), you\nget a compatible version during runtime. Thus, you would declare the\ndependency with \\^{}1.5.1 in \\texttt{.npmbundlerrc} too.\n\nHowever, there are times when you may want to be more lenient, and you\nmay need to get a lower version (1.4.0 for example) during runtime, even\nif you are developing against \\^{}1.5.1. In that case, you can declare\n\\^{}1.5.1 in your \\texttt{package.json} and \\^{}1.0.0 in\n\\texttt{.npmbundlerrc}.\n\nIn the end, it's up to you to decide how you want to handle your\ndependencies:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\texttt{package.json} (While developing)\n\\item\n  \\texttt{.npmbundlerrc} (During runtime)\n\\end{enumerate}\n\nwe recommend that you choose a versioning strategy and stick to it, to\nensure dependencies are satisfied at runtime.\n\n\\chapter{Understanding How liferay-npm-bundler Formats JavaScript\nModules for\nAMD}\\label{understanding-how-liferay-npm-bundler-formats-javascript-modules-for-amd}\n\nLiferay AMD Loader is based on the\n\\href{https://github.com/amdjs/amdjs-api/wiki/AMD}{AMD specification}.\nAll modules inside an npm OSGi bundle must be in AMD format. This is\ndone for \\href{http://www.commonjs.org/}{CommonJS} modules by wrapping\nthe module code inside a \\texttt{define} call. The liferay-npm-bundler\nhelps automate this process by wrapping the module for you. This article\nreferences the OSGi structure below as an example. You can learn more\nabout this structure in\n\\href{/docs/7-2/reference/-/knowledge_base/r/the-structure-of-osgi-bundles-containing-npm-packages}{The\nStructure of OSGi Bundles Containing NPM Packages} reference.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-bundle/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{META-INF/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{resources/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{package.json}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          name: my-bundle-package\n        \\item\n          version: 1.0.0\n        \\item\n          main: lib/index\n        \\item\n          dependencies:\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            my-bundle-package\\$isarray: 2.0.0\n          \\item\n            my-bundle-package\\$isobject: 2.1.0\n          \\end{itemize}\n        \\item\n          \\ldots{}\n        \\end{itemize}\n      \\item\n        \\texttt{lib/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{index.js}\n        \\item\n          \\ldots{}\n        \\end{itemize}\n      \\item\n        \\ldots{}\n      \\item\n        \\texttt{node\\_modules/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{my-bundle-package\\$isobject@2.1.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: my-bundle-package\\$isobject\n            \\item\n              version: 2.1.0\n            \\item\n              main: lib/index\n            \\item\n              dependencies:\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                my-bundle-package\\$isarray: 1.0.0\n              \\end{itemize}\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\item\n          \\texttt{my-bundle-package\\$isarray@1.0.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: my-bundle-package\\$isarray\n            \\item\n              version: 1.0.0\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\item\n          \\texttt{my-bundle-package\\$isarray@2.0.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: my-bundle-package\\$isarray\n            \\item\n              version: 2.0.0\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\end{itemize}\n\nFor example, the \\texttt{my-bundle-package\\$isobject@2.1.0} package's\n\\texttt{index.js} file contains the following code:\n\n\\begin{verbatim}\n'use strict';\n\nvar isArray = require('my-bundle-package$isarray');\n\nmodule.exports = function isObject(val) {\n    return val != null && typeof val === 'object' && isArray(val) === false;\n};\n\\end{verbatim}\n\nThe updated module code configured for AMD format is shown below:\n\n\\begin{verbatim}\ndefine(\n    'my-bundle-package$isobject@2.1.0/index', \n    ['module', 'require', 'my-bundle-package$isarray'], \n    function (module, require) {\n        'use strict';\n\n        var define = undefined;\n\n        var isArray = require('my-bundle-package$isarray');\n\n        module.exports = function isObject(val) {\n            return val != null && typeof val === 'object' \n            && isArray(val) === false;\n        };\n    }\n);\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The module's name must be based on its package, version,\nand file path (for example\n\\texttt{my-bundle-package\\$isobject@2.1.0/index}), otherwise Liferay AMD\nLoader can't find it.\n\n\\noindent\\hrulefill\n\nNote the module's dependencies:\n\\texttt{{[}\\textquotesingle{}module\\textquotesingle{},\\ \\textquotesingle{}require\\textquotesingle{},\\ \\textquotesingle{}my-bundle-package\\$isarray\\textquotesingle{}{]}}.\n\n\\texttt{module} and \\texttt{require} must be used to get a reference to\nthe \\texttt{module.exports} object and the local \\texttt{require}\nfunction, as defined in the AMD specification.\n\nThe subsequent dependencies state the modules on which this module\ndepends. Note that \\texttt{my-bundle-package\\$isarray} in the example is\nnot a package but rather an alias of the\n\\texttt{my-bundle-package\\$isarray} package's main module (thus, it is\nequivalent to \\texttt{my-bundle-package\\$isarray/index}).\n\nAlso note that there is enough information in the \\texttt{package.json}\nfiles to know that \\texttt{my-bundle-package\\$isarray} refers to\n\\texttt{my-bundle-package\\$isarray/index}, but also that it must be\nresolved to version \\texttt{1.0.0} of such package, i.e., that\n\\texttt{my-bundle-package\\$isarray/index} in this case refers to\n\\texttt{my-bundle-package\\$isarray@1.0.0/index}.\n\nYou may also have noted the \\texttt{var\\ define\\ =\\ undefined;} addition\nto the top of the file. This is introduced by\n\\texttt{liferay-npm-bundler} to make the module think that it is inside\na CommonJS environment (instead of an AMD one). This is because some npm\npackages are written in UMD format and, because we are wrapping it\ninside our AMD \\texttt{define()} call, we don't want them to execute\ntheir own \\texttt{define()} but prefer them to take the CommonJS path,\nwhere the exports are done through the \\texttt{module.exports} global.\n\nNow you have a better understanding of how liferay-npm-bundler formats\nJavaScript modules for AMD!\n\n\\chapter{Understanding How Liferay AMD Loader Configuration is\nExported}\\label{understanding-how-liferay-amd-loader-configuration-is-exported}\n\n\\noindent\\hrulefill\n\n\\textbf{NOTE:} This article is for users who know how Liferay AMD Loader\nworks under the hood. See\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/loading-amd-modules-in-liferay}{Liferay\nAMD Module Loader} for more information.\n\n\\noindent\\hrulefill\n\nWith\n\\href{/docs/7-2/reference/-/knowledge_base/r/how-the-liferay-npm-bundler-publishes-npm-packages\\#package-de-duplication}{de-duplication}\nin place, JavaScript modules are made available to Liferay AMD Loader\nthrough the configuration returned by the\n\\texttt{/o/js\\_loader\\_modules} URL.\n\nThe OSGi bundle shown below is used for reference in this article:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-bundle/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{META-INF/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{resources/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{package.json}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          name: my-bundle-package\n        \\item\n          version: 1.0.0\n        \\item\n          main: lib/index\n        \\item\n          dependencies:\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            isarray: 2.0.0\n          \\item\n            isobject: 2.1.0\n          \\end{itemize}\n        \\item\n          \\ldots{}\n        \\end{itemize}\n      \\item\n        \\texttt{lib/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{index.js}\n        \\item\n          \\ldots{}\n        \\end{itemize}\n      \\item\n        \\ldots{}\n      \\item\n        \\texttt{node\\_modules/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{isobject@2.1.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: isobject\n            \\item\n              version: 2.1.0\n            \\item\n              main: lib/index\n            \\item\n              dependencies:\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                isarray: 1.0.0\n              \\end{itemize}\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\item\n          \\texttt{isarray@1.0.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: isarray\n            \\item\n              version: 1.0.0\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\item\n          \\texttt{isarray@2.0.0/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{package.json}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              name: isarray\n            \\item\n              version: 2.0.0\n            \\item\n              \\ldots{}\n            \\end{itemize}\n          \\item\n            \\ldots{}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\end{itemize}\n\nFor example, for the specified structure (shown above), as explained in\n\\href{/docs/7-2/reference/-/knowledge_base/r/the-structure-of-osgi-bundles-containing-npm-packages}{The\nStructure of OSGi Bundles Containing npm Packages} reference, the\nfollowing configuration is published for Liferay AMD loader to consume:\n\n\\begin{verbatim}\nLiferay.PATHS = {\n  ...\n  'my-bundle-package@1.0.0/lib/index': '/o/js/resolved-module/my-bundle-package@1.0.0/lib/index',\n  'isobject@2.1.0/index': '/o/js/resolved-module/isobject@2.1.0/index',\n  'isarray@1.0.0/index': '/o/js/resolved-module/isarray@1.0.0/index',\n  'isarray@2.0.0/index': '/o/js/resolved-module/isarray@2.0.0/index',\n  ...\n}\nLiferay.MODULES = {\n  ...\n  \"my-bundle-package@1.0.0/lib/index.es\": {\n    \"dependencies\": [\"exports\", \"isarray\", \"isobject\"],\n    \"map\": {\n      \"isarray\": \"isarray@2.0.0\", \n      \"isobject\": \"isobject@2.1.0\"\n    }\n  },\n  \"isobject@2.1.0/index\": {\n    \"dependencies\": [\"module\", \"require\", \"isarray\"],\n    \"map\": {\n      \"isarray\": \"isarray@1.0.0\"\n    }\n  },\n  \"isarray@1.0.0/index\": {\n    \"dependencies\": [\"module\", \"require\"],\n    \"map\": {}\n  },\n  \"isarray@2.0.0/index\": {\n    \"dependencies\": [\"module\", \"require\"],\n    \"map\": {}\n  },\n  ...\n}\nLiferay.MAPS = {\n  ...\n  'my-bundle-package@1.0.0': { value: 'my-bundle-package@1.0.0/lib/index', exactMatch: true}\n  'isobject@2.1.0': { value: 'isobject@2.1.0/index', exactMatch: true},\n  'isarray@2.0.0': { value: 'isarray@2.0.0/index', exactMatch: true},\n  'isarray@1.0.0': { value: 'isarray@1.0.0/index', exactMatch: true},\n  ...\n}\n\\end{verbatim}\n\nNote:\n\n\\begin{itemize}\n\\item\n  The \\texttt{Liferay.PATHS} property describes paths to the JavaScript\n  module files.\n\\item\n  The \\texttt{Liferay.MODULES} property describes the dependency names\n  and versions of each module.\n\\item\n  The \\texttt{Liferay.MAPS} property describes the aliases of the\n  package's main modules.\n\\end{itemize}\n\n\\chapter{What Changed Between Liferay npm Bundler 1.x and\n2.x}\\label{what-changed-between-liferay-npm-bundler-1.x-and-2.x}\n\nThis reference doc outlines the key changes between liferay-npm-bundler\nversion 1.x and 2.x.\n\n\\section{Automatically Formatting Modules for\nAMD}\\label{automatically-formatting-modules-for-amd}\n\nIn version series 1.x of the bundler it was the developer's\nresponsibility to wrap project modules in an AMD \\texttt{define()} call.\nHowever, since 2.x the bundler does it for you, so the only requisite is\nthat the project's code is transpiled/written for CommonJS modules model\n(the standard model for module handling in Node.js, that uses\n\\texttt{require()} calls to load modules).\n\n\\section{Isolating Project\nDependencies}\\label{isolating-project-dependencies}\n\nPackage names are prefixed with the bundle name since version 2.0.0 of\nthe bundler, but were left intact in previous versions. This strategy is\nused to isolate packages from different bundles. You can still deploy\nbundler 1.x packages (without prefix), and they will still work as they\ndid for previous versions of the bundler.\n\n\\section{Improved Peer Dependency\nSupport}\\label{improved-peer-dependency-support}\n\nIn bundler 1.x, there was only one shared peer dependency package\navailable between widgets. With isolated dependencies per widget, it's\neasy to honor peer dependencies perfectly. Peer dependencies can be\nresolved exactly as stated in projects because their names are prefixed\nwith the project's name. This is possible because of the new\n\\href{https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-inject-peer-dependencies}{liferay-npm-bundler-plugin-inject-peer-dependencies}\nplugin. It scans all JS modules for \\texttt{require} calls. If the\nbundler finds a required package in the \\texttt{main.js} file, but it is\nnot declared in the \\texttt{package.json}, it resolves it to the proper\nversion that is found in the \\texttt{node\\_modules} folder. The plugin\nthen injects a new dependency in the output \\texttt{package.json} for\nthe required package.\n\nNote that injected dependency version constraints are the specific\nversion number required, without caret or any other semantic version\noperator. This is to honor the exact peer dependency found in the\nproject. Injecting more relaxed semantic version expressions could lead\nto unstable results.\n\n\\section{Manually De-duplicating Through\nImporting}\\label{manually-de-duplicating-through-importing}\n\nNamespacing means that each widget gets its own dependencies. Only using\nthe bundler this way obtains the same functionality as standard bundlers\nlike webpack or Browserify, so you wouldn't need a specific tool like\nliferay-npm-bundler. Since Liferay DXP is a widget based architecture,\nsharing dependencies among different widgets would be very beneficial.\n\nIn bundler 1.x that deduplication was made automatically, but there was\nno control over it. However, with version 2.x, you may now import\npackages from an external OSGi bundle, instead of using your own. This\nlets you put shared dependencies in one project, and reference them from\nthe rest. Though This new way of de-duplication is not automatic, it\nleads to full control (during build time) of how each package is\nresolved.\n\nNow that you understand what changed between version 1.x and 2.x of the\nliferay-npm-bundler, you can follow the steps in the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/migrating-a-liferay-npm-bundler-project-from-1-x-to-2-x}{Migrating\na liferay-npm-bundler Project from 1.x to 2.x} to migrate your 1.x\nprojects to 2.x.\n\n\\chapter{Understanding liferay-npm-bundler's\nLoaders}\\label{understanding-liferay-npm-bundlers-loaders}\n\nliferay-npm-bundler's mechanism is inspired by webpack. Like webpack,\nthe liferay-npm-bundler processes files using a set of rules that\ninclude loaders that transform a project's source files before producing\nthe final output.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} While webpack creates a single JS bundle file,\nliferay-npm-bundler targets an AMD loader, so webpack and\nliferay-npm-bundler loaders are not compatible.\n\n\\noindent\\hrulefill\n\nLoaders are npm packages that export a function in their main module\nthat receives source files and returns modified files, and optionally\nnew files, based on the loader's configuration. For example, the\n\\href{https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-babel-loader}{babel-loader}\nreceives ES6+ JavaScript files, runs Babel on them, and returns\ntranspiled ES5 files along with a generated source map. You can use this\npattern to\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-loaders-for-the-liferay-npm-bundler}{create\ncustom loaders}. A few example loader functions are shown below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Pass JavaScript files through Babel or TSC\n\\item\n  Convert CSS files into JS modules that dynamically inject the CSS into\n  the HTML page\n\\item\n  Process CSS files with SASS\n\\item\n  Create tools that generate code based on\n  \\href{https://en.wikipedia.org/wiki/Interface_description_language}{IDL}\n  files\n\\end{itemize}\n\nLoaders are configured via the project's \\texttt{.npmbundlerrc} file. A\nloader's configuration is specified using two key options:\n\\texttt{sources} (the folders that contain the sources files to process)\nand \\texttt{rules} (the loaders, options---if applicable---and regular\nexpressions that determine which files to process). See\n\\href{/docs/7-2/reference/-/knowledge_base/r/understanding-the-npmbundlerrcs-structure\\#package-processing-options}{Understanding\nthe \\texttt{.npmbundlerrc}'s Structure} for more information on the\nconfiguration requirements and options.\n\nLoaders can be chained. Files are processed by the loaders in the order\nthey are listed in the \\texttt{use} property. The files are passed to\nthe first loader, processed, sent to the next loader, and so on, until\nthe files are processed by the rules. You can run complex processes,\nsuch as converting a SASS file into CSS with the sass-loader, and then\nconvert it into a JavaScript module with the style-loader. Once the\nrules are applied, the liferay-npm-bundler continues with the pre, post,\nand babel phases of the bundler plugins.\n\n\\chapter{Default liferay-npm-bundler\nLoaders}\\label{default-liferay-npm-bundler-loaders}\n\nSeveral\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/understanding-liferay-npm-bundlers-loaders}{loaders}\nare available for the liferay-npm-bundler by default:\n\n\\href{https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-babel-loader}{\\texttt{babel-loader}}:\nprocesses source files with \\href{https://babeljs.io/}{Babel}. This\navoids an extra build step before the bundler.\n\n\\href{https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-copy-loader}{\\texttt{copy-loader}}:\ncopies source files (static assets) to the output folder.\n\n\\href{https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-css-loader}{\\texttt{css-loader}}:\nconverts a CSS file into a JavaScript module that's inserted into the\nDOM once it's loaded.\n\n\\href{https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-json-loader}{\\texttt{json-loader}}:\ngenerates JavaScript modules that export the contents of a JSON file as\nan object, so you can include JSON files with the \\texttt{require()}\ncall.\n\n\\href{https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-sass-loader}{\\texttt{sass-loader}}:\nruns \\texttt{node-sass} or \\texttt{sass} on source files. This lets you\ngenerate static CSS files. It can be chained before\n\\texttt{style-loader}.\n\n\\href{https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-style-loader}{\\texttt{style-loader}}:\nconverts a CSS file into a JavaScript module that inserts the CSS\ncontents into the DOM once it's loaded. This lets you include CSS files\nwith a \\texttt{require()} call.\n\nSee the\n\\href{https://github.com/izaera/liferay-js-toolkit-showcase/tree/loaders}{liferay-js-toolkit\nloaders showcase} for an example use case of the liferay-npm-bundler's\nloaders. If the default loaders don't meet your requirements, you can\nfollow the instructions in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-loaders-for-the-liferay-npm-bundler}{Creating\nCustom Loaders for the Bundler} to create your own loaders.\n\n\\chapter{Liferay JavaScript APIs}\\label{liferay-javascript-apis}\n\nThe \\texttt{Liferay} JavaScript object exposes methods, objects, and\nproperties that you can use to access Liferay DXP-specific information.\nThis section contains a comprehensive list of some of the most useful\nutilities you can find inside the \\texttt{Liferay} object.\n\n\\chapter{Accessing ThemeDisplay\nInformation}\\label{accessing-themedisplay-information}\n\nThe \\texttt{Liferay} global JavaScript Object exposes useful methods,\nobjects, and properties, each containing a wealth of information, one of\nwhich is \\texttt{ThemeDisplay}. If you have experience with Java\ndevelopment in Liferay DXP, you may be familiar with ThemeDisplay. The\nJavaScript object exposes the same information as the ThemeDisplay Java\nClass. It gives you access to valuable information that you can use in\nyour applications, such as the Portal instance, the current user, the\nuser's language, whether the user is signed in or being impersonated,\nthe file path to the theme's resources, and much more.\n\nThe \\texttt{Liferay} global object is automatically available in Liferay\nDXP at runtime. To access the \\texttt{ThemeDisplay} object, use the\nfollowing dot notation in your app:\n\n\\begin{verbatim}\nLiferay.ThemeDisplay.method-name\n\\end{verbatim}\n\nThis reference describes some of the most commonly used\n\\texttt{ThemeDisplay} methods for retrieving IDs, file paths, and login\ninformation. An exhaustive list of all of the available methods is\ndisplayed in the table below:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nMethod\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nType\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\ngetLayoutId & number & \\\\\ngetLayoutRelativeURL & string & Returns the relative URL for the page \\\\\ngetLayoutURL & string & \\\\\ngetParentLayoutId & number & \\\\\nisControlPanel & boolean & \\\\\nisPrivateLayout & boolean & \\\\\nisVirtualLayout & boolean & \\\\\ngetBCP47LanguageId & number & \\\\\ngetCDNBaseURL & string & Returns the content delivery network (CDN) base\nURL, or the current portal URL if the CDN base URL is null \\\\\ngetCDNDynamicResourcesHost & string & Returns the content delivery\nnetwork (CDN) dynamic resources host, or the current portal URL if the\nCDN dynamic resources host is null \\\\\ngetCDNHost & string & \\\\\ngetCompanyGroupId & number & \\\\\ngetCompanyId & number & Returns the portal instance ID \\\\\ngetDefaultLanguageId & number & \\\\\ngetDoAsUserIdEncoded & string & \\\\\ngetLanguageId & number & Returns the user's language ID \\\\\ngetParentGroupId & number & \\\\\ngetPathContext & string & \\\\\ngetPathImage & string & Returns the relative path of the portlet's image\ndirectory \\\\\ngetPathJavaScript & string & Returns the relative path of the directory\ncontaining the portlet's JavaScript source files \\\\\ngetPathMain & string & Returns the path of the portal instance's main\ndirectory \\\\\ngetPathThemeImages & string & Returns the path of the current theme's\nimage directory \\\\\ngetPathThemeRoot & string & Returns the relative path of the current\ntheme's root directory \\\\\ngetPlid & string & Returns the primary key of the page \\\\\ngetPortalURL & string & Returns the portal instance's base URL \\\\\ngetScopeGroupId & number & Returns the ID of the scoped or sub-scoped\nactive group (e.g.~site) \\\\\ngetScopeGroupIdOrLiveGroupId & number & \\\\\ngetSessionId & number & Returns the session ID, or a blank string if the\nsession ID is not available to the application \\\\\ngetSiteGroupId & number & \\\\\ngetURLControlPanel & string & \\\\\ngetURLHome & string & \\\\\ngetUserId & number & Returns the ID of the user for which the current\nrequest is being handled \\\\\ngetUserName & string & Returns the user's name \\\\\nisAddSessionIdToURL & boolean & \\\\\nisFreeformLayout & boolean & \\\\\nisImpersonated & boolean & Returns \\texttt{true} if the current user is\nbeing impersonated. Authorized administrative users can\n\\href{/docs/7-2/user/-/knowledge_base/u/adding-editing-and-deleting-users\\#editing-users}{impersonate}\nact as another user to test that user's account \\\\\nisSignedIn & boolean & Returns \\texttt{true} if the user is logged in to\nthe portal \\\\\nisStateExclusive & boolean & \\\\\nisStateMaximized & boolean & \\\\\nisStatePopUp & boolean & \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nThe example configuration below alerts users with a standard message if\nthey are a guest or a personal greeting if they are signed in. This is a\nbasic example, and perhaps a bit invasive, but it illustrates how you\ncan create unique experiences for each user with the\n\\texttt{ThemeDisplay} APIs:\n\n\\begin{verbatim}\nif(Liferay.ThemeDisplay.isSignedIn()){\n    alert('Hello ' + Liferay.ThemeDisplay.getUserName() + '. Welcome Back.')\n}\nelse {\n    alert('Hello Guest.')\n}\n\\end{verbatim}\n\n\\chapter{Working with URLs in\nJavaScript}\\label{working-with-urls-in-javascript}\n\nThe \\texttt{Liferay} global JavaScript Object exposes methods, objects,\nand properties that access the portal context. Four of these are helpful\nwhen working with URLS: \\texttt{authToken}, \\texttt{currentURL},\n\\texttt{currentURLEncoded}, and \\texttt{PortletURL}. If you have\nexperience with Java development in Liferay DXP, you may have worked\nwith some of these before. The \\texttt{Liferay} global object is\nautomatically available at runtime, so no additional dependencies are\nrequired.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Since Liferay DXP SP1 and Liferay Portal CE 7.2 GA2, the\n\\texttt{Liferay.PortletURL} utilities are deprecated and have been\nreplaced with \\texttt{Liferay.Util.PortletURL} utilities. We recommend\nthat you use the updated versions to ensure future compatibility. The\nexamples below use the updated utilities.\n\n\\noindent\\hrulefill\n\nThis covers how to use the \\texttt{Liferay} global JavaScript object to\nmanipulate URLs. A list of the available methods and properties appears\nin the tables shown below. Example configurations are shown below the\ntables.\n\n\\section{Portlet URL Methods}\\label{portlet-url-methods}\n\n\\texttt{Liferay.Util.PortletURL} Methods:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nMethod\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nParameters\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nReturns\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{createPortletURL} & \\texttt{basePortletURL}, \\texttt{parameters}\n& A portlet URL as a \\href{https://url.spec.whatwg.org/\\#api}{URL}\nobject \\\\\n\\texttt{createActionURL} & \\texttt{basePortletURL}, \\texttt{parameters}\n& A portlet URL as a \\href{https://url.spec.whatwg.org/\\#api}{URL}\nobject \\\\\n\\texttt{createRenderURL} & \\texttt{basePortletURL}, \\texttt{parameters}\n& A portlet URL as a \\href{https://url.spec.whatwg.org/\\#api}{URL}\nobject \\\\\n\\texttt{createResourceURL} & \\texttt{basePortletURL},\n\\texttt{parameters} & A portlet URL as a\n\\href{https://url.spec.whatwg.org/\\#api}{URL} object \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Liferay Util PortletURL}\\label{liferay-util-portleturl}\n\n\\texttt{Liferay.Util.PortletURL} provides APIs for creating portlet URLs\n(\\texttt{actionURL}, \\texttt{renderURL}, and \\texttt{resourceURL}) with\nJavaScript in your JSPs. Below is an example configuration for a JSP:\n\n\\begin{verbatim}\nvar basePortletURL = 'https://localhost:8080/group/control_panel/manage?p_p_id=com_liferay_roles_admin_web_portlet_RolesAdminPortlet';\n\nvar actionURL = Liferay.Util.PortletURL.createActionURL(\n  basePortletURL,\n  {\n    'javax.portlet.action': 'addUser',\n    foo: 'bar'\n  }  \n);\n\nconsole.log(actionURL.toString());\n// https://localhost:8080/group/control_panel/manage?p_p_id=com_liferay_roles_admin_web_portlet_RolesAdminPortlet&javax.portlet.action=addUser&com_liferay_roles_admin_web_portlet_RolesAdminPortlet_foo=bar&p_p_lifecycle=1\n\\end{verbatim}\n\nThe same API is available as a module for use in your JavaScript files.\nThe ES6 example below uses the \\texttt{createActionURL} module:\n\n\\begin{verbatim}\nimport {createActionURL} from 'frontend-js-web';\n\nvar basePortletURL = 'https://localhost:8080/group/control_panel/manage?p_p_id=com_liferay_roles_admin_web_portlet_RolesAdminPortlet';\n\nvar actionURL = createActionURL(\n  basePortletURL,\n  {\n    'p_p_id': Liferay.PortletKeys.DOCUMENT_LIBRARY,\n    foo: 'bar'\n  }  \n);\n\\end{verbatim}\n\nSee the \\hyperref[portlet-url-methods]{Portlet URL Methods} section for\nmore information about the method used in the example above.\n\n\\section{Liferay AuthToken}\\label{liferay-authtoken}\n\nThe \\texttt{Liferay.authToken} property holds the current authentication\ntoken value as a String. The \\texttt{authToken} is used to validate\npermissions when you make calls to services. To use the\n\\texttt{authToken} in a URL, pass \\texttt{Liferay.authToken} as the\nURL's \\texttt{p\\_auth} parameter, as shown in the example below:\n\n\\begin{verbatim}\nimport {createActionURL} from 'frontend-js-web';\n\nvar basePortletURL = 'https://localhost:8080/group/control_panel/manage?p_p_id=com_liferay_roles_admin_web_portlet_RolesAdminPortlet';\n\nvar actionURL = createActionURL(\n  basePortletURL,\n  {\n    'p_auth': Liferay.authToken\n  }  \n);\n\\end{verbatim}\n\n\\section{Liferay CurrentURL}\\label{liferay-currenturl}\n\nThe \\texttt{Liferay.currentURL} property holds the path of the current\nURL from the server root.\n\nFor example, if checked from \\texttt{my.domain.com/es/web/guest/home},\nthe value is \\texttt{/es/web/guest/home}, as shown below:\n\n\\begin{verbatim}\n// Inside my.domain.com/es/web/guest/home\nconsole.log(Liferay.currentURL); // \"/es/web/guest/home\"\n\\end{verbatim}\n\n\\section{Liferay CurrentURLEncoded}\\label{liferay-currenturlencoded}\n\nThe \\texttt{Liferay.currentURLEncoded} property holds the path of the\ncurrent URL, encoded in ASCII for safe transmission over the Internet,\nfrom the server root.\n\nFor example, if checked from \\texttt{my.domain.com/es/web/guest/home},\nthe value is \\texttt{\\%2Fes\\%2Fweb\\%2Fguest\\%2Fhome}, as shown below:\n\n\\begin{verbatim}\n// Inside my.domain.com/es/web/guest/home\nconsole.log(Liferay.currentURLEncoded); // \"%2Fes%2Fweb%2Fguest%2Fhome\"\n\\end{verbatim}\n\nNow you know how to manipulate URLs using methods within the\n\\texttt{Liferay} global JavaScript object.\n\n\\chapter{Liferay DXP JavaScript\nUtilities}\\label{liferay-dxp-javascript-utilities}\n\nThis reference explains some of the utility methods and objects inside\nthe \\texttt{Liferay} global JavaScript object.\n\n\\section{Retrieve Browser\nInformation}\\label{retrieve-browser-information}\n\nThe \\texttt{Liferay.Browser} object contains methods that expose the\ncurrent user agent characteristics without the need of accessing and\nparsing the global \\texttt{window.navigator} object.\n\nThe available methods for the \\texttt{Liferay.Browser} object are listed\nin the table below:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nMethod\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nReturn Type\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nacceptsGzip & boolean & Returns whether the browser accepts gzip file\ncompression \\\\\ngetMajorVersion & number & Returns the major version of the browser \\\\\ngetRevision & number & Returns the revision version of the browser \\\\\ngetVersion & number & Returns the major.minor version of the browser \\\\\nisAir & boolean & Returns whether the browser is Adobe AIR \\\\\nisChrome & boolean & Returns whether the browser is Chrome \\\\\nisFirefox & boolean & Returns whether the browser is Firefox \\\\\nisGecko & boolean & Returns whether the browser is Gecko \\\\\nisIe & boolean & Returns whether the browser is Internet Explorer \\\\\nisIphone & boolean & Returns whether the browser is on an Iphone \\\\\nisLinux & boolean & Returns whether the browser is being viewed on\nLinux \\\\\nisMac & boolean & Returns whether the browser is being viewed on Mac \\\\\nisMobile & boolean & Returns whether the browser is being viewed on a\nmobile device \\\\\nisMozilla & boolean & Returns whether the browser is Mozilla \\\\\nisOpera & boolean & Returns whether the browser is Opera \\\\\nisRtf & boolean & Returns whether the browser supports RTF \\\\\nisSafari & boolean & Returns whether the browser is Safari \\\\\nisSun & boolean & Returns whether the browser is being viewed on Sun\nOS \\\\\nisWebKit & boolean & Returns whether the browser is WebKit \\\\\nisWindows & boolean & Returns whether the browser is being viewed on\nWindows \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nBelow is an example configuration:\n\n\\begin{verbatim}\nLiferay.Browser.isChrome(); //returns true in Chrome\n\\end{verbatim}\n\n\\section{Format XML}\\label{format-xml}\n\nThe \\texttt{Liferay.Util.formatXML} utility takes XML content, as a\nString, and returns it formatted.\n\nParameters: - \\texttt{content}: The XML string to format -\n\\texttt{options}: An optional configuration object \\texttt{\\{\\}} that\ncontains additional parameters for formatting the XML\n\nThe default configuration contains these options:\n\n\\begin{verbatim}\nconst DEFAULT_OPTIONS = {\n  newLine: NEW_LINE, //'\\r\\n'\n  tagIndent: TAG_INDENT //'\\t'\n};\n\\end{verbatim}\n\nBelow is an example configuration for a JSP that overwrites the default\noptions:\n\n\\begin{verbatim}\nvar options = {newLine: '\\n', tagIndent: ' '};\n\nvar input = `<?xml xlmns:a=\"http://www.w3.org/TR/html4/\" version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE note>\n<a:note>                         <a:to>Foo</a:to>\n<a:from>Bar</a:from><a:heading>FooBar</a:heading>\n<a:body>FooBarBaz!</a:body>\n                  </a:note>\n`;\n\nvar formattedXMLString = Liferay.Util.formatXML(input, options);\n\nconsole.log(formattedXMLString);\n/*results:\n<?xml xlmns:a=\"http://www.w3.org/TR/html4/\" version=\"1.0\" encoding=\"UTF-8\"?>\\n'\n<!DOCTYPE note>\\n'\n<a:note>\\n'\n<a:to>Foo</a:to>\\n'\n<a:from>Bar</a:from>\\n'\n<a:heading>FooBar</a:heading>\\n'\n<a:body>FooBarBaz!</a:body>\\n'\n</a:note>';\n*/\n\\end{verbatim}\n\n\\section{Format Storage Size}\\label{format-storage-size}\n\nThe \\texttt{Liferay.Util.formatStorage} utility takes a storage size\nnumber (in bytes) and returns it in the proper format (KB, MB, or GB) as\na String.\n\nParameters: - \\texttt{size}: The numerical value of the storage size in\nbytes - \\texttt{options}: An optional configuration object \\texttt{\\{\\}}\nthat contains additional parameters for formatting the storage size\n\nThe default configuration contains these options:\n\n\\begin{verbatim}\nconst DEFAULT_OPTIONS = {\n  addSpaceBeforeSuffix: false,\n  decimalSeparator: '.',\n  denominator: 1024.0,\n  suffixGB: 'GB',\n  suffixKB: 'KB',\n  suffixMB: 'MB'\n};\n\\end{verbatim}\n\nBelow is an example configuration that overwrites some of the default\noptions:\n\n\\begin{verbatim}\nvar formattedSize = Liferay.Util.formatStorage(1048576, {\n  addSpaceBeforeSuffix: true,\n  decimalSeparator: ',',\n  suffixMB: 'megabytes'\n});\n\nconsole.log(formattedSize); //1,0 megabytes\n\\end{verbatim}\n\n\\section{Store and Retrieve Session Form\ndata}\\label{store-and-retrieve-session-form-data}\n\n\\texttt{Liferay.Util.setSessionValue()}: Sets a key, value pair for the\nStore utility's fetch value.\n\nParameters: - \\texttt{key}: The \\texttt{formData} key (String) -\n\\texttt{value}: The \\texttt{formData} key's corresponding value\n(Object\\textbar String)\n\n\\texttt{Liferay.Util.getSessionValue()}: Retrieves the Store utility's\nfetch value for the given \\texttt{key}.\n\nParameters: - \\texttt{key}: The key (String) to fetch the value for.\n\nBelow is an example configuration for a JSP:\n\n\\begin{verbatim}\nLiferay.Util.Session.set('state', 'open');\n\nLiferay.Util.Session.get('state').then(function(value) {\n  console.log(value); //open\n});\n\\end{verbatim}\n\nHere is an example configuration that uses ES6:\n\n\\begin{verbatim}\nimport {getSessionValue, setSessionValue} from 'frontend-js-web';\n\nsetSessionValue('state', 'open');\n\ngetSessionValue('state').then(value =>{\n  console.log(value); //open\n});\n\\end{verbatim}\n\n\\chapter{Invoking Liferay Services}\\label{invoking-liferay-services}\n\nLiferay DXP provides many web services out-of-the-box. To see a\ncomprehensive list of the available web services, navigate to\n\\texttt{http://localhost:8080/api/jsonws} (assuming your localhost is\nrunning on port 8080).\n\nThis reference covers how to invoke these web services using JavaScript.\n\n\\section{Invoking Web Services via\nJavaScript}\\label{invoking-web-services-via-javascript}\n\n7.0 contains a global JavaScript object called \\texttt{Liferay} that has\nmany useful utilities. One method is \\texttt{Liferay.Service}, which\ninvokes JSON web services.\n\nThe \\texttt{Liferay.Service} method takes four possible arguments:\n\n\\textbf{service \\{string\\textbar object\\}:} Specify the service name or\nan object with the keys as the service to call, and the value as the\nservice configuration object. (Required)\n\n\\textbf{data \\{object\\textbar node\\textbar string\\}:} Specify the data\nto send to the service. If the object passed is the ID of a form or a\nform element, the form fields will be serialized and used as the data.\n\n\\textbf{successCallback \\{function\\}:} A function to execute when the\nserver returns a response. It receives a JSON object as its first\nparameter.\n\n\\textbf{exceptionCallback \\{function\\}:} A function to execute when the\nresponse from the server contains a service exception. It receives an\nexception message as its first parameter.\n\nOne of the benefits of using the \\texttt{Liferay.Service} method versus\nusing a standard AJAX request is that it handles the authentication for\nyou.\n\nBelow is an example configuration of the \\texttt{Liferay.Service}\nmethod:\n\n\\begin{verbatim}\nLiferay.Service(\n        '/user/get-user-by-email-address',\n        {\n                companyId: Liferay.ThemeDisplay.getCompanyId(),\n                emailAddress: 'test@example.com'\n        },\n        function(obj) {\n                console.log(obj);\n        }\n);\n\\end{verbatim}\n\nThe example above retrieves information about a user by passing its\n\\texttt{companyId} and \\texttt{emailAddress}. The response data\nresembles the following JSON object:\n\n\\begin{verbatim}\n{\n        \"agreedToTermsOfUse\": true,\n        \"comments\": \"\",\n        \"companyId\": \"20116\",\n        \"contactId\": \"20157\",\n        \"createDate\": 1471990639779,\n        \"defaultUser\": false,\n        \"emailAddress\": \"test@example.com\",\n        \"emailAddressVerified\": true,\n        \"facebookId\": \"0\",\n        \"failedLoginAttempts\": 0,\n        \"firstName\": \"Test\",\n        \"googleUserId\": \"\",\n        \"graceLoginCount\": 0,\n        \"greeting\": \"Welcome Test Test!\",\n        \"jobTitle\": \"\",\n        \"languageId\": \"en_US\",\n        \"lastFailedLoginDate\": null,\n        \"lastLoginDate\": 1471996720765,\n        \"lastLoginIP\": \"127.0.0.1\",\n        \"lastName\": \"Test\",\n        \"ldapServerId\": \"-1\",\n        \"lockout\": false,\n        \"lockoutDate\": null,\n        \"loginDate\": 1472077523149,\n        \"loginIP\": \"127.0.0.1\",\n        \"middleName\": \"\",\n        \"modifiedDate\": 1472077523149,\n        \"mvccVersion\": \"7\",\n        \"openId\": \"\",\n        \"portraitId\": \"0\",\n        \"reminderQueryAnswer\": \"test\",\n        \"reminderQueryQuestion\": \"what-is-your-father's-middle-name\",\n        \"screenName\": \"test\",\n        \"status\": 0,\n        \"timeZoneId\": \"UTC\",\n        \"userId\": \"20156\",\n        \"uuid\": \"c641a7c9-5acb-aa68-b3ea-5575e1845d2f\"\n}\n\\end{verbatim}\n\nNow that you know how to send an individual request, you're ready to run\nbatch requests.\n\n\\section{Batching Requests}\\label{batching-requests}\n\nAnother way to invoke the \\texttt{Liferay.Service} method is by passing\nan object with the keys of the service to call and the value of the\nservice configuration object.\n\nBelow is an example configuration for a batch request:\n\n\\begin{verbatim}\nLiferay.Service(\n        {\n                '/user/get-user-by-email-address': {\n                        companyId: Liferay.ThemeDisplay.getCompanyId(),\n                        emailAddress: 'test@example.com'\n                }\n        },\n        function(obj) {\n                console.log(obj);\n        }\n);\n\\end{verbatim}\n\nYou can invoke multiple services with the same request by passing in an\narray of service objects. Here's an example:\n\n\\begin{verbatim}\nLiferay.Service(\n        [\n                {\n                        '/user/get-user-by-email-address': {\n                                companyId: Liferay.ThemeDisplay.getCompanyId(),\n                                emailAddress: 'test@example.com'\n                        }\n                },\n                {\n                        '/role/get-user-roles': {\n                                userId: Liferay.ThemeDisplay.getUserId()\n                        }\n                }\n        ],\n        function(obj) {\n                // obj is now an array of response objects\n                // obj[0] == /user/get-user-by-email-address data\n                // obj[1] == /role/get-user-roles data\n\n                console.log(obj);\n        }\n);\n\\end{verbatim}\n\nNext you can learn how to nest your requests.\n\n\\section{Nesting Requests}\\label{nesting-requests}\n\nNested service calls bind information from related objects together in a\nJSON object. You can call other services in the same HTTP request and\nconveniently nest returned objects.\n\nYou can use variables to reference objects returned from service calls.\nVariable names must start with a dollar sign (\\texttt{\\$}).\n\nThe example in this section retrieves user data with\n\\texttt{/user/get-user-by-id} and uses the \\texttt{contactId} returned\nfrom that service to then invoke \\texttt{/contact/get-contact} in the\nsame request.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You must flag parameters that take values from existing\nvariables. To flag a parameter, insert the \\texttt{@} prefix before the\nparameter name.\n\n\\noindent\\hrulefill\n\nBelow is an example configuration that demonstrates these concepts:\n\n\\begin{verbatim}\nLiferay.Service(\n        {\n                \"$user = /user/get-user-by-id\": {\n                        \"userId\": Liferay.ThemeDisplay.getUserId(),\n                        \"$contact = /contact/get-contact\": {\n                                \"@contactId\": \"$user.contactId\"\n                        }\n                }\n        },\n        function(obj) {\n                console.log(obj);\n        }\n);\n\\end{verbatim}\n\nHere is what the response data would look like for the request above:\n\n\\begin{verbatim}\n{\n        \"agreedToTermsOfUse\": true,\n        \"comments\": \"\",\n        \"companyId\": \"20116\",\n        \"contactId\": \"20157\",\n        \"createDate\": 1471990639779,\n        \"defaultUser\": false,\n        \"emailAddress\": \"test@example.com\",\n        \"emailAddressVerified\": true,\n        \"facebookId\": \"0\",\n        \"failedLoginAttempts\": 0,\n        \"firstName\": \"Test\",\n        \"googleUserId\": \"\",\n        \"graceLoginCount\": 0,\n        \"greeting\": \"Welcome Test Test!\",\n        \"jobTitle\": \"\",\n        \"languageId\": \"en_US\",\n        \"lastFailedLoginDate\": null,\n        \"lastLoginDate\": 1472231639378,\n        \"lastLoginIP\": \"127.0.0.1\",\n        [...]\n        \"screenName\": \"test\",\n        \"status\": 0,\n        \"timeZoneId\": \"UTC\",\n        \"userId\": \"20156\",\n        \"uuid\": \"c641a7c9-5acb-aa68-b3ea-5575e1845d2f\",\n        \"contact\": {\n                \"accountId\": \"20118\",\n                \"birthday\": 0,\n                [...]\n                \"createDate\": 1471990639779,\n                \"emailAddress\": \"test@example.com\",\n                \"employeeNumber\": \"\",\n                \"employeeStatusId\": \"\",\n                \"facebookSn\": \"\",\n                \"firstName\": \"Test\",\n                \"lastName\": \"Test\",\n                \"male\": true,\n                \"middleName\": \"\",\n                \"modifiedDate\": 1471990639779,\n                [...]\n                \"userName\": \"\"\n        }\n}\n\\end{verbatim}\n\nNow that you know how to process requests, you can learn how to filter\nthe results.\n\n\\section{Filtering Results}\\label{filtering-results}\n\nIf you don't want all the properties returned by a service, you can\ndefine a whitelist of properties. This returns only the specific\nproperties you request in the object.\n\nBelow is an example of whitelisting properties:\n\n\\begin{verbatim}\nLiferay.Service(\n        {\n                '$user[emailAddress,firstName] = /user/get-user-by-id': {\n                        userId: Liferay.ThemeDisplay.getUserId()\n                }\n        },\n        function(obj) {\n                console.log(obj);\n        }\n);\n\\end{verbatim}\n\nTo specify whitelist properties, place the properties in square brackets\n(e.g., \\texttt{{[}whiteList{]}}) immediately following the name of your\nvariable. The example above requests only the \\texttt{emailAddress} and\n\\texttt{firstName} of the user.\n\nBelow is the filtered response:\n\n\\begin{verbatim}\n{\n        \"firstName\": \"Test\",\n        \"emailAddress\": \"test@example.com\"\n}\n\\end{verbatim}\n\nNext you can learn how to populate the inner parameters of the request.\n\n\\section{Inner Parameters}\\label{inner-parameters}\n\nWhen you pass in an object parameter, you'll often need to populate its\ninner parameters (i.e., fields).\n\nConsider a default parameter \\texttt{serviceContext} of type\n\\texttt{ServiceContext}. To make an appropriate call to JSON web\nservices you might need to set \\texttt{serviceContext} fields such as\n\\texttt{scopeGroupId}, as shown below:\n\n\\begin{verbatim}\nLiferay.Service(\n        '/example/some-web-service',\n        {\n                serviceContext: {\n                        scopeGroupId: 123\n                }\n        },\n        function(obj) {\n                console.log(obj);\n        }\n);\n\\end{verbatim}\n\n\\chapter{\\texorpdfstring{Handling AJAX Requests with\n\\texttt{Liferay.Util.fetch}}{Handling AJAX Requests with Liferay.Util.fetch}}\\label{handling-ajax-requests-with-liferay.util.fetch}\n\nWhen you make Ajax requests (referred to as Service Resource\nactions/requests in Liferay DXP), they must protect against\n\\href{https://en.wikipedia.org/wiki/Cross-site_request_forgery}{CSRF}\nand include the proper credentials. Since Liferay DXP 7.2 SP1 and\nLiferay CE Portal 7.2 GA2, Liferay DXP provides a\n\\texttt{Liferay.Util.fetch} utility based on the standard\n\\href{https://fetch.spec.whatwg.org/}{\\texttt{fetch}} API that you can\nuse to make AJAX requests. It includes these key features:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  A thin wrapper on ES6\n  \\href{https://fetch.spec.whatwg.org/}{\\texttt{fetch}} that shares the\n  same API\n\\item\n  Sets \\texttt{credentials:include} to each request\n\\item\n  Sets \\texttt{x-csrf-token} header to each request\n\\item\n  Requires no dependencies\n\\end{itemize}\n\nBelow is an example configuration in ES6:\n\n\\begin{verbatim}\nimport {fetch} from 'frontend-js-web';\n\nfetch(url, {\n  body: new FormData(form),\n  method: 'POST'\n})\n  .then(response => response.json())\n  .then(response => processData(response))\n  .then(response => failureCallback(error));\n\\end{verbatim}\n\nExample use case in JSPs:\n\n\\begin{verbatim}\nLiferay.Util.fetch(url, {\n  body: new FormData(form),\n  method: 'POST'\n}).then(function(response) {\n  return response.json();\n}).then(function(response) {\n  message.innerHTML = response.message;\n}).catch(function() {\n  failureCallback();\n});\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{NOTE:} global access through \\texttt{Liferay.Util} is only meant\nfor use in JSP code. In ES6, you must use the \\texttt{fetch} module, as\nshown in the JavaScript example above.\n\n\\chapter{Working with Addresses}\\label{working-with-addresses}\n\nThe \\texttt{Liferay} global JavaScript Object exposes methods, objects,\nand properties that access the portal context. The\n\\texttt{Liferay.Address} utility contains methods for retrieving\ninformation about the addresses country and region. The \\texttt{Liferay}\nglobal object is automatically available at runtime, so no additional\ndependencies are required.\n\nThe available methods are listed below, along with an example\nconfiguration.\n\n\\texttt{Liferay.Address.getCountries(callback)}: returns an Array of the\navailable countries.\n\nParameters: - \\texttt{callback}: A callback function to post-process the\nArray of countries\n\nThe example below prints the list of available regions for the selected\ncountry (the United States in this case) in a table:\n\n\\begin{verbatim}\nLiferay.Address.getCountries(function(e){console.table(e)}, 19);\n\\end{verbatim}\n\n\\texttt{Liferay.Address.getRegions(callback,\\ selectKey)}: returns an\nArray of the available regions, by country, for the specified region ID.\n\nParameters: - \\texttt{callback}: A callback function to post-process the\nArray of regions - \\texttt{selectKey}: The selected region ID\n\nThe example below prints the list of available countries in a table to\nthe console:\n\n\\begin{verbatim}\nLiferay.Address.getCountries(function(e){console.table(e)});\n\\end{verbatim}\n\nThis example uses an AUI \\texttt{DynamicSelect} module to create a pair\nof select fields in a JSP. The first field retrieves the countries with\nthe \\texttt{Liferay.Address.getCountries()} method, and the second\nselect field is dynamically populated with the selected country's\navailable regions with the \\texttt{Liferay.Address.getRegions()} method:\n\n\\begin{verbatim}\n<aui:script use=\"liferay-dynamic-select\">\n  new Liferay.DynamicSelect(\n    [\n      {\n        select: '<portlet:namespace />countryId',\n        selectData: Liferay.Address.getCountries,\n        selectDesc: 'nameCurrentValue',\n        selectId: 'countryId',\n        selectSort: '<%= true %>',\n        selectVal: '<%= countryId %>'\n      },\n      {\n        select: '<portlet:namespace />regionId',\n        selectData: Liferay.Address.getRegions,\n        selectDesc: 'name',\n        selectDisableOnEmpty: true,\n        selectId: 'regionId',\n        selectVal: '<%= regionId %>'\n      }\n    ]\n  );\n</aui:script>\n\\end{verbatim}\n\n\\chapter{FreeMarker Taglib Macros}\\label{freemarker-taglib-macros}\n\nLiferay DXP's taglibs are mapped to FreeMarker macros, so you can use\nthem in your FreeMarker templates. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs}{Taglib\nreference} for more information on using each taglib in your theme\ntemplates. The taglib macros are defined in\n\\texttt{taglib-mappings.properties} files. For convenience, these macros\nare listed in the table below:\n\nMacro\n\nTaglib\n\nTLD\n\n\\texttt{liferay\\_aui}\n\nliferay-aui\n\nliferay-aui.tld\n\n\\texttt{liferay\\_portlet}\n\nliferay-portlet\n\nliferay-portlet-ext.tld\n\n\\texttt{liferay\\_security}\n\nliferay-security\n\nliferay-security.tld\n\n\\texttt{liferay\\_theme}\n\nliferay-theme\n\nliferay-theme.tld\n\n\\texttt{liferay\\_ui}\n\nliferay-ui\n\nliferay-ui.tld\n\n\\texttt{liferay\\_util}\n\nliferay-util\n\nliferay-util.tld\n\n\\texttt{portlet}\n\nportlet\n\nliferay-portlet.tld\n\n\\texttt{liferay\\_frontend}\n\nliferay-frontend\n\nliferay-frontend.tld\n\n\\texttt{clay}\n\nclay\n\nliferay-clay.tld\n\n\\texttt{liferay\\_map}\n\nliferay-map\n\nliferay-map.tld\n\n\\texttt{liferay\\_rss}\n\nliferay-rss\n\nliferay-rss.tld\n\n\\texttt{liferay\\_flags}\n\nliferay-flags\n\nliferay-flags.tld\n\n\\texttt{liferay\\_expando}\n\nliferay-expando\n\nliferay-expando.tld\n\n\\texttt{liferay\\_journal}\n\nliferay-journal\n\nliferay-journal.tld\n\n\\texttt{liferay\\_social\\_bookmarks}\n\nliferay-social-bookmarks\n\nliferay-social-bookmarks.tld\n\n\\texttt{liferay\\_site}\n\nliferay-site\n\nliferay-site.tld\n\n\\texttt{liferay\\_comment}\n\nliferay-comment\n\nliferay-comment.tld\n\n\\texttt{liferay\\_social\\_activities}\n\nliferay-social-activities\n\nliferay-social-activities.tld\n\n\\texttt{liferay\\_asset}\n\nliferay-asset\n\nliferay-asset.tld\n\n\\texttt{liferay\\_trash}\n\nliferay-trash\n\nliferay-trash.tld\n\n\\texttt{liferay\\_item\\_selector}\n\nliferay-item-selector\n\nliferay-item-selector.tld\n\n\\texttt{liferay\\_layout}\n\nliferay-layout\n\nliferay-layout.tld\n\n\\texttt{liferay\\_editor}\n\nliferay-editor\n\nliferay-editor.tld\n\n\\texttt{liferay-fragment}\n\nliferay-fragment\n\nliferay-fragment.tld\n\n\\texttt{liferay\\_reading\\_time}\n\nliferay-reading-time\n\nliferay-reading-time.tld\n\n\\texttt{liferay\\_sharing}\n\nliferay-sharing\n\nliferay-sharing.tld\n\n\\texttt{liferay\\_site\\_navigation}\n\nliferay-site-navigation\n\nliferay-site-navigation.tld\n\n\\texttt{adaptive\\_media\\_image}\n\nliferay-adaptive-media\n\nliferay-adaptive-media.tld\n\n\\texttt{liferay\\_product\\_navigation}\n\nliferay-product-navigation\n\nliferay-product-navigation.tld\n\n\\chapter{Setting up Your npm\nEnvironment}\\label{setting-up-your-npm-environment}\n\nIf you're using npm for development in Liferay DXP, you should set up\nyour npm environment to avoid potential permissions issues. Follow these\nsteps to configure your npm environment:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create an \\texttt{.npmrc} file in your user's home directory. This\n  helps bypass npm permission-related issues.\n\\item\n  In the \\texttt{.npmrc} file, specify a \\texttt{prefix} property based\n  on your user's home directory, like the one shown below. This value\n  specifies where to install global npm packages:\n\n\\begin{verbatim}\nprefix=/Users/[username]/.npm-packages\n\\end{verbatim}\n\\item\n  Set the \\texttt{NPM\\_PACKAGES} system environment variable to the\n  \\texttt{prefix} value you just specified:\n\n\\begin{verbatim}\nNPM_PACKAGES=/Users/[username]/.npm-packages (same as prefix value)\n\\end{verbatim}\n\\item\n  Since npm installs Yeoman and gulp executables to\n  \\texttt{\\$\\{NPM\\_PACKAGES\\}/bin} on UNIX and to\n  \\texttt{\\%NPM\\_PACKAGES\\%} on Windows, make sure to add the\n  appropriate directory to your system path. For example, on UNIX you'd\n  set this:\n\n\\begin{verbatim}\nPATH=${PATH}:${NPM_PACKAGES}/bin\n\\end{verbatim}\n\\end{enumerate}\n\n\\chapter{Sitemap Page Configuration\nOptions}\\label{sitemap-page-configuration-options}\n\nIf you're importing resources with your themes, you must define the\npages for the site in the theme's \\texttt{sitemap.json}. Below is the\nfull list of available configuration options for pages in the theme's\n\\texttt{sitemap.json}:\n\n\\textbf{colorSchemeId:} Specifies a different color scheme (by ID) than\nthe default color scheme to use for the page.\n\n\\textbf{columns:} Specifies the column contents for the page.\n\n\\textbf{friendlyURL:} Sets the page's friendly URL.\n\n\\textbf{hidden:} Sets whether the page is hidden.\n\n\\textbf{layoutCss:} Sets custom CSS for the page to load after the\ntheme.\n\n\\textbf{layoutPrototypeLinkEnabled:} Sets whether the page inherits\nchanges made to the page template (if the page has one).\n\n\\textbf{layoutPrototypeName:} Specifies the page template (by name) to\nuse for the page. If this is defined, the page template's UUID is\nretrieved using the name, and \\texttt{layoutPrototypeUuid} is not\nrequired.\n\n\\textbf{layoutPrototypeUuid:} Specifies the page template (by UUID) to\nuse for the page. If \\texttt{layoutPrototypeName} is defined, this is\nnot required.\n\n\\textbf{layouts:} Specifies child pages for a page set.\n\n\\textbf{name:} The page's name.\n\n\\textbf{nameMap:} Passes a name object with multiple name key/value\npairs. You can use this to pass translations for a page's title, as\nshown in the example above.\n\n\\textbf{privatePages:} Specifies private pages.\n\n\\textbf{publicPages:} Specifies public pages.\n\n\\textbf{themeId:} Specifies a different theme (by ID) than the default\ntheme bundled with the \\texttt{sitemap.json} to use for the page.\n\n\\textbf{title:} The page's title.\n\n\\textbf{type:} Sets the page type. The default value is \\texttt{portlet}\n(empty page). Possible values are \\texttt{copy} (copy of a page of this\nsite), \\texttt{embedded}, \\texttt{full\\_page\\_application},\n\\texttt{link\\_to\\_layout}, \\texttt{node} (page set), \\texttt{panel},\n\\texttt{portlet}, and \\texttt{url} (link to URL).\n\n\\textbf{typeSettings:} Specifies settings (using key/value pairs) for\nthe page \\texttt{type}.\n\n\\chapter{CKEditor Plugin Reference\nGuide}\\label{ckeditor-plugin-reference-guide}\n\nThis reference guide provides a list of the default CKEditor plugins\nbundled with Liferay DXP's AlloyEditor. You can\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/adding-buttons-to-alloyeditor-toolbars}{use\nthese existing CKEditor plugins in your custom AlloyEditor\nconfigurations}. Each plugin below links to its \\texttt{plugin.js} file\nfor reference, specifying the plugin's name and buttons if applicable:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/about/plugin.js}{about}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/a11yhelp/plugin.js}{allyhelp}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/a11yhelpbtn/plugin.js}{allyhelpbtn}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/ajaxsave/plugin.js}{ajaxsave}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/autocomplete/plugin.js}{autocomplete}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/basicstyles/plugin.js}{basicstyles}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/bbcode/plugin.js}{bbcode}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/bidi/plugin.js}{bidi}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/blockquote/plugin.js}{blockquote}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/clipboard/plugin.js}{clipboard}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/colorbutton/plugin.js}{colorbutton}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/colordialog/plugin.js}{colordialog}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/contextmenu/plugin.js}{contextmenu}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/creole/plugin.js}{creole}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/dialogadvtab/plugin.js}{dialogadvtab}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/div/plugin.js}{div}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/elementspath/plugin.js}{elementspath}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/enterkey/plugin.js}{enterkey}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/entities/plugin.js}{entities}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/filebrowser/plugin.js}{filebrowse}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/find/plugin.js}{find}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/flash/plugin.js}{flash}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/floatingspace/plugin.js}{floatingspace}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/font/plugin.js}{font}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/format/plugin.js}{format}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/forms/plugin.js}{forms}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/horizontalrule/plugin.js}{horizontalrule}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/htmlwriter/plugin.js}{htmlwriter}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/image/plugin.js}{image}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/iframe/plugin.js}{iframe}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/indent/plugin.js}{indent}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/itemselector/plugin.js}{itemselector}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/justify/plugin.js}{justify}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/link/plugin.js}{link}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/list/plugin.js}{list}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/liststyle/plugin.js}{liststyle}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/lfrpopup/plugin.js}{lfrpopup}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/magicline/plugin.js}{magicline}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/media/plugin.js}{media}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/newpage/plugin.js}{newpage}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/pagebreak/plugin.js}{pagebreak}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/pastefromword/plugin.js}{pastefromword}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/pastetext/plugin.js}{pastetext}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/preview/plugin.js}{preview}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/removeformat/plugin.js}{removeformat}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/resize/plugin.js}{resize}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/restore/plugin.js}{restore}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/selectall/plugin.js}{selectall}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/showblocks/plugin.js}{showblocks}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/showborders/plugin.js}{showborders}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/smiley/plugin.js}{smiley}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/sourcearea/plugin.js}{sourcearea}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/specialchar/plugin.js}{specialchar}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/stylescombo/plugin.js}{stylescombo}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/tab/plugin.js}{tab}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/table/plugin.js}{table}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/tabletools/plugin.js}{tabletools}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/templates/plugin.js}{templates}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/toolbar/plugin.js}{toolbar}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/undo/plugin.js}{undo}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/wikilink/plugin.js}{wikilink}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/wysiwygarea/plugin.js}{wysiwygarea}\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The following CKEditor plugins are not available for\ninline mode in AlloyEditor at this time, but you can still use them in\nthe classic CKEditor:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/maximize/plugin.js}{maximize}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/print/plugin.js}{print}\n\\item\n  \\href{https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/save/plugin.js}{save}\n\\end{itemize}\n\nTo use the Classic CKEditor instead of AlloyEditor, there are a few\nproperties to set, depending on the portlet. Add the\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/portal-impl/src/portal.properties\\#L5432-L5441}{properties}\nthat you need to your \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\n editor.wysiwyg.default=ckeditor\n editor.wysiwyg.portal-impl.portlet.ddm.text_html.ftl=ckeditor\n editor.wysiwyg.portal-web.docroot.html.portlet.announcements.edit_entry.jsp=ckeditor\n editor.wysiwyg.portal-web.docroot.html.portlet.blogs.edit_entry.jsp=ckeditor\n editor.wysiwyg.portal-web.docroot.html.portlet.mail.edit.jsp=ckeditor\n editor.wysiwyg.portal-web.docroot.html.portlet.mail.edit_message.jsp=ckeditor\n editor.wysiwyg.portal-web.docroot.html.portlet.message_boards.edit_message.html.jsp=ckeditor\n editor.wysiwyg.portal-web.docroot.html.taglib.ui.discussion.jsp=ckeditor\n editor.wysiwyg.portal-web.docroot.html.taglib.ui.email_notification_settings.jsp=ckeditor\n\\end{verbatim}\n\n\\chapter{Fully Qualified Portlet IDs}\\label{fully-qualified-portlet-ids}\n\nBelow is a listing of the portlet IDs for the default portlets in\nLiferay DXP. You can use these IDs to embed portlets in your theme's\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-portlets-in-a-sitemap}{sitemap}.\n\n\\textbf{Collaboration}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nPortlet\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nID\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nBlogs & \\texttt{com\\_liferay\\_blogs\\_web\\_portlet\\_BlogsPortlet} \\\\\nBlogs Aggregator &\n\\texttt{com\\_liferay\\_blogs\\_web\\_portlet\\_BlogsAgreggatorPortlet} \\\\\nCalendar &\n\\texttt{com\\_liferay\\_calendar\\_web\\_portlet\\_CalendarPortlet} \\\\\nDynamic Data Lists Display &\n\\texttt{com\\_liferay\\_dynamic\\_data\\_lists\\_web\\_portlet\\_DDLDisplayPortlet} \\\\\nForm &\n\\texttt{com\\_liferay\\_dynamic\\_data\\_mapping\\_form\\_web\\_portlet\\_DDMFormPortlet} \\\\\nInvite Members &\n\\texttt{com\\_liferay\\_invitation\\_invite\\_members\\_web\\_portlet\\_InviteMembersPortlet} \\\\\nMessage Boards &\n\\texttt{com\\_liferay\\_message\\_boards\\_web\\_portlet\\_MBPortlet} \\\\\nRecent Bloggers &\n\\texttt{com\\_liferay\\_blogs\\_recent\\_bloggers\\_web\\_portlet\\_RecentBloggersPortlet} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Community}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nPortlet\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nID\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nMy Sites &\n\\texttt{com\\_liferay\\_site\\_my\\_sites\\_web\\_portlet\\_MySitesPortlet} \\\\\nPage Comments &\n\\texttt{com\\_liferay\\_comment\\_page\\_comments\\_web\\_portlet\\_PageCommentsPortlet} \\\\\nPage Flags &\n\\texttt{com\\_liferay\\_flags\\_web\\_portlet\\_PageFlagsPortlet} \\\\\nPage Ratings &\n\\texttt{com\\_liferay\\_ratings\\_page\\_ratings\\_web\\_portlet\\_PageRatingsPortlet} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Content Management}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nPortlet\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nID\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nAsset Publisher &\n\\texttt{com\\_liferay\\_asset\\_publisher\\_web\\_portlet\\_AssetPublisherPortlet} \\\\\nBreadcrumb &\n\\texttt{com\\_liferay\\_site\\_navigation\\_breadcrumb\\_web\\_portlet\\_SiteNavigationBreadcrumbPortlet} \\\\\nCategories Navigation &\n\\texttt{com\\_liferay\\_asset\\_categories\\_navigation\\_web\\_portlet\\_AssetCategoriesNavigationPortlet} \\\\\nDocuments and Media &\n\\texttt{com\\_liferay\\_document\\_library\\_web\\_portlet\\_DLPortlet} \\\\\nHighest Rated Assets &\n\\texttt{com\\_liferay\\_asset\\_publisher\\_web\\_portlet\\_HighestRatedAssetsPortlet} \\\\\nKnowledge Base Article &\n\\texttt{com\\_liferay\\_knowledge\\_base\\_web\\_portlet\\_ArticlePortlet} \\\\\nKnowledge Base Display &\n\\texttt{com\\_liferay\\_knowledge\\_base\\_web\\_portlet\\_DisplayPortlet} \\\\\nKnowledge Base Search &\n\\texttt{com\\_liferay\\_knowledge\\_base\\_web\\_portlet\\_SearchPortlet} \\\\\nKnowledge Base Section &\n\\texttt{com\\_liferay\\_knowledge\\_base\\_web\\_portlet\\_SectionPortlet} \\\\\nMedia Gallery &\n\\texttt{com\\_liferay\\_document\\_library\\_web\\_portlet\\_IGDisplayPortlet} \\\\\nMost Viewed Assets &\n\\texttt{com\\_liferay\\_asset\\_publisher\\_web\\_portlet\\_MostViewedAssetsPortlet} \\\\\nNavigation Menu &\n\\texttt{com\\_liferay\\_site\\_navigation\\_menu\\_web\\_portlet\\_SiteNavigationMenuPortlet} \\\\\nNested Applications &\n\\texttt{com\\_liferay\\_nested\\_portlets\\_web\\_portlet\\_NestedPortletsPortlet} \\\\\nPolls Display Portlet &\n\\texttt{com\\_liferay\\_polls\\_web\\_portlet\\_PollsDisplayPortlet} \\\\\nRelated Assets &\n\\texttt{com\\_liferay\\_asset\\_publisher\\_web\\_portlet\\_RelatedAssetsPortlet} \\\\\nSite Map &\n\\texttt{com\\_liferay\\_site\\_navigation\\_site\\_map\\_web\\_portlet\\_SiteNavigationSiteMapPortlet} \\\\\nSites Directory &\n\\texttt{com\\_liferay\\_site\\_navigation\\_directory\\_web\\_portlet\\_SitesDirectoryPortlet} \\\\\nTag Cloud &\n\\texttt{com\\_liferay\\_asset\\_tags\\_navigation\\_web\\_portlet\\_AssetTagsCloudPortlet} \\\\\nTags Navigation &\n\\texttt{com\\_liferay\\_asset\\_tags\\_navigation\\_web\\_portlet\\_AssetTagsNavigationPortlet} \\\\\nWeb Content Display &\n\\texttt{com\\_liferay\\_journal\\_content\\_web\\_portlet\\_JournalContentPortlet} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{News}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nPortlet\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nID\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nAlerts &\n\\texttt{com\\_liferay\\_announcements\\_web\\_portlet\\_AlertsPortlet} \\\\\nAnnouncements &\n\\texttt{com\\_liferay\\_announcements\\_web\\_portlet\\_AnnouncementsPortlet} \\\\\nRecent Content Portlet &\n\\texttt{com\\_liferay\\_asset\\_publisher\\_web\\_portlet\\_RecentContentPortlet} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Sample}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}ll@{}}\n\\toprule\\noalign{}\nPortlet & ID \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nHello World &\n\\texttt{com\\_liferay\\_hello\\_world\\_web\\_portlet\\_HelloWorldPortlet} \\\\\nIFrame & \\texttt{com\\_liferay\\_iframe\\_web\\_portlet\\_IFramePortlet} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Search}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nPortlet\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nID\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nCategory Facet &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_category\\_facet\\_portlet\\_CategoryFacetPortlet} \\\\\nCustom Facet &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_custom\\_facet\\_portlet\\_CustomFacetPortlet} \\\\\nFolder Facet &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_folder\\_facet\\_portlet\\_FolderFacetPortlet} \\\\\nModified Facet &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_modified\\_facet\\_portlet\\_ModifiedFacetPortlet} \\\\\nSearch Bar &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_search\\_bar\\_portlet\\_SearchBarPortlet} \\\\\nSearch Insights &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_search\\_insights\\_portlet\\_SearchInsightsPortlet} \\\\\nSearch Options &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_search\\_options\\_portlet\\_SearchOptionsPortlet} \\\\\nSearch Results &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_search\\_results\\_portlet\\_SearchResultsPortlet} \\\\\nSite Facet &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_site\\_facet\\_portlet\\_SiteFacetPortlet} \\\\\nSuggestions &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_suggestions\\_portlet\\_SuggestionsPortlet} \\\\\nTag Facet &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_tag\\_facet\\_portlet\\_TagFacetPortlet} \\\\\nType Facet &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_type\\_facet\\_portlet\\_TypeFacetPortlet} \\\\\nUser Facet &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_user\\_facet\\_portlet\\_UserFacetPortlet} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Social}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nPortlet\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nID\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nActivities &\n\\texttt{com\\_liferay\\_social\\_activities\\_web\\_portlet\\_SocialActivitiesPortlet} \\\\\nContacts Center &\n\\texttt{com\\_liferay\\_contacts\\_web\\_portlet\\_ContactsCenterPortlet} \\\\\nMembers &\n\\texttt{com\\_liferay\\_social\\_networking\\_web\\_members\\_portlet\\_MembersPortlet} \\\\\nMy Contacts &\n\\texttt{com\\_liferay\\_contacts\\_web\\_portlet\\_MyContactsPortlet} \\\\\nProfile &\n\\texttt{com\\_liferay\\_contacts\\_web\\_portlet\\_ProfilePortlet} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Tools}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nPortlet\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nID\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nLanguage Selector &\n\\texttt{com\\_liferay\\_site\\_navigation\\_language\\_web\\_portlet\\_SiteNavigationLanguagePortlet} \\\\\nSearch &\n\\texttt{com\\_liferay\\_portal\\_search\\_web\\_portlet\\_SearchPortlet} \\\\\nSign In & \\texttt{com\\_liferay\\_login\\_web\\_portlet\\_LoginPortlet} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Wiki}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nPortlet\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nID\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nPage Menu &\n\\texttt{com\\_liferay\\_wiki\\_navigation\\_web\\_portlet\\_WikiNavigationPageMenuPortlet} \\\\\nTree Menu &\n\\texttt{com\\_liferay\\_wiki\\_navigation\\_web\\_portlet\\_WikiNavigationTreeMenuPortlet} \\\\\nWiki & \\texttt{com\\_liferay\\_wiki\\_web\\_portlet\\_WikiPortlet} \\\\\nWiki Display &\n\\texttt{com\\_liferay\\_wiki\\_web\\_portlet\\_WikiDisplayPortlet} \\\\\n\\end{longtable}\n\n\\chapter{Available SPA Lifecycle\nEvents}\\label{available-spa-lifecycle-events}\n\nDuring development, you may need to know when navigation has started or\nstopped in your SPA. SennaJS makes this easy by exposing lifecycle\nevents that represent state changes in the application. The available\nlifecycle events are listed in the table below:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nEvent\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nEx payload\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{beforeNavigate} & Fires before navigation starts. This event\npasses a JSON object with the path to the content you are navigating to\nand whether to update the history. &\n\\texttt{\\{\\ path:\\ \\textquotesingle{}/pages/page1.html\\textquotesingle{},\\ replaceHistory:\\ false\\ \\}} \\\\\n\\texttt{startNavigate} & Fires when navigation begins & `\\{ form: ' \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill replaceHistory: false \\}` \\textbar{}\n\n\\noindent\\hrulefill\n\n\\texttt{endNavigate} \\textbar{} Fired after the content has been\nretrieved and inserted onto\n\n\\noindent\\hrulefill the page \\textbar{}\n\\texttt{\\{\\ form:\\ \\textquotesingle{}\\textless{}form\\ name=\"form\"\\textgreater{}\\textless{}/form\\textgreater{}\\textquotesingle{},\\ path:\\ \\textquotesingle{}/pages/page1.html\\textquotesingle{}\\ \\}}\n\\textbar{}\n\nThese events can be leveraged easily by listening for them on the\nLiferay global object. For example, the JavaScript below alerts the user\nto ``Get ready to navigate to'' the URL that has been clicked, just\nbefore SPA navigation begins:\n\n\\begin{verbatim}\nLiferay.on('beforeNavigate', function(event) {\n    alert(\"Get ready to navigate to \" + event.path);\n});\n\\end{verbatim}\n\nThe alert takes advantage of the payload for the \\texttt{beforeNavigate}\nevent, retrieving the URL from the \\texttt{path} attribute of the JSON\npayload object.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/private-messaging-before-navigate.png}\n\\caption{You can leverage SPA lifecycle events in your apps.}\n\\end{figure}\n\n\\chapter{Theme Anatomy Reference\nGuide}\\label{theme-anatomy-reference-guide}\n\nA theme is made up of several files. Although most of the files are\nnamed after their matching components, their functions may be unclear.\nThis reference guide explains each file's usage to make clear which\nfiles to modify.\n\nThemes built with the\n\\href{https://github.com/liferay/liferay-js-themes-toolkit/tree/master/packages}{Liferay\nJS Theme Toolkit} have the anatomy shown below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{theme-name/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{src/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{css/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#-clay-customscss}{\\texttt{\\_clay\\_custom.scss}}\n      \\item\n        \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#-clay-variablesscss}{\\texttt{\\_clay\\_variables.scss}}\n      \\item\n        \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#-customscss}{\\texttt{\\_custom.scss}}\n      \\item\n        \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#-liferay-variables-customscss}{\\texttt{\\_liferay\\_variables\\_custom.scss}}\n      \\end{itemize}\n    \\item\n      \\texttt{images/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        (custom images)\n      \\end{itemize}\n    \\item\n      \\texttt{js/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#mainjs}{\\texttt{main.js}}\n      \\end{itemize}\n    \\item\n      \\texttt{templates/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#init-customftl}{\\texttt{init\\_custom.ftl}}\n      \\item\n        \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#navigationftl}{\\texttt{navigation.ftl}}\n      \\item\n        \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#portal-normalftl}{\\texttt{portal\\_normal.ftl}}\n      \\item\n        \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#portal-pop-upftl}{\\texttt{portal\\_pop\\_up.ftl}}\n      \\item\n        \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#portletftl}{\\texttt{portlet.ftl}}\n      \\end{itemize}\n    \\item\n      \\texttt{WEB-INF/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#liferay-look-and-feelxml}{\\texttt{liferay-look-and-feel.xml}}\n      \\item\n        \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#liferay-plugin-packageproperties}{\\texttt{liferay-plugin-package.properties}}\n      \\item\n        \\texttt{src/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{resources-importer/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            (Many directories)\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#liferay-themejson}{\\texttt{liferay-theme.json}}\n  \\item\n    \\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide\\#packagejson}{\\texttt{package.json}}\n  \\end{itemize}\n\\end{itemize}\n\nRegarding CSS files, you should only modify\n\\texttt{\\_clay\\_custom.scss}, \\texttt{\\_clay\\_variables.scss},\n\\texttt{\\_custom.scss}, and \\texttt{\\_liferay\\_variables\\_custom.scss}.\n\nYou can of course overwrite any CSS file you want, but if you modify any\nother files, you're removing styling that 7.0 needs to work properly.\n\n\\section{Theme Files}\\label{theme-files}\n\n\\section{\\_clay\\_custom.scss}\\label{clay_custom.scss}\n\nUsed for Clay custom styles, i.e.~styles for a third party Bootstrap\ntheme. Anything written in this file is compiled in the same scope as\nBootstrap/Lexicon, so you can use their variables, mixins, etc. You can\nalso implement any of the variables you define in\n\\texttt{\\_clay\\_variables.scss}.\n\n\\section{\\_clay\\_variables.scss}\\label{clay_variables.scss}\n\nUsed to store custom Sass variables. This file gets injected into the\nBootstrap/Lexicon build, so you can overwrite variables and change how\nthose libraries are compiled.\n\n\\section{\\_custom.scss}\\label{custom.scss}\n\nUsed for custom CSS styles. You should place all of your custom CSS\nmodifications in this file.\n\n\\section{\\_liferay\\_variables\\_custom.scss}\\label{liferay_variables_custom.scss}\n\nUsed for overwriting variables defined in\n\\texttt{\\_liferay\\_variables.scss} without wiping out the whole file.\n\n\\section{init\\_custom.ftl}\\label{init_custom.ftl}\n\nUsed for custom FreeMarker variables i.e.~\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/making-configurable-theme-settings}{theme\nsetting} variables.\n\n\\section{navigation.ftl}\\label{navigation.ftl}\n\nThe theme template for the theme's navigation.\n\n\\section{portal\\_normal.ftl}\\label{portal_normal.ftl}\n\nSimilar to a static site's \\texttt{index.html}, this file acts as a hub\nfor all theme templates.\n\n\\section{portal\\_pop\\_up.ftl}\\label{portal_pop_up.ftl}\n\nThe theme template for pop up dialogs for the theme's portlets.\n\n\\section{portlet.ftl}\\label{portlet.ftl}\n\nThe theme template for the theme's portlets. If your theme uses\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/theming-portlets\\#portlet-decorators}{Application\nDecorators}, you can modify this file to create application\ndecorator-specific theme settings.\n\n\\section{liferay-theme.json}\\label{liferay-theme.json}\n\nContains the configuration settings for your app server, in Node.js\ntool-based themes. You can change this file manually at any time to\nupdate your server settings. The file can also be updated via the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/updating-your-themes-app-server}{\\texttt{gulp\\ init}\ntask}.\n\n\\section{package.json}\\label{package.json}\n\nContains theme setting information such as the theme template language,\nversion, and base theme, for Node.js tool developed themes. You can\nupdate this file manually. The\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/changing-your-base-theme}{\\texttt{gulp\\ extend}\ntask} can also be used to change the base theme.\n\n\\section{main.js}\\label{main.js}\n\nUsed for custom JavaScript.\n\n\\section{liferay-look-and-feel.xml}\\label{liferay-look-and-feel.xml}\n\nContains basic information for the theme. If your theme has\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/making-configurable-theme-settings}{theme\nsettings}, they are defined in this file. For a full explanation of this\nfile, please see the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/definitions/liferay-look-and-feel_7_2_0.dtd.html}{Definitions\ndocs}.\n\n\\section{liferay-plugin-package.properties}\\label{liferay-plugin-package.properties}\n\nContains general properties for the theme.\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/importing-resources-with-a-theme}{Resources\nImporter} configuration settings are also placed in this file. For a\nfull explanation of the properties available for this file please see\nthe\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/liferay-plugin-package_7_2_0.properties.html}{7.2\nProperties documentation}.\n\n\\chapter{Freemarker Variable Reference\nGuide}\\label{freemarker-variable-reference-guide}\n\nBy default, FreeMarker templates have access to several variables\ndefined in\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-theme/frontend-theme-unstyled/src/main/resources/META-INF/resources/_unstyled/templates/init.ftl}{\\texttt{init.ftl}}\nthat you can use in your\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{themes}\nto access several theme objects, settings, and resources. Several of\nthese variables are listed below for reference:\n\n\\textbf{Common Variables}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nVariable\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{theme\\_display} & Returns the \\texttt{themeDisplay} Java Object\nand all its methods \\\\\n\\texttt{portlet\\_display} & Returns the \\texttt{portletDisplay} Java\nObject and all its methods \\\\\n\\texttt{layoutSet} & Returns the page set \\\\\n\\texttt{theme\\_timestamp} & Prints the date in the current locale with\nthe given format \\\\\n\\texttt{theme\\_settings} & Retrieves theme settings. See\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/making-configurable-theme-settings}{configurable\ntheme settings} for more information. \\\\\n\\texttt{root\\_css\\_class} & Returns the root CSS class which indicates\nthe direction of the page (\\texttt{ltr} (left-to-right) by default) \\\\\n\\texttt{css\\_class} & Returns a string of the current classes applied to\nthe body of the page \\\\\n\\texttt{page\\_group} & Retrieves the page group \\\\\n\\texttt{css\\_folder} & Returns the path to the theme's \\texttt{css}\nfolder \\\\\n\\texttt{images\\_folder} & Returns the path to the theme's\n\\texttt{images} folder \\\\\n\\texttt{javascript\\_folder} & Returns the path to the theme's\n\\texttt{javascript} folder \\\\\n\\texttt{templates\\_folder} & Returns the path to the theme's\n\\texttt{templates} folder \\\\\n\\texttt{full\\_css\\_path} & Returns the full path, which includes the\nservlet context, to the theme's \\texttt{css} \\\\\n\\texttt{full\\_templates\\_path} & returns the full path, which includes\nthe servlet context, to the theme's \\texttt{templates} \\\\\n\\texttt{css\\_main\\_file} & Returns the path to \\texttt{main.css} \\\\\n\\texttt{js\\_main\\_file} & Returns the path to \\texttt{main.js} \\\\\n\\texttt{company\\_id} & Returns the company ID \\\\\n\\texttt{company\\_name} & Returns the company name \\\\\n\\texttt{company\\_logo} & Returns the company logo's URL \\\\\n\\texttt{company\\_logo\\_height} & Returns the company logo's height \\\\\n\\texttt{company\\_logo\\_width} & Returns the company logo's width \\\\\n\\texttt{company\\_url} & Returns the URL of the home page for the\ncompany \\\\\n\\texttt{time\\_zone} & Returns the time zone for the current user \\\\\n\\texttt{is\\_login\\_redirect\\_required} & Returns whether a login\nredirect is required for the user \\\\\n\\texttt{is\\_signed\\_in} & Returns whether the user is signed in \\\\\n\\texttt{group\\_id} & Returns the group ID for the current user \\\\\n\\texttt{time\\_zone} & Returns the time zone for the current user \\\\\n\\texttt{is\\_default\\_user} & Returns if the user has a default role \\\\\n\\texttt{is\\_female} & Returns if the current user is Female \\\\\n\\texttt{is\\_male} & Returns if the current user is Male \\\\\n\\texttt{is\\_setup\\_complete} & Returns whether the user has configured\ntheir profile \\\\\n\\texttt{language} & Returns the native language for the current user \\\\\n\\texttt{language\\_id} & Returns the ID of the current locale \\\\\n\\texttt{user\\_birthday} & Returns the current user's birthday \\\\\n\\texttt{user\\_comments} & Returns comments from the user's profile \\\\\n\\texttt{user\\_email\\_address} & Returns the user's email address \\\\\n\\texttt{user\\_first\\_name} & Returns the user's first name \\\\\n\\texttt{user\\_greeting} & Returns the user's greeting \\\\\n\\texttt{user\\_id} & Returns the ID of the current user \\\\\n\\texttt{user\\_last\\_login\\_ip} & Returns the IP address that the user\nlast logged in from \\\\\n\\texttt{user\\_last\\_name} & Returns the last name of the current user \\\\\n\\texttt{user\\_login\\_ip} & Returns the current user's current IP\naddress \\\\\n\\texttt{user\\_middle\\_name} & Returns the user's middle name \\\\\n\\texttt{user\\_name} & Returns the current user's username \\\\\n\\texttt{w3c\\_language\\_id} & Returns the W3C language code of the\ncurrent language \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{URLs}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nVariable\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{show\\_control\\_panel} & Returns whether the current user has\npermission to view the Control Panel \\\\\n\\texttt{control\\_panel\\_text} & Returns the ``control-panel'' language\nkey in the current user's locale, if they have permission to view the\nControl Panel \\\\\n\\texttt{control\\_panel\\_url} & Returns the URL to the Control Panel, if\nthe current user has permission to view the Control Panel \\\\\n\\texttt{show\\_home} & Returns whether the current user is on a page \\\\\n\\texttt{home\\_text} & Returns the ``home'' language key in the current\nuser's locale \\\\\n\\texttt{home\\_url} & Returns the URL to the home page \\\\\n\\texttt{show\\_my\\_account} & Returns whether the current user's account\nicon is visible \\\\\n\\texttt{my\\_account\\_text} & Returns the ``my-account'' language key in\nthe current user's locale, if the user's account icon is visible \\\\\n\\texttt{my\\_account\\_url} & Returns the URL to the user's Account\nSettings page if the user's account icon is visible \\\\\n\\texttt{show\\_sign\\_in} & Returns whether the sign in link is visible \\\\\n\\texttt{sign\\_in\\_text} & Returns the ``sign-in'' language key in the\ncurrent user's locale, if they are signed out \\\\\n\\texttt{sign\\_in\\_url} & Returns the sign in URL, if the current user is\nsigned out \\\\\n\\texttt{show\\_sign\\_out} & Returns whether the sign out link is\nvisible \\\\\n\\texttt{sign\\_out\\_text} & Returns the ``sign-out'' language key in the\ncurrent user's locale, if they are signed in \\\\\n\\texttt{sign\\_out\\_url} & Returns the sign out URL, if the current user\nis signed in \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Page}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nVariable\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{the\\_title} & Returns the current page's title \\\\\n\\texttt{selectable} & Returns whether the current page is selectable \\\\\n\\texttt{is\\_maximized} & Returns whether the page is maximized \\\\\n\\texttt{page} & Returns the current page (layout) \\\\\n\\texttt{is\\_first\\_child} & Returns whether the current page is the\nfirst child page in the navigation \\\\\n\\texttt{is\\_first\\_parent} & Returns whether the current page is the\nfirst parent page in the navigation \\\\\n\\texttt{is\\_portlet\\_page} & Returns whether the current page is a\nwidget page (portlet) \\\\\n\\texttt{site\\_name} & Returns the site's name \\\\\n\\texttt{is\\_guest\\_group} & Returns whether the current page group is\nfor guests \\\\\n\\texttt{site\\_type} & Returns the type of the current site: site,\ncompany site, organization site, or user site \\\\\n\\texttt{site\\_default\\_url} & Returns the default URL for the site \\\\\n\\texttt{layout\\_friendly\\_url} & Returns the friendly URL of the current\npage \\\\\n\\texttt{portlet\\_id} & Returns the portlet ID for the specified\nportlet \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Logo}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nVariable\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{logo\\_css\\_class} & Returns a string of the current classes\napplied to the logo. \\\\\n\\texttt{use\\_company\\_logo} & Returns whether the logo is displayed \\\\\n\\texttt{site\\_logo\\_height} & Returns the logo's height \\\\\n\\texttt{site\\_logo\\_width} & Returns the logo's width \\\\\n\\texttt{show\\_site\\_name\\_supported} & Returns whether the logo is\nconfigured to show the site name. The value is \\texttt{true} if\n\\texttt{show\\_site\\_name\\_default} is true. \\\\\n\\texttt{show\\_site\\_name\\_default} & Returns whether the Show Site Name\nDefault theme setting is enabled \\\\\n\\texttt{show\\_site\\_name} & Returns whether the \\texttt{showSiteName}\nproperty for the current pageset is enabled \\\\\n\\texttt{logo\\_description} & Returns the Site's name or nothing if\n\\texttt{show\\_site\\_name} is enabled. It is used for alternate text for\nthe logo by default. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Navigation}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nVariable\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{has\\_navigation} & Returns whether navigation exists (i.e.~at\nleast one page exists) \\\\\n\\texttt{nav\\_items} & Returns the current pages as list \\\\\n\\texttt{nav\\_css\\_class} & Returns a string of the current classes\napplied to the page's navigation \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{My Sites}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nVariable\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{show\\_my\\_sites} & Returns whether the current user has a My\nSites page \\\\\n\\texttt{show\\_my\\_places} & Returns whether the current user has a My\nSites page \\\\\n\\texttt{my\\_sites\\_text} & Returns the ``my-sites'' language key in the\ncurrent user's locale \\\\\n\\texttt{my\\_places\\_text} & Returns whether the current user has a My\nSites page \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Includes}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nVariable\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{dir\\_include} & Returns ``/html'' \\\\\n\\texttt{body\\_bottom\\_include} & Returns\n``\\({dir_include}/common/themes/body_bottom.jsp\" |\n `body_top_include` | Returns \"\\)\\{dir\\_include\\}/common/themes/body\\_top.jsp'' \\\\\n\\texttt{bottom\\_include} & Returns\n``\\({dir_include}/common/themes/bottom.jsp\" |\n `top_head_include` | Returns \"\\)\\{dir\\_include\\}/common/themes/top\\_head.jsp'' \\\\\n\\texttt{top\\_messages\\_include} & Returns\n``\\$\\{dir\\_include\\}/common/themes/top\\_messages.jsp'' \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Date}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nVariable\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{date} & Gives access to the \\texttt{dateUtil} Java Object and\nall its methods \\\\\n\\texttt{current\\_time} & Returns the current time \\\\\n\\texttt{the\\_year} & Returns the current year \\\\\n\\end{longtable}\n\n\\chapter{Gradle Plugins}\\label{gradle-plugins}\n\nLiferay provides plugins that you can apply to your Gradle project. This\nreference documentation describes how to apply and use Liferay's Gradle\nplugins.\n\n\\textbf{Important:} If you're using\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace} to create Liferay apps, most of the Liferay Gradle plugins\ncovered in this section are already applied by default. The\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-workspace}{com.liferay.gradle.plugins.workspace}\nand\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins}{com.liferay.gradle.plugins}\ndependencies provide them, both of which are preset in workspace by\ndefault.\n\nDo not apply a Liferay Gradle plugin to an app that already has access\nto it.\n\nEach article in this section describes how to apply the plugin, what\nGradle tasks the plugin provides, the plugin's configuration properties,\nand the plugin's dependencies.\n\n\\chapter{App Javadoc Builder Gradle\nPlugin}\\label{app-javadoc-builder-gradle-plugin}\n\nThe App Javadoc Builder Gradle plugin lets you generate API\ndocumentation as a single, combined HTML document for an application\nthat spans different subprojects, each one representing a different\ncomponent of the same application.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage}\n\nTo use the plugin, include it in the build script of the root project:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.app.javadoc.builder\", version: \"1.2.2\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.app.javadoc.builder\"\n\\end{verbatim}\n\nThe App Javadoc Builder plugin automatically applies the\n\\href{https://docs.gradle.org/current/userguide/standard_plugins.html\\#N135C1}{\\texttt{base}}\nand \\texttt{reporting-base} plugins.\n\n\\section{Project Extension}\\label{project-extension}\n\nThe App Javadoc Builder plugin exposes the following properties through\nthe extension named \\texttt{appJavadocBuilder}:\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{copyTags} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to copy the custom block tags\nconfiguration from the subprojects. It sets the Javadoc\n\\href{http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html\\#tag}{\\texttt{-tag}}\nargument for the \\hyperref[appjavadoc]{\\texttt{appJavadoc}} task.\n\\texttt{doclintDisabled} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} on JDK8+, \\texttt{false} otherwise. \\textbar{} Whether to\nignore Javadoc errors. It sets the Javadoc\n\\href{docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html\\#BEJEFABE}{\\texttt{-Xdoclint}}\nand\n\\href{http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html\\#CHDGFHAA}{\\texttt{-quiet}}\narguments for the \\hyperref[appjavadoc]{\\texttt{appJavadoc}} task.\n\\texttt{groupNameClosure} \\textbar{}\n\\texttt{Closure\\textless{}String\\textgreater{}} \\textbar{} The\nsubproject's description, or the subproject's name if the description is\nempty. \\textbar{} The closure invoked in order to get the group heading\nfor a subproject. The given closure is passed a\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html}{\\texttt{Project}}\nas its parameter. If \\texttt{groupPackages} is \\texttt{false}, this\nproperty is not used. \\texttt{groupPackages} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{true} \\textbar{} Whether to separate packages on the\noverview page based on the subprojects they belong to. It sets the\n\\href{docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html\\#CHDIGGII}{\\texttt{-group}}\nargument for the \\hyperref[appjavadoc]{\\texttt{appJavadoc}} task.\n\\texttt{subprojects} \\textbar{}\n\\texttt{Set\\textless{}Project\\textgreater{}} \\textbar{}\n\\texttt{project.subprojects} \\textbar{} The subprojects to include in\nthe API documentation of the app.\n\nThe same extension exposes the following methods:\n\nMethod \\textbar{} Description\n\\texttt{AppJavadocBuilderExtension\\ onlyIf(Closure\\textless{}Boolean\\textgreater{}\\ onlyIfClosure)}\n\\textbar{} Includes a subproject in the API documentation if the given\nclosure returns \\texttt{true}. The closure is evaluated at the end of\nthe subproject configuration phase and is passed a single parameter: the\nsubproject. If the closure returns \\texttt{false}, the subproject is not\nincluded in the API documentation.\n\\texttt{AppJavadocBuilderExtension\\ onlyIf(Spec\\textless{}Project\\textgreater{}\\ onlyIfSpec)}\n\\textbar{} Includes a subproject in the API documentation if the given\nspec is satisfied. The spec is evaluated at the end of the subproject\nconfiguration phase. If the spec is not satisfied, the subproject is not\nincluded in the API documentation.\n\\texttt{AppJavadocBuilderExtension\\ subprojects(Iterable\\textless{}Project\\textgreater{}\\ subprojects)}\n\\textbar{} Include additional projects in the API documentation of the\napp.\n\\texttt{AppJavadocBuilderExtension\\ subprojects(Project...\\ subprojects)}\n\\textbar{} Include additional projects in the API documentation of the\napp.\n\n\\section{Tasks}\\label{tasks}\n\nThe plugin adds two tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{appJavadoc} \\textbar{} The \\texttt{javadoc} tasks of the\nsubprojects. \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html}{\\texttt{Javadoc}}\n\\textbar{} Generates Javadoc API documentation for the app.\n\\texttt{jarAppJavadoc} \\textbar{} \\texttt{appJavadoc} \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html}{\\texttt{Jar}}\n\\textbar{} Assembles a JAR archive containing the Javadoc files for this\napp.\n\nThe \\texttt{appJavadoc} task is automatically configured with sensible\ndefaults:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html\\#org.gradle.api.tasks.javadoc.Javadoc:classpath}{\\texttt{classpath}}\n\\textbar{} The \\texttt{javadoc.classpath} of all the subprojects.\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html\\#org.gradle.api.tasks.javadoc.Javadoc:destinationDir}{\\texttt{destinationDir}}\n\\textbar{} \\texttt{\\$\\{project.buildDir\\}/docs/javadoc}\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/external/javadoc/MinimalJavadocOptions.html\\#getEncoding()}{\\texttt{options.encoding}}\n\\textbar{} \\texttt{\"UTF-8\"}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html\\#org.gradle.api.tasks.javadoc.Javadoc:source}{\\texttt{source}}\n\\textbar{} The \\texttt{javadoc.source} of all the subprojects.\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html\\#org.gradle.api.tasks.javadoc.Javadoc:title}{\\texttt{title}}\n\\textbar{} \\texttt{project.reporting.apiDocTitle}\n\n\\chapter{Baseline Gradle Plugin}\\label{baseline-gradle-plugin}\n\nThe Baseline Gradle plugin lets you verify that the OSGi\n\\href{http://semver.org/}{semantic versioning} rules are obeyed by your\nOSGi bundle.\n\nWhen you run the \\hyperref[baseline]{\\texttt{baseline}} task, the plugin\n\\emph{baselines} the new bundle against the latest released non-snapshot\nbundle (i.e., the \\emph{baseline}). That is, it compares the public\nexported API of the new bundle with the baseline. If there are any\nchanges, it uses the OSGi semantic versioning rules to calculate the\nminimum new version. If the new bundle has a lower version, errors are\nthrown.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-1}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.baseline\", version: \"2.1.0\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.baseline\"\n\\end{verbatim}\n\nThe Baseline plugin automatically applies the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nand\n\\href{https://docs.gradle.org/current/userguide/standard_plugins.html\\#sec:base_plugins}{\\texttt{reporting-base}}\nplugins.\n\nSince the plugin needs to download the baseline, you have to configure a\n\\href{https://docs.gradle.org/current/userguide/artifact_dependencies_tutorial.html\\#sec:repositories_tutorial}{repository}\nthat hosts it; for example, the central Maven 2 repository:\n\n\\begin{verbatim}\nrepositories {\n    mavenCentral()\n}\n\\end{verbatim}\n\n\\section{Project Extension}\\label{project-extension-1}\n\nThe Baseline plugin exposes the following properties through the\n\\texttt{baselineConfiguration} extension:\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{allowMavenLocal} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{false} \\textbar{} Whether to let the baseline come\nfrom the local Maven cache (by default: \\texttt{\\$\\{user.home\\}/.m2}).\nIf the local Maven cache is not\n\\href{https://docs.gradle.org/current/userguide/dependency_management.html\\#sub:maven_local}{configured}\nas a project repository, this property has no effect.\n\\texttt{lowestBaselineVersion} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"1.0.0\"} \\textbar{} The greatest project version to ignore for\nthe baseline check. If the\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html\\#org.gradle.api.tasks.bundling.Jar:version}{project\nversion} is less than or equal to the value of this property, the\n\\hyperref[baseline]{\\texttt{baseline}} task is skipped.\n\\texttt{lowestMajorVersion} \\textbar{} \\texttt{Integer} \\textbar{}\nContent of the file\n\\texttt{\\$\\{project.projectDir\\}/.lfrbuild-lowest-major-version}, where\nthe default file name can be changed by setting the project property\n\\texttt{baseline.lowest.major.version.file}. \\textbar{} The lowest major\nversion of the released artifact to use in the baseline check.\n\\texttt{lowestMajorVersionRequired} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{false} \\textbar{} Whether to fail the build if the\n\\hyperref[lowestmajorversion]{\\texttt{lowestMajorVersion}} is not\nspecified.\n\nIf the \\texttt{lowestMajorVersion} is not specified, the plugin runs the\ncheck using the most recent released non-snapshot bundle as baseline,\nwhich matches the\n\\href{http://ant.apache.org/ivy/history/latest-milestone/settings/version-matchers.html}{version\nrange} \\texttt{(,\\$\\{project.version\\})}. Otherwise, if the\n\\texttt{lowestMajorVersion} is equal to a value \\texttt{L} and the\nproject has version \\texttt{M.x.y} (with \\texttt{L} less or equal than\n\\texttt{M}), multiple checks are performed in order, using the following\nversion ranges as baseline:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  \\texttt{{[}L.0.0,\\ (L\\ +\\ 1).0.0)}\n\\item\n  \\texttt{{[}(L\\ +\\ 1).0.0,\\ (L\\ +\\ 2).0.0)}\n\\item\n  \\ldots{}\n\\item\n  \\texttt{{[}(M\\ -\\ 2).0.0,\\ (M\\ -\\ 1).0.0)}\n\\item\n  \\texttt{{[}(M\\ -\\ 1).0.0,\\ M.0.0)}\n\\item\n  \\texttt{{[}M.0.0,\\ M.x.y)}\n\\end{enumerate}\n\nThe first failing check fails the whole build.\n\n\\section{Tasks}\\label{tasks-1}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{baseline} \\textbar{}\n\\href{(https://docs.gradle.org/current/userguide/java_plugin.html\\#sec:jar)}{\\texttt{jar}}\n\\textbar{} \\hyperref[baselinetask]{\\texttt{BaselineTask}} \\textbar{}\nCompares the public API of this project with the public API of the\nprevious released version, if found.\n\nThe \\texttt{baseline} task is automatically configured with sensible\ndefaults:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[baselineconfiguration]{\\texttt{baselineConfiguration}}\n\\textbar{}\n\\hyperref[baseline-dependency]{\\texttt{configurations.baseline}}\n\\hyperref[bndfile]{\\texttt{bndFile}} \\textbar{}\n\\texttt{\\$\\{project.projectDir\\}/bnd.bnd}\n\\hyperref[newjarfile]{\\texttt{newJarFile}} \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html\\#org.gradle.api.tasks.bundling.Jar:archivePath}{\\texttt{project.tasks.jar.archivePath}}\n\\hyperref[sourcedir]{\\texttt{sourceDir}} \\textbar{} The first\n\\texttt{resources} directory of the \\texttt{main} source set (by\ndefault: \\texttt{src/main/resources}).\n\n\\section{BaselineTask}\\label{baselinetask}\n\n\\subsection{Task Properties}\\label{task-properties}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{baselineConfiguration} \\textbar{}\n\\texttt{Configuration} \\textbar{} \\texttt{null} \\textbar{} The\nconfiguration that contains exactly one dependency to the baseline\nbundle. \\texttt{bndFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The BND file of the project. If provided, the\ntask will automatically update the\n\\href{http://bnd.bndtools.org/heads/bundle_version.html}{\\texttt{Bundle-Version}}\nheader. \\texttt{forceCalculatedVersion} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{false} \\textbar{} Whether to fail the baseline check\nif the \\texttt{Bundle-Version} has been excessively increased.\n\\texttt{ignoreExcessiveVersionIncreases} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{false} \\textbar{} Whether to ignore excessive package\nversion increase warnings. \\texttt{ignoreFailures} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether the build\nshould not break when semantic versioning errors are found.\n\\texttt{logFile} \\textbar{} \\texttt{File} \\textbar{} \\texttt{null}\n\\textbar{} The file to which the results of the baseline check are\nwritten. \\emph{(Read-only)} \\texttt{logFileName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\"baseline/\\$\\{task.name\\}.log\"}\n\\textbar{} The name of the file to which the results of the baseline\ncheck are written. If the \\texttt{reporting-base} plugin is applied, the\nfile name is relative to\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.reporting.ReportingExtension.html\\#org.gradle.api.reporting.ReportingExtension:baseDir}{\\texttt{reporting.baseDir}};\notherwise, it's relative to the project directory. \\texttt{newJarFile}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The file of\nthe new OSGi bundle. \\texttt{reportDiff} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{true} if the project property\n\\texttt{baseline.jar.report.level} has either value \\texttt{\"diff\"} or\n\\texttt{\"persist\"}; \\texttt{false} otherwise \\textbar{} Whether to show\na granular, differential report of all changes that occurred in the\nexported packages of the OSGi bundle. \\texttt{reportOnlyDirtyPackages}\n\\textbar{} \\texttt{boolean} \\textbar{} Value of the project property\n\\texttt{baseline.jar.report.only.dirty.packages} if specified;\n\\texttt{true} otherwise. \\textbar{} Whether to show only packages with\nAPI changes in the report. \\texttt{sourceDir} \\textbar{} \\texttt{File}\n\\textbar{} \\texttt{null} \\textbar{} The directory to which the\n\\href{http://bnd.bndtools.org/chapters/170-versioning.html\\#versioning-packages}{\\texttt{packageinfo}}\nfiles are generated or updated.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties to defer evaluation until task execution.\n\n\\section{Helper Tasks}\\label{helper-tasks}\n\nIf the \\hyperref[lowestmajorversion]{\\texttt{lowestMajorVersion}}\nproperty is specified with a value \\texttt{L}, the plugin creates a\nseries of helper tasks of type\n\\hyperref[baselinetask]{\\texttt{BaselineTask}} at the end of the\n\\href{https://docs.gradle.org/current/userguide/build_lifecycle.html\\#N11BAE}{project\nevaluation}, one for each major version between \\texttt{L} and the major\nversion \\texttt{M} of the project:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Task \\texttt{baseline\\$\\{L\\ +\\ 1\\}}, which depends on\n  \\texttt{baseline\\$\\{L\\ +\\ 2\\}} and uses the version range\n  \\texttt{{[}(L\\ +\\ 1).0.0,\\ (L\\ +\\ 2).0.0)} as baseline.\n\\item\n  Task \\texttt{baseline\\$\\{L\\ +\\ 2\\}}, which depends on\n  \\texttt{baseline\\$\\{L\\ +\\ 3\\}} and uses the version range\n  \\texttt{{[}(L\\ +\\ 2).0.0,\\ (L\\ +\\ 3).0.0)} as baseline.\n\\item\n  \\ldots{}\n\\item\n  Task \\texttt{baseline\\$\\{M\\ -\\ 2\\}}, which depends on\n  \\texttt{baseline\\$\\{M\\ -\\ 1\\}} and uses the version range\n  \\texttt{{[}(M\\ -\\ 2).0.0,\\ (M\\ -\\ 1).0.0)} as baseline.\n\\item\n  Task \\texttt{baseline\\$\\{M\\ -\\ 1\\}}, which depends on\n  \\texttt{baseline\\$\\{M\\}} and uses the version range\n  \\texttt{{[}(M\\ -\\ 1).0.0,\\ M.0.0)} as baseline.\n\\item\n  Task \\texttt{baseline\\$\\{M\\}}, which uses the version range\n  \\texttt{{[}M.0.0,\\ M.x.y)} as baseline.\n\\end{enumerate}\n\nThe \\texttt{baseline} task is also configured to use the version range\n\\texttt{{[}L.0.0,\\ (L\\ +\\ 1).0.0)} as baseline, and to depend on the\ntask \\texttt{baseline\\$\\{L\\ +\\ 1\\}}. This means that running the\n\\texttt{baseline} task runs the baseline check against multiple\nversions, starting from the most recent \\texttt{M} and going back to\n\\texttt{L}.\n\nMoreover, all tasks except \\texttt{baseline\\$\\{M\\}} have the property\n\\hyperref[ignoreexcessiveversionincreases]{\\texttt{ignoreExcessiveVersionIncreases}}\nset to \\texttt{true}.\n\n\\section{Additional Configuration}\\label{additional-configuration}\n\nThere are additional configurations that can help you baseline your OSGi\nbundle.\n\n\\section{Baseline Dependency}\\label{baseline-dependency}\n\nThe plugin creates a configuration called \\texttt{baseline} with a\ndefault dependency to a released non-snapshot version of the bundle:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  version range \\texttt{{[}L.0.0,\\ (L\\ +\\ 1).0.0)} if the\n  \\hyperref[lowestmajorversion]{\\texttt{lowestMajorVersion}} property is\n  specified with a value \\texttt{L}.\n\\item\n  version range \\texttt{(,\\$\\{project.version\\})} otherwise.\n\\end{itemize}\n\nIt is possible to override this setting and use a different version of\nthe bundle as baseline.\n\n\\section{System Properties}\\label{system-properties}\n\nIt is possible to set the default values of the\n\\hyperref[ignorefailures]{\\texttt{ignoreFailures}} property for a\n\\texttt{BaselineTask} task via system properties:\n\n\\begin{verbatim}\n-D${task.name}.ignoreFailures=true\n\\end{verbatim}\n\nFor example, run the following Bash command to execute the baseline\ncheck without breaking the build, in case of errors:\n\n\\begin{verbatim}\n./gradlew baseline -Dbaseline.ignoreFailures=true\n\\end{verbatim}\n\n\\chapter{Change Log Builder Gradle\nPlugin}\\label{change-log-builder-gradle-plugin}\n\nThe Change Log Builder Gradle plugin lets you generate and maintain a\nchange log file based on the Git commits in your project. A change log\nfile generated by this plugin looks like this\n\n\\begin{verbatim}\n#\n# Bundle Version 1.0.1\n#\n9c77ff4c95cb1a325db3bdd089be105206e8b63c^..b421f00ac84b065685b131833fecc594fc01c760=LPS-123 LPS-1321\n\n#\n# Bundle Version 1.0.2\n#\nb421f00ac84b065685b131833fecc594fc01c760^..bc15d8d84e12b9544f78e4e3743c510dbaec2d89=LPS-456\n\\end{verbatim}\n\nEvery time the \\hyperref[buildchangelog]{\\texttt{buildChangeLog}} task\nis executed, a new line is added to the change log, which lists all Git\n\\hyperref[ticketidprefixes]{commit prefixes} (usually issue ticket IDs)\nthat occurred in a certain range. The end of the range is always the tip\nof the current branch. The start range can vary, depending on the case:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  If \\texttt{buildChangeLog} has never been executed for the project,\n  the change log does not exist. Therefore, the most recent commit from\n  two years ago is used for the range start.\n\\item\n  If a change log already exists for your project, the start range\n  begins at the range end of the last line in the change log.\n\\end{itemize}\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-2}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.change.log.builder\", version: \"1.1.3\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.change.log.builder\"\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-2}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{buildChangeLog} \\textbar{} - \\textbar{}\n\\hyperref[buildchangelogtask]{\\texttt{BuildChangeLogTask}} \\textbar{}\nBuilds the change log file for this project.\n\nThe \\texttt{buildChangeLog} task is automatically configured with\nsensible defaults, depending on whether the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin is applied:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[changelogheader]{\\texttt{changeLogHeader}} \\textbar{}\n\\texttt{\"Bundle\\ Version\\ \\$\\{project.version\\}\"}\n\\hyperref[changelogfile]{\\texttt{changeLogFile}} \\textbar{}\n\n\\textbf{If the \\texttt{java} plugin is applied:} The\n\\texttt{META-INF/liferay-releng.changelog} file in the first\n\\texttt{resources} directory of the \\texttt{main} source set (by\ndefault, \\texttt{src/main/resources/META-INF/liferay-releng.changelog}).\n\n\\textbf{Otherwise:}\n\\texttt{\"\\$\\{project.projectDir\\}/liferay-releng.changelog\"}\n\n\\hyperref[dirs]{\\texttt{dirs}} \\textbar{}\n\\texttt{{[}project.projectDir{]}}\n\n\\section{BuildChangeLogTask}\\label{buildchangelogtask}\n\n\\subsection{Task Properties}\\label{task-properties-1}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{changeLogFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The change log file to build.\n\\texttt{changeLogHeader} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The header for the new line in the change log.\n\\texttt{dirs} \\textbar{} \\texttt{FileCollection} \\textbar{}\n\\texttt{{[}{]}} \\textbar{} The directories to consider when listing the\ncommits in the range specified. \\texttt{gitDir} \\textbar{} \\texttt{File}\n\\textbar{} \\texttt{project.rootDir} \\textbar{} The base directory to\nstart searching for the \\texttt{.git} directory. The search proceeds in\nall the ancestors of the directory specified. \\texttt{rangeEnd}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The hash\nof the last commit to consider. If not set, it corresponds to the range\nend of the last line in the change log, or the most recent commit from\nat least two years ago if the change log file does not exist yet.\n\\texttt{rangeStart} \\textbar{} \\texttt{String} \\textbar{} \\texttt{null}\n\\textbar{} The hash of the first commit to consider. If not set, it\ncorresponds to the hash of the tip of the current branch.\n\\texttt{ticketIdPrefixes} \\textbar{}\n\\texttt{Set\\textless{}String\\textgreater{}} \\textbar{}\n\\texttt{{[}\"CLDSVCS\",\\ \"LPS\",\\ \"SOS\",\\ \"SYNC\"{]}} \\textbar{} The valid\nprefix of the Git commit messages to add to the change log. For example,\nif a commit message is \\texttt{\"LPS-123\\ Bugfix\"}, \\texttt{\"LPS-123\"}\nwill be added to the change log.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties to defer evaluation until task execution.\n\n\\subsection{Task Methods}\\label{task-methods}\n\nMethod \\textbar{} Description\n\\texttt{BuildChangeLogTask\\ dirs(Iterable\\textless{}?\\textgreater{}\\ dirs)}\n\\textbar{} Adds directories to consider when listing the commits in the\nrange specified. \\texttt{BuildChangeLogTask\\ dirs(Object...\\ dirs)}\n\\textbar{} Adds directories to consider when listing the commits in the\nrange specified.\n\\texttt{BuildChangeLogTask\\ ticketIdPrefixes(Iterable\\textless{}String\\textgreater{}\\ ticketIdPrefixes)}\n\\textbar{} Adds valid prefixes of the Git commit messages to add to the\nchange log.\n\\texttt{BuildChangeLogTask\\ ticketIdPrefixes(String...\\ ticketIdPrefixes)}\n\\textbar{} Adds valid prefixes of the Git commit messages to add to the\nchange log.\n\n\\chapter{CSS Builder Gradle Plugin}\\label{css-builder-gradle-plugin}\n\nThe CSS Builder Gradle plugin lets you run the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/css-builder}{Liferay\nCSS Builder} tool to compile \\href{http://sass-lang.com/}{Sass} files in\nyour project.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-3}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.css.builder\", version: \"3.0.0\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.css.builder\"\n\\end{verbatim}\n\nSince the plugin automatically resolves the Liferay CSS Builder library\nas a dependency, you have to configure a repository that hosts the\nlibrary and its transitive dependencies. The Liferay CDN repository\nhosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-3}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{buildCSS} \\textbar{} - \\textbar{}\n\\hyperref[buildcsstask]{\\texttt{BuildCSSTask}} \\textbar{} Compiles the\nSass files in this project.\n\nThe plugin also adds the following dependencies to tasks defined by the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin:\n\nName \\textbar{} Depends On \\texttt{processResources} \\textbar{}\n\\texttt{buildCSS}\n\nThe \\texttt{buildCSS} task is automatically configured with sensible\ndefaults, depending on whether the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nor the\n\\href{https://docs.gradle.org/current/userguide/war_plugin.html}{\\texttt{war}}\nplugins are applied:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[basedir]{\\texttt{baseDir}} \\textbar{}\n\n\\textbf{If the \\texttt{java} plugin is applied:} The first\n\\texttt{resources} directory of the \\texttt{main} source set (by\ndefault: \\texttt{src/main/resources}).\n\n\\textbf{If the \\texttt{war} plugin is applied:}\n\\texttt{project.webAppDir}.\n\n\\textbf{Otherwise:} \\texttt{null}\n\n\\section{BuildCSSTask}\\label{buildcsstask}\n\nTasks of type \\texttt{BuildCSSTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.css.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}},\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args}{\\texttt{args}}\n\\textbar{} CSS Builder command line arguments\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[liferay-css-builder-dependency]{\\texttt{project.configurations.cssBuilder}}\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/JavaExec.html\\#setDefaultCharacterEncoding(java.lang.String)}{\\texttt{defaultCharacterEncoding}}\n\\textbar{} \\texttt{\"UTF-8\"}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{} \\texttt{\"com.liferay.css.builder.CSSBuilder\"}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:systemProperties}{\\texttt{systemProperties}}\n\\textbar{} \\texttt{{[}\"sass.compiler.jni.clean.temp.dir\",\\ true{]}}\n\n\\subsection{Task Properties}\\label{task-properties-2}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{appendCssImportTimestamps} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{true} \\textbar{} Whether to append\nthe current timestamp to the URLs in the \\texttt{@import} CSS at-rules.\nIt sets the \\texttt{sass.append.css.import.timestamps} argument.\n\\texttt{baseDir} \\textbar{} \\texttt{File} \\textbar{} \\texttt{null}\n\\textbar{} The base directory that contains the SCSS files to compile.\nIt sets the \\texttt{sass.docroot.dir} argument. \\texttt{cssFiles}\n\\textbar{} \\texttt{FileCollection} \\textbar{} - \\textbar{} The SCSS\nfiles to compile. \\emph{(Read-only)} \\texttt{dirNames} \\textbar{}\n\\texttt{List\\textless{}String\\textgreater{}} \\textbar{}\n\\texttt{{[}\"/\"{]}} \\textbar{} The name of the directories, relative to\n\\hyperref[basedir]{\\texttt{baseDir}}, which contain the SCSS files to\ncompile. All sub-directories are searched for SCSS files as well. It\nsets the \\texttt{sass.dir} argument. \\texttt{generateSourceMap}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto generate\n\\href{https://developers.google.com/web/tools/chrome-devtools/debug/readability/source-maps}{source\nmaps} for easier debugging. It sets the\n\\texttt{sass.generate.source.map} argument. \\texttt{importDir}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The\n\\texttt{META-INF/resources} directory of the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-css/frontend-css-common}{Liferay\nFrontend Common CSS} artifact. This is required in order to make\n\\href{http://bourbon.io}{Bourbon} and other CSS libraries available to\nthe compilation. \\texttt{importFile} \\textbar{} \\texttt{File} \\textbar{}\n\\hyperref[liferay-frontend-common-css-dependency]{\\texttt{configurations.portalCommonCSS.singleFile}}\n\\textbar{} The Liferay Frontend Common CSS JAR file. If\n\\hyperref[importdir]{\\texttt{importDir}} is set, this property has no\neffect. \\texttt{importPath} \\textbar{} \\texttt{File} \\textbar{} -\n\\textbar{} The value of the \\texttt{importDir} property if set;\notherwise \\texttt{importFile}. It sets the\n\\texttt{sass.portal.common.path} argument. \\emph{(Read-only)}\n\\texttt{outputDirName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\".sass-cache/\"} \\textbar{} The name of the sub-directories where\nthe SCSS files are compiled to. For each directory that contains SCSS\nfiles, a sub-directory with this name is created. It sets the\n\\texttt{sass.output.dir} argument. \\texttt{outputDirs} \\textbar{}\n\\texttt{FileCollection} \\textbar{} - \\textbar{} The directories where\nthe SCSS files are compiled to. Usually, these directories are ignored\nby the Version Control System. \\emph{(Read-only)} \\texttt{precision}\n\\textbar{} \\texttt{int} \\textbar{} \\texttt{5} \\textbar{} The numeric\nprecision of numbers in Sass. It sets the \\texttt{sass.precision}\nargument. \\texttt{rtlExcludedPathRegexps} \\textbar{}\n\\texttt{List\\textless{}String\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The SCSS file patterns to exclude when converting for\nright-to-left (RTL) support. It sets the\n\\texttt{sass.rtl.excluded.path.regexps} argument.\n\\texttt{sassCompilerClassName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The type of Sass compiler to use. Supported\nvalues are \\texttt{\"jni\"} and \\texttt{\"ruby\"}. If not set, defaults to\n\\texttt{\"jni\"}. It sets the \\texttt{sass.compiler.class.name} argument.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Liferay's CSS Builder is supported for Oracle's JDK and\nuses a native compiler for increased speed. If you're using an IBM JDK,\nyou may experience issues when building your Sass files (e.g., when\nbuilding a theme). It's recommended to switch to using the Oracle JDK,\nbut if you prefer using the IBM JDK, you must use the fallback Ruby\ncompiler. You can do this two ways:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  If you're working in a\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\n  Workspace} or using the\n  \\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins}{Liferay\n  Gradle Plugins} plugin, set \\texttt{sass.compiler.class.name=ruby} in\n  your \\texttt{gradle.properties} file.\n\\item\n  Otherwise, set\n  \\texttt{buildCSS.sassCompilerClassName=\\textquotesingle{}ruby\\textquotesingle{}}\n  in the project's \\texttt{build.gradle} file.\n\\end{itemize}\n\nThe \\texttt{sass.compiler.class.name=ruby} Gradle property only works\nfor modules, so if you're using the Ruby compiler in a WAR project\n(e.g., theme), you must use the second option.\n\nBe aware that the Ruby-based compiler doesn't perform as well as the\nnative compiler, so expect longer compile times.\n\n\\noindent\\hrulefill\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{int} and \\texttt{String} properties, to defer evaluation until\ntask execution.\n\n\\subsection{Task Methods}\\label{task-methods-1}\n\nMethod \\textbar{} Description\n\\texttt{BuildCSSTask\\ dirNames(Iterable\\textless{}Object\\textgreater{}\\ dirNames)}\n\\textbar{} Adds sub-directory names, relative to\n\\hyperref[basedir]{\\texttt{baseDir}}, which contain the SCSS files to\ncompile. \\texttt{BuildCSSTask\\ dirNames(Object...\\ dirNames)} \\textbar{}\nAdds sub-directory names, relative to\n\\hyperref[basedir]{\\texttt{baseDir}}, which contain the SCSS files to\ncompile.\n\\texttt{BuildCSSTask\\ rtlExcludedPathRegexps(Iterable\\textless{}Object\\textgreater{}\\ rtlExcludedPathRegexps)}\n\\textbar{} Adds SCSS file patterns to exclude when converting for\nright-to-left (RTL) support.\n\\texttt{BuildCSSTask\\ rtlExcludedPathRegexps(Object...\\ rtlExcludedPathRegexps)}\n\\textbar{} Adds SCSS file patterns to exclude when converting for\nright-to-left (RTL) support.\n\n\\section{Additional Configuration}\\label{additional-configuration-1}\n\nThere are additional configurations that can help you use the CSS\nBuilder.\n\n\\section{Liferay CSS Builder\nDependency}\\label{liferay-css-builder-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{cssBuilder} and adds a dependency to the latest released version\nof the Liferay CSS Builder. It is possible to override this setting and\nuse a specific version of the tool by manually adding a dependency to\nthe \\texttt{cssBuilder} configuration:\n\n\\begin{verbatim}\ndependencies {\n    cssBuilder group: \"com.liferay\", name: \"com.liferay.css.builder\", version: \"3.0.0\"\n}\n\\end{verbatim}\n\n\\section{Liferay Frontend Common CSS\nDependency}\\label{liferay-frontend-common-css-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{portalCommonCSS} and adds a dependency to the latest released\nversion of the Liferay Frontend Common CSS artifact. It is possible to\noverride this setting and use a specific version of the artifact by\nmanually adding a dependency to the \\texttt{portalCommonCSS}\nconfiguration:\n\n\\begin{verbatim}\ndependencies {\n    portalCommonCSS group: \"com.liferay\", name: \"com.liferay.frontend.css.common\", version: \"2.0.1\"\n}\n\\end{verbatim}\n\n\\chapter{DB Support Gradle Plugin}\\label{db-support-gradle-plugin}\n\nThe DB Support Gradle plugin lets you run the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-db-support}{Liferay\nDB Support} tool to execute certain actions on a local Liferay database.\nSo far, the following actions are available:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Cleans the Liferay database from the Service Builder tables and rows\n  of a module.\n\\end{itemize}\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-4}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.db.support\", version: \"1.0.5\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.portal.tools.db.support\"\n\\end{verbatim}\n\nSince the plugin automatically resolves the Liferay DB Support library\nas a dependency, you have to configure a repository that hosts the\nlibrary and its transitive dependencies. The Liferay CDN repository\nhosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-4}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{cleanServiceBuilder} \\textbar{} - \\textbar{}\n\\hyperref[cleanservicebuildertask]{\\texttt{CleanServiceBuilderTask}}\n\\textbar{} Cleans the Liferay database from the Service Builder tables\nand rows of a module.\n\nThe \\texttt{cleanServiceBuilder} task is automatically configured with\nsensible defaults, depending on whether the\n\\href{https://docs.gradle.org/current/userguide/standard_plugins.html\\#N135C1}{\\texttt{base}}\nplugin is applied:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[servletcontextname]{\\texttt{servletContextName}} \\textbar{}\n\n\\textbf{If the \\texttt{base} plugin is applied:} The bundle symbolic\nname of the project inferred via the\n\\href{https://github.com/gradle/gradle/blob/master/subprojects/osgi/src/main/java/org/gradle/api/internal/plugins/osgi/OsgiHelper.java}{\\texttt{OsgiHelper}}\nclass.\n\n\\textbf{Otherwise:} \\texttt{null}\n\n\\hyperref[servicexmlfile]{\\texttt{serviceXmlFile}} \\textbar{}\n\\texttt{\"\\$\\{project.projectDir\\}/service.xml\"}\n\n\\section{CleanServiceBuilderTask}\\label{cleanservicebuildertask}\n\nTasks of type \\texttt{BuildDeploymentHelperTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.lang.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}},\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args}{\\texttt{args}}\n\\textbar{} The DB Support command line arguments.\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[jdbc-drivers-dependency]{\\texttt{project.configurations.dbSupport}}\n+\n\\hyperref[liferay-db-support-dependency]{\\texttt{project.configurations.dbSupportTool}}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{} \\texttt{\"com.liferay.portal.tools.db.support.DBSupport\"}\n\n\\subsection{Task Properties}\\label{task-properties-3}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{password} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The user password for connecting to the Liferay\ndatabase. It sets the \\texttt{-\\/-password} argument. If\n\\hyperref[propertiesfile]{\\texttt{propertiesFile}} is set, this property\nhas no effect. \\texttt{propertiesFile} \\textbar{} \\texttt{File}\n\\textbar{} \\texttt{null} \\textbar{} The \\texttt{portal-ext.properties}\nfile that contains the JDBC settings for connecting to the Liferay\ndatabase. It sets the \\texttt{-\\/-properties-file} argument.\n\\texttt{servletContextName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The servlet context name (usually the value of\nthe \\texttt{Bundle-Symbolic-Name} manifest header) of the module. It\nsets the \\texttt{-\\/-servlet-context-name} argument.\n\\texttt{serviceXmlFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The \\texttt{service.xml} file of the module. It\nsets the \\texttt{-\\/-service-xml-file} argument. \\texttt{url} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The JDBC URL for\nconnecting to the Liferay database. It sets the \\texttt{-\\/-url}\nargument. If \\hyperref[propertiesfile]{\\texttt{propertiesFile}} is set,\nthis property has no effect. \\texttt{userName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The user name for\nconnecting to the Liferay database. It sets the \\texttt{-\\/-user-name}\nargument. If \\hyperref[propertiesfile]{\\texttt{propertiesFile}} is set,\nthis property has no effect.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{int} and \\texttt{String} properties to defer evaluation until\ntask execution.\n\n\\section{Additional Configuration}\\label{additional-configuration-2}\n\nThere are additional configurations that can help you use the Deployment\nHelper.\n\n\\section{JDBC Drivers Dependency}\\label{jdbc-drivers-dependency}\n\nThe plugin creates a configuration called \\texttt{dbSupport}, which can\nbe used to provide the suitable JDBC driver for your Liferay database:\n\n\\begin{verbatim}\ndependencies {\n    dbSupport group: \"mysql\", name: \"mysql-connector-java\", version: \"5.1.23\"\n    dbSupport group: \"org.mariadb.jdbc\", name: \"mariadb-java-client\", version: \"1.1.9\"\n    dbSupport group: \"org.postgresql\", name: \"postgresql\", version: \"9.4-1201-jdbc41\"\n}\n\\end{verbatim}\n\n\\section{Liferay DB Support\nDependency}\\label{liferay-db-support-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{dbSupportTool} and adds a dependency to the latest released\nversion of the Liferay DB Support. It is possible to override this\nsetting and use a specific version of the tool by manually adding a\ndependency to the \\texttt{dbSupportTool} configuration:\n\n\\begin{verbatim}\ndependencies {\n    dbSupportTool group: \"com.liferay\", name: \"com.liferay.portal.tools.db.support\", version: \"1.0.8\"\n}\n\\end{verbatim}\n\n\\chapter{Dependency Checker Gradle\nPlugin}\\label{dependency-checker-gradle-plugin}\n\nThe Dependency Checker Gradle plugin lets you warn users if a specific\nconfiguration dependency is not the latest one available from the Maven\ncentral repository. The plugin eventually fails the build if the\ndependency age (the difference between the timestamp of the current\nversion and the latest version) is above a predetermined threshold.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-5}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.dependency.checker\", version: \"1.0.3\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.dependency.checker\"\n\\end{verbatim}\n\n\\section{Project Extension}\\label{project-extension-2}\n\nThe Dependency Checker Gradle plugin exposes the following properties\nthrough the extension named \\texttt{dependencyChecker}:\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{ignoreFailures} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{true} \\textbar{} Whether to print an error message\ninstead of failing the build when the dependency check fails, either for\na network error or because the dependency is out-of-date.\n\nThe same extension exposes the following methods:\n\nMethod \\textbar{} Description\n\\texttt{void\\ maxAge(Map\\textless{}?,\\ ?\\textgreater{}\\ args)}\n\\textbar{} Declares the max age allowed for a dependency. The\n\\texttt{args} map must contain the following entries:\n\n\\texttt{configuration}: the configuration name\n\n\\texttt{group}: the dependency group\n\n\\texttt{name}: the dependency name\n\n\\texttt{maxAge}: an instance of\n\\href{http://docs.groovy-lang.org/latest/html/api/groovy/time/Duration.html}{\\texttt{groovy.time.Duration}}\nthat represents the maximum age allowed for the dependency\n\n\\texttt{throwError}: a \\texttt{boolean} value representing whether to\nthrow an error if the dependency is out-of-date\n\n\\section{Additional Configuration}\\label{additional-configuration-3}\n\nThere are additional configurations that can help you use the Deployment\nHelper.\n\n\\section{Project Properties}\\label{project-properties}\n\nIt is possible to set the default values of the\n\\hyperref[ignorefailures]{\\texttt{ignoreFailures}} property via the\nproject property \\texttt{dependencyCheckerIgnoreFailures}:\n\n\\begin{verbatim}\n-PdependencyCheckerIgnoreFailures=false\n\\end{verbatim}\n\n\\chapter{Deployment Helper Gradle\nPlugin}\\label{deployment-helper-gradle-plugin}\n\nThe Deployment Helper Gradle plugin lets you run the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/deployment-helper}{Liferay\nDeployment Helper} tool to create a cluster deployable WAR from your\nOSGi artifacts.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-6}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.deployment.helper\", version: \"1.0.5\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.deployment.helper\"\n\\end{verbatim}\n\nSince the plugin automatically resolves the Liferay Deployment Helper\nlibrary as a dependency, you have to configure a repository that hosts\nthe library and its transitive dependencies. The Liferay CDN repository\nhosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-5}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{buildDeploymentHelper} \\textbar{} - \\textbar{}\n\\hyperref[builddeploymenthelpertask]{\\texttt{BuildDeploymentHelperTask}}\n\\textbar{} Builds a WAR which contains one or more files that are copied\nonce the WAR is deployed.\n\n\\section{BuildDeploymentHelperTask}\\label{builddeploymenthelpertask}\n\nTasks of type \\texttt{BuildDeploymentHelperTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.lang.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}},\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args}{\\texttt{args}}\n\\textbar{} The Deployment Helper command line arguments.\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[liferay-deployment-helper-dependency]{\\texttt{project.configurations.deploymentHelper}}\n\\hyperref[deploymentfiles]{\\texttt{deploymentFiles}} \\textbar{} The\noutput files of the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html\\#sec:jar}{\\texttt{jar}}\ntasks of this project and all its sub-projects.\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{} \\texttt{\"com.liferay.deployment.helper.DeploymentHelper\"}\n\\hyperref[outputfile]{\\texttt{outputFile}} \\textbar{}\n\\texttt{\"\\$\\{project.buildDir\\}/\\$\\{project.name\\}.war\"}\n\n\\subsection{Task Properties}\\label{task-properties-4}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{deploymentFiles} \\textbar{} \\texttt{FileCollection}\n\\textbar{} \\texttt{{[}{]}} \\textbar{} The files or directories to\ninclude in the WAR and copy once the WAR is deployed. If a directory is\nadded to this collection, all the JAR files contained in the directory\nare included in the WAR. \\texttt{deploymentPath} \\textbar{}\n\\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The directory to which\nthe included files are copied. \\texttt{outputFile} \\textbar{}\n\\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The WAR file to build.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\n\n\\subsection{Task Methods}\\label{task-methods-2}\n\nMethod \\textbar{} Description\n\\texttt{BuildDeploymentHelperTask\\ deploymentFiles(Iterable\\textless{}?\\textgreater{}\\ deploymentFiles)}\n\\textbar{} Adds files or directories to include in the WAR and copy once\nthe WAR is deployed. The values are evaluated as per\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:files(java.lang.Object\\%5B\\%5D)}{\\texttt{project.files}}.\n\\texttt{BuildDeploymentHelperTask\\ deploymentFiles(Object...\\ deploymentFiles)}\n\\textbar{} Adds files or directories to include in the WAR and copy once\nthe WAR is deployed. The values are evaluated as per\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:files(java.lang.Object\\%5B\\%5D)}{\\texttt{project.files}}.\n\n\\section{Additional Configuration}\\label{additional-configuration-4}\n\nThere are additional configurations that can help you use the Deployment\nHelper.\n\n\\section{Liferay Deployment Helper\nDependency}\\label{liferay-deployment-helper-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{deploymentHelper} and adds a dependency to the latest released\nversion of the Liferay Deployment Helper. It is possible to override\nthis setting and use a specific version of the tool by manually adding a\ndependency to the \\texttt{deploymentHelper} configuration:\n\n\\begin{verbatim}\ndependencies {\n    deploymentHelper group: \"com.liferay\", name: \"com.liferay.deployment.helper\", version: \"1.0.4\"\n}\n\\end{verbatim}\n\n\\chapter{Go Gradle Plugin}\\label{go-gradle-plugin}\n\nThe Go Gradle plugin lets you run \\href{https://golang.org/}{Go} as part\nof your build.\n\nThe plugin has been successfully tested with Gradle 3.5.1 up to 4.10.2.\n\n\\section{Usage}\\label{usage-7}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.go\", version: \"1.0.0\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.go\"\n\\end{verbatim}\n\n\\section{Project Extension}\\label{project-extension-3}\n\nThe Go Gradle plugin exposes the following properties through the\nextension named \\texttt{go}:\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{goDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{\"\\$\\{project.buildDir\\}/go\"} \\textbar{} The directory where the\nGo distribution is unpacked. \\texttt{goUrl} \\textbar{} \\texttt{String}\n\\textbar{}\n\\texttt{\"https://dl.google.com/go/go\\$\\{go.goVersion\\}.\\$\\{platform\\}-\\$\\{bitMode\\}.\\$\\{extension\\}}\n\\textbar{} The URL of the Go distribution to download.\n\\texttt{goVersion} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"1.11.4\"} \\textbar{} The Go distribution's version to use.\n\\texttt{workingDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{\"\\$\\{project.projectDir\\}\"} \\textbar{} The directory that\ncontains the project's Go source code.\n\n\\section{Tasks}\\label{tasks-6}\n\nThe plugin adds a series of tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{downloadGo} \\textbar{} - \\textbar{}\n\\hyperref[downloadgotask]{\\texttt{DownloadGoTask}} \\textbar{} Downloads\nand unpacks the local Go distribution for the project.\n\\hyperref[gocommandprogramname-task]{\\texttt{goBuild\\$\\{programName\\}}}\n\\textbar{} \\texttt{downloadGo} \\textbar{}\n\\hyperref[executegotask]{\\texttt{ExecuteGoTask}} \\textbar{} Compiles\npackages and dependencies for the Go program.\n\\hyperref[gocommandprogramname-task]{\\texttt{goClean\\$\\{programName\\}}}\n\\textbar{} \\texttt{downloadGo} \\textbar{}\n\\hyperref[executegotask]{\\texttt{ExecuteGoTask}} \\textbar{} Removes\nobject files for the Go program.\n\\hyperref[gocommandprogramname-task]{\\texttt{goRun\\$\\{programName\\}}}\n\\textbar{} \\texttt{downloadGo} \\textbar{}\n\\hyperref[executegotask]{\\texttt{ExecuteGoTask}} \\textbar{} Compiles and\nruns the Go program.\n\\hyperref[gocommandprogramname-task]{\\texttt{goTest\\$\\{programName\\}}}\n\\textbar{} \\texttt{downloadGo} \\textbar{}\n\\hyperref[executegotask]{\\texttt{ExecuteGoTask}} \\textbar{} Tests\npackages for the Go program.\n\n\\section{DownloadGoTask}\\label{downloadgotask}\n\nThe purpose of this task is to download and unpack a Go distribution.\n\n\\subsection{Task Properties}\\label{task-properties-5}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{goDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The directory where the Go distribution is\nunpacked. \\texttt{goUrl} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The URL of the Go distribution to download.\n\nThe \\texttt{File} type support any type that can be resolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties, to defer evaluation until task execution.\n\n\\section{ExecuteGoTask}\\label{executegotask}\n\nThis is the base task to run Go in a Gradle build. All tasks of type\n\\texttt{ExecuteGoTask} automatically depend on\n\\hyperref[downloadgo]{\\texttt{downloadGo}}.\n\n\\subsection{Task Properties}\\label{task-properties-6}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{args} \\textbar{}\n\\texttt{List\\textless{}Object\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The arguments for the Go invocation. \\texttt{command}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{\"go\"} \\textbar{} The file\nname of the executable to invoke. \\texttt{environment} \\textbar{}\n\\texttt{Map\\textless{}Object,\\ Object\\textgreater{}} \\textbar{}\n\\texttt{{[}{]}} \\textbar{} The environment variables for the Go\ninvocation. \\texttt{inheritProxy} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to set the \\texttt{http\\_proxy},\n\\texttt{https\\_proxy}, and \\texttt{no\\_proxy} environment variables in\nthe Go invocation based on the values of the system properties\n\\texttt{https.proxyHost}, \\texttt{https.proxyPort},\n\\texttt{https.proxyUser}, \\texttt{https.proxyPassword},\n\\texttt{https.nonProxyHosts}, \\texttt{https.proxyHost},\n\\texttt{https.proxyPort}, \\texttt{https.proxyUser},\n\\texttt{https.proxyPassword}, and \\texttt{https.nonProxyHosts}. If these\nenvironment variables are already set, their values will not be\noverwritten. \\texttt{goDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{go.goDir}{]}(\\#godir) \\textbar{} The directory that contains the\nexecutable to invoke. \\texttt{useGradleExec} \\textbar{} \\texttt{boolean}\n\\textbar{}\n\n\\textbf{If running in a\n\\href{https://docs.gradle.org/current/userguide/gradle_daemon.html}{Gradle\nDaemon}:} \\texttt{true}\n\n\\textbf{Otherwise:} \\texttt{false}\n\n\\textbar{} Whether to invoke Go using\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:exec(org.gradle.api.Action)}{\\texttt{project.exec}},\nwhich can solve hanging problems with the Gradle Daemon.\n\\texttt{workingDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{go.workingDir}{]}(\\#workingdir) \\textbar{} The working directory\nto use in the Go invocation.\n\nThe type \\texttt{File} properties support any type that can be resolved\nby\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties to defer evaluation until task execution.\n\n\\subsection{Task Methods}\\label{task-methods-3}\n\nMethod \\textbar{} Description\n\\texttt{ExecuteGoTask\\ args(Iterable\\textless{}?\\textgreater{}\\ args)}\n\\textbar{} Adds arguments for the Go invocation.\n\\texttt{ExecuteGoTask\\ args(Object...\\ args)} \\textbar{} Adds arguments\nfor the Go invocation.\n\\texttt{ExecuteGoTask\\ environment(Map\\textless{}?,\\ ?\\textgreater{}\\ environment)}\n\\textbar{} Adds environment variables for the Go invocation.\n\\texttt{ExecuteGoTask\\ environment(Object\\ key,\\ Object\\ value)}\n\\textbar{} Adds an environment variable for the Go invocation.\n\n\\section{\\texorpdfstring{go\\({command}\\)\\{programName\\}\nTask}{go\\{command\\}\\{programName\\} Task}}\\label{gocommandprogramname-task}\n\nFor each Go program in\n\\hyperref[workingdirproperty]{\\texttt{workingDir}}, four tasks of type\n\\hyperref[executegotask]{\\texttt{ExecuteGoTask}} are added. Each of\nthese tasks are automatically configured with sensible defaults:\n\nProperty Name \\textbar{} Default Value \\texttt{args} \\textbar{}\n\\texttt{{[}\"\\$\\{command\\}\",\\ \"\\$\\{programFile.absolutePath\\}\"{]}}\n\n\\chapter{Gulp Gradle Plugin}\\label{gulp-gradle-plugin}\n\nThe Gulp Gradle plugin lets you run \\href{http://gulpjs.com/}{Gulp}\ntasks as part of your build.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-8}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.gulp\", version: \"2.0.59\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.gulp\"\n\\end{verbatim}\n\nThe Gulp plugin automatically applies the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-node}{\\texttt{com.liferay.node}}\nplugin.\n\n\\section{Tasks}\\label{tasks-7}\n\nThe plugin adds one\n\\href{https://docs.gradle.org/current/userguide/more_about_tasks.html\\#sec:task_rules}{task\nrule} to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{gulp\\textless{}Task\\textgreater{}} \\textbar{}\n\\texttt{downloadNode}, \\texttt{npmInstall} \\textbar{}\n\\hyperref[executegulptask]{\\texttt{ExecuteGulpTask}} \\textbar{} Executes\na named Gulp task.\n\n\\section{ExecuteGulpTask}\\label{executegulptask}\n\nTasks of type \\texttt{ExecuteGulpTask} extend\n\\href{/docs/7-2/reference/-/knowledge_base/r/node-gradle-plugin\\#executenodescripttask}{\\texttt{ExecuteNodeScriptTask}},\nso all its properties and methods, such as \\texttt{args} and\n\\texttt{inheritProxy}, are available. They also have the following\nproperties set by default:\n\nProperty Name \\textbar{} Default Value \\texttt{scriptFile} \\textbar{}\n\\texttt{\"node\\_modules/gulp/bin/gulp.js\"}\n\nGulp must be already installed in the \\texttt{node\\_modules} directory\nof the project; otherwise, it will not be downloaded by the task. In\norder to ensure Gulp is installed, you can add the Gulp dependency to\nthe project's \\texttt{package.json} file.\n\n\\subsection{Task Properties}\\label{task-properties-7}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{gulpCommand} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The Gulp task to execute.\n\nIt is possible to use Closures and Callables as values for the\n\\texttt{String} properties to defer evaluation until task execution.\n\n\\chapter{Jasper JSPC Gradle Plugin}\\label{jasper-jspc-gradle-plugin}\n\nThe Jasper JSPC Gradle plugin lets you run the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/jasper-jspc}{Liferay\nJasper JSPC} tool to compile the JavaServer Pages (JSP) files in your\nproject. This can be useful to\n\n\\begin{itemize}\n\\tightlist\n\\item\n  check for errors in the JSP files.\n\\item\n  pre-compile the JSP files for better performance.\n\\end{itemize}\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-9}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.jasper.jspc\", version: \"2.0.5\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.jasper.jspc\"\n\\end{verbatim}\n\nThe Jasper JSPC plugin automatically applies the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin.\n\nSince the plugin automatically resolves the Liferay Jasper JSPC library\nas a dependency, you have to configure a repository that hosts the\nlibrary and its transitive dependencies. The Liferay CDN repository\nhosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-8}\n\nThe plugin adds two tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{compileJSP} \\textbar{} \\texttt{generateJSPJava} \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html}{\\texttt{JavaCompile}}\n\\textbar{} Compiles JSP files to check for errors.\n\\texttt{generateJSPJava} \\textbar{}\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html\\#sec:jar}{\\texttt{jar}}\n\\textbar{} \\hyperref[compilejsptask]{\\texttt{CompileJSPTask}} \\textbar{}\nCompiles JSP files to Java source files to check for errors.\n\nThe \\texttt{generateJSPJava} task is automatically configured with\nsensible defaults, depending on whether the\n\\href{https://docs.gradle.org/current/userguide/war_plugin.html}{\\texttt{war}}\nplugin is applied:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[liferay-jasper-jspc-dependency]{\\texttt{project.configurations.jspCTool}}\n\\hyperref[destinationdir]{\\texttt{destinationDir}} \\textbar{}\n\\texttt{\"\\$\\{project.buildDir\\}/jspc\"}\n\\hyperref[jspcclasspath]{\\texttt{jspCClasspath}} \\textbar{}\n\\hyperref[jsp-compilation-classpath]{\\texttt{project.configurations.jspC}}\n\\hyperref[webappdir]{\\texttt{webAppDir}} \\textbar{}\n\n\\textbf{If the \\texttt{war} plugin is applied:}\n\\texttt{project.webAppDir}.\n\n\\textbf{Otherwise:} The first \\texttt{resources} directory of the\n\\texttt{main} source set (by default, \\texttt{src/main/resources}).\n\nThe \\texttt{compileJSP} task is also configured with the following\ndefaults:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html\\#org.gradle.api.tasks.compile.JavaCompile:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\texttt{project.configurations.jspCTool\\ +\\ project.configurations.jspC}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html\\#org.gradle.api.tasks.compile.JavaCompile:destinationDir}{\\texttt{destinationDir}}\n\\textbar{} \\texttt{compileJSP.temporaryDir}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html\\#org.gradle.api.tasks.compile.JavaCompile:source}{\\texttt{source}}\n\\textbar{} \\texttt{generateJSPJava.outputs}\n\n\\section{CompileJSPTask}\\label{compilejsptask}\n\nTasks of type \\texttt{CompileJSPTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.css.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}},\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{} \\texttt{\"com.liferay.jasper.jspc.JspC\"}\n\n\\subsection{Task Properties}\\label{task-properties-8}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{destinationDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The directory where the the JSP files are\ncompiled to. Package directories are automatically generated based on\nthe directories containing the uncompiled JSP files. It sets the\n\\texttt{-d} argument. \\texttt{jspCClasspath} \\textbar{}\n\\texttt{FileCollection} \\textbar{} \\texttt{null} \\textbar{} The\nclasspath to use for the JSP files compilation. \\texttt{webAppDir}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The\ndirectory containing the web application. All JSP files in the directory\nand its subdirectories are compiled. It sets the \\texttt{-webapp}\nargument.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\n\n\\section{Additional Configuration}\\label{additional-configuration-5}\n\nThere are additional configurations that can help you use Jasper JSPC.\n\n\\section{JSP Compilation Classpath}\\label{jsp-compilation-classpath}\n\nThe plugin creates a configuration called \\texttt{jspC} and adds several\ndependencies at the end of the configuration phase of the project:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  the JAR file of the project generated by the\n  \\href{https://docs.gradle.org/current/userguide/java_plugin.html\\#sec:jar}{\\texttt{jar}}\n  task.\n\\item\n  the output files of the \\texttt{main} source set.\n\\item\n  the \\texttt{compileClasspath} file collection of the \\texttt{main}\n  source set.\n\\end{itemize}\n\nIf necessary, it is possible to add more dependencies to the\n\\texttt{jspC} configuration.\n\n\\section{Liferay Jasper JSPC\nDependency}\\label{liferay-jasper-jspc-dependency}\n\nBy default, the plugin creates a configuration called \\texttt{jspCTool}\nand adds a dependency to the latest released version of the Liferay\nJasper JSPC. It is possible to override this setting and use a specific\nversion of the tool by manually adding a dependency to the\n\\texttt{jspCTool} configuration:\n\n\\begin{verbatim}\ndependencies {\n    jspCTool group: \"com.liferay\", name: \"com.liferay.jasper.jspc\", version: \"1.0.11\"\n    jspCTool group: \"org.apache.ant\", name: \"ant\", version: \"1.9.4\"\n}\n\\end{verbatim}\n\n\\chapter{Javadoc Formatter Gradle\nPlugin}\\label{javadoc-formatter-gradle-plugin}\n\nThe Javadoc Formatter Gradle plugin lets you format project Javadoc\ncomments using the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/javadoc-formatter}{Liferay\nJavadoc Formatter tool}. The tool lets you generate:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Default\n  \\href{http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html\\#@author}{\\texttt{@author}}\n  tags to all classes.\n\\item\n  Comment stubs to classes, fields, and methods.\n\\item\n  Missing\n  \\href{https://docs.oracle.com/javase/8/docs/api/java/lang/Override.html}{\\texttt{@Override}}\n  annotations.\n\\item\n  An XML representation of the Javadoc comments, which can be used by\n  tools in order to index the Javadocs of the project.\n\\end{itemize}\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-10}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.javadoc.formatter\", version: \"1.0.27\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.javadoc.formatter\"\n\\end{verbatim}\n\nSince the plugin automatically resolves the Liferay Javadoc Formatter\nlibrary as a dependency, you have to configure a repository that hosts\nthe library and its transitive dependencies. The Liferay CDN repository\nhosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-9}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{formatJavadoc} \\textbar{} - \\textbar{}\n\\hyperref[formatjavadoctask]{\\texttt{FormatJavadocTask}} \\textbar{} Runs\nthe Liferay Javadoc Formatter to format files.\n\n\\section{FormatJavadocTask}\\label{formatjavadoctask}\n\nTasks of type \\texttt{FormatJavadocTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, like\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.lang.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}},\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args}{\\texttt{args}}\n\\textbar{} Javadoc Formatter command line arguments\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[liferay-javadoc-formatter-dependency]{\\texttt{project.configurations.javadocFormatter}}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{} \\texttt{\"com.liferay.javadoc.formatter.JavadocFormatter\"}\n\n\\subsection{Task Properties}\\label{task-properties-9}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{author} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"Brian\\ Wing\\ Shun\\ Chan\"} \\textbar{} The value of the\n\\texttt{@author} tag to add at class level if missing. It sets the\n\\texttt{javadoc.author} argument. \\texttt{generateXML} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether to\ngenerate a XML representation of the Javadoc comments. The XML files are\ngenerated in the \\texttt{src/main/resources} directory only if the Java\nfiles are contained in \\texttt{src/main/java}. It sets the\n\\texttt{javadoc.generate.xml} argument.\n\\texttt{initializeMissingJavadocs} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{false} \\textbar{} Whether to add comment stubs at the\nclass, field, and method levels. If \\texttt{false}, only the class-level\n\\texttt{@author} is added. It sets the \\texttt{javadoc.init} argument.\n\\texttt{limits} \\textbar{} \\texttt{List\\textless{}String\\textgreater{}}\n\\textbar{} \\texttt{{[}{]}} \\textbar{} The Java file name patterns,\nrelative to\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:workingDir}{\\texttt{workingDir}},\nto include when formatting Javadoc comments. The patterns must be\nspecified without the \\texttt{.java} file type suffix. If empty, all\nJava files are formatted. It sets the \\texttt{javadoc.limit} argument.\n\\texttt{lowestSupportedJavaVersion} \\textbar{} \\texttt{double}\n\\textbar{} \\texttt{1.7} \\textbar{} If a method is annotated with the\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/util/javadoc-formatter/src/main/java/com/liferay/javadoc/formatter/SinceJava.java}{\\texttt{@SinceJava}}\nannotation and its \\texttt{value} argument is greater than the value\nspecified for the \\texttt{lowestSupportedJavaVersion} property, then the\n\\texttt{@Override} annotation is not automatically added, even if it is\nmissing. It sets the \\texttt{javadoc.lowest.supported.java.version}\nargument. See\n\\href{https://issues.liferay.com/browse/LPS-37353}{LPS-37353}.\n\\texttt{outputFilePrefix} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"javadocs\"} \\textbar{} The file name prefix of the XML\nrepresentation of the Javadoc comments. If \\texttt{generateXML} is\n\\texttt{false}, this property is not used. It sets the\n\\texttt{javadoc.output.file.prefix} argument. \\texttt{updateJavadocs}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto fix existing comment blocks by adding missing tags. It sets the\n\\texttt{javadoc.update} argument.\n\nIt is possible to use Closures and Callables as values for the\n\\texttt{String} properties, to defer evaluation until task execution.\n\n\\subsection{Task Methods}\\label{task-methods-4}\n\nMethod \\textbar{} Description\n\\texttt{FormatJavadocTask\\ dirNames(Iterable\\textless{}Object\\textgreater{}\\ limits)}\n\\textbar{} Adds Java file name patterns, relative to\n\\texttt{workingDir}, to include when formatting Javadoc comments.\n\\texttt{FormatJavadocTask\\ dirNames(Object...\\ limits)} \\textbar{} Adds\nJava file name patterns, relative to \\texttt{workingDir}, to include\nwhen formatting Javadoc comments.\n\n\\section{Additional Configuration}\\label{additional-configuration-6}\n\nThere are additional configurations that can help you use the Javadoc\nFormatter.\n\n\\section{Liferay Javadoc Formatter\nDependency}\\label{liferay-javadoc-formatter-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{javadocFormatter} and adds a dependency to the latest released\nversion of the Liferay Javadoc Formatter. It is possible to override\nthis setting and use a specific version of the tool by manually adding a\ndependency to the \\texttt{javadocFormatter} configuration:\n\n\\begin{verbatim}\ndependencies {\n    javadocFormatter group: \"com.liferay\", name: \"com.liferay.javadoc.formatter\", version: \"1.0.32\"\n}\n\\end{verbatim}\n\nIf the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin is applied, the \\texttt{javadocFormatter} configuration\nautomatically extends from the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html\\#sec:java_plugin_and_dependency_management}{\\texttt{compile}}\nconfiguration.\n\n\\section{System Properties}\\label{system-properties-1}\n\nIt is possible to set the default values of the \\texttt{generateXML},\n\\texttt{initializeMissingJavadocs}, \\texttt{limits}, and\n\\texttt{updateJavadocs} properties for a \\texttt{FormatJavadocTask} task\nvia system properties:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{-D\\$\\{task.name\\}.generate.xml=true}\n\\item\n  \\texttt{-D\\$\\{task.name\\}.init=SomeClassName1,SomeClassName2,com.liferay.portal.**}\n\\item\n  \\texttt{-D\\$\\{task.name\\}.limit=**/com/example/}\n\\item\n  \\texttt{-D\\$\\{task.name\\}.update=true}\n\\end{itemize}\n\n\\chapter{JS Module Config Generator Gradle\nPlugin}\\label{js-module-config-generator-gradle-plugin}\n\nThe JS Module Config Generator Gradle plugin lets you run the\n\\href{https://github.com/liferay/liferay-module-config-generator}{Liferay\nAMD Module Config Generator} to generate the configuration file needed\nto load AMD files via combo loader in Liferay.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-11}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.js.module.config.generator\", version: \"2.1.57\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.js.module.config.generator\"\n\\end{verbatim}\n\nThe JS Module Config Generator plugin automatically applies the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-node}{\\texttt{com.liferay.node}}\nplugin.\n\n\\section{Project Extension}\\label{project-extension-4}\n\nThe JS Module Config Generator plugin exposes the following properties\nthrough the extension named \\texttt{jsModuleConfigGenerator}:\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{version} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"1.2.1\"} \\textbar{} The version of the Liferay AMD Module Config\nGenerator to use.\n\n\\section{Tasks}\\label{tasks-10}\n\nThe plugin adds two tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{configJSModules} \\textbar{}\n\\texttt{downloadLiferayModuleConfigGenerator}, \\texttt{processResources}\n\\textbar{} \\hyperref[configjsmodulestask]{\\texttt{ConfigJSModulesTask}}\n\\textbar{} Generates the configuration file needed to load AMD files via\ncombo loader in Liferay. \\texttt{downloadLiferayModuleConfigGenerator}\n\\textbar{} \\texttt{downloadNode} \\textbar{}\n\\texttt{DownloadNodeModuleTask} \\textbar{} Downloads the Liferay AMD\nModule Config Generator in the project's \\texttt{node\\_modules}\ndirectory.\n\nBy default, the \\texttt{downloadLiferayModuleConfigGenerator} task\ndownloads the version of \\texttt{liferay-module-config-generator}\ndeclared in the\n\\hyperref[version]{\\texttt{jsModuleConfigGenerator.version}} property.\nIf the project's \\texttt{package.json} file, however, already lists the\n\\texttt{liferay-module-config-generator} package in its\n\\texttt{dependencies} or \\texttt{devDependencies}, the\n\\texttt{downloadLiferayModuleConfigGenerator} task is disabled.\n\nThe \\texttt{configJSModules} task is automatically configured with\nsensible defaults, depending on whether the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin is applied:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[moduleconfigfile]{\\texttt{moduleConfigFile}} \\textbar{}\n\\texttt{\"\\$\\{project.projectDir\\}/package.json\"}\n\\hyperref[outputfile]{\\texttt{outputFile}} \\textbar{}\n\\texttt{\"\\$\\{sourceSets.main.output.resourcesDir\\}/META-INF/config.json\"}\n\\hyperref[sourcedir]{\\texttt{sourceDir}} \\textbar{}\n\\texttt{\"\\$\\{sourceSets.main.output.resourcesDir\\}/META-INF/resources\"}\n\nThe plugin also adds the following dependencies to tasks defined by the\n\\texttt{java} plugin:\n\nName \\textbar{} Depends On \\texttt{classes} \\textbar{}\n\\texttt{configJSModules}\n\nIf the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-js-transpiler}{\\texttt{com.liferay.js.transpiler}}\nplugin is applied, the \\texttt{configJSModules} task is configured to\nalways run after the \\texttt{transpileJS} task.\n\n\\section{ConfigJSModulesTask}\\label{configjsmodulestask}\n\nTasks of type \\texttt{ConfigJSModulesTask} extend\n\\texttt{ExecuteNodeScriptTask}, so all its properties and methods, such\nas \\texttt{args}, \\texttt{inheritProxy}, and \\texttt{workingDir}, are\navailable. The \\texttt{ConfigJSModulesTask} instances also implement the\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/util/PatternFilterable.html}{\\texttt{PatternFilterable}}\ninterface, which lets you specify include and exclude patterns for the\nfiles in \\hyperref[sourcedir]{\\texttt{sourceDir}} to process.\n\nThey also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/util/PatternFilterable.html\\#getIncludes()}{\\texttt{includes}}\n\\textbar{} \\texttt{{[}\"**/*.es.js*\",\\ \"**/*.soy.js*\"{]}}\n\\texttt{scriptFile} \\textbar{}\n\\texttt{\"\\$\\{downloadLiferayModuleConfigGenerator.moduleDir\\}/bin/index.js\"}\n\nThe purpose of this task is to run the Liferay AMD Module Config\nGenerator from the included files in\n\\hyperref[sourcedir]{\\texttt{sourceDir}}. The generator processes these\nfiles and creates a configuration file in the location specified by the\n\\hyperref[outputfile]{\\texttt{outputFile}} property.\n\n\\subsection{Task Properties}\\label{task-properties-10}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{configVariable} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{}\nThe~configuration~variable~to~which~the~modules~should~be~added. It sets\nthe \\texttt{-\\/-config} argument. \\texttt{customDefine} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\"Liferay.Loader\"} \\textbar{} The\nnamespace of the \\texttt{define(...)} call to use in the JS files. It\nsets the \\texttt{-\\/-namespace} argument. \\texttt{ignorePath} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether not to\ncreate module \\texttt{path} and \\texttt{fullPath} properties. It sets\nthe \\texttt{-\\/-ignorePath} argument. \\texttt{keepFileExtension}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto keep the file extension when generating the module name. It sets the\n\\texttt{-\\/-keepExtension} argument. \\texttt{lowerCase} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether to convert\nfile name to lower case before using it as the module name. It sets the\n\\texttt{-\\/-lowerCase} argument. \\texttt{moduleConfigFile} \\textbar{}\n\\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The JSON file which\ncontains configuration data for the modules. It sets the\n\\texttt{-\\/-moduleConfig} argument. \\texttt{moduleExtension} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The extension for\nthe module file (e.g., \\texttt{.js}). If specified, use the provided\nstring~as~an~extension~instead~to~get~it~automatically~from~the~file~name.\nIt sets the \\texttt{-\\/-extension} argument. \\texttt{moduleFormat}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The\nregular expression and value to apply to the file name when generating\nthe module name. It sets the \\texttt{-\\/-format} argument.\n\\texttt{outputFile} \\textbar{} \\texttt{File} \\textbar{} \\texttt{null}\n\\textbar{} The file where the generated configuration is stored. It sets\nthe \\texttt{-\\/-output} argument. \\texttt{sourceDir} \\textbar{}\n\\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The directory that\ncontains the files to process.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{int} and \\texttt{String} properties to defer evaluation until\ntask execution.\n\n\\chapter{JS Transpiler Gradle Plugin}\\label{js-transpiler-gradle-plugin}\n\nThe JS Transpiler Gradle plugin lets you run\n\\href{https://github.com/metal/metal-cli}{\\texttt{metal-cli}} to build\n\\href{http://metaljs.com/}{Metal.js} code, compile Soy files, and\ntranspile ES6 to ES5.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-12}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.js.transpiler\", version: \"2.4.36\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.js.transpiler\"\n\\end{verbatim}\n\nThere are two JS Transpiler Gradle plugins you can apply to your\nproject:\n\n\\begin{itemize}\n\\item\n  \\hyperref[js-transpiler-plugin]{\\emph{JS Transpiler Plugin}}: builds\n  Metal.js code, compiles Soy files, and transpiles ES6 to ES5:\n\n\\begin{verbatim}\napply plugin: \"com.liferay.js.transpiler\"\n\\end{verbatim}\n\\item\n  \\hyperref[js-transpiler-base-plugin]{\\emph{JS Transpiler Base\n  Plugin}}: provides a way to use Gradle dependencies (such as an\n  \\href{https://docs.gradle.org/current/userguide/dependency_management.html\\#sub:module_dependencies}{external\n  module} or\n  \\href{https://docs.gradle.org/current/userguide/dependency_management.html\\#sub:project_dependencies}{project\n  dependencies}) in Node.js scripts:\n\n\\begin{verbatim}\napply plugin: \"com.liferay.js.transpiler.base\"\n\\end{verbatim}\n\\end{itemize}\n\n\\section{JS Transpiler Plugin}\\label{js-transpiler-plugin}\n\nThe JS Transpiler plugin automatically applies the\n\\hyperref[js-transpiler-base-plugin]{\\emph{JS Transpiler Base Plugin}}.\n\nThe plugin adds two tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{downloadMetalCli} \\textbar{} \\texttt{downloadNode} \\textbar{}\n\\texttt{DownloadNodeModuleTask} \\textbar{} Downloads \\texttt{metal-cli}\nin the project's \\texttt{node\\_modules} directory. \\texttt{transpileJS}\n\\textbar{} \\texttt{downloadMetalCli},\n\\texttt{expandJSCompileDependencies}, \\texttt{npmInstall},\n\\texttt{processResources} \\textbar{}\n\\hyperref[transpilejstask]{\\texttt{TranspileJSTask}} \\textbar{} Builds\nMetal.js code.\n\nBy default, the \\texttt{downloadMetalCli} task downloads the version\n1.3.1 of \\texttt{metal-cli}. If the project's \\texttt{package.json}\nfile, however, already lists the \\texttt{metal-cli} package in its\n\\texttt{dependencies} or \\texttt{devDependencies}, the\n\\texttt{downloadMetalCli} task is disabled.\n\nThe \\texttt{transpileJS} task is automatically configured with sensible\ndefaults, depending on whether the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin is applied:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[sourcedir]{\\texttt{sourceDir}} \\textbar{} The directory\n\\texttt{META-INF/resources} in the first \\texttt{resources} directory of\nthe \\texttt{main} source set (by default,\n\\texttt{src/main/resources/META-INF/resources}). \\texttt{workingDir}\n\\textbar{}\n\\texttt{\"\\$\\{sourceSets.main.output.resourcesDir\\}/META-INF/resources\"}\n\nThe plugin also adds the following dependencies to tasks defined by the\n\\texttt{java} plugin:\n\nName \\textbar{} Depends On \\texttt{classes} \\textbar{}\n\\texttt{transpileJS}\n\nThe plugin adds a new configuration to the project called\n\\texttt{soyCompile}. If one or more dependencies are added to this\nconfiguration, they will be expanded into temporary directories and\npassed to the \\texttt{transpileJS} task as additional\n\\hyperref[soydependencies]{\\texttt{soyDependencies}} values.\n\n\\section{JS Transpiler Base Plugin}\\label{js-transpiler-base-plugin}\n\nThe JS Transpiler Base plugin automatically applies the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-node}{\\texttt{com.liferay.node}}\nplugin.\n\nThe plugin adds a new configuration to the project called\n\\texttt{jsCompile}. If one or more dependencies are added to this\nconfiguration, they will be expanded into sub-directories of the\n\\texttt{node\\_modules} directory, with names equal to the names of the\ndependencies.\n\nThe plugin also adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{expandJSCompileDependencies} \\textbar{} - \\textbar{}\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/api/DefaultTask.html}{\\texttt{DefaultTask}}\n\\textbar{} Expands the additional configured JavaScript dependencies.\nThe task itself does not do any work, but depends on a series of\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html}{Copy}\ntasks called \\texttt{expandJSCompileDependency\\$\\{file\\}}, which expand\neach dependency declared in the \\texttt{jsCompile} configuration into\nthe \\texttt{node\\_modules} directory.\n\nAll the tasks of type \\texttt{ExecuteNpmTask} whose name starts with\n\\texttt{\"npmRun\"} are configured to depend on\n\\texttt{expandJSCompileDependencies}. This means that, before running\nany \\href{https://docs.npmjs.com/misc/scripts}{script} declared in the\n\\texttt{package.json} file of the project, all the \\texttt{jsCompile}\ndependencies will be expanded into the \\texttt{node\\_modules} directory.\n\n\\section{Tasks}\\label{tasks-11}\n\n\\section{TranspileJSTask}\\label{transpilejstask}\n\nTasks of type \\texttt{TranspileJSTask} extend\n\\texttt{ExecuteNodeScriptTask}, so all its properties and methods, such\nas \\texttt{args}, \\texttt{inheritProxy}, and \\texttt{workingDir}, are\navailable. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value \\texttt{scriptFile} \\textbar{}\n\\texttt{\"\\$\\{downloadMetalCli.moduleDir\\}/index.js\"}\n\\texttt{soySrcIncludes} \\textbar{} \\texttt{{[}\"**/*.soy\"{]}}\n\\texttt{srcIncludes} \\textbar{}\n\\texttt{{[}\"**/*.es.js*\",\\ \"**/*.soy.js*\"{]}}\n\nThe purpose of this task is to run the \\texttt{build} command of\n\\texttt{metal-cli} to build Metal.js code from\n\\hyperref[sourcedir]{\\texttt{sourceDir}} into the \\texttt{workingDir}\ndirectory.\n\n\\subsection{Task Properties}\\label{task-properties-11}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{bundleFileName} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{} The name of the final bundle file\nfor formats (e.g., \\emph{globals}) that create one. It sets the\n\\texttt{-\\/-bundleFileName} argument. \\texttt{globalName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The name of the\nglobal variable that holds exported modules. It sets the\n\\texttt{-\\/-globalName} argument. This is only used by the\n\\emph{globals} format build. \\texttt{moduleName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The name of the\nproject that is being compiled. All built modules are stored in a folder\nwith this name. It sets the \\texttt{-\\/-moduleName} argument. This is\nonly used by the \\emph{amd} format build. \\texttt{modules} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\"amd\"} \\textbar{} The format(s) that\nthe source files are built to. It sets the \\texttt{-\\/-format} argument.\n\\texttt{skipWhenEmpty} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to disable the task and remove its\ndependencies if the \\hyperref[sourcefiles]{\\texttt{sourceFiles}}\nproperty does not return any file at the end of the project evaluation.\n\\texttt{sourceDir} \\textbar{} \\texttt{File} \\textbar{} \\texttt{null}\n\\textbar{} The directory that contains the files to build.\n\\texttt{sourceFiles} \\textbar{} \\texttt{FileCollection} \\textbar{}\n\\texttt{{[}{]}} \\textbar{} The Soy and JS files to compile.\n\\emph{(Read-only)} \\texttt{sourceMaps} \\textbar{} \\texttt{SourceMaps}\n\\textbar{} \\texttt{enabled} \\textbar{} Whether to generate source map\nfiles. Available values include \\texttt{disabled}, \\texttt{enabled}, and\n\\texttt{enabled\\_inline}. \\texttt{soyDependencies} \\textbar{}\n\\texttt{Set\\textless{}String\\textgreater{}} \\textbar{}\n\\texttt{{[}\"\\$\\{npmInstall.workingDir\\}/node\\_modules/clay*/src/**/*.soy\",\\ \"\\$\\{npmInstall.workingDir\\}/node\\_modules/metal*/src/**/*.soy\"{]}}\n\\textbar{} The path GLOBs of Soy files that the main source files depend\non, but that should not be compiled. It sets the \\texttt{-\\/-soyDeps}\nargument. \\texttt{soySkipMetalGeneration} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{false} \\textbar{} Whether to just compile Soy files,\nwithout adding Metal.js generated code, like the \\texttt{component}\nclass. It sets the \\texttt{-\\/-soySkipMetalGeneration} argument.\n\\texttt{soySrcIncludes} \\textbar{}\n\\texttt{Set\\textless{}String\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The path GLOBs of the Soy files to compile. It sets the\n\\texttt{-\\/-soySrc} argument. \\texttt{srcIncludes} \\textbar{}\n\\texttt{Set\\textless{}String\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The path GLOBs of the JS files to compile. It sets the\n\\texttt{-\\/-src} argument.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{int} and \\texttt{String} properties to defer evaluation until\ntask execution.\n\n\\subsection{Task Methods}\\label{task-methods-5}\n\nMethod \\textbar{} Description\n\\texttt{TranspileJSTask\\ soyDependency(Iterable\\textless{}?\\textgreater{}\\ soyDependencies)}\n\\textbar{} Adds path GLOBs of Soy files that the main source files\ndepend on, but that should not be compiled.\n\\texttt{TranspileJSTask\\ soyDependency(Object...\\ soyDependencies)}\n\\textbar{} Adds path GLOBs of Soy files that the main source files\ndepend on, but that should not be compiled.\n\\texttt{TranspileJSTask\\ soySrcInclude(Iterable\\textless{}?\\textgreater{}\\ soySrcIncludes)}\n\\textbar{} Adds path GLOBs of Soy files to compile.\n\\texttt{TranspileJSTask\\ soySrcInclude(Object...\\ soySrcIncludes)}\n\\textbar{} Adds path GLOBs of Soy files to compile.\n\\texttt{TranspileJSTask\\ srcInclude(Iterable\\textless{}?\\textgreater{}\\ srcIncludes)}\n\\textbar{} Adds path GLOBs of JS files to compile.\n\\texttt{TranspileJSTask\\ srcInclude(Object...\\ srcIncludes)} \\textbar{}\nAdds path GLOBs of JS files to compile.\n\n\\chapter{JSDoc Gradle Plugin}\\label{jsdoc-gradle-plugin}\n\nThe JSDoc Gradle plugin lets you run the\n\\href{http://usejsdoc.org/}{JSDoc} tool in order to generate\ndocumentation for your project's JavaScript files.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-13}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.jsdoc\", version: \"2.0.33\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\\end{verbatim}\n\nThere are two JSDoc Gradle plugins you can apply to your project:\n\n\\begin{itemize}\n\\item\n  Apply the \\hyperref[jsdoc-plugin]{JSDoc Plugin} to generate JavaScript\n  documentation for your project:\n\n\\begin{verbatim}\napply plugin: \"com.liferay.jsdoc\"\n\\end{verbatim}\n\\item\n  Apply the \\hyperref[appjsdoc-plugin]{App JSDoc Plugin} in a parent\n  project to generate the JavaScript documentation as a single, combined\n  HTML document for an application that spans different subprojects,\n  each one representing a different component of the same application:\n\n\\begin{verbatim}\napply plugin: \"com.liferay.app.jsdoc\"\n\\end{verbatim}\n\\end{itemize}\n\nBoth plugins automatically apply the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-node}{\\texttt{com.liferay.node}}\nplugin.\n\n\\section{JSDoc Plugin}\\label{jsdoc-plugin}\n\nThe plugin adds two tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{downloadJSDoc} \\textbar{} \\texttt{downloadNode} \\textbar{}\n\\texttt{DownloadNodeModuleTask} \\textbar{} Downloads JSDoc in the\nproject's \\texttt{node\\_modules} directory. \\texttt{jsdoc} \\textbar{}\n\\texttt{downloadJSDoc} \\textbar{}\n\\hyperref[jsdoctask]{\\texttt{JSDocTask}} \\textbar{} Generates API\ndocumentation for the project's JavaScript code.\n\nBy default, the \\texttt{downloadJSDoc} task downloads version\n\\texttt{3.5.5} of the \\texttt{jsdoc} package. If the project's\n\\texttt{package.json} file, however, already lists the \\texttt{jsdoc}\npackage in its \\texttt{dependencies} or \\texttt{devDependencies}, the\n\\texttt{downloadJSDoc} task is disabled.\n\nThe \\texttt{jsdoc} task is automatically configured with sensible\ndefaults, depending on whether the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin is applied:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[destinationdir]{\\texttt{destinationDir}} \\textbar{}\n\n\\textbf{If the \\texttt{java} plugin is applied:}\n\\texttt{\"\\$\\{project.docsDir\\}/jsdoc\"}\n\n\\textbf{Otherwise:} \\texttt{\"\\$\\{project.buildDir\\}/jsdoc\"}\n\n\\hyperref[sourcedirs]{\\texttt{sourceDirs}} \\textbar{} The directory\n\\texttt{META-INF/resources} in the first \\texttt{resources} directory of\nthe \\texttt{main} source set (by default,\n\\texttt{src/main/resources/META-INF/resources}).\n\n\\section{AppJSDoc Plugin}\\label{appjsdoc-plugin}\n\nTo use the App JSDoc plugin, it is required to apply the\n\\texttt{com.liferay.app.jsdoc} plugin in a parent project (that is, a\nproject that is a common ancestor of all the subprojects representing\nthe various components of the app). It is also required to apply the\n\\hyperref[jsdoc-plugin]{\\texttt{com.liferay.jsdoc}} plugin to all the\nsubprojects that contain JavaScript files.\n\nThe App JSDoc plugin adds three tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{appJSDoc} \\textbar{} \\texttt{downloadJSDoc} \\textbar{}\n\\hyperref[jsdoctask]{\\texttt{JSDocTask}} \\textbar{} Generates API\ndocumentation for the app's JavaScript code. \\texttt{downloadJSDoc}\n\\textbar{} \\texttt{downloadNode} \\textbar{}\n\\texttt{DownloadNodeModuleTask} \\textbar{} Downloads JSDoc in the app's\n\\texttt{node\\_modules} directory. \\texttt{jarAppJSDoc} \\textbar{}\n\\texttt{appJSDoc} \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html}{\\texttt{Jar}}\n\\textbar{} Assembles a JAR archive containing the JavaScript\ndocumentation files for this app.\n\nBy default, the \\texttt{downloadJSDoc} task downloads version\n\\texttt{3.5.5} of the \\texttt{jsdoc} package. If the project's\n\\texttt{package.json} file, however, already lists the \\texttt{jsdoc}\npackage in its \\texttt{dependencies} or \\texttt{devDependencies}, the\n\\texttt{downloadJSDoc} task is disabled.\n\nThe \\texttt{appJSDoc} task is automatically configured with sensible\ndefaults:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[destinationdir]{\\texttt{destinationDir}} \\textbar{}\n\\texttt{\\$\\{project.buildDir\\}/docs/jsdoc}\n\\hyperref[sourcedirs]{\\texttt{sourceDirs}} \\textbar{} The sum of all the\n\\texttt{jsdoc.sourceDirs} values of the subprojects.\n\n\\section{Project Extension}\\label{project-extension-5}\n\nThe App JSDoc plugin exposes the following properties through the\nextension named \\texttt{appJSDocConfiguration}:\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{subprojects} \\textbar{}\n\\texttt{Set\\textless{}Project\\textgreater{}} \\textbar{}\n\\texttt{project.subprojects} \\textbar{} The subprojects to include in\nthe JavaScript documentation of the app.\n\nThe same extension exposes the following methods:\n\nMethod \\textbar{} Description\n\\texttt{AppJSDocConfigurationExtension\\ subprojects(Iterable\\textless{}Project\\textgreater{}\\ subprojects)}\n\\textbar{} Include additional projects in the JavaScript documentation\nof the app.\n\\texttt{AppJSDocConfigurationExtension\\ subprojects(Project...\\ subprojects)}\n\\textbar{} Include additional projects in the JavaScript documentation\nof the app.\n\n\\section{Tasks}\\label{tasks-12}\n\n\\section{JSDocTask}\\label{jsdoctask}\n\nTasks of type \\texttt{JSDocTask} extend \\texttt{ExecuteNodeScriptTask},\nso all its properties and methods, such as \\texttt{args},\n\\texttt{inheritProxy}, and \\texttt{workingDir}, are available.\n\nThey also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value \\texttt{scriptFile} \\textbar{}\n\\texttt{\"\\$\\{downloadJSDoc.moduleDir\\}/jsdoc.js\"}\n\n\\subsection{Task Properties}\\label{task-properties-12}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{configuration} \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.resources.TextResource.html}{\\texttt{TextResource}}\n\\textbar{} \\texttt{null} \\textbar{} The JSDoc configuration file. It\nsets the \\texttt{-\\/-configure} argument. \\texttt{destinationDir}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The\ndirectory where the JavaScript API documentation files are saved. It\nsets the \\texttt{-\\/-destination} argument. \\texttt{packageJsonFile}\n\\textbar{} \\texttt{File} \\textbar{}\n\\texttt{\"\\$\\{project.projectDir\\}/package.json\"} \\textbar{} The path to\nthe project's package file. It sets the \\texttt{-\\/-package} argument.\n\\texttt{sourceDirs} \\textbar{} \\texttt{FileCollection} \\textbar{}\n\\texttt{{[}{]}} \\textbar{} The directories that contains the files to\nprocess. \\texttt{readmeFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The path to the project's README file. It sets\nthe \\texttt{-\\/-readme} argument. \\texttt{tutorialsDir} \\textbar{}\n\\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The directory in which\nJSDoc should search for tutorials. It sets the \\texttt{-\\/-tutorials}\nargument.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\n\n\\chapter{Lang Builder Gradle Plugin}\\label{lang-builder-gradle-plugin}\n\nThe Lang Builder Gradle plugin lets you run the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/lang-builder}{Liferay\nLang Builder} tool to sort and translate the language keys in your\nproject.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-14}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.lang.builder\", version: \"3.0.12\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.lang.builder\"\n\\end{verbatim}\n\nSince the plugin automatically resolves the Liferay Lang Builder library\nas a dependency, you have to configure a repository that hosts the\nlibrary and its transitive dependencies. The Liferay CDN repository\nhosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\nSee\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/automatically-generating-translations}{this\npage} on the \\emph{Liferay Developer Network} for more information about\nusage of the Lang Builder Gradle plugin.\n\n\\section{Tasks}\\label{tasks-13}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{buildLang} \\textbar{} - \\textbar{}\n\\hyperref[buildlangtask]{\\texttt{BuildLangTask}} \\textbar{} Runs Liferay\nLang Builder to translate language property files.\n\nThe \\texttt{buildLang} task is automatically configured with sensible\ndefaults, depending on whether the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin is applied:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[langdir]{\\texttt{langDir}} \\textbar{}\n\n\\textbf{If the \\texttt{java} plugin is applied:} The directory\n\\texttt{content} in the first \\texttt{resources} directory of the\n\\texttt{main} source set (by default:\n\\texttt{src/main/resources/content}).\n\n\\textbf{Otherwise:} \\texttt{null}\n\n\\section{BuildLangTask}\\label{buildlangtask}\n\nTasks of type \\texttt{BuildLangTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.lang.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}},\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args}{\\texttt{args}}\n\\textbar{} Lang Builder command line arguments\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[liferay-lang-builder-dependency]{\\texttt{project.configurations.langBuilder}}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{} \\texttt{\"com.liferay.lang.builder.LangBuilder\"}\n\n\\subsection{Task Properties}\\label{task-properties-13}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{excludedLanguageIds} \\textbar{}\n\\texttt{Set\\textless{}String\\textgreater{}} \\textbar{}\n\\texttt{{[}\"da\",\\ \"de\",\\ \"fi\",\\ \"ja\",\\ \"nl\",\\ \"pt\\_PT\",\\ \"sv\"{]}}\n\\textbar{} The language IDs to exclude in the automatic translation. It\nsets the \\texttt{lang.excluded.language.ids} argument. \\texttt{langDir}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The\ndirectory where the language properties files are saved. It sets the\n\\texttt{lang.dir} argument. \\texttt{langFileName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\"Language\"} \\textbar{} The file name\nprefix of the language properties files (e.g.,\n\\texttt{Language\\_it.properties}). It sets the \\texttt{lang.file}\nargument. \\texttt{plugin} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to check for duplicate language keys\nbetween the project and the portal. If\n\\texttt{portalLanguagePropertiesFile} is not set, this property has no\neffect. It sets the \\texttt{lang.plugin} argument.\n\\texttt{portalLanguagePropertiesFile} \\textbar{} \\texttt{File}\n\\textbar{} \\texttt{null} \\textbar{} The \\texttt{Language.properties}\nfile of the portal. It sets the\n\\texttt{lang.portal.language.properties.file} argument.\n\\texttt{translate} \\textbar{} \\texttt{boolean} \\textbar{} \\texttt{true}\n\\textbar{} Whether to translate the language keys and generate a\nlanguage properties file for each locale that's supported by Liferay. It\nsets the \\texttt{lang.translate} argument.\n\\texttt{translateSubscriptionKey} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The subscription key for Microsoft Translation\nintegration. Subscription to the Translator Text Translation API on\nMicrosoft Cognitive Services is required. Basic subscriptions, up to 2\nmillion characters a month, are free. See\n\\href{http://docs.microsofttranslator.com/text-translate.html}{here} for\nmore information. It sets the \\texttt{lang.translate.subscription.key}\nargument.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.lang.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties, to defer evaluation until task execution.\n\n\\subsection{Task Methods}\\label{task-methods-6}\n\nMethod \\textbar{} Description\n\\texttt{BuildLangTask\\ excludedLanguageIds(Iterable\\textless{}Object\\textgreater{}\\ excludedLanguageIds)}\n\\textbar{} Adds language IDs to exclude in the automatic translation.\n\\texttt{BuildLangTask\\ excludedLanguageIds(Object...\\ excludedLanguageIds)}\n\\textbar{} Adds language IDs to exclude in the automatic translation.\n\n\\section{Additional Configuration}\\label{additional-configuration-7}\n\nThere are additional configurations that can help you use the Lang\nBuilder.\n\n\\section{Liferay Lang Builder\nDependency}\\label{liferay-lang-builder-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{langBuilder} and adds a dependency to the latest released\nversion of the Liferay Lang Builder. It is possible to override this\nsetting and use a specific version of the tool by manually adding a\ndependency to the \\texttt{langBuilder} configuration:\n\n\\begin{verbatim}\ndependencies {\n    langBuilder group: \"com.liferay\", name: \"com.liferay.lang.builder\", version: \"1.0.31\"\n}\n\\end{verbatim}\n\n\\chapter{Maven Plugin Builder Gradle\nPlugin}\\label{maven-plugin-builder-gradle-plugin}\n\nThe Maven Plugin Builder Gradle Plugin lets you generate the\n\\href{https://maven.apache.org/ref/current/maven-plugin-api/plugin.html}{Maven\nplugin descriptor} for any\n\\href{https://maven.apache.org/general.html\\#What_is_a_Mojo}{Mojos}\nfound in your project.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-15}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.maven.plugin.builder\", version: \"1.2.4\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.maven.plugin.builder\"\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-14}\n\nThe plugin adds two tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{buildPluginDescriptor}\n\\textbar{}\\href{https://docs.gradle.org/current/userguide/java_plugin.html\\#sec:compile}{\\texttt{compileJava}},\n\\hyperref[writemavensettings]{\\texttt{WriteMavenSettings}} \\textbar{}\n\\hyperref[buildplugindescriptortask]{\\texttt{BuildPluginDescriptorTask}}\n\\textbar{} Generates the Maven plugin descriptor for the project.\n\\texttt{WriteMavenSettings} \\textbar{} - \\textbar{}\n\\hyperref[writemavensettingstask]{\\texttt{WriteMavenSettingsTask}}\n\\textbar{} Writes a temporary Maven settings file to be used during\nsubsequent Maven invocations.\n\nThe Maven Plugin Builder Plugin automatically applies the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin.\n\nThe plugin also adds the following dependencies to tasks defined by the\n\\href{https://docs.gradle.org/current/userguide/maven_plugin.html}{\\texttt{maven}}\nplugin:\n\nName \\textbar{} Depends On \\texttt{install}, \\texttt{uploadArchives},\nand all the other tasks of type\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Upload.html}{\\texttt{Upload}}\n\\textbar{} \\texttt{buildPluginDescriptor}\n\nThe \\texttt{buildPluginDescriptor} task is automatically configured with\nsensible defaults:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[classesdir]{\\texttt{classesDir}} \\textbar{}\n\\texttt{sourceSets.main.output.classesDir}\n\\hyperref[mavenembedderclasspath]{\\texttt{mavenEmbedderClasspath}}\n\\textbar{}\n\\hyperref[maven-embedder-dependency]{\\texttt{configurations.mavenEmbedder}}\n\\hyperref[mavensettingsfile]{\\texttt{mavenSettingsFile}} \\textbar{}\n\\hyperref[outputfile]{\\texttt{writeMavenSettings.outputFile}}\n\\hyperref[outputdir]{\\texttt{outputDir}} \\textbar{} The directory\n\\texttt{META-INF/maven} in the first \\texttt{resources} directory of the\n\\texttt{main} source set (by default:\n\\texttt{src/main/resources/META-INF/maven}).\n\\hyperref[pomartifactid]{\\texttt{pomArtifactId}} \\textbar{} The bundle\nsymbolic name of the project inferred via the\n\\href{https://github.com/gradle/gradle/blob/master/subprojects/osgi/src/main/java/org/gradle/api/internal/plugins/osgi/OsgiHelper.java}{\\texttt{OsgiHelper}}\nclass. \\hyperref[pomgroupid]{\\texttt{pomGroupId}} \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:group}{\\texttt{project.group}}\n\\hyperref[pomversion]{\\texttt{pomVersion}} \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:version}{\\texttt{project.version}}\n(if it ends with \\texttt{\"-SNAPSHOT\"}, the suffix will be removed)\n\\hyperref[sourcedir]{\\texttt{sourceDir}} \\textbar{} The first\n\\texttt{java} directory of the \\texttt{main} source set (by default:\n\\texttt{src/main/java}).\n\nThe plugin ensures that the \\texttt{processResources} task always runs\nbefore \\texttt{buildPluginDescriptor} to let \\texttt{processResources}\ncopy the newly generated files in the\n\\texttt{buildPluginDescriptor.outputDir} directory.\n\nThe \\texttt{writeMavenSettings} task is also automatically configured\nwith sensible defaults:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[localrepositorydir]{\\texttt{localRepositoryDir}} \\textbar{}\n\\texttt{maven.repo.local} system property\n\\hyperref[nonproxyhosts]{\\texttt{nonProxyHosts}} \\textbar{}\n\\texttt{http.nonProxyHosts} system property\n\\hyperref[outputfile]{\\texttt{outputFile}} \\textbar{}\n\\texttt{\"\\$\\{project.buildDir\\}/settings.xml\"}\n\\hyperref[proxyhost]{\\texttt{proxyHost}} \\textbar{}\n\\texttt{http.ProxyHost} or \\texttt{https.proxyHost} system property\n(depending on the protocol of\n\\hyperref[repositoryurl]{\\texttt{repositoryUrl}})\n\\hyperref[proxypassword]{\\texttt{proxyPassword}} \\textbar{}\n\\texttt{http.ProxyPassword} or \\texttt{https.proxyPassword} system\nproperty (depending on the protocol of\n\\hyperref[repositoryurl]{\\texttt{repositoryUrl}})\n\\hyperref[proxyport]{\\texttt{proxyPort}} \\textbar{}\n\\texttt{http.ProxyPort} or \\texttt{https.proxyPort} system property\n(depending on the protocol of\n\\hyperref[repositoryurl]{\\texttt{repositoryUrl}})\n\\hyperref[proxyuser]{\\texttt{proxyUser}} \\textbar{}\n\\texttt{http.ProxyUser} or \\texttt{https.proxyUser} system property\n(depending on the protocol of\n\\hyperref[repositoryurl]{\\texttt{repositoryUrl}})\n\\hyperref[repositoryurl]{\\texttt{repositoryUrl}} \\textbar{}\n\\texttt{repository.url} system property\n\nIf running on JDK8+, the plugin also disables the\n\\href{http://docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html\\#BEJEFABE}{\\emph{doclint}}\nfeature in all tasks of type\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html}{\\texttt{Javadoc}}.\n\n\\section{BuildPluginDescriptorTask}\\label{buildplugindescriptortask}\n\nTasks of type \\texttt{BuildPluginDescriptorTask} work by generating a\ntemporary \\texttt{pom.xml} file based on the project, and then invoking\nthe \\href{http://maven.apache.org/ref/3.3.9/maven-embedder/}{Maven\nEmbedder} to build the Maven plugin descriptor.\n\nIt is possible to declare information for the plugin descriptor\ngeneration using either\n\\href{https://maven.apache.org/plugin-tools/maven-plugin-tools-annotations/}{Java\n5 Annotations} or\n\\href{https://maven.apache.org/plugin-tools/maven-plugin-tools-java/}{Javadoc\nTags}.\n\n\\subsection{Task Properties}\\label{task-properties-14}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{classesDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The directory that contains the compiled\nclasses. It sets the value of the\n\\href{http://maven.apache.org/ref/3.3.9/maven-model/maven.html\\#class_build}{\\texttt{build.outputDirectory}}\nelement in the generated \\texttt{pom.xml} file.\n\\texttt{configurationScopeMappings} \\textbar{}\n\\texttt{Map\\textless{}String,\\ String\\textgreater{}} \\textbar{}\n\\texttt{{[}\"compile\":\\ \"compile\",\\ \"provided\",\\ \"provided\"{]}}\n\\textbar{} The mapping between the configuration names in the Gradle\nproject and the\n\\href{https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html\\#Dependency_Scope}{dependency\nscopes} in the \\texttt{pom.xml} file. It is used to add\n\\href{http://maven.apache.org/ref/3.3.3/maven-model/maven.html\\#class_dependency}{\\texttt{dependencies.dependency}}\nelements in the generated \\texttt{pom.xml} file.\n\\texttt{forcedExclusions} \\textbar{}\n\\texttt{Set\\textless{}String\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The \\emph{group:name:version} notation of the dependencies to\nalways exclude from the ones added in the \\texttt{pom.xml} file. It adds\n\\href{http://maven.apache.org/ref/3.3.3/maven-model/maven.html\\#class_exclusion}{\\texttt{dependencies.dependency.exclusions.exclusion}}\nelements to the generated \\texttt{pom.xml} file. \\texttt{goalPrefix}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The goal\nprefix for the Maven plugin specified in the descriptor. It sets the\nvalue of the\n\\href{https://maven.apache.org/plugin-tools/maven-plugin-plugin/examples/generate-descriptor.html}{\\texttt{build.plugins.plugin.configuration.goalPrefix}}\nelement in the generated \\texttt{pom.xml} file. \\texttt{mavenDebug}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto invoke the Maven Embedder in debug mode.\n\\texttt{mavenEmbedderClasspath} \\textbar{} \\texttt{FileCollection}\n\\textbar{} \\texttt{null} \\textbar{} The classpath used to invoke the\nMaven Embedder. \\texttt{mavenEmbedderMainClassName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\"org.apache.maven.cli.MavenCli\"}\n\\textbar{} The Maven Embedder's main class name.\n\\texttt{mavenPluginPluginVersion} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"3.4\"} \\textbar{} The version of the\n\\href{https://maven.apache.org/plugin-tools/maven-plugin-plugin/}{Maven\nPlugin Plugin} to use to generate the plugin descriptor for the project.\n\\texttt{mavenSettingsFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The custom \\texttt{settings.xml} file to use.\nIt sets the \\texttt{-\\/-settings} argument on the Maven Embedder\ninvocation. \\texttt{outputDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The directory where the Maven plugin descriptor\nfiles are saved. \\texttt{pomArtifactId} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{} The identifier for the artifact that\nis unique within the group. It sets the value of the\n\\href{http://maven.apache.org/ref/3.3.3/maven-model/maven.html\\#class_project}{\\texttt{project.artifactId}}\nelement in the generated \\texttt{pom.xml} file. \\texttt{pomGroupId}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The\nuniversally unique identifier for the project. It sets the value of the\n\\href{http://maven.apache.org/ref/3.3.3/maven-model/maven.html\\#class_project}{\\texttt{project.groupId}}\nelement in the generated \\texttt{pom.xml} file. \\texttt{pomRepositories}\n\\textbar{} \\texttt{Map\\textless{}String,\\ Object\\textgreater{}}\n\\textbar{}\n\\texttt{{[}\"liferay-public\":\\ \"http://repository.liferay.com/nexus/content/groups/public\"{]}}\n\\textbar{} The name and URL of the remote repositories. It adds\n\\href{http://maven.apache.org/ref/3.3.3/maven-model/maven.html\\#class_repository}{\\texttt{repositories.repository}}\nelements in the generated \\texttt{pom.xml} file. \\texttt{pomVersion}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The\nversion of the artifact produced by this project. It sets the value of\nthe\n\\href{http://maven.apache.org/ref/3.3.3/maven-model/maven.html\\#class_project}{\\texttt{project.version}}\nelement in the generated \\texttt{pom.xml} file. \\texttt{sourceDir}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The\ndirectory that contains the source files. It sets the value of the\n\\href{http://maven.apache.org/ref/3.3.9/maven-model/maven.html\\#class_build}{\\texttt{build.sourceDirectory}}\nelement in the generated \\texttt{pom.xml} file.\n\\texttt{useSetterComments} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to allow\n\\href{https://maven.apache.org/plugin-tools/maven-plugin-tools-java/}{Mojo\nJavadoc Tags} in the setter methods of the Mojo.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.lang.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties, to defer evaluation until task execution.\n\n\\section{Task Methods}\\label{task-methods-7}\n\nMethod \\textbar{} Description\n\\texttt{void\\ configurationScopeMapping(String\\ configurationName,\\ String\\ scope)}\n\\textbar{} Adds a mapping between a configuration name in the Gradle\nproject and the dependency scope in the \\texttt{pom.xml} file.\n\\texttt{BuildPluginDescriptorTask\\ forcedExclusions(Iterable\\textless{}String\\textgreater{}\\ forcedExclusions)}\n\\textbar{} Adds \\emph{group:name:version} notations of dependencies to\nalways exclude from the ones added in the \\texttt{pom.xml} file.\n\\texttt{BuildPluginDescriptorTask\\ forcedExclusions(String...\\ forcedExclusions)}\n\\textbar{} Adds \\emph{group:name:version} notations of dependencies to\nalways exclude from the ones added in the \\texttt{pom.xml} file.\n\\texttt{BuildPluginDescriptorTask\\ pomRepositories(Map\\textless{}String,\\ ?\\textgreater{}\\ pomRepositories}\n\\textbar{} Adds names and URLs of remote repositories in the\n\\texttt{pom.xml} file.\n\\texttt{BuildPluginDescriptorTask\\ pomRepository(String\\ id,\\ Object\\ url)}\n\\textbar{} Adds the name and URL of a remote repository in the\n\\texttt{pom.xml} file.\n\n\\section{WriteMavenSettingsTask}\\label{writemavensettingstask}\n\n\\subsection{Task Properties}\\label{task-properties-15}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{localRepositoryDir} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{} The directory of the system's local\nrepository. It sets the value of the\n\\href{https://maven.apache.org/settings.html\\#Simple_Values}{\\texttt{localRepository}}\nelement in the generated \\texttt{settings.xml} file.\n\\texttt{nonProxyHosts} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The patterns of the host that should be\naccessed without going through the proxy. It sets the value of the\n\\href{https://maven.apache.org/settings.html\\#Proxies}{\\texttt{proxies.proxy.nonProxyHosts}}\nelement in the generated \\texttt{settings.xml} file. \\texttt{outputFile}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The\ngenerated \\texttt{settings.xml} file. \\texttt{proxyHost} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The host name or\naddress of the proxy server. It sets the value of the\n\\href{https://maven.apache.org/settings.html\\#Proxies}{\\texttt{proxies.proxy.host}}\nelement in the generated \\texttt{settings.xml} file.\n\\texttt{proxyPassword} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The password to use to access a protected proxy\nserver. It sets the value of the\n\\href{https://maven.apache.org/settings.html\\#Proxies}{\\texttt{proxies.proxy.password}}\nelement in the generated \\texttt{settings.xml} file. \\texttt{proxyPort}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The port\nnumber of the proxy server. It sets the value of the\n\\href{https://maven.apache.org/settings.html\\#Proxies}{\\texttt{proxies.proxy.port}}\nelement in the generated \\texttt{settings.xml} file. \\texttt{proxyUser}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The user\nname to use to access a protected proxy server. It sets the value of the\n\\href{https://maven.apache.org/settings.html\\#Proxies}{\\texttt{proxies.proxy.username}}\nelement in the generated \\texttt{settings.xml} file.\n\\texttt{repositoryUrl} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The URL of the repository\n\\href{https://maven.apache.org/guides/mini/guide-mirror-settings.html\\#Using_A_Single_Repository}{mirror}.\nIt sets the value of the\n\\href{https://maven.apache.org/settings.html\\#Mirrors}{\\texttt{mirrors.mirror.url}}\nelement in the generated \\texttt{settings.xml} file.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.lang.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties, to defer evaluation until task execution.\n\n\\section{Additional Configuration}\\label{additional-configuration-8}\n\nThere are additional configurations that can help you use the Maven\nPlugin Builder.\n\n\\section{Maven Embedder Dependency}\\label{maven-embedder-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{mavenEmbedder} and adds a dependency to the 3.3.9 version of the\nMaven Embedder. It is possible to override this setting and use a\nspecific version of the tool by manually adding a dependency to the\n\\texttt{mavenEmbedder} configuration:\n\n\\begin{verbatim}\ndependencies {\n    mavenEmbedder group: \"org.apache.maven\", name: \"maven-embedder\", version: \"3.3.9\"\n    mavenEmbedder group: \"org.apache.maven.wagon\", name: \"wagon-http\", version: \"2.10\"\n    mavenEmbedder group: \"org.eclipse.aether\", name: \"aether-connector-basic\", version: \"1.0.2.v20150114\"\n    mavenEmbedder group: \"org.eclipse.aether\", name: \"aether-transport-wagon\", version: \"1.0.2.v20150114\"\n    mavenEmbedder group: \"org.slf4j\", name: \"slf4j-simple\", version: \"1.7.5\"\n}\n\\end{verbatim}\n\n\\section{System Properties}\\label{system-properties-2}\n\nIt is possible to set the default value of the \\texttt{mavenDebug}\nproperty for a \\texttt{BuildPluginDescriptorTask} task via system\nproperty:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{-D\\$\\{task.name\\}.maven.debug=true}\n\\end{itemize}\n\nFor example, run the following Bash command to invoke the Maven Embedder\nin debug mode to attach a remote debugger:\n\n\\begin{verbatim}\n./gradlew buildPluginDescriptor -DbuildPluginDescriptor.maven.debug=true\n\\end{verbatim}\n\n\\chapter{Node Gradle Plugin}\\label{node-gradle-plugin}\n\nThe Node Gradle plugin lets you run \\href{https://nodejs.org/}{Node.js}\nand \\href{https://www.npmjs.com/}{NPM} as part of your build.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-16}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.node\", version: \"4.6.18\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.node\"\n\\end{verbatim}\n\n\\section{Project Extension}\\label{project-extension-6}\n\nThe Node Gradle plugin exposes the following properties through the\nextension named \\texttt{node}:\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{download} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to download and use a local and\nisolated Node.js distribution instead of the one installed in the\nsystem. \\texttt{global} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{false} \\textbar{} Whether to use a single Node.js installation\nfor the whole multi-project build. This reduces the time required to\nunpack the Node.js distribution and the time required to download NPM\npackages thanks to a shared packages cache. If \\texttt{download} is\n\\texttt{false}, this property has no effect. \\texttt{nodeDir} \\textbar{}\n\\texttt{File} \\textbar{}\n\n\\textbf{If \\texttt{global} is \\texttt{true}:}\n\\texttt{\"\\$\\{rootProject.buildDir\\}/node\"}\n\n\\textbf{Otherwise:} \\texttt{\"\\$\\{project.buildDir\\}/node\"}\n\n\\textbar{} The directory where the Node.js distribution is unpacked. If\n\\texttt{download} is \\texttt{false}, this property has no effect.\n\\texttt{nodeUrl} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"http://nodejs.org/dist/v\\$\\{node.nodeVersion\\}/node-v\\$\\{node.nodeVersion\\}-\\$\\{platform\\}-x\\$\\{bitMode\\}.\\$\\{extension\\}\"}\n\\textbar{} The URL of the Node.js distribution to download. If\n\\texttt{download} is \\texttt{false}, this property has no effect.\n\\texttt{nodeVersion} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"5.5.0\"} \\textbar{} The version of the Node.js distribution to\nuse. If \\texttt{download} is \\texttt{false}, this property has no\neffect. \\texttt{npmArgs} \\textbar{}\n\\texttt{List\\textless{}String\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The arguments added automatically to every task of type\n\\hyperref[executenpmtask]{\\texttt{ExecuteNpmTask}}. \\texttt{npmUrl}\n\\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"https://registry.npmjs.org/npm/-/npm-\\$\\{node.npmVersion\\}.tgz\"}\n\\textbar{} The URL of the NPM version to download. If \\texttt{download}\nis \\texttt{false}, this property has no effect. \\texttt{npmVersion}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The\nversion of NPM to use. If \\texttt{null}, the version of NPM embedded\ninside the Node.js distribution is used. If \\texttt{download} is\n\\texttt{false}, this property has no effect.\n\nIt is possible to override the default value of the \\texttt{download}\nproperty by setting the \\texttt{nodeDownload} project property. For\nexample, this can be done via command line argument:\n\n\\begin{verbatim}\n./gradlew -PnodeDownload=false npmInstall\n\\end{verbatim}\n\nThe same extension exposes the following methods:\n\nMethod \\textbar{} Description\n\\texttt{NodeExtension\\ npmArgs(Iterable\\textless{}?\\textgreater{}\\ npmArgs)}\n\\textbar{} Adds arguments to automatically add to every task of type\n\\hyperref[executenpmtask]{\\texttt{ExecuteNpmTask}}.\n\\texttt{NodeExtension\\ npmArgs(Object...\\ npmArgs)} \\textbar{} Adds\narguments to automatically add to every task of type\n\\hyperref[executenpmtask]{\\texttt{ExecuteNpmTask}}.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for\n\\texttt{String}, to defer evaluation until execution.\n\nPlease note that setting the \\texttt{global} property of the\n\\texttt{node} extension via the command line is not supported. It can\nonly be set via Gradle script, which can be done by adding the following\ncode to the \\texttt{build.gradle} file in the root of a project (e.g.,\nLiferay Workspace):\n\n\\begin{verbatim}\nallprojects {\n    plugins.withId(\"com.liferay.node\") {\n        node.global = true\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-15}\n\nThe plugin adds a series of tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{cleanNPM} \\textbar{} - \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Delete.html}{\\texttt{Delete}}\n\\textbar{} Deletes the \\texttt{node\\_modules} directory, the\n\\texttt{npm-shrinkwrap.json} file and the \\texttt{package-lock.json}\nfiles from the project, if present. \\texttt{downloadNode} \\textbar{} -\n\\textbar{} \\hyperref[downloadnodetask]{\\texttt{DownloadNodeTask}}\n\\textbar{} Downloads and unpacks the local Node.js distribution for the\nproject. If \\texttt{node.download} is \\texttt{false}, this task is\ndisabled. \\texttt{npmInstall} \\textbar{} \\texttt{downloadNode}\n\\textbar{} \\hyperref[npminstalltask]{\\texttt{NpmInstallTask}} \\textbar{}\nRuns \\texttt{npm\\ install} to install the dependencies declared in the\nproject's \\texttt{package.json} file, if present. By default, the task\nis \\hyperref[npminstallretries]{configured} to run \\texttt{npm\\ install}\ntwo more times if it fails.\n\\hyperref[npmrunscript-task]{\\texttt{npmRun\\$\\{script\\}}} \\textbar{}\n\\texttt{npmInstall} \\textbar{}\n\\hyperref[executenpmtask]{\\texttt{ExecuteNpmTask}} \\textbar{} Runs the\n\\texttt{\\$\\{script\\}} NPM script. \\texttt{npmPackageLock} \\textbar{}\n\\texttt{cleanNPM}, \\texttt{npmInstall} \\textbar{}\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/api/DefaultTask.html}{\\texttt{DefaultTask}}\n\\textbar{} Deletes the NPM files and runs \\texttt{npm\\ install} to\ninstall the dependencies declared in the project's \\texttt{package.json}\nfile, if present. \\texttt{npmShrinkwrap} \\textbar{} \\texttt{cleanNPM},\n\\texttt{npmInstall} \\textbar{}\n\\hyperref[npmshrinkwraptask]{\\texttt{NpmShrinkwrapTask}} \\textbar{}\nLocks down the versions of a package's dependencies in order to control\nwhich dependency versions are used.\n\n\\section{DownloadNodeTask}\\label{downloadnodetask}\n\nThe purpose of this task is to download and unpack a Node.js\ndistribution.\n\n\\subsection{Task Properties}\\label{task-properties-16}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{nodeDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The directory where the Node.js distribution is\nunpacked. \\texttt{nodeExeUrl} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The URL of \\texttt{node.exe} to download when\non Windows. \\texttt{nodeUrl} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The URL of the Node.js distribution to\ndownload. \\texttt{npmUrl} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The URL of the NPM version to download.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties, to defer evaluation until task execution.\n\n\\section{ExecuteNodeTask}\\label{executenodetask}\n\nThis is the base task to run Node.js in a Gradle build. All tasks of\ntype \\texttt{ExecuteNodeTask} automatically depend on\n\\hyperref[downloadnode]{\\texttt{downloadNode}}.\n\n\\subsection{Task Properties}\\label{task-properties-17}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{args} \\textbar{}\n\\texttt{List\\textless{}Object\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The arguments for the Node.js invocation. \\texttt{command}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{\"node\"} \\textbar{} The\nfile name of the executable to invoke. \\texttt{environment} \\textbar{}\n\\texttt{Map\\textless{}Object,\\ Object\\textgreater{}} \\textbar{}\n\\texttt{{[}{]}} \\textbar{} The environment variables for the Node.js\ninvocation. \\texttt{inheritProxy} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to set the \\texttt{http\\_proxy},\n\\texttt{https\\_proxy}, and \\texttt{no\\_proxy} environment variables in\nthe Node.js invocation based on the values of the system properties\n\\texttt{https.proxyHost}, \\texttt{https.proxyPort},\n\\texttt{https.proxyUser}, \\texttt{https.proxyPassword},\n\\texttt{https.nonProxyHosts}, \\texttt{https.proxyHost},\n\\texttt{https.proxyPort}, \\texttt{https.proxyUser},\n\\texttt{https.proxyPassword}, and \\texttt{https.nonProxyHosts}. If these\nenvironment variables are already set, their values will not be\noverwritten. \\texttt{nodeDir} \\textbar{} \\texttt{File} \\textbar{}\n\n\\textbf{If \\hyperref[download]{\\texttt{node.download}} is\n\\texttt{true}:} \\hyperref[nodedir]{\\texttt{node.nodeDir}}\n\n\\textbf{Otherwise:} \\texttt{null}\n\n\\textbar{} The directory that contains the executable to invoke. If\n\\texttt{null}, the executable must be available in the system\n\\texttt{PATH}. \\texttt{npmInstallRetries} \\textbar{} \\texttt{int}\n\\textbar{} \\texttt{0} \\textbar{} The number of times the\n\\texttt{node\\_modules} is deleted, the NPM cached data is verified\n(\\texttt{npm\\ cache\\ verify}), and \\texttt{npm\\ install} is retried in\ncase the Node.js invocation defined by this task fails. This can help\nsolving corrupted \\texttt{node\\_modules} directories by re-downloading\nthe project's dependencies. \\texttt{workingDir} \\textbar{} \\texttt{File}\n\\textbar{} \\texttt{project.projectDir} \\textbar{} The working directory\nto use in the Node.js invocation.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties to defer evaluation until task execution.\n\n\\subsection{Task Methods}\\label{task-methods-8}\n\nMethod \\textbar{} Description\n\\texttt{ExecuteNodeTask\\ args(Iterable\\textless{}?\\textgreater{}\\ args)}\n\\textbar{} Adds arguments for the Node.js invocation.\n\\texttt{ExecuteNodeTask\\ args(Object...\\ args)} \\textbar{} Adds\narguments for the Node.js invocation.\n\\texttt{ExecuteNodeTask\\ environment(Map\\textless{}?,\\ ?\\textgreater{}\\ environment)}\n\\textbar{} Adds environment variables for the Node.js invocation.\n\\texttt{ExecuteNodeTask\\ environment(Object\\ key,\\ Object\\ value)}\n\\textbar{} Adds an environment variable for the Node.js invocation.\n\n\\section{ExecuteNodeScriptTask}\\label{executenodescripttask}\n\nThe purpose of this task is to execute a Node.js script. Tasks of type\n\\texttt{ExecuteNodeScriptTask} extend\n\\hyperref[executenodetask]{\\texttt{ExecuteNodeTask}}.\n\n\\subsection{Task Properties}\\label{task-properties-18}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{scriptFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The Node.js script to execute.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\n\n\\section{ExecuteNpmTask}\\label{executenpmtask}\n\nThe purpose of this task is to execute an NPM command. Tasks of type\n\\texttt{ExecuteNpmTask} extend\n\\hyperref[executenodescripttask]{\\texttt{ExecuteNodeScriptTask}} with\nthe following properties set by default:\n\nProperty Name \\textbar{} Default Value \\texttt{command} \\textbar{}\n\n\\textbf{If \\texttt{nodeDir} is \\texttt{null}:} \\texttt{\"npm\"}\n\n\\textbf{Otherwise:} \\texttt{\"node\"}\n\n\\texttt{scriptFile} \\textbar{}\n\n\\textbf{If \\texttt{nodeDir} is \\texttt{null}:} \\texttt{null}\n\n\\textbf{Otherwise:}\n\\texttt{\"\\$\\{nodeDir\\}/lib/node\\_modules/npm/bin/npm-cli.js\"} or\n\\texttt{\"\\$\\{nodeDir\\}/node\\_modules/npm/bin/npm-cli.js\"} on Windows.\n\n\\subsection{Task Properties}\\label{task-properties-19}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{cacheConcurrent} \\textbar{} \\texttt{boolean}\n\\textbar{}\n\n\\textbf{If \\texttt{node.npmVersion} is greater than or equal to\n\\texttt{5.0.0}, or \\texttt{node.nodeVersion} is greater than or equal to\n\\texttt{8.0.0}:} \\texttt{true}\n\n\\textbf{Otherwise:} \\texttt{false}\n\n\\textbar{} Whether to run this task concurrently, in case the version of\nNPM in use supports multiple concurrent accesses to the same cache\ndirectory. \\texttt{cacheDir} \\textbar{} \\texttt{File} \\textbar{}\n\n\\textbf{If \\texttt{nodeDir} is \\texttt{null}, or\n\\texttt{node.npmVersion} is greater than or equal to \\texttt{5.0.0}, or\n\\texttt{node.nodeVersion} is greater than or equal to \\texttt{8.0.0}:}\n\\texttt{null}\n\n\\textbf{Otherwise:} \\texttt{\"\\$\\{nodeDir\\}/.cache\"}\n\n\\textbar{} The location of NPM's cache directory. It sets the\n\\href{https://docs.npmjs.com/misc/config\\#cache}{\\texttt{-\\/-cache}}\nargument. Leave the property \\texttt{null} to keep the default value.\n\\texttt{logLevel} \\textbar{} \\texttt{String} \\textbar{} Value to mirror\nthe log level set in the task's\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Task.html\\#org.gradle.api.Task:logger}{\\texttt{logger}}\nobject. \\textbar{} The NPM log level. It sets the\n\\href{https://docs.npmjs.com/misc/config\\#loglevel}{--loglevel}\nargument. \\texttt{production} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{false} \\textbar{} Whether to run in production mode during the\nNPM invocation. It sets the\n\\href{https://docs.npmjs.com/misc/config\\#production}{\\texttt{-\\/-production}}\nargument. \\texttt{progress} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to show a progress bar during the NPM\ninvocation. It sets the\n\\href{https://docs.npmjs.com/misc/config\\#progress}{\\texttt{-\\/-progress}}\nargument. \\texttt{registry} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The base URL of the NPM package registry. It\nsets the\n\\href{https://docs.npmjs.com/misc/config\\#registry}{\\texttt{-\\/-registry}}\nargument. Leave the property \\texttt{null} or empty to keep the default\nvalue.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties, to defer evaluation until task execution.\n\n\\section{DownloadNodeModuleTask}\\label{downloadnodemoduletask}\n\nThe purpose of this task is to download a Node.js package. The packages\nare downloaded in the \\texttt{\\$\\{workingDir\\}/node\\_modules} directory,\nwhich is equal, by default, to the \\texttt{node\\_modules} directory of\nthe project. Tasks of type \\texttt{DownloadNodeModuleTask} extend\n\\hyperref[executenpmtask]{\\texttt{ExecuteNpmTask}} in order to execute\nthe command\n\\href{https://docs.npmjs.com/cli/install}{\\texttt{npm\\ install\\ \\$\\{moduleName\\}@\\$\\{moduleVersion\\}}}.\n\n\\texttt{DownloadNodeModuleTask} instances are automatically disabled if\nthe project's \\texttt{package.json} file already lists a module with the\nsame name in its \\texttt{dependencies} or \\texttt{devDependencies}\nobject.\n\n\\subsection{Task Properties}\\label{task-properties-20}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{moduleName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The name of the Node.js package to download.\n\\texttt{moduleVersion} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The version of the Node.js package to download.\n\nIt is possible to use Closures and Callables as values for the\n\\texttt{String} properties, to defer evaluation until task execution.\n\n\\section{NpmInstallTask}\\label{npminstalltask}\n\nPurpose of these tasks is to install the dependencies declared in a\n\\texttt{package.json} file. Tasks of type \\texttt{NpmInstallTask} extend\n\\hyperref[executenpmtask]{\\texttt{ExecuteNpmTask}} in order to run the\ncommand\n\\href{https://docs.npmjs.com/cli/install}{\\texttt{npm\\ install}}.\n\n\\texttt{NpmInstallTask} instances are automatically disabled if the\n\\texttt{package.json} file does not declare any dependency in the\n\\texttt{dependency} or \\texttt{devDependencies} object.\n\n\\subsection{Task Properties}\\label{task-properties-21}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{nodeModulesCacheDir} \\textbar{} \\texttt{File}\n\\textbar{} \\texttt{null} \\textbar{}\n\nThe directory where \\texttt{node\\_modules} directories are cached. By\nsetting this property, it is possible to cache the\n\\texttt{node\\_modules} directory of a project and avoid unnecessary\ninvocations of \\texttt{npm\\ install}, useful especially in Continuous\nIntegration environments.\n\nThe \\texttt{node\\_modules} directory is cached based on the content of\nthe project's \\texttt{package-lock.json} (or\n\\texttt{npm-shrinkwrap.json}, or \\texttt{package.json} if absent).\nTherefore, if \\texttt{NpmInstallTask} tasks in multiple projects are\nconfigured with the same \\texttt{nodeModulesCacheDir}, and their\n\\texttt{package-lock.json}, \\texttt{npm-shrinkwrap.json} or\n\\texttt{package.json} declare the same dependencies, their\n\\texttt{node\\_modules} caches will be shared.\n\nThis feature is not available if the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-cache}{\\texttt{com.liferay.cache}}\nplugin is applied.\n\n\\texttt{nodeModulesCacheNativeSync} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{true} \\textbar{} Whether to use \\texttt{rsync} (on\nLinux/macOS) or \\texttt{robocopy} (on Windows) to cache and restore the\n\\texttt{node\\_modules} directory. If \\texttt{nodeModulesCacheDir} is not\nset, this property has no effect. \\texttt{nodeModulesDigestFile}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{}\n\nIf this property is set, the content of the project's\n\\texttt{package-lock.json} (or \\texttt{npm-shrinkwrap.json}, or\n\\texttt{package.json} if absent) is checked with the digest from the\n\\texttt{node\\_modules} directory. If the digests match, do nothing. If\nthe digests don't match, the \\texttt{node\\_modules} directory is deleted\nbefore running \\texttt{npm\\ install}.\n\nThis feature is not available if the \\texttt{com.liferay.cache} plugin\nis applied or if the property \\texttt{nodeModulesCacheDir} is set.\n\n\\texttt{removeShrinkwrappedUrls} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} if the \\hyperref[registry]{registry} property has a value,\n\\texttt{false} otherwise. \\textbar{} Whether to temporarily remove all\nthe hard-coded URLs in the \\texttt{from} and \\texttt{resolved} fields of\nthe \\texttt{npm-shinkwrap.json} file before invoking\n\\texttt{npm\\ install}. This way, it is possible to force NPM to download\nall dependencies from a custom registry declared in the\n\\hyperref[registry]{\\texttt{registry}} property. \\texttt{useNpmCI}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto run \\texttt{npm\\ ci} instead of \\texttt{npm\\ install}. If the\n\\texttt{package-lock.json} file does not exist, this property has no\neffect.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\n\n\\section{NpmShrinkwrapTask}\\label{npmshrinkwraptask}\n\nThe purpose of this task is to lock down the versions of a package's\ndependencies so that you can control exactly which dependency versions\nare used when your package is installed. Tasks of type\n\\texttt{NpmShrinkwrapTask} extend\n\\hyperref[executenpmtask]{\\texttt{ExecuteNpmTask}} to execute the\ncommand\n\\href{https://docs.npmjs.com/cli/shrinkwrap}{\\texttt{npm\\ shrinkwrap}}.\n\nThe generated \\texttt{npm-shrinkwrap.json} file is automatically sorted\nand formatted, so it's easier to see the changes with the previous\nversion.\n\n\\texttt{NpmShrinkwrapTask} instances are automatically disabled if the\n\\texttt{package.json} file does not exist.\n\n\\subsection{Task Properties}\\label{task-properties-22}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{excludedDependencies} \\textbar{}\n\\texttt{List\\textless{}String\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The package names to exclude from the generated\n\\texttt{npm-shrinkwrap.json} file. \\texttt{includeDevDependencies}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{true} \\textbar{} Whether\nto include the package's \\texttt{devDependencies}. It sets the\n\\href{https://docs.npmjs.com/cli/shrinkwrap\\#other-notes}{\\texttt{-\\/-dev}}\nargument.\n\nIt is possible to use Closures and Callables as values for the\n\\texttt{String} properties to defer evaluation until task execution.\n\n\\subsection{Task Methods}\\label{task-methods-9}\n\nMethod \\textbar{} Description\n\\texttt{NpmShrinkwrapTask\\ excludeDependencies(Iterable\\textless{}?\\textgreater{}\\ excludedDependencies)}\n\\textbar{} Adds package names to exclude from the generated\n\\texttt{npm-shrinkwrap.json} file.\n\\texttt{NpmShrinkwrapTask\\ excludeDependencies(Object...\\ excludedDependencies)}\n\\textbar{} Adds package names to exclude from the generated\n\\texttt{npm-shrinkwrap.json} file.\n\n\\section{PublishNodeModuleTask}\\label{publishnodemoduletask}\n\nThe purpose of this task is to publish a package to the\n\\href{https://www.npmjs.com/}{NPM registry}. Tasks of type\n\\texttt{PublishNodeModuleTask} extend\n\\hyperref[executenpmtask]{\\texttt{ExecuteNpmTask}} in order to execute\nthe command\n\\href{https://docs.npmjs.com/cli/publish}{\\texttt{npm\\ publish}}.\n\nThese tasks generate a new temporary \\texttt{package.json} file in the\ndirectory assigned to the \\hyperref[workingdir]{\\texttt{workingDir}}\nproperty; then the \\texttt{npm\\ publish} command is executed. If the\n\\texttt{package.json} file in that location does not exist, the one in\nthe root of the project directory (if found) is copied; otherwise, a new\nfile is created.\n\nThe \\texttt{package.json} is then processed by adding the values\nprovided by the task properties, if not already present in the file\nitself. It is still possible to override one or more fields of the\n\\texttt{package.json} file with the values provided by the task\nproperties by adding one or more keys (e.g., \\texttt{\"version\"}) to the\n\\texttt{overriddenPackageJsonKeys} property.\n\n\\subsection{Task Properties}\\label{task-properties-23}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{moduleAuthor} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The value of the\n\\href{https://docs.npmjs.com/files/package.json\\#people-fields-author-contributors}{\\texttt{author}}\nfield in the generated \\texttt{package.json} file.\n\\texttt{moduleBugsUrl} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The value of the\n\\href{https://docs.npmjs.com/files/package.json\\#bugs}{\\texttt{bugs.url}}\nfield in the generated \\texttt{package.json} file.\n\\texttt{moduleDescription} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{project.description} \\textbar{} The value of the\n\\href{https://docs.npmjs.com/files/package.json\\#description-1}{\\texttt{description}}\nfield in the generated \\texttt{package.json} file.\n\\texttt{moduleKeywords} \\textbar{}\n\\texttt{List\\textless{}String\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The value of the\n\\href{https://docs.npmjs.com/files/package.json\\#keywords}{\\texttt{keywords}}\nfield in the generated \\texttt{package.json} file.\n\\texttt{moduleLicense} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The value of the\n\\href{https://docs.npmjs.com/files/package.json\\#license}{\\texttt{license}}\nfield in the generated \\texttt{package.json} file. \\texttt{moduleMain}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The value\nof the\n\\href{https://docs.npmjs.com/files/package.json\\#main}{\\texttt{main}}\nfield in the generated \\texttt{package.json} file. \\texttt{moduleName}\n\\textbar{} \\texttt{String} \\textbar{} Name based on\n\\href{https://github.com/gradle/gradle/blob/master/subprojects/osgi/src/main/java/org/gradle/api/internal/plugins/osgi/OsgiHelper.java}{\\texttt{osgiHelper.bundleSymbolicName}}:\nfor example, if \\texttt{osgiHelper.bundleSymbolicName} is\n\\texttt{\"com.liferay.gradle.plugins.node\"}, the default value for the\n\\texttt{moduleName} property is \\texttt{\"liferay-gradle-plugins-node\"}.\n\\textbar{} The value of the\n\\href{https://docs.npmjs.com/files/package.json\\#name}{\\texttt{name}}\nfield in the generated \\texttt{package.json} file.\n\\texttt{moduleRepository} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The value of the\n\\href{https://docs.npmjs.com/files/package.json\\#repository}{\\texttt{repository}}\nfield in the generated \\texttt{package.json} file.\n\\texttt{moduleVersion} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{project.version} \\textbar{} The value of the\n\\href{https://docs.npmjs.com/files/package.json\\#version}{\\texttt{version}}\nfield in the generated \\texttt{package.json} file.\n\\texttt{npmEmailAddress} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The email address of the npmjs.com user that\npublishes the package. \\texttt{npmPassword} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{} The password of the npmjs.com user\nthat publishes the package. \\texttt{npmUserName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The name of the\nnpmjs.com user that publishes the package.\n\\texttt{overriddenPackageJsonKeys} \\textbar{}\n\\texttt{Set\\textless{}String\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The field values to override in the generated\n\\texttt{package.json} file.\n\n\\subsection{Task Methods}\\label{task-methods-10}\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.3529}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.6471}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nMethod\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{PublishNodeModuleTask\\ overriddenPackageJsonKeys(Iterable\\textless{}String\\textgreater{}\\ overriddenPackageJsonKeys)}\n& Adds field values to override in the generated \\texttt{package.json}\nfile. \\\\\n\\texttt{PublishNodeModuleTask\\ overriddenPackageJsonKeys(String...\\ overriddenPackageJsonKeys)}\n& Adds field values to override in the generated \\texttt{package.json}\nfile. \\\\\n\\end{longtable}\n\n\\section{npmRun\\$\\{script\\} Task}\\label{npmrunscript-task}\n\nFor each \\href{https://docs.npmjs.com/misc/scripts}{script} declared in\nthe \\texttt{package.json} file of the project, one task\n\\texttt{npmRun\\$\\{script\\}} of type\n\\hyperref[executenpmtask]{\\texttt{ExecuteNpmTask}} is added. Each of\nthese tasks is automatically configured with sensible defaults:\n\nProperty Name \\textbar{} Default Value \\texttt{args} \\textbar{}\n\\texttt{{[}\"run-script\",\\ \"\\$\\{script\\}\"{]}}\n\nIf the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin is applied and the \\texttt{package.json} file declares a script\nnamed \\texttt{\"build\"}, the script is executed before the\n\\texttt{classes} task but after the\n\\href{https://docs.gradle.org/4.0/userguide/java_plugin.html\\#sec:java_resources}{\\texttt{processResources}}\ntask.\n\nIf the\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/language/base/plugins/LifecycleBasePlugin.html}{\\texttt{lifecycle-base}}\nplugin is applied and the \\texttt{package.json} file declares a script\nnamed \\texttt{test}, the script is executed when running the\n\\texttt{check} task.\n\n\\chapter{REST Builder Gradle Plugin}\\label{rest-builder-gradle-plugin}\n\nThe REST Builder Gradle plugin lets you generate a REST layer defined in\nthe REST Builder \\texttt{rest-config.yaml} and\n\\texttt{rest-openapi.yaml} files.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-17}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.rest.builder\", version: \"1.0.21\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.portal.tools.rest.builder\"\n\\end{verbatim}\n\nThe REST Builder plugin automatically applies the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin.\n\nSince the plugin automatically resolves the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-rest-builder}{Liferay\nREST Builder} library as a dependency, you have to configure a\nrepository that hosts the library and its transitive dependencies. The\nLiferay CDN repository hosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-16}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{buildREST} \\textbar{} - \\textbar{}\n\\hyperref[buildresttask]{\\texttt{BuildRESTTask}} \\textbar{} Runs the\nLiferay REST Builder.\n\n\\section{BuildRESTTask}\\label{buildresttask}\n\nTasks of type \\texttt{BuildRESTTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.lang.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}}\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args}{\\texttt{args}}\n\\textbar{} REST Builder command line arguments\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[liferay-rest-builder-dependency]{\\texttt{project.configurations.restBuilder}}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{} \\texttt{\"com.liferay.portal.tools.rest.builder.RESTBuilder\"}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:systemProperties}{\\texttt{systemProperties}}\n\\textbar{} \\texttt{{[}{]}}\n\n\\subsection{Task Properties}\\label{task-properties-24}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{copyrightFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The file that contains the copyright header.\n\\texttt{restConfigDir} \\textbar{} \\texttt{File}\n\\textbar{}\\texttt{\\$\\{project.projectDir\\}} \\textbar{} The directory\nthat contains the \\texttt{rest-config.yaml} and\n\\texttt{rest-openapi.yaml} files.\n\nIn the typical scenario, the \\texttt{rest-config.yaml} and\n\\texttt{rest-openapi.yaml} files are contained in the project directory\nof \\texttt{my-rest-app-impl}. In the \\texttt{build.gradle} of the same\nmodule, apply the \\texttt{com.liferay.rest.builder} plugin.\n\nThe properties of type \\texttt{File} supports any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.lang.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties, to defer evaluation until task execution.\n\n\\section{Additional Configuration}\\label{additional-configuration-9}\n\nThere are additional configurations added to use REST Builder.\n\n\\section{Liferay REST Builder\nDependency}\\label{liferay-rest-builder-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{restBuilder} and adds a dependency to the latest released\nversion of Liferay REST Builder.\n\n\\begin{verbatim}\ndependencies {\n    restBuilder group: \"com.liferay\", name: \"com.liferay.portal.tools.rest.builder\", version: \"1.0.22\"\n}\n\\end{verbatim}\n\n\\chapter{Service Builder Gradle\nPlugin}\\label{service-builder-gradle-plugin}\n\nThe Service Builder Gradle plugin lets you generate a service layer\ndefined in a\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} \\texttt{service.xml} file.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-18}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.service.builder\", version: \"2.2.46\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.portal.tools.service.builder\"\n\\end{verbatim}\n\nThe Service Builder plugin automatically applies the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin.\n\nSince the plugin automatically resolves the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-service-builder}{Liferay\nService Builder} library as a dependency, you have to configure a\nrepository that hosts the library and its transitive dependencies. The\nLiferay CDN repository hosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-17}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{buildService} \\textbar{} - \\textbar{}\n\\hyperref[buildservicetask]{\\texttt{BuildServiceTask}} \\textbar{} Runs\nthe Liferay Service Builder.\n\nThe \\texttt{buildService} task is automatically configured with sensible\ndefaults, depending on whether the\n\\href{https://docs.gradle.org/current/userguide/war_plugin.html}{\\texttt{war}}\nplugin is applied, or whether the\n\\hyperref[osgimodule]{\\texttt{osgiModule}} property is \\texttt{true}:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[apidir]{\\texttt{apiDir}} \\textbar{}\n\n\\textbf{If the \\texttt{war} plugin is applied:}\n\\texttt{\\$\\{project.webAppDir\\}/WEB-INF/service}\n\n\\textbf{Otherwise:} \\texttt{null}\n\n\\hyperref[hbmfile]{\\texttt{hbmFile}} \\textbar{}\n\n\\textbf{If \\texttt{osgiModule} is \\texttt{true}:}\n\\texttt{\\$\\{buildService.resourcesDir\\}/META-INF/module-hbm.xml}\n\n\\textbf{Otherwise:}\n\\texttt{\\$\\{buildService.resourcesDir\\}/META-INF/portlet-hbm.xml}\n\n\\hyperref[impldir]{\\texttt{implDir}} \\textbar{} The first \\texttt{java}\ndirectory of the \\texttt{main} source set (by default:\n\\texttt{src/main/java}). \\hyperref[inputfile]{\\texttt{inputFile}}\n\\textbar{}\n\n\\textbf{If the \\texttt{war} plugin is applied:}\n\\texttt{\\$\\{project.webAppDir\\}/WEB-INF/service.xml}\n\n\\textbf{Otherwise:} \\texttt{\\$\\{project.projectDir\\}/service.xml}\n\n\\hyperref[modelhintsfile]{\\texttt{modelHintsFile}} \\textbar{} The file\n\\texttt{META-INF/portlet-model-hints.xml} in the first\n\\texttt{resources} directory of the \\texttt{main} source set (by\ndefault: \\texttt{src/main/resources/META-INF/portlet-model-hints.xml}).\n\\hyperref[pluginname]{\\texttt{pluginName}} \\textbar{}\n\n\\textbf{If \\texttt{osgiModule} is \\texttt{true}:} \\texttt{\"\"}\n\n\\textbf{Otherwise:} \\texttt{project.name}\n\n\\hyperref[pluginname]{\\texttt{propsUtil}} \\textbar{}\n\n\\textbf{If \\texttt{osgiModule} is \\texttt{true}:}\n\\texttt{\"\\$\\{bundleSymbolicName\\}.util.ServiceProps\"}The\n\\texttt{bundleSymbolicName} of the project is inferred via the\n\\href{https://github.com/gradle/gradle/blob/master/subprojects/osgi/src/main/java/org/gradle/api/internal/plugins/osgi/OsgiHelper.java}{\\texttt{OsgiHelper}}\nclass.\n\n\\textbf{Otherwise:} \\texttt{\"com.liferay.util.service.ServiceProps\"}\n\n\\hyperref[resourcesdir]{\\texttt{resourcesDir}} \\textbar{} The first\n\\texttt{resources} directory of the \\texttt{main} source set (by\ndefault: \\texttt{src/main/resources}).\n\\hyperref[springfile]{\\texttt{springFile}} \\textbar{}\n\n\\textbf{If \\texttt{osgiModule} is \\texttt{true}:} the file\n\\texttt{META-INF/spring/module-spring.xml} in the first\n\\texttt{resources} directory of the \\texttt{main} source set (by\ndefault: \\texttt{src/main/resources/META-INF/spring/module-spring.xml})\n\n\\textbf{Otherwise:} the file \\texttt{META-INF/portlet-spring.xml} in the\nfirst \\texttt{resources} directory of the \\texttt{main} source set (by\ndefault: \\texttt{src/main/resources/META-INF/portlet-spring.xml})\n\n\\hyperref[sqldir]{\\texttt{sqlDir}} \\textbar{}\n\n\\textbf{If the \\texttt{war} plugin is applied:}\n\\texttt{\\$\\{project.webAppDir\\}/WEB-INF/sql}\n\n\\textbf{Otherwise:} The directory \\texttt{META-INF/sql} in the first\n\\texttt{resources} directory of the \\texttt{main} source set (by\ndefault: \\texttt{src/main/resources/META-INF/sql}).\n\nIn the typical scenario of a data-driven Liferay OSGi application split\nin \\texttt{myapp-app}, \\texttt{myapp-service} and \\texttt{myapp-web}\nmodules, the \\texttt{service.xml} file is usually contained in the root\ndirectory of \\texttt{myapp-service}. In the \\texttt{build.gradle} of the\nsame module, it is enough to apply the\n\\texttt{com.liferay.service.builder} plugin \\hyperref[usage]{as\ndescribed}, and then add the following snippet to enable the use of\nLiferay Service Builder:\n\n\\begin{verbatim}\nbuildService {\n    apiDir = \"../myapp-api/src/main/java\"\n    testDir = \"../myapp-test/src/testIntegration/java\"\n}\n\\end{verbatim}\n\nWhile \\texttt{apiDir} is required, the \\texttt{testDir} property\nassignment can be left out, in which case Arquillian-based integration\ntest classes are generated.\n\n\\section{BuildServiceTask}\\label{buildservicetask}\n\nTasks of type \\texttt{BuildWSDDTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.lang.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}}\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args}{\\texttt{args}}\n\\textbar{} Service Builder command line arguments\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[liferay-service-builder-dependency]{\\texttt{project.configurations.serviceBuilder}}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{}\n\\texttt{\"com.liferay.portal.tools.service.builder.ServiceBuilder\"}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:systemProperties}{\\texttt{systemProperties}}\n\\textbar{} \\texttt{{[}\"file.encoding\":\\ \"UTF-8\"{]}}\n\n\\subsection{Task Properties}\\label{task-properties-25}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{apiDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} A directory where the service API Java source\nfiles are generated. It sets the \\texttt{service.api.dir} argument.\n\\texttt{autoImportDefaultReferences} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{true} \\textbar{} Whether to automatically add default\nreferences, like \\texttt{com.liferay.portal.ClassName},\n\\texttt{com.liferay.portal.Resource} and\n\\texttt{com.liferay.portal.User}, to the services. It sets the\n\\texttt{service.auto.import.default.references} argument.\n\\texttt{autoNamespaceTables} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to prefix table names by the namespace\nspecified in the \\texttt{service.xml} file. It sets the\n\\texttt{service.auto.namespace.tables} argument.\n\\texttt{beanLocatorUtil} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"com.liferay.util.bean.PortletBeanLocatorUtil\"} \\textbar{} The\nfully qualified class name of a bean locator class to use in the\ngenerated service classes. It sets the\n\\texttt{service.bean.locator.util} argument. \\texttt{buildNumber}\n\\textbar{} \\texttt{long} \\textbar{} \\texttt{1} \\textbar{} A specific\nvalue to assign the \\texttt{build.number} property in the\n\\texttt{service.properties} file. It sets the\n\\texttt{service.build.number} argument. \\texttt{buildNumberIncrement}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{true} \\textbar{} Whether\nto automatically increment the \\texttt{build.number} property in the\n\\texttt{service.properties} file by one at every service generation. It\nsets the \\texttt{service.build.number.increment} argument.\n\\texttt{databaseNameMaxLength} \\textbar{} \\texttt{int} \\textbar{}\n\\texttt{30} \\textbar{} The upper bound for database table and column\nname lengths to ensure it works on all databases. It sets the\n\\texttt{service.database.name.max.length} argument. \\texttt{hbmFile}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} A Hibernate\nMapping file to generate. It sets the \\texttt{service.hbm.file}\nargument. \\texttt{implDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} A directory where the service Java source files\nare generated. It sets the \\texttt{service.impl.dir} argument.\n\\texttt{inputFile} \\textbar{} \\texttt{File} \\textbar{} \\texttt{null}\n\\textbar{} The project's \\texttt{service.xml} file. It sets the\n\\texttt{service.input.file} argument. \\texttt{modelHintsConfigs}\n\\textbar{} \\texttt{Set} \\textbar{}\n\\texttt{{[}\"classpath*:META-INF/portal-model-hints.xml\",\\ \"META-INF/portal-model-hints.xml\",\\ \"classpath*:META-INF/ext-model-hints.xml\",\\ \"classpath*:META-INF/portlet-model-hints.xml\"{]}}\n\\textbar{} Paths to the model hints files for Liferay Service Builder to\nuse in generating the service layer. It sets the\n\\texttt{service.model.hints.configs} argument. \\texttt{modelHintsFile}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} A model\nhints file for the project. It sets the\n\\texttt{service.model.hints.file} argument. \\texttt{osgiModule}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto generate the service layer for OSGi modules. It sets the\n\\texttt{service.osgi.module} argument. \\texttt{pluginName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} If specified, a\nplugin can enable additional generation features, such as \\texttt{Clp}\nclass generation, for non-OSGi modules. It sets the\n\\texttt{service.plugin.name} argument. \\texttt{propsUtil} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The fully qualified\nclass name of the service properties util class to generate. It sets the\n\\texttt{service.props.util} argument. \\texttt{readOnlyPrefixes}\n\\textbar{} \\texttt{Set} \\textbar{}\n\\texttt{{[}\"fetch\",\\ \"get\",\\ \"has\",\\ \"is\",\\ \"load\",\\ \"reindex\",\\ \"search\"{]}}\n\\textbar{} Prefixes of methods to consider read-only. It sets the\n\\texttt{service.read.only.prefixes} argument.\n\\texttt{resourceActionsConfigs} \\textbar{} \\texttt{Set} \\textbar{}\n\\texttt{{[}\"META-INF/resource-actions/default.xml\",\\ \"resource-actions/default.xml\"{]}}\n\\textbar{} Paths to the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions}{resource\nactions} files for Liferay Service Builder to use in generating the\nservice layer. It sets the \\texttt{service.resource.actions.configs}\nargument. \\texttt{resourcesDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} A directory where the service non-Java files\nare generated. It sets the \\texttt{service.resources.dir} argument.\n\\texttt{springFile} \\textbar{} \\texttt{File} \\textbar{} \\texttt{null}\n\\textbar{} A service Spring file to generate. It sets the\n\\texttt{service.spring.file} argument. \\texttt{springNamespaces}\n\\textbar{} \\texttt{Set} \\textbar{} \\texttt{{[}\"beans\"{]}} \\textbar{}\nNamespaces of Spring XML Schemas to add to the service Spring file. It\nsets the \\texttt{service.spring.namespaces} argument. \\texttt{sqlDir}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} A directory\nwhere the SQL files are generated. It sets the \\texttt{service.sql.dir}\nargument. \\texttt{sqlFileName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"tables.sql\"} \\textbar{} A name (relative to \\texttt{sqlDir})\nfor the file in which the SQL table creation instructions are generated.\nIt sets the \\texttt{service.sql.file} argument.\n\\texttt{sqlIndexesFileName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"indexes.sql\"} \\textbar{} A name (relative to \\texttt{sqlDir})\nfor the file in which the SQL index creation instructions are generated.\nIt sets the \\texttt{service.sql.indexes.file} argument.\n\\texttt{sqlSequencesFileName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"sequences.sql\"} \\textbar{} A name (relative to \\texttt{sqlDir})\nfor the file in which the SQL sequence creation instructions are\ngenerated. It sets the \\texttt{service.sql.sequences.file} argument.\n\\texttt{targetEntityName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} If specified, it's the name of the entity for\nwhich Liferay Service Builder should generate the service. It sets the\n\\texttt{service.target.entity.name} argument. \\texttt{testDir}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} If\nspecified, it's a directory where integration test Java source files are\ngenerated. It sets the \\texttt{service.test.dir} argument.\n\\texttt{uadDir} \\textbar{} \\texttt{File} \\textbar{} \\texttt{null}\n\\textbar{} A directory where the UAD (user-associated data) Java source\nfiles are generated. It sets the \\texttt{service.uad.dir} argument.\n\\texttt{uadTestIntegrationDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} A directory where integration test UAD\n(user-associated data) Java source files are generated. It sets the\n\\texttt{service.uad.test.integration.dir} argument.\n\nThe properties of type \\texttt{File} supports any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.lang.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties, to defer evaluation until task execution.\n\n\\section{Additional Configuration}\\label{additional-configuration-10}\n\nThere are additional configurations that can help you use Service\nBuilder.\n\n\\section{Liferay Service Builder\nDependency}\\label{liferay-service-builder-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{serviceBuilder} and adds a dependency to the latest released\nversion of Liferay Service Builder. It is possible to override this\nsetting and use a specific version of the tool by manually adding a\ndependency to the \\texttt{serviceBuilder} configuration:\n\n\\begin{verbatim}\ndependencies {\n    serviceBuilder group: \"com.liferay\", name: \"com.liferay.portal.tools.service.builder\", version: \"1.0.292\"\n}\n\\end{verbatim}\n\nIf you're applying the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins}{\\texttt{com.liferay.gradle.plugins}}\nor\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/sdk/gradle-plugins-workspace}{\\texttt{com.liferay.gradle.plugins.workspace}}\nplugins to your project, the Service Builder dependency is already added\nto the \\texttt{serviceBuilder} configuration. Therefore, if you try to\napply a customized version of Service Builder, it's not recognized; you\nmust override the configuration already applied.\n\nTo do this, you must customize the classpath of the\n\\texttt{buildService} task. If you're supplying the customized Service\nBuilder plugin through a module named \\texttt{custom-sb-api}, you could\nmodify the \\texttt{buildService} task like this:\n\n\\begin{verbatim}\nbuildService {\n    apiDir = \"../custom-sb-api/src/main/java\"\n    classpath = configurations.serviceBuilder.filter { file -> !file.name.contains(\"com.liferay.portal.tools.service.builder\") }\n}\n\\end{verbatim}\n\nIf you do this in conjunction with the \\texttt{serviceBuilder}\ndependency configuration, the custom Service Builder version is used.\n\n\\chapter{Source Formatter Gradle\nPlugin}\\label{source-formatter-gradle-plugin}\n\nThe Source Formatter Gradle plugin lets you format project files using\nthe\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/source-formatter}{Liferay\nSource Formatter} tool.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-19}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.source.formatter\", version: \"2.3.413\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.source.formatter\"\n\\end{verbatim}\n\nSince the plugin automatically resolves the Liferay Source Formatter\nlibrary as a dependency, you have to configure a repository that hosts\nthe library and its transitive dependencies. The Liferay CDN repository\nhosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-18}\n\nThe plugin adds two tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{checkSourceFormatting} \\textbar{} - \\textbar{}\n\\hyperref[formatsourcetask]{\\texttt{FormatSourceTask}} \\textbar{} Runs\nthe Liferay Source Formatter to check for source formatting errors.\n\\texttt{formatSource} \\textbar{} - \\textbar{}\n\\hyperref[formatsourcetask]{\\texttt{FormatSourceTask}} \\textbar{} Runs\nthe Liferay Source Formatter to format the project files.\n\nIf desired, it is possible to check for source formatting errors while\nexecuting the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html\\#N15056}{\\texttt{check}}\ntask by adding the following dependency:\n\n\\begin{verbatim}\ncheck {\n    dependsOn checkSourceFormatting\n}\n\\end{verbatim}\n\nThe same can be achieved by adding the following snippet to the\n\\texttt{build.gradle} file in the root directory of a\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{\\emph{Liferay\nWorkspace}}:\n\n\\begin{verbatim}\nsubprojects {\n    afterEvaluate {\n        if (plugins.hasPlugin(\"base\") && plugins.hasPlugin(\"com.liferay.source.formatter\")) {\n            check.dependsOn checkSourceFormatting\n        }\n    }\n}\n\\end{verbatim}\n\nThe tasks \\texttt{checkSourceFormatting} and \\texttt{formatSource} are\nautomatically skipped if another task with the same name is being\nexecuted in a parent project.\n\n\\section{FormatSourceTask}\\label{formatsourcetask}\n\nTasks of type \\texttt{FormatSourceTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, like\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.lang.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}}\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args}{\\texttt{args}}\n\\textbar{} Source Formatter command line arguments\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[liferay-source-formatter-dependency]{\\texttt{project.configurations.sourceFormatter}}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{} \\texttt{\"com.liferay.source.formatter.SourceFormatter\"}\n\n\\subsection{Task Properties}\\label{task-properties-26}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{autoFix} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{false} \\textbar{} Whether to automatically fix source formatting\nerrors. It sets the \\texttt{source.auto.fix} argument. \\texttt{baseDir}\n\\textbar{} \\texttt{File} \\textbar{} \\textbar{} The Source Formatter base\ndirectory. It sets the \\texttt{source.base.dir} argument.\n\\emph{(Read-only)} \\texttt{baseDirName} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{\"./\"} \\textbar{} The name of the Source Formatter\nbase directory, relative to the project directory.\n\\texttt{fileExtensions} \\textbar{}\n\\texttt{List\\textless{}String\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The file extensions to format. If empty, all file extensions\nwill be formatted. It sets the \\texttt{source.file.extensions} argument.\n\\texttt{files} \\textbar{} \\texttt{List\\textless{}File\\textgreater{}}\n\\textbar{} \\textbar{} The list of files to format. It sets the\n\\texttt{source.files} argument. \\emph{(Read-only)} \\texttt{fileNames}\n\\textbar{} \\texttt{List\\textless{}String\\textgreater{}} \\textbar{}\n\\texttt{null} \\textbar{} The file names to format, relative to the\nproject directory. If \\texttt{null}, all files contained in\n\\texttt{baseDir} will be formatted. \\texttt{formatCurrentBranch}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto format only the files contained in \\texttt{baseDir} that are added or\nmodified in the current Git branch. It sets the\n\\texttt{format.current.branch} argument. \\texttt{formatLatestAuthor}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto format only the files contained in \\texttt{baseDir} that are added or\nmodified in the latest Git commits of the same author. It sets the\n\\texttt{format.latest.author} argument. \\texttt{formatLocalChanges}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto format only the unstaged files contained in \\texttt{baseDir}. It sets\nthe \\texttt{format.local.changes} argument.\n\\texttt{gitWorkingBranchName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"master\"} \\textbar{} The Git working branch name. It sets the\n\\texttt{git.working.branch.name} argument.\n\\texttt{includeSubrepositories} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{false} \\textbar{} Whether to format files that are in read-only\nsubrepositories. It sets the \\texttt{include.subrepositories} argument.\n\\texttt{maxLineLength} \\textbar{} \\texttt{int} \\textbar{} \\texttt{80}\n\\textbar{} The maximum number of characters allowed in Java files. It\nsets the \\texttt{max.line.length} argument. \\texttt{printErrors}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{true} \\textbar{} Whether\nto print formatting errors on the Standard Output stream. It sets the\n\\texttt{source.print.errors} argument. \\texttt{processorThreadCount}\n\\textbar{} \\texttt{int} \\textbar{} \\texttt{5} \\textbar{} The number of\nthreads used by Source Formatter. It sets the\n\\texttt{processor.thread.count} argument. \\texttt{showDebugInformation}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto show debug information, if present. It sets the\n\\texttt{show.debug.information} argument. \\texttt{showDocumentation}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto show the documentation for the source formatting issues, if present.\nIt sets the \\texttt{show.documentation} argument.\n\\texttt{showStatusUpdates} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{false} \\textbar{} Whether to show status updates during source\nformatting, if present. It sets the \\texttt{show.status.updates}\nargument. \\texttt{throwException} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{false} \\textbar{} Whether to fail the build if formatting errors\nare found. It sets the \\texttt{source.throw.exception} argument.\n\n\\section{Additional Configuration}\\label{additional-configuration-11}\n\nThere are additional configurations that can help you use the Source\nFormatter.\n\n\\section{Liferay Source Formatter\nDependency}\\label{liferay-source-formatter-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{sourceFormatter} and adds a dependency to the latest released\nversion of Liferay Source Formatter. It is possible to override this\nsetting and use a specific version of the tool by manually adding a\ndependency to the \\texttt{sourceFormatter} configuration:\n\n\\begin{verbatim}\ndependencies {\n    sourceFormatter group: \"com.liferay\", name: \"com.liferay.source.formatter\", version: \"1.0.885\"\n}\n\\end{verbatim}\n\n\\section{System Properties}\\label{system-properties-3}\n\nIt is possible to set the default values of the \\texttt{fileExtensions},\n\\texttt{fileNames}, \\texttt{formatCurrentBranch},\n\\texttt{formatLatestAuthor}, and \\texttt{formatLocalChanges} properties\nfor a \\texttt{FormatSourceTask} task via system properties:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{-D\\$\\{task.name\\}.file.extensions=java,xml}\n\\item\n  \\texttt{-D\\$\\{task.name\\}.file.names=README.markdown,src/main/resources/hello.txt}\n\\item\n  \\texttt{-D\\$\\{task.name\\}.format.current.branch=true}\n\\item\n  \\texttt{-D\\$\\{task.name\\}.format.latest.author=true}\n\\item\n  \\texttt{-D\\$\\{task.name\\}.format.local.changes=true}\n\\end{itemize}\n\nFor example, run the following Bash command to format only the unstaged\nfiles in the project:\n\n\\begin{verbatim}\n./gradlew formatSource -DformatSource.format.local.changes=true\n\\end{verbatim}\n\n\\chapter{Soy Gradle Plugin}\\label{soy-gradle-plugin}\n\nThe Soy Gradle plugin lets you compile\n\\href{https://developers.google.com/closure/templates/}{Closure\nTemplates} into JavaScript functions. It also lets you use a custom\nlocalization mechanism in the generated \\texttt{.soy.js} files by\nreplacing\n\\href{https://developers.google.com/closure/templates/docs/translation\\#closurecompiler}{\\texttt{goog.getMsg}}\ndefinitions with a different function call (e.g.,\n\\texttt{Liferay.Language.get}).\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-20}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.soy\", version: \"3.1.8\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\\end{verbatim}\n\nThere are two Soy Gradle plugins you can apply to your project:\n\n\\begin{itemize}\n\\item\n  Apply the \\hyperref[soy-plugin]{\\emph{Soy Plugin}} to compile Closure\n  Templates into JavaScript functions:\n\n\\begin{verbatim}\napply plugin: \"com.liferay.soy\"\n\\end{verbatim}\n\\item\n  Apply the \\hyperref[soy-translation-plugin]{\\emph{Soy Translation\n  Plugin}} to use a custom localization mechanism in the generated\n  \\texttt{.soy.js} files:\n\n\\begin{verbatim}\napply plugin: \"com.liferay.soy.translation\"\n\\end{verbatim}\n\\end{itemize}\n\nSince the Soy Gradle plugin automatically resolves the Soy library as a\ndependency, you have to configure a repository that hosts the library\nand its transitive dependencies. The Liferay CDN repository hosts them\nall:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Soy Plugin}\\label{soy-plugin}\n\nThe Soy plugin adds two tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{buildSoy} \\textbar{} - \\textbar{}\n\\hyperref[buildsoytask]{\\texttt{BuildSoyTask}} \\textbar{} Compiles\nClosure Templates into JavaScript functions.\n\\texttt{wrapSoyAlloyTemplate} \\textbar{} - \\texttt{configJSModules} if\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-js-module-config-generator}{\\texttt{com.liferay.js.module.config.generator}}\nis applied - \\texttt{processResources} if \\texttt{java} is applied -\n\\texttt{transpileJS} if\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-js-transpiler}{\\texttt{com.liferay.js.transpiler}}\nis applied \\textbar{}\n\\hyperref[wrapsoyalloytemplatetask]{\\texttt{WrapSoyAlloyTemplateTask}}\n\\textbar{} Wraps the JavaScript functions compiled from Closure\nTemplates into AlloyUI modules.\n\nThe plugin also adds the following dependencies to tasks defined by the\n\\texttt{java} plugin:\n\nName \\textbar{} Depends On \\texttt{classes} \\textbar{}\n\\texttt{wrapSoyAlloyTemplate}\n\nThe \\texttt{buildSoy} task is automatically configured with sensible\ndefaults, depending on whether the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin is applied:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:includes}{\\texttt{includes}}\n\\textbar{} \\texttt{{[}\"**/*.soy\"{]}}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:source}{\\texttt{source}}\n\\textbar{}\n\n\\textbf{If the \\texttt{java} plugin is applied:} The first\n\\texttt{resources} directory of the \\texttt{main} source set (by\ndefault, \\texttt{src/main/resources}).\n\n\\textbf{Otherwise:} \\texttt{{[}{]}}\n\nThe \\texttt{wrapSoyAlloyTemplate} task is \\textbf{disabled by default},\nand it is automatically configured with sensible defaults, depending on\nwhether the \\texttt{java} plugin is applied:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Task.html\\#org.gradle.api.Task:enabled}{\\texttt{enabled}}\n\\textbar{} \\texttt{false}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:includes}{\\texttt{includes}}\n\\textbar{} \\texttt{{[}\"**/*.soy.js\"{]}}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:source}{\\texttt{source}}\n\\textbar{}\n\n\\textbf{If the \\texttt{java} plugin is applied:}\n\\texttt{project.sourceSets.main.output.resourcesDir}\n\n\\textbf{Otherwise:} \\texttt{{[}{]}}\n\n\\section{Additional Configuration}\\label{additional-configuration-12}\n\nThere are additional configurations that can help you use the Soy\nlibrary.\n\n\\subsection{Soy Dependency}\\label{soy-dependency}\n\nBy default, the plugin creates a configuration called \\texttt{soy} and\nadds a dependency to the \\texttt{2015-04-10} version of the Soy library.\nIt is possible to override this setting and use a specific version of\nthe tool by manually adding a dependency to the \\texttt{soy}\nconfiguration:\n\n\\begin{verbatim}\ndependencies {\n    soy group: \"com.google.template\", name: \"soy\", version: \"2015-04-10\"\n}\n\\end{verbatim}\n\n\\section{Soy Translation Plugin}\\label{soy-translation-plugin}\n\nThe Soy Translation plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{replaceSoyTranslation} \\textbar{} - \\texttt{configJSModules} if\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-js-module-config-generator}{\\texttt{com.liferay.js.module.config.generator}}\nis applied - \\texttt{processResources} if \\texttt{java} is applied -\n\\texttt{transpileJS} if\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-js-transpiler}{\\texttt{com.liferay.js.transpiler}}\nis applied \\textbar{}\n\\hyperref[replacesoytranslationtask]{\\texttt{ReplaceSoyTranslationTask}}\n\\textbar{} Replaces \\texttt{goog.getMsg} definitions with\n\\texttt{Liferay.Language.get} calls.\n\nThe plugin also adds the following dependencies to tasks defined by the\n\\texttt{java} plugin:\n\nName \\textbar{} Depends On \\texttt{classes} \\textbar{}\n\\texttt{replaceSoyTranslation}\n\nThe \\texttt{replaceSoyTranslation} task is automatically configured with\nsensible defaults, depending on whether the \\texttt{java} plugin is\napplied:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:includes}{\\texttt{includes}}\n\\textbar{} \\texttt{{[}\"**/*.soy.js\"{]}}\n\\hyperref[replacementclosure]{\\texttt{replacementClosure}} \\textbar{}\nReplaces \\texttt{goog.getMsg} definitions with\n\\texttt{Liferay.Language.get} calls.\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:source}{\\texttt{source}}\n\\textbar{}\n\n\\textbf{If the \\texttt{java} plugin is applied:}\n\\texttt{project.sourceSets.main.output.resourcesDir}\n\n\\textbf{Otherwise:} \\texttt{{[}{]}}\n\n\\section{Tasks}\\label{tasks-19}\n\n\\section{BuildSoyTask}\\label{buildsoytask}\n\nTasks of type \\texttt{BuildSoyTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html}{\\texttt{SourceTask}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:include(java.lang.Iterable)}{\\texttt{include}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:exclude(java.lang.Iterable)}{\\texttt{exclude}},\nare available.\n\n\\subsection{Task Properties}\\label{task-properties-27}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{classpath} \\textbar{}\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/api/file/FileCollection.html}{\\texttt{FileCollection}}\n\\textbar{}\n\\hyperref[soy-dependency]{\\texttt{project.configurations.soy}}\n\\textbar{} The classpath for executing the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-soy-builder}{Liferay\nPortal Tools Soy Builder}.\n\n\\section{WrapSoyAlloyTemplateTask}\\label{wrapsoyalloytemplatetask}\n\nTasks of type \\texttt{WrapSoyAlloyTemplateTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html}{\\texttt{SourceTask}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:include(java.lang.Iterable)}{\\texttt{include}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:exclude(java.lang.Iterable)}{\\texttt{exclude}},\nare available.\n\n\\subsection{Task Properties}\\label{task-properties-28}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{moduleName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The name of the AlloyUI module.\n\\texttt{namespace} \\textbar{} \\texttt{String} \\textbar{} \\texttt{null}\n\\textbar{} The namespace of the Closure Templates of the project.\n\nIt is possible to use Closures and Callables as values for the\n\\texttt{String} properties to defer evaluation until task execution.\n\n\\section{ReplaceSoyTranslationTask}\\label{replacesoytranslationtask}\n\nThe \\texttt{ReplaceSoyTranslationTask} task type finds all the\n\\texttt{goog.getMsg} definitions in the project's files and replaces\nthem with a custom function call.\n\n\\begin{verbatim}\nvar MSG_EXTERNAL_123 = goog.getMsg('welcome-to-{$releaseInfo}', { 'releaseInfo': opt_data.releaseInfo });\n\\end{verbatim}\n\nA \\texttt{goog.getMsg} definition looks like the example above, and it\nhas the following components:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{variable name}: \\texttt{MSG\\_EXTERNAL\\_123}\n\\item\n  \\emph{language key}: \\texttt{welcome-to-\\{\\$releaseInfo\\}}\n\\item\n  \\emph{arguments object}:\n  \\texttt{\\{\\ \\textquotesingle{}releaseInfo\\textquotesingle{}:\\ opt\\_data.releaseInfo\\ \\}}\n\\end{itemize}\n\nTasks of type \\texttt{ReplaceSoyTranslationTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html}{\\texttt{SourceTask}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:include(java.lang.Iterable)}{\\texttt{include}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:exclude(java.lang.Iterable)}{\\texttt{exclude}},\nare available.\n\n\\subsection{Task Properties}\\label{task-properties-29}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{replacementClosure} \\textbar{}\n\\texttt{Closure\\textless{}String\\textgreater{}} \\textbar{} \\texttt{null}\n\\textbar{} The Closure invoked in order to get the replacement for\n\\texttt{goog.getMsg} definitions. The given Closure is passed the\n\\emph{variable name}, \\emph{language key}, and \\emph{arguments object}\nas its parameters.\n\n\\chapter{Target Platform Gradle\nPlugin}\\label{target-platform-gradle-plugin}\n\nThe Target Platform Gradle plugin helps with building multiple projects\nagainst a declared API target platform. Java dependencies can be managed\nwith Maven BOMs and OSGi modules can be resolved against an OSGi\ndistribution.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-21}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.target.platform\", version: \"2.0.0\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\\end{verbatim}\n\nThere are two Target Platform Gradle plugins you can apply to your\nproject. If you have a multi-module Gradle project, you only need to\napply these plugins to the root project.\n\n\\begin{itemize}\n\\item\n  The \\hyperref[target-platform-plugin]{\\emph{Target Platform Plugin}}\n  helps to configure your projects to build against an established set\n  of platform artifacts, including Java and OSGi dependencies.\n\n\\begin{verbatim}\napply plugin: \"com.liferay.target.platform\"\n\\end{verbatim}\n\\item\n  The \\hyperref[target-platform-ide-plugin]{\\emph{Target Platform IDE\n  Plugin}} is a superset of the Target Platform Plugin (it applies the\n  above plugin) and also adds IDE integration for searching and\n  debugging source code in the target platform artifacts.\n\n\\begin{verbatim}\napply plugin: \"com.liferay.target.platform.ide\"\n\\end{verbatim}\n\\end{itemize}\n\nSince the plugin automatically resolves target platform configurations\nas dependencies, you must configure a repository that hosts these\nartifacts. The Liferay CDN repository hosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Target Platform Plugin}\\label{target-platform-plugin}\n\nThe plugin applies the\n\\href{https://github.com/spring-gradle-plugins/dependency-management-plugin}{Spring\nDependency Management Plugin} and then adds several specific\nconfigurations to configure the BOMs that are imported to manage Java\ndependencies and the various artifacts used in resolving OSGi\ndependencies. Also, a new \\texttt{resolve} task is added to resolve all\nOSGi requirements against a declared distribution artifact.\n\nThe plugin adds a series of configurations to your project:\n\nName \\textbar{} Description \\texttt{targetPlatformBoms} \\textbar{}\nConfigures all the BOMs to import as managed dependencies.\n\\texttt{targetPlatformBundles} \\textbar{} Configures all the bundles in\naddition to the distro to resolve against. \\texttt{targetPlatformDistro}\n\\textbar{} Configures the distro JAR file to use as base for resolving\nagainst. \\texttt{targetPlatformRequirements} \\textbar{} Configures the\nlist of JAR files to use as run requirements for resolving.\n\nThe plugin adds a task \\texttt{resolve} of type\n\\hyperref[resolvetask]{\\texttt{ResolveTask}} to your project that\nperforms an OSGi resolve operation using the\n\\texttt{targetPlatformRequirements} configuration as the basis of the\nrequirements. The \\texttt{targetPlatformBundles} configuration is used\nas a repository for the resolver to resolve requirements. Lastly, the\n\\texttt{targetPlatformDistro} configuration is used to provide the\n\\emph{distro} artifact for the resolve process. The \\emph{distro} is the\nartifact that provides all the OSGi capabilities of the target platform.\nAll of these parameters are used to create a \\texttt{bndrun} file that\ncan be used as input into the Bndrun resolve operation.\n\n\\section{Target Platform IDE Plugin}\\label{target-platform-ide-plugin}\n\nThe plugin applies the \\hyperref[target-platform-plugin]{Target\nPlatform} and the\n\\href{https://docs.gradle.org/current/userguide/eclipse_plugin.html}{\\texttt{eclipse}}\nplugins to your project, and also adds a special\n\\texttt{targetPlatformIDE} configuration, which is used to configure\nboth the \\texttt{eclipse} model and \\texttt{idea} plugin model in Gradle\nto add all target platform artifacts to the classpath so they are\nvisible to both Eclipse and IntelliJ's Java Model Search (for looking up\nsources to classes).\n\n\\section{Project Extension}\\label{project-extension-7}\n\nThe Target Platform plugin exposes the following properties through the\nextension named \\texttt{targetPlatform}:\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{ignoreResolveFailures} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{true} \\textbar{} Whether to ignore resolve failures\nfound when executing tasks of type\n\\hyperref[resolvetask]{\\texttt{ResolveTask}}. \\texttt{subprojects}\n\\textbar{} \\texttt{Set\\textless{}Project\\textgreater{}} \\textbar{}\n\\texttt{project.subprojects} \\textbar{} The subprojects to configure\nwith target platform support, including dependency management and the\n\\texttt{resolve} task.\n\nThe same extension exposes the following methods:\n\nMethod \\textbar{} Description\n\\texttt{TargetPlatformExtension\\ applyToConfiguration(Iterable\\textless{}?\\textgreater{}\\ configurationNames)}\n\\textbar{} Adds additional configurations to configure the BOMs that are\nimported to manage Java dependencies and the various artifacts used in\nresolving OSGi dependencies.\n\\texttt{TargetPlatformExtension\\ applyToConfiguration(Object...\\ configurationNames)}\n\\textbar{} Adds additional configurations to configure the BOMs that are\nimported to manage Java dependencies and the various artifacts used in\nresolving OSGi dependencies.\n\\texttt{TargetPlatformExtension\\ onlyIf(Closure\\textless{}Boolean\\textgreater{}\\ onlyIfClosure)}\n\\textbar{} Includes a subproject in the target platform configuration if\nthe given closure returns \\texttt{true}. The closure is evaluated at the\nend of the subproject configuration phase and is passed a single\nparameter: the subproject. If the closure returns \\texttt{false}, the\nsubproject is not included in the target platform configuration\n\\texttt{TargetPlatformExtension\\ onlyIf(Spec\\textless{}Project\\textgreater{}\\ onlyIfSpec)}\n\\textbar{} Includes a subproject in the target platform configuration if\nthe given spec is satisfied. The spec is evaluated at the end of the\nsubproject configuration phase. If the spec is not satisfied, the\nsubproject is not included in the target platform configuration.\n\\texttt{TargetPlatformExtension\\ resolveOnlyIf(Closure\\textless{}Boolean\\textgreater{}\\ resolveOnlyIfClosure)}\n\\textbar{} Includes a subproject in the resolving process (including\nboth the requirements and bundles configuration) if the given closure\nreturns \\texttt{true}. The closure is evaluated at the end of the\nsubproject configuration phase and is passed a single parameter: the\nsubproject. If the closure returns \\texttt{false}, the subproject is the\nresolution process.\n\\texttt{TargetPlatformExtension\\ resolveOnlyIf(Spec\\textless{}Project\\textgreater{}\\ resolveOnlyIfSpec)}\n\\textbar{} Includes a subproject in the resolving platform configuration\nif the given spec is satisfied. The spec is evaluated at the end of the\nsubproject configuration phase. If the spec is not satisfied, the\nsubproject is not included in the target platform configuration.\n\\texttt{TargetPlatformExtension\\ subprojects(Iterable\\textless{}Project\\textgreater{}\\ subprojects)}\n\\textbar{} Includes additional projects to be configured with Target\nPlatform support.\n\\texttt{TargetPlatformExtension\\ subprojects(Project...\\ subprojects)}\n\\textbar{} Includes additional projects to be configured with Target\nPlatform support.\n\n\\section{Tasks}\\label{tasks-20}\n\n\\section{ResolveTask}\\label{resolvetask}\n\nThe purpose of this task is to resolve an OSGi module (or all OSGi\nmodules of subprojects) against the available\n\\texttt{targetPlatformBundles} and \\texttt{targetPlatformDistro}\nconfigurations. By default, the \\texttt{targetPlatformBundles} are all\nthe artifacts created by all the subprojects. The\n\\texttt{targetPlatformDistro} must be set explicitly to a valid\ndistribution artifact. When the task is performed, a \\texttt{bndrun}\nfile is generated using the specified \\texttt{targetPlatformDistro} as\nthe \\texttt{-distro} instruction; the \\texttt{-runrequirements} are a\nset of \\texttt{osgi.identity} requirements for the\n\\texttt{targetPlatformRequirements} configuration. If the resolve\noperation is able to find a valid set of \\texttt{-runbundles} that match\nthe \\texttt{-runrequirements}, then the task passes successfully (the\nresolution is valid). If a set of run bundles can't be found, the\nresolution has failed and the failed requirements are listed as output\nof the task.\n\n\\subsection{Task Properties}\\label{task-properties-30}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{bndrunFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} If this property is specified, it is used as\nthe \\texttt{bndrun} file to input into the resolver.\n\\texttt{bundlesFileCollection} \\textbar{} \\texttt{FileCollection}\n\\textbar{} All JAR files of subprojects with \\texttt{jar} task\n\\textbar{} The input to \\texttt{bndrun} resolve operation.\n\\texttt{distroFileCollection} \\textbar{} \\texttt{FileCollection}\n\\textbar{} \\texttt{null} \\textbar{} The \\emph{distro} parameter for the\ngenerated \\texttt{bndrun} file. \\texttt{ignoreFailures} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether the\n\\texttt{resolve} task should ignore failing the build for resolution\nerrors. \\texttt{offline} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{null} \\textbar{} Whether to run the bndrun resolve operation in\noffline mode. \\texttt{requirementsFileCollection} \\textbar{}\n\\texttt{FileCollection} \\textbar{}\n\n\\textbf{For the root project:} All the output JAR files of the\nsubprojects.\n\n\\textbf{For subprojects:} The output JAR file of the subproject.\n\n\\textbar{} For each resolve operation, the requirements must be\nspecified in the \\texttt{bndrun} file; each of the JARs in this\ncollection generate an \\texttt{osgi.identify} requirement in the\n\\texttt{bndrun} file.\n\n\\section{Additional Configuration}\\label{additional-configuration-13}\n\nThere are additional configurations that you can use to configure the\ntarget platform.\n\n\\section{Target Platform BOMs\nDependency}\\label{target-platform-boms-dependency}\n\nThe plugin creates a configuration called \\texttt{targetPlatformBoms}\nwith no defaults. You can use this dependency to set which BOMs to\nimport to configure your target platform.\n\n\\begin{verbatim}\ndependencies {\n    targetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom\", version: \"7.2.0\"\n    targetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom.compile.only\", version: \"7.2.0\"\n}\n\\end{verbatim}\n\n\\section{Target Platform Bundles\nDependency}\\label{target-platform-bundles-dependency}\n\nThe plugin creates a configuration called\n\\texttt{targetPlatformBundles}. It is configured with default\ndependencies to all resolvable bundles in a multi-project build (e.g.,\nall projects in multi-project build that have a \\texttt{jar} task). This\ncan be used to specify additional bundles that should be added to the\nset of bundles given to \\texttt{resolve} task to resolve against when\nchecking for OSGi requirements.\n\n\\begin{verbatim}\ndependencies {\n    targetPlatformBundles group: \"com.google.guava\", name: \"guava\", version: \"23.0\"\n}\n\\end{verbatim}\n\n\\section{Target Platform Distro\nDependency}\\label{target-platform-distro-dependency}\n\nThe plugin creates a configuration called \\texttt{targetPlatformDistro}.\nIt is has no default so you must specify which artifact you want to use\nas the distribution to resolve against.\n\n\\begin{verbatim}\ndependencies {\n    targetPlatformDistro group: \"com.liferay.portal\", name: \"release.portal.distro\", version: \"7.2.0\"\n}\n\\end{verbatim}\n\nIf you have created your own custom distro JAR that is available\nlocally, you can use the \\texttt{files} method to add it to the\nconfiguration.\n\n\\begin{verbatim}\ndependencies {\n    targetPlatformDistro files(\"custom-distro.jar\")\n}\n\\end{verbatim}\n\n\\section{Target Platform Requirements\nDependency}\\label{target-platform-requirements-dependency}\n\nThe plugin creates a configuration called\n\\texttt{targetPlatformRequirements}. It is configured with default\ndependencies to all resolvable bundles in a multi-project build (e.g.,\nall projects in multi-project build that have a \\texttt{jar} task). This\nis can be used to specify additional bundles that should be added to the\nset of bundles given to the \\texttt{resolve} task to set as\n\\texttt{osgi.identity} requirements.\n\n\\begin{verbatim}\ndependencies {\n    targetPlatformRequirements group: \"com.liferay\", name: \"com.liferay.other.bundle\", version: \"1.0\"\n}\n\\end{verbatim}\n\n\\chapter{Theme Builder Gradle Plugin}\\label{theme-builder-gradle-plugin}\n\nThe Theme Builder Gradle plugin lets you run the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-theme-builder}{Liferay\nTheme Builder} tool to build the Liferay theme files in your project.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-22}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.theme.builder\", version: \"2.0.7\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.portal.tools.theme.builder\"\n\\end{verbatim}\n\nThe Theme Builder plugin automatically applies the\n\\href{https://docs.gradle.org/current/userguide/war_plugin.html}{\\texttt{war}}\nplugin. It also applies the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-css-builder}{\\texttt{com.liferay.css.builder}}\nplugin to compile the \\href{http://sass-lang.com/}{Sass} files in the\ntheme.\n\nSince the plugin automatically resolves the Liferay Theme Builder\nlibrary as a dependency, you have to configure a repository that hosts\nthe library and its transitive dependencies. The Liferay CDN repository\nhosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-21}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{buildTheme} \\textbar{} - \\textbar{}\n\\hyperref[buildthemetask]{\\texttt{BuildThemeTask}} \\textbar{} Builds the\ntheme files.\n\nThe plugin also adds the following dependencies to tasks defined by the\n\\texttt{com.liferay.css.builder} and \\texttt{war} plugins:\n\nName \\textbar{} Depends On\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-css-builder\\#tasks}{\\texttt{buildCSS}}\n\\textbar{} \\texttt{buildTheme}\n\\href{https://docs.gradle.org/current/userguide/war_plugin.html\\#sec:war_default_settings}{\\texttt{war}}\n\\textbar{} \\texttt{buildTheme}\n\nThe \\texttt{buildCSS} dependency compiles the Sass files contained in\nthe directory specified by the\n\\hyperref[outputdir]{\\texttt{buildTheme.outputDir}} property. Moreover,\nthe \\texttt{war} task is configured as follows\n\n\\begin{itemize}\n\\tightlist\n\\item\n  exclude the directory specified in the\n  \\hyperref[diffsdir]{\\texttt{buildTheme.diffsDir}} property from the\n  WAR file.\n\\item\n  include the files contained in the\n  \\hyperref[outputdir]{\\texttt{buildTheme.outputDir}} directory into the\n  WAR file.\n\\item\n  include only the compiled CSS files, not SCSS files, into the WAR\n  file.\n\\end{itemize}\n\nThe \\texttt{buildTheme} task is automatically configured with sensible\ndefaults:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[diffsdir]{\\texttt{diffsDir}} \\textbar{}\n\\texttt{project.webAppDir} \\hyperref[outputdir]{\\texttt{outputDir}}\n\\textbar{} \\texttt{\"\\$\\{project.buildDir\\}/buildTheme\"}\n\\hyperref[parentfile]{\\texttt{parentFile}} \\textbar{} The first JAR file\nin the \\hyperref[parent-theme-dependencies]{\\texttt{parentThemes}}\nconfiguration that contains a\n\\texttt{META-INF/resources/\\$\\{buildTheme.parentName\\}} directory, or\nthe first WAR file in the \\texttt{parentThemes} configuration whose name\nstarts with \\texttt{\\$\\{parentName\\}-theme-}.\n\\hyperref[parentname]{\\texttt{parentName}} \\textbar{}\n\\texttt{\"\\_styled\"}\n\\hyperref[templateextension]{\\texttt{templateExtension}} \\textbar{}\n\\texttt{\"ftl\"} \\hyperref[themename]{\\texttt{themeName}} \\textbar{}\n\\texttt{project.name} \\hyperref[unstyledfile]{\\texttt{unstyledFile}}\n\\textbar{} The first JAR file in the\n\\hyperref[parent-theme-dependencies]{\\texttt{parentThemes}}\nconfiguration that contains a \\texttt{META-INF/resources/\\_unstyled}\ndirectory.\n\n\\section{BuildThemeTask}\\label{buildthemetask}\n\nTasks of type \\texttt{BuildThemeTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.css.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}},\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args}{\\texttt{args}}\n\\textbar{} Theme Builder command line arguments\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[liferay-theme-builder-dependency]{\\texttt{project.configurations.themeBuilder}}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{}\n\\texttt{\"com.liferay.portal.tools.theme.builder.ThemeBuilder\"}\n\n\\subsection{Task Properties}\\label{task-properties-31}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{diffsDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The directory that contains the files to copy\nover the parent theme. It sets the \\texttt{-\\/-diffs-dir} argument.\n\\texttt{outputDir} \\textbar{} \\texttt{File} \\textbar{} \\texttt{null}\n\\textbar{} The directory where to build the theme. It sets the\n\\texttt{-\\/-output-dir} argument. \\texttt{parentDir} \\textbar{}\n\\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The directory of the\nparent theme. It sets the \\texttt{-\\/-parent-path} argument.\n\\texttt{parentFile} \\textbar{} \\texttt{File} \\textbar{} \\texttt{null}\n\\textbar{} The JAR file of the parent theme. If \\texttt{parentDir} is\nspecified, this property has no effect. It sets the\n\\texttt{-\\/-parent-path} argument. \\texttt{parentName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The name of the\nparent theme. It sets the \\texttt{-\\/-parent-name} argument.\n\\texttt{templateExtension} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The extension of the template files, usually\n\\texttt{\"ftl\"} or \\texttt{\"vm\"}. It sets the\n\\texttt{-\\/-template-extension} argument. \\texttt{themeName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The name of the new\ntheme. It sets the \\texttt{-\\/-name} argument. \\texttt{unstyledDir}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The\ndirectory of\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-unstyled}{Liferay\nFrontend Theme Unstyled}. It sets the \\texttt{-\\/-unstyled-dir}\nargument. \\texttt{unstyledFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The JAR file of\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-unstyled}{Liferay\nFrontend Theme Unstyled}. If \\texttt{unstyledDir} is specified, this\nproperty has no effect. It sets the \\texttt{-\\/-unstyled-dir} argument.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.css.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties to defer evaluation until task execution.\n\n\\section{Additional Configuration}\\label{additional-configuration-14}\n\nThere are additional configurations that can help you use the CSS\nBuilder.\n\n\\section{Liferay Theme Builder\nDependency}\\label{liferay-theme-builder-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{themeBuilder} and adds a dependency to the latest released\nversion of the Liferay Theme Builder. It is possible to override this\nsetting and use a specific version of the tool by manually adding a\ndependency to the \\texttt{themeBuilder} configuration:\n\n\\begin{verbatim}\ndependencies {\n    themeBuilder group: \"com.liferay\", name: \"com.liferay.portal.tools.theme.builder\", version: \"1.1.7\"\n}\n\\end{verbatim}\n\n\\section{Parent Theme Dependencies}\\label{parent-theme-dependencies}\n\nBy default, the plugin creates a configuration called\n\\texttt{parentThemes} and adds dependencies to the latest released\nversions of the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-styled}{Liferay\nFrontend Theme Styled},\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-unstyled}{Liferay\nFrontend Theme Unstyled}, and\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-classic}{Liferay\nFrontend Theme Classic} artifacts. It is possible to override this\nsetting and use a specific version of the artifacts by manually adding\ndependencies to the \\texttt{parentThemes} configuration. For example,\n\n\\begin{verbatim}\ndependencies {\n    parentThemes group: \"com.liferay\", name: \"com.liferay.frontend.theme.styled\", version: \"VERSION\"\n    parentThemes group: \"com.liferay\", name: \"com.liferay.frontend.theme.unstyled\", version: \"VERSION\"\n    parentThemes group: \"com.liferay.plugins\", name: \"classic-theme\", version: \"VERSION\"\n}\n\\end{verbatim}\n\nSpecifying dependency versions is not required when leveraging\nworkspace's\n\\href{/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform}{Target\nPlatform} functionality. All dependencies with the group ID\n\\texttt{com.liferay} or \\texttt{com.liferay.portal} are automatically\nset when targeting a platform. For external theme dependencies (e.g.,\n\\texttt{classic-theme} with the group ID \\texttt{com.liferay.plugins}),\nyou can find the version used by your specific Liferay DXP instance by\nleveraging the\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nshell}. In a Gogo shell prompt, execute the following command:\n\n\\begin{verbatim}\nlb -s theme\n\\end{verbatim}\n\nThis lists the deployed theme bundles and their versions. Extract the\nversions for the theme dependencies you want to leverage and add them to\nyour configuration.\n\n\\chapter{TLD Formatter Gradle Plugin}\\label{tld-formatter-gradle-plugin}\n\nThe TLD Formatter Gradle plugin lets you format a project's TLD files\nusing the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/tld-formatter}{Liferay\nTLD Formatter} tool.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-23}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.tld.formatter\", version: \"1.0.9\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.tld.formatter\"\n\\end{verbatim}\n\nSince the plugin automatically resolves the Liferay TLD Formatter\nlibrary as a dependency, you have to configure a repository that hosts\nthe library and its transitive dependencies. The Liferay CDN repository\nhosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-22}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{formatTLD} \\textbar{} - \\textbar{}\n\\hyperref[formattldtask]{\\texttt{FormatTLDTask}} \\textbar{} Runs the\nLiferay TLD Formatter to format files.\n\n\\section{FormatTLDTask}\\label{formattldtask}\n\nTasks of type \\texttt{FormatTLDTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.lang.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}},\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args}{\\texttt{args}}\n\\textbar{} TLD Formatter command line arguments\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[liferay-tld-formatter-dependency]{\\texttt{project.configurations.tldFormatter}}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{} \\texttt{\"com.liferay.tld.formatter.TLDFormatter\"}\n\n\\subsection{Task Properties}\\label{task-properties-32}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{plugin} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to format all the TLD files contained\nin the\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:workingDir}{\\texttt{workingDir}}\ndirectory. If \\texttt{false}, all \\texttt{liferay-portlet-ext.tld} files\nare ignored. It sets the \\texttt{tld.plugin} argument.\n\n\\section{Additional Configuration}\\label{additional-configuration-15}\n\nThere are additional configurations that can help you use the TLD\nFormatter.\n\n\\section{Liferay TLD Formatter\nDependency}\\label{liferay-tld-formatter-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{tldFormatter} and adds a dependency to the latest released\nversion of Liferay TLD Formatter. It is possible to override this\nsetting and use a specific version of the tool by manually adding a\ndependency to the \\texttt{tldFormatter} configuration:\n\n\\begin{verbatim}\ndependencies {\n    tldFormatter group: \"com.liferay\", name: \"com.liferay.tld.formatter\", version: \"1.0.5\"\n}\n\\end{verbatim}\n\n\\chapter{TLDDoc Builder Gradle\nPlugin}\\label{tlddoc-builder-gradle-plugin}\n\nThe TLDDoc Builder Gradle plugin lets you run the\n\\href{http://web.archive.org/web/20070624180825/https://taglibrarydoc.dev.java.net/}{Tag\nLibrary Documentation Generator} tool in order to generate documentation\nfor the JSP Tag Library Descriptor (TLD) files in your project.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-24}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.tlddoc.builder\", version: \"1.3.3\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\\end{verbatim}\n\nThere are two TLDDoc Builder Gradle plugins you can apply to your\nproject:\n\n\\begin{itemize}\n\\item\n  Apply the \\hyperref[tlddoc-builder-plugin]{\\emph{TLDDoc Builder\n  Plugin}} to generate tag library documentation for your project:\n\n\\begin{verbatim}\napply plugin: \"com.liferay.tlddoc.builder\"\n\\end{verbatim}\n\\item\n  Apply the \\hyperref[app-tlddoc-builder-plugin]{\\emph{App TLDDoc\n  Builder Plugin}} in a parent project to generate the tag library\n  documentation as a single, combined HTML document for an application\n  that spans different subprojects, each one representing a different\n  component of the same application:\n\n\\begin{verbatim}\napply plugin: \"com.liferay.app.tlddoc.builder\"\n\\end{verbatim}\n\\end{itemize}\n\nSince the plugin automatically resolves the Tag Library Documentation\nGenerator library as a dependency, you must configure a repository that\nhosts the library and its transitive dependencies. The Liferay CDN\nrepository hosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{TLDDoc Builder Plugin}\\label{tlddoc-builder-plugin}\n\nThe plugin adds three tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{copyTLDDocResources} \\textbar{} - \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html}{\\texttt{Copy}}\n\\textbar{} Copies the tag library documentation resources from\n\\texttt{src/main/tlddoc} to the \\hyperref[destinationdir]{destination\ndirectory} of the \\texttt{tlddoc} task. \\texttt{tlddoc} \\textbar{}\n\\texttt{copyTLDDocResources}, \\texttt{validateTLD} \\textbar{}\n\\hyperref[tlddoctask]{\\texttt{TLDDocTask}} \\textbar{} Generates the tag\nlibrary documentation. \\texttt{validateTLD} \\textbar{} - \\textbar{}\n\\hyperref[validateschematask]{\\texttt{ValidateSchemaTask}} \\textbar{}\nValidates the TLD files in the project.\n\nThe \\texttt{tlddoc} task is automatically configured with sensible\ndefaults, depending on whether the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin is applied:\n\nProperty Name \\textbar{} Default Value with the \\texttt{java} plugin\n\\hyperref[destinationdir]{\\texttt{destinationDir}} \\textbar{}\n\\texttt{\\$\\{project.docsDir\\}/tlddoc}\n\\hyperref[includes]{\\texttt{includes}} \\textbar{}\n\\texttt{{[}\"**/*.tld\"{]}} \\hyperref[source]{\\texttt{source}} \\textbar{}\n\\texttt{project.sourceSets.main.resources.srcDirs}\n\nThe \\texttt{validateTLD} task is also automatically configured with\nsensible defaults, depending on whether the \\texttt{java} plugin is\napplied:\n\nProperty Name \\textbar{} Default Value \\texttt{includes} \\textbar{}\n\n\\textbf{If the \\texttt{java} plugin is applied:}\n\\texttt{{[}\"**/*.tld\"{]}}\n\n\\textbf{Otherwise:} \\texttt{{[}{]}}\n\n\\texttt{source} \\textbar{}\n\n\\textbf{If the \\texttt{java} plugin is applied:}\n\\texttt{project.sourceSets.main.resources.srcDirs}\n\n\\textbf{Otherwise:} \\texttt{null}\n\nBy default, the \\texttt{tlddoc} task generates the documentation for all\nthe TLD files that are found in the resources directories of the\n\\texttt{main} source set. The documentation files are saved in\n\\texttt{build/docs/tlddoc} and include the files copied from\n\\texttt{src/main/tlddoc}.\n\nThe \\texttt{copyTLDDocResources} task lets you add references to images\nand other resources directly in the TLD files. For example, if the\nproject includes an image called \\texttt{breadcrumb.png} in the\n\\texttt{src/main/tlddoc/images} directory, you can reference it in a TLD\nfile contained in the \\texttt{src/main/resources} directory:\n\n\\begin{verbatim}\n<description>Hello World <![CDATA[<img src=\"..](./images/breadcrumb.png\"]]></description>\n\\end{verbatim}\n\n\\section{App TLDDoc Builder Plugin}\\label{app-tlddoc-builder-plugin}\n\nIn order to use the App TLDDoc Builder plugin, it is required to apply\nthe \\texttt{com.liferay.app.tlddoc.builder} plugin in a parent project\n(that is, a project that is a common ancestor of all the subprojects\nrepresenting the various components of the app). It is also required to\napply the\n\\hyperref[tlddoc-builder-plugin]{\\texttt{com.liferay.tlddoc.builder}}\nplugin to all the subprojects that contain TLD files.\n\nThe App TLDDoc Builder plugin automatically applies the\n\\href{https://docs.gradle.org/current/userguide/standard_plugins.html\\#N135C1}{\\texttt{base}}\nplugin. It also adds three tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{appTLDDoc} \\textbar{} \\texttt{copyAppTLDDocResources}, the\n\\hyperref[validatetld]{\\texttt{validateTLD}} tasks of the subprojects\n\\textbar{} \\hyperref[tlddoctask]{\\texttt{TLDDocTask}} \\textbar{}\nGenerates tag library documentation for the app.\n\\texttt{copyAppTLDDocResources} \\textbar{} - \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html}{\\texttt{Copy}}\n\\textbar{} Copies the tag library documentation resources defined as\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskInputs.html\\#getFiles()}{inputs}\nfor the \\hyperref[copytlddocresources]{\\texttt{copyTDLDocResources}}\ntasks of the subprojects, aggregating them into the\n\\hyperref[destinationdir]{destination directory} of the\n\\texttt{appTLDDoc} task. \\texttt{jarAppTLDDoc} \\textbar{}\n\\texttt{appTLDDoc} \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html}{\\texttt{Jar}}\n\\textbar{} Assembles a JAR archive containing the tag library\ndocumentation files for this app.\n\nThe \\texttt{appTLDDoc} task is automatically configured with sensible\ndefaults:\n\nProperty Name \\textbar{} Default Value\n\\hyperref[destinationdir]{\\texttt{destinationDir}} \\textbar{}\n\\texttt{\\$\\{project.buildDir\\}/docs/tlddoc}\n\\hyperref[source]{\\texttt{source}} \\textbar{} The sum of all the\n\\texttt{tlddoc.source} values of the subprojects\n\n\\section{Project Extension}\\label{project-extension-8}\n\nThe App TLDDoc Builder plugin exposes the following properties through\nthe extension named \\texttt{appTLDDocBuilder}:\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{subprojects} \\textbar{}\n\\texttt{Set\\textless{}Project\\textgreater{}} \\textbar{}\n\\texttt{project.subprojects} \\textbar{} The subprojects to include in\nthe tag library documentation of the app.\n\nThe same extension exposes the following methods:\n\nMethod \\textbar{} Description\n\\texttt{AppTLDDocBuilderExtension\\ subprojects(Iterable\\textless{}Project\\textgreater{}\\ subprojects)}\n\\textbar{} Include additional projects in the tag library documentation\nof the app.\n\\texttt{AppTLDDocBuilderExtension\\ subprojects(Project...\\ subprojects)}\n\\textbar{} Include additional projects in the tag library documentation\nof the app.\n\n\\section{Tasks}\\label{tasks-23}\n\n\\section{TLDDocTask}\\label{tlddoctask}\n\nTasks of type \\texttt{TLDDocTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.tlddoc.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}},\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args}{\\texttt{args}}\n\\textbar{} Tag Library Documentation Generator command line arguments\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[tag-library-documentation-generator-dependency]{\\texttt{project.configurations.tlddoc}}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{} \\texttt{\"com.sun.tlddoc.TLDDoc\"}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}}\n\\textbar{} \\texttt{\"256m\"}\n\nThe \\texttt{TLDDocTask} class is also very similar to\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html}{\\texttt{SourceTask}},\nwhich means it provides a \\texttt{source} property and lets you specify\ninclude and exclude patterns.\n\n\\subsection{Task Properties}\\label{task-properties-33}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{destinationDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The directory where the tag library\ndocumentation files are saved. \\texttt{excludes} \\textbar{}\n\\texttt{Set\\textless{}String\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The TLD file patterns to exclude. \\texttt{includes}\n\\textbar{} \\texttt{Set\\textless{}String\\textgreater{}} \\textbar{}\n\\texttt{{[}{]}} \\textbar{} The TLD file patterns to include.\n\\texttt{source} \\textbar{}\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/api/file/FileTree.html}{\\texttt{FileTree}}\n\\textbar{} \\texttt{{[}{]}} \\textbar{} The TLD files to generate\ndocumentation for, after the include and exclude patterns have been\napplied. \\texttt{xsltDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The directory that contains the custom XSLT\nstylesheets used by the Tag Library Documentation Generator to produce\nthe final documentation files. It sets the \\texttt{-xslt} argument.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.tlddoc.Object)}{\\texttt{project.file}}.\n\n\\subsection{Task Methods}\\label{task-methods-11}\n\nThe methods available for \\texttt{TLDDocTask} are exactly the same as\nthe one defined in the\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html}{\\texttt{SourceTask}}\nclass.\n\n\\section{ValidateSchemaTask}\\label{validateschematask}\n\nTasks of type \\texttt{ValidateSchemaTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html}{\\texttt{SourceTask}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:include(java.lang.Iterable)}{\\texttt{include}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:exclude(java.lang.Iterable)}{\\texttt{exclude}},\nare available.\n\nTasks of this type invoke the\n\\href{http://ant.apache.org/manual/Tasks/schemavalidate.html}{\\texttt{schemavalidate}}\nAnt task in order to validate XML files described by an XML schema.\n\n\\subsection{Task Properties}\\label{task-properties-34}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{dtdDisabled} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{false} \\textbar{} Whether to disable DTD support.\n\\texttt{fullChecking} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to enable full schema checking.\n\\texttt{lenient} \\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false}\n\\textbar{} Whether to only check if the XML document is well-formed.\n\\texttt{xmlParserClassName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The class name of the XML parser to use.\n\\texttt{xmlParserClasspath} \\textbar{} \\texttt{FileCollection}\n\\textbar{} \\texttt{null} \\textbar{} The classpath with the XML parser.\n\nIt is possible to use Closures and Callables as values for the\n\\texttt{String} properties to defer evaluation until task execution.\n\n\\section{Additional Configuration}\\label{additional-configuration-16}\n\nThere are additional configurations that can help you use the TLDDoc\nBuilder.\n\n\\section{Tag Library Documentation Generator\nDependency}\\label{tag-library-documentation-generator-dependency}\n\nBy default, the plugin creates a configuration called \\texttt{tlddoc}\nand adds a dependency to the 1.3 version of the Tag Library\nDocumentation Generator. It is possible to override this setting and use\na specific version of the tool by manually adding a dependency to the\n\\texttt{tlddoc} configuration:\n\n\\begin{verbatim}\ndependencies {\n    tlddoc group: \"taglibrarydoc\", name: \"tlddoc\", version: \"1.3\"\n}\n\\end{verbatim}\n\n\\chapter{Whip Gradle Plugin}\\label{whip-gradle-plugin}\n\nThe Whip Gradle plugin lets you use the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/test/whip}{Liferay\nWhip} library to ensure that unit tests fully cover your project's code.\nSee\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-whip/src/gradleTest/smoke}{here}\nfor a usage sample.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-25}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.whip\", version: \"1.0.7\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.whip\"\n\\end{verbatim}\n\nSince the plugin automatically resolves the Liferay Whip library as a\ndependency, you have to configure a repository that hosts the library\nand its transitive dependencies. The Liferay CDN repository hosts them\nall:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\nBy default, Whip is automatically applied to all tasks of type\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/testing/Test.html}{\\texttt{Test}}.\nIf a task has Whip applied and Whip is \\hyperref[enabled]{enabled}, then\nWhip is configured as a Java Agent.\n\n\\section{Project Extension}\\label{project-extension-9}\n\nThe Whip Gradle plugin exposes the following properties through the\nextension named \\texttt{whip}:\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{version} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{latest.release} \\textbar{} The version of the Liferay Whip\nlibrary to use.\n\nThe same extension exposes the following methods:\n\nMethod \\textbar{} Description \\texttt{void\\ applyTo(Task\\ task)}\n\\textbar{} Applies Whip to a task. The task instance must implement the\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/process/JavaForkOptions.html}{\\texttt{JavaForkOptions}}\ninterface.\n\n\\section{Task Extension}\\label{task-extension}\n\nIf Whip is applied, the following task properties are available through\nthe extension named \\texttt{whip}:\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{dataFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{test-coverage/whip.dat} \\textbar{} \\texttt{enabled} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{true} \\textbar{} Whether to\nconfigure Whip as a Java Agent. \\texttt{excludes} \\textbar{}\n\\texttt{List\\textless{}String\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The class name patterns to exclude when checking for unit\ntest code coverage. For example, a value could be\n\\texttt{{[}\\textquotesingle{}.*Test\\textquotesingle{},\\ \\textquotesingle{}.*Test\\textbackslash{}\\textbackslash{}\\$.*\\textquotesingle{},\\ \\textquotesingle{}.*\\textbackslash{}\\textbackslash{}\\$Proxy.*\\textquotesingle{},\\ \\textquotesingle{}com/liferay/whip/.*\\textquotesingle{}{]}}.\n\\texttt{includes} \\textbar{}\n\\texttt{List\\textless{}String\\textgreater{}} \\textbar{} \\texttt{{[}{]}}\n\\textbar{} The class name patterns to include when checking for unit\ntest code coverage. \\texttt{instrumentDump} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{false} \\textbar{} \\texttt{whipJarFile} \\textbar{}\n\\texttt{File} \\textbar{} The first file in the \\texttt{whip}\nconfiguration whose name starts with \\texttt{com.liferay.whip-}.\n\\textbar{} The Whip JAR file.\n\nThe same extension exposes the following methods:\n\nMethod \\textbar{} Description\n\\texttt{WhipTaskExtension\\ excludes(Iterable\\textless{}Object\\textgreater{}\\ excludes)}\n\\textbar{} Adds class name patterns to exclude when checking for unit\ntest coverage. \\texttt{WhipTaskExtension\\ excludes(Object...\\ excludes)}\n\\textbar{} Adds class name patterns to exclude when checking for unit\ntest coverage.\n\\texttt{WhipTaskExtension\\ includes(Iterable\\textless{}Object\\textgreater{}\\ includes)}\n\\textbar{} Adds class name patterns to include when checking for unit\ntest coverage. \\texttt{WhipTaskExtension\\ includes(Object...\\ includes)}\n\\textbar{} Adds class name patterns to include when checking for unit\ntest coverage.\n\n\\section{Additional Configuration}\\label{additional-configuration-17}\n\nThere are additional configurations that can help you use Whip.\n\n\\section{Liferay Whip Dependency}\\label{liferay-whip-dependency}\n\nBy default, the Whip Gradle plugin creates a configuration called\n\\texttt{whip} and adds a dependency to the version of Liferay Whip\nconfigured in the \\hyperref[version]{\\texttt{whip.version}} extension\nproperty. It is possible to override this setting and use a specific\nversion of the library by manually adding a dependency to the\n\\texttt{whip} configuration:\n\n\\begin{verbatim}\ndependencies {\n    whip group: \"com.liferay\", name: \"com.liferay.whip\", version: \"1.0.1\"\n}\n\\end{verbatim}\n\nIn order to leverage the sensible default of the\n\\hyperref[whipjarfile]{\\texttt{whip.whipJarFile}} task property, the\nname of the dependency must be \\texttt{com.liferay.whip}. Otherwise, it\nwill be necessary to set the value of the \\texttt{whip.whipJarFile}\nproperty manually.\n\n\\chapter{WSDD Builder Gradle Plugin}\\label{wsdd-builder-gradle-plugin}\n\nThe WSDD Builder Gradle plugin lets you run the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-wsdd-builder}{Liferay\nWSDD Builder} tool to generate the\n\\href{http://axis.apache.org/axis/}{Apache Axis} Web Service Deployment\nDescriptor (WSDD) files from a\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} \\texttt{service.xml} file.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-26}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.wsdd.builder\", version: \"1.0.13\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.portal.tools.wsdd.builder\"\n\\end{verbatim}\n\nThe WSDD Builder plugin automatically applies the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin.\n\nSince the plugin automatically resolves the Liferay WSDD Builder library\nas a dependency, you have to configure a repository that hosts the\nlibrary and its transitive dependencies. The Liferay CDN repository\nhosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-24}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{buildWSDD} \\textbar{}\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html\\#sec:compile}{\\texttt{compileJava}}\n\\textbar{} \\hyperref[buildwsddtask]{\\texttt{BuildWSDDTask}} \\textbar{}\nRuns the Liferay WSDD Builder.\n\nBy default, the \\texttt{buildWSDD} task uses the\n\\texttt{\\$\\{project.projectDir\\}/service.xml} file as input. Then, it\ngenerates \\texttt{\\$\\{project.projectDir\\}/server-config.wsdd} and the\n\\texttt{*\\_deploy.wsdd} and \\texttt{*\\_undeploy.wsdd} files in the first\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceSet.html\\#org.gradle.api.tasks.SourceSet:resources}{\\texttt{resources}}\ndirectory of the \\texttt{main}\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html\\#N1503E}{source\nset} (by default: \\texttt{src/main/resources}).\n\nIf the\n\\href{https://docs.gradle.org/current/userguide/war_plugin.html}{\\texttt{war}}\nplugin is applied, the task uses\n\\texttt{\\$\\{project.webAppDir\\}/WEB-INF/service.xml} as input to\ngenerate \\texttt{\\$\\{project.webAppDir\\}/WEB-INF/server-config.wsdd}.\nThe \\texttt{*\\_deploy.wsdd} and \\texttt{*\\_undeploy.wsdd} files are\nstill generated in the first \\texttt{resources} directory of the\n\\texttt{main} source set.\n\nLiferay WSDD Build Service requires an additional classpath (configured\nwith the \\texttt{buildWSDD.builderClasspath} property), to correctly\ngenerate the WSDD files. The \\texttt{buildWSDD} task uses the following\ndefault value, which creates an implicit dependency to the\n\\texttt{compileJava} task:\n\n\\begin{verbatim}\ntasks.compileJava.outputs.files + sourceSets.main.compileClasspath + sourceSets.main.runtimeClasspath\n\\end{verbatim}\n\n\\section{BuildWSDDTask}\\label{buildwsddtask}\n\nTasks of type \\texttt{BuildWSDDTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args(java.lang.Iterable)}{\\texttt{args}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:maxHeapSize}{\\texttt{maxHeapSize}},\nare available. They also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:args}{\\texttt{args}}\n\\textbar{} WSDD Builder command line arguments\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:classpath}{\\texttt{classpath}}\n\\textbar{}\n\\hyperref[liferay-wsdd-builder-dependency]{\\texttt{project.configurations.wsddBuilder}}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html\\#org.gradle.api.tasks.JavaExec:main}{\\texttt{main}}\n\\textbar{} \\texttt{\"com.liferay.portal.tools.wsdd.builder.WSDDBuilder\"}\n\n\\subsection{Task Properties}\\label{task-properties-35}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{builderClasspath} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{} A classpath that the Liferay WSDD\nBuilder uses to generate WSDD files. It sets the\n\\texttt{wsdd.class.path} argument. \\texttt{inputFile} \\textbar{}\n\\texttt{File} \\textbar{} \\texttt{null} \\textbar{} A \\texttt{service.xml}\nfrom which to generate the WSDD files. It sets the\n\\texttt{wsdd.input.file} argument. \\texttt{outputDir} \\textbar{}\n\\texttt{File} \\textbar{} \\texttt{null} \\textbar{} A directory where the\n\\texttt{*\\_deploy.wsdd} and \\texttt{*\\_undeploy.wsdd} files are\ngenerated. It sets the \\texttt{wsdd.output.path} argument.\n\\texttt{serverConfigFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{\\$\\{project.projectDir\\}/server-config.wsdd} \\textbar{} A\n\\texttt{server-config.wsdd} file to generate. It sets the\n\\texttt{wsdd.server.config.file} argument. \\texttt{serviceNamespace}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{\"Plugin\"} \\textbar{} A\nnamespace for the WSDD Service. It sets the\n\\texttt{wsdd.service.namespace} argument.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.lang.Object)}{\\texttt{project.file}}.\nMoreover, it is possible to use Closures and Callables as values for the\n\\texttt{String} properties, to defer evaluation until task execution.\n\n\\section{Additional Configuration}\\label{additional-configuration-18}\n\nThere are additional configurations that can help you use the WSDD\nBuilder.\n\n\\section{Liferay WSDD Builder\nDependency}\\label{liferay-wsdd-builder-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{wsddBuilder} and adds a dependency to the latest released\nversion of the Liferay WSDD Builder. It is possible to override this\nsetting and use a specific version of the tool by manually adding a\ndependency to the \\texttt{wsddBuilder} configuration:\n\n\\begin{verbatim}\ndependencies {\n    wsddBuilder group: \"com.liferay\", name: \"com.liferay.portal.tools.wsdd.builder\", version: \"1.0.10\"\n}\n\\end{verbatim}\n\n\\chapter{WSDL Builder Gradle Plugin}\\label{wsdl-builder-gradle-plugin}\n\nThe WSDL Builder Gradle plugin lets you generate\n\\href{http://axis.apache.org/axis/}{Apache Axis} client stubs from Web\nService Description (WSDL) files.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-27}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.wsdl.builder\", version: \"2.0.3\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.wsdl.builder\"\n\\end{verbatim}\n\nThe WSDL Builder plugin automatically applies the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin.\n\nSince the plugin automatically resolves the Apache Axis library as a\ndependency, you have to configure a repository that hosts the library\nand its transitive dependencies. The Liferay CDN repository hosts them\nall:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-25}\n\nThe plugin adds one main task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{buildWSDL} \\textbar{} - \\textbar{}\n\\hyperref[buildwsdltask]{\\texttt{BuildWSDLTask}} \\textbar{} Generates\nWSDL client stubs.\n\nBy default, the \\texttt{buildWSDL} task looks for WSDL files in the\n\\texttt{\\$\\{project.projectDir\\}/wsdl} directory. If the\n\\href{https://docs.gradle.org/current/userguide/war_plugin.html}{\\texttt{war}}\nplugin is applied, it looks in the\n\\texttt{\\$\\{project.webAppDir\\}/WEB-INF/wsdl} directory.\n\nFor each WSDL file that can be found, the task generates client stubs\nvia direct invocation of the\n\\href{http://axis.apache.org/axis/java/user-guide.html\\#Client-side_bindings}{\\emph{WSDL2Java}}\ntool, saving them in the first\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceSet.html\\#org.gradle.api.tasks.SourceSet:java}{\\texttt{java}}\ndirectory of the \\texttt{main}\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html\\#N1503E}{source\nset} (by default: \\texttt{src/main/java}).\n\nIf configured to do so, \\texttt{buildWSDL} can instead save the client\nstub Java files in a temporary directory, compile them, and package them\nin JAR files. The JAR files are named after the WSDL file and saved in\n\\texttt{\\$\\{project.projectDir\\}/lib}, by default, or in\n\\texttt{\\$\\{project.webAppDir\\}/WEB-INF/lib}, if the \\texttt{war} plugin\nis applied.\n\n\\section{BuildWSDLTask}\\label{buildwsdltask}\n\nTasks of type \\texttt{FormatWSDLTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html}{\\texttt{SourceTask}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:include(java.lang.Iterable)}{\\texttt{include}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:exclude(java.lang.Iterable)}{\\texttt{exclude}},\nare available.\n\n\\subsection{Task Properties}\\label{task-properties-36}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{buildLibs} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to package the client stub classes of\neach WSDL file in JAR files, saved to the directory the\n\\texttt{destinationDir} property references. If \\texttt{false}, the task\ngenerates the client stub Java files to the \\texttt{destinationDir}\ndirectory. \\texttt{destinationDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} A directory where the client stub Java files\n(if \\texttt{buildLibs} is \\texttt{false}) or the client stub JAR files\n(if \\texttt{buildLibs} is \\texttt{true}) are saved.\n\\texttt{generateOptions.mapping} \\textbar{} \\texttt{Map} \\textbar{}\n\\texttt{{[}:{]}} \\textbar{} Namespace-to-package mappings (sets the\n\\texttt{-\\/-NStoPkg} argument in the \\emph{WSDL2Java} invocation). It is\npossible to use a \\texttt{Closure} or a \\texttt{Callable}, to defer\nevaluation until task execution.. \\texttt{generateOptions.noWrapped}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto turn off support for ``wrapped'' document/literal (sets the\n\\texttt{-\\/-noWrapped} argument in the \\emph{WSDL2Java} invocation).\n\\texttt{generateOptions.serverSide} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{false} \\textbar{} Whether to emit server-side\nbindings for the web service (sets the \\texttt{-\\/-server-side} argument\nin the \\emph{WSDL2Java} invocation). \\texttt{generateOptions.verbose}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto print informational messages (sets the \\texttt{-\\/-verbose} argument\nin the \\emph{WSDL2Java} invocation). \\texttt{includeSource} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{true} \\textbar{} Whether to package\nthe client stub Java files in the JAR file's \\texttt{OSGI-OPT/src}\ndirectory. If \\texttt{buildLibs} is \\texttt{false}, this property has no\neffect. \\texttt{includeWSDLs} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to configure the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html\\#sec:resources}{\\texttt{processResources}}\ntask to include the WSDL files in the project JAR's \\texttt{wsdl}\ndirectory.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.lang.Object)}{\\texttt{project.file}}.\n\n\\subsection{Task Methods}\\label{task-methods-12}\n\nMethod Signature \\textbar{} Description\n\\texttt{generateOptions.mapping(Object\\ namespace,\\ Object\\ packageName)}\n\\textbar{} Adds a namespace-to-package mapping.\n\\texttt{generateOptions.mappings(Map\\ mappings)} \\textbar{} Adds\nmultiple namespace-to-package mappings.\n\n\\subsection{Helper Tasks}\\label{helper-tasks-1}\n\nAt the end of the\n\\href{https://docs.gradle.org/current/userguide/build_lifecycle.html\\#N11BAE}{project\nevaluation}, a series of helper tasks are created for each WSDL file\nreturned by the\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:source}{\\texttt{source}}\nproperty of the \\texttt{BuildWSDLTask} tasks. The names of the helper\ntasks start with the WSDL file name, without any extension.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{\\$\\{WSDL\\ file\\ title\\}Generate} of type\n  \\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}}:\n  invokes\n  \\href{https://axis.apache.org/axis/java/reference.html\\#WSDL2Java_Reference}{\\emph{WSDL2Java}}\n  to generate the client stubs for the WSDL file.\n\\end{itemize}\n\nIf \\texttt{buildWSDLTask.buildLibs} is \\texttt{true}, the following\nhelper tasks are also created:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{\\$\\{WSDL\\ file\\ title\\}Compile} of type\n  \\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html}{\\texttt{JavaCompile}}:\n  compiles the client stub Java files for the WSDL file.\n\\item\n  \\texttt{\\$\\{WSDL\\ file\\ title\\}Jar} of type\n  \\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html}{\\texttt{Jar}}:\n  packages in a JAR file called \\texttt{\\$\\{WSDL\\ file\\ title\\}-ws.jar},\n  the client stub for the WSDL file.\n\\end{itemize}\n\n\\section{Additional Configuration}\\label{additional-configuration-19}\n\nThere are additional configurations that can help you use WSDL Builder.\n\n\\section{Apache Axis Dependency}\\label{apache-axis-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{wsdlBuilder} and adds the following dependencies:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{axis:axis-wsdl4j:1.5.1}\n\\item\n  \\texttt{com.liferay:org.apache.axis:1.4.LIFERAY-PATCHED-1}\n\\item\n  \\texttt{commons-discovery:commons-discovery:0.2}\n\\item\n  \\texttt{commons-logging:commons-logging:1.0.4}\n\\item\n  \\texttt{javax.activation:activation:1.1}\n\\item\n  \\texttt{javax.mail:mail:1.4}\n\\item\n  \\texttt{org.apache.axis:axis-jaxrpc:1.4}\n\\item\n  \\texttt{org.apache.axis:axis-saaj:1.4}\n\\end{itemize}\n\nIt is possible to override this setting and use a specific version of\nApache Axis, by manually populating the \\texttt{wsdlBuilder}\nconfiguration with the desired dependencies.\n\n\\chapter{XML Formatter Gradle Plugin}\\label{xml-formatter-gradle-plugin}\n\nThe XML Formatter Gradle plugin lets you format a project's XML files\nusing the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/xml-formatter}{Liferay\nXML Formatter} tool.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-28}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.xml.formatter\", version: \"1.0.11\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.xml.formatter\"\n\\end{verbatim}\n\nSince the plugin automatically resolves the Liferay XML Formatter\nlibrary as a dependency, you have to configure a repository that hosts\nthe library and its transitive dependencies. The Liferay CDN repository\nhosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-26}\n\nThe plugin adds one task to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{formatXML} \\textbar{} - \\textbar{}\n\\hyperref[formatxmltask]{\\texttt{FormatXMLTask}} \\textbar{} Runs the\nLiferay XML Formatter to format the project files.\n\nIf the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin is applied, the task formats XML files contained in the\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceSet.html\\#org.gradle.api.tasks.SourceSet:resources}{\\texttt{resources}}\ndirectories of the \\texttt{main}\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html\\#N1503E}{source\nset} (by default: \\texttt{src/main/resources/**/*.xml}).\n\n\\section{FormatXMLTask}\\label{formatxmltask}\n\nTasks of type \\texttt{FormatXMLTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html}{\\texttt{SourceTask}},\nso all its properties and methods, such as\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:include(java.lang.Iterable)}{\\texttt{include}}\nand\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html\\#org.gradle.api.tasks.SourceTask:exclude(java.lang.Iterable)}{\\texttt{exclude}},\nare available.\n\n\\subsection{Task Properties}\\label{task-properties-37}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{classpath} \\textbar{}\n\\href{https://docs.gradle.org/current/javadoc/org/gradle/api/file/FileCollection.html}{\\texttt{FileCollection}}\n\\textbar{}\n\\hyperref[liferay-xml-formatter-dependency]{\\texttt{project.configurations.xmlFormatter}}\n\\textbar{} The classpath for executing the main class.\n\\texttt{mainClassName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"com.liferay.xml.formatter.XMLFormatter\"} \\textbar{} The fully\nqualified name of the XML Formatter Main class. \\texttt{stripComments}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto remove all the comments from the XML files. It sets the\n\\texttt{xml.formatter.strip.comments} argument.\n\n\\section{Additional Configuration}\\label{additional-configuration-20}\n\nThere are additional configurations that can help you use the XML\nFormatter.\n\n\\section{Liferay XML Formatter\nDependency}\\label{liferay-xml-formatter-dependency}\n\nBy default, the plugin creates a configuration called\n\\texttt{xmlFormatter} and adds a dependency to the latest released\nversion of the Liferay XML Formatter. It is possible to override this\nsetting and use a specific version of the tool by manually adding a\ndependency to the \\texttt{xmlFormatter} configuration:\n\n\\begin{verbatim}\ndependencies {\n    xmlFormatter group: \"com.liferay\", name: \"com.liferay.xml.formatter\", version: \"1.0.5\"\n}\n\\end{verbatim}\n\n\\chapter{XSD Builder Gradle Plugin}\\label{xsd-builder-gradle-plugin}\n\nThe XSD Builder Gradle plugin lets you generate\n\\href{https://xmlbeans.apache.org/}{Apache XMLBeans} bindings from XML\nSchema (XSD) files.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n\\section{Usage}\\label{usage-29}\n\nTo use the plugin, include it in your build script:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.xsd.builder\", version: \"1.0.7\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.xsd.builder\"\n\\end{verbatim}\n\nThe XSD Builder plugin automatically applies the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html}{\\texttt{java}}\nplugin.\n\nSince the plugin automatically resolves the Liferay Service Builder\nlibrary as a dependency, you have to configure a repository that hosts\nthe library and its transitive dependencies. The Liferay CDN repository\nhosts them all:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\n\\section{Tasks}\\label{tasks-27}\n\nThe plugin adds three tasks to your project:\n\nName \\textbar{} Depends On \\textbar{} Type \\textbar{} Description\n\\texttt{buildXSD} \\textbar{} \\texttt{buildXSDCompile} \\textbar{}\n\\hyperref[buildxsdtask]{\\texttt{BuildXSDTask}} \\textbar{} Generates\nXMLBeans bindings and compiles them in a JAR file.\n\\texttt{buildXSDGenerate} \\textbar{} \\texttt{cleanBuildXSDGenerate}\n\\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html}{\\texttt{JavaExec}}\n\\textbar{} Invokes the\n\\href{https://xmlbeans.apache.org/docs/2.6.0/guide/tools.html\\#scomp}{XMLBeans\nSchema Compiler} to generate Java types from XML Schema.\n\\texttt{buildXSDCompile} \\textbar{} \\texttt{buildXSDGenerate},\n\\texttt{cleanBuildXSDCompile} \\textbar{}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html}{\\texttt{JavaCompile}}\n\\textbar{} Compiles the generated Java types.\n\nBy default, the \\texttt{buildXSD} task looks for XSD files in the\n\\texttt{\\$\\{project.projectDir\\}/xsd} directory, and saves the generated\nJAR file as\n\\texttt{\\$\\{project.projectDir\\}/lib/\\$\\{project.archivesBaseName\\}-xbean.jar}.\n\nIf the\n\\href{https://docs.gradle.org/current/userguide/war_plugin.html}{\\texttt{war}}\nplugin is applied, the task looks for XSD files in the\n\\texttt{\\$\\{project.webAppDir\\}/WEB-INF/xsd} directory, and saves the\ngenerated JAR file as\n\\texttt{\\$\\{project.webAppDir\\}/WEB-INF/lib/\\$\\{project.archivesBaseName\\}-xbean.jar}.\n\n\\section{BuildXSDTask}\\label{buildxsdtask}\n\nTasks of type \\texttt{BuildXSDTask} extend\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html}{\\texttt{Zip}}.\nThey also have the following properties set by default:\n\nProperty Name \\textbar{} Default Value\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html\\#org.gradle.api.tasks.bundling.Zip:appendix}{\\texttt{appendix}}\n\\textbar{} \\texttt{\"xbean\"}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html\\#org.gradle.api.tasks.bundling.Zip:extension}{\\texttt{extension}}\n\\textbar{} \\texttt{\"jar\"}\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html\\#org.gradle.api.tasks.bundling.Zip:version}{\\texttt{version}}\n\\textbar{} \\texttt{null}\n\nFor each task of type \\texttt{BuildXSDTask}, the following helper tasks\nare created:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{\\$\\{buildXSDTask.name\\}Compile}\n\\item\n  \\texttt{\\$\\{buildXSDTask.name\\}Generate}\n\\end{itemize}\n\n\\subsection{Task Properties}\\label{task-properties-38}\n\nProperty Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{inputDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} A directory containing XSD files from which to\ngenerate \\href{https://xmlbeans.apache.org/}{Apache XMLBeans} bindings.\n\nThe properties of type \\texttt{File} support any type that can be\nresolved by\n\\href{https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\\#org.gradle.api.Project:file(java.lang.Object)}{\\texttt{project.file}}.\n\n\\section{Additional Configuration}\\label{additional-configuration-21}\n\nThere are additional configurations that can help you use the XSD\nBuilder.\n\n\\section{Apache XMLBeans Dependency}\\label{apache-xmlbeans-dependency}\n\nBy default, the XSD Builder Gradle plugin creates a configuration called\n\\texttt{xsdBuilder} and adds a dependency to the 2.5.0 version of Apache\nXMLBeans. It is possible to override this setting and use a specific\nversion of the library by manually adding a dependency to the\n\\texttt{xsdBuilder} configuration:\n\n\\begin{verbatim}\ndependencies {\n    xsdBuilder group: \"org.apache.xmlbeans\", name: \"xmlbeans\", version: \"2.6.0\"\n}\n\\end{verbatim}\n\n\\chapter{Liferay Faces}\\label{liferay-faces}\n\nLiferay Faces is an umbrella project that provides support for the\nJavaServer™ Faces (JSF) standard within Liferay DXP. It encompasses the\nfollowing projects:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-bridge}{Liferay\n  Faces Bridge} enables you to deploy JSF web apps as portlets without\n  writing portlet-specific Java code. It also contains innovative\n  features that make it possible to leverage the power of JSF 2.x inside\n  a portlet application. Liferay Faces Bridge implements the JSR 329\n  Portlet Bridge Standard.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-alloy}{Liferay\n  Faces Alloy} enables you to use AlloyUI components in a way that is\n  consistent with JSF development.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-portal}{Liferay\n  Faces Portal} enables you to leverage Liferay-specific utilities and\n  UI components in JSF portlets.\n\\end{itemize}\n\nIn this section of reference documentation, you'll learn more about each\nof these projects. You'll also learn about the Liferay Faces version\nscheme.\n\n\\chapter{Liferay Faces Version\nScheme}\\label{liferay-faces-version-scheme}\n\nIn this article, you'll learn which Liferay Faces artifacts should be\nused with your portlet and explore the Liferay Faces versioning scheme\nby discovering what each component of a version means. Once you have the\nversioning scheme mastered, you can view several example configurations.\n\n\\section{Using The Liferay Faces Archetype\nPortlet}\\label{using-the-liferay-faces-archetype-portlet}\n\nThe \\href{http://liferayfaces.org}{Liferay Faces Archetype portlet} can\nbe used to determine the Liferay Faces artifacts and versions that you\nmust include in your portlet. Select your preferred Liferay Portal\nversion, JSF version, component suite (optional), and build tool, and\nthe portlet will provide you with both a command to generate your\nportlet from a Maven archetype and a list of dependencies that can be\ncopied into your build files. In the next section, you'll be provided\nwith compatibility information about each version of the Liferay Faces\nartifacts.\n\n\\section{Liferay Faces Alloy}\\label{liferay-faces-alloy}\n\nProvides a suite of JSF components that utilize\n\\href{http://alloyui.com/}{AlloyUI}.\n\n\\noindent\\hrulefill\n\nBranch\\textbar Example Artifact\\textbar AlloyUI\\textbar JSF\nAPI\\textbar Additional Info\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-alloy/tree/master}{master\n(4.x)}\\textbar com.liferay.faces.alloy-4.1.0.jar\\textbar3.1.x\\textbar2.2+\\textbar{}\\emph{AlloyUI\n3.1.x is the version that comes bundled with Liferay Portal\n7.3.}\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-alloy/tree/3.x}{3.x}\\textbar com.liferay.faces.alloy-3.1.0.jar\\textbar3.0.x\\textbar2.2+\\textbar{}\\emph{AlloyUI\n3.0.x is the version that comes bundled with Liferay Portal\n7.0/7.1/7.2.}\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-alloy/tree/2.x}{2.x}\\textbar com.liferay.faces.alloy-2.0.1.jar\\textbar2.0.x\\textbar2.1+\\textbar{}\\emph{AlloyUI\n2.0.x is the version that comes bundled with Liferay Portal\n6.2.}\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-alloy/tree/1.x}{1.x}\\textbar com.liferay.faces.alloy-1.0.1.jar\\textbar2.0.x\\textbar1.2\\textbar{}\\emph{AlloyUI\n2.0.x is the version that comes bundled with Liferay Portal\n6.2.}\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{Liferay Faces Bridge}\\label{liferay-faces-bridge}\n\nProvides the ability to deploy JSF web applications as portlets within\n\\href{https://portals.apache.org/pluto/}{Apache Pluto}, the reference\nimplementation for JSR 286 (Portlet 2.0) and JSR 362 (Portlet 3.0).\n\n\\noindent\\hrulefill\n\nBranch\\textbar Example Artifacts\\textbar Portlet API\\textbar JSF\nAPI\\textbar JCP Specification\\textbar Additional Info\\textbar{} API:\n\\href{https://github.com/liferay/liferay-faces-bridge-api/tree/5.x}{5.x}IMPL:\n\\href{https://github.com/liferay/liferay-faces-bridge-impl/tree/5.x}{5.x}\\textbar com.liferay.faces.bridge.api-5.0.0.jarcom.liferay.faces.bridge.impl-5.0.0.jar\\textbar3.0\\textbar2.2\\textbar{}\\href{https://www.jcp.org/en/jsr/detail?id=378}{JSR\n378}\\textbar{}\\emph{Under ``Final Review'' by the JCP and scheduled for\nrelease in 2020.}\\textbar{} API:\n\\href{https://github.com/liferay/liferay-faces-bridge-api/tree/4.x}{4.x}IMPL:\n\\href{https://github.com/liferay/liferay-faces-bridge-impl/tree/4.x}{4.x}\\textbar com.liferay.faces.bridge.api-4.1.0.jarcom.liferay.faces.bridge.impl-4.0.0.jar\\textbar2.0\\textbar2.2\\textbar{}\\href{https://www.jcp.org/en/jsr/detail?id=329}{JSR\n329}\\textbar{}\\emph{Includes non-standard bridge extensions for JSF\n2.2.}\\textbar{} API:\n\\href{https://github.com/liferay/liferay-faces-bridge-api/tree/3.x}{3.x}IMPL:\n\\href{https://github.com/liferay/liferay-faces-bridge-impl/tree/3.x}{3.x}\\textbar com.liferay.faces.bridge.api-3.1.0.jarcom.liferay.faces.bridge.impl-3.0.0.jar\\textbar2.0\\textbar2.1\\textbar{}\\href{https://www.jcp.org/en/jsr/detail?id=329}{JSR\n329}\\textbar{}\\emph{Includes non-standard bridge extensions for JSF\n2.1.}\\textbar{} API:\n\\href{https://github.com/liferay/liferay-faces-bridge-api/tree/2.x}{2.x}IMPL:\n\\href{https://github.com/liferay/liferay-faces-bridge-impl/tree/2.x}{2.x}\\textbar com.liferay.faces.bridge.api-2.1.0.jarcom.liferay.faces.bridge.impl-2.0.0.jar\\textbar2.0\\textbar1.2\\textbar{}\\href{https://www.jcp.org/en/jsr/detail?id=329}{JSR\n329} (MR1)\\textbar{}\\emph{Includes support for Maintenance Release 1\n(MR1).}\\textbar{}\n1.x\\textbar N/A\\textbar1.0\\textbar1.2\\textbar{}\\href{https://www.jcp.org/en/jsr/detail?id=301}{JSR\n301}\\textbar{}\\emph{N/A (Not Applicable) since Liferay Faces Bridge has\nnever implemented JSR 301.}\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{Liferay Faces Bridge Ext}\\label{liferay-faces-bridge-ext}\n\nExtension to Liferay Faces Bridge that provides compatibility with\n\\href{https://liferay.dev/-/portal}{Liferay Portal} and also takes\nadvantage of Liferay-specific features such as friendly URLs.\n\n\\noindent\\hrulefill\n\nBranch \\textbar Example Artifact \\textbar~~Liferay Portal\nAPI~~\\textbar~~Bridge API~~\\textbar~~Portlet API~~\\textbar JSF\nAPI\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-bridge-ext/tree/master}{8.x}\\textbar com.liferay.faces.bridge.ext-8.0.0.jar\\textbar7.3.0+\\textbar5.x\\textbar3.0\\textbar2.3\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-bridge-ext/tree/7.x}{7.x}\\textbar com.liferay.faces.bridge.ext-7.0.0.jar\\textbar7.3.0+\\textbar5.x\\textbar3.0\\textbar2.2\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-bridge-ext/tree/6.x}{6.x}\\textbar com.liferay.faces.bridge.ext-6.0.0.jar\\textbar7.3.0+\\textbar4.x\\textbar2.0\\textbar2.2\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-bridge-ext/tree/5.x}{5.x}\\textbar com.liferay.faces.bridge.ext-5.0.4.jar\\textbar7.0.x/7.1.x/7.2.x\\textbar4.x\\textbar2.0\\textbar2.2\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-bridge-ext/tree/4.x}{4.x}\\textbar UNUSED\\textbar N/A\\textbar N/A\\textbar N/A\\textbar N/A\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-bridge-ext/tree/3.x}{3.x}\\textbar com.liferay.faces.bridge.ext-3.0.1.jar\\textbar6.2.x\\textbar4.x\\textbar2.0\\textbar2.2\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-bridge-ext/tree/2.x}{2.x}\\textbar com.liferay.faces.bridge.ext-2.0.1.jar\\textbar6.2.x\\textbar3.x\\textbar2.0\\textbar2.1\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-bridge-ext/tree/1.x}{1.x}\\textbar com.liferay.faces.bridge.ext-1.0.1.jar\\textbar6.2.x\\textbar2.x\\textbar2.0\\textbar1.2\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{Liferay Faces Portal}\\label{liferay-faces-portal}\n\nProvides a suite of JSF components that are based on the JSP tags\nprovided by \\href{https://liferay.dev/-/portal}{Liferay Portal}.\n\n\\noindent\\hrulefill\n\nBranch\\textbar Example Artifact\\textbar Liferay Portal\nAPI~~\\textbar~~Portlet API\\textbar~~JSF API\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-portal/tree/master}{6.x}\\textbar com.liferay.faces.portal-6.0.0.jar\\textbar7.2+\\textbar3.0\\textbar2.3\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-portal/tree/5.x}{5.x}\\textbar com.liferay.faces.portal-5.0.0.jar\\textbar7.2+\\textbar3.0\\textbar2.2\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-portal/tree/4.x}{4.x}\\textbar com.liferay.faces.portal-4.0.0.jar\\textbar7.2/7.3\\textbar2.0\\textbar2.2\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-portal/tree/3.x}{3.x}\\textbar com.liferay.faces.portal-3.0.1.jar\\textbar7.0/7.1/7.2\\textbar2.0\\textbar2.2\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-portal/tree/2.x}{2.x}\\textbar com.liferay.faces.portal-2.0.1.jar\\textbar6.2\\textbar2.0\\textbar2.1/2.2\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-portal/tree/1.x}{1.x}\\textbar com.liferay.faces.portal-1.0.1.jar\\textbar6.2\\textbar2.0\\textbar1.2\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{Liferay Faces Util}\\label{liferay-faces-util}\n\nLibrary that contains general purpose JSF utilities to support many of\nthe sub-projects that comprise Liferay Faces.\n\n\\noindent\\hrulefill\n\nBranch\\textbar Example Artifact\\textbar~~JSF API\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-util/tree/4.x}{4.x}\\textbar com.liferay.faces.util-3.1.0.jar\\textbar2.3\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-util/tree/3.x}{3.x}\\textbar com.liferay.faces.util-3.1.0.jar\\textbar2.2\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-util/tree/2.x}{2.x}\\textbar com.liferay.faces.util-2.1.0.jar\\textbar2.1\\textbar{}\n\\href{https://github.com/liferay/liferay-faces-util/tree/1.x}{1.x}\\textbar com.liferay.faces.util-1.1.0.jar\\textbar1.2\\textbar{}\n\n\\noindent\\hrulefill\n\nNow that you know all about the Liferay Faces versioning scheme, you may\nbe curious as to how these components interact with each other. Refer to\nthe following figure to view the Liferay Faces dependency diagram.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-faces-dependency-diagram.png}\n\\caption{The Liferay Faces dependency diagram helps visualize how\ncomponents interact and depend on each other.}\n\\end{figure}\n\nNext, you can view some example configurations to see the new versioning\nscheme in action.\n\n\\chapter{Understanding Liferay Faces\nBridge}\\label{understanding-liferay-faces-bridge}\n\nThe Liferay Faces Bridge enables you to deploy JSF web apps as portlets\nwithout writing portlet-specific code. It also contains innovative\nfeatures that make it possible to leverage the power of JSF 2.x inside a\nportlet application.\n\nLiferay Faces Bridge is distributed in a \\texttt{.jar} file. You can add\nLiferay Faces Bridge as a dependency to your portlet projects, in order\nto deploy your JSF web applications as portlets within JSR 286 (Portlet\n2.0) compliant portlet containers, like Liferay Portal 5.2, 6.0, 6.1,\n6.2, and 7.0.\n\nThe Liferay Faces Bridge project home page can be found\n\\href{https://community.liferay.com/-/faces}{here}.\n\nTo fully understand Liferay Faces Bridge, you must first understand the\nportlet bridge standard. Because the Portlet 1.0 and JSF 1.0 specs were\nbeing created at essentially the same time, the Expert Group (EG) for\nthe JSF specification constructed the JSF framework to be compliant with\nportlets. For example, the\n\\href{https://javaee.github.io/javaee-spec/javadocs/javax/faces/context/ExternalContext.html\\#getRequest--}{ExternalContext.getRequest()}\nmethod returns an \\texttt{Object} instead of an\n\\href{https://javaee.github.io/javaee-spec/javadocs/javax/servlet/http/HttpServletRequest.html}{javax.servlet.http.HttpServletRequest}.\nWhen this method is used in a portal, the \\texttt{Object} can be cast to\na\n\\href{http://portals.apache.org/pluto/portlet-2.0-apidocs/javax/portlet/PortletRequest.html}{javax.portlet.PortletRequest}.\nDespite the EG's consciousness of portlet compatibility within the\ndesign of JSF, the gap between the portlet and JSF lifecycles had to be\nbridged.\n\nPortlet bridge standards and implementations evolved over time.\n\nStarting in 2004, several different JSF portlet bridge implementations\nwere developed in order to provide JSF developers with the ability to\ndeploy their JSF web apps as portlets. In 2006, the JCP formed the\nPortlet Bridge 1.0 (\\href{http://www.jcp.org/en/jsr/detail?id=301}{JSR\n301}) EG in order to define a standard bridge API, as well as detailed\nrequirements for bridge implementations. JSR 301 was released in 2010,\ntargeting Portlet 1.0 and JSF 1.2.\n\nWhen the Portlet 2.0 (\\href{http://www.jcp.org/en/jsr/detail?id=286}{JSR\n286}) standard was released in 2008, it became necessary for the JCP to\nform the Portlet Bridge 2.0\n(\\href{http://www.jcp.org/en/jsr/detail?id=329}{JSR 329}) EG. JSR 329\nwas also released in 2010, targeting Portlet 2.0 and JSF 1.2.\n\nAfter the \\href{http://www.jcp.org/en/jsr/detail?id=314}{JSR 314} EG\nreleased JSF 2.0 in 2009 and JSF 2.1 in 2010, it became evident that a\nPortlet Bridge 3.0 standard would be beneficial. In 2015 the JCP formed\n\\href{http://www.jcp.org/en/jsr/detail?id=378}{JSR 378}) which is\ndefining a bridge for Portlet 3.0 and JSF 2.2. There are also variants\nof \\emph{Liferay Faces Bridge} that support Portlet 2.0 and JSF\n1.2/2.1/2.2.\n\nLiferay Faces Bridge is the Reference Implementation (RI) of the Portlet\nBridge Standard. It also contains innovative features that make it\npossible to leverage the power of JSF 2.x inside a portlet application.\n\nNow that you're familiar with some of the history of the Portlet Bridge\nstandards, you'll learn about the responsibilities required of the\nportlet bridge.\n\nA JSF portlet bridge aligns the correct phases of the JSF lifecycle with\neach phase of the portlet lifecycle. For instance, if a browser sends an\nHTTP GET request to a portal page with a JSF portlet in it, the\n\\texttt{RENDER\\_PHASE} is performed in the portlet's lifecycle. The JSF\nportlet bridge then initiates the \\texttt{RESTORE\\_VIEW} and\n\\texttt{RENDER\\_RESPONSE} phases in the JSF lifecycle. Likewise, when an\nHTTP POST is executed on a portlet and the portlet enters the\n\\texttt{ACTION\\_PHASE}, then the full JSF lifecycle is initiated by the\nbridge.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/lifecycle-bridge.png}\n\\caption{The different phases of the JSF Lifecycle are executed\ndepending on which phase of the Portlet lifecycle is being executed.}\n\\end{figure}\n\nBesides ensuring that the two lifecycles connect correctly, the JSF\nportlet bridge also acts as a mediator between the portal URL generator\nand JSF navigation rules. JSF portlet bridges ensure that URLs created\nby the portal comply with JSF navigation rules, so that a JSF portlet is\nable to switch to different views.\n\nThe JSR 329/378 standards defines several configuration options prefixed\nwith the \\texttt{javax.portlet.faces} namespace. Liferay Faces Bridge\ndefines additional implementation-specific options prefixed with the\n\\texttt{com.liferay.faces.bridge} namespace.\n\nLiferay Faces Bridge is an essential part of the JSF development process\nfor Liferay DXP. Visit the\n\\href{/docs/7-1/tutorials/-/knowledge_base/t/jsf-portlets-with-liferay-faces}{JSF\nPortlets with Liferay Faces} section of tutorials for more information\non JSF development for Liferay DXP.\n\n\\section{Related Topics}\\label{related-topics-45}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-alloy}{Understanding\nLiferay Faces Alloy}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-portal}{Understanding\nLiferay Faces Portal}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}\n\n\\chapter{Understanding Liferay Faces\nAlloy}\\label{understanding-liferay-faces-alloy}\n\nLiferay Faces Alloy is distributed in a \\texttt{.jar} file. You can add\nLiferay Faces Alloy as a dependency to your portlet projects, to use\nAlloyUI in a way that is consistent with JSF development.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} AlloyUI is deprecated in Liferay DXP 7.2.\n\n\\noindent\\hrulefill\n\nDuring the creation of a JSF portlet in Liferay IDE/Developer Studio,\nyou have the option of choosing the portlet's JSF Component Suite. The\noptions include \\emph{JSF standard},\n\\href{http://www.icesoft.org/java/projects/ICEfaces/overview.jsf}{\\emph{ICEfaces}},\n\\href{http://primefaces.org/}{\\emph{PrimeFaces}},\n\\href{http://richfaces.jboss.org/}{\\emph{RichFaces}}, and \\emph{Liferay\nFaces Alloy}.\n\nIf you selected the Liferay Faces Alloy JSF Component Suite during your\nportlet's setup, the \\texttt{.jar} file is included in your portlet\nproject.\n\nThe Liferay Faces Alloy project provides a set of UI components that\nutilize AlloyUI. For example, a brief list of some of the supported\n\\texttt{aui:} tags are listed below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Input: \\texttt{alloy:inputText}, \\texttt{alloy:inputDate},\n  \\texttt{alloy:inputFile}\n\\item\n  Panel: \\texttt{alloy:accordion}, \\texttt{alloy:column},\n  \\texttt{alloy:fieldset}, \\texttt{alloy:row}\n\\item\n  Select: \\texttt{alloy:selectOneMenu}, \\texttt{alloy:selectOneRadio},\n  \\texttt{alloy:selectStarRating}\n\\end{itemize}\n\nIf you want to utilize Liferay's AlloyUI technology based on YUI3, you\nmust include the Liferay Faces Alloy \\texttt{.jar} file in your JSF\nportlet project. If you selected \\emph{Liferay Faces Alloy} during your\nJSF portlet's setup, you have Liferay Faces Alloy preconfigured in your\nproject, so you're automatically able to use the \\texttt{alloy:} tags.\n\nAs you can see, it's extremely easy to configure your JSF application to\nuse Liferay's AlloyUI tags.\n\n\\section{Related Topics}\\label{related-topics-46}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/developing-a-jsf-portlet-application}{Developing\na JSF Portlet Application}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-bridge}{Understanding\nLiferay Faces Bridge}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-portal}{Understanding\nLiferay Faces Portal}\n\n\\chapter{Understanding Liferay Faces\nPortal}\\label{understanding-liferay-faces-portal}\n\n\\emph{Liferay Faces Portal} is distributed in a \\texttt{.jar} file. You\ncan add Liferay Faces Portal as a dependency for your portlet projects\nto use its Liferay-specific utilities and UI components. When Liferay\nFaces Portal is included in a JSF portlet project, the\n\\texttt{com.liferay.faces.portal.{[}version{]}.jar} file resides in the\nportlet's library.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/jsf-jars-package-explorer.png}\n\\caption{The required \\texttt{.jar} files are downloaded for your JSF\nportlet based on the JSF UI Component Suite you configured.}\n\\end{figure}\n\nSome of the features included in Liferay Faces Portal are:\n\n\\begin{itemize}\n\\item\n  Utilities: Provides the \\texttt{LiferayPortletHelperUtil} which\n  contains a variety Portlet-API and Liferay-specific convenience\n  methods.\n\\item\n  JSF Components: Provides a set of JSF equivalents for popular Liferay\n  DXP JSP tags (not exhaustive):\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{liferay-ui:captcha} → \\texttt{portal:captcha}\n  \\item\n    \\texttt{liferay-ui:input-editor} → \\texttt{portal:inputRichText}\n  \\item\n    \\texttt{liferay-ui:search} → \\texttt{portal:inputSearch}\n  \\item\n    \\texttt{liferay-ui:header} → \\texttt{portal:header}\n  \\item\n    \\texttt{aui:nav} → \\texttt{portal:nav}\n  \\item\n    \\texttt{aui:nav-item} → \\texttt{portal:navItem}\n  \\item\n    \\texttt{aui:nav-bar} → \\texttt{portal:navBar}\n  \\item\n    \\texttt{liferay-security:permissionsURL} →\n    \\texttt{portal:permissionsURL}\n  \\item\n    \\texttt{liferay-portlet:runtime} → \\texttt{portal:runtime}\n  \\end{itemize}\n\n  For more information, visit\n  \\url{https://liferayfaces.org/web/guest/portal-showcase}.\n\\item\n  Expression Language: Adds a set of EL keywords such as\n  \\texttt{liferay} for getting Liferay-specific info, and \\texttt{i18n}\n  for integration with out-of-the-box Liferay internationalized\n  messages.\n\\end{itemize}\n\nGreat! You now have an understanding of what Liferay Faces Portal is,\nand what it accomplishes in your JSF application.\n\n\\section{Related Topics}\\label{related-topics-47}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/developing-a-jsf-portlet-application}{Developing\na JSF Portlet Application}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-bridge}{Understanding\nLiferay Faces Bridge}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-alloy}{Understanding\nLiferay Faces Alloy}\n\n\\chapter{Maven Plugins}\\label{maven-plugins}\n\nLiferay provides plugins that you can apply to your Maven project. This\nreference documentation describes\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Configuring the plugin in your \\texttt{pom.xml} file.\n\\item\n  The plugin's available goals you can leverage.\n\\item\n  The plugin's configuration properties.\n\\end{itemize}\n\nIf you're looking for additional instructions on using Maven with your\nmodules, see the\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven}{Maven} articles.\n\n\\chapter{Bundle Support Plugin}\\label{bundle-support-plugin}\n\nThe Bundle Support plugin lets you use\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace} as a Maven project.\n\n\\section{Usage}\\label{usage-30}\n\nTo use the plugin, include it in your project's root \\texttt{pom.xml}\nfile:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.bundle.support</artifactId>\n            <version>3.2.5</version>\n            <executions>\n                <execution>\n                    <id>clean</id>\n                    <goals>\n                        <goal>clean</goal>\n                    </goals>\n                    <phase>clean</phase>\n                    <configuration>\n                    </configuration>\n                </execution>\n                <execution>\n                    <id>deploy</id>\n                    <goals>\n                        <goal>deploy</goal>\n                    </goals>\n                    <phase>pre-integration-test</phase>\n                    <configuration>\n                    </configuration>\n                </execution>\n            </executions>\n        </plugin>\n        ...\n    </plugins>\n</build>\n\\end{verbatim}\n\n\\section{Goals}\\label{goals}\n\nThe plugin adds five Maven goals to your project:\n\nName \\textbar{} Description\n\\hyperref[clean-goals-available-parameters]{bundle-support:clean}\n\\textbar{} Deletes a file from the \\texttt{deploy} directory of a\nLiferay bundle.\n\\hyperref[create-token-goals-available-parameters]{bundle-support:create-token}\n\\textbar{} Creates a token used to validate your user credentials when\ndownloading a DXP bundle.\n\\hyperref[deploy-goals-available-parameters]{bundle-support:deploy}\n\\textbar{} Deploys the Maven project to the specified Liferay DXP\nbundle. \\hyperref[dist-goals-available-parameters]{bundle-support:dist}\n\\textbar{} Creates a distributable Liferay DXP bundle archive file\n(e.g., ZIP).\n\\hyperref[init-goals-available-parameters]{bundle-support:init}\n\\textbar{} Downloads and installs the specified Liferay DXP version.\n\n\\section{clean Goal's Available\nParameters}\\label{clean-goals-available-parameters}\n\nYou can set the following parameters in the \\texttt{clean} execution's\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{liferayHome} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{bundles} \\textbar{} The directory where your Liferay DXP\ninstance resides. This can be specified from the command line as\n\\texttt{-DliferayHome=}. \\texttt{fileName} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{\\$\\{project.artifactId\\}.\\$\\{project.packaging\\}}\n\\textbar{} The name of the file to delete from your bundle.\n\n\\section{create-token Goal's Available\nParameters}\\label{create-token-goals-available-parameters}\n\nYou can change the default parameter values of the \\texttt{create-token}\ngoal by creating an \\texttt{\\textless{}execution\\textgreater{}} section\ncontaining \\texttt{\\textless{}configuration\\textgreater{}} tags. For\nexample,\n\n\\begin{verbatim}\n<execution>\n    <id>create-token</id>\n    <goals>\n        <goal>create-token</goal>\n    </goals>\n    <configuration>\n    </configuration>\n</execution>\n\\end{verbatim}\n\nYou can set the following parameters in the \\texttt{create-token}\nexecution's \\texttt{\\textless{}configuration\\textgreater{}} section of\nthe POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{emailAddress} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The email address to use when downloading a DXP\nbundle. This email address must match the one registered for your DXP\nsubscription. \\texttt{force} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{false} \\textbar{} Whether to override the existing token with a\nnewly generated one. \\texttt{password} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{} The password to use when downloading\na DXP bundle. This password must match the one registered for your DXP\nsubscription. \\texttt{passwordFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The file to hold your password used when\ndownloading a DXP bundle. \\texttt{tokenFile} \\textbar{} \\texttt{File}\n\\textbar{} \\texttt{\\$\\{user.home\\}/.liferay/token} \\textbar{} The file\nto hold the Liferay bundle authentication token. \\texttt{tokenUrl}\n\\textbar{} \\texttt{URL} \\textbar{}\n\\texttt{https://releases-cdn.liferay.com/portal/7.1.0-b3/liferay-ce-portal-tomcat-7.1-b3-20180611140920623.zip}\n\\textbar{} The URL pointing to the bundle Zip to download.\n\nAfter executing the \\texttt{create-token} goal, you're prompted for your\nemail address and password, both of which are used to generate your\ntoken. It's recommended to configure your email and password from the\ncommand line rather than specifying them in your POM file.\n\n\\section{deploy Goal's Available\nParameters}\\label{deploy-goals-available-parameters}\n\nYou can set the following parameters in the \\texttt{deploy} execution's\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{liferayHome} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{bundles} \\textbar{} The directory where your Liferay DXP\ninstance resides. This can be specified from the command line as\n\\texttt{-DliferayHome=}. \\texttt{deployFile} \\textbar{} \\texttt{File}\n\\textbar{}\n\\texttt{\\$\\{project.build.directory\\}/\\$\\{project.build.finalName\\}.\\$\\{project.packaging\\}}\n\\textbar{} The packaged file (e.g., JAR) to deploy to the Liferay\nbundle. \\texttt{outputFileName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\\$\\{project.artifactId\\}.\\$\\{project.packaging\\}} \\textbar{} The\nname of the output file.\n\n\\section{dist Goal's Available\nParameters}\\label{dist-goals-available-parameters}\n\nYou can change the default parameter values of the \\texttt{dist} goal by\ncreating an \\texttt{\\textless{}execution\\textgreater{}} section\ncontaining \\texttt{\\textless{}configuration\\textgreater{}} tags. For\nexample,\n\n\\begin{verbatim}\n<execution>\n    <id>dist</id>\n    <goals>\n        <goal>dist</goal>\n    </goals>\n    <configuration>\n    </configuration>\n</execution>\n\\end{verbatim}\n\nYou can set the following parameters in the \\texttt{dist} execution's\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{liferayHome} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{bundles} \\textbar{} The directory where your Liferay DXP\ninstance resides. This can be specified from the command line as\n\\texttt{-DliferayHome=}. \\texttt{archiveFileName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The name for the\ngenerated archive file. \\texttt{cacheDir} \\textbar{} \\texttt{File}\n\\textbar{} \\texttt{\\$\\{user.home\\}/.liferay/bundles} \\textbar{} The\ndirectory where the downloaded bundle Zip files are stored.\n\\texttt{configs} \\textbar{} \\texttt{String} \\textbar{} \\texttt{configs}\n\\textbar{} The directory that contains the configuration files.\n\\texttt{deployFile} \\textbar{} \\texttt{File}\n\\textbar{}\\texttt{\\$\\{project.build.directory\\}/\\$\\{project.build.finalName\\}.\\$\\{project.packaging\\}}\n\\textbar{} The packaged file (e.g., JAR) to deploy to the Liferay\nbundle. \\texttt{environment} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\\$\\{liferay.workspace.environment\\}} \\textbar{} The environment\nof your Liferay home deployment. (e.g., \\texttt{common}, \\texttt{dev},\n\\texttt{local}, \\texttt{prod}, and \\texttt{uat}). \\texttt{format}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{zip} \\textbar{} The format\ntype to use when packaging the Liferay bundle as an archive.\n\\texttt{includeFolder} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to add a parent folder to the archive.\n\\texttt{outputFileName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\\$\\{project.artifactId\\}.\\$\\{project.packaging\\}} \\textbar{} The\npath to the archive file. \\texttt{password} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{} The password if your Liferay\nbundle's URL requires authentication. \\texttt{stripComponents}\n\\textbar{} \\texttt{int} \\textbar{} \\texttt{1} \\textbar{} The number of\ndirectories to strip when expanding your bundle. \\texttt{token}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto use a token to download a Liferay DXP bundle. This should be set to\n\\texttt{true} when downloading a DXP bundle. \\texttt{tokenFile}\n\\textbar{} \\texttt{File} \\textbar{}\n\\texttt{\\$\\{user.home\\}/.liferay/token} \\textbar{} The file to hold the\nLiferay bundle authentication token. \\texttt{url} \\textbar{}\n\\texttt{URL} \\textbar{} \\texttt{\\$\\{liferay.workspace.bundle.url\\}}\n\\textbar{} The URL of the Liferay bundle to expand. \\texttt{userName}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The user\nname if your Liferay bundle's URL requires authentication.\n\n\\section{init Goal's Available\nParameters}\\label{init-goals-available-parameters}\n\nYou can change the default parameter values of the \\texttt{init} goal by\ncreating an \\texttt{\\textless{}execution\\textgreater{}} section\ncontaining \\texttt{\\textless{}configuration\\textgreater{}} tags. For\nexample,\n\n\\begin{verbatim}\n<execution>\n    <id>init</id>\n    <goals>\n        <goal>init</goal>\n    </goals>\n    <configuration>\n    </configuration>\n</execution>\n\\end{verbatim}\n\nYou can set the following parameters in the \\texttt{init} execution's\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{liferayHome} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{bundles} \\textbar{} The directory where your Liferay DXP\ninstance resides. This can be specified from the command line as\n\\texttt{-DliferayHome=}. \\texttt{cacheDir} \\textbar{} \\texttt{File}\n\\textbar{} \\texttt{\\$\\{user.home\\}/.liferay/bundles} \\textbar{} The\ndirectory where the downloaded bundle Zip files are stored.\n\\texttt{configs} \\textbar{} \\texttt{String} \\textbar{} \\texttt{configs}\n\\textbar{} The directory that contains the configuration files.\n\\texttt{environment} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\\$\\{liferay.workspace.environment\\}} \\textbar{} The environment\nwith the settings appropriate for current development (e.g.,\n\\texttt{common}, \\texttt{dev}, \\texttt{local}, \\texttt{prod}, and\n\\texttt{uat}). \\texttt{password} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The password if your Liferay bundle's URL\nrequires authentication. \\texttt{stripComponents} \\textbar{}\n\\texttt{int} \\textbar{} \\texttt{1} \\textbar{} The number of directories\nto strip when expanding your bundle. \\texttt{token} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether to use a\ntoken to download a Liferay DXP bundle. This should be set to\n\\texttt{true} when downloading a DXP bundle. \\texttt{tokenFile}\n\\textbar{} \\texttt{File} \\textbar{}\n\\texttt{\\$\\{user.home\\}/.liferay/token} \\textbar{} The file to hold the\nLiferay bundle authentication token. \\texttt{url} \\textbar{}\n\\texttt{URL} \\textbar{} \\texttt{\\$\\{liferay.workspace.bundle.url\\}}\n\\textbar{} The URL of the Liferay bundle to expand. \\texttt{userName}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The user\nname if your Liferay bundle's URL requires authentication.\n\n\\chapter{CSS Builder Plugin}\\label{css-builder-plugin}\n\nThe CSS Builder plugin lets you compile\n\\href{http://sass-lang.com/}{Sass} files in your project.\n\n\\section{Usage}\\label{usage-31}\n\nTo use the plugin, include it in your project's root \\texttt{pom.xml}\nfile:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.css.builder</artifactId>\n            <version>3.0.0</version>\n            <executions>\n                <execution>\n                    <id>default-build</id>\n                    <phase>compile</phase>\n                    <goals>\n                        <goal>build</goal>\n                    </goals>\n                </execution>\n            </executions>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n\\end{verbatim}\n\nYou can view an example POM containing the CSS Builder configuration\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/util/css-builder/samples/pom.xml}{here}.\n\n\\section{Goals}\\label{goals-1}\n\nThe plugin adds one Maven goal to your project:\n\nName \\textbar{} Description \\texttt{css-builder:build} \\textbar{}\nCompiles the Sass files in the project.\n\n\\section{Available Parameters}\\label{available-parameters}\n\nYou can set the following parameters in the\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{appendCssImportTimestamps} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{true} \\textbar{} Whether to append\nthe current timestamp to the URLs in the \\texttt{@import} CSS at-rules.\n\\texttt{baseDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{\"src/META-INF/resources\"} \\textbar{} The base directory that\ncontains the SCSS files to compile. \\texttt{dirNames} \\textbar{}\n\\texttt{List\\textless{}String\\textgreater{}} \\textbar{}\n\\texttt{{[}\"/\"{]}} \\textbar{} The name of the directories, relative to\n\\hyperref[basedir]{\\texttt{baseDir}}, which contain the SCSS files to\ncompile. \\texttt{generateSourceMap} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{false} \\textbar{} Whether to generate\n\\href{https://developers.google.com/web/tools/chrome-devtools/debug/readability/source-maps}{source\nmaps} for easier debugging. \\texttt{importDir} \\textbar{} \\texttt{File}\n\\textbar{} \\texttt{null} \\textbar{} The \\texttt{META-INF/resources}\ndirectory of the\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-css/frontend-css-common}{Liferay\nFrontend Common CSS} artifact. This is required in order to make\n\\href{http://bourbon.io}{Bourbon} and other CSS libraries available to\nthe compilation. \\texttt{outputDirName} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{\".sass-cache/\"} \\textbar{} The name of the\nsub-directories where the SCSS files are compiled to. For each directory\nthat contains SCSS files, a sub-directory with this name is created.\n\\texttt{precision} \\textbar{} \\texttt{int} \\textbar{} \\texttt{9}\n\\textbar{} The numeric precision of numbers in Sass.\n\\texttt{rtlExcludedPathRegexps} \\textbar{}\n\\texttt{List\\textless{}String\\textgreater{}} \\textbar{} \\textbar{} The\nSCSS file patterns to exclude when converting for right-to-left (RTL)\nsupport. \\texttt{sassCompilerClassName} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{\"jni\"} \\textbar{} The type of Sass compiler to use.\nSupported values are \\texttt{\"jni\"} and \\texttt{\"ruby\"}. The Ruby Sass\ncompiler requires \\texttt{com.liferay.sass.compiler.ruby.jar},\n\\texttt{com.liferay.ruby.gems.jar}, and \\texttt{jruby-complete.jar} to\nbe added to the classpath.\n\nYou can also manage the \\texttt{com.liferay.frontend.css.common} default\ntheme dependency provided by the CSS Builder in your \\texttt{pom.xml}.\nThis can be modified by adding it as a project dependency:\n\n\\begin{verbatim}\n<project>\n    ...\n    <dependencies>\n        <dependency>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.frontend.css.common</artifactId>\n            <version>3.0.1</version>\n            <scope>provided</scope>\n        </dependency>\n        ...\n    </dependencies>\n</project>\n\\end{verbatim}\n\nThere are additional Liferay theme-related dependencies you can manage\nthis way that are provided by the Theme Builder. See\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-builder-plugin}{this\nsection} for more information.\n\n\\chapter{DB Support Plugin}\\label{db-support-plugin}\n\nThe DB Support plugin lets you run the Liferay DB Support tool to\nexecute certain actions on a local Liferay DXP database. The following\nactions are available:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Cleans the Liferay database from the Service Builder tables and rows\n  of a module.\n\\end{itemize}\n\n\\section{Usage}\\label{usage-32}\n\nTo use the plugin, include it in your project's \\texttt{pom.xml} file:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.db.support</artifactId>\n            <version>1.0.6</version>\n            <configuration>\n            </configuration>\n            <dependencies>\n                <dependency>\n                    <groupId>org.hsqldb</groupId>\n                    <artifactId>hsqldb</artifactId>\n                    <version>2.4.0</version>\n                </dependency>\n            </dependencies>\n        </plugin>\n    ...\n    </plugins>\n</build>\n\\end{verbatim}\n\nAlso notice the configured plugin dependency. You must configure the\nJDBC driver used by your Liferay DXP bundle so the DB Support plugin can\nproperly manage your database. Replace the HSQLDB driver listed above\nwith your custom database's JDBC driver.\n\n\\section{Goals}\\label{goals-2}\n\nThe plugin adds one Maven goal to your project:\n\nName \\textbar{} Description \\texttt{db-support:clean-service-builder}\n\\textbar{} Cleans the Liferay DXP database from the Service Builder\ntables and rows of a module.\n\n\\section{Available Parameters}\\label{available-parameters-1}\n\nYou can set the following parameters in the\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{password} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{jdbc.default.password} \\textbar{} The user password for\nconnecting to the Liferay DXP database. \\texttt{propertiesFile}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The\n\\texttt{portal-ext.properties} file which contains the JDBC settings for\nconnecting to the Liferay DXP database. \\texttt{serviceXmlFile}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The\n\\texttt{service.xml} file of the module. \\texttt{servletContextName}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The\nservlet context name (usually the value of the\n\\texttt{Bundle-Symbolic-Name} manifest header) of the module.\n\\texttt{url} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{jdbc.default.url} \\textbar{} The JDBC URL for connecting to the\nLiferay DXP database. \\texttt{userName} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{jdbc.default.username} \\textbar{} The user name for\nconnecting to the Liferay DXP database.\n\n\\chapter{Deployment Helper Plugin}\\label{deployment-helper-plugin}\n\nThe Deployment Helper plugin lets you create a cluster deployable WAR\nfrom your OSGi artifacts.\n\n\\section{Usage}\\label{usage-33}\n\nTo use the plugin, include it in your project's root \\texttt{pom.xml}\nfile:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.deployment.helper</artifactId>\n            <version>1.0.4</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n\\end{verbatim}\n\nYou can view an example POM containing the Deployment Helper\nconfiguration\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/util/deployment-helper/samples/pom.xml}{here}.\n\n\\section{Goals}\\label{goals-3}\n\nThe plugin adds one Maven goal to your project:\n\nName \\textbar{} Description \\texttt{deployment-helper:build} \\textbar{}\nBuilds a WAR which contains one or more files that are copied once the\nWAR is deployed.\n\n\\section{Available Parameters}\\label{available-parameters-2}\n\nYou can set the following parameters in the\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{deploymentFileNames} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{} The files or directories to include\nin the WAR and copy once the WAR is deployed. If a directory is added to\nthis collection, all the JAR files contained in the directory are\nincluded in the WAR. \\texttt{deploymentPath} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{} The directory to which the included\nfiles are copied. \\texttt{outputFileName} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{} The WAR file to build.\n\n\\chapter{Javadoc Formatter Plugin}\\label{javadoc-formatter-plugin}\n\nThe Javadoc Formatter plugin lets you format project Javadoc comments.\nThe tool lets you generate:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Default\n  \\href{http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html\\#@author}{\\texttt{@author}}\n  tags to all classes.\n\\item\n  Comment stubs to classes, fields, and methods.\n\\item\n  Missing\n  \\href{https://docs.oracle.com/javase/8/docs/api/java/lang/Override.html}{\\texttt{@Override}}\n  annotations.\n\\item\n  An XML representation of the Javadoc comments, which can be used by\n  tools in order to index the Javadocs of the project.\n\\end{itemize}\n\n\\section{Usage}\\label{usage-34}\n\nTo use the plugin, include it in your project's root \\texttt{pom.xml}\nfile:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.javadoc.formatter</artifactId>\n            <version>1.0.32</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n\\end{verbatim}\n\nYou can view an example POM containing the Javadoc Formatter\nconfiguration\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/util/javadoc-formatter/samples/pom.xml}{here}.\n\n\\section{Goals}\\label{goals-4}\n\nThe plugin adds one Maven goal to your project:\n\nName \\textbar{} Description \\texttt{javadoc-formatter:format} \\textbar{}\nRuns the Liferay Javadoc Formatter to format files.\n\n\\section{Available Parameters}\\label{available-parameters-3}\n\nYou can set the following parameters in the\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{author} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"Brian\\ Wing\\ Shun\\ Chan\"} \\textbar{} The value of the\n\\texttt{@author} tag to add at class level if missing.\n\\texttt{generateXml} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{false} \\textbar{} Whether to generate a XML representation of\nthe Javadoc comments. The XML files are generated in the\n\\texttt{src/main/resources} directory only if the Java files are\ncontained in \\texttt{src/main/java}. \\texttt{initializeMissingJavadocs}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto add comment stubs at the class, field, and method levels. If\n\\texttt{false}, only the class-level \\texttt{@author} is added.\n\\texttt{inputDirName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"./\"} \\textbar{} The root directory to begin searching for Java\nfiles to format. \\texttt{limits} \\textbar{} \\texttt{String{[}{]}}\n\\textbar{} \\texttt{{[}{]}} \\textbar{} The Java file name patterns,\nrelative to the working directory, to include when formatting Javadoc\ncomments. The patterns must be specified without the \\texttt{.java} file\ntype suffix. If empty, all Java files are formatted.\n\\texttt{outputFilePrefix} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"javadocs\"} \\textbar{} The file name prefix of the XML\nrepresentation of the Javadoc comments. If \\texttt{generateXML} is\n\\texttt{false}, this property is not used. \\texttt{updateJavadocs}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto fix existing comment blocks by adding missing tags.\n\n\\chapter{Lang Builder Plugin}\\label{lang-builder-plugin}\n\nThe Lang Builder plugin lets you sort and translate the language keys in\nyour project.\n\n\\section{Usage}\\label{usage-35}\n\nTo use the plugin, include it in your project's root \\texttt{pom.xml}\nfile:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.lang.builder</artifactId>\n            <version>1.0.31</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n\\end{verbatim}\n\nYou can view an example POM containing the Lang Builder configuration\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/util/lang-builder/samples/pom.xml}{here}.\n\n\\section{Goals}\\label{goals-5}\n\nThe plugin adds one Maven goal to your project:\n\nName \\textbar{} Description \\texttt{lang-builder:build} \\textbar{} Runs\nLiferay Lang Builder to translate language property files.\n\n\\section{Available Parameters}\\label{available-parameters-4}\n\nYou can set the following parameters in the\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{excludedLanguageIds} \\textbar{}\n\\texttt{String{[}{]}} \\textbar{}\n\\texttt{\\{\"da\",\\ \"de\",\\ \"fi\",\\ \"ja\",\\ \"nl\",\\ \"pt\\_PT\",\\ \"sv\"\\}}\n\\textbar{} The language IDs to exclude in the automatic translation.\n\\texttt{langDirName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"src/content\"} \\textbar{} The directory where the language\nproperties files are saved. \\texttt{langFileName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\"Language\"} \\textbar{} The file name\nprefix of the language properties files (e.g.,\n\\texttt{Language\\_it.properties}). \\texttt{plugin} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{true} \\textbar{} Whether to check\nfor duplicate language keys between the project and the portal.\n\\texttt{portalLanguagePropertiesFileName} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{} The \\texttt{Language.properties}\nfile of the portal. \\texttt{translate} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{true} \\textbar{} Whether to translate the language\nkeys and generate a language properties file for each locale that's\nsupported by Liferay DXP. \\texttt{translateSubscriptionKey} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The subscription key\nfor Microsoft Translation integration. Subscription to the Translator\nText Translation API on Microsoft Cognitive Services is required. Basic\nsubscriptions, up to 2 million characters a month, are free.\n\n\\chapter{REST Builder Plugin}\\label{rest-builder-plugin}\n\nThe REST Builder plugin lets you generate a REST layer defined in the\nREST Builder \\texttt{rest-config.yaml} and \\texttt{rest-openapi.yaml}\nfiles.\n\n\\section{Usage}\\label{usage-36}\n\nTo use the plugin, include it in your project's root \\texttt{pom.xml}\nfile:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.rest.builder</artifactId>\n            <version>1.0.22</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n\\end{verbatim}\n\nYou can view an example POM containing the REST Builder configuration\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/util/portal-tools-rest-builder/samples/pom.xml}{here}.\n\n\\section{Goals}\\label{goals-6}\n\nThe plugin adds one Maven goal to your project:\n\nName \\textbar{} Description \\texttt{rest-builder:build} \\textbar{} Runs\nthe Liferay REST Builder.\n\n\\section{Available Parameters}\\label{available-parameters-5}\n\nYou can set the following parameters in the\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{copyrightFile} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{null} \\textbar{} The file that contains the copyright header.\n\\texttt{restConfigDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{\\$\\{project.projectDir\\}} \\textbar{} The directory that contains\nthe \\texttt{rest-config.yaml} and \\texttt{rest-openapi.yaml} files.\n\n\\chapter{Service Builder Plugin}\\label{service-builder-plugin}\n\nThe Service Builder plugin lets you generate a service layer defined in\na \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} \\texttt{service.xml} file. Visit the\n\\href{/docs/7-2/reference/-/knowledge_base/r/using-service-builder-in-a-maven-project}{Using\nService Builder in a Maven Project} tutorial to learn more about\napplying Service Builder to your Maven project.\n\n\\section{Usage}\\label{usage-37}\n\nTo use the plugin, include it in your project's root \\texttt{pom.xml}\nfile:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.service.builder</artifactId>\n            <version>1.0.292</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n\\end{verbatim}\n\nYou can view an example POM containing the Service Builder configuration\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/util/portal-tools-service-builder/samples/pom.xml}{here}.\n\n\\section{Goals}\\label{goals-7}\n\nThe plugin adds one Maven goal to your project:\n\nName \\textbar{} Description \\texttt{service-builder:build} \\textbar{}\nRuns the Liferay Service Builder.\n\n\\section{Available Parameters}\\label{available-parameters-6}\n\nYou can set the following parameters in the\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{apiDirName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"../portal-kernel/src\"} \\textbar{} A directory where the service\nAPI Java source files are generated.\n\\texttt{autoImportDefaultReferences} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{true} \\textbar{} Whether to automatically add default\nreferences, like \\texttt{com.liferay.portal.ClassName},\n\\texttt{com.liferay.portal.Resource} and\n\\texttt{com.liferay.portal.User}, to the services.\n\\texttt{autoNamespaceTables} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{null} \\textbar{} Whether to prefix table names by the namespace\nspecified in the \\texttt{service.xml} file. \\texttt{beanLocatorUtil}\n\\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"com.liferay.portal.kernel.bean.PortalBeanLocatorUtil\"}\n\\textbar{} The fully qualified class name of a bean locator class to use\nin the generated service classes. \\texttt{buildNumber} \\textbar{}\n\\texttt{long} \\textbar{} \\texttt{1} \\textbar{} A specific value to\nassign the \\texttt{build.number} property in the\n\\texttt{service.properties} file. \\texttt{buildNumberIncrement}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{true} \\textbar{} Whether\nto automatically increment the \\texttt{build.number} property in the\n\\texttt{service.properties} file by one at every service generation.\n\\texttt{databaseNameMaxLength} \\textbar{} \\texttt{int} \\textbar{}\n\\texttt{30} \\textbar{} The upper bound for database table and column\nname lengths to ensure it works on all databases. \\texttt{hbmFileName}\n\\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"src/META-INF/portal-hbm.xml\"} \\textbar{} A Hibernate Mapping\nfile to generate. \\texttt{implDirName} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{\"src\"} \\textbar{} A directory where the service Java\nsource files are generated. \\texttt{inputFileName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\"service.xml\"} \\textbar{} The\nproject's \\texttt{service.xml} file. \\texttt{modelHintsConfigs}\n\\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"classpath*:META-INF/portal-model-hints.xml,\\ META-INF/portal-model-hints.xml,\\ classpath*:META-INF/ext-model-hints.xml,\\ classpath*:META-INF/portlet-model-hints.xml\"}\n\\textbar{} Paths to the model hints files for Liferay Service Builder to\nuse in generating the service layer. \\texttt{modelHintsFileName}\n\\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"src/META-INF/portal-model-hints.xml\"} \\textbar{} A model hints\nfile for the project. \\texttt{osgiModule} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{null} \\textbar{} Whether to generate the service\nlayer for OSGi modules. \\texttt{pluginName} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{null} \\textbar{} If specified, a plugin can enable\nadditional generation features, such as \\texttt{Clp} class generation,\nfor non-OSGi modules. \\texttt{propsUtil} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{\"com.liferay.portal.util.PropsUtil\"} \\textbar{} The\nfully qualified class name of the service properties util class to\ngenerate. \\texttt{readOnlyPrefixes} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{\"fetch,\\ get,\\ has,\\ is,\\ load,\\ reindex,\\ search\"}\n\\textbar{} Prefixes of methods to consider read-only.\n\\texttt{resourceActionsConfigs} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"META-INF/resource-actions/default.xml,\\ resource-actions/default.xml\"}\n\\textbar{} Paths to the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions}{resource\nactions} files for Liferay Service Builder to use in generating the\nservice layer. \\texttt{resourcesDirName} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{\"src\"} \\textbar{} A directory where the service\nnon-Java files are generated. \\texttt{springFileName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\"src/META-INF/portal-spring.xml\"}\n\\textbar{} A service Spring file to generate. \\texttt{springNamespaces}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{\"beans\"} \\textbar{}\nNamespaces of Spring XML Schemas to add to the service Spring file.\n\\texttt{sqlDirName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"../sql\"} \\textbar{} A directory where the SQL files are\ngenerated. \\texttt{sqlFileName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"portal-tables.sql\"} \\textbar{} A name (relative to\n\\texttt{sqlDir}) for the file in which the SQL table creation\ninstructions are generated. \\texttt{sqlIndexesFileName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\"indexes.sql\"} \\textbar{} A name\n(relative to \\texttt{sqlDir}) for the file in which the SQL index\ncreation instructions are generated. \\texttt{sqlSequencesFileName}\n\\textbar{} \\texttt{String} \\textbar{} \\texttt{\"sequences.sql\"}\n\\textbar{} A name (relative to \\texttt{sqlDir}) for the file in which\nthe SQL sequence creation instructions are generated.\n\\texttt{targetEntityName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} If specified, it's the name of the entity for\nwhich Liferay Service Builder should generate the service.\n\\texttt{testDirName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"test/integration\"} \\textbar{} If specified, it's a directory\nwhere integration test Java source files are generated.\n\n\\chapter{Source Formatter Plugin}\\label{source-formatter-plugin}\n\nThe Source Formatter plugin formats project files according to Liferay's\nsource formatting standards. For more documentation on Source Formatter\nspecific functionality, visit the tool's\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/source-formatter/documentation}{documentation}\nfolder.\n\n\\section{Usage}\\label{usage-38}\n\nTo use the plugin, include it in your project's root \\texttt{pom.xml}\nfile:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.source.formatter</artifactId>\n            <version>1.0.885</version>\n            <executions>\n                <execution>\n                    <phase>process-sources</phase>\n                    <goals>\n                        <goal>format</goal>\n                    </goals>\n                </execution>\n            </executions>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n\\end{verbatim}\n\nYou can view an example POM containing the Source Formatter\nconfiguration\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/util/source-formatter/samples/pom.xml}{here}.\n\n\\section{Goals}\\label{goals-8}\n\nThe plugin adds one Maven goal to your project:\n\nName \\textbar{} Description \\texttt{source-formatter:format} \\textbar{}\nRuns the Liferay Source Formatter to format source formatting errors.\n\n\\section{Available Parameters}\\label{available-parameters-7}\n\nYou can set the following parameters in the\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{autoFix} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to automatically fix source formatting\nerrors. \\texttt{baseDir} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"./\"} \\textbar{} The Source Formatter base directory.\n\\emph{(Read-only)} \\texttt{fileNames} \\textbar{} \\texttt{String{[}{]}}\n\\textbar{} \\texttt{null} \\textbar{} The file names to format, relative\nto the project directory. If \\texttt{null}, all files contained in\n\\texttt{baseDir} will be formatted. \\texttt{formatCurrentBranch}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto format only the files contained in \\texttt{baseDir} that are added or\nmodified in the current Git branch. \\texttt{formatLatestAuthor}\n\\textbar{} \\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether\nto format only the files contained in \\texttt{baseDir} that are added or\nmodified in the latest Git commits of the same author.\n\\texttt{formatLocalChanges} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{false} \\textbar{} Whether to format only the unstaged files\ncontained in \\texttt{baseDir}. \\texttt{gitWorkingBranchName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\"master\"} \\textbar{} The Git working\nbranch name. \\texttt{includeSubrepositories} \\textbar{} \\texttt{boolean}\n\\textbar{} \\texttt{false} \\textbar{} Whether to format files that are in\nread-only subrepositories. \\texttt{maxLineLength} \\textbar{}\n\\texttt{int} \\textbar{} \\texttt{80} \\textbar{} The maximum number of\ncharacters allowed in Java files. \\texttt{printErrors} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{true} \\textbar{} Whether to print\nformatting errors on the Standard Output stream.\n\\texttt{processorThreadCount} \\textbar{} \\texttt{int} \\textbar{}\n\\texttt{5} \\textbar{} The number of threads used by Source Formatter.\n\\texttt{showDocumentation} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{false} \\textbar{} Whether to show the documentation for the\nsource formatting issues, if present. \\texttt{throwException} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether to fail\nthe build if formatting errors are found.\n\n\\chapter{Theme Builder Plugin}\\label{theme-builder-plugin}\n\nThe Theme Builder plugin lets you build Liferay theme files in your\nproject. Visit the\n\\href{/docs/7-2/reference/-/knowledge_base/r/building-a-theme-with-maven}{Building\na Theme with Maven} tutorial to learn more about applying Theme Builder\nto your Maven project.\n\n\\section{Usage}\\label{usage-39}\n\nTo use the plugin, include it in your project's root \\texttt{pom.xml}\nfile:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.theme.builder</artifactId>\n            <version>1.1.7</version>\n            <executions>\n                <execution>\n                    <phase>generate-resources</phase>\n                    <goals>\n                        <goal>build</goal>\n                    </goals>\n                    <configuration>\n                    </configuration>\n                </execution>\n            </executions>\n        </plugin>\n        ...\n    </plugins>\n</build>\n\\end{verbatim}\n\nYou can view an example POM containing the Theme Builder configuration\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/util/portal-tools-theme-builder/samples/pom.xml}{here}.\n\n\\section{Goals}\\label{goals-9}\n\nThe plugin adds one Maven goal to your project:\n\nName \\textbar{} Description \\texttt{theme-builder:build} \\textbar{}\nBuilds the theme files.\n\n\\section{Available Parameters}\\label{available-parameters-8}\n\nYou can set the following parameters in the\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{diffsDir} \\textbar{} \\texttt{File} \\textbar{}\n\\texttt{\\$\\{maven.war.src\\}} \\textbar{} The directory that contains the\nfiles to copy over the parent theme. \\texttt{name} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\\$\\{project.artifactId\\}} \\textbar{}\nThe name of the new theme. \\texttt{outputDir} \\textbar{} \\texttt{File}\n\\textbar{}\n\\texttt{\\$\\{project.build.directory\\}/\\$\\{project.build.finalName\\}}\n\\textbar{} The directory where to build the theme. \\texttt{parentDir}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The\ndirectory of the parent theme. \\texttt{parentName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{null} \\textbar{} The name of the\nparent theme. \\texttt{templateExtension} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{\"ftl\"} \\textbar{} The extension of the template\nfiles, usually \\texttt{\"ftl\"} or \\texttt{\"vm\"}. \\texttt{unstyledDir}\n\\textbar{} \\texttt{File} \\textbar{} \\texttt{null} \\textbar{} The\ndirectory of\n\\href{https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-unstyled}{Liferay\nFrontend Theme Unstyled}.\n\nYou can also manage the \\texttt{com.liferay.frontend.theme.styled} and\n\\texttt{com.liferay.frontend.theme.unstyled} default theme dependencies\nprovided by the Theme Builder in your \\texttt{pom.xml}. They can be\nmodified by adding them as project dependencies:\n\n\\begin{verbatim}\n<project>\n    ...\n    <dependencies>\n        ...\n        <dependency>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.frontend.theme.styled</artifactId>\n            <version>3.0.4</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.frontend.theme.unstyled</artifactId>\n            <version>3.0.4</version>\n            <scope>provided</scope>\n        </dependency>\n    </dependencies>\n</project>\n\\end{verbatim}\n\nThere is an additional Liferay theme-related dependency you can manage\nthis way that's provided by the CSS Builder. See\n\\href{/docs/7-2/reference/-/knowledge_base/r/css-builder-plugin}{this\nsection} for more information.\n\n\\chapter{TLD Formatter Plugin}\\label{tld-formatter-plugin}\n\nThe TLD Formatter plugin lets you format a project's TLD files.\n\n\\section{Usage}\\label{usage-40}\n\nTo use the plugin, include it in your project's root \\texttt{pom.xml}\nfile:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.tld.formatter</artifactId>\n            <version>1.0.5</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n\\end{verbatim}\n\nYou can view an example POM containing the TLD Formatter configuration\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/util/tld-formatter/samples/pom.xml}{here}.\n\n\\section{Goals}\\label{goals-10}\n\nThe plugin adds one Maven goal to your project:\n\nName \\textbar{} Description \\texttt{tld-formatter:format} \\textbar{}\nRuns the Liferay TLD Formatter to format files.\n\n\\section{Available Parameters}\\label{available-parameters-9}\n\nYou can set the following parameters in the\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{baseDirName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{\"./\"} \\textbar{} The base directory to begin searching for TLD\nfiles to format. \\texttt{plugin} \\textbar{} \\texttt{boolean} \\textbar{}\n\\texttt{true} \\textbar{} Whether to format all the TLD files contained\nin the working directory. If \\texttt{false}, all\n\\texttt{liferay-portlet-ext.tld} files are ignored.\n\n\\chapter{WSDD Builder Plugin}\\label{wsdd-builder-plugin}\n\nThe WSDD Builder plugin lets you generate the\n\\href{http://axis.apache.org/axis/}{Apache Axis} Web Service Deployment\nDescriptor (WSDD) files from a\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} \\texttt{service.xml} file.\n\n\\section{Usage}\\label{usage-41}\n\nTo use the plugin, include it in your project's root \\texttt{pom.xml}\nfile:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.wsdd.builder</artifactId>\n            <version>1.0.10</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n\\end{verbatim}\n\nYou can view an example POM containing the WSDD Builder configuration\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/util/portal-tools-wsdd-builder/samples/pom.xml}{here}.\n\n\\section{Goals}\\label{goals-11}\n\nThe plugin adds one Maven goal to your project:\n\nName \\textbar{} Description \\texttt{wsdd-builder:build} \\textbar{} Runs\nthe Liferay WSDD Builder to generate the WSDD files.\n\n\\section{Available Parameters}\\label{available-parameters-10}\n\nYou can set the following parameters in the\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{classPath} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The classpath that the Liferay WSDD Builder\nuses to generate WSDD files. \\texttt{inputFileName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\"service.xml\"} \\textbar{} The file\nfrom which to generate the WSDD files. \\texttt{outputDirName} \\textbar{}\n\\texttt{String} \\textbar{} \\texttt{\"src\"} \\textbar{} The directory where\nthe \\texttt{*\\_deploy.wsdd} and \\texttt{*\\_undeploy.wsdd} files are\ngenerated. \\texttt{serverConfigFileName} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{\"server-config.wsdd\"} \\textbar{} The file to\ngenerate. \\texttt{serviceNamespace} \\textbar{} \\texttt{String}\n\\textbar{} \\texttt{\"Plugin\"} \\textbar{} The namespace for the WSDD\nService.\n\n\\chapter{XML Formatter Plugin}\\label{xml-formatter-plugin}\n\nThe XML Formatter plugin lets you format a project's XML files.\n\n\\section{Usage}\\label{usage-42}\n\nTo use the plugin, include it in your project's root \\texttt{pom.xml}\nfile:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.xml.formatter</artifactId>\n            <version>1.0.5</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n\\end{verbatim}\n\nYou can view an example POM containing the XML Formatter configuration\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/util/xml-formatter/samples/pom.xml}{here}.\n\n\\section{Goals}\\label{goals-12}\n\nThe plugin adds one Maven goal to your project:\n\nName \\textbar{} Description \\texttt{xml-formatter:format} \\textbar{}\nRuns the Liferay XML Formatter to format the project files.\n\n\\section{Available Parameters}\\label{available-parameters-11}\n\nYou can set the following parameters in the\n\\texttt{\\textless{}configuration\\textgreater{}} section of the POM:\n\nParameter Name \\textbar{} Type \\textbar{} Default Value \\textbar{}\nDescription \\texttt{fileName} \\textbar{} \\texttt{String} \\textbar{}\n\\texttt{null} \\textbar{} The XML file to format. This plugin only lets\nyou format one XML file at a time. \\texttt{stripComments} \\textbar{}\n\\texttt{boolean} \\textbar{} \\texttt{false} \\textbar{} Whether to remove\nall the comments from the XML file.\n\n\\chapter{PortletMVC4Spring}\\label{portletmvc4spring}\n\nPortletMVC4Spring integrates Spring, the Spring Web Framework, and the\nMVC design pattern with portlet development. As such, it uses\nconfiguration files from each of these areas and provides new\nannotations that facilitate portlet application development. Here are\nthe PortletMVC4Spring reference topics:\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-annotations}{PortletMVC4Spring\n  Annotations}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-configuration-files}{PortletMVC4Spring\n  Configuration Files}\n\\end{itemize}\n\n\\chapter{PortletMVC4Spring Project\nAnatomy}\\label{portletmvc4spring-project-anatomy}\n\nPortletMVC4Spring portlets are packaged in WARs. Liferay provide Maven\narchetypes for creating projects configured to use JSP/JSPX and\nThymeleaf templates. Their commands are listed below. The\nPortletMVC4Spring project structure follows the commands.\n\n\\section{Maven Commands for Generating PortletMVC4Spring\nProjects}\\label{maven-commands-for-generating-portletmvc4spring-projects}\n\nHere are Maven commands for generating PortletMVC4Spring portlet\nprojects that use JSPX and \\href{https://www.thymeleaf.org}{Thymeleaf}\nView templates:\n\n\\section{SP/JSPX Form Portlet}\\label{spjspx-form-portlet}\n\n\\begin{verbatim}\nmvn archetype:generate \\\n-DarchetypeGroupId=com.liferay.portletmvc4spring.archetype \\\n-DarchetypeArtifactId=com.liferay.portletmvc4spring.archetype.form.jsp.portlet \\\n-DarchetypeVersion=5.1.0 \\ \n-DgroupId=com.mycompany \\ \n-DartifactId=com.mycompany.my.form.jsp.portlet\n\\end{verbatim}\n\n\\section{Thymeleaf Form Portlet}\\label{thymeleaf-form-portlet}\n\n\\begin{verbatim}\nmvn archetype:generate \\\n-DarchetypeGroupId=com.liferay.portletmvc4spring.archetype \\\n-DarchetypeArtifactId=com.liferay.portletmvc4spring.archetype.form.thymeleaf.portlet \\\n-DarchetypeVersion=5.1.0 \\\n-DgroupId=com.mycompany \\\n-DartifactId=com.mycompany.my.form.thymeleaf.portlet\n\\end{verbatim}\n\n\\section{Project Structure}\\label{project-structure}\n\nThe Maven commands generate a project that includes Model and Controller\nclasses, View templates, a resource bundle, a stylesheet, and more. The\n\\href{/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-configuration-files}{Spring\ncontexts and configuration files} set PortletMVC4Spring development\nessentials. Here's the resulting project structure:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{{[}com.mycompany.my.form.jsp.portlet{]}}/ → Arbitrary project\n  name\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{src/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java/{[}my-package-path{]}/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{controller/} → Sub-package for Controller classes\n          (optional)\n        \\item\n          \\texttt{dto/} → Sub-package for Model (data transfer object)\n          classes (optional)\n        \\item\n          \\texttt{resources/} → Resources to include in the class path -\n          \\texttt{content/} → Resource bundles -\n          \\texttt{log4j.properties} → Log4J logging configuration\n        \\item\n          \\texttt{webapp/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{resources/}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{css/} → Style sheets\n            \\item\n              \\texttt{images/} → Images\n            \\end{itemize}\n          \\item\n            \\texttt{WEB-INF/}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{spring-context/} → Contexts\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                \\texttt{portlet/} → Portlet contexts\n\n                \\begin{itemize}\n                \\tightlist\n                \\item\n                  \\texttt{portlet1-context.xml} → Portlet context\n                \\end{itemize}\n              \\item\n                \\texttt{portlet-application-context.xml} → Application\n                context\n              \\end{itemize}\n            \\item\n              \\texttt{views/} → View templates\n            \\item\n              \\texttt{liferay-display.xml} → Portlet display\n              configuration\n            \\item\n              \\texttt{liferay-plugin-package.properties} → Packaging\n              descriptor\n            \\item\n              \\texttt{liferay-portlet.xml} → Liferay-specific portlet\n              configuration\n            \\item\n              \\texttt{portlet.xml} → Portlet configuration\n            \\item\n              \\texttt{web.xml} → Web application configuration\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{test/java/} → Test source files\n    \\end{itemize}\n  \\item\n    \\texttt{build.gradle} → Gradle build file\n  \\item\n    \\texttt{pom.xml} → Maven POM\n  \\end{itemize}\n\\end{itemize}\n\n\\chapter{PortletMVC4Spring\nAnnotations}\\label{portletmvc4spring-annotations}\n\nPortletMVC4Spring provides several annotations for mapping requests to\ncontroller classes and controller methods.\n\n\\section{\\texorpdfstring{\\texttt{@RenderMapping} Annotation\nExamples}{@RenderMapping Annotation Examples}}\\label{rendermapping-annotation-examples}\n\nThe following table describes some \\texttt{@RenderMapping} annotation\nexamples.\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5769}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.4231}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nExample\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{@RenderMapping} & Handle primary render requests if no other\nhandler methods match the render request. \\\\\n\\texttt{@RenderMapping(params\\ =\\ \"javax.portlet.action=success\")} &\nHandle the render request if has parameter a parameter setting\n\\texttt{javax.portlet.action=success}. \\\\\n\\texttt{@RenderMapping(param\\ =\\ \"foo\")} & Handle the request if it has\na parameter named \\texttt{foo}, regardless of its value. \\\\\n\\texttt{@RenderMapping(param\\ =\\ \"!bar\")} & Handle the request as long\nas it has not parameter named \\texttt{bar}. \\\\\n\\texttt{@RenderMapping(windowState\\ =\\ \"MAXIMIZED\")} & Handle the\nrequest if the window state is \\texttt{MAXIMIZED}. Note, supported\nportlet window states are \\texttt{NORMAL}, \\texttt{MAXIMIZED}, and\n\\texttt{MINIMIZED}. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{\\texttt{@ActionMapping} Annotation\nExamples}{@ActionMapping Annotation Examples}}\\label{actionmapping-annotation-examples}\n\nThe table below describes some \\texttt{@ActionMapping} annotation\nexamples.\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5769}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.4231}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nExample\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{@ActionMapping} & Handle primary action requests if no other\nhandler methods match the action request. \\\\\n\\texttt{@ActionMapping(params\\ =\\ some.param=yourValue\")} & Handle the\naction request if has parameter a parameter setting\n\\texttt{javax.portlet.action=success}. \\\\\n\\texttt{@ActionMapping(param\\ =\\ \"foo\")} & Handle the request if it has\na parameter named \\texttt{foo}, regardless of its value. \\\\\n\\texttt{@ActionMapping(param\\ =\\ \"!bar\")} & Handle the request as long\nas it has not parameter named \\texttt{bar}. \\\\\n\\end{longtable}\n\n\\chapter{PortletMVC4Spring Configuration\nFiles}\\label{portletmvc4spring-configuration-files}\n\nA PortletMVC4Spring application has these descriptors, Spring contexts,\nand properties files in its \\texttt{WEB-INF} folder:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{web.xml} → Web application descriptor\n\\item\n  \\texttt{portlet.xml} → Portlet application descriptor\n\\item\n  \\texttt{liferay-portlet.xml} → Liferay-specific portlet descriptor\n\\item\n  \\texttt{liferay-display.xml} → Liferay-specific display descriptor\n\\item\n  \\texttt{spring-context/portlet-application-context.xml} → Portlet\n  application context\n\\item\n  \\texttt{spring-context/portlet/{[}portlet{]}-context.xml} → Portlet\n  context\n\\item\n  \\texttt{liferay-plugin-package.properties} → Packaging descriptor\n\\end{itemize}\n\nExamples of each file are provided and portlet-specific content is\nhighlighted.\n\n\\section{web.xml}\\label{web.xml}\n\nThe servlet container processes the \\texttt{web.xml}. This file\nspecifies the servlet that render's the portlet and the portlet\napplication's context, servlet, filters, listeners, and more. Here's an\nexample \\texttt{web.xml}:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<web-app version=\"3.1\" xmlns=\"http://xmlns.jcp.org/xml/ns/javaee\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd\">\n    <context-param>\n        <param-name>contextConfigLocation</param-name>\n        <param-value>/WEB-INF/spring-context/portlet-application-context.xml</param-value>\n    </context-param>\n    <servlet>\n        <servlet-name>ViewRendererServlet</servlet-name>\n        <servlet-class>com.liferay.portletmvc4spring.ViewRendererServlet</servlet-class>\n        <load-on-startup>1</load-on-startup>\n    </servlet>\n    <servlet-mapping>\n        <servlet-name>ViewRendererServlet</servlet-name>\n        <url-pattern>/WEB-INF/servlet/view</url-pattern>\n    </servlet-mapping>\n    <filter>\n        <filter-name>delegatingFilterProxy</filter-name>\n        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>\n    </filter>\n    <filter-mapping>\n        <filter-name>delegatingFilterProxy</filter-name>\n        <url-pattern>/WEB-INF/servlet/view</url-pattern>\n        <dispatcher>FORWARD</dispatcher>\n        <dispatcher>INCLUDE</dispatcher>\n    </filter-mapping>\n    <listener>\n        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n    </listener>\n</web-app>\n\\end{verbatim}\n\nThe \\texttt{\\textless{}context-param/\\textgreater{}} element gives the\npath to the portlet application context (discussed later):\n\n\\begin{verbatim}\n<context-param>\n    <param-name>contextConfigLocation</param-name>\n    <param-value>/WEB-INF/spring-context/portlet-application-context.xml</param-value>\n</context-param>\n\\end{verbatim}\n\nThe \\texttt{\\textless{}servlet/\\textgreater{}} and\n\\texttt{\\textless{}servlet-mapping/\\textgreater{}} elements set the\nservlet and the internal location for its views.\n\n\\begin{verbatim}\n<servlet>\n    <servlet-name>ViewRendererServlet</servlet-name>\n    <servlet-class>com.liferay.portletmvc4spring.ViewRendererServlet</servlet-class>\n    <load-on-startup>1</load-on-startup>\n</servlet>\n<servlet-mapping>\n    <servlet-name>ViewRendererServlet</servlet-name>\n    <url-pattern>/WEB-INF/servlet/view</url-pattern>\n</servlet-mapping>\n\\end{verbatim}\n\nThe\n\\href{https://liferay.github.io/portletmvc4spring/apidocs/com/liferay/portletmvc4spring/ViewRendererServlet.html}{\\texttt{ViewRendererServlet}}.\nconverts portlet requests into servlet requests and enables the view to\nbe rendered using the Spring Web MVC infrastructure and the\ninfrastructure's renderers for JSP, Thymeleaf, Velocity, and more.\n\nThe filter and filter mappings are set to forward and include servlet\nviews as necessary.\n\n\\begin{verbatim}\n<filter>\n    <filter-name>delegatingFilterProxy</filter-name>\n    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>\n</filter>\n<filter-mapping>\n    <filter-name>delegatingFilterProxy</filter-name>\n    <url-pattern>/WEB-INF/servlet/view</url-pattern>\n    <dispatcher>FORWARD</dispatcher>\n    <dispatcher>INCLUDE</dispatcher>\n</filter-mapping>\n\\end{verbatim}\n\nA listener is configured for processing the application's contexts.\n\n\\begin{verbatim}\n<listener>\n    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n</listener>\n\\end{verbatim}\n\nLiferay's project archetypes generate all this boilerplate code.\n\n\\section{portlet.xml}\\label{portlet.xml}\n\nThe \\texttt{portlet.xml} file describes the portlet application to the\nportlet container. Here's an example:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<portlet-app xmlns=\"http://xmlns.jcp.org/xml/ns/portlet\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/portlet http://xmlns.jcp.org/xml/ns/portlet/portlet-app_3_0.xsd\" version=\"3.0\">\n    <portlet>\n        <portlet-name>portlet1</portlet-name>\n        <display-name>com.mycompany.my.form.jsp.portlet</display-name>\n        <portlet-class>com.liferay.portletmvc4spring.DispatcherPortlet</portlet-class>\n        <init-param>\n            <name>contextConfigLocation</name>\n            <value>/WEB-INF/spring-context/portlet/portlet1-context.xml</value>\n        </init-param>\n        <expiration-cache>0</expiration-cache>\n        <supports>\n            <mime-type>text/html</mime-type>\n            <portlet-mode>view</portlet-mode>\n        </supports>\n        <resource-bundle>content.portlet1</resource-bundle>\n        <portlet-info>\n            <title>com.mycompany.my.form.jsp.portlet</title>\n            <short-title>com.mycompany.my.form.jsp.portlet</short-title>\n            <keywords>com.mycompany.my.form.jsp.portlet</keywords>\n        </portlet-info>\n        <security-role-ref>\n            <role-name>administrator</role-name>\n        </security-role-ref>\n        <security-role-ref>\n            <role-name>guest</role-name>\n        </security-role-ref>\n        <security-role-ref>\n            <role-name>power-user</role-name>\n        </security-role-ref>\n        <security-role-ref>\n            <role-name>user</role-name>\n        </security-role-ref>\n    </portlet>\n    <filter>\n        <filter-name>SpringSecurityPortletFilter</filter-name>\n        <filter-class>com.liferay.portletmvc4spring.security.SpringSecurityPortletFilter</filter-class>\n        <lifecycle>ACTION_PHASE</lifecycle>\n        <lifecycle>RENDER_PHASE</lifecycle>\n        <lifecycle>RESOURCE_PHASE</lifecycle>\n    </filter>\n    <filter-mapping>\n        <filter-name>SpringSecurityPortletFilter</filter-name>\n        <portlet-name>portlet1</portlet-name>\n    </filter-mapping>\n</portlet-app>\n\\end{verbatim}\n\nThis application has one portlet named \\texttt{portlet1}.\n\n\\begin{verbatim}\n<portlet-name>portlet1</portlet-name>\n<display-name>com.mycompany.my.form.jsp.portlet</display-name>\n<portlet-class>com.liferay.portletmvc4spring.DispatcherPortlet</portlet-class>\n\\end{verbatim}\n\nThe \\texttt{\\textless{}portlet-name/\\textgreater{}} is internal and the\n\\texttt{\\textless{}display-name/\\textgreater{}} is shown to users.\n\\texttt{\\textless{}portlet-class/\\textgreater{}} specifies the portlet's\nJava class.\n\n\\textbf{Important:} All PortletMVC4Spring portlets must specify\n\\texttt{\\textless{}portlet-class\\textgreater{}com.liferay.portletmvc4spring.DispatcherPortlet\\textless{}/portlet-class\\textgreater{}}.\n\nThe \\texttt{\\textless{}supports/\\textgreater{}} element must declare the\nmime type that the portlet templates use.\n\nThe \\texttt{\\textless{}resource-bundle/\\textgreater{}} sets the path to\nthe portlet's localized Java message properties. For example, the\nelement refers to properties at \\texttt{content/portlet1.properties}:\n\n\\begin{verbatim}\n<resource-bundle>content.portlet1</resource-bundle>\n\\end{verbatim}\n\nThe \\texttt{\\textless{}portlet-info/\\textgreater{}} element lists the\nportlet's titles and reserved keyword.\n\nThe \\texttt{\\textless{}security-role-ref/\\textgreater{}} elements\ndeclare default user roles the portlet accounts for.\n\nLastly, the \\texttt{\\textless{}filter/\\textgreater{}} named\n\\href{https://liferay.github.io/portletmvc4spring/apidocs/index.html}{\\texttt{SpringSecurityPortletFilter}}\nprevents Cross-Site Request Forgery (CSRF).\n\n\\begin{verbatim}\n<filter>\n    <filter-name>SpringSecurityPortletFilter</filter-name>\n    <filter-class>com.liferay.portletmvc4spring.security.SpringSecurityPortletFilter</filter-class>\n    <lifecycle>ACTION_PHASE</lifecycle>\n    <lifecycle>RENDER_PHASE</lifecycle>\n    <lifecycle>RESOURCE_PHASE</lifecycle>\n</filter>\n<filter-mapping>\n    <filter-name>SpringSecurityPortletFilter</filter-name>\n    <portlet-name>portlet1</portlet-name>\n</filter-mapping>\n\\end{verbatim}\n\nThe\n\\href{https://docs.liferay.com/portlet-api/3.0/portlet-app_3_0.xsd}{\\texttt{portlet\\ XSD}}\ndefines the \\texttt{portlet.xml}. The Liferay-specific portlet\ndescriptor is next.\n\n\\section{liferay-portlet.xml}\\label{liferay-portlet.xml}\n\nThe \\texttt{liferay-portlet.xml} file applies Liferay-specific settings\nthat provide more developer features. Here's an example:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE liferay-portlet-app PUBLIC \"-//Liferay//DTD Portlet Application 7.1.0//EN\" \"http://www.liferay.com/dtd/liferay-portlet-app_7_1_0.dtd\">\n\n<liferay-portlet-app>\n    <portlet>\n        <portlet-name>portlet1</portlet-name>\n        <icon>/resources](./images/icon.png</icon>\n        <requires-namespaced-parameters>false</requires-namespaced-parameters>\n    </portlet>\n    <role-mapper>\n        <role-name>administrator</role-name>\n        <role-link>Administrator</role-link>\n    </role-mapper>\n    <role-mapper>\n        <role-name>guest</role-name>\n        <role-link>Guest</role-link>\n    </role-mapper>\n    <role-mapper>\n        <role-name>power-user</role-name>\n        <role-link>Power User</role-link>\n    </role-mapper>\n    <role-mapper>\n        <role-name>user</role-name>\n        <role-link>User</role-link>\n    </role-mapper>\n</liferay-portlet-app>\n\\end{verbatim}\n\nThis \\texttt{\\textless{}portlet/\\textgreater{}} element associates an\nicon with the portlet and indicates that name-spaced parameters aren't\nrequired.\n\nThe \\texttt{\\textless{}role-mapper/\\textgreater{}} elements associate\nthe portlet with default Liferay DXP user roles.\n\nThe\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/definitions/liferay-portlet-app_7_2_0.dtd.html}{liferay-portlet-app\nDTD} defines the \\texttt{liferay-portlet.xml} file.\n\n\\section{liferay-display.xml}\\label{liferay-display.xml}\n\nThe \\texttt{liferay-display.xml} applies display characteristics to the\nportlet. For example, this descriptor associates the portlet with a\nWidget category in Liferay DXP's Add Widget menu.\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE display PUBLIC \"-//Liferay//DTD Display 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-display_7_2_0.dtd\">\n\n<display>\n<category name=\"category.sample\">\n    <portlet id=\"portlet1\" />\n</category>\n</display>\n\\end{verbatim}\n\nSee the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/definitions/liferay-display_7_2_0.dtd.html}{liferay-display\nDTD} for details.\n\nIt's time to look at the application contexts.\n\n\\section{Portlet Application Context}\\label{portlet-application-context}\n\nThis context applies to all of the application's portlets. This is where\nyou specify view resolvers, resource bundles, security beans, proxies,\nand more. Here's an example:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<beans\n    xmlns=\"http://www.springframework.org/schema/beans\"\n    xmlns:context=\"http://www.springframework.org/schema/context\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"\n        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\n        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\">\n    <context:annotation-config />\n    <bean class=\"org.springframework.web.servlet.view.InternalResourceViewResolver\" id=\"viewResolver\">\n        <property name=\"contentType\" value=\"text/html;charset=UTF-8\" />\n        <property name=\"prefix\" value=\"/WEB-INF/views/\" />\n        <property name=\"suffix\" value=\".jspx\" />\n        <property name=\"viewClass\" value=\"com.liferay.portletmvc4spring.PortletJstlView\" />\n    </bean>\n    <bean id=\"messageSource\" class=\"org.springframework.context.support.ResourceBundleMessageSource\">\n        <property name=\"basenames\">\n            <list>\n                <value>content.portlet1</value>\n            </list>\n        </property>\n        <property name=\"defaultEncoding\" value=\"UTF-8\" />\n    </bean>\n    <bean id=\"springSecurityPortletConfigurer\" class=\"com.liferay.portletmvc4spring.security.SpringSecurityPortletConfigurer\" />\n    <bean id=\"delegatingFilterProxy\" class=\"org.springframework.web.filter.DelegatingFilterProxy\">\n        <property name=\"targetBeanName\" value=\"springSecurityFilterChain\" />\n    </bean>\n</beans>\n\\end{verbatim}\n\nThe view resolver bean above handles JSPX view templates. To resolve\nThymeleaf view templates, for example, you could specify these beans:\n\n\\begin{verbatim}\n<bean class=\"org.thymeleaf.templateresolver.ServletContextTemplateResolver\" id=\"templateResolver\">\n    <property name=\"prefix\" value=\"/WEB-INF/views/\"/>\n    <property name=\"suffix\" value=\".html\"/>\n    <property name=\"templateMode\" value=\"HTML\"/>\n</bean>\n<bean class=\"org.thymeleaf.spring5.SpringTemplateEngine\" id=\"templateEngine\">\n    <property name=\"templateResolver\" ref=\"templateResolver\"></property>\n    <property name=\"enableSpringELCompiler\" value=\"true\" />\n</bean>\n<bean class=\"org.thymeleaf.spring5.view.ThymeleafViewResolver\" id=\"viewResolver\">\n    <property name=\"templateEngine\" ref=\"templateEngine\"/>\n    <property name=\"order\" value=\"1\"/>\n</bean>\n\\end{verbatim}\n\nThe context's \\texttt{springSecurityPortletConfigurer} bean facilitates\nusing Spring Security:\n\n\\begin{verbatim}\n<bean id=\"springSecurityPortletConfigurer\" \n    class=\"com.liferay.portletmvc4spring.security.SpringSecurityPortletConfigurer\" />\n\\end{verbatim}\n\nYou can also designate contexts for each portlet in the application.\n\n\\section{Portlet Contexts}\\label{portlet-contexts}\n\nBeans specific to a portlet, go in the portlet's context. Since\nannotations are the easiest way to develop PortletMVC4Spring portlets,\nyou should specify MVC annotation scanning in the portlet context:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<beans\n    xmlns=\"http://www.springframework.org/schema/beans\"\n    xmlns:context=\"http://www.springframework.org/schema/context\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n    xsi:schemaLocation=\"\n        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\n        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\n        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n    <context:component-scan base-package=\"portlet1**\"/>\n    <mvc:annotation-driven/>\n</beans>\n\\end{verbatim}\n\nThe portlet context naming convention is\n\\texttt{{[}portlet-name{]}-context.xml}. To associate your portlet with\nits own context, edit your application's \\texttt{portlet.xml} file and\nadd an \\texttt{\\textless{}init-param/\\textgreater{}} element that maps\nthe \\texttt{\\textless{}portlet/\\textgreater{}} element to the portlet's\ncontext:\n\n\\begin{verbatim}\n<init-param>\n    <name>contextConfigLocation</name>\n    <value>/WEB-INF/spring-context/portlet/portlet1-context.xml</value>\n</init-param>\n\\end{verbatim}\n\nWhat's left is to describe your application package.\n\n\\section{liferay-plugin-package.properties}\\label{liferay-plugin-package.properties-1}\n\nThis file specifies the application's name, version, Java package\nimports/exports, and OSGi metadata. Here's an example package properties\nfile:\n\n\\begin{verbatim}\nauthor=N/A\nchange-log=\nlicenses=N/A\nliferay-versions=7.1.0+\nlong-description=\nmodule-group-id=com.mycompany\nmodule-incremental-version=1\nname=com.mycompany.my.form.jsp.portlet\npage-url=\nshort-description=my portlet short description\ntags=myTag\nBundle-Version: 1.0.0\nImport-Package: com.liferay.portal.webserver,com.liferay.portal.kernel.servlet.filters.invoker\n\\end{verbatim}\n\nIt uses this OSGi metadata header to\n\\href{/docs/7-2/customization/-/knowledge_base/c/importing-packages}{import\nrequired Java packages}:\n\n\\begin{verbatim}\nImport-Package: com.liferay.portal.webserver,\\\ncom.liferay.portal.kernel.servlet.filters.invoker\n\\end{verbatim}\n\nOn deploying the portlet application WAR file, the\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{WAB\nGenerator} adds the specified OSGi metadata to the resulting web\napplication bundle (WAB) that's deployed to Liferay's runtime framework.\n\nThe\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/liferay-plugin-package_7_2_0.properties.html}{liferay-plugin-package\nreference document} describes the\n\\texttt{liferay-plugin-package.properties} file.\n\nCongratulations! You've successfully toured the PortletMVC4Spring\nconfiguration files.\n\n\\section{Related Topics}\\label{related-topics-48}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-annotations}{PortletMVC4Spring\nAnnotations}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/migrating-to-portletmvc4spring}{Migrating\nto PortletMVC4Spring}\n\n\\chapter{Project Templates}\\label{project-templates}\n\nLiferay provides project templates that you can use to generate starter\nprojects formatted in an opinionated way. These templates can be used by\nmost build tools (e.g., Gradle, Maven, Dev Studio) to generate your\ndesired project structure.\n\nIf you're using\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI},\nexecute the following command to display a full list of project\ntemplates:\n\n\\begin{verbatim}\nblade create -l\n\\end{verbatim}\n\nIf you're using\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven}{Maven}, you can view\nand use the project templates as Maven archetypes. Execute the following\ncommand to list them:\n\n\\begin{verbatim}\nmvn archetype:generate -Dfilter=liferay\n\\end{verbatim}\n\nArchetypes with the \\texttt{com.liferay.project.templates} prefix are\nthe latest templates offered by Liferay.\n\nIf you're using\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Dev\nStudio}, navigate to \\emph{File} → \\emph{New} → \\emph{Liferay Module\nProject} and view the project templates from the \\emph{Project Template\nName} drop-down menu.\n\nIn this section of reference articles, each project template is outlined\nwith the appropriate generation command and folder structure. Visit the\nproject template article you're most interested in to start building\nyour own project!\n\n\\chapter{Activator Template}\\label{activator-template}\n\nIn this article, you'll learn how to create a Liferay activator as a\nLiferay module. To create a Liferay activator via the command line using\nBlade CLI or Maven, use one of the commands with the following\nparameters:\n\n\\begin{verbatim}\nblade create -t activator [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.activator \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{activator}. Suppose you\nwant to create an activator project called \\texttt{my-activator-project}\nwith a package name of \\texttt{com.liferay.docs.activator} and a class\nname of \\texttt{Activator}. You could run the following command to\naccomplish this:\n\n\\begin{verbatim}\nblade create -t activator -p com.liferay.docs.activator -c Activator my-activator-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.activator \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-activator-project \\\n    -Dpackage=com.liferay.docs.activator \\\n    -Dversion=1.0 \\\n    -DclassName=Activator \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nNote that in your class, you're implementing the\n\\texttt{org.osgi.framework.BundleActivator} interface.\n\nAfter running the Blade command above, your project's directory\nstructure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-activator-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/activator}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Activator.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is functional and is deployable to a Liferay DXP\ninstance. To build upon the generated app, modify the project by adding\nlogic and additional files to the folders outlined above.\n\n\\chapter{API Template}\\label{api-template}\n\nIn this tutorial, you'll learn how to create a Liferay API as a Liferay\nmodule. To create a Liferay API via the command line using Blade CLI or\nMaven, use one of the commands with the following parameters:\n\n\\begin{verbatim}\nblade create -t api [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.api \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{api}. The \\texttt{api}\ntemplate creates a simple \\texttt{api} module with an empty public\ninterface. For example, suppose you want to create an API project called\n\\texttt{my-api-project} with a package name of\n\\texttt{com.liferay.docs.api} and a class name of \\texttt{MyApi}. You\ncould run the following command to accomplish this:\n\n\\begin{verbatim}\nblade create -t api -p com.liferay.docs -c MyApi my-api-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.api \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-api-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=MyApi \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-api-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/api}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{MyApi.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/api}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{packageinfo}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is a working application and is deployable to a\nLiferay DXP instance. To build upon the generated app, modify the\nproject by adding logic and additional files to the folders outlined\nabove.\n\n\\chapter{Control Menu Entry Template}\\label{control-menu-entry-template}\n\nIn this article, you'll learn how to create a Liferay Control Menu entry\nas a Liferay module. To create a Liferay Control Menu entry via the\ncommand line using Blade CLI or Maven, use one of the commands with the\nfollowing parameters:\n\n\\begin{verbatim}\nblade create -t control-menu-entry [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.control.menu.entry \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{control-menu-entry}.\nSuppose you want to create a control menu entry project called\n\\texttt{my-control-menu-entry-project} with a package name of\n\\texttt{com.liferay.docs.entry.control.menu} and a class name of\n\\texttt{SampleProductNavigationControlMenuEntry}. You could run the\nfollowing command to accomplish this:\n\n\\begin{verbatim}\nblade create -t control-menu-entry -p com.liferay.docs.entry -c Sample my-control-menu-entry-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.control.menu.entry \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-control-menu-entry-project \\\n    -Dpackage=com.liferay.docs.entry \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure would look like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-control-menu-entry-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/entry/control/menu}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{SampleProductNavigationControlMenuEntry.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{content}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Language.properties}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is functional and is deployable to a Liferay DXP\ninstance. To build upon the generated app, modify the project by adding\nlogic and additional files to the folders outlined above. You can visit\nthe\n\\href{/docs/7-1/reference/-/knowledge_base/r/control-menu-entry}{control-menu-entry}\nsample project for a more expanded sample of a Control Menu entry.\n\n\\chapter{Form Field Template}\\label{form-field-template}\n\nIn this article, you'll learn how to create a Liferay form field as a\nLiferay module. To create a Liferay form field via the command line\nusing Blade CLI or Maven, use one of the commands with the following\nparameters:\n\n\\begin{verbatim}\nblade create -t form-field [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.form.field \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{form-field}. Suppose\nyou want to create a form field project called\n\\texttt{my-form-field-project} with a package name of\n\\texttt{com.liferay.docs.form.field} and a class name prefix of\n\\texttt{MyFormField}. You could run one of the following commands to\naccomplish this:\n\n\\begin{verbatim}\nblade create -t form-field -p com.liferay.docs -c MyFormField my-form-field-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.form.field \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-form-field-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=MyFormField \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-form-field-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/form/field}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{MyFormFieldDDMFormFieldRenderer.java}\n          \\item\n            \\texttt{MyFormFieldDDMFormFieldType.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{content}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Language.properties}\n          \\end{itemize}\n        \\item\n          \\texttt{META-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{resources}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{config.js}\n            \\item\n              \\texttt{my-form-field-project.soy}\n            \\item\n              \\texttt{my-form-field-project\\_field.js}\n            \\item\n              \\texttt{my-form-field-project\\_field.es.js}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is a working form field and is deployable to a\nLiferay DXP instance. To build upon the generated app, modify the\nproject by adding logic and additional files to the folders outlined\nabove.\n\n\\chapter{Fragment Template}\\label{fragment-template}\n\nIn this article, you'll learn how to create a Liferay fragment as a\nLiferay module. You can learn more about fragment modules in the\n\\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-osgi-fragments\\#declaring-a-fragment-host}{Declaring\na Fragment Host} article and in section 3.14 of the\n\\href{https://osgi.org/download/r6/osgi.core-6.0.0.pdf}{OSGi Alliance's\ncore specification document}.\n\nTo create a Liferay fragment via the command line using Blade CLI or\nMaven, use one of the commands with the following parameters:\n\n\\begin{verbatim}\nblade create -t fragment [-h hostBundleName] [-H hostBundleVersion] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.fragment \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{fragment}. Suppose you\nwant to create a fragment project called \\texttt{my-fragment-project}\nwith a host bundle symbolic name of \\texttt{com.liferay.login.web} and\nhost bundle version of \\texttt{1.0.0}. You could run the following\ncommand to accomplish this:\n\n\\begin{verbatim}\nblade create -t fragment -h com.liferay.login.web -H 1.0.0 my-fragment-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.fragment \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-fragment-project \\\n    -Dversion=1.0 \\\n    -Dpackage= \\\n    -DhostBundleSymbolicName=com.liferay.login.web \\\n    -DhostBundleVersion=1.0.0 \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nThe folder structure is created, but there are no files. The only files\ncreated are the \\texttt{bnd.bnd} and \\texttt{build.gradle} files, which\nspecify your host bundle and its information, and your build tool's\nfiles. After running the Blade command above, your project's directory\nstructure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-fragment-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{my/fragment/project} -\\texttt{resources}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is functional and is deployable to a Liferay DXP\ninstance. To build upon the generated app, modify the project by adding\nlogic and additional files to the folders outlined above.\n\n\\chapter{FreeMarker Portlet Template}\\label{freemarker-portlet-template}\n\nIn this article, you'll learn how to create a Liferay FreeMarker portlet\napplication as a Liferay module. To create a Liferay FreeMarker portlet\napplication via the command line using Blade CLI or Maven, use one of\nthe commands with the following parameters:\n\n\\begin{verbatim}\nblade create -t freemarker-portlet [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.freemarker.portlet \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{freemarker-portlet}.\nSuppose you want to create a FreeMarker portlet project called\n\\texttt{my-freemarker-portlet-project} with a package name of\n\\texttt{com.liferay.docs.freemarkerportlet} and a class name of\n\\texttt{MyFreemarkerPortlet}. Also, you'd like to create a service of\ntype \\texttt{javax.portlet.Portlet} that extends the\n\\texttt{com.liferay.util.bridges.freemarker.FreeMarkerPortlet} class.\nHere, \\emph{service} means an OSGi service, not a Liferay API. Another\nway to say \\emph{service type} is to say \\emph{component type}. You\ncould run the following command to accomplish this:\n\n\\begin{verbatim}\nblade create -t freemarker-portlet -p com.liferay.docs.freemarkerportlet -c MyFreemarkerPortlet my-freemarker-portlet-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.freemarker.portlet \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-freemarker-portlet-project \\\n    -Dpackage=com.liferay.docs.freemarkerportlet \\\n    -Dversion=1.0 \\\n    -DclassName=MyFreemarkerPortlet \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-freemarker-portlet-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/freemarkerportlet}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{constants}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{MyFreemarkerPortletKeys.java}\n            \\end{itemize}\n          \\item\n            \\texttt{portlet}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{MyFreemarkerPortlet.java}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{content}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Language.properties}\n          \\end{itemize}\n        \\item\n          \\texttt{META-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{resources}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{css}\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                \\texttt{main.scss}\n              \\end{itemize}\n            \\end{itemize}\n          \\end{itemize}\n        \\item\n          \\texttt{templates}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{init.ftl}\n          \\item\n            \\texttt{view.ftl}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is a working application and is deployable to a\nLiferay DXP instance. To build upon the generated app, modify the\nproject by adding logic and additional files to the folders outlined\nabove.\n\n\\chapter{Layout Template}\\label{layout-template}\n\nIn this article, you'll learn how to create a Liferay layout template as\na WAR project. To create a Liferay layout template via the command line\nusing Blade CLI or Maven, use one of the commands with the following\nparameters:\n\n\\begin{verbatim}\nblade create -t layout-template projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.layout.template \\\n    -DartifactId=[projectName] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{layout-template}.\nSuppose you want to create a layout template project called\n\\texttt{my-layout-template-project}. You could run one of the following\ncommands to accomplish this:\n\n\\begin{verbatim}\nblade create -t layout-template my-layout-template-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.layout.template \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-layout-template-project \\\n    -Dversion=1.0 \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-layout-template-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{webapp}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{WEB-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{liferay-layout-templates.xml}\n          \\item\n            \\texttt{liferay-plugin-package.properties}\n          \\end{itemize}\n        \\item\n          \\texttt{my-layout-template-project.ftl}\n        \\item\n          \\texttt{my-layout-template-project.png}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated WAR is a working layout template and is deployable to a\nLiferay DXP instance. To build upon the generated layout template,\nmodify the project by adding logic and additional files to the folders\noutlined above.\n\n\\chapter{Modules Ext Template}\\label{modules-ext-template}\n\nIn this article, you'll learn how to create an Ext module. To create an\nExt module via the command line using Blade CLI or Maven, use one of the\ncommands with the following parameters:\n\n\\begin{verbatim}\nblade create -t modules-ext [-p packageName] [-m originalModuleName] [-M originalModuleVersion] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.modules.ext \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DoriginalModuleName=[originalModuleName] \\\n    -DoriginalModuleVersion=[originalModuleVersion] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{modules-ext}. Suppose\nyou want to create an Ext module called \\texttt{my-ext-module-project}\nthat overrides the \\texttt{com.liferay.test.web} module (BSN) with\nversion \\texttt{1.0.0}. If you have\n\\href{/docs/7-1/tutorials/-/knowledge_base/t/managing-the-target-platform-for-liferay-workspace}{Target\nPlatform} enabled, you're not required to specify the intended module\nversion to override. Also, the override module has a package path of\n\\texttt{com.liferay.docs.test}. You must use the exact path of the\noriginal module when creating an Ext module. You could run the following\ncommand to accomplish this:\n\n\\begin{verbatim}\nblade create -t modules-ext -p com.liferay.docs.test -m com.liferay.test.web -M 1.0.0 my-ext-module-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.modules.ext \\\n    -DartifactId=my-ext-module-project \\\n    -Dpackage=com.liferay.docs.test \\\n    -DoriginalModuleName=com.liferay.test.web \\\n    -DoriginalModuleVersion=1.0.0 \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-ext-module-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/test}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nTo build upon the generated project, modify the project by adding logic\nand additional files to the folders outlined above.\n\n\\chapter{MVC Portlet Template}\\label{mvc-portlet-template}\n\nIn this article, you'll learn how to create a Liferay MVC portlet\napplication as a Liferay module. To create a Liferay MVC portlet\napplication via the command line using Blade CLI or Maven, use one of\nthe commands with the following parameters:\n\n\\begin{verbatim}\nblade create -t mvc-portlet [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.mvc.portlet \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{mvc-portlet}. Suppose\nyou want to create an MVC portlet project called\n\\texttt{my-mvc-portlet-project} with a package name of\n\\texttt{com.liferay.docs.mvcportlet} and a class name of\n\\texttt{MyMvcPortlet}. Also, you'd like to create a service of type\n\\texttt{javax.portlet.Portlet} that extends the\n\\texttt{com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet} class.\nHere, \\emph{service} means an OSGi service, not a Liferay API. Another\nway to say \\emph{service type} is to say \\emph{component type}. You\ncould run the following command to accomplish this:\n\n\\begin{verbatim}\nblade create -t mvc-portlet -p com.liferay.docs.mvcportlet -c MyMvcPortlet my-mvc-portlet-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.mvc.portlet \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-mvc-portlet-project \\\n    -Dpackage=com.liferay.docs.mvcportlet \\\n    -Dversion=1.0 \\\n    -DclassName=MyMvcPortlet \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-mvc-portlet-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/mvcportlet}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{constants}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{MyMvcPortletKeys.java}\n            \\end{itemize}\n          \\item\n            \\texttt{portlet}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{MyMvcPortlet.java}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{content}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Language.properties}\n          \\end{itemize}\n        \\item\n          \\texttt{META-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{resources}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{init.jsp}\n            \\item\n              \\texttt{view.jsp}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is a working application and is deployable to a\nLiferay DXP instance. To build upon the generated app, modify the\nproject by adding logic and additional files to the folders outlined\nabove.\n\n\\chapter{Panel App Template}\\label{panel-app-template}\n\nIn this article, you'll learn how to create a Liferay panel app and\ncategory as a Liferay module. To create a Liferay panel app and category\nvia the command line using Blade CLI or Maven, use one of the commands\nwith the following parameters:\n\n\\begin{verbatim}\nblade create -t panel-app [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.panel.app \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{panel-app}. Suppose you\nwant to create a panel app project called \\texttt{my-panel-app-project}\nwith a package name prefix of \\texttt{com.liferay.docs} and a class name\nprefix of \\texttt{Sample}. You could run the following command to\naccomplish this:\n\n\\begin{verbatim}\nblade create -t panel-app -p com.liferay.docs -c Sample my-panel-app-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.panel.app \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-panel-app-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure would look like this\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-panel-app-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{application/list}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{SamplePanelApp.java}\n            \\item\n              \\texttt{SamplePanelCategory.java}\n            \\end{itemize}\n          \\item\n            \\texttt{constants}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{SamplePanelCategoryKeys.java}\n            \\item\n              \\texttt{SamplePortletKeys.java}\n            \\end{itemize}\n          \\item\n            \\texttt{portlet}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{SamplePortlet.java}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{content}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Language.properties}\n          \\end{itemize}\n        \\item\n          \\texttt{META-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{resources}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{init.jsp}\n            \\item\n              \\texttt{view.jsp}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is functional and is deployable to a Liferay DXP\ninstance. The generated module, by default, creates a panel category\nwith a panel app in Liferay DXP's Product Menu. To build upon the\ngenerated app, modify the project by adding logic and additional files\nto the folders outlined above.\n\n\\chapter{Portlet Configuration Icon}\\label{portlet-configuration-icon}\n\nIn this article, you'll learn how to create a Liferay portlet\nconfiguration icon as a Liferay module. To create a portlet\nconfiguration icon via the command line using Blade CLI or Maven, use\none of the commands with the following parameters:\n\n\\begin{verbatim}\nblade create -t portlet-configuration-icon [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.portlet.configuration.icon \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is\n\\texttt{portlet-configuration-icon}. Suppose you want to create a\nportlet configuration icon project called\n\\texttt{my-portlet-config-icon} with a package name of\n\\texttt{com.liferay.docs.portlet.configuration.icon} and a class name of\n\\texttt{SamplePortletConfigurationIcon}. You could run the following\ncommand to accomplish this:\n\n\\begin{verbatim}\nblade create -t portlet-configuration-icon -p com.liferay.docs -c Sample my-portlet-config-icon\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.portlet.configuration.icon \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-portlet-config-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure would look like this\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-portlet-config-icon}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/portlet/configuration/icon}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{SamplePortletConfigurationIcon.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{content}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Language.properties}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is functional and is deployable to a Liferay DXP\ninstance. The generated module, by default, creates a sample link in the\nHello World portlet's Options menu. To build upon the generated app,\nmodify the project by adding logic and additional files to the folders\noutlined above. You can visit the\n\\href{https://github.com/liferay/liferay-blade-samples/tree/master/gradle/extensions/portlet-configuration-icon}{portlet-configuration-icon}\nsample project for a more expanded sample of a portlet configuration\nicon.\n\n\\chapter{Portlet Provider Template}\\label{portlet-provider-template}\n\nIn this article, you'll learn how to create a Liferay portlet provider\nas a Liferay module. To create a Liferay portlet provider via the\ncommand line using Blade CLI or Maven, use one of the commands with the\nfollowing parameters:\n\n\\begin{verbatim}\nblade create -t portlet-provider [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.portlet.provider \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{portlet-provider}.\nSuppose you want to create a portlet provider project called\n\\texttt{my-portlet-provider-project} with a package name of\n\\texttt{com.liferay.docs.portlet} and a class name prefix of\n\\texttt{Sample}. You could run the following command to accomplish this:\n\n\\begin{verbatim}\nblade create -t portlet-provider -p com.liferay.docs -c Sample my-portlet-provider-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.portlet.provider \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-portlet-provider-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure would look like this\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-portlet-provider-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{constants}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{SamplePortletKeys.java}\n            \\item\n              \\texttt{SampleWebKeys.java}\n            \\end{itemize}\n          \\item\n            \\texttt{portlet}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{SampleAddPortletProvider.java}\n            \\item\n              \\texttt{SamplePortlet.java}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{META-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{resources}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{init.jsp}\n            \\item\n              \\texttt{view.jsp}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is functional and is deployable to a Liferay DXP\ninstance. To build upon the generated app, modify the project by adding\nlogic and additional files to the folders outlined above. You can visit\nthe\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes}{Providing\nPortlets to Manage Requests} tutorial for instructions on customizing a\nportlet provider project.\n\n\\chapter{Portlet Toolbar Contributor\nTemplate}\\label{portlet-toolbar-contributor-template}\n\nIn this article, you'll learn how to create a Liferay portlet toolbar\ncontributor as a Liferay module. To create a portlet toolbar contributor\nentry via the command line using Blade CLI or Maven, use one of the\ncommands with the following parameters:\n\n\\begin{verbatim}\nblade create -t portlet-toolbar-contributor [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.portlet.toolbar.contributor \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is\n\\texttt{portlet-toolbar-contributor}. Suppose you want to create a\nportlet toolbar contributor project called\n\\texttt{my-portlet-toolbar-contributor} with a package name of\n\\texttt{com.liferay.docs.portlet.toolbar.contributor} and a class name\nof \\texttt{SamplePortletToolbarContributor}. You could run the following\ncommand to accomplish this:\n\n\\begin{verbatim}\nblade create -t portlet-toolbar-contributor -p com.liferay.docs -c Sample my-portlet-toolbar-contributor\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.portlet.toolbar.contributor \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-portlet-toolbar-contributor \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure would look like this\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-portlet-toolbar-contributor}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/portlet/toolbar/contributor}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{SamplePortletToolbarContributor.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{content}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Language.properties}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is functional and is deployable to a Liferay DXP\ninstance. To build upon the generated app, modify the project by adding\nlogic and additional files to the folders outlined above. This generated\nproject, by default, creates a new button on the Hello World portlet's\ntoolbar. You can visit the\n\\href{https://github.com/liferay/liferay-blade-samples/tree/master/gradle/extensions/portlet-toolbar-contributor}{portlet-toolbar-contributor}\nsample project for a more expanded sample of a portlet toolbar\ncontributor.\n\n\\chapter{REST Template}\\label{rest-template}\n\nIn this article, you'll learn how to create a Liferay RESTful web\nservice packaged in a Liferay module. To create a Liferay RESTful web\nservice via the command line using Blade CLI or Maven, use one of the\ncommands with the following parameters:\n\n\\begin{verbatim}\nblade create -t rest [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.rest \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{rest}. Suppose you want\nto create a RESTful web service project called \\texttt{my-rest-project}\nwith a package name of \\texttt{com.liferay.docs.application} and a class\nname prefix of \\texttt{Rest}. You could run one of the following\ncommands to accomplish this:\n\n\\begin{verbatim}\nblade create -t rest -p com.liferay.docs -c Rest my-rest-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.rest \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-rest-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Rest \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-rest-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/application}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{RestApplication.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{configuration}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{com.liferay.portal.remote.cxf.common.configuration.CXFEndpointPublisherConfiguration-cxf.properties}\n          \\item\n            \\texttt{com.liferay.portal.remote.rest.extender.configuration.RestExtenderConfiguration-rest.properties}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is a working RESTful web service and is deployable\nto a Liferay DXP instance. To build upon the generated app, modify the\nproject by adding logic and additional files to the folders outlined\nabove.\n\n\\chapter{Service Builder Template}\\label{service-builder-template}\n\nIn this article, you'll learn how to create a Liferay portlet\napplication that uses Service Builder as Liferay modules. To create a\nLiferay Service Builder project via the command line using Blade CLI or\nMaven, use one of the commands with the following parameters:\n\n\\begin{verbatim}\nblade create -t service-builder [-p packageName] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service.builder \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DapiPath=[apiPath] \\\n    -DdependencyInjector=[dependencyInjector] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nBy default, the Service Builder project uses OSGi Declarative Services\n(\\texttt{ds}) for its dependency injector. If you prefer using Spring,\nyou can set the parameter \\texttt{-\\/-dependency-injector\\ spring} with\nBlade CLI or \\texttt{-DdependencyInjector=spring} with Maven. See the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/dependency-injection}{Dependency\nInjection} section for more information on these options.\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{service-builder}.\nSuppose you want to create a Service Builder project called\n\\texttt{tasks} with a package name of \\texttt{com.liferay.docs.tasks}\nusing OSGi Declarative Services. You could run the following command to\naccomplish this:\n\n\\begin{verbatim}\nblade create -t service-builder -p com.liferay.docs.tasks tasks\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service.builder \\\n    -DgroupId=com.liferay \\\n    -DartifactId=tasks \\\n    -Dpackage=com.liferay.docs.tasks \\\n    -Dversion=1.0 \\\n    -DapiPath=com.liferay.api.path \\\n    -DdependencyInjector=ds \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nThis task creates the \\texttt{tasks-api} and \\texttt{tasks-service}\nfolders. In many cases, a Service Builder project also requires a\n\\texttt{-web} folder to hold, for example, portlet classes. This should\nbe created manually. After running the Blade command above, your\nproject's directory structure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{tasks}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{tasks-api}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{bnd.bnd}\n    \\item\n      \\texttt{build.gradle}\n    \\end{itemize}\n  \\item\n    \\texttt{tasks-service}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{bnd.bnd}\n    \\item\n      \\texttt{build.gradle}\n    \\item\n      \\texttt{service.xml}\n    \\end{itemize}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\item\n    \\texttt{settings.gradle}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nTo generate your service and API classes for the \\texttt{*-api} and\n\\texttt{*-service} modules, replace the \\texttt{service.xml} file in the\n\\texttt{*-service} module. Depending on your build tool, you can build\nyour services by executing\n\n\\begin{verbatim}\nblade gw buildService\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn service-builder:build\n\\end{verbatim}\n\nfrom the \\texttt{tasks} root directory. Note that \\texttt{blade\\ gw}\nonly works if the Gradle wrapper can be detected. To ensure the\navailability of the Gradle wrapper, be sure to work in a Liferay\nWorkspace.\n\nThe \\texttt{mvn\\ service-builder:build} command only works if you're\nusing the \\texttt{com.liferay.portal.tools.service.builder} plugin\nversion 1.0.145+. Maven projects using an earlier version of the Service\nBuilder plugin should update their POM accordingly.\n\nThe generated module is functional and is deployable to a Liferay DXP\ninstance. To build upon the generated app, modify the project by adding\nlogic and additional files to the folders outlined above.\n\nFor more information on Service Builder, see the\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} section.\n\n\\chapter{Service Template}\\label{service-template}\n\nIn this article, you'll learn how to create a Liferay service as a\nLiferay module. To create a Liferay service via the command line using\nBlade CLI or Maven, use one of the commands with the following\nparameters:\n\n\\begin{verbatim}\nblade create -t service [-p packageName] [-c className] [-s serviceName] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className]\n    -DserviceName=[serviceName] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{service}. Suppose you\nwant to create a service project called \\texttt{my-service-project} with\na package name of \\texttt{com.liferay.docs.service} and a class name of\n\\texttt{Service}. Also, you'd like to create a service of type\n\\texttt{com.liferay.portal.kernel.events.LifecycleAction} that also\nimplements that same service. You could run the following command to\naccomplish this:\n\n\\begin{verbatim}\nblade create -t service -p com.liferay.docs.service -c Service -s com.liferay.portal.kernel.events.LifecycleAction my-service-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-service-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Service \\\n    -DclassName=com.liferay.portal.kernel.events.LifecycleAction \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure would look like this\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-service-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/service}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Service.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is functional and is deployable to a Liferay DXP\ninstance. To build upon the generated app, modify the project by adding\nlogic and additional files to the folders outlined above.\n\n\\chapter{Service Wrapper Template}\\label{service-wrapper-template}\n\nIn this article, you'll learn how to create a Liferay service wrapper as\na Liferay module. To create a Liferay service wrapper via the command\nline using Blade CLI or Maven, use one of the commands with the\nfollowing parameters:\n\n\\begin{verbatim}\nblade create -t service-wrapper [-p packageName] [-c className] [-s serviceWrapperClass] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service.wrapper \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DserviceWrapperClass=[serviceWrapperClass] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{service-wrapper}.\nSuppose you want to create a service wrapper project called\n\\texttt{service-override} with a package name of\n\\texttt{com.liferay.docs.serviceoverride} and a class name of\n\\texttt{UserLocalServiceOverride}. Also, you'd like to create a service\nof type \\texttt{com.liferay.portal.kernel.service.ServiceWrapper} that\nextends the \\texttt{com.liferay.portal.service.UserLocalServiceWrapper}\nclass. You could run the following command to accomplish this:\n\n\\begin{verbatim}\nblade create -t service-wrapper -p com.liferay.docs.serviceoverride -c UserLocalServiceOverride -s com.liferay.portal.kernel.service.UserLocalServiceWrapper service-override\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service.wrapper \\\n    -DgroupId=com.liferay \\\n    -DartifactId=service-override \\\n    -Dpackage=com.liferay.docs.serviceoverride \\\n    -Dversion=1.0 \\\n    -DclassName=UserLocalServiceOverride \\\n    -DserviceWrapperClass=com.liferay.portal.kernel.service.UserLocalServiceWrapper \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nHere, \\emph{service} means an OSGi service, not a Liferay API. Another\nway to say \\emph{service type} is to say \\emph{component type}.\n\nAfter running the Blade command above, your project's directory\nstructure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{service-override}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/serviceoverride}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{UserLocalServiceOverride.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is a working application and is deployable to a\nLiferay DXP instance. To build upon the generated app, modify the\nproject by adding logic and additional files to the folders outlined\nabove.\n\n\\chapter{Simulation Panel Entry\nTemplate}\\label{simulation-panel-entry-template}\n\nIn this article, you'll learn how to create a Liferay simulation panel\nentry as a Liferay module. To create a simulation panel entry via the\ncommand line using Blade CLI or Maven, use one of the commands with the\nfollowing parameters:\n\n\\begin{verbatim}\nblade create -t simulation-panel-entry [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.simulation.panel.entry \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is\n\\texttt{simulation-panel-entry}. Suppose you want to create a simulation\npanel entry project called \\texttt{my-simulation-panel-entry} with a\npackage name of \\texttt{com.liferay.docs.application.list} and a class\nname of \\texttt{SampleSimulationPanelApp}. You could run the following\ncommand to accomplish this:\n\n\\begin{verbatim}\nblade create -t simulation-panel-entry -p com.liferay.docs -c Sample my-simulation-panel-entry\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.simulation.panel.entry \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-simulation-panel-entry \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure would look like this\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-simulation-panel-entry}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/application/list}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{SampleSimulationPanelApp.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{content}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Language.properties}\n          \\end{itemize}\n        \\item\n          \\texttt{META-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{resources}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{simulation\\_panel.jsp}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is functional and is deployable to a Liferay DXP\ninstance. To build upon the generated app, modify the project by adding\nlogic and additional files to the folders outlined above. You can visit\nthe\n\\href{https://github.com/liferay/liferay-blade-samples/tree/master/gradle/apps/simulation-panel-app}{simulation-panel-app}\nsample project for a more expanded sample of a control menu entry.\n\n\\chapter{Social Bookmark Template}\\label{social-bookmark-template}\n\nIn this article, you'll learn how to create a Liferay social bookmark as\na Liferay module. To create a social bookmark as a module via the\ncommand line using Blade CLI or Maven, use one of the commands with the\nfollowing parameters:\n\n\\begin{verbatim}\nblade create -t social-bookmark [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.social.bookmark \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{social-bookmark}.\nSuppose you want to create a social bookmark project called\n\\texttt{my-social-bookmark-project} with a package name of\n\\texttt{com.liferay.docs.socialbookmark} and a class name of\n\\texttt{TestSocialBookmark}. You could run the following command to\naccomplish this:\n\n\\begin{verbatim}\nblade create -t social-bookmark -p com.liferay.docs.socialbookmark -c Test my-social-bookmark-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.social.bookmark \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-social-bookmark-project \\\n    -Dpackage=com.liferay.docs.socialbookmark \\\n    -Dversion=1.0 \\\n    -DclassName=Test \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-social-bookmark-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/socialbookmark}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{social/bookmark}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{TestSocialBookmark}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{content}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Language.properties}\n          \\end{itemize}\n        \\item\n          \\texttt{META-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{resources}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{icons.svg}\n            \\item\n              \\texttt{init.jsp}\n            \\item\n              \\texttt{page.jsp}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is a working application and is deployable to a\nLiferay DXP instance. An unmodified module generated as described above\ncreates a social bookmark named \\emph{Test} that searches the current\nURL using Google Search.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/social-bookmark-project-template.png}\n\\caption{Click the magnifying glass icon to search the current URL using\nGoogle Search.}\n\\end{figure}\n\nTo build upon the generated app, modify the project by adding logic and\nadditional files to the folders outlined above. For more information on\ndeveloping social bookmarks, see the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/social-api}{Social API}\nsection of tutorials. For information on configuring social bookmarks\nfor the Blogs widget, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/displaying-blogs}{Displaying\nBlogs} article.\n\n\\chapter{Spring MVC Portlet Template}\\label{spring-mvc-portlet-template}\n\nIn this article, you'll learn how to create a Liferay Spring MVC portlet\napplication as a WAR. To create a Liferay Spring MVC portlet via the\ncommand line using Blade CLI or Maven, use one of the commands with the\nfollowing parameters:\n\n\\begin{verbatim}\nblade create -t spring-mvc-portlet [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.spring.mvc.portlet \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{spring-mvc-portlet}.\nSuppose you want to create a Spring MVC portlet project called\n\\texttt{my-spring-mvc-portlet-project} with a package name of\n\\texttt{com.liferay.docs.springmvcportlet} and a class name of\n\\texttt{MySpringMvcPortlet}. Also, you'd like to create a\nSpring-annotated portlet class named\n\\texttt{MySpringMvcPortletViewController}.\n\n\\begin{verbatim}\nblade create -t spring-mvc-portlet -p com.liferay.docs.springmvcportlet -c MySpringMvcPortlet my-spring-mvc-portlet-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.spring.mvc.portlet \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-spring-mvc-portlet-project \\\n    -Dpackage=com.liferay.docs.springmvcportlet \\\n    -Dversion=1.0 \\\n    -DclassName=MySpringMvcPortlet \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure looks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-spring-mvc-portlet-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/springmvcportlet/portlet}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{MySpringMvcPortletViewController}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{content}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Language.properties}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{webapp}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{css}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{main.scss}\n          \\end{itemize}\n        \\item\n          \\texttt{WEB-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{jsp}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{init.jsp}\n            \\item\n              \\texttt{view.jsp}\n            \\end{itemize}\n          \\item\n            \\texttt{spring-context}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{portlet}\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                \\texttt{my-spring-mvc-portlet-project.xml}\n              \\end{itemize}\n            \\item\n              \\texttt{portlet-application-context.xml}\n            \\end{itemize}\n          \\item\n            \\texttt{tld}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{liferay-portlet.tld}\n            \\item\n              \\texttt{liferay-portlet-ext.tld}\n            \\item\n              \\texttt{liferay-security.tld}\n            \\item\n              \\texttt{liferay-theme.tld}\n            \\item\n              \\texttt{liferay-ui.tld}\n            \\item\n              \\texttt{liferay-util.tld}\n            \\end{itemize}\n          \\item\n            \\texttt{liferay-display.xml}\n          \\item\n            \\texttt{liferay-plugin-package.properties}\n          \\item\n            \\texttt{liferay-portlet.xml}\n          \\item\n            \\texttt{portlet.xml}\n          \\item\n            \\texttt{web.xml}\n          \\end{itemize}\n        \\item\n          \\texttt{icon.png}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated WAR is a working application and is deployable to a\nLiferay DXP instance. To build upon the generated app, modify the\nproject by adding logic and additional files to the folders outlined\nabove. You can visit the\n\\href{/docs/7-1/reference/-/knowledge_base/r/spring-mvc-portlet}{springmvc-portlet}\nsample project for a more expanded sample of a Spring MVC portlet.\n\n\\chapter{Template Context Contributor\nTemplate}\\label{template-context-contributor-template}\n\nIn this article, you'll learn how to create a Liferay template context\ncontributor as a Liferay module. To create a template context\ncontributor via the command line using Blade CLI or Maven, use one of\nthe commands with the following parameters:\n\n\\begin{verbatim}\nblade create -t template-context-contributor [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.template.context.contributor \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is\n\\texttt{template-context-contributor}. Suppose you want to create a\ntemplate context contributor project called\n\\texttt{my-template-context-contributor} with a package name of\n\\texttt{com.liferay.docs.theme.contributor} and a class name of\n\\texttt{SampleTemplateContextContributor}. You could run the following\ncommand to accomplish this:\n\n\\begin{verbatim}\nblade create -t template-context-contributor -p com.liferay.docs -c Sample my-template-context-contributor\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.template.context.contributor \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-template-context-contributor \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's directory\nstructure would look like this\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-template-context-contributor}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/context/contributor}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{SampleTemplateContextContributor.java}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is functional and is deployable to a Liferay DXP\ninstance. To build upon the generated app, modify the project by adding\nlogic and additional files to the folders outlined above. You can visit\nthe\n\\href{https://github.com/liferay/liferay-blade-samples/tree/master/gradle/themes/template-context-contributor}{template-context-contributor}\nsample project for a more expanded sample of a template context\ncontributor. Likewise, see the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/injecting-additional-context-variables-and-functionality-into-your-theme-templates}{Context\nContributors} tutorial for instructions on customizing a template\ncontext contributor project.\n\n\\chapter{Theme Contributor Template}\\label{theme-contributor-template}\n\nIn this article, you'll learn how to create a Liferay theme contributor\nas a Liferay module. To create a theme contributor via the command line\nusing Blade CLI or Maven, use one of the commands with the following\nparameters:\n\n\\begin{verbatim}\nblade create -t theme-contributor [--contributorType contributorType] [-p packageName] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.theme.contributor \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DcontributorType=[contributorType] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{theme-contributor}.\nSuppose you want to create a theme contributor project called\n\\texttt{my-theme-contributor} with a package name of\n\\texttt{com.liferay.docs.theme.contributor} and a contributor type of\n\\texttt{my-contributor}. You could run the following command to\naccomplish this:\n\n\\begin{verbatim}\nblade create -t theme-contributor --contributor-type my-contributor -p com.liferay.docs.theme.contributor my-theme-contributor\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.theme.contributor \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-theme-contributor \\\n    -Dpackage=com.liferay.docs.theme.contributor \\\n    -Dversion=1.0 \\\n    -DcontributorType=my-contributor \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's folder structure\nwould look like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-theme-contributor}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/theme/contributor}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{META-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{resources}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{css}\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                \\texttt{my-contributor}\n\n                \\begin{itemize}\n                \\tightlist\n                \\item\n                  \\texttt{\\_body.scss}\n                \\item\n                  \\texttt{\\_control\\_menu.scss}\n                \\item\n                  \\texttt{\\_product\\_menu.scss}\n                \\item\n                  \\texttt{\\_simulation\\_panel.scss}\n                \\end{itemize}\n              \\item\n                \\texttt{my-contributor.scss}\n              \\end{itemize}\n            \\item\n              \\texttt{js}\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                \\texttt{my-contributor.js}\n              \\end{itemize}\n            \\end{itemize}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated module is functional and is deployable to a Liferay DXP\ninstance. To build upon the generated app, modify the project by adding\nlogic and additional files to the folders outlined above. You can visit\nthe\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-contributor}{Blade\nTheme Contributor} sample project for a more expanded sample of a theme\ncontributor. Likewise, see the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site}{Theme\nContributors} tutorial for instructions on customizing a theme\ncontributor project.\n\n\\chapter{Theme Template}\\label{theme-template}\n\nIn this article, you'll learn how to create a Liferay theme as a WAR\nproject. To create a Liferay theme via the command line using Blade CLI\nor Maven, use one of the commands with the following parameters:\n\n\\begin{verbatim}\nblade create -t theme projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.theme \\\n    -DartifactId=[projectName] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{theme}. Suppose you\nwant to create a theme project called \\texttt{my-theme-project} as a WAR\nfile. You could run the following command to accomplish this:\n\n\\begin{verbatim}\nblade create -t theme my-theme-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.theme \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-theme-project \\\n    -Dversion=1.0 \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's folder structure\nlooks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-theme-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{resources-importer}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{sitemap.json}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{webapp}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{css}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{\\_custom.scss}\n          \\end{itemize}\n        \\item\n          \\texttt{WEB-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{liferay-plugin-package.properties}\n          \\item\n            \\texttt{web.xml}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated theme is functional and is deployable to a Liferay DXP\ninstance. To build upon the generated project, modify the project by\nadding logic and additional files to the folders outlined above. You can\nvisit the\n\\href{/docs/7-1/reference/-/knowledge_base/r/theme}{simple-theme}\nproject for a more expanded sample of a theme. Likewise, see the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{Themes}\nsection for more information on creating themes.\n\n\\chapter{WAR Core Ext}\\label{war-core-ext}\n\nIn this article, you'll learn how to create a Liferay WAR core Ext\nproject. To create a WAR core Ext project via the command line using\nBlade CLI or Maven, use one of the commands with the following\nparameters:\n\n\\begin{verbatim}\nblade create -t war-core-ext projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.war.core.ext \\\n    -DartifactId=[projectName] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{war-core-ext}. Suppose\nyou want to create a WAR core Ext project called\n\\texttt{my-war-core-ext-project}. You could run the following command to\naccomplish this:\n\n\\begin{verbatim}\nblade create -t war-core-ext my-war-core-ext-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.war.core-ext \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-war-core-ext-project \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's folder structure\nlooks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-war-core-ext-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{extImpl}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{META-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{ext-model-hints.xml}\n          \\item\n            \\texttt{ext-spring.xml}\n          \\item\n            \\texttt{portal-log4j-ext.xml}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{extKernel}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{META-INF}\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{extUtilBridges}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{META-INF}\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{extUtilJava}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{META-INF}\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{extUtilTaglib}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{META-INF}\n        \\end{itemize}\n      \\end{itemize}\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{webapp}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{WEB-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{ext-web}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{docroot}\n\n              \\begin{itemize}\n              \\tightlist\n              \\item\n                \\texttt{WEB-INF}\n\n                \\begin{itemize}\n                \\tightlist\n                \\item\n                  \\texttt{liferay-portlet-ext.xml}\n                \\item\n                  \\texttt{portlet-ext.xml}\n                \\item\n                  \\texttt{struts-config-ext.xml}\n                \\item\n                  \\texttt{tiles-defs-ext.xml}\n                \\item\n                  \\texttt{web.xml}\n                \\end{itemize}\n              \\end{itemize}\n            \\end{itemize}\n          \\item\n            \\texttt{liferay-plugin-package.properties}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you generate a WAR Ext project using Gradle outside of\nLiferay Workspace, you must set the \\texttt{app.server.parent.dir}\nproperty in the project's \\texttt{gradle.properties}. The app server\nlocation is required for this project to compile.\n\n\\noindent\\hrulefill\n\nThe generated WAR Ext project is functional and is deployable to a\nLiferay DXP instance. To build upon the generated project, modify the\nproject by adding logic and additional files to the folders outlined\nabove. Deploying WAR Ext projects is only supported for limited use\ncases; it is recommended to leverage provided extension points offered\nin Liferay DXP. You can visit the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customization-with-ext}{Customization\nwith Ext} section for info on how to do this.\n\n\\chapter{WAR Hook Template}\\label{war-hook-template}\n\nIn this article, you'll learn how to create a Liferay WAR hook project.\nTo create a Liferay WAR hook via the command line using Blade CLI or\nMaven, use one of the commands with the following parameters:\n\n\\begin{verbatim}\nblade create -t war-hook [-p packageName] [-c className] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.war.hook \\\n    -DartifactId=[projectName]\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{war-hook}. Suppose you\nwant to create a WAR hook project called \\texttt{my-war-hook-project}\nwith a package name of \\texttt{com.liferay.docs} and a class name of\n\\texttt{MyWarHook}. You could run the following command to accomplish\nthis:\n\n\\begin{verbatim}\nblade create -t war-hook -p com.liferay.docs -c MyWarHook my-war-hook-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.war.hook \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-war-hook-project \\\n    -Dpackage=com.liferay.docs \\\n    -DclassName=MyWarHook \\\n    -Dversion=1.0 \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's folder structure\nlooks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-war-hook-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{MyWarHookLoginPostAction}\n          \\item\n            \\texttt{MyWarHookStartupAction}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{portal.properties}\n        \\end{itemize}\n      \\item\n        \\texttt{webapp}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{WEB-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{liferay-hook.xml}\n          \\item\n            \\texttt{liferay-plugin-package.properties}\n          \\item\n            \\texttt{web.xml}\n          \\end{itemize}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated WAR hook is functional and is deployable to a Liferay DXP\ninstance. To build upon the generated project, modify the project by\nadding logic and additional files to the folders outlined above.\nDeploying WAR hooks is supported for 7.0, however, it is recommended to\noptimize your WAR hooks to fragments or other applicable module\nprojects. You can visit the\n\\href{/docs/7-2/customization/-/knowledge_base/c/liferay-customization}{Liferay\nCustomization} section for info on how to do this for many project\ntypes. See the\n\\href{/docs/6-2/tutorials/-/knowledge_base/t/customizing-liferay-portal}{Customizing\nLiferay Portal} section for more information on WAR hooks.\n\n\\chapter{WAR MVC Portlet Template}\\label{war-mvc-portlet-template}\n\nIn this article, you'll learn how to create a Liferay MVC portlet\nproject as a WAR file. To create a Liferay MVC portlet project as a WAR\nvia the command line using Blade CLI or Maven, use one of the commands\nwith the following parameters:\n\n\\begin{verbatim}\nblade create -t war-mvc-portlet [-p packageName] projectName\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.war.mvc.portlet \\\n    -DartifactId=[projectName]\n    -Dpackage=[packageName] \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nYou can also insert the \\texttt{-b\\ maven} parameter in the Blade\ncommand to generate a Maven project using Blade CLI.\n\nThe template for this kind of project is \\texttt{war-mvc-portlet}.\nSuppose you want to create a WAR MVC portlet project called\n\\texttt{my-war-mvc-portlet-project} with a package name of\n\\texttt{com.liferay.docs.war.mvc} and a class name of\n\\texttt{MyWarMvcPortlet}. You could run the following command to\naccomplish this:\n\n\\begin{verbatim}\nblade create -t war-mvc-portlet -p com.liferay.docs.war.mvc my-war-mvc-portlet-project\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.war.mvc.portlet \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-war-mvc-portlet-project \\\n    -Dpackage=com.liferay.docs.war.mvc \\\n    -Dversion=1.0 \\\n    -DliferayVersion=7.2\n\\end{verbatim}\n\nAfter running the Blade command above, your project's folder structure\nlooks like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-war-mvc-portlet-project}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{gradle}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{wrapper}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{gradle-wrapper.jar}\n      \\item\n        \\texttt{gradle-wrapper.properties}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{src}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{main}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{java}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/docs/war/mvc}\n        \\end{itemize}\n      \\item\n        \\texttt{resources}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{content}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{Language.properties}\n          \\end{itemize}\n        \\end{itemize}\n      \\item\n        \\texttt{webapp}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{css}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{main.scss}\n          \\end{itemize}\n        \\item\n          \\texttt{WEB-INF}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{tld}\n\n            \\begin{itemize}\n            \\tightlist\n            \\item\n              \\texttt{liferay-portlet.tld}\n            \\item\n              \\texttt{liferay-portlet-ext.tld}\n            \\item\n              \\texttt{liferay-security.tld}\n            \\item\n              \\texttt{liferay-theme.tld}\n            \\item\n              \\texttt{liferay-ui.tld}\n            \\item\n              \\texttt{liferay-util.tld}\n            \\end{itemize}\n          \\item\n            \\texttt{liferay-display.xml}\n          \\item\n            \\texttt{liferay-plugin-package.properties}\n          \\item\n            \\texttt{liferay-portlet.xml}\n          \\item\n            \\texttt{portlet.xml}\n          \\item\n            \\texttt{web.xml}\n          \\end{itemize}\n        \\item\n          \\texttt{init.jsp}\n        \\item\n          \\texttt{view.jsp}\n        \\end{itemize}\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{build.gradle}\n  \\item\n    \\texttt{gradlew}\n  \\end{itemize}\n\\end{itemize}\n\nThe Maven-generated project includes a \\texttt{pom.xml} file and does\nnot include the Gradle-specific files, but otherwise, appears exactly\nthe same.\n\nThe generated WAR MVC portlet is functional and is deployable to a\nLiferay DXP instance. To build upon the generated project, modify the\nproject by adding logic and additional files to the folders outlined\nabove. Deploying WAR MVC portlets is supported for 7.0, however, it is\nrecommended to optimize your WAR portlet to a module project, if\npossible. You can visit the\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver}{From\nLiferay Portal 6 to 7} section for info on how to do this.\n\n\\chapter{Sample Projects}\\label{sample-projects}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} This section of articles does not provide documentation\nfor \\emph{all} sample projects residing in the\n\\texttt{liferay-blade-samples} repo. The documentation for these samples\nis in progress and will grow over time.\n\n\\noindent\\hrulefill\n\nLiferay provides sample projects that target different integration\npoints in Liferay DXP. These projects reside in the\n\\href{https://github.com/liferay/liferay-blade-samples}{liferay-blade-samples}\nGithub repository and can be easily copy/pasted to your local\nenvironment. You can also generated them using\n\\href{/docs/7-2/reference/-/knowledge_base/r/generating-project-samples-with-blade-cli}{Blade\nCLI}.\n\nThe sample projects are grouped into three different parent folders\nbased on the build tools used to generate them:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{gradle}\n\\item\n  \\texttt{liferay-workspace}\n\\item\n  \\texttt{maven}\n\\end{itemize}\n\nThe provided sample projects are organized by their development\ntoolchains to cater to a variety of developers. Each folder offers the\nsame set of sample Liferay projects. Their only difference is that the\nbuild files are specific to their toolchain. For example, the\n\\texttt{gradle} folder contains projects using standard OSS Gradle\nplugins that can be added to any Gradle composite build. The same\nconcept also applies to the \\texttt{liferay-workspace} and\n\\texttt{maven} projects.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Liferay Workspace folder stores WAR-type samples in a\nseparate folder named\n\\href{https://github.com/liferay/liferay-blade-samples/tree/7.1/liferay-workspace/wars}{wars}.\nThe Gradle and Maven tool folders mix WAR samples with the other sample\ntypes (apps, extensions, etc.).\n\n\\noindent\\hrulefill\n\nThe \\texttt{gradle} folder also uses the Liferay Gradle plugin (e.g.,\n\\texttt{com.liferay.plugin}) which encompasses additional functionality\nfor various types of Liferay projects. The Liferay Gradle plugin is\nrecommended for Gradle users developing for Liferay DXP.\n\nSome samples also come configured with logging to help you fully\nunderstand what the sample is accomplishing behind the scenes. For\nexample, OSGi module logging is implemented for several samples (e.g.,\n\\href{https://github.com/liferay/liferay-blade-samples/tree/7.1/gradle/apps/action-command-portlet}{action-command-portlet},\n\\href{/docs/7-2/reference/-/knowledge_base/r/document-action}{document-action},\n\\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-application-using-external-database-via-jdbc}{service-builder/jdbc},\netc.), which lets OSGi modules supply their own logging configuration\ndefaults without external configuration. See the\n\\href{/docs/7-2/appdev/-/knowledge_base/a/adjusting-module-logging}{Adjusting\nModule Logging} article for more information.\n\nFor a list of sample template projects available, visit the\n\\href{https://github.com/liferay/liferay-blade-samples\\#liferay-extension-points-and-template-projects}{Liferay\nextension points} sub-section in the Liferay Blade Samples repository.\nThis list is not comprehensive. A subset of missing extension point\nsamples can be found in the\n\\href{https://github.com/liferay/liferay-blade-samples\\#liferay-extension-points-without-template-projects}{Liferay\nextension points without template projects} sub-section. Visit the\nrepo's\n\\href{https://github.com/liferay/liferay-blade-samples\\#contribution-guidelines}{Contribution\nGuidelines} section for details on contributing to this repository.\n\n\\chapter{Apps}\\label{apps}\n\nThis section focuses on Liferay sample applications. You can view these\nsample apps by visiting the \\texttt{apps} folder corresponding to your\npreferred build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps}{Gradle\n  sample apps}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps}{Liferay\n  Workspace sample apps}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/apps}{Maven\n  sample apps}\n\\end{itemize}\n\nVisit a particular sample page to learn more!\n\n\\chapter{Service Builder Samples}\\label{service-builder-samples}\n\nThis section focuses on Liferay Service Builder sample projects built\nwith various build tools. You can view these samples by visiting the\n\\texttt{apps/service-builder} folder corresponding to your preferred\nbuild tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/service-builder}{Gradle\n  Service Builder sample apps}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps/service-builder}{Liferay\n  Service Builder Workspace sample apps}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/apps/service-builder}{Maven\n  Service Builder sample apps}\n\\end{itemize}\n\nThe following Service Builder samples are documented:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-application-demonstrating-actionable-dynamic-query}{Service\n  Builder application demonstrating Actionable Dynamic Query}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-application-using-external-database-via-jdbc}{Service\n  Builder application with JDBC connection}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-application-using-external-database-via-jndi}{Service\n  Builder application with JNDI connection}\n\\end{itemize}\n\nVisit a particular sample page to learn more!\n\n\\chapter{Service Builder Application Demonstrating Actionable Dynamic\nQuery}\\label{service-builder-application-demonstrating-actionable-dynamic-query}\n\nThis sample is similar to the\n\\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/service-builder/basic}{\\texttt{basic}\nService Builder sample}, which lets you perform CRUD (create, read,\nupdate, delete) operations on service builder entities. The distinctive\nfeature of the Service Builder Actionable Dynamic Query (ADQ) sample is\nthat it also lets you perform a mass update on all existing service\nbuilder entities.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adq-sample.png}\n\\caption{This sample provides options to add entities and perform a mass\nupdate.}\n\\end{figure}\n\nTo see the ADQ Service Builder sample in action, complete the following\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the sample to a page by navigating to \\emph{Add}\n  (\\includegraphics{./images/icon-add.png}) → \\emph{Widgets} →\n  \\emph{Sample} and dragging it to the page.\n\\item\n  Select the app's \\emph{Add} button and add an entity. Do this several\n  times to create multiple entities.\n\\item\n  Click the \\emph{Mass Update} button and click \\emph{Save} to invoke\n  the update.\n\n  After invoking the update, each entity's \\texttt{field3} value (whose\n  value is less than 100) is incremented.\n\\end{enumerate}\n\nYou've leveraged the actionable dynamic query API in your sample!\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight}\n\nThis sample demonstrates Liferay DXP's actionable dynamic query API.\nSpecifically, it demonstrates how to create an ADQ, add criteria to an\nADQ, specify an action for the ADQ, and execute the ADQ.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component}\n\nAn action request is sent to the \\texttt{JSPPortlet} with a \\texttt{cmd}\nrequest parameter. When the \\texttt{JSPPortlet}'s \\texttt{processAction}\nmethod processes the request, the value of the \\texttt{cmd} parameter is\nparsed and then the portlet's \\texttt{massUpdate} method is invoked. The\n\\texttt{massUpdate} method, in turn, invokes the \\texttt{massUpdate}\nmethod defined in the \\texttt{adq-service} module's\n\\texttt{BarLocalServiceImpl}. This is where the sample leverages the\nactionable dynamic query API:\n\n\\begin{verbatim}\npublic void massUpdate() {\n    ActionableDynamicQuery adq = getActionableDynamicQuery();\n\n    adq.setAddCriteriaMethod(\n        new ActionableDynamicQuery.AddCriteriaMethod() {\n\n            @Override\n            public void addCriteria(DynamicQuery dynamicQuery) {\n                dynamicQuery.add(RestrictionsFactoryUtil.lt(\"field3\", 100));\n            }\n\n        });\n\n    adq.setPerformActionMethod(\n        new ActionableDynamicQuery.PerformActionMethod<Bar>() {\n\n            @Override\n            public void performAction(Bar bar) {\n                int field3 = bar.getField3();\n\n                field3++;\n                bar.setField3(field3);\n\n                updateBar(bar);\n            }\n\n        });\n\n    try {\n        adq.performActions();\n    }\n    catch (Exception e) {\n        e.printStackTrace();\n    }\n}\n\\end{verbatim}\n\nFor more information on the actionable dynamic query API, visit its\ndedicated\n\\href{/docs/7-0/tutorials/-/knowledge_base/t/dynamic-query\\#actionable-dynamic-queries}{tutorial}.\n\n\\chapter{Service Builder Application Using External Database via\nJDBC}\\label{service-builder-application-using-external-database-via-jdbc}\n\nThis sample demonstrates how to connect a Liferay Service Builder\napplication to an external database via a JDBC connection. Here, an\nexternal database means any database other than Liferay DXP's database.\nFor this sample to work correctly, you must prepare such an external\ndatabase and configure Liferay DXP to use it. Follow the steps below to\nmake the required preparations before deploying the application.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create the external database to which your Service Builder application\n  will connect. For example, create a MariaDB database called\n  \\texttt{external}. Add a table to this database called\n  \\texttt{country} with a \\texttt{BIGINT} column called \\texttt{Id} and\n  a \\texttt{VARCHAR(255)} column called \\texttt{Name}. Add at least one\n  record to this table. Here are the MariaDB commands to accomplish\n  this:\n\n\\begin{verbatim}\ncreate database external character set utf8;\n\nuse external;\n\ncreate table country(id bigint not null primary key, name varchar(255));\n\ninsert into country(id, name) values(1, 'Australia');\n\\end{verbatim}\n\n  Make sure that your database commands were successful: Running\n  \\texttt{select\\ *\\ from\\ country;} should return the record you added.\n\\item\n  Create a \\texttt{portal-ext.properties} file in your Liferay DXP\n  instance's \\texttt{{[}LIFERAY\\_HOME{]}} folder (this folder should be\n  marked by the presence of a \\texttt{.liferay-home} file). In your\n  \\texttt{portal-ext.properties} file, define the details of your JDBC\n  data source connection:\n\n\\begin{verbatim}\njdbc.ext.driverClassName=org.mariadb.jdbc.Driver\njdbc.ext.password=userpassword\njdbc.ext.url=jdbc:mariadb://localhost/external?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\njdbc.ext.username=yourusername\n\\end{verbatim}\n\n  Note that Liferay DXP's primary data source is specified by the\n  \\texttt{jdbc.default} prefix. These details are often specified in a\n  \\texttt{portal-setup-wizard.properties} file. Here, we've chosen to\n  use the \\texttt{jdbc.ext} prefix for our alternate data source.\n\\item\n  Create a\n  \\texttt{com.liferay.blade.samples.jdbcservicebuilder.service-log4j-ext.xml}\n  in your Liferay instance's \\texttt{{[}LIFERAY\\_HOME{]}/osgi/log4}\n  folder. Create this folder if it doesn't yet exist. Add this content\n  to the XML file that you created:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">\n\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\">\n    <category name=\"com.liferay.blade.samples.jdbcservicebuilder.service.impl\">\n        <priority value=\"INFO\" />\n    </category>\n</log4j:configuration>\n\\end{verbatim}\n\n  This XML file defines the log level for the classes in the\n  \\texttt{com.liferay.blade.samples.jdbcservicebuilder.service.impl}\n  package. The\n  \\texttt{com.liferay.blade.samples.jdbcservicebuilder.service.impl.CountryLocalServiceImpl}\n  is the class that will produce log messages when the sample portlet is\n  viewed.\n\\end{enumerate}\n\nNow your sample is ready for deployment! Make sure to build and deploy\neach of the three modules that comprise the sample application:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{jdbc-api}\n\\item\n  \\texttt{jdbc-service}\n\\item\n  \\texttt{jdbc-web}\n\\end{itemize}\n\nAfter these modules have been deployed, add the \\texttt{-web} portlet to\na Liferay DXP page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/jdbc-sb-sample.png}\n\\caption{This sample prints out the values previously inputted into the\ndatabase.}\n\\end{figure}\n\nA sample table is printed in the portlet's view, representing the info\ninputted into the database.\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-1}\n\nThe sample configures the data source using Spring Beans and\ndemonstrates two ways to access data from an external database defined\nby a JDBC connection:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  extract data directly from the raw data source by explicitly\n  specifying a SQL query.\n\\item\n  read data using the helper methods that Service Builder generates in\n  your application's persistence layer.\n\\end{itemize}\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-1}\n\nOnce you've added the \\texttt{-web} portlet to a page, the\n\\texttt{CountryLocalService.useJDBC} method is invoked. This method\naccesses the database defined by the JDBC connection you specified and\nlogs information about the rows in the \\texttt{country} table to Liferay\nDXP's log.\n\n\\section{Configuring the Data Source}\\label{configuring-the-data-source}\n\nThe \\texttt{-service} module's\n\\texttt{src/main/resources/META-INF/spring/ext-spring.xml} file\nconfigures the external data source connection and applies the alias\n\\texttt{extDataSource} to the data source. The \\texttt{service.xml} file\n\\texttt{entity} element specifies the data source via the attribute\nassignment \\texttt{data-source=\"extDataSource\"}. The\n\\texttt{ext-spring.xml} and \\texttt{service.xml} files demonstrate the\nconfiguration steps explained in\n\\href{/docs/7-2/appdev/-/knowledge_base/a/connecting-the-data-source-using-spring-beans}{Connecting\nthe Data Source Using Spring Beans}.\n\n\\section{Accessing Data}\\label{accessing-data}\n\nThe first way of accessing data from the external database is to extract\nit directly from the raw data source by explicitly specifying a SQL\nquery. This technique is demonstrated by the\n\\texttt{CountryLocalServiceImpl.useJDBC} method. That method obtains the\nSpring-defined data source that's injected into the\n\\texttt{countryPersistence} bean, opens a new connection, and reads data\nfrom the data source. This is the technique used by the sample\napplication to write the data to Liferay DXP's log.\n\nThe second way of accessing data from the external database is to read\ndata using the helper methods that Service Builder generates in your\napplication's persistence layer. This technique is demonstrated by the\n\\texttt{UseJDBC.getCountries} method which first obtains an instance of\nthe \\texttt{CountryLocalService} OSGi service and then invokes\n\\texttt{countryLocalService.getCountries}. The\n\\texttt{countryLocalService.getCountries} and\n\\texttt{countryLocalService.getCountriesCount} methods are two examples\nof the persistence layer helper methods that Service Builder generates.\nThis is the technique used by the sample application to actually display\nthe data. The portlet's \\texttt{view.jsp} uses the\n\\texttt{\\textless{}search-container\\textgreater{}} JSP tag to display a\nlist of results. The results are obtained by the\n\\texttt{UseJDBC.getCountries} method mentioned above.\n\n\\chapter{Service Builder Application Using External Database via\nJNDI}\\label{service-builder-application-using-external-database-via-jndi}\n\nThe \\texttt{apps/service-builder/jndi} sample demonstrates how to\nconnect a Liferay Service Builder application to an external database\nvia a JNDI connection configured on the application server. Here, an\nexternal database means any database other than Liferay DXP's database.\nFor this sample to work correctly, you must prepare such an external\ndatabase and configure your application server to use it.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Connecting to an external data source using JNDI is\nbroken in Portal CE 7.2 GA1 and GA2, and in DXP 7.2 releases prior to\nFP5/SP2. See\n\\href{https://issues.liferay.com/browse/LPS-107733}{LPS-107733} for\ndetails.\n\n\\noindent\\hrulefill\n\nFollow the steps below to make the required preparations before\ndeploying the application.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create an external database based on sample application's\n  \\texttt{service.xml}.\n\n  \\texttt{service.xml}:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder package-path=\"com.liferay.blade.samples.jndiservicebuilder\">\n    <namespace>REGION</namespace>\n    <!--<entity data-source=\"sampleDataSource\" local-service=\"true\" name=\"Foo\" remote-service=\"false\" session-factory=\"sampleSessionFactory\" table=\"bar\" tx-manager=\"sampleTransactionManager uuid=\"true\"\">-->\n    <entity\n        data-source=\"extDataSource\"\n        local-service=\"true\"\n        name=\"Region\"\n        remote-service=\"false\"\n        table=\"region\"\n        uuid=\"false\"\n    >\n        <column db-name=\"id\" name=\"regionId\" primary=\"true\" type=\"long\" />\n        <column db-name=\"name\" name=\"regionName\" type=\"String\" />\n    </entity>\n</service-builder>\n\\end{verbatim}\n\n  The entity's data source name \\texttt{extDataSource} is arbitrary but\n  must be specified in the data source configuration in the application\n  server (next step).\n\n  Here are MariaDB commands to create the database:\n\n\\begin{verbatim}\ncreate database external character set utf8;\n\nuse external;\n\ncreate table region(id bigint not null primary key, name varchar(255));\n\ninsert into region(id, name) values(1, 'Tasmania');\n\\end{verbatim}\n\n  The database name is arbitrary; the data source configuration in your\n  application server (next step), however, must specify this same\n  database. The database table called \\texttt{region} represents the\n  service entity. The table has a \\texttt{BIGINT} column called\n  \\texttt{Id} and a \\texttt{VARCHAR(255)} column called \\texttt{Name}.\n\n  Add at least one record to this table. Running\n  \\texttt{select\\ *\\ from\\ region;} should return the record you added.\n\\item\n  In your application server configuration, define a JNDI connection to\n  your database and map it to the \\texttt{data-source} name (i.e.,\n  \\texttt{extDataSource}) that the sample \\texttt{service.xml} entities\n  specify.\n\n  For example, if Tomcat is your application server, open your\n  \\texttt{{[}LIFERAY\\_HOME{]}/tomcat-version/conf/server.xml} file and\n  add a \\texttt{Resource} element like this one inside of the\n  \\texttt{\\textless{}GlobalNamingResources\\textgreater{}} element:\n\n\\begin{verbatim}\n<Resource\n    name=\"jdbc/externalDataSource\"\n    auth=\"Container\"\n    type=\"javax.sql.DataSource\"\n    factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n    driverClassName=\"org.mariadb.jdbc.Driver\"\n    url=\"jdbc:mariadb://localhost/external?useUnicode=true&amp;characterEncoding=UTF-8&amp;useFastDateParsing=false\"\n    username=\"[place user name here]\"\n    password=\"[place password here]\"\n    maxActive=\"20\"\n    maxIdle=\"5\"\n    maxWait=\"10000\"\n/>\n\\end{verbatim}\n\n  Replace the user name and password values and see the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/database-templates}{Database\n  Templates} for the URL parameters to use for your database.\n\\item\n  If you are using Tomcat, open your\n  \\texttt{{[}LIFERAY\\_HOME{]}/tomcat-version/conf/context.xml} file and\n  add this resource link element inside of the\n  \\texttt{\\textless{}Context\\textgreater{}} element:\n\n\\begin{verbatim}\n<ResourceLink name=\"jdbc/externalDataSource\" global=\"jdbc/externalDataSource\" type=\"javax.sql.DataSource\"/>\n\\end{verbatim}\n\n  Now your data source is defined at Tomcat's scope.\n\\item\n  Create a\n  \\texttt{com.liferay.blade.samples.jndiservicebuilder.service-log4j-ext.xml}\n  in your Liferay DXP instance's \\texttt{{[}LIFERAY\\_HOME{]}/osgi/log4}\n  folder. Create this folder if it doesn't yet exist. Add this content\n  to the XML file that you created:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">\n\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\">\n    <category name=\"com.liferay.blade.samples.jndiservicebuilder.service.impl\">\n        <priority value=\"INFO\" />\n    </category>\n</log4j:configuration>\n\\end{verbatim}\n\n  This XML file defines the log level for the classes in the\n  \\texttt{com.liferay.blade.samples.jndiservicebuilder.service.impl}\n  package. The\n  \\texttt{com.liferay.blade.samples.jndiservicebuilder.service.impl.RegionLocalServiceImpl}\n  is the class that will produce log messages when the sample portlet is\n  viewed.\n\\end{enumerate}\n\nNow your sample is ready for deployment! Make sure to build and deploy\neach of the three modules that comprise the sample application:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{jndi-api}\n\\item\n  \\texttt{jndi-service}\n\\item\n  \\texttt{jndi-web}\n\\end{itemize}\n\nAfter these modules have been deployed, add the \\texttt{jndi-web}\nportlet to a Liferay DXP page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/jndi-sb-sample.png}\n\\caption{This sample prints out the values previously inputted into the\ndatabase.}\n\\end{figure}\n\nA sample table is printed in the portlet's view, representing the info\ninputted into the database.\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-2}\n\nThis sample demonstrates two ways to access data from an external\ndatabase defined by a JNDI connection:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  extract data directly from the raw data source by explicitly\n  specifying a SQL query.\n\\item\n  read data using the helper methods that Service Builder generates in\n  your application's persistence layer.\n\\end{itemize}\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-2}\n\nOnce you've added the \\texttt{jndi-web} portlet to a page, the\n\\texttt{RegionLocalServiceUtil.useJNDI} method is invoked. This method\naccesses the database defined by the JNDI connection you specified and\nlogs information about the rows in the \\texttt{region} table to Liferay\nDXP's log.\n\nThe first way of accessing data from the external database is to extract\ndata directly from the raw data source by explicitly specifying a SQL\nquery. This technique is demonstrated by the\n\\texttt{RegionLocalServiceImpl.useJNDI} method. That method obtains the\nSpring-defined data source that's injected into the\n\\texttt{regionPersistence} bean, opens a new connection, and reads data\nfrom the data source. This is the technique used by the sample\napplication to write the data to Liferay DXP's log.\n\nThe second way of accessing data from the external database is to read\ndata using the helper methods that Service Builder generates in your\napplication's persistence layer. This technique is demonstrated by the\n\\texttt{UseJNDI.getRegions} method which first obtains an instance of\nthe \\texttt{RegionLocalService} OSGi service and then invokes\n\\texttt{regionLocalService.getRegions}. The\n\\texttt{regionLocalService.getRegions} and\n\\texttt{regionLocalService.getRegionsCount} methods are two examples of\nthe persistence layer helper methods that Service Builder generates.\nThis is the technique used by the sample application to actually display\nthe data. The portlet's \\texttt{view.jsp} uses the\n\\texttt{\\textless{}search-container\\textgreater{}} JSP tag to display a\nlist of results. The results are obtained by the\n\\texttt{UseJNDI.getRegions} method mentioned above.\n\n\\section{Additional Information}\\label{additional-information}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/connecting-service-builder-to-an-external-database}{Connecting\n  to an External Data Source}\n\\end{itemize}\n\n\\chapter{Workflow Samples}\\label{workflow-samples}\n\nThis section focuses on Liferay's Workflow Framework sample projects\nbuilt with various build tools. You can view these samples by visiting\nthe \\texttt{apps/workflow} folder corresponding to your preferred build\ntool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/workflow}{Gradle\n  Workflow sample apps}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps/workflow}{Liferay\n  Workspace Workflow sample apps}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/apps/workflow}{Maven\n  Workflow sample apps}\n\\end{itemize}\n\nThe following Workflow samples are documented:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/workflow-application}{Workflow\n  application}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/workflow-application-with-asset-integration}{Workflow\n  application with Asset Integration}\n\\end{itemize}\n\nVisit a particular sample page to learn more!\n\n\\chapter{Workflow Asset Application}\\label{workflow-asset-application}\n\nThis sample demonstrates workflow enabling a model entity that is an\nasset.\n\nTo see the Workflow sample in action, complete the following steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the sample widget to a page by navigating to \\emph{Add}\n  (\\includegraphics{./images/icon-add.png}) → \\emph{Widgets} →\n  \\emph{Sample} → \\emph{Workflow Asset} and dragging it to the page.\n\\item\n  Go to \\emph{Control Panel} → \\emph{Workflow} → \\emph{Process Builder}\n  → \\emph{Configuration} and assign a workflow to the Qux entity.\n\\item\n  Select the app's \\emph{Add} button and add an entity. Do this several\n  times to create multiple entities.\n\\item\n  Go to \\emph{User} → \\emph{My Workflow Tasks} → \\emph{Assigned to My\n  Roles} and assigned the task to me and Approve the Task.\n\\end{enumerate}\n\nNow you've taken the entity and successfully run it through a workflow.\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-3}\n\nThis sample demonstrates Liferay DXP's Workflow Handler API.\nSpecifically, it demonstrates how to create a \\texttt{WorkflowHandler}\nfor your custom entity that is\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{asset\nenabled}.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-3}\n\nThe basic implementation of \\texttt{WorkflowHandler} is done via\nextension of the \\texttt{BaseWorkflowHandler} class. This is where the\nsample leverages the basic methods required for the entity's\n\\texttt{WorkflowHandler}.\n\n\\begin{verbatim}\n@Override\npublic String getClassName() {\n  return Qux.class.getName();\n}\n\n@Override\npublic String getTitle(long classPK, Locale locale) {\n  return String.valueOf(classPK);\n}\n\n@Override\npublic String getType(Locale locale) {\n  return ResourceActionsUtil.getModelResource(locale, getClassName());\n}\n\n@Override\npublic Qux updateStatus(\n    int status, Map<String, Serializable> workflowContext)\n  throws PortalException {\n\n  long userId = GetterUtil.getLong(\n    (String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));\n\n  long classPK = GetterUtil.getLong(\n    (String)workflowContext.get(\n      WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n  return _quxLocalService.updateStatus(userId, classPK, status);\n}\n\\end{verbatim}\n\nFor more information on the workflow framework, visit its dedicated\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework}{documentation}.\n\n\\chapter{Workflow Application}\\label{workflow-application}\n\nThe\n\\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/workflow/basic}{\\texttt{basic}}\nsample demonstrates workflow enabling an entity that is not an asset.\n\nTo see the Workflow sample in action, complete the following steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the sample widget to a page by navigating to \\emph{Add}\n  (\\includegraphics{./images/icon-add.png}) → \\emph{Widgets} →\n  \\emph{Sample} → \\emph{Workflow Basic} and dragging it to the page.\n\\item\n  Go to \\emph{Control Panel} → \\emph{Workflow} → \\emph{Process Builder}\n  → \\emph{Configuration} and assign a workflow to the \\texttt{Baz}\n  entity.\n\\item\n  Select the app's \\emph{Add} button and add an entity. Do this several\n  times to create multiple entities.\n\\item\n  Go to \\emph{User} → \\emph{My Workflow Tasks} → \\emph{Assigned to My\n  Roles} and assigned the task to me and Approve the Task.\n\\end{enumerate}\n\nNow you've taken the entity and successfully run it through a workflow.\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-4}\n\nThis sample demonstrates Liferay DXP's Workflow Handler API.\nSpecifically, it demonstrates how to create a \\texttt{WorkflowHandler}\nfor your custom entity.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-4}\n\nThe basic implementation of \\texttt{WorkflowHandler} is done via\nextension of the \\texttt{BaseWorkflowHandler} class. This is where the\nsample leverages the basic methods required for the entity's\n\\texttt{WorkflowHandler}.\n\n\\begin{verbatim}\n@Override\npublic String getClassName() {\n  return Baz.class.getName();\n}\n\n@Override\npublic String getTitle(long classPK, Locale locale) {\n  return String.valueOf(classPK);\n}\n\n@Override\npublic String getType(Locale locale) {\n  return ResourceActionsUtil.getModelResource(locale, getClassName());\n}\n\n@Override\npublic Baz updateStatus(\n    int status, Map<String, Serializable> workflowContext)\n  throws PortalException {\n\n  long userId = GetterUtil.getLong(\n    (String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));\n\n  long classPK = GetterUtil.getLong(\n    (String)workflowContext.get(\n      WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n  return _bazLocalService.updateStatus(userId, classPK, status);\n}\n\\end{verbatim}\n\nFor more information on the workflow framework, visit its dedicated\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework}{documentation}.\n\n\\chapter{Greedy Policy Option\nApplication}\\label{greedy-policy-option-application}\n\nThe Greedy Policy Option sample provides two portlets that can be added\nto a Liferay DXP page: Greedy Portlet and Reluctant Portlet.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/greedy-policy-app.png}\n\\caption{The Greedy Policy Option app provides two portlets that only\nprint text. You'll dive deeper later to discover their interesting\ncapabilities involving services.}\n\\end{figure}\n\nThese two portlets do not provide anything useful out-of-the-box. They\nare, however, very effective at demonstrating the ability to reference\nservices using greedy and reluctant policy options. You'll learn how to\ndo this later.\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-5}\n\nThis sample provides two modules referencing services using greedy and\nreluctant policy options.\n\n\\begin{itemize}\n\\item\n  \\texttt{service-reference}: Provides an OSGi service interface called\n  \\texttt{SomeService}, a default implementation of it, and portlets\n  that refer to service instances. One portlet refers to new higher\n  ranked instances of the service automatically. The other portlet is\n  reluctant to use new higher ranked instances and continues to use its\n  bound service. The reluctant portlet can, however, be configured\n  dynamically to use other service instances.\n\\item\n  \\texttt{higher-ranked-service}: Has a higher ranked\n  \\texttt{SomeService} implementation.\n\\end{itemize}\n\nHere are each module's file structures:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{service-reference/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{configs/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{com.liferay.blade.reluctant.vs.greedy.portlet.portlet.ReluctantPortlet.config}\n      → \\texttt{ReluctantPortlet} configuration file\n    \\end{itemize}\n  \\item\n    \\texttt{src/main/java/com/liferay/blade/reluctant/vs/greedy/portlet/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{api/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{SomeService.java} → Service interface\n      \\end{itemize}\n    \\item\n      \\texttt{constants/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{ReluctantPortletVsGreedyPortletKeys.java} → Portlet\n        constants\n      \\end{itemize}\n    \\item\n      \\texttt{portlet/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{DefaultSomeService.java} → Zero ranked service\n        implementation\n      \\item\n        \\texttt{GreedyPortlet.java} → Refers to \\texttt{SomeService}\n        using a greedy service policy option\n      \\item\n        \\texttt{ReluctantPortletPortlet.java} → Refers to\n        \\texttt{SomeService} using a reluctant service policy option by\n        default.\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  \\texttt{higher-ranked-service/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{bnd.bnd}\n  \\item\n    \\texttt{src/main/java/com/liferay/blade/reluctant/vs/greedy/svc/HigherRankedService.java}\n    → Service implementation with service ranking value of \\texttt{100}\n  \\end{itemize}\n\\end{itemize}\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-5}\n\nHere are the things you can learn using the sample modules:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\hyperref[binding-a-newly-deployed-components-service-reference-to-the-highest-ranking-service-instance-thats-available-initially]{Binding\n  a component's service reference to the highest ranked service instance\n  that's available initially.}\n\\item\n  \\hyperref[deploying-a-module-with-a-higher-ranked-service-instance-for-binding-to-greedy-references-immediately]{Deploying\n  a module with a higher ranked service instance for binding to greedy\n  references immediately.}\n\\item\n  \\hyperref[configuring-a-component-to-reference-a-different-service-instance-dynamically]{Configuring\n  a component to reference a different service instance dynamically.}\n\\end{enumerate}\n\nLet's walk through the demonstration.\n\n\\section{Binding a newly deployed component's service reference to the\nhighest ranking service instance that's available\ninitially}\\label{binding-a-newly-deployed-components-service-reference-to-the-highest-ranking-service-instance-thats-available-initially}\n\nOn deploying a component that references a service, it binds to the\nhighest ranking service instance that matches its target filter (if\nspecified).\n\nThe portlet classes refer to instances of interface\n\\texttt{SomeService}. The \\texttt{doSomething} method returns a\n\\texttt{String}.\n\n\\begin{verbatim}\npublic interface SomeService {\n\n    public String doSomething();\n\n}\n\\end{verbatim}\n\nClass \\texttt{DefaultSomeService} implements \\texttt{SomeService}. Its\n\\texttt{doSomething} method returns the \\texttt{String} ``I am\nDefault!''.\n\n\\begin{verbatim}\n@Component\npublic class DefaultSomeService implements SomeService {\n\n    @Override\n    public String doSomething() {\n        return \"I am Default!\";\n    }\n\n}\n\\end{verbatim}\n\nWhen module's portlets refer to \\texttt{DefaultSomeService}, they\ndisplay the \\texttt{String} ``I am Default!''.\n\nThe \\texttt{ReluctantPortlet} class's \\texttt{SomeService} reference's\npolicy option is the default: static and reluctant. This policy option\nkeeps the reference bound to its current service instance unless that\ninstance stops or the reference is reconfigured to refer to a different\nservice instance.\n\n\\begin{verbatim}\n@Component(\n   immediate = true,\n   property = {\n       \"com.liferay.portlet.display-category=category.sample\",\n       \"com.liferay.portlet.instanceable=true\",\n       \"javax.portlet.display-name=Reluctant Portlet\",\n       \"javax.portlet.init-param.template-path=/\",\n       \"javax.portlet.init-param.view-template=/view.jsp\",\n       \"javax.portlet.name=\" + ReluctantVsGreedyPortletKeys.Reluctant,\n       \"javax.portlet.resource-bundle=content.Language\",\n       \"javax.portlet.security-role-ref=power-user,user\"\n    },\n    service = Portlet.class\n  )\npublic class ReluctantPortlet extends MVCPortlet {\n\n   @Override\n   public void doView(\n           RenderRequest renderRequest, RenderResponse renderResponse)\n       throws IOException, PortletException {\n\n       renderRequest.setAttribute(\"doSomething\", _someService.doSomething());\n\n       super.doView(renderRequest, renderResponse);\n   }\n\n    @Reference\n    private SomeService _someService;\n\n}\n\\end{verbatim}\n\nThe \\texttt{ReluctantPortlet}'s method \\texttt{doView} sets render\nrequest attribute \\texttt{doSomething} to the value returned from the\n\\texttt{SomeService} instance's \\texttt{doSomething} method (e.g.,\n\\texttt{DefaultService} returns ``I am default!'').\n\nThe \\texttt{GreedyPortlet} class is similar to\n\\texttt{ReluctantPortlet}, except its \\texttt{SomeService} reference's\npolicy option is static and greedy (i.e.,\n\\texttt{ReferencePolicyOption.GREEDY}).\n\n\\begin{verbatim}\npublic class GreedyPortlet extends MVCPortlet {\n\n  @Override\n    public void doView(\n        RenderRequest renderRequest, RenderResponse renderResponse)\n    throws IOException, PortletException {\n\n    renderRequest.setAttribute(\"doSomething\", _someService.doSomething());\n\n    super.doView(renderRequest, renderResponse);\n    }\n\n    @Reference (policyOption = ReferencePolicyOption.GREEDY)\n    private SomeService _someService;\n\n}\n\\end{verbatim}\n\nThe greedy policy option lets the component switch to using a higher\nranked \\texttt{SomeService} instance if one becomes active in the\nsystem. The section\n\\hyperref[deploying-a-module-with-a-higher-ranked-service-instance-for-binding-to-greedy-references-immediately]{\\emph{Deploying\na module with a higher ranked service instance for binding to greedy\nreferences immediately}} demonstrates this portlet switching to a higher\nranked service.\n\nIt's time to see this module's portlets and service in action.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Stop module \\texttt{higher-ranked-service} if it's active.\n\\item\n  Deploy the \\texttt{service-reference} module.\n\\item\n  Add the \\emph{Reluctant Portlet} from the \\emph{Add} → \\emph{Widgets}\n  → \\emph{Sample} category to a site page.\n\n  The portlet displays the message ``SomeService says I am\n  Default!''--whose latter part comes from the render request attribute\n  set by the \\texttt{DefaultService} instance.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/reluctant-portlet-using-default.png}\n  \\caption{\\emph{Reluctant Portlet} displays the message ``SomeService\n  says I am Default!''}\n  \\end{figure}\n\\item\n  Add the \\emph{Greedy Portlet} from the \\emph{Add} → \\emph{Widgets} →\n  \\emph{Sample} category to a site page.\n\n  The portlet displays the message ``SomeService says I am better, use\n  me!''. Both portlets are referencing a \\texttt{DefaultService}\n  instance.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/greedy-portlet-using-default.png}\n  \\caption{\\emph{Greedy Portlet} displays the message ``SomeService says\n  I am better, use me!''}\n  \\end{figure}\n\\end{enumerate}\n\nSince \\texttt{DefaultService} is the only active \\texttt{SomeService}\ninstance in the system, the portlets refer to it for their\n\\texttt{SomeService} fields.\n\nThe \\texttt{DefaultService} and portlets \\emph{Reluctant Portlet} and\n\\emph{Greedy Portlet} are active. Let's activate a higher ranked\n\\texttt{SomeService} instance and see how the portlets react to it.\n\n\\section{Deploying a module with a higher ranked service instance for\nbinding to greedy references\nimmediately}\\label{deploying-a-module-with-a-higher-ranked-service-instance-for-binding-to-greedy-references-immediately}\n\nModule \\texttt{higher-ranked-service} provides a \\texttt{SomeService}\nimplementation called \\texttt{HigherRankedService}.\n\\texttt{HigherRankedService}'s service ranking is \\texttt{100}--that's\n\\texttt{100} more than \\texttt{DefaultService}'s ranking \\texttt{0}. Its\n\\texttt{doSomething} method returns the \\texttt{String} ``I am better,\nuse me!''.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Deploy the \\texttt{higher-ranked-service} module.\n\\item\n  Refresh your page that has the portlets \\emph{Reluctant Portlet} and\n  \\emph{Greedy Portlet}.\n\\end{enumerate}\n\n\\emph{Reluctant Portlet} continues displaying message ``SomeService says\nI am better, use me!''. It's ``reluctant'' to unbind from the\n\\texttt{DefaultService} instance and bind to the newly activated\n\\texttt{HigherRankedService} service.\n\n\\emph{Greedy Portlet} displays a new message ``SomeService says I am\nbetter, use me!''. The part of the message ``I am better, use me!''\ncomes from the \\texttt{HigherRankedService} instance to which it refers.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/greedy-portlet-using-higher-ranked-service.png}\n\\caption{The \\emph{Greedy Portlet} is using a\n\\texttt{HigherRankedService} instance}\n\\end{figure}\n\nNext, learn how to bind the \\emph{Reluctant Portlet} to a\n\\texttt{HigherRankedService} instance.\n\n\\section{Configuring a component to reference a different service\ninstance\ndynamically}\\label{configuring-a-component-to-reference-a-different-service-instance-dynamically}\n\nThe \\emph{Reluctant Portlet} is currently bound to a\n\\texttt{DefaultService} instance. It's ``reluctant'' to unbind from it\nand bind to a different service. OSGi Configuration Administration lets\nyou reconfigure service references to filter on and bind to different\nservice instances.\n\nThe \\texttt{service-reference} module's configuration files and\n\\texttt{com.liferay.blade.reluctant.vs.greedy.portlet.portlet.ReluctantPortlet.config}\nand\n\\texttt{com.liferay.blade.reluctant.vs.greedy.portlet.portlet.ReluctantPortlet.cfg}\nconfigure the \\texttt{ReluctantPortlet} component to use a\n\\texttt{HigherRankedService} instance.\n\n\\begin{verbatim}\n_someService.target=(component.name=com.liferay.blade.reluctant.vs.greedy.service.HigherRankedService)\n\\end{verbatim}\n\nThe service configuration filters on a service whose\n\\texttt{component.name} is\n\\texttt{com.liferay.blade.reluctant.vs.greedy.service.HigherRankedService}.\n\nNote: For deploying to 7.0, use file with suffix \\texttt{.config}. For\nearlier versions (i.e., Liferay DXP 7.0 Fix Packs earlier than Fix Pack\n8 and Liferay CE Portal 7.0 GA3 or earlier), use the file with suffix\n\\texttt{.cfg}.\n\nHere are the steps to reconfigure \\texttt{ReluctantPortlet} to use\n\\texttt{HigherRankedService}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Copy the configuration file to\n  \\texttt{{[}Liferay-Home{]}/osgi/configs}.\n\\item\n  Refresh your browser.\n\\end{enumerate}\n\n\\emph{Reluctant Portlet} displays a new message ``SomeService says I am\nbetter, use me!''.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/reluctant-portlet-using-higher-ranked-service.png}\n\\caption{\\emph{Reluctant Portlet} is using the\n\\texttt{HigherRankedService} instance instead of a\n\\texttt{DefaultService} instance.}\n\\end{figure}\n\n\\emph{Reluctant Portlet} is using \\texttt{HigherRankedService} instance\ninstead of a \\texttt{DefaultService} instance. You've configured\n\\emph{Reluctant Portlet} to use a \\texttt{HigherRankedService} instance!\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/greedy-policy-option-portlet}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps/greedy-policy-option-portlet}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/apps/greedy-policy-option-portlet}{Maven}\n\\end{itemize}\n\n\\chapter{Kotlin Portlet}\\label{kotlin-portlet}\n\nThe Kotlin Portlet sample provides an input form that accepts a name.\nOnce submitting a name, the portlet renders a greeting message.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kotlin-portlet.png}\n\\caption{After saving the inputted name, it's displayed as a greeting on\nthe portlet page.}\n\\end{figure}\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-6}\n\nThis sample highlights the use of the\n\\href{https://kotlinlang.org/}{Kotlin} programming language in\nconjunction with Liferay's MVC framework. Specifically, this sample\nleverages the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCActionCommand.html}{MVCActionCommand}\ninterface.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-6}\n\nThis sample uses the MVC Action Command's \\texttt{processAction(...)}\nmethod to process the inputted text (i.e., name). The text is set as an\nattribute in the \\texttt{KotlinGreeterActionCommandKt.kt} class using an\n\\texttt{ActionRequest} and then is retrieved in the JSP using a\n\\texttt{RenderRequest}.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-1}\n\nThis sample is built with the following build tools:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/kotlin-portlet}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps/kotlin-portlet}{Liferay\n  Workspace}\n\\end{itemize}\n\n\\chapter{Shared Language Keys}\\label{shared-language-keys}\n\nThe Shared Language Keys sample provides a JSP portlet that displays\nlanguage keys.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/language-web-portlet.png}\n\\caption{The sample JSP portlet displays three language keys.}\n\\end{figure}\n\nThe language keys displayed in the portlet come from two different\nmodules.\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-7}\n\nThis sample is broken into two modules:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{language}\n\\item\n  \\texttt{language-web}\n\\end{itemize}\n\nThe \\texttt{language-web} module provides a JSP portlet with unique\nlanguage keys that it displays. The \\texttt{language} module provides a\nresource module which only holds language keys. Its sole purpose is to\nshare language keys with the JSP portlet provided in\n\\texttt{language-web}. This sample conveys Liferay's recommended\napproach to sharing language keys through OSGi services.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-7}\n\nYou must deploy both \\texttt{language-web} and \\texttt{language} modules\nto simulate this sample's targeted demonstration.\n\nFirst, note the language keys provided by each module:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{language-web}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{blade\\_language\\_web\\_LanguageWebPortlet.caption=Hello\\ from\\ BLADE\\ Language\\ Web!}\n  \\item\n    \\texttt{blade\\_language\\_web\\_override\\_LanguageWebPortlet.caption=I\\ have\\ overridden\\ the\\ key\\ from\\ BLADE\\ Language\\ Module!}\n  \\end{itemize}\n\\item\n  \\texttt{language}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{blade\\_language\\_LanguageWebPortlet.caption=Hello\\ from\\ the\\ BLADE\\ Language\\ Module!}\n  \\item\n    \\texttt{blade\\_language\\_web\\_override\\_LanguageWebPortlet.caption=Hello\\ from\\ the\\ BLADE\\ Language\\ Module\\ but\\ you\\ won\\textquotesingle{}t\\ see\\ me!}\n  \\end{itemize}\n\\end{itemize}\n\nWhen you place the sample BLADE Language Web portlet on a Liferay DXP\npage, you're presented with three language keys:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/shared-language-keys.png}\n\\caption{The Language Web portlet displays three phrases, two of which\nare shared from a different module.}\n\\end{figure}\n\nThe first message is provided by the \\texttt{language-web} module. The\nsecond message is from the \\texttt{language} module. The third message\nis provided by both modules; as you can see, the \\texttt{language-web}'s\nmessage is used, overriding the \\texttt{language} module's identically\nnamed language key.\n\nThis sample shows what takes precedence when displaying language keys.\nThe order for this example goes\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  \\texttt{language-web} module language keys\n\\item\n  \\texttt{language} module language keys\n\\item\n  Liferay DXP language keys\n\\end{enumerate}\n\nSo how does sharing language keys work?\n\nBy default, the \\texttt{ResourceBundleLoaderAnalyzerPlugin} expands\nmodules with \\texttt{/content/Language.properties} files to add provided\ncapabilities:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{bundle.symbolic.name}\n\\item\n  \\texttt{resource.bundle.base.name}\n\\end{itemize}\n\nThen the deployed \\texttt{LanguageExtender} scans modules with those\ncapabilities to automatically register an associated\n\\texttt{ResourceBundleLoader}.\n\nYou can leverage this functionality to use keys from common language\nmodules by republishing an aggregate \\texttt{ResourceBundleLoader}. This\ncan be done two ways:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Via Components\n\n  You can get a reference to the registered service in your components\n  as detailed in the\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys}{Overriding\n  a Module's Language Keys} tutorial. The main disadvantage of this\n  approach is that it forces you to provide a specific implementation of\n  the \\texttt{ResourceBundleLoader}, making it harder to modularize in\n  the future.\n\\item\n  Via Provide Capability\n\n  The same \\texttt{LanguageExtender} that registers the services\n  supports an extended syntax that lets you register an aggregate of a\n  collection of bundles:\n\n\\begin{verbatim}\n-liferay-aggregate-resource-bundles: \\\n    blade.language\n\\end{verbatim}\n\n  This approach has the advantage of easier extensibility. When language\n  keys change, only the common language modules must be built and\n  redeployed for the modules referencing them to recognize their\n  updates.\n\\end{enumerate}\n\nFor more information on sharing language keys, visit the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/localization}{Internationalization}\ntutorials.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-2}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/shared-language-keys}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps/shared-language-keys}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/apps/shared-language-keys}{Maven}\n\\end{itemize}\n\n\\chapter{Simulation Panel App}\\label{simulation-panel-app}\n\nThe Simulation Panel App provides new functionality in Liferay DXP's\nSimulation Menu. When deploying this sample with no customizations, the\n\\emph{Simulation Sample} feature is provided in the Simulation Menu with\nfour options.\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-8}\n\nThis sample leverages the\n\\href{https://docs.liferay.com/dxp/apps/web-experience/latest/javadocs/com/liferay/application/list/PanelApp.html}{PanelApp}\nAPI.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-8}\n\nThis sample leverages the \\texttt{PanelApp} interface as an OSGi service\nvia the \\texttt{@Component} annotation:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"panel.app.order:Integer=500\",\n        \"panel.category.key=\" + SimulationPanelCategory.SIMULATION\n    },\n    service = PanelApp.class\n)\n\\end{verbatim}\n\nThere are also two properties provided via the \\texttt{@Component}\nannotation:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{panel.app.order}: the order in which the panel app is\n  displayed among other panel apps in the chosen category. Entries are\n  ordered from top to bottom. For example, an entry with order\n  \\texttt{1} will be listed above an entry with order \\texttt{2}. If the\n  order is not specified, it's chosen at random based on which service\n  was registered first in the OSGi container.\n\\item\n  \\texttt{panel.category.key}: the host panel category for your panel\n  app, which should be the Simulation Menu category.\n\\end{itemize}\n\nThe simulation panel app extends the\n\\href{https://docs.liferay.com/ce/apps/web-experience/latest/javadocs/com/liferay/application/list/BaseJSPPanelApp.html}{BaseJSPPanelApp},\nwhich provides a skeletal implementation of the\n\\href{https://docs.liferay.com/ce/apps/web-experience/latest/javadocs/com/liferay/application/list/PanelApp.html}{PanelApp}\ninterface with JSP support. JSPs, however, are not the only way to\nprovide frontend functionality to your panel categories/apps. You can\ncreate your own class implementing \\texttt{PanelApp} to use other\ntechnologies, such as FreeMarker.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-3}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/simulation-panel-app}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps/simulation-panel-app}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/apps/simulation-panel-app}{Maven}\n\\end{itemize}\n\n\\chapter{Extensions}\\label{extensions}\n\nThis section focuses on Liferay sample extensions. You can view these\nsample extensions by visiting the \\texttt{extensions} folder\ncorresponding to your preferred build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions}{Gradle\n  sample extensions}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions}{Liferay\n  Workspace sample extensions}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions}{Maven\n  sample extensions}\n\\end{itemize}\n\nVisit a particular sample page to learn more!\n\n\\chapter{Control Menu Entry}\\label{control-menu-entry}\n\nThe Control Menu Entry sample provides a customizable button that is\nadded to Liferay Portal's default Control Menu. When deploying this\nsample with no customizations, an additional button is added to the User\n(right side) portion of the Control Menu.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/controlmenuentry.png}\n\\caption{The User area of the Control Menu is provided an additional\nlink button when the Control Menu Entry sample is deployed to Liferay\nDXP.}\n\\end{figure}\n\nThe button navigates the user to Liferay's website:\nhttps://www.liferay.com.\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-9}\n\nThis sample leverages the\n\\href{https://docs.liferay.com/dxp/apps/web-experience/latest/javadocs/com/liferay/product/navigation/control/menu/ProductNavigationControlMenuEntry.html}{ProductNavigationControlMenuEntry}\nAPI.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-9}\n\nThis sample first leverages the\n\\texttt{ProductNavigationControlMenuEntry} interface as an OSGi service\nvia the \\texttt{@Component} annotation:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"product.navigation.control.menu.category.key=\" + ProductNavigationControlMenuCategoryKeys.USER,\n        \"product.navigation.control.menu.entry.order:Integer=1\"\n    },\n    service = ProductNavigationControlMenuEntry.class\n)\n\\end{verbatim}\n\nThere are also two properties provided via the \\texttt{@Component}\nannotation:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{product.navigation.control.menu.category.key}: the category in\n  which your entry should reside. The default Control Menu provides\n  three categories: \\emph{SITES} (left portion), \\emph{TOOLS} (middle\n  portion), and \\emph{USER} (right portion).\n\\item\n  \\texttt{product.navigation.control.menu.entry.order:Integer}: the\n  order in which your entry will be displayed in the category. Entries\n  are ordered from left to right. For example, an entry with order\n  \\texttt{1} will be listed to the left of an entry with order\n  \\texttt{2}. If the order is not specified, it's chosen at random based\n  on which service was registered first in the OSGi container.\n\\end{itemize}\n\nThis sample also implements the\n\\texttt{ProductNavigationControlMenuEntry} interface. The following\nmethods are implemented:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{getIcon(HttpServletRequest)}\n\\item\n  \\texttt{getLabel(Locale)}\n\\item\n  \\texttt{getURL(HttpServletRequest)}\n\\item\n  \\texttt{isShow(HttpServletRequest)}\n\\end{itemize}\n\nRefer to this sample's \\texttt{BladeProductNavigationControlMenuEntry}\nclass for Javadocs describing these methods.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-4}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/control-menu-entry}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/control-menu-entry}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/control-menu-entry}{Maven}\n\\end{itemize}\n\n\\chapter{Document Action}\\label{document-action}\n\nThe Document Action sample shows how to add a context menu option to an\nentry in the Documents and Media portlet. When deploying this sample\nwith no customizations, an additional menu option is available in the\nDocuments and Media Admin portlet and the Documents and Media portlet.\nThis sample creates a \\emph{Blade Basic Info} option that displays basic\ninformation about the entry (e.g., file name, type, version, etc.). For\nexample, the Admin portlet provides the new option as illustrated in the\nimages below:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/documents-and-media-admin-portlet.png}\n\\caption{The new \\emph{Blade Basic Info} option is available from the\nentry's Options menu.}\n\\end{figure}\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-10}\n\nThis sample leverages the\n\\href{@product-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/configuration/icon/PortletConfigurationIcon.html}{PortletConfigurationIcon}\nAPI.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-10}\n\nThere are four Java classes used in this sample:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{BladeActionConfigurationIcon}: Adds the new context menu\n  option to the Document Detail screen options\n  (\\includegraphics{./images/icon-options.png}) (top right corner) of\n  the Documents and Media Admin portlet. See the\n  \\href{/docs/7-0/tutorials/-/knowledge_base/t/configuring-your-admin-apps-actions-menu}{Configuring\n  Your Admin App's Actions Menu} tutorial for more details.\n\\item\n  \\texttt{BladeActionDisplayContext}: Adds the Display Context for the\n  document action. More about Display Contexts are described later.\n\\item\n  \\texttt{BladeActionDisplayContextFactory}: Adds the Display Context\n  factory for the document action.\n\\item\n  \\texttt{BladeDocumentActionPortlet}: Provides the portlet class, which\n  extends the\n  \\href{https://portals.apache.org/pluto/portlet-2.0-apidocs/javax/portlet/GenericPortlet.html}{GenericPortlet}.\n  This class generates what is shown when the context menu option is\n  selected.\n\\end{itemize}\n\nA Display Context is a Java class that controls access to a portlet\nscreen's UI elements. For example, the Document Library would use\nDisplay Contexts to provide its screens all their UI elements. It would\nuse one Display Context for its document edit screen, another for its\ndocument view screen, etc. A portlet ideally uses a different Display\nContext for each of its screens.\n\nA screen's JSP calls on the Display Context (DC) to get elements to\nrender and to decide whether to render certain types of elements. Some\nof the DC methods return a collection of UI elements (e.g., a menu\nobject of menu items), while other DC methods return booleans that\ndetermine whether to show particular element types. The DC decides which\nobjects to display, while the JSP organizes the rendered objects and\nimplements the screen's look and feel. You don't have to decide which\nelements to display in your JSP; simply call the DC methods to populate\nUI components with objects to render.\n\nTo customize or extend a portlet screen that uses a DC, you can extend\nthe DC and override the methods that control access to the elements that\ninterest you. For example, you can turn off displaying certain types of\nelements (e.g., actions) by overriding the DC method that makes that\ndecision. You can add new custom elements (e.g., new actions) or remove\nexisting elements (e.g., a delete action) from a collection of elements\na DC method returns. The beauty of customizing via a DC is that you\ndon't have to modify the JSP. You only modify the particular methods\nthat are related to the UI customization goals. And JSP updates won't\nbreak the DC customizations. Replacing a JSP, on the other hand, can\nlead to missing an important JSP modification that a new Liferay version\nintroduces.\n\nAs you create custom portlets, you may want to implement DCs. You can\nbenefit from the separation of concerns that DCs provide and customers\ncan extend your portlet DCs to specify which UI elements to display. And\nthey don't need to worry about missing out on the updates you make to\nthe JSPs.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-5}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/document-action}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/document-action}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/document-action}{Maven}\n\\end{itemize}\n\n\\chapter{Gogo Shell Command}\\label{gogo-shell-command}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe Gogo Shell Command sample demonstrates adding a custom command to\nLiferay DXP's Gogo shell environment. All Liferay DXP installations have\na Gogo shell environment, which lets system administrators interact with\nLiferay DXP's module framework on a local server machine.\n\nThis example adds a new custom Gogo shell command called\n\\texttt{usercount} under the \\texttt{blade} scope. It prints out the\nnumber of registered users on your Liferay DXP installation.\n\nTo test this sample, follow the instructions below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Start a Liferay DXP installation.\n\\item\n  Navigate to the Control Panel → \\emph{Configuration} → \\emph{Gogo\n  Shell}.\n\\item\n  Execute \\texttt{help} to view all the available commands. The sample\n  Gogo shell command is listed.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/gogo-shell-1.png}\n  \\caption{The sample Gogo shell command is listed with all the\n  available commands.}\n  \\end{figure}\n\\item\n  Execute \\texttt{usercount} to execute the new custom command. The\n  number of users on your running Liferay Portal installation is\n  printed.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/gogo-shell-2.png}\n  \\caption{The outcome of executing the \\texttt{usercount} command.}\n  \\end{figure}\n\\end{enumerate}\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-11}\n\nThis sample demonstrates creating a new Gogo shell command by leveraging\n\\texttt{osgi.command.*} properties in a Java class.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-11}\n\nTo add this new Gogo shell command, you must implement the logic in a\nJava class with the following two properties:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{osgi.command.function}: the command's name, which must match\n  the method name in the registered service implementation.\n\\item\n  \\texttt{osgi.command.scope}: the general scope or namespace for the\n  command.\n\\end{itemize}\n\nThese properties are set in your class's \\texttt{@Component} annotation\nlike this:\n\n\\begin{verbatim}\n@Component(\n    property = {\"osgi.command.function=usercount\", \"osgi.command.scope=blade\"},\n    service = Object.class\n)\n\\end{verbatim}\n\nThe logic for the \\texttt{usercount} command is specified in the method\nwith the same name:\n\n\\begin{verbatim}\npublic void usercount() {\n    System.out.println(\n        \"# of users: \" + getUserLocalService().getUsersCount());\n}\n\\end{verbatim}\n\nThis method uses \\emph{Declarative Services} to get a reference for the\n\\texttt{UserLocalService} to invoke the \\texttt{getUsersCount} method.\nThis lets you find the number of users currently in the system.\n\nFor more information on using the Gogo shell, see the\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Using\nthe Felix Gogo Shell} tutorial.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-6}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/gogo}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/gogo}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/gogo}{Maven}\n\\end{itemize}\n\n\\chapter{Index Settings Contributor}\\label{index-settings-contributor}\n\nThe Index Settings Contributor sample demonstrates how to add a custom\ntype mapping to Liferay DXP. You can demo this sample by completing the\nfollowing steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the \\emph{Control Panel} → \\emph{Configuration} →\n  \\emph{Search} menu.\n\\item\n  Click \\emph{Execute} for the \\emph{Reindex all search indexes} action.\n\n  All properties defined in your \\texttt{.json} file are added to\n  Liferay DXP's search engine. This sample adds the following index\n  properties:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{sampleDate}\n  \\item\n    \\texttt{sampleDouble}\n  \\item\n    \\texttt{sampleLong}\n  \\item\n    \\texttt{sampleText}\n  \\end{itemize}\n\n  You'll verify this next.\n\\item\n  Find your Liferay DXP's instance ID. This can be found in the\n  \\emph{Control Panel} → \\emph{Configuration} → \\emph{Virtual Instances}\n  menu.\n\\item\n  Navigate to the following URL:\n\n\\begin{verbatim}\nhttp://localhost:9200/liferay-[INSTANCE_ID]/_mapping/LiferayDocumentType?pretty\n\\end{verbatim}\n\n  Be sure to insert your instance ID into the URL.\n\\item\n  Verify the added properties are listed.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/index-settings-contributor.png}\n  \\caption{This sample added four new index properties.}\n  \\end{figure}\n\\end{enumerate}\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-12}\n\nThis sample leverages the\n\\href{https://docs.liferay.com/dxp/apps/foundation/latest/javadocs/com/liferay/portal/search/elasticsearch/settings/IndexSettingsContributor.html}{IndexSettingsContributor}\nAPI.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-12}\n\nLiferay's search engine provides an API to define custom mappings. To\nuse it, follow these fundamental steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Define the new mapping. In this sample, the mapping is defined in the\n  \\texttt{META-INF/mappings/resources/index-type-mappings.json} file.\n  Notice that the default document for Liferay DXP is called\n  \\texttt{LiferayDocumentType}. The mapping's features can be found in\n  \\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/mapping.html}{Elasticsearch's\n  docs}.\n\\item\n  Inject the mapping into Elasticsearch. The\n  \\texttt{IndexSettingsContributor} class' components are invoked during\n  the reindexing stage and receive a \\texttt{TypeMappingsHelper} as a\n  hook to add new mappings.\n\\end{enumerate}\n\nThis sample has two classes:\n\n\\begin{itemize}\n\\item\n  \\texttt{ResourceUtil}: reads the \\texttt{.json} file.\n\\item\n  \\texttt{IndexSettingsContributor}: allows the addition of type\n  mappings on Liferay DXP's search engine.\n\\end{itemize}\n\nThe \\texttt{IndexSettingsContributor}'s \\texttt{contribute} method adds\nthe type mappings:\n\n\\begin{verbatim}\n@Override\npublic void contribute(\n    String indexName, TypeMappingsHelper typeMappingsHelper) {\n    try {\n        String mappings = ResourceUtil.readResouceAsString(\n            \"META-INF/resources/mappings/index-type-mappings.json\");\n\n        typeMappingsHelper.addTypeMappings(indexName, mappings);\n    }\n    catch (Exception e) {\n        e.printStackTrace();\n    }\n}\n\\end{verbatim}\n\nFor the \\texttt{ResourceUtil.readResouceAsString} parameter, you should\npass the path for the \\texttt{.json} file that contains the properties\nto be added.\n\nAlso, it is important to highlight the\n\\texttt{IndexSettingsContributor}'s \\texttt{@Component} annotation that\nregisters a new service to the OSGi container:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    service = com.liferay.portal.search.elasticsearch6.settings.IndexSettingsContributor.class\n)\n\n> If using Elasticsearch 7, the value of the `service` property is instead `com.liferay.portal.search.elasticsearch7.settings.IndexSettingsContributor.class`.\n\\end{verbatim}\n\nThis sample demonstrates the essentials needed to contribute your own\nindex settings.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-7}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/blob/7.2/gradle/extensions/index-settings-contributor}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/blob/7.2/liferay-workspace/extensions/index-settings-contributor}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/blob/7.2/maven/extensions/index-settings-contributor}{Maven}\n\\end{itemize}\n\n\\chapter{Indexer Post Processor}\\label{indexer-post-processor}\n\nThe Indexer Post Processor sample demonstrates using the\n\\texttt{IndexerPostProcessor} interface, which is provided to customize\nsearch queries and documents before they're sent to the search engine,\nand/or customize result summaries when they're returned to end users.\nThis basic demonstration prints a message in the log when one of the\n\\texttt{*IndexerPostProcessor} methods is called.\n\nTo see this sample's messages in Liferay DXP's log, you must add a\nlogging category to the portal. Navigate to \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{Server Administration} and click on\n\\emph{Log Levels} → \\emph{Add Category}. Then fill out the form:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Logger Name}:\n  \\texttt{com.liferay.blade.samples.indexerpostprocessor}\n\\item\n  \\emph{Log Level}: \\texttt{INFO}\n\\end{itemize}\n\nOnce you save the new logging category, you can witness the sample\nindexer post processor in action. For example, you can test the sample's\n\\texttt{BlogsIndexerPostProcessor} implementation by creating a blog\nentry. When you publish the blog, the following message is logged in the\nconsole:\n\n\\begin{verbatim}\n18:27:30,737 INFO  [http-nio-8080-exec-8][BlogsIndexerPostProcessor:76] postProcessDocument\n\\end{verbatim}\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-13}\n\nThis sample leverages the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/search/IndexerPostProcessor.html}{IndexerPostProcessor}\nAPI.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-13}\n\nThis sample contains four implementations of the\n\\texttt{IndexerPostProcessor} interface:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{BlogsIndexerPostProcessor}\n\\item\n  \\texttt{MultipleEntityIndexerPostProcessor}\n\\item\n  \\texttt{MultipleIndexerPostProcessor}\n\\item\n  \\texttt{UserEntityIndexerPostProcessor}\n\\end{itemize}\n\nAll these classes leverage the interface as an OSGi service via the\n\\texttt{@Component} annotation. For example, the \\texttt{@Component}\nannotation of the \\texttt{UserEntityIndexerPostProcessor} looks like\nthis:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"indexer.class.name=com.liferay.portal.kernel.model.User\",\n        \"indexer.class.name=com.liferay.portal.kernel.model.UserGroup\"\n    },\n    service = IndexerPostProcessor.class\n)\n\\end{verbatim}\n\nThere's one property type provided via the \\texttt{@Component}\nannotation:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{indexer.class.name}: the fully qualified class name of the\n  indexed entity or an \\texttt{Indexer} class itself.\n\\end{itemize}\n\nThis sample's implementations of the \\texttt{IndexerPostProcessor}\ninterface override the following methods:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{postProcessContextBooleanFilter}\n\\item\n  \\texttt{postProcessContextQuery}\n\\item\n  \\texttt{postProcessDocument}\n\\item\n  \\texttt{postProcessFullQuery}\n\\item\n  \\texttt{postProcessSearchQuery(BooleanQuery,\\ BooleanFilter)}\n\\item\n  \\texttt{postProcessSearchQuery(BooleanQuery,\\ SearchContext)}\n\\item\n  \\texttt{postProcessSummary}\n\\end{itemize}\n\nFor more information on Liferay's Search API, refer to the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/search}{Introduction to\nLiferay Search} article.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-8}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/indexer-post-processor}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/indexer-post-processor}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/indexer-post-processor}{Maven}\n\\end{itemize}\n\n\\chapter{Model Listener}\\label{model-listener}\n\nThe Model Listener sample demonstrates adding a custom model listener to\na Liferay Portal out-of-the-box entity. When deploying this sample with\nno customizations, a custom model listener is added to the portal's\nlayouts, listening for \\texttt{onBeforeCreate} events. This means that\nany page creation will trigger this listener, which will execute before\nthe new page is created.\n\nFor example, if a new page is added with the name \\emph{My Test Page},\nthe following message is printed to the console:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/model-listener-1.png}\n\\caption{The sample model listener's message in the console.}\n\\end{figure}\n\nYou can also verify that the model listener sample was executed by\nnavigating to the new page's \\emph{Options} → \\emph{Configure Page} →\n\\emph{SEO} option. The HTML Title field looks like this:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/model-listener-2.png}\n\\caption{The page's HTML title updated by the model listener sample.}\n\\end{figure}\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-14}\n\nThis sample leverages the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/ModelListener.html}{ModelListener}\nAPI.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-14}\n\nModel Listeners are used to listen for persistence events on models and\ntake actions as a result of those events. Actions can be executed on an\nentity's database table before or after a \\texttt{create},\n\\texttt{remove}, \\texttt{update}, \\texttt{addAssociation}, or\n\\texttt{removeAssociation} event. It's possible to have more than one\nmodel listener on a single model too; the execution order is not\nguaranteed.\n\nThere are two steps to create a new model listener:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Implement a Model Listener class\n\\item\n  Register the new service in Liferay's OSGi runtime\n\\end{itemize}\n\nThis sample adds the model listener logic in a new Java class named\n\\texttt{CustomLayoutListener} that extends\n\\href{https://docs.liferay.com/dxp/portal/7.1-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/BaseModelListener.html}{BaseModelListener}.\n\n\\begin{verbatim}\npublic class CustomLayoutListener extends BaseModelListener<Layout> {\n\n    @Override\n    public void onBeforeCreate(Layout model) throws ModelListenerException {\n        System.out.println(\n            \"About to create layout: \" + model.getNameCurrentValue());\n\n        model.setTitle(\"Title generated by model listener!\");\n    }\n\n}\n\\end{verbatim}\n\nImportant things to note in this code snippet are\n\n\\begin{itemize}\n\\tightlist\n\\item\n  The entity to be targeted by this model listener is specified as the\n  parameterized type (e.g., \\texttt{Layout}).\n\\item\n  The overridden methods dictate the type of event(s) that are listened\n  for (e.g., \\texttt{onBeforeCreate}); they also trigger the logic\n  execution.\n\\end{itemize}\n\nThe final step is registering the service in Liferay's OSGi runtime,\nwhich is accomplished by the following annotation (if using Declarative\nServices):\n\n\\begin{verbatim}\n@Component(immediate = true, service = ModelListener.class)\n\\end{verbatim}\n\nFor more information on model listeners, see the\n\\href{/docs/7-2/customization/-/knowledge_base/c/model-listeners}{Creating\nModel Listeners} tutorial.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-9}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/model-listener}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/model-listener}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/model-listener}{Maven}\n\\end{itemize}\n\n\\chapter{Screen Name Validator}\\label{screen-name-validator}\n\nThe Screen Name Validator sample provides a way to validate a user's\ninputted screen name. During validation, the screen name is tested\nclient-side and server-side.\n\nThis sample checks if a user's screen name contains reserved words that\nare configured in the \\emph{Control Panel} → \\emph{Configuration} →\n\\emph{System Settings} → \\emph{Foundation} → \\emph{ScreenName Validator}\nmenu. The default values for the screen name validator's reserved words\nare \\emph{admin} and \\emph{user}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/screenname-validator-config.png}\n\\caption{Enter reserved words for the screen name validator.}\n\\end{figure}\n\nYou can test this sample by following the following steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Deploy the Screen Name Validator to your portal installation.\n\\item\n  Navigate to the \\emph{Control Panel} → \\emph{Users} → \\emph{Users and\n  Organizations} menu.\n\\item\n  Create a new user by selecting the \\emph{Add User}\n  (\\includegraphics{./images/icon-add.png}) button.\n\\item\n  Adding a screen name that contains the word \\emph{admin} or\n  \\emph{user}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/screenname-validator-test.png}\n\\caption{The error message displays when inputting a reserved word for\nthe screen name.}\n\\end{figure}\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-15}\n\nThis sample leverages the\n\\href{@product-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/security/auth/ScreenNameValidator.html}{ScreenNameValidator}\nAPI.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-15}\n\nTo customize this sample, modify its\n\\texttt{com.liferay.blade.samples.screenname.validator.internal.CustomScreenNameValidator}\nclass.\n\nYou can also customize this sample's configuration by adding more\nproperties in its\n\\texttt{com.liferay.blade.samples.screenname.validator.CustomScreenNameConfiguration}\nclass.\n\nFor more information on customizing the Validation sample to fit your\nneeds, see the Javadoc provided in this sample's Java classes.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-10}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/screen-name-validator}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/screen-name-validator}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/screen-name-validator}{Maven}\n\\end{itemize}\n\n\\chapter{Servlet}\\label{servlet}\n\nThe Servlet sample provides an OSGi Whiteboard Servlet in Liferay DXP.\nWhen deploying this sample and configuring the servlet, a \\emph{Hello\nWorld} message is displayed when accessing the servlet page URL. Log\ninfo is also outputted to your console.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/servlet-sample.png}\n\\caption{The servlet displays \\emph{Hello World} from the configured\nservlet page URL.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/servlet-sample-log.png}\n\\caption{The servlet also logs info in the console.}\n\\end{figure}\n\nTo configure the servlet in Liferay DXP, complete the following steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the \\emph{Control Panel} → \\emph{Configuration} →\n  \\emph{Server Administration} → \\emph{Log Levels}.\n\\item\n  Select \\emph{Add Category}.\n\\item\n  Insert \\emph{com.liferay.blade.samples.servlet.BladeServlet} for the\n  Logger Name and \\emph{INFO} for the Log Level.\n\\item\n  Navigate to the http://localhost:8080/o/blade/servlet URL.\n\\end{enumerate}\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-16}\n\nThis sample leverages the\n\\href{https://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServlet.html}{HttpServlet}\nAPI.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-16}\n\nTo customize this sample, modify its\n\\texttt{com.liferay.blade.samples.servlet.BladeServlet} class. This\nclass extends the \\texttt{HttpServlet} class. Creating your own servlet\nfor Liferay DXP is useful when you need to implement servlet actions.\nFor example, if you wanted to implement the CMIS server by yourself with\n\\href{https://chemistry.apache.org/}{Apache Chemistry}, you would need\nto implement your own servlet, managing requests at a low level.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-11}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/servlet}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/servlet}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/servlet}{Maven}\n\\end{itemize}\n\n\\chapter{Overrides}\\label{overrides}\n\nThis section focuses on Liferay sample overrides. You can view these\nsample overrides by visiting the \\texttt{overrides} folder corresponding\nto your preferred build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/overrides}{Gradle\n  sample overrides}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/overrides}{Liferay\n  Workspace sample overrides}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/overrides}{Maven\n  sample overrides}\n\\end{itemize}\n\nVisit a particular sample page to learn more!\n\n\\chapter{Module JSP Override}\\label{module-jsp-override}\n\nThe Module JSP Override sample conveys how to override an application's\nJSP by leveraging OSGi fragment modules. This is not the recommended\npractice for overriding JSPs in 7.0. See the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps}{Customizing\nJSPs} article for better options.\n\nThis example overrides the default \\texttt{login.jsp} file in the\n\\texttt{com.liferay.login.web} bundle by adding the red text\n\\emph{changed} to the Sign In form.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/hook-jsp.png}\n\\caption{The customized Sign In form with the new \\emph{changed} text.}\n\\end{figure}\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-17}\n\nThis sample demonstrates how to create a fragment host module and\nconfigure it to override an existing module's JSP.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-17}\n\nYou can create your own JSP override by\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Declaring the fragment host.\n\\item\n  Providing the JSP that will override the original one.\n\\end{itemize}\n\nTo properly declare the fragment host in the \\texttt{bnd.bnd} file, you\nmust specify the host module's (where the original JSP is located)\nBundle Symbolic Name and the host module's exact version to which the\nfragment belongs. In this example, this is configured like this:\n\n\\begin{verbatim}\nFragment-Host: com.liferay.login.web;bundle-version=\"1.0.0\"\n\\end{verbatim}\n\nThen you must provide the new JSP intended to override the original one.\nBe sure to mimic the host module's folder structure when overriding its\nJAR. For this example, since the original JSP is in the folder\n\\texttt{/META-INF/resources/login.jsp}, the new JSP file resides in the\nfolder \\texttt{src/main/resources/META-INF/resources/login.jsp}.\n\nIf needed, you can also target the original JSP following one of the two\npossible naming conventions: \\texttt{original} or \\texttt{portal}. This\npattern looks like\n\n\\begin{verbatim}\n<liferay-util:include\n    page=\"/login.original.jsp\"\n    servletContext=\"<%= application %>\"\n/>\n\\end{verbatim}\n\nor\n\n\\begin{verbatim}\n<liferay-util:include\n    page=\"/login.portal.jsp\"\n    servletContext=\"<%= application %>\"\n/>\n\\end{verbatim}\n\nThis approach can be used to override any application JSP (i.e., JSPs\nresiding in a module). You can also add new JSPs to an existing module\nwith this technique. For more information on other ways to customize\nJSPs, see the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps}{Customizing\nJSPs} articles.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-12}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/overrides/module-jsp-override}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/overrides/module-jsp-override}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/overrides/module-jsp-override}{Maven}\n\\end{itemize}\n\n\\chapter{Resource Bundle Override}\\label{resource-bundle-override}\n\nThis example overrides the default\n\\texttt{javax.portlet.title.com\\_liferay\\_login\\_web\\_portlet\\_LoginPortlet}\nlanguage key for Liferay DXP's default Login portlet. After deploying\nthis sample to Liferay DXP, the Login portlet's \\emph{Sign In} title is\nmodified to display \\emph{Login Portlet Override}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/hook-resourcebundle.png}\n\\caption{The customized Login portlet displays the new language key.}\n\\end{figure}\n\nFor reference, the Login portlet's language keys are stored in the\n\\href{https://github.com/liferay/liferay-portal}{liferay-portal} Github\nrepo's \\texttt{modules/apps/login/login-web/src/main/resources/content}\nfolder.\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-18}\n\nThis sample leverages the\n\\href{https://bnd.bndtools.org/chapters/220-contracts.html}{\\texttt{Provide-Capability}}\nOSGi manifest header.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-18}\n\nThis sample conveys the recommended approach to override a portlet's\nlanguage keys file for any module that is deployed to Liferay DXP's OSGi\nruntime (not applicable to Liferay DXP's core language keys).\n\nThe steps to override a portlet's language keys are\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Provide the new language keys that will override the original ones.\n\\item\n  Prioritize the new module's resource bundle.\n\\end{itemize}\n\nThis sample's \\texttt{src/main/resources/content} folder holds the\nlanguage properties file to override. Since this example's goal is to\noverride only the English keys, the \\texttt{Language\\_en.properties} is\nadded. You can add more language properties files for additional\nlanguage key locales you want to override (e.g.,\n\\texttt{Language\\_en.properties} for Spanish).\n\nOnce your language keys are in place, you must use OSGi manifest headers\nto specify your custom language keys are for the target module. To\ncompliment the target module's resource bundle, you must aggregate your\nresource bundle with the target module's resource bundle. This is done\nby ranking your module first to prioritize its resource bundle over the\ntarget module resource bundle. See this sample's \\texttt{bnd.bnd} as an\nexample for setting the \\texttt{Provide-Capability} OSGi header:\n\n\\begin{verbatim}\nProvide-Capability:\\\n    liferay.resource.bundle;\\\n        resource.bundle.base.name=\"content.Language\",\\\n    liferay.resource.bundle;\\\n        bundle.symbolic.name=com.liferay.login.web;\\\n        resource.bundle.aggregate:String=\"(bundle.symbolic.name=com.liferay.blade.login.web.resource.bundle.override),(bundle.symbolic.name=com.liferay.login.web)\";\\\n        resource.bundle.base.name=\"content.Language\";\\\n        service.ranking:Long=\"2\";\\\n        servlet.context.name=login-web\n\\end{verbatim}\n\nFor more information on the \\texttt{Provide-Capability} header and its\nparts, see the\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys\\#prioritize-your-modules-resource-bundle}{Prioritze\nYour Module's Resource Bundle} section.\n\nThis approach can be used to override any portlet's language keys (i.e.,\n\\texttt{language.properties} files that are inside a module deployed to\nLiferay DXP's OSGi runtime). If you need to override Liferay DXP's core\nlanguage keys, see the\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-global-language-keys}{Overriding\nGlobal Language Keys} article.\n\nFor more information on using a resource bundle to override a module's\nlanguage keys, see the\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys}{Overriding\na Module's Language Keys} tutorial.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-13}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/overrides/login-web-resource-bundle-override}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/overrides/login-web-resource-bundle-override}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/overrides/login-web-resource-bundle-override}{Maven}\n\\end{itemize}\n\n\\chapter{Themes}\\label{themes}\n\nThis section focuses on Liferay sample themes. You can view these sample\nthemes by visiting the \\texttt{themes} folder corresponding to your\npreferred build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/themes}{Gradle\n  sample themes}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/themes}{Liferay\n  Workspace sample themes}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/themes}{Maven\n  sample themes}\n\\end{itemize}\n\nVisit a particular sample page to learn more!\n\n\\chapter{Simple Theme}\\label{simple-theme}\n\nThe Simple Theme sample provides the base files for a theme, using the\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin}{Theme\nBuilder Gradle plugin}. When deploying this sample with no\ncustomizations, a theme based off of the \\texttt{\\_styled} base theme is\ncreated.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme.png}\n\\caption{A theme based off of the Styled base theme is created when the\nTheme Blade sample is deployed to Liferay Portal.}\n\\end{figure}\n\nFor more information on themes, visit the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{Introduction\nto Themes} tutorial.\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-19}\n\nThis sample demonstrates a way to create a simple theme in Liferay DXP.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-19}\n\nTo modify this sample, add the \\texttt{images}, \\texttt{js}, or\n\\texttt{templates} folder, along with your modified files, to the\n\\texttt{src/main/webapp} folder. The sample already provides the\n\\texttt{src/main/resources/resources-importer},\n\\texttt{src/main/webapp/WEB-INF}, and \\texttt{src/main/webapp/css}\nfolders for you. Add your style modifications to the provided\n\\texttt{css/\\_custom.scss} file. For a complete explanation of a theme's\nfiles, see the\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide}{Theme\nReference Guide}.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-14}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/themes/simple-theme}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/wars/simple-theme}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/themes/simple-theme}{Maven}\n\\end{itemize}\n\n\\chapter{Template Context\nContributor}\\label{template-context-contributor}\n\nThe Template Context Contributor sample injects a new variable into\nLiferay DXP's theme context. When deploying this sample with no\ncustomizations, you can use the \\texttt{\\$\\{sample\\_text\\}} variable\nfrom any theme.\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-20}\n\nMany developers prefer using templating frameworks like FreeMarker and\nVelocity, but don't have access to the common objects offered to those\nworking with JSPs. Context contributors allow non-JSP developers an easy\nway to inject variables into their Liferay templates.\n\nThis sample leverages the\n\\href{@product-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/template/TemplateContextContributor.html}{TemplateContextContributor}\nAPI.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-20}\n\nYou can easily modify this sample by customizing its\n\\texttt{BladeTemplateContextContributor.java} Java class. For example,\nthe default context contributor sample provides the\n\\texttt{\\$\\{sample\\_text\\}} variable by injecting it into Liferay's\n\\texttt{contextObjects}, which is a map provided by default to offer\ncommon variables to non-JSP template developers. You can easily inject\nyour own variables into the \\texttt{contextObjects} map usable by any\ntheme deployed to Liferay DXP.\n\nAre you working with templates that aren't themes (e.g., ADTs, DDM\ntemplates, etc.)? You can change the context in which your variables are\ninjected by modifying the \\texttt{property} attribute in the\n\\texttt{@Component} annotation. If you want your variable available for\nall templates, change it to\n\n\\begin{verbatim}\nproperty = {\"type=\" + TemplateContextContributor.TYPE_GLOBAL}\n\\end{verbatim}\n\nFor more information on customizing the Template Context Contributor\nsample to fit your needs, see the Javadoc listed in this sample's\n\\texttt{com.liferay.blade.samples.theme.contributorBladeTemplateContextContributor}\nclass. For more information on context contributors and how to create\nthem in Liferay DXP, visit the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/injecting-additional-context-variables-and-functionality-into-your-theme-templates}{Context\nContributors} tutorial.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-15}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/themes/template-context-contributor}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/themes/template-context-contributor}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/themes/template-context-contributor}{Maven}\n\\end{itemize}\n\n\\chapter{Theme Contributor}\\label{theme-contributor}\n\nThe Theme Contributor sample contributes updates to the UI of the theme\nbody, Control Menu, Product Menu, and Simulation Panel. When deploying\nthis sample with no customizations, the colors of the theme and\naforementioned menus are updated.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-contributor-yellow.png}\n\\caption{Your Liferay DXP pages and menu fonts now have a yellow tint.}\n\\end{figure}\n\nAlso, there's a simple JavaScript update that is provided, which logs a\nmessage to the browser's console window that states \\emph{Hello Blade\nTheme Contributor!}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-contributor-console-output.png}\n\\caption{The message is printed to your browser's console window using\nJavaScript.}\n\\end{figure}\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-21}\n\nThis sample demonstrates a way to contribute updates to a Liferay DXP\ntheme. Theme Contributors let you package UI resources (e.g., CSS and\nJS) independent of a theme to include on a Liferay DXP page.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-21}\n\nTo modify this sample, replace the corresponding JS or SCSS file with\nthe JavaScript or styles that you want, or add your own JS or SCSS\nfiles. For example, this sample provides an update to the Control Menu's\n\\texttt{background-color} in its\n\\texttt{src/main/resources/META-INF/resources/css/blade.theme.contributor/\\_control\\_menu.scss}\nfile:\n\n\\begin{verbatim}\nbody {\n        .control-menu {\n                background-color: darkkhaki;\n        }\n}\n\\end{verbatim}\n\nAll of the SCSS files used in this sample are imported into the main\n\\texttt{blade.theme.contributor.scss} file:\n\n\\begin{verbatim}\n@import \"bourbon\";\n@import \"mixins\";\n\n@import \"blade.theme.contributor/body\";\n@import \"blade.theme.contributor/control_menu\";\n@import \"blade.theme.contributor/product_menu\";\n@import \"blade.theme.contributor/simulation_panel\";\n\\end{verbatim}\n\nIf you add your own \\texttt{SCSS} files, you must add them to the list\nof imports in the \\texttt{blade.theme.contributor.scss} file.\n\nLikewise, the sample \\texttt{blade.theme.contributor.js} logs a message\nto your browser's console window using the following JS logic:\n\n\\begin{verbatim}\nconsole.log('Hello Blade Theme Contributor!');\n\\end{verbatim}\n\nFor more information on Theme Contributors, visit the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site}{Theme\nContributors} tutorial.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-16}\n\nThere are three different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/themes/theme-contributor}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/themes/theme-contributor}{Liferay\n  Workspace}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/themes/theme-contributor}{Maven}\n\\end{itemize}\n\n\\chapter{Ext}\\label{ext}\n\nThis section focuses on Liferay Ext modules. You can view these sample\napps by visiting the \\texttt{ext} folder corresponding to your preferred\nbuild tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/ext}{Gradle\n  sample apps}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/ext}{Liferay\n  Workspace sample apps}\n\\end{itemize}\n\nVisit the sample page to learn more!\n\n\\chapter{Login Web Ext}\\label{login-web-ext}\n\nThe Login Ext Module sample demonstrates how to customize a default\nLiferay module's source code. This example replaces the default\n\\texttt{login.jsp} file in the \\texttt{com.liferay.login.web} bundle by\nadding the text \\emph{Hello from com.liferay.login.web.ext module! 2 + 2\n= 4} to the Sign In form.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/login-ext.png}\n\\caption{The Login Ext module customizes the original Login module.}\n\\end{figure}\n\nIt also prints the following text to the console when you select\n\\emph{Forgot Password} from the Sign In form:\n\n\\begin{verbatim}\nIn com.liferay.login.web.internal.portlet.action.ForgotPasswordMVCRenderCommand render\n\\end{verbatim}\n\nBefore deploying the sample, you must stop the original bundle you\nintend to override. This is because the Ext sample's generated JAR\nincludes the original bundle source plus your modified source files.\nFollow the instructions below to do this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Connect to your portal instance using\n  \\href{/docs/7-1/reference/-/knowledge_base/r/using-the-felix-gogo-shell}{Gogo\n  Shell}.\n\\item\n  Search for the bundle ID of the original bundle to override. To find\n  the \\texttt{com.liferay.login.web} bundle, execute this command:\n\n\\begin{verbatim}\nlb -s | grep com.liferay.login.web\n\\end{verbatim}\n\n  This returns output similar to this:\n\n\\begin{verbatim}\n1580|Active   |   10|com.liferay.login.web (4.0.5)\n\\end{verbatim}\n\n  Make note of the ID (e.g., \\texttt{1580}).\n\\item\n  Stop the bundle:\n\n\\begin{verbatim}\nstop 1580\n\\end{verbatim}\n\\end{enumerate}\n\nOnce the original bundle is stopped, deploy the Ext module. Note that\nyou cannot leverage Blade or Gradle's \\texttt{deploy} command to do\nthis. The \\texttt{deploy} command deploys the module to the\n\\texttt{osgi\\textbackslash{}marketplace\\textbackslash{}override} folder\nby default, which does not configure Ext modules properly for usage. You\nshould build and copy the Ext module's JAR to the \\texttt{deploy} folder\nmanually, or leverage Liferay Dev Studio's\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project\\#liferay-dev-studio}{deployment}\nfeature.\n\n\\section{What API(s) and/or code components does this sample\nhighlight?}\\label{what-apis-andor-code-components-does-this-sample-highlight-22}\n\nThis sample demonstrates how to create an Ext module and configure it to\nreplace a default module bundle.\n\n\\section{How does this sample leverage the API(s) and/or code\ncomponent?}\\label{how-does-this-sample-leverage-the-apis-andor-code-component-22}\n\nYou can create your own Ext module project by\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Declaring the original module name and version.\n\\item\n  Providing the source code that will replace the original.\n\\end{itemize}\n\nTo declare the original module in the \\texttt{build.gradle} file\nproperly (only supports Gradle), you must specify the original module's\nBundle Symbolic Name and the original module's exact version. In this\nexample, this is configured like this:\n\n\\begin{verbatim}\noriginalModule group: \"com.liferay\", name: \"com.liferay.login.web\", version: \"4.0.5\"\n\\end{verbatim}\n\nIf you're leveraging\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace}, you should put your Ext module project in the \\texttt{/ext}\nfolder (default); you can specify a different Ext folder name in\nworkspace's \\texttt{gradle.properties} by adding\n\n\\begin{verbatim}\nliferay.workspace.ext.dir=EXT_DIR\n\\end{verbatim}\n\nIf you are developing an Ext module project in standalone mode (not\nassociated with Liferay Workspace), you must declare the Ext Gradle\nplugin in your \\texttt{build.gradle}:\n\n\\begin{verbatim}\napply plugin: 'com.liferay.osgi.ext.plugin'\n\\end{verbatim}\n\nThen you must provide your own code intended to replace the original\none. \\textbf{Be sure to mimic the original module's folder structure\nwhen overriding its JAR.}\n\nThe following file types can be overlaid with an Ext module:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  CSS\n\\item\n  Java\n\\item\n  JavaScript\n\\item\n  Language files (\\texttt{Language.properties})\n\\item\n  Scss\n\\item\n  Soy\n\\item\n  etc.\n\\end{itemize}\n\nThe\n\\href{https://github.com/liferay/liferay-portal/blob/master/modules/sdk/gradle-plugins/src/main/java/com/liferay/gradle/plugins/LiferayOSGiExtPlugin.java}{Ext\nGradle Plugin} helps compile your code into the JAR. For example,\n\\texttt{.scss} files are compiled into \\texttt{.css} files, which are\nincluded in your module's JAR file artifact. This is done by the\n\\texttt{buildCSS} task.\n\n\\section{Where Is This Sample?}\\label{where-is-this-sample-17}\n\nThere are two different versions of this sample, each built with a\ndifferent build tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/ext/login-web-ext}{Gradle}\n\\item\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/ext/login-web-ext}{Liferay\n  Workspace}\n\\end{itemize}\n\n\\chapter{Segmentation and Personalization\nReference}\\label{segmentation-and-personalization-reference}\n\nBrowse this section's reference articles for additional information on\nthe Segmentation and Personalization framework.\n\n\\chapter{Defining Segmentation\nCriteria}\\label{defining-segmentation-criteria}\n\nThere are three categories for defining Segment criteria:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  User Properties\n\\item\n  Organization Properties\n\\item\n  Session Properties\n\\end{itemize}\n\nThere are several types of information that can be collected by the User\nSegment interface. Some data is entered in text boxes, while others use\nselectors to select specific criteria or tools like a date picker. In\naddition, some fields use an operator, which, depending on the specific\ncontext lets you select the relationship between the user or agent data\nand the criteria:\n\n\\begin{itemize}\n\\item\n  \\emph{equals}\n\\item\n  \\emph{not equals}\n\\item\n  \\emph{greater than}\n\\item\n  \\emph{greater than or equals}\n\\item\n  \\emph{less than}\n\\item\n  \\emph{less than or equals}\n\\item\n  \\emph{contains}\n\\item\n  \\emph{does not contain}\n\\end{itemize}\n\nDepending on the nature of the criteria, the operator selection may\ncontain different combinations. For example, the \\emph{Date} selection\ndescribed below contains options for all the above option except\n\\emph{contains} and \\emph{does not contain}, whereas the \\emph{Email\nAddress} selection has \\emph{equals}, \\emph{not equals}, \\emph{contains}\nand \\emph{does not contain}.\n\nIn between each criteria and each category, you can define an ``and'' or\n``or'' conjunction. For ``and'' all criteria must be true in order for\nthe criteria to be satisfied. With ``or'' it will be true if any of the\ndefined criteria are true. You can also mix operators to create complex\ncases.\n\n\\section{User Properties}\\label{user-properties}\n\nThe following are the criteria available for defining user properties:\n\n\\textbf{Date Modified:} Provides a date picker and an relationship\nselector to select the date that user information was last changed\n\n\\textbf{Email Address:} Provides a text box to enter the email provided\nin the user's\\\\\nprofile.\n\n\\textbf{First Name:} Enter the first name provided in the user's\nprofile.\n\n\\textbf{Group:} Select a site that the user is a member of.\n\n\\textbf{Job Title:} Enter the job title provided in the user's profile.\n\n\\textbf{Last Name:} Enter the last name provided in the user's profile.\n\n\\textbf{Role:} Select a role that the user is a member of.\n\n\\textbf{Screen Name:} Enter the users' screen name.\n\n\\textbf{Team:} Select a team that the user is a member of.\n\n\\textbf{User Group:} Select a user group that the user is a member of.\n\n\\textbf{User:} Select a specific user from a list.\n\n\\textbf{Name:} The full name of the user.\n\n\\section{Organization Properties}\\label{organization-properties}\n\n\\textbf{Date Modified:} Enter the date that the organization information\nwas last modified.\n\n\\textbf{Name:} Enter the name of the organization.\n\n\\textbf{Hierarchy Path:} Enter the name of an ancestor organization.\n\n\\textbf{Organization:} Select a specific organization.\n\n\\textbf{Parent Organization:} Select a specific parent organization.\n\n\\textbf{Type:} Select the type of organization, if organization types\nhave been defined.\n\n\\section{Session Properties}\\label{session-properties}\n\n\\textbf{Browser:} Enter a property from the browser.\n\n\\textbf{Cookies:} Enter the name of a browser cookie.\n\n\\textbf{Device Brand:} Enter the brand name of the device being used.\n\n\\textbf{Device Model:} Enter the model name of the device being used.\n\n\\textbf{Device Screen Resolution Height:} Enter the screen resolution\nheight value.\n\n\\textbf{Device Screen Resolution Width:} Enter the screen resolution\nwidth value.\n\n\\textbf{Language:} Select the current Language.\n\n\\textbf{Last Sign In Date:} Select the date of the user's last sign in.\n\n\\textbf{Local Date:} Select the current date where the user is located.\n\n\\textbf{Referrer URL:} Enter the URL that the user last visited.\n\n\\textbf{Signed In:} Select whether the user is signed in.\n\n\\textbf{URL:} Enter the current URL.\n\n\\textbf{User Agent:} Enter a User Agent property.\n\n\\chapter{Tooling}\\label{tooling}\n\nYou can write code for Liferay DXP using any standard toolset. Liferay\nis tool-agnostic, which frees you to work with whatever you're already\nproductive using.\n\nLiferay has also created its own tools that streamline Liferay DXP\ndevelopment. These tools integrate with popular build environments\n(e.g., Gradle, Maven, and NodeJS). They include\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}: a\n  command line interface used to build and manage Liferay Workspaces and\n  Liferay DXP projects. This CLI is intended for Gradle or Maven\n  development.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\n  Workspace}: a generated Gradle/Maven environment built to hold and\n  manage Liferay DXP projects.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\n  Dev Studio}: an Eclipse-based IDE supporting development for Liferay\n  DXP.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/intellij}{Liferay\n  IntelliJ Plugin}: a plugin providing support for Liferay DXP\n  development with IntelliJ IDEA.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-generator}{Liferay\n  Theme Generator}: a generator that creates themes, layouts templates,\n  and themelets for Liferay DXP development.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/js-generator}{Liferay JS\n  Generator}: a generator that creates JavaScript portlets with\n  JavaScript tooling.\n\\end{itemize}\n\nLiferay also provides a plethora of\n\\href{/docs/7-2/reference/-/knowledge_base/r/gradle-plugins}{Gradle} and\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven-plugins}{Maven\nplugins} you can apply to your projects. Many of these are already built\ninto tools such as Liferay Workspace.\n\nWant samples or predefined project templates? Liferay has you covered\nwith 30+\n\\href{/docs/7-2/reference/-/knowledge_base/r/project-templates}{project\ntemplates} and many more\n\\href{/docs/7-2/reference/-/knowledge_base/r/sample-projects}{project\nsamples}.\n\nIf you're a newbie looking for the best development tool for Liferay\nDXP, or even a seasoned veteran looking for a tool you may like more\nthan your current setup, this section answers your tooling questions.\n\n\\chapter{Creating a Project}\\label{creating-a-project}\n\nLiferay provides many project templates you can use to generate starter\nprojects formatted in an opinionated way. Visit the\n\\href{/docs/7-2/reference/-/knowledge_base/r/project-templates}{Project\nTemplates} reference section for more information on the available\nproject templates. Each project template has different configurable\noptions, so be sure to research a project template before generating it.\n\nYou can use your desired tool to generate a project. The following tools\nare preconfigured for Liferay project generation:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\n  Dev Studio}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/intellij}{Liferay\n  IntelliJ Plugin}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/maven}{Maven}\n\\end{itemize}\n\nIt's recommended to create Liferay projects within a\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace}. Most tools, however, support creating projects in a\nstandalone environment (except for IntelliJ). Visit the appropriate\nsection to learn how to create a project with the highlighted tool.\n\n\\section{Blade CLI}\\label{blade-cli}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Print the available project templates by executing this:\n\n\\begin{verbatim}\nblade create -l\n\\end{verbatim}\n\n  Note the project template you want to generate; you'll use it in the\n  next step.\n\\item\n  Run the following command to create a Gradle project with Blade CLI:\n\n\\begin{verbatim}\nblade create -t [projectTemplate] [option1] [option2] ... [optionN] [projectName]\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you want to generate a project for a previous version\n(e.g., Liferay Portal 7.0), you can specify this using the \\texttt{-v}\nflag. For example, to create a project for Liferay Portal 7.0, you would\ninclude \\texttt{-v\\ 7.0} in your create command sequence.\n\n\\noindent\\hrulefill\n\nThe available configuration options are documented for each\n\\href{/docs/7-2/reference/-/knowledge_base/r/project-templates}{project\ntemplate}. Run \\texttt{blade\\ create\\ -\\/-help} for the entire list of\navailable options.\n\n\\section{Liferay Dev Studio}\\label{liferay-dev-studio}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{File} → \\emph{New} → \\emph{Liferay Module Project}.\n\\item\n  Specify the project name, location, build type, Liferay DXP version,\n  and template type.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/liferay-project-wizard.png}\n  \\caption{The New Liferay Module Project wizard offers project\n  templates for JAR and WAR-based projects.}\n  \\end{figure}\n\\item\n  Click \\emph{Next} and you're given additional configuration options\n  based on the project template you selected. For example, if you\n  selected a template that requires a component class, you must\n  configure it in the wizard.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/component-class-wizard.png}\n  \\caption{Specify your component class's details in the Portlet\n  Component Class Wizard.}\n  \\end{figure}\n\n  You can specify your component class's name, package name, and its\n  properties. The properties you assign are the ones found in the\n  \\texttt{@Component} annotation's \\texttt{property\\ =\\ \\{...\\}}\n  assignment.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You can also create a new component class for a pre-existing\n module project. Navigate to *File* &rarr; *New* &rarr; *Liferay Component\n Class*. This is a similar wizard to the previous component class wizard,\n except you can select a component class template. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\tightlist\n\\item\n  Click \\emph{Finish} to create your project.\n\\end{enumerate}\n\n\\section{Liferay IntelliJ Plugin}\\label{liferay-intellij-plugin}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{File} → \\emph{New} → \\emph{Liferay Module}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/intellij-new-liferay-module.png}\n  \\caption{Selecting \\emph{Liferay Module} opens the New Liferay Modules\n  wizard.}\n  \\end{figure}\n\\item\n  Select the project you want to create.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/intellij-modules.png}\n  \\caption{Choose the project template to create your module.}\n  \\end{figure}\n\\item\n  Configure your project's SDK (i.e., JDK), package name, class name,\n  and service name, if necessary. Then click \\emph{Next}.\n\\item\n  Give your project a name. Then click \\emph{Finish}.\n\\end{enumerate}\n\n\\section{Maven}\\label{maven}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Execute the following Maven command:\n\n\\begin{verbatim}\nmvn archetype:generate -Dfilter=liferay\n\\end{verbatim}\n\\item\n  Select the archetype you want to leverage and proceed through the\n  configuration prompts.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Maven projects can also be generated using Blade CLI.\nFollow \\hyperref[blade-cli]{Blade CLI's} project creation instructions\nand insert the \\texttt{-b\\ maven} parameter in the Blade command.\n\n\\noindent\\hrulefill\n\nArchetypes prefixed with\n\\texttt{com.liferay.project.templates.{[}TYPE{]}} or\n\\texttt{com.liferay.faces.archetype:{[}TYPE{]}} are compatible with 7.0.\nAll other Liferay archetypes are legacy archetypes targeted for previous\nversions of Liferay DXP.\n\nSee Maven's\n\\href{http://maven.apache.org/archetype/maven-archetype-plugin/generate-mojo.html}{Archetype\nGeneration} documentation for further details on how to modify the Maven\narchetype generation process.\n\n\\chapter{Deploying a Project}\\label{deploying-a-project}\n\nDeploying a project to Liferay DXP can be completed using your tool of\nchoice. The following tools are preconfigured (or can be easily\nconfigured) for Liferay project generation:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}\n\\item\n  \\href{https://gradle.org/}{Gradle}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\n  Dev Studio}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/intellij}{Liferay\n  IntelliJ Plugin}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/maven}{Maven}\n\\end{itemize}\n\nThe deployment process is the same across all tools; the deployment\ncommand/action builds and deploys your project based on the build tool's\ndeployment configuration. For example, leveraging Blade CLI in a default\nGradle Liferay Workspace uses the underlying Gradle deployment\nconfiguration. The build tool's deployment configuration is found by\nreading the Liferay Home folder. The Liferay Home folder is\npreconfigured in most cases; if it's not, ways to configure it are\nincluded below. All tools support JAR and WAR-style project deployment.\n\nIt's recommended to deploy Liferay projects within a\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace}. Most tools, however, support deploying projects in a\nstandalone environment (except for IntelliJ). Visit the appropriate\nsection to learn how to deploy a project with the highlighted tool.\n\n\\section{Blade CLI}\\label{blade-cli-1}\n\nThis is the recommended way to deploy Gradle and Maven projects in a\nLiferay Workspace via command line. Blade CLI is leveraged by Dev Studio\nand IntelliJ too.\n\nRun this command to deploy your project:\n\n\\begin{verbatim}\nblade deploy\n\\end{verbatim}\n\nIf you prefer not to use your underlying build tool's (Gradle or Maven)\nmodule deployment configuration, and instead, you want to deploy\nstraight to Liferay DXP's OSGi container, run this command:\n\n\\begin{verbatim}\nblade deploy -l\n\\end{verbatim}\n\n\\section{Gradle}\\label{gradle}\n\nDeploying with pure Gradle is not recommended unless you prefer to\ndevelop outside of a Liferay Workspace. \\hyperref[blade-cli]{Blade CLI}\nis a better tool for deploying Liferay Gradle projects in most cases.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Apply the Liferay Gradle plugin in your project's\n  \\texttt{build.gradle} file:\n\n\\begin{verbatim}\napply plugin: \"com.liferay.plugin\"\n\\end{verbatim}\n\\item\n  Extend the Liferay extension object to set your Liferay Home and\n  \\texttt{deploy} folder:\n\n\\begin{verbatim}\nliferay {\n    liferayHome = \"../../../../liferay-ce-portal-7.1.1-ga2\"\n    deployDir = file(\"${liferayHome}/deploy\")\n}\n\\end{verbatim}\n\\item\n  Run this command to deploy your project:\n\n\\begin{verbatim}\n./gradlew deploy\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Liferay Dev Studio}\\label{liferay-dev-studio-1}\n\nThese steps assume you've\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-a-liferay-server-in-dev-studio}{installed\na Liferay server in Dev Studio}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Right-click the server from the Servers window and select \\emph{Add\n  and Remove\\ldots{}}.\n\\item\n  Add the project(s) you'd like to deploy from the Available window to\n  the Configured window. Then click \\emph{Finish}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/add-and-remove-ide.png}\n  \\caption{Using the this deployment method is convenient when deploying\n  multiple projects.}\n  \\end{figure}\n\\item\n  Verify your project builds, deploys, and starts successfully by\n  viewing the results in the Console window.\n\\end{enumerate}\n\nDev Studio's deployment mechanism executes the \\texttt{watch} task\nbehind the scenes. For more information on what to expect from the\n\\texttt{watch} task, see\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{this article}.\n\n\\section{Liferay IntelliJ Plugin}\\label{liferay-intellij-plugin-1}\n\nThese steps assume you've\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-a-server-in-intellij}{installed\na Liferay server in IntelliJ}. A configured Liferay Workspace is\nrequired to create and deploy Liferay projects in IntelliJ.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Right-click your project from within the Liferay Workspace folder\n  structure and select \\emph{Liferay} → \\emph{Deploy}.\n\n  This automatically loads a build progress window viewable at the\n  bottom of your IntelliJ instance.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/intellij-project-build.png}\n  \\caption{Verify that your project built successfully.}\n  \\end{figure}\n\\item\n  Verify that your project builds successfully from the build progress\n  window. Then navigate back to your server's window and confirm it\n  starts in your configured Liferay DXP instance.\n\\end{enumerate}\n\n\\section{Maven}\\label{maven-1}\n\nIf you're developing your project in a Liferay Workspace, skip to step\n3.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the following plugin configuration to your Liferay Maven project's\n  parent \\texttt{pom.xml} file.\n\n\\begin{verbatim}\n<build>\n    <plugins>\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.bundle.support</artifactId>\n            <version>3.4.1</version>\n            <executions>\n                <execution>\n                    <id>deploy</id>\n                    <goals>\n                        <goal>deploy</goal>\n                    </goals>\n                    <phase>pre-integration-test</phase>\n                </execution>\n            </executions>\n        </plugin>\n    </plugins>\n</build>\n\\end{verbatim}\n\n  This POM configuration applies Liferay's\n  \\href{/docs/7-2/reference/-/knowledge_base/r/bundle-support-plugin}{Bundle\n  Support plugin}. This plugin is applied in Liferay Workspace by\n  default. The Bundle Support configuration defines the\n  \\href{https://maven.apache.org/guides/mini/guide-configuring-plugins.html\\#Using_the_executions_Tag}{executions}\n  tag, which configures the Bundle Support plugin to run during the\n  \\texttt{pre-integration-test} phase of your Maven project's build\n  lifecycle. The\n  \\href{http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html\\#A_Build_Phase_is_Made_Up_of_Plugin_Goals}{\\texttt{deploy}}\n  goal is defined for that lifecycle phase.\n\\item\n  Define your Liferay home folder in your POM. You can do this by adding\n  the following logic within the \\texttt{plugin} tags, but outside of\n  the \\texttt{execution} tags:\n\n\\begin{verbatim}\n<configuration>\n    <liferayHome>C:/liferay/liferay-ce-portal-7.1-ga1</liferayHome>\n</configuration>\n\\end{verbatim}\n\\item\n  Run this command to deploy your project:\n\n\\begin{verbatim}\nmvn verify\n\\end{verbatim}\n\\end{enumerate}\n\n\\chapter{Blade CLI}\\label{blade-cli-2}\n\n\\href{https://github.com/liferay/liferay-blade-cli/}{Blade CLI} is a\ncommand line tool that makes it easy for Liferay developers to create,\nmanage, and deploy Liferay projects (Gradle or Maven). Blade CLI can\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Create Liferay projects usable in any IDE or development environment\n\\item\n  Create/manage Liferay DXP instances\n\\item\n  Deploy Liferay projects\n\\item\n  And more\n\\end{itemize}\n\nThe table below describes all Blade CLI commands for the latest Blade\nCLI release.\n\nCommand \\textbar{} Description \\texttt{convert} \\textbar{} Converts a\nPlugins SDK plugin project to a Gradle Workspace project. See the\n\\href{/docs/7-1/reference/-/knowledge_base/r/migrating-traditional-plugins-to-workspace-web-applications\\#running-the-migration-command}{Running\nthe Migration Command} command for details. \\texttt{create} \\textbar{}\nCreates a new Liferay project from available templates. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project\\#blade-cli}{Creating\na Project section for Blade CLI} for more information. \\texttt{deploy}\n\\textbar{} Builds and deploys projects to Liferay DXP. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project\\#blade-cli}{Deploying\na Project section for Blade CLI} for more information.\n\\texttt{extension\\ install} \\textbar{} Installs an extension into Blade\nCLI. \\texttt{extension\\ uninstall} \\textbar{} Uninstalls an extension\nfrom Blade CLI. \\texttt{gw} \\textbar{} Executes a Gradle command using\nthe Gradle Wrapper, if detected (e.g., \\texttt{blade\\ gw\\ tasks}).\n\\texttt{help} \\textbar{} Provides information for Blade CLI's commands.\n\\texttt{init} \\textbar{} Initializes a new Liferay Workspace. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-liferay-workspace\\#blade-cli}{Creating\na Liferay Workspace} article for more information. \\texttt{samples}\n\\textbar{} Generates a sample project. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/generating-project-samples-with-blade-cli}{Generating\nProject Samples with Blade CLI} article for more information.\n\\texttt{server\\ init} \\textbar{} Initializes the Liferay server\nconfigured in Liferay Workspace's \\texttt{gradle.properties} file. Set\nthe \\texttt{liferay.workspace.bundle.url} property to configure the\nserver to initialize. \\texttt{server\\ start} \\textbar{} Starts the\nLiferay server in the background. You can add the \\texttt{-d} flag to\nstart the server in debug mode. Debug mode can be customized by adding\nthe \\texttt{-p} tag to set the custom remote debugging port (defaults\nare \\texttt{8000} for Tomcat and \\texttt{8787} for Wildfly) and/or the\nboolean \\texttt{-s} tag to set whether you want to suspend the started\nserver until the debugger is connected. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/managing-your-liferay-server-with-blade-cli}{Managing\nYour Liferay Server with Blade CLI} article for more information.\n\\texttt{server\\ stop} \\textbar{} Stops the Liferay server.\n\\texttt{server\\ run} \\textbar{} Starts the Liferay server in the\nforeground. See the \\texttt{server\\ start} property for more\ninformation. \\texttt{sh} \\textbar{} Connects to Liferay DXP, executes\nsucceeding Gogo command, and returns output. For example,\n\\texttt{blade\\ sh\\ lb} lists Liferay DXP's bundles using the Gogo shell.\nSee the\n\\href{/docs/7-2/reference/-/knowledge_base/r/managing-your-liferay-server-with-blade-cli}{Managing\nYour Liferay Server with Blade CLI} article for more information.\n\\texttt{update} \\textbar{} Updates Blade CLI to the latest version. See\nthe\n\\href{/docs/7-2/reference/-/knowledge_base/r/updating-blade-cli}{Updating\nBlade CLI} article for details. \\texttt{upgradeProps} \\textbar{}\nAnalyzes your old \\texttt{portal-ext.properties} and your newly\ninstalled 7.x server to show you properties moved to OSGi configuration\nfiles or removed from the product. \\texttt{watch} \\textbar{} Watches for\nchanges to a deployed project and automatically redeploys it when\nchanges are detected. This command does not rebuild your project and\ncopy it to Portal every time a change is detected, but rather, installs\nit into the runtime as a reference. This means that the Portal does not\nmake a cached copy of the project. This allows the Portal to see changes\nthat are made to your project's files immediately. When you cancel the\n\\texttt{watch} task, your module is uninstalled automatically. The\n\\texttt{blade\\ deploy\\ -w} command works similarly to\n\\texttt{blade\\ watch}, except it manually recompiles and deploys your\nproject every time a change is detected. This causes slower update\ntimes, but does preserve your deployed project in Portal when it's shut\ndown. \\texttt{version} \\textbar{} Displays version information about\nBlade CLI.\n\nFor information on command options, run the command with the\n\\texttt{-\\/-help} flag (e.g., \\texttt{blade\\ samples\\ -\\/-help}).\n\nContinue on to learn about leveraging Blade CLI to create and test\nLiferay DXP instances and projects.\n\n\\chapter{Installing Blade CLI}\\label{installing-blade-cli}\n\nYou can install Blade CLI using the Liferay Project SDK installer. This\ninstalls JPM and Blade CLI into your user home folder and optionally\ninitializes a\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace} folder. Do not use the Liferay Project SDK installer to\nupdate Blade CLI; instead, follow the instructions for\n\\href{/docs/7-2/reference/-/knowledge_base/r/updating-blade-cli}{updating\nBlade CLI}.\n\nIf you must configure proxy settings for Blade CLI, follow the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-blade-cli-with-proxy-requirements}{Installing\nBlade CLI with Proxy Requirements} instructions instead.\n\nFollow the steps below to download and install Blade CLI:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Download the latest\n  \\href{https://sourceforge.net/projects/lportal/files/Liferay\\%20IDE/}{Liferay\n  Project SDK installer} that corresponds with your operating system\n  (e.g., Windows, MacOS, or Linux). The Project SDK installer is listed\n  under \\emph{Liferay IDE}, so the folder versions are based on IDE\n  releases. You can select an installer without Dev Studio DXP if you\n  don't intend to use it. The Project SDK installer is available for\n  versions 3.2.0+. Do \\textbf{not} select the large green download\n  button; this downloads Liferay Portal instead.\n\\item\n  Run the installer.\n\\item\n  Select the Java Runtime to use with Blade CLI. Then click \\emph{OK}.\n\\item\n  Click \\emph{Next} to step through the installer's introduction.\n\\item\n  If you'd like to initialize a Liferay Workspace, you can set its\n  location.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/blade-installer-workspace-init.png}\n  \\caption{Determine where your Liferay Workspace should reside, if you\n  want one.}\n  \\end{figure}\n\n  Select \\emph{Don't initialize Liferay Workspace directory} if you only\n  want to install Blade CLI. Then click \\emph{Next}.\n\\item\n  If you initialized a Liferay Workspace folder, an additional option\n  appears for selecting the Liferay product type to use with your\n  workspace. Choose the product type and click \\emph{Next}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/installer-workspace-type.png}\n  \\caption{Select the product version you'll use with your Liferay\n  Workspace.}\n  \\end{figure}\n\\item\n  Click \\emph{Next} to begin installing Blade CLI/Liferay Workspace on\n  your computer.\n\\end{enumerate}\n\nThat's it! Blade CLI is installed on your machine! If you specified a\nlocation to initialize a Liferay Workspace folder, that is also\navailable.\n\nIf Blade CLI doesn't work properly on your machine, visit the\n\\href{/docs/7-2/reference/-/knowledge_base/r/common-errors-with-blade-cli}{Common\nErrors with Blade CLI} article for solutions to common problems.\n\n\\chapter{Installing Blade CLI with Proxy\nRequirements}\\label{installing-blade-cli-with-proxy-requirements}\n\nIf you have proxy server requirements and want to use Blade CLI, you\nmust configure your http(s) proxy for it using JPM:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Install JPM and Blade CLI using the Liferay Project SDK installer.\n  Read the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-blade-cli}{Installing\n  Blade CLI} tutorial for more details.\n\\item\n  Execute the following command to configure your proxy requirements for\n  Blade CLI:\n\n\\begin{verbatim}\njpm command --jvmargs \"-Dhttp(s).proxyHost=[your proxy host] -Dhttp(s).proxyPort=[your proxy port]\" jpm\n\\end{verbatim}\n\\end{enumerate}\n\nExcellent! You've configured Blade CLI with your proxy settings using\nJPM.\n\n\\chapter{Managing Your Liferay Server with Blade\nCLI}\\label{managing-your-liferay-server-with-blade-cli}\n\nYou can manage a Liferay server using Blade CLI. Managing a server with\nBlade CLI should be done in a\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace}.\n\nBlade CLI has commands for installing, starting, stopping, inspecting,\nand modifying a Liferay server:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Make sure you've created a Liferay Workspace. See the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-liferay-workspace\\#blade-cli}{Creating\n  a Liferay Workspace} article for more information.\n\\item\n  Initialize a Liferay server by running\n\n\\begin{verbatim}\nblade server init\n\\end{verbatim}\n\n  This downloads the Liferay DXP bundle set in your workspace's\n  \\texttt{gradle.propeties} file. See the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/adding-a-liferay-bundle-to-liferay-workspace}{Adding\n  a Liferay Bundle to Workspace} article for more information.\n\n  You can initialize a server based on a\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace\\#testing-projects}{defined\n  environment} by running the following command:\n\n\\begin{verbatim}\nblade server init --environment [ENVIRONMENT]\n\\end{verbatim}\n\n  For example, you could pass in the \\texttt{uat} variable to generate a\n  bundle with the configs set in the \\texttt{configs/uat} workspace\n  folder.\n\\item\n  Start your Liferay server (Tomcat or Wildfly/JBoss) by running\n\n\\begin{verbatim}\nblade server start\n\\end{verbatim}\n\n  This starts the server in the background. You can tail the logs by\n  adding the \\texttt{-t} flag. If you prefer starting the server in the\n  foreground, run \\texttt{blade\\ server\\ run}. Additionally, if you\n  prefer starting the server in debug mode, add the \\texttt{-d} flag.\n  See the \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade\n  CLI} article for additional flags you can set when starting your\n  Liferay server.\n\\item\n  Examine your server's OSGi container by using Blade CLI's \\texttt{sh}\n  command, which provides access to your server using the Felix Gogo\n  shell. For example, to check if you successfully deployed your\n  application from the previous section, you could run:\n\n\\begin{verbatim}\nblade sh lb\n\\end{verbatim}\n\n  Your output lists a long list of modules that are active/installed in\n  your server's OSGi container.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/blade-sh.png}\n  \\caption{Blade CLI accesses the Gogo shell script to run the\n  \\texttt{lb} command.}\n  \\end{figure}\n\n  You can run any Gogo command using \\texttt{blade\\ sh}. This command\n  requires\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes}{Developer\n  Mode} to be enabled. Developer Mode is enabled in workspace by\n  default. See the\n  \\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Using\n  the Felix Gogo Shell} section for more information on this tool.\n\\item\n  Once you're finished modifying your Liferay bundle, you can package it\n  as a sharable file by running this command:\n\n\\begin{verbatim}\nblade gw distBundle[Zip|Tar]\n\\end{verbatim}\n\n  This lets you create a ZIP or TAR file to share with others. This\n  option is only available with Gradle at this time. The above command\n  leverages Blade CLI's \\texttt{gw} option, which executes the project's\n  Gradle wrapper.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You can avoid deploying a module inside your workspace's\n `modules/` folder when `distBundle[Zip|Tar]` is called by adding the\n following snippet to your workspace's `build.gradle` file:\n \n ```groovy\n distBundle {\n     exclude \"com.liferay.jsp.spy*.jar\"\n }\n ```\n \n You can replace the JAR name above with the module JAR you want to exclude.\n This is useful for those who want to have a module in their workspace that\n is used for development or debug purposes only, and it should not be deployed\n to production. This works for Gradle builds only at this time.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n<!-- TODO: Add way for producing a distributable workspace using Blade, when\navailable. It can only be done currently with ./gradlew distBundle[Zip|Tar].\n-->\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{5}\n\\item\n  Turn off your Liferay server:\n\n\\begin{verbatim}\nblade server stop\n\\end{verbatim}\n\\end{enumerate}\n\nTo reference all of Blade CLI's available options, see the\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}\narticle.\n\nAwesome! You learned how to interact with Liferay DXP using Blade CLI.\n\n\\chapter{Generating Project Samples with Blade\nCLI}\\label{generating-project-samples-with-blade-cli}\n\nLiferay provides many useful\n\\href{https://github.com/liferay/liferay-blade-samples}{sample projects}\nfor those interested in learning best practices for Liferay DXP\nprojects. You can learn more about these samples by visiting\n\\href{/docs/7-2/reference/-/knowledge_base/r/sample-projects}{Sample\nProjects}.\n\nRather than cloning the repository, you can generate these samples using\nBlade CLI for convenience.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  List the available sample projects:\n\n\\begin{verbatim}\nblade samples\n\\end{verbatim}\n\n  Note the sample project you want to generate; you'll use it in the\n  next step.\n\\item\n  Run the following command to generate a sample project:\n\n\\begin{verbatim}\nblade samples <NAME>\n\\end{verbatim}\n\n  For example, to generate the\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/master/gradle/apps/ds-portlet}{portlet-ds}\n  sample, execute\n\n\\begin{verbatim}\nblade samples ds-portlet\n\\end{verbatim}\n\n  The sample is generated in the current folder.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Interested in generating legacy versions of Blade\nsamples? Pass in the \\texttt{-v} param followed by the Liferay DXP\nversion to target. For example,\n\n\\begin{verbatim}\nblade samples -v 7.0 ds-portlet\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nAwesome! You've successfully generated a Liferay sample project using\nBlade CLI!\n\n\\chapter{Updating Blade CLI}\\label{updating-blade-cli}\n\nBlade CLI is updated frequently, so you should update your Blade CLI\nenvironment for new features. You can check the released versions of\nBlade CLI on Nexus by inspecting the\n\\href{https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/blade/com.liferay.blade.cli/}{\\texttt{com.liferay.blade.cli}}\nartifact. You can check your current installed version by running\n\\texttt{blade\\ version}.\n\nTo update your Blade CLI installation to the latest stable version, run\n\n\\begin{verbatim}\nblade update\n\\end{verbatim}\n\nAlthough Blade CLI is frequently released, if you want bleeding edge\nfeatures not yet available, you can install the latest snapshot version:\n\n\\begin{verbatim}\nblade update -s\n\\end{verbatim}\n\nThis pulls the latest snapshot version of Blade CLI and installs it to\nyour local machine. Running \\texttt{blade\\ version} after installing a\nsnapshot displays output similar to this:\n\n\\begin{verbatim}\nblade version 3.3.1.SNAPSHOT201811301746\n\\end{verbatim}\n\nBe careful; snapshot versions are unstable and should only be used for\nexperimental purposes.\n\nAwesome! You've successfully learned how to update Blade CLI.\n\n\\chapter{Converting Plugins SDK Projects with Blade\nCLI}\\label{converting-plugins-sdk-projects-with-blade-cli}\n\nBlade CLI can automatically migrate a Plugins SDK project to a Liferay\nWorkspace. During the process, the Ant-based Plugins SDK project is\ncopied to the applicable workspace folder based on its project type\n(e.g., \\texttt{wars}) and is converted to a Gradle-based Liferay\nWorkspace project. This drastically speeds up the migration process when\nupgrading to a Liferay Workspace from a legacy Plugins SDK.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} There is no Maven command for the migration process yet,\nso you must complete it manually for Maven-based workspaces.\n\n\\noindent\\hrulefill\n\nTo copy your Plugins SDK project and convert it to Gradle, use the Blade\n\\texttt{convert} command:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the root folder of your workspace in a command line tool.\n\\item\n  Execute the following command:\n\n\\begin{verbatim}\nblade convert -s [PLUGINS_SDK_PATH] [PLUGINS_SDK_PROJECT_NAME]\n\\end{verbatim}\n\n  You must provide the path of the Plugins SDK your project resides in\n  and the project name you want to convert. If you prefer converting all\n  the Plugins SDK projects at once, replace the project name variable\n  with \\texttt{-a} (i.e., specifying all plugins).\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** If the `convert` task doesn't work as described above, you may\n need to update your Blade CLI version. See the\n [Updating Blade CLI](/docs/7-2/reference/-/knowledge_base/r/updating-blade-cli)\n article for more information.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nThis Gradle conversion process also works for themes; they're converted to\nautomatically leverage NodeJS. If you're converting a Java-based theme, add\nthe `-t` option to your command too. This will incorporate the\n[Theme Builder](/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin)\nGradle plugin for the theme instead. For more information on upgrading\n6.2 themes, see the\n[Upgrade a 6.2 Theme to 7.2](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-6-2-themes-to-7-2).\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When converting a Service Builder project, the\n\\texttt{convert} task automatically extracts the project's service\ninterfaces and implementations into OSGi modules (i.e., \\emph{-impl and\n}-api) and places them in the workspace's \\texttt{modules} folder. Your\nportlet and controller logic remain a WAR and reside in the\n\\texttt{wars} folder.\n\n\\noindent\\hrulefill\n\nYour project is successfully converted to a Gradle-based workspace\nproject! Great job!\n\n\\chapter{Extending Blade CLI}\\label{extending-blade-cli}\n\nYou can extend Blade in three different ways:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-custom-commands-for-blade-cli}{Creating\n  Custom Commands for Blade CLI}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-custom-project-templates-for-blade-cli}{Creating\n  Custom Project Templates for Blade CLI}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-new-extensions-for-blade-cli}{Installing\n  New Extensions for Blade CLI}\n\\end{itemize}\n\nThere are a few use cases to consider when extending Blade CLI. For\nexample, if you only want to add a new command that applies globally to\nall types of workspaces, you can create and install a new custom command\nas explained in the links above.\n\nAlternatively, you may want a set of custom commands that only apply to\na specific workspace environment. Normally, Liferay developers who use\nBlade CLI run a series of Blade commands that make sense in the\n\\emph{default} Liferay Workspace. What if the workspace, however, should\nsupport a containerized environment (e.g., Docker) or some other\nspecialized environment? The commands used in the development workflow\nmust complete the workflow differently.\n\nTo customize Blade CLI's development workflow, you must create a Blade\n\\emph{profile}. Blade profiles \\emph{override} existing Blade commands\nor add \\emph{new} commands in a preserved environment that can be\napplied to any Liferay Workspace. For example, \\texttt{blade\\ init} for\na profile \\texttt{myprofile} would override the default \\texttt{init}\ncommand to do something before/after the normal \\texttt{init} command.\nFor more information, see\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-blade-profile}{Creating\na Blade Profile}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Blade CLI leverages the profile system internally for\nMaven support. The Maven specific code is stored in an extension JAR and\nembedded inside the default Blade JAR.\n\n\\noindent\\hrulefill\n\nContinue on to learn more!\n\n\\chapter{Creating Custom Commands for Blade\nCLI}\\label{creating-custom-commands-for-blade-cli}\n\nTo create a custom command for Blade CLI, follow these steps:\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} This article creates a Gradle-based command project.\nThese steps can be completed for a Maven-based project too.\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  a generic OSGi module}.\n\\item\n  You'll leverage \\href{http://jcommander.org/}{JCommander} and the\n  Blade CLI API to create your custom command. Add these dependencies in\n  your build file. For example, a \\texttt{build.gradle} file's\n  \\texttt{dependencies} block looks like this:\n\n\\begin{verbatim}\ndependencies {\n    compileOnly group: \"com.beust\", name: \"jcommander\", version: \"1.72\"\n    compileOnly group: \"com.liferay.blade\", name: \"com.liferay.blade.cli\", version: \"latest.release\"\n}\n\\end{verbatim}\n\\item\n  Build a Command class by extending the\n  \\href{https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BaseCommand.java}{\\texttt{BaseCommand}}\n  class:\n\n\\begin{verbatim}\nimport com.liferay.blade.cli.command.BaseCommand;\n\npublic class Hello extends BaseCommand<HelloArgs> {\n\n    @Override\n    public void execute() throws Exception {\n        HelloArgs helloArgs = getArgs();\n\n        getBladeCLI().out(\"Hello \" + helloArgs.getName());\n    }\n\n    @Override\n    public Class<HelloArgs> getArgsClass() {\n        return HelloArgs.class;\n    }\n\n}\n\\end{verbatim}\n\n  This registers your new command with Blade. You must define the\n  \\texttt{execute()} command for all classes extending\n  \\texttt{BaseCommand}. The \\texttt{BaseCommand} class expects an\n  arguments class as its parameter. You'll create this next.\n\\item\n  Create a class that holds your command's arguments:\n\n\\begin{verbatim}\nimport com.beust.jcommander.Parameter;\nimport com.beust.jcommander.Parameters;\n\nimport com.liferay.blade.cli.command.BaseArgs;\n\n@Parameters(commandDescription = \"Executes a hello command\", commandNames = \"hello\")\npublic class HelloArgs extends BaseArgs {\n\n    public String getName() {\n        return _name;\n    }\n\n    @Parameter(description = \"The name to say hello to\", names = \"--name\", required = true)\n    private String _name;\n\n}\n\\end{verbatim}\n\n  This class extends the\n  \\href{https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BaseArgs.java}{\\texttt{BaseArgs}}\n  class. Notice that the class declaration has the \\texttt{@Parameters}\n  JCommander annotation. This sets your command's description and name.\n  The \\texttt{@Parameter} annotation applied to the private string\n  \\texttt{\\_name} sets how the command's parameter is called and whether\n  it's required.\n\\item\n  Since Blade looks for custom commands using the\n  \\texttt{com.liferay.blade.cli.command.BaseCommand} service interface,\n  you must use a standard JRE service loader mechanism to finish\n  registering your new command with Blade CLI.\n\n  Create a file named \\texttt{com.liferay.blade.cli.command.BaseCommand}\n  in the \\texttt{src/main/resources/META-INF/services/} folder. This\n  class should list all of your custom commands' fully qualified class\n  names:\n\n\\begin{verbatim}\ncom.liferay.extensions.sample.command.Hello\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Java's Service Loader Interface (SPI) is used to load the\n fully qualified classes in the `META-INF/services` folder. You can learn\n more about SPIs\n [here](https://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html).\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{5}\n\\tightlist\n\\item\n  Generate the extension's JAR file (e.g., \\texttt{gradlew\\ build}).\n\\end{enumerate}\n\nAwesome! You've created a custom command! You can deploy multiple custom\ncommands in a single JAR, so you can continue adding custom command\nprojects to this module, if desired. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-new-extensions-for-blade-cli}{Installing\nNew Extensions} article to install the command (JAR) to Blade CLI.\n\nYou can examine a working custom command project\n\\href{https://github.com/liferay/liferay-blade-cli/blob/master/extensions/sample-command}{here}.\n\n\\chapter{Creating Custom Project Templates for Blade\nCLI}\\label{creating-custom-project-templates-for-blade-cli}\n\nBlade comes with 32+ project templates, but many times you may feel that\nthose are too simple or don't fit the need for your development team.\nYou can create new custom project templates that fit your team's\nworkflow and have Blade use them instead.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} An extension JAR can only contain one template. If\nyou have multiple templates, each must be deployed in a separate JAR.\n\n\\noindent\\hrulefill\n\nImplementing a custom project template should mimic that of a Maven\narchetype. The best way to illustrate this is by visualizing a\n\\href{https://github.com/liferay/liferay-blade-cli/tree/master/extensions/sample-template}{sample\ntemplate}'s folder structure:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{src/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{main/resources/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{META-INF}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{maven}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{archetype-metadata.xml}\n        \\end{itemize}\n      \\item\n        \\texttt{archetype-post-generate.groovy} (optional; only invoked\n        by Maven projects)\n      \\end{itemize}\n    \\item\n      \\texttt{archetype-resources}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        Folder structure to be generated\n      \\end{itemize}\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  \\texttt{bnd.bnd}\n\\item\n  \\texttt{{[}build.gradle\\textbar{}pom.xml{]}}\n\\end{itemize}\n\nYou can read more about Maven archetypes and their features and\ncapabilities\n\\href{https://maven.apache.org/guides/introduction/introduction-to-archetypes.html}{here}.\n\nTo create a custom project template that can be generated using Blade\nCLI, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a generic Maven archetype following the folder structure\n  outlined above. Follow Maven's documentation to configure the\n  archetype project appropriately.\n\\item\n  Open the template's \\texttt{bnd.bnd} file and ensure it sets the\n  following configurations:\n\n\\begin{verbatim}\nBundle-Description: TEMPLATE_DESCRIPTION\nBundle-Name: TEMPLATE_NAME\nBundle-SymbolicName: SYMBOLIC_NAME\nBundle-Version: TEMPLATE_VERSION\nLiferay-Versions: LIFERAY_VERSION_RANGE\n-removeheaders:\\\n    Import-Package,\\\n    Private-Package,\\\n    Require-Capability\n\\end{verbatim}\n\n  For example, a template's \\texttt{bnd.bnd} could look like this:\n\n\\begin{verbatim}\nBundle-Description: Creates a Sample as a module project.\nBundle-Name: Liferay Project Templates Sample\nBundle-SymbolicName: com.liferay.project.templates.sample\nBundle-Version: 1.0.0\nLiferay-Versions: [7,8)\n-removeheaders:\\\n    Import-Package,\\\n    Private-Package,\\\n    Require-Capability\n\\end{verbatim}\n\n  The \\texttt{Bundle-SymbolicName} of your template JAR must have the\n  pattern \\texttt{*.project.templates.\\textless{}name\\textgreater{}.*}.\n  The \\texttt{-removeheaders} definition is a packaging requirement for\n  all project templates. For more information on Bnd versioning, visit\n  \\href{https://bnd.bndtools.org/chapters/170-versioning.html}{Bnd's\n  official docs}.\n\\item\n  Generate the extension's JAR file (e.g., \\texttt{gradlew\\ build}).\n\\end{enumerate}\n\nIt's that easy! You've created a custom project template. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-new-extensions-for-blade-cli}{Installing\nNew Extensions} article to install the project template (JAR) to Blade\nCLI.\n\nYou can examine a working custom project template\n\\href{https://github.com/liferay/liferay-blade-cli/blob/master/extensions/sample-template}{here}.\n\n\\chapter{Installing New Extensions for Blade\nCLI}\\label{installing-new-extensions-for-blade-cli}\n\nAfter you've created a new extension for Blade CLI, you must install it\nso it's available for use. You can learn how to create\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-custom-commands-for-blade-cli}{custom\ncommands} and\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-custom-project-templates-for-blade-cli}{custom\nproject templates} in their respective articles.\n\nWhen Blade CLI starts, it looks in the user's\n\\texttt{\\$\\{user.home\\}/.blade/extensions} folder for JAR files. All JAR\nfiles are searched to see if they contain valid Blade extensions. You'll\nlearn how to install new extensions next.\n\n\\section{Installing a New Extension}\\label{installing-a-new-extension}\n\nTo install an extension, you must move the extension JAR to the user's\n\\texttt{\\$\\{user.home\\}/.blade/extensions} folder. You can do this\nautomatically from Blade CLI by running\n\n\\begin{verbatim}\nblade extension install /path/to/my_extension.JAR\n\\end{verbatim}\n\nYou can verify that the extension is available by running the following\ncommands, depending on extension type:\n\n\\textbf{Custom Command:}\n\n\\begin{verbatim}\nblade help\n\\end{verbatim}\n\n\\textbf{Custom Project Template:}\n\n\\begin{verbatim}\nblade create -l\n\\end{verbatim}\n\nGreat! You've installed a new extension!\n\n\\section{Uninstalling an Extension}\\label{uninstalling-an-extension}\n\nYou can uninstall a Blade extension by running this:\n\n\\begin{verbatim}\nblade extension uninstall EXTENSION_NAME.jar\n\\end{verbatim}\n\nThis removes the extension JAR from the user's\n\\texttt{\\$\\{user.home\\}/.blade/extensions} folder.\n\n\\chapter{Creating a Blade Profile}\\label{creating-a-blade-profile}\n\nThere are two steps to follow when adding a new Blade profile:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[creating-a-new-profile]{Creating a new profile}\n\\item\n  \\hyperref[setting-a-profile]{Setting the profile in Liferay Workspace}\n\\end{itemize}\n\nYou'll learn how to create a profile first.\n\n\\section{Creating a New Profile}\\label{creating-a-new-profile}\n\nTo create a new Blade profile, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Create\n  a generic OSGi module}.\n\\item\n  To create a new command, create the command and arguments classes\n  extending\n  \\href{https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BaseCommand.java}{\\texttt{BaseCommand}}\n  and\n  \\href{https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BaseArgs.java}{\\texttt{BaseArgs}},\n  respectively, as described in the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-custom-commands-for-blade-cli}{Creating\n  Custom Commands} article. These classes should reside in the profile\n  module's \\texttt{src/main/java/PACKAGE\\_NAME} folder. These classes\n  register your command and arguments to Blade CLI.\n\\item\n  To override a default command, follow the same steps outlined\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-custom-commands-for-blade-cli}{here}:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Create a command class\n  \\item\n    Create an arguments class\n  \\item\n    Define your commands' fully qualified class names for the service\n    loader\n  \\end{itemize}\n\n  Instead of extending the\n  \\href{https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BaseCommand.java}{\\texttt{BaseCommand}}\n  and\n  \\href{https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BaseArgs.java}{\\texttt{BaseArgs}},\n  classes, however, extend the command/arguments classes defined for the\n  command you intend to override. Make sure to also set the\n  \\texttt{@Parameters} annotation's \\texttt{commandNames} argument to\n  the command to override.\n\n  For example, if you intend to override the default \\texttt{deploy}\n  command, your arguments class declaration would look like this:\n\n\\begin{verbatim}\n@Parameters(commandDescription = \"Overridden Deploy Command\", commandNames = \"deploy\")\npublic class OverriddenArgs extends DeployArgs {\n\n}\n\\end{verbatim}\n\n  The corresponding command class override would look like this:\n\n\\begin{verbatim}\npublic class OverriddenCommand extends BaseCommand<OverriddenArgs> {\n\n    @Override\n    public void execute() throws Exception {\n            OverriddenArgs args = getArgs();\n\n        getBladeCLI().out(\"OverriddenCommand says \" + args.isWatch());\n    }\n\n    @Override\n    public Class<OverriddenArgs> getArgsClass() {\n        return OverriddenArgs.class;\n    }\n}\n\\end{verbatim}\n\n  You can search for the default command/arguments classes\n  \\href{https://github.com/liferay/liferay-blade-cli/tree/master/cli/src/main/java/com/liferay/blade/cli/command}{here}.\n\\item\n  To associate a command to your new profile, set the\n  \\href{https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BladeProfile.java}{\\texttt{BladeProfile}}\n  annotation in your command class:\n\n\\begin{verbatim}\n@BladeProfile(\"myprofile\")\npublic class NewCommand extends BaseCommand<NewArgs> {\n\n}\n\\end{verbatim}\n\n  The annotation's parameter should specify the profile you want to\n  associate the command with (e.g., \\texttt{myprofile}).\n\\end{enumerate}\n\nExcellent! You've created a new Blade profile and learned how to add new\ncommands or override default commands by leveraging the profile.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Command classes spanning multiple JARs can use/share the\nsame profile name. For example, if you want to extend the internal\n(provided) \\texttt{maven} profile extension with new commands, you can\ndo it externally the same way you'd create a new profile.\n\n\\noindent\\hrulefill\n\nYou can reference the\n\\href{https://github.com/liferay/liferay-blade-cli/tree/master/extensions/sample-profile}{sample\nprofile project} to examine a new command and overridden command's setup\nin a custom profile.\n\nNext, you'll learn how to set your new profile for use in a Liferay\nWorkspace.\n\n\\section{Setting a Profile}\\label{setting-a-profile}\n\nTo set your new Blade profile in a Liferay Workspace, open the\n\\texttt{\\$\\{workspaceDir\\}/.blade.properties} file and set the\n\\texttt{profile.name} property to your profile name:\n\n\\begin{verbatim}\nprofile.name=myprofile\n\\end{verbatim}\n\nThis specifies which Blade profile is active and uses its defined\ncommands. The default setting is \\texttt{gradle}. You can also set this\nproperty to \\texttt{maven} out-of-the-box, which is applied for\nMaven-based workspaces. You can only set one profile for a workspace.\n\nYou can specify the Blade profile for a workspace when initializing it\ntoo. This is done by passing the profile name as an argument when\ncreating the workspace:\n\n\\begin{verbatim}\nblade init -P [profile-name] [workspace-name]\n\\end{verbatim}\n\nFor example, if you execute the following command:\n\n\\begin{verbatim}\nblade init -P myprofile my-new-custom-workspace\n\\end{verbatim}\n\nYour \\texttt{my-new-custom-workspace} has the following properties set\nin its \\texttt{\\$\\{workspaceDir\\}/.blade.properties} file:\n\n\\begin{verbatim}\nliferay.version.default=7.2\nprofile.name=myprofile\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\texttt{-P} profile parameter can be used for any\ncommand to specify the profile to use for that command. This is helpful\nif you want to run a command not associated with the workspace's current\nprofile.\n\n\\noindent\\hrulefill\n\nAwesome! You've set your new Blade profile!\n\n\\chapter{Common Errors with Blade\nCLI}\\label{common-errors-with-blade-cli}\n\nIf Blade CLI isn't working as expected, you may find answers here.\n\nThe following issues are documented:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[the-blade-command-is-not-available-in-my-cli]{The blade\n  command is not available in my CLI}\n\\item\n  \\hyperref[i-cant-update-my-blade-cli-version]{I can't update my Blade\n  CLI version}\n\\end{itemize}\n\nVisit the appropriate section to learn more.\n\n\\section{The blade command is not available in my\nCLI}\\label{the-blade-command-is-not-available-in-my-cli}\n\nThe Liferay Project SDK installer attempts to add JPM to your path. For\nWindows, it uses the Windows registry. For Mac/Linux, it updates\n\\texttt{.bashrc} or \\texttt{.zshrc}.\n\nAt a minimum, Mac/Linux users must open a new shell after the installer\nfinishes for the new features to be available. If, however, you're using\na different shell (e.g., Korn, csh) or you've customized your CLI via\n\\texttt{.profile} or some other configuration file, you must add JPM to\nyour path manually.\n\nTo add JPM's required \\texttt{bin} folder, execute the appropriate\ncommand based on your operating system.\n\nmacOS:\n\n\\begin{verbatim}\necho 'export PATH=\"$PATH:$HOME/Library/PackageManager/bin\"' >> ~/.bash_profile\n\\end{verbatim}\n\nLinux:\n\n\\begin{verbatim}\necho 'export PATH=\"$PATH:$HOME/jpm/bin\"' >> ~/.bash_profile\n\\end{verbatim}\n\nOnce you open a new shell, the \\texttt{blade} command should be\navailable.\n\n\\section{I can't update my Blade CLI\nversion}\\label{i-cant-update-my-blade-cli-version}\n\nIf you run \\texttt{blade\\ version} after updating, but don't see the\nexpected version installed, you may have two separate Blade CLI\ninstallations on your machine. This is typically caused if you installed\nan earlier version of Blade CLI and then used the Liferay Project SDK\ninstaller (at any time prior) to update the older Blade CLI instance.\nThis is not recommended. Doing this installs Blade CLI in the global and\nuser home folder of your machine. The latest Blade CLI update process\ninstalls to your user home folder, so you must delete the legacy Blade\nfiles in your global folder, if present. To do this, navigate to your\n\\texttt{GLOBAL\\_FOLDER/JPM4J} folder and delete\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{/bin/blade}\n\\item\n  \\texttt{/commands/blade}\n\\end{itemize}\n\nThe newest Blade CLI installation in your user home folder is now\nrecognized and available.\n\n\\chapter{Liferay Dev Studio}\\label{liferay-dev-studio-2}\n\nLiferay Dev Studio is an extension for the\n\\href{https://www.eclipse.org/ide/}{Eclipse} platform for developing\nLiferay DXP plugins. It works with build tools such as Gradle and Maven\nand configuration tools like BndTools.\n\nDev Studio makes Liferay development easier by providing an intuitive\nGUI. It contains\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay-specific wizards for creating/developing Liferay DXP projects.\n\\item\n  Liferay server management and project testing capabilities.\n\\item\n  Editors for Service Builder files, workflow definitions, POM files,\n  and more.\n\\item\n  Snippets for tag libraries\n\\item\n  etc.\n\\end{itemize}\n\nHere you'll learn how to\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-liferay-dev-studio}{Install\n  Dev Studio}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/setting-proxy-requirements-for-dev-studio}{Set\n  Proxy Requirements}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-a-liferay-server-in-dev-studio}{Install\n  a Liferay server}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/importing-projects-in-dev-studio}{Import\n  a Liferay project}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-the-gogo-shell-in-dev-studio}{Use\n  the Gogo Shell}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/searching-product-source-in-dev-studio}{Search\n  Liferay DXP source}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/debugging-product-source-in-dev-studio}{Debug\n  Liferay DXP source}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/updating-liferay-dev-studio}{Update\n  Dev Studio}\n\\end{itemize}\n\nYou can also find\n\\href{/docs/7-2/reference/-/knowledge_base/r/gradle-in-dev-studio}{Gradle}\nand\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven-in-dev-studio}{Maven}\nreference articles that highlight popular use cases for those respective\nbuild tools in Dev Studio.\n\nFor help\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project\\#liferay-dev-studio}{creating}\nand\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project\\#liferay-dev-studio}{deploying}\nprojects with Dev Studio, or\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-liferay-workspace\\#dev-studio}{creating\na Liferay Workspace}, visit their respective articles.\n\nLet Dev Studio aid in your conquest for Liferay DXP development.\nContinue on to learn how!\n\n\\chapter{Installing Liferay Dev\nStudio}\\label{installing-liferay-dev-studio}\n\nLiferay Dev Studio is a plugin for Eclipse that brings many\nLiferay-specific features to the table. You can install it into your\nexisting Eclipse environment, or Liferay provides a bundled version\nincluded in its Project SDK. Here you'll learn the different methods\navailable for installing Dev Studio:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[install-the-dev-studio-bundle]{install the Dev Studio bundle\n  from scratch}\n\\item\n  \\hyperref[install-dev-studio-into-eclipse]{install Dev Studio into an\n  existing Eclipse instance using an update URL}\n\\item\n  \\hyperref[install-dev-studio-into-eclipse-from-a-zip-file]{install Dev\n  Studio into an existing Eclipse instance using a ZIP file}\n\\end{itemize}\n\n\\textbf{Important:} If you're installing Dev Studio into an existing\nEclipse environment, you must be on Eclipse Photon or newer. For\ninstructions on upgrading to Photon, see Eclipse's\n\\href{https://wiki.eclipse.org/FAQ_How_do_I_upgrade_Eclipse_IDE\\%3F\\#Upgrading_existing_Eclipse_IDE_and_Installed_Features_to_newer_release}{upgrade\ndocumentation}. With this particular upgrade, you should also deactivate\nthe current available update sites in the \\emph{Window} →\n\\emph{Preferences} → \\emph{Install/Update} → \\emph{Available Software\nSites} menu to ensure a successful upgrade (e.g., Oxygen).\n\n\\section{Install the Dev Studio\nBundle}\\label{install-the-dev-studio-bundle}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Download and install \\href{http://java.oracle.com}{Java}. Liferay runs\n  on Java, so you'll need it to run everything else. Because you'll be\n  developing apps for Liferay DXP in Dev Studio, the Java Development\n  Kit (JDK) is required. It is an enhanced version of the Java\n  Environment used for developing new Java technology. You can download\n  the Java SE JDK from the Java\n  \\href{http://www.oracle.com/technetwork/java/javase/downloads/index.html}{Downloads}\n  page.\n\\item\n  Download Liferay's latest\n  \\href{https://sourceforge.net/projects/lportal/files/Liferay\\%20IDE/}{Project\n  SDK with Dev Studio}. Go to the\n  \\href{https://customer.liferay.com/group/customer/downloads}{Downloads}\n  page in Liferay's Help Center. Select \\emph{Developer Tools} in the\n  Product drop-down and \\emph{Developer Studio} for the file type. Then\n  select the executable that correlates to your operating system.\n\n  The Project SDK includes Dev Studio DXP,\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\n  Workspace}, and\n  \\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}.\n\\item\n  Run the Project SDK executable and step through the installer to\n  install everything to your machine. For help with setting up proxy\n  settings (if necessary), see the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/setting-proxy-requirements-for-dev-studio}{Dev\n  Studio Proxy Settings} and\n  \\href{/docs/7-2/reference/-/knowledge_base/r/setting-proxy-requirements-for-liferay-workspace}{Liferay\n  Workspace Proxy Settings} articles for more information.\n\\end{enumerate}\n\nCongratulations! You've installed Liferay Dev Studio! It's now available\nin the Project SDK folder's \\texttt{liferay-developer-studio}. To run\nDev Studio, execute the \\texttt{DeveloperStudio} executable. A Liferay\nWorkspace has also been initialized in that same folder.\n\n\\section{Install Dev Studio into\nEclipse}\\label{install-dev-studio-into-eclipse}\n\nIf you already have an Eclipse environment that you're using for other\nthings, it's easy to add Dev Studio to your existing Eclipse\ninstallation.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In Eclipse, select \\emph{Help} → \\emph{Install New Software}.\n\\item\n  In the \\emph{Work with} field, copy in the following URL:\n  https://releases.liferay.com/tools/ide/latest/stable/\n\\item\n  You'll see the Liferay Dev Studio components in the list below. Check\n  them off and click \\emph{Next}.\n\\item\n  Accept the terms of the agreements and click \\emph{Next}, and Dev\n  Studio is installed. Like other Eclipse plugins you'll have to restart\n  Eclipse to enable it.\n\\end{enumerate}\n\n\\section{Install Dev Studio into Eclipse from a ZIP\nFile}\\label{install-dev-studio-into-eclipse-from-a-zip-file}\n\nTo install Liferay Dev Studio into Eclipse from a Zip file, follow these\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the\n  \\href{https://community.liferay.com/en_GB/project/-/asset_publisher/TyF2HQPLV1b5/content/ide-installation-instructions}{Dev\n  Studio} downloads page. Under \\emph{Other Downloads}, select the\n  \\emph{Liferay IDE {[}version{]} Archived Update-site} option to\n  download it.\n\\item\n  In Eclipse, go to \\emph{Help} → \\emph{Install New Software\\ldots{}}.\n\\item\n  In the \\emph{Add} dialog, click the \\emph{Archive} button and browse\n  to the location of the downloaded Dev Studio Zip file.\n\\item\n  You'll see the Dev Studio components in the list below. Check them off\n  and click \\emph{Next}.\n\\item\n  Accept the terms of the agreements and click \\emph{Next}, and Liferay\n  Dev Studio is installed. Like other Eclipse plugins you'll have to\n  restart Eclipse to enable it.\n\\end{enumerate}\n\nAwesome! You've installed Liferay Dev Studio. Now you can begin Liferay\ndevelopment using a popular and supported IDE.\n\n\\chapter{Setting Proxy Requirements for Dev\nStudio}\\label{setting-proxy-requirements-for-dev-studio}\n\nIf you have proxy server requirements and want to configure your http(s)\nproxy\\\\\nto work with Liferay Dev Studio, follow the instructions below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to Eclipse's \\emph{Window} → \\emph{Preferences} →\n  \\emph{General} → \\emph{Network Connections} menu.\n\\item\n  Set the \\emph{Active Provider} drop-down selector to \\emph{Manual}.\n\\item\n  Under \\emph{Proxy entries}, configure both proxy HTTP and HTTPS by\n  clicking the field and selecting the \\emph{Edit} button.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/ide-network-connections.png}\n  \\caption{You can configure your proxy settings in Dev Studio's Network\n  Connections menu.}\n  \\end{figure}\n\\item\n  For each schema (HTTP and HTTPS), enter your proxy server's host,\n  port, and authentication settings (if necessary). Do not leave\n  whitespace at the end of your proxy host or port settings.\n\\item\n  Once you've configured your proxy entry, click \\emph{Apply and Close}.\n\\end{enumerate}\n\nIf you're working with a Liferay Workspace in Dev Studio, you'll need to\nconfigure your proxy settings for that environment too. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/setting-proxy-requirements-for-liferay-workspace}{Setting\nProxy Requirements for Liferay Workspace} article for more details.\n\nAwesome! You've successfully configured Dev Studio's proxy settings!\n\n\\section{Additional Proxy Settings}\\label{additional-proxy-settings}\n\nSome Eclipse plugins do not properly check the \\texttt{core.net} proxy\ninfrastructure when setting proxy settings via \\emph{Window} →\n\\emph{Preferences} → \\emph{General} → \\emph{Network Connections}.\nTherefore, you may need to configure additional proxy settings.\n\nTo do so, open the \\texttt{eclipse.ini} file associated with your\nEclipse installation and add the following entries:\n\n\\begin{verbatim}\n-vmargs\n-Dhttp.proxyHost=www.somehost.com\n-Dhttp.proxyPort=1080\n-Dhttp.proxyUser=userId\n-Dhttp.proxyPassword=somePassword\n-Dhttps.proxyHost=www.somehost.com\n-Dhttps.proxyPort=1080\n-Dhttps.proxyUser=userId\n-Dhttps.proxyPassword=somePassword\n\\end{verbatim}\n\nAfter saving the file, restart Eclipse. Now your additional proxy\nsettings are applied!\n\n\\chapter{Installing a Liferay Server in Dev\nStudio}\\label{installing-a-liferay-server-in-dev-studio}\n\nDev Studio offers a single GUI for managing a Liferay server and its\ndeployed projects. A server is installed and managed from the Servers\nview (lower left corner in Eclipse).\n\nFor reference, here's how the Dev Studio server buttons work with your\nLiferay DXP instance:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Start} (\\includegraphics{./images/icon-start-server.png}):\n  Starts the server.\n\\item\n  \\emph{Stop} (\\includegraphics{./images/icon-stop-server.png}): Stops\n  the server.\n\\item\n  \\emph{Debug} (\\includegraphics{./images/icon-debug-server.png}):\n  Starts the server in debug mode. For more information on debugging in\n  Dev Studio, see the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/debugging-product-source-in-dev-studio}{Debugging\n  Liferay DXP source in Dev Studio} article.\n\\end{itemize}\n\nFollow these steps to install your server. Note you must have already\ndownloaded and de-compressed the server bundle:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the Servers view, click the \\emph{No Servers are available} link.\n  If you already have a server installed, you can install a new server\n  by right-clicking in the Servers view and selecting \\emph{New} →\n  \\emph{Server}. This brings up a wizard that walks you through the\n  process of defining a new server.\n\\item\n  Select the type of server you would like to create from the list of\n  available options. For a standard server, open the \\emph{Liferay,\n  Inc.} folder and select the \\emph{Liferay 7.x} option. You can change\n  the server name to something more unique too; this is the name\n  displayed in the Servers view. Then click \\emph{Next}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/define-new-server.png}\n  \\caption{Choose the type of server you want to create.}\n  \\end{figure}\n\n  \\textbf{Note:} If you've already configured previous Liferay servers,\n  you'll be provided the \\emph{Server runtime environment} field, which\n  lets you choose previously configured runtime environments. If you\n  want to re-add an existing server, select one from the dropdown menu.\n  You can also add a new server by selecting \\emph{Add}, or you can edit\n  existing servers by selecting \\emph{Configure runtime environments}.\n  Once you've configured the server runtime environment, select\n  \\emph{Finish}. If you selected an existing server, your server\n  installation is finished; you can skip steps 3-5.\n\\item\n  Enter a name for your server. This is the name for the Liferay DXP\n  runtime configuration used by Dev Studio. This is not the display name\n  used in the Servers tab.\n\\item\n  Browse to the installation folder of the Liferay DXP bundle. For\n  example,\n  \\texttt{C:\\textbackslash{}liferay-ce-portal-7.2.0-m2\\textbackslash{}tomcat-9.0.10}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/specify-bundle-directory.png}\n  \\caption{Specify the installation folder of the bundle.}\n  \\end{figure}\n\\item\n  Select a runtime JRE and click \\emph{Finish}. Your new server appears\n  under the Servers view.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/new-server-added.png}\n  \\caption{Your new server appears under the \\emph{Servers} view.}\n  \\end{figure}\n\\end{enumerate}\n\nCongratulations! Your server is now available in Liferay Dev Studio!\n\n\\chapter{Importing Projects in Dev\nStudio}\\label{importing-projects-in-dev-studio}\n\nThere are two types of Liferay projects you can import into Dev Studio:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay Module Project (this also includes WAR-style projects)\n\\item\n  Liferay Workspace Project\n\\end{itemize}\n\nYou cannot import Liferay projects individually that reside in a\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace}. You can either import a non-workspace Liferay project (or\ngroup of projects if the parent folder is specified) or an entire\nworkspace project with all its Liferay projects.\n\nTo import a pre-existing Liferay project into Dev Studio, follow the\nsteps outlined below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Right-click in the Project Explorer and select \\emph{Import} →\n  \\emph{Liferay Module Project}. If you're interested in importing an\n  entire Liferay Workspace, select the \\emph{Liferay Workspace Project}\n  instead.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/import-liferay-project.png}\n  \\caption{You can import a single project or folder of projects.}\n  \\end{figure}\n\n  Once you've selected your project(s), the project build type is\n  displayed.\n\\item\n  Click \\emph{Finish}.\n\\end{enumerate}\n\nNow your Liferay project is available from the Package Explorer.\n\n\\chapter{Using the Gogo Shell in Dev\nStudio}\\label{using-the-gogo-shell-in-dev-studio}\n\nIf you're using Dev Studio to develop and deploy your projects, you may\nbe interested in managing them after they're deployed with Dev Studio\ntoo. You can do this with the Dev Studio's\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nShell} feature.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Right-click your started portal instance in the Servers view.\n\\item\n  Select \\emph{Open Gogo Shell}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/open-gogo-shell.png}\n  \\caption{Select \\emph{Open Gogo Shell} to open a terminal window in\n  Dev Studio using Gogo shell.}\n  \\end{figure}\n\n  A Gogo shell terminal appears, allowing you to enter Gogo commands to\n  inspect your Liferay instance and the projects deployed to it.\n\\item\n  A common use case for the Gogo Shell is verifying successful project\n  deployment. Enter the \\texttt{lb} command to view a list of deployed\n  bundles. If the project status is active, then it deployed\n  successfully.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/gogo-deploy-successful.png}\n  \\caption{You can check to see if your project deployed successfully to\n  Liferay using the Gogo shell.}\n  \\end{figure}\n\\end{enumerate}\n\n\\textbf{Important:} Dev Studio's Gogo shell usage requires\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes}{Developer\nMode} to be enabled. Developer Mode is enabled in\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace} by default.\n\nExcellent! You've learned how to manage your deployed projects with Dev\nStudio's Gogo Shell integration.\n\n\\chapter{Searching Liferay DXP Source in Dev\nStudio}\\label{searching-liferay-dxp-source-in-dev-studio}\n\nIn Liferay Dev Studio, you can search through Liferay DXP's source code\nto aid in the development of your project. Liferay provides great\nresources to help with development (e.g., official documentation,\n\\href{https://docs.liferay.com/}{docs.liferay.com},\n\\href{/docs/7-2/reference/-/knowledge_base/r/sample-projects}{sample\nprojects}, etc.), but sometimes searching through Liferay's codebase\n(i.e., platform and official apps) for patterns is just as useful. For\nexample, if you're creating an application that extends a class provided\nin Liferay's \\texttt{portal-kernel} JAR, you can inspect that class and\nresearch how it's used in other areas of Liferay DXP's codebase.\n\nTo do this, you must be developing in a\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace}. Liferay Workspace is able to provide this functionality by\ntargeting a specific Liferay DXP version, which indexes the configured\nLiferay DXP source code to provide advanced search. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform}{Managing\nthe Target Platform in Liferay Workspace} tutorial for more information\non how this works.\n\nWorkspace does not perform portal source indexing by default. You must\nenable this functionality by adding the following property to your\nworkspace's \\texttt{gradle.properties} file:\n\n\\begin{verbatim}\ntarget.platform.index.sources=true\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Portal source indexing is disabled in Gradle workspace\nversion 2.0.3+ (Target Platform plugin version 2.0.0+).\n\n\\noindent\\hrulefill\n\nIn this tutorial, you'll explore three use cases where advanced search\nwould be useful.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[search-class-hierarchy]{Search class hierarchy}\n\\item\n  \\hyperref[search-method-declarations]{Search declarations}\n\\item\n  \\hyperref[search-annotation-references]{Search references}\n\\end{itemize}\n\nThese examples are just a small subset of what you can search in Liferay\nDev Studio. See Eclipse's documentation on\n\\href{http://help.eclipse.org/oxygen/index.jsp?topic=\\%2Forg.eclipse.jdt.doc.user\\%2Fconcepts\\%2Fconcept-java-search.htm&resultof=\\%22\\%6a\\%61\\%76\\%61\\%22\\%20}{Java\nSearch} for a comprehensive guide.\n\n\\section{Search Class Hierarchy}\\label{search-class-hierarchy}\n\nInspecting classes that extend a similar superclass can help you find\nuseful patterns and examples for how you can develop your own app. For\nexample, suppose your app extends the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCPortlet.html}{MVCPortlet}\nclass. You can search classes that extend that same class in Dev Studio.\nComplete the steps below for a simple example:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Right-click the \\texttt{MVCPortlet} declaration.\n\\item\n  Select \\emph{Open Type Hierarchy}.\n\\end{enumerate}\n\nThis opens a window that lets you inspect all classes residing in the\ntarget platform that extend \\texttt{MVCPortlet}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/open-type-hierarchy.png}\n\\caption{Browse the Type Hierarchy window and open the provided classes\nfor examples on how to extend a class.}\n\\end{figure}\n\nGreat! Now you can search for all extensions and implementations of a\nclass/interface to aid in your quest for developing the perfect app.\n\n\\section{Search Method Declarations}\\label{search-method-declarations}\n\nSometimes you want a search to be more granular, exploring the\ndeclarations of a specific method provided by a class/interface. Dev\nStudio's advanced search has no limits; Liferay Workspace's target\nplatform indexing provides method exploration too!\n\nSuppose in the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCPortlet.html}{MVCPortlet}\nclass you're extending, you want to search for declarations of its\n\\texttt{doView} method you're overriding. Here's how to do it:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Right-click the \\texttt{doView} method declaration in your custom\n  app's class.\n\\item\n  Select \\emph{Declarations} → \\emph{Workspace}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/inspect-declared-method.png}\n\\caption{All declarations of the method are returned in the Search\nwindow.}\n\\end{figure}\n\nThe rendered Search window displays the other occurrences in the target\nplatform where that method was overridden.\n\n\\section{Search Annotation\nReferences}\\label{search-annotation-references}\n\nAnnotations used in Liferay DXP's source code can sometimes be cryptic.\nYou can find out how they can be used in your own application by\nsearching for where these types of annotations exist in Liferay's target\nplatform.\n\nFor example, you may find some documentation on using the\n\\texttt{@Reference} annotation in an OSGi module and implement it in\nyour custom app. It could be useful to reference real world examples in\nLiferay DXP's apps to check how it was used elsewhere. You can complete\nthis search like this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Right-click the \\texttt{@Reference} annotation in a class.\n\\item\n  Select \\emph{References} → \\emph{Workspace}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/inspect-references-ide.png}\n\\caption{All matching annotations are displayed in the Search window.}\n\\end{figure}\n\nThe rendered Search window displays the other occurrences in the target\nplatform where that annotation was used.\n\nExcellent! You now have the tools to search the configured target\nplatform specified in your Liferay Workspace!\n\n\\chapter{Debugging Liferay DXP Source in Dev\nStudio}\\label{debugging-liferay-dxp-source-in-dev-studio}\n\nYou can debug Liferay DXP source code in Dev Studio to help resolve\nerrors. Debugging Liferay DXP code follows most of the same techniques\nassociated with debugging in Eclipse. If you need help with general\ndebugging, you can visit Eclipse's documentation. Here's some helpful\nEclipse links to visit:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{http://help.eclipse.org/oxygen/index.jsp?topic=\\%2Forg.eclipse.jdt.doc.user\\%2Fconcepts\\%2Fcdebugger.htm&cp=1_2_9}{Debugger}\n\\item\n  \\href{http://help.eclipse.org/oxygen/index.jsp?topic=\\%2Forg.eclipse.jdt.doc.user\\%2Fconcepts\\%2Fclocdbug.htm&cp=1_2_11}{Local\n  Debugging}\n\\item\n  \\href{http://help.eclipse.org/oxygen/index.jsp?topic=\\%2Forg.eclipse.jdt.doc.user\\%2Fconcepts\\%2Fcremdbug.htm&cp=1_2_12}{Remote\n  Debugging}\n\\end{itemize}\n\nThere are a couple Liferay-specific configurations to know before\ndebugging Liferay DXP code:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[configure-your-target-platform]{Configure your target\n  platform.}\n\\item\n  \\hyperref[configure-a-liferay-server-and-start-it-in-debug-mode]{Configure\n  a Liferay server and start it in debug mode.}\n\\end{itemize}\n\nLet's explore these Liferay-specific debugging configurations.\n\n\\section{Configure Your Target\nPlatform}\\label{configure-your-target-platform}\n\nTo configure your target platform, you must be developing in a\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace}. Liferay Workspace is able to provide debugging capabilities\nby targeting a specific Liferay DXP version, which indexes the\nconfigured Liferay DXP source code. Liferay Workspace does not perform\nportal source indexing by default. You must enable this functionality by\nadding the following property to your workspace's\n\\texttt{gradle.properties} file:\n\n\\begin{verbatim}\ntarget.platform.index.sources=true\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Portal source indexing is disabled in Gradle workspace\nversion 2.0.3+ (Target Platform plugin version 2.0.0+).\n\n\\noindent\\hrulefill\n\nWithout specifying a target platform, Liferay DXP's source code cannot\nbe accessed by Dev Studio. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform}{Managing\nthe Target Platform in Liferay Workspace} tutorial for more information\non how this works.\n\n\\textbf{Important:} The target platform should match the Liferay server\nversion you configure in the next section.\n\nOnce the target platform is configured in your workspace, Eclipse has\naccess to all of Liferay DXP's source code. Next, you'll configure a\nLiferay server and learn how to start it in Debug mode.\n\n\\section{Configure a Liferay Server and Start It in Debug\nMode}\\label{configure-a-liferay-server-and-start-it-in-debug-mode}\n\nConfiguring your target platform gives Eclipse Liferay DXP's source code\nto reference. Now you must configure a Liferay server matching the\ntarget platform version so you can deploy the custom code you wish to\ndebug.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Set up your Liferay DXP server to run in Dev Studio. See the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-a-liferay-server-in-dev-studio}{Installing\n  a Server in Dev Studio} for more details.\n\\item\n  Start the server in debug mode. To do this, click the debug button in\n  the Servers pane of Dev Studio.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/ide-debug.png}\n  \\caption{The red box in this screenshot highlights the debug button.\n  Click this button to start the server in debug mode.}\n  \\end{figure}\n\\end{enumerate}\n\nAwesome! You're now equipped to begin debugging in Liferay Dev Studio!\n\n\\chapter{Updating Liferay Dev Studio}\\label{updating-liferay-dev-studio}\n\nIf you're already using Liferay Dev Studio but must update your\nenvironment, follow the steps below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In Dev Studio, go to \\emph{Help} → \\emph{Install New\n  Software\\ldots{}}.\n\\item\n  In the \\emph{Work with} field, copy in the URL\n  http://releases.liferay.com/tools/ide/latest/stable/.\n\\item\n  You'll see the Dev Studio components in the list below. Check them off\n  and click \\emph{Next}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/ide-updatesite-install.png}\n  \\caption{Make sure to check all the Dev Studio components you wish to\n  install.}\n  \\end{figure}\n\\item\n  Accept the terms of the agreements. Click \\emph{Next}, and Dev Studio\n  is updated. You must restart Dev Studio for the updates to take\n  effect.\n\\end{enumerate}\n\nYou're now on the latest version of Liferay Dev Studio!\n\n\\chapter{Gradle in Dev Studio}\\label{gradle-in-dev-studio}\n\n\\href{http://gradle.org/}{Gradle} is a popular open source build\nautomation system. You can take full advantage of Gradle in Liferay Dev\nStudio through\n\\href{https://projects.eclipse.org/releases/photon}{Buildship}, a\ncollection of Eclipse plugins that provide support for building software\nusing Gradle with Liferay Dev Studio. Buildship is bundled with Liferay\nDev Studio versions 3.0 and higher.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/buildship-in-liferayide.png}\n\\caption{Navigate to \\emph{Help} → \\emph{Installation Details} to view\nplugins included in Dev Studio.}\n\\end{figure}\n\nThis reference article highlights some useful tips for leveraging Gradle\nin Dev Studio.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[creating-pure-gradle-projects]{Creating Pure Gradle\n  Projects}\n\\item\n  \\hyperref[importing-pure-gradle-projects]{Importing Pure Gradle\n  Projects}\n\\item\n  \\hyperref[gradle-tasks-and-executions]{Gradle Tasks and Executions}\n\\end{itemize}\n\nNote that\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project\\#liferay-dev-studio}{creating\nLiferay Gradle projects} and\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project\\#liferay-dev-studio}{deploying\nGradle projects} with Dev Studio are covered in their respective\narticles.\n\nThe first thing you'll learn about in this tutorial is creating pure\nGradle projects in Dev Studio.\n\n\\section{Creating Pure Gradle\nProjects}\\label{creating-pure-gradle-projects}\n\nMost of Dev Studio's wizards rely on your usage of Liferay Workspace.\nThis is for good reason; it's the recommended developer environment for\nLiferay projects. You can, however, create pure Gradle projects and\nmanually configure them to be deployable to Liferay DXP.\n\nYou can create a pure Gradle project by using the Gradle Project wizard.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{File} → \\emph{New} → \\emph{Project\\ldots{}}.\n\\item\n  Select \\emph{Gradle} → \\emph{Gradle Project}. Then click \\emph{Next} →\n  \\emph{Next}.\n\\item\n  Enter a valid project name. You can also specify your project location\n  and working sets.\n\\item\n  Optionally, you can navigate to the next page and specify your Gradle\n  distribution and other advanced options. Once you're finished, select\n  \\emph{Finish}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/new-gradle-project.png}\n\\caption{You can specify your Gradle distribution and advanced options\nsuch as home directories, JVM options, and program arguments.}\n\\end{figure}\n\nExcellent! You've created a pure Gradle project using Dev Studio.\n\n\\section{Importing Pure Gradle\nProjects}\\label{importing-pure-gradle-projects}\n\nYou can also import existing pure Gradle projects in Dev Studio.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{File} → \\emph{Import\\ldots{}}.\n\\item\n  Select \\emph{Gradle} → \\emph{Existing Gradle Project} → \\emph{Next} →\n  \\emph{Next}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/import-gradle-project.png}\n  \\caption{You can specify what Gradle project to import from the\n  \\emph{Import Gradle Project} wizard.}\n  \\end{figure}\n\\item\n  Click the \\emph{Browse\\ldots{}} button to choose a Gradle project.\n\\item\n  Optionally, you can navigate to the next page and specify your Gradle\n  distribution and other advanced options. Once you're finished, click\n  \\emph{Next} again to review the import configuration. Select\n  \\emph{Finish} once you've confirmed your Gradle project import.\n\\end{enumerate}\n\nNext you'll learn about Gradle tasks and executions, and learn how to\nrun and display them in Dev Studio.\n\n\\section{Gradle Tasks and Executions}\\label{gradle-tasks-and-executions}\n\nDev Studio provides two views to enhance your development experience\nusing Gradle: Gradle Tasks and Gradle Executions. You can open these\nviews by following the instructions below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Window} → \\emph{Show View} → \\emph{Other\\ldots{}}.\n\\item\n  Navigate to the \\emph{Gradle} folder and open \\emph{Gradle Tasks} and\n  \\emph{Gradle Executions}.\n\\end{enumerate}\n\nGradle tasks and executions views open automatically once you create or\nimport a Gradle project.\n\nThe Gradle Tasks view lets you display the Gradle tasks available for\nyou to use in your Gradle project. Users can execute a task listed under\nthe Gradle project by double-clicking it.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/gradle-tasks.png}\n\\caption{Navigate into your preferred Gradle project to view its\navailable Gradle tasks.}\n\\end{figure}\n\nOnce you've executed a Gradle task, you can open the Gradle Executions\nview to inspect its output.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/gradle-executions.png}\n\\caption{The Gradle Executions view helps you visualize the Gradle build\nprocess.}\n\\end{figure}\n\nKeep in mind that if you change the Gradle build scripts inside your\nGradle projects (e.g., \\texttt{build.gradle} or\n\\texttt{settings.gradle}), you must refresh the project so Dev Studio\ncan account for the change and display it properly in your views. To\nrefresh a Gradle project, right-click on the project and select\n\\emph{Gradle} → \\emph{Refresh Gradle Project}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/refresh-gradle-project.png}\n\\caption{Make sure to always refresh your Gradle project in Dev Studio\nafter build script edits.}\n\\end{figure}\n\nIf you prefer Eclipse refresh your Gradle projects automatically,\nnavigate to \\emph{Window} → \\emph{Preferences} → \\emph{Gradle} and\nenable the \\emph{Automatic Project Synchronization} checkbox. If you'd\nlike to enable Gradle's automatic synchronization for just one Gradle\nproject, you can right-click a Gradle project and select\n\\emph{Properties} → \\emph{Gradle} and enable auto sync that way.\n\nExcellent! You're now equipped with the knowledge to add, import, and\nbuild your Gradle projects in Liferay Dev Studio!\n\n\\chapter{Maven in Dev Studio}\\label{maven-in-dev-studio}\n\nYou can take full advantage of \\href{https://maven.apache.org/}{Maven}\nin Liferay Dev Studio with its built-in Maven support. In this article,\nyou'll learn about the following topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[installing-maven-plugins-for-dev-studio]{Installing Maven\n  Plugins for Liferay Dev Studio}\n\\item\n  \\hyperref[importing-maven-projects]{Importing Maven Projects}\n\\item\n  \\hyperref[using-the-pom-graphic-editor]{Using the POM Graphic Editor}\n\\end{itemize}\n\nNote that\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project\\#liferay-dev-studio}{creating}\nand\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project\\#liferay-dev-studio}{deploying}\nMaven projects with Dev Studio are covered in their respective articles.\n\nFirst you'll install the necessary Maven plugins for Dev Studio.\n\n\\section{Installing Maven Plugins for Dev\nStudio}\\label{installing-maven-plugins-for-dev-studio}\n\nTo support Maven projects in Dev Studio properly, you first need a\nmechanism to recognize Maven projects as Dev Studio projects. For Dev\nStudio to recognize the Maven project and for it to be able to leverage\nJava EE tooling features (e.g., the Servers view) with the project, the\nproject must be a flexible web project. Dev Studio relies on the\nfollowing Eclipse plugins to provide this capability:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{m2e} (Maven integration for Eclipse)\n\\item\n  \\texttt{m2e-wtp} (Maven integration for WTP)\n\\end{itemize}\n\nAll you have to do is install them so you can begin developing Maven\nprojects for Liferay DXP.\n\nWhen first installing Dev Studio, the installation startup screen asks\nif you want to install the Maven plugins automatically. Don't worry if\nyou missed this during setup. You'll learn how to install the required\nMaven plugins for Dev Studio manually below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Help} → \\emph{Install New Software}. In the\n  \\emph{Work with} field, insert the following value:\n\n\\begin{verbatim}\nhttp://releases.liferay.com/tools/ide/latest/stable/\n\\end{verbatim}\n\\item\n  Check the \\emph{Liferay IDE Maven Support} option. This bundles all\n  the required Maven plugins you need to begin developing Maven projects\n  for Liferay DXP.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/maven-install-ide-plugins.png}\n  \\caption{You can install all the necessary Maven plugins for Dev\n  Studio by installing the \\emph{Liferay IDE Maven Support} option.}\n  \\end{figure}\n\n  If the \\emph{Liferay IDE Maven Support} option does not appear, then\n  it's already installed. To verify that it's installed, uncheck the\n  \\emph{Hide items that are already installed} checkbox and look for\n  \\emph{Liferay IDE Maven Support} in the list of installed plugins.\n  Also, if you want to view everything that is bundled with the\n  \\emph{Liferay IDE Maven Support} option, uncheck the \\emph{Group items\n  by category} checkbox.\n\\item\n  Click \\emph{Next}, review the install details, accept the term and\n  license agreements, and select \\emph{Finish}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Both Maven and Eclipse have their own standard build\nproject lifecycles that are independent from each other. For both to\nwork together and run seamlessly within Dev Studio, a lifecycle mapping\nis required to link both lifecycles into one combined lifecycle.\nNormally, this would have to be done manually by the user. Fortunately,\nthe m2e-liferay plugin combines the lifecycle metadata mapping and\nEclipse build lifecycles, to provide a seamless user experience. The\nlifecycle mappings for your project can be viewed by right-clicking your\nproject and selecting \\emph{Properties} → \\emph{Maven} → \\emph{Lifecycle\nMapping}.\n\n\\noindent\\hrulefill\n\nAwesome! Your Dev Studio is ready to develop Maven projects for Liferay\nDXP!\n\nYou'll learn about importing Maven projects in Dev Studio next.\n\n\\section{Importing Maven Projects}\\label{importing-maven-projects}\n\nTo import a pre-existing, non-Liferay Maven project into Dev Studio,\nfollow the steps outlined below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{File} → \\emph{Import} → \\emph{Maven} →\n  \\emph{Existing Maven Projects} and click \\emph{Next}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/import-maven-project.png}\n  \\caption{Dev Studio offers the Maven folder in the Import wizard.}\n  \\end{figure}\n\\item\n  Click \\emph{Browse\\ldots{}} and select the root folder for your Maven\n  project. Once you've selected it, the \\texttt{pom.xml} for that\n  project should be visible in the Projects menu.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/select-maven-import.png}\n  \\caption{Use the Import Maven Projects wizard to import your\n  pre-existing project.}\n  \\end{figure}\n\\item\n  Click \\emph{Finish}.\n\\end{enumerate}\n\nNow your Maven project is available from the Package Explorer. To import\na Liferay project built with Maven, see the\n\\href{/docs/7-2/reference/-/knowledge_base/r/importing-projects-in-dev-studio}{Importing\nProjects in Dev Studio} Next you'll learn about Dev Studio's POM\ngraphical editor.\n\n\\section{Using the POM Graphic\nEditor}\\label{using-the-pom-graphic-editor}\n\nYou're provided a graphical POM editor when opening your Maven project's\n\\texttt{pom.xml} in Dev Studio. This gives you several different ways to\nleverage the power of Maven in your project:\n\n\\begin{itemize}\n\\item\n  \\textbf{Overview:} provides a graphical interface where you can add to\n  and edit the \\texttt{pom.xml} file.\n\\item\n  \\textbf{Dependencies:} provides a graphical interface for adding and\n  editing dependencies in your project, as well as modifying the\n  \\texttt{dependencyManagement} section of the \\texttt{pom.xml} file.\n\\item\n  \\textbf{Effective POM:} provides a read-only version of your project\n  POM merged with its parent POM(s), \\texttt{settings.xml}, and the\n  settings in Eclipse for Maven.\n\\item\n  \\textbf{Dependency Hierarchy:} provides a hierarchical view of project\n  dependencies and an interactive listing of resolved dependencies.\n\\item\n  \\textbf{pom.xml:} provides an editor for your POM's source XML.\n\\end{itemize}\n\nThe figure below shows the \\texttt{pom.xml} file editor and its modes.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/pom-editor-features.png}\n\\caption{Liferay Dev Studio provides five interactive modes to help you\nedit and organize your POM..}\n\\end{figure}\n\nBy taking advantage of these interactive modes, Dev Studio makes\nmodifying and organizing your POM and its dependencies a snap!\n\n\\chapter{IntelliJ}\\label{intellij}\n\nThe\n\\href{https://plugins.jetbrains.com/plugin/10739-liferay-intellij-plugin}{Liferay\nIntelliJ plugin} provides support for Liferay DXP development in\n\\href{https://www.jetbrains.com/idea/}{IntelliJ IDEA}. Liferay's\nIntelliJ plugin provides the following built-in features:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Creating a Liferay Workspace (Gradle and Maven based)\n\\item\n  Creating Liferay projects (Gradle and Maven based)\n\\item\n  Liferay DXP Tomcat/Wildfly server support for project deployment and\n  debugging\n\\item\n  Support for adding line markers for each entity in the service editor\n\\item\n  Syntax checking, highlighting, and code completion (e.g., Bnd and XML\n  files)\n\\end{itemize}\n\nIn these articles, you'll learn how to install the Liferay IntelliJ\nplugin and leverage its features to improve Liferay development with\nIntelliJ IDEA.\n\n\\chapter{Installing the Liferay IntelliJ\nPlugin}\\label{installing-the-liferay-intellij-plugin}\n\nThere are two ways to install the Liferay IntelliJ plugin:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[installing-via-intellij-marketplace]{via IntelliJ\n  Marketplace}\n\\item\n  \\hyperref[installing-via-zip-file]{via Zip file}\n\\end{itemize}\n\nFollow the steps pertaining to your preferred installation process.\n\n\\section{Installing Via IntelliJ\nMarketplace}\\label{installing-via-intellij-marketplace}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In IntelliJ, navigate to \\emph{File} → \\emph{Settings} →\n  \\emph{Plugins}.\n\\item\n  In the Marketplace tab, search for \\emph{Liferay} in the provided\n  search bar.\n\\item\n  Click \\emph{Install} next to the Liferay IntelliJ Plugin.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/intellij-marketplace-installation.png}\n  \\caption{IntelliJ Marketplace offers a streamlined way to install\n  plugins.}\n  \\end{figure}\n\\item\n  After the plugin has downloaded, select \\emph{Restart IDE}.\n\\end{enumerate}\n\nOnce IntelliJ restarts, the Liferay IntelliJ plugin is installed and\nready for use.\n\n\\section{Installing Via Zip File}\\label{installing-via-zip-file}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the\n  \\href{https://plugins.jetbrains.com/plugin/10739-liferay-intellij-plugin}{JetBrains'\n  Liferay IntelliJ plugin} page and download it to your local machine.\n\\item\n  In IntelliJ, navigate to \\emph{File} → \\emph{Settings} →\n  \\emph{Plugins}.\n\\item\n  Click the gear icon from the top menu and select \\emph{Install Plugin\n  from Disk\\ldots{}}.\n\\item\n  Select the Liferay IntelliJ plugin and click \\emph{OK}.\n\\item\n  Navigate to the Installed tab in the top menu and select \\emph{Restart\n  IDE}.\n\\end{enumerate}\n\nOnce IntelliJ restarts, the Liferay IntelliJ plugin is installed and\nready for use.\n\nGreat job! You're now ready to develop for Liferay DXP in IntelliJ!\n\n\\chapter{Installing a Server in\nIntelliJ}\\label{installing-a-server-in-intellij}\n\nInstalling a Liferay server in IntelliJ is easy. In just a few steps,\nyou'll have your server up and running.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Tomcat and Wildfly are the only supported Liferay app\nserver bundles available to install in IntelliJ.\n\n\\noindent\\hrulefill\n\nFollow these steps to install your server:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Right-click your Liferay Workspace and select \\emph{Liferay} →\n  \\emph{InitBundle}.\n\n  This downloads the Liferay DXP bundle specified in your workspace's\n  \\texttt{gradle.properties} file. You can change the default bundle by\n  updating the \\texttt{liferay.workspace.bundle.url} property. For\n  example, this is required to update the default bundle version and/or\n  type (e.g., Wildfly). The downloaded bundle is stored in the\n  workspace's \\texttt{bundles} folder.\n\\item\n  Navigate to the top right Configurations dropdown menu and select\n  \\emph{Edit Configurations}. From here, you can configure your server's\n  run and debug configurations.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/intellij-server-dropdown.png}\n  \\caption{You have several options to choose from the server dropdown\n  menu.}\n  \\end{figure}\n\\item\n  Click the \\emph{Add New Configuration} button\n  (\\includegraphics{./images/icon-intellij-add-config.png}) and select\n  \\emph{Liferay Server}.\n\\item\n  Give your server a better name and modify any other configurations, if\n  necessary. Then select \\emph{OK} .\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/intellij-run-debug-wizard.png}\n  \\caption{Set your Liferay server's configurations in the Run/Debug\n  Configurations menu.}\n  \\end{figure}\n\\end{enumerate}\n\nYour server is now available in IntelliJ! Make sure to select it in the\nConfigurations dropdown before executing the configuration buttons\n(below).\n\nFor reference, here's how the IntelliJ configuration buttons work with\nyour Liferay DXP instance:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Start}\n  (\\includegraphics{./images/icon-intellij-start-server.png}): Starts\n  the server.\n\\item\n  \\emph{Stop}\n  (\\includegraphics{./images/icon-intellij-stop-server.png}): Stops the\n  server.\n\\item\n  \\emph{Debug}\n  (\\includegraphics{./images/icon-intellij-debug-server.png}): Starts\n  the server in debug mode. For more information on debugging in\n  IntelliJ, see the\n  \\href{https://www.jetbrains.com/help/idea/debugging-code.html}{IntelliJ\n  Debugging} article.\n\\end{itemize}\n\nNow you're ready to use your server in IntelliJ!\n\n\\chapter{Updating Liferay IntelliJ\nPlugin}\\label{updating-liferay-intellij-plugin}\n\nIf you're already using the Liferay IntelliJ plugin but need to update\nyour environment, follow the steps below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In IntelliJ, navigate to \\emph{File} → \\emph{Settings} →\n  \\emph{Plugins}.\n\\item\n  Click the \\emph{Updates} tab. If the Liferay IntelliJ plugin is\n  outdated, it will be listed as an available update.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/update-intellij-plugin.png}\n  \\caption{Check for updates periodically to ensure you're leveraging\n  the latest features.}\n  \\end{figure}\n\\item\n  Click the \\emph{Update} button (if available) to update the Liferay\n  IntelliJ plugin.\n\\item\n  Once it's downloaded, click the \\emph{Restart IDE} button.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/intellij-update-restart.png}\n  \\caption{The Available Updates prompt also prints the plugin version\n  to which you're updating.}\n  \\end{figure}\n\\end{enumerate}\n\nOnce IntelliJ restarts, the Liferay IntelliJ plugin is updated and ready\nfor use.\n\n\\chapter{Liferay JS Generator}\\label{liferay-js-generator}\n\nThe Liferay JS Generator generates JavaScript widgets using pure\nJavaScript tooling. You don't have to have a deep understanding of Java\nto write a widget for Liferay DXP. See the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-and-bundling-javascript-widgets-with-javascript-tooling}{Liferay\nJS Generator developer documentation} for more information on\nconfiguring generated JavaScript widgets. This section covers these\nreference topics for the Liferay JS Generator:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  How to install the Liferay JS Generator and use it to create a JS\n  widget\n\\item\n  An explanation of JS Portlet Extender's method signature\n\\item\n  A reference list of the available configuration options for system\n  settings and instance settings\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Liferay Bundle Generator is deprecated as of v2.7.1\nof the \\href{https://github.com/liferay/liferay-js-toolkit}{Liferay JS\nToolkit}. It has been renamed the Liferay JS Generator. If you're still\nrunning the Liferay Bundle Generator, we recommend that you install the\nLiferay JS Generator instead at your earliest convenience, as the\nLiferay Bundle Generator will be removed in future versions.\n\n\\noindent\\hrulefill\n\nThe available commands for bundles created with the Liferay JS Generator\nare listed below:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nCommand\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{npm\\ run\\ build} & Places the output of liferay-npm-bundler in\nthe designated output folder. The standard output is a JAR file that can\nbe deployed manually to Liferay DXP. \\\\\n\\texttt{npm\\ run\\ deploy} & Deploys the application to the configured\nserver. \\\\\n\\texttt{npm\\ run\\ start} & Tests the application in a local webpack\ninstallation instead of a Liferay DXP server. This speeds up development\nbecause you can see live changes without the need to deploy. Note,\nhowever, that because it's outside a Liferay instance, you don't have\naccess to Liferay's APIs. \\\\\n\\texttt{npm\\ run\\ translate} & Runs the translation features for your\nbundle. Note that this feature requires Microsoft Translator\ncredentials. See\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-translation-features-in-your-widget}{Using\nTranslation Features in Your Widget} for more information. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} By default, the webpack server uses port 8080, which\nconflicts with the port used by Tomcat. You can point the webpack server\nto a different port by setting the \\texttt{port} key in\n\\texttt{.npmbuildrc}:\n\n\\begin{verbatim}\n\"webpack\": {\n  \"port\": 2070\n}\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nRead this section to learn how to install the Liferay JS Generator and\nunderstand its configuration.\n\n\\chapter{Installing the JS Generator and Generating a\nBundle}\\label{installing-the-js-generator-and-generating-a-bundle}\n\n{This document has been replaced by an article on Liferay Learn and is\nno longer maintained here. The Liferay CLI tool is used for creating\nJavaScript application projects for Liferay versions 7.1+.}\n\nHere you'll learn how to install the\n\\href{https://www.npmjs.com/package/generator-liferay-bundle}{Liferay JS\nGenerator} and use it to create JavaScript widgets. See the\n\\href{/docs/7-2/appdev/-/knowledge_base/a/developing-an-angular-application}{Angular\nApplication},\n\\href{/docs/7-2/appdev/-/knowledge_base/a/developing-a-react-application}{React\nApplication}, and\n\\href{/docs/7-2/appdev/-/knowledge_base/a/developing-a-vue-application}{Vue\nApplication} articles to learn how to use your existing Angular, React,\nand Vue apps in Liferay DXP.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To use the Liferay JS Generator, you must have the\nLiferay JS Portlet Extender activated in your Liferay DXP instance. It's\nactivated by default. You can confirm this by opening the Control Menu,\nnavigating to the \\emph{App Manager}, and searching for\n\\texttt{com.liferay.frontend.js.portlet.extender}.\n\n\\noindent\\hrulefill\n\nFollow these steps to create your JavaScript widget:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Install \\href{http://nodejs.org/}{Node.js}. Note that Node Package\n  Manager (npm) is installed with this as well. You'll use npm to\n  install the remaining dependencies and generator, and\n  \\href{/docs/7-2/reference/-/knowledge_base/r/setting-up-your-npm-environment}{configure\n  your npm environment}.\n\\item\n  Install \\href{http://yeoman.io/}{Yeoman} for the generator:\n\n\\begin{verbatim}\nnpm install -g yeoman\n\\end{verbatim}\n\\item\n  Install the Liferay JS Generator:\n\n\\begin{verbatim}\nnpm install -g generator-liferay-js\n\\end{verbatim}\n\\item\n  Run the generator with the command below, select the JavaScript widget\n  you want to create, and answer the prompts that follow.\n\n\\begin{verbatim}\nyo liferay-js\n\\end{verbatim}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/liferay-js-generator-prompts.png}\n  \\caption{The liferay-js generator prompts you for widget options.}\n  \\end{figure}\n\\item\n  If you specified your app server information when your widget was\n  generated, you can deploy your widget by running the command below.\n  You can verify this by checking the value of the \\texttt{liferayDir}\n  entry in the widget's \\texttt{.npmbuildrc}.\n\n\\begin{verbatim}\nnpm run deploy\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! Now you know how to install and run the Liferay JS Generator.\n\n\\chapter{Understanding the JS Portlet Extender\nConfiguration}\\label{understanding-the-js-portlet-extender-configuration}\n\nBundles generated with the Liferay JS Generator require specific method\nsignatures, MANIFEST headers, and configuration within their\n\\texttt{package.json} file to use the JS Portlet Extender. This\nconfiguration is provided by default.\n\n\\section{Manifest Header}\\label{manifest-header}\n\nThe OSGi bundle contains the MANIFEST header shown below, which\nspecifies a dependency on the JS Portlet Extender:\n\n\\begin{verbatim}\nRequire-Capability: osgi.extender;filter:=\"(osgi.extender=liferay.npm.portlet)\"\n\\end{verbatim}\n\n\\section{Main Entry Point}\\label{main-entry-point}\n\nThe main module of your JavaScript widget must export a JavaScript\nfunction with the signature below. Bundles created with the Liferay JS\nGenerator have this out-of-the-box:\n\n\\begin{verbatim}\nfunction({portletNamespace, contextPath, portletElementId, configuration}) {\n  ...\n}\n\\end{verbatim}\n\nThe entry point function receives one object parameter with four fields:\n\n\\begin{itemize}\n\\item\n  \\texttt{portletNamespace}: the unique namespace of the widget as\n  defined in the Portlet specification.\n\\item\n  \\texttt{contextPath}: the URL path that can be used to retrieve bundle\n  resources from the browser (it doesn't contain the protocol, host, or\n  port, just the absolute path).\n\\item\n  \\texttt{portletElementId}: the DOM identifier of the widget's\n  \\texttt{\\textless{}div\\textgreater{}} node that can be used to render\n  HTML.\n\\item\n  \\texttt{configuration} (optional): since JS Portlet Extender version\n  1.1.0, this field contains the system (OSGi) and portlet instance\n  (preferences as described in the Portlet spec) configuration for the\n  widget. It has two subfields:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{system:} contains the system level configuration (defined in\n    Control Panel → System Settings)\n  \\item\n    \\texttt{portletInstance:} contains the per-widget configuration\n    (defined in the Configuration menu option of the widget)\n  \\end{itemize}\n\\end{itemize}\n\nNote that all values are received as strings, no matter what their type\nis in OSGi configuration store.\n\nThe JavaScript-based widget's main \\texttt{index.js} file configuration\nis shown below for reference. Note that system settings and localization\nare enabled in the example below:\n\n\\begin{verbatim}\nexport default function main({portletNamespace, contextPath, portletElementId, configuration}) {\n    \n    const node = document.getElementById(portletElementId);\n\n    node.innerHTML =`\n        <div>\n            <span class=\"tag\">${Liferay.Language.get('porlet-namespace')}:</span>\n            <span class=\"value\">${portletNamespace}</span>\n        </div>\n        <div>\n            <span class=\"tag\">${Liferay.Language.get('context-path')}:</span>\n            <span class=\"value\">${contextPath}</span>\n        </div>\n        <div>\n            <span class=\"tag\">${Liferay.Language.get('portlet-element-id')}:</span>\n            <span class=\"value\">${portletElementId}</span>\n        </div>\n        \n        <div>\n            <span class=\"tag\">${Liferay.Language.get('configuration')}:</span>\n            <span class=\"value\">\n                ${JSON.stringify(configuration, null, 2)}\n            </span>\n        </div>\n        \n    `;\n    \n}\n\\end{verbatim}\n\nThe JavaScript file containing the main entry point function is\nspecified in the \\texttt{main} entry of the \\texttt{package.json} file.\nBelow is the \\texttt{main} entry for the \\emph{JavaScript based widget}:\n\n\\begin{verbatim}\n\"main\": \"index.js\"\n\\end{verbatim}\n\n\\chapter{Configuration JSON Available\nOptions}\\label{configuration-json-available-options}\n\nIf you've\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-js-generator-and-generating-a-bundle}{created\nan OSGi bundle with the Liferay JS Generator} and want to provide system\nsettings or instance settings for your widget, you must provide a\n\\texttt{configuration.json} file. This reference guide lists the\navailable configuration options for \\texttt{configuration.json} along\nwith example code.\n\n\\section{JSON Format}\\label{json-format}\n\nThe \\texttt{configuration.json} must follow the basic pattern shown\nbelow:\n\n\\begin{verbatim}\n{\n  \"system\": {\n    \"category\": \"{category identifier}\",\n    \"name\": \"{name of configuration}\",\n    \"fields\": {\n      \"{field id 1}\": {\n        \"type\": \"{field type}\",\n        \"name\": \"{field name}\",\n        \"description\": \"{field description}\",\n        \"default\": \"{default value}\",\n        \"options\": {\n          \"{option id 1}\": \"{option name 1}\",\n          \"{option id 2}\": \"{option name 2}\",\n\n          \"{option id n}\": \"{option name n}\"\n        }\n      },\n      \"{field id 2}\": {},\n\n      \"{field id n}\": {}\n    }\n  },\n  \"portletInstance\": {\n    \"name\": \"{name of configuration}\",\n    \"fields\": {\n      \"{field id 1}\": {\n        \"type\": \"{field type}\",\n        \"name\": \"{field name}\",\n        \"description\": \"{field description}\",\n        \"default\": \"{default value}\",\n        \"options\": {\n          \"{option id 1}\": \"{option name 1}\",\n          \"{option id 2}\": \"{option name 2}\",\n\n          \"{option id n}\": \"{option name n}\"\n        }\n      },\n      \"{field id 2}\": {},\n\n      \"{field id n}\": {}\n    }\n  }\n}\n\\end{verbatim}\n\nThe available options are described in the table below:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5000}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nOption\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nValue\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{\\{category\\ identifier\\}} & Describes the identifier of the\nconfiguration category where the settings must be placed. It's\nequivalent to the category field of the\n\\texttt{@ExtendedObjectClassDefinition} annotation explained\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/categorizing-the-configuration}{here}.\nThe category field of \\texttt{configuration.json} is optional and, when\nnot set, the project's name specified in \\texttt{package.json} is used.\nYou need JS Portlet Extender 1.1.0+ for this feature to work. Otherwise,\nthe system configuration will show up under \\emph{Platform} →\n\\emph{Third Party} in System Settings. \\\\\n\\texttt{\\{name\\ of\\ configuration\\}} & the configuration's name as a\nstring or a localization key. If no value is given, the bundler falls\nback to the project's name, then description given in\n\\texttt{package.json}. \\\\\n\\texttt{\\{field\\ id\\}} & the field's name as a string or a localization\nkey \\\\\n\\texttt{\\{field\\ type\\}} & specifies the field's type, which can be one\nof the following types: ~- \\texttt{number}: an integer number~-\n\\texttt{float}: a floating point number~- \\texttt{string}: a string~-\n\\texttt{boolean}: true or false~- \\texttt{password}: a password\n(string) \\\\\n\\texttt{\\{field\\ name\\}} & the field's name as a string or a\nlocalization key \\\\\n\\texttt{\\{field\\ description\\}} & an optional string or a localization\nkey that describes the field's purpose and appears as hint text near\nit \\\\\n\\texttt{\\{default\\ value\\}} & an optional default value for the field \\\\\n\\texttt{options} & an optional section that defines a fixed set of\nvalues for the field \\\\\n\\texttt{\\{option\\ id\\}} & a string that defines the option's ID \\\\\n\\texttt{\\{option\\ name\\}} & the option's name as a string or a\nlocalization key \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nAn example configuration is shown below:\n\n\\begin{verbatim}\n{\n  \"system\": {\n    \"category\": \"third-party\",\n    \"name\": \"My project\",\n    \"fields\": {\n      \"a-number\": {\n        \"type\": \"number\",\n        \"name\": \"A number\",\n        \"description\": \"An integer number\",\n        \"default\": \"42\"\n      },\n      \"a-string\": {\n        \"type\": \"string\",\n        \"name\": \"A string\",\n        \"description\": \"An arbitrary length string\",\n        \"default\": \"this is a string\"\n      },\n      \"a-password\": {\n        \"type\": \"password\",\n        \"name\": \"A password\",\n        \"description\": \"A secret string\",\n        \"default\": \"s3.cr3t\"\n      },\n      \"a-boolean\": {\n        \"type\": \"boolean\",\n        \"name\": \"A boolean\",\n        \"description\": \"A true|false value\",\n        \"default\": true\n      },\n      \"an-option\": {\n        \"type\": \"string\",\n        \"name\": \"An option\",\n        \"description\": \"A restricted values option\",\n        \"required\": true,\n        \"default\": \"A\",\n        \"options\": {\n          \"A\": \"Option a\",\n          \"B\": \"Option b\"\n        }\n      }\n    }\n  },\n  \"portletInstance\": {\n    \"name\": \"Widget configuration\",\n    \"fields\": {\n      \"a-float\": {\n        \"type\": \"float\",\n        \"name\": \"A float\",\n        \"description\": \"A floating point number\",\n        \"default\": \"1.1\"\n      }\n    }\n  }\n}\n\\end{verbatim}\n\n\\chapter{Adapting Existing Apps to Run on Liferay\nDXP}\\label{adapting-existing-apps-to-run-on-liferay-dxp}\n\nThere are two ways to get your existing front-end applications running\non Liferay DXP:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/web-front-ends}{Migrate your\n  project} to a Liferay JS Toolkit project.\n\\item\n  Since v2.15.0 of the Liferay JS Toolkit, create projects normally, as\n  you would with\n  \\href{https://facebook.github.io/create-react-app/}{create-react-app},\n  \\href{https://cli.angular.io/}{Angular CLI} (any project containing\n  \\texttt{@angular/cli} as a dependency or devDependency), and\n  \\href{https://cli.vuejs.org/}{Vue CLI} (any project containing\n  \\texttt{@vue/cli-service} as a dependency or devDependency), and adapt\n  them to run on Liferay DXP.\n\\end{enumerate}\n\nOnly adapt your project if you intend it to be platform-agnostic. If you\nwant to integrate with Liferay DXP fully and have access to all the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-and-bundling-javascript-widgets-with-javascript-tooling}{features\nand benefits} that it provides,\n\\href{/docs/7-2/appdev/-/knowledge_base/a/web-front-ends}{migrate your\nproject} to a true Liferay JS Toolkit project instead.\n\nThe reason for this is some of Liferay DXP's features may not be\navailable because the native frameworks expect certain things. For\nexample, Angular assumes that it controls a whole Single Page\nApplication as opposed to the small portion of the page that it controls\nin a portlet-based platform such as Liferay DXP. Since webpack bundles\nall JavaScript in a single file to consume per app, if there are five\nwidgets on a page, you have five copies of the framework in the\nJavaScript interpreter. To prevent this,\n\\href{/docs/7-2/appdev/-/knowledge_base/a/web-front-ends}{migrate your\nproject} to a true Liferay JS Toolkit project instead.\n\nTo adapt your project, it must have the structure shown below:\n\n\\begin{itemize}\n\\item\n  \\textbf{Angular CLI projects} must use \\texttt{app-root} as the\n  application's Dom selector.\n\\item\n  \\textbf{creact-react-app projects} must use \\texttt{ReactDom.render()}\n  call in your entry point with a \\texttt{document.getElementById()}\n  parameter.\n\\item\n  \\textbf{Vue CLI projects} must use \\texttt{\\#app} as the application's\n  DOM selector.\n\\end{itemize}\n\nWhen your project meets the requirements, you can follow these steps to\nuse the Liferay JS Generator to adapt it:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the command line and navigate to your project's folder.\n\\item\n  Run the Liferay JS Generator's \\texttt{adapt} subtarget:\n\n\\begin{verbatim}\nyo liferay-js:adapt\n\\end{verbatim}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/liferay-js-generator-adapt-run.png}\n  \\caption{You can run the adapt subtarget of the Liferay JS Generator\n  to adapt your existing apps for Liferay.}\n  \\end{figure}\n\\item\n  Answer the prompts. An example configuration appears below:\n\n\\begin{verbatim}\n? Under which category should your widget be listed? category.sample\n? Do you have a local installation of Liferay for development? Yes\n? Where is your local installation of Liferay placed? /home/user/liferay\n\\end{verbatim}\n\n  Your project is adapted to use the Liferay JS Toolkit and run on\n  Liferay DXP!\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/liferay-js-generator-adapt-complete.png}\n  \\caption{You can run the adapt subtarget of the Liferay JS Generator\n  to adapt your existing apps for Liferay.}\n  \\end{figure}\n\\item\n  The adapt process automatically adds a few npm scripts to the\n  project's \\texttt{package.json} so you can build and deploy your\n  project to Liferay DXP. Note that you can swap \\texttt{npm} for\n  \\texttt{yarn} below if you prefer to use yarn instead.\n\n  Run the command below to add a deployable JAR to the\n  \\texttt{build.liferay} folder in your project. For example, if you\n  want to build the JAR and copy it to another app server, you can run\n  this command:\n\n\\begin{verbatim}\nnpm run build:liferay\n\\end{verbatim}\n\n  Run the command below to deploy the adapted app to your Liferay DXP\n  instance:\n\n\\begin{verbatim}\nnpm run deploy:liferay\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! Now you know how to use the Liferay JS Generator to adapt your\nexisting apps to run on Liferay DXP. See the\n\\href{https://github.com/liferay/liferay-docs/tree/master/en/developer/reference/code/adapted-react-app/}{React\nGuestbook App} for a working example of an adapted app.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/liferay-js-generator-adapt-deployed.png}\n\\caption{Your adapted app runs in Liferay in no time.}\n\\end{figure}\n\n\\chapter{Liferay Workspace}\\label{liferay-workspace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nA \\emph{Liferay Workspace} is a generated environment that is built to\nhold and manage your Liferay projects. This workspace is intended to aid\nin the management of Liferay projects by providing various build scripts\nand configured properties. You can download the\n\\href{https://sourceforge.net/projects/lportal/files/Liferay\\%20IDE/}{Liferay\nProject SDK installer} and run it to install\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI}\n(default CLI for workspace), initialize a new Liferay Workspace, and\ndownload Dev Studio DXP.\n\nLiferay Workspace is the official way to create/manage 7.0 projects\nusing Gradle. Do you prefer Maven over Gradle? You can also generate a\nMaven-based workspace.\n\nYou'll cover the following topics in this section:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-liferay-workspace}{Installing\n  Liferay Workspace}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-liferay-workspace}{Creating\n  a Liferay Workspace}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/importing-a-liferay-workspace-into-an-ide}{Importing\n  a Liferay Workspace}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/setting-proxy-requirements-for-liferay-workspace}{Setting\n  Proxy Requirements}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/adding-a-liferay-bundle-to-liferay-workspace}{Adding\n  a Bundle}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/setting-environment-configurations-for-liferay-workspace}{Setting\n  Environment Configurations}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/building-node-js-themes-in-liferay-workspace}{Building\n  Node.js Themes}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/building-gradle-maven-themes-in-liferay-workspace}{Building\n  Gradle/Maven Themes}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform}{Managing\n  the Target Platform}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/validating-modules-against-the-target-platform}{Validating\n  Modules Against the Target Platform}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/leveraging-docker}{Leveraging\n  Docker}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/updating-liferay-workspace}{Updating\n  Liferay Workspace}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/updating-default-plugins-provided-by-liferay-workspace}{Updating\n  Default Plugins Provided by Liferay Workspace}\n\\end{itemize}\n\nLiferay Workspaces can be used in many different development\nenvironments, which makes it flexible and applicable to many different\ndevelopers. For example, a Liferay Workspace easily integrates with\nEclipse and IntelliJ, providing a seamless development experience. See\nhow to\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-liferay-workspace}{install}\nand\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-liferay-workspace}{create}\na Liferay Workspace for more information.\n\nYou'll learn about workspace's anatomy and development lifecycle next.\n\n\\section{Workspace Anatomy}\\label{workspace-anatomy}\n\nA Liferay Workspace offers a development environment that can be\nconfigured to fit your development needs. Properties are available to\nhelp manage default and optional folders. This provides you the power to\ncustomize your workspace's folder structure any way you'd like. The\ntop-level files/folder of a Liferay (Gradle) Workspace are outlined\nbelow:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{bundles} (generated): the default folder for Liferay DXP\n  bundles.\n\\item\n  \\texttt{configs}: holds the configuration files for different\n  environments. These files serve as your global configuration files for\n  all Liferay DXP servers and projects residing in your workspace. To\n  learn more about using the \\texttt{configs} folder, see the\n  \\hyperref[testing-projects]{Testing Projects} section.\n\\item\n  \\texttt{ext} (generated): holds the Ext OSGi modules and Ext plugins.\n\\item\n  \\texttt{gradle}: holds the Gradle Wrapper used by your workspace.\n\\item\n  \\texttt{modules}: holds your custom modules. This can also hold\n  front-end portlets created with the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/js-generator}{Liferay JS\n  Toolkit}.\n\\item\n  \\texttt{themes}: holds Node.js-style themes that use the Liferay JS\n  Theme Toolkit, which are built using the Liferay Theme Generator.\n\\item\n  \\texttt{wars}: holds traditional WAR-style web application projects\n  and theme projects (i.e., generated by the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-template}{\\texttt{theme}}\n  project template).\n\\item\n  \\texttt{build.gradle}: the common Gradle build file.\n\\item\n  \\texttt{gradle.properties}: specifies the workspace's project\n  locations and Liferay DXP server configuration globally.\n\\item\n  \\texttt{gradle-local.properties}: sets user-specific properties for\n  your workspace. This lets multiple users use a single workspace,\n  letting them configure specific properties for the workspace on their\n  own machine.\n\\item\n  \\texttt{gradlew}: executes the Gradle command wrapper.\n\\item\n  \\texttt{settings.gradle}: applies plugins to the workspace and\n  configures its dependencies.\n\\end{itemize}\n\nIf you're using a workspace generated for Maven projects, your folder\nhierarchy is the same, except the Gradle build files are swapped out for\na \\texttt{pom.xml} file.\n\nVisit your workspace's \\texttt{gradle.properties} file for a list of\nproperties (with descriptions) you can define to adapt your workspace.\nFor a Maven-based workspace, see the\n\\href{/docs/7-2/reference/-/knowledge_base/r/bundle-support-plugin}{Bundle\nSupport Plugin} article for info on adapting your Maven workspace.\n\nIf you'd like to keep the global Gradle properties the same, but want to\nchange them for yourself only (perhaps for local testing), you can\noverride the \\texttt{gradle.properties} file with your own\n\\texttt{gradle-local.properties} file.\n\nNext, you'll learn about workspace's development lifecycle.\n\n\\section{Development Lifecycle}\\label{development-lifecycle}\n\nLiferay Workspaces offer a full development lifecycle for your projects\nto make your Liferay development easier than ever. The development\nlifecycle includes\n\n\\begin{itemize}\n\\item\n  \\hyperref[creating-projects]{Creating projects}\n\\item\n  \\hyperref[building-projects]{Building projects}\n\\item\n  \\hyperref[deploying-projects]{Deploying projects}\n\\item\n  \\hyperref[testing-projects]{Testing projects}\n\\item\n  \\hyperref[releasing-projects]{Releasing projects}\n\\item\n  \\hyperref[development-lifecycle]{Test}\n\\end{itemize}\n\nYou'll learn about each lifecycle option next.\n\n\\section{Creating Projects}\\label{creating-projects}\n\nWorkspace provides a slew of\n\\href{/docs/7-2/reference/-/knowledge_base/r/project-templates}{project\ntemplates} that you can use to create many different types of Liferay\nprojects. Workspace also provides development support for front-end\nportlets generated with the\n\\href{/docs/7-2/reference/-/knowledge_base/r/js-generator}{Liferay JS\nToolkit}. They're stored in the \\texttt{modules} folder by default.\n\nYou can also configure where to generate certain projects (modules,\nthemes, WARs, etc.). These settings are documented in the\n\\texttt{gradle.properties} file. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Creating\na Project} article for more information.\n\nLiferay Workspace manages theme projects in two separate folders based\non how they're created:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-generator}{Liferay\n  Theme Generator} (Node.js-based themes that use the Liferay JS Theme\n  Toolkit)\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-template}{Project\n  template/archetype} (Gradle/Maven-based themes)\n\\end{itemize}\n\nLiferay Workspace offers an environment where developers can use the\nLiferay Theme Generator to create themes and their work can be\nseamlessly integrated into their overall DevOps strategy. You can\nleverage the Liferay Theme Generator to\n\\href{/docs/7-2/reference/-/knowledge_base/r/building-node-js-themes-in-liferay-workspace}{create\nNode.js-based themes inside workspace} or you can leverage it externally\nand copy themes into Workspace.\n\nWorkspace also offers a\n\\href{/docs/7-2/reference/-/knowledge_base/r/building-gradle-maven-themes-in-liferay-workspace}{traditional\nJava-based theme approach} (leveraging Gradle/Maven) for those that\ncan't use the Liferay JS Theme Toolkit's tools in their CI environment.\n\n\\section{Building Projects}\\label{building-projects}\n\nLiferay Workspace abstracts many build requirements away so you can\nfocus on developing projects instead of worrying about how to build\nthem. This is done by incorporating a slew of plugins under the hood to\nallow for easily accessible tooling. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/gradle-plugins}{Gradle\nPlugins} and\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven-plugins}{Maven\nPlugins} sections for information on some of the plugins provided by\nworkspace.\n\nGradle-based workspaces also include a Gradle wrapper in its ROOT folder\n(e.g., \\texttt{gradlew}), which you can leverage to execute Gradle\ncommands. This means that you can run familiar Gradle build commands\n(e.g., \\texttt{build}, \\texttt{clean}, \\texttt{compile}, etc.) from a\nLiferay Workspace without having Gradle installed on your machine. For\nMaven-based workspaces, Maven build commands are supported (e.g.,\n\\texttt{package}, \\texttt{verify}, \\texttt{deploy}, etc.).\n\nLiferay Workspace lets you build your projects out-of-the-box without\nthe hassle of manual build configurations.\n\n\\section{Deploying Projects}\\label{deploying-projects}\n\nLiferay Workspace provides easy-to-use deployment mechanisms that let\nyou deploy your project to a Liferay server without any custom\nconfiguration. To learn more about deploying projects from a workspace,\nvisit the\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploying\na Project} article.\n\n\\section{Testing Projects}\\label{testing-projects}\n\nLiferay provides many configuration settings for 7.0. Configuring\nseveral different Liferay DXP installations to simulate/test certain\nbehaviors can become cumbersome and time consuming. With Liferay\nWorkspace, you can easily organize environment settings and generate an\nenvironment installation with those settings.\n\nLiferay Workspace provides the \\texttt{configs} folder, which lets you\nconfigure different environments in the same workspace. For example, you\ncould configure separate Liferay DXP environment settings for\ndevelopment, testing, and production in a single Liferay Workspace. So\nhow does it work?\n\nThe \\texttt{configs} folder offers six subfolders:\n\n\\texttt{common}: holds a common configuration that you want applied to\nall environments.\n\n\\texttt{dev}: holds the development configuration.\n\n\\texttt{docker}: holds the configuration for a Docker container.\n\n\\texttt{local}: holds the configuration intended for testing locally.\n\n\\texttt{prod}: holds the configuration for a production site.\n\n\\texttt{uat}: holds the configuration for a UAT site.\n\nYou're not limited to just these environments. You can create any\nsubfolder in the \\texttt{configs} folder (e.g., \\texttt{aws},\n\\texttt{test}, etc.) to simulate any environment. Each environment\nfolder can supply its own database, \\texttt{portal-ext.properties},\nElasticsearch, etc. The files in each folder overlay your Liferay DXP\ninstallation, which you generate from within workspace.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workspace-configs.png}\n\\caption{The \\texttt{configs/common} and\n\\texttt{configs/{[}environment{]}} overlay you Liferay DXP bundle when\nit's generated.}\n\\end{figure}\n\nWhen workspace generates a Liferay DXP bundle, these things happen:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Configuration files found in the \\texttt{configs/common} folder are\n  applied to the Liferay DXP bundle.\n\\item\n  The configured workspace environment (\\texttt{dev}, \\texttt{local},\n  etc.) is applied on top of any existing configurations from the\n  \\texttt{common} folder.\n\\end{enumerate}\n\nSee the\n\\href{/docs/7-2/reference/-/knowledge_base/r/setting-environment-configurations-for-liferay-workspace}{Setting\nEnvironment Configurations for Liferay Workspace} article for more\ninformation.\n\n\\section{Releasing Projects}\\label{releasing-projects}\n\nLiferay Workspace does not provide a built-in release mechanism, but\nthere are easy ways to use external release tools with workspace. The\nmost popular choice is uploading your projects to a Maven Nexus\nrepository. You could also use other release tools like\n\\href{https://www.jfrog.com/artifactory/}{Artifactory}.\n\nUploading projects to a remote repository is useful if you need to share\nthem with other non-workspace projects. Also, if you're ready for your\nprojects to be in the spotlight, uploading them to a public remote\nrepository gives other developers the chance to use them.\n\nFor more instructions on how to set up a Maven Nexus repository for your\nworkspace's projects, see the\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-maven-repository}{Creating\na Maven Repository} and\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-liferay-maven-artifacts-to-a-repository}{Deploying\nLiferay Maven Artifacts to a Repository} articles.\n\n\\chapter{Installing Liferay\nWorkspace}\\label{installing-liferay-workspace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nYou can install Liferay Workspace using the Liferay Project SDK\ninstaller. This installs JPM and\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI} into\nyour user home folder and optionally initializes a Liferay Workspace\nfolder. This is the same installer covered in the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-blade-cli}{Installing\nBlade CLI} article.\n\nFollow the steps below to download and install Liferay Workspace:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Download the latest\n  \\href{https://sourceforge.net/projects/lportal/files/Liferay\\%20IDE/}{Liferay\n  Project SDK installer} that corresponds with your operating system\n  (e.g., Windows, MacOS, or Linux). The Project SDK installer is listed\n  under \\emph{Liferay IDE}, so the folder versions are based on IDE\n  releases. You can select an installer that does not include Dev Studio\n  DXP, if you don't intend to use it. The Project SDK installer is\n  available for versions 3.2.0+. Do \\textbf{not} select the large green\n  download button; this downloads Liferay Portal instead.\n\\item\n  Run the installer. Click \\emph{Next} to step through the installer's\n  introduction.\n\\item\n  Set the folder where your Liferay Workspace should be initialized.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/blade-installer-workspace-init.png}\n  \\caption{Determine where your Liferay Workspace should reside.}\n  \\end{figure}\n\n  Then click \\emph{Next}.\n\\item\n  Choose the Liferay product type you intend to use with the workspace.\n  Then click \\emph{Next}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/installer-workspace-type.png}\n  \\caption{Select the product version you'll use with your Liferay\n  Workspace.}\n  \\end{figure}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You'll be prompted for your liferay.com username and password\n before downloading the Liferay DXP bundle. Your credentials are not saved\n locally; they're saved as a token in the `~/.liferay` folder. The token is\n used by your workspace if you ever decide to redownload a DXP bundle.\n Furthermore, the bundle that is downloaded in your workspace is also\n copied to your `~/.liferay/bundles` folder, so if you decide to initialize\n another Liferay DXP instance of the same version, the bundle is not\n re-downloaded. See the\n [Adding a Liferay Bundle to Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/adding-a-liferay-bundle-to-liferay-workspace)\n for more information on this topic.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{4}\n\\tightlist\n\\item\n  Click \\emph{Next} to begin installing Liferay Workspace on your\n  machine.\n\\end{enumerate}\n\nThat's it! Liferay Workspace is now installed on your machine!\n\n\\chapter{Creating a Liferay\nWorkspace}\\label{creating-a-liferay-workspace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nYou can create a Liferay Workspace using the following tools:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[blade-cli]{Blade CLI}\n\\item\n  \\hyperref[dev-studio]{Dev Studio}\n\\item\n  \\hyperref[intellij]{IntelliJ}\n\\item\n  \\hyperref[maven]{Maven}\n\\end{itemize}\n\nVisit the appropriate section to learn how to create a workspace with\nthe highlighted tool.\n\n\\section{Blade CLI}\\label{blade-cli-3}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the folder where you want your workspace generated.\n\\item\n  Run the following command to build a Gradle-based workspace:\n\n\\begin{verbatim}\nblade init -v 7.2 [WORKSPACE_NAME]\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note**: The version you set when first initializing your workspace is\n stored in the workspace's `.blade.properties` file with the\n `liferay.version.default` property. This version is applied when creating\n projects based on the corresponding project template versions.\n\n If you wish to develop projects for a different Liferay DXP version, you can\n pass a different version in the Blade init command. For example,\n\n ```bash\n blade init -v 7.0 [WORKSPACE_NAME]\n ```\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nYou can also create a Maven-based workspace with Blade CLI. See the\n\\hyperref[maven]{Maven} section for more information.\n\n\\section{Dev Studio}\\label{dev-studio}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select \\emph{File} → \\emph{New} → \\emph{Liferay Workspace Project}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/selecting-liferay-workspace.png}\n  \\caption{By selecting \\emph{Liferay Workspace Project}, you begin the\n  process of creating a new workspace for your Liferay projects.}\n  \\end{figure}\n\n  A New Liferay Workspace dialog appears, presenting several\n  configuration options.\n\\item\n  Give your workspace project a name.\n\\item\n  Choose the location where you'd like your workspace to reside.\n  Checking the \\emph{Use default location} checkbox places your Liferay\n  Workspace in the Eclipse workspace you're working in.\n\\item\n  Select the build tool you want your workspace to be built with (i.e.,\n  Gradle or Maven).\n\\item\n  Choose the Liferay Portal version you plan to develop for (i.e., 7.2,\n  7.1, or 7.0).\n\\item\n  Select the specific target platform version corresponding to the GA\n  release you're developing for (e.g., 7.2.0 → 7.2 GA1). For more\n  information on target platform benefits, see the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform}{Managing\n  the Target Platform} articles.\n\\item\n  Check the \\emph{Download Liferay bundle} checkbox if you'd like to\n  auto-generate a Liferay instance in your workspace. You'll be prompted\n  to name the server and provide the server's download URL, if selected.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You can configure a pre-existing Liferay bundle in your\n workspace by creating a folder for the bundle in your workspace and\n configuring it in the workspace's `gradle.properties` file by setting the\n `liferay.workspace.home.dir` property.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{7}\n\\item\n  Check the \\emph{Add project to working set} checkbox if you want your\n  workspace to be a part of a larger working set you've already created\n  in Dev Studio. For more information on working sets, visit\n  \\href{https://help.eclipse.org/mars/index.jsp?topic=\\%2Forg.eclipse.platform.doc.user\\%2Fconcepts\\%2Fcworkset.htm}{Eclipse\n  Help}.\n\\item\n  Click \\emph{Finish} to create your Liferay Workspace.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/new-workspace-menu.png}\n  \\caption{Dev Studio provides an easy-to-follow menu to create your\n  Liferay Workspace.}\n  \\end{figure}\n\\end{enumerate}\n\nA dialog appears prompting you to open the Liferay Workspace\nperspective. Click \\emph{Yes}, and your perspective will switch to\nLiferay Workspace.\n\n\\section{IntelliJ}\\label{intellij-1}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the New Project wizard by selecting \\emph{File} → \\emph{New} →\n  \\emph{Project}. If you're starting IntelliJ for the first time, you\n  can do this by selecting \\emph{Create New Project} in the opening\n  window.\n\\item\n  Select \\emph{Liferay} from the left menu.\n\\item\n  Choose the build type for your workspace (i.e., Gradle or Maven). Then\n  click \\emph{Next}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/intellij-workspace-build.png}\n  \\caption{Choose \\emph{Liferay Gradle Workspace} or \\emph{Liferay Maven\n  Workspace}, depending on the build you prefer.}\n  \\end{figure}\n\\item\n  Specify your workspace's name, location, intended Liferay DXP version,\n  \\href{/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform}{target\n  platform}, and SDK (i.e., Java JDK). Then click \\emph{Finish}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/intellij-workspace-project.png}\n  \\caption{Specify your workspace's configurations.}\n  \\end{figure}\n\\item\n  A window opens for additional build configurations for the build type\n  you selected (i.e., Gradle or Maven). Verify the settings and click\n  \\emph{OK}.\n\\end{enumerate}\n\n\\section{Maven}\\label{maven-2}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Execute the following Maven command:\n\n\\begin{verbatim}\nmvn archetype:generate -Dfilter=liferay\n\\end{verbatim}\n\\item\n  Select the \\texttt{com.liferay.project.templates.workspace} archetype\n  to generate.\n\\item\n  Step through the remaining prompts to generate the workspace project.\n\\end{enumerate}\n\nA Maven-based Liferay Workspace can also be generated using Blade CLI.\nFollow \\hyperref[blade-cli]{Blade CLI's} workspace creation instructions\nand insert the \\texttt{-b\\ \\ maven} parameter in the Blade command.\n\n\\chapter{Importing a Liferay Workspace into an\nIDE}\\label{importing-a-liferay-workspace-into-an-ide}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay supports two IDEs with preconfigured Liferay Workspace wizards\nand functionalities\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[dev-studio]{Dev Studio}\n\\item\n  \\hyperref[intellij]{IntelliJ}\n\\end{itemize}\n\nThese aren't the only IDEs you can leverage to use Liferay Workspace,\nbut they are the ones with out-of-the-box support for it.\n\nVisit the appropriate section to learn how to import a workspace with\nthe highlighted tool.\n\n\\section{Dev Studio}\\label{dev-studio-1}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{File} → \\emph{Import} → \\emph{Liferay} →\n  \\emph{Liferay Workspace Project}.\n\\item\n  Click \\emph{Next} and browse for your workspace project.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/liferay-workspace-import.png}\n  \\caption{You can import an existing Liferay Workspace into your\n  current Dev Studio session.}\n  \\end{figure}\n\\item\n  Once you've selected you workspace, click \\emph{Finish}.\n\\end{enumerate}\n\n\\section{IntelliJ}\\label{intellij-2}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select \\emph{File} → \\emph{New} → \\emph{Project from Existing\n  Sources\\ldots{}}.\n\\item\n  Select the workspace you want to import. Then click \\emph{OK}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/intellij-import-workspace.png}\n  \\caption{Specify your workspace's configurations.}\n  \\end{figure}\n\\item\n  Click the \\emph{Import project from external model} radio button and\n  select the build tool your workspace uses (e.g., Gradle or Maven).\n\\item\n  Configure the project import (if necessary) and then click\n  \\emph{Finish}. See the\n  \\href{https://www.jetbrains.com/help/idea/creating-and-managing-projects.html\\#importing-project}{Import\n  a Project} section of IntelliJ's official documentation for more\n  information.\n\\item\n  Step through the remaining import prompts and then open your imported\n  workspace as you desire (i.e., in the current window or a new window).\n\\end{enumerate}\n\n\\chapter{Setting Proxy Requirements for Liferay\nWorkspace}\\label{setting-proxy-requirements-for-liferay-workspace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nIf you're working behind a corporate firewall that requires using a\nproxy server to access external repositories, you need to add some extra\nconfiguration to make Liferay Workspace work within your environment.\nYou'll learn how to set proxy requirements for both Gradle and Maven\nenvironments.\n\n\\section{Gradle}\\label{gradle-1}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your \\texttt{\\textasciitilde{}/.gradle/gradle.properties} file.\n  Create this file if it does not exist.\n\\item\n  Add the following properties to the file:\n\n\\begin{verbatim}\nsystemProp.http.proxyHost=www.somehost.com\nsystemProp.http.proxyPort=1080\nsystemProp.https.proxyHost=www.somehost.com\nsystemProp.https.proxyPort=1080\n\\end{verbatim}\n\n  Make sure to replace the proxy host and port values with your own.\n\\item\n  If the proxy server requires authentication, also add the following\n  properties:\n\n\\begin{verbatim}\nsystemProp.http.proxyUser=userId\nsystemProp.http.proxyPassword=yourPassword\nsystemProp.https.proxyUser=userId\nsystemProp.https.proxyPassword=yourPassword\n\\end{verbatim}\n\\end{enumerate}\n\nExcellent! Your proxy settings are set in your Liferay Workspace's\nGradle environment.\n\n\\section{Maven}\\label{maven-3}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your \\texttt{\\textasciitilde{}/.m2/settings.xml} file. Create\n  this file if it does not exist.\n\\item\n  Add the following XML snippet to the file:\n\n\\begin{verbatim}\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n    <settings xmlns=\"http://maven.apache.org/SETTINGS/1.0.0\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd\">\n        <proxies>\n            <proxy>\n                <id>httpProxy</id>\n                <active>true</active>\n                <protocol>http</protocol>\n                <host>www.somehost.com</host>\n                <port>1080</port>\n            </proxy>\n            <proxy>\n                <id>httpsProxy</id>\n                <active>true</active>\n                <protocol>https</protocol>\n                <host>www.somehost.com</host>\n                <port>1080</port>\n            </proxy>\n        </proxies>\n    </settings>\n\\end{verbatim}\n\n  Make sure to replace the proxy host and port values with your own.\n\\item\n  If the proxy server requires authentication, also add the\n  \\texttt{username} and \\texttt{password} proxy properties. For example,\n  the HTTP proxy authentication configuration would look like this:\n\n\\begin{verbatim}\n<proxy>\n  <id>httpProxy</id>\n  <active>true</active>\n  <protocol>http</protocol>\n  <host>www.somehost.com</host>\n  <port>1080</port>\n  <username>userID</username>\n  <password>somePassword</password>\n</proxy>\n\\end{verbatim}\n\\end{enumerate}\n\nExcellent! Your Maven proxy settings are now set.\n\n\\chapter{Adding a Liferay Bundle to Liferay\nWorkspace}\\label{adding-a-liferay-bundle-to-liferay-workspace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay Workspaces can generate and hold a Liferay Server. This lets you\nbuild/test your workspace's plugins against a running Liferay instance.\nFollow the instructions below to get started.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your workspace's root \\texttt{gradle.properties} file.\n\\item\n  Set the \\texttt{liferay.workspace.bundle.url} property to the bundle's\n  download URL you want to generate and install. For example,\n\n\\begin{verbatim}\nliferay.workspace.bundle.url=https://releases-cdn.liferay.com/portal/7.2.0-ga1/liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761.7z\n\\end{verbatim}\n\n  For DXP subscribers, it would look like this:\n\n\\begin{verbatim}\nliferay.workspace.bundle.url=https://api.liferay.com/downloads/portal/7.2.10/liferay-dxp-tomcat-7.2.10-ga1-20190531140450482.7z\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The DXP download URL must be set to the bundle hosted on\n *api.liferay.com*. It can be tricky to find the fully qualified bundle\n name/number for the DXP bundle you want. You cannot access Liferay's API\n site directly to find it, so you must start to download DXP manually from\n Liferay's Customer Portal, take note of the file name, and append it to\n `https://api.liferay.com/downloads/portal/`.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nDXP subscribers must also set the `liferay.workspace.bundle.token.download`\nproperty to `true` to allow your workspace to access Liferay's API site.\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Navigate to your workspace's root folder and run\n\n\\begin{verbatim}\nblade server init\n\\end{verbatim}\n\\item\n  Verify your bundle was downloaded. The bundle is generated in the\n  \\texttt{bundles} folder by default. You can change this by setting the\n  \\texttt{gradle.properties} file's \\texttt{liferay.workspace.home.dir}\n  property to a different folder.\n\\end{enumerate}\n\nYou can also produce a distributable Liferay bundle (Zip or Tar) from\nwithin a workspace. To do this, navigate to your workspace's root folder\nand run the following command:\n\n\\begin{verbatim}\n./gradlew distBundle[Zip|Tar]\n\\end{verbatim}\n\nYour distribution file is available from the workspace's \\texttt{/build}\nfolder.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can define different environments for your Liferay\nbundle for easy testing. You can learn more about this in the\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace\\#testing-projects}{Testing\nProjects} section.\n\n\\noindent\\hrulefill\n\nYou're all set to develop projects for a nested Liferay DXP bundle.\n\n\\chapter{Setting Environment Configurations for Liferay\nWorkspace}\\label{setting-environment-configurations-for-liferay-workspace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay Workspace offers the \\texttt{configs} folder, which provides a\nway to organize multiple environment settings and generate a Liferay\nbundle for each environment configuration.\n\nTo simulate using the \\texttt{configs} folder, you'll explore a typical\nscenario. Suppose you want a local Liferay DXP installation for testing\nand a UAT installation for simulating a production site. Assume you want\nthe following configuration for the two environments:\n\n\\textbf{Local Environment}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Use MySQL database pointing to localhost\n\\item\n  Skip setup wizard\n\\end{itemize}\n\n\\textbf{UAT Environment}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Use MySQL database pointing to a live server\n\\item\n  Skip setup wizard\n\\end{itemize}\n\nTo configure these two environments in your workspace, follow the steps\nbelow:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\texttt{configs/common} folder and add the\n  \\texttt{portal-setup-wizard.properties} file with the\n  \\texttt{setup.wizard.enabled=false} property.\n\\item\n  Open the \\texttt{configs/local} folder and configure the MySQL\n  database settings for localhost in a \\texttt{portal-ext.properties}\n  file.\n\\item\n  Open the \\texttt{configs/uat} folder and configure the MySQL database\n  settings for the live server in a \\texttt{portal-ext.properties} file.\n\\item\n  Now that your two environments are configured, generate one of them:\n\n\\begin{verbatim}\nblade server init --environment uat\n\\end{verbatim}\n\\item\n  To generate a distributable Liferay DXP installation of the\n  environment to the workspace's \\texttt{/build} folder, run\n\n\\begin{verbatim}\n./gradlew distBundle[Zip|Tar] -Pliferay.workspace.environment=uat\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You may prefer to set your workspace environment in the\n `gradle.properties` file instead of passing it via Gradle command. If so,\n it's recommended to set the workspace environment variable inside the\n `[USER_HOME]/.gradle/gradle.properties` file.\n \n ```properties\n liferay.workspace.environment=local\n ```\n \n The variable is set to `local` by default.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nYou've successfully configured two environments and generated one of them.\n\\end{verbatim}\n\nAwesome! You can now test various Liferay DXP bundle environments using\nLiferay Workspace.\n\n\\chapter{Building Node.js Themes in Liferay\nWorkspace}\\label{building-node.js-themes-in-liferay-workspace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay Workspace reserves the \\texttt{themes} folder only for themes\nthat are created with the Themes Generator. There are no Blade\nCLI-provided commands or Maven archetypes to generate a theme for this\nfolder. You must leverage the\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-generator}{Liferay\nTheme Generator} from within the \\texttt{themes} folder to create them;\nyou can also copy a generated theme into the folder.\n\nYou'll demo this theme management capability next. Be sure the Liferay\nTheme Generator's required tooling is installed.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to your workspace's \\texttt{themes} folder and run the\n  following command:\n\n\\begin{verbatim}\nyo liferay-theme\n\\end{verbatim}\n\n  Follow the prompts to create your theme.\n\\item\n  Navigate into your new theme and run\n\n\\begin{verbatim}\n./gradlew build\n\\end{verbatim}\n\n  Liferay Workspace builds the front-end theme using Gradle. Under the\n  hood, Liferay's\n  \\href{/docs/7-2/reference/-/knowledge_base/r/node-gradle-plugin}{Node\n  Gradle Plugin} is applied and used to build your theme.\n\\item\n  Workspace is smart enough to differentiate between theme types. For\n  instance, you can't copy a theme built with the Theme Generator into\n  the \\texttt{wars} folder and expect it to build. You can test if your\n  project is recognized by workspace by running this command from\n  workspace's root folder:\n\n\\begin{verbatim}\n./gradlew projects\n\\end{verbatim}\n\n  Your CLI should display your new theme under the \\texttt{themes}\n  project.\n\n  ```bash Root project `liferay-workspace' +--- Project `:themes'\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n    \\--- Project ':themes:my-generated-theme'\n\\end{verbatim}\n\n\\noindent\\hrulefill ```\n\n\\begin{verbatim}\nIf you moved a WAR-style theme (Gradle/Maven-based) into the `themes`\nfolder, it is not recognized by the Gradle `projects` command.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Workspace identifies whether a theme was generated by the\nTheme Generator by checking whether it has a \\texttt{package.json} file.\nAny theme without this file is not compatible in the \\texttt{themes}\nfolder.\n\n\\noindent\\hrulefill\n\nExcellent! You learned how generated themes are recognized in workspace\nand where they should reside. For more information on building\nGradle/Maven-based themes in workspace, see its dedicated\n\\href{/docs/7-2/reference/-/knowledge_base/r/building-gradle-maven-themes-in-liferay-workspace}{article}.\n\n\\chapter{Building Gradle/Maven Themes in Liferay\nWorkspace}\\label{building-gradlemaven-themes-in-liferay-workspace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay Workspace provides the \\texttt{wars} folder for any WAR-style\nproject. Themes created with\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI} or\nMaven using the\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-template}{\\texttt{theme}}\nproject template or archetype are automatically generated here when\ncreating the project within Workspace.\n\nFollow the steps below to build a Gradle/Maven theme in workspace's\n\\texttt{wars} folder:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Follow the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-project}{Creating\n  a Project} article to generate a project based on a project template\n  or archetype. Make sure to select the \\texttt{theme} template.\n\n  Themes built using Liferay's \\texttt{theme} project template are\n  always WARs and should always reside in Workspace's \\texttt{wars}\n  folder. They should never be moved to the \\texttt{themes} folder; that\n  folder is reserved for\n  \\href{/docs/7-2/reference/-/knowledge_base/r/building-node-js-themes-in-liferay-workspace}{themes\n  generated by the Theme Generator}.\n\\item\n  Navigate into your new theme and run\n\n\\begin{verbatim}\n./gradlew build\n\\end{verbatim}\n\n  Liferay Workspace builds the theme using Gradle. Under the hood,\n  Liferay's\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin}{Theme\n  Builder Gradle Plugin} is applied and used to build your theme. It\n  works similarly in a Maven workspace. See the\n  \\href{/docs/7-1/frameworks/-/knowledge_base/frameworks/building-themes-in-a-maven-project}{Building\n  Themes in a Maven Project} article for more information.\n\\end{enumerate}\n\nAwesome! You know how WAR-style themes are built in workspace and where\nthey should reside.\n\n\\chapter{Managing the Target\nPlatform}\\label{managing-the-target-platform}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Target Platform articles currently assume you're\nusing Gradle as a build tool. If your projects are built with Maven, you\ncan still leverage the Target Platform features, but it is not built\ninto Liferay Workspace \\emph{yet}\n(\\href{https://issues.liferay.com/browse/LPS-90524}{LPS-90524}). See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/targeting-a-platform-with-maven}{Targeting\na Platform with Maven} article to set the Target Platform for\nMaven-based projects.\n\n\\noindent\\hrulefill\n\nLiferay Workspace helps you target a specific release of Liferay DXP, so\ndependencies get resolved properly. This makes upgrades easy: specify\nyour target platform, and Workspace points to the new version. All your\ndependencies are updated to the latest ones provided in the targeted\nrelease.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} There are times when configuring dependencies based on a\nversion range is better than tracking exact versions. See the\n\\href{/docs/7-2/customization/-/knowledge_base/c/semantic-versioning}{Semantic\nVersioning} tutorial for more details.\n\n\\noindent\\hrulefill\n\nNext, you'll discover how all of this is possible.\n\n\\section{Dependency Management with\nBOMs}\\label{dependency-management-with-boms}\n\nYou can target a version by importing a predefined bill of materials\n(BOM). This only requires that you specify a property in your\nworkspace's \\texttt{gradle.properties} file (see\n\\href{/docs/7-2/reference/-/knowledge_base/r/setting-the-target-platform}{this\narticle} for details).\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Target Platform feature is only supported for Gradle\nprojects at this time.\n\n\\noindent\\hrulefill\n\nEach Liferay DXP version has a predefined BOM that you can specify for\nyour workspace to reference. Each BOM defines the artifacts and their\nversions used in the specific release. BOMs list all dependencies in a\nmanagement fashion, so it doesn't \\textbf{add} dependencies to your\nproject; it only \\textbf{provides} your build tool (e.g., Gradle or\nMaven) the versions needed for the project's defined artifacts. This\nmeans you don't need to specify your dependency versions; the BOM\nautomatically defines the appropriate artifact versions based on the\nBOM.\n\nYou can override a BOM's defined artifact version by specifying a\ndifferent version in your project's \\texttt{build.gradle}. Artifact\nversions defined in your project's build files override those specified\nin the predefined BOM. Note that overriding the BOM can be dangerous;\nmake sure the new version is compatible in the targeted platform.\n\nFor more information on BOMs, see the\n\\href{https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism\\#Importing_Dependencies}{Importing\nDependencies} section in Maven's official documentation. To view a BOM\nfile and its mapping of artifacts and versions, visit\n\\href{https://repository.liferay.com}{repository.liferay.com} and search\nfor the BOM artifacts (e.g.,\n\\href{https://repository.liferay.com/nexus/index.html\\#nexus-search;quick~release.portal.bom}{release.portal.bom}\nand\n\\href{https://repository.liferay.com/nexus/index.html\\#nexus-search;quick~release.dxp.bom}{release.dxp.bom}).\n\nPretty cool, right? Next, you'll learn how to leverage platform\ntargeting in Dev Studio.\n\n\\section{Leveraging Target Platform in Dev\nStudio}\\label{leveraging-target-platform-in-dev-studio}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\nDev Studio 3.2+} helps you streamline targeting a specific version even\nmore. Dev Studio can index the configured Liferay DXP source code to\n\n\\begin{itemize}\n\\tightlist\n\\item\n  provide advanced Java search (Open Type and Reference Searching)\n  (\\href{/docs/7-2/reference/-/knowledge_base/r/searching-product-source-in-dev-studio}{article})\n\\item\n  debug Liferay DXP sources\n  (\\href{/docs/7-2/reference/-/knowledge_base/r/debugging-product-source-in-dev-studio}{article})\n\\end{itemize}\n\nTo enable this functionality, set the following property in your\nworkspace's \\texttt{gradle.properties} file:\n\n\\begin{verbatim}\ntarget.platform.index.sources=true\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Portal source indexing is disabled in Gradle workspace\nversion 2.0.3+ (Target Platform plugin version 2.0.0+). See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/updating-liferay-workspace}{Updating\nLiferay Workspace} article for instructions on how to update your\nworkspace.\n\n\\noindent\\hrulefill\n\nThese options in Dev Studio are only available when developing in a\nLiferay Workspace, or if you have the\n\\href{/docs/7-2/reference/-/knowledge_base/r/target-platform-gradle-plugin}{Target\nPlatform Gradle plugin} applied to your multi-module Gradle project with\nspecific configurations. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/targeting-a-platform-outside-of-workspace}{Targeting\na Platform Outside of Workspace} article for more info on applying the\nTarget Platform Gradle plugin.\n\nContinue on to learn how to set the target platform.\n\n\\chapter{Setting the Target Platform}\\label{setting-the-target-platform}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nSetting the target platform version to develop for takes two steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the workspace's \\texttt{gradle.properties} file and set the\n  \\texttt{liferay.workspace.target.platform.version} property to the\n  version you want to target. For example,\n\n\\begin{verbatim}\nliferay.workspace.target.platform.version=7.2.0\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You must explicitly uncomment the property in your workspace's\n `gradle.properties` file to set it. Target Platform is not enabled by\n default.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nIf you're using Liferay DXP, you can set the property like this:\n\n```properties\nliferay.workspace.target.platform.version=7.2.10\n```\n\nThe versions following a GA1 release of DXP follow fix pack versions (e.g.,\n`7.2.10.fp1`, `7.2.10.fp2`, etc.).\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Once the target platform is configured, check to make sure no\n  dependencies in your Gradle build files specify a version. The\n  versions are now imported from the configured target platform's BOM.\n  For example, a simple MVC portlet's \\texttt{build.gradle} may look\n  something like this:\n\n\\begin{verbatim}\ndependencies {\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\"\n    compileOnly group: \"javax.portlet\", name: \"portlet-api\"\n    compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n    compileOnly group: \"jstl\", name: \"jstl\"\n    compileOnly group: \"org.osgi\", name: \"osgi.cmpn\"\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: The \\texttt{liferay.workspace.target.platform.version}\nproperty also sets the distro JAR, which can be used to validate your\nprojects during the build process. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/validating-modules-against-the-target-platform}{Validating\nModules Against the Target Platform} articles for more info.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The target platform functionality is available in Liferay\nWorkspace version 1.9.0+. If you have an older version, you must update\nit to leverage platform targeting. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/updating-liferay-workspace}{Updating\nLiferay Workspace} article to do this.\n\n\\noindent\\hrulefill\n\nYou've configured your target platform in workspace. You're all set!\n\n\\chapter{Targeting a Platform Outside of\nWorkspace}\\label{targeting-a-platform-outside-of-workspace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nIf you prefer to not use Liferay Workspace, but still want to target a\nplatform, you must apply the\n\\href{/docs/7-2/reference/-/knowledge_base/r/target-platform-gradle-plugin}{Target\nPlatform Gradle plugin} to the root \\texttt{build.gradle} file of your\ncustom multi-module Gradle build.\n\nTo do this, follow the steps below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your project's \\texttt{build.gradle} file and add this:\n\n\\begin{verbatim}\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.target.platform\", version: \"2.0.0\"\n    }\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\\end{verbatim}\n\n  This sets the dependency on the Target Platform Gradle plugin and\n  configures the repository that provides the necessary artifacts for\n  your project build.\n\\item\n  Apply Liferay's Target Platform Gradle plugin to the\n  \\texttt{build.xml} file:\n\n\\begin{verbatim}\napply plugin: \"com.liferay.target.platform\"\n\\end{verbatim}\n\\item\n  Set the Target Platform plugin's dependencies:\n\n\\begin{verbatim}\ndependencies {\n    targetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom\", version: \"7.2.0\"\n    targetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom.compile.only\", version: \"7.2.0\"\n    targetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom.third.party\", version: \"7.2.0\"\n}\n\\end{verbatim}\n\n  These dependencies are described below:\n\n  \\texttt{com.liferay.ce.portal.bom}: provides all the artifacts\n  included in Liferay DXP.\n\n  \\texttt{com.liferay.ce.portal.compile.only}: provides artifacts that\n  are not included in Liferay DXP, but are necessary to reference during\n  the build (e.g., \\texttt{org.osgi.core}).\n\n  \\texttt{release.portal.bom.third.party}: provides all third party\n  artifacts that make up the Liferay Portal bundle.\n\n  Liferay DXP users must replace the artifact names and versions:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{release.portal.bom} → \\texttt{release.dxp.bom}\n  \\item\n    \\texttt{release.portal.bom.compile.only} →\n    \\texttt{release.dxp.bom.compile.only}\n  \\item\n    \\texttt{release.portal.bom.third.party} →\n    \\texttt{release.dxp.bom.third.party}\n  \\item\n    \\texttt{7.2.0} → \\texttt{7.2.10}\n  \\end{itemize}\n\\item\n  If you're interested in\n  \\href{/docs/7-2/reference/-/knowledge_base/r/searching-product-source-in-dev-studio}{advanced\n  search} and/or\n  \\href{/docs/7-2/reference/-/knowledge_base/r/debugging-product-source-in-dev-studio}{debugging}\n  Liferay DXP's source using\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\n  Dev Studio}, you must also apply the following configuration:\n\n\\begin{verbatim}\ntargetPlatformIDE {\n    includeGroups \"com.liferay\", \"com.liferay.portal\"\n}\n\\end{verbatim}\n\n  This indexes the target platform's source code and makes it available\n  to Dev Studio.\n\\end{enumerate}\n\nNow you can define your target platform!\n\n\\chapter{Targeting a Platform with\nMaven}\\label{targeting-a-platform-with-maven}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAlthough a Maven-based Liferay Workspace does not offer a configurable\nproperty to set the target platform, you can still leverage the Target\nPlatform framework by adding a few dependencies to your project.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your workspace's root \\texttt{pom.xml} file and add the following\n  dependencies:\n\n\\begin{verbatim}\n<dependencyManagement>\n    <dependencies>\n        <dependency>\n            <groupId>com.liferay.portal</groupId>\n            <artifactId>release.portal.bom</artifactId>\n            <version>7.2.0</version>\n            <type>pom</type>\n            <scope>import</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.liferay.portal</groupId>\n            <artifactId>release.portal.bom.compile.only</artifactId>\n            <version>7.2.0</version>\n            <type>pom</type>\n            <scope>import</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.liferay.portal</groupId>\n            <artifactId>release.portal.bom.third.party</artifactId>\n            <version>7.2.0</version>\n            <type>pom</type>\n            <scope>import</scope>\n        </dependency>\n    </dependencies>\n</dependencyManagement>\n\\end{verbatim}\n\n  These dependencies are described below:\n\n  \\texttt{com.liferay.ce.portal.bom}: provides all the artifacts\n  included in Liferay DXP.\n\n  \\texttt{com.liferay.ce.portal.compile.only}: provides artifacts that\n  are not included in Liferay DXP, but are necessary to reference during\n  the build (e.g., \\texttt{org.osgi.core}).\n\n  \\texttt{release.portal.bom.third.party}: provides all third party\n  artifacts that make up the Liferay Portal bundle.\n\n  Liferay DXP users must replace the artifact names and versions:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{release.portal.bom} → \\texttt{release.dxp.bom}\n  \\item\n    \\texttt{release.portal.bom.compile.only} →\n    \\texttt{release.dxp.bom.compile.only}\n  \\item\n    \\texttt{release.portal.bom.third.party}\n  \\item\n    \\texttt{7.2.0} → \\texttt{7.2.10}\n  \\end{itemize}\n\\item\n  Go through the remaining POMs in your workspace and remove\n  \\texttt{\\textless{}version\\textgreater{}} tags for all\n  Liferay-specific artifacts. These versions are now being provided by\n  the Target Platform framework.\n\\end{enumerate}\n\nGreat! You can now target a platform in your Maven-based workspace.\n\n\\chapter{Validating Modules Against the Target\nPlatform}\\label{validating-modules-against-the-target-platform}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Validating modules with the \\texttt{resolve} task is\ndeprecated. It only functions as it's documented here in versions prior\nto Liferay Workspace (Gradle only) version 2.0.3. It is being redesigned\nfor workspace versions 2.0.3+ and is still in development at this time.\n\n\\noindent\\hrulefill\n\nAfter you write a module in Liferay Workspace, you can validate it\nbefore deployment to make sure of several things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Will my app deploy successfully?\n\\item\n  Will there be some sort of missing requirement?\n\\item\n  If there's an issue, how do I diagnose it?\n\\end{itemize}\n\nThese are all common worries that can be frustrating.\n\nInstead of deploying your app and checking for errors in the log, you\ncan validate your app before deployment. This is done by calling Liferay\nWorkspace's \\texttt{resolve} task, which validates your modules against\na targeted platform.\n\nYou'll cover the following topics in this section:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[resolving-your-modules]{Resolving your modules}.\n\\item\n  \\hyperref[modifying-the-target-platforms-capabilities]{Modifying the\n  target platform's capabilities}.\n\\item\n  \\hyperref[including-the-resolver-in-your-gradle-build]{Including the\n  resolver in your Gradle build}.\n\\end{itemize}\n\nContinue on to learn how this works.\n\n\\section{Resolving Your Modules}\\label{resolving-your-modules}\n\nYou can resolve your modules before deployment. This can be done by\ncalling the \\texttt{resolve} Gradle task provided by Liferay Workspace.\n\n\\begin{verbatim}\n./gradlew resolve\n\\end{verbatim}\n\nThis task gathers all the capabilities provided by\n\n\\begin{itemize}\n\\tightlist\n\\item\n  the specified version of Liferay DXP (i.e.,\n  \\href{/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform}{targeted\n  platform})\n\\item\n  the current workspace's modules\n\\end{itemize}\n\nSome capabilities/information gathered by the \\texttt{resolve} task that\nare validated include\n\n\\begin{itemize}\n\\tightlist\n\\item\n  declared required capabilities\n\\item\n  module versions\n\\item\n  package imports/use constraints\n\\item\n  service references\n\\end{itemize}\n\nIt also computes a list of run requirements for your project. Then it\ncompares the current project's requirements against the gathered\ncapabilities. If your project requires something not available in the\ngathered list of capabilities, the task fails.\n\nThe task can only validate OSGi modules. It does not work with WAR-style\nprojects, themes, or npm portlets.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\texttt{resolve} task can be executed from a specific\nproject folder or from the workspace's root folder. Running the task\nfrom the root folder validates all the modules in your workspace.\n\n\\noindent\\hrulefill\n\nThe \\texttt{resolve} task can automatically gather the available\ncapabilities from your workspace, but you must specify this for your\ntargeted Liferay DXP version. To do this, open your workspace's\n\\texttt{gradle.properties} file and set the\n\\texttt{liferay.workspace.target.platform.version} property to the\nversion you want to target. For example,\n\n\\begin{verbatim}\nliferay.workspace.target.platform.version=7.2.0\n\\end{verbatim}\n\nIf you're using Liferay DXP, you can set the property like this:\n\n\\begin{verbatim}\nliferay.workspace.target.platform.version=7.2.10\n\\end{verbatim}\n\nThe versions following a GA1 release of DXP follow fix pack versions\n(e.g., \\texttt{7.2.10.fp1}, \\texttt{7.2.10.fp2}, etc.).\n\nSetting the target platform property provides a static \\emph{distro} JAR\nfor the specified version of Liferay DXP, which contains all the\nmetadata (i.e., capabilities, packages, versions, etc.) running in that\nversion. The distro JAR is a complete snapshot of everything provided in\nthe OSGi runtime; this serves as the target platform's list of\ncapabilities that your modules are validated against.\n\nYou can now validate your module projects before deploying them! If the\nresolver throws errors, see the article on\n\\href{/docs/7-2/reference/-/knowledge_base/r/how-to-resolve-common-output-errors-reported-by-the-resolve-task}{how\nto resolve common output errors reported by the \\texttt{resolve} task}.\nSometimes, you must modify the \\texttt{resolve} task's default behavior\nto successfully validate your app. See the next section for more\ninformation.\n\n\\section{Modifying the Target Platform's\nCapabilities}\\label{modifying-the-target-platforms-capabilities}\n\nIn a perfect world, everything the \\texttt{resolve} task gathers and\nchecks against would work during your development process.\nUnfortunately, there are exceptions that may force you to modify the\ndefault functionality of the \\texttt{resolve} task.\n\nThere are two scenarios you may run into during development that require\na modification for your project to pass the resolver check.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  You're depending on a third party library that is not available in the\n  targeted Liferay DXP instance or the current workspace.\n\\item\n  You're depending on a customized distribution of Liferay DXP.\n\\end{itemize}\n\nYou'll explore these use cases next.\n\n\\section{Depending on Third Party Libraries Not Included in Liferay\nDXP}\\label{depending-on-third-party-libraries-not-included-in-liferay-dxp}\n\nThe \\texttt{resolve} task, by default, gathers all of Liferay DXP's\ncapabilities and the capabilities of your workspace's modules. What if,\nhowever, your module depends on a third party project that is not\nincluded in either space (e.g.,\n\\href{https://opensource.google.com/projects/guava}{Google Guava})?. The\n\\texttt{resolve} task fails by default if your project depends on this\nproject type. You probably plan to have this project deployed and\navailable at runtime, so it's not a concern, but the resolver doesn't\nknow that; you must customize the resolver to bypass this.\n\nThere are three ways you can do this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{Embed\n  the third party library in your module}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/adding-a-third-party-librarys-capabilities-to-the-resolvers-capabilities}{Add\n  the third party library's capabilities to the current static set of\n  resolver capabilities}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/skipping-the-resolving-process-for-a-module}{Skip\n  the resolving process for your module}\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You should only embed a third party library in your\nmodule if it's the only module that depends on it. You should not bypass\nthe resolver failure this way if more than one project in the OSGi\ncontainer depends on that library.\n\n\\noindent\\hrulefill\n\nFor help resolving third party dependency errors, see the\n\\href{/docs/7-1/frameworks/-/knowledge_base/frameworks/adding-third-party-libraries-to-a-module}{Resolving\nThird Party Library Package Dependencies} tutorial.\n\n\\section{Depending on a Customized Distribution of Liferay\nDXP}\\label{depending-on-a-customized-distribution-of-liferay-dxp}\n\nThere are times when manually specifying your project's list of\ndependent JARs does not suffice. If your app requires a customized\nLiferay DXP instance to run, you must regenerate the target platform's\ndefault list of capabilities with an updated list. Two examples of a\ncustomized Liferay DXP instance are described below:\n\n\\textbf{Example 1: Leveraging an External Feature}\n\nThere are many external features/frameworks available that are not\nincluded in the downloadable bundle by default. After deploying a\nfeature/framework, it's available for your module projects to leverage.\nWhen validating your app, however, the \\texttt{resolve} task does not\nhave access to external capabilities not included by default. For\nexample, Audience Targeting is an example of this type of external\nframework. If you're creating a Liferay Audience Targeting rule that\ndepends on the Audience Targeting framework, you can't easily provide a\nslew of JARs for your module. In this case, you should install the\nplatform your code depends on and regenerate an updated list of\ncapabilities that your Liferay DXP instance provides.\n\n\\textbf{Example 2: Leveraging a Customized Core Feature}\n\nYou can extend Liferay DXP's core features to provide a customized\nexperience for your intended audience. Once deployed, you can assume\nthese customizations are present and build other things on top of them.\nThe new capabilities resulting from your customizations are not\navailable, however, in the target platform's default list of\ncapabilities. Therefore, when your application relies on non-default\ncapabilities, it fails during the \\texttt{resolve} task. To get around\nthis, you must regenerate a new list of capabilities that your\ncustomized Liferay DXP instance provides.\n\nTo regenerate the target platform's capabilities (distro JAR) based on\nthe current workspace's Liferay DXP instance, follow the\n\\href{/docs/7-2/reference/-/knowledge_base/r/depending-on-a-customized-distribution-of-product}{Depending\non a Customized Distribution of Liferay DXP} article.\n\n\\section{Including the Resolver in Your Gradle\nBuild}\\label{including-the-resolver-in-your-gradle-build}\n\nBy default, Liferay Workspace provides the \\texttt{resolve} task as an\nindependent executable. It's provided by the\n\\href{/docs/7-2/reference/-/knowledge_base/r/target-platform-gradle-plugin}{Target\nPlatform} Gradle plugin and is not integrated in any other Gradle\nprocesses. This gives you control over your Gradle build without\nimposing strategies you may not want included in your default build\nprocess.\n\nWith that said, the \\texttt{resolve} task can be useful to include in\nyour build process if you want to check for errors in your module\nprojects before deployment. Instead of resolving your projects\nseparately from your standard build, you can build and resolve them all\nin one shot.\n\nIn Liferay Workspace, the recommended path for doing this is adding it\nto the default \\texttt{check} Gradle task. The \\texttt{check} task is\nprovided by default in a workspace by the\n\\href{https://docs.gradle.org/current/userguide/java_plugin.html\\#_lifecycle_tasks}{Java}\nplugin. Adding the \\texttt{resolve} task to the \\texttt{check} lifecycle\ntask also promotes the \\texttt{resolve} task to run for CI and other\ntest tools that typically run the \\texttt{check} task for verification.\nOf course, Gradle's \\texttt{build} task also depends on the\n\\texttt{check} task, so you can run \\texttt{gradlew\\ build} and run the\nresolver too.\n\nYou can learn how to include the resolver in your Gradle build by\nvisiting\n\\href{/docs/7-2/reference/-/knowledge_base/r/including-the-resolver-in-your-gradle-build}{this\narticle}.\n\nContinue on for various step-by-step instructions for\nconfiguring/manipulating the resolver task.\n\n\\chapter{Adding a Third Party Library's Capabilities to the Resolver's\nCapabilities}\\label{adding-a-third-party-librarys-capabilities-to-the-resolvers-capabilities}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nYou can add your third party dependencies to the target platform's\ndefault list of capabilities by listing them as provided modules.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your workspace's root \\texttt{build.gradle} file.\n\\item\n  Add a code snippet similar to this:\n\n\\begin{verbatim}\ndependencies {\n    providedModules group: \"GROUP_ID\", name: \"NAME\", version: \"VERSION\"\n}\n\\end{verbatim}\n\n  For example, if you wanted to add\n  \\href{https://opensource.google.com/projects/guava}{Google Guava} as a\n  provided module, it would look like this:\n\n\\begin{verbatim}\ndependencies {\n    providedModules group: \"com.google.guava\", name: \"guava\", version: \"23.0\"\n}\n\\end{verbatim}\n\\end{enumerate}\n\nThis both provides the third party dependency to the resolver, and it\ndownloads and includes it in your Liferay DXP bundle's\n\\texttt{osgi/modules} folder when you initialize it (e.g.,\n\\texttt{blade\\ server\\ init}).\n\n\\chapter{Skipping the Resolving Process for a\nModule}\\label{skipping-the-resolving-process-for-a-module}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nIt may be easiest to skip validating a particular module during the\nresolve process.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your workspace's root \\texttt{build.gradle} file.\n\\item\n  Insert the following Gradle code at the bottom of the file:\n\n\\begin{verbatim}\ntargetPlatform {\n    resolveOnlyIf { project ->\n        project.name != 'PROJECT_NAME'\n    }\n}\n\\end{verbatim}\n\n  Be sure to replace the \\texttt{PROJECT\\_NAME} filler with your\n  module's name (e.g., \\texttt{test-api}).\n\\item\n  (Optional) If you prefer to disable the Target Platform plugin\n  altogether, you can add a slightly different directive to your\n  \\texttt{build.gradle} file:\n\n\\begin{verbatim}\ntargetPlatform {\n    onlyIf { project ->\n        project.name != 'PROJECT_NAME'\n    }\n}\n\\end{verbatim}\n\n  This both skips the \\texttt{resolve} task execution and disables BOM\n  dependency management.\n\\end{enumerate}\n\nNow the \\texttt{resolve} task skips your module project.\n\n\\chapter{Depending on a Customized Distribution of Liferay\nDXP}\\label{depending-on-a-customized-distribution-of-liferay-dxp-1}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nTo regenerate the target platform's capabilities (distro JAR) based on\nthe current workspace's Liferay DXP instance, follow the steps below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Start the Liferay DXP instance stored in your workspace. Make sure the\n  platform you want to depend on is installed.\n\\item\n  Download the\n  \\href{https://search.maven.org/\\#search\\%7Cga\\%7C1\\%7Cbiz.aqute.remote.agent}{BND\n  Remote Agent JAR file} and copy it into the \\texttt{osgi/modules}\n  folder.\n\\item\n  From the root folder of your workspace, run the following command:\n\n\\begin{verbatim}\nbnd remote distro -o custom_distro.jar release.portal.distro 7.2.0\n\\end{verbatim}\n\n  Liferay DXP users must replace the \\texttt{release.portal.distro}\n  artifact name with \\texttt{release.dxp.distro} and use the\n  \\texttt{7.2.10} version syntax.\n\n  This connects to the newly deployed BND agent running in Liferay DXP\n  and generates a new distro JAR named \\texttt{custom\\_distro.jar}. All\n  other capabilities inherit their functionality based on your Liferay\n  DXP instance, so verify the workspace bundle is the version you plan\n  to release in production.\n\\item\n  Navigate to your workspace's root \\texttt{build.gradle} file and add\n  the following dependency:\n\n\\begin{verbatim}\ndependencies {\n    targetPlatformDistro files('custom_distro.jar')\n}\n\\end{verbatim}\n\\end{enumerate}\n\nNow your workspace is pointing to a custom distro JAR file instead of\nthe default one provided. Run the \\texttt{resolve} task to validate your\nmodules against the new set of capabilities.\n\n\\chapter{Including the Resolver in Your Gradle\nBuild}\\label{including-the-resolver-in-your-gradle-build-1}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nTo call the \\texttt{resolve} task during Gradle's \\texttt{check} task\nautomatically, follow the instructions below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your workspace's root \\texttt{build.gradle} file.\n\\item\n  Add the following directive:\n\n\\begin{verbatim}\ncheck.dependsOn resolve\n\\end{verbatim}\n\n  The \\texttt{resolve} task is now called during the \\texttt{check}\n  task.\n\n  You can also configure this for specific projects in a workspace if\n  you don't want all modules to be included in the global\n  \\texttt{check}.\n\\item\n  (Optional) If the \\texttt{resolve} task runs during every Gradle\n  build, you may want to prevent the build from failing if there are\n  errors reported by the resolver. To do this, open your workspace's\n  root \\texttt{build.gradle} file and add the following code:\n\n\\begin{verbatim}\ntargetPlatform {\n    ignoreResolveFailures = true\n}\n\\end{verbatim}\n\n  This reports the failures without failing the build. Note, this can\n  only be configured in the workspace's root \\texttt{build.gradle} file.\n\\end{enumerate}\n\nAwesome! You can now run the \\texttt{resolve} task in your current\nGradle lifecycle.\n\n\\chapter{How to Resolve Common Output Errors Reported by the Resolve\nTask}\\label{how-to-resolve-common-output-errors-reported-by-the-resolve-task}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay Workspace provides the \\texttt{resolve} Gradle task to validate\nmodules. This is very useful for finding issues and reporting them as\noutput before deployment. For general help with OSGi related issues,\nvisit the\n\\href{/docs/7-2/appdev/-/knowledge_base/a/troubleshooting-application-development-issues}{Troubleshooting\nFAQ} section.\n\nFor help interpreting the \\texttt{resolve} task's output, see the list\nbelow for common output errors, what they mean, and how to fix them.\n\n\\section{Missing Import Error}\\label{missing-import-error}\n\nWhen your module refers to an unavailable import, the container throws\nthis error. For example, suppose you have a module \\texttt{test-service}\nthat depends on the \\texttt{com.google.common.base} package. If the\ncontainer can't find that package, it throws this error:\n\n\\begin{verbatim}\nResolution exception in project 'modules:test-service': Unresolved requirements in root project 'modules:test-service':\n    Mandatory:\n        [osgi.wiring.package ] com.google.common.base; version=[23.0.0,24.0.0)\n        [osgi.identity       ] test.service\n\\end{verbatim}\n\nThis kind of error can also occur when separate modules require\ndifferent versions of another module. If you have \\emph{module A}\nrequiring \\emph{module Test version 1} and \\emph{module B} requiring\n\\emph{module Test version 4}, without running the resolver, both modules\nA and B would compile successfully. When they were deployed, however,\none would fail in the OSGi runtime because both dependencies cannot be\nsatisfied. These types of scenarios are difficult to diagnose, but with\nthe \\texttt{resolve} task, can be found with ease.\n\nTo fix missing import errors, you may need to adjust the\n\\href{/docs/7-2/customization/-/knowledge_base/c/exporting-packages}{export}\nand/or\n\\href{/docs/7-2/customization/-/knowledge_base/c/importing-packages}{import}\nconfiguration of your modules. Also, see the\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{Resolving\nThird Party Library Package Dependencies} tutorial for more information\non resolving import errors. Sometimes, this kind of error can be solved\nby editing the \\texttt{resolve} task's list of capabilities. See the\n\\href{/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module}{Resolving\nThird Party Library Package Dependencies} section to learn how to do\nthis.\n\n\\section{Missing Service Reference}\\label{missing-service-reference}\n\nIf your module references a non-existent service, an error is thrown.\nThis is helpful because service reference issues are hard to diagnose\nduring deployment without using the\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nShell}.\n\nFor example, if your module \\texttt{test-portlet} references a service\n(e.g., \\texttt{test.api.TestApi}) it does not have access to, the\nfollowing error is thrown:\n\n\\begin{verbatim}\nResolution exception in project 'modules:test-portlet': Unresolved requirements in project 'modules:test-portlet':\n    Mandatory:\n        [osgi.identity ] test.portlet\n        [osgi.service  ] objectClass=test.api.TestApi\n\\end{verbatim}\n\nTo fix this, you must make the service available to your module. If\nyou're expecting the service to be provided by your target platform,\ncheck to make sure it's being provided. If it's a service provided by a\ncustom module, check that service provider module and ensure it's\ncorrectly providing that service to your module. To check the target\nplatform for available services, follow the steps below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Start your target platform instance.\n\\item\n  Open the Gogo shell.\n\\item\n  List all services containing a keyword by running\n  \\texttt{services\\ \\textbar{}\\ grep\\ \\ \\ \\ \\ \"SERVICE\\_NAME\"}. It's\n  easiest to do this rather than listing all services since there are\n  usually too many to sift through.\n\\item\n  You can also list services provided by a component. Run\n  \\texttt{lb\\ -s} to list all provided bundles by their bundle symbolic\n  name (BSN). Find the BSN for the desired component and then run\n  \\texttt{scr:info\\ \\textless{}BSN\\textgreater{}}.\n\\end{enumerate}\n\nIf you're unable to track down your missing service, it may be provided\nby a customized Liferay DXP core feature or an external Liferay DXP\nfeature. If this is the case, it isn't included in the target platform's\ndefault capabilities. You can make the custom service capability\navailable to reference by\n\\href{/docs/7-2/reference/-/knowledge_base/r/depending-on-a-customized-distribution-of-product}{generating\na new custom distro JAR}.\n\n\\section{Missing Fragment Host}\\label{missing-fragment-host}\n\nReferring to a non-existent fragment host throws an error. For example,\nif your \\texttt{test.login} fragment is configured to modify a fragment\nhost named \\texttt{com.liferay.login.web} that cannot be referenced, the\nfollowing error is thrown:\n\n\\begin{verbatim}\nResolution exception in project 'modules:test.login': Unresolved requirements in project 'modules:test-login':\n    Mandatory:\n        [osgi.identity    ] test.login\n        [osgi.wiring.host ] com.liferay.login.web; version=1.0.10\n\\end{verbatim}\n\nConfiguring a fragment host in your module is typically done with the\n\\texttt{Fragment-Host} header in the \\texttt{bnd.bnd} file:\n\n\\begin{verbatim}\nFragment-Host: com.liferay.login.web;bundle-version=\"[1.0.0,1.0.1)\"\n\\end{verbatim}\n\nTo fix this, inspect your target platform to ensure it includes the JAR\nyou're attempting to add a fragment for. Your fragment host header may\nbe referencing an incorrect bundle symbolic name (BSN) or version. The\neasiest way to check this is by using the\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nShell}. Follow the steps below to find the bundle symbolic name:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Start your target platform instance.\n\\item\n  Open the Gogo shell.\n\\item\n  List all installed bundles by BSN with the command \\texttt{lb\\ -s}.\n  You can search through the output to find the BSN. If you already know\n  the BSN and want to check the version, run\n  \\texttt{lb\\ -s\\ \\textbar{}\\ grep\\ \"\\textless{}BSN\\textgreater{}\"}.\n\\end{enumerate}\n\nOnce you know the correct BSN/version to reference, update your\n\\texttt{Fragment-Host} header to resolve the error.\n\nFor more information on fragments, see the\n\\href{/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-osgi-fragments}{JSP\nOverrides Using OSGi Fragments} tutorial.\n\n\\chapter{Validating Modules Outside of\nWorkspace}\\label{validating-modules-outside-of-workspace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nIf you prefer to not use Liferay Workspace, but still want to validate\nmodules against a target platform, you must apply the\n\\href{/docs/7-2/reference/-/knowledge_base/r/target-platform-gradle-plugin}{Target\nPlatform Gradle plugin} to the root \\texttt{build.gradle} file of your\nmulti-module Gradle build. Follow the\n\\href{/docs/7-2/reference/-/knowledge_base/r/targeting-a-platform-outside-of-workspace}{Targeting\na Platform Outside of Workspace} section to do this.\n\nOnce you have the Target Platform plugin and its BOM dependencies\nconfigured, you must configure the \\texttt{targetPlatformDistro}\ndependency. Follow the instructions below to do this.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your project's root \\texttt{build.gradle} file.\n\\item\n  Add the \\texttt{targetPlatformDistro} dependency to the list of\n  dependencies. It should look like this:\n\n\\begin{verbatim}\ndependencies {\n    targetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom\", version: \"7.2.0\"\n    targetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom.compile.only\", version: \"7.2.0\"\n    targetPlatformDistro group: \"com.liferay.portal\", name \"release.portal.distro\", version: \"7.2.0\"\n}\n\\end{verbatim}\n\n  Liferay DXP users must replace the artifact names and versions:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{release.portal.bom} → \\texttt{release.dxp.bom}\n  \\item\n    \\texttt{release.portal.bom.compile.only} →\n    \\texttt{release.dxp.bom.compile.only}\n  \\item\n    \\texttt{release.portal.distro} → \\texttt{release.dxp.distro}\n  \\item\n    \\texttt{7.2.0} → \\texttt{7.2.10}\n  \\end{itemize}\n\\end{enumerate}\n\nNow you can validate your non-workspace modules against a target\nplatform!\n\n\\chapter{Leveraging Docker}\\label{leveraging-docker}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nDocker has become increasingly popular in today's development lifecycle,\nby providing an automated way to package software and its dependencies\ninto a standardized unit that can be shared cross-platform. Read\nDocker's extensive \\href{https://docs.docker.com/}{documentation} to\nlearn more.\n\nLiferay provides Docker images for\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://hub.docker.com/r/liferay/portal}{Liferay Portal}\n\\item\n  \\href{https://hub.docker.com/r/liferay/dxp}{Liferay DXP}\n\\item\n  \\href{https://hub.docker.com/r/liferay/commerce}{Liferay Commerce}\n\\item\n  \\href{https://hub.docker.com/r/liferay/portal-snapshot}{Liferay Portal\n  Snapshots}\n\\end{itemize}\n\nYou can pull Liferay's Docker images from those resources and manage\nthem yourself. Liferay Workspace, however, provides an easy way to\nintegrate Docker development into your existing development workflow\nwith preconfigured Gradle tasks.\n\nThe following Docker commands (Gradle-based) are available in Liferay\nWorkspace:\n\nCommand \\textbar{} Description \\texttt{buildDockerImage} \\textbar{}\nBuilds the Docker image with all modules/configurations deployed.\n\\texttt{createDockerContainer} \\textbar{} Creates a Docker container\nfrom the Liferay DXP image and mounts the workspace's\n\\texttt{/build/docker} folder to the container's \\texttt{/etc/liferay}\nfolder. \\texttt{createDockerfile} \\textbar{} Creates a\n\\texttt{Dockerfile} to build the Docker image. \\texttt{dockerDeploy}\n\\textbar{} Deploys the project to the container's \\texttt{deploy} folder\nby copying the project archive file to workspace's\n\\texttt{build/docker/deploy} folder. This command can also be executed\nfrom workspace's root folder to deploy all projects and copy all Docker\nconfigurations (i.e., from the \\texttt{configs/common} and\n\\texttt{configs/docker} folders) to the container.\n\\texttt{logsDockerContainer} \\textbar{} Prints the portal runtime's\nlogs. You can exit log tracking mode while maintaining a running\ncontainer (e.g., {[}Ctrl\\textbar Command{]} + C).\n\\texttt{pullDockerImage} \\textbar{} Pulls the Docker image.\n\\texttt{removeDockerContainer} \\textbar{} Removes the container from\nDocker's system. \\texttt{startDockerContainer} \\textbar{} Starts the\nDocker container. \\texttt{stopDockerContainer} \\textbar{} Stops the\nDocker container.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Leveraging Docker in Liferay Workspace is only available\nfor Gradle projects at this time.\n\n\\noindent\\hrulefill\n\nIn this section, you'll learn how to\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-product-docker-container}{Create\n  a Docker container based on a provided Liferay DXP image}.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/configuring-a-docker-container}{Configure\n  the container}.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/building-a-custom-docker-image}{Build\n  a custom image}.\n\\end{itemize}\n\nContinue on to learn more.\n\n\\chapter{Creating a Liferay DXP Docker\nContainer}\\label{creating-a-liferay-dxp-docker-container}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nTo create a Liferay DXP Docker container in Liferay Workspace, complete\nthe steps below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Choose the Docker image you need. This is configured in your\n  workspace's \\texttt{gradle.properties} file by customizing this\n  property:\n\n\\begin{verbatim}\nliferay.workspace.docker.image.liferay\n\\end{verbatim}\n\n  To find the possible property values you can set, see the official\n  Liferay DXP Docker Hub's Tags section (e.g.,\n  \\href{https://hub.docker.com/r/liferay/portal/tags}{Liferay Portal\n  Docker Tags}). For example, if you want to base your container on the\n  Liferay Portal 7.2 GA1 image, you would set this property:\n\n\\begin{verbatim}\nliferay.workspace.docker.image.liferay=liferay/portal:7.2.0-ga1\n\\end{verbatim}\n\\item\n  Run the following command from your workspace's root folder:\n\n\\begin{verbatim}\n./gradlew createDockerContainer\n\\end{verbatim}\n\\end{enumerate}\n\nThis command creates a new container named\n\\texttt{{[}projectName{]}-liferayapp}. A new \\texttt{build/docker}\nfolder is generated in your workspace. This folder is mounted into the\ncontainer's file system. This means files in workspace's\n\\texttt{build/docker} folder are also available in the container's\n\\texttt{/etc/liferay} folder.\n\nAny projects in your workspace are automatically compiled and copied to\nthe \\texttt{build/docker/deploy} folder when the container is created;\nthis means that when the container is started, all your projects are\ndeployed to the container. All configurations are also applied to the\ncontainer.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} During your container's startup, you may run into the\nfollowing error:\n\n\\begin{verbatim}\n/etc/liferay/entrypoint.sh: line 3:    11 Killed\n${LIFERAY_HOME}/tomcat/bin/catalina.sh run\n\\end{verbatim}\n\nThis usually means you have not allocated enough memory to your Docker\nengine to successfully run your container. See Docker's\n\\href{https://docs.docker.com}{documentation} to learn how to increase\nresources available to Docker.\n\n\\noindent\\hrulefill\n\nOnce your container is created, you can\n\\href{/docs/7-2/reference/-/knowledge_base/r/configuring-a-docker-container}{configure\nit}.\n\n\\chapter{Configuring a Docker\nContainer}\\label{configuring-a-docker-container}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nBefore starting your container, you may want to add additional portal\nconfigurations. This could include things like\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Property overrides (e.g., \\texttt{portal-ext.properties})\n\\item\n  Marketplace app overrides\n\\item\n  App server configurations\n\\item\n  License files\n\\end{itemize}\n\nYou can do this by applying files (and their accompanying folder\nstructures, if necessary) to your workspace's \\texttt{configs/docker}\nfolder. This folder is treated as your Liferay Home for Docker\ndevelopment; you add additional files that overlay your workspace's\n\\texttt{configs/common} folder and your Liferay DXP container's default\nconfiguration.\n\nAs an example, you'll enable the\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nshell} for your container.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a \\texttt{portal-ext.properties} file to your workspace's\n  \\texttt{configs/docker} folder.\n\\item\n  Add the following property to the \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\nmodule.framework.properties.osgi.console=0.0.0.0:11311\n\\end{verbatim}\n\n  This lets you access your container using Gogo shell via telnet\n  session.\n\\item\n  Start the container.\n\n  Once the container is started, the configurations stored in\n  \\texttt{configs/common} and \\texttt{configs/docker} are transferred to\n  the \\texttt{build/docker/files} folder, which applies all\n  configurations to the container's file system. For more information on\n  workspace's \\texttt{configs} folder, see\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace\\#testing-projects}{this\n  section}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** You can call the `deployDocker` Gradle task from your\n workspace's root folder to initiate the Docker configuration transfer to\n the `build/docker/files` folder manually. It's executed automatically when\n creating or starting the container.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nYou can now apply configurations to your Liferay DXP Docker container.\n\n\\chapter{Building a Custom Docker\nImage}\\label{building-a-custom-docker-image}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nYou can preserve your container's configuration by building it as an\nimage.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Build your custom Liferay DXP image by running\n\n\\begin{verbatim}\n./gradlew buildDockerImage\n\\end{verbatim}\n\n  A \\texttt{Dockerfile} is generated for your container when building\n  your image. The \\texttt{Dockerfile} is generated in your workspace's\n  \\texttt{build/docker} folder. For more information on how to configure\n  the \\texttt{Dockerfile}, see Docker's\n  \\href{https://docs.docker.com/engine/reference/builder/}{Dockerfile\n  reference documentation}.\n\n  You can generate a \\texttt{Dockerfile} manually at any time by running\n\n\\begin{verbatim}\n./gradlew createDockerfile\n\\end{verbatim}\n\\item\n  Run \\texttt{docker\\ image\\ ls} to verify the image's availability.\n\\end{enumerate}\n\nYou can now build Liferay DXP Docker images in Liferay Workspace!\n\n\\chapter{Updating Liferay Workspace}\\label{updating-liferay-workspace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay Workspace is continuously being updated with new features. If\nyou created your workspace a while ago, you may be missing out on some\nof the latest features that could improve your Liferay DXP development\nexperience. Updating your Liferay Workspace is easy; you'll learn how to\ndo it for Gradle and Maven-based workspaces next.\n\n\\section{Gradle}\\label{gradle-2}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Find the latest Liferay Workspace version. To do this, view the\n  Workspace Gradle plugin's\n  \\href{https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.gradle.plugins.workspace/}{released\n  versions} on Liferay's repository. Copy the version to which you want\n  to upgrade.\n\\item\n  Open your Liferay Workspace's \\texttt{settings.gradle} file. This file\n  resides in your Workspace's root folder.\n\\item\n  In the \\texttt{dependencies} block, you'll find code similar to below:\n\n\\begin{verbatim}\ndependencies {\n    classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.workspace\", version: \"[WORKSPACE_VERSION]\"\n}\n\\end{verbatim}\n\n  Update the \\texttt{com.liferay.gradle.plugins.workspace} dependency's\n  \\texttt{version} to the version number you copied in step 1.\n\\item\n  Execute any Gradle command to initiate the update process for your\n  Workspace (e.g., \\texttt{blade\\ gw\\ tasks}).\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Gradle wrapper provided in a Gradle-based Liferay\nWorkspace must be updated if you're migrating from a workspace before\nversion \\texttt{1.10.14} to the latest available version. To update your\nGradle wrapper, run\n\n\\begin{verbatim}\n./gradlew wrapper --gradle-version=4.10.2\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nAwesome! You've upgraded your Gradle-based Liferay Workspace!\n\n\\section{Maven}\\label{maven-4}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Find the latest Liferay Workspace version. To do this, view the Bundle\n  Support plugin's\n  \\href{https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.portal.tools.bundle.support/}{released\n  versions} on Liferay's repository. Copy the version to which you want\n  to upgrade.\n\\item\n  Open your Liferay Workspace's root \\texttt{pom.xml} file.\n\\item\n  Within the \\texttt{plugin} tags, you'll find code similar to below:\n\n\\begin{verbatim}\n<plugin>\n    <groupId>com.liferay</groupId>\n    <artifactId>com.liferay.portal.tools.bundle.support</artifactId>\n    <version>3.4.2</version>\n    ...\n</plugin>\n\\end{verbatim}\n\n  Update the \\texttt{com.liferay.portal.tools.bundle.support} artifact's\n  \\texttt{version} to the version number you copied in step 1.\n\\item\n  Execute any Maven command to initiate the update process for your\n  Workspace (e.g., \\texttt{mvn\\ verify}).\n\\end{enumerate}\n\nAwesome! You've upgraded your Maven-based Liferay Workspace!\n\n\\chapter{Updating Default Plugins Provided by Liferay\nWorkspace}\\label{updating-default-plugins-provided-by-liferay-workspace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay Workspace comes with a slew of plugins like these:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/css-builder}{CSS\n  Builder}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/javadoc-formatter}{Javadoc\n  Formatter}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/lang-builder}{Lang\n  Builder}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-service-builder}{Service\n  Builder}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/source-formatter}{Source\n  Formatter}\n\\item\n  \\href{https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-theme-builder}{Theme\n  Builder}\n\\item\n  etc.\n\\end{itemize}\n\nBundled plugins are updated with each release of workspace. Suppose you\nneed a new feature in the\n\\href{https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.source.formatter/}{Source\nFormatter plugin}, but the latest workspace version has not yet been\nupdated to include it. You can upgrade it yourself!\n\nTo upgrade one of workspace's bundled plugins, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Find the bundle symbolic name (BSN) for the plugin you want to update.\n  You can find this value in the\n  \\href{https://github.com/liferay/liferay-portal/blob/master/modules/sdk/gradle-plugins/src/main/resources/com/liferay/gradle/plugins/dependencies/portal-tools.properties}{\\texttt{portal-tools.properties}}\n  file. For example, the Source Formatter's BSN is\n  \\texttt{com.liferay.source.formatter}.\n\\item\n  Open your workspace's \\texttt{build.gradle} file and copy the plugin's\n  BSN followed by \\texttt{.version} and set the desired plugin version\n  you want to use. For example,\n\n\\begin{verbatim}\ncom.liferay.source.formatter.version=1.0.819\n\\end{verbatim}\n\n  If you're most interested in the latest and greatest plugins, you can\n  set the above property to \\texttt{latest.release} to always use the\n  latest available version. This could, however, cause your workspace to\n  become unstable.\n\\end{enumerate}\n\nThat's it! You're no longer tied to particular plugin versions provided\nby your workspace.\n\n\\chapter{Maven}\\label{maven-5}\n\n\\href{https://maven.apache.org/}{Maven} is a viable option for managing\nLiferay projects if you don't want to use Liferay's default Gradle\nmanagement system. Liferay provides several\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven-plugins}{Maven\nplugins} for generating and managing your project. Liferay also provides\neasy to obtain Maven artifacts that are required for Liferay Maven\nmodule development. Here, you'll learn how to\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Install Liferay Maven artifacts\n\\item\n  Create/Manage a Maven Repository\n\\item\n  Apply Maven plugins\n\\end{itemize}\n\nBecause Liferay DXP is tool-agnostic, Maven is fully supported for\nLiferay DXP development. Read on for details about these topics.\n\n\\section{Installing Liferay Maven\nArtifacts}\\label{installing-liferay-maven-artifacts}\n\nTo create Liferay projects using Maven, you'll need its dependencies.\nThis isn't a problem---Liferay provides them as Maven artifacts. You can\nretrieve them from a remote repository.\n\nThere are two remote repositories that contain Liferay artifacts:\nCentral Repository and Liferay Repository. The Central Repository is the\ndefault repository used to download artifacts if you don't have a remote\nrepository configured. Using the Central Repository to install Liferay\nMaven artifacts only requires that you\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{specify\nyour module's dependencies} in its \\texttt{pom.xml} file.\n\nWhen packaging your module, the automatic Maven artifact installation\nprocess only downloads the artifacts necessary for that module from the\nCentral Repository.\n\nThe Central Repository \\emph{usually} offers the latest Liferay Maven\nartifacts, but the Liferay Repository \\emph{guarantees} access to the\nlatest artifacts released by Liferay. Other than a slight delay in\nartifact releases, the two repositories are identical. When the Liferay\nrepository is configured in your \\texttt{settings.xml} file, archetypes\nare generated based on that repository's contents. The Liferay Maven\nrepository offers a good alternative for those who want the most\nup-to-date Maven artifacts produced by Liferay.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you've configured the Liferay Nexus repository to\naccess Liferay Maven artifacts and you've already been syncing from the\nCentral Repository, you might have to clear out parts of your local\nrepository to force Maven to re-download the newer artifacts. Also,\ndon't leave the Liferay repository enabled when publishing artifacts to\nMaven Central. You must comment out the Liferay Repository credentials\nwhen publishing your artifacts.\n\n\\noindent\\hrulefill\n\nNext, you'll learn about managing your Maven artifacts.\n\n\\section{Managing Maven Artifacts in a\nRepository}\\label{managing-maven-artifacts-in-a-repository}\n\nYou can share Liferay artifacts and modules with teammates or manage\nyour repositories using a GUI by using\n\\href{http://www.sonatype.org/nexus/}{Sonatype Nexus}. It's a Maven\nrepository management server for creating and managing release servers,\nsnapshot servers, and proxy servers. There are several other Maven\nrepository management servers you can use (for example,\n\\href{https://www.jfrog.com/artifactory/}{Artifactory}), but this\nsection focuses on Nexus.\n\nYou'll learn how to\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-maven-repository}{Create\n  a repository}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/configuring-local-maven-settings-to-access-repositories}{Configure\n  a repository}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-liferay-maven-artifacts-to-a-repository}{Deploy\n  artifacts to a repository}\n\\end{itemize}\n\nBefore using repository servers, you must specify them in your Maven\nenvironment settings. Your repository settings let Maven find the\nrepository and retrieve and install artifacts. You can configure your\nlocal Maven settings in the \\texttt{{[}USER\\_HOME{]}/.m2/settings.xml}\nfile.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: You must only configure a repository server if you're\nsharing artifacts (e.g., Liferay artifacts and/or your modules) with\nothers. If you're installing Liferay artifacts from the Central/Liferay\nRepository and aren't interested in sharing artifacts, you don't need a\nrepository server specified in your Maven settings. You can find out\nmore about installing artifacts from the Central Repository or Liferay's\nown Nexus repository in the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-remote-liferay-maven-artifacts}{Installing\nRemote Liferay Maven Artifacts} article.\n\n\\noindent\\hrulefill\n\nTo deploy to a remote repository, your Liferay project should be\npackaged using Maven. Maven provides a packaging command that creates an\nartifact (JAR) that can be easily deployed to your remote repository.\n\nOnce you've created a deployable artifact, you can configure your module\nproject to communicate with your remote repository and use Maven's\n\\texttt{deploy} command to send it on its way. Once your module project\nresides on the remote repository, other developers can configure your\nremote repository in their projects and set dependencies in their\nproject POMs to reference it.\n\n\\section{Applying Maven Plugins}\\label{applying-maven-plugins}\n\nThere are several important Maven plugins that provide important\nfunctionality to Liferay Maven projects. The available Liferay Maven\nplugins are available in the\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven-plugins}{Maven\nPlugins} section.\n\nThe following tasks are covered in this section:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/building-an-osgi-module-jar-with-maven}{Building\n  an OSGi module JAR}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/building-a-theme-with-maven}{Building\n  themes}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/compiling-sass-files-in-a-maven-project}{Compiling\n  Sass files}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/using-service-builder-in-a-maven-project}{Using\n  Service Builder}\n\\end{itemize}\n\nRead on to learn more!\n\n\\chapter{Installing Liferay Maven\nArtifacts}\\label{installing-liferay-maven-artifacts-1}\n\nIf you haven't configured your project to retrieve artifacts from a\ncustom Maven repository, your project will leverage the artifacts from\nthe Central Repository. You can view these artifacts from the\n\\href{https://search.maven.org/}{Maven Central Repository} site. Use the\nLatest Version column as a guide to see what's available for the version\nof Liferay DXP you're developing for.\n\nIf you'd like to access Liferay's latest released Maven artifacts,\nconfigure Maven to use\n\\href{https://repository-cdn.liferay.com}{Liferay's Nexus repository}\ninstead. To do this, open your project's parent \\texttt{pom.xml} and add\nthis:\n\n\\begin{verbatim}\n<repositories>\n    <repository>\n        <id>liferay-public-releases</id>\n        <name>Liferay Public Releases</name>\n        <url>https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-releases</url>\n    </repository>\n</repositories>\n\n<pluginRepositories>\n    <pluginRepository>\n        <id>liferay-public-releases</id>\n        <url>https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-releases/</url>\n    </pluginRepository>\n</pluginRepositories>\n\\end{verbatim}\n\nThe above configuration retrieves artifacts from Liferay's release\nrepository.\n\nIf you're most interested in retrieving Liferay's latest snapshot\nartifacts, follow the instructions below to configure Liferay's Nexus\nrepository to access them.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your project's parent \\texttt{pom.xml} and add this:\n\n\\begin{verbatim}\n<repositories>\n    <repository>\n        <id>liferay-public-snapshots</id>\n        <name>Liferay Public Snapshots</name>\n        <url>https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-snapshots</url>\n    </repository>\n</repositories>\n\n<pluginRepositories>\n    <pluginRepository>\n        <id>liferay-public-snapshots</id>\n        <url>https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-snapshots/</url>\n    </pluginRepository>\n</pluginRepositories>\n\\end{verbatim}\n\\item\n  Enable your project to access snapshot artifacts by adding this code\n  to your parent project's \\texttt{pom.xml}:\n\n\\begin{verbatim}\n<snapshots>\n    <enabled>true</enabled>\n</snapshots>\n\\end{verbatim}\n\\end{enumerate}\n\nYou're now equipped to access Liferay's Maven artifacts via the\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Central Repository\n\\item\n  Liferay Repository (releases)\n\\item\n  Liferay repository (snapshots)\n\\end{itemize}\n\nGreat job!\n\n\\chapter{Creating a Maven Repository}\\label{creating-a-maven-repository}\n\nTo create a Maven repository using Nexus, download\n\\href{https://help.sonatype.com/display/NXRM2/Download}{Nexus} and\nfollow the instructions on Nexus'\n\\href{https://help.sonatype.com/display/NXRM2/Installing+and+Running}{Installation\npage} to install and start it.\n\nTo create your own repository using Nexus, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your web browser; navigate to your Nexus repository server (e.g.,\n  \\url{http://localhost:8081/nexus}) and log in. The default user name\n  is \\texttt{admin} with password \\texttt{admin123}.\n\\item\n  Click on \\emph{Repositories} and navigate to \\emph{Add\\ldots{}} →\n  \\emph{Hosted Repository}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/maven-nexus-create-repo.png}\n  \\caption{Adding a repository to hold your Liferay artifacts is easy\n  with Nexus.}\n  \\end{figure}\n\n  To learn more about each type of Nexus repository, read Sonatype's\n  \\href{http://books.sonatype.com/nexus-book/reference/confignx-sect-manage-repo.html}{Managing\n  Repositories} guide.\n\\item\n  Enter repository properties appropriate for the type of artifacts it\n  will hold. If you're installing release version artifacts into the\n  repository, specify \\emph{Release} as the repository policy. Below are\n  example repository property values:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Repository ID:} \\emph{liferay-releases}\n  \\item\n    \\textbf{Repository Name:} \\emph{Liferay Release Repository}\n  \\item\n    \\textbf{Provider:} \\emph{Maven2}\n  \\item\n    \\textbf{Repository Policy:} \\emph{Release}\n  \\end{itemize}\n\n  To create a snapshot repository, choose \\emph{Snapshot} for the\n  Repository Policy and update the ID and name accordingly.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nVoila! You've created a repository for your Liferay releases (i.e.,\n\\texttt{liferay-releases}) and/or Liferay snapshots (i.e.,\n\\texttt{liferay-snapshots}). To learn how to deploy your Liferay Maven\nartifacts to a Nexus repository, see the\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-liferay-maven-artifacts-to-a-repository}{Deploying\nLiferay Maven Artifacts to a Repository} tutorial.\n\nSee the\n\\href{/docs/7-2/reference/-/knowledge_base/r/configuring-local-maven-settings-to-access-repositories}{Configuring\nLocal Maven Settings to Access Repositories} to configure your new\nrepository servers in your Maven settings to install artifacts to them.\n\n\\chapter{Configuring Local Maven Settings to Access\nRepositories}\\label{configuring-local-maven-settings-to-access-repositories}\n\nTo configure your Maven environment to access your repository servers,\ndo the following:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to your \\texttt{{[}USER\\_HOME{]}/.m2/settings.xml} file.\n  Create it if it doesn't yet exist.\n\\item\n  Configure your repository server settings. Here are contents from a\n  \\texttt{settings.xml} file that has \\texttt{liferay-releases} and\n  \\texttt{liferay-snapshots} repository servers configured:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<settings>\n    <servers>\n        <server>\n            <id>liferay-releases</id>\n            <username>admin</username>\n            <password>admin123</password>\n        </server>\n        <server>\n            <id>liferay-snapshots</id>\n            <username>admin</username>\n            <password>admin123</password>\n        </server>\n    </servers>\n</settings>\n\\end{verbatim}\n\n  The user name \\texttt{admin} and password \\texttt{admin123} are the\n  credentials of the default Nexus administrator account. If you changed\n  these credentials for your Nexus server, make sure to update\n  \\texttt{settings.xml} with these changes.\n\\end{enumerate}\n\nNow that your repositories are configured, they're ready to receive all\nthe Liferay Maven artifacts you'll download and the Liferay module\nartifacts you'll create!\n\n\\chapter{Deploying Liferay Maven Artifacts to a\nRepository}\\label{deploying-liferay-maven-artifacts-to-a-repository}\n\nDeploying artifacts to a remote repository is important if you intend to\nshare your Maven projects with others. First, you must have a remote\nrepository that can hold deployed Maven artifacts. If you do not\ncurrently have a remote repository,\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-maven-repository}{create\none}. Also make sure your \\texttt{{[}USER\\_HOME{]}/.m2/settings.xml}\nfile specifies your remote repository's ID, user name, and password\n(configuration instructions\n\\href{/docs/7-2/reference/-/knowledge_base/r/configuring-local-maven-settings-to-access-repositories}{here}).\n\nTo follow this article, you'll need a Liferay module built with Maven.\nFor demonstration purposes, this tutorial uses the \\texttt{portlet.ds}\nsample module project. To follow along with this module, download the\n\\href{https://portal.liferay.dev/documents/113763090/114000186/portlet.ds.zip}{portlet.ds}\nZip.\n\nNow it's time to deploy a Maven artifact to your Nexus repository.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a folder anywhere on your machine to serve as the parent folder\n  for your Liferay modules. Unzip the \\texttt{portlet.ds} module project\n  into that folder.\n\\item\n  Create a \\texttt{pom.xml} file inside this folder. Copy the following\n  logic into the parent POM:\n\n\\begin{verbatim}\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project\n    xmlns=\"http://maven.apache.org/POM/4.0.0\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n>\n\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>liferay.sample</groupId>\n    <artifactId>liferay.sample.maven</artifactId>\n    <version>1.0.0</version>\n    <name>Liferay Maven Module Projects</name>\n    <packaging>pom</packaging>\n\n    <distributionManagement>\n        <repository>\n            <id>liferay-releases</id>\n            <url>http://localhost:8081/nexus/content/repositories/liferay-releases</url>\n        </repository>\n    </distributionManagement>\n\n    <modules>\n        <module>portlet.ds</module>\n    </modules>\n</project>\n\\end{verbatim}\n\n  The tags \\texttt{\\textless{}modelVersion\\textgreater{}} through\n  \\texttt{\\textless{}packaging\\textgreater{}} are POM tags that are used\n  frequently in parent POMs. Visit Maven's\n  \\href{https://maven.apache.org/pom.html}{POM Reference} documentation\n  for more information.\n\n  The \\texttt{\\textless{}distributionManagement\\textgreater{}} tag\n  specifies the deployment repository for all module projects residing\n  in the parent folder. This repository should also be specified in your\n  \\texttt{{[}USER\\_HOME{]}/.m2/settings.xml}. Both the parent POM and\n  \\texttt{settings.xml} file's repository declarations are required to\n  deploy your modules to that remote repository.\n\n  Finally, you must list the modules residing in the parent folder that\n  you want deployed using the \\texttt{\\textless{}modules\\textgreater{}}\n  tag. The \\texttt{portlet.ds} module is specified within that tag.\n\\item\n  Open the \\texttt{portlet.ds} module's \\texttt{pom.xml} file. If you\n  did not download the \\texttt{portlet.ds} module project Zip, you can\n  reference its POM below.\n\n\\begin{verbatim}\n<project\n    xmlns=\"http://maven.apache.org/POM/4.0.0\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>portlet.ds</artifactId>\n    <version>1.0.0</version>\n    <packaging>jar</packaging>\n\n    <parent>\n        <groupId>liferay.sample</groupId>\n        <artifactId>liferay.sample.maven</artifactId>\n        <version>1.0.0</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <dependencies>\n        <dependency>\n            <groupId>javax.portlet</groupId>\n            <artifactId>portlet-api</artifactId>\n            <version>2.0</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.osgi</groupId>\n            <artifactId>org.osgi.service.component.annotations</artifactId>\n            <version>1.3.0</version>\n            <scope>provided</scope>\n        </dependency>\n    </dependencies>\n</project>\n\\end{verbatim}\n\n  The \\texttt{portlet.ds} module's POM specifies its own attributes\n  first, followed by the parent POM's attributes. Declaring the\n  \\texttt{\\textless{}parent\\textgreater{}} tag like above links the\n  \\texttt{portlet.ds} module to its parent POM, which is necessary to\n  deploy to the remote repository. Then the module's dependencies are\n  listed. These dependencies are downloaded from the Central Repository\n  and installed to your local \\texttt{.m2} repository when you package\n  the \\texttt{portlet.ds} module.\n\\item\n  Now that you've configured your parent POM and module POM, package\n  your Maven project. Navigate to your module project (e.g.,\n  \\texttt{project.ds}) using the command line and run the Maven package\n  command:\n\n\\begin{verbatim}\nmvn package\n\\end{verbatim}\n\n  This downloads and installs all your module's dependencies and\n  packages the project into a JAR file. Navigate to your module\n  project's generated build folder (e.g., \\texttt{/target}). You'll\n  notice there is a newly generated JAR file. This is the artifact\n  you'll deploy to your Nexus repository.\n\\item\n  Run Maven's deploy command to deploy your module project's artifact to\n  your configured remote repository.\n\n\\begin{verbatim}\nmvn deploy\n\\end{verbatim}\n\n  Your console shows output from the artifact being deployed into your\n  repository server.\n\\end{enumerate}\n\nTo verify that your artifact is deployed, navigate to the\n\\emph{Repositories} page of your Nexus server and select your\nrepository. A window appears below showing the Liferay artifact now\ndeployed to your repository.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/maven-verify-deployment.png}\n\\caption{Your repository server now provides access to your Liferay\nMaven artifacts.}\n\\end{figure}\n\nAwesome! You can now share your Liferay module projects with anyone by\ndeploying them as artifacts to your remote repository!\n\n\\chapter{Building an OSGi Module JAR with\nMaven}\\label{building-an-osgi-module-jar-with-maven}\n\nIf you have an existing Liferay module built with Maven that you created\nfrom scratch, or you're upgrading your Maven project from a previous\nversion of Liferay DXP, your project probably can't generate an\nexecutable OSGi JAR. Don't fret! You can do this by making a few minor\nconfigurations in your module's POMs.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you used Liferay's Maven archetypes to generate your\nmodule project, the project already has the Maven plugins required to\ngenerate an OSGi JAR.\n\n\\noindent\\hrulefill\n\nContinue on to see how this is done.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your project's \\texttt{pom.xml} file, add the\n  \\href{http://njbartlett.name/2015/03/27/announcing-bnd-maven-plugin.html}{BND\n  Maven Plugin} declaration:\n\n\\begin{verbatim}\n<plugin>\n    <groupId>biz.aQute.bnd</groupId>\n    <artifactId>bnd-maven-plugin</artifactId>\n    <version>3.3.0</version>\n    <executions>\n        <execution>\n            <goals>\n                <goal>bnd-process</goal>\n            </goals>\n        </execution>\n    </executions>\n    <dependencies>\n        <dependency>\n            <groupId>biz.aQute.bnd</groupId>\n            <artifactId>biz.aQute.bndlib</artifactId>\n            <version>3.2.0</version>\n        </dependency>\n        <dependency>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.ant.bnd</artifactId>\n            <version>2.0.41</version>\n        </dependency>\n    </dependencies>\n</plugin>\n\\end{verbatim}\n\n  The BND Maven plugin prepares all your Maven module's resources (e.g.,\n  \\texttt{MANIFEST.MF}) and inserts them into the generated\n  \\texttt{{[}Maven\\ Project{]}/target/classes} folder. This plugin\n  prepares your module to be packaged as an OSGi JAR deployable to\n  Liferay DXP.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Although WABs can be generated using the `bnd-maven-plugin`,\n this is not supported by Liferay. WABs should be created as a standard WAR\n project and deployed to the\n [Liferay WAB Generator](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator),\n which generates a WAB for you.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  In your project's \\texttt{pom.xml} file, add the\n  \\href{http://maven.apache.org/plugins/maven-jar-plugin/}{Maven JAR\n  Plugin} declaration:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n        <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-jar-plugin</artifactId>\n            <version>2.6</version>\n            <configuration>\n                <archive>\n                    <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>\n                </archive>\n            </configuration>\n        </plugin>\n    </plugins>\n</build>\n\\end{verbatim}\n\n  The Maven JAR plugin builds your Maven project as a JAR file,\n  including the resources generated by the BND Maven plugin. The above\n  configuration also sets the default project \\texttt{MANIFEST.MF} file\n  path for your project, which is essential when packaging your module\n  using the BND Maven plugin. By default, the Maven JAR Plugin ignores\n  the \\texttt{target/classes/META-INF/MANIFEST.MF} generated by the BND\n  Maven plugin, so you must explicitly set it as the manifest file so\n  it's included properly in the generated JAR file.\n\\item\n  Add a \\href{http://bnd.bndtools.org/}{\\texttt{bnd.bnd} file} to your\n  Liferay Maven project, residing in the same folder as your project's\n  \\texttt{pom.xml} file.\n\\item\n  Build your Maven OSGi JAR by running\n\n\\begin{verbatim}\nmvn package\n\\end{verbatim}\n\n  Your Maven JAR is generated in your project's \\texttt{/target} folder.\n  You can deploy it manually into Liferay DXP's \\texttt{/deploy} folder,\n  or you can configure your project to deploy automatically to Liferay\n  DXP by following the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project\\#maven}{Deploying\n  a Project} article.\n\\end{enumerate}\n\nFantastic! You've configured your Liferay Maven project to package\nitself into a deployable OSGi module.\n\n\\chapter{Building a Theme with Maven}\\label{building-a-theme-with-maven}\n\nLiferay's Theme Builder is used to build Liferay DXP theme files in your\nproject. You can incorporate the Theme Builder into your Maven project\nto generate WAR-style\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{themes}\ndeployable to Liferay DXP.\n\nThe easiest way to create a Liferay theme with Maven is to create a new\nMaven project using Liferay's provided\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-template}{Theme\narchetype}; Theme Builder is configured in the new project by default.\nIn some cases, however, this may not be convenient. For instance, if you\nhave a legacy theme project and don't want to start over, generating a\nnew project is not ideal.\n\nFor cases like this, you should manually configure your Maven project to\nleverage Theme Builder. You'll learn how to do this next.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Configure Liferay's\n  \\href{/docs/7-2/reference/-/knowledge_base/r/theme-builder-plugin}{Theme\n  Builder} plugin in your project's \\texttt{pom.xml} file:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.theme.builder</artifactId>\n            <version>1.1.7</version>\n            <executions>\n                <execution>\n                    <phase>generate-resources</phase>\n                    <goals>\n                        <goal>build</goal>\n                    </goals>\n                    <configuration>\n                        <diffsDir>${maven.war.src}</diffsDir>\n                        <name>${project.artifactId}</name>\n                        <outputDir>${project.build.directory}/${project.build.finalName}</outputDir>\n                        <parentDir>${project.build.directory}/deps/com.liferay.frontend.theme.styled.jar</parentDir>\n                        <parentName>_styled</parentName>\n                        <templateExtension>ftl</templateExtension>\n                        <unstyledDir>${project.build.directory}/deps/com.liferay.frontend.theme.unstyled.jar</unstyledDir>\n                    </configuration>\n                </execution>\n            </executions>\n        </plugin>\n    </plugins>\n</build>\n\\end{verbatim}\n\n  The above configuration applies the Theme Builder plugin and then\n  defines the Theme Builder's execution and configuration.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    The\n    \\href{https://maven.apache.org/guides/mini/guide-configuring-plugins.html\\#Using_the_executions_Tag}{executions}\n    tag configures the Theme Builder to run during the\n    \\texttt{generate-resources} phase of your Maven project's build\n    lifecycle. The \\texttt{build}\n    \\href{http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html\\#A_Build_Phase_is_Made_Up_of_Plugin_Goals}{goal}\n    is defined for that lifecycle phase.\n  \\item\n    The \\href{https://maven.apache.org/pom.html\\#Plugins}{configuration}\n    defines tag several important properties. For more info on these\n    properties, see the\n    \\href{/docs/7-2/reference/-/knowledge_base/r/theme-builder-plugin}{Theme\n    Builder Plugin} article.\n  \\end{itemize}\n\\item\n  Apply the CSS Builder plugin, which is required to use Theme Builder:\n\n\\begin{verbatim}\n<plugin>\n    <groupId>com.liferay</groupId>\n    <artifactId>com.liferay.css.builder</artifactId>\n    <version>2.1.3</version>\n    <executions>\n        <execution>\n            <id>default-build</id>\n            <phase>compile</phase>\n            <goals>\n                <goal>build</goal>\n            </goals>\n        </execution>\n    </executions>\n    <configuration>\n        <docrootDirName>target/${project.build.finalName}</docrootDirName>\n        <outputDirName>/</outputDirName>\n        <portalCommonPath>target/deps/com.liferay.frontend.css.common.jar</portalCommonPath>\n    </configuration>\n</plugin>\n\\end{verbatim}\n\n  You can learn more about the CSS Builder's Maven configuration by\n  visiting the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/compiling-sass-files-in-a-maven-project}{Compiling\n  Sass Files in a Maven Project} tutorial.\n\\item\n  You can configure your project to exclude Sass files from being\n  packaged in your theme. This is optional, but is a nice convenience to\n  keep any unnecessary source code out of your WAR. Since the Theme\n  Builder creates a WAR-style theme, you should apply the\n  \\href{https://maven.apache.org/plugins/maven-war-plugin/}{maven-war-plugin}\n  so it instructs the WAR file packaging process to exclude Sass files:\n\n\\begin{verbatim}\n<plugin>\n    <artifactId>maven-war-plugin</artifactId>\n    <version>3.0.0</version>\n    <configuration>\n        <packagingExcludes>**/*.scss</packagingExcludes>\n    </configuration>\n</plugin>\n\\end{verbatim}\n\\item\n  Insert the \\texttt{\\textless{}packaging\\textgreater{}} tag in your\n  project's POM so your project is correctly packaged as a WAR file.\n  This tag can be placed with your project's \\texttt{groupId},\n  \\texttt{artifactId}, and \\texttt{version} specifications like this:\n\n\\begin{verbatim}\n<groupId>com.liferay</groupId>\n<artifactId>com.liferay.project.templates.theme</artifactId>\n<version>1.0.0</version>\n<packaging>war</packaging>\n\\end{verbatim}\n\\item\n  Building themes requires certain dependencies. You can\n  \\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{configure\n  these dependenices} in your project's \\texttt{pom.xml} as directories\n  or JAR files. If you choose to use JARs, you must apply the\n  \\href{http://maven.apache.org/plugins/maven-dependency-plugin/}{maven-dependency-plugin}\n  and have it copy JAR dependencies into your project from Maven\n  Central:\n\n\\begin{verbatim}\n<plugin>\n    <artifactId>maven-dependency-plugin</artifactId>\n    <executions>\n        <execution>\n            <phase>generate-sources</phase>\n            <goals>\n                <goal>copy</goal>\n            </goals>\n            <configuration>\n                <artifactItems>\n                    <artifactItem>\n                        <groupId>com.liferay</groupId>\n                        <artifactId>com.liferay.frontend.css.common</artifactId>\n                        <version>${com.liferay.frontend.css.common.version}</version>\n                    </artifactItem>\n                    <artifactItem>\n                        <groupId>com.liferay</groupId>\n                        <artifactId>com.liferay.frontend.theme.styled</artifactId>\n                        <version>${com.liferay.frontend.theme.styled.version}</version>\n                    </artifactItem>\n                    <artifactItem>\n                        <groupId>com.liferay</groupId>\n                        <artifactId>com.liferay.frontend.theme.unstyled</artifactId>\n                        <version>${com.liferay.frontend.theme.unstyled.version}</version>\n                    </artifactItem>\n                </artifactItems>\n                <outputDirectory>${project.build.directory}/deps</outputDirectory>\n                <stripVersion>true</stripVersion>\n            </configuration>\n        </execution>\n    </executions>\n</plugin>\n\\end{verbatim}\n\n  This configuration copies the\n  \\texttt{com.liferay.frontend.css.common},\n  \\texttt{com.liferay.frontend.theme.styled}, and\n  \\texttt{com.liferay.frontend.theme.unstyled} dependencies into your\n  Maven project. Notice that you've set the \\texttt{stripVersion} tag to\n  \\texttt{true} and you're setting the artifact versions within each\n  \\texttt{artifactItem} tag. You'll set these versions and a few other\n  properties for your Maven project next.\n\\item\n  Configure the properties for your project in its \\texttt{pom.xml}\n  file:\n\n\\begin{verbatim}\n<properties>\n    <com.liferay.css.builder.version>2.1.3</com.liferay.css.builder.version>\n    <com.liferay.frontend.css.common.version>2.0.4</com.liferay.frontend.css.common.version>\n    <com.liferay.frontend.theme.styled.version>2.0.28</com.liferay.frontend.theme.styled.version>\n    <com.liferay.frontend.theme.unstyled.version>2.2.5</com.liferay.frontend.theme.unstyled.version>\n    <com.liferay.portal.tools.theme.builder.version>1.1.7</com.liferay.portal.tools.theme.builder.version>\n</properties>\n\\end{verbatim}\n\n  The properties above set the versions for the CSS and Theme Builder\n  plugins and their dependencies.\n\\end{enumerate}\n\nYou've successfully configured your Maven project to build a Liferay\ntheme with Theme Builder!\n\n\\chapter{Compiling Sass Files in a Maven\nProject}\\label{compiling-sass-files-in-a-maven-project}\n\nIf your Liferay Maven project uses Sass files to style its UI, you must\nconfigure the project to convert its Sass files into CSS files so they\nare recognizable for Maven's build lifecycle. It would be a real pain to\nconvert your Sass files into CSS files manually before building your\nMaven project!\n\nLiferay provides the\n\\href{/docs/7-2/reference/-/knowledge_base/r/css-builder-plugin}{CSS\nBuilder} plugin, which converts Sass files into CSS files so the Maven\nbuild can parse your style sheets.\n\nHere's how to apply Liferay's CSS builder to your Maven project.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your project's \\texttt{pom.xml} file and apply Liferay's CSS\n  Builder:\n\n\\begin{verbatim}\n<plugin>\n    <groupId>com.liferay</groupId>\n    <artifactId>com.liferay.css.builder</artifactId>\n    <version>2.1.0</version>\n    <executions>\n        <execution>\n            <id>default-build</id>\n            <phase>compile</phase>\n            <goals>\n                <goal>build</goal>\n            </goals>\n        </execution>\n    </executions>\n    <configuration>\n        <docrootDirName>${com.liferay.portal.tools.theme.builder.outputDir}</docrootDirName>\n        <outputDirName>/</outputDirName>\n        <portalCommonPath>target/deps/com.liferay.frontend.css.common.jar</portalCommonPath>\n    </configuration>\n</plugin>\n\\end{verbatim}\n\n  The above configuration applies the CSS Builder and then defines the\n  CSS Builder's execution and configuration.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    The\n    \\href{https://maven.apache.org/guides/mini/guide-configuring-plugins.html\\#Using_the_executions_Tag}{executions}\n    tag configures the CSS Builder to run during the \\texttt{compile}\n    phase of your Maven project's build lifecycle. The \\texttt{build}\n    \\href{http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html\\#A_Build_Phase_is_Made_Up_of_Plugin_Goals}{goal}\n    is defined for that lifecycle phase.\n  \\item\n    The \\href{https://maven.apache.org/pom.html\\#Plugins}{configuration}\n    tag defines two important properties. For more info on these\n    properties, see the\n    \\href{/docs/7-2/reference/-/knowledge_base/r/css-builder-plugin}{CSS\n    Builder Plugin} article.\n  \\end{itemize}\n\\item\n  If you're using \\href{http://bourbon.io/}{Bourbon} in your Sass files,\n  you'll need to\n  \\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{add\n  an additional plugin dependency} to your project's POM. If you're not\n  using Bourbon, skip this step. Add the following plugin dependency:\n\n\\begin{verbatim}\n<plugin>\n    <artifactId>maven-dependency-plugin</artifactId>\n    <executions>\n        <execution>\n            <phase>generate-sources</phase>\n            <goals>\n                <goal>copy</goal>\n            </goals>\n            <configuration>\n                <artifactItems>\n                    <artifactItem>\n                        <groupId>com.liferay</groupId>\n                        <artifactId>com.liferay.frontend.css.common</artifactId>\n                        <version>2.0.4</version>\n                    </artifactItem>\n                </artifactItems>\n                <outputDirectory>${project.build.directory}/deps</outputDirectory>\n                <stripVersion>true</stripVersion>\n            </configuration>\n        </execution>\n    </executions>\n</plugin>\n\\end{verbatim}\n\n  The\n  \\href{http://maven.apache.org/plugins/maven-dependency-plugin/}{maven-dependency-plugin}\n  copies the \\texttt{com.liferay.frontend.css.common} dependency from\n  Maven Central to your project's build folder so the CSS Builder can\n  leverage it.\n\\item\n  Use this command to compile your Maven project's Sass files:\n\n\\begin{verbatim}\nmvn compile\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Liferay's CSS Builder is supported for Oracle's JDK and\nuses a native compiler for increased speed. If you're using an IBM JDK,\nyou may experience issues when building your Sass files (e.g., when\nbuilding a theme). It's recommended to switch to using the Oracle JDK,\nbut if you prefer using the IBM JDK, you must use the fallback Ruby\ncompiler. To do this, add the following tag to your CSS Builder\nconfiguration in your POM:\n\n\\begin{verbatim}\n<sassCompilerClassName>ruby</sasscompilerClassName>\n\\end{verbatim}\n\nBe aware that the Ruby-based compiler doesn't perform as well as the\nnative compiler, so expect longer compile times.\n\n\\noindent\\hrulefill\n\nAwesome! You can now compile Sass files in your Liferay Maven project.\n\n\\chapter{Using Service Builder in a Maven\nProject}\\label{using-service-builder-in-a-maven-project}\n\nThe easiest way to add\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder} to your Maven project is to create a new Maven project using\nLiferay's provided Service Builder archetype; it's configured in the new\nproject by default. You can learn how to generate a Service Builder\nMaven project by visiting the\n\\href{/docs/7-2/reference/-/knowledge_base/r/using-the-service-builder-template}{Service\nBuilder Template} article.\n\nIn some cases, you should not use this template due to a number of\nreasons:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  You're updating a legacy Maven project to follow OSGi modular\n  architecture.\n\\item\n  You have a pre-existing modular Maven project and don't want to start\n  over.\n\\end{itemize}\n\nFor these cases, you can configure Service Builder in your project\nmanually. Follow the instructions below to configure Service Builder in\nyour Maven project!\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Apply the Service Builder plugin in your Maven project's\n  \\texttt{pom.xml} file:\n\n\\begin{verbatim}\n<build>\n    <plugins>\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.service.builder</artifactId>\n            <version>1.0.276</version>\n            <configuration>\n                <apiDirName>../basic-api/src/main/java</apiDirName>\n                <autoImportDefaultReferences>true</autoImportDefaultReferences>\n                <autoNamespaceTables>true</autoNamespaceTables>\n                <buildNumberIncrement>true</buildNumberIncrement>\n                <hbmFileName>src/main/resources/META-INF/module-hbm.xml</hbmFileName>\n                <implDirName>src/main/java</implDirName>\n                <inputFileName>service.xml</inputFileName>\n                <modelHintsFileName>src/main/resources/META-INF/portlet-model-hints.xml</modelHintsFileName>\n                <mergeModelHintsConfigs>src/main/resources/META-INF/portlet-model-hints.xml</mergeModelHintsConfigs>\n                <osgiModule>true</osgiModule>\n                <propsUtil>com.liferay.blade.samples.servicebuilder.service.util.PropsUtil</propsUtil>\n                <resourcesDirName>src/main/resources</resourcesDirName>\n                <springFileName>src/main/resources/META-INF/spring/module-spring.xml</springFileName>\n                <springNamespaces>beans,osgi</springNamespaces>\n                <sqlDirName>src/main/resources/META-INF/sql</sqlDirName>\n                <sqlFileName>tables.sql</sqlFileName>\n                <testDirName>src/main/test</testDirName>\n            </configuration>\n        </plugin>\n    </plugins>\n</build>\n\\end{verbatim}\n\n  The \\texttt{configuration} tag used above is an example of what a\n  Service Builder project's configuration could look like. All the\n  configuration attributes above should be modified to fit your project.\n\n  The above code configures Service Builder for a sample\n  \\texttt{basic-service} module. When running Service Builder with this\n  configuration, the project's API classes are generated in the\n  \\texttt{basic-api} module's \\texttt{src/main/java} folder. You can\n  also specify your hibernate module mappings, implementation directory\n  name, model hints file, Spring configurations, SQL configurations,\n  etc. You can reference all the configurable Service Builder properties\n  in the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-plugin}{Service\n  Builder Plugin} reference article.\n\\item\n  Apply the WSDD Builder plugin declaration directly after the Service\n  Builder plugin declaration:\n\n\\begin{verbatim}\n<plugin>\n    <groupId>com.liferay</groupId>\n    <artifactId>com.liferay.portal.tools.wsdd.builder</artifactId>\n    <version>1.0.10</version>\n    <configuration>\n        <inputFileName>service.xml</inputFileName>\n        <outputDirName>src/main/java</outputDirName>\n        <serverConfigFileName>src/main/resources/server-config.wsdd</serverConfigFileName>\n    </configuration>\n</plugin>\n\\end{verbatim}\n\n  The WSDD Builder is required to generate your project's remote\n  services.\n\n  See the\n  \\href{/docs/7-2/reference/-/knowledge_base/r/wsdd-builder-plugin}{WSDD\n  Builder Plugin} article for more information on the configuration\n  properties.\n\\end{enumerate}\n\nTerrific! You've successfully configured your Maven project to use\nService Builder by applying the Service Builder and WSDD Builder plugins\nin your project's POM.\n\n\\chapter{Upgrading Your Maven Build\nEnvironment}\\label{upgrading-your-maven-build-environment}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} This upgrade article only applies to projects residing in\na pre Liferay Portal 7.0 Maven environment that are not upgrading to\nLiferay Workspace. If you're interested in upgrading to a Maven-based\nLiferay Workspace (recommended), see the\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver}{Upgrading\nCode to 7.0} tutorials for more information.\n\n\\noindent\\hrulefill\n\nIf you're an avid Maven user and have been using it for Liferay Portal\n6.2 project development or older, you must upgrade your Maven build to\nbe compatible with 7.0 development. There are two main parts of the\nMaven environment upgrade process that you must address:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[upgrading-to-new-product-ver-maven-plugins]{Upgrading to new\n  7.0 Maven plugins}\n\\item\n  \\hyperref[updating-liferay-maven-artifact-dependencies]{Updating\n  Liferay Maven artifact dependencies}\n\\end{itemize}\n\nFor more information on using Maven with 7.0, see the\n\\href{/docs/7-2/reference/-/knowledge_base/r/maven}{Maven} section.\n\nLiferay also offers a Maven development environment tailored\nspecifically for 7.0 development. Learn more about this in the\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace} section.\n\nYou'll start off by upgrading your Maven environment's Liferay Maven\nplugins.\n\n\\section{Upgrading to New 7.0 Maven\nPlugins}\\label{upgrading-to-new-7.0-maven-plugins}\n\nThe biggest change for your project's build plugins is the removal of\nthe \\texttt{liferay-maven-plugin}. Liferay now provides several\nindividual Maven plugins that accomplish specific tasks. For example,\nyou can configure Maven plugins for Liferay's CSS Builder, Service\nBuilder, Theme Builder, etc. With smaller plugins available to\naccomplish specific tasks in your project, you no longer have to rely on\none large plugin that provides many things you may not want.\n\nFor example, suppose your Liferay Portal 6.2 project was using the\n\\texttt{liferay-maven-plugin} for Liferay CSS Builder only. First, you\nshould remove the legacy \\texttt{liferay-maven-plugin} plugin dependency\nfrom your project's \\texttt{pom.xml} file:\n\n\\begin{verbatim}\n<plugin>\n    <groupId>com.liferay.maven.plugins</groupId>\n    <artifactId>liferay-maven-plugin</artifactId>\n    <version>${liferay.version}</version>\n    <configuration>\n        <autoDeployDir>${liferay.auto.deploy.dir}</autoDeployDir>\n        <liferayVersion>${liferay.version}</liferayVersion>\n        <pluginType>portlet</pluginType>\n    </configuration>\n</plugin>\n\\end{verbatim}\n\nThen, add the CSS Builder plugin dependency to your project's\n\\texttt{pom.xml} file:\n\n\\begin{verbatim}\n<plugin>\n    <groupId>com.liferay</groupId>\n    <artifactId>com.liferay.css.builder</artifactId>\n    <version>2.1.3</version>\n    <executions>\n        <execution>\n            <id>default-build</id>\n            <phase>generate-sources</phase>\n            <goals>\n                <goal>build</goal>\n            </goals>\n        </execution>\n    </executions>\n        <configuration>\n            <docrootDirName>src/main/webapp</docrootDirName>\n        </configuration>\n</plugin>\n\\end{verbatim}\n\nSome common Liferay Maven plugins are listed below, with their\ncorresponding artifact IDs and articles explaining how to configure\nthem:\n\n\\textbf{Common Liferay Maven Plugins}\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.2083}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.4583}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nName\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nArtifact ID\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nTutorial\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nBundle Support &\n\\href{https://search.maven.org/search?q=com.liferay.portal.tools.bundle.support}{com.liferay.portal.tools.bundle.support}\n&\n\\href{/docs/7-2/reference/-/knowledge_base/r/bundle-support-plugin}{Bundle\nSupport Plugin} \\\\\nCSS Builder &\n\\href{https://search.maven.org/search?q=com.liferay.css.builder}{com.liferay.css.builder}\n& \\href{/docs/7-2/reference/-/knowledge_base/r/css-builder-plugin}{CSS\nBuilder Plugin} \\\\\nDB Support &\n\\href{https://search.maven.org/search?q=com.liferay.portal.tools.db.support}{com.liferay.portal.tools.db.support}\n& \\href{/docs/7-2/reference/-/knowledge_base/r/db-support-plugin}{DB\nSupport Plugin} \\\\\nDeployment Helper &\n\\href{https://search.maven.org/search?q=com.liferay.deployment.helper}{com.liferay.deployment.helper}\n&\n\\href{/docs/7-2/reference/-/knowledge_base/r/deployment-helper-plugin}{Deployment\nHelper Plugin} \\\\\nJavadoc Formatter &\n\\href{https://search.maven.org/search?q=com.liferay.javadoc.formatter}{com.liferay.javadoc.formatter}\n&\n\\href{/docs/7-2/reference/-/knowledge_base/r/javadoc-formatter-plugin}{Javadoc\nFormatter Plugin} \\\\\nLang Builder &\n\\href{https://search.maven.org/search?q=com.liferay.lang.builder}{com.liferay.lang.builder}\n& \\href{/docs/7-2/reference/-/knowledge_base/r/lang-builder-plugin}{Lang\nBuilder Plugin} \\\\\nREST Builder &\n\\href{https://search.maven.org/search?q=com.liferay.portal.tools.rest.builder}{com.liferay.portal.tools.rest.builder}\n& \\href{/docs/7-2/reference/-/knowledge_base/r/rest-builder-plugin}{REST\nBuilder Plugin} \\\\\nService Builder &\n\\href{https://search.maven.org/search?q=com.liferay.portal.tools.service.builder}{com.liferay.portal.tools.service.builder}\n&\n\\href{/docs/7-2/reference/-/knowledge_base/r/service-builder-plugin}{Service\nBuilder Plugin} \\\\\nSource Formatter &\n\\href{https://search.maven.org/search?q=com.liferay.source.formatter}{com.liferay.source.formatter}\n&\n\\href{/docs/7-2/reference/-/knowledge_base/r/source-formatter-plugin}{Source\nFormatter Plugin} \\\\\nTheme Builder &\n\\href{https://search.maven.org/search?q=com.liferay.portal.tools.theme.builder}{com.liferay.portal.tools.theme.builder}\n&\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-builder-plugin}{Theme\nBuilder Plugin} \\\\\nTLD Formatter &\n\\href{https://search.maven.org/search?q=com.liferay.tld.formatter}{com.liferay.tld.formatter}\n& \\href{/docs/7-2/reference/-/knowledge_base/r/tld-formatter-plugin}{TLD\nFormatter Plugin} \\\\\nWSDD Builder &\n\\href{https://search.maven.org/search?q=com.liferay.portal.tools.wsdd.builder}{com.liferay.portal.tools.wsdd.builder}\n& \\href{/docs/7-2/reference/-/knowledge_base/r/wsdd-builder-plugin}{WSDD\nBuilder Plugin} \\\\\nXML Formatter &\n\\href{https://search.maven.org/search?q=com.liferay.xml.formatter}{com.liferay.xml.formatter}\n& \\href{/docs/7-2/reference/-/knowledge_base/r/xml-formatter-plugin}{XML\nFormatter Plugin} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When upgrading to a Liferay Workspace built with Maven,\nmany of these plugins are applied to the workspace by default.\n\n\\noindent\\hrulefill\n\nIn Liferay Portal 6.2, you were also required to specify all your app\nserver configuration settings. For example, your parent POM probably\ncontained settings similar to these:\n\n\\begin{verbatim}\n<properties>\n    <liferay.app.server.deploy.dir>\n        E:\\liferay-portal-6.2.0-ce-ga1\\tomcat-7.0.42\\webapps\n    </liferay.app.server.deploy.dir>\n\n    <liferay.app.server.lib.global.dir>\n        E:\\liferay-portal-6.2.0-ce-ga1\\tomcat-7.0.42\\lib\\ext\n    </liferay.app.server.lib.global.dir>\n\n    <liferay.app.server.portal.dir>\n        E:\\liferay-portal-6.2.0-ce-ga1\\tomcat-7.0.42\\webapps\\root\n    </liferay.app.server.portal.dir> \n\n    <liferay.auto.deploy.dir>\n        E:\\liferay-portal-6.2.0-ce-ga1\\deploy\n    </liferay.auto.deploy.dir>\n\n    <liferay.version>\n        6.2.0\n    </liferay.version>\n\n    <liferay.maven.plugin.version>\n        6.2.0\n    </liferay.maven.plugin.version\n \n</properties>\n\\end{verbatim}\n\nThis is no longer required in 7.0 because Liferay's Maven tools no\nlonger rely on your Liferay DXP installation's specific versions. You\nshould remove them from your POM file.\n\nAwesome! You've learned about the new Maven plugins available to you for\n7.0 development. Next, you'll learn about updating your Liferay Maven\nartifacts.\n\n\\section{Updating Liferay Maven Artifact\nDependencies}\\label{updating-liferay-maven-artifact-dependencies}\n\nMany Liferay Portal 6.2 artifact dependencies you were using have\nchanged in 7.0. See the table below for popular Liferay Maven artifacts\nthat have changed:\n\n\\begin{longtable}[]{@{}ll@{}}\n\\toprule\\noalign{}\nLiferay Portal 6.2 Artifact ID & 7.0 Artifact ID \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{portal-service} & \\texttt{com.liferay.portal.kernel} \\\\\n\\texttt{util-bridges} & \\texttt{com.liferay.util.bridges} \\\\\n\\texttt{util-java} & \\texttt{com.liferay.util.java} \\\\\n\\texttt{util-slf4j} & \\texttt{com.liferay.util.slf4j} \\\\\n\\texttt{util-taglib} & \\texttt{com.liferay.util.taglib} \\\\\n\\end{longtable}\n\nFor more information on resolving dependencies in 7.0, see the\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Resolving\na Plugin's Dependencies} article.\n\nOf course, you must also update the artifacts you're referencing for\nyour projects. If you're using the Central Repository to install Liferay\nMaven artifacts, you won't need to do anything more than update the\nartifacts in your POMs. If, however, you're working behind a proxy or\ndon't have Internet access, you must update your company-shared or local\nrepository with the latest 7.0 Maven artifacts.\n\nWith these updates, you can easily upgrade your Liferay Maven build so\nyou can begin developing projects for 7.0.\n\n\\chapter{Theme Generator}\\label{theme-generator}\n\nThe Liferay Theme Generator generates themes for Liferay DXP. It is just\none of Liferay JS Theme Toolkit's\n\\href{https://github.com/liferay/liferay-themes-sdk/tree/master/packages}{tools}.\n\nA couple versions of the Liferay Theme Generator are available. The\nversion you must install depends on the version of Liferay DXP you're\ndeveloping on. The required versions are listed in the table below:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.1717}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.4949}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nLiferay Version\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nLiferay Theme Generator Version\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nCommand\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n6.2 & 7.x.x &\n\\texttt{npm\\ install\\ -g\\ generator-liferay-theme@\\^{}7.x.x} \\\\\n7.0 & 7.x.x or 8.x.x & Same as above or below \\\\\n7.1 & 8.x.x &\n\\texttt{npm\\ install\\ -g\\ generator-liferay-theme@\\^{}8.x.x} \\\\\n7.2 & 9.x.x &\n\\texttt{npm\\ install\\ -g\\ generator-liferay-theme@\\^{}9.x.x} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nSee\n\\href{https://learn.liferay.com/w/dxp/building-applications/tooling/reference/node-version-information\\#version-compatibility-matrix}{Version\nCompatibility Matrix} for more information.\n\n\\section{Sub-generators}\\label{sub-generators}\n\nThe Liferay Theme Generator includes the sub-generators listed in the\ntable below:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.1376}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.2661}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 4\\tabcolsep) * \\real{0.5963}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nSub-generator\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nCommand to run\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nLayouts & \\texttt{yo\\ liferay-theme:layout} & Generate layout templates\nwith an interactive VIM. \\\\\nThemelets & \\texttt{yo\\ liferay-theme:themelet} & Create small,\nreusable, pieces of CSS and HTML for your themes. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Layouts Sub-generator}\\label{layouts-sub-generator}\n\nThe Layouts sub-generator provides the controls to create a layout\ntemplate that meets your needs. You can add and remove rows and columns\non-the-fly as you require. See\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator}{Generating\nLayout Templates} for more information.\n\n\\section{Themelets Sub-generator}\\label{themelets-sub-generator}\n\nThemelets are small, extendable, and reusable pieces of code. They only\nrequire the CSS and/or JavaScript files that you want to add to your\ntheme. This modular approach reduces the need for duplicated code across\nthemes and makes it easy for developers to share code snippets with\nother developers. Themelets are applicable for changes both small and\nlarge, from modifying the appearance of an individual piece of UI to\nadding new features. If there is something you have to manually code for\nevery theme you create, it's a good candidate for a themelet. See\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator}{Generating\nThemelets} for more information.\n\nWhile you can create themes using the tools you prefer, the Liferay\nTheme Generator is designed with theme development for Liferay DXP in\nmind. Its toolset and features help streamline theme development.\n\n\\chapter{Installing the Theme Generator and Creating a\nTheme}\\label{installing-the-theme-generator-and-creating-a-theme}\n\nThe steps below show how to install the Liferay Theme Generator and\ngenerate a theme.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-generator-theme-example.png}\n\\caption{The tools are in your hands to create any theme you can\nimagine.}\n\\end{figure}\n\nYour first step in generating a theme is installing\n\\href{http://nodejs.org/}{NodeJS} (along with Node Package Manager(npm))\nif it's not already installed. We recommend installing\n\\href{https://nodejs.org/download/release/v10.15.1/}{v10.15.1}, which is\nthe version Liferay Portal 7.2 supports (See the\n\\href{https://learn.liferay.com/w/dxp/building-applications/tooling/reference/node-version-information\\#version-compatibility-matrix}{compatibility\nmatrix}). Once NodeJS is installed and you've\n\\href{/docs/7-2/reference/-/knowledge_base/r/setting-up-your-npm-environment}{set\nup your npm environment}, you can follow these steps to install the\nLiferay Theme Generator and generate a theme:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Use npm to install the \\href{http://yeoman.io/}{Yeoman} dependency:\n\n\\begin{verbatim}\nnpm install -g yo\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Gulp is included as a local dependency in generated themes, so you\n are not required to install it. It can be accessed by running\n `node_modules\\.bin\\gulp` followed by the Gulp task from a generated theme's\n root folder.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Install the Liferay Theme Generator with the command below:\n\n\\begin{verbatim}\nnpm install -g generator-liferay-theme\n\\end{verbatim}\n\n  If you're on Windows, follow the instructions in step 3 to install\n  Sass, otherwise you can skip to step 4.\n\\item\n  The generator uses node-sass. If you're on Windows, you must also\n  install\n  \\href{https://github.com/nodejs/node-gyp\\#installation}{node-gyp and\n  Python}.\n\\item\n  Run the generator and follow the prompts to create your theme:\n\n\\begin{verbatim}\nyo liferay-theme\n\\end{verbatim}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/theme-generator-theme-prompt.png}\n  \\caption{You can generate a theme by answering just a few\n  configuration questions.}\n  \\end{figure}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Since Liferay DXP Fix Pack 2 and Liferay Portal 7.2 CE GA2, Font\n Awesome is available globally as a system setting, which is enabled by\n default. If you're using Font Awesome icons in your theme, answer yes (y)\n to the Font Awesome question to include Font Awesome imports in your\n theme. This ensures that your icons won't break if a Site Administrator\n disables the global setting.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{4}\n\\tightlist\n\\item\n  Navigate to your theme folder and run \\texttt{gulp\\ deploy} to deploy\n  your new theme to the server.\n\\end{enumerate}\n\nNow you have a powerful theme development tool at your disposal. The sky\nis the limit!\n\n\\chapter{Generating Layout Templates with the Theme\nGenerator}\\label{generating-layout-templates-with-the-theme-generator}\n\nThis article shows how to use the Liferay Theme Generator's Layouts\nsub-generator to create a layout template.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/layout-template-1-2-1-columns.png}\n\\caption{The \\emph{1-2-1 Columns} page layout creates a nice flow for\nyour content.}\n\\end{figure}\n\nYour first step in creating a layout template with the Liferay Theme\nGenerator's Layouts sub-generator is installing the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Liferay\nTheme Generator} if it's not already installed. Once the generator is\ninstalled, you can follow these steps to create a layout template:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Open the Command Line and navigate to the folder where you want to\n  create your layout template.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Run the Layouts sub-generator from the theme's root folder to\n bundle it with the theme. This adds the layout template to the theme's\n `src/layouttpl/custom` folder. This **only works** for generated themes.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Run The Layouts sub-generator with the command below, and use the\n  options listed below to create your layout:\n\n\\begin{verbatim}\nyo liferay-theme:layout\n\\end{verbatim}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/layout-column-widths.png}\n  \\caption{You must specify the width for each column in the row.}\n  \\end{figure}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/layout-prompt.png}\n  \\caption{The Layouts sub-generator automates the layout creation\n  process.}\n  \\end{figure}\n\n  \\begin{itemize}\n  \\item\n    \\textbf{Add a row:} Adds a row below the last row.\n  \\item\n    \\textbf{Insert row:} Displays a vi to insert your row. Use your\n    arrow keys to choose where to insert your row, highlighted in blue,\n    then press Enter to insert the row.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/insert-row.png}\n  \\caption{Rows can be inserted using the layout vi.}\n  \\end{figure}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Remove row:} Displays a vi to remove your row. Use your\n    arrow keys to select the row you want to remove, highlighted in red,\n    then press Enter to remove the row.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/remove-row.png}\n  \\caption{Rows are removed using the layout vi.}\n  \\end{figure}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Finish Layout:} Complete the layout template.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/finish-layout.png}\n  \\caption{Select the \\emph{Finish layout} option to complete your\n  design.}\n  \\end{figure}\n\\item\n  Run \\texttt{gulp\\ deploy} to deploy your layout template to the server\n  you specified, or deploy your theme if the layout is\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/including-layout-templates-with-a-theme}{bundled\n  with it}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Gulp is included as a local dependency of the generator, so you \n are not required to install it. It can be accessed by running \n `node_modules\\.bin\\gulp` followed by the Gulp task from a generated theme's \n root folder.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nAwesome! You just created a layout template with the Theme Generator's\nLayouts sub-generator. Your layout template project should have a file\nstructure similar to the one below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{my-liferay-layout-layouttpl/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{docroot/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{WEB-INF/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{liferay-layout-templates.xml}\n      \\item\n        \\texttt{liferay-plugin-package.properties}\n      \\end{itemize}\n    \\item\n      \\texttt{my\\_liferay\\_layout.png}\n    \\item\n      \\texttt{my\\_liferay\\_layout.tpl}\n    \\end{itemize}\n  \\item\n    \\texttt{node\\_modules/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      (lots of packages)\n    \\end{itemize}\n  \\item\n    \\texttt{gulpfile.js}\n  \\item\n    \\texttt{liferay-plugin.json}\n  \\item\n    \\texttt{package-lock.json}\n  \\item\n    \\texttt{package.json}\n  \\end{itemize}\n\\end{itemize}\n\n\\chapter{Generating Themelets with the Theme\nGenerator}\\label{generating-themelets-with-the-theme-generator}\n\nThis steps below show how to use the Liferay Theme Generator's Themelets\nsub-generator to create a themelet.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/product-menu-animation-themelet.png}\n\\caption{Themelets can be used to modify one aspect of the UI, that you\ncan then reuse in your other themes.}\n\\end{figure}\n\nYour first step in creating a themelet is installing the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme}{Liferay\nTheme Generator} if it's not already installed. Once the generator is\ninstalled, you can follow these steps to create a themelet:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Command Line and navigate to the folder you want to create\n  your themelet in.\n\\item\n  Run \\texttt{yo\\ liferay-theme:themelet} and follow the prompts to\n  generate the themelet.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/themelet-prompt.png}\n  \\caption{The Themelet sub-generator automates the themelet creation\n  process, making it quick and easy.}\n  \\end{figure}\n\n  The generated themelet contains a \\texttt{package.json} file with\n  configuration information and a \\texttt{src/css} folder that contains\n  a \\texttt{\\_custom.scss} file. Just like a theme, add your CSS changes\n  to the \\texttt{src/css} folder, and add your JavaScript changes to the\n  \\texttt{src/js} folder.\n\\item\n  To use your themelet, you must install it globally first. This makes\n  the themelet visible to the generator. To install your themelet\n  globally, navigate into its root folder and run \\texttt{npm\\ link}.\n  Note, you may need to run the command using \\texttt{sudo\\ npm\\ link}.\n  This creates a globally-installed symbolic link for the themelet in\n  your npm packages folder. Now your themelet is available to install in\n  your themes.\n\\end{enumerate}\n\nAwesome! You just created a themelet with the Theme Generator's\nThemelets sub-generator.\n\n\\chapter{Liferay Upgrade Planner}\\label{liferay-upgrade-planner}\n\nThe Liferay Upgrade Planner provides an automated way to adapt your\ninstallation's data and legacy plugins to your desired Liferay DXP\nupgrade version. We recommend leveraging this tool for any of the\nfollowing upgrades:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay Portal 6.2 → Liferay DXP 7.0, 7.1, or 7.2\n\\item\n  Liferay DXP 7.0 → Liferay DXP 7.1 or 7.2\n\\item\n  Liferay DXP 7.1 → Liferay DXP 7.2\n\\end{itemize}\n\nFor step-by-step instructions for following the two upgrade paths, see\nthe following documentation:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver}{Data\n  Upgrade}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver}{Code\n  Upgrade}\n\\end{itemize}\n\nThe Upgrade Planner is provided in\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio}{Liferay\nDev Studio} (versions 3.6+). Here's what the Upgrade Planner does:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Updates your development environment.\n\\item\n  Identifies code affected by the API changes.\n\\item\n  Describes each API change related to the code.\n\\item\n  Suggests how to adapt the code.\n\\item\n  Provides options, in some cases, to adapt code automatically.\n\\item\n  Transfers database and server data to your new environment.\n\\end{itemize}\n\nEven if you prefer tools other than Dev Studio (which is based on\nEclipse), you should upgrade your data and legacy plugins using the\nUpgrade Planner first--you can use your favorite tools afterward.\n\nTo start the Upgrade Planner in Dev Studio, do this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Project} → \\emph{New Liferay Upgrade Plan\\ldots{}}.\n\\item\n  In the New Liferay Upgrade Plan wizard, assign your plan a name and\n  choose an upgrade plan outline. The data and code upgrade processes\n  are separate, so you must step through each process independently.\n\\item\n  Choose your current Liferay version and the new version you're\n  upgrading to.\n\\item\n  If you chose to complete a code upgrade, you must also select the\n  folder where your legacy plugins reside (e.g., Plugins SDK for Liferay\n  6.2 projects).\n\\item\n  Click \\emph{Finish}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/upgrade-plan-wizard.png}\n\\caption{Configure your upgrade plan before beginning the upgrade\nprocess.}\n\\end{figure}\n\nSwitch to the new Liferay Upgrade Planner perspective (prompted\nautomatically). You're now offered several windows in the UI:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Project Explorer:} displays your legacy plugin environment and\n  new development environment. It also displays your\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/fixing-upgrade-problems}{upgrade\n  problems} that are detected during the \\emph{Fix Upgrade Problems}\n  step.\n\\item\n  \\emph{Liferay Upgrade Plan:} outlines the upgrade plan's steps and\n  step summaries.\n\\item\n  \\emph{Liferay Upgrade Plan Info:} shows official documentation that\n  describes the upgrade step.\n\\end{itemize}\n\nTo progress through your upgrade plan, click the steps outlined in the\nLiferay Upgrade Plan window. Each step can have several options:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Click to preview:} previews what an automated step will perform.\n\\item\n  \\emph{Click to perform:} executes an automated process provided with\n  the step. This is only offered for steps where the Upgrade Planner can\n  assist.\n\\item\n  \\emph{Click when complete:} marks the step as complete. This is only\n  offered when the Upgrade Planner cannot provide automated assistance\n  and, instead, only offers documentation to assist in completing the\n  step manually.\n\\item\n  \\emph{Restart:} marks a completed step as unfinished. The step is\n  performed again if automation is involved.\n\\item\n  \\emph{Skip:} skips the step and jumps to the next step in the outline.\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/preview-upgrade-planner-changes.png}\n\\caption{You can preview the Upgrade Planner's automated updates before\nyou perform them.}\n\\end{figure}\n\nGreat! You now have a good understanding of the Liferay Upgrade\nPlanner's UI and how to get started. Visit the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver}{Data\nUpgrade} and\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver}{Code\nUpgrade} sections for more information on those upgrade processes.\n\n\\chapter{Using the Upgrade Planner with Proxy\nRequirements}\\label{using-the-upgrade-planner-with-proxy-requirements}\n\nIf you have proxy server requirements and want to configure your http(s)\nproxy\\\\\nto work with the Liferay Upgrade Planner, follow the instructions below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In Dev Studio's \\texttt{DeveloperStudio.ini}/\\texttt{eclipse.ini}\n  file, add the following parameters:\n\n\\begin{verbatim}\n-Djdk.http.auth.proxying.disabledSchemes=\n-Djdk.http.auth.tunneling.disabledSchemes=\n\\end{verbatim}\n\\item\n  Launch Dev Studio.\n\\item\n  Go to \\emph{Window} → \\emph{Preferences} → \\emph{General} →\n  \\emph{Network Connections}.\n\\item\n  Set the \\emph{Active Provider} drop-down selector to \\emph{Manual}.\n\\item\n  Under \\emph{Proxy entries}, configure both proxy HTTP and HTTPS by\n  clicking the field and selecting the \\emph{Edit} button.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/upgrade-planner-proxy.png}\n  \\caption{You can configure your proxy settings in Dev Studio's Network\n  Connections menu.}\n  \\end{figure}\n\\item\n  For each schema (HTTP and HTTPS), enter your proxy server's host,\n  port, and authentication settings (if necessary). Do not leave\n  whitespace at the end of your proxy host or port settings.\n\\item\n  Once you've configured your proxy entry, click \\emph{Apply and Close}.\n\\end{enumerate}\n\nAwesome! You've successfully configured the Upgrade Planner's proxy\nsettings!\n\n\\chapter{Web Experience Management\nReference}\\label{web-experience-management-reference}\n\nBrowse this section's reference articles for additional information on\nthe Web Experience Management framework.\n\n\\chapter{Fragment Specific Tags}\\label{fragment-specific-tags}\n\nThere are Liferay-specific tags for creating editable, text, image, and\nlink fields, and for embedding widgets.\n\n\\section{Making Text Editable}\\label{making-text-editable}\n\nYou can make text of a fragment editable by enclosing it in an\n\\texttt{\\textless{}lfr-editable\\textgreater{}} tag like this:\n\n\\begin{verbatim}\n<lfr-editable id=\"unique-id\" type=\"text\">\n   This is editable text!\n</lfr-editable>\n\\end{verbatim}\n\nIf you need formatting options like text or color styles, use\n\\texttt{rich-text}:\n\n\\begin{verbatim}\n<lfr-editable id=\"unique-id\" type=\"rich-text\">\n   This is editable text that I can make bold or italic! \n</lfr-editable>\n\\end{verbatim}\n\nThe \\texttt{lfr-editable} tag doesn't render without a unique\n\\texttt{id}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you want to make text inside an HTML element editable,\nyou must use the \\texttt{rich-text} type. The \\texttt{text} type strips\nHTML formatting out of the text before rendering.\n\n\\noindent\\hrulefill\n\n\\section{Making Images Editable}\\label{making-images-editable}\n\nImages use the same \\texttt{\\textless{}lfr-editable\\textgreater{}} tag\nas text, but with the \\texttt{image} type, like this:\n\n\\begin{verbatim}\n<lfr-editable id=\"unique-id\" type=\"image\">\n   <img src=\"...\">\n</lfr-editable>\n\\end{verbatim}\n\nAfter you add the \\texttt{lfr-editable} tag with the type \\texttt{image}\nto a Fragment, when you add that Fragment to a page, you can then click\non the editable image to select an image or configure content mapping\nfor the image.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-image-editor.png}\n\\caption{You have several options for defining an image on a Content\nPage.}\n\\end{figure}\n\nMost images can be handled like this, but to add an editable background\nimage you must add an additional property to set the background image\nID, \\texttt{data-lfr-background-image-id}. The background image ID is\nset in the main \\texttt{div} for the Fragment and is the same as your\neditable image ID.\n\n\\begin{verbatim}\n<div data-lfr-background-image-id=\"unique-id\">\n   <lfr-editable id=\"unique-id\" type=\"image\">\n      <img src=\"...\">\n   </lfr-editable>\n</div>\n\\end{verbatim}\n\nContent mapping connects editable fields in your Fragment with fields\nfrom an Asset type like Web Content or Blogs. For example, you can map\nan image field to display a preview image for a Web Content Article. For\nmore information on mapping fields, see\n\\href{/docs/7-2/user/-/knowledge_base/u/content-page-elements\\#editable-elements}{Editable\nElements}.\n\n\\section{Creating Editable Links}\\label{creating-editable-links}\n\nThere is also a specific syntax for creating editable link elements:\n\n\\begin{verbatim}\n<lfr-editable id=\"unique-id\" type=\"link\">\n    <a href=\"default-target-url-goes-here\">Link text goes here</a>\n</lfr-editable>\n\\end{verbatim}\n\nMarketers can edit the link text, target URL, and basic link\nstyling---primary button, secondary button, link.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-link-editor.png}\n\\caption{You have several options for defining a link's appearance and\nbehavior.}\n\\end{figure}\n\nFor more information on editable links, see\n\\href{/docs/7-2/user/-/knowledge_base/u/content-page-elements\\#editable-links}{Editable\nLinks}.\n\n\\section{Including Widgets Within A\nFragment}\\label{including-widgets-within-a-fragment}\n\nTo include a widget, you must know its registered name. For example, the\nSite Navigation Menu portlet is registered as \\texttt{nav}. Each\nregistered portlet has an \\texttt{lfr-widget-{[}name{]}} tag that's used\nto embed it. For example: the Navigation Menu tag is\n\\texttt{\\textless{}lfr-widget-nav\\ /\\textgreater{}}. You could embed it\nin a block like this:\n\n\\begin{verbatim}\n<div class=\"nav-widget\">\n    <lfr-widget-nav>\n    </lfr-widget-nav>\n</div>\n\\end{verbatim}\n\nThese are the widgets that can be embedded and their accompanying tags:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}ll@{}}\n\\toprule\\noalign{}\nWidget Name & Tag \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nDDL Display &\n\\texttt{\\textless{}lfr-widget-dynamic-data-list\\textgreater{}} \\\\\nForm & \\texttt{\\textless{}lfr-widget-form\\textgreater{}} \\\\\nAsset Publisher &\n\\texttt{\\textless{}lfr-widget-asset-list\\textgreater{}} \\\\\nBreadcrumb & \\texttt{\\textless{}lfr-widget-breadcrumb\\textgreater{}} \\\\\nCategories Navigation &\n\\texttt{\\textless{}lfr-widget-categories-nav\\textgreater{}} \\\\\nFlash & \\texttt{\\textless{}lfr-widget-flash\\textgreater{}} \\\\\nMedia Gallery &\n\\texttt{\\textless{}lfr-widget-media-gallery\\textgreater{}} \\\\\nNavigation Menu & \\texttt{\\textless{}lfr-widget-nav\\textgreater{}} \\\\\nPolls Display & \\texttt{\\textless{}lfr-widget-polls\\textgreater{}} \\\\\nRelated Assets &\n\\texttt{\\textless{}lfr-widget-related-assets\\textgreater{}} \\\\\nSite Map & \\texttt{\\textless{}lfr-widget-site-map\\textgreater{}} \\\\\nTag Cloud & \\texttt{\\textless{}lfr-widget-tag-cloud\\textgreater{}} \\\\\nTags Navigation &\n\\texttt{\\textless{}lfr-widget-tags-nav\\textgreater{}} \\\\\nWeb Content Display &\n\\texttt{\\textless{}lfr-widget-web-content\\textgreater{}} \\\\\nRSS Publisher (Deprecated) &\n\\texttt{\\textless{}lfr-widget-rss\\textgreater{}} \\\\\nIframe & \\texttt{\\textless{}lfr-widget-iframe\\textgreater{}} \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Enabling Embedding for Your\nWidget}\\label{enabling-embedding-for-your-widget}\n\nIf you have a custom widget that you want to embed in a fragment, you\ncan configure that widget to be embeddable. To embed your widget, it\nmust be an OSGi Component. Inside the \\texttt{@Component} annotation for\nthe portlet class you want to embed, add this property:\n\n\\begin{verbatim}\ncom.liferay.fragment.entry.processor.portlet.alias=app-name\n\\end{verbatim}\n\nWhen you deploy your widget, it's available to add. The name you specify\nin the property must be appended to the \\texttt{lfr-widget} tag like\nthis:\n\n\\begin{verbatim}\n<lfr-widget-app-name>\n</lfr-widget-app-name>\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} According to the W3C HTML standards, custom elements\ncannot be self closing. Therefore, even though you cannot add anything\nbetween the opening and closing\n\\texttt{\\textless{}lfr-widget...\\textgreater{}} tags, you cannot use the\nself closing notation for the tag.\n\n\\chapter{Fragment Configuration\nTypes}\\label{fragment-configuration-types}\n\nThere are four configurable Fragment types available to implement:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{checkbox}\n\\item\n  \\texttt{colorPalette}\n\\item\n  \\texttt{select}\n\\item\n  \\texttt{text}\n\\end{itemize}\n\nFor more information on how to make a Fragment configurable, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/making-a-fragment-configurable}{Making\na Fragment Configurable}.\n\nIn this article, you'll explore JSON examples of each Fragment type.\n\n\\section{Checkbox Configuration}\\label{checkbox-configuration}\n\nThe following JSON configuration creates a checkbox you can implement\nfor cases where a boolean value selection is necessary:\n\n\\begin{verbatim}\n{\n    \"fieldSets\": [\n        {\n            \"fields\": [\n                {\n                    \"name\": \"hideBody\",\n                    \"label\": \"Hide Body\",\n                    \"description\": \"hide-body\",\n                    \"type\": \"checkbox\",\n                    \"defaultValue\": false\n                }\n                ...\n            ]\n        }\n    ]\n}\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-config-checkbox.png}\n\\caption{The checkbox configuration is useful when a boolean selection\nis necessary.}\n\\end{figure}\n\n\\section{Color Palette Configuration}\\label{color-palette-configuration}\n\nThe following JSON configuration creates a color selector you can\nimplement for cases where you must select a color:\n\n\\begin{verbatim}\n{\n    \"fieldSets\": [\n        {\n            \"fields\": [\n                {\n                    \"name\": \"textColor\",\n                    \"label\": \"Text color\",\n                    \"type\": \"colorPalette\",\n                    \"dataType\": \"object\",\n                    \"defaultValue\": {\n                        \"cssClass\": \"white\",\n                        \"rgbValue\": \"rgb(255,255,255)\"\n                    }\n                }\n                ...\n            ]\n        }\n    ]\n}\n\\end{verbatim}\n\nThe \\texttt{colorPalette} type stores an object with two values:\n\\texttt{cssClass} and \\texttt{rgbValue}.\n\nFor example, if you implement the snippet above, you can use it in the\nFreeMarker context like this:\n\n\\begin{verbatim}\n<h3 class=\"text-${configuration.textColor.cssClass}\">Example</h3>\n\\end{verbatim}\n\nIf you were to choose the color white, the \\texttt{h3} tag heading would\nhave the class \\texttt{text-white\\textquotesingle{}}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-config-colorpalette.png}\n\\caption{The \\texttt{colorPalette} configuration is useful when a color\nselection is necessary.}\n\\end{figure}\n\n\\section{Select Configuration}\\label{select-configuration}\n\nThe following JSON configuration creates a selector you can implement\nfor cases where you must select a predefined option:\n\n\\begin{verbatim}\n{\n    \"fieldSets\": [\n        {\n            \"fields\": [\n                {\n                    \"name\": \"numberOfFeatures\",\n                    \"label\": \"Number Of Features\",\n                    \"description\": \"number-of-features\",\n                    \"type\": \"select\",\n                    \"dataType\": \"int\",\n                    \"typeOptions\": {\n                        \"validValues\": [\n                            {\"value\": \"1\"},\n                            {\"value\": \"2\"},\n                            {\"value\": \"3\"}\n                        ]\n                    },\n                    \"defaultValue\": \"3\"\n                }\n                ...\n            ]\n        }\n    ]\n}\n\\end{verbatim}\n\nConfiguration values inserted into the FreeMarker context honor the\ndefined \\texttt{datatype} value specified in the JSON file.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-config-select.png}\n\\caption{The \\texttt{select} configuration is useful when an option\nchoice is necessary.}\n\\end{figure}\n\n\\section{Text Configuration}\\label{text-configuration}\n\nThe following JSON configuration creates an input text field you can\nimplement for cases where you must manually input a text option:\n\n\\begin{verbatim}\n{\n    \"fieldSets\": [\n        {\n            \"fields\": [\n                {\n                    \"name\": \"buttonText\",\n                    \"label\": \"Button Text\",\n                    \"description\": \"button-text\",\n                    \"type\": \"text\",\n                    \"typeOptions\": {\n                        \"placeholder\": \"Placeholder\"\n                    },\n                    \"dataType\": \"string\",\n                    \"defaultValue\": \"Go Somewhere\"\n                }\n                ...\n            ]\n        }\n    ]\n}\n\\end{verbatim}\n\nConfiguration values inserted into the FreeMarker context honor the\ndefined \\texttt{datatype} value specified in the JSON file.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-config-text.png}\n\\caption{The \\texttt{text} configuration is useful when an input text\noption is necessary.}\n\\end{figure}\n\n\\chapter{Escaping Fragment Configuration Text\nValues}\\label{escaping-fragment-configuration-text-values}\n\nWhen you define text configuration and other options for a Fragment,\nFragment developers can declare any text value they want. With this\nfreedom comes risk; malicious code could be inserted into the text\nfield, wreaking havoc for other users of the Fragment.\n\nIn this article, you'll learn how to escape Fragment text values so\nFragment authors are protected from XSS attacks.\n\n\\section{Escaping Values in\nHTML/FreeMarker}\\label{escaping-values-in-htmlfreemarker}\n\nYou must take special care when adding a text value in your Fragment's\nHTML. For example, if a user includes malicious code within\n\\texttt{\\textless{}script\\textgreater{}} tags, it runs when the page is\nrendered.\n\nTo solve this problem, a utility is available in the FreeMarker context\nvia the \\texttt{htmlUtil} class.\n\nFor generic cases, an \\texttt{escape} method is available:\n\n\\begin{verbatim}\n<div class=\"fragment_38816\">\n    ${htmlUtil.escape(configuration.text)}\"\n</div>\n\\end{verbatim}\n\nThis escapes your Fragment configuration's \\texttt{text} field,\npreventing malicious code from affecting Fragment authors.\n\nFor more information on escaping methods, see the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/HtmlUtil.html}{HtmlUtil}\nclass.\n\n\\section{Escaping Values in\nJavaScript}\\label{escaping-values-in-javascript}\n\nWhen including JavaScript in your Fragment, you must be aware of\npotential attack vectors, like setting an attribute or appending HTML\nchildren. To prevent these attacks, you should use the\n\\texttt{Liferay.Util.escapeHTML} function. You can call it from your\nFragment's JavaScript like this:\n\n\\begin{verbatim}\nfunction (fragmentElement, configuration) {\n    const escapedValue = Liferay.Util.escapeHTML(configuration.text)\n}\n\\end{verbatim}\n\nThis escapes your Fragment configuration's \\texttt{text} field,\npreventing malicious code in your Fragment's JavaScript code.\n\n\\chapter{Asset Display Page Taglib}\\label{asset-display-page-taglib}\n\nOnce you have created an Asset Display Page associated with your Asset\ntype, you can add the option to select an Asset Display Page in the form\nwhere you create the Asset. The\n\\texttt{\\textless{}liferay-asset:select-asset-display-page\\ /\\textgreater{}}\ntaglib renders a form field for selecting an Asset Display Page for your\nasset.\n\nThere are three options when selecting a display page:\n\n\\begin{itemize}\n\\item\n  The default display page for the asset type if one has been\n  configured.\n\\item\n  Any other selectable display page.\n\\item\n  None\n\\end{itemize}\n\nIf you select no default display page, a display page is not defined.\n\n\\section{Display Page Attributes}\\label{display-page-attributes}\n\nWhen you use the display page selector taglib, you can define the\nfollowing attributes:\n\n\\texttt{classNameId\\ (long)} (required): a class name ID of the asset\ntype to select an asset display page for.\n\n\\texttt{classPK\\ (long)}: a primary key of the asset entry to select an\nasset display page for.\n\n\\texttt{classTypeId\\ (long)}: a class type ID of the asset type to\nselect an asset display page for.\n\n\\texttt{eventName\\ (String)}: event name which fires when a user selects\na display page using the item selector.\n\n\\texttt{groupId\\ (long)} (required): the entity's group ID to select an\nasset display page for.\n\n\\texttt{showPortletLayouts\\ (boolean)}: allow selection of pages that\nhave Asset Publisher configured as a default Asset Publisher for the\npage.\n\nThe attribute \\texttt{showPortletLayout} provides backwards\ncompatibility for display pages created for Journal Articles in older\nversions. When \\texttt{showPortletLayouts} is set to true, you can\nselect any public or private pages with an Asset Publisher widget on it\nconfigured as the \\emph{Default Asset Publisher for the page}.\n\nWhen submitting a form with the taglib, it populates the request with\nthe following parameters:\n\n\\texttt{displayPageType\\ (int)}: 1 = Default, 2 = Specific, 3 = None.\n\n\\texttt{assetDisplayPageId\\ (long)}: ID of selected Asset Display Page.\n\n\\texttt{layoutUuid}: Layout UUID in case of a portlet page with default\nAsset Publisher.\n\n\\chapter{Crafting XML Workflow\nDefinitions}\\label{crafting-xml-workflow-definitions}\n\nYou don't need a fancy visual designer to build workflows. To be clear,\nKaleo Designer may make you a faster workflow designer through its\ngraphical interface. If you plan to build lots of workflow processes, a\nDigital Enterprise subscription gets you access to Kaleo Designer. But\nwith a little copy and paste from existing workflows and a little\nhandcrafted XML, you can build any workflow and attain workflow\nwizard-hood in the process. Follow this set of tutorials to learn what\nelements you can put into your definitions.\n\n\\section{Existing Workflow\nDefinitions}\\label{existing-workflow-definitions}\n\nOnly one workflow definition is installed by default: Single Approver.\nSeveral more, however, are embedded in the source code of your Liferay\nDXP installation. If you're comfortable extracting the XML files from a\nJAR file embedded in an LPKG file, you're welcome to follow the steps\nbelow to obtain the workflow definitions.\n\nTo extract the definitions for yourself,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to\n\n\\begin{verbatim}\n[Liferay Home]/osgi/marketplace\n\\end{verbatim}\n\\item\n  Using an archive manager, open the LPKG\n\n\\begin{verbatim}\nLiferay CE Forms and Workflow.lpkg\n\\end{verbatim}\n\\item\n  Open the JAR file named\n\n\\begin{verbatim}\ncom.liferay.portal.workflow.kaleo.runtime.impl-[version].jar\n\\end{verbatim}\n\\item\n  In the JAR file, navigate to\n\n\\begin{verbatim}\nMETA-INF/definitions/\n\\end{verbatim}\n\\item\n  Extract the four XML workflow definition files. These definitions\n  provide good reference material for many of the workflow features and\n  elements described in these articles. In fact, most of the XML\n  snippets you see here are lifted directly from these definitions.\n\\end{enumerate}\n\n\\section{Schema}\\label{schema}\n\nThe XML structure of a workflow definition is defined in an XSD file:\n\n\\begin{verbatim}\nliferay-worklow-definition-7_2_0.xsd\n\\end{verbatim}\n\nDeclare the schema at the top of the workflow definition file:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<workflow-definition\n    xmlns=\"urn:liferay.com:liferay-workflow_7.2.0\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"urn:liferay.com:liferay-workflow_7.2.0\n        http://www.liferay.com/dtd/liferay-workflow-definition_7_2_0.xsd\">\n\\end{verbatim}\n\nTo read 464 lines of beautifully formatted XML that defines how to write\nmore XML (it's practically poetic), check out the XSD\n\\href{https://www.liferay.com/dtd/liferay-workflow-definition_7_2_0.xsd}{here}.\nOtherwise, move on to entering the definition's metadata.\n\n\\section{Metadata}\\label{metadata}\n\nGive the definition a name, description, and version:\n\n\\begin{verbatim}\n<name>Category Specific Approval</name>\n<description>A single approver can approve a workflow content.</description>\n<version>1</version>\n\\end{verbatim}\n\nAll these tags are optional. If present the first time a definition is\nsaved, the \\texttt{\\textless{}name\\textgreater{}} tag serves as a unique\nidentifier for the definition. If not specified (or added sometime after\nthe first save), a random unique name is generated and used to identify\nthe workflow.\n\nOnce the schema and metadata are in place, it's time to turn up the\nfunky beats and get into the flow (the workflow). Learn about workflow\nnodes in the next article.\n\n\\chapter{Workflow Definition Nodes}\\label{workflow-definition-nodes}\n\nAfter your definition's schema and metadata are in place, begin defining\nthe process. \\emph{Node} elements, with their sub-elements, are\nfundamental building blocks making up workflow definitions.\n\n\\section{State Nodes}\\label{state-nodes}\n\nState nodes don't require user input. The workflow does whatever is\nspecified in the state node's \\texttt{actions} tag (a notification\nand/or a custom script), and then moves to the provided transition.\nWorkflows start and end with a state. The initial state node often only\ncontains a transition:\n\n\\begin{verbatim}\n<state>\n    <name>created</name>\n    <initial>true</initial>\n    <transitions>\n        <transition>\n            <name>Determine Branch</name>\n            <target>determine-branch</target>\n            <default>true</default>\n        </transition>\n    </transitions>\n</state>\n\\end{verbatim}\n\nIf a notification or script is required in your state node, use an\n\\texttt{actions} tag. Here's an \\texttt{action} element containing a\nGroovy script. This is found in many terminal state nodes and marks the\nasset as approved in the workflow.\n\n\\begin{verbatim}\n<actions>\n    <action>\n        <name>Approve</name>\n        <description>Approve</description>\n        <script>\n            <![CDATA[\n            com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil.\n                updateStatus(com.liferay.portal.kernel.workflow.WorkflowConstants.     \n                    getLabelStatus(\"approved\"), workflowContext);]]>\n        </script>\n        <script-language>groovy</script-language>\n        <execution-type>onEntry</execution-type>\n    </action>\n</actions>\n\\end{verbatim}\n\n\\section{Conditions}\\label{conditions}\n\nConditions let you inspect the asset (or its execution context) and do\nsomething, like send it to a particular transition.\n\nHere's the \\texttt{determine-branch} condition from the Category\nSpecific Approval workflow definition:\n\n\\begin{verbatim}\n<condition>\n    <name>determine-branch</name>\n    <script>\n        <![CDATA[\n            import com.liferay.asset.kernel.model.AssetCategory;\n            import com.liferay.asset.kernel.model.AssetEntry;\n            import com.liferay.asset.kernel.model.AssetRenderer;\n            import com.liferay.asset.kernel.model.AssetRendererFactory;\n            import com.liferay.asset.kernel.service.AssetEntryLocalServiceUtil;\n            import com.liferay.portal.kernel.util.GetterUtil;\n            import com.liferay.portal.kernel.workflow.WorkflowConstants;\n            import com.liferay.portal.kernel.workflow.WorkflowHandler;\n            import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;\n\n            import java.util.List;\n\n            String className = (String)workflowContext.get(WorkflowConstants.CONTEXT_ENTRY_CLASS_NAME);\n\n            WorkflowHandler workflowHandler = WorkflowHandlerRegistryUtil.getWorkflowHandler(className);\n\n            AssetRendererFactory assetRendererFactory = workflowHandler.getAssetRendererFactory();\n\n            long classPK = GetterUtil.getLong((String)workflowContext.get(WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n            AssetRenderer assetRenderer = workflowHandler.getAssetRenderer(classPK);\n\n            AssetEntry assetEntry = assetRendererFactory.getAssetEntry(assetRendererFactory.getClassName(), assetRenderer.getClassPK());\n\n            List<AssetCategory> assetCategories = assetEntry.getCategories();\n\n            returnValue = \"Content Review\";\n\n            for (AssetCategory assetCategory : assetCategories) {\n                String categoryName = assetCategory.getName();\n\n                if (categoryName.equals(\"legal\")) {\n                    returnValue = \"Legal Review\";\n\n                    return;\n                }\n            }\n        ]]>\n    </script>\n    <script-language>groovy</script-language>\n    <transitions>\n        <transition>\n            <name>Legal Review</name>\n            <target>legal-review</target>\n            <default>false</default>\n        </transition>\n        <transition>\n            <name>Content Review</name>\n            <target>content-review</target>\n            <default>false</default>\n        </transition>\n    </transitions>\n</condition>\n\\end{verbatim}\n\nThis example checks the asset category to choose the processing path,\nwhether to transition to the \\emph{Legal Review} task or the\n\\emph{Content Review} task.\n\nThe \\texttt{returnValue} variable points from the condition to a\ntransition, and its value must match a valid transition name. This\nscript looks up the asset in question, retrieves its asset category, and\nsets an initial \\texttt{returnValue}. Then it checks to see if the asset\nhas been marked with the \\emph{legal} category. If not it goes through\n\\emph{Content Review} (to the content-review task in the workflow), and\nif it does it goes through \\emph{Legal Review} (to the legal-review task\nin the workflow).\n\n\\section{Forks and Joins}\\label{forks-and-joins}\n\nForks split the workflow process, and joins bring the process back to a\nunified branch. Processing must always be brought back using a Join (or\na Join XOR), and the number of forks and joins in a workflow definition\nmust be equal.\n\n\\begin{verbatim}\n<fork>\n    <name>fork-1</name>\n    <transitions>\n        <transition>\n            <name>transition-1</name>\n            <target>task-1</target>\n            <default>true</default>\n        </transition>\n        <transition>\n            <name>transition-2</name>\n            <target>task-2</target>\n            <default>false</default>\n        </transition>\n    </transitions>\n</fork>\n<join>\n    <name>join-1</name>\n    <transitions>\n        <transition>\n            <name>transition-4</name>\n            <target>EndNode</target>\n            <default>true</default>\n        </transition>\n    </transitions>\n</join>\n\\end{verbatim}\n\nThe workflow doesn't move past the join until the asset transitions to\nit from both of the forks. To fork the workflow process, but then allow\nthe processing to continue when only one fork is completed, use a Join\nXOR.\n\nA Join XOR differs from a join in one important way: it removes the\nconstraint that both forks must be completed before processing can\ncontinue. The asset must complete just one of the forks before\nprocessing continues.\n\n\\begin{verbatim}\n<join-xor>\n    <name>join-xor</name>\n    <transitions>\n        <transition>\n            <name>transition3</name>\n            <target>EndNode</target>\n            <default>true</default>\n        </transition>\n    </transitions>\n</join-xor>\n\\end{verbatim}\n\n\\section{Task Nodes}\\label{task-nodes}\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/workflow-task-nodes}{Task\nnodes} are at the core of the workflow definition. They're the part\nwhere a user interacts with the asset in some way. Tasks can also have\nsub-elements, including notifications, assignments, and task timers.\n\nHere's the \\texttt{content-review} task from the Category Specific\nApproval workflow, with some of the \\texttt{role} assignment tags cut\nout for brevity:\n\n\\begin{verbatim}\n<task>\n    <name>content-review</name>\n    <actions>\n        <notification>\n            <name>Review Notification</name>\n            <template>You have a new submission waiting for your review in the workflow.</template>\n            <template-language>text</template-language>\n            <notification-type>email</notification-type>\n            <notification-type>user-notification</notification-type>\n            <execution-type>onAssignment</execution-type>\n        </notification>\n    </actions>\n    <assignments>\n        <roles>\n            <role>\n                <role-type>organization</role-type>\n                <name>Organization Administrator</name>\n            </role>\n            ...\n        </roles>\n    </assignments>\n    <task-timers>\n        <task-timer>\n            <name></name>\n            <delay>\n                <duration>1</duration>\n                <scale>hour</scale>\n            </delay>\n            <blocking>false</blocking>\n            <timer-actions>\n                <timer-notification>\n                    <name></name>\n                    <template></template>\n                    <template-language>text</template-language>\n                    <notification-type>user-notification</notification-type>\n                </timer-notification>\n            </timer-actions>\n        </task-timer>\n    </task-timers>\n    <transitions>\n        <transition>\n            <name>approve</name>\n            <target>approved</target>\n            <default>true</default>\n        </transition>\n        <transition>\n            <name>reject</name>\n            <target>update</target>\n            <default>false</default>\n        </transition>\n    </transitions>\n</task>\n\\end{verbatim}\n\nLearn more about workflow tasks in the next article.\n\n\\chapter{Workflow Task Nodes}\\label{workflow-task-nodes}\n\nTask nodes are fundamental parts of a workflow definition. When you\ndefine your organization's business processes and design corresponding\nworkflows, you likely first envision the tasks. As the name implies,\ntasks are the part of the workflow where \\emph{work} is done. A user\nenters the picture and must interact with the submitted asset. Users\noften take the role of reviewer, deciding if an asset from the workflow\nis acceptable for publication or needs more work.\n\nUnlike other workflow nodes, task nodes have Assignments, because a user\nis expected to \\emph{do something} (often approve or reject the\nsubmitted asset) when a workflow process enters the task node.\n\nCommonly, task nodes contain task timers, assignments, actions (which\ncan include notifications and scripts), and transitions. Notifications\nand actions aren't limited to task nodes, but task nodes and their\nassignments deserve their own article (this one).\n\nCheck out the Review task in the Single Approver definition, noting that\nseveral \\texttt{\\textless{}role\\textgreater{}} tags are excluded from\nthis snippet for brevity:\n\n\\begin{verbatim}\n<task>\n    <name>review</name>\n    <actions>\n        <notification>\n            <name>Review Notification</name>\n            <template>${userName} sent you a ${entryType} for review in the workflow.</template>\n            <template-language>freemarker</template-language>\n            <notification-type>email</notification-type>\n            <notification-type>user-notification</notification-type>\n            <execution-type>onAssignment</execution-type>\n        </notification>\n        <notification>\n            <name>Review Completion Notification</name>\n            <template><![CDATA[Your submission was reviewed<#if taskComments?has_content> and the reviewer applied the following ${taskComments}</#if>.]]></template>\n            <template-language>freemarker</template-language>\n            <notification-type>email</notification-type>\n            <recipients>\n                <user />\n            </recipients>\n            <execution-type>onExit</execution-type>\n        </notification>\n    </actions>\n    <assignments>\n        <roles>\n            <role>\n                <role-type>organization</role-type>\n                <name>Organization Administrator</name>\n            </role>\n              ...\n        </roles>\n    </assignments>\n    <transitions>\n        <transition>\n            <name>approve</name>\n            <target>approved</target>\n        </transition>\n        <transition>\n            <name>reject</name>\n            <target>update</target>\n            <default>false</default>\n        </transition>\n    </transitions>\n</task>\n\\end{verbatim}\n\nThere are two \\texttt{actions} in the review task, both\n\\texttt{\\textless{}notification\\textgreater{}}s. Each notification may\ncontain a name, template, notification-type, execution-type, and\nrecipients. Besides notifications, You can also use the\n\\texttt{\\textless{}action\\textgreater{}} tag. These have a name and a\n\\href{/docs/7-2/user/-/knowledge_base/u/leveraging-the-script-engine-in-workflow}{script}\nand are more often used in state nodes than tasks.\n\n\\section{Assignments}\\label{assignments}\n\nWorkflow tasks are completed by a user. Assignments make sure the right\nusers can access the tasks. You can choose how you want to configure\nyour assignments.\n\nYou can choose to add assignments to specific roles, to multiple roles\nof a role type (organization, site, or regular role types), to the asset\ncreator, to resource actions, or to specific users. Additionally, you\ncan write a script to define the assignment. For an example, see the\n\\texttt{single-approver-definition-scripted-assignment.xml}.\n\n\\begin{verbatim}\n<assignments>\n    <roles>\n        <role>\n            <role-type>organization</role-type>\n            <name>Organization Administrator</name>\n        </role>\n    </roles>\n</assignments>\n\\end{verbatim}\n\nThe above assignment specifies that an Organization Administrator must\ncomplete the task.\n\n\\begin{verbatim}\n<assignments>\n    <user>\n        <user-id>20156</user-id>\n    </user>\n</assignments>\n\\end{verbatim}\n\nThe above assignment specifies that only the user with the user ID of\n20156 may complete the task. Alternatively, specify the\n\\texttt{\\textless{}screen-name\\textgreater{}} or\n\\texttt{\\textless{}email-address\\textgreater{}} of the user.\n\n\\begin{verbatim}\n<assignments>\n    <scripted-assignment>\n        <script>\n            <![CDATA[\n                    import com.liferay.portal.kernel.model.Group;\n                    import com.liferay.portal.kernel.model.Role;\n                    import com.liferay.portal.kernel.service.GroupLocalServiceUtil;\n                    import com.liferay.portal.kernel.service.RoleLocalServiceUtil;\n                    import com.liferay.portal.kernel.util.GetterUtil;\n                    import com.liferay.portal.kernel.workflow.WorkflowConstants;\n\n                    long companyId = GetterUtil.getLong((String)workflowContext.get(WorkflowConstants.CONTEXT_COMPANY_ID));\n\n                    long groupId = GetterUtil.getLong((String)workflowContext.get(WorkflowConstants.CONTEXT_GROUP_ID));\n\n                    Group group = GroupLocalServiceUtil.getGroup(groupId);\n\n                    roles = new ArrayList<Role>();\n\n                    Role adminRole = RoleLocalServiceUtil.getRole(companyId, \"Administrator\");\n\n                    roles.add(adminRole);\n\n                    if (group.isOrganization()) {\n                        Role role = RoleLocalServiceUtil.getRole(companyId, \"Organization Content Reviewer\");\n\n                        roles.add(role);\n                    }\n                    else {\n                        Role role = RoleLocalServiceUtil.getRole(companyId, \"Site Content Reviewer\");\n\n                        roles.add(role);\n                    }\n\n                    user = null;\n                ]]>\n            </script>\n        <script-language>groovy</script-language>\n    </scripted-assignment>\n</assignments>\n\\end{verbatim}\n\nThe above assignment assigns the task to the \\emph{Administrator} role,\nthen checks whether the \\emph{group} of the asset is an Organization. If\nit is, the \\emph{Organization Content Reviewer} role is assigned to it.\nIf it's not, the task is assigned to the \\emph{Site Content Reviewer}\nrole.\n\nNote the\n\\texttt{roles\\ =\\ new\\ ArrayList\\textless{}Role\\textgreater{}();} line\nabove. In a scripted assignment, the \\texttt{roles} variable is where\nyou specify any roles the task is assigned to. For example, when\n\\texttt{roles.add(adminRole);} is called, the Administrator role is\nadded to the assignment.\n\nAssigning tasks to Roles, Organizations, or Asset Creators is a\nstraightforward concept, but what does it mean to assign a workflow task\nto a Resource Action? Imagine an \\emph{UPDATE} resource action. If your\nworkflow definition specifies the UPDATE action in an assignment, then\nanyone who has permission to update the type of asset being processed in\nthe workflow is assigned to the task. You can configure multiple\nassignments for a task.\n\n\\section{Resource Action Assignments}\\label{resource-action-assignments}\n\n\\emph{Resource actions} are operations performed by users on an\napplication or entity. For example, a user might have permission to\nupdate Message Boards Messages. This is called an UPDATE resource\naction, because the user can update the resource. If you're uncertain\nabout what resource actions are, refer to the developer tutorial on the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions}{permission\nsystem} for a more detailed explanation.\n\nTo find all the resource actions that have been created, you need access\nto the Roles Admin application in the Control Panel (in other words, you\nneed permission for the VIEW action on the roles resource).\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Navigate to Control Panel → Users → Roles.\n\\item\n  Add a new Regular Role. See the\n  \\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{article\n  on managing roles} for more information.\n\\item\n  Once the role is added, navigate to the Define Permissions interface\n  for the role.\n\\item\n  Find the resource whose action should define your workflow assignment.\n\\end{itemize}\n\nHere's what the assignment's XML looks like:\n\n\\begin{verbatim}\n<assignments>\n    <resource-actions>\n        <resource-action>UPDATE</resource-action>\n    </resource-actions>\n</assignments>\n\\end{verbatim}\n\nNow when the workflow proceeds to the task with the resource action\nassignment, users with \\texttt{UPDATE} permission on the resource (for\nexample, Message Boards Messages) are notified of the task and can\nassign it to themselves (if the notification is set to Task Assignees).\nSpecifically, users see the tasks in their \\emph{My Workflow Tasks}\napplication under the tab \\emph{Assigned to My Roles}.\n\nUse all upper case letters for resource action names. Here are some\ncommon resource actions:\n\n\\begin{verbatim}\nUPDATE\nADD\nDELETE\nVIEW\nPERMISSIONS\nSUBSCRIBE\nADD_DISCUSSION\n\\end{verbatim}\n\nDetermine the probable resource action name from the permissions screen\nfor a resource. For example, in Message Boards, one of the permissions\ndisplayed on that screen is \\emph{Add Discussion}. Convert that to all\nuppercase and replace the space with an underscore, and you have the\naction name.\n\n\\section{Task Timers}\\label{task-timers}\n\nTask timers trigger an action after a specified time period passes.\nTimers are useful for ensuring a task does not go unattended for a long\ntime. Available timer actions include sending an additional\nnotification, reassigning the asset, or creating a timer action.\n\n\\begin{verbatim}\n<task-timers>\n    <task-timer>\n        <name></name>\n        <delay>\n            <duration>1</duration>\n            <scale>hour</scale>\n        </delay>\n        <blocking>false</blocking>\n        <recurrence>\n            <duration>10</duration>\n            <scale>minute</scale>\n        </recurrence>\n        <timer-actions>\n            <timer-notification>\n                <name></name>\n                <template></template>\n                <template-language>text</template-language>\n                <notification-type>user-notification</notification-type>\n            </timer-notification>\n        </timer-actions>\n    </task-timer>\n</task-timers>\n\\end{verbatim}\n\nThe above task timer creates a notification. Specify a time period in\nthe \\texttt{\\textless{}delay\\textgreater{}} tag, and specify what action\nto take when the time expires in the\n\\texttt{\\textless{}timer-actions\\textgreater{}} block. The\n\\texttt{\\textless{}blocking\\textgreater{}} element specifies whether the\ntimer actions may recur. If blocking is set to \\texttt{false}, timer\nactions may recur. In a \\texttt{recurrence} element, specify the\nrecurrence interval using a \\texttt{duration} and a \\texttt{scale}, as\ndemonstrated above. The above recurrence element specifies that the\ntimer actions run again every ten minutes after the initial occurrence.\nSetting blocking to true prevents timer actions from recurring.\n\n\\begin{verbatim}\n<timer-actions>\n    <reassignments>\n       <assignments>\n         <roles>\n          <role>\n              <role-type></role-type>\n              <name></name>\n          </role>\n          ...\n         </roles>\n       </assignments>\n    </reassignments>\n</timer-actions>\n\\end{verbatim}\n\nThe above snippet demonstrates how to set up a reassignment action.\n\nLike \\texttt{\\textless{}action\\textgreater{}} elements,\n\\texttt{\\textless{}timer-action\\textgreater{}} elements can contain\nscripts.\n\n\\begin{verbatim}\n<timer-actions>\n    <timer-action>\n        <name>doSomething</name>\n        <description>Do something cool when time runs out.</description>\n        <script>\n           ...\n        </script>\n        <script-language>groovy</script-language>\n    </timer-action>\n</timer-actions>\n\\end{verbatim}\n\nThe above example isn't functional but it demonstrates setting up a\n\\texttt{\\textless{}script\\textgreater{}} in your task timer.\n\\href{/docs/7-2/user/-/knowledge_base/u/leveraging-the-script-engine-in-workflow}{Read\nthe \\emph{Scripting in Workflow} article} for more information.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} A \\texttt{timer-action} can contain all the same tags as\nan \\texttt{action}, with one exception: \\texttt{execution-type}. Timer\nactions are always triggered once the time is up, so specifying and\nexecution type of \\texttt{onEntry}, for example, isn't meaningful inside\na timer.\n\n\\noindent\\hrulefill\n\nTasks are at the core of the workflow definition. Once you understand\nhow to create tasks and the other\n\\href{/docs/7-2/reference/-/knowledge_base/r/workflow-definition-nodes}{workflow\nnodes} and add transitions between the nodes, you're on the cusp of\nworkflow wizard-hood.\n\n\\chapter{Workflow Notifications}\\label{workflow-notifications}\n\nWhile an asset is in a workflow, relevant Users should be notified about\ncertain events, like when a review task is completed. Any workflow node\nwith an \\texttt{\\textless{}actions\\textgreater{}} element can have\nnotifications.\n\n\\begin{verbatim}\n<actions>\n    <action>\n        <notification>\n            <name>Creator Modification Notification</name>\n            <template>Your submission was rejected by ${userName}, please modify and resubmit.</template>\n            <template-language>freemarker</template-language>\n            <notification-type>email</notification-type>\n            <notification-type>user-notification</notification-type>\n            <execution-type>onAssignment</execution-type>\n        </notification>\n    </actions>\n</actions>\n\\end{verbatim}\n\nThe above Creator Modification Notification sends a notification message\nin two ways: via email and via user notification (this goes to the\nNotifications widget in the User's Site). The message is defined in a\nFreeMarker template and sent once a task assignment is created. But who\nreceives the notification? If no recipients are explicitly specified via\na \\texttt{recipients} tag, the asset's creator receives the\nnotification.\n\n\\section{Notification Options}\\label{notification-options}\n\nThere are several elements that can be specified in a\n\\texttt{\\textless{}notification\\textgreater{}}:\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Name}]\nSet the name of the notification in the\n\\texttt{\\textless{}name\\textgreater{}} element. This information is used\nto display the notification in the \\emph{My Workflow Tasks} widget of a\nUser's personal Site.\n\\item[\\textbf{Description}]\nThe \\texttt{\\textless{}description\\textgreater{}} element contains the\nsubject of the notification if the notification type is \\texttt{email}.\nThe syntax is determined by the template language you're using.\n\\item[\\textbf{Template}]\nThe \\texttt{\\textless{}template\\textgreater{}} element contains the\nmessage of the notification. The syntax is determined by the template\nlanguage you're using.\n\\item[\\textbf{Template Language}]\nChoose from \\texttt{freemarker}, \\texttt{velocity}, or plain\n\\texttt{text} in the \\texttt{\\textless{}template-language\\textgreater{}}\ntag.\n\\item[\\textbf{Notification Type}]\nChoose whether to send an \\texttt{email}, \\texttt{user-notification}\n(via the Notification widget), \\texttt{im} (instant message), or\n\\texttt{private-message} in the\n\\texttt{\\textless{}notification-type\\textgreater{}} tag.\n\\end{description}\n\n\\begin{verbatim}\n<notification-type>email</notification-type>\n\\end{verbatim}\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Execution Type}]\nChoose to link the sending of the notification to entry into the node\n(\\texttt{onEntry}), when a task is assigned (\\texttt{onAssignment}), or\nwhen the workflow processing is leaving a node (\\texttt{onExit}). If you\nspecify a notification to be sent on assignment, the assignee is\nnotified automatically.\n\\item[\\textbf{Recipients}]\nDecide who should receive the notification in the\n\\texttt{\\textless{}recipients\\textgreater{}} tag:\n\\end{description}\n\n\\begin{verbatim}\n<recipients>\n    [SEE BELOW FOR THE AVAILABLE RECIPIENT TAGS]\n</recipients>\n\\end{verbatim}\n\nAvailable recipient tags are\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{\\textless{}user\\textgreater{}}: notify the User that sent the\n  asset through the workflow. Specify the tag as\n  \\texttt{\\textless{}user\\ /\\textgreater{}}. To notify a specific user,\n  enter the \\texttt{userId}:\n\\end{itemize}\n\n\\begin{verbatim}\n<recipients>\n    <user />\n</recipients>\n<recipients>\n    <user>\n        <user-id>20139</user-id>\n    </user>\n</recipients>\n\\end{verbatim}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{\\textless{}roles\\textgreater{}}: notify specific Roles, either\n  by ID or by their type and name.\n\\end{itemize}\n\n\\begin{verbatim}\n<recipients>\n    <roles>\n        <role>\n            <role-id>33621</role-id>\n        </role>\n    </roles>\n</recipients>\n<recipients>\n    <roles>\n        <role>\n            <role-type>regular</role-type>\n            <name>Power User</name>\n            <auto-create>false</auto-create>\n        </role>\n    </roles>\n</recipients>\n\\end{verbatim}\n\n\\begin{itemize}\n\\item\n  \\texttt{\\textless{}assignees\\ /\\textgreater{}}: notify the task\n  assignees.\n\\item\n  \\texttt{\\textless{}scripted-recipient\\textgreater{}}: use a script to\n  identify notification recipients.\n\\end{itemize}\n\n\\begin{verbatim}\n<recipients>\n    <scripted-recipient>\n        <script>\n            <![CDATA[Script logic goes here]]>\n        </script>\n        <script-language>groovy</script-language>\n    </scripted-recipient>\n</recipients>\n\\end{verbatim}\n\nIf the notification type is \\texttt{email}, you can specify the\n\\texttt{recipientType} attribute of the\n\\texttt{\\textless{}recipients\\textgreater{}} tag as \\emph{To},\n\\emph{CC}, or \\emph{BCC}.\n\n\\begin{verbatim}\n<recipients receptionType=\"cc\">\n    <roles>\n        <role>\n            <role-type>regular</role-type>\n            <name>Manager</name>\n        </role>\n    </roles>\n</recipients>\n\\end{verbatim}\n\nBy default, \\texttt{recipientType} is \\texttt{to}.\n\nAs always, read the\n\\href{https://www.liferay.com/dtd/liferay-workflow-definition_7_1_0.xsd}{schema\nfor all the details}.\n\n\\chapter{Breaking Changes}\\label{breaking-changes}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThis document presents a chronological list of changes that break\nexisting functionality, APIs, or contracts with third party Liferay\ndevelopers or users. We try our best to minimize these disruptions, but\nsometimes they are unavoidable.\n\nHere are some of the types of changes documented in this file:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Functionality that is removed or replaced\n\\item\n  API incompatibilities: Changes to public Java or JavaScript APIs\n\\item\n  Changes to context variables available to templates\n\\item\n  Changes in CSS classes available to Liferay themes and portlets\n\\item\n  Configuration changes: Changes in configuration files, like\n  \\texttt{portal.properties}, \\texttt{system.properties}, etc.\n\\item\n  Execution requirements: Java version, J2EE Version, browser versions,\n  etc.\n\\item\n  Deprecations or end of support: For example, warning that a certain\n  feature or API will be dropped in an upcoming version.\n\\item\n  Recommendations: For example, recommending using a newly introduced\n  API that replaces an old API, in spite of the old API being kept in\n  Liferay Portal for backwards compatibility.\n\\end{itemize}\n\n\\section{Breaking Changes List}\\label{breaking-changes-list}\n\n\\section{Removed Support for JSP Templates in\nThemes}\\label{removed-support-for-jsp-templates-in-themes}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2018-Nov-14\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-87064}{LPS-87064}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed}\n\nThemes can no longer leverage JSP templates. Also, related logic has\nbeen removed from the public APIs\n\\texttt{com.liferay.portal.kernel.util.ThemeHelper} and\n\\texttt{com.liferay.taglib.util.ThemeUtil}.\n\n\\subsection{Who is affected?}\\label{who-is-affected}\n\nThis affects anyone who has themes using JSP templates or is using the\nremoved methods.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code}\n\nIf you have a theme using JSP templates, consider migrating it to\nFreeMarker.\n\n\\subsection{Why was this change made?}\\label{why-was-this-change-made}\n\nJSP is not a real template engine and is rarely used. FreeMarker is the\nrecommended template engine moving forward.\n\nThe removal of JSP templates allows for an increased focus on existing\nand new template engines.\n\n\\section{Lodash Is No Longer Included by\nDefault}\\label{lodash-is-no-longer-included-by-default}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2018-Nov-27\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-87677}{LPS-87677}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-1}\n\nPreviously, Lodash was included in every page by default and made\navailable through the global \\texttt{window.\\_} and scoped\n\\texttt{AUI.\\_} variables. Lodash is no longer included by default and\nthose variables are now undefined.\n\n\\subsection{Who is affected?}\\label{who-is-affected-1}\n\nThis affects any developer who used the \\texttt{AUI.\\_} or\n\\texttt{window.\\_} variables in their custom scripts.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-1}\n\nYou should provide your own Lodash version for your custom developments\nto use following any of the possible strategies to add third party\nlibraries.\n\nAs a temporary measure, you can bring back the old behavior by setting\nthe \\emph{Enable Lodash} property in Liferay Portal's \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{System Settings} → \\emph{Third\nParty} → \\emph{Lodash} to \\texttt{true}.\n\n\\subsection{Why was this change made?}\\label{why-was-this-change-made-1}\n\nThis change was made to avoid bundling and serving additional library\ncode on every page that was mostly unused and redundant.\n\n\\section{Moved Two Staging Properties to OSGi\nConfiguration}\\label{moved-two-staging-properties-to-osgi-configuration}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2018-Dec-12\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-88018}{LPS-88018}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-2}\n\nTwo Staging properties have been moved from \\texttt{portal.properties}\nto an OSGi configuration named\n\\texttt{ExportImportServiceConfiguration.java} in the\n\\texttt{export-import-service} module.\n\n\\subsection{Who is affected?}\\label{who-is-affected-2}\n\nThis affects anyone using the following portal properties:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{staging.delete.temp.lar.on.failure}\n\\item\n  \\texttt{staging.delete.temp.lar.on.success}\n\\end{itemize}\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-2}\n\nInstead of overriding the \\texttt{portal.properties} file, you can\nmanage the properties from Portal's configuration administrator. This\ncan be accessed by navigating to Liferay Portal's \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{System Settings} → \\emph{Infrastructure} →\n\\emph{Export/Import} and editing the settings there.\n\nIf you would like to include the new configuration in your application,\nfollow the instructions for\n\\href{https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-1/making-applications-configurable}{making\napplications configurable}.\n\n\\subsection{Why was this change made?}\\label{why-was-this-change-made-2}\n\nThis change was made as part of the modularization efforts to ease\nportal configuration changes.\n\n\\section{Remove Link Application URLs to Page\nFunctionality}\\label{remove-link-application-urls-to-page-functionality}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2018-Dec-14\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-85948}{LPS-85948}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-3}\n\nThe \\emph{Link Portlet URLs to Page} option in the Look and Feel portlet\nwas marked as deprecated in Liferay Portal 7.1, allowing the user to\nshow and hide the option through a configuration property. In Liferay\nPortal 7.2, this has been removed and can no longer be configured.\n\n\\subsection{Who is affected?}\\label{who-is-affected-3}\n\nThis affects administrators who used the option in the UI and developers\nwho leveraged the option in the portlet.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-3}\n\nYou should update any portlets leveraging this feature, since any\npreconfigured reference to the property is ignored in the portal.\n\n\\subsection{Why was this change made?}\\label{why-was-this-change-made-3}\n\nA limited number of portlets use this property; there are better ways to\nachieve the same results.\n\n\\section{Moved TermsOfUseContentProvider out of\nkernel.util}\\label{moved-termsofusecontentprovider-out-of-kernel.util}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Jan-07\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-88869}{LPS-88869}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-4}\n\nThe \\texttt{TermsOfUseContentProvider} interface's package changed:\n\n\\texttt{com.liferay.portal.kernel.util} →\n\\texttt{com.liferay.portal.kernel.term.of.use}\n\nThe \\texttt{TermsOfUseContentProviderRegistryUtil} class' name and\npackage changed:\n\n\\texttt{TermsOfUseContentProviderRegistryUtil} →\n\\texttt{TermsOfUseContentProviderUtil}\n\nand \\texttt{com.liferay.portal.kernel.util} →\n\\texttt{com.liferay.portal.internal.terms.of.use}\n\nThe logic of getting \\texttt{TermsOfUseContentProvider} was also\nchanged. Instead of always returning the first service registered, which\nis random and depends on the order of registered services, the\n\\texttt{TermsOfUseContentProvider} service is tracked and updated with\n\\texttt{com.liferay.portal.kernel.util.ServiceProxyFactory}. As a\nresult, the \\texttt{TermsOfUseContentProvider} now respects service\nranking.\n\n\\subsection{Who is affected?}\\label{who-is-affected-4}\n\nThis affects anyone who used\n\\texttt{com.liferay.portal.kernel.util.TermsOfUseContentProviderRegistryUtil}\nto lookup the\n\\texttt{com.liferay.portal.kernel.util.TermsOfUseContentProvider}\nservice.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-4}\n\nIf \\texttt{com.liferay.portal.kernel.util.TermsOfUseContentProvider} is\nused, update the import package name. If there is any usage in\n\\texttt{portal-web}, update\n\\texttt{com.liferay.portal.kernel.util.TermsOfUseContentProviderRegistryUtil}\nto\n\\texttt{com.liferay.portal.kernel.term.of.use.TermsOfUseContentProviderUtil}.\nRemove usages of\n\\texttt{com.liferay.portal.kernel.util.TermsOfUseContentProviderRegistryUtil}\nin modules and use the \\texttt{@Reference} annotation to fetch the\n\\texttt{com.liferay.portal.kernel.term.of.use.TermsOfUseContentProvider}\nservice instead.\n\n\\subsection{Why was this change made?}\\label{why-was-this-change-made-4}\n\nThis is one of several steps to clean up kernel provider interfaces to\nreduce the chance of package version lock down.\n\n\\section{Removed HibernateConfigurationConverter and\nConverter}\\label{removed-hibernateconfigurationconverter-and-converter}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Jan-07\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-88870}{LPS-88870}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-5}\n\nThe interface \\texttt{com.liferay.portal.kernel.util.Converter} and its\nimplementation\n\\texttt{com.liferay.portal.spring.hibernate.HibernateConfigurationConverter}\nwere removed.\n\n\\subsection{Who is affected?}\\label{who-is-affected-5}\n\nThis removes the support of generating customized\n\\texttt{portlet-hbm.xml} files implemented by\n\\texttt{HibernateConfigurationConverter}. Refer to\n\\href{https://issues.liferay.com/browse/LPS-5363}{LPS-5363} for more\ninformation.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-5}\n\nYou should remove usages of \\texttt{HibernateConfigurationConverter}.\nMake sure the generated \\texttt{portlet-hbm.xml} is accurate.\n\n\\subsection{Why was this change made?}\\label{why-was-this-change-made-5}\n\nThis is one of several steps to clean up kernel provider interfaces to\nreduce the chance of package version lock down.\n\n\\section{Switched to Use JDK Function and\nSupplier}\\label{switched-to-use-jdk-function-and-supplier}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Jan-08\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-88911}{LPS-88911}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-6}\n\nThe \\texttt{Function} and \\texttt{Supplier} interfaces in package\n\\texttt{com.liferay.portal.kernel.util} were removed. Their usages were\nreplaced with \\texttt{java.util.function.Function} and\n\\texttt{java.util.function.Supplier}.\n\n\\subsection{Who is affected?}\\label{who-is-affected-6}\n\nThis affects anyone who implemented the \\texttt{Function} and\n\\texttt{Supplier} interfaces in package\n\\texttt{com.liferay.portal.kernel.util}.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-6}\n\nYou should replace usages of\n\\texttt{com.liferay.portal.kernel.util.Function} and\n\\texttt{com.liferay.portal.kernel.util.Supplier} with\n\\texttt{java.util.function.Function} and\n\\texttt{java.util.function.Supplier}, respectively.\n\n\\subsection{Why was this change made?}\\label{why-was-this-change-made-6}\n\nThis is one of several steps to clean up kernel provider interfaces to\nreduce the chance of package version lock down.\n\n\\section{Deprecated com.liferay.portal.service.InvokableService\nInterface}\\label{deprecated-com.liferay.portal.service.invokableservice-interface}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Jan-08\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-88912}{LPS-88912}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-7}\n\nThe \\texttt{InvokableService} and \\texttt{InvokableLocalService}\ninterfaces in package \\texttt{com.liferay.portal.kernel.service} were\nremoved.\n\n\\subsection{Who is affected?}\\label{who-is-affected-7}\n\nThis affects anyone who used \\texttt{InvokableService} and\n\\texttt{InvokableLocalService} in package\n\\texttt{com.liferay.portal.kernel.service}.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-7}\n\nYou should remove usages of \\texttt{InvokableService} and\n\\texttt{InvokableLocalService}. Make sure to use the latest version of\nService Builder to generate implementations for services in case there\nis any compile errors after removal.\n\n\\subsection{Why was this change made?}\\label{why-was-this-change-made-7}\n\nThis is one of several steps to clean up kernel provider interfaces to\nreduce the chance of package version lock down.\n\n\\section{Dropped Support of\nServiceLoaderCondition}\\label{dropped-support-of-serviceloadercondition}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Jan-08\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-88913}{LPS-88913}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-8}\n\nThe interface \\texttt{ServiceLoaderCondition} and its implementation\n\\texttt{DefaultServiceLoaderCondition} in package\n\\texttt{com.liferay.portal.kernel.util} were removed.\n\n\\subsection{Who is affected?}\\label{who-is-affected-8}\n\nThis affects anyone using \\texttt{ServiceLoaderCondition} and\n\\texttt{DefaultServiceLoaderCondition}.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-8}\n\nYou should remove usages of \\texttt{ServiceLoaderCondition}. Update\nusages of \\texttt{load} methods in\n\\texttt{com.liferay.portal.kernel.util.ServiceLoader} according to the\nupdated method signatures.\n\n\\subsection{Why was this change made?}\\label{why-was-this-change-made-8}\n\nThis is one of several steps to clean up kernel provider interfaces to\nreduce the chance of package version lock down.\n\n\\section{Switched to Use JDK\nPredicate}\\label{switched-to-use-jdk-predicate}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Jan-14\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-89139}{LPS-89139}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-9}\n\nThe interface \\texttt{com.liferay.portal.kernel.util.PredicateFilter}\nwas removed and replaced with \\texttt{java.util.function.Predicate}. As\na result, the following implementations were removed:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.liferay.portal.kernel.util.AggregatePredicateFilter}\n\\item\n  \\texttt{com.liferay.portal.kernel.util.PrefixPredicateFilter}\n\\item\n  \\texttt{com.liferay.portal.kernel.portlet.JavaScriptPortletResourcePredicateFilter}\n\\item\n  \\texttt{com.liferay.dynamic.data.mapping.form.values.query.internal.model.DDMFormFieldValuePredicateFilter}\n\\end{itemize}\n\nThe \\texttt{com.liferay.portal.kernel.util.ArrayUtil\\_IW} class was\nregenerated.\n\n\\subsection{Who is affected?}\\label{who-is-affected-9}\n\nThis affects anyone who used \\texttt{PredicateFilter},\n\\texttt{AggregatePredicateFilter}, \\texttt{PrefixPredicateFilter},\n\\texttt{JavaScriptPortletResourcePredicateFilter}, and\n\\texttt{DDMFormFieldValuePredicateFilter}.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-9}\n\nYou should replace usages of\n\\texttt{com.liferay.portal.kernel.util.PredicateFilter} with\n\\texttt{java.util.function.Predicate}. Additionally, remove usages of\n\\texttt{AggregatePredicateFilter}, \\texttt{PrefixPredicateFilter},\n\\texttt{JavaScriptPortletResourcePredicateFilter}, and\n\\texttt{DDMFormFieldValuePredicateFilter}.\n\n\\subsection{Why was this change made?}\\label{why-was-this-change-made-9}\n\nThis is one of several steps to clean up kernel provider interfaces to\nreduce the chance of package version lock down.\n\n\\section{Removed Unsafe Functional Interfaces in Package\ncom.liferay.portal.kernel.util}\\label{removed-unsafe-functional-interfaces-in-package-com.liferay.portal.kernel.util}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Jan-15\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-89223}{LPS-89223}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-10}\n\nThe \\texttt{com.liferay.portal.osgi.util.test.OSGiServiceUtil} class was\nremoved. Also, the following interfaces were removed from the\n\\texttt{com.liferay.portal.kernel.util} package:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{UnsafeConsumer}\n\\item\n  \\texttt{UnsafeFunction}\n\\item\n  \\texttt{UnsafeRunnable}\n\\end{itemize}\n\n\\subsection{Who is affected?}\\label{who-is-affected-10}\n\nThis affects anyone using the class/interfaces mentioned above.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-10}\n\nThe \\texttt{com.liferay.portal.osgi.util.test.OSGiServiceUtil} class has\nbeen deprecated since Liferay Portal 7.1. If usages for this class still\nexist, replace it with its direct replacement:\n\\texttt{com.liferay.osgi.util.service.OSGiServiceUtil}. Replace usages\nof \\texttt{UnsafeConsumer}, \\texttt{UnsafeFunction} and\n\\texttt{UnsafeRunnable} with their corresponding interfaces in package\n\\texttt{com.liferay.petra.function}.\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-10}\n\nThis is one of several steps to clean up kernel provider interfaces to\nreduce the chance of package version lock down.\n\n\\section{Deprecated NTLM in Portal\nDistribution}\\label{deprecated-ntlm-in-portal-distribution}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Jan-21\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-88300}{LPS-88300}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-11}\n\nNTLM modules have been moved from the \\texttt{portal-security-sso}\nproject to a new project named \\texttt{portal-security-sso-ntlm}. This\nnew project is deprecated and available to download from Liferay\nMarketplace.\n\n\\subsection{Who is affected?}\\label{who-is-affected-11}\n\nThis affects anyone using NTLM as an authentication system.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-11}\n\nIf you want to continue using NTLM as an authentication system, you must\ndownload the corresponding modules from Liferay Marketplace.\nAlternatively, you can migrate to Kerberos (recommended), which requires\nno changes and is compatible with Liferay Portal 7.0+.\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-11}\n\nThis change was made to avoid using an old proprietary solution (NTLM).\nKerberos is now recommended, which is a standard protocol and a more\nsecure method of authentication compared to NTLM.\n\n\\section{Deprecated OpenID in Portal\nDistribution}\\label{deprecated-openid-in-portal-distribution}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Jan-21\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-88906}{LPS-88906}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-12}\n\nOpenID modules have been moved to a new project named\n\\texttt{portal-security-sso-openid}. This new project is deprecated and\navailable to download from Liferay Marketplace.\n\n\\subsection{Who is affected?}\\label{who-is-affected-12}\n\nThis affects anyone using OpenID as an authentication system.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-12}\n\nIf you want to continue using OpenID as an authentication system, you\nmust download the corresponding module from Liferay Marketplace.\nAlternatively, you should migrate to OpenID Connect, available on\nLiferay Portal Distribution.\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-12}\n\nThis change was made to avoid using a deprecated solution (OpenID).\nOpenID Connect is now recommended, which is a more secure method of\nauthentication since it runs on top of OAuth.\n\n\\section{Deprecated Google SSO in Portal\nDistribution}\\label{deprecated-google-sso-in-portal-distribution}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Jan-21\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-88905}{LPS-88905}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-13}\n\nGoogle SSO modules have been moved from the \\texttt{portal-security-sso}\nproject to a new project named \\texttt{portal-security-sso-google}. This\nnew project is deprecated and available to download from Liferay\nMarketplace.\n\n\\subsection{Who is affected?}\\label{who-is-affected-13}\n\nThis affects anyone using Google SSO as an authentication system.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-13}\n\nIf you want to continue using Google SSO as an authentication system,\nyou must download the corresponding module from Liferay Marketplace.\nAlternatively, you can use OpenID Connect.\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-13}\n\nThis change was made to avoid using an old solution for authentication\n(Google SSO). OpenID Connect is the recommended specification to use\nGoogle implementation for authentication.\n\n\\section{Updated AlloyEditor v2.0 Includes New Major Version of\nReact}\\label{updated-alloyeditor-v2.0-includes-new-major-version-of-react}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Feb-04\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-90079}{LPS-90079}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-14}\n\nAlloyEditor was upgraded to version 2.0.0, which includes a major\nupgrade from React v15 to v16.\n\nThe \\texttt{React.createClass} was\n\\href{https://reactjs.org/blog/2017/04/07/react-v15.5.0.html}{deprecated\nin React v15.5.0} (April 2017) and\n\\href{https://reactjs.org/blog/2017/09/26/react-v16.0.html}{removed in\nReact v16.0.0} (September 2017). All the buttons bundled with\nAlloyEditor have been updated to use the ES6 class syntax instead of\n\\texttt{React.createClass}.\n\n\\subsection{Who is affected?}\\label{who-is-affected-14}\n\nThis affects anyone who built their own buttons using\n\\texttt{React.createClass}. The \\texttt{createClass} function is no\nlonger available, and attempts to access it at runtime will trigger an\nerror.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-14}\n\nYou should update your code in one of two ways:\n\n\\begin{itemize}\n\\item\n  Port custom buttons from the \\texttt{React.createClass} API to use the\n  ES6 \\texttt{class} API, as described in\n  \\href{https://reactjs.org/docs/react-component.html}{the React\n  documentation}. For example, see the changes made in moving to an\n  \\href{https://github.com/liferay/alloy-editor/blob/b082c312179ae6626cb2ddcc04ad3ebc5b355e1b/src/components/buttons/button-ol.jsx}{ES6\n  class-based button} from\n  \\href{https://github.com/liferay/alloy-editor/blob/2826ab9ceabe17c6ba0d38985baf8a787c23db43/src/ui/react/src/components/buttons/button-ol.jsx}{the\n  previous \\texttt{createClass}-based implementation}.\n\\item\n  Provide a compatibility adapter. The\n  \\href{https://www.npmjs.com/package/create-react-class}{create-react-class\n  package} (described\n  \\href{https://reactjs.org/docs/react-without-es6.html}{here}) can be\n  injected into the page to restore the \\texttt{createClass} API.\n\\end{itemize}\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-14}\n\nThis change was made to use a newer major version of React, which brings\nperformance and compatibility improvements and reduces the bundle size\nby removing deprecated APIs.\n\n\\section{Deprecated dl.tabs.visible\nproperty}\\label{deprecated-dl.tabs.visible-property}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Apr-10\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-93948}{LPS-93948}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-15}\n\nThe \\texttt{dl.tabs.visible} property let users toggle the visibility of\na Documents and Media widget's navigation tabs when placed on a widget\npage. This configuration option has been removed, so the navigation tab\nwill never appear on widget pages.\n\n\\subsection{Who is affected?}\\label{who-is-affected-15}\n\nThis affects anyone who set the \\texttt{dl.tabs.visible} property to\n\\texttt{true}.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-15}\n\nNo code changes are necessary.\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-15}\n\nDocuments \\& Media has been reviewed from a UX perspective, and removing\nthe navigation tabs in widget pages was part of a UI clean up process.\n\n\\section{Move the User Menu out of the Product\nMenu}\\label{move-the-user-menu-out-of-the-product-menu}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Apr-19\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-87868}{LPS-87868}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-16}\n\nThe User Menu was removed from the Product Menu, and the user menu\nentries were moved to the new Personal Menu, a dropdown menu triggered\nby the user avatar.\n\n\\subsection{Who is affected?}\\label{who-is-affected-16}\n\nThis affects anyone who has customized the User Menu section of the\nProduct Menu.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-16}\n\nIf you would like to keep your custom user menu entries and have them\navailable in the Personal Menu, you need to implement the\n\\texttt{PersonalMenuEntry} interface. All panel apps registered with the\n\\texttt{PanelCategoryKeys.USER},\n\\texttt{PanelCategoryKeys.USER\\_MY\\_ACCOUNT}, and\n\\texttt{PanelCategoryKeys.USER\\_SIGN\\_OUT} panel category keys should be\nconverted to \\texttt{PersonalMenuEntry}.\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-16}\n\nProduct navigation has been reviewed from a UX perspective, and removing\nthe User Menu from the Product Menu and splitting the menu to its own\nprovides a better user experience.\n\n\\section{Removed Hong Kong and Macau from the List of\nCountries}\\label{removed-hong-kong-and-macau-from-the-list-of-countries}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Apr-26\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-82203}{LPS-82203}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-17}\n\nHong Kong and Macau have been removed from the list of countries and\nlisted as regions of China as Xianggang (region code: CN-91) and Aomen\n(region code: CN-92), respectively.\n\n\\subsection{Who is affected?}\\label{who-is-affected-17}\n\nThis affects anyone who used Hong Kong or Macau in their addresses.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-17}\n\nNo code changes are necessary. However, if you have hardcoded the\n\\texttt{countryId} of Hong Kong and Macau in your code, they should be\nupdated to China's \\texttt{countryId}. References to Hong Kong and Macau\nshould be done with their corresponding \\texttt{regionId}.\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-17}\n\nAfter the handover of Hong Kong in 1997 and of Macau in 1999, Hong Kong\nand Macau are now the special administrative regions of China.\n\n\\section{JGroups Was Upgraded From 3.6.16 to\n4.1.1}\\label{jgroups-was-upgraded-from-3.6.16-to-4.1.1}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Aug-15\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-97897}{LPS-97897}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-18}\n\nJGroups was upgraded from version 3.6.16 to 4.1.1.\n\n\\subsection{Who is affected?}\\label{who-is-affected-18}\n\nThis affects anyone using Cluster Link.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-18}\n\nThe \\texttt{cluster.link.channel.properties.*} property in\n\\texttt{portal.properties} no longer accepts a connection string as a\nvalue; it now requires a file path to a configuration XML file. Some of\nthe protocol properties from 3.6.16 are removed and no longer parsed by\n4.1.1; you should update the protocol properties accordingly.\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-18}\n\nThis upgrade was made to fix a security issue.\n\n\\section{\\texorpdfstring{Liferay \\texttt{AssetEntries\\_AssetCategories}\nIs No Longer\nUsed}{Liferay AssetEntries\\_AssetCategories Is No Longer Used}}\\label{liferay-assetentries_assetcategories-is-no-longer-used}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2019-Sep-11\n\\item\n  \\textbf{JIRA Tickets:}\n  \\href{https://issues.liferay.com/browse/LPS-99973}{LPS-99973},\n  \\href{https://issues.liferay.com/browse/LPS-76488}{LPS-76488}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-19}\n\nPreviously, Liferay used a mapping table and a corresponding interface\nfor the relationship between \\texttt{AssetEntry} and\n\\texttt{AssetCategory} in \\texttt{AssetEntryLocalService} and\n\\texttt{AssetCategoryLocalService}. This mapping table and the\ncorresponding interface have been replaced by the table\n\\texttt{AssetEntryAssetCategoryRel} and the service\n\\texttt{AssetEntryAssetCategoryRelLocalService}.\n\n\\subsection{Who is affected?}\\label{who-is-affected-19}\n\nThis affects any content or code that relies on calling the old\ninterfaces for the \\texttt{AssetEntries\\_AssetCategories} relationship,\nthrough the \\texttt{AssetEntryLocalService} and\n\\texttt{AssetCategoryLocalService}.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-19}\n\nUse the new methods in \\texttt{AssetEntryAssetCategoryRelLocalService}\nto retrieve the same data as before. The method signatures haven't\nchanged; they have just been relocated to a different service.\n\n\\textbf{Example}\n\nOld way:\n\n\\begin{verbatim}\nList<AssetEntry> entries =\nAssetEntryLocalServiceUtil.getAssetCategoryAssetEntries(categoryId);\n\nfor (AssetEntry entry: entries) {\n  ...\n}\n\\end{verbatim}\n\nNew way:\n\n\\begin{verbatim}\nlong[] assetEntryPKs =\n_assetEntryAssetCategoryRelLocalService.getAssetEntryPrimaryKeys(assetCategoryId);\n\nfor (long assetEntryPK: assetEntryPKs) {\n  AssetEntry = _assetEntryLocalService.getEntry(assetEntryPK);\n  ...\n}\n\n...\n\n@Reference\nprivate AssetEntryAssetCategoryRelLocalService _assetEntryAssetCategoryRelLocalService;\n\n@Reference\nprivate AssetEntryLocalService _assetEntryLocalService;\n\\end{verbatim}\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-19}\n\nThis change was made due to changes resulting from\n\\href{https://issues.liferay.com/browse/LPS-76488}{LPS-76488}, which let\ndevelopers control the order of a list of assets for a given category.\n\n\\section{Auto Tagging Must Be Reconfigured\nManually}\\label{auto-tagging-must-be-reconfigured-manually}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date: 2019-Oct-2}\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-97123}{LPS-97123}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-20}\n\nAuto Tagging configurations were renamed and reorganized. There's no\nlonger an automatic upgrade process, so you must reconfigure Auto\nTagging manually.\n\n\\subsection{Who is affected?}\\label{who-is-affected-20}\n\nThis affects DXP 7.2 installations that are upgraded to SP1 and have\nAuto Tagging configured and enabled.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-20}\n\nYou must reconfigure Auto Tagging through System Settings (please see\nthe\n\\href{https://help.liferay.com/hc/en-us/articles/360029041551-Configuring-Asset-Auto-Tagging}{official\ndocumentation} for details). Any code referencing the old configuration\ninterfaces must be updated to use the new ones.\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-20}\n\nThis change unifies the previously split configuration interfaces,\nimproving the user experience.\n\n\\section{Blogs Image Properties Were Moved to System\nSettings}\\label{blogs-image-properties-were-moved-to-system-settings}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date: 2019-Oct-2}\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-95298}{LPS-95298}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-21}\n\nBlogs image configuration was moved from \\texttt{portal.properties} to\nSystem Settings. There's no automatic upgrade process, so custom Blogs\nimage properties must be reconfigured manually.\n\n\\subsection{Who is affected?}\\label{who-is-affected-21}\n\nThis affects DXP 7.2 installations that are upgraded to SP1 and have\ncustom values for the \\texttt{blogs.image.max.size} and\n\\texttt{blogs.image.extensions} properties.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-21}\n\nIf you would like to keep your custom Blogs image property values, you\nmust reconfigure them through the System Settings under\n\\emph{Configuration} → \\emph{Blogs} → \\emph{File Uploads}. Any code\nreferencing the old properties must be updated to use the new\nconfiguration interfaces.\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-21}\n\nThis change was made so Blogs image properties can be configured without\na restart.\n\n\\section{Removed Cache Bootstrap\nFeature}\\label{removed-cache-bootstrap-feature}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Date:} 2020-Jan-8\n\\item\n  \\textbf{JIRA Ticket:}\n  \\href{https://issues.liferay.com/browse/LPS-96563}{LPS-96563}\n\\end{itemize}\n\n\\subsection{What changed?}\\label{what-changed-22}\n\nThe cache bootstrap feature has been removed. These properties can no\nlonger be used to enable/configure cache bootstrap:\n\n\\texttt{ehcache.bootstrap.cache.loader.enabled},\n\\texttt{ehcache.bootstrap.cache.loader.properties.default},\n\\texttt{ehcache.bootstrap.cache.loader.properties.\\$\\{specific.cache.name\\}}.\n\n\\subsection{Who is affected?}\\label{who-is-affected-22}\n\nThis affects anyone using the properties listed above.\n\n\\subsection{How should I update my\ncode?}\\label{how-should-i-update-my-code-22}\n\nThere's no direct replacement for the removed feature. If you have code\nthat depends on it, you must implement it yourself.\n\n\\subsection{Why was this change\nmade?}\\label{why-was-this-change-made-22}\n\nThis change was made to avoid security issues.\n\n\\chapter{CDI Portlet Predefined\nBeans}\\label{cdi-portlet-predefined-beans}\n\nLiferay DXP provides injectable portlet artifacts for\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/cdi-dependency-injection}{CDI}\ncalled Portlet Predefined Beans, as specified by\n\\href{https://jcp.org/en/jsr/detail?id=362}{JSR 362}. There are two\ntypes of predefined beans:\n\n\\begin{itemize}\n\\item\n  Portlet Request Scoped Beans\n  (\\href{https://docs.liferay.com/portlet\\%20\\%7C\\%20-\\%20\\%7C\\%20api/3.0/javadocs/javax/portlet/annotations/PortletRequestScoped.html}{\\texttt{@PortletRequestScoped}})\n\\item\n  Dependent Scoped Beans\n  (\\href{https://docs.oracle.com/javaee/7/api/javax/enterprise/context/Dependent.html}{\\texttt{@Dependent}\n  scoped})\n\\end{itemize}\n\nThe table below describes these attributes for each bean:\n\n\\textbf{Artifact:} The bean's type.\n\n\\textbf{Bean EL Name:} Expression Language (EL) name for accessing the\nbean in a JSP or JSF page.\n\n\\textbf{Qualifier:} Annotation applied to the bean for defining and\nselecting a bean implementation.\n\n\\textbf{Valid during (phase):} The\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{portlet phases}\nin which the bean is valid.\n\n\\section{Portlet Request Scoped\nBeans}\\label{portlet-request-scoped-beans}\n\nThese beans have the \\texttt{@PortletRequestScoped} annotation. Here are\ntheir artifact types, bean EL names, and annotation qualifiers, along\nwith their valid portlet phases.\n\nTable 1: Portlet Request Scoped Beans\\footnote{Martin Scott Nicklous,\n  Java™ Portlet Specification 3.0, page 122.}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}llll@{}}\n\\toprule\\noalign{}\nArtifact & Bean EL Name & Qualifier & Valid during \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{PortletConfig} & \\texttt{portletConfig} & - & all \\\\\n\\texttt{PortletRequest} & \\texttt{portletRequest} & - & all \\\\\n\\texttt{PortletResponse} & \\texttt{portletResponse} & - & all \\\\\n\\texttt{ActionRequest} & \\texttt{actionRequest} & - & action \\\\\n\\texttt{ActionResponse} & \\texttt{actionResponse} & - & action \\\\\n\\texttt{HeaderRequest} & \\texttt{headerRequest} & - & header \\\\\n\\texttt{HeaderResponse} & \\texttt{headerResponse} & - & header \\\\\n\\texttt{RenderRequest} & \\texttt{renderRequest} & - & render \\\\\n\\texttt{RenderResponse} & \\texttt{renderResponse} & - & render \\\\\n\\texttt{EventRequest} & \\texttt{eventRequest} & - & event \\\\\n\\texttt{EventResponse} & \\texttt{eventResponse} & - & event \\\\\n\\texttt{ResourceRequest} & \\texttt{resourceRequest} & - & resource \\\\\n\\texttt{ResourceResponse} & \\texttt{resourceResponse} & - & resource \\\\\n\\texttt{StateAwareResponse} & \\texttt{stateAwareResponse} & - & action,\nevent \\\\\n\\texttt{MimeResponse} & \\texttt{mimeResponse} & - & header, render,\nresource \\\\\n\\texttt{ClientDataRequest} & \\texttt{clientDataRequest} & - & action,\nresource \\\\\n\\texttt{RenderParameters} & \\texttt{renderParams} & - & all \\\\\n\\texttt{MutableRenderParameters} & \\texttt{mutableRenderParams} & - &\naction, event \\\\\n\\texttt{ActionParameters} & \\texttt{actionParams} & - & action \\\\\n\\texttt{ResourceParameters} & \\texttt{resourceParams} & - & resource \\\\\n\\texttt{PortletContext} & \\texttt{portletContext} & - & all \\\\\n\\texttt{PortletMode} & \\texttt{portletMode} & - & all \\\\\n\\texttt{WindowState} & \\texttt{windowState} & - & all \\\\\n\\texttt{PortletPreferences} & \\texttt{portletPreferences} & - & all \\\\\n\\texttt{Cookies(List\\textless{}Cookie\\textgreater{})} & \\texttt{cookies}\n& - & all \\\\\n\\texttt{PortletSession} & \\texttt{portletSession} & - & all \\\\\n\\texttt{Locales(List\\textless{}Locale\\textgreater{})} & \\texttt{locales}\n& - & all \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Dependent Scoped Beans}\\label{dependent-scoped-beans}\n\nThese beans use the \\texttt{@Dependent} scope. They're of type\n\\texttt{java.lang.String}, which is \\texttt{final}. This disqualifies\nthem from being proxied. To prevent using dependent scoped beans in a\nscope broader than their original scope, you should only inject them\ninto \\texttt{@PortletRequestScoped} beans.\n\nTable 2: Dependent Scoped Beans\\footnote{Martin Scott Nicklous, Java™\n  Portlet Specification 3.0, page 123.}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}llll@{}}\n\\toprule\\noalign{}\nArtifact & Bean EL Name & Qualifier & Valid during \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\texttt{Namespace} (String) & \\texttt{namespace} & \\texttt{@Namespace} &\nall \\\\\n\\texttt{ContextPath} (String) & \\texttt{contextPath} &\n\\texttt{@ContextPath} & all \\\\\n\\texttt{WindowID} (String) & \\texttt{windowId} & \\texttt{@WindowId} &\nall \\\\\n\\texttt{Portlet\\ name} (String) & \\texttt{portletName} &\n\\texttt{@PortletName} & all \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Related Topics}\\label{related-topics-49}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/cdi-dependency-injection}{CDI\nDependency Injection}\n\n\\chapter{Item Selector Criterion and Return\nTypes}\\label{item-selector-criterion-and-return-types}\n\nLiferay DXP contains Item Selector criterion\n(\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/ItemSelectorCriterion.html}{\\texttt{ItemSelectorCriterion}})\nand return type\n(\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/ItemSelectorReturnType.html}{\\texttt{ItemSelectorReturnType}})\nclasses that developers can use in Item Selectors. The following\nsections in this document list the available classes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[item-selector-criterion-classes]{Criterion Classes}\n\\item\n  \\hyperref[item-selector-return-type-classes]{Return Type Classes}\n\\end{itemize}\n\nIf there isn't a criterion or return type for your needs, you can create\nyour own by following the instructions in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-criterion-and-return-types}{Creating\nCustom Criterion and Return Types}. For more information on Item\nSelectors in general, including definitions of criterion and return\ntypes, see the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/item-selector}{Item\nSelector introduction}.\n\n\\section{Item Selector Criterion\nClasses}\\label{item-selector-criterion-classes}\n\n\\textbf{Assets:}\n\n\\href{https://docs.liferay.com/dxp/apps/asset/latest/javadocs/com/liferay/asset/display/page/item/selector/criterion/AssetDisplayPageSelectorCriterion.html}{AssetDisplayPageSelectorCriterion}:\nAsset display page.\n\n\\textbf{Blogs:}\n\n\\href{https://docs.liferay.com/dxp/apps/blogs/latest/javadocs/com/liferay/blogs/item/selector/criterion/BlogsItemSelectorCriterion.html}{BlogsItemSelectorCriterion}:\nBlogs item.\n\n\\textbf{Page Fragments:}\n\n\\href{https://docs.liferay.com/dxp/apps/fragment/latest/javadocs/com/liferay/fragment/item/selector/criterion/FragmentItemSelectorCriterion.html}{FragmentItemSelectorCriterion}:\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/page-fragments}{Page\nfragment}.\n\n\\textbf{Item Selector:}\n\n\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/criteria/audio/criterion/AudioItemSelectorCriterion.html}{AudioItemSelectorCriterion}:\nAudio file.\n\n\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/criteria/file/criterion/FileItemSelectorCriterion.html}{FileItemSelectorCriterion}:\nDocument Library file.\n\n\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/criteria/image/criterion/ImageItemSelectorCriterion.html}{ImageItemSelectorCriterion}:\nImage file.\n\n\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/criteria/upload/criterion/UploadItemSelectorCriterion.html}{UploadItemSelectorCriterion}:\nUploadable file.\n\n\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/criteria/url/criterion/URLItemSelectorCriterion.html}{URLItemSelectorCriterion}:\nURL.\n\n\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/criteria/video/criterion/VideoItemSelectorCriterion.html}{VideoItemSelectorCriterion}:\nVideo file.\n\n\\textbf{Journal (Web Content):}\n\n\\href{https://docs.liferay.com/dxp/apps/journal/latest/javadocs/com/liferay/journal/item/selector/criterion/JournalItemSelectorCriterion.html}{JournalItemSelectorCriterion}:\nWeb content article.\n\n\\textbf{Knowledge Base:}\n\n\\href{https://docs.liferay.com/dxp/apps/knowledge-base/latest/javadocs/com/liferay/knowledge/base/item/selector/criterion/KBAttachmentItemSelectorCriterion.html}{KBAttachmentItemSelectorCriterion}:\nKnowledge base attachment.\n\n\\textbf{Layout:}\n\n\\href{https://docs.liferay.com/dxp/apps/layout/latest/javadocs/com/liferay/layout/item/selector/criterion/LayoutItemSelectorCriterion.html}{LayoutItemSelectorCriterion}:\nPage layout.\n\n\\textbf{Organizations:}\n\n\\href{https://docs.liferay.com/dxp/apps/organizations/latest/javadocs/com/liferay/organizations/item/selector/OrganizationItemSelectorCriterion.html}{OrganizationItemSelectorCriterion}:\nOrganization.\n\n\\textbf{Roles:}\n\n\\href{https://docs.liferay.com/dxp/apps/roles/latest/javadocs/com/liferay/roles/item/selector/RoleItemSelectorCriterion.html}{RoleItemSelectorCriterion}:\nRole.\n\n\\textbf{Site Navigation:}\n\n\\href{https://docs.liferay.com/dxp/apps/site-navigation/latest/javadocs/com/liferay/site/navigation/item/selector/criterion/SiteNavigationMenuItemItemSelectorCriterion.html}{SiteNavigationMenuItemItemSelectorCriterion}:\nSite navigation menu item.\n\n\\href{https://docs.liferay.com/dxp/apps/site-navigation/latest/javadocs/com/liferay/site/navigation/item/selector/criterion/SiteNavigationMenuItemSelectorCriterion.html}{SiteNavigationMenuItemSelectorCriterion}:\nSite navigation menu.\n\n\\textbf{Sites:}\n\n\\href{https://docs.liferay.com/dxp/apps/site/latest/javadocs/com/liferay/site/item/selector/criterion/SiteItemSelectorCriterion.html}{SiteItemSelectorCriterion}:\nSite.\n\n\\textbf{User Groups Admin:}\n\n\\href{https://docs.liferay.com/dxp/apps/user-groups-admin/latest/javadocs/com/liferay/user/groups/admin/item/selector/UserGroupItemSelectorCriterion.html}{UserGroupItemSelectorCriterion}:\nUser group.\n\n\\textbf{Users Admin:}\n\n\\href{https://docs.liferay.com/dxp/apps/users-admin/latest/javadocs/com/liferay/users/admin/item/selector/UserItemSelectorCriterion.html}{UserItemSelectorCriterion}:\nUser.\n\n\\textbf{Wiki:}\n\n\\href{https://docs.liferay.com/dxp/apps/wiki/latest/javadocs/com/liferay/wiki/item/selector/criterion/WikiAttachmentItemSelectorCriterion.html}{WikiAttachmentItemSelectorCriterion}:\nWiki attachment.\n\n\\href{https://docs.liferay.com/dxp/apps/wiki/latest/javadocs/com/liferay/wiki/item/selector/criterion/WikiPageItemSelectorCriterion.html}{WikiPageItemSelectorCriterion}:\nWiki page.\n\n\\section{Item Selector Return Type\nClasses}\\label{item-selector-return-type-classes}\n\n\\textbf{Adaptive Media:}\n\n\\href{https://docs.liferay.com/dxp/apps/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/item/selector/AMImageFileEntryItemSelectorReturnType.html}{AMImageFileEntryItemSelectorReturnType}:\nAdaptive Media image file.\n\n\\href{https://docs.liferay.com/dxp/apps/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/item/selector/AMImageURLItemSelectorReturnType.html}{AMImageURLItemSelectorReturnType}:\nAdaptive Media image URL.\n\n\\textbf{Item Selector:}\n\n\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/criteria/Base64ItemSelectorReturnType.html}{Base64ItemSelectorReturnType}:\nThe entity's Base64 encoding as a \\texttt{String}.\n\n\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/criteria/DownloadURLItemSelectorReturnType.html}{DownloadURLItemSelectorReturnType}:\nThe entity's download URL as a \\texttt{String}.\n\n\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/criteria/FileEntryItemSelectorReturnType.html}{FileEntryItemSelectorReturnType}:\nFile entry information as a JSON object.\n\n\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/criteria/URLItemSelectorReturnType.html}{URLItemSelectorReturnType}:\nThe entity's URL as a \\texttt{String}.\n\n\\href{https://docs.liferay.com/dxp/apps/item-selector/latest/javadocs/com/liferay/item/selector/criteria/UUIDItemSelectorReturnType.html}{UUIDItemSelectorReturnType}:\nThe entity's universally unique identifier (UUID) as a \\texttt{String}.\n\n\\textbf{Site:}\n\n\\href{https://docs.liferay.com/dxp/apps/site/latest/javadocs/com/liferay/site/item/selector/criteria/SiteItemSelectorReturnType.html}{SiteItemSelectorReturnType}:\nThe Site's information as a JSON object.\n\n\\textbf{Wiki:}\n\n\\href{https://docs.liferay.com/dxp/apps/wiki/latest/javadocs/com/liferay/wiki/item/selector/WikiPageTitleItemSelectorReturnType.html}{WikiPageTitleItemSelectorReturnType}:\nThe wiki page's title.\n\n\\href{https://docs.liferay.com/dxp/apps/wiki/latest/javadocs/com/liferay/wiki/item/selector/WikiPageURLItemSelectorReturnType.html}{WikiPageURLItemSelectorReturnType}:\nThe wiki page's URL.\n\n\\chapter{Java APIs}\\label{java-apis}\n\nHere you'll find Javadoc for Liferay DXP and Liferay DXP apps. Note that\neach link to the Javadoc listed here opens in a new window.\n\nFor help finding module attributes and configuring dependencies, see\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Configuring\nDependencies}.\n\n\\section{7.0 Java APIs}\\label{java-apis-1}\n\nThis table contains links to the Javadoc for 7.0 API modules. The root\nlocation for these modules' Javadoc is\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/}{here}.\n\nCore\n\ncom.liferay.portal.kernel (portal-kernel): ~for developing applications\non Liferay DXP\n\ncom.liferay.util.bridges (util-bridges): ~for using various\nnon-proprietary computing languages, frameworks, and utilities on\nLiferay DXP\n\ncom.liferay.util.java (util-java): ~for using various Java-related\nframeworks and utilities on Liferay DXP\n\ncom.liferay.util.slf4j (util-slf4j): ~for using the Simple Logging\nFacade for Java (SLF4J)\n\ncom.liferay.portal.impl (portal-impl): ~refer to this only if you are an\nadvanced Liferay developer that needs a deeper understanding of 7.0's\nimplementation in order to contribute to it\n\n\\section{Liferay DXP App Java APIs}\\label{liferay-dxp-app-java-apis}\n\nThe tables in this section link to the API modules for apps in these\ncategories:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[collaboration]{Collaboration}\n\\item\n  \\hyperref[forms-and-workflow]{Forms and Workflow}\n\\item\n  \\hyperref[foundation]{Foundation}\n\\item\n  \\hyperref[web-experience]{Web Experience}\n\\end{itemize}\n\nNote that the root location for these modules' Javadoc is\n{[}https://docs.liferay.com/dxp/apps{]}( \\#\\# Collaboration\n\nAnnouncements\n\ncom.liferay.announcements.api\n\nBlogs\n\ncom.liferay.blogs.api\n\ncom.liferay.blogs.item.selector.api\n\ncom.liferay.blogs.recent.bloggers.api\n\nComment\n\ncom.liferay.comment.api\n\nDocument Library\n\ncom.liferay.document.library.api\n\ncom.liferay.document.library.content.api\n\ncom.liferay.document.library.file.rank.api\n\ncom.liferay.document.library.repository.authorization.api\n\ncom.liferay.document.library.repository.cmis.api\n\ncom.liferay.document.library.repository.external.api\n\ncom.liferay.document.library.sync.api\n\nFlags\n\ncom.liferay.flags.api\n\nInvitation\n\ncom.liferay.invitation.invite.members.api\n\nItem Selector\n\ncom.liferay.item.selector.api\n\ncom.liferay.item.selector.criteria.api\n\nMentions\n\ncom.liferay.mentions.api\n\nMessage Boards\n\ncom.liferay.message.boards.api\n\nRatings\n\ncom.liferay.ratings.api\n\nReading Time\n\ncom.liferay.reading.time.api\n\nSocial\n\ncom.liferay.social.activities.api\n\ncom.liferay.social.activity.api\n\ncom.liferay.social.bookmarks.api\n\ncom.liferay.social.user.statistics.api\n\nSubscription\n\ncom.liferay.subscription.api\n\nUpload\n\ncom.liferay.upload.api\n\nWiki\n\ncom.liferay.wiki.api\n\n\\section{Forms and Workflow}\\label{forms-and-workflow}\n\nCalendar\n\ncom.liferay.calendar.api\n\nDynamic Data Lists\n\ncom.liferay.dynamic.data.lists.api\n\nDynamic Data Mapping\n\ncom.liferay.dynamic.data.mapping.api\n\nPolls\n\ncom.liferay.polls.api\n\nPortal Reports Engine\n\ncom.liferay.portal.reports.engine.api\n\nPortal Rules Engine\n\ncom.liferay.portal.rules.engine.api\n\nPortal Workflow\n\ncom.liferay.portal.workflow.api\n\ncom.liferay.portal.workflow.kaleo.api\n\ncom.liferay.portal.workflow.kaleo.definition.api\n\ncom.liferay.portal.workflow.kaleo.runtime.api\n\n\\section{Foundation}\\label{foundation}\n\nCaptcha\n\ncom.liferay.captcha.api\n\nConfiguration Admin\n\ncom.liferay.configuration.admin.api\n\nContacts\n\ncom.liferay.contacts.api\n\nFriendly URL\n\ncom.liferay.friendly.url.api\n\nFrontend Editor\n\ncom.liferay.frontend.editor.api\n\nFrontend Image Editor\n\ncom.liferay.frontend.image.editor.api\n\nFrontend JS\n\ncom.liferay.frontend.js.loader.modules.extender.api\n\nMap\n\ncom.liferay.map.api\n\nMobile Device Rules\n\ncom.liferay.mobile.device.rules.api\n\nOrganizations\n\ncom.liferay.organizations.api\n\ncom.liferay.organizations.item.selector.api\n\nPassword Policies Admin\n\ncom.liferay.password.policies.admin.api\n\nPortal Background Task\n\ncom.liferay.portal.background.task.api\n\nPortal Cache\n\ncom.liferay.portal.cache.api\n\nPortal Configuration\n\ncom.liferay.portal.configuration.upgrade.api\n\nPortal Instances\n\ncom.liferay.portal.instances.api\n\nPortal Lock\n\ncom.liferay.portal.lock.api\n\nPortal Remote\n\ncom.liferay.portal.remote.soap.extender.api\n\nPortal Scripting\n\ncom.liferay.portal.scripting.api\n\nPortal Search\n\ncom.liferay.portal.search.api\n\ncom.liferay.portal.search.engine.adapter.api\n\ncom.liferay.portal.search.web.api\n\nPortal Security Audit\n\ncom.liferay.portal.security.audit.api\n\ncom.liferay.portal.security.audit.event.generators.api\n\ncom.liferay.portal.security.audit.storage.api\n\nPortal Security SSO\n\ncom.liferay.portal.security.sso.cas.api\n\ncom.liferay.portal.security.sso.facebook.connect.api\n\ncom.liferay.portal.security.sso.ntlm.api\n\ncom.liferay.portal.security.sso.openid.api\n\ncom.liferay.portal.security.sso.openid.connect.api\n\ncom.liferay.portal.security.sso.opensso.api\n\ncom.liferay.portal.security.sso.token.api\n\nPortal Security\n\ncom.liferay.portal.security.exportimport.api\n\ncom.liferay.portal.security.ldap.api\n\ncom.liferay.portal.security.permission.api\n\ncom.liferay.portal.security.service.access.policy.api\n\ncom.liferay.portal.security.service.access.quota.api\n\nPortal Security SSO Google\n\ncom.liferay.portal.security.sso.google.api\n\nPortal Settings\n\ncom.liferay.portal.settings.api\n\nPortal Template\n\ncom.liferay.portal.template.soy.api\n\nPortal URL Builder\n\ncom.liferay.portal.url.builder.api\n\nPortal\n\ncom.liferay.portal.custom.jsp.bag.api\n\ncom.liferay.portal.dao.orm.custom.sql.api\n\ncom.liferay.portal.instance.lifecycle.api\n\ncom.liferay.portal.jmx.api\n\ncom.liferay.portal.output.stream.container.api\n\ncom.liferay.portal.spring.extender.api\n\ncom.liferay.portal.upgrade.api\n\nRoles\n\ncom.liferay.roles.admin.api\n\ncom.liferay.roles.item.selector.api\n\nText Localizer\n\ncom.liferay.text.localizer.address.api\n\nUser-associated Data\n\ncom.liferay.user.associated.data.api\n\nUser Groups Admin\n\ncom.liferay.user.groups.admin.api\n\ncom.liferay.user.groups.admin.item.selector.api\n\nUsers Admin\n\ncom.liferay.users.admin.api\n\ncom.liferay.users.admin.item.selector.api\n\nXStream\n\ncom.liferay.xstream.configurator.api\n\n\\section{Web Experience}\\label{web-experience}\n\nApplication List\n\ncom.liferay.application.list.api\n\nAsset\n\ncom.liferay.asset.api\n\ncom.liferay.asset.categories.navigation.api\n\ncom.liferay.asset.category.property.api\n\ncom.liferay.asset.display.api\n\ncom.liferay.asset.display.page.api\n\ncom.liferay.asset.display.page.item.selector.api\n\ncom.liferay.asset.entry.rel.api\n\ncom.liferay.asset.publisher.api\n\ncom.liferay.asset.tag.stats.api\n\ncom.liferay.asset.tags.api\n\ncom.liferay.asset.tags.navigation.api\n\nExport Import\n\ncom.liferay.exportimport.api\n\ncom.liferay.exportimport.changeset.api\n\nFragment\n\ncom.liferay.fragment.api\n\ncom.liferay.fragment.item.selector.api\n\nHTML Preview\n\ncom.liferay.html.preview.api\n\nJournal\n\ncom.liferay.journal.api\n\ncom.liferay.journal.content.asset.addon.entry.api\n\ncom.liferay.journal.item.selector.api\n\nLayout\n\ncom.liferay.layout.api\n\ncom.liferay.layout.admin.api\n\ncom.liferay.layout.item.selector.api\n\ncom.liferay.layout.page.template.api\n\ncom.liferay.layout.prototype.api\n\ncom.liferay.layout.set.prototype.api\n\nPortlet Display Template\n\ncom.liferay.portlet.display.template.api\n\nProduct Navigation\n\ncom.liferay.product.navigation.control.menu.api\n\ncom.liferay.product.navigation.product.menu.api\n\ncom.liferay.product.navigation.simulation.api\n\nRSS\n\ncom.liferay.rss.api\n\nSite Navigation\n\ncom.liferay.site.navigation.api\n\ncom.liferay.site.navigation.admin.api\n\ncom.liferay.site.navigation.item.selector.api\n\ncom.liferay.site.navigation.language.api\n\nSite\n\ncom.liferay.site.api\n\ncom.liferay.site.item.selector.api\n\nStaging\n\ncom.liferay.staging.api\n\nTrash\n\ncom.liferay.trash.api\n\n\\section{JavaScript and CSS}\\label{javascript-and-css}\n\n\\href{https://liferay.design/lexicon/}{\\textbf{Lexicon}}: A system for\nbuilding applications in and outside of Liferay DXP, designed to be\nfluid and extensible, as well as provide a consistent and documented\nAPI.\n\n\\href{https://clayui.com/}{\\textbf{Clay}}: The web implementation of\nLexicon.\n\n\\href{http://getbootstrap.com/}{\\textbf{Bootstrap}}: The base CSS\nlibrary onto which Lexicon is built. Liferay DXP uses Bootstrap natively\nand all of its CSS classes and JavaScript features are available within\nportlets, templates, and themes.\n\n\\href{http://alloyui.com}{\\textbf{AlloyUI}}: AlloyUI and all of its\nJavaScript APIs are available within portlets, templates, and themes.\n\n\\section{Descriptor Definitions}\\label{descriptor-definitions}\n\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/definitions/}{\\textbf{DTDs}}:\nDescribes the XML files used in configuring Liferay DXP apps, 7.0\nplugins, and Liferay DXP 7.2.\n\n\\chapter{Meaningful Schema\nVersioning}\\label{meaningful-schema-versioning}\n\nLiferay's data schema version convention communicates a schema's\ncompatibility with older versions of the software. It tells you whether\na schema's changes maintain or break compatibility with existing\nsoftware. For example, if a new data schema removes a field your\nsoftware expects, the schema breaks compatibility. But if a new schema's\nchanges are non-breaking (e.g., adds a new field), the schema is\ncompatible and can be used with existing software.\n\nSince Liferay DXP 7.1, Liferay uses a meaningful schema version\nconvention (similar to \\href{http://semver.org}{Semantic Versioning}) to\ndefine new\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-an-upgrade-process-for-your-app}{upgrade\nsteps} and support rollback of schema micro versions. The schema version\ndefines the status of the database schema and its data belonging to that\nmodule or Core in a certain moment. The concept of schema versioning is\ndifferent from bundle versioning. The biggest concern in versioning a\ndata schema is \\textbf{backward-compatibility} between the new schema\nand the code that operates on the data.\n\nHere's Liferay's schema version convention:\n\n\\textbf{MAJOR.MINOR.MICRO}\n\nEach part means something:\n\n\\textbf{MAJOR:} Contains breaking schema/data changes that are\nincompatible with the previous version of the code.\n\n\\textbf{MINOR:} Contains schema/data changes compatible with the\nprevious version of the code. The changes are required for the new\nversion of the code to work (the application will fail without applying\nthe schema/data changes)\n\n\\textbf{MICRO:} Contains schema/data changes that are compatible with\nthe previous version of the code. The changes are optional.\n\nIf you're not sure what kind of schema version change represents your\nupgrade step, ask yourself these questions:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Will the previous code version (previous FP, SP, or GA) work with\n  these schema/data changes?\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    If not, it is a major change.\n  \\item\n    If yes, continue.\n  \\end{itemize}\n\\item\n  Are the schema/data changes required for the application to work?\n  (Obviously, all changes are intended to improve the application but in\n  some cases the application is fully functional without them)\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    If yes, it is a minor change.\n  \\item\n    If not, it is a micro change.\n  \\end{itemize}\n\\end{enumerate}\n\nNext are some concrete examples of micro, minor, and major changes.\n\n\\section{Micro change examples}\\label{micro-change-examples}\n\nHere are common micro changes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Increasing \\texttt{VARCHAR} field sizes.\n\\item\n  Modifying DB indexes.\n\\item\n  Modifying data values to adapt to current logic. These include\n  backwards compatible data changes only. These changes commonly occur\n  when data updates are missed for new functionalities.\n\\item\n  Converting a field from a String to a CLOB, as long as the field has\n  few records and isn't used in \\texttt{DISTINCT} or \\texttt{GROUP\\ BY}\n  SQL clauses.\n\\end{itemize}\n\n\\section{Minor change examples}\\label{minor-change-examples}\n\nHere are common minor changes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Adding a new DB field.\n\\item\n  Adding a new DB table.\n\\end{itemize}\n\n\\textbf{Important:} The changes above are major if they require\nmodifying current existing data or extract information to populate the\nnew field or table. In such cases, the data can become incorrect if you\nrolled back to the previous code version and then, after some time,\ninstalled the new code again.\n\n\\section{Major change examples}\\label{major-change-examples}\n\nHere are common major changes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Making data modifications that are not backward compatible.\n\\item\n  Removing a DB field\n\\item\n  Removing a DB table.\n\\item\n  Altering a column name.\n\\item\n  Decreasing the size of a \\texttt{VARCHAR} field.\n\\item\n  Converting a field from a String to a CLOB, where the field is has\n  many records or is used in \\texttt{DISTINCT} or \\texttt{GROUP\\ BY} SQL\n  clauses.\n\\item\n  Adding a new DB field or table that requires modifying current\n  existing data or extracts information to populate the new field or\n  table.\n\\end{itemize}\n\nNow you can ascribe meaningful versions to your module's data schemas.\n\n\\chapter{Portlet 3.0 API Opt In}\\label{portlet-3.0-api-opt-in}\n\nA portlet must specify version 3.0 to ``opt in'' to the Portlet 3.0 API.\nThe 3.0 Portlet API version can be specified in the following ways.\n\n\\section{\\texorpdfstring{Standard Portlet \\texttt{@PortletApplication}\nAnnotation}{Standard Portlet @PortletApplication Annotation}}\\label{standard-portlet-portletapplication-annotation}\n\nStandard portlets need only specify the\n\\href{https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/PortletApplication.html}{\\texttt{@PortletApplication}}\nannotation.\n\n\\begin{verbatim}\n@PortletApplication(version=\"3.0\") // 3.0 is the default for this annotation attribute\n@PortletConfiguration(portletName=\"myPortlet\")\npublic class MyPortlet {\n    ...\n}\n\\end{verbatim}\n\n\\section{\\texorpdfstring{Liferay MVC Portlet \\texttt{@Component}\nAnnotation}{Liferay MVC Portlet @Component Annotation}}\\label{liferay-mvc-portlet-component-annotation}\n\nDeclarative Services portlets, such as \\texttt{MVCPortlet}, can specify\nversion 3.0 in their \\texttt{@Component} annotation.\n\n\\begin{verbatim}\n@Component(properties=\"javax.portlet.version=3.0\", service=javax.portlet.Portlet.class)\npublic class MyDeclarativeServicesPortlet {\n    ...\n}\n\\end{verbatim}\n\n\\section{\\texorpdfstring{\\texttt{portlet.xml}\nDescriptor}{portlet.xml Descriptor}}\\label{portlet.xml-descriptor}\n\nAll portlets can specify version 3.0 in their \\texttt{portlet.xml}\ndescriptor.\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<portlet-app xmlns=\"http://xmlns.jcp.org/xml/ns/portlet\"\n             xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n             xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/portlet http://xmlns.jcp.org/xml/ns/portlet/portlet-app_3_0.xsd\"\n             version=\"3.0\">\n    ...\n</portlet-app>\n\\end{verbatim}\n\n\\chapter{Portlet Descriptor to OSGi Service Property\nMap}\\label{portlet-descriptor-to-osgi-service-property-map}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThis article maps portlet XML descriptor values to OSGi service\nproperties for publishing OSGi Portlets.\n\nOSGi service definitions can use properties. OSGi service properties\ncentralize and simplify portlet configuration. They are typically\nrepresented as key-value pairs or, more generally, as a Map-like object.\n\nPortlet spec property keys are prefixed by\n\n\\begin{verbatim}\njavax.portlet.\n\\end{verbatim}\n\nLiferay property keys are prefixed by\n\n\\begin{verbatim}\ncom.liferay.portlet.\n\\end{verbatim}\n\nThe mappings essentially flatten what is found in the XML descriptor.\nThe property names resemble the original descriptor names.\n\nThis article covers these descriptor mappings:\n\n\\begin{itemize}\n\\item\n  \\hyperref[portlet-descriptor-mappings]{Portlet descriptor mappings}\n\\item\n  \\hyperref[liferay-descriptor-mappings]{Liferay descriptor mappings}\n\n  \\begin{itemize}\n  \\item\n    \\hyperref[liferay-display]{From \\texttt{liferay-display.xml}}\n  \\item\n    \\hyperref[liferay-portlet]{From \\texttt{liferay-portlet.xml}}\n  \\end{itemize}\n\\end{itemize}\n\nThe standard portlet descriptor mappings are first.\n\n\\section{Portlet Descriptor Mappings}\\label{portlet-descriptor-mappings}\n\n\\textbf{Note:} XPath notation derived from the \\textbf{Portlet XSD}\n\\hyperref[four]{4} is used in this document for simplicity.\n\n\\noindent\\hrulefill\n\nportlet.xml XPath \\textbar{} OSGi Portlet Service Property\\textbar{}\n\\texttt{/portlet-app/container-runtime-option}\\textbar not\nsupported\\textbar{}\n\\texttt{/portlet-app/custom-portlet-mode}\\textbar not\nsupported\\textbar{}\n\\texttt{/portlet-app/custom-window-state}\\textbar not\nsupported\\textbar{}\n\\texttt{/portlet-app/default-namespace}\\textbar{}\\texttt{javax.portlet.default-namespace=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/event-definition}\\textbar{}\\texttt{javax.portlet.event-definition=\\textless{}QNameLocalPart\\textgreater{};\\textless{}QNameURI\\textgreater{}{[};\\textless{}PayloadType\\textgreater{}{]}{[},\\textless{}AliasQNameLocalPart\\textgreater{};\\textless{}AliasQNameURI\\textgreater{}{]}}\n\\hyperref[two]{2}\\textbar{}\n\\texttt{/portlet-app/filter}\\texttt{/portlet-app/filter/init-param/name}\\texttt{/portlet-app/filter-mapping}\\textbar{}\\hyperref[three]{3}\\texttt{javax.portlet.init-param.\\textless{}name\\textgreater{}=\\textless{}value\\textgreater{}}\n\\hyperref[three]{3}, \\hyperref[nine]{9}\\hyperref[three]{3}\\textbar{}\n\\texttt{/portlet-app/public-render-parameter}\\textbar not\nsupported\\textbar{} \\texttt{/portlet-app/resource-bundle}\\textbar not\nsupported\\textbar{}\n\\texttt{/portlet-app/security-constraint}\\textbar not\nsupported\\textbar{} \\texttt{/portlet-app/user-attribute}\\textbar not\nsupported\\textbar{}\n\\texttt{/portlet-app/version}\\textbar{}\\texttt{javax.portlet.version=\\textless{}value\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/async-supported}\\textbar{}\\texttt{javax.portlet.async-supported=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/cache-scope}\\textbar not\nsupported\\textbar{}\n\\texttt{/portlet-app/portlet/container-runtime-option}\\textbar{}\\texttt{javax.portlet.container-runtime-option.\\textless{}name\\textgreater{}=\\textless{}value\\textgreater{}}\n\\hyperref[two]{2}\\textbar{}\n\\texttt{/portlet-app/portlet/dependency}\\textbar{}\\texttt{javax.portlet.dependency=\\textless{}name\\textgreater{};\\textless{}scope\\textgreater{};\\textless{}version\\textgreater{}}\n\\hyperref[two]{2}, \\hyperref[six]{6}\\textbar{}\n\\texttt{/portlet-app/portlet/description}\\textbar{}\\texttt{javax.portlet.description=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/display-name}\\textbar{}\\texttt{javax.portlet.display-name=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/expiration-cache}\\textbar{}\\texttt{javax.portlet.expiration-cache=\\textless{}int\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/init-param/name}\\textbar{}\\texttt{javax.portlet.init-param.\\textless{}name\\textgreater{}=\\textless{}value\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/listener}\\textbar{}\\texttt{javax.portlet.listener=\\textless{}listener-class\\textgreater{};\\textless{}ordinal\\textgreater{}}\n\\hyperref[two]{2},\\hyperref[eight]{8}\\textbar{}\n\\texttt{/portlet-app/portlet/multipart-config/file-size-threshold}\\textbar{}\\texttt{javax.portlet.multipart.file-size-threshold=\\textless{}Integer\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/multipart-config/location}\\textbar{}\\texttt{javax.portlet.multipart.location=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/multipart-config/max-file-size}\\textbar{}\\texttt{javax.portlet.multipart.max-file-size=\\textless{}Long\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/multipart-config/max-request-size}\\textbar{}\\texttt{javax.portlet.multipart.max-request-size=\\textless{}Long\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/portlet-class}\\textbar{}\\hyperref[one]{1}\\textbar{}\n\\texttt{/portlet-app/portlet/portlet-info/keywords}\\textbar{}\\texttt{javax.portlet.info.keywords=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/portlet-info/short-title}\\textbar{}\\texttt{javax.portlet.info.short-title=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/portlet-info/title}\\textbar{}\\texttt{javax.portlet.info.title=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/portlet-name}\n\\hyperref[ten]{10}\\textbar{}\\texttt{javax.portlet.name=\\textless{}String\\textgreater{}}\n\\hyperref[ten]{10}\\textbar{}\n\\texttt{/portlet-app/portlet/portlet-preferences}\\textbar{}\\texttt{javax.portlet.preferences=\\textless{}String\\textgreater{}}OR\\texttt{javax.portlet.preferences=classpath:\\textless{}path\\_to\\_file\\_in\\_jar\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/portlet-preferences/preferences-validator}\\textbar{}\\texttt{javax.portlet.preferences-validator=\\textless{}String\\textgreater{}}\n\\hyperref[one]{1}\\textbar{}\n\\texttt{/portlet-app/portlet/resource-bundle}\\textbar{}\\texttt{javax.portlet.resource-bundle=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/security-role-ref}\\textbar{}\\texttt{javax.portlet.security-role-ref=\\textless{}String\\textgreater{}{[},\\textless{}String\\textgreater{}{]}}\\hyperref[two]{2}\\textbar{}\n\\texttt{/portlet-app/portlet/supported-locale}\\textbar{}\\texttt{javax.portlet.supported-locale=\\textless{}String\\textgreater{}}\n\\hyperref[two]{2}\\textbar{}\n\\texttt{/portlet-app/portlet/supported-processing-event}\\textbar{}\\texttt{javax.portlet.supported-processing-event=\\textless{}QNameLocalPart\\textgreater{}}\nOR\n\\texttt{javax.portlet.supported-processing-event=\\textless{}QNameLocalPart\\textgreater{};\\textless{}QNameURI\\textgreater{}}\n\\hyperref[two]{2}\\textbar{}\n\\texttt{/portlet-app/portlet/supported-public-render-parameter}\\textbar{}\\texttt{javax.portlet.supported-public-render-parameter=\\textless{}String\\textgreater{}}\\hyperref[two]{2}\\textbar{}\n\\texttt{/portlet-app/portlet/supported-publishing-event}\\textbar{}\\texttt{javax.portlet.supported-publishing-event=\\textless{}QNameLocalPart\\textgreater{}}\nOR\n\\texttt{javax.portlet.supported-publishing-event=\\textless{}QNameLocalPart\\textgreater{};\\textless{}QNameURI\\textgreater{}}\n\\hyperref[two]{2}\\textbar{}\n\\texttt{/portlet-app/portlet/supports/mime-type}\\textbar{}\\texttt{javax.portlet.mime-type=\\textless{}mime-type\\textgreater{}}\\textbar{}\n\\texttt{/portlet-app/portlet/supports/portlet-mode}\\textbar{}\\texttt{javax.portlet.portlet-mode=\\textless{}mime-type\\textgreater{};\\textless{}portlet-mode\\textgreater{}{[},\\textless{}portlet-mode\\textgreater{}{]}*}\\textbar{}\n\\texttt{/portlet-app/portlet/supports/window-state}\\textbar{}\\texttt{javax.portlet.window-state=\\textless{}mime-type\\textgreater{};\\textless{}window-state\\textgreater{}{[},\\textless{}window-state\\textgreater{}{]}*}\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{Liferay Descriptor Mappings}\\label{liferay-descriptor-mappings}\n\n\\section{Liferay Display}\\label{liferay-display}\n\n\\noindent\\hrulefill\n\nliferay-display.xml XPath \\textbar{} OSGi Portlet Service\nProperty\\textbar{}\n\\texttt{/display/category{[}@name{]}}\\textbar{}\\texttt{com.liferay.portlet.display-category=\\textless{}value\\textgreater{}}\\textbar{}\n\n\\noindent\\hrulefill\n\n\\section{Liferay Portlet}\\label{liferay-portlet}\n\n\\textbf{Note:} XPath notation derived from \\textbf{Liferay Portlet}\n\\hyperref[five]{5} is used in this document for simplicity.\n\n\\noindent\\hrulefill\n\nliferay-portlet.xml XPath \\textbar{} OSGi Liferay Portlet Service\nProperty\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/action-timeout}\\textbar{}\\texttt{com.liferay.portlet.action-timeout=\\textless{}int\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/action-url-redirect}\\textbar{}\\texttt{com.liferay.portlet.action-url-redirect=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/active}\\textbar{}\\texttt{com.liferay.portlet.active=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/add-default-resource}\\textbar{}\\texttt{com.liferay.portlet.add-default-resource=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/ajaxable}\\textbar{}\\texttt{com.liferay.portlet.ajaxable=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/asset-renderer-factory}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/atom-collection-adapter}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/autopropagated-parameters}\\textbar{}\\texttt{com.liferay.portlet.autopropagated-parameters=\\textless{}String\\textgreater{}}\\hyperref[two]{2}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/configuration-action-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/configuration-path}\\textbar{}\\texttt{com.liferay.portlet.configuration-path=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/control-panel-entry-category}\\textbar{}\\texttt{com.liferay.portlet.control-panel-entry-category=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/control-panel-entry-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/control-panel-entry-weight}\\textbar{}\\texttt{com.liferay.portlet.control-panel-entry-weight=\\textless{}double\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/css-class-wrapper}\\textbar{}\\texttt{com.liferay.portlet.css-class-wrapper=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/custom-attributes-display}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/ddm-display}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/facebook-integration}\\textbar{}\\texttt{com.liferay.portlet.facebook-integration=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/footer-portal-css}\\textbar{}\\texttt{com.liferay.portlet.footer-portal-css=\\textless{}String\\textgreater{}}\\hyperref[two]{2}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/footer-portal-javascript}\\textbar{}\\texttt{com.liferay.portlet.footer-portal-javascript=\\textless{}String\\textgreater{}}\\hyperref[two]{2}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/footer-portlet-css}\\textbar{}\\texttt{com.liferay.portlet.footer-portlet-css=\\textless{}String\\textgreater{}}\\hyperref[two]{2}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/footer-portlet-javascript}\\textbar{}\\texttt{com.liferay.portlet.footer-portlet-javascript=\\textless{}String\\textgreater{}}\\hyperref[two]{2}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/friendly-url-mapper-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/friendly-url-mapping}\\textbar{}\\texttt{com.liferay.portlet.friendly-url-mapping=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/friendly-url-routes}\\textbar{}\\texttt{com.liferay.portlet.friendly-url-routes=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/header-portal-css}\\textbar{}\\texttt{com.liferay.portlet.header-portal-css=\\textless{}String\\textgreater{}}\\hyperref[two]{2}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/header-portal-javascript}\\textbar{}\\texttt{com.liferay.portlet.header-portal-javascript=\\textless{}String\\textgreater{}}\\hyperref[two]{2}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/header-portlet-css}\\textbar{}\\texttt{com.liferay.portlet.header-portlet-css=\\textless{}String\\textgreater{}}\\hyperref[two]{2}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/header-portlet-javascript}\\textbar{}\\texttt{com.liferay.portlet.header-portlet-javascript=\\textless{}String\\textgreater{}}\\hyperref[two]{2}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/header-request-attribute-prefix}\\textbar{}\\texttt{com.liferay.portlet.header-request-attribute-prefix=\\textless{}String\\textgreater{}}\n\\hyperref[seven]{7}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/header-timeout}\\textbar{}\\texttt{header-timeout=\\textless{}int\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/icon}\\textbar{}\\texttt{com.liferay.portlet.icon=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/include}\\textbar not\nsupported\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/indexer-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/instanceable}\\textbar{}\\texttt{com.liferay.portlet.instanceable=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/layout-cacheable}\\textbar{}\\texttt{com.liferay.portlet.layout-cacheable=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/maximize-edit}\\textbar{}\\texttt{com.liferay.portlet.maximize-edit=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/maximize-help}\\textbar{}\\texttt{com.liferay.portlet.maximize-help=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/open-search-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/parent-struts-path}\\textbar{}\\texttt{com.liferay.portlet.parent-struts-path=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/permission-propagator}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/poller-processor-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/pop-message-listener-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/pop-up-print}\\textbar{}\\texttt{com.liferay.portlet.pop-up-print=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/portlet-data-handler-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/portlet-layout-listener-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/portlet-name}\\textbar not\nsupported\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/portlet-url-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/preferences-company-wide}\\textbar{}\\texttt{com.liferay.portlet.preferences-company-wide=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/preferences-owned-by-group}\\textbar{}\\texttt{com.liferay.portlet.preferences-owned-by-group=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/preferences-unique-per-layout}\\textbar{}\\texttt{com.liferay.portlet.preferences-unique-per-layout=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/private-request-attributes}\\textbar{}\\texttt{com.liferay.portlet.private-request-attributes=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/private-session-attributes}\\textbar{}\\texttt{com.liferay.portlet.private-session-attributes=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/remoteable}\\textbar{}\\texttt{com.liferay.portlet.remoteable=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/render-timeout}\\textbar{}\\texttt{com.liferay.portlet.render-timeout=\\textless{}int\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/render-weight}\\textbar{}\\texttt{com.liferay.portlet.render-weight=\\textless{}int\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/requires-namespaced-parameters}\\textbar{}\\texttt{com.liferay.portlet.requires-namespaced-parameters=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/restore-current-view}\\textbar{}\\texttt{com.liferay.portlet.restore-current-view=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/scheduler-entry}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/scopeable}\\textbar{}\\texttt{com.liferay.portlet.scopeable=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/show-portlet-access-denied}\\textbar{}\\texttt{com.liferay.portlet.show-portlet-access-denied=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/show-portlet-inactive}\\textbar{}\\texttt{com.liferay.portlet.show-portlet-inactive=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/single-page-application}\\textbar{}\\texttt{com.liferay.portlet.single-page-application=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/social-activity-interpreter-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/social-request-interpreter-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/social-interactions-configuration}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/staged-model-data-handler-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/struts-path}\\textbar{}\\texttt{com.liferay.portlet.struts-path=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/system}\\textbar{}\\texttt{com.liferay.portlet.system=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/template-handler}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/trash-handler}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/url-encoder-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/use-default-template}\\textbar{}\\texttt{com.liferay.portlet.use-default-template=\\textless{}boolean\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/user-notification-definitions}\\textbar not\nsupported\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/user-notification-handler-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/user-principal-strategy}\\textbar{}\\texttt{com.liferay.portlet.user-principal-strategy=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/virtual-path}\\textbar{}\\texttt{com.liferay.portlet.virtual-path=\\textless{}String\\textgreater{}}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/webdav-storage-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/webdav-storage-token}\\textbar not\nsupported\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/workflow-handler}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\\texttt{/liferay-portlet-app/portlet/xml-rpc-method-class}\\textbar{}\\hyperref[three]{3}\\textbar{}\n\n\\noindent\\hrulefill\n\n\\begin{itemize}\n\\item\n  {[}1{]} Portlets are registered as concrete objects.\n\\item\n  {[}2{]} Multiples of these properties may be used. This results in an\n  array of values.\n\\item\n  {[}3{]} This type is registered as an OSGi service.\n\\item\n  {[}4{]} https://xmlns.jcp.org/xml/ns/portlet/portlet-app\\_3\\_0.xsd\n\\item\n  {[}5{]}\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/definitions/liferay-portlet-app_7_2_0.dtd.html}{http://www.liferay.com/dtd/liferay-portlet-app\\_7\\_2\\_0.dtd}\n\\item\n  {[}6{]} Here's an example of using multiple\n  \\texttt{javax.portlet.dependency} properties.\n\n  \\emph{Old:}\n\n\\begin{verbatim}\n<portlet>\n    ...\n    <dependency>\n        <name>jquery</name>\n        <scope>com.jquery</scope>\n        <version>2.1.1</version>\n    </dependency>\n    <dependency>\n        <name>jsutil</name>\n        <scope>com.mycompany</scope>\n        <version>1.0.0</version>\n    </dependency>\n    ...\n</portlet>\n\\end{verbatim}\n\n  \\emph{New:}\n\n\\begin{verbatim}\n@Component(\n    immediate = true, property = {\n        \"javax.portlet.name=my_portlet\",\n        \"javax.portlet.display-name=my-portlet\",\n        \"javax.portlet.dependency=jquery;com.jquery;2.1.1\",\n        \"javax.portlet.dependency=jsutil;com.mycompany;1.0.0\"\n    }, service = Portlet.class\n)\npublic class MyPortlet extends GenericPortlet {\n    ...\n} \n\\end{verbatim}\n\\item\n  {[}7{]} Here's an example for the\n  \\texttt{com.liferay.portlet.header-request-attribute-prefix} property.\n\n  \\emph{Old:}\n\n\\begin{verbatim}\n<portlet>\n    ...\n    <header-request-attribute-prefix>com.mycompany</header-request-attribute-prefix>\n    ...\n</portlet>\n\\end{verbatim}\n\n  \\emph{New:}\n\n\\begin{verbatim}\n@Component(\n    immediate = true, property = {\n        \"javax.portlet.name=my_portlet\",\n        \"javax.portlet.display-name=my-portlet\",\n        \"javax.portlet.dependency=jquery;com.jquery;2.1.1\",\n        \"javax.portlet.dependency=jsutil;com.mycompany;1.0.0\",\n        \"com.liferay.portlet.header-request-attribute-prefix=com.mycompany\"\n    }, service = Portlet.class\n)\npublic class MyPortlet extends GenericPortlet {\n    ...\n} \n\\end{verbatim}\n\\item\n  {[}8{]} Here's an example for the \\texttt{javax.portlet.listener}\n  property.\n\n  \\emph{Old:}\n\n\\begin{verbatim}\n<portlet>\n    ...\n    <listener>\n        <listener-class>com.mycompany.MyPortletURLGenerationListener</listener-class>\n        <ordinal>1</ordinal>\n    </listener>\n    ...\n</portlet>\n\\end{verbatim}\n\n  \\emph{New:}\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\"javax.portlet.name=myPortlet\",\n        \"javax.portlet.listener=com.mycompany.MyPortletURLGenerationListener;1\"\n    }, service = Portlet.class\n)\npublic class MyPortlet extends GenericPortlet {\n    ...\n} \n\\end{verbatim}\n\\item\n  {[}9{]} An \\texttt{javax.portlet.init-param} property can be declared\n  like this:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\"javax.portlet.name=myPortlet\", \n        \"javax.portlet.init-param.myInitParam=1234\"},\n    service = PortletFilter.class\n)\npublic class MyFilter implements RenderFilter {\n    ...\n}\n\\end{verbatim}\n\\item\n  {[}10{]} Liferay DXP creates each portlet's ID based on the portlet's\n  name (i.e., the \\texttt{portlet-name} descriptor in\n  \\texttt{liferay-portlet.xml} or the \\texttt{javax.portlet.name} OSGi\n  service property). Dashes, periods, and spaces are allowed in the\n  portlet name, but they and all other JavaScript unsafe characters are\n  stripped from the name value that's used for the portlet ID.\n  Therefore, make your portlet name unique in light of the characters\n  that are removed. Otherwise, if you try to deploy a portlet whose ID\n  is the same as a portlet that's already deployed, your portlet\n  deployment fails and Liferay DXP logs a message like this:\n\n\\begin{verbatim}\nPortlet id [portletId] is already in use\n\\end{verbatim}\n\\end{itemize}\n\n\\chapter{Third Party Packages Portal\nExports}\\label{third-party-packages-portal-exports}\n\n{ This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe \\texttt{com.liferay.portal.bootstrap} module exports many third\nparty Java packages that can cause problems if used improperly. If your\nWAR's Gradle file, for example, uses the \\texttt{compile} scope for a\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{dependency}\nthat Liferay's OSGi runtime already provides, the dependency JAR is\nincluded in the WAR's \\texttt{WEB-INF/lib} and deployed in the resulting\nWAB, and two versions of dependency classes wind up on the classpath.\nThis can cause weird errors that are hard to debug.\n\nTo find a list of the packages exported by\n\\texttt{com.liferay.portal.bootstrap}, go to the source file\n\\texttt{modules/core/portal-bootstrap/system.packages.extra.bnd}. If you\ndon't have access to the source code, the same list (in a less\nuser-friendly format) is in the\n\\texttt{META-INF/system.packages.extra.mf} file in\n\\texttt{{[}LIFERAY\\_HOME{]}/osgi/core/com.liferay.portal.bootstrap.jar}.\nThese packages are installed and available in Liferay's OSGi runtime. If\nyour module or WAR uses one of them, specify the corresponding\ndependency as being ``provided'' (provided by Liferay DXP). Here's how\nto specify a provided dependency:\n\nMaven:\n\\texttt{\\textless{}scope\\textgreater{}provided\\textless{}/scope\\textgreater{}}\n\nGradle: \\texttt{providedCompile}\n\nNow you can safely leverage third party packages Liferay DXP provides!\n"
  },
  {
    "path": "book/developer/tutorials.aux",
    "content": "\\relax \n\\providecommand{\\transparent@use}[1]{}\n\\providecommand\\hyper@newdestlabel[2]{}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {1}Developer Tutorials}{3}{chapter.1}\\protected@file@percent }\n\\newlabel{developer-tutorials}{{1}{3}{Developer Tutorials}{chapter.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {2}Developing a Web Application}{5}{chapter.2}\\protected@file@percent }\n\\newlabel{developing-a-web-application}{{2}{5}{Developing a Web Application}{chapter.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {2.1}{\\ignorespaces It looks humble, but there's a lot of functionality packed in this application.}}{5}{figure.2.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {3}Setting Up a Development Environment}{7}{chapter.3}\\protected@file@percent }\n\\newlabel{setting-up-a-development-environment}{{3}{7}{Setting Up a Development Environment}{chapter.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {3.1}Installing a Liferay Dev Studio DXP Bundle}{7}{section.3.1}\\protected@file@percent }\n\\newlabel{installing-a-liferay-dev-studio-dxp-bundle}{{3.1}{7}{Installing a Liferay Dev Studio DXP Bundle}{section.3.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {3.2}Creating a Liferay Workspace}{8}{section.3.2}\\protected@file@percent }\n\\newlabel{creating-a-liferay-workspace}{{3.2}{8}{Creating a Liferay Workspace}{section.3.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {3.1}{\\ignorespaces By selecting \\emph  {Liferay Workspace}, you begin the process of creating a new workspace for your Liferay DXP projects.}}{8}{figure.3.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {4}Generating the Back-end}{11}{chapter.4}\\protected@file@percent }\n\\newlabel{generating-the-back-end}{{4}{11}{Generating the Back-end}{chapter.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {4.1}{\\ignorespaces Service Builder generates the shaded layers of your application.}}{11}{figure.4.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {5}What is Service Builder?}{13}{chapter.5}\\protected@file@percent }\n\\newlabel{what-is-service-builder}{{5}{13}{What is Service Builder?}{chapter.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {5.1}Guestbook Application Design}{13}{section.5.1}\\protected@file@percent }\n\\newlabel{guestbook-application-design}{{5.1}{13}{Guestbook Application Design}{section.5.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {5.1}{\\ignorespaces When you're done, the Guestbook supports multiple guestbooks and makes use of many Liferay features.}}{13}{figure.5.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {5.2}Service Layer}{14}{section.5.2}\\protected@file@percent }\n\\newlabel{service-layer}{{5.2}{14}{Service Layer}{section.5.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {5.2}{\\ignorespaces Your current project structure.}}{14}{figure.5.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {6}Generating Model, Service, and Persistence Layers}{15}{chapter.6}\\protected@file@percent }\n\\newlabel{generating-model-service-and-persistence-layers}{{6}{15}{Generating Model, Service, and Persistence Layers}{chapter.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {6.1}{\\ignorespaces The Model, Service, and Persistence Layer comprise a loose coupling design.}}{19}{figure.6.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {7}Implementing Service Methods}{21}{chapter.7}\\protected@file@percent }\n\\newlabel{implementing-service-methods}{{7}{21}{Implementing Service Methods}{chapter.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {7.1}Updating Generated Classes}{25}{section.7.1}\\protected@file@percent }\n\\newlabel{updating-generated-classes}{{7.1}{25}{Updating Generated Classes}{section.7.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {8}Building the Web Front-End}{27}{chapter.8}\\protected@file@percent }\n\\newlabel{building-the-web-front-end}{{8}{27}{Building the Web Front-End}{chapter.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {9}Creating the Web Project}{29}{chapter.9}\\protected@file@percent }\n\\newlabel{creating-the-web-project}{{9}{29}{Creating the Web Project}{chapter.9}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {9.1}{\\ignorespaces Complete the New Module Project wizard.}}{30}{figure.9.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {9.2}{\\ignorespaces After you move it, all of your modules are in the same folder..}}{31}{figure.9.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {9.1}What is a Portlet?}{31}{section.9.1}\\protected@file@percent }\n\\newlabel{what-is-a-portlet}{{9.1}{31}{What is a Portlet?}{section.9.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {9.2}What is a Component?}{31}{section.9.2}\\protected@file@percent }\n\\newlabel{what-is-a-component}{{9.2}{31}{What is a Component?}{section.9.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {9.3}{\\ignorespaces Many Liferay applications can run at the same time on the same page.}}{32}{figure.9.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {9.3}Deploying the Application}{32}{section.9.3}\\protected@file@percent }\n\\newlabel{deploying-the-application}{{9.3}{32}{Deploying the Application}{section.9.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {9.4}{\\ignorespaces Drag and drop the module.}}{34}{figure.9.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {9.5}{\\ignorespaces This is your new page with the Guestbook application that you created.}}{35}{figure.9.5}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {10}Defining the Component Metadata Properties}{37}{chapter.10}\\protected@file@percent }\n\\newlabel{defining-the-component-metadata-properties}{{10}{37}{Defining the Component Metadata Properties}{chapter.10}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {10.1}{\\ignorespaces Users choose applications from a list of display categories.}}{38}{figure.10.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {11}Creating Portlet Keys}{41}{chapter.11}\\protected@file@percent }\n\\newlabel{creating-portlet-keys}{{11}{41}{Creating Portlet Keys}{chapter.11}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {12}Integrating the Back-end}{43}{chapter.12}\\protected@file@percent }\n\\newlabel{integrating-the-back-end}{{12}{43}{Integrating the Back-end}{chapter.12}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {13}Creating an Add Entry Button}{45}{chapter.13}\\protected@file@percent }\n\\newlabel{creating-an-add-entry-button}{{13}{45}{Creating an Add Entry Button}{chapter.13}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {13.1}{\\ignorespaces Your new button is awesome, but it doesn't work yet.}}{46}{figure.13.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {14}Generating Portlet URLs}{47}{chapter.14}\\protected@file@percent }\n\\newlabel{generating-portlet-urls}{{14}{47}{Generating Portlet URLs}{chapter.14}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {15}Linking to Another Page}{49}{chapter.15}\\protected@file@percent }\n\\newlabel{linking-to-another-page}{{15}{49}{Linking to Another Page}{chapter.15}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {16}Forms and Action URLs}{51}{chapter.16}\\protected@file@percent }\n\\newlabel{forms-and-action-urls}{{16}{51}{Forms and Action URLs}{chapter.16}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {16.1}Action URLs}{51}{section.16.1}\\protected@file@percent }\n\\newlabel{action-urls}{{16.1}{51}{Action URLs}{section.16.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {16.2}Forms}{51}{section.16.2}\\protected@file@percent }\n\\newlabel{forms}{{16.2}{51}{Forms}{section.16.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {16.1}{\\ignorespaces This is the Guestbook application's form for adding entries.}}{52}{figure.16.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {17}Implementing Portlet Actions}{53}{chapter.17}\\protected@file@percent }\n\\newlabel{implementing-portlet-actions}{{17}{53}{Implementing Portlet Actions}{chapter.17}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {17.1}Creating an Add Entry Action}{53}{section.17.1}\\protected@file@percent }\n\\newlabel{creating-an-add-entry-action}{{17.1}{53}{Creating an Add Entry Action}{section.17.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {17.2}Creating a Delete Entry Action}{55}{section.17.2}\\protected@file@percent }\n\\newlabel{creating-a-delete-entry-action}{{17.2}{55}{Creating a Delete Entry Action}{section.17.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {18}Displaying Guestbook Entries}{57}{chapter.18}\\protected@file@percent }\n\\newlabel{displaying-guestbook-entries}{{18}{57}{Displaying Guestbook Entries}{chapter.18}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {18.1}Rendering the Portlet}{57}{section.18.1}\\protected@file@percent }\n\\newlabel{rendering-the-portlet}{{18.1}{57}{Rendering the Portlet}{section.18.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {18.2}Displaying Guestbook Entries}{58}{section.18.2}\\protected@file@percent }\n\\newlabel{displaying-guestbook-entries-1}{{18.2}{58}{Displaying Guestbook Entries}{section.18.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {18.3}Creating an Actions JSP}{60}{section.18.3}\\protected@file@percent }\n\\newlabel{creating-an-actions-jsp}{{18.3}{60}{Creating an Actions JSP}{section.18.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {18.1}{\\ignorespaces You have a form to enter information.}}{61}{figure.18.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {18.2}{\\ignorespaces Submitted entries are displayed here.}}{61}{figure.18.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {19}Fitting it All Together}{63}{chapter.19}\\protected@file@percent }\n\\newlabel{fitting-it-all-together}{{19}{63}{Fitting it All Together}{chapter.19}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {19.1}The Back-End}{63}{section.19.1}\\protected@file@percent }\n\\newlabel{the-back-end}{{19.1}{63}{The Back-End}{section.19.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {19.1}{\\ignorespaces Service Builder makes generating your database entities and your Java objects a snap.}}{63}{figure.19.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {19.2}The Front-End}{64}{section.19.2}\\protected@file@percent }\n\\newlabel{the-front-end}{{19.2}{64}{The Front-End}{section.19.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {19.3}Deploying and Testing the Application}{64}{section.19.3}\\protected@file@percent }\n\\newlabel{deploying-and-testing-the-application}{{19.3}{64}{Deploying and Testing the Application}{section.19.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {19.4}What's Next?}{64}{section.19.4}\\protected@file@percent }\n\\newlabel{whats-next}{{19.4}{64}{What's Next?}{section.19.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {19.2}{\\ignorespaces The controller directs page flow in an MVC Portlet application.}}{65}{figure.19.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {19.3}{\\ignorespaces Your first guestbook and entry appears. Nice job!}}{66}{figure.19.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {20}Writing an Administrative Portlet}{67}{chapter.20}\\protected@file@percent }\n\\newlabel{writing-an-administrative-portlet}{{20}{67}{Writing an Administrative Portlet}{chapter.20}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {20.1}{\\ignorespaces The Guestbook Admin portlet lets administrators manage Guestbooks.}}{67}{figure.20.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {21}Creating the Classes}{69}{chapter.21}\\protected@file@percent }\n\\newlabel{creating-the-classes}{{21}{69}{Creating the Classes}{chapter.21}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {21.1}Panels and Categories}{69}{section.21.1}\\protected@file@percent }\n\\newlabel{panels-and-categories}{{21.1}{69}{Panels and Categories}{section.21.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {21.1}{\\ignorespaces The product menu is split into three sections: the Control Panel, the User menu, and the Sites menu.}}{70}{figure.21.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {22}Adding Metadata}{71}{chapter.22}\\protected@file@percent }\n\\newlabel{adding-metadata}{{22}{71}{Adding Metadata}{chapter.22}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {23}Updating Your Service Layer}{75}{chapter.23}\\protected@file@percent }\n\\newlabel{updating-your-service-layer}{{23}{75}{Updating Your Service Layer}{chapter.23}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {23.1}Adding Guestbook Service Methods}{75}{section.23.1}\\protected@file@percent }\n\\newlabel{adding-guestbook-service-methods}{{23.1}{75}{Adding Guestbook Service Methods}{section.23.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {24}Defining Portlet Actions}{77}{chapter.24}\\protected@file@percent }\n\\newlabel{defining-portlet-actions}{{24}{77}{Defining Portlet Actions}{chapter.24}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {24.1}Adding Three Portlet Actions}{77}{section.24.1}\\protected@file@percent }\n\\newlabel{adding-three-portlet-actions}{{24.1}{77}{Adding Three Portlet Actions}{section.24.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {25}Adding Tabs to the Guestbook Portlet}{81}{chapter.25}\\protected@file@percent }\n\\newlabel{adding-tabs-to-the-guestbook-portlet}{{25}{81}{Adding Tabs to the Guestbook Portlet}{chapter.25}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {25.1}{\\ignorespaces Users can click a tab to choose which Guestbook to sign.}}{81}{figure.25.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {26}Creating a User Interface}{83}{chapter.26}\\protected@file@percent }\n\\newlabel{creating-a-user-interface}{{26}{83}{Creating a User Interface}{chapter.26}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {26.1}Step 1: Creating the Default View}{83}{section.26.1}\\protected@file@percent }\n\\newlabel{step-1-creating-the-default-view}{{26.1}{83}{Step 1: Creating the Default View}{section.26.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {26.2}Step 2: Creating the Actions Button}{84}{section.26.2}\\protected@file@percent }\n\\newlabel{step-2-creating-the-actions-button}{{26.2}{84}{Step 2: Creating the Actions Button}{section.26.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {26.3}Step 3: Creating the Edit Guestbook JSP}{86}{section.26.3}\\protected@file@percent }\n\\newlabel{step-3-creating-the-edit-guestbook-jsp}{{26.3}{86}{Step 3: Creating the Edit Guestbook JSP}{section.26.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {26.1}{\\ignorespaces The Guestbook Admin portlet can add or edit guestbooks, configure their permissions, or delete them.}}{87}{figure.26.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {27}Displaying Messages and Errors}{89}{chapter.27}\\protected@file@percent }\n\\newlabel{displaying-messages-and-errors}{{27}{89}{Displaying Messages and Errors}{chapter.27}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {27.1}{\\ignorespaces You can use Liferay's APIs to display helpful messages.}}{89}{figure.27.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {28}Creating Language Keys}{91}{chapter.28}\\protected@file@percent }\n\\newlabel{creating-language-keys}{{28}{91}{Creating Language Keys}{chapter.28}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {29}Adding Failure and Success Messages}{93}{chapter.29}\\protected@file@percent }\n\\newlabel{adding-failure-and-success-messages}{{29}{93}{Adding Failure and Success Messages}{chapter.29}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {30}Adding Messages to JSPs}{95}{chapter.30}\\protected@file@percent }\n\\newlabel{adding-messages-to-jsps}{{30}{95}{Adding Messages to JSPs}{chapter.30}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {30.1}{\\ignorespaces Now the message displays the value you specified in \\texttt  {Language.properties}.}}{95}{figure.30.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {31}Using Resources and Permissions}{97}{chapter.31}\\protected@file@percent }\n\\newlabel{using-resources-and-permissions}{{31}{97}{Using Resources and Permissions}{chapter.31}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {32}Defining Permissions}{99}{chapter.32}\\protected@file@percent }\n\\newlabel{defining-permissions}{{32}{99}{Defining Permissions}{chapter.32}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {32.1}{\\ignorespaces Portlet permissions and resource permissions cover different parts of the application.}}{100}{figure.32.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {32.1}Defining Model Permissions}{101}{section.32.1}\\protected@file@percent }\n\\newlabel{defining-model-permissions}{{32.1}{101}{Defining Model Permissions}{section.32.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {32.2}Defining Portlet Permissions}{103}{section.32.2}\\protected@file@percent }\n\\newlabel{defining-portlet-permissions}{{32.2}{103}{Defining Portlet Permissions}{section.32.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {33}Registering Your Permissions in the Database}{105}{chapter.33}\\protected@file@percent }\n\\newlabel{registering-your-permissions-in-the-database}{{33}{105}{Registering Your Permissions in the Database}{chapter.33}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {33.1}Registering Guestbook Resources}{105}{section.33.1}\\protected@file@percent }\n\\newlabel{registering-guestbook-resources}{{33.1}{105}{Registering Guestbook Resources}{section.33.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {33.2}Registering Guestbook Entry Resources}{106}{section.33.2}\\protected@file@percent }\n\\newlabel{registering-guestbook-entry-resources}{{33.2}{106}{Registering Guestbook Entry Resources}{section.33.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {34}Registering Permissions with the Container}{107}{chapter.34}\\protected@file@percent }\n\\newlabel{registering-permissions-with-the-container}{{34}{107}{Registering Permissions with the Container}{chapter.34}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {35}Assigning Permissions to Resources}{111}{chapter.35}\\protected@file@percent }\n\\newlabel{assigning-permissions-to-resources}{{35}{111}{Assigning Permissions to Resources}{chapter.35}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {35.1}Creating a Guestbook Portlet Permission Helper}{111}{section.35.1}\\protected@file@percent }\n\\newlabel{creating-a-guestbook-portlet-permission-helper}{{35.1}{111}{Creating a Guestbook Portlet Permission Helper}{section.35.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {35.2}Creating Model Permission Helpers}{112}{section.35.2}\\protected@file@percent }\n\\newlabel{creating-model-permission-helpers}{{35.2}{112}{Creating Model Permission Helpers}{section.35.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {36}Checking for Permission in JSPs}{115}{chapter.36}\\protected@file@percent }\n\\newlabel{checking-for-permission-in-jsps}{{36}{115}{Checking for Permission in JSPs}{chapter.36}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {36.1}Checking Permissions in the UI}{115}{section.36.1}\\protected@file@percent }\n\\newlabel{checking-permissions-in-the-ui}{{36.1}{115}{Checking Permissions in the UI}{section.36.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {36.2}Testing the Application}{117}{section.36.2}\\protected@file@percent }\n\\newlabel{testing-the-application}{{36.2}{117}{Testing the Application}{section.36.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {37}Search and Indexing}{119}{chapter.37}\\protected@file@percent }\n\\newlabel{search-and-indexing}{{37}{119}{Search and Indexing}{chapter.37}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {37.1}{\\ignorespaces Add a search bar so users can search for Guestbook Entries. If a message or name matches the search query, the Entry is displayed in the search results.}}{120}{figure.37.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {38}Enabling Search and Indexing for Guestbooks}{121}{chapter.38}\\protected@file@percent }\n\\newlabel{enabling-search-and-indexing-for-guestbooks}{{38}{121}{Enabling Search and Indexing for Guestbooks}{chapter.38}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {39}Understanding Search and Indexing}{123}{chapter.39}\\protected@file@percent }\n\\newlabel{understanding-search-and-indexing}{{39}{123}{Understanding Search and Indexing}{chapter.39}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {40}Registering Guestbooks with the Search Framework}{125}{chapter.40}\\protected@file@percent }\n\\newlabel{registering-guestbooks-with-the-search-framework}{{40}{125}{Registering Guestbooks with the Search Framework}{chapter.40}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {41}Indexing Guestbooks}{127}{chapter.41}\\protected@file@percent }\n\\newlabel{indexing-guestbooks}{{41}{127}{Indexing Guestbooks}{chapter.41}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {41.1}Implementing \\texttt  {ModelDocumentContributor}}{127}{section.41.1}\\protected@file@percent }\n\\newlabel{implementing-modeldocumentcontributor}{{41.1}{127}{\\texorpdfstring {Implementing \\texttt {ModelDocumentContributor}}{Implementing ModelDocumentContributor}}{section.41.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {41.2}Implementing \\texttt  {ModelIndexerWriterContributor}}{128}{section.41.2}\\protected@file@percent }\n\\newlabel{implementing-modelindexerwritercontributor}{{41.2}{128}{\\texorpdfstring {Implementing \\texttt {ModelIndexerWriterContributor}}{Implementing ModelIndexerWriterContributor}}{section.41.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {42}Querying for Guestbook Documents}{131}{chapter.42}\\protected@file@percent }\n\\newlabel{querying-for-guestbook-documents}{{42}{131}{Querying for Guestbook Documents}{chapter.42}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {42.1}Implementing \\texttt  {KeywordQueryContributor}}{131}{section.42.1}\\protected@file@percent }\n\\newlabel{implementing-keywordquerycontributor}{{42.1}{131}{\\texorpdfstring {Implementing \\texttt {KeywordQueryContributor}}{Implementing KeywordQueryContributor}}{section.42.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {43}Generating Results Summaries}{133}{chapter.43}\\protected@file@percent }\n\\newlabel{generating-results-summaries}{{43}{133}{Generating Results Summaries}{chapter.43}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {44}Handling Indexing in the Guestbook Service Layer}{135}{chapter.44}\\protected@file@percent }\n\\newlabel{handling-indexing-in-the-guestbook-service-layer}{{44}{135}{Handling Indexing in the Guestbook Service Layer}{chapter.44}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {45}Enabling Search and Indexing for Entries}{137}{chapter.45}\\protected@file@percent }\n\\newlabel{enabling-search-and-indexing-for-entries}{{45}{137}{Enabling Search and Indexing for Entries}{chapter.45}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {46}Registering Entries with the Search Framework}{139}{chapter.46}\\protected@file@percent }\n\\newlabel{registering-entries-with-the-search-framework}{{46}{139}{Registering Entries with the Search Framework}{chapter.46}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {47}Indexing Entries}{141}{chapter.47}\\protected@file@percent }\n\\newlabel{indexing-entries}{{47}{141}{Indexing Entries}{chapter.47}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {47.1}Implementing \\texttt  {ModelDocumentContributor}}{141}{section.47.1}\\protected@file@percent }\n\\newlabel{implementing-modeldocumentcontributor-1}{{47.1}{141}{\\texorpdfstring {Implementing \\texttt {ModelDocumentContributor}}{Implementing ModelDocumentContributor}}{section.47.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {47.2}Implementing \\texttt  {ModelIndexerWriterContributor}}{142}{section.47.2}\\protected@file@percent }\n\\newlabel{implementing-modelindexerwritercontributor-1}{{47.2}{142}{\\texorpdfstring {Implementing \\texttt {ModelIndexerWriterContributor}}{Implementing ModelIndexerWriterContributor}}{section.47.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {47.3}Implementing \\texttt  {GuestbookEntryBatchReindexer}}{143}{section.47.3}\\protected@file@percent }\n\\newlabel{implementing-guestbookentrybatchreindexer}{{47.3}{143}{\\texorpdfstring {Implementing \\texttt {GuestbookEntryBatchReindexer}}{Implementing GuestbookEntryBatchReindexer}}{section.47.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {48}Querying for Guestbook Entry Documents}{145}{chapter.48}\\protected@file@percent }\n\\newlabel{querying-for-guestbook-entry-documents}{{48}{145}{Querying for Guestbook Entry Documents}{chapter.48}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {48.1}Implementing \\texttt  {KeywordQueryContributor}}{145}{section.48.1}\\protected@file@percent }\n\\newlabel{implementing-keywordquerycontributor-1}{{48.1}{145}{\\texorpdfstring {Implementing \\texttt {KeywordQueryContributor}}{Implementing KeywordQueryContributor}}{section.48.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {49}Generating Results Summaries}{147}{chapter.49}\\protected@file@percent }\n\\newlabel{generating-results-summaries-1}{{49}{147}{Generating Results Summaries}{chapter.49}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {50}Handling Indexing in the Entry Service Layer}{149}{chapter.50}\\protected@file@percent }\n\\newlabel{handling-indexing-in-the-entry-service-layer}{{50}{149}{Handling Indexing in the Entry Service Layer}{chapter.50}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {51}Updating Your User Interface For Search}{151}{chapter.51}\\protected@file@percent }\n\\newlabel{updating-your-user-interface-for-search}{{51}{151}{Updating Your User Interface For Search}{chapter.51}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {52}Adding a Search Bar to the Guestbook Portlet}{153}{chapter.52}\\protected@file@percent }\n\\newlabel{adding-a-search-bar-to-the-guestbook-portlet}{{52}{153}{Adding a Search Bar to the Guestbook Portlet}{chapter.52}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {53}Creating a Search Results JSP for the Guestbook Portlet}{155}{chapter.53}\\protected@file@percent }\n\\newlabel{creating-a-search-results-jsp-for-the-guestbook-portlet}{{53}{155}{Creating a Search Results JSP for the Guestbook Portlet}{chapter.53}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {53.1}{\\ignorespaces The search results should appear in a search container, and the Actions button should appear for each entry. The search bar should also be displayed.}}{155}{figure.53.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {53.2}{\\ignorespaces The Guestbook Application now supports searching for indexed Guestbook Entries.}}{159}{figure.53.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {54}Assets: Integrating with Liferay's Framework}{161}{chapter.54}\\protected@file@percent }\n\\newlabel{assets-integrating-with-liferays-framework}{{54}{161}{Assets: Integrating with Liferay's Framework}{chapter.54}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {55}Enabling Assets at the Service Layer}{163}{chapter.55}\\protected@file@percent }\n\\newlabel{enabling-assets-at-the-service-layer}{{55}{163}{Enabling Assets at the Service Layer}{chapter.55}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {56}Handling Assets for the Guestbook Service}{165}{chapter.56}\\protected@file@percent }\n\\newlabel{handling-assets-for-the-guestbook-service}{{56}{165}{Handling Assets for the Guestbook Service}{chapter.56}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {57}Handling Assets for the GuestbookEntry Service}{167}{chapter.57}\\protected@file@percent }\n\\newlabel{handling-assets-for-the-guestbookentry-service}{{57}{167}{Handling Assets for the GuestbookEntry Service}{chapter.57}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {58}Implementing Asset Renderers}{169}{chapter.58}\\protected@file@percent }\n\\newlabel{implementing-asset-renderers}{{58}{169}{Implementing Asset Renderers}{chapter.58}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {59}Implementing a Guestbook Asset Renderer}{171}{chapter.59}\\protected@file@percent }\n\\newlabel{implementing-a-guestbook-asset-renderer}{{59}{171}{Implementing a Guestbook Asset Renderer}{chapter.59}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {59.1}Creating the AssetRenderer Class}{171}{section.59.1}\\protected@file@percent }\n\\newlabel{creating-the-assetrenderer-class}{{59.1}{171}{Creating the AssetRenderer Class}{section.59.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {59.2}Creating the GuestbookAssetRendererFactory Class}{175}{section.59.2}\\protected@file@percent }\n\\newlabel{creating-the-guestbookassetrendererfactory-class}{{59.2}{175}{Creating the GuestbookAssetRendererFactory Class}{section.59.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {60}Implementing a Guestbook Entry Asset Renderer}{179}{chapter.60}\\protected@file@percent }\n\\newlabel{implementing-a-guestbook-entry-asset-renderer}{{60}{179}{Implementing a Guestbook Entry Asset Renderer}{chapter.60}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {60.1}Creating the GuestbookEntryAssetRenderer Class}{179}{section.60.1}\\protected@file@percent }\n\\newlabel{creating-the-guestbookentryassetrenderer-class}{{60.1}{179}{Creating the GuestbookEntryAssetRenderer Class}{section.60.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {60.2}Creating the GuestbookEntryAssetRendererFactory Class}{182}{section.60.2}\\protected@file@percent }\n\\newlabel{creating-the-guestbookentryassetrendererfactory-class}{{60.2}{182}{Creating the GuestbookEntryAssetRendererFactory Class}{section.60.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {60.1}{\\ignorespaces After you've implemented and registered your asset renderers for your custom entities, the Asset Publisher can display your entities.}}{185}{figure.60.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {61}Adding Asset Features to Your User Interface}{187}{chapter.61}\\protected@file@percent }\n\\newlabel{adding-asset-features-to-your-user-interface}{{61}{187}{Adding Asset Features to Your User Interface}{chapter.61}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {62}Creating JSPs for Displaying Custom Assets in the Asset Publisher}{189}{chapter.62}\\protected@file@percent }\n\\newlabel{creating-jsps-for-displaying-custom-assets-in-the-asset-publisher}{{62}{189}{Creating JSPs for Displaying Custom Assets in the Asset Publisher}{chapter.62}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {62.1}{\\ignorespaces When you click the title for a guestbook or guestbook entry in the Asset Publisher, your \\texttt  {full\\_content.jsp} should be displayed.}}{190}{figure.62.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {63}Enabling Tags, Categories, and Related Assets for Guestbooks}{191}{chapter.63}\\protected@file@percent }\n\\newlabel{enabling-tags-categories-and-related-assets-for-guestbooks}{{63}{191}{Enabling Tags, Categories, and Related Assets for Guestbooks}{chapter.63}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {63.1}Enabling Asset Features}{191}{section.63.1}\\protected@file@percent }\n\\newlabel{enabling-asset-features}{{63.1}{191}{Enabling Asset Features}{section.63.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {63.1}{\\ignorespaces Once you've updated your Guestbook Admin portlet's \\texttt  {edit\\_guestbook.jsp} page, you'll see forms for adding tags and selecting related assets.}}{193}{figure.63.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {64}Enabling Tags, Categories, and Related Assets for Guestbook Entries}{195}{chapter.64}\\protected@file@percent }\n\\newlabel{enabling-tags-categories-and-related-assets-for-guestbook-entries}{{64}{195}{Enabling Tags, Categories, and Related Assets for Guestbook Entries}{chapter.64}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {64.1}{\\ignorespaces Now you can see comments, rating, and the full range of asset features.}}{199}{figure.64.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {65}Using Workflow}{201}{chapter.65}\\protected@file@percent }\n\\newlabel{using-workflow}{{65}{201}{Using Workflow}{chapter.65}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {65.1}{\\ignorespaces Enable workflow in your assets, just like Liferay DXP's own assets.}}{201}{figure.65.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {66}Supporting Workflow at the Service Layer}{203}{chapter.66}\\protected@file@percent }\n\\newlabel{supporting-workflow-at-the-service-layer}{{66}{203}{Supporting Workflow at the Service Layer}{chapter.66}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {67}Setting the Guestbook Status}{205}{chapter.67}\\protected@file@percent }\n\\newlabel{setting-the-guestbook-status}{{67}{205}{Setting the Guestbook Status}{chapter.67}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {67.1}Creating the updateStatus Method}{206}{section.67.1}\\protected@file@percent }\n\\newlabel{creating-the-updatestatus-method}{{67.1}{206}{Creating the updateStatus Method}{section.67.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {68}Setting the Entry Workflow Status}{209}{chapter.68}\\protected@file@percent }\n\\newlabel{setting-the-entry-workflow-status}{{68}{209}{Setting the Entry Workflow Status}{chapter.68}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {69}Retrieving Guestbooks and Entries by Status}{211}{chapter.69}\\protected@file@percent }\n\\newlabel{retrieving-guestbooks-and-entries-by-status}{{69}{211}{Retrieving Guestbooks and Entries by Status}{chapter.69}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {69.1}Calling the Persistence Layer}{212}{section.69.1}\\protected@file@percent }\n\\newlabel{calling-the-persistence-layer}{{69.1}{212}{Calling the Persistence Layer}{section.69.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {70}Handling Workflow}{213}{chapter.70}\\protected@file@percent }\n\\newlabel{handling-workflow}{{70}{213}{Handling Workflow}{chapter.70}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {71}Creating a Workflow Handler for Guestbooks}{215}{chapter.71}\\protected@file@percent }\n\\newlabel{creating-a-workflow-handler-for-guestbooks}{{71}{215}{Creating a Workflow Handler for Guestbooks}{chapter.71}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {71.1}{\\ignorespaces Click the workflow notification in the Notifications portlet to review the guestbook submitted to the workflow.}}{217}{figure.71.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {71.2}{\\ignorespaces Click the workflow notification in the Notifications portlet to review the guestbook submitted to the workflow.}}{217}{figure.71.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {72}Creating a Workflow Handler for Guestbook Entries}{219}{chapter.72}\\protected@file@percent }\n\\newlabel{creating-a-workflow-handler-for-guestbook-entries}{{72}{219}{Creating a Workflow Handler for Guestbook Entries}{chapter.72}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {73}Displaying Approved Workflow Items}{221}{chapter.73}\\protected@file@percent }\n\\newlabel{displaying-approved-workflow-items}{{73}{221}{Displaying Approved Workflow Items}{chapter.73}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {74}Displaying Guestbook Status}{223}{chapter.74}\\protected@file@percent }\n\\newlabel{displaying-guestbook-status}{{74}{223}{Displaying Guestbook Status}{chapter.74}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {74.1}{\\ignorespaces The Guestbook Admin's main view currently shows the name of the guestbook and its actions button.}}{223}{figure.74.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {74.2}{\\ignorespaces The Guestbook Admin's main view, displaying the status of each guestbook.}}{224}{figure.74.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {75}Displaying Approved Entries}{225}{chapter.75}\\protected@file@percent }\n\\newlabel{displaying-approved-entries}{{75}{225}{Displaying Approved Entries}{chapter.75}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {75.1}{\\ignorespaces If you don't update the counter method to account for workflow status, it displays an incorrect count in the search container.}}{226}{figure.75.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {76}Upgrading Code to 7.0}{227}{chapter.76}\\protected@file@percent }\n\\newlabel{upgrading-code-to-7.0}{{76}{227}{Upgrading Code to 7.0}{chapter.76}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {77}Upgrading Your Development Environment}{231}{chapter.77}\\protected@file@percent }\n\\newlabel{upgrading-your-development-environment}{{77}{231}{Upgrading Your Development Environment}{chapter.77}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {77.1}Setting Up Liferay Workspace}{231}{section.77.1}\\protected@file@percent }\n\\newlabel{setting-up-liferay-workspace}{{77.1}{231}{Setting Up Liferay Workspace}{section.77.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {77.2}Creating New Liferay Workspace}{231}{section.77.2}\\protected@file@percent }\n\\newlabel{creating-new-liferay-workspace}{{77.2}{231}{Creating New Liferay Workspace}{section.77.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {77.3}Importing Existing Liferay Workspace}{232}{section.77.3}\\protected@file@percent }\n\\newlabel{importing-existing-liferay-workspace}{{77.3}{232}{Importing Existing Liferay Workspace}{section.77.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {77.4}Configuring Liferay Workspace Settings}{232}{section.77.4}\\protected@file@percent }\n\\newlabel{configuring-liferay-workspace-settings}{{77.4}{232}{Configuring Liferay Workspace Settings}{section.77.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {77.5}Configure Workspace Product Key}{232}{section.77.5}\\protected@file@percent }\n\\newlabel{configure-workspace-product-key}{{77.5}{232}{Configure Workspace Product Key}{section.77.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {77.6}Initializing Server Bundle}{232}{section.77.6}\\protected@file@percent }\n\\newlabel{initializing-server-bundle}{{77.6}{232}{Initializing Server Bundle}{section.77.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {77.7}Migrate .cfg Files to .config Files}{232}{section.77.7}\\protected@file@percent }\n\\newlabel{migrate-.cfg-files-to-.config-files}{{77.7}{232}{Migrate .cfg Files to .config Files}{section.77.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {78}Migrating Plugins SDK Projects to Liferay Workspace}{233}{chapter.78}\\protected@file@percent }\n\\newlabel{migrating-plugins-sdk-projects-to-liferay-workspace}{{78}{233}{Migrating Plugins SDK Projects to Liferay Workspace}{chapter.78}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {78.1}Importing Existing Plugins SDK Projects}{233}{section.78.1}\\protected@file@percent }\n\\newlabel{importing-existing-plugins-sdk-projects}{{78.1}{233}{Importing Existing Plugins SDK Projects}{section.78.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {78.2}Migrating Existing Plugins to Workspace}{233}{section.78.2}\\protected@file@percent }\n\\newlabel{migrating-existing-plugins-to-workspace}{{78.2}{233}{Migrating Existing Plugins to Workspace}{section.78.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {79}Upgrading Build Dependencies}{235}{chapter.79}\\protected@file@percent }\n\\newlabel{upgrading-build-dependencies}{{79}{235}{Upgrading Build Dependencies}{chapter.79}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {79.1}Updating the Repository URL}{235}{section.79.1}\\protected@file@percent }\n\\newlabel{updating-the-repository-url}{{79.1}{235}{Updating the Repository URL}{section.79.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {79.2}Updating the Workspace Plugin Version}{236}{section.79.2}\\protected@file@percent }\n\\newlabel{updating-the-workspace-plugin-version}{{79.2}{236}{Updating the Workspace Plugin Version}{section.79.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {79.3}Removing Your Project's Build Dependency Versions}{236}{section.79.3}\\protected@file@percent }\n\\newlabel{removing-your-projects-build-dependency-versions}{{79.3}{236}{Removing Your Project's Build Dependency Versions}{section.79.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {80}Fixing Upgrade Problems}{237}{chapter.80}\\protected@file@percent }\n\\newlabel{fixing-upgrade-problems}{{80}{237}{Fixing Upgrade Problems}{chapter.80}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {80.1}Auto-Correcting Upgrade Problems}{237}{section.80.1}\\protected@file@percent }\n\\newlabel{auto-correcting-upgrade-problems}{{80.1}{237}{Auto-Correcting Upgrade Problems}{section.80.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {80.2}Finding Upgrade Problems}{237}{section.80.2}\\protected@file@percent }\n\\newlabel{finding-upgrade-problems}{{80.2}{237}{Finding Upgrade Problems}{section.80.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {80.3}Resolving Upgrade Problems}{238}{section.80.3}\\protected@file@percent }\n\\newlabel{resolving-upgrade-problems}{{80.3}{238}{Resolving Upgrade Problems}{section.80.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {80.4}Removing Problem Markers}{238}{section.80.4}\\protected@file@percent }\n\\newlabel{removing-problem-markers}{{80.4}{238}{Removing Problem Markers}{section.80.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {81}Resolving a Project's Dependencies}{239}{chapter.81}\\protected@file@percent }\n\\newlabel{resolving-a-projects-dependencies}{{81}{239}{Resolving a Project's Dependencies}{chapter.81}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {81.1}Class Moved to a Package in the Classpath}{239}{section.81.1}\\protected@file@percent }\n\\newlabel{class-moved-to-a-package-in-the-classpath}{{81.1}{239}{Class Moved to a Package in the Classpath}{section.81.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {81.2}Class Moved to a Module Not in the Classpath}{240}{section.81.2}\\protected@file@percent }\n\\newlabel{class-moved-to-a-module-not-in-the-classpath}{{81.2}{240}{Class Moved to a Module Not in the Classpath}{section.81.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {81.1}{\\ignorespaces Liferay refactored the portal-service JAR for 7.0. Application APIs now exist in their own modules, and the portal-service JAR is now \\emph  {portal-kernel}.}}{240}{figure.81.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {81.3}Class Replaced or Removed}{241}{section.81.3}\\protected@file@percent }\n\\newlabel{class-replaced-or-removed}{{81.3}{241}{Class Replaced or Removed}{section.81.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {82}Resolving Breaking Changes}{243}{chapter.82}\\protected@file@percent }\n\\newlabel{resolving-breaking-changes}{{82}{243}{Resolving Breaking Changes}{chapter.82}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {83}Upgrading Service Builder Services}{245}{chapter.83}\\protected@file@percent }\n\\newlabel{upgrading-service-builder-services}{{83}{245}{Upgrading Service Builder Services}{chapter.83}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {84}Removing Legacy Files}{247}{chapter.84}\\protected@file@percent }\n\\newlabel{removing-legacy-files}{{84}{247}{Removing Legacy Files}{chapter.84}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {85}Converting a Service Builder Module from Spring DI to OSGi DS}{249}{chapter.85}\\protected@file@percent }\n\\newlabel{converting-a-service-builder-module-from-spring-di-to-osgi-ds}{{85}{249}{Converting a Service Builder Module from Spring DI to OSGi DS}{chapter.85}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {86}Rebuilding Services}{251}{chapter.86}\\protected@file@percent }\n\\newlabel{rebuilding-services}{{86}{251}{Rebuilding Services}{chapter.86}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {87}Upgrading Customization Plugins}{253}{chapter.87}\\protected@file@percent }\n\\newlabel{upgrading-customization-plugins}{{87}{253}{Upgrading Customization Plugins}{chapter.87}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {88}Upgrading Customization Modules}{255}{chapter.88}\\protected@file@percent }\n\\newlabel{upgrading-customization-modules}{{88}{255}{Upgrading Customization Modules}{chapter.88}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {89}Upgrading Core JSP Hooks}{257}{chapter.89}\\protected@file@percent }\n\\newlabel{upgrading-core-jsp-hooks}{{89}{257}{Upgrading Core JSP Hooks}{chapter.89}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {90}Upgrading Portlet JSP Hooks}{259}{chapter.90}\\protected@file@percent }\n\\newlabel{upgrading-portlet-jsp-hooks}{{90}{259}{Upgrading Portlet JSP Hooks}{chapter.90}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {91}Upgrading Service Wrapper Hooks}{261}{chapter.91}\\protected@file@percent }\n\\newlabel{upgrading-service-wrapper-hooks}{{91}{261}{Upgrading Service Wrapper Hooks}{chapter.91}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {92}Upgrading Core Language Key Hooks}{263}{chapter.92}\\protected@file@percent }\n\\newlabel{upgrading-core-language-key-hooks}{{92}{263}{Upgrading Core Language Key Hooks}{chapter.92}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {93}Upgrading Portlet Language Key Hooks}{265}{chapter.93}\\protected@file@percent }\n\\newlabel{upgrading-portlet-language-key-hooks}{{93}{265}{Upgrading Portlet Language Key Hooks}{chapter.93}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {94}Upgrading Model Listener Hooks}{267}{chapter.94}\\protected@file@percent }\n\\newlabel{upgrading-model-listener-hooks}{{94}{267}{Upgrading Model Listener Hooks}{chapter.94}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {95}Upgrading Event Action Hooks}{269}{chapter.95}\\protected@file@percent }\n\\newlabel{upgrading-event-action-hooks}{{95}{269}{Upgrading Event Action Hooks}{chapter.95}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {96}Upgrading Servlet Filter Hooks}{271}{chapter.96}\\protected@file@percent }\n\\newlabel{upgrading-servlet-filter-hooks}{{96}{271}{Upgrading Servlet Filter Hooks}{chapter.96}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {97}Upgrading Portal Property Hooks}{273}{chapter.97}\\protected@file@percent }\n\\newlabel{upgrading-portal-property-hooks}{{97}{273}{Upgrading Portal Property Hooks}{chapter.97}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {98}Upgrading Struts Action Hooks}{275}{chapter.98}\\protected@file@percent }\n\\newlabel{upgrading-struts-action-hooks}{{98}{275}{Upgrading Struts Action Hooks}{chapter.98}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.1}Converting Your Old Wrapper to \\texttt  {MVCCommand}s}{275}{section.98.1}\\protected@file@percent }\n\\newlabel{converting-your-old-wrapper-to-mvccommands}{{98.1}{275}{\\texorpdfstring {Converting Your Old Wrapper to \\texttt {MVCCommand}s}{Converting Your Old Wrapper to MVCCommands}}{section.98.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.2}Mapping Your \\texttt  {MVCCommand} URLs}{276}{section.98.2}\\protected@file@percent }\n\\newlabel{mapping-your-mvccommand-urls}{{98.2}{276}{\\texorpdfstring {Mapping Your \\texttt {MVCCommand} URLs}{Mapping Your MVCCommand URLs}}{section.98.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {99}Upgrading a Theme to 7.2}{277}{chapter.99}\\protected@file@percent }\n\\newlabel{upgrading-a-theme-to-7.2}{{99}{277}{Upgrading a Theme to 7.2}{chapter.99}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {100}Upgrading Your Theme from Liferay Portal 6.2 to 7.2}{279}{chapter.100}\\protected@file@percent }\n\\newlabel{upgrading-your-theme-from-liferay-portal-6.2-to-7.2}{{100}{279}{Upgrading Your Theme from Liferay Portal 6.2 to 7.2}{chapter.100}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {100.1}{\\ignorespaces The Lunar Resort example theme upgraded in this tutorial uses a clean, minimal design.}}{280}{figure.100.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {101}Setting up the Development Environment}{281}{chapter.101}\\protected@file@percent }\n\\newlabel{setting-up-the-development-environment}{{101}{281}{Setting up the Development Environment}{chapter.101}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {102}Installing the Liferay Theme Generator to Import a 6.2 Theme}{283}{chapter.102}\\protected@file@percent }\n\\newlabel{installing-the-liferay-theme-generator-to-import-a-6.2-theme}{{102}{283}{Installing the Liferay Theme Generator to Import a 6.2 Theme}{chapter.102}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {103}Importing the Theme into the Liferay JS Theme Toolkit}{285}{chapter.103}\\protected@file@percent }\n\\newlabel{importing-the-theme-into-the-liferay-js-theme-toolkit}{{103}{285}{Importing the Theme into the Liferay JS Theme Toolkit}{chapter.103}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {104}Running the Upgrade Task for 6.2 Themes}{287}{chapter.104}\\protected@file@percent }\n\\newlabel{running-the-upgrade-task-for-6.2-themes}{{104}{287}{Running the Upgrade Task for 6.2 Themes}{chapter.104}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {105}Updating 6.2 CSS Code}{289}{chapter.105}\\protected@file@percent }\n\\newlabel{updating-6.2-css-code}{{105}{289}{Updating 6.2 CSS Code}{chapter.105}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {106}Updating 6.2 CSS Rules and Imports}{291}{chapter.106}\\protected@file@percent }\n\\newlabel{updating-6.2-css-rules-and-imports}{{106}{291}{Updating 6.2 CSS Rules and Imports}{chapter.106}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {107}Updating the Responsiveness}{295}{chapter.107}\\protected@file@percent }\n\\newlabel{updating-the-responsiveness}{{107}{295}{Updating the Responsiveness}{chapter.107}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {108}Updating 6.2 Theme Templates}{297}{chapter.108}\\protected@file@percent }\n\\newlabel{updating-6.2-theme-templates}{{108}{297}{Updating 6.2 Theme Templates}{chapter.108}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {108.1}{\\ignorespaces The Dockbar was removed and must be replaced with the new Control Menu.}}{298}{figure.108.1}\\protected@file@percent }\n\\gdef \\LT@i {\\LT@entry \n    {1}{107.11876pt}\\LT@entry \n    {1}{362.63625pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {109}Updating 6.2 Portal Normal Theme Template}{299}{chapter.109}\\protected@file@percent }\n\\newlabel{updating-6.2-portal-normal-theme-template}{{109}{299}{Updating 6.2 Portal Normal Theme Template}{chapter.109}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {110}Updating 6.2 Navigation Theme Template}{301}{chapter.110}\\protected@file@percent }\n\\newlabel{updating-6.2-navigation-theme-template}{{110}{301}{Updating 6.2 Navigation Theme Template}{chapter.110}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {111}Updating 6.2 Init Custom Theme Template}{303}{chapter.111}\\protected@file@percent }\n\\newlabel{updating-6.2-init-custom-theme-template}{{111}{303}{Updating 6.2 Init Custom Theme Template}{chapter.111}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {112}Updating the Resources Importer}{305}{chapter.112}\\protected@file@percent }\n\\newlabel{updating-the-resources-importer}{{112}{305}{Updating the Resources Importer}{chapter.112}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {113}Updating 6.2 Liferay Plugin Package Properties}{307}{chapter.113}\\protected@file@percent }\n\\newlabel{updating-6.2-liferay-plugin-package-properties}{{113}{307}{Updating 6.2 Liferay Plugin Package Properties}{chapter.113}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {114}Updating 6.2 Web Content}{309}{chapter.114}\\protected@file@percent }\n\\newlabel{updating-6.2-web-content}{{114}{309}{Updating 6.2 Web Content}{chapter.114}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {115}Updating the 6.2 Sitemap}{313}{chapter.115}\\protected@file@percent }\n\\newlabel{updating-the-6.2-sitemap}{{115}{313}{Updating the 6.2 Sitemap}{chapter.115}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {116}Applying Clay Design Patterns}{315}{chapter.116}\\protected@file@percent }\n\\newlabel{applying-clay-design-patterns}{{116}{315}{Applying Clay Design Patterns}{chapter.116}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {117}Upgrading Your Theme from Liferay Portal 7.0 to 7.2}{317}{chapter.117}\\protected@file@percent }\n\\newlabel{upgrading-your-theme-from-liferay-portal-7.0-to-7.2}{{117}{317}{Upgrading Your Theme from Liferay Portal 7.0 to 7.2}{chapter.117}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {118}Running the Upgrade Task for 7.0 Themes}{319}{chapter.118}\\protected@file@percent }\n\\newlabel{running-the-upgrade-task-for-7.0-themes}{{118}{319}{Running the Upgrade Task for 7.0 Themes}{chapter.118}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {119}Updating 7.0 CSS Code}{321}{chapter.119}\\protected@file@percent }\n\\newlabel{updating-7.0-css-code}{{119}{321}{Updating 7.0 CSS Code}{chapter.119}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {120}Updating 7.0 CSS File Names for Clay}{323}{chapter.120}\\protected@file@percent }\n\\newlabel{updating-7.0-css-file-names-for-clay}{{120}{323}{Updating 7.0 CSS File Names for Clay}{chapter.120}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {121}Updating 7.0 Class Variables}{325}{chapter.121}\\protected@file@percent }\n\\newlabel{updating-7.0-class-variables}{{121}{325}{Updating 7.0 Class Variables}{chapter.121}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {122}Updating 7.0 Imports}{327}{chapter.122}\\protected@file@percent }\n\\newlabel{updating-7.0-imports}{{122}{327}{Updating 7.0 Imports}{chapter.122}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {123}Updating 7.0 Theme Templates to 7.2}{329}{chapter.123}\\protected@file@percent }\n\\newlabel{updating-7.0-theme-templates-to-7.2}{{123}{329}{Updating 7.0 Theme Templates to 7.2}{chapter.123}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {124}Updating 7.0 Theme Templates}{331}{chapter.124}\\protected@file@percent }\n\\newlabel{updating-7.0-theme-templates}{{124}{331}{Updating 7.0 Theme Templates}{chapter.124}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {125}Using the Bootstrap 3 Lexicon CSS Compatibility Layer}{333}{chapter.125}\\protected@file@percent }\n\\newlabel{using-the-bootstrap-3-lexicon-css-compatibility-layer}{{125}{333}{Using the Bootstrap 3 Lexicon CSS Compatibility Layer}{chapter.125}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {126}Upgrading 7.1 Themes to 7.2}{335}{chapter.126}\\protected@file@percent }\n\\newlabel{upgrading-7.1-themes-to-7.2}{{126}{335}{Upgrading 7.1 Themes to 7.2}{chapter.126}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {127}Upgrading a Layout Template to 7.2}{337}{chapter.127}\\protected@file@percent }\n\\newlabel{upgrading-a-layout-template-to-7.2}{{127}{337}{Upgrading a Layout Template to 7.2}{chapter.127}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {128}Upgrading 6.2 Layout Templates to 7.2}{339}{chapter.128}\\protected@file@percent }\n\\newlabel{upgrading-6.2-layout-templates-to-7.2}{{128}{339}{Upgrading 6.2 Layout Templates to 7.2}{chapter.128}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {129}Upgrading 7.0 and 7.1 Layout Templates to 7.2}{341}{chapter.129}\\protected@file@percent }\n\\newlabel{upgrading-7.0-and-7.1-layout-templates-to-7.2}{{129}{341}{Upgrading 7.0 and 7.1 Layout Templates to 7.2}{chapter.129}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {130}Upgrading Frameworks and Features}{343}{chapter.130}\\protected@file@percent }\n\\newlabel{upgrading-frameworks-and-features}{{130}{343}{Upgrading Frameworks and Features}{chapter.130}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {131}Upgrading JNDI Data Source Usage}{345}{chapter.131}\\protected@file@percent }\n\\newlabel{upgrading-jndi-data-source-usage}{{131}{345}{Upgrading JNDI Data Source Usage}{chapter.131}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {132}Upgrading Service Builder Service Invocation}{347}{chapter.132}\\protected@file@percent }\n\\newlabel{upgrading-service-builder-service-invocation}{{132}{347}{Upgrading Service Builder Service Invocation}{chapter.132}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {133}Upgrading Service Builder}{349}{chapter.133}\\protected@file@percent }\n\\newlabel{upgrading-service-builder}{{133}{349}{Upgrading Service Builder}{chapter.133}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {133.1}Step 1: Adapt the Code to 7.0's API}{349}{section.133.1}\\protected@file@percent }\n\\newlabel{step-1-adapt-the-code-to-7.0s-api}{{133.1}{349}{Step 1: Adapt the Code to 7.0's API}{section.133.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {133.2}Step 2: Resolve Dependencies}{350}{section.133.2}\\protected@file@percent }\n\\newlabel{step-2-resolve-dependencies}{{133.2}{350}{Step 2: Resolve Dependencies}{section.133.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {133.3}Step 3: Build the Services}{350}{section.133.3}\\protected@file@percent }\n\\newlabel{step-3-build-the-services}{{133.3}{350}{Step 3: Build the Services}{section.133.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {134}Migrating Off of Velocity Templates}{351}{chapter.134}\\protected@file@percent }\n\\newlabel{migrating-off-of-velocity-templates}{{134}{351}{Migrating Off of Velocity Templates}{chapter.134}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {135}Upgrading Portlets}{353}{chapter.135}\\protected@file@percent }\n\\newlabel{upgrading-portlets}{{135}{353}{Upgrading Portlets}{chapter.135}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {136}Upgrading a GenericPortlet}{355}{chapter.136}\\protected@file@percent }\n\\newlabel{upgrading-a-genericportlet}{{136}{355}{Upgrading a GenericPortlet}{chapter.136}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {137}Upgrading a Liferay MVC Portlet}{357}{chapter.137}\\protected@file@percent }\n\\newlabel{upgrading-a-liferay-mvc-portlet}{{137}{357}{Upgrading a Liferay MVC Portlet}{chapter.137}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {138}Upgrading a Liferay JSF Portlet}{359}{chapter.138}\\protected@file@percent }\n\\newlabel{upgrading-a-liferay-jsf-portlet}{{138}{359}{Upgrading a Liferay JSF Portlet}{chapter.138}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {138.1}{\\ignorespaces The Liferay Faces site gives you options to generate dependencies for many environments.}}{360}{figure.138.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {139}Upgrading a Servlet-based Portlet}{363}{chapter.139}\\protected@file@percent }\n\\newlabel{upgrading-a-servlet-based-portlet}{{139}{363}{Upgrading a Servlet-based Portlet}{chapter.139}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {140}Upgrading a Spring Portlet MVC Portlet}{365}{chapter.140}\\protected@file@percent }\n\\newlabel{upgrading-a-spring-portlet-mvc-portlet}{{140}{365}{Upgrading a Spring Portlet MVC Portlet}{chapter.140}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {141}Upgrading a Struts 1 Portlet}{367}{chapter.141}\\protected@file@percent }\n\\newlabel{upgrading-a-struts-1-portlet}{{141}{367}{Upgrading a Struts 1 Portlet}{chapter.141}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {142}Upgrading Web Plugins}{369}{chapter.142}\\protected@file@percent }\n\\newlabel{upgrading-web-plugins}{{142}{369}{Upgrading Web Plugins}{chapter.142}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {143}Upgrading Ext Plugins}{371}{chapter.143}\\protected@file@percent }\n\\newlabel{upgrading-ext-plugins}{{143}{371}{Upgrading Ext Plugins}{chapter.143}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {144}Creating a Theme}{373}{chapter.144}\\protected@file@percent }\n\\newlabel{creating-a-theme}{{144}{373}{Creating a Theme}{chapter.144}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {144.1}{\\ignorespaces The finished Lunar Resort Theme uses Liferay DXP's tools to produce a user-friendly UI that is maintainable.}}{374}{figure.144.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {145}Setting up the Theme}{375}{chapter.145}\\protected@file@percent }\n\\newlabel{setting-up-the-theme}{{145}{375}{Setting up the Theme}{chapter.145}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {145.1}{\\ignorespaces Answer no for the Font Awesome Prompt}}{376}{figure.145.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {146}Customizing the Lunar Resort's Header and Logo}{377}{chapter.146}\\protected@file@percent }\n\\newlabel{customizing-the-lunar-resorts-header-and-logo}{{146}{377}{Customizing the Lunar Resort's Header and Logo}{chapter.146}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {147}Customizing the Navigation}{381}{chapter.147}\\protected@file@percent }\n\\newlabel{customizing-the-navigation}{{147}{381}{Customizing the Navigation}{chapter.147}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {147.1}{\\ignorespaces The updated Header and navigation are much more user-friendly now.}}{386}{figure.147.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {148}Defining the Lunar Resort's Footer and Footer Navigation}{387}{chapter.148}\\protected@file@percent }\n\\newlabel{defining-the-lunar-resorts-footer-and-footer-navigation}{{148}{387}{Defining the Lunar Resort's Footer and Footer Navigation}{chapter.148}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {148.1}{\\ignorespaces The updated Footer provides everything visitors need to follow and contact the Lunar Resort.}}{390}{figure.148.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {149}Adding a Color Scheme Variant for the Lunar Resort Theme}{391}{chapter.149}\\protected@file@percent }\n\\newlabel{adding-a-color-scheme-variant-for-the-lunar-resort-theme}{{149}{391}{Adding a Color Scheme Variant for the Lunar Resort Theme}{chapter.149}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {149.1}{\\ignorespaces Color schemes are a good way to subtly change the look and feel of your site.}}{394}{figure.149.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {149.2}{\\ignorespaces The finished color scheme gives the Lunar Resort site a fiery glow.}}{395}{figure.149.2}\\protected@file@percent }\n\\@setckpt{developer/tutorials}{\n\\setcounter{page}{396}\n\\setcounter{equation}{0}\n\\setcounter{enumi}{8}\n\\setcounter{enumii}{6}\n\\setcounter{enumiii}{3}\n\\setcounter{enumiv}{0}\n\\setcounter{footnote}{0}\n\\setcounter{mpfootnote}{0}\n\\setcounter{@memmarkcntra}{-1}\n\\setcounter{storedpagenumber}{1}\n\\setcounter{book}{0}\n\\setcounter{part}{1}\n\\setcounter{chapter}{149}\n\\setcounter{section}{0}\n\\setcounter{subsection}{0}\n\\setcounter{subsubsection}{0}\n\\setcounter{paragraph}{0}\n\\setcounter{subparagraph}{0}\n\\setcounter{@ppsavesec}{0}\n\\setcounter{@ppsaveapp}{0}\n\\setcounter{vslineno}{0}\n\\setcounter{poemline}{0}\n\\setcounter{modulo@vs}{0}\n\\setcounter{memfvsline}{0}\n\\setcounter{verse}{0}\n\\setcounter{chrsinstr}{0}\n\\setcounter{poem}{0}\n\\setcounter{newflo@tctr}{4}\n\\setcounter{@contsubnum}{0}\n\\setcounter{section@level}{0}\n\\setcounter{maxsecnumdepth}{1}\n\\setcounter{sidefootnote}{0}\n\\setcounter{pagenote}{0}\n\\setcounter{pagenoteshadow}{0}\n\\setcounter{memfbvline}{0}\n\\setcounter{bvlinectr}{0}\n\\setcounter{cp@cntr}{0}\n\\setcounter{ism@mctr}{0}\n\\setcounter{xsm@mctr}{0}\n\\setcounter{csm@mctr}{0}\n\\setcounter{ksm@mctr}{0}\n\\setcounter{xksm@mctr}{0}\n\\setcounter{cksm@mctr}{0}\n\\setcounter{msm@mctr}{0}\n\\setcounter{xmsm@mctr}{0}\n\\setcounter{cmsm@mctr}{0}\n\\setcounter{bsm@mctr}{0}\n\\setcounter{workm@mctr}{0}\n\\setcounter{sheetsequence}{468}\n\\setcounter{lastsheet}{2851}\n\\setcounter{lastpage}{2779}\n\\setcounter{figure}{2}\n\\setcounter{lofdepth}{1}\n\\setcounter{table}{0}\n\\setcounter{lotdepth}{1}\n\\setcounter{Item}{599}\n\\setcounter{Hfootnote}{5}\n\\setcounter{bookmark@seq@number}{0}\n\\setcounter{memhycontfloat}{0}\n\\setcounter{Hpagenote}{0}\n\\setcounter{r@tfl@t}{0}\n\\setcounter{float@type}{4}\n\\setcounter{LT@tables}{1}\n\\setcounter{LT@chunks}{3}\n\\setcounter{parentequation}{0}\n\\setcounter{FancyVerbLine}{0}\n}\n"
  },
  {
    "path": "book/developer/tutorials.tex",
    "content": "\\chapter{Developer Tutorials}\\label{developer-tutorials}\n\nLiferay's developer tutorials help you learn from scratch. They are the\n``opinionated'' way to work with code on Liferay's platform. Here,\nyou'll learn these things:\n\n\\begin{itemize}\n\\item\n  Writing applications on various frameworks, such as Bean Portlet,\n  PortletMVC4Spring, and Liferay MVC Portlet (coming soon)\n\\item\n  Writing front-end applications using Angular, React, and Vue (coming\n  soon)\n\\item\n  Upgrading your code from older versions of Liferay DXP\n\\item\n  Creating back-end and headless services (coming soon)\n\\end{itemize}\n\n\\chapter{Developing a Web\nApplication}\\label{developing-a-web-application}\n\nThis tutorial shows you how to build a Liferay application from\nbeginning to end. Starting by designing the back-end using Liferay's\nobject-relational mapper Service Builder, you'll move from there to\ndesigning the widget that calls all of those services. In the process,\nyou'll learn about many of Liferay's development frameworks, including\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Permissions\n\\item\n  Search\n\\item\n  Assets\n\\item\n  REST Builder web services\n\\item\n  Workflow\n\\item\n  Staging\n\\end{itemize}\n\nWhen you're finished, the application looks like this:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/finished-guestbook-intro.png}\n\\caption{It looks humble, but there's a lot of functionality packed in\nthis application.}\n\\end{figure}\n\nThis tutorial assumes nothing, so it starts at the beginning: setting up\na Liferay development environment. Though you can use anything from a\ntext editor and the command line to your Java IDE of choice, Liferay Dev\nStudio DXP optimizes development on Liferay's platform. It integrates\nLiferay's Blade tools for modular development.\n\nOnce you have an environment ready, you'll jump right in and start\ncreating your object-relational map. After you've created your back-end,\nyou'll move to the front-end.\n\nFrom there you'll see everything from UI standards to providing remote\nservices. Once everything is completed and wrapped up with a bow, you\ncan distribute the application on Marketplace.\n\nLet's Go!{}\n\n\\chapter{Setting Up a Development\nEnvironment}\\label{setting-up-a-development-environment}\n\nLiferay's development tools help you get started fast. All you need as a\nprerequisite is a Java Development Kit version 8\n(\\href{http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html}{JDK}\nor \\href{https://jdk.java.net/java-se-ri/8}{OpenJDK} is fine). Once\nthat's installed, there are only three steps.\n\n\\begin{itemize}\n\\item\n  Download a Liferay Dev Studio DXP bundle.\n\\item\n  Unzip the downloaded package to a location on your system.\n\\item\n  Start Dev Studio DXP.\n\\end{itemize}\n\nYou'll follow these steps and then generate a Liferay Workspace for\ndeveloping your first Liferay DXP application.\n\n\\section{Installing a Liferay Dev Studio DXP\nBundle}\\label{installing-a-liferay-dev-studio-dxp-bundle}\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Download and install\n  \\href{/docs/7-2/reference/-/knowledge_base/r/installing-liferay-dev-studio}{Liferay\n  Dev Studio DXP} Installing it is easy: run the installer.\n\\item\n  To run Liferay Dev Studio DXP, run the \\texttt{DeveloperStudio}\n  executable. On Windows, the installer doesn't create a menu entry, so\n  you should add a shortcut to it to your desktop or task bar.\n\\end{enumerate}\n\nThe first time you start Liferay Dev Studio DXP, it prompts you to\nselect an Eclipse workspace. If you specify an empty folder, Liferay Dev\nStudio DXP creates a new workspace in that folder. Follow these steps to\ncreate a new workspace:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  When prompted, indicate your workspace's path. Name your new workspace\n  \\texttt{guestbook-workspace} and click \\emph{OK}. Windows has path\n  length limitations, so on that OS, you may want to create your\n  workspace the root folder (\\texttt{C:\\textbackslash{}}).\n\\item\n  When Liferay Dev Studio DXP first launches, it presents a welcome\n  page. Click the \\emph{Workbench} icon to continue.\n\\end{enumerate}\n\nNice job! Your development environment is installed and your Eclipse\nworkspace is set up.\n\n\\section{Creating a Liferay\nWorkspace}\\label{creating-a-liferay-workspace}\n\nNow you'll create another kind of workspace: a\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace}. Liferay Workspace helps manage Liferay projects by providing\nvarious build scripts, properties, and configuration for you. Liferay\nWorkspace uses\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI} and\n\\href{https://gradle.org/}{Gradle} to manage dependencies and organize\nyour build environment. Note that to avoid configuration issues, you can\nonly create one Liferay Workspace for each Eclipse Workspace.\n\nFollow these steps to create a Liferay Workspace in Liferay Dev Studio\nDXP:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select \\emph{File} → \\emph{New} → \\emph{Liferay Workspace Project}.\n  Note: you may have to select \\emph{File} → \\emph{New} → \\emph{Other},\n  then choose \\emph{Liferay Workspace Project} in the \\emph{Liferay}\n  category.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/dev-studio-create-workspace.png}\n  \\caption{By selecting \\emph{Liferay Workspace}, you begin the process\n  of creating a new workspace for your Liferay DXP projects.}\n  \\end{figure}\n\n  A \\emph{New Liferay Workspace} dialog appears, which presents several\n  configuration options.\n\\item\n  Give your workspace the name \\texttt{com-liferay-docs-guestbook}.\n\\item\n  Next, choose your workspace's location. Leave the default setting\n  checked. This places your Liferay Workspace inside your Eclipse\n  workspace.\n\\item\n  For \\emph{Liferay Version}, 7.2 should already be selected.\n\\item\n  Leave the rest of the defaults.\n\\item\n  Check the \\emph{Download Liferay bundle} checkbox to download and\n  unzip a Liferay DXP instance in your workspace automatically. When\n  prompted, name the server \\texttt{liferay-tomcat-bundle}.\n\\item\n  Click \\emph{Finish} to create your Liferay Workspace. This may take a\n  while because Liferay Liferay DXP downloads the Liferay DXP bundle in\n  the background.\n\\end{enumerate}\n\nCongratulations! Your development environment is ready! Next, you'll get\nstarted developing your first Liferay DXP application.\n\n\\chapter{Generating the Back-end}\\label{generating-the-back-end}\n\nYou can start writing an application in either the front-end or the\nback-end. If you start with the front-end, you design the screens and\nforms first, using mock data. If you start with the back-end, you create\nyour data store up front, and then you can create your front-end later.\nThis is what you'll do with the Guestbook application.\n\nA \\emph{persistence} layer and a \\emph{service} layer make up the bottom\nlayers of your back-end. You'll persist guestbooks and their entries to\na database. Your service layer calls your persistence layer, providing a\nbuffer in case you wish to swap out your persistence technology later.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/application-layers.png}\n\\caption{Service Builder generates the shaded layers of your\napplication.}\n\\end{figure}\n\n\\emph{Service Builder} is Liferay's code generation tool for defining\nobject models and mapping those models to SQL databases. By defining\nyour model in a single XML file, you can generate your object model (the\nM in MVC), your service layer, and your persistence layer all in one\nshot. At the same time, you can support every database Liferay DXP\nsupports.\n\nReady to begin?\n\nLet's Go!{}\n\n\\chapter{What is Service Builder?}\\label{what-is-service-builder}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Generating the Back-End</p><p>Step 1 of 3</p>\n\\end{verbatim}\n\nNow you'll use Service Builder to generate your application's Model,\nService, and Persistence layers. Then you can add your application's\nnecessary business logic.\n\n\\section{Guestbook Application\nDesign}\\label{guestbook-application-design}\n\nThe Guestbook application handles multiple Guestbooks and their entries.\nTo make this work, you'll create two tables in the database: one for\nguestbooks and one for guestbook entries.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-final.png}\n\\caption{When you're done, the Guestbook supports multiple guestbooks\nand makes use of many Liferay features.}\n\\end{figure}\n\n\\section{Service Layer}\\label{service-layer}\n\nThis application is data-driven. It uses services for storing and\nretrieving data. The application asks for data, and the service fetches\nit from the persistence layer. The application can then display this\ndata to the user, who reads or modifies it. If the data is modified, the\napplication passes it back to the service, which calls the persistence\nlayer to store it. The application doesn't need to know anything about\nhow the service does what it does.\n\nTo get started, you'll create a Service Builder project and populate its\n\\texttt{service.xml} file with all the necessary entities to generate\nthis code:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In Liferay Dev Studio DXP, click \\emph{File} → \\emph{New} →\n  \\emph{Liferay Module Project}.\n\\item\n  Name the project \\texttt{guestbook}.\n\\item\n  Select \\texttt{service-builder} for the Project Template Name.\n\\item\n  Click \\emph{Next}.\n\\item\n  Enter \\texttt{com.liferay.docs.guestbook} for the \\emph{Package Name}.\n\\item\n  Click \\emph{Finish}.\n\\end{enumerate}\n\nThis creates two modules: an API module (\\texttt{guestbook-api}) and a\nservice module (\\texttt{guestbook-service}). Next, you'll learn how to\nuse them.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-service-project.png}\n\\caption{Your current project structure.}\n\\end{figure}\n\n\\chapter{Generating Model, Service, and Persistence\nLayers}\\label{generating-model-service-and-persistence-layers}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Generating the Back-End</p><p>Step 2 of 3</p>\n\\end{verbatim}\n\nTo model the guestbooks and entries, you'll create guestbook and entry\nmodel classes. But you won't do this directly in Java. Instead, you'll\ndefine them in Service Builder, which generates your object model and\nmaps it to all the SQL databases Liferay DXP supports.\n\nThis application's design allows for multiple guestbooks, each\ncontaining different sets of entries. All users with permission to\naccess the application can add entries, but only administrative users\ncan add guestbooks.\n\nIt's time to get started. You'll create the \\texttt{Guestbook} entity\nfirst:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your \\texttt{guestbook-service} project, open \\texttt{service.xml}.\n  Make sure the \\emph{Source} tab is selected.\n\\item\n  When Liferay Dev Studio DXP generated your project, it filled this\n  file with dummy entities, which you'll replace. Remove everything in\n  the file below the \\texttt{DOCTYPE}. Replace the file's opening\n  contents with the following code:\n\n\\begin{verbatim}\n<service-builder dependency-injector=\"ds\" package-path=\"com.liferay.docs.guestbook\" mvcc-enabled=\"true\">\n    <author>liferay</author>\n    <namespace>GB</namespace>\n    <entity name=\"Guestbook\" local-service=\"true\" uuid=\"true\" remote-service=\"true\">\n\\end{verbatim}\n\n  This defines the author, namespace, and the entity name. The namespace\n  keeps the database field names from conflicting. The last tag is the\n  opening tag for the \\texttt{Guestbook} entity definition. In this tag,\n  you enable local and remote services for the entity, define its name,\n  and specify that it should have a\n  \\href{https://en.wikipedia.org/wiki/Universally_unique_identifier}{universally\n  unique identifier (UUID)}.\n\\item\n  The Guestbook requires only two fields: a primary key to identify it\n  uniquely in the database, and a name. Add these fields:\n\n\\begin{verbatim}\n<!-- Guestbook fields -->\n\n<column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n<column name=\"name\" type=\"String\" />\n\\end{verbatim}\n\n  This defines \\texttt{guestbookId} as the entity's primary key of the\n  type \\texttt{long} and the name as a \\texttt{String}.\n\\item\n  Next, define the group instance. The \\texttt{groupId} defines the ID\n  of the Site in Liferay DXP where the entity instance exists. The\n  \\texttt{companyId} is the primary key of a\n  \\href{/docs/7-2/user/-/knowledge_base/u/setting-up}{portal instance}.\n\n\\begin{verbatim}\n<!-- Group instance -->\n\n<column name=\"groupId\" type=\"long\" />\n<column name=\"companyId\" type=\"long\" />\n\\end{verbatim}\n\\item\n  Next, add audit fields. These fields help you track owners of entity\n  instances, along with those instances' create and modify dates:\n\n\\begin{verbatim}\n<!-- Audit fields -->\n\n<column name=\"userId\" type=\"long\" />\n<column name=\"userName\" type=\"String\" />\n<column name=\"createDate\" type=\"Date\" />\n<column name=\"modifiedDate\" type=\"Date\" />\n\\end{verbatim}\n\\item\n  After this, add fields that support Liferay's workflow system. These\n  provide fields in the database to track your entity's status as it\n  passes through the workflow.\n\n\\begin{verbatim}\n<!-- Status fields -->\n\n<column name=\"status\" type=\"int\" />\n<column name=\"statusByUserId\" type=\"long\" />\n<column name=\"statusByUserName\" type=\"String\" />\n<column name=\"statusDate\" type=\"Date\" />\n\\end{verbatim}\n\\item\n  Before the closing \\texttt{\\textless{}/entity\\textgreater{}} tag, add\n  this finder definition:\n\n\\begin{verbatim}\n    <finder name=\"GroupId\" return-type=\"Collection\">\n        <finder-column name=\"groupId\" />\n    </finder>\n\n</entity>\n\\end{verbatim}\n\\end{enumerate}\n\nA\n\\href{/docs/7-2/appdev/-/knowledge_base/a/defining-service-entity-finder-methods}{finder}\ngenerates a \\texttt{get} method for retrieving Guestbook entities. The\nfields used by the finder define the scope of the data retrieved. This\nfinder gets all Guestbooks by their \\texttt{groupId}. This is how\nadministrators put Guestbooks on multiple Sites, and each\n\\texttt{Guestbook} has its own data scoped to its Site.\n\nThe \\texttt{Guestbook} entity is finished for now. Next, you'll create\nthe \\texttt{GuestbookEntry} entity:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the opening entity tag:\n\n\\begin{verbatim}\n<entity name=\"GuestbookEntry\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n\\end{verbatim}\n\n  As with the \\texttt{Guestbook} entity, you enable local and remote\n  services, define the entity's name, and specify that it should have a\n  UUID.\n\\item\n  Add the fields that define the \\texttt{GuestbookEntry}'s data:\n\n\\begin{verbatim}\n<!-- Guestbook Entry fields -->\n\n<column name=\"entryId\" primary=\"true\" type=\"long\" />\n<column name=\"name\" type=\"String\" />\n<column name=\"email\" type=\"String\" />\n<column name=\"message\" type=\"String\" />\n<column name=\"guestbookId\" type=\"long\" />\n\\end{verbatim}\n\n  The \\texttt{name}, \\texttt{email}, and \\texttt{message} fields\n  comprise a \\texttt{GuestbookEntry}. These fields define the name of\n  the person creating the entry, an email address, and the Guestbook\n  message, respectively. The \\texttt{guestbookId} is assigned\n  automatically by code you'll write, and is a foreign key to the\n  \\texttt{Guestbook} where this entry belongs.\n\\item\n  Add fields to track the portal instance and group:\n\n\\begin{verbatim}\n<!-- Group instance -->\n\n<column name=\"groupId\" type=\"long\" />\n<column name=\"companyId\" type=\"long\" />\n\\end{verbatim}\n\\item\n  Add audit fields:\n\n\\begin{verbatim}\n<!-- Audit fields -->\n\n<column name=\"userId\" type=\"long\" />\n<column name=\"userName\" type=\"String\" />\n<column name=\"createDate\" type=\"Date\" />\n<column name=\"modifiedDate\" type=\"Date\" />\n\\end{verbatim}\n\\item\n  Add status fields to track workflow:\n\n\\begin{verbatim}\n<!-- Status fields -->\n\n<column name=\"status\" type=\"int\" />\n<column name=\"statusByUserId\" type=\"long\" />\n<column name=\"statusByUserName\" type=\"String\" />\n<column name=\"statusDate\" type=\"Date\" />\n\\end{verbatim}\n\\item\n  When querying for \\texttt{GuestbookEntry}s, you can order them by one\n  or more columns. Since visitors sign \\texttt{Guestbook}s in order by\n  time, order your \\texttt{GuestbookEntry} instances by the date they\n  were created:\n\n\\begin{verbatim}\n<order>\n    <order-column name=\"createDate\" order-by=\"desc\" />\n</order>\n\\end{verbatim}\n\\item\n  Add a finder that retrieves \\texttt{GuestbooEntry}s by a combination\n  of \\texttt{groupId} and \\texttt{guestbookId}. This supports Liferay\n  DXP's multi-tenancy by only returning those entries that belong both\n  to the current Site and the current Guestbook. After defining your\n  finder add the closing entity tag:\n\n\\begin{verbatim}\n    <finder name=\"G_G\" return-type=\"Collection\">\n        <finder-column name=\"groupId\" />\n        <finder-column name=\"guestbookId\" />\n    </finder>\n</entity>\n\\end{verbatim}\n\\item\n  Define exception types outside the\n  \\texttt{\\textless{}entity\\textgreater{}} tags, just before the closing\n  \\texttt{\\textless{}/service-builder\\textgreater{}} tag:\n\n\\begin{verbatim}\n    <exceptions>\n        <exception>GuestbookEntryEmail</exception>\n        <exception>GuestbookEntryMessage</exception>\n        <exception>GuestbookEntryName</exception>\n        <exception>GuestbookName</exception>\n    </exceptions>\n\n</service-builder>\n\\end{verbatim}\n\n  These generate exception classes you'll use later in try/catch\n  statements.\n\\item\n  Save your \\texttt{service.xml} file.\n\\end{enumerate}\n\nNow you're ready to run Service Builder to generate your model, service,\nand persistence layers!\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the Gradle Tasks pane on the right side of Dev Studio DXP, open\n  \\texttt{com-liferay-docs-guestbook} → \\texttt{modules} →\n  \\texttt{guestbook} → \\texttt{guestbook-service} → \\texttt{build}.\n\\item\n  Run \\texttt{buildService} by right-clicking it and selecting \\emph{Run\n  Gradle Tasks}. Make sure you're connected to the Internet, as Gradle\n  downloads dependencies the first time you run it.\n\\item\n  In the Project Explorer, right-click the \\texttt{guestbook-service}\n  module and select \\emph{Refresh}. Repeat this step for the\n  \\texttt{guestbook-api} module. This ensures that the new classes and\n  interfaces generated by Service Builder show up in Dev Studio DXP.\n\\item\n  In the Project Explorer, right-click the \\texttt{guestbook-service}\n  module and select \\emph{Gradle} → \\emph{Refresh Gradle Project}.\n  Repeat this step for the \\texttt{guestbook-api} module. This ensures\n  that your modules' Gradle dependencies are up to date.\n\\end{enumerate}\n\nService Builder is based on a design philosophy called loose coupling.\nIt generates three layers of your application: the model, the service,\nand the persistence layers. Loose coupling means you can swap out the\npersistence layer with little to no change in the model and service\nlayers. The model is in the \\texttt{-api} module, and the service and\npersistence layers are in the \\texttt{-service} module.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/model-service-persistence.png}\n\\caption{The Model, Service, and Persistence Layer comprise a loose\ncoupling design.}\n\\end{figure}\n\nEach layer is implemented using Java Interfaces and implementations of\nthose interfaces. Rather than have one \\texttt{Guestbook} class that\nrepresents your model, Service Builder generates a system of classes\nthat includes a \\texttt{Guestbook} interface, a\n\\texttt{GuestbookBaseImpl} abstract class that Service Builder manages,\nand a \\texttt{GuestbookImpl} class that you can customize. With this\ndesign, you can customize your model and let Service Builder generate\nthe tedious-to-write code. That's why Service Builder is a code\ngenerator for code generator haters.\n\nNext, you'll create the service implementations.\n\n\\chapter{Implementing Service\nMethods}\\label{implementing-service-methods}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Generating the Back-End</p><p>Step 3 of 3</p>\n\\end{verbatim}\n\nWhen you use Service Builder, you implement the services in the service\nmodule. Because your application's projects are\n\\href{/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services}{components},\nyou can reference your service layer from your web module.\n\nYou'll implement services for guestbooks and entries in the\n\\texttt{guestbook-service} module's \\texttt{GuestbookLocalServiceImpl}\nand \\texttt{GuestbookEntryLocalServiceImpl}, respectively.\n\nFollow these steps to implement services for guestbooks in\n\\texttt{GuestbookLocalServiceImpl}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\texttt{com.liferay.docs.guestbook.service.impl} package, open\n  \\texttt{GuestbookLocalServiceImpl}. Then add this\n  \\texttt{addGuestbook} method:\n\n\\begin{verbatim}\npublic Guestbook addGuestbook(long userId, String name,\n        ServiceContext serviceContext) throws PortalException {\n\n    long groupId = serviceContext.getScopeGroupId();\n\n    User user = userLocalService.getUserById(userId);\n\n    Date now = new Date();\n\n    validate(name);\n\n    long guestbookId = counterLocalService.increment();\n\n    Guestbook guestbook = guestbookPersistence.create(guestbookId);\n\n    guestbook.setUuid(serviceContext.getUuid());\n    guestbook.setUserId(userId);\n    guestbook.setGroupId(groupId);\n    guestbook.setCompanyId(user.getCompanyId());\n    guestbook.setUserName(user.getFullName());\n    guestbook.setCreateDate(serviceContext.getCreateDate(now));\n    guestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n    guestbook.setName(name);\n    guestbook.setExpandoBridgeAttributes(serviceContext);\n\n    guestbookPersistence.update(guestbook);\n\n    return guestbook;\n}\n\\end{verbatim}\n\n  This method adds a guestbook to the database. It retrieves metadata\n  from the environment (such as the current user's ID, the group ID,\n  etc.), along with data passed from the user. It validates this data\n  and uses it to construct a \\texttt{Guestbook} object. The method then\n  persists this object to the database and returns the object. You\n  implement the business logic here because Service Builder already\n  generated the model and all the code that maps that model to the\n  database.\n\\item\n  Add the methods for getting \\texttt{Guestbook} objects:\n\n\\begin{verbatim}\npublic List<Guestbook> getGuestbooks(long groupId) {\n\n    return guestbookPersistence.findByGroupId(groupId);\n}\n\npublic List<Guestbook> getGuestbooks(long groupId, int start, int end,\n        OrderByComparator<Guestbook> obc) {\n\n    return guestbookPersistence.findByGroupId(groupId, start, end, obc);\n}\n\npublic List<Guestbook> getGuestbooks(long groupId, int start, int end) {\n\n    return guestbookPersistence.findByGroupId(groupId, start, end);\n}\n\npublic int getGuestbooksCount(long groupId) {\n\n    return guestbookPersistence.countByGroupId(groupId);\n}\n\\end{verbatim}\n\n  These call the finders you generated with Service Builder. The first\n  method retrieves a list of guestbooks from the Site specified by\n  \\texttt{groupId}. The next two methods get paginated lists, optionally\n  in a particular order. The final method gives you the total number of\n  guestbooks for a given Site.\n\\item\n  Finally, add the guestbook validator method:\n\n\\begin{verbatim}\nprotected void validate(String name) throws PortalException {\n    if (Validator.isNull(name)) {\n        throw new GuestbookNameException();\n    }\n}\n\\end{verbatim}\n\n  This method uses Liferay DXP's \\texttt{Validator} to make sure the\n  user entered text for the guestbook name.\n\\item\n  Press {[}CTRL{]}+{[}SHIFT{]}+O to organize imports and select the\n  following classes when prompted:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{java.util.List}\n  \\item\n    \\texttt{java.util.Date}\n  \\item\n    \\texttt{com.liferay.portal.kernel.util.Validator}\n  \\end{itemize}\n\\end{enumerate}\n\nNow you're ready to implement services for entries in\n\\texttt{GuestbookEntryLocalServiceImpl}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\texttt{com.liferay.docs.guestbook.service.impl} package, open\n  \\texttt{GuestbookEntryLocalServiceImpl}. Add this \\texttt{addEntry}\n  method:\n\n\\begin{verbatim}\npublic GuestbookEntry addGuestbookEntry(long userId, long guestbookId, String name,\n        String email, String message, ServiceContext serviceContext)\n    throws PortalException {\n\n    long groupId = serviceContext.getScopeGroupId();\n\n    User user = userLocalService.getUserById(userId);\n\n    Date now = new Date();\n\n    validate(name, email, message);\n\n    long entryId = counterLocalService.increment();\n\n    GuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\n    entry.setUuid(serviceContext.getUuid());\n    entry.setUserId(userId);\n    entry.setGroupId(groupId);\n    entry.setCompanyId(user.getCompanyId());\n    entry.setUserName(user.getFullName());\n    entry.setCreateDate(serviceContext.getCreateDate(now));\n    entry.setModifiedDate(serviceContext.getModifiedDate(now));\n    entry.setExpandoBridgeAttributes(serviceContext);\n    entry.setGuestbookId(guestbookId);\n    entry.setName(name);\n    entry.setEmail(email);\n    entry.setMessage(message);\n\n    guestbookEntryPersistence.update(entry);\n\n    // Calls to other Liferay frameworks go here\n\n    return entry;\n}\n\\end{verbatim}\n\n  Like the \\texttt{addGuestbook} method, \\texttt{addGuestbookEntry}\n  takes data from the current context along with data the user entered,\n  validates it, and creates a model object. That object is then\n  persisted to the database and returned.\n\\item\n  Add this \\texttt{updateGuestbookEntry} method:\n\n\\begin{verbatim}\npublic GuestbookEntry updateGuestbookEntry(long userId, long guestbookId,\n        long entryId, String name, String email, String message,\n        ServiceContext serviceContext)\n    throws PortalException, SystemException {\n\n    Date now = new Date();\n\n    validate(name, email, message);\n\n    GuestbookEntry entry =\n        guestbookEntryPersistence.findByPrimaryKey(entryId);\n\n    User user = userLocalService.getUserById(userId);\n\n    entry.setUserId(userId);\n    entry.setUserName(user.getFullName());\n    entry.setModifiedDate(serviceContext.getModifiedDate(now));\n    entry.setName(name);\n    entry.setEmail(email);\n    entry.setMessage(message);\n    entry.setExpandoBridgeAttributes(serviceContext);\n\n    guestbookEntryPersistence.update(entry);\n\n    // Integrate with Liferay frameworks here.\n\n    return entry;\n}\n\\end{verbatim}\n\n  This method first retrieves the entry and updates its data to reflect\n  what the user submitted, including its date modified.\n\\item\n  Add two \\texttt{deleteGuestbookEntry} methods:\n\n\\begin{verbatim}\npublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry entry)\n    throws PortalException {\n\n    guestbookEntryPersistence.remove(entry);\n\n    return entry;\n}\n\npublic GuestbookEntry deleteGuestbookEntry(long entryId) throws PortalException {\n\n    GuestbookEntry entry =\n        guestbookEntryPersistence.findByPrimaryKey(entryId);\n\n    return deleteGuestbookEntry(entry);\n}\n\\end{verbatim}\n\n  These methods delete entries using the \\texttt{entryId} or the object.\n  If you supply the \\texttt{entryId}, the object is retrieved and passed\n  to the method that deletes the object.\n\\item\n  Add the methods for getting \\texttt{GuestbookEntry} objects:\n\n\\begin{verbatim}\npublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId) {\n    return guestbookEntryPersistence.findByG_G(groupId, guestbookId);\n}\n\npublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n        int start, int end) throws SystemException {\n\n    return guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n            end);\n}\n\npublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n        int start, int end, OrderByComparator<GuestbookEntry> obc) {\n\n    return guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n            end, obc);\n}\n\npublic GuestbookEntry getGuestbookEntry(long entryId) throws PortalException {\n    return guestbookEntryPersistence.findByPrimaryKey(entryId);\n}\n\npublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n    return guestbookEntryPersistence.countByG_G(groupId, guestbookId);\n}\n\\end{verbatim}\n\n  These methods, like the getters in \\texttt{GuestbookLocalServiceImpl},\n  call the finders you generated with Service Builder. These\n  \\texttt{getGuestbookEntries*} methods, however, retrieve entries from\n  a specified Guestbook and Site. The first method gets a list of\n  entries. The next method gets a paginated list. The third method sorts\n  the paginated list, and the last method gets the total number of\n  entries as an integer.\n\\item\n  Add the \\texttt{validate} method:\n\n\\begin{verbatim}\nprotected void validate(String name, String email, String entry)\n    throws PortalException {\n\n    if (Validator.isNull(name)) {\n        throw new GuestbookEntryNameException();\n    }\n\n    if (!Validator.isEmailAddress(email)) {\n        throw new GuestbookEntryEmailException();\n    }\n\n    if (Validator.isNull(entry)) {\n        throw new GuestbookEntryMessageException();\n    }\n}\n\\end{verbatim}\n\n  This method makes sure the user entered relevant data when creating an\n  entry.\n\\item\n  Press {[}CTRL{]}+{[}SHIFT{]}+O to organize imports and select the\n  following classes when prompted:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{java.util.List}\n  \\item\n    \\texttt{java.util.Date}\n  \\item\n    \\texttt{com.liferay.portal.kernel.util.Validator}\n  \\end{itemize}\n\\end{enumerate}\n\nNice work! These local service methods implement the services that are\nreferenced in the portlet class.\n\n\\section{Updating Generated Classes}\\label{updating-generated-classes}\n\nNow that you've implemented the service methods, you must make them\navailable to the rest of your application. To do this, run\n\\texttt{buildService} again:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In \\emph{Gradle Tasks} → \\emph{guestbook-service} → \\emph{build},\n  right-click \\texttt{buildService} and select \\emph{Run Gradle Tasks}.\n  In the utility classes, Service Builder populates calls to your newly\n  created service methods.\n\\item\n  In the Project Explorer, right-click the \\texttt{guestbook-service}\n  module and select \\emph{Refresh}. Repeat this step for the\n  \\texttt{guestbook-api} module. This ensures that the changes made by\n  Service Builder show up in Liferay Dev Studio DXP.\n\\item\n  In the Project Explorer, right-click the \\texttt{guestbook-service}\n  module and select \\emph{Gradle} → \\emph{Refresh Gradle Project}.\n  Repeat this step for the \\texttt{guestbook-api} module. This ensures\n  that your modules' Gradle dependencies are up to date.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} If something goes awry when working with Service Builder,\nrepeat these steps to run Service Builder again and refresh your API and\nservice modules.\n\n\\noindent\\hrulefill\n\nExcellent! Your new back-end has been generated. Now it's time to create\na web application that uses it.\n\n\\chapter{Building the Web Front-End}\\label{building-the-web-front-end}\n\nYou now have a back-end: you created a \\texttt{service.xml} file to\ndefine your application's data model, and generated the back-end code\n(the model, service, and persistence layers) with Service Builder. You\nalso added service methods using the appropriate extension points: your\nentities' \\texttt{*LocalServiceImpl} classes. Now you can add a\nfront-end to match new back-end, creating a fully functional\napplication.\n\nYou'll create two portlets: the Guestbook portlet for users to use to\nadd entries and in a later step, a Guestbook Admin portlet for\nadministrators to add Guestbooks.\n\nStarting with this step,\n\\href{https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/guestbook/04-web-front-end}{source\ncode is provided} in case you get stuck.\n\nReady to begin?\n\nLet's Go!{}\n\n\\chapter{Creating the Web Project}\\label{creating-the-web-project}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Building the Web Front-End</p><p>Step 1 of 11</p>\n\\end{verbatim}\n\nYour first step is to create another Liferay Module Project. Modules are\nthe core building blocks of Liferay DXP applications. Every application\nis made from one or more modules. Each module encapsulates a functional\npiece of an application. Multiple modules form a complete application.\n\nModules can be web modules or \\href{https://www.osgi.org/}{OSGi}\nmodules. Since you'll be creating a Liferay MVC Portlet, you'll create\nan OSGI module. The OSGi container in Liferay DXP can run any OSGi\nmodule. Each module is packaged as a JAR file that contains a manifest\nfile. The manifest is needed for the container to recognize the module.\nTechnically, a module that contains only a manifest is still valid. Of\ncourse, such a module wouldn't be very interesting.\n\nYou already created Service Builder modules. Now you'll create your MVC\nPortlet module. For the purpose of this tutorial, you'll create your\nmodules inside your Liferay Workspace.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In Liferay Dev Studio DXP, select \\emph{File} → \\emph{New} →\n  \\emph{Liferay Module Project}.\n\\item\n  Complete the first screen of the wizard with the following\n  information:\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/new-module-project.png}\n  \\caption{Complete the New Module Project wizard.}\n  \\end{figure}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Enter \\texttt{guestbook-web} for the Project name.\n  \\item\n    Use the \\emph{Gradle} Build type.\n  \\item\n    The Liferay version is \\emph{7.2}.\n  \\item\n    Select \\texttt{mvc-portlet} for the Project Template.\n  \\end{itemize}\n\n  Click \\emph{Next}.\n\\item\n  On the second screen of the wizard, enter \\texttt{Guestbook} for the\n  component class name, and \\texttt{com.liferay.docs.guestbook.portlet}\n  for the package name. Click \\emph{Finish}.\n\\end{enumerate}\n\nNote that it may take a while for Dev Studio DXP to create your project,\nbecause Gradle downloads your project's dependencies for you during\nproject creation. Once this is done, you have a module project named\n\\texttt{guestbook-web}. The \\texttt{mvc-portlet} template configured the\nproject with the proper dependencies and generated all the files you\nneed to get started:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  The portlet class (in the package you specified)\n\\item\n  JSP files (in \\texttt{/src/main/resources})\n\\item\n  Language properties (also in \\texttt{/src/main/resources})\n\\end{itemize}\n\nYour new module project is a \\emph{portlet} application. You'll learn\nwhat that is in a moment, but first there's some housekeeping to do.\n\nIn larger projects, it is important to have all of your files and\nmodules well organized. Since the \\texttt{guestbook-web} module really\ngoes with your Service Builder modules, it should be in the\n\\texttt{guestbook} folder.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\emph{Project Explorer}, right-click on \\texttt{guestbook-web}\n  and select \\emph{Move}.\n\\item\n  In the window that appears, click \\emph{Browse}, choose the\n  \\texttt{guestbook} folder and then click \\emph{OK}.\n\\end{enumerate}\n\nYour \\texttt{guestbook-web} folder now appears in the structure with the\nother modules.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-refactor.png}\n\\caption{After you move it, all of your modules are in the same\nfolder..}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Sometimes Eclipse refuses to move your project. If that\nhappens, close Eclipse, use your operating system's file manager to move\nthe \\texttt{guestbook-web} folder into the \\texttt{guestbook} folder,\nand then restart Eclipse.\n\n\\noindent\\hrulefill\n\nYou're now ready to begin writing your front-end, but first some\nexplanation is in order.\n\n\\section{What is a Portlet?}\\label{what-is-a-portlet}\n\nWeb applications can be simple or complex: they might display an article\nor calculate your taxes. These applications run on a \\emph{platform}\nthat provides application developers the building blocks they need to\nmake applications.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/portlet-applications.png}\n\\caption{Many Liferay applications can run at the same time on the same\npage.}\n\\end{figure}\n\nLiferay DXP provides a platform that contains common features needed by\ntoday's applications, including user management, security, user\ninterfaces, services, and more. Portlets are one of those basic building\nblocks. Often a web application takes up the entire page. Portlets can\ndo this or share the page with many applications at the same time.\nLiferay DXP's framework takes this into account at every step.\n\n\\section{What is a Component?}\\label{what-is-a-component}\n\nLiferay MVC Portlets are \\emph{Components}. If a module (sometimes also\ncalled a \\emph{bundle}) encapsulates pieces of your application, a\ncomponent is the object that contains the core functionality. A\nComponent is managed by a component framework or container. Components\nare deployed inside modules, and they're created, started, stopped, and\ndestroyed as needed by the container. What a perfect model for a web\napplication! It can be made available only when needed, and when it's\nnot, the container can make sure it doesn't use resources needed by\nother components.\n\nIn this case, you created a Declarative Services (DS) component. With\nDeclarative Services, you declare that an object is a component, and you\ndefine data about the component so the container knows how to manage it.\nA default configuration was created for you; you'll examine it later.\n\n\\section{Deploying the Application}\\label{deploying-the-application}\n\nEven though all you've done is generate it, the \\texttt{guestbook-web}\nproject is ready to be built and deployed.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Make sure that your server is running, and if it isn't, select it in\n  Dev Studio DXP's Servers pane and click the start button\n  (\\includegraphics{./images/icon-start-server.png}).\n\\item\n  After it starts, drag and drop the \\texttt{guestbook-web} project from\n  the Project Explorer to the server.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/deploy-module.png}\n  \\caption{Drag and drop the module.}\n  \\end{figure}\n\\item\n  Open a browser and navigate to Liferay DXP\n  (\\url{http://localhost:8080} by default).\n\n  If this is your first time starting Liferay DXP, you'll go through a\n  short wizard to set up your server. In this wizard, make sure you use\n  the default database (Hypersonic). Although this database isn't\n  intended for production use, it works fine for development and\n  testing.\n\\item\n  Click the menu button at the top left and select \\emph{Site Builder} →\n  \\emph{Pages}.\n\\item\n  Click the \\includegraphics{./images/icon-add.png} button at the top\n  right to add a Public Page.\n\\item\n  Choose \\emph{Widget Page} and name it \\emph{Guestbook}.\n\\item\n  Choose the \\emph{One Column} layout and click \\emph{Save}.\n\\item\n  Click \\emph{Go to Site} on the left, and then navigate to your new\n  Guestbook page.\n\\item\n  Click \\emph{Add} (\\includegraphics{./images/icon-add-app.png}) in the\n  upper right hand corner.\n\\item\n  Select \\emph{Widgets}. In the Applications list, your application\n  appears in the Sample category. Its name is \\texttt{Guestbook}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/default-guestbook-application.png}\n\\caption{This is your new page with the Guestbook application that you\ncreated.}\n\\end{figure}\n\nNow you're ready to jump in and start developing your Guestbook portlet.\n\n\\chapter{Defining the Component Metadata\nProperties}\\label{defining-the-component-metadata-properties}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Building the Web Front-End</p><p>Step 2 of 11</p>\n\\end{verbatim}\n\nWhen users add applications to a page, they pick them from a list of\n\\emph{display categories}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/display-categories.png}\n\\caption{Users choose applications from a list of display categories.}\n\\end{figure}\n\nA portlet's display category is defined in its component class as a\nmetadata property. Since the Guestbook portlet lets users communicate\nwith each other, you'll add it to the Social category. Only one\nGuestbook portlet should be added to a page, so you'll also define it as\na \\emph{non-instanceable} portlet. Such a portlet can appear only once\non a page or Site, depending on its scope.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\texttt{GuestbookPortlet} class and update the component\n  class metadata properties to match this configuration:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n      \"com.liferay.portlet.display-category=category.social\",\n      \"com.liferay.portlet.instanceable=false\",\n      \"com.liferay.portlet.scopeable=true\",\n      \"javax.portlet.display-name=Guestbook\",\n      \"javax.portlet.expiration-cache=0\",\n      \"javax.portlet.init-param.template-path=/\",\n      \"javax.portlet.init-param.view-template=/guestbook/view.jsp\",\n      \"javax.portlet.resource-bundle=content.Language\",\n      \"javax.portlet.security-role-ref=power-user,user\",\n      \"javax.portlet.supports.mime-type=text/html\"\n    },\n    service = Portlet.class\n)\n\\end{verbatim}\n\\end{enumerate}\n\nThe \\texttt{com.liferay.portlet.display-category=category.social}\nproperty sets the Guestbook portlet's display category to \\emph{Social}.\nThe \\texttt{com.liferay.portlet.instanceable=false} property specifies\nthat the Guestbook portlet is non-instanceable, so only one instance of\nthe portlet can be added to a page. In the property\n\\texttt{javax.portlet.init-param.view-template}, you also update the\nlocation of the main \\texttt{view.jsp} to a folder in\n\\texttt{src/main/resources/META-INF/resources} called\n\\texttt{/guestbook}. You'll wind up creating two folders there for the\ntwo different portlets you'll create: \\texttt{guestbook} and\n\\texttt{guestbook-admin}. For now, just create the \\texttt{guestbook}\nfolder:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{src/main/resources}, then open \\texttt{META-INF}.\n  Right-click on the \\texttt{resources} folder and select \\emph{New} →\n  \\emph{Folder}.\n\\item\n  Name the folder \\emph{guestbook} and hit \\emph{Enter} (or click OK).\n\\item\n  Drag \\texttt{view.jsp} and drop it onto the \\texttt{guestbook} folder\n  to move it there.\n\\item\n  Open \\texttt{view.jsp} and modify the path to \\texttt{init.jsp} to\n  include it from the parent folder:\n\n\\begin{verbatim}\n<%@ include file=\"../init.jsp\" %>\n\\end{verbatim}\n\\end{enumerate}\n\nSince you edited the portlet's metadata, you must remove and re-add the\nportlet to the page before continuing:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\texttt{localhost:8080} in your web browser.\n\\item\n  Sign in to your administrative account.\n\\item\n  The Guestbook portlet now shows an error on the page. Click its\n  portlet menu (at the top-right of the portlet), then select\n  \\emph{Remove} and click \\emph{OK} to confirm.\n\\item\n  Open the \\emph{Add} menu and select \\emph{Widgets}.\n\\item\n  Open the \\emph{Social} category and drag and drop the \\emph{Guestbook}\n  widget onto the page.\n\\end{enumerate}\n\nGreat! Now the Guestbook portlet appears in an appropriate category.\nThough you were able to add it to the page before, the user experience\nis better.\n\n\\chapter{Creating Portlet Keys}\\label{creating-portlet-keys}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Building the Web Front-End</p><p>Step 3 of 11</p>\n\\end{verbatim}\n\n\\texttt{PortletKeys} manage important things like the portlet name or\nother repeatable, commonly used variables in one place. This way, if you\nmust change the portlet's name, you can do it in one place and then\nreference it in every class that needs it. Keys are created in a\n\\texttt{PortletKeys} class and then referenced in a component property.\n\nFollow these steps to create your application's \\texttt{PortletKeys}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\texttt{com.liferay.docs.guestbook.constants} package.\n\\item\n  Open \\texttt{GuestbookPortletKeys} and make sure there's a public,\n  static, final String called \\texttt{GUESTBOOK} with a value of\n  \\texttt{com\\_liferay\\_docs\\_guestbook\\_portlet\\_GuestbookPortlet}:\n\n\\begin{verbatim}\npublic static final String GUESTBOOK =\n         \"com_liferay_docs_guestbook_portlet_GuestbookPortlet\";\n\\end{verbatim}\n\\item\n  Save the file.\n\\item\n  In your \\texttt{guestbook-web} module, open the\n  \\texttt{GuestbookPortlet} class and update the component class\n  metadata properties by adding one new property:\n\n\\begin{verbatim}\n\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK,\n\\end{verbatim}\n\n  Note that you need the trailing comma if you've added the property to\n  the middle of the list. If you've added it to the end of the last,\n  leave it off (but add a trailing comma to the prior property!).\n\\item\n  Save \\texttt{GuestbookPortlet}.\n\\end{enumerate}\n\nNice job!\n\nNext, you'll integrate your application with the back-end you generated\nwith Service Builder.\n\n\\chapter{Integrating the Back-end}\\label{integrating-the-back-end}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Building the Web Front-End</p><p>Step 4 of 11</p>\n\\end{verbatim}\n\nTo use your Service Builder-generated back-end in your front-end, you\nmust tell your front-end project that the back-end exists and where to\nfind it.\n\nFor the web module to access the generated services, you must make it\naware of the API and service modules:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{guestbook-web}'s \\texttt{build.gradle} file and add these\n  dependencies:\n\n\\begin{verbatim}\ncompileOnly project(\":modules:guestbook:guestbook-api\")\ncompileOnly project(\":modules:guestbook:guestbook-service\")\n\\end{verbatim}\n\\item\n  Save the file. Right-click on the \\texttt{guestbook-web} project and\n  select \\emph{Gradle} → \\emph{Refresh Gradle Project}.\n\\item\n  Now you must add \\emph{references} to the Service Builder services you\n  need. To do this, add them as class variables with \\texttt{@Reference}\n  annotations on their setter methods. Open \\texttt{GuestbookPortlet}\n  and add these references to the bottom of the file:\n\n\\begin{verbatim}\n@Reference\nprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n@Reference\nprivate GuestbookLocalService _guestbookLocalService;\n\\end{verbatim}\n\n  Note that it's Liferay's code style to add class variables this way.\n  The \\texttt{@Reference} annotation causes Liferay's OSGi container to\n  inject references to your generated services so you can use them.\n\\item\n  Press Ctrl-Shift-O to add the import you need:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{org.osgi.service.component.annotations.Reference}\n  \\end{itemize}\n\\end{enumerate}\n\nNow you're ready to begin building your front-end.\n\n\\chapter{Creating an Add Entry\nButton}\\label{creating-an-add-entry-button}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Building the Web Front-End</p><p>Step 5 of 11</p>\n\\end{verbatim}\n\nA guestbook application is pretty simple, right? People come to your\nsite and post their names and brief messages. Others can read these\nentries and post their own.\n\nWhen you created your project, it generated a file named\n\\texttt{view.jsp}, which you've already moved to\n\\texttt{src/main/resources/META-INF/resources/guestbook} folder. This\nfile contains the default view for users when the portlet is added to\nthe page. Right now it contains sample content:\n\n\\begin{verbatim}\n<%@ include file=\"../init.jsp\" %>\n\n<p>\n    <b><liferay-ui:message key=\"guestbook-web.caption\"/></b>\n</p>\n\\end{verbatim}\n\nFirst, \\texttt{view.jsp} imports \\texttt{init.jsp}. By Liferay\nconvention, we declare imports and tag library declarations in an\n\\texttt{init.jsp} file. The other JSP files in the application import\n\\texttt{init.jsp}. This reserves JSP dependency management to a single\nfile.\n\nBesides importing \\texttt{init.jsp}, \\texttt{view.jsp} displays a\nmessage defined by a language key. This key and its value are declared\nin your project's\n\\texttt{src/main/resources/content/Language.properties} file.\n\nIt's time to start developing the Guestbook application. First, users\nneed a way to add a Guestbook entry. In \\texttt{view.jsp}, follow these\nsteps to add this button:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Remove everything under the include for \\texttt{init.jsp}.\n\\item\n  Below the include, add the following\n  \\href{http://alloyui.com/}{AlloyUI} tags to display an Add Entry\n  button inside of a button row:\n\n\\begin{verbatim}\n<aui:button-row>\n    <aui:button value=\"Add Entry\"></aui:button>\n</aui:button-row>\n\\end{verbatim}\n\\end{enumerate}\n\nYou can use \\texttt{aui} tags in \\texttt{view.jsp} since\n\\texttt{init.jsp} declares the AlloyUI tag library by default (as well\nas other important imports and tags):\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\" %>\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\" %>\n\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\" %>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\" %>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\" %>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\" %>\n\n<portlet:defineObjects />\n\n<liferay-theme:defineObjects />\n\\end{verbatim}\n\nYour application now displays a button instead of a message, but the\nbutton doesn't do anything. Next, you'll create a URL for your button.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-new-button.png}\n\\caption{Your new button is awesome, but it doesn't work yet.}\n\\end{figure}\n\n\\chapter{Generating Portlet URLs}\\label{generating-portlet-urls}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Building the Web Front-End</p><p>Step 6 of 11</p>\n\\end{verbatim}\n\nSince users can place multiple portlets on a single page, you have no\nidea what other portlets may share a page with yours. This means that\nyou can't define URLs for various functions in your application like you\notherwise would.\n\nFor example, consider a Calendar application that a user puts on the\nsame page as a Blog application. To implement the functionality for\ndeleting calendar events and blog entries in the respective application,\nboth application developers append the \\texttt{del} parameter to the URL\nand give it a primary key value so the application can look up and\ndelete the calendar event or blog entry. Since both applications read\nthis parameter, their delete functionality clashes.\n\nSystem-generated URLs prevent this. If the system generates a unique URL\nfor each piece of functionality, multiple applications can coexist in\nperfect harmony.\n\nIn \\texttt{view.jsp}, follow these steps to create system-generated URLs\nin your portlet:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add these tags below\n  \\texttt{\\textless{}\\%@\\ include\\ file=\"../init.jsp\"\\ \\%\\textgreater{}},\n  but above the \\texttt{\\textless{}aui:button-row\\textgreater{}} tag:\n\n\\begin{verbatim}\n<portlet:renderURL var=\"addEntryURL\">\n    <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\"></portlet:param>\n</portlet:renderURL>\n\\end{verbatim}\n\\item\n  Add this attribute to the \\texttt{\\textless{}aui:button\\textgreater{}}\n  tag, before \\texttt{value=\"Add\\ Entry\"}:\n\n\\begin{verbatim}\nonClick=\"<%= addEntryURL.toString() %>\"\n\\end{verbatim}\n\n  Your \\texttt{view.jsp} page should now look like this:\n\n\\begin{verbatim}\n<%@ include file=\"/init.jsp\" %>\n\n<portlet:renderURL var=\"addEntryURL\">\n    <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\"></portlet:param>\n</portlet:renderURL>\n\n<aui:button-row>\n    <aui:button onClick=\"<%= addEntryURL.toString() %>\" value=\"Add Entry\"></aui:button>\n</aui:button-row>\n\\end{verbatim}\n\\end{enumerate}\n\nThe \\texttt{\\textless{}portlet:renderURL\\textgreater{}} tag's\n\\texttt{var} attribute creates the \\texttt{addEntryURL} variable to hold\nthe system-generated URL. The\n\\texttt{\\textless{}portlet:param\\textgreater{}} tag defines a URL\nparameter to append to the URL. In this example, a URL parameter named\n\\texttt{mvcPath} with a value of \\texttt{/edit\\_entry.jsp} is appended\nto the URL.\n\nNote that your \\texttt{GuestbookPortlet} class (located in your\n\\texttt{guestbook-web} module's\n\\texttt{com.liferay.docs.guestbook.portlet} package) extends Liferay's\n\\texttt{MVCPortlet} class. In a\n\\href{/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet}{Liferay\nMVC portlet}, the \\texttt{mvcPath} URL parameter indicates a page within\nyour portlet. To navigate to another page in your portlet, use a portal\nURL with the parameter \\texttt{mvcPath} to link to the specific page.\n\nIn the example above, you created a \\texttt{renderURL} that points to\nyour application's \\texttt{edit\\_entry.jsp} page, which you haven't yet\ncreated. Note that using an AlloyUI button to follow the generated URL\nisn't required. You can use any HTML construct that contains a link.\nUsers can click your button to access your application's\n\\texttt{edit\\_entry.jsp} page. This currently produces an error since no\n\\texttt{edit\\_entry.jsp} exists yet. Creating \\texttt{edit\\_entry.jsp}\nis your next step.\n\n\\chapter{Linking to Another Page}\\label{linking-to-another-page}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Building the Web Front-End</p><p>Step 7 of 11</p>\n\\end{verbatim}\n\nIn the same folder your \\texttt{view.jsp} is in, create the\n\\texttt{edit\\_entry.jsp} file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Right-click your project's\n  \\texttt{src/main/resources/META-INF/resources/guestbook} folder and\n  choose \\emph{New} → \\emph{File}.\n\\item\n  Name the file \\texttt{edit\\_entry.jsp} and click \\emph{Finish}.\n\\item\n  Add this line to the top of the file:\n\n\\begin{verbatim}\n<%@ include file=\"../init.jsp\" %>\n\\end{verbatim}\n\n  Remember, it's a best practice to add all JSP imports and tag library\n  declarations to a single file that's imported by your application's\n  other JSP files. For \\texttt{edit\\_entry.jsp}, you need these imports\n  to access the portlet tags that create URLs and the Alloy tags that\n  create the form.\n\\item\n  Next, you need a scriptlet that helps determine the function the user\n  accessed. You named this JSP \\texttt{edit\\_entry.jsp} because it's\n  used both for adding and editing. Add this scriptlet to add logic for\n  determining which function the user wants:\n\n\\begin{verbatim}\n<% \n\nlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\nGuestbookEntry entry = null;\nif (entryId > 0) {\n  entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n}\n\nlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n%>\n\\end{verbatim}\n\n  If an \\texttt{entryId} greater than \\texttt{0} is found in the\n  request, editing a \\texttt{GuestbookEntry} is assumed. Otherwise, the\n  user is adding.\n\\item\n  You'll create two URLs: one in the next step to submit the form and\n  one in this step to go back to \\texttt{view.jsp}. To create the URL to\n  go back to \\texttt{view.jsp}, add the following tag below the first\n  line you added:\n\n\\begin{verbatim}\n<portlet:renderURL var=\"viewURL\">\n    <portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\"></portlet:param>\n</portlet:renderURL>\n\\end{verbatim}\n\\end{enumerate}\n\nNext, you must create a new URL for submitting the form. This is a\ndifferent kind of URL, for it triggers a portlet action.\n\n\\chapter{Forms and Action URLs}\\label{forms-and-action-urls}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Building the Web Front-End/p><p>Step 8 of 11</p>\n\\end{verbatim}\n\nRecall that portlets run in a portion of a page, and a page can contain\nmultiple portlets. Because of this, portlets have \\emph{phases} of\noperation. Here, you'll learn about the most important two. The first\nphase is the one you've already used: the \\emph{render} phase. All this\nmeans is that the portlet draws itself, using the JSPs you write for it.\n\nThe other phase is called the \\emph{action} phase. This phase runs once,\nwhen a user triggers a portlet action. The portlet performs whatever\naction the user triggered, such as performing a search or adding a\nrecord to a database. Then the portlet goes back to the render phase and\nre-renders itself according to its new state.\n\n\\section{Action URLs}\\label{action-urls}\n\nTo save a guestbook entry, you must trigger a portlet action. For this,\nyou'll create an action URL.\n\nAdd the following tag in \\texttt{edit\\_entry.jsp} after the closing\n\\texttt{\\textless{}/portlet:renderURL\\textgreater{}} tag:\n\n\\begin{verbatim}\n<portlet:actionURL name=\"addEntry\" var=\"addEntryURL\" />\n\\end{verbatim}\n\nYou now have the two required URLs for your form.\n\n\\section{Forms}\\label{forms}\n\nThe form for creating guestbook entries has three fields: one for the\nname of the person submitting the entry, one for the person's email\naddress, and one for the entry itself.\n\nAdd the following tags to the end of your \\texttt{edit\\_entry.jsp} file:\n\n\\begin{verbatim}\n<aui:form action=\"<%= addEntryURL %>\" name=\"<portlet:namespace />fm\">\n\n<aui:model-context bean=\"<%= entry %>\" model=\"<%= GuestbookEntry.class %>\" />\n\n    <aui:fieldset>\n\n        <aui:input name=\"name\" />\n        <aui:input name=\"email\" />\n        <aui:input name=\"message\" />\n        <aui:input name=\"entryId\" type=\"hidden\" />\n        <aui:input name=\"guestbookId\" type=\"hidden\" value='<%= entry == null ? guestbookId : entry.getGuestbookId() %>'/>\n\n    </aui:fieldset>\n\n    <aui:button-row>\n\n        <aui:button type=\"submit\"></aui:button>\n        <aui:button type=\"cancel\" onClick=\"<%= viewURL.toString() %>\"></aui:button>\n\n    </aui:button-row>\n</aui:form>\n\\end{verbatim}\n\nLiferay uses its Alloy UI tag library to create forms.\n\nSave \\texttt{edit\\_entry.jsp} and redeploy your application. If you\nrefresh the page and click the \\emph{Add Entry} button, your form\nappears. If you click the \\emph{Cancel} button, you go back to\n\\texttt{view.jsp}, but don't try the \\emph{Save} button yet. You haven't\nyet created the action that saves a guestbook entry, so clicking\n\\emph{Save} produces an error.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/first-guestbook-portlet-edit-entry.png}\n\\caption{This is the Guestbook application's form for adding entries.}\n\\end{figure}\n\nImplementing portlet actions (what happens when the user clicks\n\\emph{Save} or \\emph{Delete}) is your next task.\n\n\\chapter{Implementing Portlet\nActions}\\label{implementing-portlet-actions}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Building the Web Front-End</p><p>Step 9 of 11</p>\n\\end{verbatim}\n\nWhen users submit the form, your application stores the form data for\ndisplay in the guestbook. This is where you call the back-end you\ngenerated to store the data for later retrieval in the database.\n\nTo make your portlet do anything other than re-render itself, you must\nimplement portlet actions. An action defines some processing, usually\nbased on user input, that the portlet must perform before it renders\nitself. In the case of the guestbook portlet, the action you'll\nimplement next saves a guestbook entry that a user typed into the form.\nSaved guestbook entries can be retrieved and displayed later.\n\nSince you're using Liferay's MVC Portlet framework, you have an easy way\nto implement actions. Portlet actions are implemented in the portlet\nclass, which is the controller. In the form you just created, you made\nan action URL, and you called it \\texttt{addEntry}. To create a portlet\naction, you create a method in the portlet class with the same name.\n\\texttt{MVCPortlet} calls that method when a user triggers its matching\nURL.\n\n\\section{Creating an Add Entry\nAction}\\label{creating-an-add-entry-action}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{GuestbookPortlet}.\n\\item\n  Create a method with the following signature:\n\n\\begin{verbatim}\npublic void addEntry(ActionRequest request, ActionResponse response) {\n\n}\n\\end{verbatim}\n\\item\n  Press {[}CTRL{]}+{[}SHIFT{]}+O to organize imports and import the\n  required \\texttt{javax.portlet.ActionRequest} and\n  \\texttt{javax.portlet.ActionResponse} classes.\n\\end{enumerate}\n\nYou've now created a portlet action. It doesn't do anything, but at\nleast you won't get an error now if you submit your form. Next, you\nshould make the action save the form data.\n\nThe following method implements adding a guestbook entry using the\nback-end you generated with Service Builder:\n\n\\begin{verbatim}\npublic void addEntry(ActionRequest request, ActionResponse response)\n            throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            GuestbookEntry.class.getName(), request);\n\n        String userName = ParamUtil.getString(request, \"name\");\n        String email = ParamUtil.getString(request, \"email\");\n        String message = ParamUtil.getString(request, \"message\");\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n        long entryId = ParamUtil.getLong(request, \"entryId\");\n\n    if (entryId > 0) {\n\n        try {\n\n            _guestbookEntryLocalService.updateGuestbookEntry(\n                serviceContext.getUserId(), guestbookId, entryId, userName,\n                email, message, serviceContext);\n\n            response.setRenderParameter(\n                \"guestbookId\", Long.toString(guestbookId));\n\n        }\n        catch (Exception e) {\n            System.out.println(e);\n\n            PortalUtil.copyRequestParameters(request, response);\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook/edit_entry.jsp\");\n        }\n\n    }\n    else {\n\n        try {\n            _guestbookEntryLocalService.addGuestbookEntry(\n                serviceContext.getUserId(), guestbookId, userName, email,\n                message, serviceContext);\n\n            response.setRenderParameter(\n                \"guestbookId\", Long.toString(guestbookId));\n\n        }\n        catch (Exception e) {\n            System.out.println(e);\n\n            PortalUtil.copyRequestParameters(request, response);\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook/edit_entry.jsp\");\n        }\n    }\n}\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Replace your existing \\texttt{addEntry} method with the above method.\n\\item\n  Press {[}CTRL{]}+{[}SHIFT{]}+O to organize imports.\n\\end{enumerate}\n\nThis \\texttt{addEntry} method gets the name, message, and email fields\nthat the user submits in the JSP and passes them to the service to be\nstored as entry data. It also gets a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/understanding-servicecontext}{\\texttt{ServiceContext}}\nso information about the request's current context can be retrieved,\nsuch as the ID of the current user. The \\texttt{if-else} logic checks\nwhether there's an existing \\texttt{entryId}. If there is, the\n\\texttt{update} service method is called, and if not, the \\texttt{add}\nservice method is called. In both cases, it sets a render parameter with\nthe Guestbook ID so the application can display the guestbook's entries\nafter this one has been added. This is all done in \\texttt{try...catch}\nstatements. Note the setting of the \\texttt{mvcPath} render parameter to\ndirect processing to the proper JSP based on what happens.\n\n\\section{Creating a Delete Entry\nAction}\\label{creating-a-delete-entry-action}\n\nNext, create an action that deletes an entry:\n\n\\begin{verbatim}\npublic void deleteEntry(ActionRequest request, ActionResponse response) throws PortalException {\n        long entryId = ParamUtil.getLong(request, \"entryId\");\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            GuestbookEntry.class.getName(), request);\n\n        try {\n\n            response.setRenderParameter(\n                \"guestbookId\", Long.toString(guestbookId));\n\n            _guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n        }\n\n        catch (Exception e) {\n            Logger.getLogger(GuestbookPortlet.class.getName()).log(\n                Level.SEVERE, null, e);\n        }\n}\n\\end{verbatim}\n\nThis action accepts an \\texttt{entryId} from the request and calls the\nservice to delete it.\n\nBut there's something missing, isn't there? These methods expect a\n\\texttt{guestbookId} to be in the request, so the\n\\texttt{GuestbookEntry} can be connected to its \\texttt{Guestbook}.\nYou'll create that next, as well as a mechanism for viewing guestbook\nentries.\n\n\\chapter{Displaying Guestbook\nEntries}\\label{displaying-guestbook-entries}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Building the Web Front-End</p><p>Step 10 of 11</p>\n\\end{verbatim}\n\nTo display guestbook entries, you must do the reverse of what you did to\nstore them: retrieve them the database, loop through them, and present\nthem on the page. To do this, you must override the default MVC Portlet\n\\texttt{render} method so you can tell your portlet how to render\nitself.\n\n\\section{Rendering the Portlet}\\label{rendering-the-portlet}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the following \\texttt{render} method to \\texttt{GuestbookPortlet}:\n\n\\begin{verbatim}\n@Override\npublic void render(RenderRequest renderRequest, RenderResponse renderResponse)\n    throws IOException, PortletException {\n\n    try {\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), renderRequest);\n\n        long groupId = serviceContext.getScopeGroupId();\n\n        long guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n        List<Guestbook> guestbooks = _guestbookLocalService.getGuestbooks(\n            groupId);\n\n        if (guestbooks.isEmpty()) {\n            Guestbook guestbook = _guestbookLocalService.addGuestbook(\n                serviceContext.getUserId(), \"Main\", serviceContext);\n\n            guestbookId = guestbook.getGuestbookId();\n        }\n\n        if (guestbookId == 0) {\n            guestbookId = guestbooks.get(0).getGuestbookId();\n        }\n\n        renderRequest.setAttribute(\"guestbookId\", guestbookId);\n    }\n    catch (Exception e) {\n        throw new PortletException(e);\n    }\n\n    super.render(renderRequest, renderResponse);\n}\n\\end{verbatim}\n\n  This \\texttt{render} method checks for guestbooks in the current Site.\n  If there aren't any, it creates one. The \\texttt{guestbookId} that it\n  has (either the first one or one that has been selected in\n  functionality you haven't written yet) is set in the request object so\n  that it becomes the current guestbook.\n\\item\n  Press {[}CTRL{]}+{[}SHIFT{]}+O to organize imports and then save the\n  file.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\nNote: When you are prompted to choose imports, here are some guidelines:\n\n\\begin{itemize}\n\\item\n  Always use \\texttt{org.osgi...} packages instead of\n  \\texttt{aQute.bnd...}\n\\item\n  Generally use \\texttt{java.util...} or \\texttt{javax.portlet...}\n  packages.\n\\item\n  You never use \\texttt{java.awt...} in this project.\n\\item\n  Only use \\texttt{com.liferay...} when it is for a Liferay specific\n  implementation or your custom implementation of a concept.\n\\end{itemize}\n\nFor example:\n\n\\begin{itemize}\n\\item\n  If you are given the choice between \\texttt{javax.portlet.Portlet} and\n  \\texttt{com.liferay.portlet.Portlet} choose\n  \\texttt{javax.portlet.Portlet}.\n\\item\n  If you are given the choice between \\texttt{org.osgi.component} and\n  \\texttt{aQute.bnd.annotation.component} choose\n  \\texttt{org.osgi.component}\n\\end{itemize}\n\nIf at some point you think you chose an incorrect import, but you're not\nsure what it might be, you can erase all of the imports from the file\nand press {[}CTRL{]}+{[}SHIFT{]}+O again and see if you can identify\nwhere you went wrong.\n\n\\noindent\\hrulefill\n\nNow that you have your controller preparing your data for display, your\nnext step is to implement the view so users can see guestbook entries.\n\n\\section{Displaying Guestbook\nEntries}\\label{displaying-guestbook-entries-1}\n\nLiferay's development framework makes it easy to loop through data and\ndisplay it nicely to the end user. You'll use a Liferay UI construct\ncalled \\emph{Search Container} to make this happen.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Replace the contents of \\texttt{view.jsp} with this code:\n\n\\begin{verbatim}\n<%@include file=\"../init.jsp\"%>\n\n<%\nlong guestbookId = Long.valueOf((Long) renderRequest\n        .getAttribute(\"guestbookId\"));\n%>\n\n<aui:button-row cssClass=\"guestbook-buttons\">\n\n    <portlet:renderURL var=\"addEntryURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n        <portlet:param name=\"guestbookId\"\n            value=\"<%=String.valueOf(guestbookId)%>\" />\n    </portlet:renderURL>\n\n    <aui:button onClick=\"<%=addEntryURL.toString()%>\" value=\"Add Entry\"></aui:button>\n\n</aui:button-row>\n\n<liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntriesCount()%>\">\n<liferay-ui:search-container-results\n    results=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(scopeGroupId.longValue(),\n                    guestbookId, searchContainer.getStart(),\n                    searchContainer.getEnd())%>\" />\n\n<liferay-ui:search-container-row\n    className=\"com.liferay.docs.guestbook.model.GuestbookEntry\" modelVar=\"entry\">\n\n    <liferay-ui:search-container-column-text property=\"message\" />\n\n    <liferay-ui:search-container-column-text property=\"name\" />\n\n    <liferay-ui:search-container-column-jsp\n        align=\"right\"\n        path=\"/guestbook/entry_actions.jsp\" />\n\n</liferay-ui:search-container-row>\n\n<liferay-ui:search-iterator />\n\n</liferay-ui:search-container>\n\\end{verbatim}\n\\item\n  You've used a lot of new objects in this JSP, so you must declare them\n  in \\texttt{init.jsp}. Replace the contents of \\texttt{init.jsp} with\n  this:\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\"%>\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\"%>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n<%@ taglib uri=\"http://liferay.com/tld/security\" prefix=\"liferay-security\" %>\n\n<%@ page import=\"java.util.List\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.ParamUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.HtmlUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.petra.string.StringPool\" %>\n<%@ page import=\"com.liferay.portal.kernel.model.PersistedModel\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.SearchEntry\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.ResultRow\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.Guestbook\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.GuestbookEntry\" %>\n\n<liferay-theme:defineObjects />\n\n<portlet:defineObjects />\n\\end{verbatim}\n\\end{enumerate}\n\nMany of these objects, such as \\texttt{HtmlUtil}, \\texttt{ParamUtil},\nand \\texttt{StringPool}, are Liferay helper utilities that enable you\nwith a single line of code do things like extract parameters, escape\ndata, or provide \\texttt{String}s that otherwise have to be escaped.\n\nSave your work.\n\n\\section{Creating an Actions JSP}\\label{creating-an-actions-jsp}\n\nActions can be performed on your entities once they're stored. Users who\nenter Guestbook entries may wish to edit them or delete them. Now you'll\nprovide that functionality.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Right-click on the\n  \\texttt{src/main/resources/META-INF/resources/guestbook} folder and\n  select \\emph{New} → \\emph{File}.\n\\item\n  Name the file \\texttt{entry\\_actions.jsp}.\n\\item\n  Paste the following code into the file:\n\n\\begin{verbatim}\n<%@include file=\"../init.jsp\"%>\n\n<%\nString mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\nResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);\n\nGuestbookEntry entry = (GuestbookEntry)row.getObject();\n%>\n\n<liferay-ui:icon-menu>\n\n        <portlet:renderURL var=\"editURL\">\n            <portlet:param name=\"entryId\"\n                value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n            <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n        </portlet:renderURL>\n\n        <liferay-ui:icon image=\"edit\" message=\"Edit\"\n            url=\"<%=editURL.toString() %>\" />\n\n        <portlet:actionURL name=\"deleteEntry\" var=\"deleteURL\">\n            <portlet:param name=\"entryId\"\n                value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n            <portlet:param name=\"guestbookId\"\n                value=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n        </portlet:actionURL>\n\n        <liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n</liferay-ui:icon-menu>\n\\end{verbatim}\n\\end{enumerate}\n\nYou may have noticed this JSP was included in the Search Container rows\nin your \\texttt{view.jsp}. As the Search Container loops through\nGuestbook entries, this JSP generates an Actions button for each of them\ncontaining two functions: a call to your \\texttt{addEntry} method (which\nboth adds and edits) and a call to your \\texttt{deleteEntry} method.\nBoth calls supply the current \\texttt{guestbookId} and \\texttt{entryId}\nparameters so the Action method has everything it needs to call the\nservice method that does the work.\n\nAwesome! You've now completed the first iteration of your Guestbook\napplication.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-prototype-form.png}\n\\caption{You have a form to enter information.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-prototype-container.png}\n\\caption{Submitted entries are displayed here.}\n\\end{figure}\n\nNext you'll review what's been done so far, and you'll deploy and test\nyour application.\n\n\\chapter{Fitting it All Together}\\label{fitting-it-all-together}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Building the Web Front-End</p><p>Step 11 of 11</p>\n\\end{verbatim}\n\nYou've created a complete data-driven application from the back-end to\nthe display. It's a great time to review how everything connects\ntogether.\n\n\\section{The Back-End}\\label{the-back-end}\n\nThe first thing you did was generate back-end services for your\napplication using Liferay's object-relational mapper, Service Builder.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/service-builder-guestbook.png}\n\\caption{Service Builder makes generating your database entities and\nyour Java objects a snap.}\n\\end{figure}\n\nUsing a single \\texttt{service.xml} file, you generated your object\nmodel, SQL to create your tables, a persistence layer to perform all\nCRUD operations on your data, and a service layer for your business\nlogic. Then you added business logic to your service layer to expose the\npersistence layer's functionality according to your business rules.\n\nWith all of that ready, it was time to build a front-end client.\n\n\\section{The Front-End}\\label{the-front-end}\n\nOnce you completed your back-end, you decided to develop a web front-end\nusing Liferay's MVC Portlet framework. You generated a Liferay MVC\nPortlet project and used its Model View Controller development paradigm\nto implement a user interface for your back-end functionality.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-mvc-diagram-1.png}\n\\caption{The controller directs page flow in an MVC Portlet\napplication.}\n\\end{figure}\n\nFirst, you created a default view in \\texttt{view.jsp}. You created a\nbutton there that links to \\texttt{edit\\_entry.jsp}, which is used for\nboth adding and editing Guestbook entries (though you haven't\nimplemented editing yet). Here, you created a form to capture\ninformation from Users adding Guestbook entries. The form's Action URL\ndirects processing to your controller's portlet action of the same name.\nThis action extracts the data from the form (in the\n\\texttt{ActionRequest} object) and passes it to the business logic you\ncreated in your service layer.\n\nAfter the business logic runs, your controller passes processing back to\n\\texttt{view.jsp}, where the new Guestbook entry is displayed.\n\nNow that you've built the application and you can see a clear picture of\nhow it all works, it's time to test it.\n\n\\section{Deploying and Testing the\nApplication}\\label{deploying-and-testing-the-application}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Drag and drop the \\texttt{guestbook-api} module onto the server.\n\\item\n  Drag and drop the \\texttt{guestbook-service} module onto the server.\n\\item\n  Look for the STARTED messages from the console.\n\\item\n  Go to your Liferay DXP instance at \\texttt{localhost:8080} in your\n  browser to test your updated application.\n\\item\n  Like you did before, use your administrative account to remove the\n  Guestbook from the page and add it again.\n\\item\n  Click \\emph{Add Entry}.\n\\item\n  Enter a \\emph{Name}, \\emph{Message}, and \\emph{Email Address}.\n\\item\n  Click \\emph{Submit}.\n\\item\n  Verify that your entry appears.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-entry-test.png}\n\\caption{Your first guestbook and entry appears. Nice job!}\n\\end{figure}\n\n\\section{What's Next?}\\label{whats-next}\n\nYou've created a working web application and deployed it on Liferay DXP.\nIf you've created web applications before, though, you know that it's\nmissing some important features: security, front-end validation, and an\ninterface for administrators to create multiple guestbooks per portlet.\nIn the next section, you'll begin adding these features.\n\n\\chapter{Writing an Administrative\nPortlet}\\label{writing-an-administrative-portlet}\n\nThe Guestbook application lets users add and view guestbook entries. The\napplication's back-end, however, is much more powerful. It can support\nmany guestbooks and their associated entries. But at this point, there's\nno UI to support these added features. When you create this UI, you must\nalso make sure that only administrators can add guestbooks.\n\nTo accomplish this, you'll create a Guestbook Admin portlet and place it\nin Liferay DXP's administrative interface---specifically, within the\nContent menu. This way, the Guestbook Admin portlet is accessible only\nto Site Administrators, while users use the Guestbook portlet to create\nentries.\n\nIn short, this is a simple application with a simple interface:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/admin-app-start.png}\n\\caption{The Guestbook Admin portlet lets administrators manage\nGuestbooks.}\n\\end{figure}\n\nIf you get stuck,\n\\href{https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/guestbook/05-admin-portlet}{source\ncode} for this step is provided.\n\nAre you ready to begin?\n\nLet's Go!{}\n\n\\chapter{Creating the Classes}\\label{creating-the-classes}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Writing the Guestbook Admin App</p><p>Step 1 of 6</p>\n\\end{verbatim}\n\nBecause the Guestbook and Guestbook Admin applications should be bundled\ntogether, you'll create the new application inside the\n\\texttt{guestbook-web} project. If you disagree with this design\ndecision, you can create a separate project for Guestbook Admin; the\nproject template you'd use is \\emph{panel-app}. For now, however, it's\nbetter to go through the process manually to learn how it all works:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Right-click the \\texttt{com.liferay.docs.guestbook.portlet} package in\n  the \\texttt{guestbook-web} project and select \\emph{New} →\n  \\emph{Class}.\n\\item\n  Name the class \\texttt{GuestbookAdminPortlet}.\n\\item\n  Click \\emph{Browse} next to the Superclass and search for\n  \\texttt{MVCPortlet}. Click it and select \\emph{OK}.\n\\item\n  Click \\emph{Finish}.\n\\end{enumerate}\n\nYou now have your Guestbook Admin application's portlet class. For an\nadministrative application, however, you need at least one more\ncomponent.\n\n\\section{Panels and Categories}\\label{panels-and-categories}\n\nAs described in the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu}{product\nmenu tutorial}, there are three sections of the product menu as\nillustrated below.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/product-menu-parts.png}\n\\caption{The product menu is split into three sections: the Control\nPanel, the User menu, and the Sites menu.}\n\\end{figure}\n\nEach section is called a \\emph{panel category}. A panel category can\nhold various menu items called \\emph{panel apps}. In the illustration\nabove, the Sites menu is open to reveal its panel apps and categories\n(yes, you can nest them).\n\nThe most natural place for the Guestbook Admin portlet is in the\n\\emph{Content \\& Data} panel category with Liferay DXP's other\ncontent-based apps. This integrates it nicely in the spot where Site\nadministrators expect it to be. This also means you don't have to create\na new category for it: you can just create the panel entry, which is\nwhat you'll do next. If you'd like to learn more about panel categories\nand apps after this, see the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu}{product\nmenu tutorial} and the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-the-control-menu}{control\nmenu tutorial}.\n\nFollow these steps to create the panel entry for the Guestbook Admin\nportlet:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the dependency you need to extend Liferay DXP's panel categories\n  and apps. To do this, open \\texttt{guestbook-web}'s\n  \\texttt{build.gradle} file and add these dependencies:\n\n\\begin{verbatim}\ncompileOnly group: \"com.liferay\", name: \"com.liferay.application.list.api\"\ncompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\"\n\\end{verbatim}\n\\item\n  After saving the file, right-click \\texttt{guestbook-web} and select\n  \\emph{Gradle} → \\emph{Refresh Gradle Project}.\n\\item\n  Right-click \\texttt{src/main/java} in the \\texttt{guestbook-web}\n  project and select \\emph{New} → \\emph{Package}. Name the package\n  \\texttt{com.liferay.docs.guestbook.application.list} and click\n  \\emph{Finish}.\n\\item\n  Right-click your new package and select \\emph{New} → \\emph{Class}.\n  Name the class \\texttt{GuestbookAdminPanelApp}.\n\\item\n  Click \\emph{Browse} next to Superclass, search for\n  \\texttt{BasePanelApp}, select it, and click \\emph{OK}. Then click\n  \\emph{Finish}.\n\\end{enumerate}\n\nGreat! You've created the classes you need, and you're ready to begin\nworking on them.\n\n\\chapter{Adding Metadata}\\label{adding-metadata}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Writing the Guestbook Admin App</p><p>Step 2 of 6</p>\n\\end{verbatim}\n\nNow that you've generated the classes, you must turn them into OSGi\ncomponents. Remember that because components are container-managed\nobjects, you must provide metadata that tells Liferay DXP's OSGi\ncontainer how to manage their life cycles.\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the following portlet key to the \\texttt{GuestbookPortletKeys}\n  class (it's in the \\texttt{com.liferay.docs.guestbook.constants}\n  package):\n\n\\begin{verbatim}\npublic static final String GUESTBOOK_ADMIN =\n  \"com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet\";\n\\end{verbatim}\n\n  Save the file.\n\\item\n  Open the \\texttt{GuestbookAdminPortlet} class and add the\n  \\texttt{@Component} annotation immediately above the class\n  declaration:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n            \"com.liferay.portlet.display-category=category.hidden\",\n            \"com.liferay.portlet.scopeable=true\",\n            \"javax.portlet.display-name=Guestbooks\",\n            \"javax.portlet.expiration-cache=0\",\n            \"javax.portlet.init-param.portlet-title-based-navigation=true\",\n            \"javax.portlet.init-param.template-path=/\",\n            \"javax.portlet.init-param.view-template=/guestbook_admin/view.jsp\",\n            \"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN,\n            \"javax.portlet.resource-bundle=content.Language\",\n            \"javax.portlet.security-role-ref=administrator\",\n            \"javax.portlet.supports.mime-type=text/html\",\n            \"com.liferay.portlet.add-default-resource=true\"\n    },\n    service = Portlet.class\n)\n\\end{verbatim}\n\\item\n  Hit {[}CTRL{]}+{[}SHIFT{]}+O to add the \\texttt{javax.portlet.Portlet}\n  and other imports.\n\\end{enumerate}\n\nThere are only a few new things here. Note the value of the\n\\texttt{javax.portlet.display-name} property: \\texttt{Guestbooks}. This\nis the name that appears in the Site menu. Also note the value of the\n\\texttt{javax.portlet.name} property:\n\\texttt{+\\ GuestbookPortletKeys.GUESTBOOK\\_ADMIN}. This specifies the\nportlet's title via the \\texttt{GUESTBOOK\\_ADMIN} portlet key that you\njust created.\n\nPay special attention to the following metadata property:\n\n\\begin{verbatim}\ncom.liferay.portlet.display-category=category.hidden\n\\end{verbatim}\n\nThis is the same property you used before with the Guestbook portlet.\nYou placed that portlet in the Social category. The value\n\\texttt{category.hidden} specifies a special category that doesn't\nappear anywhere. The Guestbook Admin portlet goes here because you don't\nwant users adding it to a page. This prevents them from doing that.\n\nNext, you can configure the Panel app class. Follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\texttt{GuestbookAdminPanelApp} class and add the\n  \\texttt{@Component} annotation immediately above the class\n  declaration:\n\n\\begin{verbatim}\n@Component(\n    immediate = true,\n    property = {\n        \"panel.app.order:Integer=300\",\n        \"panel.category.key=\" + PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT\n    },\n    service = PanelApp.class\n)\n\\end{verbatim}\n\n  The \\texttt{panel.category.key} metadata property determines where to\n  place the Guestbook Admin portlet in the Product Menu. Remember that\n  the Product Menu is divided into three main sections: the Control\n  Panel, the User Menu, and the Site Administration area. The value of\n  the \\texttt{panel.category.key} property is\n  \\texttt{PanelCategoryKeys.SITE\\_ADMINISTRATION\\_CONTENT}, which means\n  Guestbook Admin is in \\emph{Site Builder} → \\emph{Content \\& Data}.\n  The key is provided by\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/application-list/application-list-api/src/main/java/com/liferay/application/list/constants/PanelCategoryKeys.java}{the\n  \\texttt{PanelCategoryKeys} class}. The \\texttt{panel.app.order} value\n  determines the rank for the Guestbook Admin portlet in the list.\n\\item\n  Finally, update the class to use the proper name and portlet keys:\n\n\\begin{verbatim}\npublic class GuestbookAdminPanelApp extends BasePanelApp {\n\n    @Override\n    public String getPortletId() {\n        return GuestbookPortletKeys.GUESTBOOK_ADMIN;\n    }\n\n    @Override\n    @Reference(\n        target = \"(javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN + \")\",\n        unbind = \"-\"\n    )\n    public void setPortlet(Portlet portlet) {\n        super.setPortlet(portlet);\n    }\n\n}\n\\end{verbatim}\n\\item\n  Hit {[}CTRL{]}+{[}SHIFT{]}+O to organize imports. This time, import\n  \\texttt{com.liferay.portal.kernel.model.Portlet} instead of\n  \\texttt{javax.portlet.Portlet}.\n\\end{enumerate}\n\nNow that the configuration is out of the way, you're free to implement\nthe app's functionality: adding, editing, and deleting Guestbooks.\nThat's the next step.\n\n\\chapter{Updating Your Service Layer}\\label{updating-your-service-layer}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Writing the Guestbook Admin App</p><p>Step 3 of 6</p>\n\\end{verbatim}\n\nEarlier, you wrote an \\texttt{addGuestbook} service method in\n\\texttt{GuestbookLocalServiceImpl} and only used it to add a default\nguestbook. To have full functionality over guestbooks, you must also add\nmethods for updating and deleting guestbooks, as well as for returning\nthe number of guestbooks in a Site.\n\n\\section{Adding Guestbook Service\nMethods}\\label{adding-guestbook-service-methods}\n\nRemember that when working with Service Builder, you define your service\nin the \\texttt{*Impl} classes. After you add, remove, or change the\nsignature of a method in an \\texttt{*Impl} class, you must run Service\nBuilder. Service Builder updates the affected interfaces and any other\ngenerated code.\n\nFollow these steps to add the required guestbook service methods:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\texttt{guestbook-service} project and open\n  \\texttt{GuestbookLocalServiceImpl.java} in the\n  \\texttt{com.liferay.docs.guestbook.service.impl} package. Add the\n  following method for updating a guestbook:\n\n\\begin{verbatim}\npublic Guestbook updateGuestbook(long userId, long guestbookId,\n    String name, ServiceContext serviceContext) throws PortalException,\n                SystemException {\n\n        Date now = new Date();\n\n        validate(name);\n\n        Guestbook guestbook = getGuestbook(guestbookId);\n\n        User user = userLocalService.getUser(userId);\n\n        guestbook.setUserId(userId);\n        guestbook.setUserName(user.getFullName());\n        guestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n        guestbook.setName(name);\n        guestbook.setExpandoBridgeAttributes(serviceContext);\n\n        guestbookPersistence.update(guestbook);\n\n        return guestbook;\n}\n\\end{verbatim}\n\n  The \\texttt{updateGuestbook} method retrieves the \\texttt{Guestbook}\n  by its ID, replaces its data with what the user entered, and then\n  calls the persistence layer to save it back to the database.\n\\item\n  Next, add the following method for deleting a guestbook:\n\n\\begin{verbatim}\npublic Guestbook deleteGuestbook(long guestbookId,\n                ServiceContext serviceContext) throws PortalException,\n                SystemException {\n\n        Guestbook guestbook = getGuestbook(guestbookId);\n\n        List<GuestbookEntry> entries = _guestbookEntryLocalService.getGuestbookEntries(\n                        serviceContext.getScopeGroupId(), guestbookId);\n\n        for (GuestbookEntry entry : entries) {\n                _guestbookEntryLocalService.deleteGuestbookEntry(entry.getEntryId());\n        }\n\n        guestbook = deleteGuestbook(guestbook);\n\n        return guestbook;\n}\n\\end{verbatim}\n\n  It's important to consider what should happen if you delete a\n  guestbook that has existing entries. If you only deleted the\n  guestbook, the guestbook's orphaned entries would still exist in the\n  database. Your \\texttt{deleteGuestbook} service method makes a service\n  call to delete a guestbook's entries before deleting that guestbook.\n  This way, guestbook entries are never orphaned.\n\\item\n  Add a reference to the \\texttt{GuestbookEntry} local service, so it\n  can be injected and used by the \\texttt{deleteGuestbook} method:\n\n\\begin{verbatim}\n@Reference\nprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\\end{verbatim}\n\n  By convention, Liferay adds these to the bottom of the class.\n\\item\n  Use {[}CTRL{]}+{[}SHIFT{]}+O to update your imports, choosing\n  \\texttt{com.liferay.portal.kernel.exception.SystemException}, and then\n  save \\texttt{GuestbookLocalServiceImpl.java}.\n\\item\n  In the Gradle Tasks pane on the right side in Liferay Dev Studio DXP,\n  run Service Builder by opening the \\texttt{guestbook-service} module\n  and double-clicking \\texttt{buildService}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you prefer, you can use\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{Blade CLI} to\nrun your Gradle tasks. If you have Blade CLI installed, go to the\n\\texttt{guestbook-service} folder on your CLI and enter the command\n\\texttt{blade\\ gw\\ \\ buildService}. This runs Service Builder to build\nyour services outside of Eclipse.\n\n\\noindent\\hrulefill\n\nNow that you've finished updating the service layer, it's time to work\non the Guestbook Admin portlet itself.\n\n\\chapter{Defining Portlet Actions}\\label{defining-portlet-actions}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Writing the Guestbook Admin App</p><p>Step 4 of 6</p>\n\\end{verbatim}\n\nThe Guestbook Admin portlet now needs action methods for adding,\nupdating, and deleting guestbooks. As with the Guestbook portlet, action\nmethods call the corresponding service methods. Note that since your\nservices all run in the same container, any application can call the\nGuestbook services. This is an advantage of Liferay DXP's OSGi-based\narchitecture: different applications or modules can call services\npublished by other modules. If a service is published, it can be used\nvia \\texttt{@Reference}. You'll take advantage of this here in the\nGuestbook Admin portlet to consume one of the same services consumed by\nthe Guestbook portlet (the \\texttt{addGuestbook} service).\n\n\\section{Adding Three Portlet\nActions}\\label{adding-three-portlet-actions}\n\nThe Guestbook Admin portlet must let administrators add, update, and\ndelete \\texttt{Guestbook} objects. You'll create portlet actions to meet\nthese requirements. Open \\texttt{GuestbookAdminPortlet.java} and follow\nthese steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the following action method and instance variables needed for\n  adding a new guestbook:\n\n\\begin{verbatim}\npublic void addGuestbook(ActionRequest request, ActionResponse response)\n    throws PortalException {\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n        Guestbook.class.getName(), request);\n\n    String name = ParamUtil.getString(request, \"name\");\n\n    try {\n        _guestbookLocalService.addGuestbook(\n            serviceContext.getUserId(), name, serviceContext);\n    }\n    catch (PortalException pe) {\n\n        Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n            Level.SEVERE, null, pe);\n\n        response.setRenderParameter(\n            \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n    }\n}\n\n@Reference\nprivate GuestbookLocalService _guestbookLocalService;\n\\end{verbatim}\n\n  Since \\texttt{addGuestbook} is a portlet action method, it takes\n  \\texttt{ActionRequest} and \\texttt{ActionResponse} parameters. To make\n  the service call to add a new guestbook, the guestbook's name must be\n  retrieved from the request. The \\texttt{serviceContext} must also be\n  retrieved from the request and passed as an argument in the service\n  call. If an exception is thrown, you should display the Add Guestbook\n  form and not the default view. That's why you add this line in the\n  \\texttt{catch} block:\n\n\\begin{verbatim}\nresponse.setRenderParameter(\"mvcPath\",\n        \"/guestbook_admin/edit_guestbook.jsp\");\n\\end{verbatim}\n\n  Later, you'll use this for field validation and to show error messages\n  to the user. Note that \\texttt{/guestbook\\_admin/edit\\_guestbook.jsp}\n  doesn't exist yet; you'll create it in the next step.\n\\item\n  Add the following action method for updating an existing guestbook:\n\n\\begin{verbatim}\npublic void updateGuestbook(ActionRequest request, ActionResponse response)\n    throws PortalException {\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n        Guestbook.class.getName(), request);\n\n    String name = ParamUtil.getString(request, \"name\");\n    long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n    try {\n        _guestbookLocalService.updateGuestbook(\n            serviceContext.getUserId(), guestbookId, name, serviceContext);\n\n    } catch (PortalException pe) {\n\n        Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n            Level.SEVERE, null, pe);\n\n        response.setRenderParameter(\n            \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n    }\n}\n\\end{verbatim}\n\n  This method retrieves the guestbook name, ID, and the\n  \\texttt{serviceContext} from the request. The \\texttt{updateGuestbook}\n  service call uses the guestbook's ID to identify the guestbook to\n  update. If there's a problem with the service call, the Guestbook\n  Admin portlet displays the Edit Guestbook form again so that the user\n  can edit the form and resubmit:\n\n\\begin{verbatim}\nresponse.setRenderParameter(\"mvcPath\",\n        \"/guestbook_admin/edit_guestbook.jsp\");\n\\end{verbatim}\n\n  Note that the Edit Guestbook form uses the same JSP as the Add\n  Guestbook form to avoid duplication of code.\n\\item\n  Add the following action method for deleting a guestbook:\n\n\\begin{verbatim}\npublic void deleteGuestbook(ActionRequest request, ActionResponse response)\n    throws PortalException {\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n        Guestbook.class.getName(), request);\n\n    long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n    try {\n        _guestbookLocalService.deleteGuestbook(guestbookId, serviceContext);\n    }\n    catch (PortalException pe) {\n\n        Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n            Level.SEVERE, null, pe);\n    }\n}\n\\end{verbatim}\n\n  This method uses the service layer to delete the guestbook by its ID.\n  Since the \\texttt{deleteGuestbook} action is invoked from the\n  Guestbook Admin portlet's default view, there's no need to set the\n  \\texttt{mvcPath} render parameter to point to a particular JSP if\n  there was a problem with the \\texttt{deleteGuestbook} service call.\n\\item\n  Hit {[}CTRL{]}+{[}SHIFT{]}+O to organize imports. Import the logging\n  classes from \\texttt{java.util}. Save the file.\n\\end{enumerate}\n\nYou now have your service methods and portlet action methods in place.\nBefore you implement the Guestbook Admin portlet's user interface, you\nshould update the Guestbook portlet so it can show users all the\nGuestbooks your administrators add.\n\n\\chapter{Adding Tabs to the Guestbook\nPortlet}\\label{adding-tabs-to-the-guestbook-portlet}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Writing the Guestbook Admin App</p><p>Step 5 of 6</p>\n\\end{verbatim}\n\nBefore you finish the Guestbook Admin portlet, you must prepare the\nGuestbook portlet's UI to display multiple Guestbooks. As administrators\nadd Guestbooks using the Guestbook Admin portlet, users must be able to\nchoose which Guestbook they want to sign. They'll do this using a series\nof tabs across the top:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-tabs.png}\n\\caption{Users can click a tab to choose which Guestbook to sign.}\n\\end{figure}\n\nThis is surprisingly easy to do using Liferay's Alloy UI tag library:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{view.jsp} from the\n  \\texttt{src/main/resources/META-INF/resources/guestbook} folder.\n\\item\n  After the first Java snippet (the one that gets the\n  \\texttt{guestbookId} out of the request), add this code:\n\n\\begin{verbatim}\n\n<aui:nav cssClass=\"nav-tabs\">\n\n    <%\n        List<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n            for (int i = 0; i < guestbooks.size(); i++) {\n\n                Guestbook curGuestbook = (Guestbook) guestbooks.get(i);\n                String cssClass = StringPool.BLANK;\n\n                if (curGuestbook.getGuestbookId() == guestbookId) {\n                    cssClass = \"active\";\n                }\n\n    %>\n\n    <portlet:renderURL var=\"viewPageURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\" />\n        <portlet:param name=\"guestbookId\"\n            value=\"<%=String.valueOf(curGuestbook.getGuestbookId())%>\" />\n    </portlet:renderURL>\n\n\n    <aui:nav-item cssClass=\"<%=cssClass%>\" href=\"<%=viewPageURL%>\"\n        label=\"<%=HtmlUtil.escape(curGuestbook.getName())%>\" />\n\n    <%  \n                }\n\n    %>\n\n</aui:nav>\n\\end{verbatim}\n\\item\n  Save the file.\n\\end{enumerate}\n\nThis code declares the AUI navigation tabs. Then a code scriptlet gets\nall the Guestbooks in this scope and loops through each one. As it\nexamines them, it checks to see if the one it's examining is the current\nGuestbook. If so, a CSS style called \\texttt{active} is applied.\n\nAfter this, a new URL called \\texttt{viewPageURL} is created that points\nto \\texttt{view.jsp} with a \\texttt{guestbookId} parameter containing\nthe current Guestbook in the loop. Finally, an\n\\texttt{\\textless{}aui:nav-item\\textgreater{}} tag declares the markup\nfor the tab, using the CSS class, the URL containing the parameters to\nnavigate to the new Guestbook, and the name to label it.\n\nThe loop continues until all the retrieved Guestbooks have tabs.\n\nAwesome! You've updated the Guestbook portlet so it can display all the\nGuestbooks administrators add. Now it's time to provide a UI for your\nGuestbook Admin portlet so they can do just that.\n\n\\chapter{Creating a User Interface}\\label{creating-a-user-interface}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Writing the Guestbook Admin App</p><p>Step 6 of 6</p>\n\\end{verbatim}\n\nIt's time to create the Guestbook Admin portlet's user interface. The\nportlet's default view has a button for adding new guestbooks. It must\nalso display the guestbooks that already exist.\n\nEach guestbook's name appears next to an Actions button. The Actions\nbutton reveals options for editing the guestbook, configuring its\npermissions, or deleting it.\n\n\\section{Step 1: Creating the Default\nView}\\label{step-1-creating-the-default-view}\n\nThe Guestbook Admin portlet's user interface is made up of three JSPs:\nthe default view, the Actions button, and the form for adding or editing\na guestbook.\n\nCreate the default view first:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In \\texttt{src/main/resources/META-INF/resources}, create a folder\n  called \\texttt{guestbook\\_admin}, where you'll create your JSPs.\n\\item\n  Create a file in this folder called \\texttt{view.jsp} and fill it with\n  this code:\n\n\\begin{verbatim}\n<%@include file=\"../init.jsp\"%>\n\n<liferay-ui:search-container\n    total=\"<%= GuestbookLocalServiceUtil.getGuestbooksCount(scopeGroupId) %>\">\n    <liferay-ui:search-container-results\n        results=\"<%= GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId, \n            searchContainer.getStart(), searchContainer.getEnd()) %>\" />\n\n    <liferay-ui:search-container-row\n        className=\"com.liferay.docs.guestbook.model.Guestbook\" modelVar=\"guestbook\">\n\n        <liferay-ui:search-container-column-text property=\"name\" />\n\n        <liferay-ui:search-container-column-jsp\n            align=\"right\" \n            path=\"/guestbook_admin/guestbook_actions.jsp\" />\n\n    </liferay-ui:search-container-row>\n\n    <liferay-ui:search-iterator />\n</liferay-ui:search-container>\n\n<aui:button-row cssClass=\"guestbook-admin-buttons\">\n    <portlet:renderURL var=\"addGuestbookURL\">\n        <portlet:param name=\"mvcPath\"\n            value=\"/guestbook_admin/edit_guestbook.jsp\" />\n        <portlet:param name=\"redirect\" value=\"<%= \"currentURL\" %>\" />\n    </portlet:renderURL>\n\n    <aui:button onClick=\"<%= addGuestbookURL.toString() %>\"\n        value=\"Add Guestbook\" />\n</aui:button-row>\n\\end{verbatim}\n\\end{enumerate}\n\nFirst is the \\texttt{init.jsp} include to gain access to the imports.\n\nNext is a button row with a single button for adding new guestbooks:\n\\texttt{\\textless{}aui:button-row\\ cssClass=\"guestbook-admin-buttons\"\\textgreater{}}.\nThe \\texttt{cssClass} attribute specifies a custom CSS class for\nadditional styling. The\n\\texttt{\\textless{}portlet:renderURL\\textgreater{}} tag constructs a URL\nthat points to the \\texttt{edit\\_guestbook.jsp}. You haven't created\nthis JSP yet, but you'll use it for adding a new guestbook and editing\nan existing one.\n\nFinally, a Liferay search container displays the list of guestbooks.\nThree sub-tags define the search container:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{\\textless{}liferay-ui:search-container-results\\textgreater{}}\n\\item\n  \\texttt{\\textless{}liferay-ui:search-container-row\\textgreater{}}\n\\item\n  \\texttt{\\textless{}liferay-ui:search-iterator\\textgreater{}}\n\\end{itemize}\n\nThe\n\\texttt{\\textless{}liferay-ui:search-container-results\\textgreater{}}\ntag's \\texttt{results} attribute uses a service call to retrieve the\nguestbooks in the scope. The \\texttt{total} attribute uses another\nservice call to get a count of guestbooks.\n\nThe \\texttt{\\textless{}liferay-ui:search-container-row\\textgreater{}}\ntag defines what rows contain. In this case, the \\texttt{className}\nattribute defines \\texttt{com.liferay.docs.guestbook.model.Guestbook}.\nThe \\texttt{modelVar} attribute defines \\texttt{guestbook} as the\nvariable for the currently iterated guestbook. In the search container\nrow, two columns are defined. The\n\\texttt{\\textless{}liferay-ui:search-container-column-text\\ property=\"name\"\\ /\\textgreater{}}\ntag specifies the first column. This tag displays text. Its\n\\texttt{property=\"name\"} attribute specifies that the text to be\ndisplayed is the current guestbook object's \\texttt{name} attribute. The\ntag \\texttt{\\textless{}liferay-ui:search-container-column-jsp}\n\\texttt{path=\"/guestbook\\_admin/guestbook\\_actions.jsp\"\\ align=\"right\"\\ /\\textgreater{}}\nspecifies the second (and last) column. This tag includes another JSP\nfile within a search container column. Its \\texttt{path} attribute\nspecifies the path to the JSP file that should be displayed:\n\\texttt{guestbook\\_actions.jsp}.\n\nFinally, the\n\\texttt{\\textless{}liferay-ui:search-iterator\\ /\\textgreater{}} tag\niterates through and displays the list of guestbooks. Using Liferay's\nsearch container makes the Guestbook Admin portlet look like a native\nLiferay DXP portlet. It also provides built-in pagination so that your\nportlet can automatically display large numbers of guestbooks on one\nSite.\n\n\\section{Step 2: Creating the Actions\nButton}\\label{step-2-creating-the-actions-button}\n\nNow create the \\texttt{guestbook\\_actions.jsp} file that displays the\nlist of possible actions for each guestbook.\n\nCreate a new file called \\texttt{guestbook\\_actions.jsp} in your\nproject's \\texttt{/guestbook\\_admin} folder. Paste in this code:\n\n\\begin{verbatim}\n<%@include file=\"../init.jsp\"%>\n\n<%\n    String mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n    ResultRow row = (ResultRow) request\n                    .getAttribute(\"SEARCH_CONTAINER_RESULT_ROW\");\n\n    Guestbook guestbook = (Guestbook) row.getObject();\n%>\n\n<liferay-ui:icon-menu>\n\n    <portlet:renderURL var=\"editURL\">\n        <portlet:param name=\"guestbookId\"\n            value=\"<%=String.valueOf(guestbook.getGuestbookId()) %>\" />\n        <portlet:param name=\"mvcPath\"\n            value=\"/guestbook_admin/edit_guestbook.jsp\" />\n    </portlet:renderURL>\n\n    <liferay-ui:icon image=\"edit\" message=\"Edit\"\n            url=\"<%=editURL.toString() %>\" />\n\n    <portlet:actionURL name=\"deleteGuestbook\" var=\"deleteURL\">\n            <portlet:param name=\"guestbookId\"\n                value=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\" />\n    </portlet:actionURL>\n\n    <liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n</liferay-ui:icon-menu>\n\\end{verbatim}\n\nThis JSP comprises the pop-up actions menu that shows the actions users\ncan perform on a guestbook: editing it or deleting it. First,\n\\texttt{init.jsp} is included because it contains all the JSP imports.\nBecause \\texttt{guestbook\\_actions.jsp} is included for every Search\nContainer row, it retrieves the guestbook in the current iteration. The\nscriptlet grabs that guestbook so its ID can be supplied to the menu\ntags.\n\nThe \\texttt{\\textless{}liferay-ui:icon-menu\\textgreater{}} tag dominates\n\\texttt{guestbook\\_actions.jsp}. It's a container for menu items, of\nwhich there are currently only two (you'll add more later). The Edit\nmenu item displays the Edit icon and the message \\emph{Edit}:\n\n\\begin{verbatim}\n<liferay-ui:icon image=\"edit\" message=\"Edit\"\n        url=\"<%=editURL.toString() %>\" />\n\\end{verbatim}\n\nThe \\texttt{editURL} variable comes from the\n\\texttt{\\textless{}portlet:renderURL\\ var=\"editURL\"\\textgreater{}} tag\nwith two parameters: \\texttt{guestbookId} and \\texttt{mvcPath}. The\n\\texttt{guestbookId} parameter specifies the guestbook to edit (it's the\none from the selected search container result row), and the\n\\texttt{mvcPath} parameter specifies the Edit Guestbook form's path.\n\nThe Delete menu item displays a delete icon and the default message\n\\emph{Delete}:\n\n\\begin{verbatim}\n<liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\\end{verbatim}\n\nUnlike the \\texttt{editURL}, which is a render URL that links to the\n\\texttt{edit\\_guestbook.jsp}, the \\texttt{deleteURL} is an action URL\nthat invokes the portlet's \\texttt{deleteGuestbook} action. The tag\n\\texttt{\\textless{}portlet:actionURL\\ name=\"deleteGuestbook\"\\ var=\"deleteURL\"\\textgreater{}}\ncreates this action URL, which only takes one parameter: the\n\\texttt{guestbookId} of the guestbook to be deleted.\n\n\\section{Step 3: Creating the Edit Guestbook\nJSP}\\label{step-3-creating-the-edit-guestbook-jsp}\n\nNow there's just one more JSP file left to create: the\n\\texttt{edit\\_guestbook.jsp} that contains the form for adding a new\nguestbook and editing an existing one.\n\nCreate a new file called \\texttt{edit\\_guestbook.jsp} in your project's\n\\texttt{/guestbook\\_admin} directory. Then add the following code to it:\n\n\\begin{verbatim}\n<%@include file = \"../init.jsp\" %>\n\n<%\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n        \n        Guestbook guestbook = null;\n\n        if (guestbookId > 0) {\n                guestbook = GuestbookLocalServiceUtil.getGuestbook(guestbookId);\n        }\n%>\n\n<portlet:renderURL var=\"viewURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook_admin/view.jsp\" />\n</portlet:renderURL>\n\n<portlet:actionURL name='<%= guestbook == null ? \"addGuestbook\" : \"updateGuestbook\" %>' var=\"editGuestbookURL\" />\n\n<aui:form action=\"<%= editGuestbookURL %>\" name=\"fm\">\n\n        <aui:model-context bean=\"<%= guestbook %>\" model=\"<%= Guestbook.class %>\" />\n\n        <aui:input type=\"hidden\" name=\"guestbookId\"\n            value='<%= guestbook == null ? \"\" : guestbook.getGuestbookId() %>' />\n\n        <aui:fieldset>\n             <aui:input name=\"name\" />\n        </aui:fieldset>\n\n        <aui:button-row>\n             <aui:button type=\"submit\" />\n             <aui:button onClick=\"<%= viewURL %>\" type=\"cancel\"  />\n        </aui:button-row>\n</aui:form>\n\\end{verbatim}\n\nAfter the \\texttt{init.jsp} import, you declare a \\texttt{null}\nguestbook variable. If there's a \\texttt{guestbookId} parameter in the\nrequest, you use the \\texttt{guestbookId} to retrieve the corresponding\nguestbook via a service call for edit. Otherwise, you know that you're\nadding a new guestbook.\n\nNext is a view URL that points to the Guestbook Admin portlet's default\nview. This URL is invoked if the user clicks \\emph{Cancel} on the Add\nGuestbook or Edit Guestbook form. After that, you create an action URL\nthat invokes either the Guestbook Admin portlet's \\texttt{addGuestbook}\nmethod or its \\texttt{updateGuestbook} method, depending on whether the\n\\texttt{guestbook} variable is null.\n\nIf a guestbook is being edited, its name should appear in the form's\nname field. You use the following tag to define a model of the guestbook\nthat can be used in the AlloyUI form:\n\n\\begin{verbatim}\n<aui:model-context bean=\"<%= guestbook %>\" model=\"<%= Guestbook.class %>\" />\n\\end{verbatim}\n\nThe form is created with the following tag:\n\n\\begin{verbatim}\n<aui:form action=\"<%= editGuestbookURL %>\" name=\"<portlet:namespace />fm\">\n\\end{verbatim}\n\nThe form is submitted via the \\texttt{editGuestbookURL}, which calls the\nGuestbook Admin portlet's \\texttt{addGuestbook} or\n\\texttt{updateGuestbook} action method, as discussed above.\n\nThe \\texttt{guestbookId} must appear on the form so that it can be\nsubmitted. The user, however, doesn't need to see it. Thus, you specify\n\\texttt{type=\"hidden\"}:\n\n\\begin{verbatim}\n<aui:input type=\"hidden\" name=\"guestbookId\"\n        value='<%= guestbook == null ? \"\" : guestbook.getGuestbookId() %>' />\n\\end{verbatim}\n\nThe name, of course, should be editable by the user so it's not hidden.\n\nThe last item on the form is a button row with two buttons. The\n\\emph{Submit} button submits the form, invoking the\n\\texttt{editGuestbookURL} which, in turn, invokes either the\n\\texttt{addGuestbook} or \\texttt{updateGuestbook} method. The\n\\emph{Cancel} button invokes the \\texttt{viewURL} which displays the\ndefault view.\n\nExcellent! You've now finished creating the UI for the Guestbook Admin\nportlet. It should now match the figure below:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/admin-app-start.png}\n\\caption{The Guestbook Admin portlet can add or edit guestbooks,\nconfigure their permissions, or delete them.}\n\\end{figure}\n\nSave all your files and wait for redeploy. Test out the Guestbook Admin\nportlet! Try adding, editing, and deleting guestbooks.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you get ``Guestbook is unavailable'' errors, remove\nthe modules from the server, redeploy them, and test again.\n\n\\noindent\\hrulefill\n\nNow all the Guestbook application's primary functions work. There are\nstill many missing features, however. For example, if there's ever an\nerror, users never see it: all the code written so far just prints\nmessages in the logs. Next, you'll learn how to display those errors to\nthe user.\n\n\\chapter{Displaying Messages and\nErrors}\\label{displaying-messages-and-errors}\n\nWhen users interact with your application, they perform tasks it\ndefines, like saving or editing things. The Guestbook application is no\ndifferent. Your application should provide feedback on these operations\nso users can know they completed. Up to now, you've been placing this\ninformation in logs that only administrators can access. Wouldn't it be\nbetter to show users these messages?\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-status-message.png}\n\\caption{You can use Liferay's APIs to display helpful messages.}\n\\end{figure}\n\nThat's exactly what you'll do next, in three steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Create language keys for your messages.\n\\item\n  Add the error messages to your action methods.\n\\item\n  Report those error messages in your JSPs.\n\\end{enumerate}\n\nIf you get stuck,\n\\href{https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook}{source\ncode} for this step is provided.\n\nReady to get started?\n\nLet's Go!{}\n\n\\chapter{Creating Language Keys}\\label{creating-language-keys}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Displaying Messages and Errors</p><p>Step 1 of 3</p>\n\\end{verbatim}\n\nModern applications should place messages and form field labels in a\nlanguage keys files that hold multiple language translations. Here,\nyou'll learn how to provide a \\emph{default} set of English language\nkeys for your application. For more information on language keys and\nproviding automatically translated language keys,\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/automatically-generating-translations}{see\nGenerating Translations}.\n\nLanguage keys are stored in the \\texttt{Language.properties} file\nincluded in your \\texttt{guestbook-web} module.\n\\texttt{Language.properties} is the default, but you can create more\ntranslations by appending the ISO-639 language code to the file name\n(e.g., \\texttt{Language\\_es.properties} for Spanish or\n\\texttt{Language\\_de.properties} for German). For now, stick to the\ndefault language keys.\n\nFollow these steps to create your language keys:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{/src/main/resources/content/Language.properties} in your\n  \\texttt{guestbook-web} module. Remove the default keys in this file.\n\\item\n  Paste in the following keys:\n\n\\begin{verbatim}\nentry-added=Entry added successfully.\nentry-deleted=Entry deleted successfully.\nguestbook-added=Guestbook added successfully.\nguestbook-updated=Guestbook updated successfully.\nguestbook-deleted=Guestbook deleted successfully.\n\\end{verbatim}\n\\item\n  Save the file.\n\\end{enumerate}\n\nYour messages are now in place, and your application can use them. Next,\nyou'll add them to your action methods.\n\n\\chapter{Adding Failure and Success\nMessages}\\label{adding-failure-and-success-messages}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Displaying Messages and Errors</p><p>Step 2 of 3</p>\n\\end{verbatim}\n\nTo display feedback to users properly, you must edit your portlet\nclasses to use Liferay DXP's \\texttt{SessionMessages} and\n\\texttt{SessionErrors} classes. These classes collect messages that the\nview layer shows to the user through a tag.\n\nYou'll add these messages to code that runs when the user triggers a\nsystem function that can succeed or fail, such as creating, editing, or\ndeleting a Guestbook or Guestbook entry. This happens in action methods.\nYou must update these methods to handle failure and success states in\n\\texttt{GuestbookPortlet.java} and \\texttt{GuestbookAdminPortlet.java}.\nStart by updating \\texttt{addEntry} and \\texttt{deleteEntry} in\n\\texttt{GuestbookPortlet.java}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Find the \\texttt{addEntry} method in \\texttt{GuestbookPortlet.java}.\n  In the first \\texttt{try...catch} block's \\texttt{try} section, and\n  add the success message just before the closing \\texttt{\\}}:\n\n\\begin{verbatim}\nSessionMessages.add(request, \"entryAdded\");\n\\end{verbatim}\n\n  This uses Liferay's \\texttt{SessionMessages} API to add a success\n  message whenever a Guestbook is successfully added. It looks up the\n  message you placed in the \\texttt{Language.properties} file and\n  inserts the message for the key \\texttt{entry-added} (it automatically\n  converts the key from camel case).\n\\item\n  Below that, in the \\texttt{catch} block, find the following code:\n\n\\begin{verbatim}\nSystem.out.println(e);\n\\end{verbatim}\n\\item\n  Beneath it, paste this line:\n\n\\begin{verbatim}\nSessionErrors.add(request, e.getClass().getName());\n\\end{verbatim}\n\n  Now you not only log the message to the console, you also use the\n  \\texttt{SessionErrors} object to show the message to the user.\n\\end{enumerate}\n\nNext, do the same for the \\texttt{deleteEntry} method:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  After the logic to delete the entry, add a success message:\n\n\\begin{verbatim}\nSessionMessages.add(request, \"entryDeleted\");\n\\end{verbatim}\n\\item\n  Find the \\texttt{Logger...} block of code in the \\texttt{deleteEntry}\n  method and after it, paste this line:\n\n\\begin{verbatim}\nSessionErrors.add(request, e.getClass().getName());\n\\end{verbatim}\n\\item\n  Hit {[}CTRL{]}+{[}SHIFT{]}+O to import\n  \\texttt{com.liferay.portal.kernel.servlet.SessionErrors} and\n  \\texttt{com.liferay.portal.kernel.servlet.SessionMessages}. Save the\n  file.\n\\end{enumerate}\n\nWell done! You've added the messages to \\texttt{GuestbookPortlet}. Now\nyou must update \\texttt{GuestbookAdminPortlet.java}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{GuestbookAdminPortlet.java} and look for the same cues.\n\\item\n  Add the appropriate success messages to the \\texttt{try} section of\n  the \\texttt{try...catch} in \\texttt{addGuestbook},\n  \\texttt{updateGuestbook}, and \\texttt{deleteGuestbook}, respectively:\n\n\\begin{verbatim}\nSessionMessages.add(request, \"guestbookAdded\");\n\nSessionMessages.add(request, \"guestbookUpdated\");\n\nSessionMessages.add(request, \"guestbookDeleted\");\n\\end{verbatim}\n\\item\n  In the \\texttt{catch} section of those same methods, find\n  \\texttt{Logger.getlogger...} and paste the \\texttt{SessionErrors}\n  block beneath it:\n\n\\begin{verbatim}\nSessionErrors.add(request, pe.getClass().getName());\n\\end{verbatim}\n\\item\n  Hit {[}CTRL{]}+{[}SHIFT{]}+O to import \\texttt{SessionErrors} and\n  \\texttt{SessionMessages}. Save the file.\n\\end{enumerate}\n\nGreat! The controller now makes relevant and detailed feedback\navailable. Now all you need to do is publish this feedback in the view\nlayer.\n\n\\chapter{Adding Messages to JSPs}\\label{adding-messages-to-jsps}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Displaying Messages and Errors</p><p>Step 3 of 3</p>\n\\end{verbatim}\n\nAny messages the user should see are now stored in either\n\\texttt{SessionMessages} or \\texttt{SessionErrors}. Next, you'll make\nthese messages appear in your JSPs.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\texttt{guestbook-web} module, open\n  \\texttt{guestbook/view.jsp}. Add the following block of success\n  messages to the top of the file, just below the \\texttt{init.jsp}\n  include statement:\n\n\\begin{verbatim}\n<liferay-ui:success key=\"entryAdded\" message=\"entry-added\" />\n<liferay-ui:success key=\"entryDeleted\" message=\"entry-deleted\" />\n\\end{verbatim}\n\n  This tag accesses what's stored in \\texttt{SessionMessages}. It has\n  two attributes. The first is the \\texttt{SessionMessages} key that you\n  provided in the \\texttt{GuestbookPortlet.java} class's add and delete\n  methods. The second looks up the specified key in the\n  \\texttt{Language.properties} file. You could have specified a\n  hard-coded message here, but it's far better to provide a localized\n  key.\n\\item\n  Now open \\texttt{guestbook\\_admin/view.jsp}. Add the following block\n  of success messages in the same spot below the include:\n\n\\begin{verbatim}\n<liferay-ui:success key=\"guestbookAdded\" message=\"guestbook-added\" />\n<liferay-ui:success key=\"guestbookUpdated\" message=\"guestbook-updated\" />\n<liferay-ui:success key=\"guestbookDeleted\" message=\"guestbook-deleted\" />\n\\end{verbatim}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/message-complete.png}\n\\caption{Now the message displays the value you specified in\n\\texttt{Language.properties}.}\n\\end{figure}\n\nCongratulations! You've added useful feedback for operations in your\napplication.\n\nYour application is shaping up, but it is missing another important\nfeature: permissions. Next, you'll add permission checking for your\nguestbooks and entries.\n\nLook for the next part soon!\n\n\\chapter{Using Resources and\nPermissions}\\label{using-resources-and-permissions}\n\nYour application is a great foundation to build on. What comes next?\nWhat if users want a Guestbook that's limited to certain trusted people?\nWhat if you don't want just any old user to go around editing or\ndeleting people's Guestbook entries? To do that, you have to implement\npermissions.\n\nThankfully, with Liferay DXP you don't have to write an entire\npermissions system from scratch: the framework provides a robust and\nwell-tested permissions system that you can implement quickly. You'll\nfollow Liferay's well-defined process for implementing permissions,\ncalled \\emph{DRAC}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Define} all resources and permissions\n\\item\n  \\textbf{Register} all defined resources in the permissions system\n\\item\n  \\textbf{Associate} permissions with resources\n\\item\n  \\textbf{Check} for permission before returning resources\n\\end{itemize}\n\nIf you get stuck,\n\\href{https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook}{source\ncode} for this step is provided.\n\nReady to start?\n\nLet's Go!{}\n\n\\chapter{Defining Permissions}\\label{defining-permissions}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Implementing Permissions</p><p>Step 1 of 5</p>\n\\end{verbatim}\n\nLiferay DXP's permissions framework is configured declaratively, like\nService Builder. You define all your permissions in an XML file that by\nconvention is called \\texttt{default.xml} (but you could really call it\nwhatever you want). Then you implement permissions checks in the\nfollowing places in your code:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  In the view layer, when showing links or buttons to protected\n  functionality\n\\item\n  In the actions, before performing a protected action\n\\item\n  Later, in your service, before calling the remote service\n\\end{itemize}\n\nYou should first define the permissions you want. To get started, think\nof your application's use cases and how access to that functionality\nshould be controlled:\n\n\\begin{itemize}\n\\item\n  The Add Guestbook button should be available only to administrators.\n\\item\n  The Guestbook tabs should be filtered by permissions so administrators\n  can control who can see them.\n\\item\n  To prevent anonymous users from spamming the guestbook, the Add Entry\n  button should be available only to Site members.\n\\item\n  Users should be able to set permissions on their own entries.\n\\end{itemize}\n\nNow you're ready to create the permissions configuration. Objects in\nyour application (such as \\texttt{Guestbook} and\n\\texttt{GuestbookEntry}) are defined as \\emph{resources}, and\n\\emph{resource actions} manage how users can interact with those\nresources. There are therefore two kinds of permissions: portlet\npermissions and resource (or model) permissions. Portlet permissions\nprotect access to global functions, such as \\emph{Add Entry}. If users\ndon't have permission to access that global function, they're missing a\nportlet permission. Resource permissions protect access to objects, such\nas \\texttt{Guestbook} and \\texttt{GuestbookEntry}. A user may have\npermission to view one \\texttt{GuestbookEntry}, view and edit another\n\\texttt{GuestbookEntry}, and may not be able to access another\n\\texttt{GuestbookEntry} at all. This is due to a resource permission.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/permission-types.png}\n\\caption{Portlet permissions and resource permissions cover different\nparts of the application.}\n\\end{figure}\n\n\\section{Defining Model Permissions}\\label{defining-model-permissions}\n\nFirst, create the permissions file in the \\texttt{guestbook-service}\nproject:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\texttt{/src/main/resources/META-INF} folder, create a\n  subfolder called \\texttt{resource-actions}.\n\\item\n  Create a new file in this folder called \\texttt{default.xml}.\n\\item\n  Click the \\emph{Source} tab. Add the following \\texttt{DOCTYPE}\n  declaration to the top of the file:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action  \nMapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n\\end{verbatim}\n\\item\n  Place the following wrapper tags into your \\texttt{default.xml} file,\n  below the \\texttt{DOCTYPE} declaration:\n\n\\begin{verbatim}\n<resource-action-mapping>\n\n</resource-action-mapping>\n\\end{verbatim}\n\n  You'll define your resource and model permissions inside these tags.\n\\item\n  Next, place the permissions for your\n  \\texttt{com.liferay.docs.guestbook} package between the\n  \\texttt{\\textless{}resource-action-mapping\\textgreater{}} tags:\n\n\\begin{verbatim}\n<model-resource>\n    <model-name>com.liferay.docs.guestbook</model-name>\n    <portlet-ref>\n        <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n        <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n    </portlet-ref>\n    <root>true</root>\n    <permissions>\n        <supports>\n            <action-key>ADD_GUESTBOOK</action-key>\n            <action-key>ADD_ENTRY</action-key>\n            <action-key>VIEW</action-key>\n        </supports>\n        <site-member-defaults>\n            <action-key>ADD_ENTRY</action-key>\n        </site-member-defaults>\n        <guest-defaults>\n            <action-key>VIEW</action-key>\n        </guest-defaults>\n        <guest-unsupported>\n            <action-key>ADD_GUESTBOOK</action-key>\n            <action-key>ADD_ENTRY</action-key>\n        </guest-unsupported>\n    </permissions>\n</model-resource>\n\\end{verbatim}\n\n  This defines the baseline configuration for the \\texttt{Guestbook} and\n  \\texttt{GuestbookEntry} entities. The supported actions are\n  \\texttt{ADD\\_GUESTBOOK} and \\texttt{ADD\\_ENTRY}. Site members can\n  \\texttt{ADD\\_ENTRY} by default, and guests can't perform either action\n  (but they can view).\n\\item\n  Below that, but above the closing\n  \\texttt{\\textless{}/resource-action-mapping\\textgreater{}}, place the\n  \\texttt{Guestbook} model permissions:\n\n\\begin{verbatim}\n<model-resource>\n    <model-name>com.liferay.docs.guestbook.model.Guestbook</model-name>\n    <portlet-ref>\n        <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n        <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n    </portlet-ref>\n    <permissions>\n        <supports>\n            <action-key>ADD_ENTRY</action-key>\n            <action-key>DELETE</action-key>\n            <action-key>PERMISSIONS</action-key>\n            <action-key>UPDATE</action-key>\n            <action-key>VIEW</action-key>\n        </supports>\n        <site-member-defaults>\n            <action-key>ADD_ENTRY</action-key>\n            <action-key>VIEW</action-key>\n        </site-member-defaults>\n        <guest-defaults>\n            <action-key>VIEW</action-key>\n        </guest-defaults>\n        <guest-unsupported>\n            <action-key>UPDATE</action-key>\n        </guest-unsupported>\n    </permissions>\n</model-resource>\n\\end{verbatim}\n\n  This defines the \\texttt{Guestbook} specific actions, including\n  adding, deleting, updating, and viewing. By default, Site members and\n  guests can view guestbooks, but guests can't update them.\n\\item\n  Below the \\texttt{Guestbook} model permissions, but still above the\n  closing \\texttt{\\textless{}/resource-action-mapping\\textgreater{}},\n  place the \\texttt{GuestbookEntry} model permissions:\n\n\\begin{verbatim}\n<model-resource>\n    <model-name>com.liferay.docs.guestbook.model.GuestbookEntry</model-name>\n    <portlet-ref>\n        <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n    </portlet-ref>\n    <permissions>\n        <supports>\n            <action-key>DELETE</action-key>\n            <action-key>PERMISSIONS</action-key>\n            <action-key>UPDATE</action-key>\n            <action-key>VIEW</action-key>\n        </supports>\n        <site-member-defaults>\n            <action-key>VIEW</action-key>\n        </site-member-defaults>\n        <guest-defaults>\n            <action-key>VIEW</action-key>\n        </guest-defaults>\n        <guest-unsupported>\n            <action-key>UPDATE</action-key>\n        </guest-unsupported>\n    </permissions>\n</model-resource>\n\\end{verbatim}\n\n  This defines \\texttt{GuestbookEntry} specific actions. By default, a\n  Site member can add or view a Guestbook entry, and a guest can only\n  view a Guestbook entry.\n\\item\n  Save the file.\n\\end{enumerate}\n\nNext, you must tell the framework where your permissions are defined.\nYou'll define resource and model permissions in the module where your\nmodel is defined:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In \\texttt{guestbook-service}'s \\texttt{src/main/resources} folder,\n  create a file called \\texttt{portlet.properties}.\n\\item\n  In this file, place the following property:\n\n\\begin{verbatim}\nresource.actions.configs=META-INF/resource-actions/default.xml\n\\end{verbatim}\n\\end{enumerate}\n\nThis property defines the name and location of your permissions\ndefinition file.\n\n\\section{Defining Portlet\nPermissions}\\label{defining-portlet-permissions}\n\nYou now have permissions defined at the model level, but you must also\ndefine portlet permissions. These are managed in the\n\\texttt{guestbook-web} module, which contains the portlet class. Follow\nthese steps to add the portlet permissions in the \\texttt{guestbook-web}\nmodule:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a subfolder called \\texttt{resource-actions} in the\n  \\texttt{src/main/resources/META-INF} folder.\n\\item\n  Create a new file in this folder called \\texttt{default.xml}.\n\\item\n  Add the following \\texttt{DOCTYPE} declaration to the top of the file:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action  \nMapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n\\end{verbatim}\n\\item\n  Below the \\texttt{DOCTYPE} declaration, add the following\n  \\texttt{resource-action-mapping} tags:\n\n\\begin{verbatim}\n<resource-action-mapping>\n\n</resource-action-mapping>\n\\end{verbatim}\n\n  You'll define your portlet permissions inside these tags.\n\\item\n  Insert this block of code inside the \\texttt{resource-action-mapping}\n  tags:\n\n\\begin{verbatim}\n<portlet-resource>\n    <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n    <permissions>\n        <supports>\n            <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n            <action-key>CONFIGURATION</action-key>\n            <action-key>VIEW</action-key>\n        </supports>\n        <site-member-defaults>\n            <action-key>VIEW</action-key>\n        </site-member-defaults>\n        <guest-defaults>\n            <action-key>VIEW</action-key>\n        </guest-defaults>\n        <guest-unsupported>\n            <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n            <action-key>CONFIGURATION</action-key>\n        </guest-unsupported>\n    </permissions>\n</portlet-resource>\n\\end{verbatim}\n\n  This defines the default permissions for the Guestbook Admin portlet.\n  It supports the actions \\texttt{ACCESS\\_IN\\_CONTROL\\_PANEL},\n  \\texttt{CONFIGURATION}, and \\texttt{VIEW}. While anyone can view the\n  app, guests and Site members can't configure it or access it in the\n  Control Panel. Since it's a Control Panel portlet, this effectively\n  means that only administrators can access it.\n\\item\n  Below the Guestbook Admin permissions, insert this block of code:\n\n\\begin{verbatim}\n<portlet-resource>\n    <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n    <permissions>\n        <supports>\n            <action-key>ADD_TO_PAGE</action-key>\n            <action-key>CONFIGURATION</action-key>\n            <action-key>VIEW</action-key>\n        </supports>\n        <site-member-defaults>\n            <action-key>VIEW</action-key>\n        </site-member-defaults>\n        <guest-defaults>\n            <action-key>VIEW</action-key>\n        </guest-defaults>\n        <guest-unsupported />\n    </permissions>\n</portlet-resource>\n\\end{verbatim}\n\n  This defines permissions for the Guestbook portlet. It supports the\n  actions \\texttt{ADD\\_TO\\_PAGE}, \\texttt{CONFIGURATION}, and\n  \\texttt{VIEW}. Site members and guests get the \\texttt{VIEW}\n  permission by default.\n\\item\n  Save the file.\n\\item\n  In \\texttt{guestbook-web}'s \\texttt{src/main/resources} folder, create\n  a file called \\texttt{portlet.properties}.\n\\item\n  In this file, place the following property:\n\n\\begin{verbatim}\nresource.actions.configs=META-INF/resource-actions/default.xml\n\\end{verbatim}\n\\item\n  Save the file.\n\\end{enumerate}\n\nGreat job! You've now successfully designed and implemented a\npermissions scheme for your application. Next, you'll create the Java\ncode to support permissions in the service layer.\n\n\\chapter{Registering Your Permissions in the\nDatabase}\\label{registering-your-permissions-in-the-database}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Implementing Permissions</p><p>Step 2 of 5</p>\n\\end{verbatim}\n\nThe last step introduced the concept of \\emph{resources}. Resources are\ndata stored with your entities that define how they can be accessed. For\nexample, when the configuration in your \\texttt{default.xml} files is\napplied to your application's entities in the database, resources are\ncreated. These resources are then used in conjunction with Liferay DXP's\npermissions system to determine who can do what to the entities.\n\nTo use these resources, Liferay DXP must know about them. To do that you\n\\emph{register} the resources with the system, both in the database and\nwith the running permissions system in the OSGi container.\n\nLiferay DXP provides a complete API---integrated with Service\nBuilder---for managing resources. This API is injected into your\nimplementation classes automatically. To manage the resources, you need\nonly call the API in the service's add and delete methods. Follow these\nsteps to do this in your application.\n\n\\section{Registering Guestbook\nResources}\\label{registering-guestbook-resources}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your \\texttt{guestbook-service} module, open\n  \\texttt{GuestbookLocalServiceImpl.java} from the\n  \\texttt{com.liferay.docs.guestbook.service.impl} package.\n\\item\n  Just before the \\texttt{addGuestbook} method's \\texttt{return}\n  statement, add this code:\n\n\\begin{verbatim}\nresourceLocalService.addResources(user.getCompanyId(), groupId, userId,\n    Guestbook.class.getName(), guestbookId, false, true, true);\n\\end{verbatim}\n\n  Note that the \\texttt{resourceLocalService} object is already there,\n  ready for you to use. This is one of several utilities that are\n  injected automatically by Service Builder into the base class your\n  implementation class extends. You'll see the rest in the future.\n\n  This code adds a resource to Liferay DXP's database to correspond with\n  your entity (note that the \\texttt{guestbookId} is included in the\n  call). The three booleans at the end are settings. The first is\n  whether to add portlet action permissions. This should only be\n  \\texttt{true} if the permission is for a portlet resource. Since this\n  permission is for a model resource (an entity), it's \\texttt{false}.\n  The other two are settings for adding group and guest permissions. If\n  you set these to \\texttt{true}, you'll add the default permissions you\n  defined in the permissions configuration file (\\texttt{default.xml})\n  in the previous step. Since you definitely want to do this, these\n  booleans are set to \\texttt{true}.\n\\item\n  Next, go to the \\texttt{updateGuestbook} method. Add a similar bit of\n  code in between \\texttt{guestbookPersistence.update(guestbook);} and\n  the \\texttt{return} statement:\n\n\\begin{verbatim}\nresourceLocalService.updateResources(serviceContext.getCompanyId(),\n    serviceContext.getScopeGroupId(), \n    Guestbook.class.getName(), guestbookId,\n    serviceContext.getModelPermissions());\n\\end{verbatim}\n\\item\n  Now you'll do the same for \\texttt{deleteGuestbook}. Add this code in\n  between \\texttt{guestbook\\ =\\ deleteGuestbook(guestbook);} and the\n  \\texttt{return} statement:\n\n\\begin{verbatim}\nresourceLocalService.deleteResource(serviceContext.getCompanyId(),\n    Guestbook.class.getName(), ResourceConstants.SCOPE_INDIVIDUAL,\n    guestbookId);\n\\end{verbatim}\n\\item\n  Hit {[}CTRL{]}+{[}SHIFT{]}+O to organize the imports and save the\n  file.\n\\end{enumerate}\n\n\\section{Registering Guestbook Entry\nResources}\\label{registering-guestbook-entry-resources}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Now you'll add resources for the \\texttt{GuestbookEntry} entity. Open\n  \\texttt{GuestbookEntryLocalServiceImpl.java} from the same package.\n  For \\texttt{addGuestbookEntry}, add a line of code that adds resources\n  for this entity, just before the return statement:\n\n\\begin{verbatim}\nresourceLocalService.addResources(user.getCompanyId(), groupId, userId,\n    GuestbookEntry.class.getName(), entryId, false, true, true);\n\\end{verbatim}\n\\item\n  Find \\texttt{updateEntry} and add its resource action, also just\n  before the \\texttt{return} statement:\n\n\\begin{verbatim}\nresourceLocalService.updateResources(\n      user.getCompanyId(), serviceContext.getScopeGroupId(), \n      GuestbookEntry.class.getName(), entryId, \n      serviceContext.getModelPermissions());\n\\end{verbatim}\n\\item\n  For \\texttt{deleteGuestbookEntry}, add this code just before the\n  \\texttt{return} statement and just after the call to\n  \\texttt{guestbookEntryPersistence}:\n\n\\begin{verbatim}\nresourceLocalService.deleteResource(\n   entry.getCompanyId(), GuestbookEntry.class.getName(),\n   ResourceConstants.SCOPE_INDIVIDUAL, entry.getEntryId());\n\\end{verbatim}\n\\item\n  Hit Ctrl-Shift-O to add imports and save the file.\n\\end{enumerate}\n\nThat's all it takes to add permissions resources to the database. Future\nentities added to the database are fully permissions-enabled. Note,\nhowever, that entities you've already added to your Guestbook\napplication in the portal don't have resources and thus can't be\nprotected by permissions. You'll fix this later. Now you must register\npermissions with the permissions system, so it knows how to check for\nthem.\n\n\\chapter{Registering Permissions with the\nContainer}\\label{registering-permissions-with-the-container}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Implementing Permissions</p><p>Step 3 of 5</p>\n\\end{verbatim}\n\nA running service checks permissions, but since the Guestbook portlet,\nGuestbooks, and Guestbook Entries are new to the system, it must be\ntaught about them. You do this by creating permissions registrar\nclasses. These follow what you did in \\texttt{default.xml}: you need one\nfor your portlet permissions and one for each of your entities. First,\nyou must do a little reorganization.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your API module, create a \\texttt{GuestbookConstants} class in a\n  new package called \\texttt{com.liferay.docs.guestbook.constants}:\n\n\\begin{verbatim}\npackage com.liferay.docs.guestbook.constants;\n\npublic class GuestbookConstants {\n\n    public static final String RESOURCE_NAME = \"com.liferay.docs.guestbook\";\n\n}\n\\end{verbatim}\n\n  The \\texttt{RESOURCE\\_NAME} string must match exactly your resource\n  name from \\texttt{default.xml}. You'll see why in a moment.\n\\item\n  You have a \\texttt{GuestbookPortletKeys} class in your web module.\n  These keys must now be accessible to all modules, so drag this class\n  from the web module and drop it into the new\n  \\texttt{com.liferay.docs.guestbook.constants} package in your API\n  module.\n\\end{enumerate}\n\nNow you're ready to create your permissions registrar classes.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\texttt{build.gradle} file in your \\texttt{guestbook-service}\n  module.\n\\item\n  Add the following three dependencies and save the file:\n\n\\begin{verbatim}\ncompileOnly group: \"com.liferay\", name: \"com.liferay.petra.function\"\ncompileOnly group: \"com.liferay\", name: \"com.liferay.petra.model.adapter\"\ncompileOnly group: \"com.liferay\", name: \"com.liferay.petra.reflect\"\n\\end{verbatim}\n\\item\n  In your service module, create a package that by convention ends in\n  \\texttt{internal.security.permission.resource}.\n\\item\n  Create a class in this package called\n  \\texttt{GuestbookModelResourcePermissionRegistrar} with the contents\n  below.\n\n\\begin{verbatim}\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.StagedModelPermissionLogic;\nimport com.liferay.portal.kernel.security.permission.resource.WorkflowedModelPermissionLogic;\nimport com.liferay.portal.kernel.service.GroupLocalService;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\nimport com.liferay.portal.kernel.workflow.permission.WorkflowPermission;\n\n@Component (immediate=true)\npublic class GuestbookModelResourcePermissionRegistrar {\n\n @Activate\n    public void activate(BundleContext bundleContext) {\n        Dictionary<String, Object> properties = new HashMapDictionary<>();\n\n        properties.put(\"model.class.name\", Guestbook.class.getName());\n\n        _serviceRegistration = bundleContext.registerService(\n            ModelResourcePermission.class,\n            ModelResourcePermissionFactory.create(\n                Guestbook.class, Guestbook::getGuestbookId,\n                _guestbookLocalService::getGuestbook, _portletResourcePermission,\n                (modelResourcePermission, consumer) -> {\n                    consumer.accept(\n                        new StagedModelPermissionLogic<>(\n                            _stagingPermission, GuestbookPortletKeys.GUESTBOOK,\n                            Guestbook::getGuestbookId));\n                    consumer.accept(\n                        new WorkflowedModelPermissionLogic<>(\n                                _workflowPermission, modelResourcePermission,\n                                _groupLocalService, Guestbook::getGuestbookId));\n                }),\n            properties);\n    }\n\n    @Deactivate\n    public void deactivate() {\n        _serviceRegistration.unregister();\n    }\n\n    @Reference\n    private GuestbookLocalService _guestbookLocalService;\n\n    @Reference(target = \"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\")\n    private PortletResourcePermission _portletResourcePermission;\n\n    private ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n    @Reference\n    private StagingPermission _stagingPermission;\n\n    @Reference\n    private WorkflowPermission _workflowPermission;\n\n    @Reference\n    private GroupLocalService _groupLocalService;\n}\n\\end{verbatim}\n\\end{enumerate}\n\nThis class registers a chain of permission logic classes for checking\npermissions for Guestbook entities. Since this functionality is the same\nfor all entities, all that's necessary is to specify yours in addition\nto the standard Liferay ones for staging and workflow. Introspection is\ndone on your entity by the factory to create the necessary permissions\nservice. You implemented the constants class so you can specify the\nresource model name you defined in \\texttt{default.xml}. The\n\\texttt{model.class.name} is set so that any module needing this service\ncan find this model resource permission by its type.\n\nNow create the registrar for the \\texttt{GuestbookEntry} entity:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a class in the same package called\n  \\texttt{GuestbookEntryModelResourcePermissionRegistrar}.\n\\item\n  The only difference between this class and the one above is that it\n  operates on \\texttt{GuestbookEntry} entities instead of\n  \\texttt{Guestbook} entities (the imports have been left off in the\n  snippet below):\n\n\\begin{verbatim}\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\n@Component(immediate = true)\npublic class GuestbookEntryModelResourcePermissionRegistrar {\n\n @Activate\n    public void activate(BundleContext bundleContext) {\n        Dictionary<String, Object> properties = new HashMapDictionary<>();\n\n        properties.put(\"model.class.name\", GuestbookEntry.class.getName());\n\n        _serviceRegistration = bundleContext.registerService(\n            ModelResourcePermission.class,\n            ModelResourcePermissionFactory.create(\n                GuestbookEntry.class, GuestbookEntry::getEntryId,\n                _guestbookEntryLocalService::getGuestbookEntry, _portletResourcePermission,\n                (modelResourcePermission, consumer) -> {\n                    consumer.accept(\n                        new StagedModelPermissionLogic<>(\n                            _stagingPermission, GuestbookPortletKeys.GUESTBOOK,\n                            GuestbookEntry::getEntryId));\n                    consumer.accept(\n                        new WorkflowedModelPermissionLogic<>(\n                                _workflowPermission, modelResourcePermission,\n                                _groupLocalService, GuestbookEntry::getEntryId));\n                }),\n            properties);\n    }\n\n    @Deactivate\n    public void deactivate() {\n        _serviceRegistration.unregister();\n    }\n\n    @Reference\n    private GuestbookEntryLocalService _guestbookEntryLocalService;\n\n    @Reference(target = \"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\")\n    private PortletResourcePermission _portletResourcePermission;\n\n    private ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n    @Reference\n    private StagingPermission _stagingPermission;\n\n    @Reference\n    private WorkflowPermission _workflowPermission;\n\n    @Reference\n    private GroupLocalService _groupLocalService;\n}\n\\end{verbatim}\n\\item\n  Hit Ctrl-Shift-O to add the imports, and then save the file.\n\\end{enumerate}\n\nFinally, create the registrar for the portlet permissions:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a class in the same package called\n  \\texttt{GuestbookPortletResourcePermissionRegistrar}.\n\\item\n  This class is simpler because you don't have to tell it how to\n  retrieve primary keys from any entity:\n\n\\begin{verbatim}\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\n@Component (immediate = true)\npublic class GuestbookPortletResourcePermissionRegistrar {\n\n        @Activate\n    public void activate(BundleContext bundleContext) {\n        Dictionary<String, Object> properties = new HashMapDictionary<>();\n\n        properties.put(\"resource.name\", GuestbookConstants.RESOURCE_NAME);\n\n        _serviceRegistration = bundleContext.registerService(\n            PortletResourcePermission.class,\n            PortletResourcePermissionFactory.create(\n                GuestbookConstants.RESOURCE_NAME,\n                new StagedPortletPermissionLogic(\n                    _stagingPermission, GuestbookPortletKeys.GUESTBOOK)),\n            properties);\n    }\n\n    @Deactivate\n    public void deactivate() {\n        _serviceRegistration.unregister();\n    }\n\n    private ServiceRegistration<PortletResourcePermission> _serviceRegistration;\n\n    @Reference\n    private StagingPermission _stagingPermission;\n\n}\n\\end{verbatim}\n\\item\n  Type Ctrl-Shift-O to add the imports. Save the file.\n\\end{enumerate}\n\nYou've now completed step two: the R in DRAC: registering permissions.\nNext, you'll enable users to associate permissions with resources.\n\n\\chapter{Assigning Permissions to\nResources}\\label{assigning-permissions-to-resources}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Implementing Permissions</p><p>Step 4 of 5</p>\n\\end{verbatim}\n\nYou've now defined your permissions and registered them in both the\ncontainer and the database so permissions can be checked. Now you'll\ncreate a UI for users to assign permissions along with helper classes to\nmake it easy to check permissions in the final step.\n\nHere's how it works. You have a permission, such as \\texttt{ADD\\_ENTRY},\nand a resource, such as a \\texttt{Guestbook}. For a user to add an entry\nto a guestbook, you must check if that user has the \\texttt{ADD\\_ENTRY}\npermission for that guestbook. Helper classes make it easier to check\npermissions.\n\n\\section{Creating a Guestbook Portlet Permission\nHelper}\\label{creating-a-guestbook-portlet-permission-helper}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Right-click the \\texttt{guestbook-web} module and select \\emph{New} →\n  \\emph{Package}. To follow Liferay's practice, name the package\n  \\texttt{com.liferay.docs.guestbook.web.security.permission.resource}.\n  This is where you'll place your helper classes.\n\\item\n  Right-click the new package and select \\emph{New} → \\emph{Class}. Name\n  the class \\texttt{GuestbookPermission}.\n\\item\n  Replace this class's contents with the following code:\n\n\\begin{verbatim}\npackage com.liferay.docs.guestbook.web.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\n\n@Component(immediate=true)\npublic class GuestbookPermission {\n\n    public static boolean contains(PermissionChecker permissionChecker, long groupId, String actionId) {\n\n        return _portletResourcePermission.contains(permissionChecker, groupId, actionId);\n\n    }\n\n    @Reference(\n            target=\"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\",\n            unbind=\"-\"\n            )\n    protected void setPortletResourcePermission(PortletResourcePermission portletResourcePermission) {\n\n        _portletResourcePermission = portletResourcePermission;\n    }\n\n    private static PortletResourcePermission _portletResourcePermission;\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\nThis class is a component defining one static method (so you don't have\nto instantiate the class) that encapsulates the model you're checking\npermissions for. Liferay's \\texttt{PermissionChecker} class does most of\nthe work: give it the proper resource and action, such as\n\\texttt{ADD\\_ENTRY}, and it returns whether the permission exists or\nnot.\n\nThere's only one method: a \\texttt{check} method that throws an\nexception if the user doesn't have permission.\n\n\\section{Creating Model Permission\nHelpers}\\label{creating-model-permission-helpers}\n\nNext, you'll create helpers for your two entities:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a class in the same package called\n  \\texttt{GuestbookModelPermission.java}.\n\\item\n  Replace this class's contents with the following code:\n\n\\begin{verbatim}\npackage com.liferay.docs.guestbook.web.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n\n@Component(immediate = true)\npublic class GuestbookModelPermission {\n\n    public static boolean contains(\n            PermissionChecker permissionChecker, Guestbook guestbook, String actionId) throws PortalException {\n\n        return _guestbookModelResourcePermission.contains(permissionChecker, guestbook, actionId);\n    }\n\n    public static boolean contains(\n            PermissionChecker permissionChecker, long guestbookId, String actionId) throws PortalException {\n\n        return _guestbookModelResourcePermission.contains(permissionChecker, guestbookId, actionId);\n    }\n\n    @Reference(\n            target = \"(model.class.name=com.liferay.docs.guestbook.model.Guestbook)\",\n            unbind = \"-\")\n    protected void setEntryModelPermission(ModelResourcePermission<Guestbook> modelResourcePermission) {\n\n        _guestbookModelResourcePermission = modelResourcePermission;\n    }\n\n    private static ModelResourcePermission<Guestbook>_guestbookModelResourcePermission;\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\nAs you can see, this class is similar to \\texttt{GuestbookPermission}.\nThe difference is that \\texttt{GuestbookModelPermission} is for the\nmodel/resource permission, so you supply the entity or its primary key\n(\\texttt{guestbookId}).\n\nYour final class is almost identical to\n\\texttt{GuestbookModelPermission}, but it's for the\n\\texttt{GuestbookEntry} entity. Follow these steps to create it:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a class in the same package called\n  \\texttt{GuestbookEntryPermission.java}.\n\\item\n  Replace this class's contents with the following code:\n\n\\begin{verbatim}\npackage com.liferay.docs.guestbook.web.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n\n@Component(immediate = true)\npublic class GuestbookEntryPermission {\n\n    public static boolean contains(\n            PermissionChecker permissionChecker, GuestbookEntry entry, String actionId) throws PortalException {\n\n        return _guestbookEntryModelResourcePermission.contains(permissionChecker, entry, actionId);\n    }\n\n    public static boolean contains(\n            PermissionChecker permissionChecker, long entryId, String actionId) throws PortalException {\n\n        return _guestbookEntryModelResourcePermission.contains(permissionChecker, entryId, actionId);\n    }\n\n    @Reference(\n            target = \"(model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\",\n            unbind = \"-\")\n    protected void setEntryModelPermission(ModelResourcePermission<GuestbookEntry> modelResourcePermission) {\n\n        _guestbookEntryModelResourcePermission = modelResourcePermission;\n    }\n\n    private static ModelResourcePermission<GuestbookEntry>_guestbookEntryModelResourcePermission;\n\n}\n\\end{verbatim}\n\\end{enumerate}\n\nThis class is almost identical to \\texttt{GuestbookModelPermission}. The\nonly difference is that \\texttt{GuestbookEntryPermission} is for the\n\\texttt{GuestbookEntry} entity.\n\nNow you can expose the permissions UI to your users so they can assign\npermissions:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\texttt{init.jsp} in your \\texttt{guestbook-web} project.\n  Add the following imports to the file:\n\n\\begin{verbatim}\n<%@ page import=\"com.liferay.docs.guestbook.web.security.permission.resource.GuestbookModelPermission\" %>\n<%@ page import=\"com.liferay.docs.guestbook.web.security.permission.resource.GuestbookPermission\" %>\n<%@ page import=\"com.liferay.docs.guestbook.web.security.permission.resource.GuestbookEntryPermission\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.portal.kernel.security.permission.ActionKeys\" %>\n\\end{verbatim}\n\n  The first three are the permissions helper classes you just created.\n\\item\n  Save the file.\n\\item\n  Open \\texttt{guestbook\\_actions.jsp} from the\n  \\texttt{guestbook\\_admin} folder. Add this code just after the\n  \\texttt{\\textless{}liferay-ui:icon-delete\\textgreater{}} tag:\n\n\\begin{verbatim}\n<c:if\ntest=\"<%=GuestbookModelPermission.contains(permissionChecker, guestbook.getGuestbookId(), ActionKeys.PERMISSIONS) %>\">\n\n    <liferay-security:permissionsURL\n        modelResource=\"<%= Guestbook.class.getName() %>\"\n        modelResourceDescription=\"<%= guestbook.getName() %>\"\n        resourcePrimKey=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\"\n        var=\"permissionsURL\" />\n\n    <liferay-ui:icon image=\"permissions\" url=\"<%= permissionsURL %>\" />\n\n</c:if>\n\\end{verbatim}\n\\item\n  Save the file.\n\\end{enumerate}\n\nYou just added an action button that displays Liferay's permissions UI\nfor Guestbooks. On top of that, you used the permissions helper you just\ncreated to test whether users can even see the action button. It only\nappears if users have the \\emph{permissions} permission.\n\nYou'll implement this for Guestbook entries in the next step.\n\nCongratulations! You've now created helper classes for your permissions,\nand you've enabled users to associate permissions with their resources.\nThe only thing left is to implement permission checks in the\napplication's view layer. You'll do this next.\n\n\\chapter{Checking for Permission in\nJSPs}\\label{checking-for-permission-in-jsps}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Implementing Permissions</p><p>Step 5 of 5</p>\n\\end{verbatim}\n\nYou've already seen how user interface components can be wrapped in\npermission checks pretty easily. In this step, you'll implement the\nrest.\n\n\\section{Checking Permissions in the\nUI}\\label{checking-permissions-in-the-ui}\n\nRecall that you want to restrict access to three areas in your\napplication:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  The guestbook tabs across the top of your application\n\\item\n  The Add Guestbook button\n\\item\n  The Add Entry button\n\\end{itemize}\n\nFirst, you'll check permissions for the guestbook tabs:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{/guestbook/view.jsp} and find the scriptlet that gets the\n  \\texttt{guestbookId} from the request. Below this are the\n  \\texttt{\\textless{}aui-nav\\textgreater{}} tags that generate the tabs.\n  Remove those tags and all the code between them. In its place, add the\n  following code, which is the same thing with the addition of\n  permission checks:\n\n\\begin{verbatim}\n<aui:nav cssClass=\"nav-tabs\">\n\n    <%\n        List<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n            for (int i = 0; i < guestbooks.size(); i++) {\n\n                Guestbook curGuestbook = guestbooks.get(i);\n                String cssClass = StringPool.BLANK;\n\n                if (curGuestbook.getGuestbookId() == guestbookId) {\n                    cssClass = \"active\";\n                }\n\n                if (GuestbookModelPermission.contains(\n                    permissionChecker, curGuestbook.getGuestbookId(), \"VIEW\")) {\n\n    %>\n\n    <portlet:renderURL var=\"viewPageURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbookwebportlet/view.jsp\" />\n        <portlet:param name=\"guestbookId\"\n            value=\"<%=String.valueOf(curGuestbook.getGuestbookId())%>\" />\n    </portlet:renderURL>\n\n\n    <aui:nav-item cssClass=\"<%=cssClass%>\" href=\"<%=viewPageURL%>\"\n        label=\"<%=HtmlUtil.escape(curGuestbook.getName())%>\" />\n\n    <%  \n                }\n\n            }\n    %>\n\n</aui:nav>\n\\end{verbatim}\n\n  This code gets a list of guestbooks from the database, iterates\n  through them, checks the permission for each against the current\n  user's Roles, and adds the guestbooks the user can access to a list of\n  tabs.\n\n  You've now implemented your first permission check. As you can see,\n  it's relatively straightforward thanks to the static methods in your\n  helper classes. The code above shows the tab only if the current user\n  has the \\texttt{VIEW} permission for the guestbook.\n\n  Next, you'll add permission checks to the Add Entry button.\n\\item\n  Scroll down to the line that reads\n  \\texttt{\\textless{}aui:button-row\\ cssClass=\"guestbook-buttons\"\\textgreater{}}.\n  Just below this line, add the following line of code to check for the\n  \\texttt{ADD\\_ENTRY} permission:\n\n\\begin{verbatim}\n<c:if test='<%= GuestbookPermission.contains(permissionChecker, scopeGroupId, \"ADD_ENTRY\") %>'>\n\\end{verbatim}\n\\item\n  After this is the code that creates the \\texttt{addEntryURL} and the\n  Add Entry button. After the \\texttt{aui:button} tag and above the\n  \\texttt{\\textless{}/aui:button-row\\textgreater{}} tag, add the closing\n  tag for the \\texttt{\\textless{}c:if\\textgreater{}} statement:\n\n\\begin{verbatim}\n</c:if>\n\\end{verbatim}\n\n  You've now implemented your permission check for the Add Entry button\n  by using JSTL tags.\n\\item\n  Save the file.\n\\end{enumerate}\n\nNext, you'll add permission checking to \\texttt{entry\\_actions.jsp} to\ndetermine what options appear for logged in users who can see the\nactions menu in the portlet. Just like before, you'll wrap each\n\\texttt{renderURL} in an \\texttt{if} statement that checks the\npermissions against available actions. To do this, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open\n  \\texttt{src/main/resources/META-INF/resources/guestbook/entry\\_actions.jsp}.\n\\item\n  Remove all the code from this file and replace it with what's below:\n\n  ```markup \\textless\\%@include file=``../init.jsp''\\%\\textgreater{}\n\n\\begin{verbatim}\n<%\nString mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\nResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);\n\nGuestbookEntry entry = (GuestbookEntry)row.getObject(); \n%>\n\n<liferay-ui:icon-menu>\n\n    <c:if\n        test=\"<%= GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.UPDATE) %>\">\n        <portlet:renderURL var=\"editURL\">\n            <portlet:param name=\"entryId\"\n                value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n            <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n        </portlet:renderURL>\n\n        <liferay-ui:icon image=\"edit\" message=\"Edit\"\n            url=\"<%=editURL.toString() %>\" />\n    </c:if>\n\n    <c:if\n    test=\"<%=GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.PERMISSIONS) %>\">\n\n        <liferay-security:permissionsURL\n            modelResource=\"<%= GuestbookEntry.class.getName() %>\"\n            modelResourceDescription=\"<%= entry.getMessage() %>\"\n            resourcePrimKey=\"<%= String.valueOf(entry.getEntryId()) %>\"\n            var=\"permissionsURL\" />\n\n        <liferay-ui:icon image=\"permissions\" url=\"<%= permissionsURL %>\" />\n\n    </c:if>\n\n    <c:if\n        test=\"<%=GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.DELETE) %>\">\n\n        <portlet:actionURL name=\"deleteEntry\" var=\"deleteURL\">\n            <portlet:param name=\"entryId\"\n                value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n            <portlet:param name=\"guestbookId\"\n                value=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n        </portlet:actionURL>\n\n        <liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n    </c:if>\n\n</liferay-ui:icon-menu>\n```\n\\end{verbatim}\n\\item\n  Save the file.\n\\end{enumerate}\n\nThis code updates each button with a permissions check. If the current\nuser can't perform the given action, the action doesn't appear.\n\nExcellent! You've now implemented all the permissions checks for the\nGuestbook portlet.\n\n\\section{Testing the Application}\\label{testing-the-application}\n\nBefore testing the application, you must reset your database, because\nguestbook entries you created without resources won't work with\npermissions.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If your server is running, shut it down.\n\\item\n  Find your Liferay Workspace on your file system (it should be inside\n  your Eclipse workspace). Inside the \\texttt{bundles/data} folder is a\n  \\texttt{hypersonic} folder.\n\\item\n  Remove everything from the \\texttt{hypersonic} folder.\n\\item\n  Restart your server.\n\\end{enumerate}\n\nAdd new guestbooks and entries to test your application with different\nusers. Administrative users see all the buttons, regular users see the\nAdd Entry button, and guests see no buttons at all (but can navigate).\n\nNow see if you can do the same for the Guestbook Admin portlet. Don't\nworry if you can't: at the end of this Learning Path is a link to the\ncompleted project for you to examine.\n\nGreat! The next step is to integrate search and indexing into your\napplication. This is a prerequisite for the much more powerful stuff to\ncome.\n\n\\chapter{Search and Indexing}\\label{search-and-indexing}\n\nThe Guestbook and Guestbook Admin portlets are up and running. The\nGuestbook portlet lets users add, edit, delete, and configure\npermissions for Guestbook Entries. The Guestbook Admin portlet lets Site\nadministrators create, edit, delete, and configure permissions for\nGuestbooks. In the case of a very popular event (maybe a \\emph{Lunar\nLuau} dinner at the Lunar Resort), there could be many Guestbook Entries\nin the portlet, and users might want to search for Entries that\nmentioned the delicious low-gravity ham that was served (melts in your\nmouth). Searching for the word \\emph{ham} should display these Guestbook\nentries. In short, Guestbook entries must be searchable via a search bar\nin the Guestbook portlet.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} In previous versions of Liferay DXP, search was only\n\\emph{permissions aware} (indexed with the entity's permissions and\nsearched with those permissions intact) if the application developer\nspecified this line in the \\texttt{Indexer} class's constructor:\n\n\\begin{verbatim}\n setPermissionAware(true);\n\\end{verbatim}\n\nNow, search is permissions aware by default \\emph{if the new permissions\napproach}, as described in the previous step of this tutorial and in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions}{these\narticles}, is implemented for an application.\n\n\\noindent\\hrulefill\n\nTo enable search, you must index Guestbooks and Guestbook entries.\nAlthough you probably won't have enough Guestbooks in a Site to warrant\nsearching the Guestbook Admin portlet, indexing Guestbooks has other\nbenefits. In a later step, you'll asset-enable Guestbooks and Guestbook\nentries so the Asset Publisher can display them. Enabling search is a\nprerequisite for this, because the Asset Publisher uses the index to\nfind assets.\n\nIf you get stuck,\n\\href{https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook}{source\ncode} for this step is provided.\n\nBut assets are for later. Right now it's time to index those Guestbooks.\nReady?\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-portlet-search.png}\n\\caption{Add a search bar so users can search for Guestbook Entries. If\na message or name matches the search query, the Entry is displayed in\nthe search results.}\n\\end{figure}\n\nLet's Go!{}\n\n\\chapter{Enabling Search and Indexing for\nGuestbooks}\\label{enabling-search-and-indexing-for-guestbooks}\n\nIn this section, you'll create the classes that control these aspects of\nthe search functionality:\n\n\\begin{itemize}\n\\item\n  Registration:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{GuestbookSearchRegistrar} registers the search services to\n    the search framework for the Guestbook entity.\n  \\end{itemize}\n\\item\n  Indexing:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{GuestbookModelDocumentContributor} controls which Guestbook\n    fields are indexed in the search engine.\n  \\item\n    \\texttt{GuestbookModelIndexerWriterContributor} configures the\n    re-indexing and batch re-indexing behavior for Guestbooks.\n  \\end{itemize}\n\\item\n  Querying:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{GuestbookKeywordQueryContributor} contributes clauses to the\n    ongoing search query.\n  \\item\n    \\texttt{GuestbookModelPreFilterContributor} controls how search\n    results are filtered before they're returned from the search engine.\n  \\end{itemize}\n\\item\n  Generating Result Summaries:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{GuestbookModelSummaryContributor} constructs the result\n    summary for Guestbooks, including specifying which fields to use.\n  \\end{itemize}\n\\end{itemize}\n\nAfter creating the search classes, you'll modify the service layer to\nupdate the search index when a guestbook is persisted. Specifically,\n\\texttt{GuestbookLocalServiceImpl}'s \\texttt{addGuestbook},\n\\texttt{updateGuestbook}, and \\texttt{deleteGuestbook} methods are\nupdated to invoke the guestbook indexer.\n\nIn prior versions of Liferay DXP, search and indexing was accomplished\nwith one \\texttt{*Indexer} class that extended \\texttt{BaseIndexer}. In\n7.0 is a new pattern that relies on\n\\href{https://stackoverflow.com/questions/2399544/difference-between-inheritance-and-composition}{composition\ninstead of inheritance}. If you want to use the old approach, feel free\nto extend \\texttt{BaseIndexer}. It's still supported.\n\nSince there's no reason to search for guestbooks in the UI, only\nback-end work is necessary.\n\nLet's Go!{}\n\n\\chapter{Understanding Search and\nIndexing}\\label{understanding-search-and-indexing}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Search and Indexing for Guestbooks</p><p>Step 1 of 6</p>\n\\end{verbatim}\n\nBy default, Liferay DXP uses Elasticsearch, a search engine backed by\nthe popular Lucene search library, to implement its search and indexing\nfunctionality. You could search the database, but that requires\nresource-hogging table merges. Instead, a search engine like\nElasticsearch converts searchable entities into \\emph{documents}. In\nElasticsearch, documents are searchable database entities converted into\nJSON objects. After you implement indexing for guestbook entries,\nLiferay DXP creates a document for each entry. The indexing code\nspecifies which guestbook entry fields to add to each guestbook entry\ndocument, and it adds all the guestbook entry documents to an index. A\nsearch returns a \\emph{hits} object containing pointers to documents\nmatching the search query. Searching for entities with a search engine\nvia an index is faster than searching for entities in the database.\nElasticsearch provides some additional features like relevancy scoring\nand fuzzy search queries.\n\nAlong with the search engine, Liferay DXP has its own search\ninfrastructure. Liferay DXP adds the following features to the existing\nElasticsearch API:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Indexed documents include the fields needed by Liferay DXP (e.g.,\n  \\texttt{entryClassName}, \\texttt{entryClassPK},\n  \\texttt{assetTagNames}, \\texttt{assetCategories}, \\texttt{companyId},\n  \\texttt{groupId}, staging status).\n\\item\n  It ensures the scope of returned search results is appropriate by\n  applying the right filters to search requests.\n\\item\n  It provides permission checking and hit summaries to display in the\n  search portlet.\n\\end{itemize}\n\nTo understand how the search and indexing code presented here makes your\ncustom models seamlessly searchable, you must know how to influence each\nportion of the search and indexing cycle:\n\n\\textbf{Indexing}: Model entities store data fields in the database. For\nexample, Guestbooks store the \\emph{name} field. During the cycle's\nIndexing step, you prepare the model entity to be searchable by defining\nthe model's fields that are sent to the search engine, later used during\na search.\n\n\\textbf{To influence the way model entity fields are indexed in search\nengine documents,}\n\n\\texttt{ModelDocumentContributor} classes specify which database fields\nare indexed in the model entity's search engine documents. This class's\n\\texttt{contribute} method is called each time the \\texttt{add} and\n\\texttt{update} methods in the entity's service layer are called.\n\n\\texttt{ModelIndexerWriterContributor} classes configure the re-indexing\nand batch re-indexing behavior for the model entity. This class's method\nis called when a re-index is triggered from the Search administrative\napplication found in Control Panel → Configuration → Search.\n\n\\textbf{Searching:} Most searches start with a user entering keywords\ninto a search bar. The entered keywords are processed by the back-end\nsearch infrastructure, transformed into a \\emph{query} the search engine\ncan understand, and used to match against each search document's fields.\n\n\\textbf{To exert control over the way your model entity documents are\nsearched,}\n\n\\texttt{KeywordQueryContributor} classes contribute clauses to the\nongoing search query. This is called at search time, and ensures that\nall the fields you indexed are also the ones searched. For example, if\nyou index fields with the Site locale appended to them\n(\\texttt{title\\_en\\_us}, for example), you want the search query to\ninclude the same locale when the document is searched. If the search\nquery contain another locale (like \\texttt{title\\_es\\_ES}) or searches\nthe plain field (\\texttt{title}), inaccurate results are returned.\n\n\\texttt{ModelPreFilterContributor}s control how search results are\nfiltered before they're returned from the search engine. For example,\nadding the workflow status to the query ensures that an entity in the\ntrash isn't returned in the search results. For the Guestbook\napplication, a \\texttt{ModelPrefilterContributor} isn't necessary until\nyou get to the section on workflow-enabling Guestbooks.\n\n\\textbf{Returning Results:} When a model entity's indexed search\ndocument is obtained during a search request, it's converted into a\nsummary of the model entity.\n\n\\textbf{To influence the result summaries for your model entity\ndocuments,}\n\n\\texttt{ModelSummaryContributor} classes get the \\texttt{Summary} object\ncreated for each search document, so you can manipulate it by adding\nspecific fields or setting the length of the displayed content.\n\n\\texttt{ModelVisibilityContributor} classes control the visibility of\nmodel entities that can be attached to other asset types (for example,\nFile Entries can be attached to Wiki Pages), in the search context.\nSince Guestbooks and Guestbook entries won't be attached to other\nassets, a model visibility contributor isn't necessary.\n\nOne important step must occur to make sure the above classes are\ndiscovered by the search framework.\n\n\\textbf{Registration}\n\nTo register the model entity with Liferay's search framework,\n\n\\texttt{SearchRegistrar}s use the search framework's registry to define\ncertain things about your model entity's \\texttt{ModelSearchDefinition}:\nwhich fields are used by default to retrieve documents from the search\nengine, and which optional search services are registered for your\nentity. Registration occurs as soon as the Component is activated\n(during portal startup).\n\nLet's index some Guestbooks, shall we?\n\n\\chapter{Registering Guestbooks with the Search\nFramework}\\label{registering-guestbooks-with-the-search-framework}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Search and Indexing for Guestbooks</p><p>Step 2 of 6</p>\n\\end{verbatim}\n\nFirst, update your \\texttt{build.gradle} with the necessary imports.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\texttt{build.gradle} file in your \\texttt{guestbook-service}\n  project.\n\\item\n  Add the Search Service Provider Interface and API dependencies to the\n  \\texttt{build.gradle} file:\n\n\\begin{verbatim}\ncompileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.spi\"\ncompileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.api\"\n\\end{verbatim}\n\\item\n  Save the file and run \\texttt{Refresh\\ Gradle\\ Project}.\n\\end{enumerate}\n\nOnce the dependency is configured, register the Search services that\nbuild the entity's \\texttt{ModelSearchDefinition}.\n\nA \\texttt{*SearchRegistrar} specifies the classes that the entity uses\nto contribute to building a \\texttt{ModelSearchDefinition}. Activation\nof the \\texttt{SearchRegistrar} component results in a cascade of\nactivity in the search framework, culminating with the building of a\n\\texttt{DefaultIndexer}. The \\texttt{DefaultIndexer} is registered under\nthe class name defined in the registrar, and then used for\nindexing/searching objects of that class.\n\nCreate the \\texttt{GuestbookSearchRegistrar}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new package in the \\texttt{guestbook-service} module\n  project's \\texttt{src/main/java} folder called\n  \\texttt{com.liferay.docs.guestbook.search}. In this package, create a\n  new class called \\texttt{GuestbookSearchRegistrar} and populate it\n  with two methods, \\texttt{activate} and \\texttt{deactivate}.\n\n\\begin{verbatim}\n@Component(immediate = true)\npublic class GuestbookSearchRegistrar {\n\n    @Activate\n    protected void activate(BundleContext bundleContext) {\n\n        _serviceRegistration = modelSearchRegistrarHelper.register(\n            Guestbook.class, bundleContext, modelSearchDefinition -> {\n                modelSearchDefinition.setDefaultSelectedFieldNames(\n                    Field.ASSET_TAG_NAMES, Field.COMPANY_ID, Field.CONTENT,\n                    Field.ENTRY_CLASS_NAME, Field.ENTRY_CLASS_PK,\n                    Field.GROUP_ID, Field.MODIFIED_DATE, Field.SCOPE_GROUP_ID,\n                    Field.TITLE, Field.UID);\n\n                modelSearchDefinition.setModelIndexWriteContributor(\n                    modelIndexWriterContributor);\n                modelSearchDefinition.setModelSummaryContributor(\n                    modelSummaryContributor);\n            });\n    }\n\n    @Deactivate\n    protected void deactivate() {\n\n        _serviceRegistration.unregister();\n    }\n\\end{verbatim}\n\n  The annotations \\texttt{@Activate} and \\texttt{@Deactivate} ensure\n  each method is invoked as soon as the Component is started (activated)\n  or when it's about to be stopped (deactivated). On activation of the\n  Component, a chain of search and indexing classes is registered for\n  the Guestbook entity. Set the default selected field names used to\n  retrieve results documents from the search engine. Then set the\n  contributors used to build a model search definition.\n\\item\n  Specify the service references for the class:\n\n\\begin{verbatim}\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.Guestbook)\")\n    protected ModelIndexerWriterContributor<Guestbook> modelIndexWriterContributor;\n\n    @Reference\n    protected ModelSearchRegistrarHelper modelSearchRegistrarHelper;\n\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.Guestbook)\")\n    protected ModelSummaryContributor modelSummaryContributor;\n\n    private ServiceRegistration<?> _serviceRegistration;\n\n}\n\\end{verbatim}\n\n  Target the \\texttt{Guestbook} model while looking up a reference to\n  the contributor classes. Later, when you create these contributor\n  classes, you'll specify the model name again to complete the circle.\n\\item\n  Add the imports by Organizing Imports (Ctrl-Shift-O). Choose the\n  \\texttt{com.liferay.portal.kernel.search.Field} class.\n\\item\n  Export the \\texttt{com.liferay.docs.guestbook.search} package in the\n  \\texttt{guestbook-service} module's \\texttt{bnd.bnd} file. The export\n  section should look like this:\n\n\\begin{verbatim}\nExport-Package: com.liferay.docs.guestbook.search\n\\end{verbatim}\n\\end{enumerate}\n\nThe Guestbook search and indexing class registration is completed. Next,\nyou'll write the search and indexing logic.\n\n\\chapter{Indexing Guestbooks}\\label{indexing-guestbooks}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Search and Indexing for Guestbooks</p><p>Step 3 of 6</p>\n\\end{verbatim}\n\nTo control how Guestbook objects are translated into search engine\ndocuments, create two classes in the new search package:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Implement a \\texttt{ModelDocumentContributor} that ``contributes''\n  fields to a search document indexed by the search engine. The main\n  searchable field for guestbooks is the guestbook name.\n\\item\n  \\texttt{ModelIndexerWriterContributor} configures the batch indexing\n  behavior for Guestbooks. This code is executed when Guestbooks are\n  re-indexed from the Search administration section of the Control\n  Panel.\n\\end{enumerate}\n\n\\section{\\texorpdfstring{Implementing\n\\texttt{ModelDocumentContributor}}{Implementing ModelDocumentContributor}}\\label{implementing-modeldocumentcontributor}\n\nCreate \\texttt{GuestbookModelDocumentContributor} and populate it with\nthis:\n\n\\begin{verbatim}\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelDocumentContributor.class\n)\npublic class GuestbookModelDocumentContributor\n    implements ModelDocumentContributor<Guestbook> {\n\n    @Override\n    public void contribute(Document document, Guestbook guestbook) {\n        try {\n            document.addDate(Field.MODIFIED_DATE, guestbook.getModifiedDate());\n\n            Locale defaultLocale = PortalUtil.getSiteDefaultLocale(\n    guestbook.getGroupId());\n\n            String localizedTitle = LocalizationUtil.getLocalizedName(\n    Field.TITLE, defaultLocale.toString());\n\n            document.addText(localizedTitle, guestbook.getName());\n        } catch (PortalException pe) {\n            if (_log.isWarnEnabled()) {\n                _log.warn(\n    \"Unable to index guestbook \" + guestbook.getGuestbookId(), pe);\n            }\n        }\n    }\n\n    private static final Log _log = LogFactoryUtil.getLog(\n    GuestbookModelDocumentContributor.class);\n\n}\n\\end{verbatim}\n\nBecause Liferay DXP supports localization, you should too. The above\ncode gets the default locale from the Site by passing the\n\\texttt{Guestbook}'s group ID to the \\texttt{getSiteDefaultLocale}\nmethod, then using it to get the localized name of the Guestbook's title\nfield. The retrieved Site locale is appended to the field (e.g.,\n\\texttt{title\\_en\\_US}), so the field gets passed to the search engine\nand goes through the right analysis and\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/analysis-tokenizers.html}{tokenization}.\n\nUse Ctrl-Shift-O to add these imports, and then save the file:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Field}\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Document}\n\\end{itemize}\n\n\\section{\\texorpdfstring{Implementing\n\\texttt{ModelIndexerWriterContributor}}{Implementing ModelIndexerWriterContributor}}\\label{implementing-modelindexerwritercontributor}\n\nCreate \\texttt{GuestbookModelIndexerWriterContributor} and populate it\nwith this:\n\n\\begin{verbatim}\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelIndexerWriterContributor.class\n)\npublic class GuestbookModelIndexerWriterContributor\n    implements ModelIndexerWriterContributor<Guestbook> {\n\n    @Override\n    public void customize(\n        BatchIndexingActionable batchIndexingActionable,\n        ModelIndexerWriterDocumentHelper modelIndexerWriterDocumentHelper) {\n\n        batchIndexingActionable.setPerformActionMethod((Guestbook guestbook) -> {\n            Document document = modelIndexerWriterDocumentHelper.getDocument(\n    guestbook);\n\n            batchIndexingActionable.addDocuments(document);\n        });\n    }\n\n    @Override\n    public BatchIndexingActionable getBatchIndexingActionable() {\n        return dynamicQueryBatchIndexingActionableFactory.getBatchIndexingActionable(\n    guestbookLocalService.getIndexableActionableDynamicQuery());\n    }\n\n    @Override\n    public long getCompanyId(Guestbook guestbook) {\n        return guestbook.getCompanyId();\n    }\n\n    @Override\n    public void modelIndexed(Guestbook guestbook) {\n        guestbookEntryBatchReindexer.reindex(\n    guestbook.getGuestbookId(), guestbook.getCompanyId());\n    }\n\n    @Reference\n    protected DynamicQueryBatchIndexingActionableFactory\n    dynamicQueryBatchIndexingActionableFactory;\n\n    @Reference\n    protected GuestbookEntryBatchReindexer guestbookEntryBatchReindexer;\n\n    @Reference\n    protected GuestbookLocalService guestbookLocalService;\n\n}\n\\end{verbatim}\n\nFirst look at the \\texttt{customize} method. Configure the batch\nindexing behavior for the entity's documents by calling\n\\texttt{BatchIndexingActionable} methods. This code uses the Guestbook's\nactionable dynamic query helper method to retrieve all Guestbooks in the\nvirtual instance (identified by the Company ID). Service Builder\ngenerated this query method for you when you built the services. Each\nGuestbook's document is then retrieved and added to a collection.\n\nWhen you write the indexing classes for Guestbook entries, you'll add\nthe Guestbook title to the \\texttt{GuestbookEntry} document. Thus, you\nmust provide a way to update the indexed \\texttt{GuestbookEntry}\ndocuments if a Guestbook title is changed. The \\texttt{modelIndexed}\nmethod calls a \\texttt{reindex} method from an interface that will be\ncreated later for \\texttt{GuestbookEntry}s. For now, ignore the error in\nthe \\texttt{modelIndexed} method.\n\nUse Ctrl-Shift-O to add this import, and save the file:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Document}\n\\end{itemize}\n\nOnce the re-indexing behavior is in place, you can move on to\ncontrolling how Guestbook documents are queried from the search engine.\n\n\\chapter{Querying for Guestbook\nDocuments}\\label{querying-for-guestbook-documents}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Search and Indexing for Guestbooks</p><p>Step 4 of 6</p>\n\\end{verbatim}\n\nThe code is in place for for indexing Guestbooks to the search engine.\nNext, you'll code the behavior necessary for querying the indexed\ndocuments.\n\nImplement two interfaces:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\texttt{KeywordQueryContributor} contributes clauses to the ongoing\n  search query.\n\\item\n  \\texttt{ModelPreFilterContributor} controls how search results are\n  filtered before they're returned from the search engine.\n\\end{enumerate}\n\n\\section{\\texorpdfstring{Implementing\n\\texttt{KeywordQueryContributor}}{Implementing KeywordQueryContributor}}\\label{implementing-keywordquerycontributor}\n\nCreate \\texttt{GuestbookKeywordQueryContributor}:\n\n\\begin{verbatim}\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = KeywordQueryContributor.class\n)\npublic class GuestbookKeywordQueryContributor\n    implements KeywordQueryContributor {\n\n    @Override\n    public void contribute(\n        String keywords, BooleanQuery booleanQuery,\n        KeywordQueryContributorHelper keywordQueryContributorHelper) {\n\n        SearchContext searchContext =\n    keywordQueryContributorHelper.getSearchContext();\n\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.TITLE, false);\n    }\n\n    @Reference\n    protected QueryHelper queryHelper;\n\n}\n\\end{verbatim}\n\nThis class adds Guestbook fields to the search query constructed by the\nSearch application in Liferay DXP. Later, when you asset-enable\nGuestbooks, this code allows indexed Guestbooks to be searched from the\nSearch application when a keyword is entered into the search bar. Use\nthe query helper to add search terms to the query that allow Guestbooks\nto be found. Here it's important to note that adding the localized\nsearch term is important. Since the localized Guestbook title was\nindexed, you must retrieve the localized value from the search engine.\n\nDon't forget to add imports with Ctrl-Shift-O. Choose\n\\texttt{com.liferay.portal.kernel.search.BooleanQuery} and\n\\texttt{com.liferay.portal.kernel.search.Field}.\n\nOnce the query code is in place, define how returned Guestbook documents\nare summarized.\n\n\\chapter{Generating Results\nSummaries}\\label{generating-results-summaries}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Search and Indexing for Guestbooks</p><p>Step 5 of 6</p>\n\\end{verbatim}\n\nThe Search application and the Asset Publisher application must display\nresults retrieved from the search engine. Control the summarized content\nby implementing a \\texttt{ModelSummaryContributor}.\n\nA summary is a condensed, text-based version of the entity's document\nthat can be displayed generically. You create it by combining key parts\nof the entity's data so users can browse through search resmlts to find\nthe entity they want.\n\nCreate a \\texttt{GuestbookModelSummaryContributor}:\n\n\\begin{verbatim}\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelSummaryContributor.class\n)\npublic class GuestbookModelSummaryContributor\n    implements ModelSummaryContributor {\n\n    @Override\n    public Summary getSummary(\n        Document document, Locale locale, String snippet) {\n\n        Summary summary = createSummary(document);\n\n        summary.setMaxContentLength(200);\n\n        return summary;\n    }\n\n    private Summary createSummary(Document document) {\n        String prefix = Field.SNIPPET + StringPool.UNDERLINE;\n\n        String title = document.get(prefix + Field.TITLE, Field.TITLE);\n\n        return new Summary(title, StringPool.BLANK);\n    }\n\n}\n\\end{verbatim}\n\nFirst override \\texttt{getSummary} and set the maximum summary length on\nthe summary returned. The value \\texttt{200} is a Liferay standard.\nControl the summary creation in a utility method called\n\\texttt{createSummary}. Create a \\texttt{prefix} variable using two\nconstants, \\texttt{Field.SNIPPET} and \\texttt{Stringpool.UNDERLINE}. The\n\\texttt{snippet\\_title} field is returned from the \\texttt{document.get}\ncall, and added to the summary. Using the snippet field provides two\nbenefits:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Snippet text can be highlighted so matching keywords are emphasized.\n\\item\n  Snippet text can be shortened automatically by the Search application\n  so a sensible portion of the field's text is displayed in the search\n  results.\n\\end{enumerate}\n\nGuestbook titles are likely short, so only the highlighting behavior is\nuseful for the title field of Guestbooks. For longer fields (like some\n\\texttt{content} fields), the clipping behavior is more useful.\nAdditional highlighting behavior can be configured via the\n\\texttt{index.search.highlight.*} properties in\n\\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Lucene\\%20Search}{portal.properties}.\n\nCreate summaries by combining key parts of the entity's data so users\ncan browse through search results to find the entity they want.\n\nDon't forget to Ctrl-Shift-O and import these classes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Field}\n\\item\n  \\texttt{com.liferay.petra.string.StringPool}\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Summary}\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Document}\n\\end{itemize}\n\nSave your file.\n\nOnce all the search and indexing logic is in place, update the service\nlayer so \\texttt{add}, \\texttt{update}, and \\texttt{delete} service\ncalls trigger the new logic.\n\n\\chapter{Handling Indexing in the Guestbook Service\nLayer}\\label{handling-indexing-in-the-guestbook-service-layer}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Search and Indexing for Guestbooks</p><p>Step 6 of 6</p>\n\\end{verbatim}\n\nWhenever a Guestbook database entity is added, updated, or deleted, the\nsearch index must be updated accordingly. The Liferay DXP annotation\n\\texttt{@Indexable} combines with the \\texttt{IndexableType} to mark\nyour service methods so documents can be updated or deleted. Next,\nyou'll annotate \\texttt{addGuestbook}, \\texttt{updateGuestbook}, and\n\\texttt{deleteGuestbook} service methods.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{GuestbookLocalServiceImpl} in the\n  \\texttt{guestbook-service} module's\n  \\texttt{com.liferay.docs.guestbook.service.impl} package and add the\n  following annotation above the method signature for the\n  \\texttt{addGuestbook} and \\texttt{updateGuestbook} methods:\n\n\\begin{verbatim}\n@Indexable(type = IndexableType.REINDEX)\npublic Guestbook addGuestbook(...)\n\n@Indexable(type = IndexableType.REINDEX)\npublic Guestbook updateGuestbook(...)\n\\end{verbatim}\n\n  The \\texttt{@Indexable} annotation indicates that an index update is\n  required following the method execution. The indexing classes control\n  the type of index: setting the \\texttt{@Indexable} annotation type to\n  \\texttt{IndexableType.REINDEX} updates the document in the index that\n  corresponds to the updated Guestbook.\n\\item\n  Add the following annotation above the method signature for the\n  \\texttt{deleteGuestbook} method:\n\n\\begin{verbatim}\n@Indexable(type = IndexableType.DELETE)\npublic Guestbook deleteGuestbook(...)\n\\end{verbatim}\n\n  When a Guestbook is deleted from the database, its document shouldn't\n  remain in the search index. This ensures that it is deleted.\n\\item\n  Use Ctrl-Shift-O to add the necessary imports:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\n\\end{verbatim}\n\n  Save the file.\n\\item\n  In the Gradle Tasks pane on the right-hand side of Liferay Dev Studio\n  DXP, double-click \\texttt{buildService} in \\texttt{guestbook-service}\n  → \\texttt{build}. This re-runs Service Builder to incorporate your\n  changes to \\texttt{GuestbookLocalServiceImpl}.\n\\end{enumerate}\n\nNext, you'll enable search and indexing for Guestbook Entries.\n\n\\chapter{Enabling Search and Indexing for\nEntries}\\label{enabling-search-and-indexing-for-entries}\n\nNow you'll create the classes that control these aspects of the search\nfunctionality:\n\n\\begin{itemize}\n\\item\n  Registration:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{GuestbookEntrySearchRegistrar} registers the search service\n    for the \\texttt{GuestbookEntry} entity.\n  \\end{itemize}\n\\item\n  Indexing:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{GuestbookEntryModelDocumentContributor} controls which\n    \\texttt{GuestbookEntry} fields are indexed in the search engine.\n  \\item\n    \\texttt{GuestbookEntryModelIndexerWriterContributor} configures the\n    re-indexing and batch re-indexing behavior for\n    \\texttt{GuestbookEntry}s.\n  \\item\n    \\texttt{GuestbookEntryBatchReindexer}, an interface, and its\n    \\texttt{GuestbookEntryBatchReindexerImpl}, for re-indexing\n    \\texttt{GuestbookEntries}when their Guestbook is updated.\n  \\end{itemize}\n\\item\n  Querying:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{GuestbookEntryKeywordQueryContributor} contributes clauses\n    to the ongoing search query.\n  \\item\n    \\texttt{GuestbookEntryModelPreFilterContributor} controls how search\n    results are filtered before they're returned from the search engine.\n  \\end{itemize}\n\\item\n  Generating Result Summaries:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{GuestbookEntryModelSummaryContributor} constructs the result\n    summary for \\texttt{GuestbookEntry}s, including specifying which\n    fields to use.\n  \\end{itemize}\n\\end{itemize}\n\nAfter creating the search classes, modify the service layer to update\nthe search index when a \\texttt{GuestbookEntry}is persisted:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Update \\texttt{GuestbookEntryLocalServiceImpl}'s \\texttt{addEntry},\n  \\texttt{updateEntry}, and \\texttt{deleteEntry} methods to update the\n  index so it matches the database.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} In prior versions of Liferay DXP, search and indexing was\naccomplished with one \\texttt{*Indexer} class that extended\n\\texttt{BaseIndexer}. This tutorial demonstrates a new pattern that\nrelies on\n\\href{https://stackoverflow.com/questions/2399544/difference-between-inheritance-and-composition}{composition\ninstead of inheritance}. If you desire to use the old approach, feel\nfree to extend \\texttt{BaseIndexer}. It's still supported.\n\n\\noindent\\hrulefill\n\nLet's Go!{}\n\n\\chapter{Registering Entries with the Search\nFramework}\\label{registering-entries-with-the-search-framework}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Search and Indexing for Entries</p><p>Step 1 of 5</p>\n\\end{verbatim}\n\nThe search registrar for Entries is very similar to the one created for\nGuestbooks. You'll even put it in the same package\n(\\texttt{com.liferay.docs.guestbook.search}).\n\nCreate the \\texttt{GuestbookEntrySearchRegistrar}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In \\texttt{com.liferay.docs.guestbook.search}, create a new class\n  called \\texttt{GuestbookEntrySearchRegistrar} and populate it with two\n  methods, \\texttt{activate} and \\texttt{deactivate}.\n\n\\begin{verbatim}\n@Component(immediate = true)\npublic class GuestbookEntrySearchRegistrar {\n\n    @Activate\n    protected void activate(BundleContext bundleContext) {\n\n        _serviceRegistration = modelSearchRegistrarHelper.register(\n            GuestbookEntry.class, bundleContext, modelSearchDefinition -> {\n                modelSearchDefinition.setDefaultSelectedFieldNames(\n                    Field.COMPANY_ID, Field.ENTRY_CLASS_NAME,\n                    Field.ENTRY_CLASS_PK, Field.UID, \n                    Field.SCOPE_GROUP_ID, Field.GROUP_ID);\n\n                modelSearchDefinition.setDefaultSelectedLocalizedFieldNames(\n                    Field.TITLE, Field.CONTENT);\n\n                modelSearchDefinition.setModelIndexWriteContributor(\n                    modelIndexWriterContributor);\n                modelSearchDefinition.setModelSummaryContributor(\n                    modelSummaryContributor);\n                modelSearchDefinition.setSelectAllLocales(true);\n\n            });\n    }\n\n    @Deactivate\n    protected void deactivate() {\n        _serviceRegistration.unregister();\n    }\n\\end{verbatim}\n\\end{enumerate}\n\nAs you did with Guestbooks, set the default selected field names used to\nretrieve results documents from the search engine. For entries, call\n\\texttt{setDefaultSelectedLocalizedFieldNames} for the title and content\nfields. This ensures that the localized version of the field is searched\nand returned. The only other difference with Entries is the call to\n\\texttt{setSelectAllLocales(true)}. It takes the fields set in\n\\texttt{setDefaultSelectedLocalizedFieldNames} and sets those fields for\neach available locale in the \\texttt{stored\\_fields} parameter of the\nsearch request. If not set to \\texttt{true}, only a single locale is\nsearched.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Specify the service references for the class:\n\n\\begin{verbatim}\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n    protected ModelIndexerWriterContributor<GuestbookEntry> modelIndexWriterContributor;\n\n    @Reference\n    protected ModelSearchRegistrarHelper modelSearchRegistrarHelper;\n\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n    protected ModelSummaryContributor modelSummaryContributor;\n\n    private ServiceRegistration<?> _serviceRegistration;\n\n}\n\\end{verbatim}\n\n  Target the \\texttt{GuestbookEntry} model while looking up a reference\n  to the contributor classes. Later, when you create these contributor\n  classes, you'll specify the model name again to complete the circle.\n\\item\n  Use Ctrl-Shift-O to add imports:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{com.liferay.portal.kernel.search.Field}\n  \\end{itemize}\n\\end{enumerate}\n\nThe entry search and indexing class registration is completed. Next,\nyou'll write the search and indexing logic.\n\n\\chapter{Indexing Entries}\\label{indexing-entries}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Search and Indexing for Entries</p><p>Step 2 of 5</p>\n\\end{verbatim}\n\nTo control how \\texttt{GuestbookEntry}objects are translated into search\nengine documents, create these classes in the search package:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\texttt{ModelDocumentContributor}: The main searchable fields for\n  entries are \\emph{Name} and \\emph{Message}. The Guestbook name\n  associated with the entry is indexed, too.\n\\item\n  \\texttt{ModelIndexerWriterContributor} configures the batch indexing\n  behavior for entries. This code is executed when Entries are\n  re-indexed from the Search administration section of the Control\n  Panel.\n\\item\n  A new interface, \\texttt{GuestbookEntryBatchReindexer}, with its\n  implementation, \\texttt{GuestbookEntryBatchReindexerImpl}. These\n  classes contain code to ensure that entries are re-indexed when their\n  Guestbook is updated.\n\\end{enumerate}\n\n\\section{\\texorpdfstring{Implementing\n\\texttt{ModelDocumentContributor}}{Implementing ModelDocumentContributor}}\\label{implementing-modeldocumentcontributor-1}\n\nCreate \\texttt{GuestbookEntryModelDocumentContributor} and populate it\nwith this:\n\n\\begin{verbatim}\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelDocumentContributor.class\n)\npublic class GuestbookEntryModelDocumentContributor\n    implements ModelDocumentContributor<GuestbookEntry> {\n\n    @Override\n    public void contribute(Document document, GuestbookEntry entry) {\n        try {\n            Locale defaultLocale = PortalUtil.getSiteDefaultLocale(\n    entry.getGroupId());\n\n            document.addDate(Field.MODIFIED_DATE, entry.getModifiedDate());\n            document.addText(\"entryEmail\", entry.getEmail());\n\n            String localizedTitle = LocalizationUtil.getLocalizedName(\n    Field.TITLE, defaultLocale.toString());\n            String localizedContent = LocalizationUtil.getLocalizedName(\n    Field.CONTENT, defaultLocale.toString());\n\n            document.addText(localizedTitle, entry.getName());\n            document.addText(localizedContent, entry.getMessage());\n\n            long guestbookId = entry.getGuestbookId();\n\n            Guestbook guestbook = _guestbookLocalService.getGuestbook(\n    guestbookId);\n\n            String guestbookName = guestbook.getName();\n\n            String localizedGbName = LocalizationUtil.getLocalizedName(\n    Field.NAME, defaultLocale.toString());\n\n            document.addText(localizedGbName, guestbookName);\n        } catch (PortalException pe) {\n            if (_log.isWarnEnabled()) {\n                _log.warn(\"Unable to index entry \" + entry.getEntryId(), pe);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    private static final Log _log = LogFactoryUtil.getLog(\n    GuestbookEntryModelDocumentContributor.class);\n\n    @Reference\n    private GuestbookLocalService _guestbookLocalService;\n\n}\n\\end{verbatim}\n\nAs with Guestbooks, add the localized values for fields that might be\ntranslated. The Site locale is appended to the field (e.g.,\n\\texttt{title\\_en\\_US}), so the field gets passed to the search engine\nand goes through the right analysis and\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/analysis-tokenizers.html}{tokenization}.\n\nUse Ctrl-Shift-O to add the following imports and save the file:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Document}\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Field}\n\\end{itemize}\n\n\\section{\\texorpdfstring{Implementing\n\\texttt{ModelIndexerWriterContributor}}{Implementing ModelIndexerWriterContributor}}\\label{implementing-modelindexerwritercontributor-1}\n\nCreate \\texttt{GuestbookEntryModelIndexerWriterContributor} and populate\nit with this:\n\n\\begin{verbatim}\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelIndexerWriterContributor.class\n)\npublic class GuestbookEntryModelIndexerWriterContributor\n    implements ModelIndexerWriterContributor<GuestbookEntry> {\n\n    @Override\n    public void customize(\n        BatchIndexingActionable batchIndexingActionable,\n        ModelIndexerWriterDocumentHelper modelIndexerWriterDocumentHelper) {\n\n        batchIndexingActionable.setPerformActionMethod((GuestbookEntry entry) -> {\n            Document document = modelIndexerWriterDocumentHelper.getDocument(\n    entry);\n\n            batchIndexingActionable.addDocuments(document);\n            \n        });\n    }\n\n    @Override\n    public BatchIndexingActionable getBatchIndexingActionable() {\n        return dynamicQueryBatchIndexingActionableFactory.getBatchIndexingActionable(\n    guestbookEntryLocalService.getIndexableActionableDynamicQuery());\n    }\n\n    @Override\n    public long getCompanyId(GuestbookEntry entry) {\n        return entry.getCompanyId();\n    }\n\n    @Reference\n    protected DynamicQueryBatchIndexingActionableFactory\n    dynamicQueryBatchIndexingActionableFactory;\n\n    @Reference\n    protected GuestbookEntryLocalService guestbookEntryLocalService;\n\n}\n\\end{verbatim}\n\nThe interesting work is done in the \\texttt{customize} method, where all\nentries are retrieved and added to a collection.\n\nUse Ctrl-Shift-O to add an import for\n\\texttt{com.liferay.portal.kernel.search.Document} and save the file.\n\n\\section{\\texorpdfstring{Implementing\n\\texttt{GuestbookEntryBatchReindexer}}{Implementing GuestbookEntryBatchReindexer}}\\label{implementing-guestbookentrybatchreindexer}\n\nCreate a new interface, \\texttt{GuestbookEntryBatchReindexer}, with one\nmethod called \\texttt{reindex}:\n\n\\begin{verbatim}\npackage com.liferay.docs.guestbook.search;\n\npublic interface GuestbookEntryBatchReindexer {\n\n    public void reindex(long guestbookId, long companyId);\n\n}\n\\end{verbatim}\n\nThen create the implementation class,\n\\texttt{GuestbookEntryBatchReindexerImpl}:\n\n\\begin{verbatim}\n@Component(immediate = true, service = GuestbookEntryBatchReindexer.class)\npublic class GuestbookEntryBatchReindexerImpl implements GuestbookEntryBatchReindexer {\n\n    @Override\n    public void reindex(long guestbookId, long companyId) {\n        BatchIndexingActionable batchIndexingActionable =\n    indexerWriter.getBatchIndexingActionable();\n\n        batchIndexingActionable.setAddCriteriaMethod(dynamicQuery -> {\n            Property guestbookIdPropery = PropertyFactoryUtil.forName(\n    \"guestbookId\");\n\n            dynamicQuery.add(guestbookIdPropery.eq(guestbookId));\n        });\n\n        batchIndexingActionable.setCompanyId(companyId);\n\n        batchIndexingActionable.setPerformActionMethod((GuestbookEntry entry) -> {\n            Document document = indexerDocumentBuilder.getDocument(entry);\n\n            batchIndexingActionable.addDocuments(document);\n        });\n\n        batchIndexingActionable.performActions();\n\n    }\n\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n    protected IndexerDocumentBuilder indexerDocumentBuilder;\n\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n    protected IndexerWriter<GuestbookEntry> indexerWriter;\n\n}\n\\end{verbatim}\n\nThe \\texttt{reindex} method of the interface is called when a Guestbook\nis updated. The entry documents are re-indexed to include the update\nGuestbook title.\n\nUse Ctrl-Shift-O to add the following imports, and save the file:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Document}\n\\item\n  \\texttt{com.liferay.portal.kernel.dao.orm.Property}\n\\end{itemize}\n\nYou should notice that errors in the project go away at this point.\n\nOnce the re-indexing behavior is in place, you can move on to the code\nfor controlling how \\texttt{GuestbookEntry} documents are queried from\nthe search engine.\n\n\\chapter{Querying for Guestbook Entry\nDocuments}\\label{querying-for-guestbook-entry-documents}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Search and Indexing for Entries</p><p>Step 3 of 5</p>\n\\end{verbatim}\n\nThe code is in place for indexing Guestbook entries to the search\nengine. Next code the behavior necessary for querying the indexed\ndocuments.\n\nImplement two classes:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\texttt{GuestbookEntryKeywordQueryContributor} contributes clauses to\n  the ongoing search query.\n\\item\n  \\texttt{GuestbookEntryModelPreFilterContributor} controls how search\n  results are filtered before they're returned from the search engine.\n\\end{enumerate}\n\n\\section{\\texorpdfstring{Implementing\n\\texttt{KeywordQueryContributor}}{Implementing KeywordQueryContributor}}\\label{implementing-keywordquerycontributor-1}\n\nCreate \\texttt{GuestbookEntryKeywordQueryContributor} and populate it\nwith this:\n\n\\begin{verbatim}\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = KeywordQueryContributor.class\n)\npublic class GuestbookEntryKeywordQueryContributor implements KeywordQueryContributor {\n\n    @Override\n    public void contribute(\n        String keywords, BooleanQuery booleanQuery,\n        KeywordQueryContributorHelper keywordQueryContributorHelper) {\n\n        SearchContext searchContext =\n    keywordQueryContributorHelper.getSearchContext();\n\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.TITLE, false);\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.CONTENT, false);\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, \"entryEmail\", false);\n    }\n\n    @Reference\n    protected QueryHelper queryHelper;\n\n}\n\\end{verbatim}\n\nAdding the localized search terms is important. For all localized\n\\texttt{GuestbookEntry} fields in the index, retrieve the localized\nvalue from the search engine.\n\nUse Ctrl-Shift-O to add these imports, and then save the file:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.liferay.portal.kernel.search.BooleanQuery}\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Field}\n\\end{itemize}\n\nNow that the query code is in place, you can define how returned\n\\texttt{GuestbookEntry} documents are summarized.\n\n\\chapter{Generating Results\nSummaries}\\label{generating-results-summaries-1}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Search and Indexing for Entries</p><p>Step 3 of 5</p>\n\\end{verbatim}\n\nThe Search application and the Asset Publisher application display\nresults retrieved from the search engine. You can control the display by\nimplementing a \\texttt{ModelSummaryContributor}.\n\nCreate a \\texttt{GuestbookEntryModelSummaryContributor}:\n\n\\begin{verbatim}\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelSummaryContributor.class\n)\npublic class GuestbookEntryModelSummaryContributor implements ModelSummaryContributor {\n\n    @Override\n    public Summary getSummary(\n        Document document, Locale locale, String snippet) {\n\n        Summary summary = createSummary(document);\n\n        summary.setMaxContentLength(128);\n\n        return summary;\n    }\n\n    private Summary createSummary(Document document) {\n        String prefix = Field.SNIPPET + StringPool.UNDERLINE;\n\n        String title = document.get(prefix + Field.TITLE, Field.CONTENT);\n        String content = document.get(prefix + Field.CONTENT, Field.CONTENT);\n\n        return new Summary(title, content);\n    }\n\n}\n\\end{verbatim}\n\nFirst override \\texttt{getSummary}, and set the maximum summary length\non the summary returned. The value \\texttt{200} is a Liferay standard.\nControl the summary creation in a utility method called\n\\texttt{createSummary}. Guestbooks only included the title in the\nsummary, but Entries use the title and the content (the Entry message\nfield) to populate the summary.\n\nCreate summaries by combining key parts of the entity's data.\n\nUse Ctrl-Shift-O to add these imports, and then save the file:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Field}\n\\item\n  \\texttt{com.liferay.petra.string.StringPool}\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Summary}\n\\item\n  \\texttt{com.liferay.portal.kernel.search.Document}\n\\end{itemize}\n\nNow that the search and indexing logic is in place, you can update the\nservice layer so \\texttt{add}, \\texttt{update}, and \\texttt{delete}\nservice calls trigger the new logic.\n\n\\chapter{Handling Indexing in the Entry Service\nLayer}\\label{handling-indexing-in-the-entry-service-layer}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Search and Indexing for Entries</p><p>Step 5 of 5</p>\n\\end{verbatim}\n\nWhenever a Guestbook entry is added, updated, or deleted, the\ncorresponding document should also be updated or deleted. A minor update\nto each of the \\texttt{addEntry}, \\texttt{updateEntry}, and\n\\texttt{deleteEntry} service methods for Entries is all it takes.\n\nFollow these steps to update the methods:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{GuestbookEntryLocalServiceImpl} in the\n  \\texttt{guestbook-service} module's\n  \\texttt{com.liferay.docs.guestbook.service.impl} package, and add the\n  annotation \\texttt{@Indexable(type\\ =\\ IndexableType.REINDEX)} above\n  the signature for the \\texttt{addGuestbookEntry} and\n  \\texttt{updateGuestbookEntry} methods:\n\n\\begin{verbatim}\n@Indexable(type = IndexableType.REINDEX)\npublic GuestbookEntry addGuestbookEntry(...)\n\n@Indexable(type = IndexableType.REINDEX)\npublic GuestbookEntry updateGuestbookEntry(...)\n\\end{verbatim}\n\n  The \\texttt{@Indexable} annotation indicates that an index update is\n  required following method execution. The indexing classes control\n  exactly how the indexing happens. Setting the \\texttt{@Indexable}\n  annotation's type to \\texttt{IndexableType.REINDEX} updates the\n  indexed document that corresponds to the updated\n  \\texttt{GuestbookEntry}.\n\\item\n  Add the \\texttt{@Indexable(type\\ =\\ IndexableType.DELETE)} annotation\n  above the signature for the \\texttt{deleteEntry} method. The indexable\n  type \\texttt{IndexableType.DELETE} ensures that the\n  \\texttt{GuestbookEntry} is deleted from the index:\n\n\\begin{verbatim}\n@Indexable(type = IndexableType.DELETE)\npublic GuestbookEntry deleteGuestbookEntry(...)\n\\end{verbatim}\n\\item\n  Use Ctrl-Shift-O to add the required imports:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\n\\end{verbatim}\n\n  Save the file.\n\\item\n  In the Gradle Tasks pane on the right-hand side of Liferay Dev Studio\n  DXP, double-click \\texttt{buildService} in \\texttt{guestbook-service}\n  → \\texttt{build}. This re-runs Service Builder to incorporate your\n  changes to \\texttt{GuestbookEntryLocalServiceImpl}.\n\\end{enumerate}\n\nGuestbooks and their entries now have search and indexing support in the\nback-end. Next, you'll enable search in the Guestbook portlet's\nfront-end.\n\n\\chapter{Updating Your User Interface For\nSearch}\\label{updating-your-user-interface-for-search}\n\nUpdating the Guestbook portlet's user interface for search takes two\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Update the Guestbook portlet's default view JSP to display a search\n  bar for submitting queries.\n\\item\n  Create a new JSP for the Guestbook portlet to display search results.\n\\end{enumerate}\n\nYou'll start by updating the Guestbook portlet's view JSP.\n\nLet's Go!{}\n\n\\chapter{Adding a Search Bar to the Guestbook\nPortlet}\\label{adding-a-search-bar-to-the-guestbook-portlet}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating Your UI for Search</p><p>Step 1 of 2</p>\n\\end{verbatim}\n\nCreate the search bar UI for the Guestbook portlet:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In \\texttt{guestbook-web}, open the file\n  \\texttt{src/main/resources/META-INF/resources/guestbook/view.jsp}. Add\n  a render URL near the top of the file, just after the scriptlet that\n  gets the \\texttt{guestbookId} from the request:\n\n\\begin{verbatim}\n<portlet:renderURL var=\"searchURL\">\n    <portlet:param name=\"mvcPath\" \n    value=\"/guestbook/view_search.jsp\" />\n</portlet:renderURL>\n\\end{verbatim}\n\n  The render URL points to \\texttt{/guestbook/view\\_search.jsp} (created\n  in the next step). You construct the URL first to specify what happens\n  when the user submits a search query.\n\\item\n  Right after the render URL, create an AUI form that adds an input\n  field for search keywords and a \\emph{Submit} button that executes the\n  form action, which is mapped to the \\texttt{searchURL}.\n\n\\begin{verbatim}\n<aui:form action=\"${searchURL}\" name=\"fm\">\n\n    <div class=\"row\">\n        <div class=\"col-md-8\">\n            <aui:input inlineLabel=\"left\" label=\"\" name=\"keywords\" placeholder=\"search-entries\" size=\"256\" />\n        </div>\n\n        <div class=\"col-md-4\">\n            <aui:button type=\"submit\" value=\"search\" />\n        </div>\n    </div>\n\n </aui:form>\n\\end{verbatim}\n\\end{enumerate}\n\nThe body of the search form consists of a\n\\texttt{\\textless{}div\\textgreater{}} with one row containing two\nfields: an input field, named \\texttt{keywords} and a \\emph{Submit}\nbutton. Its \\texttt{name=\"keywords\"} attribute specifies the name of the\nURL parameter that contains the search query. The\n\\texttt{\\textless{}aui:button\\textgreater{}} tag defines the search\nbutton. The \\texttt{type=\"submit\"} attribute specifies that when the\nbutton is clicked (or the \\emph{Enter} key is pressed), the AUI form is\nsubmitted. The \\texttt{value=\"search\"} attribute specifies the name that\nappears on the button.\n\nThat's all there is to the search form! When the form is submitted, the\n\\texttt{mvcPath} parameter pointing to the \\texttt{view\\_search.jsp} is\nincluded in the URL along with the \\texttt{keywords} parameter\ncontaining the search query. Next you'll create the\n\\texttt{view\\_search.jsp} file to display the search results.\n\n\\chapter{Creating a Search Results JSP for the Guestbook\nPortlet}\\label{creating-a-search-results-jsp-for-the-guestbook-portlet}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating Your UI for Search</p><p>Step 2 of 2</p>\n\\end{verbatim}\n\nThere are several design goals to implement in the search results JSP:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Use a search container to display guestbook entries matching a search\n  query.\n\\item\n  Make the Actions button available for each guestbook entry in the\n  results, like it is in the main view's search container.\n\\item\n  Include the search bar so that users can edit and resubmit their\n  queries without having to click the back link to go to the portlet's\n  default view.\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-portlet-search-results.png}\n\\caption{The search results should appear in a search container, and the\nActions button should appear for each entry. The search bar should also\nbe displayed.}\n\\end{figure}\n\nFollow these steps to create the search results JSP:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new file called \\texttt{view\\_search.jsp} in your\n  \\texttt{guestbook-web} module's\n  \\texttt{src/main/resources/META-INF/resources/guestbook} folder. In\n  this file, include the \\texttt{init.jsp}:\n\n\\begin{verbatim}\n<%@include file=\"../init.jsp\"%>\n\\end{verbatim}\n\\item\n  Extract the \\texttt{keywords} and \\texttt{guestbookId} parameters from\n  the request. The \\texttt{keywords} parameter contains the search\n  query, and the \\texttt{guestbookId} parameter contains the ID of the\n  guestbook being searched:\n\n\\begin{verbatim}\n<%\n  String keywords = ParamUtil.getString(request, \"keywords\");\n  long guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n%>\n\\end{verbatim}\n\\item\n  Define the \\texttt{searchURL} and \\texttt{viewURL} as\n  \\texttt{renderURL}s. Both use the \\texttt{mvcPath} parameter that's\n  available to Liferay MVC Portlets:\n\n\\begin{verbatim}\n<portlet:renderURL var=\"searchURL\">\n        <portlet:param name=\"mvcPath\" \n        value=\"/guestbook/view_search.jsp\" />\n</portlet:renderURL>\n\n<portlet:renderURL var=\"viewURL\">\n    <portlet:param \n        name=\"mvcPath\" \n        value=\"/guestbook/view.jsp\" \n    />\n</portlet:renderURL>\n\\end{verbatim}\n\n  The \\texttt{searchURL} points to the current JSP:\n  \\texttt{view\\_search.jsp}. The \\texttt{viewURL} points back to the\n  Guestbook portlet's main view. These URLs are used in the AUI form\n  that you'll create next.\n\\item\n  Add this AUI form:\n\n\\begin{verbatim}\n<aui:form action=\"${searchURL}\" name=\"fm\">\n\n    <liferay-ui:header backURL=\"${viewURL}\" title=\"back\" />\n\n    <div class=\"row\">\n        <div class=\"col-md-8\">\n            <aui:input inlineLabel=\"left\" label=\"\" name=\"keywords\" placeholder=\"search-entries\" size=\"256\" />\n        </div>\n\n        <div class=\"col-md-4\">\n            <aui:button type=\"submit\" value=\"search\" />\n        </div>\n    </div>\n</aui:form>\n\\end{verbatim}\n\n  This form is identical to the one that you added to the Guestbook\n  portlet's \\texttt{view.jsp}, except that this one contains a\n  \\texttt{\\textless{}liferay-ui:header\\textgreater{}} tag that displays\n  the Back icon next to the word \\emph{Back}. The \\texttt{backURL}\n  attribute in the header uses the \\texttt{viewURL} defined above.\n  Submitting the form invokes the \\texttt{searchURL} with the user's\n  search query added to the URL in the \\texttt{keywords} parameter.\n\\item\n  Start a scriptlet to get a search context and set some attributes in\n  it:\n\n\\begin{verbatim}\n<%\n    SearchContext searchContext = SearchContextFactory.getInstance(request);\n\n    searchContext.setKeywords(keywords);\n    searchContext.setAttribute(\"paginationType\", \"more\");\n    searchContext.setStart(0);\n    searchContext.setEnd(10);\n\\end{verbatim}\n\n  To execute a search, you need a \\texttt{SearchContext} object.\n  \\texttt{SearchContextFactory} creates a \\texttt{SearchContext} from\n  the request object. Add the user's search query to the\n  \\texttt{SearchContext} by passing the \\texttt{keywords} URL parameter\n  to the \\texttt{setKeywords} method. Then specify details about\n  pagination and how the search results should be displayed.\n\\item\n  Still in the scriptlet, obtain an \\texttt{Indexer} to run a search.\n  Retrieve the entry indexer from the map in Liferay DXP's indexer\n  registry by passing in the indexer's class or class name:\n\n\\begin{verbatim}\nIndexer<GuestbookEntry> indexer = IndexerRegistryUtil.getIndexer(GuestbookEntry.class);\n\\end{verbatim}\n\\item\n  In the same scriptlet, use the indexer and the search context to run a\n  search:\n\n\\begin{verbatim}\nHits hits = indexer.search(searchContext);\n\nList<GuestbookEntry> entries = new ArrayList<GuestbookEntry>();\n\nfor (int i = 0; i < hits.getDocs().length; i++) {\n        Document doc = hits.doc(i);\n\n        long entryId = GetterUtil\n        .getLong(doc.get(Field.ENTRY_CLASS_PK));\n\n        GuestbookEntry entry = null;\n\n        try {\n                entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n        } catch (PortalException pe) {\n                _log.error(pe.getLocalizedMessage());\n        } catch (SystemException se) {\n                _log.error(se.getLocalizedMessage());\n        }\n\n        entries.add(entry);\n}\n\\end{verbatim}\n\n  The search results return as \\texttt{Hits} objects containing pointers\n  to documents that correspond to guestbook entries. You then loop\n  through the hit documents, retrieving the corresponding guestbook\n  entries and adding them to a list.\n\\item\n  Finish the scriptlet by retrieving a list of all the guestbooks that\n  exist in the current site. Create a map between the guestbook IDs and\n  the guestbook names.\n\n\\begin{verbatim}\n    List<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n    Map<String, String> guestbookMap = new HashMap<String, String>();\n\n    for (Guestbook guestbook : guestbooks) {\n            guestbookMap.put(Long.toString(guestbook.getGuestbookId()), guestbook.getName());\n    }\n%>\n\\end{verbatim}\n\n  Making this single service call and creating a map is more efficient\n  than making separate service calls for each guestbook.\n\\item\n  Display the search results in a search container:\n\n\\begin{verbatim}\n<liferay-ui:search-container delta=\"10\" \n    emptyResultsMessage=\"no-entries-were-found\" \n    total=\"<%= entries.size() %>\">\n        <liferay-ui:search-container-results\n                results=\"<%= entries %>\"\n/>\n\\end{verbatim}\n\n  This specifies three attributes for the\n  \\texttt{\\textless{}liferay-ui:search-container\\textgreater{}} tag:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{delta=\"10\"}: specifies that at most, 10 entries can appear\n    per page.\n  \\item\n    \\texttt{emptyResultsMessage}: specifies the message indicating there\n    are no results.\n  \\item\n    \\texttt{total}: specifies the number of search results.\n  \\end{itemize}\n\n  The \\texttt{results} attribute of the tag\n  \\texttt{\\textless{}liferay-ui:search-container-results\\textgreater{}}\n  specifies the search results. This is easy since you stored the\n  entries resulting from the search in the \\texttt{entries} list.\n\\item\n  Use the\n  \\texttt{\\textless{}liferay-ui:search-container-row\\textgreater{}} tag\n  to set the name of the class whose properties are displayed in each\n  row:\n\n\\begin{verbatim}\n<liferay-ui:search-container-row\n        className=\"com.liferay.docs.guestbook.model.GuestbookEntry\"\n        keyProperty=\"entryId\" modelVar=\"entry\" escapedModel=\"<%=true%>\">\n\\end{verbatim}\n\n  This uses the \\texttt{className} attribute for the class name and\n  specifies the entity's primary key attribute in the\n  \\texttt{keyProperty} attribute. The \\texttt{modelVar} property\n  specifies the name of the \\texttt{Entry} variable that's available to\n  each search container row. To ensure that each field of the\n  \\texttt{entry} variable is escaped (sanitized), the\n  \\texttt{escapedModel} is \\texttt{true}. This prevents potential hacks\n  that could occur if users submitted malicious code into the Add\n  Guestbook form, for example.\n\\item\n  Inside the\n  \\texttt{\\textless{}liferay-ui:search-container-row\\textgreater{}} tag,\n  specify the four columns to display: the guestbook entry's guestbook\n  name, message, entry name, and the actions JSP. The guestbook name is\n  retrieved from the map created in the scriptlet:\n\n\\begin{verbatim}\n    <liferay-ui:search-container-column-text name=\"guestbook\"\n        value=\"<%=guestbookMap.get(Long.toString(entry.getGuestbookId()))%>\" />\n\n    <liferay-ui:search-container-column-text property=\"message\" />\n\n    <liferay-ui:search-container-column-text property=\"name\" />\n\n    <liferay-ui:search-container-column-jsp\n        path=\"/guestbook/entry_actions.jsp\"\n        align=\"right\" />\n</liferay-ui:search-container-row>\n\\end{verbatim}\n\\item\n  Use the \\texttt{\\textless{}liferay-ui:search-iterator\\textgreater{}}\n  tag to iterate through the search results and handle pagination. Close\n  the search container tag:\n\n\\begin{verbatim}\n        <liferay-ui:search-iterator />\n</liferay-ui:search-container>\n\\end{verbatim}\n\\item\n  At the bottom of \\texttt{view\\_search.jsp}, declare a \\texttt{Log}\n  object. You used this log in the \\texttt{catch} clauses of the\n  \\texttt{try} clause that calls the\n  \\texttt{GuestbookEntryLocalServiceUtil.getGuestbookEntry} method to\n  retrieve the guestbook entries. If this service call throws an\n  exception, it's best to log the error so a server administrator can\n  determine what went wrong. Liferay DXP's convention is to declare\n  custom logs for individual classes or JSPs at the bottom of the file:\n\n\\begin{verbatim}\n<%!\n    private static Log _log = LogFactoryUtil.getLog(\"html.guestbook.view_search_jsp\");\n%>\n\\end{verbatim}\n\\item\n  Finally, your \\texttt{view\\_search.jsp} requires some extra imports.\n  Add the following imports to \\texttt{init.jsp}:\n\n\\begin{verbatim}\n<%@ page import=\"com.liferay.portal.kernel.dao.search.SearchContainer\" %>\n<%@ page import=\"com.liferay.portal.kernel.exception.PortalException\" %>\n<%@ page import=\"com.liferay.portal.kernel.exception.SystemException\" %>\n<%@ page import=\"com.liferay.portal.kernel.language.LanguageUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.log.Log\" %>\n<%@ page import=\"com.liferay.portal.kernel.log.LogFactoryUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Indexer\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.IndexerRegistryUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.SearchContext\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.SearchContextFactory\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Hits\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Document\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Field\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.GetterUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.Validator\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.PortalUtil\" %>\n\n<%@ page import=\"java.util.ArrayList\" %>\n<%@ page import=\"java.util.Map\" %>\n<%@ page import=\"java.util.HashMap\" %>\n\n<%@ page import=\"javax.portlet.PortletURL\" %>\n\\end{verbatim}\n\\end{enumerate}\n\nGood work! The Guestbook portlet now supports search! Now your users can\nfind those Guestbook Entries they were looking for.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-portlet-search-conclusion.png}\n\\caption{The Guestbook Application now supports searching for indexed\nGuestbook Entries.}\n\\end{figure}\n\nAs before, remove the \\texttt{hypersonic} folder from the \\texttt{data}\nfolder of your Liferay bundle, and remove the modules from your server\nin Dev Studio DXP. Start your server and redeploy all three modules, add\nsome Guestbook entries, and try searching for them.\n\nOnce indexing is in place, the asset framework can be added to the\nGuestbook application. It provides functionality that's shared across\ndifferent types of content like blog posts, message board posts, wiki\narticles, and more. This is the heart of integration with Liferay DXP's\ndevelopment platform.\n\n\\chapter{Assets: Integrating with Liferay's\nFramework}\\label{assets-integrating-with-liferays-framework}\n\nThe asset framework transforms entities into a common format that can be\npublished anywhere in your Site. Web content articles, blog posts, wiki\narticles, and documents are some asset-enabled entities that come\nout-of-the-box. By asset-enabling your own applications, you can take\nadvantage of Liferay DXP's functionality for publishing your\napplication's data across your Site in the form of asset publisher\nentries, notifications, social activities, and more.\n\nThe asset framework includes these features:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Tags and categories\n\\item\n  Comments and ratings\n\\item\n  Related assets (a.k.a. Asset links)\n\\item\n  Faceted search\n\\item\n  Integration with the Asset Publisher portlet\n\\item\n  Integration with the Search portlet\n\\item\n  Integration with the Tags Navigation, Tag Cloud, and Categories\n  Navigation portlets\n\\end{itemize}\n\nNow you'll asset-enable the guestbook and guestbook entry entities.\nYou'll implement tags, categories, and related assets for guestbooks and\nguestbook entries. You'll implement comments and ratings in guestbook\nentries. You'll also learn how asset-enabled guestbooks and guestbook\nentries integrate with core portlets like the Asset Publisher, Tags\nNavigation, Tag Cloud, and Categories Navigation portlets.\n\nAs always,\n\\href{https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook}{source\ncode} is provided in case you get stuck.\n\nReady to start?\n\nLet's Go!{}\n\n\\chapter{Enabling Assets at the Service\nLayer}\\label{enabling-assets-at-the-service-layer}\n\n\\begin{verbatim}\n<p>Enabling Assets at the Service Layer</p><p>Step 1 of 3</p>\n\\end{verbatim}\n\nEach row in the \\texttt{AssetEntry} table represents an asset. It has an\n\\texttt{entryId} primary key along with \\texttt{classNameId} and\n\\texttt{classPK} foreign keys. The \\texttt{classNameId} specifies the\nasset's type. For example, an asset with a \\texttt{classNameId} of\n\\texttt{JournalArticle} means that the asset represents a web content\narticle (\\texttt{JournalArticle} is the back-end name for a web content\narticle). An asset's \\texttt{classPK} is the primary key of the entity\nrepresented by the asset.\n\nFollow these steps to make asset services available to your entities'\nservice layers:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\texttt{guestbook-service} module's \\texttt{service.xml} file,\n  add the following references directly above the closing\n  \\texttt{\\textless{}/entity\\textgreater{}} tags for \\texttt{Guestbook}\n  and \\texttt{GuestbookEntry}:\n\n\\begin{verbatim}\n<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetEntry\" />\n<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetLink\" />\n\\end{verbatim}\n\n  As mentioned above, you must use the \\texttt{AssetEntry} service so\n  your application can add asset entries corresponding to guestbooks and\n  guestbook entries. You also use the \\texttt{AssetLink} service to\n  support related assets. \\emph{Asset links} are Liferay DXP's back-end\n  term for related assets.\n\\item\n  You must add finders---two for \\texttt{Guestbook}s and two for\n  \\texttt{GuestbookEntry}s---so your assets show in Asset Publisher,\n  because it searches for entities by \\texttt{status} (i.e., is it\n  Workflow-approved?) and by \\texttt{groupId} (i.e., is it in this\n  Site?). Add these below the existing finders for the\n  \\texttt{Guestbook} and \\texttt{GuestbookEntry} entities:\n\n\\begin{verbatim}\n<finder name=\"Status\" return-type=\"Collection\">\n    <finder-column name=\"status\" />\n</finder>\n\n<finder name=\"G_S\" return-type=\"Collection\">\n    <finder-column name=\"groupId\" />\n    <finder-column name=\"status\" />\n</finder>\n\\end{verbatim}\n\\item\n  Run the \\texttt{buildService} Gradle task. This task injects the\n  objects referenced above into your services for use.\n\\item\n  Right-click \\texttt{build.gradle} and select \\emph{Gradle} →\n  \\emph{Refresh Gradle Project}.\n\\end{enumerate}\n\nGreat! Next, you'll handle assets in your service layer.\n\n\\chapter{Handling Assets for the Guestbook\nService}\\label{handling-assets-for-the-guestbook-service}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Assets at the Service Layer</p><p>Step 2 of 3</p>\n\\end{verbatim}\n\nBefore you can update the Service Layer to add the Asset Renderers, you\nmust update your \\texttt{build.gradle} to provide the\n\\texttt{portlet-api} and \\texttt{javax.servlet-api} libraries that the\nasset link service needs to function.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\texttt{build.gradle} file in your \\texttt{guestbook-service}\n  module.\n\\item\n  Add the following two lines in the \\texttt{dependencies} section:\n\n\\begin{verbatim}\ncompileOnly group: \"javax.portlet\", name: \"portlet-api\"\ncompileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n\\end{verbatim}\n\\item\n  Save your \\texttt{build.gradle} file, which refreshes your project.\n\\end{enumerate}\n\nNow you'll update the guestbook service layer to use assets. You must\nupdate the \\texttt{add}, \\texttt{update}, and \\texttt{delete} methods of\nyour project's \\texttt{GuestbookLocalServiceImpl}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your project's \\texttt{GuestbookLocalServiceImpl} class and find\n  the \\texttt{addGuestbook} method. Add the call to add the asset\n  entries below the call that adds resources:\n\n\\begin{verbatim}\nAssetEntry assetEntry = assetEntryLocalService.updateEntry(userId,\n                  groupId, guestbook.getCreateDate(),\n                  guestbook.getModifiedDate(), Guestbook.class.getName(),\n                  guestbookId, guestbook.getUuid(), 0,\n                  serviceContext.getAssetCategoryIds(),\n                  serviceContext.getAssetTagNames(), true, true, null, null, null, null,\n                  ContentTypes.TEXT_HTML, guestbook.getName(), null, null, null,\n                  null, 0, 0, null);\n\nassetLinkLocalService.updateLinks(userId, assetEntry.getEntryId(),\n                  serviceContext.getAssetLinkEntryIds(),\n                  AssetLinkConstants.TYPE_RELATED);\n\\end{verbatim}\n\n  Calling \\texttt{assetEntryLocalService.updateEntry} adds a new row\n  (corresponding to the guestbook that's being added) to the\n  \\texttt{AssetEntry} table in Liferay DXP's database.\n  \\texttt{AssetEntryLocalServiceImpl}'s \\texttt{updateEntry} method both\n  adds and updates asset entries because it checks to see whether the\n  asset entry already exists in the database and then takes the\n  appropriate action. If you check the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-impl/com/liferay/portlet/asset/service/impl/AssetEntryLocalServiceImpl.html}{Javadoc}\n  for \\texttt{AssetEntryLocalServiceUtil.updateEntry}, you'll see that\n  this method is overloaded. Now, why did you use a version of this\n  method with such a long method signature? Because there's only one\n  version of \\texttt{updateEntry} that takes a \\texttt{title} parameter\n  (to set the asset entry's title). Since you want to set the asset\n  title to \\texttt{guestbook.getName()}, that's the version you use.\n\n  Later, you'll update the Guestbook Admin portlet's form for adding\n  guestbooks to allow the selection of related assets, which are stored\n  in the database's \\texttt{AssetLink} table. The\n  \\texttt{assetLinkLocalService.updateLinks} call adds the appropriate\n  entries to the table so related assets work for your guestbook\n  entities. The \\texttt{updateEntry} method adds and updates asset\n  entries the same way \\texttt{updateLink} adds and updates asset links.\n\\item\n  Next, add the asset calls to \\texttt{GuestbookLocalServiceImpl}'s\n  \\texttt{updateGuestbook} method, directly after the resource call:\n\n\\begin{verbatim}\nAssetEntry assetEntry = assetEntryLocalService.updateEntry(guestbook.getUserId(),\n                  guestbook.getGroupId(), guestbook.getCreateDate(),\n                  guestbook.getModifiedDate(), Guestbook.class.getName(),\n                  guestbookId, guestbook.getUuid(), 0,\n                  serviceContext.getAssetCategoryIds(),\n                  serviceContext.getAssetTagNames(), true, true, guestbook.getCreateDate(), \n                  null, null, null, ContentTypes.TEXT_HTML, guestbook.getName(), null, null, \n                  null, null, 0, 0, serviceContext.getAssetPriority());\n\nassetLinkLocalService.updateLinks(serviceContext.getUserId(),\n                  assetEntry.getEntryId(), serviceContext.getAssetLinkEntryIds(),\n                  AssetLinkConstants.TYPE_RELATED);\n\\end{verbatim}\n\n  Here, \\texttt{assetEntryLocalService.updateEntry} updates an existing\n  asset entry and \\texttt{assetLinkLocalService.updateLinks} adds or\n  updates that entry's asset links (related assets).\n\\item\n  Next, add the asset calls to the \\texttt{deleteGuestbook} method,\n  directly after the resource calls:\n\n\\begin{verbatim}\nAssetEntry assetEntry = assetEntryLocalService.fetchEntry(\n                  Guestbook.class.getName(), guestbookId);\n\nassetLinkLocalService.deleteLinks(assetEntry.getEntryId());\n\nassetEntryLocalService.deleteEntry(assetEntry);\n\\end{verbatim}\n\n  Here, you use the guestbook's class name and ID to retrieve the\n  corresponding asset entry. Then you delete that asset entry's asset\n  links and the asset entry itself.\n\\item\n  Finally, organize your imports, save the file, and run Service Builder\n  to apply the changes.\n\\end{enumerate}\n\nNext, you'll do the same thing for guestbook entries.\n\n\\chapter{Handling Assets for the GuestbookEntry\nService}\\label{handling-assets-for-the-guestbookentry-service}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Enabling Assets at the Service Layer</p><p>Step 3 of 3</p>\n\\end{verbatim}\n\nNow you must update the guestbook entry entity's service methods. In\nthese methods, the calls you'll make to \\texttt{assetEntryLocalService}\nand \\texttt{assetLinkLocalService} are identical to the ones you made in\nthe guestbook entity's service methods, except you're specifying assets\nfor \\texttt{GuestbookEntry} entities.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{GuestbookEntryLocalServiceImpl} and add the asset calls\n  to the \\texttt{addGuestbookEntry} method after the resource calls:\n\n\\begin{verbatim}\nAssetEntry assetEntry = assetEntryLocalService.updateEntry(userId,\n                  groupId, entry.getCreateDate(), entry.getModifiedDate(),\n                  GuestbookEntry.class.getName(), entryId, entry.getUuid(), 0,\n                  serviceContext.getAssetCategoryIds(),\n                  serviceContext.getAssetTagNames(), true, true, null, null, null, null,\n                  ContentTypes.TEXT_HTML, entry.getMessage(), null, null, null,\n                  null, 0, 0, null);\n\nassetLinkLocalService.updateLinks(userId, assetEntry.getEntryId(),\n                  serviceContext.getAssetLinkEntryIds(),\n                  AssetLinkConstants.TYPE_RELATED);\n\\end{verbatim}\n\\item\n  Next, add the asset calls to the \\texttt{updateGuestbookEntry} method\n  after the resource calls:\n\n\\begin{verbatim}\nAssetEntry assetEntry = assetEntryLocalService.updateEntry(userId,\n              serviceContext.getScopeGroupId(),\n              entry.getCreateDate(), entry.getModifiedDate(),\n              GuestbookEntry.class.getName(), entryId, entry.getUuid(),\n              0, serviceContext.getAssetCategoryIds(),\n              serviceContext.getAssetTagNames(), true, true,\n              entry.getCreateDate(), null, null, null,\n              ContentTypes.TEXT_HTML, entry.getMessage(), null,\n              null, null, null, 0, 0,\n              serviceContext.getAssetPriority());\n\nassetLinkLocalService.updateLinks(userId, assetEntry.getEntryId(),\n              serviceContext.getAssetLinkEntryIds(),\n              AssetLinkConstants.TYPE_RELATED);\n\\end{verbatim}\n\\item\n  Add the asset calls to the \\texttt{deleteGuestbookEntry} method after\n  the resource calls:\n\n\\begin{verbatim}\nAssetEntry assetEntry = assetEntryLocalService.fetchEntry(\n                      GuestbookEntry.class.getName(), entry.getEntryId());\n\nassetLinkLocalService.deleteLinks(assetEntry.getEntryId());\n\nassetEntryLocalService.deleteEntry(assetEntry);\n\\end{verbatim}\n\\item\n  Organize your imports, save the file, and run Service Builder.\n\\item\n  Finally, add these language keys to the\n  \\texttt{guestbook-web/src/main/resources/content/Language.properties}\n  file:\n\n\\begin{verbatim}\nmodel.resource.com.liferay.docs.guestbook.model.Guestbook=Guestbook\nmodel.resource.com.liferay.docs.guestbook.model.GuestbookEntry=Guestbook Entry\n\\end{verbatim}\n\\end{enumerate}\n\nExcellent! You've asset-enabled your guestbook and guestbook entry\nentities at the service layer. Your next step is to implement asset\nrenderers for these entities so they can be fully integrated into the\nasset framework. Every asset needs an asset renderer class so the Asset\nPublisher portlet can display it.\n\n\\chapter{Implementing Asset\nRenderers}\\label{implementing-asset-renderers}\n\nAssets are display versions of entities, so they contain fields like\n\\texttt{title}, \\texttt{description}, and \\texttt{summary}. Liferay DXP\nuses these fields to display assets. Asset Renderers translate an entity\ninto an asset via these fields. You must therefore create and register\nAsset Renderer classes for your guestbook and guestbook entry entities.\nWithout these classes, Liferay DXP can't display your entities in Asset\nPublisher, Notifications, Activities, or anywhere else that displays\nassets.\n\nYour next task is to create these Asset Renderers. Ready to begin?\n\nLet's Go!{}\n\n\\chapter{Implementing a Guestbook Asset\nRenderer}\\label{implementing-a-guestbook-asset-renderer}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Implementing Asset Renderers</p><p>Step 1 of 2</p>\n\\end{verbatim}\n\nLiferay DXP's asset renderers follow the factory pattern, so you must\ncreate a \\texttt{GuestbookAssetRendererFactory} that instantiates the\n\\texttt{GuestbookAssetRenderer}'s private guestbook object. Here, you'll\ncreate both classes.\n\nYou'll create the Asset Renderer class first.\n\n\\section{Creating the AssetRenderer\nClass}\\label{creating-the-assetrenderer-class}\n\nFollow these steps to create the \\texttt{GuestbookAssetRenderer} class:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new package called\n  \\texttt{com.liferay.docs.guestbook.web.internal.asset} in the\n  \\texttt{guestbook-web} module's \\texttt{src/main/java} folder. In this\n  package, create a \\texttt{GuestbookAssetRenderer} class that extends\n  Liferay DXP's \\texttt{BaseJSPAssetRenderer} class. Extending this\n  class gives you a head-start on implementing the\n  \\texttt{AssetRenderer} interface:\n\n\\begin{verbatim}\npublic class GuestbookAssetRenderer extends BaseJSPAssetRenderer<Guestbook> {\n\n}\n\\end{verbatim}\n\\item\n  Add the constructor, the guestbook class variable, and the permissions\n  model resource. Most of the methods in this class are getters that\n  return fields from the private \\texttt{\\_guestbook} object. Methods\n  requiring a permission check use\n  \\texttt{\\_guestbookModelResourcePermission}:\n\n\\begin{verbatim}\npublic GuestbookAssetRenderer(Guestbook guestbook, ModelResourcePermission<Guestbook> modelResourcePermission) {\n\n            _guestbook = guestbook;\n            _guestbookModelResourcePermission = modelResourcePermission;\n}\n\n// Add the other methods here\n\nprivate Guestbook _guestbook;\nprivate final ModelResourcePermission<Guestbook> _guestbookModelResourcePermission;   \nprivate Logger logger = Logger.getLogger(this.getClass().getName());\n\\end{verbatim}\n\\item\n  The \\texttt{BaseJSPAssetRenderer} abstract class that you're extending\n  contains dummy implementations of the \\texttt{hasEditPermission} and\n  \\texttt{hasViewPermission} methods that you must override with actual\n  permission checks using the permissions resources that you created\n  earlier. Add these methods below the comment labeled\n  \\texttt{Add\\ the\\ other\\ methods\\ here}:\n\n\\begin{verbatim}\n@Override\npublic boolean hasEditPermission(PermissionChecker permissionChecker) \n{\n    try {\n        return _guestbookModelResourcePermission.contains(\n            permissionChecker, _guestbook, ActionKeys.UPDATE);\n    }\n    catch (Exception e) {\n    }\n\n    return false;\n}\n\n@Override\npublic boolean hasViewPermission(PermissionChecker permissionChecker) \n{\n    try {\n        return _guestbookModelResourcePermission.contains(\n            permissionChecker, _guestbook, ActionKeys.VIEW);\n    }\n    catch (Exception e) {\n    }\n\n    return true;\n}\n\\end{verbatim}\n\\item\n  Add the following getter methods to retrieve information about the\n  guestbook asset:\n\n\\begin{verbatim}\n@Override\npublic Guestbook getAssetObject() {\n  return _guestbook;\n}\n\n@Override\npublic long getGroupId() {\n  return _guestbook.getGroupId();\n}\n\n@Override\npublic long getUserId() {\n\n  return _guestbook.getUserId();\n}\n\n@Override\npublic String getUserName() {\n  return _guestbook.getUserName();\n}\n\n@Override\npublic String getUuid() {\n  return _guestbook.getUuid();\n}\n\n@Override\npublic String getClassName() {\n  return Guestbook.class.getName();\n}\n\n@Override\npublic long getClassPK() {\n  return _guestbook.getGuestbookId();\n}\n\n@Override\npublic String getSummary(PortletRequest portletRequest, PortletResponse \n  portletResponse) {\n    return \"Name: \" + _guestbook.getName();\n}\n\n@Override\npublic String getTitle(Locale locale) {\n  return _guestbook.getName();\n}\n\n@Override\npublic boolean include(HttpServletRequest request, HttpServletResponse \n  response, String template) throws Exception {\n    request.setAttribute(\"GUESTBOOK\", _guestbook);\n    request.setAttribute(\"HtmlUtil\", HtmlUtil.getHtml());\n    request.setAttribute(\"StringUtil\", new StringUtil());\n    return super.include(request, response, template);\n}\n\\end{verbatim}\n\n  The final method makes several utilities and the \\texttt{Guestbook}\n  entity available in the \\texttt{HttpServletRequest} object.\n\\item\n  Override the \\texttt{getJspPath} method. It returns a string\n  representing the path to the JSP that renders the guestbook asset.\n  When the Asset Publisher displays an asset's full content, it invokes\n  the asset renderer class's \\texttt{getJspPath} method and passes a\n  \\texttt{template} string parameter that equals\n  \\texttt{\"full\\_content\"}. This returns\n  \\texttt{/asset/guestbook/full\\_content.jsp} when the\n  \\texttt{full\\_content} template string is passed as a parameter.\n  You'll create this JSP later when updating your application's user\n  interface:\n\n\\begin{verbatim}\n@Override\npublic String getJspPath(HttpServletRequest request, String template) {\n\n    if (template.equals(TEMPLATE_FULL_CONTENT)) {\n      request.setAttribute(\"gb_guestbook\", _guestbook);\n\n      return \"/asset/guestbook/\" + template + \".jsp\";\n    } else {\n      return null;\n    }\n}\n\\end{verbatim}\n\\item\n  Override the \\texttt{getURLEdit} method. This method returns a URL for\n  editing the asset:\n\n  @Override public PortletURL getURLEdit(LiferayPortletRequest\n  liferayPortletRequest, LiferayPortletResponse liferayPortletResponse)\n  throws Exception \\{\n\n\\begin{verbatim}\nPortletURL portletURL = liferayPortletResponse.createLiferayPortletURL(\n    getControlPanelPlid(liferayPortletRequest), GuestbookPortletKeys.GUESTBOOK,\n    PortletRequest.RENDER_PHASE);\nportletURL.setParameter(\"mvcPath\", \"/guestbook/edit_guestbook.jsp\");\nportletURL.setParameter(\"guestbookId\", String.valueOf(_guestbook.getGuestbookId()));\nportletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n\nreturn portletURL;\n\\end{verbatim}\n\n  \\}\n\\item\n  Override the \\texttt{getURLViewInContext} method. This method returns\n  a URL to view the asset in its native application:\n\n\\begin{verbatim}\n@Override\npublic String getURLViewInContext(LiferayPortletRequest liferayPortletRequest,\n  LiferayPortletResponse liferayPortletResponse, String noSuchEntryRedirect) throws Exception {\n    try {\n      long plid = PortalUtil.getPlidFromPortletId(_guestbook.getGroupId(),\n          GuestbookPortletKeys.GUESTBOOK);\n\n      PortletURL portletURL;\n      if (plid == LayoutConstants.DEFAULT_PLID) {\n        portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(liferayPortletRequest),\n            GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n      } else {\n        portletURL = PortletURLFactoryUtil.create(liferayPortletRequest,\n            GuestbookPortletKeys.GUESTBOOK, plid, PortletRequest.RENDER_PHASE);\n      }\n\n      portletURL.setParameter(\"mvcPath\", \"/guestbook/view.jsp\");\n      portletURL.setParameter(\"guestbookId\", String.valueOf(_guestbook.getGuestbookId()));\n\n      String currentUrl = PortalUtil.getCurrentURL(liferayPortletRequest);\n\n      portletURL.setParameter(\"redirect\", currentUrl);\n\n      return portletURL.toString();\n\n    } catch (PortalException e) {\n\n        logger.log(Level.SEVERE, e.getMessage());\n\n    } catch (SystemException e) {\n\n        logger.log(Level.SEVERE, e.getMessage());\n\n    }\n\n    return noSuchEntryRedirect;\n}\n\\end{verbatim}\n\\item\n  Override the \\texttt{getURLView} method. This method returns a URL to\n  view the asset from within the Asset Publisher:\n\n\\begin{verbatim}\n@Override\npublic String getURLView(LiferayPortletResponse liferayPortletResponse, \nWindowState windowState) throws Exception {\n\n    return super.getURLView(liferayPortletResponse, windowState);\n}\n\\end{verbatim}\n\\item\n  Organize imports (Ctrl-Shift-O) and save the file. Choose these\n  imports:\n\\end{enumerate}\n\n\\begin{verbatim}\n- `java.util.logging.Logger` \n- `com.liferay.portal.kernel.exception.SystemException` \n- `java.util.logging.Level` \n- `com.liferay.petra.string.StringUtil`\n\\end{verbatim}\n\nNext you can create the \\texttt{AssetRendererFactory} class.\n\n\\section{Creating the GuestbookAssetRendererFactory\nClass}\\label{creating-the-guestbookassetrendererfactory-class}\n\nFollow these steps to create the \\texttt{GuestbookAssetRendererFactory}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\texttt{com.liferay.docs.guestbook.web.internal.asset} package,\n  create a class called \\texttt{GuestbookAssetRendererFactory} that\n  extends Liferay DXP's \\texttt{BaseAssetRendererFactory} class, and\n  overwrite the generated constructor and class variables with this:\n\n\\begin{verbatim}\n@Component(immediate = true, \n  property = {\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK}, \n  service = AssetRendererFactory.class\n  )\npublic class GuestbookAssetRendererFactory extends \n  BaseAssetRendererFactory<Guestbook> {\n\n  public GuestbookAssetRendererFactory() {\n    setClassName(CLASS_NAME);\n    setLinkable(_LINKABLE);\n    setPortletId(GuestbookPortletKeys.GUESTBOOK); setSearchable(true);\n    setSelectable(true); \n  }         \n\n  // Add the other methods here\n\n  private ServletContext _servletContext;\n  private GuestbookLocalService _guestbookLocalService;\n  private static final boolean _LINKABLE = true;\n  public static final String CLASS_NAME = Guestbook.class.getName();\n  public static final String TYPE = \"guestbook\";\n  private Logger logger = Logger.getLogger(this.getClass().getName());\n  private ModelResourcePermission<Guestbook> _guestbookModelResourcePermission;\n}\n\\end{verbatim}\n\n  This code contains the class declaration, the constructor, and the\n  class variables. It sets the class name it creates an\n  \\texttt{AssetRenderer} for, a portlet ID, and a \\texttt{true} boolean\n  (\\texttt{\\_LINKABLE}). The boolean denotes implemented methods that\n  provide URLs in the generated \\texttt{AssetRenderer}.\n\n  Insert the methods below where you see the comment\n  \\texttt{Add\\ the\\ other\\ methods\\ here}.\n\\item\n  Implement the \\texttt{getAssetRenderer} method, which constructs new\n  \\texttt{GuestbookAssetRenderer} instances for particular guestbooks.\n  It uses the \\texttt{classPK} (primary key) parameter to retrieve the\n  guestbook from the database. It then calls the\n  \\texttt{GuestbookAssetRenderer}'s constructor, passing the retrieved\n  guestbook and permissions resource model as arguments:\n\n\\begin{verbatim}\n@Override\npublic AssetRenderer<Guestbook> getAssetRenderer(long classPK, int type) \nthrows PortalException {\n\n  Guestbook guestbook = _guestbookLocalService.getGuestbook(classPK);\n\n  GuestbookAssetRenderer guestbookAssetRenderer = \n  new GuestbookAssetRenderer(guestbook, _guestbookModelResourcePermission);\n\n  guestbookAssetRenderer.setAssetRendererType(type);\n  guestbookAssetRenderer.setServletContext(_servletContext);\n\n  return guestbookAssetRenderer;\n}\n\\end{verbatim}\n\\item\n  You're extending \\texttt{BaseAssetRendererFactory}, an abstract class\n  that implements the \\texttt{AssetRendererFactory} interface. To ensure\n  that your custom asset is associated with the correct entity, each\n  asset renderer factory must implement the \\texttt{getClassName} and\n  \\texttt{getType} methods (among others):\n\n\\begin{verbatim}\n@Override\npublic String getClassName() {\n    return CLASS_NAME;\n}\n\n@Override\npublic String getType() {\n    return TYPE;\n}\n\\end{verbatim}\n\\item\n  Implement the \\texttt{hasPermission} method via the\n  \\texttt{GuestbookPermission} class:\n\n\\begin{verbatim}\n@Override\npublic boolean hasPermission(PermissionChecker permissionChecker, \nlong classPK, String actionId) throws Exception {\n\n  Guestbook guestbook = _guestbookLocalService.getGuestbook(classPK);\n  long groupId = guestbook.getGroupId();\n  return GuestbookPermission.contains(permissionChecker, groupId, \n  actionId);\n}\n\\end{verbatim}\n\\item\n  Add the remaining code to create the portlet URL for the asset and\n  specify whether it's linkable:\n\n\\begin{verbatim}\n@Override\npublic PortletURL getURLAdd(LiferayPortletRequest liferayPortletRequest,\n  LiferayPortletResponse liferayPortletResponse, long classTypeId) {\n    PortletURL portletURL = null;\n\n    try {\n      ThemeDisplay themeDisplay = (ThemeDisplay) \n      liferayPortletRequest.getAttribute(WebKeys.THEME_DISPLAY);\n\n      portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(themeDisplay),\n          GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n      portletURL.setParameter(\"mvcPath\", \"/guestbook/edit_guestbook.jsp\");\n      portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n\n      } catch (PortalException e) {\n\n            logger.log(Level.SEVERE, e.getMessage()); \n\n      }\n\n    return portletURL;\n}\n\n@Override\npublic boolean isLinkable() {\n    return _LINKABLE;\n}\n\n@Override\npublic String getIconCssClass() {\n  return \"bookmarks\";\n}\n\n@Reference(target = \"(osgi.web.symbolicname=com.liferay.docs.guestbook.portlet)\",\n    unbind = \"-\")\npublic void setServletContext(ServletContext servletContext) {\n    _servletContext = servletContext;\n}\n\n@Reference(unbind = \"-\")\nprotected void setGuestbookLocalService(GuestbookLocalService guestbookLocalService) {\n    _guestbookLocalService = guestbookLocalService; \n}\n\\end{verbatim}\n\\item\n  Organize imports (Ctrl-Shift-O) and save the file. Select these\n  imports:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{java.util.logging.Logger}\n  \\item\n    \\texttt{java.util.logging.Level}\n  \\end{itemize}\n\\end{enumerate}\n\nGreat! The guestbook asset renderer is complete. Next, you'll create the\nentry asset renderer.\n\n\\chapter{Implementing a Guestbook Entry Asset\nRenderer}\\label{implementing-a-guestbook-entry-asset-renderer}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Implementing Asset Renderers</p><p>Step 2 of 2</p>\n\\end{verbatim}\n\nThe classes you'll create here are nearly identical to the\n\\texttt{GuestbookAssetRenderer} and\n\\texttt{GuestbookAssetRendererFactory} classes you created for\nguestbooks in the previous step. This step provides the code needed for\nguestbook entries. Please review the previous sections to learn how this\ncode works.\n\n\\section{Creating the GuestbookEntryAssetRenderer\nClass}\\label{creating-the-guestbookentryassetrenderer-class}\n\nIn the \\texttt{com.liferay.docs.guestbook.web.internal.asset} package,\ncreate a \\texttt{GuestbookEntryAssetRenderer} class that extends Liferay\nDXP's \\texttt{BaseJSPAssetRenderer} class. Replace the contents of your\n\\texttt{GuestbookEntryAssetRenderer} class with the following code:\n\n\\begin{verbatim}\npackage com.liferay.docs.guestbook.web.internal.asset;\n\nimport com.liferay.asset.kernel.model.BaseJSPAssetRenderer;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.LayoutConstants;\nimport com.liferay.portal.kernel.portlet.LiferayPortletRequest;\nimport com.liferay.portal.kernel.portlet.LiferayPortletResponse;\nimport com.liferay.portal.kernel.portlet.PortletURLFactoryUtil;\nimport com.liferay.portal.kernel.security.permission.ActionKeys;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.util.HtmlUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.petra.string.StringUtil;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport java.util.Locale;\nimport javax.portlet.PortletRequest;\nimport javax.portlet.PortletResponse;\nimport javax.portlet.PortletURL;\nimport javax.portlet.WindowState;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class GuestbookEntryAssetRenderer extends BaseJSPAssetRenderer<GuestbookEntry> {\n\n    public GuestbookEntryAssetRenderer(GuestbookEntry entry, ModelResourcePermission<GuestbookEntry> modelResourcePermission) {\n\n        _entry = entry;\n        _guestbookEntryModelResourcePermission = modelResourcePermission;\n    }\n\n    @Override\n    public boolean hasViewPermission(PermissionChecker permissionChecker) \n    {\n        try {\n            return _guestbookEntryModelResourcePermission.contains(\n                    permissionChecker, _entry, ActionKeys.VIEW);\n        }\n        catch (Exception e) {\n        }\n\n        return true;\n    }\n\n    @Override\n    public GuestbookEntry getAssetObject() {\n        return _entry;\n    }\n\n    @Override\n    public long getGroupId() {\n        return _entry.getGroupId();\n    }\n\n    @Override\n    public long getUserId() {\n\n        return _entry.getUserId();\n    }\n\n    @Override\n    public String getUserName() {\n        return _entry.getUserName();\n    }\n\n    @Override\n    public String getUuid() {\n        return _entry.getUuid();\n    }\n\n    @Override\n    public String getClassName() {\n        return GuestbookEntry.class.getName();\n    }\n\n    @Override\n    public long getClassPK() {\n        return _entry.getEntryId();\n    }\n\n    @Override\n    public String getSummary(PortletRequest portletRequest, \n            PortletResponse portletResponse) {\n        return \"Name: \" + _entry.getName() + \". Message: \" + _entry.getMessage();\n    }\n\n    @Override\n    public String getTitle(Locale locale) {\n        return _entry.getMessage();\n    }\n\n    @Override\n    public boolean include(HttpServletRequest request, \n            HttpServletResponse response, String template) throws Exception {\n        request.setAttribute(\"ENTRY\", _entry);\n        request.setAttribute(\"HtmlUtil\", HtmlUtil.getHtml());\n        request.setAttribute(\"StringUtil\", new StringUtil());\n        return super.include(request, response, template);\n    }\n\n    @Override\n    public String getJspPath(HttpServletRequest request, String template) {\n\n        if (template.equals(TEMPLATE_FULL_CONTENT)) {\n            request.setAttribute(\"gb_entry\", _entry);\n\n            return \"/asset/entry/\" + template + \".jsp\";\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public PortletURL getURLEdit(LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse) throws Exception {\n        PortletURL portletURL = liferayPortletResponse.createLiferayPortletURL(\n                getControlPanelPlid(liferayPortletRequest), GuestbookPortletKeys.GUESTBOOK,\n                PortletRequest.RENDER_PHASE);\n        portletURL.setParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n        portletURL.setParameter(\"entryId\", String.valueOf(_entry.getEntryId()));\n        portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n\n        return portletURL;\n    }\n\n    @Override\n    public String getURLViewInContext(LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse, String noSuchEntryRedirect) \n                    throws Exception {\n        try {\n            long plid = PortalUtil.getPlidFromPortletId(_entry.getGroupId(),\n                    GuestbookPortletKeys.GUESTBOOK);\n\n            PortletURL portletURL;\n            if (plid == LayoutConstants.DEFAULT_PLID) {\n                portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(liferayPortletRequest),\n                        GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n            } else {\n                portletURL = PortletURLFactoryUtil.create(liferayPortletRequest,\n                        GuestbookPortletKeys.GUESTBOOK, plid, PortletRequest.RENDER_PHASE);\n            }\n\n            portletURL.setParameter(\"mvcPath\", \"/guestbook/view_entry.jsp\");\n            portletURL.setParameter(\"entryId\", String.valueOf(_entry.getEntryId()));\n\n            String currentUrl = PortalUtil.getCurrentURL(liferayPortletRequest);\n\n            portletURL.setParameter(\"redirect\", currentUrl);\n\n            return portletURL.toString();\n\n        } catch (PortalException e) {\n\n        } catch (SystemException e) {\n        }\n\n        return noSuchEntryRedirect;\n    }\n\n    @Override\n    public String getURLView(LiferayPortletResponse liferayPortletResponse, \n            WindowState windowState) throws Exception {\n\n        return super.getURLView(liferayPortletResponse, windowState);\n    }\n\n    @Override\n    public boolean isPrintable() {\n        return true;\n    }\n    private final ModelResourcePermission<GuestbookEntry> _guestbookEntryModelResourcePermission;\n    private GuestbookEntry _entry;\n}\n\\end{verbatim}\n\nThis class is similar to the \\texttt{GuestbookAssetRenderer} class. For\nthe \\texttt{GuestbookEntryAssetRenderer.getSummary} method, you return a\nsummary that displays the entry name (the name of the user who created\nthe entry) and the entry message.\n\n\\texttt{GuestbookAssetRenderer.getSummary} returns a summary that\ndisplays the guestbook name.\n\\texttt{GuestbookEntryAssetRenderer.getTitle} returns the entry message.\n\\texttt{GuestbookAssetRenderer.getTitle} returns the guestbook name. The\nother methods of \\texttt{GuestbookEntryAssetRenderer} are nearly\nidentical to those of \\texttt{GuestbookAssetRenderer}.\n\n\\section{Creating the GuestbookEntryAssetRendererFactory\nClass}\\label{creating-the-guestbookentryassetrendererfactory-class}\n\nNext, you must create the guestbook entry asset renderer's factory\nclass. In the \\texttt{com.liferay.docs.guestbook.web.internal.asset}\npackage, create a class called\n\\texttt{GuestbookEntryAssetRendererFactory} that extends Liferay DXP's\n\\texttt{BaseAssetRendererFactory} class. Replace its content with the\nfollowing code:\n\n\\begin{verbatim}\npackage com.liferay.docs.guestbook.web.internal.asset;\n\nimport com.liferay.asset.kernel.model.AssetRenderer;\nimport com.liferay.asset.kernel.model.AssetRendererFactory;\nimport com.liferay.asset.kernel.model.BaseAssetRendererFactory;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookEntryPermission;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.LiferayPortletRequest;\nimport com.liferay.portal.kernel.portlet.LiferayPortletResponse;\nimport com.liferay.portal.kernel.portlet.LiferayPortletURL;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.theme.ThemeDisplay;\nimport com.liferay.portal.kernel.util.WebKeys;\n\nimport javax.portlet.PortletRequest;\nimport javax.portlet.PortletURL;\nimport javax.portlet.WindowState;\nimport javax.portlet.WindowStateException;\nimport javax.servlet.ServletContext;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\n@Component(\n        immediate = true,\n        property = {\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK},\n        service = AssetRendererFactory.class\n        )\npublic class GuestbookEntryAssetRendererFactory extends BaseAssetRendererFactory<GuestbookEntry> {\n\n    public GuestbookEntryAssetRendererFactory() {\n        setClassName(CLASS_NAME);\n        setLinkable(_LINKABLE);\n        setPortletId(GuestbookPortletKeys.GUESTBOOK);\n        setSearchable(true);\n        setSelectable(true);\n\n    }\n\n    @Override\n    public AssetRenderer<GuestbookEntry> getAssetRenderer(long classPK, int type)\n            throws PortalException {\n\n        GuestbookEntry entry = _guestbookEntryLocalService.getGuestbookEntry(classPK);\n\n        GuestbookEntryAssetRenderer guestbookEntryAssetRenderer = new GuestbookEntryAssetRenderer(entry, _guestbookEntryModelResourcePermission);\n\n        guestbookEntryAssetRenderer.setAssetRendererType(type);\n        guestbookEntryAssetRenderer.setServletContext(_servletContext);\n\n        return guestbookEntryAssetRenderer;\n    }\n\n    @Override\n    public String getClassName() {\n        return CLASS_NAME;\n    }\n\n    @Override\n    public String getType() {\n        return TYPE;\n    }\n\n    @Override\n    public boolean hasPermission(PermissionChecker permissionChecker,\n            long classPK, String actionId) throws Exception {\n\n        GuestbookEntry entry = _guestbookEntryLocalService.getGuestbookEntry(classPK);\n        return GuestbookEntryPermission.contains(permissionChecker, entry, actionId);\n    }\n\n    @Override\n    public PortletURL getURLAdd(LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse, long classTypeId) {\n\n        PortletURL portletURL = null;\n\n        try {\n            ThemeDisplay themeDisplay = (ThemeDisplay) liferayPortletRequest.getAttribute(WebKeys.THEME_DISPLAY);\n\n            portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(themeDisplay),\n                    GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n            portletURL.setParameter(\"mvcRenderCommandName\", \"/guestbook/edit_entry\");\n            portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n        } catch (PortalException e) {\n        }\n\n        return portletURL;\n    }\n\n    @Override\n    public PortletURL getURLView(LiferayPortletResponse liferayPortletResponse, WindowState windowState) {\n\n        LiferayPortletURL liferayPortletURL\n        = liferayPortletResponse.createLiferayPortletURL(\n                GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n\n        try {\n            liferayPortletURL.setWindowState(windowState);\n        } catch (WindowStateException wse) {\n\n        }\n        return liferayPortletURL;\n    }\n\n    @Override\n    public boolean isLinkable() {\n        return _LINKABLE;\n    }\n\n    @Override\n    public String getIconCssClass() {\n        return \"pencil\";\n    }\n\n    @Reference(target = \"(osgi.web.symbolicname=com.liferay.docs.guestbook.portlet)\",\n            unbind = \"-\")\n    public void setServletContext (ServletContext servletContext) {\n        _servletContext = servletContext;\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setGuestbookEntryLocalService(GuestbookEntryLocalService guestbookEntryLocalService) {\n        _guestbookEntryLocalService = guestbookEntryLocalService;\n    }\n\n\n    private GuestbookEntryLocalService _guestbookEntryLocalService;\n    private ServletContext _servletContext;\n    private static final boolean _LINKABLE = true;\n    public static final String CLASS_NAME = GuestbookEntry.class.getName();\n    public static final String TYPE = \"entry\";\n\n    private ModelResourcePermission<GuestbookEntry>\n    _guestbookEntryModelResourcePermission;\n\n\n}\n\\end{verbatim}\n\nNow your guestbook project's entities are fully asset-enabled. To test\nthe functionality, add the Asset Publisher portlet to a page. Then add\nand edit guestbooks and guestbook entries. Then check the Asset\nPublisher portlet. The Asset Publisher dynamically displays assets of\nany kind from the current Site.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/custom-entities-asset-publisher.png}\n\\caption{After you've implemented and registered your asset renderers\nfor your custom entities, the Asset Publisher can display your\nentities.}\n\\end{figure}\n\nConfirm that the Asset Publisher displays the guestbooks and guestbook\nentries that you added.\n\nGreat! Next, you'll update your portlets' user interfaces to use several\nasset framework features: comments, ratings, tags, categories, and\nrelated assets.\n\n\\chapter{Adding Asset Features to Your User\nInterface}\\label{adding-asset-features-to-your-user-interface}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Adding Asset Features to Your UI</p><p>Step 1 of 5</p>\n\\end{verbatim}\n\nNow that your guestbook and guestbook entry entities are asset-enabled,\nyou can add asset functionality to your application. You'll start by\nimplementing comments, ratings, tags, categories, and related assets for\nguestbooks. Then you'll do the same for guestbook entries. All the\nback-end support for these features is already implemented. Your only\ntask is to update your applications' user interfaces to use these\nfeatures.\n\nNow you'll create several new JSPs that need new imports. Add the\nfollowing imports to the \\texttt{guestbook-web} module project's\n\\texttt{init.jsp} file:\n\n\\begin{verbatim}\n<%@ taglib uri=\"http://liferay.com/tld/asset\" prefix=\"liferay-asset\" %>\n<%@ taglib uri=\"http://liferay.com/tld/comment\" prefix=\"liferay-comment\" %>\n\n<%@ page import=\"com.liferay.asset.kernel.service.AssetEntryLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.asset.kernel.service.AssetTagLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.asset.kernel.model.AssetEntry\" %>\n<%@ page import=\"com.liferay.asset.kernel.model.AssetTag\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.ListUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.comment.Discussion\" %>\n<%@ page import=\"com.liferay.portal.kernel.comment.CommentManagerUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.service.ServiceContextFunction\" %>\n\\end{verbatim}\n\nAdd these imports now so you don't run into errors as you work through\nthe steps.\n\n\\chapter{Creating JSPs for Displaying Custom Assets in the Asset\nPublisher}\\label{creating-jsps-for-displaying-custom-assets-in-the-asset-publisher}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Adding Asset Features to Your UI</p><p>Step 2 of 5</p>\n\\end{verbatim}\n\nBefore proceeding, you must tie up a loose end from the previous step.\nRemember that you implemented \\texttt{getJspPath} methods in your\n\\texttt{GuestbookAssetRenderer} and \\texttt{GuestbookEntryAssetRenderer}\nclasses to JSPs that don't exist yet. These methods return paths to JSPs\nthe Asset Publisher uses to display the assets' full content. The\n\\texttt{getJspPath} method of \\texttt{GuestbookAssetRenderer} returns\n\\texttt{\"/asset/guestbook/full\\_content.jsp\"}, and the\n\\texttt{getJspPath} method of \\texttt{EntryAssetRenderer} returns\n\\texttt{\"/asset/entry/full\\_content.jsp\"}. It's time to create these\nJSPs.\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\texttt{guestbook-web} module project, create a new folder\n  called \\texttt{asset} under the \\texttt{resources/META-INF/resources}\n  folder. Add two folders to this new folder: \\texttt{entry} and\n  \\texttt{guestbook}.\n\\item\n  Create a new file called \\texttt{full\\_content.jsp} in the\n  \\texttt{/asset/guestbook} folder. This JSP displays a guestbook\n  asset's full content. Add the following code to this file:\n\n\\begin{verbatim}\n<%@include file=\"../../init.jsp\"%>\n\n<%\nGuestbook guestbook = (Guestbook)request.getAttribute(\"gb_guestbook\");\n\nguestbook = guestbook.toEscapedModel();\n%>\n\n<dl>\n        <dt>Name</dt>\n        <dd><%= guestbook.getName() %></dd>\n</dl>\n\\end{verbatim}\n\n  This JSP grabs the guestbook object from the request and displays the\n  guestbook's name. In \\texttt{GuestbookAssetRenderer}, the\n  \\texttt{getJspPath} method added the \\texttt{gb\\_guestbook} request\n  attribute:\n\n\\begin{verbatim}\nrequest.setAttribute(\"gb_guestbook\", _guestbook);\n\\end{verbatim}\n\n  The guestbook's \\texttt{toEscapedModel} method belongs to the\n  \\texttt{GuestbookModelImpl} class, which was generated by Service\n  Builder. This method returns a \\emph{safe} guestbook object (a\n  guestbook in which each field is HTML-escaped). Calling\n  \\texttt{guestbook\\ =\\ guestbook.toEscapedModel()} before displaying\n  the guestbook name ensures that your JSP won't display malicious code\n  that's masquerading as a guestbook name.\n\\item\n  Next, in the \\texttt{/asset/entry} folder, create a\n  \\texttt{full\\_content.jsp} for displaying a guestbook entry asset's\n  full content. Add the following code to this file:\n\n\\begin{verbatim}\n<%@include file=\"../../init.jsp\"%>\n\n<%\nGuestbookEntry entry = (GuestbookEntry)request.getAttribute(\"gb_entry\");\n\nentry = entry.toEscapedModel();\n%>\n\n<dl>\n        <dt>Guestbook</dt>\n        <dd><%= GuestbookLocalServiceUtil.getGuestbook(entry.getGuestbookId()).getName() %></dd>\n        <dt>Name</dt>\n        <dd><%= entry.getName() %></dd>\n        <dt>Message</dt>\n        <dd><%= entry.getMessage() %></dd>\n</dl>\n\\end{verbatim}\n\\end{enumerate}\n\nThis JSP shows a combination of fields from the Guestbook and the\nselected Guestbook Entry.\n\nAfter deploying your changes, test your new JSPs by clicking a\nguestbook's or guestbook entry's title in the Asset Publisher. The Asset\nPublisher renders \\texttt{full\\_content.jsp}:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/asset-publisher-full-content.png}\n\\caption{When you click the title for a guestbook or guestbook entry in\nthe Asset Publisher, your \\texttt{full\\_content.jsp} should be\ndisplayed.}\n\\end{figure}\n\nBy default, when displaying an asset's full view, the Asset Publisher\ndisplays additional links for social media so you can publicize your\nasset. The \\emph{Back} icon and the \\emph{View in Context} link return\nyou to the Asset Publisher's default view.\n\n\\chapter{Enabling Tags, Categories, and Related Assets for\nGuestbooks}\\label{enabling-tags-categories-and-related-assets-for-guestbooks}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Adding Asset Features to Your UI</p><p>Step 3 of 5</p>\n\\end{verbatim}\n\nSince you already asset-enabled guestbooks at the service layer,\nguestbook entities can now support tags and categories. All that's left\nis to enable them in the UI. In this step, you'll update the Guestbook\nAdmin portlet's \\texttt{edit\\_guestbook.jsp} so administrators can add,\nedit, or remove tags and categories when adding or updating a guestbook.\n\n\\section{Enabling Asset Features}\\label{enabling-asset-features}\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\texttt{guestbook-web} module's\n  \\texttt{/guestbook\\_admin/edit\\_guestbook.jsp}, add the tags\n  \\texttt{\\textless{}liferay-asset:asset-categories-error\\ /\\textgreater{}}\n  and \\texttt{\\textless{}liferay-asset:asset-tags-error/\\textgreater{}}\n  to the \\texttt{aui:form} below the closing\n  \\texttt{\\textless{}/aui:button-row\\textgreater{}} tag:\n\n\\begin{verbatim}\n<liferay-asset:asset-categories-error />\n<liferay-asset:asset-tags-error />\n\\end{verbatim}\n\n  These tags display error messages if an error occurs with the tags or\n  categories submitted in the form.\n\\item\n  Below the error tags, add a\n  \\texttt{\\textless{}liferay-ui:panel\\textgreater{}} tag surrounded by a\n  \\texttt{\\textless{}c:if\\textgreater{}} statement:\n\n\\begin{verbatim}\n<c:if test=\"<%= guestbook != null %>\">\n\n    <liferay-ui:panel defaultState=\"closed\" extended=\"<%= false %>\"\n      id=\"guestbookCategorizationPanel\" persistState=\"<%= true %>\"\n      title=\"categorization\">\n\n    </liferay-ui:panel>\n\n</c:if>\n\\end{verbatim}\n\n  The \\texttt{\\textless{}liferay-ui:panel\\textgreater{}} tag generates a\n  collapsible section. The tags you'll add in the next step don't work\n  if \\texttt{guestbook} is \\texttt{null}, so you only display the panel\n  if the current Guestbook is being edited.\n\\item\n  Add input fields for tags and categories inside the panel section you\n  just created. Specify the \\texttt{assetCategories} and\n  \\texttt{assetTags} types for the\n  \\texttt{\\textless{}aui:input\\ /\\textgreater{}} tags. These input tags\n  represent asset categories and asset tags. You can group related input\n  fields together with an \\texttt{\\textless{}aui:fieldset\\textgreater{}}\n  tag. The tags generate the appropriate selectors for tags and\n  categories and displays those that have already been added to the\n  guestbook:\n\n\\begin{verbatim}\n<aui:fieldset>\n    <liferay-asset:asset-categories-selector className=\"<%= Guestbook.class.getName() %>\" classPK=\"<%= guestbook.getGuestbookId() %>\" />\n    <liferay-asset:asset-tags-selector className=\"<%= Guestbook.class.getName() %>\" classPK=\"<%= guestbook.getGuestbookId() %>\" />\n</aui:fieldset>\n\\end{verbatim}\n\\item\n  Add a second \\texttt{\\textless{}liferay-ui:panel\\textgreater{}} tag\n  under the existing one. In this new tag, add an\n  \\texttt{\\textless{}aui:fieldset\\textgreater{}} tag containing a\n  \\texttt{\\textless{}liferay-ui:asset-links\\textgreater{}} tag. To\n  display the correct asset links (the selected guestbook's related\n  assets), set the \\texttt{className} and \\texttt{classPK} attributes:\n\n\\begin{verbatim}\n<liferay-ui:panel defaultState=\"closed\" extended=\"<%= false %>\"\n  id=\"guestbookAssetLinksPanel\" persistState=\"<%= true %>\"\n  title=\"related-assets\">\n  <aui:fieldset>\n    <liferay-asset:input-asset-links\n      className=\"<%= Guestbook.class.getName() %>\"\n      classPK=\"<%= guestbookId %>\" />\n  </aui:fieldset>\n</liferay-ui:panel>\n\\end{verbatim}\n\\end{enumerate}\n\nTest the updated \\texttt{edit\\_guestbook.jsp} page by navigating to the\nGuestbook Admin portlet in the Control Panel and clicking \\emph{Add\nGuestbook}. After adding the Guestbook, edit it. You'll see a field for\nadding tags and a selector for selecting related assets.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/guestbook-tags-related-assets.png}\n\\caption{Once you've updated your Guestbook Admin portlet's\n\\texttt{edit\\_guestbook.jsp} page, you'll see forms for adding tags and\nselecting related assets.}\n\\end{figure}\n\nDon't do anything with these fields yet, because you're not done\nimplementing assets. Next, you'll enable tags and categories for\nguestbook entries.\n\n\\chapter{Enabling Tags, Categories, and Related Assets for Guestbook\nEntries}\\label{enabling-tags-categories-and-related-assets-for-guestbook-entries}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Adding Asset Features to Your UI</p><p>Step 4 of 5</p>\n\\end{verbatim}\n\nEnabling tags, categories, and related assets for guestbook entries is\nsimilar to enabling them for guestbooks. Please refer back to the\nprevious step for a detailed explanation.\n\nOpen your \\texttt{guestbook-web} module's\n\\texttt{guestbook/edit\\_entry.jsp} file. You'll add two pieces of code:\na header for navigation and a panel for tags and categories similar to\nthe one you added to the \\texttt{edit\\_guestbook.jsp} file.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the header after the \\texttt{addEntry} action URL tag:\n\n\\begin{verbatim}\n<liferay-ui:header\n    backURL=\"<%= viewURL.toString() %>\"\n    title=\"<%= entry == null ? \"Add Entry\" : entry.getName() %>\"\n/>\n\\end{verbatim}\n\\item\n  Add the asset tags/categories/links in a collapsible panel after the\n  closing \\texttt{\\textless{}/aui:fieldset\\textgreater{}}:\n\n  ```markup\n\\end{enumerate}\n\n\\begin{verbatim}\n<aui:fieldset>\n   <liferay-asset:asset-categories-selector className=\"<%= GuestbookEntry.class.getName() %>\" classPK=\"<%= entryId %>\" />\n   <liferay-asset:asset-tags-selector className=\"<%= GuestbookEntry.class.getName() %>\" classPK=\"<%= entryId %>\" />\n</aui:fieldset>\n\\end{verbatim}\n\n\\begin{verbatim}\n<aui:fieldset collapsed=\"<%= true %>\" collapsible=\"<%= true %>\" label=\"related-assets\">\n        \n    <liferay-asset:input-asset-links\n        className=\"<%= GuestbookEntry.class.getName() %>\"\n        classPK=\"<%= entryId %>\"\n    />\n        \n</aui:fieldset>\n\\end{verbatim}\n\n\\begin{verbatim}\n\nTest your JSP by using the Guestbook portlet to add and update Guestbook \nentries. Add and remove tags, categories, and related assets. \n\n\n\\noindent\\hrulefill\n\n **Note:** Setting your custom asset as the *Main Asset* of a page is\n required to display related assets in the Related Assets portlet. This is done\n when creating\n [Friendly URLs](/docs/7-2/tutorials/-/knowledge_base/t/making-urls-friendlier)\n in a later step.\n\n\\noindent\\hrulefill\n\nWell done! Next, you'll enable comments and ratings for guestbook entries. \n\n\n# Enabling Comments and Ratings for Guestbook Entries\n\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Adding Asset Features to Your UI</p><p>Step 5 of 5</p>\n</div>\n\nThe asset framework lets users comment on and rate assets. As with tags,\ncategories, and related assets, you must update the user interface to expose\nthese features. Good application design requires that you have a View page where\nusers can rate and comment on assets. Follow these steps to enable comments and\nratings on guestbook entries: \n\n1.  Create a new file called `view_entry.jsp` in your `guestbook-web` module \n    project's `src/main/resources/META-INF/resources/guestbook` folder. \n\n2.  Add a Java scriptlet to the file you just created. In this scriptlet, use an \n    `entryId` request attribute to get a `GuestbookEntry` object. For security\n    reasons, convert this object to an escaped model as discussed in the earlier\n    step \n    [Creating JSPs for Displaying Custom Assets in the Asset Publisher](/docs/7.2/tutorials/-/knowledge_base/t/creating-jsps-for-displaying-custom-assets-in-the-asset-publisher):\n\n    ```markup\n    <%@ include file=\"../init.jsp\"%>\n\n    <%\n      long entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\n      long guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n      GuestbookEntry entry = null;\n\n      entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n\n      entryId = entry.getEntryId();\n\n      entry = entry.toEscapedModel();\n\n      AssetEntry assetEntry = \n      AssetEntryLocalServiceUtil.getEntry(GuestbookEntry.class.getName(), \n      entry.getEntryId());\n    ```\n\n3.  Next, update the breadcrumb entry with the current entry's name: \n\n    ```markup\n    String currentURL = PortalUtil.getCurrentURL(request);\n    PortalUtil.addPortletBreadcrumbEntry(request, entry.getMessage(),\n    currentURL);\n    ```\n\n4.  End the scriptlet by adding the names of the current entry's existing \n    asset tags as keywords to the portal page. These tag names appear in a \n    `<meta content=\"[tag names here]\" lang=\"en-US\" name=\"keywords\" />` element \n    in your portal page's `<head>` section. These keywords can help search \n    engines find and index your page: \n\n    ```markup\n        PortalUtil.setPageSubtitle(entry.getMessage(), request);\n        PortalUtil.setPageDescription(entry.getMessage(), request);\n\n        List<AssetTag> assetTags = \n        AssetTagLocalServiceUtil.getTags(GuestbookEntry.class.getName(), \n        entry.getEntryId());\n        PortalUtil.setPageKeywords(ListUtil.toString(assetTags, \"name\"), \n        request);\n    %>\n    ```\n\n5.  After the scriptlet, specify the URLs for the page and back link: \n\n    ```markup\n    <liferay-portlet:renderURL varImpl=\"viewEntryURL\">\n      <portlet:param name=\"mvcPath\"\n        value=\"/guestbook/view_entry.jsp\" />\n      <portlet:param name=\"entryId\" value=\"<%=String.valueOf(entryId)%>\" />\n    </liferay-portlet:renderURL>\n\n    <liferay-portlet:renderURL varImpl=\"viewURL\">\n      <portlet:param name=\"mvcPath\"\n        value=\"/guestbook/view.jsp\" />\n    </liferay-portlet:renderURL>\n\n    <liferay-ui:header backURL=\"<%=viewURL.toString()%>\"\n      title=\"<%=entry.getName()%>\" \n    />\n    ```\n\n6.  Next, define the page's main content. Display the guestbook's name and the \n    entry's name and message with the `<dl>`, `<dt>`, and `<dd>` tags: \n\n    ```markup\n    <dl>\n      <dt>Guestbook</dt>\n      <dd><%=GuestbookLocalServiceUtil.getGuestbook(entry.getGuestbookId()).getName()%></dd>\n      <dt>Name</dt>\n      <dd><%=entry.getName()%></dd>\n      <dt>Message</dt>\n      <dd><%=entry.getMessage()%></dd>\n    </dl>\n    ```\n\n    This is the same way you defined the page's main content in \n    `/asset/full_content.jsp`. \n\n7.  Next, use a `<liferay-ui:panel-container>` tag to create a panel container. \n    Inside this tag, use a `<liferay-ui:panel>` tag to create a panel to display\n    the comments and ratings components: \n\n    ```markup\n    <liferay-ui:panel-container extended=\"<%=false%>\"\n      id=\"guestbookCollaborationPanelContainer\" persistState=\"<%=true%>\">\n      <liferay-ui:panel collapsible=\"<%=true%>\" extended=\"<%=true%>\"\n        id=\"guestbookCollaborationPanel\" persistState=\"<%=true%>\"\n        title=\"Collaboration\">\n    ```\n\n8.  Add the ratings component with the `<liferay-ui:ratings>` tag:\n\n    ```markup\n    <liferay-ui:ratings className=\"<%=GuestbookEntry.class.getName()%>\"\n      classPK=\"<%=entry.getEntryId()%>\" type=\"stars\" />\n\n    <br />\n    ```\n\n9.  Next, add a scriptlet to retrieve the comments discussion object:\n\n    ```markup\n    <% \n        Discussion discussion = \n        CommentManagerUtil.getDiscussion(user.getUserId(), \n        scopeGroupId, GuestbookEntry.class.getName(), \n        entry.getEntryId(), new ServiceContextFunction(request));\n    %>\n    ```\n\n10.  Below that add the tag for tracking the number of comments:\n\n    ```markup\n    <c:if test=\"<%= discussion != null %>\">\n      <h2>\n        <strong><liferay-ui:message arguments=\"<%= discussion.getDiscussionCommentsCount() %>\" key='<%= (discussion.getDiscussionCommentsCount() == 1) ? \"x-comment\" : \"x-comments\" %>' /></strong>\n    ```\n\n11. Create the `liferay-comment:discussion` tag, which creates the comments\n    form, *Reply* button, and retrieves the discussion content. It also\n    handles the form action of posting the comment without requiring\n    you to create a portlet action URL.\n\n    ```markup\n      <liferay-comment:discussion\n        className=\"<%= GuestbookEntry.class.getName() %>\"\n        classPK=\"<%= entry.getEntryId() %>\"\n        discussion=\"<%= discussion %>\"\n        formName=\"fm2\"\n        ratingsEnabled=\"true\"\n        redirect=\"<%= currentURL %>\"\n        userId=\"<%= entry.getUserId() %>\"\n        />\n    </c:if>\n\n  </liferay-ui:panel>\n</liferay-ui:panel-container>\n    ```\n\n12. To restrict comments and ratings access to logged-in users, wrap the whole \n    panel container in a `<c:if>` tag that tests the expression \n    `themeDisplay.isSignedIn()`:\n\n    ```markup\n    <c:if test=\"<%= themeDisplay.isSignedIn() %>\">\n        ... your panel container ...\n    </c:if>\n    ```\n\n    Make sure you add the closing `</c:if>` tag after the closing \n    `</liferay-ui:panel-container>` tag.\n\n\n\\noindent\\hrulefill\n\n **Note:** Discussions (comments) are implemented as message board messages. In\n the `MBMessage` table, there's a `classPK` column. This `classPK` represents\n the guestbook entry's `entryId`, linking the comment to the guestbook. Ratings\n are stored in the `RatingsEntry` table. Similarly, the `RatingsEntry` table\n contains a `classPK` column that links the guestbook entry to the rating. Using\n a `classPK` foreign key in one table to represent the primary key of another\n table is a common pattern throughout Liferay DXP.\n\n\\noindent\\hrulefill\n\nNext, you'll update the guestbook actions to use the new view. \n\n## Updating the Entry Actions JSP\n\nYour `view_entry.jsp` page is currently orphaned. Fix this by adding the *View*\noption to the Actions Menu. Open the `/guestbook/entry_actions.jsp`\nand find the following line:\n\n```markup\n<liferay-ui:icon-menu>\n\\end{verbatim}\n\nAdd the following lines below it:\n\n\\begin{verbatim}\n<portlet:renderURL var=\"viewEntryURL\">\n  <portlet:param name=\"entryId\"\n    value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n  <portlet:param name=\"mvcPath\"\n    value=\"/guestbook/view_entry.jsp\" />\n</portlet:renderURL>\n\n<liferay-ui:icon message=\"View\" url=\"<%= viewEntryURL.toString() %>\" />\n\\end{verbatim}\n\nHere, you create a URL that points to \\texttt{view\\_entry.jsp}. Test\nthis link by selecting the \\emph{View} option in a guestbook entry's\nActions Menu. Then test your comments and ratings.\n\nExcellent! You've asset-enabled the guestbook and guestbook entry\nentities and enabled tags, categories, and related assets for both\nentities. You've also enabled comments and ratings for guestbook entry\nentities! Great job!\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/asset-publisher-full-content-finished.png}\n\\caption{Now you can see comments, rating, and the full range of asset\nfeatures.}\n\\end{figure}\n\nYour next task is to add Workflow, so you can approve or deny guestbook\nentries, thereby preventing people from spamming your guestbook.\n\n\\chapter{Using Workflow}\\label{using-workflow}\n\nThe Guestbook application accepts submissions from any logged in user,\nso there's no telling what people could post. Illegal data,\nobjectionable content, the entire contents of Don Quixote: all of these\nand more are possibilities. You can make sure user posts don't run afoul\nof the law or policy by enabling \\emph{workflow} in your application.\n\nWorkflow is a review process that ensures a submitted entity isn't\npublished before it's reviewed. To prevent posting objectionable\ncontent, an initially submitted Guestbook entry should be marked as a\n\\emph{draft} and sent through the workflow framework. It comes back to\nthe application code ready to have any relevant fields updated in the\ndatabase based on its status. The view layer must filter entities by\nstatus to display only reviewed entities.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The exact review process is defined separately from the\ncode that enables workflow. An XML file provides the definition of a\nworkflow in Liferay DXP. If you're a Liferay Digital Enterprise\nsubscriber, you have access to the Workflow Designer, which offers a\nconvenient drag-and-drop user interface for designing workflow\ndefinition files. You can read more about this in Liferay DXP's\n\\href{https://help.liferay.com/hc/en-us/articles/360028821892-Workflow-Designer}{documentation}.\nLiferay DXP comes with a workflow definition called the \\emph{Single\nApprover} definition, but you can write your own workflow definitions\naccording to your organization's requirements.\n\nA few additional definitions are included in Liferay DXP's source code,\nwhich you can use to see how workflow definitions are defined. To\ndiscover how to access these files, see\n\\href{/docs/7-2/user/-/knowledge_base/u/enabling-workflow}{here}.\n\n\\noindent\\hrulefill\n\nThis tutorial instructs the reader in workflow-enabling the Guestbook\nApp's \\texttt{Guestbook} and \\texttt{GuestbookEntry} entities to ensure\nthat only approved content is published after review.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-config.png}\n\\caption{Enable workflow in your assets, just like Liferay DXP's own\nassets.}\n\\end{figure}\n\nThere are five steps to enabling workflow:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Update the service layer to set each entity's status fields.\n\\item\n  Send the entity to Liferay DXP's workflow framework.\n\\item\n  Add \\emph{getter} methods that account for an entity's workflow\n  status.\n\\item\n  Handle the entity as it returns from the workflow framework.\n\\item\n  Update the user interface to account for workflow status.\n\\end{enumerate}\n\nThe first three steps happen in the service layer, so that's a good\nplace to start.\n\nLet's Go!{}\n\n\\chapter{Supporting Workflow at the Service\nLayer}\\label{supporting-workflow-at-the-service-layer}\n\nWhen you\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/assets-integrating-with-liferays-framework}{asset\nenabled the Guestbook Application}, you used four database columns in\nthe Guestbook entities that keep track of workflow status (they were\nadded in the beginning; celebrate!). The necessary fields are\n\\texttt{status}, \\texttt{statusByUserName}, \\texttt{statusByUserId}, and\n\\texttt{statusDate}. The columns are defined in the\n\\texttt{guestbook-service} module's \\texttt{service.xml} file.\n\n\\begin{verbatim}\n<column name=\"status\" type=\"int\" />\n<column name=\"statusByUserId\" type=\"long\" />\n<column name=\"statusByUserName\" type=\"String\" />\n<column name=\"statusDate\" type=\"Date\" />\n\\end{verbatim}\n\nThe \\texttt{status} field tells you the current status of the entity (it\ndefaults to \\texttt{0}, which evaluates to \\emph{approved}). The other\nstatus fields store the date of the last change (\\texttt{statusDate})\nalong with the ID and name of the user (\\texttt{statusByUserId} and\n\\texttt{statusByUserName}) who made the update.\n\nAlthough the status columns are in the Guestbook application's entity\ntables, you must update the local service implementation's \\texttt{add}\nmethods to set them, and while you're there, send the entity to the\nworkflow framework. You'll also write a method to update the status\nfields when the entity returns from the workflow framework, along with\ngetters that take workflow status as a parameter. That sounds like a lot\nof work, but thanks to Service Builder, you must change only three\nfiles: \\texttt{service.xml}, \\texttt{GuestbookLocalServiceImpl}, and\n\\texttt{GuestbookEntryLocalServiceImpl}.\n\nLet's Go!{}\n\n\\chapter{Setting the Guestbook\nStatus}\\label{setting-the-guestbook-status}\n\n\\begin{verbatim}\n<p>Supporting Workflow at the Service Layer<br>Step 1 of 3</p>\n\\end{verbatim}\n\nBefore now, you set the status of all added guestbooks to approved in\nthe service layer. Now you'll set it to draft and pass it to the\nworkflow framework.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From \\texttt{guestbook-service}, open\n  \\texttt{GuestbookLocalServiceImpl} and add the status fields below the\n  existing setter methods in the \\texttt{addGuestbook} method:\n\n\\begin{verbatim}\nguestbook.setStatus(WorkflowConstants.STATUS_DRAFT);\nguestbook.setStatusByUserId(userId);\nguestbook.setStatusByUserName(user.getFullName());\nguestbook.setStatusDate(serviceContext.getModifiedDate(null));\n\\end{verbatim}\n\n  This manually populates the status fields and sets the workflow status\n  as a draft in the \\texttt{GB\\_GuestbookEntry} database table. At this\n  point they're identical to the similarly named non-status counterparts\n  (like \\texttt{setUserId} and \\texttt{setStatusByUserId}), but they'll\n  be updated independently in the \\texttt{updateStatus} method you write\n  later.\n\\item\n  Still in the \\texttt{addGuestbook} method, place the following code\n  right before the \\texttt{return} statement:\n\n\\begin{verbatim}\nWorkflowHandlerRegistryUtil.startWorkflowInstance(guestbook.getCompanyId(), \n            guestbook.getGroupId(), guestbook.getUserId(), Guestbook.class.getName(), \n            guestbook.getPrimaryKey(), guestbook, serviceContext);\n\\end{verbatim}\n\n  The call to \\texttt{startWorkflowInstance} detects whether workflow is\n  installed and enabled. If it isn't, the added entity is automatically\n  marked as approved. The \\texttt{startWorkflowInstance} call also calls\n  your \\texttt{GuestbookWorkflowHandler} class, which you'll create\n  later.\n\\item\n  Organize imports (\\emph{{[}CTRL{]}+{[}SHIFT{]}+O}), and save your\n  work.\n\\end{enumerate}\n\nThe \\texttt{startWorkflowInstance} method is where your entity enters\nthe workflow framework, but you're not finished yet. Just like you\nwouldn't drop your child off at college and then change your number and\nmove to a new address, you're not going to abandon your\n\\texttt{Guestbook} entity (yet).\n\n\\section{Creating the updateStatus\nMethod}\\label{creating-the-updatestatus-method}\n\nExert control over how the status fields are updated in the database.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create an \\texttt{updateStatus} method in\n  \\texttt{GuestbookLocalServiceImpl}, immediately following the\n  \\texttt{deleteGuestbook} method. Here's the first half of it:\n\n\\begin{verbatim}\npublic Guestbook updateStatus(long userId, long guestbookId, int status,\n        ServiceContext serviceContext) throws PortalException,\n        SystemException {\n\n    User user = userLocalService.getUser(userId);\n    Guestbook guestbook = getGuestbook(guestbookId);\n\n    guestbook.setStatus(status);\n    guestbook.setStatusByUserId(userId);\n    guestbook.setStatusByUserName(user.getFullName());\n    guestbook.setStatusDate(new Date());\n\n    guestbookPersistence.update(guestbook);\n\\end{verbatim}\n\n  If this method is called, it's because your entity is returning from\n  the workflow framework, and it's time to update the status values in\n  the database. Set the status fields, then persist the updated entity\n  to the database.\n\\item\n  Before saving, finish the method:\n\n\\begin{verbatim}\n    if (status == WorkflowConstants.STATUS_APPROVED) {\n\n        assetEntryLocalService.updateVisible(Guestbook.class.getName(),\n                guestbookId, true);\n\n    } else {\n\n        assetEntryLocalService.updateVisible(Guestbook.class.getName(),\n                guestbookId, false);\n    }\n\n    return guestbook;\n}\n\\end{verbatim}\n\n  This \\texttt{if} statement determines the visibility of the asset\n  based on its workflow status. If it's approved, the\n  \\texttt{assetEntryLocalService.updateVisible} method sets the\n  guestbook in question to \\texttt{true} so it can be displayed in the\n  Asset Publisher and in the search results. Otherwise (\\texttt{else})\n  it sets the visibility to \\texttt{false} to ensure that unapproved\n  guestbooks aren't displayed to users in the Asset Publisher or the\n  Search portlet.\n\\item\n  There's one more update to make in the \\texttt{deleteGuestbook}\n  method. When deleting, you must clean up the workflow system's\n  database tables to avoid leaving orphaned entries when the backing\n  entity is deleted. Before making the method call, open\n  \\texttt{service.xml} and add the following tag below the existing\n  \\texttt{\\textless{}reference\\textgreater{}} tags in the\n  \\texttt{Guestbook} entity:\n\n\\begin{verbatim}\n<reference entity=\"WorkflowInstanceLink\" package-path=\"com.liferay.portal\" />\n\\end{verbatim}\n\\item\n  Back in \\texttt{GuestbookLocalServiceImpl}, find the\n  \\texttt{deleteGuestbook} method and put this method call right before\n  the \\texttt{return} statement:\n\n\\begin{verbatim}\nworkflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(\n    guestbook.getCompanyId(), guestbook.getGroupId(),\n    Guestbook.class.getName(), guestbook.getGuestbookId());\n\\end{verbatim}\n\\item\n  Organize imports (\\emph{{[}CTRL{]}+{[}SHIFT{]}+O}) and save your work.\n  Then run the \\texttt{buildService} Gradle task. It injects the\n  \\texttt{WorkflowInstanceLinkLocalService} service into a protected\n  variable in \\texttt{GuesbookLocalServiceBaseImpl}. Since\n  \\texttt{GuestbookLocalServiceImpl} extends the base class, you can use\n  it directly.\n\\item\n  Run \\emph{Refresh Gradle Project}.\n\\end{enumerate}\n\nNow the guestbook entity's service layer populates the status fields in\nthe database, sends the entity into the workflow framework, and cleans\nup when it's deleted. You'll do the same thing for guestbook entries\nnext.\n\n\\chapter{Setting the Entry Workflow\nStatus}\\label{setting-the-entry-workflow-status}\n\n\\begin{verbatim}\n<p>Supporting Workflow at the Service Layer<br>Step 2 of 3</p>\n\\end{verbatim}\n\nNow you'll set the status fields, introduce entries to the workflow\nframework, and add the \\texttt{updateStatus} method to\n\\texttt{GuestbookEntryLocalServiceImpl}. It works the same as it did for\nguestbooks.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the following lines in the \\texttt{addGuestbookEntry} method,\n  immediately after the existing setter methods (e.g.,\n  \\texttt{entry.setMessage(message)}):\n\n\\begin{verbatim}\nentry.setStatus(WorkflowConstants.STATUS_DRAFT);\nentry.setStatusByUserId(userId);\nentry.setStatusByUserName(user.getFullName());\nentry.setStatusDate(serviceContext.getModifiedDate(null));\n\\end{verbatim}\n\\item\n  Still in the \\texttt{addGuestbookEntry} method, place the following\n  code right before the \\texttt{return} statement:\n\n\\begin{verbatim}\nWorkflowHandlerRegistryUtil.startWorkflowInstance(entry.getCompanyId(), \n            entry.getGroupId(), entry.getUserId(), GuestbookEntry.class.getName(), \n            entry.getPrimaryKey(), entry, serviceContext);\n\\end{verbatim}\n\n  The \\texttt{startWorkflowInstance} call eventually directs the\n  workflow processing to your \\texttt{GuestbookEntryWorkflowHandler}\n  class, which you'll create later. That class is responsible for making\n  sure the entity is updated in the database (via an\n  \\texttt{updateStatus} method), but it's best practice to make\n  persistence calls in the service layer.\n\\item\n  Add a corresponding \\texttt{updateStatus} method here in\n  \\texttt{GuestbookEntryLocalServiceImpl}. Add this method to the bottom\n  of the class:\n\n\\begin{verbatim}\npublic GuestbookEntry updateStatus(long userId, long guestbookId, long entryId, int status,\n       ServiceContext serviceContext) throws PortalException,\n       SystemException {\n\n   User user = userLocalService.getUser(userId);\n   GuestbookEntry entry = getGuestbookEntry(entryId);\n\n   entry.setStatus(status);\n   entry.setStatusByUserId(userId);\n   entry.setStatusByUserName(user.getFullName());\n   entry.setStatusDate(new Date());\n\n   guestbookEntryPersistence.update(entry);\n\n   if (status == WorkflowConstants.STATUS_APPROVED) {\n\n       assetEntryLocalService.updateVisible(GuestbookEntry.class.getName(),\n               entryId, true);\n\n   } else {\n\n       assetEntryLocalService.updateVisible(GuestbookEntry.class.getName(),\n               entryId, false);\n   }\n\n   return entry;\n}\n\\end{verbatim}\n\\item\n  As with Guestbooks, you must add a call to\n  \\texttt{deleteWorkflowInstanceLinks} in the entry's delete method to\n  avoid leaving orphaned database entries in the\n  \\texttt{workflowinstancelinks} table. First add the following\n  \\texttt{\\textless{}reference\\textgreater{}} tag to\n  \\texttt{service.xml}, this time in the \\texttt{entry} entity section,\n  below the existing reference tags:\n\n\\begin{verbatim}\n<reference entity=\"WorkflowInstanceLink\" package-path=\"com.liferay.portal\" />\n\\end{verbatim}\n\\item\n  Add the following method call to the \\texttt{deleteGuestbookEntry}\n  method in \\texttt{GuestbookEntryLocalServiceImpl}, right before the\n  \\texttt{return} statement:\n\n\\begin{verbatim}\nworkflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(\n    entry.getCompanyId(), entry.getGroupId(),\n    GuestbookEntry.class.getName(), entry.getEntryId());\n\\end{verbatim}\n\\item\n  Organize imports (\\emph{{[}CTRL{]}+{[}SHIFT{]}+O}), save your work,\n  run Service Builder, and refresh the Gradle project.\n\\end{enumerate}\n\nNow both entities support the status of the entity and can handle it as\nit enters the workflow framework and as it returns from the workflow\nframework. There's one more update to make in the local service\nimplementation classes: adding getter methods that take the status as a\nparameter. Later you'll use these methods in the view layer so you can\ndisplay only approved guestbooks and entries.\n\n\\chapter{Retrieving Guestbooks and Entries by\nStatus}\\label{retrieving-guestbooks-and-entries-by-status}\n\n\\begin{verbatim}\n<p>Supporting Workflow at the Service Layer<br>Step 3 of 3</p>\n\\end{verbatim}\n\nThe service implementation for both entities now supports adding the\nstatus fields to the database tables. There's one more update to make in\nthe service layer, but to understand why, you must think about the view\nlayer. When the Guestbook portlet displays entries, you must make sure\nit doesn't show entries that haven't been approved. Currently, the\nentry's view layer shows all guestbooks:\n\n\\begin{verbatim}\n    List<Guestbook> guestbooks = GuestbookLocalServiceUtil\n                .getGuestbooks(scopeGroupId);\n\\end{verbatim}\n\nThere's a problem: the getter only takes the \\texttt{scopeGroupId} as a\nparameter, so there's no way to get guestbooks by their status.\n\nLikewise, unapproved entries must not be displayed, but the view layer\ncurrently gets all entries:\n\n\\begin{verbatim}\n    <liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntriesCount()%>\">\n    <liferay-ui:search-container-results\n        results=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(scopeGroupId.longValue(),\n                        guestbookId, searchContainer.getStart(),\n                        searchContainer.getEnd())%>\" />\n\\end{verbatim}\n\nThe solution is to implement for guestbooks and entries a getter that\ntakes the \\texttt{status} field as a parameter. Thankfully, Service\nBuilder makes it easy.\n\nOpen the \\texttt{guestbook-service} module's \\texttt{service.xml} file.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  For the \\texttt{GuestbookEntry} entity, remove the following finder:\n\n\\begin{verbatim}\n <finder name=\"G_S\" return-type=\"Collection\">\n   <finder-column name=\"groupId\" />\n   <finder-column name=\"status\" />\n </finder>\n\\end{verbatim}\n\\item\n  Add this finder in its place:\n\n\\begin{verbatim}\n<finder name=\"G_G_S\" return-type=\"Collection\">\n   <finder-column name=\"groupId\" />\n   <finder-column name=\"guestbookId\" />\n   <finder-column name=\"status\" />\n</finder>\n\\end{verbatim}\n\\end{enumerate}\n\nRun service builder (double-click\n\\texttt{guestbook-service/build/buildService} in the Gradle Tasks pane).\nService Builder generates finder methods in the persistence layer that\ntake the specified fields (for example, \\texttt{status}) as parameters.\n\n\\section{Calling the Persistence\nLayer}\\label{calling-the-persistence-layer}\n\nDon't call the persistence layer directly in the application code.\nInstead expose the new persistence methods in the service layer.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{GuestbookLocalServiceImpl}, add this getter, and save the\n  file:\n\n\\begin{verbatim}\npublic List<Guestbook> getGuestbooks(long groupId, int status)\n    throws SystemException {\n\n    return guestbookPersistence.findByG_S(\n        groupId, WorkflowConstants.STATUS_APPROVED);\n}\n\\end{verbatim}\n\n  This getter gets only approved guestbooks. That's why you hard code\n  the workflow constant \\texttt{STATUS\\_APPROVED} into the status\n  parameter when calling the persistence method.\n\\item\n  Now open \\texttt{GuestbookEntryLocalServiceImpl}, add these two\n  getters, and save the file:\n\n\\begin{verbatim}\npublic List<GuestbookEntry> getGuestbookEntries(\n    long groupId, long guestbookId, int status, int start, int end)\n    throws SystemException {\n\n    return guestbookEntryPersistence.findByG_G_S(\n        groupId, guestbookId, WorkflowConstants.STATUS_APPROVED);\n}\n\npublic int getGuestbookEntriesCount(\n    long groupId, long guestbookId, int status)\n    throws SystemException {\n\n    return guestbookEntryPersistence.countByG_G_S(\n        groupId, guestbookId, WorkflowConstants.STATUS_APPROVED);\n}\n\\end{verbatim}\n\n  You'll replace the existing methods with these\n  \\texttt{getGuestbookEntries} and \\texttt{getGuestbookEntriesCount}\n  methods in the view layer, ensuring that only approved entries are\n  displayed.\n\\item\n  Save the file, run Service Builder, and refresh the Gradle project.\n\\end{enumerate}\n\nThe work here relates to the UI updates you'll make later. Next, you\nmust implement workflow handlers so that you can call the\n\\texttt{updateStatus} service method when the entity returns from the\nworkflow framework.\n\n\\chapter{Handling Workflow}\\label{handling-workflow}\n\nThe guestbook project's service layer is now updated to handle workflow.\nIt now properly sets the status fields for guestbooks and guestbook\nentries, gets entities by their statuses, and sends entities to Liferay\nDXP's workflow framework whenever the \\texttt{addGuestbook} or\n\\texttt{addGuestbookEntry} methods are called. Recall that you still\nhave an uncalled service method, \\texttt{updateStatus}, for both\nentities. Now you'll implement workflow handlers, classes that interact\nwith Liferay DXP's workflow framework and your service layer (by calling\n\\texttt{updateStatus} on the appropriate entity).\n\nThere's a handy abstract class you can extend to make the job easier,\ncalled \\texttt{BaseWorkflowHandler}. You'll do this next for both\nentities of the guestbook project, starting with guestbooks.\n\nLet's Go!{}\n\n\\chapter{Creating a Workflow Handler for\nGuestbooks}\\label{creating-a-workflow-handler-for-guestbooks}\n\n\\begin{verbatim}\n<p>Handling Workflow<br>Step 1 of 2</p>\n\\end{verbatim}\n\nEach workflow enabled entity needs a \\texttt{WorkflowHandler}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new package in the \\texttt{guestboook-service} module called\n  \\texttt{com.liferay.docs.guestbook.workflow}, then create the\n  \\texttt{GuestbookWorkflowHandler} class in it. Extend\n  \\texttt{BaseWorkflowHandler} and pass in \\texttt{Guestbook} as the\n  type parameter:\n\n\\begin{verbatim}\npublic class GuestbookWorkflowHandler extends BaseWorkflowHandler<Guestbook> {\n\\end{verbatim}\n\\item\n  Make it a Component class:\n\n\\begin{verbatim}\n@Component(immediate = true, service = WorkflowHandler.class)\n\\end{verbatim}\n\\item\n  There are three abstract methods to implement: \\texttt{getClassName},\n  \\texttt{getType}, and \\texttt{updateStatus}. First add\n  \\texttt{getClassName}:\n\n\\begin{verbatim}\n@Override\npublic String getClassName() {\n    return Guestbook.class.getName();\n}\n\\end{verbatim}\n\n  \\texttt{getClassName} returns the guestbook entity's fully qualified\n  class name (\\texttt{com.liferay.docs.guestbook.model.Guestbook}).\n\\item\n  Next, add \\texttt{getType}:\n\n\\begin{verbatim}\n@Override\npublic String getType(Locale locale) {\n    return _resourceActions.getModelResource(locale, getClassName());\n}\n\\end{verbatim}\n\n  \\texttt{getType} returns the model resource name\n  (\\texttt{model.resource.com.liferay.docs.guestbook.model.Guestbook}).\n\\item\n  Finally, add the meat of the workflow handler, which is in the\n  \\texttt{updateStatus} method:\n\n\\begin{verbatim}\n@Override\npublic Guestbook updateStatus(\n        int status, Map<String, Serializable> workflowContext)\n    throws PortalException {\n\n    long userId = GetterUtil.getLong(\n        (String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));\n    long resourcePrimKey = GetterUtil.getLong(\n        (String)workflowContext.get(\n            WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n    ServiceContext serviceContext = (ServiceContext)workflowContext.get(\n        \"serviceContext\");\n\n    return _guestbookLocalService.updateStatus(\n        userId, resourcePrimKey, status, serviceContext);\n}\n\\end{verbatim}\n\n  When you crafted the service layer's \\texttt{updateStatus} method (see\n  the last section for more details), you specified parameters that must\n  be passed to the method. Here you're making sure that those parameters\n  are available to pass to the service call. Get the \\texttt{userId} and\n  \\texttt{resourcePrimKey} from \\texttt{GetterUtil}. Its\n  \\texttt{getLong} method takes a \\texttt{String}, which you can get\n  from the \\texttt{workflowContext} \\texttt{Map} using\n  \\texttt{WorkflowConstants} for the context user ID and the context\n  entry class PK.\n\\item\n  Make sure you inject the \\texttt{ResourceActions} service into a\n  private variable at the end of the class, using the\n  \\texttt{@Reference} annotation:\n\n\\begin{verbatim}\n@Reference(unbind = \"-\")\nprotected void setResourceActions(ResourceActions resourceActions) {\n\n    _resourceActions = resourceActions;\n}\n\nprivate ResourceActions _resourceActions;\n\\end{verbatim}\n\\item\n  Inject a \\texttt{GuestbookLocalService} into a private variable using\n  the \\texttt{@Reference} annotation.\n\n\\begin{verbatim}\n    @Reference(unbind = \"-\")\n    protected void setGuestbookLocalService(\n        GuestbookLocalService guestbookLocalService) {\n\n        _guestbookLocalService = guestbookLocalService;\n    }\n\n    private GuestbookLocalService _guestbookLocalService;\n\n}\n\\end{verbatim}\n\\item\n  Organize imports (\\emph{{[}CTRL{]}+{[}SHIFT{]}+O}) and save your work.\n\\end{enumerate}\n\nNow the Guestbook application updates the database with the necessary\nstatus information, interacting with Liferay's workflow classes to make\nsure each entity is properly handled by Liferay DXP. At this point you\ncan enable workflow for the Guestbook inside Liferay DXP and see how it\nworks. Navigate to \\emph{Control Panel → Workflow} → \\emph{Process\nBuilder} → \\emph{Configuration}. The Guestbook entity appears among\nLiferay DXP's native entities. Enable the Single Approver Workflow for\nGuestbooks; then go to the Guestbook Admin portlet and add a new\nGuestbook. A notification appears next to your user name in the product\nmenu. You receive a notification from the workflow that a task is ready\nfor review. Click it, and you're taken to the My Workflow Tasks portlet,\nwhere you can complete the review task.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-notification.png}\n\\caption{Click the workflow notification in the Notifications portlet to\nreview the guestbook submitted to the workflow.}\n\\end{figure}\n\nTo complete the review, click the actions button\n(\\includegraphics{./images/icon-actions.png}) from My Workflow Tasks and\nselect \\emph{Assign to Me}. Click the actions button again and select\n\\emph{Approve}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-assign-to-me.png}\n\\caption{Click the workflow notification in the Notifications portlet to\nreview the guestbook submitted to the workflow.}\n\\end{figure}\n\nRight now the workflow process for guestbooks is functional, but the UI\nisn't adapted for it. You'll write the workflow handler for guestbook\nentries next, and then update the UI to account for each entity's\nworkflow status.\n\n\\chapter{Creating a Workflow Handler for Guestbook\nEntries}\\label{creating-a-workflow-handler-for-guestbook-entries}\n\n\\begin{verbatim}\n<p>Handling Workflow<br>Step 2 of 2</p>\n\\end{verbatim}\n\nThe Guestbook entry's workflow handler is almost identical to the\nguestbook's.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new class in the \\texttt{com.liferay.docs.guestbook.workflow}\n  package of the \\texttt{guestbook-service} module. Name it\n  \\texttt{GuestbookEntryWorkflowHandler} and extend\n  \\texttt{BaseWorkflowHandler}. Paste this in as the class body:\n\n\\begin{verbatim}\n@Component(immediate = true, service = WorkflowHandler.class)\npublic class GuestbookEntryWorkflowHandler extends BaseWorkflowHandler<GuestbookEntry> {\n\n    @Override\n    public String getClassName() {\n\n        return GuestbookEntry.class.getName();\n\n    }\n\n    @Override\n    public String getType(Locale locale) {\n\n        return _resourceActions.getModelResource(locale, getClassName());\n\n    }\n\n    @Override\n    public GuestbookEntry updateStatus(\n        int status, Map<String, Serializable> workflowContext)\n        throws PortalException {\n\n        long userId = GetterUtil.getLong(\n            (String) workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));\n        long resourcePrimKey = GetterUtil.getLong(\n            (String) workflowContext.get(\n                WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n        ServiceContext serviceContext =\n            (ServiceContext) workflowContext.get(\"serviceContext\");\n\n        long guestbookId =\n            _guestbookEntryLocalService.getGuestbookEntry(resourcePrimKey).getGuestbookId();\n\n        return _guestbookEntryLocalService.updateStatus(\n            userId, guestbookId, resourcePrimKey, status, serviceContext);\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setGuestbookEntryLocalService(GuestbookEntryLocalService guestbookEntryLocalService) {\n\n        _guestbookEntryLocalService = guestbookEntryLocalService;\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setResourceActions(ResourceActions resourceActions) {\n\n        _resourceActions = resourceActions;\n    }\n\n    private GuestbookEntryLocalService _guestbookEntryLocalService;\n    private ResourceActions _resourceActions;\n}\n\\end{verbatim}\n\n  There is nothing unique about this code as compared with the\n  guestbook's workflow handler, except that we need the\n  \\texttt{gustbookId} for the entry. That's easily obtained by getting\n  the \\texttt{GuestbookEntry} object with\n  \\texttt{guestbookEntryLocalService}, then getting its\n  \\texttt{guestbookId}. See the last article for the rest of the\n  handler's implementation details.\n\\item\n  Organize imports with \\emph{CTRL+SHIFT+O} and save the file.\n\\end{enumerate}\n\nThe back-end of the guestbook project is fully workflow enabled. All\nthat's left is to update the Guestbook Application's UI to handle\nworkflow status.\n\n\\chapter{Displaying Approved Workflow\nItems}\\label{displaying-approved-workflow-items}\n\nThere's not much left to do. Both entities in the guestbook project's\nback-end are workflow enabled, so it's time to update the UI. The\nGuestbook Admin portlet and the Guestbook portlet each requires its own\ndisplay strategy.\n\nThe Guestbook Admin application is accessed by administrators, so it can\ndisplay all guestbooks that have been submitted, even if they're not\nmarked as approved. However, adding a \\emph{Status} field to the search\ncontainer makes sense. That way admins can see which guestbooks are\nalready approved, which are drafts, which are pending, etc.\n\nThe Guestbook application is meant to be viewed by site members and even\nguests (unauthenticated users of your site). Here it's smart to display\nonly approved guestbooks and approved entries.\n\nStart by updating the Guestbook Admin UI.\n\nLet's Go!{}\n\n\\chapter{Displaying Guestbook Status}\\label{displaying-guestbook-status}\n\n\\begin{verbatim}\n<p>Displaying Approved Workflow Items<br>Step 1 of 2</p>\n\\end{verbatim}\n\nThe Guestbook Admin application's main view currently has a search\ncontainer with two columns: the guestbook name and the guestbook actions\nbutton.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/lp-workflow-admin-nostatus.png}\n\\caption{The Guestbook Admin's main view currently shows the name of the\nguestbook and its actions button.}\n\\end{figure}\n\nNow you'll add a third column between the two existing ones:\n\\emph{Status}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open\n  \\texttt{guestbook-web/src/main/reosurces/META-INF/resources/guestbook\\_admin/view.jsp}.\n\\item\n  Find the existing \\texttt{search-container-column} definitions:\n\n\\begin{verbatim}\n<liferay-ui:search-container-column-text property=\"name\" />\n\n<liferay-ui:search-container-column-jsp align=\"right\"\n    path=\"/guestbook_admin/guestbook_actions.jsp\" />\n\\end{verbatim}\n\\item\n  Put the following new column between the existing columns:\n\n\\begin{verbatim}\n<liferay-ui:search-container-column-status property=\"status\" />\n\\end{verbatim}\n\\end{enumerate}\n\nSave the file and wait for the \\texttt{web} module to redeploy. With the\naddition of one line in the JSP, the Guestbook Admin application now\ndisplays the guestbook's workflow status.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/lp-workflow-admin-status.png}\n\\caption{The Guestbook Admin's main view, displaying the status of each\nguestbook.}\n\\end{figure}\n\nNow you can move on to the Guestbook application's view layer.\n\n\\chapter{Displaying Approved Entries}\\label{displaying-approved-entries}\n\n\\begin{verbatim}\n<p>Displaying Approved Workflow Items<br>Step 2 of 2</p>\n\\end{verbatim}\n\nThe Guestbook application needs to be updated so that only guestbooks\nand entries with a status of \\emph{approved} appear in the UI.\n\nChange the getters used to retrieve both entities in the view layer.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  You need a new import, so first open\n  \\texttt{guestbook-web/src/main/resources/META-INF/resources/init.jsp}\n  and add this line:\n\n\\begin{verbatim}\n<%@ page import=\"com.liferay.portal.kernel.workflow.WorkflowConstants\"%>\n\\end{verbatim}\n\\item\n  Now open\n  \\texttt{guestbook-web/src/main/resources/META-INF/resources/guestbook/view.jsp}.\n  Find the scriptlet that retrieves guestbooks:\n\n\\begin{verbatim}\n<%\n    List<Guestbook> guestbooks = GuestbookLocalServiceUtil\n                .getGuestbooks(scopeGroupId);\n        for (int i = 0; i < guestbooks.size(); i++) {\n            Guestbook curGuestbook = (Guestbook) guestbooks.get(i);\n            String cssClass = StringPool.BLANK;\n            if (curGuestbook.getGuestbookId() == guestbookId) {\n                cssClass = \"active\";\n            }\n            if (GuestbookPermission.contains(\n                permissionChecker, curGuestbook.getGuestbookId(), \"VIEW\")) {\n\n%>\n\\end{verbatim}\n\n  Change it so it calls the getter you added that takes workflow status\n  into account. All you need to do is change this method call\n\n\\begin{verbatim}\nList<Guestbook> guestbooks = GuestbookLocalServiceUtil\n            .getGuestbooks(scopeGroupId);\n\\end{verbatim}\n\n  to\n\n\\begin{verbatim}\nList<Guestbook> guestbooks = GuestbookLocalServiceUtil\n            .getGuestbooks(scopeGroupId, WorkflowConstants.STATUS_APPROVED);\n\\end{verbatim}\n\n  Save the file, and now only approved guestbooks are displayed in the\n  Guestbook application.\n\\item\n  Next, update the entry's UI in the same \\texttt{view.jsp}. Find the\n  tags that set the search container's total and its results:\n\n\\begin{verbatim}\n<liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.\n                getGuestbookEntriesCount()%>\">\n<liferay-ui:search-container-results results=\n                \"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries\n                (scopeGroupId.longValue(),\n                guestbookId, searchContainer.getStart(),\n                searchContainer.getEnd())%>\" />\n\\end{verbatim}\n\n  Replace the getters to use the ones that take workflow status as a\n  parameter, and pass \\texttt{WorkflowConstants.STATUS\\_APPROVED} as the\n  status. Here's what it looks like when you're finished:\n\n\\begin{verbatim}\n<liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.\n                getGuestbookEntriesCount(scopeGroupId.longValue(), \n                guestbookId, WorkflowConstants.STATUS_APPROVED)%>\">\n<liferay-ui:search-container-results results=\n                \"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(\n                scopeGroupId.longValue(), guestbookId, \n                WorkflowConstants.STATUS_APPROVED, \n                searchContainer.getStart(), searchContainer.getEnd())%>\" />\n\\end{verbatim}\n\\end{enumerate}\n\nNow only approved entries are displayed, and the search container's\ncounter only counts the approved entries. If you update the\n\\texttt{getGuestbookEntries} call but not the\n\\texttt{getGuestbookEntriesCount} call, the count that's displayed\nincludes approved entries and entries with any other workflow status,\nand it won't match the total that's displayed at the bottom of the\nsearch container.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/lp-workflow-entries-count.png}\n\\caption{If you don't update the counter method to account for workflow\nstatus, it displays an incorrect count in the search container.}\n\\end{figure}\n\nNow Guestbooks and Guestbook Entries are now fully workflow enabled, to\nthe great relief of the Lunar Resort's site administrators. You've saved\nthem a lot of headaches dealing with inappropriate content, primarily\nsubmitted by visitors from Mars. Those Martians really need some lessons\nin netiquette.\n\n\\chapter{Upgrading Code to 7.0}\\label{upgrading-code-to-7.0}\n\nUpgrading to 7.0 involves migrating your installation and code (your\ncustom apps) to the new version. You'll learn how to upgrade your code\nin this section.\n\nThese tutorials assume you're using the\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-upgrade-planner}{Liferay\nUpgrade Planner}. To follow along with this section, install the planner\nand step through the upgrade instructions. You can also use the planner\nto\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver}{upgrade\nyour data}; this is a separate process that must be done independently\nfrom the code upgrade process.\n\nFor convenience, this tutorial section also references documentation and\noutlined steps to aid those opting to upgrade their code manually.\n\nHere are the code upgrade steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\{.root\\}\\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment}{Upgrade\n  Your Development Environment}\n\n  Legacy project environments should be upgraded to the latest version\n  of Liferay Workspace to ensure you leverage all available\n  features.\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment\\#setting-up-liferay-workspace}{Set\n    Up Liferay Workspace}\n\n    A Liferay Workspace is a generated environment that is built to hold\n    and manage your Liferay projects. Create/import a workspace to get\n    started.\\{.summary\\}\n\n    \\begin{enumerate}\n    \\def\\labelenumiii{\\arabic{enumiii}.}\n    \\item\n      \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment\\#creating-new-liferay-workspace}{Create\n      New Liferay Workspace}\n\n      If you don't have an existing 7.x Liferay Workspace, you must\n      create one. Skip to the next step if you have an existing\n      workspace.\\{.summary\\}\n    \\item\n      \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment\\#importing-existing-liferay-workspace}{Import\n      Existing Liferay Workspace}\n\n      Import an existing Liferay Workspace. If you don't have one,\n      revisit the previous step.\\{.summary\\}\n    \\end{enumerate}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment\\#configuring-liferay-workspace-settings}{Configure\n    Liferay Workspace Settings}\n\n    Set the Liferay DXP version in workspace's configuration you intend\n    to upgrade to.\\{.summary\\}\n\n    \\begin{enumerate}\n    \\def\\labelenumiii{\\arabic{enumiii}.}\n    \\item\n      \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment\\#configure-workspace-product-key}{Configure\n      Workspace Product Key}\n\n      Configure your workspace by setting a product key.\\{.summay\\}\n    \\item\n      \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment\\#initializing-server-bundle}{Initialize\n      Server Bundle}\n\n      Download the Liferay DXP bundle you're upgrading to.\n    \\item\n      \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment\\#migrate-cfg-files-to-config-files}{Migrate\n      .cfg Files to .config Files}\n\n      Convert \\texttt{.cfg} files to \\texttt{.config} files.\\{.summay\\}\n    \\end{enumerate}\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/migrating-plugins-sdk-projects-to-liferay-workspace}{Migrate\n  Plugins SDK Projects}\n\n  Copy your Plugins SDK projects into workspace and convert them to\n  Gradle/Maven projects.\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/migrating-plugins-sdk-projects-to-liferay-workspace\\#importing-existing-plugins-sdk-projects}{Import\n    Existing Plugins SDK Projects}\n\n    Import your existing Plugins SDK projects.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/migrating-plugins-sdk-projects-to-liferay-workspace\\#migrating-existing-plugins-to-workspace}{Migrate\n    Existing Plugins to Workspace}\n\n    Migrate your existing plugins to workspace. This involves moving the\n    plugin to workspace and converting it to the workspace's build\n    environment.\\{.summary\\}\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-build-dependencies}{Upgrade\n  Build Dependencies}\n\n  Optimize your workspace's build environment for the most efficient\n  code upgrade experience.\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-build-dependencies\\#updating-the-repository-url}{Update\n    Repository URL}\n\n    Update your repository URL to Liferay's frequently updated CDN\n    repository.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-build-dependencies\\#updating-the-workspace-plugin-version}{Update\n    Workspace Plugin Version}\n\n    Update your Workspace plugin version to leverage the latest features\n    of Liferay Workspace.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-build-dependencies\\#removing-your-projects-build-dependency-versions}{Remove\n    Dependency Versions}\n\n    Remove the project's dependency versions since it's leveraging\n    target platform.\\{.summary\\}\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/fixing-upgrade-problems}{Fix\n  Upgrade Problems}\n\n  Fix common upgrade problems dealing with your project's dependencies\n  and breaking changes.\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/fixing-upgrade-problems\\#auto-correcting-upgrade-problems}{Auto-Correct\n    Upgrade Problems}\n\n    Auto-correct straightforward upgrade problems.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/fixing-upgrade-problems\\#finding-upgrade-problems}{Find\n    Upgrade Problems}\n\n    Find upgrade problems. These are problems that cannot be\n    auto-corrected; you can update them manually according to the\n    breaking changes documentation.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/fixing-upgrade-problems\\#resolving-upgrade-problems}{Resolve\n    Upgrade Problems}\n\n    Mark upgrade problems as resolved after addressing them.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/fixing-upgrade-problems\\#removing-problem-markers}{Remove\n    Problem Markers}\n\n    After fixing your upgrade problems, remove the problem\n    markers.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolving\n    a Project's Dependencies}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-breaking-changes}{Resolving\n    Breaking Changes}\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-service-builder-services}{Upgrade\n  Service Builder Services}\n\n  Upgrade your Liferay Service Builder services.\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/removing-legacy-files}{Remove\n    Legacy Files}\n\n    Remove legacy files that are no longer leveraged by Service\n    Builder.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/converting-a-service-builder-module-from-spring-di-to-osgi-ds}{Migrate\n    from Spring DI to OSGi Declarative Services}\n\n    Leverage OSGi Declarative Services in your Service Builder\n    project.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/rebuilding-services}{Rebuild\n    Services}\n\n    Rebuild your project's services to persist your updates.\\{.summary\\}\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-customization-plugins}{Upgrade\n  Customization Plugins}\n\n  Upgrade your customization plugins so they're deployable to\n  7.0.\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-customization-modules}{Upgrade\n    Customization Modules}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-core-jsp-hooks}{Upgrade\n    Core JSP Hooks}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portlet-jsp-hooks}{Upgrade\n    Portlet JSP Hooks}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-service-wrapper-hooks}{Upgrade\n    Service Wrapper Hooks}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-core-language-key-hooks}{Upgrade\n    Core Language Key Hooks}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portlet-language-key-hooks}{Upgrade\n    Portlet Language Key Hooks}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-model-listener-hooks}{Upgrade\n    Model Listener Hooks}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-event-action-hooks}{Upgrade\n    Event Action Hooks}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-servlet-filter-hooks}{Upgrade\n    Servlet Filter Hooks}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portal-property-hooks}{Upgrade\n    Portal Properties Hooks}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-struts-action-hooks}{Upgrade\n    Struts Action Hooks}\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-theme-to-7-2}{Upgrade\n  Themes}\n\n  Upgrade your themes so they're deployable to 7.0.\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-6-2-themes-to-7-2}{Upgrade\n    6.2 Themes to 7.2}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-7-0-themes-to-7-2}{Upgrade\n    7.0 Themes to 7.2}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-7-1-themes-to-7-2}{Upgrade\n    7.1 Themes to 7.2}\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-layout-template-to-7-2}{Upgrade\n  Layout Templates}\n\n  Upgrade your layout templates so they're deployable to\n  7.0.\\{.summary\\}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-frameworks-and-features}{Upgrade\n  Frameworks \\& Features}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-jndi-data-source-usage}{Upgrade\n    JNDI Data Source Usage}\n\n    Use Liferay DXP's class loader to access the app server's JNDI\n    API.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-service-builder-service-invocation}{Upgrade\n    Service Builder Service Invocation}\n\n    For Service Builder logic remaining in a WAR, you must implement a\n    service tracker to call services. For logic divided into OSGi\n    modules, you can leverage Declarative Services.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-service-builder}{Upgrade\n    Service Builder}\n\n    Adapt your app to account for Service Builder-specific\n    changes.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/migrating-off-of-velocity-templates}{Migrate\n    Off of Velocity Templates}\n\n    Velocity template usage is deprecated for 7.0. You should convert\n    your template to FreeMarker.\\{.summary\\}\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portlets}{Upgrade\n  Portlets}\n\n  Upgrade your portlets so they're deployable to 7.0.\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-genericportlet}{Upgrade\n    Generic Portlets}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-liferay-mvc-portlet}{Upgrade\n    Liferay MVC Portlets}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-liferay-jsf-portlet}{Upgrade\n    JSF Portlets}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-servlet-based-portlet}{Upgrade\n    Servlet-based Portlets}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-spring-portlet-mvc-portlet}{Upgrading\n    Spring Portlet MVC Portlets}\n  \\item\n    \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-struts-1-portlet}{Upgrade\n    Struts 1 Portlets}\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-web-plugins}{Upgrade\n  Web Plugins}\n\n  Upgrade web plugins previously stored in the \\texttt{webs} folder of\n  your legacy Plugins SDK.\\{.summary\\}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-ext-plugins}{Upgrade\n  Ext Plugins}\n\n  Attempt to leverage an extension point instead of upgrading your Ext\n  plugin. If an Ext plugin is necessary, you must review all changes\n  between the previous Liferay Portal instance you were using and 7.0,\n  and then manually modify your Ext plugin to merge your changes with\n  Liferay DXP's.\\{.summary\\}\n\\end{enumerate}\n\nOnce you've finished the code upgrade steps, your custom apps will be\ncompatible with 7.0!\n\n\\chapter{Upgrading Your Development\nEnvironment}\\label{upgrading-your-development-environment}\n\nA\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace} is a generated environment that is built to hold and manage\nyour Liferay projects. It is intended to aid in the management of\nLiferay projects by providing various build scripts and configured\nproperties.\n\nLiferay Workspace is the recommended environment for your code\nmigration; therefore, it will be the assumed development environment in\nthis section.\n\nContinue on to set up a workspace.\n\n\\section{Setting Up Liferay\nWorkspace}\\label{setting-up-liferay-workspace}\n\nYou must set up your workspace development environment before you begin\nupgrading your custom apps. If you don't have an existing workspace,\nfollow the step for creating one. If you have an existing workspace,\nfollow the step on importing it into the Upgrade Planner.\n\n\\section{Creating New Liferay\nWorkspace}\\label{creating-new-liferay-workspace}\n\nInitiating this step in the Upgrade Planner loads the Liferay Workspace\nProject wizard.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Give your new workspace a name.\n\\item\n  Choose the build type (Gradle or Maven) you prefer for your workspace\n  environment and future Liferay projects.\n\\item\n  Click Finish.\n\\end{enumerate}\n\nYou now have a new Liferay Workspace available in the Upgrade Planner!\n\nFor more information on creating a Liferay Workspace outside the\nplanner, see the\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-a-liferay-workspace}{Creating\na Liferay Workspace} section.\n\n\\section{Importing Existing Liferay\nWorkspace}\\label{importing-existing-liferay-workspace}\n\nIf you already have an existing 7.x Liferay Workspace, you should import\nit into the planner. Once you initiate this step, you're given a File\nExplorer/Manager to select your existing workspace. After selecting it,\nthe workspace is imported into the Project Explorer.\n\nFor more information importing a workspace into your IDE, see\n\\href{/docs/7-2/reference/-/knowledge_base/r/importing-projects-in-dev-studio}{this\narticle}.\n\n\\section{Configuring Liferay Workspace\nSettings}\\label{configuring-liferay-workspace-settings}\n\nYou must configure your workspace with the Liferay DXP version you\nintend to upgrade to.\n\n\\section{Configure Workspace Product\nKey}\\label{configure-workspace-product-key}\n\nConfigure your workspace by setting a product key. This automatically\nsets the Target Platform version, Docker image name, bundle URL, and\nother default settings for the Liferay DXP release.\n\n\\section{Initializing Server Bundle}\\label{initializing-server-bundle}\n\nOnce your workspace is configured for the Liferay DXP version you're\nupgrading to, you can initialize the server bundle. This involves\ndownloading the bundle and extracting it into its folder (e.g.,\n\\texttt{bundles}). If you have an existing workspace already equipped\nwith an older Liferay bundle, this deletes the old bundle and\ninitializes the new one.\n\nIf you're upgrading your code manually and working in Dev Studio, you\ncan do this by right-clicking the workspace project and selecting\n\\emph{Liferay} → \\emph{Initialize Server Bundle}. See the\n\\href{/docs/7-2/reference/-/knowledge_base/r/installing-a-server-in-intellij}{Installing\na Server in IntelliJ} article if you use IntelliJ instead. Visit the\n\\href{/docs/7-2/reference/-/knowledge_base/r/managing-your-liferay-server-with-blade-cli}{Managing\nYour Liferay Server with Blade CLI} article for information on how to do\nthis via the command line.\n\n\\section{Migrate .cfg Files to .config\nFiles}\\label{migrate-.cfg-files-to-.config-files}\n\n\\texttt{.config} files are preferred over \\texttt{.cfg} files because\nthey allow specifying a property value's type, and allow multi-valued\nproperties.\n\n\\chapter{Migrating Plugins SDK Projects to Liferay\nWorkspace}\\label{migrating-plugins-sdk-projects-to-liferay-workspace}\n\nThe Plugins SDK was deprecated for Liferay DXP 7.0 and removed for\nLiferay DXP 7.1. Therefore, to upgrade your custom apps to 7.0, you must\nmigrate them to a new environment.\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace}{Liferay\nWorkspace} is the recommended environment for your code migration and\nwill be the assumed choice in this section.\n\nThere are two steps you must follow to migrate your custom code to\nworkspace:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Import the Plugins SDK project into the Upgrade Planner.\n\\item\n  Convert the Plugins SDK project to a supported workspace build type.\n\\end{enumerate}\n\nYou'll step through importing a Plugins SDK project first.\n\n\\section{Importing Existing Plugins SDK\nProjects}\\label{importing-existing-plugins-sdk-projects}\n\nInitiating this step in the Upgrade Planner imports your Plugins SDK\nprojects into the Upgrade Planner. These projects originate from the\nPlugins SDK you set when the Upgrade Planner process was started.\n\nIf you're manually upgrading your code, you can skip this step.\n\nYou're now ready to migrate your Plugins SDK projects to your new\nworkspace!\n\n\\section{Migrating Existing Plugins to\nWorkspace}\\label{migrating-existing-plugins-to-workspace}\n\nLiferay Workspace can be generated as a Gradle or Maven environment, but\nit does not support the Plugins SDK's Ant build. Because of this, you\nmust convert your projects to one of the supported build tools:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Gradle\n\\item\n  Maven\n\\end{itemize}\n\nWhen initiating this step for a Gradle-based workspace, your Ant-based\nPlugins SDK project is copied to the applicable workspace folder based\non its project type (e.g., \\texttt{wars}) and is converted to a Gradle\nproject. There is also a Blade CLI command that completes this via the\ncommand line. Visit the\n\\href{/docs/7-2/reference/-/knowledge_base/r/converting-plugins-sdk-projects-with-blade-cli}{Converting\nPlugins SDK Projects with Blade CLI} article for more information.\n\nIf you're migrating your Ant project to a Maven workspace, you must\nmanually copy the project to the applicable folder based on the project\ntype (e.g., \\texttt{wars}). The majority of Plugins SDK projects belong\nin the workspace's \\texttt{wars} folder. You can consult the\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-workspace\\#workspace-anatomy}{Workspace\nAnatomy} section for a full overview of a workspace's folder structure\nand choose where your custom app should reside. Once you've made the\ndecision, copy your custom app to the applicable workspace folder.\n\nThen you must convert your project from Ant to Maven. You'll have to\ncomplete this conversion manually.\n\nOnce you're finished, you should have your project(s) residing in the\napplicable workspace folders as Gradle/Maven projects.\n\n\\chapter{Upgrading Build\nDependencies}\\label{upgrading-build-dependencies}\n\nNow that your projects are readily available in a workspace, you must\nensure your project build dependencies are upgraded. Your workspace\nstreamlines the build dependency upgrade process by only requiring three\nmodifications:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[updating-the-repository-url]{Update the repository URL}\n  (Gradle only)\n\\item\n  \\hyperref[updating-the-workspace-plugin-version]{Update the workspace\n  plugin version}\n\\item\n  \\hyperref[removing-your-projects-build-dependency-versions]{Remove\n  your project's build dependency versions} (Gradle only)\n\\end{itemize}\n\nIf you're upgrading a recently created workspace, only a subset of these\ntasks may be required.\n\nYou'll start by updating the repository URL.\n\n\\section{Updating the Repository URL}\\label{updating-the-repository-url}\n\nInitiating this step in the Upgrade Planner updates the repository URL\nused to download artifacts for your workspace.\n\nIf you're using a Gradle-based workspace, the repository URL is updated\nto point to the latest Liferay CDN repository. This is set in your\nworkspace's \\texttt{settings.gradle} file within the\n\\texttt{buildscript} block like this:\n\n\\begin{verbatim}\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n\\end{verbatim}\n\nOnce the repository URL is set to the proper CDN repository, your build\ndependencies will be downloaded from Liferay's own managed repo.\n\nFor Maven-based workspaces, Maven Central is the default repository, so\nno action is required.\n\n\\section{Updating the Workspace Plugin\nVersion}\\label{updating-the-workspace-plugin-version}\n\nFor the best upgrade experience, you should ensure you're leveraging the\nlatest Liferay Workspace version so all the latest features are\navailable to you. Initiate this step to upgrade the appropriate plugin.\n\nSee the\n\\href{/docs/7-2/reference/-/knowledge_base/r/updating-liferay-workspace}{Updating\nLiferay Workspace} article to do this for Gradle-based workspaces\nmanually. For Maven-based workspaces, make sure you set the latest\n\\href{/docs/7-2/reference/-/knowledge_base/r/bundle-support-plugin}{Bundle\nSupport plugin} version in your root \\texttt{pom.xml} file.\n\n\\section{Removing Your Project's Build Dependency\nVersions}\\label{removing-your-projects-build-dependency-versions}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} This step only applies to Gradle-based workspaces since\nthe target platform feature is only available for Gradle projects at\nthis time.\n\n\\noindent\\hrulefill\n\nSince your workspace is leveraging the target platform feature, there is\nno need to set your plugin's dependency versions in its\n\\texttt{build.gradle} file. This is because the target platform version\nyou set already defines the artifact versions your project uses.\nTherefore, if dependency versions are present in any of your projects'\n\\texttt{build.gradle} files, you must remove them.\n\nInitiate this step to remove your dependency versions from your\nproject's \\texttt{build.gradle} file\n\nAs an example of what a \\texttt{build.gradle}'s \\texttt{dependencies}\nblock should look like, see the below snippet:\n\n\\begin{verbatim}\ndependencies {\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\"\n    compileOnly group: \"javax.portlet\", name: \"portlet-api\"\n    compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n    compileOnly group: \"jstl\", name: \"jstl\"\n    compileOnly group: \"org.osgi\", name: \"osgi.cmpn\"\n}\n\\end{verbatim}\n\nIf you have not set the target platform feature in your workspace, see\nthe\n\\href{/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform}{Managing\nthe Target Platform} article for more information.\n\nGreat! You've successfully upgraded your build dependencies! You likely\nhave compile errors in your project; this is because your dependencies\nmay have changed. You'll learn how to update that and more next.\n\n\\chapter{Fixing Upgrade Problems}\\label{fixing-upgrade-problems}\n\nNow that your development environment build configuration is settled,\nyou can start upgrading your project(s). The two most common upgrade\nproblems are\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Broken\n  project dependencies}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-breaking-changes}{Breaking\n  changes}\n\\end{itemize}\n\nVisit these upgrade problem tutorials for tips on how to fix them.\n\nThis tutorial is heavily focused on the Liferay Upgrade Planner. If\nyou're upgrading your code manually, continue to the listed tutorials\nabove to fix your code upgrade problems.\n\nYou'll begin auto-correcting upgrade problems first.\n\n\\section{Auto-Correcting Upgrade\nProblems}\\label{auto-correcting-upgrade-problems}\n\nInitiate this step to auto-correct straightforward updates like\n\n\\begin{itemize}\n\\tightlist\n\\item\n  package imports\n\\item\n  JSP tag names\n\\item\n  Liferay descriptor versions\n\\item\n  XML descriptor content\n\\item\n  etc.\n\\end{itemize}\n\nIf you choose to preview the auto-correct upgrade problems first, you\ncan view them in the Project Explorer under the \\emph{Liferay Upgrade\nProblems} dropdown. If you click one of the upgrade problems listed with\nthe preview, you're offered documentation in the \\emph{Liferay Upgrade\nPlan Info} window on the proposed change.\n\nOnce you've performed this step, the result list is removed.\n\n\\section{Finding Upgrade Problems}\\label{finding-upgrade-problems}\n\nInitiating this step finds the upgrade problems that were not eligible\nfor auto-correction. The problems are listed under the \\emph{Liferay\nUpgrade Problems} dropdown. If you click one of the upgrade problems\nlisted with the preview, you're offered documentation in the\n\\emph{Liferay Upgrade Plan Info} window on the proposed change.\n\nThese upgrade problems are available in the\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-breaking-changes}{breaking\nchanges} for the version upgrade you're performing.\n\nThe next step is resolving the reported upgrade problems.\n\n\\section{Resolving Upgrade Problems}\\label{resolving-upgrade-problems}\n\nNow that the upgrade problems have been located, you must resolve them.\nAs you select each upgrade problem, the documentation for how to adapt\nyour code is displayed in the \\emph{Liferay Upgrade Plan Info} window.\n\nFor each upgrade problem node, you're also given the version the upgrade\nproblem applies to (e.g., when upgrading to Liferay DXP 7.2 from Liferay\nPortal 6.2, you could have upgrade problems from the 7.0, 7.1, or 7.2\nupgrade). As you step through the reported problems, mark them as\nresolved/skipped using the context menu. You can right-click on the\nproblem in the Project Explorer and choose from four options:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Mark done\n\\item\n  Mark undone\n\\item\n  Ignore\n\\item\n  Ignore all problems of this type\n\\end{itemize}\n\nLeave this step marked as \\emph{Incomplete} until you have resolved all\nupgrade problems accordingly.\n\n\\section{Removing Problem Markers}\\label{removing-problem-markers}\n\nAfter resolving all the reported upgrade problems, you must remove all\npreviously found markers because, in most cases, the line number and\nother accompanying marker information are out of date and must be\nremoved before continuing. Initiate this step to remove all the problem\nmarkers.\n\nGreat! You've fixed all the upgrade problems that could be automatically\ndetected by the Code Upgrade Tool. Next, you'll take a deeper look at\nresolving project dependency errors.\n\nLet's Go!{}\n\n\\chapter{Resolving a Project's\nDependencies}\\label{resolving-a-projects-dependencies}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Fixing Upgrade Problems</p><p>Step 1 of 2</p>\n\\end{verbatim}\n\nYou may have compile errors due to missing Liferay classes or unresolved\nsymbols because they've been moved, renamed, or removed. As a part of\nmodularization in Liferay DXP, many of these classes reside in new\nmodules.\n\nYou must resolve all of these Liferay classes for your project. Some of\nthe class changes are quick and easy to fix. Changes involving the new\nmodules require more effort to resolve, but doing so is still\nstraightforward.\n\nLiferay class changes and required adaptations can be grouped into three\ncategories:\n\n\\begin{itemize}\n\\item\n  \\hyperref[class-moved-to-a-package-in-the-classpath]{Class moved to a\n  package in the classpath}\n\\item\n  \\hyperref[class-moved-to-a-module-not-in-the-classpath]{Class moved to\n  a module \\emph{not} in the classpath}\n\\item\n  \\hyperref[class-replaced-or-removed]{Class replaced or removed}\n\\end{itemize}\n\nContinue on to learn how to resolve each change.\n\n\\section{Class Moved to a Package in the\nClasspath}\\label{class-moved-to-a-package-in-the-classpath}\n\nThis change is common and easy to fix. Consider resolving these classes\nfirst.\n\nSince the module is already on your classpath, you need only update the\nclass import. You can do this by using the Liferay Upgrade Planner or by\norganizing imports in Dev Studio/IntelliJ. The Upgrade Planner reports\neach moved class for you to address one by one. Organizing imports in\nDev Studio/IntelliJ automatically resolves multiple classes at once.\n\nIt's typically faster to resolve moved classes using the mentioned IDEs.\nYou can follow similar instructions for both IDEs:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Comment out or remove any imports marked as errors.\n\\item\n  Execute the \\emph{Organize Imports} keyboard sequence\n  \\emph{Ctrl-Shift-o} (Dev Studio) or \\emph{Ctrl-Alt-o} (IntelliJ).\n\\end{enumerate}\n\nThe IDEs automatically generate the new import statements. If there is\nmore than one available import package for a class, a wizard appears\nthat lets you select the correct import.\n\nGreat! You've updated your class imports!\n\n\\section{Class Moved to a Module Not in the\nClasspath}\\label{class-moved-to-a-module-not-in-the-classpath}\n\nYou must resolve the new module as a dependency for your project. This\nrequires identifying the module and specifying your project's dependency\non it.\n\nBefore Liferay DXP 7.0, all the platform APIs were in\n\\texttt{portal-service.jar}. Many of these APIs are now in independent\nmodules. Modularization has resulted in many benefits, as described in\nthe article\n\\href{/docs/7-2/customization/-/knowledge_base/c/the-benefits-of-modularity}{The\nBenefits of Modularity}. One such advantage is that these API modules\ncan evolve separately from the platform kernel. They also simplify\nfuture upgrades. For example, instead of having to check all of\nLiferay's APIs, each module's \\href{http://semver.org}{Semantic\nVersioning} indicates whether the module contains any\nbackwards-incompatible changes. You need only adapt your code to such\nmodules (if any).\n\nAs part of the modularization, \\texttt{portal-service.jar} has been\nrenamed appropriately to \\texttt{portal-kernel.jar}, as it continues to\nhold the portal kernel's APIs.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/from-liferay-6-portal-apis-before-after.png}\n\\caption{Liferay refactored the portal-service JAR for 7.0. Application\nAPIs now exist in their own modules, and the portal-service JAR is now\n\\emph{portal-kernel}.}\n\\end{figure}\n\nEach app module consists of a set of classes that are highly cohesive\nand have a specific purpose, such as providing the app's API,\nimplementation, or UI. The app modules are therefore much easier to\nunderstand. Next, you'll track down the modules that now hold the\nclasses referenced by your plugin.\n\nThe reference article\n\\href{/docs/7-2/reference/-/knowledge_base/r/classes-moved-from-portal-service-jar}{Classes\nMoved from \\texttt{portal-service.jar}} contains a table that maps each\nclass moved from \\texttt{portal-service.jar} to its new module in\nLiferay DXP 7.1. The table includes each class's new package and\nsymbolic name (artifact ID). You'll use this information to configure\nyour plugin's dependencies on these modules.\n\nFor more information on finding and resolving your project dependencies,\nsee\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{Configuring\nDependencies}.\n\n\\section{Class Replaced or Removed}\\label{class-replaced-or-removed}\n\nIn cases where the class has been replaced by another class or removed\nfrom the product, some investigation is required. The easiest way to\nresolve this type of issue is to use the Upgrade Planner. It finds\nremoved classes your project is referencing and explains what happened\nto the class, how to handle the change, and why the change was made.\nThese are listed as breaking changes (among other types of changes).\nMove on to the next section to learn about Liferay's breaking changes.\n\n\\chapter{Resolving Breaking Changes}\\label{resolving-breaking-changes}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Fixing Upgrade Problems</p><p>Step 2 of 2</p>\n\\end{verbatim}\n\nLiferay goes to great lengths to maintain backwards compatibility.\nSometimes, breaking changes are necessary to improve Liferay DXP. There\nmay be cases where breaking changes affect your code upgrade process and\nmust be resolved. A breaking change can include\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Functionality that is removed or replaced\n\\item\n  API incompatibilities: Changes to public Java or JavaScript APIs\n\\item\n  Changes to context variables available to templates\n\\item\n  Changes in CSS classes available to Liferay themes and portlets\n\\item\n  Configuration changes: Changes in configuration files, like\n  \\texttt{portal.properties}, \\texttt{system.properties}, etc.\n\\item\n  Execution requirements: Java version, J2EE Version, browser versions,\n  etc.\n\\item\n  Deprecations or end of support: For example, warning that a certain\n  feature or API will be dropped in an upcoming version.\n\\item\n  Recommendations: For example, recommending using a newly introduced\n  API that replaces an old API, in spite of the old API being kept in\n  Liferay Portal for backwards compatibility.\n\\end{itemize}\n\nLiferay provides a list of breaking changes for every major release to\nensure you can easily adapt your code during the upgrade process.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-0/reference/-/knowledge_base/r/breaking-changes}{Liferay\n  DXP 7.0 Breaking Changes}\n\\item\n  \\href{/docs/7-1/reference/-/knowledge_base/r/breaking-changes}{Liferay\n  DXP 7.1 Breaking Changes}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/breaking-changes}{7.0\n  Breaking Changes}\n\\end{itemize}\n\nThe easiest way to resolve breaking changes is by using the\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-upgrade-planner}{Liferay\nUpgrade Planner}. It automatically finds all documented breaking changes\nand can automatically resolve some of them on its own.\n\nIf you're resolving breaking changes manually, make sure to investigate\neach breaking change document if you're upgrading code across multiple\nversions. For example, if you're upgrading from Liferay Portal 6.2 to\n7.0, you must resolve all the breaking changes listed in the three\ndocuments listed above.\n\nNow that you've resolved your breaking changes, you'll learn how to\nupgrade service builder services next.\n\n\\chapter{Upgrading Service Builder\nServices}\\label{upgrading-service-builder-services}\n\nTo properly upgrade app's leveraging service builder, you must complete\nthe following steps:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Remove Legacy Files\n\\item\n  Migrate from Spring DI to OSGi Declarative Services\n\\item\n  Rebuild Services\n\\end{itemize}\n\nYou'll start by removing legacy files.\n\nLet's Go!{}\n\n\\chapter{Removing Legacy Files}\\label{removing-legacy-files}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Service Builder Services</p><p>Step 1 of 3</p>\n\\end{verbatim}\n\nThe first step in upgrading your Service Builder services is to delete\nlegacy files. These legacy files include\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{portlet-spring.xml}\n\\item\n  \\texttt{shard-data-source-spring.xml}\n\\item\n  \\texttt{/src/main/resources/META-INF/} (folder)\n\\end{itemize}\n\nWhen initiating this step, these files/folders are automatically removed\nfrom your Service Builder project.\n\nIf you're manually upgrading your code, delete the listed files/folders\nabove.\n\nNext, you'll convert your Service Builder Module from Spring DI to OSGi\nDS.\n\n\\chapter{Converting a Service Builder Module from Spring DI to OSGi\nDS}\\label{converting-a-service-builder-module-from-spring-di-to-osgi-ds}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Service Builder Services</p><p>Step 2 of 3</p>\n\\end{verbatim}\n\nPrior to 7.0, Service Builder modules could only use Spring for\ndependency injection (DI). Now\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{OSGi\nDeclarative Services} (DS) is the default DI mechanism for new Service\nBuilder modules. Although OSGi DS is the default DI mechanism, Spring is\nstill supported. Therefore, this is an optional migration step.\n\nTo learn more about the decision to convert your Service Builder\nmodules' DI mechanism and how to complete the conversion process, see\nthe\n\\href{/docs/7-2/appdev/-/knowledge_base/a/migrating-a-service-builder-module-from-spring-di-to-osgi-ds}{Migrating\na Service Builder Module from Spring DI to OSGi DS} article.\n\n\\chapter{Rebuilding Services}\\label{rebuilding-services}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Service Builder Services</p><p>Step 3 of 3</p>\n\\end{verbatim}\n\nTo properly upgrade Service Builder projects, you must rebuild all\nservice classes so your changes are persisted across your project.\nInitiate this step to rebuild your services and finalize your Service\nBuilder upgrade.\n\nGreat! Your Service Builder services are upgraded!\n\n\\chapter{Upgrading Customization\nPlugins}\\label{upgrading-customization-plugins}\n\nLiferay DXP has more extension points than ever, and connecting existing\nhook plugins to them takes very few steps. In most cases, after you\nupgrade your hook using the Liferay Upgrade Planner, it's ready to run\non Liferay DXP. The following tutorials show you how to upgrade each\ntype of hook plugin.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-customization-modules}{Override/Extension\n  Modules}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-core-jsp-hooks}{Core\n  JSP Hooks}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portlet-jsp-hooks}{Portlet\n  JSP Hooks}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-service-wrapper-hooks}{Service\n  Wrapper Hooks}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-core-language-key-hooks}{Core\n  Language Key Hooks}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portlet-language-key-hooks}{Portlet\n  Language Key Hooks}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-model-listener-hooks}{Model\n  Listener Hooks}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-event-action-hooks}{Event\n  Actions Hooks}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-servlet-filter-hooks}{Servlet\n  Filter Hooks}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portal-property-hooks}{Portal\n  Properties Hooks}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-struts-action-hooks}{Struts\n  Action Hooks}\n\\end{itemize}\n\nContinue on to get started!\n\nLet's Go!{}\n\n\\chapter{Upgrading Customization\nModules}\\label{upgrading-customization-modules}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 1 of 11</p>\n\\end{verbatim}\n\nCustomization modules include any module extension or override used to\ncustomize another module. For examples of these types of modules, visit\nthe\n\\href{https://github.com/liferay/liferay-blade-samples/tree/master/liferay-workspace/extensions}{\\texttt{extensions}}\nand\n\\href{https://github.com/liferay/liferay-blade-samples/tree/master/liferay-workspace/overrides}{\\texttt{overrides}}\nsample projects.\n\nGetting a customization module running on 7.0 takes two steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt your code to 7.0's API using the Liferay Upgrade Planner. When\n  you ran the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues were autocorrected or flagged. For any remaining\n  errors, consult the\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolving\n  a Project's Dependencies} article.\n\\item\n  Deploy your module.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} A fragment was a common customization module in past\nversions of Liferay DXP. Fragments are no longer recommended; you should\nupgrade a fragment to a dynamic include or portlet filter. For more\ninformation on recommended ways of customizing JSPs in 7.0, see the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps}{Customizing\nJSPs} section.\n\n\\noindent\\hrulefill\n\nGreat! Your customization module is upgraded for 7.0!\n\n\\chapter{Upgrading Core JSP Hooks}\\label{upgrading-core-jsp-hooks}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 2 of 11</p>\n\\end{verbatim}\n\nGetting a core JSP hook running on 7.0 takes two steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt your code to 7.0's API using the Liferay Upgrade Planner. When\n  you ran the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues were autocorrected/flagged. For any remaining errors,\n  consult the\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolving\n  a Project's Dependencies} article.\n\\item\n  Deploy your hook plugin.\n\\end{enumerate}\n\nLiferay DXP's Plugin Compatibility Layer converts the plugin WAR to a\nWeb Application Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nAlthough you can upgrade your core JSP hook to 7.0, there are better\nways to override a core JSP. The two recommended approaches are\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Dynamic includes\n\\item\n  Portlet filters\n\\end{itemize}\n\nFor more information on recommended ways of customizing JSPs in 7.0, see\nthe\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps}{Customizing\nJSPs} section.\n\n\\chapter{Upgrading Portlet JSP Hooks}\\label{upgrading-portlet-jsp-hooks}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 3 of 11</p>\n\\end{verbatim}\n\nGetting a portlet JSP hook running on 7.0 takes two steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt your code to 7.0's API using the Liferay Upgrade Planner. When\n  you ran the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues were autocorrected/flagged. For any remaining errors,\n  consult the\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolving\n  a Project's Dependencies} article.\n\\item\n  Deploy your hook plugin.\n\\end{enumerate}\n\nLiferay DXP's Plugin Compatibility Layer converts the plugin WAR to a\nWeb Application Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nAlthough you can upgrade your portlet JSP hook to 7.0, there are better\nways to override a portlet JSP. The two recommended approaches are\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Dynamic includes\n\\item\n  Portlet filters\n\\end{itemize}\n\nFor more information on recommended ways of customizing JSPs in 7.0, see\nthe\n\\href{/docs/7-2/customization/-/knowledge_base/c/customizing-jsps}{Customizing\nJSPs} section.\n\n\\chapter{Upgrading Service Wrapper\nHooks}\\label{upgrading-service-wrapper-hooks}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 4 of 11</p> \n\\end{verbatim}\n\nUpgrading traditional\n\\href{/docs/6-2/tutorials/-/knowledge_base/t/overriding-a-portal-service-using-a-hook}{service\nwrapper hook plugins} to 7.0 is quick and easy.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt your code to 7.0's API using the Liferay Upgrade Planner. When\n  you ran the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues were autocorrected/flagged. For any remaining errors,\n  consult the\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolving\n  a Project's Dependencies} article.\n\\item\n  Deploy the plugin.\n\\end{enumerate}\n\nLiferay DXP's Plugin Compatibility Layer converts the plugin WAR to a\nWeb Application Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nYour service wrapper hook is now available in Liferay DXP.\n\n\\chapter{Upgrading Core Language Key\nHooks}\\label{upgrading-core-language-key-hooks}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 5 of 11</p>\n\\end{verbatim}\n\nHere are the steps for upgrading a core language key hook to 7.0.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new module based on the Blade sample \\texttt{resource-bundle}\n  project\n  (\\href{https://github.com/liferay/liferay-blade-samples/tree/master/gradle/extensions/resource-bundle}{Gradle}\n  or\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/master/maven/extensions/resource-bundle}{Maven}).\n\n  Here are the main parts of the module folder structure:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{src/main/java/{[}resource\\ bundle\\ path{]}} → Custom\n    resource bundle class goes here\n  \\item\n    \\texttt{src/main/resources/content}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{Language.properties}\n    \\item\n      \\texttt{Language\\_xx.properties}\n    \\item\n      \\ldots{}\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  Copy all your plugin's language properties files into the module\n  folder \\texttt{src/main/resources/content/}.\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-global-language-keys\\#create-a-resource-bundle-service-component}{Create\n  a resource bundle loader}.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  your module}.\n\\end{enumerate}\n\nYour core language key customizations are deployed to 7.0.\n\n\\chapter{Upgrading Portlet Language Key\nHooks}\\label{upgrading-portlet-language-key-hooks}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 6 of 11</p>\n\\end{verbatim}\n\nYou can upgrade your portlet language key hooks to 7.0 by following\nthese steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new module based on the Blade sample \\texttt{resource-bundle}\n  project\n  (\\href{https://github.com/liferay/liferay-blade-samples/tree/master/gradle/extensions/resource-bundle}{Gradle}\n  or\n  \\href{https://github.com/liferay/liferay-blade-samples/tree/master/maven/extensions/resource-bundle}{Maven}).\n\n  Here are the module folder structure's main files:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{src/main/java/{[}resource\\ bundle\\ path{]}} →\n    \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/}{\\texttt{ResourceBundleLoader}\n    extension} goes here\n  \\item\n    \\texttt{src/main/resources/content}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{Language.properties}\n    \\item\n      \\texttt{Language\\_xx.properties}\n    \\item\n      \\ldots{}\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  Copy your language properties files into module folder\n  \\texttt{src/main/resources/content/}.\n\\item\n  In your \\texttt{bnd.bnd} file,\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys}{specify\n  OSGi manifest headers} that target the portlet module's resource\n  bundle, but prioritize yours.\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  your module}.\n\\end{enumerate}\n\nYour portlet language key customizations are deployed in your new module\non 7.0.\n\n\\chapter{Upgrading Model Listener\nHooks}\\label{upgrading-model-listener-hooks}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 7 of 11</p>\n\\end{verbatim}\n\nDevelopers have been creating model listeners for several Liferay DXP\nversions. Upgrading model listener Hooks from previous portal versions\nhas never been easier.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt your code to 7.0's API using the Liferay Upgrade Planner. When\n  you ran the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues were autocorrected/flagged. For any remaining errors,\n  consult the\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolving\n  a Project's Dependencies} article.\n\\item\n  Deploy your hook plugin.\n\\end{enumerate}\n\nLiferay DXP's Plugin Compatibility Layer converts the plugin WAR to a\nWeb Application Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nYour model listener hook is now available in Liferay DXP.\n\n\\chapter{Upgrading Event Action\nHooks}\\label{upgrading-event-action-hooks}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 8 of 11</p>\n\\end{verbatim}\n\nEvent action hooks can be upgraded by completing these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt your code to 7.0's API using the Liferay Upgrade Planner. When\n  you ran the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues were autocorrected/flagged. For any remaining errors,\n  consult the\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolving\n  a Project's Dependencies} article.\n\\item\n  Deploy your hook plugin.\n\\end{enumerate}\n\nLiferay DXP's Plugin Compatibility Layer converts the plugin WAR to a\nWeb Application Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nYour event action hook is now available in Liferay DXP.\n\n\\chapter{Upgrading Servlet Filter\nHooks}\\label{upgrading-servlet-filter-hooks}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 9 of 11</p>\n\\end{verbatim}\n\nIf you have servlet filter hooks ready to be upgraded, this tutorial's\nfor you. The process is simple:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt your code to 7.0's API using the Liferay Upgrade Planner. When\n  you ran the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues were autocorrected/flagged. For any remaining errors,\n  consult the\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolving\n  a Project's Dependencies} article.\n\\item\n  Deploy your hook plugin.\n\\end{enumerate}\n\nLiferay DXP's Plugin Compatibility Layer converts the plugin WAR to a\nWeb Application Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nYour Servlet Filter is running on 7.0!\n\n\\chapter{Upgrading Portal Property\nHooks}\\label{upgrading-portal-property-hooks}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 10 of 11</p>\n\\end{verbatim}\n\nAll portal properties in previous Liferay DXP versions that are also\nused in 7.0 can be overridden using portal property hooks. To upgrade\nportal property hooks, do this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt your code to 7.0's API using the Liferay Upgrade Planner. When\n  you ran the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues were autocorrected/flagged. For any remaining errors,\n  consult the\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolving\n  a Project's Dependencies} article.\n\\item\n  Deploy your hook plugin.\n\\end{enumerate}\n\nLiferay DXP's Plugin Compatibility Layer converts the plugin WAR to a\nWeb Application Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nYour custom property values are live!\n\n\\chapter{Upgrading Struts Action\nHooks}\\label{upgrading-struts-action-hooks}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 11 of 11</p>\n\\end{verbatim}\n\nIn Liferay Portal 6.1 and 6.2, developers could customize the Portal and\nPortlet Struts Actions using a Hook and \\texttt{StrutsAction} wrapper.\nFor example, the \\texttt{liferay-hook.xml} file for a hook that overrode\nthe login portlet's login action had this entry:\n\n\\begin{verbatim}\n<struts-action>\n    <struts-action-path>/login/login</struts-action-path>\n    <struts-action-impl>\n        com.liferay.sample.hook.action.ExampleStrutsPortletAction\n    </struts-action-impl>\n</struts-action>\n\\end{verbatim}\n\nThe \\texttt{liferay-hook.xml} contains the Struts mapping and the new\nclass that overrides the default login action.\n\nThe wrapper could extend either \\texttt{BaseStrutsAction} or\n\\texttt{BaseStrutsPortletAction}, depending on whether the Struts Action\nwas a portal or portlet action, respectively.\n\nSince Liferay DXP 7.0, this mechanism no longer applies for most\nportlets because they no longer use Struts Actions, but instead use\nLiferay \\texttt{MVCCommand}s.\n\nThis tutorial demonstrates how to convert your existing\n\\texttt{StrutsAction} wrappers to \\texttt{MVCCommand}s.\n\n\\section{\\texorpdfstring{Converting Your Old Wrapper to\n\\texttt{MVCCommand}s}{Converting Your Old Wrapper to MVCCommands}}\\label{converting-your-old-wrapper-to-mvccommands}\n\nConverting \\texttt{StrutsAction} wrappers to \\texttt{MVCCommand}s is\neasier than you may think.\n\nAs a review, legacy \\texttt{StrutsAction} wrappers implemented all\nmethods, such as \\texttt{processAction}, \\texttt{render}, and\n\\texttt{serveResource}, even if only one method was being customized.\nEach of these methods can now be customized independently using\ndifferent classes, making the logic simpler and easier to maintain.\nDepending on the method you customized in your \\texttt{StrutsAction}\nwrapper, you need to use the matching\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCCommand.html}{\\texttt{MVCCommand}\ninterface} shown below:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{processAction} →\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCActionCommand.html}{\\texttt{MVCActionCommand}}\n\\item\n  \\texttt{render} →\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html}{\\texttt{MVCRenderCommand}}\n\\item\n  \\texttt{serveResource} →\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html}{\\texttt{MVCResourceCommand}}\n\\end{itemize}\n\nLook at the\n\\href{/docs/6-2/tutorials/-/knowledge_base/t/overriding-and-adding-struts-actions}{\\texttt{ExampleStrutsPortletAction}\nclass} for a \\texttt{StrutsAction} wrapper example. Depending on the\nactions overridden, the user must use different \\texttt{MVCCommand}s. In\nthis example, the action and render were overridden, so to migrate to\nthe new pattern, you would create two classes: an\n\\texttt{MVCActionCommand} and \\texttt{MVCRenderCommand}.\n\nNext you'll determine the mapping the \\texttt{MVCCommand} uses.\n\n\\section{\\texorpdfstring{Mapping Your \\texttt{MVCCommand}\nURLs}{Mapping Your MVCCommand URLs}}\\label{mapping-your-mvccommand-urls}\n\nFor most cases, the \\texttt{MVCCommand} mapping is the same mapping\ndefined in the legacy Struts Action.\n\nUsing the beginning login example once again, the\n\\texttt{struts-action-path} mapping, \\texttt{/login/login}, remains the\nsame for the \\texttt{MVCCommand} mapping in 7.0, but some of the\nmappings may have changed. It's best to check Liferay DXP's source code\nto determine the correct mapping.\n\nMap to your \\texttt{MVCCommand} URLs using portlet URL tags:\n\n\\begin{itemize}\n\\item\n  \\texttt{MVCRenderCommand} URLs go in \\texttt{mvcRenderCommandName}\n  parameters. For example,\n\n\\begin{verbatim}\n<portlet:renderURL var=\"editEntryURL\">\n    <portlet:param name=\"mvcRenderCommandName\" value=\"/hello/edit_entry\" />\n    <portlet:param name=\"entryId\" value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n</portlet:renderURL>\n\\end{verbatim}\n\\item\n  \\texttt{MVCActionCommand} URLs go in \\texttt{actionURL} tag\n  \\texttt{name} attributes or in a parameter\n  \\texttt{ActionRequest.ACTION\\_NAME}. For example,\n\n\\begin{verbatim}\n<portlet:actionURL name=\"/blogs/edit_entry\" var=\"editEntryURL\" />\n\\end{verbatim}\n\\item\n  \\texttt{MVCResourceCommand} URLs go in \\texttt{resourceURL} tag\n  \\texttt{id} attributes. For example,\n\n\\begin{verbatim}\n<portlet:resourceURL id=\"/login/captcha\" var=\"captchaURL\" />\n\\end{verbatim}\n\\end{itemize}\n\nOnce you have this information, you can override the \\texttt{MVCCommand}\nby following the instructions found in these \\texttt{MVCCommand}\narticles:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands}{Adding\n  Logic to MVCCommands}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-mvcrendercommand}{Overriding\n  MVCRenderCommands}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-mvcactioncommand}{Overriding\n  MVCActionCommands}\n\\item\n  \\href{/docs/7-2/customization/-/knowledge_base/c/overriding-mvcresourcecommand}{Overriding\n  MVCResourceCommands}\n\\end{itemize}\n\nNow you know how to convert your \\texttt{StrutsActionWrapper}s to\n\\texttt{MVCCommand}s!\n\n\\chapter{Upgrading a Theme to 7.2}\\label{upgrading-a-theme-to-7.2}\n\nIn these tutorials, you'll learn how to upgrade your themes from earlier\nversions of Liferay DXP to 7.0. As you go through this process, you'll\nlearn how to upgrade your theme's metadata, styling, templates, UI, and\nmore using all the best practices and standards. By the end of the\ntutorial, you'll have a theme that runs on 7.0.\n\nTo upgrade your theme, select the tutorial below that corresponds to the\ncurrent version of your theme:\n\nLet's Go 6.2!{}\n\nLet's Go 7.0!{}\n\nLet's Go 7.1!{}\n\n\\chapter{Upgrading Your Theme from Liferay Portal 6.2 to\n7.2}\\label{upgrading-your-theme-from-liferay-portal-6.2-to-7.2}\n\nIn this tutorial, you'll upgrade the Lunar Resort theme developed in the\nLiferay Portal 6.2\n\\href{/docs/6-2/tutorials/-/knowledge_base/t/developing-a-liferay-theme}{Developing\na Liferay Theme} Learning Path to 7.0 using the\n\\href{https://github.com/liferay/liferay-js-themes-toolkit/tree/master/packages}{Liferay\nJS Theme Toolkit}. The Lunar Resort theme is similar to many Liferay\nPortal 6.2 themes, as it extends the\n\\href{https://github.com/liferay/liferay-portal/tree/6.2.x/portal-web/docroot/html/themes/_styled}{\\texttt{\\_styled}\ntheme}, adds configurable settings, and incorporates a responsive design\nthat leverages Font Awesome icons and Bootstrap. The theme\n\\href{https://github.com/liferay/liferay-docs/blob/7.0.x/develop/tutorials/code/upgrading-themes/lunar-resort-theme-migration-6.2.zip}{ZIP\nfile} contains its original source code.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/finished-7-2-theme.png}\n\\caption{The Lunar Resort example theme upgraded in this tutorial uses a\nclean, minimal design.}\n\\end{figure}\n\nAs you upgrade this theme, you'll learn how to update metadata, theme\ntemplates, UI, and more using all the best practices and standards.\nCompleting this tutorial prepares you for upgrading your own theme.\n\nLet's Go!{}\n\n\\chapter{Setting up the Development\nEnvironment}\\label{setting-up-the-development-environment}\n\nIn this section, you'll set up your development environment to use the\n\\href{https://github.com/liferay/liferay-js-themes-toolkit/tree/master/packages}{Liferay\nJS Theme Toolkit}. Setting up your development environment involves\nthese steps:\n\n\\begin{itemize}\n\\item\n  Install NodeJS with npm.\n\\item\n  Install Yeoman.\n\\item\n  Install the Liferay Theme Generator.\n\\item\n  Import the 6.2 theme to use the Liferay JS Theme Toolkit\n\\end{itemize}\n\nLet's Go{}\n\n\\chapter{Installing the Liferay Theme Generator to Import a 6.2\nTheme}\\label{installing-the-liferay-theme-generator-to-import-a-6.2-theme}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Setting up the Development Environment</p><p>Step 1 of 2</p>\n\\end{verbatim}\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Install \\href{http://nodejs.org/}{NodeJS} (along with Node Package\n  Manager(npm)) if it's not already installed. We recommend installing\n  the Long Term Support (LTS) version. Once NodeJS is installed,\n  \\href{/docs/7-2/reference/-/knowledge_base/r/setting-up-your-npm-environment}{set\n  up your npm environment}.\n\\item\n  Use npm to install the \\href{http://yeoman.io/}{Yeoman} dependency:\n\n\\begin{verbatim}\nnpm install -g yo\n\\end{verbatim}\n\\item\n  Install the Liferay Theme Generator v8.x.x with the command below:\n\n\\begin{verbatim}\nnpm install -g generator-liferay-theme@8.x.x\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Liferay Theme Generator v8.x.x supports importing 6.2 themes to use \n the Liferay JS Theme Toolkit. Later on, you will install the latest version of \n the Liferay Theme Generator to complete the upgrade process.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nIf you're on Windows, follow the instructions in step 4 to install Sass, \notherwise you can skip that step.\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\tightlist\n\\item\n  The generator uses node-sass. If you're on Windows, you must also\n  install\n  \\href{https://github.com/nodejs/node-gyp\\#installation}{node-gyp and\n  Python}.\n\\end{enumerate}\n\nNice job! Your npm environment is set up and the Liferay Theme Generator\nand dependencies are installed. Next, you can import the Liferay DXP 6.2\ntheme to use the Liferay JS Theme Toolkit.\n\n\\chapter{Importing the Theme into the Liferay JS Theme\nToolkit}\\label{importing-the-theme-into-the-liferay-js-theme-toolkit}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Setting up the Development Environment</p><p>Step 2 of 2</p>\n\\end{verbatim}\n\nNow you'll import your theme to use the Liferay JS Theme Toolkit. The\nLiferay JS Theme Toolkit provides several useful Gulp tasks for\nautomating theme development and maintenance, including upgrading parts\nof the theme.\n\nFollow these steps to import your theme to use the Liferay JS Theme\nToolkit:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Run the command below to import your 6.2 theme to use the Liferay JS\n  Theme Toolkit:\n\n\\begin{verbatim}\nyo liferay-theme:import\n\\end{verbatim}\n\\item\n  Provide the path (relative or absolute) to the 6.2 theme project's\n  root folder and press the \\emph{Enter} key to import the theme.\n\\item\n  Answer the prompts to configure the location to your 7.0 app server.\n\\end{enumerate}\n\nCongratulations! Your development environment is set up to use the\nLiferay JS Theme Toolkit. Next, you'll start upgrading the theme.\n\n\\chapter{Running the Upgrade Task for 6.2\nThemes}\\label{running-the-upgrade-task-for-6.2-themes}\n\nYou can upgrade a Liferay Portal 6.2 theme to 7.0, regardless of the\ndevelopment environment you use. This tutorial uses the Liferay JS Theme\nToolkit's Gulp \\texttt{upgrade} task to automate much of the steps.\nBecause the theme was built on Liferay DXP 6.2, the Gulp\n\\texttt{upgrade} task must be run three times to bring it up to 7.0.\n\nThe Liferay Theme Generator is available in a few different versions. To\nupdate the Liferay DXP 6.2 theme to Liferay DXP 7.0, you must install\nv8.x.x of the \\texttt{liferay-theme-tasks} dependency. After the theme\nis updated to 7.1, you must then install v9.x.x of the\n\\texttt{liferay-theme-tasks} dependency to complete the upgrade process.\n\nHere's what the Upgrade Task does:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Updates the theme's Liferay version\n\\item\n  Updates the theme's Bootstrap version\n\\item\n  Updates the theme's Lexicon version\n\\item\n  Updates CSS file names\n\\item\n  Updates theme dependencies\n\\item\n  Suggests specific code updates\n\\end{itemize}\n\nFollow these steps to take the theme through the upgrade process:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the theme's root directory and run the command below to\n  update the theme's \\texttt{liferay-theme-tasks} dependency to version\n  \\texttt{8.x.x}:\n\n\\begin{verbatim}\nnpm install --save-dev liferay-theme-tasks@8.x.x\n\\end{verbatim}\n\\item\n  Run the command below to initially upgrade it from 6.2 to 7.0.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note**: The Upgrade task overwrites the theme's files. We recommend that \n you backup your files before proceeding with the upgrade process.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n```bash\ngulp upgrade\n```\n\nHere's what the 6.2 to 7.0 upgrade task does:\n\n- Updates the theme's Liferay version\n- Renames CSS files\n- Suggests specific code updates\n\nThe task continues upgrading CSS files, prompting you to update CSS file \nnames. For 7.0, Sass files should use the `.scss` extension, and \nfile names for Sass partials should start with an underscore (e.g., \n`_custom.scss`). The `upgrade` task prompts you for each CSS file to rename. \n\nThe upgrade task automatically upgrades CSS code that it can identify. For \neverything else, it suggests upgrades. \n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Run the \\texttt{gulp\\ upgrade} command again to upgrade the 7.0 theme\n  to 7.1.\n\n  Here's what it does:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Creates core code for generating theme base files\n  \\item\n    Collects removed Bootstrap and Lexicon variables\n  \\item\n    Updates Bootstrap version references\n  \\item\n    Updates Lexicon version references\n  \\item\n    Updates Liferay version references\n  \\end{itemize}\n\\item\n  You must update the theme's \\texttt{liferay-theme-tasks} dependency to\n  version \\texttt{9.x.x} to complete the upgrade process. Install the\n  latest version of the Liferay Theme Generator as well while you're at\n  it, so future uses of the tool will be compatible with the 7.0 theme.\n  Both commands are shown below. Run them separately:\n\n\\begin{verbatim}\nnpm install --save-dev liferay-theme-tasks@9.x.x\n\nnpm install -g generator-liferay-theme@9.x.x\n\\end{verbatim}\n\\item\n  With the \\texttt{9.x.x} versions of the \\texttt{liferay-theme-tasks}\n  and Liferay Theme Generator installed, run the \\texttt{gulp\\ upgrade}\n  command for the final time to upgrade the 7.1 theme to 7.2:\n\n  Here's what it does:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Updates Liferay version references\n  \\item\n    Updates theme dependencies\n  \\end{itemize}\n\\end{enumerate}\n\nThe Gulp \\texttt{upgrade} task lists any deprecated or removed\nvariables. For other areas of the code it suspects might need updates,\nit logs suggestions. The task also reports changes that may affect theme\ntemplates.\n\nThe Gulp \\texttt{upgrade} task jump-starts the upgrade process, but it\ndoesn't complete it. Manual updates are required. The remaining portion\nof this tutorial covers these manual steps.\n\n\\chapter{Updating 6.2 CSS Code}\\label{updating-6.2-css-code}\n\n7.0's UI improvements require these CSS-related changes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Updating rules and imports\n\\item\n  Modifying responsiveness tokens\n\\end{itemize}\n\nThe theme upgrade process involves conforming to these changes.\n\nLet's Go{}\n\n\\chapter{Updating 6.2 CSS Rules and\nImports}\\label{updating-6.2-css-rules-and-imports}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating 6.2 CSS Code<p><p>Step 1 of 2</p>\n\\end{verbatim}\n\n7.0 uses Bootstrap 4.3's CSS rule syntax. Font Awesome icons have been\nremoved from base themes, so you should remove those stale imports if\nyou have them. The Gulp \\texttt{upgrade} task reports automatic CSS\nupdates and suggests manual updates. For example, here is part of the\ntask log for the Lunar Resort theme upgrade from 6.2 to 7.0. For each\nupdate performed and suggested, the task reports a file name and line\nnumber range:\n\n\\begin{verbatim}\n Bootstrap Upgrade (2 to 3)\n\nBecause Liferay Portal 7.0 uses Bootstrap 3, the default box model has been \nchanged to box-sizing: border-box. So if you were using width or height, and \npadding together on an element, you may need to make changes, or those elements \nmay have unexpected sizes.\n\nFile: src/css/_aui_variables.scss\n    Line 5: \"$white\" has been removed\n    Line 31: \"$white\" has been removed\nFile: src/css/_custom.scss\n    Line 201: Padding no longer affects width or height, you may need to change \n    your rule (lines 201-227)\n    Line 207: Padding no longer affects width or height, you may need to change \n    your rule (lines 207-226)\n    Line 212: You would change height from \"62px\" to \"82px\"\n    Line 305: Padding no longer affects width or height, you may need to change \n    your rule (lines 305-314)\n    Line 308: You would change height from \"39px\" to \"46px\"\n    Line 409: Padding no longer affects width or height, you may need to change \n    your rule (lines 409-418)\n\\end{verbatim}\n\nFollow these steps to update your theme's Bootstrap rules and Font\nAwesome imports:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Since Bootstrap 3 adopted the \\texttt{box-sizing:\\ border-box}\n  property for all elements and pseudo-elements (e.g., \\texttt{:before}\n  and \\texttt{:after}), padding no longer affects dimensions.\n  \\href{https://getbootstrap.com/docs/3.3/css/\\#less-mixins-box-sizing}{Bootstrap's\n  documentation} describes the box sizing changes. Update the width and\n  height for all CSS rules that use padding. For example, examine the\n  \\texttt{height} value change in this CSS rule for the Lunar Resort\n  theme's \\texttt{\\_custom.scss} file:\n\n  Original:\n\n\\begin{verbatim}\n#reserveBtn {\n    background-color: #00C4FB;\n    border-radius: 10px;\n    color: #FFF;\n    font-size: 1.5em;\n    height: 62px;\n    margin: 30px;\n    padding: 10px 0;\n    ...\n}\n\\end{verbatim}\n\n  Updated:\n\n\\begin{verbatim}\n#reserveBtn {\n    background-color: #00C4FB;\n    border-radius: 10px;\n    color: #FFF;\n    font-size: 1.5em;\n    height: 82px;\n    margin: 30px;\n    padding: 10px 0;\n    ...\n}\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** For individual elements, you can overwrite the \n `box-sizing:border-box` rule with `box-sizing:content-box`. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  The following variables are removed in Bootstrap 4. Remove these\n  variables where they are used in the theme:\n\n\\begin{verbatim}\n$line-height-computed\n$padding-base-horizontal\n$padding-base-vertical\n$padding-large-horizontal\n$padding-large-vertical\n$padding-small-horizontal\n$padding-small-vertical\n$padding-xs-horizontal\n$padding-xs-vertical\n$gray-base\n$gray-darker\n$gray-dark\n$gray\n$gray-light\n$gray-lighter\n$brand-primary\n$brand-success\n$brand-info\n$brand-warning\n$brand-danger\n$state-success-text\n$state-success-bg\n$state-success-border\n$state-info-text\n$state-info-bg\n$state-info-border\n$state-warning-text\n$state-warning-bg\n$state-warning-border\n$state-danger-text\n$state-danger-bg\n$state-danger-border\n\\end{verbatim}\n\n  See the\n  \\href{http://getbootstrap.com/migration/\\#migrating-from-2x-to-30}{Migrating\n  from 2.x to 3.0 guide} for CSS rules that changed in Bootstrap 3.\n  Likewise, you can refer to the\n  \\href{https://getbootstrap.com/docs/4.3/migration/}{Migrating to v4\n  guide} for updating CSS rules to Bootstrap 4.\n\\item\n  Font Awesome icons were removed from the theme and Font are now\n  included as a package dependency if you answer yes (y) to include Font\n  Awesome during the upgrade task. If you included the old imports in\n  \\texttt{\\_custom.scss}, they must be removed:\n\n\\begin{verbatim}\n@import \"aui/alloy-font-awesome/scss/mixins-alloy\";\n@import \"aui/alloy-font-awesome/scss/variables\";\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! The rules and imports are updated. You can update the\nresponsiveness next.\n\n\\chapter{Updating the Responsiveness}\\label{updating-the-responsiveness}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating 6.2 CSS Code</p><p>Step 2 of 2</p>\n\\end{verbatim}\n\nBootstrap 4 explicit media queries replaced the Bootstrap 2\n\\texttt{respond-to} mixins for CSS responsiveness. Follow these steps to\nupdate the theme's responsiveness:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{\\_custom.scss}.\n\\item\n  Replace all \\texttt{respond-to} mixins with corresponding media\n  queries shown below. Note that some of the dimensions have slightly\n  changed:\n\n  \\textbf{Media Query Replacements}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n Liferay Portal 6.2 Mixin                            |  &nbsp;7.0 Media Query                                     |\n\\end{verbatim}\n\n\\noindent\\hrulefill --------------------------------------\n\\textbar:----------------------------------------------------------\n\\textbar{} \\texttt{@include\\ respond-to(phone)} (max-width: 767px)\n\\textbar{} \\texttt{@include\\ media-breakpoint-down(sm)} (max-width:\n767px) \\textbar{} \\texttt{@include\\ respond-to(tablet)} (min-width:\n768px, max-width: 979px) \\textbar{}\n\\texttt{@include\\ media-breakpoint-only(md)} (min-width: 768px,\nmax-width: 991px) \\textbar{}\n\\texttt{@include\\ respond-to(phone,\\ tablet)} (max-width: 979px)\n\\textbar{} \\texttt{@include\\ media-breakpoint-down(md)} (max-width:\n991px) \\textbar{} \\texttt{@include\\ respond-to(desktop,\\ tablet)}\n(min-width: 768px) \\textbar{} \\texttt{@include\\ media-breakpoint-up(md)}\n(min-width: 768px) \\textbar{} \\texttt{@include\\ respond-to(desktop)}\n(min-width: 980px) \\textbar{} \\texttt{@include\\ media-breakpoint-up(lg)}\n(min-width: 992px) \\textbar{}\n\n\\begin{verbatim}\nThe Lunar Resort theme's original and updated syntax is shown below:\n\nOriginal:\n\n```scss\n@include respond-to(phone, tablet) {\n    html #wrapper #banner #navigation {\n    ...\n    }\n}\n```\n\nUpdated:\n\n```scss\n@include media-breakpoint-down(md) {\n    html #wrapper #banner #navigation {\n    ...\n    }\n}\n```\n\\end{verbatim}\n\nThe CSS code is updated! Next you'll update the theme's templates.\n\n\\chapter{Updating 6.2 Theme\nTemplates}\\label{updating-6.2-theme-templates}\n\nLiferay DXP 6.2 theme templates and 7.0 theme templates are essentially\nthe same. Here are the main changes:\n\n\\begin{itemize}\n\\item\n  Velocity templates were deprecated in Liferay Portal CE 7.0 and are\n  now removed in favor of FreeMarker templates in Liferay DXP. Below are\n  the key reasons for this move:\n\n  \\begin{itemize}\n  \\item\n    FreeMarker is developed and maintained regularly, while Velocity is\n    no longer actively being developed.\n  \\item\n    FreeMarker is faster and supports more sophisticated macros.\n  \\item\n    FreeMarker supports using taglibs directly rather than requiring a\n    method to represent them. You can pass body content to them,\n    parameters, etc.\n  \\end{itemize}\n\\item\n  The Dockbar has been replaced and reorganized into a set of three\n  distinct menus:\n\n  \\begin{itemize}\n  \\item\n    \\emph{The Product Menu}: Manage Site and page navigation, content,\n    settings and pages for the current Site, and navigate to user\n    account settings, etc.\n  \\item\n    \\emph{The Control Menu}: Configure and add content to the page and\n    view the page in a simulation window.\n  \\item\n    \\emph{The User Personal Bar}: Display notifications and the user's\n    avatar and name.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/upgrading-themes-dockbar.png}\n  \\caption{The Dockbar was removed and must be replaced with the new\n  Control Menu.}\n  \\end{figure}\n\\end{itemize}\n\nStart by converting your Velocity theme templates to FreeMarker. You can\nrefer to Apache's\n\\href{https://freemarker.apache.org/docs/ref.html}{FreeMarker\ndocumentation} for help. Common Liferay DXP FreeMarker variables and\nmacros can be found in\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-template/portal-template-freemarker/src/main/resources/FTL_liferay.ftl}{\\texttt{FTL\\_liferay.ftl}}\n\nThe \\href{running-the-upgrade-task-for-6.2-themes}{Gulp \\texttt{upgrade}\ntask} reports the required theme template changes in the log. For\nexample, here are the 6.2 to 7.0 upgrade log and 7.0 to 7.1 upgrade logs\nfor the Lunar Resort theme:\n\n\\begin{verbatim}\n Liferay Upgrade (6.2 to 7)\n\nFile: portal_normal.ftl\n    Warning: <@liferay.dockbar /> is deprecated, replace with \n    <@liferay.control_menu /> for new admin controls.\n    Warning: not all admin controls will be visible without \n    <@liferay.control_menu />\n    Warning: ${theme} variable is no longer available in Freemarker \n    templates, see https://goo.gl/9fXzYt for more information.\n[18:57:23] Finished 'upgrade:log-changes' after 5.61 ms\n[18:57:23] Finished 'upgrade' after 19 s\n\n Liferay Upgrade (7.0 to 7.1)\n\nRenamed aui.scss to clay.scss\n[19:16:54] Finished 'upgrade:log-changes' after 2.53 ms\n[19:16:54] Finished 'upgrade' after 16 min\n\\end{verbatim}\n\nThe log warns about removed and deprecated code and suggests\nreplacements when applicable.\n\nIn this section you'll learn how to update various theme templates to\n7.0.\n\nLet's Go{}\n\n\\chapter{Updating 6.2 Portal Normal Theme\nTemplate}\\label{updating-6.2-portal-normal-theme-template}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating 6.2 Theme Templates</p><p>Step 1 of 3</p>\n\\end{verbatim}\n\nFollow these steps to update \\texttt{portal\\_normal.ftl}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{portal\\_normal.ftl} and replace the following 6.2\n  directives with the updated syntax. This change is described in the\n  \\href{/docs/7-0/reference/-/knowledge_base/r/breaking-changes\\#taglibs-are-no-longer-accessible-via-the-theme-variable-in-freemarker}{7.0\n  Breaking Changes} reference document:\n\n  \\begin{longtable}[]{@{}\n    >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.2209}}\n    >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.7791}}@{}}\n  \\toprule\\noalign{}\n  \\begin{minipage}[b]{\\linewidth}\\raggedright\n  6.2\n  \\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n  ~Updated\n  \\end{minipage} \\\\\n  \\midrule\\noalign{}\n  \\endhead\n  \\bottomrule\\noalign{}\n  \\endlastfoot\n  \\texttt{\\$\\{theme.include(top\\_head\\_include)\\}} &\n  \\texttt{\\textless{}@liferay\\_util{[}\"include\"{]}\\ page=top\\_head\\_include\\ /\\textgreater{}} \\\\\n  \\texttt{\\$\\{theme.include(body\\_top\\_include)\\}} &\n  \\texttt{\\textless{}@liferay\\_util{[}\"include\"{]}\\ page=body\\_top\\_include\\ /\\textgreater{}} \\\\\n  \\texttt{\\$\\{theme.include(content\\_include)\\}} &\n  \\texttt{\\textless{}@liferay\\_util{[}\"include\"{]}\\ page=content\\_include\\ /\\textgreater{}} \\\\\n  \\texttt{\\$\\{theme.wrapPortlet(\"portlet.ftl\",\\ content\\_include)\\}} &\n  \\texttt{\\textless{}@liferay\\_theme{[}\"wrap-portlet\"{]}\\ page=\"portlet.ftl\"\\textgreater{}\\ \\textless{}@liferay\\_util{[}\"include\"{]}\\ page=content\\_include\\ /\\textgreater{}\\ \\textless{}/@\\textgreater{}} \\\\\n  \\texttt{\\$\\{theme.include(body\\_bottom\\_include)\\}} &\n  \\texttt{\\textless{}@liferay\\_util{[}\"include\"{]}\\ page=body\\_bottom\\_include\\ /\\textgreater{}} \\\\\n  \\texttt{\\$\\{theme.include(bottom\\_include)\\}} &\n  \\texttt{\\textless{}@liferay\\_util{[}\"include\"{]}\\ page=bottom\\_include\\ /\\textgreater{}} \\\\\n  \\texttt{\\$\\{theme\\_settings{[}\"my-theme-setting\"{]}\\}} &\n  \\texttt{\\$\\{themeDisplay.getThemeSetting(\"my-theme-setting\")\\}} \\\\\n  \\texttt{\\$\\{theme.runtime(\"56\",\\ \"articleId=\"\\ +\\ my\\_article\\_id)\\}}\n  &\n  \\texttt{\\textless{}@liferay\\_portlet{[}\"runtime\"{]}\\ portletName=}\\texttt{\"com\\_liferay\\_journal\\_content\\_web\\_portlet\\_JournalContentPortlet\"}\n  \\texttt{queryString=\"articleId=\"\\ +\\ my\\_article\\_id\\ /\\textgreater{}} \\\\\n  \\end{longtable}\n\\item\n  Remove the breadcrumbs and page title code:\n\n\\begin{verbatim}\n<nav id=\"breadcrumbs\">      \n    <@liferay.breadcrumbs />        \n</nav>\n...\n<h2 class=\"page-title\">\n    <span>${the_title}</span>\n</h2>\n\\end{verbatim}\n\\item\n  Remove \\texttt{dockbar-split} from the \\texttt{body} element's\n  \\texttt{class} value so it matches the markup below:\n\n\\begin{verbatim}\n<body class=\"${css_class}\">\n\\end{verbatim}\n\\item\n  Find the\n  \\texttt{\\textless{}a\\ href=\"\\#main-content\"\\ id=\"skip-to-content\"\\textgreater{}\\textless{}@liferay.language\\ key=\"skip-to-content\"\\ /\\textgreater{}\\textless{}/a\\textgreater{}}\n  element and replace it with the updated Liferay UI quick access macro\n  shown below:\n\n\\begin{verbatim}\n<@liferay_ui[\"quick-access\"] contentId=\"#main-content\" />\n\\end{verbatim}\n\\item\n  Replace the \\texttt{\\textless{}@liferay.dockbar\\ /\\textgreater{}}\n  macro with the updated Control menu macro:\n\n\\begin{verbatim}\n<@liferay.control_menu />\n\\end{verbatim}\n\\item\n  Replace the \\texttt{\\textbar{}\\textbar{}\\ is\\_signed\\_in} condition\n  for the \\texttt{navigation.ftl} theme template include with\n  \\texttt{\\&\\&\\ is\\_setup\\_complete}:\n\n\\begin{verbatim}\n<#if has_navigation && is_setup_complete>\n    <#include \"${full_templates_path}/navigation.ftl\" />\n</#if>\n\\end{verbatim}\n\\item\n  Replace the \\texttt{content} \\texttt{\\textless{}div\\textgreater{}}\n  with an HTML 5 \\texttt{section} element. The \\texttt{section} element\n  is more accurate and provides better accessibility for screen readers:\n\n\\begin{verbatim}\n<section id=\"content\">\n\\end{verbatim}\n\\item\n  Add the\n  \\texttt{\\textless{}h1\\ class=\"hide-accessible\"\\textgreater{}\\$\\{the\\_title\\}\\textless{}/h1\\textgreater{}}\n  header element just inside the \\texttt{content}\n  \\texttt{\\textless{}section\\textgreater{}} to support accessibility,\n  and remove the breadcrumbs \\texttt{\\textless{}nav\\textgreater{}}\n  element from inside it.\n\\end{enumerate}\n\n\\texttt{portal\\_normal.ftl} is updated! Next you can update the\nnavigation template.\n\n\\chapter{Updating 6.2 Navigation Theme\nTemplate}\\label{updating-6.2-navigation-theme-template}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating 6.2 Theme Templates</p><p>Step 2 of 3</p>\n\\end{verbatim}\n\nFollow these steps to update \\texttt{navigation.ftl}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Below the\n  \\texttt{\\textless{}nav\\ class=\"\\$\\{nav\\_css\\_class\\}\"\\ id=\"navigation\"\\ role=\"navigation\"\\textgreater{}}\n  element, add the heading below to improve accessibility for screen\n  readers:\n\n\\begin{verbatim}\n<h1 class=\"hide-accessible\">\n    <@liferay.language key=\"navigation\" />\n</h1>\n\\end{verbatim}\n\\item\n  Remove the \\texttt{nav\\_item\\_attr\\_selected} variable declaration at\n  the top, and add the layout declaration shown below instead, to access\n  the layout. Don't forget to remove all uses of\n  \\texttt{nav\\_item\\_attr\\_selected} throughout the rest of the\n  template:\n\n\\begin{verbatim}\n<#assign nav_item_layout = nav_item.getLayout() />\n\\end{verbatim}\n\\item\n  Replace the \\texttt{\\$\\{nav\\_item.icon()\\}}variable in the\n  \\texttt{\\textless{}a\\ aria-labelledby=\"layout\\_\\$\\{nav\\_item.getLayoutId()\\}\"...\\textless{}/a\\textgreater{}}\n  anchor with the element below:\n\n\\begin{verbatim}\n<@liferay_theme[\"layout-icon\"] layout=nav_item_layout />\n\\end{verbatim}\n\\item\n  Remove the \\texttt{nav\\_child\\_attr\\_selected} variable from the\n  bottom of the template, including all uses throughout the rest of the\n  template.\n\\end{enumerate}\n\nThe navigation template is updated. You can update \\texttt{portlet.ftl}\nnext.\n\n\\chapter{Updating 6.2 Init Custom Theme\nTemplate}\\label{updating-6.2-init-custom-theme-template}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating 6.2 Theme Templates</p><p>Step 3 of 3</p>\n\\end{verbatim}\n\nThe Lunar Resort theme has a couple theme settings defined in\n\\texttt{init\\_custom.ftl}. The syntax has changed slightly in 7.0.\nFollow these steps to update the theme setting syntax:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Replace the \\texttt{getterUtil.getBoolean(theme\\_settings} method with\n  \\texttt{getterUtil.getBoolean(themeDisplay.getThemeSetting}:\n\n  Original:\n\n\\begin{verbatim}\n<#assign show_breadcrumbs = \ngetterUtil.getBoolean(theme_settings[\"show-breadcrumbs\"])/>\n\n<#assign show_page_title = \ngetterUtil.getBoolean(theme_settings[\"show-page-title\"])/>\n\\end{verbatim}\n\n  Updated:\n\n\\begin{verbatim}\n<#assign show_breadcrumbs = \ngetterUtil.getBoolean(themeDisplay.getThemeSetting(\"show-breadcrumbs\"))/>\n\n<#assign show_page_title = \ngetterUtil.getBoolean(themeDisplay.getThemeSetting(\"show-page-title\"))/>\n\\end{verbatim}\n\\item\n  Although the Lunar Resort theme doesn't have any String variables, you\n  would replace the \\texttt{getterUtil.getString(theme\\_settings} method\n  with \\texttt{themeDisplay.getThemeSetting}:\n\n  Original:\n\n\\begin{verbatim}\n<#assign string_setting = \ngetterUtil.getString(theme_settings[\"my-string-key\"])/>\n\\end{verbatim}\n\n  Updated:\n\n\\begin{verbatim}\n<#assign string_setting = \nthemeDisplay.getThemeSetting(\"my-string-key\")/>\n\\end{verbatim}\n\\end{enumerate}\n\nAwesome! The theme templates are updated. You can always compare theme\ntemplates with the updated ones found in the\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-unstyled/src/main/resources/META-INF/resources/_unstyled/templates}{\\texttt{\\_unstyled}\ntheme}, if you're unsure if something has changed. Refer to the\nsuggested changes that the Gulp \\texttt{upgrade} task reports for the\ntheme.\n\n\\chapter{Updating the Resources\nImporter}\\label{updating-the-resources-importer}\n\nThe Resources Importer is now an OSGi module bundled with Liferay DXP,\nso you don't have to download the Resources Importer separately. The\nfollowing components have been updated and are the focus of this\nsection:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Plugin properties\n\\item\n  Web content article files and folder structure\n\\item\n  Sitemap\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Due to the page and article import order, articles that\nlink to pages in the Site's layout cause a null pointer exception\n\\href{https://issues.liferay.com/browse/LPS-64859}{issue}. These links\nhave been removed from the Lunar Resort theme's web content articles to\navoid this issue.\n\n\\noindent\\hrulefill\n\nLet's Go{}\n\n\\chapter{Updating 6.2 Liferay Plugin Package\nProperties}\\label{updating-6.2-liferay-plugin-package-properties}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating 6.2 Resources Importer</p><p>Step 1 of 3</p>\n\\end{verbatim}\n\nSince the Lunar Resort theme was developed in the Plugins SDK, it\nrequires the updates covered in this section. Themes developed outside\nof the Plugins SDK do not require these changes.\n\nFollow these steps to update the Lunar Resort Theme's\n\\texttt{liferay-plugin-package.properties} file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the\n  \\texttt{src\\textbackslash{}WEB-INF\\textbackslash{}liferay-plugin-package.properties}\n  file and remove the \\texttt{required-deployment-contexts} property.\n  This is no longer needed since the Resources Importer is bundled with\n  Liferay DXP.\n\\item\n  The group model class's fully-qualified class name has changed.\n  Replace the \\texttt{resources-importer-target-class-name} property's\n  value with the updated one below:\n\\end{enumerate}\n\n\\begin{verbatim}\ncom.liferay.portal.kernel.model.Group\n\\end{verbatim}\n\nNow that the \\texttt{liferay-plugin-package.properties} is updated, you\ncan update the theme's web content.\n\n\\chapter{Updating 6.2 Web Content}\\label{updating-6.2-web-content}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating 6.2 Resources Importer</p><p>Step 2 of 3</p>\n\\end{verbatim}\n\nAll web content articles must be written in XML and have a structure for\narticle creation and a template for rendering.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The example Lunar Resort theme's updated XML articles are\nin the ZIP file's\n\\texttt{/resources-importer/journal/articles/Basic\\ Web\\ Content/}\nfolder for reference.\n\n\\noindent\\hrulefill\n\nFollow these steps to update the theme's web content:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a subfolder called \\texttt{BASIC\\_WEB\\_CONTENT} in the\n  \\texttt{/resources-importer/journal/articles/} folder, and move all\n  the basic HTML articles (articles that did not require a structure or\n  template previously) into it.\n\\item\n  Create a subfolder in the\n  \\texttt{/resources-importer/journal/templates/} folder with the same\n  name as the folder you just created (\\texttt{BASIC\\_WEB\\_CONTENT}).\n  The articles and template folder names \\textbf{must match} for the web\n  content to import properly.\n\\item\n  XML article structures are now written in JSON. Open the\n  \\texttt{/resources-importer/journal/structures/} folder and create a\n  new file inside called \\texttt{BASIC\\_WEB\\_CONTENT.json}. The\n  structure name \\textbf{must match} the folder names created in the\n  previous steps. To ensure the syntax is correct for web content\n  articles that used a structure and template before, we recommend that\n  you recreate the structure and template in Liferay DXP.\n\\item\n  Add the JSON structure below to the \\texttt{BASIC\\_WEB\\_CONTENT.json}\n  file. This provides the required metadata to render standard web\n  content articles (i.e.~the language, fields, etc.):\n\n\\begin{verbatim}\n{\n    \"availableLanguageIds\": [\n        \"en_US\"\n    ],\n    \"defaultLanguageId\": \"en_US\",\n    \"fields\": [\n        {\n            \"label\": {\n                \"en_US\": \"Content\"\n            },\n            \"predefinedValue\": {\n                \"en_US\": \"\"\n            },\n            \"style\": {\n                \"en_US\": \"\"\n            },\n            \"tip\": {\n                \"en_US\": \"\"\n            },\n            \"dataType\": \"html\",\n            \"fieldNamespace\": \"ddm\",\n            \"indexType\": \"keyword\",\n            \"localizable\": true,\n            \"name\": \"content\",\n            \"readOnly\": false,\n            \"repeatable\": false,\n            \"required\": false,\n            \"showLabel\": true,\n            \"type\": \"ddm-text-html\"\n        }\n    ]\n}\n\\end{verbatim}\n\\item\n  Create a new FreeMarker template file for basic web content inside the\n  \\texttt{/resources-importer/journal/templates/BASIC\\_WEB\\_CONTENT}\n  folder called \\texttt{BASIC\\_WEB\\_CONTENT.ftl}, and add the method\n  below to retrieve the article's data:\n\n\\begin{verbatim}\n${content.getData()}\n\\end{verbatim}\n\\item\n  Convert the basic web content articles from HTML to XML to conform to\n  the new format. Replace the \\texttt{.html} file extensions with\n  \\texttt{.xml}. wrap each basic web content article's content with the\n  XML shown below:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<root available-locales=\"en_US\" default-locale=\"en_US\">\n        <dynamic-element name=\"content\" type=\"text_area\"\n        index-type=\"keyword\" index=\"0\">\n                <dynamic-content language-id=\"en_US\">\n                        <![CDATA[\n                        ORIGINAL HTML CONTENT GOES HERE\n                        ]]>\n                </dynamic-content>\n        </dynamic-element>\n</root>\n\\end{verbatim}\n\\item\n  7.0's updated Bootstrap requires that you replace all\n  \\texttt{span{[}number{]}} classes with the updated\n  \\texttt{col-{[}device-size{]}-{[}number{]}} syntax.\n  \\texttt{{[}device-size{]}} can be \\texttt{xs}, \\texttt{sm},\n  \\texttt{md}, or \\texttt{lg}. See Bootstrap's\n  \\href{https://getbootstrap.com/docs/4.0/layout/grid/}{documentation}\n  for more information. The original and updated classes for the Lunar\n  Resort's \\texttt{2\\ column\\ description.xml} article are shown below:\n\n  Original:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<root available-locales=\"en_US\" default-locale=\"en_US\">\n    <dynamic-element name=\"content\" type=\"text_area\"\n    index-type=\"keyword\" index=\"0\">\n        <dynamic-content language-id=\"en_US\">\n            <![CDATA[\n                <div class=\"container-fluid\">\n                    <div class=\"span4\" id=\"columnLeft\">\n                        Out of This World\n                    </div>\n                    <div class=\"span8\" id=\"columnRight\">\n                        Come to the Lunar Resort...\n                    </div>\n                </div>\n            ]]>\n        </dynamic-content>\n    </dynamic-element>\n</root>\n\\end{verbatim}\n\n  Updated:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<root available-locales=\"en_US\" default-locale=\"en_US\">\n    <dynamic-element name=\"content\" type=\"text_area\"\n    index-type=\"keyword\" index=\"0\">\n        <dynamic-content language-id=\"en_US\">\n            <![CDATA[\n                <div class=\"container-fluid\">\n                    <div class=\"col-md-4\" id=\"columnLeft\">\n                        Out of This World\n                    </div>\n                    <div class=\"col-md-8\" id=\"columnRight\">\n                        Come to the Lunar Resort...\n                    </div>\n                </div>\n            ]]>\n        </dynamic-content>\n    </dynamic-element>\n</root>\n\\end{verbatim}\n\\end{enumerate}\n\nThe web content is updated! Next, you must update the theme's sitemap\nfile.\n\n\\chapter{Updating the 6.2 Sitemap}\\label{updating-the-6.2-sitemap}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating 6.2 Resources Importer</p><p>Step 3 of 3</p>\n\\end{verbatim}\n\nIn Liferay DXP 6.2, portlet IDs were incremental numbers. In 7.0,\nthey're explicit class names. Update the \\texttt{sitemap.json} file with\nthe new portlet IDs. Follow these steps to update the sitemap:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Replace the portlet IDs with the updated class names. The\n  \\href{/docs/7-2/reference/-/knowledge_base/r/fully-qualified-portlet-ids}{Portlet\n  ID Quick Reference Guide} list the default portlet IDs. Check\n  \\texttt{liferay-portlet.xml} for the portlet ID number in 6.2 and\n  replace it with the updated ID in the quick reference Guide.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** you can also retrieve a portlet's ID from the UI. Open the \n portlet's *Options* menu, select *Look and Feel Configuration*. \n \n ![ You can find the portlet ID in the *Look and Feel Configuration* menu.](./images/upgrading-themes-look-and-feel-menu.png)\n \n Select the *Advanced Styling* tab. The `Portlet ID` value appears in the \n blue box. \n \n ![ The portlet ID appears within the blue box in the *Advanced Styling* tab.](./images/upgrading-themes-portlet-id.png)\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nThe original and updated versions of the Lunar Resort theme's `sitemap.json` \nare shown below:\n\nOriginal:\n\n```json\n{\n    \"name\": \"Collaboration\",\n    \"title\": \"Collaboration\",\n    \"friendlyURL\": \"/collaboration\",\n    \"layoutTemplateId\": \"2_columns\",\n    \"columns\": [\n        [\n            {\n            \"portletId\": \"36\"\n            }\n        ],\n        [\n            {\n            \"portletId\": \"115\"\n            }\n        ]\n    \n    ]\n}\n```\n\nUpdated:\n\n```json\n{\n  \"name\": \"Collaboration\",\n  \"title\": \"Collaboration\",\n  \"friendlyURL\": \"/collaboration\",\n  \"layoutTemplateId\": \"2_columns\",\n  \"columns\": [\n    [\n      {\n      \"portletId\": \"com_liferay_wiki_web_portlet_WikiPortlet\"\n      }\n    ],\n    [\n      {\n      \"portletId\": \"com_liferay_blogs_web_portlet_BlogsAgreggatorPortlet\"\n      }\n    ]\n  \n  ]\n},\n```\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\tightlist\n\\item\n  Update references to the web content articles in the\n  \\texttt{sitemap.json} to use the XML file extensions.\n\\end{enumerate}\n\nGreat! The Resources Importer updates are complete. Next you'll apply\nClay markup patterns to the theme's custom UI.\n\n\\chapter{Applying Clay Design\nPatterns}\\label{applying-clay-design-patterns}\n\n7.0 uses \\href{https://clayui.com/}{Clay}, a web implementation of\nLiferay's \\href{https://lexicondesign.io/}{Lexicon Experience Language}.\nThe Lexicon Experience Language provides styling guidelines and best\npractices for application UIs. Clay's CSS, HTML, and JavaScript\ncomponents enable developers to build fully-realized UIs quickly and\neffectively. Liferay DXP's\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/using-the-bootstrap-3-lexicon-css-compatibility-layer}{compatibility\nlayer} let's you use \\href{https://lexiconcss.wedeploy.io/}{Lexicon CSS}\nmarkup alongside \\href{https://clayui.com/}{Clay CSS}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The compatibility layer is meant as a short-term solution\nto ensure that your Bootstrap 3 and Lexicon CSS components aren't broken\nwhile you update your theme to use\n\\href{https://getbootstrap.com/docs/4.3/migration/}{Bootstrap 4} and\n\\href{https://clayui.com/docs/css-framework/scss.html}{Clay CSS}. It\nwill be disabled in a future release. Migrate your theme to use\nBootstrap 4 and Clay CSS as soon as you're able to.\n\n\\noindent\\hrulefill\n\nThis section demonstrates how to apply Clay to the Lunar Resort's form.\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Replace the \\texttt{control-group} classes with \\texttt{form-group}\n  classes:\n\\item\n  Remove the \\texttt{control-label} classes from the \\texttt{label}\n  elements:\n\\item\n  Remove \\texttt{\\textless{}div\\ class=\"controls\"\\textgreater{}}\n  elements.\n\\item\n  Add the \\texttt{form-control} class to each \\texttt{input} element.\n\\item\n  Add the \\texttt{btn-primary} class to your submit buttons to emphasize\n  them.\n\\end{enumerate}\n\nThe Lunar Resort's original form and updated form are shown below:\n\nOriginal form markup:\n\n\\begin{verbatim}\n<form class=\"form-horizontal\">\n        <fieldset>\n          <legend>Reservation Form</legend>\n          <div class=\"control-group\">\n              <label class=\"control-label\" for=\"inputName\">Name</label>\n              <div class=\"controls\">\n                      <input type=\"text\" id=\"inputName\"\n                      placeholder=\"Enter your Name here\" required=\"required\">\n                  </div>\n          </div>\n          <div class=\"control-group\">\n              <label class=\"control-label\" for=\"inputEmail\">Email</label>\n              <div class=\"controls\">\n                  <input type=\"email\" id=\"inputEmail\"\n                  placeholder=\"Enter your E-Mail here\" required=\"required\">\n              </div>\n          </div>\n          <div class=\"control-group\">\n              <div class=\"controls\">\n                  <button type=\"submit\" class=\"btn\">Submit</button>\n              </div>\n          </div>\n        </fieldset>\n</form>\n\\end{verbatim}\n\nUpdated form markup:\n\n\\begin{verbatim}\n<form role=\"form-horizontal\">\n        <fieldset>\n          <legend>Reservation Form</legend>\n          <div class=\"form-group\">\n              <label for=\"inputName\">Name</label>\n              <input type=\"text\" id=\"inputName\" class=\"form-control\"\n              placeholder=\"Enter your Name here\" required=\"required\">\n          </div>\n          <div class=\"form-group\">\n              <label for=\"inputEmail\">Email</label>\n              <input type=\"email\" id=\"inputEmail\" class=\"form-control\"\n              placeholder=\"Enter your E-Mail here\" required=\"required\">\n          </div>\n          <div class=\"form-group\">\n                  <button type=\"submit\" class=\"btn btn-primary\">Submit\n                  </button>\n          </div>\n        </fieldset>\n</form>\n\\end{verbatim}\n\nThe Lunar Resort theme is updated for 7.0!\n\n\\chapter{Upgrading Your Theme from Liferay Portal 7.0 to\n7.2}\\label{upgrading-your-theme-from-liferay-portal-7.0-to-7.2}\n\nIn this tutorial, you'll learn how to use the\n\\href{https://github.com/liferay/liferay-js-themes-toolkit/tree/master/packages}{Liferay\nJS Theme Toolkit} to upgrade a Liferay DXP 7.0 theme to 7.0. As you\nupgrade this theme, you'll learn how to update metadata, theme\ntemplates, UI (including support for Bootstrap 4 and Lexicon 2.0.), and\nmore using all the best practices and standards. Completing this\ntutorial prepares you for upgrading your own theme.\n\nTheme upgrades involve these steps:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Updating project metadata\n\\item\n  Updating CSS\n\\item\n  Updating theme templates\n\\end{itemize}\n\nLet's Go!{}\n\n\\chapter{Running the Upgrade Task for 7.0\nThemes}\\label{running-the-upgrade-task-for-7.0-themes}\n\nYou can upgrade a Liferay DXP 7.0 theme to 7.0, regardless of the\ndevelopment environment you use. This tutorial uses the Liferay JS Theme\nToolkit's Gulp \\texttt{upgrade} task to automate much of the steps. The\nGulp \\texttt{upgrade} task must be run twice to bring a Liferay DXP 7.0\ntheme up to 7.0.\n\nThe Liferay Theme Generator is available in a few different versions. To\nupdate the Liferay DXP 7.0 theme to Liferay DXP 7.1, you must install\nv8.x.x of the \\texttt{liferay-theme-tasks} dependency. After the theme\nis updated to 7.1, you must then install v9.x.x of the\n\\texttt{liferay-theme-tasks} dependency to complete the upgrade process.\n\nHere's what the Upgrade Task does:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Updates the theme's Liferay version\n\\item\n  Updates the theme's Bootstrap version\n\\item\n  Updates the theme's Lexicon version\n\\item\n  Suggests specific code updates\n\\end{itemize}\n\nFollow these steps to take the theme through the upgrade process:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the theme's root directory and run the command below to\n  update the theme's \\texttt{liferay-theme-tasks} dependency to version\n  \\texttt{8.x.x}:\n\n\\begin{verbatim}\nnpm install --save-dev liferay-theme-tasks@8.x.x\n\\end{verbatim}\n\\item\n  Run the \\texttt{gulp\\ upgrade} command to upgrade the Liferay DXP 7.0\n  theme to 7.1.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note**: The Upgrade task overwrites the theme's files. We recommend that \n you backup your files before proceeding with the upgrade process.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nHere's what it does:\n\n- Creates core code for generating theme base files\n- Collects removed Bootstrap and Lexicon variables\n- Updates Bootstrap version references\n- Updates Lexicon version references\n- Updates Liferay version references\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  You must update the theme's \\texttt{liferay-theme-tasks} dependency to\n  version \\texttt{9.x.x} to complete the upgrade process. Install the\n  latest version of the Liferay Theme Generator as well while you're at\n  it, so future uses of the tool will be compatible with the 7.0 theme.\n  Both commands are shown below. Run them separately:\n\n\\begin{verbatim}\nnpm install --save-dev liferay-theme-tasks@9.x.x\n\nnpm install -g generator-liferay-theme@9.x.x\n\\end{verbatim}\n\\item\n  With the \\texttt{9.x.x} versions of the \\texttt{liferay-theme-tasks}\n  and Liferay Theme Generator installed, run the \\texttt{gulp\\ upgrade}\n  command for the final time to upgrade the 7.1 theme to 7.2:\n\n  Here's what it does:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Updates Liferay version references\n  \\item\n    Updates theme dependencies\n  \\end{itemize}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Since Liferay DXP Fix Pack 2 and Liferay Portal 7.2 CE GA2, Font \n Awesome is available globally as a system setting, which is enabled by \n default. If you're using Font Awesome icons in your theme, answer yes (y) \n to the Font Awesome question during the Upgrade task to include Font \n Awesome imports in your theme. This ensures that your icons won't break if \n a Site Administrator disables the global setting. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{4}\n\\tightlist\n\\item\n  Run \\texttt{gulp\\ init} from your theme's root directory to update the\n  path of your Liferay DXP server to point to your 7.2 Liferay DXP\n  server.\n\\end{enumerate}\n\nThe Gulp \\texttt{upgrade} task lists any deprecated or removed\nvariables. For other areas of the code it suspects might need updates,\nit logs suggestions. The task also reports changes that may affect theme\ntemplates. This jump-starts the upgrade process, but it doesn't complete\nit. Manual updates are required. The remaining portion of this tutorial\ncovers these manual steps.\n\n\\chapter{Updating 7.0 CSS Code}\\label{updating-7.0-css-code}\n\n7.0's UI improvements requires these CSS-related changes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Renaming CSS files\n\\item\n  Class variable changes\n\\item\n  Updating core imports\n\\end{itemize}\n\nThe theme upgrade process involves conforming to these changes. Now\nyou'll update the theme's CSS files to reflect these changes. Start with\nupdating CSS file names.\n\nLet's Go{}\n\n\\chapter{Updating 7.0 CSS File Names for\nClay}\\label{updating-7.0-css-file-names-for-clay}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating 7.0 CSS Code</p><p>Step 1 of 3</p>\n\\end{verbatim}\n\nSome of the CSS filenames have changed to reflect the introduction of\nClay (previously Lexicon CSS). The file name changes for the Unstyled\ntheme are listed below. Refer to the\n\\href{/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide}{Theme\nReference Guide} for a complete list of expected theme CSS files.\n\nOrignal AUI file names:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{css/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_aui\\_custom.scss}\n  \\item\n    \\texttt{\\_aui\\_variables.scss}\n  \\item\n    \\texttt{aui.scss}\n  \\end{itemize}\n\\end{itemize}\n\nUpdated Clay file names:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{css/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{\\_clay\\_custom.scss}\n  \\item\n    \\texttt{\\_clay\\_variables.scss}\n  \\item\n    \\texttt{clay.scss}\n  \\end{itemize}\n\\end{itemize}\n\nNext, you can update the theme's CSS variables.\n\n\\chapter{Updating 7.0 Class\nVariables}\\label{updating-7.0-class-variables}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating 7.0 CSS Code</p><p>Step 2 of 3</p>\n\\end{verbatim}\n\n7.0 uses Bootstrap 4's CSS rule syntax. The new syntax lets developers\nleverage Bootstrap 4 features and improvements. The\n\\href{https://getbootstrap.com/docs/4.0/migration/}{Migrating to v4\nguide} provides complete instructions for updating CSS rules to\nBootstrap 4.\n\nFollow these steps to upgrade the theme's CSS variables:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Consult the upgrade log produced by the \\texttt{gulp\\ upgrade} task.\n  It suggests the manual Lexicon updates required for the theme.\n\\item\n  Make the required changes in the log. The log lists removed and/or\n  deprecated variables and suggests possible changes. For each update\n  performed or suggested, the task reports a file name. For example,\n  here is part of the task log for the 7.0 Westeros Bank theme:\n\\end{enumerate}\n\n\\begin{verbatim}\n Lexicon Upgrade (1.0 to 2.0)\n\nFile: _variables_custom.scss\n    $brand-default was deprecated in Lexicon CSS 1.x.x and has been removed \n        in the new Clay 2.x.x version\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If the \\texttt{gulp\\ upgrade} task detects any variables\nin the theme that are removed in Clay from the previous LexiconCSS\nversion, it adds a \\texttt{\\_variables\\_deprecated.scss} file to the\ntheme containing the removed variables, to make sure the theme compiles\nand to decouple it from future upgrades.\n\n\\noindent\\hrulefill\n\nAfter updating your theme's CSS variables and mixins, you should update\nthe imports next.\n\n\\chapter{Updating 7.0 Imports}\\label{updating-7.0-imports}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Updating 7.0 CSS Code</p><p>Step 3 of 3</p>\n\\end{verbatim}\n\nFont Awesome imports and core imports have changed. Follow these steps\nto update the theme:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Originally in Liferay Portal CE 7.0 and Liferay DXP, Font Awesome\n  icons were imported in \\texttt{\\_aui\\_variables.scss} (now renamed\n  \\texttt{\\_clay\\_variables.scss}). Font Awesome icons are now included\n  as a package dependency if you answer yes (y) to include Font Awesome\n  during the upgrade task. If a 7.0 theme was made prior to this move\n  and \\texttt{\\_aui\\_variables.scss} was modified, the Font Awesome\n  imports shown below must be removed from\n  \\texttt{\\_clay\\_variables.scss}:\n\n\\begin{verbatim}\n// Icon paths\n$FontAwesomePath: \"aui/lexicon/fonts/alloy-font-awesome/font\";\n$font-awesome-path: \"aui/lexicon/fonts/alloy-font-awesome/font\";\n$icon-font-path: \"aui/lexicon/fonts/\";\n\\end{verbatim}\n\\item\n  Update the old AUI lexicon paths to use the new Clay paths instead, as\n  shown in the table below:\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nPattern|Replacement|\n---|---|\n`@import \"/aui/lexicon/bootstrap/mixins/\";`|removed|\n`@import \"/aui/lexicon/lexicon-base/mixins/\";`|removed|\n`@import \"/aui/lexicon/atlas-theme/mixins/\";`|removed|\n`@import \"aui/lexicon/atlas-variables\";`|`@import \"clay/atlas-variables\";`|\n`@import \"aui/lexicon/atlas\";`|`@import \"clay/atlas\";`|\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nGreat! Your imports are updated, and your CSS upgrade is complete. Next\nyou can upgrade the theme templates.\n\n\\chapter{Updating 7.0 Theme Templates to\n7.2}\\label{updating-7.0-theme-templates-to-7.2}\n\nLiferay DXP 7.0 theme templates and 7.0 theme templates are essentially\nthe same. Here are the main changes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Velocity templates were deprecated in Liferay Portal CE 7.0 and are\n  now removed in favor of FreeMarker templates in 7.0.\n\\end{itemize}\n\nKey reasons for using FreeMarker templates and removing Velocity\ntemplates are these:\n\n\\begin{itemize}\n\\item\n  FreeMarker is developed and maintained regularly, while Velocity is no\n  longer actively being developed.\n\\item\n  FreeMarker is faster and supports more sophisticated macros.\n\\item\n  FreeMarker supports using taglibs directly rather than requiring a\n  method to represent them. You can pass body content to them,\n  parameters, etc.\n\\end{itemize}\n\nIf you haven't converted your Velocity theme templates to FreeMarker,\n\\textbf{you must convert your Velocity theme templates to FreeMarker\nnow}.\n\nThe \\texttt{gulp\\ upgrade} command reports the required theme template\nchanges in the log. For example, here is the \\texttt{gulp\\ upgrade} log\nfor the Westeros Bank theme:\n\n\\begin{verbatim}\n Liferay Upgrade (7.0 to 7.1)\n\nRenamed aui.scss to clay.scss\nFile: footer.ftl\n    Warning: .container-fluid-1280 has been deprecated. Please use \n    .container-fluid.container-fluid-max-xl instead.\nFile: portal_normal.ftl\n    Warning: .navbar-header has been removed. This container should be \n    removed in most cases. Please, use your own container if necessary.\n\\end{verbatim}\n\nThe log warns about removed and deprecated code and suggests\nreplacements when applicable. For reference, the main changes between\nLiferay DXP 7.0 themes and 7.0 themes appear below:\n\n\\begin{itemize}\n\\item\n  List items inside a container with the \\texttt{list-inline} class\n  \\href{https://getbootstrap.com/docs/4.3/migration/\\#typography}{now\n  require} the \\texttt{list-inline-item} class.\n\\item\n  The \\texttt{container-fluid-1280} class has been deprecated. Please\n  use \\texttt{container-fluid\\ container-fluid-max-xl} instead.\n\\item\n  Responsive navbar behaviors\n  \\href{https://getbootstrap.com/docs/4.3/migration/\\#navbar}{are now\n  applied} to the \\texttt{navbar} class via the required\n  \\texttt{navbar-expand-\\{breakpoint\\}} class.\n\\item\n  The \\texttt{navbar-toggle} class is now \\texttt{navbar-toggler} and\n  \\href{https://getbootstrap.com/docs/4.3/migration/\\#navbar}{has\n  different inner markup}.\n\\item\n  The \\texttt{navbar-header} class has been removed. This container\n  should be removed in most cases. Please, use your own container if\n  necessary.\n\\end{itemize}\n\nIn this section you'll learn how to update various theme templates to\n7.0.\n\nLet's Go{}\n\n\\chapter{Updating 7.0 Theme\nTemplates}\\label{updating-7.0-theme-templates}\n\nFollow these steps to update the theme's templates. Note these changes\nare only required if the templates are modified in the theme:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{portal\\_normal.ftl} and remove the breadcrumbs:\n\n\\begin{verbatim}\n<nav id=\"breadcrumbs\">      \n    <@liferay.breadcrumbs />        \n</nav>\n\\end{verbatim}\n\\item\n  Still inside \\texttt{portal\\_normal.ftl}, remove\n  \\texttt{id=\"main-surface\"} from the \\texttt{body} tag so it looks like\n  the one below. This is not needed for SPA to work properly:\n\n\\begin{verbatim}\n<body class=\"${css_class}\">\n\\end{verbatim}\n\\item\n  Open \\texttt{navigation.ftl} and remove the\n  \\texttt{nav\\_item\\_attr\\_selected} variable declaration at the top.\n  Don't forget to remove all uses of the\n  \\texttt{nav\\_item\\_attr\\_selected} throughout the rest of the\n  template.\n\\item\n  Also inside \\texttt{navigation.ftl}, remove the\n  \\texttt{nav\\_child\\_attr\\_selected} variable from the bottom of the\n  template, including all uses throughout the rest of the template.\n\\item\n  Open \\texttt{portlet.ftl} and find the code snippet below:\n\n\\begin{verbatim}\n<a \n  class=\"icon-monospaced portlet-icon-back text-default\" \n  href=\"${portlet_back_url}\" \n  title=\"<@liferay.language key=\"return-to-full-page\" />\"\n>\n\\end{verbatim}\n\n  Add the \\texttt{list-unstyled} class to it:\n\n\\begin{verbatim}\n<a \n  class=\"icon-monospaced list-unstyled portlet-icon-back text-default\" \n  href=\"${portlet_back_url}\" \n  title=\"<@liferay.language key=\"return-to-full-page\" />\"\n>\n\\end{verbatim}\n\\item\n  Still inside \\texttt{portlet.ftl}, find the\n  \\texttt{\\textless{}div\\ class=\"autofit-float\\ autofit-row\"\\textgreater{}}\n  element and add the \\texttt{portlet-header} class to it:\n\n\\begin{verbatim}\n<div class=\"autofit-float autofit-row portlet-header\">\n\\end{verbatim}\n\\end{enumerate}\n\nThe theme templates are updated! If you modified any other FreeMarker\ntheme templates, you can compare them with templates in the\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-unstyled/src/main/resources/META-INF/resources/_unstyled/templates}{\\texttt{\\_unstyled}\ntheme}. Next you can learn how to use Liferay DXP's compatibility layer\nto help ease the transition to Bootstrap 4 and Clay CSS.\n\n\\chapter{Using the Bootstrap 3 Lexicon CSS Compatibility\nLayer}\\label{using-the-bootstrap-3-lexicon-css-compatibility-layer}\n\nBy default, 7.0 includes Bootstrap 4 out-of-the-box. Bootstrap 4 has\nbeen completely rewritten and therefore includes some\n\\href{https://getbootstrap.com/docs/4.3/migration/}{notable changes} and\n\\href{https://getbootstrap.com/docs/4.3/getting-started/introduction/}{compatibility\nupdates} that may be cause for concern if your theme uses Bootstrap 3 or\nLexicon CSS. Not to worry though. To ensure that your upgrade runs\nsmoothly, Liferay DXP includes a compatibility layer so you can use\nBootstrap 3 markup and Lexicon CSS markup alongside the new Bootstrap 4\nand Clay CSS. If your theme extends the\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-styled}{Styled\nbase theme}, this compatibility layer is included by default.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The compatibility layer is meant as a short-term solution\nto ensure that your Bootstrap 3 and Lexicon CSS components aren't broken\nwhile you update your theme to use\n\\href{https://getbootstrap.com/docs/4.3/migration/}{Bootstrap 4} and\n\\href{https://clayui.com/docs/css-framework/scss.html}{Clay CSS}. It\nwill be disabled in a future release. Migrate your theme to use\nBootstrap 4 and Clay CSS as soon as you're able to.\n\n\\noindent\\hrulefill\n\nFollow these guidelines to update your markup:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Inspect your themes UI with the compatibility layer enabled (it's\n  enabled by default), and note any issues.\n\\item\n  Individually disable the component(s) in the compatibility layer that\n  you don't need. These are listed in the\n  \\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-theme/frontend-theme-styled/src/main/resources/META-INF/resources/_styled/css/compat/_variables.scss}{\\texttt{css/compat/\\_variables.scss}}\n  file. For convenience, these components are listed below:\n\n\\begin{verbatim}\n// Compatibility layer components config\n\n$compat-alerts: true !default;\n$compat-basic_search: true !default;\n$compat-breadcrumbs: true !default;\n$compat-button_groups: true !default;\n$compat-buttons: true !default;\n$compat-cards: true !default;\n$compat-component_animations: true !default;\n$compat-dropdowns: true !default;\n$compat-figures: true !default;\n$compat-form_validation: true !default;\n$compat-forms: true !default;\n$compat-grid: true !default;\n$compat-icons: true !default;\n$compat-labels: true !default;\n$compat-liferay: true !default;\n$compat-list_groups: true !default;\n$compat-management_bar: true !default;\n$compat-modals: true !default;\n$compat-nav_tabs: true !default;\n$compat-navbar: true !default;\n$compat-navs: true !default;\n$compat-pager: true !default;\n$compat-pagination: true !default;\n$compat-panels: true !default;\n$compat-progress_bars: true !default;\n$compat-responsive_utilities: true !default;\n$compat\\noindent\\hrulefill: true !default;\n$compat-simple_flexbox_grid: true !default;\n$compat-stickers: true !default;\n$compat-tables: true !default;\n$compat-toggle_card: true !default;\n$compat-toggle_switch: true !default;\n$compat-toolbar: true !default;\n$compat-user_icons: true !default;\n$compat-utilities: true !default;\n\\end{verbatim}\n\n  To disable a component, add the component you want to remove\n  compatibility for to \\texttt{/src/css/\\_clay\\_custom.scss} (create\n  this file if it doesn't exist) and set its value to \\texttt{false}.\n  The example below removes compatibility for alerts and cards:\n\n\\begin{verbatim}\n$compat-alerts: false !default;\n$compat-cards: false !default;\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Some Liferay DXP components haven't been migrated to Bootstrap 4.\n Disabling certain components might cause portions of the UI to break.\n Therefore, after upgrading your markup, we recommend that you re-enable any\n components you disable. Proceed with caution.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Update your markup to Bootstrap 4 and Clay CSS until you're satisfied\n  with the result.\n\\item\n  Re-enable any components you disabled in the compatibility layer by\n  removing any components you set to false in\n  \\texttt{/src/css/\\_clay\\_custom.scss}. This prevents Liferay DXP's UI\n  from breaking.\n\\end{enumerate}\n\nNow you know how to use the Bootstrap 3 and Lexicon CSS compatibility\nlayer to provide a smooth transition during your theme upgrade.\n\n\\chapter{Upgrading 7.1 Themes to 7.2}\\label{upgrading-7.1-themes-to-7.2}\n\nYou can upgrade a Liferay Portal 7.1 theme to 7.0, regardless of the\ndevelopment environment you use. This tutorial uses the Liferay JS Theme\nToolkit's Gulp \\texttt{upgrade} task to automate much of the steps. This\nrequires v9.x.x of the Liferay Theme Generator and liferay theme tasks.\n\nHere's what the Upgrade Task does:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Updates Liferay version references\n\\item\n  Updates theme dependencies\n\\end{itemize}\n\nFollow these steps to upgrade the theme:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Install the Liferay Theme Generator v9.x.x with the command below:\n\n\\begin{verbatim}\nnpm install -g generator-liferay-theme@9.x.x\n\\end{verbatim}\n\\item\n  You must update the theme's \\texttt{liferay-theme-tasks} dependency to\n  version \\texttt{9.x.x} as well to run the upgrade process:\n\n\\begin{verbatim}\nnpm install --save-dev liferay-theme-tasks@9.x.x\n\\end{verbatim}\n\\item\n  With the \\texttt{9.x.x} versions of the \\texttt{liferay-theme-tasks}\n  and Liferay Theme Generator installed, run the \\texttt{gulp\\ upgrade}\n  command to upgrade the 7.1 theme to 7.2.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note**: The Upgrade task overwrites the theme's files. We recommend that \n you backup your files before proceeding with the upgrade process.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  In 7.1, Font Awesome \\& Glyphicons were included in the\n  \\href{/docs/7-1/tutorials/-/knowledge_base/t/using-the-bootstrap-3-lexicon-css-compatibility-layer}{compatibility\n  layer}. Since Liferay DXP Fix Pack 2 and Liferay Portal 7.2 CE GA2,\n  Font Awesome is available globally as a system setting, which is\n  enabled by default. If you're using Font Awesome icons in your theme,\n  answer yes (y) to the Font Awesome question during the Upgrade task to\n  include the Font Awesome dependency in your theme. This ensures that\n  your icons won't break if a Site Administrator disables the global\n  setting.\n\\item\n  Run \\texttt{gulp\\ init} from the theme's root directory to update the\n  path of the app server to point to the new 7.2 app server.\n\\end{enumerate}\n\nThere you have it! The theme is ready to run on 7.0.\n\n\\chapter{Upgrading a Layout Template to\n7.2}\\label{upgrading-a-layout-template-to-7.2}\n\nIn these tutorials, you'll learn how to upgrade your layout templates\nfrom earlier versions of Liferay DXP to 7.0. By the end of the tutorial,\nyou'll have a layout template that runs on 7.0.\n\nSelect the tutorial below that corresponds to the current version of\nyour layout template:\n\nLet's Go 6.2!{}\n\nLet's Go 7.0 and 7.1!{}\n\n\\chapter{Upgrading 6.2 Layout Templates to\n7.2}\\label{upgrading-6.2-layout-templates-to-7.2}\n\nUpgrading your Liferay DXP 6.2 layout template to 7.0 requires a few\nupdates:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your layout template's \\texttt{liferay-plugin-package.properties}\n  file and update the \\texttt{liferay-versions} property to\n  \\texttt{7.2.0+}:\n\n\\begin{verbatim}\nliferay-versions=7.2.0+\n\\end{verbatim}\n\\item\n  Velocity layout templates are supported, but deprecated as of Liferay\n  DXP 7.1. We recommend that you convert your Velocity layout templates\n  to FreeMarker now. Wrap the\n  \\texttt{processor.processColumn(\"column-1\",\\ \"portlet-column-content\\ portlet-column-content-first\")}\n  methods with braces (\\texttt{\\{...\\}}) and change the template's file\n  extension to \\texttt{.ftl}.\n\\item\n  Update the Bootstrap \\texttt{span{[}number{]}} classes to use the\n  newer \\texttt{col-{[}size{]}-{[}number{]}} classes. See\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/layout-templates-intro}{Layout\n  Templates} for more information on the updated syntax.\n\\item\n  Save the changes.\n\n  Below is an example configuration:\n\n  Original:\n\n\\begin{verbatim}\n<div class=\"span4 span6 portlet-column portlet-column-first\" \nid=\"column-1\">\n        $processor.processColumn(\"column-1\", \n        \"portlet-column-content portlet-column-content-first\")\n</div>\n<div class=\"span8 span6 portlet-column portlet-column-last\" \nid=\"column-2\">\n        $processor.processColumn(\"column-2\", \n        \"portlet-column-content portlet-column-content-last\")\n</div>\n</div>\n\\end{verbatim}\n\n  Updated:\n\n\\begin{verbatim}\n<div class=\"col-md-4 col-sm-6 portlet-column portlet-column-first\" \nid=\"column-1\">\n        ${processor.processColumn(\"column-1\", \n        \"portlet-column-content portlet-column-content-first\")}\n</div>\n<div class=\"col-md-8 col-sm-6 portlet-column portlet-column-last\" \nid=\"column-2\">\n        ${processor.processColumn(\"column-2\", \n        \"portlet-column-content portlet-column-content-last\")}\n</div>\n</div>\n\\end{verbatim}\n\\end{enumerate}\n\nAwesome! Your layout template is upgraded.\n\n\\chapter{Upgrading 7.0 and 7.1 Layout Templates to\n7.2}\\label{upgrading-7.0-and-7.1-layout-templates-to-7.2}\n\nIf you're upgrading your Liferay DXP 7.0 or Liferay DXP 7.1 layout\ntemplate to 7.0, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your layout template's \\texttt{liferay-plugin-package.properties}\n  file and update the \\texttt{liferay-versions} property to\n  \\texttt{7.2.0+}:\n\n\\begin{verbatim}\nliferay-versions=7.2.0+\n\\end{verbatim}\n\\item\n  Velocity layout templates are supported, but deprecated as of Liferay\n  DXP 7.1. We recommend that you convert your Velocity layout templates\n  to FreeMarker now. Wrap the\n  \\texttt{processor.processColumn(\"column-1\",\\ \"portlet-column-content\\ portlet-column-content-first\")}\n  methods with braces (\\texttt{\\{...\\}}) and change the template's file\n  extension to \\texttt{.ftl}.\n\\item\n  Save the changes.\n\n  Below is an example configuration:\n\n  Original (\\texttt{my\\_layout\\_template.tpl}):\n\n\\begin{verbatim}\n<div class=\"col-md-4 col-sm-6 portlet-column portlet-column-first\" \nid=\"column-1\">\n        $processor.processColumn(\"column-1\", \n        \"portlet-column-content portlet-column-content-first\")\n</div>\n<div class=\"col-md-8 col-sm-6 portlet-column portlet-column-last\" \nid=\"column-2\">\n        $processor.processColumn(\"column-2\", \n        \"portlet-column-content portlet-column-content-last\")\n</div>\n</div>\n\\end{verbatim}\n\n  Updated (\\texttt{my\\_layout\\_template.ftl}):\n\n\\begin{verbatim}\n<div class=\"col-md-4 col-sm-6 portlet-column portlet-column-first\" \nid=\"column-1\">\n        ${processor.processColumn(\"column-1\", \n        \"portlet-column-content portlet-column-content-first\")}\n</div>\n<div class=\"col-md-8 col-sm-6 portlet-column portlet-column-last\" \nid=\"column-2\">\n        ${processor.processColumn(\"column-2\", \n        \"portlet-column-content portlet-column-content-last\")}\n</div>\n</div>\n\\end{verbatim}\n\\end{enumerate}\n\nAwesome! Your layout template is upgraded.\n\n\\chapter{Upgrading Frameworks and\nFeatures}\\label{upgrading-frameworks-and-features}\n\nYour upgrade process not only relies on portlet technology, themes, and\ncustomization plugins, but also the frameworks your project leverages.\nThe following frameworks and their upgrade processes are discussed in\nthis section:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  JNDI data source usage\n\\item\n  Service Builder service invocation\n\\item\n  Service Builder\n\\item\n  Velocity templates\n\\end{itemize}\n\nContinue on to learn more about upgrading these frameworks.\n\nLet's Go!{}\n\n\\chapter{Upgrading JNDI Data Source\nUsage}\\label{upgrading-jndi-data-source-usage}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Frameworks and Features</p><p>Step 1 of 4</p>\n\\end{verbatim}\n\nIn Liferay DXP's OSGi environment, you must use the portal's class\nloader to load the application server's JNDI classes. An OSGi bundle's\nattempt to connect to a JNDI data source without using Liferay DXP's\nclass loader results in a \\texttt{java.lang.ClassNotFoundException}.\n\nFor more information on how to do this, see the\n\\href{/docs/7-2/appdev/-/knowledge_base/a/connecting-to-data-sources-using-jndi}{Connecting\nto JNDI Data Sources} article.\n\n\\chapter{Upgrading Service Builder Service\nInvocation}\\label{upgrading-service-builder-service-invocation}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Frameworks and Features</p><p>Step 2 of 4</p>\n\\end{verbatim}\n\nWhen upgrading a portlet leveraging\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}, you must first decide if you're building your Service Builder\nlogic as a WAR or modularizing it.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Service Builder portlets automatically migrated to\nLiferay Workspace using the Upgrade Planner or Blade CLI's\n\\texttt{convert} command automatically have its Service Builder logic\nconverted to API and implementation modules. This is a best practice for\n7.0.\n\n\\noindent\\hrulefill\n\nIf you prefer keeping your Service Builder logic as a WAR, you must\nimplement a service tracker to call services. See the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker}{Service\nTrackers} article for more information.\n\n\\chapter{Upgrading Service Builder}\\label{upgrading-service-builder}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Frameworks and Features</p><p>Step 3 of 4</p>\n\\end{verbatim}\n\n7.0 continues to use\n\\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\nBuilder}, so you can focus on your application's business logic instead\nof its persistence details. It still generates model classes, local and\nremote services, and persistence.\n\nUpgrading most Service Builder portlets involves these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  \\hyperref[step-1-adapt-the-code-to-product-vers-api]{Adapt the code to\n  7.0's API}\n\\item\n  \\hyperref[step-2-resolve-dependencies]{Resolve dependencies}\n\\item\n  \\hyperref[step-3-build-the-services]{Build the services}\n\\end{enumerate}\n\nStart by adapting the code.\n\n\\section{Step 1: Adapt the Code to 7.0's\nAPI}\\label{step-1-adapt-the-code-to-7.0s-api}\n\nAdapt the portlet to 7.0's API using the Upgrade Planner. When running\nthe planner's \\emph{Fix Upgrade Problems} step, many of the existing\nissues are autocorrected. For remaining issues, the planner identifies\ncode affected by the new API and ways to adapt it.\n\nFor example, consider an example portlet with the following compilation\nerror:\n\n\\begin{verbatim}\n/html/guestbook/view.jsp(58,1) PWC6131: Attribute total invalid for tag search-container-results according to TLD\n\\end{verbatim}\n\nThe \\texttt{view.jsp} file specifies a tag library attribute\n\\texttt{total} that doesn't exist in 7.0's \\texttt{liferay-ui} tag\nlibrary. Notice the second attribute \\texttt{total}.\n\n\\begin{verbatim}\n<liferay-ui:search-container-results\n    results=\"<%=EntryLocalServiceUtil.getEntries(scopeGroupId,\n                    guestbookId, searchContainer.getStart(),\n                    searchContainer.getEnd())%>\"\n    total=\"<%=EntryLocalServiceUtil.getEntriesCount(scopeGroupId,\n                    guestbookId)%>\" />\n\\end{verbatim}\n\nRemove the \\texttt{total} attribute assignment to make the tag like\nthis:\n\n\\begin{verbatim}\n<liferay-ui:search-container-results\n    results=\"<%=EntryLocalServiceUtil.getEntries(scopeGroupId,\n                    guestbookId, searchContainer.getStart(),\n                    searchContainer.getEnd())%>\" />\n\\end{verbatim}\n\nResolve these error types and others until your code is adapted to the\nnew API.\n\n\\section{Step 2: Resolve\nDependencies}\\label{step-2-resolve-dependencies}\n\nTo adapt your app's dependencies, refer to the\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolving\na Project's Dependencies} tutorial. Once your dependencies are upgraded,\nrebuild your services!\n\n\\section{Step 3: Build the Services}\\label{step-3-build-the-services}\n\nAn example change where upgrading legacy Service Builder code can\nproduce differing results is explained below.\n\nA Liferay Portal 6.2 portlet's \\texttt{service.xml} file specifies\nexception class names in \\texttt{exception} elements like this:\n\n\\begin{verbatim}\n<service-builder package-path=\"com.liferay.docs.guestbook\">\n    ...\n    <exceptions>\n        <exception>GuestbookName</exception>\n        <exception>EntryName</exception>\n        <exception>EntryMessage</exception>\n        <exception>EntryEmail</exception>\n    </exceptions>\n</service-builder>\n\\end{verbatim}\n\nIn Liferay Portal 6.2, Service Builder generates exception classes to\nthe path attribute \\texttt{package-path} specifies. In 7.0, Service\nBuilder generates them to \\texttt{{[}package-path{]}/exception}.\n\nOld path:\n\n\\begin{verbatim}\n[package-path]\n\\end{verbatim}\n\nNew path:\n\n\\begin{verbatim}\n[package-path]/exception \n\\end{verbatim}\n\nFor example, the example portlet's package path is\n\\texttt{com.liferay.docs.guestbook}. Its exception class for\n\\texttt{exception} element \\texttt{GuestbookName} is generated to\n\\texttt{docroot/WEB-INF/service/com/liferay/docs/guestbook/exception}.\nClasses that use the exception must import\n\\texttt{com.liferay.docs.guestbook.exception.GuestbookNameException}. If\nthis upgrade is required in your Service Builder project, you must\nupdate the references to your portlet's exception classes.\n\nOnce your Service Builder portlet is upgraded,\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{deploy\nit}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Service Builder portlets automatically migrated to\nLiferay Workspace using the Upgrade Planner or Blade CLI's\n\\texttt{convert} command automatically has its Service Builder logic\nconverted to API and implementation modules. This is a best practice for\n7.0.\n\n\\noindent\\hrulefill\n\nThe portlet is now available on Liferay DXP. Congratulations on\nupgrading a portlet that uses Service Builder!\n\n\\chapter{Migrating Off of Velocity\nTemplates}\\label{migrating-off-of-velocity-templates}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Frameworks and Features</p><p>Step 4 of 4</p>\n\\end{verbatim}\n\nVelocity templates were deprecated in Liferay Portal 7.0 and are now\nremoved in favor of FreeMarker templates in 7.0. Below are the key\nreasons for this move:\n\n\\begin{itemize}\n\\item\n  FreeMarker is developed and maintained regularly, while Velocity is no\n  longer actively being developed.\n\\item\n  FreeMarker is faster and supports more sophisticated macros.\n\\item\n  FreeMarker supports using taglibs directly rather than requiring a\n  method to represent them. You can pass body content to them,\n  parameters, etc.\n\\end{itemize}\n\nAlthough Velocity templates still work in 7.0, we highly recommend\nmigrating to FreeMarker templates. For more information on this topic,\nsee the\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-layout-template-to-7-2}{Upgrading\nLayout Templates} section.\n\n\\chapter{Upgrading Portlets}\\label{upgrading-portlets}\n\nAll portlet types developed for Liferay Portal 6.x, 7.0, and 7.1 can be\nupgraded and deployed to 7.0.\n\nUpgrading most portlets involves these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Adapt the code to 7.0's API\n\\item\n  Resolve dependencies\n\\end{enumerate}\n\nLiferay's Upgrade Planner helps you adapt your code to 7.0's API. This\nmakes resolving a portlet's dependencies straightforward. In most cases,\nafter you finish the above steps, you can deploy your portlet to Liferay\nDXP.\n\nThe portlet upgrade tutorials show you how to upgrade the following\ncommon portlets:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-genericportlet}{GenericPortlet}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-liferay-mvc-portlet}{Liferay\n  MVC Portlet}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-liferay-jsf-portlet}{Liferay\n  JSF Portlet}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-servlet-based-portlet}{Servlet-based\n  portlet}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-spring-portlet-mvc-portlet}{Spring\n  Portlet MVC}\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-struts-1-portlet}{Struts\n  Portlet}\n\\end{itemize}\n\nLet's get your portlet running on 7.0!\n\nLet's Go!{}\n\n\\chapter{Upgrading a GenericPortlet}\\label{upgrading-a-genericportlet}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Portlets</p><p>Step 1 of 6</p>\n\\end{verbatim}\n\nIt's common to create portlets that extend\n\\texttt{javax.portlet.GenericPortlet}. After all,\n\\texttt{GenericPortlet} provides a default\n\\texttt{javax.portlet.Portlet} interface implementation. Upgrading a\n\\texttt{GenericPortlet} is straightforward and takes only two steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt the portlet to 7.0's API using the Liferay Upgrade Planner. When\n  running the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues are autocorrected. For remaining issues, the planner\n  identifies code affected by the new API and ways to adapt it.\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolve\n  its dependencies}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  it}\n\\end{enumerate}\n\nWhen the portlet WAR file is deployed, Liferay DXP's Plugin\nCompatibility Layer converts the WAR to a Web Application Bundle (WAB)\nand installs the portlet as a WAB to Liferay DXP's OSGi runtime.\n\nOn deploying an upgraded portlet, the server prints messages that\nindicate the following portlet status:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  WAR processing\n\\item\n  WAB startup\n\\item\n  Availability to users\n\\end{itemize}\n\nDeploying a portlet produces messages like these:\n\n\\begin{verbatim}\n2018-03-21 17:44:59.179 INFO  [com.liferay.portal.kernel.deploy.auto.AutoDeployScanner][AutoDeployDir:262] Processing sample-dao-portlet-7.1.0.1.war\n...\n2018-03-21 17:45:09.959 INFO  [Refresh Thread: Equinox Container: 0012cbb0-7e2c-0018-146e-95a4d71cdf95][PortletHotDeployListener:298] 1 portlet for sample-dao-portlet is available for use \n...\n2018-03-21 17:45:10.151 INFO  [Refresh Thread: Equinox Container: 0012cbb0-7e2c-0018-146e-95a4d71cdf95][BundleStartStopLogger:35] STARTED sample-dao-portlet_7.1.0.1 [655]\n\\end{verbatim}\n\nThe portlet is now available on Liferay DXP.\n\nYou've learned how to upgrade and deploy a portlet that extends\n\\texttt{GenericPortlet}. You adapt the code, resolve dependencies, and\ndeploy the portlet as you always have. It's just that easy!\n\n\\chapter{Upgrading a Liferay MVC\nPortlet}\\label{upgrading-a-liferay-mvc-portlet}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Portlets</p><p>Step 2 of 6</p>\n\\end{verbatim}\n\nLiferay's MVC Portlet framework is used extensively in Liferay DXP's\nportlets and is a popular choice for portlet developers. The\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCPortlet.html}{\\texttt{MVCPortlet}}\nclass is a lightweight extension of\n\\texttt{javax.portlet.GenericPortlet}. Its \\texttt{init} method saves\nyou from writing a lot of boilerplate code. MVC portlets can be upgraded\nto 7.0 without a hitch.\n\nUpgrading a Liferay MVC Portlet involves these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt the portlet to 7.0's API using the Liferay Upgrade Planner. When\n  running the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues are autocorrected. For remaining issues, the planner\n  identifies code affected by the new API and ways to adapt it.\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolve\n  its dependencies}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  it}\n\\end{enumerate}\n\nAfter deploying the upgraded portlet, the server prints messages that\nindicate the following portlet status:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  WAR processing\n\\item\n  WAB startup\n\\item\n  Availability to users\n\\end{itemize}\n\nYou've upgraded and deployed your Liferay MVC Portlet on your 7.0\ninstance. Have fun showing off your upgraded portlet!\n\n\\chapter{Upgrading a Liferay JSF\nPortlet}\\label{upgrading-a-liferay-jsf-portlet}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Portlets</p><p>Step 3 of 6</p>\n\\end{verbatim}\n\nLiferay JSF portlets are easy to upgrade and require few changes. They\ninterface with the\n\\href{/docs/7-2/reference/-/knowledge_base/r/liferay-faces}{Liferay\nFaces} project, which encapsulates Liferay DXP's Java API and JavaScript\ncode. Because of this, upgrading JSF portlets to 7.0 requires only\nupdating dependencies.\n\nThere are two ways to find a JSF portlet's dependencies for 7.0:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  The \\url{http://liferayfaces.org/} home page lets you look up the\n  dependencies (Gradle or Maven) by Liferay DXP version, JSF version,\n  and component suites.\n\\item\n  The\n  \\href{/docs/7-2/reference/-/knowledge_base/r/liferay-faces-version-scheme}{Liferay\n  Faces Version Scheme} article's tables list artifacts by Liferay DXP\n  version, JSF version, portlet version, and AlloyUI and Metal component\n  suite version.\n\\end{itemize}\n\nIn this article, you'll upgrade a Liferay DXP JSF portlet's (JSF 2.2)\ndependencies to 7.0.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your Liferay JSF portlet's build file (e.g., \\texttt{pom.xml},\n  \\texttt{build.gradle}) to where the dependencies are configured.\n\\item\n  Navigate to the \\url{http://liferayfaces.org/} site and generate a\n  dependency list by choosing the environment to which you want to\n  upgrade your portlet.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/jsf-dependency-generation.png}\n  \\caption{The Liferay Faces site gives you options to generate\n  dependencies for many environments.}\n  \\end{figure}\n\\item\n  Compare the generated dependencies with your portlet's dependencies\n  and make any necessary updates. For example, in the sample\n  dependencies listed below, the Mojarra dependency and two Liferay\n  Faces dependencies require updating:\n\n\\begin{verbatim}\n<dependency>\n    <groupId>org.glassfish</groupId>\n    <artifactId>javax.faces</artifactId>\n    <version>2.2.13</version>\n    <scope>runtime</scope>\n</dependency>\n<dependency>\n    <groupId>com.liferay.faces</groupId>\n    <artifactId>com.liferay.faces.bridge.ext</artifactId>\n    <version>3.0.0</version>\n</dependency>\n<dependency>\n    <groupId>com.liferay.faces</groupId>\n    <artifactId>com.liferay.faces.bridge.impl</artifactId>\n    <version>4.0.0</version>\n</dependency>\n\\end{verbatim}\n\n  Using the \\url{http://liferayfaces.org/} dependency list as a guide,\n  these dependencies would be updated to\n\n\\begin{verbatim}\n<dependency>\n    <groupId>org.glassfish</groupId>\n    <artifactId>javax.faces</artifactId>\n    <version>2.2.19</version>\n    <scope>runtime</scope>\n</dependency>\n<dependency>\n    <groupId>com.liferay.faces</groupId>\n    <artifactId>com.liferay.faces.bridge.ext</artifactId>\n    <version>5.0.4</version>\n</dependency>\n<dependency>\n    <groupId>com.liferay.faces</groupId>\n    <artifactId>com.liferay.faces.bridge.impl</artifactId>\n    <version>4.1.3</version>\n</dependency>\n\\end{verbatim}\n\\end{enumerate}\n\nOnce your Liferay JSF portlet's dependencies are updated, it's\ndeployable to 7.0! Follow the\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploying\na Project} article for deployment help.\n\nWhen the portlet WAR is deployed, Liferay DXP's Plugin Compatibility\nLayer converts the WAR to a Web Application Bundle (WAB) and installs\nthe portlet as a WAB to Liferay DXP's OSGi runtime. The server prints\nmessages that indicate the following portlet status:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  WAR processing\n\\item\n  WAB startup\n\\item\n  Availability to users\n\\end{itemize}\n\nDeploying a Liferay JSF portlet produces messages like these:\n\n\\begin{verbatim}\n13:41:43,690 INFO ... [com.liferay.portal.kernel.deploy.auto.AutoDeployScanner][AutoDeployDir:252] Processing com.liferay.faces.demo.jsf.applicant.portlet-1.0.war\n...\n13:42:03,522 INFO  [fileinstall-C:/liferay-ce-portal-7.2-ga1/osgi/war][BundleStartStopLogger:35] STARTED com.liferay.faces.demo.jsf.applicant.portlet-1.0_4.1.0 [503]\n...\n13:42:05,169 INFO  [fileinstall-C:/liferay-ce-portal-7.2-ga1/osgi/war][PortletHotDeployListener:293] 1 portlet for com.liferay.faces.demo.jsf.applicant.portlet-1.0 is available for use\n\\end{verbatim}\n\nAfter the portlet deployment is complete, it's available on Liferay DXP.\n\nYou've learned how to upgrade and deploy a Liferay JSF portlet. You\nresolved dependencies and deployed the portlet as you always have. It's\njust that easy!\n\n\\chapter{Upgrading a Servlet-based\nPortlet}\\label{upgrading-a-servlet-based-portlet}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Portlets</p><p>Step 4 of 6</p>\n\\end{verbatim}\n\nThis tutorial shows you how to upgrade servlet-based portlets. It refers\nto code from before and after upgrading a sample servlet-based portlet\ncalled \\emph{Sample JSON} (project \\texttt{sample-json-portlet}). The\nportlet shows a \\emph{Click me} link. When users click the link, the\nLiferay logo appears.\n\nFollow these steps to upgrade a servlet-based portlet:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt the portlet to 7.0's API using the Liferay Upgrade Planner. When\n  running the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues are autocorrected. For remaining issues, the planner\n  identifies code affected by the new API and ways to adapt it.\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolve\n  its dependencies}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  it}\n\\end{enumerate}\n\nFor an example upgrade scenario, consider this:\n\nSome servlet-based portlets relied on Liferay Portal to provide several\ndependency JAR files. Here's the \\texttt{portal-dependency-jars}\nproperty from a sample portlet's\n\\texttt{liferay-plugin-package.properties} file:\n\n\\begin{verbatim}\nportal-dependency-jars=\\\n    dom4j.jar,\\\n    jabsorb.jar,\\\n    json-java.jar\n\\end{verbatim}\n\nThis property is deprecated in 7.0 because importing and exporting Java\npackages has replaced wholesale use of JARs. This means modules and WABs\ncan import packages without concerning themselves with JARs. Liferay DXP\nexports many third party packages for plugins to use. Best practices for\nusing packages that Liferay DXP exports are found\n\\href{/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies}{here}.\n\nOnce you've deployed your portlet, the server prints messages that\nindicate the following portlet status:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  WAR processing\n\\item\n  WAB startup\n\\item\n  Availability to users\n\\end{itemize}\n\nThe portlet is installed to Liferay's OSGi runtime and is available to\nusers.\n\nCongratulations! You've upgraded and deployed your servlet-based portlet\nto 7.0.\n\n\\chapter{Upgrading a Spring Portlet MVC\nPortlet}\\label{upgrading-a-spring-portlet-mvc-portlet}\n\n\\begin{verbatim}\n<p>Upgrading Portlets<br>Step 5 of 6</p>\n\\end{verbatim}\n\nUpgraded portlets that use Spring Portlet MVC should be migrated to use\nPortletMVC4Spring. The main reason is that PortletMVC4Spring is\nmaintained for compatibility with the latest versions of the Spring\nFramework.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The PortletMVC4Spring project began as Spring Portlet MVC\nand was part of the\n\\href{https://spring.io/projects/spring-framework}{Spring Framework}.\nWhen the project was pruned from version 5.0.x of the Spring Framework\nunder\n\\href{https://github.com/spring-projects/spring-framework/issues/18701}{SPR-14129},\nit became necessary to fork and rename the project. This made it\npossible to improve and maintain the project for compatibility with the\nlatest versions of the Spring Framework and the Portlet API.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\href{http://www.liferay.com/}{Liferay} adopted Spring Portlet MVC in\nMarch of 2019 and the project was renamed to PortletMVC4Spring.\n\n\\noindent\\hrulefill\n\nFor more information on PortletMVC4Spring, see its dedicated\n\\href{/docs/7-2/appdev/-/knowledge_base/a/portletmvc4spring}{section of\narticles}. For specific information on migrating a portlet using Spring\nPortlet MVC to PortletMVC4Spring, see the\n\\href{/docs/7-2/appdev/-/knowledge_base/a/migrating-to-portletmvc4spring}{Migrating\nto PortletMVC4Spring} article.\n\nOnce you've migrated your portlet to leverage the PortletMVC4Spring\nframework, you must also adapt your Liferay-specific APIs and\ndependencies. To do this, complete the following steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt the portlet to 7.0's API using the Liferay Upgrade Planner. When\n  running the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues are autocorrected. For remaining issues, the planner\n  identifies code affected by the new API and ways to adapt it.\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolve\n  its dependencies}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  it}\n\\end{enumerate}\n\nAfter deploying the upgraded portlet, the server prints messages that\nindicate the following portlet status:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  WAR processing\n\\item\n  WAB startup\n\\item\n  Availability to users\n\\end{itemize}\n\nYou've migrated your Spring Portlet MVC portlet to the updated\nPortletMVC4Spring framework, updated any additional APIs and\ndependencies, and deployed it to your 7.0 instance. Your portlet's\nupgrade process is complete!\n\n\\chapter{Upgrading a Struts 1\nPortlet}\\label{upgrading-a-struts-1-portlet}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Portlets</p><p>Step 6 of 6</p>\n\\end{verbatim}\n\nStruts is a stable, widely adopted framework that implements the Model\nView Controller (MVC) design pattern. If you have a Struts portlet for\nprevious versions of Liferay Portal, you can upgrade it to 7.0.\n\nUpgrading Struts portlets to 7.0 is easier than you might think. Liferay\nDXP lets you continue working with Struts portlets as Java EE web\napplications.\n\nThis tutorial demonstrates how to upgrade a portlet that uses the Struts\n1 Framework.\n\nHere's a sample Struts portlet's folder structure with file/folder\ndescriptions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{sample-struts-portlet}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{docroot/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{html/portlet/sample\\_struts\\_portlet/} → JSPs\n    \\item\n      \\texttt{WEB-INF/}\n\n      \\begin{itemize}\n      \\tightlist\n      \\item\n        \\texttt{lib/} → Required third-party libraries unavailable in\n        the Liferay DXP system\n      \\item\n        \\texttt{src/}\n\n        \\begin{itemize}\n        \\tightlist\n        \\item\n          \\texttt{com/liferay/samplestruts/model/} → Model classes\n        \\item\n          \\texttt{com/liferay/samplestruts/servlet/} → Test servlet and\n          servlet context listener\n        \\item\n          \\texttt{com/liferay/samplestruts/struts/}\n\n          \\begin{itemize}\n          \\tightlist\n          \\item\n            \\texttt{action/} → \\texttt{Action} classes that return View\n            pages to the client\n          \\item\n            \\texttt{form/} → \\texttt{ActionForm} classes for model\n            interaction\n          \\item\n            \\texttt{render/} → \\texttt{Action} classes that present\n            additional pages and handle input\n          \\item\n            \\texttt{SampleException.java} → Exception class\n          \\end{itemize}\n        \\item\n          \\texttt{content/test/} → Resource bundles\n        \\item\n          \\texttt{META-INF/} → Javadoc\n        \\end{itemize}\n      \\item\n        \\texttt{tld/} → Tag library definitions\n      \\item\n        \\texttt{liferay-display.xml} → Sets the application category\n      \\item\n        \\texttt{liferay-plugin-package.properties} → Sets metadata and\n        portal dependencies\n      \\item\n        \\texttt{liferay-portlet.xml} → Maps descriptive role names to\n        roles\n      \\item\n        \\texttt{liferay-releng.properties} → (internal) Release\n        properties\n      \\item\n        \\texttt{portlet.xml} → Defines the portlet and its\n        initialization parameters and security roles\n      \\item\n        \\texttt{struts-config.xml} → Struts configuration\n      \\item\n        \\texttt{tiles-defs.xml} → Struts Tile definitions\n      \\item\n        \\texttt{validation.xml} → Defines form inputs for validation\n      \\item\n        \\texttt{validation-rules.xml} → Struts validation rules\n      \\item\n        \\texttt{web.xml} → Web application descriptor\n      \\end{itemize}\n    \\end{itemize}\n  \\item\n    \\texttt{build.xml} → Apache Ant build file\n  \\end{itemize}\n\\end{itemize}\n\nUpgrading a Struts 1 portlet involves these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt the portlet to 7.0's API using the Liferay Upgrade Planner. When\n  running the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues are autocorrected. For remaining issues, the planner\n  identifies code affected by the new API and ways to adapt it.\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolve\n  its dependencies}\n\\end{enumerate}\n\nYou've resolved the Sample Struts portlet's dependencies. It's ready to\ndeploy.\n\n\\noindent\\hrulefill\n\n\\textbf{Important}: Setting Portal property\n\\texttt{jsp.page.context.force.get.attribute} (described in the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#JSP}{JSP\nsection}) to \\texttt{true} (default) forces calls to\n\\texttt{com.liferay.taglib.servlet.PageContextWrapper\\#findAttribute(String)}\nto use \\texttt{getAttribute(String)}. Although this improves performance\nby avoiding unnecessary fall-backs, it can cause attribute lookup\nproblems in Struts portlets. To use Struts portlets in your sites, makes\nsure to set the Portal property\n\\texttt{jsp.page.context.force.get.attribute} to \\texttt{false} in a\nfile \\texttt{{[}Liferay-Home{]}/portal-ext.properties}.\n\n\\begin{verbatim}\njsp.page.context.force.get.attribute=false\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nOn\n\\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{deploying}\na Struts portlet Web Application aRchive (WAR), Liferay DXP's Web\nApplication Bundle (WAB) Generator creates an OSGi module (bundle) for\nthe portlet and installs it to Liferay's OSGi framework. The server\nprints messages indicating the following portlet status:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  WAR processing\n\\item\n  WAB startup\n\\item\n  Availability to users\n\\end{itemize}\n\nThe Struts portlet is now available on your Liferay DXP instance. The\nStruts portlet behaves just as it did on previous versions on your 7.0\nsite.\n\nCongratulations on upgrading your Struts portlet to 7.0!\n\n\\chapter{Upgrading Web Plugins}\\label{upgrading-web-plugins}\n\n\\begin{verbatim}\n<p id=\"stepTitle\">Upgrading Web Plugins</p><p>Step 1 of 1</p>\n\\end{verbatim}\n\nWeb plugins are regular\n\\href{https://docs.oracle.com/cd/E19226-01/820-7627/bnadx/index.html}{Java\nEE web modules} designed to work with Liferay DXP. These plugins were\nstored in the \\texttt{webs} folder of the legacy Plugins SDK.\n\nUpgrading a Liferay web plugin involves these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Adapt the plugin to 7.0's API using the Liferay Upgrade Planner. When\n  running the planner's \\emph{Fix Upgrade Problems} step, many of the\n  existing issues are autocorrected. For remaining issues, the planner\n  identifies code affected by the new API and ways to adapt it.\n\\item\n  \\href{/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies}{Resolve\n  its dependencies}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/deploying-a-project}{Deploy\n  it}\n\\end{enumerate}\n\nAfter deploying the upgraded portlet, the server prints messages that\nindicate the following portlet status:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  WAR processing\n\\item\n  WAB startup\n\\item\n  Availability to users\n\\end{itemize}\n\nYou've upgraded and deployed your Liferay web plugin on your 7.0\ninstance. Great job!\n\n\\chapter{Upgrading Ext Plugins}\\label{upgrading-ext-plugins}\n\nExt plugins let you use internal APIs and even let you overwrite Liferay\nDXP core files. This puts your deployment at risk of being incompatible\nwith security, performance, or feature updates released by Liferay. When\nupgrading to a new version of Liferay DXP, you must review all changes\nand manually modify your Ext projects to merge your changes with Liferay\nDXP's.\n\nDuring your upgrade to 7.0, it's highly recommended to leverage an\nextension point to customize Liferay DXP instead of using you existing\nExt plugin, if possible. 7.0 provides many extension points that let you\ncustomize almost every detail of Liferay DXP. If there's a way to\ncustomize what you want with an extension point, do it that way instead.\nSee\n\\href{/docs/7-2/customization/-/knowledge_base/c/finding-extension-points}{Finding\nExtension Points} for more details.\n\nFor more information on Ext projects, how to decide if you need one, and\nhow to manage them, see the\n\\href{/docs/7-2/customization/-/knowledge_base/c/customization-with-ext}{Customization\nwith Ext} section.\n\n\\chapter{Creating a Theme}\\label{creating-a-theme}\n\nThis tutorial takes you step-by-step through the process of creating a\ntheme. You'll create a responsive theme for Liferay's Lunar Resort that\ndemonstrates best practices and uses Liferay DXP's theme tools,\nextensions, and mechanisms. Several example files are referenced\nthroughout this tutorial. You can download the\n\\href{https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code}{\\texttt{lunar-resort-theme.zip}}\nif you want to follow along locally. The Lunar Resort theme's files are\nalso included in the\n\\href{https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme}{\\texttt{lunar-resort-theme}}\nfolder of the Liferay Docs repo, if you would rather view them there.\n\nThis tutorial covers these topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Generating the theme and configuring it to extend the Atlas base theme\n\\item\n  Customizing the Header and logo\n\\item\n  Customizing the Header navigation\n\\item\n  Customizing the Footer and embedding footer navigation\n\\item\n  Creating a color scheme variant\n\\end{itemize}\n\nBy the end of this tutorial, you'll be able to create the theme below:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-tutorial-finished-theme.png}\n\\caption{The finished Lunar Resort Theme uses Liferay DXP's tools to\nproduce a user-friendly UI that is maintainable.}\n\\end{figure}\n\n\\chapter{Setting up the Theme}\\label{setting-up-the-theme}\n\nIn this section, you'll use the Liferay JS Theme Toolkit's Liferay Theme\nGenerator to generate the theme's files. You'll complete these tasks:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Install the Liferay Theme Generator and its dependencies\n\\item\n  Generate a theme\n\\item\n  Configure the theme to extend the\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/customizing-atlas-and-clay-base-themes}{Atlas\n  base theme}.\n\\end{itemize}\n\nAtlas provides the look of the Classic theme. It builds on the default\nClay Base theme and provides additional styles.\n\nFollow these steps to generate and configure the theme:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Install the Theme Generator. Since you're developing a theme for 7.0,\n  install v9.x.x if it's not installed already. Run the command below:\n\n\\begin{verbatim}\nnpm install -g generator-liferay-theme@9.x.x\n\\end{verbatim}\n\\item\n  Install the Yeoman and gulp dependencies:\n\n\\begin{verbatim}\nnpm install -g yo gulp\n\\end{verbatim}\n\\item\n  Generate the starting theme with the Theme Generator. Enter\n  \\emph{Lunar Resort Theme} for the name and \\emph{lunar-resort} for the\n  ID, and answer no for the Font Awesome prompt. This theme uses Clay\n  icons instead:\n\n\\begin{verbatim}\nyo liferay-theme\n\\end{verbatim}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/theme-tutorial-yeoman-prompt.png}\n  \\caption{Answer no for the Font Awesome Prompt}\n  \\end{figure}\n\\item\n  To develop the theme you must copy the default files from the theme's\n  build and modify them. The \\texttt{/src/css/} folder and\n  \\texttt{\\_custom.scss} file are included by default. Run the command\n  below from the theme's root folder to build the files:\n\n\\begin{verbatim}\ngulp build\n\\end{verbatim}\n\\item\n  Create a new \\texttt{/src/templates/} folder and copy\n  \\texttt{portal\\_normal.ftl} from the \\texttt{build/templates/} folder\n  into it.\n\\item\n  Configure the theme to extend the Atlas theme. Add a\n  \\texttt{clay.scss} file to the theme's \\texttt{/src/css/} folder and\n  add the import shown below:\n\n\\begin{verbatim}\n@import \"clay/atlas\";\n\\end{verbatim}\n\\item\n  Create an \\texttt{\\_imports.scss} file in the \\texttt{/src/css/}\n  folder and add the imports shown below to it. This includes the\n  default imports and replaces the \\texttt{clay/base-variables} with the\n  Atlas base variables:\n\n\\begin{verbatim}\n@import \"bourbon\";\n\n@import \"mixins\";\n\n@import \"compat/mixins\";\n\n@import \"clay/atlas-variables\";\n\\end{verbatim}\n\\end{enumerate}\n\nYou've generated the theme, prepared it for development, and configured\nit to extend the Atlas theme. Continue to the next section to build the\nLunar Resort's Header and customize the logo.\n\n\\chapter{Customizing the Lunar Resort's Header and\nLogo}\\label{customizing-the-lunar-resorts-header-and-logo}\n\nThe Header contains the navigation and logo for the site. In this\nsection you'll customize the look and feel of the Header and add a\ncustom logo.\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{portal\\_normal.ftl} and replace the\n  \\texttt{\\textless{}header\\textgreater{}...\\textless{}/header\\textgreater{}}\n  element and contents with the updated code snippet below. This updates\n  the structure slightly, making the banner expand the full width of the\n  Header, and adds a new \\texttt{header\\_css\\_class} variable to the\n  \\texttt{class} attribute. This variable is defined in a later step.\n\n\\begin{verbatim}\n<header class=\"${header_css_class}\">\n    <div class=\"container-fluid\" id=\"banner\" role=\"banner\">\n        <a class=\"${logo_css_class}\" href=\"${site_default_url}\" title=\"<@liferay.language_format arguments=\"${site_name}\" key=\"go-to-x\" />\">\n            <img alt=\"${logo_description}\" height=\"${site_logo_height}\" src=\"${site_logo}\" width=\"${site_logo_width}\" />\n            <#if show_site_name>\n                ${site_name}\n            </#if>\n        </a>\n\n        <#if has_navigation>\n            <#include \"${full_templates_path}/navigation.ftl\" />\n        </#if>\n    </div>\n</header>\n\\end{verbatim}\n\\item\n  Replace the\n  \\texttt{\\textless{}div\\ class=\"container-fluid\"\\ id=\"wrapper\"\\textgreater{}}\n  element with the updated code below to remove some margins and\n  padding:\n\n\\begin{verbatim}\n<div class=\"container-fluid mt-0 pt-0 px-0\" id=\"wrapper\">\n\\end{verbatim}\n\n  And move the wrapper down, and place it directly above the\n  \\texttt{\\textless{}section\\ id=\"content\"\\textgreater{}} element:\n\n\\begin{verbatim}\n<div class=\"container-fluid mt-0 pt-0 px-0\" id=\"wrapper\">\n  <section id=\"content\">\n  ...\n  </section>\n  <footer...>\n  ...\n  </footer>\n</div>\n\\end{verbatim}\n\\item\n  The logo's height is retrieved with the\n  \\texttt{\\$\\{site\\_logo\\_height\\}} variable. The height of the logo is\n  a bit too large for the Lunar Resort theme, so you must adjust it.\n  Remove the \\texttt{width} attribute from the logo's image so it\n  defaults to \\texttt{auto}:\n\n\\begin{verbatim}\n<img alt=\"${logo_description}\" height=\"${site_logo_height}\" src=\"${site_logo}\" />\n\\end{verbatim}\n\\item\n  Create \\texttt{init\\_custom.ftl} in your theme's\n  \\texttt{/src/templates/} folder and assign the logo's\n  \\texttt{site\\_logo\\_height} variable to the value below:\n\n\\begin{verbatim}\n<#assign site_logo_height = 56 />\n\\end{verbatim}\n\\item\n  Assign the new \\texttt{header\\_css\\_class} variable you added in step\n  one to the value below:\n\n\\begin{verbatim}\n<\n#assign header_css_class = \n\"navbar navbar-expand-md navbar-dark flex-column flex-md-row bd-navbar\" \n/>\n\\end{verbatim}\n\n  This applies Bootstrap and Clay utility classes to provide the overall\n  look and feel of the Header. Assigning the classes to a variable keeps\n  \\texttt{portal\\_normal} clean and makes the code easy to maintain. If\n  you want to update the classes, you just have to modify the variable\n  (e.g.~\\texttt{header\\_css\\_class\\ =\\ header\\_css\\_class\\ +\\ \"\\ my-new-class\"}).\n\\item\n  Add the code snippet below to update the \\texttt{logo\\_css\\_class}\n  variable to use Bootstrap's \\texttt{navbar-brand} class:\n\n\\begin{verbatim}\n<#assign logo_css_class = logo_css_class + \" navbar-brand\" />\n\\end{verbatim}\n\\item\n  Before you upload the theme to see what it looks like so far, you must\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-a-thumbnail-preview-for-your-theme}{create\n  a theme thumbnail} so you can identify it. To save time, copy the\n  \\texttt{thumbnail.png} asset from the\n  {[}\\texttt{lunar-resort-build/assets{]}(./images/}{]}(https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets\n  folder to a new \\texttt{/src{]}(./images/} folder. Note that its\n  dimensions are 480px by 270px. These dimensions are required to\n  display the theme thumbnail properly.\n\\item\n  The theme isn't complete yet, but you'll deploy what you have so you\n  can replace the default logo with the Lunar Resort logo. Enable\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes}{Developer\n  Mode} before deploying your theme, so the theme's files are not cached\n  for future deployments. Start the server, if it's not already started,\n  and deploy the theme with the command below:\n\n\\begin{verbatim}\ngulp deploy\n\\end{verbatim}\n\\item\n  Before you configure the pages, you must import the Lunar Resort's\n  pages. Open the Control Menu and navigate to \\emph{Publishing} →\n  \\emph{Import}. Click the Plus button to create a new import process.\n  Click \\emph{Select File} and import the\n  \\texttt{lunar\\_resort\\_pages.lar} from the\n  \\href{https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets}{\\texttt{lunar-resort-build/assets/}}\n  folder. Keep the default settings and click \\emph{Import}.\n\\item\n  Open the Control Menu and navigate to \\emph{Site Builder} →\n  \\emph{Pages}. Click the Gear icon next to \\emph{Public Pages} to open\n  the configuration menu. Under the \\emph{Look and Feel} tab, scroll\n  down and click the \\emph{Change Current Theme} button and select the\n  Lunar Resort Theme. Scroll to the Logo heading, click the\n  \\emph{Change} button, upload the \\texttt{lunar-resort-logo.png} asset\n  from the\n  {[}\\texttt{lunar-resort-build/assets{]}(./images/}{]}(https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets\n  folder, and click the \\emph{Save} button to apply the theme and logo.\n\\end{enumerate}\n\nGreat! You've customized the Lunar Resort's Header and applied a custom\nlogo. Next, you'll configure and customize the theme's navigation.\n\n\\chapter{Customizing the Navigation}\\label{customizing-the-navigation}\n\nNavigation items (pages) are defined and configured in Liferay DXP. The\nNavigation template iterates through the existing navigation items\n(pages) and assigns the template's markup for each of them. Page updates\ntherefore require no updates to the theme directly and can be made by a\nSite Administrator, thus reducing the maintenance costs.\n\nTo customize the navigation, you can either use the default navigation\nprovided in \\texttt{navigation.ftl} and customize the markup template,\nor you can embed the navigation portlet in the theme and customize its\npreferences. Both approaches use the same overall markup. This section\ntakes the former approach and customizes the default configuration in\n\\texttt{navigation.ftl}. in the next section, you'll embed the\nnavigation portlet in the Footer and configure its preferences to only\ndisplay the top level (parent) navigation items.\n\nFollow these steps to configure the Header's navigation:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Copy the default \\texttt{navigation.ftl} file from the\n  \\texttt{/src/build/templates/} folder into the theme's\n  \\texttt{/src/templates/} folder. The \\texttt{build} folder was\n  generated when you built the theme and again when you initially\n  deployed the theme in the last section.\n\\item\n  By default, the User Personal Bar is hidden from the theme. You can\n  either enable this via System Settings outside the scope of the theme,\n  or you can include it in your theme. In this case, you'll include it\n  in the theme. Open the \\texttt{navigation.ftl} template you just\n  copied and add this User Personal Bar markup to the top:\n\n\\begin{verbatim}\n<div class=\"mx-1 mx-sm-3 order-md-1 lunar-user\">\n    <@liferay.user_personal_bar />\n</div>\n\\end{verbatim}\n\n  along with some utility classes to position and order the User\n  Personal Bar, this also adds a custom \\texttt{lunar-user} class, which\n  you'll use later for styling.\n\\item\n  Modify the default template to use Bootstrap's \\texttt{navbar} format.\n  Wrap the\n  \\texttt{\\textless{}nav\\textgreater{}...\\textless{}/nav\\textgreater{}}\n  element with the \\texttt{\\textless{}div\\textgreater{}} shown below:\n\n\\begin{verbatim}\n<div class=\"collapse navbar-collapse\" id=\"lunarNav\">\n  <nav ... >\n  </nav>\n</div>\n\\end{verbatim}\n\\item\n  Open the \\texttt{portal\\_normal.ftl} template and find this\n  conditional wrapper:\n\n\\begin{verbatim}\n<#if has_navigation>\n  <#include \"${full_templates_path}/navigation.ftl\" />\n</#if>\n\\end{verbatim}\n\n  Update the conditional to include the menu toggler for the mobile\n  navigation. This targets the \\texttt{\\#lunarNav} wrapper that you\n  added in the previous step:\n\n\\begin{verbatim}\n<#if has_navigation>\n  <button \n    aria-controls=\"navigation\" \n    aria-expanded=\"false\" \n    class=\"btn-monospaced ml-auto navbar-toggler\" \n    data-target=\"#lunarNav\" \n    data-toggle=\"collapse\" \n    type=\"button\">\n    <span class=\"navbar-toggler-icon\"></span>\n  </button>\n  <#include \"${full_templates_path}/navigation.ftl\" />\n</#if>\n\\end{verbatim}\n\\item\n  Open \\texttt{navigation.ftl} and add the \\texttt{navbar-nav} and\n  \\texttt{mr-auto} classes to the \\texttt{\\textless{}ul\\textgreater{}}\n  element at the top:\n\n\\begin{verbatim}\n<ul aria-label=\"<@liferay.language key=\"site-pages\" />\" class=\"navbar-nav mr-auto\" role=\"menubar\">\n\\end{verbatim}\n\\item\n  Open \\texttt{navigation.ftl} and replace the first\n  \\texttt{\\textless{}\\#assign...\\ /\\textgreater{}} declaration with the\n  one below. This adds the \\texttt{nav-item} class to the\n  \\texttt{nav\\_item\\_css\\_class} variable declaration in\n  \\texttt{navigation.ftl} and declares a new \\texttt{nav\\_item\\_caret}\n  variable:\n\n\\begin{verbatim}\n<#assign\n  nav_item_attr_has_popup = \"\"\n  nav_item_css_class = \"nav-item\"\n  nav_item_layout = nav_item.getLayout()\n  nav_item_caret = \"\"\n/>\n\\end{verbatim}\n\\item\n  Replace the \\texttt{nav\\_item.isSelected} conditional block with the\n  one shown below. This adds the \\texttt{selected} class to the existing\n  \\texttt{nav\\_item\\_css\\_class} classes:\n\n\\begin{verbatim}\n<#if nav_item.isSelected()>\n  <#assign\n    nav_item_attr_has_popup = \"aria-haspopup='true'\"\n    nav_item_css_class = \"${nav_item_css_class} selected\"\n  />\n</#if>\n\\end{verbatim}\n\\item\n  The Lunar Resort contains nested pages (child navigation items). By\n  default, child navigation items are displayed at the block level.\n  Instead, the Administrator wants to display these items in a dropdown\n  list that is only displayed on hover of the parent navigation item.\n  Add this conditional block directly below the\n  \\texttt{nav\\_item.isSelected} block you just modified. This adds the\n  \\texttt{dropdown} class to the parent navigation item and updates the\n  \\texttt{nav\\_item\\_caret} variable to hold Clay caret icon markup to\n  indicate the parent navigation has nested child items:\n\n\\begin{verbatim}\n<#if nav_item.hasChildren()>\n  <#assign\n    nav_item_css_class = \"${nav_item_css_class} dropdown\"\n    nav_item_caret = '<svg class=\"lexicon-icon\">\n    <use xlink:href=\"${images_folder}/lexicon/icons.svg#caret-bottom\" />\n    </svg>'\n  />\n</#if>\n\\end{verbatim}\n\\item\n  Locate the anchor's markup below:\n\n\\begin{verbatim}\n<a aria-labelledby=\"layout_${nav_item.getLayoutId()}\" \n${nav_item_attr_has_popup} \nhref=\"${nav_item.getURL()}\" \n${nav_item.getTarget()} \nrole=\"menuitem\"\n>\n  <span>\n    <@liferay_theme[\"layout-icon\"] layout=nav_item_layout /> \n    ${nav_item.getName()}\n  </span>\n</a>\n\\end{verbatim}\n\n  Replace it with the updated markup shown below to include the\n  \\texttt{\\$\\{nav\\_item\\_caret\\}} variable:\n\n\\begin{verbatim}\n<a \n  aria-labelledby=\"layout_${nav_item.getLayoutId()}\" \n  class=\"nav-link\" ${nav_item_attr_has_popup} \n  href=\"${nav_item.getURL()}\" \n  ${nav_item.getTarget()} \n  role=\"menuitem\"\n>\n  <span>\n    <@liferay_theme[\"layout-icon\"] layout=nav_item_layout /> \n    ${nav_item.getName()}\n  </span> \n  ${nav_item_caret}\n</a>\n\\end{verbatim}\n\\item\n  Add the \\texttt{dropdown-menu} class to the\n  \\texttt{\\textless{}ul\\ class=\"child-menu\"\\ role=\"menu\"\\textgreater{}}\n  element and replace the \\texttt{nav\\_child\\_css\\_class} variable\n  declarations with the ones below to add the \\texttt{nav-item} class to\n  them:\n\n\\begin{verbatim}\n<#assign\n  nav_child_css_class = \"nav-item\"\n/>\n\n<#if nav_item.isSelected()>\n  <#assign\n    nav_child_css_class = \"nav-item selected\"\n  />\n</#if>\n\\end{verbatim}\n\\item\n  Find the \\texttt{\\textless{}a\\textgreater{}} element with the\n  \\texttt{aria-labelledby=\"layout\\_\\$\\{nav\\_child.getLayoutId()\\}\"}\n  attribute and add the \\texttt{class=\"nav-link\"} attribute to it:\n\\end{enumerate}\n\n\\begin{verbatim}\n```markup\n<a aria-labelledby=\"layout_${nav_child.getLayoutId()}\" class=\"nav-link\"...></a>\n```\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{11}\n\\tightlist\n\\item\n  Add a call to action for the visitors to the Lunar Resort site so they\n  can book their flight. Add the book now button's code below the\n  closing \\texttt{\\textless{}/nav\\textgreater{}} element. This uses some\n  utility classes for the basic look and feel and ordering, as well as a\n  custom \\texttt{btn-orange} class that you'll provide styling for\n  later:\n\\end{enumerate}\n\n\\begin{verbatim}\n```html\n<a aria-controls=\"book-now\" class=\"btn text-white btn-orange order-md-2\">\n    <p class=\"book-now-text mb-0\">Book Now</p>\n</a>\n```\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{12}\n\\item\n  The Lunar Resort's color scheme is comprised of three colors: orange,\n  white, and blue. Since these colors are used throughout the theme,\n  you'll store them in SASS variables in a separate file. Create a new\n  file called \\texttt{\\_colors.scss} inside the theme's\n  \\texttt{/src/css/} folder and add these variables to it. Note that\n  White is already defined as the global variable \\texttt{\\$white} by\n  the Atlas theme.\n\n\\begin{verbatim}\n$lunar-resort-orange: #dfa356;\n$lunar-resort-blue: #415fa7;\n$lunar-resort-link-teal: #00ccFF;\n\\end{verbatim}\n\\item\n  Now that the main colors are defined, open\n  \\texttt{/src/css/\\_custom.scss} and add the code snippet below. This\n  imports the \\texttt{\\_colors.scss} file so you can use the variables\n  you just created. It adds some basic styling for the Header and\n  navigation, including a style to highlight the page that is currently\n  active via the \\texttt{selected} class. It also displays the child\n  menu items at the block level on smaller devices with the\n  \\texttt{@include\\ media-breakpoint-down} breakpoint:\n\n\\begin{verbatim}\n@import 'colors';\n\nbody {\n\n  a.btn-orange {\n    background-color: $lunar-resort-orange;\n    margin-right: 5px;\n\n    &:hover {\n      border-color: $white;\n    }\n\n    @include media-breakpoint-down(sm){\n      width: 100%;\n    }\n  }\n\n  header {\n    background-color: $lunar-resort-blue;\n\n    .lunar-user a {\n        color: $lunar-resort-link-teal;\n    }\n\n    .user-avatar-link .lexicon-icon {\n      color: $lunar-resort-blue;\n    }\n\n    li.nav-item {\n      & a.nav-link span {\n        font-size: 1.5em;\n      }\n\n      &:hover ul.child-menu {\n        background-color: $lunar-resort-blue;\n        display: block;\n        margin-top: -10px;\n      }\n\n      &.selected {\n        background-color: $white;\n        height: 73px;\n        & a.nav-link {\n          color: $lunar-resort-blue;\n          font-weight: bold;\n          &:hover {\n            color: $lunar-resort-blue;\n            font-weight: normal;\n            padding-left: 9.619px;\n            padding-right: 9.619px;\n          }\n        }\n      }\n\n      @include media-breakpoint-down(sm){\n        ul.child-menu {\n          display: block;\n        }\n      }    \n    }\n  }\n}\n\\end{verbatim}\n\\item\n  The Control Menu is displayed on top of everything when the user is\n  signed in, which covers the Header. You must update the\n  \\texttt{navigation.ftl} template to account for the Control Menu.\n  Liferay DXP provides a unique class that is added to the \\texttt{body}\n  of the page when each product navigation (which includes the Control\n  Menu) is visible. Use the \\texttt{has-control-menu} class is added to\n  the body when the Control Menu is visible. Open \\texttt{\\_custom.scss}\n  and add this code snippet just above the closing bracket for the\n  \\texttt{body} to add a top margin to the Header that's equal to the\n  height of the Control Menu:\n\n\\begin{verbatim}\n&.has-control-menu {\n  header {\n    margin-top: 56px;  \n  }\n}\n\\end{verbatim}\n\\item\n  The Control Menu's height is slightly smaller on mobile devices, so\n  you must account for that responsiveness in your styling. Update the\n  code snippet you just added to match the one below:\n\n\\begin{verbatim}\n&.has-control-menu {\n  header {\n    margin-top: 56px;\n    @include media-breakpoint-down(sm){\n      margin-top: 48px;\n    }  \n  }\n}\n\\end{verbatim}\n\\end{enumerate}\n\nGreat! The Header's navigation is customized. The updated Header and\nlogo should look like the figure below:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-tutorial-updated-navigation.png}\n\\caption{The updated Header and navigation are much more user-friendly\nnow.}\n\\end{figure}\n\nNext, you'll define the Footer and embed a navigation portlet to display\nnavigation.\n\n\\chapter{Defining the Lunar Resort's Footer and Footer\nNavigation}\\label{defining-the-lunar-resorts-footer-and-footer-navigation}\n\nYou've configured the Header and its navigation, but at the moment the\nFooter is a bit bare bones. In this section, you'll update the Footer to\ninclude contact information for the Lunar Resort and include navigation\nwith an embedded navigation portlet.\n\nFollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  To keep the Portal Normal template uncluttered, create a separate\n  template to hold the Footer's markup. Create a new file called\n  \\texttt{footer.ftl} in the theme's \\texttt{/src/templates/} folder.\n\\item\n  Copy the Footer markup (shown below) from \\texttt{portal\\_normal.ftl}\n  into \\texttt{footer.ftl}:\n\n\\begin{verbatim}\n<footer id=\"footer\" role=\"contentinfo\">\n    <p class=\"powered-by\">\n        <@liferay.language key=\"powered-by\" /> <a href=\"http://www.liferay.com\" rel=\"external\">Liferay</a>\n    </p>\n</footer>\n\\end{verbatim}\n\n  And update the \\texttt{\\textless{}p\\textgreater{}} element in\n  \\texttt{footer.ftl} to include the classes shown below:\n\n\\begin{verbatim}\n<footer id=\"footer\" role=\"contentinfo\">\n    <p class=\"powered-by text-center text-white py-3 mb-0\">\n        <@liferay.language key=\"powered-by\" /> <a href=\"http://www.liferay.com\" rel=\"external\">Liferay</a>\n    </p>\n</footer>\n\\end{verbatim}\n\\item\n  Add this \\texttt{@liferay.navigation\\_menu} macro snippet above the\n  \\texttt{powered-by} paragraph to embed the navigation portlet. This\n  configuration stores the portlet preferences in a\n  \\texttt{preferencesMap} variable. The \\texttt{displayDepth} of\n  \\texttt{1} specifies that the portlet must only render the top-level\n  parent navigation, and \\texttt{portletSetupPortletDecoratorId} sets\n  the portlet decorator to \\texttt{barebone}, which removes the\n  portlet's wrapper and only renders the portlet's content:\n\n\\begin{verbatim}\n<nav id=\"navbarFooter\">\n    <div class=\"text-center mx-auto\">\n        <div class=\"nav text-uppercase\" role=\"menubar\">\n            <#assign preferencesMap = {\"displayDepth\": \"1\", \"portletSetupPortletDecoratorId\": \"barebone\"} />\n\n            <@liferay.navigation_menu\n                default_preferences=freeMarkerPortletPreferences.getPreferences(preferencesMap)\n                instance_id=\"footer_navigation_menu\"\n            />\n        </div>\n    </div>\n</nav>\n\\end{verbatim}\n\\item\n  The visitors need some social media links so they can keep tabs on the\n  latest and greatest news from the Lunar Resort. Replace the snippet\n  you just added with the one below. This uses\n  \\href{https://clayui.com/docs/components/icons.html}{Clay icons} and\n  adds a wrapper to prepare for the next step.\n\n\\begin{verbatim}\n<div id=\"navbarContactWrapper\" class=\"row mx-0\">\n  <nav id=\"navbarFooter\" class=\"col-12 col-md-6 pt-5\">\n    <div id=\"socialMediaWrapper\" class=\"col-12 col-md-4 text-center mx-auto mb-4\">\n      <h2 class=\"nav-heading\">\n          Follow Us\n      </h2>\n        <div id=\"socialMediaLinks\">\n          <ul class=\"nav flex-row mx-auto\">\n              <li class=\"mx-2\">\n                  <div id=\"facebook\"><a class=\"text-white\"\n                  href=\"http://www.facebook.com/pages/Liferay/45119213107\" \n                  target=\"_blank\"><span class=\"hide\">Facebook</span>\n                  <@clay[\"icon\"] symbol=\"social-facebook\" />\n                  </a></div>\n              </li>\n              <li class=\"mx-2\">\n                  <div id=\"twitter\"><a class=\"text-white\" \n                  href=\"http://www.twitter.com/liferay\" \n                  target=\"_blank\"><span class=\"hide\">Twitter</span>\n                  <@clay[\"icon\"] symbol=\"twitter\" />\n                  </a></div>\n              </li>\n              <li class=\"mx-2\">\n                  <div id=\"linked-in\"><a class=\"text-white\"\n                  href=\"http://www.linkedin.com/company/83609\" \n                  target=\"_blank\"><span class=\"hide\">LinkedIn</span>\n                  <@clay[\"icon\"] symbol=\"social-linkedin\" />\n                  </a></div>\n              </li>\n              <li class=\"mx-2\">\n                  <div id=\"youtube\"><a class=\"text-white\"\n                  href=\"http://www.youtube.com/user/liferayinc\" \n                  target=\"_blank\"><span class=\"hide\">YouTube</span>\n                  <@clay[\"icon\"] symbol=\"video\" />\n                  </a></div>\n              </li>\n          </ul>\n        </div>\n    </div>\n    <div class=\"text-center mx-auto\">\n      <div class=\"nav text-uppercase\" role=\"menubar\">\n        <#assign preferencesMap = {\"displayDepth\": \"1\", \"portletSetupPortletDecoratorId\": \"barebone\"} />\n\n        <@liferay.navigation_menu\n          default_preferences=freeMarkerPortletPreferences.getPreferences(preferencesMap)\n          instance_id=\"footer_navigation_menu\"\n        />\n      </div>\n    </div>\n  </nav>\n</div>\n\\end{verbatim}\n\\item\n  Add this snippet below the closing\n  \\texttt{\\textless{}/nav\\textgreater{}} tag to add the Lunar Resort's\n  contact information. Also, copy the\n  \\texttt{lunar-resort-logo-vertical.png} asset from the\n  {[}\\texttt{lunar-resort-build/assets{]}(./images/}{]}(https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets\n  folder to the \\texttt{/src{]}(./images/} folder so you can use it in\n  the Footer:\n\n\\begin{verbatim}\n<div class=\"contact-info-container text-center pt-5 pb-2 col-12 col-md-4 mx-auto mb-4\">\n  <img alt=\"lunar-resort-logo\" height=\"90\" class=\"mb-2\" src=\"${images_folder}/lunar-resort-logo-vertical.png\" />\n  <div id=\"contactTextWrapper\" class=\"row mx-0\">\n    <p class=\"col-12 col-md-6\">\n      123 Mare Nectaris Lane<br>\n      Mare Nectaris, Moon Colony 10010<br>\n    </p>\n    <p class=\"col-12 col-md-6\">\n      Tel: 4-919-843-6666<br>\n      Fax: 4-919-843-6667<br>\n      <a href=\"mailto:info@lunarresort.com\">info@thelunarresort.com</a>\n    </p>\n  </div>\n</div>\n\\end{verbatim}\n\\item\n  The Administrator doesn't want to display the Footer on every page, so\n  she would like the option to hide it. To do that, create a\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/making-configurable-theme-settings}{theme\n  setting} to optionally show the Footer. Open your theme's\n  \\texttt{/src/WEB-INF/liferay-look-and-feel.xml} file and add this\n  snippet just below the\n  \\texttt{\\textless{}template-extension\\textgreater{}ftl\\textless{}/template-extension\\textgreater{}}\n  entry. This renders a togglable \\emph{Show Footer} option in the\n  \\emph{Look and Feel} section for the theme's configuration.\n\n\\begin{verbatim}\n<settings>\n  <setting configurable=\"true\" key=\"show-footer\" type=\"checkbox\" value=\"true\" />\n</settings>\n\\end{verbatim}\n\\item\n  Now you must define a FreeMarker variable to store the value of the\n  \\texttt{show-footer} theme setting so you can check for it in\n  \\texttt{portal\\_normal.ftl}. Open \\texttt{init\\_custom.ftl} and add\n  the variable declaration below to set the \\texttt{show\\_footer}\n  variable to the value (true or false) of the \\texttt{show-footer}\n  theme setting:\n\n\\begin{verbatim}\n<#assign\n  show_footer = getterUtil.getBoolean(themeDisplay.getThemeSetting(\"show-footer\"))\n/>\n\\end{verbatim}\n\\item\n  Open \\texttt{portal\\_normal.ftl} and replace the Footer markup with\n  the code snippet below to include the Footer template when the\n  \\texttt{show-footer} theme setting is \\texttt{true}:\n\n\\begin{verbatim}\n<#if show_footer>\n    <#include \"${full_templates_path}/footer.ftl\" />\n</#if>\n\\end{verbatim}\n\\item\n  Open \\texttt{\\_custom.scss} and add this snippet above the\n  \\texttt{\\&.has-control-menu} styling to style the Footer:\n\n\\begin{verbatim}\n#footer {\n  background-color: $lunar-resort-blue;\n  color: $white;\n  ul {\n    margin-left: auto;\n    margin-right: auto;\n\n    &.navbar-nav {\n      width: 410px;\n      .nav-item.hover:after {\n        width: auto;\n      }\n\n      a {\n        color: $white;\n        @include media-breakpoint-down(sm) {\n          padding-left: 6px;\n          padding-right: 6px;\n        }\n      }\n    }\n  }\n\n  #socialMediaWrapper ul {\n    width: 192px;\n\n    li a {\n      font-size: 2rem;\n    }\n  }\n\n  p.powered-by a, .contact-info-container a {\n      color: $lunar-resort-link-teal;\n  }\n\n}\n\\end{verbatim}\n\\item\n  The majority of the Lunar Resort's content is provided with\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/page-fragments}{Fragments}.\n  Since Fragments are out of the scope of this tutorial, you'll upload\n  the completed fragments. Open the Control Menu and navigate to\n  \\emph{Site Builder} → \\emph{Page Fragments}, and select the\n  \\emph{Import} option from the New dropdown menu. Import the\n  \\texttt{collections-lunar-resort.zip} asset from the\n  \\href{https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets}{\\texttt{lunar-resort-build/assets/}}\n  folder.\n\\item\n  Re-deploy the updated theme with the command below:\n\n\\begin{verbatim}\ngulp deploy\n\\end{verbatim}\n\\end{enumerate}\n\nThe updated Footer and navigation should look like the figure below:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/theme-tutorial-updated-footer.png}\n\\caption{The updated Footer provides everything visitors need to follow\nand contact the Lunar Resort.}\n\\end{figure}\n\nIn the next section you'll learn how to create a color scheme for the\nLunar Resort.\n\n\\chapter{Adding a Color Scheme Variant for the Lunar Resort\nTheme}\\label{adding-a-color-scheme-variant-for-the-lunar-resort-theme}\n\nIn this section, you'll create a color scheme variant for the Lunar\nResort Theme to apply during the Lunar Eclipse, when special discounts\nare available. You'll create a color scheme that reflects the reds and\nyellows present during a lunar eclipse. Since the majority of the Lunar\nResort Site's content is created with page fragments, you must account\nfor the color scheme styles in the page fragments as well. Follow these\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the theme's \\texttt{WEB-INF/liferay-look-and-feel.xml} file and\n  add these color-scheme entries above the\n  \\texttt{\\textless{}portlet-decorator\\textgreater{}..\\textless{}/portlet-decorator\\textgreater{}}\n  ones:\n\n\\begin{verbatim}\n<theme id=\"my-theme-id\" name=\"My Theme Name\">\n  <template-extension>ftl</template-extension>\n  <color-scheme id=\"01\" name=\"Default\">\n        <default-cs>true</default-cs>\n        <css-class>default</css-class>\n        <color-scheme-images-path>\n                ${images-path}/color_schemes/${css-class}\n        </color-scheme-images-path>\n  </color-scheme>\n  <color-scheme id=\"02\" name=\"Eclipse\">\n    <css-class>eclipse</css-class>\n  </color-scheme>\n  ...\n</theme>\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Color schemes are sorted alphabetically by `name` rather than \n `id`. For example, a color scheme named `Clouds` and `id` `02` would be \n selected by default over a color scheme named `Day` with `id` `01`. The \n `<default-cs>` element overrides the alphabetical sorting and sets the \n color scheme that is selected by default when the theme is chosen.  \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Open \\texttt{/src/css/\\_colors.scss} and update the colors to include\n  the two new ones (eclipse yellow and eclipse red) for the color\n  scheme:\n\n\\begin{verbatim}\n$lunar-resort-orange: #dfa356;\n$lunar-resort-blue: #415fa7;\n$lunar-resort-link-teal: #00ccFF;\n$lunar-resort-eclipse-yellow: #dfd456;\n$lunar-resort-eclipse-red: #a75441;\n\\end{verbatim}\n\\item\n  Create a \\texttt{/src/css/color\\_schemes/} folder for the color\n  scheme, and add a \\texttt{eclipse.scss} file to it for the Eclipse\n  color scheme. The default color scheme's styles are included in\n  \\texttt{\\_custom.scss}, so you don't need to create anything for them.\n\\item\n  The color scheme's class is added to the\n  \\texttt{\\textless{}body\\textgreater{}} element when the theme's color\n  scheme is applied, so you must prefix the body styles with the\n  \\texttt{eclipse} class to target the proper color scheme. Open\n  \\texttt{/src/css/color\\_schemes/eclipse.scss} and add this import and\n  styles to it to use the new colors you defined:\n\n\\begin{verbatim}\n@import '../colors';\n\nbody.eclipse {\n\n  a.btn-orange {\n    background-color: $lunar-resort-eclipse-yellow;\n  }\n\n  header {\n    background-color: $lunar-resort-eclipse-red;\n\n    .user-avatar-link .lexicon-icon {\n      color: $lunar-resort-eclipse-red;\n    }\n\n    li.nav-item {\n\n      ul.child-menu {\n        background-color: $lunar-resort-eclipse-red;\n      }\n\n      &:hover ul.child-menu {\n        background-color: $lunar-resort-eclipse-red;\n      }\n\n      &.selected {\n        & a.nav-link {\n          color: $lunar-resort-eclipse-red;\n          &:hover {\n            color: $lunar-resort-eclipse-red;\n          }\n        }\n      }\n    }\n  }\n\n  #footer {\n    background-color: $lunar-resort-eclipse-red;\n  }\n\n}\n\\end{verbatim}\n\\item\n  Import the eclipse color scheme's CSS file into \\texttt{\\_custom.scss}\n  so it's loaded with the rest of the custom styles:\n\n\\begin{verbatim}\n@import \"color_schemes/eclipse\";\n\\end{verbatim}\n\\item\n  You must create thumbnails for each color scheme, just like you did\n  the theme. To save time, copy the\n  {[}\\texttt{lunar-resort-build/assets{]}(./images/color\\_schemes/}{]}(https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets\n  folder to the theme's \\texttt{/src{]}(./images/} folder. Note that the\n  color scheme folder names match the color scheme CSS class names\n  defined in \\texttt{liferay-look-and-feel.xml}.\n\\item\n  Now that the color scheme is created, you must update the page\n  fragments to use the eclipse color scheme class so they have the same\n  look as the color scheme when it's applied to the page. The fragments\n  don't have access to the SASS color variables, so you must use the\n  hexadecimal color codes. To save time, import the updated page\n  fragments from the \\texttt{lunar-resort-build/assets/} folder. Open\n  the Control Menu and navigate to \\emph{Site Builder} → \\emph{Page\n  Fragments}, click the Actions menu next to COLLECTIONS, and select the\n  \\emph{Import} option. Import the\n  \\texttt{collections-lunar-resort-color-scheme.zip} asset from the\n  \\href{https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets}{\\texttt{lunar-resort-build/assets/}}\n  folder. Note that each fragment style that requires a color change is\n  duplicated and prefixed with \\texttt{body.eclipse}. A couple example\n  configurations are shown below:\n\n\\begin{verbatim}\n.fragment_35201 h3.text-center {\n  background-color: #dfa356;\n  color: #FFF;\n}\n\nbody.eclipse .fragment_35201 h3.text-center {\n  background-color: #dfd456;\n}\n\\end{verbatim}\n\n\\begin{verbatim}\n.fragment_35201 a.btn {\n  background-color: #415fa7;\n}\n\nbody.eclipse .fragment_35201 a.btn {\n  background-color: #a75441;\n}\n\\end{verbatim}\n\\item\n  Deploy the theme. Open the Control Menu, navigate to \\emph{Site\n  Builder} → \\emph{Pages}, and click the Gear icon next to \\emph{Public\n  Pages}. Select the Eclipse color scheme under the \\emph{LOOK AND FEEL}\n  tab and save to apply the changes.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/theme-tutorial-color-schemes.png}\n  \\caption{Color schemes are a good way to subtly change the look and\n  feel of your site.}\n  \\end{figure}\n\n  The theme should look like the figure below with the Eclipse color\n  scheme applied:\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/theme-tutorial-eclipse-color-scheme.png}\n  \\caption{The finished color scheme gives the Lunar Resort site a fiery\n  glow.}\n  \\end{figure}\n\\end{enumerate}\n\nGreat! You've seen how you can quickly change the look and feel of the\nLunar Resort with just a simple color scheme. Now you know how to\ndevelop a theme to customize the overall look and feel of your site!\n\nSee the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{Themes\nsection} for information on developing themes.\n"
  },
  {
    "path": "book/developing-liferay-dxp-72.tex",
    "content": "\\documentclass[11pt,openright,twoside]{memoir}\n\n% \\usepackage{ucs}\n\\usepackage[english]{babel}\n\\usepackage{fontspec}\n\\usepackage{graphicx}\n\\usepackage{calc}\n\\usepackage{hyperref}\n% \\usepackage{enumerate}\n\\usepackage{enumitem}\n\\usepackage{ctable}\n%\\usepackage[labelformat=empty,font=small]{caption}\n\\usepackage{titlesec}\n\\usepackage{float}\n\\usepackage{morefloats}\n\\usepackage{wrapfig}\n\\usepackage{longtable}\n\\usepackage{geometry}\n\\usepackage{framed}\n\\defaultfontfeatures{Ligatures=TeX}\n\n% Begin stuff from Pandoc template \n\n\\usepackage{amsmath,amssymb}\n\\usepackage{iftex}\n\\ifPDFTeX\n  \\usepackage[T1]{fontenc}\n  \\usepackage[utf8]{inputenc}\n  \\usepackage{textcomp} % provide euro and other symbols\n\\else % if luatex or xetex\n  \\usepackage{unicode-math} % this also loads fontspec\n  \\defaultfontfeatures{Scale=MatchLowercase}\n  \\defaultfontfeatures[\\rmfamily]{Ligatures=TeX,Scale=1}\n\\fi\n\\usepackage{lmodern}\n\\ifPDFTeX\\else\n  % xetex/luatex font selection\n\\fi\n% Use upquote if available, for straight quotes in verbatim environments\n\\IfFileExists{upquote.sty}{\\usepackage{upquote}}{}\n\\IfFileExists{microtype.sty}{% use microtype if available\n  \\usepackage[]{microtype}\n  \\UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts\n}{}\n\\makeatletter\n\\@ifundefined{KOMAClassName}{% if non-KOMA class\n  \\IfFileExists{parskip.sty}{%\n    \\usepackage{parskip}\n  }{% else\n    \\setlength{\\parindent}{0pt}\n    \\setlength{\\parskip}{6pt plus 2pt minus 1pt}}\n}{% if KOMA class\n  \\KOMAoptions{parskip=half}}\n\\makeatother\n\\usepackage{xcolor}\n\\usepackage{color}\n\\usepackage{fancyvrb}\n\\newcommand{\\VerbBar}{|}\n\\newcommand{\\VERB}{\\Verb[commandchars=\\\\\\{\\}]}\n\\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\{\\}}\n% Add ',fontsize=\\small' for more characters per line\n\\newenvironment{Shaded}{}{}\n\\newcommand{\\AlertTok}[1]{\\textcolor[rgb]{1.00,0.00,0.00}{\\textbf{#1}}}\n\\newcommand{\\AnnotationTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textbf{\\textit{#1}}}}\n\\newcommand{\\AttributeTok}[1]{\\textcolor[rgb]{0.49,0.56,0.16}{#1}}\n\\newcommand{\\BaseNTok}[1]{\\textcolor[rgb]{0.25,0.63,0.44}{#1}}\n\\newcommand{\\BuiltInTok}[1]{\\textcolor[rgb]{0.00,0.50,0.00}{#1}}\n\\newcommand{\\CharTok}[1]{\\textcolor[rgb]{0.25,0.44,0.63}{#1}}\n\\newcommand{\\CommentTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textit{#1}}}\n\\newcommand{\\CommentVarTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textbf{\\textit{#1}}}}\n\\newcommand{\\ConstantTok}[1]{\\textcolor[rgb]{0.53,0.00,0.00}{#1}}\n\\newcommand{\\ControlFlowTok}[1]{\\textcolor[rgb]{0.00,0.44,0.13}{\\textbf{#1}}}\n\\newcommand{\\DataTypeTok}[1]{\\textcolor[rgb]{0.56,0.13,0.00}{#1}}\n\\newcommand{\\DecValTok}[1]{\\textcolor[rgb]{0.25,0.63,0.44}{#1}}\n\\newcommand{\\DocumentationTok}[1]{\\textcolor[rgb]{0.73,0.13,0.13}{\\textit{#1}}}\n\\newcommand{\\ErrorTok}[1]{\\textcolor[rgb]{1.00,0.00,0.00}{\\textbf{#1}}}\n\\newcommand{\\ExtensionTok}[1]{#1}\n\\newcommand{\\FloatTok}[1]{\\textcolor[rgb]{0.25,0.63,0.44}{#1}}\n\\newcommand{\\FunctionTok}[1]{\\textcolor[rgb]{0.02,0.16,0.49}{#1}}\n\\newcommand{\\ImportTok}[1]{\\textcolor[rgb]{0.00,0.50,0.00}{\\textbf{#1}}}\n\\newcommand{\\InformationTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textbf{\\textit{#1}}}}\n\\newcommand{\\KeywordTok}[1]{\\textcolor[rgb]{0.00,0.44,0.13}{\\textbf{#1}}}\n\\newcommand{\\NormalTok}[1]{#1}\n\\newcommand{\\OperatorTok}[1]{\\textcolor[rgb]{0.40,0.40,0.40}{#1}}\n\\newcommand{\\OtherTok}[1]{\\textcolor[rgb]{0.00,0.44,0.13}{#1}}\n\\newcommand{\\PreprocessorTok}[1]{\\textcolor[rgb]{0.74,0.48,0.00}{#1}}\n\\newcommand{\\RegionMarkerTok}[1]{#1}\n\\newcommand{\\SpecialCharTok}[1]{\\textcolor[rgb]{0.25,0.44,0.63}{#1}}\n\\newcommand{\\SpecialStringTok}[1]{\\textcolor[rgb]{0.73,0.40,0.53}{#1}}\n\\newcommand{\\StringTok}[1]{\\textcolor[rgb]{0.25,0.44,0.63}{#1}}\n\\newcommand{\\VariableTok}[1]{\\textcolor[rgb]{0.10,0.09,0.49}{#1}}\n\\newcommand{\\VerbatimStringTok}[1]{\\textcolor[rgb]{0.25,0.44,0.63}{#1}}\n\\newcommand{\\WarningTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textbf{\\textit{#1}}}}\n\n\\makeatletter\n\\def\\maxwidth{\\ifdim\\Gin@nat@width>\\linewidth\\linewidth\\else\\Gin@nat@width\\fi}\n\\def\\maxheight{\\ifdim\\Gin@nat@height>\\textheight\\textheight\\else\\Gin@nat@height\\fi}\n\\makeatother\n% Scale images if necessary, so that they will not overflow the page\n% margins by default, and it is still possible to overwrite the defaults\n% using explicit options in \\includegraphics[width, height, ...]{}\n\\setkeys{Gin}{width=\\maxwidth,height=\\maxheight,keepaspectratio}\n% Set default figure placement to htbp\n\\makeatletter\n\\def\\fps@figure{htbp}\n\\makeatother\n\\setlength{\\emergencystretch}{3em} % prevent overfull lines\n\\providecommand{\\tightlist}{%\n  \\setlength{\\itemsep}{0pt}\\setlength{\\parskip}{0pt}}\n\\setcounter{secnumdepth}{-\\maxdimen} % remove section numbering\n\\ifLuaTeX\n  \\usepackage{selnolig}  % disable illegal ligatures\n\\fi\n\\IfFileExists{bookmark.sty}{\\usepackage{bookmark}}{\\usepackage{hyperref}}\n\\IfFileExists{xurl.sty}{\\usepackage{xurl}}{} % add URL line breaks if available\n\\urlstyle{same}\n% End stuff from Pandoc template \n\n%\\geometry{paperwidth=191mm,paperheight=235mm,\n%    hmargin={20mm,20mm},vmargin={20mm,20mm}}\n\\geometry{letterpaper, hmargin={1in, 1in}, vmargin={1in,1in}}\n\n\\setmainfont{Source Serif Pro}\n\\setsansfont{Source Sans Pro}\n\\setmonofont{IosevkaTermSlab Nerd Font}\n\\newfontfamily\\sectionfont{Source Sans Pro}\n\\newfontfamily\\subsectionfont{Source Sans Pro}\n\\newfontfamily\\subsubsectionfont{Source Sans Pro}\n\\newfontfamily\\captionfont{Source Sans Pro}\n\\newcommand{\\hruleafter}[1]{#1\\hrule}\n\\titleformat{\\section}{\\large\\bfseries\\sffamily\\sectionfont}{\\thesection}{1em}{\\hruleafter}\n\\titleformat*{\\subsection}{\\bfseries\\sffamily\\subsectionfont}\n\\titleformat*{\\subsubsection}{\\itshape\\subsubsectionfont}\n\\captionnamefont{\\tiny\\sffamily}\n\\captiontitlefont{\\tiny\\sffamily}\n\\aliaspagestyle{part}{empty}\n\\setlistdepth{9}\n\n\\makeatletter\n\\g@addto@macro\\@verbatim\\scriptsize\n\\makeatother\n\n\\newlength{\\imgwidth}\n\\newlength{\\drop}% for my convenience\n\n\\newcommand*{\\titleGM}{\\begingroup% Gentle Madness\n\\drop = 0.1\\textheight\n%\\vspace*{\\baselineskip}\n\\vfill\n\\hbox{%\n\\hspace*{0.2\\textwidth}%\n\\rule{1pt}{\\textheight}\n\\hspace*{0.05\\textwidth}%\n\\parbox[b]{0.75\\textwidth}{\n\\vbox{%\n\\vspace{\\drop}\n{\\noindent\\HUGE\\bfseries Developing\\\\[0.5\\baselineskip]\nLiferay DXP 7.2}\\\\[2\\baselineskip]\n{\\Large\\itshape A Complete Guide}\\\\[4\\baselineskip]\n{\\Large THE LIFERAY DOCUMENTATION TEAM}\\par\n{\\small Richard Sezov, Jr.}\\par\n{\\small Jim Hinkey}\\par\n{\\small Stephen Kostas}\\par\n{\\small Jesse Rao}\\par\n{\\small Cody Hoag}\\par\n{\\small Nicholas Gaskill}\\par\n{\\small Michael Williams}\\par\n\\vspace{0.25\\textheight}\n{\\noindent Liferay Press}\\\\[\\baselineskip]\n}% end of vbox\n}% end of parbox\n}% end of hbox\n\\vfill\n\\null\n\\endgroup}\n\n\\makeatletter\n\\newcommand\\thickhrulefill{\\leavevmode \\leaders \\hrule height 1ex \\hfill \\kern \\z@}\n\\setlength\\midchapskip{10pt}\n\\makechapterstyle{VZ14}{\n  \\renewcommand\\chapternamenum{}\n  \\renewcommand\\printchaptername{}\n  \\renewcommand\\chapnamefont{\\sffamily\\Large\\scshape}\n  \\renewcommand\\printchapternum{%\n    \\chapnamefont\\null\\thickhrulefill\\quad\n    \\@chapapp\\space\\thechapter\\quad\\thickhrulefill}\n  \\renewcommand\\printchapternonum{%\n    \\par\\thickhrulefill\\par\\vskip\\midchapskip\n    \\hrule\\vskip\\midchapskip\n  }\n  \\renewcommand\\chaptitlefont{\\sffamily\\Huge\\scshape\\centering}\n  \\renewcommand\\afterchapternum{%\n    \\par\\nobreak\\vskip\\midchapskip\\hrule\\vskip\\midchapskip}\n  \\renewcommand\\afterchaptertitle{%\n    \\par\\vskip\\midchapskip\\hrule\\nobreak\\vskip\\afterchapskip}\n}\n\\makeatother\n\n\\newcommand\\scalegraphics[1]{%   \n    \\settowidth{\\imgwidth}{\\includegraphics{#1}}%\n    \\setlength{\\imgwidth}{\\minof{\\imgwidth}{\\textwidth}}%\n    \\includegraphics[width=\\imgwidth]{#1}%\n}\n\n\n\\usepackage{fancybox}\n\\newenvironment{roundedframe}{%\n\\def\\FrameCommand{%\n\\cornersize*{20pt}%\n\\setlength{\\fboxsep}{5pt}%\n\\ovalbox}%\n\\MakeFramed{\\advance\\hsize-\\width \\FrameRestore}}%\n{\\endMakeFramed}\n\n\n\\author{Richard L. Sezov, Jr. }\n\\title{Developing Liferay DXP 7.2}\n\\date{12/11/2020}\n\n\\begin{document}\n\n\\pagestyle{empty}\n\n\\titleGM\n\nDeveloping Liferay DXP 7.2\n\nby The Liferay Documentation Team \n\nCopyright \\copyright 2020 by Liferay, Inc.\\\\[2\\baselineskip]\nThis work is offered under the following license: \\\\[2\\baselineskip]\nCreative Commons Attribution-Share Alike Unported\n\n\\scalegraphics{./images/cc-by-sa.png}\n\nYou are free:\n\n\\begin{enumerate}\n\\item\n  to share---to copy, distribute, and transmit the work\n\\item\n  to remix---to adapt the work\n\\end{enumerate}\n\nUnder the following conditions:\n\n\\begin{enumerate}\n\\item\n  Attribution. You must attribute the work in the manner specified by\n  the author or licensor (but not in any way that suggests that they\n  endorse you or your use of the work).\n\\item\n  Share Alike. If you alter, transform, or build upon this work, you may\n  distribute the resulting work only under the same, similar or a\n  compatible license.\n\\end{enumerate}\n\nThe full version of this license is here:\n\n\\href{http://creativecommons.org/licenses/by-sa/3.0}{http://creativecommons.org/licenses/by-sa/3.0}\n\nThis book was created out of material from the \\href{https://github.com/liferay/liferay-docs}{Liferay \n    Docs} repository. Where the content of this book and the repository differ, \nthe site is more up to date. \n\n\\clearpage\n\n\\frontmatter\n\\pagestyle{plain}\n\\pagenumbering{roman}\n\\chapterstyle{VZ14}\n\n\\tableofcontents\n\n\\chapter{Preface}\n\nWelcome to the world of the Liferay DXP development platform! This book was written for anyone who wants to create applications built on Liferay DXP. It contains everything you need to know about Liferay's development tools and projects. You'll learn all you need to know about plugins, OSGi, the Liferay Workspace, Service Builder, and more. Use this book as a handbook for everything you need to do to get your application running on Liferay DXP, and then keep it by your side as you update and add features to help your users work more effectively.\n\n\\section{Conventions}\n\nThe information contained herein has been organized in a way that makes it easy to locate information. The book has two parts. The first part, \\textit{Developer Tutorials}, shows you how to work step-by-step with Liferay's technology. The second part, \\textit{Developer Reference}, shows exhaustively the options and APIs you need. \n\nSections are broken up into multiple levels of headings, and these are designed to make it easy to find information.\n\nSource code and configuration file directives are presented monospaced, as below.\n\n\\begin{verbatim}\n\n  Source code appears in a non-proportional font. \n\n\\end{verbatim}\n\n\\textit{Italics} represent links or buttons to be clicked on in a user interface.\n\n\\texttt{Monospaced type} denotes Java classes, code, or properties within the text.\n\n\\textbf{Bold} describes field labels and portlets.\n\nPage headers denote the chapters and the section within the chapter.\n\n\\section{Publisher Notes}\n\nIt is our hope that this book is valuable to you, and that it becomes an indispensable resource as you work with Liferay DXP. If you need assistance beyond what is covered in this book, Liferay offers training\\footnote{https://learn.liferay.com}, consulting\\footnote{https://www.liferay.com/consulting}, and support\\footnote{https://help.liferay.com} services to fill any need that you might have. \n\nFor up-to-date documentation on the latest versions of Liferay, please see the documentation pages on Liferay Learn.\\footnote{https://learn.liferay.com} \n\nAs always, we welcome feedback. If there is any way you think we could make this book better, please feel free to mention it on our forums or in the feedback on Liferay Learn. You can also use any of the email addresses on our Contact Us page.\\footnote{\\href{http://www.liferay.com/contact-us}{https://www.liferay.com/contact-us}} We are here to serve you, our users and customers, and to help make your experience using Liferay DXP the best it can be.\n\n\\mainmatter\n\n\\part{Developer Tutorials}\n\n\\include{developer/tutorials}\n\n\\part{Customization} \n\n\\include{developer/customization}\n\n\\part{Application Development Platform}\n\n\\include{developer/appdev}\n\n\\part{Liferay Frameworks} \n\n\\include{developer/frameworks}\n\n\\part{Reference}\n\n\\include{developer/reference}\n\n\\end{document}\n"
  },
  {
    "path": "book/readme.md",
    "content": "# Liferay Documentation Books Readme\n\nThe documentation in this branch has been concatenated together in all its sections and converted to LaTeX to create a PDF for publishing on [Liferay Learn](https://learn.liferay.com). It represents \"legacy\" documentation now. \n\nFollow these steps to build these PDFs: \n\n1. Make sure you have a LaTeX distribution installed. For Liferay, this was done with `lualatex` as shipped with Texlive. \n\n2. Copy all the images for all the sections of docs into the images folder here. There will be duplicates; overwrite them. \n\n3. Configure your `~/latexmkrc` file like this: \n\n   ```bash\n\n    # PDF-generating modes are:\n    # 1: pdflatex, as specified by $pdflatex variable (still largely in use)\n    # 2: postscript conversion, as specified by the $ps2pdf variable (useless)\n    # 3: dvi conversion, as specified by the $dvipdf variable (useless)\n    # 4: lualatex, as specified by the $lualatex variable (best)\n    # 5: xelatex, as specified by the $xelatex variable (second best)\n    $pdf_mode = 4;\n    $dvi_mode = '0';\n    $pdf_previewer = 'start okular'\n\n   ```\n  \n   Substitute your PDF viewer of choice if you don't use Okular. \n\n4. Run `latexmk` for each of the files: \n\n   ```bash\n\n   latexmk developing-liferay-dxp-72.tex \n\n   latexmk using-liferay-dxp-72.tex\n   \n   ```\n\nWhen `latexmk` finishes running, you should have a PDF. If the table of contents is blank, run `latexmk` on that file again. \n"
  },
  {
    "path": "book/user/deployment.aux",
    "content": "\\relax \n\\providecommand{\\transparent@use}[1]{}\n\\providecommand\\zref@newlabel[2]{}\n\\providecommand\\hyper@newdestlabel[2]{}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {361}Deploying Liferay DXP}{1253}{chapter.361}\\protected@file@percent }\n\\newlabel{deploying-liferay-dxp}{{361}{1253}{Deploying Liferay DXP}{chapter.361}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {362}Obtaining Liferay DXP}{1255}{chapter.362}\\protected@file@percent }\n\\newlabel{obtaining-liferay-dxp}{{362}{1255}{Obtaining Liferay DXP}{chapter.362}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {362.1}Liferay Tomcat Bundle}{1255}{section.362.1}\\protected@file@percent }\n\\newlabel{liferay-tomcat-bundle}{{362.1}{1255}{Liferay Tomcat Bundle}{section.362.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {362.2}Downloading the Liferay WAR and Dependency JARs}{1256}{section.362.2}\\protected@file@percent }\n\\newlabel{downloading-the-liferay-war-and-dependency-jars}{{362.2}{1256}{Downloading the Liferay WAR and Dependency JARs}{section.362.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {363}Preparing for Install}{1257}{chapter.363}\\protected@file@percent }\n\\newlabel{preparing-for-install}{{363}{1257}{Preparing for Install}{chapter.363}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {363.1}JDK Requirements}{1257}{section.363.1}\\protected@file@percent }\n\\newlabel{jdk-requirements}{{363.1}{1257}{JDK Requirements}{section.363.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {363.2}JVM Requirements}{1258}{section.363.2}\\protected@file@percent }\n\\newlabel{jvm-requirements}{{363.2}{1258}{JVM Requirements}{section.363.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {363.3}Preparing a Database}{1259}{section.363.3}\\protected@file@percent }\n\\newlabel{preparing-a-database}{{363.3}{1259}{Preparing a Database}{section.363.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {363.4}Using the Built-in Data Source}{1259}{section.363.4}\\protected@file@percent }\n\\newlabel{using-the-built-in-data-source}{{363.4}{1259}{Using the Built-in Data Source}{section.363.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {363.5}Using a Data Source on Your Application Server}{1260}{section.363.5}\\protected@file@percent }\n\\newlabel{using-a-data-source-on-your-application-server}{{363.5}{1260}{Using a Data Source on Your Application Server}{section.363.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {363.6}Limiting Database Access}{1260}{section.363.6}\\protected@file@percent }\n\\newlabel{limiting-database-access}{{363.6}{1260}{Limiting Database Access}{section.363.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {364}Installing Liferay DXP}{1263}{chapter.364}\\protected@file@percent }\n\\newlabel{installing-liferay-dxp}{{364}{1263}{Installing Liferay DXP}{chapter.364}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {364.1}Extracting a Liferay DXP Bundle}{1263}{section.364.1}\\protected@file@percent }\n\\newlabel{extracting-a-liferay-dxp-bundle}{{364.1}{1263}{Extracting a Liferay DXP Bundle}{section.364.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {364.2}Installing the JDBC Driver}{1263}{section.364.2}\\protected@file@percent }\n\\newlabel{installing-the-jdbc-driver}{{364.2}{1263}{Installing the JDBC Driver}{section.364.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {364.3}Running Liferay DXP for the First Time}{1264}{section.364.3}\\protected@file@percent }\n\\newlabel{running-liferay-dxp-for-the-first-time}{{364.3}{1264}{Running Liferay DXP for the First Time}{section.364.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {364.4}Using the Setup Wizard}{1264}{section.364.4}\\protected@file@percent }\n\\newlabel{using-the-setup-wizard}{{364.4}{1264}{Using the Setup Wizard}{section.364.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {364.5}Portal}{1264}{section.364.5}\\protected@file@percent }\n\\newlabel{portal}{{364.5}{1264}{Portal}{section.364.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {364.6}Administrator User}{1264}{section.364.6}\\protected@file@percent }\n\\newlabel{administrator-user}{{364.6}{1264}{Administrator User}{section.364.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {364.1}{\\ignorespaces Supply the information for your portal and your portal's default administrator user on the Basic Configuration page.}}{1265}{figure.364.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {364.7}Database}{1265}{section.364.7}\\protected@file@percent }\n\\newlabel{database}{{364.7}{1265}{Database}{section.364.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {365}Installing Liferay DXP on Tomcat}{1267}{chapter.365}\\protected@file@percent }\n\\newlabel{installing-liferay-dxp-on-tomcat}{{365}{1267}{Installing Liferay DXP on Tomcat}{chapter.365}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {365.1}Installing Dependencies}{1267}{section.365.1}\\protected@file@percent }\n\\newlabel{installing-dependencies}{{365.1}{1267}{Installing Dependencies}{section.365.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {365.2}Configuring Tomcat}{1268}{section.365.2}\\protected@file@percent }\n\\newlabel{configuring-tomcat}{{365.2}{1268}{Configuring Tomcat}{section.365.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {365.3}Database Configuration}{1272}{section.365.3}\\protected@file@percent }\n\\newlabel{database-configuration}{{365.3}{1272}{Database Configuration}{section.365.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {365.4}Mail Configuration}{1273}{section.365.4}\\protected@file@percent }\n\\newlabel{mail-configuration}{{365.4}{1273}{Mail Configuration}{section.365.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {365.5}Deploying Liferay DXP}{1273}{section.365.5}\\protected@file@percent }\n\\newlabel{deploying-liferay-dxp-1}{{365.5}{1273}{Deploying Liferay DXP}{section.365.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {366}Installing Liferay DXP on Wildfly}{1275}{chapter.366}\\protected@file@percent }\n\\newlabel{installing-liferay-dxp-on-wildfly}{{366}{1275}{Installing Liferay DXP on Wildfly}{chapter.366}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {366.1}Installing Dependencies}{1275}{section.366.1}\\protected@file@percent }\n\\newlabel{installing-dependencies-1}{{366.1}{1275}{Installing Dependencies}{section.366.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {366.2}Running Liferay DXP on Wildfly in Standalone Mode vs.~Domain Mode}{1276}{section.366.2}\\protected@file@percent }\n\\newlabel{running-liferay-dxp-on-wildfly-in-standalone-mode-vs.-domain-mode}{{366.2}{1276}{Running Liferay DXP on Wildfly in Standalone Mode vs.~Domain Mode}{section.366.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {366.3}Configuring Wildfly}{1277}{section.366.3}\\protected@file@percent }\n\\newlabel{configuring-wildfly}{{366.3}{1277}{Configuring Wildfly}{section.366.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {366.4}Database Configuration}{1280}{section.366.4}\\protected@file@percent }\n\\newlabel{database-configuration-1}{{366.4}{1280}{Database Configuration}{section.366.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {366.5}Mail Configuration}{1281}{section.366.5}\\protected@file@percent }\n\\newlabel{mail-configuration-1}{{366.5}{1281}{Mail Configuration}{section.366.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {366.6}Deploying Liferay DXP}{1282}{section.366.6}\\protected@file@percent }\n\\newlabel{deploying-liferay-dxp-2}{{366.6}{1282}{Deploying Liferay DXP}{section.366.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {367}Installing Liferay DXP on JBoss EAP}{1283}{chapter.367}\\protected@file@percent }\n\\newlabel{installing-liferay-dxp-on-jboss-eap}{{367}{1283}{Installing Liferay DXP on JBoss EAP}{chapter.367}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {367.1}Installing Dependencies}{1283}{section.367.1}\\protected@file@percent }\n\\newlabel{installing-dependencies-2}{{367.1}{1283}{Installing Dependencies}{section.367.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {367.2}Running Liferay DXP on JBoss EAP in Standalone Mode vs.~Domain Mode}{1285}{section.367.2}\\protected@file@percent }\n\\newlabel{running-liferay-dxp-on-jboss-eap-in-standalone-mode-vs.-domain-mode}{{367.2}{1285}{Running Liferay DXP on JBoss EAP in Standalone Mode vs.~Domain Mode}{section.367.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {367.3}Configuring JBoss}{1285}{section.367.3}\\protected@file@percent }\n\\newlabel{configuring-jboss}{{367.3}{1285}{Configuring JBoss}{section.367.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {367.4}Database Configuration}{1288}{section.367.4}\\protected@file@percent }\n\\newlabel{database-configuration-2}{{367.4}{1288}{Database Configuration}{section.367.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {367.5}Mail Configuration}{1289}{section.367.5}\\protected@file@percent }\n\\newlabel{mail-configuration-2}{{367.5}{1289}{Mail Configuration}{section.367.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {367.6}Deploy Liferay}{1290}{section.367.6}\\protected@file@percent }\n\\newlabel{deploy-liferay}{{367.6}{1290}{Deploy Liferay}{section.367.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {368}Installing Liferay DXP on WebLogic 12c R2}{1291}{chapter.368}\\protected@file@percent }\n\\newlabel{installing-liferay-dxp-on-weblogic-12c-r2}{{368}{1291}{Installing Liferay DXP on WebLogic 12c R2}{chapter.368}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {368.1}Configuring WebLogic's Node Manager}{1291}{section.368.1}\\protected@file@percent }\n\\newlabel{configuring-weblogics-node-manager}{{368.1}{1291}{Configuring WebLogic's Node Manager}{section.368.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {368.2}Configuring WebLogic}{1292}{section.368.2}\\protected@file@percent }\n\\newlabel{configuring-weblogic}{{368.2}{1292}{Configuring WebLogic}{section.368.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {368.3}Setting Liferay DXP Properties}{1293}{section.368.3}\\protected@file@percent }\n\\newlabel{setting-liferay-dxp-properties}{{368.3}{1293}{Setting Liferay DXP Properties}{section.368.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {368.4}Installing Liferay DXP Dependencies}{1293}{section.368.4}\\protected@file@percent }\n\\newlabel{installing-liferay-dxp-dependencies}{{368.4}{1293}{Installing Liferay DXP Dependencies}{section.368.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {368.5}Database Configuration}{1294}{section.368.5}\\protected@file@percent }\n\\newlabel{database-configuration-3}{{368.5}{1294}{Database Configuration}{section.368.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {368.6}Mail Configuration}{1295}{section.368.6}\\protected@file@percent }\n\\newlabel{mail-configuration-3}{{368.6}{1295}{Mail Configuration}{section.368.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {368.7}Deploying Liferay DXP}{1295}{section.368.7}\\protected@file@percent }\n\\newlabel{deploying-liferay-dxp-3}{{368.7}{1295}{Deploying Liferay DXP}{section.368.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {369}Installing Liferay DXP on WebSphere}{1297}{chapter.369}\\protected@file@percent }\n\\newlabel{installing-liferay-dxp-on-websphere}{{369}{1297}{Installing Liferay DXP on WebSphere}{chapter.369}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.1}Preparing WebSphere for Liferay DXP}{1297}{section.369.1}\\protected@file@percent }\n\\newlabel{preparing-websphere-for-liferay-dxp}{{369.1}{1297}{Preparing WebSphere for Liferay DXP}{section.369.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {369.1}{\\ignorespaces Choose the Advanced profile option to specify your own settings.}}{1298}{figure.369.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.2}Configuring the WebSphere Application Server}{1299}{section.369.2}\\protected@file@percent }\n\\newlabel{configuring-the-websphere-application-server}{{369.2}{1299}{Configuring the WebSphere Application Server}{section.369.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.3}Setting up JVM Parameters for Liferay DXP}{1299}{section.369.3}\\protected@file@percent }\n\\newlabel{setting-up-jvm-parameters-for-liferay-dxp}{{369.3}{1299}{Setting up JVM Parameters for Liferay DXP}{section.369.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {369.2}{\\ignorespaces Example of the settings before creating the profile.}}{1300}{figure.369.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.4}Removing the secureSessionCookie Tag}{1301}{section.369.4}\\protected@file@percent }\n\\newlabel{removing-the-securesessioncookie-tag}{{369.4}{1301}{Removing the secureSessionCookie Tag}{section.369.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.5}Installing Liferay DXP's Dependencies}{1301}{section.369.5}\\protected@file@percent }\n\\newlabel{installing-liferay-dxps-dependencies}{{369.5}{1301}{Installing Liferay DXP's Dependencies}{section.369.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.6}Ensuring that Liferay DXP's portlet.jar is loaded first}{1302}{section.369.6}\\protected@file@percent }\n\\newlabel{ensuring-that-liferay-dxps-portlet.jar-is-loaded-first}{{369.6}{1302}{Ensuring that Liferay DXP's portlet.jar is loaded first}{section.369.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.7}Database Configuration}{1302}{section.369.7}\\protected@file@percent }\n\\newlabel{database-configuration-4}{{369.7}{1302}{Database Configuration}{section.369.7}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {369.3}{\\ignorespaces WebSphere JDBC providers}}{1303}{figure.369.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {369.4}{\\ignorespaces Completed JDBC provider configurations.}}{1303}{figure.369.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {369.5}{\\ignorespaces Modifying data source properties in WebSphere}}{1304}{figure.369.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.8}Mail Configuration}{1305}{section.369.8}\\protected@file@percent }\n\\newlabel{mail-configuration-4}{{369.8}{1305}{Mail Configuration}{section.369.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.9}Creating a WebSphere-Managed Mail Session (Optional)}{1305}{section.369.9}\\protected@file@percent }\n\\newlabel{creating-a-websphere-managed-mail-session-optional}{{369.9}{1305}{Creating a WebSphere-Managed Mail Session (Optional)}{section.369.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.10}Verifying WebSphere Mail Provider}{1306}{section.369.10}\\protected@file@percent }\n\\newlabel{verifying-websphere-mail-provider}{{369.10}{1306}{Verifying WebSphere Mail Provider}{section.369.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.11}Enable Cookies for HTTP Sessions}{1306}{section.369.11}\\protected@file@percent }\n\\newlabel{enable-cookies-for-http-sessions}{{369.11}{1306}{Enable Cookies for HTTP Sessions}{section.369.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.12}Enable UTF-8}{1306}{section.369.12}\\protected@file@percent }\n\\newlabel{enable-utf-8}{{369.12}{1306}{Enable UTF-8}{section.369.12}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.13}Deploy Liferay DXP}{1307}{section.369.13}\\protected@file@percent }\n\\newlabel{deploy-liferay-dxp}{{369.13}{1307}{Deploy Liferay DXP}{section.369.13}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.14}Setting the JDK Version for Compiling JSPs}{1307}{section.369.14}\\protected@file@percent }\n\\newlabel{setting-the-jdk-version-for-compiling-jsps}{{369.14}{1307}{Setting the JDK Version for Compiling JSPs}{section.369.14}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {369.6}{\\ignorespaces Review your deployment options before deploying.}}{1308}{figure.369.6}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {369.15}Start Liferay DXP}{1309}{section.369.15}\\protected@file@percent }\n\\newlabel{start-liferay-dxp}{{369.15}{1309}{Start Liferay DXP}{section.369.15}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {370}Activating Liferay DXP}{1311}{chapter.370}\\protected@file@percent }\n\\newlabel{activating-liferay-dxp}{{370}{1311}{Activating Liferay DXP}{chapter.370}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {371}Setting Up Marketplace}{1313}{chapter.371}\\protected@file@percent }\n\\newlabel{setting-up-marketplace}{{371}{1313}{Setting Up Marketplace}{chapter.371}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {371.1}Server is Firewalled without Access to the Internet}{1313}{section.371.1}\\protected@file@percent }\n\\newlabel{server-is-firewalled-without-access-to-the-internet}{{371.1}{1313}{Server is Firewalled without Access to the Internet}{section.371.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {371.2}Limited Database Access}{1314}{section.371.2}\\protected@file@percent }\n\\newlabel{limited-database-access}{{371.2}{1314}{Limited Database Access}{section.371.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {372}Trial Plugin Installation}{1315}{chapter.372}\\protected@file@percent }\n\\newlabel{trial-plugin-installation}{{372}{1315}{Trial Plugin Installation}{chapter.372}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {372.1}Installation Process}{1315}{section.372.1}\\protected@file@percent }\n\\newlabel{installation-process}{{372.1}{1315}{Installation Process}{section.372.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {372.1}{\\ignorespaces Hover over the Profile button and click \\emph  {Sign In/Create Account}.}}{1315}{figure.372.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {372.2}{\\ignorespaces Click the \\emph  {Store} link and authorize Marketplace to access your local account.}}{1316}{figure.372.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {372.2}FAQ}{1316}{section.372.2}\\protected@file@percent }\n\\newlabel{faq}{{372.2}{1316}{FAQ}{section.372.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {373}Document Repository Configuration}{1319}{chapter.373}\\protected@file@percent }\n\\newlabel{document-repository-configuration}{{373}{1319}{Document Repository Configuration}{chapter.373}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {373.1}{\\ignorespaces The File Storage page in System Settings lets you configure document repository storage.}}{1320}{figure.373.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {374}Using the Simple File System Store}{1321}{chapter.374}\\protected@file@percent }\n\\newlabel{using-the-simple-file-system-store}{{374}{1321}{Using the Simple File System Store}{chapter.374}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {375}Using the Advanced File System Store}{1323}{chapter.375}\\protected@file@percent }\n\\newlabel{using-the-advanced-file-system-store}{{375}{1323}{Using the Advanced File System Store}{chapter.375}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {375.1}{\\ignorespaces The advanced file system store creates a more nested folder structure than the file system store.}}{1323}{figure.375.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {376}Using Amazon Simple Storage Service}{1325}{chapter.376}\\protected@file@percent }\n\\newlabel{using-amazon-simple-storage-service}{{376}{1325}{Using Amazon Simple Storage Service}{chapter.376}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {377}Using the DBStore}{1327}{chapter.377}\\protected@file@percent }\n\\newlabel{using-the-dbstore}{{377}{1327}{Using the DBStore}{chapter.377}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {378}Configuring Liferay DXP}{1329}{chapter.378}\\protected@file@percent }\n\\newlabel{configuring-liferay-dxp}{{378}{1329}{Configuring Liferay DXP}{chapter.378}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {379}Configuring Mail}{1331}{chapter.379}\\protected@file@percent }\n\\newlabel{configuring-mail}{{379}{1331}{Configuring Mail}{chapter.379}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {379.1}Configuring Liferay DXP's Built-in Mail Session}{1331}{section.379.1}\\protected@file@percent }\n\\newlabel{configuring-liferay-dxps-built-in-mail-session}{{379.1}{1331}{Configuring Liferay DXP's Built-in Mail Session}{section.379.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {379.2}Built-in Mail Session in the Control Panel}{1332}{section.379.2}\\protected@file@percent }\n\\newlabel{built-in-mail-session-in-the-control-panel}{{379.2}{1332}{Built-in Mail Session in the Control Panel}{section.379.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {379.3}Built-in Mail Session Portal Properties}{1332}{section.379.3}\\protected@file@percent }\n\\newlabel{built-in-mail-session-portal-properties}{{379.3}{1332}{Built-in Mail Session Portal Properties}{section.379.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {379.4}Configuring a Mail Session on the Application Server}{1333}{section.379.4}\\protected@file@percent }\n\\newlabel{configuring-a-mail-session-on-the-application-server}{{379.4}{1333}{Configuring a Mail Session on the Application Server}{section.379.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {379.5}Configuring default email senders}{1333}{section.379.5}\\protected@file@percent }\n\\newlabel{configuring-default-email-senders}{{379.5}{1333}{Configuring default email senders}{section.379.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {380}Locales and Encoding Configuration}{1335}{chapter.380}\\protected@file@percent }\n\\newlabel{locales-and-encoding-configuration}{{380}{1335}{Locales and Encoding Configuration}{chapter.380}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {380.1}Time Zones}{1335}{section.380.1}\\protected@file@percent }\n\\newlabel{time-zones}{{380.1}{1335}{Time Zones}{section.380.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {380.1}{\\ignorespaces You can change the default and available languages and the time zone in Instance Settings.}}{1336}{figure.380.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {380.2}Set the JVM Time Zone to GMT}{1336}{section.380.2}\\protected@file@percent }\n\\newlabel{set-the-jvm-time-zone-to-gmt}{{380.2}{1336}{Set the JVM Time Zone to GMT}{section.380.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {380.3}Friendly URLs and Locales}{1337}{section.380.3}\\protected@file@percent }\n\\newlabel{friendly-urls-and-locales}{{380.3}{1337}{Friendly URLs and Locales}{section.380.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {380.4}Modifying Language Keys}{1337}{section.380.4}\\protected@file@percent }\n\\newlabel{modifying-language-keys}{{380.4}{1337}{Modifying Language Keys}{section.380.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {380.5}Right to Left}{1337}{section.380.5}\\protected@file@percent }\n\\newlabel{right-to-left}{{380.5}{1337}{Right to Left}{section.380.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {380.6}Localizing User Names}{1338}{section.380.6}\\protected@file@percent }\n\\newlabel{localizing-user-names}{{380.6}{1338}{Localizing User Names}{section.380.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {380.7}Related Topics}{1338}{section.380.7}\\protected@file@percent }\n\\newlabel{related-topics}{{380.7}{1338}{Related Topics}{section.380.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {381}Liferay DXP Clustering}{1339}{chapter.381}\\protected@file@percent }\n\\newlabel{liferay-dxp-clustering}{{381}{1339}{Liferay DXP Clustering}{chapter.381}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {381.1}{\\ignorespaces Liferay DXP is designed to scale to as large an installation as you need.}}{1340}{figure.381.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {382}Point all Nodes to the Same Liferay DXP Database}{1341}{chapter.382}\\protected@file@percent }\n\\newlabel{point-all-nodes-to-the-same-liferay-dxp-database}{{382}{1341}{Point all Nodes to the Same Liferay DXP Database}{chapter.382}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {382.1}Read-Writer Database Configuration}{1341}{section.382.1}\\protected@file@percent }\n\\newlabel{read-writer-database-configuration}{{382.1}{1341}{Read-Writer Database Configuration}{section.382.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {382.2}JDBC}{1341}{section.382.2}\\protected@file@percent }\n\\newlabel{jdbc}{{382.2}{1341}{JDBC}{section.382.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {382.3}JNDI}{1343}{section.382.3}\\protected@file@percent }\n\\newlabel{jndi}{{382.3}{1343}{JNDI}{section.382.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {383}Configure Documents and Media the Same for all Nodes}{1345}{chapter.383}\\protected@file@percent }\n\\newlabel{configure-documents-and-media-the-same-for-all-nodes}{{383}{1345}{Configure Documents and Media the Same for all Nodes}{chapter.383}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {384}Clustering Search}{1347}{chapter.384}\\protected@file@percent }\n\\newlabel{clustering-search}{{384}{1347}{Clustering Search}{chapter.384}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {385}Enabling Cluster Link}{1349}{chapter.385}\\protected@file@percent }\n\\newlabel{enabling-cluster-link}{{385}{1349}{Enabling Cluster Link}{chapter.385}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {385.1}Enabling Cluster Link}{1349}{section.385.1}\\protected@file@percent }\n\\newlabel{enabling-cluster-link-1}{{385.1}{1349}{Enabling Cluster Link}{section.385.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {385.1}{\\ignorespaces Liferay DXP's cache algorithm is extremely efficient.}}{1350}{figure.385.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {385.2}Multicast Over UDP}{1351}{section.385.2}\\protected@file@percent }\n\\newlabel{multicast-over-udp}{{385.2}{1351}{Multicast Over UDP}{section.385.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {385.3}Checkpoint:}{1351}{section.385.3}\\protected@file@percent }\n\\newlabel{checkpoint}{{385.3}{1351}{Checkpoint:}{section.385.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {385.4}Unicast over TCP}{1352}{section.385.4}\\protected@file@percent }\n\\newlabel{unicast-over-tcp}{{385.4}{1352}{Unicast over TCP}{section.385.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {385.5}JDBC Ping}{1353}{section.385.5}\\protected@file@percent }\n\\newlabel{jdbc-ping}{{385.5}{1353}{JDBC Ping}{section.385.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {385.6}S3 Ping}{1354}{section.385.6}\\protected@file@percent }\n\\newlabel{s3-ping}{{385.6}{1354}{S3 Ping}{section.385.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {385.7}Other Pings}{1354}{section.385.7}\\protected@file@percent }\n\\newlabel{other-pings}{{385.7}{1354}{Other Pings}{section.385.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {385.8}Using Different Control and Transport Channel Ports}{1354}{section.385.8}\\protected@file@percent }\n\\newlabel{using-different-control-and-transport-channel-ports}{{385.8}{1354}{Using Different Control and Transport Channel Ports}{section.385.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {385.9}Modifying the Cache Configuration with a Module}{1356}{section.385.9}\\protected@file@percent }\n\\newlabel{modifying-the-cache-configuration-with-a-module}{{385.9}{1356}{Modifying the Cache Configuration with a Module}{section.385.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {385.10}Conclusion}{1356}{section.385.10}\\protected@file@percent }\n\\newlabel{conclusion}{{385.10}{1356}{Conclusion}{section.385.10}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {386}Auto Deploy to All Nodes}{1357}{chapter.386}\\protected@file@percent }\n\\newlabel{auto-deploy-to-all-nodes}{{386}{1357}{Auto Deploy to All Nodes}{chapter.386}{}}\n\\gdef \\LT@iii {\\LT@entry \n    {1}{69.69774pt}\\LT@entry \n    {1}{224.26688pt}\\LT@entry \n    {1}{175.83119pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {387}Updating a Cluster}{1359}{chapter.387}\\protected@file@percent }\n\\newlabel{updating-a-cluster}{{387}{1359}{Updating a Cluster}{chapter.387}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {388}Rolling Restarts}{1363}{chapter.388}\\protected@file@percent }\n\\newlabel{rolling-restarts}{{388}{1363}{Rolling Restarts}{chapter.388}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {388.1}New Modules and Plugins}{1363}{section.388.1}\\protected@file@percent }\n\\newlabel{new-modules-and-plugins}{{388.1}{1363}{New Modules and Plugins}{section.388.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {388.2}Updating Existing Modules and Plugins}{1364}{section.388.2}\\protected@file@percent }\n\\newlabel{updating-existing-modules-and-plugins}{{388.2}{1364}{Updating Existing Modules and Plugins}{section.388.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {388.3}Applying Fix Packs (DXP only)}{1364}{section.388.3}\\protected@file@percent }\n\\newlabel{applying-fix-packs-dxp-only}{{388.3}{1364}{Applying Fix Packs (DXP only)}{section.388.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {388.4}Reverting Fix Packs (DXP only)}{1364}{section.388.4}\\protected@file@percent }\n\\newlabel{reverting-fix-packs-dxp-only}{{388.4}{1364}{Reverting Fix Packs (DXP only)}{section.388.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {388.5}Portal Properties controlled by \\texttt  {portal-ext.properties}}{1364}{section.388.5}\\protected@file@percent }\n\\newlabel{portal-properties-controlled-by-portal-ext.properties}{{388.5}{1364}{\\texorpdfstring {Portal Properties controlled by \\texttt {portal-ext.properties}}{Portal Properties controlled by portal-ext.properties}}{section.388.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {388.6}System Settings controlled by Configuration Admin Files}{1364}{section.388.6}\\protected@file@percent }\n\\newlabel{system-settings-controlled-by-configuration-admin-files}{{388.6}{1364}{System Settings controlled by Configuration Admin Files}{section.388.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {388.7}Application Server or JVM setting modifications}{1364}{section.388.7}\\protected@file@percent }\n\\newlabel{application-server-or-jvm-setting-modifications}{{388.7}{1364}{Application Server or JVM setting modifications}{section.388.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {388.8}Java Version Updates}{1365}{section.388.8}\\protected@file@percent }\n\\newlabel{java-version-updates}{{388.8}{1365}{Java Version Updates}{section.388.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {388.9}Related Topics}{1365}{section.388.9}\\protected@file@percent }\n\\newlabel{related-topics-1}{{388.9}{1365}{Related Topics}{section.388.9}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {389}Blue-Green Deployment}{1367}{chapter.389}\\protected@file@percent }\n\\newlabel{blue-green-deployment}{{389}{1367}{Blue-Green Deployment}{chapter.389}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {389.1}Related Topics}{1367}{section.389.1}\\protected@file@percent }\n\\newlabel{related-topics-2}{{389.1}{1367}{Related Topics}{section.389.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {390}Configuring Remote Staging in a Clustered Environment}{1369}{chapter.390}\\protected@file@percent }\n\\newlabel{configuring-remote-staging-in-a-clustered-environment}{{390}{1369}{Configuring Remote Staging in a Clustered Environment}{chapter.390}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {390.1}{\\ignorespaces This is the assumed setup for your clustered environment.}}{1370}{figure.390.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {390.2}{\\ignorespaces When selecting the Remote Staging radio button, you're given a list of options to configure.}}{1372}{figure.390.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {391}Content Delivery Network}{1373}{chapter.391}\\protected@file@percent }\n\\newlabel{content-delivery-network}{{391}{1373}{Content Delivery Network}{chapter.391}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {391.1}Using CDN for Performance Enhancements}{1373}{section.391.1}\\protected@file@percent }\n\\newlabel{using-cdn-for-performance-enhancements}{{391.1}{1373}{Using CDN for Performance Enhancements}{section.391.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {391.1}{\\ignorespaces The red lines on the map represent the required distances traveled by requests from a server to the user. Using CDN allows a user to request static resources from a much closer local server, improving download times.}}{1374}{figure.391.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {391.2}Liferay CDN Requirements}{1374}{section.391.2}\\protected@file@percent }\n\\newlabel{liferay-cdn-requirements}{{391.2}{1374}{Liferay CDN Requirements}{section.391.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {391.3}Configuring Liferay DXP to Use a CDN}{1375}{section.391.3}\\protected@file@percent }\n\\newlabel{configuring-liferay-dxp-to-use-a-cdn}{{391.3}{1375}{Configuring Liferay DXP to Use a CDN}{section.391.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {391.2}{\\ignorespaces The Control Panel lets you configure your portal's CDN.}}{1375}{figure.391.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {392}Tuning Guidelines}{1377}{chapter.392}\\protected@file@percent }\n\\newlabel{tuning-guidelines}{{392}{1377}{Tuning Guidelines}{chapter.392}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {392.1}Database Connection Pool}{1377}{section.392.1}\\protected@file@percent }\n\\newlabel{database-connection-pool}{{392.1}{1377}{Database Connection Pool}{section.392.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {392.2}Deactivating Development Settings in the JSP Engine}{1378}{section.392.2}\\protected@file@percent }\n\\newlabel{deactivating-development-settings-in-the-jsp-engine}{{392.2}{1378}{Deactivating Development Settings in the JSP Engine}{section.392.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {392.3}Thread Pool}{1378}{section.392.3}\\protected@file@percent }\n\\newlabel{thread-pool}{{392.3}{1378}{Thread Pool}{section.392.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {393}Java Virtual Machine Tuning}{1381}{chapter.393}\\protected@file@percent }\n\\newlabel{java-virtual-machine-tuning}{{393}{1381}{Java Virtual Machine Tuning}{chapter.393}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {393.1}Garbage Collector}{1381}{section.393.1}\\protected@file@percent }\n\\newlabel{garbage-collector}{{393.1}{1381}{Garbage Collector}{section.393.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {393.2}Code Cache}{1382}{section.393.2}\\protected@file@percent }\n\\newlabel{code-cache}{{393.2}{1382}{Code Cache}{section.393.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {393.3}Java Heap}{1382}{section.393.3}\\protected@file@percent }\n\\newlabel{java-heap}{{393.3}{1382}{Java Heap}{section.393.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {393.4}JVM Advanced Options}{1383}{section.393.4}\\protected@file@percent }\n\\newlabel{jvm-advanced-options}{{393.4}{1383}{JVM Advanced Options}{section.393.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {394}Installing a Search Engine}{1385}{chapter.394}\\protected@file@percent }\n\\newlabel{installing-a-search-engine}{{394}{1385}{Installing a Search Engine}{chapter.394}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {394.1}Choosing a Search Engine}{1385}{section.394.1}\\protected@file@percent }\n\\newlabel{choosing-a-search-engine}{{394.1}{1385}{Choosing a Search Engine}{section.394.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {394.2}End User Feature Limitations of Liferay's Solr Integration}{1385}{section.394.2}\\protected@file@percent }\n\\newlabel{end-user-feature-limitations-of-liferays-solr-integration}{{394.2}{1385}{End User Feature Limitations of Liferay's Solr Integration}{section.394.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {394.3}Developer Feature Limitations of Liferay's Solr Integration}{1386}{section.394.3}\\protected@file@percent }\n\\newlabel{developer-feature-limitations-of-liferays-solr-integration}{{394.3}{1386}{Developer Feature Limitations of Liferay's Solr Integration}{section.394.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {394.4}Elasticsearch Java Distribution Compatibility}{1387}{section.394.4}\\protected@file@percent }\n\\newlabel{elasticsearch-java-distribution-compatibility}{{394.4}{1387}{Elasticsearch Java Distribution Compatibility}{section.394.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {395}Elasticsearch}{1389}{chapter.395}\\protected@file@percent }\n\\newlabel{elasticsearch}{{395}{1389}{Elasticsearch}{chapter.395}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {395.1}{\\ignorespaces To see information about the currently connected search engine, go to \\emph  {Control Panel} → \\emph  {Configuration} → \\emph  {Search}.}}{1389}{figure.395.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {395.1}Embedded vs.~Remote Operation Mode}{1390}{section.395.1}\\protected@file@percent }\n\\newlabel{embedded-vs.-remote-operation-mode}{{395.1}{1390}{Embedded vs.~Remote Operation Mode}{section.395.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {395.2}Troubleshooting Elasticsearch Integration}{1390}{section.395.2}\\protected@file@percent }\n\\newlabel{troubleshooting-elasticsearch-integration}{{395.2}{1390}{Troubleshooting Elasticsearch Integration}{section.395.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {396}Preparing to Install Elasticsearch}{1393}{chapter.396}\\protected@file@percent }\n\\newlabel{preparing-to-install-elasticsearch}{{396}{1393}{Preparing to Install Elasticsearch}{chapter.396}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {396.1}Sizing Your Deployment}{1393}{section.396.1}\\protected@file@percent }\n\\newlabel{sizing-your-deployment}{{396.1}{1393}{Sizing Your Deployment}{section.396.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {396.2}CPU}{1393}{section.396.2}\\protected@file@percent }\n\\newlabel{cpu}{{396.2}{1393}{CPU}{section.396.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {396.3}Memory}{1394}{section.396.3}\\protected@file@percent }\n\\newlabel{memory}{{396.3}{1394}{Memory}{section.396.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {396.4}Disk}{1394}{section.396.4}\\protected@file@percent }\n\\newlabel{disk}{{396.4}{1394}{Disk}{section.396.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {396.5}Cluster Size}{1394}{section.396.5}\\protected@file@percent }\n\\newlabel{cluster-size}{{396.5}{1394}{Cluster Size}{section.396.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {396.6}Networking}{1394}{section.396.6}\\protected@file@percent }\n\\newlabel{networking}{{396.6}{1394}{Networking}{section.396.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {397}Installing Elasticsearch}{1395}{chapter.397}\\protected@file@percent }\n\\newlabel{installing-elasticsearch}{{397}{1395}{Installing Elasticsearch}{chapter.397}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {397.1}Step One: Download a Supported Version of Elasticsearch}{1396}{section.397.1}\\protected@file@percent }\n\\newlabel{step-one-download-a-supported-version-of-elasticsearch}{{397.1}{1396}{Step One: Download a Supported Version of Elasticsearch}{section.397.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {397.2}Step Two: Install Elasticsearch}{1397}{section.397.2}\\protected@file@percent }\n\\newlabel{step-two-install-elasticsearch}{{397.2}{1397}{Step Two: Install Elasticsearch}{section.397.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {397.3}Step Three: Install Elasticsearch Plugins}{1397}{section.397.3}\\protected@file@percent }\n\\newlabel{step-three-install-elasticsearch-plugins}{{397.3}{1397}{Step Three: Install Elasticsearch Plugins}{section.397.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {397.4}Step Four: Name Your Elasticsearch Cluster}{1398}{section.397.4}\\protected@file@percent }\n\\newlabel{step-four-name-your-elasticsearch-cluster}{{397.4}{1398}{Step Four: Name Your Elasticsearch Cluster}{section.397.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {397.5}Step Five: Configure Liferay DXP to Connect to your Elasticsearch Cluster}{1398}{section.397.5}\\protected@file@percent }\n\\newlabel{step-five-configure-liferay-dxp-to-connect-to-your-elasticsearch-cluster}{{397.5}{1398}{Step Five: Configure Liferay DXP to Connect to your Elasticsearch Cluster}{section.397.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {397.6}Step Six: Restart Liferay DXP and Reindex}{1399}{section.397.6}\\protected@file@percent }\n\\newlabel{step-six-restart-liferay-dxp-and-reindex}{{397.6}{1399}{Step Six: Restart Liferay DXP and Reindex}{section.397.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {397.1}{\\ignorespaces To see information about the currently connected search engine, go to \\emph  {Control Panel → Configuration → Search}.}}{1399}{figure.397.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {398}Configuring the Liferay Elasticsearch Connector}{1401}{chapter.398}\\protected@file@percent }\n\\newlabel{configuring-the-liferay-elasticsearch-connector}{{398}{1401}{Configuring the Liferay Elasticsearch Connector}{chapter.398}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.1}Starting Elasticsearch}{1402}{section.398.1}\\protected@file@percent }\n\\newlabel{starting-elasticsearch}{{398.1}{1402}{Starting Elasticsearch}{section.398.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.2}Configuring the Liferay Elasticsearch Connector}{1402}{section.398.2}\\protected@file@percent }\n\\newlabel{configuring-the-liferay-elasticsearch-connector-1}{{398.2}{1402}{Configuring the Liferay Elasticsearch Connector}{section.398.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.3}Configuring the Connector in the Control Panel}{1402}{section.398.3}\\protected@file@percent }\n\\newlabel{configuring-the-connector-in-the-control-panel}{{398.3}{1402}{Configuring the Connector in the Control Panel}{section.398.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {398.1}{\\ignorespaces Use the System Settings application in Liferay DXP's Control Panel to configure the Elasticsearch connector.}}{1403}{figure.398.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.4}Configuring the Connector with an OSGi \\texttt  {.config} File}{1403}{section.398.4}\\protected@file@percent }\n\\newlabel{configuring-the-connector-with-an-osgi-.config-file}{{398.4}{1403}{\\texorpdfstring {Configuring the Connector with an OSGi \\texttt {.config} File}{Configuring the Connector with an OSGi .config File}}{section.398.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {398.2}{\\ignorespaces Configure the Elasticsearch connector's settings. Make sure you set the Operation Mode to \\emph  {Remote}.}}{1404}{figure.398.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.5}Configuring a Remote Elasticsearch Host}{1405}{section.398.5}\\protected@file@percent }\n\\newlabel{configuring-a-remote-elasticsearch-host}{{398.5}{1405}{Configuring a Remote Elasticsearch Host}{section.398.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.6}Clustering Elasticsearch in Remote Operation Mode}{1406}{section.398.6}\\protected@file@percent }\n\\newlabel{clustering-elasticsearch-in-remote-operation-mode}{{398.6}{1406}{Clustering Elasticsearch in Remote Operation Mode}{section.398.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {398.7}Elasticsearch Connector System Settings, By Operation Mode}{1406}{section.398.7}\\protected@file@percent }\n\\newlabel{elasticsearch-connector-system-settings-by-operation-mode}{{398.7}{1406}{Elasticsearch Connector System Settings, By Operation Mode}{section.398.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {399}Advanced Configuration of the Liferay Elasticsearch Connector}{1407}{chapter.399}\\protected@file@percent }\n\\newlabel{advanced-configuration-of-the-liferay-elasticsearch-connector}{{399}{1407}{Advanced Configuration of the Liferay Elasticsearch Connector}{chapter.399}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {399.1}Adding Settings and Mappings to the Liferay Elasticsearch Connector}{1407}{section.399.1}\\protected@file@percent }\n\\newlabel{adding-settings-and-mappings-to-the-liferay-elasticsearch-connector}{{399.1}{1407}{Adding Settings and Mappings to the Liferay Elasticsearch Connector}{section.399.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {399.2}Additional Configurations}{1407}{section.399.2}\\protected@file@percent }\n\\newlabel{additional-configurations}{{399.2}{1407}{Additional Configurations}{section.399.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {399.3}Adding Index Configurations}{1408}{section.399.3}\\protected@file@percent }\n\\newlabel{adding-index-configurations}{{399.3}{1408}{Adding Index Configurations}{section.399.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {399.4}Adding Type Mappings}{1408}{section.399.4}\\protected@file@percent }\n\\newlabel{adding-type-mappings}{{399.4}{1408}{Adding Type Mappings}{section.399.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {399.5}Overriding Type Mappings}{1409}{section.399.5}\\protected@file@percent }\n\\newlabel{overriding-type-mappings}{{399.5}{1409}{Overriding Type Mappings}{section.399.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {399.6}Multi-line YAML Configurations}{1410}{section.399.6}\\protected@file@percent }\n\\newlabel{multi-line-yaml-configurations}{{399.6}{1410}{Multi-line YAML Configurations}{section.399.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {400}Elasticsearch Connector Settings: Reference}{1411}{chapter.400}\\protected@file@percent }\n\\newlabel{elasticsearch-connector-settings-reference}{{400}{1411}{Elasticsearch Connector Settings: Reference}{chapter.400}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {400.1}Configurations only Affecting the Embedded Elasticsearch Server}{1413}{section.400.1}\\protected@file@percent }\n\\newlabel{configurations-only-affecting-the-embedded-elasticsearch-server}{{400.1}{1413}{Configurations only Affecting the Embedded Elasticsearch Server}{section.400.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {401}Installing Liferay Enterprise Search Security}{1415}{chapter.401}\\protected@file@percent }\n\\newlabel{installing-liferay-enterprise-search-security}{{401}{1415}{Installing Liferay Enterprise Search Security}{chapter.401}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {401.1}Enabling X-Pack Security}{1415}{section.401.1}\\protected@file@percent }\n\\newlabel{enabling-x-pack-security}{{401.1}{1415}{Enabling X-Pack Security}{section.401.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {401.2}Setting Up X-Pack Users}{1415}{section.401.2}\\protected@file@percent }\n\\newlabel{setting-up-x-pack-users}{{401.2}{1415}{Setting Up X-Pack Users}{section.401.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {401.3}Enabling Transport Layer Security}{1416}{section.401.3}\\protected@file@percent }\n\\newlabel{enabling-transport-layer-security}{{401.3}{1416}{Enabling Transport Layer Security}{section.401.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {401.4}Generate Node Certificates}{1416}{section.401.4}\\protected@file@percent }\n\\newlabel{generate-node-certificates}{{401.4}{1416}{Generate Node Certificates}{section.401.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {401.5}Enable TLS for Elasticsearch 7}{1417}{section.401.5}\\protected@file@percent }\n\\newlabel{enable-tls-for-elasticsearch-7}{{401.5}{1417}{Enable TLS for Elasticsearch 7}{section.401.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {401.6}Elasticsearch 6 TLS}{1418}{section.401.6}\\protected@file@percent }\n\\newlabel{elasticsearch-6-tls}{{401.6}{1418}{Elasticsearch 6 TLS}{section.401.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {401.7}Example Elasticsearch Security Configuration}{1418}{section.401.7}\\protected@file@percent }\n\\newlabel{example-elasticsearch-security-configuration}{{401.7}{1418}{Example Elasticsearch Security Configuration}{section.401.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {401.8}Install and Configure the Liferay Enterprise Search Security app}{1419}{section.401.8}\\protected@file@percent }\n\\newlabel{install-and-configure-the-liferay-enterprise-search-security-app}{{401.8}{1419}{Install and Configure the Liferay Enterprise Search Security app}{section.401.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {401.9}Disabling Elasticsearch Deprecation Logging}{1420}{section.401.9}\\protected@file@percent }\n\\newlabel{disabling-elasticsearch-deprecation-logging}{{401.9}{1420}{Disabling Elasticsearch Deprecation Logging}{section.401.9}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {402}Backing Up Elasticsearch}{1421}{chapter.402}\\protected@file@percent }\n\\newlabel{backing-up-elasticsearch}{{402}{1421}{Backing Up Elasticsearch}{chapter.402}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {402.1}Creating a Repository}{1421}{section.402.1}\\protected@file@percent }\n\\newlabel{creating-a-repository}{{402.1}{1421}{Creating a Repository}{section.402.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {402.2}Taking Snapshots of the Cluster}{1422}{section.402.2}\\protected@file@percent }\n\\newlabel{taking-snapshots-of-the-cluster}{{402.2}{1422}{Taking Snapshots of the Cluster}{section.402.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {402.3}Restoring from a Snapshot}{1423}{section.402.3}\\protected@file@percent }\n\\newlabel{restoring-from-a-snapshot}{{402.3}{1423}{Restoring from a Snapshot}{section.402.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {403}Upgrading to Elasticsearch 6.5}{1425}{chapter.403}\\protected@file@percent }\n\\newlabel{upgrading-to-elasticsearch-6.5}{{403}{1425}{Upgrading to Elasticsearch 6.5}{chapter.403}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {403.1}Re-index}{1425}{section.403.1}\\protected@file@percent }\n\\newlabel{re-index}{{403.1}{1425}{Re-index}{section.403.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {403.2}Reverting to Elasticsearch 6.1}{1426}{section.403.2}\\protected@file@percent }\n\\newlabel{reverting-to-elasticsearch-6.1}{{403.2}{1426}{Reverting to Elasticsearch 6.1}{section.403.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {404}Upgrading to Elasticsearch 6.8}{1427}{chapter.404}\\protected@file@percent }\n\\newlabel{upgrading-to-elasticsearch-6.8}{{404}{1427}{Upgrading to Elasticsearch 6.8}{chapter.404}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {404.1}Re-index}{1427}{section.404.1}\\protected@file@percent }\n\\newlabel{re-index-1}{{404.1}{1427}{Re-index}{section.404.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {404.2}Reverting to Elasticsearch 6.1}{1428}{section.404.2}\\protected@file@percent }\n\\newlabel{reverting-to-elasticsearch-6.1-1}{{404.2}{1428}{Reverting to Elasticsearch 6.1}{section.404.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {405}Upgrading to Elasticsearch 7}{1429}{chapter.405}\\protected@file@percent }\n\\newlabel{upgrading-to-elasticsearch-7}{{405}{1429}{Upgrading to Elasticsearch 7}{chapter.405}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {405.1}Backing up Application-Specific Indexes}{1430}{section.405.1}\\protected@file@percent }\n\\newlabel{backing-up-application-specific-indexes}{{405.1}{1430}{Backing up Application-Specific Indexes}{section.405.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {405.2}Blacklisting Elasticsearch 6}{1430}{section.405.2}\\protected@file@percent }\n\\newlabel{blacklisting-elasticsearch-6}{{405.2}{1430}{Blacklisting Elasticsearch 6}{section.405.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {405.3}Re-index}{1430}{section.405.3}\\protected@file@percent }\n\\newlabel{re-index-2}{{405.3}{1430}{Re-index}{section.405.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {405.4}Reverting to Elasticsearch 6}{1430}{section.405.4}\\protected@file@percent }\n\\newlabel{reverting-to-elasticsearch-6}{{405.4}{1430}{Reverting to Elasticsearch 6}{section.405.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {406}Installing Liferay Enterprise Search}{1433}{chapter.406}\\protected@file@percent }\n\\newlabel{installing-liferay-enterprise-search}{{406}{1433}{Installing Liferay Enterprise Search}{chapter.406}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {407}Installing Liferay Enterprise Search Monitoring}{1435}{chapter.407}\\protected@file@percent }\n\\newlabel{installing-liferay-enterprise-search-monitoring}{{407}{1435}{Installing Liferay Enterprise Search Monitoring}{chapter.407}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {407.1}Enable Encrypting Communication (TLS/SSL) in Elasticsearch and in Liferay DXP}{1436}{section.407.1}\\protected@file@percent }\n\\newlabel{enable-encrypting-communication-tlsssl-in-elasticsearch-and-in-liferay-dxp}{{407.1}{1436}{Enable Encrypting Communication (TLS/SSL) in Elasticsearch and in Liferay DXP}{section.407.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {407.2}Enable Data Collection}{1436}{section.407.2}\\protected@file@percent }\n\\newlabel{enable-data-collection}{{407.2}{1436}{Enable Data Collection}{section.407.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {407.3}Install Kibana}{1436}{section.407.3}\\protected@file@percent }\n\\newlabel{install-kibana}{{407.3}{1436}{Install Kibana}{section.407.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {407.4}Configure Kibana with Authentication}{1437}{section.407.4}\\protected@file@percent }\n\\newlabel{configure-kibana-with-authentication}{{407.4}{1437}{Configure Kibana with Authentication}{section.407.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {407.5}Configuring Kibana with Encryption (TLS/SSL)}{1437}{section.407.5}\\protected@file@percent }\n\\newlabel{configuring-kibana-with-encryption-tlsssl}{{407.5}{1437}{Configuring Kibana with Encryption (TLS/SSL)}{section.407.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {407.6}Configuring the Liferay Enterprise Search Monitoring app}{1438}{section.407.6}\\protected@file@percent }\n\\newlabel{configuring-the-liferay-enterprise-search-monitoring-app}{{407.6}{1438}{Configuring the Liferay Enterprise Search Monitoring app}{section.407.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {407.7}Monitoring in Liferay DXP}{1439}{section.407.7}\\protected@file@percent }\n\\newlabel{monitoring-in-liferay-dxp}{{407.7}{1439}{Monitoring in Liferay DXP}{section.407.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {407.8}Example Kibana Configuration}{1439}{section.407.8}\\protected@file@percent }\n\\newlabel{example-kibana-configuration}{{407.8}{1439}{Example Kibana Configuration}{section.407.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {408}Liferay Enterprise Search: Learning to Rank}{1441}{chapter.408}\\protected@file@percent }\n\\newlabel{liferay-enterprise-search-learning-to-rank}{{408}{1441}{Liferay Enterprise Search: Learning to Rank}{chapter.408}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {408.1}Disabling Learning to Rank on a Search Page}{1441}{section.408.1}\\protected@file@percent }\n\\newlabel{disabling-learning-to-rank-on-a-search-page}{{408.1}{1441}{Disabling Learning to Rank on a Search Page}{section.408.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {408.2}Prerequisites}{1442}{section.408.2}\\protected@file@percent }\n\\newlabel{prerequisites}{{408.2}{1442}{Prerequisites}{section.408.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {408.3}Technical Overview}{1442}{section.408.3}\\protected@file@percent }\n\\newlabel{technical-overview}{{408.3}{1442}{Technical Overview}{section.408.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {408.4}Model Training}{1443}{section.408.4}\\protected@file@percent }\n\\newlabel{model-training}{{408.4}{1443}{Model Training}{section.408.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {409}Configuring Learning to Rank}{1445}{chapter.409}\\protected@file@percent }\n\\newlabel{configuring-learning-to-rank}{{409}{1445}{Configuring Learning to Rank}{chapter.409}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {409.1}Step 1: Install the Learning to Rank Plugin on Elasticsearch}{1445}{section.409.1}\\protected@file@percent }\n\\newlabel{step-1-install-the-learning-to-rank-plugin-on-elasticsearch}{{409.1}{1445}{Step 1: Install the Learning to Rank Plugin on Elasticsearch}{section.409.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {409.2}Step 2: Training and Uploading a Model}{1445}{section.409.2}\\protected@file@percent }\n\\newlabel{step-2-training-and-uploading-a-model}{{409.2}{1445}{Step 2: Training and Uploading a Model}{section.409.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {409.3}Upload the Model to the Learning to Rank Plugin}{1446}{section.409.3}\\protected@file@percent }\n\\newlabel{upload-the-model-to-the-learning-to-rank-plugin}{{409.3}{1446}{Upload the Model to the Learning to Rank Plugin}{section.409.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {409.4}Step 3: Enable Learning to Rank}{1447}{section.409.4}\\protected@file@percent }\n\\newlabel{step-3-enable-learning-to-rank}{{409.4}{1447}{Step 3: Enable Learning to Rank}{section.409.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {409.1}{\\ignorespaces Enable Learning to Rank in Liferay DXP from the System Settings entry.}}{1448}{figure.409.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {410}Installing Solr}{1449}{chapter.410}\\protected@file@percent }\n\\newlabel{installing-solr}{{410}{1449}{Installing Solr}{chapter.410}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {410.1}Blacklisting Elasticsearch-Only Features}{1449}{section.410.1}\\protected@file@percent }\n\\newlabel{blacklisting-elasticsearch-only-features}{{410.1}{1449}{Blacklisting Elasticsearch-Only Features}{section.410.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {411}Installing Solr: Basic Installation}{1451}{chapter.411}\\protected@file@percent }\n\\newlabel{installing-solr-basic-installation}{{411}{1451}{Installing Solr: Basic Installation}{chapter.411}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {411.1}Installing and Configuring Solr 7}{1451}{section.411.1}\\protected@file@percent }\n\\newlabel{installing-and-configuring-solr-7}{{411.1}{1451}{Installing and Configuring Solr 7}{section.411.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {411.2}Installing and Configuring the Liferay Solr Adapter}{1453}{section.411.2}\\protected@file@percent }\n\\newlabel{installing-and-configuring-the-liferay-solr-adapter}{{411.2}{1453}{Installing and Configuring the Liferay Solr Adapter}{section.411.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {411.3}Stopping the Elasticsearch Connector}{1453}{section.411.3}\\protected@file@percent }\n\\newlabel{stopping-the-elasticsearch-connector}{{411.3}{1453}{Stopping the Elasticsearch Connector}{section.411.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {411.4}Install and Configure the Solr Connector}{1454}{section.411.4}\\protected@file@percent }\n\\newlabel{install-and-configure-the-solr-connector}{{411.4}{1454}{Install and Configure the Solr Connector}{section.411.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {411.1}{\\ignorespaces Once the Solr connector is installed, you can re-index your Liferay DXP data against your Solr server.}}{1454}{figure.411.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {411.2}{\\ignorespaces You can configure Solr from Liferay DXP's System Settings application. This is most useful during development and testing.}}{1455}{figure.411.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {412}Installing Solr: High Availability with SolrCloud}{1457}{chapter.412}\\protected@file@percent }\n\\newlabel{installing-solr-high-availability-with-solrcloud}{{412}{1457}{Installing Solr: High Availability with SolrCloud}{chapter.412}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {412.1}Configure the Solr Adapter for SolrCloud}{1459}{section.412.1}\\protected@file@percent }\n\\newlabel{configure-the-solr-adapter-for-solrcloud}{{412.1}{1459}{Configure the Solr Adapter for SolrCloud}{section.412.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {412.1}{\\ignorespaces From the Solr 7 System Settings entry, set the \\emph  {Client Type} to \\emph  {Cloud}.}}{1459}{figure.412.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {413}Solr Connector Settings}{1461}{chapter.413}\\protected@file@percent }\n\\newlabel{solr-connector-settings}{{413}{1461}{Solr Connector Settings}{chapter.413}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {413.1}Solr 7}{1461}{section.413.1}\\protected@file@percent }\n\\newlabel{solr-7}{{413.1}{1461}{Solr 7}{section.413.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {414}Securing Liferay DXP}{1463}{chapter.414}\\protected@file@percent }\n\\newlabel{securing-liferay-dxp}{{414}{1463}{Securing Liferay DXP}{chapter.414}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {414.1}Authentication Overview}{1463}{section.414.1}\\protected@file@percent }\n\\newlabel{authentication-overview}{{414.1}{1463}{Authentication Overview}{section.414.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {414.2}Authorization and Permission Checking}{1464}{section.414.2}\\protected@file@percent }\n\\newlabel{authorization-and-permission-checking}{{414.2}{1464}{Authorization and Permission Checking}{section.414.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {414.3}Additional Security Features}{1464}{section.414.3}\\protected@file@percent }\n\\newlabel{additional-security-features}{{414.3}{1464}{Additional Security Features}{section.414.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {414.4}Secure Configuration and Run Recommendations}{1465}{section.414.4}\\protected@file@percent }\n\\newlabel{secure-configuration-and-run-recommendations}{{414.4}{1465}{Secure Configuration and Run Recommendations}{section.414.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {415}Logging into Liferay DXP}{1467}{chapter.415}\\protected@file@percent }\n\\newlabel{logging-into-liferay-dxp}{{415}{1467}{Logging into Liferay DXP}{chapter.415}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {415.1}Authentication Types}{1467}{section.415.1}\\protected@file@percent }\n\\newlabel{authentication-types}{{415.1}{1467}{Authentication Types}{section.415.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {415.2}The Sign In Portlet}{1468}{section.415.2}\\protected@file@percent }\n\\newlabel{the-sign-in-portlet}{{415.2}{1468}{The Sign In Portlet}{section.415.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {415.1}{\\ignorespaces By default, the Sign In portlet allows users to log in, create a new account, or request a password reset.}}{1468}{figure.415.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {416}Service Access Policies}{1471}{chapter.416}\\protected@file@percent }\n\\newlabel{service-access-policies}{{416}{1471}{Service Access Policies}{chapter.416}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {416.1}{\\ignorespaces To get to a service, a request must pass through the door lock of user permissions, the padlock of the verification layer, the brick wall of service access policies, and finally the safe of predefined IP permissions.}}{1472}{figure.416.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {416.1}Managing Service Access Policies}{1472}{section.416.1}\\protected@file@percent }\n\\newlabel{managing-service-access-policies}{{416.1}{1472}{Managing Service Access Policies}{section.416.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {416.2}Service Access Policy Module}{1474}{section.416.2}\\protected@file@percent }\n\\newlabel{service-access-policy-module}{{416.2}{1474}{Service Access Policy Module}{section.416.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {416.3}Summary}{1475}{section.416.3}\\protected@file@percent }\n\\newlabel{summary}{{416.3}{1475}{Summary}{section.416.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {416.4}Related Topics}{1475}{section.416.4}\\protected@file@percent }\n\\newlabel{related-topics-3}{{416.4}{1475}{Related Topics}{section.416.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {417}Authentication Verifiers}{1477}{chapter.417}\\protected@file@percent }\n\\newlabel{authentication-verifiers}{{417}{1477}{Authentication Verifiers}{chapter.417}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.1}Authentication Verification Process Overview}{1477}{section.417.1}\\protected@file@percent }\n\\newlabel{authentication-verification-process-overview}{{417.1}{1477}{Authentication Verification Process Overview}{section.417.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.2}Basic Auth Header}{1478}{section.417.2}\\protected@file@percent }\n\\newlabel{basic-auth-header}{{417.2}{1478}{Basic Auth Header}{section.417.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.3}Digest Auth Header}{1479}{section.417.3}\\protected@file@percent }\n\\newlabel{digest-auth-header}{{417.3}{1479}{Digest Auth Header}{section.417.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.4}HTTP Tunnel Extender}{1479}{section.417.4}\\protected@file@percent }\n\\newlabel{http-tunnel-extender}{{417.4}{1479}{HTTP Tunnel Extender}{section.417.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.5}Image Request Authentication Verifier}{1479}{section.417.5}\\protected@file@percent }\n\\newlabel{image-request-authentication-verifier}{{417.5}{1479}{Image Request Authentication Verifier}{section.417.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.6}Portal Sessions Auth Verifiers}{1479}{section.417.6}\\protected@file@percent }\n\\newlabel{portal-sessions-auth-verifiers}{{417.6}{1479}{Portal Sessions Auth Verifiers}{section.417.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.7}Request Parameter Auth Verifiers}{1480}{section.417.7}\\protected@file@percent }\n\\newlabel{request-parameter-auth-verifiers}{{417.7}{1480}{Request Parameter Auth Verifiers}{section.417.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.8}Tunnel Authentication Verifiers}{1480}{section.417.8}\\protected@file@percent }\n\\newlabel{tunnel-authentication-verifiers}{{417.8}{1480}{Tunnel Authentication Verifiers}{section.417.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {417.9}Related Topics}{1480}{section.417.9}\\protected@file@percent }\n\\newlabel{related-topics-4}{{417.9}{1480}{Related Topics}{section.417.9}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {418}LDAP}{1481}{chapter.418}\\protected@file@percent }\n\\newlabel{ldap}{{418}{1481}{LDAP}{chapter.418}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {418.1}Connection}{1481}{section.418.1}\\protected@file@percent }\n\\newlabel{connection}{{418.1}{1481}{Connection}{section.418.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {418.2}Checkpoint}{1482}{section.418.2}\\protected@file@percent }\n\\newlabel{checkpoint-1}{{418.2}{1482}{Checkpoint}{section.418.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {418.3}Instance Settings vs.~System Settings}{1482}{section.418.3}\\protected@file@percent }\n\\newlabel{instance-settings-vs.-system-settings}{{418.3}{1482}{Instance Settings vs.~System Settings}{section.418.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {418.4}Security}{1483}{section.418.4}\\protected@file@percent }\n\\newlabel{security}{{418.4}{1483}{Security}{section.418.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {418.5}Configuring LDAP Import/Export}{1483}{section.418.5}\\protected@file@percent }\n\\newlabel{configuring-ldap-importexport}{{418.5}{1483}{Configuring LDAP Import/Export}{section.418.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {418.6}Users}{1483}{section.418.6}\\protected@file@percent }\n\\newlabel{users}{{418.6}{1483}{Users}{section.418.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {418.7}Groups}{1484}{section.418.7}\\protected@file@percent }\n\\newlabel{groups}{{418.7}{1484}{Groups}{section.418.7}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {418.1}{\\ignorespaces You should see a list of users when you click the \\emph  {Test LDAP Users} button.}}{1485}{figure.418.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {418.8}Export}{1485}{section.418.8}\\protected@file@percent }\n\\newlabel{export}{{418.8}{1485}{Export}{section.418.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {418.9}Related Topics}{1486}{section.418.9}\\protected@file@percent }\n\\newlabel{related-topics-5}{{418.9}{1486}{Related Topics}{section.418.9}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {419}Configuring LDAP}{1487}{chapter.419}\\protected@file@percent }\n\\newlabel{configuring-ldap}{{419}{1487}{Configuring LDAP}{chapter.419}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {419.1}Export}{1487}{section.419.1}\\protected@file@percent }\n\\newlabel{export-1}{{419.1}{1487}{Export}{section.419.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {419.2}General}{1487}{section.419.2}\\protected@file@percent }\n\\newlabel{general}{{419.2}{1487}{General}{section.419.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {419.3}Import}{1488}{section.419.3}\\protected@file@percent }\n\\newlabel{import}{{419.3}{1488}{Import}{section.419.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {419.1}{\\ignorespaces Ziltoid and Rex have been imported because they logged in.}}{1488}{figure.419.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {419.4}Servers}{1489}{section.419.4}\\protected@file@percent }\n\\newlabel{servers}{{419.4}{1489}{Servers}{section.419.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {419.5}LDAP Options Available in System Settings}{1489}{section.419.5}\\protected@file@percent }\n\\newlabel{ldap-options-available-in-system-settings}{{419.5}{1489}{LDAP Options Available in System Settings}{section.419.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {420}Token-based Single Sign On Authentication}{1491}{chapter.420}\\protected@file@percent }\n\\newlabel{token-based-single-sign-on-authentication}{{420}{1491}{Token-based Single Sign On Authentication}{chapter.420}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {420.1}Required SiteMinder Configuration}{1492}{section.420.1}\\protected@file@percent }\n\\newlabel{required-siteminder-configuration}{{420.1}{1492}{Required SiteMinder Configuration}{section.420.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {420.2}Summary}{1492}{section.420.2}\\protected@file@percent }\n\\newlabel{summary-1}{{420.2}{1492}{Summary}{section.420.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {421}Authenticating with OpenID Connect}{1493}{chapter.421}\\protected@file@percent }\n\\newlabel{authenticating-with-openid-connect}{{421}{1493}{Authenticating with OpenID Connect}{chapter.421}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {421.1}Creating a Client in OpenID Connect Provider}{1493}{section.421.1}\\protected@file@percent }\n\\newlabel{creating-a-client-in-openid-connect-provider}{{421.1}{1493}{Creating a Client in OpenID Connect Provider}{section.421.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {421.2}Configuring an OpenID Connect Provider Connection}{1494}{section.421.2}\\protected@file@percent }\n\\newlabel{configuring-an-openid-connect-provider-connection}{{421.2}{1494}{Configuring an OpenID Connect Provider Connection}{section.421.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {421.3}Enabling OpenID Connect Authentication}{1495}{section.421.3}\\protected@file@percent }\n\\newlabel{enabling-openid-connect-authentication}{{421.3}{1495}{Enabling OpenID Connect Authentication}{section.421.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {421.4}Signing In With OpenID Connect}{1495}{section.421.4}\\protected@file@percent }\n\\newlabel{signing-in-with-openid-connect}{{421.4}{1495}{Signing In With OpenID Connect}{section.421.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {422}OpenAM Single Sign On Authentication}{1497}{chapter.422}\\protected@file@percent }\n\\newlabel{openam-single-sign-on-authentication}{{422}{1497}{OpenAM Single Sign On Authentication}{chapter.422}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {422.1}{\\ignorespaces OpenSSO Configuration.}}{1498}{figure.422.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {423}CAS (Central Authentication Service) Single Sign On Authentication}{1501}{chapter.423}\\protected@file@percent }\n\\newlabel{cas-central-authentication-service-single-sign-on-authentication}{{423}{1501}{CAS (Central Authentication Service) Single Sign On Authentication}{chapter.423}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {424}Authenticating Using SAML}{1503}{chapter.424}\\protected@file@percent }\n\\newlabel{authenticating-using-saml}{{424}{1503}{Authenticating Using SAML}{chapter.424}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {424.1}What's new in Liferay Connector to SAML 2.0}{1503}{section.424.1}\\protected@file@percent }\n\\newlabel{whats-new-in-liferay-connector-to-saml-2.0}{{424.1}{1503}{What's new in Liferay Connector to SAML 2.0}{section.424.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {424.2}Important SAML Paths}{1504}{section.424.2}\\protected@file@percent }\n\\newlabel{important-saml-paths}{{424.2}{1504}{Important SAML Paths}{section.424.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {424.3}Single Sign On}{1504}{section.424.3}\\protected@file@percent }\n\\newlabel{single-sign-on}{{424.3}{1504}{Single Sign On}{section.424.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {424.4}Identity Provider Initiated SSO}{1504}{section.424.4}\\protected@file@percent }\n\\newlabel{identity-provider-initiated-sso}{{424.4}{1504}{Identity Provider Initiated SSO}{section.424.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {424.1}{\\ignorespaces Identity Provider Initiated SSO}}{1505}{figure.424.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{The SSO Request to the IdP}{1505}{figure.424.1}\\protected@file@percent }\n\\newlabel{the-sso-request-to-the-idp}{{424.4}{1505}{The SSO Request to the IdP}{figure.424.1}{}}\n\\@writefile{toc}{\\contentsline {subsection}{The SSO Response from the IdP}{1505}{figure.424.1}\\protected@file@percent }\n\\newlabel{the-sso-response-from-the-idp}{{424.4}{1505}{The SSO Response from the IdP}{figure.424.1}{}}\n\\@writefile{toc}{\\contentsline {subsection}{The SP Processes the SSO Response}{1506}{figure.424.1}\\protected@file@percent }\n\\newlabel{the-sp-processes-the-sso-response}{{424.4}{1506}{The SP Processes the SSO Response}{figure.424.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {424.5}Service Provider Initiated SSO}{1506}{section.424.5}\\protected@file@percent }\n\\newlabel{service-provider-initiated-sso}{{424.5}{1506}{Service Provider Initiated SSO}{section.424.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{The SSO Request to the SP}{1506}{figure.424.2}\\protected@file@percent }\n\\newlabel{the-sso-request-to-the-sp}{{424.5}{1506}{The SSO Request to the SP}{figure.424.2}{}}\n\\@writefile{toc}{\\contentsline {subsection}{The AuthnRequest to the IdP}{1506}{figure.424.2}\\protected@file@percent }\n\\newlabel{the-authnrequest-to-the-idp}{{424.5}{1506}{The AuthnRequest to the IdP}{figure.424.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {424.2}{\\ignorespaces Service Provider Initiated SSO}}{1507}{figure.424.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{The SSO Response from the IdP}{1507}{figure.424.2}\\protected@file@percent }\n\\newlabel{the-sso-response-from-the-idp-1}{{424.5}{1507}{The SSO Response from the IdP}{figure.424.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {424.6}Single Log Off}{1507}{section.424.6}\\protected@file@percent }\n\\newlabel{single-log-off}{{424.6}{1507}{Single Log Off}{section.424.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {424.7}Identity Provider Initiated SLO}{1508}{section.424.7}\\protected@file@percent }\n\\newlabel{identity-provider-initiated-slo}{{424.7}{1508}{Identity Provider Initiated SLO}{section.424.7}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {424.3}{\\ignorespaces Identity Provider Initiated SLO}}{1508}{figure.424.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{The SLO Request to the IdP}{1508}{figure.424.3}\\protected@file@percent }\n\\newlabel{the-slo-request-to-the-idp}{{424.7}{1508}{The SLO Request to the IdP}{figure.424.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{The SLO Response from the SP}{1509}{figure.424.3}\\protected@file@percent }\n\\newlabel{the-slo-response-from-the-sp}{{424.7}{1509}{The SLO Response from the SP}{figure.424.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {424.8}Service Provider Initiated SLO}{1509}{section.424.8}\\protected@file@percent }\n\\newlabel{service-provider-initiated-slo}{{424.8}{1509}{Service Provider Initiated SLO}{section.424.8}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {424.4}{\\ignorespaces Service Provider Initiated SLO}}{1509}{figure.424.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{The SLO Request to the SP}{1509}{figure.424.4}\\protected@file@percent }\n\\newlabel{the-slo-request-to-the-sp}{{424.8}{1509}{The SLO Request to the SP}{figure.424.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{The SLO Response from the SP}{1510}{figure.424.4}\\protected@file@percent }\n\\newlabel{the-slo-response-from-the-sp-1}{{424.8}{1510}{The SLO Response from the SP}{figure.424.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {424.9}Related Topics}{1510}{section.424.9}\\protected@file@percent }\n\\newlabel{related-topics-6}{{424.9}{1510}{Related Topics}{section.424.9}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {425}Setting up Liferay DXP as a SAML Identity Provider}{1511}{chapter.425}\\protected@file@percent }\n\\newlabel{setting-up-liferay-dxp-as-a-saml-identity-provider}{{425}{1511}{Setting up Liferay DXP as a SAML Identity Provider}{chapter.425}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {425.1}Storing Your Keystore}{1511}{section.425.1}\\protected@file@percent }\n\\newlabel{storing-your-keystore}{{425.1}{1511}{Storing Your Keystore}{section.425.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {425.2}Configuring Liferay DXP as a SAML Identity Provider}{1512}{section.425.2}\\protected@file@percent }\n\\newlabel{configuring-liferay-dxp-as-a-saml-identity-provider}{{425.2}{1512}{Configuring Liferay DXP as a SAML Identity Provider}{section.425.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {425.1}{\\ignorespaces Select a SAML role for Liferay and enter an entity ID.}}{1512}{figure.425.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {425.3}Changing the Identity Provider Settings}{1513}{section.425.3}\\protected@file@percent }\n\\newlabel{changing-the-identity-provider-settings}{{425.3}{1513}{Changing the Identity Provider Settings}{section.425.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {425.2}{\\ignorespaces The General tab of the SAML Admin portlet displays information about the current certificate and private key and allows administrators to download the certificate or replace the certificate.}}{1514}{figure.425.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {425.4}Checkpoint}{1515}{section.425.4}\\protected@file@percent }\n\\newlabel{checkpoint-2}{{425.4}{1515}{Checkpoint}{section.425.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {426}Registering a SAML Service Provider}{1517}{chapter.426}\\protected@file@percent }\n\\newlabel{registering-a-saml-service-provider}{{426}{1517}{Registering a SAML Service Provider}{chapter.426}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {426.1}Checkpoint}{1518}{section.426.1}\\protected@file@percent }\n\\newlabel{checkpoint-3}{{426.1}{1518}{Checkpoint}{section.426.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {427}Setting up Liferay DXP as a SAML Service Provider}{1521}{chapter.427}\\protected@file@percent }\n\\newlabel{setting-up-liferay-dxp-as-a-saml-service-provider}{{427}{1521}{Setting up Liferay DXP as a SAML Service Provider}{chapter.427}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {427.1}Checkpoint}{1523}{section.427.1}\\protected@file@percent }\n\\newlabel{checkpoint-4}{{427.1}{1523}{Checkpoint}{section.427.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {427.2}Setting Up Liferay DXP as a SAML Service Provider in a Clustered Environment}{1523}{section.427.2}\\protected@file@percent }\n\\newlabel{setting-up-liferay-dxp-as-a-saml-service-provider-in-a-clustered-environment}{{427.2}{1523}{Setting Up Liferay DXP as a SAML Service Provider in a Clustered Environment}{section.427.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {428}Changing the Settings for Service Provider and Identity Provider Connections}{1525}{chapter.428}\\protected@file@percent }\n\\newlabel{changing-the-settings-for-service-provider-and-identity-provider-connections}{{428}{1525}{Changing the Settings for Service Provider and Identity Provider Connections}{chapter.428}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {428.1}Changing the SAML Identity Provider Connection Settings}{1526}{section.428.1}\\protected@file@percent }\n\\newlabel{changing-the-saml-identity-provider-connection-settings}{{428.1}{1526}{Changing the SAML Identity Provider Connection Settings}{section.428.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {428.2}Configuring Negotiation Via metadata.xml}{1528}{section.428.2}\\protected@file@percent }\n\\newlabel{configuring-negotiation-via-metadata.xml}{{428.2}{1528}{Configuring Negotiation Via metadata.xml}{section.428.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {429}NTLM Single Sign On Authentication}{1531}{chapter.429}\\protected@file@percent }\n\\newlabel{ntlm-single-sign-on-authentication}{{429}{1531}{NTLM Single Sign On Authentication}{chapter.429}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {429.1}Summary}{1532}{section.429.1}\\protected@file@percent }\n\\newlabel{summary-2}{{429.1}{1532}{Summary}{section.429.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {430}OpenID Single Sign On Authentication}{1533}{chapter.430}\\protected@file@percent }\n\\newlabel{openid-single-sign-on-authentication}{{430}{1533}{OpenID Single Sign On Authentication}{chapter.430}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {430.1}OpenID at the System Scope}{1533}{section.430.1}\\protected@file@percent }\n\\newlabel{openid-at-the-system-scope}{{430.1}{1533}{OpenID at the System Scope}{section.430.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {430.2}OpenID at the Instance Scope}{1534}{section.430.2}\\protected@file@percent }\n\\newlabel{openid-at-the-instance-scope}{{430.2}{1534}{OpenID at the Instance Scope}{section.430.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {431}Authenticating with Kerberos}{1535}{chapter.431}\\protected@file@percent }\n\\newlabel{authenticating-with-kerberos}{{431}{1535}{Authenticating with Kerberos}{chapter.431}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {431.1}How Kerberos Authentication Works}{1536}{section.431.1}\\protected@file@percent }\n\\newlabel{how-kerberos-authentication-works}{{431.1}{1536}{How Kerberos Authentication Works}{section.431.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {431.1}{\\ignorespaces Kerberos authentication requires a web server in front of your Liferay DXP server.}}{1536}{figure.431.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {431.2}Configuring Kerberos Authentication}{1537}{section.431.2}\\protected@file@percent }\n\\newlabel{configuring-kerberos-authentication}{{431.2}{1537}{Configuring Kerberos Authentication}{section.431.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {431.3}Creating the User Keytab}{1537}{section.431.3}\\protected@file@percent }\n\\newlabel{creating-the-user-keytab}{{431.3}{1537}{Creating the User Keytab}{section.431.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {431.4}Configuring Your Web Server}{1537}{section.431.4}\\protected@file@percent }\n\\newlabel{configuring-your-web-server}{{431.4}{1537}{Configuring Your Web Server}{section.431.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {431.5}Connecting Liferay DXP to Active Directory over LDAP}{1538}{section.431.5}\\protected@file@percent }\n\\newlabel{connecting-liferay-dxp-to-active-directory-over-ldap}{{431.5}{1538}{Connecting Liferay DXP to Active Directory over LDAP}{section.431.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {431.6}Configuring your Clients}{1539}{section.431.6}\\protected@file@percent }\n\\newlabel{configuring-your-clients}{{431.6}{1539}{Configuring your Clients}{section.431.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {432}Configuring CORS}{1541}{chapter.432}\\protected@file@percent }\n\\newlabel{configuring-cors}{{432}{1541}{Configuring CORS}{chapter.432}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {432.1}Enabling CORS for Liferay DXP Services}{1541}{section.432.1}\\protected@file@percent }\n\\newlabel{enabling-cors-for-liferay-dxp-services}{{432.1}{1541}{Enabling CORS for Liferay DXP Services}{section.432.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {432.1}{\\ignorespaces The CORS system settings provide a way to configure CORS headers for Liferay services.}}{1542}{figure.432.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {432.2}Enabling CORS for JAX-RS Applications}{1543}{section.432.2}\\protected@file@percent }\n\\newlabel{enabling-cors-for-jax-rs-applications}{{432.2}{1543}{Enabling CORS for JAX-RS Applications}{section.432.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {432.2}{\\ignorespaces There's a separate system settings category for CORS web contexts.}}{1544}{figure.432.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {433}AntiSamy}{1545}{chapter.433}\\protected@file@percent }\n\\newlabel{antisamy}{{433}{1545}{AntiSamy}{chapter.433}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {433.1}Configuring AntiSamy}{1545}{section.433.1}\\protected@file@percent }\n\\newlabel{configuring-antisamy}{{433.1}{1545}{Configuring AntiSamy}{section.433.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {433.1}{\\ignorespaces Liferay DXP's AntiSamy configuration options allow you to specify both a blacklist and a whitelist.}}{1546}{figure.433.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {433.2}Using Wildcards}{1546}{section.433.2}\\protected@file@percent }\n\\newlabel{using-wildcards}{{433.2}{1546}{Using Wildcards}{section.433.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {434}OAuth 2.0}{1547}{chapter.434}\\protected@file@percent }\n\\newlabel{oauth-2.0}{{434}{1547}{OAuth 2.0}{chapter.434}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {434.1}Flow of OAuth 2.0}{1547}{section.434.1}\\protected@file@percent }\n\\newlabel{flow-of-oauth-2.0}{{434.1}{1547}{Flow of OAuth 2.0}{section.434.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {434.1}{\\ignorespaces OAuth 2.0 takes advantage of web standards.}}{1548}{figure.434.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {434.2}OAuth 2.0 Terminology}{1549}{section.434.2}\\protected@file@percent }\n\\newlabel{oauth-2.0-terminology}{{434.2}{1549}{OAuth 2.0 Terminology}{section.434.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {434.3}Creating an Application}{1550}{section.434.3}\\protected@file@percent }\n\\newlabel{creating-an-application}{{434.3}{1550}{Creating an Application}{section.434.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {434.2}{\\ignorespaces Adding an application registers it so users can authorize access to their data.}}{1550}{figure.434.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {435}OAuth2 Scopes}{1553}{chapter.435}\\protected@file@percent }\n\\newlabel{oauth2-scopes}{{435}{1553}{OAuth2 Scopes}{chapter.435}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {435.1}Creating a Scope for a JSONWS Service}{1553}{section.435.1}\\protected@file@percent }\n\\newlabel{creating-a-scope-for-a-jsonws-service}{{435.1}{1553}{Creating a Scope for a JSONWS Service}{section.435.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {435.1}{\\ignorespaces A Service Access Policy defines a scope for OAuth 2.0 applications.}}{1554}{figure.435.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {435.2}Creating the Authorization Page}{1554}{section.435.2}\\protected@file@percent }\n\\newlabel{creating-the-authorization-page}{{435.2}{1554}{Creating the Authorization Page}{section.435.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {435.2}{\\ignorespaces Scopes named with the proper prefix appear in the Scopes tab of your application configuration.}}{1555}{figure.435.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {436}Authorizing Account Access with OAuth2}{1557}{chapter.436}\\protected@file@percent }\n\\newlabel{authorizing-account-access-with-oauth2}{{436}{1557}{Authorizing Account Access with OAuth2}{chapter.436}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {436.1}Authorization Code Flow}{1557}{section.436.1}\\protected@file@percent }\n\\newlabel{authorization-code-flow}{{436.1}{1557}{Authorization Code Flow}{section.436.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {436.2}PKCE Extended Authorization Code Flow}{1558}{section.436.2}\\protected@file@percent }\n\\newlabel{pkce-extended-authorization-code-flow}{{436.2}{1558}{PKCE Extended Authorization Code Flow}{section.436.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {436.3}Client Credentials and Resource Owner Flows}{1559}{section.436.3}\\protected@file@percent }\n\\newlabel{client-credentials-and-resource-owner-flows}{{436.3}{1559}{Client Credentials and Resource Owner Flows}{section.436.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {436.4}Token Use}{1559}{section.436.4}\\protected@file@percent }\n\\newlabel{token-use}{{436.4}{1559}{Token Use}{section.436.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {436.5}Revoking Access}{1559}{section.436.5}\\protected@file@percent }\n\\newlabel{revoking-access}{{436.5}{1559}{Revoking Access}{section.436.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {436.1}{\\ignorespaces Users have complete control over what applications have access to their data in their account profiles.}}{1560}{figure.436.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {436.2}{\\ignorespaces All authorizations for an app appear in the Authorizations tab for the app.}}{1560}{figure.436.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {436.6}Summary}{1561}{section.436.6}\\protected@file@percent }\n\\newlabel{summary-3}{{436.6}{1561}{Summary}{section.436.6}{}}\n\\gdef \\LT@iv {\\LT@entry \n    {1}{298.46002pt}\\LT@entry \n    {1}{171.29498pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {437}Upgrading to 7.0}{1563}{chapter.437}\\protected@file@percent }\n\\newlabel{upgrading-to-7.0}{{437}{1563}{Upgrading to 7.0}{chapter.437}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {438}Planning for Deprecated Applications}{1567}{chapter.438}\\protected@file@percent }\n\\newlabel{planning-for-deprecated-applications}{{438}{1567}{Planning for Deprecated Applications}{chapter.438}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {439}Test Upgrading a Liferay DXP Backup Copy}{1569}{chapter.439}\\protected@file@percent }\n\\newlabel{test-upgrading-a-liferay-dxp-backup-copy}{{439}{1569}{Test Upgrading a Liferay DXP Backup Copy}{chapter.439}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {439.1}Preparing a Test Server and Database}{1569}{section.439.1}\\protected@file@percent }\n\\newlabel{preparing-a-test-server-and-database}{{439.1}{1569}{Preparing a Test Server and Database}{section.439.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {439.2}Copy the Production Installation to a Test Server}{1570}{section.439.2}\\protected@file@percent }\n\\newlabel{copy-the-production-installation-to-a-test-server}{{439.2}{1570}{Copy the Production Installation to a Test Server}{section.439.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {439.3}Copy the Production Backup to the Test Database}{1570}{section.439.3}\\protected@file@percent }\n\\newlabel{copy-the-production-backup-to-the-test-database}{{439.3}{1570}{Copy the Production Backup to the Test Database}{section.439.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {440}Pruning the Database}{1571}{chapter.440}\\protected@file@percent }\n\\newlabel{pruning-the-database}{{440}{1571}{Pruning the Database}{chapter.440}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {440.1}Remove Duplicate Web Content Structure Field Names}{1571}{section.440.1}\\protected@file@percent }\n\\newlabel{remove-duplicate-web-content-structure-field-names}{{440.1}{1571}{Remove Duplicate Web Content Structure Field Names}{section.440.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {440.2}Find and Remove Unused Objects}{1571}{section.440.2}\\protected@file@percent }\n\\newlabel{find-and-remove-unused-objects}{{440.2}{1571}{Find and Remove Unused Objects}{section.440.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {440.3}Objects From the Large/Populated Tables}{1572}{section.440.3}\\protected@file@percent }\n\\newlabel{objects-from-the-largepopulated-tables}{{440.3}{1572}{Objects From the Large/Populated Tables}{section.440.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {440.4}Common Object Types Worth Checking}{1572}{section.440.4}\\protected@file@percent }\n\\newlabel{common-object-types-worth-checking}{{440.4}{1572}{Common Object Types Worth Checking}{section.440.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {440.5}Test with the Pruned Database Copy}{1574}{section.440.5}\\protected@file@percent }\n\\newlabel{test-with-the-pruned-database-copy}{{440.5}{1574}{Test with the Pruned Database Copy}{section.440.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {441}Example: Removing Intermediate Journal Article Versions}{1575}{chapter.441}\\protected@file@percent }\n\\newlabel{example-removing-intermediate-journal-article-versions}{{441}{1575}{Example: Removing Intermediate Journal Article Versions}{chapter.441}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {442}Upgrading Your Test Server and Database}{1579}{chapter.442}\\protected@file@percent }\n\\newlabel{upgrading-your-test-server-and-database}{{442}{1579}{Upgrading Your Test Server and Database}{chapter.442}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {442.1}Install Liferay on a Test Server and Configure It to Use the Pruned Database}{1579}{section.442.1}\\protected@file@percent }\n\\newlabel{install-liferay-on-a-test-server-and-configure-it-to-use-the-pruned-database}{{442.1}{1579}{Install Liferay on a Test Server and Configure It to Use the Pruned Database}{section.442.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {442.2}Tune Your Database for the Upgrade}{1579}{section.442.2}\\protected@file@percent }\n\\newlabel{tune-your-database-for-the-upgrade}{{442.2}{1579}{Tune Your Database for the Upgrade}{section.442.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {442.3}Upgrade the Database}{1579}{section.442.3}\\protected@file@percent }\n\\newlabel{upgrade-the-database}{{442.3}{1579}{Upgrade the Database}{section.442.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {442.4}Test the Upgraded Portal and Resolve Any Issues}{1580}{section.442.4}\\protected@file@percent }\n\\newlabel{test-the-upgraded-portal-and-resolve-any-issues}{{442.4}{1580}{Test the Upgraded Portal and Resolve Any Issues}{section.442.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {442.5}Checkpoint: You've Pruned and Upgraded a Production Database Copy}{1580}{section.442.5}\\protected@file@percent }\n\\newlabel{checkpoint-youve-pruned-and-upgraded-a-production-database-copy}{{442.5}{1580}{Checkpoint: You've Pruned and Upgraded a Production Database Copy}{section.442.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {443}Preparing to Upgrade the Liferay DXP Database}{1581}{chapter.443}\\protected@file@percent }\n\\newlabel{preparing-to-upgrade-the-liferay-dxp-database}{{443}{1581}{Preparing to Upgrade the Liferay DXP Database}{chapter.443}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {443.1}Remove All Unused Objects You Identified Earlier}{1581}{section.443.1}\\protected@file@percent }\n\\newlabel{remove-all-unused-objects-you-identified-earlier}{{443.1}{1581}{Remove All Unused Objects You Identified Earlier}{section.443.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {443.2}Test Using the Pruned Database}{1581}{section.443.2}\\protected@file@percent }\n\\newlabel{test-using-the-pruned-database}{{443.2}{1581}{Test Using the Pruned Database}{section.443.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {443.3}Upgrade Your Marketplace Apps}{1582}{section.443.3}\\protected@file@percent }\n\\newlabel{upgrade-your-marketplace-apps}{{443.3}{1582}{Upgrade Your Marketplace Apps}{section.443.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {443.4}Publish all Staged Changes to Production}{1582}{section.443.4}\\protected@file@percent }\n\\newlabel{publish-all-staged-changes-to-production}{{443.4}{1582}{Publish all Staged Changes to Production}{section.443.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {443.5}Synchronize a Complete Backup}{1582}{section.443.5}\\protected@file@percent }\n\\newlabel{synchronize-a-complete-backup}{{443.5}{1582}{Synchronize a Complete Backup}{section.443.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {444}Preparing a New Liferay DXP Server for Data Upgrade}{1583}{chapter.444}\\protected@file@percent }\n\\newlabel{preparing-a-new-liferay-dxp-server-for-data-upgrade}{{444}{1583}{Preparing a New Liferay DXP Server for Data Upgrade}{chapter.444}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {444.1}Request an Upgrade Patch from Liferay Support (Liferay DXP only)}{1583}{section.444.1}\\protected@file@percent }\n\\newlabel{request-an-upgrade-patch-from-liferay-support-liferay-dxp-only}{{444.1}{1583}{Request an Upgrade Patch from Liferay Support (Liferay DXP only)}{section.444.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {444.2}Install Liferay}{1583}{section.444.2}\\protected@file@percent }\n\\newlabel{install-liferay}{{444.2}{1583}{Install Liferay}{section.444.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {444.3}Install the Latest Upgrade Patch or Fix Pack (Liferay DXP only)}{1584}{section.444.3}\\protected@file@percent }\n\\newlabel{install-the-latest-upgrade-patch-or-fix-pack-liferay-dxp-only}{{444.3}{1584}{Install the Latest Upgrade Patch or Fix Pack (Liferay DXP only)}{section.444.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {444.4}Migrate Your OSGi Configurations (7.0+)}{1584}{section.444.4}\\protected@file@percent }\n\\newlabel{migrate-your-osgi-configurations-7.0}{{444.4}{1584}{Migrate Your OSGi Configurations (7.0+)}{section.444.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {444.5}Migrate Your Portal Properties}{1584}{section.444.5}\\protected@file@percent }\n\\newlabel{migrate-your-portal-properties}{{444.5}{1584}{Migrate Your Portal Properties}{section.444.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {444.6}Update Your Portal Properties}{1584}{section.444.6}\\protected@file@percent }\n\\newlabel{update-your-portal-properties}{{444.6}{1584}{Update Your Portal Properties}{section.444.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {444.7}Convert Applicable Properties to OSGi Configurations}{1585}{section.444.7}\\protected@file@percent }\n\\newlabel{convert-applicable-properties-to-osgi-configurations}{{444.7}{1585}{Convert Applicable Properties to OSGi Configurations}{section.444.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {444.8}Update Your Database Driver}{1586}{section.444.8}\\protected@file@percent }\n\\newlabel{update-your-database-driver}{{444.8}{1586}{Update Your Database Driver}{section.444.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {444.9}Configure Your Documents and Media File Store}{1586}{section.444.9}\\protected@file@percent }\n\\newlabel{configure-your-documents-and-media-file-store}{{444.9}{1586}{Configure Your Documents and Media File Store}{section.444.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {444.10}Configure Kerberos in place of NTLM}{1587}{section.444.10}\\protected@file@percent }\n\\newlabel{configure-kerberos-in-place-of-ntlm}{{444.10}{1587}{Configure Kerberos in place of NTLM}{section.444.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {444.11}Disable Indexing}{1587}{section.444.11}\\protected@file@percent }\n\\newlabel{disable-indexing}{{444.11}{1587}{Disable Indexing}{section.444.11}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {445}Upgrading the Liferay DXP Data}{1589}{chapter.445}\\protected@file@percent }\n\\newlabel{upgrading-the-liferay-dxp-data}{{445}{1589}{Upgrading the Liferay DXP Data}{chapter.445}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {446}Tuning for the Data Upgrade}{1591}{chapter.446}\\protected@file@percent }\n\\newlabel{tuning-for-the-data-upgrade}{{446}{1591}{Tuning for the Data Upgrade}{chapter.446}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.1}Tuning the Database Upgrade Java Process}{1592}{section.446.1}\\protected@file@percent }\n\\newlabel{tuning-the-database-upgrade-java-process}{{446.1}{1592}{Tuning the Database Upgrade Java Process}{section.446.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.2}Tuning the Database Transaction Engine for Executing Updates}{1593}{section.446.2}\\protected@file@percent }\n\\newlabel{tuning-the-database-transaction-engine-for-executing-updates}{{446.2}{1593}{Tuning the Database Transaction Engine for Executing Updates}{section.446.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.3}IBM DB2}{1593}{section.446.3}\\protected@file@percent }\n\\newlabel{ibm-db2}{{446.3}{1593}{IBM DB2}{section.446.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.4}MariaDB}{1593}{section.446.4}\\protected@file@percent }\n\\newlabel{mariadb}{{446.4}{1593}{MariaDB}{section.446.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.5}Microsoft SQL Server}{1593}{section.446.5}\\protected@file@percent }\n\\newlabel{microsoft-sql-server}{{446.5}{1593}{Microsoft SQL Server}{section.446.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.6}MySQL}{1593}{section.446.6}\\protected@file@percent }\n\\newlabel{mysql}{{446.6}{1593}{MySQL}{section.446.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.7}Oracle Database}{1594}{section.446.7}\\protected@file@percent }\n\\newlabel{oracle-database}{{446.7}{1594}{Oracle Database}{section.446.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.8}PostgreSQL}{1594}{section.446.8}\\protected@file@percent }\n\\newlabel{postgresql}{{446.8}{1594}{PostgreSQL}{section.446.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.9}Tuning the Database Transaction Log}{1594}{section.446.9}\\protected@file@percent }\n\\newlabel{tuning-the-database-transaction-log}{{446.9}{1594}{Tuning the Database Transaction Log}{section.446.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.10}IBM DB2}{1594}{section.446.10}\\protected@file@percent }\n\\newlabel{ibm-db2-1}{{446.10}{1594}{IBM DB2}{section.446.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.11}MariaDB}{1594}{section.446.11}\\protected@file@percent }\n\\newlabel{mariadb-1}{{446.11}{1594}{MariaDB}{section.446.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.12}Microsoft SQL Server}{1594}{section.446.12}\\protected@file@percent }\n\\newlabel{microsoft-sql-server-1}{{446.12}{1594}{Microsoft SQL Server}{section.446.12}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.13}MySQL}{1595}{section.446.13}\\protected@file@percent }\n\\newlabel{mysql-1}{{446.13}{1595}{MySQL}{section.446.13}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.14}Oracle Database}{1595}{section.446.14}\\protected@file@percent }\n\\newlabel{oracle-database-1}{{446.14}{1595}{Oracle Database}{section.446.14}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {446.15}PostgreSQL}{1595}{section.446.15}\\protected@file@percent }\n\\newlabel{postgresql-1}{{446.15}{1595}{PostgreSQL}{section.446.15}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {447}Configuring the Data Upgrade}{1597}{chapter.447}\\protected@file@percent }\n\\newlabel{configuring-the-data-upgrade}{{447}{1597}{Configuring the Data Upgrade}{chapter.447}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {447.1}Configuring the Core Upgrade}{1597}{section.447.1}\\protected@file@percent }\n\\newlabel{configuring-the-core-upgrade}{{447.1}{1597}{Configuring the Core Upgrade}{section.447.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {447.2}Configuring app-server.properties}{1598}{section.447.2}\\protected@file@percent }\n\\newlabel{configuring-app-server.properties}{{447.2}{1598}{Configuring app-server.properties}{section.447.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {447.3}Configuring portal-upgrade-database.properties}{1598}{section.447.3}\\protected@file@percent }\n\\newlabel{configuring-portal-upgrade-database.properties}{{447.3}{1598}{Configuring portal-upgrade-database.properties}{section.447.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {447.4}Configuring portal-upgrade-ext.properties}{1599}{section.447.4}\\protected@file@percent }\n\\newlabel{configuring-portal-upgrade-ext.properties}{{447.4}{1599}{Configuring portal-upgrade-ext.properties}{section.447.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {447.5}Example Upgrade Configuration}{1599}{section.447.5}\\protected@file@percent }\n\\newlabel{example-upgrade-configuration}{{447.5}{1599}{Example Upgrade Configuration}{section.447.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {447.6}Configuring Non-Core Module Data Upgrades}{1600}{section.447.6}\\protected@file@percent }\n\\newlabel{configuring-non-core-module-data-upgrades}{{447.6}{1600}{Configuring Non-Core Module Data Upgrades}{section.447.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {448}Upgrading the Core Using the Upgrade Tool}{1601}{chapter.448}\\protected@file@percent }\n\\newlabel{upgrading-the-core-using-the-upgrade-tool}{{448}{1601}{Upgrading the Core Using the Upgrade Tool}{chapter.448}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {448.1}Upgrade Tool Usage}{1601}{section.448.1}\\protected@file@percent }\n\\newlabel{upgrade-tool-usage}{{448.1}{1601}{Upgrade Tool Usage}{section.448.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {448.2}Running and Managing the Core Upgrade}{1602}{section.448.2}\\protected@file@percent }\n\\newlabel{running-and-managing-the-core-upgrade}{{448.2}{1602}{Running and Managing the Core Upgrade}{section.448.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {449}Upgrading Modules Using Gogo Shell}{1603}{chapter.449}\\protected@file@percent }\n\\newlabel{upgrading-modules-using-gogo-shell}{{449}{1603}{Upgrading Modules Using Gogo Shell}{chapter.449}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {449.1}Command Usage}{1603}{section.449.1}\\protected@file@percent }\n\\newlabel{command-usage}{{449.1}{1603}{Command Usage}{section.449.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {449.2}Listing module upgrade processes}{1604}{section.449.2}\\protected@file@percent }\n\\newlabel{listing-module-upgrade-processes}{{449.2}{1604}{Listing module upgrade processes}{section.449.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {449.3}Executing module upgrades}{1605}{section.449.3}\\protected@file@percent }\n\\newlabel{executing-module-upgrades}{{449.3}{1605}{Executing module upgrades}{section.449.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {449.4}Checking upgrade status}{1605}{section.449.4}\\protected@file@percent }\n\\newlabel{checking-upgrade-status}{{449.4}{1605}{Checking upgrade status}{section.449.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {449.5}Executing verify processes}{1606}{section.449.5}\\protected@file@percent }\n\\newlabel{executing-verify-processes}{{449.5}{1606}{Executing verify processes}{section.449.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {450}Executing Post-Upgrade Tasks}{1607}{chapter.450}\\protected@file@percent }\n\\newlabel{executing-post-upgrade-tasks}{{450}{1607}{Executing Post-Upgrade Tasks}{chapter.450}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {450.1}Tuning Your Database for Production}{1607}{section.450.1}\\protected@file@percent }\n\\newlabel{tuning-your-database-for-production}{{450.1}{1607}{Tuning Your Database for Production}{section.450.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {450.2}Re-enabling Search Indexing and Re-indexing Search Indexes}{1607}{section.450.2}\\protected@file@percent }\n\\newlabel{re-enabling-search-indexing-and-re-indexing-search-indexes}{{450.2}{1607}{Re-enabling Search Indexing and Re-indexing Search Indexes}{section.450.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {450.3}Enabling Web Content View Permissions}{1608}{section.450.3}\\protected@file@percent }\n\\newlabel{enabling-web-content-view-permissions}{{450.3}{1608}{Enabling Web Content View Permissions}{section.450.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {451}Upgrading a Sharded Environment}{1609}{chapter.451}\\protected@file@percent }\n\\newlabel{upgrading-a-sharded-environment}{{451}{1609}{Upgrading a Sharded Environment}{chapter.451}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {451.1}Add Configurations Before the Data Upgrade}{1609}{section.451.1}\\protected@file@percent }\n\\newlabel{add-configurations-before-the-data-upgrade}{{451.1}{1609}{Add Configurations Before the Data Upgrade}{section.451.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {451.2}Upgrade and Update Properties}{1610}{section.451.2}\\protected@file@percent }\n\\newlabel{upgrade-and-update-properties}{{451.2}{1610}{Upgrade and Update Properties}{section.451.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {452}Migrating From Audience Targeting to Segmentation and Personalization}{1613}{chapter.452}\\protected@file@percent }\n\\newlabel{migrating-from-audience-targeting-to-segmentation-and-personalization}{{452}{1613}{Migrating From Audience Targeting to Segmentation and Personalization}{chapter.452}{}}\n\\gdef \\LT@v {\\LT@entry \n    {1}{154.56912pt}\\LT@entry \n    {1}{160.56912pt}\\LT@entry \n    {1}{154.56912pt}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {453}Migrating User Segments}{1615}{chapter.453}\\protected@file@percent }\n\\newlabel{migrating-user-segments}{{453}{1615}{Migrating User Segments}{chapter.453}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {453.1}Upgrade Process}{1615}{section.453.1}\\protected@file@percent }\n\\newlabel{upgrade-process}{{453.1}{1615}{Upgrade Process}{section.453.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {453.1}{\\ignorespaces A Liferay DXP 7.1 Audience Targeting Segment.}}{1616}{figure.453.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {453.2}{\\ignorespaces A Liferay DXP 7.2 Segment}}{1617}{figure.453.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {454}Manually Migrating from Audience Targeting}{1619}{chapter.454}\\protected@file@percent }\n\\newlabel{manually-migrating-from-audience-targeting}{{454}{1619}{Manually Migrating from Audience Targeting}{chapter.454}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {454.1}User Attribute Rules}{1619}{section.454.1}\\protected@file@percent }\n\\newlabel{user-attribute-rules}{{454.1}{1619}{User Attribute Rules}{section.454.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {454.2}Session Rules}{1619}{section.454.2}\\protected@file@percent }\n\\newlabel{session-rules}{{454.2}{1619}{Session Rules}{section.454.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {454.3}Behavior Rules}{1619}{section.454.3}\\protected@file@percent }\n\\newlabel{behavior-rules}{{454.3}{1619}{Behavior Rules}{section.454.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {454.4}Migrating Custom Rules}{1620}{section.454.4}\\protected@file@percent }\n\\newlabel{migrating-custom-rules}{{454.4}{1620}{Migrating Custom Rules}{section.454.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {454.5}Migrating Display Portlets}{1620}{section.454.5}\\protected@file@percent }\n\\newlabel{migrating-display-portlets}{{454.5}{1620}{Migrating Display Portlets}{section.454.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {454.6}User Segment Content Display}{1620}{section.454.6}\\protected@file@percent }\n\\newlabel{user-segment-content-display}{{454.6}{1620}{User Segment Content Display}{section.454.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {454.7}Asset Publisher Personalization}{1620}{section.454.7}\\protected@file@percent }\n\\newlabel{asset-publisher-personalization}{{454.7}{1620}{Asset Publisher Personalization}{section.454.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {455}Deprecated Apps in 7.2: What to Do}{1621}{chapter.455}\\protected@file@percent }\n\\newlabel{deprecated-apps-in-7.2-what-to-do}{{455}{1621}{Deprecated Apps in 7.2: What to Do}{chapter.455}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {455.1}Foundation}{1621}{section.455.1}\\protected@file@percent }\n\\newlabel{foundation}{{455.1}{1621}{Foundation}{section.455.1}{}}\n\\gdef \\LT@vi {\\LT@entry \n    {1}{66.80023pt}\\LT@entry \n    {1}{275.39513pt}\\LT@entry \n    {1}{127.55965pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {455.2}Personalization}{1622}{section.455.2}\\protected@file@percent }\n\\newlabel{personalization}{{455.2}{1622}{Personalization}{section.455.2}{}}\n\\gdef \\LT@vii {\\LT@entry \n    {1}{66.80023pt}\\LT@entry \n    {1}{275.39513pt}\\LT@entry \n    {1}{127.55965pt}}\n\\gdef \\LT@viii {\\LT@entry \n    {1}{66.80023pt}\\LT@entry \n    {1}{275.39513pt}\\LT@entry \n    {1}{127.55965pt}}\n\\gdef \\LT@ix {\\LT@entry \n    {3}{56.1729pt}\\LT@entry \n    {1}{66.80472pt}\\LT@entry \n    {3}{149.97061pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {455.3}Web Experience}{1623}{section.455.3}\\protected@file@percent }\n\\newlabel{web-experience}{{455.3}{1623}{Web Experience}{section.455.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {455.4}Forms}{1623}{section.455.4}\\protected@file@percent }\n\\newlabel{forms}{{455.4}{1623}{Forms}{section.455.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {455.5}Security}{1623}{section.455.5}\\protected@file@percent }\n\\newlabel{security-1}{{455.5}{1623}{Security}{section.455.5}{}}\n\\gdef \\LT@x {\\LT@entry \n    {1}{47.80994pt}\\LT@entry \n    {1}{262.7372pt}\\LT@entry \n    {1}{159.24869pt}}\n\\gdef \\LT@xi {\\LT@entry \n    {1}{71.41252pt}\\LT@entry \n    {1}{398.34248pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {455.6}User and System Management}{1624}{section.455.6}\\protected@file@percent }\n\\newlabel{user-and-system-management}{{455.6}{1624}{User and System Management}{section.455.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {455.7}Related Topics}{1624}{section.455.7}\\protected@file@percent }\n\\newlabel{related-topics-7}{{455.7}{1624}{Related Topics}{section.455.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {456}Apps in Maintenance Mode}{1625}{chapter.456}\\protected@file@percent }\n\\newlabel{apps-in-maintenance-mode}{{456}{1625}{Apps in Maintenance Mode}{chapter.456}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {456.1}Related Topics}{1625}{section.456.1}\\protected@file@percent }\n\\newlabel{related-topics-8}{{456.1}{1625}{Related Topics}{section.456.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {457}Maintaining Liferay DXP}{1627}{chapter.457}\\protected@file@percent }\n\\newlabel{maintaining-liferay-dxp}{{457}{1627}{Maintaining Liferay DXP}{chapter.457}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {458}Patching Liferay DXP}{1629}{chapter.458}\\protected@file@percent }\n\\newlabel{patching-liferay-dxp}{{458}{1629}{Patching Liferay DXP}{chapter.458}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {459}Patching Basics}{1631}{chapter.459}\\protected@file@percent }\n\\newlabel{patching-basics}{{459}{1631}{Patching Basics}{chapter.459}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {459.1}Fix Packs}{1631}{section.459.1}\\protected@file@percent }\n\\newlabel{fix-packs}{{459.1}{1631}{Fix Packs}{section.459.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {459.2}Hotfixes}{1631}{section.459.2}\\protected@file@percent }\n\\newlabel{hotfixes}{{459.2}{1631}{Hotfixes}{section.459.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {459.3}Service Packs}{1632}{section.459.3}\\protected@file@percent }\n\\newlabel{service-packs}{{459.3}{1632}{Service Packs}{section.459.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {459.4}How Patches are Tested}{1632}{section.459.4}\\protected@file@percent }\n\\newlabel{how-patches-are-tested}{{459.4}{1632}{How Patches are Tested}{section.459.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {460}Using the Patching Tool}{1633}{chapter.460}\\protected@file@percent }\n\\newlabel{using-the-patching-tool}{{460}{1633}{Using the Patching Tool}{chapter.460}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {460.1}Installing the Patching Tool}{1633}{section.460.1}\\protected@file@percent }\n\\newlabel{installing-the-patching-tool}{{460.1}{1633}{Installing the Patching Tool}{section.460.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {460.2}Executables}{1634}{section.460.2}\\protected@file@percent }\n\\newlabel{executables}{{460.2}{1634}{Executables}{section.460.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {461}Installing Patches}{1635}{chapter.461}\\protected@file@percent }\n\\newlabel{installing-patches}{{461}{1635}{Installing Patches}{chapter.461}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {461.1}Handling Hotfixes and Patches}{1636}{section.461.1}\\protected@file@percent }\n\\newlabel{handling-hotfixes-and-patches}{{461.1}{1636}{Handling Hotfixes and Patches}{section.461.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {461.2}Fix Pack Dependencies}{1637}{section.461.2}\\protected@file@percent }\n\\newlabel{fix-pack-dependencies}{{461.2}{1637}{Fix Pack Dependencies}{section.461.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {461.3}Updating the Patching Tool}{1637}{section.461.3}\\protected@file@percent }\n\\newlabel{updating-the-patching-tool}{{461.3}{1637}{Updating the Patching Tool}{section.461.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {461.4}Cleaning Up}{1637}{section.461.4}\\protected@file@percent }\n\\newlabel{cleaning-up}{{461.4}{1637}{Cleaning Up}{section.461.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {462}Working with Patches}{1639}{chapter.462}\\protected@file@percent }\n\\newlabel{working-with-patches}{{462}{1639}{Working with Patches}{chapter.462}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {462.1}Including support-info in Support Tickets}{1639}{section.462.1}\\protected@file@percent }\n\\newlabel{including-support-info-in-support-tickets}{{462.1}{1639}{Including support-info in Support Tickets}{section.462.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {462.2}Uninstalling Patches}{1639}{section.462.2}\\protected@file@percent }\n\\newlabel{uninstalling-patches}{{462.2}{1639}{Uninstalling Patches}{section.462.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {462.3}Showing collisions between patches and deployed plugins}{1640}{section.462.3}\\protected@file@percent }\n\\newlabel{showing-collisions-between-patches-and-deployed-plugins}{{462.3}{1640}{Showing collisions between patches and deployed plugins}{section.462.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {462.4}Separating Patches from the Installation}{1640}{section.462.4}\\protected@file@percent }\n\\newlabel{separating-patches-from-the-installation}{{462.4}{1640}{Separating Patches from the Installation}{section.462.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {462.5}Restoring the Separated Patch Files}{1641}{section.462.5}\\protected@file@percent }\n\\newlabel{restoring-the-separated-patch-files}{{462.5}{1641}{Restoring the Separated Patch Files}{section.462.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {463}Configuring the Patching Tool}{1643}{chapter.463}\\protected@file@percent }\n\\newlabel{configuring-the-patching-tool}{{463}{1643}{Configuring the Patching Tool}{chapter.463}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {464}Patching Tool Basic configuration}{1645}{chapter.464}\\protected@file@percent }\n\\newlabel{patching-tool-basic-configuration}{{464}{1645}{Patching Tool Basic configuration}{chapter.464}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {465}Patching Tool Advanced Configuration}{1647}{chapter.465}\\protected@file@percent }\n\\newlabel{patching-tool-advanced-configuration}{{465}{1647}{Patching Tool Advanced Configuration}{chapter.465}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {465.1}Using Profiles with the Patching Tool}{1647}{section.465.1}\\protected@file@percent }\n\\newlabel{using-profiles-with-the-patching-tool}{{465.1}{1647}{Using Profiles with the Patching Tool}{section.465.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {466}Installing patches on the 7.0 WAR}{1649}{chapter.466}\\protected@file@percent }\n\\newlabel{installing-patches-on-the-7.0-war}{{466}{1649}{Installing patches on the 7.0 WAR}{chapter.466}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {466.1}Prerequisites}{1649}{section.466.1}\\protected@file@percent }\n\\newlabel{prerequisites-1}{{466.1}{1649}{Prerequisites}{section.466.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {466.2}Install the patch on the Liferay DXP WAR and artifacts}{1649}{section.466.2}\\protected@file@percent }\n\\newlabel{install-the-patch-on-the-liferay-dxp-war-and-artifacts}{{466.2}{1649}{Install the patch on the Liferay DXP WAR and artifacts}{section.466.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {466.3}Related Topics}{1650}{section.466.3}\\protected@file@percent }\n\\newlabel{related-topics-9}{{466.3}{1650}{Related Topics}{section.466.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {467}Keeping up with Fix packs and Service Packs}{1651}{chapter.467}\\protected@file@percent }\n\\newlabel{keeping-up-with-fix-packs-and-service-packs}{{467}{1651}{Keeping up with Fix packs and Service Packs}{chapter.467}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {468}Backing up a Liferay DXP Installation}{1653}{chapter.468}\\protected@file@percent }\n\\newlabel{backing-up-a-liferay-dxp-installation}{{468}{1653}{Backing up a Liferay DXP Installation}{chapter.468}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {468.1}Backing up Source Code}{1653}{section.468.1}\\protected@file@percent }\n\\newlabel{backing-up-source-code}{{468.1}{1653}{Backing up Source Code}{section.468.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {468.2}Backing up Liferay DXP's File System}{1654}{section.468.2}\\protected@file@percent }\n\\newlabel{backing-up-liferay-dxps-file-system}{{468.2}{1654}{Backing up Liferay DXP's File System}{section.468.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {468.3}Backing up Liferay DXP's Database}{1654}{section.468.3}\\protected@file@percent }\n\\newlabel{backing-up-liferay-dxps-database}{{468.3}{1654}{Backing up Liferay DXP's Database}{section.468.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {469}Monitoring Liferay DXP}{1655}{chapter.469}\\protected@file@percent }\n\\newlabel{monitoring-liferay-dxp}{{469}{1655}{Monitoring Liferay DXP}{chapter.469}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {470}Monitoring Garbage Collection and the JVM}{1657}{chapter.470}\\protected@file@percent }\n\\newlabel{monitoring-garbage-collection-and-the-jvm}{{470}{1657}{Monitoring Garbage Collection and the JVM}{chapter.470}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {470.1}VisualVM}{1657}{section.470.1}\\protected@file@percent }\n\\newlabel{visualvm}{{470.1}{1657}{VisualVM}{section.470.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {470.2}JMX Console}{1657}{section.470.2}\\protected@file@percent }\n\\newlabel{jmx-console}{{470.2}{1657}{JMX Console}{section.470.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {470.1}{\\ignorespaces VisualVM's Visual GC plugin shows the garbage collector in real-time.}}{1658}{figure.470.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {470.3}Garbage Collector Verbose Logging}{1658}{section.470.3}\\protected@file@percent }\n\\newlabel{garbage-collector-verbose-logging}{{470.3}{1658}{Garbage Collector Verbose Logging}{section.470.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {470.2}{\\ignorespaces VisualVM monitors the JVM using Java Management Extensions.}}{1659}{figure.470.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {471}Managing Liferay DXP with Liferay Connected Services}{1661}{chapter.471}\\protected@file@percent }\n\\newlabel{managing-liferay-dxp-with-liferay-connected-services}{{471}{1661}{Managing Liferay DXP with Liferay Connected Services}{chapter.471}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {472}Getting Started with LCS}{1663}{chapter.472}\\protected@file@percent }\n\\newlabel{getting-started-with-lcs}{{472}{1663}{Getting Started with LCS}{chapter.472}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {472.1}{\\ignorespaces Click \\emph  {Get Started} to begin the wizard.}}{1664}{figure.472.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {472.2}{\\ignorespaces Select the LCS project you want to create the environment in, and click \\emph  {Next}.}}{1665}{figure.472.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {472.3}{\\ignorespaces Name and describe the environment, then click \\emph  {Next}.}}{1666}{figure.472.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {472.4}{\\ignorespaces Select the environment's subscription type, then click \\emph  {Next}.}}{1667}{figure.472.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {472.5}{\\ignorespaces Select whether this is a clustered environment, then click \\emph  {Next}.}}{1668}{figure.472.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {472.6}{\\ignorespaces Select whether this is an elastic environment, then click \\emph  {Next}.}}{1669}{figure.472.6}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {472.7}{\\ignorespaces Enable or disable the LCS services you want to use for servers that connect to the environment, then click \\emph  {Next}.}}{1669}{figure.472.7}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {472.8}{\\ignorespaces This form contains each of your selections from the previous steps. Make any changes you want, then click \\emph  {Create Environment}.}}{1670}{figure.472.8}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {473}LCS Preconfiguration}{1671}{chapter.473}\\protected@file@percent }\n\\newlabel{lcs-preconfiguration}{{473}{1671}{LCS Preconfiguration}{chapter.473}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {473.1}Downloading the LCS Client App}{1671}{section.473.1}\\protected@file@percent }\n\\newlabel{downloading-the-lcs-client-app}{{473.1}{1671}{Downloading the LCS Client App}{section.473.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {473.1}{\\ignorespaces Click the app's \\emph  {Free} button to begin the purchase process.}}{1672}{figure.473.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {473.2}{\\ignorespaces Liferay Marketplace displays your receipt for the LCS client app.}}{1673}{figure.473.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {473.2}Preconfiguring LCS to Connect Through a Proxy}{1673}{section.473.2}\\protected@file@percent }\n\\newlabel{preconfiguring-lcs-to-connect-through-a-proxy}{{473.2}{1673}{Preconfiguring LCS to Connect Through a Proxy}{section.473.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {473.3}JVM App Server Arguments}{1674}{section.473.3}\\protected@file@percent }\n\\newlabel{jvm-app-server-arguments}{{473.3}{1674}{JVM App Server Arguments}{section.473.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {473.4}LCS Client App Properties}{1674}{section.473.4}\\protected@file@percent }\n\\newlabel{lcs-client-app-properties}{{473.4}{1674}{LCS Client App Properties}{section.473.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {473.5}Ensuring Access to LCS}{1675}{section.473.5}\\protected@file@percent }\n\\newlabel{ensuring-access-to-lcs}{{473.5}{1675}{Ensuring Access to LCS}{section.473.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {473.6}NTP Server Synchronization}{1675}{section.473.6}\\protected@file@percent }\n\\newlabel{ntp-server-synchronization}{{473.6}{1675}{NTP Server Synchronization}{section.473.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {473.7}Configuring WebSphere}{1675}{section.473.7}\\protected@file@percent }\n\\newlabel{configuring-websphere}{{473.7}{1675}{Configuring WebSphere}{section.473.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {473.8}Installing the LCS Client App}{1676}{section.473.8}\\protected@file@percent }\n\\newlabel{installing-the-lcs-client-app}{{473.8}{1676}{Installing the LCS Client App}{section.473.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {473.9}Upgrading the LCS Client App}{1677}{section.473.9}\\protected@file@percent }\n\\newlabel{upgrading-the-lcs-client-app}{{473.9}{1677}{Upgrading the LCS Client App}{section.473.9}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {474}Registering Your Liferay DXP Server with LCS}{1679}{chapter.474}\\protected@file@percent }\n\\newlabel{registering-your-liferay-dxp-server-with-lcs}{{474}{1679}{Registering Your Liferay DXP Server with LCS}{chapter.474}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {474.1}{\\ignorespaces Select your LCS project from the menu highlighted by the red box in this screenshot.}}{1679}{figure.474.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {474.2}{\\ignorespaces You must register your server in an LCS environment. The red box in this screenshot highlights environments.}}{1680}{figure.474.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {474.3}{\\ignorespaces An environment's Registration tab lets you manage the token file used to register your server in the environment.}}{1681}{figure.474.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {474.1}Determining Your Server's LCS Connection Status}{1682}{section.474.1}\\protected@file@percent }\n\\newlabel{determining-your-servers-lcs-connection-status}{{474.1}{1682}{Determining Your Server's LCS Connection Status}{section.474.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {474.4}{\\ignorespaces The server is connected to LCS.}}{1683}{figure.474.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {475}Using LCS}{1685}{chapter.475}\\protected@file@percent }\n\\newlabel{using-lcs}{{475}{1685}{Using LCS}{chapter.475}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {476}What LCS Stores About Your Liferay DXP Servers}{1687}{chapter.476}\\protected@file@percent }\n\\newlabel{what-lcs-stores-about-your-liferay-dxp-servers}{{476}{1687}{What LCS Stores About Your Liferay DXP Servers}{chapter.476}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {477}Managing LCS Users in Your Project}{1689}{chapter.477}\\protected@file@percent }\n\\newlabel{managing-lcs-users-in-your-project}{{477}{1689}{Managing LCS Users in Your Project}{chapter.477}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {477.1}{\\ignorespaces The Users tab lets you manage the LCS users in your project.}}{1690}{figure.477.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {477.1}Managing LCS Roles}{1690}{section.477.1}\\protected@file@percent }\n\\newlabel{managing-lcs-roles}{{477.1}{1690}{Managing LCS Roles}{section.477.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {477.2}{\\ignorespaces You can assign or revoke a user's LCS Roles.}}{1691}{figure.477.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {478}Using the Dashboard}{1693}{chapter.478}\\protected@file@percent }\n\\newlabel{using-the-dashboard}{{478}{1693}{Using the Dashboard}{chapter.478}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {478.1}Using the Project View}{1693}{section.478.1}\\protected@file@percent }\n\\newlabel{using-the-project-view}{{478.1}{1693}{Using the Project View}{section.478.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {478.1}{\\ignorespaces The LCS project view shows an overview of your LCS project.}}{1694}{figure.478.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {479}Managing LCS Environments}{1695}{chapter.479}\\protected@file@percent }\n\\newlabel{managing-lcs-environments}{{479}{1695}{Managing LCS Environments}{chapter.479}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {479.1}Creating Environments}{1695}{section.479.1}\\protected@file@percent }\n\\newlabel{creating-environments}{{479.1}{1695}{Creating Environments}{section.479.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {479.1}{\\ignorespaces The New Environment form lets you create environments.}}{1696}{figure.479.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {479.2}Working with Environments}{1697}{section.479.2}\\protected@file@percent }\n\\newlabel{working-with-environments}{{479.2}{1697}{Working with Environments}{section.479.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {479.2}{\\ignorespaces The LCS environment view shows an overview of an LCS environment.}}{1697}{figure.479.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {480}Managing LCS Servers}{1699}{chapter.480}\\protected@file@percent }\n\\newlabel{managing-lcs-servers}{{480}{1699}{Managing LCS Servers}{chapter.480}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {480.1}Details}{1700}{section.480.1}\\protected@file@percent }\n\\newlabel{details}{{480.1}{1700}{Details}{section.480.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {480.1}{\\ignorespaces The Details tab shows information about your server.}}{1700}{figure.480.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {480.2}Server Settings}{1700}{section.480.2}\\protected@file@percent }\n\\newlabel{server-settings}{{480.2}{1700}{Server Settings}{section.480.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {480.2}{\\ignorespaces You can use the Server Settings tab to give your server a fun name.}}{1701}{figure.480.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {480.3}Page Analytics}{1701}{section.480.3}\\protected@file@percent }\n\\newlabel{page-analytics}{{480.3}{1701}{Page Analytics}{section.480.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {480.4}Snapshot Metrics}{1701}{section.480.4}\\protected@file@percent }\n\\newlabel{snapshot-metrics}{{480.4}{1701}{Snapshot Metrics}{section.480.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {480.3}{\\ignorespaces The Page Analytics interface in the LCS Server view.}}{1702}{figure.480.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {480.4}{\\ignorespaces The LCS application metrics show portlet performance statistics, like frequency of use and average load time.}}{1703}{figure.480.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {480.5}Fix Packs}{1703}{section.480.5}\\protected@file@percent }\n\\newlabel{fix-packs-1}{{480.5}{1703}{Fix Packs}{section.480.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {480.6}Portal Properties}{1703}{section.480.6}\\protected@file@percent }\n\\newlabel{portal-properties}{{480.6}{1703}{Portal Properties}{section.480.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {480.5}{\\ignorespaces The LCS JVM metrics show performance data for memory and the garbage collector.}}{1704}{figure.480.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {480.6}{\\ignorespaces The LCS server metrics show current threads and JDBC connection pools.}}{1705}{figure.480.6}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {480.7}{\\ignorespaces The Fix Packs tab displays your server's available and installed fix packs.}}{1705}{figure.480.7}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {480.8}{\\ignorespaces Click the gear icon to select the type of portal properties to show in the table.}}{1706}{figure.480.8}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {481}Managing Your LCS Account}{1707}{chapter.481}\\protected@file@percent }\n\\newlabel{managing-your-lcs-account}{{481}{1707}{Managing Your LCS Account}{chapter.481}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {481.1}{\\ignorespaces You can add rules to determine the events that trigger notifications.}}{1708}{figure.481.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {481.2}{\\ignorespaces You can change your LCS account's general preferences.}}{1709}{figure.481.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {481.1}Using Web Notifications}{1709}{section.481.1}\\protected@file@percent }\n\\newlabel{using-web-notifications}{{481.1}{1709}{Using Web Notifications}{section.481.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {481.3}{\\ignorespaces Web notifications let you know what's happening in your LCS projects.}}{1710}{figure.481.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {482}Managing Liferay DXP Subscriptions}{1711}{chapter.482}\\protected@file@percent }\n\\newlabel{managing-liferay-dxp-subscriptions}{{482}{1711}{Managing Liferay DXP Subscriptions}{chapter.482}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {482.1}{\\ignorespaces LCS lets you view and manage your subscriptions.}}{1712}{figure.482.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {482.1}Decommissioning Servers}{1713}{section.482.1}\\protected@file@percent }\n\\newlabel{decommissioning-servers}{{482.1}{1713}{Decommissioning Servers}{section.482.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {482.2}Elastic Subscriptions}{1713}{section.482.2}\\protected@file@percent }\n\\newlabel{elastic-subscriptions}{{482.2}{1713}{Elastic Subscriptions}{section.482.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {482.2}{\\ignorespaces The \\emph  {Elastic Subscriptions} tab shows details about your project's elastic servers.}}{1714}{figure.482.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {483}Understanding Environment Tokens}{1715}{chapter.483}\\protected@file@percent }\n\\newlabel{understanding-environment-tokens}{{483}{1715}{Understanding Environment Tokens}{chapter.483}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {484}Troubleshooting Your LCS Connection}{1717}{chapter.484}\\protected@file@percent }\n\\newlabel{troubleshooting-your-lcs-connection}{{484}{1717}{Troubleshooting Your LCS Connection}{chapter.484}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {484.1}LCS Grace Periods}{1718}{section.484.1}\\protected@file@percent }\n\\newlabel{lcs-grace-periods}{{484.1}{1718}{LCS Grace Periods}{section.484.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {484.2}Connection Grace Period}{1718}{section.484.2}\\protected@file@percent }\n\\newlabel{connection-grace-period}{{484.2}{1718}{Connection Grace Period}{section.484.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {484.1}{\\ignorespaces A warning message is displayed to administrators if the server can't connect to LCS to validate the subscription.}}{1718}{figure.484.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {484.3}Subscription Grace Period}{1719}{section.484.3}\\protected@file@percent }\n\\newlabel{subscription-grace-period}{{484.3}{1719}{Subscription Grace Period}{section.484.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {484.2}{\\ignorespaces LCS sends you a notification prior to the expiration of your subscription.}}{1719}{figure.484.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {484.4}Troubleshooting}{1720}{section.484.4}\\protected@file@percent }\n\\newlabel{troubleshooting}{{484.4}{1720}{Troubleshooting}{section.484.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {484.5}Server Can't Reach LCS}{1720}{section.484.5}\\protected@file@percent }\n\\newlabel{server-cant-reach-lcs}{{484.5}{1720}{Server Can't Reach LCS}{section.484.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {484.6}Subscription Issues}{1720}{section.484.6}\\protected@file@percent }\n\\newlabel{subscription-issues}{{484.6}{1720}{Subscription Issues}{section.484.6}{}}\n\\gdef \\LT@xii {\\LT@entry \n    {1}{207.41331pt}\\LT@entry \n    {1}{262.34169pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {484.7}Invalid Token}{1721}{section.484.7}\\protected@file@percent }\n\\newlabel{invalid-token}{{484.7}{1721}{Invalid Token}{section.484.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {484.8}Increasing Log Levels}{1721}{section.484.8}\\protected@file@percent }\n\\newlabel{increasing-log-levels}{{484.8}{1721}{Increasing Log Levels}{section.484.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {484.9}Control Panel}{1722}{section.484.9}\\protected@file@percent }\n\\newlabel{control-panel}{{484.9}{1722}{Control Panel}{section.484.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {484.10}Log4j}{1722}{section.484.10}\\protected@file@percent }\n\\newlabel{log4j}{{484.10}{1722}{Log4j}{section.484.10}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {485}Deployment Reference}{1725}{chapter.485}\\protected@file@percent }\n\\newlabel{deployment-reference}{{485}{1725}{Deployment Reference}{chapter.485}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {486}Liferay Home}{1727}{chapter.486}\\protected@file@percent }\n\\newlabel{liferay-home}{{486}{1727}{Liferay Home}{chapter.486}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {487}Portal Properties}{1729}{chapter.487}\\protected@file@percent }\n\\newlabel{portal-properties-1}{{487}{1729}{Portal Properties}{chapter.487}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {488}System Properties}{1731}{chapter.488}\\protected@file@percent }\n\\newlabel{system-properties}{{488}{1731}{System Properties}{chapter.488}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {489}Database Templates}{1733}{chapter.489}\\protected@file@percent }\n\\newlabel{database-templates}{{489}{1733}{Database Templates}{chapter.489}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {489.1}MariaDB}{1733}{section.489.1}\\protected@file@percent }\n\\newlabel{mariadb-2}{{489.1}{1733}{MariaDB}{section.489.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {489.2}MySQL}{1733}{section.489.2}\\protected@file@percent }\n\\newlabel{mysql-2}{{489.2}{1733}{MySQL}{section.489.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {489.3}PostgreSQL}{1734}{section.489.3}\\protected@file@percent }\n\\newlabel{postgresql-2}{{489.3}{1734}{PostgreSQL}{section.489.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {490}Comparing Patch Levels}{1735}{chapter.490}\\protected@file@percent }\n\\newlabel{comparing-patch-levels}{{490}{1735}{Comparing Patch Levels}{chapter.490}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {491}Patching Tool Configuration Properties}{1737}{chapter.491}\\protected@file@percent }\n\\newlabel{patching-tool-configuration-properties}{{491}{1737}{Patching Tool Configuration Properties}{chapter.491}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {492}Troubleshooting Deployments}{1739}{chapter.492}\\protected@file@percent }\n\\newlabel{troubleshooting-deployments}{{492}{1739}{Troubleshooting Deployments}{chapter.492}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {493}Liferay DXP Failed to Initialize Because the Database Wasn't Ready}{1741}{chapter.493}\\protected@file@percent }\n\\newlabel{liferay-dxp-failed-to-initialize-because-the-database-wasnt-ready}{{493}{1741}{Liferay DXP Failed to Initialize Because the Database Wasn't Ready}{chapter.493}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {493.1}Related Topics}{1741}{section.493.1}\\protected@file@percent }\n\\newlabel{related-topics-10}{{493.1}{1741}{Related Topics}{section.493.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {494}Sort Order Changed with a Different Database}{1743}{chapter.494}\\protected@file@percent }\n\\newlabel{sort-order-changed-with-a-different-database}{{494}{1743}{Sort Order Changed with a Different Database}{chapter.494}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {495}Using Files to Configure Module Components}{1745}{chapter.495}\\protected@file@percent }\n\\newlabel{using-files-to-configure-module-components}{{495}{1745}{Using Files to Configure Module Components}{chapter.495}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {495.1}Configuration File Formats}{1745}{section.495.1}\\protected@file@percent }\n\\newlabel{configuration-file-formats}{{495.1}{1745}{Configuration File Formats}{section.495.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {495.2}Naming Configuration Files}{1746}{section.495.2}\\protected@file@percent }\n\\newlabel{naming-configuration-files}{{495.2}{1746}{Naming Configuration Files}{section.495.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {495.1}{\\ignorespaces You can create multiple instances of components whose System Settings page has a \\emph  {Configuration Entries} section.}}{1746}{figure.495.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {495.3}Resolving Configuration File Deployment Failures}{1747}{section.495.3}\\protected@file@percent }\n\\newlabel{resolving-configuration-file-deployment-failures}{{495.3}{1747}{Resolving Configuration File Deployment Failures}{section.495.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {495.4}Related Articles}{1747}{section.495.4}\\protected@file@percent }\n\\newlabel{related-articles}{{495.4}{1747}{Related Articles}{section.495.4}{}}\n\\@setckpt{user/deployment}{\n\\setcounter{page}{1748}\n\\setcounter{equation}{0}\n\\setcounter{enumi}{4}\n\\setcounter{enumii}{3}\n\\setcounter{enumiii}{3}\n\\setcounter{enumiv}{0}\n\\setcounter{footnote}{0}\n\\setcounter{mpfootnote}{0}\n\\setcounter{@memmarkcntra}{0}\n\\setcounter{storedpagenumber}{1}\n\\setcounter{book}{0}\n\\setcounter{part}{2}\n\\setcounter{chapter}{495}\n\\setcounter{section}{4}\n\\setcounter{subsection}{0}\n\\setcounter{subsubsection}{0}\n\\setcounter{paragraph}{0}\n\\setcounter{subparagraph}{0}\n\\setcounter{@ppsavesec}{0}\n\\setcounter{@ppsaveapp}{0}\n\\setcounter{vslineno}{0}\n\\setcounter{poemline}{0}\n\\setcounter{modulo@vs}{0}\n\\setcounter{memfvsline}{0}\n\\setcounter{verse}{0}\n\\setcounter{chrsinstr}{0}\n\\setcounter{poem}{0}\n\\setcounter{newflo@tctr}{4}\n\\setcounter{@contsubnum}{0}\n\\setcounter{section@level}{0}\n\\setcounter{maxsecnumdepth}{1}\n\\setcounter{sidefootnote}{0}\n\\setcounter{pagenote}{0}\n\\setcounter{pagenoteshadow}{0}\n\\setcounter{memfbvline}{0}\n\\setcounter{bvlinectr}{0}\n\\setcounter{cp@cntr}{0}\n\\setcounter{ism@mctr}{0}\n\\setcounter{xsm@mctr}{0}\n\\setcounter{csm@mctr}{0}\n\\setcounter{ksm@mctr}{0}\n\\setcounter{xksm@mctr}{0}\n\\setcounter{cksm@mctr}{0}\n\\setcounter{msm@mctr}{0}\n\\setcounter{xmsm@mctr}{0}\n\\setcounter{cmsm@mctr}{0}\n\\setcounter{bsm@mctr}{0}\n\\setcounter{workm@mctr}{0}\n\\setcounter{sheetsequence}{1796}\n\\setcounter{lastsheet}{1795}\n\\setcounter{lastpage}{1747}\n\\setcounter{figure}{1}\n\\setcounter{lofdepth}{1}\n\\setcounter{table}{0}\n\\setcounter{lotdepth}{1}\n\\setcounter{Item}{2273}\n\\setcounter{Hfootnote}{5}\n\\setcounter{bookmark@seq@number}{0}\n\\setcounter{memhycontfloat}{0}\n\\setcounter{mem@Hpagenote}{0}\n\\setcounter{r@tfl@t}{0}\n\\setcounter{float@type}{4}\n\\setcounter{LT@tables}{12}\n\\setcounter{LT@chunks}{3}\n\\setcounter{@anim@ltxcnt}{0}\n\\setcounter{parentequation}{0}\n\\setcounter{FancyVerbLine}{0}\n}\n"
  },
  {
    "path": "book/user/deployment.tex",
    "content": "\\chapter{Deploying Liferay DXP}\\label{deploying-liferay-dxp}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay DXP is one of the most flexible applications on the market today\nwith respect to database and application server support. It supports a\nwide variety of databases and application servers, freeing you to use\nthe ones you want. Liferay DXP also scales very well. You can install it\non a shared hosting account, on a multi-node cluster running a\ncommercial application server, or on anything in between. In fact,\nLiferay DXP is used successfully in all of these scenarios every day.\n\nYou'll find that because of Liferay DXP's flexibility in its deployment\noptions, it is also easy to install. If you already have an application\nserver, you can use your application server's deployment tools to\ninstall Liferay DXP. If you don't already have an application server,\nLiferay provides several application server bundles from which to\nchoose. These are pre-configured application servers with Liferay DXP\nalready installed on them. With a small amount of configuration, these\ncan be made into production-ready systems.\n\nThere are some preparations to make before installing. You must create a\ndatabase and install a supported Java Development Kit (JDK). It can also\nbe worthwhile to pre-configure or gather information for configuring a\ndata source, mail session, and more. You'll get guidance for these\npreparations.\n\nLastly, you'll install and deploy Liferay DXP for the first time and\nthen set up Marketplace. You can continue configuring and tuning as you\ndesire.\n\nRead on to obtain the Liferay DXP installer that's right for you.\n\n\\chapter{Obtaining Liferay DXP}\\label{obtaining-liferay-dxp}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nBefore you begin, you should answer a few questions.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Which version of Liferay DXP will you install?\n\\item\n  Is your installation for development purposes or are you preparing to\n  run in production?\n\\item\n  Are you installing Liferay DXP in a clustered environment?\n\\item\n  Which application server do you want Liferay DXP to run on?\n\\item\n  Are you installing on an existing application server?\n\\end{itemize}\n\nHere you'll determine the installation that's best for you and download\nit.\n\nAnyone can download Liferay DXP from\n\\href{https://www.liferay.com}{liferay.com}. Clicking \\emph{Resources →\nCommunity Downloads} takes you to the\n\\href{https://www.liferay.com/downloads-community}{Community Downloads\npage}, where you can access Liferay Portal CE or a trial of the\nenterprise supported Liferay DXP. The installers are available in\nseveral different formats. The formats include a Liferay Tomcat bundle\n(Liferay DXP bundled with Tomcat) as well as a Liferay DXP \\texttt{.war}\n(WAR) file for installing @product@ on an existing application server of\nchoice.\n\nLiferay enterprise subscribers can download Liferay DXP from the\n\\href{https://help.liferay.com/hc}{Help Center}.\n\nLiferay DXP runs on a wide variety of application servers (Check the\n\\href{https://help.liferay.com/hc/categories/360000894391-Product-Support}{Support\npage} for a complete listing). Here are the ways to install Liferay DXP:\n\n\\begin{itemize}\n\\item\n  \\hyperref[liferay-tomcat-bundle]{Install a Liferay Tomcat bundle}\n  (Tomcat application server with Liferay DXP pre-installed).\n\\item\n  \\hyperref[downloading-the-liferay-war-and-dependency-jars]{Install the\n  Liferay WAR} (and supporting libraries) onto an existing application\n  server.\n\\end{itemize}\n\nSince the Liferay Tomcat bundle is the easiest way, it's described\nfirst.\n\n\\section{Liferay Tomcat Bundle}\\label{liferay-tomcat-bundle}\n\nThe Liferay Tomcat bundle includes the Tomcat application server with\nLiferay DXP pre-installed. If you prefer using another application\nserver with Liferay DXP, you must install it manually. If you don't\ncurrently have an application server preference, consider starting with\nthe Tomcat bundle. Tomcat is one of the most lightweight and\nstraightforward bundles to configure.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Application server bundles for proprietary application\nservers such as WebLogic or WebSphere aren't available because the\nlicenses for these servers don't allow for redistribution. Liferay DXP's\ncommercial offering, however, runs just as well on these application\nservers as it does on the others.\n\n\\noindent\\hrulefill\n\nBundles are released as 7-Zip (\\texttt{.7z}) and gzipped\n(\\texttt{.tar.gz}) compressed file archive formats.\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-product}{Installing\nLiferay DXP} demonstrates installing Liferay DXP from a bundle. Follow\nits instructions once you've prepared for installing Liferay DXP (see\nthe next article).\n\nLiferay DXP bundles might not be appropriate for you. Here are some\nreasons for installing the Liferay DXP WAR manually instead of using a\nbundle.\n\n\\begin{itemize}\n\\item\n  There is no Liferay DXP bundle for the application server you want to\n  use.\n\\item\n  You're installing Liferay DXP in a clustered environment.\n\\item\n  You're installing to an existing application server.\n\\end{itemize}\n\nManual installation is described next.\n\n\\section{Downloading the Liferay WAR and Dependency\nJARs}\\label{downloading-the-liferay-war-and-dependency-jars}\n\nManual installation requires installing the Liferay DXP WAR and\ndependency JARs onto the application server. These files are available\nto download for \\href{https://customer.liferay.com/downloads}{DXP} or\n\\href{https://www.liferay.com/downloads-community}{Portal CE}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay DXP WAR file\n\\item\n  Dependencies ZIP file\n\\item\n  OSGi Dependencies ZIP file\n\\end{itemize}\n\nAfter preparing for install (next), follow the Liferay DXP installation\nsteps for your application server. There are specific configuration and\nscript changes required on each application. The installation article\ntitles follow this format, with \\emph{{[}Application Server{]}} replaced\nby the application server name.\n\n\\emph{Installing Liferay DXP on {[}Application Server{]}}\n\n\\chapter{Preparing for Install}\\label{preparing-for-install}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay DXP doesn't require much to deploy. You need a Java Development\nKit (JDK) and a database. Several configuration topics (e.g.,\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-a-search-engine}{search\nengine integration},\n\\href{/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration}{document\nrepository configuration},\n\\href{/docs/7-2/deploy/-/knowledge_base/d/securing-product}{security\nmanagement},\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering}{clustering},\nand more) can be addressed \\emph{after} deploying Liferay DXP.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you are installing Liferay DXP to multiple machines\n(e.g., in a\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering}{cluster})\nor prefer centralizing configuration in a file, using portal properties\nin a\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{{[}LIFERAY\\_HOME{]}/portal-ext.properties}\nfile} is the recommended way to configure. The install preparation\ntopics here and the configuration topics throughout this guide\ndemonstrate using applicable portal properties.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} \\texttt{LIFERAY\\_HOME} is the location from which Liferay\nDXP launches applications, applies configurations, loads JAR files, and\ngenerates logs. Liferay Home is customizable and can differ between\napplication servers. The\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home\nreference} describes its folder structure.\n\n\\noindent\\hrulefill\n\nStart preparing for Liferay DXP install by installing a supported Java\nDevelopment Kit.\n\n\\section{JDK Requirements}\\label{jdk-requirements}\n\nLiferay DXP deployment requires a JDK. The\n\\href{https://help.liferay.com/hc/categories/360000894391-Product-Support}{Support\npage} lists the supported JDKs from various vendors. You must use one of\nthese JDK versions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  JDK 8\n\\item\n  JDK 11\n\\end{itemize}\n\nJDK 11 is backwards compatible with JDK 8 applications. Applications and\ncustomizations developed on JDK 8 run on JDK 8 or JDK 11 runtimes. This\nmakes JDK 8 best for developing on 7.0.\n\n\\section{JVM Requirements}\\label{jvm-requirements}\n\nLiferay DXP requires that the application server JVM use the GMT time\nzone and UTF-8 file encoding. Include these JVM arguments to set the\nrequired values.\n\n\\begin{verbatim}\n-Dfile.encoding=UTF8 -Duser.timezone=GMT\n\\end{verbatim}\n\nOn JDK 11, it's recommended to add this JVM argument to display\nfour-digit years.\n\n\\begin{verbatim}\n-Djava.locale.providers=JRE,COMPAT,CLDR\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Since JDK 9, the Unicode Common Locale Data Repository\n(CLDR) is the default locales provider. CLDR, however, is not providing\nyears in a four-digit format (see\n\\href{https://issues.liferay.com/browse/LPS-87191}{LPS-87191}). The\nsetting \\texttt{java.locale.providers=JRE,COMPAT,CLDR} works around this\nissue by using JDK 8's default locales provider.\n\n\\noindent\\hrulefill\n\nThe recommended maximum heap size is 2GB. Setting the minimum heap size\nto the maximum heap size value minimizes garbage collections.\n\n\\begin{verbatim}\n-Xms2560m -Xmx2560m\n\\end{verbatim}\n\nIf you're using JDK 11, you may see \\emph{Illegal Access} warnings like\nthese:\n\n\\begin{verbatim}\nWARNING: An illegal reflective access operation has occurred\nWARNING: Illegal reflective access by com.liferay.petra.reflect.ReflectionUtil (file:/Users/malei/dev/project/bundles/master-bundles/tomcat-9.0.10/lib/ext/com.liferay.petra.reflect.jar) to field java.lang.reflect.Field.modifiers\nWARNING: Please consider reporting this to the maintainers of com.liferay.petra.reflect.ReflectionUtil\nWARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations\nWARNING: All illegal access operations will be denied in a future release\n\\end{verbatim}\n\nThis is a known issue:\n\\href{https://issues.liferay.com/browse/LPS-87421}{LPS-87421}. As a\nworkaround, you can eliminate these warnings by adding these properties\nafter your application server JVM options:\n\n\\begin{verbatim}\n --add-opens=java.base/java.io=ALL-UNNAMED \\\n --add-opens=java.base/java.lang.reflect=ALL-UNNAMED \\\n --add-opens=java.base/java.lang=ALL-UNNAMED \\\n --add-opens=java.base/java.net=ALL-UNNAMED \\\n --add-opens=java.base/java.nio=ALL-UNNAMED \\\n --add-opens=java.base/java.text=ALL-UNNAMED \\\n --add-opens=java.base/java.util=ALL-UNNAMED \\\n --add-opens=java.base/sun.nio.ch=ALL-UNNAMED \\\n --add-opens=java.desktop/java.awt.font=ALL-UNNAMED \\\n --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED \\\n --add-opens=java.xml/com.sun.org.apache.xerces.internal.parsers=ALL-UNNAMED\n\\end{verbatim}\n\nIf you're using JDK 11 on Linux or UNIX and are activating Liferay DXP\nusing an LCS 5.0.0 client, you may see an error like this:\n\n\\begin{verbatim}\nERROR [LCS Worker 2][BaseScheduledTask:92] java.lang.reflect.InaccessibleObjectException: Unable to make public long com.sun.management.internal.OperatingSystemImpl.getOpenFileDescriptorCount() accessible: module jdk.management does not\n \"opens com.sun.management.internal\" to unnamed module @1a3325e5\njava.lang.reflect.InaccessibleObjectException: Unable to make public long com.sun.management.internal.OperatingSystemImpl.getOpenFileDescriptorCount() accessible: module jdk.management does not \"opens com.sun.management.internal\" to unnamed module @1a3325e5\nat java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:\nat java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:\nat java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:198)\nat java.base/java.lang.reflect.Method.setAccessible(Method.java:192)\n\\end{verbatim}\n\nTo workaround this issue, add this property after your application\nserver JVM options:\n\n\\begin{verbatim}\n --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED\n\\end{verbatim}\n\nIt's time to prepare your database.\n\n\\section{Preparing a Database}\\label{preparing-a-database}\n\nThe recommended way to set up your Liferay DXP database is also the\nsimplest. Liferay DXP takes care of just about everything. Here are the\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Create a blank database encoded with the character set UTF-8. Liferay\n  DXP is a multilingual application and needs UTF-8 encoding to display\n  all of its supported character sets.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** If you plan to migrate from one database vendor to another,\n [configure the database to use the default query result order you expect for entities Liferay DXP lists](/docs/7-2/deploy/-/knowledge_base/d/sort-order-changed-with-a-different-database). \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\tightlist\n\\item\n  Create a database user for accessing this database. Grant this\n  database user all rights, including the rights to create and drop\n  tables, to the blank Liferay DXP database.\n\\end{enumerate}\n\nLiferay DXP uses this database user's credentials to connect to the\ndatabase either \\hyperref[using-the-built-in-data-source]{directly} or\n\\hyperref[using-a-data-source-on-your-application-server]{through its\napplication server}. After you've configured the database connection,\nLiferay DXP creates its tables in the database automatically, complete\nwith indexes.\n\nThis is the recommended way to set up Liferay DXP. It enables @product@\nto maintain its database automatically during upgrades or when various\nLiferay DXP plugins that create database tables of their own are\ninstalled. This method is by far the best way to set up your database.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} If you're using an Oracle database, use the\n\\texttt{ojdbc8.jar} driver library with at least Oracle 12.2.0.1.0 JDBC\n4.2 versioning because\n\\href{https://issues.liferay.com/browse/LPS-79229}{data truncation\nissues} have been detected reading data from CLOB columns.\n\n\\noindent\\hrulefill\n\nYou can connect Liferay DXP with your database using @product@'s\nbuilt-in data source (recommended) or using a data source you create on\nyour app server.\n\n\\section{Using the Built-in Data\nSource}\\label{using-the-built-in-data-source}\n\nYou can configure the built-in data source from the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-product\\#using-the-setup-wizard}{Basic\nConfiguration page} (available when Liferay DXP starts up the first\ntime) or by specifying it using portal properties.\n\nHere's how set it using portal properties:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}\n  file} if you haven't created one already.\n\\item\n  Copy a set of \\texttt{jdbc.*} properties from one of the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/database-templates}{JDBC\n  templates} into your \\texttt{portal-ext.properties} file.\n\\item\n  Modify the \\texttt{jdbc.*} property values to specify your database\n  and database user credentials.\n\\item\n  Put the \\texttt{portal-ext.properties} file into your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{LIFERAY\\_HOME}\n  once you've established it based on your installation.\n\\end{enumerate}\n\nLiferay DXP connects to the data source on startup.\n\nAs an alternative to the built-in data source, you can use your\napplication server's data source.\n\n\\section{Using a Data Source on Your Application\nServer}\\label{using-a-data-source-on-your-application-server}\n\nHere's how to use your application server's data source:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create your data source based on the instructions in the\n  \\emph{Installing Liferay DXP on {[}Application Server{]}} article (for\n  your application server) and your application server's documentation.\n\\item\n  Create a\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}\n  file}, if you haven't created one already.\n\\item\n  Add the \\texttt{jdbc.default.jndi.name} property set to the data\n  source's JNDI name. Here's an example:\n\n\\begin{verbatim}\njdbc.default.jndi.name=jdbc/LiferayPool\n\\end{verbatim}\n\\item\n  Put the \\texttt{portal-ext.properties} file into your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{LIFERAY\\_HOME},\n  once you've established your LIFERAY\\_HOME based on your installation.\n\\end{enumerate}\n\nLiferay DXP connects to your data source on startup.\n\nAllowing the database user you're using to initialize the Liferay DXP\ndatabase to continue with all database rights is recommended. If you're\nfine with that user having the recommended permissions, skip the next\nsection on limiting database access.\n\n\\section{Limiting Database Access}\\label{limiting-database-access}\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} The instructions below are not ideal for Liferay DXP\ninstallations The following procedure is documented so that enterprises\nwith more restrictive standards can install Liferay DXP with stricter\n(but sub-optimal) database settings. If it's at all possible, allow the\ndatabase user that initializes the database to continue using the\ndatabase with the same recommended permissions. The start of this\nsection (\\hyperref[preparing-a-database]{Database Preparation})\ndescribes the recommended procedure for initializing the Liferay DXP\ndatabase and preserving that user's permissions for maintaining the\nLiferay DXP database and updating the database as plugin installations\nand plugin updates require.\n\n\\noindent\\hrulefill\n\nEven though it's recommended for Liferay DXP to use the same database\nuser to create and maintain its database automatically, your\norganizations might insist on revoking database initialization and\nmaintenance permissions from that user once the database is initialized.\nIf permissions for Select, Insert, Update and Delete operations are the\nonly ones you allow for that user, you must initialize and maintain the\ndatabase manually (even though it's not recommended). Here is the manual\nprocedure:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new, blank, database for Liferay DXP.\n\\item\n  Grant full rights for the Liferay DXP database user to do anything to\n  the database.\n\\item\n  Install Liferay DXP and start it so that it automatically populates\n  the database.\n\\item\n  Once the database has been populated with the Liferay DXP tables,\n  remove all permissions from that user except permissions to perform\n  Select, Insert, Update and Delete operations.\n\\end{enumerate}\n\nThere are some caveats to running Liferay DXP like this. Many plugins\ncreate new tables when they're deployed. Additionally, you must run the\ndatabase upgrade function to upgrade Liferay DXP. If the @product@\ndatabase user doesn't have adequate rights to create/modify/drop tables\nin the database, you must grant those rights to that user before you\ndeploy one of these plugins or start upgrading Liferay DXP. Once the\ntables are created or the upgrade completes, you can remove those rights\nuntil the next deploy or upgrade. Additionally, your own developers\nmight create plugins that must create their own tables. These are just\nlike Liferay DXP plugins that do the same thing, and they can only be\ninstalled if Liferay DXP can create database tables. Installing these\nplugins requires granting the Liferay DXP database user rights to create\ntables in the database before you attempt to install the plugins.\n\nLiferay DXP has many more configurable features; but they can wait until\n\\emph{after} deployment. The\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-product}{Configuring\nLiferay DXP} section explains them.\n\nNow it's time to install Liferay DXP.\n\n\\chapter{Installing Liferay DXP}\\label{installing-liferay-dxp}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nNow that you've\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install}{prepared\nfor installing Liferay DXP}, you can install and deploy it! This\ninvolves uncompressing the archive (the 7-Zip or gzip bundle file),\npossibly copying a JDBC driver to your application server, and starting\nthe application server. Lastly, Liferay DXP provides a setup wizard to\nconfigure portal essentials.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Since bundles are the easiest way to complete an\ninstallation, the installation steps here assume you're installing a\nLiferay DXP bundle. If you plan to install Liferay DXP manually, please\nrefer to the \\emph{Installing @product@ on {[}Application Server{]}}\narticle for the application server you're installing on.\n\n\\noindent\\hrulefill\n\n\\section{Extracting a Liferay DXP\nBundle}\\label{extracting-a-liferay-dxp-bundle}\n\nExtract your Liferay DXP bundle to the appropriate location on your\nserver. This folder is the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{\\emph{Liferay\nHome}}.\n\n\\section{Installing the JDBC Driver}\\label{installing-the-jdbc-driver}\n\nIf you're using a supported open source database or if you're setting up\nLiferay DXP to use the embedded HSQL database for demo purposes, you can\nskip this step. Otherwise, copy your database's JDBC driver\n\\texttt{.jar} file to the folder your application server documentation\nspecifies. On Tomcat, for example, the driver belongs in the\n\\texttt{{[}Tomcat{]}/lib/ext} folder.\n\n\\section{Running Liferay DXP for the First\nTime}\\label{running-liferay-dxp-for-the-first-time}\n\nStart your application server using the start script bundled with your\napplication server. For example, the Tomcat bundle provides the\n\\texttt{startup.sh} script in \\texttt{\\$CATALINA\\_HOME/bin}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Liferay DXP writes log files to folder\n\\texttt{{[}Liferay\\ Home{]}/logs}.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Liferay DXP requires that the application server JVM\nuse the GMT time zone and UTF-8 file encoding. They're preset in your\nLiferay DXP bundle.\n\n\\noindent\\hrulefill\n\nThe first time Liferay DXP starts, it creates all of its database\ntables. On completing startup, it launches a web browser that displays\nthe Basic Configuration page (the setup wizard). If for some reason your\nbrowser doesn't load the Basic Configuration page automatically, open\nyour browser and navigate to your app server's address and port (for\nexample, http://localhost:8080).\n\n\\section{Using the Setup Wizard}\\label{using-the-setup-wizard}\n\nThe Basic Configuration page provides a convenient way to configure\nthese things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Portal name and default locale\n\\item\n  Administrator user\n\\item\n  Database\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/basic-configuration1.png}}\n\\caption{Supply the information for your portal and your portal's\ndefault administrator user on the Basic Configuration page.}\n\\end{figure}\n\n\\section{Portal}\\label{portal}\n\nSupply this basic portal information:\n\n\\textbf{Portal Name:} name the installation you're powering with Liferay\nDXP.\n\n\\textbf{Default Language:} choose your portal's default locale and click\nthe \\emph{Change} button. This immediately localizes your portal\ncontent, including the Basic Configuration page.\n\n\\textbf{Time Zone:} select your Liferay DXP instance's default time\nzone.\n\n\\section{Administrator User}\\label{administrator-user}\n\nFor the administrator, supply the following information:\n\n\\textbf{First Name:} the default administrator user's first name\n\n\\textbf{Last Name:} the default administrator user's last name\n\n\\textbf{Email:} the default administrator user's email address\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} the administrator user's email domain is used as the\nLiferay DXP instance's default domain (i.e., the\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Company}{\\texttt{company.default.web.id}}\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\nproperty}).\n\n\\noindent\\hrulefill\n\n\\section{Database}\\label{database}\n\nThis section lets you connect to Liferay DXP's built-in data source.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} If you haven't created a database for Liferay DXP,\ncreate one now following\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install\\#preparing-a-database}{database\npreparation instructions} in the preceding article.\n\n\\noindent\\hrulefill\n\nHSQL is selected as the default database, but it's primarily for\ndemonstration or trial purposes.\n\nClick the \\emph{Change} link if you want to use Liferay DXP's built-in\ndata source and configure it to use the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install\\#preparing-a-database}{database\nyou created earlier}.\n\nThe database configuration section also has an \\emph{Add Sample Data}\ncheckbox for adding sample data to your database. This data includes\nUsers, Sites, and Organizations for demo purposes. If you're installing\nLiferay DXP on your own machine to explore features, the sample data may\nbe useful. If, however, you're installing Liferay DXP on a real server,\nstart with a clean system by leaving this checkbox unselected.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} HSQL should not be used in production Liferay DXP\ninstances. Configure Liferay DXP to use a different database; specify\nthat database via the Basic Configuration page here or using portal\nproperties. See\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install\\#preparing-a-database}{Database\nPreparation} for details.\n\n\\noindent\\hrulefill\n\nOnce you've filled out the Basic Configuration form, click \\emph{Finish\nConfiguration}. The setup wizard creates a\n\\texttt{{[}LIFERAY\\_HOME{]}/portal-setup-wizard.properties} file which\nstores the settings that you entered. When you begin customizing your\nportal's configuration, however, use a\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}\nfile}. The \\href{@platform-ref@/7.2-latest/propertiesdoc}{Portal\nproperties reference documentation} lists the default properties and\ndescribes all the properties you can set for Liferay DXP.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} The wizard is a helpful tool, especially if you're setting\nup Liferay DXP for the first time. If you're a veteran and you already\nhave your various properties set up, you can disable the setup wizard.\nIf you disable the setup wizard, you must configure everything manually\nfrom a portal properties file (e.g.,\n\\texttt{{[}LIFERAY\\_HOME{]}/portal-ext.properties}). To disable the\nsetup wizard, set \\texttt{setup.wizard.enabled=false} in your portal\nproperties file. Note that property values in\n\\texttt{portal-setup-wizard.properties} (the file the setup wizards\ncreates in Liferay Home) override property values in\n\\texttt{portal-ext.properties}.\n\n\\noindent\\hrulefill\n\nOn finishing basic configuration, Liferay DXP prompts you to restart\nyour server. When you restart your application server, Liferay DXP\ninitializes the database you specified.\n\nNow that Liferay DXP is up and running, you can continue configuring it\nas desired. Here are some suggestions:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-mail}{Configure\n  your mail session}, if you haven't already configured it.\n\\item\n  Install the Marketplace plugin, if it isn't already installed. If your\n  machine has restricted access to the public network or if you\n  restricted the Liferay DXP database user's permissions after\n  initializing the database (not recommended), you can still set up\n  Marketplace by following the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/setting-up-marketplace-and-portal-security}{Marketplace\n  setup instructions}.\n\\item\n  Read the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-product}{Configuring\n  Liferay DXP} articles for guidance in configuring Liferay DXP's\n  default time zone, locales, logging, search engine, document\n  repository, and more.\n\\end{enumerate}\n\nYou're on your way to providing your organization with terrific\nexperiences on Liferay DXP.\n\n\\chapter{Installing Liferay DXP on\nTomcat}\\label{installing-liferay-dxp-on-tomcat}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\n7.0 bundled with Tomcat 9 is available on the\n\\href{https://customer.liferay.com/downloads}{Help Center} (DXP) or the\n\\href{https://www.liferay.com/downloads-community}{Community Downloads\npage} (Portal CE). The Tomcat bundle contains JARs, scripts, and\nconfiguration files required for deploying Liferay DXP. Copying these\nfiles from the @product@ Tomcat bundle facilitates installing Liferay\nDXP on an existing Tomcat application server.\n\nWhether you copy bundle files (recommended) or download and create the\nfiles, you must download these files for\n\\href{https://customer.liferay.com/downloads}{DXP} or\n\\href{https://www.liferay.com/downloads-community}{Portal CE}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay DXP WAR file\n\\item\n  Dependencies ZIP file\n\\item\n  OSGi Dependencies ZIP file\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:}\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install}{Prepare\nfor the install} before continuing.\n\n\\noindent\\hrulefill\n\nHere are the basic steps for installing Liferay DXP on Tomcat:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[installing-dependencies]{Installing dependencies to your\n  application server}\n\\item\n  \\hyperref[configuring-tomcat]{Configuring your application server for\n  Liferay DXP}\n\\item\n  \\hyperref[deploying-product]{Deploying the Liferay DXP WAR file to\n  your application server}\n\\end{itemize}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{\\emph{Liferay\nHome}} is Tomcat server's parent folder. \\texttt{\\$TOMCAT\\_HOME} refers\nto your Tomcat server folder. It is usually named\n\\texttt{tomcat-{[}version{]}} or \\texttt{apache-tomcat-{[}version{]}}.\n\n\\section{Installing Dependencies}\\label{installing-dependencies}\n\nLiferay DXP depends on many JARs included by @product@ Tomcat bundle.\nSome of the bundle's JARs are not strictly required but can still be\nuseful. If you don't have a bundle, you can download the Liferay JARs by\ndownloading the \\emph{Dependencies} archive and the \\emph{OSGi\nDependencies} archive, and you can download the third-party JARs as\ndescribed below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create the folder \\texttt{\\$TOMCAT\\_HOME/lib/ext} if it doesn't exist\n  and extract the JARs from the dependencies ZIP to it.\n\\item\n  Copy the following JARs from a Liferay DXP Tomcat bundle (or download\n  them) to the \\texttt{\\$TOMCAT\\_HOME/lib/ext} folder:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\href{http://www.oracle.com/technetwork/java/javase/jaf-136260.html}{\\texttt{activation.jar}}\n  \\item\n    \\href{http://mvnrepository.com/artifact/javax.ccpp/ccpp/1.0}{\\texttt{ccpp.jar}}\n  \\item\n    \\href{http://www.oracle.com/technetwork/java/docs-136352.html}{\\texttt{jms.jar}}\n  \\item\n    \\href{http://www.oracle.com/technetwork/java/javaee/jta/index.html}{\\texttt{jta.jar}}\n  \\item\n    \\href{http://mvnrepository.com/artifact/com.beetstra.jutf7/jutf7}{\\texttt{jutf7.jar}}\n  \\item\n    \\href{http://www.oracle.com/technetwork/java/index-138643.html}{\\texttt{mail.jar}}\n  \\item\n    \\href{http://mvnrepository.com/artifact/org.eclipse.persistence/javax.persistence/2.1.1}{\\texttt{persistence.jar}}\n  \\item\n    \\href{http://mvnrepository.com/artifact/com.liferay.portal/com.liferay.support.tomcat}{\\texttt{support-tomcat.jar}}\n  \\end{itemize}\n\\item\n  Copy the JDBC driver for your database to the\n  \\texttt{\\$CATALINA\\_BASE/lib/ext} folder.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The [Liferay DXP Compatibility Matrix](https://web.liferay.com/documents/14/21598941/Liferay+DXP+7.2+Compatibility+Matrix/b6e0f064-db31-49b4-8317-a29d1d76abf7?) specifies supported databases and environments.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\tightlist\n\\item\n  Create an \\texttt{osgi} folder in your Liferay Home. Extract the\n  folders (i.e., \\texttt{configs}, \\texttt{core}, and more) from OSGi\n  ZIP file to the \\texttt{osgi} folder. The \\texttt{osgi} folder\n  provides the necessary modules for Liferay DXP's OSGi runtime.\n\\end{enumerate}\n\n\\section{Configuring Tomcat}\\label{configuring-tomcat}\n\nConfiguring Tomcat to run Liferay DXP includes\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Setting environment variables\n\\item\n  Specifying a web application context for Liferay DXP\n\\item\n  Setting properties and descriptors\n\\end{itemize}\n\nOptionally, if you're not using Liferay DXP's built-in data source or\nmail session, you can configure Tomcat to manage them:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[database-configuration]{Data source}\n\\item\n  \\hyperref[mail-configuration]{Mail session}\n\\end{itemize}\n\nStart with configuring Tomcat to run Liferay DXP.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If you have a Liferay DXP Tomcat bundle, copy the \\texttt{setenv.bat},\n  \\texttt{setenv.sh}, \\texttt{startup.bat}, \\texttt{startup.sh},\n  \\texttt{shutdown.bat}, and \\texttt{shutdown.sh} files from it to your\n  \\texttt{\\$CATALINA\\_BASE/bin} folder. If not, create the\n  \\texttt{setenv.bat} and \\texttt{setenv.sh}scripts.\n\n  The scripts set JVM options for Catalina, which is Tomcat's servlet\n  container. Among these options is the location of the Java runtime\n  environment. If this environment is not available on your server\n  globally, you must set its location in in these files so Tomcat can\n  run. Do this by pointing the \\texttt{JAVA\\_HOME} environment variable\n  to a Liferay DXP-supported JRE:\n\n\\begin{verbatim}\nexport JAVA_HOME=/usr/lib/jvm/java-8-jdk\nexport PATH=$JAVA_HOME/bin:$PATH\n\\end{verbatim}\n\n  Then configure Catalina's JVM options to support Liferay DXP.\n\n  Unix:\n\n\\begin{verbatim}\nCATALINA_OPTS=\"$CATALINA_OPTS -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Dorg.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=false -Duser.timezone=GMT -Xms2560m -Xmx2560m -XX:MaxMetaspaceSize=512m\"\n\\end{verbatim}\n\n  Windows:\n\n\\begin{verbatim}\nset \"CATALINA_OPTS=%CATALINA_OPTS% -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Dorg.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=false -Duser.timezone=GMT -Xms2560m -Xmx2560m -XX:MaxMetaspaceSize=512m\"\n\\end{verbatim}\n\n  This sets the file encoding to UTF-8, prefers an IPv4 stack over IPv6,\n  prevents Tomcat from working around garbage collection bugs relating\n  to static or final fields (these bugs don't exist in Liferay DXP and\n  working around them causes problems with the logging system), sets the\n  time zone to GMT, gives the JVM 2GB of RAM, and limits Metaspace to\n  512MB.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Important:** Liferay DXP requires that the application server JVM use the\n GMT time zone and UTF-8 file encoding.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** On JDK 11, it's recommended to add this JVM argument to display\n four-digit years.\n\n ```properties\n -Djava.locale.providers=JRE,COMPAT,CLDR\n ```\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nAfter installation, tune your system (including these JVM options) for\nperformance.\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  If you have a Liferay DXP Tomcat bundle, copy its\n  \\texttt{\\$CATALINA\\_BASE/conf/Catalina/localhost/ROOT.xml} file to the\n  corresponding location in your application server. Create the file\n  path if it doesn't exist. If you don't have a Liferay DXP Tomcat\n  bundle, create a \\texttt{ROOT.xml} file.\n\n  The \\texttt{ROOT.xml} file specifies a web application context for\n  Liferay DXP. \\texttt{ROOT.xml} looks like this:\n\n\\begin{verbatim}\n<Context crossContext=\"true\" path=\"\">\n\n    <!-- JAAS -->\n\n    <!--<Realm\n        className=\"org.apache.catalina.realm.JAASRealm\"\n        appName=\"PortalRealm\"\n        userClassNames=\"com.liferay.portal.kernel.security.jaas.PortalPrincipal\"\n        roleClassNames=\"com.liferay.portal.kernel.security.jaas.PortalRole\"\n    />-->\n\n    <!--\n    Uncomment the following to disable persistent sessions across reboots.\n    -->\n\n    <!--<Manager pathname=\"\" />-->\n\n    <!--\n    Uncomment the following to not use sessions. See the property\n    \"session.disabled\" in portal.properties.\n    -->\n\n    <!--<Manager className=\"com.liferay.support.tomcat.session.SessionLessManagerBase\" />-->\n\n    <Resources>\n        <PreResources\n            base=\"${catalina.base}/lib/ext/portal\"\n            className=\"com.liferay.support.tomcat.webresources.ExtResourceSet\"\n            webAppMount=\"/WEB-INF/lib\"\n        />\n    </Resources>\n</Context>\n\\end{verbatim}\n\n  Setting \\texttt{crossContext=\"true\"} lets multiple web applications\n  use the same class loader. This configuration includes commented\n  instructions and tags for configuring a JAAS realm, disabling\n  persistent sessions, and disabling sessions entirely.\n\\item\n  Provide Catalina access to the JARs in\n  \\texttt{\\$CATALINA\\_BASE/lib/ext} by opening your\n  \\texttt{\\$CATALINA\\_BASE/conf/catalina.properties} file and appending\n  this value to the \\texttt{common.loader} property:\n\n\\begin{verbatim}\n,\"${catalina.home}/lib/ext/global\",\"${catalina.home}/lib/ext/global/*.jar\",\"${catalina.home}/lib/ext\",\"${catalina.home}/lib/ext/*.jar\"\n\\end{verbatim}\n\\item\n  Make sure to use UTF-8 URI encoding consistently. If you have a\n  Liferay DXP Tomcat bundle, copy the\n  \\texttt{\\$CATALINA\\_BASE/conf/server.xml} file to your server. If not,\n  open your \\texttt{\\$CATALINA\\_BASE/conf/server.xml} file and add the\n  attribute \\texttt{URIEncoding=\"UTF-8\"} to HTTP and AJP connectors that\n  use \\texttt{redirectPort=8443}. Here are examples:\n\n  Old:\n\n\\begin{verbatim}\n<Connector port=\"8080\" protocol=\"HTTP/1.1\" connectionTimeout=\"20000\" redirectPort=\"8443\" />\n\\end{verbatim}\n\n  New:\n\n\\begin{verbatim}\n<Connector port=\"8080\" protocol=\"HTTP/1.1\" connectionTimeout=\"20000\" redirectPort=\"8443\" URIEncoding=\"UTF-8\" />\n\\end{verbatim}\n\n  Old:\n\n\\begin{verbatim}\n<Connector port=\"8009\" protocol=\"AJP/1.3\" redirectPort=\"8443\" />\n\\end{verbatim}\n\n  New:\n\n\\begin{verbatim}\n<Connector port=\"8009\" protocol=\"AJP/1.3\" redirectPort=\"8443\" URIEncoding=\"UTF-8\" />\n\\end{verbatim}\n\\item\n  Refrain from writing access logs (optional) by commenting out the\n  access log \\texttt{Valve} element in\n  \\texttt{\\$CATALINA\\_BASE/conf/server.xml}. It's commented out here:\n\n\\begin{verbatim}\n<!-- <Valve className=\"org.apache.catalina.valves.AccessLogValve\"\n       directory=\"logs\"\n       prefix=\"localhost_access_log\" suffix=\".txt\"\n       pattern=\"%h %l %u %t &quot;%r&quot; %s %b\" /> -->\n\\end{verbatim}\n\\item\n  Optionally, set the following log levels in your\n  \\texttt{\\$CATALINA\\_HOME/conf/logging.properties} file:\n\n\\begin{verbatim}\norg.apache.catalina.startup.Catalina.level=INFO\norg.apache.catalina.startup.ClassLoaderFactory.level=SEVERE\norg.apache.catalina.startup.VersionLoggerListener.level=WARNING\norg.apache.level=WARNING\n\\end{verbatim}\n\\item\n  In \\texttt{\\$CATALINA\\_HOME/conf/web.xml}, set the JSP compiler to\n  Java 8 and set Liferay DXP's \\texttt{TagHandlerPool} class to manage\n  the JSP tag pool. Do this by adding the following elements above the\n  \\texttt{jsp} servlet element's\n  \\texttt{\\textless{}load-on-startup\\textgreater{}} element.\n\n\\begin{verbatim}\n<init-param>\n    <param-name>compilerSourceVM</param-name>\n    <param-value>1.8</param-value>\n</init-param>\n<init-param>\n    <param-name>compilerTargetVM</param-name>\n    <param-value>1.8</param-value>\n</init-param>\n<init-param>\n    <param-name>tagpoolClassName</param-name>\n    <param-value>com.liferay.support.tomcat.jasper.runtime.TagHandlerPool</param-value>\n</init-param>\n\\end{verbatim}\n\\item\n  In \\texttt{\\$CATALINA\\_HOME/conf/web.xml}, specify whether the\n  application server should look for extra metadata, such as annotations\n  in the application's JARs and classes. Setting \\texttt{web-app}\n  element's attribute \\texttt{metadata-complete=\"true\"} tells the\n  application server there's no extra metadata. The application server\n  starts up faster this way. The default is to check for extra metadata.\n\\item\n  If you're on Unix, Linux, or Mac OS, make the shell scripts in your\n  \\texttt{\\$CATALINA\\_HOME/bin} and \\texttt{\\$CATALINA\\_BASE/bin}\n  folders executable by running this command in each folder:\n\n\\begin{verbatim}\nchmod a+x *.sh\n\\end{verbatim}\n\\end{enumerate}\n\n\\textbf{Checkpoint:}\n\nYour application server is configured to run Liferay DXP.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The file encoding, user time-zone, and preferred protocol stack are\n  set in your \\texttt{setenv.sh}.\n\\item\n  The default memory available and Metaspace limit are set.\n\\item\n  \\texttt{\\$CATALINA\\_BASE/conf/Catalina/localhost/ROOT.xml} declares\n  the web application context.\n\\item\n  The \\texttt{common.loader} property in\n  \\texttt{\\$CATALINA\\_BASE/conf/catalina.properties} grants Catalina\n  access to the JARs in \\texttt{\\$CATALINA\\_BASE/lib/ext}.\n\\item\n  \\texttt{\\$CATALINA\\_BASE/conf/server.xml} sets UTF-8 encoding.\n\\item\n  \\texttt{\\$CATALINA\\_BASE/conf/server.xml} doesn't declare any valve\n  for writing host access logs. (optional)\n\\item\n  \\texttt{\\$CATALINA\\_HOME/conf/logging.properties} sets the desired log\n  levels.\n\\item\n  \\texttt{\\$CATALINA\\_HOME/conf/web.xml} sets the tag handler pool and\n  sets Java 8 as the JSP compiler.\n\\item\n  \\texttt{\\$CATALINA\\_HOME/conf/web.xml} specifies for the application\n  server to refrain from looking for extra metadata. (optional)\n\\item\n  The scripts in Tomcat's \\texttt{bin} folders are executable.\n\\end{enumerate}\n\n\\section{Database Configuration}\\label{database-configuration}\n\nThe easiest way to handle your database configuration is to let Liferay\nDXP manage your data source. If you want to use the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install\\#using-the-built-in-data-source}{built-in\ndata source (recommended)}, skip this section.\n\nIf you want Tomcat to manage your data source, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Make sure your database server is installed and working. If it's\n  installed on a different machine, make sure your Liferay DXP machine\n  can access it.\n\\item\n  Open \\texttt{\\$CATALINA\\_BASE/conf/Catalina/localhost/ROOT.xml} and\n  add your data source as a \\texttt{Resource} in your web application\n  \\texttt{Context}:\n\n\\begin{verbatim}\n<Context...>\n    ...\n    <Resource\n        name=\"jdbc/LiferayPool\"\n        auth=\"Container\"\n        type=\"javax.sql.DataSource\"\n        driverClassName=\"[place the database driver class here]\"\n        url=\"[place the URL to your database here]\"\n        username=\"[place your user name here]\"\n        password=\"[place your password here]\"\n        maxTotal=\"100\"\n        maxIdle=\"30\"\n        maxWaitMillis=\"10000\"\n    />\n</Context>\n\\end{verbatim}\n\\end{enumerate}\n\nMake sure to replace the database URL, user name, and password with the\nappropriate values. For example JDBC connection values, please see\n\\href{/docs/7-2/deploy/-/knowledge_base/d/database-templates}{Database\nTemplates}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  In a \\texttt{portal-ext.properties} file in your Liferay Home, specify\n  your data source:\n\n\\begin{verbatim}\njdbc.default.jndi.name=jdbc/LiferayPool\n\\end{verbatim}\n\\end{enumerate}\n\nYou created a data source for Tomcat to manage and configured Liferay\nDXP to use it. Mail session configuration is next.\n\n\\section{Mail Configuration}\\label{mail-configuration}\n\nAs with database configuration, the easiest way to configure mail is to\nlet Liferay DXP handle your mail session. If you want to use @product@'s\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-mail}{built-in\nmail session}, skip this section.\n\nIf you want to manage your mail session with Tomcat, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\texttt{\\$CATALINA\\_BASE/conf/Catalina/localhost/ROOT.xml} and\n  add your mail session as a \\texttt{Resource} in your web application\n  \\texttt{Context}. Make sure to replace the example mail session values\n  with your own.\n\n\\begin{verbatim}\n<Context...>\n    ...\n    <Resource\n        name=\"mail/MailSession\"\n        auth=\"Container\"\n        type=\"javax.mail.Session\"\n        mail.pop3.host=\"pop.gmail.com\"\n        mail.pop3.port=\"110\"\n        mail.smtp.host=\"smtp.gmail.com\"\n        mail.smtp.port=\"465\"\n        mail.smtp.user=\"user\"\n        mail.smtp.password=\"password\"\n        mail.smtp.auth=\"true\"\n        mail.smtp.starttls.enable=\"true\"\n        mail.smtp.socketFactory.class=\"javax.net.ssl.SSLSocketFactory\"\n        mail.imap.host=\"imap.gmail.com\"\n        mail.imap.port=\"993\"\n        mail.transport.protocol=\"smtp\"\n        mail.store.protocol=\"imap\"\n    />\n</Context>\n\\end{verbatim}\n\\item\n  In your \\texttt{portal-ext.properties} file in Liferay Home, reference\n  your mail session:\n\n\\begin{verbatim}\nmail.session.jndi.name=mail/MailSession\n\\end{verbatim}\n\\end{enumerate}\n\nYou've created a mail session for Tomcat to manage and configured\nLiferay DXP to use it.\n\n\\section{Deploying Liferay DXP}\\label{deploying-liferay-dxp-1}\n\nNow you're ready to deploy Liferay DXP using the @product@ WAR file.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If you are manually installing Liferay DXP on a clean Tomcat server,\n  delete the contents of the \\texttt{\\$CATALINA\\_BASE/webapps/ROOT}\n  folder. This removes the default Tomcat home page.\n\\item\n  Extract the Liferay DXP \\texttt{.war} file contents to\n  \\texttt{\\$CATALINA\\_BASE/webapps/ROOT}.\n\n  It's time to launch Liferay DXP on Tomcat!\n\\item\n  Start Tomcat by navigating to \\texttt{\\$CATALINA\\_HOME/bin} and\n  executing \\texttt{./startup.sh}. Alternatively, execute\n  \\texttt{./catalina.sh\\ run} to tail Liferay DXP's log file. The log\n  audits startup activities and is useful for debugging deployment.\n\\end{enumerate}\n\nCongratulations on successfully installing and deploying Liferay DXP on\nTomcat!\n\n\\chapter{Installing Liferay DXP on\nWildfly}\\label{installing-liferay-dxp-on-wildfly}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nInstalling Liferay DXP on Wildfly 11 takes three steps:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[installing-dependencies]{Installing dependencies to your\n  application server}\n\\item\n  \\hyperref[configuring-wildfly]{Configuring your application server for\n  Liferay DXP}\n\\item\n  \\hyperref[deploying-product]{Deploying the Liferay DXP WAR file to\n  your application server}\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Before installing Liferay DXP, familiarize yourself\nwith\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install}{preparing\nfor install}.\n\n\\noindent\\hrulefill\n\nNow,\n\\href{/docs/7-2/deploy/-/knowledge_base/d/obtaining-product\\#downloading-the-liferay-war-and-dependency-jars}{download\nthe Liferay DXP WAR and Dependency JARs}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay DXP WAR file\n\\item\n  Dependencies ZIP file\n\\item\n  OSGi Dependencies ZIP file\n\\end{itemize}\n\nNote that\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{\\emph{Liferay\nHome}} is the folder containing your Wildfly server folder. After\ninstalling and deploying Liferay DXP, the Liferay Home folder contains\nthe Wildfly server folder as well as \\texttt{data}, \\texttt{deploy},\n\\texttt{logs}, and \\texttt{osgi} folders. \\texttt{\\$WILDFLY\\_HOME}\nrefers to your Wildfly server folder. It is usually named\n\\texttt{wildfly-{[}version{]}}.\n\n\\section{Installing Dependencies}\\label{installing-dependencies-1}\n\nLiferay DXP depends on a driver for your database and the JARs in the\nDependencies ZIP and OSGi Dependencies ZIP files you downloaded. Here's\nhow to install them:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create the folder\n  \\texttt{\\$WILDFLY\\_HOME/modules/com/liferay/portal/main} if it doesn't\n  exist and extract the Dependencies ZIP JARs to it.\n\\item\n  Download your database driver \\texttt{.jar} file and copy it into the\n  same folder.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The [Liferay DXP Compatibility Matrix](https://web.liferay.com/documents/14/21598941/Liferay+DXP+7.2+Compatibility+Matrix/b6e0f064-db31-49b4-8317-a29d1d76abf7?) specifies supported databases and environments.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Create the file \\texttt{module.xml} in the\n  \\texttt{\\$WILDFLY\\_HOME/modules/com/liferay/portal/main} folder and\n  insert this configuration:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<module xmlns=\"urn:jboss:module:1.0\" name=\"com.liferay.portal\">\n    <resources>\n        <resource-root path=\"com.liferay.petra.concurrent.jar\" />\n        <resource-root path=\"com.liferay.petra.executor.jar\" />\n        <resource-root path=\"com.liferay.petra.function.jar\" />\n        <resource-root path=\"com.liferay.petra.io.jar\" />\n        <resource-root path=\"com.liferay.petra.lang.jar\" />\n        <resource-root path=\"com.liferay.petra.memory.jar\" />\n        <resource-root path=\"com.liferay.petra.nio.jar\" />\n        <resource-root path=\"com.liferay.petra.process.jar\" />\n        <resource-root path=\"com.liferay.petra.reflect.jar\" />\n        <resource-root path=\"com.liferay.petra.string.jar\" />\n        <resource-root path=\"com.liferay.registry.api.jar\" />\n        <resource-root path=\"hsql.jar\" />\n        <resource-root path=\"[place your database vendor's jar here]\" />\n        <resource-root path=\"portal-kernel.jar\" />\n        <resource-root path=\"portlet.jar\" />\n    </resources>\n    <dependencies>\n        <module name=\"javax.api\" />\n        <module name=\"javax.mail.api\" />\n        <module name=\"javax.servlet.api\" />\n        <module name=\"javax.servlet.jsp.api\" />\n        <module name=\"javax.transaction.api\" />\n    </dependencies>\n</module>\n\\end{verbatim}\n\n  Replace\n  \\texttt{{[}place\\ your\\ database\\ vendor\\textquotesingle{}s\\ jar\\ here{]}}\n  with the driver JAR for your database.\n\\item\n  Create an \\texttt{osgi} folder in your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  folder. Extract the OSGi Dependencies ZIP file that you downloaded\n  into the \\texttt{{[}Liferay\\ Home{]}/osgi} folder.\n\n  The \\texttt{osgi} folder provides the necessary modules for Liferay\n  DXP's OSGi runtime.\n\\end{enumerate}\n\n\\section{Running Liferay DXP on Wildfly in Standalone Mode vs.~Domain\nMode}\\label{running-liferay-dxp-on-wildfly-in-standalone-mode-vs.-domain-mode}\n\nWildfly can be launched in either \\emph{standalone} mode or\n\\emph{domain} mode. Domain mode allows multiple application server\ninstances to be managed from a single control point. A collection of\nsuch application servers is known as a \\emph{domain}. For more\ninformation on standalone mode vs.~domain mode, please refer to the\nsection on this topic in the\n\\href{https://docs.jboss.org/author/display/WFLY/Admin+Guide\\#AdminGuide-Operatingmodes}{Wildfly\nAdmin Guide}. Liferay DXP fully supports Wildfly in standalone mode but\nnot in domain mode.\n\nYou can run Liferay DXP on Wildfly in domain mode, but this method is\nnot fully supported. In particular, Liferay DXP's hot-deploy does not\nwork with a managed deployment, since Wildfly manages the content of a\nmanaged deployment by copying files (exploded or non-exploded). This\nprevents JSP hooks and Ext plugins from working as intended. For\nexample, JSP hooks don't work on Wildfly running in managed domain mode,\nsince Liferay DXP's JSP override mechanism relies on the application\nserver. Since JSP hooks and Ext plugins are deprecated, however, you may\nnot be using them.\n\nThe command line interface is recommended for domain mode deployments.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} This does not prevent Liferay DXP from running in a\nclustered environment on multiple Wildfly servers. You can set up a\ncluster of Liferay DXP instances running on Wildfly servers running in\nstandalone mode. Please refer to the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering}{Liferay\nDXP clustering articles} for more information.\n\n\\noindent\\hrulefill\n\n\\section{Configuring Wildfly}\\label{configuring-wildfly}\n\nConfiguring Wildfly to run Liferay DXP includes these things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Setting environment variables\n\\item\n  Setting properties and descriptors\n\\item\n  Removing unnecessary configurations\n\\end{itemize}\n\nOptionally, you can configure Wildfly to manage Liferay DXP's data\nsource and mail session.\n\nStart with configuring Wildfly to run Liferay DXP.\n\nMake the following modifications to\n\\texttt{\\$WILDFLY\\_HOME/standalone/configuration/standalone.xml}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Locate the closing \\texttt{\\textless{}/extensions\\textgreater{}} tag.\n  Directly beneath that tag, insert the following system properties:\n\n\\begin{verbatim}\n<system-properties>\n    <property name=\"org.apache.catalina.connector.URI_ENCODING\" value=\"UTF-8\" />\n    <property name=\"org.apache.catalina.connector.USE_BODY_ENCODING_FOR_QUERY_STRING\" value=\"true\" />\n</system-properties>\n\\end{verbatim}\n\\item\n  Add the following \\texttt{\\textless{}filter-spec\\textgreater{}} tag\n  within the \\texttt{\\textless{}console-handler\\textgreater{}} tag,\n  directly below the\n  \\texttt{\\textless{}level\\ name=\"INFO\"/\\textgreater{}} tag:\n\n\\begin{verbatim}\n<filter-spec value=\"not(any(match(&quot;WFLYSRV0059&quot;),match(&quot;WFLYEE0007&quot;)))\" />\n\\end{verbatim}\n\\item\n  Add a timeout for the deployment scanner by setting\n  \\texttt{deployment-timeout=\"600\"} as seen in the excerpt below.\n\n\\begin{verbatim}\n<subsystem xmlns=\"urn:jboss:domain:deployment-scanner:2.0\">\n    <deployment-scanner deployment-timeout=\"600\" path=\"deployments\" relative-to=\"jboss.server.base.dir\" scan-interval=\"5000\" runtime-failure-causes-rollback=\"${jboss.deployment.scanner.rollback.on.failure:false}\"/>\n</subsystem>\n\\end{verbatim}\n\\item\n  Add the following JAAS security domain to the security subsystem\n  \\texttt{\\textless{}security-domains\\textgreater{}} defined in element\n  \\texttt{\\textless{}subsystem\\ xmlns=\"urn:jboss:domain:security:2.0\"\\textgreater{}}.\n\n\\begin{verbatim}\n<security-domain name=\"PortalRealm\">\n    <authentication>\n        <login-module code=\"com.liferay.portal.security.jaas.PortalLoginModule\" flag=\"required\" />\n    </authentication>\n</security-domain>\n\\end{verbatim}\n\\item\n  Remove the welcome content code snippets:\n\n\\begin{verbatim}\n<location name=\"/\" handler=\"welcome-content\"/>\n\\end{verbatim}\n\n  and\n\n\\begin{verbatim}\n<handlers>\n    <file name=\"welcome-content\" path=\"${jboss.home.dir}/welcome-content\"/>\n</handlers>\n\\end{verbatim}\n\\item\n  Find the \\texttt{\\textless{}jsp-config/\\textgreater{}} tag and set the\n  \\texttt{development}, \\texttt{source-vm}, and \\texttt{target-vm}\n  attributes in the tag. Once finished, the tag should look like this:\n\n\\begin{verbatim}\n<jsp-config development=\"true\" source-vm=\"1.8\" target-vm=\"1.8\" />\n\\end{verbatim}\n\\end{enumerate}\n\n\\textbf{Checkpoint:}\n\nBefore continuing, verify the following properties have been set in the\n\\texttt{standalone.xml} file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The new \\texttt{\\textless{}system-property\\textgreater{}} is added.\n\\item\n  The new \\texttt{\\textless{}filter-spec\\textgreater{}} is added.\n\\item\n  The \\texttt{\\textless{}deployment-timeout\\textgreater{}} is set to\n  \\texttt{600}.\n\\item\n  The new \\texttt{\\textless{}security-domain\\textgreater{}} is created.\n\\item\n  Welcome content is removed.\n\\item\n  The \\texttt{\\textless{}jsp-config\\textgreater{}} tag contains its new\n  attributes.\n\\end{enumerate}\n\nNow you must configure your JVM and startup scripts.\n\nIn the \\texttt{\\$WILDFLY\\_HOME/bin/} folder, modify your standalone\ndomain's configuration script file \\texttt{standalone.conf}\n(\\texttt{standalone.conf.bat} on Windows):\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Set the file encoding to \\texttt{UTF-8}\n\\item\n  Set the user time zone to \\texttt{GMT}\n\\item\n  Set the preferred protocol stack\n\\item\n  Increase the default amount of memory available.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} For Liferay DXP to work properly, the application\nserver JVM must use the \\texttt{GMT} time zone and \\texttt{UTF-8} file\nencoding.\n\n\\noindent\\hrulefill\n\nMake the following edits as applicable for your operating system:\n\n\\textbf{Windows:}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Comment out the initial \\texttt{JAVA\\_OPTS} assignment like this:\n\n\\begin{verbatim}\nrem set \"JAVA_OPTS=-Xms64M -Xmx512M -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=2560m\"\n\\end{verbatim}\n\\item\n  Add the following \\texttt{JAVA\\_OPTS} assignment one line above the\n  \\texttt{:JAVA\\_OPTS\\_SET} line found at end of the file:\n\n\\begin{verbatim}\nset \"JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Djboss.as.management.blocking.timeout=480 -Duser.timezone=GMT -Xms2560m -Xmx2560m -XX:MaxMetaspaceSize=512m -XX:MetaspaceSize=200m\"\n\\end{verbatim}\n\\end{enumerate}\n\n\\textbf{Unix:}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Below the \\texttt{if\\ {[}\\ \"x\\$JAVA\\_OPTS\"\\ =\\ \"x\"\\ {]};} statement,\n  replace this \\texttt{JAVA\\_OPTS} statement:\n\n\\begin{verbatim}\nJAVA_OPTS=\"-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true\"\n\\end{verbatim}\n\n  with this:\n\n\\begin{verbatim}\nJAVA_OPTS=\"-Djava.net.preferIPv4Stack=true\"\n\\end{verbatim}\n\\item\n  Add the following statement to the bottom of the file:\n\n\\begin{verbatim}\nJAVA_OPTS=\"$JAVA_OPTS -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Djboss.as.management.blocking.timeout=480 -Duser.timezone=GMT -Xms2560m -Xmx2560m -XX:MaxMetaspaceSize=512m -XX:MetaspaceSize=200m\"\n\\end{verbatim}\n\\end{enumerate}\n\nThis sets the file encoding to UTF-8, prefers an IPv4 stack over IPv6,\nsets the time zone to GMT, gives the JVM 2GB of RAM, and limits\nMetaspace to 512MB.\n\nOn JDK 11, it's recommended to add this JVM argument to display\nfour-digit years.\n\n\\begin{verbatim}\n-Djava.locale.providers=JRE,COMPAT,CLDR\n\\end{verbatim}\n\nAfter installation, tune your system (including these JVM options) for\nperformance.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} For Liferay DXP to work properly, the application\nserver JVM must use the \\texttt{GMT} time zone and \\texttt{UTF-8} file\nencoding.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you plan on using the IBM JDK with your Wildfly\nserver, you must complete some additional steps. First, navigate to the\n\\texttt{\\$WILDFLY\\_HOME/modules/com/liferay/portal/main/module.xml} file\nand insert the following dependency within the\n\\texttt{\\textless{}dependencies\\textgreater{}} element:\n\n\\begin{verbatim}\n <module name=\"ibm.jdk\" />\n\\end{verbatim}\n\nThen navigate to the\n\\texttt{\\$WILDFLY\\_HOME/modules/system/layers/base/sun/jdk/main/module.xml}\nfile and insert the following path names inside the\n\\texttt{\\textless{}paths\\textgreater{}...\\textless{}/paths\\textgreater{}}\nelement:\n\n\\begin{verbatim}\n <path name=\"com/sun/crypto\" />\n <path name=\"com/sun/crypto/provider\" />\n <path name=\"com/sun/org/apache/xml/internal/resolver\" />\n <path name=\"com/sun/org/apache/xml/internal/resolver/tools\" />\n\\end{verbatim}\n\nThe added paths resolve issues with deployment exceptions and image\nuploading problems.\n\n\\noindent\\hrulefill\n\n\\textbf{Checkpoint:}\n\nYou've configured the application server's JVM settings.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The file encoding, user time-zone, preferred protocol stack have been\n  set in the \\texttt{JAVA\\_OPTS} in the \\texttt{standalone.conf.bat}\n  file.\n\\item\n  The default amount of memory available has been increased.\n\\end{enumerate}\n\nThe prescribed script modifications are now complete for your Liferay\nDXP installation on Wildfly. Next you'll configure your database.\n\n\\section{Database Configuration}\\label{database-configuration-1}\n\nThe easiest way to handle database configuration is to let Liferay DXP\nmanage your data source. The\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install\\#using-the-built-in-data-source}{Basic\nConfiguration} page lets you configure Liferay DXP's built-in data\nsource. If you want to use the built-in data source, skip this section.\n\nIf using WildFly to manage the data source, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the data source inside the\n  \\texttt{\\$WILDFLY\\_HOME/standalone/configuration/standalone.xml}\n  file's \\texttt{\\textless{}datasources\\textgreater{}} element:\n\n\\begin{verbatim}\n<datasource jndi-name=\"java:jboss/datasources/ExampleDS\" pool-name=\"ExampleDS\" enabled=\"true\" jta=\"true\" use-java-context=\"true\" use-ccm=\"true\">\n    <connection-url>[place the URL to your database here]</connection-url>\n    <driver>[place your driver name here]</driver>\n    <security>\n        <user-name>[place your user name here]</user-name>\n        <password>[place your password here]</password>\n    </security>\n</datasource>\n\\end{verbatim}\n\n  Make sure to replace the database URL, user name, and password with\n  the appropriate values. For example JDBC connection values, please see\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/database-templates}{Database\n  Templates}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** If the data source `jndi-name` must be changed, edit the `datasource` element in the `<default-bindings>` tag.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the driver to the \\texttt{standalone.xml} file's\n  \\texttt{\\textless{}drivers\\textgreater{}} element also found within\n  the \\texttt{\\textless{}datasources\\textgreater{}} element.\n\n\\begin{verbatim}\n<drivers>\n    <driver name=\"[name of database driver]\" module=\"com.liferay.portal\">\n        <driver-class>[JDBC driver class]</driver-class>\n    </driver>\n</drivers>\n\\end{verbatim}\n\n  A final data sources subsystem that uses MySQL should look like this:\n\n\\begin{verbatim}\n<subsystem xmlns=\"urn:jboss:domain:datasources:1.0\">\n    <datasources>\n        <datasource jndi-name=\"java:jboss/datasources/ExampleDS\" pool-name=\"ExampleDS\" enabled=\"true\" jta=\"true\" use-java-context=\"true\" use-ccm=\"true\">\n            <connection-url>jdbc:mysql://localhost/lportal</connection-url>\n            <driver>mysql</driver>\n            <security>\n                <user-name>root</user-name>\n                <password>root</password>\n            </security>\n        </datasource>\n        <drivers>\n            <driver name=\"mysql\" module=\"com.liferay.portal\">\n                <driver-class>com.mysql.cj.jdbc.Driver</driver-class>\n            </driver>\n        </drivers>\n    </datasources>\n</subsystem>\n\\end{verbatim}\n\\item\n  In a\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}}\n  file in your Liferay Home, specify your data source:\n\n\\begin{verbatim}\njdbc.default.jndi.name=java:jboss/datasources/ExampleDS\n\\end{verbatim}\n\\end{enumerate}\n\nNow that you've configured your data source, the mail session is next.\n\n\\section{Mail Configuration}\\label{mail-configuration-1}\n\nAs with database configuration, the easiest way to configure mail is to\nlet Liferay DXP handle your mail session. If you want to use @product@'s\nbuilt-in mail session, skip this section and\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-mail}{configure\nthe mail session} in the Control Panel.\n\nIf you want to manage your mail session with Wildfly, follow these\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Specify your mail subsystem in the\n  \\texttt{\\$WILDFLY\\_HOME/standalone/configuration/standalone.xml} file\n  like this:\n\n\\begin{verbatim}\n<subsystem xmlns=\"urn:jboss:domain:mail:3.0\">\n    <mail-session jndi-name=\"java:jboss/mail/MailSession\" name=\"mail-smtp\">\n        <smtp-server ssl=\"true\" outbound-socket-binding-ref=\"mail-smtp\" username=\"USERNAME\" password=\"PASSWORD\"/>\n   </mail-session>\n</subsystem>\n...\n<socket-binding-group name=\"standard-sockets\" default-interface=\"public\" port-offset=\"${jboss.socket.binding.port-offset:0}\">\n...\n<outbound-socket-binding name=\"mail-smtp\">\n        <remote-destination host=\"[place SMTP host here]\" port=\"[place SMTP port here]\"/>\n    </outbound-socket-binding>\n</socket-binding-group>\n\\end{verbatim}\n\\item\n  In your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}}\n  file in Liferay Home, reference your mail session:\n\n\\begin{verbatim}\nmail.session.jndi.name=java:jboss/mail/MailSession\n\\end{verbatim}\n\\end{enumerate}\n\nNext, you'll deploy Liferay DXP to your Wildfly app server.\n\n\\section{Deploying Liferay DXP}\\label{deploying-liferay-dxp-2}\n\nNow you're ready to deploy Liferay DXP using the @product@ WAR file.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If the folder \\texttt{\\$WILDFLY\\_HOME/standalone/deployments/ROOT.war}\n  already exists in your Wildfly installation, delete all of its\n  subfolders and files. Otherwise, create a new folder called\n  \\texttt{\\$WILDFLY\\_HOME/standalone/deployments/ROOT.war}.\n\\item\n  Unzip the Liferay DXP \\texttt{.war} file into the \\texttt{ROOT.war}\n  folder.\n\\item\n  To trigger deployment of \\texttt{ROOT.war}, create an empty file named\n  \\texttt{ROOT.war.dodeploy} in your\n  \\texttt{\\$WILDFLY\\_HOME/standalone/deployments/} folder. On startup,\n  Wildfly detects this file and deploys it as a web application.\n\\item\n  Start the Wildfly application server by navigating to\n  \\texttt{\\$WILDFLY\\_HOME/bin} and running \\texttt{standalone.bat} or\n  \\texttt{standalone.sh}.\n\\end{enumerate}\n\nCongratulations; you've deployed Liferay DXP on Wildfly!\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} After deploying Liferay DXP, you may see excessive\nwarnings and log messages, such as the ones below, involving\n\\texttt{PhaseOptimizer}. These are benign and can be ignored. Make sure\nto adjust your app server's logging level or log filters to avoid\nexcessive benign log messages.\n\n\\begin{verbatim}\n May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n WARNING: Skipping pass gatherExternProperties\n May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n WARNING: Skipping pass checkControlFlow\n May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n INFO: pass supports: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, modules, exponent operator (**), async function, trailing comma in param list]\n current AST contains: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, exponent operator (**), async function, trailing comma in param list, object literals with spread, object pattern rest]\n\\end{verbatim}\n\n\\chapter{Installing Liferay DXP on JBoss\nEAP}\\label{installing-liferay-dxp-on-jboss-eap}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nInstalling Liferay DXP on JBoss EAP 7.1 takes three steps:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Installing dependencies to your application server\n\\item\n  Configuring your application server for Liferay DXP\n\\item\n  Installing the Liferay DXP WAR file to your application server\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Before installing Liferay DXP, familiarize yourself\nwith\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install}{preparing\nfor install}.\n\n\\noindent\\hrulefill\n\nNow,\n\\href{/docs/7-2/deploy/-/knowledge_base/d/obtaining-product\\#downloading-the-liferay-war-and-dependency-jars}{download\nthe Liferay DXP WAR and Dependency JARs}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay DXP WAR file\n\\item\n  Dependencies ZIP file\n\\item\n  OSGi Dependencies ZIP file\n\\end{itemize}\n\nNot that\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{\\emph{Liferay\nHome}} is the folder containing your JBoss server folder. After\ninstalling and deploying Liferay DXP, the Liferay Home folder contains\nthe JBoss server folder as well as \\texttt{data}, \\texttt{deploy},\n\\texttt{logs}, and \\texttt{osgi} folders. \\texttt{\\$JBOSS\\_HOME} refers\nto your JBoss server folder. This folder is usually named\n\\texttt{jboss-eap-{[}version{]}}.\n\n\\section{Installing Dependencies}\\label{installing-dependencies-2}\n\nLiferay DXP depends on several Liferay-specific and third-party JARs.\nDownload and install the required JARs as described below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create the folder\n  \\texttt{\\$JBOSS\\_HOME/modules/com/liferay/portal/main} if it doesn't\n  exist and extract the JARs from the dependencies ZIP to it.\n\\item\n  Download your database driver \\texttt{.jar} file and copy it into the\n  same folder.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The [Liferay DXP Compatibility Matrix](https://web.liferay.com/documents/14/21598941/Liferay+DXP+7.2+Compatibility+Matrix/b6e0f064-db31-49b4-8317-a29d1d76abf7?) specifies supported databases and environments.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Create the file \\texttt{module.xml} in the\n  \\texttt{\\$JBOSS\\_HOME/modules/com/liferay/portal/main} folder and\n  insert this configuration:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<module xmlns=\"urn:jboss:module:1.0\" name=\"com.liferay.portal\">\n    <resources>\n        <resource-root path=\"com.liferay.petra.concurrent.jar\" />\n        <resource-root path=\"com.liferay.petra.executor.jar\" />\n        <resource-root path=\"com.liferay.petra.function.jar\" />\n        <resource-root path=\"com.liferay.petra.io.jar\" />\n        <resource-root path=\"com.liferay.petra.lang.jar\" />\n        <resource-root path=\"com.liferay.petra.memory.jar\" />\n        <resource-root path=\"com.liferay.petra.nio.jar\" />\n        <resource-root path=\"com.liferay.petra.process.jar\" />\n        <resource-root path=\"com.liferay.petra.reflect.jar\" />\n        <resource-root path=\"com.liferay.petra.string.jar\" />\n        <resource-root path=\"com.liferay.registry.api.jar\" />\n        <resource-root path=\"hsql.jar\" />\n        <resource-root path=\"[place your database vendor's jar here]\" />\n        <resource-root path=\"portal-kernel.jar\" />\n        <resource-root path=\"portlet.jar\" />\n    </resources>\n    <dependencies>\n        <module name=\"javax.api\" />\n        <module name=\"javax.mail.api\" />\n        <module name=\"javax.servlet.api\" />\n        <module name=\"javax.servlet.jsp.api\" />\n        <module name=\"javax.transaction.api\" />\n    </dependencies>\n</module>\n\\end{verbatim}\n\n  Replace\n  \\texttt{{[}place\\ your\\ database\\ vendor\\textquotesingle{}s\\ jar\\ here{]}}\n  with the driver JAR for your database.\n\\item\n  Create an \\texttt{osgi} folder in your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  folder. Extract the OSGi Dependencies ZIP file that you downloaded\n  into the \\texttt{{[}Liferay\\ Home{]}/osgi} folder.\n\n  The \\texttt{osgi} folder provides the necessary modules for Liferay\n  DXP's OSGi runtime.\n\\end{enumerate}\n\n\\textbf{Checkpoint:}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  The dependencies files have been unzipped into the\n  \\texttt{\\$JBOSS\\_HOME/modules/com/liferay/portal/main} folder and a\n  database jar.\n\\item\n  The \\texttt{module.xml} contains all JARs in the\n  \\texttt{\\textless{}resource-root-path\\textgreater{}} elements.\n\\item\n  The \\texttt{osgi} dependencies have been unzipped into the\n  \\texttt{osgi} folder.\n\\end{enumerate}\n\n\\section{Running Liferay DXP on JBoss EAP in Standalone Mode vs.~Domain\nMode}\\label{running-liferay-dxp-on-jboss-eap-in-standalone-mode-vs.-domain-mode}\n\nJBoss EAP can be launched in either \\emph{standalone} mode or\n\\emph{domain} mode. Domain mode allows multiple application server\ninstances to be managed from a single control point. A collection of\nsuch application servers is known as a \\emph{domain}. For more\ninformation on standalone mode vs.~domain mode, please refer to the\nsection on this topic in the\n\\href{https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.1/html/introduction_to_jboss_eap/overview_of_jboss_eap\\#operating_modes}{JBoss\nEAP Product Documentation}.\n\nLiferay DXP supports JBoss EAP when it runs in standalone mode but not\nwhen it runs in domain mode. Liferay DXP's hot-deploy does not work with\na managed deployment, since JBoss manages the content of a managed\ndeployment by copying files (exploded or non-exploded). This prevents\nJSP hooks and Ext plugins from working as intended. For example, JSP\nhooks don't work on JBoss EAP running in managed domain mode, since\nLiferay DXP's JSP override mechanism relies on the application server.\nSince JSP hooks and Ext plugins are deprecated, however, you may not be\nusing them.\n\nThe command line interface is recommended for domain mode deployments.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} This does not prevent Liferay DXP from running in a\nclustered environment on multiple JBoss servers. You can set up a\ncluster of Liferay DXP instances running on JBoss EAP servers running in\nstandalone mode. Please refer to the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering}{Liferay\nDXP clustering articles} for more information.\n\n\\noindent\\hrulefill\n\n\\section{Configuring JBoss}\\label{configuring-jboss}\n\nConfiguring JBoss to run Liferay DXP includes these things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Setting environment variables\n\\item\n  Setting properties and descriptors\n\\item\n  Removing unnecessary configurations\n\\end{itemize}\n\nOptionally, you can configure JBoss to manage Liferay DXP's data source\nand mail session.\n\nStart with configuring JBoss to run Liferay DXP.\n\nMake the following modifications to\n\\texttt{\\$JBOSS\\_HOME/standalone/configuration/standalone.xml}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Locate the closing \\texttt{\\textless{}/extensions\\textgreater{}} tag.\n  Directly beneath that tag, insert these system properties:\n\n\\begin{verbatim}\n<system-properties>\n    <property name=\"org.apache.catalina.connector.URI_ENCODING\" value=\"UTF-8\" />\n    <property name=\"org.apache.catalina.connector.USE_BODY_ENCODING_FOR_QUERY_STRING\" value=\"true\" />\n</system-properties>\n\\end{verbatim}\n\\item\n  Add the following \\texttt{\\textless{}filter-spec\\textgreater{}} tag\n  within the \\texttt{\\textless{}console-handler\\textgreater{}} tag,\n  directly below the\n  \\texttt{\\textless{}level\\ name=\"INFO\"/\\textgreater{}} tag:\n\n\\begin{verbatim}\n<filter-spec value=\"not(any(match(&quot;WFLYSRV0059&quot;),match(&quot;WFLYEE0007&quot;)))\" />\n\\end{verbatim}\n\\item\n  Add a timeout for the deployment scanner by setting\n  \\texttt{deployment-timeout=\"600\"} as seen in the excerpt below.\n\n\\begin{verbatim}\n<subsystem xmlns=\"urn:jboss:domain:deployment-scanner:2.0\">\n    <deployment-scanner deployment-timeout=\"600\" path=\"deployments\" relative-to=\"jboss.server.base.dir\" scan-interval=\"5000\"/>\n</subsystem>\n\\end{verbatim}\n\\item\n  Add the following JAAS security domain to the security subsystem\n  \\texttt{\\textless{}security-domains\\textgreater{}} defined in element\n  \\texttt{\\textless{}subsystem\\ xmlns=\"urn:jboss:domain:security:2.0\"\\textgreater{}}.\n\n\\begin{verbatim}\n<security-domain name=\"PortalRealm\">\n    <authentication>\n        <login-module code=\"com.liferay.portal.security.jaas.PortalLoginModule\" flag=\"required\" />\n    </authentication>\n</security-domain>\n\\end{verbatim}\n\\item\n  Remove the welcome content code snippets:\n\n\\begin{verbatim}\n<location name=\"/\" handler=\"welcome-content\"/>\n\\end{verbatim}\n\n  and\n\n\\begin{verbatim}\n<handlers>\n    <file name=\"welcome-content\" path=\"${jboss.home.dir}/welcome-content\"/>\n</handlers>\n\\end{verbatim}\n\\item\n  Find the \\texttt{\\textless{}jsp-config/\\textgreater{}} tag and set the\n  \\texttt{development}, \\texttt{source-vm}, and \\texttt{target-vm}\n  attributes in the tag. Once finished, the tag should look like this:\n\n\\begin{verbatim}\n<jsp-config development=\"true\" source-vm=\"1.8\" target-vm=\"1.8\"/>\n\\end{verbatim}\n\\end{enumerate}\n\n\\textbf{Checkpoint:}\n\nBefore continuing, verify the following properties have been set in the\n\\texttt{standalone.xml} file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The new \\texttt{\\textless{}system-property\\textgreater{}} is added.\n\\item\n  The new \\texttt{\\textless{}filter-spec\\textgreater{}} is added.\n\\item\n  The \\texttt{\\textless{}deployment-timeout\\textgreater{}} is set to\n  \\texttt{360}.\n\\item\n  The new \\texttt{\\textless{}security-domain\\textgreater{}} is created.\n\\item\n  Welcome content is removed.\n\\item\n  The \\texttt{\\textless{}jsp-config\\textgreater{}} tag contains its new\n  attributes.\n\\end{enumerate}\n\nNow you should configure your JVM and startup scripts.\n\nIn the \\texttt{\\$WILDFLY\\_HOME/bin/} folder, modify your standalone\ndomain's configuration script file \\texttt{standalone.conf}\n(\\texttt{standalone.conf.bat} on Windows):\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Set the file encoding to \\texttt{UTF-8}\n\\item\n  Set the user time zone to \\texttt{GMT}\n\\item\n  Set the preferred protocol stack\n\\item\n  Increase the default amount of memory available.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} For Liferay DXP to work properly, the application\nserver JVM must use the \\texttt{GMT} time zone and \\texttt{UTF-8} file\nencoding.\n\n\\noindent\\hrulefill\n\nMake the following edits as applicable to your operating system:\n\n\\textbf{Windows}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Comment out the initial \\texttt{JAVA\\_OPTS} assignment as demonstrated\n  in the following line:\n\n\\begin{verbatim}\nrem set \"JAVA_OPTS=-Xms1G -Xmx1G -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=2560m\"\n\\end{verbatim}\n\\item\n  Add the following \\texttt{JAVA\\_OPTS} assignment one line above the\n  \\texttt{:JAVA\\_OPTS\\_SET} line found at end of the file:\n\n\\begin{verbatim}\nset \"JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Djboss.as.management.blocking.timeout=480 -Duser.timezone=GMT -Xms2560m -Xmx2560m -XX:MaxMetaspaceSize=768m\"\n\\end{verbatim}\n\\end{enumerate}\n\n\\textbf{Unix}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Below the \\texttt{if\\ {[}\\ \"x\\$JAVA\\_OPTS\"\\ =\\ \"x\"\\ {]};} statement,\n  replace this \\texttt{JAVA\\_OPTS} statement:\n\n\\begin{verbatim}\nJAVA_OPTS=\"-Xms1303m -Xmx1303m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=2560m -Djava.net.preferIPv4Stack=true\"\n\\end{verbatim}\n\n  with this:\n\n\\begin{verbatim}\nJAVA_OPTS=\"-Djava.net.preferIPv4Stack=true\"\n\\end{verbatim}\n\\item\n  Add the following statement to the bottom of the file:\n\n\\begin{verbatim}\nJAVA_OPTS=\"$JAVA_OPTS -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Djboss.as.management.blocking.timeout=480 -Duser.timezone=GMT -Xms2560m -Xmx2560m -XX:MaxMetaspaceSize=512m\"\n\\end{verbatim}\n\\end{enumerate}\n\nOn JDK 11, it's recommended to add this JVM argument to display\nfour-digit years.\n\n\\begin{verbatim}\n-Djava.locale.providers=JRE,COMPAT,CLDR\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you plan on using the IBM JDK with your JBoss server,\nyou must complete some additional steps. First, navigate to the\n\\texttt{\\$JBOSS\\_HOME/modules/com/liferay/portal/main/module.xml} file\nand insert the following dependency within the\n\\texttt{\\textless{}dependencies\\textgreater{}} element:\n\n\\begin{verbatim}\n <module name=\"ibm.jdk\" />\n\\end{verbatim}\n\nThen navigate to the\n\\texttt{\\$JBOSS\\_HOME/modules/system/layers/base/sun/jdk/main/module.xml}\nfile and insert the following path names inside the\n\\texttt{\\textless{}paths\\textgreater{}...\\textless{}/paths\\textgreater{}}\nelement:\n\n\\begin{verbatim}\n <path name=\"com/sun/crypto\" />\n <path name=\"com/sun/crypto/provider\" />\n <path name=\"com/sun/image/codec/jpeg\" />\n <path name=\"com/sun/org/apache/xml/internal/resolver\" />\n <path name=\"com/sun/org/apache/xml/internal/resolver/tools\" />\n\\end{verbatim}\n\nThe added paths resolve issues with portal deployment exceptions and\nimage uploading problems.\n\n\\noindent\\hrulefill\n\n\\textbf{Checkpoint:}\n\nAt this point, you've finished configuring the application server's JVM\nsettings.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The file encoding, user time-zone, preferred protocol stack have been\n  set in the \\texttt{JAVA\\_OPTS} in the \\texttt{standalone.conf.bat}\n  file.\n\\item\n  The default amount of memory available has been increased.\n\\end{enumerate}\n\nThe prescribed script modifications are now complete for your Liferay\nDXP installation on JBoss. Next you'll configure the database and mail.\n\n\\section{Database Configuration}\\label{database-configuration-2}\n\nThe easiest way to handle your database configuration is to let Liferay\nDXP manage your data source. The\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install\\#using-the-built-in-data-source}{Basic\nConfiguration} page lets you configure Liferay DXP's built-in data\nsource. If you want to use the built-in data source, skip this section.\n\nThis section demonstrates configuring a MySQL database. If you're using\na different database, modify the data source and driver snippets as\nnecessary.\n\nIf using JBoss to manage the data source, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the data source inside the\n  \\texttt{\\$JBOSS\\_HOME/standalone/configuration/standalone.xml} file's\n  the \\texttt{\\textless{}datasources\\textgreater{}} element.\n\n\\begin{verbatim}\n<datasource jndi-name=\"java:jboss/datasources/ExampleDS\" pool-name=\"ExampleDS\" enabled=\"true\" jta=\"true\" use-java-context=\"true\" use-ccm=\"true\">\n    <connection-url>[place the URL to your database here]</connection-url>\n    <driver>[place the driver name here]</driver>\n    <security>\n        <user-name>[place your user name here]</user-name>\n        <password>[place your password here]</password>\n    </security>\n</datasource>\n\\end{verbatim}\n\n  Make sure to replace the database URL, user name, and password with\n  the appropriate values.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** If the data source `jndi-name` must be changed, edit the `datasource` element in the `<default-bindings>` tag.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Add your driver to the \\texttt{standalone.xml} file's\n  \\texttt{\\textless{}drivers\\textgreater{}} element also found within\n  the \\texttt{\\textless{}datasources\\textgreater{}} element.\n\n\\begin{verbatim}\n<drivers>\n    <driver name=\"[name of driver must match name above]\" module=\"com.liferay.portal\">\n        <driver-class>[place your JDBC driver class here]</driver-class>\n    </driver>\n</drivers>\n\\end{verbatim}\n\n  A final data sources subsystem that uses MySQL should look like this:\n\n\\begin{verbatim}\n<subsystem xmlns=\"urn:jboss:domain:datasources:5.0\">\n    <datasources>\n        <datasource jndi-name=\"java:jboss/datasources/ExampleDS\" pool-name=\"ExampleDS\" enabled=\"true\" jta=\"true\" use-java-context=\"true\" use-ccm=\"true\">\n            <connection-url>jdbc:mysql://localhost/lportal</connection-url>\n            <driver>mysql</driver>\n            <security>\n                <user-name>root</user-name>\n                <password>root</password>\n            </security>\n        </datasource>\n        <drivers>\n            <driver name=\"mysql\" module=\"com.liferay.portal\"/>\n        </drivers>\n    </datasources>\n</subsystem>\n\\end{verbatim}\n\\item\n  In a\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}}\n  file in your Liferay Home, specify your data source:\n\n\\begin{verbatim}\njdbc.default.jndi.name=java:jboss/datasources/ExampleDS\n\\end{verbatim}\n\\end{enumerate}\n\nNow that you've configured your data source, the mail session is next.\n\n\\section{Mail Configuration}\\label{mail-configuration-2}\n\nAs with database configuration, the easiest way to configure mail is to\nlet Liferay DXP handle your mail session. If you want to use @product@'s\nbuilt-in mail session, skip this section and\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-mail}{configure\nthe mail session} in the Control Panel.\n\nIf you want to manage your mail session with JBoss, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Specify your mail subsystem in the\n  \\texttt{\\$JBOSS\\_HOME/standalone/configuration/standalone.xml} file\n  like this:\n\n\\begin{verbatim}\n<subsystem xmlns=\"urn:jboss:domain:mail:3.0\">\n    <mail-session jndi-name=\"java:jboss/mail/MailSession\" >\n        <smtp-server ssl=\"true\" outbound-socket-binding-ref=\"mail-smtp\">\n            <login username=\"[place user name here]\" password=\"[place password here]\"/>\n        </smtp-server>\n   </mail-session>\n</subsystem>\n...\n<socket-binding-group name=\"standard-sockets\" default-interface=\"public\" port-offset=\"${jboss.socket.binding.port-offset:0}\">\n...\n<outbound-socket-binding name=\"mail-smtp\">\n        <remote-destination host=\"[place SMTP mail host here]\" port=\"[place mail port here]\"/>\n    </outbound-socket-binding>\n</socket-binding-group>\n\\end{verbatim}\n\\item\n  In your \\texttt{portal-ext.properties} file in Liferay Home, reference\n  your mail session:\n\n\\begin{verbatim}\nmail.session.jndi.name=java:jboss/mail/MailSession\n\\end{verbatim}\n\\end{enumerate}\n\nYou've got mail! Next, you'll deploy Liferay DXP to your JBoss app\nserver.\n\n\\section{Deploy Liferay}\\label{deploy-liferay}\n\nNow you're ready to deploy Liferay DXP using the @product@ WAR file.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If the folder \\texttt{\\$JBOSS\\_HOME/standalone/deployments/ROOT.war}\n  already exists in your JBoss installation, delete all of its\n  subfolders and files. Otherwise, create a new folder called\n  \\texttt{\\$JBOSS\\_HOME/standalone/deployments/ROOT.war}.\n\\item\n  Unzip the Liferay DXP \\texttt{.war} file into the \\texttt{ROOT.war}\n  folder.\n\\item\n  To trigger deployment of \\texttt{ROOT.war}, create an empty file named\n  \\texttt{ROOT.war.dodeploy} in your\n  \\texttt{\\$JBOSS\\_HOME/standalone/deployments/} folder. On startup,\n  JBoss detects this file and deploys it as a web application.\n\\item\n  Start the JBoss application server by navigating to\n  \\texttt{\\$JBOSS\\_HOME/bin} and running \\texttt{standalone.bat} or\n  \\texttt{standalone.sh}.\n\\end{enumerate}\n\nCongratulations; you've now deployed Liferay DXP on JBoss!\n\n\\noindent\\hrulefill\n\nAfter deploying Liferay DXP, you may see excessive warnings and log\nmessages, such as the ones below, involving \\texttt{PhaseOptimizer}.\nThese are benign and can be ignored. Make sure to adjust your app\nserver's logging level or log filters to avoid excessive benign log\nmessages.\n\n\\begin{verbatim}\n May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n WARNING: Skipping pass gatherExternProperties\n May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n WARNING: Skipping pass checkControlFlow\n May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n INFO: pass supports: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, modules, exponent operator (**), async function, trailing comma in param list]\n current AST contains: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, exponent operator (**), async function, trailing comma in param list, object literals with spread, object pattern rest]\n\\end{verbatim}\n\n\\chapter{Installing Liferay DXP on WebLogic 12c\nR2}\\label{installing-liferay-dxp-on-weblogic-12c-r2}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAlthough you can install Liferay DXP in a WebLogic Admin Server, this\nisn't recommended. It's a best practice to install web apps, including\nLiferay DXP, in a WebLogic Managed server. Deploying to a Managed Server\nlets you start or shut down Liferay DXP more quickly and facilitates\ntransitioning into a cluster configuration. This article therefore\nfocuses on installing Liferay DXP in a Managed Server.\n\nBefore getting started, create your Admin and Managed Servers. See\n\\href{http://www.oracle.com/technetwork/middleware/weblogic/documentation/index.html}{WebLogic's\ndocumentation} for instructions on setting up and configuring Admin and\nManaged Servers.\n\nAlso familiarize yourself with\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install}{preparing\nfor install}.\n\nNow,\n\\href{/docs/7-2/deploy/-/knowledge_base/d/obtaining-product\\#downloading-the-liferay-war-and-dependency-jars}{download\nthe Liferay DXP WAR and Dependency JARs}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay DXP WAR file\n\\item\n  Dependencies ZIP file\n\\item\n  OSGi Dependencies ZIP file\n\\end{itemize}\n\n\\section{Configuring WebLogic's Node\nManager}\\label{configuring-weblogics-node-manager}\n\nWebLogic's Node Manager starts and stops managed servers.\n\nIf you're running WebLogic on a UNIX system other than Solaris or Linux,\nuse the Java Node Manager, instead of the native version of the Node\nManager, by configuring these Node Manager properties in the\n\\texttt{domains/your\\_domain\\_name/nodemanager/nodemanager.properties}\nfile:\n\n\\begin{verbatim}\nNativeVersionEnabled=false\n\nStartScriptEnabled=true\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} By default, SSL is used with Node Manager. If you want to\ndisable SSL during development, for example, set\n\\texttt{SecureListener=false} in your \\texttt{nodemanager.properties}\nfile.\n\n\\noindent\\hrulefill\n\nSee Oracle's\n\\href{https://docs.oracle.com/middleware/1212/wls/NODEM/java_nodemgr.htm\\#NODEM173}{Configuring\nJava Node Manager} documentation for details.\n\n\\section{Configuring WebLogic}\\label{configuring-weblogic}\n\nNext, you must set some variables in two WebLogic startup scripts. These\nvariables and scripts are as follows. Be sure to use \\texttt{set}\ninstead of \\texttt{export} if you're on Windows.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\texttt{your-domain/startWebLogic.{[}cmd\\textbar{}sh{]}}: This is the\n  Admin Server's startup script.\n\\item\n  \\texttt{your-domain/bin/startWebLogic.{[}cmd\\textbar{}sh{]}}: This is\n  the startup script for Managed Servers.\n\n  Add the following variables to both\n  \\texttt{startWebLogic.{[}cmd\\textbar{}sh{]}} scripts:\n\n\\begin{verbatim}\nexport DERBY_FLAG=\"false\"\nexport JAVA_OPTIONS=\"${JAVA_OPTIONS} -Dfile.encoding=UTF-8 -Duser.timezone=GMT -da:org.apache.lucene... -da:org.aspectj...\"\nexport MW_HOME=\"/your/weblogic/directory\"\nexport USER_MEM_ARGS=\"-Xmx2560m -Xms2560m\"\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Important:** For Liferay DXP to work properly, the application server JVM\n must use the `GMT` time zone and `UTF-8` file encoding.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nThe `DERBY_FLAG` setting disables the Derby server built in to WebLogic, as\nLiferay DXP doesn't require this server. The remaining settings support\nLiferay DXP's  memory requirements, UTF-8 requirement, Lucene usage, and\nAspect Oriented  Programming via AspectJ. Also make sure to set `MW_HOME` to\nthe directory  containing your WebLogic server on your machine. For example:\n\n```bash\nexport MW_HOME=\"/Users/ray/Oracle/wls12210\"\n```\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Some of the settings are also found in the\n  \\texttt{your-domain/bin/SetDomainEnv.{[}cmd\\textbar{}sh{]}} . Add the\n  following variables (Windows):\n\n\\begin{verbatim}\nset WLS_MEM_ARGS_64BIT=-Xms2560m -Xmx2560m\nset WLS_MEM_ARGS_32BIT=-Xms2560m -Xmx2560m\n\\end{verbatim}\n\n  or on Mac or Linux:\n\n\\begin{verbatim}\nWLS_MEM_ARGS_64BIT=\"-Xms2560m -Xmx2560m\"\nexport WLS_MEM_ARGS_64BIT\n\nWLS_MEM_ARGS_32BIT=\"-Xms2560m -Xmx2560m\"\nexport WLS_MEM_ARGS_32BIT\n\\end{verbatim}\n\\item\n  Set the Java file encoding to UTF-8 in\n  \\texttt{your-domain/bin/SetDomainEnv.{[}cmd\\textbar{}sh{]}} by\n  appending \\texttt{-Dfile.encoding=UTF-8} ahead of your other Java\n  properties:\n\n\\begin{verbatim}\nJAVA_PROPERTIES=\"-Dfile.encoding=UTF-8 ${JAVA_PROPERTIES} ${CLUSTER_PROPERTIES}\"\n\\end{verbatim}\n\\item\n  You must also ensure that the Node Manager sets Liferay DXP's memory\n  requirements when starting the Managed Server. In the Admin Server's\n  console UI, navigate to the Managed Server you want to deploy Liferay\n  DXP to and select the \\emph{Server Start} tab. Enter the following\n  parameters into the \\emph{Arguments} field:\n\n\\begin{verbatim}\n-Xmx2560m -Xms2560m -XX:MaxMetaspaceSize=512m\n\\end{verbatim}\n\n  Click \\emph{Save} when you're finished.\n\\end{enumerate}\n\nNext, you'll set some Liferay DXP-specific properties for your @product@\ninstallation.\n\n\\section{Setting Liferay DXP\nProperties}\\label{setting-liferay-dxp-properties}\n\nBefore installing Liferay DXP, you must set the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{\\emph{Liferay\nHome}} folder's location via the \\texttt{liferay.home} property in a\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}}\nfile. You can also use this file to override\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html}{other\nLiferay DXP properties} that you may need.\n\nFirst, decide which folder you want to serve as Liferay Home. In\nWebLogic, your domain's folder is generally Liferay Home, but you can\nchoose any folder on your machine. Then create your\n\\texttt{portal-ext.properties} file and add the \\texttt{liferay.home}\nproperty:\n\n\\begin{verbatim}\nliferay.home=/full/path/to/your/liferay/home/folder\n\\end{verbatim}\n\nRemember to change this file path to the location on your machine that\nyou want to serve as Liferay Home.\n\nNow that you've created your \\texttt{portal-ext.properties} file, you\nmust put it inside the Liferay DXP WAR file. Expand the @product@ WAR\nfile and place \\texttt{portal-ext.properties} in the\n\\texttt{WEB-INF/classes} folder. Later, you can deploy the expanded\narchive to WebLogic. Alternatively, you can re-WAR the expanded archive\nfor later deployment. In either case, Liferay DXP reads your property\nsettings once it starts up.\n\nIf you need to make any changes to \\texttt{portal-ext.properties} after\nLiferay DXP deploys, you can find it in your domain's\n\\texttt{autodeploy/ROOT/WEB-INF/classes} folder. Note that the\n\\texttt{autodeploy/ROOT} folder contains the Liferay DXP deployment.\n\nNext, you'll install Liferay DXP's dependencies.\n\n\\section{Installing Liferay DXP\nDependencies}\\label{installing-liferay-dxp-dependencies}\n\nYou must now install Liferay DXP's dependencies. Recall that earlier you\ndownloaded two ZIP files containing these dependencies. Install their\ncontents now:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Unzip the Dependencies ZIP file and place its contents in your\n  WebLogic domain's \\texttt{lib} folder.\n\\item\n  Unzip the OSGi Dependencies ZIP file and place its contents in the\n  \\texttt{Liferay\\_Home/osgi} folder (create this folder if it doesn't\n  exist).\n\\end{enumerate}\n\nYou must also add your database's driver JAR file to your domain's\n\\texttt{lib} folder.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Although Hypersonic is fine for testing purposes,\n\\textbf{do not} use it for production Liferay DXP instances.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The\n\\href{https://web.liferay.com/documents/14/21598941/Liferay+DXP+7.2+Compatibility+Matrix/b6e0f064-db31-49b4-8317-a29d1d76abf7?}{Liferay\nDXP Compatibility Matrix} specifies supported databases and\nenvironments.\n\n\\noindent\\hrulefill\n\nNext, you'll configure your database.\n\n\\section{Database Configuration}\\label{database-configuration-3}\n\nUse the following procedure if you want WebLogic to manage your\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install\\#using-the-built-in-data-source}{database}\nfor Liferay DXP. You can skip this section if you want to use\n@product@'s built-in Hypersonic database.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Log in to your AdminServer console.\n\\item\n  In the \\emph{Domain Structure} tree, find your domain and navigate to\n  \\emph{Services} → \\emph{JDBC} → \\emph{Data Sources}.\n\\item\n  To create a new data source, click \\emph{New}. Fill in the \\emph{Name}\n  field with \\texttt{Liferay\\ Data\\ Source} and the \\emph{JNDI Name}\n  field with \\texttt{jdbc/LiferayPool}. Select your database type and\n  driver. For example, MySQL is \\emph{MySQL's Driver (Type 4)\n  Versions:using com.mysql.cj.jdbc.Driver}. Click \\emph{Next} to\n  continue.\n\\item\n  Accept the default settings on this page and click \\emph{Next} to move\n  on.\n\\item\n  Fill in your database information for your MySQL database.\n\\item\n  If using MySQL, add the text\n  \\texttt{?useUnicode=true\\&characterEncoding=UTF-8\\&\\textbackslash{}useFastDateParsing=false}\n  to the URL line and test the connection.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Tip:** For more example URLs, see the `jdbc.default.url` values in [Database Templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates).\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nIf the connection works, click *Next*.\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{6}\n\\item\n  Select the target for the data source and click \\emph{Finish}.\n\\item\n  You must now tell Liferay DXP about the JDBC data source. Create a\n  \\texttt{portal-ext.propreties} file in your Liferay Home directory,\n  and add the line:\n\n\\begin{verbatim}\njdbc.default.jndi.name=jdbc/LiferayPool\n\\end{verbatim}\n\\end{enumerate}\n\nAlternatively, you can make the above configuration strictly via\nproperties in the \\texttt{portal-ext.properties} file. Please see the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/database-templates}{Database\nTemplates} for example properties.\n\nNext, you'll configure your mail session.\n\n\\section{Mail Configuration}\\label{mail-configuration-3}\n\nIf you want WebLogic to manage your\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-mail}{mail\nsession}, use the following procedure. If you want to use Liferay's\nbuilt-in mail session (recommended), skip this section.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Start WebLogic and log in to your Admin Server's console.\n\\item\n  Select \\emph{Services} → \\emph{Mail Sessions} from the \\emph{Domain\n  Structure} box on the left hand side of your Admin Server's console\n  UI.\n\\item\n  Click \\emph{New} to begin creating a new mail session.\n\\item\n  Name the session \\emph{LiferayMail} and give it the JNDI name\n  \\texttt{mail/MailSession}. Then fill out the \\emph{Session Username},\n  \\emph{Session Password}, \\emph{Confirm Session Password}, and\n  \\emph{JavaMail Properties} fields as necessary for your mail server.\n  See the\n  \\href{http://docs.oracle.com/middleware/1221/wls/FMWCH/pagehelp/Mailcreatemailsessiontitle.html}{WebLogic\n  documentation} for more information on these fields. Click \\emph{Next}\n  when you're done.\n\\item\n  Choose the Managed Server that you'll install Liferay DXP on, and\n  click \\emph{Finish}. Then shut down your Managed and Admin Servers.\n\\item\n  With your Managed and Admin servers shut down, add the following\n  property to your \\texttt{portal-ext.properties} file in Liferay Home:\n\n\\begin{verbatim}\nmail.session.jndi.name=mail/MailSession\n\\end{verbatim}\n\\end{enumerate}\n\nLiferay DXP references your WebLogic mail session via this property\nsetting. If you've already deployed Liferay DXP, you can find your\n\\texttt{portal-ext.properties} file in your domain's\n\\texttt{autodeploy/ROOT/WEB-INF/classes} folder.\n\nYour changes take effect upon restarting your Managed and Admin servers.\n\n\\section{Deploying Liferay DXP}\\label{deploying-liferay-dxp-3}\n\nAs mentioned earlier, although you can deploy Liferay DXP to a WebLogic\nAdmin Server, you should instead deploy it to a WebLogic Managed Server.\nDedicating the Admin Server to managing other servers that run your apps\nis a best practice.\n\nFollow these steps to deploy Liferay DXP to a Managed Server:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Make sure the Managed Server you want to deploy Liferay DXP to is shut\n  down.\n\\item\n  In your Admin Server's console UI, select \\emph{Deployments} from the\n  \\emph{Domain Structure} box on the left hand side. Then click\n  \\emph{Install} to start a new deployment.\n\\item\n  Select the Liferay DXP WAR file or its expanded contents on your file\n  system. Alternatively, you can upload the WAR file by clicking the\n  \\emph{Upload your file(s)} link. Click \\emph{Next}.\n\\item\n  Select \\emph{Install this deployment as an application} and click\n  \\emph{Next}.\n\\item\n  Select the Managed Server you want to deploy Liferay DXP to and click\n  \\emph{Next}.\n\\item\n  If the default name is appropriate for your installation, keep it.\n  Otherwise, give it a name of your choosing and click \\emph{Next}.\n\\item\n  Click \\emph{Finish}. After the deployment finishes, click \\emph{Save}\n  if you want to save the configuration.\n\\item\n  Start the Managed Server where you deployed Liferay DXP. @product@\n  precompiles all the JSPs and then launches.\n\\end{enumerate}\n\nNice work! Now you're running Liferay DXP on WebLogic.\n\n\\noindent\\hrulefill\n\nAfter deploying Liferay DXP, you may see excessive warnings and log\nmessages, such as the ones below, involving \\texttt{PhaseOptimizer}.\nThese are benign and can be ignored. Make sure to adjust your app\nserver's logging level or log filters to avoid excessive benign log\nmessages.\n\n\\begin{verbatim}\n May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n WARNING: Skipping pass gatherExternProperties\n May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n WARNING: Skipping pass checkControlFlow\n May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n INFO: pass supports: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, modules, exponent operator (**), async function, trailing comma in param list]\n current AST contains: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, exponent operator (**), async function, trailing comma in param list, object literals with spread, object pattern rest]\n\\end{verbatim}\n\n\\chapter{Installing Liferay DXP on\nWebSphere}\\label{installing-liferay-dxp-on-websphere}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nIBM ® WebSphere ® is a trademark of International Business Machines\nCorporation, registered in many jurisdictions worldwide.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} Throughout this installation and configuration process,\nWebSphere prompts you to click \\emph{Save} to apply changes to the\nMaster Configuration. Do \\textbar{} so intermittently to save your\nchanges.\n\n\\noindent\\hrulefill\n\nFor Liferay DXP to work correctly, WebSphere 9 (Fix Pack 11 is the\nlatest) must be installed. You can find more information about this fix\npack\n\\href{http://www-01.ibm.com/support/docview.wss?uid=swg24043005}{here}.\n\nPlease also note that Liferay DXP doesn't support the WebSphere\nApplication Liberty Profile.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Before installing Liferay DXP, familiarize yourself\nwith\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install}{preparing\nfor install}.\n\n\\noindent\\hrulefill\n\nNow,\n\\href{/docs/7-2/deploy/-/knowledge_base/d/obtaining-product\\#downloading-the-liferay-war-and-dependency-jars}{download\nthe Liferay DXP WAR and Dependency JARs}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay DXP WAR file\n\\item\n  Dependencies ZIP file\n\\item\n  OSGi Dependencies ZIP file\n\\end{itemize}\n\nNote that the\n\\href{docs/7-2/deploy/-/knowledge_base/d/liferay-home}{\\emph{Liferay\nHome} folder} is important to the operation of Liferay DXP. In Liferay\nHome, @product@ creates certain files and folders that it needs to run.\nOn WebSphere, Liferay Home is typically\n\\texttt{{[}Install\\ Location{]}/WebSphere/AppServer/profiles/your-profile/liferay}.\n\nWithout any further ado, get ready to install Liferay DXP in WebSphere!\n\n\\section{Preparing WebSphere for Liferay\nDXP}\\label{preparing-websphere-for-liferay-dxp}\n\nWhen the application server binaries have been installed, start the\n\\emph{Profile Management Tool} to create a profile appropriate for\nLiferay DXP.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Create\\ldots{}}, choose \\emph{Application Server}, and\n  then click \\emph{Next}.\n\\item\n  Click the \\emph{Advanced} profile creation option and then click\n  \\emph{Next}. You need the advanced profile to specify your own values\n  for settings such as the location of the profile and names of the\n  profile, node and host, to assign your own ports, or to optionally\n  choose whether to deploy the administrative console and sample\n  application and also add web-server definitions for IBM HTTP Server.\n  See the WebSphere documentation for more information about these\n  options.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/websphere-01-profile.png}}\n  \\caption{Choose the Advanced profile option to specify your own\n  settings.}\n  \\end{figure}\n\\item\n  Check the box \\emph{Deploy the administrative console}. This gives you\n  a web-based UI for working with your application server. Skip the\n  default applications. You'd only install these on a development\n  machine. Click \\emph{Next}.\n\\item\n  Set the profile name and location. Ensure you specify a performance\n  tuning setting other than \\emph{Development}, since you're installing\n  a production server. See the WebSphere documentation for more\n  information about performance tuning settings. Click \\emph{Next}.\n\\item\n  Choose node, server, and host names for your server. These are\n  specific to your environment. Click \\emph{Next}.\n\\item\n  Administrative security in WebSphere is a way to restrict who has\n  access to the administrative tools. You may want to have it enabled in\n  your environment so that a user name and password are required to\n  administer the WebSphere server. See WebSphere's documentation for\n  more information. Click \\emph{Next}.\n\\item\n  Each profile needs a security certificate, which comes next in the\n  wizard. If you don't have certificates already, choose the option to\n  generate a personal certificate and a signing certificate and click\n  \\emph{Next}.\n\\item\n  Once the certificates are generated, set a password for your keystore.\n  Click \\emph{Next}.\n\\item\n  Now you can customize the ports this server profile uses. Be sure to\n  choose ports that are open on your machine. When choosing ports, the\n  wizard detects existing WebSphere installations and if it finds\n  activity, it increments ports by one.\n\\item\n  Choose whether to start this profile when the machine starts. Click\n  \\emph{Next}.\n\\item\n  WebSphere ships with IBM HTTP Server, which is a re-branded version of\n  Apache. Choose whether you want a web server definition, so that this\n  JVM receives requests forwarded from the HTTP server. See WebSphere's\n  documentation for details on this. When finished, click \\emph{Next}.\n\\item\n  The wizard then shows you a summary of what you selected, enabling you\n  to keep your choices or go back and change something. When you're\n  satisfied, click \\emph{Next}.\n\\end{enumerate}\n\nWebSphere then creates your profile and finishes with a message telling\nyou the profile was created successfully. Awesome! Your profile is\ncomplete. Now there are a few things you need to configure in your\napplication server.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/websphere-02-profile.png}}\n\\caption{Example of the settings before creating the profile.}\n\\end{figure}\n\n\\section{Configuring the WebSphere Application\nServer}\\label{configuring-the-websphere-application-server}\n\nIn this version of WebSphere, servlet filters are not initialized on web\napplication startup, but rather, on first access. This can cause\nproblems when deploying certain apps to Liferay DXP. To configure\nservlet filters to initialize on application startup (i.e., deployment),\nset the following \\texttt{webcontainer} properties in your WebSphere\napplication server:\n\n\\begin{verbatim}\ncom.ibm.ws.webcontainer.initFilterBeforeInitServlet = true\ncom.ibm.ws.webcontainer.invokeFilterInitAtStartup = true\n\\end{verbatim}\n\nTo set \\texttt{webcontainer} properties in the WebSphere application\nserver, follow the instructions\n\\href{http://www-01.ibm.com/support/docview.wss?rss=180&uid=swg21284395}{here\nin WebSphere's documentation}.\n\n\\section{Setting up JVM Parameters for Liferay\nDXP}\\label{setting-up-jvm-parameters-for-liferay-dxp}\n\nNext, in the WebSphere profile you created for Liferay DXP, you must set\nan argument that supports Liferay DXP's Java memory requirements. You'll\nmodify this file:\n\n\\begin{verbatim}\n[Install Location]/WebSphere/AppServer/profiles/your-profile/config/cells/your-cell/nodes/your-node/servers/your-server/server.xml\n\\end{verbatim}\n\nAdd \\texttt{maximumHeapSize=\"2560\"} inside the \\texttt{jvmEntries} tag.\nFor example:\n\n\\begin{verbatim}\n<jvmEntries xmi:id=\"JavaVirtualMachine_1183122130078\" ... maximumHeapSize=\"2560\">\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The JVM parameters used here are defaults intended for\ninitial deployment of production systems. Administrators should change\nthe settings to values that best address their specific environments.\nThese must be tuned depending on need.\n\n\\noindent\\hrulefill\n\nAdministrators can set the UTF-8 properties in the\n\\texttt{\\textless{}jvmEntries\\ genericJvmArguments=.../\\textgreater{}}\nattribute in \\texttt{server.xml}. This is required or else special\ncharacters will not be parsed correctly. Set the maximum and minimum\nheap sizes to \\texttt{2560m} there too. Add the following inside the\n\\texttt{jvmEntries} tag:\n\n\\begin{verbatim}\n<jvmEntries xmi:id=\"JavaVirtualMachine_1183122130078\" ...genericJvmArguments=\"-Dfile.encoding=UTF-8 -Duser.timezone=GMT -Xms2560m -Xmx2560m\">\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} For Liferay DXP to work properly, the application\nserver JVM must use the \\texttt{GMT} time zone and \\texttt{UTF-8} file\nencoding.\n\n\\noindent\\hrulefill\n\nAlternately, you can set the UTF-8 properties from the WebSphere Admin\nConsole. (See below.)\n\n\\section{Removing the secureSessionCookie\nTag}\\label{removing-the-securesessioncookie-tag}\n\nIn the same profile, you should delete a problematic\n\\texttt{secureSessionCookie} tag that can cause Liferay DXP startup\nerrors. Note that this is just a default setting; once Liferay DXP is\ninstalled, you should tune it appropriately based on your usage.\n\nIn\n\\texttt{{[}Install\\ Location{]}/WebSphere/AppServer/profiles/your-profile/config/cells/your-cell/cell.xml},\nDelete the \\texttt{secureSessionCookie} tag containing\n\\texttt{xmi:id=\"SecureSessionCookie\\_1\"}.\n\nIf this tag is not removed, an error similar to this may occur:\n\n\\begin{verbatim}\nWSVR0501E: Error creating component com.ibm.ws.runtime.component.CompositionUnitMgrImpl@d74fa901\ncom.ibm.ws.exception.RuntimeWarning: com.ibm.ws.webcontainer.exception.WebAppNotLoadedException: Failed to load webapp: Failed to load webapp: SRVE8111E: The application, LiferayEAR, is trying to modify a cookie which matches a pattern in the restricted programmatic session cookies list [domain=*, name=JSESSIONID, path=/].\n\\end{verbatim}\n\n\\section{Installing Liferay DXP's\nDependencies}\\label{installing-liferay-dxps-dependencies}\n\nYou must now install Liferay DXP's dependencies. Recall that earlier you\ndownloaded two ZIP files containing these dependencies. Install their\ncontents now:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Unzip the Dependencies ZIP file and place its contents in your\n  WebSphere application server's\n  \\texttt{{[}Install\\ Location{]}/WebSphere/AppServer/lib/ext} folder.\n  If you have a JDBC database driver \\texttt{JAR}, copy it to this\n  location as well.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The [Liferay DXP Compatibility Matrix](https://web.liferay.com/documents/14/21598941/Liferay+DXP+7.2+Compatibility+Matrix/b6e0f064-db31-49b4-8317-a29d1d76abf7?) specifies supported databases and environments.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  From the same archive, copy \\texttt{portlet.jar}into\n  \\texttt{{[}Install\\ Location{]}/WebSphere/AppServer/javaext} for\n  WebSphere 9.0.0.x. WebSphere already contains an older version of\n  \\texttt{portlet.jar} which must be overridden at the highest class\n  loader level. The new \\texttt{portlet.jar} (version 3) is\n  backwards-compatible.\n\\item\n  Unzip the OSGi Dependencies ZIP file and place its contents in the\n  \\texttt{{[}Liferay\\ Home{]}/osgi} folder (create this folder if it\n  doesn't exist). This is typically\n  \\texttt{{[}Install\\ Location{]}/WebSphere/AppServer/profiles/your-profile/liferay/osgi}.\n\\end{enumerate}\n\n\\section{Ensuring that Liferay DXP's portlet.jar is loaded\nfirst}\\label{ensuring-that-liferay-dxps-portlet.jar-is-loaded-first}\n\nIn addition to placing the \\texttt{portlet.jar} in the correct folder,\nyou must configure the \\texttt{config.ini} file so that it is loaded\nfirst. Navigate to\n\\texttt{/IBM/WebSphere/AppServer/configuration/config.ini}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Find the property \\texttt{com.ibm.CORBA,com.ibm}\n\\item\n  Insert the property\n  \\texttt{javax.portlet,javax.portlet.filter,javax.portlet.annotations}\n  after \\texttt{com.ibm.CORBA} and before \\texttt{com.ibm}.\n\\item\n  Save the file.\n\\end{enumerate}\n\nOnce you've installed these dependencies and configured the\n\\texttt{config.ini} file, start the server profile you created for\nLiferay DXP. Once it starts, you're ready to configure your database.\n\n\\section{Database Configuration}\\label{database-configuration-4}\n\nIf you want WebSphere to manage the database connections, follow the\ninstructions below. Note this is not necessary if you plan to use\nLiferay DXP's standard database configuration; in that case, skip this\nsection. See the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install\\#using-the-built-in-data-source}{Using\nthe Built-in Data Sources} section for more article.\n\nYou'll set your database information in Liferay DXP's setup wizard after\nthe install.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Although Liferay DXP's embedded database is fine for\ntesting purposes, you \\textbf{should not} use it for production Liferay\nDXP instances.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/websphere-jdbc-providers.png}}\n\\caption{WebSphere JDBC providers}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Start WebSphere.\n\\item\n  Open the Administrative Console and log in.\n\\item\n  Click \\emph{Resources → JDBC Providers}.\n\\item\n  Select a scope and then click \\emph{New}.\n\\item\n  Select your database type, provider type, and implementation type. If\n  you select a predefined database, the wizard fills in the name and\n  description fields for you. If the database you want to use isn't\n  listed, select \\emph{User-defined} from the \\emph{Database type} field\n  and then fill in the \\emph{Implementation Class Name}. For example, if\n  you use MySQL, select \\emph{Database type} → \\emph{User-defined}, and\n  then enter\n  \\texttt{com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource}\n  in \\emph{Implementation Class Name}. Click \\emph{Next} when you are\n  finished.\n\\item\n  Clear any text in the class path settings. You already copied the\n  necessary JARs to a location on the server's class path. Click\n  \\emph{Next}.\n\\item\n  Review your settings and click \\emph{Finish}. The final configuration\n  should look like this:\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/websphere-03.png}}\n  \\caption{Completed JDBC provider configurations.}\n  \\end{figure}\n\\item\n  Click your new provider configuration when it appears in the table,\n  and then click \\emph{Data Sources} under \\emph{Additional Properties}.\n  Click \\emph{New}.\n\\item\n  Enter \\emph{liferaydatabasesource} in the \\emph{Data source name}\n  field and \\texttt{jdbc/LiferayPool} in the \\emph{JNDI name} field.\n  Click \\emph{Next}.\n\\item\n  Click \\emph{Next} in the remaining screens of the wizard to accept the\n  default values. Then review your changes and click \\emph{Finish}.\n\\item\n  Click the data source when it appears in the table and then click\n  \\emph{Custom Properties}. Now click the \\emph{Show Filter Function}\n  button. This is the second from last of the small icons under the\n  \\emph{New} and \\emph{Delete} buttons.\n\\item\n  Type \\emph{user} into the search terms and click \\emph{Go}.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/websphere-database-properties.png}}\n  \\caption{Modifying data source properties in WebSphere}\n  \\end{figure}\n\\item\n  Select the \\emph{user} property and give it the value of the user name\n  to your database. Click \\emph{OK} and save to master configuration.\n\\item\n  Do another filter search for the \\emph{url} property. Give this\n  property a value that points to your database. For example, a MySQL\n  URL would look like this:\n\n\\begin{verbatim}\njdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT&useFastDateParsing=false\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Tip:** For more example URLs, see the `jdbc.default.url` values in\n [Database Templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates).\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nClick *OK* and save to master configuration.\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{14}\n\\item\n  Do another filter search for the \\emph{password} property. Enter the\n  password for the user ID you added earlier as the value for this\n  property. Click \\emph{OK} and save to master configuration.\n\\item\n  Go back to the data source page by clicking it in the breadcrumb\n  trail. Click the \\emph{Test Connection} button. It should connect\n  successfully.\n\\end{enumerate}\n\nOnce you've set up your database, you can set up your mail session.\n\n\\section{Mail Configuration}\\label{mail-configuration-4}\n\nIf you want WebSphere to manage your mail sessions, use the following\nprocedure. If you want to use Liferay DXP's built-in mail sessions, you\ncan skip this section. See the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-mail}{Configuring\nMail} article on how to use Liferay DXP's built-in mail sessions.\n\n\\section{Creating a WebSphere-Managed Mail Session\n(Optional)}\\label{creating-a-websphere-managed-mail-session-optional}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Resources → Mail → Mail Providers}.\n\\item\n  Click the Built-In Mail Provider for your node and server.\n\\item\n  Click \\emph{Mail Sessions} and then click the \\emph{New} button.\n\\item\n  Give your mail session a name of \\emph{liferaymail} and a JNDI name of\n  \\texttt{mail/MailSession}. Fill in the correct information for your\n  mail server in the sections \\emph{Outgoing Mail Properties} and\n  \\emph{Incoming Mail Properties}. Click \\emph{OK} and then save to the\n  master configuration.\n\\item\n  Click your mail session when it appears in the table and select\n  \\emph{Custom Properties} under the \\emph{Additional Properties}\n  section. Set any other JavaMail properties required by your mail\n  server, such as the protocol, ports, whether to use SSL, and so on.\n\\item\n  Click \\emph{Security → Global Security} and de-select \\emph{Use Java 2\n  security to restrict application access to local resources} if it is\n  selected. Click \\emph{Apply}.\n\\end{enumerate}\n\nNote that you may also need to retrieve a SSL certificate from your mail\nserver and add it to WebSphere's trust store. See WebSphere's\ndocumentation for instructions on this.\n\n\\section{Verifying WebSphere Mail\nProvider}\\label{verifying-websphere-mail-provider}\n\nTo validate that the mail session has been configured correctly, there\nare a number of ways to test this once the WAR has been deployed, the\nserver has started, and the user has signed in as the system\nadministrator. One quick way to validate is to create a new user with a\nvalid email account. The newly created user should receive an email\nnotification. The logs should display that the SMTP server has been\npinged with the correct port number listed.\n\n\\section{Enable Cookies for HTTP\nSessions}\\label{enable-cookies-for-http-sessions}\n\nWebSphere restricts cookies to HTTPS sessions by default. If you're\nusing HTTP instead, this prevents users from signing in to Liferay DXP\nand displays the following error in the console:\n\n\\begin{verbatim}\n20:07:14,021 WARN  [WebContainer : 1][SecurityPortletContainerWrapper:341]\nUser 0 is not allowed to access URL http://localhost:9081/web/guest/home and\nportlet com_liferay_login_web_portlet_LoginPortlet\n\\end{verbatim}\n\nThis occurs because Liferay DXP can't use the HTTPS cookie when you use\nHTTP. The end result is that new sessions are created on each page\nrefresh. Follow these steps to resolve this issue in WebSphere:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Application Servers} → \\emph{server1} → \\emph{Session\n  Management} → Enable Cookies\n\\item\n  De-select \\emph{Restrict cookies to HTTPS sessions}\n\\item\n  Click \\emph{Apply}\n\\item\n  Click \\emph{Save}\n\\end{enumerate}\n\n\\section{Enable UTF-8}\\label{enable-utf-8}\n\nIf you did not add the \\texttt{-Dfile.encoding=UTF-8} property in the\n\\texttt{server.xml}, you can do so in the Administrative Console.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Application Servers} → \\emph{server1} → \\emph{Process\n  definition}.\n\\item\n  Click \\emph{Java Virtual Machine} under \\emph{Additional Properties}.\n\\item\n  Enter \\texttt{-Dfile.encoding=UTF-8} in the \\emph{Generic JVM\n  arguments} field.\n\\item\n  Click \\emph{Apply} and then \\emph{Save} to master configuration.\n\\end{enumerate}\n\nOnce the changes have been saved, Liferay DXP can parse special\ncharacters if there is localized content.\n\n\\section{Deploy Liferay DXP}\\label{deploy-liferay-dxp}\n\nNow you're ready to deploy Liferay DXP!\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In WebSphere's administrative console, click \\emph{Applications} →\n  \\emph{New Application} → \\emph{New Enterprise Application}.\n\\item\n  Browse to the Liferay DXP \\texttt{.war} file, select it, and click\n  \\emph{Next}.\n\\item\n  Leave \\emph{Fast Path} selected and click \\emph{Next}. Ensure that\n  \\emph{Distribute Application} has been checked and click \\emph{Next}\n  again.\n\\item\n  Choose the WebSphere runtimes and/or clusters where you want Liferay\n  DXP deployed. Click \\emph{Next}.\n\\item\n  Select the virtual host to deploy Liferay DXP on and click\n  \\emph{Next}.\n\\item\n  Map Liferay DXP to the root context (\\texttt{/}) and click\n  \\emph{Next}.\n\\item\n  Select the \\emph{metadata-complete attribute} setting that you want to\n  use and click \\emph{Next}.\n\\item\n  Ensure that you have made all the correct choices and click\n  \\emph{Finish}. When Liferay DXP has installed, click \\emph{Save to\n  Master Configuration}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/websphere-deploy-dxp.png}}\n\\caption{Review your deployment options before deploying.}\n\\end{figure}\n\nYou've now installed Liferay DXP!\n\n\\section{Setting the JDK Version for Compiling\nJSPs}\\label{setting-the-jdk-version-for-compiling-jsps}\n\nLiferay DXP requires that its JSPs are compiled to the Java 8 bytecode\nformat. To ensure that WebSphere does this, shut down WebSphere after\nyou've deployed the Liferay DXP \\texttt{.war} file. Navigate to the\n\\texttt{WEB\\_INF} folder and add the following setting to the\n\\texttt{ibm-web-ext.xml} or in most cases the \\texttt{ibm-web-ext.xmi}\nfile:\n\n\\begin{verbatim}\n<jsp-attribute name=\"jdkSourceLevel\" value=\"18\" />\n\\end{verbatim}\n\nThe exact path to the \\texttt{ibm-web-ext.xmi} file depends on your\nWebSphere installation location and Liferay DXP version, but here's an\nexample:\n\n\\begin{verbatim}\n/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/config/cells/localhostNode01Cell/applications/liferayXX.ear/deployments/liferayXX/liferayXX.war/WEB-INF/ibm-web-ext.xmi\n\\end{verbatim}\n\nNote that the Liferay DXP \\texttt{.war} comes pre-packaged with the\n\\texttt{ibm-web-ext.xmi} file; this format is functionally the same as\n\\texttt{.xml} and WebSphere recognizes both formats. For more general\ninformation on how WebSphere compiles JSPs see IBM's official\ndocumentation for\n\\href{https://www.ibm.com/support/knowledgecenter/en/SSEQTP_9.0.0/com.ibm.websphere.base.doc/ae/rweb_jspengine.html}{WebSphere\nApplication Server 9.0.0.x}.\n\n\\section{Start Liferay DXP}\\label{start-liferay-dxp}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If you plan to use Liferay DXP's {[}setup\n\n  wizard{]}(/docs/7-2/deploy/-/knowledge\\_base/d/installing-product\\#using-the-setup-wizard),\n  skip to the next step. If you wish to use WebSphere's data source and\n  mail session, create a file called \\texttt{portal-ext.properties} in\n  your Liferay Home folder. Place the following configuration in the\n  file:\n\n\\begin{verbatim}\njdbc.default.jndi.name=jdbc/LiferayPool\nmail.session.jndi.name=mail/MailSession\nsetup.wizard.enabled=false\n\\end{verbatim}\n\\item\n  In the WebSphere administrative console, navigate to \\emph{Enterprise\n  Applications}, select the Liferay DXP application, and click\n  \\emph{Start}. While Liferay DXP is starting, WebSphere displays a\n  spinning graphic.\n\\item\n  In Liferay DXP's setup wizard, select and configure your database\n  type. Click \\emph{Finish} when you're done. Liferay DXP then creates\n  the tables it needs in the database.\n\\end{enumerate}\n\nCongratulations! You've installed Liferay DXP on WebSphere!\n\n\\noindent\\hrulefill\n\nAfter deploying Liferay DXP, you may see excessive warnings and log\nmessages, such as the ones below, involving \\texttt{PhaseOptimizer}.\nThese are benign and can be ignored. Make sure to adjust your app\nserver's logging level or log filters to avoid excessive benign log\nmessages.\n\n\\begin{verbatim}\n May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n WARNING: Skipping pass gatherExternProperties\n May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n WARNING: Skipping pass checkControlFlow\n May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n INFO: pass supports: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, modules, exponent operator (**), async function, trailing comma in param list]\n current AST contains: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, exponent operator (**), async function, trailing comma in param list, object literals with spread, object pattern rest]\n\\end{verbatim}\n\n\\chapter{Activating Liferay DXP}\\label{activating-liferay-dxp}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThere are two ways to activate your Liferay DXP instance:\n\n\\begin{itemize}\n\\item\n  With an XML activation key that you request and receive from Liferay\n  Support.\n\\item\n  Online activation through Liferay Connected Services (LCS). Liferay\n  DXP 7.0 introduced LCS as a way to activate Liferay DXP instances. LCS\n  can also install fix packs, monitor each instance's performance, and\n  help administrators automatically manage Liferay DXP subscriptions.\n  See the\n  \\href{/docs/7-1/deploy/-/knowledge_base/d/managing-liferay-dxp-with-liferay-connected-services}{LCS\n  documentation} for instructions on activating your instances with LCS.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You must use LCS for activation of Elastic subscriptions.\nOtherwise, you don't have to use LCS for activation. You can instead\nrequest an XML activation key from Liferay Support.\n\n\\noindent\\hrulefill\n\n\\chapter{Setting Up Marketplace}\\label{setting-up-marketplace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\n\\href{https://www.liferay.com/marketplace}{Liferay Marketplace} is more\nthan just a store for Liferay applications. Under the hood, it provides\nboth the store and Liferay DXP's application deployment features. For\nthis reason, you must ensure that Marketplace can run and configure\nitself.\n\nHere are some scenarios to work around to ensure Marketplace works\nsuccessfully:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Server is Firewalled without Access to the Internet\n\\item\n  Limited Database Access\n\\end{itemize}\n\nThe firewall scenario is discussed first.\n\n\\section{Server is Firewalled without Access to the\nInternet}\\label{server-is-firewalled-without-access-to-the-internet}\n\nYour server might be behind a firewall that prevents access to the\nInternet. Or your security policy might not allow direct download and\ninstallation from the Internet. In these cases, you have two options:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From an Internet-enabled computer, download the\n  \\href{https://www.liferay.com/marketplace/download}{Marketplace\n  plugin}. Then deploy the plugin (\\texttt{.lpkg} file) by copying it\n  into the \\texttt{deploy} folder in\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}.\n\\item\n  Alternately, once you have the downloaded \\texttt{.lpkg} file, deploy\n  it using the\n  \\href{/docs/7-2/user/-/knowledge_base/u/managing-and-configuring-apps}{App\n  Manager}.\n\\end{enumerate}\n\nNext you'll learn how to work around database access restrictions.\n\n\\section{Limited Database Access}\\label{limited-database-access}\n\nSome production environments do not have the necessary database\npermissions for Liferay DXP, apps, modules, and plugins to maintain\ntheir tables. In these cases:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Grant the Liferay DXP database user temporary full rights to the\n  database.\n\\item\n  Install Liferay DXP and start it so that it populates its database.\n\\item\n  Once the database is created, remove the permissions for creating\n  tables and dropping tables from the Liferay DXP database user.\n\\end{enumerate}\n\nSee the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install\\#limiting-database-access}{database\npreparation instructions} for more information. Note that many\nsophisticated Liferay DXP apps---not just the Marketplace app---require\nnew tables when deployed. If your environment restricts database access,\nyou may need to repeat the above steps whenever you deploy a new app.\n\nYou've prepared Liferay DXP for installing Marketplace and additional\napps.\n\n\\chapter{Trial Plugin Installation}\\label{trial-plugin-installation}\n\n{This document has been updated and ported to \\textless a\nhref=``https://learn.liferay.com/dxp/latest/en/system-administration/installing-and-managing-apps/installing-apps/accessing-ee-plugins-during-a-trial-period.html)\nand is no longer maintained here.}\n\nFor Liferay customers who are evaluating Liferay DXP on a trial basis,\n\\textbf{the plugins can be accessed from within the \\emph{Apps} →\n\\emph{Store} (i.e., Marketplace) section of the Control Panel in your\nproduct installation}.\n\n\\section{Installation Process}\\label{installation-process}\n\nFollow the steps below to install a trial plugin:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Register a \\texttt{liferay.com} account (LRDC) account by visiting\n  Liferay's home page (if necessary). Do this by clicking \\emph{Sign\n  In/Create Account} button from the top right Profile button.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/liferay-com-sign-in.png}}\n  \\caption{Hover over the Profile button and click \\emph{Sign In/Create\n  Account}.}\n  \\end{figure}\n\\item\n  Start your Liferay DXP instance (trial license is OK).\n\\item\n  After signing in as an Admin in your Liferay DXP trial server, go to\n  the Control Panel → \\emph{Apps} → \\emph{Store} and sign in to the\n  Marketplace using your \\texttt{liferay.com} (LRDC) account\n  credentials. Authorize Marketplace to access your local account.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/dxp-store-link.png}}\n  \\caption{Click the \\emph{Store} link and authorize Marketplace to\n  access your local account.}\n  \\end{figure}\n\\item\n  Once signed into the Store, click on the \\emph{Purchased} link, and\n  then click on the \\emph{EE} tab.\n\n  Here you can see a list of Liferay DXP plugins that are installed, as\n  well as options to update or install certain plugins. See\n  \\href{/docs/7-2/user/-/knowledge_base/u/using-the-liferay-marketplace}{Using\n  the Liferay Marketplace} for details.\n\\end{enumerate}\n\nNext are answers to some common questions.\n\n\\section{FAQ}\\label{faq}\n\n\\textbf{Q:} Where are the \\emph{Liferay DXP Trial Plugins}?\n\n\\textbf{A:} There is no such thing. The Liferay DXP plugins in Liferay\nMarketplace are the same ones that you get to try out with your Liferay\nDXP trial license for your portal. The Liferay DXP license (trial or\nofficial @product@ subscriber) gives you access to the Liferay DXP\nplugins. Also, there is no difference code-wise or release-wise between\na Liferay DXP trial installation and a regular @product@ non-trial\ninstallation. The only difference is the license.\n\n\\textbf{Q:} Why can't I go to liferay.com/marketplace? Why can't I\n\\emph{purchase} from the Marketplace site?\n\n\\textbf{A:} DXP trial users must use the Marketplace from within the\nproduct's Control Panel (instructions above). You do not need to\n\\emph{purchase} any DXP plugins because if you access Marketplace from\nwithin the Control Panel, Marketplace sees that you have a DXP license\ninstalled and gives access to DXP plugins. Official DXP subscription\ncustomers (i.e., non trial) can log into \\texttt{liferay.com} with their\ndesignated DXP subscriber login and access all DXP plugins through the\nMarketplace website.\n\n\\textbf{Q:} Why are the plugins under the Purchased tab? If I click on\nthe \\emph{DXP Marketplace} link, it does not let me get the DXP plugins.\n\n\\textbf{A:} Once you're signed into the Store, click on the\n\\emph{Purchased} tab, then click on the \\emph{EE} tab.\n\n\\textbf{Q:} What happens when DXP trial customers become official\nLiferay Digital Experience subscribers?\n\n\\textbf{A:} They can still complete the above process, or they can also\nvisit the \\href{https://www.liferay.com/marketplace}{Liferay Marketplace\nwebsite}.\n\n\\textbf{Q:} Do DXP trial customers get the DXP source code?\n\n\\textbf{A:} No, they can only install the plugin. The DXP source code\nbecomes available once they are official Liferay DXP Enterprise\nsubscribers.\n\n\\textbf{Q:} Can this process of installing DXP plugins be used from\nLiferay Portal CE (Community Edition)?\n\n\\textbf{A:} No, the Marketplace must detect that you are running Liferay\nDXP.\n\n\\chapter{Document Repository\nConfiguration}\\label{document-repository-configuration}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nYou can configure file storage in several ways. Each option is a\n\\emph{store} which can be configured through the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}}\nfile by setting the\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Document\\%20Library\\%20Service}{\\texttt{dl.store.impl=}\nproperty}.\n\nThe default store is called Simple File Store. It stores\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-documents-and-media}{documents\nand media} files on a file system (local or mounted). The store's\ndefault root folder is\n\\texttt{{[}Liferay\\ Home{]}/data/document\\_library}. You can specify a\ndifferent root directory from within\n\\href{/docs/7-2/user/-/knowledge_base/u/system-settings}{System\nSettings}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Access System Settings by opening the \\emph{Menu}\n  (\\pandocbounded{\\includegraphics[keepaspectratio]{./images/icon-menu.png}})\n  and navigating to \\emph{Control Panel → Configuration → System\n  Settings}.\n\\item\n  In the \\emph{Platform} section, click \\emph{File Storage}. The File\n  Storage page appears.\n\\item\n  Click \\emph{Simple File System Store}.\n\\item\n  For the store's \\emph{Root directory} value, specify its absolute path\n  or its path relative to\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}.\n\\item\n  Click the \\emph{Save} button.\n\\end{enumerate}\n\nThe document library store switches immediately to the new folder.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/file-storage.png}}\n\\caption{The File Storage page in System Settings lets you configure\ndocument repository storage.}\n\\end{figure}\n\nYou can also use an entirely different method for storing documents and\nmedia files:\n\n\\textbf{Simple File System Store}: uses the file system (local or a\nmounted share) to store files.\n\n\\textbf{Advanced File System Store}: in addition to using the file\nsystem (local or a mounted share) to store files, Advanced File System\nStore nests the files into folders by version, for faster performance\nand to store more files.\n\n\\textbf{S3 Store (Amazon Simple Storage)}: uses Amazon's cloud-based\nstorage solution.\n\n\\textbf{DBStore (Database Storage)}: stores the files in the database.\nDBStore's file (stored as a blob) size is 1 gigabyte. To store files\nlarger than 1 gigabyte, use Simple File System Store or Advanced File\nSystem Store.\n\nThese articles explain details for each one.\n\n\\chapter{Using the Simple File System\nStore}\\label{using-the-simple-file-system-store}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe simple file storage implementation is the default store. It uses a\nlocal folder to store files. You can use the file system for your\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering}{clustered}\nconfiguration, but the folder you're pointing to must be shared by all\nnodes and handle concurrent requests and file locking. For this reason,\nyou need to use a Storage Area Network or a clustered file system.\n\nThe file system store was the first store used in Liferay DXP and is\nheavily bound to the Liferay DXP database. By default, documents are\nstored in a \\texttt{document\\_library} subfolder of the \\texttt{data}\nfolder. Of course, you can change this path to anything you want in\n\\href{/docs/7-2/user/-/knowledge_base/u/system-settings}{System\nSettings}.\n\nThe Simple File System store uses this folder path format for storing\ndocuments:\n\n\\begin{verbatim}\n/companyId/folderId/numericFileEntryName/versionNumber\n\\end{verbatim}\n\nThe first folder name is the site's company ID. The second folder name\nis the Documents and Media folder's ID where the document resides. The\nthird folder name is the document's numeric file entry name. Finally,\nthe fourth name is a version number used for storing multiple versions\nof the document.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} A document's numeric file entry name is distinct from the\ndocument ID; don't confuse the two! Each has an independent counter. The\nnumeric file entry name is used in the folder path for storing the\ndocument but the document ID is not. The numeric file entry name is in\nthe \\texttt{name} column of the \\texttt{DLFileEntry} table in Liferay\nDXP's database; the document ID is in the \\texttt{fileEntryId} column of\nthe same table.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} If a database transaction rollback occurs in the\nDocument Library, file system changes that have occurred since the start\nof the transaction aren't reversed. Inconsistencies between Document\nLibrary files and those in the file system store can occur and may\nrequire manual synchronization.\n\n\\noindent\\hrulefill\n\nThe Simple File System Store binds documents very closely to Liferay\nDXP, and may not be exactly what you want. If you've been using the\ndefault settings for a while and must migrate your documents, there's a\nmigration utility in the Control Panel in \\emph{Server Administration} →\n\\emph{Data Migration}. The utility facilitates moving documents from one\nstore implementation to another.\n\n\\chapter{Using the Advanced File System\nStore}\\label{using-the-advanced-file-system-store}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe advanced file system store is similar to the simple file system\nstore (the default store). Like that store, it saves files to the local\nfile system---which, of course, could be a remote file system mount. It\nuses a slightly different folder structure to store files, which is\npictured below.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/enterprise-adv-file-system-store.png}}\n\\caption{The advanced file system store creates a more nested folder\nstructure than the file system store.}\n\\end{figure}\n\nSo what makes the advanced file system store \\emph{advanced}? Several\noperating systems have limitations on the number of files that can be\nstored in a particular folder. The advanced file system store overcomes\nthis limitation by programmatically creating a structure that can expand\nto millions of files, by alphabetically nesting the files in folders.\nThis not only allows for more files to be stored, but also improves\nperformance as there are fewer files stored per folder.\n\nThe same rules apply to the advanced file system store as apply to the\nsimple file system store. To\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering}{cluster}\nthis, you must point the store to a network mounted file system that all\nthe nodes can access, and that networked file system must support\nconcurrent requests and file locking. Otherwise, you may experience data\ncorruption issues if two users attempt to write to the same file at the\nsame time from two different nodes.\n\nTo use the advanced file system store, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Configure \\texttt{portal-ext.properties} with this property:\n\n\\begin{verbatim}\ndl.store.impl=com.liferay.portal.store.file.system.AdvancedFileSystemStore\n\\end{verbatim}\n\\item\n  Restart Liferay DXP.\n\\item\n  In the Control Panel, navigate to \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{File Storage}.\n\\item\n  In the \\emph{Advanced File System Store} screen, configure the store\n  your way.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nLiferay DXP is using the advanced file system store.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} If a database transaction rollback occurs in the\nDocument Library, file system changes that have occurred since the start\nof the transaction aren't reversed. Inconsistencies between Document\nLibrary files and those in the file system store can occur and may\nrequire manual synchronization.\n\n\\noindent\\hrulefill\n\nYou may decide the advanced file system store for whatever reason\ndoesn't serve your needs. If this is the case, you can of course mount\nother file systems into the documents and media library. In addition to\nthis, you can also redefine the Liferay DXP store to use one of the\nother supported protocols. S3 store is next.\n\n\\chapter{Using Amazon Simple Storage\nService}\\label{using-amazon-simple-storage-service}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAmazon's simple storage service (S3) is a cloud-based storage solution\nthat you can use with Documents and Media. All you need is an account,\nand you can store your documents to the cloud from all nodes,\nseamlessly.\n\nWhen you sign up for the service, Amazon assigns you unique keys that\nlink you to your account. In Amazon's interface, you can create\n``buckets'' of data optimized by region.\n\nHere are the steps for configuring Liferay DXP to use your S3 account\nfor file storage:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Amazon S3 requires a \\texttt{SAXParser} from the application server to\n  operate. If you are using an app server like Apache Tomcat that have\n  one, you must include this property in a\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/system-properties}{\\texttt{system-ext.properties}}\n  file:\n\n\\begin{verbatim}\norg.xml.sax.driver=com.sun.org.apache.xerces.internal.parsers.SAXParser\n\\end{verbatim}\n\\item\n  Place your \\texttt{system-ext.properties} file in a folder that\n  resides in your Liferay DXP installation's class path (e.g.,\n  \\texttt{/WEB-INF/classes/}).\n\\item\n  Set the following property in a\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}}\n  file in your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  folder:\n\n\\begin{verbatim}\ndl.store.impl=com.liferay.portal.store.s3.S3Store\n\\end{verbatim}\n\\item\n  Restart Liferay DXP.\n\\item\n  In the Control Panel, navigate to \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{File Storage}.\n\\item\n  In the \\emph{S3 Store Configuration} screen, configure the store your\n  way.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nYour Liferay DXP instance is using the Amazon S3 store.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} If a database transaction rollback occurs in a\nDocument Library that uses a file system based store, file system\nchanges that have occurred since the start of the transaction aren't\nreversed. Inconsistencies between Document Library files and those in\nthe file system store can occur and may require manual synchronization.\nAll stores except DBStore are vulnerable to this limitation.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} No action is required to support AWS Signature Version 4\nrequest authorization.\n\n\\noindent\\hrulefill\n\nConsult the Amazon Simple Storage documentation for additional details\non using Amazon's service.\n\n\\chapter{Using the DBStore}\\label{using-the-dbstore}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nYou can store Documents and Media files in your Liferay DXP database\nusing DBStore. DBStore's maximum file (stored as a blob) size is 1\ngigabyte. To store files larger than that, use Simple File System Store\nor Advanced File System Store.\n\nHere are the DBStore configuration steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Set the following property in a\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}}\n  file in your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  folder:\n\n\\begin{verbatim}\ndl.store.impl=com.liferay.portal.store.db.DBStore\n\\end{verbatim}\n\\item\n  Restart Liferay DXP.\n\\end{enumerate}\n\nDocuments and Media now uses Liferay DXP's database via DBStore.\n\n\\chapter{Configuring Liferay DXP}\\label{configuring-liferay-dxp}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nOnce you have Liferay DXP installed, it's time to configure it to the\nspecifics of your environment. This means doing things like setting the\ntime zone and language, configuring mail, configuring a cluster,\nconfiguring a Content Delivery Network, tuning, and more. These topics\nand more are discussed here.\n\n\\chapter{Configuring Mail}\\label{configuring-mail}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay DXP uses a mail server and SMTP to email notifications.\n@product@'s built-in mail session is the easiest way to configure mail\nand it's recommended. You can configure the built-in mail session before\nor after deploying Liferay DXP. Alternatively, you can configure Liferay\nDXP to use a mail session on the application server.\n\nCreating a mail session in Liferay DXP or on the application server\nrequires this information:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Incoming POP Server and port\n\\item\n  POP User Name\n\\item\n  POP Password\n\\item\n  Outgoing SMTP Server and port\n\\item\n  SMTP User Name\n\\item\n  SMTP Password\n\\item\n  All JavaMail properties you want to use\n\\end{itemize}\n\nBuilt-in mail session setup is recommended and easiest.\n\n\\section{Configuring Liferay DXP's Built-in Mail\nSession}\\label{configuring-liferay-dxps-built-in-mail-session}\n\nThe built-in mail session setup can be done using either of these\nmethods:\n\n\\begin{itemize}\n\\item\n  Control Panel\n\\item\n  Portal properties\n\\end{itemize}\n\n\\section{Built-in Mail Session in the Control\nPanel}\\label{built-in-mail-session-in-the-control-panel}\n\nAfter deploying Liferay DXP, you can configure the mail session from the\nControl Panel.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Sign in as the administrative user (the user you specified on the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/installing-product\\#using-the-setup-wizard}{Basic\n  Configuration page}).\n\\item\n  Navigate to \\emph{Control Panel → Configuration → Server\n  Administration → Mail}.\n\\item\n  Fill out the form. You're asked for the following information:\n\n  \\textbf{Incoming POP Server:} The hostname for a server running the\n  Post Office Protocol. Liferay DXP checks this mailbox for incoming\n  messages, such as message board replies.\n\n  \\textbf{Incoming Port:} The port on which the POP server is listening.\n\n  \\textbf{Use a Secure Network Connection:} Use an encrypted connection\n  when connecting to the POP server.\n\n  \\textbf{User Name:} The user ID Liferay DXP should use to log into the\n  POP server.\n\n  \\textbf{Password:} The password Liferay DXP should use to log into the\n  POP server.\n\n  \\textbf{Outgoing SMTP Server:} The hostname for a server running the\n  Simple Mail Transfer Protocol. Liferay DXP uses this server to send\n  emails, such as password change emails and other notifications.\n\n  \\textbf{Outgoing Port:} The port on which the SMTP server is\n  listening.\n\n  \\textbf{Use a Secure Network Connection:} Use an encrypted connection\n  when connecting to the SMTP server.\n\n  \\textbf{User Name:} The user ID Liferay DXP should use to log into the\n  SMTP server.\n\n  \\textbf{Password:} The password Liferay DXP should use to log into the\n  SMTP server.\n\n  \\textbf{Manually specify additional JavaMail properties to override\n  the above configuration:} If there are additional properties you need\n  to specify, supply them here.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nLiferay DXP connects to the mail session immediately.\n\n\\section{Built-in Mail Session Portal\nProperties}\\label{built-in-mail-session-portal-properties}\n\nIf you prefer specifying your mail session offline or before deploying\nLiferay DXP, use portal properties.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}\n  file}, if you haven't already created one.\n\\item\n  Copy these default property settings into your\n  \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\nmail.session.mail=false\nmail.session.mail.pop3.host=localhost\nmail.session.mail.pop3.password=\nmail.session.mail.pop3.port=110\nmail.session.mail.pop3.user=\nmail.session.mail.smtp.auth=false\nmail.session.mail.smtp.host=localhost\nmail.session.mail.smtp.password=\nmail.session.mail.smtp.port=25\nmail.session.mail.smtp.user=\nmail.session.mail.store.protocol=pop3\nmail.session.mail.transport.protocol=smtp\n\\end{verbatim}\n\\item\n  Replace the default mail session values with your own.\n\\item\n  Put the \\texttt{portal-ext.properties} file into your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{LIFERAY\\_HOME},\n  once you've established it based on your installation.\n\\end{enumerate}\n\nLiferay DXP connects to the mail session on the next startup.\n\n\\section{Configuring a Mail Session on the Application\nServer}\\label{configuring-a-mail-session-on-the-application-server}\n\nYou can manage a mail session for Liferay DXP on your application\nserver. Here's how:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a mail session on your application server, following your\n  application server documentation.\n\\item\n  Point Liferay DXP to that mail session using the Control Panel or\n  portal properties. Here are instructions for both:\n\n  \\begin{itemize}\n  \\item\n    Configure the JNDI name in the \\emph{Mail} page at \\emph{Control\n    Panel → Configuration → Server Administration → Mail}.\n  \\item\n    Set a \\texttt{mail.session.jndi.name} portal property in a\n    \\texttt{{[}LIFERAY\\_HOME{]}/portal-ext.properties} file. Here's an\n    example property:\n\n\\begin{verbatim}\n    mail.session.jndi.name=mail/MailSession\n\\end{verbatim}\n  \\end{itemize}\n\\end{enumerate}\n\nLastly, configure your instance's email senders.\n\n\\section{Configuring default email\nsenders}\\label{configuring-default-email-senders}\n\nEmail senders are the default name and email address Liferay DXP uses to\nsend administrative emails and announcement emails.\n\nDefault email senders are configured in the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}\nfile}.\n\n\\begin{itemize}\n\\item\n  Admin email configuration:\n\n\\begin{verbatim}\nadmin.email.from.name=Joe Bloggs\nadmin.email.from.address=test@domain.invalid\n\\end{verbatim}\n\\item\n  Announcements email configuration:\n\n\\begin{verbatim}\nannouncements.email.to.name=\nannouncements.email.to.address=noreply@domain.invalid\n\\end{verbatim}\n\\end{itemize}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Replace the names and email addresses above with your values.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Following emails are blacklisted by default and cannot be\nused in any Liferay DXP installation:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{noreply@liferay.com}\n\\item\n  \\texttt{test@liferay.com}\n\\item\n  \\texttt{noreply@domain.invalid}\n\\item\n  \\texttt{test@domain.invalid}\n\\end{itemize}\n\nIf you use them, Liferay DXP logs a \\texttt{WARN} trace:\n\n\\texttt{Email\\ xxx\\ will\\ be\\ ignored\\ because\\ it\\ is\\ included\\ in\\ mail.send.blacklist}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\tightlist\n\\item\n  Restart your server.\n\\end{enumerate}\n\nCongratulations on configuring mail for Liferay DXP.\n\n\\chapter{Locales and Encoding\nConfiguration}\\label{locales-and-encoding-configuration}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nYou can display content based on language, time zone, ``right to left''\n(that is, languages such as Hebrew, Arabic, and Persian), and you can\nlocalize user names and titles. Administrators can localize specific\ncore UI messages so that the messages display in certain languages.\n\n\\section{Time Zones}\\label{time-zones}\n\nYou can set time zones in the Control Panel and theoretically in the JVM\n(but this must be set to GMT: see below).\n\nTime zone configuration and default language customization are done in\nthe Control Panel, at the Instance level.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the \\emph{Control Panel} → \\emph{Configuration}.\n\\item\n  Click \\emph{Instance Settings}.\n\\item\n  Click on the \\emph{Miscellaneous} tab.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/instance-locales.png}}\n\\caption{You can change the default and available languages and the time\nzone in Instance Settings.}\n\\end{figure}\n\nThe central left and right arrows let you add or remove available\nlanguages and locales. You can also set these as properties in your\n\\texttt{portal-ext.properties} file in your\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\nfolder. The \\texttt{portal.properties} reference document's\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Company}{Company}\nsection defines the default locale. The\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Languages\\%20and\\%20Time\\%20Zones}{Languages\nand Time Zones} section defines the available and current locales.\n\n\\begin{verbatim}\ncompany.default.locale=en_GB \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\texttt{company.default.locale} portal property is\nonly intended for use on initial startup. To change the language\nsettings on an existing instance, open the Control Panel and navigate to\n\\emph{Configuration} → \\emph{Instance Settings} and select the\nLocalization category under the PLATFORM heading. Under the Language\nentry you can change the default language, as well as define the current\nlocales.\n\n\\noindent\\hrulefill\n\nAs an example, the above property changes the locale to English, Great\nBritain.\n\n\\section{Set the JVM Time Zone to\nGMT}\\label{set-the-jvm-time-zone-to-gmt}\n\nIf you set the time zone in the JVM, it causes issues such as Calendar\nEvents and Web Content articles displaying the wrong dates. This happens\nbecause the system assumes each date stored in the database is stored in\nGMT time. When the system needs to display one stored date to the end\nusers, the display date is calculated by the application server's\ncurrent date. This date is affected by the configured JVM level time\nzone and the stored GMT format date. To make sure the display date is\ncalculated correctly, the time zone must be configured to GMT at the JVM\nlevel. Otherwise, an incorrect time zone offset at the JVM level causes\nthe display date to be wrongly calculated and displayed.\n\n\\section{Friendly URLs and Locales}\\label{friendly-urls-and-locales}\n\nIn addition to configuring instance settings, you can also define unique\nURLs for specific languages using the \\texttt{I18nServlet} by editing\nPortal's \\texttt{web.xml} file:\n\n\\begin{verbatim}\n<servlet-mapping>\n    <servlet-name>I18n Servlet</servlet-name>\n    <url-pattern>/ar/*</url-pattern>\n</servlet-mapping>\n  .\n  .\n.\n<servlet-mapping>\n    <servlet-name>I18n Servlet</servlet-name>\n    <url-pattern>/de/*</url-pattern>\n</servlet-mapping>\n\\end{verbatim}\n\nThe defaults should be sufficient for nearly all circumstances. Because\n\\texttt{web.xml} changes require stopping and possibly redeploying\nLiferay DXP (depending on your app server), test the defaults and make\nsure you really need to modify these settings. If you're clustered, you\nmust make these changes on all nodes.\n\n\\section{Modifying Language Keys}\\label{modifying-language-keys}\n\nDevelopers can add or modify certain core UI messages (e.g.~\\emph{Your\nrequest completed successfully.}) by\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys}{modifying\nthe language keys} that ship by default.\n\n\\section{Right to Left}\\label{right-to-left}\n\nFor languages that are displayed right to left, use the following\nlanguage properties settings:\n\n\\begin{verbatim}\nlang.dir=rtl\nlang.line.begin=right\nlang.line.end=left\n\\end{verbatim}\n\nTo display right to left by default,\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-global-language-keys}{override\nthese properties globally}.\n\n\\section{Localizing User Names}\\label{localizing-user-names}\n\nUsers can change the prefix and suffix values for a locale. For example,\nfor Spanish, the \\texttt{language\\_es.properties} file contains these\nvalues:\n\n\\begin{verbatim}\nlang.user.name.field.names=prefix,first-name,last-name\nlang.user.name.prefix.values=Sr,Sra,Sta,Dr,Dra\nlang.user.name.required.field.names=last-name\n\\end{verbatim}\n\nFor more information, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-liferays-localization-settings}{Using\nLiferay Language Settings}.\n\n\\section{Related Topics}\\label{related-topics}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-liferays-localization-settings}{Using\nLiferay Language Settings}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-global-language-keys}{Overriding\nGlobal Language Keys}\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys}{Overriding\na Module's Language Keys}\n\n\\chapter{Liferay DXP Clustering}\\label{liferay-dxp-clustering}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay DXP can serve everything from the smallest to the largest web\nsites. Out of the box, it's configured optimally for a single server\nenvironment. If one server isn't sufficient to serve your site's high\ntraffic needs, Liferay DXP scales to the size you need.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/clustering-enterprise-configuration.png}}\n\\caption{Liferay DXP is designed to scale to as large an installation as\nyou need.}\n\\end{figure}\n\nLiferay DXP works well in clusters of multiple machines (horizontal\ncluster) or in clusters of multiple VMs on a single machine (vertical\ncluster), or any mixture of the two. Once you have Liferay DXP installed\non more than one application server node, there are several\noptimizations that must be made. At a minimum, Liferay DXP should be\nconfigured in the following way for a clustered environment:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/point-all-nodes-to-the-same-database}{All\n  nodes should point to the same database or database cluster.}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/configure-documents-and-media-the-same-for-all-nodes}{Documents\n  and Media repositories must have the same configuration and be\n  accessible to all nodes of the cluster.}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/clustering-search}{Search\n  should be on a separate search server that is optionally clustered.}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/enabling-cluster-link}{Cluster\n  Link must be enabled so the cache replicates across all nodes of the\n  cluster.}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/auto-deploy-to-all-nodes}{Applications\n  must be auto-deployed to each node individually.}\n\\end{enumerate}\n\nMany of these configuration changes can be made by adding or modifying\nproperties in your \\texttt{portal-ext.properties} file. Remember that\nthis file overrides the\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html}{defaults}\nin the \\texttt{portal.properties} file. It's a best practice to copy the\nrelevant section you want to modify from \\texttt{portal.properties} into\nyour \\texttt{portal-ext.properties} file, and then modify the values\nthere.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} This documentation describes a Liferay DXP-specific\ncluster configuration without getting into specific implementations of\nthird party software, such as Java EE application servers, HTTP servers,\nand load balancers. Please consult your documentation for those\ncomponents of your cluster to configure those components. Before\ncreating a Liferay DXP cluster, make sure your OS is not defining the\nhostname of your box to the local network at 127.0.0.1.\n\n\\noindent\\hrulefill\n\nEach step defined above is covered below to give you a step by step\nprocess for creating your cluster. Start with making all Nodes point to\nthe same database.\n\n\\chapter{Point all Nodes to the Same Liferay DXP\nDatabase}\\label{point-all-nodes-to-the-same-liferay-dxp-database}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nEach node should have a data source that points to one Liferay DXP\ndatabase (or a database cluster) that all the nodes share. This means,\nof course, Liferay DXP cannot (and should not) use the embedded HSQL\ndatabase that is shipped with the bundles (but you already knew that,\nright?). And, of course, the database server should be on a separate\nsystem from the Liferay DXP server.\n\n\\section{Read-Writer Database\nConfiguration}\\label{read-writer-database-configuration}\n\nTo improve database performance, you can use a read-writer database\nconfiguration. Instead of using the same data source for read and\nread-write operations, this strategy uses a separate data source for\neach operation type. DXP's Aspect Oriented Programming (AOP) transaction\ninfrastructure directs read transactions to the read data source and\nread-write transactions to the write data source.\n\nConnections to separate read and read-write\n\\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#JDBC}{data\nsources} are configured using JDBC or JNDI\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{Portal\nProperties} (e.g., in a\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}\nfile}), as explained in the following sections. The data sources should\nuse separate instances of the DXP database, where the read-write\ndatabase instance is replicated to the read database instance.\n\n\\section{JDBC}\\label{jdbc}\n\nEdit your \\texttt{portal-ext.properties} file following these steps to\nconnect directly to your separate read and write data sources using\n\\href{/docs/7-2/deploy/-/knowledge_base/d/database-templates}{JDBC}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Set the default connection pool provider. For provider information,\n  see the\n  \\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#JDBC}{JDBC\n  properties reference}. The default setting specifies\n  \\href{https://github.com/brettwooldridge/HikariCP}{HikariCP} as the\n  pool provider:\n\n\\begin{verbatim}\njdbc.default.liferay.pool.provider=hikaricp\n\\end{verbatim}\n\\item\n  Configure JDBC connections to your separate read and write data\n  sources. Here's an example:\n\n\\begin{verbatim}\njdbc.read.driverClassName=[place your driver name here]\njdbc.read.url=[place the URL to your \"read\" database here]\njdbc.read.username=[place your user name here]\njdbc.read.password=[place your password here]\n\njdbc.write.driverClassName=[place your driver name here]\njdbc.write.url=[place the URL to your \"read-write\" database here]\njdbc.write.username=[place your user name here]\njdbc.write.password=[place your password here]\n\\end{verbatim}\n\n  For example JDBC connection values, please see\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/database-templates}{Database\n  Templates}.\n\\item\n  Configure DXP to use the write data source (the data source whose\n  prefix is \\texttt{jdbc.write.}) to create the\n  \\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Counter}{Counter}\n  data source. A separate data source is always dedicated to the\n  counter.\n\n\\begin{verbatim}\ncounter.jdbc.prefix=jdbc.write.\n\\end{verbatim}\n\\item\n  Optionally validate the data connections to make sure bad connections\n  are handled gracefully.\n\n  Some connection pools used with JDBC4 (check your driver's JDBC\n  version) validate connections automatically. Other connection pools\n  may require additional, vendor-specific connection validation\n  properties---specify them in a Portal Properties file. Refer to your\n  connection pool provider documentation for connection validation\n  details.\n\\item\n  Enable the read-writer database configuration by copying the default\n  \\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Spring}{\\texttt{spring.configs}\n  and \\texttt{spring.infrastructure.configs} Portal Properties} to your\n  \\texttt{portal-ext.properties} file and adding the following Spring\n  configuration file paths to them.\n\n  Add to \\texttt{spring.configs}:\n\n\\begin{verbatim}\nMETA-INF/dynamic-data-source-spring.xml\n\\end{verbatim}\n\n  Add to \\texttt{spring.infrastructure.configs}:\n\n\\begin{verbatim}\nMETA-INF/dynamic-data-source-infrastructure-spring.xml\n\\end{verbatim}\n\n  For more information, see the\n  \\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Spring}{Spring\n  configuration Portal Properties}.\n\\end{enumerate}\n\n\\section{JNDI}\\label{jndi}\n\nEdit your \\texttt{portal-ext.properties} file following these steps to\nconnect to your read and write data sources on your app server using\nJNDI:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Set your read and write JNDI data source user names and passwords.\n\n\\begin{verbatim}\njdbc.read.jndi.name=[place your \"read\" data source JNDI name here]\n\njdbc.read.username=*[place your user name here]\njdbc.read.password=[place your password here]\n\njdbc.write.jndi.name=[place your \"read-write\" data source JNDI name here]\n\njdbc.write.username=[place your user name here]\njdbc.write.password=[place your password here]\n\\end{verbatim}\n\\item\n  Configure DXP to use the write data source (the data source whose\n  prefix is \\texttt{jdbc.write.}) to create the\n  \\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Counter}{Counter}\n  data source. A separate data source is always dedicated to the\n  counter.\n\n\\begin{verbatim}\ncounter.jdbc.prefix=jdbc.write.\n\\end{verbatim}\n\\item\n  Optionally validate the data connections to make sure bad connections\n  are handled gracefully.\n\n  Some connection pools used with JDBC4 (check your driver's JDBC\n  version) validate connections automatically. Other connection pools\n  may require additional, vendor-specific connection validation\n  properties---specify them in a Portal Properties file. Refer to your\n  connection pool provider documentation for connection validation\n  details.\n\\item\n  Enable the read-writer database configuration by copying the default\n  \\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Spring}{\\texttt{spring.configs}\n  and \\texttt{spring.infrastructure.configs} Portal Properties} to your\n  \\texttt{portal-ext.properties} file and add the following Spring\n  configuration file paths to them.\n\n  Add to \\texttt{spring.configs}:\n\n\\begin{verbatim}\nMETA-INF/dynamic-data-source-spring.xml\n\\end{verbatim}\n\n  Add to \\texttt{spring.infrastructure.configs}:\n\n\\begin{verbatim}\nMETA-INF/dynamic-data-source-infrastructure-spring.xml\n\\end{verbatim}\n\n  For more information, see the\n  \\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Spring}{Spring\n  configuration Portal Properties}.\n\\end{enumerate}\n\nDXP uses a read data source, a write data source, and a counter data\nsource the next time it starts.\n\n\\chapter{Configure Documents and Media the Same for all\nNodes}\\label{configure-documents-and-media-the-same-for-all-nodes}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nIn a cluster, Documents and Media must use the same\n\\href{/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration}{document\nrepository configuration} on all nodes.\n\nNote if you are using the \\texttt{File\\ System} or\n\\texttt{Advanced\\ File\\ System} stores, the file system must be\naccessible from all nodes (i.e., a network share), support concurrent\nrequests, and file locking.\n\n\\textbf{Checkpoint}: Verify sharing works by executing these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  On Node 1 upload a document to the Documents and Media.\n\\item\n  On Node 2 download the document. The download should be successful.\n\\item\n  Repeat the test with reversed roles.\n\\end{enumerate}\n\n\\chapter{Clustering Search}\\label{clustering-search}\n\nSearch should always run on a separate environment from your Liferay DXP\nserver. Liferay DXP supports\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch}{Elasticsearch},\nand \\href{/docs/7-2/deploy/-/knowledge_base/d/installing-solr}{Solr}.\nBoth can also be clustered.\n\nFor more information on how to cluster Elasticsearch, see\n\\href{https://www.elastic.co/guide/en/elasticsearch/guide/current/distributed-cluster.html}{Elasticsearch's\ndistributed cluster setup}.\n\nOnce Liferay DXP servers have been properly configured as a cluster and\nthe same for Elasticsearch, change Liferay DXP from \\emph{embedded} mode\nto \\emph{remote} mode. On the first connection, the two sets of\nclustered servers communicate with each other the list of all IP\naddresses; in case of a node going down, the proper failover protocols\nenable. Queries and indexes can continue to be sent for all nodes.\n\nFor more information on how to cluster Solr, see\n\\href{https://cwiki.apache.org/confluence/display/solr/SolrCloud}{Apache\nSolr Cloud} documentation.\n\nOnce Liferay DXP servers have been properly configured as a cluster,\ndeploy the Liferay Connector to Solr on all nodes. (This app is\navailable for download from Liferay Marketplace) Create a Solr Cloud\n(cluster) managed by \\emph{Apache Solr Zookeeper}. Connect the Liferay\nDXP cluster to Zookeeper and finish the final configurations to connect\nthe two clusters.\n\n\\chapter{Enabling Cluster Link}\\label{enabling-cluster-link}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nEnabling Cluster Link automatically activates distributed caching. The\ncache is distributed across multiple Liferay DXP nodes running\nconcurrently. Cluster Link does \\href{http://www.ehcache.org}{Ehcache}\nreplication. The Ehcache global settings are in the\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Ehcache}{\\texttt{portal.properties}\nfile}.\n\nBy default Liferay does not copy cached entities between nodes. If an\nentity is deleted or changed, for example, Cluster Link sends a\n\\emph{remove} message to the other nodes to invalidate this entity in\ntheir local caches. Requesting that entity on another node results in a\ncache \\emph{miss}; the entity is then retrieved from the database and\nput into the local cache. Entities added to one node's local cache are\nnot copied to local caches of the other nodes. An attempt to retrieve a\nnew entity on a node which doesn't have that entity cached results in a\ncache \\emph{miss}. The miss triggers the node to retrieve the entity\nfrom the database and store it in its local cache.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/clustering-cache-efficient-algorithm.png}}\n\\caption{Liferay DXP's cache algorithm is extremely efficient.}\n\\end{figure}\n\nHere are the Cluster Link topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[enabling-cluster-link]{Enabling Cluster Link}\n\\item\n  \\hyperref[multicast-over-udp]{Multicast Over UDP}\n\\item\n  \\hyperref[unicast-over-tcp]{Unicast Over TCP}\n\\item\n  \\hyperref[using-different-control-and-transport-channel-ports]{Using\n  Different Control and Transport Channel Ports}\n\\item\n  \\hyperref[modifying-the-cache-configuration-with-a-module]{Modifying\n  the Cache Configuration with a Module}\n\\item\n  \\hyperref[conclusion]{Conclusion}\n\\end{itemize}\n\n\\section{Enabling Cluster Link}\\label{enabling-cluster-link-1}\n\nTo enable Cluster Link, add this\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\nproperty} to a \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\ncluster.link.enabled=true\n\\end{verbatim}\n\nThe\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Cluster\\%20Link}{Cluster\nLink portal properties} provide a default configuration that you can\noverride to fit your needs.\n\nMany of the defaults use \\texttt{localhost}, instead of a real address.\nIn some configurations, however, \\texttt{localhost} is bound to the\ninternal loopback network (\\texttt{127.0.0.1} or \\texttt{::1}), rather\nthan the host's real address. If for some reason you need this\nconfiguration, you can make DXP auto detect the real address with this\nproperty:\n\n\\begin{verbatim}\ncluster.link.autodetect.address=www.google.com:80\n\\end{verbatim}\n\nSet it to connect to some other host that's contactable by your server.\nBy default, it points to Google, but this may not work if your server is\nbehind a firewall. If you use each host's real address, you don't need\nto set the auto-detect address.\n\nCluster Link depends on \\href{http://www.jgroups.org}{JGroups} and\nprovides an API for nodes to communicate. It can\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Send messages to all nodes in a cluster\n\\item\n  Send messages to a specific node\n\\item\n  Invoke methods and retrieve values from all, some, or specific nodes\n\\item\n  Detect membership and notify when nodes join or leave\n\\end{itemize}\n\nCluster Link contains an enhanced algorithm that provides one-to-many\ntype communication between the nodes. This is implemented by default\nwith JGroups's UDP multicast, but unicast and TCP are also available.\n\n\\section{Multicast Over UDP}\\label{multicast-over-udp}\n\nWhen you enable Cluster Link, Liferay DXP's default clustering\nconfiguration is enabled. This configuration defines IP multicast over\nUDP. Liferay DXP uses two groups of\n\\href{http://www.jgroups.org/manual4/index.html\\#_channel}{channels from\nJGroups} to implement this: a control group and a transport group. If\nyou want to customize the\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Cluster\\%20Link}{channel\nproperties}, you can do so in \\texttt{portal-ext.properties}:\n\n\\begin{verbatim}\ncluster.link.channel.name.control=[your control channel name]\ncluster.link.channel.properties.control=[your control channel properties]\n\\end{verbatim}\n\nPlease see\n\\href{http://www.jgroups.org/manual4/index.html\\#protlist}{JGroups's\ndocumentation} for channel properties. The default configuration sets\nmany properties whose settings are discussed there.\n\nMulticast broadcasts to all devices on the network. Clustered\nenvironments on the same network communicate with each other by default.\nMessages and information (e.g., scheduled tasks) sent between them can\nlead to unintended consequences. Isolate such cluster environments by\neither separating them logically or physically on the network, or by\nconfiguring each cluster's \\texttt{portal-ext.properties} to use\ndifferent sets of\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Multicast}{multicast\ngroup address and port values}.\n\nJGroups sets a bind address automatically. If you want to set a manual\naddress, you can do this. By default, these are set to\n\\texttt{localhost}:\n\n\\begin{verbatim}\ncluster.link.bind.addr[\"cluster-link-control\"]=localhost\ncluster.link.bind.addr[\"cluster-link-udp\"]=localhost\n\\end{verbatim}\n\nIn some configurations, however, \\texttt{localhost} is bound to the\ninternal loopback network (\\texttt{127.0.0.1} or \\texttt{::1}), rather\nthan the host's real address. If for some reason you need this\nconfiguration, you can make Liferay DXP auto detect its real address\nwith this property:\n\n\\begin{verbatim}\ncluster.link.autodetect.address=www.google.com:80\n\\end{verbatim}\n\nSet it to connect to some other host that's contactable by your server.\nBy default, it points to Google, but this may not work if your server is\nbehind a firewall. If you set the address manually using the properties\nabove, you don't need to set the auto-detect address.\n\nYour network configuration may preclude the use of multicast over TCP,\nso below are some other ways you can get your cluster communicating.\nNote that these methods are all provided by JGroups.\n\n\\section{Checkpoint:}\\label{checkpoint}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If you are binding the IP address instead of using \\texttt{localhost},\n  make sure the right IP addresses are declared using these properties:\n\n\\begin{verbatim}\ncluster.link.bind.addr[\"cluster-link-control\"]=localhost\ncluster.link.bind.addr[\"cluster-link-udp\"]=localhost\n\\end{verbatim}\n\\item\n  Test your load and then optimize your settings if necessary.\n\\end{enumerate}\n\n\\section{Unicast over TCP}\\label{unicast-over-tcp}\n\nIf your network configuration or the geographical distance between nodes\nprevents you from using UDP Multicast clustering, you can configure TCP\nUnicast. You must use this if you have a firewall separating any of your\nnodes or if your nodes are in different geographical locations.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a parameter to your app server's JVM on each node:\n\n\\begin{verbatim}\n-Djgroups.bind_addr=[node_ip_address]\n\\end{verbatim}\n\n  Use the node's IP address.\n\\item\n  Select a discovery protocol for the nodes to use to find each other.\n  Here are the protocol choices:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    TCPPing\n  \\item\n    JDBCPing\n  \\item\n    S3\\_Ping\n  \\item\n    Rackspace\\_Ping\n  \\end{itemize}\n\n  If you aren't sure which one to choose, use TCPPing. The rest of these\n  steps use TCPPing\n\\item\n  Extract the \\texttt{tcp.xml} file from\n  \\texttt{\\$LIFERAY.HOME/osgi/marketplace/Liferay\\ Foundation\\ -\\ Liferay\\ Portal\\ -\\ Impl.lpkg/com\\hspace{0pt}.\\hspace{0pt}liferay\\hspace{0pt}.\\hspace{0pt}portal\\hspace{0pt}.\\hspace{0pt}cluster\\hspace{0pt}.\\hspace{0pt}multiple\\hspace{0pt}-\\hspace{0pt}{[}version{]}.\\hspace{0pt}jar/lib\\hspace{0pt}/\\hspace{0pt}jgroups\\hspace{0pt}-\\hspace{0pt}{[}version{]}.\\hspace{0pt}Final\\hspace{0pt}.\\hspace{0pt}jar/tcp.xml}\n  to a location accessible to DXP, such as a folder called\n  \\texttt{jgroups} in the DXP web application's \\texttt{WEB-INF/classes}\n  folder.\n\n\\begin{verbatim}\nWEB-INF/classes/jgroups/tcp.xml\n\\end{verbatim}\n\\item\n  In the \\texttt{tcp.xml} file, set the TCP bind port to an unused port\n  on your node. Here's an example:\n\n\\begin{verbatim}\n<TCP bind_port=\"7800\"/>\n\\end{verbatim}\n\\item\n  In the \\texttt{tcp.xml} file, make each node discoverable to TCPPing\n  by specifying its IP address and an unused port on that node. Building\n  off of the previous step, here's an example\n  \\texttt{\\textless{}TCPPing\\textgreater{}} element:\n\n\\begin{verbatim}\n<TCP bind_port=\"7800\"/>\n<TCPPING async_discovery=\"true\"\n    initial_hosts=\"192.168.224.154[7800],192.168.224.155[7800]\"\n    port_range=\"0\"/>\n\\end{verbatim}\n\n  \\textbf{Regarding Initial Hosts:}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    An alternative to specifying initial hosts in a TCP XML file is to\n    specify them to your app server using a JVM argument like this:\n    \\texttt{-Djgroups.tcpping.initial\\_hosts=192.168.224.154{[}7800{]},192.168.224.155{[}7800{]}}.\n  \\item\n    Make sure the initial hosts value accounts for all your nodes. If\n    \\texttt{initial\\_hosts} is not specified in a TCP XML file or in a\n    JVM argument, \\texttt{localhost} is the initial host.\n  \\end{itemize}\n\\item\n  Copy your \\texttt{tcp.xml} file to each node, making sure to set the\n  TCP bind port to the node's bind port. On the node with IP address\n  \\texttt{192.168.224.155}, for example, configure TCPPing like this:\n\n\\begin{verbatim}\n<TCP bind_port=\"7800\"/>\n<TCPPING async_discovery=\"true\"\n    initial_hosts=\"192.168.224.154[7800],192.168.224.155[7800]\"\n    port_range=\"0\"/>\n\\end{verbatim}\n\\item\n  Modify the\n  \\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Cluster\\%20Link}{Cluster\n  Link properties} in the node's \\texttt{portal-ext.properties} file to\n  enable Cluster Link and point to the TCP XML file for each Cluster\n  Link channel:\n\\end{enumerate}\n\n\\begin{verbatim}\ncluster.link.enabled=true\ncluster.link.channel.properties.control=/jgroups/tcp.xml\ncluster.link.channel.properties.transport.0=/jgroups/tcp.xml\n\\end{verbatim}\n\nThe JGroups configuration demonstrated above is typically all that\nUnicast over TCP requires. However, in a very specific case, if\n\\emph{(and only if)} cluster nodes are deployed across multiple\nnetworks, then the parameter \\texttt{external\\_addr} must be set on each\nhost to the external (public IP) address of the firewall. This kind of\nconfiguration is usually only necessary when nodes are geographically\nseparated. By setting this, clustered nodes deployed to separate\nnetworks (e.g.~separated by different firewalls) can communicate\ntogether. This configuration may be flagged in security audits of your\nsystem. See\n\\href{http://www.jgroups.org/manual4/index.html\\#_transport_protocols}{JGroups\ndocumentation} for more information.\n\n\\begin{quote}\n\\textbf{Note:} The \\texttt{singleton\\_name} TCP attribute was deprecated\nin JGroups v4.0.0 and has therefore been removed since Liferay DXP 7.2\nSP1 and Liferay Portal CE GA2 which use JGroups v 4.1.1-Final.\n\\end{quote}\n\nYou're now set up for Unicast over TCP clustering! Repeat either TCPPING\nprocess for each node you want to add to the cluster.\n\n\\section{JDBC Ping}\\label{jdbc-ping}\n\nRather than use TCP Ping to discover cluster members, you can use a\ncentral database accessible by all the nodes to help them find each\nother. Cluster members write their own and read the other members'\naddresses from this database. To enable this configuration, replace the\n\\texttt{TCPPING} tag with the corresponding \\texttt{JDBC\\_PING} tag:\n\n\\begin{verbatim}\n<JDBC_PING\n    connection_url=\"[place the URL to your database here]\"\n    connection_username=\"[place your user name here]\"\n    connection_password=\"[place your password here]\"\n    connection_driver=\"[place your driver name here]\"/>\n\\end{verbatim}\n\nFor example JDBC connection values, please see\n\\href{/docs/7-2/deploy/-/knowledge_base/d/database-templates}{Database\nTemplates}. For further information about JDBC Ping, please see the\n\\href{http://www.jgroups.org/manual4/index.html\\#DiscoveryProtocols}{JGroups\nDocumentation}.\n\n\\section{S3 Ping}\\label{s3-ping}\n\nAmazon S3 Ping can be used for servers running on Amazon's EC2 cloud\nservice. Each node uploads a small file to an S3 bucket, and all the\nother nodes read the files from this bucket to discover the other nodes.\nWhen a node leaves, its file is deleted.\n\nTo configure S3 Ping, replace the \\texttt{TCPPING} tag with the\ncorresponding \\texttt{S3\\_PING} tag:\n\n\\begin{verbatim}\n<S3_PING\n    secret_access_key=\"[SECRETKEY]\"\n    access_key=\"[ACCESSKEY]\"\n    location=\"ControlBucket\"/>\n\\end{verbatim}\n\nSupply your Amazon keys as values for the parameters above. For further\ninformation about S3 Ping, please see the\n\\href{http://www.jgroups.org/manual4/index.html\\#_s3_ping}{JGroups\nDocumentation}.\n\n\\section{Other Pings}\\label{other-pings}\n\nJGroups supplies other means for cluster members to discover each other,\nincluding Rackspace Ping, BPing, File Ping, and others. Please see the\n\\href{http://www.jgroups.org/manual4/index.html\\#DiscoveryProtocols}{JGroups\nDocumentation} for information about these discovery methods.\n\nThe control and transport channels can be configured to use different\nports.\n\n\\section{Using Different Control and Transport Channel\nPorts}\\label{using-different-control-and-transport-channel-ports}\n\nUsing separate control and transport channel ports lets you monitor\ncontrol and transport traffic and helps you isolate information to\ndiagnose problems. These steps use Unicast over TCPPing to demonstrate\nthe approach.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a parameter to your app server's JVM on each node:\n\n\\begin{verbatim}\n-Djgroups.bind_addr=[node_ip_address]\n\\end{verbatim}\n\\item\n  Extract the \\texttt{tcp.xml} file from\n  \\texttt{\\$LIFERAY.HOME/osgi/marketplace/Liferay\\ Foundation\\ -\\ Liferay\\ Portal\\ -\\ Impl.lpkg/com\\hspace{0pt}.\\hspace{0pt}liferay\\hspace{0pt}.\\hspace{0pt}portal\\hspace{0pt}.\\hspace{0pt}cluster\\hspace{0pt}.\\hspace{0pt}multiple\\hspace{0pt}-\\hspace{0pt}{[}version{]}.\\hspace{0pt}jar/lib\\hspace{0pt}/\\hspace{0pt}jgroups\\hspace{0pt}-\\hspace{0pt}{[}version{]}.\\hspace{0pt}Final\\hspace{0pt}.\\hspace{0pt}jar/tcp.xml}\n  to a location accessible to DXP, such as a folder called\n  \\texttt{jgroups} in the DXP web application's \\texttt{WEB-INF/classes}\n  folder.\n\\item\n  Make a copy of the \\texttt{tcp.xml} in the same location and rename\n  both files, designating one for the control channel and the other for\n  the transport channel. For example, you could use these file names:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{tcp-control.xml}\n  \\item\n    \\texttt{tcp-transport.xml}\n  \\end{itemize}\n\\item\n  Modify the\n  \\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Cluster\\%20Link}{Cluster\n  Link properties} in the node's \\texttt{portal-ext.properties} file to\n  enable Cluster Link and point to the TCP XML file for each Cluster\n  Link channel:\n\n\\begin{verbatim}\ncluster.link.enabled=true\ncluster.link.channel.properties.control=/jgroups/tcp-control.xml\ncluster.link.channel.properties.transport.0=/jgroups/tcp-transport.xml\n\\end{verbatim}\n\\item\n  Modify each \\texttt{tcp-*.xml} file's the TCP and TCPPing elements to\n  account for each node's IP address and bind port.\n\n  If you're vertically clustering (i.e., you have multiple servers\n  running on the same physical or virtual system), every channel must\n  use a unique unused bind port for discovery communication. In each\n  \\texttt{tcp-*.xml} file, assign the TCP tag's \\texttt{bind\\_port}\n  attribute to a unique, unused port.\n\n  For example, your first two nodes might assign these bind ports:\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n Node   | Properties File     | Port   |\n :----- | :------------------ | :----- |\n Node 1 | `tcp-control.xml`   | `7800` |\n Node 1 | `tcp-transport.xml` | `7801` |\n Node 2 | `tcp-control.xml`   | `7802` |\n Node 2 | `tcp-transport.xml` | `7803` |\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nHere are example TCP and TCPPing elements using the bind ports on nodes running on the same system (i.e., same IP address):\n\n**Node 1 `tcp-control.xml`**\n\n```xml\n<TCP bind_port=\"7800\"/>\n<TCPPING async_discovery=\"true\"\n    initial_hosts=\"192.168.224.154[7800],192.168.224.154[7802]\"\n    port_range=\"0\"/>\n```\n\n**Node 1 `tcp-transport.xml`**\n\n```xml\n<TCP bind_port=\"7801\"/>\n<TCPPING async_discovery=\"true\"\n    initial_hosts=\"192.168.224.154[7801],192.168.224.154[7803]\"\n    port_range=\"0\"/>\n```\n\n**Node 2 `tcp-control.xml`**\n\n```xml\n<TCP bind_port=\"7802\"/>\n<TCPPING async_discovery=\"true\"\n    initial_hosts=\"192.168.224.154[7800],192.168.224.154[7802]\"\n    port_range=\"0\"/>\n```\n\n**Node 2 `tcp-transport.xml`**\n\n```xml\n<TCP bind_port=\"7803\"/>\n<TCPPING async_discovery=\"true\"\n    initial_hosts=\"192.168.224.154[7801],192.168.224.154[7803]\"\n    port_range=\"0\"/>\n```\n\\end{verbatim}\n\nIf you have added entities that can be cached or you want to tune the\ncache configuration for your system, you can do so using a module.\n\n\\section{Modifying the Cache Configuration with a\nModule}\\label{modifying-the-cache-configuration-with-a-module}\n\nIt's recommended to test your system under a load that best simulates\nthe kind of traffic your system must handle. If you serve a lot of\nmessage board messages, your script should reflect that. If web content\nis the core of your site, your script should reflect that too.\n\nAs a result of a load test, you may find that the default distributed\ncache settings aren't optimized for your site. In this case, tweak the\nsettings using a module. Follow instructions for\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/overriding-cache}{Overriding\nCache}.\n\nYou can install the module on each node and change the settings without\ntaking down the cluster. This is a great benefit, but beware: since\nEhcache doesn't allow for changes to cache settings while the cache is\nalive, reconfiguring a cache while the server is running flushes the\ncache.\n\n\\section{Conclusion}\\label{conclusion}\n\nOnce you've configured your cluster, you can start it. A log file\nmessage shows your cluster's name (e.g.,\n\\texttt{cluster=liferay-channel-control}):\n\n\\begin{verbatim}\nGMS: address=oz-52865, cluster=liferay-channel-control, physical address=192.168.1.10:50643\n\\end{verbatim}\n\nCongratulations! Your cluster is using Cluster Link.\n\n\\chapter{Auto Deploy to All Nodes}\\label{auto-deploy-to-all-nodes}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAll modules and WAR files you deploy onto the cluster must be deployed\nto all cluster nodes. Because Liferay DXP\n\\href{/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator}{installs\napplications as OSGi bundles}, you cannot rely on your application\nserver's means of installing WAR files (even if you only intend to\ninstall WAR files) to deploy an application to the entire cluster.\nInstead, place the application in each node's auto deploy folder (e.g.,\n\\texttt{{[}Liferay\\ Home{]}/deploy}).\n\nThis, as you might imagine, can be done with a script. Write a shell\nscript that uploads applications to each node using sftp or some other\nservice. This way, when you deploy an application, it uploads to each\nnode's auto deploy folder and installs to Liferay DXP on each node.\n\n\\chapter{Updating a Cluster}\\label{updating-a-cluster}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nMaintaining a\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering}{cluster}\nis a big responsibility. It includes deploying new and updated plugins\nand modules, applying\n\\href{/docs/7-2/deploy/-/knowledge_base/d/maintaining-liferay}{fix\npacks}, making configuration changes, and more. Maximizing server uptime\nand minimizing risks take priority when applying changes. Liferay DXP\nsupports using standard cluster maintenance techniques.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/using-rolling-restarts}{Rolling\n  restarts}: Nodes are shut down and updated one at a time.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/other-cluster-update-techniques}{Blue-green\n  deployment}: Blue-green involves duplicating the current environment\n  (\\emph{blue} environment), updating the duplicate (\\emph{green}\n  environment), and cutting over users to the updated environment\n  (green).\n\\end{itemize}\n\nThe techniques are compared below.\n\n\\textbf{Cluster Update Techniques}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.1429}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.4762}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.3810}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nUpdate\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n~Rolling Restart\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n~Blue-green\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nPlugin/module installation & Supported & Supported \\\\\nPlugin/module update (backward-compatible data/schema changes) &\nSupported & Supported \\\\\nPlugin/module update (non-backward-compatible data/schema changes)\n\\hyperref[one]{1} & Not supported & Supported \\\\\nFix pack installation and removal (revertable fix pack) & Supported &\nSupported \\\\\nFix pack installation (non-revertible fix pack) & Not supported &\nSupported \\\\\nCluster code changes \\hyperref[two]{2} & Not supported & Supported \\\\\nPortal property changes & Supported & Supported \\\\\nSystem Setting changes via configuration admin files & Supported &\nSupported \\\\\nApplication server updates & Supported & Supported \\\\\nJVM setting changes & Supported & Supported \\\\\nNew Java version (minor) & Supported & Supported \\\\\nNew Java version (major) & Not supported & Supported \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n{[}1{]} Data and data schema changes that are not backward-compatible\ninclude, but are not limited to these:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Modifying data in existing columns\n\\item\n  Dropping columns\n\\item\n  Changing column types\n\\item\n  Changing data formats used in columns (such as changing from XML to\n  JSON)\n\\item\n  Updating a Service Builder service module's data schema to a version\n  outside of the module's required data schema range. A module's\n  \\texttt{Liferay-Require-SchemaVersion} (specified in its\n  \\texttt{bnd.bnd}) must match the module's schema version value in the\n  \\texttt{Release\\_} table. Installing a module with a new schema\n  version updates the \\texttt{Release\\_} table with that schema version\n  and triggers a data upgrade process. If you install such a module on\n  one node, the schema version in the \\texttt{Release\\_} table no longer\n  matches the \\texttt{Liferay-Require-SchemaVersion} of the modules on\n  the other nodes, and the module's Service Builder services become\n  unavailable until the module is installed on the other nodes. Such\n  changes cannot be reverted: the database must be restored from a\n  backup. These schema version changes must be applied while all nodes\n  are shut down.\n\\end{itemize}\n\n{[}2{]} Cluster communication must stay intact. For this reason, cluster\ncode must not be updated in rolling restarts. The Customer Portal\nidentifies DXP fix packs that contain such changes as non-revertible.\nHere are packages you must not change in rolling restarts:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.liferay.portal.kernel.cluster}\n\\item\n  \\texttt{com.liferay.portal.kernel.cluster.*}\n\\item\n  \\texttt{com.liferay.portal.kernel.exception.NoSuchClusterGroupException}\n\\item\n  \\texttt{com.liferay.portal.kernel.scheduler.multiple}\n\\item\n  \\texttt{com.liferay.portal.kernel.scheduler.multiple.*}\n\\item\n  \\texttt{com.liferay.portal.cache.multiple}\n\\item\n  \\texttt{com.liferay.portal.cache.multiple.*}\n\\item\n  \\texttt{com.liferay.portal.scheduler.multiple}\n\\item\n  \\texttt{com.liferay.portal.scheduler.multiple.*}\n\\end{itemize}\n\nSince eligible changes should be done with rolling restarts, it's\nexplained first.\n\n\\chapter{Rolling Restarts}\\label{rolling-restarts}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe rolling restart cluster maintenance process involves shutting down\nand updating nodes one at a time (while the other nodes are running)\nuntil they're all updated. It maximizes uptime while you update your\ncluster. Rolling restarts can be used in container and image based\nenvironments.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Rolling restart does not include concepts for blue-green\n(separate, but identical environments) architectures, as these concepts\nspecifically address multi-cluster style developments.\n\n\\noindent\\hrulefill\n\nHere are the rolling restart steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Shut down one cluster node (JVM instance).\n\\item\n  Update/modify the deployment for that node (see the maintenance\n  scenarios that follow).\n\\item\n  Start the node.\n\\item\n  Repeat these steps for all other cluster nodes.\n\\end{enumerate}\n\nMaintenance scenarios vary in how they behave in rolling restarts. For\nexample, UI changes in a plugin update are only visible on the updated\nnodes. Users on nodes that haven't been updated don't see the UI\nchanges. Maintenance scenarios might have specific cases that cannot be\nperformed in rolling restarts---the scenario descriptions mention these\ncases.\n\nThe maintenance scenarios eligible for rolling restart are described\nbelow.\n\n\\section{New Modules and Plugins}\\label{new-modules-and-plugins}\n\nFor a new plugin or module (one that does not already exist in the\ncluster) to be eligible for rolling restart it must not modify data, or\ndelete or rename database columns in a way that breaks compatibility\nwith existing plugins or modules.\n\n\\section{Updating Existing Modules and\nPlugins}\\label{updating-existing-modules-and-plugins}\n\nFor a new version of an existing plugin or module to be eligible for\nrolling restart, it must not modify data or delete or rename database\ncolumns in a way that breaks compatibility with the existing version of\nthe plugin or module.\n\n\\section{Applying Fix Packs (DXP\nonly)}\\label{applying-fix-packs-dxp-only}\n\nThe Customer Portal identifies\n\\href{/docs/7-2/deploy/-/knowledge_base/d/maintaining-liferay}{fix\npacks} that are not revertible, and therefore ineligible for rolling\nrestart. All other fix packs are eligible.\n\n\\section{Reverting Fix Packs (DXP\nonly)}\\label{reverting-fix-packs-dxp-only}\n\nRevertible fix packs can be removed in rolling restarts.\n\n\\section{\\texorpdfstring{Portal Properties controlled by\n\\texttt{portal-ext.properties}}{Portal Properties controlled by portal-ext.properties}}\\label{portal-properties-controlled-by-portal-ext.properties}\n\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html}{Portal\nProperties} file changes can be applied in rolling restarts.\n\n\\section{System Settings controlled by Configuration Admin\nFiles}\\label{system-settings-controlled-by-configuration-admin-files}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files}{System\nconfiguration} files can be applied in rolling restarts.\n\n\\section{Application Server or JVM setting\nmodifications}\\label{application-server-or-jvm-setting-modifications}\n\nModifications to application server and JVM settings can be done in\nrolling restarts.\n\n\\section{Java Version Updates}\\label{java-version-updates}\n\nMinor version updates of Java can be applied in rolling restarts. Major\nversion updates are not supported in rolling restarts, and should\ninstead be done when all cluster nodes are shut down.\n\nAll rolling restart eligible updates can be applied using the rolling\nrestart steps listed earlier. Other updates must be done differently as\ndescribed next.\n\n\\section{Related Topics}\\label{related-topics-1}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering}{Liferay\nDXP Clustering}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/maintaining-liferay}{Maintaining\nLiferay DXP}\n\n\\chapter{Blue-Green Deployment}\\label{blue-green-deployment}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nBlue-green is a deployment technique in which you duplicate your\nproduction environment (the \\emph{blue} environment) and modify the\nduplicate (the \\emph{green} environment) with software and data changes.\nWhen you've successfully tested the green environment, you cut users\nover from the blue environment to the green environment. Blue-green\neliminates system down time.\n\nData schema and data changes require special attention. Custom\nplugin/module data schema changes that break compatibility with existing\ncode must be introduced over several releases in which the data is\ntransitioned and maintained in old and new columns until the old columns\nare unnecessary.\n\nData and schema changes require these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new column.\n\\item\n  Copy the data to the new column.\n\\item\n  Maintain both columns until the old column is no longer used by any\n  cluster nodes.\n\\item\n  Delete the column in the next release.\n\\end{enumerate}\n\nFor more information, refer to these blue-green deployment articles:\n\n\\begin{itemize}\n\\item\n  \\href{http://martinfowler.com/bliki/BlueGreenDeployment.html}{BlueGreenDeployment}\n\\item\n  \\href{https://www.thoughtworks.com/insights/blog/implementing-blue-green-deployments-aws}{Implementing\n  Blue-Green Deployments with AWS}\n\\end{itemize}\n\n\\section{Related Topics}\\label{related-topics-2}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/using-rolling-restarts}{Rolling\nRestarts}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering}{Liferay\nDXP Clustering}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/maintaining-liferay}{Maintaining\nLiferay DXP}\n\n\\chapter{Configuring Remote Staging in a Clustered\nEnvironment}\\label{configuring-remote-staging-in-a-clustered-environment}\n\nIf you're running Liferay DXP as a\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering}{clustered\nenvironment} and you want to use remote staging, you must configure it\nproperly for a seamless experience. In this tutorial, you'll learn how\nto set up remote staging in an example clustered environment scenario.\nThe example environment assumes you have\n\n\\begin{itemize}\n\\tightlist\n\\item\n  a Staging instance with database configurations and a file repository\n  different from the cluster nodes.\n\\item\n  a balancer responsible for managing the traffic flow between the\n  cluster's nodes.\n\\item\n  two nodes that call two Liferay app servers (e.g., \\emph{App Server 1}\n  and \\emph{App Server 2}), both of which are connected to the same\n  database.\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/remote-staging-clustering.png}}\n\\caption{This is the assumed setup for your clustered environment.}\n\\end{figure}\n\nThe steps below also assume your web tier, application tier, and cluster\nenvironment are already configured. You may need to adjust the\nconfigurations in this tutorial to work with your specific\nconfiguration.\n\nLet's begin!\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  You must secure the communication made between your nodes and Staging\n  server. Add the following property to both app servers' and Staging\n  server's \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\ntunneling.servlet.shared.secret=[secret]\n\\end{verbatim}\n\n  This secret key denies other portals access to your configured portal\n  servers. If you'd like to set your secret key using hexadecimal\n  encoding, also set the following property in your\n  \\texttt{portal-ext.properties} files:\n\n\\begin{verbatim}\ntunneling.servlet.shared.secret.hex=true\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The following key lengths are supported by the available\n encryption algorithms:\n \n - *AES:* 128, 192, and 256-bit keys\n - *Blowfish:* 32-448 bit keys\n - *DESede (Triple DES):* 56, 112, or 168 bit keys (Liferay places an\n   artificial limit on the minimum key length and does not support the 56-bit\n   key length)\n \n For example, you can use [OpenSSL](https://www.openssl.org/) to generate a\n 128-bit AES key:\n \n     openssl enc -aes-128-cbc -k abc123 -P -md sha1\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  You must allow the connection between the configured IPs of your app\n  servers and the Staging server. Open your remote Liferay server's\n  \\texttt{portal-ext.properties} file and add the following properties:\n\n\\begin{verbatim}\ntunnel.servlet.hosts.allowed=127.0.0.1,SERVER_IP,[STAGING_IP]\ntunnel.servlet.https.required=false\n\\end{verbatim}\n\n  The \\texttt{{[}STAGING\\_IP{]}} variable must be replaced by the\n  staging server's IP addresses. The \\texttt{SERVER\\_IP} constant can\n  remain set for this property; it's automatically replaced by the\n  Liferay server's IP addresses.\n\\item\n  If you're validating IPv6 addresses, you must configure the app\n  server's JVM to not force the usage of IPv4 addresses. For example, if\n  you're using Tomcat, add the following attribute in the\n  \\texttt{\\$TOMCAT\\_HOME\\textbackslash{}bin\\textbackslash{}setenv.{[}bat\\textbar{}sh{]}}\n  file.\n\n\\begin{verbatim}\n     `-Djava.net.preferIPv4Stack=false`\n\\end{verbatim}\n\\item\n  Restart both app servers for the new properties to take effect.\n\\item\n  Configure the \\emph{TunnelAuthVerifier} property for your nodes' app\n  servers. There are two ways to do this:\n\n  \\begin{itemize}\n  \\item\n    \\textbf{Use a \\texttt{.config} file (recommended):} In the\n    \\texttt{\\$LIFERAY\\_HOME/osgi/configs} folder of one of your node\n    Liferay DXP instances, create (if necessary) a\n    \\texttt{com.liferay.portal.security.auth.verifier.tunnel.module.configuration.TunnelAuthVerifierConfiguration-default.config}\n    file and insert the properties listed below. Creating one\n    \\texttt{.config} file configures all cluster nodes the same way. For\n    more information on \\texttt{.config} files, see the\n    \\href{/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files}{Understanding\n    System Configuration Files} article.\n\n\\begin{verbatim}\n  enabled=true\n  hostsAllowed=127.0.0.1,SERVER_IP,STAGING_IP\n  serviceAccessPolicyName=SYSTEM_USER_PASSWORD\n  urlsIncludes=/api/liferay/do\n\\end{verbatim}\n  \\item\n    \\textbf{Via System Settings:} Navigate to the \\emph{Control Panel} →\n    \\emph{Configuration} → \\emph{System Settings} → \\emph{Foundation} →\n    \\emph{Tunnel Auth Verifiers}. Click on the \\emph{/api/liferay/do}\n    configuration entry and add the Staging IP address to the\n    \\emph{Hosts allowed} field. If you choose to configure the\n    \\emph{TunnelAuthVerifier} this way, you \\textbf{must} do this for\n    all nodes (e.g., App Server 1 and App Server 2).\n  \\end{itemize}\n\\item\n  On your Staging instance, navigate to the Site Administration portion\n  of the Product Menu and select \\emph{Publishing} → \\emph{Staging}.\n  Then select \\emph{Remote Live}.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/remote-staging-menu.png}}\n  \\caption{When selecting the Remote Staging radio button, you're given\n  a list of options to configure.}\n  \\end{figure}\n\\item\n  For the Remote Host/IP field, insert the balancer's IP of your web\n  tier. Configuring the Staging instance with the balancer's IP ensures\n  the availability of the environment at the time of publication from\n  staging to live.\n\\item\n  Enter the port on which the balancer is running into the Remote Port\n  field.\n\\item\n  Insert the remote site ID of your app servers into the Remote Site ID\n  field. The site ID of all your app servers are the same since they are\n  configured for the same database and are shared between nodes.\n\n  Navigate to the Site Administration portion of the Product Menu and\n  select \\emph{Configuration} → \\emph{Site Settings} to find the site\n  ID.\n\\item\n  Save the Remote Live settings.\n\\end{enumerate}\n\nThat's it! You've configured remote staging in your clustered\nenvironment.\n\n\\chapter{Content Delivery Network}\\label{content-delivery-network}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nA Content Delivery Network (CDN) is an network of servers deployed in\nmultiple data centers that contain your static content. When users hit\nyour site, that static content is loaded from a server with geographical\nproximity to the user, speeding up requests.\n\nHere, you'll first discover the perks of using a CDN and learn about\ngeneral guidelines for using a CDN with Liferay DXP. Then, you'll\nconfigure a CDN. It's time to distribute your content around the world!\n\n\\section{Using CDN for Performance\nEnhancements}\\label{using-cdn-for-performance-enhancements}\n\nA CDN serves static web resources to users. These resources (images, CSS\nfiles, JavaScript files, etc.) are stored on multiple servers around the\nworld. When requested, the resources are retrieved from the server\nnearest to the user.\n\nThe CDN functions as a caching proxy. This means that once static\ncontent is copied to a local server, it is stored in a cache for quick\nand easy retrieval. This drastically improves latency time, because\nbrowsers can download static resources from a local server down the\nstreet instead of halfway around the world. A user's request to the CDN\nfor content is directed to specific server machine based on an algorithm\nthat finds the server closest to the user. The figure below shows a\nvisual representation of using geographical proximity to improve\nlatency.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/cdn-map.png}}\n\\caption{The red lines on the map represent the required distances\ntraveled by requests from a server to the user. Using CDN allows a user\nto request static resources from a much closer local server, improving\ndownload times.}\n\\end{figure}\n\nBecause of the reduced wait time for requests and reduced load on your\napplication server, a CDN is a great option to improve performance.\nUsing a CDN with Liferay DXP, however, has some restrictions.\n\n\\section{Liferay CDN Requirements}\\label{liferay-cdn-requirements}\n\nLiferay DXP only works with CDNs that can dynamically retrieve requested\nresources. Dynamic resources change over time or via interaction with\nend users and thus cannot be cached. For this reason, check with your\nCDN provider to make sure you don't have to upload anything manually in\norder for the CDN to work. The CDN must automatically fetch the content.\n\nThe CDN must work like a transparent proxy. A request first goes to the\nCDN. If the CDN doesn't have the requested resource, the CDN makes an\nidentical request back to the origin (Liferay DXP), caches the resource,\nthen serves the resource.\n\nOnce you're using a CDN (see below), it serves both portal resources and\nplugin resources (e.g., theme resources or JavaScript files referenced\nfrom a plugin's \\texttt{liferay-portlet.xml} file). The CDN only serves\nresources that are included in a plugin. It does not serve resources\nthat are dynamically loaded from external sources.\n\nTo get the CDN URL for a resource, developers should replace the portal\nhost in the resource path with\n\\texttt{themeDisplay.getCDNDynamicResourcesHost()}. Prefix resources\nwith the CDN host name. Don't manually upload any resources to the CDN\nor put anything on the CDN which requires permission checking or complex\npolicy access.\n\nThere are several portal properties for configuring your CDN to suit\nyour needs. You'll learn how to do this next.\n\n\\section{Configuring Liferay DXP to Use a\nCDN}\\label{configuring-liferay-dxp-to-use-a-cdn}\n\nNow that you understand what a CDN accomplishes and how it's used, it's\ntime to set one up for yourself. You can set your CDN and its properties\nusing two different methods:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  By editing your portal properties file\n\\item\n  By using the Control Panel\n\\end{enumerate}\n\nTo configure your CDN via a properties file, create a\n\\texttt{portal-ext.properties} file in your\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\nfolder and set the appropriate\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Content\\%20Delivery\\%20Network}{Content\nDelivery Network properties}.\n\nOnce you configure your CDN host, Liferay DXP generates URLs to the\nstatic assets that replace the old host with your new CDN host so they\nare automatically cached and served afterwards by the CDN.\n\nTo configure your CDN in the Control Panel, navigate to \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{Instance Settings}. In the main\nconfiguration, there are three fields related to CDNs:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{CDN Host HTTP}\n\\item\n  \\emph{CDN Host HTTPS}\n\\item\n  \\emph{Enable CDN Dynamic Resources}\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/cdn-control-panel.png}}\n\\caption{The Control Panel lets you configure your portal's CDN.}\n\\end{figure}\n\nThese properties are exactly the same as the ones you can specify in\nyour \\texttt{portal-ext.properties}. Make sure to visit the Content\nDelivery Network section of the\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Content\\%20Delivery\\%20Network}{portal.properties}\nreference document if you don't know how to fill in the CDN fields. Once\nyou're finished, click \\emph{Save} and your old host is replaced with\nyour new CDN host for static content.\n\nAs you can see, configuring a CDN is easy and can drastically reduce\nlatency time and improve performance.\n\n\\chapter{Tuning Guidelines}\\label{tuning-guidelines}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAlthough setting names may differ, these concepts apply to most\napplication servers. To keep things simple, Tomcat is used as the\nexample. For other application servers, consult the provider's\ndocumentation for specific settings.\n\nHere are the tuning topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Database Connection Pool\n\\item\n  Deactivating Development Settings in the JSP Engine\n\\item\n  Thread Pool\n\\end{itemize}\n\n\\section{Database Connection Pool}\\label{database-connection-pool}\n\nThe database connection pool should be roughly 30-40\\% of the thread\npool size. It provides a connection whenever Liferay DXP needs to\nretrieve data from the database (e.g., user login). If the pool size is\ntoo small, requests queue in the server waiting for database\nconnections. If the size is too large, however, idle database\nconnections waste resources. As with thread pools, monitor these\nsettings and adjust them based on your performance tests.\n\nIn Tomcat, the connection pools are configured in the Resource elements\nin \\texttt{\\$CATALINA\\_HOME/conf/Catalina/localhost/ROOT.xml}. Liferay\nEngineering tests with this configuration:\n\n\\begin{verbatim}\n<Resource auth=\"Container\"         \n    description=\"Digital Enterprise DB Connection\"   \n    driverClass=\"[place the driver class name here]\"   \n    maxPoolSize=\"75\"   \n    minPoolSize=\"10\"           \n    acquireIncrement=\"5\"   \n    name=\"jdbc/LiferayPool\"  \n    user=\"[place your user name here]\"   \n    password=\"[place your password here]\"           \n    factory=\"org.apache.naming.factory.BeanFactory\"\n    type=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\n    jdbcUrl=\"[place the URL to your database here]\"/>\n\\end{verbatim}\n\nThis configuration starts with 10 threads and increments by 5 as needed\nto a maximum of 75 connections in the pool.\n\nThere are a variety of database connection pool providers, including\nDBCP, C3P0, HikariCP, and Tomcat. You may also configure the Liferay\nJDBC settings in your\n\\href{https://docs.liferay.com/ce/portal/7.2-latest/propertiesdoc/portal.properties.html}{\\texttt{portal-ext.properties}}\nfile. For example JDBC connection values, please see\n\\href{/docs/7-2/deploy/-/knowledge_base/d/database-templates}{Database\nTemplates}\n\n\\section{Deactivating Development Settings in the JSP\nEngine}\\label{deactivating-development-settings-in-the-jsp-engine}\n\nMany application servers' JSP Engines are in development mode by\ndefault. Deactivate these settings prior to entering production:\n\n\\textbf{Development mode:} This makes the JSP container poll the file\nsystem for changes to JSP files. Since you won't change JSPs on the fly\nlike this in production, turn off this mode.\n\n\\textbf{Mapped File:} Generates static content with one print statement\nversus one statement per line of JSP text.\n\nTo disable these in Tomcat, for example, update the\n\\texttt{\\$CATALINA\\_HOME/conf/web.xml} file's JSP servlet configuration\nto this:\n\n\\begin{verbatim}\n<servlet>   \n    <servlet-name>jsp</servlet-name>\n    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>   \n    <init-param>    \n        <param-name>development</param-name>    \n        <param-value>false</param-value>   \n    </init-param>   \n    <init-param>    \n        <param-name>mappedFile</param-name>    \n        <param-value>false</param-value>   \n    </init-param>   \n    <load-on-startup>3</load-on-startup>\n</servlet>\n\\end{verbatim}\n\nDevelopment mode and mapped files are disabled.\n\n\\section{Thread Pool}\\label{thread-pool}\n\nEach request to the application server consumes a worker thread for the\nduration of the request. When no threads are available to process\nrequests, the request is queued to wait for the next available worker\nthread. In a finely tuned system, the number of threads in the thread\npool are balanced with the total number of concurrent requests. There\nshould not be a significant amount of threads left idle to service\nrequests.\n\nUse an initial thread pool setting of 50 threads and then monitor it\nwithin your application server's monitoring consoles. You may wish to\nuse a higher number (e.g., 250) if your average page times are in the\n2-3 second range. Too few threads in the thread pool might queue\nexcessive requests; too many threads can cause excessive context\nswitching.\n\nIn Tomcat, the thread pools are configured in the\n\\texttt{\\$CATALINA\\_HOME/conf/server.xml} file's \\texttt{Connector}\nelement. The\n\\href{https://tomcat.apache.org/tomcat-9.0-doc/config/http.html}{Apache\nTomcat documentation} provides more details. Liferay Engineering tests\nwith this configuration:\n\n\\begin{verbatim}\n<Connector maxThreads=\"75\" minSpareThreads=\"50\"\n    maxConnections=\"16384\" port=\"8080\"     \n    connectionTimeout=\"600000\" redirectPort=\"8443\"\n    URIEncoding=\"UTF-8\"  socketBuffer=\"-1\"     \n    maxKeepAliveRequests=\"-1\" address=\"xxx.xxx.xxx.xxx\"/>\n\\end{verbatim}\n\nAdditional tuning parameters around Connectors are available, including\nthe connector types, the connection timeouts, and TCP queue. Consult\nyour application server's documentation for further details.\n\n\\chapter{Java Virtual Machine Tuning}\\label{java-virtual-machine-tuning}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nJava Virtual Machine (JVM) tuning primarily focuses on adjusting the\ngarbage collector and the Java memory heap. We used Oracle's 1.8 JVM for\nthe reference architecture. You may choose other supported JVM versions\nand implementations. Please consult the\n\\href{https://web.liferay.com/group/customer/dxp/support/compatibility-matrix}{Liferay\nDXP Compatibility Matrix} for additional compatible JVMs.\n\nHere are the JVM tuning topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[garbage-collector]{Garbage Collector}\n\\item\n  \\hyperref[code-cache]{Code Cache}\n\\item\n  \\hyperref[java-heap]{Java Heap}\n\\item\n  \\hyperref[jvm-advanced-options]{JVM Advanced Options}\n\\end{itemize}\n\nGarbage collection is first.\n\n\\section{Garbage Collector}\\label{garbage-collector}\n\nChoosing the appropriate garbage collector (GC) helps improve the\nresponsiveness of your Liferay DXP deployment. Use the concurrent low\npause collectors:\n\n\\begin{verbatim}\n-XX:+UseParNewGC -XX:ParallelGCThreads=16 -XX:+UseConcMarkSweepGC\n-XX:+CMSParallelRemarkEnabled -XX:+CMSCompactWhenClearAllSoftRefs\n-XX:CMSInitiatingOccupancyFraction=85 -XX:+CMSScavengeBeforeRemark\n\\end{verbatim}\n\nYou may choose from other available GC algorithms, including parallel\nthroughput collectors and G1 collectors. Start tuning using parallel\ncollectors in the new generation and concurrent mark sweep (CMS) in the\nold generation.\n\n\\textbf{Note:} the \\texttt{ParallelGCThreads} value (e.g.,\n\\texttt{ParallelGCThreads=16}) varies based on the type of CPUs\navailable. Set the value according to CPU specification. On Linux\nmachines, report the number of available CPUs by running\n\\texttt{cat\\ /proc/cpuinfo}.\n\n\\textbf{Note:} There are additional ``new'' algorithms like G1, but\nLiferay Engineering's tests for G1 indicated that it does not improve\nperformance. Since your application performance may vary, you should add\nG1 to your testing and tuning plans.\n\n\\section{Code Cache}\\label{code-cache}\n\nJava's just-in-time (JIT) compiler generates native code to improve\nperformance. The default size is \\texttt{48m}. This may not be\nsufficient for larger applications. Too small a code cache reduces\nperformance, as the JIT isn't able to optimize high frequency methods.\nFor Liferay DXP, start with \\texttt{64m} for the initial code cache\nsize.\n\n\\begin{verbatim}\n-XX:InitialCodeCacheSize=64m -XX:ReservedCodeCacheSize=96m\n\\end{verbatim}\n\nExamine the efficacy of the parameter changes by adding the following\nlogging parameters:\n\n\\begin{verbatim}\n-XX:+PrintCodeCache -XX:+PrintCodeCacheOnCompilation\n\\end{verbatim}\n\n\\section{Java Heap}\\label{java-heap}\n\nWhen most people think about tuning the Java memory heap, they think of\nsetting the maximum and minimum memory of the heap. Unfortunately, most\ndeployments require far more sophisticated heap tuning to obtain optimal\nperformance, including tuning the young generation size, tenuring\ndurations, survivor spaces, and many other JVM internals.\n\nFor most systems, it's best to start with at least the following memory\nsettings:\n\n\\begin{verbatim}\n-server -XX:NewSize=700m -XX:MaxNewSize=700m -Xms2048m -Xmx2048m -XX:MetaspaceSize=512m\n-XX:MaxMetaspaceSize=512m -XX:SurvivorRatio=6 -XX:TargetSurvivorRatio=9 -XX:MaxTenuringThreshold=15\n\\end{verbatim}\n\nOn systems that require large heap sizes (e.g., above 4GB), it may be\nbeneficial to use large page sizes. You can activate large page sizes\nlike this:\n\n\\begin{verbatim}\n-XX:+UseLargePages -XX:LargePageSizeInBytes=256m\n\\end{verbatim}\n\nYou may choose to specify different page sizes based on your application\nprofile.\n\n\\textbf{Note:} To use large pages in the JVM, you must configure your\nunderlying operating system to activate them. In Linux, run\n\\texttt{cat\\ /proc/meminfo} and look at ``huge page'' items.\n\n\\noindent\\hrulefill\n\n\\textbf{Caution:} Avoid allocating more than 32GB to your JVM heap. Your\nheap size should be commensurate with the speed and quantity of\navailable CPU resources.\n\n\\noindent\\hrulefill\n\n\\section{JVM Advanced Options}\\label{jvm-advanced-options}\n\nThe following advanced JVM options were also applied in the Liferay\nbenchmark environment:\n\n\\begin{verbatim}\n-XX:+UseLargePages -XX:LargePageSizeInBytes=256m \n-XX:+UseCompressedOops -XX:+DisableExplicitGC -XX:-UseBiasedLocking \n-XX:+BindGCTaskThreadsToCPUs -XX:UseFastAccessorMethods\n\\end{verbatim}\n\nPlease consult your JVM documentation for additional details on advanced\nJVM options.\n\nCombining the above recommendations together, makes this configuration:\n\n\\begin{verbatim}\n-server -XX:NewSize=1024m -XX:MaxNewSize=1024m -Xms4096m\n-Xmx4096m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m\n-XX:SurvivorRatio=12 -XX:TargetSurvivorRatio=90\n-XX:MaxTenuringThreshold=15 -XX:+UseLargePages \n-XX:LargePageSizeInBytes=256m -XX:+UseParNewGC \n-XX:ParallelGCThreads=16 -XX:+UseConcMarkSweepGC \n-XX:+CMSParallelRemarkEnabled -XX:+CMSCompactWhenClearAllSoftRefs\n-XX:CMSInitiatingOccupancyFraction=85 -XX:+CMSScavengeBeforeRemark \n-XX:+UseLargePages -XX:LargePageSizeInBytes=256m\n-XX:+UseCompressedOops -XX:+DisableExplicitGC -XX:-UseBiasedLocking\n-XX:+BindGCTaskThreadsToCPUs -XX:+UseFastAccessorMethods\n-XX:InitialCodeCacheSize=32m -XX:ReservedCodeCacheSize=96m\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Caution:} The above JVM settings should formulate a starting\npoint for your performance tuning. Every system's final parameters vary\ndue to many factors, including number of current users and transaction\nspeed.\n\n\\noindent\\hrulefill\n\nMonitor the garbage collector statistics to ensure your environment has\nsufficient allocations for metaspace and also for the survivor spaces.\nUsing the configuration above in the wrong environment could result in\ndangerous runtime scenarios like out of memory failures. Improperly\ntuned survivor spaces also lead to wasted heap space.\n\n\\chapter{Installing a Search Engine}\\label{installing-a-search-engine}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nA search engine is a critical component of your Liferay DXP\ninstallation. If you're here, you probably know the basics already and\nwant to configure a search engine for your Liferay DXP deployment.\n\nElasticsearch, a highly scalable, full-text search engine, is installed\nby default, as an embedded server. Elasticsearch is well-supported and\nalmost certainly meets any search and indexing need you have, but you\nmust not use the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/elasticsearch\\#embedded-vs-remote-operation-mode}{embedded\nversion in your production deployment}.\n\nLearn to configure a remote Elasticsearch server or cluster\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch}{here}.\n\n\\href{http://lucene.apache.org/solr}{Solr} is another capable and\npopular search engine supported in Liferay DXP.\n\nLearn to configure a remote Solr server or cluster\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-solr}{here}. But\nfirst, make sure you understand the disparity in functionality between\nthe supported search engines.\n\n\\section{Choosing a Search Engine}\\label{choosing-a-search-engine}\n\nElasticsearch and Solr are both supported, but there are limitations to\nLiferay's Solr integration. To make use of some features, you must\nchoose Elasticsearch.\n\n\\section{End User Feature Limitations of Liferay's Solr\nIntegration}\\label{end-user-feature-limitations-of-liferays-solr-integration}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://learn.liferay.com/commerce-2.x/index.html}{Liferay\n  Commerce}\n\\item\n  \\href{https://help.liferay.com/hc/en-us/articles/360029042071-Workflow-Metrics-The-Service-Level-Agreement-SLA-}{Workflow\n  Metrics}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/filtering-search-results-with-the-custom-filter-widget}{Custom\n  Filter search widget}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/low-level-search-options-searching-additional-or-alternate-indexes}{The\n  Low Level Search Options widget}\n\\item\n  \\href{https://help.liferay.com/hc/en-us/articles/360034473872-Search-Tuning-Customizing-Search-Results}{Search\n  Tuning: Customizing Search Results}\n\\item\n  \\href{https://help.liferay.com/hc/en-us/articles/360034473852-Search-Tuning-Synonym-Sets}{Search\n  Tuning: Synonyms}\n\\end{itemize}\n\n\\section{Developer Feature Limitations of Liferay's Solr\nIntegration}\\label{developer-feature-limitations-of-liferays-solr-integration}\n\nImplementation for the following APIs may be added in the future, but\nthey are not currently supported by Liferay's Solr connector.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  From Portal Core (Module: \\texttt{portal-kernel}, Artifact:\n  \\texttt{com.liferay.portal.kernel}):\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{com.liferay.portal.kernel.search.generic.NestedQuery}\n  \\item\n    \\texttt{com.liferay.portal.kernel.search.filter}:\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{ComplexQueryPart}\n    \\item\n      \\texttt{GeoBoundingBoxFilter}\n    \\item\n      \\texttt{GeoDistanceFilter}\n    \\item\n      \\texttt{GeoDistanceRangeFilter}\n    \\item\n      \\texttt{GeoPolygonFilter}\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  From the Portal Search API (Module: \\texttt{portal-search-api},\n  Artifact: \\texttt{com.liferay.portal.search.api}):\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{com.liferay.portal.search.filter}:\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{ComplexQueryPart}\n    \\item\n      \\texttt{TermsSetFilter}\n    \\end{itemize}\n  \\item\n    \\texttt{com.liferay.portal.search.geolocation.*}\n  \\item\n    \\texttt{com.liferay.portal.search.highlight.*}\n  \\item\n    \\texttt{com.liferay.portal.search.query.function.*}\n  \\item\n    \\texttt{com.liferay.portal.search.query.*}:\n  \\item\n    \\texttt{com.liferay.portal.search.script.*}\n  \\item\n    \\texttt{com.liferay.portal.search.significance.*}\n  \\item\n    \\texttt{com.liferay.portal.search.sort.*}: only\n    \\texttt{Sort},\\texttt{FieldSort}, and \\texttt{ScoreSort} are\n    supported\n  \\end{itemize}\n\\item\n  Portal Search Engine Adapter API (Module:\n  \\texttt{portal-search-engine-adapter-api}, Artifact:\n  \\texttt{com.liferay.portal.search.engine.adapter.api})\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{com.liferay.portal.search.engine.adapter.cluster.*}\n  \\item\n    \\texttt{com.liferay.portal.search.engine.adapter.document.UpdateByQueryDocumentRequest}\n  \\item\n    \\texttt{com.liferay.portal.search.engine.adapter.index.*}: only\n    \\texttt{RefreshIndexRequest} is supported\n  \\item\n    \\texttt{com.liferay.portal.search.engine.adapter.search.*}:\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{MultisearchSearchRequest}\n    \\item\n      \\texttt{SuggestSearchRequest}\n    \\end{itemize}\n  \\item\n    \\texttt{com.liferay.portal.search.engine.adapter.snapshot.*}\n  \\end{itemize}\n\\end{itemize}\n\nLiferay Commerce requires the \\texttt{TermsSetFilter} implementation,\nonly available in the Elasticsearch connector.\n\n\\section{Elasticsearch Java Distribution\nCompatibility}\\label{elasticsearch-java-distribution-compatibility}\n\nAnother factor to consider in your search engine selection is JDK\nversion. The search engine and Liferay DXP must use the same Java\nversion and distribution (e.g., Oracle Open JRE 1.8.0\\_201). Consult the\n\\href{https://www.elastic.co/support/matrix\\#matrix_jvm}{Elasticsearch\ncompatibility matrix} and the\n\\href{https://help.liferay.com/hc/en-us/sections/360002103292-Compatibility-Matrix}{Liferay\nDXP compatibility matrix} to learn more about supported JDK\ndistributions and versions. This consideration is not necessary for\nSolr, because no JVM level serialization happens between the servers.\nAll communication occurs at the HTTP level.\n\n\\chapter{Elasticsearch}\\label{elasticsearch}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nElasticsearch is an open source, highly scalable, full-text search and\nanalytics engine.\n\nBy default, Elasticsearch runs as an embedded search engine, which is\nuseful for development and testing but is not supported in production.\nIn production environments you must run Elasticsearch in remote mode, as\na separate server or cluster. This guide walks you through the process\nof configuring Elasticsearch in remote mode.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/search-admin-engineinfo.png}}\n\\caption{To see information about the currently connected search engine,\ngo to \\emph{Control Panel} → \\emph{Configuration} → \\emph{Search}.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Although Elasticsearch 6.5 is shipped as the embedded\nElasticsearch server version, Elasticsearch 7 is the most recent\nsupported Elasticsearch version for 7.0. Installing Elasticsearch 7\nrequires that you are running Service Pack 1/Fix Pack 2 or later (GA2 or\nlater for CE users). Elasticsearch 6.8.x is also supported. See the\n\\href{https://help.liferay.com/hc/en-us/articles/360016511651}{compatibility\nmatrix for exact versions}.\n\n\\noindent\\hrulefill\n\nIf you'd rather use Solr, it's also supported. See the documentation on\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-solr}{Installing\nSolr} if you're interested.\n\nTo get up and running quickly with Elasticsearch as a remote server,\nrefer to the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch}{Installing\nElasticsearch article}. Included there are basic instructions for\ninstalling and configuring Elasticsearch in a single server environment.\nAdditional articles include more details and information on configuring\nand tuning Elasticsearch.\n\nThese terms are useful to understand as you read this guide:\n\n\\begin{itemize}\n\\item\n  \\emph{Elasticsearch Home} refers to the root folder of your unzipped\n  Elasticsearch installation (for example,\n  \\texttt{elasticsearch-7.4.1}).\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{\\emph{Liferay\n  Home}} refers to the root folder of your Liferay DXP installation. It\n  contains the \\texttt{osgi}, \\texttt{deploy}, \\texttt{data}, and\n  \\texttt{license} folders, among others.\n\\end{itemize}\n\n\\section{Embedded vs.~Remote Operation\nMode}\\label{embedded-vs.-remote-operation-mode}\n\nWhen you start Liferay DXP, this message is displayed in the log:\n\n\\begin{verbatim}\n2019-04-29 09:59:02.276 WARN  [Elasticsearch initialization thread][EmbeddedElasticsearchConnection:288] Liferay is configured to use embedded Elasticsearch as its search engine. Do NOT use embedded Elasticsearch in production. Embedded Elasticsearch is useful for development and demonstration purposes. Refer to the documentation for details on the limitations of embedded Elasticsearch. Remote Elasticsearch connections can be configured in the Control Panel.\n\\end{verbatim}\n\nWhen you start Liferay DXP, Elasticsearch is already running in embedded\nmode. Liferay DXP runs an Elasticsearch node in the same JVM so it's\neasy to test-drive with minimal configuration. Running both servers in\nthe same process has drawbacks:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Elasticsearch must use the same JVM options as Liferay DXP.\n\\item\n  Liferay DXP and Elasticsearch compete for the same system resources.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} While it's not a supported production configuration,\ninstalling Kibana to monitor the embedded Elasticsearch server is useful\nduring development and testing. Just be aware that you must install the\n\\href{https://www.elastic.co/downloads/kibana-oss}{OSS only Kibana\nbuild}.\n\n\\noindent\\hrulefill\n\nYou wouldn't run an embedded database like HSQL in production, and you\nshouldn't run Elasticsearch in embedded mode in production either.\nInstead, run Elasticsearch in \\emph{remote operation mode}, as a\nstandalone server or cluster of server nodes.\n\n\\section{Troubleshooting Elasticsearch\nIntegration}\\label{troubleshooting-elasticsearch-integration}\n\nSometimes things don't go as planned. If you've set up Liferay DXP with\nElasticsearch in remote mode, but Liferay DXP can't connect to\nElasticsearch, check these things:\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Cluster name:}]\nThe value of the \\texttt{cluster.name} property in\n\\texttt{elasticsearch.yml} must match the \\texttt{clusterName} property\nconfigured in the Liferay DXP Elasticsearch connector.\n\\item[\\textbf{Transport address:}]\nThe value of the \\texttt{transportAddresses} property in the\nElasticsearch connector configuration must contain at least one valid\nhost and port where an Elasticsearch node is running. If Liferay DXP is\nrunning in embedded mode, and you start a standalone Elasticsearch node\nor cluster, it detects that port \\texttt{9300} is taken and switches to\nport \\texttt{9301}. If you then set Liferay's Elasticsearch connector to\nremote mode, it continues to look for Elasticsearch at the default port\n(\\texttt{9300}).\n\\end{description}\n\nThe following articles cover the Liferay Connector to Elasticsearch's\nconfiguration options in more detail.\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Cluster Sniffing (Additional Configurations):}]\nElasticsearch clusters can have multiple node\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-node.html\\#modules-node}{types}.\n\\href{https://www.elastic.co/guide/en/elasticsearch/client/java-api/7.x/transport-client.html}{Cluster\nsniffing}, enabled by default in the Liferay DXP connector, looks for\n\\texttt{data} nodes configured in the \\texttt{transportAddresses}\nproperty. If none are available, the connector may throw a\n\\texttt{NoNodeAvailableException} in the console log. If cluster\nsniffing is to remain enabled, be sure that your configuration allows\nfor at least one \\texttt{data} node's transport address to be\n``sniffable'' at all times to avoid this error.\n\\end{description}\n\nTo disable cluster sniffing, add \\texttt{clientTransportSniff=false} to\nthe \\texttt{.config} file or un-check the Client Transport Sniff\nproperty in System Settings.\n\n\\chapter{Preparing to Install\nElasticsearch}\\label{preparing-to-install-elasticsearch}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nBy default, 7.0 and its\n\\href{/docs/7-2/deploy/-/knowledge_base/d/elasticsearch\\#embedded-vs-remote-operation-mode}{embedded\nElasticsearch engine} run in the same JVM. Although this enables\nout-of-the-box search, it's only supported for development. For\nproduction use, Elasticsearch must run in a separate JVM. See the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch}{installation\nguide} for information on installing a remote Elasticsearch cluster.\n\nBecause search engines benefit heavily from caching, their JVM memory\nprofiles differ substantially from those of a JVM running Liferay DXP.\nTherefore, the two applications should always be kept separate in\nproduction environments.\n\nThe following sections provide a synopsis of Elasticsearch\nconfigurations for 7.0. Prior to deployment, we strongly recommend\nreading\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index.html}{Elastic's\ndocumentation on production deployment}.\n\n\\section{Sizing Your Deployment}\\label{sizing-your-deployment}\n\nWhen sizing your Elasticsearch deployment, carefully consider CPU,\nmemory, disk, and network capacity. To scale effectively and avoid using\nlots of machines, deploy Elasticsearch on medium to large machines (for\nexample, machines with two to eight CPUs). Avoid running multiple\nElasticsearch JVMs on the same operating system.\n\n\\section{CPU}\\label{cpu}\n\nWe recommend allocating at least eight total CPU cores to the\nElasticsearch engine, assuming only one Elasticsearch JVM is running on\nthe machine.\n\n\\section{Memory}\\label{memory}\n\nAt least 16 GB of memory is recommended, with 64 GB preferred. The\nprecise memory allocation required depends on how much data is indexed.\nFor index sizes 500 GB to 1 TB, 64 GB of memory suffices.\n\n\\section{Disk}\\label{disk}\n\nSearch engines store their indexes on disk, so disk I/O capacity can\nimpact search performance. Deploy Elasticsearch on SSD whenever\npossible. Otherwise use high-performance traditional hard disks (for\nexample, 15k RPM). In either case, consider using RAID 0.\n\nAvoid using Network Attached Storage (NAS) whenever possible as the\nnetwork overhead can be large. If you're using public cloud\ninfrastructure like Amazon Web Services, use instance local storage\ninstead of network storage, such as Elastic Block Store (EBS).\n\nMaintain 25 percent more disk capacity than the total size of your\nindexes. If your index is 60 GB, make sure you have at least 75 GB of\ndisk space available. To estimate the disk space you need, you can index\na representative sample of your production content and multiply that\nsize by the fraction of your production content that it represents. For\nexample, index 25 percent of your production content and then multiply\nthe resulting index size by four. Keep in mind that indexing a 1 MB file\ndoesn't result in 1 MB of disk space in the search index.\n\n\\section{Cluster Size}\\label{cluster-size}\n\nWhile Liferay DXP can work with an Elasticsearch cluster comprised of\none or two nodes, the minimum cluster size recommended by Elastic for\nfault tolerance is three nodes.\n\n\\section{Networking}\\label{networking}\n\nElasticsearch relies on clustering and sharding to deliver fast,\naccurate search results, and thus requires a fast and reliable network.\nMost modern data centers provide 1 GbE or 10 GbE between machines.\n\n\\chapter{Installing Elasticsearch}\\label{installing-elasticsearch}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay DXP uses Elasticsearch to index its content. By default, it's\ninstalled as an embedded service. It works, but it's not a supported\nconfiguration for a production server. Feel free to use it while testing\nor developing, but when you're ready to put your site in production, you\nmust run Elasticsearch as a standalone process. This is better anyway,\nbecause it frees you to design your infrastructure the way you want it.\nIf you've got hardware or a VM to spare, you can separate your search\ninfrastructure from Liferay DXP and reap some performance gains by\nputting search on a separate box. If you're more budget-conscious, you\ncan still increase performance by running Elasticsearch in a separate,\nindividually tunable JVM on the same box.\n\nBefore installing Elasticsearch, refer to\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-to-install-elasticsearch}{Preparing\nto Install Elasticsearch} for guidance on configuring the servers to\nsupport an Elasticsearch deployment properly.\n\nHere's an overview of the installation steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Download a supported version of Elasticsearch. See\n  \\href{https://www.elastic.co}{Elastic's} website.\n\\item\n  Install Elasticsearch by extracting its archive to the system where\n  you want it to run.\n\\item\n  Install some required Elasticsearch plugins.\n\\item\n  Name your Elasticsearch cluster.\n\\item\n  Configure Liferay DXP to connect to your Elasticsearch cluster.\n\\item\n  Restart Liferay DXP and reindex your search and spell check indexes.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Prerequisites:} Before continuing, make sure you have set the\n\\href{https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/}{\\texttt{JAVA\\_HOME}\nenvironment variable}.\n\nIf you have multiple JDKs installed, make sure Elasticsearch and Liferay\nDXP are using the same version and distribution (e.g., Oracle Open JDK\n1.8.0\\_201). You can specify this in\n\\texttt{{[}Elasticsearch\\ Home{]}/bin/elasticsearch.in.sh}:\n\\texttt{JAVA\\_HOME=/path/to/java}.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Replacing the Default Elasticsearch 6 Connector:} If you're\ninstalling Elasticsearch 6, use the connector application installed by\ndefault. If you're installing Elasticsearch 7, you'll need to download\nthe connector from Liferay Marketplace for either\n\\href{https://web.liferay.com/en/marketplace/-/mp/application/170642090}{CE}\nand\n\\href{https://web.liferay.com/en/marketplace/-/mp/application/170390307}{DXP}.\nAlways refer to the\n\\href{https://www.liferay.com/documents/10182/246659966/Liferay+DXP+7.2+Compatibility+Matrix.pdf/ed234765-db47-c4ad-7c82-2acb4c73b0f9}{compatibility\nmatrix to find the exact versions supported}. Before installing the\nconnector, blacklist the Elasticsearch 6 connector and APIs. The\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-elasticsearch-7\\#blacklisting-elasticsearch-6}{upgrade\ndocumentation} holds detailed blacklisting steps.\n\n\\noindent\\hrulefill\n\nWhen you perform these steps, you'll have a basic, production-ready\ninstance of Liferay DXP and Elasticsearch up and running. But that's\njust the beginning of your server/connector configuration:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Read about\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector}{Configuring\n  Elasticsearch} for Liferay DXP in more detail.\n\\item\n  Learn how to\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/installing-liferay-enterprise-search-security}{Secure\n  Elasticsearch}.\n\\item\n  {[}Liferay Enterprise Search{]} Learn how to configure\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/installing-liferay-enterprise-search-monitoring}{Monitoring}.\n\\end{itemize}\n\nFor complete information on compatibility, check the\n\\href{https://help.liferay.com/hc/en-us/articles/360028982631-Liferay-DXP-7-2-Compatibility-Matrix}{Liferay\nDXP compatibility matrix} and the\n\\href{https://help.liferay.com/hc/en-us/articles/360016511651\\#Liferay-Enterprise-Search}{Liferay\nEnterprise Search compatibility matrix} if you have a subscription.\n\n\\section{Step One: Download a Supported Version of\nElasticsearch}\\label{step-one-download-a-supported-version-of-elasticsearch}\n\nIf Liferay DXP isn't running, start it.\n\nVisit port 9200 on localhost to access the embedded Elasticsearch:\n\n\\begin{verbatim}\nhttp://localhost:9200\n\\end{verbatim}\n\nA JSON document is returned that looks similar to this:\n\n\\begin{verbatim}\n{\n  \"name\" : \"01BT8H4\",\n  \"cluster_name\" : \"LiferayElasticsearchCluster\",\n  \"cluster_uuid\" : \"ziPGEBeSToGHc7lVqaYHnA\",\n  \"version\" : {\n    \"number\" : \"6.5.0\",\n    \"build_flavor\" : \"unknown\",\n    \"build_type\" : \"unknown\",\n    \"build_hash\" : \"816e6f6\",\n    \"build_date\" : \"2018-11-09T18:58:36.352602Z\",\n    \"build_snapshot\" : false,\n    \"lucene_version\" : \"7.5.0\",\n    \"minimum_wire_compatibility_version\" : \"5.6.0\",\n    \"minimum_index_compatibility_version\" : \"5.0.0\"\n  },\n  \"tagline\" : \"You Know, for Search\"\n}\n\\end{verbatim}\n\nThe version of Elasticsearch that's running is the value of the\n\\texttt{number} field. In this example, it's 6.5.0. You can install the\nembedded version, but it might not be the most up-to-date version of\nElasticsearch that's supported with Liferay DXP. Consult the\n\\href{https://help.liferay.com/hc/en-us/articles/360016511651}{Compatibility\nMatrix} for definitive information on what's supported.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Although the embedded server uses Elasticsearch 6.5,\nElasticsearch 6.8.x has been tested with 7.0 GA1, and is fully\nsupported. If you've upgraded to 7.0 Service Pack 1/Fix Pack 2 (or GA2\nfor CE users), Elasticsearch 7 is supported through the Liferay\nConnector to Elasticsearch 7, which can be downloaded from Liferay\nMarketplace for both\n\\href{https://web.liferay.com/en/marketplace/-/mp/application/170642090}{CE}\nand\n\\href{https://web.liferay.com/en/marketplace/-/mp/application/170390307}{DXP}.\nAlways refer to the\n\\href{https://help.liferay.com/hc/en-us/articles/360016511651}{compatibility\nmatrix to find the exact versions supported}.\n\n\\noindent\\hrulefill\n\nShut down the Liferay DXP server. In a local, single-machine testing\nenvironment, if you continue without shutting down, the Elasticsearch\nserver you're about to install and start throws errors in the log if its\ncluster name and HTTP port match the already-running embedded\nElasticsearch server. An alternative to shutting down Liferay DXP is to\nuse a different cluster name (i.e., not\n\\texttt{LiferayElasticsearchCluster}) and HTTP port (i.e., not\n\\texttt{9200}) in the remote Elasticsearch server.\n\nWhen you know the version of Elasticsearch you need, go to\n\\href{https://www.elastic.co}{Elastic's} website and download that\nversion.\n\n\\section{Step Two: Install\nElasticsearch}\\label{step-two-install-elasticsearch}\n\nMost of this step entails deciding where you want to run Elasticsearch.\nDo you want to run it on the same machine as Liferay DXP, or do you want\nto run it on its own hardware? The answer to this question comes down to\na combination of the resources you have available and the size of your\ninstallation. Regardless of what you decide, either way you get the\nbenefit of a separately tunable search infrastructure.\n\nOnce you have a copy of the right version of Elasticsearch, extract it\nto a folder on the machine where you want it running. That's it!\n\n\\section{Step Three: Install Elasticsearch\nPlugins}\\label{step-three-install-elasticsearch-plugins}\n\nInstall the following required Elasticsearch plugins:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{analysis-icu}\n\\item\n  \\texttt{analysis-kuromoji}\n\\item\n  \\texttt{analysis-smartcn}\n\\item\n  \\texttt{analysis-stempel}\n\\end{itemize}\n\nTo install these plugins, navigate to Elasticsearch Home and enter\n\n\\begin{verbatim}\n./bin/elasticsearch-plugin install [plugin-name]\n\\end{verbatim}\n\nReplace \\emph{{[}plugin-name{]}} with the Elasticsearch plugin's name.\n\n\\section{Step Four: Name Your Elasticsearch\nCluster}\\label{step-four-name-your-elasticsearch-cluster}\n\nA \\emph{cluster} in Elasticsearch is a collection of nodes (servers)\nidentified as a cluster by a shared cluster name. The nodes work\ntogether to share data and workload. A one node cluster is discussed\nhere; to create a multi-node cluster, please refer to\n\\href{https://www.elastic.co/guide/index.html}{Elastic's documentation}.\n\nNow that you've installed Elastic, it sits in a folder on your machine,\nwhich is referred to here as \\texttt{{[}Elasticsearch\\ Home{]}}. To name\nyour cluster, you'll define the cluster name in both Elasticsearch and\nin Liferay DXP. First, define it in Elasticsearch. Edit the following\nfile:\n\n\\begin{verbatim}\n[Elasticsearch Home]/config/elasticsearch.yml\n\\end{verbatim}\n\nUncomment the line that begins with \\texttt{cluster.name}. Set the\ncluster name to whatever you want to name your cluster:\n\n\\begin{verbatim}\ncluster.name: LiferayElasticsearchCluster\n\\end{verbatim}\n\nOf course, this isn't a very imaginative name; you may choose to name\nyour cluster \\texttt{finders\\_keepers} or something else you can\nremember more easily. Save the file.\n\n\\noindent\\hrulefill\n\n\\textbf{Elasticsearch 6.x:} On Elasticsearch 6.x, you must also disable\nX-Pack Security unless you have a Liferay Enterprise Search\nsubscription. Add this to \\texttt{elasticsearch.yml}:\n\\texttt{xpack.security.enabled:\\ false}.\n\n\\noindent\\hrulefill\n\nNow you can start Elasticsearch. Run the executable for your operating\nsystem from the \\texttt{{[}Elasticsearch\\ Home{]}/bin} folder:\n\n\\begin{verbatim}\n./elasticsearch\n\\end{verbatim}\n\nElasticsearch starts, and one of its status messages includes a\ntransport address:\n\n\\begin{verbatim}\n[2019-04-01T16:55:50,127][INFO ][o.e.t.TransportService   ] [HfkqdKv] publish_address {127.0.0.1:9300}, bound_addresses {[::1]:9300}, {127.0.0.1:9300}\n\\end{verbatim}\n\nTake note of this address; you'll need to give it to your Liferay DXP\nserver so it can find Elasticsearch on the network.\n\n\\section{Step Five: Configure Liferay DXP to Connect to your\nElasticsearch\nCluster}\\label{step-five-configure-liferay-dxp-to-connect-to-your-elasticsearch-cluster}\n\nNow that you're ready to configure Liferay DXP, start it if you haven't\nalready, log in, and then click on \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{System Settings} → \\emph{Search}. Enter the\nterm \\emph{elasticsearch} in the search bar and click the\n\\emph{Elasticsearch {[}Version{]}} entry from the list of settings (at\nthe time of writing, the version will either be \\emph{6} or \\emph{7}).\nNow you can configure it. Here are the configuration options to change:\n\n\\textbf{Cluster Name:} Enter the name of the cluster as you defined it\nin Elasticsearch.\n\n\\textbf{Operation Mode:} Defaults to EMBEDDED. Change it to REMOTE to\nconnect to a standalone Elasticsearch.\n\n\\textbf{Transport Addresses:} Enter a delimited list of transport\naddresses for Elasticsearch nodes. Here, you'll enter the transport\naddress from the Elasticsearch server you started. The default value is\n\\texttt{localhost:9300}, which will work.\n\nWhen finished, click \\emph{Save}. You're almost done.\n\n\\section{Step Six: Restart Liferay DXP and\nReindex}\\label{step-six-restart-liferay-dxp-and-reindex}\n\nIf you're doing a local test installation, you probably only changed the\nOperation Mode in the connector configuration, so there's no need to\nrestart; skip to re-indexing. If you've made more configuration changes\nin the connector's configuration, stop and restart Liferay DXP. When\nit's back up, log in as an administrative user and click on\n\\emph{Control Panel} → \\emph{Configuration} → \\emph{Search} and click\nthe \\emph{Execute} button for \\emph{Reindex all search indexes} and then\n\\emph{Reindex all spell check indexes}. When you do that, you'll see\nsome messages scroll up in the Elasticsearch log.\n\nWhen restarting Liferay DXP, \\texttt{update\\_mappings} messages will\nappear in the Elasticsearch logs:\n\n\\begin{verbatim}\n[2019-04-01T17:08:57,462][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-0/m27eNsekTAyP27zDOjGojw] update_mapping [LiferayDocumentType]\n[2019-04-01T17:08:57,474][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-0/m27eNsekTAyP27zDOjGojw] update_mapping [LiferayDocumentType]\n[2019-04-01T17:08:58,393][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-0/m27eNsekTAyP27zDOjGojw] update_mapping [LiferayDocumentType]\n[2019-04-01T17:08:58,597][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-0/m27eNsekTAyP27zDOjGojw] update_mapping [LiferayDocumentType]\n[2019-04-01T17:09:07,040][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/OJidpYkgR5OcCD5dgWB8Aw] update_mapping [LiferayDocumentType]\n\\end{verbatim}\n\nOnce you reindex, more log messages appear in Elasticsearch:\n\n\\begin{verbatim}\n[2019-04-01T17:11:17,338][INFO ][o.e.c.m.MetaDataDeleteIndexService] [HfkqdKv] [liferay-20101/OJidpYkgR5OcCD5dgWB8Aw] deleting index\n[2019-04-01T17:11:17,389][INFO ][o.e.c.m.MetaDataCreateIndexService] [HfkqdKv] [liferay-20101] creating index, cause [api], templates [], shards [1]/[0], mappings [LiferayDocumentType]\n[2019-04-01T17:11:17,471][INFO ][o.e.c.r.a.AllocationService] [HfkqdKv] Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[liferay-20101][0]] ...]).\n[2019-04-01T17:11:17,520][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:19,047][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:19,133][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:19,204][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:19,249][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:21,215][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:21,262][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:21,268][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:21,275][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:21,282][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:21,373][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n\\end{verbatim}\n\nReindexing the spell check dictionaries produces log messages like\nthese:\n\n\\begin{verbatim}\n2019-04-29 14:02:22.034 INFO  [liferay/search_writer/SYSTEM_ENGINE-11][BaseSpellCheckIndexWriter:278] Start indexing dictionary for com/liferay/portal/search/dependencies/spellchecker/en_US.txt\n2019-04-29 14:02:34.166 INFO  [liferay/search_writer/SYSTEM_ENGINE-11][BaseSpellCheckIndexWriter:299] Finished indexing dictionary for com/liferay/portal/search/dependencies/spellchecker/en_US.txt\n2019-04-29 14:02:34.167 INFO  [liferay/search_writer/SYSTEM_ENGINE-11][BaseSpellCheckIndexWriter:278] Start indexing dictionary for com/liferay/portal/search/dependencies/spellchecker/es_ES.txt\n2019-04-29 14:02:39.379 INFO  [liferay/search_writer/SYSTEM_ENGINE-11][BaseSpellCheckIndexWriter:299] Finished indexing dictionary for com/liferay/portal/search/dependencies/spellchecker/es_ES.txt\n\\end{verbatim}\n\nFor additional confirmation that Liferay DXP recognizes the remote\nsearch engine, navigate to the Search Control Panel application and note\nthe subtle change there: the vendor name is now simply\n\\emph{Elasticsearch}, whereas prior to the installation of the remote\nElasticsearch server, it said \\emph{Elasticsearch (Embedded)}.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/search-admin-engineinfo-remote.png}}\n\\caption{To see information about the currently connected search engine,\ngo to \\emph{Control Panel → Configuration → Search}.}\n\\end{figure}\n\nFor additional details refer to the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/getting-started-install.html}{Elasticsearch\ninstallation guide}.\n\n\\chapter{Configuring the Liferay Elasticsearch\nConnector}\\label{configuring-the-liferay-elasticsearch-connector}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nFor detailed Elasticsearch configuration information, refer to the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/settings.html}{Elasticsearch\ndocumentation}.\n\nThe name of your Elasticsearch cluster is important. When you're running\nElasticsearch in remote mode, the cluster name is used by Liferay DXP to\nrecognize the Elasticsearch cluster. To learn about setting the\nElasticsearch cluster name on the Liferay DXP side, refer below to the\nsection called Configuring the Liferay Elasticsearch Connector.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\texttt{http.enabled} setting in Elasticsearch\ncorresponds to the \\texttt{httpEnabled} setting in the Liferay Connector\nto Elasticsearch 6 application. As this setting was\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/6.5/release-notes-6.3.0.html}{deprecated\nin Elasticsearch 6.3}, the connector's corresponding setting is now also\ndeprecated. This setting was only used for configuring the embedded\nElasticsearch server, so its deprecation should have minimal impact to\nproduction deployments.\n\n\\noindent\\hrulefill\n\nElasticsearch's configuration files are written in\n\\href{http://www.yaml.org}{YAML} and kept in the\n\\texttt{{[}Elasticsearch\\ Home{]}/config} folder. The main configuration\nfile is \\texttt{elasticsearch.yml}, used for configuring Elasticsearch\nmodules.\n\nTo set the name of the Elasticsearch cluster, open\n\\texttt{{[}Elasticsearch\\ Home{]}/config/elasticsearch.yml} and specify\n\n\\begin{verbatim}\ncluster.name: LiferayElasticsearchCluster\n\\end{verbatim}\n\nSince \\texttt{LiferayElasticsearchCluster} is the default name given to\nthe cluster in the Liferay DXP Elasticsearch connector, this works just\nfine. Of course, you can name your cluster whatever you want (we humbly\nsubmit the recommendation\n\\texttt{clustery\\_mcclusterface}).\\hyperref[footnote1]{1} Configure your\nnode name using the same syntax (setting the \\texttt{node.name}\nproperty). There's no client setting for this, it exists only in each\nElasticsearch node's \\texttt{elasticsearch.yml} file.\n\nIf you'd rather work from the command line than in the configuration\nfile, navigate to Elasticsearch Home and enter\n\n\\begin{verbatim}\n./bin/elasticsearch --cluster.name clustery_mcclusterface --node.name nody_mcnodeface\n\\end{verbatim}\n\nFeel free to change the node name or the cluster name. Once you\nconfigure Elasticsearch to your liking, start it up.\n\n\\section{Starting Elasticsearch}\\label{starting-elasticsearch}\n\nStart Elasticsearch by navigating to Elasticsearch Home and typing\n\n\\begin{verbatim}\n./bin/elasticsearch\n\\end{verbatim}\n\nif you run Linux, or\n\n\\begin{verbatim}\n\\bin\\elasticsearch.bat\n\\end{verbatim}\n\nif you run Windows.\n\nTo run as a daemon in the background, add the \\texttt{-d} switch to\neither command:\n\n\\begin{verbatim}\n./bin/elasticsearch -d\n\\end{verbatim}\n\nOnce both Elasticsearch and Liferay DXP are installed and running,\nintroduce them to each other.\n\n\\section{Configuring the Liferay Elasticsearch\nConnector}\\label{configuring-the-liferay-elasticsearch-connector-1}\n\nThe Elasticsearch connector provides integration between Elasticsearch\nand the portal. Before configuring the connector, make sure\nElasticsearch is running.\n\nThere are two ways to configure the connector:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\hyperref[configuring-the-connector-in-the-control-panel]{Use the\n  System Settings application in the Control Panel.}\n\\item\n  \\hyperref[configuring-the-connector-with-an-osgi-config-file]{Manually\n  create an OSGi configuration file.}\n\\end{enumerate}\n\nIt's convenient to configure the Elasticsearch connector from System\nSettings, but this is often only possible during development and\ntesting. If you're not familiar with System Settings, read about it\n\\href{/docs/7-2/user/-/knowledge_base/u/system-settings}{here}. Remember\nthat you can generate configuration files for deployment to other\nsystems by configuring System Settings, and then exporting the\n\\texttt{.config} file with your configuration.\n\n\\section{Configuring the Connector in the Control\nPanel}\\label{configuring-the-connector-in-the-control-panel}\n\nTo configure the Elasticsearch connector from the System Settings\napplication,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Start Liferay DXP.\n\\item\n  Navigate to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Platform}.\n\\item\n  Find the \\emph{Elasticsearch} entry (scroll down and browse to it or\n  use the search box) and click the Actions icon\n  (\\pandocbounded{\\includegraphics[keepaspectratio]{./images/icon-actions.png}}),\n  then \\emph{Edit}.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/cfg-elasticsearch-sys-settings.png}}\n  \\caption{Use the System Settings application in Liferay DXP's Control\n  Panel to configure the Elasticsearch connector.}\n  \\end{figure}\n\\item\n  Make any edits to the configuration and click \\emph{Save}.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/cfg-elasticsearch-sys-settings2.png}}\n  \\caption{Configure the Elasticsearch connector's settings. Make sure\n  you set the Operation Mode to \\emph{Remote}.}\n  \\end{figure}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you switch operation modes (\\texttt{EMBEDDED} →\n\\texttt{REMOTE}), you must trigger a re-index. Navigate to \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{Search}, and click \\emph{Execute}\nnext to \\emph{Reindex all search indexes.}\n\n\\noindent\\hrulefill\n\n\\section{\\texorpdfstring{Configuring the Connector with an OSGi\n\\texttt{.config}\nFile}{Configuring the Connector with an OSGi .config File}}\\label{configuring-the-connector-with-an-osgi-.config-file}\n\nWhen preparing a system for production deployment, you want to use a\nrepeatable deployment process. Therefore, it's best to use the OSGi\nconfiguration file, where your configuration is maintained in a\ncontrolled source.\n\nFollow these steps to configure the Elasticsearch connector using a\nconfiguration file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create the following file:\n\n\\begin{verbatim}\n [Liferay_Home]/osgi/configs/com.liferay.portal.search.elasticsearch7.configuration.ElasticsearchConfiguration.config\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Elasticsearch 6:** The name of the `.config` file for the Elasticsearch\n 6 connector is\n `com.liferay.portal.search.elasticsearch6.configuration.ElasticsearchConfiguration.config`\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Add configurations to the file, in the format\n  \\texttt{propertyName=\"Value\"}. For example,\n\n\\begin{verbatim}\n operationMode=\"REMOTE\"\n # If running Elasticsearch from a different computer:\n #transportAddresses=\"ip.of.elasticsearch.node:9300\"\n # Highly recommended for all non-production usage (e.g., practice, tests, diagnostics):\n #logExceptionsOnly=\"false\"\n\\end{verbatim}\n\\item\n  Start Liferay DXP or re-index if already running.\n\\end{enumerate}\n\nAs you can see from the System Settings entry for Elasticsearch, there\nare a lot more configuration options available that help you tune your\nsystem for optimal performance.\n\nWhat follows here are some known good configurations for clustering\nElasticsearch. These, however, can't replace the manual process of\ntuning, testing under load, and tuning again, so we encourage you to\nexamine the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/important-settings.html}{Elasticsearch\ndocumentation} and go through that process once you have a working\nconfiguration.\n\n\\section{Configuring a Remote Elasticsearch\nHost}\\label{configuring-a-remote-elasticsearch-host}\n\nIn production systems Elasticsearch and Liferay DXP are installed on\ndifferent servers. To make Liferay DXP aware of the Elasticsearch\ncluster, set\n\n\\begin{verbatim}\ntransportAddresses=[IP address of Elasticsearch Node]:9300\n\\end{verbatim}\n\nHere's an example that sets the IP address of two nodes in the\nElasticsearch cluster:\n\n\\begin{verbatim}\ntransportAddresses=[\"192.168.1.1:9300\",\"192.168.1.2:9300\"]\n\\end{verbatim}\n\nSet this in the Elasticsearch connector's OSGi configuration file. List\nas many or as few Elasticsearch nodes in this property as you want. This\ntells Liferay DXP the IP address or host name where search requests\nshould be sent. If using System Settings, set the value in the\n\\emph{Transport Addresses} property.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} In an Elasticsearch cluster you can list the transport\naddresses for multiple Elasticsearch nodes as a comma-separated list in\nthe \\texttt{transportAddresses} property. If you set only one transport\naddress, Liferay DXP loses contact with Elasticsearch if that node goes\ndown.\n\n\\noindent\\hrulefill\n\nOn the Elasticsearch side, set the \\texttt{network.host} property in\nyour \\texttt{elaticsearch.yml} file. This property simultaneously sets\nboth the \\emph{bind host} (the host where Elasticsearch listens for\nrequests) and the \\emph{publish host} (the host name or IP address\nElasticsearch uses to communicate with other nodes). See\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-network.html}{here}\nfor more information.\n\n\\section{Clustering Elasticsearch in Remote Operation\nMode}\\label{clustering-elasticsearch-in-remote-operation-mode}\n\nTo cluster Elasticsearch, first set\n\\texttt{node.max\\_local\\_storage\\_nodes} to be something greater than\n\\texttt{1}. When you run the Elasticsearch start script, a new local\nstorage node is added to the cluster. If you want four nodes running\nlocally, for example, run \\texttt{./bin/elasticsearch} four times. See\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-node.html\\#max-local-storage-nodes}{here}\nfor more information.\n\nConfigure the number of shards and replicas in the Elasticsearch 6\nconnector, using the \\texttt{indexNumberOfShards} and\n\\texttt{indexNumberOfReplicas} properties to specify the number of\nprimary shards and number of replica shards, respectively.\nElasticsearch's default configuration works for a cluster of up to ten\nnodes, since the default number of shards is \\texttt{5} and the default\nnumber of replica shards is \\texttt{1}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Elasticsearch uses the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/6.x/modules-discovery-zen.html}{Zen\nDiscovery Module} by default, which provides unicast discovery.\nAdditionally, nodes in the cluster communicate using the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-transport.html}{Transport\nModule}, through TCP. See the Elasticsearch documentation for the\navailable properties (to be set in the \\texttt{elasticsearch.yml} file),\nand the Liferay DXP Elasticsearch connector's settings for the\nconnector's available settings.\n\nAt a minimum, provide the list of hosts (as \\texttt{host:port}) to act\nas gossip routers during unicast discovery in the\n\\texttt{elasticsearch.yml}:\n\n\\begin{verbatim}\n discovery.zen.ping.unicast.hosts: [\"node1.ip.address\", \"node2.ip.address\"]\n\\end{verbatim}\n\nFor example,\n\n\\begin{verbatim}\n discovery.zen.ping.unicast.hosts: [\"10.10.10.5\", \"10.10.10,.5:9305\"]\n\\end{verbatim}\n\nFor more information on configuring an Elasticsearch cluster, see the\ndocumentation on\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index-modules.html}{Elasticsearch\nIndex Settings}.\n\n\\noindent\\hrulefill\n\n\\section{Elasticsearch Connector System Settings, By Operation\nMode}\\label{elasticsearch-connector-system-settings-by-operation-mode}\n\nSome of the settings available for the Elasticsearch connector are\napplicable for only one operation mode (REMOTE or EMBEDDED). Refer to\nthe table below:\n\nConnector Setting/Operation Mode \\textbar{} EMBEDDED \\textbar{} REMOTE\n\\textbar{} \\texttt{clusterName} \\textbar{} x \\textbar{} x\n\\texttt{operationMode} \\textbar{} x \\textbar{} x\n\\texttt{indexNamePrefix} \\textbar{} x \\textbar{} x\n\\texttt{indexNumberOfReplicas*} \\textbar{} x \\textbar{} x\n\\texttt{indexNumberOfShards*} \\textbar{} x \\textbar{} x\n\\texttt{bootstrapMlockAll} \\textbar{} x \\textbar{} -\n\\texttt{logExceptionsOnly} \\textbar{} x \\textbar{} x\n\\texttt{retryOnConflict} \\textbar{} x \\textbar{} x\n\\texttt{discoveryZenPingUnicastHostsPort} \\textbar{} x \\textbar{} -\n\\texttt{networkHost} \\textbar{} x \\textbar{} - \\texttt{networkBindHost}\n\\textbar{} x \\textbar{} - \\texttt{networkPublishHost} \\textbar{} x\n\\textbar{} - \\texttt{transportTcpPort} \\textbar{} x \\textbar{} -\n\\texttt{transportAddresses} \\textbar{} - \\textbar{} x\n\\texttt{clientTransportSniff} \\textbar{} - \\textbar{} x\n\\texttt{clientTransportIgnoreClusterName} \\textbar{} - \\textbar{} x\n\\texttt{clientTransportPingTimeout*} \\textbar{} - \\textbar{} x\n\\texttt{clientTransportNodesSamplerInterval} \\textbar{} - \\textbar{} x\n\\texttt{httpEnabled} \\textbar{} x \\textbar{} - \\texttt{httpCORSEnabled}\n\\textbar{} x \\textbar{} - \\texttt{httpCORSAllowOrigin} \\textbar{} x\n\\textbar{} - \\texttt{httpCORSConfigurations} \\textbar{} x \\textbar{} -\n\\texttt{additionalConfigurations} \\textbar{} x \\textbar{} x\n\\texttt{additionalIndexConfigurations} \\textbar{} x \\textbar{} x\n\\texttt{additionalTypeMappings} \\textbar{} x \\textbar{} x\n\\texttt{overrideTypeMappings} \\textbar{} x \\textbar{} x\n\n1 This is, of course, a nod to all those fans of\n\\href{http://www.theatlantic.com/international/archive/2016/05/boaty-mcboatface-parliament-lessons/482046}{Boaty\nMcboatface}.\n\n\\chapter{Advanced Configuration of the Liferay Elasticsearch\nConnector}\\label{advanced-configuration-of-the-liferay-elasticsearch-connector}\n\nThe default configuration for Liferay's Elasticsearch connector module\nis set in a Java class called \\texttt{ElasticsearchConfiguration}.\n\nWhile the Elasticsearch connector has a lot of configuration options out\nof the box, you might find an Elasticsearch configuration you need that\nisn't provided by default. In this case, add the configuration options\nyou need. If something is configurable for Elasticsearch, it's\nconfigurable using the Elasticsearch connector.\n\n\\section{Adding Settings and Mappings to the Liferay Elasticsearch\nConnector}\\label{adding-settings-and-mappings-to-the-liferay-elasticsearch-connector}\n\nThink of the available configuration options as being divided into two\ngroups: the most common ones that are easily configured, and more\ncomplex configurations requiring a more brute-force approach: these\ninclude the \\texttt{additionalConfigurations},\n\\texttt{additionalIndexConfigurations}, \\texttt{additionalTypeMappings},\nand \\texttt{overrideTypeMappings} settings.\n\n\\href{./images/cfg-elasticsearch-additional-configs.png}{Figure\n1: You can add Elasticsearch configurations to the ones currently\navailable in System Settings.}\n\n\\section{Additional Configurations}\\label{additional-configurations}\n\nThe \\texttt{additionalConfigurations} configuration defines extra\nsettings (in YAML) for the embedded Elasticsearch. This is only useful\nfor testing environments using the embedded Elasticsearch server. Any\nnode settings normally set in \\texttt{elasticsearch.yml} can be declared\nhere. See the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index.html}{Elasticsearch\ndocumentation} for a description of all possible node settings.\n\n\\section{Adding Index Configurations}\\label{adding-index-configurations}\n\nThe \\texttt{additionalIndexConfigurations} configuration defines extra\nsettings (in JSON or YAML) that are applied to the Liferay DXP index\nwhen it's created. For example, you can create custom analyzers and\nfilters using this setting. For a complete list of available settings,\nsee the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index-modules.html}{Elasticsearch\nreference}.\n\nHere's an example that shows how to configure\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index-modules-analysis.html}{analysis}\nthat can be applied to a field or a dynamic template (see\n\\hyperref[overriding-type-mappings]{below} for an example application to\na dynamic template).\n\n\\begin{verbatim}\n{  \n    \"analysis\": {\n        \"analyzer\": {\n            \"kuromoji_liferay_custom\": {\n                \"filter\": [\n                    \"cjk_width\",\n                    \"kuromoji_baseform\",\n                    \"pos_filter\"\n                ],\n                \"tokenizer\": \"kuromoji_tokenizer\"\n            }\n        },\n        \"filter\": {\n            \"pos_filter\": {\n                \"type\": \"kuromoji_part_of_speech\"\n            }\n        }\n    }\n}\n\\end{verbatim}\n\n\\section{Adding Type Mappings}\\label{adding-type-mappings}\n\n\\texttt{additionalTypeMappings} defines extra mappings for the\n\\texttt{LiferayDocumentType} type definition. These are applied when the\nindex is created. Add the mappings using JSON syntax. For more\ninformation see\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/mapping.html}{here}\nand\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/indices-put-mapping.html}{here}.\nUse \\texttt{additionalTypeMappings} for new field (\\texttt{properties})\nmappings and new dynamic templates, but don't try to override existing\nmappings. If any of the mappings set here overlap with existing\nmappings, index creation fails. Use \\texttt{overrideTypeMappings} to\nreplace default mappings.\n\nAs with dynamic templates, you can add sub-field mappings to Liferay\nDXP's type mapping. These are referred to as\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/properties.html}{properties}\nin Elasticsearch.\n\n\\begin{verbatim}\n{ \n    \"LiferayDocumentType\": {  \n        \"properties\": {   \n            \"fooName\": {\n                \"index\": \"true\",\n                \"store\": \"true\",\n                \"type\": \"keyword\"\n            }\n        }   \n    }\n}\n\\end{verbatim}\n\nSee\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/mapping-types.html}{here}\nfor more details on Elasticsearch's field datatypes.\n\nThe above example shows how a \\texttt{fooName} field might be added to\nLiferay DXP's type mapping. Because \\texttt{fooName} is not an existing\nproperty in the mapping, it works fine. If you try to override an\nexisting property mapping, index creation fails. Instead use the\n\\texttt{overrideTypeMappings} setting to override \\texttt{properties} in\nthe mapping.\n\nTo see that your additional mappings have been added to the\n\\texttt{LiferayDocumentType}, use \\texttt{curl} to access this URL after\nsaving your additions and re-indexing:\n\n\\begin{verbatim}\ncurl http://[HOST]:[ES_PORT]/liferay-[COMPANY_ID]/_mapping/LiferayDocumentType?pretty\n\\end{verbatim}\n\nHere's what it would look like for an Elasticsearch instance running on\n\\texttt{localhost:9200}, with a Liferay DXP Company ID of\n\\texttt{20116}:\n\n\\begin{verbatim}\ncurl http://localhost:9200/liferay-20116/_mapping/LiferayDocumentType?pretty\n\\end{verbatim}\n\nIn the above URL, \\texttt{liferay-20116}is the index name. Including it\nindicates that you want to see the mappings that were used to create the\nindex with that name.\n\n\\section{Overriding Type Mappings}\\label{overriding-type-mappings}\n\nUse \\texttt{overrideTypeMappings} to override Liferay DXP's default type\nmappings. This is an advanced feature that should be used only if\nstrictly necessary. If you set this value, the default mappings used to\ndefine the Liferay Document Type in Liferay DXP source code (for\nexample, \\texttt{liferay-type-mappings.json}) are ignored entirely, so\ninclude the whole mappings definition in this property, not just the\nsegment you're modifying. To make a modification, find the entire list\nof the current mappings being used to create the index by navigating to\nthe URL\n\n\\begin{verbatim}\nhttp://[HOST]:[ES_PORT]/liferay-[COMPANY_ID]/_mapping/LiferayDocumentType?pretty\n\\end{verbatim}\n\nCopy the contents in as the value of this property (either into System\nSettings or your OSGi configuration file). Leave the opening curly brace\n\\texttt{\\{}, but delete lines 2-4 entirely:\n\n\\begin{verbatim}\n\"liferay-[COMPANY_ID]\": {\n    \"mappings\" : {\n        \"LiferayDocumentType\" : {\n\\end{verbatim}\n\nThen, from the end of the mappings, delete the concluding three curly\nbraces.\n\n\\begin{verbatim}\n        }\n    }\n}\n\\end{verbatim}\n\nNow modify whatever mappings you'd like. The changes take effect once\nyou save the changes and trigger a re-index from Server Administration.\n\nHere's a partial example, showing a\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/dynamic-templates.html}{dynamic\ntemplate} that uses the analysis configuration from\n\\texttt{additionalIndexConfigurations} to analyze all string fields that\nend with \\texttt{\\_ja}. You'd include this with all the other default\nmappings, replacing the provided \\texttt{template\\_ja} with this custom\none:\n\n\\begin{verbatim}\n{\n    \"LiferayDocumentType\": {\n        \"dynamic_templates\": [\n            {\n                \"template_ja\": {\n                    \"mapping\": {\n                        \"analyzer\": \"kuromoji_liferay_custom\",\n                        \"index\": \"analyzed\",\n                        \"store\": \"true\",\n                        \"term_vector\": \"with_positions_offsets\",\n                        \"type\": \"string\"\n                    },\n                    \"match\": \"\\\\w+_ja\\\\b|\\\\w+_ja_[A-Z]{2}\\\\b\",\n                    \"match_mapping_type\": \"string\",\n                    \"match_pattern\": \"regex\"\n                }\n                ...\n            }\n        ]\n    }\n}\n\\end{verbatim}\n\n\\section{Multi-line YAML\nConfigurations}\\label{multi-line-yaml-configurations}\n\nIf you configure the settings from the last section using an OSGi\nconfiguration file, you might find yourself needing to write YAML\nsnippets that span multiple lines. The syntax for that is\nstraightforward and just requires appending each line with\n\\texttt{\\textbackslash{}n\\textbackslash{}}, like this:\n\n\\begin{verbatim}\nadditionalConfigurations=\\\n                    cluster.routing.allocation.disk.threshold_enabled: false\\n\\\n                    cluster.service.slow_task_logging_threshold: 600s\\n\\\n                    index.indexing.slowlog.threshold.index.warn: 600s\\n\\\n                    index.search.slowlog.threshold.fetch.warn: 600s\\n\\\n                    index.search.slowlog.threshold.query.warn: 600s\\n\\\n                    monitor.jvm.gc.old.warn: 600s\\n\\\n                    monitor.jvm.gc.young.warn: 600s\n\\end{verbatim}\n\nFrom simple configurations to overriding existing type mappings,\nElasticsearch and Liferay's connector to Elasticsearch are configurable.\n\n\\chapter{Elasticsearch Connector Settings:\nReference}\\label{elasticsearch-connector-settings-reference}\n\nElasticsearch is the default search engine for 7.0. The connection is\nmanaged through the \\emph{Liferay Connector to Elasticsearch\n{[}Version{]}}, and is configurable through System Settings or an OSGi\nconfiguration file named\n\n\\begin{verbatim}\ncom.liferay.portal.search.elasticsearch6.configuration.ElasticsearchConfiguration.config\n\\end{verbatim}\n\nIf you are using Elasticsearch 7, your configuration file must be named\n\n\\begin{verbatim}\ncom.liferay.portal.search.elasticsearch7.configuration.ElasticsearchConfiguration.config\n\\end{verbatim}\n\nDeploy the file to \\texttt{{[}Liferay\\_Home{]}/osgi/configs} and a\nlistener auto-detects it.\n\nThe list below is all the configuration settings for Liferay's default\nElasticsearch connector, in the order they appear in the System Settings\napplication (The \\emph{Elasticsearch {[}Version{]}} entry under the\n\\emph{Search} category):\n\n\\begin{description}\n\\tightlist\n\\item[\\texttt{clusterName=LiferayElasticsearchCluster}]\nA String value that sets the name of the cluster to integrate with. This\nname should match the remote cluster when Operation Mode is set to\nremote. (See also: remote operation mode)\n\\item[\\texttt{operationMode=EMBEDDED}]\nThere are two operation modes you can choose from: EMBEDDED or REMOTE.\nSet to REMOTE to connect to a remote standalone Elasticsearch cluster.\nSet to EMBEDDED to start Liferay with an internal Elasticsearch\ninstance. Embedded operation mode is unsupported for production\nenvironments.\n\\item[\\texttt{indexNamePrefix=liferay-}]\nSet a String value to use as the prefix for the search index name. The\ndefault value should not be changed under normal conditions. If you\nchange it, you must also perform a \\emph{reindex all} operation for the\nportal and then manually delete the old index using the Elasticsearch\nadministration console.\n\\end{description}\n\n\\texttt{indexNumberOfReplicas=} Set the number of replicas for each\nindex. If left unset, no replicas are used. A full reindex is required\nto make changes take effect.\n\n\\texttt{indexNumberOfShards=} Set the number of index shards to use when\na Liferay index is created. If left unset, a single shard is used. A\nfull reindex is required to make changes take effect.\n\n\\begin{description}\n\\tightlist\n\\item[\\texttt{bootstrapMlockAll=false}]\nA boolean setting that, when set to \\texttt{true}, tries to lock the\nprocess address space into RAM, preventing any Elasticsearch memory from\nbeing swapped out (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/setup-configuration-memory.html\\#bootstrap-memory_lock}{here})\nfor more information)\n\\item[\\texttt{logExceptionsOnly=true}]\nA boolean setting that, when set to true, only logs exceptions from\nElasticsearch, and does not rethrow them.\n\\item[\\texttt{retryOnConflict=5}]\nSet an int value for the number of retries to attempt if a version\nconflict occurs because the document was updated between getting it and\nupdating it (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/docs-update.html\\#docs-update-api-query-params}{here}\nfor more information).\n\\item[\\texttt{discoveryZenPingUnicastHostsPort=9300-9400}]\nSet a String value for the range of ports to use when building the value\nfor discovery.zen.ping.unicast.hosts. Multiple Elasticsearch nodes on a\nrange of ports can act as gossip routers at the same computer (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-discovery-hosts-providers.html}{here}\nfor more information).\n\\item[\\texttt{networkHost=}]\nSet this String value to instruct the node to bind to this hostname or\nIP address and publish (advertise) this host to other nodes in the\ncluster. This is a shortcut which sets the bind host and the publish\nhost at the same time (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-network.html\\#common-network-settings}{here}\nfor more information).\n\\item[\\texttt{networkBindHost=}]\nSet the String value of the network interface(s) a node should bind to\nin order to listen for incoming requests (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-network.html\\#advanced-network-settings}{here}\nfor more information).\n\\item[\\texttt{networkPublishHost=}]\nSet the String value of a single interface that the node advertises to\nother nodes in the cluster, so that those nodes can connect to it (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-network.html\\#advanced-network-settings}{here}\nfor more information).\n\\item[\\texttt{transportTcpPort=}]\nSet the String value for the port to bind for communication between\nnodes. Accepts a single value or a range (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-transport.html\\#_tcp_transport}{here}\nfor more information).\n\\item[\\texttt{transportAddresses=localhost:9300}]\nSet the String values for the addresses of the remote Elasticsearch\nnodes to connect to. This value is required when Operation Mode is set\nto remote (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/client/java-api/7.x/transport-client.html}{here}\nfor more information). Specify as many or few nodes as you see fit.\n\\item[\\texttt{clientTransportSniff=true}]\nSet this boolean to true to enable cluster sniffing and dynamically\ndiscover available data nodes in the cluster (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/client/java-api/7.x/transport-client.html}{here}\nfor more information).\n\\item[\\texttt{clientTransportIgnoreClusterName=false}]\nSet this boolean to true to ignore cluster name validation of connected\nnodes (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/client/java-api/7.x/transport-client.html}{here}\nfor more information).\n\\end{description}\n\n\\texttt{clientTransportPingTimeout=} The time (in seconds) the client\nnode waits for a ping response from a node. If unset, the default\nElasticsearch \\texttt{client.transport.ping\\_timeout} is used.\n\n\\begin{description}\n\\tightlist\n\\item[\\texttt{clientTransportNodesSamplerInterval=}]\nSet this String value to instruct the client node on how often to sample\n/ ping the nodes listed and connected (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/client/java-api/7.x/transport-client.html}{here}\nfor more information).\n\\item[\\texttt{httpEnabled=true}]\nSet this boolean to false to disable the http layer entirely on nodes\nwhich are not meant to serve REST requests directly. As this setting was\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/6.7/release-notes-6.3.0.html\\#deprecation-6.3.0}{deprecated\nin Elasticsearch 6.3}, the connector's corresponding setting is now also\ndeprecated. This setting was only used for configuring the embedded\nElasticsearch server, so its deprecation should have minimal impact to\nproduction deployments.\n\\item[\\texttt{httpCORSEnabled=true}]\nSet this boolean to false to disable cross-origin resource sharing,\ni.e.~whether a browser on another origin can do requests to\nElasticsearch. If disabled, web front end tools like elasticsearch-head\nmay be unable to connect (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-http.html\\#_settings}{here}\nfor more information).\n\\item[\\texttt{httpCORSAllowOrigin=/https?:\\textbackslash{}\\textbackslash{}/\\textbackslash{}\\textbackslash{}/localhost(:{[}0-9{]}+)?/}]\nSet the String origins to allow when HTTP CORS is enabled (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-http.html\\#_settings}{here}\nfor more information).\n\\item[\\texttt{httpCORSConfigurations=}]\nSet the String values for custom settings for HTTP CORS, in YML format\n(\\texttt{elasticsearch.yml}) (see\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-http.html\\#_settings}{here}\nfor more information).\n\\item[\\texttt{additionalConfigurations=}]\nSet the String values for custom settings for embedded Elasticsearch, in\nYML format. See: Adding Settings to the Liferay Elasticsearch Connector\n\\item[\\texttt{additionalIndexConfigurations=}]\nSet the String values for custom settings for the Liferay index, in JSON\nor YML format (refer to the Elasticsearch Create Index API for more\ninformation). See: Adding Settings to the Liferay Elasticsearch\nConnector\n\\item[\\texttt{additionalTypeMappings=}]\nSet the String values for custom mappings for the\n\\texttt{LiferayDocumentType}, in JSON format (refer to the Elasticsearch\nPut Mapping API for more information) See: Adding Settings to the\nLiferay Elasticsearch Connector\n\\end{description}\n\n\\texttt{overrideTypeMappings=} Settings here override Liferay DXP's\ndefault type mappings. This is an advanced feature that should be used\nonly if strictly necessary. If you set this value, the default mappings\nused to define the Liferay Document Type in Liferay DXP source code (for\nexample, \\texttt{liferay-type-mappings.json}) are ignored entirely, so\ninclude the whole mappings definition in this property, not just the\nsegment you're modifying.\n\n\\section{Configurations only Affecting the Embedded Elasticsearch\nServer}\\label{configurations-only-affecting-the-embedded-elasticsearch-server}\n\nThese settings (defined above) are only meant to use while configuring\nthe embedded Elasticsearch server. Configuring these will elicit no\neffect on remote Elasticsearch installations:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{bootstrapMlockAll}\n\\item\n  \\texttt{discoveryZenPingUnicastHostsPort}\n\\item\n  \\texttt{networkHost}\n\\item\n  \\texttt{networkBindHost}\n\\item\n  \\texttt{networkPublishHost}\n\\item\n  \\texttt{transportTcpPort}\n\\item\n  \\texttt{httpEnabled}\n\\item\n  \\texttt{httpCORSEnabled}\n\\item\n  \\texttt{httpCORSAllowOrigin}\n\\item\n  \\texttt{httpCORSConfigurations}\n\\end{itemize}\n\nYou can easily configure these settings in the System Setting\napplication, or as mentioned above, you can specify them in a deployable\nOSGi \\texttt{.config} file.\n\n\\chapter{Installing Liferay Enterprise Search\nSecurity}\\label{installing-liferay-enterprise-search-security}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe very first thing you must do to secure Elasticsearch is enable\nX-Pack Security. After that you can begin configuring authentication and\nTransport Layer Security.\n\n\\noindent\\hrulefill\n\n\\textbf{Elasticsearch 6.x:} If you're using Elasticsearch 6, you'll need\na Liferay Enterprise Search (LES) subscription to use X-Pack. Starting\nwith the Liferay Connector to Elasticsearch 7 (available on Liferay\nMarketplace), X-Pack security is included by default. X-Pack monitoring\nstill requires LES.\n\n\\noindent\\hrulefill\n\n\\section{Enabling X-Pack Security}\\label{enabling-x-pack-security}\n\nTo enable security, add this setting in \\texttt{elasticsearch.yml}:\n\n\\begin{verbatim}\nxpack.security.enabled: true\n\\end{verbatim}\n\nNow you can set up X-Pack users.\n\n\\section{Setting Up X-Pack Users}\\label{setting-up-x-pack-users}\n\nIn a system using X-Pack Security and X-Pack Monitoring, these built-in\nX-Pack users are important:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{kibana}\n\\item\n  \\texttt{elastic}\n\\end{itemize}\n\nSet the passwords for all X-Pack's\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/built-in-users.html}{built-in\nusers}. The \\texttt{setup-passwords} command is the simplest method to\nset the built-in users' first-use passwords for the first time. To\nupdate a password subsequently, use Kibana's UI or the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-change-password.html}{Change\nPassword API}.\n\nThe \\texttt{interactive} argument sets the passwords for all built-in\nusers. The configuration shown in these articles assumes you set all\npasswords to \\emph{liferay}. Of course, that's not recommended for\nproduction systems.\n\n\\begin{verbatim}\n./bin/elasticsearch-setup-passwords interactive\n\\end{verbatim}\n\nElastic's\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/setup-passwords.html}{setup-passwords\ncommand} documentation describes additional options.\n\nSince you're securing Elasticsearch, remember the \\texttt{elastic}\nuser's password.\n\nEnable transport layer security on each node.\n\n\\section{Enabling Transport Layer\nSecurity}\\label{enabling-transport-layer-security}\n\nThe following instructions for enabling TLS use \\texttt{liferay} as the\npassword whenever one is needed. Use your own passwords for your\ninstallation.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Elasticsearch and Liferay DXP must share the keys\nand certificates used to configure TLS. Copy them between servers and\npoint to the local copy in the corresponding configuration files.\n\n\\noindent\\hrulefill\n\n\\section{Generate Node Certificates}\\label{generate-node-certificates}\n\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/configuring-tls.html\\#node-certificates}{Generate\na node certificate} for each node. Alternatively, use a Certificate\nAuthority to obtain node certificates.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a certificate authority, using\n  \\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/certutil.html}{X-Pack's\n  \\texttt{certutil}} command:\n\n\\begin{verbatim}\n./bin/elasticsearch-certutil ca --pem --ca-dn CN=localhost\n\\end{verbatim}\n\n  This generates a ZIP file. Unzip the contents in the\n  \\texttt{{[}Elasticsearch\\ Home{]}/config/certs} folder.\n\\item\n  Generate X.509 certificates and private keys using the CA from Step 1:\n\n\\begin{verbatim}\n./bin/elasticsearch-certutil cert --pem --ca-cert /path/to/ca.crt --ca-key /path/to/ca.key --dns localhost --ip 127.0.0.1 --name localhost\n\\end{verbatim}\n\n  This generates another ZIP file. Extract the contents in the\n  \\texttt{{[}Elasticsearch\\ Home{]}/config/certs} folder.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\texttt{certutil} command defaults to using the\n\\emph{PKSC\\#12} format for certificate generation. Since Kibana does not\nwork with PKSC\\#12 certificates, the \\texttt{-\\/-pem} option (generates\nthe certificate in PEM format) is important if you're using X-Pack\nmonitoring.\n\n\\noindent\\hrulefill\n\n\\textbf{Checkpoint:} You now have the following files in your\n\\texttt{{[}Elasticsearch\\ Home{]}/config/certs} folder:\n\n\\begin{verbatim}\nca.crt\nca.key\nlocalhost.crt\nlocalhost.key\n\\end{verbatim}\n\n\\section{Enable TLS for Elasticsearch\n7}\\label{enable-tls-for-elasticsearch-7}\n\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/configuring-tls.html\\#enable-ssl}{Enable\nTLS} on each node via its \\texttt{elasticsearch.yml}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Enable transport layer TLS with these settings in\n  \\texttt{elasticsearch.yml} for inter-node communication:\n\n\\begin{verbatim}\nxpack.security.transport.ssl.enabled: true\n\\end{verbatim}\n\\item\n  Add the certificate, key and certificate authority paths to each\n  node's \\texttt{elasticsearch.yml}:\n\n\\begin{verbatim}\nxpack.security.transport.ssl.certificate: certs/localhost.key\nxpack.security.transport.ssl.certificate_authorities: [ \"certs/ca.crt\" ]\nxpack.security.transport.ssl.key: certs/localhost.crt\nxpack.security.transport.ssl.verification_mode: certificate\n\\end{verbatim}\n\n  The example paths above assume you added the certificate to\n  \\texttt{Elasticsearch\\ Home/config/}.\n\\item\n  Enable TLS on the HTTP layer to encrypt client communication:\n\n\\begin{verbatim}\nxpack.security.http.ssl.enabled: true\n\\end{verbatim}\n\\item\n  Configure the certificate, key, and certificate authority paths to\n  each node's \\texttt{elasticsearch.yml}:\n\n\\begin{verbatim}\nxpack.security.http.ssl.certificate_authorities: [ \"certs/ca.crt\" ]\nxpack.security.http.ssl.certificate: certs/localhost.crt\nxpack.security.http.ssl.key: certs/localhost.key\nxpack.security.http.ssl.verification_mode: certificate\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Elasticsearch 6 TLS}\\label{elasticsearch-6-tls}\n\nThe settings on Elasticsearch 6 were slightly different than those\npresented above for Elasticsearch 7.\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/configuring-tls.html\\#enable-ssl}{Enable\nTLS} on each node via its \\texttt{elasticsearch.yml}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the certificate, key and certificate authority paths to each\n  node's \\texttt{elasticsearch.yml}:\n\n\\begin{verbatim}\nxpack.ssl.certificate: certs/localhost.crt\nxpack.ssl.certificate_authorities: [ \"certs/ca.crt\" ]\nxpack.ssl.key: certs/localhost.key\nxpack.ssl.verification_mode: certificate \n\\end{verbatim}\n\n  The example paths above assume you added the certificate to\n  \\texttt{Elasticsearch\\ Home/config/}.\n\\item\n  Enable transport layer TLS with these settings in\n  \\texttt{elasticsearch.yml}:\n\n\\begin{verbatim}\nxpack.security.transport.ssl.enabled: true\n\\end{verbatim}\n\\item\n  Enable TLS on the HTTP layer to encrypt client communication:\n\n\\begin{verbatim}\nxpack.security.http.ssl.enabled: true\n\\end{verbatim}\n\\end{enumerate}\n\nAfter X-Pack is installed and TLS is enabled, configure the X-Pack\nSecurity adapter in Liferay DXP.\n\n\\section{Example Elasticsearch Security\nConfiguration}\\label{example-elasticsearch-security-configuration}\n\nFor ease of copying and pasting, here is the complete Elasticsearch\nconfiguration (\\texttt{elasticsearch.yml}) used in this guide (with the\nElasticsearch 6 example commented out):\n\n\\begin{verbatim}\n# For Elasticsearch 7.3/7.4\ncluster.name: LiferayElasticsearchCluster\n\n# X-Pack Security\nxpack.security.enabled: true\n\n## TLS/SSL settings for Transport layer\nxpack.security.transport.ssl.enabled: true\nxpack.security.transport.ssl.verification_mode: certificate \nxpack.security.transport.ssl.key: certs/localhost.key\nxpack.security.transport.ssl.certificate: certs/localhost.crt\nxpack.security.transport.ssl.certificate_authorities : [ \"certs/ca.crt\" ]\n\n# TLS/SSL settings for HTTP layer\nxpack.security.http.ssl.enabled: true\nxpack.security.http.ssl.verification_mode: certificate \nxpack.security.http.ssl.key: certs/localhost.key\nxpack.security.http.ssl.certificate: certs/localhost.crt\nxpack.security.http.ssl.certificate_authorities : [ \"certs/ca.crt\" ]\n\n# Comment out when Kibana and Liferay's X-Pack Monitoring are also configured\n#xpack.monitoring.collection.enabled: true\n\n# For Elasticsearch 6.5/6.8\n#cluster.name: LiferayElasticsearchCluster\n#\n# X-Pack Security\n#xpack.security.enabled: true\n#\n# Enable TLS/SSL\n#xpack.security.transport.ssl.enabled: true # To enable Transport level SSL for internode-communication\n#xpack.security.http.ssl.enabled: true # To enable HTTP level SSL required by Kibana\n#\n## General TLS/SSL settings for both Transport and HTTP levels\n#xpack.ssl.verification_mode: certificate \n#xpack.ssl.key: certs/localhost.key\n#xpack.ssl.certificate: certs/localhost.crt\n#xpack.ssl.certificate_authorities : [ \"certs/ca.crt\" ]\n#\n# Comment out when Kibana and Liferay's X-Pack Monitoring are also configured\n#xpack.monitoring.collection.enabled: true\n\\end{verbatim}\n\nFor both Elasticsearch 6 and Elasticsearch 7, the Liferay Connector\nsettings are the same.\n\n\\section{Install and Configure the Liferay Enterprise Search Security\napp}\\label{install-and-configure-the-liferay-enterprise-search-security-app}\n\nIf you have a Liferay Enterprise Search subscription,\n\\href{https://web.liferay.com/group/customer/dxp/downloads/enterprise-search}{download}\nthe Liferay Enterprise Search Security app . Install the LPKG file by\ncopying it into the \\texttt{Liferay\\ Home/deploy} folder.\n\nTo configure the X-Pack adapter, navigate to \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{System Settings}. Find the \\emph{Search}\ncategory and click on the \\emph{X-Pack Security} entry. You can enter\nthe property values here, but it's more common to use a\n\\href{/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files}{configuration\nfile} deployed to \\texttt{{[}Liferay\\ Home{]}/osgi/configs}. For the\nX-Pack security connector, create a file called\n\n\\begin{verbatim}\ncom.liferay.portal.search.elasticsearch7.configuration.XPackSecurityConfiguration.config\n\\end{verbatim}\n\nThe exact contents of the file depend on your X-Pack setup. To configure\nthe adapter according to the Elasticsearch setup documented here,\npopulate the file like this:\n\n\\begin{verbatim}\nsslKeyPath=\"/path/to/localhost.key\"\nsslCertificatePath=\"/path/to/localhost.crt\"\ncertificateFormat=\"PEM\"\nrequiresAuthentication=\"true\"\nusername=\"elastic\"\npassword=\"liferay\"\nsslCertificateAuthoritiesPaths=\"/path/to/ca.crt\"\ntransportSSLVerificationMode=\"certificate\"\ntransportSSLEnabled=\"true\"\n\\end{verbatim}\n\nThe \\texttt{password} should match what you set during the X-Pack\npassword setup above.\n\nThe certificate and key files referenced here are the same ones used on\nthe Elasticsearch server. Copy them to the Liferay DXP server and update\ntheir paths in the configuration accordingly.\n\nEnable authentication by setting \\texttt{requiresAuthentication} to\n\\texttt{true} and providing the credentials for the Elasticsearch user.\nFor TLS, enable transport TLS, set the certificate verification mode and\ncertificate format, and provide the path to the certificate, key, and\ncertificate authority. Of course, the exact values depend on your X-Pack\nconfiguration.\n\nHere's the complete list of configuration options for the X-Pack\nConnector:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{sslKeyPath}\n\\item\n  \\texttt{sslCertificatePath}\n\\item\n  \\texttt{sslCertificateAuthoritiesPaths}\n\\item\n  \\texttt{certificateFormat}\n\\item\n  \\texttt{requiresAuthentication}\n\\item\n  \\texttt{username}\n\\item\n  \\texttt{password}\n\\item\n  \\texttt{transportSSLVerificationMode}\n\\item\n  \\texttt{transportSSLEnabled}\n\\item\n  \\texttt{sslKeystorePath}\n\\item\n  \\texttt{sslKeystorePassword}\n\\item\n  \\texttt{sslTruststorePath}\n\\item\n  \\texttt{sslTruststorePassword}\n\\end{itemize}\n\nWhen you're finished configuring X-Pack Security, restart Elasticsearch.\nThese steps require a full cluster restart.\n\n\\section{Disabling Elasticsearch Deprecation\nLogging}\\label{disabling-elasticsearch-deprecation-logging}\n\nSome Elasticsearch APIs used by Liferay's Elasticsearch 6 connector were\ndeprecated as of Elasticsearch 6.6 and 6.7. This can result WARN log\nentries in Elasticsearch's deprecation log when Liferay DXP is\nconfigured with Elasticsearch 6.8.x and X-Pack Security is enabled:\n\n\\begin{verbatim}\n2019-07-16T14:47:05,779][WARN ][o.e.d.c.j.Joda           ] [\node_name]'y' year should be replaced with 'u'. Use 'y' for year-of-era. Prefix your date format with '8' to use the new specifier.\n[2019-07-16T14:47:06,007][WARN ][o.e.d.c.s.Settings       ] [\node_name][xpack.ssl.certificate] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version.\n[2019-07-16T14:47:06,007][WARN ][o.e.d.c.s.Settings       ] [\node_name][xpack.ssl.certificate_authorities] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version.\n[2019-07-16T14:47:06,008][WARN ][o.e.d.c.s.Settings       ] [\node_name][xpack.ssl.key] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version.\n[2019-07-16T14:47:06,463][WARN ][o.e.d.x.c.s.SSLService   ] [\node_name]SSL configuration [xpack.http.ssl] relies upon fallback to another configuration for [key configuration, trust configuration], which is deprecated.\n[2019-07-16T14:47:06,464][WARN ][o.e.d.x.c.s.SSLService   ] [\node_name]SSL configuration [xpack.security.transport.ssl.] relies upon fallback to another configuration for [key configuration, trust configuration], which is deprecated.\n\\end{verbatim}\n\nThese warnings do not signal any functional issues, and can be disabled\n(see\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/logging.html\\#deprecation-logging}{here}\nto learn how).\n\n\\chapter{Backing Up Elasticsearch}\\label{backing-up-elasticsearch}\n\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index-modules.html\\#index-modules-settings}{Elasticsearch\nreplicas} protect against a node going down, but they won't help you\nwith a catastrophic failure. Only good backup practices can help you\nthen.\n\nBack up and restore your Elasticsearch cluster in three steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Configure a repository\n\\item\n  Make a snapshot of the cluster\n\\item\n  Restore from the snapshot\n\\end{enumerate}\n\nFor more detailed information, refer to the\n\\href{https://www.elastic.co/guide/en/elasticsearch/guide/master/administration.html}{Elasticsearch\nadministration guide}, and in particular to the documentation on the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/snapshot-restore.html}{Snapshot\nand Restore module}.\n\n\\section{Creating a Repository}\\label{creating-a-repository}\n\nFirst\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/snapshots-register-repository.html}{create\na repository} to store your snapshots. Several repository types are\nsupported:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Shared file system, such as a Network File System or NAS\n\\item\n  Amazon S3\n\\item\n  HDFS (Hadoop Distributed File System)\n\\item\n  Azure Cloud\n\\end{itemize}\n\nIf using a shared file system repository type, first register the path\nto the shared file system in each node's \\texttt{elasticsearch.yml}\nusing\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/snapshots-register-repository.html\\#snapshots-filesystem-repository}{the\npath.repo setting}.\n\n\\begin{verbatim}\npath.repo: [\"path/to/shared/file/system/\"]\n\\end{verbatim}\n\nOnce the path to the folder hosting the repository is registered (make\nsure the folder exists), create the repository with a PUT command. For\nexample,\n\n\\begin{verbatim}\ncurl -X PUT \"localhost:9200/_snapshot/test_backup\" -H 'Content-Type: application/json' -d'\n{\n  \"type\": \"fs\",\n  \"settings\": {\n    \"location\": \"/path/to/shared/file/system/\"\n  }\n}'\n\\end{verbatim}\n\nReplace \\texttt{localhost:9200} with the proper \\texttt{hostname:port}\ncombination for your system, replace \\texttt{test\\_backup} with the name\nof the repository to create, and use the absolute path to your shared\nfile system in the \\texttt{location}.\n\nIf the repository is set up successfully, you see this message:\n\n\\begin{verbatim}\n{\"acknowledged\":true}\n\\end{verbatim}\n\nOnce the repository exists, you can start creating snapshots.\n\n\\section{Taking Snapshots of the\nCluster}\\label{taking-snapshots-of-the-cluster}\n\nThe easiest snapshot approach is to create a\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/snapshots-take-snapshot.html}{snapshot\nof all the indexes in your cluster}. For example,\n\n\\begin{verbatim}\ncurl -XPUT localhost:9200/_snapshot/test_backup/snapshot_1\n\\end{verbatim}\n\nIf \\texttt{\\{\"accepted\":true\\}} appears in the terminal, the snapshot\nwas a success.\n\nIt's possible to be more selective when taking snapshots. For example,\nif you use LES Monitoring, you can exclude the monitoring indexes.\nExplicitly declare the indexes to include in the snapshot. For example,\n\n\\begin{verbatim}\ncurl -XPUT localhost:9200/_snapshot/test_backup/snapshot_2\n{ \"indices\": \"liferay-0,liferay-20116\" }\n\\end{verbatim}\n\n\\textbf{Note:} For a list of all the Elasticsearch indexes, use this\ncommand:\n\n\\begin{verbatim}\ncurl -X GET \"localhost:9200/_cat/indices?v\"\n\\end{verbatim}\n\nThis shows the index metrics:\n\n\\begin{verbatim}\nhealth status index         uuid                   pri rep docs.count docs.deleted store.size pri.store.size\ngreen  open   liferay-20099 obqiNE1_SDqfuz7rincrGQ   1   0        195            0    303.1kb        303.1kb\ngreen  open   liferay-47206 3YEjtye1S9OVT0i0EZcXcw   1   0          7            0     69.7kb         69.7kb\ngreen  open   liferay-0     shBWwpkXRxuAmGEaE475ug   1   0        147            1    390.9kb        390.9kb\n\\end{verbatim}\n\nIt's important to note that Elasticsearch uses a \\emph{smart snapshots}\napproach. To understand what that means, consider a single index. The\nfirst snapshot includes a copy of the entire index, while subsequent\nsnapshots only include the delta between the first, complete index\nsnapshot and the current state of the index.\n\nEventually you'll end up with a lot of snapshots in your repository, and\nno matter how cleverly you name the snapshots, you may forget what some\nsnapshots contain. For this purpose, the Elasticsearch API provides\ngetting information about any snapshot. For example:\n\n\\begin{verbatim}\ncurl -XGET localhost:9200/_snapshot/test_backup/snapshot_1\n\\end{verbatim}\n\nreturns\n\n\\begin{verbatim}\n{\"snapshots\":[\n    {\"snapshot\":\"snapshot_1\",\n    \"uuid\":\"WlSjvJwHRh-xlAny7zeW3w\",\n    \"version_id\":6.80399,\n    \"version\":\"6.8.2\",\n    \"indices\":[\"liferay-20099\",\"liferay-0\",\"liferay-47206\"],\n    \"state\":\"SUCCESS\",\n    \"start_time\":\"2018-08-15T21:40:17.261Z\",\n    \"start_time_in_millis\":1534369217261,\n    \"end_time\":\"2018-08-15T21:40:17.482Z\",\n    \"end_time_in_millis\":1534369217482,\n    \"duration_in_millis\":221,\n    \"failures\":[],\n    \"shards\":{\n        \"total\":3,\n        \"failed\":0,\n        \"successful\":3\n        \n        }\n    }\n]}\n\\end{verbatim}\n\nThere's lots of useful information here, including which indexes were\nincluded in the snapshot.\n\nIf you want to get rid of a snapshot, use the \\texttt{DELETE} command.\n\n\\begin{verbatim}\ncurl -XDELETE localhost:9200/_snapshot/test_backup/snapshot_1\n\\end{verbatim}\n\nYou might trigger creation of a snapshot and regret it (for example, you\ndidn't want to include all the indexes in the snapshot). If your\nsnapshots contain a lot of data, this can cost time and resources. To\ncancel the ongoing creation of a snapshot, use the same \\texttt{DELETE}\ncommand. The snapshot process is terminated and the partial snapshot is\ndeleted from the repository.\n\n\\section{Restoring from a Snapshot}\\label{restoring-from-a-snapshot}\n\nWhat good is a snapshot if you can't use it to\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/snapshots-restore-snapshot.html}{restore\nyour search indexes} in case of catastrophic failure? Use the\n\\texttt{\\_restore} API to restore all the snapshot's indexes:\n\n\\begin{verbatim}\ncurl -XPOST localhost:9200/_snapshot/test_backup/snapshot_1/_restore\n\\end{verbatim}\n\nRestore only specific indexes from a snapshot by passing in the\n\\texttt{indices} option, and rename the indexes using the\n\\texttt{rename\\_pattern} and \\texttt{rename\\_replacement} options:\n\n\\begin{verbatim}\ncurl -XPOST\nlocalhost:9200/_snapshot/test_backup/snapshot_1/_restore\n{\n    \"indices\": \"liferay-20116\",\n    \"rename_pattern\": \"liferayindex_(.+)\",\n    \"rename_replacement\": \"restored_liferayindex_$1\"\n}\n\\end{verbatim}\n\nThis restores only the index named \\texttt{liferay-20116index\\_1} from\nthe snapshot. The \\texttt{rename...} settings specify that the beginning\n\\texttt{liferayindex\\_} are replaced with\n\\texttt{restored\\_liferayindex\\_}, so \\texttt{liferay-20116index\\_1}\nbecomes \\texttt{restored\\_liferay-20116index\\_1}.\n\nAs with the process for taking snapshots, an errant restored index can\nbe canceled with the \\texttt{DELETE} command:\n\n\\begin{verbatim}\ncurl -XDELETE localhost:9200/restored_liferay-20116index_3\n\\end{verbatim}\n\nNobody likes catastrophic failure on a production system, but\nElasticsearch's API for taking snapshots and restoring indexes can help\nyou rest easy knowing that your search cluster can be restored if\ndisaster strikes. For more details and options, read Elastic's\ndocumentation on the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/snapshot-restore.html}{Snapshot\nand Restore Module}.\n\n\\chapter{Upgrading to Elasticsearch\n6.5}\\label{upgrading-to-elasticsearch-6.5}\n\nElasticsearch 6.5.x is the default, most up-to-date supported version of\nElasticsearch for Liferay DXP. If you're upgrading @product@ and still\nrunning Elasticsearch 6.1, it's time to upgrade your Elasticsearch\nservers too. If you're setting up a new system and not already running a\nremote Elasticsearch 6.1.x server, follow the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch}{installation\nguide} to install Elasticsearch and the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector}{configuration\nguide} to configure the Elasticsearch adapter. Here, you'll learn to\nupgrade an existing Elasticsearch 6.1.x server (or cluster) to\nElasticsearch 6.5.x:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/elasticsearch}{Install and\n  configure Elasticsearch 6.5.x}.\n\\item\n  Disable X-Pack Security in \\texttt{elasticsearch.yml} unless you have\n  an Liferay Enterprise Search subscription, which gives you access to\n  the Liferay Enterprise Search Security app:\n\n\\begin{verbatim}\nxpack.security.enabled: false\n\\end{verbatim}\n\\item\n  Configure the bundled Liferay Connector to Elasticsearch 6.\n\\item\n  Re-index all search and spell check indexes.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Before Proceeding,} back up your existing data before upgrading\nElasticsearch. If something goes wrong during or after the upgrade, roll\nback to the previous version using the uncorrupted index snapshots. See\n\\href{/docs/7-2/deploy/-/knowledge_base/d/backing-up-elasticsearch}{here}\nfor more information.\n\n\\noindent\\hrulefill\n\nLearn about configuring Elasticsearch\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector}{here}.\n\n\\section{Re-index}\\label{re-index}\n\nOnce the Elasticsearch adapter is installed and talking to the\nElasticsearch cluster, navigate to \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{Server Administration}, and click\n\\emph{Execute} for the \\emph{Reindex all search indexes} entry.\n\nYou must also re-index the spell check indexes.\n\n\\section{Reverting to Elasticsearch\n6.1}\\label{reverting-to-elasticsearch-6.1}\n\nStuff happens. If that stuff involves an unrecoverable failure during\nthe upgrade to Elasticsearch 6.5, roll back to Elasticsearch 6.1 and\nregroup.\n\nSince your 6.1 and 6.5 are currently two separate installations, this\nprocedure is straightforward:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Stop the Liferay Connector to Elasticsearch 6.\n\\item\n  Stop Elasticsearch 6.5 and make sure that the Elasticsearch 6.1\n  \\texttt{elasticsearch.yml} and the connector app are configured to use\n  the same port (9200 by default).\n\\item\n  Start the Elasticsearch server, and then restart the Liferay Connector\n  to Elasticsearch 6.\n\\end{enumerate}\n\n\\chapter{Upgrading to Elasticsearch\n6.8}\\label{upgrading-to-elasticsearch-6.8}\n\nElasticsearch 6.8.x is supported for 7.0. If you're upgrading Liferay\nDXP and still running Elasticsearch 6.1, it's time to upgrade your\nElasticsearch servers too. If you're setting up a new system and not\nalready running a remote Elasticsearch 6.1.x server, follow the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch}{installation\nguide} to install Elasticsearch and the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector}{configuration\nguide} to configure the Elasticsearch adapter. Here, you'll learn to\nupgrade an existing Elasticsearch 6.1.x server (or cluster) to\nElasticsearch 6.8.x:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/elasticsearch}{Install and\n  configure Elasticsearch 6.8.x}.\n\\item\n  Disable X-Pack Security in \\texttt{elasticsearch.yml} unless you have\n  an Liferay Enterprise Search subscription which gives you access to\n  the LES Security app:\n\n\\begin{verbatim}\nxpack.security.enabled: false\n\\end{verbatim}\n\\item\n  Configure the bundled Liferay Connector to Elasticsearch 6.\n\\item\n  Re-index all search and spell check indexes.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Before Proceeding,} back up your existing data before upgrading\nElasticsearch. If something goes wrong during or after the upgrade, roll\nback to the previous version using the uncorrupted index snapshots. See\n\\href{/docs/7-2/deploy/-/knowledge_base/d/backing-up-elasticsearch}{here}\nfor more information.\n\n\\noindent\\hrulefill\n\nLearn about configuring Elasticsearch\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector}{here}.\n\n\\section{Re-index}\\label{re-index-1}\n\nOnce the Elasticsearch adapter is installed and talking to the\nElasticsearch cluster, navigate to \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{Server Administration}, and click\n\\emph{Execute} for the \\emph{Reindex all search indexes} entry.\n\nYou must also re-index the spell check indexes.\n\n\\section{Reverting to Elasticsearch\n6.1}\\label{reverting-to-elasticsearch-6.1-1}\n\nStuff happens. If that stuff involves an unrecoverable failure during\nthe upgrade to Elasticsearch 6.8, roll back to Elasticsearch 6.1 and\nregroup.\n\nSince your 6.1 and 6.8 are currently two separate installations, this\nprocedure is straightforward:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Stop the Liferay Connector to Elasticsearch 6.\n\\item\n  Stop Elasticsearch 6.8 and make sure that the Elasticsearch 6.1\n  \\texttt{elasticsearch.yml} and the connector app are configured to use\n  the same port (9200 by default).\n\\item\n  Start the Elasticsearch server, and then restart the Liferay Connector\n  to Elasticsearch 6.\n\\end{enumerate}\n\n\\chapter{Upgrading to Elasticsearch\n7}\\label{upgrading-to-elasticsearch-7}\n\nElasticsearch 7 is supported for 7.0. If you're upgrading Liferay DXP\nand still running Elasticsearch 6, consider upgrading your Elasticsearch\nservers too. If you're setting up a new system and not already running a\nremote Elasticsearch 6 server, follow the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch}{installation\nguide} to install Elasticsearch and the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector}{configuration\nguide} to configure the Elasticsearch adapter.\n\n\\noindent\\hrulefill\n\n\\textbf{Before Proceeding,} back up your existing data before upgrading\nElasticsearch. If something goes wrong during or after the upgrade, roll\nback to the previous version using the uncorrupted index snapshots. See\n\\href{/docs/7-2/deploy/-/knowledge_base/d/backing-up-elasticsearch}{here}\nfor more information.\n\n\\noindent\\hrulefill\n\nHere, you'll learn to upgrade an existing Elasticsearch 6 server (or\ncluster) to Elasticsearch 7:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch}{Install\n  and configure Elasticsearch 7}.\n\\item\n  Back up the application specific indexes for Workflow Metrics and\n  Result Rankings.\n\\item\n  In 7.0, security is now provided out of the box. If you're using\n  X-Pack security, enable it (it's disabled by default):\n\n\\begin{verbatim}\nxpack.security.enabled: true\n\\end{verbatim}\n\\item\n  Blacklist the bundled Liferay Connector to Elasticsearch 6.\n\\item\n  Install and configure the Liferay Connector to Elasticsearch 7.\n\\item\n  Re-index all search and spell check indexes.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Known Issue:} See\n\\href{https://issues.liferay.com/browse/LPS-103938}{LPS-103938}. The\nLiferay Connector to Elasticsearch 7 throws an exception in the log when\nthe LPKG file is deployed. There are no known functional impacts. If\nunexpected errors occur, re-start the Liferay DXP server.\n\n\\noindent\\hrulefill\n\nLearn about configuring Elasticsearch\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector}{here}.\n\n\\section{Backing up Application-Specific\nIndexes}\\label{backing-up-application-specific-indexes}\n\nTo preserve data stored in application-specific indexes, use a\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/rolling-upgrades.html}{rolling\nupgrade} for each index you need to preserve across the upgrade.\n\n\\noindent\\hrulefill\n\n\\textbf{Synonym Sets:} If you follow the workaround for the bug\n\\href{https://issues.liferay.com/browse/LPS-100272}{LPS-100272}, your\nSynonym sets are preserved across the upgrade, as they are stored in the\nindex settings directly, and not in their own index.\n\n\\noindent\\hrulefill\n\n\\section{Blacklisting Elasticsearch\n6}\\label{blacklisting-elasticsearch-6}\n\nTo blacklist Elasticsearch 6,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a configuration file named\n\n\\begin{verbatim}\ncom.liferay.portal.bundle.blacklist.internal.BundleBlacklistConfiguration.config\n\\end{verbatim}\n\\item\n  Give it these contents:\n\n\\begin{verbatim}\nblacklistBundleSymbolicNames=[ \\\n    \"com.liferay.portal.search.elasticsearch6.api\", \\\n    \"com.liferay.portal.search.elasticsearch6.impl\", \\\n    \"com.liferay.portal.search.elasticsearch6.spi\", \\\n    \"com.liferay.portal.search.elasticsearch6.xpack.security.impl\", \\\n    \"Liferay Connector to X-Pack Security [Elastic Stack 6.x] - Impl\", \\\n    \"Liferay Enterprise Search Security  - Impl\" \\\n]\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Re-index}\\label{re-index-2}\n\nOnce the Elasticsearch adapter is installed and talking to the\nElasticsearch cluster, navigate to \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{Search}, and click \\emph{Execute} for the\n\\emph{Reindex all search indexes} entry.\n\nYou must also re-index the spell check indexes.\n\n\\section{Reverting to Elasticsearch\n6}\\label{reverting-to-elasticsearch-6}\n\nStuff happens. If that stuff involves an unrecoverable failure during\nthe upgrade to Elasticsearch 7, roll back to Elasticsearch 6 and\nregroup.\n\nSince your Elasticsearch 6 and 7 are currently two separate\ninstallations, this procedure takes only a few steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Stop the Liferay Connector to Elasticsearch 6.\n\\item\n  Stop Elasticsearch 7 and make sure that the Elasticsearch 6\n  \\texttt{elasticsearch.yml} and the connector app are configured to use\n  the same port (9200 by default).\n\\item\n  Start the Elasticsearch server, and then restart the Liferay Connector\n  to Elasticsearch 6.\n\\end{enumerate}\n\n\\chapter{Installing Liferay Enterprise\nSearch}\\label{installing-liferay-enterprise-search}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nA Liferay Enterprise Search (LES) subscription gets you additional\nfeatures beyond what's available out of the box with your Liferay DXP\nsubscription. It includes\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay Enterprise Search Security*\n\\item\n  Liferay Enterprise Search Monitoring\n\\item\n  Liferay Enterprise Search Learning to Rank\n\\end{itemize}\n\n* A LES subscription is not necessary if using Elasticsearch 7 via the\n\\_Liferay Connector to Elasticsearch 7\\_as X-Pack's security features\nare bundled. See the\n\\href{https://help.liferay.com/hc/en-us/articles/360016511651\\#Liferay-Enterprise-Search}{LES\ncompatibility matrix} for more information.\n\nX-Pack is an\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/setup-xpack.html}{Elasticsearch\nextension} for securing and monitoring Elasticsearch clusters. If you\nuse Elasticsearch, you should secure it with X-Pack. The security\nfeatures of X-Pack include authenticating access to the Elasticsearch\ncluster's data and encrypting Elasticsearch's internal and external\ncommunications. These are necessary security features for most\nproduction systems. A LES subscription gets you access to two connectors\nif you're using Elasticsearch 6: monitoring and security. Elasticsearch\n7 bundles these security features, and Liferay has followed suit.\nTherefore, security is bundled with the Liferay Connector to\nElasticsearch 7, and no LES subscription is necessary. Because of this,\nthe documentation for\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-liferay-enterprise-search-security}{installing\nLiferay Enterprise Search Security} on Liferay DXP has been moved from\nthe LES documentation section (this section) to the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/elasticsearch}{Elasticsearch}\ninstallation and configuration guide. Contact\n\\href{https://www.liferay.com/contact-us\\#contact-sales}{Liferay's Sales\ndepartment for more information}.\n\nHere's an overview of using the LES applications with Liferay DXP:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Get an\n  \\href{https://help.liferay.com/hc/en-us/articles/360014400932}{Enterprise\n  Search subscription}.\n\\item\n  You'll receive a license for X-Pack monitoring. Install it on your\n  Elasticsearch servers.\n\n  \\textbf{Note:} If using Elasticsearch 6, you'll also need a LES\n  subscription for X-Pack security.\n\\item\n  Download and install the Liferay Enterprise Search apps you purchased.\n  Find them in the \\href{https://customer.liferay.com/en/downloads}{Help\n  Center Downloads page}, choosing Enterprise Search from the Product\n  drop-down menu.\n\\item\n  Configure the connectors with the proper credentials, encryption\n  information, and settings.\n\\item\n  Restart Elasticsearch. These steps require a full cluster restart.\n\\end{enumerate}\n\nMore detailed installation instructions are available in the article for\neach LES feature.\n\nElastic's documentation explains additional configuration options,\nfeatures, and the architecture of\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/configuring-security.html}{X-Pack}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Out of the box, X-Pack comes with a\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/start-trial.html}{30-day\ntrial}. This can be useful if there's a delay between your subscription\nand receipt of your production X-Pack license.\n\n\\noindent\\hrulefill\n\nNow configure security, monitoring, and/or Learning to Rank, depending\non your needs.\n\n\\chapter{Installing Liferay Enterprise Search\nMonitoring}\\label{installing-liferay-enterprise-search-monitoring}\n\nFirst configure security if you're using X-Pack's security features.\nThen come back here for instructions on installing and configuring\nKibana (the monitoring server) with X-Pack so that Elasticsearch\n(secured with X-Pack), Kibana (secured with X-Pack), and Liferay DXP can\ncommunicate effortlessly and securely. A Liferay Enterprise Search (LES)\nsubscription is necessary for this integration. Contact\n\\href{https://www.liferay.com/contact-us\\#contact-sales}{Liferay's Sales\ndepartment for more information}.\n\n\\noindent\\hrulefill\n\n\\textbf{The same monitoring connector is used for Elasticsearch 6 and\n7}: When first created, the Liferay Enterprise Search Monitoring app was\nintended to be used only with Elasticsearch 6. However, testing revealed\nthat the same connector works with Elasticsearch 7, so a new connector\nis not necessary if you're using Elasticsearch 7.\n\n\\noindent\\hrulefill\n\nTo install X-Pack monitoring,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Tell Elasticsearch to enable data collection.\n\\item\n  Download and install Kibana.\n\\item\n  Configure Kibana with the proper security settings.\n\\item\n  Install the Liferay Enterprise Search Monitoring app.\n\\item\n  Configure the connector to communicate with Elasticsearch.\n\\end{enumerate}\n\nThis document assumes you're enabling security (with authentication and\nencrypted communication) \\emph{and} monitoring for Elasticsearch 7, but\ndifferences in the process for Elasticsearch 6 are noted where\nnecessary.\n\n\\section{Enable Encrypting Communication (TLS/SSL) in Elasticsearch and\nin Liferay\nDXP}\\label{enable-encrypting-communication-tlsssl-in-elasticsearch-and-in-liferay-dxp}\n\nStart by following the steps in this\n\\href{/docs/7-2/deployment/-/knowledge_base/u/installing-liferay-enterprise-search-security}{article}\nto enable TLS/SSL in your Elasticsearch and Liferay DXP installation.\n\nThen continue by enabling data collection in Elasticsearch.\n\n\\section{Enable Data Collection}\\label{enable-data-collection}\n\nMonitoring is enabled on Elasticsearch by default, but data collection\nisn't. Enable data collection by adding this line to\n\\texttt{elasticsearch.yml}.\n\n\\begin{verbatim}\nxpack.monitoring.collection.enabled: true\n\\end{verbatim}\n\nNow install Kibana.\n\n\\section{Install Kibana}\\label{install-kibana}\n\nMake sure to install the correct version of Kibana. Check the\n\\href{https://help.liferay.com/hc/en-us/articles/360016511651\\#Liferay-Enterprise-Search}{Liferay\nEnterprise Search compatibility matrix} for details.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{https://www.elastic.co/downloads/kibana}{Download Kibana} and\n  extract it. The root folder is referred to as \\emph{Kibana Home}.\n\\item\n  Tell Kibana where to send monitoring data by setting Elasticsearch's\n  URL in \\texttt{kibana.yml}:\n\n\\begin{verbatim}\nelasticsearch.hosts: [ \"https://localhost:9200\" ]\n\\end{verbatim}\n\n  On 6.5 and below, use\n\n\\begin{verbatim}\nelasticsearch.url: \"https://localhost:9200\"\n\\end{verbatim}\n\n  If TLS/SSL is not enabled on Elasticsearch, this is an \\texttt{http}\n  URL, otherwise use \\texttt{https}.\n\\item\n  If not using X-Pack security, start Kibana by opening a command prompt\n  to Kibana Home and entering this command:\n\n\\begin{verbatim}\n./bin/kibana\n\\end{verbatim}\n\\end{enumerate}\n\nIf you're using X-Pack's security features on the Elasticsearch server,\nthere's additional configuration required before starting Kibana.\n\n\\section{Configure Kibana with\nAuthentication}\\label{configure-kibana-with-authentication}\n\nIf X-Pack requires authentication to access the Elasticsearch cluster,\nfollow these steps or refer to\n\\href{https://www.elastic.co/guide/en/kibana/7.x/monitoring-xpack-kibana.html}{Elastic's\ndocumentation}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Set the password for the built-in \\texttt{kibana} user in\n  \\texttt{{[}Kibana\\ Home{]}/config/kibana.yml}:\n\n\\begin{verbatim}\nelasticsearch.username: \"kibana\"\nelasticsearch.password: \"liferay\"\n\\end{verbatim}\n\n  Use your \\texttt{kibana} user password from your X-Pack setup. Once\n  Kibana is installed, you can change the built-in user passwords from\n  the \\emph{Management} user interface.\n\\item\n  If you're not encrypting communication with the Elasticsearch cluster,\n  start Kibana from Kibana home.\n\n\\begin{verbatim}\n./bin/kibana\n\\end{verbatim}\n\\item\n  Go to \\texttt{http://localhost:5601} and make sure you can sign in as\n  a\n  \\href{https://www.elastic.co/guide/en/elasticsearch/reference/current/realms.html}{user}\n  who has the \\texttt{kibana\\_user}\n  \\href{https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-roles.html}{role}\n  or a superuser (like the \\texttt{elastic} user).\n\\end{enumerate}\n\n\\section{Configuring Kibana with Encryption\n(TLS/SSL)}\\label{configuring-kibana-with-encryption-tlsssl}\n\nFollow these steps to configure Kibana if X-Pack encrypts communication\nwith the Elasticsearch cluster. Consult\n\\href{https://www.elastic.co/guide/en/kibana/7.x/using-kibana-with-security.html\\#using-kibana-with-security}{Elastic's\nguide} for more information.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Copy the \\texttt{{[}Elasticsearch\\ Home{]}/config/certs} folder into\n  the \\texttt{{[}Kibana\\ Home{]}/config/} folder.\n\n  This example reuses the certificate files\n  \\href{/docs/7-2/deployment/-/knowledge_base/u/installing-liferay-enterprise-search-security}{created\n  for Elasticsearch itself}. If you wish to generate a separate\n  certificate for your Kibana instance, make sure it is signed by the\n  same CA as the Elasticsearch node certificates.\n\\item\n  Add these settings to \\texttt{kibana.yml}:\n\n\\begin{verbatim}\nxpack.security.encryptionKey: \"xsomethingxatxleastx32xcharactersx\"\nxpack.security.sessionTimeout: 600000\n\nelasticsearch.hosts: [ \"https://localhost:9200\" ]\n\nelasticsearch.ssl.verificationMode: certificate\nelasticsearch.ssl.certificateAuthorities: [ \"config/certs/ca.crt\" ]\nelasticsearch.ssl.certificate: config/certs/localhost.crt\nelasticsearch.ssl.key: config/certs/localhost.key\n\nserver.ssl.enabled: true\nserver.ssl.certificateAuthorities: [ \"config/certs/ca.crt\" ]\nserver.ssl.certificate: config/certs/localhost.crt\nserver.ssl.key: config/certs/localhost.key\n\\end{verbatim}\n\\end{enumerate}\n\nElasticsearch/Kibana 6.5 and below use a different property for\nspecifying the host URL. Replace the \\texttt{elasticsearch.hosts}\nproperty with\n\n\\begin{verbatim}\nelasticsearch.url: \"https://localhost:9200\"\n\\end{verbatim}\n\nFor more information about monitoring and security best practices in a\nclustered environment, refer to\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/setup-xpack.html}{Elastic's\ndocumentation}.\n\nAfter this step you can access Kibana at \\texttt{https://localhost:5601}\nand sign in with a Kibana user. The last step is to connect Kibana to\nLiferay DXP.\n\n\\section{Configuring the Liferay Enterprise Search Monitoring\napp}\\label{configuring-the-liferay-enterprise-search-monitoring-app}\n\nIf you have a LES subscription, download the Liferay Enterprise Search\nMonitoring app . Install the LPKG file by copying it into the\n\\texttt{Liferay\\ Home/deploy} folder.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Once the connector is installed and Kibana and Elasticsearch are\n  securely configured, create a\n  \\href{/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files}{configuration\n  file} named\n\n\\begin{verbatim}\ncom.liferay.portal.search.elasticsearch6.xpack.monitoring.web.internal.configuration.XPackMonitoringConfiguration.config\n\\end{verbatim}\n\\item\n  Place these settings in the \\texttt{.config} file:\n\n\\begin{verbatim}\nkibanaPassword=\"liferay\"\nkibanaUserName=\"elastic\"\nkibanaURL=\"https://localhost:5601\"\n\\end{verbatim}\n\n  The values depend on your Kibana configuration. For example, use a URL\n  such as \\texttt{kibanaURL=\"http://localhost:5601\"} if you are not\n  using X-Pack Security TLS/SSL features.\n\n  Alternatively, configure the monitoring adapter from\n  \\href{/docs/7-2/user/-/knowledge_base/u/system-settings}{System\n  Settings}. Navigate to \\emph{Control Panel} → \\emph{Configuration} →\n  \\emph{System Settings} and find the X-Pack Monitoring entry in the\n  Search category. All the configuration options for the monitoring\n  connector appear there.\n\\item\n  Deploy this configuration file to \\texttt{Liferay\\ Home/osgi/configs},\n  and your running instance applies the settings. There's no need to\n  restart the server.\n\\item\n  There are two more settings to add to Kibana itself. The first forbids\n  Kibana from rewriting requests prefixed with \\texttt{server.basePath}.\n  The second sets Kibana's base path for the Monitoring portlet to act\n  as a proxy for Kibana's monitoring UI. Add this to\n  \\texttt{kibana.yml}:\n\n\\begin{verbatim}\nserver.rewriteBasePath: false\nserver.basePath: \"/o/portal-search-elasticsearch-xpack-monitoring/xpack-monitoring-proxy\"\n\\end{verbatim}\n\n  Note that once you set the \\texttt{server.basePath}, you cannot access\n  the Kibana UI through Kibana's URL (e.g.,\n  \\texttt{https://localhost:5601}). All access to the Kibana UI is\n  through the Monitoring portlet, which is only accessible to signed in\n  Liferay DXP users. Navigate directly to the portlet using this URL:\n\n  \\url{http://localhost:8080/o/portal-search-elasticsearch-xpack-monitoring/xpack-monitoring-proxy/app/monitoring}\n\\item\n  Because you're using the Monitoring portlet in Liferay DXP as a proxy\n  to Kibana's UI, if you are using X-Pack Security with TLS/SSL, you\n  must configure the application server's startup JVM parameters to\n  recognize a valid \\emph{truststore} and \\emph{password}.\n\n  First, navigate to Elasticsearch Home and generate a PKSC\\#12\n  certificate from the CA you created when setting up X-Pack security:\n\n\\begin{verbatim}\n./bin/elasticsearch-certutil cert --ca-cert /path/to/ca.crt --ca-key /path/to/ca.key --ip 127.0.0.1 --dns localhost --name localhost --out /path/to/Elasticsearch_Home/config/localhost.p12\n\\end{verbatim}\n\n  Next use the \\texttt{keytool} command to generate a truststore:\n\n\\begin{verbatim}\nkeytool -importkeystore -deststorepass liferay -destkeystore /path/to/truststore.jks -srckeystore /path/to/Elasticsearch_Home/config/localhost.p12 -srcstoretype PKCS12 -srcstorepass liferay\n\\end{verbatim}\n\n  Add the trustore path and password to your application server's\n  startup JVM parameters. Here are example truststore and path\n  parameters for appending to a Tomcat server's \\texttt{CATALINA\\_OPTS}:\n\n\\begin{verbatim}\n-Djavax.net.ssl.trustStore=/path/to/truststore.jks -Djavax.net.ssl.trustStorePassword=liferay\n\\end{verbatim}\n\\end{enumerate}\n\nRestart Liferay DXP and Kibana.\n\n\\section{Monitoring in Liferay DXP}\\label{monitoring-in-liferay-dxp}\n\nOnce Kibana and X-Pack are successfully installed and configured and all\nthe servers are running, add the X-Pack Monitoring portlet to a page:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\emph{Add} menu on a page and choose \\emph{Widgets}\n\\item\n  Search for \\emph{monitoring} and drag the \\emph{X-Pack Monitoring}\n  widget from the Search category onto the page.\n\\end{enumerate}\n\nSee the Elastic documentation for information on\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/es-monitoring.html}{monitoring\nElasticsearch}.\n\n\\section{Example Kibana\nConfiguration}\\label{example-kibana-configuration}\n\nHere are the \\texttt{kibana.yml} properties demonstrated in this\narticle, for convenient copy/pasting:\n\n\\begin{verbatim}\n# X-Pack Security enabled (Basic Auth)\nelasticsearch.username: \"kibana\"\nelasticsearch.password: \"liferay\"\n\n# When TLS/SSL is enabled in X-Pack Security\nxpack.security.encryptionKey: \"xsomethingxatxleastx32xcharactersx\"\nxpack.security.sessionTimeout: 600000\n\n# If on Elasticsearch 6.5 or below, replace the next property with:\n# elasticsearch.url: \"http://localhost:9200\"\nelasticsearch.hosts: [ \"https://localhost:9200\" ]\n\n# Enable TLS/SSL for out-bound traffic: from Kibana to Elasticsearch\nelasticsearch.ssl.verificationMode: certificate\nelasticsearch.ssl.certificateAuthorities: [ \"config/certs/ca.crt\" ]\nelasticsearch.ssl.certificate: config/certs/localhost.crt\nelasticsearch.ssl.key: config/certs/localhost.key\n\n# Enable TLS/SSL for in-bound traffic: from browser or\n#  DXP (X-Pack Monitoring widget, proxy) to Kibana\nserver.ssl.enabled: true\nserver.ssl.certificateAuthorities: [ \"config/certs/ca.crt\" ]\nserver.ssl.certificate: config/certs/localhost.crt\nserver.ssl.key: config/certs/localhost.key\n\n# To use Kibana inside the X-Pack Monitoring widget\nserver.rewriteBasePath: false\nserver.basePath: \"/o/portal-search-elasticsearch-xpack-monitoring/xpack-monitoring-proxy\"\n\\end{verbatim}\n\n\\chapter{Liferay Enterprise Search: Learning to\nRank}\\label{liferay-enterprise-search-learning-to-rank}\n\nSearch engines like Elasticsearch have well-tuned relevance algorithms,\ngood for general search purposes. Sometimes, this ``generally good''\nrelevance scoring just isn't good enough. You can attain more perfect\nsearch results by employing machine learning.\n\nLearning to Rank harnesses machine learning to improve search result\nrankings. It combines the expertise of data scientists with machine\nlearning to produce a smarter scoring function that's applied to search\nqueries.\n\n7.0, Service Pack 1/Fix Pack 2 and later, supports Learning to Rank\nthrough its support of Elasticsearch versions 6.x and 7.4.x. It requires\na \\href{https://help.liferay.com/hc/en-us/articles/360014400932}{LES}\nsubscription. It's important to understand that the\n\\href{https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/index.html}{Elasticsearch\nLearning to Rank plugin} is not produced by Elastic, and there is not a\npre-built plugin for all of Liferay DXP's supported Elasticsearch\nversions.\n\n\\section{Disabling Learning to Rank on a Search\nPage}\\label{disabling-learning-to-rank-on-a-search-page}\n\nLearning to Rank does not work with the\n\\href{/docs/7-2/user/-/knowledge_base/u/sorting-search-results-with-the-sort-widget}{Sort\nwidget}.\n\nIf you must use Learning to Rank in your Liferay DXP instance, but want\nto disable it on a particular Search page (perhaps to use the Sort\nwidget), you can:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a\n  \\href{/docs/7-2/user/-/knowledge_base/u/low-level-search-options-searching-additional-or-alternate-indexes}{Low\n  Level Search Options} widget to the Search page.\n\\item\n  Open the widget's Configuration screen by clicking\n\n  \\emph{Configure additional low level search options in this page.}\n\\item\n  In the \\emph{Contributors to Exclude} field, enter\n\n  \\texttt{com.liferay.portal.search.learning.to.rank}\n\\end{enumerate}\n\nNow the Learning to Rank re-scoring process is skipped for queries\nentered into the page's Search Bar, and results are sortable in the Sort\nwidget and returned using the default relevance algorithm.\n\n\\section{Prerequisites}\\label{prerequisites}\n\nThere are some prerequisites for using Learning to Rank to re-score\nLiferay queries sent to Elasticsearch:\n\n\\begin{itemize}\n\\item\n  If using Elasticsearch 7, 7.0 Service Pack 1/Fix Pack 2 or later is\n  required, with the appropriate Elasticsearch Connector version\n  installed.\n\\item\n  If using Elasticsearch 6, 7.0 Fix Pack 3 or later is required, with\n  the appropriate Elasticsearch Connector version installed.\n\\item\n  A\n  \\href{https://help.liferay.com/hc/en-us/articles/360014400932}{Liferay\n  Enterprise Search} (LES) subscription is required for Learning to\n  Rank. Once you have a subscription,\n  \\href{https://customer.liferay.com/downloads}{download the Liferay\n  Enterprise Search Learning to Rank} LPKG file and\n  \\href{/docs/7-2/user/-/knowledge_base/u/installing-apps-manually\\#installing-apps-manually}{install\n  it to your Liferay DXP server.}\n\\item\n  A remote Elasticsearch server, with your data indexed into it.\n\\item\n  The corresponding version of the\n  \\href{https://github.com/o19s/elasticsearch-learning-to-rank}{Elasticsearch\n  Learning to Rank} plugin installed into Elasticsearch.\n\\item\n  A\n  \\href{https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/training-models.html}{trained\n  model} uploaded into the Learning to Rank plugin.\n\\end{itemize}\n\nTo understand more about the compatibility requirements for LES, see its\n\\href{https://help.liferay.com/hc/en-us/articles/360016511651\\#Liferay-Enterprise-Search}{compatibility\nmatrix}.\n\nHow does Learning to Rank work?\n\n\\section{Technical Overview}\\label{technical-overview}\n\nIn a normal search, the User sends a query to the search engine via\nLiferay DXP's\n\\href{/docs/7-2/user/-/knowledge_base/u/searching-for-assets\\#search-bar}{Search\nBar}. The order of returned results is dictated by the search engine's\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index-modules-similarity.html\\#bm25}{relevance\nscoring algorithm}.\n\nHere's where Learning to Rank intervenes and makes that process\ndifferent:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  User enters a query into the search bar.\n\\item\n  Liferay sends the query to Elasticsearch, and retrieves the first 1000\n  results as usual, using the search engine's relevance algorithm.\n\\item\n  The top 1000 results are not returned as search hits, but are used by\n  Elasticsearch for\n  \\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-request-body.html\\#request-body-search-rescore}{re-scoring}\n  via the\n  \\href{https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/searching-with-your-model.html\\#rescore-top-n-with-sltr}{rescore\n  functionality}.\n\\item\n  The results are re-scored by the\n  \\href{https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/searching-with-your-model.html}{SLTR\n  query}, which includes the keywords and the trained model to use for\n  re-scoring.\n\\item\n  Once the trained model re-ranks the results, they're returned in\n  Liferay's\n  \\href{/docs/7-2/user/-/knowledge_base/u/search-results}{Search\n  Results} in their new order.\n\\end{enumerate}\n\nThough it's just a sub-bullet point in the ordered list above, much of\nthe work in this paradigm is in creating and honing the trained model.\nThat's beyond the scope of Liferay's role, but we'll help you get all\nthe pieces in place to orchestrate the magic of machine learning on your\nLiferay queries. Here's a brief overview of what constitutes \\emph{model\ntraining}.\n\n\\section{Model Training}\\label{model-training}\n\nA useful trained model is produced when a good judgment list and a good\nfeature set are fed to a Learning to Rank algorithm (this is the machine\nlearning part of the puzzle). Therefore, it's incumbent on you to\nassemble\n\n\\begin{itemize}\n\\item\n  The Learning to Rank algorithm you wish to use for creating a training\n  model. This demonstration uses\n  \\href{https://sourceforge.net/p/lemur/wiki/RankLib/}{RankLib}.\n\\item\n  A \\emph{judgment list}, containing a graded list of search results.\n  The algorithm is designed to produce a model that honors the ordering\n  of the judgment list.\n\\item\n  A feature set, containing all the \\emph{features} you're handing to\n  the Learning to Rank algorithm, which it uses in conjunction with the\n  judgment list to produce a reliable model. An example feature set for\n  Liferay DXP data is shown in the next article.\n\\end{itemize}\n\n\\href{https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/core-concepts.html\\#judgments-expression-of-the-ideal-ordering}{Judgment\nlists} are lists of graded search results.\n\n\\href{https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/core-concepts.html\\#features-the-raw-material-of-relevance}{Features}\nare the variables that the algorithm uses to create a function that can\nscore results in a smarter way. If you don't give enough, or the\ncorrect, relevant features, your model will not be ``smart'' enough to\nprovide improved results.\n\nIn the next article you'll see the steps required to configure Learning\nto Rank with Liferay DXP.\n\n\\chapter{Configuring Learning to\nRank}\\label{configuring-learning-to-rank}\n\nBefore beginning, you must have a remote\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-elasticsearch-7}{Elasticsearch\n6 or 7} cluster communicating with 7.0. See the\n\\href{https://help.liferay.com/hc/en-us/articles/360016511651\\#Liferay-Enterprise-Search}{compatibility\nmatrix for more information}\n\n\\noindent\\hrulefill\n\n\\textbf{Helpful hint:} Use\n\\href{/docs/7-2/user/-/knowledge_base/u/searching-for-assets\\#search-suggestions}{Suggestions}\nto discover the most common queries (this can be one way to decide which\nqueries to create Learning to Rank models for).\n\n\\noindent\\hrulefill\n\n\\section{Step 1: Install the Learning to Rank Plugin on\nElasticsearch}\\label{step-1-install-the-learning-to-rank-plugin-on-elasticsearch}\n\nSee\n\\href{https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/\\#installing}{the\nElasticsearch Learning to Rank plugin documentation} to learn about\ninstalling the Learning to Rank plugin.\n\nYou'll be running a command like this one, depending on the plugin\nversion you're installing:\n\n\\begin{verbatim}\n./bin/elasticsearch-plugin install http://es-learn-to-rank.labs.o19s.com/ltr-1.1.0-es6.5.4.zip\n\\end{verbatim}\n\nIf using X-Pack security in your Elasticsearch cluster, there\n\\href{https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/x-pack.html}{may\nbe additional steps.}\n\n\\section{Step 2: Training and Uploading a\nModel}\\label{step-2-training-and-uploading-a-model}\n\nDetailed instructions on training models is outside the scope of this\nguide. This requires the intervention of data scientists, who can\nrecommend appropriate tools and models. Use what works for you. In doing\nso, you'll almost certainly be compiling\n\\href{https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/core-concepts.html\\#judgments-expression-of-the-ideal-ordering}{Judgment\nlists} and\n\\href{https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/building-features.html}{feature\nsets} that can be used by the training tool you select to generate a\nmodel that produces good search results. This can be a long journey, but\nonce you get to the end of it, you'll want to upload the model to the\nLearning to Rank plugin.\n\n\\section{Upload the Model to the Learning to Rank\nPlugin}\\label{upload-the-model-to-the-learning-to-rank-plugin}\n\nYou'll upload the model using a \\texttt{POST} request, but first you\nneed to make sure you have a \\texttt{\\_ltr} index and a feature set\nuploaded to the Learning to Rank plugin. Use Kibana (or even better, the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-liferay-enterprise-search-monitoring}{Monitoring\nwidget}), to make these tasks easier.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If you don't already have an \\texttt{\\_ltr} index, create one:\n\n\\begin{verbatim}\nPUT _ltr\n\\end{verbatim}\n\\item\n  Add a feature set to the \\texttt{\\_ltr} index. In this example the set\n  is called \\texttt{liferay}:\n\n\\begin{verbatim}\nPOST _ltr/_featureset/liferay\n{\n  \"featureset\": {\n    \"name\": \"liferay\",\n    \"features\": [\n      {\n        \"name\": \"title\",\n        \"params\": [\n          \"keywords\"\n        ],\n        \"template\": {\n          \"match\": {\n            \"title_en_US\": \"{{keywords}}\"\n          }\n        }\n      },\n      {\n        \"name\": \"content\",\n        \"params\": [\n          \"keywords\"\n        ],\n        \"template\": {\n          \"match\": {\n            \"content_en_US\": \"{{keywords}}\"\n          }\n        }\n      },\n      {\n        \"name\": \"asset tags\",\n        \"params\": [\n          \"keywords\"\n        ],\n        \"template\": {\n          \"match\": {\n            \"assetTagNames\": \"{{keywords}}\"\n          }\n        }\n      }\n    ]\n  }\n}\n\\end{verbatim}\n\n  Take note of the syntax used here, since it's required.\n\\item\n  Add the trained model to the feature set:\n\n\\begin{verbatim}\nPOST _ltr/_featureset/liferay/_createmodel\n{\n  \"model\": {\n    \"name\": \"linearregression\",\n    \"model\": {\n      \"type\": \"model/ranklib\",\n      \"definition\": \"\"\"\n# Linear Regression\n# Lambda = 1.0E-10\n0:-0.717621803830712 1:-0.717621803830712 2:-2.235841905601106 3:19.546816765721594\n\"\"\"\n    }\n  }\n}\n\\end{verbatim}\n\\end{enumerate}\n\nThis is a very high level set of instructions, because there's not much\nto do in Liferay DXP. To learn in more detail about what's required, see\nthe\n\\href{https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/index.html}{Learning\nto Rank plugin's documentation}.\n\nKeep reworking those judgment lists!\n\n\\section{Step 3: Enable Learning to\nRank}\\label{step-3-enable-learning-to-rank}\n\nEnable Learning to Rank from Control Panel → Configuration → System\nSettings → Search → Learning to Rank. There's a simple on/off\nconfiguration and a text field where you must enter the name of the\ntrained model to apply to search queries.\n\nThe model in the previous step was named \\texttt{linearregression}, so\nthat's what you'd enter.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/search-learning-to-rank.png}}\n\\caption{Enable Learning to Rank in Liferay DXP from the System Settings\nentry.}\n\\end{figure}\n\nThat's all the configuration required to get the Elasticsearch Learning\nto Rank plugin ingesting a trained model, a feature set, and search\nqueries from Liferay DXP.\n\n\\chapter{Installing Solr}\\label{installing-solr}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nSolr is a popular enterprise search platform built on Apache Lucene.\nIt's reliable, scalable, and fault tolerant. Read more about it\n\\href{http://lucene.apache.org/solr/}{here}.\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector}{Elasticsearch}\nis the default search engine that ships with Liferay DXP, and some\nLiferay Search features are only available on Elasticsearch. It's valid,\nhowever, to use Solr instead. In particular, if you've already been\nusing Solr with a previous version of Liferay DXP, or your platform (for\nexample, your OS or JVM)\n\\href{https://www.elastic.co/support/matrix}{isn't supported by\nElasticsearch}, you might choose to use Solr to search and index your\nLiferay DXP data.\n\nThere are circumstances that force you to use Elasticsearch instead of\nSolr. Read\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-a-search-engine\\#choosing-a-search-engine}{here}\nfor more information.\n\nLiferay DXP 7.2, Fix Pack 1, supports Solr 7.5.x through the Liferay\nConnector to Solr 7 application, version 2.0.0.\n\nLiferay DXP 7.2, Service Pack 1/Fix Pack 2 and later, supports Solr\n7.5.x through the Liferay Connector to Solr 7 application, version\n2.0.1.\n\nLiferay Portal CE 7.2, GA2 and later (not available at time of writing),\nsupport Solr 7.5.x through the Liferay CE Connector to Solr 7\napplication.\n\n\\noindent\\hrulefill\n\n\\textbf{Upgrading to Service Pack 1 or Fix Pack 2 (or later) requires\ninstallation of a new Solr connector:} If you were running version 2.0.0\nof the Liferay Connector to Solr 7 application, and you want to install\nService Pack 1/Fix Pack 1 (or later), you must install version 2.0.1 of\nthe Liferay Connector to Solr 7 application.\n\n\\noindent\\hrulefill\n\n\\section{Blacklisting Elasticsearch-Only\nFeatures}\\label{blacklisting-elasticsearch-only-features}\n\nBefore installing Solr, you must\n\\href{/docs/7-2/user/-/knowledge_base/u/blacklisting-osgi-bundles-and-components}{blacklist}\ncertain DXP\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-a-search-engine\\#choosing-a-search-engine}{features\nthat only work with Elasticsearch}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a configuration file named\n\n\\begin{verbatim}\ncom.liferay.portal.bundle.blacklist.internal.BundleBlacklistConfiguration.config\n\\end{verbatim}\n\\item\n  Give it these contents:\n\n\\begin{verbatim}\nblacklistBundleSymbolicNames=[\"com.liferay.portal.search.tuning.web.api\",\"com.liferay.portal.search.tuning.web\",\"com.liferay.portal.search.tuning.synonyms.web\",\"com.liferay.portal.search.tuning.rankings.web\"]\n\\end{verbatim}\n\\item\n  Place the file in \\texttt{Liferay\\ Home/osgi/configs}.\n\\end{enumerate}\n\nIt is required during the Solr installation process to also\n\\href{https://portal.liferay.dev/docs/7-2/deploy/-/knowledge_base/d/installing-solr-basic-installation\\#stopping-the-elasticsearch-connector}{stop\nthe Elasticsearch Connectors} that ship with Liferay DXP. If you're\nready to blacklist those bundles now, use these contents in the\nblacklist configuration file:\n\n\\begin{verbatim}\n    blacklistBundleSymbolicNames=[\"com.liferay.portal.search.tuning.web.api\",\"com.liferay.portal.search.tuning.web\",\"com.liferay.portal.search.tuning.synonyms.web\",\"com.liferay.portal.search.tuning.rankings.web\",\"com.liferay.portal.search.elasticsearch6.spi\",\"com.liferay.portal.search.elasticsearch6.api\",\"com.liferay.portal.search.elasticsearch6.impl\",\"Liferay Enterprise Search Monitoring \",\"Liferay Enterprise Search Security \"]\n\\end{verbatim}\n\nThe Liferay Enterprise Search bundles must be excluded if you don't have\na LES subscription.\n\n\\chapter{Installing Solr: Basic\nInstallation}\\label{installing-solr-basic-installation}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThere are two ways to install the Liferay Connector to Solr 7:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Navigate to \\href{https://web.liferay.com/marketplace/}{Liferay\n  Marketplace} and download the app that corresponds to your portal.\n\\end{enumerate}\n\nOnce the app LPKG is downloaded, copy it to\n\\texttt{Liferay\\_Home/osgi/marketplace}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\tightlist\n\\item\n  In your running portal instance, navigate to \\emph{Control Panel} →\n  \\emph{Apps} → \\emph{Store}. Sign in using your credentials, search for\n  Solr Search Engine, and purchase (it's free) the Liferay Connector to\n  Solr 7 entry.\n\\end{enumerate}\n\nAs you proceed, remember these terms:\n\n\\emph{Solr Home}: The center of the Solr system (pun intended). This\ndirectory is \\texttt{solr-{[}version{]}/server/solr}.\n\n\\emph{Liferay Home}: The root folder of your Liferay DXP installation.\nIt contains the \\texttt{osgi}, \\texttt{deploy}, \\texttt{data}, and\n\\texttt{license} folders, among others.\n\nThere are two installation steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Installing and configuring Solr 7.\n\\item\n  Installing and configuring the Solr 7 connector for Liferay DXP.\n\\end{enumerate}\n\nBefore configuring Liferay DXP for Solr, install and set up Solr.\n\n\\section{Installing and Configuring Solr\n7}\\label{installing-and-configuring-solr-7}\n\nTo install and properly configure Solr for Liferay DXP:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Download\n  \\href{http://archive.apache.org/dist/lucene/solr/7.5.0/}{Solr} and\n  unzip it.\n\\item\n  Navigate to \\texttt{solr-{[}version{]}/server/solr}. This is Solr\n  Home.\n\\item\n  Create a new folder called \\texttt{liferay}.\n\\item\n  In the \\texttt{liferay} folder, create two new folders: \\texttt{conf}\n  and \\texttt{data}.\n\\item\n  Copy the contents of \\texttt{Solr\\_Home/configsets/\\_default/conf} to\n  \\texttt{Solr\\_Home/liferay/conf}.\n\\item\n  Open the Liferay Connector to Solr 7's LPKG file with an archive\n  manager.\n\n  Open the \\texttt{com.liferay.portal.search.solr7.impl.jar} file, and\n  extract\n\n\\begin{verbatim}\nMETA-INF/resources/solrconfig.xml\n\\end{verbatim}\n\n  and\n\n\\begin{verbatim}\nMETA-INF/resources/schema.xml\n\\end{verbatim}\n\n  to\n\n\\begin{verbatim}\nSolr_Home/liferay/conf\n\\end{verbatim}\n\n  This replaces the current \\texttt{solrconfig.xml} and\n  \\texttt{schema.xml} files with ones that tell Solr how to index data\n  coming from Liferay DXP.\n\\item\n  Create a \\texttt{core.properties} file in \\texttt{Solr\\_Home/liferay}\n  and add this configuration:\n\n\\begin{verbatim}\nconfig=solrconfig.xml\ndataDir=data\nname=liferay\nschema=schema.xml\n\\end{verbatim}\n\\item\n  Checkpoint: your \\texttt{Solr\\_Home/liferay} folder now has this\n  structure:\n\n\\begin{verbatim}\nliferay\n├── conf\n│   ├── lang\n│   │   ├── contractions_ca.txt\n│   │   ├── ....txt\n│   ├── managed-schema\n│   ├── params.json\n│   ├── protwords.txt\n│   ├── schema.xml\n│   ├── solrconfig.xml\n│   ├── stopwords.txt\n│   └── synonyms.txt\n├── core.properties\n└── data\n\\end{verbatim}\n\\item\n  Start the Solr server by entering\n\n\\begin{verbatim}\n./bin/solr start -f\n\\end{verbatim}\n\n  from the top-level folder of your Solr installation\n  (\\texttt{solr-{[}version{]}}).\n\\item\n  The Solr server listens on port \\texttt{8983} by default. Navigate to\n  \\texttt{http://localhost:8983/solr/\\#/\\textasciitilde{}cores}\n  (assuming you're testing locally with \\texttt{localhost} as your\n  host), and confirm that the \\texttt{liferay} core is available.\n\\end{enumerate}\n\nSolr is now installed. Next install and configure the Solr connector.\n\n\\section{Installing and Configuring the Liferay Solr\nAdapter}\\label{installing-and-configuring-the-liferay-solr-adapter}\n\nSince Elasticsearch is the default search engine, the Elasticsearch\nconnector is already installed and running. You must stop it before\nconfiguring the Solr connector.\n\n\\section{Stopping the Elasticsearch\nConnector}\\label{stopping-the-elasticsearch-connector}\n\nStop the Elasticsearch connector bundle using the App Manager, the Felix\nGogo shell, or the bundle blacklist. If you're a Liferay DXP customer,\nuse the blacklist feature as described below. The App Manager and Gogo\nshell rely on the \\texttt{osgi/state} folder to ``remember'' the state\nof the bundle. If you delete this folder (recommended during patching)\nthe Elasticsearch connector is reinstalled and started automatically.\n\nNavigate to Control Panel → Apps → App Manager.\n\nOnce in the App Manager, search for \\emph{elasticsearch}. Find the\nLiferay Connector to Elasticsearch 6 module and click the Actions\n((\\pandocbounded{\\includegraphics[keepaspectratio]{./images/icon-actions.png}}))\nbutton. Choose the Deactivate option. This leaves the bundle installed,\nbut stops it in the OSGi runtime.\n\nAlternatively, use the\n\\href{/developer/tutorials/-/knowledge_base/7-2/using-the-felix-gogo-shell}{Felix\nGogo shell} to stop the Elasticsearch connector. Enter\n\n\\begin{verbatim}\nlb elasticsearch\n\\end{verbatim}\n\nYou'll see two active bundles for the Liferay Connector to Elasticsearch\n6: an API and an IMPL bundle.\n\n\\begin{verbatim}\nID|State      |Level|Name\n476|Active     |   10|Liferay (CE) Connector to Elasticsearch 6 - API (3.0.0)\n478|Active     |   10|Liferay Portal Search Elasticsearch 6 API (3.0.4)\n480|Active     |   10|Liferay Portal Search Elasticsearch 6 SPI (3.2.1)\n706|Active     |   10|Liferay (CE) Connector to Elasticsearch 6 - Impl (3.0.0)\n707|Active     |   10|Liferay Portal Search Elasticsearch 6 Implementation (3.0.15)\n\\end{verbatim}\n\nStop the API bundle by entering\n\n\\begin{verbatim}\nstop [bundle ID]\n\\end{verbatim}\n\nIn the example above, the \\texttt{{[}bundle\\ ID{]}} is \\texttt{476}.\n\n\\textbf{Liferay DXP:} DXP customers should\n\\href{/docs/7-2/user/-/knowledge_base/u/blacklisting-osgi-bundles-and-components}{blacklist}\nthe Elasticsearch, Shield, and Marvel plugins.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a\n\n\\begin{verbatim}\ncom.liferay.portal.bundle.blacklist.internal.BundleBlacklistConfiguration.config\n\\end{verbatim}\n\n  file with these contents:\n\n\\begin{verbatim}\nblacklistBundleSymbolicNames=[\"com.liferay.portal.search.elasticsearch6.spi\",\"com.liferay.portal.search.elasticsearch6.api\",\"com.liferay.portal.search.elasticsearch6.impl\",\"Liferay Enterprise Search Monitoring \",\"Liferay Enterprise Search Security \"]\n\\end{verbatim}\n\n  If the LES Security and Monitoring LPKG files are installed, you must\n  blacklist these too.\n\\item\n  Place the file in \\texttt{Liferay\\ Home/osgi/configs}.\n\\end{enumerate}\n\n\\section{Install and Configure the Solr\nConnector}\\label{install-and-configure-the-solr-connector}\n\nNow you're ready to install the connector:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Start Liferay DXP, then deploy the Solr connector by copying the LPKG\n  you downloaded to \\texttt{Liferay\\_Home/deploy}.\n\n  You'll see a \\texttt{STARTED} message in your Liferay DXP log once the\n  Solr connector is installed. Here's what the log message looks like:\n\n\\begin{verbatim}\n2018-11-06 19:59:49.396 INFO  [pipe-start 943 944][BundleStartStopLogger:39] STARTED com.liferay.portal.search.solr7.api_2.0.5 [943]\n2018-11-06 19:59:49.490 INFO  [pipe-start 943 944][BundleStartStopLogger:39] STARTED com.liferay.portal.search.solr7.impl_2.0.11 [944]\n\\end{verbatim}\n\\item\n  To re-index against Solr, navigate to \\emph{Control Panel} →\n  \\emph{Configuration} → \\emph{Search}, and click \\emph{Execute} next to\n  the \\emph{Reindex all search indexes} option.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/solr-reindex.png}}\n\\caption{Once the Solr connector is installed, you can re-index your\nLiferay DXP data against your Solr server.}\n\\end{figure}\n\nIn production deployments, specify your edits to the Solr connector's\ndefault configurations using a configuration file deployed to the\n\\texttt{Liferay\\_Home/osgi/configs} folder. Name the file\n\n\\begin{verbatim}\ncom.liferay.portal.search.solr7.configuration.SolrConfiguration.config\n\\end{verbatim}\n\nDuring testing and development, use the Solr 7 System Settings entry\nControl Panel → Configuration → System Settings for editing the default\nconfigurations.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/solr-system-settings.png}}\n\\caption{You can configure Solr from Liferay DXP's System Settings\napplication. This is most useful during development and testing.}\n\\end{figure}\n\nThe next article covers clustering Solr with SolrCloud.\n\n\\chapter{Installing Solr: High Availability with\nSolrCloud}\\label{installing-solr-high-availability-with-solrcloud}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nUse SolrCloud if you need a cluster of Solr servers. Note that to use\nSolrCloud in production, you should set up an\n\\href{https://cwiki.apache.org/confluence/display/solr/Setting+Up+an+External+ZooKeeper+Ensemble}{external\nZooKeeper ensemble}. \\href{http://zookeeper.apache.org/}{ZooKeeper} is a\ncentralized coordination service for managing distributed systems like\nyour SolrCloud cluster.\n\nThe steps included here should be considered the bare minimum of what\nmust be done to configure SolrCloud with Liferay DXP. For example, these\ninstructions cover configuring SolrCloud on a single machine, whereas a\nproduction environment would feature multiple physical or virtual\nmachines. These instructions also assume you've followed the earlier\nsection on \\emph{Installing and Configuring Solr 7}. Refer to the\n\\href{https://cwiki.apache.org/confluence/display/solr/SolrCloud}{SolrCloud\nguide for more information}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Stop the Solr server if it's running.\n\\item\n  Navigate to the \\texttt{Solr\\_Home/configsets} folder and create a\n  folder called\n\n\\begin{verbatim}\n liferay_configs\n\\end{verbatim}\n\\item\n  Copy the \\texttt{conf} folder from \\texttt{Solr\\_Home/liferay} to the\n  \\texttt{liferay\\_configs} folder you just created.\n\n  The \\texttt{configset/liferay\\_configs} folder contains the SolrCloud\n  Liferay DXP collection configuration and is uploaded to ZooKeeper. By\n  copying the \\texttt{conf} folder from the \\texttt{liferay} server\n  configured earlier, you're using the \\texttt{schema.xml} and\n  \\texttt{solrconfig.xml} files provided with the Liferay Solr Adapter.\n\\item\n  Next launch an interactive SolrCloud session to configure your\n  SolrCloud cluster. Use this command:\n\n\\begin{verbatim}\n ./bin/solr -e cloud\n\\end{verbatim}\n\\item\n  Complete the setup wizard. These steps demonstrate creating a two-node\n  cluster:\n\n  \\begin{itemize}\n  \\item\n    Enter \\texttt{2} for the number of nodes.\n  \\item\n    Specify ports \\texttt{8983} and \\texttt{7574} (the defaults). Both\n    nodes are started with the start commands printed in the log:\n\n\\begin{verbatim}\n Starting up Solr on port 8983 using command:\n \"bin/solr\" start -cloud -p 8983 -s \"example/cloud/node1/solr\"\n\n Waiting up to 180 seconds to see Solr running on port 8983 [|]  [-]  \n Started Solr server on port 8983 (pid=8846). Happy searching!\n\n\n Starting up Solr on port 7574 using command:\n \"bin/solr\" start -cloud -p 7574 -s \"example/cloud/node2/solr\" -z localhost:9983\n\n Waiting up to 180 seconds to see Solr running on port 7574 [|]  [/]  \n Started Solr server on port 7574 (pid=9026). Happy searching!\n\\end{verbatim}\n  \\item\n    Name the collection \\emph{liferay}.\n  \\item\n    Split the collection into two shards.\n  \\item\n    Specify two replicas per shard.\n  \\item\n    When prompted to choose a configuration, enter\n    \\emph{liferay\\_configs}. You should see a log message that concludes\n    like this when the cluster has been started:\n  \\end{itemize}\n\n\\begin{verbatim}\n SolrCloud example running, please visit http://localhost:8983/solr\n\\end{verbatim}\n\\end{enumerate}\n\nNow you have a new collection called \\emph{liferay} in your local\nSolrCloud cluster. Verify its status by running the \\emph{status}\ncommand:\n\n\\begin{verbatim}\n./bin/solr status\n\\end{verbatim}\n\nYou'll see log output like this:\n\n\\begin{verbatim}\nFound 2 Solr nodes: \n\nSolr process 12828 running on port 8983\nINFO  - 2019-07-18 16:46:35.137; org.apache.solr.util.configuration.SSLCredentialProviderFactory; Processing SSL Credential Provider chain: env;sysprop\n{\n  \"solr_home\":\"/home/russell/liferay-bundles/liferay-portal-7.2.10-ga1/solr-7.5.0/example/cloud/node1/solr\",\n  \"version\":\"7.5.0 b5bf70b7e32d7ddd9742cc821d471c5fabd4e3df - jimczi - 2018-09-18 13:07:55\",\n  \"startTime\":\"2019-07-18T20:44:13.138Z\",\n  \"uptime\":\"0 days, 0 hours, 2 minutes, 22 seconds\",\n  \"memory\":\"56.4 MB (%11.5) of 490.7 MB\",\n  \"cloud\":{\n    \"ZooKeeper\":\"localhost:9983\",\n    \"liveNodes\":\"2\",\n    \"collections\":\"1\"}}\n\n\nSolr process 12995 running on port 7574\nINFO  - 2019-07-18 16:46:35.848; org.apache.solr.util.configuration.SSLCredentialProviderFactory; Processing SSL Credential Provider chain: env;sysprop\n{\n  \"solr_home\":\"/home/russell/liferay-bundles/liferay-portal-7.2.10-ga1/solr-7.5.0/example/cloud/node2/solr\",\n  \"version\":\"7.5.0 b5bf70b7e32d7ddd9742cc821d471c5fabd4e3df - jimczi - 2018-09-18 13:07:55\",\n  \"startTime\":\"2019-07-18T20:44:16.847Z\",\n  \"uptime\":\"0 days, 0 hours, 2 minutes, 19 seconds\",\n  \"memory\":\"108.2 MB (%22.1) of 490.7 MB\",\n  \"cloud\":{\n    \"ZooKeeper\":\"localhost:9983\",\n    \"liveNodes\":\"2\",\n    \"collections\":\"1\"}}\n\\end{verbatim}\n\nTo stop Solr while running in SolrCloud mode, use the \\emph{stop}\ncommand, like this:\n\n\\begin{verbatim}\nbin/solr stop -all\n\\end{verbatim}\n\n\\section{Configure the Solr Adapter for\nSolrCloud}\\label{configure-the-solr-adapter-for-solrcloud}\n\nThere's only one thing left to do: specify the client type as\n\\emph{CLOUD} in Liferay's Solr connector.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From System Settings or your OSGi configuration file, set the\n  \\emph{Client Type} to \\emph{CLOUD}.\n\n\\begin{verbatim}\nclientType=\"CLOUD\"\n\\end{verbatim}\n\\item\n  Start Liferay DXP if it's not running already.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/solr-client-type.png}}\n\\caption{From the Solr 7 System Settings entry, set the \\emph{Client\nType} to \\emph{Cloud}.}\n\\end{figure}\n\nNow you can configure Liferay DXP for Solr and Solr for @product@.\nRemember that Elasticsearch is the default search engine, so if you're\nnot constrained to use Solr or already a Solr expert, consider\nElasticsearch for your search engine requirements. If you do use Solr,\ntell all your colleagues that your Liferay DXP installation's search\ncapability is Solr powered (pun intended).\n\n\\chapter{Solr Connector Settings}\\label{solr-connector-settings}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nSolr can be configured for use with 7.0. Liferay Marketplace includes a\nSolr connector app called the Liferay Connector to Solr 7. The connector\nis configurable through System Settings or an OSGi configuration file\nnamed\n\\texttt{com.liferay.portal.search.solr7.configuration.SolrConfiguration.config}\nand deployed to \\texttt{{[}Liferay\\_Home{]}/osgi/configs}.\n\nThe list below is all the configuration settings for Liferay's Solr\nconnector, in the order they appear in the System Settings application:\n\n\\section{Solr 7}\\label{solr-7}\n\n\\begin{description}\n\\tightlist\n\\item[\\texttt{authenticationMode=BASIC}]\nA String with the value of \\emph{BASIC} or \\emph{CERT}. Use BASIC when\nconnecting using the\n\\href{https://cwiki.apache.org/confluence/display/solr/Basic+Authentication+Plugin}{Basic\nAuthentication plugin}, otherwise select CERT to connect using\n\\href{https://cwiki.apache.org/confluence/display/solr/Enabling+SSL}{2-way\nSSL authentication}.\n\\item[\\texttt{clientType=REPLICATED}]\nA String with the value of \\emph{REPLICATED} or \\emph{CLOUD}. Use the\ndefault (REPLICATED) when connecting to a single-node Solr server.\nSpecify CLOUD to connect to SolrCloud (see the next section, titled\n\\emph{High Availability with SolrCloud} for more information).\n\\item[\\texttt{logExceptionsOnly=true}]\nA boolean value that, when set to true, only logs exceptions from Solr,\nwithout rethrowing them.\n\\item[\\texttt{readURL=http://localhost:8983/solr/liferay}]\nA String array with the URLs to which Liferay will send search requests.\nThis will be different from the \\texttt{writeURL} if you use separate\nservers for indexing (write) and searching (read).\n\\item[\\texttt{writeURL=http://localhost:8983/solr/liferay}]\nA String array with the URLs to which Liferay will send indexing\nrequests. This is different from the \\texttt{readURL} if you use\nseparate servers for indexing (write) and searching (read).\n\\item[\\texttt{zkHost=localhost:9983}]\nA String with the ZooKeeper host and port. This is required when using\nthe adapter in CLOUD mode.\n\\end{description}\n\n\\chapter{Securing Liferay DXP}\\label{securing-liferay-dxp}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay follows the OWASP Top 10 (2013) and CWE/SANS Top 25 lists to\nensure Liferay DXP is as secure as possible. Following these\nrecommendations protects the product against known kinds of attacks and\nsecurity vulnerabilities. For example, Liferay DXP's persistence layer\nis generated and maintained by the Service Builder framework which\nprevents SQL Injection using Hibernate and parameter based queries.\n\nTo prevent Cross Site Scripting (XSS), user-submitted values are escaped\non output. To support integration features, Liferay DXP doesn't encode\ninput. Data is stored in the original form as submitted by the user.\nLiferay DXP includes built-in protection against CSRF attacks, Local\nFile Inclusion, Open Redirects, Uploading and serving files of dangerous\ntypes, Content Sniffing, Clickjacking, Path Traversal, and many other\ncommon attacks.\n\nTo stay on top, Liferay DXP also contains fixes for state-of-the-art\nattacks and techniques to improve product security. For example, Liferay\nDXP uses PBKDF2 to store passwords. Liferay DXP also contains mitigation\nfor Quadratic Blowup XXE attack, Rosetta Flash vulnerability, Reflected\nFile Download, and other kinds of attacks.\n\nThis section of tutorials shows you how to configure various security\nand login features, such as LDAP, single sign-on, Service Access\nPolicies, and more. What follows is an overview of what's available.\n\n\\section{Authentication Overview}\\label{authentication-overview}\n\nLiferay DXP user authentication can take place using any of a variety of\nprepared solutions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Form authentication using the Sign In Portlet with extensible adapters\n  for checking and storing credentials (portal database, LDAP)\n\\item\n  Single-Sign-On (SSO) solutions - NTLM, CAS, SiteMinder, OpenSSO,\n  OpenID, Facebook\n\\item\n  \\href{https://www.liferay.com/marketplace/-/mp/application/15188711}{SAML\n  plugin}\n\\item\n  JAAS integration with application server\n\\end{itemize}\n\nNote: Although Liferay's SSO solutions are incompatible with WebDAV,\nthey can be used with Liferay Sync. See the\n\\href{/docs/7-1/user/-/knowledge_base/u/publishing-files}{Publishing\nFiles} article for more information on WebDAV and Liferay Sync.\n\nYou can authenticate and authorize apps remotely using the\n\\texttt{AuthVerifier} layer:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Password based HTTP Basic + Digest authentication\n\\item\n  Token based OAuth plugin\n\\item\n  Portal session based solution for JavaScript applications\n\\end{itemize}\n\nBoth user authentication and remote application authentication are\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/authentication-pipelines}{extensible}.\nDevelopers can create custom Login portlets and plugins, extend the\ndefault Login portlet \\texttt{auth.pipeline}, create \\texttt{AutoLogin}\nextensions for SSO, or create custom \\texttt{AuthVerifier}\nimplementations.\n\n\\section{Authorization and Permission\nChecking}\\label{authorization-and-permission-checking}\n\nThere are several adjustable authorization layers in place to prevent\nunauthorized or unsecured access to data:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Remote IP and HTTPS transport check to limit access to Liferay DXP's\n  Java servlets\n\\item\n  Extensible Access Control Policies layer to perform any portal service\n  related authorization check\n\\item\n  Extensible role-based permission framework for almost any portal\n  entity or data (stored in the portal database or elsewhere)\n\\item\n  Portlet Container security checks to control portlet access\n\\item\n  Remote IP check for portal remote API authentication methods\n\\item\n  Service Access Policies to control access to portal remote API\n\\end{itemize}\n\n\\section{Additional Security\nFeatures}\\label{additional-security-features}\n\nUsers can be assigned to sites, teams, user groups, or organizations.\nCustom roles can be created, permissions can be assigned to those roles,\nand those roles can be applied to users. Roles are scoped to apply only\nwith a specific context like a site, an organization, or globally. See\nthe \\href{/docs/7-1/user/-/knowledge_base/u/roles-and-permissions}{Roles\nand Permissions} documentation for more information.\n\n\\noindent\\hrulefill\n\nNote: Liferay DXP relies on the application server for sanitizing CRLF\nin HTTP headers. You should, therefore, make sure this is configured\nproperly in your application server, or you may experience false\npositives in reports from automatic security verification software such\nas Veracode. There is one exception to this for Resin, which does not\nhave support for this feature. In this case, Liferay DXP sanitizes HTTP\nheaders.\n\n\\noindent\\hrulefill\n\nThere are additional security plugins available from\n\\href{https://www.liferay.com/marketplace}{Liferay Marketplace}. For\nexample, you can find an Audit plugin for tracking user actions or an\nAntiSamy plugin for clearing HTML from XSS vectors.\n\nThere are many ways to fine-tune or disable various security features.\nHere are a few examples of these kinds of configuration actions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Disable the Sign In portlet's \\emph{Create Account} link\n\\item\n  Configure Liferay DXP's HTTPS web server address\n\\item\n  Configure the list of allowed servers to which users can be redirected\n\\item\n  Configure the list of portlets that can be accessed from any page\n\\item\n  Configure the file types allowed to be uploaded and downloaded\n\\end{itemize}\n\n\\section{Secure Configuration and Run\nRecommendations}\\label{secure-configuration-and-run-recommendations}\n\nLiferay DXP is built using the ``secure by default'' concept in mind.\nIt's not recommended to disable built-in protections or to allow all\nvalues in security white-lists. Such acts may lead to security\nmisconfiguration and an insecure deployment.\n\nAlso, customers are advised to deploy security patches as described on\nthe\n\\href{https://www.liferay.com/group/customer/products/portal/security-vulnerability}{customer\nportal}.\n\nFor community and CE deployments, the stay secure by always using the\nlatest community version, which contains all previous security patches.\n\n\\chapter{Logging into Liferay DXP}\\label{logging-into-liferay-dxp}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nOne of the primary functions of a security system is to make pages,\ncontent, and web applications accessible only to the appropriate users.\nA student logging into a university portal should not be able to access\nthe same resources a professor can. A patient logging into a health care\nportal should not be able to access a doctor's resources. Some content\n(at least a login page) should be available to everybody, including\nunauthenticated users (called \\emph{guest} users). To learn more about\nhow Liferay DXP restricts access to portal resources to different users,\nplease see the\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{Roles and\nPermissions} documentation.\n\n\\section{Authentication Types}\\label{authentication-types}\n\nThere are three authentication types: by email address, screen name, or\nuser ID. To choose an authentication type, navigate to the Control\nPanel, click on \\emph{Configuration} → \\emph{Instance Settings} →\n\\emph{Platform} → \\emph{User Authentication} and use the \\emph{How do\nusers authenticate?} selector to make a selection. Alternatively, add\nthe following lines to your \\texttt{portal-ext.properties} file,\nuncomment the appropriate line, comment out the others, and restart your\nserver.\n\n\\begin{verbatim}\ncompany.security.auth.type=emailAddress\n#company.security.auth.type=screenName\n#company.security.auth.type=userId\n\\end{verbatim}\n\nThe default authentication type is by email address, but you can choose\nscreen names or user IDs instead. Users choose screen names when they\ncreate their accounts or administrators can choose them. User IDs are\nauto-generated when the account is created. Regardless of which\nauthentication type is configured, users must always enter a password.\nFor information on adding restrictions on the kinds of passwords that\nare allowed or required (e.g., to require a minimum password length or\nrequire special characters), please see the\n\\href{/docs/7-2/user/-/knowledge_base/u/password-policies}{Password\nPolicies} documentation.\n\n\\section{The Sign In Portlet}\\label{the-sign-in-portlet}\n\nSign In portlet is how users log in. By default, the Sign In portlet can\ncreate new accounts or request a password reset. The default home page\ncontains a Sign In portlet. You can access this page at\n\\url{http://localhost:8080/web/guest/home}.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/sign-in-portlet.png}}\n\\caption{By default, the Sign In portlet allows users to log in, create\na new account, or request a password reset.}\n\\end{figure}\n\nIf the Sign In portlet doesn't appear on any page, you can still access\nit here:\n\n\\url{localhost:8080/c/portal/login}\n\nBy default, guest users can create accounts by clicking on the\n\\emph{Create Account} link in the Sign In portlet, completing the form,\nand submitting it. If a user has an account but has forgotten its\npassword, the user can click the \\emph{Forgot Password} link to request\na password reset.\n\nBoth the \\emph{Create Account} form and the \\emph{Forgot Password} form\ninclude a CAPTCHA-based text verification field. Using\n\\href{http://www.captcha.net}{CAPTCHA} prevents bots from submitting\nthese forms.\n\nYou can use\n\\href{https://www.google.com/recaptcha/intro/index.html}{reCAPTCHA}\ninstead of CAPTCHA. One advantage of reCAPTCHA is that it can allow\nvisually impaired users to pass the test. To use reCAPTCHA, navigate to\nthe Control Panel, then click on \\emph{Configuration} → \\emph{System\nSettings} → \\emph{CAPTCHA}.\n\nYou can prevent guest users from creating new user accounts, if your\nsite requires users be registered by administrators. Navigate to the\nControl Panel → \\emph{Configuration} → \\emph{Instance Settings} →\n\\emph{Platform} → \\emph{User Authentication} and uncheck the \\emph{Allow\nstrangers to create accounts?} box. You can also prevent users from\nrequesting forgotten passwords or from requesting password reset links\nby unchecking the appropriate boxes. With these options, the Create\nAccount and Forgot Password links no longer appear in the Sign-In\nportlet.\n\nRemember that the Sign In portlet is the default way for users to log\nin, but it's not the only way. User accounts can be imported from and\nexported to LDAP directories. You can use single-sign-on (SSO) solutions\nor token-based authentication, which allows remote web applications to\nauthenticate. Please refer to the other articles in this section for\nmore information. Finally, remember that user authentication and remote\napplication authentication mechanisms are\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/authentication-pipelines}{extensible}.\n\n\\chapter{Service Access Policies}\\label{service-access-policies}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\n\\emph{Service access policies} comprise a layer of web service security\nthat defines services or service methods that can be invoked remotely.\nYou can apply many of them at once to produce a combined effect. To help\nyou understand how service access policies fit into the big picture,\nhere's a summary of Liferay DXP's web service security layers:\n\n\\textbf{IP permission layer:} The IP address from which a web service\ninvocation request originates must be white-listed in the portal\nproperties file. A web service invocation coming from a non-whitelisted\nIP address automatically fails.\n\n\\textbf{Service access policy layer:} Methods corresponding to a web\nservice invocation request must be whitelisted by each service access\npolicy that's in effect. You can use wildcards to reduce the number of\nservice classes and methods that must be explicitly whitelisted.\n\n\\textbf{Authentication/verification layer (browser-only):} If a web\nservice invocation request comes from a browser, the request must\ninclude an authentication token. This authentication token is the value\nof the \\texttt{p\\_auth} URL parameter. The token is generated by Liferay\nDXP and associated with your browser session. The \\texttt{p\\_auth}\nparameter is automatically supplied when you invoke a Liferay DXP web\nservice via the JSON web services API page or via JavaScript using\n\\texttt{Liferay.Service(...)}. If Liferay DXP cannot associate the\ncaller's authentication token with a User, the web service invocation\nrequest fails.\n\n\\textbf{User permission layer:} Properly implemented web services have\npermission checks. The user invoking a web service must have permission\nto invoke the service.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/service-access-policies-security-layers.png}}\n\\caption{To get to a service, a request must pass through the door lock\nof user permissions, the padlock of the verification layer, the brick\nwall of service access policies, and finally the safe of predefined IP\npermissions.}\n\\end{figure}\n\nNote that service access policies respect the permissions system. If a\nservice access policy grants a user access to a remote service, the user\nmust still have the appropriate permissions to invoke that service.\n\nService access policies are especially useful when remote applications\nsuch as mobile devices or Liferay Sync instances must access web\nservices. Administrators can use service access policies to ensure that\nthese devices can only invoke remote services from approved lists that\ncan be modified at runtime.\n\n\\section{Managing Service Access\nPolicies}\\label{managing-service-access-policies}\n\nNavigate to the Control Panel and click on \\emph{Service Access Policy}\nunder the Configuration heading. Here, you can see the default service\naccess policies and add new ones. When creating or editing service\naccess policies, keep these points in mind:\n\n\\begin{itemize}\n\\item\n  Service access policy names must be unique per portal instance.\n\\item\n  Service access policy names can include only these allowed characters:\n\n\\begin{verbatim}\n  0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#:@-./_\n\\end{verbatim}\n\\item\n  Service access policy titles can be localized; service access policy\n  names cannot be localized.\n\\item\n  Allowed service signatures must be entered one per line. Wildcards\n  (\\texttt{*}) are allowed for both class names and method names. The\n  \\texttt{\\#} symbol must be used to separate a class name from a method\n  name.\n\\end{itemize}\n\nFor example, \\texttt{com.liferay.portal.kernel.service.UserService}\nallows any method from the \\texttt{UserService} class to be invoked.\n\\texttt{com.liferay.document.library.kernel.service.DLAppService\\#get*}\nallows any method from the \\texttt{DLAppService} that starts with\n\\texttt{get} to be invoked. Thus,\n\n\\begin{verbatim}\ncom.liferay.portal.kernel.service.UserService\ncom.liferay.document.library.kernel.service.DLAppService#get*\n\\end{verbatim}\n\nallows any method from the \\texttt{UserService} class to be invoked and\nany method from the \\texttt{DLAppService} whose name starts with\n\\texttt{get} to be invoked.\n\nThere are 16 service access policies that are enabled by default. Six of\nthese have to do with the system:\n\n\\textbf{ASSET\\_ENTRY\\_DEFAULT:} Allows the view counter for assets to be\nupdated when an asset is retrieved.\n\n\\textbf{CALENDAR\\_DEFAULT:} Makes it possible to search public events in\nthe calendar.\n\n\\textbf{SYNC\\_DEFAULT:} Allows only the\n\\texttt{com.liferay.sync.service.SyncDLObjectService.getSyncContext}\nmethod. It applies to every Liferay Sync request, including\nunauthenticated Sync requests.\n\n\\textbf{SYNC\\_TOKEN:} Allows \\texttt{com.liferay.sync.service.*},\nmeaning that any API function that's a method of a class in this package\ncan be invoked. It applies to Sync requests which are accompanied by an\nauthentication token.\n\n\\textbf{SYSTEM\\_DEFAULT:} Allows access to country/region services by\nJavaScript calls, so users can switch languages on the fly. Applies to\nevery request, including unauthenticated requests.\n\n\\textbf{SYSTEM\\_USER\\_PASSWORD:} Allows any method to be invoked. Of\ncourse, since API functions include permission checks, this call works\nonly if the user has the required permission. It applies to requests for\nwhich \\texttt{AuthVerifierResult.isPasswordBasedAuthentication} is\n\\texttt{true}: i.e., whenever user authentication took place using a\npassword. If you want to completely disallow certain API functions from\nbeing invoked, you can change the \\texttt{SYSTEM\\_USER\\_PASSWORD} policy\nto something more restrictive than \\texttt{*}.\n\n\\texttt{SYNC\\_DEFAULT} and \\texttt{SYSTEM\\_DEFAULT}, as their names\nsuggest, are default service access policies. Default service access\npolicies are applied to all incoming requests, including unauthenticated\nrequests.\n\nThe other 10 policies have to do with OAuth and JSON web services:\n\n\\textbf{OAUTH2\\_analytics.read/write:} Integrates with\n\\href{https://www.liferay.com/products/analytics-cloud}{Liferay\nAnalytics Cloud}, allowing it access to JSON web services.\n\n\\textbf{OAUTH2\\_everything/read/documents/userprofile/write:} The\nEverything policies grant access to all the JSON web services for\nvarious reasons. Everything is everything: all JSON web services\n(matches \\texttt{*}). The others match method signatures appropriate to\ntheir description. For example, OAUTH2\\_everything.read matches all\nmethods starting with \\texttt{fetch}, \\texttt{get}, \\texttt{has},\n\\texttt{is}, or \\texttt{search}.\n\n\\textbf{OAUTH\\_READ/WRITE:} These provide access to JSON web services\nvia the OAuth 1.0a plugin.\n\nThe default configuration makes available corresponding scopes that\nprovide access to all web services shipped with the system. The scopes\nmust be assigned to OAuth 1 or 2 applications before they become usable.\nAdministrators should review the ones you want to use and disable the\nothers.\n\nYou can create new default service access policies:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the \\emph{Configuration} → \\emph{Service Access Policy}\n  section of the Control Panel.\n\\item\n  Click \\emph{Add}\n  (\\pandocbounded{\\includegraphics[keepaspectratio]{./images/icon-add.png}}).\n\\item\n  Give your policy a name.\n\\item\n  Flip the switch to enable your policy.\n\\item\n  If you want the policy applied to unauthenticated requests as well as\n  authenticated requests, flip the switch labeled \\emph{Default}.\n\\item\n  Give your policy a localized title.\n\\item\n  Under Allowed Service Signatures, start typing the fully qualified\n  name of a service class that's installed. Code completion helps you\n  find the class. For example, if you're creating a policy for Liferay's\n  Knowledge Base application, you could enter\n  \\texttt{com.liferay.knowledge.base.service.KBArticleService}.\n\\item\n  Under Method Name, start typing a service method call. Again, code\n  completion helps you. For Knowledge Base, you could enter\n  \\texttt{getKBArticle}.\n\\item\n  To specify another service or method, click the plus icon to add\n  another entry.\n\\item\n  When done, click \\emph{Save}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you know all the method signatures ahead of time, you\ncan click \\emph{Switch to Advanced Mode} and enter them all in one field\non separate lines.\n\n\\noindent\\hrulefill\n\nLiferay applications can declare their own default policies (the\n\\texttt{SYNC\\_DEFAULT} policy is a good example). This policy can then\nbe changed or disabled by administrators. In this case, the plugin can\nstill verify that the policy exists so there is no need to redefine or\nupdate it.\n\nBy default, Liferay's tunneling servlet uses the\n\\texttt{SYSTEM\\_USER\\_PASSWORD} service access policy. You can, however,\ncreate your own policy for the tunneling servlet and use the property\n\\texttt{service.access.policy.name} for the\n\\texttt{TunnelingServletAuthVerifier} to specify that your policy should\nbe used instead.\n\n\\section{Service Access Policy\nModule}\\label{service-access-policy-module}\n\nLiferay's service access policy functionality is provided by the Service\nAccess Policy module. This module includes the following important\nclasses:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.liferay.portal.kernel.security.service.access.policy.ServiceAccessPolicy}:\n  defines the public interface for \\texttt{ServiceAccessPolicy}.\n\\item\n  \\texttt{com.liferay.portal.kernel.security.service.access.policy.ServiceAccessPolicyManager}:\n  defines the public interface for retrieving instances of\n  \\texttt{ServiceAccessPolicy}.\n\\item\n  \\texttt{com.liferay.portal.kernel.security.service.access.policy.ServiceAccessPolicyManagerUtil}:\n  bridges service access policy functionality to the parts of Liferay's\n  core that have not yet been modularized.\n\\item\n  \\texttt{com.liferay.portal.kernel.security.service.access.policy.ServiceAccessPolicyThreadLocal}:\n  makes \\texttt{ServiceAccessPolicy} instances active.\n\\end{itemize}\n\nLiferay's Service Access Policy module resides in the\n\\texttt{modules/apps/service-access-policy} folder in the source code.\nWhen running, these three bundles provide the service access policy\nfunctionality (they're in the \\texttt{{[}Liferay\\ Home{]}/osgi/modules}\nfolder):\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.liferay.service.access.policy.api.jar}\n\\item\n  \\texttt{com.liferay.service.access.policy.service.jar}\n\\item\n  \\texttt{com.liferay.service.access.policy.web.jar}\n\\end{itemize}\n\nThese modules provide the service access policy management UI that's\naccessible from the Control Panel. They also provide the interface and\ndefault implementation for \\texttt{ServiceAccessPolicy}.\n\nTo configure the Service Access Policy module, navigate to the Control\nPanel, click on \\emph{System Settings}, and find the \\emph{Service\nAccess Policies} module in the Security section. Click on its name to\nedit it. Here, you can edit the default service access policy\nconfiguration. You can also force a default policy to be applied even\nwhen no policies are applied by the \\texttt{AuthVerifier}.\n\nThere's also an \\texttt{AuthenticatedAccessControlPolicy}. This policy\ndoesn't do anything if a \\texttt{ServiceAccessPolicyManager}\nimplementation is present. If the service access policy module is\ndisabled, however, the \\texttt{AuthenticatedAccessControlPolicy}\nprovides a fallback that still requires authenticated access for web\nservices.\n\n\\section{Summary}\\label{summary}\n\nGreat! Now you know service access policies can restrict access to\nLiferay DXP's web services. Custom service access policies can be\ncreated by portal administrators. They are applied by the portal's token\nauthenticator, e.g., by OAuth.\n\n\\section{Related Topics}\\label{related-topics-3}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/service-access-policies}{Creating\nService Access Policies}\n\n\\chapter{Authentication Verifiers}\\label{authentication-verifiers}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe Authentication Verification Layer is a centralized and extensible\nway to authenticate remote invocations of Liferay DXP's API.\n\nThe main responsibilities of the authentication verification layer are\nto\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Verify provided credentials using registered \\texttt{AuthVerifier}\n  instances\n\\item\n  Create portal authorization contexts based on verification results\n\\end{enumerate}\n\nIf no available \\texttt{AuthVerifier} can verify request credentials, an\nauthorization context supporting non-authenticated access is created for\na guest user. This allows each API to expose only a single API endpoint.\nIn contrast, legacy (prior to 6.2) versions of Liferay DXP exposed two\nAPI endpoints for each API: the \\texttt{/api/endpoint} URI was for\nnon-authenticated access and the URI \\texttt{/api/secure/endpoint} was\nfor authenticated access.\n\nThere are built-in \\texttt{AuthVerifier} implementations for the most\ncommon situations, such as when remote clients use HTTP Basic or HTTP\nDigest authentication, send credentials in request parameters, send\nauthenticated \\texttt{JSESSIONID}s, or use shared secrets to establish\ntrust. Other \\texttt{AuthVerifier} implementations can be deployed as\nmodules containing implementations of the \\texttt{AuthVerifier}\ninterface that are registered as services in the OSGi runtime.\n\nNote: The authentication verification layer's focus is on verifying\nauthentication, not on providing credentials. It does NOT issue tokens,\ncredentials, or display Sign In portlets. Instead, the layer verifies\nexisting credentials and authenticated sessions and is therefore a\ncomplement to authentication endpoints. To ensure backwards\ncompatibility, however, the default implementations support requests\nproviding user name and password credentials. Thus, the authentication\nverification layer stands on the border between authentication and\nauthorization.\n\n\\section{Authentication Verification Process\nOverview}\\label{authentication-verification-process-overview}\n\nThis layer and surrounding processes are provided by the\n\\texttt{AuthVerifierFilter} class that implements the\n\\texttt{javax.servlet.Filter} interface.\n\n\\textbf{Step 1: Verify Request Credentials}\n\nThe layer uses the chain of responsibility design pattern to support\nboth built-in and third party \\texttt{AuthVerifier} implementations.\nEach \\texttt{AuthVerifier} can provide configurations where it specifies\nmapped URLs and other properties.\n\nEach incoming request is matched against all registered\n\\texttt{AuthVerifier}s to select the final list of\n\\texttt{AuthVerifier}s that is used to process the request. It's the\nresponsibility of each \\texttt{AuthVerifier} to verify the incoming\nrequest credentials.\n\n\\textbf{Step 2: Create an Authorization Context}\n\nWhen a request is processed by all matching \\texttt{AuthVerifier}s,\nLiferay DXP creates an authorization context for the resolved user.\n\nThis encompasses setting the \\texttt{HttpServletRequest}\n\\texttt{remoteUser} to return the resolved user ID setting\n\\texttt{ThreadLocal}s to the resolved user.\n\nThe resolved user can be the user returned by one of the\n\\texttt{AuthVerifier} instances or a guest user if no instance was able\nto verify the provided credentials. \\texttt{AuthVerifier}s are created\nby developers, and are processed automatically as long as they're\nregistered in the OSGi runtime. Each Auth Verifier gets its own\nconfiguration in \\emph{Control Panel} → \\emph{System Settings} →\n\\emph{Security} → \\emph{API Authentication}. Configuration for Auth\nVerifiers that ship with the product include\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Basic Auth Header\n\\item\n  Digest Authentication\n\\item\n  HTTP Tunnel Extender\n\\item\n  Image Request\n\\item\n  Portal Sessions\n\\item\n  Request Parameter\n\\item\n  Tunnel Auth\n\\end{itemize}\n\nThe following Auth Verifiers are enabled by default and can be used to\naccess remote API out-of-the-box:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Basic Auth Header\n\\item\n  Portal Sessions\n\\end{itemize}\n\n\\section{Basic Auth Header}\\label{basic-auth-header}\n\nThis Auth Verifier allows the remote client to authenticate using\n\\href{https://en.wikipedia.org/wiki/Basic_access_authentication}{HTTP\nBasic Authentication}. Configure it by providing URL paths that should\nbe authenticated this way. When Force Basic Authentication field is\nchecked then HTTP Basic Authentication is required.\n\nThe default URLs are \\texttt{/api/*,/xmlrpc*} for web services. The\nmapping excludes \\texttt{/api/liferay*} to prevent accessing\n\\texttt{TunnelServlet}. For more information please see Tunnel\nAuthentication Verifiers.\n\n\\section{Digest Auth Header}\\label{digest-auth-header}\n\nThis Auth Verifier allows the remote client to authenticate using\n\\href{https://en.wikipedia.org/wiki/Digest_access_authentication}{HTTP\nDigest Authentication}. Configure it by providing URL paths that should\nbe authenticated this way. When Force Digest Authentication field is\nchecked then HTTP Basic Authentication is required.\n\nThis Auth Verifier is not enabled by default.\n\n\\section{HTTP Tunnel Extender}\\label{http-tunnel-extender}\n\nAs Liferay embraced modularity, this extender was written to enable\nmodules to be part of \\texttt{TunnelServlet}. It maps\n\\texttt{TunnelServlet} and \\texttt{TunnelingServletAuthVerifier} to the\nmodule servlet context. Modules with \\texttt{Http-Tunnel} in the\nmanifest can make use of the Tunnel Servlet, and can expose the API via\n\\texttt{/o/\\_module\\_/api/liferay/do}.\n\nConfigure it by setting client IP addresses allowed to tunnel. For more\ninformation, please see\n\\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#HTTP\\%20Tunneling}{the\nproperties documentation} as well as\n\\href{/docs/7-2/user/-/knowledge_base/u/enabling-remote-live-staging}{remote\nstaging}.\n\nNote that this is not a recommended way to export remote APIs; it's far\nbetter to expose remote services using JAX-RS or Liferay JSON Web\nService technologies.\n\n\\section{Image Request Authentication\nVerifier}\\label{image-request-authentication-verifier}\n\nWhen connected to LibreOffice/OpenOffice, the Office process must\ndownload images from Liferay DXP to render docs with images. To do this,\na \\href{https://jwt.io}{JWT Token} is created to access the images\nsecurely.\n\nConfigure this by setting the Hosts Allowed, URLs included, and URLs\nexcluded if necessary.\n\nThis Auth Verifier is not enabled by default.\n\n\\section{Portal Sessions Auth\nVerifiers}\\label{portal-sessions-auth-verifiers}\n\nEnables JavaScript in a browser to access Liferay JSON Web Services\nusing an existing portal session.\n\nIn the default configuration, the URLs included field shields access to\nthe legacy JSON remote services layer:\n\\texttt{/api/json*,/api/jsonws*,/c/portal/json\\_service*}.\n\n\\section{Request Parameter Auth\nVerifiers}\\label{request-parameter-auth-verifiers}\n\nFor backwards compatibility with \\texttt{RequestParameterAutoLogin} you\ncan authenticate and access portal endpoints with credentials inside\nHTTP request parameters \\texttt{parameterAutoLoginLogin} and\n\\texttt{parameterAutoLoginPassword}.\n\nThis Auth Verifier is not enabled by default.\n\n\\section{Tunnel Authentication\nVerifiers}\\label{tunnel-authentication-verifiers}\n\n\\texttt{TunnelServlet} is a legacy remote API endpoint mapped at\n\\texttt{/api/liferay/do} to provide access to the portal remote\nservices. The Tunnel Auth Verifier allows trusted remote clients\nauthenticated access using any user ID provided, on behalf of the user.\n\nAn example of a trusted remote client is the Staging remote publishing\nfeature.\n\nTrusted remote clients authenticate using a shared secret stored in the\nportal property \\texttt{tunneling.servlet.shared.secret}. The default\nvalue is empty and forbids all access.\n\nEven though the default configuration is enabled by default, access is\nlimited to localhost only. Configure it by setting client IP addresses\nallowed to tunnel. For more information, please see\n\\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#HTTP\\%20Tunneling}{the\nproperties documentation} as well as\n\\href{/docs/7-2/user/-/knowledge_base/u/enabling-remote-live-staging}{remote\nstaging}.\n\n\\section{Related Topics}\\label{related-topics-4}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/service-access-policies}{Service\nAccess Policies}\n\n\\chapter{LDAP}\\label{ldap}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLDAP is a common user store for Liferay DXP. You can configure LDAP at\nthe system scope in System Settings or at the instance scope in Instance\nsettings. Users can be imported from LDAP or exported to LDAP. To access\nLDAP configuration settings, navigate to \\emph{Control Panel →\nConfiguration} → \\emph{Instance Settings}. At the bottom of the list on\nthe left, click \\emph{Servers}.\n\nClick the \\emph{Add} button to add an LDAP server connection. If you\nhave more than one LDAP server, you can arrange the servers by order of\npreference using the up/down arrows. Regardless of how many LDAP servers\nyou add, each server has the same configuration options.\n\n\\textbf{Server Name:} Enter a name for your LDAP server.\n\n\\textbf{Default Values:} Several common directory servers appear here.\nIf you use one of these, select it. The rest of the form is populated\nwith default values for that directory.\n\n\\section{Connection}\\label{connection}\n\nThese settings cover the connection to LDAP.\n\n\\textbf{Base Provider URL:} The link to the LDAP server. Make sure the\nLiferay DXP server can communicate with the LDAP server. If there is a\nfirewall between the two systems, make sure the appropriate ports are\nopened.\n\n\\textbf{Base DN:} The Base Distinguished Name for your LDAP directory.\nIt is usually modeled after your organization. It may look similar to\nthis: \\texttt{dc=companynamehere,dc=com}.\n\n\\textbf{Principal:} The default LDAP administrator user ID is populated\nhere. If your administrator ID differs, use that credential instead. You\nneed an administrative credential because Liferay DXP uses this ID to\nsynchronize user accounts to and from LDAP.\n\n\\textbf{Credentials:} This is the password for the LDAP administrative\nuser.\n\nThis is all you need to make a regular connection to an LDAP directory.\nThe rest of the configuration, however, may need to be customized, as it\nrepresents ``best guesses'' as to correct defaults. The default\nattribute mappings usually provide enough data to synchronize back to\nthe Liferay DXP database when a user attempts to log in. To test the\nconnection to your LDAP server, click the \\emph{Test LDAP Connection}\nbutton.\n\n\\section{Checkpoint}\\label{checkpoint-1}\n\nBefore proceeding to fine tune Liferay DXP's LDAP connections, ensure\nthe following steps have been taken:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The LDAP connection is enabled. Depending on your needs, LDAP\n  authentication may be required so that only users who have been bound\n  may log in.\n\\item\n  \\emph{Export/Import}: for users in a clustered environment, Enable\n  Import/Export on Startup should be disabled so that there are no\n  massive imports on every node upon start up.\n\\item\n  When adding the LDAP server, the \\emph{Server Name}, \\emph{Default\n  Values}, \\emph{Connection} values are correct. It is always a good\n  idea to click the \\emph{Test LDAP Connection} before saving.\n\\end{enumerate}\n\n\\section{Instance Settings vs.~System\nSettings}\\label{instance-settings-vs.-system-settings}\n\nYou can define an LDAP server connection at the System Settings scope as\nwell. Because this user interface is auto-generated, it's not as helpful\nas the one in Instance Settings. For this reason, you should define and\ntroubleshoot your settings in Instance Settings first. If you decide you\nwant your LDAP connection at the system scope, you can copy your\nconfiguration from Instance Settings and then delete the server from\nInstance Settings.\n\nOf course, you can also configure LDAP servers at the system scope using\nOSGi \\texttt{.config} files. The easiest way to do this is to use the\nGUI and export the configuration. Then you can use the resulting\n\\texttt{.config} file anywhere you need it (such as other nodes in a\ncluster).\n\n\\textbf{Note:} To use \\texttt{config} files for LDAP server\nconfiguration, you must specify the Virtual Instance ID (in the source,\nthe variable name is \\texttt{companyId}) in the exported configuration\nfile, because servers are defined at the instance scope, not the system\nscope. To do this, specify the virtual instance ID somewhere in the file\nlike this:\n\n\\begin{verbatim}\ncompanyId=1234\n\\end{verbatim}\n\nYou can find your Virtual Instance ID in Control Panel → Configuration →\nVirtual Instances.\n\n\\section{Security}\\label{security}\n\nIf you run your LDAP directory in SSL mode to encrypt credential\ninformation on the network, you must perform extra steps to share the\nencryption key and certificate between the two systems.\n\nFor example, if your LDAP directory is Microsoft Active Directory on\nWindows Server 2003, you'd share the certificate like this:\n\nClick \\emph{Start} → \\emph{Administrative Tools} → \\emph{Certificate\nAuthority}. Highlight the machine that is the certificate authority,\nright-click on it, and click \\emph{Properties}. From the General menu,\nclick \\emph{View Certificate}. Select the Details view, and click\n\\emph{Copy To File}. Use the resulting wizard to save the certificate as\na file. You must also import the certificate into the \\emph{cacerts\nkeystore} like this:\n\n\\begin{verbatim}\nkeytool -import -trustcacerts -keystore /some/path/java-8-jdk/jre/lib/security/cacerts -storepass changeit -noprompt -alias MyRootCA -file /some/path/MyRootCA.cer\n\\end{verbatim}\n\nThe \\texttt{keytool} utility ships as part of the Java SDK.\n\nOnce this is done, go back to the LDAP page in the Control Panel. Modify\nthe LDAP URL in the Base DN field to the secure version by changing the\nprotocol to \\texttt{ldaps} and the port to \\texttt{636} like this:\n\n\\begin{verbatim}\nldaps://myLdapServerHostname:636\n\\end{verbatim}\n\nSave the changes. Communication to LDAP is now encrypted.\n\n\\section{Configuring LDAP\nImport/Export}\\label{configuring-ldap-importexport}\n\nThe other settings configure mappings between LDAP and Liferay DXP so\nusers and groups can be imported.\n\n\\section{Users}\\label{users}\n\nThis section contains settings for finding users in your LDAP directory.\n\n\\textbf{Authentication Search Filter:} Use this search filter box to\ndetermine the search criteria for user logins. By default, Liferay DXP\nuses users' email addresses for their login names. The value here must\nuse the authentication method you use. For example, if you changed\nLiferay DXP's authentication method to use screen names instead of the\nemail addresses, you would modify the search filter so it can match the\nentered log in name:\n\n\\begin{verbatim}\n(cn=@screen_name@)\n\\end{verbatim}\n\n\\textbf{Import Search Filter:} Depending on the LDAP schema, there are\ndifferent ways to identify the user. The default setting is usually\nfine:\n\n\\begin{verbatim}\n(objectClass=inetOrgPerson)\n\\end{verbatim}\n\nIf you want to search for only a subset of users or users that have\ndifferent LDAP object classes, you can change this.\n\n\\textbf{User Mapping:} Next, you can define mappings from LDAP\nattributes to Liferay fields. Though LDAP user attributes may be\ndifferent from LDAP server to LDAP server, there are five fields Liferay\nDXP requires to be mapped for the user to be recognized:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Screen Name} (e.g., \\texttt{uid} or \\texttt{cn})\n\\item\n  \\emph{Password} (e.g., \\texttt{userPassword})\n\\item\n  \\emph{Email Address} (e.g., \\texttt{mail} or \\texttt{email})\n\\item\n  \\emph{First Name} (e.g., \\texttt{name} or \\texttt{givenName})\n\\item\n  \\emph{Last Name} (e.g., \\texttt{sn})\n\\end{itemize}\n\n\\textbf{Note:} If you intend to create or import users with no email\naddresses, you must set \\texttt{users.email.address.required=false} in\n\\texttt{portal-ext.properties}. With this set, Liferay auto-generates an\nemail address combining the user ID plus the suffix defined in the\nproperty \\texttt{users.email.address.auto.suffix=}. Finally, make sure\nto set Liferay and LDAP authentication to something other than email\naddress.\n\nIf you want to import LDAP groups as Liferay DXP user groups, make sure\ndefine a mapping for the Liferay DXP group field so that membership\ninformation is preserved:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Group} (e.g., \\emph{member})\n\\end{itemize}\n\nThe other LDAP user mapping fields are optional.\n\nThe Control Panel provides default mappings for commonly used LDAP\nattributes. You can also add your own mappings.\n\n\\textbf{Test LDAP Users:} Once you have your attribute mappings set up\n(see above), click the \\emph{Test LDAP Users} button and Liferay DXP\nattempts to pull LDAP users and match them with their mappings as a\npreview.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/testing-ldap-users.png}}\n\\caption{You should see a list of users when you click the \\emph{Test\nLDAP Users} button.}\n\\end{figure}\n\n\\section{Groups}\\label{groups}\n\nThis section contains settings for mapping LDAP groups to Liferay DXP\nuser groups.\n\n\\textbf{Import Search Filter:} This is the filter for mapping LDAP\ngroups to Liferay DXP user groups. For example,\n\n\\begin{verbatim}\n(objectClass=groupOfNames)\n\\end{verbatim}\n\nEnter the LDAP group attributes you want retrieved for this mapping. The\nfollowing attributes can be mapped. The \\emph{Group Name} and\n\\emph{User} fields are required, the \\emph{Description} is optional.\n\n\\begin{itemize}\n\\item\n  \\emph{Group Name} (e.g., \\texttt{cn} or \\texttt{o})\n\\item\n  \\emph{Description} (e.g., \\texttt{description})\n\\item\n  \\emph{User} (e.g., \\texttt{member})\n\\end{itemize}\n\n\\textbf{Test LDAP Groups:} Click the \\emph{Test LDAP Groups} button to\ndisplay a list of the groups returned by your search filter.\n\n\\section{Export}\\label{export}\n\nThis section contains settings for exporting user data from LDAP.\n\n\\textbf{Users DN:} Enter the location in your LDAP tree where the users\nare stored. Liferay DXP exports the users to this location.\n\n\\textbf{User Default Object Classes:} Users are exported with the listed\ndefault object classes. To find out what your default object classes\nare, use an LDAP browser tool such as Apache Directory Studio to locate\na user and view the Object Class attributes stored in LDAP for that\nuser.\n\n\\textbf{Groups DN:} Enter the location in your LDAP tree where the\ngroups are stored. When Liferay DXP does an export, it exports the\ngroups to this location.\n\n\\textbf{Group Default Object Classes:} When a group is exported, the\ngroup is created with the listed default object classes. To find out\nwhat your default object classes are, use an LDAP browser tool such as\n\\href{https://directory.apache.org/studio}{Apache Directory Studio} to\nlocate a group and view the Object Class attributes stored in LDAP for\nthat group.\n\nWhen you've set all your options and tested your connection, click\n\\emph{Save}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If a user changes a value like a password in Liferay DXP,\nthat change is passed to the LDAP server, provided Liferay DXP has\nenough schema access to make the change.\n\n\\noindent\\hrulefill\n\nNow you know how to connect an LDAP server to Liferay DXP and how to\nconfigure user import behavior, export behavior, and other LDAP\nsettings.\n\n\\section{Related Topics}\\label{related-topics-5}\n\n\\href{/docs/7-0/deploy/-/knowledge_base/d/liferay-portal-security-overview}{Liferay\nDXP Security Overview}\n\\href{/docs/7-0/deploy/-/knowledge_base/d/logging-in-to-liferay}{Logging\ninto Liferay DXP}\n\n\\chapter{Configuring LDAP}\\label{configuring-ldap}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nIn this article, you'll learn how to configure import settings, export\nsettings, and related LDAP configuration settings.\n\nTo access LDAP configuration settings, navigate to \\emph{Control Panel →\nConfiguration} → \\emph{Instance Settings} → \\emph{Security} →\n\\emph{LDAP}. There are four categories on the left: Export, General,\nImport, and Servers. The Servers category was covered in\n\\href{/docs/7-2/deploy/-/knowledge_base/d/ldap}{the last article}. The\nrest are covered below.\n\n\\section{Export}\\label{export-1}\n\n\\textbf{Enable Export:} Check this box to export user accounts to LDAP.\nA listener tracks changes made to the \\texttt{User} object and pushes\nupdates to the LDAP server whenever a \\texttt{User} object is modified.\nNote that by default on every login, fields such as\n\\texttt{lastLoginDate} are updated. When export is enabled, this causes\na user export every time the user logs in. You can prevent updates to\nusers' \\texttt{lastLoginDate} fields from triggering LDAP user exports\nby setting the following property in your \\texttt{portal-ext.properties}\nfile:\n\n\\begin{verbatim}\nusers.update.last.login=false\n\\end{verbatim}\n\n\\textbf{Enable Group Export:} Export groups to LDAP.\n\n\\section{General}\\label{general}\n\n\\textbf{Enabled:} Check this box to enable LDAP Authentication.\n\n\\textbf{Required:} Check this box if LDAP authentication is required.\nUsers can't log in unless they can bind to the LDAP directory\nsuccessfully. Uncheck this box if users with Liferay DXP accounts but no\nLDAP accounts can log in.\n\n\\textbf{Use LDAP Password Policy:} Liferay DXP uses its own password\npolicy by default. This can be configured on the Control Panel's\nPassword Policies page. Check the \\emph{Use LDAP Password Policy} box if\nyou want to use the password policies defined by your LDAP directory.\nOnce this is enabled, the Password Policies tab states that you are not\nusing a local password policy. You must now use your LDAP directory's\nmechanism for setting password policies. Liferay DXP cannot enforce\nthese policies; the best it can do is pass through the messages returned\nby your LDAP server. It does this by parsing the messages in the LDAP\ncontrols the server returns. By default, Liferay DXP is configured to\nparse the messages returned by the Fedora Directory Server. If you use a\ndifferent LDAP server, you must customize the messages in \\emph{System\nSettings} → \\emph{Security} → \\emph{LDAP} → \\emph{Connection}.\n\n\\textbf{Method:} Choose \\emph{Bind} (the default) or \\emph{Password\nCompare}. Bind does a standard LDAP bind; Password Compare attempts to\ncompare Liferay and LDAP passwords using the encryption algorithm\nspecified in the field below. Password Compare is rarely used.\n\n\\textbf{Password Encryption Algorithm:} Choose the password encryption\nalgorithm your LDAP server uses to encrypt passwords so they can be\ncompared if using the Password Compare bind method. This is rarely used.\n\n\\section{Import}\\label{import}\n\nYou can import user data from LDAP directories using the following\noptions:\n\n\\textbf{Enable Import:} Check this box to do a mass import from your\nLDAP directories. Otherwise, Users are imported as they log in.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/imported-ldap-users.png}}\n\\caption{Ziltoid and Rex have been imported because they logged in.}\n\\end{figure}\n\n\\textbf{Enable Import on Startup:} Check this box to do the mass import\nwhen Liferay DXP starts. Note: this box only appears if you check\n\\textbf{Enable Import}, described above. Definitely leave this unchecked\nif you have a Liferay DXP cluster, or all your nodes will do a mass\nimport when each of them starts up.\n\n\\textbf{Import Interval:} When mass importing users, import users every\nX minutes.\n\n\\textbf{Import Method:} Set either User or Group. If you set this to\nUser, Liferay DXP imports all users from the location specified in the\nserver connection. If you set this to Group, Liferay DXP searches all\nthe groups and imports the users in each group. If you have users who do\nnot belong to any groups, they are not imported.\n\n\\textbf{Lock Expiration Time:} Set the account lock expiration time for\nLDAP User import. The default is one day.\n\n\\textbf{Import User Sync Strategy:} Set the strategy used to sync user\naccounts. Options are Auth Type (i.e., the way the user authenticates,\nlike with screen name) and UUID (requires a UUID attribute in LDAP).\n\n\\textbf{Enable User Password on Import:} Assign a default password (see\nbelow) when users are imported, so they can be synced between the two\nsystems.\n\n\\textbf{Autogenerate User Password on Import:} Create a random password\non user import.\n\n\\textbf{Default User Password:} Enter the default password users are\nassigned when they first log in via LDAP.\n\n\\textbf{Enable Group Cache on Import:} Cache the imported groups so\nimport isn't slowed by database access.\n\n\\textbf{Create Role per Group on Import:} For every LDAP group, create a\ncorresponding Liferay Role.\n\n\\section{Servers}\\label{servers}\n\n\\textbf{LDAP Servers:} Liferay DXP supports connections to multiple LDAP\nservers. Use the \\emph{Add} button to add LDAP servers. Each LDAP server\nhas several configuration options\n\\href{/docs/7-2/deploy/-/knowledge_base/d/ldap}{explained here}.\n\nOnce you've finished configuring LDAP, click the \\emph{Save} button.\n\n\\section{LDAP Options Available in System\nSettings}\\label{ldap-options-available-in-system-settings}\n\nAlthough most LDAP configuration can be done from Instance Settings,\nthere are several configuration parameters that are only available in\nSystem Settings. There are also settings duplicated from the ones in\nInstance Settings. These change the \\emph{default} settings for new\nvirtual instances (see note below).\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: When you make a change in System Settings, it takes\neffect for the virtual instance you're in. If after changing a setting\nyou create a new virtual instance, that virtual instance inherits the\nsettings of the one it was created from as defaults. For example, say\nyou have virtual instances named A, B, and C. From A, you modify\n\\emph{Error password history keywords}. This change appears only in A,\nnot in B or C. Then from A, you create virtual instance D. The change to\n\\emph{Error password history keywords} appears in D (not B or C), since\nD defaults to A's settings because you created it from A.\n\n\\noindent\\hrulefill\n\nIf you must change any of these options, navigate to \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{System Settings}. Go to the\n\\emph{Security} section and find the entries with LDAP in the title. The\nonly new settings here are in the \\emph{Connection} entry.\n\nUse the \\emph{Connection} entry to manage error properties like\n\\emph{Error password age keywords} which lets you set a list of phrases\nfrom error messages which can possibly be returned by the LDAP server.\nWhen a user binds to LDAP, the server returns \\emph{controls} with its\nresponse of success or failure. These controls contain a message\ndescribing the error or the information that is returned with the\nresponse. Though the controls are the same across LDAP servers, the\nmessages can be different. The properties described here contain\nsnippets of words from those messages and work with Red Hat's Fedora\nDirectory Server. If you are not using that server, the word snippets\nmay not work with your LDAP server. If they don't, you can replace the\nvalues of these properties with phrases from your server's error\nmessages. This enables Liferay DXP to recognize them.\n\n\\chapter{Token-based Single Sign On\nAuthentication}\\label{token-based-single-sign-on-authentication}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nToken-based SSO authentication was introduced in Liferay DXP 7.0 to\nstandardize support for Shibboleth, SiteMinder, Oracle OAM, or any other\nSSO product that works by propagating a token via one of the following\nmechanisms:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  HTTP request parameter\n\\item\n  HTTP request header\n\\item\n  HTTP cookie\n\\item\n  Session attribute\n\\end{itemize}\n\nSince these providers have a built-in web server module, you should use\nthe Token SSO configuration.\n\nThe authentication token contains the Liferay DXP user's screen name or\nemail address, whichever Liferay DXP has been configured to use for the\nparticular company (portal instance). Recall that Liferay DXP supports\nthree authentication methods:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  By email address\n\\item\n  By screen name\n\\item\n  By user ID\n\\end{itemize}\n\nToken-based authentication only supports email address and screen name.\nIf Liferay DXP is configured to use user ID when a token-based\nauthentication is attempted, the \\texttt{TokenAutoLogin} class logs this\nwarning:\n\n\\begin{verbatim}\nIncompatible setting for: company.security.auth.type\n\\end{verbatim}\n\nPlease note that the above sources are fully trusted.\n\nFurthermore, you must use a security mechanism external to Liferay DXP,\nsuch as a fronting web server like Apache. The chosen fronting solution\nmust prevent malicious Liferay DXP user impersonation that otherwise\nmight be possible by sending HTTP requests directly to Liferay DXP from\nthe client's web browser.\n\nToken-based authentication is disabled by default. To manage token-\nbased SSO authentication, navigate to Control Panel → \\emph{System\nSettings}, → \\emph{Security} → \\emph{SSO}. Token Based SSO appears in\nVirtual Instance Scope at the bottom. Here are the configuration options\nfor the Token Based SSO module:\n\n\\textbf{Enabled:} Check this box to enable token-based SSO\nauthentication.\n\n\\textbf{Import from LDAP:} Check this box to import users automatically\nfrom LDAP if they don't exist.\n\n\\textbf{User token name:} Set equal to the name of the token. This is\nretrieved from the specified location. (Example: SM\\_USER)\n\n\\textbf{Token location:} Set this to the location of the user token. As\nmentioned earlier, the options are:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  HTTP request parameter\n\\item\n  HTTP request header\n\\item\n  HTTP cookie\n\\item\n  Session attribute\n\\end{itemize}\n\n\\textbf{Authentication cookies:} Set this to the cookie names that must\nbe removed after logout. (Example: \\texttt{SMIDENTITY},\n\\texttt{SMSESSION})\n\n\\textbf{Logout redirect URL:} When user logs out of Liferay DXP, the\nuser is redirected to this URL.\n\nRemember to click \\emph{Save} to activate Token Based SSO.\n\n\\section{Required SiteMinder\nConfiguration}\\label{required-siteminder-configuration}\n\nIf you use SiteMinder, note that Liferay DXP sometimes uses the tilde\ncharacter in its URLs. By default, SiteMinder treats the tilde character\n(and others) as bad characters and returns an HTTP 500 error if it\nprocesses a URL containing any of them. To avoid this issue, change this\ndefault setting in the SiteMinder configuration to this one:\n\n\\begin{verbatim}\nBadUrlChars       //,./,/.,/*,*.,\\,%00-%1f,%7f-%ff,%25\n\\end{verbatim}\n\nThe configuration above is the same as the default except the\n\\texttt{\\textasciitilde{}} was removed from the bad URL character list.\nRestart SiteMinder to make your configuration update take effect. For\nmore information, please refer to SiteMinder's\n\\href{https://support.ca.com/cadocs/0/CA\\%20SiteMinder\\%20r6\\%200\\%20SP6-ENU/Bookshelf_Files/HTML/index.htm?toc.htm?258201.html}{documentation}\n\n\\section{Summary}\\label{summary-1}\n\nLiferay DXP's token-based SSO authentication mechanism is highly\nflexible and compatible with any SSO solution that provides it with a\nvalid Liferay DXP user's screen name or email address. These include\nShibboleth and SiteMinder.\n\n\\chapter{Authenticating with OpenID\nConnect}\\label{authenticating-with-openid-connect}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nOpenID Connect is a lightweight authentication layer built on top of the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0}{OAuth 2.0}\nauthorization protocol. It compliments local accounts by enabling users\nto authenticate using accounts they have on other systems. Users who\navoid signing up for new accounts can then use an account they already\nhave to sign into your website. By using OpenID Connect, you\n\\emph{delegate} user authentication to other providers, making it easy\nfor users with existing accounts to authenticate to your system.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can add multiple providers to your installation, but\nLiferay DXP can't be an OpenID Connect provider.\n\n\\noindent\\hrulefill\n\nBecause OpenID Connect is built on OAuth 2.0, its token flow is similar.\nOAuth 2.0 is only an authorization protocol, so it sends an \\emph{access\ntoken} that grants access to particular APIs. OpenID Connect adds to\nthis an \\emph{identity token} that passes user information like name and\nemail, provided the user has authenticated and granted permission.\n\n\\section{Creating a Client in OpenID Connect\nProvider}\\label{creating-a-client-in-openid-connect-provider}\n\nTo use OpenID Connect, you must first register it as a client in your\nprovider. This is an OAuth 2.0 client. The process varies by provider:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the provider's website and create a client.\n\\item\n  During the creation process, you must supply an \\emph{authorized\n  redirect URL} that can process the tokens sent from the provider.\n  Liferay DXP's URL is\n\n\\begin{verbatim}\nhttps://[server.domain]/c/portal/login/openidconnect\n\\end{verbatim}\n\\item\n  The provider sends several pieces of information. Some of these, like\n  the Discovery Endpoint, Authorization Endpoint, or Issuer URL are the\n  same regardless of the client. The two pieces of information unique to\n  your request are the \\texttt{client\\_id} and the\n  \\texttt{client\\_secret}.\n\\end{enumerate}\n\nCollect the information from the provider. You'll need it create the\nprovider next.\n\n\\section{Configuring an OpenID Connect Provider\nConnection}\\label{configuring-an-openid-connect-provider-connection}\n\nGo to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\nSettings} → \\emph{Security} → \\emph{SSO} and select \\textbf{\\emph{OpenID\nConnect Provider}} under the \\emph{System Scope} and follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the provider by clicking the \\emph{Add} button.\n\\item\n  Use the information you received from the provider to fill out the\n  form:\n\\end{enumerate}\n\n\\textbf{Provider Name:} This name appears in the Sign-In Portlet when\nusers use OpenID Connect to log in.\n\n\\textbf{OpenID Client ID:} Provide the OAuth 2.0 Client ID you received\nfrom your provider.\n\n\\textbf{OpenID Connect Client Secret:} Provide the OAuth 2.0 Client\nSecret you received from your provider.\n\n\\textbf{Scopes:} Leave the default, which requests the user name and the\nemail. Your provider may offer other scopes of user information.\n\n\\textbf{Discovery Endpoint:} Other URLs may be obtained from this URL,\nand they vary by provider.\n\n\\textbf{Discovery Endpoint Cache in Milliseconds:} Cache the endpoints\n(URLs) discovered for this amount of time.\n\n\\textbf{Authorization Endpoint:} This URL points to the provider's URL\nfor authorizing the user (i.e., signing the user in).\n\n\\textbf{Issuer URL:} The provider's URL that points to information about\nthe provider who is issuing the user information.\n\n\\textbf{JWKS URI:} A URL that points to the provider's JSON Web Key Set\nthat contains the public keys that can verify the provider's tokens.\n\n\\textbf{ID Token Signing Algorithms:} Set the supported ID token\nalgorithms manually. Normally, this is ``discovered'' at the discovery\nendpoint. You can add as many of these as you need.\n\n\\textbf{Subject Types:} A Subject Identifier is a unique and never\nreassigned identifier the provider uses to establish who the user is,\nand is consumed by the client (i.e., Liferay DXP). There are two types:\npublic (provides the same value to all clients) and private (provides a\ndifferent value to each client).\n\n\\textbf{Token Endpoint:} The provider's URL where tokens can be\nrequested.\n\n\\textbf{User Information Endpoint:} The OAuth 2.0 protected URL from\nwhich user information can be obtained.\n\nOnce you've filled out the form, click \\emph{Save}, and you're ready to\nenable OpenID Connect authentication.\n\n\\noindent\\hrulefill\n\nSystem Settings configuration file:\n\n\\begin{verbatim}\n com.liferay.portal.security.sso.openid.connect.internal.configuration.OpenIdConnectProviderConfiguration-[name].config\n\\end{verbatim}\n\nwhere \\texttt{{[}name{]}} is a descriptive, but unique name for example\n\\texttt{provider1}.\n\n\\noindent\\hrulefill\n\n\\section{Enabling OpenID Connect\nAuthentication}\\label{enabling-openid-connect-authentication}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Security} → \\emph{SSO} and select\n  \\textbf{\\emph{OpenID Connect}} under \\emph{Virtual Instance Scope}.\n\\item\n  Click the \\emph{Enabled} check box, and then click \\emph{Save}.\n\\end{enumerate}\n\n\\textbf{Note:} You can also enable OpenID Connect authentication for the\ngiven virtual instance through the \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{Instance Settings} → \\emph{OpenID Connect}\ntab.\n\n\\noindent\\hrulefill\n\nSystem Settings configuration file:\n\n\\begin{verbatim}\n com.liferay.portal.security.sso.openid.connect.configuration.OpenIdConnectConfiguration.config\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nNow users can sign in with OpenID Connect.\n\n\\section{Signing In With OpenID\nConnect}\\label{signing-in-with-openid-connect}\n\nThere's a new link in the Sign-In Portlet for signing in with OpenID\nConnect:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the Sign-In Portlet, click the OpenID Connect link at the bottom.\n\\item\n  Choose a provider and click \\emph{Sign In}.\n\\item\n  This takes you to your provider's sign in page. Enter your credentials\n  and log in.\n\\item\n  Upon successful authentication, you're redirected back to Liferay DXP\n  in an authenticated state.\n\\end{enumerate}\n\nOpenID is a standards-based, secure way to authenticate users from other\nsystems.\n\n\\chapter{OpenAM Single Sign On\nAuthentication}\\label{openam-single-sign-on-authentication}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nOpenAM is an open source single sign-on solution that comes from the\ncode base of Sun's System Access Manager product. Liferay DXP integrates\nwith OpenAM, allowing you to use OpenAM to integrate Liferay DXP into an\ninfrastructure that contains a multitude of different authentication\nschemes against different repositories of identities.\n\nNote that OpenAM relies on cookie sharing between applications. Thus, in\norder for OpenAM to work, \\textbf{all applications that require SSO must\nbe in the same web domain}. You should also add the following property\nif you have enabled HTTPOnly cookies due to the way some web containers\n(like Apache Tomcat™) parse cookies that contain special characters:\n\n\\begin{verbatim}\ncom.iplanet.am.cookie.encode=true\n\\end{verbatim}\n\nYou can install OpenAM on the same or different server as Liferay DXP.\nBe sure to review the context path and server hostname for your OpenAM\nserver.\n\nIf you want to install OpenAM on the same server as Liferay DXP, you\nmust deploy the OpenAM \\texttt{.war}, downloadable from\n\\href{https://backstage.forgerock.com/downloads/browse/am/archive/productId:openam}{here}.\nOtherwise, follow the instructions at the\n\\href{https://backstage.forgerock.com/docs/openam/13/install-guide/}{OpenAM\n13 site} to install OpenAM.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: OpenAM 12 and below work with Liferay DXP, but are at end\nof life. Because of this, we recommend only OpenAM 13 for production\nuse.\n\n\\noindent\\hrulefill\n\nOnce you have it installed, create the Liferay DXP administrative user\nin it. Users are mapped back and forth by screen names. By default, the\nLiferay DXP administrative user has a screen name of \\emph{test}, so if\nyou were to use that account, register the user in OpenAM with the ID of\n\\emph{test} and the email address specified in the\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Admin\\%20Portlet}{\\texttt{admin.email.from.address}}\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\nproperty}). Once you have the user set up, log in to OpenAM using this\nuser.\n\nIn the same browser window, log in to Liferay DXP as the administrative\nuser (using the previous admin email address). Go to the Control Panel\nand click \\emph{Configuration} → \\emph{Instance Settings} →\n\\emph{Security} → \\emph{SSO}. Then choose \\emph{OpenSSO} in the list on\nthe left.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/opensso-configuration.png}}\n\\caption{OpenSSO Configuration.}\n\\end{figure}\n\nModify the three URL fields (Login URL, Logout URL, and Service URL) so\nthey point to your OpenAM server (in other words, only modify the host\nname portion of the URLs), check the \\emph{Enabled} box, and click\n\\emph{Save}. Liferay DXP then redirects users to OpenAM when they\nrequest the \\texttt{/c/portal/login} URL \\emph{for example, when they\nclick on the }Sign In* link).\n\nLiferay DXP's OpenAM configuration can be applied at either the system\nscope or at the instance scope. To configure the OpenAM SSO module at\nthe system scope, navigate to the Control Panel, click on\n\\emph{Configuration} → \\emph{System Settings} → \\emph{Security} →\n\\emph{SSO} → \\emph{OpenSSO}. Click on it and you'll find these settings\nto configure. The values configured here provide the default values for\nall portal instances. Enter them in the same format as you would when\ninitializing a Java primitive type with a literal value.\n\nProperty Label \\textbar{} Property Key \\textbar{} Description \\textbar{}\nType \\textbf{Version} \\textbar{} \\texttt{version} \\textbar{} OpenAM\nversion to use (12 and below or 13) \\textbar{} \\texttt{String}\n\\textbf{Enabled} \\textbar{} \\texttt{enabled} \\textbar{} Check this box\nto enable OpenAM authentication. Note that OpenAM works only if LDAP\nauthentication is also enabled and Liferay DXP's authentication type is\nset to screen name. \\textbar{} \\texttt{boolean} \\textbf{Import from\nLDAP} \\textbar{} \\texttt{importFromLDAP} \\textbar{} If this is checked,\nusers authenticated from OpenAM that do not exist in Liferay DXP are\nimported from LDAP. LDAP must be enabled. \\textbar{} \\texttt{boolean}\n\\textbf{Login URL} \\textbar{} \\texttt{loginURL} \\textbar{} The URL to\nthe login page of the OpenAM server \\textbar{} \\texttt{String}\n\\textbf{Logout URL} \\textbar{} \\texttt{logoutURL} \\textbar{} The URL to\nthe logout page of the OpenAM server \\textbar{} \\texttt{String}\n\\textbf{Service URL} \\textbar{} \\texttt{serviceURL} \\textbar{} The URL\nby which OpenAM can be accessed to use the authenticated web services.\nIf you are using OpenAM Express 8 or higher, you need to have the server\nrunning Java 6. \\textbar{} \\texttt{String} \\textbf{Screen Name\nAttribute} \\textbar{} \\texttt{screenNameAttr} \\textbar{} The name of the\nattribute on the OpenAM representing the user's screen name \\textbar{}\n\\texttt{String} \\textbf{Email Address Attribute} \\textbar{}\n\\texttt{emailAddressAttr} \\textbar{} The name of the attribute on the\nOpenAM representing the user's email address \\textbar{} \\texttt{String}\n\\textbf{First Name Attribute} \\textbar{} \\texttt{firstNameAttr}\n\\textbar{} The name of the attribute on the OpenAM representing the\nuser's first name \\textbar{} \\texttt{String} \\textbf{Last Name\nAttribute} \\textbar{} \\texttt{lastNameAttr} \\textbar{} The name of the\nattribute on the OpenAM representing the user's last name \\textbar{}\n\\texttt{String}\n\nTo override these default settings for a particular portal instance,\nnavigate to the Control Panel and click \\emph{Configuration} →\n\\emph{Instance Settings} → \\emph{Security} → \\emph{SSO}. Then choose\n\\emph{OpenSSO} in the list on the left.\n\n\\chapter{CAS (Central Authentication Service) Single Sign On\nAuthentication}\\label{cas-central-authentication-service-single-sign-on-authentication}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nCAS is an authentication system originally created at Yale University.\nIt is a widely used open source single sign-on solution and was the\nfirst SSO product to be supported by Liferay DXP. @product@'s CAS module\nincludes the CAS client, so there's no need to install it separately.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Liferay DXP supports CAS 3.3.x. If you use a later\nversion of CAS, it is best to use CAS's support for standards such as\nOpenID Connect or SAML to interface with Liferay DXP.\n\n\\noindent\\hrulefill\n\nThe CAS Server application requires your server to have a properly\nconfigured Secure Socket Layer (SSL) certificate. To generate one\nyourself, use the \\texttt{keytool} utility that comes with the JDK.\nFirst generate the key, then export the key into a file. Finally, import\nthe key into your local Java key store. For public, Internet-based\nproduction environments, you must purchase a signed key from a\nrecognized certificate authority or have your key signed by a recognized\ncertificate authority. For Intranets, you should have your IT department\npre-configure users' browsers to accept the certificate so they don't\nget warning messages about the certificate.\n\nTo generate a key, use the following command:\n\n\\begin{verbatim}\nkeytool -genkey -alias tomcat -keypass changeit -keyalg RSA\n\\end{verbatim}\n\nInstead of the password in the example (\\texttt{changeit}), use a\npassword you can remember. If you are not using Tomcat, you may want to\nuse a different alias as well. For first and last names, enter\n\\texttt{localhost} or the host name of your server. It cannot be an IP\naddress.\n\nTo export the key to a file, use the following command:\n\n\\begin{verbatim}\nkeytool -export -alias tomcat -keypass changeit -file server.cert\n\\end{verbatim}\n\nFinally, to import the key into your Java key store, use the following\ncommand:\n\n\\begin{verbatim}\nkeytool -import -alias tomcat -file server.cert -keypass changeit -keystore $JAVA_HOME/jre/lib/security/cacerts\n\\end{verbatim}\n\nIf you are on a Windows system, replace \\texttt{\\$JAVA\\_HOME} above with\n\\texttt{\\%JAVA\\_HOME\\%}. Of course, all of this must be done on the\nsystem where CAS is running.\n\nOnce your CAS server is up and running, configure Liferay DXP to use it.\nCAS configuration can be applied either at the system scope or at the\nscope of a portal instance. To configure the CAS SSO module at the\nsystem or instance scope, navigate to the Control Panel, click on\n\\emph{Configuration} → \\emph{System Settings} (or \\emph{Instance\nSettings}) → \\emph{Security} → \\emph{SSO}. The values configured in\nSystem Settings provide the default values for all portal instances.\nEnable CAS authentication and then modify the URL properties to point to\nyour CAS server.\n\n\\textbf{Enabled:} Check this box to enable CAS single sign-on.\n\n\\textbf{Import from LDAP:} A user may be authenticated from CAS and not\nyet exist in Liferay DXP. Select this to automatically import users from\nLDAP if they do not exist in Liferay DXP. For this to work, LDAP must be\nenabled.\n\nThe rest of the settings are various URLs with defaults included. Change\n\\emph{localhost} in the default values to point to your CAS server. When\nyou are finished, click \\emph{Save}. After this, when users click the\n\\emph{Sign In} link, they are directed to the CAS server to sign in to\nLiferay DXP.\n\nFor some situations, it might be more convenient to specify the system\nconfiguration via files on the disk. To do so, create the following\nfile:\n\n\\begin{verbatim}\n{LIFERAY_HOME}/osgi/configs/com.liferay.portal.security.sso.cas.configuration.CASConfiguration.cfg\n\\end{verbatim}\n\nThe format of this file is the same as any properties file. The key to\nuse for each property that can be configured is shown below. Enter\nvalues in the same format as you would when initializing a Java\nprimitive type with a literal value.\n\nProperty Label \\textbar{} Property Key \\textbar{} Description \\textbar{}\nType \\textbf{Enabled} \\textbar{} \\texttt{enabled} \\textbar{} Check this\nbox to enable CAS SSO authentication. \\textbar{} \\texttt{boolean}\n\\textbf{Import from LDAP} \\textbar{} \\texttt{importFromLDAP} \\textbar{}\nUsers authenticated from CAS that do not exist in Liferay DXP are\nimported from LDAP. LDAP must be enabled separately. \\textbar{}\n\\texttt{boolean} \\textbf{Login URL} \\textbar{} \\texttt{loginURL}\n\\textbar{} Set the CAS server login URL. \\textbar{} \\texttt{String}\n\\textbf{Logout on session expiration} \\textbar{}\n\\texttt{logoutOnSessionExpiration} \\textbar{} If checked, browsers with\nexpired sessions are redirected to the CAS logout URL. \\textbar{}\n\\texttt{boolean} \\textbf{Logout URL} \\textbar{} \\texttt{logoutURL}\n\\textbar{} The CAS server logout URL. Set this if you want Liferay DXP's\nlogout function to trigger a CAS logout \\textbar{} \\texttt{String}\n\\textbf{Server Name} \\textbar{} \\texttt{serverName} \\textbar{} The name\nof the Liferay DXP instance (e.g., \\texttt{liferay.com}). If the\nprovided name includes the protocol (\\texttt{https://}, for example)\nthen this will be used together with the path \\texttt{/c/portal/login}\nto construct the URL to which the CAS server will provide tickets. If no\nscheme is provided, the scheme normally used to access the @product@\nlogin page will be used. \\textbar{} \\texttt{String} \\textbf{Server URL}\n\\textbar{} \\texttt{serviceURL} \\textbar{} If provided, this will be used\nas the URL to which the CAS server provides tickets. This overrides any\nURL constructed based on the Server Name as above. \\textbar{}\n\\texttt{String} \\textbf{No Such User Redirect URL} \\textbar{}\n\\texttt{noSuchUserRedirectURL} \\textbar{} Set the URL to which to\nredirect the user if the user can authenticate with CAS but cannot be\nfound in Liferay DXP. If import from LDAP is enabled, the user is\nredirected if the user could not be found or could not be imported from\nLDAP. \\textbar{} \\texttt{String}\n\nTo override system defaults for a particular portal instance, navigate\nto the Control Panel, click on \\emph{Configuration} → \\emph{Instance\nSettings}, click on \\emph{Authentication} on the right and then on\n\\emph{CAS} at the top.\n\n\\chapter{Authenticating Using SAML}\\label{authenticating-using-saml}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe SAML (Security Assertion Markup Language) adapter provides Single\nSign On (SSO) and Single Log Off (SLO) in your deployment. Each Liferay\nDXP instance serves as either the Service Provider (SP) or the Identity\nProvider (IdP). An identity provider is a trusted provider that provides\nsingle sign-on for users to access other websites. A service provider is\na website that hosts applications and grants access only to identified\nusers with proper credentials.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} A single Liferay DXP instance is \\emph{either} the SP or\nthe IdP in your SSO setup; it can't be both. You can, however, use\nseparate instances for both purposes (for example, one instance is the\nSP and another is the IdP).\n\n\\noindent\\hrulefill\n\nBelow is background on how SAML works. To jump right to its\nconfiguration, see the articles on\n\\href{/docs/7-2/deploy/-/knowledge_base/d/setting-up-liferay-as-a-saml-identity-provider}{Setting\nUp SAML as an Identity Provider} or\n\\href{/docs/7-2/deploy/-/knowledge_base/d/setting-up-liferay-as-a-saml-service-provider}{Setting\nUp SAML as a Service Provider} for instructions on using the\n\\href{https://web.liferay.com/marketplace/-/mp/application/15188711}{SAML\nadapter}. Use the instructions to make the conceptual magic from this\narticle come to life!\n\n\\section{What's new in Liferay Connector to SAML\n2.0}\\label{whats-new-in-liferay-connector-to-saml-2.0}\n\nThe \\texttt{5.0.0} version of the application for Liferay DXP brings\nsome long-awaited improvements:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay DXP acting as a Service Provider (SP) can now connect to\n  multiple Identity Providers (IdP).\n\\item\n  Developers have an extension point for customizing which Identity\n  Providers to users can use to sign in.\n\\item\n  Support for other Signature Algorithms (like \\texttt{SHA-256})\n\\item\n  Signature method algorithm URL's can now be blacklisted from the\n  metadata (for example, disabling \\texttt{SHA-1}:\n  \\texttt{http://www.w3.org/2000/09/xmldsig\\#rsa-sha1})\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you're migrating from a Liferay SAML adapter prior to\nversion 3.1.0, your portal properties are automatically migrated to\nSystem Settings configurations. Please see the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-saml}{Configuring\nSAML} article for details on settings.\n\n\\noindent\\hrulefill\n\n\\section{Important SAML Paths}\\label{important-saml-paths}\n\nFor reference, here are a few important SAML paths.\n\nThis URL is the default location of the metadata XML file:\n\n\\begin{verbatim}\n[host]:[port]/c/portal/saml/metadata\n\\end{verbatim}\n\nNote that when configuring SAML, no importing of SAML certificates is\nrequired. Liferay DXP reads certificates from the SAML metadata XML\nfile. If you want a third-party application like Salesforce to read a\nLiferay SAML certificate, you can export the Liferay DXP certificate\nfrom the keystore. The default keystore file is\n\n\\begin{verbatim}\n[Liferay Home]/data/keystore.jks \n\\end{verbatim}\n\nYou can change this path in System Settings → SSO → SAML Configuration →\nKey Store Path.\n\n\\section{Single Sign On}\\label{single-sign-on}\n\nBoth the IdP and the SP can initiate the Single Sign On process, and the\nSSO flow is different depending on each one. Regardless of how it's\ninitiated, SSO is configured for HTTPS between the SP and IdP, so all\ntransport-level communication is encrypted. SAML requests are signed\nusing certificates configured in Liferay DXP, using the SAML Web Browser\nSSO profile as defined in the\n\\href{http://saml.xml.org/saml-specifications}{SAML 2.0 specification}.\nIn all cases, responses are sent using HTTP-POST or HTTP-Redirect.\nHTTP-POST is preferred because it reduces the risk that the URL is too\nlong for a browser to handle.\n\nConsider IdP initiated SSO first.\n\n\\section{Identity Provider Initiated\nSSO}\\label{identity-provider-initiated-sso}\n\nSometimes a user enters the SSO cycle by sending a request directly from\nthe browser to the IdP.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/saml-idp-initiated-sso.png}}\n\\caption{Identity Provider Initiated SSO}\n\\end{figure}\n\n\\subsection{The SSO Request to the\nIdP}\\label{the-sso-request-to-the-idp}\n\nIf Liferay DXP is the IdP, the IdP initiated SSO URL\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Must specify the path as \\texttt{/c/portal/saml/sso}.\n\\item\n  Must include the \\texttt{entityId} parameter which is the identifier\n  to a previously configured Service Provider Connection (SPC).\n\\item\n  May include a \\texttt{RelayState} parameter which contains a URL\n  encoded value where the user is redirected upon successful\n  authentication. This URL should point to a location on the desired SPC\n  (according to the\n  \\href{https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf}{SAML\n  2.0 standards section 3.4.3}, this value \\emph{must not} exceed 80\n  bytes in length). It is useful to specify a landing page after SSO has\n  been executed.\n\\end{itemize}\n\nFor non-Liferay DXP IdPs (Siteminder, ADFS, etc.), consult the vendor's\ndocumentation on constructing IdP initiated SSO URLs.\n\nIf the IdP determines that the user isn't authenticated, it prompts the\nuser with the appropriate login screen.\n\n\\subsection{The SSO Response from the\nIdP}\\label{the-sso-response-from-the-idp}\n\nUpon successful authentication, the IdP constructs a SAML Response. It\nincludes attribute statements configured in the designated Service\nProvider Connection (SPC; see the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/setting-up-liferay-as-a-saml-identity-provider}{next\narticle} on setting up the SPC in Liferay DXP's SAML adapter).\n\nThe IdP sends the response to the Assertion Consumer Service URL. The\nrequest contains two parameters: \\texttt{SAMLResponse} and\n\\texttt{RelayState}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The method for sending the SAML response (for example,\nHTTP-POST) and the Assertion Consumer Service URL are usually imported\nas part of the SAML metadata XML provided by the SP. In Liferay DXP, you\nimport the SP's metadata in the SAML Adapter's Service Provider\nConnections tab.\n\n\\noindent\\hrulefill\n\n\\subsection{The SP Processes the SSO\nResponse}\\label{the-sp-processes-the-sso-response}\n\nThe SP validates and processes the SAML Response. Liferay DXP's SAML\nsolution requires signed \\texttt{SAMLResponse} messages. This signature\nprocess ensures proper identification for the IdP and prevents potential\nSAML Response spoofing.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  If one Liferay DXP instance is the IdP and another is the SP, make\n  sure the SAML metadata XML file imported into the SP contains the\n  IdP's certificate.\n\\item\n  If Liferay DXP is the IdP and another application is the SP, export\n  the certificate from the IdP and import it into the SP's certificate\n  store.\n\\end{itemize}\n\nIf a \\texttt{RelayState} is included in the SAML Response, the user is\nredirected to it. Otherwise the home page of the SP is served.\n\n\\section{Service Provider Initiated\nSSO}\\label{service-provider-initiated-sso}\n\nMost of the time, authentication requests come from the Service\nProvider.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/saml-sp-initiated-sso.png}}\n\\caption{Service Provider Initiated SSO}\n\\end{figure}\n\n\\subsection{The SSO Request to the SP}\\label{the-sso-request-to-the-sp}\n\nWhen the user's browser requests a protected resource or login URL on\nthe SP, it triggers the SP initiated SSO process. When Liferay DXP is\nthe SAML SP, SSO is initiated either by requesting\n\\texttt{/c/portal/login} URL or a protected resource that requires\nauthentication (for example, a document not viewable by the Guest Role).\nIf the user requests a protected resource, its URL is recorded in the\n\\texttt{RelayState} parameter. If the user requested\n\\texttt{/c/portal/login}, the \\texttt{RelayState} can be set by\nproviding the \\texttt{redirect} parameter. Otherwise, if the\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html}{portal\nproperty} \\texttt{auth.forward.by.last.path} is set to \\texttt{true},\nthe last accessed path is set as the \\texttt{RelayState}. For\nnon-Liferay DXP SPs, consult the vendor documentation on initiating SSO.\n\n\\subsection{The AuthnRequest to the\nIdP}\\label{the-authnrequest-to-the-idp}\n\nThe SP looks up the IdP's Single Sign On service URL and sends an\n\\texttt{AuthnRequest}. When Liferay DXP is the SP it looks up the\nconfigured SAML Identity Provider Connection and sends a SAML\n\\texttt{AuthnRequest} to the IdP's Single Sign On service URL as defined\nin the SAML metadata XML document. Liferay DXP supports sending and\nreceiving the \\texttt{AuthnRequest} using HTTP-POST or HTTP-Redirect\nbinding. HTTP-POST is preferred.\n\nIf the user doesn't have an active session or if \\texttt{ForceAuthn} was\nrequested by the SP, the user must authenticate by providing\ncredentials. When Liferay DXP is the IdP, authentication occurs in the\nLogin Portlet. Liferay DXP decodes and verifies the\n\\texttt{AuthnRequest} before requesting the user to authenticate.\n\n\\subsection{The SSO Response from the\nIdP}\\label{the-sso-response-from-the-idp-1}\n\nAfter authentication, a SAML Response is constructed, sent to the\nAssertion Consumer Service URL of the SP, and verified. The IdP\nautomatically makes this choice based on the SP metadata.\n\nWhen Liferay DXP is configured as the IdP, any attributes configured on\nthe Service Provider Connection are included in the response as\nattribute statements. The Assertion Consumer Service URL is looked up\nfrom the SAML metadata XML of the SP.\n\nWhen Liferay DXP is configured as the SP, response and assertion\nsignatures are verified. Liferay DXP requires the sender to be\nauthenticated. This is done via whole message signature from the issuing\nIdP. Responses missing the signature are considered unauthenticated and\nthe response is rejected. For non-Liferay DXP SP or IdP vendors, consult\ntheir documentation.\n\nThe user is redirected to the requested resource or to the URL contained\nin the \\texttt{RelayState} parameter (for example, the last page the\nuser accessed before initiating SSO).\n\n\\section{Single Log Off}\\label{single-log-off}\n\nThe Single Log Off request is sent from the user's browser to the IdP or\nan SP, and the SLO flow differs in each case. First consider IdP\ninitiated SLO.\n\n\\section{Identity Provider Initiated\nSLO}\\label{identity-provider-initiated-slo}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/saml-idp-initiated-slo.png}}\n\\caption{Identity Provider Initiated SLO}\n\\end{figure}\n\n\\subsection{The SLO Request to the\nIdP}\\label{the-slo-request-to-the-idp}\n\nAn IdP initiated SLO request is sent directly to the IdP by the user's\nbrowser. When Liferay DXP is the IdP, the IdP initiated SSO URL must\nspecify the URL path as\n\n\\texttt{/c/portal/logout}\n\nIf the user is signed on to any configured SP, the SAML plugin takes\nover the logout process, displaying all the signed on services. The\nsingle logout screen displays the authentication status of each SP and\nwhether any SPs can't be logged out of (for example, if the SP is down\nor doesn't support SLO). For non-Liferay DXP IdPs (Siteminder, ADFS,\netc.) consult the vendor's documentation on constructing IdP initiated\nSLO URLs.\n\nThe IdP sends a SAML \\texttt{LogoutRequest} to the SP.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  When Liferay DXP is configured as the IdP, the \\texttt{LogoutRequest}\n  is sent using either HTTP-POST, HTTP-Redirect, or SOAP binding.\n  HTTP-Post binding is preferred but in its absence, the first available\n  SLO endpoint with supported binding is selected.\n\\item\n  When Liferay DXP is configured as the SP, supported bindings for\n  \\texttt{LogoutRequest} are HTTP-Post, HTTP-Redirect, or SOAP.\n\\item\n  For other IdPs or SPs, please consult the vendor's documentation.\n\\end{itemize}\n\n\\subsection{The SLO Response from the\nSP}\\label{the-slo-response-from-the-sp}\n\nThe SP delivers a \\texttt{LogoutResponse} to the IdP.\n\nThe IdP sends a SAML \\texttt{LogoutRequest} to the second SP.\n\nThe second SP then delivers the \\texttt{LogoutResponse} to the IdP. The\nprocess is repeated for all SPs the user is logged into. When Liferay\nDXP is the IdP, Liferay DXP logs the user out after the last SP has\ndelivered its \\texttt{LogoutResponse} or has timed out.\n\n\\section{Service Provider Initiated\nSLO}\\label{service-provider-initiated-slo}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/saml-sp-initiated-slo.png}}\n\\caption{Service Provider Initiated SLO}\n\\end{figure}\n\n\\subsection{The SLO Request to the SP}\\label{the-slo-request-to-the-sp}\n\nIn SP initiated SLO, the user's browser sends a logout request directly\nto the SP. When Liferay DXP is configured as the SP, the SLO is\ninitiated by requesting this logout URL:\n\n\\begin{verbatim}\n/c/portal/logout\n\\end{verbatim}\n\nFor other SPs, consult the vendor's documentation on initiating SLO.\n\nA SAML \\texttt{LogoutRequest} is sent to the Single Log Out service URL\nof the IdP.\n\n\\begin{itemize}\n\\item\n  If Liferay DXP serves as the SP, the \\texttt{LogoutRequest} is sent to\n  the IdP configured by the IdP Connections tab of the SAML provider\n  (see the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/setting-up-liferay-as-a-saml-identity-provider}{next\n  article} to set up the IdP Connection) and the SLO service URL defined\n  in the SAML metadata.\n\\item\n  When Liferay DXP is the IdP, if the user has logged on to other SPs,\n  the user is presented with a single logout screen with the status of\n  each SP logout, flagging any that can't be looged out of (some SPs\n  might not support SLO or are currently down). If there are no other\n  SPs to log out of, the SAML session terminates and the IdP destroys\n  its session.\n\\end{itemize}\n\n\\subsection{The SLO Response from the\nSP}\\label{the-slo-response-from-the-sp-1}\n\nIf the user is logged in to additional SPs (beyond just the initiating\nSP), the IdP sends the SAML \\texttt{LogoutRequest} to each one. When\nLiferay DXP is the IdP, the \\texttt{LogoutResponse} is sent using either\nHTTP-Post, HTTP-Redirect, or SOAP binding.\n\nEach SP delivers its \\texttt{LogoutResponse} to the IdP. When Liferay\nDXP is the SP, the \\texttt{LogoutResponse} is sent using either\nHTTP-Post, HTTP-Redirect or direct response to SOAP request.\n\nAfter all additional SPs deliver their \\texttt{LogoutResponse}s to the\nIdP, the IdP destroys its SSO session. When Liferay DXP is the IdP, once\nthe last SP has delivered its \\texttt{LogoutResponse} or has timed out,\nthe IdP destroys the Liferay DXP session, logging out the user.\n\nFinally, the IdP sends a \\texttt{LogoutResponse} to the SP that\ninitiated SLO. The initiating SP terminates its SAML session and logs\nthe user out.\n\n\\section{Related Topics}\\label{related-topics-6}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/setting-up-liferay-as-a-saml-identity-provider}{Setting\n  Up SAML as an Identity Provider}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/setting-up-liferay-as-a-saml-service-provider}{Setting\n  Up SAML as a Service Provider}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/token-based-single-sign-on-authentication}{Token-Based\n  SSO Authentication}\n\\end{itemize}\n\n\\chapter{Setting up Liferay DXP as a SAML Identity\nProvider}\\label{setting-up-liferay-dxp-as-a-saml-identity-provider}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAn identity provider is a trusted provider that provides single sign-on\nfor users to access other websites. A service provider is a website that\nhosts applications and grants access only to identified users with\nproper credentials. Liferay Portal 6.1 EE and later versions support\nSAML 2.0 integration via the\n\\href{https://web.liferay.com/marketplace/-/mp/application/15188711}{Liferay\nConnector to SAML 2.0} application. It is provided from Liferay\nMarketplace and allows Liferay DXP to act as a SAML 2.0 identity\nprovider or as a service provider.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} You can set Liferay DXP up as an Identity Provider\nor as a Service Provider. Each single Liferay DXP instance can serve as\nan identity provider or as a service provider, but \\textbf{not both}.\nBoth configurations are covered in this article.\n\n\\noindent\\hrulefill\n\n\\section{Storing Your Keystore}\\label{storing-your-keystore}\n\nYour first step is to determine where to store your keystore. You have\ntwo options:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  In the file system\n\\item\n  In the Documents and Media library\n\\end{itemize}\n\nThe file system keystore manager is used by default and the default\nlocation is the \\texttt{{[}Liferay\\ Home{]}/data} directory (you can\nchange the location in System Settings → SSO → SAML Configuration → Key\nStore Path). To use Documents and Media library storage for your\nkeystore instead of file system storage, go to \\emph{Control Panel} →\n\\emph{System Settings} → \\emph{Security} → \\emph{SSO} → \\emph{SAML\nKeyStoreManager Implementation Configuration}. Select from the two\noptions: \\emph{Filesystem Keystore Manager} or \\emph{Document Library\nKeystore Manager}.\n\nIf you use Document Library storage, you can use any number of\n\\href{/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration}{back-end\nfile stores}. These are protected not only by the system where the key\nis stored, but also by Liferay DXP's permissions system.\n\n\\section{Configuring Liferay DXP as a SAML Identity\nProvider}\\label{configuring-liferay-dxp-as-a-saml-identity-provider}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  To access the SAML Admin interface, click on \\emph{Control Panel} →\n  \\emph{Configuration} and then on \\emph{SAML Admin}.\n\\item\n  To begin configuring Liferay DXP to use SAML, select a SAML role for\n  @product@ and choose an entity ID.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/saml-initial-config.png}}\n  \\caption{Select a SAML role for Liferay and enter an entity ID.}\n  \\end{figure}\n\n  Select the \\emph{Identity Provider} SAML role. Enter your own entity\n  ID. Then click \\emph{Save}. A new Certificate and Private Key section\n  appears.\n\\item\n  The Certificate and Private Key section lets you create a keystore for\n  SAML. Click on \\emph{Create Certificate} and enter the following\n  information:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Your common name (your first and last name)\n  \\item\n    The name of your organization\n  \\item\n    The name of your organizational unit\n  \\item\n    The name of your city or locality\n  \\item\n    The name of your state or province\n  \\item\n    The name of your country\n  \\item\n    The length in days that your keystore will remain valid (how long\n    before the keystore expires)\n  \\item\n    The key algorithm (RSA is the default)\n  \\item\n    The key length in bits (2048 is the default)\n  \\item\n    The key password\n  \\end{itemize}\n\n  Click \\emph{Save}.\n\n  When you create the certificate and private key, you also create a\n  keystore if one doesn't already exist. As described above, this\n  keystore has two storage options: file system storage (the default)\n  and Documents and Media storage. By default, the certificate uses the\n  \\texttt{SHA256} algorithm for encryption and is fingerprinted and\n  self-signed via RSA and \\texttt{SHA256}.\n\\item\n  After you click \\emph{Save}, you can click \\emph{Replace Certificate}\n  at any time to replace the current certificate with a new one if your\n  old one has expired or if you want to change the key's password.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/saml-keystore-info.png}}\n  \\caption{The General tab of the SAML Admin portlet displays\n  information about the current certificate and private key and allows\n  administrators to download the certificate or replace the\n  certificate.}\n  \\end{figure}\n\n  Three more tabs now appear:\n\n  \\textbf{General:} For enabling or disabling a SAML IdP and managing\n  the required keystore.\n\n  \\textbf{Identity Provider:} Contains IdP options, such as whether to\n  enable SSL. If SSL has been enabled, then SAML requests are not\n  approved unless they are also encrypted.\n\n  \\textbf{Service Provider Connections:} This tab manages any Service\n  Providers connected to this Liferay DXP instance.\n\n  See below for more information on the Identity Provider and Service\n  Provider Connections tabs.\n\\item\n  After you save your certificate and private key information, check the\n  \\emph{Enabled} box at the top of the General tab and click\n  \\emph{Save}. You successfully set Liferay DXP up as a SAML Identity\n  Provider!\n\\end{enumerate}\n\n\\section{Changing the Identity Provider\nSettings}\\label{changing-the-identity-provider-settings}\n\nTo configure Liferay DXP's SAML Identity Provider Settings, navigate to\nthe \\emph{Identity Provider} tab of the SAML Admin Control Panel entry.\n\nThe \\emph{Identity Provider} tab includes these options:\n\n\\textbf{Sign Metadata?:} Check this box to ensure the metadata XML file\nthat's produced is signed.\n\n\\textbf{SSL Required:} Check this box to reject any SAML messages that\nare \\emph{not} sent over SSL. This affects URLs in the generated\nmetadata.\n\n\\textbf{Require Authn Request Signature?:} When this box is checked,\neach Authn Request must be signed by the sending Service Provider. In\nmost cases, this should be enabled.\n\n\\textbf{Session Maximum Age:} Specify the maximum duration of the SAML\nSSO session in seconds. If this property is not set or is set to\n\\texttt{0}, the SSO session has an unlimited duration. The SSO session\nmaximum duration can be longer than the portal session maximum duration.\nIf the portal session expires before the SSO session expires, the user\nis logged back in to Liferay DXP automatically. SSO session expiration\ndoes not trigger a single logout from all service providers. You can use\nthe session maximum age, for example, to force users to sign in again\nafter a certain period of time.\n\n\\textbf{Session Timeout:} Specify the maximum idle time of the SAML SSO\nsession. Even if the session maximum age is unlimited, the SSO session\nexpires whenever the user's idle time reaches the limit set by the\nsession timeout property.\n\n\\section{Checkpoint}\\label{checkpoint-2}\n\nBefore adding a Service Provider (SP), verify you've completed these\ntasks:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  A SAML keystore has been generated. It can be stored in one of two\n  locations: the \\texttt{data} folder or in the Documents and Media\n  library.\n\\item\n  On the \\emph{Identity Provider} tab, the following settings have been\n  set:\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\alph{enumii}.}\n  \\item\n    \\textbf{Sign Metadata} has been checked.\n  \\item\n    \\textbf{SSL Required} - checked if SSL is active elsewhere. SSL is\n    disabled by default.\n  \\item\n    \\textbf{Authn Request Signature Required:} has been checked.\n  \\item\n    \\textbf{Session Maximum Age:} has been set. If set to \\texttt{0},\n    then the SSO has an unlimited duration.\n  \\item\n    \\textbf{Session Timeout:} Specify the maximum idle time of the SAML\n    SSO session.\n  \\end{enumerate}\n\\item\n  Once the \\emph{Enabled} checkbox has been checked, the IdP is live,\n  and you can generate the required metadata. This URL is the default\n  location of Liferay DXP's metadata XML file:\n\n\\begin{verbatim}\n [host]:[port]/c/portal/saml/metadata \n\\end{verbatim}\n\\end{enumerate}\n\nIf this URL does not display correctly, then the SAML instance has not\nbeen enabled. Use the URL or click \\emph{Save} in the browser to\ngenerate an actual \\texttt{XML} file.\n\nYour Identity Provider is now set up. Next, you must register a Service\nProvider.\n\n\\chapter{Registering a SAML Service\nProvider}\\label{registering-a-saml-service-provider}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nSetting up Liferay DXP as a SAML Identity Provider is only useful if you\ncan connect to one or more SAML Service Providers. Navigate to the\nService Provider Connections tab of the SAML Admin Control Panel entry\nand click the \\emph{Add Service Provider} button to add a SAML Service\nProvider.\n\nThe New Service Provider page includes these options:\n\n\\textbf{Name:} The name of the Service Provider with which to connect.\nThe name can be anything; it's purely cosmetic.\n\n\\textbf{Entity ID:} The Service Provider's entity ID. This value must\nmatch the entity ID declared in the Service Provider metadata.\n\n\\textbf{Enabled:} Check this box to activate the Service Provider\nconnection.\n\n\\textbf{Assertion Lifetime:} Defines the number of seconds after which\nthe SAML assertion issued by the Identity Provider should be considered\nexpired.\n\n\\textbf{Force Encryption:} If the SP does not provide a public key for\nencrypting the assertions, abort the single sign-on.\n\n\\textbf{Metadata:} Provide a URL to the Service Provider metadata XML\nfile or manually upload the Service Provider metadata XML file. If you\nprovide a URL, the XML file is retrieved and periodically polled for\nupdates. The update interval can be configured in System Settings with\nthe Runtime Metadata Refresh Interval\n(\\texttt{saml.metadata.refresh.interval} if using a \\texttt{config}\nfile) property which specifies a number of seconds. If fetching the\nmetadata XML file by URL fails, you can't enable the Service Provider\nconnection. If the Identity Provider server cannot access the metadata\nvia URL, you can upload the XML file manually. In this case, the\nmetadata XML file is not updated automatically.\n\n\\textbf{Name Identifier Format:} Choose the Name Identifier Format used\nin the SAML Response. This should be set according to what the Service\nProvider expects to receive. For Liferay Service Providers, any\nselection other than email address indicates that the Name Identifier\nrefers to screen name. The formats don't have any special meaning to\nLiferay Identity Providers. The \\texttt{NameID} value is defined by the\nName Identifier attribute. See the next option.\n\n\\textbf{Name Identifier Attribute Name:} This specifies which attribute\nof the Liferay DXP \\texttt{User} object to use as the \\texttt{NameID}\nvalue. Possible values include \\texttt{emailAddress},\n\\texttt{screenName} and \\texttt{uuid}. Additionally, you can prefix the\nname with \\texttt{static:} or \\texttt{expando:}. If you use the prefix\n\\texttt{static}, the value is whatever comes after \\texttt{static:}. If\nyou use the prefix \\texttt{expando}, the value is whatever custom field\nis specified after \\texttt{expando:}. For example, \\texttt{expando:SSN}\nwould look up the \\texttt{User} custom field with the name \\texttt{SSN}.\n\n\\textbf{Attributes Enabled:} Include and resolve assertion attributes.\n\n\\textbf{Attributes Namespace Enabled:} When this box is checked, the\nattribute names are namespaced like this:\n\n\\begin{verbatim}\nurn:liferay:user:expando:\nurn:liferay:user:\nurn:liferay:groups:\nurn:liferay:organizationRole:\nurn:liferay:organization:\nurn:liferay:roles:\nurn:liferay:siteRole:\nurn:liferay:userGroupRole:\nurn:liferay:userGroups:\n\\end{verbatim}\n\n\\textbf{Attributes:} Enter a list of attributes to include in the\nassertion, one per line. Each line is an expression that gets parsed.\nExamples:\n\n\\begin{verbatim}\norganizations\norganizationRoles\nroles\nsiteRoles\nuserGroups\nstatic:[attributeName]=[attributeValue]\nexpando:[userCustomFieldName] \n\\end{verbatim}\n\nNote that the full namespace depends on the attribute name. Attribute\nnamespaces can be useful. Use them when attribute names from different\nnamespaces might conflict. For example, \\texttt{expando:user} vs\n\\texttt{urn:liferay:roles:user}.\n\n\\textbf{Keep Alive URL:} If users are logged into several Liferay DXP SP\ninstances via a Liferay DXP IdP, their sessions can be kept alive as\nlong as they keep a browser window open to one of them. Configure this\nonly if the SP is Liferay DXP. The URL is\n\\texttt{https://{[}SP\\ host\\ name{]}/c/portal/saml/keep\\_alive}.\n\n\\section{Checkpoint}\\label{checkpoint-3}\n\nVerify your settings are correct by connecting the Liferay DXP-based IdP\nto its first SP. SPs connect to only one IdP, so if the first one\ndoesn't work, the rest won't either.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Provide a general name for the SP.\n\\item\n  The \\texttt{Entity\\ ID} name must be identical to the one declared in\n  the Service Provider metadata.\n\\item\n  Check the \\emph{Enabled} checkbox.\n\\item\n  Set a value for the \\emph{Assertion Lifetime}.\n\\item\n  Choose whether encryption should be required (recommended).\n\\item\n  Make sure the SP's metadata has been provided either as a URL or an\n  XML file has been uploaded.\n\\item\n  Make sure \\emph{Name Identifier Format} and \\emph{Name Identifier\n  Attribute Name} have been set.\n\\item\n  Make sure \\emph{Attributes Namespace Enabled} has been set.\n\\end{enumerate}\n\nIf you don't have a Service Provider to add right now, that's fine. In\nthe next section, you'll learn how to set Liferay DXP up as a SAML\nService Provider. The same instance can't be both, but after you set up\nanother Liferay DXP instance as a Service Provider, come back to this\none and add the Service Provider according to the instructions above.\n\n\\chapter{Setting up Liferay DXP as a SAML Service\nProvider}\\label{setting-up-liferay-dxp-as-a-saml-service-provider}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nMany of these steps are similar to configuring Liferay DXP as a SAML\nIdentity Provider. As a reminder, a single Liferay DXP installation can\nbe configured as a SAML Identify Provider \\emph{or} as a SAML Service\nProvider but not as both. If your Liferay DXP installation is already a\nSAML Identity Provider, use a \\emph{different} Liferay DXP installation\nas a SAML Service Provider.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you have a third party IdP with Liferay DXP as the SP,\nall messages coming from the IdP must be signed. If they're not, an\nerror message appears and communication between the IdP and Liferay DXP\nfails.\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Install the Liferay Connector to SAML 2.0 app. To confirm successful\n  deployment, look for the \\emph{SAML Admin} entry in the Configuration\n  section of the Control Panel.\n\\item\n  To begin configuring Liferay DXP to use SAML, you must select a SAML\n  role for Liferay DXP and choose an entity ID. Select the \\emph{Service\n  Provider} SAML role. Choose your own entity ID. Then click \\emph{Save}\n  and a new section entitled Certificate and Private Key appears.\n\\item\n  The Certificate and Private Key section is for creating a keystore for\n  SAML. Click \\emph{Create Certificate} and enter the following\n  information:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Your common name (your first and last name)\n  \\item\n    The name of your organization\n  \\item\n    The name of your organizational unit\n  \\item\n    The name of your city or locality\n  \\item\n    The name of your state or province\n  \\item\n    The name of your country\n  \\item\n    The length in days that your keystore will remain valid (how long\n    before the keystore expires)\n  \\item\n    The key algorithm (RSA is the default)\n  \\item\n    The key length in bits (2048 is the default)\n  \\item\n    The key password\n  \\end{itemize}\n\n  Remember that the keystore has two storage options: file system\n  storage (the default) and Documents and Media storage.By default, the\n  certificate uses the SHA256 algorithm for encryption and is\n  fingerprinted and self-signed via MD5 and SHA1. When you enter all the\n  required information, click \\emph{Save}.\n\\item\n  After you clicked \\emph{Save}, check that you can view information\n  about your certificate or download your certificate. If you can, you\n  successfully created a keystore. After you create a keystore,\n  additional options appear. There are three tabs:\n\n  \\textbf{General}: Enables or disables SAML SP and manages the required\n  keystore.\n\n  \\textbf{Service Provider}: This tab manages basic and advanced\n  configurations for the SP.\n\n  \\textbf{Identity Provider Connection}: This tab manages connections to\n  the IdP. There can be multiple IdP connections.\n\\item\n  You can also generate an encryption certificate. This is a separate\n  key for encrypting assertions. If you want assertions encrypted, you\n  must generate a key for this. The procedure is exactly the same as\n  generating your certificate in step 3 above.\n\\item\n  Next, you must configure an Identity Provider connection. Click on the\n  \\emph{Identity Provider Connections} tab. Enter a name for the\n  Identity Provider, enter its entity ID, and enter its metadata URL. If\n  you have already followed the previous instructions and configured a\n  separate Liferay DXP installation as an Identify provider, you'd enter\n  the following information:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Name: \\emph{Liferay IdP}\n  \\item\n    Entity ID: {[}ID of IdP{]}\n  \\item\n    Clock Skew: Set the tolerance in milliseconds between SP and IdP.\n  \\item\n    Force Authn: Whether the IdP should force reauthentication\n    regardless of context.\n  \\item\n    Metadata URL: http://localhost:8080/c/portal/saml/metadata (test\n    this URL first)\n  \\item\n    Name Identifier Format: See settings.\n  \\item\n    Attribute Mapping: See settings.\n  \\item\n    Keep Alive URL: See settings.\n  \\end{itemize}\n\n  \\textbf{Important}: The Liferay Connector to SAML 2.0 app supports\n  using \\emph{either} a URL to a SAML IdP metadata file \\emph{or} an\n  actual (uploaded) SAML metadata XML file. The value entered in the\n  \\emph{Metadata URL} field is persisted to the database only when there\n  a metadata URL and there is no specified metadata XML file. Otherwise,\n  Liferay DXP keeps the original metadata URL in the database. This\n  behavior ensures that once a metadata URL has been specified, there is\n  always a metadata URL saved in the database. This way, if a portal\n  administrator forgets the previously entered metadata URL or its\n  format, he or she can look at the displayed metadata URL and choose to\n  modify the displayed metadata URL or overwrite the previously saved\n  metadata URL by specifying a metadata XML file.\n\\item\n  Finally, after you save your certificate and private key information\n  and configure an Identity Provider connection, check the\n  \\emph{Enabled} box at the top of the General tab and click\n  \\emph{Save}. Liferay DXP is now a SAML Service Provider!\n\\end{enumerate}\n\nNote that the SAML Service Provider session is tied to the normal\nsession on the application server. Session expiration on the application\nserver terminates the session on the Service Provider but does not\ninitiate single logout.\n\nYou can add multiple IdP connections. To add another Identity Provider,\nclick \\emph{Add Identity Provider} again and enter the details for the\nother provider. When users log in, they are asked to choose an identity\nprovider, so be sure to name the providers so users can recognize them.\n\n\\section{Checkpoint}\\label{checkpoint-4}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  A SAML keystore has been generated.\n\\item\n  Verify the connection to the IdP.\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\alph{enumii}.}\n  \\item\n    \\emph{Name}: generic name for the IdP.\n  \\item\n    \\emph{Entity ID}: the same name of the IdP. If the IdP is another\n    Liferay DXP instance, then it is the same name as the above example.\n  \\item\n    \\emph{Metadata URL}: The IdP's metadata as a URL or as an XML file.\n  \\item\n    If the IdP is another @product instance, ensure its corresponding\n    Service Provider Connection for this SP is enabled.\n  \\end{enumerate}\n\\item\n  On the \\emph{General} tab, the \\emph{Enabled} checkbox has been\n  checked.\n\\item\n  Once \\emph{Enabled} checkbox has been checked, the service provider's\n  metadata becomes available:\n\n\\begin{verbatim}\n [host]:[port]/c/portal/saml/metadata\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Setting Up Liferay DXP as a SAML Service Provider in a\nClustered\nEnvironment}\\label{setting-up-liferay-dxp-as-a-saml-service-provider-in-a-clustered-environment}\n\nYou can use the Liferay Connector to SAML 2.0 app as an SSO solution for\na clustered Liferay DXP environment. If your multi-node cluster is\nbehind a load balancer, you must enable all the nodes as SPs, and they\nmust share the same keystore manager.\n\nIf using the Filesystem Keystore Manager (the default):\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Configure each node of your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering}{Liferay\n  DXP cluster} as a SAML service provider as above.\n\\item\n  Copy the keystore file\n  (\\texttt{{[}Liferay\\ Home{]}/data/keystore.jks}, by default) from the\n  first node to the remaining nodes. This file is the Java keystore\n  that's created by the SAML Provider app. The keystore contains the\n  valid or self-signed certificate managed by the SAML connector app.\n\\item\n  Verify that the service provider metadata has been generated to be\n  used either as a URL or an XML file. The metadata is the same for all\n  nodes because of the same database back-end. The IdP's request goes\n  through the load balancer.\n\\item\n  At this point, all nodes have the same SAML SP configuration and each\n  of them can respond to web requests and handle the SAML protocol. To\n  test your SSO solution, sign into Liferay DXP via your load balancer,\n  navigate to a few pages of a few different sites, and then log out.\n\\end{enumerate}\n\nIf using the Document Library Keystore Manager, skip step 2 because the\nkeystore file is stored in the database shared by all the nodes.\n\nNow you know how to configure Liferay DXP either as a SAML identity\nprovider or a service provider. You also know how to configure SAML in a\nclustered environment.\n\n\\chapter{Changing the Settings for Service Provider and Identity\nProvider\nConnections}\\label{changing-the-settings-for-service-provider-and-identity-provider-connections}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nTo change the SAML Service Provider Settings, navigate to the Service\nProvider tab.\n\nThe Service Provider tab includes these options:\n\n\\textbf{Require Assertion Signature?:} Check this box to require SAML\nassertions to be individually signed in addition to the entire SAML\nmessage.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Individual assertions need not be signed as long as the\nSAML response itself is signed. The SP and IdP should always communicate\nover \\texttt{https} to have encryption at the transport level.\n\nIf you believe man-in-the-middle attacks are possible, the SAML response\ncan be signed. The only reason to sign the assertions is if the SAML\nresponse is not signed. In this case, assertions should not only be\nsigned but also encrypted.\n\n\\noindent\\hrulefill\n\n\\textbf{Clock Skew:} Clock skew is a tolerance in milliseconds used by\nthe Service Provider for mitigating time differences between the clocks\nof the Identity Provider and the Service Provider. This usually only\nmatters when assertions have been made to expire very quickly.\n\n\\textbf{LDAP Import Enabled:} Check this box to import user information\nfrom the configured LDAP connection based on the resolved\n\\texttt{NameID}. LDAP connections can be configured from Instance\nSettings.\n\n\\textbf{Sign Authn Requests:} Check this box to sign the\n\\texttt{AuthnRequest} even if the Identity Provider metadata indicates\nthat it's not required.\n\n\\textbf{Sign Metadata:} Check this box to sign the metadata XML file.\n\n\\textbf{SSL Required:} Check this box to reject SAML messages that are\nnot sent over HTTPS. This does not affect how URLs are generated.\n\n\\section{Changing the SAML Identity Provider Connection\nSettings}\\label{changing-the-saml-identity-provider-connection-settings}\n\nTo configure Liferay DXP's SAML Identity Provider Settings, navigate to\nthe Identity Provider Connection tab of the SAML Admin portlet and click\nthe \\emph{Edit} action button on the IdP you want to configure.\n\n\\textbf{Name:} The name of the Identity Provider with which to connect.\n\n\\textbf{Entity ID:} The Identity Provider's entity ID. This value must\nmatch the entity ID declared in the Identity Provider metadata.\n\n\\textbf{Enabled:} Check the box to enable this IdP.\n\n\\textbf{Clock Skew:} Clock skew is a tolerance in milliseconds used by\nthe Service Provider for mitigating time differences between the clocks\nof the Identity Provider and the Service Provider. This usually only\nmatters when assertions have been made to expire very quickly.\n\n\\textbf{Force Authn:} Check this box to have the Service Provider ask\nthe Identity Provider to re-authenticate the user before verifying the\nuser.\n\n\\textbf{Metadata:} You can provide a URL to the Identity Provider\nmetadata XML file or you can manually upload it. If you provide a URL,\nthe XML file is automatically retrieved and periodically polled for\nupdates. You can change the update interval in System Settings by\nmodifying the Runtime Metadata Refresh Interval property which specifies\na number of seconds. If fetching the metadata XML file by URL fails, you\ncan't enable the Identity Provider connection. If the metadata is\ninaccessible via URL, you can upload the XML file manually. In this\ncase, the metadata XML file is not updated automatically.\n\n\\textbf{Name Identifier Format:} Choose the Name Identifier Format used\nin the SAML Response. Set this according to what the Service Provider\nexpects to receive. For Liferay Service Providers, selections other than\nemail address indicate that the Name Identifier refers to screen name.\nThe formats don't have any special meaning to Liferay Identity\nProviders. The Name Identifier attribute defines the \\texttt{NameID}\nvalue.\n\n\\textbf{Attribute Mapping:} Attribute mapping is done from the attribute\nname or friendly name in the SAML Response to the Liferay DXP attribute\nname. For example, if you want to map a response attribute named\n\\texttt{mail} to the Liferay DXP attribute \\texttt{emailAddress}, enter\nthe following mapping:\n\n\\begin{verbatim}\nmail=emailAddress\n\\end{verbatim}\n\nAvailable Liferay DXP attributes are: \\texttt{emailAddress},\n\\texttt{screenName}, \\texttt{firstName}, \\texttt{lastName},\n\\texttt{modifiedDate}, and \\texttt{uuid}.\n\n\\textbf{Keep Alive URL:} If users are logged into several Liferay DXP SP\ninstances via a Liferay DXP IdP, their sessions can be kept alive as\nlong as they keep a browser window open to one of them. Configure this\nonly if the IdP is Liferay DXP. The URL is\n\\texttt{https://{[}IdP\\ host\\ name{]}/c/portal/saml/keep\\_alive}. On the\nLiferay DXP IdP, configure this URL the same way, but point back to this\nSP.\n\nSave your changes when you are finished configuring the Liferay DXP\ninstance as a service provider. There is no need to restart the server:\nthe changes are applied immediately.\n\nMake the above configurations through the SAML Control Panel interface\nand not via properties. Some features of the Liferay Connector to SAML\n2.0 app are not available as properties.\n\n\\noindent\\hrulefill\n\n\\textbf{Limitation:} The Liferay SAML app can only be used with a single\nvirtual host. Technically, this means that in the SAML metadata for\nLiferay DXP, only one binding can be added in this form:\n\n\\begin{verbatim}\n<md:EntityDescriptor>\n...\n<md:SPSSODescriptor>\n...\n<md:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"https://portal.domain.com/c/portal/saml/acs\" index=\"1\" isDefault=\"true\" />\n...\n</md:SPSSODescriptor>\n</md:EntityDescriptor>\n\n\\noindent\\hrulefill\n\n\n\n# Configuring SAML\n\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/configuring-sso/authenticating-with-saml.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThere are some ways of configuring the SAML plugin outside the UI. This is done\nvia OSGi configuration files and by uploading metadata XML to configure how\nconnections are negotiated. \n\n## OSGi Configuration Properties\n\nAs noted in the previous tutorials, anything related to configuring SP\nconnections must be done through the SAML Admin UI where configurations are\nsaved to Liferay's database. SP connections can no longer be made via properties\nfiles as they were in versions prior to 3.1.0. \n\n\n\\noindent\\hrulefill\n\n**Note:** Don't use OSGi `.config` files or Liferay DXP's System Settings Control\nPanel application to configure SAML providers (IdP or SP). The System Settings\nUI is auto-generated, and is for advanced administrators. It does not perform the\nenhanced validation on the fields that the SAML Admin UI performs, so it could\nallow administrators to create invalid configurations.\n\n\\noindent\\hrulefill\n\nThis is a portal instance scoped configuration which can be managed via OSGi\nConfiguration Admin. The affected properties are those in the\n`SAMLProviderConfiguration` metatype:\n\n   - `keyStoreCredentialPassword()`\n   - `keyStoreEncryptionCredentialPassword()`\n   - `assertionSignatureRequired()`\n   - `authnRequestSignatureRequired()`\n   - `clockSkew()`\n   - `defaultAssertionLifetime()`\n   - `entityId()`\n   - `enabled()`\n   - `ldapImportEnabled`\n   - `role()`\n   - `sessionMaximumAge`\n   - `sessionTimeout()`\n   - `signAuthnRequest()`\n   - `signMetadata()`\n   - `sslRequired()`\n   - `allowShowingTheLoginPortlet()`\n\nThe SAML Admin UI remains the place for creating the portal instance scoped\nconfiguration instances.\n\nNote that there is also a system wide configuration, represented by the\n`SamlConfiguration` metatype. \n\nIf you used Liferay 6.2, please note that the following system wide properties\nwere removed:\n\n   `saml.metadata.paths` (served no purpose after removal of SP connection defaults)\n   `saml.runtime.metadata.max.refresh.delay`\n   `saml.runtime.metadata.min.refresh.delay`\n\nThe latter two properties were replaced with the single property\n`com.liferay.saml.runtime.configuration.SamlConfiguration.getMetadataRefreshInterval()`.\n\nNote also the introduction of the *SAML KeyStoreManager Implementation\nConfiguration* in *Control Panel* &rarr; *System Settings* &rarr; Security\n&rarr; SSO. The options for this configuration are explained above in the\nSetting up Liferay DXP as a SAML Identity Provider section.\n\nIn the latest version of the plugin, the `SHA256` algorithm is the default\nencryption algorithm used in the configuration and to generate keys. The default\nconfiguration tries `SHA256`, then `SHA384`, then `SHA512` before falling back to\n`SHA1`. Because `SHA1` is potentially vulnerable, you can blacklist it using this\nproperty: \n\n```properties\nblacklisted.algorithms=[\"blacklisted_algorithm_url\", \"another_blacklisted_algorithm_url\"]\n\\end{verbatim}\n\nTo blacklist \\texttt{SHA1}, therefore, you'd have this configuration:\n\n\\begin{verbatim}\nblacklisted.algorithms=[\"http://www.w3.org/2000/09/xmldsig#sha1\"]\n\\end{verbatim}\n\nPlace these in a config file with this name:\n\n\\begin{verbatim}\ncom.liferay.saml.opensaml.integration.internal.bootstrap.SecurityConfigurationBootstrap.config\n\\end{verbatim}\n\nThere's a lot more granularity in how connections are negotiated if you\nconfigure the metadata XML.\n\n\\section{Configuring Negotiation Via\nmetadata.xml}\\label{configuring-negotiation-via-metadata.xml}\n\nIf the default negotiation configuration doesn't work for you, you can\ncraft your own configuration and upload it. Before doing this, visit\nyour host's metadata URL and save a copy of the configuration in case\nyou need it later:\n\n\\begin{verbatim}\nhttp://[hostname]/c/portal/saml/metadata\n\\end{verbatim}\n\nFor example, if you're stuck connecting to a legacy IdP that only\nsupports \\texttt{SHA1}, you can upload a configuration that disables the\nother algorithms:\n\n\\begin{verbatim}\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"samlidp\">\n  <md:IDPSSODescriptor WantAuthnRequestsSigned=\"true\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n    <md:Extensions>\n      <alg:SigningMethod xmlns:alg=\"urn:oasis:names:tc:SAML:metadata:algsupport\" Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/>\n    </md:Extensions>\n    <md:KeyDescriptor use=\"signing\">\n      <ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n        <ds:X509Data>\n          <ds:X509Certificate>... omitted ...</ds:X509Certificate>\n        </ds:X509Data>\n      </ds:KeyInfo>\n    </md:KeyDescriptor>\n    <md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"http://localhost:8080/c/portal/saml/slo\"/>\n    <md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"http://localhost:8080/c/portal/saml/slo\"/>\n    <md:SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"http://localhost:8080/c/portal/saml/sso\"/>\n    <md:SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"http://localhost:8080/c/portal/saml/sso\"/>\n  </md:IDPSSODescriptor>\n</md:EntityDescriptor>\n\\end{verbatim}\n\nNotice that in the configuration above, the\n\\texttt{\\textless{}md:Extensions\\textgreater{}} block has only one\nsigning algorithm: \\texttt{SHA1}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Since the default configuration falls back to\n\\texttt{SHA1}, you shouldn't need to do this unless your legacy system\ncan't negotiate via the fallback mechanism. Also note that if you\nblacklisted \\texttt{SHA1}, this won't work. Due to\n\\href{https://en.wikipedia.org/wiki/SHA-1}{vulnerabilities in\n\\texttt{SHA1}}, it's best to avoid using it altogether if possible.\n\n\\noindent\\hrulefill\n\nIf you've changed your metadata configuration, you can go back to the\ndefault configuration if you saved it before making the change. If you\ndidn't, you can provide a URL instead of an uploaded XML file to one of\nyour peers' metadata configurations.\n\n\\chapter{NTLM Single Sign On\nAuthentication}\\label{ntlm-single-sign-on-authentication}\n\nNTLM (NT LAN Manager) is a suite of Microsoft protocols that provide\nauthentication, integrity, and confidentiality for users. Though\nMicrosoft has adopted Kerberos in modern versions of Windows server,\nNTLM is still used when authenticating to a workgroup. Liferay DXP now\nsupports NTLM v2 authentication. NTLM v2 is more secure and has a\nstronger authentication process than NTLMv1.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} NTLM authentication was deprecated in 7.0 and was\nremoved. You can still install it from Marketplace\n\\href{https://web.liferay.com/marketplace/-/mp/application/125668266}{here}\nor\n\\href{https://web.liferay.com/marketplace/-/mp/application/125668305}{here}.\n\n\\noindent\\hrulefill\n\nNote that in order to use NTLM SSO, Liferay DXP's portal instance\nauthentication type must be set to screen name.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To USE NTLM with Liferay DXP, you must configure your\nbrowser. Consult your browser vendor's documentation for the details.\n\n\\noindent\\hrulefill\n\nMost importantly, all users \\emph{must} be imported from an Active\nDirectory server. NTLM (and Kerberos) works only if the users are in the\nAD; otherwise any SSO requests initiated by Liferay DXP fail.\n\nNTLM configuration can be applied either at the system scope or at the\nscope of a portal instance. To configure the NTLM SSO module at the\nsystem scope, navigate to the Control Panel, click on\n\\emph{Configuration} → \\emph{System Settings} → \\emph{Security} →\n\\emph{SSO} → NTLM. The values configured there provide the default\nvalues for all portal instances. Enter values in the same format as you\nwould when initializing a Java primitive type with a literal value.\n\nProperty Label \\textbar{} Property Key \\textbar{} Description \\textbar{}\nType \\textbf{Enabled} \\textbar{} \\texttt{enabled} \\textbar{} Check this\nbox to enable NTLN SSO authentication. Note that NTLM will only work if\nLiferay DXP's authentication type is set to screen name. \\textbar{}\n\\texttt{boolean} \\textbf{Domain Controller} \\textbar{}\n\\texttt{domainController} \\textbar{} Enter the IP address of your domain\ncontroller. This is the server that contains the user accounts you want\nto use with Liferay DXP. \\textbar{} \\texttt{String} \\textbf{Domain\nController Name} \\textbar{} \\texttt{domainControllerName} \\textbar{}\nSpecify the domain controller NetBIOS name. \\textbar{} \\texttt{String}\n\\textbf{Domain} \\textbar{} \\texttt{domain} \\textbar{} Enter the domain /\nworkgroup name \\textbar{} \\texttt{String} \\textbf{Service Account}\n\\textbar{} \\texttt{serviceAccount} \\textbar{} You need to create a\nservice account for NTLM. This account will be a computer account, not a\nuser account. \\textbar{} \\texttt{String} \\textbf{Service Password}\n\\textbar{} \\texttt{serviceAccount} \\textbar{} Enter the password for the\nservice account. \\textbar{} \\texttt{String} \\textbf{Negotiate Flags}\n\\textbar{} \\texttt{negotiateFlags} \\textbar{} Only available at system\nlevel. Set according to the client's requested capabilities and the\nserver's ServerCapabilities. See\n\\href{http://msdn.microsoft.com/en-us/library/cc717152\\%28v=PROT.10\\%29.aspx}{here}\n\\textbar{} \\texttt{String}\n\nNote the AD's name and IP address correspond to the\n\\texttt{domainControllerName} and \\texttt{domainController} settings.\nThe \\texttt{Service\\ Account} is for the \\emph{NTLM} account (registered\nwith NTLM), not the Liferay DXP user account.\n\nTo override system defaults for a particular portal instance, navigate\nto the Control Panel, click on \\emph{Configuration} → \\emph{Instance\nSettings}, click on \\emph{Authentication} and then on \\emph{NTLM}.\n\n\\section{Summary}\\label{summary-2}\n\nNTLM authentication is often highly desirable in Intranet scenarios\nwhere the IT department has control over what software is running on\nclient devices and thus can ensure NTLM compatibility. In an Active\nDirectory based network / domain, it is hard to beat the user experience\nthat NTLM authentication can provide.\n\nPlease remember that in order to use NTLM SSO, your Liferay DXP instance\nauthentication type must be set to screen name \\emph{and} that all users\nhave been imported from your active directory. If this is not acceptable\nfor your Liferay DXP implementation, then another SSO solution (such as\nCAS) can be used as a broker between your portal and the NTLM\nauthentication process.\n\n\\chapter{OpenID Single Sign On\nAuthentication}\\label{openid-single-sign-on-authentication}\n\nOpenID is a single sign-on standard implemented by multiple vendors.\nUsers can register for an ID with the vendor they trust. The credential\nissued by that vendor can be used by all the web sites that support\nOpenID. Some high profile OpenID vendors are Google, Paypal, Amazon, and\nMicrosoft. Please see the \\href{http://www.openid.net/}{OpenID site} for\na more complete list.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} OpenID is deprecated in 7.0 and has been removed. You can\nstill install it from Marketplace\n\\href{https://web.liferay.com/marketplace/-/mp/application/125668346}{here}\nor\n\\href{https://web.liferay.com/marketplace/-/mp/application/125668379}{here}.\n\n\\noindent\\hrulefill\n\nWith OpenID, users don't have to register for a new account on every\nsite which requires an account. Users register on \\emph{one} site (the\nOpenID provider's site) and then use those credentials to authenticate\nto many web sites which support OpenID. Web site owners sometimes\nstruggle to build communities because users are reluctant to register\nfor \\emph{another} account. Supporting OpenID removes that barrier,\nmaking it easier for site owners to build their communities. All the\naccount information is kept with the OpenID provider, making it much\neasier to manage this information and keep it up to date.\n\nLiferay DXP can act as an OpenID consumer, allowing users to\nautomatically register and sign in with their OpenID accounts.\nInternally, the product uses\n\\href{https://github.com/jbufu/openid4java}{OpenID4Java} to implement\nthe feature.\n\n\\section{OpenID at the System Scope}\\label{openid-at-the-system-scope}\n\nOpenID is enabled by default in Liferay DXP but can be disabled or\nenabled at either the system scope or portal instance scope. To\nconfigure the OpenID SSO module at the system level, navigate to the\nControl Panel and click on \\emph{Configuration} → \\emph{System Settings}\n→ \\emph{Security} → \\emph{SSO}. There's only a single configuration\nsetting. Check the \\emph{Enabled} box to enable OpenID at the system\nscope (for all portal instances), uncheck it to disable it at the system\nscope.\n\n\\section{OpenID at the Instance\nScope}\\label{openid-at-the-instance-scope}\n\nTo configure the OpenID SSO module at the portal instance scope,\nnavigate to the Control Panel and click on \\emph{Configuration} →\n\\emph{Instance Settings}, then on \\emph{Authentication} → \\emph{OpenID}.\nThere's only a single configuration setting. Check the \\emph{Enabled}\nbox to enable OpenID for the current portal instance, or uncheck it to\ndisable it for the current portal instance.\n\nRegardless of whether OpenID is enabled at the System or Instance scope,\nusers can see the OpenID icon when they sign into Liferay DXP. Click\n\\emph{Sign In}. The OpenID icon is displayed at the lower left.\n\n\\chapter{Authenticating with\nKerberos}\\label{authenticating-with-kerberos}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nYou can use Kerberos to authenticate Microsoft Windows ™ accounts with\nLiferay DXP. This is done completely through configuration by using a\ncombination of Liferay DXP's LDAP support and a web server that supports\nthe Kerberos protocol.\n\nNote that this configuration is preferred above\n\\href{/docs/7-1/deploy/-/knowledge_base/d/ntlm-single-sign-on-authentication}{NTLM}\nbecause security vulnerabilities persist.\n\nWhile it's beyond the scope of this article to explain how to set up\nKerberos and Active Directory on a Windows ™ server, we can describe the\nminimum prerequisites for setting up Liferay authentication:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  A Windows ™ server with Active Directory and DNS set up so the AD\n  server and Liferay DXP can resolve each other on the network. In other\n  words, they must be able to ping each other \\emph{by name}.\n\\item\n  An administrative user in AD Liferay DXP can use to bind to AD.\n\\item\n  A Kerberos keytab file exported via the \\texttt{ktpass} command\n  containing the cryptographic information the Liferay DXP server needs\n  to bind to AD.\n\\item\n  A web server in front of Liferay DXP that supports Kerberos, such as\n  Apache, NGNIX, or IIS. The web server must also support injecting a\n  header to be used as a token in the Liferay DXP configuration (see\n  below).\n\\item\n  Of course, you need a Liferay DXP installation that can also resolve\n  by name the other servers. It should never run on the Active Directory\n  server.\n\\end{enumerate}\n\nWhen you have all of these prerequisites in place, you're ready to\nconfigure Kerberos authentication.\n\n\\section{How Kerberos Authentication\nWorks}\\label{how-kerberos-authentication-works}\n\nFrom the prerequisites, you may be able to guess that there are several\nmoving parts to how SSO works with Kerberos.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/kerberos.png}}\n\\caption{Kerberos authentication requires a web server in front of your\nLiferay DXP server.}\n\\end{figure}\n\nFirst, a properly configured web browser sends a negotiate request using\nencrypted Windows user data. To configure this, the browser must\nrecognize the site as a trusted site (explained below). The web server's\nKerberos module uses the keytab file to bind over the Kerberos protocol\nto AD and verify the user information. If all is okay, the AD server\nconfirms the connection with a valid response.\n\nThe web server you choose must support both the Kerberos protocol and\nthe injection of a custom header into the request that Liferay DXP can\nlater read. When the web server forwards the request to Liferay DXP, it\nreads the header to obtain the user data and authenticate the user.\n\nNext, you'll learn how to get all of this working.\n\n\\section{Configuring Kerberos\nAuthentication}\\label{configuring-kerberos-authentication}\n\nThere are four components to configure: a user keytab from Active\nDirectory, a web server in front of your application server, Liferay\nDXP, and your Windows™ clients.\n\n\\section{Creating the User Keytab}\\label{creating-the-user-keytab}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a user so Liferay DXP can bind to Active Directory.\n\\item\n  Generate a Kerberos keytab file using \\texttt{ktpass}:\n\n\\begin{verbatim}\nktpass -princ HTTP/[web server host name]@[domain] -mapuser [user name]@[domain] -crypto ALL -ptype KRB5_NT_PRINCIPAL -pass [password] -out c:\\kerberos.keytab\n\\end{verbatim}\n\n  For example:\n\n\\begin{verbatim}\nktpass -princ HTTP/mywebserver.intdomain.local@INTDOMAIN.LOCAL -mapuser Marta@INTDOMAIN.LOCAL -crypto ALL -ptype KRB5_NT_PRINCIPAL -pass password-for-Marta -out c:\\kerberos.keytab\n\\end{verbatim}\n\\item\n  Ensure that the AD domain controller and the web server can see each\n  other on the network via DNS configuration or \\texttt{hosts} file.\n\\end{enumerate}\n\n\\section{Configuring Your Web Server}\\label{configuring-your-web-server}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Configure Kerberos authentication. On Linux, this involves installing\n  \\texttt{krb5} and configuring it to match your realm that's already\n  configured for Active Directory. The example domain for the user\n  configured in step two above would look like this:\n\n\\begin{verbatim}\n[libdefaults]\n    default_realm = INTDOMAIN.LOCAL\n\n[domain_realm]\n    mywebserver.intdomain.local = INTDOMAIN.LOCAL\n    intdomain.local = INTDOMAIN.LOCAL\n    .intdomain.local = INTDOMAIN.LOCAL\n\n[realms]\nINTDOMAIN.LOCAL = {\n    admin_server = winserver.intdomain.local\n    kdc = winserver.intdomain.local\n}\n\\end{verbatim}\n\\item\n  Copy the keytab file you generated on your AD server to your web\n  server.\n\\item\n  Configure your web server, making sure you set the correct server\n  name, Kerberos service name, Kerberos authentication realms, and the\n  path to the keytab file. For example, if you're using the Apache HTTP\n  server, the configuration might look like this:\n\n\\begin{verbatim}\nLoadModule headers_module /usr/lib/apache2/modules/mod_headers.so\nLoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so\nLoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so\nLoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so\nLoadModule proxy_ajp_module /usr/lib/apache2/modules/mod_proxy_ajp.so\nLoadModule auth_kerb_module /usr/lib/apache2/modules/mod_auth_kerb.so\n\n<VirtualHost *:10080>\n    <Proxy *>\n        Order deny,allow\n        Allow from all\n    </Proxy>\n    ProxyRequests     Off\n    ProxyPreserveHost On\n    ProxyPass / ajp://localhost:8009/\n    ProxyPassReverse / ajp://localhost:8009/\n    ServerName mywebserver.intdomain.local\n    <Location />\n                Order allow,deny\n                Allow from all\n                AuthType Kerberos\n                KrbServiceName HTTP/mywebserver.intdomain.local@INTDOMAIN.LOCAL\n                AuthName \"Domain login\"\n                KrbAuthRealms INTDOMAIN.LOCAL\n                Krb5KeyTab /etc/apache2/kerberos.keytab\n                require valid-user\n                KrbMethodNegotiate  On\n                KrbMethodK5Passwd   Off\n                #KrbLocalUserMapping On\n\n                # Below directives put logon name of authenticated user into http header X-User-Global-ID\n                RequestHeader unset X-User-Global-ID\n                RewriteEngine On\n                RewriteCond   %{LA-U:REMOTE_USER} (.+)\n                RewriteRule   /.* - [E=RU:%1,L,NS]\n                RequestHeader set X-User-Global-ID %{RU}e\n\n                # Remove domain suffix to get the simple logon name\n                # RequestHeader edit X-User-Global-ID \"@INTDOMAIN.LOCAL$\" \"\"\n\n    </Location>\n</VirtualHost>\nListen 10080\n\\end{verbatim}\n\n  The last line is commented out based on user preference. If you want\n  the domain removed from the user name when saved in Liferay DXP,\n  uncomment it. Otherwise, leave it commented out to store the domain\n  with the user name.\n\\end{enumerate}\n\n\\section{Connecting Liferay DXP to Active Directory over\nLDAP}\\label{connecting-liferay-dxp-to-active-directory-over-ldap}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Finally, configure Liferay DXP to access Active Directory via the LDAP\n  protocol. Change authentication to be by Screen Name by selecting it\n  in Configuration → Instance Settings → Authentication → General.\n\\item\n  Connect Liferay DXP to AD over LDAP by going to Configuration →\n  Instance Settings → Authentication → LDAP and adding an LDAP server.\n  Provide the information appropriate to your installation:\n\n  \\textbf{Base Provider URL:} Your AD server on the proper port.\n\n  \\textbf{Base DN:} Your domain configuration. The example above might\n  be \\texttt{DC=INTDOMAIN.DC=LOCAL}.\n\n  \\textbf{Principal/Credentials:} Supply the credentials for the user\n  exported to the keytab file.\n\n  \\textbf{Authentication Search Filter:} Supply the appropriate search\n  filter to return user objects. For example,\n  \\texttt{(\\&(objectCategory=person)(sAMAccountName=*))}\n\n  \\textbf{UUID:} Supply what uniquely identifies a user, such as\n  \\texttt{sAMAccountName}.\n\n  \\textbf{Screen Name:} Supply the field that should be mapped to\n  Liferay DXP's screen name field, such as \\texttt{sAMAccountName}.\n\n  \\textbf{Password:} Supply the field that contains the user's password,\n  such as \\texttt{userPassword}.\n\\item\n  Be sure to test the connection, save, and enable the configuration.\n\\item\n  Finally, configure the token for single sign-on at Configuration →\n  System Settings → Security → SSO → Token Based SSO. Make sure the User\n  Token Name matches \\emph{exactly} the token you configured in your web\n  server. Click the \\emph{Enabled} and \\emph{Import from LDAP} boxes and\n  click \\emph{Save}.\n\\end{enumerate}\n\nExcellent! You've configured your servers. All that's left is to\nconfigure your clients.\n\n\\section{Configuring your Clients}\\label{configuring-your-clients}\n\nYou must do two things: make your computer log into the domain and\nconfigure your Liferay DXP server as a trusted Internet site.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Join your computer to your domain. In keeping with the example above,\n  you'd make your computer a member of the \\texttt{INTDOMAIN.LOCAL}\n  domain.\n\\item\n  Log in as a user in that domain.\n\\item\n  Internet Explorer, Edge, and Chrome use the Windows™ settings for\n  trusted sites. If you use these browsers, go to Internet Options →\n  Security → Local Intranet Sites and add your Liferay DXP server's URL.\n  For example, add \\texttt{http://mywebserver.intdomain.local:10080}.\n\\item\n  Firefox can be configured by typing \\texttt{about:config} in its\n  address bar. Search for the below two preferences and add the Liferay\n  DXP server's URL as the value for both:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{network.negotiate-auth.delegation-uris}\n  \\item\n    \\texttt{network.negotiate-auth.trusted-uris}\n  \\end{itemize}\n\\end{enumerate}\n\nAfter configuring these things, test your configuration by accessing\nLiferay DXP through the web server's URL. Since you are already logged\ninto your client machine, you should be automatically logged into\nLiferay DXP without a user/password prompt.\n\nCongratulations on configuring Kerberos with Liferay DXP!\n\n\\chapter{Configuring CORS}\\label{configuring-cors}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nCORS stands for Cross-Origin Resource Sharing. An Origin is a web server\nat a different domain, and a Resource is some asset stored on the\nserver, like an image, PDF, or HTML file. Sometimes you must request\nresources stored on another origin. This is called a cross-origin\nrequest, and web servers have policies to allow or deny such requests.\n\nFor example, browsers themselves don't allow cross-origin AJAX-style\nrequests from scripts to help mitigate\n\\href{https://en.wikipedia.org/wiki/Cross-site_scripting}{cross-site\nscripting} attacks. These APIs follow a \\emph{same origin} policy. But\nfor certain resources, it can be convenient to allow Liferay DXP to\nserve them to different origins.\n\nFor example, if you manage images in Docs \\& Media, you may want to\nallow cross-origin requests for them. You can enable CORS for matching\nURLs in Liferay DXP or for JAX-RS application resources.\n\n\\section{Enabling CORS for Liferay DXP\nServices}\\label{enabling-cors-for-liferay-dxp-services}\n\nYou'll find the settings in Configuration → System Settings → Security →\nSecurity Tools → Portal Cross Resource Origin Sharing (CORS):\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Add} to create a configuration entry.\n\\item\n  Fill out the fields on the form. When finished, click \\emph{Save}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/CORS-portal.png}}\n\\caption{The CORS system settings provide a way to configure CORS\nheaders for Liferay services.}\n\\end{figure}\n\n\\textbf{Enabled:} Check this box to enable the entry.\n\n\\textbf{Name:} Give the configuration entry a name.\n\n\\textbf{URL Pattern:} Use the Plus button to add as many patterns as you\nneed. Define patterns that match URLs to the resources you want to\nshare. For example, if you have many attachments in the Knowledge Base\napplication, you could define this pattern:\n\n\\begin{verbatim}\n/knowledge_base/*\n\\end{verbatim}\n\nThis would define resources stored in the Knowledge Base as applicable\nto the policy you set in the response header below.\n\n\\textbf{CORS Response Headers:} Use the Plus button to add as many\nheaders as you need. Define policies for any of the\n\\href{https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers\\#CORS}{CORS\nheaders} here.\n\nYou can also use a\n\\href{/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files}{configuration\nfile} to configure CORS.\n\n\\section{Enabling CORS for JAX-RS\nApplications}\\label{enabling-cors-for-jax-rs-applications}\n\nYou'll find the settings in Configuration → System Settings → Security →\nSecurity Tools → Web Contexts Resource Origin Sharing (CORS):\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Add} to create a configuration entry.\n\\item\n  Fill out the fields on the form. When finished, click \\emph{Save}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/CORS-jax-rs.png}}\n\\caption{There's a separate system settings category for CORS web\ncontexts.}\n\\end{figure}\n\n\\textbf{Dynamic Web Context OSGi Filter:} Define an LDAP-style\n\\href{https://osgi.org/specification/osgi.cmpn/7.0.0/service.http.whiteboard.html}{filter}\nto define which JAX-RS whiteboard applications the CORS headers in this\nentry apply to. This is the default filter:\n\n\\begin{verbatim}\n(&(!(liferay.cors=false))(osgi.jaxrs.name=*))\n\\end{verbatim}\n\nIt applies CORS headers to all deployed JAX-RS whiteboard applications\nwithout a \\texttt{liferay.cors=false} property. This helps during\ndevelopment, but in production you should use the narrowest\nconfiguration possible.\n\n\\textbf{URL Pattern:} Use the Plus button to add as many patterns as you\nneed. Define patterns that match URLs to the web services you want to\naccess.\n\n\\textbf{CORS Response Headers:} Use the Plus button to add as many\nheaders as you need. Define policies for any of the\n\\href{https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers\\#CORS}{CORS\nheaders} here.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/jax-rs}{JAX-RS} developers\ncan use the \\texttt{@CORS} annotation to set policies for their deployed\napplications.\n\n\\chapter{AntiSamy}\\label{antisamy}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay DXP includes an\n\\href{https://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project}{AntiSamy}\nmodule that protects against user-entered malicious code. If your site\nallows users to post content, such as in message boards, blogs, or other\napplications, they could include malicious code either intentionally or\nunintentionally. The AntiSamy module filters HTML/CSS fragments and\nremoves suspect JavaScript code from them.\n\nThe module leverages the powerful\n\\href{https://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project}{OWASP\nAntiSamy library} to enforce a content policy that's been effective for\nthe auction site eBay. The AntiSamy module adds an OWASP AntiSamy\nimplementation to your portal's list of existing sanitizer\nimplementations. Liferay DXP uses the AntiSamy sanitizer and any\nexisting configured sanitizers to scrub user input to blogs entries,\ncalendar events, message boards posts, wiki pages, and web content\narticles.\n\nAntiSamy is enabled by default.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/antisamy.png}}\n\\caption{Liferay DXP's AntiSamy configuration options allow you to\nspecify both a blacklist and a whitelist.}\n\\end{figure}\n\n\\section{Configuring AntiSamy}\\label{configuring-antisamy}\n\nAntiSamy uses both a blacklist and a whitelist, so you can define\nsubsets of entities that should be sanitized or not sanitized. The\nwhitelist prevents content of that type from being filtered, while the\nblacklist filters content of that type.\n\nBy default, everything is sanitized except for \\texttt{JournalArticle},\n\\texttt{BlogsEntry}, and \\texttt{FragmentEntry}. The assumption is that\nusers posting these kinds of content are trusted, while users posting\nmessage boards or wiki articles may not be trusted. If this is not the\nconfiguration you want, you can change it:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Control Panel} → \\emph{System Settings} →\n  \\emph{Security Tools} → \\emph{AntiSamy Sanitizer}.\n\\item\n  Enter a package path you want to sanitize into the \\emph{Blacklist}\n  field.\n\\item\n  Use the plus (+) button to add further Blacklist fields if you need\n  them.\n\\item\n  Use the plus (+) button to add further Whitelist fields if you need\n  them.\n\\item\n  Enter a package path you don't want sanitized into a \\emph{Whitelist}\n  field.\n\\item\n  If you want to remove a package path from the configuration, click the\n  trash can icon.\n\\item\n  When finished, click \\emph{Save}.\n\\end{enumerate}\n\n\\section{Using Wildcards}\\label{using-wildcards}\n\nYou can use wildcards in the configuration. For example, if you only\nwant to sanitize message board posts and nothing else, you can\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Configure the whitelist to \\texttt{*}\n\\item\n  Configure the blacklist to \\texttt{com.liferay.message.boards.*}\n\\end{enumerate}\n\nThe whitelist and the blacklist work together. Without the blacklist,\nthe above configuration's whitelist must include every content type\nexcept \\texttt{com.liferay.message.boards}, which would be a daunting\ntask to configure.\n\nUse AntiSamy to ensure user-generated content stays safe for other users\nto view.\n\n\\chapter{OAuth 2.0}\\label{oauth-2.0}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nOAuth 2.0 is an industry-standard authorization protocol. Users can\nseamlessly share select credentials from your Liferay-based website with\nvarious clients. You've seen this before: any time you see a ``This site\nwants to access:'' button (followed by a list of things like email\naddress, friends list, etc.) from Google or Facebook, or you authorize a\nthird-party Twitter client, that's OAuth 2.0 in action. It works by\nauthorizing password-less access to portions of user-owned resources\n(such as an email address, a user profile picture, or something else\nfrom your account) and other permissioned resources.\n\nOAuth 2.0's design encrypts all authorization transport through HTTPS,\nwhich prevents data passed between the systems from being intercepted.\n\n\\section{Flow of OAuth 2.0}\\label{flow-of-oauth-2.0}\n\nOAuth 2.0 takes advantage of web standards wherever possible: transport\nis encrypted with HTTPS; tokens are implemented as HTTP headers; data is\npassed via web services.\n\nHere's how OAuth 2.0 works:\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/oauth-flow.png}}\n\\caption{OAuth 2.0 takes advantage of web standards.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  A user accesses a third-party application that supports authorization\n  via credentials from a Liferay-based website. In the application (web\n  or mobile), the user requests authorization via OAuth, sending the\n  browser or app to the Liferay-based website. When using PKCE\n  (explained below), the application also generates a code verifier and\n  sends a code challenge that is created by applying a transformation to\n  it.\n\\item\n  The user authenticates and is shown the resources the application\n  wants permission to access. When the user gives permission by clicking\n  \\emph{Allow}, Liferay generates an authorization code that's sent to\n  the application over HTTPS.\n\\item\n  The application then requests a more permanent authorization token and\n  sends the code with the request (along with the PKCE code verifier).\n\\item\n  If the authorization code matches (and the transformed PKCE code\n  verifier matches the previously sent code challenge), Liferay\n  cryptographically generates an authorization token for this user and\n  application combination. It sends the token to the application over\n  HTTPS. Initial authorization is now complete!\n\\item\n  When the application must retrieve data, it sends the token with the\n  request to prove it's authorized to have that data.\n\\item\n  Provided the token matches what Liferay has for that user and\n  application, access is granted to retrieve the data.\n\\end{enumerate}\n\nThat description throws around a lot of terms. Definitions provided\nbelow.\n\n\\section{OAuth 2.0 Terminology}\\label{oauth-2.0-terminology}\n\n\\textbf{Authentication:} Providing credentials so a system can verify\nwho you are by matching those credentials with what it has stored. OAuth\nis not an authentication protocol.\n\n\\textbf{Authorization:} Granting access to resources stored on another\nsystem. OAuth is an authorization protocol.\n\n\\textbf{Application:} Any client (web, mobile, etc.) that must be\nauthorized to have access to resources. Applications must be registered\nby administrators before users can authorize access to their resources.\n\n\\textbf{Client:} Almost synonymous with \\emph{application}, except that\napplications can have variants, such as web and mobile. These variants\nare clients.\n\n\\textbf{Client ID:} An identifier given to a client so it can be\nrecognized.\n\n\\textbf{Client Secret:} A previously agreed-upon text string that\nidentifies a client as a legitimate client.\n\n\\textbf{Access Token:} A cryptographically generated text string that\nidentifies a user/client combination for access to that User's\nresources.\n\n\\textbf{Response Type:} OAuth 2.0 supports several response types.\nPictured and described above is the most common: the authorization code.\nOther response types are \\emph{password} (logging in with a user name\nand password), and \\emph{client credentials} (headless predefined\napplication access).\n\n\\textbf{Scope:} A list of items that define what the application wants\nto access. This list is sent during the initial authorization request\n(or otherwise defaults to scopes selected in the application\nregistration) so users can grant or deny access to their resources.\n\n\\textbf{Callback URI:} Also called a Redirection Endpoint URI. After\nauthorization is complete, the authorization server (i.e., Liferay)\nsends the client to this location.\n\n\\section{Creating an Application}\\label{creating-an-application}\n\nWhen you have an application that can use OAuth 2.0 for authorization,\nyou must register that application so Liferay DXP can recognize it. Do\nthis by accessing \\emph{Control Panel} → \\emph{Configuration} →\n\\emph{OAuth2 Administration}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Add}\n  (\\pandocbounded{\\includegraphics[keepaspectratio]{./images/icon-add.png}})\n  button.\n\\item\n  Fill out the form (description below).\n\\item\n  Click \\emph{Save} to save the application.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/oauth-new-application.png}}\n\\caption{Adding an application registers it so users can authorize\naccess to their data.}\n\\end{figure}\n\n\\textbf{Name:} Give the application a recognizable title.\n\n\\textbf{Website URL:} Add a link to the application's website.\n\n\\textbf{Callback URIs:} Enter at least one (line-separated) URI where\nusers should be redirected after they authorize (or refuse to authorize)\naccess to their accounts. This should link to a handler for whichever\nAllowed Authorization Types you support (see below).\n\n\\textbf{Client Profile:} Choose a template that filters the\nauthorization types that are appropriate (secure) for that profile. For\nexample, if your application is a web application, choose \\emph{Web\nApplication}, and these authorization types are available and selected\nautomatically: Authorization Code, Client Credentials, Refresh Token,\nand Resource Owner Password Credentials. These are OAuth 2 ``flows''\ndocumented in the \\href{https://tools.ietf.org/html/rfc6749}{OAuth2 RFC\n6749 Standards Document}. If you want to select authorization types\nmanually, select \\emph{Other}.\n\n\\textbf{Allowed Authorization Types:} Select the defined OAuth 2\n\\href{https://tools.ietf.org/html/rfc6749\\#section-1.2}{protocol flows}\nyour application supports. Several common combinations are defined for\nyou in the various Client Profiles above.\n\nAfter you save the form, it reappears with additional fields:\n\n\\textbf{Client ID:} The system generates this for you; it's an\nidentifier for your application, so that Liferay DXP knows what\napplication is being authorized to access user data.\n\n\\textbf{Client Secret:} Click the \\emph{pencil} icon to generate a\nclient secret. The secret identifies the client during the authorization\nprocess (see figure 1 above). Not all client profiles require a client\nsecret, because some are incapable of keeping it secret! This is when\nthe aforementioned PKCE code challenge and verifier is needed.\n\n\\textbf{Icon:} Upload an icon that your application's users identify\nwith your application. This is displayed on the authorization screen.\n\n\\textbf{Privacy Policy URL:} Add a link to your application's privacy\npolicy.\n\n\\textbf{Token Introspection:} Allow your application to retrieve\nmetadata from the token by requesting it from Liferay DXP. This\nimplements \\href{https://tools.ietf.org/html/rfc7662}{RFC 7662}.\n\nExcellent! Now you know how to add OAuth2 authorization for your\napplication to Liferay DXP! Next, you must define scopes of user data\nthe application can access.\n\n\\chapter{OAuth2 Scopes}\\label{oauth2-scopes}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nIn OAuth 2.0, applications are granted access to limited subsets of user\ndata. These are called \\emph{scopes} (not to be confused with Liferay\nscopes). They are created in two ways:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  By administrators, by creating a Service Access Policy for the scope\n\\item\n  By developers, by creating a JAX-RS endpoint. By default, scopes are\n  generated based on the HTTP verbs supported by the JAX-RS endpoint. A\n  special annotation overrides this behavior and registers specific\n  scopes.\n\\end{enumerate}\n\n\\section{Creating a Scope for a JSONWS\nService}\\label{creating-a-scope-for-a-jsonws-service}\n\nThe most common way to create a scope is to create a\n\\href{/docs/7-2/deploy/-/knowledge_base/d/service-access-policies}{Service\nAccess Policy} prefixed with the name \\texttt{OAUTH2\\_}. This naming\nconvention causes the policy to appear in the OAuth application\nconfiguration screen as a scope.\n\nFor example, say the application needs access to a user's profile\ninformation to retrieve the email address. To grant the application\naccess to this, go to \\emph{Control Panel} → \\emph{Configuration} →\n\\emph{Service Access Policy}, and create the policy pictured below.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/oauth-service-access-policy.png}}\n\\caption{A Service Access Policy defines a scope for OAuth 2.0\napplications.}\n\\end{figure}\n\nNote that the policy is not a default policy, and that it grants access\nonly to one method in the \\texttt{UserService}. This is a JSONWS web\nservice generated by Service Builder. You can view a list of all\navailable services in your installation at this URL:\n\n\\begin{verbatim}\nhttp://[host]:[port]/api/jsonws/\n\\end{verbatim}\n\nOnce you create a policy and name it with the \\texttt{OAUTH2\\_} prefix,\nit appears in the \\emph{Scopes} tab in OAuth2 Administration.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/oauth-scopes-tab.png}}\n\\caption{Scopes named with the proper prefix appear in the Scopes tab of\nyour application configuration.}\n\\end{figure}\n\nNow you can select it and save your application.\n\n\\section{Creating the Authorization\nPage}\\label{creating-the-authorization-page}\n\nThis step is optional. Users need an interface to authorize access to\ntheir accounts, and one is provided automatically. If, however, you want\nto customize the page, you can create an authorization page in your\nSite.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{System Settings} → \\emph{Security}\n  → \\emph{OAuth2}. Click the bottom item on the left, labeled\n  \\emph{Authorize Screen}.\n\\item\n  Two defaults appear. The first is the URL to the authorize page. By\n  default, it's \\texttt{/group/guest/authorize-oauth2-application}. This\n  corresponds to the default site's URL and a page on that site called\n  \\texttt{authorize-oauth2-application}.\n\\item\n  If you have customized the name and URL of your default site, make the\n  appropriate change here so the URL matches the page you'll create in\n  that site next. Click \\emph{Save}.\n\\item\n  Go to your Site's \\emph{Build} → \\emph{Pages} screen. Click the\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/icon-add.png}}\n  button and choose \\emph{Private Page}. This forces users to log in.\n\\item\n  Choose the \\emph{Full Page Application} type.\n\\item\n  Give the page the same name you configured in step 2.\n\\item\n  Uncheck the box labeled \\emph{Add this Page to the following Menus:}.\n  You don't want this page showing up in your Site navigation.\n\\item\n  On the page that appears next, verify the Friendly URL matches the URL\n  you configured in step 2.\n\\item\n  Under \\emph{Full Page Application}, choose \\emph{OAuth2 Authorize\n  Portlet}.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nExcellent! Users can use the default or the UI of your design to go\nthrough the authorization process. Now that you have the UI and you\nunderstand scopes, it's time to make the authorization process happen in\nyour application.\n\n\\chapter{Authorizing Account Access with\nOAuth2}\\label{authorizing-account-access-with-oauth2}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nOnce you have an application registered, you can start authorizing\nusers. To do that, you must construct the URL to the authorization\nserver (Liferay DXP). The authorization server asks users to authorize\nthe requested permissions to their resources, defined as you saw in the\nprevious tutorial as scopes.\n\n\\section{Authorization Code Flow}\\label{authorization-code-flow}\n\nThe most common OAuth flow is the Authorization Code flow, used for web\napplications. The URL for this requires the following request\nparameters:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{response\\_type}\n\\item\n  \\texttt{client\\_id}\n\\end{itemize}\n\nTo construct a URL for this authorization, follow this pattern:\n\n\\begin{verbatim}\nhttps://[hostname]/o/oauth2/authorize?response_type=code&client_id=[client ID]\n\\end{verbatim}\n\nThe client ID comes from registering the application. It's automatically\ngenerated (you can change it if you edit the application).\n\nIMPORTANT: Sometimes the phrase ``web application'' is used loosely,\nimplying applications where the above URL is requested from the web\nbrowser directly. If this happens, you'd leak the client secret,\ncompromising the security of the grant flow and the application. In such\ncases, select the ``User Agent Application'' client profile instead when\nregistering your application. This makes a secure alternative available\nto your application: PKCE Extended Authorization Code flow (see below).\n\nOnce the user has authorized the requested permissions to their\nresources, the authorization server returns an authorization code to\nyour application at its registered callback URI (A.K.A. redirect URI) as\na query string parameter.\n\n\\begin{verbatim}\n[your callback URI]?code=[authorization server generated code]\n\\end{verbatim}\n\nYour application must then exchange this authorization code for an\naccess token by sending a POST request following this pattern:\n\n\\begin{verbatim}\nhttp://localhost:8080/o/oauth2/token\n\\end{verbatim}\n\nWith the following parameters in the body (encoded as\n\\texttt{application/x-www-form-urlencoded}):\n\n\\begin{verbatim}\nclient_id=[client ID]\nclient_secret=[client secret]\ngrant_type=authorization_code\ncode=[authorization server generated code]\nredirect_uri=[registered callback URI]\n\\end{verbatim}\n\nIn the body of HTTP response to this request, you receive JSON like\nthis:\n\n\\begin{verbatim}\n{\n    \"access_token\": \"[authorization server generated access token]\",\n    \"token_type\": \"Bearer\",\n    \"expires_in\": 600,\n    \"scope\": \"[the scopes that were authorized by the user]\",\n    \"refresh_token\": \"[authorization server generated refresh token]\"\n}\n\\end{verbatim}\n\nFrom this you should extract and persist the access token. If you intend\nto use the token for an indefinite amount of time (beyond 600 seconds\nfrom the above example) you also need the refresh token. This can be\nused in conjunction with the Refresh Token Flow to obtain a new access\ntoken with the same permissions, without further user authorization. The\nauthorization server only issues Refresh Tokens if your application\nregistration is registered for this flow.\n\n\\section{PKCE Extended Authorization Code\nFlow}\\label{pkce-extended-authorization-code-flow}\n\nThis flow is the same as above with the addition of the Proof Key for\nCode Exchange (PKCE). It requires another request parameter:\n\\texttt{code\\_challenge}. This flow is for clients like smartphone\napplications that may not have sole access to the URL (and thus the\nrequest parameters) redirected to by the authorization server after the\nuser authorization. It protects against a malicious application on the\nsame system authorizing itself by reading the response code. To do this,\nthe client application sends a \\emph{code challenge} with the\nauthorization request: a string it has generated and which it only\nknows. To generate this string it must first create another secret\nstring known as the \\emph{Code Verifier}, and then apply a\ntransformation to it. After authorization, the code verifier is sent\nwith the authorization code, validating the client.\n\nFor more detail on how to do this, please refer to the\n\\href{https://tools.ietf.org/html/rfc7636}{PKCE specification}.\n\nTo support this flow, you must have defined PKCE as an Allowed\nAuthorization Type when you created the application. This is part of the\nNative Application and User Agent Application client profiles. To\nrequest an authorization code using PKCE, use a URL containing the\n\\texttt{code\\_challenge} request parameter:\n\n\\begin{verbatim}\nhttps://[hostname]/o/oauth2/authorize?response_type=code&client_id=[client ID]&code_challenge=[PKCE code challenge]\n\\end{verbatim}\n\nThe rest of the process is identical to Authorization Code flow, except\nthat when making the final request to get the access token, you must\nalso provide the following parameter:\n\n\\begin{verbatim}\ncode_verifier=[Code Verifier that was transformed and sent as code_challenge previously]\n\\end{verbatim}\n\n\\section{Client Credentials and Resource Owner\nFlows}\\label{client-credentials-and-resource-owner-flows}\n\nThere are two other, less used flows. If you have a scenario where two\nservers exchange agreed upon, non user-centric data, you can bypass the\nAllow/Deny screen for users and authorize the client. This is called the\nClient Credentials flow, and you'd use this URL pattern:\n\n\\begin{verbatim}\nhttps://[hostname]/o/oauth2/token?grant_type=client_credentials&client_id=[client ID]&client_secret=[client secret]\n\\end{verbatim}\n\nA final flow, where users trust the application with their passwords is\nrare, but possible. This is called the Resource Owner Password flow, and\nits URL pattern looks like this:\n\n\\begin{verbatim}\nhttps://[hostname]/o/oauth2/token?grant_type=password&client_id=[client ID]&client_secret=[client secret]&username=[user@emailaddress.com]&password=\n\\end{verbatim}\n\nUsers are prompted for their passwords, and upon successful log in,\nreceive an authorization code.\n\n\\section{Token Use}\\label{token-use}\n\nAll flows above result in an access token that's sent by the\nauthorization server (Liferay DXP) to the client application. This token\nis sent in the response for the client application to store and send\nalong with any future request for data.\n\nFor example, say the authorization code\n\\texttt{946856e2b5ddf0928f6fc55f657bab73} was sent to the client\napplication. When the client requests data, this code must be sent in\neach request header. Using a command line HTTP client such as Curl, you\ncould send a request like this:\n\n\\begin{verbatim}\ncurl -H 'Authorization: Bearer 946856e2b5ddf0928f6fc55f657bab73' 'https://[hostname]/o/api/sample2'\n\\end{verbatim}\n\nOAuth 2.0 provides a convenient way for client applications to be\ngranted access to particular services (scopes) by users without sharing\ncredential information.\n\n\\section{Revoking Access}\\label{revoking-access}\n\nOnce access is granted, users or administrators are free to revoke\naccess whenever they wish. If this happens to a client, the token\nbecomes invalid and the client must ask the user for authorization\nagain. This puts users in control of what has access to their data, and\nthey can exercise this control at any time.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/oauth-user-apps.png}}\n\\caption{Users have complete control over what applications have access\nto their data in their account profiles.}\n\\end{figure}\n\nIn their account areas, users can click \\emph{OAuth2 Connected\nApplications} and see a list of applications they've allowed to access\ntheir accounts. From here, they can revoke access by clicking the\n\\emph{Remove Access} item in the Action menu or the \\emph{Remove Access}\nbutton in the detail screen for the application.\n\nAdministrators can view the authorizations in the Authorizations tab of\nany app in \\emph{Control Panel} → \\emph{Configuration} → \\emph{OAuth2\nAdministration}.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/oauth-revoke-access.png}}\n\\caption{All authorizations for an app appear in the Authorizations tab\nfor the app.}\n\\end{figure}\n\nClicking the \\emph{Revoke} button on any listed authorization revokes\nthat application's access to that user's account.\n\n\\section{Summary}\\label{summary-3}\n\nOAuth 2.0 provides a complete and secure authorization flow for users,\nwithout their having to share any credential information. Once\napplications are created in the system, secure tokens provide access to\nparticular scopes of information, and this access can be revoked at any\ntime, making OAuth 2.0 a convenient method for users and developers\nalike to access the information they need.\n\n\\chapter{Upgrading to 7.0}\\label{upgrading-to-7.0}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nUpgrading to 7.0 involves migrating your installation and\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver}{code\n(your theme and custom apps)} to the new version. Here you'll learn how\nto upgrade your installation.\n\nHere are the installation upgrade paths:\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 2\\tabcolsep) * \\real{0.6389}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 2\\tabcolsep) * \\real{0.3611}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nUpgrade Path\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nDescription\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nLiferay Portal 5.x and 6.0.x → Liferay Portal 6.2 → Liferay DXP 7.2 &\nSupport life ended for Liferay Portal 5.0, 5.1, 5.2, and 6.0 \\\\\nLiferay Portal 6.1.x → Liferay DXP 7.1 → @product@ 7.2 & Support life\nended for Liferay Portal 6.1 \\\\\nLiferay Portal 6.2+ → Liferay DXP 7.2 & \\\\\nLiferay DXP 7.0+ → @product@ 7.2 & \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Themes and custom apps from Liferay Portal 6.0 through\nLiferay DXP 7.1 can be upgraded directly to Liferay DXP 7.2. See the\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver}{code\nupgrade instructions} for details.\n\n\\noindent\\hrulefill\n\nHere are the upgrade steps:\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database}{prepare\na new Liferay server for data upgrade} in parallel with the steps up to\nand including the step to\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-product-data}{upgrading\nthe Liferay DXP data}.\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\{.root\\}\\href{/docs/6-2/deploy/-/knowledge_base/d/upgrading-liferay}{If\n  You're Upgrading to Liferay Portal 6.2, Follow the Liferay Portal 6.2\n  Upgrade Instructions First}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-a-sharded-environment}{If\n  You're Upgrading a Sharded Environment, Follow the Instructions for\n  Upgrading It}\n\n  Upgrading a sharded installation to 7.0 requires migrating it to as\n  many non-sharded Liferay DXP installations (servers) as you have\n  shards.\\{.summary\\}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/updating-a-cluster}{If\n  You're a Upgrading a Cluster, Read Those Instructions First}\n\n  If you're updating a cluster, read those instructions first and apply\n  them to your upgrade.\\{.summary\\}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/planning-for-deprecated-applications}{Plan\n  for Handling the Deprecated Applications}\n\n  Every application deprecation has different ramifications. Learn how\n  the deprecations might affect your site and decide how to replace the\n  functionality you use from those applications.\\{.summary\\}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/test-upgrading-a-product-backup-copy}{Test\n  Upgrading a Liferay DXP Backup Copy}\n\n  Here you'll prune a backup copy of your database and upgrade the data.\n  You'll learn how to use the upgrade tool and resolve upgrade issues.\n  The notes and scripts you assemble as you prune and upgrade the\n  database copy are invaluable for correctly and efficiently upgrading\n  the Liferay DXP database you'll use with 7.0.\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/test-upgrading-a-product-backup-copy\\#copy-the-production-installation-to-a-test-server}{Copy\n    the Production Installation to a Test Server}\n\n    You'll use the installation copy to test data changes.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/test-upgrading-a-product-backup-copy\\#copy-the-production-backup-to-the-test-database}{Copy\n    the Production Database Backup}\n\n    Copy the production backup to the test database and save the copy\n    logs for analysis.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/pruning-the-database}{Remove\n    Duplicate Web Content and Structure Field Names}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/pruning-the-database}{Find\n    and Remove Unused Objects}\n\n    You may have intermediate versions of objects (e.g.,\n    \\texttt{JournalArticle} objects) that you don't need. Remove them\n    and objects that only reference them.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/pruning-the-database\\#test-with-the-pruned-database-copy}{Test\n    Liferay DXP with its Pruned Database Copy}\n\n    Make sure Liferay DXP continues to work successfully. If it's\n    broken, start over with a fresh database backup and prune it more\n    carefully.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-your-test-server-and-database}{Install\n    the New Liferay DXP Version on a Test Server}\n\n    Install the Liferay DXP version you're upgrading to, to use its\n    upgrade tool.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/tuning-for-the-data-upgrade}{Tune\n    Your Database for the Upgrade}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-product-data}{Upgrade\n    the Liferay Data, then Return Here}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/test-upgrading-a-product-backup-copy\\#copy-the-production-backup-to-the-test-database}{If\n    the Upgrade Took too Long, Prune a Fresh Database Backup More and\n    Upgrade Its Data}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/pruning-the-database}{Test\n    the Upgraded Instance}\n\n    Make sure Liferay DXP continues to work successfully. If it's\n    broken, start over with a fresh database backup and prune it more\n    carefully.\\{.summary\\}\n  \\item\n    Checkpoint: You've pruned and upgraded your production database\n    copy. You're ready to prepare for upgrading the production database.\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database}{Prepare\n  to Upgrade the Liferay DXP Database}\n\n  Preparing for the production database upgrade involves pruning and\n  testing it, upgrading your Marketplace apps, publishing staged\n  changes, and synchronizing a complete data and configuration\n  backup.\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database\\#remove-all-unused-objects-you-identified-earlier}{Remove\n    All Noted Unused Objects}\n\n    Remove all unused objects you noted from pruning your test\n    database.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database\\#test-using-the-pruned-database}{Test\n    Liferay DXP}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database\\#upgrade-your-marketplace-apps}{Upgrade\n    Your Marketplace Apps}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database\\#publish-all-staged-changes-to-production}{Publish\n    All Staged Changes}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database\\#synchronize-a-complete-backup}{Synchronize\n    a Complete Liferay DXP Backup}\n\n    Synchronize a complete backup of your production Liferay DXP server\n    installation and pruned production database.\\{.summary\\}\n  \\item\n    Checkpoint: You're ready to prepare a 7.0 server for upgrading a\n    production database.\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade}{Prepare\n  a New Liferay DXP Server for Data Upgrade}\n\n  Set up a production server with 7.0, configured to use your document\n  repository and Liferay DXP database. You'll migrate your portal and\n  system properties too. (Note, this step can be done in parallel with\n  any of the previous steps.)\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database\\#synchronize-a-complete-backup}{Request\n    an Upgrade Patch From Liferay Support (Liferay DXP Only)}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade\\#install-liferay}{Install\n    the Liferay DXP Version You're Upgrading To}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade\\#install-the-latest-upgrade-patch-or-fix-pack-liferay-dxp-only}{Install\n    the Latest Upgrade Patch or Fix Pack (Liferay DXP Only)}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade\\#migrate-your-osgi-configurations-70}{Migrate\n    Your OSGi Configurations (Liferay DXP 7.0+)}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade\\#migrate-your-portal-properties}{Migrate\n    Your Portal Properties}\n\n    Migrate your portal properties to your new 7.0 server.\\{.summary\\}\n\n    \\begin{enumerate}\n    \\def\\labelenumiii{\\arabic{enumiii}.}\n    \\item\n      \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade\\#update-your-portal-properties}{Update\n      Your Portal Properties}\n\n      Some of the portal properties have new values or have been removed\n      or replaced. Update your properties for 7.0.\\{.summary\\}\n    \\item\n      \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade\\#convert-applicable-properties-to-osgi-configurations}{Convert\n      Applicable Properties to OSGi Configurations}\n\n      Many applications are configured using OSGi Configuration (Config\n      Admin) instead of portal properties. Convert your existing\n      properties to their OSGi Configuration replacements.\\{.summary\\}\n    \\end{enumerate}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade\\#configure-your-documents-and-media-file-store}{Configure\n    Your Documents and Media File Store}\n\n    The upgrade tool upgrades your Documents and Media file store too.\n    Update your Documents and Media file store configuration and specify\n    it for the upgrade tool.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade\\#disable-indexing}{Disable\n    Indexing}\n\n    Improve the data upgrade performance by disabling\n    indexing.\\{.summary\\}\n  \\item\n    Checkpoint: You've prepared a new Liferay DXP server for executing\n    the data upgrade\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-product-data}{Upgrade\n  the Liferay DXP data}\n\n  This section explains the data upgrade options, upgrade configuration,\n  and the upgrade process.\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/tuning-for-the-data-upgrade}{Tune\n    Your Database for the Upgrade}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-data-upgrade}{Configure\n    the Data Upgrade}\n\n    Configure the data upgrade, including the data store and whether to\n    automatically upgrade the modules.\\{.summary\\}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-core-using-the-upgrade-tool}{Upgrade\n    the Core}\n\n    \\begin{enumerate}\n    \\def\\labelenumiii{\\arabic{enumiii}.}\n    \\item\n      \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-core-using-the-upgrade-tool\\#upgrade-tool-usage}{Run\n      the Data Upgrade Tool}\n\n      Run the data upgrade tool. Resolve any core upgrade\n      issues.\\{.summary\\}\n    \\item\n      \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database\\#synchronize-a-complete-backup}{Issues\n      Upgrading to 7.0 or Lower? Restore the Database Backup}\n\n      If the issues were with upgrades to Liferay 7.0 or lower, get a\n      clean start by restoring the pruned production database\n      backup.\\{.summary\\}\n    \\item\n      \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-core-using-the-upgrade-tool}{Upgrade\n      Your Resolved Issues}\n\n      If there were issues upgrading to 7.2, resolve them and restart\n      the data upgrade tool; continue if there were no\n      issues.\\{.summary\\}\n    \\end{enumerate}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell}{Upgrade\n    the Liferay Modules}\n\n    Learn how to use Gogo Shell to upgrade the Liferay modules, if you\n    didn't upgrade them automatically with the core.\\{.summary\\}\n\n    \\begin{enumerate}\n    \\def\\labelenumiii{\\arabic{enumiii}.}\n    \\item\n      \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell\\#command-usage}{Upgrade\n      Modules that are Ready for Upgrade}\n\n      Discover which modules are ready for upgrade and upgrade\n      them.\\{.summary\\}\n    \\item\n      \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell\\#checking-upgrade-status}{Check\n      Module Upgrade Status and Resolve Any Module Upgrade Issues}\n    \\item\n      Checkpoint: You've completed upgrading the Liferay data. It's time\n      to get your server ready for production.\\{.summary\\}\n    \\end{enumerate}\n  \\end{enumerate}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/executing-post-upgrade-tasks}{Execute\n  the Post-Upgrade Tasks}\n\n  Now that your database is upgraded, clean up remnants of upgrading by\n  restoring your database optimizations, enabling and regenerating your\n  search indexes, and more.\\{.summary\\}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/executing-post-upgrade-tasks\\#tuning-your-database-for-production}{Remove\n    the Database Tuning}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/executing-post-upgrade-tasks\\#re-enabling-search-indexing-and-re-indexing-search-indexes}{Re-enable\n    and Re-Index the Search Indexes}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/executing-post-upgrade-tasks\\#enabling-web-content-view-permissions}{Update\n    Web Content Permissions (7.0 and lower)}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/planning-for-deprecated-applications}{Address\n    Any Deprecated Apps That Still Need Handling}\n  \\end{enumerate}\n\\item\n  Checkpoint: You've completed the upgrade and post-upgrade tasks\n\\end{enumerate}\n\nFollow the steps above to upgrade your Liferay DXP installation to 7.0.\nThey link upgrade topic details to help complete a safe, successful\nupgrade.\n\n\\chapter{Planning for Deprecated\nApplications}\\label{planning-for-deprecated-applications}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAs Liferay DXP evolves so do Liferay applications. They may, for\nexample, be deprecated in favor of newer and better Liferay\napplications. The deprecations might call for migrating application data\nto a new application or completely removing the application and data.\nBefore upgrading, examine the deprecations:\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/deprecated-apps-in-7-2-what-to-do}{7.2\n  deprecations}\n\\item\n  \\href{/docs/7-1/deploy/-/knowledge_base/d/deprecated-apps-in-7-1-what-to-do}{7.1\n  deprecations}\n\\end{itemize}\n\nDetermine how and when to address the deprecations in your upgrade plan.\n\n\\chapter{Test Upgrading a Liferay DXP Backup\nCopy}\\label{test-upgrading-a-liferay-dxp-backup-copy}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nBefore upgrading your production Liferay instance, you should do a trial\nrun (even multiple runs) to make sure that you upgrade successfully and\nefficiently. Here's the process:\n\n\\begin{itemize}\n\\item\n  \\hyperref[preparing-a-test-server-and-database]{Preparing a test\n  server and database}: This involves copying your current production\n  installation to a test server and copying your production data backup\n  to a test database. After you prune data from the test database (next\n  step) you'll test against it.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/pruning-the-database}{Pruning\n  the database}: Free your database of duplicate and unused objects. By\n  removing them you can reduce upgrade time and improve your server's\n  performance.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-your-test-server-and-database}{Upgrading\n  your test server and database}: First you'll optimize your database\n  for the data upgrade. Taking time to do this can save upgrade time.\n  Then you'll do an upgrade test run (or several test runs) on a the\n  pruned database copy. After going through the upgrade process,\n  resolving any issues, and testing the upgraded server successfully,\n  you can confidently upgrade your production database.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} These steps and\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade}{preparing\na new Liferay DXP server} can be done in parallel to save time.\n\n\\noindent\\hrulefill\n\nNow prepare your test environment.\n\n\\section{Preparing a Test Server and\nDatabase}\\label{preparing-a-test-server-and-database}\n\nUsing a new separate server and database let's you safely test\nupgrading.\n\n\\section{Copy the Production Installation to a Test\nServer}\\label{copy-the-production-installation-to-a-test-server}\n\nPrepare a test server to use a copy of your production installation.\nYour test server must use the same Liferay version you're using on\nproduction. Configure your server to use a new empty database for\ntesting data upgrades.\n\n\\section{Copy the Production Backup to the Test\nDatabase}\\label{copy-the-production-backup-to-the-test-database}\n\nImport data from your\n\\href{/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation}{production\ndatabase backup} to the new empty database.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Make sure to save the data import log---you'll\nexamine it in the next steps.\n\n\\noindent\\hrulefill\n\nNext you'll prune your database of unneeded data.\n\n\\chapter{Pruning the Database}\\label{pruning-the-database}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAccumulating unneeded site data is common. For example, you may have\nmany unused versions of Web Content articles or Documents and Media\nfiles. If you're done revising them and don't need the intermediate\nrevisions, you can remove them. This saves you space and upgrade time.\nHere you'll remove unneeded data and then test your server.\n\n\\section{Remove Duplicate Web Content Structure Field\nNames}\\label{remove-duplicate-web-content-structure-field-names}\n\nIf you've used Web Content Management extensively, you might have\nstructures without unique field names. Find and remove duplicate field\nnames before upgrading. If you upgraded to Liferay Portal 6.2 previously\nand skipped doing this, you'll encounter this error:\n\n\\begin{verbatim}\n19:29:35,298 ERROR [main][VerifyProcessTrackerOSGiCommands:221] com.liferay.portal.verify.VerifyException: com.liferay.dynamic.data.mapping.validator.DDMFormValidationException$MustNotDuplicateFieldName: The field name page cannot be defined more than once\ncom.liferay.portal.verify.VerifyException: com.liferay.dynamic.data.mapping.validator.DDMFormValidationException$MustNotDuplicateFieldName: The field name page cannot be defined more than once\n\\end{verbatim}\n\nIf this is the case, roll back to your previous backup of Liferay Portal\n6.2 and find and remove duplicate field names.\n\n\\section{Find and Remove Unused\nObjects}\\label{find-and-remove-unused-objects}\n\nIn the UI or using database queries, identify unused objects. Then\nremove them via Liferay's UI or using Liferay's API through the\n\\href{/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console}{script\nconsole} or a portlet you create.\n\n\\noindent\\hrulefill\n\n\\textbf{Important}: You should only use Liferay's UI or API because they\naccount for relationships between Liferay DXP objects.\n\nNever use SQL directly on your database to remove records. Your SQL\nmight miss object relationships, orphaning objects and causing\nperformance problems.\n\n\\noindent\\hrulefill\n\nHere are some common places to check for unused objects.\n\n\\section{Objects From the Large/Populated\nTables}\\label{objects-from-the-largepopulated-tables}\n\nTable rows are mapped to Liferay DXP objects. Large tables with many\nrecords might contain lots of unused objects. The greater the table size\nand the records per table, the longer upgrading takes.\n\nFinding and removing unused objects associated with such tables reduces\nupgrade times. Your data import log (from the previous step) can provide\nvaluable table information. Database engines show this information in\ndifferent ways. Your database import log might look like this:\n\n\\begin{verbatim}\nProcessing object type SCHEMA\\_EXPORT/TABLE/TABLE\\_DATA\n\nimported \"LIFERAY\".\"JOURNALARTICLE\" 13.33 GB 126687 rows\n\nimported \"LIFERAY\".\"RESOURCEPERMISSION\" 160.9 MB 1907698 rows\n\nimported \"LIFERAY\".\"PORTLETPREFERENCES\" 78.13 MB 432285 rows\n\nimported \"LIFERAY\".\"LAYOUT\" 52.05 MB 124507 rows\n\nimported \"LIFERAY\".\"ASSETENTRY\" 29.11 MB 198809 rows\n\nimported \"LIFERAY\".\"MBMESSAGE\" 24.80 MB 126185 rows\n\nimported \"LIFERAY\".\"PORTALPREFERENCES\" 4.091 MB 62202 rows\n\nimported \"LIFERAY\".\"USER\\_\" 17.32 MB 62214 rows\n\n...\n\\end{verbatim}\n\nSeveral items stand out in the example database import:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  The \\texttt{JOURNALARTICLE} table makes up 98\\% of the database size.\n\\item\n  There are many \\texttt{RESOURCEPERMISSION} records.\n\\item\n  There are many \\texttt{PORTLETPREFERENCES} records.\n\\end{itemize}\n\nSearch for unused objects associated with the tables that stand out and\nuse Liferay's API (e.g., the UI or\n\\href{/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console}{script\nconsole}) to delete the objects.\n\n\\section{Common Object Types Worth\nChecking}\\label{common-object-types-worth-checking}\n\nSome object types should be checked for unused objects. Here are some\nreasons for checking them:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Removing them frees related unused objects for removal\n\\item\n  They're version objects that aren't worth keeping\n\\end{itemize}\n\nCheck these object types:\n\n\\begin{itemize}\n\\item\n  \\textbf{Sites}: Remove sites you don't need. When you remove a site,\n  remove its related objects:\n\n  \\begin{itemize}\n  \\item\n    Layouts\n  \\item\n    Portlet preferences\n  \\item\n    File entries (document library objects)\n  \\item\n    Asset Entries\n  \\item\n    Tags\n  \\item\n    Vocabularies and categories\n  \\item\n    Expando fields and their values\n  \\item\n    \\texttt{ResourcePermission} objects\n  \\item\n    (and everything else)\n  \\end{itemize}\n\\item\n  \\textbf{Instances}: Unused instances are rare, but since they are the\n  highest object in the hierarchy, removing their objects can optimize\n  upgrades considerably:\n\n  \\begin{itemize}\n  \\item\n    Sites (and all their related content)\n  \\item\n    Users\n  \\item\n    Roles\n  \\item\n    Organizations\n  \\item\n    Global \\texttt{ResourcePermission} objects\n  \\end{itemize}\n\\item\n  \\textbf{Intermediate web content versions:} Liferay DXP generates a\n  new web content version after any modification (including\n  translations). Consider removing versions you don't need. Removing a\n  Journal Article, for example, also removes related objects such as\n  image files (\\texttt{JournalArticleImage}) that are part of the\n  content. Removing unneeded image files frees space in your database\n  and file system. For more details, see\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/example-removing-intermediate-journal-article-versions}{Example:\n  Removing Intermediate Journal Article Versions}.\n\\item\n  \\textbf{Document versions}: As with Journal Articles, if you don't\n  need intermediate document versions, delete them. This saves space\n  both in the database and on the file system, space that no longer\n  needs to be upgraded.\n\\item\n  \\textbf{Layouts:} Layouts are site pages, and they affect upgrade\n  performance because they relate to other entities such as portlet\n  preferences, permissions, assets, ratings, and more. Remove unneeded\n  layouts.\n\\item\n  \\textbf{Roles}: Remove any Roles you don't need. Deleting them also\n  deletes related \\texttt{ResourceBlockPermission} and\n  \\texttt{ResourcePermission} objects.\n\\item\n  \\textbf{Users:} If you have Users that aren't active anymore, remove\n  them.\n\\item\n  \\textbf{Vocabularies}: Remove any unused vocabularies. Note that\n  removing a vocabulary also removes its categories.\n\\item\n  \\textbf{Orphaned data}: Check for unused objects that are not\n  connected to anything. Here are some examples:\n\n  \\begin{itemize}\n  \\item\n    \\texttt{DLFileEntries} with no file system data.\n  \\item\n    \\texttt{ResourcePermission} objects associated to a Role, Layout,\n    User, portlet instance, etc. that no longer exists.\n  \\item\n    \\texttt{PortletPreference} objects associated with a portlet or\n    layout that no longer exists. This is common in environments with\n    many embedded portlets. These portlet instances have a different\n    lifecycle and aren't deleted when the portlet is removed from a\n    template.\n  \\end{itemize}\n\\end{itemize}\n\nIf you want to see an example of removing intermediate object versions,\nread\n\\href{/docs/7-2/deploy/-/knowledge_base/d/example-removing-intermediate-journal-article-versions}{Example:\nRemoving Intermediate Journal Article Versions} and then return here.\n\nNext, you'll test Liferay DXP with its pruned database.\n\n\\section{Test with the Pruned Database\nCopy}\\label{test-with-the-pruned-database-copy}\n\nFind and resolve any issues related to the objects you removed. You can\nalways restart pruning a new copy of your production database if you\ncan't resolve an issue.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} the upgrade to Liferay DXP 7.2 moves Web Content\nimages to the Document Library and then deletes their former table\n\\texttt{JournalArticleImage}. Make sure the images show in the upgraded\nWeb Content articles.\n\n\\noindent\\hrulefill\n\nOnce you've successfully tested Liferay DXP with its pruned database\ncopy, you can upgrade the database to 7.0.\n\n\\chapter{Example: Removing Intermediate Journal Article\nVersions}\\label{example-removing-intermediate-journal-article-versions}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThese instructions and code samples demonstrate removing intermediate\nJournal Article versions. In the\n\\href{/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console}{script\nconsole}, you can remove unneeded object versions by executing Java or\nGroovy code.\n\nHere are example steps for removing intermediate Journal Article\nversions:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Decide how many of the latest versions to keep. You must keep the\n  original version and the most recent version, but you may keep older\n  recent versions too. For example, you may want to keep the two latest\n  versions or just the latest.\n\\item\n  Find a method for deleting the entity versions. Liferay DXP\n  \\href{@app-ref@/apps/}{app APIs} and\n  \\href{@platform-ref@/7.2-latest/javadocs/portal-kernel/}{com.lifieray.portal.kernel\n  API} are available at \\href{@platform-ref@}{@platform-ref@}.\n\n  If it's a\n  \\href{/docs/7-2/appdev/-/knowledge_base/a/service-builder}{Service\n  Builder} entity, examine the \\texttt{delete*} methods in the entity's\n  \\texttt{*LocalServiceUtil} class. For example, this\n  \\texttt{deleteArticle} in\n  \\href{@app-ref@/web-experience/latest/javadocs/com/liferay/journal/service/JournalArticleLocalServiceUtil.html\\#deleteArticle-long-java.lang.String-double-java.lang.String-com.liferay.portal.kernel.service.ServiceContext-}{\\texttt{JournalArticleLocalServiceUtil}}\n  deletes a Journal Article version:\n\n\\begin{verbatim}\ndeleteArticle(long groupId, java.lang.String articleId, double version, \n    java.lang.String articleURL, \n    com.liferay.portal.kernel.service.ServiceContext serviceContext)\n\\end{verbatim}\n\\item\n  Aggregate the entity versions to delete and the information required\n  to delete them. For example, get all the Journal Article versions in\n  range that match your removal criteria and associate their entity IDs\n  and group IDs with them---the \\texttt{deleteArticle} method requires\n  the entity ID and group ID.\n\n  The entity object (e.g., \\texttt{JournalArticle}) typically has a\n  version field. \\texttt{JournalArticleResource} has each Journal\n  Article's article ID (the entity's ID) and group ID.\n  \\texttt{JournalArticleResource} is our key to getting each\n  \\texttt{JournalArticle}, which can have multiple versions. Here are\n  steps for identifying the Journal Article versions to delete:\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\tightlist\n  \\item\n    Get all the \\texttt{JournalArticleResource} objects.\n  \\end{enumerate}\n\n\\begin{verbatim}\nList<JournalArticleResource> journalArticleResources = \n    JournalArticleLocalServiceUtil.getJournalArticleResources(start, end);\n\\end{verbatim}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\setcounter{enumii}{1}\n  \\item\n    Get each Journal Article version's workflow status via the\n    \\texttt{JournalArticle} object associated with each\n    \\texttt{JournalArticleResource}. Dynamic Query is an efficient way\n    to get exactly the data you want (and nothing more) from each\n    object.\n  \\end{enumerate}\n\n\\begin{verbatim}\nfor (JournalArticleResource\n    journalArticeResource : journalArticleResources) {\n\n    List<Double> journalArticlesVersionsToDelete =\n        new ArrayList<Double>();\n\n    DynamicQuery dq =\n        DynamicQueryFactoryUtil.forClass(JournalArticle.class)\n            .setProjection(ProjectionFactoryUtil.projectionList()\n                .add(ProjectionFactoryUtil.property(\"id\"))\n                .add(ProjectionFactoryUtil.property(\"version\"))\n                .add(ProjectionFactoryUtil.property(\"status\")))\n            .add(PropertyFactoryUtil.forName(\"groupId\")\n                .eq(journalArticeResource.getGroupId()))\n            .add(PropertyFactoryUtil.forName(\"articleId\")\n                .eq(journalArticeResource.getArticleId()))\n            .addOrder(OrderFactoryUtil.asc(\"version\"));\n\n    List<Object[]> result =\n        JournalArticleLocalServiceUtil.dynamicQuery(dq);\n\n    // See the next step for the sample code that goes here\n}\n\\end{verbatim}\n\n  \\begin{enumerate}\n  \\def\\labelenumii{\\arabic{enumii}.}\n  \\setcounter{enumii}{2}\n  \\tightlist\n  \\item\n    For each \\texttt{JournalArticleResource} (there's one for each\n    Journal Article entity), build a list of intermediate versions in\n    range of the first or latest versions you want to keep and whose\n    status qualifies them for deletion. For example, you may want to\n    delete intermediate article versions that are approved or expired\n    (i.e.,\n    \\href{@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/workflow/WorkflowConstants.html}{WorkflowConstants.STATUS\\_APPROVED\n    or WorkflowConstants.STATUS\\_EXPIRED}). The\n    \\texttt{MIN\\_NUMBER\\_FIRST\\_VERSIONS\\_KEPT} and\n    \\texttt{MIN\\_NUMBER\\_LATEST\\_VERSIONS\\_KEPT} variables here mark the\n    minimum and maximum number of first (oldest) and latest (newest)\n    versions to keep.\n  \\end{enumerate}\n\n\\begin{verbatim}\nList<Double> journalArticlesVersionsToDelete =\n    new ArrayList<Double>();\n\nfor (int i=0; i < result.size(); i++) {\n    long id = (long) result.get(i)[0];\n    double version = (double) result.get(i)[1];\n    int status = (int) result.get(i)[2];\n\n    if ((status == WorkflowConstants.STATUS_APPROVED) || (status == WorkflowConstants.STATUS_EXPIRED) {\n\n        if (i < MIN_NUMBER_FIRST_VERSIONS_KEPT) {\n            continue;\n        }\n\n        if (i >= (result.size() -\n            MIN_NUMBER_LATEST_VERSIONS_KEPT)) {\n            continue;\n        }\n\n        journalArticlesVersionsToDelete.add(version);\n    }\n}\n\n// See the next step for the sample code that goes here\n\\end{verbatim}\n\\item\n  Lastly, delete each Journal Article matching the versions you\n  aggregated.\n\n\\begin{verbatim}\nfor (double version : journalArticlesVersionsToDelete) {\n{\n    JournalArticleLocalServiceUtil.deleteArticle(journalArticeResource.getGroupId(),\n        journalArticeResource.getArticleId(), \n        journalArticlesVersionsToDelete(i), null, null);\n}\n\\end{verbatim}\n\\end{enumerate}\n\nYou can write similar code to remove intermediate versions of other\nentities.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} Print the version (and any other information of interest)\nof each object you're removing. You can also comment out the object\ndeletion call and read the printout of versions to be removed as a test\nbefore committing to deleting them.\n\n\\noindent\\hrulefill\n\nAfter you've pruned your database, test it with Liferay DXP.\n\n\\chapter{Upgrading Your Test Server and\nDatabase}\\label{upgrading-your-test-server-and-database}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAfter you've\n\\href{/docs/7-2/deploy/-/knowledge_base/d/pruning-the-database}{pruned\nyour database and tested it successfully}, it's ready for upgrade. Here\nyou'll install 7.0 and migrate your current installation files to it and\nupgrade them. Then you'll optimize your database for the upgrade and\nupgrade your data. Lastly, you'll test this upgraded test environment.\nYou may run into issues that require you to start again with backup of\nyour pruned database. After you're satisfied with the test upgrade, you\ncan prepare for upgrading production. Start with preparing 7.0 on a test\nserver.\n\n\\section{Install Liferay on a Test Server and Configure It to Use the\nPruned\nDatabase}\\label{install-liferay-on-a-test-server-and-configure-it-to-use-the-pruned-database}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade}{Prepare\na new test server with 7.0}. Configure it to use the pruned database\ncopy---keep the original backup in case you want to restart test\nupgrades on a copy of it. You'll use the new test server's Liferay\nupgrade tool next.\n\n\\section{Tune Your Database for the\nUpgrade}\\label{tune-your-database-for-the-upgrade}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/tuning-for-the-data-upgrade}{Tune\nyour database for the upgrade}.\n\n\\section{Upgrade the Database}\\label{upgrade-the-database}\n\nUpgrade the database to 7.0 (see\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-product-data}{Upgrade\nthe Database}); then return here.\n\nIf the upgrade took too long, search the upgrade log to identify more\nunused objects. Then retry these steps with a fresh copy of the\nproduction database.\n\n\\section{Test the Upgraded Portal and Resolve Any\nIssues}\\label{test-the-upgraded-portal-and-resolve-any-issues}\n\nTest this upgraded 7.0 instance and resolve any issues. If you can't\nresolve an issue, retry these steps with a fresh copy of the production\ndatabase.\n\n\\section{Checkpoint: You've Pruned and Upgraded a Production Database\nCopy}\\label{checkpoint-youve-pruned-and-upgraded-a-production-database-copy}\n\nBy removing unused objects from Liferay DXP in your test environment,\nyou've made upgrading feasible to do in production. You identified\nunused objects, documented/scripted removing them, and successfully\nupgraded the Liferay DXP database copy.\n\nIt's time to prepare your production environment for upgrading.\n\n\\chapter{Preparing to Upgrade the Liferay DXP\nDatabase}\\label{preparing-to-upgrade-the-liferay-dxp-database}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAfter testing the upgrade on a copy of your production database, you can\napply what you learned to your production database.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} This step and\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade}{preparing\na new Liferay DXP server} can be done in parallel to save time.\n\n\\noindent\\hrulefill\n\n\\section{Remove All Unused Objects You Identified\nEarlier}\\label{remove-all-unused-objects-you-identified-earlier}\n\nPreviously you identified and removed unused objects from a copy of your\nLiferay DXP production database backup. In the same way (in the script\nconsole or UI) you removed the unused objects from the backup, remove\nthem from your pre-upgrade production database.\n\n\\section{Test Using the Pruned\nDatabase}\\label{test-using-the-pruned-database}\n\nFind and resolve any issues related to the objects you removed. By\nremoving the objects from production and testing your changes before\nupgrading, you can more easily troubleshoot issues, knowing that they're\nnot related to upgrade processes.\n\n\\section{Upgrade Your Marketplace\nApps}\\label{upgrade-your-marketplace-apps}\n\nUpgrade each Marketplace app (Kaleo, Calendar, Notifications, etc.) that\nyou're using to its latest version for your Liferay DXP installation.\nBefore proceeding with the upgrade, troubleshoot any issues regarding\nthese apps.\n\n\\section{Publish all Staged Changes to\nProduction}\\label{publish-all-staged-changes-to-production}\n\nIf you have\n\\href{/docs/7-2/user/-/knowledge_base/u/enabling-staging}{local/remote\nstaging enabled} and have content or data saved on the staged site,\n\\href{/docs/7-2/user/-/knowledge_base/u/publishing-staged-content-efficiently}{publish}\nit to the live site. If you skip this step, you must run a full publish\n(or manually publish changes) after the upgrade, since the system won't\nknow what content changed since the last publishing date.\n\n\\section{Synchronize a Complete\nBackup}\\label{synchronize-a-complete-backup}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation}{Completely\nback up your Liferay DXP installation, pruned production database, and\ndocument repository}.\n\nIt's time to prepare a new Liferay DXP server.\n\n\\chapter{Preparing a New Liferay DXP Server for Data\nUpgrade}\\label{preparing-a-new-liferay-dxp-server-for-data-upgrade}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nTo upgrade your Liferay DXP database, prepare a new server for hosting\n7.0. You'll use this server to run the database upgrade and run 7.0.\nThen you can run your production server while you're configuring a new\nserver to host 7.0 exactly the way you want.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} these steps can be done in parallel with any of the\nupgrade preparation steps: planning for deprecated apps, testing\nupgrades on a Liferay DXP backup copy, or preparing to upgrade the\n@product@ database.\n\n\\noindent\\hrulefill\n\nGet the latest fixes for 7.0 by requesting an upgrade patch.\n\n\\section{Request an Upgrade Patch from Liferay Support (Liferay DXP\nonly)}\\label{request-an-upgrade-patch-from-liferay-support-liferay-dxp-only}\n\nAn \\emph{upgrade patch} contains the latest fix pack and hot fixes\nplanned for the next service pack. Upgrade patches provide the latest\nfixes available for your data upgrade.\n\n\\section{Install Liferay}\\label{install-liferay}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/deploying-product}{Install\nLiferay DXP on your application server} or\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-product}{use\nLiferay DXP bundled with your application server of choice}.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Do not start your application server. It's not ready\nto start until after the Liferay DXP database upgrade.\n\n\\noindent\\hrulefill\n\n\\section{Install the Latest Upgrade Patch or Fix Pack (Liferay DXP\nonly)}\\label{install-the-latest-upgrade-patch-or-fix-pack-liferay-dxp-only}\n\nInstall the upgrade patch (if you requested it from Liferay Support) or\nthe\n\\href{https://help.liferay.com/hc/en-us/articles/360028810452-Patching-Liferay-DXP}{latest\nFix Pack}.\n\n\\section{Migrate Your OSGi Configurations\n(7.0+)}\\label{migrate-your-osgi-configurations-7.0}\n\nCopy your\n\\href{/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files}{OSGi\nconfiguration files} (i.e., \\texttt{.config} files) to your new server's\n\\texttt{{[}Liferay\\ Home{]}/osgi/configs} folder.\n\n\\section{Migrate Your Portal\nProperties}\\label{migrate-your-portal-properties}\n\nIt is likely that you have overridden portal properties to customize\nyour installation. If so, you must update the properties files (e.g.,\n\\texttt{portal-setup-wizard.properties} and\n\\texttt{portal-ext.properties}) to 7.0. For features that use OSGi\nConfig Admin, you must convert your properties to OSGi configurations.\nAs you do this, you must account for property changes in all versions of\nLiferay DXP since your current version up to and including 7.0. Start\nwith updating your portal properties.\n\n\\section{Update Your Portal\nProperties}\\label{update-your-portal-properties}\n\nIf you're coming from a version prior to Liferay Portal 6.2, start with\nthese property-related updates:\n\n\\begin{itemize}\n\\item\n  If you're on Liferay Portal 6.1,\n  \\href{/docs/6-2/deploy/-/knowledge_base/d/upgrading-liferay\\#review-the-liferay-6}{adapt\n  your properties to the new defaults that Liferay Portal 6.2\n  introduced}.\n\\item\n  If you're on Liferay 6.0.12,\n  \\href{/docs/6-2/deploy/-/knowledge_base/d/upgrading-liferay\\#migrate-your-image-gallery-images}{migrate\n  the Image Gallery}.\n\\item\n  If you have a sharded environment,\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-a-sharded-environment}{configure\n  your upgrade to generate a non-sharded environment}.\n\\item\n  Liferay's image sprite framework is deprecated as of 7.2 and is\n  disabled by default. The framework requires scanning plugins for image\n  sprites. If you don't use the framework, there's no need for it to\n  scan for images sprites. If you use the framework, enable it by\n  overriding the default \\texttt{sprite.enabled} portal property (new in\n  7.2) value with the following setting in a\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}}\n  file:\n\n\\begin{verbatim}\nsprite.enabled=true\n\\end{verbatim}\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can build image sprites using any framework you like\nand deploy them in your plugins.\n\n\\noindent\\hrulefill\n\nWhen a new version of Liferay DXP is released, there are often changes\nto default settings, and this release is no different. If you rely on\nthe defaults from your old version, you should review the changes and\ndecide to keep the defaults from your old version or accept the defaults\nof the new.\n\nBecause no existing properties changed from 7.1 to 7.2, here's a list of\nthe 6.2 properties that have changed in 7.2:\n\n\\begin{verbatim}\nusers.image.check.token=false\norganizations.types=regular-organization,location\norganizations.rootable[regular-organization]=true\norganizations.children.types[regular-organization]=regular-organization,location\norganizations.country.enabled[regular-organization]=false\norganizations.country.required[regular-organization]=false\norganizations.rootable[location]=false\norganizations.country.enabled[location]=true\norganizations.country.required[location]=true\nlayout.set.prototype.propagate.logo=true\neditor.wysiwyg.portal-web.docroot.html.taglib.ui.discussion.jsp=simple\nweb.server.servlet.check.image.gallery=true\nblogs.trackback.enabled=true\ndiscussion.comments.format=bbcode\ndiscussion.max.comments=0\ndl.file.entry.thumbnail.max.height=128\ndl.file.entry.thumbnail.max.width=128\n\\end{verbatim}\n\nThis property was removed:\n\n\\begin{verbatim}\norganizations.children.types[location]\n\\end{verbatim}\n\nThe latest\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html}{portal\nproperties reference} provides property details and examples. Some\nproperties are replaced by OSGi configurations.\n\n\\section{Convert Applicable Properties to OSGi\nConfigurations}\\label{convert-applicable-properties-to-osgi-configurations}\n\nProperties in modularized features have changed and must now be deployed\nseparately in\n\\href{/docs/7-2/user/-/knowledge_base/u/system-settings\\#exporting-and-importing-configurations}{OSGi\nconfiguration files} (OSGi Config Admin).\n\nUse the\n\\href{/docs/7-2/reference/-/knowledge_base/r/blade-cli}{\\texttt{blade\\ upgradeProps}}\ncommand to scan your \\texttt{portal-ext.properties} file to discover\nwhich properties are now set via OSGi Config Admin. You can also check\nthe upgrade log from previous attempts for traces like these:\n\n\\begin{verbatim}\n2019-03-09 17:05:17.678 ERROR [main][VerifyProperties:161] Portal property \"layout.first.pageable[link_to_layout]\" is obsolete\n2019-03-09 17:05:17.679 ERROR [main][VerifyProperties:136] Portal property \"journal.article.check.interval\" was modularized to com.liferay.journal.web as \"check.interval\"\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} The Control Panel's \\emph{Configuration → System Settings}\nscreens are the most accurate way to create \\texttt{.config} files. Use\nthem to\n\\href{/docs/7-2/user/-/knowledge_base/u/system-settings\\#exporting-and-importing-configurations}{export\na screen's configuration} to a \\texttt{.config} file.\n\n\\noindent\\hrulefill\n\n\\section{Update Your Database Driver}\\label{update-your-database-driver}\n\nInstall the recommended database driver and update your database\nconnection driver specified in your \\texttt{portal-ext.properties}. See\nthe\n\\href{/docs/7-2/deploy/-/knowledge_base/d/database-templates}{Database\nTemplates}.\n\n\\section{Configure Your Documents and Media File\nStore}\\label{configure-your-documents-and-media-file-store}\n\nGeneral document store configuration (e.g.,\n\\texttt{dl.store.impl={[}File\\ Store\\ Impl\\ Class{]}}) continues to be\ndone using \\texttt{portal-ext.properties}. But here's what's changed for\ndocument storage:\n\n\\begin{itemize}\n\\item\n  Store implementation class package names changed from\n  \\texttt{com.liferay.portlet.documentlibrary.store.*} in Liferay Portal\n  6.2 to \\texttt{com.liferay.portal.store.*} in Liferay DXP 7.0+. Make\n  sure your \\texttt{portal-ext.properties} file sets\n  \\texttt{dl.store.impl} in one of these ways:\n\n\\begin{verbatim}\ndl.store.impl=com.liferay.portal.store.file.system.FileSystemStore\ndl.store.impl=com.liferay.portal.store.db.DBStore\ndl.store.impl=com.liferay.portal.store.file.system.AdvancedFileSystemStore\ndl.store.impl=com.liferay.portal.store.s3.S3Store\n\\end{verbatim}\n\\item\n  JCR Store was deprecated in Liferay DXP 7.0. The\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration}{Document\n  Repository Configuration} documentation describes other store options.\n  \\href{/docs/7-2/user/-/knowledge_base/u/server-administration}{Migrate\n  to a supported document store} before upgrading your data.\n\\item\n  CMIS Store was deprecated since 7.0.10 Fix Pack 14 and was removed in\n  Liferay DXP 7.2. The\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration}{Document\n  Repository Configuration} documentation describes other store options.\n  \\href{/docs/7-2/user/-/knowledge_base/u/server-administration}{Migrate\n  to a supported document store} before upgrading your data.\n\\item\n  Since Liferay DXP 7.0, document store type-specific configuration\n  (e.g., specific to Simple File Store, Advanced File Store, S3, etc.)\n  is done in the Control Panel at \\emph{Configuration → System Settings\n  → File Storage} or using OSGi configuration files (\\texttt{.config}\n  files). Type specific configuration is no longer done using\n  \\texttt{portal-ext.properties}.\n\\end{itemize}\n\nFor example, these steps to create a \\texttt{.config} file specifying a\nroot file location for a Simple File Store or Advanced File Store:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a \\texttt{.config} file named after your store implementation\n  class.\n\n  Simple File Store:\n  \\texttt{com.liferay.portal.store.file.system.configuration.FileSystemStoreConfiguration.config}\n\n  Advanced File Store:\n  \\texttt{com.liferay.portal.store.file.system.configuration.AdvancedFileSystemStoreConfiguration.config}\n\\item\n  Set the following \\texttt{rootDir} property and replace\n  \\texttt{\\{document\\_library\\_path\\}} with your file store's path.\n\n\\begin{verbatim}\nrootDir=\"{document_library_path}\"\n\\end{verbatim}\n\\item\n  Copy the \\texttt{.config} file to your\n  \\texttt{{[}Liferay\\ Home{]}/osgi/configs} folder.\n\\end{enumerate}\n\nThe\n\\href{/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration}{Document\nRepository Configuration} provides more document store configuration\ndetails.\n\n\\section{Configure Kerberos in place of\nNTLM}\\label{configure-kerberos-in-place-of-ntlm}\n\nIf you're using NTLM to authenticate Microsoft Windows ™ accounts with\nLiferay DXP, switch to using\n\\href{/docs/7-2/deploy/-/knowledge_base/d/authenticating-with-kerberos}{Kerberos}.\nSecurity vulnerabilities persist with NTLM. NTLM has been deprecated and\nremoved from the bundle, but you can still\n\\href{https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/portal-security-sso-ntlm}{build\nand deploy the module}.\n\n\\section{Disable Indexing}\\label{disable-indexing}\n\nBefore starting the upgrade process in your new installation, you must\ndisable indexing to prevent upgrade process performance issues that\narise when the indexer attempts to re-index content.\n\nTo disable indexing, create a file called\n\\texttt{com.liferay.portal.search.configuration.IndexStatusManagerConfiguration.config}\nin your \\texttt{{[}Liferay\\ Home{]}/osgi/configs} folder and add the\nfollowing content:\n\n\\begin{verbatim}\nindexReadOnly=\"true\"\n\\end{verbatim}\n\nAfter you complete the upgrade, re-enable indexing by removing the\n\\texttt{.config} file or setting \\texttt{indexReadOnly=\"false\"}.\n\nYour new 7.0 server is ready for upgrading your database.\n\n\\chapter{Upgrading the Liferay DXP\nData}\\label{upgrading-the-liferay-dxp-data}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nNow you're ready to upgrade the Liferay DXP data. The upgrade processes\nupdate the database schema for the core and your installed modules.\nVerification processes test the upgrade. Configured verifications for\nthe core and modules run afterwards, but can be run manually too.\n\nHere are the ways to upgrade:\n\n\\begin{itemize}\n\\item\n  \\textbf{Upgrade everything in one shot}: Use the upgrade tool to\n  upgrade the core and all the modules.\n\\item\n  \\textbf{Upgrade the core and the modules separately}: Use the upgrade\n  tool (recommended) or\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell}{Gogo\n  shell} to upgrade the core. Then use Gogo shell to upgrade each\n  module.\n\\end{itemize}\n\nIf you are upgrading from Liferay Portal 6.2 or earlier, use the upgrade\ntool to upgrade everything. It's the easiest, most comprehensive way to\nupgrade from those versions. Since version 7.0, however, Liferay DXP's\nmodular framework lets you upgrade modules---even the\ncore---individually. A helpful practice for large databases is to focus\nfirst on upgrading the core and your most important modules; then back\nup your database before continuing upgrades. Upgrading is a flexible\nprocess that adjusts to your preferences.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Liferay enterprise subscribers can use the upgrade tool\nto execute upgrades for fix packs. Since Liferay DXP 7.1, a fix pack's\nmicro upgrade processes in the core (database schema micro version\nchanges) are not mandatory. This means you can install a fix pack (i.e.,\ncore code) without having to execute the database schema micro version\nchanges. You can execute micro version changes when you want, even\noutside of major or minor version upgrades. Before using the upgrade\ntool to execute a fix pack's micro upgrade process, however, you must\nshut down the server, install the fix pack, and\n\\href{/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation}{back\nup the Liferay DXP database, installation, and Document Library store}.\n\nModule micro database schema version changes in fix packs execute\nautomatically on server startup unless the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-data-upgrade}{\\texttt{autoUpgrade}\nsetting} is \\texttt{false} (the default is \\texttt{true}).\n\n\\chapter{Tuning for the Data Upgrade}\\label{tuning-for-the-data-upgrade}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nUpgrading impacts the database differently from daily running in\nproduction. Because of this, you should tune your database for the\nupgrade process before you run it, and then re-apply your production\nsettings after the upgrade completes.\n\n\\begin{itemize}\n\\item\n  Data upgrades execute many more update statements (\\texttt{INSERT},\n  \\texttt{UPDATE}, and \\texttt{DELETE}) and less \\texttt{SELECT}\n  statements than production instances. When upgrading, tune your\n  database for executing updates.\n\\item\n  Data upgrades should be done in safe environments completely separate\n  from production servers and should use database backup copies. If\n  upgrade errors occur or you make mistakes, they don't impact\n  production, and you can always restart using your database backup\n  copy.\n\\end{itemize}\n\nThe data upgrade tuning instructions given here are a starting point for\ntuning your Liferay DXP data upgrade. They account for data upgrade\nactivities and a safe data upgrade environment:\n\n\\begin{itemize}\n\\item\n  Deactivate data integrity measures that impact performance. Restore\n  the backup if failures occur.\n\\item\n  Make commit-related transaction I/O operations asynchronous.\n\\item\n  Increase the interval to flush commits to disk.\n\\item\n  Minimize transaction logging.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} These options worked well for us on specific versions of\neach database. Please consult your database vendor's documentation for\ninformation on how to optimize executing updates on your specific\ndatabase version.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Test your database configuration to determine tuning\nthat's best for your system, and consult your DBA as appropriate.\n\\textbf{Never} use database upgrade configurations in production. Always\nrestore your production database settings before starting your Liferay\nDXP server for production use with the database.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Some database properties and configurations are global\nand affect schemas in the same database.\n\n\\noindent\\hrulefill\n\nThese configurations were optimal for upgrading data in a Liferay 6.2 EE\ninstallation that had these characteristics:\n\n\\begin{itemize}\n\\item\n  3.2 GB database\n\\item\n  15 GB Document Library\n\\item\n  Content translated in 3 languages\n\\item\n  Record count for most populated entities:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    1,694,000 rating entries\n  \\item\n    1,605,000 permissions (\\texttt{ResourcePermission} objects)\n  \\item\n    871,000 assets (\\texttt{AssetEntry} objects)\n  \\item\n    400,000 users\n  \\item\n    400,000 sites (\\texttt{Group} objects)\n  \\item\n    402,000 images\n  \\item\n    259,000 message forum threads and posts\n  \\item\n    200,000 documents\n  \\item\n    193,000 portlet preferences\n  \\item\n    103,000 web content pieces (\\texttt{JournalArticle} objects)\n  \\item\n    50,600 pages\n  \\item\n    3,276 journal article images\n  \\item\n    3,100 document folders\n  \\end{itemize}\n\\end{itemize}\n\nStart with configuring the database upgrade tool's Java process.\n\n\\section{Tuning the Database Upgrade Java\nProcess}\\label{tuning-the-database-upgrade-java-process}\n\nMake sure to provide adequate memory for the database upgrade tool's\nJava process. 15GB was appropriate for the test scenario. Also make sure\nto set the file encoding to UTF-8 and the time zone to GMT. Here are the\nJava process settings:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Xmx 15 GB RAM\n\\item\n  File encoding UTF-8\n\\item\n  User time zone GMT\n\\end{itemize}\n\nHere is the \\texttt{db\\_upgrade.sh} command:\n\n\\begin{verbatim}\ndb_upgrade.sh -j \"-Xmx15000m -Dfile.encoding=UTF-8 -Duser.timezone=GMT\"\n\\end{verbatim}\n\nIt's time to tune your database transaction engine.\n\n\\section{Tuning the Database Transaction Engine for Executing\nUpdates}\\label{tuning-the-database-transaction-engine-for-executing-updates}\n\nMany more update statements are executed during data upgrade than in\nproduction. Here's how to optimize each database's transaction engine\nfor the updates.\n\n\\section{IBM DB2}\\label{ibm-db2}\n\nPlease consult IBM's official DB2 documentation.\n\n\\section{MariaDB}\\label{mariadb}\n\nIn addition to the default database configuration, turn off InnoDB\ndouble-write.\n\n\\section{Microsoft SQL Server}\\label{microsoft-sql-server}\n\nIn addition to the default database configuration, set\n\\href{https://docs.microsoft.com/en-us/sql/relational-databases/logs/control-transaction-durability}{transaction\ndurability} to \\texttt{FORCED}.\n\n\\section{MySQL}\\label{mysql}\n\nIn addition to the default database configuration, turn off\n\\href{https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html\\#sysvar_innodb_doublewrite}{InnoDB\ndouble-write}.\n\n\\section{Oracle Database}\\label{oracle-database}\n\nThe default configuration works well. It configures\n\\href{https://docs.oracle.com/database/121/REFRN/GUID-FD8D1BD2-0F85-4844-ABE7-57B4F77D1608.htm\\#REFRN10048}{asynchronous\nI/O to disk} automatically.\n\n\\section{PostgreSQL}\\label{postgresql}\n\nIn addition to the default database configuration, turn off\n\\href{https://www.postgresql.org/docs/10/wal-async-commit.html}{synchronous\ncommits}.\n\n\\section{Tuning the Database Transaction\nLog}\\label{tuning-the-database-transaction-log}\n\nIn production, transaction logs mark safe states to roll back to. In\ndata upgrades, however, the safe state is the original data backup.\nSince transaction logging is insignificant for data upgrades, it should\nbe disabled or minimized. Here are log tuning instructions for each\ndatabase.\n\n\\section{IBM DB2}\\label{ibm-db2-1}\n\nPlease consult IBM's official DB2 documentation.\n\n\\section{MariaDB}\\label{mariadb-1}\n\nIn addition to the default database configuration, set the InnoDB flush\nlog at transaction commit to \\texttt{0}.\n\n\\section{Microsoft SQL Server}\\label{microsoft-sql-server-1}\n\nUse the default database configuration.\n\n\\section{MySQL}\\label{mysql-1}\n\nIn addition to the default database configuration, set the\n\\href{https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html\\#sysvar_innodb_flush_log_at_trx_commit}{InnoDB\nflush log at transaction commit} to \\texttt{0}.\n\n\\section{Oracle Database}\\label{oracle-database-1}\n\nUse the default database configuration.\n\n\\section{PostgreSQL}\\label{postgresql-1}\n\nIn addition to the default database configuration, Set the\n\\href{https://www.postgresql.org/docs/10/wal-async-commit.html}{write\nahead log writer delay} to \\texttt{1000} milliseconds.\n\nCongratulations! You have a starting point to plan your own Liferay DXP\ndata upgrade project. Remember, optimal tuning depends on your data,\ninfrastructure conditions, and database vendor. Analyze your data, tune\nfor upgrade, and time your test upgrades. Use this information to\ndetermine the best database and Java process configuration for your\nLiferay DXP data upgrade.\n\n\\chapter{Configuring the Data\nUpgrade}\\label{configuring-the-data-upgrade}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe upgrade tool provides the easiest way to upgrade the core and\ninstalled modules. You can use text files or the tool's command line\ninterface to configure your upgrade. The upgrade tool can upgrade\neverything---the core and all the modules---together or separately.\n\n7.0 bundles include the upgrade tool. If you installed @product-ver@\nmanually, you can download the upgrade tool separately.\n\n\\begin{itemize}\n\\item\n  \\emph{Liferay DXP 7.2}: Go to the\n  \\href{https://customer.liferay.com/group/customer/downloads}{\\emph{Downloads}\n  page} and select the \\emph{DXP 7.2} product and the\n  \\emph{Product/Service Packs} file type. In the listing that appears,\n  click \\emph{Download} for the \\emph{Liferay DXP Upgrade Client}.\n\\item\n  \\emph{Liferay Portal CE 7.2}: Go to the\n  \\href{https://www.liferay.com/downloads-community}{\\emph{Downloads}\n  page} and select \\emph{Download} for \\emph{Liferay Portal Tools for\n  7.2}.\n\\end{itemize}\n\nBefore starting the data upgrade process, configure the upgrade tool for\nthe core upgrade and specify whether the upgrade tool should upgrade\nnon-core module data automatically.\n\n\\section{Configuring the Core\nUpgrade}\\label{configuring-the-core-upgrade}\n\nThe core upgrade requires configuration. You can configure it at runtime\nvia the command line interface or pre-configure it in these files in\n\\texttt{{[}Liferay\\ Home{]}/tools/portal-tools-db-upgrade-client/}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{app-server.properties}: Specifies the server's location and\n  libraries.\n\\item\n  \\texttt{portal-upgrade-database.properties}: Configures the database\n  connection.\n\\item\n  \\texttt{portal-upgrade-ext.properties}: Sets the rest of the portal\n  properties that the upgrade requires. You might want to copy your\n  current portal properties (except your database properties) into this\n  file. Before copying your current properties, make sure you've\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database}{updated\n  the portal properties for 7.0}.\n\\end{itemize}\n\nEach file's properties are described next.\n\n\\section{Configuring\napp-server.properties}\\label{configuring-app-server.properties}\n\nSpecify the following information to configure 7.0's app server:\n\n\\texttt{dir:} the absolute path of the application server folder.\n\\emph{(required)}\n\n\\texttt{extra.lib.dirs:} a comma delimited list of extra directories\ncontaining any binaries or resources to add to the class path. Use all\nabsolute paths OR all paths relative to \\texttt{dir}. \\emph{(required)}\n\n\\texttt{global.lib.dir:} the application server's global library\ndirectory. Use the absolute path or a path relative to \\texttt{dir}.\n\\emph{(required)}\n\n\\texttt{portal.dir:} the directory where portal is installed in your app\nserver. Use the absolute path or a path relative to \\texttt{dir}.\n\\emph{(required)}\n\n\\texttt{server.detector.server.id:} ID of a supported application\nserver. (\\emph{required}) Here are the IDs:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{jboss}\n\\item\n  \\texttt{jonas}\n\\item\n  \\texttt{resin}\n\\item\n  \\texttt{tomcat}\n\\item\n  \\texttt{weblogic}\n\\item\n  \\texttt{websphere}\n\\item\n  \\texttt{wildfly}\n\\end{itemize}\n\nRelative paths must use Unix style format. The following properties, for\nexample, are for Windows and use relative paths:\n\n\\begin{verbatim}\ndir=D:\\\nextra.lib.dirs=Liferay/liferay-portal-master/tomcat-9.0.10/bin\nglobal.lib.dir=Liferay/liferay-portal-master/tomcat-9.0.10/lib\nportal.dir=Liferay/liferay-portal-master/tomcat-9.0.10/webapps/ROOT\nserver.detector.server.id=tomcat\n\\end{verbatim}\n\nThese properties, for example, are for Linux and use all absolute paths:\n\n\\begin{verbatim}\ndir=/\nextra.lib.dirs=/home/user/liferay/liferay-portal-master/tomcat-9.0.10/bin\nglobal.lib.dir=/home/user/liferay/liferay-portal-master/tomcat-9.0.10/lib\nportal.dir=/home/user/liferay/liferay-portal-master/tomcat-9.0.10/webapps/ROOT\nserver.detector.server.id=tomcat\n\\end{verbatim}\n\n\\section{Configuring\nportal-upgrade-database.properties}\\label{configuring-portal-upgrade-database.properties}\n\nSpecify the following information to configure the database you're\nupgrading. Note that these properties correspond exactly to the\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#JDBC}{JDBC\nportal properties} you'd use in a \\texttt{portal-ext.properties} file.\n\n\\texttt{jdbc.default.driverClassName} \\emph{(required)}\n\n\\texttt{jdbc.default.url} \\emph{(required)}\n\n\\texttt{jdbc.default.username} \\emph{(required)}\n\n\\texttt{jdbc.default.password} \\emph{(required)}\n\n\\section{Configuring\nportal-upgrade-ext.properties}\\label{configuring-portal-upgrade-ext.properties}\n\nSpecify the following information to configure the upgrade:\n\n\\texttt{liferay.home:} The\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay home\nfolder} \\emph{(required)}\n\n\\texttt{dl.store.impl:} The implementation for persisting documents to\nthe document library store. This property is mandatory if you're using a\n\\texttt{*FileSystemStore} implementation. If you\n\\href{/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade}{updated\nthis property in your \\texttt{portal-ext.properties}}, copy it here.\nOtherwise, set the property one of these ways:\n\n\\begin{verbatim}\ndl.store.impl=com.liferay.portal.store.file.system.FileSystemStore\ndl.store.impl=com.liferay.portal.store.db.DBStore\ndl.store.impl=com.liferay.portal.store.file.system.AdvancedFileSystemStore\ndl.store.impl=com.liferay.portal.store.s3.S3Store\n\\end{verbatim}\n\n\\texttt{hibernate.jdbc.batch\\_size:} The JDBC batch size used to improve\nperformance; set to \\emph{250} by default \\emph{(optional)}\n\n\\section{Example Upgrade\nConfiguration}\\label{example-upgrade-configuration}\n\nHere's an example interaction with the upgrade tool's command line\ninterface:\n\n\\begin{verbatim}\nPlease enter your application server (tomcat):\ntomcat\nPlease enter your application server directory (../../tomcat-8.0.32):\n\nPlease enter your extra library directories (../../tomcat-8.0.32/bin):\n\nPlease enter your global library directory (../../tomcat-8.0.32/lib):\n\nPlease enter your portal directory (../../tomcat-8.0.32/webapps/ROOT):\n\n[ db2 mariadb mysql oracle postgresql sqlserver sybase ]\nPlease enter your database (mysql):\nmariadb\nPlease enter your database host (localhost):\n\n(etc.)\n\\end{verbatim}\n\nThe command line interface creates the configuration files based on your\ninput. You can put this information into configuration files to\nconfigure the tool manually.\n\nHere are example upgrade configuration files that you can customize and\ncopy into\n\\texttt{{[}Liferay\\ Home{]}/tools/portal-tools-db-upgrade-client/}:\n\n\\begin{itemize}\n\\item\n  \\texttt{app-server.properties}:\n\n\\begin{verbatim}\ndir=../../tomcat-8.0.32\nglobal.lib.dir=/lib\nportal.dir=/webapps/ROOT\nserver.detector.server.id=tomcat\nextra.lib.dirs=/bin\n\\end{verbatim}\n\\item\n  \\texttt{portal-upgrade-database.properties}:\n\n\\begin{verbatim}\njdbc.default.url=jdbc:mysql://lportal62?characterEncoding=UTF-8&dontTrackOpenResources=true&holdResultsOpenOverStatementClose=true&serverTimezone=GMT&useFastDateParsing=false&useUnicode=true\njdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\njdbc.default.username=root\njdbc.default.password=\n\\end{verbatim}\n\\item\n  \\texttt{portal-upgrade-ext.properties}:\n\n\\begin{verbatim}\nliferay.home=/home/user/servers/liferay7\nmodule.framework.base.dir=/home/user/servers/liferay7/osgi\ndl.store.impl=com.liferay.portal.store.file.system.FileSystemStore\n\\end{verbatim}\n\\end{itemize}\n\nNext, decide if the upgrade tool should upgrade non-core modules\nautomatically.\n\n\\section{Configuring Non-Core Module Data\nUpgrades}\\label{configuring-non-core-module-data-upgrades}\n\nYou can configure the upgrade tool to upgrade all installed modules\nautomatically or to open a Gogo shell (after core upgrade completes) for\nyou to execute module upgrades manually.\n\nIf the upgrade tool's \\texttt{autoUpgrade} property is set to\n\\texttt{true} (the default setting), upgrade processes for all installed\nmodules are run too.\n\nIf you set \\texttt{autoUpgrade=\"false\"} in a file called\n\\texttt{com.liferay.portal.upgrade.internal.configuration.ReleaseManagerConfiguration.config}\nand copy the file into the \\texttt{{[}Liferay\\ Home{]}/osgi/configs}\nfolder, the upgrade tool opens Gogo shell after the core upgrade. In the\nGogo shell, you can\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell}{administer\nmodule upgrades}.\n\nIt's time to run the upgrade tool.\n\n\\chapter{Upgrading the Core Using the Upgrade\nTool}\\label{upgrading-the-core-using-the-upgrade-tool}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe upgrade tool provides the easiest way to upgrade the core and\ninstalled modules. Here's how to use it.\n\n\\section{Upgrade Tool Usage}\\label{upgrade-tool-usage}\n\nThe \\texttt{db\\_upgrade.sh} script in the\n\\texttt{{[}Liferay\\ Home{]}/tools/portal-tools-db-upgrade-client} folder\n(\\texttt{db\\_upgrade.bat} on Windows) invokes the upgrade tool.\n\nThis command prints the upgrade tool usage:\n\n\\begin{verbatim}\ndb_upgrade.sh --help\n\\end{verbatim}\n\nThis configuration prevents automatic module upgrade, but causes the\nupgrade tool to open a Gogo shell for\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell}{upgrading\nmodules} after finishing the core upgrade.\n\nHere are the tool's default Java parameters:\n\n\\begin{verbatim}\n-Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.timezone=GMT -Xmx2048m \n\\end{verbatim}\n\nThe \\texttt{-j} option overrides the JVM parameters. For example, these\noptions set the JVM memory to 10GB, which is a good starting point for\nthis process type:\n\n\\begin{verbatim}\ndb_upgrade.sh -j \"-Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.timezone=GMT -Xmx10240m\"\n\\end{verbatim}\n\nThe \\texttt{-l} option specifies the tool's log file name:\n\n\\begin{verbatim}\ndb_upgrade.sh -l \"output.log\"\n\\end{verbatim}\n\nHere are all the upgrade tool command line options:\n\n\\textbf{--help} or \\textbf{-h}: Prints the tool's help message.\n\n\\textbf{--jvm-opts} or \\textbf{-j} + \\textbf{{[}arg{]}}: Sets any JVM\noptions for the upgrade process.\n\n\\textbf{--log-file} or \\textbf{-l} + \\textbf{{[}arg{]}}: Specifies the\ntool's log file name---the default name is \\texttt{upgrade.log}.\n\n\\textbf{--shell} or \\textbf{-s}: Automatically connects you to the Gogo\nshell after finishing the upgrade process.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Only execute the upgrade process on a server with ideal\nmemory, CPU, and database connection configurations. If executing an\nupgrade remotely using \\texttt{ssh}, make sure to guard against\ninterruptions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  If you're executing the upgrade using \\texttt{ssh}, ignore hangups\n  (connection loss) by using \\texttt{nohup} or something similar.\n\\item\n  On the machine you're connecting from, disable settings that shutdown\n  or sleep that machine.\n\\end{itemize}\n\nThe upgrade process continues on the server even if you lose connection\nto it. If you lose connection, reconnect and monitor upgrade status via\nthe log (default log file is \\texttt{upgrade.log}). If you're using an\nearlier version of 7.0 and upgrade execution is interrupted, check your\nlog file for where execution stopped.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  If execution stopped during an upgrade process for Core 7.1 or higher,\n  or any module upgrade process, restart the upgrade tool to continue\n  the upgrade from that point. You can also use Gogo shell to\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell\\#checking-upgrade-status}{check\n  module upgrade status} and continue upgrading modules.\n\\item\n  If execution stopped during an upgrade process for Core 7.0 or lower,\n  you must\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation}{restore\n  the data from a backup} and start the upgrade again.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} To prevent the tool's expanded command from growing\ntoo large for Windows, execute the upgrade tool script from the\n\\texttt{{[}Liferay\\ \\ Home{]}/tools/portal-tools-db-upgrade-client}\nfolder.\n\n\\noindent\\hrulefill\n\nIt's time to upgrade your core data using the upgrade tool.\n\n\\section{Running and Managing the Core\nUpgrade}\\label{running-and-managing-the-core-upgrade}\n\nStart the upgrade tool, as the previous section explains. Here are the\ncore upgrade stages:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Show the upgrade patch level\n\\item\n  Execute the core upgrade processes\n\\item\n  Execute the core verifiers\n\\end{enumerate}\n\nMonitor the upgrade via the upgrade tool log file (default file is\n\\texttt{upgrade.log}). If a core upgrade process fails, analyze the\nfailure and resolve it. If a core upgrade step for Liferay DXP 7.1 (or\nnewer) fails, executing the upgrade tool again starts it from that step.\n\nIf you configured the upgrade tool to upgrade non-core modules, the tool\nopens a Gogo shell and starts upgrading them. The Gogo shell lets you\nupgrade modules, check module upgrade status, verify upgrades, and\nrestart module upgrades. Read on to learn how to use Gogo shell commands\nto complete Liferay DXP upgrades.\n\n\\chapter{Upgrading Modules Using Gogo\nShell}\\label{upgrading-modules-using-gogo-shell}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay's Gogo shell can upgrade and verify individual modules. It's a\nfine-grained approach to upgrading the core and non-core modules. If you\nhaven't already upgraded your non-core modules using the upgrade tool or\nif there are modules you need to revisit upgrading, you can upgrade them\nusing Gogo Shell.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: You must\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-data-upgrade}{Configure\nthe core upgrade} before using Gogo shell commands to upgrade the core.\n\n\\noindent\\hrulefill\n\nBelow is a list of commands.\n\n\\section{Command Usage}\\label{command-usage}\n\nIf you ran the upgrade tool and it opened Gogo shell, you're already\nconnected. Otherwise, you can execute commands using the\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nShell portlet}.\n\nHere are the commands:\n\n\\texttt{exit} or \\texttt{quit:} Exits the Gogo shell\n\n\\texttt{upgrade:help:} Displays upgrade commands\n\n\\texttt{upgrade:check:} Lists upgrades pending execution because they\nfailed in the past or the module hasn't reached its final version\n\n\\texttt{upgrade:execute\\ \\{module\\_name\\}:} Executes upgrades for that\nmodule\n\n\\texttt{upgrade:executeAll:} Executes all pending module upgrade\nprocesses\n\n\\texttt{upgrade:list:} Lists all registered upgrades\n\n\\texttt{upgrade:list\\ \\{module\\_name\\}:} Lists the module's required\nupgrade steps\n\n\\texttt{upgrade:list\\ \\textbar{}\\ grep\\ Registered:} Lists registered\nupgrades and their versions\n\n\\texttt{verify:help:} Displays verify commands\n\n\\texttt{verify:check\\ \\{module\\_name\\}:} Lists the latest execution\nresult for the module's verify process\n\n\\texttt{verify:checkAll:} Lists the latest execution results for all\nverify processes\n\n\\texttt{verify:execute\\ \\{module\\_name\\}:} Executes the module's\nverifier\n\n\\texttt{verify:executeAll:} Executes all verifiers\n\n\\texttt{verify:list:} Lists all registered verifiers\n\nThere are many useful\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Liferay\ncommands and standard commands available in Gogo shell}. The following\nsections describe Liferay upgrade commands.\n\n\\section{Listing module upgrade\nprocesses}\\label{listing-module-upgrade-processes}\n\nBefore upgrading modules, you should find which have unresolved\ndependencies, which are resolved and available to upgrade, and examine\nthe module upgrade processes.\n\nExecuting \\texttt{upgrade:list} in the Gogo shell lists the modules\nwhose upgrade dependencies are satisfied. These modules can be upgraded.\n\nIf a module is active but not listed, its dependencies must be upgraded.\nThe Gogo shell command\n\\texttt{scr:info\\ {[}upgrade\\_step\\_class\\_qualified\\_name{]}} shows the\nupgrade step class's unsatisfied dependencies. Here's an example\n\\texttt{scr:info} command:\n\n\\begin{verbatim}\nscr:info com.liferay.journal.upgrade.JournalServiceUpgrade\n\\end{verbatim}\n\nInvoking \\texttt{upgrade:list\\ {[}module\\_name{]}} lists the module's\nupgrade processes, in no particular order. For example, executing\n\\texttt{upgrade:list\\ com.liferay.bookmarks.service} (for the Bookmarks\nService module), lists this:\n\n\\begin{verbatim}\nRegistered upgrade processes for com.liferay.bookmarks.service 1.0.0\n        {fromSchemaVersionString=0.0.0, toSchemaVersionString=1.0.0, upgradeStep=com.liferay.portal.spring.extender.internal.context.ModuleApplicationContextExtender$ModuleApplicationContextExtension$1@6e9691da}\n        {fromSchemaVersionString=0.0.1, toSchemaVersionString=1.0.0-step-3, upgradeStep=com.liferay.bookmarks.upgrade.v1_0_0.UpgradePortletId@5f41b7ee}\n        {fromSchemaVersionString=1.0.0-step-1, toSchemaVersionString=1.0.0, upgradeStep=com.liferay.bookmarks.upgrade.v1_0_0.UpgradePortletSettings@53929b1d}\n        {fromSchemaVersionString=1.0.0-step-2, toSchemaVersionString=1.0.0-step-1, upgradeStep=com.liferay.bookmarks.upgrade.v1_0_0.UpgradeLastPublishDate@3e05b7c8}\n        {fromSchemaVersionString=1.0.0-step-3, toSchemaVersionString=1.0.0-step-2, upgradeStep=com.liferay.bookmarks.upgrade.v1_0_0.UpgradeClassNames@6964cb47}\n\\end{verbatim}\n\nAn application's upgrade step class names typically reveal their\nintention. For example, the example's\n\\texttt{com.liferay.bookmarks.upgrade.v1\\_0\\_0.UpgradePortletId} upgrade\nstep class updates the app's portlet ID. The other example upgrade step\nclasses update class names, the \\texttt{LastPublishDate}, and\n\\texttt{PortletSettings}. The example's step from \\texttt{0.0.0} to\n\\texttt{1.0.0} upgrades the module from an empty database.\n\nTo examine a module's upgrade process better, you can sort the listed\nupgrade steps mentally or in a text editor. Here's the upgrade step\norder for a Bookmarks Service module to be upgraded from Liferay Portal\n6.2 (the module's database exists) to schema version \\texttt{1.0.0}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{0.0.1} to \\texttt{1.0.0-step-3}\n\\item\n  \\texttt{0.0.1-step-3} to \\texttt{1.0.0-step-2}\n\\item\n  \\texttt{0.0.1-step-2} to \\texttt{1.0.0-step-1}\n\\item\n  \\texttt{0.0.1-step-1} to \\texttt{1.0.0}\n\\end{itemize}\n\nThe overall module upgrade process starts at version \\texttt{0.0.1} and\nfinishes at version \\texttt{1.0.0}. The first step starts on the initial\nversion (\\texttt{0.0.1}) and finishes on the target version's highest\nstep (\\texttt{step-3}). The last step starts on the target version's\nlowest step (\\texttt{step-1}) and finishes on the target version\n(\\texttt{1.0.0}).\n\nOnce you understand the module's upgrade process, you can execute it\nwith confidence.\n\n\\section{Executing module upgrades}\\label{executing-module-upgrades}\n\nExecuting \\texttt{upgrade:execute\\ {[}module\\_name{]}} upgrades the\nmodule. You might run into upgrade errors that you must resolve.\nExecuting the command again starts the upgrade from the last successful\nstep.\n\nYou can check upgrade status by executing\n\\texttt{upgrade:list\\ {[}module\\_name{]}}. For example, entering\n\\texttt{upgrade:list\\ com.liferay.iframe.web} outputs this:\n\n\\begin{verbatim}\nRegistered upgrade processes for com.liferay.iframe.web 0.0.1\n   {fromSchemaVersionString=0.0.1, toSchemaVersionString=1.0.0, upgradeStep=com.liferay.iframe.web.upgrade.IFrameWebUpgrade$1@1537752d}\n\\end{verbatim}\n\nThe first line lists the module's name and current version. The example\nmodule's current version is \\texttt{0.0.1}. The\n\\texttt{toSchemaVersionString} value is the target version.\n\nExecuting \\texttt{upgrade:list\\ {[}module\\_name{]}} on the module after\nsuccessfully upgrading it shows the module's name followed by the\nversion you targeted.\n\nFor example, if you successfully upgraded\n\\texttt{com.liferay.iframe.web} to version \\texttt{1.0.0}, executing\n\\texttt{upgrade:list\\ com.liferay.iframe.web} shows the module's version\nis \\texttt{1.0.0}:\n\n\\begin{verbatim}\nRegistered upgrade processes for com.liferay.iframe.web 1.0.0\n   {fromSchemaVersionString=0.0.1, toSchemaVersionString=1.0.0, upgradeStep=com.liferay.iframe.web.upgrade.IFrameWebUpgrade$1@1537752d}\n\\end{verbatim}\n\nFor module upgrades that don't complete, you can check their status and\nresolve their issues.\n\n\\section{Checking upgrade status}\\label{checking-upgrade-status}\n\nThe command \\texttt{upgrade:check} lists modules that have impending\nupgrades.\n\nFor example, if module \\texttt{com.liferay.dynamic.data.mapping.service}\nfailed in a step labeled \\texttt{1.0.0-step-2}, executing\n\\texttt{upgrade:check} shows this:\n\n\\begin{verbatim}\nWould upgrade com.liferay.dynamic.data.mapping.service from 1.0.0-step-2 to\n1.0.0 and its dependent modules\n\\end{verbatim}\n\nModules often depend on other modules to complete upgrading. Executing\n\\texttt{scr:info\\ {[}upgrade\\_step\\_class\\_qualified\\_name{]}} shows the\nupgrade step class's dependencies. You must upgrade modules on which\nyour module depends.\n\nTo resolve and activate a module, its upgrade must complete. The\n\\href{http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager/tutorials/leveraging-the-shell.html}{Apache\nFelix Dependency Manager} Gogo shell command \\texttt{dm\\ wtf} reveals\nunresolved dependencies. If your module requires a certain data schema\nversion (e.g., its \\texttt{bnd.bnd} specifies\n\\texttt{Liferay-Require-SchemaVersion:\\ 1.0.2}) but the module hasn't\ncompleted upgrade to that version, \\texttt{dm\\ wtf} shows that the\nschema version is not registered.\n\n\\begin{verbatim}\n1 missing dependencies found.\n-------------------------------------\nThe following service(s) are missing:\n * com.liferay.portal.kernel.model.Release (&(release.bundle.symbolic.name=com.liferay.journal.service)(release.schema.version=1.0.2)) is not found in the service registry\n\\end{verbatim}\n\nThe \\texttt{dm\\ wtf} command can also help detect errors in portlet\ndefinitions and custom portlet \\texttt{schemaVersion} fields.\n\nBrowsing the Liferay DXP database \\texttt{Release\\_} table can help you\ndetermine a module's upgrade status too. The core's\n\\texttt{servletContextName} field value is \\texttt{portal}. If the\ncore's \\texttt{schemaVersion} field matches your new Liferay DXP version\n(e.g., \\texttt{7.2.1} for Liferay Portal CE GA2) and the\n\\texttt{verified} field is \\texttt{1} (true), the core upgrade completed\nsuccessfully.\n\nEach module has one \\texttt{Release\\_} table record, and the value for\nits \\texttt{schemaVersion} field must be \\texttt{1.0.0} or greater\n(\\texttt{1.0.0} is the initial version for 7.0 modules, except for those\nthat were previously traditional plugins intended for Liferay Portal\nversion 6.2 or earlier).\n\n\\section{Executing verify processes}\\label{executing-verify-processes}\n\nSome modules have verify processes. These make sure the upgrade executed\nsuccessfully. Verify processes in the core are automatically executed\nafter upgrading Liferay DXP. You can also execute them by configuring\nthe\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Verify}{\\texttt{verify.*}\nportal properties} and restarting your server.\n\nTo check for available verify processes, enter the Gogo shell command\n\\texttt{verify:list}. To run a verify process, enter\n\\texttt{verify:execute\\ {[}verify\\_qualified\\_name{]}}.\n\n\\chapter{Executing Post-Upgrade\nTasks}\\label{executing-post-upgrade-tasks}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nSince you optimized your system for upgrading, after the upgrade is\ncomplete you must re-optimize it for production.\n\n\\section{Tuning Your Database for\nProduction}\\label{tuning-your-database-for-production}\n\nPrior to upgrading your Liferay DXP database, you tuned it for upgrade.\nNow that upgrade is complete, restore the production database tuning you\nused previously.\n\n\\section{Re-enabling Search Indexing and Re-indexing Search\nIndexes}\\label{re-enabling-search-indexing-and-re-indexing-search-indexes}\n\nMake sure to re-enable search indexing by removing the\n\\texttt{com.liferay.portal.search.configuration.IndexStatusManagerConfiguration.config}\nfile from your \\texttt{{[}Liferay\\ Home{]}/osgi/configs} folder or\nsetting this property in it:\n\n\\begin{verbatim}\nindexReadOnly=\"false\"\n\\end{verbatim}\n\nThen re-index Liferay DXP's search indexes. Don't just do this blindly,\nhowever. By default, Liferay DXP ships with an embedded configuration\nfor Elasticsearch. This configuration works great for demo purposes, but\nis not supported in production. Make sure to\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch}{install\nand configure a standalone Elasticsearch instance to run in production}.\n\n\\section{Enabling Web Content View\nPermissions}\\label{enabling-web-content-view-permissions}\n\nPrior to Liferay DXP 7.1, all users could view web content articles by\ndefault. Now view permissions are checked by default. Here are options\nfor opening view permissions:\n\nOption 1: Edit view permissions per web content article per role.\n\nOption 2: Open view permissions for all web content articles by going to\n\\emph{System Settings → Web Experience → Web Content} and deselecting\n\\emph{Article view permissions check enabled}.\n\nOnce you've configured search, re-indexed your search index, and set web\ncontent view permissions, your upgraded system is ready for action!\nCongratulations!\n\n\\chapter{Upgrading a Sharded\nEnvironment}\\label{upgrading-a-sharded-environment}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nSince Liferay DXP 7.0, Liferay removed its own physical partitioning\nimplementation (also known as sharding) in favor of the capabilities\nprovided natively by database vendors. Upgrading a sharded installation\nto DXP 7.0 or higher requires migrating it to as many non-sharded\nLiferay DXP installations (servers) as you have shards. These steps\nguide you through configuring the new Liferay DXP servers to use your\nformerly sharded data.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Liferay continues to support its logical partitioning\ncapabilities (also known as\n\\href{/docs/7-2/user/-/knowledge_base/u/setting-up-a-virtual-instance}{virtual\ninstances}) for the foreseeable future.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\nFor any further assistance with sharding contact your Liferay account\nmanager or Liferay Support.\n\n\\noindent\\hrulefill\n\n\\section{Add Configurations Before the Data\nUpgrade}\\label{add-configurations-before-the-data-upgrade}\n\nIn addition to other configurations, you will need to set more\nproperties to migrate your shards to virtual instances for your data\nupgrade.\n\nHere is how to configure the upgrade to migrate from sharding:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Copy all of the shard JDBC connection properties from\n  \\texttt{portal-ext.properties}\n  to\\texttt{portal-upgrade-database.properties}. For example, JDBC\n  connections for a default shard and two non-default shards might look\n  like this:\n\n\\begin{verbatim}\njdbc.default.driverClassName=[the database driver class name]\njdbc.default.url=[the URL to the default database shard]\njdbc.default.username=[the user name]\njdbc.default.password=[the password]\n\njdbc.one.driverClassName=[the database driver class name]\njdbc.one.url=[the URL to database shard one]\njdbc.one.username=[the user name]\njdbc.one.password=[the password]\n\njdbc.two.driverClassName=[the database driver class name]\njdbc.two.url=[the URL to database shard two]\njdbc.two.username=[the user name]\njdbc.two.password=[the password]\n\\end{verbatim}\n\\item\n  Set the JDBC \\emph{default} connection properties in each server's\n  \\texttt{portal-upgrade-database.properties} to specify the associated\n  shard.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Add the original JDBC properties for the respective non-default\n    shard database. For example, shard \\texttt{one}'s original\n    properties might start with \\texttt{jdbc.one}:\n  \\end{itemize}\n\n\\begin{verbatim}\njdbc.one.driverClassName=[the database driver class name]\njdbc.one.url=[the URL to database shard one]\njdbc.one.username=[the user name]\njdbc.one.password=[the password]\n\\end{verbatim}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Rename the properties to start with \\texttt{jdbc.default}. For\n    example:\n  \\end{itemize}\n\n\\begin{verbatim}\njdbc.default.driverClassName=[the database driver class name]\njdbc.default.url=[the URL to database shard one]\njdbc.default.username=[the user name]\njdbc.default.password=[the password]\n\\end{verbatim}\n\\end{enumerate}\n\n\\section{Upgrade and Update\nProperties}\\label{upgrade-and-update-properties}\n\nWhen you perform the database upgrade, upgrade the default shard first,\nand then each of the non-default shards. See\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-product-data}{Using\nthe Database Upgrade Tool} for more information on running the database\nupgrade.\n\nAfter the database upgrade has been completed, make the following\nconfiguration changes to your application servers:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In each server's \\texttt{portal-ext.properties}, use the JDBC\n  \\emph{default} properties you specified in the\n  \\texttt{portal-upgrade-database.properties} (see the \\emph{default}\n  properties above).\n\\item\n  Remove the non-default shard JDBC properties from the default shard\n  server's \\texttt{portal-ext.properties} file, leaving only the default\n  shard database \\texttt{jdbc.default} properties. For example:\n\n  Old JDBC properties:\n\n\\begin{verbatim}\njdbc.default.driverClassName=[the database driver class name]\njdbc.default.url=[the URL to the default database shard]\njdbc.default.username=[the user name]\njdbc.default.password=[the password]\n\njdbc.one.driverClassName=[the database driver class name]\njdbc.one.url=[the URL to database shard one]\njdbc.one.username=[the user name]\njdbc.one.password=[the password]\n\njdbc.two.driverClassName=[the database driver class name]\njdbc.two.url=[the URL to database shard two]\njdbc.two.username=[the user name]\njdbc.two.password=v[the password]\n\\end{verbatim}\n\n  New JDBC properties:\n\n\\begin{verbatim}\njdbc.default.driverClassName=[the database driver class name]\njdbc.default.url=[the URL to your database]\njdbc.default.username=[the user name]\njdbc.default.password=[the password]\n\\end{verbatim}\n\\end{enumerate}\n\nOnce you have completed all of these steps, you have migrated off of a\nsharded environment to virtual instances on separate Liferay DXP servers\ntogether with your DXP upgrade.\n\nCongratulations! You have migrated off of a sharded environment to\nvirtual instances on separate Liferay DXP servers. You have also\nupgraded to 7.0. Your virtual instances are ready for action.\n\n\\chapter{Migrating From Audience Targeting to Segmentation and\nPersonalization}\\label{migrating-from-audience-targeting-to-segmentation-and-personalization}\n\n7.0 integrates all the Audience Targeting app's features into Liferay's\ncore as Segmentation and Personalization. This enables better\nintegration with other applications and provides developers with easier\naccess to Segmentation and Personalization features. Audience Targeting\nusers must migrate their user segments into the new Segments\napplication. There are three steps to the migration process:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Upgrade to 7.0.\n\\item\n  Migrate custom rules.\n\\item\n  Migrate behavior-based features.\n\\end{enumerate}\n\nFirst, to upgrade to the latest version of Liferay DXP, follow the\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver}{upgrade\nguide}. Most of your Audience Targeting configuration is automatically\ntransferred into the new engine.\n\nNext, any custom rules that were created in Audience Targeting must be\nre-evaluated. Some custom rules may have an out-of-the-box equivalent\nnow, while others must be migrated. If a rule must be re-implemented,\nfollow the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/segmentation-personalization}{Segmentation\nand Personalization development guide}. You can check\n\\href{/docs/7-2/deploy/-/knowledge_base/d/migrating-user-segments}{the\nlist of rules that are automatically migrated} to see how much\nadditional work you have in store. You must also\n\\href{/docs/7-2/deploy/-/knowledge_base/d/manually-migrating-from-audience-targeting}{migrate\ndisplay widgets} since the new Personalization features use different\ntools.\n\nFinally, you must migrate behavior-based features, but since Audience\nTargeting's analytics features are now part of Analytics Cloud, there\nisn't a direct path to upgrade. See the\n\\href{https://help.liferay.com/hc/en-us/articles/360006947671-Creating-Segments}{Analytics\nCloud documentation}.\n\n\\chapter{Migrating User Segments}\\label{migrating-user-segments}\n\nIn Audience Targeting, a user segment represents a subset of users. A\nuser segment is defined by one or more rules that users must match to\nbelong to that user segment. In 7.0, segments work in a similar way, but\nthey are defined by criteria instead of rules. Segment criteria are sets\nof fields defined by different user actions or properties (profile\ninformation, organization information, session information) that can be\ncombined through operations (like equals, not equals, contains, not\ncontains, greater than, and less than) and conjunctions (AND, OR) to\ndefine complex filters.\n\nDue to the similarities between Audience Targeting user segments and 7.0\nSegments, certain data can be migrated automatically as part of the\nupgrade process.\n\n\\section{Upgrade Process}\\label{upgrade-process}\n\nAs a result of the upgrade process,\n\n\\begin{itemize}\n\\tightlist\n\\item\n  All Audience Targeting User Segments appear under the new Segments\n  administration in 7.2, with the same name.\n\\item\n  For every segment, those Audience Targeting rules with an equivalent\n  in Liferay DXP 7.2 have been migrated into the corresponding criteria\n  fields (see Table below).\n\\item\n  Audience Targeting tables have been removed from your Liferay DXP\n  Database.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.3333}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.3333}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nAudience Targeting Rule\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nLiferay DXP 7.2. Segment Criteria Field\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nUpgrade Path\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nBrowser & Browser & Automated. Use user agent field with\n\\texttt{contains} operation as an alternative \\\\\nCustom Field & Custom Field & Automated \\\\\nLanguage & Language & Automated \\\\\nLast Login Date & Last Sign In Date & Automated \\\\\nOrganization Member & Organization & Automated \\\\\nOS & User Agent & Automated \\\\\nPrevious Visited Site & Not Available & Automated \\\\\nRegular Role & Role & Automated \\\\\nSite Member & Site & Automated \\\\\nUser Group Member & User Group & Automated \\\\\nAge & Not Available & Suggested: custom field \\\\\nFacebook (various) & Not Available & Suggested: custom field \\\\\nGender & Not Available & Suggested: custom field \\\\\nScore Points & Not Available & Suggested: cookie \\\\\nVisited Page/Content & Not Available & Suggested: cookie \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\nHere's an example user segment as it would appear in Audience Targeting\nfor Liferay DXP 7.1:\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/migrating-audience-targeting-segment.png}}\n\\caption{A Liferay DXP 7.1 Audience Targeting Segment.}\n\\end{figure}\n\nAnd here is the same segment migrated to Liferay 7.2:\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/migrating-new-segment.png}}\n\\caption{A Liferay DXP 7.2 Segment}\n\\end{figure}\n\nFor those Audience Targeting rules without a direct equivalent, a manual\nmigration is required. If you have any these rules, you can learn about\nyour next steps in\n\\href{/docs/7-2/deploy/-/knowledge_base/d/manually-migrating-from-audience-targeting}{Manual\nMigration}.\n\n\\chapter{Manually Migrating from Audience\nTargeting}\\label{manually-migrating-from-audience-targeting}\n\nAs explained in the previous article, some Audience Targeting rules do\nnot have a direct equivalent in Liferay DXP 7.2 and, therefore, they\ncannot be migrated automatically. Here are the recommended solutions for\neach rule type.\n\n\\section{User Attribute Rules}\\label{user-attribute-rules}\n\nSome User Attributes, like Gender or Age, do not have a direct\nequivalent in 7.0. User Attributes retrieved from external sources like\nFacebook also do not have a replacement. To replace these, you must\ncreate a\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-segments-with-custom-fields-and-session-data}{custom\nuser field} and use that to define your new Segment.\n\n\\section{Session Rules}\\label{session-rules}\n\nFor Session attributes that do not have a direct equivalent, the\nrecommended solution is to use a URL field for the current URL or a\npreviously visited URL on your site as criteria, or to use a Cookie for\nmore advanced session tracking needs.\n\n\\section{Behavior Rules}\\label{behavior-rules}\n\nIn 7.0 analytics is now managed through Analytics Cloud. You can learn\nmore about creating behavior based rules in the\n\\href{https://help.liferay.com/hc/en-us/articles/360006947671-Creating-Segments}{Analytics\nCloud documentation}.\n\n\\section{Migrating Custom Rules}\\label{migrating-custom-rules}\n\nAudience Targeting segmentation features could be extended using custom\nrules. As part of the upgrade planning process, the function of any such\nrules should be re-evaluated with the new Segmentation features of 7.0\nin mind.\n\nFirst, check the\n\\href{/docs/7-2/reference/-/knowledge_base/r/defining-segmentation-criteria}{Segmentation\nreference} if any new criteria fields can replace their function. In\nparticular, custom fields, URL fields, and cookies might help you\nmigrate your custom rules with little to no additional development.\n\nIf none of them cover your requirements, follow the development guide\nfor instructions on\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/segmentation-personalization}{how\nto add new criteria fields and contributors}.\n\n\\section{Migrating Display Portlets}\\label{migrating-display-portlets}\n\nWith Audience Targeting, you could display personalized content with the\nUser Segment Display Content portlet or by using Asset Publisher with\nthe\n\\href{https://help.liferay.com/hc/en-us/articles/360018174271-Using-the-Audience-Targeting-Widgets-}{Segments\nfilter enabled}. In 7.0, you must choose the most appropriate\npersonalization option for your use cases.\n\n\\section{User Segment Content\nDisplay}\\label{user-segment-content-display}\n\nThe User Segment Content Display portlet was used to display existing\ncontent based on segment membership rules. In 7.0, you can cover the\nsame use case by defining manual content sets with variations for your\ndifferent audiences and applying it to an asset publisher. See the\ndocumentation for\n\\href{/docs/7-2/user/-/knowledge_base/u/content-set-personalization}{creating\npersonalized Content Sets}. With this feature, you can assign any number\nof assets to the Content List for the given audience, and then use the\nAsset Publisher to define how content is displayed on the page.\n\n\\section{Asset Publisher\nPersonalization}\\label{asset-publisher-personalization}\n\nFinally, if you want to display a dynamic list of content for your\ndifferent audiences based on a filter in the same way you did with in\nAudience Targeting with the Segments filter in the Asset Publisher, you\ncan create a dynamic content set with variations for your audiences and\napply it to an asset publisher.\n\nIn addition, the new\n\\href{/docs/7-2/user/-/knowledge_base/u/content-page-personalization}{Experience-based\nContent Page personalization} feature may fulfill a use case that you\nwere previously solving with one of the methods previously available.\n\n\\chapter{Deprecated Apps in 7.2: What to\nDo}\\label{deprecated-apps-in-7.2-what-to-do}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nDuring the development of any software product, it's sometimes necessary\nto stop development on or remove outdated or unpopular features. 7.0 is\nno different. In 7.0, Liferay has deprecated several apps and features.\n\nThere are three types of deprecated apps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Deprecated apps that remain in Liferay DXP, but will be removed in a\n  future release. (Availability: \\emph{Bundled})\n\\item\n  Deprecated apps that have been removed from Liferay DXP, yet are still\n  available for download via\n  \\href{https://web.liferay.com/marketplace}{Liferay Marketplace}\n  (Availability: \\emph{Marketplace})\n\\item\n  Deprecated apps that have been removed from Liferay DXP and aren't\n  available for download. (Availability: \\emph{Removed})\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} All apps deprecated by Liferay are no longer in active\ndevelopment. You should therefore plan to stop using these apps. Such\napps, however, may still be available for download.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} For information on apps deprecated in Liferay DXP 7.1,\nplease see\n\\href{/docs/7-1/deploy/-/knowledge_base/d/deprecated-apps-in-7-1-what-to-do}{Deprecated\nApps in 7.1: What to Do}\n\n\\noindent\\hrulefill\n\nHere are the apps deprecated in 7.0.\n\n\\section{Foundation}\\label{foundation}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.1364}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.5909}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.2727}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nApp\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nAvailability\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nNotes\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nAlloyUI & Bundled & Replaced by \\href{https://metaljs.com/}{MetalJS}\n(temporary) exposed as\n\\href{/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs}{ClayUI\ntag} equivalents. \\\\\nCMIS Store & Removed & Migrate to another\n\\href{/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration}{Document\nRepository Store option}. Before\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver}{upgrading\nto 7.0}, migrate your document store data using\n\\href{/docs/7-2/user/-/knowledge_base/u/server-administration}{Data\nMigration in Server Administration}. \\\\\nJCRStore & Removed & Migrate to another\n\\href{/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration}{Document\nRepository Store option}. Before\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver}{upgrading\nto 7.0}, migrate your document store data using\n\\href{/docs/7-2/user/-/knowledge_base/u/server-administration}{Data\nMigration in Server Administration}. \\\\\nLegacy Search Portlet & Bundled & Will be removed in a future release.\nReplaced by the \\href{/docs/7-2/user/-/knowledge_base/u/search}{Search\nwidgets}. \\\\\nLiferay Mobile Device Detection Enterprise & Removed & Contact 51Degrees\nfor up-to-date definitions. \\\\\nSprite framework & Bundled & Liferay's image sprite framework is\ndeprecated and is disabled by default via the \\texttt{sprite.enabled}\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\nproperty}. You can still build image sprites using any framework you\nlike and deploy them in your plugins. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Personalization}\\label{personalization}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.1364}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.5909}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.2727}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nApp\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nAvailability\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nNotes\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nAudience Targeting & Removed & Replaced by\n\\href{/docs/7-2/user/-/knowledge_base/u/segmentation-and-personalization}{Personalization}. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Web Experience}\\label{web-experience}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.1364}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.5909}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.2727}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nApp\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nAvailability\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nNotes\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nRSS Publisher & Bundled & See\n\\href{/docs/7-1/user/-/knowledge_base/u/the-rss-publisher-widget}{the\narticle} on enabling and using this widget. \\\\\nUser Group Pages (Copy Mode) & Bundled & See the\n\\href{/docs/7-1/user/-/knowledge_base/u/user-group-sites\\#legacy-user-group-sites-behavior}{Legacy\nUser Group Sites Beahavior} instructions on how to enable it. \\\\\nResources Importer & Bundled & Deprecated as of 7.1 with no direct\nreplacement \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Forms}\\label{forms}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}lll@{}}\n\\toprule\\noalign{}\nApp & Availability & Notes \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nWeb Form & Removed & Final version released for 7.0. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Security}\\label{security-1}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.0938}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.5625}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 4\\tabcolsep) * \\real{0.3438}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nApp\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nAvailability\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nNotes\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nCentral Authentication Service (CAS) & Bundled & Migrate to\n\\href{https://help.liferay.com/hc/en-us/articles/360028711032-Introduction-to-Authenticating-Using-SAML}{SAML\nbased authentication}. \\\\\nGoogle Login & Marketplace & Replaced by\n\\href{/docs/7-2/deploy/-/knowledge_base/d/authenticating-with-openid-connect}{OpenID\nConnect}. \\\\\nNTLM & Marketplace & Replaced by\n\\href{/docs/7-2/deploy/-/knowledge_base/d/authenticating-with-kerberos}{Kerberos}. \\\\\nOpenAM / OpenSSO & Bundled & Migrate to\n\\href{https://help.liferay.com/hc/en-us/articles/360028711032-Introduction-to-Authenticating-Using-SAML}{SAML\nbased authentication}. \\\\\nOpenID & Marketplace & Replaced by\n\\href{/docs/7-2/deploy/-/knowledge_base/d/authenticating-with-openid-connect}{OpenID\nConnect}. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{User and System Management}\\label{user-and-system-management}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 2\\tabcolsep) * \\real{0.1429}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 2\\tabcolsep) * \\real{0.8571}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nApp\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nAvailability\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nLive Users & Enabled through the\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html}{\\texttt{live.users.enabled}}\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\nproperty}. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Related Topics}\\label{related-topics-7}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/apps-in-maintenance-mode}{Apps\nin Maintenance Mode}\n\n\\chapter{Apps in Maintenance Mode}\\label{apps-in-maintenance-mode}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAt a designated time, Liferay may cease enhancing a product or\ncapability. This is called \\emph{maintenance mode}. During this mode,\nLiferay actively supports and provides bug fixes for the product or\ncapability in accordance with the subscribers' subscription level and\nthe end of service life policies of the compatible Liferay DXP version.\nMaintenance mode does not necessarily mean that deprecation in a future\nLiferay DXP version is planned for the product or capability; it only\nmeans that enhancements aren't being made for the current Liferay DXP\ndevelopment cycle.\n\nAs of Liferay DXP 7.2, these products and capabilities have transitioned\ninto maintenance mode:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay Connected Services\n\\item\n  Liferay Connector to OAuth 1.0a\n\\item\n  Liferay Drools\n\\item\n  Liferay Mobile Experience (Liferay Screens, Liferay Mobile SDK,\n  Liferay Push)\n\\item\n  Liferay Reports\n\\item\n  Liferay Sync\n\\item\n  Staging\n\\end{itemize}\n\n\\section{Related Topics}\\label{related-topics-8}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/deprecated-apps-in-7-2-what-to-do}{Deprecated\nApps in 7.2: What to do?}\n\n\\chapter{Maintaining Liferay DXP}\\label{maintaining-liferay-dxp}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nOnce you have a Liferay DXP installation, there are some things you must\ndo to keep it running smoothly. Backing up your installation in case of\na hardware failure protects your data and helps you get your system back\nin working order quickly. And if you're a DXP customer, patching your\nsystem regularly brings the latest bug fixes to your running instance.\n\n\\noindent\\hrulefill\n\nUpgrading 7.0 to a new GA version can require\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver}{data\nupgrade}. Until you perform all required data upgrades (if any), Liferay\nDXP startup fails with messages like these:\n\n\\begin{verbatim}\n2019-03-06 17:22:35.025 INFO  [main][StartupHelper:72] There are no patches installed\nYou must first upgrade to Liferay DXP 7210\n2019-03-06 17:22:35.098 ERROR [main][MainServlet:277] java.lang.RuntimeException: You must first upgrade to Liferay DXP 7201\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nRead on to learn about how to keep your system running well.\n\n\\chapter{Patching Liferay DXP}\\label{patching-liferay-dxp}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nWhile we strive for perfection with every Liferay DXP release, the\nreality of the human condition dictates that releases may not be as\nperfect as originally intended. But we've planned for that. Included\nwith every Liferay DXP bundle is a Patching Tool that handles installing\ntwo types of patches: fix packs and hotfixes.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Make sure to\n\\href{/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation}{back\nup your Liferay DXP installation and database} regularly, especially\nbefore patching. The patching tool installs code changes and some of\nthese make data changes (if necessary) automatically on startup.\n\nCertain fix packs (service packs) can include data/schema micro\nchanges---they're optional and revertible. Module upgrades and any micro\nchanges they include are applied at server startup by default, or can be\napplied manually by\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-data-upgrade\\#configuring-non-core-module-data-upgrades}{disabling\nthe \\texttt{autoUpgrade} property}. Server startup skips all Core micro\nchanges. Instead, you can apply them using the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver}{upgrade\ntool} before server startup.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Installing the latest service pack on top of Liferay\nDXP 7.2 GA1/FP1 requires running the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-product-data}{data\nupgrade tool}. Examine the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver}{Liferay\nDXP upgrade instructions} to determine preparations, testing, and post\nupgrade steps that are appropriate for you. To eliminate system down\ntime during upgrade, consider using the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/other-cluster-update-techniques}{Blue-green\ndeployment technique}.\n\nLiferay DXP 7.2 FP2/SP1's data schema change adds version columns to\nseveral tables.\n\\href{https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch05.html\\#d0e2225}{Hibernate's\noptimistic locking system} uses the columns to preserve data integrity\nduring concurrent data modifications.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:}\n\\href{/docs/7-2/deploy/-/knowledge_base/d/updating-a-cluster}{Patching a\ncluster} requires additional considerations.\n\n\\chapter{Patching Basics}\\label{patching-basics}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay ships 7.0 fixes through three different channels:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Fix packs\n\\item\n  Hotfixes\n\\item\n  Service Packs\n\\end{itemize}\n\n\\section{Fix Packs}\\label{fix-packs}\n\nThe latest fixes that patch the core are bundled together weekly into\nfix packs that are provided to all of Liferay's customers. Fix packs\ninclude fixes for both the core and the applications and modules that\nship with Liferay DXP. The fixes address regressions or obvious bugs and\ndon't require you to make additional changes. Each fix pack contains all\nprevious fix packs since the last service pack.\n\n\\href{https://help.liferay.com/hc/en-us/articles/360035038331}{Security\nFix Packs} are a special fix pack type for deploying critical security\nfixes quickly without changing fix pack levels.\n\nFixes that don't fit these requirements are considered for service packs\nor hot fixes.\n\n\\section{Hotfixes}\\label{hotfixes}\n\nA hotfix is provided to customers when they contact Liferay about an\nemergency situation, and Liferay's support team---working with the\ncustomer---determines that the problem is a product issue that must be\nfixed very quickly. Support fixes the bug and provides a hotfix to the\ncustomer immediately. This is a short-term fix. Hotfixes can patch the\ncore, the applications, and modules.\n\n\\section{Service Packs}\\label{service-packs}\n\nService packs are built on top of the original Liferay DXP release and\nrepackaged with the latest fix pack, Patching Tool, and modules. Since a\nservice pack is a fix pack, it contains all previous fix packs since the\nlast service pack. Each one includes the most recent patches and\nupdates.\n\nService packs can also include changes that have these characteristics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Require more extensive testing.\n\\item\n  Require some of your attention, such as updating your documentation.\n\\item\n  Improve the product.\n\\end{itemize}\n\nRather than updating existing Liferay DXP systems with service packs,\nyou should\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Keep systems up-to-date with fix packs (according to your own\n  deployment schedule).\n\\item\n  Install the latest Marketplace updates frequently.\n\\item\n  Update the Patching Tool when necessary.\n\\end{enumerate}\n\nThis method updates the installation to the service pack levels, while\nallowing scheduled deployments and avoiding full environment rebuilds.\n\n\\section{How Patches are Tested}\\label{how-patches-are-tested}\n\nLiferay extensively tests service packs, fix packs, and hotfixes to\nensure high quality. Fixes in fix packs go through both automated\nregression testing and manual testing. Hotfixes receive similar\nautomated testing, and the support engineer who fixes a reported issue\ntests it.\n\nBefore releasing a service pack, Liferay runs test suites on the\npackaged service pack.\n\n\\chapter{Using the Patching Tool}\\label{using-the-patching-tool}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe Patching Tool installs, removes, compares, and prepares Liferay DXP\npatches. It is pre-installed in Liferay DXP bundles, easy to install\ninto @product@ manual installations, and easy to update. The Patching\nTool's executable scripts facilitate patching.\n\nHere are the essentials to get started using the Patching Tool:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[installing-the-patching-tool]{Installing the Patching Tool}\n  (for manual installations only)\n\\item\n  \\hyperref[executables]{Executables}\n\\end{itemize}\n\n\\section{Installing the Patching\nTool}\\label{installing-the-patching-tool}\n\nLiferay DXP bundles come with the Patching Tool pre-installed (in\n\\texttt{{[}Liferay\\ Home{]}/patching-tool}) and pre-configured with the\ndefault settings. Skip this section if you're using a bundle.\n\nIf you installed Liferay DXP manually, however, you must also install\nthe Patching Tool manually.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Download the Patching Tool from the\n  \\href{https://customer.liferay.com/downloads?p_p_id=com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_productAssetCategoryId=118191019&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_fileTypeAssetCategoryId=118191066}{Customer\n  Portal}.\n\\item\n  Unzip the Patching Tool to your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  folder (recommended) or to another folder.\n\\end{enumerate}\n\nAfter installing the Patching Tool, you must\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-patching-tool}{configure\nit to use your Liferay DXP installation}. The \\texttt{patching-tool}\nfolder you extracted from the Patching Tool ZIP file contains the\nPatching Tool, including its executable scripts.\n\n\\section{Executables}\\label{executables}\n\nThe Unix shell and Windows batch scripts distributed with the Patching\nTool make it easier to use. On Unix systems, run\n\n\\begin{verbatim}\n./patching-tool.sh parameters\n\\end{verbatim}\n\nOn Windows, run\n\n\\begin{verbatim}\npatching-tool parameters\n\\end{verbatim}\n\nThe Windows command \\texttt{patching-tool} is used in the examples that\nfollow. On Unix, replace the name of the executable before running the\ncommands.\n\nInstalling patches is next!\n\n\\chapter{Installing Patches}\\label{installing-patches}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nBefore installing any patches, you must shut down your server. On\nWindows operating systems, files in use are locked by the OS, and can't\nbe patched. On Unix-style systems, you can usually replace files that\nare running, but the old ones reside in memory. For these reasons, it is\nbest to shut down Liferay DXP before installing patches.\n\nLiferay distributes all patches (fix packs and hotfixes) as ZIP files.\nWhen you download a patch, either from a\n\\href{https://help.liferay.com/hc}{Help Center} ticket (hotfix) or from\nthe \\href{https://customer.liferay.com/downloads}{Customer Portal} (fix\npack), place it in the Patching Tool's \\texttt{patches} folder (e.g.,\n\\texttt{{[}Liferay\\ Home{]}/patching-tool/patches}) without unzipping\nit. To list your installed patches and available local patches, execute\nthis command:\n\n\\begin{verbatim}\npatching-tool info\n\\end{verbatim}\n\nThis displays a list of patches you've already installed, along with a\nlist of patches that \\emph{can} be installed from what's in the\n\\texttt{patches} folder.\n\nTo install the available patches, use the following steps. First, issue\nthe following command:\n\n\\begin{verbatim}\npatching-tool install\n\\end{verbatim}\n\nTo make sure the all changed OSGi bundles replace the existing ones,\ndelete the \\texttt{osgi/state} folder from the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home\nfolder}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: The \\texttt{osgi/state} folder contains OSGi bundle state\ninformation. If an OSGi bundle's changes in a hot fix or fix pack are\ninternal only, they are invisible to the OSGi framework, that OSGi\nbundle stays installed, and its state information stays unchanged. Hot\nfixes, for example, may contain in-place changes that do not use the\nAPI. The framework cannot detect such changes. A fix pack's changes may\nalso be transparent to the framework. For these reasons, deleting the\n\\texttt{osgi/state} folder after applying fix packs and hot fixes is\nrecommended.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Important}: The \\texttt{osgi/state} folder should ONLY be\ndeleted when working in a development environment or when applying a fix\npack or hot fix.\n\n\\noindent\\hrulefill\n\nIf there are new database indexes created by the patch, the Patching\nTool tells you to update them. To get the list, run this command:\n\n\\begin{verbatim}\npatching-tool index-info\n\\end{verbatim}\n\nSince there's no database connection at patching time, the indexes must\nbe created at portal startup. If the server has permissions to modify\nthe database indexes, instruct Liferay DXP to create the indexes\nautomatically at startup by adding this setting to your\n\\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\ndatabase.indexes.update.on.startup=true\n\\end{verbatim}\n\nOtherwise, you must create the indexes manually. Check the\n\\texttt{patching-tool\\ index-info} command output for more details.\n\nAfter installing patches, you can execute the\n\\texttt{patching-tool\\ info} command to verify them.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If there are any issues with the installed patches,\nverify that there aren't any remaining files from the previous patch\ninstallation of a fix pack or hotfix within the application server\ncache.\n\n\\noindent\\hrulefill\n\nDuring the installation, \\texttt{patching-backup-deps.zip} and\n\\texttt{patching-backup.zip} files are created and stored in the web\napplication's \\texttt{WEB-INF} folder. These files are required to\nrestore the Liferay DXP's original state; removing them disables\npatching.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When installing patches, Liferay DXP's \\texttt{web.xml}\nis always overwritten by the one contained in the patch. If you've\ncustomized \\texttt{web.xml}, you must re-implement your customizations\nafter installing a patch.\n\n\\noindent\\hrulefill\n\nThe \\texttt{patching-backup.zip} file is necessary for installing future\npatches, because the Patching Tool reverts the installed fix pack before\ninstalling a new one. To revert the installed fix pack, it examines the\ncontents of the \\texttt{patching-backup.zip} to determine the changes\nthat it needs to revert.\n\n\\section{Handling Hotfixes and\nPatches}\\label{handling-hotfixes-and-patches}\n\nAs stated previously, hotfixes are short term fixes provided as quickly\nas possible, and fix packs are larger bundles of hotfixes provided to\nall customers at regular intervals. If you already have a hotfix\ninstalled and the fix pack that contains that hotfix is released, the\nPatching Tool can manage this for you. Fix packs always supersede\nhotfixes; so when you install your fix pack, the hotfix it contains is\nuninstalled and the fix pack version is installed in its place.\n\nThe Patching Tool applies fixes to fix packs automatically. If a new\n(fixed) version of a fix pack is released, install it with the Patching\nTool. The Patching Tool uninstalls the old fix pack and installs the new\nversion in its place.\n\n\\section{Fix Pack Dependencies}\\label{fix-pack-dependencies}\n\nSome hotfixes depend on fix packs. If you attempt to install a hotfix\nthat depends on a fix pack, the Patching Tool notifies you. Go to the\n\\href{hhttps://customer.liferay.com/downloads}{Customer Portal} and\nobtain the hotfix dependency. Once all the necessary patches are\navailable in the \\texttt{patches} folder, the Patching Tool installs\nthem.\n\n\\section{Updating the Patching Tool}\\label{updating-the-patching-tool}\n\nWhen a patch you're trying to install requires a Patching Tool update,\nthe Patching Tool tells you. To update the Patching Tool, download the\nlatest one from the\n\\href{https://customer.liferay.com/downloads?p_p_id=com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_productAssetCategoryId=118191019&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_fileTypeAssetCategoryId=118191066}{Customer\nPortal}. Overwrite the existing Patching Tool by unzipping the new one\nto the \\texttt{patching-tool} folder's parent folder.\n\n\\section{Cleaning Up}\\label{cleaning-up}\n\nAfter you've performed your patching procedure (whether you've installed\nor\n\\href{/docs/7-2/deploy/-/knowledge_base/d/working-with-patches\\#uninstalling-patches}{removed\npatches}), it's important to clean up Liferay DXP's cache of deployed\ncode. This ensures that you're using the revision you've just installed\nthe patches for when you start the server. This is really easy to do.\n\nTo clear out the cached code, remove the contents of the\n\\texttt{{[}Liferay\\ Home{]}/work} folder. Now you're ready to start your\nserver.\n\n\\chapter{Working with Patches}\\label{working-with-patches}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nHere are some things you might need to do with patches:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Report Patch Levels to Liferay Support\n\\item\n  Uninstall Patches\n\\item\n  Show collisions between patches and deployed plugins\n\\item\n  Separate Patches from your Installation\n\\end{itemize}\n\nStart with reporting patch levels to Liferay Support.\n\n\\section{Including support-info in Support\nTickets}\\label{including-support-info-in-support-tickets}\n\nProviding your environment's information (e.g., hardware architecture)\nand patch level to Liferay Support is critical for reproducing your\nissues. Write your support information (including your patch level) to a\nfile by executing this command:\n\n\\begin{verbatim}\npatching-tool support-info\n\\end{verbatim}\n\nThe support information is written to file\n\\texttt{patching-tool-support-info-actual-timestamp.txt} in your\n\\texttt{patching-tool} folder. Please upload this file to the\n\\href{https://help.liferay.com/hc}{Help Center} ticket.\n\n\\section{Uninstalling Patches}\\label{uninstalling-patches}\n\nHave you noticed that the Patching Tool only seems to have an\n\\texttt{install} command? This is because patches are managed not by the\ncommand, but by what appears in the \\texttt{patches} folder. You manage\nthe patches you have installed by adding or removing patches from this\nfolder.\n\nHere's how to uninstall (remove) a patch:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Remove the patch from your \\texttt{patches} folder.\n\\item\n  Run the \\texttt{patching-tool\\ install} command.\n\\end{enumerate}\n\nTo revert ALL patches, run this command:\n\n\\begin{verbatim}\npatching-tool revert\n\\end{verbatim}\n\nNow you know how to remove and revert patches. You can also compare\npatch levels. See the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/comparing-patch-levels}{reference\nguide} for a list of the available commands.\n\n\\section{Showing collisions between patches and deployed\nplugins}\\label{showing-collisions-between-patches-and-deployed-plugins}\n\nSome patches update files you might have customized via a plugin. The\n\\texttt{patching-tool\\ list-collisions} command lists differences\n(collisions) between installed patch files and your plugin's version of\nthem. Here's the command:\n\n\\begin{verbatim}\npatching-tool list-collisions\n\\end{verbatim}\n\nIt is an alias for the following diff command:\n\n\\begin{verbatim}\npatching-tool diff collisions files _base\n\\end{verbatim}\n\n\\texttt{\\_base} is the literal patch level name. Collisions are only\nlisted for installed patches that contain source code files.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} As of Patching Tool 2.0.9,\n\\texttt{patching-tool\\ list-collisions} lists only JSP file collisions\nin fragment bundles.\n\n\\noindent\\hrulefill\n\n\\section{Separating Patches from the\nInstallation}\\label{separating-patches-from-the-installation}\n\nThe Patching Tool's \\texttt{separate} command helps reduce the patched\nLiferay DXP installation size. If the installation has been patched, you\ncan make it smaller by moving the restore files out of it.\n\nPatched installations are large because the restore files are stored\ninside the web application's \\texttt{WEB-INF} folder by default. These\nfiles are required for patching the installation again.\n\nIf these files are removed, subsequent patching processes fail. Because\nof this, Liferay added an option to separate the patching files from the\ninstallation while still preserving and restoring them safely when new\npatches arrive. To do this, use this command:\n\n\\begin{verbatim}\npatching-tool separate [separation_name] \n\\end{verbatim}\n\nThis command produces a\n\\texttt{liferay-patching-files-{[}separation-name{]}.zip} file in the\nPatching Tool's \\texttt{patches} folder. It contains the necessary files\nand metadata for patching, verification, and validation. Once you create\nthis file, the patch files are removed from their default location and\nare now only available in this file. You can store this file elsewhere\nto reduce your installation's size.\n\n\\textbf{WARNING:} If the product is separated from its patches in this\nway, you cannot run most of the Patching Tool commands until the patches\nare restored.\n\nAfter the separation process only the following commands can be used:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{auto-discovery}\n\\item\n  \\texttt{info}\n\\item\n  \\texttt{setup}\n\\end{itemize}\n\nAny other command returns this:\n\n\\begin{verbatim}\nThis installation does not include data for patching. Please copy the\nliferay-patching-files-[separation-name].zip file into the 'patches' directory\nand run patching-tool setup. \n\\end{verbatim}\n\nThis is how you restore the patch files to your system. Details below.\n\n\\section{Restoring the Separated Patch\nFiles}\\label{restoring-the-separated-patch-files}\n\nWhen you need to patch Liferay DXP again, you must restore the separated\npatch artifact. To do this, copy the\n\\texttt{liferay-patching-files-{[}separation-name{]}.zip} back to the\nPatching Tool's \\texttt{patches} folder and run\n\\texttt{patching-tool\\ setup} command.\n\nThe command finds the necessary patching artifact and restores the patch\nfiles to the installation. After that, the Patching Tool works like it\ndid prior to separating the patches.\n\n\\chapter{Configuring the Patching\nTool}\\label{configuring-the-patching-tool}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe Patching Tool installs Liferay DXP patches. It ships with\nprepackaged Liferay DXP bundles. If any of the following scenarios\ndescribes your @product@ installation, however, you must configure the\nPatching Tool manually:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Installed Liferay DXP manually on an existing application server\n\\item\n  Customized your Liferay DXP folder structure\n\\item\n  Running in a cluster\n\\end{itemize}\n\nIf none of the above scenarios describe your installation, you can skip\nthis section.\n\nIf you installed Liferay DXP manually, you must also install the\nPatching Tool manually. Download it from the\n\\href{https://customer.liferay.com/downloads?p_p_id=com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_productAssetCategoryId=118191019&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_fileTypeAssetCategoryId=118191066}{Customer\nPortal}. Unzipping it to your\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\nfolder is the easiest way to use it.\n\nRead on to configure the Patching Tool for your environment.\n\n\\chapter{Patching Tool Basic\nconfiguration}\\label{patching-tool-basic-configuration}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThere are two ways to configure the Patching Tool:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Automatically by executing the \\texttt{auto-discovery} command\n\\item\n  Manually by editing the configuration file (see\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/patching-tool-advanced-configuration}{Patching\n  Tool Advanced Configuration})\n\\end{enumerate}\n\nAutomatic configuration generates the configuration files by looking for\nLiferay DXP files in the local file system. By default the Patching Tool\nlooks for them in its parent folder. To start the process, run this\ncommand in your Patching Tool folder (\\texttt{patching-tool}):\n\n\\begin{verbatim}\npatching-tool auto-discovery\n\\end{verbatim}\n\nIf Liferay DXP is not installed in the parent folder, specify its\nlocation:\n\n\\begin{verbatim}\npatching-tool auto-discovery /opt/liferay-dxp\n\\end{verbatim}\n\nIf you specified the wrong location of Liferay DXP or it is not in the\nparent folder, the Patching Tool can't find the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\nand reports an error like this:\n\n\\begin{verbatim}\nThe .liferay-home has not been detected in the given directory tree.\n\nConfiguration:\npatching.mode=binary\nwar.path=../tomcat-9.0.17/webapps/ROOT/\nglobal.lib.path=../tomcat-9.0.17/lib/ext/\nliferay.home=**[please enter manually]**\n\nThe configuration hasn't been saved. Please save this to the default.properties file.\n\\end{verbatim}\n\nHere are ways to resolve the Liferay Home issue:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Specify the Liferay Home path in the \\texttt{default.properties} file.\n\\item\n  If the Liferay Home is in the Patching Tool's tree, create a\n  \\texttt{.liferay-home} file in the Liferay Home folder and re-run the\n  auto-discovery process.\n\\end{itemize}\n\nWhen the Patching Tool is configured, running\n\\texttt{patching-tool\\ info} reports product version information.\n\nThat's it! Now that you've installed and configured the Patching Tool,\nyou're ready to download and install patches.\n\n\\chapter{Patching Tool Advanced\nConfiguration}\\label{patching-tool-advanced-configuration}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nBy default, the Patching Tool's configuration file called\n\\texttt{default.properties} is in the tool's folder.\n\nA Patching Tool configuration file typically looks like this:\n\n\\begin{verbatim}\npatching.mode=binary\nwar.path=../tomcat-9.0.17/webapps/ROOT/\nglobal.lib.path=../tomcat-9.0.17/lib/ext/\nliferay.home=../\n\\end{verbatim}\n\nThe properties above (described fully in\n\\href{/docs/7-2/deploy/-/knowledge_base/d/patching-tool-configuration-properties}{Patching\nTool Configuration Properties}) define the location of\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home},\nthe patching mode (binary or source), the path to where WAR files are\ndeployed in the app server, and the global library path. The tool's\nauto-discovery bases the OSGi module framework paths on the Liferay\nHome. If, however, you changed the OSGi module framework paths to\nsomething different than those under the default folder\n\\texttt{{[}Liferay\\ Home{]}/osgi}, you must manually specify the\nfollowing properties:\n\n\\begin{verbatim}\nmodule.framework.core.path=path_to_modules_core_dir\nmodule.framework.marketplace.path=path_to_modules_marketplace_dir\nmodule.framework.modules.path=path_to_modules_modules_dir\nmodule.framework.portal.path=path_to_modules_portal_dir\nmodule.framework.static.path=path_to_modules_static_dir\n\\end{verbatim}\n\nUsing auto-discovery and working with the default profile\n\\texttt{default.properties} is the easiest way to use the Patching Tool,\nand is great for smaller, single server installations. But many Liferay\nDXP installations serve millions of pages per day, and the Patching Tool\nhas been designed for this as well. So if you're running a small,\nmedium, or large cluster of Liferay DXP machines, you can use the\nPatching Tool profiles to manage patching for all of them.\n\n\\section{Using Profiles with the Patching\nTool}\\label{using-profiles-with-the-patching-tool}\n\nYou can create profiles for multiple runtimes by running auto-discovery\nor creating them manually. To auto-discover other runtimes, run the\nPatching Tool with parameters like this:\n\n\\begin{verbatim}\n./patching-tool.sh [name of profile] auto-discovery [path/to/Liferay Home]\n\\end{verbatim}\n\nThis runs the same discovery process, but on the path you specify. It\nwrites the profile information to a file called\n\\texttt{{[}name\\ of\\ profile{]}.properties}. Alternatively, you can\nmanually create profile property files in your \\texttt{patching-tool}\nfolder.\n\nSee\n\\href{/docs/7-2/deploy/-/knowledge_base/d/patching-tool-configuration-properties}{Patching\nTool configuration properties} (profile properties) for a complete list\nof the available configuration properties.\n\nYou can have as many profiles as you want and use the same Patching Tool\nto patch all of them. This helps to keep all your installations in sync.\n\n\\chapter{Installing patches on the 7.0\nWAR}\\label{installing-patches-on-the-7.0-war}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nIf you\n\\href{/docs/7-1/deploy/-/knowledge_base/d/installing-product-on-tomcat}{installed\nLiferay DXP manually} as a WAR file on a supported application server,\nyou must apply patches to the WAR file and supporting files and\nre-deploy them. This article shows you how to do that.\n\n\\section{Prerequisites}\\label{prerequisites-1}\n\nDownload the necessary artifacts from the\n\\href{https://customer.liferay.com/downloads}{Customer Portal:}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay DXP WAR file (\\texttt{liferay-dxp-{[}version{]}.war})\n\\item\n  Dependencies ZIP file\n  (\\texttt{liferay-dxp-dependencies-{[}version{]}.zip})\n\\item\n  OSGi JARs ZIP file (\\texttt{liferay-dxp-osgi-{[}version{]}.zip})\n\\item\n  \\href{https://customer.liferay.com/downloads?p_p_id=com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_productAssetCategoryId=118191019&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_fileTypeAssetCategoryId=118191066}{Latest\n  Patching Tool}\n\\end{itemize}\n\n\\section{Install the patch on the Liferay DXP WAR and\nartifacts}\\label{install-the-patch-on-the-liferay-dxp-war-and-artifacts}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create an arbitrary folder. Unzip the dependency artifacts and the\n  Patching Tool into it. The folder contents should look like this:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{{[}patching-home{]}/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{liferay-dxp-dependencies-{[}version{]}/} ← Unzipped\n      Dependencies\n    \\item\n      \\texttt{osgi/} ← Unzipped OSGi JARs\n    \\item\n      \\texttt{patching-tool/} ← Unzipped Patching Tool\n    \\item\n      \\texttt{liferay-dxp-{[}version{]}.war/} ← Liferay DXP WAR File\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  Create the default profile configuration file in the Patching Tool\n  folder: \\texttt{patching-home/patching-tool/default.properties}. The\n  contents should look like this:\n\\end{enumerate}\n\n\\begin{verbatim}\npatching.mode=binary\nwar.path=../../patching-home/liferay-dxp-[version].war\nglobal.lib.path=../../patching-home/liferay-dxp-dependencies-[version]\nliferay.home=../../patching-home\n\\end{verbatim}\n\n\\begin{verbatim}\nIf you're using a different OSGi folder structure, you can specify it as\nthe [Patching Tool Advanced Configuration](/docs/7-2/deploy/-/knowledge_base/d/patching-tool-advanced-configuration)\ndocumentation describes: \n\\end{verbatim}\n\n\\begin{verbatim}\nmodule.framework.core.path=/osgi-home/osgi/core\nmodule.framework.marketplace.path=/osgi-home/osgi/marketplace\nmodule.framework.modules.path=/osgi-home/osgi/modules\nmodule.framework.portal.path=/osgi-home/osgi/portal\nmodule.framework.static.path=/osgi-home/osgi/static \n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  Download the patch (fix pack or hotfix) to install and put it in a\n  folder called \\texttt{patches} in your Patching Tool folder\n  (i.e.~\\texttt{{[}patching-home{]}/patching-tool/patches}).\n\\item\n  Execute the Patching Tool's \\texttt{info} command:\n\\end{enumerate}\n\n\\begin{verbatim}\n/patching-home/patching-tool> patching-tool info\nLoading product and patch information...\nProduct information:\n  * installation type: binary\n  * build number: 7210\n  * service pack version:\n    - available SP version: Not available\n    - installable SP version: Not available\n  * patching-tool version: 2.0.12\n  * time: 2019-06-03 18:30Z\n  * host: 91WRQ72 (8 cores)\n  * plugins: no plugins detected\n\nCurrently installed patches: -\n...\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{4}\n\\tightlist\n\\item\n  Install the patch.\n\\end{enumerate}\n\n\\begin{verbatim}\n/patching-home/patching-tool> patching-tool.sh  install\nOne patch is ready to be installed. Applying dxp...\nCleaning up: [1%..10%..20%..30%..40%..50%..60%..70%..80%..90%..100%]\nInstalling patches: [1%..10%..20%..30%..40%..50%..60%..70%..80%..90%...100%]\nThe installation was successful. One patch is installed on the system.\n\\end{verbatim}\n\nGreat! You have successfully patched the artifacts, and they are ready\nto be deployed on any supported Application Server.\n\n\\section{Related Topics}\\label{related-topics-9}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/patching-tool-advanced-configuration}{Patching\nTool Advanced Configuration}\n\n\\href{/docs/7-2/deploy/-/knowledge_base/d/deploying-product}{Deploying\nLiferay DXP}\n\n\\chapter{Keeping up with Fix packs and Service\nPacks}\\label{keeping-up-with-fix-packs-and-service-packs}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe \\emph{Announcements} section on\n\\href{https://help.liferay.com/hc}{Liferay's Help Center page} lists all\nfix pack updates, security alerts, product releases, and system updates.\nThe approximate frequency of fix pack and service pack releases is\nexplained\n\\href{/docs/7-2/deploy/-/knowledge_base/d/patching-basics}{here}. The\n\\emph{Receive Notifications} sidebar lets you subscribe to the latest\nupdates on products, patches, and system improvements.\n\nClick \\emph{Downloads} on the Liferay Digital Experience Platform page\nto access:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Latest Release\n\\item\n  Fix Packs\n\\item\n  Service Packs Archive\n\\item\n  Security Advisories\n\\item\n  Patching Tool\n\\end{itemize}\n\nClick \\emph{Support Information} to access the compatibility matrix,\nsupport FAQs, and more.\n\n\\chapter{Backing up a Liferay DXP\nInstallation}\\label{backing-up-a-liferay-dxp-installation}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nOnce you have an installation of Liferay DXP running, you should\nimplement a comprehensive backup plan. In case some kind of catastrophic\nhardware failure occurs, you'll be thankful to have backups and\nprocedures for restoring Liferay DXP from one of them. @product@ isn't\nvery different from other Java web applications that might be running on\nyour application server. Nevertheless, there are some specific\ncomponents you should include in your backup plan.\n\nThe recommended backup plan includes backing up these things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Source code\n\\item\n  Liferay DXP's file System\n\\item\n  Liferay DXP's database\n\\end{itemize}\n\n\\section{Backing up Source Code}\\label{backing-up-source-code}\n\nIf you have extended Liferay DXP or have written any plugins, they\nshould be stored in a source code repository such as Git, Subversion, or\nCVS, unless you're Linus Torvalds, and then tarballs are okay too\n(that's a joke). You should back up your source code repository on a\nregular basis to preserve your ongoing work. This probably goes without\nsaying in your organization since nobody wants to lose source code\nthat's taken months to produce. Thus you should include source code in\nyour Liferay DXP backup plan.\n\nNext, let's examine the Liferay DXP installation items you should back\nup.\n\n\\section{Backing up Liferay DXP's File\nSystem}\\label{backing-up-liferay-dxps-file-system}\n\nThe \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home\nfolder} stores Liferay DXP's properties configuration files, such as\n\\texttt{portal-setup-\\ wizard.properties} and\n\\texttt{portal-ext.properties}. You should absolutely back them up. In\nfact, it's best to back up your entire application server and Liferay\nHome folder contents.\n\nLiferay DXP stores configuration files, search indexes, and cache\ninformation in Liferay Home's \\texttt{/data} folder. If you're using the\nFile System store or the Advanced File System store, the documents and\nmedia repository is also stored here by default. It's always important\nto back up your \\texttt{/data} folder.\n\nThe files that comprise Liferay DXP's OSGi runtime are stored in Liferay\nHome's \\texttt{/osgi} folder. It contains all of the app and module JAR\nfiles deployed to Liferay DXP. The \\texttt{/osgi} folder also contains\nother required JAR files, configuration files, and log files. It's also\nimportant to back up your \\texttt{/osgi} folder.\n\nLiferay Home's \\texttt{/logs} folder contains Liferay DXP's log files.\nIf a problem occurs on Liferay DXP, the @product@ log files often\nprovide valuable information for determining what went wrong. The\n\\texttt{/data}, \\texttt{/osgi}, and \\texttt{/logs} folders are all\ncontained in the Liferay Home folder. Thus, if you're backing up both\nyour application server folder and your Liferay Home folder, you're in\ngood shape.\n\nRemember that if you've configured the document library to store files\nto a location other than the default location, you should also back up\nthat location.\n\nThat covers the Liferay DXP file system locations you should back up.\nNext, let's discuss how to back up Liferay DXP's database.\n\n\\section{Backing up Liferay DXP's\nDatabase}\\label{backing-up-liferay-dxps-database}\n\nLiferay DXP's database is the central repository for all of the portal's\ninformation. It's the most important component to back up. You can back\nup the database live (if your database allows this) or by exporting\n(dumping) the database into a file and then backing up the exported\nfile. For example, MySQL ships with a \\texttt{mysqldump} utility which\nlets you export the entire database and data into a large SQL file. This\nfile can then be backed up. On restoring the database you can import\nthis file into the database to recreate the database state to that of\nthe time you exported the database.\n\nIf you're storing Liferay DXP's Documents and Media Library files to a\nJackrabbit JSR-170 repository database, you should back it up. If you've\nplaced your search index into a database (not recommended; see the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering}{Liferay\nDXP Clustering} article for information on using Cluster Link or Solr),\nyou should back up that database too.\n\nIf you wish to avoid re-indexing your content after restoring your\ndatabase, back up your search indexes. This is easiest to do if you have\na separate Elastic or Solr environment on which your index is stored. If\nyou're in a clustered configuration and you're replicating indexes,\nyou'll need to back up each index replica.\n\nRestoring your application server, your Liferay Home folder, the\nlocations of any file system-based media repositories, and your database\nfrom a backup system should give you a functioning portal. Restoring\nsearch indexes should avoid the need to re-index when you bring your\nsite back up after a catastrophic failure. Good, consistent backup\nprocedures are key to recovering successfully from a hardware failure.\n\n\\chapter{Monitoring Liferay DXP}\\label{monitoring-liferay-dxp}\n\nThese articles show you how to monitor Liferay DXP. Monitoring vital\nstatistics such as Java memory heaps, garbage collection, database\nconnection pools, and the application server helps you optimize\nperformance. Better monitoring means better tuning and thus avoids\ndangerous runtime scenarios like out of memory errors and wasted heap\nspace.\n\nYou'll learn basic monitoring techniques, such as\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Using the Visual VM tool and the JMX Console\n\\item\n  Garbage Collection\n\\end{itemize}\n\nRead on to learn more about monitoring Liferay DXP!\n\n\\chapter{Monitoring Garbage Collection and the\nJVM}\\label{monitoring-garbage-collection-and-the-jvm}\n\nAlthough the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/tuning-guidelines}{tuning\nparameters} give you a good start to JVM tuning, you must monitor GC\nperformance to ensure you have the best settings to meet your needs.\nThere are several tools to help you monitor Oracle JVM performance.\n\n\\section{VisualVM}\\label{visualvm}\n\n\\href{https://visualvm.github.io/}{VisualVM} provides a centralized\nconsole for viewing Oracle JVM performance information and its Visual GC\nplugin shows garbage collector activities.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/visual-vm-gc.png}}\n\\caption{VisualVM's Visual GC plugin shows the garbage collector in\nreal-time.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Oracle's JDK has VisualVM bundled\n(\\texttt{\\$JAVA\\_HOME/bin/jvisualvm}). However, always download and use\nthe latest version from VisualVM's\n\\href{https://visualvm.github.io/}{official website}.\n\n\\noindent\\hrulefill\n\n\\section{JMX Console}\\label{jmx-console}\n\nThis tool helps display various statistics like Liferay DXP's\ndistributed cache performance, application server thread performance,\nJDBC connection pool usage, and more.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The JMX Console is the preferred tool for monitoring\napplication server performance.\n\n\\noindent\\hrulefill\n\nTo enable JMX connections, add these JVM arguments:\n\n\\begin{verbatim}\n-Dcom.sun.management.jmxremote=true\n-Dcom.sun.management.jmxremote.port=5000\n-Dcom.sun.management.jmxremote.authenticate=false\n-Dcom.sun.management.jmxremote.ssl=false\n\\end{verbatim}\n\nIf you're running JMX Console from a another machine, add these JVM\narguments too:\n\n\\begin{verbatim}\n-Dcom.sun.management.jmxremote.local.only=false\n-Dcom.sun.management.jmxremote.rmi.port=5000\n-Djava.rmi.server.hostname=[place IP address here]\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/visual-vm-jmx.png}}\n\\caption{VisualVM monitors the JVM using Java Management Extensions.}\n\\end{figure}\n\n\\section{Garbage Collector Verbose\nLogging}\\label{garbage-collector-verbose-logging}\n\nAdd these JVM arguments to activate verbose logging for the JVM garbage\ncollector.\n\n\\begin{verbatim}\n-verbose:gc -Xloggc:/tmp/liferaygc1.log -XX:+PrintGCDetails \n-XX:+PrintGCCause -XX:+PrintGCApplicationConcurrentTime \n-XX:+PrintGCApplicationStoppedTime\n\\end{verbatim}\n\nExamining these logs helps you tune the JVM properly.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Adding these JVM arguments generates a heap dump if an\n\\texttt{OutOfMemoryError} occurs. The dump is written to the heap dump\npath specified. Specify the path to use:\n\n\\texttt{-XX:+HeapDumpOnOutOfMemoryError\\ -XX:HeapDumpPath=/heap/dump/path/}\n\n\\noindent\\hrulefill\n\nGarbage collector log files can grow huge. You can use additional\narguments like the following ones to rotate the log to a new log file\nwhen the current log file reaches a maximum size:\n\n\\begin{verbatim}\n-XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 \n-XX:GCLogFileSize=50M\n\\end{verbatim}\n\nThese arguments rotate the logs to up to \\texttt{10} log files with a\nmaximum size of \\texttt{50M} each.\n\nNow you can monitor garbage collection in the JVM and tune it for top\nperformance.\n\n\\chapter{Managing Liferay DXP with Liferay Connected\nServices}\\label{managing-liferay-dxp-with-liferay-connected-services}\n\nLiferay Connected Services (LCS) is a set of tools and services for\nmanaging and monitoring your Liferay DXP instances. LCS can help you\ninstall fix packs, monitor your instances' performance, activate your\ninstances, and help you manage your subscriptions. In other words, LCS\nis like a butler for the mansion that is Liferay DXP. It's like having a\nsingle butler that can serve several mansions at once!\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nBefore going any further, you should take note of a few key terms used\nthroughout this guide:\n\n\\textbf{Project:} Represents a group of users belonging to a company or\norganization. For example, a project can consist of all the users from a\nproject team or business unit, or it can include the entire company.\n\n\\textbf{Environment}: Represents a physical cluster of servers or a\nvirtual or logical aggregation of servers.\n\n\\textbf{Server}: Describes a concrete Liferay DXP instance. It can be a\nstandalone server or a cluster node.\n\nAs you go through this guide, you'll cover the following topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/getting-started-with-lcs}{Getting\n  Started}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/lcs-preconfiguration}{LCS\n  Preconfiguration}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/activating-your-liferay-dxp-server-with-lcs}{Registering\n  Your Liferay DXP Server with LCS}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/using-lcs}{Using LCS}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/troubleshooting-your-lcs-connection}{Troubleshooting\n  Your LCS Connection}\n\\end{itemize}\n\nYou'll get started with the configuration steps required to use LCS with\nLiferay DXP.\n\n\\chapter{Getting Started with LCS}\\label{getting-started-with-lcs}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nTo use LCS, you must register a server in an LCS environment. An LCS\nenvironment represents a physical cluster of servers or a virtual or\nlogical aggregation of servers. Each environment is part of an LCS\nproject. An LCS project represents a group of users belonging to a\ncompany or organization. For example, a project can consist of all the\nusers from a project team or business unit, or it can include the entire\ncompany.\n\nLCS projects don't initially contain any environments. You must\ntherefore create one before you can register any servers in LCS. The\nfirst time you log in to\n\\href{https://lcs.liferay.com}{lcs.liferay.com}, LCS presents you with a\nwizard that walks you through the environment creation process. Click\n\\emph{Get Started} to begin.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-onboarding-00.png}}\n\\caption{Click \\emph{Get Started} to begin the wizard.}\n\\end{figure}\n\nEach of these steps corresponds to a step in the wizard:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select the LCS project for your new environment. You can select any of\n  your available LCS projects. Note that each project lists its\n  available subscriptions and whether it supports elastic subscriptions.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-onboarding-01.png}}\n  \\caption{Select the LCS project you want to create the environment in,\n  and click \\emph{Next}.}\n  \\end{figure}\n\\item\n  Name and describe the environment. The name is mandatory, but the\n  description is optional. Although you can enter anything you wish in\n  these fields, it's best to choose a name and description that\n  accurately identify the environment (e.g., Development, Production,\n  Test, etc.). Note that you can change these values after creating the\n  environment.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-onboarding-02.png}}\n  \\caption{Name and describe the environment, then click \\emph{Next}.}\n  \\end{figure}\n\\item\n  Select the environment's subscription type from the project's\n  available subscriptions. Even if you won't use LCS to activate the\n  servers defined for this environment, you must still select a\n  subscription type. Also note that you can't change this selection\n  after creating the environment.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-onboarding-03.png}}\n  \\caption{Select the environment's subscription type, then click\n  \\emph{Next}.}\n  \\end{figure}\n\\item\n  Select whether servers that connect to this environment are part of a\n  cluster. LCS provides additional tools in clustered environments that\n  help you manage the cluster. For example, clustered environments show\n  cluster-specific metrics, and fix packs apply to all cluster nodes.\n  There are a few things to keep in mind if you set the environment as\n  clustered:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    You can't change this setting after creating the environment.\n  \\item\n    Each clustered environment can only support nodes that belong to a\n    single cluster. To connect a different cluster's nodes, you must\n    create a separate clustered environment exclusively for those nodes.\n  \\item\n    You must set the portal property \\texttt{cluster.link.enabled} to\n    \\texttt{true} in any servers that connect to a clustered\n    environment.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-onboarding-04.png}}\n  \\caption{Select whether this is a clustered environment, then click\n  \\emph{Next}.}\n  \\end{figure}\n\\item\n  Select whether the environment allows elastic subscriptions. Elastic\n  subscriptions let you register an unlimited amount of servers. This is\n  critical for auto-scaling situations in which servers are created and\n  destroyed automatically in response to demand. Elastic environments\n  are also useful for bringing additional servers online on a temporary\n  basis for any other purpose, such as business continuity planning. For\n  more information, see\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/managing-liferay-dxp-subscriptions\\#elastic-subscriptions}{the\n  documentation on elastic subscriptions}. Also note that you can't\n  change this selection after creating the environment.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-onboarding-05.png}}\n  \\caption{Select whether this is an elastic environment, then click\n  \\emph{Next}.}\n  \\end{figure}\n\\item\n  Enable the LCS service you want to use with servers that connect to\n  this environment. The following service is available:\n\n  \\textbf{Liferay Instance Activation:} Enabling this lets LCS activate\n  any Liferay DXP instances that connect to the environment. If you\n  disable this service, you must activate via an XML file from Liferay\n  support, and such instances must run version 5.0.0 or newer of the LCS\n  client app.\n\n  Note that you \\textbf{must} use LCS for activation of Elastic\n  subscriptions. Otherwise, you don't have to use LCS for activation.\n\n  Portal Analytics, Fix Pack Management and Portal Properties Analysis\n  have been removed from the list of available services. For more\n  information about this change, please read\n  \\href{https://help.liferay.com/hc/en-us/articles/360037317691-Liferay-Connected-Services-Feature-Deprecation-Update-March-2020}{this\n  article}\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-onboarding-06.png}}\n  \\caption{Enable or disable the LCS services you want to use for\n  servers that connect to the environment, then click \\emph{Next}.}\n  \\end{figure}\n\\item\n  A completed form presents your selections. Review them and make any\n  changes that you want. When you're finished, click \\emph{Create\n  Environment}.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-onboarding-07.png}}\n  \\caption{This form contains each of your selections from the previous\n  steps. Make any changes you want, then click \\emph{Create\n  Environment}.}\n  \\end{figure}\n\\end{enumerate}\n\nAfter creating your environment, the wizard shows a screen that lets you\ndownload the LCS client app, download the environment's token file, and\ngo to your project's dashboard in LCS. Before registering a server in\nyour new environment, however, you must complete the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/lcs-preconfiguration}{preconfiguration\nsteps} for that server.\n\n\\chapter{LCS Preconfiguration}\\label{lcs-preconfiguration}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nBefore registering your server with LCS, there are a few things you must\nconfigure. The sections in this guide walk you through these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  \\hyperref[downloading-the-lcs-client-app]{Downloading the LCS Client\n  App}\n\\item\n  \\hyperref[preconfiguring-lcs-to-connect-through-a-proxy]{Preconfiguring\n  LCS to Connect Through a Proxy}\n\\item\n  \\hyperref[ensuring-access-to-lcs]{Ensuring Access to LCS}\n\\item\n  \\hyperref[ntp-server-synchronization]{NTP Server Synchronization}\n\\item\n  \\hyperref[configuring-websphere]{Configuring WebSphere}: This is only\n  necessary if you're running Liferay DXP on the WebSphere application\n  server.\n\\item\n  \\hyperref[installing-the-lcs-client-app]{Installing the LCS Client\n  App}\n\\end{enumerate}\n\n\\hyperref[upgrading-the-lcs-client-app]{The last section} in this guide\nshows you how to upgrade the LCS client app once your server is\nregistered with LCS. We highly recommend that you upgrade the app\nwhenever Liferay releases a new version of it.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You must use LCS for activation of Elastic subscriptions.\nOtherwise, you don't have to use LCS for activation. You can instead\nrequest an XML activation key from Liferay Support.\n\n\\noindent\\hrulefill\n\n\\section{Downloading the LCS Client\nApp}\\label{downloading-the-lcs-client-app}\n\nThe LCS client app is included in each Liferay DXP bundle and\nautodeploys when the bundle starts. The included version of the app,\nhowever, may be outdated. To get the latest version of the LCS client\napp, you must first download it via Liferay Marketplace.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Even though Liferay Marketplace and this guide use the\nterm \\emph{purchase}, the LCS client app is free of charge. The purchase\nprocess for a free app in Liferay Marketplace adds the app to your\nLiferay project, much like downloading a free app in a mobile app store\nadds the app to your account.\n\n\\noindent\\hrulefill\n\nUse these steps to purchase and download the app (if you've already\npurchased the app, you can skip to step 3 to download it):\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to\n  \\href{https://web.liferay.com/marketplace/-/mp/application/71774947}{the\n  LCS client app in Liferay Marketplace}. Sign in to Marketplace, then\n  click the LCS client app's \\emph{Free} button.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-client-app-marketplace.png}}\n  \\caption{Click the app's \\emph{Free} button to begin the purchase\n  process.}\n  \\end{figure}\n\\item\n  Select your project, accept the license agreement, and then click the\n  \\emph{Purchase} button. Marketplace then displays your receipt.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-client-app-receipt.png}}\n  \\caption{Liferay Marketplace displays your receipt for the LCS client\n  app.}\n  \\end{figure}\n\\item\n  On the receipt, click \\emph{See Purchased}. This shows where you can\n  download the LCS client app. To download the app, click the \\emph{App}\n  button next to the latest version of the app.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** If you must download the LCS client app later, such as when\n [upgrading it](#upgrading-the-lcs-client-app), \n select *Purchased Apps* from the User menu at the top-right of Liferay \n Marketplace. On the Purchased Apps screen, select the project you \n associated with the LCS client app and then select the app. This takes you \n to the same downloads page shown in the screenshot. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n![ Click the *App* button next to the version of the app you want to download.](./images/lcs-client-download-page.png){./images/lcs-client-download-page.png)\n\\end{verbatim}\n\nGreat! You've successfully downloaded the LCS client app. Before\ninstalling it, however, there are a few additional pre-configuration\nsteps you should complete. These appear next; then you'll learn how to\ninstall the app.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If your server connects to the Internet through a proxy,\nyou must configure your server or the LCS client app \\textbf{before}\ndeploying the app. The following section contains instructions on this.\nIf your server doesn't connect through a proxy, skip this section.\n\n\\noindent\\hrulefill\n\n\\section{Preconfiguring LCS to Connect Through a\nProxy}\\label{preconfiguring-lcs-to-connect-through-a-proxy}\n\nIf your server connects to the Internet through a proxy, you must set\nsome properties \\textbf{before} deploying the LCS client app. There are\ntwo ways to do so---chose only one:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\hyperref[jvm-app-server-arguments]{As JVM app server arguments}.\n\\item\n  \\hyperref[lcs-client-app-properties]{As LCS client app properties}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Use only one of these methods to configure your server to\nconnect through a proxy.\n\n\\noindent\\hrulefill\n\n\\section{JVM App Server Arguments}\\label{jvm-app-server-arguments}\n\nTo set the proxy properties in your server, set them as JVM app server\narguments. Set these properties to the appropriate values for your\nproxy:\n\n\\begin{verbatim}\n-Dhttp.proxyHost=\n-Dhttp.proxyPort=\n-Dhttp.proxyUser=\n-Dhttp.proxyPassword=\n-Dhttps.proxyHost=\n-Dhttps.proxyPort=\n\\end{verbatim}\n\nNote that the \\texttt{user}, \\texttt{password}, and \\texttt{https}\nproperties are only needed if your proxy requires authentication.\n\n\\section{LCS Client App Properties}\\label{lcs-client-app-properties}\n\nTo set the proxy properties via the LCS client app, you must create and\ndeploy a config file containing the properties. Follow these steps to do\nso:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create the config file\n  \\texttt{com.liferay.lcs.client.configuration.LCSConfiguration.config}.\n  In the steps that follow, you'll set the proxy properties in this\n  file.\n\\item\n  Set these \\texttt{proxy*} properties to the appropriate values for\n  your proxy:\n\n\\begin{verbatim}\nproxyHostName=\"\"\nproxyHostPort=\"\"\n\\end{verbatim}\n\\item\n  If your proxy requires authentication, pass the credentials via these\n  properties:\n\n\\begin{verbatim}\nproxyHostLogin=\"\"\nproxyHostPassword=\"\"\n\\end{verbatim}\n\\item\n  If your proxy requires NTLM authentication, you must also populate\n  these properties:\n\n\\begin{verbatim}\nproxyAuthType=\"ntlm\"\nproxyDomain=\"\"\nproxyWorkstation=\"\"\n\\end{verbatim}\n\n  Be sure to set \\texttt{proxyDomain} and \\texttt{proxyWorkstation} to\n  the appropriate values for your proxy. Note that you can leave\n  \\texttt{proxyWorkstation} blank if you don't need it.\n\\item\n  Deploy the config file to \\texttt{osgi/configs}.\n\\end{enumerate}\n\n\\section{Ensuring Access to LCS}\\label{ensuring-access-to-lcs}\n\nFor the LCS client app to work, it must be able to access the following\nDNS names. If your server is behind a proxy and/or a firewall, then you\nmust open access to these:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{lcs.liferay.com}\n\\item\n  \\texttt{lcs-gateway.liferay.com}\n\\end{itemize}\n\nAs an added security measure, you can also restrict traffic to HTTPS.\n\nThe next section discusses NTP server synchronization.\n\n\\section{NTP Server Synchronization}\\label{ntp-server-synchronization}\n\nFor LCS to work properly, the application server running Liferay DXP\nshould be synchronized with a time server. If it's not, you may get log\nerrors similar to these:\n\n\\begin{verbatim}\nERROR [pool-6-thread-3][HandshakeTask:68] java.lang.RuntimeException: Handshake expired. \nCheck that the server is synchronized with an NTP server. \n\nWARN [liferay/hot_deploy-1][LCSHotDeployMessageListener:186] LCS portlet is not connected \njava.lang.RuntimeException: com.liferay.jsonwebserviceclient.JSONWebServiceInvocationException: \ncom.fasterxml.jackson.core.JsonParseException: Unrecognized token 'oauth_problem': was expecting \n('true', 'false' or 'null')_ at [Source: oauth_problem=timestamp_refused&oauth_acceptable_timestamps=1477311475-1477312075; \nline: 1, column: 14] [Sanitized]\n\\end{verbatim}\n\nFor information on how to synchronize your application server with a\ntime server, see your application server's documentation.\n\n\\section{Configuring WebSphere}\\label{configuring-websphere}\n\nIBM ® WebSphere ® is a trademark of International Business Machines\nCorporation, registered in many jurisdictions worldwide.\n\nIf you're running the WebSphere application server, then there are some\nadditional configuration steps you must take before deploying the LCS\nclient app:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Shut down the application server.\n\\item\n  Add these properties in a \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\nmodule.framework.properties.org.osgi.framework.bootdelegation=\\\n    __redirected,\\\n    com.sun.ccpp,\\\n    com.sun.ccpp.*,\\\n    com.liferay.aspectj,\\\n    com.liferay.aspectj.*,\\\n    com.liferay.portal.servlet.delegate,\\\n    com.liferay.portal.servlet.delegate*,\\\n    com.sun.crypto.*,\\\n    com.sun.image.*,\\\n    com.sun.jmx.*,\\\n    com.sun.jna,\\\n    com.sun.jndi.*,\\\n    com.sun.mail.*,\\\n    com.sun.management.*,\\\n    com.sun.media.*,\\\n    com.sun.msv.*,\\\n    com.sun.org.*,\\\n    com.sun.syndication,\\\n    com.sun.tools.*,\\\n    com.sun.xml.*,\\\n    com.yourkit.*,\\\n    com.ibm.crypto.*,\\\n    sun.*,\\\n    javax.validation,\\\n    javax.validation.*,\\\n    jdk.*,\\\n    weblogic.jndi,\\\n    weblogic.jndi.*\\\n\\end{verbatim}\n\\item\n  In your Liferay DXP installation, delete the \\texttt{osgi/state}\n  folder.\n\\item\n  Start the application server.\n\\item\n  Navigate to the WebSphere console in a browser.\n\\item\n  Select your server and navigate to \\emph{Java and Process Management}\n  → \\emph{Process Definition} → \\emph{Additional Properties}.\n\\item\n  Select \\emph{Java Virtual Machine} → \\emph{Custom Properties}.\n\\item\n  Click \\emph{New}, and enter the following:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Name: \\texttt{com.ibm.crypto.provider.DoRSATypeChecking}\n  \\item\n    Value: \\texttt{false}\n  \\end{itemize}\n\\item\n  Click \\emph{Save}, then \\emph{OK} to apply changes to the master\n  configuration.\n\\end{enumerate}\n\nNote that for LCS client app versions prior to 5.0.0, you must also\nchange the value of the \\texttt{digital.signature.algorithm.provider}\nproperty in the app's \\texttt{portlet.properties} file to\n\\texttt{IBMJCE}:\n\n\\begin{verbatim}\ndigital.signature.algorithm.provider=IBMJCE\n\\end{verbatim}\n\n\\section{Installing the LCS Client\nApp}\\label{installing-the-lcs-client-app}\n\nOnce you've addressed the above pre-configuration steps, you're ready to\ninstall the LCS client app. Follow these steps to install the app:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  folder (usually the parent folder of the application server's folder),\n  delete this file:\n\n\\begin{verbatim}\nosgi/marketplace/Liferay Connected Services Client.lpkg\n\\end{verbatim}\n\\item\n  Place the new \\texttt{Liferay\\ Connected\\ Services\\ Client.lpkg} in\n  \\texttt{osgi/marketplace}.\n\\end{enumerate}\n\nGreat! Now you're all set to\n\\href{/docs/7-2/deploy/-/knowledge_base/d/activating-your-liferay-dxp-server-with-lcs}{register\nyour server with LCS}.\n\nThe next section shows you how to upgrade the LCS client app. We highly\nrecommend that you do this whenever Liferay releases a new version of\nthe app.\n\n\\section{Upgrading the LCS Client\nApp}\\label{upgrading-the-lcs-client-app}\n\nYour server should always be running the latest version of the LCS\nclient app. There are two ways to upgrade the app, depending on the\nexact LCS pre-configuration steps you followed:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Via Liferay Marketplace \\emph{inside} Liferay DXP. Use this method if\n  you don't need to configure the LCS client app (e.g., to connect\n  through a proxy) before it deploys.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** If you choose this method and have a clustered environment, you \n must perform the upgrade separately on each node in your cluster. \n Therefore, you may prefer to upgrade manually as detailed in the next step \n to ensure that all your nodes are running the exact same version of the \n LCS client app. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nTo perform the upgrade, first navigate to *Control Panel* &rarr; *Apps* \n&rarr; *Purchased*. Apps needing an update are listed first. Click *Update* \nnext to the LCS client app. Note that you may need to restart your server \nfor the upgrade to complete. \n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\tightlist\n\\item\n  Manually, after downloading the LCS client app's LPKG file to your\n  machine. Use this method if you must pre-configure the LCS client app\n  to connect through a proxy.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** If you used JVM app server arguments to configure your server to \n connect through a proxy, then you don't need to pre-configure the LCS \n client app to connect through the same proxy. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nTo update the LCS client app manually, follow the previous sections in this\nguide for downloading and pre-configuring the app. Then deploy it to\n`[Liferay Home]/deploy` as you would any other app. \n\\end{verbatim}\n\nContact Liferay Support if you need additional assistance with the\nupgrade process.\n\n\\chapter{Registering Your Liferay DXP Server with\nLCS}\\label{registering-your-liferay-dxp-server-with-lcs}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nFollow these steps to register your Liferay DXP server with LCS:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Ensure that you've completed the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/lcs-preconfiguration}{LCS\n  preconfiguration steps}.\n\\item\n  Log in to \\href{https://lcs.liferay.com}{lcs.liferay.com}. This takes\n  you to your company's LCS project. If your company has multiple\n  projects, from the menu to the right of the Dashboard tab select the\n  project that's getting a new server.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-select-project.png}}\n  \\caption{Select your LCS project from the menu highlighted by the red\n  box in this screenshot.}\n  \\end{figure}\n\\item\n  Select or create the environment in which to register this server. If\n  you're using LCS for activation, upon connection to LCS your server\n  consumes an activation key from the subscription type assigned to the\n  environment. Note that a subscription type can only be assigned to an\n  environment when creating the environment. If you have sufficient\n  permissions in your company's project, you can\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-environments\\#creating-environments}{create\n  a new environment} by selecting \\emph{Add Environment}.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-registration-select-environment.png}}\n  \\caption{You must register your server in an LCS environment. The red\n  box in this screenshot highlights environments.}\n  \\end{figure}\n\\item\n  Select the environment's \\emph{Registration} tab. This is where you\n  manage and download the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/understanding-environment-tokens}{environment's\n  token file}, that registers servers in the environment.\n\n  In the Registration tab's \\emph{Services} section, change the Liferay\n  Instance Activation setting, if needed. Note that if you change this\n  option and there are servers already registered in the environment,\n  you must regenerate the token file and use it to reconnect those\n  servers to LCS. You'll regenerate and/or download the token in the\n  next step.\n\n  Additionally, If you disable this service, you must activate via an\n  XML file from Liferay support, and such instances must run version\n  5.0.0 or newer of the LCS client app.\n\n  Liferay Instance Activation is either enabled or disabled for all\n  servers that connect to this environment. If Portal Property Analysis\n  is selected, you can prevent LCS environment.\n\n  \\begin{figure}\n  \\centering\n  \\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-registration.png}}\n  \\caption{An environment's Registration tab lets you manage the token\n  file used to register your server in the environment.}\n  \\end{figure}\n\\item\n  What you do now depends on what you did in the previous step:\n\n  \\textbf{Changes to Liferay Instance Activation:} Regenerate and\n  download the token. Regenerating a token causes all servers using the\n  old token to disconnect from LCS. You must reconnect them using the\n  new token.\n\n  \\textbf{No changes to LCS service selections:} Download the token.\n\\item\n  Place the token file in your server's\n  \\texttt{{[}Liferay\\ Home{]}/data} folder. Note that\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  is usually the parent folder of the application server's folder. If\n  your server is running, it should connect to LCS in about one minute.\n  If your server isn't running, it connects to LCS on startup.\n\\item\n  Celebrate! Your Liferay DXP server is registered in LCS. If for some\n  reason it isn't, see the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/troubleshooting-your-lcs-connection}{LCS\n  troubleshooting article}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You may be wondering what happens if LCS goes offline.\nDon't worry, this doesn't cause a rift in the space-time continuum. LCS\nis deployed on a global cloud infrastructure set up for automatic\nfailure recovery. The potential for non-availability is very low. In the\nevent of an outage, however, registered servers maintain a local copy of\ntheir uptime information to transmit to LCS when it comes back online.\nIf you use LCS for activation, active subscriptions have a 30-day grace\nperiod to re-establish connectivity and remain valid. This is ample time\nfor LCS to come back online.\n\n\\noindent\\hrulefill\n\n\\section{Determining Your Server's LCS Connection\nStatus}\\label{determining-your-servers-lcs-connection-status}\n\nIn Liferay DXP, you can view your LCS connection status in the LCS\nclient app. Access the client by clicking \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{Liferay Connected Services}.\n\nHere's a full description of what a connected LCS client app displays:\n\n\\textbf{Connection Uptime:} The duration of the client's connection with\nLCS.\n\n\\textbf{Last Message Received:} The time the LCS client received the\nlatest connection message from LCS. These messages occur only upon\nconnection/reconnection and are unrelated to server metrics. It's\ntherefore common for a long period of time to pass before the client\nreceives another such message for a reconnection event.\n\n\\textbf{Services:} The LCS services enabled for this server. Note that\nall servers in an environment use the same set of LCS services. LCS\nservices can't be controlled on a server-by-server basis.\n\nNote: Portal Analytics, Fix Pack Management and Portal Properties\nAnalysis have been removed from the list of available services. For more\ninformation about this change, please read\n\\href{https://help.liferay.com/hc/en-us/articles/360037317691-Liferay-Connected-Services-Feature-Deprecation-Update-March-2020}{this\narticle}\n\n\\textbf{Project Home:} A link to this server's LCS project.\n\n\\textbf{Environment:} A link to this server's LCS environment.\n\n\\textbf{Server Dashboard:} A link to the server on LCS.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-server-connected.png}}\n\\caption{The server is connected to LCS.}\n\\end{figure}\n\n\\chapter{Using LCS}\\label{using-lcs}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nOnce your Liferay DXP server is connected to LCS, you can get down to\nthe business that LCS is designed for---managing your servers. If you're\nnot already there, log in with your account on\n\\href{https://lcs.liferay.com}{lcs.liferay.com}. This is where you'll\nmanage environments, register servers and more.\n\nThis articles in this section each detail one or more of LCS's features:\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/what-lcs-stores-about-your-liferay-dxp-servers}{\\textbf{What\n  LCS Stores About Your Liferay DXP Servers:}} For LCS to work, the LCS\n  servers must store certain information about your servers. Sensitive\n  data, however, isn't stored on the LCS servers. This article describes\n  the data that LCS does and doesn't store.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-users-in-your-project}{\\textbf{Managing\n  LCS Users in Your Project:}} Learn how to manage your LCS project's\n  users by assigning them roles.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/using-the-dashboard}{\\textbf{Using\n  the Dashboard:}} Learn how to manage your LCS projects and access your\n  environments and servers in LCS.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-environments}{\\textbf{Managing\n  LCS Environments:}} Learn how to create and manage your LCS project's\n  environments. This includes instructions on generating tokens for an\n  environment's servers.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-servers}{\\textbf{Managing\n  LCS Servers:}} Learn how to manage your servers in LCS. This includes\n  viewing server status and editing server settings.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/managing-your-lcs-account}{\\textbf{Managing\n  Your LCS Account:}} Learn how to manage your LCS account. This\n  includes setting general account preferences, managing LCS web\n  notifications, and configuring LCS to send you notification emails\n  when specific events occur in your LCS projects.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/managing-liferay-dxp-subscriptions}{\\textbf{Managing\n  Liferay DXP Subscriptions:}} Learn how to view and manage your Liferay\n  DXP subscriptions for the servers in your LCS project.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/understanding-environment-tokens}{\\textbf{Understanding\n  Environment Tokens:}} Learn about the environment tokens that you use\n  to connect your servers to LCS.\n\\end{itemize}\n\n\\chapter{What LCS Stores About Your Liferay DXP\nServers}\\label{what-lcs-stores-about-your-liferay-dxp-servers}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nTo protect your users' privacy, LCS only stores system-specific data.\nLCS doesn't gather or store data on your users.\n\nBy default, LCS stores the following information about your server:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Portal build number and edition\n\\item\n  Patching Tool Version\n\\item\n  LCS Client Build Number\n\\item\n  Application Server Name\n\\item\n  Database Name\n\\item\n  File Encoding\n\\item\n  OS Name and Version\n\\item\n  Timezone\n\\item\n  IP Address\n\\item\n  Java Version and Java Options\n\\item\n  Number of Processor Cores\n\\item\n  File System Usage\n\\item\n  Memory Usage\n\\end{itemize}\n\nThe other data LCS stores depends on the services you enabled in your\nenvironment token, and whether your server was connected before certain\nservices were removed. For more information on this, see\n\\href{/docs/7-2/deploy/-/knowledge_base/d/activating-your-liferay-dxp-server-with-lcs}{Registering\nServers with LCS}. If you enabled the following services, LCS gathered\nand stored the data listed for each:\n\n\\begin{itemize}\n\\item\n  \\textbf{Portal analytics:}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Portal and portlet metrics\n  \\item\n    JVM metrics\n  \\item\n    Cache and server metrics\n  \\end{itemize}\n\\item\n  \\textbf{Fix pack management:}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Patches installed on the server\n  \\end{itemize}\n\\item\n  \\textbf{Portal properties analysis:}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{portal.properties} (except sensitive data)\n  \\end{itemize}\n\\end{itemize}\n\nSensitive data is any key-value pair that contains user names or\npasswords. For example, LCS did not store the following properties\nbecause they contain sensitive data:\n\n\\begin{verbatim}\nomniadmin.users\nldap.security.credentials.0, ldap.security.credentials.1, ldap.security.credentials.2 ...\nfacebook.connect.app.secret\nauth.token.shared.secret\nauth.mac.shared.key\ncaptcha.engine.recaptcha.key.private\namazon.secret.access.key\ntunneling.servlet.shared.secret\nmicrosoft.translator.client.secret\ndl.store.s3.secret.key\nauto.deploy.glassfish.jee.dm.passwd\n\\end{verbatim}\n\nLCS also did not store properties that end in \\texttt{.password},\nbesides the following non-sensitive properties:\n\n\\begin{verbatim}\nportal.jaas.plain.password\nportal.jaas.strict.password\nlogin.create.account.allow.custom.password\n\\end{verbatim}\n\nLCS also allowed you to prevent it from analyzing specific properties of\nyour choosing, by defining blacklisted properties.\n\nLCS is no longer gathering or storing the data listed above, that was\nassociated with enabled services.\n\n\\chapter{Managing LCS Users in Your\nProject}\\label{managing-lcs-users-in-your-project}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nThe Users section of LCS is where you manage the LCS users that are part\nof your project. It's here that you can grant or revoke LCS Roles. To\nmanage users, first click the \\emph{Users} tab just below the Dashboard\ntab on the upper-left of your screen.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can't add users to your project via the LCS UI or the\nLCS client app. To add users to your project, you must contact Liferay\nsupport.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-users.png}}\n\\caption{The Users tab lets you manage the LCS users in your project.}\n\\end{figure}\n\nThe \\emph{Users} tab displays a list of the users in your project. This\nlist includes each user's name, email, image, LCS Roles, and a\n\\emph{Manage Roles} button. Each LCS user must have an assigned Role.\nThe following Roles are available:\n\n\\textbf{LCS Administrator:} All LCS functionality is available to\nadministrators. This is the only Role that can manage other users'\nRoles.\n\n\\textbf{LCS Environment Manager:} All LCS functionality is available in\nthe scope of an environment, with the exception of managing other users.\n\n\\textbf{LCS Environment Viewer:} Has read-only access in the scope of an\nenvironment.\n\nYou should note that each of these LCS Roles assume users already have\nthe LCS User Role in their Liferay.com accounts. The LCS User Role is\ngranted automatically the first time a user logs into LCS. The actions\nthat can be performed by each of the LCS Roles are detailed in the below\npermissions matrix.\n\n\\textbf{LCS Permissions Matrix}\n\nAction \\textbar{} ~LCS Administrator \\textbar{} ~LCS Environment Manager\n\\textbar{} ~LCS Environment Viewer \\textbar{} Access LCS \\textbar{} true\n\\textbar{} true \\textbar{} true \\textbar{} Access Any Environment\n\\textbar{} true \\textbar{} false \\textbar{} false \\textbar{} Access a\nParticular Environment \\textbar{} true \\textbar{} true \\textbar{} true\n\\textbar{} Manage Users \\textbar{} true \\textbar{} false \\textbar{}\nfalse \\textbar{} Create and Delete Environments \\textbar{} true\n\\textbar{} false \\textbar{} false \\textbar{} Edit Any Environment\n\\textbar{} true \\textbar{} false \\textbar{} false \\textbar{} Edit a\nParticular Environment \\textbar{} true \\textbar{} true \\textbar{} false\n\\textbar{} Server Registration in Any Environment \\textbar{} true\n\\textbar{} false \\textbar{} false \\textbar{} Server Registration in a\nParticular Environment \\textbar{} true \\textbar{} true \\textbar{} false\n\\textbar{} Install Fix Packs in Any Environment \\textbar{} true\n\\textbar{} false \\textbar{} false \\textbar{} Install Fix Packs in a\nParticular Environment \\textbar{} true \\textbar{} true \\textbar{} false\n\\textbar{}\n\nNow that you know what Roles are available in an LCS project and what\nthey do, you're ready to learn how to manage them.\n\n\\section{Managing LCS Roles}\\label{managing-lcs-roles}\n\nFollow these steps to manage a user's LCS Roles:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the user's \\emph{Manage Roles} button.\n\\item\n  To revoke a Role, click \\emph{Revoke Role} for that Role.\n\\item\n  To assign a Role, choose the Role (and environment, if applicable) and\n  click \\emph{Assign}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} A user can't have an environment Role (e.g., LCS\nEnvironment Manager, LCS Environment Viewer) and the LCS Administrator\nRole at the same time.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-user-roles.png}}\n\\caption{You can assign or revoke a user's LCS Roles.}\n\\end{figure}\n\n\\chapter{Using the Dashboard}\\label{using-the-dashboard}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nThe LCS Dashboard shows a project's environments and servers. If you're\nnot already at the Dashboard, click it near the upper left-hand corner\nof your LCS site. Clicking \\emph{Dashboard} takes you to the project\nview. From there, you can get to the environment view and the server\nview. Each of these views gives you a different look into certain\naspects of your LCS project. You'll start with the project view.\n\n\\section{Using the Project View}\\label{using-the-project-view}\n\nYou can get to the project view at any time by clicking the\n\\emph{Dashboard} tab near the upper left-hand corner of your LCS site.\nThe project appears to the right of this tab, with a drop-down arrow for\nswitching between projects if you have more than one. You can also\nswitch between projects from the user menu at the top right of the\nDockbar. The project view contains a Status table that lists status\nmessages for each server in your project. For example, a status message\nappears for a server when the server is offline. Status messages also\nappear for servers when fix packs are available, monitoring is\nunavailable, the patching tool is unavailable, or other events occur\nthat relate to LCS.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-project-view.png}}\n\\caption{The LCS project view shows an overview of your LCS project.}\n\\end{figure}\n\nLCS lists the environments in your project on the left side of the\nscreen. You can also create new environments here by clicking the\n\\emph{Add Environment} tab (more on this shortly). To view an\nenvironment's settings, click the environment's gear icon. Clicking an\nenvironment shows more information about it. This takes you to the\nenvironment view. Also note that each environment's icon indicates the\nenvironment's type and status:\n\n\\textbf{Red icon:} There's a problem with one or more of the\nenvironment's servers.\n\n\\textbf{Green icon:} The environment's servers are operating properly.\n\n\\textbf{Icon with a circle:} The environment's servers are clustered.\n\n\\chapter{Managing LCS Environments}\\label{managing-lcs-environments}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nEnvironments are the key components of your LCS project. When you\nregister a server in LCS, you do so in an environment. An environment is\ntherefore the gateway to managing and monitoring your servers in LCS.\n\n\\section{Creating Environments}\\label{creating-environments}\n\nThe first time you log in to LCS, a wizard walks you through each step\nrequired to create your project's first environment. The\n\\href{/docs/7-2/deploy/-/knowledge_base/d/getting-started-with-lcs}{getting\nstarted article} explains this in detail. You can create additional\nenvironments via the same wizard or a simple form.\n\nTo create an environment, click the \\emph{Add Environment} button from\nthe Dashboard. This opens the New Environment form. Each section in this\nform corresponds to a step in the wizard. If you want to use the wizard\ninstead, click the \\emph{Open Wizard} link at the top of the form. See\nthe\n\\href{/docs/7-2/deploy/-/knowledge_base/d/getting-started-with-lcs}{getting\nstarted article} for a description of each setting in the form and\nwizard.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When creating an environment, make your selections\ncarefully for the \\emph{Subscription Type}, \\emph{Cluster}, and\n\\emph{Elastic} fields. You can't change them after creating the\nenvironment.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-new-environment.png}}\n\\caption{The New Environment form lets you create environments.}\n\\end{figure}\n\n\\section{Working with Environments}\\label{working-with-environments}\n\nClicking an environment on the left-hand side of the Dashboard takes you\nto the environment view, which lets you manage an environment in your\nLCS project.\n\nThe UI is segmented into three tabs:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{Fix Packs:} View and apply fix packs for the environment's\n  servers. This tab only appears if a server is registered in the\n  environment. A table displays the following information for each fix\n  pack:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Name:} The fix pack's name.\n  \\item\n    \\textbf{Status:} The fix pack's status.\n  \\item\n    \\textbf{Server:} The server the fix pack can be applied to.\n  \\item\n    \\textbf{Size:} The fix pack's size. This only appears if the server\n    is running.\n  \\item\n    \\textbf{Download:} A button to download the fix pack to the server.\n    This only appears if the server is running.\n  \\end{itemize}\n\n  Once a fix pack downloads, LCS prompts you to restart your server,\n  which installs any downloaded fix packs. Note that you must start your\n  server with the privileges required to write to the disk location\n  where patches are stored and processed (the \\texttt{patching-tool}\n  folder). To use LCS to install fix packs across a cluster, follow the\n  same procedure. LCS downloads and installs fix packs simultaneously\n  across all nodes---you don't have to handle each separately.\n\\item\n  \\textbf{Registration:} Generate and download\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/understanding-environment-tokens}{\\emph{environment\n  tokens}} that connect your servers to LCS.\n\\item\n  \\textbf{Environment Settings:} Change the environment's name,\n  location, and description. You can also see if the environment allows\n  clustered servers and view the environment's subscription type. Click\n  the \\emph{Save} button to save any changes you make in the Environment\n  Settings tab. You can also delete the environment by clicking\n  \\emph{Delete Environment}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-environment-view.png}}\n\\caption{The LCS environment view shows an overview of an LCS\nenvironment.}\n\\end{figure}\n\n\\chapter{Managing LCS Servers}\\label{managing-lcs-servers}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nClicking a server in the Dashboard or environment view takes you to the\nserver view. Server view provides detailed information about a server.\nTo protect your users' privacy, LCS doesn't gather, store, or analyze\nuser data.\n\nServer view is segmented into six tabs:\n\n\\textbf{Page Analytics:} This service has been disabled, if you enabled\nit earlier you can see here the past history for metrics on page views\nand load times.\n\n\\textbf{Snapshot Metrics:} This service has been disabled, if you\nenabled it earlier you can see here the past history for application,\nJVM, and server metrics.\n\n\\textbf{Fix Packs:} This service has been disabled, if you enabled it\nearlier you can see here the past history for the server's available and\ninstalled fix packs.\n\n\\textbf{Portal Properties:} This service has been disabled, if you\nenabled it earlier you can see here the past history for your portal's\nproperties and their settings.\n\n\\textbf{Details:} Displays general information about your Liferay DXP\ninstallation, Java version, and hardware.\n\n\\textbf{Server Settings:} View or change your server's name, location,\nand description. You can also unregister the server from LCS.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS only supported Snapshot Metrics for servers running\non Tomcat or WebLogic. On other application servers you may see a\nconsole message indicating that LCS doesn't support server metrics for\nyour application server. You may also see a benign\n\\texttt{NullPointerException} for the LCS\n\\texttt{TaskSchedulerServiceImpl} and \\texttt{ScheduleTasksCommand}.\n\n\\noindent\\hrulefill\n\n\\section{Details}\\label{details}\n\nThe \\emph{Details} tab shows general information about your server.\nThere are three tabs under Details: \\emph{Software}, \\emph{Java}, and\n\\emph{Hardware}. Each shows information, respectively, about your\nLiferay DXP installation, Java installation, and hardware. This\ninformation is useful to the Liferay Support team in the event you need\ntheir assistance.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-server-details.png}}\n\\caption{The Details tab shows information about your server.}\n\\end{figure}\n\n\\section{Server Settings}\\label{server-settings}\n\nFinally, the \\emph{Server Settings} tab lets you view and edit your\nserver's name, location, and description. You can also unregister your\nserver from LCS.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-server-settings.png}}\n\\caption{You can use the Server Settings tab to give your server a fun\nname.}\n\\end{figure}\n\n\\section{Page Analytics}\\label{page-analytics}\n\nPage Analytics appears by default when you enter server view. Page\nAnalytics shows page views and load times for the selected site and time\nperiod. By default, all sites are selected. You can select a specific\nsite from the \\emph{Site} selector menu. You can also select a different\ntime period in the \\emph{Period} and \\emph{Ending At} fields. The arrows\nnext to the \\emph{Ending At} field move the selected time period up or\ndown, respectively, by one period. For example, if you select \\emph{One\nHour} in the \\emph{Period} field, pressing the right arrow next to\n\\emph{Ending At} moves the selected time period up by one hour. Note\nthat at the beginning of the current time period, it can take up to 15\nminutes for data to become available. Also note that data is available\nfor three months from the time LCS collected it.\n\nBy default, load times and page views for all pages are plotted against\ntime in separate graphs. Below these graphs, a table displays summary\nstatistics of data over the same time period, for each page. If you\nclick a page in the table, the graphs plot the data for just that page.\nIf you can't find the page you're looking for, you can search for it in\nthe \\emph{Search} box at the top of the table. To plot data for all\npages again, click the \\emph{All Pages} row at the bottom of the table.\n\nLoad times are also color coded to indicate speed. The \\emph{Load Times}\ngraph's background is red for values above 3,000 ms, orange for values\nfrom 2,000 to 3,000 ms, and green for values less than 2,000 ms.\nLikewise, the table displays all load times greater than 3,000 ms in red\ntext.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-page-analytics-01.png}}\n\\caption{The Page Analytics interface in the LCS Server view.}\n\\end{figure}\n\n\\section{Snapshot Metrics}\\label{snapshot-metrics}\n\nTo view other metrics and statistics of your server's performance, click\nthe \\emph{Snapshot Metrics} tab near the top of the page. These metrics\nare broken down into three main categories: \\emph{Application},\n\\emph{JVM}, and \\emph{Server}. Application is selected by default when\nyou click the Snapshot Metrics button.\n\nThe Application category also has three other categories: \\emph{Pages},\n\\emph{Portlets}, and \\emph{Cache}. Pages lists the frequency that\nspecific pages load, along with their average load times. Portlets lists\nthe same statistics, but for specific portlets in your server. The Cache\ncategory lists Liferay Single VM metrics and Hibernate metrics. The\nfollowing screenshot shows the statistics in the Portlets category.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-server-metrics-application-portlets.png}}\n\\caption{The LCS application metrics show portlet performance\nstatistics, like frequency of use and average load time.}\n\\end{figure}\n\nThe JVM category, as its name indicates, shows statistics about the JVM\nrunning on your server. This includes data on the garbage collector and\nmemory. The number of runs, total time, and average time are listed for\neach garbage collector item. The memory metrics are presented in a bar\nchart that shows the usage of the PS Eden Space, Code Cache, Compressed\nClass Space, PS Old Gen, PS Survivor Space, and Metaspace.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-server-metrics-jvm.png}}\n\\caption{The LCS JVM metrics show performance data for memory and the\ngarbage collector.}\n\\end{figure}\n\nServer is the third category in Snapshot Metrics. The Server category\nshows additional information about how your server is running. For\nexample, horizontal bar graphs show the number of current threads\nrunning on your server, as well as the JDBC connection pools.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-metrics-server.png}}\n\\caption{The LCS server metrics show current threads and JDBC connection\npools.}\n\\end{figure}\n\nNote that in Snapshot Metrics, the application and garbage collector\nmetrics are based on data collected by LCS from server registration to\nthe present. Memory and server metrics, however, show only the current\nstate.\n\n\\section{Fix Packs}\\label{fix-packs-1}\n\nTo view your server's fix packs, click the Fix Packs tab near the top of\nthe page. The available and installed fix packs appear in separate\ntables. The available fix packs table functions exactly like the Fix\nPacks table in environment view for downloading and installing fix\npacks.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-server-fix-packs.png}}\n\\caption{The Fix Packs tab displays your server's available and\ninstalled fix packs.}\n\\end{figure}\n\n\\section{Portal Properties}\\label{portal-properties}\n\nThe \\emph{Portal Properties} tab lets you view your portal's property\nvalues in a searchable table. This gives you a convenient display for\nyour portal property settings. The properties in this table are\norganized into the following categories:\n\n\\textbf{Default Values:} The default values for your portal's\nproperties.\n\n\\textbf{Custom Values:} Any custom values you've set for your portal's\nproperties. This includes any property values you change via a\n\\texttt{portal-ext.properties} file.\n\n\\textbf{Dynamic Properties:} Any property values set at runtime. For\nexample, the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\nfolder's location depends on your configuration. To specify this folder\nwhen setting any properties that require it, use\n\\texttt{\\$\\{liferay.home\\}} instead of an absolute directory path.\n\nYou can display any combination of these categories by selecting the\ncorresponding checkboxes from the gear icon next to the search box at\nthe top-right of the table. For example, by checking the \\emph{Show\nDefault Values} and \\emph{Show Custom Values} checkboxes, the table\nshows your portal's default and custom property values. To show only the\ncustom values, select only \\emph{Show Custom Values}.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-server-portal-properties.png}}\n\\caption{Click the gear icon to select the type of portal properties to\nshow in the table.}\n\\end{figure}\n\n\\chapter{Managing Your LCS Account}\\label{managing-your-lcs-account}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nTo manage your LCS account, select \\emph{My Account} from the user menu\nin the Dockbar. This takes you to a UI with four tabs:\n\n\\textbf{Projects:} Displays your LCS projects in a searchable table that\nlists the administrator's email address for each project.\n\n\\textbf{Email Notifications:} Configure LCS to send you emails when\nspecific events occur in your LCS projects by adding rules that define\nwhat events trigger a notification. There are no rules by default. Click\nthe \\emph{Add Rule} button to define one.\n\nFirst specify the project, environment, and server for the notification.\nNote that you have the option of selecting all environments and servers\nin a project. Then check the checkbox for each event that you want to\ntrigger an email notification. For example, if you create a notification\nrule with \\emph{The server unexpectedly shuts down} selected for all\nservers and environments in your project, then LCS sends you an email if\nany server in your project goes offline without a normal shut down\nevent. Click \\emph{Save} when you're done defining the notification\nrule. It then appears in a table along with other existing rules. Each\nhas Edit and Delete Action buttons.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-add-notification-rule.png}}\n\\caption{You can add rules to determine the events that trigger\nnotifications.}\n\\end{figure}\n\n\\textbf{Notification History:} Displays your web notification history in\na searchable table. You can also select the date range from which to\ndisplay notifications.\n\n\\textbf{Preferences:} Manage your LCS account's preferences. You can\nchange your account's language, time zone, and default LCS project. Your\ndefault LCS project is the one shown each time you log in to LCS.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-account-preferences.png}}\n\\caption{You can change your LCS account's general preferences.}\n\\end{figure}\n\n\\section{Using Web Notifications}\\label{using-web-notifications}\n\nLCS also displays web notifications under the bell icon in the Dockbar.\nA red badge on this icon shows your unread notification count. LCS and\nLiferay Support send these notifications. For example, LCS generates\nnotifications when a server shuts down or some other event requiring\nyour attention occurs. To mark a notification as read, click the small\n\\emph{x} icon next to it. To mark all notifications as read, click the\n\\emph{Mark All as Read} button. To mark notifications as unread again,\nclick the \\emph{Undo} button that appears. To see your notification\nhistory, click the \\emph{Notifications History} button. You can also\naccess your notification history by selecting \\emph{My Account} from the\nuser menu in the Dockbar.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-user-web-notifications.png}}\n\\caption{Web notifications let you know what's happening in your LCS\nprojects.}\n\\end{figure}\n\n\\chapter{Managing Liferay DXP\nSubscriptions}\\label{managing-liferay-dxp-subscriptions}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nLCS lets you use and view your Liferay DXP subscriptions. Recall that\nwhen you\n\\href{/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-environments\\#creating-environments}{create\nan environment}, you assign its subscription type and choose whether LCS\nactivates servers that connect to that environment. If you use LCS for\nactivation, registering a server in that environment consumes an\nactivation key from the environment's subscription type. You can also\nview your project's available activation keys and see how they're being\nused.\n\nDepending on your subscription agreement, LCS also lets you register\nservers via \\emph{elastic subscriptions}. Elastic subscriptions let you\nregister an unlimited number servers. This is invaluable in auto-scaling\nenvironments, where servers are automatically created and destroyed in\nresponse to load. Note that to use elastic subscriptions, you must set\nthe environment as elastic when you create it. Also note that LCS only\nuses elastic subscriptions for servers that exceed the number that the\nenvironment's subscription type allows. In other words, LCS uses the\nenvironment's regular subscriptions before any elastic subscriptions.\n\nYou can access these features from the \\emph{Subscriptions} tab on the\nupper-left of the LCS site. This tab contains two other tabs:\n\\emph{Details} and \\emph{Elastic Subscriptions}.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-subscriptions.png}}\n\\caption{LCS lets you view and manage your subscriptions.}\n\\end{figure}\n\nThere are four tables in the \\emph{Details} tab:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{Subscriptions:} Shows a list of the available subscriptions in\n  your LCS project. For each subscription, this table shows the\n  following information:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Subscription Type\n  \\item\n    Start Date\n  \\item\n    Expiration Date\n  \\item\n    Support End Date\n  \\item\n    Platform\n  \\item\n    Product\n  \\item\n    Processor Cores Allowed\n  \\item\n    Activation Keys\n  \\item\n    Used Activation Keys\n  \\end{itemize}\n\n  Note that \\emph{Processor Cores Allowed} shows the number of processor\n  cores that the subscription allows for each server.\n\\item\n  \\textbf{Subscriptions Summary:} Shows how your subscriptions are\n  currently used in your project. For each subscription type, this table\n  shows the number of activation keys allowed, used, and available.\n\\item\n  \\textbf{Project Environments:} Shows your project's environments and\n  their assigned subscription types. Each environment must have a\n  subscription type.\n\\item\n  \\textbf{Project Servers:} Shows the environment and subscription type\n  for each server in your LCS project.\n\\end{enumerate}\n\nIf any of the information in these tables is missing or incorrect,\ncontact Liferay Support.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you don't use LCS for activating your servers, then\nyou can register as many servers as you want in LCS.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you try to activate a server that exceeds the number\nof processor cores that your subscription allows per server, the\nactivation fails and the server is locked down. A console error also\nindicates the server's core count. You can compare this with your\nsubscription's processor cores allowed in LCS's Subscriptions table. To\nactivate the server, you can either reduce the number of cores it uses\n(e.g., by deploying to different server hardware, or reducing the number\nof virtual processors in a VM or container), or contact Liferay Sales to\nincrease the number of processor cores that your subscription allows per\nserver.\n\n\\noindent\\hrulefill\n\n\\section{Decommissioning Servers}\\label{decommissioning-servers}\n\nTo decommission a server and free its activation key for reuse, select\nthe server's environment on the left and then select the server. In the\nserver's \\emph{Server Settings} tab, select \\emph{Unregister}. Also note\nthat when you shut down a server normally, its activation key is\nimmediately freed for reuse. If the server crashes or its shutdown is\nforced (e.g., kill), its activation key is freed for reuse within six\nminutes.\n\n\\section{Elastic Subscriptions}\\label{elastic-subscriptions}\n\nElastic subscriptions let you register an unlimited number of servers.\nThis is crucial for auto-scaling environments where servers are created\nand destroyed automatically. You can view data on your elastic servers\nfrom the \\emph{Subscriptions} tab's \\emph{Elastic Subscriptions} tab.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To register elastic servers in an environment, that\nenvironment must be set as elastic when it's created. For more\ninformation, see the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-environments\\#creating-environments}{documentation\non creating environments}.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-elastic-subscriptions.png}}\n\\caption{The \\emph{Elastic Subscriptions} tab shows details about your\nproject's elastic servers.}\n\\end{figure}\n\nThe \\emph{Elastic Subscriptions} tab displays the number of elastic\nservers online and the uptime details for each. A graph shows the number\nof elastic servers online each day, while a table lists each elastic\nserver's start time, end time, and duration. The total duration for\nservers is below the table. To download a report of the table's data,\nclick \\emph{Download Report}. Also, you can use the \\emph{Environment}\nand \\emph{Month} selectors above the graph to select the environment and\nmonth to show data from, respectively. The data in both the graph and\nthe table reflect your selections here.\n\n\\chapter{Understanding Environment\nTokens}\\label{understanding-environment-tokens}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nTo register a server in an environment, you must use that environment's\ntoken file. LCS Administrators and Environment Managers can generate and\ndistribute this file. It contains all the information the LCS client app\nneeds to register the server in the environment. When the server starts\nup, it uses the token to connect to LCS. If you use LCS for activation,\nthe server automatically consumes an activation key from the\nenvironment's subscription upon connection. This makes it possible to\nactivate servers automatically on startup with no interaction required.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} For instructions on using and managing your environment\ntokens, see the instructions on\n\\href{/docs/7-2/deploy/-/knowledge_base/d/activating-your-liferay-dxp-server-with-lcs}{registering\nyour server with LCS}.\n\n\\noindent\\hrulefill\n\nThere are a few things to keep in mind when using environment tokens:\n\n\\begin{itemize}\n\\item\n  Each environment can have only one token file. If you regenerate the\n  token, servers using the old file are disconnected from LCS and can't\n  reconnect until receiving the new file. If the server disconnects due\n  to token regeneration and is running version 4.0.2 or later of the LCS\n  client app, the server enters a 30-day grace period during which it\n  functions normally. This gives the administrator time to use the new\n  token file to reconnect to LCS. Servers running earlier versions of\n  the LCS client app present users with an error page until the\n  administrator reconnects with the new token.\n\\item\n  Use caution when distributing the token file, as anyone can use it to\n  connect to your environment (and consume an activation key in your\n  subscription if you're using LCS for activation).\n\\item\n  Minimal information (server name, location, etc\\ldots) is used to\n  register a server with LCS. You can change this information from\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-servers}{the\n  server view in LCS} at any time.\n\\item\n  Environment tokens connect using OAuth. Using an environment token\n  overrides the OAuth authorization cycle. If LCS Administrators or\n  Environment Managers have never registered servers in LCS, the first\n  time they do so an OAuth authorization entry is created in LCS. If\n  they've previously registered servers in LCS, their existing\n  credentials are used when they create a token file.\n\\item\n  If the credentials of the LCS user who generated the token become\n  invalid, you must generate a new token and use it to reconnect to LCS.\n  An LCS user's credentials become invalid if the user leaves the LCS\n  project or becomes an LCS Environment Manager or LCS Environment\n  Viewer in a different environment.\n\\end{itemize}\n\nSo why bother with environment tokens at all? Besides simplifying the\nLCS connection process, environment tokens are valuable in auto-scaling\nenvironments where algorithms create and destroy servers automatically.\nIn this situation, having clients that activate and configure themselves\nis crucial.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: If your auto-scaling environment creates new server nodes\nfrom a server in a system image, that server can't require human\ninteraction during setup. When creating such an image, you must change\nany portal property settings that prevent automatic setup. By default,\nLiferay DXP's setup wizard requires human interaction. You must\ntherefore set the \\texttt{setup.wizard.enabled} property to\n\\texttt{false} if you want your auto-scaling environment to create new\nnodes from the server.\n\n\\chapter{Troubleshooting Your LCS\nConnection}\\label{troubleshooting-your-lcs-connection}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LCS is deprecated and will be shut down on December 31,\n2021. Customers who activate LCS are advised to replace it with our\nlatest activation key type which is suitable for virtualized\nenvironments.\n\nFor further information, please see\n\\href{https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation}{Changes\nto Liferay Product Activation}.\n\n\\noindent\\hrulefill\n\nIf you use LCS to activate Liferay DXP, your server must maintain its\nconnection to LCS at all times. If this connection is interrupted, your\nserver enters a grace period to allow for reconnection. Lengthy\ninterruptions, however, can affect your server's uptime.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You must use LCS for activation of Elastic subscriptions.\nOtherwise, you don't have to use LCS for activation. You can instead\nrequest an XML activation key from Liferay Support.\n\n\\noindent\\hrulefill\n\nThe following sections in this document provide some background\ninformation and help you troubleshoot problems with your server's LCS\nconnection:\n\n\\hyperref[lcs-grace-periods]{\\textbf{LCS Grace Periods:}} Describes how\ngrace periods work in LCS. You should read this section before\nattempting any troubleshooting steps.\n\n\\hyperref[troubleshooting]{\\textbf{Troubleshooting:}} Presents\ntroubleshooting steps for specific problems.\n\n\\hyperref[increasing-log-levels]{\\textbf{Increasing Log Levels:}} If you\ncontact Liferay Support, you'll be asked to increase your server's log\nlevels and then provide your log files. This section shows you how to do\nthis.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The odds of LCS being unavailable are low. LCS is\ndeployed on a global cloud infrastructure set up for automatic failure\nrecovery. Notifications also let the LCS team react quickly to any\ndowntime. During LCS updates and new version releases, however, LCS is\nunavailable for a few minutes while changes are applied.\n\n\\noindent\\hrulefill\n\n\\section{LCS Grace Periods}\\label{lcs-grace-periods}\n\nThere are 2 grace period types in LCS:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{Connection Grace Period:} Occurs when your activated LCS\n  connection is interrupted. This gives you time to re-establish the\n  connection.\n\\item\n  \\textbf{Subscription Grace Period:} Occurs when your subscription is\n  about to expire. This gives you time to renew the subscription.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} These grace periods only apply to servers previously\nconnected and activated in LCS. If the subscription check or connection\nfails when a server attempts to connect to LCS for the first time, that\nserver doesn't enter a grace period. It's therefore important to verify\nthat an active subscription is available before connecting a new server\nto LCS. To do this, check the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/managing-liferay-dxp-subscriptions}{Subscriptions\ntab} in LCS.\n\n\\noindent\\hrulefill\n\n\\section{Connection Grace Period}\\label{connection-grace-period}\n\nIf your server's LCS connection is interrupted, the server continues to\nrun and enters a grace period that lasts for up to 30 days to allow for\nreconnection. During this grace period, Liferay DXP displays a warning\nmessage to administrators. Upon seeing this message, administrators\nshould contact Liferay Support and follow the troubleshooting steps\nbelow. LCS automatically restores your server's activation upon\nreconnection (you shouldn't need to restart the server). If for some\nreason the connection can't be restored, Liferay Support will provide an\nalternative way to activate your server.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-grace-period.png}}\n\\caption{A warning message is displayed to administrators if the server\ncan't connect to LCS to validate the subscription.}\n\\end{figure}\n\nWhile disconnected from LCS, the LCS client app continually attempts to\nreconnect. If reconnection continues to fail, ensure that your server\ncan access \\texttt{lcs.liferay.com} and\n\\texttt{lcs-gateway.liferay.com}. If the LCS client app stops attempting\nto reconnect, there will be no activity in the logs. In this case, you\ncan force reconnection by redeploying the app. Follow these steps to do\nso:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your server's\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  folder (usually the parent folder of the application server's folder),\n  remove this file:\n\n\\begin{verbatim}\nosgi/marketplace/Liferay Connected Services Client.lpkg\n\\end{verbatim}\n\\item\n  Place \\texttt{Liferay\\ Connected\\ Services\\ Client.lpkg} in\n  \\texttt{{[}Liferay\\ Home{]}/deploy}. If you\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/lcs-preconfiguration\\#preconfiguring-lcs-to-connect-through-a-proxy}{connect\n  to LCS through a proxy}, and configured this inside the LCS client\n  app, make sure the app you deploy is also configured to do so.\n\\end{enumerate}\n\nYou should also ensure that you've enabled email notifications in LCS\nfor server disconnection events. To do this, you must create a\nnotification rule that sends an email whenever the server shuts down\nunexpectedly. The documentation on\n\\href{/docs/7-2/deploy/-/knowledge_base/d/managing-your-lcs-account}{managing\nyour LCS account} explains how to do this.\n\n\\section{Subscription Grace Period}\\label{subscription-grace-period}\n\nAt least 90 days before the subscription expires, Liferay will reach out\nto begin the renewal process. 30 days before expiration, Liferay Support\nsends warning messages through the Help Center,\n\\href{https://lcs.liferay.com}{the LCS site}, and\n\\href{https://www.liferay.com/group/customer}{the Customer Portal}.\nAfter the expiration date, your servers may be placed in an additional\ngrace period, which is communicated through the same support channels.\nIf the renewal isn't completed during this grace period, then the\nsubscription becomes inactive and the Liferay DXP instance enters the\n30-day grace period. As soon as the renewal is processed, the instance\nactivates and any error or warning messages disappear within 24 hours.\nNote that by using XML activation keys (provided by Liferay Support upon\nrequest), you can continue to use your Liferay DXP instances even after\na subscription has expired.\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/lcs-support-expiration.png}}\n\\caption{LCS sends you a notification prior to the expiration of your\nsubscription.}\n\\end{figure}\n\n\\section{Troubleshooting}\\label{troubleshooting}\n\nIf you encounter issues with LCS, the Liferay Support team is here to\nhelp. If you need support, open a\n\\href{https://help.liferay.com/hc}{Help Center} ticket. You can begin\ntroubleshooting the following scenarios, which the Liferay Support team\ncan also assist you with.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Before troubleshooting specific issues or contacting\nLiferay Support, make sure that you've followed the LCS\n\\href{/docs/7-2/deploy/-/knowledge_base/d/lcs-preconfiguration}{preconfiguration}\nand\n\\href{/docs/7-2/deploy/-/knowledge_base/d/activating-your-liferay-dxp-server-with-lcs}{registration}\nsteps correctly.\n\n\\noindent\\hrulefill\n\n\\section{Server Can't Reach LCS}\\label{server-cant-reach-lcs}\n\nIf your server can't reach LCS, verify that you can access the public\nsites required by LCS:\n\n\\begin{itemize}\n\\item\n  \\href{https://lcs.liferay.com/}{\\texttt{lcs.liferay.com}} should be\n  viewable in a browser.\n\\item\n  \\texttt{lcs-gateway.liferay.com} should respond on port 443:\n\n\\begin{verbatim}\ncurl -vk -I \"https://lcs-gateway.liferay.com\"\ntelnet lcs-gateway.liferay.com 443\n\\end{verbatim}\n\\end{itemize}\n\n\\section{Subscription Issues}\\label{subscription-issues}\n\nFor issues related to your subscription, first review the documentation\non\n\\href{/docs/7-2/deploy/-/knowledge_base/d/managing-liferay-dxp-subscriptions}{managing\nyour subscription}. Subscription errors usually involve one of these\nproblems:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Your server can reach LCS, but can't locate a subscription.\n\\item\n  Your server can reach LCS and locate a subscription, but activating\n  your server would exceed the subscription's number of activation keys\n  or cores.\n\\end{itemize}\n\nIn either case, you must verify that a subscription is available and\nthat you're not exceeding its number of activation keys or cores. You\ncan find this information on the LCS site's Subscriptions page, as\ndescribed in\n\\href{/docs/7-2/deploy/-/knowledge_base/d/managing-liferay-dxp-subscriptions}{the\ndocumentation on managing subscriptions}. If the environment in which\nyou're trying to activate a server isn't assigned the subscription you\nwant to use, then you must create a new environment and assign it the\ncorrect subscription. Once assigned, you can't change an environment's\nsubscription. Follow\n\\href{/docs/7-2/deploy/-/knowledge_base/d/activating-your-liferay-dxp-server-with-lcs}{the\ninitial registration steps} for instructions on creating a new\nenvironment and activating a new server.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When shutting down servers, you must ensure that the LCS\nsite receives the server shutdown commands. Otherwise, LCS may not\nrelease that server's activation key for reuse and attempts to activate\nadditional servers may exceed the subscription's number of activation\nkeys. There's a higher likelihood of this happening in rolling\ndeployments and/or when using containers. For more information, see the\n\\href{https://help.liferay.com/hc/en-us/articles/360018261011}{KB\narticle on properly unregistering subscriptions}.\n\n\\noindent\\hrulefill\n\n\\section{Invalid Token}\\label{invalid-token}\n\nIf the token is invalid, first review the documentation on\n\\href{/docs/7-2/deploy/-/knowledge_base/d/understanding-environment-tokens}{environment\ntokens}. The following table lists causes and solutions for invalid\ntokens.\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 2\\tabcolsep) * \\real{0.4400}}\n  >{\\raggedright\\arraybackslash}p{(\\linewidth - 2\\tabcolsep) * \\real{0.5600}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\n~Cause\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n~Solution\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nThe LCS user who generated the token no longer has permissions. This\nhappens when the user leaves the LCS project or becomes an LCS\nEnvironment Manager or LCS Environment Viewer in a different\nenvironment. & Regenerate the token. \\\\\nThe token's file name is changed after download. & Download the token\nagain from LCS. \\\\\nThe token is regenerated. & Use the regenerated token. \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Increasing Log Levels}\\label{increasing-log-levels}\n\nIf you contact Liferay Support, you're asked to increase your server's\nlog levels and then provide your log files. You can find these log files\nin \\texttt{{[}Liferay\\ Home{]}/logs}\n(\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\nis usually the parent folder of the application server's folder). There\nare 2 types of log files in this folder:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{Liferay log files:} The files \\texttt{liferay.{[}date{]}.log}\n  and \\texttt{liferay.{[}date{]}.xml} are the logs for your Liferay DXP\n  installation. Note that LOG and XML files for the same date contain\n  the same information--the only difference is the file format.\n\\item\n  \\textbf{LCS log files:} The \\texttt{lcs-portlet-{[}date{]}.log} files\n  are the LCS client app's logs. Note that if there's only a single LCS\n  log file, it may appear without a date as \\texttt{lcs-portlet.log}.\n  When you increase the log levels as described in the following\n  sections, the additional log messages are written to these LCS log\n  files.\n\\end{enumerate}\n\nThere are 2 ways to increase the log levels:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{In your Liferay DXP instance's Control Panel:} This is a\n  temporary configuration that resets upon shutting down the server.\n  Note that if the server isn't activated, you can't access the Control\n  Panel. In that case, Liferay Support can provide an XML activation\n  key.\n\\item\n  \\textbf{In a Log4j configuration:} This is a permanent configuration\n  that persists through server shutdown and restart.\n\\end{enumerate}\n\nThe following sections cover both options.\n\n\\section{Control Panel}\\label{control-panel}\n\nFollow these steps to increase the log levels via the Control Panel:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Control Panel} → \\emph{Configuration} → \\emph{Server\n  Administration}.\n\\item\n  Click the \\emph{Log Levels} tab.\n\\item\n  Search for ``lcs''.\n\\item\n  Change the log level for each matching entry to DEBUG.\n\\item\n  While in the Control Panel, you should also navigate to\n  \\emph{Configuration} → \\emph{Liferay Connected Services} and take a\n  screenshot of what you see there. This is useful to Liferay Support.\n\\end{enumerate}\n\n\\section{Log4j}\\label{log4j}\n\nFollow these steps to increase the log levels via Log4j:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Download the latest LCS client as instructed in the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/lcs-preconfiguration}{LCS\n  preconfiguration article}. The app downloads as\n  \\texttt{Liferay\\ Connected\\ Services\\ Client.lpkg}. If you don't want\n  to download the latest client, you can use the one already installed\n  in your server: it's in \\texttt{{[}Liferay\\ Home{]}/osgi/marketplace}\n  (just make sure to shut down your server before following the rest of\n  the steps in this section). Recall that the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  folder is usually the parent folder of the application server's\n  folder.\n\\item\n  Expand the LPKG file, then expand the\n  \\texttt{lcs-portlet-{[}version{]}.war} file inside it.\n\\item\n  Inside the \\texttt{WAR} file, replace the contents of\n  \\texttt{WEB-INF\\textbackslash{}classes\\textbackslash{}META-INF\\textbackslash{}portal-log4j.xml}\n  with the following configuration:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">\n\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\">\n        <appender class=\"org.apache.log4j.rolling.RollingFileAppender\" name=\"RollingFileAppender\">\n                <rollingPolicy class=\"org.apache.log4j.rolling.TimeBasedRollingPolicy\">\n                        <param name=\"ActiveFileName\" value=\"@liferay.home@/logs/lcs-portlet.log\" />\n                        <param name=\"FileNamePattern\" value=\"@liferay.home@/logs/lcs-portlet.%d{yyyy-MM-dd}.log.zip\" />\n                </rollingPolicy>\n\n                <layout class=\"org.apache.log4j.EnhancedPatternLayout\">\n                        <param name=\"ConversionPattern\" value=\"%d{yyyy/MM/dd HH\\:mm\\:ss} %-5p [%t][%c{1}:%L] %m%n\" />\n                </layout>\n        </appender>\n\n        <category name=\"com.liferay.lcs.task.scheduler\">\n                <priority value=\"ALL\" />\n        </category>\n\n        <logger additivity=\"false\" name=\"com.liferay.lcs\">\n                <level value=\"ALL\" />\n                <appender-ref ref=\"RollingFileAppender\" />\n        </logger>\n</log4j:configuration>\n\\end{verbatim}\n\\item\n  Save the file and repackage the WAR and LPKG (make sure not to change\n  the names of these files).\n\\item\n  Make sure your server is shut down.\n\\item\n  In your installation's\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  folder, delete the existing LCS client app:\n\n\\begin{verbatim}\nosgi/marketplace/Liferay Connected Services Client.lpkg\n\\end{verbatim}\n\\item\n  Place the \\texttt{Liferay\\ Connected\\ Services\\ Client.lpkg} that you\n  repackaged in step 4 in \\texttt{osgi/marketplace}.\n\\item\n  Start your server.\n\\end{enumerate}\n\nIf you need assistance with the issues in this guide, or any other\nissues with LCS, contact Liferay Support.\n\n\\chapter{Deployment Reference}\\label{deployment-reference}\n\nHere you'll find definitions, default settings, templates, and more.\nHere are some of the topics:\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}:\n  From this location, Liferay DXP launches applications, applies\n  configurations, loads JAR files, and generates logs.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{Portal\n  properties}: Use a \\texttt{portal-ext.properties} file (or another\n  qualified properties file) to configure Liferay DXP and override\n  features.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/system-properties}{System\n  properties}: Examine Liferay DXP default system configuration.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/database-templates}{Database\n  templates}: Use these portal properties templates to specify the\n  Liferay DXP database.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/elasticsearch-connector-settings-reference}{Elasticsearch\n  settings}: Examine the configuration settings for Liferay's default\n  Elasticsearch adapter.\n\\end{itemize}\n\n\\chapter{Liferay Home}\\label{liferay-home}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\n\\emph{Liferay Home} is the location from which Liferay DXP launches\napplications, applies configurations, loads JAR files, and generates\nlogs.\n\n\\begin{itemize}\n\\item\n  \\emph{In a Liferay DXP bundle,} Liferay Home is the installation's\n  top-level folder and it contains the application server.\n\\item\n  \\emph{In a manual installation,} the Liferay Home folder varies by\n  application server. If you're doing a manual installation, please\n  refer to the article covering that app server (e.g., \\emph{Installing\n  Liferay DXP on {[}app server{]}}) for the Liferay Home location.\n\\end{itemize}\n\nBundles contain this folder structure regardless of application server:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{\\hyperref[liferay-home]{Liferay Home}}\n\n  \\begin{itemize}\n  \\item\n    \\textbf{{[}Application Server{]}}: This folder is named after the\n    application server where Liferay DXP is installed.\n  \\item\n    \\texttt{data} (if HSQL database is selected): Stores an embedded\n    HSQL database, Liferay DXP's file repository, and search indexes.\n    The embedded HSQL database is configured by default, but it's\n    intended for demonstration and trial purposes only. The\n    \\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#JDBC}{Portal\n    property \\texttt{jdbc.default.url}} sets the Hypersonic embedded\n    HSQL database location.\n  \\item\n    \\texttt{deploy}: To auto-deploy plugins, copy them to this folder.\n    It supports application \\texttt{.lpkg} files from Liferay\n    Marketplace, plugin \\texttt{.war} files, and plugin \\texttt{.jar}\n    files. The\n    \\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Auto\\%20Deploy}{Portal\n    property \\texttt{auto.deploy.deploy.dir}} sets the auto-deploy\n    location.\n  \\item\n    \\texttt{license}: Liferay DXP's copyright and version files are\n    here.\n  \\item\n    \\texttt{logs}: Log files go here. Examine them as you diagnose\n    problems. \\texttt{portal-impl.jar}'s\n    \\texttt{portal-impl/src/META-INF/portal-log4j.xml} file sets the log\n    file location. To override the log file location, you must\n    \\href{/docs/7-0/tutorials/-/knowledge_base/t/advanced-customization-with-ext-plugins\\#using-advanced-configuration-files}{use\n    an \\texttt{ext-impl/src/META-INF/portal-log4j-ext.xml} file in an\n    Ext plugin}.\n  \\item\n    \\texttt{osgi}: All the JAR files and a few configuration files for\n    the OSGi runtime belong in this folder. The\n    \\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Module\\%20Framework}{Portal\n    property \\texttt{module.framework.base.dir}} sets the OSGi folder\n    location. Here are its subfolders:\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{configs}: Component configuration files.\n    \\item\n      \\texttt{core}: Liferay DXP's core modules.\n    \\item\n      \\texttt{marketplace}: Marketplace applications and application\n      suites.\n    \\item\n      \\texttt{modules}: Modules you've deployed.\n    \\item\n      \\texttt{portal}: Liferay DXP's non-core modules.\n    \\item\n      \\texttt{state}: Contains OSGi internal state files for such things\n      as OSGi bundle installation, bundle storage, and more.\n    \\item\n      \\texttt{target-platform}: Target platform index.\n    \\item\n      \\texttt{test}: Modules that support test integration.\n    \\item\n      \\texttt{war}: WAR plugins you've deployed.\n    \\end{itemize}\n  \\item\n    \\texttt{patching-tool}: (Liferay DXP only) This folder contains\n    patches and a utility for installing the patches.\n  \\item\n    \\texttt{tools}: For Liferay DXP upgrade and target platform indexer.\n  \\item\n    \\texttt{work}: Module Jasper work files.\n  \\end{itemize}\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If Liferay DXP cannot create resources in the Liferay\nHome folder or if it finds itself running on certain application\nservers, it creates a folder called \\texttt{liferay} in the home folder\nof the operating system user that is running Liferay DXP. In this case,\nthat \\texttt{liferay} folder becomes Liferay Home. For example, if the\noperating system user's name is jbloggs, the \\texttt{liferay} folder\npath is \\texttt{/home/jbloggs/liferay} or\n\\texttt{C:\\textbackslash{}Users\\textbackslash{}jbloggs\\textbackslash{}liferay}.\n\n\\chapter{Portal Properties}\\label{portal-properties-1}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nYou can set portal properties to configure and override Liferay DXP\nfeatures. Your installation's \\texttt{portal-impl.jar} embeds the\ndefault properties file:\n\n{ portal.properties { (Opens New Window)} }\n\nOverriding a portal property requires creating an \\emph{extension}\nportal properties file that specifies the properties you're overriding.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} In a portal properties extension file, specify only the\nproperties you're overriding.\n\n\\noindent\\hrulefill\n\nHere's an example of setting Portal's data source to a MySQL database by\nadding override properties in a\n\\texttt{{[}Liferay\\ Home{]}/portal-ext.properties} file:\n\n\\begin{verbatim}\njdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\njdbc.default.url=jdbc:mysql://localhost/lportal?characterEncoding=UTF-8&dontTrackOpenResources=true&holdResultsOpenOverStatementClose=true&serverTimezone=GMT&useFastDateParsing=false&useUnicode=true\njdbc.default.username=jbloggs\njdbc.default.password=pass123\n\\end{verbatim}\n\nThe\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#Properties\\%20Override}{\\texttt{include-and-override}}\nproperty specifies portal property files that override the defaults. It\nspecifies the order the files are read---the last file read takes\nhighest priority.\n\nProperties file prioritization (highest to lowest):\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  \\texttt{{[}Liferay\\ Home{]}/portal-setup-wizard.properties}\n\\item\n  \\texttt{{[}user\\ home{]}/portal-setup-wizard.properties}\n\\item\n  \\texttt{{[}Liferay\\ Home{]}/portal-ext.properties}\n\\item\n  \\texttt{{[}user\\ home{]}/portal-ext.properties}\n\\item\n  \\texttt{{[}Liferay\\ Home{]}/portal-bundle.properties}\n\\item\n  \\texttt{{[}user\\ home{]}/portal-bundle.properties}\n\\item\n  \\texttt{{[}Liferay\\ Home{]}/portal.properties}\n\\item\n  \\texttt{portal-impl.jar/portal.properties}\n\\end{enumerate}\n\n\\texttt{{[}Liferay\\ Home{]}/portal-ext.properties} is the most commonly\nused file for overriding the defaults.\n\n\\chapter{System Properties}\\label{system-properties}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nSystem properties configure the Liferay DXP system. Your installation's\n\\texttt{portal-impl.jar} embeds the default properties file:\n\n{ system.properties { (Opens New Window)} }\n\n\\chapter{Database Templates}\\label{database-templates}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nBelow are templates (example\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\nproperties}) for configuring various databases as a built-in data source\nfor Liferay DXP.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The\n\\href{https://web.liferay.com/documents/14/21598941/Liferay+DXP+7.2+Compatibility+Matrix/b6e0f064-db31-49b4-8317-a29d1d76abf7?}{Liferay\nDXP Compatibility Matrix} specifies supported databases and\nenvironments.\n\n\\noindent\\hrulefill\n\n\\section{MariaDB}\\label{mariadb-2}\n\n\\begin{verbatim}\njdbc.default.driverClassName=org.mariadb.jdbc.Driver\njdbc.default.url=jdbc:mariadb://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\njdbc.default.username=\njdbc.default.password=\n\\end{verbatim}\n\n\\section{MySQL}\\label{mysql-2}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} MySQL Connector/J 8.0 is highly recommended for use with\nMySQL Server 8.0 and 5.7.\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\njdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\njdbc.default.url=jdbc:mysql://localhost/lportal?characterEncoding=UTF-8&dontTrackOpenResources=true&holdResultsOpenOverStatementClose=true&serverTimezone=GMT&useFastDateParsing=false&useUnicode=true\njdbc.default.username=\njdbc.default.password=\n\\end{verbatim}\n\n\\section{PostgreSQL}\\label{postgresql-2}\n\n\\begin{verbatim}\njdbc.default.driverClassName=org.postgresql.Driver\njdbc.default.url=jdbc:postgresql://localhost:5432/lportal\njdbc.default.username=sa\njdbc.default.password\n\\end{verbatim}\n\nSee the\n\\href{@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\\#JDBC}{default\nportal properties} for more database templates.\n\n\\chapter{Comparing Patch Levels}\\label{comparing-patch-levels}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nIf you're a developer, the Patching Tool can show you what changed\nbetween different Liferay DXP patches and versions. These commands show\nyou information about the different patch levels:\n\n\\texttt{patching-tool\\ diff}: Prints the differences between two patch\nlevels. At least one stored patch level must be available. This command\naccepts options for filtering the output:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{source}: Shows the source differences between the two patch\n  levels.\n\\item\n  \\texttt{files}: Shows a list of the modified files.\n\\item\n  \\texttt{fixed-issues}: Shows a list of LPS/LPE issues from our issue\n  tracking system.\n\\item\n  \\texttt{html}: Specify this along with one of the filtering options\n  (\\texttt{source}, \\texttt{files}, or \\texttt{fixed-issues}) and after\n  the patch levels, to write the differences to an HTML file\n  (\\texttt{\\textless{}stored-name-1\\textgreater{}-\\textless{}stored-name-2\\textgreater{}-{[}type{]}-diff.html})\n  in the \\texttt{diffs} folder. Additions are colored green and\n  deletions are colored red.\n\\item\n  \\texttt{collisions}: Shows a list of modified files which collide with\n  deployed plugins.\n\\end{itemize}\n\nFor detailed usage information, run \\texttt{patching-tool\\ help\\ diff}.\n\n\\texttt{patching-tool\\ store}: Manages patching level information for\nthe \\texttt{diff} command. Your patches must contain source code to\nstore the patch level and to prepare usable information for the\n\\texttt{diff} command. Here are the \\texttt{store} command options:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{info}: Prints the list of patches which make up the stored\n  patch level.\n\\item\n  \\texttt{add}: Stores the patch level that can be found in the patches\n  directory.\n\\item\n  \\texttt{update}: Adds or updates patch level information.\n\\item\n  \\texttt{rm}: Removes previously stored patch level information.\n\\end{itemize}\n\nFor detailed usage information, run \\texttt{patching-tool\\ help\\ store}.\n\n\\chapter{Patching Tool Configuration\nProperties}\\label{patching-tool-configuration-properties}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nHere are the Patching Tool configuration properties. See\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-patching-tool}{Configuring\nthe Patching Tool} for more information on configuring the Patching\nTool.\n\n\\textbf{patching.mode:} This can be \\texttt{binary} (the default) or\n\\texttt{source} if you're patching a source tree. Patches contain both\nbinary and source patches. If your development team extends Liferay DXP,\nhave them patch their source tree.\n\n\\textbf{patches.folder:} Specify where to store patches. The default\nlocation is \\texttt{./patches}.\n\n\\textbf{war.path:} Specify the location of the Liferay DXP installation\ninside your application server. Alternatively, you can specify a\n\\texttt{.war} file here, and you can patch a Liferay DXP \\texttt{.war}\nfor installation to your application server.\n\n\\textbf{global.lib.path:} Specify the location for storing \\texttt{.jar}\nfiles on the global classpath. If you're not sure, search for\n\\texttt{portal-kernel.jar}; it's on the global classpath. This property\nis only valid if your \\texttt{patching.mode} is \\texttt{binary}.\n\n\\textbf{liferay.home:} Specify the default location for the\n\\texttt{data}, \\texttt{osgi}, and \\texttt{tools} folders.\n\n\\textbf{source.path:} Specify the location of your Liferay DXP source\ntree. This property is only valid if your \\texttt{patching.mode} is\n\\texttt{source}.\n\nService Pack detection is available behind a proxy server. To configure\nyour proxy, use the following settings, making sure to replace\n\\texttt{{[}PROXY\\_IP\\_ADDRESS{]}} with your proxy server's IP address\nand replace the port numbers with yours:\n\n\\begin{verbatim}\n## Proxy settings\n\n# HTTP Proxy\n\n#proxy.http.host=[PROXY_IP_ADDRESS]\n#proxy.http.port=80\n#proxy.http.user=user\n#proxy.http.password=password\n\n# HTTPS Proxy\n\nproxy.https.host=[PROXY_IP_ADDRESS]\nproxy.https.port=80\nproxy.https.user=user\nproxy.https.password=password\n\n# SOCKS Proxy\n\n#proxy.socks.host=[PROXY_IP_ADDRESS]\n#proxy.socks.port=1080\n#proxy.socks.user=user\n#proxy.socks.password=password\n\\end{verbatim}\n\n\\chapter{Troubleshooting Deployments}\\label{troubleshooting-deployments}\n\nWhen coding on any platform, you can sometimes run into issues that have\nno clear resolution. This can be particularly frustrating. If you have\nissues building, deploying, or running apps and modules, you want to\nresolve them fast. These frequently asked questions and answers help you\ntroubleshoot and correct problems.\n\nClick a question to view the answer.\n\n{Why did the entity sort order change when I migrated to a new database\ntype?~{}}\n\n\\begin{verbatim}\n<p><a href=\"/docs/7-2/deploy/-/knowledge_base/d/sort-order-changed-with-a-different-database\">Your new database uses a different default query result order--you should be able to configure a different order</a>.</p>\n\\end{verbatim}\n\n{How can I use files to configure components?~{}}\n\n\\begin{verbatim}\n<p>See <a href=\"/docs/7-2/deploy/-/knowledge_base/d/using-files-to-configure-product-modules\">Using Files to Configure Module Components</a>. </p>\n\\end{verbatim}\n\n{The application server and database started, but Liferay DXP failed to\nconnect to the database. What happened and how can I fix this?~{}}\n\n\\begin{verbatim}\n<p>Liferay DXP initialization can fail while attempting to connect to a database server that isn't ready. <a href=\"/docs/7-2/deploy/-/knowledge_base/d/portal-failed-to-initialize-because-the-database-wasnt-ready\">Configuring startup to retry JDBC connections</a> facilitates connecting @product@ to databases. </p>\n\\end{verbatim}\n\n\\chapter{Liferay DXP Failed to Initialize Because the Database Wasn't\nReady}\\label{liferay-dxp-failed-to-initialize-because-the-database-wasnt-ready}\n\nIf you start your database server and application server at the same\ntime, Liferay DXP might try connecting to the data source before the\ndatabase is ready. By default, Liferay DXP doesn't retry connecting to\nthe database; it just fails. But there is a way to avoid this situation:\ndatabase connection retries.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a \\texttt{portal-ext.properties} file in your\n  \\href{/docs/7-1/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  folder.\n\\item\n  Set the property \\texttt{retry.jdbc.on.startup.max.retries} equal to\n  the number of times to retry connecting to the data source.\n\\item\n  Set property \\texttt{retry.jdbc.on.startup.delay} equal to the number\n  of seconds to wait before retrying connection.\n\\end{enumerate}\n\nIf at first the connection doesn't succeed, Liferay DXP uses the retry\nsettings to try again.\n\n\\section{Related Topics}\\label{related-topics-10}\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/connecting-to-data-sources-using-jndi}{Connecting\nto JNDI Data Sources}\n\n\\chapter{Sort Order Changed with a Different\nDatabase}\\label{sort-order-changed-with-a-different-database}\n\nIf you've been using Liferay DXP, but are switching it to use a\ndifferent database type, consult your database vendor documentation to\nunderstand your old and new database's default query result order. The\ndefault order is either case-sensitive or case-insensitive. This affects\nentity sort order in Liferay DXP.\n\nHere are some examples of ascending alphabetical sort order.\n\nCase-sensitive:\n\n\\begin{verbatim}\n111\n222\nAAA\nBBB\naaa\nbbb\n\\end{verbatim}\n\nCase-insensitive:\n\n\\begin{verbatim}\n111\n222\nAAA\naaa\nBBB\nbbb\n\\end{verbatim}\n\nYour new database's default query result order might differ from your\ncurrent database's order.\n\nConsult your vendor's documentation to configure the order the way you\nwant.\n\n\\chapter{Using Files to Configure Module\nComponents}\\label{using-files-to-configure-module-components}\n\nLiferay DXP uses\n\\href{http://felix.apache.org/documentation/subprojects/apache-felix-file-install.html}{Felix\nFile Install} to monitor file system folders for new/updated\nconfiguration files, and the \\href{http://felix.apache.org/}{Felix OSGi\nimplementation} of\n\\href{http://felix.apache.org/documentation/subprojects/apache-felix-config-admin.html}{Configuration\nAdmin} to let you use files to configure module service components.\n\nTo learn how to work with configuration files, first review\n\\href{/docs/7-1/user/-/knowledge_base/u/understanding-system-configuration-files}{Understanding\nSystem Configuration Files}.\n\n\\section{Configuration File Formats}\\label{configuration-file-formats}\n\nThere are two different configuration file formats:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{.cfg}: An older, simple format that only supports\n  \\texttt{String} values as properties.\n\\item\n  \\texttt{.config}: A format that supports strings, type information,\n  and other non-string values in its properties.\n\\end{itemize}\n\nAlthough Liferay DXP supports both formats, use \\texttt{.config} files\nfor their flexibility and ability to use type information. Since\n\\texttt{.cfg} files lack type information, if you want to store anything\nbut a \\texttt{String}, you must use properties utility classes to cast\n\\texttt{String}s to intended types (and you must carefully document\nproperties that aren't \\texttt{String}s). \\texttt{.config} files\neliminate this need by allowing type information. The articles below\nexplain the file formats:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-1/user/-/knowledge_base/u/understanding-system-configuration-files}{Understanding\n  System Configuration Files}\n\\item\n  \\href{https://sling.apache.org/documentation/bundles/configuration-installer-factory.html\\#configuration-files-config}{Configuration\n  file (\\texttt{.config}) syntax}\n\\item\n  \\href{https://sling.apache.org/documentation/bundles/configuration-installer-factory.html\\#property-files-cfg}{Properties\n  file(\\texttt{.cfg}) syntax}\n\\end{itemize}\n\n\\section{Naming Configuration Files}\\label{naming-configuration-files}\n\nBefore you\n\\href{/docs/7-1/user/-/knowledge_base/u/creating-configuration-files}{create\na configuration file}, follow these steps to determine whether multiple\ninstances of the component can be created or if the component is\nintended to be a singleton:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Deploy the component's module if you haven't done so already.\n\\item\n  In Liferay DXP's UI, go to \\emph{Control Panel} → \\emph{Configuration}\n  → \\emph{System Settings}.\n\\item\n  Find the component's settings by searching or browsing for the\n  component.\n\\item\n  If the component's settings page has a section called\n  \\emph{Configuration Entries}, you can create multiple instances of the\n  component configured however you like. Otherwise, you should treat the\n  component as a singleton.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\pandocbounded{\\includegraphics[keepaspectratio]{./images/system-settings-page-lists-configuration-entries.png}}\n\\caption{You can create multiple instances of components whose System\nSettings page has a \\emph{Configuration Entries} section.}\n\\end{figure}\n\n\\emph{All} configuration file names must start with the component's PID\n(PID stands for \\emph{persistent identity}) and end with\n\\texttt{.config} or \\texttt{.cfg}.\n\nFor example, this class uses\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/declarative-services}{Declarative\nServices} to define a component:\n\n\\begin{verbatim}\npackage com;\n@Component\nclass Foo {}\n\\end{verbatim}\n\nThe component's PID is \\texttt{com.Foo}. All the component's\nconfiguration files must start with the PID \\texttt{com.Foo}.\n\nFor each non-singleton component instance you want to create or update\nwith a configuration, you must use a uniquely named configuration file\nthat starts with the component's PID and ends with \\texttt{.config} or\n\\texttt{.cfg}. Creating configurations for multiple component instances\nrequires that the configuration files use different \\emph{subnames}. A\nsubname is the part of a configuration file name after the PID and\nbefore the suffix \\texttt{.config} or \\texttt{.cfg}. Here's the\nconfiguration file name pattern for non-singleton components:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{{[}PID{]}-{[}subname1{]}.config}\n\\item\n  \\texttt{{[}PID{]}-{[}subname2{]}.config}\n\\item\n  etc.\n\\end{itemize}\n\nFor example, you could configure two different instances of the\ncomponent \\texttt{com.Foo} by using configuration files with these\nnames:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.Foo-one.config}\n\\item\n  \\texttt{com.Foo-two.config}\n\\end{itemize}\n\nEach configuration file creates and/or updates an instance of the\ncomponent that matches the PID. The subname is arbitrary---it doesn't\nhave to match a specific component instance. This means you can use\nwhatever subname you like. For example, these configuration files are\njust as valid as the two above:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{com.Foo-puppies.config}\n\\item\n  \\texttt{com.Foo-kitties.config}\n\\end{itemize}\n\nUsing the subname \\texttt{default}, however, is Liferay DXP's convention\nfor configuring a component's first instance. The file name pattern is\ntherefore\n\n\\begin{verbatim}\n[PID]-default.config\n\\end{verbatim}\n\nA singleton component's configuration file must also start with\n\\texttt{{[}PID{]}} and end with \\texttt{.config} or \\texttt{.cfg}.\nHere's the common pattern used for singleton component configuration\nfile names:\n\n\\begin{verbatim}\n[PID].config\n\\end{verbatim}\n\nWhen you're done creating a configuration file, you can\n\\href{/docs/7-1/user/-/knowledge_base/u/understanding-system-configuration-files\\#deploying-a-configuration-file}{deploy\nit}.\n\n\\section{Resolving Configuration File Deployment\nFailures}\\label{resolving-configuration-file-deployment-failures}\n\nThe following \\texttt{IOException} hints that the configuration file has\na syntax issue:\n\n\\begin{verbatim}\nFailed to install artifact: [path to .config or .cfg file]\njava.io.IOException: Unexpected token 78; expected: 61 (line=0, pos=107)\n\\end{verbatim}\n\nTo resolve this, fix the\n\\hyperref[configuration-file-formats]{configuration file's syntax}.\n\nGreat! Now you know how to configure module components using\nconfiguration files.\n\n\\section{Related Articles}\\label{related-articles}\n\n\\href{/docs/7-1/user/-/knowledge_base/u/understanding-system-configuration-files}{Understanding\nSystem Configuration Files}\n"
  },
  {
    "path": "book/user/user.aux",
    "content": "\\relax \n\\providecommand{\\transparent@use}[1]{}\n\\providecommand\\zref@newlabel[2]{}\n\\providecommand\\hyper@newdestlabel[2]{}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {1}The Liferay Distinction}{1}{chapter.1}\\protected@file@percent }\n\\newlabel{the-liferay-distinction}{{1}{1}{The Liferay Distinction}{chapter.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {2}What's New in Liferay 7.2!}{3}{chapter.2}\\protected@file@percent }\n\\newlabel{whats-new-in-liferay-7.2}{{2}{3}{What's New in Liferay 7.2!}{chapter.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {2.1}Key New Features}{3}{section.2.1}\\protected@file@percent }\n\\newlabel{key-new-features}{{2.1}{3}{Key New Features}{section.2.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {2.2}Experience Creation}{3}{section.2.2}\\protected@file@percent }\n\\newlabel{experience-creation}{{2.2}{3}{Experience Creation}{section.2.2}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Content Authoring}{3}{section.2.2}\\protected@file@percent }\n\\newlabel{content-authoring}{{2.2}{3}{Content Authoring}{section.2.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {2.1}{\\ignorespaces Content authors can build pages out of building blocks called \\emph  {Fragments}.}}{4}{figure.2.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{Site Building}{4}{figure.2.1}\\protected@file@percent }\n\\newlabel{site-building}{{2.2}{4}{Site Building}{figure.2.1}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Fragments}{4}{figure.2.1}\\protected@file@percent }\n\\newlabel{fragments}{{2.2}{4}{Fragments}{figure.2.1}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Fragments Toolkit/CLI}{4}{figure.2.2}\\protected@file@percent }\n\\newlabel{fragments-toolkitcli}{{2.2}{4}{Fragments Toolkit/CLI}{figure.2.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {2.2}{\\ignorespaces Fragments make it easy to build pages.}}{5}{figure.2.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {2.3}{\\ignorespaces Use the Fragments Editor or download the toolkit and use your own tools.}}{5}{figure.2.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{In-Context Editing and Content Previews}{6}{figure.2.3}\\protected@file@percent }\n\\newlabel{in-context-editing-and-content-previews}{{2.2}{6}{In-Context Editing and Content Previews}{figure.2.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Content Usages}{6}{figure.2.3}\\protected@file@percent }\n\\newlabel{content-usages}{{2.2}{6}{Content Usages}{figure.2.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{A/B Testing (DXP only)}{6}{figure.2.3}\\protected@file@percent }\n\\newlabel{ab-testing-dxp-only}{{2.2}{6}{A/B Testing (DXP only)}{figure.2.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {2.3}Personalization}{6}{section.2.3}\\protected@file@percent }\n\\newlabel{personalization}{{2.3}{6}{Personalization}{section.2.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Session Rules}{6}{section.2.3}\\protected@file@percent }\n\\newlabel{session-rules}{{2.3}{6}{Session Rules}{section.2.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Rule Builder}{6}{section.2.3}\\protected@file@percent }\n\\newlabel{rule-builder}{{2.3}{6}{Rule Builder}{section.2.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Content Sets}{6}{figure.2.4}\\protected@file@percent }\n\\newlabel{content-sets}{{2.3}{6}{Content Sets}{figure.2.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {2.4}{\\ignorespaces The Rule Builder provides a drag-and-drop interface that helps you build exactly the criteria you need to target the right information to the right users.}}{7}{figure.2.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{Experiences}{7}{figure.2.4}\\protected@file@percent }\n\\newlabel{experiences}{{2.3}{7}{Experiences}{figure.2.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Content Recommendations (DXP only)}{7}{figure.2.4}\\protected@file@percent }\n\\newlabel{content-recommendations-dxp-only}{{2.3}{7}{Content Recommendations (DXP only)}{figure.2.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {2.4}Bulk Management}{8}{section.2.4}\\protected@file@percent }\n\\newlabel{bulk-management}{{2.4}{8}{Bulk Management}{section.2.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Auto-Tagging}{8}{section.2.4}\\protected@file@percent }\n\\newlabel{auto-tagging}{{2.4}{8}{Auto-Tagging}{section.2.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Bulk Operations}{8}{section.2.4}\\protected@file@percent }\n\\newlabel{bulk-operations}{{2.4}{8}{Bulk Operations}{section.2.4}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Automatic Document Versioning Policies}{8}{section.2.4}\\protected@file@percent }\n\\newlabel{automatic-document-versioning-policies}{{2.4}{8}{Automatic Document Versioning Policies}{section.2.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {2.5}Business Operations}{8}{section.2.5}\\protected@file@percent }\n\\newlabel{business-operations}{{2.5}{8}{Business Operations}{section.2.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Forms API}{8}{section.2.5}\\protected@file@percent }\n\\newlabel{forms-api}{{2.5}{8}{Forms API}{section.2.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Workflow Metrics}{9}{section.2.5}\\protected@file@percent }\n\\newlabel{workflow-metrics}{{2.5}{9}{Workflow Metrics}{section.2.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Workflow Reports}{9}{section.2.5}\\protected@file@percent }\n\\newlabel{workflow-reports}{{2.5}{9}{Workflow Reports}{section.2.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Online Document Editing}{9}{section.2.5}\\protected@file@percent }\n\\newlabel{online-document-editing}{{2.5}{9}{Online Document Editing}{section.2.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{P2P Asset Sharing}{9}{section.2.5}\\protected@file@percent }\n\\newlabel{p2p-asset-sharing}{{2.5}{9}{P2P Asset Sharing}{section.2.5}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Improved Identity Management Tools}{9}{figure.2.5}\\protected@file@percent }\n\\newlabel{improved-identity-management-tools}{{2.5}{9}{Improved Identity Management Tools}{figure.2.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {2.6}Headless Capabilities}{9}{section.2.6}\\protected@file@percent }\n\\newlabel{headless-capabilities}{{2.6}{9}{Headless Capabilities}{section.2.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {2.5}{\\ignorespaces You can share assets with other users easily.}}{10}{figure.2.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{OpenAPI}{10}{section.2.6}\\protected@file@percent }\n\\newlabel{openapi}{{2.6}{10}{OpenAPI}{section.2.6}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Headless CMS}{10}{section.2.6}\\protected@file@percent }\n\\newlabel{headless-cms}{{2.6}{10}{Headless CMS}{section.2.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {2.7}Developer Experience}{11}{section.2.7}\\protected@file@percent }\n\\newlabel{developer-experience}{{2.7}{11}{Developer Experience}{section.2.7}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Upgrade Tool}{11}{section.2.7}\\protected@file@percent }\n\\newlabel{upgrade-tool}{{2.7}{11}{Upgrade Tool}{section.2.7}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Front-end Toolkits}{11}{section.2.7}\\protected@file@percent }\n\\newlabel{front-end-toolkits}{{2.7}{11}{Front-end Toolkits}{section.2.7}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Struts Removed}{11}{section.2.7}\\protected@file@percent }\n\\newlabel{struts-removed}{{2.7}{11}{Struts Removed}{section.2.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {2.8}What's Next}{11}{section.2.8}\\protected@file@percent }\n\\newlabel{whats-next}{{2.8}{11}{What's Next}{section.2.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {3}Setting Up}{13}{chapter.3}\\protected@file@percent }\n\\newlabel{setting-up}{{3}{13}{Setting Up}{chapter.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {4}Where Configuration Happens}{15}{chapter.4}\\protected@file@percent }\n\\newlabel{where-configuration-happens}{{4}{15}{Where Configuration Happens}{chapter.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {5}Configuration Scope}{17}{chapter.5}\\protected@file@percent }\n\\newlabel{configuration-scope}{{5}{17}{Configuration Scope}{chapter.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {6}System Wide Settings}{19}{chapter.6}\\protected@file@percent }\n\\newlabel{system-wide-settings}{{6}{19}{System Wide Settings}{chapter.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {7}System Settings}{21}{chapter.7}\\protected@file@percent }\n\\newlabel{system-settings}{{7}{21}{System Settings}{chapter.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {7.1}Editing System Configurations}{21}{section.7.1}\\protected@file@percent }\n\\newlabel{editing-system-configurations}{{7.1}{21}{Editing System Configurations}{section.7.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {7.1}{\\ignorespaces System Settings are accessed through the Control Panel.}}{22}{figure.7.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {7.2}{\\ignorespaces System Settings are organized by section and category.}}{23}{figure.7.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {7.3}{\\ignorespaces After saving changes to a configuration, the actions \\emph  {Reset Default Values} and \\emph  {Export} are available for it.}}{23}{figure.7.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {7.2}Configuration Scope}{23}{section.7.2}\\protected@file@percent }\n\\newlabel{configuration-scope-1}{{7.2}{23}{Configuration Scope}{section.7.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {7.4}{\\ignorespaces Some System Settings entries are system scoped.}}{24}{figure.7.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {7.5}{\\ignorespaces Some System Settings are virtual instance scoped.}}{24}{figure.7.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {7.6}{\\ignorespaces Some System Settings are site scoped.}}{24}{figure.7.6}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {7.7}{\\ignorespaces Some System Settings entries are widget scoped.}}{25}{figure.7.7}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {7.3}Exporting and Importing Configurations}{25}{section.7.3}\\protected@file@percent }\n\\newlabel{exporting-and-importing-configurations}{{7.3}{25}{Exporting and Importing Configurations}{section.7.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {8}Understanding System Configuration Files}{27}{chapter.8}\\protected@file@percent }\n\\newlabel{understanding-system-configuration-files}{{8}{27}{Understanding System Configuration Files}{chapter.8}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {9}Creating Configuration Files}{29}{chapter.9}\\protected@file@percent }\n\\newlabel{creating-configuration-files}{{9}{29}{Creating Configuration Files}{chapter.9}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {9.1}{\\ignorespaces The Web Content System Settings entry has the back-end ID \\texttt  {com.liferay.journal.configuration.JournalServiceConfiguration}.}}{29}{figure.9.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {9.1}Key/Value Syntax}{29}{section.9.1}\\protected@file@percent }\n\\newlabel{keyvalue-syntax}{{9.1}{29}{Key/Value Syntax}{section.9.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {9.2}Multi-Value Settings}{30}{section.9.2}\\protected@file@percent }\n\\newlabel{multi-value-settings}{{9.2}{30}{Multi-Value Settings}{section.9.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {9.2}{\\ignorespaces The Web Content System Settings entry has many \\emph  {Characters Blacklist} fields.}}{30}{figure.9.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {9.3}Escaping Characters}{31}{section.9.3}\\protected@file@percent }\n\\newlabel{escaping-characters}{{9.3}{31}{Escaping Characters}{section.9.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {9.4}Typed Values}{31}{section.9.4}\\protected@file@percent }\n\\newlabel{typed-values}{{9.4}{31}{Typed Values}{section.9.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {9.5}Deploying a Configuration File}{31}{section.9.5}\\protected@file@percent }\n\\newlabel{deploying-a-configuration-file}{{9.5}{31}{Deploying a Configuration File}{section.9.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {9.6}Configuration Files and Clustering}{32}{section.9.6}\\protected@file@percent }\n\\newlabel{configuration-files-and-clustering}{{9.6}{32}{Configuration Files and Clustering}{section.9.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {10}Factory Configurations}{33}{chapter.10}\\protected@file@percent }\n\\newlabel{factory-configurations}{{10}{33}{Factory Configurations}{chapter.10}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {10.1}{\\ignorespaces If a System Settings entry has an ADD button, it's suitable for factory configurations.}}{33}{figure.10.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {11}Server Administration}{37}{chapter.11}\\protected@file@percent }\n\\newlabel{server-administration}{{11}{37}{Server Administration}{chapter.11}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {11.1}{\\ignorespaces The Resources tab of Server Administration shows a graph of your server's memory usage.}}{37}{figure.11.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {12}Server Administration: Resources}{39}{chapter.12}\\protected@file@percent }\n\\newlabel{server-administration-resources}{{12}{39}{Server Administration: Resources}{chapter.12}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {12.1}{\\ignorespaces The Resources tab of Server Administration lets you execute several server maintenance tasks.}}{40}{figure.12.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {13}Server Administration: External Services}{41}{chapter.13}\\protected@file@percent }\n\\newlabel{server-administration-external-services}{{13}{41}{Server Administration: External Services}{chapter.13}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {13.1}ImageMagick Configuration}{42}{section.13.1}\\protected@file@percent }\n\\newlabel{imagemagick-configuration}{{13.1}{42}{ImageMagick Configuration}{section.13.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {13.1}{\\ignorespaces Enable ImageMagick and Ghostscript, and verify that the paths are correct.}}{42}{figure.13.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {13.2}Xuggler Configuration}{42}{section.13.2}\\protected@file@percent }\n\\newlabel{xuggler-configuration}{{13.2}{42}{Xuggler Configuration}{section.13.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {13.2}{\\ignorespaces Install Xuggler.}}{43}{figure.13.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {14}Setting Up a Virtual Instance}{45}{chapter.14}\\protected@file@percent }\n\\newlabel{setting-up-a-virtual-instance}{{14}{45}{Setting Up a Virtual Instance}{chapter.14}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {15}Virtual Instances}{47}{chapter.15}\\protected@file@percent }\n\\newlabel{virtual-instances}{{15}{47}{Virtual Instances}{chapter.15}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {15.1}{\\ignorespaces Add and manage virtual instances of Liferay in the Control Panel's \\emph  {Configuration} → \\emph  {Virtual Instances} section.}}{47}{figure.15.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {16}Configuring Virtual Instances}{49}{chapter.16}\\protected@file@percent }\n\\newlabel{configuring-virtual-instances}{{16}{49}{Configuring Virtual Instances}{chapter.16}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {17}Email Instance Settings}{51}{chapter.17}\\protected@file@percent }\n\\newlabel{email-instance-settings}{{17}{51}{Email Instance Settings}{chapter.17}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {17.1}Account Created Notification}{51}{section.17.1}\\protected@file@percent }\n\\newlabel{account-created-notification}{{17.1}{51}{Account Created Notification}{section.17.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {17.2}Email Sender}{52}{section.17.2}\\protected@file@percent }\n\\newlabel{email-sender}{{17.2}{52}{Email Sender}{section.17.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {17.1}{\\ignorespaces Customize the email template for the email messages sent to new Users.}}{52}{figure.17.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {17.3}Email Verification Notification}{52}{section.17.3}\\protected@file@percent }\n\\newlabel{email-verification-notification}{{17.3}{52}{Email Verification Notification}{section.17.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {17.4}Mail Host Names}{52}{section.17.4}\\protected@file@percent }\n\\newlabel{mail-host-names}{{17.4}{52}{Mail Host Names}{section.17.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {17.5}Password Changed Notification}{53}{section.17.5}\\protected@file@percent }\n\\newlabel{password-changed-notification}{{17.5}{53}{Password Changed Notification}{section.17.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {17.6}Password Reset Notification}{53}{section.17.6}\\protected@file@percent }\n\\newlabel{password-reset-notification}{{17.6}{53}{Password Reset Notification}{section.17.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {17.2}{\\ignorespaces There are some handy variables available for use in email templates.}}{53}{figure.17.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {18}Instance Configuration Instance Settings}{55}{chapter.18}\\protected@file@percent }\n\\newlabel{instance-configuration-instance-settings}{{18}{55}{Instance Configuration Instance Settings}{chapter.18}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {18.1}Appearance}{55}{section.18.1}\\protected@file@percent }\n\\newlabel{appearance}{{18.1}{55}{Appearance}{section.18.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {18.2}Contact Information}{56}{section.18.2}\\protected@file@percent }\n\\newlabel{contact-information}{{18.2}{56}{Contact Information}{section.18.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {18.3}General}{56}{section.18.3}\\protected@file@percent }\n\\newlabel{general}{{18.3}{56}{General}{section.18.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {18.4}Terms of Use}{57}{section.18.4}\\protected@file@percent }\n\\newlabel{terms-of-use}{{18.4}{57}{Terms of Use}{section.18.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {18.1}{\\ignorespaces The Site ID in Site Settings is the Group ID in the terms of Use configuration.}}{57}{figure.18.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {18.2}{\\ignorespaces The Web Content Article ID is displayed in the edit screen.}}{57}{figure.18.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {18.3}{\\ignorespaces Turn a Web Content Article into the Site's Terms of Use page.}}{58}{figure.18.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {19}User Authentication Instance Settings}{59}{chapter.19}\\protected@file@percent }\n\\newlabel{user-authentication-instance-settings}{{19}{59}{User Authentication Instance Settings}{chapter.19}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {19.1}General}{59}{section.19.1}\\protected@file@percent }\n\\newlabel{general-1}{{19.1}{59}{General}{section.19.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {19.1}{\\ignorespaces Configure general authentication behavior and settings for external authentication systems.}}{60}{figure.19.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {19.2}Reserved Credentials}{61}{section.19.2}\\protected@file@percent }\n\\newlabel{reserved-credentials}{{19.2}{61}{Reserved Credentials}{section.19.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {20}Users Instance Settings}{63}{chapter.20}\\protected@file@percent }\n\\newlabel{users-instance-settings}{{20}{63}{Users Instance Settings}{chapter.20}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {20.1}Personal Menu}{63}{section.20.1}\\protected@file@percent }\n\\newlabel{personal-menu}{{20.1}{63}{Personal Menu}{section.20.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {20.2}Default User Associations}{63}{section.20.2}\\protected@file@percent }\n\\newlabel{default-user-associations}{{20.2}{63}{Default User Associations}{section.20.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {20.3}Fields}{64}{section.20.3}\\protected@file@percent }\n\\newlabel{fields}{{20.3}{64}{Fields}{section.20.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {21}More Platform Section Instance Settings}{65}{chapter.21}\\protected@file@percent }\n\\newlabel{more-platform-section-instance-settings}{{21}{65}{More Platform Section Instance Settings}{chapter.21}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {21.1}Infrastructure}{65}{section.21.1}\\protected@file@percent }\n\\newlabel{infrastructure}{{21.1}{65}{Infrastructure}{section.21.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {21.2}Localization}{65}{section.21.2}\\protected@file@percent }\n\\newlabel{localization}{{21.2}{65}{Localization}{section.21.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {21.3}Analytics}{66}{section.21.3}\\protected@file@percent }\n\\newlabel{analytics}{{21.3}{66}{Analytics}{section.21.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {21.4}Third Party}{66}{section.21.4}\\protected@file@percent }\n\\newlabel{third-party}{{21.4}{66}{Third Party}{section.21.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {22}Using Liferay's Script Engine}{67}{chapter.22}\\protected@file@percent }\n\\newlabel{using-liferays-script-engine}{{22}{67}{Using Liferay's Script Engine}{chapter.22}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {23}Invoking Liferay Services From Scripts}{69}{chapter.23}\\protected@file@percent }\n\\newlabel{invoking-liferay-services-from-scripts}{{23}{69}{Invoking Liferay Services From Scripts}{chapter.23}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {23.1}Related Topics}{70}{section.23.1}\\protected@file@percent }\n\\newlabel{related-topics}{{23.1}{70}{Related Topics}{section.23.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {24}Running Scripts From the Script Console}{71}{chapter.24}\\protected@file@percent }\n\\newlabel{running-scripts-from-the-script-console}{{24}{71}{Running Scripts From the Script Console}{chapter.24}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {24.1}Running the Sample Script}{71}{section.24.1}\\protected@file@percent }\n\\newlabel{running-the-sample-script}{{24.1}{71}{Running the Sample Script}{section.24.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {24.1}{\\ignorespaces The script console's sample Groovy script prints the User count to the console's \\emph  {Output} section.}}{72}{figure.24.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {24.2}Predefined Variables}{72}{section.24.2}\\protected@file@percent }\n\\newlabel{predefined-variables}{{24.2}{72}{Predefined Variables}{section.24.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {24.2}{\\ignorespaces Here's an example of invoking a Groovy script that uses the predefined \\texttt  {out}, \\texttt  {actionRequest}, and \\texttt  {userInfo} variables to print information about the company and current user.}}{73}{figure.24.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {24.3}Tips}{73}{section.24.3}\\protected@file@percent }\n\\newlabel{tips}{{24.3}{73}{Tips}{section.24.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {25}Leveraging the Script Engine in Workflow}{75}{chapter.25}\\protected@file@percent }\n\\newlabel{leveraging-the-script-engine-in-workflow}{{25}{75}{Leveraging the Script Engine in Workflow}{chapter.25}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {25.1}Injected Variables}{75}{section.25.1}\\protected@file@percent }\n\\newlabel{injected-variables}{{25.1}{75}{Injected Variables}{section.25.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {25.2}Variables that are Always Available}{75}{section.25.2}\\protected@file@percent }\n\\newlabel{variables-that-are-always-available}{{25.2}{75}{Variables that are Always Available}{section.25.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {25.3}Variables Injected into Task Nodes}{76}{section.25.3}\\protected@file@percent }\n\\newlabel{variables-injected-into-task-nodes}{{25.3}{76}{Variables Injected into Task Nodes}{section.25.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {25.4}Scripting Examples}{76}{section.25.4}\\protected@file@percent }\n\\newlabel{scripting-examples}{{25.4}{76}{Scripting Examples}{section.25.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {25.5}Calling OSGi Services}{78}{section.25.5}\\protected@file@percent }\n\\newlabel{calling-osgi-services}{{25.5}{78}{Calling OSGi Services}{section.25.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {25.6}Related Topics}{79}{section.25.6}\\protected@file@percent }\n\\newlabel{related-topics-1}{{25.6}{79}{Related Topics}{section.25.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {26}Script Examples}{81}{chapter.26}\\protected@file@percent }\n\\newlabel{script-examples}{{26}{81}{Script Examples}{chapter.26}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {26.1}Example 1: Presenting New Terms of Use to Users}{81}{section.26.1}\\protected@file@percent }\n\\newlabel{example-1-presenting-new-terms-of-use-to-users}{{26.1}{81}{Example 1: Presenting New Terms of Use to Users}{section.26.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {26.2}Example 2: Embedding HTML Markup in Script Outputs}{82}{section.26.2}\\protected@file@percent }\n\\newlabel{example-2-embedding-html-markup-in-script-outputs}{{26.2}{82}{Example 2: Embedding HTML Markup in Script Outputs}{section.26.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {26.1}{\\ignorespaces Here's an example of invoking a Groovy script that embeds HTML markup in the output of the script.}}{83}{figure.26.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {26.3}Example 3: Show Exceptions in the Script Console}{83}{section.26.3}\\protected@file@percent }\n\\newlabel{example-3-show-exceptions-in-the-script-console}{{26.3}{83}{Example 3: Show Exceptions in the Script Console}{section.26.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {26.2}{\\ignorespaces Here's an example of a Groovy script that catches exceptions and prints exception information to the script console.}}{83}{figure.26.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {26.4}Example 4: Implement a Preview Mode}{84}{section.26.4}\\protected@file@percent }\n\\newlabel{example-4-implement-a-preview-mode}{{26.4}{84}{Example 4: Implement a Preview Mode}{section.26.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {26.5}Example 5: Plan a File Output for Long-Running Scripts}{84}{section.26.5}\\protected@file@percent }\n\\newlabel{example-5-plan-a-file-output-for-long-running-scripts}{{26.5}{84}{Example 5: Plan a File Output for Long-Running Scripts}{section.26.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {26.6}Related Topics}{85}{section.26.6}\\protected@file@percent }\n\\newlabel{related-topics-2}{{26.6}{85}{Related Topics}{section.26.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {27}Custom Fields}{87}{chapter.27}\\protected@file@percent }\n\\newlabel{custom-fields}{{27}{87}{Custom Fields}{chapter.27}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {27.1}Adding Custom Fields}{88}{section.27.1}\\protected@file@percent }\n\\newlabel{adding-custom-fields}{{27.1}{88}{Adding Custom Fields}{section.27.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {27.1}{\\ignorespaces At The Lunar Resort, a Head Circumference field is necessary for all users.}}{88}{figure.27.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {27.2}{\\ignorespaces The Custom Fields panel is found at the bottom of the Edit User form.}}{89}{figure.27.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {27.2}Editing a Custom Field}{89}{section.27.2}\\protected@file@percent }\n\\newlabel{editing-a-custom-field}{{27.2}{89}{Editing a Custom Field}{section.27.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {27.3}{\\ignorespaces The exact Custom Fields configuration options you use depend on the field type you choose.}}{90}{figure.27.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {27.4}{\\ignorespaces You can delete a custom field, edit it, or configure its permissions.}}{91}{figure.27.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {28}Managing Users}{93}{chapter.28}\\protected@file@percent }\n\\newlabel{managing-users}{{28}{93}{Managing Users}{chapter.28}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {28.1}{\\ignorespaces Administrators can access the Control Panel from the Product Menu.}}{94}{figure.28.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {29}Users and Organizations}{95}{chapter.29}\\protected@file@percent }\n\\newlabel{users-and-organizations}{{29}{95}{Users and Organizations}{chapter.29}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {29.1}What are Users?}{95}{section.29.1}\\protected@file@percent }\n\\newlabel{what-are-users}{{29.1}{95}{What are Users?}{section.29.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {30}Adding, Editing, and Deleting Users}{97}{chapter.30}\\protected@file@percent }\n\\newlabel{adding-editing-and-deleting-users}{{30}{97}{Adding, Editing, and Deleting Users}{chapter.30}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {30.1}Adding Users}{97}{section.30.1}\\protected@file@percent }\n\\newlabel{adding-users}{{30.1}{97}{Adding Users}{section.30.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {30.1}{\\ignorespaces Add Users from the Users and Organizations section of the Control Panel.}}{98}{figure.30.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {30.2}Editing Users}{98}{section.30.2}\\protected@file@percent }\n\\newlabel{editing-users}{{30.2}{98}{Editing Users}{section.30.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {30.2}{\\ignorespaces At a minimum, enter a screen name, email address, and first name to create a new user account. Then you'll be taken to the Information form and can continue configuring the user.}}{99}{figure.30.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {30.3}{\\ignorespaces Enter the password twice to manually set the password for a user. If the Password Policy you're using is configured to allow it, select whether to require the user to reset their password the first time they sign in to the portal.}}{100}{figure.30.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {30.3}Deleting Users}{100}{section.30.3}\\protected@file@percent }\n\\newlabel{deleting-users}{{30.3}{100}{Deleting Users}{section.30.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {30.4}{\\ignorespaces You can choose whether to view active or inactive (deactivated) portal users in the users list found at \\emph  {Product Menu} → \\emph  {Control Panel} → \\emph  {Users} → \\emph  {Users and Organizations}.}}{101}{figure.30.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {31}User Management: Additional Topics}{103}{chapter.31}\\protected@file@percent }\n\\newlabel{user-management-additional-topics}{{31}{103}{User Management: Additional Topics}{chapter.31}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {31.1}Password Resets}{103}{section.31.1}\\protected@file@percent }\n\\newlabel{password-resets}{{31.1}{103}{Password Resets}{section.31.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {31.2}Adding an Administrative User}{103}{section.31.2}\\protected@file@percent }\n\\newlabel{adding-an-administrative-user}{{31.2}{103}{Adding an Administrative User}{section.31.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {31.3}Gender}{104}{section.31.3}\\protected@file@percent }\n\\newlabel{gender}{{31.3}{104}{Gender}{section.31.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {31.4}User Profile Pictures}{104}{section.31.4}\\protected@file@percent }\n\\newlabel{user-profile-pictures}{{31.4}{104}{User Profile Pictures}{section.31.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {31.1}{\\ignorespaces Upload images for user avatars in the Edit User form.}}{105}{figure.31.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {31.2}{\\ignorespaces The default user profile picture is an icon with the user initials over a randomly colored bubble.}}{105}{figure.31.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {31.3}{\\ignorespaces If you disable the default initials-based profile picture, this icon is used instead.}}{105}{figure.31.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {31.5}Numeric Screen Names}{106}{section.31.5}\\protected@file@percent }\n\\newlabel{numeric-screen-names}{{31.5}{106}{Numeric Screen Names}{section.31.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {32}Organizations}{109}{chapter.32}\\protected@file@percent }\n\\newlabel{organizations}{{32}{109}{Organizations}{chapter.32}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {32.1}When to Use Organizations}{109}{section.32.1}\\protected@file@percent }\n\\newlabel{when-to-use-organizations}{{32.1}{109}{When to Use Organizations}{section.32.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {32.2}What can Organization Administrators Do?}{110}{section.32.2}\\protected@file@percent }\n\\newlabel{what-can-organization-administrators-do}{{32.2}{110}{What can Organization Administrators Do?}{section.32.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {32.1}{\\ignorespaces The My Organizations application lets Organization Administrators manage their organizations in their personal site.}}{111}{figure.32.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {32.3}Organization Roles and Permissions}{112}{section.32.3}\\protected@file@percent }\n\\newlabel{organization-roles-and-permissions}{{32.3}{112}{Organization Roles and Permissions}{section.32.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {32.4}Organization Sites}{112}{section.32.4}\\protected@file@percent }\n\\newlabel{organization-sites}{{32.4}{112}{Organization Sites}{section.32.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {33}Managing Organizations}{113}{chapter.33}\\protected@file@percent }\n\\newlabel{managing-organizations}{{33}{113}{Managing Organizations}{chapter.33}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {33.1}Adding Organizations}{113}{section.33.1}\\protected@file@percent }\n\\newlabel{adding-organizations}{{33.1}{113}{Adding Organizations}{section.33.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {33.2}Editing Organizations}{114}{section.33.2}\\protected@file@percent }\n\\newlabel{editing-organizations}{{33.2}{114}{Editing Organizations}{section.33.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {33.3}Organization Types}{114}{section.33.3}\\protected@file@percent }\n\\newlabel{organization-types}{{33.3}{114}{Organization Types}{section.33.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {33.1}{\\ignorespaces Create new organization types through the System Settings entry called Organization Types.}}{115}{figure.33.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {33.2}{\\ignorespaces Custom configuration types are available in the Add Organization form.}}{116}{figure.33.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {34}Roles and Permissions}{117}{chapter.34}\\protected@file@percent }\n\\newlabel{roles-and-permissions}{{34}{117}{Roles and Permissions}{chapter.34}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {34.1}{\\ignorespaces Assign Users to a role, directly or by their association with a Site, Organization, or User Group.}}{117}{figure.34.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {34.1}Deleting Asset Containers}{118}{section.34.1}\\protected@file@percent }\n\\newlabel{deleting-asset-containers}{{34.1}{118}{Deleting Asset Containers}{section.34.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {34.2}Default Liferay Roles}{118}{section.34.2}\\protected@file@percent }\n\\newlabel{default-liferay-roles}{{34.2}{118}{Default Liferay Roles}{section.34.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {35}Managing Roles}{121}{chapter.35}\\protected@file@percent }\n\\newlabel{managing-roles}{{35}{121}{Managing Roles}{chapter.35}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {35.1}{\\ignorespaces The Roles application lets you add and manage roles for the global (Regular), Site, or Organization scope.}}{121}{figure.35.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {35.1}Creating Roles}{122}{section.35.1}\\protected@file@percent }\n\\newlabel{creating-roles}{{35.1}{122}{Creating Roles}{section.35.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {35.2}Assigning Users to a Role}{122}{section.35.2}\\protected@file@percent }\n\\newlabel{assigning-users-to-a-role}{{35.2}{122}{Assigning Users to a Role}{section.35.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {36}Defining Role Permissions}{123}{chapter.36}\\protected@file@percent }\n\\newlabel{defining-role-permissions}{{36}{123}{Defining Role Permissions}{chapter.36}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {36.1}{\\ignorespaces When defining permissions on a Role, the Summary view provides a list of permissions that have already been defined for the role. The area on the left side of the screen lets you drill down through various categories of permissions.}}{124}{figure.36.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {36.2}{\\ignorespaces Users assigned to the User Group Manager Role can't find any users to add unless they have view permissions on the User resource.}}{124}{figure.36.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {36.1}Delegating Social Activities Configuration}{125}{section.36.1}\\protected@file@percent }\n\\newlabel{delegating-social-activities-configuration}{{36.1}{125}{Delegating Social Activities Configuration}{section.36.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {36.3}{\\ignorespaces You can fine-tune which actions are defined for a role within a specific application like the Message Boards.}}{126}{figure.36.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {37}Managing User Data}{127}{chapter.37}\\protected@file@percent }\n\\newlabel{managing-user-data}{{37}{127}{Managing User Data}{chapter.37}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {37.1}Anonymizing Data}{128}{section.37.1}\\protected@file@percent }\n\\newlabel{anonymizing-data}{{37.1}{128}{Anonymizing Data}{section.37.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {37.1}{\\ignorespaces Anonymized content is presented with the User Anonymous Anonymous's identifying information.}}{128}{figure.37.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {37.2}{\\ignorespaces Assign your own Anonymous User from Control Panel → Configuration → System Settings → Users → Anonymous User.}}{129}{figure.37.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {37.2}Manual Anonymization}{129}{section.37.2}\\protected@file@percent }\n\\newlabel{manual-anonymization}{{37.2}{129}{Manual Anonymization}{section.37.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {37.3}{\\ignorespaces Even though this Message Boards Message (a comment on a blog post in this case) is anonymized, it should be edited to remove User Associated Data from the content of the message.}}{131}{figure.37.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {38}Sanitizing User Data}{133}{chapter.38}\\protected@file@percent }\n\\newlabel{sanitizing-user-data}{{38}{133}{Sanitizing User Data}{chapter.38}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {38.1}The Personal Data Erasure Screen}{134}{section.38.1}\\protected@file@percent }\n\\newlabel{the-personal-data-erasure-screen}{{38.1}{134}{The Personal Data Erasure Screen}{section.38.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {38.1}{\\ignorespaces From here, you can browse all data the user posted on his or her personal Site.}}{134}{figure.38.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {38.2}{\\ignorespaces Choose Regular Sites to browse all data posted by the user on administratively-created Sites.}}{135}{figure.38.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {38.3}{\\ignorespaces Pepper's blog entry might need review.}}{135}{figure.38.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {38.2}Delete the User}{136}{section.38.2}\\protected@file@percent }\n\\newlabel{delete-the-user}{{38.2}{136}{Delete the User}{section.38.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {38.4}{\\ignorespaces To finish the data erasure process, delete the User.}}{136}{figure.38.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {39}Exporting User Data}{137}{chapter.39}\\protected@file@percent }\n\\newlabel{exporting-user-data}{{39}{137}{Exporting User Data}{chapter.39}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {39.1}Exporting and Downloading}{137}{section.39.1}\\protected@file@percent }\n\\newlabel{exporting-and-downloading}{{39.1}{137}{Exporting and Downloading}{section.39.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {39.1}{\\ignorespaces The Export Personal Data tool lets you export all or some of the User's data.}}{138}{figure.39.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {39.2}{\\ignorespaces Once User data is successfully exported, the export process is displayed in the User's Export Personal Data list.}}{139}{figure.39.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {39.2}Examining Exported Data}{139}{section.39.2}\\protected@file@percent }\n\\newlabel{examining-exported-data}{{39.2}{139}{Examining Exported Data}{section.39.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {39.3}{\\ignorespaces A Comment on a blog post is User Associated Data.}}{141}{figure.39.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {40}User Groups}{143}{chapter.40}\\protected@file@percent }\n\\newlabel{user-groups}{{40}{143}{User Groups}{chapter.40}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {41}Creating a User Group}{145}{chapter.41}\\protected@file@percent }\n\\newlabel{creating-a-user-group}{{41}{145}{Creating a User Group}{chapter.41}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {41.1}Assigning Members to a User Group}{145}{section.41.1}\\protected@file@percent }\n\\newlabel{assigning-members-to-a-user-group}{{41.1}{145}{Assigning Members to a User Group}{section.41.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {41.1}{\\ignorespaces The New User Group form.}}{146}{figure.41.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {41.2}{\\ignorespaces The user group you just created now appears in the table.}}{146}{figure.41.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {41.3}{\\ignorespaces Select the users to add to the user group.}}{147}{figure.41.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {42}User Groups and Site Membership}{149}{chapter.42}\\protected@file@percent }\n\\newlabel{user-groups-and-site-membership}{{42}{149}{User Groups and Site Membership}{chapter.42}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {42.1}{\\ignorespaces Select \\emph  {Memberships} from the People menu.}}{150}{figure.42.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {42.2}{\\ignorespaces The User Groups tab in Memberships shows the User Groups currently assigned to the Site.}}{150}{figure.42.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {43}User Group Sites}{151}{chapter.43}\\protected@file@percent }\n\\newlabel{user-group-sites}{{43}{151}{User Group Sites}{chapter.43}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {43.1}Creating User Group Sites From Site Templates}{151}{section.43.1}\\protected@file@percent }\n\\newlabel{creating-user-group-sites-from-site-templates}{{43.1}{151}{Creating User Group Sites From Site Templates}{section.43.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {43.2}Creating User Group Sites Manually}{152}{section.43.2}\\protected@file@percent }\n\\newlabel{creating-user-group-sites-manually}{{43.2}{152}{Creating User Group Sites Manually}{section.43.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {43.3}Legacy User Group Sites Behavior}{153}{section.43.3}\\protected@file@percent }\n\\newlabel{legacy-user-group-sites-behavior}{{43.3}{153}{Legacy User Group Sites Behavior}{section.43.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {44}Configuring User Group Permissions}{155}{chapter.44}\\protected@file@percent }\n\\newlabel{configuring-user-group-permissions}{{44}{155}{Configuring User Group Permissions}{chapter.44}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {44.1}{\\ignorespaces Select \\emph  {Memberships} from the Site Administration menu.}}{156}{figure.44.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {44.2}{\\ignorespaces Select \\emph  {Assign Site Roles} for the user group.}}{156}{figure.44.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {45}Editing User Groups}{157}{chapter.45}\\protected@file@percent }\n\\newlabel{editing-user-groups}{{45}{157}{Editing User Groups}{chapter.45}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {45.1}{\\ignorespaces The user groups appear in a table.}}{157}{figure.45.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {45.2}{\\ignorespaces The list of Users lets you manage the User Group's membership.}}{158}{figure.45.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {45.3}{\\ignorespaces The Actions menu for a user group.}}{158}{figure.45.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {46}Password Policies}{159}{chapter.46}\\protected@file@percent }\n\\newlabel{password-policies}{{46}{159}{Password Policies}{chapter.46}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {46.1}Adding and Configuring Password Policies}{159}{section.46.1}\\protected@file@percent }\n\\newlabel{adding-and-configuring-password-policies}{{46.1}{159}{Adding and Configuring Password Policies}{section.46.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {46.1}{\\ignorespaces You can create new password policies to suit your needs.}}{160}{figure.46.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {46.2}Assigning Users to a Password Policy}{160}{section.46.2}\\protected@file@percent }\n\\newlabel{assigning-users-to-a-password-policy}{{46.2}{160}{Assigning Users to a Password Policy}{section.46.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {46.2}{\\ignorespaces Assign members to new password policies to make them take effect.}}{161}{figure.46.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {46.3}Default Policy Properties}{161}{section.46.3}\\protected@file@percent }\n\\newlabel{default-policy-properties}{{46.3}{161}{Default Policy Properties}{section.46.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {47}Auditing Users}{163}{chapter.47}\\protected@file@percent }\n\\newlabel{auditing-users}{{47}{163}{Auditing Users}{chapter.47}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {48}Viewing Audit Events}{165}{chapter.48}\\protected@file@percent }\n\\newlabel{viewing-audit-events}{{48}{165}{Viewing Audit Events}{chapter.48}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {48.1}{\\ignorespaces The Audit app displays the events it captures in a searchable list.}}{165}{figure.48.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {48.2}{\\ignorespaces Click an event in the list to show its details. The details for this event show that John Watson updated his user account's \\texttt  {prefixId} from \\texttt  {1} to \\texttt  {4}. The \\texttt  {prefixId} represents a name prefix like Dr., Mr., Mrs., or Ms.}}{166}{figure.48.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {48.1}Finding Audit Events}{166}{section.48.1}\\protected@file@percent }\n\\newlabel{finding-audit-events}{{48.1}{166}{Finding Audit Events}{section.48.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {48.3}{\\ignorespaces Searching for audit events is easy with the Audit app's advanced search form. You can specify various search criteria to find the types of events you want.}}{168}{figure.48.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {48.4}{\\ignorespaces This record shows that the default administrative user removed the Power User Role from the User Test Test.}}{169}{figure.48.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {49}Configuring Audits}{171}{chapter.49}\\protected@file@percent }\n\\newlabel{configuring-audits}{{49}{171}{Configuring Audits}{chapter.49}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {49.1}Reporting Audit Events in Liferay's Logs and Console}{171}{section.49.1}\\protected@file@percent }\n\\newlabel{reporting-audit-events-in-liferays-logs-and-console}{{49.1}{171}{Reporting Audit Events in Liferay's Logs and Console}{section.49.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {49.2}Configuring Audit Events for Scheduled Liferay Jobs}{172}{section.49.2}\\protected@file@percent }\n\\newlabel{configuring-audit-events-for-scheduled-liferay-jobs}{{49.2}{172}{Configuring Audit Events for Scheduled Liferay Jobs}{section.49.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {49.3}Enabling or Disabling Audit Events Entirely}{173}{section.49.3}\\protected@file@percent }\n\\newlabel{enabling-or-disabling-audit-events-entirely}{{49.3}{173}{Enabling or Disabling Audit Events Entirely}{section.49.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {49.4}The End of the Story}{173}{section.49.4}\\protected@file@percent }\n\\newlabel{the-end-of-the-story}{{49.4}{173}{The End of the Story}{section.49.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {50}Web Experience Management}{175}{chapter.50}\\protected@file@percent }\n\\newlabel{web-experience-management}{{50}{175}{Web Experience Management}{chapter.50}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {51}Authoring Content: Structured and Inline Content}{177}{chapter.51}\\protected@file@percent }\n\\newlabel{authoring-content-structured-and-inline-content}{{51}{177}{Authoring Content: Structured and Inline Content}{chapter.51}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {51.1}Structured Web Content}{177}{section.51.1}\\protected@file@percent }\n\\newlabel{structured-web-content}{{51.1}{177}{Structured Web Content}{section.51.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {51.2}Inline Content}{178}{section.51.2}\\protected@file@percent }\n\\newlabel{inline-content}{{51.2}{178}{Inline Content}{section.51.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {51.3}What's Best for Your Use Case?}{178}{section.51.3}\\protected@file@percent }\n\\newlabel{whats-best-for-your-use-case}{{51.3}{178}{What's Best for Your Use Case?}{section.51.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {52}Building a Site}{181}{chapter.52}\\protected@file@percent }\n\\newlabel{building-a-site}{{52}{181}{Building a Site}{chapter.52}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {53}Site Management}{183}{chapter.53}\\protected@file@percent }\n\\newlabel{site-management}{{53}{183}{Site Management}{chapter.53}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {54}Understanding Site Management}{185}{chapter.54}\\protected@file@percent }\n\\newlabel{understanding-site-management}{{54}{185}{Understanding Site Management}{chapter.54}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {54.1}Site Scope}{185}{section.54.1}\\protected@file@percent }\n\\newlabel{site-scope}{{54.1}{185}{Site Scope}{section.54.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {54.1}{\\ignorespaces Your Site's content resides in the Site Administration menu.}}{186}{figure.54.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {54.2}Site Hierarchies}{186}{section.54.2}\\protected@file@percent }\n\\newlabel{site-hierarchies}{{54.2}{186}{Site Hierarchies}{section.54.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {54.2}{\\ignorespaces The Site Map application lets users navigate among pages of a Site organized hierarchically.}}{187}{figure.54.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {54.3}Site Members}{188}{section.54.3}\\protected@file@percent }\n\\newlabel{site-members}{{54.3}{188}{Site Members}{section.54.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {54.4}Page Sets}{188}{section.54.4}\\protected@file@percent }\n\\newlabel{page-sets}{{54.4}{188}{Page Sets}{section.54.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {55}Adding Sites}{189}{chapter.55}\\protected@file@percent }\n\\newlabel{adding-sites}{{55}{189}{Adding Sites}{chapter.55}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {56}Adding Pages to Sites}{193}{chapter.56}\\protected@file@percent }\n\\newlabel{adding-pages-to-sites}{{56}{193}{Adding Pages to Sites}{chapter.56}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {57}Creating Pages}{195}{chapter.57}\\protected@file@percent }\n\\newlabel{creating-pages}{{57}{195}{Creating Pages}{chapter.57}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {57.1}{\\ignorespaces The Pages screen lets you edit your Site pages as a whole.}}{195}{figure.57.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {57.2}{\\ignorespaces Understanding the options on Site Pages.}}{196}{figure.57.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {57.3}{\\ignorespaces You must select a page type when adding pages.}}{197}{figure.57.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {57.4}{\\ignorespaces Here are three different page types as they're displayed in the heading.}}{198}{figure.57.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {58}Creating Content Pages}{199}{chapter.58}\\protected@file@percent }\n\\newlabel{creating-content-pages}{{58}{199}{Creating Content Pages}{chapter.58}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {58.1}Creating Page Fragments}{199}{section.58.1}\\protected@file@percent }\n\\newlabel{creating-page-fragments}{{58.1}{199}{Creating Page Fragments}{section.58.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {58.2}Building Content Pages}{200}{section.58.2}\\protected@file@percent }\n\\newlabel{building-content-pages}{{58.2}{200}{Building Content Pages}{section.58.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {58.3}The Content Page Interface}{200}{section.58.3}\\protected@file@percent }\n\\newlabel{the-content-page-interface}{{58.3}{200}{The Content Page Interface}{section.58.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {58.4}Personalizing Content Pages}{200}{section.58.4}\\protected@file@percent }\n\\newlabel{personalizing-content-pages}{{58.4}{200}{Personalizing Content Pages}{section.58.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {59}Content Page Elements}{201}{chapter.59}\\protected@file@percent }\n\\newlabel{content-page-elements}{{59}{201}{Content Page Elements}{chapter.59}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {59.1}{\\ignorespaces A Section named \\emph  {Banner} being displayed while editing a Content Page.}}{201}{figure.59.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {59.2}{\\ignorespaces A 3 Column and 1 Column layout stacked on top of each other.}}{202}{figure.59.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {59.3}{\\ignorespaces Here are several of Liferay's out of the box components arranged in the layout you saw previously.}}{202}{figure.59.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {59.1}Editable Elements}{203}{section.59.1}\\protected@file@percent }\n\\newlabel{editable-elements}{{59.1}{203}{Editable Elements}{section.59.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {59.2}Editable Text}{203}{section.59.2}\\protected@file@percent }\n\\newlabel{editable-text}{{59.2}{203}{Editable Text}{section.59.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {59.4}{\\ignorespaces The rich text editor provides a simple WYSIWYG interface with a number of formatting options.}}{203}{figure.59.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {59.3}Editable Images}{203}{section.59.3}\\protected@file@percent }\n\\newlabel{editable-images}{{59.3}{203}{Editable Images}{section.59.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {59.5}{\\ignorespaces Editing an image allows you to enter a URL, select an image from Documents and Media or set a link for the image.}}{204}{figure.59.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {59.4}Editable Links}{204}{section.59.4}\\protected@file@percent }\n\\newlabel{editable-links}{{59.4}{204}{Editable Links}{section.59.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {60}Content Page Management Interface}{207}{chapter.60}\\protected@file@percent }\n\\newlabel{content-page-management-interface}{{60}{207}{Content Page Management Interface}{chapter.60}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {60.1}{\\ignorespaces Each Content Page starts as a blank page.}}{207}{figure.60.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {60.1}Sections}{208}{section.60.1}\\protected@file@percent }\n\\newlabel{sections}{{60.1}{208}{Sections}{section.60.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {60.2}{\\ignorespaces \\emph  {Sections} contains Fragments that fully define spaces on your page.}}{208}{figure.60.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {60.3}{\\ignorespaces The Section managment tool provide powerful tools, but with the training wheels still on.}}{209}{figure.60.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {60.2}Section Builder}{210}{section.60.2}\\protected@file@percent }\n\\newlabel{section-builder}{{60.2}{210}{Section Builder}{section.60.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {60.4}{\\ignorespaces \\emph  {Sections Builder} contains \\emph  {Component} Fragments which are intended to be combined to create Sections.}}{210}{figure.60.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {60.3}Widgets}{210}{section.60.3}\\protected@file@percent }\n\\newlabel{widgets}{{60.3}{210}{Widgets}{section.60.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {60.5}{\\ignorespaces The Widgets section provides a list of Widgets that can be added inside of a Layout.}}{211}{figure.60.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {60.4}Page Structure}{212}{section.60.4}\\protected@file@percent }\n\\newlabel{page-structure}{{60.4}{212}{Page Structure}{section.60.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {60.6}{\\ignorespaces \\emph  {Page Structure} shows you a hierarchy of your page.}}{212}{figure.60.6}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {60.5}Look and Feel}{212}{section.60.5}\\protected@file@percent }\n\\newlabel{look-and-feel}{{60.5}{212}{Look and Feel}{section.60.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {60.6}Comments}{213}{section.60.6}\\protected@file@percent }\n\\newlabel{comments}{{60.6}{213}{Comments}{section.60.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {60.7}{\\ignorespaces Administrators can enable comments for content pages.}}{213}{figure.60.7}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {60.7}The Title Bar}{213}{section.60.7}\\protected@file@percent }\n\\newlabel{the-title-bar}{{60.7}{213}{The Title Bar}{section.60.7}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {60.8}{\\ignorespaces When creating content pages, you and your team can comment on any fragments.}}{214}{figure.60.8}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {60.9}{\\ignorespaces The title bar has several tools built into it.}}{215}{figure.60.9}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {61}Building Content Pages}{217}{chapter.61}\\protected@file@percent }\n\\newlabel{building-content-pages-1}{{61}{217}{Building Content Pages}{chapter.61}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {61.1}{\\ignorespaces You have lots of flexibility when arranging Fragments on a page.}}{217}{figure.61.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {61.1}Creating a Content Page}{218}{section.61.1}\\protected@file@percent }\n\\newlabel{creating-a-content-page}{{61.1}{218}{Creating a Content Page}{section.61.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {61.2}{\\ignorespaces You have lots of flexibility when arranging Fragments on a page.}}{219}{figure.61.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {61.3}{\\ignorespaces Edit the text and formatting as you see fit.}}{219}{figure.61.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {61.4}{\\ignorespaces Add some images, and the big picture comes together.}}{220}{figure.61.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {61.5}{\\ignorespaces Add some images, and the big picture comes together.}}{220}{figure.61.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {61.6}{\\ignorespaces You can change the background color, image, or edit spacing and padding for a section. You can also remove it.}}{220}{figure.61.6}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {62}Propagation of Changes}{223}{chapter.62}\\protected@file@percent }\n\\newlabel{propagation-of-changes}{{62}{223}{Propagation of Changes}{chapter.62}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {62.1}{\\ignorespaces Viewing the Usages and Propagation page.}}{224}{figure.62.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {63}Creating Page Fragments}{225}{chapter.63}\\protected@file@percent }\n\\newlabel{creating-page-fragments-1}{{63}{225}{Creating Page Fragments}{chapter.63}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {63.1}Creating and Managing Fragments}{225}{section.63.1}\\protected@file@percent }\n\\newlabel{creating-and-managing-fragments}{{63.1}{225}{Creating and Managing Fragments}{section.63.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {63.1}{\\ignorespaces Here is the Page Fragments page with no custom Fragments or Collections created.}}{226}{figure.63.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {63.2}{\\ignorespaces The Fragments editor provides an environment for creating all the parts of a Fragment.}}{227}{figure.63.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {63.3}{\\ignorespaces The Resources tab can be selected from the Fragment Collection.}}{228}{figure.63.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {64}Exporting and Importing Fragments}{229}{chapter.64}\\protected@file@percent }\n\\newlabel{exporting-and-importing-fragments}{{64}{229}{Exporting and Importing Fragments}{chapter.64}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {64.1}Exporting Fragments}{229}{section.64.1}\\protected@file@percent }\n\\newlabel{exporting-fragments}{{64.1}{229}{Exporting Fragments}{section.64.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {64.1}{\\ignorespaces Select Collections to export.}}{230}{figure.64.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {64.2}{\\ignorespaces Exporting all of the Fragments in a Collection.}}{230}{figure.64.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {64.2}Importing}{231}{section.64.2}\\protected@file@percent }\n\\newlabel{importing}{{64.2}{231}{Importing}{section.64.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {64.3}Importing Collections}{231}{section.64.3}\\protected@file@percent }\n\\newlabel{importing-collections}{{64.3}{231}{Importing Collections}{section.64.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {64.3}{\\ignorespaces Importing and exporting Collections is accessed from a single menu.}}{231}{figure.64.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {64.4}Importing Individual Page Fragments}{232}{section.64.4}\\protected@file@percent }\n\\newlabel{importing-individual-page-fragments}{{64.4}{232}{Importing Individual Page Fragments}{section.64.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {65}Using Widget Pages}{233}{chapter.65}\\protected@file@percent }\n\\newlabel{using-widget-pages}{{65}{233}{Using Widget Pages}{chapter.65}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {66}Creating Widget Pages}{235}{chapter.66}\\protected@file@percent }\n\\newlabel{creating-widget-pages}{{66}{235}{Creating Widget Pages}{chapter.66}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {66.1}Adding a Widget Page}{235}{section.66.1}\\protected@file@percent }\n\\newlabel{adding-a-widget-page}{{66.1}{235}{Adding a Widget Page}{section.66.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {66.1}{\\ignorespaces Create a page called \\emph  {Community} with two columns.}}{236}{figure.66.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {66.2}{\\ignorespaces Your page has been added to the navigation automatically.}}{236}{figure.66.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {66.2}Adding Widgets to a Page}{237}{section.66.2}\\protected@file@percent }\n\\newlabel{adding-widgets-to-a-page}{{66.2}{237}{Adding Widgets to a Page}{section.66.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {66.3}{\\ignorespaces Your page layout options are virtually limitless with a slew of application and layout combinations.}}{237}{figure.66.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {67}Creating Widget Pages from Templates}{239}{chapter.67}\\protected@file@percent }\n\\newlabel{creating-widget-pages-from-templates}{{67}{239}{Creating Widget Pages from Templates}{chapter.67}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {67.1}{\\ignorespaces The Blog page template is already available for use along with the Search and Wiki page templates.}}{239}{figure.67.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {67.2}{\\ignorespaces You can choose whether or not to inherit changes made to the page template.}}{240}{figure.67.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {67.1}Sharing Widget Page Templates}{241}{section.67.1}\\protected@file@percent }\n\\newlabel{sharing-widget-page-templates}{{67.1}{241}{Sharing Widget Page Templates}{section.67.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {68}Building a Responsive Site}{243}{chapter.68}\\protected@file@percent }\n\\newlabel{building-a-responsive-site}{{68}{243}{Building a Responsive Site}{chapter.68}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {69}Built-in Mobile Support}{245}{chapter.69}\\protected@file@percent }\n\\newlabel{built-in-mobile-support}{{69}{245}{Built-in Mobile Support}{chapter.69}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {69.1}{\\ignorespaces A widget adjusts its size.}}{245}{figure.69.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {69.2}{\\ignorespaces The main navigation adjusts its size.}}{246}{figure.69.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {69.1}Using the Device Simulator}{246}{section.69.1}\\protected@file@percent }\n\\newlabel{using-the-device-simulator}{{69.1}{246}{Using the Device Simulator}{section.69.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {69.2}Designing Mobile Friendly Pages}{246}{section.69.2}\\protected@file@percent }\n\\newlabel{designing-mobile-friendly-pages}{{69.2}{246}{Designing Mobile Friendly Pages}{section.69.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {69.3}{\\ignorespaces The Simulation panel defines multiple screen sizes.}}{247}{figure.69.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {70}Mobile Device Rules}{249}{chapter.70}\\protected@file@percent }\n\\newlabel{mobile-device-rules}{{70}{249}{Mobile Device Rules}{chapter.70}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {71}Creating Mobile Device Rules}{251}{chapter.71}\\protected@file@percent }\n\\newlabel{creating-mobile-device-rules}{{71}{251}{Creating Mobile Device Rules}{chapter.71}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {71.1}{\\ignorespaces Create a Mobile Device Family so you can create rules.}}{252}{figure.71.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {71.2}{\\ignorespaces Select the operating system and device type for your rule.}}{253}{figure.71.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {71.3}{\\ignorespaces You can select a mobile device family to apply for a Site or page.}}{254}{figure.71.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {72}Mobile Device Actions}{255}{chapter.72}\\protected@file@percent }\n\\newlabel{mobile-device-actions}{{72}{255}{Mobile Device Actions}{chapter.72}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {72.1}{\\ignorespaces Disable using public pages device rules.}}{255}{figure.72.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {72.2}{\\ignorespaces Getting to the Manage Actions page.}}{256}{figure.72.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {72.1}Mobile Device Rules Example}{257}{section.72.1}\\protected@file@percent }\n\\newlabel{mobile-device-rules-example}{{72.1}{257}{Mobile Device Rules Example}{section.72.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {72.3}{\\ignorespaces Create the Classification rule.}}{258}{figure.72.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {72.4}{\\ignorespaces Create the Actions for Android.}}{260}{figure.72.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {73}Using Full Page Applications}{261}{chapter.73}\\protected@file@percent }\n\\newlabel{using-full-page-applications}{{73}{261}{Using Full Page Applications}{chapter.73}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {73.1}Configuring the Page}{261}{section.73.1}\\protected@file@percent }\n\\newlabel{configuring-the-page}{{73.1}{261}{Configuring the Page}{section.73.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {73.1}{\\ignorespaces The Full Page Application configuration page.}}{262}{figure.73.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {73.2}{\\ignorespaces The Wiki displayed as a Full Page Application.}}{263}{figure.73.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {73.3}{\\ignorespaces Configuring the scope.}}{263}{figure.73.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {74}Managing Site Navigation}{265}{chapter.74}\\protected@file@percent }\n\\newlabel{managing-site-navigation}{{74}{265}{Managing Site Navigation}{chapter.74}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {75}Page Hierarchy}{267}{chapter.75}\\protected@file@percent }\n\\newlabel{page-hierarchy}{{75}{267}{Page Hierarchy}{chapter.75}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {75.1}Creating a Page}{267}{section.75.1}\\protected@file@percent }\n\\newlabel{creating-a-page}{{75.1}{267}{Creating a Page}{section.75.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {75.1}{\\ignorespaces In the default site, initially only the \\emph  {Home} and the hidden \\emph  {Search} pages exist in the Public Pages Hierarchy.}}{267}{figure.75.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {75.2}{\\ignorespaces When you create a page, by default it is added to the site hierarchy.}}{268}{figure.75.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {75.2}Organizing Pages}{268}{section.75.2}\\protected@file@percent }\n\\newlabel{organizing-pages}{{75.2}{268}{Organizing Pages}{section.75.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {75.3}{\\ignorespaces You can see the order of pages in Site Administration vs.~how they appear on the site.}}{269}{figure.75.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {75.4}{\\ignorespaces \\emph  {About Us} is now the home page, and \\emph  {Welcome} is second in the nav.}}{269}{figure.75.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {75.5}{\\ignorespaces \\emph  {About Us} is now nested under \\emph  {Welcome} and appear when you mouse-over \\emph  {Welcome}.}}{269}{figure.75.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {75.3}Public and Private Pages}{270}{section.75.3}\\protected@file@percent }\n\\newlabel{public-and-private-pages}{{75.3}{270}{Public and Private Pages}{section.75.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {75.4}Page Options}{270}{section.75.4}\\protected@file@percent }\n\\newlabel{page-options}{{75.4}{270}{Page Options}{section.75.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {76}Creating and Managing Navigation Menus}{271}{chapter.76}\\protected@file@percent }\n\\newlabel{creating-and-managing-navigation-menus}{{76}{271}{Creating and Managing Navigation Menus}{chapter.76}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {76.1}Creating a Navigation Menu}{271}{section.76.1}\\protected@file@percent }\n\\newlabel{creating-a-navigation-menu}{{76.1}{271}{Creating a Navigation Menu}{section.76.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {76.1}{\\ignorespaces Menus can have a standard page, a submenu, and a URL link in the submenu.}}{272}{figure.76.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {76.2}Managing Menus}{272}{section.76.2}\\protected@file@percent }\n\\newlabel{managing-menus}{{76.2}{272}{Managing Menus}{section.76.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {76.3}Modifying Menus}{273}{section.76.3}\\protected@file@percent }\n\\newlabel{modifying-menus}{{76.3}{273}{Modifying Menus}{section.76.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {76.2}{\\ignorespaces Menus with a standard page, a submenu, and a URL link in the submenu are created for different reasons.}}{273}{figure.76.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {77}Displaying Navigation Menus}{275}{chapter.77}\\protected@file@percent }\n\\newlabel{displaying-navigation-menus}{{77}{275}{Displaying Navigation Menus}{chapter.77}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {77.1}Navigation Menu Widget}{275}{section.77.1}\\protected@file@percent }\n\\newlabel{navigation-menu-widget}{{77.1}{275}{Navigation Menu Widget}{section.77.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {77.2}Choosing a Navigation Menu}{275}{section.77.2}\\protected@file@percent }\n\\newlabel{choosing-a-navigation-menu}{{77.2}{275}{Choosing a Navigation Menu}{section.77.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {77.1}{\\ignorespaces Configuring the Navigation Menu Widget.}}{276}{figure.77.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {77.3}Display Template}{276}{section.77.3}\\protected@file@percent }\n\\newlabel{display-template}{{77.3}{276}{Display Template}{section.77.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {77.4}Menu Items to Show}{277}{section.77.4}\\protected@file@percent }\n\\newlabel{menu-items-to-show}{{77.4}{277}{Menu Items to Show}{section.77.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {77.2}{\\ignorespaces Navigation menus give you many ways to help users navigate your Site.}}{278}{figure.77.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {78}Building Sites from Templates}{279}{chapter.78}\\protected@file@percent }\n\\newlabel{building-sites-from-templates}{{78}{279}{Building Sites from Templates}{chapter.78}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {79}Creating a Site Template}{281}{chapter.79}\\protected@file@percent }\n\\newlabel{creating-a-site-template}{{79}{281}{Creating a Site Template}{chapter.79}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {79.1}{\\ignorespaces You can see the name of the Site template you're currently editing.}}{282}{figure.79.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {80}Managing Site Templates}{285}{chapter.80}\\protected@file@percent }\n\\newlabel{managing-site-templates}{{80}{285}{Managing Site Templates}{chapter.80}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {80.1}{\\ignorespaces Site templates have several configurable options including the option to allow Site administrators to modify pages associated with the Site template.}}{286}{figure.80.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {81}Propagating Changes from Site Templates to Sites}{287}{chapter.81}\\protected@file@percent }\n\\newlabel{propagating-changes-from-site-templates-to-sites}{{81}{287}{Propagating Changes from Site Templates to Sites}{chapter.81}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {81.1}Site Template Page Behavior}{287}{section.81.1}\\protected@file@percent }\n\\newlabel{site-template-page-behavior}{{81.1}{287}{Site Template Page Behavior}{section.81.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {81.1}{\\ignorespaces You can click the Information icon to view important information about your Site template.}}{288}{figure.81.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {81.2}Merging and Resetting Changes}{288}{section.81.2}\\protected@file@percent }\n\\newlabel{merging-and-resetting-changes}{{81.2}{288}{Merging and Resetting Changes}{section.81.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {81.2}{\\ignorespaces This type of warning is given when there are friendly URL conflicts with Site template pages.}}{289}{figure.81.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {82}Sharing Site Templates}{291}{chapter.82}\\protected@file@percent }\n\\newlabel{sharing-site-templates}{{82}{291}{Sharing Site Templates}{chapter.82}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {83}Configuring Sites}{293}{chapter.83}\\protected@file@percent }\n\\newlabel{configuring-sites}{{83}{293}{Configuring Sites}{chapter.83}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {84}Configuring Site Settings}{295}{chapter.84}\\protected@file@percent }\n\\newlabel{configuring-site-settings}{{84}{295}{Configuring Site Settings}{chapter.84}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {84.1}Details}{295}{section.84.1}\\protected@file@percent }\n\\newlabel{details}{{84.1}{295}{Details}{section.84.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {84.2}Membership Options}{295}{section.84.2}\\protected@file@percent }\n\\newlabel{membership-options}{{84.2}{295}{Membership Options}{section.84.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {84.3}Site Hierarchies}{295}{section.84.3}\\protected@file@percent }\n\\newlabel{site-hierarchies-1}{{84.3}{295}{Site Hierarchies}{section.84.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {84.4}Pages}{296}{section.84.4}\\protected@file@percent }\n\\newlabel{pages}{{84.4}{296}{Pages}{section.84.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {84.1}{\\ignorespaces Selecting a Site Template.}}{296}{figure.84.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {84.5}Categorization}{296}{section.84.5}\\protected@file@percent }\n\\newlabel{categorization}{{84.5}{296}{Categorization}{section.84.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {84.6}Site URL}{296}{section.84.6}\\protected@file@percent }\n\\newlabel{site-url}{{84.6}{296}{Site URL}{section.84.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {84.2}{\\ignorespaces When configuring virtual hosts, the public and private pages of a site can be configured to different domains.}}{297}{figure.84.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {84.7}Documents and Media}{297}{section.84.7}\\protected@file@percent }\n\\newlabel{documents-and-media}{{84.7}{297}{Documents and Media}{section.84.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {84.8}Site Template}{298}{section.84.8}\\protected@file@percent }\n\\newlabel{site-template}{{84.8}{298}{Site Template}{section.84.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {84.9}Asset Auto Tagging}{298}{section.84.9}\\protected@file@percent }\n\\newlabel{asset-auto-tagging}{{84.9}{298}{Asset Auto Tagging}{section.84.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {84.10}Custom Fields}{298}{section.84.10}\\protected@file@percent }\n\\newlabel{custom-fields-1}{{84.10}{298}{Custom Fields}{section.84.10}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {85}Social Settings and Languages}{299}{chapter.85}\\protected@file@percent }\n\\newlabel{social-settings-and-languages}{{85}{299}{Social Settings and Languages}{chapter.85}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {85.1}Ratings}{299}{section.85.1}\\protected@file@percent }\n\\newlabel{ratings}{{85.1}{299}{Ratings}{section.85.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {85.2}Mentions}{299}{section.85.2}\\protected@file@percent }\n\\newlabel{mentions}{{85.2}{299}{Mentions}{section.85.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {85.3}Languages}{299}{section.85.3}\\protected@file@percent }\n\\newlabel{languages}{{85.3}{299}{Languages}{section.85.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {85.1}{\\ignorespaces In the Languages tab, you can configure the site to use the instance's default language or another supported language.}}{300}{figure.85.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {86}Advanced Site Settings}{301}{chapter.86}\\protected@file@percent }\n\\newlabel{advanced-site-settings}{{86}{301}{Advanced Site Settings}{chapter.86}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {86.1}Default User Associations}{301}{section.86.1}\\protected@file@percent }\n\\newlabel{default-user-associations-1}{{86.1}{301}{Default User Associations}{section.86.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {86.2}Analytics}{301}{section.86.2}\\protected@file@percent }\n\\newlabel{analytics-1}{{86.2}{301}{Analytics}{section.86.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {86.1}{\\ignorespaces To set up Google Analytics: sign up, receive an ID, and then enter it into the Google Analytics ID field.}}{302}{figure.86.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {86.3}Maps}{303}{section.86.3}\\protected@file@percent }\n\\newlabel{maps}{{86.3}{303}{Maps}{section.86.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {86.4}Recycle Bin}{303}{section.86.4}\\protected@file@percent }\n\\newlabel{recycle-bin}{{86.4}{303}{Recycle Bin}{section.86.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {86.5}Content Sharing}{303}{section.86.5}\\protected@file@percent }\n\\newlabel{content-sharing}{{86.5}{303}{Content Sharing}{section.86.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {87}Customizing Personal Sites}{305}{chapter.87}\\protected@file@percent }\n\\newlabel{customizing-personal-sites}{{87}{305}{Customizing Personal Sites}{chapter.87}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {88}Importing/Exporting Sites and Content}{307}{chapter.88}\\protected@file@percent }\n\\newlabel{importingexporting-sites-and-content}{{88}{307}{Importing/Exporting Sites and Content}{chapter.88}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {88.1}Backing Up and Restoring Pages and Their Content}{307}{section.88.1}\\protected@file@percent }\n\\newlabel{backing-up-and-restoring-pages-and-their-content}{{88.1}{307}{Backing Up and Restoring Pages and Their Content}{section.88.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {88.2}Page Export Example}{308}{section.88.2}\\protected@file@percent }\n\\newlabel{page-export-example}{{88.2}{308}{Page Export Example}{section.88.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {88.1}{\\ignorespaces You can configure your export options manually by selecting pages, content, and permissions.}}{309}{figure.88.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {88.3}Export Templates}{309}{section.88.3}\\protected@file@percent }\n\\newlabel{export-templates}{{88.3}{309}{Export Templates}{section.88.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {89}Styling Apps and Assets}{311}{chapter.89}\\protected@file@percent }\n\\newlabel{styling-apps-and-assets}{{89}{311}{Styling Apps and Assets}{chapter.89}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {90}Styling Widgets with Widget Templates}{313}{chapter.90}\\protected@file@percent }\n\\newlabel{styling-widgets-with-widget-templates}{{90}{313}{Styling Widgets with Widget Templates}{chapter.90}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {90.1}Creating a Widget Template}{313}{section.90.1}\\protected@file@percent }\n\\newlabel{creating-a-widget-template}{{90.1}{313}{Creating a Widget Template}{section.90.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {90.1}{\\ignorespaces The Site Administration dropdown menu lets you choose the context in which your widget template resides.}}{314}{figure.90.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {90.2}The Template Editor}{314}{section.90.2}\\protected@file@percent }\n\\newlabel{the-template-editor}{{90.2}{314}{The Template Editor}{section.90.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {90.2}{\\ignorespaces Liferay offers a versatile script editor to customize your widget template.}}{315}{figure.90.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {90.3}Configuring Widget Templates}{316}{section.90.3}\\protected@file@percent }\n\\newlabel{configuring-widget-templates}{{90.3}{316}{Configuring Widget Templates}{section.90.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {90.3}{\\ignorespaces In the \\emph  {Configuration} menu of an app, you can edit and manage available widget templates.}}{317}{figure.90.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {91}Widget Template Example}{319}{chapter.91}\\protected@file@percent }\n\\newlabel{widget-template-example}{{91}{319}{Widget Template Example}{chapter.91}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {91.1}{\\ignorespaces After applying the Carousel widget template, your pictures are displayed as a carousel slideshow.}}{320}{figure.91.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {92}Setting a Default Widget Template}{321}{chapter.92}\\protected@file@percent }\n\\newlabel{setting-a-default-widget-template}{{92}{321}{Setting a Default Widget Template}{chapter.92}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {92.1}{\\ignorespaces The widget template configuration in System Settings lets you change the display style.}}{321}{figure.92.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {92.1}Default Widget Template Example}{322}{section.92.1}\\protected@file@percent }\n\\newlabel{default-widget-template-example}{{92.1}{322}{Default Widget Template Example}{section.92.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {92.2}{\\ignorespaces System Settings shows where you can find the Template Key.}}{322}{figure.92.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {92.3}{\\ignorespaces You can see the new default configuration.}}{323}{figure.92.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {93}Customizing Page Options}{325}{chapter.93}\\protected@file@percent }\n\\newlabel{customizing-page-options}{{93}{325}{Customizing Page Options}{chapter.93}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {94}Configuring Page Sets}{327}{chapter.94}\\protected@file@percent }\n\\newlabel{configuring-page-sets}{{94}{327}{Configuring Page Sets}{chapter.94}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {94.1}{\\ignorespaces Selecting the Page Set configuration option.}}{327}{figure.94.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {95}Configuring Page Sets}{329}{chapter.95}\\protected@file@percent }\n\\newlabel{configuring-page-sets-1}{{95}{329}{Configuring Page Sets}{chapter.95}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {95.1}{\\ignorespaces The Look and Feel page set tab.}}{329}{figure.95.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {95.1}Themes}{329}{section.95.1}\\protected@file@percent }\n\\newlabel{themes}{{95.1}{329}{Themes}{section.95.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {95.2}{\\ignorespaces The Look and Feel interface allows you to choose a theme for the current site.}}{330}{figure.95.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {95.3}{\\ignorespaces You can define a specific look and feel for a page.}}{331}{figure.95.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {95.2}Using a Custom Logo for a Site}{331}{section.95.2}\\protected@file@percent }\n\\newlabel{using-a-custom-logo-for-a-site}{{95.2}{331}{Using a Custom Logo for a Site}{section.95.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {96}Advanced Page Set Options}{333}{chapter.96}\\protected@file@percent }\n\\newlabel{advanced-page-set-options}{{96}{333}{Advanced Page Set Options}{chapter.96}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {96.1}Executing JavaScript in Site Pages}{333}{section.96.1}\\protected@file@percent }\n\\newlabel{executing-javascript-in-site-pages}{{96.1}{333}{Executing JavaScript in Site Pages}{section.96.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {96.2}Merge Public Pages}{333}{section.96.2}\\protected@file@percent }\n\\newlabel{merge-public-pages}{{96.2}{333}{Merge Public Pages}{section.96.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {96.3}Rendering Pages for Mobile Devices}{334}{section.96.3}\\protected@file@percent }\n\\newlabel{rendering-pages-for-mobile-devices}{{96.3}{334}{Rendering Pages for Mobile Devices}{section.96.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {96.4}Robots}{334}{section.96.4}\\protected@file@percent }\n\\newlabel{robots}{{96.4}{334}{Robots}{section.96.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {96.5}Notifying Search Engines of Site Pages}{334}{section.96.5}\\protected@file@percent }\n\\newlabel{notifying-search-engines-of-site-pages}{{96.5}{334}{Notifying Search Engines of Site Pages}{section.96.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {97}Configuring Individual Pages}{335}{chapter.97}\\protected@file@percent }\n\\newlabel{configuring-individual-pages}{{97}{335}{Configuring Individual Pages}{chapter.97}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {98}Individual Page Settings}{337}{chapter.98}\\protected@file@percent }\n\\newlabel{individual-page-settings}{{98}{337}{Individual Page Settings}{chapter.98}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.1}General}{337}{section.98.1}\\protected@file@percent }\n\\newlabel{general-2}{{98.1}{337}{General}{section.98.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.2}Name and Friendly URL}{337}{section.98.2}\\protected@file@percent }\n\\newlabel{name-and-friendly-url}{{98.2}{337}{Name and Friendly URL}{section.98.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.3}Page Layout}{338}{section.98.3}\\protected@file@percent }\n\\newlabel{page-layout}{{98.3}{338}{Page Layout}{section.98.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {98.1}{\\ignorespaces Setting a layout template for your page.}}{338}{figure.98.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.4}Categorization and SEO}{338}{section.98.4}\\protected@file@percent }\n\\newlabel{categorization-and-seo}{{98.4}{338}{Categorization and SEO}{section.98.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.5}Categorization}{338}{section.98.5}\\protected@file@percent }\n\\newlabel{categorization-1}{{98.5}{338}{Categorization}{section.98.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.6}SEO}{339}{section.98.6}\\protected@file@percent }\n\\newlabel{seo}{{98.6}{339}{SEO}{section.98.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {98.2}{\\ignorespaces Enter the custom canonical URL that you want to use for the page.}}{339}{figure.98.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.7}Look and Feel}{339}{section.98.7}\\protected@file@percent }\n\\newlabel{look-and-feel-1}{{98.7}{339}{Look and Feel}{section.98.7}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {98.3}{\\ignorespaces You can also configure canonical URLs at the global and instance levels.}}{340}{figure.98.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.8}Advanced Settings}{340}{section.98.8}\\protected@file@percent }\n\\newlabel{advanced-settings}{{98.8}{340}{Advanced Settings}{section.98.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.9}Query String}{341}{section.98.9}\\protected@file@percent }\n\\newlabel{query-string}{{98.9}{341}{Query String}{section.98.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.10}Custom Fields}{341}{section.98.10}\\protected@file@percent }\n\\newlabel{custom-fields-2}{{98.10}{341}{Custom Fields}{section.98.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.11}Embedded Widgets}{341}{section.98.11}\\protected@file@percent }\n\\newlabel{embedded-widgets}{{98.11}{341}{Embedded Widgets}{section.98.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.12}Customization Settings}{341}{section.98.12}\\protected@file@percent }\n\\newlabel{customization-settings}{{98.12}{341}{Customization Settings}{section.98.12}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.13}JavaScript}{341}{section.98.13}\\protected@file@percent }\n\\newlabel{javascript}{{98.13}{341}{JavaScript}{section.98.13}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {98.14}Mobile Device Rules}{342}{section.98.14}\\protected@file@percent }\n\\newlabel{mobile-device-rules-1}{{98.14}{342}{Mobile Device Rules}{section.98.14}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {99}Personalizing Pages}{343}{chapter.99}\\protected@file@percent }\n\\newlabel{personalizing-pages}{{99}{343}{Personalizing Pages}{chapter.99}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {99.1}Enabling Page Customizations}{343}{section.99.1}\\protected@file@percent }\n\\newlabel{enabling-page-customizations}{{99.1}{343}{Enabling Page Customizations}{section.99.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {99.1}{\\ignorespaces To enable page customizations, click on the \\emph  {Configure Page} button next to the page, expand the \\emph  {Customization Settings} area, and click on the \\emph  {Customizable} button.}}{344}{figure.99.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {99.2}{\\ignorespaces Customizable regions are colored green and non-customizable regions are colored red.}}{344}{figure.99.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {99.2}Customization Permissions}{344}{section.99.2}\\protected@file@percent }\n\\newlabel{customization-permissions}{{99.2}{344}{Customization Permissions}{section.99.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {99.3}Customizing Pages}{344}{section.99.3}\\protected@file@percent }\n\\newlabel{customizing-pages}{{99.3}{344}{Customizing Pages}{section.99.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {99.3}{\\ignorespaces Customizable areas are highlighted green when organizing apps on the page.}}{345}{figure.99.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {99.4}Viewing Customized Pages}{345}{section.99.4}\\protected@file@percent }\n\\newlabel{viewing-customized-pages}{{99.4}{345}{Viewing Customized Pages}{section.99.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {99.5}Customization Example}{346}{section.99.5}\\protected@file@percent }\n\\newlabel{customization-example}{{99.5}{346}{Customization Example}{section.99.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {99.4}{\\ignorespaces In this example, the user added the Language app, and changed the display style from icons to a select box.}}{346}{figure.99.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {100}Changing Page Permissions}{347}{chapter.100}\\protected@file@percent }\n\\newlabel{changing-page-permissions}{{100}{347}{Changing Page Permissions}{chapter.100}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {100.1}{\\ignorespaces The Permissions offer a plethora of options for each role.}}{348}{figure.100.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {101}Configuring Widgets}{349}{chapter.101}\\protected@file@percent }\n\\newlabel{configuring-widgets}{{101}{349}{Configuring Widgets}{chapter.101}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {102}Look and Feel Configuration}{351}{chapter.102}\\protected@file@percent }\n\\newlabel{look-and-feel-configuration}{{102}{351}{Look and Feel Configuration}{chapter.102}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {102.1}General Settings}{351}{section.102.1}\\protected@file@percent }\n\\newlabel{general-settings}{{102.1}{351}{General Settings}{section.102.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {102.1}{\\ignorespaces The General tab of the Look and Feel Configuration menu lets you define a custom widget title and select the widget contrast option using decorators.}}{352}{figure.102.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {102.2}Text Styles}{352}{section.102.2}\\protected@file@percent }\n\\newlabel{text-styles}{{102.2}{352}{Text Styles}{section.102.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {102.3}Background Styles}{352}{section.102.3}\\protected@file@percent }\n\\newlabel{background-styles}{{102.3}{352}{Background Styles}{section.102.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {102.4}Border Styles}{352}{section.102.4}\\protected@file@percent }\n\\newlabel{border-styles}{{102.4}{352}{Border Styles}{section.102.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {102.2}{\\ignorespaces The Text Styles tab lets you configure the format of the text that appears in the widget.}}{353}{figure.102.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {102.3}{\\ignorespaces The Background Styles tab lets you specify the widget's background color.}}{353}{figure.102.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {102.4}{\\ignorespaces The Border Styles tab lets you specify a border width, style, and color for each side of the widget.}}{354}{figure.102.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {102.5}Margin and Padding}{354}{section.102.5}\\protected@file@percent }\n\\newlabel{margin-and-padding}{{102.5}{354}{Margin and Padding}{section.102.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {102.5}{\\ignorespaces The Margin and Padding tab allows you to specify margin and padding lengths for the sides of your widget.}}{355}{figure.102.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {102.6}Advanced Styling}{355}{section.102.6}\\protected@file@percent }\n\\newlabel{advanced-styling}{{102.6}{355}{Advanced Styling}{section.102.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {102.6}{\\ignorespaces The Advanced Styling tab displays your widget's Liferay ID and allows you to enter CSS code to customize the look and feel of your widget.}}{356}{figure.102.6}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {103}Exporting/Importing Widget Data}{357}{chapter.103}\\protected@file@percent }\n\\newlabel{exportingimporting-widget-data}{{103}{357}{Exporting/Importing Widget Data}{chapter.103}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {103.1}{\\ignorespaces You can access a widget's administrative \\emph  {Export/Import} feature by selecting its Options menu.}}{358}{figure.103.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {103.2}{\\ignorespaces You can access a widget's \\emph  {Export/Import} feature by selecting its Options menu.}}{358}{figure.103.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {103.1}Exporting Widget Data}{359}{section.103.1}\\protected@file@percent }\n\\newlabel{exporting-widget-data}{{103.1}{359}{Exporting Widget Data}{section.103.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {103.2}Importing Widget Data}{359}{section.103.2}\\protected@file@percent }\n\\newlabel{importing-widget-data}{{103.2}{359}{Importing Widget Data}{section.103.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {103.3}{\\ignorespaces When importing widget data, you can choose a LAR file using the file explorer or drag and drop the file between the dotted lines.}}{360}{figure.103.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {104}Communication Between Portlet Widgets}{361}{chapter.104}\\protected@file@percent }\n\\newlabel{communication-between-portlet-widgets}{{104}{361}{Communication Between Portlet Widgets}{chapter.104}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {104.1}{\\ignorespaces You can configure portlets to communicate with each other using public render parameters.}}{361}{figure.104.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {105}Sharing Widgets with Other Sites}{363}{chapter.105}\\protected@file@percent }\n\\newlabel{sharing-widgets-with-other-sites}{{105}{363}{Sharing Widgets with Other Sites}{chapter.105}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {105.1}Any Web Site}{363}{section.105.1}\\protected@file@percent }\n\\newlabel{any-web-site}{{105.1}{363}{Any Web Site}{section.105.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {105.2}Facebook}{363}{section.105.2}\\protected@file@percent }\n\\newlabel{facebook}{{105.2}{363}{Facebook}{section.105.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {105.1}{\\ignorespaces The Sharing tab in your widget's Configuration menu lets you share your widget in a variety of ways.}}{364}{figure.105.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {105.3}OpenSocial Gadget}{364}{section.105.3}\\protected@file@percent }\n\\newlabel{opensocial-gadget}{{105.3}{364}{OpenSocial Gadget}{section.105.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {105.4}Netvibes}{365}{section.105.4}\\protected@file@percent }\n\\newlabel{netvibes}{{105.4}{365}{Netvibes}{section.105.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {106}Widget Permissions}{367}{chapter.106}\\protected@file@percent }\n\\newlabel{widget-permissions}{{106}{367}{Widget Permissions}{chapter.106}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {106.1}{\\ignorespaces Viewing the permissions configuration for a widget.}}{367}{figure.106.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {107}Widget Scope}{369}{chapter.107}\\protected@file@percent }\n\\newlabel{widget-scope}{{107}{369}{Widget Scope}{chapter.107}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {107.1}{\\ignorespaces You can change the scope of your widget's content by navigating to its Configuration menu.}}{370}{figure.107.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {107.2}{\\ignorespaces Use the drop-down menu under Content \\& Data to determine which scope to manage content for.}}{371}{figure.107.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {108}Configuration Templates}{373}{chapter.108}\\protected@file@percent }\n\\newlabel{configuration-templates}{{108}{373}{Configuration Templates}{chapter.108}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {108.1}{\\ignorespaces Create a configuration template to save your app's configuration settings.}}{373}{figure.108.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {108.1}Summary}{374}{section.108.1}\\protected@file@percent }\n\\newlabel{summary}{{108.1}{374}{Summary}{section.108.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {109}Managing Members in Your Site}{375}{chapter.109}\\protected@file@percent }\n\\newlabel{managing-members-in-your-site}{{109}{375}{Managing Members in Your Site}{chapter.109}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {110}Adding Members to Sites}{377}{chapter.110}\\protected@file@percent }\n\\newlabel{adding-members-to-sites}{{110}{377}{Adding Members to Sites}{chapter.110}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {110.1}Administrating Site Membership}{377}{section.110.1}\\protected@file@percent }\n\\newlabel{administrating-site-membership}{{110.1}{377}{Administrating Site Membership}{section.110.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {110.2}Adding Members to a Site}{377}{section.110.2}\\protected@file@percent }\n\\newlabel{adding-members-to-a-site}{{110.2}{377}{Adding Members to a Site}{section.110.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {110.1}{\\ignorespaces The current members of the Site as displayed on the \\emph  {Site Memberships} page.}}{378}{figure.110.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {110.2}{\\ignorespaces The list of users available to add to the current Site. Note that the current members are visible but cannot be added or removed here.}}{378}{figure.110.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {110.3}Removing User Membership from a Site}{378}{section.110.3}\\protected@file@percent }\n\\newlabel{removing-user-membership-from-a-site}{{110.3}{378}{Removing User Membership from a Site}{section.110.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {110.3}{\\ignorespaces Selecting to remove a user.}}{379}{figure.110.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {110.4}Assigning Site Roles}{379}{section.110.4}\\protected@file@percent }\n\\newlabel{assigning-site-roles}{{110.4}{379}{Assigning Site Roles}{section.110.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {110.4}{\\ignorespaces Assigning Site Roles.}}{380}{figure.110.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {111}Creating Teams to Empower Site Members}{381}{chapter.111}\\protected@file@percent }\n\\newlabel{creating-teams-to-empower-site-members}{{111}{381}{Creating Teams to Empower Site Members}{chapter.111}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {111.1}{\\ignorespaces Creating teams within your site can foster teamwork and collaboration, as team permissions enable team members to access the same resources and perform the same types of tasks.}}{382}{figure.111.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {111.2}{\\ignorespaces The Lunar Resort Message Board Moderators Site Team has unlimited permissions on the Message Boards application.}}{383}{figure.111.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {112}Managing Content}{385}{chapter.112}\\protected@file@percent }\n\\newlabel{managing-content}{{112}{385}{Managing Content}{chapter.112}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {113}Managing Web Content}{387}{chapter.113}\\protected@file@percent }\n\\newlabel{managing-web-content}{{113}{387}{Managing Web Content}{chapter.113}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {114}Publishing Basic Web Content}{389}{chapter.114}\\protected@file@percent }\n\\newlabel{publishing-basic-web-content}{{114}{389}{Publishing Basic Web Content}{chapter.114}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {115}Creating Web Content}{391}{chapter.115}\\protected@file@percent }\n\\newlabel{creating-web-content}{{115}{391}{Creating Web Content}{chapter.115}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {115.1}{\\ignorespaces You can choose where to create content by navigating to the Site Administration menu and selecting your Site and page scope.}}{391}{figure.115.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {115.2}{\\ignorespaces By default, \\emph  {Basic Web Content} is the only article type available. The next tutorial covers how to create new types.}}{392}{figure.115.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {116}Using the Web Content Editor}{393}{chapter.116}\\protected@file@percent }\n\\newlabel{using-the-web-content-editor}{{116}{393}{Using the Web Content Editor}{chapter.116}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {116.1}Basic Editor Functions}{393}{section.116.1}\\protected@file@percent }\n\\newlabel{basic-editor-functions}{{116.1}{393}{Basic Editor Functions}{section.116.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {116.1}{\\ignorespaces You can access the image editor through the item selector window.}}{394}{figure.116.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {116.2}Editing the Article Source}{394}{section.116.2}\\protected@file@percent }\n\\newlabel{editing-the-article-source}{{116.2}{394}{Editing the Article Source}{section.116.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {116.2}{\\ignorespaces You can view how your HTML would render by using the preview pane.}}{395}{figure.116.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {116.3}Web Content Options}{396}{section.116.3}\\protected@file@percent }\n\\newlabel{web-content-options}{{116.3}{396}{Web Content Options}{section.116.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {116.3}{\\ignorespaces New web content can be customized in various ways using the menu located to the right of the editor.}}{396}{figure.116.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {116.4}{\\ignorespaces This blog entry has links to two Related Assets: an article and a message board thread.}}{397}{figure.116.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {117}Publishing Web Content}{399}{chapter.117}\\protected@file@percent }\n\\newlabel{publishing-web-content}{{117}{399}{Publishing Web Content}{chapter.117}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {117.1}{\\ignorespaces Add the Web Content Display app to a page to begin displaying your new web content article.}}{399}{figure.117.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {117.2}{\\ignorespaces Publishing web content is a snap. At a minimum, you only have to select the content you wish to publish. You can also enable lots of optional features to let your users interact with your content.}}{401}{figure.117.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {117.1}Editing Published Content}{401}{section.117.1}\\protected@file@percent }\n\\newlabel{editing-published-content}{{117.1}{401}{Editing Published Content}{section.117.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {117.3}{\\ignorespaces Comparing web content articles is a great feature to use during the Workflow process.}}{402}{figure.117.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {118}Other Content Options}{403}{chapter.118}\\protected@file@percent }\n\\newlabel{other-content-options}{{118}{403}{Other Content Options}{chapter.118}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {118.1}Localizing Content}{403}{section.118.1}\\protected@file@percent }\n\\newlabel{localizing-content}{{118.1}{403}{Localizing Content}{section.118.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {118.1}{\\ignorespaces Adding a translation to an article works like adding the default translation.}}{404}{figure.118.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {118.2}Xuggler for Embedding Video}{404}{section.118.2}\\protected@file@percent }\n\\newlabel{xuggler-for-embedding-video}{{118.2}{404}{Xuggler for Embedding Video}{section.118.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {118.2}{\\ignorespaces If you've installed and enabled Xuggler from the \\emph  {Server Administration} → \\emph  {External Tools} section of the Control Panel, you can add audio and video to your web content!}}{405}{figure.118.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {118.3}XML Format Downloads}{405}{section.118.3}\\protected@file@percent }\n\\newlabel{xml-format-downloads}{{118.3}{405}{XML Format Downloads}{section.118.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {118.4}Subscribing to Content}{405}{section.118.4}\\protected@file@percent }\n\\newlabel{subscribing-to-content}{{118.4}{405}{Subscribing to Content}{section.118.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {118.3}{\\ignorespaces The \\emph  {View Source} button is available from the \\emph  {Options} button.}}{406}{figure.118.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {118.4}{\\ignorespaces Click the Subscribe icon in the web content entity's \\emph  {Options} menu to begin receiving web content notifications.}}{406}{figure.118.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {118.5}Organizing Structure Names}{406}{section.118.5}\\protected@file@percent }\n\\newlabel{organizing-structure-names}{{118.5}{406}{Organizing Structure Names}{section.118.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {118.5}{\\ignorespaces The default ordering for Web Content Structures can yield confusing results.}}{407}{figure.118.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {118.6}{\\ignorespaces Web Content Administration will now display structures in alphabetical order.}}{408}{figure.118.6}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {119}Designing Structured Content}{409}{chapter.119}\\protected@file@percent }\n\\newlabel{designing-structured-content}{{119}{409}{Designing Structured Content}{chapter.119}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {120}Creating Structured Web Content}{411}{chapter.120}\\protected@file@percent }\n\\newlabel{creating-structured-web-content}{{120}{411}{Creating Structured Web Content}{chapter.120}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {121}Editing Structures}{413}{chapter.121}\\protected@file@percent }\n\\newlabel{editing-structures}{{121}{413}{Editing Structures}{chapter.121}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {121.1}Structure Fields}{413}{section.121.1}\\protected@file@percent }\n\\newlabel{structure-fields}{{121.1}{413}{Structure Fields}{section.121.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {121.1}{\\ignorespaces Structures are not pre-installed. You have to make your own.}}{414}{figure.121.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {121.2}{\\ignorespaces The structure editor gives you many options to customize your Web Content.}}{415}{figure.121.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {122}Configuring Structure Fields}{417}{chapter.122}\\protected@file@percent }\n\\newlabel{configuring-structure-fields}{{122}{417}{Configuring Structure Fields}{chapter.122}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {122.1}Structure Default Values}{418}{section.122.1}\\protected@file@percent }\n\\newlabel{structure-default-values}{{122.1}{418}{Structure Default Values}{section.122.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {122.1}{\\ignorespaces You can edit default values via the \\emph  {Actions} button of the Manage Structures interface.}}{418}{figure.122.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {122.2}{\\ignorespaces You can define values for your structure fields and the standard asset metadata fields.}}{419}{figure.122.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {123}Structure Settings}{421}{chapter.123}\\protected@file@percent }\n\\newlabel{structure-settings}{{123}{421}{Structure Settings}{chapter.123}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {123.1}Assigning Permissions}{421}{section.123.1}\\protected@file@percent }\n\\newlabel{assigning-permissions}{{123.1}{421}{Assigning Permissions}{section.123.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {123.2}WebDAV URL}{421}{section.123.2}\\protected@file@percent }\n\\newlabel{webdav-url}{{123.2}{421}{WebDAV URL}{section.123.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {123.1}{\\ignorespaces You're able to assign structure permissions via the \\emph  {Actions} button.}}{422}{figure.123.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {124}Designing Web Content with Templates}{423}{chapter.124}\\protected@file@percent }\n\\newlabel{designing-web-content-with-templates}{{124}{423}{Designing Web Content with Templates}{chapter.124}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {125}Adding Templates with Structures}{425}{chapter.125}\\protected@file@percent }\n\\newlabel{adding-templates-with-structures}{{125}{425}{Adding Templates with Structures}{chapter.125}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {125.1}{\\ignorespaces The Lunar Resort News Article is shaping up!}}{426}{figure.125.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {126}Embedding Widgets in Templates}{427}{chapter.126}\\protected@file@percent }\n\\newlabel{embedding-widgets-in-templates}{{126}{427}{Embedding Widgets in Templates}{chapter.126}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {126.1}{\\ignorespaces You can find the Template Key when view the Edit page for a template..}}{427}{figure.126.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {127}Using Taglibs in Templates}{429}{chapter.127}\\protected@file@percent }\n\\newlabel{using-taglibs-in-templates}{{127}{429}{Using Taglibs in Templates}{chapter.127}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {127.1}{\\ignorespaces You can hover your pointer over a variable for a more detailed description.}}{429}{figure.127.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {128}Assigning Template Permissions}{431}{chapter.128}\\protected@file@percent }\n\\newlabel{assigning-template-permissions}{{128}{431}{Assigning Template Permissions}{chapter.128}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {128.1}Assigning Permissions for Individual Templates}{431}{section.128.1}\\protected@file@percent }\n\\newlabel{assigning-permissions-for-individual-templates}{{128.1}{431}{Assigning Permissions for Individual Templates}{section.128.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {129}Display Page Templates for Web Content}{433}{chapter.129}\\protected@file@percent }\n\\newlabel{display-page-templates-for-web-content}{{129}{433}{Display Page Templates for Web Content}{chapter.129}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {130}Creating Display Page Templates}{435}{chapter.130}\\protected@file@percent }\n\\newlabel{creating-display-page-templates}{{130}{435}{Creating Display Page Templates}{chapter.130}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {130.1}{\\ignorespaces Connecting structure fields to fragment data.}}{436}{figure.130.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {131}Display Page Template Example}{437}{chapter.131}\\protected@file@percent }\n\\newlabel{display-page-template-example}{{131}{437}{Display Page Template Example}{chapter.131}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {131.1}{\\ignorespaces Selecting the Asset type and Subtype.}}{438}{figure.131.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {131.1}Publishing with Display Page Templates}{438}{section.131.1}\\protected@file@percent }\n\\newlabel{publishing-with-display-page-templates}{{131.1}{438}{Publishing with Display Page Templates}{section.131.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {131.2}{\\ignorespaces The Display Page Template creation interface.}}{439}{figure.131.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {131.3}{\\ignorespaces Editing a Display Page Template with some Fragments added.}}{439}{figure.131.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {131.4}{\\ignorespaces Mapping the editable fragments to structure fields.}}{440}{figure.131.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {131.5}{\\ignorespaces Selecting the Asset type and Subtype.}}{441}{figure.131.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {131.6}{\\ignorespaces Selecting the Asset type and Subtype.}}{441}{figure.131.6}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {132}Managing Content Sets}{443}{chapter.132}\\protected@file@percent }\n\\newlabel{managing-content-sets}{{132}{443}{Managing Content Sets}{chapter.132}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {132.1}Creating and Displaying Content Sets}{443}{section.132.1}\\protected@file@percent }\n\\newlabel{creating-and-displaying-content-sets}{{132.1}{443}{Creating and Displaying Content Sets}{section.132.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {132.2}Content Set Personalization}{444}{section.132.2}\\protected@file@percent }\n\\newlabel{content-set-personalization}{{132.2}{444}{Content Set Personalization}{section.132.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {132.3}Converting Asset Publisher Configurations to Content Sets}{444}{section.132.3}\\protected@file@percent }\n\\newlabel{converting-asset-publisher-configurations-to-content-sets}{{132.3}{444}{Converting Asset Publisher Configurations to Content Sets}{section.132.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {133}Creating Content Sets}{445}{chapter.133}\\protected@file@percent }\n\\newlabel{creating-content-sets}{{133}{445}{Creating Content Sets}{chapter.133}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {133.1}Creating a Manual Content Set}{445}{section.133.1}\\protected@file@percent }\n\\newlabel{creating-a-manual-content-set}{{133.1}{445}{Creating a Manual Content Set}{section.133.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {133.1}{\\ignorespaces Content Sets is found in the Content \\& Data section of Site Administration.}}{446}{figure.133.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {133.2}{\\ignorespaces You can select the type of asset to add to the Content Set.}}{446}{figure.133.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {133.2}Creating a Dynamic Content Set}{447}{section.133.2}\\protected@file@percent }\n\\newlabel{creating-a-dynamic-content-set}{{133.2}{447}{Creating a Dynamic Content Set}{section.133.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {133.3}{\\ignorespaces Content Sets use the same filter system as the Asset Publisher.}}{447}{figure.133.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {134}Displaying Content Sets}{449}{chapter.134}\\protected@file@percent }\n\\newlabel{displaying-content-sets}{{134}{449}{Displaying Content Sets}{chapter.134}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {134.1}Configuring the Asset Publisher for Content Sets}{449}{section.134.1}\\protected@file@percent }\n\\newlabel{configuring-the-asset-publisher-for-content-sets}{{134.1}{449}{Configuring the Asset Publisher for Content Sets}{section.134.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {134.1}{\\ignorespaces The Asset Publisher has a number of options available for selecting its source for content.}}{450}{figure.134.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {134.2}{\\ignorespaces Select the Content Set you want to use.}}{450}{figure.134.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {134.3}{\\ignorespaces You can see the results as the standard Asset Publisher output. You can create Widget Templates to add more style and pizzazz here.}}{451}{figure.134.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {134.2}Adding Items to an existing Content Set}{451}{section.134.2}\\protected@file@percent }\n\\newlabel{adding-items-to-an-existing-content-set}{{134.2}{451}{Adding Items to an existing Content Set}{section.134.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {134.4}{\\ignorespaces The result is dynamically added to the Content List wherever it is displayed.}}{452}{figure.134.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {135}Converting Asset Publisher Configurations to Content Sets}{455}{chapter.135}\\protected@file@percent }\n\\newlabel{converting-asset-publisher-configurations-to-content-sets-1}{{135}{455}{Converting Asset Publisher Configurations to Content Sets}{chapter.135}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {135.1}{\\ignorespaces You can generate a Content Set directly from the Asset Publisher configuration.}}{455}{figure.135.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {135.2}{\\ignorespaces The Content Set is added right alongside any existing sets.}}{456}{figure.135.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {136}Organizing Content with Tags and Categories}{457}{chapter.136}\\protected@file@percent }\n\\newlabel{organizing-content-with-tags-and-categories}{{136}{457}{Organizing Content with Tags and Categories}{chapter.136}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {136.1}{\\ignorespaces Here is the Web Content application's metadata section.}}{457}{figure.136.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {137}Tagging Content}{459}{chapter.137}\\protected@file@percent }\n\\newlabel{tagging-content}{{137}{459}{Tagging Content}{chapter.137}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {137.1}{\\ignorespaces The Add Tag interface is very simple, only requiring the name of your tag.}}{460}{figure.137.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {138}Defining Categories for Content}{461}{chapter.138}\\protected@file@percent }\n\\newlabel{defining-categories-for-content}{{138}{461}{Defining Categories for Content}{chapter.138}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {138.1}{\\ignorespaces After adding new vocabularies, you'll notice your vocabularies indicate the amount of categories existing beneath them.}}{462}{figure.138.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {139}Targeted Vocabularies}{465}{chapter.139}\\protected@file@percent }\n\\newlabel{targeted-vocabularies}{{139}{465}{Targeted Vocabularies}{chapter.139}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {139.1}{\\ignorespaces You can target vocabularies by checking the \\emph  {Allow Multiple Categories} selector and then selecting the Asset Types.}}{465}{figure.139.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {139.1}Single and Multi-valued Vocabularies}{466}{section.139.1}\\protected@file@percent }\n\\newlabel{single-and-multi-valued-vocabularies}{{139.1}{466}{Single and Multi-valued Vocabularies}{section.139.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {139.2}{\\ignorespaces Multi-valued vocabularies allow multiple categories from the vocabulary to be applied to an asset. Single-valued vocabularies only allow one category from the vocabulary to be applied. Here, the \\emph  {Dining} and \\emph  {Nightlife} categories are selected to be applied but the \\emph  {Scenic Adventures} category is not.}}{466}{figure.139.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {139.2}Separated Entries}{466}{section.139.2}\\protected@file@percent }\n\\newlabel{separated-entries}{{139.2}{466}{Separated Entries}{section.139.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {139.3}{\\ignorespaces Vocabularies have their own entries, making it easy to select available categories.}}{467}{figure.139.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {140}Geolocating Assets}{469}{chapter.140}\\protected@file@percent }\n\\newlabel{geolocating-assets}{{140}{469}{Geolocating Assets}{chapter.140}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {140.1}Geolocating Web Content}{469}{section.140.1}\\protected@file@percent }\n\\newlabel{geolocating-web-content}{{140.1}{469}{Geolocating Web Content}{section.140.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {140.1}{\\ignorespaces Add a geolocation field to your structure to enable geolocation in your web content.}}{470}{figure.140.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {140.2}Geolocating Data Lists}{470}{section.140.2}\\protected@file@percent }\n\\newlabel{geolocating-data-lists}{{140.2}{470}{Geolocating Data Lists}{section.140.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {140.2}{\\ignorespaces Add the Content and Geolocation snippets to create your web content template quickly.}}{471}{figure.140.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {140.3}Geolocating Documents and Media}{471}{section.140.3}\\protected@file@percent }\n\\newlabel{geolocating-documents-and-media}{{140.3}{471}{Geolocating Documents and Media}{section.140.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {140.3}{\\ignorespaces You can enter your location in the address bar, move the indicator to a location, or share your location with the browser.}}{472}{figure.140.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {140.4}{\\ignorespaces Make sure your browser is configured to share your location.}}{472}{figure.140.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {140.5}{\\ignorespaces The Asset Publisher can display your geolocated assets on a map.}}{473}{figure.140.5}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {141}Publishing Content Dynamically}{475}{chapter.141}\\protected@file@percent }\n\\newlabel{publishing-content-dynamically}{{141}{475}{Publishing Content Dynamically}{chapter.141}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {142}Defining Content Relationships}{477}{chapter.142}\\protected@file@percent }\n\\newlabel{defining-content-relationships}{{142}{477}{Defining Content Relationships}{chapter.142}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {142.1}Related Assets Widget}{477}{section.142.1}\\protected@file@percent }\n\\newlabel{related-assets-widget}{{142.1}{477}{Related Assets Widget}{section.142.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {142.1}{\\ignorespaces Select an asset in the Asset Publisher to see its related assets displayed in the Related Assets application.}}{478}{figure.142.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {142.2}{\\ignorespaces Related Assets applications can be configured to display specific content.}}{479}{figure.142.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {143}Publishing Assets}{481}{chapter.143}\\protected@file@percent }\n\\newlabel{publishing-assets}{{143}{481}{Publishing Assets}{chapter.143}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {144}Querying for Content}{483}{chapter.144}\\protected@file@percent }\n\\newlabel{querying-for-content}{{144}{483}{Querying for Content}{chapter.144}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {145}Selecting Assets}{485}{chapter.145}\\protected@file@percent }\n\\newlabel{selecting-assets}{{145}{485}{Selecting Assets}{chapter.145}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {145.1}Selecting Assets Manually}{485}{section.145.1}\\protected@file@percent }\n\\newlabel{selecting-assets-manually}{{145.1}{485}{Selecting Assets Manually}{section.145.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {145.1}{\\ignorespaces Selecting assets in the Asset Publisher manually is similar to selecting assets in the Web Content Display application except that you can select assets of any type, not just web content. You can also add scopes to expand the list of assets that are available to be displayed in the Asset Publisher.}}{486}{figure.145.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {145.2}Selecting Assets Dynamically}{487}{section.145.2}\\protected@file@percent }\n\\newlabel{selecting-assets-dynamically}{{145.2}{487}{Selecting Assets Dynamically}{section.145.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {145.2}{\\ignorespaces You can filter by tags and categories, and you can set up as many filter rules as you need.}}{487}{figure.145.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {145.3}Selecting a Content Set}{489}{section.145.3}\\protected@file@percent }\n\\newlabel{selecting-a-content-set}{{145.3}{489}{Selecting a Content Set}{section.145.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {146}Configuring Display Settings}{491}{chapter.146}\\protected@file@percent }\n\\newlabel{configuring-display-settings}{{146}{491}{Configuring Display Settings}{chapter.146}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {146.1}{\\ignorespaces You can configure the Asset Publisher to display various kinds of metadata about the displayed assets.}}{493}{figure.146.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {147}Configuring Asset Publisher Subscriptions}{495}{chapter.147}\\protected@file@percent }\n\\newlabel{configuring-asset-publisher-subscriptions}{{147}{495}{Configuring Asset Publisher Subscriptions}{chapter.147}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {147.1}{\\ignorespaces An email subscription notifies users when new assets are published.}}{495}{figure.147.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {148}Publishing RSS Feeds}{497}{chapter.148}\\protected@file@percent }\n\\newlabel{publishing-rss-feeds}{{148}{497}{Publishing RSS Feeds}{chapter.148}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {149}Configuring RSS Feeds}{499}{chapter.149}\\protected@file@percent }\n\\newlabel{configuring-rss-feeds}{{149}{499}{Configuring RSS Feeds}{chapter.149}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {149.1}{\\ignorespaces To create a new RSS feed, you only need to specify a name, target page, and web content structure. Of course, you can also configure other features of the feed such as its permissions, web content constraints, and presentation settings.}}{500}{figure.149.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {150}The RSS Publisher Widget}{503}{chapter.150}\\protected@file@percent }\n\\newlabel{the-rss-publisher-widget}{{150}{503}{The RSS Publisher Widget}{chapter.150}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {150.1}{\\ignorespaces The RSS Publisher widget lets you display RSS feeds of your choosing.}}{503}{figure.150.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {150.1}Using the RSS Publisher Widget}{504}{section.150.1}\\protected@file@percent }\n\\newlabel{using-the-rss-publisher-widget}{{150.1}{504}{Using the RSS Publisher Widget}{section.150.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {150.2}{\\ignorespaces The RSS Publisher widget's configuration lets you customize how the widget displays RSS feeds.}}{505}{figure.150.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {150.3}{\\ignorespaces You can also use the RSS Publisher widget's configuration to specify which feeds to display.}}{506}{figure.150.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {151}Restoring Deleted Assets}{507}{chapter.151}\\protected@file@percent }\n\\newlabel{restoring-deleted-assets}{{151}{507}{Restoring Deleted Assets}{chapter.151}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {152}Configuring the Recycle Bin}{509}{chapter.152}\\protected@file@percent }\n\\newlabel{configuring-the-recycle-bin}{{152}{509}{Configuring the Recycle Bin}{chapter.152}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {152.1}{\\ignorespaces The Recycle Bin offers several configurable options for your site.}}{509}{figure.152.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {153}Using the Recycle Bin}{511}{chapter.153}\\protected@file@percent }\n\\newlabel{using-the-recycle-bin}{{153}{511}{Using the Recycle Bin}{chapter.153}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {153.1}{\\ignorespaces The Recycle Bin provides a seamless administrative experience for deleting and removing content.}}{511}{figure.153.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {153.2}{\\ignorespaces In the Recycle Bin, you have the option of restoring or permanently deleting the content.}}{512}{figure.153.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {153.1}Drag and Drop}{513}{section.153.1}\\protected@file@percent }\n\\newlabel{drag-and-drop}{{153.1}{513}{Drag and Drop}{section.153.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {153.3}{\\ignorespaces A quick and easy way of disposing your items is the drag and drop method.}}{513}{figure.153.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {154}Recycle Bin Intelligence and Support}{515}{chapter.154}\\protected@file@percent }\n\\newlabel{recycle-bin-intelligence-and-support}{{154}{515}{Recycle Bin Intelligence and Support}{chapter.154}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {155}Collaboration}{517}{chapter.155}\\protected@file@percent }\n\\newlabel{collaboration}{{155}{517}{Collaboration}{chapter.155}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {155.1}{\\ignorespaces You can use the Documents and Media Library to manage and use documents in the portal.}}{518}{figure.155.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {155.2}{\\ignorespaces You can also make your blog entries look great.}}{519}{figure.155.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {155.3}{\\ignorespaces The Message Boards app is fantastic for facilitating discussions.}}{520}{figure.155.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {156}Managing Documents and Media}{521}{chapter.156}\\protected@file@percent }\n\\newlabel{managing-documents-and-media}{{156}{521}{Managing Documents and Media}{chapter.156}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {157}Publishing Files}{523}{chapter.157}\\protected@file@percent }\n\\newlabel{publishing-files}{{157}{523}{Publishing Files}{chapter.157}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {157.1}{\\ignorespaces These documents are awesome.}}{524}{figure.157.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {157.2}{\\ignorespaces This slideshow rules.}}{524}{figure.157.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {157.3}{\\ignorespaces Viewing a file's details is fun.}}{525}{figure.157.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {158}Adding Files to a Document Library}{527}{chapter.158}\\protected@file@percent }\n\\newlabel{adding-files-to-a-document-library}{{158}{527}{Adding Files to a Document Library}{chapter.158}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {158.1}Granting File Permissions and Roles}{527}{section.158.1}\\protected@file@percent }\n\\newlabel{granting-file-permissions-and-roles}{{158.1}{527}{Granting File Permissions and Roles}{section.158.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {158.1}{\\ignorespaces It's often helpful to define a role for specific users to access Documents and Media from Site Administration.}}{528}{figure.158.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {158.2}Using the Add Menu}{528}{section.158.2}\\protected@file@percent }\n\\newlabel{using-the-add-menu}{{158.2}{528}{Using the Add Menu}{section.158.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {158.2}{\\ignorespaces The Documents and Media's \\emph  {Home} folder starts empty.}}{529}{figure.158.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {158.3}{\\ignorespaces The Add menu lets you upload and add all kinds of documents to the library.}}{530}{figure.158.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {159}Creating Folders}{531}{chapter.159}\\protected@file@percent }\n\\newlabel{creating-folders}{{159}{531}{Creating Folders}{chapter.159}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {159.1}Adding a Folder}{531}{section.159.1}\\protected@file@percent }\n\\newlabel{adding-a-folder}{{159.1}{531}{Adding a Folder}{section.159.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {159.1}{\\ignorespaces Select your folder's permissions.}}{532}{figure.159.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {159.2}{\\ignorespaces Your new folder appears in the Document Library.}}{532}{figure.159.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {159.2}Document Type Restrictions and Workflow}{532}{section.159.2}\\protected@file@percent }\n\\newlabel{document-type-restrictions-and-workflow}{{159.2}{532}{Document Type Restrictions and Workflow}{section.159.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {159.3}{\\ignorespaces You can set the document type restrictions and workflow to use for a folder's files.}}{533}{figure.159.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {159.3}Setting Folder Permissions}{533}{section.159.3}\\protected@file@percent }\n\\newlabel{setting-folder-permissions}{{159.3}{533}{Setting Folder Permissions}{section.159.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {160}Using the Documents and Media Management Bar}{535}{chapter.160}\\protected@file@percent }\n\\newlabel{using-the-documents-and-media-management-bar}{{160}{535}{Using the Documents and Media Management Bar}{chapter.160}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {160.1}{\\ignorespaces The Management Bar is a great place to hang out if you're managing documents.}}{535}{figure.160.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {160.1}View Types}{535}{section.160.1}\\protected@file@percent }\n\\newlabel{view-types}{{160.1}{535}{View Types}{section.160.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {160.2}{\\ignorespaces The Cards View type shows items in large card-like renderings.}}{536}{figure.160.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {160.2}The Info Panel}{536}{section.160.2}\\protected@file@percent }\n\\newlabel{the-info-panel}{{160.2}{536}{The Info Panel}{section.160.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {160.3}Finding and Arranging Items}{537}{section.160.3}\\protected@file@percent }\n\\newlabel{finding-and-arranging-items}{{160.3}{537}{Finding and Arranging Items}{section.160.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {160.4}Selecting Items}{537}{section.160.4}\\protected@file@percent }\n\\newlabel{selecting-items}{{160.4}{537}{Selecting Items}{section.160.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {160.3}{\\ignorespaces With items selected, the Management Bar changes.}}{537}{figure.160.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {161}Viewing File Previews}{539}{chapter.161}\\protected@file@percent }\n\\newlabel{viewing-file-previews}{{161}{539}{Viewing File Previews}{chapter.161}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {161.1}File Preview Apps}{539}{section.161.1}\\protected@file@percent }\n\\newlabel{file-preview-apps}{{161.1}{539}{File Preview Apps}{section.161.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {161.1}{\\ignorespaces File previews let you view and manage a file.}}{540}{figure.161.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {161.2}Managing Files}{541}{section.161.2}\\protected@file@percent }\n\\newlabel{managing-files}{{161.2}{541}{Managing Files}{section.161.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {161.3}The Info Panel}{542}{section.161.3}\\protected@file@percent }\n\\newlabel{the-info-panel-1}{{161.3}{542}{The Info Panel}{section.161.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {162}Editing Images}{543}{chapter.162}\\protected@file@percent }\n\\newlabel{editing-images}{{162}{543}{Editing Images}{chapter.162}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {162.1}{\\ignorespaces You can access the image editor through the Documents and Media repository.}}{544}{figure.162.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {162.2}{\\ignorespaces You can also access the image editor through the item selector preview window.}}{545}{figure.162.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {162.3}{\\ignorespaces The image editor's UI is clear and to the point, offering only what you need.}}{545}{figure.162.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {162.4}{\\ignorespaces Select from a set of preset image filters.}}{545}{figure.162.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {162.5}{\\ignorespaces The history bar lets you undo, redo, and reset changes.}}{546}{figure.162.5}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {163}Publishing Files}{547}{chapter.163}\\protected@file@percent }\n\\newlabel{publishing-files-1}{{163}{547}{Publishing Files}{chapter.163}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {163.1}Using the Media Gallery}{547}{section.163.1}\\protected@file@percent }\n\\newlabel{using-the-media-gallery}{{163.1}{547}{Using the Media Gallery}{section.163.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {163.1}{\\ignorespaces The Media Gallery renders large thumbnail images of media files.}}{549}{figure.163.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {163.2}{\\ignorespaces The Media Gallery's slideshow provides a nice way to view images.}}{549}{figure.163.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {164}Checking Out and Editing Files}{551}{chapter.164}\\protected@file@percent }\n\\newlabel{checking-out-and-editing-files}{{164}{551}{Checking Out and Editing Files}{chapter.164}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {164.1}{\\ignorespaces The file on the right in this image is checked out.}}{551}{figure.164.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {164.2}{\\ignorespaces The version history actions let you inspect, delete, and reinstate file versions.}}{553}{figure.164.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {165}Sharing Files}{555}{chapter.165}\\protected@file@percent }\n\\newlabel{sharing-files}{{165}{555}{Sharing Files}{chapter.165}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {165.1}Sharing Files in Documents and Media}{556}{section.165.1}\\protected@file@percent }\n\\newlabel{sharing-files-in-documents-and-media}{{165.1}{556}{Sharing Files in Documents and Media}{section.165.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {165.2}Working with Shared Files}{556}{section.165.2}\\protected@file@percent }\n\\newlabel{working-with-shared-files}{{165.2}{556}{Working with Shared Files}{section.165.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {165.1}{\\ignorespaces To share a file, you must fill out the Share dialog as these steps describe.}}{557}{figure.165.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {165.2}{\\ignorespaces The Notifications app contains the notifications that are sent when a user shares a file with you.}}{558}{figure.165.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {165.3}Managing Shared Files}{558}{section.165.3}\\protected@file@percent }\n\\newlabel{managing-shared-files}{{165.3}{558}{Managing Shared Files}{section.165.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {165.3}{\\ignorespaces The Shared Content app lists the files shared with you, and the files you shared.}}{559}{figure.165.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {165.4}{\\ignorespaces Click \\emph  {Manage Collaborators} to open up the list of users you shared the file with.}}{560}{figure.165.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {165.5}{\\ignorespaces The Collaborators dialog lets you unshare a file or change the file permissions for each user.}}{561}{figure.165.5}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {166}Configuring Sharing}{563}{chapter.166}\\protected@file@percent }\n\\newlabel{configuring-sharing}{{166}{563}{Configuring Sharing}{chapter.166}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {166.1}Global Configuration}{563}{section.166.1}\\protected@file@percent }\n\\newlabel{global-configuration}{{166.1}{563}{Global Configuration}{section.166.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {166.2}Instance Configuration}{563}{section.166.2}\\protected@file@percent }\n\\newlabel{instance-configuration}{{166.2}{563}{Instance Configuration}{section.166.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {166.1}{\\ignorespaces Configure sharing globally.}}{564}{figure.166.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {166.2}{\\ignorespaces You can enable or disable sharing for each instance.}}{564}{figure.166.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {166.3}Site Configuration}{564}{section.166.3}\\protected@file@percent }\n\\newlabel{site-configuration}{{166.3}{564}{Site Configuration}{section.166.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {166.3}{\\ignorespaces You can enable or disable sharing for each Site.}}{565}{figure.166.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {167}Desktop Access to Documents and Media}{567}{chapter.167}\\protected@file@percent }\n\\newlabel{desktop-access-to-documents-and-media}{{167}{567}{Desktop Access to Documents and Media}{chapter.167}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {167.1}{\\ignorespaces Select \\emph  {Access from Desktop} to get the folder's WebDAV URL.}}{568}{figure.167.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {168}Linking to Google Drive™}{569}{chapter.168}\\protected@file@percent }\n\\newlabel{linking-to-google-drive}{{168}{569}{Linking to Google Drive™}{chapter.168}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {168.1}Install the App}{569}{section.168.1}\\protected@file@percent }\n\\newlabel{install-the-app}{{168.1}{569}{Install the App}{section.168.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {168.2}Configure Your Google Project}{570}{section.168.2}\\protected@file@percent }\n\\newlabel{configure-your-google-project}{{168.2}{570}{Configure Your Google Project}{section.168.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {168.3}Configure Your Portal}{570}{section.168.3}\\protected@file@percent }\n\\newlabel{configure-your-portal}{{168.3}{570}{Configure Your Portal}{section.168.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {168.4}Creating Linked Files}{571}{section.168.4}\\protected@file@percent }\n\\newlabel{creating-linked-files}{{168.4}{571}{Creating Linked Files}{section.168.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {168.1}{\\ignorespaces Enter your Google project's OAuth 2 client ID, OAuth 2 client secret, and Picker API key.}}{572}{figure.168.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {168.2}{\\ignorespaces Select \\emph  {New Google Drive Shortcut} from the \\emph  {Add} menu in your Document Library.}}{573}{figure.168.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {168.3}{\\ignorespaces You can select files from Google Drive™ or your photos.}}{573}{figure.168.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {169}Metadata Sets}{575}{chapter.169}\\protected@file@percent }\n\\newlabel{metadata-sets}{{169}{575}{Metadata Sets}{chapter.169}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {169.1}Managing Metadata Sets}{575}{section.169.1}\\protected@file@percent }\n\\newlabel{managing-metadata-sets}{{169.1}{575}{Managing Metadata Sets}{section.169.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {169.1}{\\ignorespaces The Metadata Sets management window lets you view existing sets and create new ones for applying to document types.}}{576}{figure.169.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {169.2}Creating Metadata Sets}{576}{section.169.2}\\protected@file@percent }\n\\newlabel{creating-metadata-sets}{{169.2}{576}{Creating Metadata Sets}{section.169.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {169.2}{\\ignorespaces Add your metadata set's fields to the canvas.}}{578}{figure.169.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {169.3}{\\ignorespaces Edit your metadata set's fields to match the metadata that you want each field to hold.}}{579}{figure.169.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {170}Document Types}{581}{chapter.170}\\protected@file@percent }\n\\newlabel{document-types}{{170}{581}{Document Types}{chapter.170}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {170.1}Managing Document Types}{581}{section.170.1}\\protected@file@percent }\n\\newlabel{managing-document-types}{{170.1}{581}{Managing Document Types}{section.170.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {170.2}Creating Document Types}{581}{section.170.2}\\protected@file@percent }\n\\newlabel{creating-document-types}{{170.2}{581}{Creating Document Types}{section.170.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {170.1}{\\ignorespaces The Document Types management window lets you view existing document types and create new ones.}}{582}{figure.170.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {170.2}{\\ignorespaces Create your new document type.}}{583}{figure.170.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {171}Online File Creation and Editing with Google Docs™}{585}{chapter.171}\\protected@file@percent }\n\\newlabel{online-file-creation-and-editing-with-google-docs}{{171}{585}{Online File Creation and Editing with Google Docs™}{chapter.171}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {171.1}{\\ignorespaces You can create new Google documents in Documents and Media.}}{586}{figure.171.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {171.2}{\\ignorespaces You can also use Google's document editor to edit existing Documents and Media files.}}{587}{figure.171.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {171.3}{\\ignorespaces When using Google's document editor, you can save or discard your changes via the editor's toolbar.}}{587}{figure.171.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {172}Configuring Google Docs™ Integration}{589}{chapter.172}\\protected@file@percent }\n\\newlabel{configuring-google-docs-integration}{{172}{589}{Configuring Google Docs™ Integration}{chapter.172}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {172.1}Configure Your Google Project}{589}{section.172.1}\\protected@file@percent }\n\\newlabel{configure-your-google-project-1}{{172.1}{589}{Configure Your Google Project}{section.172.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {172.2}Configuring the Portal}{589}{section.172.2}\\protected@file@percent }\n\\newlabel{configuring-the-portal}{{172.2}{589}{Configuring the Portal}{section.172.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {172.1}{\\ignorespaces Enter your Google project's OAuth 2 client ID and client secret.}}{591}{figure.172.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {173}Creating and Editing Files with Google Docs™}{593}{chapter.173}\\protected@file@percent }\n\\newlabel{creating-and-editing-files-with-google-docs}{{173}{593}{Creating and Editing Files with Google Docs™}{chapter.173}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {173.1}Authentication}{593}{section.173.1}\\protected@file@percent }\n\\newlabel{authentication}{{173.1}{593}{Authentication}{section.173.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {173.2}Creating Files}{593}{section.173.2}\\protected@file@percent }\n\\newlabel{creating-files}{{173.2}{593}{Creating Files}{section.173.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {173.1}{\\ignorespaces You can unlink your Google account from the portal.}}{594}{figure.173.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {173.2}{\\ignorespaces Select the type of Google document you want to create.}}{595}{figure.173.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {173.3}{\\ignorespaces Save or discard your changes by using the toolbar in the editor.}}{595}{figure.173.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {173.3}Editing Files}{595}{section.173.3}\\protected@file@percent }\n\\newlabel{editing-files}{{173.3}{595}{Editing Files}{section.173.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {173.4}Multiple Editing Sessions}{596}{section.173.4}\\protected@file@percent }\n\\newlabel{multiple-editing-sessions}{{173.4}{596}{Multiple Editing Sessions}{section.173.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {173.4}{\\ignorespaces Select \\emph  {Edit in Google Docs} from the file's Actions menu.}}{597}{figure.173.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {174}Integration with Microsoft Office 365™}{599}{chapter.174}\\protected@file@percent }\n\\newlabel{integration-with-microsoft-office-365}{{174}{599}{Integration with Microsoft Office 365™}{chapter.174}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {174.1}{\\ignorespaces You can create new Office 365™ documents in Documents and Media.}}{599}{figure.174.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {174.2}{\\ignorespaces You can also edit existing Documents and Media files in Office 365™.}}{600}{figure.174.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {175}Configuring Office 365™ Integration}{601}{chapter.175}\\protected@file@percent }\n\\newlabel{configuring-office-365-integration}{{175}{601}{Configuring Office 365™ Integration}{chapter.175}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {175.1}Register an Application with the Microsoft Identity Platform}{601}{section.175.1}\\protected@file@percent }\n\\newlabel{register-an-application-with-the-microsoft-identity-platform}{{175.1}{601}{Register an Application with the Microsoft Identity Platform}{section.175.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {175.2}Configuring Liferay DXP}{601}{section.175.2}\\protected@file@percent }\n\\newlabel{configuring-liferay-dxp}{{175.2}{601}{Configuring Liferay DXP}{section.175.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {175.1}{\\ignorespaces Enter your application's client ID, client secret, and tenant.}}{603}{figure.175.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {176}Creating and Editing Documents and Media Files with Office 365™}{605}{chapter.176}\\protected@file@percent }\n\\newlabel{creating-and-editing-documents-and-media-files-with-office-365}{{176}{605}{Creating and Editing Documents and Media Files with Office 365™}{chapter.176}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {176.1}Authentication}{605}{section.176.1}\\protected@file@percent }\n\\newlabel{authentication-1}{{176.1}{605}{Authentication}{section.176.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {176.2}Creating Files}{605}{section.176.2}\\protected@file@percent }\n\\newlabel{creating-files-1}{{176.2}{605}{Creating Files}{section.176.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {176.1}{\\ignorespaces You can unlink your account from the portal.}}{606}{figure.176.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {176.2}{\\ignorespaces Select the type of document you want to create.}}{607}{figure.176.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {176.3}{\\ignorespaces Give the document a name.}}{607}{figure.176.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {176.3}Editing Files}{608}{section.176.3}\\protected@file@percent }\n\\newlabel{editing-files-1}{{176.3}{608}{Editing Files}{section.176.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {176.4}{\\ignorespaces Select \\emph  {Edit in Office 365} from the file's Actions menu.}}{609}{figure.176.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {177}Store Types}{611}{chapter.177}\\protected@file@percent }\n\\newlabel{store-types}{{177}{611}{Store Types}{chapter.177}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {177.1}Simple File System Store}{611}{section.177.1}\\protected@file@percent }\n\\newlabel{simple-file-system-store}{{177.1}{611}{Simple File System Store}{section.177.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {177.1}{\\ignorespaces The Simple File System Store creates a folder structure based on primary keys in Liferay DXP's database.}}{612}{figure.177.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {177.2}Using the Advanced File System Store}{612}{section.177.2}\\protected@file@percent }\n\\newlabel{using-the-advanced-file-system-store}{{177.2}{612}{Using the Advanced File System Store}{section.177.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {177.2}{\\ignorespaces The Advanced File System Store creates a more nested folder structure than the Simple File System Store.}}{613}{figure.177.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {177.3}Using Amazon Simple Storage Service}{613}{section.177.3}\\protected@file@percent }\n\\newlabel{using-amazon-simple-storage-service}{{177.3}{613}{Using Amazon Simple Storage Service}{section.177.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {178}Liferay Sync}{615}{chapter.178}\\protected@file@percent }\n\\newlabel{liferay-sync}{{178}{615}{Liferay Sync}{chapter.178}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {179}Administering Liferay Sync}{617}{chapter.179}\\protected@file@percent }\n\\newlabel{administering-liferay-sync}{{179}{617}{Administering Liferay Sync}{chapter.179}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {180}Installing Liferay Sync's Prerequisites}{619}{chapter.180}\\protected@file@percent }\n\\newlabel{installing-liferay-syncs-prerequisites}{{180}{619}{Installing Liferay Sync's Prerequisites}{chapter.180}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {181}Configuring Liferay Sync}{621}{chapter.181}\\protected@file@percent }\n\\newlabel{configuring-liferay-sync}{{181}{621}{Configuring Liferay Sync}{chapter.181}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {181.1}{\\ignorespaces The Control Panel's Configuration section contains Sync Connector Admin.}}{622}{figure.181.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {181.2}{\\ignorespaces Sync Connector Admin's Sites tab lets you manage Sync on a per-Site basis.}}{623}{figure.181.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {181.3}{\\ignorespaces Sync Connector Admin's Devices tab lists all the devices Sync has registered.}}{624}{figure.181.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {182}Preventing Accidental File Deletion in Liferay Sync}{625}{chapter.182}\\protected@file@percent }\n\\newlabel{preventing-accidental-file-deletion-in-liferay-sync}{{182}{625}{Preventing Accidental File Deletion in Liferay Sync}{chapter.182}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {183}Ensuring Liferay Sync Security}{627}{chapter.183}\\protected@file@percent }\n\\newlabel{ensuring-liferay-sync-security}{{183}{627}{Ensuring Liferay Sync Security}{chapter.183}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {183.1}Liferay Sync Permissions Demonstration}{627}{section.183.1}\\protected@file@percent }\n\\newlabel{liferay-sync-permissions-demonstration}{{183.1}{627}{Liferay Sync Permissions Demonstration}{section.183.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {183.1}{\\ignorespaces The upload error occurs because the user only has permission to view files.}}{628}{figure.183.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {184}Using Liferay Sync on Your Desktop}{629}{chapter.184}\\protected@file@percent }\n\\newlabel{using-liferay-sync-on-your-desktop}{{184}{629}{Using Liferay Sync on Your Desktop}{chapter.184}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {185}Installing and Configuring the Desktop Liferay Sync Client}{631}{chapter.185}\\protected@file@percent }\n\\newlabel{installing-and-configuring-the-desktop-liferay-sync-client}{{185}{631}{Installing and Configuring the Desktop Liferay Sync Client}{chapter.185}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {185.1}Installing the Liferay Sync Desktop Client}{631}{section.185.1}\\protected@file@percent }\n\\newlabel{installing-the-liferay-sync-desktop-client}{{185.1}{631}{Installing the Liferay Sync Desktop Client}{section.185.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {185.1}{\\ignorespaces Drag the Liferay Sync icon to the Applications folder.}}{632}{figure.185.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {185.2}Configuring the Liferay Sync Desktop Client}{632}{section.185.2}\\protected@file@percent }\n\\newlabel{configuring-the-liferay-sync-desktop-client}{{185.2}{632}{Configuring the Liferay Sync Desktop Client}{section.185.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {185.2}{\\ignorespaces The first time you run Liferay Sync, you must tell it how to communicate with your Liferay DXP server.}}{633}{figure.185.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {185.3}{\\ignorespaces When connecting over HTTPS, Liferay Sync produces an error if it can't verify the security certificate. Choosing \\emph  {Proceed Anyway} bypasses verification and leaves the connection open to compromise.}}{633}{figure.185.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {185.4}{\\ignorespaces Select the Sites you want to sync with. Clicking a Site's gear icon opens another window where you can choose to sync with only specific subfolders in that Site.}}{634}{figure.185.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {185.5}{\\ignorespaces Choose the Site's subfolders that you want to sync with. The checkbox with the minus sign indicates that not all of the \\emph  {registration} folder's subfolders are selected.}}{634}{figure.185.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {185.6}{\\ignorespaces Congratulations, you've successfully set up Liferay Sync!}}{636}{figure.185.6}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {186}Using the Liferay Sync Desktop Client}{637}{chapter.186}\\protected@file@percent }\n\\newlabel{using-the-liferay-sync-desktop-client}{{186}{637}{Using the Liferay Sync Desktop Client}{chapter.186}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {186.1}{\\ignorespaces The Liferay Sync menu in the Windows task bar and Mac menu bar gives you quick access to Sync.}}{637}{figure.186.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {186.2}{\\ignorespaces When you sync with more than one Liferay DXP instance, Sync shows submenus for each.}}{638}{figure.186.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {186.1}Using Sync Preferences}{638}{section.186.1}\\protected@file@percent }\n\\newlabel{using-sync-preferences}{{186.1}{638}{Using Sync Preferences}{section.186.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {186.3}{\\ignorespaces The Preferences menu's \\emph  {Accounts} tab lets you manage syncing with Sites per account.}}{639}{figure.186.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {186.4}{\\ignorespaces The Preferences menu's \\emph  {General} tab contains settings for Sync's general behavior.}}{640}{figure.186.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {186.5}{\\ignorespaces The Preferences menu's \\emph  {Network} tab contains settings for Sync's data transfer behavior.}}{641}{figure.186.5}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {187}Using Your Local Liferay Sync Folder}{643}{chapter.187}\\protected@file@percent }\n\\newlabel{using-your-local-liferay-sync-folder}{{187}{643}{Using Your Local Liferay Sync Folder}{chapter.187}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {187.1}{\\ignorespaces Updating a file through Liferay Sync increments the file's version number. You can view a file's version number through the web interface.}}{644}{figure.187.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {188}Using Liferay Sync on Your Mobile Device}{645}{chapter.188}\\protected@file@percent }\n\\newlabel{using-liferay-sync-on-your-mobile-device}{{188}{645}{Using Liferay Sync on Your Mobile Device}{chapter.188}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {189}Connecting Liferay Sync Mobile}{647}{chapter.189}\\protected@file@percent }\n\\newlabel{connecting-liferay-sync-mobile}{{189}{647}{Connecting Liferay Sync Mobile}{chapter.189}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {189.1}{\\ignorespaces This panel lets you access the app's settings, as well as your Sites and documents.}}{647}{figure.189.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {189.2}{\\ignorespaces Tapping the title bar at the top of My Sites or My Documents opens the main Sync panel's compact view.}}{648}{figure.189.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {189.3}{\\ignorespaces The Settings screen for the Sync app lets you sign out of your Liferay DXP instance, enable Security Mode, view the app's version, and send feedback.}}{649}{figure.189.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {190}Managing Files and Folders in Liferay Sync Mobile}{651}{chapter.190}\\protected@file@percent }\n\\newlabel{managing-files-and-folders-in-liferay-sync-mobile}{{190}{651}{Managing Files and Folders in Liferay Sync Mobile}{chapter.190}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {190.1}{\\ignorespaces Sync shows files and folders in a list.}}{652}{figure.190.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {190.2}{\\ignorespaces Downloaded files appear in the list with their size in blue.}}{652}{figure.190.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {190.3}{\\ignorespaces The badge on the file's icon shows the file's version in the Liferay DXP instance. You can also share files that you've downloaded.}}{653}{figure.190.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {191}Adapting Your Media Across Multiple Devices}{655}{chapter.191}\\protected@file@percent }\n\\newlabel{adapting-your-media-across-multiple-devices}{{191}{655}{Adapting Your Media Across Multiple Devices}{chapter.191}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {192}Installing Adaptive Media}{657}{chapter.192}\\protected@file@percent }\n\\newlabel{installing-adaptive-media}{{192}{657}{Installing Adaptive Media}{chapter.192}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {192.1}Adaptive Media's Modules}{657}{section.192.1}\\protected@file@percent }\n\\newlabel{adaptive-medias-modules}{{192.1}{657}{Adaptive Media's Modules}{section.192.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {192.2}Processing Animated GIFs}{658}{section.192.2}\\protected@file@percent }\n\\newlabel{processing-animated-gifs}{{192.2}{658}{Processing Animated GIFs}{section.192.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {193}Adding Image Resolutions}{659}{chapter.193}\\protected@file@percent }\n\\newlabel{adding-image-resolutions}{{193}{659}{Adding Image Resolutions}{chapter.193}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {193.1}{\\ignorespaces Adaptive Media's image resolutions are listed in a table.}}{659}{figure.193.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {193.1}Adding a New Image Resolution}{660}{section.193.1}\\protected@file@percent }\n\\newlabel{adding-a-new-image-resolution}{{193.1}{660}{Adding a New Image Resolution}{section.193.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {193.2}{\\ignorespaces The form for adding a new Adaptive Media resolution.}}{661}{figure.193.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {194}Managing Image Resolutions}{663}{chapter.194}\\protected@file@percent }\n\\newlabel{managing-image-resolutions}{{194}{663}{Managing Image Resolutions}{chapter.194}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {194.1}Disabling Image Resolutions}{663}{section.194.1}\\protected@file@percent }\n\\newlabel{disabling-image-resolutions}{{194.1}{663}{Disabling Image Resolutions}{section.194.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {194.2}Enabling Image Resolutions}{663}{section.194.2}\\protected@file@percent }\n\\newlabel{enabling-image-resolutions}{{194.2}{663}{Enabling Image Resolutions}{section.194.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {194.3}Editing Image Resolutions}{664}{section.194.3}\\protected@file@percent }\n\\newlabel{editing-image-resolutions}{{194.3}{664}{Editing Image Resolutions}{section.194.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {194.4}Deleting Image Resolutions}{664}{section.194.4}\\protected@file@percent }\n\\newlabel{deleting-image-resolutions}{{194.4}{664}{Deleting Image Resolutions}{section.194.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {194.5}Generating Missing Adapted Images}{664}{section.194.5}\\protected@file@percent }\n\\newlabel{generating-missing-adapted-images}{{194.5}{664}{Generating Missing Adapted Images}{section.194.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {194.1}{\\ignorespaces The \\emph  {Adapted Images} column shows the percentage of images that are adapted for each resolution.}}{664}{figure.194.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {194.6}The Recycle Bin and Adapted Images}{665}{section.194.6}\\protected@file@percent }\n\\newlabel{the-recycle-bin-and-adapted-images}{{194.6}{665}{The Recycle Bin and Adapted Images}{section.194.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {195}Creating Content with Adapted Images}{667}{chapter.195}\\protected@file@percent }\n\\newlabel{creating-content-with-adapted-images}{{195}{667}{Creating Content with Adapted Images}{chapter.195}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {195.1}Including Adapted Images in Content}{667}{section.195.1}\\protected@file@percent }\n\\newlabel{including-adapted-images-in-content}{{195.1}{667}{Including Adapted Images in Content}{section.195.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {195.2}Using Adapted Images in Structured Web Content}{668}{section.195.2}\\protected@file@percent }\n\\newlabel{using-adapted-images-in-structured-web-content}{{195.2}{668}{Using Adapted Images in Structured Web Content}{section.195.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {195.3}Staging Adapted Images}{669}{section.195.3}\\protected@file@percent }\n\\newlabel{staging-adapted-images}{{195.3}{669}{Staging Adapted Images}{section.195.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {196}Migrating Documents and Media Thumbnails to Adaptive Media}{671}{chapter.196}\\protected@file@percent }\n\\newlabel{migrating-documents-and-media-thumbnails-to-adaptive-media}{{196}{671}{Migrating Documents and Media Thumbnails to Adaptive Media}{chapter.196}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {196.1}Adding the Replacement Image Resolutions}{671}{section.196.1}\\protected@file@percent }\n\\newlabel{adding-the-replacement-image-resolutions}{{196.1}{671}{Adding the Replacement Image Resolutions}{section.196.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {196.2}Creating the Adaptive Media Images}{672}{section.196.2}\\protected@file@percent }\n\\newlabel{creating-the-adaptive-media-images}{{196.2}{672}{Creating the Adaptive Media Images}{section.196.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {196.3}Running the Migration Process}{672}{section.196.3}\\protected@file@percent }\n\\newlabel{running-the-migration-process}{{196.3}{672}{Running the Migration Process}{section.196.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {197}Advanced Configuration Options}{675}{chapter.197}\\protected@file@percent }\n\\newlabel{advanced-configuration-options}{{197}{675}{Advanced Configuration Options}{chapter.197}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {197.1}{\\ignorespaces You can configure Gifsicle and the maximum image size for Adaptive Media.}}{676}{figure.197.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {197.2}{\\ignorespaces You can also configure Adaptive Media's image processing resources.}}{676}{figure.197.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {198}Collaborating}{677}{chapter.198}\\protected@file@percent }\n\\newlabel{collaborating}{{198}{677}{Collaborating}{chapter.198}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {198.1}{\\ignorespaces This blog entry looks fascinating.}}{678}{figure.198.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {198.2}{\\ignorespaces This is a great thread.}}{679}{figure.198.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {198.3}{\\ignorespaces The Wiki widget displays your wiki on a Site page.}}{680}{figure.198.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {199}Publishing Blogs}{681}{chapter.199}\\protected@file@percent }\n\\newlabel{publishing-blogs}{{199}{681}{Publishing Blogs}{chapter.199}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {199.1}{\\ignorespaces This blog entry looks fascinating.}}{682}{figure.199.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {200}Adding Blog Entries}{683}{chapter.200}\\protected@file@percent }\n\\newlabel{adding-blog-entries}{{200}{683}{Adding Blog Entries}{chapter.200}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {200.1}{\\ignorespaces This screenshot shows some of the blog entry editor's controls.}}{684}{figure.200.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {200.2}{\\ignorespaces When creating a blog entry, the Configuration panel lets you control when and where the blog entry appears, and what to use for the entry's abstract.}}{686}{figure.200.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {200.3}{\\ignorespaces The Blogs app in Site Administration lists the site's blog entries.}}{687}{figure.200.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {201}Using the Blog Entry Editor}{689}{chapter.201}\\protected@file@percent }\n\\newlabel{using-the-blog-entry-editor}{{201}{689}{Using the Blog Entry Editor}{chapter.201}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {201.1}{\\ignorespaces This screenshot shows some of the blog entry editor's controls.}}{689}{figure.201.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {201.1}Using the Editor's Text View}{690}{section.201.1}\\protected@file@percent }\n\\newlabel{using-the-editors-text-view}{{201.1}{690}{Using the Editor's Text View}{section.201.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {201.2}Using the Editor's Code View}{690}{section.201.2}\\protected@file@percent }\n\\newlabel{using-the-editors-code-view}{{201.2}{690}{Using the Editor's Code View}{section.201.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {201.2}{\\ignorespaces Editing in code view lets you work with your blog entry's underlying HTML.}}{691}{figure.201.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {202}Managing Blog Entries}{693}{chapter.202}\\protected@file@percent }\n\\newlabel{managing-blog-entries}{{202}{693}{Managing Blog Entries}{chapter.202}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {202.1}{\\ignorespaces The Blogs app in Site Administration lists the site's blog entries.}}{693}{figure.202.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {202.1}View Types}{694}{section.202.1}\\protected@file@percent }\n\\newlabel{view-types-1}{{202.1}{694}{View Types}{section.202.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {202.2}Finding and Arranging Blog Entries}{694}{section.202.2}\\protected@file@percent }\n\\newlabel{finding-and-arranging-blog-entries}{{202.2}{694}{Finding and Arranging Blog Entries}{section.202.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {202.3}Selecting Blog Entries}{694}{section.202.3}\\protected@file@percent }\n\\newlabel{selecting-blog-entries}{{202.3}{694}{Selecting Blog Entries}{section.202.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {202.2}{\\ignorespaces With multiple blog entries selected, the management bar changes to reflect the actions you can take on the selected entries.}}{695}{figure.202.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {202.4}Sharing Blog Entries}{695}{section.202.4}\\protected@file@percent }\n\\newlabel{sharing-blog-entries}{{202.4}{695}{Sharing Blog Entries}{section.202.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {202.3}{\\ignorespaces You can share a blog entry via its Actions menu.}}{695}{figure.202.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {203}Configuring the Blogs App}{697}{chapter.203}\\protected@file@percent }\n\\newlabel{configuring-the-blogs-app}{{203}{697}{Configuring the Blogs App}{chapter.203}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {203.1}{\\ignorespaces You can configure the options for your site's Blogs app.}}{697}{figure.203.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {204}Displaying Blogs}{699}{chapter.204}\\protected@file@percent }\n\\newlabel{displaying-blogs}{{204}{699}{Displaying Blogs}{chapter.204}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {204.1}{\\ignorespaces Fancy a lunar spelunking trip? This blog entry's abstract lets you know what you're getting into.}}{700}{figure.204.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {204.2}{\\ignorespaces The Card display template makes your blog posts look like fun little trading cards.}}{701}{figure.204.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {205}Aggregating Blogs}{703}{chapter.205}\\protected@file@percent }\n\\newlabel{aggregating-blogs}{{205}{703}{Aggregating Blogs}{chapter.205}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {205.1}{\\ignorespaces The Blogs Aggregator lets you display blog entries authored by multiple authors from different sites.}}{704}{figure.205.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {206}Highlighting Recent Bloggers}{705}{chapter.206}\\protected@file@percent }\n\\newlabel{highlighting-recent-bloggers}{{206}{705}{Highlighting Recent Bloggers}{chapter.206}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {206.1}{\\ignorespaces You can show off your site or organization's most recent bloggers from the Recent Bloggers app.}}{706}{figure.206.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {207}Creating Forums with Message Boards}{707}{chapter.207}\\protected@file@percent }\n\\newlabel{creating-forums-with-message-boards}{{207}{707}{Creating Forums with Message Boards}{chapter.207}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {207.1}{\\ignorespaces The Message Boards widget lets you explore its categories, interact with message threads, and post new messages.}}{708}{figure.207.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {207.2}{\\ignorespaces A thread's view displays author information and thread content, for the thread and all replies to the thread.}}{708}{figure.207.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {208}Creating Message Boards}{709}{chapter.208}\\protected@file@percent }\n\\newlabel{creating-message-boards}{{208}{709}{Creating Message Boards}{chapter.208}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {208.1}Site-scoped Message Boards}{709}{section.208.1}\\protected@file@percent }\n\\newlabel{site-scoped-message-boards}{{208.1}{709}{Site-scoped Message Boards}{section.208.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {208.2}Page-scoped Message Boards}{709}{section.208.2}\\protected@file@percent }\n\\newlabel{page-scoped-message-boards}{{208.2}{709}{Page-scoped Message Boards}{section.208.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {208.1}{\\ignorespaces A Message Board instance starts empty, ready for you to configure for your purposes.}}{710}{figure.208.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {208.3}Globally-scoped Message Boards}{710}{section.208.3}\\protected@file@percent }\n\\newlabel{globally-scoped-message-boards}{{208.3}{710}{Globally-scoped Message Boards}{section.208.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {208.2}{\\ignorespaces Select the page's scope under the \\emph  {Content \\& Data} menu in Site Administration.}}{711}{figure.208.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {208.3}{\\ignorespaces After changing to the global scope, select \\emph  {Message Boards} from the \\emph  {Content \\& Data} menu in Site Administration.}}{712}{figure.208.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {209}Configuring Message Boards}{713}{chapter.209}\\protected@file@percent }\n\\newlabel{configuring-message-boards}{{209}{713}{Configuring Message Boards}{chapter.209}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {209.1}General Setup}{713}{section.209.1}\\protected@file@percent }\n\\newlabel{general-setup}{{209.1}{713}{General Setup}{section.209.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {209.2}Email Setup}{714}{section.209.2}\\protected@file@percent }\n\\newlabel{email-setup}{{209.2}{714}{Email Setup}{section.209.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {209.3}Thread Priorities}{714}{section.209.3}\\protected@file@percent }\n\\newlabel{thread-priorities}{{209.3}{714}{Thread Priorities}{section.209.3}{}}\n\\gdef \\LT@i {\\LT@entry \n    {1}{258.95407pt}\\LT@entry \n    {1}{210.80093pt}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {209.4}User Ranks}{715}{section.209.4}\\protected@file@percent }\n\\newlabel{user-ranks}{{209.4}{715}{User Ranks}{section.209.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {209.5}RSS}{715}{section.209.5}\\protected@file@percent }\n\\newlabel{rss}{{209.5}{715}{RSS}{section.209.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {210}Message Board Permissions}{717}{chapter.210}\\protected@file@percent }\n\\newlabel{message-board-permissions}{{210}{717}{Message Board Permissions}{chapter.210}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {211}Message Board Categories}{719}{chapter.211}\\protected@file@percent }\n\\newlabel{message-board-categories}{{211}{719}{Message Board Categories}{chapter.211}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {211.1}Adding Categories}{719}{section.211.1}\\protected@file@percent }\n\\newlabel{adding-categories}{{211.1}{719}{Adding Categories}{section.211.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {211.1}{\\ignorespaces You have several options to create a message board category for your needs.}}{720}{figure.211.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {211.2}{\\ignorespaces Categories help you organize threads so users can find topical threads that interest them.}}{721}{figure.211.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {211.2}Adding Subcategories}{721}{section.211.2}\\protected@file@percent }\n\\newlabel{adding-subcategories}{{211.2}{721}{Adding Subcategories}{section.211.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {211.3}Moving and Merging Categories}{722}{section.211.3}\\protected@file@percent }\n\\newlabel{moving-and-merging-categories}{{211.3}{722}{Moving and Merging Categories}{section.211.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {211.3}{\\ignorespaces The Move Category form lets you move and merge categories.}}{722}{figure.211.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {212}User Subscriptions and Mailing Lists}{723}{chapter.212}\\protected@file@percent }\n\\newlabel{user-subscriptions-and-mailing-lists}{{212}{723}{User Subscriptions and Mailing Lists}{chapter.212}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {212.1}User Subscriptions}{723}{section.212.1}\\protected@file@percent }\n\\newlabel{user-subscriptions}{{212.1}{723}{User Subscriptions}{section.212.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {212.2}Mailing Lists}{724}{section.212.2}\\protected@file@percent }\n\\newlabel{mailing-lists}{{212.2}{724}{Mailing Lists}{section.212.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {213}Using the Message Boards}{727}{chapter.213}\\protected@file@percent }\n\\newlabel{using-the-message-boards}{{213}{727}{Using the Message Boards}{chapter.213}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {213.1}{\\ignorespaces The Message Boards widget lets you explore its categories, interact with message threads, and post new messages.}}{727}{figure.213.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {213.1}Posting New Threads}{728}{section.213.1}\\protected@file@percent }\n\\newlabel{posting-new-threads}{{213.1}{728}{Posting New Threads}{section.213.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {213.2}{\\ignorespaces The Add Message form lets you create a new thread.}}{728}{figure.213.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {213.2}Participating in Message Board Threads}{729}{section.213.2}\\protected@file@percent }\n\\newlabel{participating-in-message-board-threads}{{213.2}{729}{Participating in Message Board Threads}{section.213.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {213.3}{\\ignorespaces A thread's view displays author information and thread content, for the thread and all replies to the thread.}}{730}{figure.213.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {214}Managing Message Boards}{731}{chapter.214}\\protected@file@percent }\n\\newlabel{managing-message-boards}{{214}{731}{Managing Message Boards}{chapter.214}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {214.1}Locking Threads}{731}{section.214.1}\\protected@file@percent }\n\\newlabel{locking-threads}{{214.1}{731}{Locking Threads}{section.214.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {214.1}{\\ignorespaces Define the permissions you want to use for the message boards administrators.}}{732}{figure.214.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {214.2}Moving Threads}{733}{section.214.2}\\protected@file@percent }\n\\newlabel{moving-threads}{{214.2}{733}{Moving Threads}{section.214.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {214.3}Deleting Threads}{733}{section.214.3}\\protected@file@percent }\n\\newlabel{deleting-threads}{{214.3}{733}{Deleting Threads}{section.214.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {214.4}Banning Users}{733}{section.214.4}\\protected@file@percent }\n\\newlabel{banning-users}{{214.4}{733}{Banning Users}{section.214.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {214.5}Splitting Threads}{733}{section.214.5}\\protected@file@percent }\n\\newlabel{splitting-threads}{{214.5}{733}{Splitting Threads}{section.214.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {214.6}Editing Posts}{734}{section.214.6}\\protected@file@percent }\n\\newlabel{editing-posts}{{214.6}{734}{Editing Posts}{section.214.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {214.7}Post Permissions}{734}{section.214.7}\\protected@file@percent }\n\\newlabel{post-permissions}{{214.7}{734}{Post Permissions}{section.214.7}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {215}Mentioning Users}{735}{chapter.215}\\protected@file@percent }\n\\newlabel{mentioning-users}{{215}{735}{Mentioning Users}{chapter.215}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {215.1}{\\ignorespaces As you enter a user name after the \\texttt  {@} character, links to users that match the text you enter are displayed. Select the user you want to mention and publish your content.}}{735}{figure.215.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {215.2}{\\ignorespaces Your notifications are accessible from your user menu and appear in a list.}}{736}{figure.215.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {215.3}{\\ignorespaces You can enable or disable the Mentions feature for all of the Virtual Instance's sites.}}{737}{figure.215.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {215.4}{\\ignorespaces Mentions can also be enabled or disabled per site.}}{737}{figure.215.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {216}Working Together with the Wiki}{739}{chapter.216}\\protected@file@percent }\n\\newlabel{working-together-with-the-wiki}{{216}{739}{Working Together with the Wiki}{chapter.216}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {216.1}{\\ignorespaces The Wiki widget displays your wiki on a Site page.}}{740}{figure.216.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {217}Getting Started with Wikis}{741}{chapter.217}\\protected@file@percent }\n\\newlabel{getting-started-with-wikis}{{217}{741}{Getting Started with Wikis}{chapter.217}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {217.1}{\\ignorespaces The Wiki app instance has a wiki node named \\emph  {Main} with a single front page. You can build on the Main node or click the Add icon to create a new node.}}{741}{figure.217.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {217.1}Configuring Wikis}{741}{section.217.1}\\protected@file@percent }\n\\newlabel{configuring-wikis}{{217.1}{741}{Configuring Wikis}{section.217.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {217.2}Adding Wikis}{742}{section.217.2}\\protected@file@percent }\n\\newlabel{adding-wikis}{{217.2}{742}{Adding Wikis}{section.217.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {217.3}Wiki Node Options}{742}{section.217.3}\\protected@file@percent }\n\\newlabel{wiki-node-options}{{217.3}{742}{Wiki Node Options}{section.217.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {217.2}{\\ignorespaces The New Wiki Node form lets you describe your new node, set view permissions, and set permissions for the Guest and Site Member roles.}}{743}{figure.217.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {217.3}{\\ignorespaces Each wiki node's Actions menu lists actions you can perform.}}{743}{figure.217.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {218}Adding and Editing Wiki Pages}{745}{chapter.218}\\protected@file@percent }\n\\newlabel{adding-and-editing-wiki-pages}{{218}{745}{Adding and Editing Wiki Pages}{chapter.218}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {218.1}{\\ignorespaces Each empty wiki page presents a default message link you can click to edit the page.}}{745}{figure.218.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {218.2}{\\ignorespaces The wiki page editing form lets you create and edit your page's content.}}{746}{figure.218.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {218.3}{\\ignorespaces The wiki node's view in site administration has features that help you access and learn information about a wiki node's pages.}}{747}{figure.218.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {219}Using the Wiki on Site Pages}{749}{chapter.219}\\protected@file@percent }\n\\newlabel{using-the-wiki-on-site-pages}{{219}{749}{Using the Wiki on Site Pages}{chapter.219}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {219.1}{\\ignorespaces Users can interact with your Wiki nodes when you add the Wiki widget to a page.}}{750}{figure.219.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {219.2}{\\ignorespaces Here the user has selected to create a new Wiki instance scoped to the current page named \\emph  {Welcome}}}{751}{figure.219.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {220}Wiki Page Details}{753}{chapter.220}\\protected@file@percent }\n\\newlabel{wiki-page-details}{{220}{753}{Wiki Page Details}{chapter.220}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {220.1}{\\ignorespaces Click \\emph  {Details} to view the wiki page's details.}}{753}{figure.220.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {220.2}{\\ignorespaces The wiki page's details.}}{753}{figure.220.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {220.1}Details}{754}{section.220.1}\\protected@file@percent }\n\\newlabel{details-1}{{220.1}{754}{Details}{section.220.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {220.2}History}{754}{section.220.2}\\protected@file@percent }\n\\newlabel{history}{{220.2}{754}{History}{section.220.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {220.3}{\\ignorespaces The Activities tab displays the actions taken on the wiki page.}}{754}{figure.220.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {220.3}Incoming/Outgoing Links}{755}{section.220.3}\\protected@file@percent }\n\\newlabel{incomingoutgoing-links}{{220.3}{755}{Incoming/Outgoing Links}{section.220.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {220.4}Attachments}{755}{section.220.4}\\protected@file@percent }\n\\newlabel{attachments}{{220.4}{755}{Attachments}{section.220.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {221}Other Wiki Widgets}{757}{chapter.221}\\protected@file@percent }\n\\newlabel{other-wiki-widgets}{{221}{757}{Other Wiki Widgets}{chapter.221}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {221.1}Page Menu}{757}{section.221.1}\\protected@file@percent }\n\\newlabel{page-menu}{{221.1}{757}{Page Menu}{section.221.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {221.1}{\\ignorespaces The Page Menu widget displays a wiki page's outgoing links.}}{758}{figure.221.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {221.2}Tree Menu}{758}{section.221.2}\\protected@file@percent }\n\\newlabel{tree-menu}{{221.2}{758}{Tree Menu}{section.221.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {221.2}{\\ignorespaces The Tree Menu widget displays a wiki node's hierarchy to the configured depth.}}{758}{figure.221.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {221.3}Wiki Display}{759}{section.221.3}\\protected@file@percent }\n\\newlabel{wiki-display}{{221.3}{759}{Wiki Display}{section.221.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {222}Sending Alerts and Announcements}{761}{chapter.222}\\protected@file@percent }\n\\newlabel{sending-alerts-and-announcements}{{222}{761}{Sending Alerts and Announcements}{chapter.222}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {222.1}Creating Alerts and Announcements}{761}{section.222.1}\\protected@file@percent }\n\\newlabel{creating-alerts-and-announcements}{{222.1}{761}{Creating Alerts and Announcements}{section.222.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {222.1}{\\ignorespaces The Alerts widget provides administrators with an easy way to communicate important information to appropriate groups of users.}}{762}{figure.222.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {222.2}{\\ignorespaces Enter your alert or announcement's title and content.}}{763}{figure.222.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {222.2}User Configuration}{763}{section.222.2}\\protected@file@percent }\n\\newlabel{user-configuration}{{222.2}{763}{User Configuration}{section.222.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {222.3}{\\ignorespaces Configure your new alert or announcement.}}{764}{figure.222.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {222.4}{\\ignorespaces Each user can choose how they receive alerts and announcements.}}{765}{figure.222.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {222.3}Alert and Announcement Roles}{765}{section.222.3}\\protected@file@percent }\n\\newlabel{alert-and-announcement-roles}{{222.3}{765}{Alert and Announcement Roles}{section.222.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {223}Managing Notifications and Requests}{767}{chapter.223}\\protected@file@percent }\n\\newlabel{managing-notifications-and-requests}{{223}{767}{Managing Notifications and Requests}{chapter.223}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {223.1}{\\ignorespaces The \\emph  {Notifications List} section displays all your notifications in a paginated list.}}{767}{figure.223.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {223.1}Managing Notifications}{767}{section.223.1}\\protected@file@percent }\n\\newlabel{managing-notifications}{{223.1}{767}{Managing Notifications}{section.223.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {223.2}Managing Multiple Notifications}{768}{section.223.2}\\protected@file@percent }\n\\newlabel{managing-multiple-notifications}{{223.2}{768}{Managing Multiple Notifications}{section.223.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {223.3}Managing Requests}{768}{section.223.3}\\protected@file@percent }\n\\newlabel{managing-requests}{{223.3}{768}{Managing Requests}{section.223.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {224}Using the Knowledge Base}{769}{chapter.224}\\protected@file@percent }\n\\newlabel{using-the-knowledge-base}{{224}{769}{Using the Knowledge Base}{chapter.224}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {224.1}{\\ignorespaces Knowledge Base Display's navigation and viewing provide a great reading experience.}}{770}{figure.224.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {225}Creating Knowledge Base Articles}{771}{chapter.225}\\protected@file@percent }\n\\newlabel{creating-knowledge-base-articles}{{225}{771}{Creating Knowledge Base Articles}{chapter.225}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {225.1}Authoring Articles in the Editor}{771}{section.225.1}\\protected@file@percent }\n\\newlabel{authoring-articles-in-the-editor}{{225.1}{771}{Authoring Articles in the Editor}{section.225.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {225.1}{\\ignorespaces The Knowledge Base app in Site Administration lets you create Knowledge Base articles.}}{772}{figure.225.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {225.2}{\\ignorespaces You can create and modify a Knowledge Base article's content using the WYSIWYG editor.}}{773}{figure.225.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {225.2}Importing Knowledge Base Articles}{773}{section.225.2}\\protected@file@percent }\n\\newlabel{importing-knowledge-base-articles}{{225.2}{773}{Importing Knowledge Base Articles}{section.225.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {225.3}{\\ignorespaces You can import ZIP files that contain Knowledge Base articles in Markdown format.}}{774}{figure.225.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {226}Managing the Knowledge Base}{775}{chapter.226}\\protected@file@percent }\n\\newlabel{managing-the-knowledge-base}{{226}{775}{Managing the Knowledge Base}{chapter.226}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {226.1}Setting the Knowledge Base's Options}{775}{section.226.1}\\protected@file@percent }\n\\newlabel{setting-the-knowledge-bases-options}{{226.1}{775}{Setting the Knowledge Base's Options}{section.226.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {226.1}{\\ignorespaces You can manage Knowledge Base articles, folders, and suggestions.}}{776}{figure.226.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {226.2}{\\ignorespaces The Knowledge Base App's options.}}{776}{figure.226.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {226.2}Managing Knowledge Base Articles}{777}{section.226.2}\\protected@file@percent }\n\\newlabel{managing-knowledge-base-articles}{{226.2}{777}{Managing Knowledge Base Articles}{section.226.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {226.3}{\\ignorespaces This screenshot uses a red box to highlight the text that indicates the current position in the folder hierarchy.}}{777}{figure.226.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {227}Knowledge Base Templates}{779}{chapter.227}\\protected@file@percent }\n\\newlabel{knowledge-base-templates}{{227}{779}{Knowledge Base Templates}{chapter.227}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {227.1}{\\ignorespaces The Knowledge Base app's Templates tab.}}{779}{figure.227.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {227.1}Creating Templates}{780}{section.227.1}\\protected@file@percent }\n\\newlabel{creating-templates}{{227.1}{780}{Creating Templates}{section.227.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {227.2}{\\ignorespaces The New Template form.}}{780}{figure.227.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {227.2}Managing Templates}{780}{section.227.2}\\protected@file@percent }\n\\newlabel{managing-templates}{{227.2}{780}{Managing Templates}{section.227.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {228}Responding to Knowledge Base Feedback}{781}{chapter.228}\\protected@file@percent }\n\\newlabel{responding-to-knowledge-base-feedback}{{228}{781}{Responding to Knowledge Base Feedback}{chapter.228}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {228.1}{\\ignorespaces The Suggestions tab in Knowledge Base displays each piece of feedback that users leave on Knowledge Base articles.}}{781}{figure.228.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {229}Knowledge Base Display}{783}{chapter.229}\\protected@file@percent }\n\\newlabel{knowledge-base-display}{{229}{783}{Knowledge Base Display}{chapter.229}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {229.1}{\\ignorespaces Select the article or folder of articles that the Knowledge Base Display widget displays.}}{784}{figure.229.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {229.2}{\\ignorespaces Knowledge Base Display's navigation and viewing provide a great reading experience.}}{785}{figure.229.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {229.1}Displaying Different Article Sets}{785}{section.229.1}\\protected@file@percent }\n\\newlabel{displaying-different-article-sets}{{229.1}{785}{Displaying Different Article Sets}{section.229.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {229.3}{\\ignorespaces Knowledge Base Display's content folder feature lets users switch between different sets of articles.}}{786}{figure.229.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {230}Other Knowledge Base Widgets}{787}{chapter.230}\\protected@file@percent }\n\\newlabel{other-knowledge-base-widgets}{{230}{787}{Other Knowledge Base Widgets}{chapter.230}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {230.1}Knowledge Base Article}{787}{section.230.1}\\protected@file@percent }\n\\newlabel{knowledge-base-article}{{230.1}{787}{Knowledge Base Article}{section.230.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {230.1}{\\ignorespaces The Knowledge Base Article app is great at displaying individual articles.}}{788}{figure.230.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {230.2}Knowledge Base Section}{788}{section.230.2}\\protected@file@percent }\n\\newlabel{knowledge-base-section}{{230.2}{788}{Knowledge Base Section}{section.230.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {230.2}{\\ignorespaces The Knowledge Base Section widget.}}{788}{figure.230.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {230.3}Knowledge Base Search}{789}{section.230.3}\\protected@file@percent }\n\\newlabel{knowledge-base-search}{{230.3}{789}{Knowledge Base Search}{section.230.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {230.3}{\\ignorespaces The Knowledge Base Search widget lets you search the Knowledge Base for keywords.}}{790}{figure.230.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {231}Importing Knowledge Base Articles}{791}{chapter.231}\\protected@file@percent }\n\\newlabel{importing-knowledge-base-articles-1}{{231}{791}{Importing Knowledge Base Articles}{chapter.231}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {232}Knowledge Base ZIP File Requirements}{793}{chapter.232}\\protected@file@percent }\n\\newlabel{knowledge-base-zip-file-requirements}{{232}{793}{Knowledge Base ZIP File Requirements}{chapter.232}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {232.1}{\\ignorespaces Selecting \\emph  {Add} → \\emph  {Import} in Knowledge Base brings up the interface for selecting a ZIP file of Markdown source files and images to produce and update articles in your Knowledge Base.}}{796}{figure.232.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {233}Knowledge Base Importer FAQs}{797}{chapter.233}\\protected@file@percent }\n\\newlabel{knowledge-base-importer-faqs}{{233}{797}{Knowledge Base Importer FAQs}{chapter.233}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {234}Knowledge Base System Settings}{799}{chapter.234}\\protected@file@percent }\n\\newlabel{knowledge-base-system-settings}{{234}{799}{Knowledge Base System Settings}{chapter.234}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {234.1}Source URL Settings}{799}{section.234.1}\\protected@file@percent }\n\\newlabel{source-url-settings}{{234.1}{799}{Source URL Settings}{section.234.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {234.2}Importer File Convention Settings}{800}{section.234.2}\\protected@file@percent }\n\\newlabel{importer-file-convention-settings}{{234.2}{800}{Importer File Convention Settings}{section.234.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {234.3}Section Names Setting}{801}{section.234.3}\\protected@file@percent }\n\\newlabel{section-names-setting}{{234.3}{801}{Section Names Setting}{section.234.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {234.1}{\\ignorespaces Create the sections you want to use with the Knowledge Base Section widget.}}{801}{figure.234.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {235}Inviting Members to Your Site}{803}{chapter.235}\\protected@file@percent }\n\\newlabel{inviting-members-to-your-site}{{235}{803}{Inviting Members to Your Site}{chapter.235}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {235.1}{\\ignorespaces You can invite users by clicking the add sign next to the user's name.}}{804}{figure.235.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {235.2}{\\ignorespaces You can confirm or ignore the invitation.}}{804}{figure.235.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {236}Assets}{805}{chapter.236}\\protected@file@percent }\n\\newlabel{assets}{{236}{805}{Assets}{chapter.236}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {236.1}{\\ignorespaces The tags \\emph  {freight car} and \\emph  {electric locomotive} were automatically applied to this image.}}{806}{figure.236.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {237}Configuring Asset Auto Tagging}{807}{chapter.237}\\protected@file@percent }\n\\newlabel{configuring-asset-auto-tagging}{{237}{807}{Configuring Asset Auto Tagging}{chapter.237}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {237.1}Configuration Levels}{807}{section.237.1}\\protected@file@percent }\n\\newlabel{configuration-levels}{{237.1}{807}{Configuration Levels}{section.237.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {237.2}Global Configuration}{808}{section.237.2}\\protected@file@percent }\n\\newlabel{global-configuration-1}{{237.2}{808}{Global Configuration}{section.237.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {237.1}{\\ignorespaces You can configure auto tagging globally in the Assets section of System Settings.}}{808}{figure.237.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {237.3}Instance-level Configuration}{808}{section.237.3}\\protected@file@percent }\n\\newlabel{instance-level-configuration}{{237.3}{808}{Instance-level Configuration}{section.237.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {237.2}{\\ignorespaces You can also configure auto tagging for each instance.}}{809}{figure.237.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {237.4}Site-level Configuration}{809}{section.237.4}\\protected@file@percent }\n\\newlabel{site-level-configuration}{{237.4}{809}{Site-level Configuration}{section.237.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {237.3}{\\ignorespaces You can enable or disable auto-tagging for a site.}}{810}{figure.237.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {238}Auto Tagging Images}{811}{chapter.238}\\protected@file@percent }\n\\newlabel{auto-tagging-images}{{238}{811}{Auto Tagging Images}{chapter.238}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {238.1}Configuring TensorFlow Image Auto Tagging}{811}{section.238.1}\\protected@file@percent }\n\\newlabel{configuring-tensorflow-image-auto-tagging}{{238.1}{811}{Configuring TensorFlow Image Auto Tagging}{section.238.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {238.1}{\\ignorespaces The tags \\emph  {freight car} and \\emph  {electric locomotive} were automatically applied to this image.}}{812}{figure.238.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {238.2}{\\ignorespaces Configure TensorFlow image auto-tagging for your portal instances.}}{813}{figure.238.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {238.2}Configuring Google Cloud Vision}{813}{section.238.2}\\protected@file@percent }\n\\newlabel{configuring-google-cloud-vision}{{238.2}{813}{Configuring Google Cloud Vision}{section.238.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {238.3}{\\ignorespaces You can fine tune the process that runs the TensorFlow image auto tagging in the portal.}}{814}{figure.238.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {238.3}Configuring Microsoft Cognitive Services}{814}{section.238.3}\\protected@file@percent }\n\\newlabel{configuring-microsoft-cognitive-services}{{238.3}{814}{Configuring Microsoft Cognitive Services}{section.238.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {238.4}{\\ignorespaces The Google Cloud Vision provider requires an API key.}}{815}{figure.238.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {238.5}{\\ignorespaces The Microsoft Cognitive Services provider requires an API key and an endpoint.}}{816}{figure.238.5}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {239}Auto Tagging Text}{817}{chapter.239}\\protected@file@percent }\n\\newlabel{auto-tagging-text}{{239}{817}{Auto Tagging Text}{chapter.239}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {239.1}Configuring Google Cloud Natural Language Text Auto Tagging}{818}{section.239.1}\\protected@file@percent }\n\\newlabel{configuring-google-cloud-natural-language-text-auto-tagging}{{239.1}{818}{Configuring Google Cloud Natural Language Text Auto Tagging}{section.239.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {239.2}Configuring OpenNLP Text Auto Tagging}{818}{section.239.2}\\protected@file@percent }\n\\newlabel{configuring-opennlp-text-auto-tagging}{{239.2}{818}{Configuring OpenNLP Text Auto Tagging}{section.239.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {239.1}{\\ignorespaces Configure Google Cloud Natural Language text auto tagging for your portal instances.}}{819}{figure.239.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {239.2}{\\ignorespaces Configure OpenNLP text auto tagging for your portal instances.}}{820}{figure.239.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {240}Creating A Social Network}{821}{chapter.240}\\protected@file@percent }\n\\newlabel{creating-a-social-network}{{240}{821}{Creating A Social Network}{chapter.240}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {240.1}{\\ignorespaces The Activities widget shows information about asset-related user activity in the current Site.}}{821}{figure.240.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {240.2}{\\ignorespaces Users get a notification that lets them respond to connection requests.}}{822}{figure.240.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {240.3}{\\ignorespaces The Contacts Center widget lets users make connections.}}{822}{figure.240.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {241}Using the Activities Widget}{823}{chapter.241}\\protected@file@percent }\n\\newlabel{using-the-activities-widget}{{241}{823}{Using the Activities Widget}{chapter.241}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {241.1}{\\ignorespaces The Activities widget shows information about asset-related user activity in the current Site.}}{823}{figure.241.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {242}Connecting Users}{825}{chapter.242}\\protected@file@percent }\n\\newlabel{connecting-users}{{242}{825}{Connecting Users}{chapter.242}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {242.1}{\\ignorespaces The Contacts Center widget lets users make connections.}}{825}{figure.242.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {242.2}{\\ignorespaces Users get a notification that lets them respond to connection requests.}}{826}{figure.242.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {243}Exporting Widgets To Other Websites}{827}{chapter.243}\\protected@file@percent }\n\\newlabel{exporting-widgets-to-other-websites}{{243}{827}{Exporting Widgets To Other Websites}{chapter.243}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {243.1}Sharing OpenSocial Gadgets}{827}{section.243.1}\\protected@file@percent }\n\\newlabel{sharing-opensocial-gadgets}{{243.1}{827}{Sharing OpenSocial Gadgets}{section.243.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {243.1}{\\ignorespaces You can share widgets via OpenSocial.}}{828}{figure.243.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {244}Integrating with Facebook}{829}{chapter.244}\\protected@file@percent }\n\\newlabel{integrating-with-facebook}{{244}{829}{Integrating with Facebook}{chapter.244}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {244.1}Facebook Sign On}{829}{section.244.1}\\protected@file@percent }\n\\newlabel{facebook-sign-on}{{244.1}{829}{Facebook Sign On}{section.244.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {244.2}Using Your Widget as Facebook Applications}{829}{section.244.2}\\protected@file@percent }\n\\newlabel{using-your-widget-as-facebook-applications}{{244.2}{829}{Using Your Widget as Facebook Applications}{section.244.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {245}Using Social Bookmarks}{831}{chapter.245}\\protected@file@percent }\n\\newlabel{using-social-bookmarks}{{245}{831}{Using Social Bookmarks}{chapter.245}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {245.1}{\\ignorespaces The default social bookmarks appear in a menu below content.}}{831}{figure.245.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {246}Search}{833}{chapter.246}\\protected@file@percent }\n\\newlabel{search}{{246}{833}{Search}{chapter.246}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {246.1}{\\ignorespaces The Type Facet configuration lists the searchable out-of-the-box asset types.}}{833}{figure.246.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {246.1}Elasticsearch}{833}{section.246.1}\\protected@file@percent }\n\\newlabel{elasticsearch}{{246.1}{833}{Elasticsearch}{section.246.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {246.2}Search Features}{834}{section.246.2}\\protected@file@percent }\n\\newlabel{search-features}{{246.2}{834}{Search Features}{section.246.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {246.2}{\\ignorespaces There's a search bar embedded on all pages by default.}}{834}{figure.246.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {246.3}{\\ignorespaces Results are displayed in the Search Results portlet.}}{834}{figure.246.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {246.3}Search Bar}{835}{section.246.3}\\protected@file@percent }\n\\newlabel{search-bar}{{246.3}{835}{Search Bar}{section.246.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {246.4}Search Results and Relevance}{835}{section.246.4}\\protected@file@percent }\n\\newlabel{search-results-and-relevance}{{246.4}{835}{Search Results and Relevance}{section.246.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {246.5}Search Facets}{835}{section.246.5}\\protected@file@percent }\n\\newlabel{search-facets}{{246.5}{835}{Search Facets}{section.246.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {246.4}{\\ignorespaces \\emph  {Site} and \\emph  {Type} are two of the facet sets you'll encounter. They let you drill down to results that contain the search terms you entered.}}{836}{figure.246.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {247}What's New in Search}{837}{chapter.247}\\protected@file@percent }\n\\newlabel{whats-new-in-search}{{247}{837}{What's New in Search}{chapter.247}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.1}New and Improved Widgets}{837}{section.247.1}\\protected@file@percent }\n\\newlabel{new-and-improved-widgets}{{247.1}{837}{New and Improved Widgets}{section.247.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.2}Custom Filter}{837}{section.247.2}\\protected@file@percent }\n\\newlabel{custom-filter}{{247.2}{837}{Custom Filter}{section.247.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.3}Sort}{837}{section.247.3}\\protected@file@percent }\n\\newlabel{sort}{{247.3}{837}{Sort}{section.247.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.4}Search Insights}{838}{section.247.4}\\protected@file@percent }\n\\newlabel{search-insights}{{247.4}{838}{Search Insights}{section.247.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.5}New Search Admin Functionality}{838}{section.247.5}\\protected@file@percent }\n\\newlabel{new-search-admin-functionality}{{247.5}{838}{New Search Admin Functionality}{section.247.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.6}Search Engine Info}{838}{section.247.6}\\protected@file@percent }\n\\newlabel{search-engine-info}{{247.6}{838}{Search Engine Info}{section.247.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.7}Field Mappings}{838}{section.247.7}\\protected@file@percent }\n\\newlabel{field-mappings}{{247.7}{838}{Field Mappings}{section.247.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.8}Indexing Progress}{839}{section.247.8}\\protected@file@percent }\n\\newlabel{indexing-progress}{{247.8}{839}{Indexing Progress}{section.247.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.9}New System Settings}{839}{section.247.9}\\protected@file@percent }\n\\newlabel{new-system-settings}{{247.9}{839}{New System Settings}{section.247.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.10}New Infrastructure}{839}{section.247.10}\\protected@file@percent }\n\\newlabel{new-infrastructure}{{247.10}{839}{New Infrastructure}{section.247.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.11}Elasticsearch Support}{839}{section.247.11}\\protected@file@percent }\n\\newlabel{elasticsearch-support}{{247.11}{839}{Elasticsearch Support}{section.247.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.12}Application-Specific Indexes}{839}{section.247.12}\\protected@file@percent }\n\\newlabel{application-specific-indexes}{{247.12}{839}{Application-Specific Indexes}{section.247.12}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.13}API Enhancements}{840}{section.247.13}\\protected@file@percent }\n\\newlabel{api-enhancements}{{247.13}{840}{API Enhancements}{section.247.13}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {247.14}Multi-Language Search}{840}{section.247.14}\\protected@file@percent }\n\\newlabel{multi-language-search}{{247.14}{840}{Multi-Language Search}{section.247.14}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {248}Configuring Search Pages}{841}{chapter.248}\\protected@file@percent }\n\\newlabel{configuring-search-pages}{{248}{841}{Configuring Search Pages}{chapter.248}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {248.1}Search Page Templates}{841}{section.248.1}\\protected@file@percent }\n\\newlabel{search-page-templates}{{248.1}{841}{Search Page Templates}{section.248.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {248.1}{\\ignorespaces At first glance, not much is happening on the search page. But, there's more than meets the eye.}}{842}{figure.248.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {248.2}Default Search Pages}{842}{section.248.2}\\protected@file@percent }\n\\newlabel{default-search-pages}{{248.2}{842}{Default Search Pages}{section.248.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {248.2}{\\ignorespaces By default, the embedded search bar points to the pre-configured \\texttt  {/search} destination page.}}{843}{figure.248.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {248.3}{\\ignorespaces The default page is pre-configured with the Search Results widget and the various Facet widgets to provide a full search experience.}}{843}{figure.248.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {248.4}{\\ignorespaces Configure the Search page. By default, it doesn't inherit changes from the page template.}}{844}{figure.248.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {248.3}Manual Search Page Configuration}{845}{section.248.3}\\protected@file@percent }\n\\newlabel{manual-search-page-configuration}{{248.3}{845}{Manual Search Page Configuration}{section.248.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {248.5}{\\ignorespaces The search bar is only visible if it points to an existent page.}}{845}{figure.248.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {248.6}{\\ignorespaces There's a handy page template for creating search pages.}}{845}{figure.248.6}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {248.4}Legacy Search Experience}{846}{section.248.4}\\protected@file@percent }\n\\newlabel{legacy-search-experience}{{248.4}{846}{Legacy Search Experience}{section.248.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {249}Searching for Assets}{847}{chapter.249}\\protected@file@percent }\n\\newlabel{searching-for-assets}{{249}{847}{Searching for Assets}{chapter.249}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {249.1}Search Bar}{847}{section.249.1}\\protected@file@percent }\n\\newlabel{search-bar-1}{{249.1}{847}{Search Bar}{section.249.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {249.1}{\\ignorespaces The default search configuration displays a search bar in its default view, beckoning users to enter the search context.}}{847}{figure.249.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {249.2}Entering Search Terms}{848}{section.249.2}\\protected@file@percent }\n\\newlabel{entering-search-terms}{{249.2}{848}{Entering Search Terms}{section.249.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {249.2}{\\ignorespaces Search for text in a specific field using Elasticsearch's Query String syntax.}}{848}{figure.249.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {249.3}Matching Exact Phrases: Quoted Search}{848}{section.249.3}\\protected@file@percent }\n\\newlabel{matching-exact-phrases-quoted-search}{{249.3}{848}{Matching Exact Phrases: Quoted Search}{section.249.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {249.4}Prefix Searching}{848}{section.249.4}\\protected@file@percent }\n\\newlabel{prefix-searching}{{249.4}{848}{Prefix Searching}{section.249.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {249.3}{\\ignorespaces Search for exact phrase matches by enclosing search terms in quotes. If a user searched for \\emph  {``agile frameworks''}, this result would not be returned.}}{849}{figure.249.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {249.4}{\\ignorespaces Searching for \\emph  {lever} also returns \\emph  {leverage} and \\emph  {leveraging}.}}{849}{figure.249.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {249.5}Configuring the Search Bar}{849}{section.249.5}\\protected@file@percent }\n\\newlabel{configuring-the-search-bar}{{249.5}{849}{Configuring the Search Bar}{section.249.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {249.5}{\\ignorespaces Configure the search bar behavior in its configuration screen.}}{850}{figure.249.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {249.6}{\\ignorespaces Let the user choose which scope the search is executed for.}}{851}{figure.249.6}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {249.6}Search Suggestions}{851}{section.249.6}\\protected@file@percent }\n\\newlabel{search-suggestions}{{249.6}{851}{Search Suggestions}{section.249.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {249.7}{\\ignorespaces Configure the suggestion settings to allow for user input mistakes and help lead users to results.}}{852}{figure.249.7}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {250}Facets}{853}{chapter.250}\\protected@file@percent }\n\\newlabel{facets}{{250}{853}{Facets}{chapter.250}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {250.1}{\\ignorespaces \\emph  {Site} and \\emph  {Type} are two of the facet sets you'll encounter.}}{853}{figure.250.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {250.1}Using Facets}{854}{section.250.1}\\protected@file@percent }\n\\newlabel{using-facets}{{250.1}{854}{Using Facets}{section.250.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {250.2}{\\ignorespaces When presented lots of search results, facets narrow down the results list so users can find relevant content.}}{854}{figure.250.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {250.2}Multiple Facet Selection}{855}{section.250.2}\\protected@file@percent }\n\\newlabel{multiple-facet-selection}{{250.2}{855}{Multiple Facet Selection}{section.250.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {250.3}{\\ignorespaces Facet terms are additive when applied in the same facet. Any Blogs Entry OR Web Content article matching the keyword is shown here.}}{855}{figure.250.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {250.4}{\\ignorespaces Facet terms selected from different facets are exclusive. These results must be of type Blogs Entry AND be from the User Marvin Smart.}}{856}{figure.250.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {250.3}Facets and Friendly URLs}{856}{section.250.3}\\protected@file@percent }\n\\newlabel{facets-and-friendly-urls}{{250.3}{856}{Facets and Friendly URLs}{section.250.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {250.5}{\\ignorespaces Both intra-facet and inter-facet selection is possible.}}{857}{figure.250.5}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {251}Site Facet}{859}{chapter.251}\\protected@file@percent }\n\\newlabel{site-facet}{{251}{859}{Site Facet}{chapter.251}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {251.1}{\\ignorespaces Each Site with matching content is a facet term.}}{859}{figure.251.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {251.2}{\\ignorespaces The Site Facet is configurable.}}{860}{figure.251.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {252}Type Facet}{861}{chapter.252}\\protected@file@percent }\n\\newlabel{type-facet}{{252}{861}{Type Facet}{chapter.252}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {252.1}{\\ignorespaces Each Asset Type with matching content is a Type Facet term.}}{861}{figure.252.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {252.2}{\\ignorespaces The Type Facet is configurable.}}{862}{figure.252.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {253}Tag and Category Facets}{863}{chapter.253}\\protected@file@percent }\n\\newlabel{tag-and-category-facets}{{253}{863}{Tag and Category Facets}{chapter.253}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {253.1}{\\ignorespaces Each Tag or Category with matching content is a facet term.}}{863}{figure.253.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {253.2}{\\ignorespaces Tag and Category Facets are configurable.}}{864}{figure.253.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {254}Folder Facet}{865}{chapter.254}\\protected@file@percent }\n\\newlabel{folder-facet}{{254}{865}{Folder Facet}{chapter.254}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {254.1}{\\ignorespaces Each Folder with matching content is a facet term.}}{865}{figure.254.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {254.2}{\\ignorespaces The Folder Facet is configurable.}}{866}{figure.254.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {255}User Facet}{867}{chapter.255}\\protected@file@percent }\n\\newlabel{user-facet}{{255}{867}{User Facet}{chapter.255}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {255.1}{\\ignorespaces Each User with matching content is a facet term.}}{867}{figure.255.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {255.2}{\\ignorespaces The User Facet is configurable.}}{868}{figure.255.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {256}Modified Facet}{869}{chapter.256}\\protected@file@percent }\n\\newlabel{modified-facet}{{256}{869}{Modified Facet}{chapter.256}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {256.1}{\\ignorespaces Each time period with matching content is a facet term.}}{869}{figure.256.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {256.2}{\\ignorespaces Users can include a Custom Range in the Modified Facet.}}{870}{figure.256.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {256.3}{\\ignorespaces The time ranges are set in the facet's configuration.}}{870}{figure.256.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {257}Custom Facet}{873}{chapter.257}\\protected@file@percent }\n\\newlabel{custom-facet}{{257}{873}{Custom Facet}{chapter.257}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {257.1}{\\ignorespaces Custom Facets must be configured first.}}{873}{figure.257.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {257.2}{\\ignorespaces Configure a Custom Facet in no time.}}{874}{figure.257.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {257.1}Finding Indexed Fields}{874}{section.257.1}\\protected@file@percent }\n\\newlabel{finding-indexed-fields}{{257.1}{874}{Finding Indexed Fields}{section.257.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {258}Search Results}{877}{chapter.258}\\protected@file@percent }\n\\newlabel{search-results}{{258}{877}{Search Results}{chapter.258}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {258.1}{\\ignorespaces The goal is to return the perfect results to Users searching your site.}}{877}{figure.258.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {259}Display Settings}{879}{chapter.259}\\protected@file@percent }\n\\newlabel{display-settings}{{259}{879}{Display Settings}{chapter.259}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {259.1}{\\ignorespaces Viewing a results document lets you inspect exactly what's being indexed for a particular asset. This is just a small portion of one document.}}{880}{figure.259.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {259.2}{\\ignorespaces The number of results per page and the URL parameter names used to control pagination behavior are configurable.}}{881}{figure.259.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {260}Filtering Search Results with the Custom Filter Widget}{883}{chapter.260}\\protected@file@percent }\n\\newlabel{filtering-search-results-with-the-custom-filter-widget}{{260}{883}{Filtering Search Results with the Custom Filter Widget}{chapter.260}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {260.1}Adding and Configuring Custom Filters}{883}{section.260.1}\\protected@file@percent }\n\\newlabel{adding-and-configuring-custom-filters}{{260.1}{883}{Adding and Configuring Custom Filters}{section.260.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {260.1}{\\ignorespaces A custom filter has no impact until it's configured.}}{884}{figure.260.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {260.2}Custom Filter Configuration Options}{884}{section.260.2}\\protected@file@percent }\n\\newlabel{custom-filter-configuration-options}{{260.2}{884}{Custom Filter Configuration Options}{section.260.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {260.2}{\\ignorespaces Once the Custom Filter is added to the page, mold it like soft clay into the beautiful sculpture you've envisioned.}}{885}{figure.260.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {261}Sorting Search Results with the Sort Widget}{887}{chapter.261}\\protected@file@percent }\n\\newlabel{sorting-search-results-with-the-sort-widget}{{261}{887}{Sorting Search Results with the Sort Widget}{chapter.261}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {261.1}Adding and Configuring the Sort Widget}{887}{section.261.1}\\protected@file@percent }\n\\newlabel{adding-and-configuring-the-sort-widget}{{261.1}{887}{Adding and Configuring the Sort Widget}{section.261.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {261.1}{\\ignorespaces Users can re-order search results with the Sort widget.}}{888}{figure.261.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {261.2}Configuring the Sort Widget}{888}{section.261.2}\\protected@file@percent }\n\\newlabel{configuring-the-sort-widget}{{261.2}{888}{Configuring the Sort Widget}{section.261.2}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Finding Sortable Fields}{888}{figure.261.2}\\protected@file@percent }\n\\newlabel{finding-sortable-fields}{{261.2}{888}{Finding Sortable Fields}{figure.261.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {261.2}{\\ignorespaces From the Sort widget's configuration, add, edit, or remove Sort options.}}{889}{figure.261.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{Adding New Sort Options}{890}{figure.261.2}\\protected@file@percent }\n\\newlabel{adding-new-sort-options}{{261.2}{890}{Adding New Sort Options}{figure.261.2}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Editing and Deleting Sort Options}{890}{figure.261.2}\\protected@file@percent }\n\\newlabel{editing-and-deleting-sort-options}{{261.2}{890}{Editing and Deleting Sort Options}{figure.261.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {261.3}Controlling the Sort Order}{890}{section.261.3}\\protected@file@percent }\n\\newlabel{controlling-the-sort-order}{{261.3}{890}{Controlling the Sort Order}{section.261.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {262}Search Results Behavior}{891}{chapter.262}\\protected@file@percent }\n\\newlabel{search-results-behavior}{{262}{891}{Search Results Behavior}{chapter.262}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {262.1}Filtering Results with Facets}{891}{section.262.1}\\protected@file@percent }\n\\newlabel{filtering-results-with-facets}{{262.1}{891}{Filtering Results with Facets}{section.262.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {262.2}Search Results Relevance}{891}{section.262.2}\\protected@file@percent }\n\\newlabel{search-results-relevance}{{262.2}{891}{Search Results Relevance}{section.262.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {262.3}Permissions and Search Results}{892}{section.262.3}\\protected@file@percent }\n\\newlabel{permissions-and-search-results}{{262.3}{892}{Permissions and Search Results}{section.262.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {262.4}Initial Permissions Checking}{892}{section.262.4}\\protected@file@percent }\n\\newlabel{initial-permissions-checking}{{262.4}{892}{Initial Permissions Checking}{section.262.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {262.5}Final Permissions Checking}{892}{section.262.5}\\protected@file@percent }\n\\newlabel{final-permissions-checking}{{262.5}{892}{Final Permissions Checking}{section.262.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {262.6}Search and Staging}{893}{section.262.6}\\protected@file@percent }\n\\newlabel{search-and-staging}{{262.6}{893}{Search and Staging}{section.262.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {262.7}Result Summaries}{893}{section.262.7}\\protected@file@percent }\n\\newlabel{result-summaries}{{262.7}{893}{Result Summaries}{section.262.7}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {262.1}{\\ignorespaces User summaries contain only the User's full name.}}{894}{figure.262.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {262.2}{\\ignorespaces Documents and Media and Web Content folders include titles and descriptions in their summaries.}}{894}{figure.262.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {262.8}Highlighting}{894}{section.262.8}\\protected@file@percent }\n\\newlabel{highlighting}{{262.8}{894}{Highlighting}{section.262.8}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {262.3}{\\ignorespaces Some document summaries have lots of highlights if the search term matches text that appears in the summary.}}{894}{figure.262.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {262.4}{\\ignorespaces Results that match the search term won't always have highlights.}}{895}{figure.262.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {263}Search Insights}{897}{chapter.263}\\protected@file@percent }\n\\newlabel{search-insights-1}{{263}{897}{Search Insights}{chapter.263}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {263.1}Inspecting The Search Query String}{897}{section.263.1}\\protected@file@percent }\n\\newlabel{inspecting-the-search-query-string}{{263.1}{897}{Inspecting The Search Query String}{section.263.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {263.1}{\\ignorespaces The Search Insights widget is helpful during testing and development.}}{898}{figure.263.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {263.2}{\\ignorespaces The full query string isn't for the faint of heart. This example is clipped to spare the reader.}}{898}{figure.263.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {263.2}Explaining Search Results}{898}{section.263.2}\\protected@file@percent }\n\\newlabel{explaining-search-results}{{263.2}{898}{Explaining Search Results}{section.263.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {264}Searching for Localized Content}{901}{chapter.264}\\protected@file@percent }\n\\newlabel{searching-for-localized-content}{{264}{901}{Searching for Localized Content}{chapter.264}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.1}What is Localized Search?}{902}{section.264.1}\\protected@file@percent }\n\\newlabel{what-is-localized-search}{{264.1}{902}{What is Localized Search?}{section.264.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.2}Fully Localized}{902}{section.264.2}\\protected@file@percent }\n\\newlabel{fully-localized}{{264.2}{902}{Fully Localized}{section.264.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.3}Fully Localized for Search}{902}{section.264.3}\\protected@file@percent }\n\\newlabel{fully-localized-for-search}{{264.3}{902}{Fully Localized for Search}{section.264.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.4}Site-Localized Search}{902}{section.264.4}\\protected@file@percent }\n\\newlabel{site-localized-search}{{264.4}{902}{Site-Localized Search}{section.264.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.5}Assets Supporting Localized Search}{903}{section.264.5}\\protected@file@percent }\n\\newlabel{assets-supporting-localized-search}{{264.5}{903}{Assets Supporting Localized Search}{section.264.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.6}Web Content Articles:}{903}{section.264.6}\\protected@file@percent }\n\\newlabel{web-content-articles}{{264.6}{903}{Web Content Articles:}{section.264.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.7}Categories:}{903}{section.264.7}\\protected@file@percent }\n\\newlabel{categories}{{264.7}{903}{Categories:}{section.264.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.8}Documents and Media File Entries:}{904}{section.264.8}\\protected@file@percent }\n\\newlabel{documents-and-media-file-entries}{{264.8}{904}{Documents and Media File Entries:}{section.264.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.9}Dynamic Data Mapping Fields:}{904}{section.264.9}\\protected@file@percent }\n\\newlabel{dynamic-data-mapping-fields}{{264.9}{904}{Dynamic Data Mapping Fields:}{section.264.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.10}Examples}{904}{section.264.10}\\protected@file@percent }\n\\newlabel{examples}{{264.10}{904}{Examples}{section.264.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.11}Fully Localized Search for Web Content Articles}{904}{section.264.11}\\protected@file@percent }\n\\newlabel{fully-localized-search-for-web-content-articles}{{264.11}{904}{Fully Localized Search for Web Content Articles}{section.264.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {264.12}Site-Localized Search for Documents and Media}{905}{section.264.12}\\protected@file@percent }\n\\newlabel{site-localized-search-for-documents-and-media}{{264.12}{905}{Site-Localized Search for Documents and Media}{section.264.12}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {264.1}{\\ignorespaces Even though the content of this DM File is written in Portuguese, it was appended with the \\emph  {en} locale, so it's searchable in an English language site.}}{905}{figure.264.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {264.2}{\\ignorespaces The uploaded DM File doesn't appear when the site language is changed, because only fields with the site's locale are searched.}}{906}{figure.264.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {264.3}{\\ignorespaces Once the field is reindexed with the site's locale, it can be returned as a search result in the site.}}{906}{figure.264.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {265}Configuring Search}{907}{chapter.265}\\protected@file@percent }\n\\newlabel{configuring-search}{{265}{907}{Configuring Search}{chapter.265}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.1}System Scoped Search Configuration}{907}{section.265.1}\\protected@file@percent }\n\\newlabel{system-scoped-search-configuration}{{265.1}{907}{System Scoped Search Configuration}{section.265.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.2}Default Keyword Query}{907}{section.265.2}\\protected@file@percent }\n\\newlabel{default-keyword-query}{{265.2}{907}{Default Keyword Query}{section.265.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {265.1}{\\ignorespaces There are numerous system scoped entries for search in System Settings.}}{908}{figure.265.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.3}Default Search Result Permission Filter}{909}{section.265.3}\\protected@file@percent }\n\\newlabel{default-search-result-permission-filter}{{265.3}{909}{Default Search Result Permission Filter}{section.265.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.4}Index Status Manager}{909}{section.265.4}\\protected@file@percent }\n\\newlabel{index-status-manager}{{265.4}{909}{Index Status Manager}{section.265.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.5}Indexer Writer Helper}{909}{section.265.5}\\protected@file@percent }\n\\newlabel{indexer-writer-helper}{{265.5}{909}{Indexer Writer Helper}{section.265.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.6}Index Registry}{910}{section.265.6}\\protected@file@percent }\n\\newlabel{index-registry}{{265.6}{910}{Index Registry}{section.265.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.7}Index Query Preprocessor}{910}{section.265.7}\\protected@file@percent }\n\\newlabel{index-query-preprocessor}{{265.7}{910}{Index Query Preprocessor}{section.265.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.8}Reindex}{910}{section.265.8}\\protected@file@percent }\n\\newlabel{reindex}{{265.8}{910}{Reindex}{section.265.8}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.9}Engine Helper}{911}{section.265.9}\\protected@file@percent }\n\\newlabel{engine-helper}{{265.9}{911}{Engine Helper}{section.265.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.10}Permission Checker}{911}{section.265.10}\\protected@file@percent }\n\\newlabel{permission-checker}{{265.10}{911}{Permission Checker}{section.265.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.11}Title Field Query Builder}{911}{section.265.11}\\protected@file@percent }\n\\newlabel{title-field-query-builder}{{265.11}{911}{Title Field Query Builder}{section.265.11}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.12}Elasticsearch 6}{911}{section.265.12}\\protected@file@percent }\n\\newlabel{elasticsearch-6}{{265.12}{911}{Elasticsearch 6}{section.265.12}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.13}Search Web}{912}{section.265.13}\\protected@file@percent }\n\\newlabel{search-web}{{265.13}{912}{Search Web}{section.265.13}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.14}Search Administration}{912}{section.265.14}\\protected@file@percent }\n\\newlabel{search-administration}{{265.14}{912}{Search Administration}{section.265.14}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Index Actions}{912}{section.265.14}\\protected@file@percent }\n\\newlabel{index-actions}{{265.14}{912}{Index Actions}{section.265.14}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Field Mappings}{913}{section.265.14}\\protected@file@percent }\n\\newlabel{field-mappings-1}{{265.14}{913}{Field Mappings}{section.265.14}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.15}Portal Properties}{913}{section.265.15}\\protected@file@percent }\n\\newlabel{portal-properties}{{265.15}{913}{Portal Properties}{section.265.15}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.16}Site Scoped Search Configuration}{913}{section.265.16}\\protected@file@percent }\n\\newlabel{site-scoped-search-configuration}{{265.16}{913}{Site Scoped Search Configuration}{section.265.16}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {265.17}Widget Scoped Search Configuration}{914}{section.265.17}\\protected@file@percent }\n\\newlabel{widget-scoped-search-configuration}{{265.17}{914}{Widget Scoped Search Configuration}{section.265.17}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {266}Low Level Search Options: Searching Additional or Alternate Indexes}{915}{chapter.266}\\protected@file@percent }\n\\newlabel{low-level-search-options-searching-additional-or-alternate-indexes}{{266}{915}{Low Level Search Options: Searching Additional or Alternate Indexes}{chapter.266}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {266.1}Configuring Low Level Search}{915}{section.266.1}\\protected@file@percent }\n\\newlabel{configuring-low-level-search}{{266.1}{915}{Configuring Low Level Search}{section.266.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {266.1}{\\ignorespaces The Low Level Options widget has several configuration options.}}{916}{figure.266.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {266.2}Example: Searching an Alternate Index}{917}{section.266.2}\\protected@file@percent }\n\\newlabel{example-searching-an-alternate-index}{{266.2}{917}{Example: Searching an Alternate Index}{section.266.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {266.2}{\\ignorespaces Configure the search page to search a different index.}}{918}{figure.266.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {267}Search Tuning: Synonym Sets}{919}{chapter.267}\\protected@file@percent }\n\\newlabel{search-tuning-synonym-sets}{{267}{919}{Search Tuning: Synonym Sets}{chapter.267}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {267.1}Requirements and Limitations}{919}{section.267.1}\\protected@file@percent }\n\\newlabel{requirements-and-limitations}{{267.1}{919}{Requirements and Limitations}{section.267.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {267.2}Creating and Managing Synonym Sets}{920}{section.267.2}\\protected@file@percent }\n\\newlabel{creating-and-managing-synonym-sets}{{267.2}{920}{Creating and Managing Synonym Sets}{section.267.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {267.1}{\\ignorespaces Add as many synonymous keywords to a set as you'd like.}}{920}{figure.267.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {267.2}{\\ignorespaces Synonym sets can be managed in bulk.}}{920}{figure.267.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {267.3}Using Synonym Sets}{921}{section.267.3}\\protected@file@percent }\n\\newlabel{using-synonym-sets}{{267.3}{921}{Using Synonym Sets}{section.267.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {267.3}{\\ignorespaces The Blogs Entry does not contain the word ``rover'' but it can be matched because of a synonym set mapping ``cart'' as its synonym. The synonym is even highlighted.}}{921}{figure.267.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {267.4}Known Issues}{921}{section.267.4}\\protected@file@percent }\n\\newlabel{known-issues}{{267.4}{921}{Known Issues}{section.267.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {267.5}Related Resources}{921}{section.267.5}\\protected@file@percent }\n\\newlabel{related-resources}{{267.5}{921}{Related Resources}{section.267.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {268}Search Tuning: Customizing Search Results}{923}{chapter.268}\\protected@file@percent }\n\\newlabel{search-tuning-customizing-search-results}{{268}{923}{Search Tuning: Customizing Search Results}{chapter.268}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {268.1}{\\ignorespaces The Lunar Resort wants to tweak these results: pin the Activities page to the top, and hide the legal content entirely.}}{924}{figure.268.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.1}Availability}{924}{section.268.1}\\protected@file@percent }\n\\newlabel{availability}{{268.1}{924}{Availability}{section.268.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.2}Requirements and Limitations}{924}{section.268.2}\\protected@file@percent }\n\\newlabel{requirements-and-limitations-1}{{268.2}{924}{Requirements and Limitations}{section.268.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.3}Creating and Managing Result Rankings}{925}{section.268.3}\\protected@file@percent }\n\\newlabel{creating-and-managing-result-rankings}{{268.3}{925}{Creating and Managing Result Rankings}{section.268.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.4}Adding Aliases}{925}{section.268.4}\\protected@file@percent }\n\\newlabel{adding-aliases}{{268.4}{925}{Adding Aliases}{section.268.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {268.2}{\\ignorespaces Apply your custom rankings to matched results of additional search terms.}}{925}{figure.268.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.5}Activating and Deactivating Aliases}{926}{section.268.5}\\protected@file@percent }\n\\newlabel{activating-and-deactivating-aliases}{{268.5}{926}{Activating and Deactivating Aliases}{section.268.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.6}Pinning and Hiding Results}{926}{section.268.6}\\protected@file@percent }\n\\newlabel{pinning-and-hiding-results}{{268.6}{926}{Pinning and Hiding Results}{section.268.6}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {268.3}{\\ignorespaces Pin results to the top of the Search Results list.}}{926}{figure.268.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.7}Adding Results}{927}{section.268.7}\\protected@file@percent }\n\\newlabel{adding-results}{{268.7}{927}{Adding Results}{section.268.7}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {268.4}{\\ignorespaces Add results that aren't normally returned.}}{927}{figure.268.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.8}Re-Ordering Pinned Results}{928}{section.268.8}\\protected@file@percent }\n\\newlabel{re-ordering-pinned-results}{{268.8}{928}{Re-Ordering Pinned Results}{section.268.8}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {268.5}{\\ignorespaces Re-order the pinned rankings if you want to emphasize or de-emphasize certain results.}}{928}{figure.268.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.9}Result Rankings Scope and Permissions}{928}{section.268.9}\\protected@file@percent }\n\\newlabel{result-rankings-scope-and-permissions}{{268.9}{928}{Result Rankings Scope and Permissions}{section.268.9}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.10}Result Rankings Aliases versus Synonyms}{929}{section.268.10}\\protected@file@percent }\n\\newlabel{result-rankings-aliases-versus-synonyms}{{268.10}{929}{Result Rankings Aliases versus Synonyms}{section.268.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {268.11}Known Issues}{929}{section.268.11}\\protected@file@percent }\n\\newlabel{known-issues-1}{{268.11}{929}{Known Issues}{section.268.11}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {269}Forms}{931}{chapter.269}\\protected@file@percent }\n\\newlabel{forms}{{269}{931}{Forms}{chapter.269}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {269.1}Forms and Lists}{932}{section.269.1}\\protected@file@percent }\n\\newlabel{forms-and-lists}{{269.1}{932}{Forms and Lists}{section.269.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {269.2}Which Form Builder Should I Use?}{932}{section.269.2}\\protected@file@percent }\n\\newlabel{which-form-builder-should-i-use}{{269.2}{932}{Which Form Builder Should I Use?}{section.269.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {270}Creating and Managing Forms}{933}{chapter.270}\\protected@file@percent }\n\\newlabel{creating-and-managing-forms}{{270}{933}{Creating and Managing Forms}{chapter.270}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {270.1}Viewing Forms}{933}{section.270.1}\\protected@file@percent }\n\\newlabel{viewing-forms}{{270.1}{933}{Viewing Forms}{section.270.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {270.1}{\\ignorespaces Get feedback from guests of The Lunar Resort.}}{934}{figure.270.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {270.2}{\\ignorespaces Forms are displayed in List format by default.}}{934}{figure.270.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {270.2}Building a Form}{935}{section.270.2}\\protected@file@percent }\n\\newlabel{building-a-form}{{270.2}{935}{Building a Form}{section.270.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {270.3}{\\ignorespaces You can choose from nine field types when creating forms.}}{936}{figure.270.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {270.4}{\\ignorespaces The form builder page lets you preview your form layout, add a page to the form, or add some more fields.}}{937}{figure.270.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {270.3}Accessing Forms}{938}{section.270.3}\\protected@file@percent }\n\\newlabel{accessing-forms}{{270.3}{938}{Accessing Forms}{section.270.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {270.5}{\\ignorespaces Add a page for guests to view and fill out your new form.}}{938}{figure.270.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {270.6}{\\ignorespaces You must first publish a form before you can get a shareable link.}}{939}{figure.270.6}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {270.7}{\\ignorespaces Copy the link to your form.}}{939}{figure.270.7}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {270.8}{\\ignorespaces Lunar Resort guests can use a simple form to record their feelings about the resort.}}{940}{figure.270.8}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {271}Managing Form Entries}{941}{chapter.271}\\protected@file@percent }\n\\newlabel{managing-form-entries}{{271}{941}{Managing Form Entries}{chapter.271}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {271.1}Viewing Form Entries}{941}{section.271.1}\\protected@file@percent }\n\\newlabel{viewing-form-entries}{{271.1}{941}{Viewing Form Entries}{section.271.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {271.1}{\\ignorespaces You can view the entries right in the Forms application.}}{941}{figure.271.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {271.2}{\\ignorespaces You can view a single entry right in the Forms application.}}{942}{figure.271.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {271.2}Exporting Form Entries}{942}{section.271.2}\\protected@file@percent }\n\\newlabel{exporting-form-entries}{{271.2}{942}{Exporting Form Entries}{section.271.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {271.3}{\\ignorespaces You can export entries as CSV, JSON, XLS, or XML.}}{943}{figure.271.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {271.3}Deleting Form Entries}{943}{section.271.3}\\protected@file@percent }\n\\newlabel{deleting-form-entries}{{271.3}{943}{Deleting Form Entries}{section.271.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {271.4}{\\ignorespaces Delete all form entries in one fell swoop.}}{944}{figure.271.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {272}Form Field Types}{945}{chapter.272}\\protected@file@percent }\n\\newlabel{form-field-types}{{272}{945}{Form Field Types}{chapter.272}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {272.1}{\\ignorespaces There are many useful out-of-the-box form field types.}}{946}{figure.272.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {272.2}{\\ignorespaces Use Paragraph fields to enter longer instructions on Form Pages.}}{946}{figure.272.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {272.3}{\\ignorespaces Text fields can be single line or multi-line.}}{947}{figure.272.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {272.4}{\\ignorespaces Use a select from list field to let Users choose predefined options.}}{947}{figure.272.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {272.5}{\\ignorespaces Single selection fields allow only one selection.}}{947}{figure.272.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {272.6}{\\ignorespaces Date fields show a date picker so Users enter a valid date.}}{948}{figure.272.6}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {272.7}{\\ignorespaces A multiple selection field can use a toggle.}}{948}{figure.272.7}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {272.8}{\\ignorespaces Grid fields use the same options (columns) for multiple categories (rows).}}{949}{figure.272.8}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {272.9}{\\ignorespaces Numeric fields accept only numeric input.}}{949}{figure.272.9}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {272.10}{\\ignorespaces : Upload fields let Users attach files to the form.}}{949}{figure.272.10}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {273}Form Rules}{951}{chapter.273}\\protected@file@percent }\n\\newlabel{form-rules}{{273}{951}{Form Rules}{chapter.273}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {273.1}The Anatomy of a Form Rule}{951}{section.273.1}\\protected@file@percent }\n\\newlabel{the-anatomy-of-a-form-rule}{{273.1}{951}{The Anatomy of a Form Rule}{section.273.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {273.2}Creating Form Rules: Rule Builder}{952}{section.273.2}\\protected@file@percent }\n\\newlabel{creating-form-rules-rule-builder}{{273.2}{952}{Creating Form Rules: Rule Builder}{section.273.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {273.1}{\\ignorespaces The Rule Builder gives you a handy interface for creating dynamic form rules.}}{952}{figure.273.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {273.3}Conditions}{953}{section.273.3}\\protected@file@percent }\n\\newlabel{conditions}{{273.3}{953}{Conditions}{section.273.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {274}Action: Show and Hide}{955}{chapter.274}\\protected@file@percent }\n\\newlabel{action-show-and-hide}{{274}{955}{Action: Show and Hide}{chapter.274}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {274.1}{\\ignorespaces Build form rules quickly by defining your conditions and actions.}}{956}{figure.274.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {274.2}{\\ignorespaces Once a rule is saved, it is displayed so that you can easily understand what it does.}}{956}{figure.274.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {275}Action: Require}{957}{chapter.275}\\protected@file@percent }\n\\newlabel{action-require}{{275}{957}{Action: Require}{chapter.275}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {275.1}{\\ignorespaces Build form rules quickly by defining your conditions and actions.}}{958}{figure.275.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {275.2}{\\ignorespaces Once a rule is saved, it is displayed so that you can easily understand what it does.}}{958}{figure.275.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {276}Action: Enable and Disable}{959}{chapter.276}\\protected@file@percent }\n\\newlabel{action-enable-and-disable}{{276}{959}{Action: Enable and Disable}{chapter.276}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {276.1}{\\ignorespaces Build form rules quickly by defining your conditions and actions.}}{960}{figure.276.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {276.2}{\\ignorespaces Once a rule is saved, it is displayed so that you can easily understand what it does.}}{960}{figure.276.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {277}Action: Jump to Page}{961}{chapter.277}\\protected@file@percent }\n\\newlabel{action-jump-to-page}{{277}{961}{Action: Jump to Page}{chapter.277}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {277.1}{\\ignorespaces Build form rules quickly by defining your conditions and actions.}}{962}{figure.277.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {277.2}{\\ignorespaces Once a rule is saved, it is displayed so that you can easily understand what it does.}}{962}{figure.277.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {278}Action: Autofill}{963}{chapter.278}\\protected@file@percent }\n\\newlabel{action-autofill}{{278}{963}{Action: Autofill}{chapter.278}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {278.1}{\\ignorespaces Build form rules quickly by defining your conditions and actions.}}{964}{figure.278.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {278.2}{\\ignorespaces Once a rule is saved, it is displayed so that you can easily understand what it does.}}{964}{figure.278.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {278.1}Using Inputs with Autofill}{964}{section.278.1}\\protected@file@percent }\n\\newlabel{using-inputs-with-autofill}{{278.1}{964}{Using Inputs with Autofill}{section.278.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {278.3}{\\ignorespaces Create a data provider for the autofill rule.}}{965}{figure.278.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {278.4}{\\ignorespaces Create a form with a text field and a select from list field. These are used to provide the input to the data provider and be autofilled by its output.}}{966}{figure.278.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {278.5}{\\ignorespaces Create the autofill rule. Brag of your prowess.}}{967}{figure.278.5}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {279}Action: Calculate}{969}{chapter.279}\\protected@file@percent }\n\\newlabel{action-calculate}{{279}{969}{Action: Calculate}{chapter.279}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {279.1}{\\ignorespaces Build calculate actions with a handy calculator.}}{970}{figure.279.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {279.2}{\\ignorespaces Once a rule is saved, it is displayed so that you can easily understand what it does.}}{970}{figure.279.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {280}Form Element Sets}{971}{chapter.280}\\protected@file@percent }\n\\newlabel{form-element-sets}{{280}{971}{Form Element Sets}{chapter.280}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {280.1}Creating Element Sets}{971}{section.280.1}\\protected@file@percent }\n\\newlabel{creating-element-sets}{{280.1}{971}{Creating Element Sets}{section.280.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {280.2}Using Element Sets}{971}{section.280.2}\\protected@file@percent }\n\\newlabel{using-element-sets}{{280.2}{971}{Using Element Sets}{section.280.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {280.1}{\\ignorespaces Creating Element Sets is just like creating Forms. You just can't publish them.}}{972}{figure.280.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {280.2}{\\ignorespaces Add an Element Set the same way you add other Form Elements, like fields.}}{973}{figure.280.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {281}Data Providers}{975}{chapter.281}\\protected@file@percent }\n\\newlabel{data-providers}{{281}{975}{Data Providers}{chapter.281}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {281.1}Adding a Basic Data Provider}{976}{section.281.1}\\protected@file@percent }\n\\newlabel{adding-a-basic-data-provider}{{281.1}{976}{Adding a Basic Data Provider}{section.281.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {281.2}Using a Data Provider in a Select Field}{976}{section.281.2}\\protected@file@percent }\n\\newlabel{using-a-data-provider-in-a-select-field}{{281.2}{976}{Using a Data Provider in a Select Field}{section.281.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {281.1}{\\ignorespaces Set up a simple data provider in no time.}}{977}{figure.281.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {281.2}{\\ignorespaces Form users select an option form the list populated by the Data Provider.}}{978}{figure.281.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {281.3}Granting Data Provider Permissions}{978}{section.281.3}\\protected@file@percent }\n\\newlabel{granting-data-provider-permissions}{{281.3}{978}{Granting Data Provider Permissions}{section.281.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {281.4}Data Provider Configuration}{978}{section.281.4}\\protected@file@percent }\n\\newlabel{data-provider-configuration}{{281.4}{978}{Data Provider Configuration}{section.281.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {281.5}Troubleshooting Data Provider Errors}{979}{section.281.5}\\protected@file@percent }\n\\newlabel{troubleshooting-data-provider-errors}{{281.5}{979}{Troubleshooting Data Provider Errors}{section.281.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {281.3}{\\ignorespaces Set up Data Providers to display data retrieved from a REST service.}}{980}{figure.281.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {282}Auto-Save}{981}{chapter.282}\\protected@file@percent }\n\\newlabel{auto-save}{{282}{981}{Auto-Save}{chapter.282}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {282.1}{\\ignorespaces Configure the auto-save duration.}}{981}{figure.282.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {283}Translating Forms}{983}{chapter.283}\\protected@file@percent }\n\\newlabel{translating-forms}{{283}{983}{Translating Forms}{chapter.283}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {283.1}{\\ignorespaces Add a translation for the form.}}{984}{figure.283.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {283.2}{\\ignorespaces Translate as much of the form as possible into each language you expect users to need.}}{984}{figure.283.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {283.3}{\\ignorespaces Select the form's language.}}{985}{figure.283.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {284}Autocompleting Text Fields}{987}{chapter.284}\\protected@file@percent }\n\\newlabel{autocompleting-text-fields}{{284}{987}{Autocompleting Text Fields}{chapter.284}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {284.1}Configuring Autocomplete}{987}{section.284.1}\\protected@file@percent }\n\\newlabel{configuring-autocomplete}{{284.1}{987}{Configuring Autocomplete}{section.284.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {284.1}{\\ignorespaces You can configure a manual data provider to specify the options users can select from.}}{988}{figure.284.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {284.2}{\\ignorespaces When typing in a field with autocomplete, users are presented a list of selections from the configured data provider. The displayed results are filtered to include only selections containing the text entered by the user.}}{988}{figure.284.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {285}Form Success Pages}{991}{chapter.285}\\protected@file@percent }\n\\newlabel{form-success-pages}{{285}{991}{Form Success Pages}{chapter.285}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {285.1}{\\ignorespaces The default success message alerts users when their request completes successfully.}}{991}{figure.285.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {285.2}{\\ignorespaces There's a default Success Page message if you can't think of anything else to say.}}{992}{figure.285.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {285.3}{\\ignorespaces Add a Success Page using the edit menu for the form page.}}{992}{figure.285.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {286}Workflow and Forms}{993}{chapter.286}\\protected@file@percent }\n\\newlabel{workflow-and-forms}{{286}{993}{Workflow and Forms}{chapter.286}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {286.1}{\\ignorespaces Workflow is enabled in the Control Panel or in Site Administration for most Liferay DXP assets.}}{993}{figure.286.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {286.1}Enabling Workflow in a Form}{994}{section.286.1}\\protected@file@percent }\n\\newlabel{enabling-workflow-in-a-form}{{286.1}{994}{Enabling Workflow in a Form}{section.286.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {286.2}{\\ignorespaces Enable workflow for each form in its Settings window.}}{994}{figure.286.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {286.2}Testing the Workflow}{995}{section.286.2}\\protected@file@percent }\n\\newlabel{testing-the-workflow}{{286.2}{995}{Testing the Workflow}{section.286.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {286.3}{\\ignorespaces Each entry's status is visible in the Forms application's Form Entries screen.}}{995}{figure.286.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {287}Duplicating Forms and Form Fields}{997}{chapter.287}\\protected@file@percent }\n\\newlabel{duplicating-forms-and-form-fields}{{287}{997}{Duplicating Forms and Form Fields}{chapter.287}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {287.1}{\\ignorespaces The Duplicate option works the same for forms and form fields.}}{997}{figure.287.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {287.2}{\\ignorespaces You can duplicate form fields.}}{998}{figure.287.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {288}Form Pages}{999}{chapter.288}\\protected@file@percent }\n\\newlabel{form-pages}{{288}{999}{Form Pages}{chapter.288}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {288.1}{\\ignorespaces The default pagination style.}}{999}{figure.288.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {288.2}{\\ignorespaces The alternate pagination style as seen in the Form Builder.}}{1000}{figure.288.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {288.3}{\\ignorespaces You can add new pages or reset the current page from the Page Actions menu.}}{1000}{figure.288.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {289}Help Text, Placeholder Text, and Predefined Values}{1003}{chapter.289}\\protected@file@percent }\n\\newlabel{help-text-placeholder-text-and-predefined-values}{{289}{1003}{Help Text, Placeholder Text, and Predefined Values}{chapter.289}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {289.1}{\\ignorespaces Predefined values and placeholder text are entered in the Properties tab.}}{1004}{figure.289.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {289.2}{\\ignorespaces The Full Name field here uses help text and placeholder text, while the sandwiches field uses a predefined value.}}{1004}{figure.289.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {290}Validating Text and Numeric Fields}{1005}{chapter.290}\\protected@file@percent }\n\\newlabel{validating-text-and-numeric-fields}{{290}{1005}{Validating Text and Numeric Fields}{chapter.290}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {290.1}{\\ignorespaces Validate data to ensure you're collecting only useful information.}}{1005}{figure.290.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {290.1}Validating Text Fields}{1006}{section.290.1}\\protected@file@percent }\n\\newlabel{validating-text-fields}{{290.1}{1006}{Validating Text Fields}{section.290.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {290.2}If Input Contains/Does Not Contain}{1006}{section.290.2}\\protected@file@percent }\n\\newlabel{if-input-containsdoes-not-contain}{{290.2}{1006}{If Input Contains/Does Not Contain}{section.290.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {290.2}{\\ignorespaces If \\emph  {Liferay} isn't part of the field's value, an error message is displayed.}}{1006}{figure.290.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {290.3}If Input Is not URL/Email}{1007}{section.290.3}\\protected@file@percent }\n\\newlabel{if-input-is-not-urlemail}{{290.3}{1007}{If Input Is not URL/Email}{section.290.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {290.3}{\\ignorespaces Use text field validation to make sure users enter a valid email address or URL.}}{1007}{figure.290.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {290.4}If Input Does Not Match}{1007}{section.290.4}\\protected@file@percent }\n\\newlabel{if-input-does-not-match}{{290.4}{1007}{If Input Does Not Match}{section.290.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {290.5}Validating Numeric Fields}{1007}{section.290.5}\\protected@file@percent }\n\\newlabel{validating-numeric-fields}{{290.5}{1007}{Validating Numeric Fields}{section.290.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {290.4}{\\ignorespaces Regular expression text validation opens up countless possibilities.}}{1008}{figure.290.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {290.5}{\\ignorespaces Numeric conditions constrain user-entered numeric data.}}{1008}{figure.290.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {290.6}{\\ignorespaces Make sure user-entered numeric data is within reasonable bounds. Nobody needs 11 sandwiches for lunch.}}{1009}{figure.290.6}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {291}Enabling CAPTCHA on Form Submissions}{1011}{chapter.291}\\protected@file@percent }\n\\newlabel{enabling-captcha-on-form-submissions}{{291}{1011}{Enabling CAPTCHA on Form Submissions}{chapter.291}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {291.1}{\\ignorespaces You can enable CAPTCHA for your form in the Form Settings window.}}{1011}{figure.291.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {291.2}{\\ignorespaces Once you enable CAPTCHA, your form has protection against bot submissions.}}{1012}{figure.291.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {292}Form Notifications}{1013}{chapter.292}\\protected@file@percent }\n\\newlabel{form-notifications}{{292}{1013}{Form Notifications}{chapter.292}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {292.1}{\\ignorespaces Configure email notifications each time a form entry is submitted.}}{1014}{figure.292.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {293}Redirecting Users}{1015}{chapter.293}\\protected@file@percent }\n\\newlabel{redirecting-users}{{293}{1015}{Redirecting Users}{chapter.293}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {293.1}{\\ignorespaces Redirect users after they submit a form.}}{1016}{figure.293.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {294}Form Permissions}{1017}{chapter.294}\\protected@file@percent }\n\\newlabel{form-permissions}{{294}{1017}{Form Permissions}{chapter.294}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {294.1}{\\ignorespaces You can configure a form's permissions.}}{1018}{figure.294.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {295}Styling Form Pages}{1019}{chapter.295}\\protected@file@percent }\n\\newlabel{styling-form-pages}{{295}{1019}{Styling Form Pages}{chapter.295}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {295.1}{\\ignorespaces This is the default single-column, vertically-oriented form.}}{1019}{figure.295.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {295.2}{\\ignorespaces Putting form fields in multiple columns can give you more space.}}{1020}{figure.295.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {295.3}{\\ignorespaces The first row is in two columns and the second row is in three columns.}}{1020}{figure.295.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {295.4}{\\ignorespaces Form field borders.}}{1020}{figure.295.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {295.5}{\\ignorespaces After resizing, the field is smaller.}}{1020}{figure.295.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {295.6}{\\ignorespaces There are now two fields in the row.}}{1021}{figure.295.6}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {295.7}{\\ignorespaces You can also move fields on form pages.}}{1021}{figure.295.7}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {296}Dynamic Data Lists}{1023}{chapter.296}\\protected@file@percent }\n\\newlabel{dynamic-data-lists}{{296}{1023}{Dynamic Data Lists}{chapter.296}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {296.1}System Configuration}{1024}{section.296.1}\\protected@file@percent }\n\\newlabel{system-configuration}{{296.1}{1024}{System Configuration}{section.296.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {297}Creating Data Definitions}{1025}{chapter.297}\\protected@file@percent }\n\\newlabel{creating-data-definitions}{{297}{1025}{Creating Data Definitions}{chapter.297}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {297.1}{\\ignorespaces The Data Definitions screen.}}{1025}{figure.297.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {297.2}{\\ignorespaces After naming your data definition, expand the Details section of the form and give your definition a description and parent definition, if desired.}}{1026}{figure.297.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {297.3}{\\ignorespaces Use the data definition designer to add fields to the data definition.}}{1027}{figure.297.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {297.4}{\\ignorespaces Configure the settings for each field in your data definition.}}{1029}{figure.297.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {298}Managing Data Definitions}{1031}{chapter.298}\\protected@file@percent }\n\\newlabel{managing-data-definitions}{{298}{1031}{Managing Data Definitions}{chapter.298}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {298.1}{\\ignorespaces You can copy an existing data definition, manage its templates, and more.}}{1031}{figure.298.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {299}Creating Data Lists}{1033}{chapter.299}\\protected@file@percent }\n\\newlabel{creating-data-lists}{{299}{1033}{Creating Data Lists}{chapter.299}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {299.1}Creating List Records}{1033}{section.299.1}\\protected@file@percent }\n\\newlabel{creating-list-records}{{299.1}{1033}{Creating List Records}{section.299.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {299.1}{\\ignorespaces The New List form.}}{1034}{figure.299.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {299.2}{\\ignorespaces Dynamic Data Lists Display widget.}}{1035}{figure.299.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {299.2}Configuring the Dynamic Data Lists Display Widget}{1035}{section.299.2}\\protected@file@percent }\n\\newlabel{configuring-the-dynamic-data-lists-display-widget}{{299.2}{1035}{Configuring the Dynamic Data Lists Display Widget}{section.299.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {299.3}{\\ignorespaces The Dynamic Data Lists Display widget's optional configuration.}}{1036}{figure.299.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {300}Using Templates to Display Forms and Lists}{1037}{chapter.300}\\protected@file@percent }\n\\newlabel{using-templates-to-display-forms-and-lists}{{300}{1037}{Using Templates to Display Forms and Lists}{chapter.300}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {300.1}Managing Display and Form Templates}{1037}{section.300.1}\\protected@file@percent }\n\\newlabel{managing-display-and-form-templates}{{300.1}{1037}{Managing Display and Form Templates}{section.300.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {301}Creating Form Templates}{1039}{chapter.301}\\protected@file@percent }\n\\newlabel{creating-form-templates}{{301}{1039}{Creating Form Templates}{chapter.301}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {302}Creating Display Templates}{1041}{chapter.302}\\protected@file@percent }\n\\newlabel{creating-display-templates}{{302}{1041}{Creating Display Templates}{chapter.302}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {302.1}{\\ignorespaces Create your display template in the editor.}}{1042}{figure.302.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {302.1}Display Template Editor}{1042}{section.302.1}\\protected@file@percent }\n\\newlabel{display-template-editor}{{302.1}{1042}{Display Template Editor}{section.302.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {302.2}{\\ignorespaces Extract appropriate display information, rather than spitting out the whole object.}}{1044}{figure.302.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {303}Workflow}{1045}{chapter.303}\\protected@file@percent }\n\\newlabel{workflow}{{303}{1045}{Workflow}{chapter.303}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {303.1}What's New with Workflow?}{1045}{section.303.1}\\protected@file@percent }\n\\newlabel{whats-new-with-workflow}{{303.1}{1045}{What's New with Workflow?}{section.303.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {303.2}DXP Feature: Workflow Metrics}{1046}{section.303.2}\\protected@file@percent }\n\\newlabel{dxp-feature-workflow-metrics}{{303.2}{1046}{DXP Feature: Workflow Metrics}{section.303.2}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Service Level Agreements (SLAs)}{1046}{section.303.2}\\protected@file@percent }\n\\newlabel{service-level-agreements-slas}{{303.2}{1046}{Service Level Agreements (SLAs)}{section.303.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {303.1}{\\ignorespaces Use Service Level Agreements (SLAs) to define how workflow metrics are reported.}}{1046}{figure.303.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{Workflow Reports}{1046}{figure.303.1}\\protected@file@percent }\n\\newlabel{workflow-reports-1}{{303.2}{1046}{Workflow Reports}{figure.303.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {303.3}Control Panel Reorganization}{1046}{section.303.3}\\protected@file@percent }\n\\newlabel{control-panel-reorganization}{{303.3}{1046}{Control Panel Reorganization}{section.303.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {303.2}{\\ignorespaces See Workflow Reports generated based on your SLAs.}}{1047}{figure.303.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {303.3}{\\ignorespaces Workflow has a top-level entry in the Control Panel.}}{1047}{figure.303.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {303.4}Workflow Definition Permissions: System Settings}{1048}{section.303.4}\\protected@file@percent }\n\\newlabel{workflow-definition-permissions-system-settings}{{303.4}{1048}{Workflow Definition Permissions: System Settings}{section.303.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {303.4}{\\ignorespaces Explicit permission must be granted before administrators are allowed to publish and edit workflow definitions.}}{1048}{figure.303.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {303.5}Embedded Workflows}{1048}{section.303.5}\\protected@file@percent }\n\\newlabel{embedded-workflows}{{303.5}{1048}{Embedded Workflows}{section.303.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {304}Activating Workflow}{1049}{chapter.304}\\protected@file@percent }\n\\newlabel{activating-workflow}{{304}{1049}{Activating Workflow}{chapter.304}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {304.1}Workflow Assets}{1049}{section.304.1}\\protected@file@percent }\n\\newlabel{workflow-assets}{{304.1}{1049}{Workflow Assets}{section.304.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {304.2}Activating Workflow in Applications}{1050}{section.304.2}\\protected@file@percent }\n\\newlabel{activating-workflow-in-applications}{{304.2}{1050}{Activating Workflow in Applications}{section.304.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {304.3}Workflow Behavior}{1050}{section.304.3}\\protected@file@percent }\n\\newlabel{workflow-behavior}{{304.3}{1050}{Workflow Behavior}{section.304.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {304.1}{\\ignorespaces Activate workflow on Web Content folders from the folder's edit screen.}}{1051}{figure.304.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {304.2}{\\ignorespaces Activate workflow on Documents and Media folders from the folder's edit screen.}}{1052}{figure.304.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {304.3}{\\ignorespaces Activate workflow for each individual Dynamic Data List.}}{1052}{figure.304.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {304.4}{\\ignorespaces Activate workflow on each form's entries from the Form Settings window.}}{1053}{figure.304.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {304.5}{\\ignorespaces Instead of a Publish button, a Submit for Publication button appears for workflow-enabled resources.}}{1053}{figure.304.5}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {304.6}{\\ignorespaces With workflow enabled on Page Revisions, the Site administrator must submit their page variation for publication before it can go live.}}{1053}{figure.304.6}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {305}Managing Workflows}{1055}{chapter.305}\\protected@file@percent }\n\\newlabel{managing-workflows}{{305}{1055}{Managing Workflows}{chapter.305}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {305.1}Workflow Definition Publication Permissions}{1055}{section.305.1}\\protected@file@percent }\n\\newlabel{workflow-definition-publication-permissions}{{305.1}{1055}{Workflow Definition Publication Permissions}{section.305.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {305.2}Adding, Editing, and Deleting}{1056}{section.305.2}\\protected@file@percent }\n\\newlabel{adding-editing-and-deleting}{{305.2}{1056}{Adding, Editing, and Deleting}{section.305.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {305.3}Uploading Workflow Definitions}{1056}{section.305.3}\\protected@file@percent }\n\\newlabel{uploading-workflow-definitions}{{305.3}{1056}{Uploading Workflow Definitions}{section.305.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {305.4}Published Versus Unpublished}{1057}{section.305.4}\\protected@file@percent }\n\\newlabel{published-versus-unpublished}{{305.4}{1057}{Published Versus Unpublished}{section.305.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {305.5}Workflow Versions}{1057}{section.305.5}\\protected@file@percent }\n\\newlabel{workflow-versions}{{305.5}{1057}{Workflow Versions}{section.305.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {305.1}{\\ignorespaces View and restore prior versions of a workflow.}}{1058}{figure.305.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {306}Reviewing Assets}{1059}{chapter.306}\\protected@file@percent }\n\\newlabel{reviewing-assets}{{306}{1059}{Reviewing Assets}{chapter.306}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {306.1}{\\ignorespaces Users manage workflow tasks from their My Workflow Tasks widget.}}{1059}{figure.306.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {306.1}Asset Submission to Workflow}{1060}{section.306.1}\\protected@file@percent }\n\\newlabel{asset-submission-to-workflow}{{306.1}{1060}{Asset Submission to Workflow}{section.306.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {306.2}{\\ignorespaces A User with VIEW permission on Web Content cannot manage Approved Articles.}}{1060}{figure.306.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {306.2}Assigning the Task}{1060}{section.306.2}\\protected@file@percent }\n\\newlabel{assigning-the-task}{{306.2}{1060}{Assigning the Task}{section.306.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {306.3}{\\ignorespaces A User with access to Web Content in the Workflow can manage Pending Articles.}}{1061}{figure.306.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {306.4}{\\ignorespaces The assets assigned to a user are listed in \\emph  {Assigned to Me}.}}{1061}{figure.306.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {306.5}{\\ignorespaces The Assets assigned to Roles are listed in each associated user's \\emph  {Assigned to My Roles} tab.}}{1061}{figure.306.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {306.3}Completing the Task}{1062}{section.306.3}\\protected@file@percent }\n\\newlabel{completing-the-task}{{306.3}{1062}{Completing the Task}{section.306.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {306.6}{\\ignorespaces Complete Tasks right from the \\emph  {Assigned to Me} list.}}{1062}{figure.306.6}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {306.7}{\\ignorespaces Inspect Assets before completing the Task.}}{1063}{figure.306.7}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {307}Workflow Metrics: The Service Level Agreement (SLA)}{1065}{chapter.307}\\protected@file@percent }\n\\newlabel{workflow-metrics-the-service-level-agreement-sla}{{307}{1065}{Workflow Metrics: The Service Level Agreement (SLA)}{chapter.307}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {307.1}Adding SLAs}{1065}{section.307.1}\\protected@file@percent }\n\\newlabel{adding-slas}{{307.1}{1065}{Adding SLAs}{section.307.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {307.1}{\\ignorespaces Add SLAs to a workflow definition from the Metrics application.}}{1066}{figure.307.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {307.2}{\\ignorespaces Manage SLAs from the SLAs screen.}}{1067}{figure.307.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{Valid Start and Stop Events}{1067}{figure.307.2}\\protected@file@percent }\n\\newlabel{valid-start-and-stop-events}{{307.1}{1067}{Valid Start and Stop Events}{figure.307.2}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Durations}{1067}{figure.307.2}\\protected@file@percent }\n\\newlabel{durations}{{307.1}{1067}{Durations}{figure.307.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {308}Workflow Metrics: Reports}{1069}{chapter.308}\\protected@file@percent }\n\\newlabel{workflow-metrics-reports}{{308}{1069}{Workflow Metrics: Reports}{chapter.308}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {308.1}{\\ignorespaces In this view, the only process with pending items is the Single Approver.}}{1069}{figure.308.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {308.1}Understanding Reports}{1069}{section.308.1}\\protected@file@percent }\n\\newlabel{understanding-reports}{{308.1}{1069}{Understanding Reports}{section.308.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {308.2}{\\ignorespaces See data on the Pending Items and the Workload by Step for a process.}}{1070}{figure.308.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {308.2}Pending Items}{1070}{section.308.2}\\protected@file@percent }\n\\newlabel{pending-items}{{308.2}{1070}{Pending Items}{section.308.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {308.3}Workload by Step}{1070}{section.308.3}\\protected@file@percent }\n\\newlabel{workload-by-step}{{308.3}{1070}{Workload by Step}{section.308.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {308.4}Completed Items}{1071}{section.308.4}\\protected@file@percent }\n\\newlabel{completed-items}{{308.4}{1071}{Completed Items}{section.308.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {308.5}Completion Velocity}{1071}{section.308.5}\\protected@file@percent }\n\\newlabel{completion-velocity}{{308.5}{1071}{Completion Velocity}{section.308.5}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {308.3}{\\ignorespaces View the completion rate of items in a workflow process over time.}}{1071}{figure.308.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {308.6}Items View}{1072}{section.308.6}\\protected@file@percent }\n\\newlabel{items-view}{{308.6}{1072}{Items View}{section.308.6}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {308.7}Filtering by SLA Status}{1072}{section.308.7}\\protected@file@percent }\n\\newlabel{filtering-by-sla-status}{{308.7}{1072}{Filtering by SLA Status}{section.308.7}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {308.8}Filtering by Process Status and Completion Period}{1072}{section.308.8}\\protected@file@percent }\n\\newlabel{filtering-by-process-status-and-completion-period}{{308.8}{1072}{Filtering by Process Status and Completion Period}{section.308.8}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {308.4}{\\ignorespaces Filter by SLA status: Overdue, On Time, or Untracked.}}{1073}{figure.308.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {308.9}Filtering by Process Step}{1073}{section.308.9}\\protected@file@percent }\n\\newlabel{filtering-by-process-step}{{308.9}{1073}{Filtering by Process Step}{section.308.9}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {308.5}{\\ignorespaces Filter by Process Status and Completion Period.}}{1074}{figure.308.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {308.10}Combining Filters}{1074}{section.308.10}\\protected@file@percent }\n\\newlabel{combining-filters}{{308.10}{1074}{Combining Filters}{section.308.10}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {308.11}Item Details}{1074}{section.308.11}\\protected@file@percent }\n\\newlabel{item-details}{{308.11}{1074}{Item Details}{section.308.11}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {308.6}{\\ignorespaces Combine filters to see just the items you want.}}{1075}{figure.308.6}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {308.7}{\\ignorespaces Item Details include SLA status information and whether the item is Resolved or Open.}}{1076}{figure.308.7}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {309}Workflow Designer}{1077}{chapter.309}\\protected@file@percent }\n\\newlabel{workflow-designer}{{309}{1077}{Workflow Designer}{chapter.309}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {310}Managing Workflows with the Workflow Designer}{1079}{chapter.310}\\protected@file@percent }\n\\newlabel{managing-workflows-with-the-workflow-designer}{{310}{1079}{Managing Workflows with the Workflow Designer}{chapter.310}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {310.1}Adding New Workflow Definitions with the Workflow Designer}{1079}{section.310.1}\\protected@file@percent }\n\\newlabel{adding-new-workflow-definitions-with-the-workflow-designer}{{310.1}{1079}{Adding New Workflow Definitions with the Workflow Designer}{section.310.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {310.2}Saving and Publishing Workflow Definitions}{1079}{section.310.2}\\protected@file@percent }\n\\newlabel{saving-and-publishing-workflow-definitions}{{310.2}{1079}{Saving and Publishing Workflow Definitions}{section.310.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {310.1}{\\ignorespaces The Workflow Designer's graphical interface makes designing workflows intuitive.}}{1080}{figure.310.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {310.3}Adding Nodes}{1080}{section.310.3}\\protected@file@percent }\n\\newlabel{adding-nodes}{{310.3}{1080}{Adding Nodes}{section.310.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {310.2}{\\ignorespaces View a list of the current workflows that can be edited in the Workflow Designer.}}{1081}{figure.310.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {310.3}{\\ignorespaces You can add a node by creating a transition that ends at a blank spot on your Designer canvas.}}{1081}{figure.310.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {310.4}Node Settings}{1081}{section.310.4}\\protected@file@percent }\n\\newlabel{node-settings}{{310.4}{1081}{Node Settings}{section.310.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {310.4}{\\ignorespaces You can edit a node's settings.}}{1082}{figure.310.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {310.5}Related Topics}{1082}{section.310.5}\\protected@file@percent }\n\\newlabel{related-topics-3}{{310.5}{1082}{Related Topics}{section.310.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {311}Workflow Definition Nodes}{1083}{chapter.311}\\protected@file@percent }\n\\newlabel{workflow-definition-nodes}{{311}{1083}{Workflow Definition Nodes}{chapter.311}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {311.1}Node Actions and Notifications}{1083}{section.311.1}\\protected@file@percent }\n\\newlabel{node-actions-and-notifications}{{311.1}{1083}{Node Actions and Notifications}{section.311.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {311.2}Actions}{1084}{section.311.2}\\protected@file@percent }\n\\newlabel{actions}{{311.2}{1084}{Actions}{section.311.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {311.1}{\\ignorespaces You can add an Action to a Task node.}}{1084}{figure.311.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {311.3}Notifications}{1085}{section.311.3}\\protected@file@percent }\n\\newlabel{notifications}{{311.3}{1085}{Notifications}{section.311.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {311.2}{\\ignorespaces You can send a Notification from a Task node.}}{1086}{figure.311.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {311.4}Start and End Nodes}{1087}{section.311.4}\\protected@file@percent }\n\\newlabel{start-and-end-nodes}{{311.4}{1087}{Start and End Nodes}{section.311.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {311.5}State Nodes}{1087}{section.311.5}\\protected@file@percent }\n\\newlabel{state-nodes}{{311.5}{1087}{State Nodes}{section.311.5}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {311.6}Related Topics}{1087}{section.311.6}\\protected@file@percent }\n\\newlabel{related-topics-4}{{311.6}{1087}{Related Topics}{section.311.6}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {312}Affecting the Processing of Workflow Definitions}{1089}{chapter.312}\\protected@file@percent }\n\\newlabel{affecting-the-processing-of-workflow-definitions}{{312}{1089}{Affecting the Processing of Workflow Definitions}{chapter.312}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {312.1}Transitions}{1089}{section.312.1}\\protected@file@percent }\n\\newlabel{transitions}{{312.1}{1089}{Transitions}{section.312.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {312.1}{\\ignorespaces You connect nodes and direct workflow processing with transitions. The Single Approver workflow has transitions named Submit, Resubmit, Reject, and Approve.}}{1090}{figure.312.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {312.2}{\\ignorespaces In the Single Approver workflow, a user in the Review task can choose to Approve or Reject the asset, which sends the asset either to the EndNode or to the Update task.}}{1090}{figure.312.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {312.2}Forks and Joins}{1091}{section.312.2}\\protected@file@percent }\n\\newlabel{forks-and-joins}{{312.2}{1091}{Forks and Joins}{section.312.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {312.3}{\\ignorespaces Forks and Joins are used to enable parallel processing in the workflow.}}{1091}{figure.312.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {312.3}Conditions}{1092}{section.312.3}\\protected@file@percent }\n\\newlabel{conditions-1}{{312.3}{1092}{Conditions}{section.312.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {312.4}{\\ignorespaces The Category Specific Approval definition starts with a Condition node.}}{1092}{figure.312.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {312.4}Related Topics}{1093}{section.312.4}\\protected@file@percent }\n\\newlabel{related-topics-5}{{312.4}{1093}{Related Topics}{section.312.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {313}Creating Tasks in the Workflow Designer}{1095}{chapter.313}\\protected@file@percent }\n\\newlabel{creating-tasks-in-the-workflow-designer}{{313}{1095}{Creating Tasks in the Workflow Designer}{chapter.313}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {313.1}Assignments}{1095}{section.313.1}\\protected@file@percent }\n\\newlabel{assignments}{{313.1}{1095}{Assignments}{section.313.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {313.1}{\\ignorespaces You can add an Assignment to a Task node.}}{1096}{figure.313.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {313.2}Resource Action Assignments}{1096}{section.313.2}\\protected@file@percent }\n\\newlabel{resource-action-assignments}{{313.2}{1096}{Resource Action Assignments}{section.313.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {313.2}{\\ignorespaces Configure resource action assignments in the Workflow Designer.}}{1097}{figure.313.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {313.3}Scripted Assignments}{1097}{section.313.3}\\protected@file@percent }\n\\newlabel{scripted-assignments}{{313.3}{1097}{Scripted Assignments}{section.313.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {313.4}Related Topics}{1098}{section.313.4}\\protected@file@percent }\n\\newlabel{related-topics-6}{{313.4}{1098}{Related Topics}{section.313.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {314}Kaleo Forms}{1099}{chapter.314}\\protected@file@percent }\n\\newlabel{kaleo-forms}{{314}{1099}{Kaleo Forms}{chapter.314}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {314.1}Creating Kaleo Forms Process}{1099}{section.314.1}\\protected@file@percent }\n\\newlabel{creating-kaleo-forms-process}{{314.1}{1099}{Creating Kaleo Forms Process}{section.314.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {314.1}{\\ignorespaces Add a Kaleo Forms Process to link a form with a workflow definition.}}{1100}{figure.314.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {314.2}{\\ignorespaces Define and choose your form's fields.}}{1100}{figure.314.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {314.3}{\\ignorespaces This example workflow has three tasks that happen sequentially.}}{1101}{figure.314.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {314.4}{\\ignorespaces Assign a form to each task in the workflow, and for the initial state.}}{1102}{figure.314.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {314.2}Adding Records to a Process}{1102}{section.314.2}\\protected@file@percent }\n\\newlabel{adding-records-to-a-process}{{314.2}{1102}{Adding Records to a Process}{section.314.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {315}Segmentation and Personalization}{1103}{chapter.315}\\protected@file@percent }\n\\newlabel{segmentation-and-personalization}{{315}{1103}{Segmentation and Personalization}{chapter.315}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {315.1}Defining Segments}{1103}{section.315.1}\\protected@file@percent }\n\\newlabel{defining-segments}{{315.1}{1103}{Defining Segments}{section.315.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {315.2}Integrating Segments with Analytics Cloud}{1104}{section.315.2}\\protected@file@percent }\n\\newlabel{integrating-segments-with-analytics-cloud}{{315.2}{1104}{Integrating Segments with Analytics Cloud}{section.315.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {315.3}Personalizing Experiences}{1104}{section.315.3}\\protected@file@percent }\n\\newlabel{personalizing-experiences}{{315.3}{1104}{Personalizing Experiences}{section.315.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {315.4}Content Page Personalization}{1104}{section.315.4}\\protected@file@percent }\n\\newlabel{content-page-personalization}{{315.4}{1104}{Content Page Personalization}{section.315.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {315.5}Content Set Personalization}{1104}{section.315.5}\\protected@file@percent }\n\\newlabel{content-set-personalization-1}{{315.5}{1104}{Content Set Personalization}{section.315.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {316}The Segment Editor}{1105}{chapter.316}\\protected@file@percent }\n\\newlabel{the-segment-editor}{{316}{1105}{The Segment Editor}{chapter.316}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {316.1}{\\ignorespaces The top portion of the Segment Editor has the segment name and its members.}}{1105}{figure.316.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {316.2}{\\ignorespaces You use the Segment Editor to create new Segments.}}{1106}{figure.316.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {316.1}User Properties}{1106}{section.316.1}\\protected@file@percent }\n\\newlabel{user-properties}{{316.1}{1106}{User Properties}{section.316.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {316.2}Organization Properties}{1106}{section.316.2}\\protected@file@percent }\n\\newlabel{organization-properties}{{316.2}{1106}{Organization Properties}{section.316.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {316.3}Session Properties}{1107}{section.316.3}\\protected@file@percent }\n\\newlabel{session-properties}{{316.3}{1107}{Session Properties}{section.316.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {316.4}Operations and Conjunctions}{1107}{section.316.4}\\protected@file@percent }\n\\newlabel{operations-and-conjunctions}{{316.4}{1107}{Operations and Conjunctions}{section.316.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {317}Creating User Segments}{1109}{chapter.317}\\protected@file@percent }\n\\newlabel{creating-user-segments}{{317}{1109}{Creating User Segments}{chapter.317}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {317.1}Creating a Custom Segment}{1109}{section.317.1}\\protected@file@percent }\n\\newlabel{creating-a-custom-segment}{{317.1}{1109}{Creating a Custom Segment}{section.317.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {317.1}{\\ignorespaces Setting the comparator to \\emph  {contains} includes variations of ``Engineer'' like ``Software Engineer'' in the segment.}}{1110}{figure.317.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {317.2}{\\ignorespaces You can prevent typos by directly selecting Organizations through the interface.}}{1110}{figure.317.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {317.2}Managing Segments}{1110}{section.317.2}\\protected@file@percent }\n\\newlabel{managing-segments}{{317.2}{1110}{Managing Segments}{section.317.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {317.3}{\\ignorespaces You can view the list of Segment members at any time.}}{1111}{figure.317.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {317.4}{\\ignorespaces You can edit, delete or manage permissions from the options menu.}}{1111}{figure.317.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {318}Creating Segments with Custom Fields and Session Data}{1113}{chapter.318}\\protected@file@percent }\n\\newlabel{creating-segments-with-custom-fields-and-session-data}{{318}{1113}{Creating Segments with Custom Fields and Session Data}{chapter.318}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {318.1}Creating a Custom Field}{1113}{section.318.1}\\protected@file@percent }\n\\newlabel{creating-a-custom-field}{{318.1}{1113}{Creating a Custom Field}{section.318.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {318.1}{\\ignorespaces You can easily create custom fields to capture whatever kind of data you need.}}{1114}{figure.318.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {318.2}Defining a Segment with a Custom Field}{1114}{section.318.2}\\protected@file@percent }\n\\newlabel{defining-a-segment-with-a-custom-field}{{318.2}{1114}{Defining a Segment with a Custom Field}{section.318.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {318.2}{\\ignorespaces The custom field you created is seamlessly integrated into segment creation.}}{1115}{figure.318.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {318.3}Extending a Segment With Session Data}{1115}{section.318.3}\\protected@file@percent }\n\\newlabel{extending-a-segment-with-session-data}{{318.3}{1115}{Extending a Segment With Session Data}{section.318.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {319}Using Analytics Cloud With User Segments}{1117}{chapter.319}\\protected@file@percent }\n\\newlabel{using-analytics-cloud-with-user-segments}{{319}{1117}{Using Analytics Cloud With User Segments}{chapter.319}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {319.1}Creating a New Segment}{1117}{section.319.1}\\protected@file@percent }\n\\newlabel{creating-a-new-segment}{{319.1}{1117}{Creating a New Segment}{section.319.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {319.2}Getting Segment Analytics}{1117}{section.319.2}\\protected@file@percent }\n\\newlabel{getting-segment-analytics}{{319.2}{1117}{Getting Segment Analytics}{section.319.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {319.1}{\\ignorespaces When you see Analytics Cloud Segments in the list of Segments, they are marked with the Analytics Cloud icon.}}{1118}{figure.319.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {320}Personalization Experience Management}{1119}{chapter.320}\\protected@file@percent }\n\\newlabel{personalization-experience-management}{{320}{1119}{Personalization Experience Management}{chapter.320}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {320.1}Managing Content Page Personalization}{1119}{section.320.1}\\protected@file@percent }\n\\newlabel{managing-content-page-personalization}{{320.1}{1119}{Managing Content Page Personalization}{section.320.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {320.1}{\\ignorespaces You can add, edit, delete, or change priority for Experiences.}}{1120}{figure.320.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {320.2}{\\ignorespaces You can add a new Segment while creating a new Experience.}}{1120}{figure.320.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {320.2}Managing Content Set Personalization}{1121}{section.320.2}\\protected@file@percent }\n\\newlabel{managing-content-set-personalization}{{320.2}{1121}{Managing Content Set Personalization}{section.320.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {320.3}{\\ignorespaces Select a Segment to create a variation for.}}{1121}{figure.320.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {320.3}Previewing User Experiences}{1121}{section.320.3}\\protected@file@percent }\n\\newlabel{previewing-user-experiences}{{320.3}{1121}{Previewing User Experiences}{section.320.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {320.4}{\\ignorespaces You can preview or delete a Personalized Variation from the \\emph  {Actions} menu.}}{1122}{figure.320.4}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {320.5}{\\ignorespaces You can preview different experiences from the Preview Panel.}}{1122}{figure.320.5}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {321}Content Page Personalization}{1123}{chapter.321}\\protected@file@percent }\n\\newlabel{content-page-personalization-1}{{321}{1123}{Content Page Personalization}{chapter.321}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {321.1}Creating the Default Page}{1123}{section.321.1}\\protected@file@percent }\n\\newlabel{creating-the-default-page}{{321.1}{1123}{Creating the Default Page}{section.321.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {321.1}{\\ignorespaces Open Layouts from the Section Builder.}}{1124}{figure.321.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {321.2}Defining Custom Experiences}{1124}{section.321.2}\\protected@file@percent }\n\\newlabel{defining-custom-experiences}{{321.2}{1124}{Defining Custom Experiences}{section.321.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {321.2}{\\ignorespaces Your final result might look something like this.}}{1125}{figure.321.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {321.3}{\\ignorespaces Click on the current experience to create a new one or select a different existing experience.}}{1125}{figure.321.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {321.4}{\\ignorespaces Your final result for the card prospects might look something like this.}}{1126}{figure.321.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {322}Content Set Personalization}{1127}{chapter.322}\\protected@file@percent }\n\\newlabel{content-set-personalization-2}{{322}{1127}{Content Set Personalization}{chapter.322}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {322.1}Creating and Setting the Default Content Set}{1127}{section.322.1}\\protected@file@percent }\n\\newlabel{creating-and-setting-the-default-content-set}{{322.1}{1127}{Creating and Setting the Default Content Set}{section.322.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {322.1}{\\ignorespaces Click \\emph  {Select} to add a new Asset Entries.}}{1128}{figure.322.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {322.2}Personalizing the Content Set}{1128}{section.322.2}\\protected@file@percent }\n\\newlabel{personalizing-the-content-set}{{322.2}{1128}{Personalizing the Content Set}{section.322.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {322.2}{\\ignorespaces Create a new Personalized Variation.}}{1128}{figure.322.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {323}Recommending Content Based on User Behavior}{1129}{chapter.323}\\protected@file@percent }\n\\newlabel{recommending-content-based-on-user-behavior}{{323}{1129}{Recommending Content Based on User Behavior}{chapter.323}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {323.1}{\\ignorespaces A user's interests are stored and accessible from Analytics Cloud.}}{1130}{figure.323.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {323.1}Adding Tags to Track User Behavior}{1130}{section.323.1}\\protected@file@percent }\n\\newlabel{adding-tags-to-track-user-behavior}{{323.1}{1130}{Adding Tags to Track User Behavior}{section.323.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {323.2}Displaying Content Based on User Behavior}{1131}{section.323.2}\\protected@file@percent }\n\\newlabel{displaying-content-based-on-user-behavior}{{323.2}{1131}{Displaying Content Based on User Behavior}{section.323.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {323.2}{\\ignorespaces Enable Content Recommendation for your Content Set.}}{1131}{figure.323.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {324}A/B Testing}{1133}{chapter.324}\\protected@file@percent }\n\\newlabel{ab-testing}{{324}{1133}{A/B Testing}{chapter.324}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {325}Enabling A/B Testing}{1135}{chapter.325}\\protected@file@percent }\n\\newlabel{enabling-ab-testing}{{325}{1135}{Enabling A/B Testing}{chapter.325}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {325.1}Setting A/B Testing Permissions}{1135}{section.325.1}\\protected@file@percent }\n\\newlabel{setting-ab-testing-permissions}{{325.1}{1135}{Setting A/B Testing Permissions}{section.325.1}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {326}Creating A/B Tests}{1137}{chapter.326}\\protected@file@percent }\n\\newlabel{creating-ab-tests}{{326}{1137}{Creating A/B Tests}{chapter.326}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {326.1}{\\ignorespaces Fill out the form to create your A/B test.}}{1138}{figure.326.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {326.2}{\\ignorespaces You now have an A/B test, but there are additional configurations you can apply.}}{1139}{figure.326.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {326.3}{\\ignorespaces Set the click target to be tracked.}}{1140}{figure.326.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {326.4}{\\ignorespaces Once the click target is set, you can run the A/B test.}}{1140}{figure.326.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {327}Running A/B Tests}{1141}{chapter.327}\\protected@file@percent }\n\\newlabel{running-ab-tests}{{327}{1141}{Running A/B Tests}{chapter.327}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {327.1}{\\ignorespaces Configure the final parameters of your A/B test before running it.}}{1142}{figure.327.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {328}Monitoring A/B Test Results}{1143}{chapter.328}\\protected@file@percent }\n\\newlabel{monitoring-ab-test-results}{{328}{1143}{Monitoring A/B Test Results}{chapter.328}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {329}Publishing A/B Test Variants}{1145}{chapter.329}\\protected@file@percent }\n\\newlabel{publishing-ab-test-variants}{{329}{1145}{Publishing A/B Test Variants}{chapter.329}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {329.1}{\\ignorespaces If you're satisfied with the A/B test's results, publish the winning Variant.}}{1146}{figure.329.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {329.2}{\\ignorespaces Once you've published a Variant, the A/B test is complete.}}{1147}{figure.329.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {330}Content Publication Management}{1149}{chapter.330}\\protected@file@percent }\n\\newlabel{content-publication-management}{{330}{1149}{Content Publication Management}{chapter.330}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {331}Staging}{1151}{chapter.331}\\protected@file@percent }\n\\newlabel{staging}{{331}{1151}{Staging}{chapter.331}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {332}Enabling Staging}{1153}{chapter.332}\\protected@file@percent }\n\\newlabel{enabling-staging}{{332}{1153}{Enabling Staging}{chapter.332}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {333}Enabling Local Live Staging}{1155}{chapter.333}\\protected@file@percent }\n\\newlabel{enabling-local-live-staging}{{333}{1155}{Enabling Local Live Staging}{chapter.333}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {334}Enabling Remote Live Staging}{1157}{chapter.334}\\protected@file@percent }\n\\newlabel{enabling-remote-live-staging}{{334}{1157}{Enabling Remote Live Staging}{chapter.334}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {334.1}{\\ignorespaces After your remote Liferay server and local Liferay server have been configured to communicate with each other, you have to specify a few Remote Live connection settings.}}{1158}{figure.334.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {335}Configuring Servers for Remote Live Staging}{1161}{chapter.335}\\protected@file@percent }\n\\newlabel{configuring-servers-for-remote-live-staging}{{335}{1161}{Configuring Servers for Remote Live Staging}{chapter.335}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {335.1}Applying Patches When Using Remote Staging}{1163}{section.335.1}\\protected@file@percent }\n\\newlabel{applying-patches-when-using-remote-staging}{{335.1}{1163}{Applying Patches When Using Remote Staging}{section.335.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {335.2}Configuring Remote Staging's Buffer Size}{1163}{section.335.2}\\protected@file@percent }\n\\newlabel{configuring-remote-stagings-buffer-size}{{335.2}{1163}{Configuring Remote Staging's Buffer Size}{section.335.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {336}Enabling Page Versioning and Staged Content}{1165}{chapter.336}\\protected@file@percent }\n\\newlabel{enabling-page-versioning-and-staged-content}{{336}{1165}{Enabling Page Versioning and Staged Content}{chapter.336}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {336.1}{\\ignorespaces You can decide to use versioning and choose what content should be staged.}}{1166}{figure.336.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {337}Publishing Staged Content Efficiently}{1169}{chapter.337}\\protected@file@percent }\n\\newlabel{publishing-staged-content-efficiently}{{337}{1169}{Publishing Staged Content Efficiently}{chapter.337}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {337.1}Understanding the Publication Process}{1169}{section.337.1}\\protected@file@percent }\n\\newlabel{understanding-the-publication-process}{{337.1}{1169}{Understanding the Publication Process}{section.337.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {337.2}Planning Ahead for Staging}{1170}{section.337.2}\\protected@file@percent }\n\\newlabel{planning-ahead-for-staging}{{337.2}{1170}{Planning Ahead for Staging}{section.337.2}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {338}Using the Staging Environment}{1171}{chapter.338}\\protected@file@percent }\n\\newlabel{using-the-staging-environment}{{338}{1171}{Using the Staging Environment}{chapter.338}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {338.1}{\\ignorespaces You can see the new staging options added to the top and left of your screen.}}{1171}{figure.338.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {339}Staging Content}{1173}{chapter.339}\\protected@file@percent }\n\\newlabel{staging-content}{{339}{1173}{Staging Content}{chapter.339}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {339.1}{\\ignorespaces The staging toolbar indicates whether you're able to publish to the live site.}}{1174}{figure.339.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {339.2}{\\ignorespaces The Simple Publication menu displays the changes since last publication and a way to name your publication.}}{1174}{figure.339.2}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {340}Advanced Publication with Staging}{1177}{chapter.340}\\protected@file@percent }\n\\newlabel{advanced-publication-with-staging}{{340}{1177}{Advanced Publication with Staging}{chapter.340}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {340.1}Date}{1177}{section.340.1}\\protected@file@percent }\n\\newlabel{date}{{340.1}{1177}{Date}{section.340.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {340.2}Pages}{1177}{section.340.2}\\protected@file@percent }\n\\newlabel{pages-1}{{340.2}{1177}{Pages}{section.340.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {340.1}{\\ignorespaces You have several ways to specify the pages you want included in your publication.}}{1178}{figure.340.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {340.3}Content}{1178}{section.340.3}\\protected@file@percent }\n\\newlabel{content}{{340.3}{1178}{Content}{section.340.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {340.4}Deletions}{1179}{section.340.4}\\protected@file@percent }\n\\newlabel{deletions}{{340.4}{1179}{Deletions}{section.340.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {340.5}Permissions}{1180}{section.340.5}\\protected@file@percent }\n\\newlabel{permissions}{{340.5}{1180}{Permissions}{section.340.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {341}Managing Content Types in Staging}{1181}{chapter.341}\\protected@file@percent }\n\\newlabel{managing-content-types-in-staging}{{341}{1181}{Managing Content Types in Staging}{chapter.341}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {341.1}{\\ignorespaces Click the \\emph  {Change} button for a content group to manage its specific content.}}{1181}{figure.341.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {341.1}Referenced Content}{1182}{section.341.1}\\protected@file@percent }\n\\newlabel{referenced-content}{{341.1}{1182}{Referenced Content}{section.341.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {341.2}Version History}{1182}{section.341.2}\\protected@file@percent }\n\\newlabel{version-history}{{341.2}{1182}{Version History}{section.341.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {341.3}Previews and Thumbnails}{1182}{section.341.3}\\protected@file@percent }\n\\newlabel{previews-and-thumbnails}{{341.3}{1182}{Previews and Thumbnails}{section.341.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {341.4}Vocabularies}{1183}{section.341.4}\\protected@file@percent }\n\\newlabel{vocabularies}{{341.4}{1183}{Vocabularies}{section.341.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {341.5}Deletions}{1183}{section.341.5}\\protected@file@percent }\n\\newlabel{deletions-1}{{341.5}{1183}{Deletions}{section.341.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {342}Staging Processes and Templates}{1185}{chapter.342}\\protected@file@percent }\n\\newlabel{staging-processes-and-templates}{{342}{1185}{Staging Processes and Templates}{chapter.342}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {342.1}{\\ignorespaces Your staging processes can be viewed at any time.}}{1185}{figure.342.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {343}Disabling Staging}{1187}{chapter.343}\\protected@file@percent }\n\\newlabel{disabling-staging}{{343}{1187}{Disabling Staging}{chapter.343}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {343.1}Disabling Local Live Staging}{1187}{section.343.1}\\protected@file@percent }\n\\newlabel{disabling-local-live-staging}{{343.1}{1187}{Disabling Local Live Staging}{section.343.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {343.2}Disabling Remote Live Staging}{1187}{section.343.2}\\protected@file@percent }\n\\newlabel{disabling-remote-live-staging}{{343.2}{1187}{Disabling Remote Live Staging}{section.343.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {343.3}Steps to Disable Staging}{1188}{section.343.3}\\protected@file@percent }\n\\newlabel{steps-to-disable-staging}{{343.3}{1188}{Steps to Disable Staging}{section.343.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {344}Publishing Single Assets From a Staged Site}{1189}{chapter.344}\\protected@file@percent }\n\\newlabel{publishing-single-assets-from-a-staged-site}{{344}{1189}{Publishing Single Assets From a Staged Site}{chapter.344}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {344.1}{\\ignorespaces You can publish the single web content article to the live site.}}{1190}{figure.344.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {345}Organizing Pages for Staging}{1193}{chapter.345}\\protected@file@percent }\n\\newlabel{organizing-pages-for-staging}{{345}{1193}{Organizing Pages for Staging}{chapter.345}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {346}Using Multi and Single Page Variations}{1195}{chapter.346}\\protected@file@percent }\n\\newlabel{using-multi-and-single-page-variations}{{346}{1195}{Using Multi and Single Page Variations}{chapter.346}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {346.1}Enabling Page Versioning}{1196}{section.346.1}\\protected@file@percent }\n\\newlabel{enabling-page-versioning}{{346.1}{1196}{Enabling Page Versioning}{section.346.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {346.1}{\\ignorespaces You can enable page versioning for public and/or private pages.}}{1196}{figure.346.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {346.2}Using Site Pages Variations}{1196}{section.346.2}\\protected@file@percent }\n\\newlabel{using-site-pages-variations}{{346.2}{1196}{Using Site Pages Variations}{section.346.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {346.2}{\\ignorespaces Select the \\emph  {Enable} button to create a missing page in the current Site pages variation.}}{1197}{figure.346.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {346.3}Using Page Variations}{1197}{section.346.3}\\protected@file@percent }\n\\newlabel{using-page-variations}{{346.3}{1197}{Using Page Variations}{section.346.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {346.4}Managing Variation Permissions}{1198}{section.346.4}\\protected@file@percent }\n\\newlabel{managing-variation-permissions}{{346.4}{1198}{Managing Variation Permissions}{section.346.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {346.3}{\\ignorespaces Configure the roles that can access and modify your variation.}}{1198}{figure.346.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {347}Creating Multi and Single Page Variations}{1199}{chapter.347}\\protected@file@percent }\n\\newlabel{creating-multi-and-single-page-variations}{{347}{1199}{Creating Multi and Single Page Variations}{chapter.347}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {347.1}{\\ignorespaces When selecting the \\emph  {Site Pages Variation} link from the Staging Bar, you're able to add and manage your Site pages variations.}}{1199}{figure.347.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {348}Merging Site Pages Variations}{1201}{chapter.348}\\protected@file@percent }\n\\newlabel{merging-site-pages-variations}{{348}{1201}{Merging Site Pages Variations}{chapter.348}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {348.1}{\\ignorespaces Select the site pages variation you'd like to merge with your base variation.}}{1201}{figure.348.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {349}Managing Permissions}{1203}{chapter.349}\\protected@file@percent }\n\\newlabel{managing-permissions}{{349}{1203}{Managing Permissions}{chapter.349}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {350}Scheduling Web Content Publication}{1205}{chapter.350}\\protected@file@percent }\n\\newlabel{scheduling-web-content-publication}{{350}{1205}{Scheduling Web Content Publication}{chapter.350}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {350.1}{\\ignorespaces The web content scheduler can be easily accessed from the right panel of the page.}}{1205}{figure.350.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {351}Managing Apps}{1207}{chapter.351}\\protected@file@percent }\n\\newlabel{managing-apps}{{351}{1207}{Managing Apps}{chapter.351}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {352}Managing and Configuring Apps}{1209}{chapter.352}\\protected@file@percent }\n\\newlabel{managing-and-configuring-apps}{{352}{1209}{Managing and Configuring Apps}{chapter.352}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {352.1}Managing Apps in Production}{1209}{section.352.1}\\protected@file@percent }\n\\newlabel{managing-apps-in-production}{{352.1}{1209}{Managing Apps in Production}{section.352.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {352.2}Using the App Manager}{1210}{section.352.2}\\protected@file@percent }\n\\newlabel{using-the-app-manager}{{352.2}{1210}{Using the App Manager}{section.352.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {352.1}{\\ignorespaces The App Manager lets you manage the apps, modules, and components installed in your Liferay DXP instance.}}{1210}{figure.352.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {352.3}Using the Components Listing}{1211}{section.352.3}\\protected@file@percent }\n\\newlabel{using-the-components-listing}{{352.3}{1211}{Using the Components Listing}{section.352.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {352.2}{\\ignorespaces The components listing lets you manage the portlets, themes, and layout templates installed in your Liferay DXP instance.}}{1211}{figure.352.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {352.3}{\\ignorespaces You can activate or deactivate a component, and change its permissions.}}{1212}{figure.352.3}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {353}Using the Liferay Marketplace}{1213}{chapter.353}\\protected@file@percent }\n\\newlabel{using-the-liferay-marketplace}{{353}{1213}{Using the Liferay Marketplace}{chapter.353}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {353.1}Finding and Purchasing Apps}{1213}{section.353.1}\\protected@file@percent }\n\\newlabel{finding-and-purchasing-apps}{{353.1}{1213}{Finding and Purchasing Apps}{section.353.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {353.1}{\\ignorespaces The Liferay Marketplace home page lets you browse and search for apps.}}{1214}{figure.353.1}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {353.2}{\\ignorespaces Click an app to view its details.}}{1215}{figure.353.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {353.2}Managing Purchased Apps}{1215}{section.353.2}\\protected@file@percent }\n\\newlabel{managing-purchased-apps}{{353.2}{1215}{Managing Purchased Apps}{section.353.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {353.3}{\\ignorespaces You can manage your purchased apps from your liferay.com account's home page.}}{1216}{figure.353.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {353.4}{\\ignorespaces You can also manage your purchased apps from within a running Liferay instance.}}{1216}{figure.353.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {353.3}Renewing a Purchased App}{1217}{section.353.3}\\protected@file@percent }\n\\newlabel{renewing-a-purchased-app}{{353.3}{1217}{Renewing a Purchased App}{section.353.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {354}Installing Apps Manually}{1219}{chapter.354}\\protected@file@percent }\n\\newlabel{installing-apps-manually}{{354}{1219}{Installing Apps Manually}{chapter.354}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {354.1}Using the Control Panel to Install Apps}{1219}{section.354.1}\\protected@file@percent }\n\\newlabel{using-the-control-panel-to-install-apps}{{354.1}{1219}{Using the Control Panel to Install Apps}{section.354.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {354.2}Using Your File System to Install Apps}{1220}{section.354.2}\\protected@file@percent }\n\\newlabel{using-your-file-system-to-install-apps}{{354.2}{1220}{Using Your File System to Install Apps}{section.354.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {354.3}Manually Deploying an LPKG App}{1220}{section.354.3}\\protected@file@percent }\n\\newlabel{manually-deploying-an-lpkg-app}{{354.3}{1220}{Manually Deploying an LPKG App}{section.354.3}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {355}App Types}{1221}{chapter.355}\\protected@file@percent }\n\\newlabel{app-types}{{355}{1221}{App Types}{chapter.355}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {355.1}OSGi Modules}{1221}{section.355.1}\\protected@file@percent }\n\\newlabel{osgi-modules}{{355.1}{1221}{OSGi Modules}{section.355.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {355.2}Portlets}{1222}{section.355.2}\\protected@file@percent }\n\\newlabel{portlets}{{355.2}{1222}{Portlets}{section.355.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {355.3}Web Plugins}{1222}{section.355.3}\\protected@file@percent }\n\\newlabel{web-plugins}{{355.3}{1222}{Web Plugins}{section.355.3}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {355.4}Templates and Themes}{1222}{section.355.4}\\protected@file@percent }\n\\newlabel{templates-and-themes}{{355.4}{1222}{Templates and Themes}{section.355.4}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {355.5}Liferay Marketplace App Packages}{1223}{section.355.5}\\protected@file@percent }\n\\newlabel{liferay-marketplace-app-packages}{{355.5}{1223}{Liferay Marketplace App Packages}{section.355.5}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {356}Blacklisting OSGi Bundles and Components}{1225}{chapter.356}\\protected@file@percent }\n\\newlabel{blacklisting-osgi-bundles-and-components}{{356}{1225}{Blacklisting OSGi Bundles and Components}{chapter.356}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {356.1}Blacklisting Bundles}{1225}{section.356.1}\\protected@file@percent }\n\\newlabel{blacklisting-bundles}{{356.1}{1225}{Blacklisting Bundles}{section.356.1}{}}\n\\gdef \\LT@ii {\\LT@entry \n    {1}{196.74754pt}\\LT@entry \n    {1}{273.00746pt}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {356.1}{\\ignorespaces This blacklist uninstalls the \\texttt  {com.liferay.docs.greeting.api} bundle, Liferay Marketplace LPKG, and \\texttt  {classic-theme} WAR.}}{1226}{figure.356.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {356.2}Reinstalling Blacklisted Bundles}{1226}{section.356.2}\\protected@file@percent }\n\\newlabel{reinstalling-blacklisted-bundles}{{356.2}{1226}{Reinstalling Blacklisted Bundles}{section.356.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {356.3}Blacklisting Components}{1227}{section.356.3}\\protected@file@percent }\n\\newlabel{blacklisting-components}{{356.3}{1227}{Blacklisting Components}{section.356.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {356.2}{\\ignorespaces This blacklist disables the components \\texttt  {com.liferay.portal.security.ldap.internal.authenticator.LDAPAuth} and \\texttt  {com.liferay.ip.geocoder.sample.web.internal.portlet.IPGeocoderSamplePortlet}.}}{1228}{figure.356.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {356.4}Re-enabling Blacklisted Components}{1228}{section.356.4}\\protected@file@percent }\n\\newlabel{re-enabling-blacklisted-components}{{356.4}{1228}{Re-enabling Blacklisted Components}{section.356.4}{}}\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {357}Polls}{1229}{chapter.357}\\protected@file@percent }\n\\newlabel{polls}{{357}{1229}{Polls}{chapter.357}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {357.1}Creating a Poll}{1229}{section.357.1}\\protected@file@percent }\n\\newlabel{creating-a-poll}{{357.1}{1229}{Creating a Poll}{section.357.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {357.1}{\\ignorespaces Besides the Title and the Polls Question, you must enter data for each of the Choices fields when creating a new poll.}}{1230}{figure.357.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {357.2}Adding a Poll to a Page}{1230}{section.357.2}\\protected@file@percent }\n\\newlabel{adding-a-poll-to-a-page}{{357.2}{1230}{Adding a Poll to a Page}{section.357.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {357.2}{\\ignorespaces These buttons provide shortcuts to the widget's configuration, as well as to some of the Polls Application's functionality.}}{1231}{figure.357.2}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {357.3}Viewing Poll Results}{1231}{section.357.3}\\protected@file@percent }\n\\newlabel{viewing-poll-results}{{357.3}{1231}{Viewing Poll Results}{section.357.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {357.3}{\\ignorespaces Selecting a poll in the Polls portlet puts the data at your fingertips.}}{1232}{figure.357.3}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {357.4}{\\ignorespaces This is what the vertical bar graph for the Lunar Resort poll results looks like.}}{1233}{figure.357.4}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {358}Using the Calendar}{1235}{chapter.358}\\protected@file@percent }\n\\newlabel{using-the-calendar}{{358}{1235}{Using the Calendar}{chapter.358}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {358.1}Configuring the Calendar}{1235}{section.358.1}\\protected@file@percent }\n\\newlabel{configuring-the-calendar}{{358.1}{1235}{Configuring the Calendar}{section.358.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {358.1}{\\ignorespaces The Setup → User Settings tab provides the options you need to get started quickly.}}{1236}{figure.358.1}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {359}Using the Calendar Widget}{1239}{chapter.359}\\protected@file@percent }\n\\newlabel{using-the-calendar-widget}{{359}{1239}{Using the Calendar Widget}{chapter.359}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {359.1}{\\ignorespaces The default view is set in configuration, but a user can change it at any time.}}{1239}{figure.359.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {359.1}Adding New Calendars}{1240}{section.359.1}\\protected@file@percent }\n\\newlabel{adding-new-calendars}{{359.1}{1240}{Adding New Calendars}{section.359.1}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {359.2}Adding Events to a Calendar}{1240}{section.359.2}\\protected@file@percent }\n\\newlabel{adding-events-to-a-calendar}{{359.2}{1240}{Adding Events to a Calendar}{section.359.2}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {359.3}Additional Event Functions}{1240}{section.359.3}\\protected@file@percent }\n\\newlabel{additional-event-functions}{{359.3}{1240}{Additional Event Functions}{section.359.3}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {359.2}{\\ignorespaces Personal and Site calendars are shown in the lower left. This image shows calendars belonging to User \\emph  {Test Test} and Site \\emph  {Liferay DXP}.}}{1241}{figure.359.2}\\protected@file@percent }\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {359.3}{\\ignorespaces When you click anywhere on the calendar, you'll see the event creation pop up appear. Click \\emph  {Edit} to specify details for your event.}}{1242}{figure.359.3}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{Details}{1242}{section.359.3}\\protected@file@percent }\n\\newlabel{details-2}{{359.3}{1242}{Details}{section.359.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Invitations}{1242}{section.359.3}\\protected@file@percent }\n\\newlabel{invitations}{{359.3}{1242}{Invitations}{section.359.3}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Reminders}{1242}{Item.1549}\\protected@file@percent }\n\\newlabel{reminders}{{359.3}{1242}{Reminders}{Item.1549}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Categorization}{1242}{Item.1549}\\protected@file@percent }\n\\newlabel{categorization-2}{{359.3}{1242}{Categorization}{Item.1549}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {359.4}{\\ignorespaces You can specify event details such as the event title, start date, end date, description, location, and more.}}{1243}{figure.359.4}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {subsection}{Related Assets}{1243}{Item.1549}\\protected@file@percent }\n\\newlabel{related-assets}{{359.3}{1243}{Related Assets}{Item.1549}{}}\n\\@writefile{toc}{\\contentsline {subsection}{Saving and Drafting Changes and Updating Permissions}{1243}{Item.1549}\\protected@file@percent }\n\\newlabel{saving-and-drafting-changes-and-updating-permissions}{{359.3}{1243}{Saving and Drafting Changes and Updating Permissions}{Item.1549}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {359.5}{\\ignorespaces The \\emph  {Repeat} box allows you to specify whether an events repeats daily, weekly, monthly, or yearly, how often it repeats, and when (or if) it ends.}}{1244}{figure.359.5}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {359.4}Customizing Email Notifications}{1245}{section.359.4}\\protected@file@percent }\n\\newlabel{customizing-email-notifications}{{359.4}{1245}{Customizing Email Notifications}{section.359.4}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {359.6}{\\ignorespaces Email templates apply to a single calendar and all its events.}}{1246}{figure.359.6}\\protected@file@percent }\n\\@writefile{lof}{\\addvspace {10pt}}\n\\@writefile{lot}{\\addvspace {10pt}}\n\\@writefile{toc}{\\contentsline {chapter}{\\chapternumberline {360}Calendar Resources and Porting}{1247}{chapter.360}\\protected@file@percent }\n\\newlabel{calendar-resources-and-porting}{{360}{1247}{Calendar Resources and Porting}{chapter.360}{}}\n\\@writefile{toc}{\\contentsline {section}{\\numberline {360.1}Calendar Resources}{1247}{section.360.1}\\protected@file@percent }\n\\newlabel{calendar-resources}{{360.1}{1247}{Calendar Resources}{section.360.1}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {360.1}{\\ignorespaces Resources are accessed from the tab menu at the top of the widget.}}{1247}{figure.360.1}\\protected@file@percent }\n\\@writefile{toc}{\\contentsline {section}{\\numberline {360.2}Exporting and Importing Calendar Data}{1248}{section.360.2}\\protected@file@percent }\n\\newlabel{exporting-and-importing-calendar-data}{{360.2}{1248}{Exporting and Importing Calendar Data}{section.360.2}{}}\n\\@writefile{lof}{\\contentsline {figure}{\\numberline {360.2}{\\ignorespaces This LAR is ready to be downloaded.}}{1248}{figure.360.2}\\protected@file@percent }\n\\@setckpt{user/user}{\n\\setcounter{page}{1250}\n\\setcounter{equation}{0}\n\\setcounter{enumi}{7}\n\\setcounter{enumii}{0}\n\\setcounter{enumiii}{0}\n\\setcounter{enumiv}{0}\n\\setcounter{footnote}{0}\n\\setcounter{mpfootnote}{0}\n\\setcounter{@memmarkcntra}{0}\n\\setcounter{storedpagenumber}{1}\n\\setcounter{book}{0}\n\\setcounter{part}{1}\n\\setcounter{chapter}{360}\n\\setcounter{section}{2}\n\\setcounter{subsection}{0}\n\\setcounter{subsubsection}{0}\n\\setcounter{paragraph}{0}\n\\setcounter{subparagraph}{0}\n\\setcounter{@ppsavesec}{0}\n\\setcounter{@ppsaveapp}{0}\n\\setcounter{vslineno}{0}\n\\setcounter{poemline}{0}\n\\setcounter{modulo@vs}{0}\n\\setcounter{memfvsline}{0}\n\\setcounter{verse}{0}\n\\setcounter{chrsinstr}{0}\n\\setcounter{poem}{0}\n\\setcounter{newflo@tctr}{4}\n\\setcounter{@contsubnum}{0}\n\\setcounter{section@level}{0}\n\\setcounter{maxsecnumdepth}{1}\n\\setcounter{sidefootnote}{0}\n\\setcounter{pagenote}{0}\n\\setcounter{pagenoteshadow}{0}\n\\setcounter{memfbvline}{0}\n\\setcounter{bvlinectr}{0}\n\\setcounter{cp@cntr}{0}\n\\setcounter{ism@mctr}{0}\n\\setcounter{xsm@mctr}{0}\n\\setcounter{csm@mctr}{0}\n\\setcounter{ksm@mctr}{0}\n\\setcounter{xksm@mctr}{0}\n\\setcounter{cksm@mctr}{0}\n\\setcounter{msm@mctr}{0}\n\\setcounter{xmsm@mctr}{0}\n\\setcounter{cmsm@mctr}{0}\n\\setcounter{bsm@mctr}{0}\n\\setcounter{workm@mctr}{0}\n\\setcounter{sheetsequence}{1298}\n\\setcounter{lastsheet}{1795}\n\\setcounter{lastpage}{1747}\n\\setcounter{figure}{2}\n\\setcounter{lofdepth}{1}\n\\setcounter{table}{0}\n\\setcounter{lotdepth}{1}\n\\setcounter{Item}{1566}\n\\setcounter{Hfootnote}{5}\n\\setcounter{bookmark@seq@number}{0}\n\\setcounter{memhycontfloat}{0}\n\\setcounter{mem@Hpagenote}{0}\n\\setcounter{r@tfl@t}{0}\n\\setcounter{float@type}{4}\n\\setcounter{LT@tables}{2}\n\\setcounter{LT@chunks}{3}\n\\setcounter{@anim@ltxcnt}{0}\n\\setcounter{parentequation}{0}\n\\setcounter{FancyVerbLine}{0}\n}\n"
  },
  {
    "path": "book/user/user.tex",
    "content": "\\chapter{The Liferay Distinction}\\label{the-liferay-distinction}\n\nYour web presence is a big deal, and the software platform that runs it\nmust be up to the task. You must be able to update your site easily, to\nprovide your partners with the services they need, and to enable your\nusers and customers to interact with you. It shouldn't be complicated:\nin today's cloud-based environment, you should be able to make changes\nwith a click of a button and dynamically allocate nodes whenever you\nneed them.\n\nLiferay DXP offers all of this and more. It is a mature, stable, open\nsource platform with a strong heritage of serving some of the biggest\nsites in the world---and some of the smallest, too.\n\nThe documentation here shows you how to use it. This is the\n\\textbf{User} section, which describes the features of an installed\nLiferay DXP, how to configure its applications, and how to build your\nwebsite.\n\nThe \\textbf{Developer} section is divided into five subsections:\n\n\\href{/docs/7-2/appdev/-/knowledge_base/a/application-development}{\\textbf{Application\nDevelopment}} shows you how you can build applications using industry\nstandard tools and frameworks on Liferay DXP.\n\n\\href{/docs/7-2/tutorials/-/knowledge_base/t/developer-tutorials}{\\textbf{Tutorials}}\nlead you step-by-step through specific tasks, such as developing web\napplications, upgrading old applications, creating themes, and more.\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/frameworks}{\\textbf{Frameworks}}\nshow you all the Liferay APIs and development frameworks you can use in\nyour applications to streamline development.\n\n\\href{/docs/7-2/customization/-/knowledge_base/c/liferay-customization}{\\textbf{Customization}}\nexplains the myriad of ways Liferay DXP can be customized to your exact\nspecifications.\n\n\\href{/docs/7-2/reference/-/knowledge_base/r/developer-reference}{\\textbf{Reference}}\nis a collection of material showing developers the options available for\nvarious APIs.\n\nThe\n\\href{https://learn.liferay.com/dxp/latest/en/installation-and-upgrades.html}{\\textbf{Installation\nand Upgrades}} section shows administrators how to obtain, install, and\nconfigure Liferay DXP on bare metal, virtualized environments, and the\ncloud.\n\nIf you're coming from an older version, read on to learn what's new in\n7.2.\n\n\\chapter{What's New in Liferay 7.2!}\\label{whats-new-in-liferay-7.2}\n\nThe latest version of Liferay DXP delivers powerful tools to help\nbusinesses create and personalize any experience across their solutions,\nleverage the flexibility of a decoupled CMS architecture, and streamline\nbusiness operations.\n\nLiferay DXP meets the needs of today's digital-first business teams to\ncreate experiences rapidly across channels. It equips enterprises with a\nwide variety of easy-to-use applications and tools to build tailored\nsolutions and experiences on a flexible platform.\n\nFor a full feature list, please read the\n\\href{https://www.liferay.com/resource?folderId=3292406&title=Liferay+DXP+7.2+Features+Overview&utm_source=whitepaper&utm_medium=content&utm_content=liferay\\%20dxp\\%207.2\\%20new\\%20features\\%20summary}{\\emph{Liferay\nDXP 7.2 Features Overview}} or contact sales@liferay.com.\n\n\\section{Key New Features}\\label{key-new-features}\n\nEnsure your business evolves and stays relevant with new features\ndesigned to support great customer experiences across all stages of the\ncustomer journey.\n\n\\section{Experience Creation}\\label{experience-creation}\n\nGreat digital experiences are imperative in today's competition for new\nbusiness. Companies also need to make sure they stay relevant after the\nsale, investing in long-term customer relationships that cultivate\nloyalty and repeat business. Here are some new features to help you\nensure you're creating and delivering excellent experiences all the\ntime.\n\n\\subsection{Content Authoring}\\label{content-authoring}\n\n7.0 evolves our content authoring and management capabilities\nsignificantly, making it even easier and more intuitive for the\nnon-technical content creator to create and manage engaging content.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/build-pages.png}\n\\caption{Content authors can build pages out of building blocks called\n\\emph{Fragments}.}\n\\end{figure}\n\n\\subsection{Site Building}\\label{site-building}\n\nContent pages now allow users to create experiences that have both\napplications and content on the same page, giving businesses increased\nflexibility to create experiences tailored to fit the needs of different\nend users. More tools have been provided to allow for richer layouts and\nfunctionality for the non-technical user to build a visually appealing\nsite experience easily.\n\n\\subsection{Fragments}\\label{fragments}\n\nThis release expands on the functionality available to create fragments,\nwhich previously required developer involvement. For marketers looking\nto create a fragment for their use case, we provide new functionality\nthat allows a non-technical user to create simple fragments easily\nthrough the page editor itself by dragging and dropping out-of-the-box\ncomponents into a container.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragments-pages.png}\n\\caption{Fragments make it easy to build pages.}\n\\end{figure}\n\n\\subsection{Fragments Toolkit/CLI}\\label{fragments-toolkitcli}\n\nImprovements to the web developer's experience has also been made for\n7.0. A set of front-end toolkits---including a CLI tool---is provided so\nweb developers can write fragments in their own code editors and\nimport/export them without needing to redeploy.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragments-toolkit.png}\n\\caption{Use the Fragments Editor or download the toolkit and use your\nown tools.}\n\\end{figure}\n\n\\subsection{In-Context Editing and Content\nPreviews}\\label{in-context-editing-and-content-previews}\n\nAn improved site building interface shows users how different content\nwould appear to different visitors. Liferay DXP allows content creators\nto preview content in context of how it would look like on a live site.\n\n\\subsection{Content Usages}\\label{content-usages}\n\nFor structured content that can be mapped and delivered across multiple\nlocations, a new feature has been introduced to allow the content\ncreator to view where specific pieces of web content are being used and\nreused across their channels.\n\n\\subsection{A/B Testing (DXP only)}\\label{ab-testing-dxp-only}\n\nContent creators can use A/B testing to create and customize tests to\nevaluate which elements on Content Pages perform better and edit content\naccordingly. Liferay DXP's A/B tests leverage Bayesian statistics to\nidentify the probable values of lift for a variant, allowing your\nbusiness to make more informed decisions. Native integration with\nLiferay Analytics Cloud allows for data collected for the running test\nto determine a winning variant.\n\n\\section{Personalization}\\label{personalization}\n\nIn 7.0, segmentation and personalization capabilities have been moved\ninto the core product. This allows for a more seamless integration of\ncontent creation and personalization functionality and helps streamline\nthe process of creating segments while creating personalized\nexperiences.\n\n\\subsection{Session Rules}\\label{session-rules}\n\nThere are many different pieces of information available to track a web\nvisitor. Session rules help further identify their distinct digital\nfingerprint per visit and is something that marketers can leverage to\nidentify who is a repeat visitor and who is a new visitor.\n\n\\subsection{Rule Builder}\\label{rule-builder}\n\nBeing able to sort intelligently through vast amounts of data and\nassociated individuals can be tedious; this process should be easy and\nnot have to be curated manually. Marketers should instead be able to\nbuild lists of individuals based on a specific set of criteria using\nrules, leading to time savings and efficiency. The rule builder helps\nenable more effective targeting by reducing the amount of manual work\nneeded to create a specific list of people who fall under certain pieces\nof criteria.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/rule-builder.png}\n\\caption{The Rule Builder provides a drag-and-drop interface that helps\nyou build exactly the criteria you need to target the right information\nto the right users.}\n\\end{figure}\n\n\\subsection{Content Sets}\\label{content-sets}\n\nContent sets help address the use case of personalizing a feed of\ncontent for different groups of people. Users can easily target and\npersonalize multiple pieces of content in a fragment or widget and have\nit display accordingly for the visitor.\n\n\\subsection{Experiences}\\label{experiences}\n\nThe new Experiences feature allows a user to create different variations\nof a page. All the tools available for creating a page in the layout\neditor are also available for making these page variations.\nNon-technical users can easily tailor the messaging, images and even\nwidgets on a page.\n\n\\subsection{Content Recommendations (DXP\nonly)}\\label{content-recommendations-dxp-only}\n\nContent recommendations leverages interest models generated in Liferay\nAnalytics Cloud to recommend content on Liferay DXP. The option to do so\nis available through Content Sets and automatically filters content\nbased on interest keywords. Liferay Analytics Cloud uses AI to cluster\ntopics and model long-term interest for known and unknown visitors.\nKeywords are taken from the categories, tags, and keywords of content.\n\n\\section{Bulk Management}\\label{bulk-management}\n\nEnterprises regularly manage large volumes of content and work that gets\ngenerated on their digital properties. The need to manage and accurately\ncategorize these assets can be overwhelming, if not impossible, to\nmaintain and use. Bulk management features provided in 7.0 help reduce\nthe amount of manual labor through automation and tools that perform\nbulk actions.\n\n\\subsection{Auto-Tagging}\\label{auto-tagging}\n\nUsers can automatically assign the correct metadata for images,\ndocuments and web content. Having rich metadata assigned to the proper\ncontent helps to not only reduce the amount of manual, tedious work, but\nalso improves the searchability of assets. The resulting metadata that\naccumulates helps establish the foundation needed for content\npersonalization efforts and content automation. An Auto-Tagging API lets\ndevelopers extend the functionality to tag any asset with any selected\nauto-tagging service.\n\n\\subsection{Bulk Operations}\\label{bulk-operations}\n\nImprovements have been made to make the experience of managing tags,\ncategories, and file operations much easier at scale.\n\n\\subsection{Automatic Document Versioning\nPolicies}\\label{automatic-document-versioning-policies}\n\nRefinements to how documents can be versioned are provided in 7.2. The\nfunctionality for document versioning has been expanded to let users\ndefine their own versioning policies and apply them to Liferay DXP. This\nis in addition to the existing default versioning system Liferay DXP\nprovides out-of-the-box.\n\n\\section{Business Operations}\\label{business-operations}\n\nDigital enterprises today must possess efficient back office\norganization to support the delivery of great customer experiences. It\nis crucial to fulfill these customer interactions and ensure those\nexperiences are delivered and supported in a timely manner. Achieving\nthis kind of efficiency, for a great customer experience, requires\ninvolvement from different departments and stakeholders and affects a\nlot of disparate content and processes. 7.0 helps enterprises navigate\nthese problems with features to help support streamlining and optimizing\nthose processes and operations.\n\n\\subsection{Forms API}\\label{forms-api}\n\nReduce the time it takes for IT to deliver custom applications for the\nbusiness. Automate data collection through applications.\n\n\\subsection{Workflow Metrics}\\label{workflow-metrics}\n\nWorkflow metrics helps users gain insight into how long certain workflow\nevents take to complete. Users can set deadlines on a workflow process's\nevents; these configurations are called Service Level Agreements (SLAs).\nOnce defined, use Workflow Reports to measure compliance with the SLAs.\nThe analytics and metrics generated can help you understand the\nthroughput performance of your processes in a given timeframe, allowing\nusers to better optimize their processes.\n\n\\subsection{Workflow Reports}\\label{workflow-reports}\n\nCut down the time it takes to serve your customers. New reports for\nworkflow processes help users identify operational bottlenecks and work\nefficiently to increase revenue goals.\n\n\\subsection{Online Document Editing}\\label{online-document-editing}\n\nIntegration with Office 365 and Google Docs allows the creation and\nediting of documents stored in Liferay DXP. Users can manage documents,\npresentations, and spreadsheets with the power that the suite provides\nand store them in Liferay DXP's document repository for future access.\nThis feature takes advantage of the existing permissions system,\nversioning, and sharing capabilities already included with 7.0.\n\n\\subsection{P2P Asset Sharing}\\label{p2p-asset-sharing}\n\nIn addition to being able to create and edit documents easily, users can\nnow comment and share documents with other registered users. This allows\nfor faster coordination without the need of assigning custom\npermissions.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/share-assets.png}\n\\caption{You can share assets with other users easily.}\n\\end{figure}\n\n\\subsection{Improved Identity Management\nTools}\\label{improved-identity-management-tools}\n\nNew improvements have also been made to help tackle tedious tasks of\nhandling personal data erasure requests. Logical hierarchies for assets\nare now in place to help simplify bulk deletion and anonymization during\nthe review process. Filtering and scoping capabilities across Liferay\nDXP help provide greater context when reviewing personal data, and\nadministrators can be informed when specific applications will delete or\nanonymize data during the auto-anonymization process. All of these\nimprovements help ensure compliance with GDPR and other data protection\nlaws.\n\n\\section{Headless Capabilities}\\label{headless-capabilities}\n\nGive your developers the freedom to create any presentation layer and\ncontent creators the ease of traditional content management tools. 7.0\nprovides a decoupled architecture to allow for a range of approaches\ntowards content management and delivery. Enterprises can choose a\ntraditional CMS or experience management approach, a full headless\napproach or a hybrid approach.\n\n\\subsection{OpenAPI}\\label{openapi}\n\nLiferay's API layer supports the\n\\href{https://swagger.io/resources/open-api/}{OpenAPI} specification,\nthe most popular open source framework for REST APIs. This allows for\ngreater flexibility, security and ease of integration as the OpenAPI\nspecification is language-agnostic, allows for security audits, and\nclients can understand and consume services without knowledge of server\nimplementation or access to the server code.\n\n\\subsection{Headless CMS}\\label{headless-cms}\n\nEnable developers to manage web content headlessly. Headless\ncapabilities in this version are also provided to help enable\nomnichannel experiences from a single source of data. Content delivery\nAPIs allow for developers to deliver richer, faster and more responsive\nuser experiences, no matter the device. Front-end developers can\nleverage their native tools and frameworks they're already familiar with\nto build sites. These APIs provide access to a variety of assets and\ncontent in Liferay DXP.\n\nContent participation APIs are also provided to support user\ninteractions with comments and ratings for web content and assets.\n\n\\section{Developer Experience}\\label{developer-experience}\n\nIn addition to the new headless capabilities that are provided on 7.0,\nimprovements to developer tools and the developer experience has been\nmade. These tools help developers accelerate time to market and tailor\nthe platform for your business needs.\n\n\\subsection{Upgrade Tool}\\label{upgrade-tool}\n\nA revamped Upgrade Tool simplifies the upgrade process from previous\nversions of Liferay DXP. This all-in-one tool allows system\nadministrators to reduce the time spent troubleshooting by helping them\nplan their upgrade process for database migrations, checking properties,\nand restarting failed upgrades.\n\n\\subsection{Front-end Toolkits}\\label{front-end-toolkits}\n\nFront-end toolkits allow developers to create an application using their\nfavorite JavaScript framework. The toolkit resolves any conflicting\nJavaScript packages and bundles the application properly to deploy it\ninto Liferay DXP seamlessly.\n\n\\subsection{Struts Removed}\\label{struts-removed}\n\nThough Liferay DXP never used a version of Struts with security\nvulnerabilities, the Struts library has been removed, and it is no\nlonger a part of the product.\n\n\\section{What's Next}\\label{whats-next}\n\nLearn more about how Liferay can help your business take the next step\nin your digital strategy. Request a demo from one of our team members at\n\\href{https://www.liferay.com/request-a-demo?utm_source=whitepaper\\%20&utm_medium=content&utm_content=liferay\\%20dxp\\%207.2\\%20new\\%20features\\%20summary}{\\emph{liferay.com/request-a-demo}}.\n\n\\chapter{Setting Up}\\label{setting-up}\n\nIf Liferay DXP is anything, it's configurable. As the core is shrinking\ndue to its increased modularity, it's important that all the\napplications in Liferay are also configurable.\n\nBreaking it down, three types of applications must be configurable:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  The platform itself\n\\item\n  Liferay's out-of-the-box applications\n\\item\n  Custom applications\n\\end{enumerate}\n\nTo this end, Liferay's engineers have made the platform and its\napplications configurable, and created a mechanism for developers to\nmake their\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/configurable-applications}{applications\nconfigurable}.\n\n\\chapter{Where Configuration Happens}\\label{where-configuration-happens}\n\nLiferay's configuration takes place in the following places:\n\n\\textbf{User Interface:} configuration through Liferay's UI is stored in\na database. The values set in the UI always override configurations set\nin properties files.\n\n\\textbf{Properties files:} properties files that set default behavior\nmay be included in the platform or the modules. Keep in mind that these\nsettings can always be overridden by a system administrator in the UI.\nTo find what properties are configurable this way, visit\n\\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc}{docs.liferay.com}.\n\nThe UI location where these configuration options appear depends on the\nscope you want to affect with the settings you choose.\n\n\\chapter{Configuration Scope}\\label{configuration-scope}\n\nDepending on the configuration scope of a setting you change, you'll\nimpact the platform and its applications with more or less granularity.\nAt one end of the spectrum, you can affect the scope of the whole\nsystem. Configurations made at the system scope affect all virtual\ninstances, Sites, and widgets. At the opposite end of the spectrum,\nconfigurations made at the widget level provide configuration settings\nonly for that instance of the widget.\n\nTake Language settings, for example. You can set the default language\nused by the virtual instance. You can also set the default language of a\nSite. Some applications even let you set the default language used by a\nspecific piece of content.\n\nHere's an overview of the available configuration scopes:\n\n\\textbf{System:} Configuring Liferay and its applications through System\nSettings provides system scoped configurations and sets default values\nfor all virtual instances, sites, or widgets.\n\n\\textbf{Virtual Instance:} Configuring in Instance Settings provides\nsettings that act on the specific virtual instance for which they are\nmade, including Sites and widgets in the virtual instance.\n\n\\textbf{Site:} Configurations made at the Site scope, where you select\nthe Site to configure in the Site selector, provide settings that take\nplace only in that Site. Alternate configurations can be made in\ndifferent Sites.\n\n\\textbf{Widget Scope:} Configuring a specific widget only provides a\nconfiguration for that particular widget.\n\nScopes in Liferay are hierarchical so that one scope can set the default\nvalues for the underlying sub-scopes. For example, making a system-wide\nconfiguration sets the default values for all virtual instances, sites\nor widgets of the system. If a different configuration is set at a level\nwith more granularity (for example, the widget scope), it takes\nprecedence over the settings made at less granular scopes (for example,\nthe virtual instance scope).\n\nThis section contains articles on configuring Liferay at the System and\nInstance scopes:\n\nSystem wide configuration:\n\n\\begin{itemize}\n\\item\n  System Settings is the primary location for system configuration.\n\\item\n  Server Administration contains some lower-level server configuration\n  options, such as logging.\n\\end{itemize}\n\nSetting up a virtual instance:\n\n\\begin{itemize}\n\\item\n  Virtual Instances is where virtual instances are added and edited.\n\\item\n  Instance Settings is the primary location for a virtual instance's\n  configuration.\n\\item\n  Custom Fields is where additional database fields are added to\n  existing virtual instance entities.\n\\end{itemize}\n\nAll of these are accessed through the Control Panel. Start by learning\nto configure modules system-wide in the System Settings Control Panel\napp.\n\n\\chapter{System Wide Settings}\\label{system-wide-settings}\n\nIt can be hard to keep track of all the configuration interfaces. The\nControl Panel's Configuration section houses a lot of the higher level\n(for example, system and instance scoped) configuration options. This\nsection considers the configuration options dealing with the\n\\emph{System} scope. Configuration at the system level affects all the\n\\href{/docs/7-2/user/-/knowledge_base/u/setting-up-a-virtual-instance}{\\emph{Virtual\nInstances}} of Liferay in the system.\n\n\\begin{itemize}\n\\item\n  System Settings is the primary location for system configuration.\n\\item\n  Server Administration contains some lower-level server configuration\n  options, such as logging.\n\\end{itemize}\n\nGet started by learning about System Settings.\n\n\\chapter{System Settings}\\label{system-settings}\n\nLiferay DXP is modular, meaning it's composed of many applications\ndivided into even smaller ``chunks'' of functionality. The applications,\nand sometimes even code chunks, are configurable at several scopes, as\ndiscussed in the introductory article for this section.\n\nIn System Settings, administrators make system scoped configuration\nchanges and set system-wide default configurations for other scopes.\nSystem Settings is located in Control Panel → Configuration → System\nSettings.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/system-settings-categories.png}\n\\caption{System Settings are accessed through the Control Panel.}\n\\end{figure}\n\n\\section{Editing System\nConfigurations}\\label{editing-system-configurations}\n\nSystem Settings is organized into sections (for example, Content) and\ncategories (for example, Workflow) based on the functionality being\nconfigured. There's also a Search bar to make finding configuration\nentries easier. Search for the name of a specific configuration entry,\nor even a specific field within an entry.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/system-settings-nav-search.png}\n\\caption{System Settings are organized by section and category.}\n\\end{figure}\n\nChanging a configuration isn't difficult:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Find the configuration entry you need, either by searching or browsing\n  the sections and categories.\n\\item\n  Open the configuration form for the entry.\n\\item\n  Make any changes you'd like, then click \\emph{Save}. Your\n  configuration changes are saved and applied throughout the system.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Content generated using templates (e.g., FreeMarker\ntemplates and Application Display Templates) is cached. Cached content\nmight not reflect configuration changes until the cache is invalidated\n(cleared). The\n\\href{/docs/7-2/user/-/knowledge_base/u/server-administration-resources}{Server\nAdministration → Resources tab} provides cache clearing options.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/system-settings-actions.png}\n\\caption{After saving changes to a configuration, the actions\n\\emph{Reset Default Values} and \\emph{Export} are available for it.}\n\\end{figure}\n\nIf you make some regrettable configuration decisions and can't recall\nexactly what you did, start over by clicking the actions button\n(\\includegraphics{./images/icon-actions.png}), then clicking \\emph{Reset\nDefault Values}.\n\n\\section{Configuration Scope}\\label{configuration-scope-1}\n\nWhile browsing the categories of System Settings, you'll notice that\nclicking into a topic (for example, Blogs) reveals entries at different\nscopes. All the settings here act at the system scope. For scopes\nlabeled other than System, these configurations act as defaults. In\nother words, they identify where the system-wide configuration is\noverridden. True system-scoped configurations (those under a category's\n\\emph{System Scope} heading) are not overridden anywhere. There are four\nvalues that you'll see under Scope:\n\n\\begin{itemize}\n\\item\n  \\emph{System Scope:} Any System Settings configuration made for system\n  scoped entries becomes the final value for the application in a\n  system-wide fashion. It affects the whole system and isn't overridden\n  anywhere else.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/system-settings-system-scope.png}\n  \\caption{Some System Settings entries are system scoped.}\n  \\end{figure}\n\\item\n  \\emph{Virtual Instance Scope:} Configuration at the Virtual Instance\n  level is overridden in Instance Settings.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/system-settings-instance-scope.png}\n  \\caption{Some System Settings are virtual instance scoped.}\n  \\end{figure}\n\\item\n  \\emph{Site Scope:} Configuration at this scope is overridden in each\n  site.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/system-settings-site-scope.png}\n  \\caption{Some System Settings are site scoped.}\n  \\end{figure}\n\\item\n  \\emph{Widget Scope:} Configuration at this scope is overridden in each\n  Widget Instance (like the Blogs example below).\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/system-settings-application-scope.png}\n  \\caption{Some System Settings entries are widget scoped.}\n  \\end{figure}\n\\end{itemize}\n\nIf a configuration changed in System Settings is also configurable at a\ndifferent scope, the System Settings value acts as a default that can be\noverridden. Once a configuration change is made at a more granular\nscope, making a change at the system level doesn't do anything.\n\nFor example, allowing comments is configurable for each Blogs Entry. Set\nthe default behavior at Control Panel → Configuration → System Settings\n→ Blogs. In the Blogs Entry under Widget Scope, disable the \\emph{Enable\nComments} checkbox.\n\nNow add a Blog Entry to a Site's Content \\& Data → Blogs application.\nThen go to a public page and add the Blogs Widget to the page. Click the\nOptions button (\\includegraphics{./images/icon-app-options.png}) for the\nwidget and select \\emph{Configuration}. You'll see the same Enable\nComments checkbox, and its default is now false (unchecked). Checking\nthe box in the Widget Configuration screen breaks its link with the\nSystem Settings entry. Changing the System Settings configuration has no\neffect on this widget anymore.\n\n\\section{Exporting and Importing\nConfigurations}\\label{exporting-and-importing-configurations}\n\nWhat if you change many default configurations in System Settings, and\nthen need to make the same changes in another installation? Don't worry,\nyou don't need to remember and duplicate every choice you made. The\nSystem Settings application lets you export a single entry's\nconfigurations, or all the settings you made in the System Settings\ninterface. The exported files are deployable to any other installation\nof the same version.\n\nTo export a single entry's configurations, click the actions button\n(\\includegraphics{./images/icon-actions.png}), then click \\emph{Export}.\nA \\texttt{.config} file containing your configuration downloads to your\nsystem.\n\nTo export all the configuration changes you've made in System Settings,\nclick the System Settings options button\n(\\includegraphics{./images/icon-options.png}), then click \\emph{Export\nAll Settings}. The \\texttt{.config} files for all the entries you edited\nthen download in a ZIP file.\n\nTo make these configurations active in the destination system, unzip and\nplace the \\texttt{.config} files in the\n\\texttt{{[}Liferay\\_Home{]}/osgi/configs} folder.\n\nNow you know what System Settings is and how to use it. All that's left\nis to explore the entries to see what configuration options are\navailable. If you aren't sure what something does, check the\ndocumentation for the feature you're interested in, as specific\nconfigurations are covered there.\n\n\\chapter{Understanding System Configuration\nFiles}\\label{understanding-system-configuration-files}\n\nThe \\href{/docs/7-2/user/-/knowledge_base/u/system-settings}{System\nSettings application} is convenient for making system-scoped\nconfiguration changes and setting default configurations for other\n\\href{/docs/7-2/user/-/knowledge_base/u/setting-up\\#configuration-scope}{scopes}.\nBut there's another supported configuration approach: configuration\nfiles. You can use configuration files to transfer configurations from\npre-production systems to production systems, or between any other\nLiferay DXP systems. Sometimes developers choose to distribute the\ndefault configuration for their applications via configuration file.\nWhatever the reason, configuration files offer another configuration\napproach.\n\nConfiguration files use the \\texttt{.config} property value format\ndefined by the\n\\href{http://felix.apache.org/documentation/subprojects/apache-felix-config-admin.html}{Apache\nFelix Configuration Admin framework}.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Content generated using templates (e.g., FreeMarker\ntemplates and Application Display Templates) is cached. Cached content\nmight not reflect configuration changes until the cache is invalidated\n(cleared). The\n\\href{/docs/7-2/user/-/knowledge_base/u/server-administration-resources}{Server\nAdministration → Resources tab} provides cache clearing options.\n\n\\textbf{Note:} The \\texttt{.cfg} file format is common in OSGi\nenvironments, and it's a supported format, but \\texttt{.config} files\nare preferable since they allow specifying a property value's type, and\nallow multi-valued properties. The syntax described in these articles is\nfor \\texttt{.config} files.\n\n\\chapter{Creating Configuration\nFiles}\\label{creating-configuration-files}\n\nSystem Settings provides an\n\\href{/docs/7-2/user/-/knowledge_base/u/system-settings\\#exporting-and-importing-configurations}{\\emph{Export}}\noption that becomes available once you modify a configuration entry.\nExporting is the recommended way to create \\texttt{.config} files: you\ndownload a \\texttt{.config} file containing the entry's settings in a\n\\texttt{key=value} format. Liferay DXP exports an entry's total\navailable configuration keys and values, even if only one value was\nchanged. You can export a single configuration entry or the entire set\nof modified configurations.\n\nTo avoid a file name conflict, name configuration files using a unique\nidentifier. For example, the Journal Service entry, which backs Web\nContent functionality, has this file name:\n\n\\begin{verbatim}\ncom.liferay.journal.configuration.JournalServiceConfiguration.config\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/config-web-content-entry.png}\n\\caption{The Web Content System Settings entry has the back-end ID\n\\texttt{com.liferay.journal.configuration.JournalServiceConfiguration}.}\n\\end{figure}\n\n\\section{Key/Value Syntax}\\label{keyvalue-syntax}\n\nThe syntax for all keys and values in a \\texttt{.config} file is the\nsame:\n\n\\begin{verbatim}\nconfigurationName=\"value\"\n\\end{verbatim}\n\nFor single value configurations without special characters, that's all\nthere is to know. Settings with multiple values and certain characters\nrequire slight modifications.\n\n\\section{Multi-Value Settings}\\label{multi-value-settings}\n\nConfiguration entries can have properties that accept multiple values.\nFor example, a configuration property for specifying supported file\nextensions needs more than one value. Here's how to write a multi-value\nsetting in a \\texttt{.config} file:\n\n\\begin{verbatim}\nmultiValueSetting=[\"Value 1\",\"Value 2\", ...]\n\\end{verbatim}\n\nDo not use a space character between values (after the comma). The\nproperty won't be loaded.\n\nOpen the Web Content category in System Settings (under the Content\nsection), and select \\emph{Web Content} for the virtual instance scope.\nYou'll see what looks like multiple single value entries for\n\\emph{Characters Blacklist}:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/config-web-content-blacklist.png}\n\\caption{The Web Content System Settings entry has many \\emph{Characters\nBlacklist} fields.}\n\\end{figure}\n\nIn the configuration file, this is really a single key with an array of\ncomma-separated values:\n\n\\begin{verbatim}\ncharactersblacklist=[\"&\",\"'\",\"@\",\"\\\\\",\"]\",\"}\",\":\",\"\\=\",\">\",\"/\",\"<\",\"[\",\"{\",\"%\",\"+\",\"#\",\"`\",\"?\",\"\\\"\",\";\",\"*\",\"~\"]\n\\end{verbatim}\n\n\\section{Escaping Characters}\\label{escaping-characters}\n\nDouble quotes (\\texttt{\"}) and equals signs (\\texttt{=}) must be\n\\emph{escaped} in \\texttt{.config} files. Escaping is using another\ncharacter to denote that a character shouldn't be used in its normal\nway. Since double quotes and equals signs are already used in\n\\texttt{.config} files, escaping them tells the framework not to read\nthem the normal way, but to pass them through as part of the value. Use\na backslash to escape characters in the \\texttt{.config} file:\n\n\\begin{verbatim}\ncharactersblacklist=[\"&\",\"\\\"\",\"\\=\"]\n\\end{verbatim}\n\nThis setting illustrates a multi-value setting with a regular, unescaped\ncharacter (\\texttt{\\&}), and two escaped ones\n(\\texttt{\\textbackslash{}\"} and \\texttt{\\textbackslash{}=}).\n\nAlong with the mandatory escaping of double quotes and equals\ncharacters, it's beneficial to escape spaces inside values to avoid\nproblems. In this example, a backslash is used before each space\ncharacter to ensure it's read and processed properly:\n\n\\begin{verbatim}\nblacklistBundleSymbolicNames=[\"Liferay\\ Marketplace\",\"Liferay\\ Sharepoint\\ Connector\"]\n\\end{verbatim}\n\nIf you don't escape spaces yourself, the framework adds the backslash\nfor you after deployment.\n\n\\section{Typed Values}\\label{typed-values}\n\nThe \\texttt{.config} file format supports specifying the type of a\nconfiguration value by inserting a special type marker character.\nBecause Liferay DXP already knows the correct type for each\nconfiguration property, the type characters are only useful for\ninformational purposes. For example, a configuration with a boolean type\nhas \\emph{B} just before the value to mark it as a boolean type:\n\n\\begin{verbatim}\naddDefaultStructures=B\"true\"\n\\end{verbatim}\n\nIf you see type markers in \\texttt{.config} files, you can safely ignore\nthem. The example included above functions identically without the type\nmarker:\n\n\\begin{verbatim}\naddDefaultStructures=\"true\"\n\\end{verbatim}\n\n\\section{Deploying a Configuration\nFile}\\label{deploying-a-configuration-file}\n\nOnce you have a configuration file, deploy it. It's registered and the\ntargeted configuration values are updated automatically.\n\nTo deploy the \\texttt{.config} file, place it in your\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home's}\n\\texttt{osgi/configs} folder. To change the configuration further, you\ncan edit the \\texttt{.config} file directly or use System Settings.\n\n\\section{Configuration Files and\nClustering}\\label{configuration-files-and-clustering}\n\nIn a clustered environment, each node needs the same configuration\nvalues for each entry. For example, all nodes should use the same\n\\emph{Blogs} configuration settings. To accomplish this, deploy copies\nof the same \\texttt{.config} file to each node's\n\\texttt{{[}Liferay\\ Home{]}/osgi/configs} folder.\n\n---\n\nheader-id: factory-configurations\n\nFactory Configurations\n\n{[}TOC levels=1-4{]}\n\nConfigurations supporting multiple entries are called factory\nconfigurations.\n\nFactory Configuration Example: JAX-WS and JAX-RS web services are\n\\textbar{} supported. These services must use a CXF Endpoint, which is a\ncontext path where \\textbar{} the web services are deployed and\naccessed. Endpoints can be created via \\textbar{} factory configuration\nby navigating to the CXF Endpoints entry in System \\textbar{} Settings\n(System Settings → Platform → Web API → CXF Endpoints). \\textbar{} Click\nADD, enter the desired configuration values, then repeat the process to\n\\textbar{} add as many CXF Endpoint configurations as needed. Creating\nCXF Endpoint \\textbar{} configurations also creates CXF Endpoints\nthemselves. This is how factory \\textbar{} configurations work.\n\nIf your service supports factory configurations, use the convention of\ncalling the configuration's first instance -default.config:\n\nThe next instance contains a unique subname (something other than\ndefault). It's good practice to use a descriptive name:\n\nTo follow the CXF Endpoints example described above, if Liferay's\ndevelopers had shipped an initial CXF Endpoint .config file with Liferay\nDXP, it would have been named this:\n\nIf this -default.config configuration specifies a context path for REST\nweb services, and you create another endpoint with a different context\npath for SOAP web services, your second configuration file could be\nnamed:\n\nNote: Some System Settings entries (like the CXF Endpoints entry) don't\nship \\textbar{} with a configuration file, so anything you create is the\nfirst occurrence. \\textbar{} However, if you configure one and export it\nto obtain the .config file, it \\textbar{} doesn't use the\n-default.config naming convention. Instead it's given a \\textbar{}\nguaranteed unique identifier for its subname, like this: \\textbar{}\n\\textbar{}\ncom.liferay.portal.remote.cxf.common.configuration.CXFEndpointPublisherConfiguration-a6f67e48-6dca-49c6-bf6b-8fd5e6016b2d.config\n\\textbar{} \\textbar{} This guarantees that the file has a unique name.\nIf you're exporting the \\textbar{} configuration file for deployment in\na separate system, you can rename \\textbar{} the exported file to use a\nmore descriptive subname. If you rename the file and \\textbar{} deploy\nit to the same system it was exported from, the new subname marks it as\n\\textbar{} an entirely new configuration. You'll end up with an\nadditional configuration \\textbar{} instance in this case, not just a\nrenamed one.\n\n\\chapter{Factory Configurations}\\label{factory-configurations}\n\nConfigurations supporting multiple entries are called \\emph{factory\nconfigurations}.\n\n\\begin{quote}\nFactory Configuration Example: Adding Organization types is supported,\nand is useful if you need to model real-life hierarchies or enforce\nhierarchical rules. In Liferay DXP, each Organization type is created\nvia a factory configuration entry in System Settings.\n\\end{quote}\n\nIf a service is meant to support factory configurations, its System\nSettings entry has an ADD button.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/factory-configuration-entry.png}\n\\caption{If a System Settings entry has an ADD button, it's suitable for\nfactory configurations.}\n\\end{figure}\n\nAs with single-instance configurations, you can set factory\nconfigurations in the System Settings interface (as described in the\nexample above) or via configuration files. Name a standard\nsingle-instance configuration file like this:\n\n\\begin{verbatim}\nmy.service.ServiceConfiguration.config\n\\end{verbatim}\n\nIf your service supports factory configurations, use the convention of\ncalling the configuration's first instance \\texttt{-default.config}:\n\n\\begin{verbatim}\nmy.service.ServiceConfiguration-default.config\n\\end{verbatim}\n\nThe next instance contains a unique \\emph{subname} (something other than\n\\emph{default}). It's good practice to use a descriptive name:\n\n\\begin{verbatim}\nmy.service.ServiceConfiguration-port9080.config\n\\end{verbatim}\n\nIn the Organization type example, the default Organization type (aptly\nnamed \\emph{organization}) is created by a \\texttt{-default.config} file\nnamed\n\n\\begin{verbatim}\ncom.liferay.organizations.internal.configuration.OrganizationTypeConfiguration-default.config\n\\end{verbatim}\n\nFollowing the example from the\n\\href{../../users-and-permissions/organizations/adding-a-new-organization-type.md}{Adding\na New Organization Type} article, you could add the \\emph{League} type\nwith a configuration file named\n\n\\begin{verbatim}\ncom.liferay.organizations.internal.configuration.OrganizationTypeConfiguration-league.config\n\\end{verbatim}\n\nSome System Settings entries that support factory configuration don't\nship with a configuration file for the default instance (e.g., the\nAnonymous User entry). If you export a factory configuration file to\nobtain the \\texttt{.config} file, it doesn't use the\n\\texttt{-default.config} naming convention. Instead, whether it's the\nfirst occurrence or an additional one, it's given a guaranteed unique\nidentifier for its subname:\n\n\\begin{verbatim}\ncom.liferay.user.associated.data.web.internal.configuration.AnonymousUserConfiguration-6befcd73-7c8b-4597-b396-a18f64f8c308.config\n\\end{verbatim}\n\nThis guarantees that the file has a unique name. If you're exporting the\nconfiguration file for deployment in a separate system, you can rename\nthe exported file to use a more descriptive subname. If you rename the\nfile and deploy it to the same system it was exported from, the new\nsubname marks it as an entirely new configuration. You'll end up with an\nadditional configuration instance in this case, not just a renamed one.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning::} For configuration entries supporting factory\nconfigurations, omitting the subname from a \\texttt{.config} file's name\ncauses System Settings to disallow adding new entries for the\nconfiguration (only the configuration entry targeted by this\n\\texttt{.config} file). This is caused by a known bug. See\n\\href{https://issues.liferay.com/browse/LPS-76352}{LPS-76352} for more\ninformation. Once an improperly named configuration file is deployed,\nyou can't add any entries for the configuration in question from its\nSystem Settings entry. For example, if you deploy the following file to\nconfigure an Organization Type, not only does this not add an\nOrganiaztion Type, it also prevents you from adding any via System\nSettings:\n\n\\begin{verbatim}\n com.liferay.organizations.internal.configuration.OrganizationTypeConfiguration.config\n\\end{verbatim}\n\nDeploying an erroneous (lacking a subname) \\texttt{.config} file doesn't\ndisable anything permanently. Just rename the file using the proper\nconvention described above or remove it entirely and start over.\n\n\\noindent\\hrulefill\n\nIn many cases, configuration files can be used to force a factory\nconfiguration scenario, but not all configurations can be used this way.\nIt's best to stick to the intended use cases. Use System Settings as\ndescribed above to determine if using factory configurations is a good\nidea. If not, stick to the single occurrence mode of configuration\n(specifying only one configuration file for the service).\n\n---\n\nheader-id: server-administration\n\nServer Administration\n\n{[}TOC levels=1-4{]}\n\nServer Administration lets you manage and monitor your Liferay DXP\nserver. Access the application by clicking Control Panel → Configuration\n→ Server Administration.\n\nFigure 1: The Resources tab of Server Administration shows a graph of\nyour server's memory usage.\n\nServer Administration's functionality is segmented into these tabs:\n\nResources: View memory usage and perform management tasks like running\nthe garbage collector, clearing the database cache, and more. For more\ninformation, see Resources.\n\nLog Levels: View and set logging levels. You can make dynamic\nmodifications of log levels for any class hierarchy in Liferay DXP.\nCustom objects not on the list can be added with the Add Category tab.\nChanges to the log level near the top of the class hierarchy (such as at\ncom.liferay) also change log levels for all the classes under that\nhierarchy. Modifications unnecessarily high in the hierarchy generate\ntoo many messages to be useful.\n\nProperties: View JVM and portal properties. This tab has two sub-tabs:\nSystem Properties and Portal Properties. The System Properties tab shows\nan exhaustive list of system properties for the JVM, as well as many\n\nCreate a backup copy of the Document Library repository and Liferay DXP\ndatabase.\n\nConfigure the new file store in System Settings → Platform: File\nStorage.\n\nIn this tab (Server Administration → Data Migration), select the\nrepository hook for the file store you configured and click Execute.\n\nMake sure the data migrated correctly.\n\nConfigure the new repository as the default.\n\nIf you used a portal-ext.properties file to configure the repository,\nrestart the server.\n\nMail: Instead of using a portal-ext.properties file to configure a mail\nserver, you can configure a mail server from this tab. If your message\nboards receive mail, you can connect a POP mail server. If Liferay DXP\nsends mail (useful for sending notifications to users), you can connect\nto an SMTP server. Note that if you configure mail server settings here\nin System Settings, these settings override any mail server settings in\nyour portal-ext.properties file.\n\nExternal Services: Configure external services for generating file\npreviews. For more information, see the article on External Services.\n\nScript: A scripting console for executing migration or management code.\nThe Groovy scripting language is supported out of the box. .\n\nShutdown: Schedule a shutdown that notifies logged-in users of the\nimpending shutdown. You can define the number of minutes until the\nshutdown and a message to display. Liferay DXP displays the message at\nthe top of users' pages for the duration of time you specified. When the\ntime expires, all pages display a message saying the portal has been\nshut down. The server must then be restarted to restore access.\n\n\\chapter{Server Administration}\\label{server-administration}\n\nServer Administration lets you manage and monitor your Liferay DXP\nserver. Access the application by clicking \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{Server Administration}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/server-admin-memory.png}\n\\caption{The Resources tab of Server Administration shows a graph of\nyour server's memory usage.}\n\\end{figure}\n\nServer Administration's functionality is segmented into these tabs:\n\n\\textbf{Resources:} View memory usage and perform management tasks like\nrunning the garbage collector, clearing the database cache, and more.\nFor more information, see\n\\href{/docs/7-2/user/-/knowledge_base/u/server-administration-resources}{Resources}.\n\n\\textbf{Log Levels:} View and set logging levels. You can make dynamic\nmodifications of log levels for any class hierarchy in Liferay DXP.\nCustom objects not on the list can be added with the \\emph{Add Category}\ntab. Changes to the log level near the top of the class hierarchy (such\nas at \\texttt{com.liferay}) also change log levels for all the classes\nunder that hierarchy. Modifications unnecessarily high in the hierarchy\ngenerate too many messages to be useful.\n\n\\textbf{Properties:} View JVM and portal properties. This tab has two\nsub-tabs: System Properties and Portal Properties. The System Properties\ntab shows an exhaustive list of system properties for the JVM, as well\nas many Liferay DXP system properties. You can use this information for\ndebugging purposes or to check the currently running configuration. The\nPortal Properties tab shows an exhaustive list of the current portal\nproperty values. For explanations of these properties, see the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html}{portal\nproperties reference documentation}.\n\n\\textbf{Data Migration:} Migrate documents from one repository to\nanother. For example, you can migrate your documents to a new repository\non a different disk or in a\n\\href{/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration}{new\nformat}. Here are the steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Create a backup copy of the Document Library repository and\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation}{Liferay\n  DXP database}.\n\\item\n  Configure the new file store in \\emph{System Settings} →\n  \\emph{Platform: File Storage}.\n\\item\n  In this tab (\\emph{Server Administration} → \\emph{Data Migration}),\n  select the repository hook for the file store you configured and click\n  \\emph{Execute}.\n\\item\n  Make sure the data migrated correctly.\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration}{Configure\n  the new repository} as the default.\n\\item\n  If you used a \\texttt{portal-ext.properties} file to configure the\n  repository, restart the server.\n\\end{enumerate}\n\n\\textbf{Mail:} Instead of using a \\texttt{portal-ext.properties} file to\nconfigure a mail server, you can configure a mail server from this tab.\nIf your\n\\href{/docs/7-2/user/-/knowledge_base/u/user-subscriptions-and-mailing-lists}{message\nboards receive mail}, you can connect a POP mail server. If Liferay DXP\nsends mail (useful for sending notifications to users), you can connect\nto an SMTP server. Note that if you configure mail server settings here\nin System Settings, these settings override any mail server settings in\nyour \\texttt{portal-ext.properties} file.\n\n\\textbf{External Services:} Configure external services for generating\nfile previews. For more information, see\n\\href{/docs/7-2/user/-/knowledge_base/u/server-administration-external-services}{the\narticle on External Services}.\n\n\\textbf{Script:} A scripting console for executing migration or\nmanagement code. The Groovy scripting language is supported out of the\nbox.\n\n\\textbf{Shutdown:} Schedule a shutdown that notifies logged-in users of\nthe impending shutdown. You can define the number of minutes until the\nshutdown and a message to display. Liferay DXP displays the message at\nthe top of users' pages for the duration of time you specified. When the\ntime expires, all pages display a message saying the portal has been\nshut down. The server must then be restarted to restore access.\n\n\\chapter{Server Administration:\nResources}\\label{server-administration-resources}\n\nThe Server Administration app's Resources tab contains several server\nwide actions that an administrator can execute. These include the\nfollowing items:\n\n\\textbf{Run the garbage collector:} Tells the JVM to free memory by\nrunning the garbage collector.\n\n\\textbf{Generate a thread dump:} Generates a thread dump for later\nscrutiny to determine the presence and location of any deadlocks. Useful\nduring performance testing, but you must add a logger category for\n\\texttt{com.liferay.server.admin.web.internal.portlet.action.EditServerMVCActionCommand}\nand set it to \\texttt{INFO} before executing.\n\n\\textbf{Clear content cached by this VM:} Clears content stored in the\nlocal cache. Only local JVM scope Ehcache content is cleared, not\nclustered Ehcache. \\hyperref[one]{1}\n\n\\textbf{Clear content cached across the cluster:} Clears the content of\nthe entire clustered Ehcache. \\hyperref[one]{1}\n\n\\textbf{Clear the database cache:} Clears the database cache. Does not\nclear any Ehcache content except database results at the persistence\nlayer. \\hyperref[one]{1}\n\n\\textbf{Clear the direct servlet cache:} Clears the direct servlet\ncache. In case emergency fixes must be applied, this action allows an\nadministrator to clear out the cache manually to force JSPs to reload.\n\nThe direct servlet context optimizes JSP serving performance by caching\nand accessing the generated servlets directly instead of accessing them\nover the application server's dispatcher chain. This function is only\nsuitable for cases where no filter is required for the JSPs; it should\nbe enabled for production mode to improve performance, but disabled for\ndevelopment mode to allow JSP servlets to be reloaded on the fly. See\nthe Direct Servlet Context section of the \\texttt{portal.properties}\nfile for details. \\hyperref[one]{1}\n\n\\textbf{Verify database tables of all plugins:} Checks all tables\nagainst their indexes for data retrieval accuracy.\n\n\\textbf{Verify membership policies:} Checks that existing Site\nmembership policies were correctly applied and automatically makes\nupdates. If the Liferay DXP database is changed manually or is\nhacked---resulting in a user assigned to a Site in violation of a site\nmembership policy---this action triggers the verification methods of all\nimplemented Site membership policies. Changes are automatically made to\nany memberships in violation.\n\n\\textbf{Reset preview and thumbnail files for Documents and Media:}\nRegenerates previews of each item in your Documents and Media libraries.\n\n\\textbf{Clean up permissions:} Removes permissions on the Guest, User,\nand Power User Roles to simplify the management of User Customizable\nPages. The Add To Page permission is removed from the Guest and User\nRoles for all portlets, and is reduced in scope for Power Users from\nportal-wide to User Personal Site.\n\n\\textbf{Clean up portlet preferences:} This action cleans up database\nentries if portlet preferences become orphaned in the Liferay DXP\ndatabase.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/server-admin-resources.png}\n\\caption{The Resources tab of Server Administration lets you execute\nseveral server maintenance tasks.}\n\\end{figure}\n\n{[}1{]} Caching occurs at multiple levels. Some higher caching layers\naren't aware of lower caching layers. Always clear the cache at the\nlowest (most granular) layer possible, even if you've already cleared a\nhigher level cache.\n\n\\chapter{Server Administration: External\nServices}\\label{server-administration-external-services}\n\nUsers can upload and share any type of file via the Documents and Media\nlibrary, a customizable and permissions-enabled online repository for\nfiles (see\n\\href{/docs/7-2/user/-/knowledge_base/u/publishing-files}{publishing\nfiles} for more information). \\href{https://pdfbox.apache.org/}{PDFBox}\nis included with Liferay DXP and generates automatic previews for\ncertain file types (mostly PDFs). You can install two additional tools\nto generate previews for other file types:\n\n\\begin{itemize}\n\\item\n  \\href{https://www.openoffice.org/}{\\textbf{OpenOffice:}} or\n  \\href{https://www.libreoffice.org/}{\\textbf{LibreOffice:}} Convert and\n  generate previews for many types of documents.\n\\item\n  \\href{https://www.imagemagick.org/script/index.php}{\\textbf{ImageMagick:}}\n  Generate higher-quality image previews for many types of images.\n\\item\n  \\href{http://www.xuggle.com/xuggler/}{\\textbf{Xuggler:}} Convert and\n  generate previews for audio and video files.\n\\end{itemize}\n\nAs of Liferay 7.1, OpenOffice/LibreOffice is configured in OSGi\nConfiguration Admin instead of portal properties. To adjust the these\nsettings, go to \\emph{Control Panel} → \\emph{Configuration} →\n\\emph{System Settings} → \\emph{Connectors} → \\emph{OpenOffice\nIntegration}. You can also adjust these settings by deploying a\n\\texttt{com.liferay.document.library.document.conversion.internal.configuration.OpenOfficeConfiguration.config}\nfile to your \\texttt{{[}Liferay\\ Home{]}/osgi/configs} folder. See the\n\\href{/docs/7-1/reference/-/knowledge_base/r/breaking-changes\\#moved-openoffice-properties-to-osgi-configuration}{Breaking\nChanges} document for details.\n\nOnce you've installed ImageMagick and Xuggler, you can use the Server\nAdministration app's External Services tab to configure Liferay DXP to\nuse them. Make sure to choose the correct versions of these tools for\nyour operating system. Install the latest stable versions, as older\nversions may not run properly with Liferay DXP. ImageMagick must be\ninstalled manually, but you can install Xuggler from the Control Panel.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} If you're running Liferay DXP on a Linux server and\nexperience a problem enabling Xuggler, check your server's glibc\nversion. For Xuggler to work, you may need to update glibc to version\n2.6 or later.\n\n\\noindent\\hrulefill\n\n\\section{ImageMagick Configuration}\\label{imagemagick-configuration}\n\nBefore configuring ImageMagick to generate image and PDF previews,\ninstall it and its dependency, Ghostscript. This differs by operating\nsystem: on Linux, both are likely already installed. They are not likely\nto be installed on Windows, but may be on macOS.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Download and install\n  \\href{https://www.imagemagick.org/script/index.php}{ImageMagick}.\n\\item\n  Download and install \\href{https://www.ghostscript.com/}{Ghostscript}.\n\\end{enumerate}\n\nOnce installed, you must enable ImageMagick in the Server Administration\napp's External Services tab, or in a \\texttt{portal-ext.properties}\nfile. If using \\texttt{portal-ext.properties}, add the following lines\nand make sure the search path points to the directories containing the\nImageMagick and Ghostscript executables. You may also need to configure\nthe path for fonts used by Ghostscript when in macOS or Unix\nenvironments.\n\n\\begin{verbatim}\nimagemagick.enabled=true\nimagemagick.global.search.path[apple]=/opt/local/bin:/opt/local/share/ghostscript/fonts:/opt/local/share/fonts/urw-fonts\nimagemagick.global.search.path[unix]=/usr/local/bin:/usr/local/share/ghostscript/fonts:/usr/local/share/fonts/urw-fonts\nimagemagick.global.search.path[windows]=C:\\\\Program Files\\\\ImageMagick\n\\end{verbatim}\n\nFollow these steps to instead enable ImageMagick from the Server\nAdministration app's External Services tab:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\emph{Control Panel}, navigate to \\emph{Configuration} →\n  \\emph{Server Administration}, then click the \\emph{External Services}\n  tab.\n\\item\n  Expand the ImageMagick and Ghostscript section and select\n  \\emph{Enabled}.\n\\item\n  Verify that the paths to the ImageMagick and Ghostscript executables\n  are correct.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/imagemagick-ghostscript.png}\n\\caption{Enable ImageMagick and Ghostscript, and verify that the paths\nare correct.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\tightlist\n\\item\n  Set the Resource limits to use.\n\\end{enumerate}\n\n\\section{Xuggler Configuration}\\label{xuggler-configuration}\n\nFollow these steps to install and configure Xuggler:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\emph{Control Panel}, navigate to \\emph{Configuration} →\n  \\emph{Server Administration}, then click the \\emph{External Services}\n  tab.\n\\item\n  In the Xuggler section, select the Xuggler \\texttt{.jar} file that\n  matches your operating system. Then click \\emph{Install}.\n\\item\n  Restart your application server.\n\\item\n  Enable Xuggler for your portal. There are two ways to do this:\n\n  \\begin{itemize}\n  \\item\n    In the Control Panel, navigate to the \\emph{Server Administration} →\n    \\emph{External Services} tab, select \\emph{Enabled}, then click\n    \\emph{Save}.\n  \\item\n    Add the following line to your \\texttt{portal-ext.properties} file\n    and restart your application server:\n\n\\begin{verbatim}\nxuggler.enabled=true\n\\end{verbatim}\n  \\end{itemize}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/xuggler-install.png}\n\\caption{Install Xuggler.}\n\\end{figure}\n\n\\chapter{Setting Up a Virtual\nInstance}\\label{setting-up-a-virtual-instance}\n\nOnce Liferay DXP is installed, the configuration begins. Recall that\nconfiguration happens at different scopes. Here we're covering\nconfiguration at the virtual instance scope. There's an important\ndifference between the system scope and the instance scope. The system\nscope is the highest level scope you can make configurations at. All\nvirtual instances are impacted by configuration done at this scope. The\ninstance scope applies only to one particular virtual instance.\n\nVirtual instances have unique domain names but share a server and\ndatabase. Each virtual instance can have independent data and\nconfigurations.\n\nThe articles in this section cover these topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Adding a virtual instance\n\\item\n  Configuring a virtual instance\n\\end{itemize}\n\nGet started by learning how to add a virtual instance.\n\n\\chapter{Virtual Instances}\\label{virtual-instances}\n\nHere's a quick scenario: you already have a server hosting a Liferay DXP\ninstallation and a database. It has many\n\\href{/docs/7-2/user/-/knowledge_base/u/users-and-organizations}{Users},\n\\href{/docs/7-2/user/-/knowledge_base/u/building-a-site}{Sites}, and\nspecific\n\\href{/docs/7-2/user/-/knowledge_base/u/instance-configuration-instance-settings\\#general}{instance\nsettings}. If you require a second similar installation, then adding a\n\\emph{Virtual Instance} might be right for you.\n\nYou can run more than one Virtual Instance on a single server with a\nshared database, but separate data and configurations. Users are\ndirected to the correct Virtual Instance via its unique domain name. And\nbecause Virtual Instances share an application server and OSGi\ncontainer, they also share these customizations:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Custom code deployed by developers and administrators.\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/system-settings}{System-scoped\n  configurations} (e.g., \\texttt{.config} files, changes made in\n  \\emph{Control Panel} → \\emph{Configuration} → \\emph{System Settings}).\n\\item\n  Application server configuration.\n\\end{itemize}\n\nAdministrators can manage Virtual Instances in \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{System Settings}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/virtual-instances.png}\n\\caption{Add and manage virtual instances of Liferay in the Control\nPanel's \\emph{Configuration} → \\emph{Virtual Instances} section.}\n\\end{figure}\n\nFollow these steps to create a Virtual Instance:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Before you create a Virtual Instance, configure its domain name in\n  your network.\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{Virtual\n  Instances}.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n  This opens the \\emph{New Instance} form.\n\\item\n  Complete the New Instance form as follows:\n\\end{enumerate}\n\n\\textbf{Web ID:} The instance's ID. Using the domain name is a common\nconvention.\n\n\\textbf{Virtual Host:} The domain name you configured in your network.\nWhen users are directed to your server via this domain name, they'll be\nsent to the Virtual Instance that contains their data.\n\n\\textbf{Mail Domain:} The mail host's domain name for the Virtual\nInstance. Email notifications are sent from the instance using this\ndomain.\n\n\\textbf{Max Users:} The maximum number of user accounts the Virtual\nInstance supports. Enter \\emph{0} to support unlimited users.\n\n\\textbf{Active:} Whether the Virtual Instance is active. Note that\ninactive Virtual Instances aren't accessible to anyone, even the\nadministrator.\n\nClick \\emph{Save} when you're done filling out the form.\n\nNow you can navigate to the instance using its domain name. You're\nbrought to what looks like a clean install of Liferay DXP. This is your\nnew Virtual Instance! You can configure it any way you like. The\nremaining articles in this section show you how to configure an\ninstance's settings.\n\n\\chapter{Configuring Virtual\nInstances}\\label{configuring-virtual-instances}\n\nTo access instance settings, open the Control Panel and navigate to\n\\emph{Configuration} → \\emph{Instance Settings}. The Instance Settings\nare organized into three sections: PLATFORM, SECURITY, and CONTENT AND\nDATA. Here we focus on the instance settings available under the\nPLATFORM section. The CONTENT AND DATA settings are specific to each\napplication, and the SECURITY instance settings are covered in their\nrespective articles, listed below for reference:\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-ldap}{LDAP}\n\\item\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/oauth2-scopes\\#creating-the-authorization-page}{OAuth\n  2}\n\\item\n  SSO:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/cas-central-authentication-service-single-sign-on-authentication}{CAS\n    Server}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/token-based-single-sign-on-authentication}{Token\n    based SSO}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/authenticating-with-openid-connect\\#enabling-openid-connect-authentication}{Open\n    ID Connect}\n  \\item\n    \\href{/docs/7-2/deploy/-/knowledge_base/d/opensso-single-sign-on-authentication}{OpenSSO}\n  \\end{itemize}\n\n  The PLATFORM instance settings are covered in the articles shown\n  below:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\href{/docs/7-2/user/-/knowledge_base/u/email-instance-settings}{Email}\n  \\item\n    \\href{/docs/7-2/user/-/knowledge_base/u/instance-configuration-instance-settings}{Instance\n    Configuration}\n  \\item\n    \\href{/docs/7-2/user/-/knowledge_base/u/user-authentication-instance-settings}{User\n    Authentication}\n  \\item\n    \\href{/docs/7-2/user/-/knowledge_base/u/users-instance-settings}{Users}\n  \\item\n    \\href{/docs/7-2/user/-/knowledge_base/u/more-platform-section-instance-settings}{More\n    Platform Settings}\n  \\end{itemize}\n\\end{itemize}\n\n\\chapter{Email Instance Settings}\\label{email-instance-settings}\n\nThe Email configuration is your one-stop-shop for all your email\nnotification needs. To access the Email configuration settings for your\ninstance, open the Control Panel and navigate to \\emph{Configuration} →\n\\emph{Instance Settings} and select the \\emph{Email} category under the\nPLATFORM section. The Email configuration contains six entries:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Account Created Notification\n\\item\n  Email Sender\n\\item\n  Email Verification Notification\n\\item\n  Mail Host Names\n\\item\n  Password Changed Notification\n\\item\n  Password Reset Notification\n\\end{itemize}\n\nEach configuration entry is described in the corresponding section\nbelow.\n\n\\section{Account Created\nNotification}\\label{account-created-notification}\n\nThe Account Created Notification entry defines the email templates, with\nand without the new User's password included in the body of the text,\nfor the message sent to notify Users when they create a new account. You\ncan specify whether this email is sent by checking/unchecking the\n\\emph{Enabled} checkbox at the top. Use the template variables listed at\nthe bottom of the configuration under the ``Definition of Terms''\nheading to help build your email template.\n\n\\section{Email Sender}\\label{email-sender}\n\nThe Email Sender entry specifies the virtual instance's administrative\nName and Address for email notifications, declared as the\n\\texttt{{[}\\$FROM\\_NAME\\${]}} and \\texttt{{[}\\$FROM\\_ADDRESS\\${]}}\nvariables respectively in the email templates. By default, they are from\nthe\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Admin\\%20Portlet}{\\texttt{admin.email.from.name}\nand \\texttt{admin.email.from.address}}\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\nproperties}). This name and email address appear in the \\emph{From}\nfield in all email messages sent by the virtual instance.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/instance-settings-account-created.png}\n\\caption{Customize the email template for the email messages sent to new\nUsers.}\n\\end{figure}\n\n\\section{Email Verification\nNotification}\\label{email-verification-notification}\n\nThe Email Verification Notification entry defines the email template for\nthe message sent to Users when asked to verify their Email Address. Use\nthe template variables listed at the bottom of the configuration under\nthe ``Definition of Terms'' heading to help build your email template.\n\n\\section{Mail Host Names}\\label{mail-host-names}\n\nThe Mail Host Names entry specifies which mail host names are owned by\nyour organization for the virtual instance. Enter one mail host name per\nline, besides the one specified in the \\href{}{General tab}.\n\n\\section{Password Changed\nNotification}\\label{password-changed-notification}\n\nThe Password Changed Notification entry defines the email template for\nthe message sent to notify Users when their password has changed. Use\nthe template variables listed at the bottom of the configuration under\nthe ``Definition of Terms'' heading to help build your email template.\n\n\\section{Password Reset Notification}\\label{password-reset-notification}\n\nThe Password Reset Notification entry defines the email template for the\nmessage sent to notify Users when a request has been made to reset their\npassword. Use the template variables listed at the bottom of the\nconfiguration under the ``Definition of Terms'' heading to help build\nyour email template.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/instance-settings-definition-of-terms.png}\n\\caption{There are some handy variables available for use in email\ntemplates.}\n\\end{figure}\n\n\\chapter{Instance Configuration Instance\nSettings}\\label{instance-configuration-instance-settings}\n\nThe Instance Configuration settings define the basic configuration\ninformation for the virtual instance, from the appearance to the Terms\nof Use for your Users to agree to. Access the Instance Configuration\nsettings from the Control Panel's \\emph{Configuration} → \\emph{Instance\nSettings} section, and select the \\emph{Instance Configuration} category\nunder the \\emph{PLATFORM} section. The Instance Configuration contains\nfour entries:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Appearance\n\\item\n  Contact Information\n\\item\n  General\n\\item\n  Terms of Use\n\\end{itemize}\n\nEach configuration entry is described in the corresponding section\nbelow.\n\n\\section{Appearance}\\label{appearance}\n\nThe Appearance configuration entry defines the default logo and overall\nlook and feel for the virtual instance. It's organized into LOGO and\nLOOK AND FEEL sections:\n\n\\textbf{LOGO:} Change the default logo and check/uncheck the \\emph{Allow\nsite administrators to use their own logo?} checkbox, enabled by\ndefault, to specify whether site administrators can upload a logo when\nthey configure a site. When configuring a new logo, be careful to choose\nan image file that fits the space. Large images might overlap with the\nnavigation.\n\n\\textbf{LOOK AND FEEL:} Set the default theme(s) for the instance and\nControl Panel.\n\n\\section{Contact Information}\\label{contact-information}\n\nThe Contact Information configuration entry specifies how to contact the\norganization that owns the virtual instance. It's divided into several\nsections:\n\n\\textbf{ADDRESSES:} Specify the primary, mailing, shipping, P.O. Box,\netc. address of the organization.\n\n\\textbf{PHONE NUMBERS:} Provide the fax, local, etc. phone numbers for\nthe organization.\n\n\\textbf{ADDITIONAL EMAIL ADDRESSES:} Specify any additional email\naddresses associated with the organization.\n\n\\textbf{WEBSITES:} Specify the public and/or intranet websites for the\norganization.\n\nDevelopers can query for this contact information in their applications.\nNote that you can add and remove additional entries in a section with\nthe plus and minus buttons respectively.\n\n\\section{General}\\label{general}\n\nThe General entry specifies the virtual instance's configuration\ninformation, landing pages, and the associated organization's basic\nlegal information. It has two sections:\n\n\\textbf{Main Configuration:} Configure this information for the virtual\ninstance:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Set the name of the entity responsible for running the virtual\n  instance.\n\\item\n  Set the mail domain.\n\\item\n  Set the virtual host.\n\\item\n  Set the URLs to a CDN configured to serve static resources.\n\\end{itemize}\n\n\\textbf{Navigation:} Set a home page for your virtual instance as well\nas default landing and logout pages. To set these pages, use the page's\nrelative URL that follows the domain. For example, to set the default\nlanding page to \\texttt{http://localhost:8080/web/guest/login}, use\n\\texttt{/web/guest/login}. You can use the variable\n\\texttt{\\$\\{liferay:screenName\\}} as part of the address to redirect\nUsers to their personal pages upon login. For example, the User\n\\texttt{marvin} has this default URL to his personal page:\n\n\\begin{verbatim}\nhttp://localhost:8080/user/marvin\n\\end{verbatim}\n\nTo make sure he's directed there on login, place\n\\texttt{/user/\\$\\{liferay:screenName\\}} in the Default Landing Page\nfield. These URLs can also be set at the system scope, in a\n\\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\ndefault.landing.page.path=\ndefault.logout.page.path=\ncompany.default.home.url=\n\\end{verbatim}\n\nAll virtual instances share the values specified in the properties file.\nChanges made in Instance Settings override the values set in the\nproperties file. For more information, see the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html}{Portal\nProperties documentation}.\n\n\\textbf{Additional Information:} Specify a Legal name, ID, company type,\nSIC code, ticker symbol, industry and industry type for the owner of the\nvirtual instance.\n\n\\section{Terms of Use}\\label{terms-of-use}\n\nThe Terms of Use entry contains everything you need to provide a custom\nTerms of Use agreement for your Users. Terms of Use are important when\nyou need them, but not all Sites do. They're not listed first, but\nthey're one of the first things to configure for your virtual instance,\nwhether you require them or not.\n\nSince the Terms of Use page is enabled by default, one of your first\nactions should be to disable or replace the default, placeholder terms.\nYou can disable the requirement for all Users to read the Terms of Use\nor set the Group ID and Article ID for the Web Content Article that\ncontains your Terms of Use. ``How do I find a web content article's\nGroup ID and Article ID,'' you ask? No problem. The Group ID is the ID\nof the Site the Web Content is associated with. To find the Group/Site\nID, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Menu} → \\emph{Configuration} → \\emph{Settings}.\n\\item\n  Find the Site ID field in the General tab. Enter it into the Group ID\n  field.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/instance-settings-group-id.png}\n\\caption{The Site ID in Site Settings is the Group ID in the terms of\nUse configuration.}\n\\end{figure}\n\nTo find the Web Content Article's ID, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\emph{Site Menu} → \\emph{Content \\& Data} → \\emph{Web\n  Content}.\n\\item\n  Click on your Terms of Use article. Its ID appears at the top of the\n  screen, with the Version and Workflow Status.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/instance-settings-wc-id.png}\n\\caption{The Web Content Article ID is displayed in the edit screen.}\n\\end{figure}\n\nSave the configuration. All your users signing in for the first time see\nyour Terms of Use article. Users must agree to the Terms of Use to\nregister their User Accounts.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/instance-settings-terms-of-use.png}\n\\caption{Turn a Web Content Article into the Site's Terms of Use page.}\n\\end{figure}\n\n\\chapter{User Authentication Instance\nSettings}\\label{user-authentication-instance-settings}\n\nThe User Authentication settings define how Users can authenticate, the\nvarious authentication methods that are required for them, and the\nscreen names and email addresses that are reserved and can't be\nregistered by Users.\n\nAccess the User Authentication settings in the Control Panel's\n\\emph{Configuration} → \\emph{Instance Settings} section, and select the\n\\emph{User Authentication} category under the \\emph{PLATFORM} section.\n\nUser Authentication contains two entries:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  General\n\\item\n  Reserved Credentials\n\\end{itemize}\n\nEach configuration entry is described in the corresponding section\nbelow.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/instance-settings-auth.png}\n\\caption{Configure general authentication behavior and settings for\nexternal authentication systems.}\n\\end{figure}\n\n\\section{General}\\label{general-1}\n\nThe General configuration entry contains several general authentication\nsettings:\n\n\\begin{itemize}\n\\item\n  Authenticate by email address (default), screen name, or User ID (a\n  numerical ID auto-generated in the database---not recommended).\n\\item\n  Enable/Disable automatic log in. If enabled, a User can check a box\n  which will cause the Site to ``remember'' the login information by\n  placing a cookie on the browser. If disabled, Users must always log in\n  manually.\n\\item\n  Enable/Disable forgotten password functionality.\n\\item\n  Enable/Disable request password reset links.\n\\item\n  Enable/Disable account creation by strangers. If running an Internet\n  site, leave this enabled so visitors can create accounts on your Site.\n\\item\n  Enable/Disable account creation by those using an email address in the\n  domain of the company running the Site (which is set on the General\n  page of Instance Settings). This is handy if you're using Liferay to\n  host both internal and external web sites. Make sure all internal IDs\n  are created by administrators but external Users can register for IDs\n  themselves.\n\\item\n  Enable/Disable email address verification. If enabled, Users receive a\n  verification email with a link back to the virtual instance, verifying\n  that the email address they entered is valid.\n\\end{itemize}\n\nBy default, all settings except for the last are enabled. User\nauthentication by email address is an important default for the\nfollowing reasons:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  An email address is unique to the User who owns it.\n\\item\n  People remember their email addresses. A Users who hasn't logged in\n  for a while could forget their screen name.\n\\item\n  If email address isn't used to authenticate, a User might change her\n  email address then forget to update the email address in her profile.\n  If this occurs, no notifications sent by the virtual instance will\n  reach the User. Keeping the email address at the forefront of a User's\n  mind when she logs in helps ensure the User keeps it current.\n\\end{enumerate}\n\n\\section{Reserved Credentials}\\label{reserved-credentials}\n\nThe Reserved Credentials configuration entry specifies the screen names\nand email addresses Users aren't allowed to use. This prevents Users\nfrom creating IDs that look like administrative IDs or that have\nreserved words in their names.\n\nLearn to configure a third party authentication service or set up Single\nSign On (SSO) in the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/securing-product}{security\ndocumentation}.\n\n\\chapter{Users Instance Settings}\\label{users-instance-settings}\n\nThe Users configuration defines the look and feel of the Personal Menu,\nthe default user associations for Users, and defines whether specific\nfields are available when a new User creates an account.\n\nTo access the Users settings, Open the Control Panel and navigate to\n\\emph{Configuration} → \\emph{Instance Settings}, and select the\n\\emph{Users} category under the \\emph{PLATFORM} section.\n\nThe Users Instance configuration contains three entries:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Personal Menu\n\\item\n  Default User Associations\n\\item\n  Fields\n\\end{itemize}\n\nEach configuration entry is described in the corresponding section\nbelow.\n\n\\section{Personal Menu}\\label{personal-menu}\n\nThe Personal Menu configuration entry specifies whether personal\napplications use the same look and feel as the current site or if they\nshould use the look and feel of the My Dashboard pages instead. You can\nalso specify whether the personal menu appears in the Control Menu (as\nit did in past versions of Liferay DXP) by toggling the \\emph{Show in\nControl Menu} option on and off.\n\n\\section{Default User Associations}\\label{default-user-associations}\n\nThe Default User Associations configuration entry defines the default\nSites, Organization Sites, Roles, and User Groups you want all new Users\nassigned to automatically. By default, new Users are only assigned to\nthe Users Role. User groups are handy tools for pre-populating your\nUsers' private Sites with pages, assigning Roles and permissions, and\nmanaging site membership. If you update this configuration after your\nUsers have created their accounts, don't worry. You can apply the\nupdates to existing Users by checking the \\emph{Apply to Existing Users}\ncheckbox. Changes take effect the next time the User signs in.\n\n\\section{Fields}\\label{fields}\n\nThe Fields configuration entry contains settings for enabling/disabling\nthe fields listed below on the Add/Edit User Form:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Autogeneration of screen names\n\\item\n  Birthday field\n\\item\n  Gender field\n\\end{itemize}\n\n\\chapter{More Platform Section Instance\nSettings}\\label{more-platform-section-instance-settings}\n\nThe Instance Settings also contain settings for Infrastructure and\nLocalization, as well as integrations for Analytics and Third Party map\nAPIs. Each configuration entry is described in the corresponding section\nbelow.\n\n\\section{Infrastructure}\\label{infrastructure}\n\nThe Infrastructure category contains settings to specify which content\ntypes are validated during the Import/Export process and whether to\ndelete temporary LAR files during the Import/Export process.\n\nTo access these settings, open the Control Panel and navigate to\n\\emph{Configuration} → \\emph{Instance Settings} and select\n\\emph{Infrastructure} under the PLATFORM heading.\n\n\\section{Localization}\\label{localization}\n\nThe Localization category contains the configuration for setting the\ndefault instance language and time zone for the virtual instance. You\ncan also configure the Language Selector's look and feel from the Widget\nScope.\n\nTo access the Localization settings, Open the Control Panel and navigate\nto \\emph{Configuration} → \\emph{Instance Settings}, and select the\n\\emph{Localization} category under the \\emph{PLATFORM} section.\n\n\\section{Analytics}\\label{analytics}\n\nThe Analytics category defines the available analytics systems for the\nvirtual instance. Enter an analytics system or remove one of the two\npre-configured options (\\texttt{google} and \\texttt{piwik}). Activate\nthese systems here, and configure them at the\n\\href{/docs/7-2/user/-/knowledge_base/u/advanced-site-settings\\#analytics}{site\nlevel}.\n\nTo access Analytics configuration settings, open the Control Panel and\nnavigate to \\emph{Configuration} → \\emph{Instance Settings}, and select\nthe \\emph{Analytics} category under the \\emph{PLATFORM} section.\n\n\\section{Third Party}\\label{third-party}\n\nThe Third Party category specifies the maps API provider for\ngeolocalized assets. Choose OpenStreetMap or Google Maps as the maps API\nprovider.\n\nTo access Third Party configuration settings, open the Control Panel and\nnavigate to \\emph{Configuration} → \\emph{Instance Settings}, and select\nthe \\emph{Third Party} category under the \\emph{PLATFORM} section.\n\n\\chapter{Using Liferay's Script\nEngine}\\label{using-liferays-script-engine}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay DXP provides a robust script engine for executing\n\\href{http://groovy-lang.org/}{Groovy} scripts. You can execute scripts\nto perform maintenance tasks like data cleanup, user maintenance\noperations, bulk invocations of Liferay's API, or even system level\noperations in the scripting console.\n\nThese tutorials cover the following scripting topics:\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/invoking-liferay-services-from-scripts}{Invoking\n  Liferay services}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console}{Running\n  scripts from the script console}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/leveraging-the-script-engine-in-workflow}{Using\n  the script engine with workflow}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/script-examples}{Script\n  examples}\n\\end{itemize}\n\nThe most common use of the scripting console is to invoke Liferay's\nservices, so that's covered first. Familiarity with Liferay's API makes\nthe scripting console a useful tool.\n\n\\chapter{Invoking Liferay Services From\nScripts}\\label{invoking-liferay-services-from-scripts}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nMany scripting scenarios require invoking Liferay services.\n\\href{https://docs.liferay.com/ce/portal/7.2-latest/javadocs/portal-kernel/}{Liferay\n\\texttt{*ServiceUtil} classes} are the fastest and most convenient way\nto invoke Liferay services in the\n\\href{/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console}{script\nconsole}. You can use Groovy to invoke Liferay services the same way you\nwould use Java. Groovy's syntax facilitates writing concise, elegant\nscripts.\n\nThis first example illustrates correct syntax for interacting with\nLiferay services. It uses \\texttt{UserLocalServiceUtil} to retrieve a\nlist of users and print their names to Liferay's log. To do this, you\ncould deploy a module with Java code like this:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport java.util.List;\n\n...\n\nint userCount = UserLocalServiceUtil.getUsersCount();\nList<User> users = UserLocalServiceUtil.getUsers(0, userCount);\n\nfor (User user:users) {\n    System.out.println(\"User Name: \" + user.getFullName());\n}\n\n...\n\\end{verbatim}\n\nOr you could use Groovy---based on Java---and do the whole thing right\nfrom the\n\\href{/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console}{script\nconsole} with the same code:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport java.util.List;\n\nint userCount = UserLocalServiceUtil.getUsersCount();\nList<User> users = UserLocalServiceUtil.getUsers(0, userCount);\n\nfor (User user:users) {\n    System.out.println(\"User Name: \" + user.getFullName());\n}\n\nYou can even make the code somewhat Groovier:\n\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil\n\nuserCount = UserLocalServiceUtil.getUsersCount()\nusers = UserLocalServiceUtil.getUsers(0, userCount)\nfor (user in users){\n    System.out.println(\"User Name: \" + user.getFullName())\n}\n\\end{verbatim}\n\nGroovy scripts that invoke Liferay services are easy to write and\nexecute in the script console.\n\nHow to run scripts is next.\n\n\\section{Related Topics}\\label{related-topics}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console}{Running\nScripts From the Script Console}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/leveraging-the-script-engine-in-workflow}{Leveraging\nthe Script Engine in Workflow}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/script-examples}{Script\nExamples}\n\n\\chapter{Running Scripts From the Script\nConsole}\\label{running-scripts-from-the-script-console}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe script console provides a single view for executing Groovy scripts\nand printing their output. It has predefined variables that facilitate\nprinting output and working with portlets and users. Here you'll learn\nthese things:\n\n\\begin{itemize}\n\\item\n  \\hyperref[running-the-sample-script]{How to execute a script in the\n  script console}\n\\item\n  \\hyperref[predefined-variables]{The predefined variables available in\n  the script console}\n\\item\n  \\hyperref[tips]{Tips for running scripts in the script console}\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} The script console is for system operations and\nmaintenance and not for end users. Limit script console access to portal\nadministrators.\n\n\\noindent\\hrulefill\n\nStart with running the script console's sample script.\n\n\\section{Running the Sample Script}\\label{running-the-sample-script}\n\nHere's how to run the sample script in the script console:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Sign in as an administrator.\n\\item\n  In the Product Menu, navigate to \\emph{Control Panel} →\n  \\emph{Configuration} → \\emph{Server Administration}.\n\\item\n  Click on \\emph{Script}. This is the script console. The default sample\n  script prints the User count to the console output.\n\n\\begin{verbatim}\n// ## Groovy Sample ##\n\nnumber = com.liferay.portal.kernel.service.UserLocalServiceUtil.getUsersCount();\n\nout.println(number);\n\\end{verbatim}\n\\item\n  Click \\emph{Execute} and check the script console \\emph{Output} for\n  the User count.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/groovy-script-sample.png}\n\\caption{The script console's sample Groovy script prints the User count\nto the console's \\emph{Output} section.}\n\\end{figure}\n\nThe Groovy sample invokes the Liferay service utility\n\\texttt{UserLocalServiceUtil} to get the user count. Then it uses\n\\texttt{out} (a built-in \\texttt{PrintWriter}) to write the count to the\nscript console. Note that if you use \\texttt{System.out.println} instead\nof \\texttt{out.println}, your output is printed to Liferay's log file\nrather than to the script console.\n\n\\section{Predefined Variables}\\label{predefined-variables}\n\nHere are the predefined variables available to scripts executed in the\nscript console:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{out} (\\texttt{java.io.PrintWriter})\n\\item\n  \\texttt{actionRequest} (\\texttt{javax.portlet.ActionRequest})\n\\item\n  \\texttt{actionResponse} (\\texttt{javax.portlet.ActionReponse})\n\\item\n  \\texttt{portletConfig} (\\texttt{javax.portlet.PortletConfig})\n\\item\n  \\texttt{portletContext} (\\texttt{javax.portlet.PortletContext})\n\\item\n  \\texttt{preferences} (\\texttt{javax.portlet.PortletPreferences})\n\\item\n  \\texttt{userInfo}\n  (\\texttt{java.util.Map\\textless{}String,\\ String\\textgreater{}})\n\\end{itemize}\n\nThis script demonstrates using the \\texttt{actionRequest} variable to\nget the portal instance's \\texttt{Company}:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.util.*\n\ncompany = PortalUtil.getCompany(actionRequest)\nout.println(\"Current Company:${company.getName()}\\n\")\n\nout.println(\"User Info:\")\nuserInfo.each {\n        k,v -> out.println(\"${k}:${v}\")\n}\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/groovy-script-current-user-info.png}\n\\caption{Here's an example of invoking a Groovy script that uses the\npredefined \\texttt{out}, \\texttt{actionRequest}, and \\texttt{userInfo}\nvariables to print information about the company and current user.}\n\\end{figure}\n\n\\section{Tips}\\label{tips}\n\nKeep these things in mind when using the script console:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  There is no undo.\n\\item\n  There is no preview.\n\\item\n  Permissions checking is not enforced for local services.\n\\item\n  Scripts are executed synchronously. Avoid executing scripts that might\n  take a long time.\n\\end{itemize}\n\nFor these reasons, use the script console cautiously. Test your scripts\non non-production systems before running them on production.\n\nOf course, Liferay's script engine can be used outside of the script\nconsole. Next, you'll learn how workflows leverage Liferay's script\nengine.\n\n\\chapter{Leveraging the Script Engine in\nWorkflow}\\label{leveraging-the-script-engine-in-workflow}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay's Kaleo workflow engine provides a robust system for reviewing\nand approving content in an enterprise environment. Even if you don't\nleverage scripts, it's a powerful and robust workflow solution. Adding\nscripts takes it to the next level. These scripts aren't run from the\nscript console, but are embedded in\n\\href{/docs/7-2/reference/-/knowledge_base/r/crafting-xml-workflow-definitions}{XML\nworkflow definitions} and run during the execution of the workflow.\n\n\\section{Injected Variables}\\label{injected-variables}\n\nUsually when you're scripting in Groovy, you must define your variables.\n\n\\begin{verbatim}\nKaleoInstanceToken kaleoInstanceToken=new KaleoInstanceToken();\n\\end{verbatim}\n\nIn workflow scripts, there are several\n\\href{https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-workflow/portal-workflow-kaleo-runtime-scripting-impl/src/main/java/com/liferay/portal/workflow/kaleo/runtime/scripting/internal/util/ScriptingContextBuilderImpl.java}{pre-defined\nvariables} injected into your script context, to be called without\ndefining them first.\n\n\\section{Variables that are Always\nAvailable}\\label{variables-that-are-always-available}\n\nThese variables are available from anywhere that you can run a workflow\nscript:\n\n\\begin{description}\n\\tightlist\n\\item[\\texttt{kaleoInstanceToken}\n(\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-workflow/portal-workflow-kaleo-api/src/main/java/com/liferay/portal/workflow/kaleo/model/KaleoInstanceToken.java}{\\texttt{KaleoInstanceToken}})]\nA workflow instance and corresponding instance token (the\n\\texttt{KaleoInstanceToken}) are created each time a User clicks\n\\emph{Submit for Publication}. Use the injected token to retrieve its\nID, by calling \\texttt{kaleoInstanceToken.getKaleoInstanceTokenId()}.\nThis is often passed as a method parameter in a script.\n\\item[\\texttt{userId}]\nThe \\texttt{userId} returned is context dependent. Technically, the\nlogic works like this: if the\n\\texttt{KaleoTaskInstanceToke.getcompletionUserId()} is null, check\n\\texttt{KaloeTaskInstanceToken.getUserId()}. If that's null too, call\n\\texttt{KaleoInstanceToken.getUserId()}. It's the ID of the last User to\nintervene in the workflow at the time the script is run. In the\n\\texttt{created} node, this would be the User that clicked \\emph{Submit\nfor Publication}, whereas it's the ID of the reviewer upon exit of the\n\\texttt{review} node of the Single Approver definition.\n\\item[\\texttt{workflowContext}\n(\\texttt{Map\\textless{}String,\\ Serializable\\textgreater{}})]\nThe workflow context is full of useful information you can use in your\nscripts. Usually you'll pass this as a parameter to a method that\nrequires a \\texttt{WorkflowContext} object, but all of the\n\\texttt{WorkflowContext}'s attributes are available in the script as\nwell. The workflow context in the script is context dependent. If a call\nto \\texttt{ExecutionContext.getWorkflowContext()} comes back null, then\nthe workflow context is obtained by\n\\texttt{KaleoInstanceModel.getWorkflowContext()}.\n\\end{description}\n\n\\section{Variables Injected into Task\nNodes}\\label{variables-injected-into-task-nodes}\n\nIf a \\texttt{kaleoTaskInstanceToken} has been created:\n\n\\begin{description}\n\\tightlist\n\\item[\\texttt{kaleoTaskInstanceToken}\n(\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-workflow/portal-workflow-kaleo-api/src/main/java/com/liferay/portal/workflow/kaleo/model/KaleoTaskInstanceToken.java}{\\texttt{KaleoTaskInstanceToken}})]\nThe task's token itself is available in the workflow script. Use it to\nget its ID, to use in other useful programmatic workflow activities,\nlike programmatic assignment.\n\\item[\\texttt{taskName} (\\texttt{String})]\nThe task's own name is accessible (returns the same as\n\\texttt{KaleoTak.getName()}).\n\\item[\\texttt{workflowTaskAssignees}\n(\\texttt{List\\textless{}}\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/portal-kernel/src/com/liferay/portal/kernel/workflow/WorkflowTaskAssignee.java}{\\texttt{WorkflowTaskAssignee}}\\texttt{\\textgreater{}})]\nIf the script is inside a task node, get a \\texttt{List} of its\nassignees.\n\\item[\\texttt{kaleoTimerInstanceToken}\n(\\href{https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-workflow/portal-workflow-kaleo-api/src/main/java/com/liferay/portal/workflow/kaleo/model/KaleoTimerInstanceToken.java}{\\texttt{KaleoTimerInstanceToken}})]\nIf a\n\\href{/docs/7-2/reference/-/knowledge_base/r/workflow-task-nodes\\#task-timers}{task\ntimer} exists, use the \\texttt{kaleoTimerInstanceToken} to get its ID,\nby calling\n\\texttt{kaleoTimerInstanceToken.getKaleoTimerInstanceTokenId()}.\n\\end{description}\n\n\\section{Scripting Examples}\\label{scripting-examples}\n\nThe final step in a workflow runs a script that makes content available\nfor use. The snippet below accesses the Java class associated with the\nworkflow to set content's status to \\emph{approved}.\n\n\\begin{verbatim}\n<script>\n    <![CDATA[\n        import com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil;\n        import com.liferay.portal.kernel.workflow.WorkflowConstants;\n\n        WorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus(\"approved\"), workflowContext);\n    ]]>\n</script>\n<script-language>groovy</script-language>\n\\end{verbatim}\n\nAt virtually any point in a workflow, you can use Liferay's script\nengine to access workflow APIs or other Liferay APIs. There are a lot of\ndifferent ways you could use this. Here are a few practical examples:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Getting a list of users with a specific workflow-related role\n\\item\n  Sending an email to the designated content approver with a list of\n  people to contact if he is unable to review the content\n\\item\n  Creating an alert to be displayed in the Alerts portlet for any user\n  assigned to approve content\n\\end{itemize}\n\nOf course, before you try any of this, you need to know the appropriate\nsyntax for inserting a script into a workflow. In an XML workflow\ndefinition, a script can be used in any XML type that can contain an\n\\texttt{actions} tag: those types are\n\\texttt{\\textless{}state\\textgreater{}},\n\\texttt{\\textless{}task\\textgreater{}},\n\\texttt{\\textless{}fork\\textgreater{}} and\n\\texttt{\\textless{}join\\textgreater{}}. Inside of one of those types,\nformat your script like this:\n\n\\begin{verbatim}\n<actions>\n    <action>\n        <script>\n            <![CDATA[*the contents of your script*]]>\n        </script>\n        <script-language>*your scripting language of choice*</script-language>\n    </action>\n    ...\n</actions>\n\\end{verbatim}\n\nHere's an example of a workflow script created in Groovy. This one is\nused with a \\texttt{Condition} statement in Kaleo. It accesses Liferay's\nasset framework to determine the category of an asset. The script uses\nthe category to determine the correct approval process automatically. If\nthe category \\texttt{legal} has been applied to the asset, the asset is\nsent to the \\texttt{Legal\\ Review} task upon submission. Otherwise, the\nasset is sent to the \\texttt{Default\\ Review} task.\n\n\\begin{verbatim}\n<script>\n    <![CDATA[\n        import com.liferay.portal.kernel.util.GetterUtil;\n        import com.liferay.portal.kernel.workflow.WorkflowConstants;\n        import com.liferay.portal.kernel.workflow.WorkflowHandler;\n        import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;\n        import com.liferay.asset.kernel.model.AssetCategory;\n        import com.liferay.asset.kernel.model.AssetEntry;\n        import com.liferay.asset.kernel.model.AssetRenderer;\n        import com.liferay.asset.kernel.model.AssetRendererFactory;\n        import com.liferay.asset.kernel.service.AssetEntryLocalServiceUtil;\n\n        import java.util.List;\n\n        String className = (String)workflowContext.get(\n            WorkflowConstants.CONTEXT_ENTRY_CLASS_NAME);\n\n        WorkflowHandler workflowHandler =\n            WorkflowHandlerRegistryUtil.getWorkflowHandler(className);\n\n        AssetRendererFactory assetRendererFactory =\n            workflowHandler.getAssetRendererFactory();\n\n        long classPK =\n            GetterUtil.getLong((String)workflowContext.get\n            (WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n        AssetRenderer assetRenderer =\n            workflowHandler.getAssetRenderer(classPK);\n\n        AssetEntry assetEntry = assetRendererFactory.getAssetEntry(\n            assetRendererFactory.getClassName(), assetRenderer.getClassPK());\n\n        List<AssetCategory> assetCategories = assetEntry.getCategories();\n\n        returnValue = \"Default Review\";\n\n        for (AssetCategory assetCategory : assetCategories) {\n            String categoryName = assetCategory.getName();\n\n            if (categoryName.equals(\"legal\")) {\n                returnValue = \"Legal Review\";\n\n                return;\n            }\n        }\n       ]]>\n</script>\n<script-language>groovy</script-language>\n\\end{verbatim}\n\nWithin a workflow, the next task or state is chosen based on the return\nvalue. See some examples of workflow scripts by accessing the\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow\\#embedded-workflows}{embedded\nworkflows} and inspecting the XML.\n\n\\section{Calling OSGi Services}\\label{calling-osgi-services}\n\nHow do you call OSGi services from a workflow script, accounting for the\ndynamic environment of the OSGi runtime, where services your script\ndepends on can disappear without notice?\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/service-trackers-for-osgi-services}{Use\na service tracker}. That way you can make sure your code has access to\nthe service it needs, and if not, do something appropriate in response.\nHere's a little example code to show you how this might look in Groovy:\n\n\\begin{verbatim}\nimport com.liferay.journal.model.JournalArticle;\nimport com.liferay.journal.service.JournalArticleLocalService;\nimport com.liferay.portal.scripting.groovy.internal.GroovyExecutor;\n\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\nServiceTracker<JournalArticleLocalService, JournalArticleLocalService> st;\n\ntry {\n    Bundle bundle = FrameworkUtil.getBundle(GroovyExecutor.class);\n\n    st = new ServiceTracker(bundle.getBundleContext(), JournalArticleLocalService.class, null);\n    st.open();\n\n    JournalArticleLocalService jaService = st.waitForService(500);\n\n    if (jaService == null) {\n        _log.warn(\"The required service 'JournalArticleLocalService' is not available.\");\n    }\n    else {\n        java.util.List<JournalArticle>articles = jaService.getArticles();\n        if (articles != null) {\n            _log.info(\"Article count: \" + articles.size());\n        } else {\n            _log.info(\"no articles\");\n        }\n    }\n}\ncatch(Exception e) {\n    //Handle error appropriately\n}\nfinally {\n    if (st != null) {\n        st.close();\n    }\n}\n\\end{verbatim}\n\nIf you read the article on\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/service-trackers-for-osgi-services}{service\ntrackers}, the only odd looking piece of the above code is the\n\\texttt{getBundle} call: why is \\texttt{GroovyExecutor.class} passed as\na parameter? The parameter passed to the\n\\texttt{FrameworkUtil.getBundle} call must be a class from the OSGi\nbundle executing the workflow script. This is different from the context\nof a plugin project, where you'd want to get the bundle hosting the\nclass where you're making the call (using \\texttt{this.getClass()}, for\nexample). Note that for another scripting engine, you must pass in a\nconcrete class from the particular bundle executing your script.\n\nThe combination of Liferay's script and workflow engines is incredibly\npowerful. Since, however, it enables users to execute code, it can be\ndangerous. When configuring your permissions, be aware of the potential\nconsequences of poorly or maliciously written scripts inside a workflow\ndefinition. For more information on creating workflow definitions, see\nthe \\href{/docs/7-2/user/-/knowledge_base/u/workflow}{workflow\ndocumentation}.\n\n\\section{Related Topics}\\label{related-topics-1}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console}{Running\nScripts From the Script Console}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/script-examples}{Script\nExamples}\n\n\\chapter{Script Examples}\\label{script-examples}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nHere are some examples to help you use Liferay's script console. Note:\nMost of these originated from a\n\\href{https://liferay.dev/blogs/-/blogs/5-tips-to-improve-usage-of-the-liferay-script-console}{Liferay\nblog post}.\n\nThe following scripts are Groovy scripts but they can be adapted to\nother languages.\n\n\\begin{itemize}\n\\item\n  \\hyperref[example-1-presenting-new-terms-of-use-to-users]{Example 1:\n  Presenting New Terms of Use to Users}\n\\item\n  \\hyperref[example-2-embedding-html-markup-in-script-outputs]{Example\n  2: Embedding HTML Markup in Script Outputs}\n\\item\n  \\hyperref[example-3-show-exceptions-in-the-script-console]{Example 3:\n  Show Exceptions in the Script Console}\n\\item\n  \\hyperref[example-4-implement-a-preview-mode]{Example 4: Implement a\n  Preview Mode}\n\\item\n  \\hyperref[example-5-plan-a-file-output-for-long-running-scripts]{Example\n  5: Plan a File Output for Long-Running Scripts}\n\\end{itemize}\n\n\\section{Example 1: Presenting New Terms of Use to\nUsers}\\label{example-1-presenting-new-terms-of-use-to-users}\n\nThis example retrieves user information from the database, makes\nchanges, and then saves the changes in the database. Suppose that your\ncompany has updated the\n\\href{/docs/7-2/user/-/knowledge_base/u/instance-configuration-instance-settings\\#terms-of-use}{terms\nof use} and wants present users with the updated terms of use whenever\nthey sign in next. When they agree to the terms of use, a boolean\nattribute called \\texttt{agreedToTermsOfUse} is set in their user\nrecords. As long as the value of this variable is \\texttt{true}, users\naren't presented with the terms of use when they sign in. If you set\nthis flag to \\texttt{false} for each user, each user must agree to the\nterms of use again before they can sign in.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Enter and execute the following code in the script console:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil\n\nuserCount = UserLocalServiceUtil.getUsersCount()\nusers = UserLocalServiceUtil.getUsers(0, userCount)\n\nfor (user in users) { println(\"User Name: \" + user.getFullName() + \" -- \" +\nuser.getAgreedToTermsOfUse()) }\n\\end{verbatim}\n\n  This code prints each user's \\texttt{agreedToTermsOfUse} attribute\n  value.\n\\item\n  Replace that with this script:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil\n\nuserCount = UserLocalServiceUtil.getUsersCount()\nusers = UserLocalServiceUtil.getUsers(0, userCount)\n\nlong currentUserId = Long.parseLong(userInfo.get(\"liferay.user.id\"))\n\nfor (user in users){\n\n    if(!user.isDefaultUser() && (user.getUserId() != currentUserId)) {\n\n            user.setAgreedToTermsOfUse(false)\n            UserLocalServiceUtil.updateUser(user)\n\n    }\n\n}\n\\end{verbatim}\n\n  This sets each user's \\texttt{agreedToTermsOfUse} attribute to\n  \\texttt{false}. It skips the default user as well as the default admin\n  user that's currently signed in and running the script.\n\\item\n  Click \\emph{Execute}.\n\\item\n  Verify the script updated the records by running the first script\n  again.\n\n  All users (except the default user and your user) have been updated.\n\\end{enumerate}\n\nYou've enabled the new terms of use agreement for all users to accept.\n\n\\section{Example 2: Embedding HTML Markup in Script\nOutputs}\\label{example-2-embedding-html-markup-in-script-outputs}\n\nThe output of the script console is rendered as HTML content. Thus, you\ncan embed HTML markup in your output to change its look and feel. Here's\nan example:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.service.*\n\nnumber = com.liferay.portal.kernel.service.UserLocalServiceUtil.getUsersCount();\nout.println(\n        \"\"\"\n                <div style=\"background-color:black; text-align: center\">\n                        <h1 style=\"color: #37A9CC; font-size:xx-large\">${number}</h1>\n                </div>\n        \"\"\");\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/groovy-script-embed-html-markup.png}\n\\caption{Here's an example of invoking a Groovy script that embeds HTML\nmarkup in the output of the script.}\n\\end{figure}\n\n\\section{Example 3: Show Exceptions in the Script\nConsole}\\label{example-3-show-exceptions-in-the-script-console}\n\nWhen any exception occurs during script execution, the error message is\nalways the same:\n\n\\begin{verbatim}\nYour request failed to complete.\n\\end{verbatim}\n\nThis message gives no detail about the error. To find information about\nthe error and what caused it, you must usually examine the server logs.\n\nYou can, however, use the following technique to make exception details\nappear in the script console. Wrap your code with a try / catch block\nand print the stacktrace to the console output from the catch clause.\nNote that even this technique does not catch script syntax errors.\nHere's an example:\n\n\\begin{verbatim}\ntry {\n        nullVar = null\n        out.println(nullVar.length())\n} catch(e) {\n        out.println(\"\"\"<div class=\"portlet-msg-error\">${e}</div>\"\"\")\n        e.printStackTrace(out)\n}\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/groovy-script-show-exception.png}\n\\caption{Here's an example of a Groovy script that catches exceptions\nand prints exception information to the script console.}\n\\end{figure}\n\n\\section{Example 4: Implement a Preview\nMode}\\label{example-4-implement-a-preview-mode}\n\nSince Liferay's script console does not provide an undo feature, it can\nbe convenient to set up a kind of preview mode. The purpose of a preview\nmode is to determine any permanent effects of a script before any\ninformation is actually saved to the Liferay database. The preview mode\nconsists in using a \\texttt{previewMode} flag which determines whether\nthe operations with permanent effects should be executed or not. If\n\\texttt{previewMode} is \\texttt{true}, all the data that would be\npermanently affected by the script is printed instead. Then you can see\nan outline of the data impacted by the script. If everything is okay,\nswitch the flag so the script can make permanent updates to the\ndatabase.\n\nHere's an example Groovy script that sets users to inactive. Clearly,\nyou'd want to test this with preview mode before running it:\n\n\\begin{verbatim}\nimport java.util.Calendar\nimport com.liferay.portal.kernel.service.*\nimport com.liferay.portal.kernel.model.*\nimport com.liferay.portal.kernel.dao.orm.*\nimport static com.liferay.portal.kernel.workflow.WorkflowConstants.*\n\n//\n// Deactivate users never logged and created since more than 2 years\n//\n\npreviewMode = true // Update this flag to false to really make changes\n\nCalendar twoYearsAgo = Calendar.getInstance()\ntwoYearsAgo.setTime(new Date())\ntwoYearsAgo.add(Calendar.YEAR, -2)\n\nDynamicQuery query = DynamicQueryFactoryUtil.forClass(User.class)\n        .add(PropertyFactoryUtil.forName(\"lastLoginDate\").isNull())\n        .add(PropertyFactoryUtil.forName(\"createDate\").lt(twoYearsAgo.getTime()))\n\nusers = UserLocalServiceUtil.dynamicQuery(query)\n\nusers.each { u ->\n         if(!u.isDefaultUser() && u.getStatus() != STATUS_INACTIVE) {\n                out.println(u.getEmailAddress())\n                if(!previewMode) {\n                        UserLocalServiceUtil.updateStatus(u.getUserId(), STATUS_INACTIVE)\n                }\n         }\n}\n\nif(previewMode) {\n        out.println('Preview mode is on: switch off the flag and execute '\n                + 'again this script to make changes to the database')\n}\n\\end{verbatim}\n\n\\section{Example 5: Plan a File Output for Long-Running\nScripts}\\label{example-5-plan-a-file-output-for-long-running-scripts}\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} The script console is for system operations and\nmaintenance and not for end users. Limit script console access to portal\nadministrators.\n\n\\noindent\\hrulefill\n\nWhen a script has been running for a long time, the console could return\nan error even though the script can continue running and potentially\nconclude successfully. But it's impossible to know the outcome without\nthe corresponding output!\n\nTo bypass this limitation, you can send the output of the script console\nto a file instead of to the console itself or to the Liferay log. For\nexample, consider this script:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.service.*\nimport com.liferay.portal.kernel.dao.orm.*\n\n// Output management\n\nfinal def SCRIPT_ID = \"MYSCRIPT\"\noutputFile = new File(\"\"\"${System.getProperty(\"liferay.home\")}/scripting/out-${SCRIPT_ID}.txt\"\"\")\noutputFile.getParentFile().mkdirs()\n\ndef trace(message) {\n        out.println(message)\n        outputFile << \"${message}\\n\"\n}\n\n// Main code\n\nusers = UserLocalServiceUtil.getUsers(QueryUtil.ALL_POS, QueryUtil.ALL_POS)\nusers.each { u ->\n        trace(u.getFullName())\n}\n\\end{verbatim}\n\nThe script above creates a subfolder of\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\ncalled \\texttt{scripting} and saves the script output to a file in this\nfolder. After running the script above, you can read the generated file\nwithout direct access to the file system. Here's a second script that\ndemonstrates this:\n\n\\begin{verbatim}\nfinal def SCRIPT_ID = \"MYSCRIPT\"\noutputFile = new File(\"\"\"${System.getProperty(\"liferay.home\")}/scripting/out-${SCRIPT_ID}.txt\"\"\")\nout.println(outputFile.text)\n\\end{verbatim}\n\nOne advantage of using a dedicated output file instead of using a\nclassic logger is that it's easier to get the script output data back.\nGetting the script output data would be more difficult to obtain from\nthe portal log, for example, because of all the other information there.\n\n\\section{Related Topics}\\label{related-topics-2}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console}{Running\nScripts From the Script Console}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/leveraging-the-script-engine-in-workflow}{Leveraging\nthe Script Engine in Workflow}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/using-liferays-script-engine}{Using\nLiferay's Script Engine}\n\n\\chapter{Custom Fields}\\label{custom-fields}\n\nHave you ever wondered why there's no \\emph{Head Circumference} field in\nthe form for adding users to Liferay DXP? Probably because most sites\nbased on Liferay DXP don't need it. If you're an administrator at the\nLunar Resort, however, you certainly need to know your guests' head\ncircumference so you can provide them with a properly fitting helmet.\n\nMany of Liferay DXP's assets and resources let you add new fields to\ntheir edit forms. Here's the complete list:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Blogs Entry\n\\item\n  Calendar Event\n\\item\n  Document\n\\item\n  Documents Folder\n\\item\n  Knowledge Base Article\n\\item\n  Knowledge Base Folder\n\\item\n  Message Boards Category\n\\item\n  Message Boards Message\n\\item\n  Organization\n\\item\n  Page\n\\item\n  Role\n\\item\n  Site\n\\item\n  User\n\\item\n  User Group\n\\item\n  Web Content Article\n\\item\n  Web Content Folder\n\\item\n  Wiki Page\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Developer Use Case:} Adding custom fields to Liferay DXP\nresources affords flexibility to developers. Suppose you must limit the\nnumber of users that can be assigned to a particular Role. First an\nadministrator creates a custom field called \\emph{max-users} for the\nRole. A developer then creates a module that inserts logic before a user\nis added to that Role. If the logic detects that the maximum number of\nRole users would be exceeded by completing the action, an exception is\nthrown and the action does not complete.\n\n\\noindent\\hrulefill\n\n\\section{Adding Custom Fields}\\label{adding-custom-fields}\n\nTo add custom fields, find the Custom Fields entry beneath the Control\nPanel's Configuration heading.\n\nTo add a custom field to one of the listed entities,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Choose a resource by clicking on it.\n\\item\n  Click the add (\\includegraphics{./images/icon-add.png}) button.\n\\item\n  Choose a field type: Text Area, Input Field, Dropdown, Checkbox,\n  Radio, Geolocation, Date, True/False.\n\\item\n  Add a name that's used as a key for accessing the field\n  programmatically.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** The Key you enter here is the name of the new field. It's stored\n in the database and used by developers to access the custom field with the\n `<liferay-ui:custom-attribute />` tag. It is also used to label the\n field in the UI.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  Choose the Data Type of field and set any advanced properties.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/custom-fields-user-head-circumference.png}\n  \\caption{At The Lunar Resort, a Head Circumference field is necessary\n  for all users.}\n  \\end{figure}\n\\item\n  Click Save.\n\\end{enumerate}\n\nThat's it.\n\nOnce you have a custom field for a resource, go find it in the UI of the\nresource. First find the UI location for the resource, and all its\ncustom fields are displayed in a Custom Fields panel. For example,\nconsider the Users UI:\n\nNavigate to \\emph{Control Panel → Users and Organizations}. Click on a\nUser to open the Edit User form and scroll down to find your custom\nfield.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/custom-fields-panel.png}\n\\caption{The Custom Fields panel is found at the bottom of the Edit User\nform.}\n\\end{figure}\n\nYou can also leverage your custom field in Content Pages and Display\nPage Templates. See\n\\href{/docs/7-2/user/-/knowledge_base/u/content-page-elements\\#editable-elements}{Editable\nElements} for more information.\n\n\\section{Editing a Custom Field}\\label{editing-a-custom-field}\n\nYou can't change the key or field type of a custom field, but you can\ndelete it and create a new one if necessary.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/custom-fields-configuration.png}\n\\caption{The exact Custom Fields configuration options you use depend on\nthe field type you choose.}\n\\end{figure}\n\nEdit an individual custom field's permissions by clicking the field's\nkebab menu (\\includegraphics{./images/icon-actions.png}), then\n\\emph{Permissions}. Permission can be granted or removed for these\nactions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Delete\n\\item\n  Permissions\n\\item\n  Update\n\\item\n  View\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/custom-fields-edit.png}\n\\caption{You can delete a custom field, edit it, or configure its\npermissions.}\n\\end{figure}\n\nCustom fields make many of Liferay DXP's entities extensible directly\nfrom the administrative user interface. Use them as is or combine them\nwith some back-end code, and you have yet another powerful, flexible\nfeature at your disposal. As they're fond of saying at The Lunar Resort,\n``The sky is certainly not the limit.''\n\n\\chapter{Managing Users}\\label{managing-users}\n\nEver heard a retailer advertise as a ``one stop shop'' for anything you\nwant? The idea is they have so much stuff that whatever you want is\nprobably there. Liferay's Control Panel is like this. Where do you\ncreate Users, Organizations, or Sites? Where do you configure\npermissions and plugins and pretty much anything else? You do it from\nthe Control Panel.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/usrmgmt-control-panel.png}\n\\caption{Administrators can access the Control Panel from the Product\nMenu.}\n\\end{figure}\n\nThe Control Panel is divided into six main areas: Users, Sites, Apps,\nConfiguration, and Workflow. The Users section lets you create and\nmanage Users, Organizations, User Groups, Roles, and Password Policies.\nIf monitoring has been enabled, you can also view all the live sessions\nof your Users.\n\n\\noindent\\hrulefill\n\n\\textbf{Anonymous User:} \\emph{Anonymous Anonymous} is used for the new\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-user-data}{Managing\nUser Data} functionality. Created the first time an administrator clicks\n\\emph{Delete Personal Data} for a User, \\emph{Anonymous Anonymous} is a\ndeactivated User assigned\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-user-data\\#anonymizing-data}{anonymized\nassets}. The Anonymous User is configurable, so the name and\nconfiguration details might be different in your virtual instance.\n\n\\noindent\\hrulefill\n\nBegin exploring Liferay's User Management functionality by reading about\nadding and editing users.\n\n\\chapter{Users and Organizations}\\label{users-and-organizations}\n\n\\emph{Users} and \\emph{Organizations} are fundamental entities. If your\nsite requires people (even just a set of site administrators) to have\naccounts to do anything, you need to know about users. If your users are\nat all divided hierarchically, like into departments, you'll find that\norganizations are helpful.\n\nYou're probably not surprised to hear that Users and Organizations are\nmanaged in the Control Panel's \\emph{Users and Organizations} section.\nIf it were any different, it'd be weird.\n\nConsider the Lunar Resort site. Consider what you'd do if\n\n\\begin{itemize}\n\\tightlist\n\\item\n  An employee leaves the company to join that pesky competitor, Martian\n  Resort and Luxury Spa.\n\\item\n  An employee joins the resort as a new Mechanical Crew member.\n\\item\n  An employee is promoted from Crew Supervisor to Department Head and\n  needs the requisite permissions.\n\\item\n  You need to organize the users by department.\n\\item\n  A new department is added to the Lunar Resort and the employees need\n  their own internal website.\n\\item\n  An employee gets married, and their name changes.\n\\end{itemize}\n\nThe user tasks listed above are all resolved in the Users and\nOrganizations section of the Control Panel.\n\n\\section{What are Users?}\\label{what-are-users}\n\nIn case there's any confusion over the term, a User is an entity that\ncan sign into the portal and do something. Generally a User has more\nprivileges, called Permissions, than a Guest of your site, who does not\nsign in. Users are assigned Roles, and Roles define the User's\nprivileges.\n\nUnderstanding Users is pretty straightforward. Organizations are a bit\ntrickier, but a smart administrator like you is undoubtedly up to the\nchallenge. Read more about Organizations\n\\href{/docs/7-2/user/-/knowledge_base/u/organizations}{here}.\n\nThe remaining articles in this section give you guidance on managing\n(creating, deleting, editing, and more) Users and Organizations.\n\n\\chapter{Adding, Editing, and Deleting\nUsers}\\label{adding-editing-and-deleting-users}\n\nAt the root of managing Users is adding, editing, and deleting them. As\nlong as you're the Administrative user, you can do all these things and\nmore.\n\n\\section{Adding Users}\\label{adding-users}\n\nHere's how to edit Users:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the Product Menu, click \\emph{Control Panel} → \\emph{Users} →\n  \\emph{Users and Organizations}.\n\\item\n  In the Users tab, click the \\emph{Add} button .\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/usrmgmt-add-user.png}\n  \\caption{Add Users from the Users and Organizations section of the\n  Control Panel.}\n  \\end{figure}\n\\item\n  Fill out the Add User form and click \\emph{Save}. At a minimum,\n  provide a Screen Name, First Name, Last Name, and Email Address for\n  the User.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n Note: Screen names and email addresses are not interchangeable. A screen\n name cannot contain an `@` symbol because it is used in the URL to a User's\n private page.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nThe Add User functionality is split over several independent forms. Saving\nthe first form creates the User, and then you'll see a success message\nsaying\n\n    Success. Your request completed successfully. \n\\end{verbatim}\n\nAfter submission of the first form, you see a larger form with many\nsections. The one you're on is the Information section. To the left is a\nnavigation pane where you can continue configuring the user you're\nadding by clicking through the available sections. The options in the\nleft menu change as you click through the tabs at the top. Peruse the\nsections for the three tabs (General, Contact, Preferences) and fill in\nall the applicable information.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/add-user-forms-menu.png}\n\\caption{At a minimum, enter a screen name, email address, and first\nname to create a new user account. Then you'll be taken to the\nInformation form and can continue configuring the user.}\n\\end{figure}\n\nYou don't have to fill anything else out right now. Just note that when\nthe user account was created, a password was automatically generated. If\nLiferay was correctly installed and a\n\\href{/docs/7-2/user/-/knowledge_base/u/server-administration}{mail\nserver was set up}, an email message with the User's new password was\nsent to the User's email address.\n\nIf you haven't set up a mail server, click the \\emph{Password} item from\nthe General menu and manually set a password for your new user. Enter\nthe new password twice.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/usrmgmt-require-password-reset.png}\n\\caption{Enter the password twice to manually set the password for a\nuser. If the Password Policy you're using is configured to allow it,\nselect whether to require the user to reset their password the first\ntime they sign in to the portal.}\n\\end{figure}\n\n\\section{Editing Users}\\label{editing-users}\n\nIf you click on \\emph{Users and Organizations} in the Control Panel,\nyou'll see your own user's account in the list of Users, along with any\nothers. To change something about a particular user, click the\n\\emph{Actions} button (\\includegraphics{./images/icon-actions.png}) next\nto that user.\n\nChoosing \\emph{Edit} takes you back to the Edit User page where you can\nmodify any aspect of the User account including the screen name, email\naddress, first name, last name, Site and Organization memberships,\nRoles, etc.\n\nChoosing \\emph{Permissions} allows you to define which Roles have\npermissions to edit the User.\n\nChoosing \\emph{Manage Pages} allows you to configure the personal pages\nof a User.\n\nChoosing \\emph{Impersonate User} opens another browser window that loads\nthe site as if you were the User so you can test your User management on\na User to make sure you're achieving the desired behavior, without\nhaving to repeatedly log out of your administrator account and into the\nUser's account.\n\nChoosing \\emph{Deactivate} deactivates the user's account. The User is\nstill in your database along with all the rest of your Users, but the\naccount is deactivated, so the User cannot sign in to the portal. You\ncan toggle between active and inactive Users in the Users view. If all\nthe Users are active, this filtering option doesn't appear.\n\nChoosing \\emph{Erase Personal Data}\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-user-data}{deletes the\nUser's personal data}.\n\nChoosing \\emph{Export Personal Data} lets you\n\\href{/docs-7-2/user/-/knowledge_base/u/exporting-user-data}{download\nthe User's personal data}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/usrmgmt-active.png}\n\\caption{You can choose whether to view active or inactive (deactivated)\nportal users in the users list found at \\emph{Product Menu} →\n\\emph{Control Panel} → \\emph{Users} → \\emph{Users and Organizations}.}\n\\end{figure}\n\nMost Users can't perform any of the above actions. In fact, most Users\nwon't have access to the Control Panel at all. You can perform all of\nthe above functions because you have administrative access.\n\n\\section{Deleting Users}\\label{deleting-users}\n\nYou must be careful when deleting Users. To guard against accidental\ndeletion of Users, a two-step process must be followed: deactivate\nfirst, then delete.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Find the User to delete in the Users tab of \\emph{Control Panel} →\n  \\emph{Users} → \\emph{Users and Organizations}. If you have a lot of\n  Users, save time by searching for the User.\n\\item\n  Click the \\emph{Actions} menu for the User and select\n  \\emph{Deactivate}. You're asked to confirm that you want to deactivate\n  the User. Click \\emph{OK}.\n\n  You'll see a success message and the User disappears, but isn't gone\n  yet.\n\\item\n  By default the Users table displays only Active users. Click on\n  \\emph{Filter and order} in the top of the table and a dropdown menu\n  appears. Click \\emph{Inactive}, and you can see the User you just\n  deactivated.\n\\item\n  Click the Actions menu again, and click \\emph{Delete} if you really\n  mean to delete the User. Confirm that you want to delete the User, and\n  now the User is gone. This time, it's for real.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Deactivated Users:} Deactivating a User means the User can't log\nin to the portal. He/she has no more permissions in the Sites and pages\nof the portal than a guest, although the account still exists in the\nsystem.\n\nUsers are reactivated when an administrator finds them in the Users\ntable (be sure you're filtering the table results by Deactivated users),\nclicks the Actions menu, and selects Activate. There's no confirmation\nwindow for activation: they're automatically restored to their former\nstatus once Activate is clicked.\n\n\\noindent\\hrulefill\n\nNow you understand the basic principles of User administration. There\nare important additional topics in the next article that you should\nconsider mandatory information for all portal administrators, so do\ncontinue reading.\n\n\\chapter{User Management: Additional\nTopics}\\label{user-management-additional-topics}\n\nYou've learned the basics on adding and editing Users, but there are\nadditional important topics that go beyond the most basic tasks an\nadministrator must perform. Read on to learn about these.\n\n\\section{Password Resets}\\label{password-resets}\n\nThe Add User functionality includes a \\emph{Require Password Reset}\ncheckbox at the bottom of the Password form. The default password policy\ndoes not even allow administrators to deselect this option. As the\nadministrator, however, you can modify the default password policy so\nthat this box becomes usable.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Password Policies} in Control Panel → Users.\n\\item\n  Click on the \\emph{Default Password Policy}.\n\\item\n  Deselect the \\emph{Change Required} switcher in the Password Changes\n  section. Now you can decide whether users you add must reset their\n  passwords.\n\\end{enumerate}\n\nSee \\href{/docs/7-2/user/-/knowledge_base/u/password-policies}{Password\nPolicies} for more information on editing the default policy or creating\nyour own.\n\n\\section{Adding an Administrative\nUser}\\label{adding-an-administrative-user}\n\nIf you're setting things up for the first time, you're likely to be\nusing the default administrator account, the account of one of those\nfamous Liferay Administrators, \\emph{Test Test} or her cousin, \\emph{Joe\nBloggs}. Because these are default accounts, hackers know about them, so\nit's better to set up your own administrator account. Add a user with\nyour information, then give your user account the same administrative\nrights as the default administrator's account:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Roles} link in the left navigation pane (in the\n  \\emph{Edit User} page's \\emph{General} tab). This page of the form\n  shows the Roles to which your account is currently assigned. No Roles\n  appear by default (the User role does not appear since it can't be\n  removed).\n\\item\n  Click \\emph{Select} under Regular Roles and assign the Administrator\n  Role to your user account. A dialog box pops up with a list of all the\n  regular (portal-scoped) Roles in the portal. Select the Administrator\n  Role from the list (click \\emph{Choose}). The dialog box disappears\n  and the Role is added to the list of Roles associated with your\n  account. You are now a portal administrator. Log out and then log back\n  in with your own user account.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Power Users:} Users are not assigned the Power User Role by\ndefault. The Power User Role grants more permissions than the User Role.\nIf the User Role is sufficient for you, ignore the Power User Role.\nAlternatively, use it to provide a second level of User permissions and\nassign it to those Users. If there are certain custom permissions that\nyou'd like all of your portal Users to have, you can grant these\npermissions to the User Role. You can also customize the default Roles a\nnew User receives via \\emph{Default User Associations}. This is covered\nin\n\\href{/docs/7-2/user/-/knowledge_base/u/setting-up-a-virtual-instance}{Setting\nUp a Virtual Instance}.\n\n\\noindent\\hrulefill\n\nIn production, you should always delete or disable the default\nadministrator account to secure your portal.\n\n\\section{Gender}\\label{gender}\n\nTo collect data on users' genders, enable the binary gender field in the\n\\emph{Add User} form or create a\n\\href{/docs/7-2/user/-/knowledge_base/u/custom-fields}{custom field}\nthat meets your needs.\n\nEnable the binary field by including the following in\n\\texttt{portal-ext.properties}:\n\n\\begin{verbatim}\n`field.enable.com.liferay.portal.kernel.model.Contact.male=true`\n\\end{verbatim}\n\n\\section{User Profile Pictures}\\label{user-profile-pictures}\n\nUsers have profile pictures. Administrative Users can upload images in\nthe Edit User form, and any User can update her own account information,\nincluding image, from her personal site (\\emph{My account} →\n\\emph{Account Settings}).\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/usrmgmt-ray-avatar.png}\n\\caption{Upload images for user avatars in the Edit User form.}\n\\end{figure}\n\nIf no image is explicitly uploaded for a User's profile picture, a\ndefault User icon is assigned as the User avatar. By default the User's\ninitials are displayed (First Name then Last Name) over a random color.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/users-default-user-image.png}\n\\caption{The default user profile picture is an icon with the user\ninitials over a randomly colored bubble.}\n\\end{figure}\n\nIf the initials-based approach for generating User profile pictures\nisn't suitable for your portal, disable the inclusion of Users' initials\nin the default icons:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings}.\n\\item\n  In the Platform section, click \\emph{Users} → \\emph{User Images}.\n\\item\n  Deselect \\emph{Use Initials for Default User Portrait}.\n\\end{enumerate}\n\nNow, instead of the default icon, which is a colorful circle containing\nthe user's initials, the icon is a gray circle containing the\napproximate shape of a human being.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/user-image-not-initials.png}\n\\caption{If you disable the default initials-based profile picture, this\nicon is used instead.}\n\\end{figure}\n\nThis is just the default. To override it with your own default image:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create at least one image that is a 100x100 px square. Place it\n  somewhere on the application server's classpath. For example, in\n  Tomcat you could place it in the\n  \\texttt{tomcat/webapps/ROOT/WEB-INF/classes} folder.\n\\item\n  Set the following property in a \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\nimage.default.user.portrait=image-filename-here.png\n\\end{verbatim}\n\n  This overrides the value of this\n  \\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html}{portal\n  property}:\n\n\\begin{verbatim}\nimage.default.user.portrait=com/liferay/portal/dependencies/user_portrait.png\n\\end{verbatim}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **NOTE:** If you are using the binary field to collect information on users'\n genders (see above), then you'll have two default images to override. Set\n these properties instead:\n\n     image.default.user.female.portrait=image-filename.png\n     image.default.user.male.portrait=image-filename.png\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\tightlist\n\\item\n  Restart the application server.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} There's a way to adjust which initials are displayed and\nin what order, so you can make the default user icon (with the user\ninitials) work for your locale. These settings are configured in a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/using-liferays-localization-settings}{Language\nSettings module}, so kidnap a friendly developer, give him a cup of\ncoffee, and tell him the settings you want to change:\n\n\\texttt{lang.user.default.portrait=initials} sets the type of icon to\nuse for avatars. The default value is \\emph{initials}. If set to\ninitials, the next property configures which initials to display, and in\nwhat order. Alternatively, specify \\emph{image}, which gives you the\nsame non-initials default image shown above.\n\n\\texttt{lang.user.initials.fields=first-name,last-name} determines which\ninitials appear in the user portrait and in what order. The setting here\nonly matters if \\texttt{lang.user.default.portrait} is set to\n\\emph{initials}. Valid values are first name, middle name, last name,\nwith first and last name as the defaults.\n\n\\noindent\\hrulefill\n\n\\section{Numeric Screen Names}\\label{numeric-screen-names}\n\nIn prior versions, numeric user screen names were disabled out of the\nbox via the default portal property\n\n\\begin{verbatim}\nusers.screen.name.allow.numeric=false\n\\end{verbatim}\n\nOther user management systems (LDAP, for example) did not have the same\nrestriction, which made importing users more difficult. Administrators\nfirst had to set the above property to \\texttt{true} before importing\nand hope that no screen names conflicted with site IDs. In 7.0, this\nproperty defaults to \\texttt{true} and there's no danger of numeric\nscreen names conflicting with site IDs:\n\n\\begin{verbatim}\nusers.screen.name.allow.numeric=true\n\\end{verbatim}\n\nThis means you're free to set a user screen name to \\emph{24601}, or\nwhatever other number you can think of, and imports from systems that\nallow numeric screen names go more smoothly. That's everything you need\nto know to take advantage of this feature. Keep reading to understand\nwhat enabled the change.\n\nBecause users have personal sites, the URL to user \\emph{24601}'s\npersonal site is\n\n\\begin{verbatim}\nhttp://localhost:8080/web/24601\n\\end{verbatim}\n\nMeanwhile, a default site URL to cleverly named \\emph{Test Site} is\n\n\\begin{verbatim}\nhttp://localhost:8080/web/test-site\n\\end{verbatim}\n\nThere's no conflict here, but two conditions could easily lead to one:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\emph{Test Site}'s group ID matches the number chosen for the user's\n  screen name. Each site has a unique numeric identifier in the\n  database, called group ID. There's nothing stopping it from matching\n  the user's numeric screen name, so it could easily be \\texttt{24601}\n  just like the hypothetical user above.\n\\item\n  A site administrator comes along and changes the site's friendly URL\n  to match its \\texttt{groupId}. Hello, URL conflict! Now the site's URL\n  matches the user's URL:\n\n\\begin{verbatim}\nhttp://localhost:8080/web/24601\n\\end{verbatim}\n\\end{enumerate}\n\nThis conflict is no longer possible. In 7.0, a site's friendly URL is\nnot allowed to be numeric. See for yourself:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the site's \\emph{Configuration} → \\emph{Site Settings} →\n  \\emph{Site URL} section.\n\\item\n  In the Friendly URL section, enter \\emph{24601} and save the form. A\n  failure message appears if you don't have a user with the matching\n  screen name:\n\n\\begin{verbatim}\nThe friendly URL may conflict with another page.\n\\end{verbatim}\n\n  You'll see this failure message if there's an existing conflict with a\n  user screen name:\n\n\\begin{verbatim}\nPlease enter a unique friendly URL. Site [user-first-name user-last-name] has the same friendly URL.\n\\end{verbatim}\n\\end{enumerate}\n\nNext, learn about collecting users in organizations.\n\n\\chapter{Organizations}\\label{organizations}\n\nAn \\emph{Organization} groups\n\\href{/docs/7-2/user/-/knowledge_base/u/adding-editing-and-deleting-users}{\\emph{Users}}\nhierarchically. For example, you can model a company's departments\n(i.e., Human Resources and Customer Support) with Organizations.\nOrganizations often have their own Sites. The \\emph{how-to} portion of\nmanaging Organizations is in the next article,\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-organizations}{Managing\nOrganizations}. This article contains important conceptual information\non what Organizations are and when they're needed.\n\nMany simple portal designs don't use Organizations at all; they only use\nsites. The main purpose of Organizations is to enable distributed User\nmanagement. Portal administrators can delegate some user management\nresponsibilities to Organization administrators. If you don't anticipate\nneeding to delegate User management responsibilities, your portal design\nprobably doesn't need to include Organizations.\n\n\\noindent\\hrulefill\n\n\\textbf{User Groups and Organizations:} It's easy to confuse User Groups\n(covered in a separate article) with Organizations since they both group\nUsers. User Groups are an ad hoc collection of Users, organized for a\nspecific function. In the Lunar Resort, if you wanted a group of\nbloggers, for example, it wouldn't make sense to assign the Sales\nDepartment the role of blogging (see the article on Roles if you're not\nsure what they are). The Sales Department users could blog whenever a\nnew T-shirt design became available in the Lunar Resort store, but they\nprobably wouldn't be as diligent about announcing the new Rover Racing\nschedule. Instead, creating a User Group containing one individual from\neach department who is responsible for blogging would make more sense.\nRead the article on User Groups to learn more about how to use them in\nyour portal.\n\n\\noindent\\hrulefill\n\n\\section{When to Use Organizations}\\label{when-to-use-organizations}\n\nTo decide whether your portal design should include Organizations, think\nabout its function. A photo-sharing web site could be powered by Sites\nonly. On the other hand, Organizations are useful for corporations or\neducational institutions since their users can be placed into a\nhierarchical structure. Don't think that Organizations are only for\nlarge enterprises, though. Any group hierarchy, from large government\nagencies all the way down to small clubs, can be modeled with\nOrganizations. Also, don't think that you must decide between an\nOrganization-based structure or a Site-based structure for assembling\nyour portal's Users. Users can belong both to Organizations and to\nindependent Sites. For example, a corporation or educational institution\ncould create a social networking site open to all Users, even ones from\nseparate Organizations.\n\nTo illustrate what an Organization is, consider a potential Organization\nof the Lunar Resort's Intranet. The company hierarchy has three tiers:\nThe Lunar Resort, its departments, and divisions within each department.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Lunar Resort--The top-level Organization.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Physical Plant Department--Department of users that keep the place\n    running.\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      Grounds Crew--Users that maintain the grounds.\n    \\item\n      Janitorial Crew--Users who keep the resort clean.\n    \\item\n      Mechanical Crew--Users who fix stuff, like lunar rovers.\n    \\end{itemize}\n  \\item\n    Recreation Department--A department that makes sure much fun is had\n    by guests of the Lunar Resort.\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      Golf Instructors--Teach guests how to golf on the moon.\n    \\item\n      Rover Race Instructors--Teach guests how to drive the lunar\n      rovers.\n    \\item\n      Lunar Sherpas--Lead guests on moon hikes.\n    \\end{itemize}\n  \\item\n    Sales Department--A department of users who sell things to Lunar\n    Resort guests.\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      Up-sale Group--Make sure guests know how easy it is to improve\n      their stay by spending more money.\n    \\item\n      Souvenir and Memorabilia Group--Peddle souvenirs to Lunar Resort\n      guests.\n    \\item\n      Retail Group--Maintain the Lunar Resort store, which contains\n      basic necessities, since guests are coming all the way from Earth.\n    \\end{itemize}\n  \\item\n    Sentient Organism Resources Department--Department of Users that\n    hire, fire and regulate intra-company relationships. We'd call it\n    Human Resources, but what's stopping Martians from applying?\n    Nothing!\n  \\end{itemize}\n\\end{itemize}\n\nEach department is a sub-Organization of the resort, and each division\nis a sub-Organization of the department.\n\n\\section{What can Organization Administrators\nDo?}\\label{what-can-organization-administrators-do}\n\nWhenever you have a collection of Users that fits into a hierarchical\nstructure, you can use Organizations to model those Users. Organization\nadministrators can manage all the Users in their Organization \\emph{and}\nin any sub-Organization. Referring to the hierarchy above, for example,\nan Organization administrator of the Lunar Resort could manage any Users\nbelonging to the resort itself, to any of the departments, or to any of\na department's subdivisions. An Organization Administrator of the\nPhysical Plant Department can manage any Users belonging to the Physical\nPlant Department itself, or to the Grounds Crew, the Janitorial Crew, or\nthe Mechanical Crew. However, an administrator of the Physical Plant\nDepartment can't manage Users belonging to the Recreation Department or\nthe Retail Group organization.\n\nOrganizations and sub-Organization hierarchies can nest to unlimited\nlevels. Users can be members of one or many Organizations. The rights of\nan Organization administrator apply both to his/her Organization and to\nany child Organizations. Members of child Organizations are implicit\nmembers of their parent Organizations. This means, for example, that\nmembers of child Organizations can access the private pages of their\nparent Organizations. This behavior can be customized in the\n\\texttt{Organizations}\n\\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Organizations}{section\nof the portal-ext.properties} file where the properties specific to\norganizations are listed.\n\nSince Organizations are designed for distributed user administration,\nOrganization Administrators have an entirely different set of privileges\nthan Site Administrators. Site Administrators are responsible for the\npages, portlets, content, and membership of their Sites. To this end,\nthey can set the membership type to Open, Restricted, or Private. They\ncan also add Users to or remove Users from their Sites but cannot manage\nthe Users themselves. If an Organization has a Site attached to it, the\nOrganization Administrator has the same rights as a Site Administrator\nfor managing the Site's content, but an Organization Site's members are\nthe members of the Organization. Thus Organization administrators have\nmore user management permissions than Site administrators: they can edit\nusers belonging to their Organization or any sub-Organization. They\ncannot add existing portal Users to their organization, but they can\ncreate new Users within their Organization. Only portal administrators\ncan add existing users to an Organization.\n\nOrganization Administrators can't access the Control Panel by default,\nbut it's not necessary. In their personal Sites, Organization\nadministrators can click the \\emph{My Organizations} link to gain access\nto any Organizations they manage.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/orgs-my-organizations.png}\n\\caption{The My Organizations application lets Organization\nAdministrators manage their organizations in their personal site.}\n\\end{figure}\n\n\\section{Organization Roles and\nPermissions}\\label{organization-roles-and-permissions}\n\nA huge time-saving benefit of including Organizations into your portal\ndesign is that Organization administrators can assign\nOrganization-scoped Roles to members of the entire Organization. For\nexample, consider an IT Security group in a corporate setting. You could\nhave a sub-Organization of your IT organization that handles security\nfor all applications company-wide. If you grant the IT Security\nOrganization the portal administrator Role, all members of the\nOrganization get administrative access to the entire system. Suppose\nfurther that a User in this Organization was later hired by the Human\nResources department. The simple act of removing the User from the IT\nSecurity Organization also removes the User's administrative privileges,\nsince the privilege came from the IT Security Organization's Role. By\nadding the User to the HR Organization, any roles the HR Organization\nhas (such as access to a benefits system in the portal) are transferred\nto the User. In this manner, you can design your portal to correspond\nwith your existing organization chart and Users' permissions are granted\naccording to their positions in the chart.\n\nOf course, this is only one way to set things up. If you have more\ncomplex requirements for permissions within an Organization, you can\ncreate custom Organization-scoped Roles to assemble the permissions you\nwish to grant to particular Users. Alternatively, attach a Site to your\nOrganization and use Site Teams to assemble the sets of permissions (see\nbelow). See the\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{Roles and\nPermissions article} for more detail.\n\n\\section{Organization Sites}\\label{organization-sites}\n\nDoes your Organization need to have its own Site? If an organization has\nan attached Site, the Organization's administrators are treated as the\nSite administrators. This means that they can manage the pages,\nportlets, and content of the Site as well as the Users of the\nOrganization. Members of an Organization with an attached Site are\ntreated as members of the Organization's Site. This means that they can\naccess the private pages of the Organization's Site, along with any\nportlets or content there. Attaching Sites to Organizations allows\nportal administrators to use Organizations to facilitate distributed\nportal administration, not just distributed User administration.\n\nThat's a lot of information on Organizations. Next, learn how to create\nand manage Users and Organizations.\n\n\\chapter{Managing Organizations}\\label{managing-organizations}\n\nIf you're not entirely sure what Organizations are or whether you need\nthem, start\n\\href{/docs/7-2/user/-/knowledge_base/u/organizations}{here}. This\narticle gets right to the practical stuff: how to manage Organizations.\n\n\\section{Adding Organizations}\\label{adding-organizations}\n\nAdd an Organization (perhaps start by adding the \\emph{Physical Plant\nDepartment} organization to the Lunar Resort):\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Users and Organizations} from Control Panel → Users.\n\\item\n  Go to the \\emph{Organizations} tab and click the \\emph{Add} button.\n  Fill out the Name field at a minimum.\n\\item\n  If you're creating a child Organization, use the Parent Organization\n  \\emph{Select} button to select an Organization in the system to be the\n  direct parent. Click the \\emph{Remove} button to remove the currently\n  configured parent.\n\\item\n  Click \\emph{Save} when finished filling out the Add Organization form.\n\\end{enumerate}\n\nAs when creating a new user, once you submit the form a success message\nappears and you have access to a new form which lets you enter\nadditional information about the Organization. Organizations can have\nassociated multiple email addresses, postal addresses, web sites, and\nphone numbers. The Services link can be used to indicate the operating\nhours of the Organization, if any.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} After creating an Organization, assign the desired user to\nthe Organization Owner Role. The Organization Owner can do everything\nthat an organization Administrator can. In addition to their full\nadministrative rights within the Organization, they can do these things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Appoint other Users to be Organization Administrators\n\\item\n  Appoint other Users to be Organization Owners\n\\item\n  Remove the memberships of other Organization Administrators or Owners\n\\end{itemize}\n\nOrganization Administrators can't make these Role assignments and can't\nmanage the memberships of other Organization Administrators or Owners.\n\n\\noindent\\hrulefill\n\n\\section{Editing Organizations}\\label{editing-organizations}\n\nTo edit an Organization, go to the Users and Organizations section of\nthe Control Panel and click the \\emph{Organizations} tab. All active\nOrganizations are listed. Click the \\emph{Actions} button next to an\nOrganization. This shows a list of actions you can perform on this\nOrganization.\n\n\\begin{itemize}\n\\item\n  \\emph{Edit} lets you specify details about the Organization, including\n  addresses, phone numbers, and email addresses. You can also create a\n  Site for the Organization.\n\\item\n  \\emph{Manage Site} lets you create and manage the public and private\n  pages of the Organization's Site. This only appears after a Site has\n  been created for the Organization.\n\\item\n  \\emph{Assign Organization Roles} lets you assign Organization-scoped\n  Roles to Users. By default, Organizations are created with three\n  Roles: Organization Administrator, Organization User and Organization\n  Owner. You can assign one or more of these Roles to Users in the\n  Organization. All members of the Organization automatically get the\n  Organization User Role so this Role is hidden when you click Assign\n  Organization Roles.\n\\item\n  \\emph{Assign Users} lets you search and select Users to be assigned to\n  this Organization as members.\n\\item\n  \\emph{Add User} adds a new User and assigns the User as a member of\n  this Organization.\n\\item\n  \\emph{Add Organization} lets you add a child Organization to this\n  Organization. This is how you create hierarchies of Organizations with\n  parent-child relationships.\n\\item\n  \\emph{Delete} removes this Organization. Make sure the Organization\n  has no Users in it first. You'll be prompted for confirmation that you\n  want to delete the Organization. If there are Users in the\n  Organization or if there are sub-Organizations, you must remove the\n  Users and delete the sub-Organizations before deleting the parent\n  Organization.\n\\end{itemize}\n\nIf you click the Organization name you can view both a list of Users who\nare members of this Organization and a list of all the sub-Organizations\nof this Organization.\n\n\\section{Organization Types}\\label{organization-types}\n\nBy default, Liferay DXP only includes the \\emph{Organization} type.\nConfigure the existing type or add additional types using the aptly\nnamed Organization Type entry in System Settings. There are two main\nreasons to configure Organization types:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Organizations usually correlate to real-life hierarchical structures.\n  Calling them by their real names is helpful for administrators and\n  Users. In the Major League Baseball (MLB) example, \\emph{League},\n  \\emph{Division}, and \\emph{Team} Organization types are useful.\n\\item\n  Enforce control over which Organizations can be top level\n  Organizations and the type of sub-Organization allowed for each parent\n  Organization type. For example, MLB would not allow Division\n  Organization types to be sub-Organizations of Team Organizations.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/orgs-organization-type.png}\n\\caption{Create new organization types through the System Settings entry\ncalled Organization Types.}\n\\end{figure}\n\nCheck out the configuration options that configure the default\n\\emph{Organization} type and then configure an additional type.\n\nTo add another Organization type called \\emph{League}, enter these\noptions into the configuration form:\n\n\\begin{description}\n\\tightlist\n\\item[Name: \\emph{League}]\nAdds League to the list of Organization types that appear in the Add\nOrganization menu.\n\\item[Country Enabled: \\emph{True}]\nEnables the Country selection list field on the form for adding and\nediting League types.\n\\item[Country Required: \\emph{False}]\nSpecifies that the \\emph{Country} field is not required when adding a\nLeague.\n\\item[Rootable: \\emph{True}]\nEnables Leagues as a top level Organization. Limit League to\nsub-Organization status by excluding this property.\n\\item[Children Types: \\emph{Division}]\nSpecifies Division as the only allowable sub-Organization type for the\nLeague parent type.\n\\end{description}\n\nOnce you configure additional Organization types and click Save, you'll\nfind your new type(s) available for selection in the Add Organization\nform.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/orgs-add-custom-organization.png}\n\\caption{Custom configuration types are available in the Add\nOrganization form.}\n\\end{figure}\n\nUsers can join or be assigned to Sites when they share a common\ninterest. Users can be assigned to Organizations when they fit into a\nhierarchical structure. User groups provide a more ad hoc way to group\nusers than sites and Organizations. You'll look at them next.\n\n\\chapter{Roles and Permissions}\\label{roles-and-permissions}\n\nIf a \\emph{Role} were to win a Grammy or an Oscar or some other\nego-feeding popularity contest, it better remember to thank all its\n\\emph{permissions} groupies during the acceptance speech, because\nthey're the ones doing the real work. The Role is just the pretty face,\nso to speak.\n\nRoles collect permissions that define a particular function, according\nto a particular scope. Roles collect permissions, and Users are assigned\nto Roles.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Roles are assigned to Users, but it's tedious to assign\neach User to a Role intended for lots of Users. Recall that Users are\ngrouped in Sites, Organizations, and User Groups. Implicitly assign\nregular scoped permissions to Users by assigning a Role directly to one\nof these User groupings.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/roles-assignees.png}\n\\caption{Assign Users to a role, directly or by their association with a\nSite, Organization, or User Group.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\nTake a Message Board Administrator Role, for example. A Role with that\nname should have permissions relevant to the specific Message Board\nportlets delegated to it. Users with this Role inherit the permissions\ncollected underneath the umbrella of the Role.\n\nIn addition to regular Roles, Site Roles, and Organization Roles, there\nare also Teams. Teams can be created by site administrators within a\nspecific Site. The permissions granted to a Team are defined and applied\nonly within the Team's site. The permissions defined by regular, Site,\nand Organization Roles, by contrast, are defined at the global level,\nalthough they are applied to different scopes.\n\n\\begin{description}\n\\tightlist\n\\item[Regular role]\nPermissions are defined at the global level and are applied at the\nglobal scope.\n\\item[Site role]\nPermissions are defined at the global level and are applied to one\nspecific Site.\n\\item[Organization role]\nPermissions are defined at the global level and are applied to one\nspecific Organization.\n\\item[Team]\nPermissions are defined within a specific Site and are assigned within\nthat specific Site.\n\\end{description}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Some permissions cannot be handled from the control\npanel. Asset-level permissions (for instance, permission to edit an\nindividual blog post, or view a folder in the Documents and Media\nlibrary) are managed from the individual asset. See\n\\href{/docs/7-2/user/-/knowledge_base/u/widget-permissions}{Widget\nPermissions} for details.\n\n\\noindent\\hrulefill\n\n\\section{Deleting Asset Containers}\\label{deleting-asset-containers}\n\nA Web Content Folder contains Web Content articles. The Web Content\nFolder is an asset container, and the Web Content Article is an asset.\nIt's possible to give a Role permission to delete an asset container\nwithout giving the Role permission to delete individual assets. In that\ncase, beware: if a Role assignee deletes an asset container with\nindividual assets in it, the individual assets themselves are deleted as\nwell.\n\nBesides Web Content Folders, examples of asset containers include\nBookmarks Folders, Message Boards Categories, Wiki Nodes, and Documents\nand Media Folders.\n\nYou might not need to create a Role for a certain functionality. Liferay\nprovides many pre-configured Roles for your convenience.\n\n\\section{Default Liferay Roles}\\label{default-liferay-roles}\n\nIn the Roles Application appears a list of all the Roles in Liferay, by\nscope.\n\nThese are some of the pre-configured regular Roles:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Guest: The Guest role is assigned to unauthenticated users and grants\n  the lowest-level permissions.\n\\item\n  User: The User role is assigned to authenticated Users and grants\n  basic permissions (mostly \\emph{Add to Page} permissions for their own\n  Sites).\n\\item\n  Power User: The Power User Role grants more permissions than the User\n  Role. It's an extension point for distinguishing regular Users from\n  more privileged Users. For example, you can set things up so that only\n  Power Users have personal sites.\n\\item\n  Administrator: The administrator Role grants permission manage the\n  entire portal, including global portal settings and individual Sites,\n  Organizations, and Users.\n\\end{itemize}\n\nThese are some of the pre-configured site roles:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Site Member: The Site Member Role grants basic privileges within a\n  Site, such as permission to visit the Site's private pages.\n\\item\n  Site Administrator: The Site Administrator Role grants permission to\n  manage \\emph{almost} all aspects of a Site including site content,\n  site memberships, and site settings. Site Administrators cannot delete\n  the membership of or remove roles from other Site Administrators or\n  Site Owners. They also \\emph{cannot} assign other Users as Site\n  Administrators or Site Owners.\n\\item\n  Site Owner: The Site Owner Role is the same as the Site Administrator\n  Role except that it grants permission to manage \\emph{all} aspects of\n  a Site, including permission to delete the membership of or remove\n  Roles from Site Administrators or other Site Owners. They \\emph{can}\n  assign other users as Site Administrators or Site Owners.\n\\end{itemize}\n\nThese are some of the pre-configured organization roles:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Organization User: The Organization User role grants basic privileges\n  within an Organization. If the Organization has an attached Site, the\n  Organization User Role implicitly grants the Site member Role within\n  the attached Site.\n\\item\n  Organization Administrator: The Organization Administrator Role grants\n  permission to manage \\emph{almost} all aspects of an Organization\n  including the Organization's Users and the Organization's Site (if it\n  exists). Organization Administrators cannot delete the membership of\n  or remove Roles from other Organization Administrators or Organization\n  Owners. They also \\emph{cannot} assign other Users as Organization\n  Administrators or Organization Owners.\n\\item\n  Organization Owner: The Organization Owner Role is the same as the\n  Organization Administrator Role except that it grants permission to\n  manage \\emph{all} aspects of an Organization, including permission to\n  delete the membership of or remove Roles from Organization\n  Administrators or other Organization Owners. They \\emph{can} assign\n  other Users as Organization Administrators or Organization Owners.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} It's easy to overlook the differences between owner type\nroles and administrator type roles for Sites and Organizations. Site and\nOrganization administrators \\emph{cannot} remove the administrator or\nowner Role from any other administrator or owner, and they \\emph{cannot}\nappoint other Users as site or organization administrators or owners.\n\nIn contrast, site and organization owners \\emph{can} do those things.\n\n\\noindent\\hrulefill\n\nRoles, and the permissions granted with their assignment, are\nfoundational components in Liferay. Understanding their uses and\nconfiguration enhances your ability to configure Liferay DXP to suit\nyour organizational needs.\n\n\\chapter{Managing Roles}\\label{managing-roles}\n\nYou manage Roles and Permissions in the Control Panel (\\emph{Control\nPanel} → \\emph{Users} → \\emph{Roles}). There you'll find an application\nfor creating Roles, granting them permissions, and assigning Users to\nthem. Roles can be scoped by portal, Site, or Organization.\n\nDefining a Role's permissions is a topic deserving its own article. Read\n\\href{/docs/7-1/user/-/knowledge_base/u/defining-role-permissions}{here}\nabout defining a role's permissions.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/roles-app.png}\n\\caption{The Roles application lets you add and manage roles for the\nglobal (Regular), Site, or Organization scope.}\n\\end{figure}\n\n\\section{Creating Roles}\\label{creating-roles}\n\nDetermine the scope of the Role you must create. Roles can be scoped\nglobally (Regular Roles), to a specific Site (Site Roles), or to an\nOrganization (Organization Roles).\n\nTo create a Role:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the tab for the proper Role scope, then click the \\emph{Add}\n  (\\includegraphics{./images/icon-add.png}) button.\n\\item\n  Enter a title and description. The title field is required but the\n  description is optional.\n\\item\n  Enter a Key, if desired. This required field provides a key that can\n  be used to refer to the Role programmatically. It's auto-populated\n  with the title text, but you can override it if desired.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nNow the Role is present in the database and ready for further\nconfiguration.\n\n\\section{Assigning Users to a Role}\\label{assigning-users-to-a-role}\n\nAssign users to a Role in the Assignees tab of the Add/Edit Role form.\nRoles are assigned to Users, Sites, Organizations, or User Groups.\nHere's how to assign the User Group Manager Role created in the last\nsection to Users:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the Assignees tab of the Add/Edit Role form, click the second level\n  tab for \\emph{Users}.\n\\item\n  Click the Add button (\\includegraphics{./images/icon-add.png}).\n\\item\n  Select the Users you want to add to the Role and click \\emph{Add}.\n\\end{enumerate}\n\nIf assigning a group, note that all Users assigned to that group inherit\nthe Role as well.\n\nThat's a good start, but your Role isn't worth the database row it\noccupies without defining permissions for the Role. Read the next\narticle to learn how.\n\n\\chapter{Defining Role Permissions}\\label{defining-role-permissions}\n\nRoles collect permissions, so when Users are given a Role, they receive\nall the permissions defined by the Role.\n\nIf you create a Role with permission to access something in the Control\nPanel, keep in mind that the \\emph{View Control Panel Menu} permission\nis automatically granted.\n\nConsider a Role called User Group Manager. Define the permissions for\nthe User Group Manager Role so that assigned Users can add Users to or\nremove Users from any User Group:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the Control Panel and then click on \\emph{Users} → \\emph{Roles}.\n\\item\n  On the Regular Roles screen, click \\emph{Add}\n  (\\includegraphics{./images/icon-add.png}).\n\\item\n  After naming your Role, click \\emph{Save}.\n\\item\n  Click on the \\emph{Define Permissions} tab.\n\\item\n  Drill down in the menu on the left to \\emph{Control Panel} →\n  \\emph{Users} → \\emph{User Groups}.\n\\item\n  Under the \\emph{General Permissions} heading, flag \\emph{Access in\n  Control Panel} and \\emph{View}. This lets user group managers access\n  the User Groups Control Panel portlet and view existing User Groups.\n\\item\n  Since you want User Group managers to be able to view User Groups and\n  assign members to them, also check the \\emph{Assign Members} and\n  \\emph{View} permissions under the \\emph{Resource Permissions} →\n  \\emph{User Group} heading.\n\\item\n  There's one last necessary permission you might not think of in\n  association with this Role. In \\emph{Control Panel} → \\emph{Users} →\n  \\emph{Users and Organizations}, User Group managers need \\emph{View}\n  permission on the User resource. Grant this permission.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/roles-define-permissions.png}\n\\caption{When defining permissions on a Role, the Summary view provides\na list of permissions that have already been defined for the role. The\narea on the left side of the screen lets you drill down through various\ncategories of permissions.}\n\\end{figure}\n\nNow the User Group Manager Role has all the permissions necessary for\nadding Users to User Groups. After all, User Group managers can view\nUser Groups, assign members, and access User Groups in the Control\nPanel. The permission to view Users in the Control Panel was necessary\nbecause you must view Users to assign them as members of a Role. Without\nthis permission, User Group managers see an empty list if they try to\nadd Users to a Role.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/roles-no-users-found.png}\n\\caption{Users assigned to the User Group Manager Role can't find any\nusers to add unless they have view permissions on the User resource.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Roles application in the Control Panel is not the\nonly place where permissions are configured. You can configure a Role's\npermissions on a resource at a more granular level. For example, from a\nparticular application instance, click its \\emph{Options}\n(\\includegraphics{./images/icon-options.png}) menu and select\n\\emph{Permissions}. There you can configure permissions for the resource\nthat overlap with those configured in the Control Panel's Roles\napplication. However, permissions granted or removed in the Control\nPanel override those made at the more granular level.\n\n\\noindent\\hrulefill\n\nThere are three categories of permissions: \\emph{Control Panel},\n\\emph{Site Administration}, and \\emph{User}. By default, Users can\nmanage their User accounts via the permissions belonging to the User\ncategory. Site Administrators can access the site administration tools\nbelonging to the Site Administration category. Portal Administrators can\naccess the entire Control Panel. For custom Roles, you can mix and match\npermissions from as many categories as you like.\n\nThe permissions in the Site Administration → Applications categories\ngovern the content that can be created by portlets such as the Wiki and\nMessage Boards. If you pick one of the portlets from this list, you see\noptions for defining permissions on its content. For example, if you\npick Message Boards, you see permissions for creating categories and\nthreads or deleting and moving topics.\n\nSite application permissions affect the application as a whole. Using\nthe Message Boards as an example, an application permission might define\nwho can add the Message Boards portlet to a page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/roles-message-board-content-permissions.png}\n\\caption{You can fine-tune which actions are defined for a role within a\nspecific application like the Message Boards.}\n\\end{figure}\n\nThe Control Panel permissions affect how the Control Panel appears to\nthe User in the Control Panel. The Control Panel appears differently for\ndifferent Users, depending on their permissions. Some Control Panel\nportlets have a Configuration button, and you can define who gets to see\nthat. You can also fine-tune who gets to see various applications in the\nControl Panel.\n\nIf you want to change the scope of a permission, click the \\emph{Change}\nlink next to the gear icon next to the permission and then choose a new\nscope. After you click \\emph{Save}, you'll see a list of all permissions\ncurrently granted to the Role. From the Summary view, you can add more\npermissions or go back to the Role Application default view by clicking\non the \\emph{Back} (\\includegraphics{./images/icon-back.png}) icon.\n\nSometimes you might find that a certain permission grants more or less\naccess than what you expected---always test your permissions\nconfigurations!\n\n\\section{Delegating Social Activities\nConfiguration}\\label{delegating-social-activities-configuration}\n\nThere's a permission that allows Site administrators to delegate\nresponsibility for configuring social activities to other Users. To add\nthis permission to a Role, click \\emph{Actions} next to the desired Role\nand select \\emph{Define Permissions}. Find the \\emph{Site\nAdministration} → \\emph{Configuration} → \\emph{Social Activity}\npermissions category. Flag all of the permissions and then click\n\\emph{Save}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Access in Site Administration\n\\item\n  Configuration\n\\item\n  Permissions\n\\item\n  Preferences\n\\item\n  View\n\\end{itemize}\n\nOnce these permissions are granted, Role assignees can manage the site's\nSocial Activities.\n\nRoles allow portal administrators to define various permissions in\nwhatever combinations they like. This gives you as much flexibility as\npossible to build the Site you have designed.\n\n\\chapter{Managing User Data}\\label{managing-user-data}\n\nInternet users are increasingly and justifiably concerned about how\ntheir personal data is processed by the systems they use. The\nenforcement of GDPR is a crystallization of these concerns into\nlegislative action. Companies processing the personal data of EU\nresidents must adopt appropriate measures to protect User data. Of\ncourse, legal requirements like those in GDPR only explain one reason\nfor companies to develop policies for ensuring their users' right to\nprivacy. The market demands site owners show higher levels of\nresponsiveness to User inquiries into how their data is stored and\nprocessed.\n\nLiferay is aware of the need for functionality to address User data\nmanagement, and added two important features toward this end:\n\n\\begin{description}\n\\tightlist\n\\item[\\href{/docs/7-2/user/-/knowledge_base/u/sanitizing-user-data}{Erase\nand/or anonymize data associated with a User}]\nAdministrative Users go through a step by step process, choosing to\nerase certain pieces of data and anonymize others.\n\\item[\\href{/docs/7-2/user/-/knowledge_base/u/exporting-user-data}{Export\na User's personal data}]\nExport ZIP files containing the data associated with a User.\n\\end{description}\n\nThese features are tools that get you closer to meeting two of GDPR's\ntechnically challenging requirements, the \\emph{right to data\nportability} and the \\emph{right to be forgotten}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} It is Liferay's sincerest hope that through the User\nManagement functionality of Liferay DXP, companies processing the\npersonal data of their website's users can satisfy the requirements of\nGDPR. However, the tools discussed here and anywhere else in the\ndocumentation, including those directly aimed at addressing GDPR\nrequirements (as in this article) do not guarantee compliance with the\nlegal requirements of GDPR. Each company or individual whose website\nprocesses user personal data and is under the jurisdiction of GDPR must\ncarefully determine the precise steps necessary to ensure they are fully\ncompliant with GDPR.\n\n\\noindent\\hrulefill\n\n\\section{Anonymizing Data}\\label{anonymizing-data}\n\nDeleting User data is the safest way to honor \\emph{right to be\nforgotten} requests. When User data must be preserved, automatic\nanonymization of the data is in order. Users being anonymized must have\ntheir identifiers (for example, User ID and User Name) removed from\ncontent they've interacted with. However, portal content usually\nrequires this information for its applications to work properly.\nTherefore, the User's identifiers must be replaced by something, or\nsomeone. Meet the new User, \\emph{Anonymous Anonymous}, identity swapper\n\\emph{extraordinaire}. This deactivated User is dedicated to be the User\nwhose identifiers are assigned to anonymized content. This identity swap\nis an important step in the anonymization process, but additional manual\nintervention may be necessary to truly achieve anonymization.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/users-anonymized-content.png}\n\\caption{Anonymized content is presented with the User Anonymous\nAnonymous's identifying information.}\n\\end{figure}\n\nHere at Liferay, we've grown fond of \\emph{Anonymous Anonymous}. If\nyou'd rather start from scratch or assign an existing User to be the\nAnonymous User, get rid of \\emph{Anonymous Anonymous} and configure your\nown Anonymous User.\n\nThe anonymous user is programmatically created for each instance the\nfirst time an Administrator clicks \\emph{Delete Personal Data} from a\nUser's Actions menu (\\includegraphics{./images/icon-actions.png}). If\nyou haven't yet done that, no Anonymous User exists.\n\nThe easiest way to set up a new User as the Anonymous User is to edit an\nexisting Anonymous User configuration, passing in the new Anonymous\nUser's User ID.\n\nTo edit an existing configuration:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Control Panel → Configuration → System Settings → Users →\n  Anonymous User.\n\\item\n  Edit the existing configuration, providing a different User ID.\n\n  Get the User ID from Control Panel → Users → Users and Organizations.\n  Click on the User and find the User ID in the Information screen of\n  the Edit User application.\n\\item\n  Click \\emph{Update}.\n\\end{enumerate}\n\nTo create a new Anonymous User:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/adding-editing-and-deleting-users\\#adding-users}{Create\n  a User} use for data anonymization. Alternatively, you can use an\n  existing User.\n\\item\n  If there's already an Anonymous User configured for the instance,\n  there are two ways to remove it:\n\n  Delete the User entirely. Deleting the User simultaneously deletes its\n  configuration as the Anonymous User. Go to Control Panel → Users →\n  Users and Organizations. If it's an active User, first deactivate,\n  then delete the User. The default Anonymous Anonymous User is\n  deactivated by default. Simply delete the User in this case. Click the\n  Actions button (\\includegraphics{./images/icon-actions.png}) and\n  select \\emph{Delete}.\n\n  If you don't want to delete the User, just delete the User's\n  configuration as the Anonymous User. Go to Control Panel →\n  Configuration → System Settings → Users → Anonymous Users.\n\\item\n  Add a new Anonymous User configuration. Click the \\emph{Add} button.\n\\item\n  Fill out the two fields, Company ID and User ID.\n\n  Get the Company ID from Control Panel → Configuration → Virtual\n  Instances. The Instance ID and Company ID are the same.\n\n  Get the User ID from Control Panel → Users → Users and Organizations.\n  Click on the User and find the User ID in the Information screen of\n  the Edit User application.\n\\end{enumerate}\n\nThere can only be one Anonymous User configured for each instance.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/users-anonymous-config.png}\n\\caption{Assign your own Anonymous User from Control Panel →\nConfiguration → System Settings → Users → Anonymous User.}\n\\end{figure}\n\n\\section{Manual Anonymization}\\label{manual-anonymization}\n\nAnonymizing just the User's identification fields is often not enough.\nIf a User named Ziltoid Omniscient complains about The Lunar Resort's\ncoffee in a Message Boards Message and in it signs the post with\n\\emph{Supreme Leader of Ziltoidea 9}, anonymizing this post would remove\nthe User's name (Ziltoid Omnisicent) and replace it with Anonymous\nAnonymous, but searching the Internet for \\emph{Ziltoidea 9} quickly\nreveals that the post was written by\n\\href{https://en.wikipedia.org/wiki/Ziltoid_the_Omniscient}{Ziltoid the\nOmniscient}. There can be user-entered personal data within the content\nof an application. You must manually edit such content to remove\nidentifying details.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/users-partial-anonymization.png}\n\\caption{Even though this Message Boards Message (a comment on a blog\npost in this case) is anonymized, it should be edited to remove User\nAssociated Data from the content of the message.}\n\\end{figure}\n\n\\chapter{Sanitizing User Data}\\label{sanitizing-user-data}\n\nOne of the technically challenging requirements of the General Data\nProtection Regulation (GDPR) is \\emph{the right to be forgotten}. The\npurpose of this article is not to go into the details of this\nrequirement, but to show you how the personal data erasure functionality\ncan assist you in satisfying this requirement.\n\nA simple way to think of what it means to be \\emph{forgotten} by\nsoftware is to consider a scenario where a new portal administrator is\nhired immediately after a User's right to be forgotten request has been\nhonored. The new portal administrator has access to all of the Site's\ncontent and administration capabilities. Despite this, the administrator\nmust not be able to glean information that could lead her to knowing the\nidentity of the User whose personal data was erased.\n\nConceptually, forgetting a User means two things, at a minimum:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Erasing the User's identifying information from the system. In Liferay\n  DXP, this entails removing the User from database tables and search\n  indexes.\n\\item\n  Erasing or anonymizing content the User has interacted with so it\n  cannot be tracked to a real person.\n\\end{itemize}\n\nUsers can already be deactivated and then deleted, so why add new\nfunctionality? Deleting removes the User from the table of Users in the\ndatabase. The User's information is preserved in other locations,\nhowever. In a standard User deletion scenario, all of a User's\npersonally created content is still assigned to the User and her\nidentifiers (User ID and User Name) still appear in the UI next to\ncontent associated with her. This unintentional preservation of\nuser-identifying data is inadequate for satisfying some of the GDPR\nrequirements and is the primary reason why the data erasure\nfunctionality was added in 7.0.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Personal data erasure can help companies in their\nattempts to satisfy the requirements of GDPR. Using the data erasure\ntool described here provides no guarantee of compliance with the legal\nrequirements of GDPR. Each company or individual whose website processes\nuser personal data and is under the jurisdiction of GDPR must carefully\ndetermine the precise steps necessary to ensure they are fully compliant\nwith GDPR.\n\n\\noindent\\hrulefill\n\nTo begin sanitizing a user's data,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Control Panel → Users → Users and Organizations.\n\\item\n  Click the Actions button for a User\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Delete\n  Personal Data}. If you have not deactivated the user, you will be\n  asked to do so.\n\n  The User's Personal Data Erasure screen appears.\n\\end{enumerate}\n\n\\section{The Personal Data Erasure\nScreen}\\label{the-personal-data-erasure-screen}\n\nYou can browse all data the user has posted on the system. Click\n\\emph{Personal Site} to browse data from that site.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/users-data-erasure-personal.png}\n\\caption{From here, you can browse all data the user posted on his or\nher personal Site.}\n\\end{figure}\n\nClick \\emph{Regular Sites} to browse any data posted in regular Liferay\nsites.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/users-data-erasure-regular.png}\n\\caption{Choose Regular Sites to browse all data posted by the user on\nadministratively-created Sites.}\n\\end{figure}\n\nTo review the user's data, click the item. For example, Pepper seems to\nhave posted a blog entry on her personal Site. Clicking that entry\nreveals the title of that blog entry.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/users-data-erasure-blog.png}\n\\caption{Pepper's blog entry might need review.}\n\\end{figure}\n\nTo review any entry, click it. You're brought to the edit mode of the\napplication (in this case, Blogs), where you can make any changes to the\ncontent that are necessary.\n\nTo manage (anonymize or delete) all the items for an application at\nonce:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the Actions button (\\includegraphics{./images/icon-actions.png})\n  for the application.\n\\item\n  If you're sure all items for an application can be safely deleted,\n  choose \\emph{Delete}.\n\\item\n  If you're sure simple anonymization is good enough for all of an\n  application's items, choose \\emph{Anonymize}.\n\\end{enumerate}\n\nUse the interface to browse through the Sites, applications, and data.\n\n\\section{Delete the User}\\label{delete-the-user}\n\nOnce all data is reviewed, deleted, edited, and/or anonymized as\nappropriate, delete the User. A dialog box pops up automatically when\nyou're finished. This step is simple: Click \\emph{OK}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/users-delete-user.png}\n\\caption{To finish the data erasure process, delete the User.}\n\\end{figure}\n\nNow the User's data is anonymized or deleted, and the User is also\ndeleted.\n\n\\chapter{Exporting User Data}\\label{exporting-user-data}\n\nUser Management practices must account for the EU's General Data\nProtection Regulation. One of its tenets is that Users have a right to\n\\emph{data portability}.\n\nData portability means that a User has the right to receive their\npersonal data in a machine-readable format.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Personal data export can help companies in their attempts\nto satisfy the requirements of GDPR. Using the export tool described\nhere provides no guarantee of compliance with any GDPR requirement. Each\ncompany or individual whose website processes user personal data and is\nunder the jurisdiction of GDPR must carefully determine the precise\nsteps necessary to ensure they are fully compliant with GDPR.\n\n\\noindent\\hrulefill\n\nThe Control Panel's User Management system now natively supports the\nexport of a User's personal data to a ZIP file for download. The data\nformat for the files containing the data is XML.\n\n\\section{Exporting and Downloading}\\label{exporting-and-downloading}\n\nTo export user data,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Users} → \\emph{Users and\n  Organizations}.\n\\item\n  Find the User and click the Actions button\n  (\\includegraphics{./images/icon-actions.png}), then click \\emph{Export\n  Personal Data}.\n\n  This opens the User's Export Personal Data screen.\n\\item\n  If there are no existing export processes shown, there's only one\n  thing to do: click the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}). The tool for exporting the\n  User's data appears.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/users-export-data.png}\n  \\caption{The Export Personal Data tool lets you export all or some of\n  the User's data.}\n  \\end{figure}\n\\item\n  Most of the time you want to export all the available data. Click\n  \\emph{Select Items}, and all applications containing User data are\n  selected in the UI.\n\\item\n  Click \\emph{Export}. You're taken back to the User's Export Personal\n  Data screen, but now there's an export process in the list.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/users-export-processes.png}\n  \\caption{Once User data is successfully exported, the export process\n  is displayed in the User's Export Personal Data list.}\n  \\end{figure}\n\\item\n  Download the data. Click the Actions button\n  (\\includegraphics{./images/icon-actions.png}) for the process and\n  select \\emph{Download}.\n\\end{enumerate}\n\n\\section{Examining Exported Data}\\label{examining-exported-data}\n\nSo what does the exported data look like?\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n\n<model>\n    <model-name>com.liferay.message.boards.model.MBMessage</model-name>\n    <column>\n        <column-name>messageId</column-name>\n        <column-value><![CDATA[38099]]></column-value>\n    </column>\n    <column>\n        <column-name>statusByUserId</column-name>\n        <column-value><![CDATA[38045]]></column-value>\n    </column>\n    <column>\n        <column-name>statusByUserName</column-name>\n        <column-value><![CDATA[Jane Slaughter]]></column-value>\n    </column>\n    <column>\n        <column-name>userId</column-name>\n        <column-value><![CDATA[38045]]></column-value>\n    </column>\n    <column>\n        <column-name>userName</column-name>\n        <column-value><![CDATA[Jane Slaughter]]></column-value>\n    </column>\n    <column>\n        <column-name>subject</column-name>\n        <column-value><![CDATA[Great list. I was thinking of bringing the family,...]]></column-value>\n    </column>\n    <column>\n        <column-name>body</column-name>\n        <column-value><![CDATA[<p>Great list. I was thinking of bringing the family, but I don&#39;t\n  actually believe humans have ever been to the moon, so I guess it\n  would be silly to book a trip! LOL!</p>]]></column-value>\n    </column>\n</model>\n\\end{verbatim}\n\nIn this example, User Jane Slaughter made a Message Boards Message post,\nand her User information was recorded in the \\texttt{MBMessage} model's\ndatabase table.\n\nThis actually corresponds with a comment on a Blogs Entry:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/users-mbmessage.png}\n\\caption{A Comment on a blog post is User Associated Data.}\n\\end{figure}\n\nExporting User data informs Site owners and Users about how much\npersonal data the sight may have.\n\n\\chapter{User Groups}\\label{user-groups}\n\nA User Group is a list of Users created for a specific purpose. User\nGroups can be created across the hierarchical boundaries of\n\\href{/docs/7-2/user/-/knowledge_base/u/organizations}{Organizations}.\nFor example, an administrator could create a Teachers User Group for\nadding all members to multiple Sites, assign them all to a\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{Regular\nRole}, and create a common set of profile pages for all teachers in the\nUser Group. User Groups are integrated with Roles, Sites, Site\nTemplates, and permissions. This flexibility means that there are many\ndifferent use cases for User Groups. The articles in this section show\nyou how to work with User Groups to serve the most common use cases.\n\nUser Groups are most often used in these scenarios:\n\n\\begin{itemize}\n\\item\n  \\textbf{Manage Site membership:} Grant Site membership to all Users in\n  a User Group. Using the previous example, the Teachers user group\n  could be added as a member of the Sites \\emph{University Employees}\n  and \\emph{Students and Teachers Collaboration}. All users in that User\n  Group would become members of those Sites.\n\\item\n  \\textbf{Manage user personal pages:} Provide predefined public or\n  private pages to the users in the user group. For example, the\n  Teachers user group could be created to ensure the home page on all\n  teachers' personal Sites has the same layout and applications.\n\\item\n  \\textbf{Collect permissions:} Assign Roles and permissions to a group\n  of Users that don't share an organization. For example, in a\n  university's portal, a user group could be created to group all\n  teachers independently of their departments (organization). This would\n  make it easier to assign one or several Roles at once to all the\n  teachers.\n\\end{itemize}\n\nRead on to learn how to manage User Groups.\n\n\\chapter{Creating a User Group}\\label{creating-a-user-group}\n\nFollow these steps to create a user group:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Menu (\\includegraphics{./images/icon-menu.png}) and select\n  \\emph{Control Panel} → \\emph{Users} → \\emph{Users Groups}. Any\n  existing user groups appear here in a table.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n  This opens the \\emph{New User Group} form.\n\\item\n  Give your user group a name and description.\n\\item\n  If you want to create My Profile and/or My Dashboard pages for the\n  user group's members, select a Site Template to use from the \\emph{My\n  Profile} and \\emph{My Dashboard} selector menus.\n\\item\n  Click \\emph{Save}. The new user group then appears in the table.\n\\end{enumerate}\n\nNote that new User Groups don't have any Users. The next section shows\nyou how to add members to a user group.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/new-user-group.png}\n\\caption{The New User Group form.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/user-groups-table.png}\n\\caption{The user group you just created now appears in the table.}\n\\end{figure}\n\n\\section{Assigning Members to a User\nGroup}\\label{assigning-members-to-a-user-group}\n\nFollow these steps to add existing users to a user group:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If you're not already there, open the Menu\n  (\\includegraphics{./images/icon-menu.png}) and select \\emph{Control\n  Panel} → \\emph{Users} → \\emph{Users Groups}. The available user groups\n  appear here in a table.\n\\item\n  Click \\emph{Actions} (\\includegraphics{./images/icon-actions.png}) →\n  \\emph{Assign Members} for the user group you want to add users to. The\n  group's existing users appear in a table.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n  This opens a list of the users you can select.\n\\item\n  Select one or more users from the list, then click \\emph{Add}. This\n  adds the selected users to the group, and returns you to the table\n  containing the group's users. The users you added now appear in the\n  table.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/user-group-add-users.png}\n\\caption{Select the users to add to the user group.}\n\\end{figure}\n\n\\chapter{User Groups and Site\nMembership}\\label{user-groups-and-site-membership}\n\nUser Groups are used to manage Site membership. When you assign a User\nGroup to a Site, the group's Users become members of that Site. This is\none of the main use cases for User Groups.\n\nFollow these steps to assign a user group to a Site:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Menu (\\includegraphics{./images/icon-menu.png}), select the\n  Site you want to work in, then open its Site Administration menu.\n\\item\n  In the Site Administration menu, select \\emph{People} →\n  \\emph{Memberships}. This opens the Site Memberships screen.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/site-memberships.png}\n  \\caption{Select \\emph{Memberships} from the People menu.}\n  \\end{figure}\n\\item\n  In Memberships, select the \\emph{User Groups} tab. This tab displays\n  any User Groups currently assigned to the Site.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}),\n  select any User Groups you want to assign to the Site, then click\n  \\emph{Done}. The user groups you selected now appear in the User\n  Groups tab.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/user-groups-site-memberships.png}\n\\caption{The User Groups tab in Memberships shows the User Groups\ncurrently assigned to the Site.}\n\\end{figure}\n\n\\chapter{User Group Sites}\\label{user-group-sites}\n\nEach User has a personal Site that consists of public (Profile) and\nprivate (Dashboard) pages. A \\emph{User Group Site} determines the base\npages of the User Group members' personal Sites. If the User Group Site\npages are added to a User's Profile pages, then the User Group Site is a\npublic Site, accessible to anyone with the URL\n(\\texttt{http://www.{[}sitename{]}.com/web/{[}username{]}}). If the User\nGroup Site pages are added to the user's Dashboard pages, then the User\nGroup Site is a private Site. A mixed approach can also be used, where\nboth private and public pages are added for the User Group Site. If\nUsers belong to multiple User Groups, all the pages from those User\nGroup Sites are made part of their personal Sites.\n\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-a-user-group}{When\ncreating a user group}, you can create the user group Site via the\n\\href{/docs/7-2/user/-/knowledge_base/u/building-sites-from-templates}{Site\nTemplates} available for selection in the \\emph{My Profile} and \\emph{My\nDashboard} selector menus. You can also create a User Group Site later,\neither manually or via a Site Template.\n\n\\section{Creating User Group Sites From Site\nTemplates}\\label{creating-user-group-sites-from-site-templates}\n\nFollow these steps to create a User Group Site from a\n\\href{/docs/7-1/user/-/knowledge_base/u/building-sites-from-templates}{Site\nTemplate}, for a User Group that already exists:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Menu (\\includegraphics{./images/icon-menu.png}) and select\n  \\emph{Control Panel} → \\emph{Users} → \\emph{User Groups}. User groups\n  appear in a table.\n\\item\n  Click \\emph{Actions} (\\includegraphics{./images/icon-actions.png}) →\n  \\emph{Edit} for the User Group you want to create a Site for. This\n  opens a form that you can use to edit the User Group. Note that this\n  is the same form that appears when\n  \\href{/docs/7-2/user/-/knowledge_base/u/creating-a-user-group}{creating\n  a user group}.\n\\item\n  To use a Site Template to create a public profile for the Users on\n  their \\emph{My Profile} Site, select that Site Template from the\n  \\emph{My Profile} menu. To use a Site Template to create private pages\n  for the Users on their \\emph{My Dashboard} Site, select that Site\n  Template from the \\emph{My Dashboard} menu. Note that you can also do\n  both.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nNow, when one of the group's Users navigates to their \\emph{My Profile}\nor \\emph{My Dashboard} Sites, the content of those Sites reflect the\nSite Template(s) you selected.\n\nUser Group Site pages function similarly to regular Site Template pages,\nwith an important exception: User Group Site pages aren't copied for\neach user. They're shown dynamically along with any custom pages that\nUsers may have on their personal Site. For this reason, Users can't\nmodify pages inherited from the User Group. If needed, the User Group\nadministrator can define certain areas of a page as customizable, like\nwith regular Sites. This lets Users add and configure widgets in the\nspecified area of the page.\n\nThis flexibility lets you achieve almost any desired configuration for a\nUser's personal Site without having to modify it directly. When Users\nare assigned to a User Group, they'll immediately have access to the\nUser Group's Site pages from their personal Sites.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Site Templates have an option that propagates changes\nmade to the Site Template. If you use a Site Template with this option\nenabled, the User Group Sites update automatically when that template\nchanges. If you disable this option but enable it again later, the\ntemplate's pages are copied to the Users' Sites, overwriting any changes\nthey may have made. For more information on the automatically\npropagating Site Template changes, see\n\\href{/docs/7-2/user/-/knowledge_base/u/building-sites-from-templates}{Site\nTemplates}.\n\n\\noindent\\hrulefill\n\n\\section{Creating User Group Sites\nManually}\\label{creating-user-group-sites-manually}\n\nYou can create a User Group's Site manually, instead of basing it on a\nSite Template. Follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Menu (\\includegraphics{./images/icon-menu.png}) and select\n  \\emph{Control Panel} → \\emph{Users} → \\emph{Users Groups}. User groups\n  appear here.\n\\item\n  Click \\emph{Actions} (\\includegraphics{./images/icon-actions.png}) →\n  \\emph{Manage Pages} for the user group you want to create a Site for.\n  This opens the \\emph{Pages} window. Note that this is the same window\n  you use for\n  \\href{/docs/7-2/user/-/knowledge_base/u/creating-pages}{creating\n  pages}.\n\\item\n  Create the public and/or private pages that you want to use for the\n  Users' \\emph{My Profile} and/or \\emph{My Dashboard} Sites. Public\n  pages you create here become pages on users' \\emph{My Profile} Site,\n  while private pages become pages on users' \\emph{My Dashboard} Site.\n\\end{enumerate}\n\nWhen you return to User Groups in the Control Panel, you can access a\nUser Group's public and/or private pages via these links in the User\nGroup's \\emph{Actions} button\n(\\includegraphics{./images/icon-actions.png}):\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Go to Profile Pages:} Opens the User Group's public \\emph{My\n  Profile} page(s) in a new browser window.\n\\item\n  \\textbf{Go to Dashboard Pages:} Opens the User Group's private\n  \\emph{My Dashboard} page(s) in a new browser window.\n\\end{itemize}\n\nIn the new window, you can add more pages and portlets and configure\nSite settings.\n\n\\section{Legacy User Group Sites\nBehavior}\\label{legacy-user-group-sites-behavior}\n\nSince the inheritance of User Group Site pages is now dynamic, even if\nthere are hundreds of thousands of Users, even millions, there isn't an\nimpact in performance. Versions of Liferay Portal and Liferay DXP prior\nto 7.0 required User Group pages be copied to each User's personal Site.\nIf you long for the old days, or if you're upgrading from an older\nversion and must keep that behavior, enable it by adding the following\nline to your \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\nuser.groups.copy.layouts.to.user.personal.site=true\n\\end{verbatim}\n\nWhen this property is set to \\texttt{true}, the template pages are\ncopied to a User's personal Site once, and then may be modified by the\nUser. This means that if changes are made to the template pages later,\nthey only affect Users added to the User Group after the change is made.\nUsers with administrative privileges over their personal Sites can\nmodify the pages and their content if the \\emph{Allow Site\nAdministrators to Modify the Pages Associated with This Site Template}\nbox has been checked for the template. When a User is removed from a\nUser Group, the associated pages are removed from the User's personal\nSite. If a User is removed from a group and is subsequently added back,\nthe group's template pages are copied to the User's Site a second time.\nNote that if a User Group's Site is based on a Site Template and an\nadministrator modifies the User Group's Site Template after users have\nalready been added to the group, those changes only take effect if the\n\\emph{Enable propagation of changes from the Site Template} box for the\nUser Group was checked.\n\n\\chapter{Configuring User Group\nPermissions}\\label{configuring-user-group-permissions}\n\nAdministrators commonly create User Groups so the group's Users can take\nsome specific action in a Site. This is done by assigning the\npermissions for that action to a Role, and then assigning that Role to\nthe User Group. This grants the Role's permissions to the User Group's\nUsers.\n\nFollow these steps to grant permissions to Users in a User Group:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/creating-a-user-group}{Create\n  the User Group}.\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/user-groups-and-site-membership}{Assign\n  the User Group to a Site}.\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{Create\n  the Site Role and define its permissions}.\n\\item\n  Assign the Role to the User Group.\n\\end{enumerate}\n\nFor instructions on the first three steps, click those links above. This\narticle shows you how to assign a Role to a User Group:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Menu (\\includegraphics{./images/icon-menu.png}), select the\n  Site to work in, then open its Site Administration menu.\n\\item\n  In the Site Administration menu, select \\emph{People} →\n  \\emph{Memberships}. This opens the Memberships screen.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/site-memberships.png}\n  \\caption{Select \\emph{Memberships} from the Site Administration menu.}\n  \\end{figure}\n\\item\n  In Memberships, select the \\emph{User Groups} tab. This tab displays\n  User Groups currently assigned to the Site.\n\\item\n  Click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) for the User Group you\n  want to assign to a Role, and select \\emph{Assign Site Roles}. This\n  opens the Assign Site Roles dialog.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/user-groups-site-role.png}\n  \\caption{Select \\emph{Assign Site Roles} for the user group.}\n  \\end{figure}\n\\item\n  In the Assign Site Roles dialog, select the Role from the list and\n  click \\emph{Done}.\n\\end{enumerate}\n\n\\chapter{Editing User Groups}\\label{editing-user-groups}\n\nYou can access User Groups from \\emph{Control Panel} → \\emph{Users} →\n\\emph{User Groups}. Managing membership is the most common action you'll\nlikely perform on a User Group.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/user-groups-table.png}\n\\caption{The user groups appear in a table.}\n\\end{figure}\n\nFollow these steps to add/remove users to/from a User Group:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the User Group's name or description. Alternatively, you can\n  click the User Group's \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Assign\n  Members}. This presents a list of the User Group's users.\n\\item\n  To remove a User from the User Group, click the \\texttt{X} button next\n  to that User. To remove multiple Users at once, check each User's\n  checkbox and then click the trash icon\n  (\\includegraphics{./images/icon-trash.png}) that appears in the\n  Management Bar above the User list.\n\\item\n  To add Users to the User Group, click the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}). In the dialog that appears,\n  select the Users and click \\emph{Add}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/user-groups-users.png}\n\\caption{The list of Users lets you manage the User Group's membership.}\n\\end{figure}\n\nOther options are available in each User Group's Actions button\n(\\includegraphics{./images/icon-actions.png}):\n\n\\textbf{Edit:} Modify the User Group's name or description, or choose\nSite templates to use for the\n\\href{/docs/7-2/user/-/knowledge_base/u/user-group-sites}{User Group's\nSites}.\n\n\\textbf{Permissions:} Assign permissions for viewing and managing the\nUser Group.\n\n\\textbf{User Group Pages Permissions:} Assign permissions for managing\nthe User Group's Site pages.\n\n\\textbf{Manage Pages:} Manually manage the User Group's Site pages. See\nthe\n\\href{/docs/7-2/user/-/knowledge_base/u/user-group-sites\\#creating-user-group-sites-manually}{documentation\non user group Sites} for details.\n\n\\textbf{Assign Members:} Add/remove Users to/from the User Group. This\nis described in detail above.\n\n\\textbf{Delete:} Remove the User Group. Note that you can't delete a\nUser Group that contains Users. You must first remove the Users from the\ngroup.\n\nIf your User Group has public and private Site pages, the options\n\\emph{Go to Profile Pages} and \\emph{Go to Dashboard Pages} also appear\nin your User Group's Actions menu. Clicking one of these links opens\nthat Site in a new browser window. See the\n\\href{/docs/7-2/user/-/knowledge_base/u/user-group-sites}{documentation\non user group Sites} for details.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/user-groups-actions.png}\n\\caption{The Actions menu for a user group.}\n\\end{figure}\n\n\\chapter{Password Policies}\\label{password-policies}\n\nPassword policies enforce password rules to help users specify secure\npasswords. Use the default policy that ships with Liferay (modified or\nas is), or create your own policies. Password policies can be assigned\nto Users or Organizations, or set as the default policy used throughout\na virtual instance.\n\n\\section{Adding and Configuring Password\nPolicies}\\label{adding-and-configuring-password-policies}\n\nTo add or edit password policies,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Control Panel} → \\emph{Users} → \\emph{Password\n  Policies}.\n\n  There's already a default password policy in the system.\n\\item\n  Edit this the same way you edit other resources: click \\emph{Actions}\n  (\\includegraphics{./images/icon-actions.png}) and then click\n  \\emph{Edit}.\n\\item\n  To add a new policy, click the \\emph{Add}\n  (\\includegraphics{./images/icon-add.png}) button.\n\n  Provide the \\emph{Name} (required), \\emph{Description}, and specific\n  configuration options for your new password policy.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/password-policy-add.png}\n\\caption{You can create new password policies to suit your needs.}\n\\end{figure}\n\nThere are several configuration categories.\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Password Changes}]\nAllow or disallow users to change their passwords, and set a time limit\non the validity of password reset links.\n\\item[\\textbf{Password Syntax Checking}]\nIf enabled, require users to use a certain syntax when choosing a\npassword. You can disallow dictionary words, set a minimum length, and\nmore in this section.\n\\item[\\textbf{Password History}]\nIf enabled, decide how many passwords to keep in the history, preventing\nusers from reusing an old password.\n\\item[\\textbf{Password Expiration}]\nDecide whether to expire passwords after a specified time. If enabled,\nspecify how long passwords are valid, when and whether to send a\nwarning, and how many times the user can log in after the password is\nexpired before needing to set a new password (called a \\emph{Grace\nLimit}).\n\\item[\\textbf{Lockout}]\nIf enabled, set a maximum number of failed authentication attempts\nbefore the account is locked, how long the number of attempts is stored,\nand the lockout duration.\n\\item[\\textbf{Self Destruct}]\nIf enabled, set the time after lockout before Liferay self destructs\ncatastrophically, sending the world into apocalyptic chaos, out of which\nself-aware robots arise and recolonize the world, enslaving the\nsurviving remnant of humanity for their own nefarious purposes. We\nrecommend you keep this disabled.\n\\end{description}\n\nJust making sure you were paying attention; that last one doesn't\nactually exist.\n\nOnce you configure the policy, click \\emph{Save} to add it to the list\nof ready-to-use password policies.\n\n\\section{Assigning Users to a Password\nPolicy}\\label{assigning-users-to-a-password-policy}\n\nTo use the default password policy, you don't have to do anything: like\nits name suggests, it's the default. If you create a new password\npolicy, however, you must assign users to it. To do this click\n\\emph{Actions} (\\includegraphics{./images/icon-actions.png}) →\n\\emph{Assign Members}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/password-policy-assign-members.png}\n\\caption{Assign members to new password policies to make them take\neffect.}\n\\end{figure}\n\nChoose whether to assign users directly or to assign organizations to\nthe password policy, then click \\emph{Add}\n(\\includegraphics{./images/icon-add.png}).\n\nOnce assignments are saved, the password policy is in effect. Did you\nknow you can change the default password policy and configure it using\nLiferay's \\texttt{portal.properties} file?\n\n\\section{Default Policy Properties}\\label{default-policy-properties}\n\nThe Default Password Policy is set as the default and configured in\nLiferay's\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Passwords}{portal.properties}\nfile. Find the properties that start with\n\\texttt{passwords.default.policy}. To make changes, including changing\nthe default policy, add whichever properties and values you choose to\nmodify in your \\texttt{portal-ext.properties} file, as usual. Restart\nthe application server and your changes take effect.\n\n\\begin{verbatim}\n#\n# Set the properties of the default password policy.\n#\n\n...\npasswords.default.policy.name=Default Password Policy\n...\n\\end{verbatim}\n\nAs you can see, Password Policies give you a simple yet powerful way to\nset password rules.\n\n\\chapter{Auditing Users}\\label{auditing-users}\n\nYou've just finished lunch and are ready to get back to work. You have a\nSite you use to manage your project and before you left, you were about\nto create a folder in your Documents and Media library for sharing some\nrequirements documentation. Sitting down at your desk, you navigate to\nthe repository and attempt to create the folder.\n\n\\emph{You do not have permission to perform this action}, Liferay DXP\nhelpfully tells you.\n\n``\\emph{What?}'' you blurt in surprise. ``This is \\emph{my} project!''\n\n``Ah, you too?'' asks a co-worker from over the cube wall. ``I lost\naccess to a wiki I was updating just a few minutes ago. I was about to\nenter a support ticket for it.''\n\n``Forget the ticket. Let's go see the admin now,'' you say.\n\nAnd off you go, two floors down and to the far end of the building\nwhere, as you approach, you can already hear stress in the admin's voice\nas he tries to reassure someone on the phone.\n\n``Yes, Mr.~Jones. Yes, I'll fix it.'' (\\emph{Jones? The president of the\ncompany?} goes through your mind.) ``I'll get on it right away,\nMr.~Jones. It was just a mistake; I'll fix it. Thank you, Mr.~Jones,''\nand he hangs up the phone.\n\n``Problems?'' you ask the admin, whose name is Harry. He does look\nrather harried.\n\n``Yeah, Tom,'' he says. ``Somebody changed a bunch of permissions---it\nwasn't me. I'm assuming you and Dick are here because of the same\nproblem?''\n\n``Yup,'' you say. ``I lost access to a document repository folder.''\n\n``And I lost access to a wiki,'' Dick says.\n\n``It was probably due to some Site membership change. Let's take a look\nat the Audit app in the Control Panel and see what happened.''\n\nSometimes you need to know what Users are doing and exactly who is doing\nit. If you're a DXP subscriber, you can find this out with the Audit\napp. In combination with some settings in\n\\texttt{portal-ext.properties}, the Audit app shows you all the activity\nthat occurs on your server. You can quickly find out what changes were\nmade and by whom. If you've delegated permission granting to any group\nof people, this is an essential feature you're likely to use.\n\n\\chapter{Viewing Audit Events}\\label{viewing-audit-events}\n\nThe Audit app shows activities in your Liferay DXP installation. Access\nit by navigating to \\emph{Control Panel} → \\emph{Configuration} →\n\\emph{Audit}. The app displays a searchable list of captured events. You\ncan browse the list, but searching it is typically faster.\n\nThis figure shows that John Watson logged in and performed some actions\non the site. Click an entry to view details about any of these events.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/audit-list-events.png}\n\\caption{The Audit app displays the events it captures in a searchable\nlist.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/audit-detail.png}\n\\caption{Click an event in the list to show its details. The details for\nthis event show that John Watson updated his user account's\n\\texttt{prefixId} from \\texttt{1} to \\texttt{4}. The \\texttt{prefixId}\nrepresents a name prefix like Dr., Mr., Mrs., or Ms.}\n\\end{figure}\n\nAs you can see, depending on how many users you have, this list can get\npopulated very quickly. That's why page view events aren't displayed by\ndefault. They'll clutter up your audit report, since they'll definitely\nbe the most frequent event.\n\n\\textbf{Note:} You can add page view events to your audit report, but\nkeep in mind that doing so adds LOTS of events. If you're a glutton for\nthis kind of punishment, add this property to your\n\\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\naudit.message.com.liferay.portal.kernel.model.Layout.VIEW=true\n\\end{verbatim}\n\nLiferay DXP's code refers to pages as \\emph{layouts}. Setting this\nproperty to \\texttt{true} therefore records audit events for page views.\nIt's turned off by default because this is too fine-grained for most\ninstallations.\n\nOnce you've added the property, restart your server.\n\n\\section{Finding Audit Events}\\label{finding-audit-events}\n\nFinding what you want in a big list of events is like searching for a\nneedle in a haystack. This is why the Audit app has a robust search\nmechanism. By default, there's only a single search field. Clicking the\n\\emph{magnifier} icon next to the search bar, however, reveals an\nadvanced search dialog broken out by various fields you can use in your\nsearch.\n\nHere are the available search options:\n\n\\textbf{Match:} Search for matches to \\emph{all} the fields you've\nspecified or \\emph{any} single field.\n\n\\textbf{User ID:} The user ID to search for. This is usually the User\nwho performed some action you'd like to audit.\n\n\\textbf{User Name:} The user name to search for. This is often easier\nthan searching for a user ID, especially if you don't have access to the\ndatabase containing the user ID.\n\n\\textbf{Resource ID:} The ID of the resource that was modified or viewed\nin this audit record.\n\n\\textbf{Class Name:} The name of the resource that was modified or\nviewed in this audit record. For example, you could search for user\nresources to see if someone modified a user's account.\n\n\\textbf{Resource Action:} An action performed on the resource. This\ncould be any of these: \\texttt{add}, \\texttt{assign}, \\texttt{delete},\n\\texttt{impersonate}, \\texttt{login}, \\texttt{login\\_failure},\n\\texttt{logout}, \\texttt{unassign}, or \\texttt{update}.\n\n\\textbf{Session ID:} The session ID to search for. You can use this to\ncorrelate a session ID from your web server logs with activity in\nLiferay DXP.\n\n\\textbf{Client IP:} The IP address of the client that performed the\nactivity you wish to audit.\n\n\\textbf{Client Host:} The host name of the client that performed the\nactivity you wish to audit.\n\n\\textbf{Server Name:} The name of the server in which the activity\noccurred. If you're using a cluster, each member of the cluster can be\nindividually queried.\n\n\\textbf{Server Port:} The server port in which the activity occurred.\nYou need this if you run a vertical cluster of multiple VMs on the same\nmachine.\n\n\\textbf{Start Date:} The low end of the date range you wish to search\nfor.\n\n\\textbf{End Date:} The high end of the date range you wish to search.\n\nFor example, to check if someone unassigned a User from a particular\nRole, you might search for a resource name of \\emph{user} and a resource\naction of \\emph{unassign}. Once you have the search results, you can\nclick any of the returned records to see that record's detail page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/audit-unassign-search.png}\n\\caption{Searching for audit events is easy with the Audit app's\nadvanced search form. You can specify various search criteria to find\nthe types of events you want.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/audit-unassign-detail.png}\n\\caption{This record shows that the default administrative user removed\nthe Power User Role from the User Test Test.}\n\\end{figure}\n\nAs you can see, the Audit app shows you what's happening as Users make\nchanges. Use this information to troubleshoot problems, determine\nownership of particular actions, or, as Harry (from the story in the\nintroduction) is about to do, find out who made permission changes they\nweren't supposed to make.\n\n\\chapter{Configuring Audits}\\label{configuring-audits}\n\nAudits are enabled by default. The Audit app reports audit events, but\nyou can also report them in Liferay DXP's logs or console, enable them\nfor scheduled jobs, or disable them entirely.\n\nThere are two main ways to configure Liferay DXP:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Edit a configuration via the Control Panel. This saves the\n  configuration to the database.\n\\item\n  Edit a configuration via an OSGi configuration file (\\texttt{.config}\n  file) in your \\texttt{{[}Liferay\\ Home{]}/osgi/configs} folder.\n\\end{enumerate}\n\nThese methods apply to each of the audit configuration options explained\nbelow.\n\n\\section{Reporting Audit Events in Liferay's Logs and\nConsole}\\label{reporting-audit-events-in-liferays-logs-and-console}\n\nFollow these steps to use the Control Panel to configure the reporting\nof log events in Liferay DXP's log and console:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings} and select \\emph{Audit} from the \\emph{Security} section.\n\\item\n  In the \\emph{SYSTEM SCOPE} column on the left, select \\emph{Logging\n  Message Audit Message Processor}.\n\\item\n  Select the \\emph{Enabled} checkbox to report audit events in Liferay\n  DXP's log.\n\\item\n  Select the \\emph{Output to Console} checkbox to report audit events in\n  the console.\n\\item\n  In the \\emph{Log Message Format} selector menu, select the format for\n  the audit events (CSV or JSON).\n\\item\n  Click \\emph{Save} when you're finished.\n\\end{enumerate}\n\nAlternatively, you can make the same configuration via an OSGi\nconfiguration file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a file called\n  \\texttt{com.liferay.portal.security.audit.router.configuration.LoggingAuditMessageProcessorConfiguration.config}.\n\\item\n  Add these properties to the file:\n\n\\begin{verbatim}\nenabled=\"true\"\nlogMessageFormat=\"CSV\"\n#logMessageFormat=\"JSON\"\noutputToConsole=\"true\"\n\\end{verbatim}\n\n  Note that these are the same options set in the Control Panel. Edit\n  them as you see fit.\n\\item\n  Deploy the file to the \\texttt{{[}Liferay\\ Home{]}/osgi/configs}\n  folder. Note that the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home\n  folder} is typically the application server's parent folder.\n\\end{enumerate}\n\nRegardless of your configuration approach, you must also extend Liferay\nDXP's \\texttt{log4j-ext.xml} file to configure Log4j (Liferay DXP's\nlogging implementation) to log messages produced by the appropriate\nclass to the appropriate file. To do so, create a\n\\texttt{portal-log4j-ext.xml} file in\n\\texttt{{[}Liferay\\ Home{]}/tomcat-{[}version{]}/webapps/ROOT/WEB-INF/classes/META-INF}\nwith this configuration:\n\n\\begin{verbatim}\n<?xml version=\"1.0\"?>\n<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">\n\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\">\n\n    <!-- additional audit logging -->\n\n    <appender name=\"auditFile\" class=\"org.apache.log4j.rolling.RollingFileAppender\">\n        <rollingPolicy class=\"org.apache.log4j.rolling.TimeBasedRollingPolicy\">\n            <param name=\"FileNamePattern\" value=\"@liferay.home@/logs/audit.%d{yyyy-MM-dd}.log\" />\n        </rollingPolicy>\n        <layout class=\"org.apache.log4j.EnhancedPatternLayout\">\n            <param name=\"ConversionPattern\" value=\"%d{ABSOLUTE} %-5p [%t][%c{1}:%L] %m%n\" />\n        </layout>\n    </appender>\n\n    <category name=\"com.liferay.portal.security.audit.router.internal.LoggingAuditMessageProcessor\">\n        <priority value=\"INFO\" />\n        <appender-ref ref=\"auditFile\"/>\n    </category>\n</log4j:configuration>\n\\end{verbatim}\n\nThis configures Log4j to record INFO level messages from the class\n\\texttt{com.liferay.portal.security.audit.router.internal.LoggingAuditMessageProcessor}\nto a file called \\texttt{audit.yyyy-MM-dd.log} in the\n\\texttt{{[}Liferay\\ Home{]}/logs} folder. Adjust the audit file\nproperties or log level to your liking.\n\n\\section{Configuring Audit Events for Scheduled Liferay\nJobs}\\label{configuring-audit-events-for-scheduled-liferay-jobs}\n\nBy default, scheduled jobs don't trigger audit events. Follow these\nsteps to enable them via the Control Panel:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings}, and select \\emph{Infrastructure} from the \\emph{Platform}\n  section.\n\\item\n  In the \\emph{SYSTEM SCOPE} column on the left, select \\emph{Scheduler\n  Engine Helper}.\n\\item\n  Select the checkbox for \\emph{Audit scheduler job enabled} and click\n  \\emph{Save}.\n\\end{enumerate}\n\nAlternatively, you can make the same configuration via an OSGi\nconfiguration file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a file called\n  \\texttt{com.liferay.portal.scheduler.configuration.SchedulerEngineHelperConfiguration.config}.\n\\item\n  Add this property to the file:\n\n\\begin{verbatim}\nauditSchedulerJobEnabled=true\n\\end{verbatim}\n\\item\n  Deploy the file to the \\texttt{{[}Liferay\\ Home{]}/osgi/configs}\n  folder. Note that the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home\n  folder} is typically the application server's parent folder.\n\\end{enumerate}\n\nAuditing scheduled jobs is a smart choice if there's a chance someone\nwith a dubious competence level would try to schedule jobs, as you'll\nfind out below in the conclusion of our story.\n\n\\section{Enabling or Disabling Audit Events\nEntirely}\\label{enabling-or-disabling-audit-events-entirely}\n\nAudit events are enabled by default. Follow these steps to disable them\nvia the Control Panel:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings} and then click \\emph{Audit} in the \\emph{Security} section.\n\\item\n  Uncheck the \\emph{Enabled} box. Note that when auditing is enabled,\n  you can adjust the audit message max queue size from its default\n  value.\n\\end{enumerate}\n\nAlternatively, you can enable or disable auditing via an OSGi\nconfiguration file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a file called\n  \\texttt{com.liferay.portal.scheduler.configuration.SchedulerEngineHelperConfiguration.config}.\n\\item\n  Add these properties to the file. You can adjust their values as\n  desired:\n\n\\begin{verbatim}\nenabled=\"true\"\nauditMessageMaxQueueSize=\"200\"\n\\end{verbatim}\n\\item\n  Deploy the file to the \\texttt{{[}Liferay\\ Home{]}/osgi/configs}\n  folder. Note that the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home\n  folder} is typically the application server's parent folder.\n\\end{enumerate}\n\n\\section{The End of the Story}\\label{the-end-of-the-story}\n\n``Okay,'' says Harry, ``let's fire up the audit system and see if we can\nfigure out what happened.''\n\nYou and Dick stand behind Harry's chair and watch as he enters a query\ninto a form on the Audit app. After clicking \\emph{search}, the screen\nfills up with audit events.\n\n``Wow, that's a lot of unassign events.'' Harry says. ``And look who the\nculprit is,'' he adds sarcastically.\n\n``Who's Melvin Dooitrong?'' Dick asks.\n\n``That's my new intern,'' Harry says. ``He's gonna be sorry.'' Harry\npushes out his chair and walks down the row of cubes to the end, where a\nkid no more than 20 years old with disheveled hair sits, earbuds in his\nears.\n\n``Hey Melvin,'' Harry says as Melvin turns around to face him. ``Didn't\nI ask you to move that set of users from Site membership to Organization\nmembership?''\n\n``Yeah,'' Melvin says, ``I did that already.''\n\n``How'd you do it?''\n\n``It was going to take a while to do it manually, so I wrote a script\nand executed it in the scripting host,'' Melvin replies,\nmatter-of-factly.\n\n``You did, did you? Well, guess what? Your script removed\n\\emph{everybody} from \\emph{all} Sites.''\n\n``\\emph{What?}''\n\n``Yeah, and now you're going to start adding them back, one by one,\nmanually, starting with Mr.~Jones\\ldots.''\n\nTom and Dick back away slowly from Melvin's cube as Harry and Melvin\ncontinue to have their---let's call it a discussion. One thing is clear:\nthey're having a better day than Melvin is.\n\n\\chapter{Web Experience Management}\\label{web-experience-management}\n\nExperience: consider that word for a moment. Not the type of experience\nyou gain with repetition, but the contact or encounter you have with\nsomething. Suppose you're buying a new phone. It's easy to look at the\nphone with the biggest screen or the fastest processor and say that it's\nthe best, but what will your experience be? The ``best'' phone might not\nbe the fastest or the biggest; it might be the phone with the longest\nbattery life or the most comprehensive suite of integrated apps. Or it\nmight not be any of those things. Experience isn't always something that\nyou can quantify with specs or features.\n\nLiferay takes the experience factor very seriously when it comes to site\nand content management. The Web Experience Management suite is focused\non providing the best experience for users building websites. When it\ncomes to web experience, just like with your phone, everyone is looking\nfor something different. Some smartphone users might love watching\nvideos during their commute with a big beautiful screen, while on the\nother side, some people might be more excited about a small sleek phone\nwith a great battery life that fits easily in their pocket and simply\ndoes all the basic communication they need.\n\nLiferay's Content Management is the big beautiful phone and the sleek\nutilitarian one all in one. Marketers will find easy to use tools to\nbuild content without having to write any code or peak under the hood.\nDevelopers will find powerful tools like Structures and Templates that\nenable them to create dynamic content. And designers will love how\nFragments and Content pages provide a way to perfectly realize their\ndesigns.\n\n\\chapter{Authoring Content: Structured and Inline\nContent}\\label{authoring-content-structured-and-inline-content}\n\nThe primary goal of Content Management isn't to show off the flashiest\nnew features or follow all the latest trends in design, but to provide\nyou with the tools you need to create digital content that communicates\nyour message clearly and effectively. With this in mind, Liferay DXP\noffers two core approaches to help you accelerate and simplify creating\nand organizing content: Structured Web Content and Inline Content.\n\n\\section{Structured Web Content}\\label{structured-web-content}\n\nIf you've entered content into a CMS before, you may be familiar with\nthe process of filling content into various fields like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Title\n\\item\n  Abstract\n\\item\n  Text Body\n\\end{itemize}\n\nThis is an example of Structured Content. Structured Content is created\nwithin a predefined content structure and then added to pages as needed.\nThe structure defines the fields and then a template defines its styles.\nThe content is then saved, ready to be added to a page later.\n\nThe structure defines what kind of content you are creating and provides\ndifferent types of fields that can be used. A developer could create a\nformat for publishing articles that contains a \\textbf{Title},\n\\textbf{Header Image}, \\textbf{Body Text}, and a \\textbf{Key Quotation}.\nThe template defines how the elements of the structure are rendered. You\ncould style the elements in the structure with the \\textbf{Title} as\nlarge bold text, the \\textbf{Header Image} as a full page width block\nabove the title, the \\textbf{Text} as standard text, and the \\textbf{Key\nQuotation} as large font italics with a thin border that displays within\nthe main text section. A content writer or marketer could then create\nany number of articles, all having a uniform style based on the\nstructure.\n\nIn Liferay DXP, those articles could be added to pages across the site,\nor displayed dynamically with tools like the Asset Publisher and Web\nContent Display Pages.\n\n\\section{Inline Content}\\label{inline-content}\n\nInline Content is content that is created directly within a page. Rather\nthan filling in fields to create content that's added to a page later,\nyou have a completed page design where you edit the text and image\ncontent. Content Pages start with a design which is then created with\nFragments. Inside the Fragments, a developer can define where text and\nimages can be placed or edited. Marketers and content writers are then\nfree to write or add images within the page and publish it.\n\nWith content pages, basic HTML and CSS define the primary design, while\nJavaScript and Liferay specific tags can add dynamic behavior to the\nContent Page. After a developer creates the page and a content writer or\nmarketer provides the content, the content exists inline within the\npage, and is published with it.\n\n\\section{What's Best for Your Use\nCase?}\\label{whats-best-for-your-use-case}\n\nWhen you step back to look at the big picture, what you see are two\ndifferent paradigms for building pages: content pages, where the content\nis built into the page; and widget pages, where content and other\nfeatures can be added, removed, and rearranged as desired.\n\nOften it is helpful to have reusable elements or content that can be\nmoved around a page or placed anywhere on a site. For example, you might\nhave a content based banner which you want to be able to drop onto any\nvarious pages with different layouts and styles, or you might have\ncontent that uses the same template to create similar items for\ndifferent pages. Structured Content on widget pages is the tool you need\nto quickly create what you need and manage these cases. Widget pages\nwith structured content are great for some cases, for example:\n\n\\begin{itemize}\n\\item\n  Portal pages, where you provide users a gateway into several different\n  services or providing aggregated information.\n\\item\n  Pages where the primary focus is widgets.\n\\item\n  Pages that are based around structured content.\n\\end{itemize}\n\nIn other cases, you need to create a page as a complete unit. For\nexample, you have a series of marketing driven landing pages that must\nmatch a specific design and have associated content intended for use on\nthat page or with that campaign. Content Pages provide the best tool for\nquickly bringing a design to life and empowering marketing with inline\ncontent. Content Pages are useful for pages like:\n\n\\begin{itemize}\n\\item\n  Landing pages\n\\item\n  Front pages that provide marketing information or a direct path into\n  the website.\n\\item\n  Pages with multiple variations and small graphical or textual changes\n  across a large number of pages.\n\\end{itemize}\n\nMost sites need a little bit of both. Read on to learn more about\nbuilding sites with Liferay DXP and how Content Pages and Structured\nContent can help you do that.\n\n\\chapter{Building a Site}\\label{building-a-site}\n\nA site is a set of pages where content or applications are published.\nSites can be independent or serve as an associated organization's\nwebsite. You can create as many different sites as you like within the\ncontext of a single Liferay instance.\n\nYou'll start with a tour of the site management user interface. Then\nyou'll create a custom Lunar Resort Example instance and explore ways to\ncreate sites and pages for that Liferay instance. Finally, you'll learn\nhow to change various settings for sites and pages to meet your needs.\nTo begin building a site, continue on to the next section.\n\n\\chapter{Site Management}\\label{site-management}\n\nYou can have many Sites on one Liferay instance, which work together to\ncreate one complete website, or you can simply have one Site which\ncontains all of your pages and content---or anything in between. In this\nsection you'll look at the interface for creating and managing Sites,\ncreate a Site, and learn how to use Site Templates for more efficient\nSite creation.\n\n\\chapter{Understanding Site\nManagement}\\label{understanding-site-management}\n\nWhether you're building a large corporate web Site or a small Site for\nfacilitating collaboration among team members, supporting different\nkinds of collaboration and social scenarios is a must. Liferay's Sites\nprovide three membership types:\n\n\\textbf{Open:} Users can become members of the Site at any time.\n\n\\textbf{Restricted:} Users can request Site membership but Site\nadministrators must approve requests for users to become members.\n\n\\textbf{Private:} Users cannot join the Site or request Site membership.\nSite administrators must manually select users and assign them as Site\nmembers.\n\nIn addition to these memberships, when a Site is associated with an\norganization, all the users of that organization are automatically\nconsidered members of the Site.\n\nYou can view all the available open and restricted Sites by adding the\nMy Sites application to a page and accessing the \\emph{Available Sites}\ntab. You can request access to any of the Sites you're not already a\nmember of by selecting the Site's \\emph{Options} button\n\n(\\includegraphics{./images/icon-actions.png}) and clicking \\emph{Join}.\n\n\\section{Site Scope}\\label{site-scope}\n\nMembers of a Site can be given additional privileges in the Site by\nusing permissions. It is also possible to assign different roles within\nthe Site to different members. This can be done through \\emph{Site\nRoles}, which are defined equally for all Sites or \\emph{Teams} which\nare unique for each Site. These concepts are discussed later.\n\nLiferay DXP separates Site-scoped information from the Control Panel by\nplacing it in the Site menu. From this menu, you can select the specific\nSite to work on. The Site Administration panel is available for your\nSite, which includes Build, Content, Categorization, Recycle Bin,\nMembers, Configuration, and Publishing.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-site-content.png}\n\\caption{Your Site's content resides in the Site Administration menu.}\n\\end{figure}\n\n\\section{Site Hierarchies}\\label{site-hierarchies}\n\nSites can also be organized hierarchically, just like Organizations. The\ndifference between Sites and Organizations, of course, is that Sites\norganize pages, content, application data, and users (via Site\nmemberships) whereas organizations only group users. Content sharing is\navailable for Sites within the same hierarchy. For instance, if a parent\nSite has a document type called \\emph{Lunar Presentation} and all its\nchild Sites should have a copy, the parent Site's administrator can\nenable content sharing to share the document type automatically with its\nchild Sites. Also, content sharing privileges can be set to let every\nSite administrator share content across Sites they manage. You can share\nthe following content across Sites:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Web Content Structures\n\\item\n  Web Content Templates\n\\item\n  Document Types\n\\item\n  Vocabularies and Categories\n\\item\n  Widget Templates\n\\item\n  Data Definitions (Dynamic Data Lists)\n\\end{itemize}\n\nPlease refer to the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Sites\\%20Admin\\%20Portlet}{Sites\nAdmin Portlet} section of Liferay's \\texttt{portal.properties} file for\na list of relevant configurable properties. For example, the\n\\texttt{Sites.content.sharing.with.children.enabled} property can\ndisable content sharing between Sites and child Sites, disable it by\ndefault while allowing Site administrators to enable it per Site, or to\nenable it by default while allowing administrators to disable it per\nSite.\n\nThe Sites Directory application is a configurable app that shows a\nhierarchy of Sites and child Sites. It enables users to navigate to any\nof the displayed Sites. To use this app to display Site hierarchies, add\nit to a page, open its Configuration window, and under Display Style,\nselect \\emph{List Hierarchy}. The My Sites Directory application is\nsimilar to the Sites Directory application, except that it lists only\nthe Sites a user belongs to.\n\nEach child Site in the hierarchy has its own administrator, and the Site\nAdministrator role permissions do not flow down to child Sites in the\nhierarchy. If a Site Administrator creates a child Site, he or she has\nthe same permissions in that child Site. This is not, however, because\nof inheritance. It is only because creating a Site makes you the Owner\nof that Site. A Site Administrator or a parent Site has no default role\nin any child Sites created by other Site Administrators.\n\nIf you wanted a user to have administrative access to all Sites in a\nSite/child Site hierarchy, you must create a role based on the Site\nAdministrator role that has the permission \\emph{Manage SubSites}.\n\nThe Site Map application helps users navigate a Site. A Site\nadministrator can configure a root page and a display depth. Just as\nSites can have hierarchies, so can the pages within a Site. The display\ndepth of the Site Map application determines how many levels of nested\npages to display.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/site-directory-site-map.png}\n\\caption{The Site Map application lets users navigate among pages of a\nSite organized hierarchically.}\n\\end{figure}\n\n\\section{Site Members}\\label{site-members}\n\nAnother useful administrative application is the Site Members\napplication. This enables administrators to survey all the users,\norganizations, and user groups that reside in the Site. Similarly,\nLiferay provides the Portal Directory application, which functions the\nsame as the Site Members app, but globally scoped for all Sites in the\ninstance.\n\n\\section{Page Sets}\\label{page-sets}\n\nSites have two categories of pages called page sets. There are two kinds\nof page sets: public pages and private pages. A Site can have only\npublic pages, only private pages, or both. Private pages can only be\naccessed by Site members. Public pages can be accessed by anyone,\nincluding users who haven't logged in. It's possible to restrict access\nto pages at the page set level or at the level of individual pages\nthrough the permissions system. Public pages and private pages have\ndifferent URLs and can have different content, applications, themes, and\nlayouts.\n\nBuilding a corporate intranet is a typical use case for Sites. A\ncorporate intranet could have Sites for all the organizations in the\ncompany: Sales, Marketing, Information Technology, Human Resources and\nso on. But what about the corporate health and fitness center? That's\nsomething everybody in the company, regardless of organization, may want\nto join. This makes it a good candidate for an open and independent\nSite. Similarly, the home page for a corporate intranet should probably\nbe placed in an open independent Site so any member of the instance can\naccess it.\n\nFor other kinds of websites, you may want to use independent Sites to\nbring users together who share a common interest. If you were building a\nphoto sharing website, you might have independent Sites based on the\ntypes of photos people want to share. For example, those who enjoy\ntaking pictures of landscapes could join a Landscapes Site and those who\nenjoy taking pictures of sunsets could join a Sunsets Site.\n\nThere is always one default Site, which is also known as the main Site\nof the instance. This Site does not have its own name but rather takes\nthe name of the instance. By default the instance name is \\emph{Liferay}\nbut this value can be changed through the configuration of the setup\nwizard. The instance name can also be changed at any time through the\nControl Panel within \\emph{Configuration → }Instance Settings*.\n\n\\chapter{Adding Sites}\\label{adding-sites}\n\nSites can be created through the Control Panel by a Liferay\nadministrator. The Control Panel provides an administrative interface\nfor managing your Liferay instance. There are four main sections of the\nLiferay Control Panel: Users, Sites, Apps, and Configuration. In this\nsection, you'll learn how to use the Control Panel to manage Sites. For\ninformation about the Apps, Users, and Configuration sections of the\nControl Panel, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/using-the-liferay-marketplace}{Using\nthe Liferay Marketplace},\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-users}{Managing Users},\nand \\href{/docs/7-2/user/-/knowledge_base/u/system-wide-settings}{System\nWide Settings} sections, respectively.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} If you're signed in as an administrator, you can access\nall Sites by navigating to the Site Administration menu from the Control\nPanel. To manage a single Site, navigate to the Site by going to the\nMenu and clicking the \\emph{Site Selector} button\n(\\includegraphics{./images/icon-compass.png}) from the Sites dropdown\nmenu and selecting the appropriate Site name. Once finished, the Site\nadministration options (i.e., Navigation, Content, Members, etc.) for\nthat Site are available.\n\n\\noindent\\hrulefill\n\nNow, you'll add a Site for the Lunar Resort.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Control Panel and select \\emph{Sites} → \\emph{Sites}.\n\\item\n  Click the Add icon (\\includegraphics{./images/icon-add.png}) at the\n  top right of the page.\n\\item\n  Select a \\emph{Blank Site}.\n\n  Any available Site templates appear for you to select. Site templates\n  provide a preconfigured set of pages, applications, and content that\n  can be used as the basis of a Site's public or private page set. To\n  create a Site from scratch, select \\emph{Blank Site}. Otherwise,\n  select the name of the Site template you want to use. If you opt to\n  create a Site from a Site template, you have to choose whether to copy\n  the Site template's pages as your new Site's public or private page\n  set. If other Site templates are created, they will appear in the Add\n  menu as they become available.\n\\item\n  Name your Site ``The Lunar Resort''\n\\end{enumerate}\n\nAfter you enter the name, you will be prompted to enter additional\ninformation about the Site and configure certain Site settings.\n\n\\textbf{Name:} names the Site you wish to create. You also have the\noption to translate the name for many different languages. This can be\ndone by selecting the language flag under the Name field, and inserting\nthe name in the selected language. Liferay saves the name translation\nfor each language and displays the translated Site name when that\nspecific language is selected for the instance. If a name translation is\nnot provided, the default instance language's name is displayed.\n\n\\textbf{Description:} describes the Site's intended function. The\ndescription can also be translated to other languages; see the Name\ndescription for more information on translating the Site's description.\n\n\\textbf{Active:} determines whether a Site is active or inactive.\nInactive Sites are inaccessible but can be activated whenever a Site\nadministrator wishes.\n\n\\textbf{Membership Type:} can be open, restricted, or private. An open\nSite appears in the My Sites app and users can join and leave the Site\nwhenever they want. A restricted Site is the same except users must\nrequest membership. A Site administrator must then explicitly grant or\ndeny users' requests to join. A private Site does not appear in the My\nSites app and users must be added to it manually by a Site\nadministrator.\n\n\\textbf{Allow Manual Membership Management:} determines whether to allow\nor disallow users to be manually added or removed from the Site. By\ndefault, manual Site membership management is enabled. This allows\nadministrators to manually assign users to the Site. It also allows\nusers to join open Sites or request membership from restricted Sites\nusing the My Sites app. For organization Sites, manual Site membership\nmanagement is disabled, by default. This causes organization members to\nbe automatically assigned membership following the organization's\nmembership policy. Also, because manual membership management is\ndisabled for organization Sites, by default, the \\emph{Users} section of\n\\emph{Sites} is unavailable. To activate the \\emph{Users} functionality\nfor your organization Site, you'll need to check \\emph{Allow Manual\nMembership Management} after creating the organization Site by\nnavigating to its \\emph{Site Settings} menu.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} It's possible for Site memberships to be handled\nautomatically by a membership policy. The membership policy can check\nvarious pieces of information from each user, such as their first names,\nlast names, birthdays, job titles, organizations, and user groups. Using\nthis information, the Site membership policy can automatically assign\nmembers to the Site. If your Site will implement a membership policy,\nyour Site administrators can disallow manual membership management for\ntheir Site. When the Allow Manual Membership Management option is\ndisabled, the \\emph{Members} section of Site Administration (Site\nMemberships and Site Teams) is hidden, even from administrators.\n\n\\noindent\\hrulefill\n\n\\textbf{Parent Site:} lets you select a parent Site for the Site that's\nbeing created. Sites can be organized hierarchically. Using hierarchical\nSites provides a simplified way to manage Site memberships and Site\ncontent sharing. For organizations that have attached Sites, the\norganization hierarchy should match the Site hierarchy. When you select\na parent Site, an additional option appears: \\emph{Limit membership to\nmembers of the parent Site}. If this option is enabled, the Site's\nmembership policy performs a check so that you can only assign members\nto the current Site if they're already members of the parent Site.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Set the \\emph{Membership Type} as \\emph{Restricted}.\n\\item\n  Leave the remain defaults and click \\emph{Save}.\n\\end{enumerate}\n\nWhen creating a blank Site or organization Site, the Site is not\nimmediately viewable. This is because Sites without a page are\nimpossible to view. Therefore, before you can view your Site, you must\nfirst create a page for it. To add a page for your temporarily invisible\nSite, navigate to the \\emph{Navigation} option from Site Administration.\nThen add a public page. After adding your Site's first page, it renders\nand your Site is viewable. For more information about adding pages, see\nthe\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-and-managing-pages}{Creating\nand Managing Pages} section.\n\nYou can also categorize your Site template using tags and categories by\nselecting the \\emph{Categorization} menu from the bottom of the page. To\nlearn more about using tags and categories in Liferay, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/organizing-content-with-tags-and-categories}{Organizing\nContent with Tags and Categories} section. Lastly, at the top of the\npage is an additional tab named \\emph{Social}. This tab manages whether\nusers of your Site can mention other users. You'll learn about\nmentioning users later in the Social Collaboration sections.\n\nWhen creating a Site from a Site template, you're asked if you want to\ncopy the pages from the template as public pages or as private pages. By\ndefault, the Site is linked to the Site template and changes to the Site\ntemplate propagate to any Site based on it. A checkbox appears for\nunlinking the Site template if the User has permission to do so.\n\nOnce the Site has been created, you should configure its settings to fit\nyour needs. You can learn more about Site Settings in\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-sites}{Configuring\nSites}.\n\n\\chapter{Adding Pages to Sites}\\label{adding-pages-to-sites}\n\nIn the previous section, you learned how to create sites. You may have\ngathered from that section that sites aren't particularly useful without\npages. In fact, sites primarily exist for the sake of organizing pages\nand content, so now you'll learn about the different types of pages in\nLiferay, and how to select the best tools based on your use cases.\nYou'll also learn how to manage pages and use various configuration\noptions.\n\nBefore diving into page creation, you should understand the two major\npage types provided in 7.0:\n\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-pages}{\\emph{Content\nPages}}: This new page type is flexible, especially for non-technical\nusers. You can build pages using content created from pre-defined\nfragments, which themselves can contain widgets.\n\n\\href{/docs/7-2/user/-/knowledge_base/u/adding-applications-to-a-page}{\\emph{Widget\nPages}}: Liferay DXP's traditional page type is made up of one or more\nwidgets. There are some use cases (particularly if a page's sole purpose\nis to host an application) to prefer widget pages.\n\nYou should always opt for Content Pages, unless there's a concrete\nreason otherwise. Content Pages offer many key features of Widget Pages\nplus more. Some key features of Content Pages include\n\\href{/docs/7-2/user/-/knowledge_base/u/segmentation-and-personalization}{personalized\nExperiences} and\n\\href{https://help.liferay.com/hc/en-us/articles/360034856751-A-B-Testing}{A/B\nTesting} Furthermore, Content Pages are easier to use and are more\npowerful for non-technical users compared to Widget Pages.\n\nWhy would someone prefer Widget Pages? Widget Pages were once the only\npage type available in earlier versions of Liferay DXP, so they're more\nfamiliar than Content Pages.\n\nAdditionally, there are still a few things that Widget Pages provide\nthat are not possible with Content Pages:\n\n\\begin{itemize}\n\\item\n  \\emph{Developing an advanced custom layout}: Using Content Pages,\n  authors can create their own page layouts. This prevents developers\n  from creating pre-selectable, custom layouts with FreeMarker like\n  Widget Pages allowed for. Though Content Pages let you create a layout\n  visually (a more user-friendly approach), the programmatic approach of\n  Widget Pages allows for more advanced capabilities.\n\\item\n  \\emph{User-Customizable columns}: This was a rarely used feature of\n  Widget Pages that is not provided in Content Pages. If your page\n  requires a user-customizable column, you must use a Widget Page.\n\\item\n  \\emph{Using Staging with Page Variations}: Content Pages do not\n  support Staging's Page Variations. This avoids possible confusion with\n  the similar capability to create variations of a page that are used in\n  personalization and A/B Testing.\n\\item\n  \\emph{Widget permissions}: You can configure widget permissions on a\n  Widget Page. This is not yet possible for Content Pages; it's planned\n  for a future release.\n\\item\n  \\emph{Widget Look \\& Feel}: On Widget Pages you can access the\n  \\href{/docs/7-2/user/-/knowledge_base/u/look-and-feel-configuration}{Look\n  and Feel} tool for widgets, offering fine-grained control over its\n  CSS. This is not available for widgets on Content Pages, since the\n  look and feel of your content is defined in the theme or by using\n  Fragments.\n\\end{itemize}\n\nContinue on to learn more about creating pages!\n\n\\chapter{Creating Pages}\\label{creating-pages}\n\nAfter you create a Site, you can add new pages and maintain them. You\ncan do everything you need with pages from Site Administration.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  If you're not currently on the Site you want to edit, click the\n  \\emph{Site Selector} button\n  (\\includegraphics{./images/icon-compass.png}) next to your current\n  Site name in the Menu and select your desired Site.\n\\item\n  Go to \\emph{Site Administration} → \\emph{Site Builder}.\n\\item\n  Click on \\emph{Pages}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/managing-site-pages.png}\n\\caption{The Pages screen lets you edit your Site pages as a whole.}\n\\end{figure}\n\nFrom here, you'll create pages and page templates.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Pages are always part of page sets, and page sets are\nalways associated with Sites. Even users' personal pages are part of\ntheir personal Sites. All pages belong to one of two types of page sets:\npublic pages and private pages. By default, anyone can access public\npages, even non-logged in users (guests). Only users who are members of\nthe Site that owns the pages can access private pages. This means the\nprivate pages of an organization's Site are viewable only by Site\nmembers and members of the organization.\n\n\\noindent\\hrulefill\n\nFrom \\emph{Pages} you can do several things:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the (\\includegraphics{./images/icon-add.png}) button in the top\n  right corner to add a new page.\n\\item\n  Click options, icons, manage page, or page set settings.\n\\item\n  Create child pages by clicking the \\texttt{+} button next to an\n  existing page.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/site-pages-breakdown.png}\n\\caption{Understanding the options on Site Pages.}\n\\end{figure}\n\nAdding a child page creates child pages in the hierarchy below the page\nyou've selected. You can nest pages as deep as you like.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You're not forced to define the page hierarchy in a\npage's friendly URL. Therefore, child page friendly URLs are not\nrequired to include their parent page. For example, a parent page named\nParent and its child page named Child could have the URLs\n\\emph{SITE\\_URL/parent} and \\emph{SITE\\_URL/child}, respectively. The\ndefault friendly URL given to a page is based only on the page name and\nnot the hierarchy. If you wish to modify a generated friendly URL, you\ncan do so by following the\n\\href{/docs/7-2/user/-/knowledge_base/u/individual-page-settings\\#name-and-friendly-url}{Friendly\nURL} configuration section.\n\n\\noindent\\hrulefill\n\nOnce you've clicked the \\texttt{+} icon to add a page, you're asked to\nselect the type of page you are creating. There are two top options\nfollowed by other page types:\n\n\\textbf{Widget:} Creates a page with a layout template that defines a\nnumber of rows and columns for adding widgets to your page.\n\n\\textbf{Content:} Creates a Content Page with inline editing based on\nFragments.\n\nBelow those you have other options:\n\n\\textbf{Full Page Application:} Creates a page that displays a single\nfull page application.\n\n\\textbf{Page Set:} Creates a container for subpages that is not actually\na page itself.\n\n\\textbf{Link to a Page of this Site:} Links to a page within the same\nSite. This is often used to make a page available in multiple parts of a\nSites hierarchy.\n\n\\textbf{Panel:} A page containing any number of applications as selected\nby an administrator, but only one is displayed at a time. Users select\nthe portlet they want to use from a menu on the left side of the page,\nand the selected portlet takes up the entire page.\n\n\\textbf{Embedded:} Displays content from another website inside your\ninstance. An administrator can set a URL from the page management\ninterface and that page appears in the context and within the navigation\nof your Liferay instance. To embed an external website, you must provide\nthe protocol in the URL (e.g.~\\texttt{https://www.liferay.com/}).\n\n\\textbf{Link to URL:} Creates a link to any URL. This could be an\nexternal page or a link across Sites in the same Liferay instance.\n\nTo the left, under Collections, you can choose to view the basic page\ntypes or a collection of page templates. By default, only \\emph{Global\nTemplates} appears, but additional collections you create appear here as\nwell.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/page-types-adding.png}\n\\caption{You must select a page type when adding pages.}\n\\end{figure}\n\nAfter you've added a page, it may be difficult to track what kind of\npage you're currently viewing. The page type appears at the top of the\npage to help you determine the administration options you have and where\nyou need to go to configure the page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/page-type-guide.png}\n\\caption{Here are three different page types as they're displayed in the\nheading.}\n\\end{figure}\n\nNow that you know the basics of adding pages, you can start working on\nthe Lunar Resort Site. If you're not currently on the right Site,\nnavigate to Site Administration in the Menu, select the compass icon\nnext to the current Site name, and select the Site you wish to edit.\n\nIf you must ever modify the page you've created for your Site, select\n\\emph{Configure} from the Options menu for the page from \\emph{Pages}.\nWhen configuring a specific page, you have more options than when you\nwere creating a new page. You can also read\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-sites}{Configuring\nSites}.\n\nThere are also configuration options that are only available for\nindividual pages or page groups only. You'll learn about options\navailable for both instances.\n\nNext, you'll look at creating the main page types you'll use in Liferay\nDXP.\n\n\\chapter{Creating Content Pages}\\label{creating-content-pages}\n\nContent Pages provide a new paradigm for creating pages in Liferay DXP.\nThey empower marketers and content creators to build pages that can be\neasily managed and have their content edited in-line and on the fly.\n\nWith Content Pages, you build pages from reusable, extensible elements\ncalled \\textbf{Page Fragments}. Page Fragments contain things like\nimages, content, or functionality and are added to a page to be edited\nand configured to match your design. Web Developers can create Page\nFragments to expand the options you have available for building pages.\n\nSome Fragments are completely static elements, like an image or text.\nThese can be useful for building a landing page, or realizing a\ncomplicated design vision. Other Fragments have a number of editable\nfields which enable flexibility and experimentation when creating the\nperfect page for your needs.\n\n\\section{Creating Page Fragments}\\label{creating-page-fragments}\n\nThe first thing you need to create a page, are the raw materials: Page\nFragments. Fragments are organized into Collections which describe some\naspect of the purpose of the Fragments and help keep them organized. For\nexample, if you have a number of Fragments associated with a certain\nmarketing campaign, you could create a Collection for that campaign, so\nthat you have all of those Fragments readily available when you're\nworking on a page for that campaign.\n\nWith all of the Fragments and Collections that are included in 7.0, many\nprojects can be completed using only the out-of-the-box Fragments and\ncapabilities. In many cases, though, you need to develop your own\nFragments. You can learn about the Fragment creation process and editing\ninterface in the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-page-fragments}{Creating\nPage Fragments} article. Developing Fragments is covered in more detail\nin the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/page-fragments}{Page\nFragment development guide}.\n\n\\section{Building Content Pages}\\label{building-content-pages}\n\nAfter you've got your materials, you need to arrange them on a page.\nContent Pages are built with Page Fragments and Widgets. Widgets work\njust like they always have, drag them onto the page where you want to\nuse them. Fragments are intended to be reusable, so each Fragment is\nlike a single puzzle piece that can fit in many different puzzles. Using\nthe tools you have available, you can create stunning pages through an\nintuitive, empowering interface. In the\n\\href{/docs/7-2/user/-/knowledge_base/u/building-content-pages}{Building\nContent Pages guide} you will create Content Pages using a variety of\nthe features and capabilities of Content Pages.\n\n\\section{The Content Page Interface}\\label{the-content-page-interface}\n\nThere are many features of the Content Page Interface---too many to\ncover in a practical exercise. For a complete overview of every nook and\ncranny of the Content Page management interface see the\n\\href{/docs/7-2/user/-/knowledge_base/u/content-page-management-interface}{Content\nPage Interface reference}.\n\n\\section{Personalizing Content Pages}\\label{personalizing-content-pages}\n\nWhen you create Content Pages, you can create different\n\\textbf{Experiences} for users based on User Segments. You can create a\nunique Experience on any Content Page for any existing User Segment. For\nmore information, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/content-page-personalization}{Content\nPage Personalization guide}.\n\n\\chapter{Content Page Elements}\\label{content-page-elements}\n\nContent Pages, like Widget Pages, are built by dragging and dropping\nelements onto the page and then configuring the way those elements\nappear. There are three kinds of elements:\n\n\\textbf{Sections} are fragments that define a space to place other\nelements. A section fills the entire width of the page. Sections can be\nthought of as \\emph{complete} Fragments that serve a purpose by\nthemselves. A large banner image with a text overlay is an example of\nsomething you might build as a section.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-section-example.png}\n\\caption{A Section named \\emph{Banner} being displayed while editing a\nContent Page.}\n\\end{figure}\n\n\\textbf{Layouts} are special Sections that define spaces where you can\nadd fragments or widgets. Each layout you add fills the width of the\npage. You can add any number of layouts to the page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-layout-example.png}\n\\caption{A 3 Column and 1 Column layout stacked on top of each other.}\n\\end{figure}\n\n\\textbf{Components} are small design elements or pieces that add\nfunctionality to the page. A component might be an image with formatting\nor a block of text with styling pre-applied. Components must be added to\nthe page inside a Layout. If you add a component outside an existing\nLayout, a one column layout is automatically added to contain the\nComponent. While Sections should be complete by themselves, Components\nwork together to build pages piece by piece.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-component-example.png}\n\\caption{Here are several of Liferay's out of the box components\narranged in the layout you saw previously.}\n\\end{figure}\n\nLiferay DXP ships with a plethora of Layouts, Sections, and Components\nto use to build pages, and a\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-fragments}{web\ndeveloper can create their own Fragments} to add to these.\n\n\\section{Editable Elements}\\label{editable-elements}\n\nFragments can have editable elements. After a Fragment has been added to\na page, you can click on an editable area to provide your own text,\nimage, or links in place of the default defined in the Fragment.\n\nYou can also map these elements to content. You can set the\n\\emph{Content} for the element (web content article, document, or blog)\nand choose its applicable \\emph{Field} to display (e.g., title, author\nname, tags, etc.). You can configure this by selecting the element's\n\\emph{Map} button (\\includegraphics{./images/icon-map.png}).\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Many mapping improvements were released in Liferay DXP\n7.2 SP1+ and Liferay Portal 7.2 GA2+. For example, mapping editables to\ntext/URL fields of existing content and mapping Fragment background\nimages to image fields of existing content. You can also map\n\\href{/docs/7-2/user/-/knowledge_base/u/custom-fields}{custom fields}.\nTo ensure you leverage the latest editable element mapping features,\nupgrade to these versions.\n\n\\noindent\\hrulefill\n\nFor more information on developing these elements, see\n\\href{/docs/7-2/reference/-/knowledge_base/r/fragment-specific-tags}{Fragment\nSpecific Tags}.\n\nNow you'll learn about each editable type.\n\n\\section{Editable Text}\\label{editable-text}\n\nEditable text can be plain or rich text. Plain text has no special\nstyling. Rich text enables text styles, typographical emphasis,\nalignment, and list formatting.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-rich-text-editor.png}\n\\caption{The rich text editor provides a simple WYSIWYG interface with a\nnumber of formatting options.}\n\\end{figure}\n\n\\section{Editable Images}\\label{editable-images}\n\nEditable image elements allow replacing the image URL or an image from\nyour Documents and Media library. You can provide a link target for the\nimage.\n\nTo edit an image from the Content Page editor,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on the image you want to replace.\n\\item\n  Click \\includegraphics{./images/icon-edit.png}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-image-editor.png}\n\\caption{Editing an image allows you to enter a URL, select an image\nfrom Documents and Media or set a link for the image.}\n\\end{figure}\n\nFrom here, you can click \\emph{Select} to upload an image from Docs and\nMedia or define an image URL. Click \\emph{Clear} to reset the image. You\ncan also specify an image description.\n\nYou can also specify a background image for a layout from Section\nBuilder. Click the Layout, select \\emph{Layout Background Image}, and\ndefine the image to display.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Mapping a Layout background image is available in Liferay\nDXP 7.2 SP1+ and Liferay Portal 7.2 GA2+.\n\n\\noindent\\hrulefill\n\nFor more information on developing editable images, see\n\\href{/docs/7-2/reference/-/knowledge_base/r/fragment-specific-tags\\#making-images-editable}{Making\nImages Editable}.\n\nYou can also define a link for your image. You'll learn about this next.\n\n\\section{Editable Links}\\label{editable-links}\n\nEditable links can be associated with entities that redirect you to a\ncontent type or Page (e.g., buttons).\n\nTo edit a link from the content page editor,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on the link or button that you want to edit.\n\\item\n  Click on \\includegraphics{./images/icon-edit.png} to edit the link\n  text.\n\\item\n  Click on \\includegraphics{./images/icon-link.png} to edit the link\n  properties.\n\\item\n  Click on \\includegraphics{./images/icon-map.png} to edit the link\n  mapping (described earlier).\n\\end{enumerate}\n\nFrom the Link Properties popup, you can define the following link\noptions:\n\n\\emph{Manual:} defines a manual link or map it to an existing content\nfield.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{URL:} sets the link's URL.\n\\item\n  \\emph{Target:} set the link's behavior.\n\\end{itemize}\n\n\\emph{From Content Field:}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Content:} sets the content type.\n\\item\n  \\emph{Field:} sets the field to display for the selected content.\n\\end{itemize}\n\nSome of the content fields include\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Categories\n\\item\n  Tags\n\\item\n  Display Page URL\n\\item\n  Description\n\\item\n  Publish Date\n\\item\n  Summary\n\\item\n  Title\n\\item\n  Last Editor Name\n\\item\n  Author Name\n\\item\n  Basic Web Content\n\\end{itemize}\n\nFor more information on developing editable links, see\n\\href{/docs/7-2/reference/-/knowledge_base/r/fragment-specific-tags\\#creating-editable-links}{Creating\nEditable Links}.\n\nNext you'll learn about the Content Page Editing Interface.\n\n\\chapter{Content Page Management\nInterface}\\label{content-page-management-interface}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nUnlike Widget Pages, Content Pages can only be edited through the\n\\emph{Site Builder} and cannot be edited live on the page. Any edits\nthat you make to a page are saved as a draft until you publish the page.\nSubsequent changes after the initial publication are again saved as a\ndraft, without affecting the live page, until the page is published\nagain. To create a Content Page,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Management} → \\emph{Site Builder} → \\emph{Pages}.\n\\item\n  Click \\includegraphics{./images/icon-add.png}.\n\\item\n  On the next page, select \\emph{Content Page} and provide a name for\n  the page.\n\n  You will be brought to the Content Page management interface.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/content-page-edit-blank-page.png}\n  \\caption{Each Content Page starts as a blank page.}\n  \\end{figure}\n\\end{enumerate}\n\nTo edit an existing Content Page,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Site Builder} → \\emph{Pages}.\n\\item\n  Click \\emph{Actions}\n  (\\includegraphics{./images/icon-staging-bar-options.png}) →\n  \\emph{Edit} next to the Content Page you want to edit.\n\\end{enumerate}\n\nYou can also get to this page by selecting the \\emph{Edit} button\n(\\includegraphics{./images/icon-edit-pencil.png}) from the Control Menu\nif you're viewing the published Content Page.\n\nOn this page you can view a preview of your page, add Fragments and\nWidgets, and manage the configuration for the page or any Fragments and\nWidgets currently residing on the page.\n\nYour tools for building the page are all found on the right side of the\npage. From top to bottom, the options are\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[sections]{Sections}\n\\item\n  \\hyperref[section-builder]{Section Builder}\n\\item\n  \\hyperref[widgets]{Widgets}\n\\item\n  \\hyperref[page-structure]{Page Structure}\n\\item\n  \\hyperref[look-and-feel]{Look and Feel}\n\\end{itemize}\n\n\\section{Sections}\\label{sections}\n\nWhen you open \\emph{Sections} you see a list of Collections available.\nInitially, you only have the \\textbf{Basic Sections} Collection which is\nincluded with the product. You can open the Collection and drag Sections\ndirectly onto the page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-sections-editor.png}\n\\caption{\\emph{Sections} contains Fragments that fully define spaces on\nyour page.}\n\\end{figure}\n\nOnce a section is added to a page, you can edit its background color,\nbackground image, and spacing. Since these options are available to\nmarketers and administrators editing a page, the options are limited,\nand the color palette can be set by the Fragment developer.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-sections-config.png}\n\\caption{The Section managment tool provide powerful tools, but with the\ntraining wheels still on.}\n\\end{figure}\n\n\\section{Section Builder}\\label{section-builder}\n\nIn Section Builder, you start with \\emph{Layouts} and \\emph{Basic\nComponents}. Add Layouts to the page to provide a spaces where you can\nadd Components.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-section-builder-editor.png}\n\\caption{\\emph{Sections Builder} contains \\emph{Component} Fragments\nwhich are intended to be combined to create Sections.}\n\\end{figure}\n\n\\section{Widgets}\\label{widgets}\n\nThe Widgets section functions just like the \\emph{Add} menu on a Widget\nPage. The full list of available widgets is displayed, and you can add\nthem to the page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-widget-editor.png}\n\\caption{The Widgets section provides a list of Widgets that can be\nadded inside of a Layout.}\n\\end{figure}\n\nThe main difference is that only the main configuration options are\navailable for widgets on Content Pages. Various other configurations\nlike \\emph{Look and Feel} are only available for widgets on Widget\nPages.\n\n\\section{Page Structure}\\label{page-structure}\n\nPage Structure provides a high level view of every Fragment and every\nfield within each Fragment on the page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-page-structure-editor.png}\n\\caption{\\emph{Page Structure} shows you a hierarchy of your page.}\n\\end{figure}\n\nClicking on a field in Page Structure will highlight it on the page. On\nlarge complicated page, this helps you keep on top of where everything\nis and also access items that might be hard to click on directly.\n\n\\section{Look and Feel}\\label{look-and-feel}\n\nClick the \\emph{Look and Feel} icon\n(\\includegraphics{./images/icon-look-and-feel.png}) to change the theme\nor manage other options for the page. These options are fully documented\nin \\href{/docs/7-2/user/-/knowledge_base/u/creating-pages}{Creating\nPages}.\n\n\\section{Comments}\\label{comments}\n\nYou can also comment on any page fragments. This allows discussion and\ncollaboration for teams creating content pages.\n\nComments are disabled by default, but administrators can enable them\nfrom \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\nSettings} → \\emph{Pages} → \\emph{Content Page Editor}. Select the\n\\emph{Comments Enabled} checkbox and click \\emph{Update}. This enables\ncontent page comments for all instances. To control this on an\ninstance-by-instance basis, navigate to the same setting in\n\\emph{Instance Settings} (instead of System Settings).\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/enable-content-page-comments.png}\n\\caption{Administrators can enable comments for content pages.}\n\\end{figure}\n\nIf comments are enabled, you can access them via the \\emph{Comments}\nicon (\\includegraphics{./images/icon-comments.png}). The comments appear\nfor the selected fragment. You can take the following actions in the\ncomments UI for a fragment:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Add new comments and reply to any existing ones.\n\\item\n  Resolve comments by clicking the checkbox for each. Resolving a\n  comment hides it from view, unless \\emph{Show Resolved Comments} is\n  selected.\n\\item\n  Edit and delete your own comments via the Actions button\n  (\\includegraphics{./images/icon-actions.png}) for each.\n\\end{itemize}\n\nIf you de-select a fragment or enter the comments UI without a fragment\nselected, a list of the fragments on the page appears with the number of\ncomments for each. Selecting a fragment then shows its comments.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-comments.png}\n\\caption{When creating content pages, you and your team can comment on\nany fragments.}\n\\end{figure}\n\n\\section{The Title Bar}\\label{the-title-bar}\n\nThe title bar provides navigation back to the Main Menu, a link to page\nconfiguration, and the ability to search for other pages. The title bar\nis covered in more detail in\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-pages}{Creating Pages}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-edit-title-bar.png}\n\\caption{The title bar has several tools built into it.}\n\\end{figure}\n\nGreat! Now you know how to use the content page interface!\n\n\\chapter{Building Content Pages}\\label{building-content-pages-1}\n\nTo demonstrate Content Page capabilities, create a \\emph{Landing Page}\nfor a space shuttle tour based on this design:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-design-mockup.png}\n\\caption{You have lots of flexibility when arranging Fragments on a\npage.}\n\\end{figure}\n\n\\section{Creating a Content Page}\\label{creating-a-content-page}\n\nTo create the page, follow these general steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Add the Fragments you need to define the basic structure of the page.\n\\item\n  Edit the text for the current page.\n\\item\n  Edit the images.\n\\item\n  Publish the page.\n\\end{enumerate}\n\nYou'll complete this process next.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From Site Administration for your Site, go to \\emph{Site Builder} →\n  \\emph{Pages}.\n\\item\n  Click the (\\includegraphics{./images/icon-add.png}) and select\n  \\emph{Content Page}.\n\\item\n  Set the \\emph{Name} as \\emph{Space Landing Page} and click\n  \\emph{Save}.\n\n  Now you're in Content Page creation. As you work, a draft of the page\n  is automatically saved, but you must click \\emph{Publish} to make it\n  available for use.\n\\item\n  Open \\emph{Sections} → \\emph{Basic Sections} and drag a \\emph{Banner\n  Center} Fragment onto the page.\n\\item\n  Open \\emph{Section Builder} → \\emph{Layouts} and add a 2 Column layout\n  below that.\n\\item\n  Open \\emph{Basic Components}.\n\\item\n  Add a \\emph{Card} to the left column of the layout.\n\\item\n  Add a \\emph{Paragraph} to the right side, and then an \\emph{Image}\n  below that inside the same column.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-creation-step-1.png}\n\\caption{You have lots of flexibility when arranging Fragments on a\npage.}\n\\end{figure}\n\nNow that the structure is defined, start editing the text and images.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click in each text box and edit the text to be relevant to your goal\n  of directing potential customers to space shuttle tours.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/content-page-creation-step-2.png}\n  \\caption{Edit the text and formatting as you see fit.}\n  \\end{figure}\n\\item\n  Click on the main banner image, and then on the\n  \\includegraphics{./images/icon-edit-pencil.png} icon.\n\\item\n  Select one of the many space flight images that are surely filling up\n  your Documents and Media library, upload a new one, or specify a URL.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-creation-step-3.png}\n\\caption{Add some images, and the big picture comes together.}\n\\end{figure}\n\nAnytime during Fragment creation, you can remove, duplicate, or\nconfigure the Fragment.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-page-fragment-options.png}\n\\caption{Add some images, and the big picture comes together.}\n\\end{figure}\n\nConfiguring a Fragment lets you modify the default options for a\nprovided Fragment. This also lets you duplicate a Fragment and configure\nduplicates differently, so you can reuse base Fragments instead of\ndeveloping new ones. When duplicating a Fragment, its configuration and\neditable elements are also copied.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Fragment configuration and duplication are available in\nLiferay DXP 7.2 SP1+ and Liferay Portal GA2+.\n\n\\noindent\\hrulefill\n\nThis is looking good so far, but the one difference between the design\nmockup and the final result is that the background was black for the\noriginal. To finish it up, change the background color for the section\nto black.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on the bottom Section and some additional icons will appear.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/content-page-section-editor.png}\n  \\caption{You can change the background color, image, or edit spacing\n  and padding for a section. You can also remove it.}\n  \\end{figure}\n\\item\n  Click on \\emph{Background Color}\n  \\includegraphics{./images/icon-color.png}, and select Black.\n\\item\n  Finally, publish your page.\n\\end{enumerate}\n\nIn just a few minutes, you used the power of Content Pages and Fragments\nto go from nothing to perfectly recreating a page design. To take it to\nthe next level, head over to the\n\\href{/docs/7-2/user/-/knowledge_base/u/segmentation-and-personalization}{Segmentation\nand Personalization guide}.\n\n\\chapter{Propagation of Changes}\\label{propagation-of-changes}\n\nIf you make an update to a Page Fragment or Content Page Template, it\ndoesn't automatically propagate changes, but you can access the\n\\emph{Usages and Propagation} page to selectively propagate changes.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the Site Administration menu, go to \\emph{Site Builder} →\n  \\emph{Page Fragments}\n\\item\n  Select the Collection containing the changed fragment.\n\\item\n  Open the \\emph{Actions} menu\n  (\\includegraphics{./images/icon-actions.png}) for the Fragment and\n  select \\emph{View Usages}.\n\\end{enumerate}\n\nThe \\emph{Usages and Propagation} page shows a list of every Page, Page\nTemplate, and Display Page that uses the selected Page Fragment. You can\nthen selectively propagate Fragment changes to any or all of the pages\nlisted. You can use the various filters and selection options to apply\nupdates to pages quickly.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-usages-and-propagation.png}\n\\caption{Viewing the Usages and Propagation page.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Beginning in Liferay DXP 7.2 SP1+ and Liferay Portal 7.2\nGA2+, you can propagate changes from global Fragments to their usages on\nchild Sites. See\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-page-fragments\\#creating-and-managing-fragments}{Creating\nand Managing Fragments} for more information on global Fragments.\n\n\\noindent\\hrulefill\n\nTo update a page or template,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select the page or pages you want to update by checking the box next\n  to the page name.\n\\item\n  Click the \\emph{Propagate} icon (\n  \\includegraphics{./images/icon-propagate.png})\n\n  After you propagate changes, visit any affected page to verify there\n  were no unexpected side effects of the changes.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Developers or others testing a Fragment can enable Fragment change\n propagation from the Control Panel. You can learn more about this\n [here](/docs/7-2/frameworks/-/knowledge_base/f/managing-fragments-and-collections#propagating-fragment-changes-automatically).\n It's recommended to only leverage this functionality during testing, as\n automatic propagation on the production environment can cause unintended\n consequences.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nChanges to existing `editable` fields are not propagated since this\noverwrites content currently in content pages. To force propagation to\ncontent in an `editable` field, a developer must change the field ID. Any\ncontent created in that field no longer appears in the Content Page\nwhen the changes are propagated, but it remains in the database and can\nbe retrieved using the old ID.\n\\end{verbatim}\n\nNext you'll learn how to create your own Page Fragments.\n\n\\chapter{Creating Page Fragments}\\label{creating-page-fragments-1}\n\nFragments enable you to create rich content. With all of the included\nFragments and Collections, many projects can be completed using just\nwhat ships with Liferay DXP. Sometimes, though, you must create your own\nFragments. Fragments are built using HTML, CSS, and JavaScript. For a\ndeeper dive into creating a custom Fragment, see\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-fragments}{Developing\nFragments}.\n\nIn this article, you'll learn about the Page Fragments interface.\n\n\\section{Creating and Managing\nFragments}\\label{creating-and-managing-fragments}\n\nTo navigate to the Page Fragments interface,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Go to Site Administration. Make sure the Site where you want to work\n  is selected. If you prefer creating a Fragment that's available for\n  all Sites, navigate to the \\emph{Global} Site and create your Fragment\n  there. Global Fragments are inherited by child Sites, so they can only\n  be edited from the Global Site. Any resources the Global Fragment\n  references (e.g., image) from the Global Site is copied to a Site that\n  leverages the Fragment.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Creating Global Fragments is available in Liferay DXP 7.2\n SP1+ and Liferay Portal 7.2 GA2+. To expose this feature in the initial\n releases of those versions, however, you must create a `.config` file.\n Create the\n `com.liferay.fragment.web.internal.configuration.FragmentGlobalPanelAppConfiguration.config`\n file and add the `enabled=B\"true\"` property. Then copy it to your\n Liferay DXP instance's `osgi/configs` folder.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\tightlist\n\\item\n  Select \\emph{Site Builder} → \\emph{Page Fragments}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/empty-fragments-page.png}\n\\caption{Here is the Page Fragments page with no custom Fragments or\nCollections created.}\n\\end{figure}\n\nFragments are organized in \\emph{Collections}. The main Page Fragments\npage shows available Collections (out-of-the-box Fragment Collections to\nstart), allows Import and Export, and enables you to create Collections.\nYou can also manage the organization and display of Fragments and\nCollections you have created.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You cannot edit a default Fragment. If you'd like to\nprovide an edited version of a default Fragment, you can copy it to your\ncustom Collection and edit it there. To do this, navigate to the default\nFragment Collection and click the Fragment's \\emph{Actions}\n(\\includegraphics{./images/icon-actions.png}) → \\emph{Copy To} button.\nThen select the Collection to copy the default Fragment to. Copying\ndefault Fragments is available in Liferay DXP 7.2 SP1+ and Liferay\nPortal 7.2 GA2+.\n\n\\noindent\\hrulefill\n\nTo create a Fragment, you must first create a Collection.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{New} → \\emph{Collection} to add a Collection.\n\\item\n  Give the Collection a \\emph{Name} and \\emph{Description} and click\n  \\emph{Save}.\n\\end{enumerate}\n\nCollections help you organize Fragments, and can be used to\ndifferentiate between different types of Fragments or Fragments used by\ndifferent groups or departments. Next, create a Fragment inside the\nCollection you created.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on the Collection you created.\n\\item\n  Click the \\emph{Add} icon (\\includegraphics{./images/icon-add.png}) to\n  create a Fragment.\n\\item\n  Choose either \\emph{Section} or \\emph{Component}.\n\\item\n  Give it a \\emph{Name} and click \\emph{Save}.\n\\end{enumerate}\n\nThe Fragment development environment appears. Each pane in the editor\nhas a different function:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  The top left pane is for entering HTML.\n\\item\n  The top right pane is for entering CSS.\n\\item\n  The bottom left pane is for entering JavaScript.\n\\item\n  The bottom right pane provides a live preview as you work in the other\n  panes.\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragments-editor.png}\n\\caption{The Fragments editor provides an environment for creating all\nthe parts of a Fragment.}\n\\end{figure}\n\nIn addition to standard HTML, CSS, and JavaScript, developers can also\nembed widgets and provide fields containing text and images that can be\nedited during the final Content Page publication process. Fragment\ndevelopment is covered in depth in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/creating-fragments}{Developing\nFragments}.\n\nYou can also include resources in your Fragment Collection that your\nFragments can reference. This is helpful when exporting/importing\nFragments: their resources are automatically included. If they're stored\nsomewhere else (e.g., Documents and Media), you must export/import them\nseparately. Click the \\emph{Resources} tab for your Collection and add\nresources (e.g., image, document, etc.) there.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/fragment-resources-tab.png}\n\\caption{The Resources tab can be selected from the Fragment\nCollection.}\n\\end{figure}\n\nOnce added, you can reference resources from your Fragment's code\nwithout worrying about their availability. You can learn more about\ndoing this in\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/including-default-resources-in-fragments}{Including\nDefault Resources in Fragments}.\n\nNext you'll learn how to import and export Page Fragments.\n\n\\chapter{Exporting and Importing\nFragments}\\label{exporting-and-importing-fragments}\n\nOften you'll want to reuse or re-purpose code from Fragments. To do\nthis, export your Fragment Collections.\n\n\\section{Exporting Fragments}\\label{exporting-fragments}\n\nThere are two ways to export Fragments:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Export a Collection or set of Collections.\n\\item\n  Export some Fragments outside of a Collection.\n\\end{enumerate}\n\nTo export a single Collection,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Build} → \\emph{Page\n  Fragments}.\n\\item\n  Next to \\emph{Collections} click \\emph{Actions}\n  (\\includegraphics{./images/icon-actions.png}) and select\n  \\emph{Export}.\n\\item\n  Select a Collection or multiple Collections to be exported. Each\n  collection exports in a separate file.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/collections-export.png}\n  \\caption{Select Collections to export.}\n  \\end{figure}\n\\end{enumerate}\n\nEach Collection \\texttt{.zip} contains all Collection data and\nFragments.\n\nTo export individual Fragments,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on the Collection that you want to export.\n\\item\n  To export all Fragments in the Collection without exporting any\n  Collection data, click \\emph{Actions}\n  (\\includegraphics{./images/icon-actions.png}) \\emph{Export} next to\n  the Collection name. A \\texttt{.zip} file is generated and downloaded\n  automatically.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/fragments-export-individual.png}\n  \\caption{Exporting all of the Fragments in a Collection.}\n  \\end{figure}\n\\item\n  To export a single Fragment, click \\emph{Actions}\n  (\\includegraphics{./images/icon-actions.png}) → \\emph{Export} next to\n  the Fragment. A \\texttt{.zip} file is generated and downloaded\n  automatically.\n\\end{enumerate}\n\nNote that if you export a single Fragment or a group of Fragments\nwithout a collection, they must be imported into an existing Collection.\n\nYou can also export Global Collections and single Fragments from your\nSite.\n\nNow it's time to import your Fragments to where you need them.\n\n\\section{Importing}\\label{importing}\n\nYou can import a Collection that was created in Liferay DXP, a\nCollection created using external tools, or Page Fragments without a\ncollection. When you first import Page Fragments, they aren't available\nfor use until you have approved them for use. This is to ensure that\nthere are no errors in any imported fragments before they are added to a\npage.\n\nSee\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/page-fragments-desktop-tools\\#importing-and-exporting-fragments}{Developing\na Fragment Using Desktop Tools} for more information on creating and\nimporting Fragments using other tools.\n\n\\section{Importing Collections}\\label{importing-collections}\n\nTo import a collection, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Build} → \\emph{Page\n  Fragments}.\n\\item\n  Next to \\emph{Collections}, click \\emph{Actions}\n  (\\includegraphics{./images/icon-actions.png}) and select\n  \\emph{Import}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/collections-import.png}\n  \\caption{Importing and exporting Collections is accessed from a single\n  menu.}\n  \\end{figure}\n\\item\n  On the next screen, click \\emph{Choose File} and select the file you\n  want to import.\n\\item\n  If you want to replace an existing collection, make sure the box is\n  checked for \\emph{Overwrite Existing Files}.\n\\item\n  Click \\emph{Import} and the collection is uploaded.\n\\end{enumerate}\n\n\\section{Importing Individual Page\nFragments}\\label{importing-individual-page-fragments}\n\nYou can also import a single Page Fragment or a set that was exported\noutside of a collection.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the root level of the Fragments page, click on an existing\n  Collection where you want to import the Fragments.\n\\item\n  From inside the Collection click the \\emph{Actions}\n  (\\includegraphics{./images/icon-actions.png}) button in the top right\n  corner of the page.\n\\item\n  Select \\emph{Import}.\n\\item\n  Drag-and-drop or click \\emph{Select} to upload the Fragments\n  \\texttt{.zip}.\n\\item\n  Click \\emph{Import}\n\\end{enumerate}\n\nThe Fragments are imported into the Collection.\n\nExporting and importing fragments is the preferred way to share code or\nbring it into your Site.\n\n\\chapter{Using Widget Pages}\\label{using-widget-pages}\n\nWidget Pages are composed of \\emph{widgets}. A widget is any application\nthat you can add to a page. Widget Pages are constructed by adding\nwidgets to the page and filling them with content.\n\nA widget could be a wiki display or a dynamic publishing tool like the\nAsset Publisher. The content you display with widgets could be long-form\ntext or an image gallery, or anything in between. In this section,\nyou'll learn to create widget pages and build content with them.\n\n\\chapter{Creating Widget Pages}\\label{creating-widget-pages}\n\nWidget Pages are the classic type of page in Liferay DXP. They're simple\nto create and fill with content or functionality. You create a blank\npage, define a layout, and then add widgets to the layout. Widgets can\ndisplay content or provide some tool or function for Users.\n\n\\section{Adding a Widget Page}\\label{adding-a-widget-page}\n\nWhen you first start Liferay DXP you get a widget page by default as\nyour home page. To create a new widget page,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Site Builder} → \\emph{Pages}.\n\\item\n  Click the \\emph{Add} icon ( \\includegraphics{./images/icon-add.png})\n  in the top right and select \\emph{Public Page} to add a new page.\n\\item\n  Select \\emph{Basic Pages} if it is not selected by default.\n\\item\n  Select the \\emph{Widget Page} type.\n\\item\n  Name the page \\emph{Community} and click \\emph{Save}.\n\\item\n  On the next screen, you can select a Layout Template or manage other\n  options. Leave the defaults and click \\emph{Save}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/creating-community-page.png}\n\\caption{Create a page called \\emph{Community} with two columns.}\n\\end{figure}\n\nCreating a page by default also adds it to any Navigation Menus that are\nconfigured to have new pages added to them. If you don't want a new page\nadded to a specific Navigation Menu that is listed during page creation,\nuncheck the box for that menu.\n\nYour new page is now added to the navigation.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the logo in the top left of the page to go back to your Site's\n  front page. The page you just created appears in the main navigation.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/community-page-created.png}\n  \\caption{Your page has been added to the navigation automatically.}\n  \\end{figure}\n\\item\n  Click on \\emph{Community} to go to the page.\n\\end{enumerate}\n\nCurrently the page is empty. Next you'll add some widgets to give it\nfunctionality.\n\n\\section{Adding Widgets to a Page}\\label{adding-widgets-to-a-page}\n\nTo add a widget to a page, go to the page and click the \\emph{Add}\nbutton (\\includegraphics{./images/icon-add-widget.png}) from the top\nmenu and select the \\emph{Widgets} tab. You can either browse through\nthe categories of available widgets until you find the one you want, or\nyou can search for widgets by name. Once you've found a widget, click\nthe \\emph{Add} button to add it to the current page. Once there, you can\ndrag it to a new position. Alternatively, you can drag the widget\ndirectly from the Widgets menu to a specific location on the page.\nFollow the steps below to add some Collaboration apps to the Lunar\nResort Site.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the top menu, select \\emph{Add} → \\emph{Widgets}.\n\\item\n  In the menu that appears, expand the \\emph{Collaboration} category.\n\\item\n  Drag the \\emph{Blogs Aggregator} widget onto the right column of your\n  page.\n\\item\n  Next, drag the \\emph{Wiki} app to the left column.\n\\end{enumerate}\n\nSee how easy it is to add applications to your pages? You've added the\nWiki app and Blogs Aggregator app to a page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/app-layout-design.png}\n\\caption{Your page layout options are virtually limitless with a slew of\napplication and layout combinations.}\n\\end{figure}\n\nIf the default layout options provided aren't enough, you can create\nyour own. For more information about developing custom layout templates,\nsee the tutorial\n\\href{/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator}{Layout\nTemplates with the Liferay Theme Generator}.\n\nNext, you'll look at creating reusable templates for widget pages.\n\n\\chapter{Creating Widget Pages from\nTemplates}\\label{creating-widget-pages-from-templates}\n\nPage templates provide pre-configured pages to reuse. There are two\ntypes of page templates in 7.0: Widget Page templates consist of a\nportlet layout and configuration. Content Page templates are constructed\nfrom Fragments. You can read about\n\\href{/docs/7-2/user/-/knowledge_base/u/building-content-pages}{Content\nPage Templates in this article}.\n\nThree sample layout page templates are installed by default:\n\n\\begin{itemize}\n\\item\n  \\textbf{Search:} Contains a search bar and configuration to display\n  various facets.\n\\item\n  \\textbf{Wiki:} Provides a page with three applications related to\n  authoring a wiki.\n\\item\n  \\textbf{Blog:} Provides a page with three applications related to\n  blogging.\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/default-page-templates.png}\n\\caption{The Blog page template is already available for use along with\nthe Search and Wiki page templates.}\n\\end{figure}\n\nTo add a new widget page template,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Builder} → \\emph{Pages}.\n\\item\n  Select the \\emph{Page Templates} tab.\n\\item\n  Click \\emph{New} and create a collection named \\emph{Lunar Resort\n  Templates}.\n\\item\n  Click the \\emph{Add} icon (\\includegraphics{./images/icon-add.png})\n  and select \\emph{Widget Page Template}.\n\\item\n  Enter a name.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nThe editing page for the template appears. You can add widgets to the\npage or access page configuration now. The changes you make are\ninstantly applied to the template.\n\nIf you want to edit the template again,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go back to the \\emph{Page Templates} tab.\n\\item\n  Click the \\emph{Actions} icon\n  (\\includegraphics{./images/icon-actions.png}).\n\\item\n  Click \\emph{Configure}.\n\\end{enumerate}\n\nNote that after a new page template has been created, the default\npermissions only allow the creator to use the page template. To give\nother users access to it,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Use the Actions menu for the template and select \\emph{Permissions}.\n\\item\n  In the matrix of Roles and permissions, check the \\emph{View}\n  permission for the Roles needed to see the page template in the list\n  of available page templates when creating a new page.\n\\end{enumerate}\n\nIf you want any user who can create a page to be able to use the page\ntemplate, check the \\emph{View} permission for the \\emph{User} Role.\n\nTo use your template to create a new page,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Site Administration and select the \\emph{Pages} option from the\n  \\emph{Site Builder} menu dropdown option.\n\\item\n  Click the \\emph{Add} icon (\\includegraphics{./images/icon-add.png}).\n\\item\n  Inside the Lunar Resort collection, select the page template that you\n  created.\n\\item\n  Enter the name of your page and click \\emph{Add}.\n\\end{enumerate}\n\nPages based on templates can inherit changes from the page template:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/automatic-application-page-template-changes.png}\n\\caption{You can choose whether or not to inherit changes made to the\npage template.}\n\\end{figure}\n\nBy default, when a Site administrator creates pages based on a page\ntemplate, future changes to the template are automatically propagated to\nthose pages. Site administrators can disable this behavior by disabling\nthe \\emph{Inherit Changes} selector. Occasionally, propagation for page\ntemplates fails due to unintended errors. To learn how to manage a\nfailed page template propagation, visit the\n\\href{/docs/7-2/user/-/knowledge_base/u/propagating-changes-from-site-templates-to-sites}{Propagating\nChanges from Site Templates to Sites} article.\n\nIf staging has been enabled, changes to the page template are\nautomatically propagated to the staged page. These changes must still be\napproved before the page is published to live. For this reason, the\nautomatic propagation of page template changes to the staged page cannot\nbe turned off and the \\emph{Inherit Changes} selector does not appear.\n\nYou can read more about staging in the\n\\href{/docs/7-2/user/-/knowledge_base/u/staging}{Stagingl} articles.\n\n\\section{Sharing Widget Page\nTemplates}\\label{sharing-widget-page-templates}\n\nWhen importing pages to a new site or environment, you must also import\ntemplates associated with those pages. Generally templates are included\nautomatically when an associated page is exported, but if not you can\nexport the template collection separately so the page can be imported to\nthe new environment. To export page templates,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Management} → \\emph{Site Builder} → \\emph{Pages}.\n\\item\n  Select the \\emph{Page Templates} tab.\n\\item\n  At the top right of the page, click \\emph{Options}\n  (\\includegraphics{./images/icon-options.png}) → \\emph{Export/Import}.\n\\item\n  On the \\emph{Export/Import} page you can choose to export\n  configuration data and change which Collections and templates are\n  being exported.\n\\item\n  When you're done configuring the export, click \\emph{Export} and save\n  the exported LAR file.\n\\item\n  On the target environment, go to \\emph{Site Management} → \\emph{Site\n  Builder} → \\emph{Pages} and select \\emph{Page Templates}.\n\\item\n  At the top right of the page, click \\emph{Options}\n  (\\includegraphics{./images/icon-options.png}) → \\emph{Export/Import}.\n\\item\n  Select the \\emph{Import} tab.\n\\item\n  Upload the LAR with your template data. If the LAR contains additional\n  content you don't want to import, you can deselect it.\n\\end{enumerate}\n\nOnce the template has been imported, the page can be imported normally\nto your new environment. For more information on exporting/importing\ncontent, visit the\n\\href{/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content}{Importing/Exporting\nSites and Content} article.\n\n\\chapter{Building a Responsive Site}\\label{building-a-responsive-site}\n\nNow more than half of all page views in the world come from mobile\ndevices like phones and tablets. That means that if your pages don't\nlook good on mobile devices, your pages don't look good for more than\nhalf the people looking at them. Liferay DXP can provide the best\nexperience possible no matter what device you're using.\n\nMost of the heavy lifting for mobile optimization comes from your\ndevelopers. Themes must be designed to be responsive. Web Content\nStructures and Templates and Page Fragments must be created with code\nthat transitions gracefully to fit smaller screens on mobile devices.\nBut there's work to do for marketers and administrators as well.\n\n\\chapter{Built-in Mobile Support}\\label{built-in-mobile-support}\n\nOut of the box, there are several features that help make your pages\nlook just as good and have the same functionality on mobile devices as\nthey do on a desktop:\n\n\\begin{itemize}\n\\item\n  Liferay Widgets and custom widgets that use Liferay's UI frameworks\n  automatically scale to fit the screen size.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/widget-adjustment.png}\n  \\caption{A widget adjusts its size.}\n  \\end{figure}\n\\item\n  UI elements like the navigation and Product Menu automatically adjust\n  to remain usable on smaller screens.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/navigation-adjustment.png}\n  \\caption{The main navigation adjusts its size.}\n  \\end{figure}\n\\item\n  When the screen width is low, Liferay combines columns so that all\n  content remains legible.\n\\item\n  For web developers, Liferay's theme tools provide a number of tools to\n  help ensure optimum mobile performance.\n\\end{itemize}\n\nFor most business users, this means that all you need to do to display\npages on Mobile device is to create a page. However, you also have tools\navailable to verify that everything displays as intended. The Device\nSimulator (\\includegraphics{./images/icon-simulation.png}) is a powerful\ntool that shows you how pages look on different devices.\n\n\\section{Using the Device Simulator}\\label{using-the-device-simulator}\n\nWhen creating a page or reviewing a page before it is published, one of\nyour most important tools is the Device Simulator found in the top right\ncorner of every page. The simulator lets you view the current page in a\nnumber of resolutions based on different display types. There are three\npredefined options:\n\n\\textbf{Desktop:} Fixes the width to display the page at full size.\n\n\\textbf{Tablet:} Puts your page in a box as if it is being displayed on\na tablet. It also activates some of Liferay's built-in mobile features.\n\n\\textbf{Mobile:} Puts your page in an even smaller box to demonstrate\nhow the page looks to your average smartphone user.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/device-simulation.png}\n\\caption{The Simulation panel defines multiple screen sizes.}\n\\end{figure}\n\nThere are also two options available to display\n\n\\textbf{AutoSize:} Provides another way to view the default behavior\nwhere the page shrinks and grows based on the width of the browser\nwindow.\n\n\\textbf{Custom:} Lets you enter a specific size for testing and fixes\nthe height and width of the display.\n\nBecause modern mobile browsers are built on the same technology as\ndesktop browsers, the behavior you see in the simulator should match the\nexperience of users on mobile devices. In addition to making sure the\nbasic layout looks good and that all functionality remains, it's also\nimportant to make sure that automatic features---like how columns are\ncombined at lower resolutions---don't have unintended effects.\n\n\\section{Designing Mobile Friendly\nPages}\\label{designing-mobile-friendly-pages}\n\nLiferay DXP provides the tools you need, but building pages that provide\na good experience across all kinds of devices still means working across\nall levels of web development and publishing. Theme developers must\ncreate themes that use Liferay's frameworks to scale content well across\nall kinds of displays. Designers must have multiple screen sizes in mind\nwhen designing pages. And before anything it published it must be\nthoroughly reviewed to make sure that it provides the best experience to\nall of your users.\n\nNow that you've learned about Liferay's tools for making your website\nmobile friendly, let's look at your options for adapting to different\ntypes of mobile devices.\n\n\\chapter{Mobile Device Rules}\\label{mobile-device-rules}\n\nWith mobile device rules, you can alter what gets displayed based on the\ndevice being used to access Liferay DXP. For instance, you can configure\nthe look and feel of pages accessed by mobile device users differently\nfrom those accessed by users on a computer.\n\nWhole Sites or individual pages can be configured for mobile device\nfamilies. A family describes a group of devices. You can set rules that\ndescribe a category of devices, such as all Android devices or all iOS\ndevices. You can define as many rules in a family as you need to\nclassify all the devices for which you'd like to define actions.\nFamilies can be prioritized to determine which one applies to a given\npage request.\n\n\\chapter{Creating Mobile Device\nRules}\\label{creating-mobile-device-rules}\n\nTo configure mobile device rules, you need a way to find out the\ncharacteristics of the device. While some of the characteristics are\nprovided by the device, most are not. For this reason, there are\ndatabases that contain information about thousands of devices. These\ndatabases make it possible to learn device details from the device type.\nLiferay DXP's Mobile Device Rules can connect to device databases so\nthat you can use their device characteristics in your rules.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} For the features described in this article to work,\nyou must install the\n\\href{https://web.liferay.com/marketplace/-/mp/application/92831494}{Liferay\nMobile Device Detection Lite (LMDD)} app from Liferay Marketplace. This\napp provides the device detection database that's required to detect\nwhich mobile devices are accessing it.\n\n\\noindent\\hrulefill\n\nYou can develop plugins that integrate with other device databases. Even\nif you don't have a device database, you can still set up mobile device\nrules. They won't, however, be effective until a database is deployed,\nbecause the portal won't have enough information about the devices being\nused to make page requests.\n\nTo access the Mobile Device Families administrative page,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\emph{Product Menu}.\n\\item\n  Use the \\emph{Site Selector}\n  (\\includegraphics{./images/icon-compass.png}) to choose the Site that\n  you want to define Mobile Device Rules for.\n\\item\n  Select \\emph{Configuration} → \\emph{Mobile Device Families}.\n\\end{enumerate}\n\nYou can also add families for all Sites by navigating to the Control\nPanel → \\emph{Sites} → \\emph{Sites} → \\emph{Global}. The Mobile Device\nFamilies administrative page displays a list of defined families and\nlets you add more. To add rules, you must first add a family.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Add} button (\\includegraphics{./images/icon-add.png}) to\n  add a \\emph{New Device Family}.\n\\item\n  Enter a \\emph{Name} and \\emph{Description}.\n\\item\n  Click \\emph{Save}.\n\\item\n  Click on the name of the Mobile Device Family to access the rules\n  page.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/mobile-device-families.png}\n\\caption{Create a Mobile Device Family so you can create rules.}\n\\end{figure}\n\nThe rules defined for a family, along with the priorities of the\nfamilies selected for a particular Site or page, determine which\nfamily's actions are applied to a given request. From the New\nClassification Rule page for a specific rule set, you can add a rule by\nspecifying an operating system, rule type, physical screen size, and\nscreen resolution. Remember that you can add as many rules to a family\nas you need in order to classify the devices on which you'd like to take\nactions.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}) to add a new rule.\n\\item\n  Enter a \\emph{Name} and \\emph{Description}.\n\\item\n  Select the classifications you want for this rule from \\emph{Operating\n  System and Type}, \\emph{Physical Screen Size}, and \\emph{Screen\n  Resolution}.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nYou'll notice after saving the classification rule that it's\ncharacterized as a \\emph{Simple Rule}. Only Simple Rules are included\nwith Liferay DXP, but the rules are designed to be extensible, and\nadditional rule types can be added by your developers.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/mobile-device-editing-rule.png}\n\\caption{Select the operating system and device type for your rule.}\n\\end{figure}\n\nOnce you've created some mobile device families and added some rules to\nthem, you're ready to create some actions. The actions defined for a\nfamily determine what happens to a request when the device is detected\nand the family has been found to apply.\n\nYou can add families to a Site, individual page, or page set from their\nrespective configuration pages. To do it for a Page Set:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Builder} → \\emph{Pages} in your Site.\n\\item\n  Click on \\emph{Configure} (\\includegraphics{./images/icon-gear.png})\n  for the Public Pages.\n\\item\n  Select the \\emph{Advanced} tab and open the \\emph{Mobile Device Rules}\n  option in the bottom menu.\n\\item\n  Click \\emph{Select} to open the list of families that can be applied.\n\\end{enumerate}\n\nFrom the same page, you can access the configuration for an individual\npage, or you can configure Mobile Device Rules for an entire Site from\n\\emph{Configuration} → \\emph{Site Settings}. You can select multiple\nfamilies for a particular Site or page and order them by priority. The\nfamilies are checked in decreasing order of priority: the actions\ndefined by the first family that applies are executed.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/mobile-device-selection.png}\n\\caption{You can select a mobile device family to apply for a Site or\npage.}\n\\end{figure}\n\n\\chapter{Mobile Device Actions}\\label{mobile-device-actions}\n\nAfter you've created families and applied rules to define those\nfamilies, you can associate specific actions that occur when a user\nvisits that Site on a device.\n\nTo add actions to a selected rule group:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Configure} (\\includegraphics{./images/icon-gear.png}) for\n  the page, page set, or Site where you have configured a device family.\n  The configuration page appears.\n\\item\n  Click the \\emph{Advanced} tab.\n\\item\n  Decline using the same mobile device rules from the public pages by\n  setting the option to \\emph{No}. The device family section appears.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/configure-mobile-device-rule-for-page.png}\n  \\caption{Disable using public pages device rules.}\n  \\end{figure}\n\\item\n  Click \\emph{Select} to open the Device Families page.\n\\item\n  In the \\emph{Mobile Device Rules} section, click \\emph{Actions}\n  (\\includegraphics{./images/icon-actions.png}) → \\emph{Manage Actions}\n  next to the device family that you wish to add an action for.\n\\item\n  Click \\emph{Add Action}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/manage-mobile-actions.png}\n\\caption{Getting to the Manage Actions page.}\n\\end{figure}\n\nBy default, there are four kinds of actions that can be configured for\nmobile families:\n\n\\textbf{Layout Template Modification:} Changes the way portlets are\narranged on pages delivered to mobile devices. For example, you could\nhave pages with more complex layouts automatically switch to a simpler\ntemplate if it detects a mobile device---even if the resolution is\ntheoretically high enough to support the standard layout.\n\n\\textbf{Theme Modification:} Selects a specific theme for different\nmobile device families. You'd have to have a mobile version of your\nSite's theme that is automatically applied when a device hits your page.\n\n\\textbf{URL Redirect:} Sends mobile users to any URL. This can be used\nto direct mobile users to a mobile app download or a mobile version of\nthe page.\n\n\\textbf{Site Redirect:} Sends mobile users to a different Site on your\nportal. In some cases, mobile content could be created on a mirror of\nyour Site.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} 7.0 was designed from the ground up to be responsive and\nadapt to any device that might be accessing it. Before creating new\nthemes or forcing a layout template change, you should test how the Site\nbehaves using Liferay DXP out of the box. Certain features, like URL\nRedirects, can be disruptive and frustrating for users if used\nimproperly.\n\n\\noindent\\hrulefill\n\nLike mobile device rules, mobile device actions are extensible. Your\ndevelopers can define custom actions in addition to the four actions\nprovided by default.\n\nTo review, if you want to configure an action or actions that take place\nwhen mobile device requests are received, take the following steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a mobile device family to represent the group of devices for\n  which to define an action or actions.\n\\item\n  Define one or more rules for your family that describe the group of\n  devices represented by your family.\n\\item\n  Apply your family to an entire page set of a Site (all the public\n  pages of a Site or all the private pages) or to a single page.\n\\item\n  Define one or more actions for your family that describe how requests\n  should be handled.\n\\end{enumerate}\n\n\\section{Mobile Device Rules Example}\\label{mobile-device-rules-example}\n\nNow you'll look at an example of using mobile device rules. Suppose you\nwant to create a rule so that when a Site is accessed by an Android\ndevice, a different layout is used. To set this up, you must follow the\nsame four steps described above.\n\nFirst create the Mobile Device Family:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the \\emph{Mobile Device Families} page of \\emph{Site\n  Administration}.\n\\item\n  Click \\emph{Add Device Family}\n  (\\includegraphics{./images/icon-add.png}).\n\\item\n  Enter \\emph{Android} for the \\emph{Name}.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nNext create a rule for the family:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the \\emph{Mobile Device Families} page, click on \\emph{Android}.\n\\item\n  Click \\emph{Add Classification Rule}\n  (\\includegraphics{./images/icon-add.png}).\n\\item\n  Name the rule \\emph{Rule 1}.\n\\item\n  Under \\emph{Operating System} select \\emph{Android} (you can hold or\n  to select multiple items).\n\\item\n  Under \\emph{Device Type} select \\emph{All},\n\\item\n  Click \\emph{Save}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/example-classification-rule.png}\n  \\caption{Create the Classification rule.}\n  \\end{figure}\n\\end{enumerate}\n\nAs with the previous example, you only need one rule to describe your\ndevice family. Now you must apply the rule to some pages.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Builder} → \\emph{Pages} in Site Administration.\n\\item\n  Click on the \\emph{Configure} icon for the \\emph{Public Pages}\n\\item\n  Go to the \\emph{Advanced} tab.\n\\item\n  Under \\emph{Mobile Device Rules}, select the \\emph{Android} device\n  family.\n\\end{enumerate}\n\nNow you must define an action for your Android rule group to use a\ndifferent layout.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Actions} → \\emph{Manage Actions} for the \\emph{Android}\n  rule.\n\\item\n  Click \\emph{Add Action}.\n\\item\n  Enter the name \\emph{Layout Template Modification}, and select the\n  \\emph{Layout Template Modification} action type.\n\\item\n  Select the \\emph{1 Column} layout template.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/example-mobile-action.png}\n\\caption{Create the Actions for Android.}\n\\end{figure}\n\nNow the Liferay Site's pages are presented to Android users with the 1\nColumn layout template.\n\nMobile Device Rules are a powerful way to manage the way pages and\ncontent appear on the various devices that access your Site. But\nremember to consider the power of modern devices and the experience of\nyour users, and use this great power responsibly---to help users have a\ngreat experience on your website and to not interrupt or negatively\nimpact that experience on whatever device they're using.\n\n\\chapter{Using Full Page\nApplications}\\label{using-full-page-applications}\n\nFull Page Applications are the ideal way to display a Message Board,\nWiki, or other application that demands a full page.\n\n\\section{Configuring the Page}\\label{configuring-the-page}\n\nCreating a Full Page Application starts just like creating any other\ntype of page.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Site Builder} → \\emph{Pages}.\n\\item\n  Click the (\\includegraphics{./images/icon-add.png}) icon.\n\\item\n  Select the \\emph{Full Page Application} page type.\n\\item\n  Give your page a \\emph{Name} and click \\emph{Save}.\n\\item\n  Set the \\emph{Full Page Application} to \\emph{Wiki} and click\n  \\emph{Save}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/full-page-app-configure.png}\n  \\caption{The Full Page Application configuration page.}\n  \\end{figure}\n\\item\n  Finish the page configuration and click \\emph{Save}.\n\n  Out of the box, you can set the Blogs, Wiki, Media Gallery, Message\n  Boards, RSS, Hello Soy Portlet, Documents and Media, Form, or\n  Application Authorization Request to be the sole application for the\n  page. Developers can make their applications Full Page Applications.\n\\item\n  Click \\emph{Go to Site} in the Site Administration menu, and then\n  click on your page.\n\\end{enumerate}\n\nNow the page is configured to display the Wiki and only the Wiki. No\nother widgets can be added to the page, and the Wiki app cannot be\nremoved.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/single-page-app-wiki.png}\n\\caption{The Wiki displayed as a Full Page Application.}\n\\end{figure}\n\nNote that all of the applications that can be added to the page are\nnon-instanceable and the content of whichever application you select is\nbased on the instance for that site. So if you already had data in your\nWiki it appears on this page.\n\nIf you want to configure the application to be scoped to this specific\npage, you can configure that through the application's settings.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the page, click the (\\includegraphics{./images/icon-options.png})\n  button for the Wiki and select \\emph{Configuration}.\n\\item\n  From the \\emph{Wiki - Configuration} page, select the \\emph{Scope}\n  tab.\n\\item\n  Open the \\emph{Scope} menu and select \\emph{Space Wiki}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/configuring-scope.png}\n\\caption{Configuring the scope.}\n\\end{figure}\n\nNow the Wiki is scoped for this page, and doesn't share data with the\nSite or globally scoped Wiki.\n\n\\chapter{Managing Site Navigation}\\label{managing-site-navigation}\n\nLiferay provides powerful tools for creating and organizing pages. You\ncan have anything from a simple, flat Site navigation to a complex\nhierarchy with tree of sub-pages nested down many levels.\n\n7.0 lets you create Navigation Menus separate from your page hierarchy.\nNow you have the freedom to leave one-off marketing landing pages out of\nthe navigation or create multiple navigation menus: a main menu,\nsecondary menus, footer menus, and custom menus for anything that you\ncan dream up.\n\nMenus can differ by page: landing pages can show a simple list of\nfrequently visited pages, and the rest can appear in secondary\nnavigation. You can also create specific menus for different landing\npages to direct users to content that is relevant to them.\n\nGo to \\emph{Site Builder} → \\emph{Pages} to view the existing pages or\ncreate new pages. The Site hierarchy as displayed on \\emph{Pages} is the\nmain reference for the organization of pages on that Site. While\nNavigation Menus can customize their organization and what appears and\nwhat doesn't appear, this menu is always the primary reference for the\npages on your Site.\n\n\\chapter{Page Hierarchy}\\label{page-hierarchy}\n\nUsing the Page Hierarchy, you create public and private pages and\norganize those pages in whatever order or structure that you see fit.\n\n\\section{Creating a Page}\\label{creating-a-page}\n\nNew pages are created on the \\emph{Site Builder} → \\emph{Pages} page in\nSite Administration. Pages can be created as \\emph{Public Pages} which\nanyone can view, or \\emph{Private Pages} which can only be viewed by\nSite Members. To create a new page,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} for the Site you want to work on,\n  then \\emph{Site Builder} → \\emph{Pages}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/default-nav-pages.png}\n  \\caption{In the default site, initially only the \\emph{Home} and the\n  hidden \\emph{Search} pages exist in the Public Pages Hierarchy.}\n  \\end{figure}\n\\item\n  Click \\emph{Add} (\\includegraphics{./images/icon-add.png}) and select\n  \\emph{Public Page}.\n\\item\n  Select \\emph{Widget Page}.\n\\item\n  Set the \\emph{Name} as \\emph{About Us}.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/page-hierarchy-menu.png}\n\\caption{When you create a page, by default it is added to the site\nhierarchy.}\n\\end{figure}\n\nNow that the page is created, it appears in the hierarchy, and you can\nmove or organize its position there.\n\n\\section{Organizing Pages}\\label{organizing-pages}\n\nDrag and drop pages to reorder their position in the page hierarchy (and\nsubsequently the default navigation that users see), and to nest them as\nsubpages. The page at the top of the list is the \\emph{Home} page that\nusers see automatically when visiting your Site.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Go to Site} to view the organization of the navigation\n  menu. You can see the order of the pages matches the order of the\n  pages from Site Administration.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/navigation-practical1.png}\n  \\caption{You can see the order of pages in Site Administration vs.~how\n  they appear on the site.}\n  \\end{figure}\n\\item\n  Drag the \\emph{About Us} page above the \\emph{Welcome} page in the\n  list. It automatically becomes the \\emph{Home} page.\n\\item\n  Click \\emph{Go to Site} to see how this affects your menu.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/navigation-practical2.png}\n  \\caption{\\emph{About Us} is now the home page, and \\emph{Welcome} is\n  second in the nav.}\n  \\end{figure}\n\\item\n  Drag \\emph{About Us} on top of \\emph{Welcome} to nest it.\n\\item\n  Click \\emph{Go to Site} one more time to see how nested pages appear.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/navigation-practical3.png}\n\\caption{\\emph{About Us} is now nested under \\emph{Welcome} and appear\nwhen you mouse-over \\emph{Welcome}.}\n\\end{figure}\n\nAs you've just demonstrated, organizing pages in the default menu is\nsimple, but very powerful.\n\n\\section{Public and Private Pages}\\label{public-and-private-pages}\n\nAs noted above, Private Pages work just like Public Pages, except they\ncan be viewed only by registered members of a Site. In the default\nconfiguration, Public Pages are at the URL\n\\texttt{{[}web-address{]}/}\\textbf{web}\\texttt{/{[}site-name{]}} while\nPrivate Pages are at\n\\texttt{{[}web-address{]}/}\\textbf{group}\\texttt{/{[}site-name{]}}.\nOther than the membership distinctions, Public and Private Pages share\nthe same behavior.\n\n\\section{Page Options}\\label{page-options}\n\nWhile managing the default menu, you can also access page options.\nClicking on the \\emph{Options} icon\n\\href{./images/icon-options.png}{Option} accesses several configuration\ntools:\n\n\\textbf{View} goes to the selected page on the Site.\n\n\\textbf{Configure} goes to page configuration.\n\n\\textbf{Copy Page} creates a new page in the current Site that\nduplicates the selected page.\n\n\\textbf{Permissions} opens the Permissions dialog.\n\n\\textbf{Orphan Widgets} clears data related to widgets that have been\nremoved from the page.\n\n\\textbf{Delete} deletes the page and all its data.\n\nCreating and managing pages using the page hierarchy is simple but very\npowerful. If you need more navigation options, however, Navigation Menus\nprovide more flexibility.\n\n\\chapter{Creating and Managing Navigation\nMenus}\\label{creating-and-managing-navigation-menus}\n\nTo better understand Navigation Menus, it's time to create a new menu.\n\n\\section{Creating a Navigation Menu}\\label{creating-a-navigation-menu}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Site Builder} →\n  \\emph{Navigation Menus}.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  to add a new menu.\n\\item\n  Give your menu a name and click \\emph{Save}.\n\\end{enumerate}\n\nOn the next page appears a number of elements that you can add to a\nmenu.\n\n\\textbf{Page}: Select an existing page from the current Site to add to\nthe navigation menu.\n\n\\textbf{Submenu}: Create a second level of menu navigation.\n\n\\textbf{URL}: Create a link to any page anywhere by providing a URL. The\nlink appears just like any other option in your menu.\n\nClick on \\emph{Page} and you see a view of all the current pages on the\ncurrent Site. Select a page and click \\emph{Add} to add that page to the\nmenu.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When you click on a page, you select that page. Multiple\npages can be selected by clicking on each page one at a time. To\ndeselect a page, click on the page again.\n\n\\noindent\\hrulefill\n\nNow you see the menu management screen. From here, you can drag and drop\nmenu elements to rearrange or nest them. You can also manage options for\nthis menu by clicking the gear icon in the top right. Let's add another\nitem to the menu.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\texttt{+} icon.\n\\item\n  Select \\emph{Submenu} in the menu that pops up.\n\\item\n  Name your menu \\emph{External Links}.\n\\item\n  Click \\emph{Add}.\n\\end{enumerate}\n\nClick the \\texttt{+} button again and select \\emph{URL}. You're prompted\nto enter a page name and URL.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Enter a \\emph{Name}.\n\\item\n  Enter the \\emph{URL} for an external Site.\n\\item\n  Click \\emph{Add}.\n\\end{enumerate}\n\nDrag the URL item onto the \\emph{External Links} submenu. This nests the\nURL item in the submenu.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/basic-nav-menu.png}\n\\caption{Menus can have a standard page, a submenu, and a URL link in\nthe submenu.}\n\\end{figure}\n\nNow that you can see how menus work, you can learn the details.\n\n\\section{Managing Menus}\\label{managing-menus}\n\nAfter you create a menu, more configuration options appear on the main\nNavigation Menus page.\n\n\\textbf{Title}: Your menu's name.\n\n\\textbf{Add New Pages}: Determines if new pages added to the main\nnavigation are added to the menu automatically.\n\n\\textbf{Marked As}: Can be set as \\emph{Primary Navigation},\n\\emph{Secondary Navigation}, or \\emph{Social Navigation}.\n\n\\textbf{Author}: The user that created the menu.\n\n\\textbf{Create Date}: When the menu was created.\n\nTheme and Fragment developers primarily use the menu types to determine\nhow a menu should be styled. \\textbf{Primary Navigation} is the main\nnavigation for a page. \\textbf{Secondary Navigation} is a second level\nof navigation, possibly a sidebar or a separate menu within a page.\n\\textbf{Social Navigation} is for menus that contain links for sharing\ncontent on social media or similar tasks.\n\n\\section{Modifying Menus}\\label{modifying-menus}\n\nNext click on the options menu at the far right of your new navigation\nmenu:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/nav-menu-options.png}\n\\caption{Menus with a standard page, a submenu, and a URL link in the\nsubmenu are created for different reasons.}\n\\end{figure}\n\n\\textbf{Edit}: Add, remove, or organize menu items.\n\n\\textbf{Rename}: Change the name of your menu.\n\n\\textbf{Permissions}: Define who can view, update, delete, and manage\nthe permissions for the menu.\n\n\\textbf{Mark As}: Change the menu type.\n\n\\textbf{Delete}: Deletes the menu.\n\nNext you'll learn about the tools for displaying menus on pages.\n\n\\chapter{Displaying Navigation Menus}\\label{displaying-navigation-menus}\n\nYou can display Navigation Menus in different ways on your Site. You may\nwant to configure different display styles for a main menu, sidebar, and\nfooter menu all on one page.\n\n\\section{Navigation Menu Widget}\\label{navigation-menu-widget}\n\nThe Navigation Menu widget lets you add navigation wherever you need it.\nYou can place the widget on a page and then select a menu and style for\nthe menu you are displaying.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to a Widget Page, open the Add menu on the right side of the page\n  and add the \\emph{Content Management} → \\emph{Navigation Menu} to the\n  page.\n\\item\n  Open the \\emph{Configuration} menu.\n\\end{enumerate}\n\nFrom here you can configure three main categories:\n\n\\begin{itemize}\n\\item\n  The Navigation Menu to be displayed\n\\item\n  The styling of the menu\n\\item\n  What level of navigation to display\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/nav-widget-configuration.png}\n\\caption{Configuring the Navigation Menu Widget.}\n\\end{figure}\n\n\\section{Choosing a Navigation Menu}\\label{choosing-a-navigation-menu}\n\nThe Navigation Menu Widget has two ways to select a menu. You can choose\nto \\emph{Select Navigation} or \\emph{Choose Menu}.\n\n\\textbf{Select Navigation:} Select from the three main menu types:\n\\emph{Primary Navigation}, \\emph{Secondary Navigation}, and \\emph{Social\nNavigation}.\n\n\\textbf{Choose Menu:} Choose any menu that was created for that Site.\n\nOnce you select a menu, you must choose how to display it.\n\n\\section{Display Template}\\label{display-template}\n\nThe \\emph{Display Template} option lets you select an\n\\href{/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates}{Widget\nTemplate} for Navigation Menus. There ten included by default:\n\n\\textbf{List Menu:} Displays all the items in a vertical list.\n\n\\textbf{Pills Horizontal:} Displays the items horizontally and uses a\nbutton style for highlighting.\n\n\\textbf{Pills Justified:} Like Pills Horizontal, but pads the items to\nfill out the horizontal space.\n\n\\textbf{Pills Stacked:} A vertical version of the pills style.\n\n\\textbf{Tabs:} Displays the items like navigation tabs.\n\n\\textbf{Tabs Justified:} Navigation tabs that fill horizontal space.\n\n\\textbf{Bar Minimally Styled:} A lightweight version of the default\ndisplay that you see in the embedded menu on your page.\n\n\\textbf{Bar Minimally Justified Styled:} Like Bar Minimally Styled with\nhorizontal padding.\n\n\\textbf{Bar Default Styled:} The default embedded menu.\n\n\\textbf{Split Button Dropdowns:} Displays each item as a button with a\ndropdown for multiple navigation levels.\n\nYou can also add your own custom templates.\n\n\\section{Menu Items to Show}\\label{menu-items-to-show}\n\n\\emph{Menu Items to Show} configures which pages at what level from the\nmenu are displayed in the widget. You can choose the starting level, how\nmany levels deep to display, and how to display sub-levels.\n\n\\textbf{Start with Menu Items In:} Choose to start at a specific level\nof the navigation or a level relative to the current level (above or\nbelow).\n\n\\textbf{Sublevels to Display:} Select the number of levels to display in\nthe navigation, from \\textbf{1} down to \\textbf{Infinite}.\n\n\\textbf{Expand Sublevels:} Choose if hovering your mouse over the\nnavigation reveals navigation levels one at a time automatically or\nreveal all the levels at once.\n\nNow you can see how there are a variety of customizations and\nconfigurations available for navigation menus that you can implement for\nyour Site.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/navigation-menu-examples.png}\n\\caption{Navigation menus give you many ways to help users navigate your\nSite.}\n\\end{figure}\n\n\\chapter{Building Sites from\nTemplates}\\label{building-sites-from-templates}\n\nSite Templates create a single Site structure that can be used for any\nnew Site. They are created and administered from the Control Panel. In\naddition to creating multiple Sites with the same design, you can also\nuse them to manage changes across multiple Sites with propagation of\nchanges. Site templates can contain multiple pages, each with its own\ntheme, layout template, applications, and app configurations.\n\nSite templates can also contain content just like actual Sites. This\nallows administrators to use Site Templates to create new Sites that are\neach created with the same default pages, applications, and content.\nAfter they've been created, these Sites and their pages can be modified\nby Site administrators. Using Site templates can save Site\nadministrators a lot of work even if each Site that was created from a\ngiven Site Template ends up being very different.\n\n\\chapter{Creating a Site Template}\\label{creating-a-site-template}\n\nSuppose you need to create the following three private Sites for the\nLunar Resort's internal use: Engineering, Marketing, and Legal. These\nshould be accessible only to members of these respective departments.\nYou could design each Site separately, but you can save yourself time if\nyou create a Site template instead.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the Control Panel and click \\emph{Sites} → \\emph{Site\n  Templates}.\n\\item\n  Click the \\emph{Add} icon (\\includegraphics{./images/icon-add.png})\n  and enter the name \\emph{Department} for your template.\n\\item\n  Leave the \\emph{Active} and \\emph{Allow Site administrators to modify\n  pages associated with this Site template\\ldots{}} boxes checked.\n\\item\n  Click \\emph{Save} to create your Site template.\n\\end{enumerate}\n\nThe \\emph{Active} box must be checked for your template to be usable. If\nyour template is still a work in progress, uncheck it to ensure that no\none uses it until it's ready. Checking \\emph{Allow Site administrators\nto modify pages associated with this Site template\\ldots{}} allows Site\nadministrators to modify or remove the pages and apps that the template\nintroduces to their Sites---if you want the templates to be completely\nstatic, you should uncheck this.\n\nNow it's time to edit your Site template. This example, includes four\npages.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} This section assumes knowledge of Liferay DXP 7.2 page\nmanagement. For more information on how to create and manage pages in\nLiferay DXP 7.2, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-and-managing-pages}{Adding\nPages to Sites article}.\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Options} icon\n  (\\includegraphics{./images/icon-options.png}) and select\n  \\emph{Manage}.\n\n  This brings you to the \\emph{Pages} page for the Site Template. You\n  already have a home page. Create three more pages.\n\\item\n  Create a \\emph{Full Page Application} page named \\emph{Documents}.\n\\item\n  Click \\emph{Options} (\\includegraphics{./images/icon-actions.png}) →\n  \\emph{Configure} and set the \\emph{Full Page Application} to\n  \\emph{Documents and Media}.\n\\item\n  Create a page using the Global Page Template \\emph{Wiki} and name it\n  \\emph{Wiki}.\n\\item\n  Create a widget page named \\emph{Message Boards}.\n\\item\n  Click \\emph{Go to Site} in the menu to the left to go to the pages you\n  just created.\n\\item\n  On the \\emph{Home} page add the Activities, Announcements, and\n  Calendar apps.\n\\item\n  On the \\emph{Message Boards} page add the Message Boards and Tag Cloud\n  apps.\n\\end{enumerate}\n\nThe changes you made to your Site template above are completed in real\ntime, so there's no \\emph{Save} button.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/editing-site-template.png}\n\\caption{You can see the name of the Site template you're currently\nediting.}\n\\end{figure}\n\nNext, you'll use your Site template to create the Engineering, Marketing\nand Legal Sites.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the Control Panel and click on \\emph{Sites} → \\emph{Sites}.\n\\item\n  Click the \\emph{Add} icon (\\includegraphics{./images/icon-add.png}) →\n  \\emph{Department}.\n\\item\n  Enter \\emph{Engineering} for the Site name.\n\\item\n  Check the \\emph{Create default pages as private (available only to\n  members). If unchecked, they will be public (available to anyone)}\n  option since the Engineering Site is intended for internal use only.\n\\item\n  Click \\emph{Save}.\n\\item\n  In the next section, set the Membership Type to \\emph{Private}.\n\n  Recall that private Sites don't appear in the My Sites application so\n  that regular users won't even know that the Engineering Site exists.\n  Also, the only way users can be added to a private Site is via an\n  invitation from a Site administrator.\n\\item\n  Leave the \\emph{Active} selector enabled so that your Site can be used\n  immediately.\n\\item\n  Check the \\emph{Create default pages as private (available only to\n  members). If unchecked, they will be public (available to anyone).}\n  option since the Engineering Site is intended for internal use only.\n\\item\n  Leave the \\emph{Enable propagation of changes from the Site template}\n  box enabled so that the Engineering Site receives updates if the\n  Department Site template is modified.\n\\item\n  Click \\emph{Save} to create your Engineering Site.\n\\item\n  Repeat these steps to create the Marketing and Legal Sites.\n\\end{enumerate}\n\nThe new Sites have all the pages and apps you created in the Site\ntemplate. To view the pages of the new Sites, click \\emph{Sites} →\n\\emph{Sites} in the Control Panel and then click \\emph{Actions} →\n\\emph{Go to Private Pages} next to one of your new Sites.\n\nUsing Site templates streamlines the Site creation process for\nadministrators, making it easy to create Sites quickly. Now each\ndepartment of the Lunar Resort has its own Calendar, Documents and Media\nLibrary, Wiki, and Message Boards on their Sites. Although the pages and\napps of each department's Site are the same, each Site will quickly be\nfilled with department-specific information as users add and share\ncontent within the Sites. Also, Site administrators can add new pages,\napps, and content to their Sites, further differentiating each\ndepartment's Site from the others.\n\n\\chapter{Managing Site Templates}\\label{managing-site-templates}\n\nTo get started, click on \\emph{Site Templates} in the Sites section of\nthe Control Panel. Here, you can add, manage, or delete Site templates.\nYou can also configure the permissions of Site templates. As long as a\nSite is linked to the Site template it was created from, changes to the\nSite template's pages, apps, and app configurations are propagated to\nthe Site. Changes to a Site template's content, however, are not\npropagated to existing Sites that are linked to the Site template.\nYou'll learn about the propagation of changes between Site templates and\nSites in more detail in the section on Site template use cases below.\n\nTo manage a Site Template's pages,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on \\emph{Site Templates} in the Control Panel.\n\\item\n  Select the \\emph{Actions} icon\n  (\\includegraphics{./images/icon-actions.png}) and then \\emph{Manage}\n  for an existing template.\n\\end{enumerate}\n\nIf you open the main Menu on the left side of your screen (if\nnecessary), the Site Template is selected in the Site Administration\ndropdown menu. You're provided similar options as a regular Site,\nincluding \\emph{Build}, \\emph{Content}, \\emph{Configuration}, and\n\\emph{Publishing}. By default, the Manage Interface opens \\emph{Build} →\n\\emph{Pages}. From here, you can add or remove pages from a Site\nTemplate or select themes and layout templates to apply to the Site\nTemplate. You can also configure each page to have any theme, any layout\ntemplate, and any number of applications, just like a page of a regular\nSite. As with Site pages, you can organize a Site Template's pages into\nhierarchies. When you create a Site using a Site template, the\nconfiguration of pages and apps is copied from the template to the Site.\nBy default, all changes made to the Site template are automatically\ncopied to Sites based on that template.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} If you want to publish a piece of web content to many\nSites and ensure modifications are applied to all, don't use Site\ntemplate content for that purpose. Instead, place the content in the\nglobal scope and then reference it from a \\emph{Web Content Display}\napplication in each Site.\n\n\\noindent\\hrulefill\n\nThe Content section offers separate repositories for content related\napps based on your Site Template. For instance, by clicking \\emph{Polls}\nfrom the Content section, you can create a poll question that is only\navailable for that specific Site template. Assets created within your\ntemplate's Content section can only be accessed by Sites using the\ntemplate.\n\nThe Configuration section includes Widget Templates and Mobile Device\nconfiguration options for your Site Template. Also, nested in the\nConfiguration section is the \\emph{Site Template Settings}. This edits\nthe template's name and description while also offering boolean options\nfor activating your Site template and allowing Site administrators to\nmodify pages associated with your template.\n\nThe following figure displays the form shown when editing the\n\\emph{Department} template's settings:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/site-template-settings.png}\n\\caption{Site templates have several configurable options including the\noption to allow Site administrators to modify pages associated with the\nSite template.}\n\\end{figure}\n\nBy default, the following Site templates are provided:\n\n\\begin{itemize}\n\\item\n  \\textbf{Intranet Site:} Provides a preconfigured Site for an intranet.\n  The Home page displays the activities of the members of the Site,\n  search, a language selector, and a list of the recent content created\n  in the intranet. It also provides two additional pages for Documents\n  and Media and external News obtained through public feeds.\n\\item\n  \\textbf{Community Site:} Provides a preconfigured Site for building\n  online communities. The Home page of a \\emph{community Site} provides\n  message boards, search, a display of a poll and statistics of the\n  activity of community members. The Site will also be created with a\n  page for a wiki.\n\\end{itemize}\n\nNow that you know the basics for creating and managing your Site\ntemplates, you can learn about propagating changes next.\n\n\\chapter{Propagating Changes from Site Templates to\nSites}\\label{propagating-changes-from-site-templates-to-sites}\n\nSite Template administrators can add, update, or delete Site Template\npages. Changes made to a Site Template can be propagated to Sites whose\npage sets are linked to the Site Template. When you create a Site based\non a Site Template with the \\emph{Enable propagation of changes from the\nSite template} box checked this link is created. To configure\npropagation of changes:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select the Site from the Sites dropdown in the Menu by selecting the\n  \\emph{Site Selector} button\n  (\\includegraphics{./images/icon-compass.png}).\n\\item\n  Navigate to the \\emph{Configuration} → \\emph{Settings} page and\n  uncheck or recheck the \\emph{Enable propagation of changes from the\n  Site template} checkbox.\n\\end{enumerate}\n\nIn this section, you'll learn about the propagation of changes from Site\ntemplates to Sites and discuss the options available to Site\nadministrators and Site template administrators.\n\n\\section{Site Template Page Behavior}\\label{site-template-page-behavior}\n\nIf a Site's page set has been created from a Site template and the\npropagation of changes from the Site template is enabled, Site\nadministrators can add new pages but cannot remove or reorder the pages\nimported from the Site Template. If a Site has both pages imported from\na Site template and custom Site pages, the Site Template pages always\nappear first in the Site page hierarchy; custom pages added by Site\nadministrators appear after the Site template pages. Only Site template\nadministrators can remove, reorder, or add Site template pages. Site\nadministrators can add or remove custom Site pages. They can also\nreorder custom Site pages as long as they're all positioned after the\nSite template pages. Site template administrators cannot add, remove, or\nreorder custom Site pages.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Pages containing a fragment (e.g.,\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-pages}{Content\nPages}) cannot propagate changes after a Site is \\emph{first} created\nbased on a site template.\n\n\\noindent\\hrulefill\n\nIf a Site administrator changes a page that was imported from a Site\nTemplate and refreshes the page, the following Information icon\n(\\includegraphics{./images/icon-control-menu-information.png}) appears\nin the Control Menu with the following message:\n\n\\begin{verbatim}\nThis page has been changed since the last update from the Site template. No\nfurther updates from the Site template will be applied.\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/site-template-update-message.png}\n\\caption{You can click the Information icon to view important\ninformation about your Site template.}\n\\end{figure}\n\n\\section{Merging and Resetting\nChanges}\\label{merging-and-resetting-changes}\n\nIf the Site administrator clicks the \\emph{Reset Changes} button,\nchanges are propagated from the Site template page to the corresponding\nSite page that was imported from the Site template. Clicking the\n\\emph{Reset Changes} button makes two kinds of updates to a page. First,\nchanges made by Site administrators to the Site page are undone. Second,\nchanges made by Site template administrators to the Site template page\nare applied to the Site page. Note: clicking the \\emph{Reset Changes}\nbutton only resets one page. If multiple Site pages have been modified\nand you'd like to re-apply the Site template pages to them, you'll need\nto click the \\emph{Reset Changes} button for each page.\n\nSite template administrators can set preferences for apps on Site\ntemplate pages. When a Liferay administrator creates a Site from a Site\ntemplate, the app preferences are copied from the Site template's apps,\noverriding any default app preferences. When merging Site template and\nSite changes (e.g., when resetting), app preferences are copied from\nSite template apps to Site apps. Only global app preferences or local\napp preferences which don't refer to IDs are overwritten.\n\nIn some cases, merging Site template and Site changes fails. For\nexample, if pages from a Site template cannot be propagated because\ntheir friendly URLs are in conflict, Liferay DXP could try to\ncontinuously merge the Site changes. Instead of entering into an\ninfinite loop of merge fails, Liferay DXP stops the merge after several\nunsuccessful attempts. Liferay DXP, however, doesn't stop there: your\nmerge is temporarily paused, you're given an indication of the current\nmerge fail, and then you have the opportunity to fix your merge\nconflicts. After you've squared away your conflict, navigate to your\nSite's \\emph{Site Administration} → \\emph{Configuration} → \\emph{Site\nSettings} and click the \\emph{Reset and Propagate} button.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/friendly-url-propagation-failure.png}\n\\caption{This type of warning is given when there are friendly URL\nconflicts with Site template pages.}\n\\end{figure}\n\nThe \\emph{Reset and Propagate} button resets the merge fail count and\nattempts to propagate your Site changes again. This process gives you\nthe opportunity to detect and fix a merge fail when problems arise. This\nhelpful process can also be done with page template merges, which\nfollows similar steps.\n\nSite administrators can also add data to Site template applications. For\nexample, Site template administrators can add the Wiki app to a Site\ntemplate page and use the Wiki to create lots of articles. When a\nLiferay administrator creates a Site from a Site template, data is\ncopied from the Site template's apps to the Site's apps. The preferences\nof the Site's apps are updated with the IDs of the copied data. For\nexample, if a Site is created from a Site template that has a Wiki app\nwith lots of wiki articles, the wiki articles are copied from the Site\ntemplate's scope to the Site's scope and the Site's Wiki app is updated\nwith the IDs of the copied wiki articles.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} App data, fragment-based pages, related resources,\nand permissions on resources are only copied from a Site template to a\nSite when that Site is \\emph{first} created based on the template. No\nchanges made to these entities are propagated to the Site after the Site\nis created. Neither are such changes propagated to a Site by the\n\\emph{Reset} or \\emph{Reset and Propagate} features.\n\n\\noindent\\hrulefill\n\nFor example, consider a Site template administrator who includes a\nMessage Boards app as part of a Site template. They even create Message\nBoard categories and configures permissions over the actions of the\ncategories. The first time a Site is created based on the Site template,\nthe categories (app data) and related permissions are copied to the\nSite. If the Site template administrator adds, removes, or deletes some\ncategories, however, such changes \\emph{aren't} propagated to the Site.\n\nNow that you've learned how Site templates work, you'll learn how to\nshare Site templates.\n\n\\chapter{Sharing Site Templates}\\label{sharing-site-templates}\n\nIf you want to export a Site that uses Site or Page Templates to a\ndifferent environment (through a LAR file or remote publication), the\ntemplates must be exported and imported manually in advance or the\nimport fails.\n\nTo export a Site using a Site Template, use the following process:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\emph{Control Panel} → \\emph{Sites} → \\emph{Site Templates}\n  menu.\n\\item\n  Click the \\emph{Actions} icon\n  (\\includegraphics{./images/icon-actions.png}) and then \\emph{Manage}\n  for the Site template you want to export.\n\\item\n  Open the Site Template's management section and click on\n  \\emph{Publishing} → \\emph{Export}.\n\\item\n  Click the \\emph{Add} icon (\\includegraphics{./images/icon-add.png}) to\n  create a new Custom Export.\n\\item\n  Select the content and pages you want to export and click\n  \\emph{Export}.\n\\item\n  Click on the \\emph{Download} icon for the template that you exported.\n\\item\n  In your target environment, go to \\emph{Control Panel} → \\emph{Sites}\n  → \\emph{Site Templates} and create a new Site template.\n\\item\n  Click \\emph{Actions} → \\emph{Import} for that Site template and upload\n  the LAR file containing your Site template's content.\n\\end{enumerate}\n\nNow the template can be used normally in the new environment. For more\ninformation on exporting/importing content, visit the\n\\href{/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content}{Importing/Exporting\nPages and Content} article.\n\n\\chapter{Configuring Sites}\\label{configuring-sites}\n\nJust like there's more than one way to cook and an egg --- or eat an egg\nfor that matter --- there's more than one way to build a site. Liferay\nis created with that in mind, and beyond just creating content and\npages, Liferay has a wealth of configuration options and tools available\nto help you create the site that meets your needs and the needs of your\nusers.\n\nIn this section, you'll explore all of Liferay's options for configuring\nsites, and gain a deeper understanding of why you might want to use\nvarious options and configurations.\n\n\\chapter{Configuring Site Settings}\\label{configuring-site-settings}\n\nGeneral settings range from core configuration, like your Site's\nMembership Type, to finer details like Documents and Media indexing\noptions.\n\n\\section{Details}\\label{details}\n\n\\emph{Details} provides the same menu you filled out when first creating\nyour Site. This allows an administrator to change the description and\nmembership type of a Site.\n\n\\section{Membership Options}\\label{membership-options}\n\nThe membership type can be set as open, restricted, or private based on\nthe privacy needs of the Site. Users can join and leave an open Site at\nwill. To join a restricted Site, users must be added by the Site\nadministrator, but they can request membership through the Sites section\nof the Control Panel. A private Site works like a restricted Site but is\nhidden from users who aren't members.\n\n\\section{Site Hierarchies}\\label{site-hierarchies-1}\n\nSites can be organized into hierarchies. At the bottom of the Details\nsub-section is the Parent Site section. When you select the parent Site\nfor the Site you're currently on, a checkbox appears for limiting\nmembership to members of the parent Site.\n\n\\section{Pages}\\label{pages}\n\nUnder Pages you can view your Site's Public or Private Pates, if any\nexist. If they don't exist, a \\emph{Site Templates} selector appears for\ncreating pages with a Site Template.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/selecting-site-template.png}\n\\caption{Selecting a Site Template.}\n\\end{figure}\n\n\\section{Categorization}\\label{categorization}\n\n\\emph{Categorization} helps administrators organize the Site and allows\nfor users to easily find your Site and its content through search and\nnavigation. For more information on using tags and categories, visit the\n\\href{/docs/7-2/user/-/knowledge_base/u/organizing-content-with-tags-and-categories}{Organizing\nContent with Tags and Categories} section.\n\n\\section{Site URL}\\label{site-url}\n\nThe \\emph{Friendly URL} option lets you set your Site's URL paths.\nFriendly URLs are used for both public and private pages. The public\nSite base URL is \\texttt{https://localhost:8080/web}, and the private\none is \\texttt{https://localhost:8080/group}. Each friendly URL must be\nunique.\n\nFor example, setting the friendly URL of your default Site to\n\\texttt{/lunar-resort} makes your Site's public home page's URL\n\\texttt{https://localhost:8080/web/lunar-resort/home}. The private\nSite's URL is thus\n\\texttt{https://localhost:8080/group/lunar-resort/home}.\n\nNote that if you add a friendly URL for your instance's home page, you\nshould update your instance's Home URL field so that page requests to\n\\texttt{http://localhost:8080} redirect properly:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Configuration} → \\emph{Instance Settings} → \\emph{Instance\n  Configuration} → \\emph{General} in the Control Panel.\n\\item\n  Under \\emph{Navigation}, in the \\emph{Home URL} field enter your home\n  URL (i.e. \\emph{/web/lunar-resort/home}).\n\\end{enumerate}\n\nOnce you've entered this setting, page requests to\n\\texttt{localhost:8080} redirect to the friendly URL of your Liferay\ninstance's new home page.\n\nYou can also configure Virtual Hosts, which connects a domain name to a\nSite, under \\emph{Site URL}. You can use this to define a domain name\n(i.e., \\texttt{www.lunar-resort.com}) for your Site. This can be a full\ndomain or a subdomain. You can use this to host a number of web sites as\nseparate Sites on one Liferay server.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/settting-virtual-hosts.png}\n\\caption{When configuring virtual hosts, the public and private pages of\na site can be configured to different domains.}\n\\end{figure}\n\nFor instance, if you set this up for the Lunar Resort's development\nnetwork, users in that Site would access\n\\texttt{developers.lunar-resort.com}, provided that the Lunar Resort\ninstance's network administrators created the domain name and pointed it\nto the Liferay server.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  With your provider, set the DNS name\n  \\emph{developers.lunar-resort.com} to point to your Liferay instance's\n  IP address.\n\\item\n  In the Virtual Host tab for the Developers Site, set the URL to\n  \\emph{http://developers.lunar-resort.com}\n\\end{enumerate}\n\nThis helps users quickly access their Site without having to recall an\nextended URL. The \\emph{Site URL} option is listed under the General\ntab.\n\n\\section{Documents and Media}\\label{documents-and-media}\n\nBelow that is \\emph{Documents and Media}, which lets you enable/disable\nDirectory Indexing. If on, Site administrators can browse your Site's\nDocuments and Media files and folders. For example, a Site administrator\nof a Site called \\emph{Lunar Resort} can browse documents at\n\\texttt{http://localhost:8080/documents/lunar-resort} if this option is\nenabled.\n\n\\section{Site Template}\\label{site-template}\n\nIf you created your Site using a Site Template, this section appears and\ndisplays information about the link between the Site Template and the\nSite. Specifically, you can see which Site Template was used and whether\nor not it allows modifications to the pages inherited from it by Site\nadministrators. To learn more about Site Templates and how to create\nyour own, see\n\\href{/docs/7-2/user/-/knowledge_base/u/building-sites-from-templates}{Building\nSites from Templates}.\n\n\\section{Asset Auto Tagging}\\label{asset-auto-tagging}\n\n\\emph{Asset Auto Tagging} lets you enable or disable the use of any\nAsset Auto Tagging rules on your site. See \\href{link}{Asset Auto\nTagging} to learn more about setting up auto tagging features.\n\n\\section{Custom Fields}\\label{custom-fields-1}\n\n\\emph{Custom Fields} only appears if you've created them in Control\nPanel → \\emph{Configuration} → \\emph{Custom Fields}. For more\ninformation on Custom Fields, see\n\\href{/docs/7-2/user/-/knowledge_base/u/setting-up}{Custom Fields}.\n\n\\chapter{Social Settings and\nLanguages}\\label{social-settings-and-languages}\n\nThe Social tab provides options for managing the social interactions on\nyour Site. Languages lets you configure language options and change the\ndefault language options for the Site.\n\n\\section{Ratings}\\label{ratings}\n\nThe \\emph{Ratings} option lets you select the ratings type to use for\napplications like Documents and Media, Web Content, Comments, etc.\nRatings types include Stars, Likes, and Thumbs.\n\n\\section{Mentions}\\label{mentions}\n\nAt the bottom of the page is \\emph{Mentions}, which lets you\nenable/disable Mentioning functionality, which is used to \\emph{mention}\n(notify and/or draw attention to) friends and colleagues by entering the\n``@'' character followed by their user names. See the\n\\href{/docs/7-2/user/-/knowledge_base/u/mentioning-users}{Mentioning\nUsers} article for more information.\n\n\\section{Languages}\\label{languages}\n\nThe \\emph{Languages} tab lets you configure the language options for\nyour Site.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/site-language.png}\n\\caption{In the Languages tab, you can configure the site to use the\ninstance's default language or another supported language.}\n\\end{figure}\n\nYou can use the default language or define another supported language as\nthe default for your Site.\n\n\\chapter{Advanced Site Settings}\\label{advanced-site-settings}\n\nAdvanced Settings relate to security (like User Roles) or require\nexternal configuration (like creating a Google Analytics account) to\nuse.\n\n\\section{Default User Associations}\\label{default-user-associations-1}\n\n\\emph{Default User Associations} configures Site roles and teams that\nnewly assigned Site members have by default. If you'd like to learn more\nabout creating roles and/or teams, visit the\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{Roles and\nPermissions} and\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-teams-for-advanced-site-membership-management}{Creating\nTeams for Advanced Site Membership Management}. sections.\n\n\\section{Analytics}\\label{analytics-1}\n\nLiferay DXP includes built-in support for Google Analytics for analyzing\ntraffic on your Site. Google Analytics provides a snippet of code which\nyou add to your pages enable tracking. Adding this code to every page on\na Site would be tedious, especially if it's a large Site with a lot of\nuser-generated content.\n\nThere are two ways to mitigate this problem:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  A web developer can hard-code the tracking code into a theme, which\n  embeds it on every page.\n\\item\n  An administrator can enter the tracking code in Site settings.\n\\end{enumerate}\n\nTo use option \\#2:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Configuration} → \\emph{Settings} → \\emph{Advanced}.\n\\item\n  Expand the \\emph{Analytics} section.\n\\item\n  Enter your Google Analytics ID.\n\\item\n  Click \\emph{Save}.\n\n  All the pages in the Site you selected now have the Google Analytics\n  code and can be tracked.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/maintaining-google-analytics.png}\n\\caption{To set up Google Analytics: sign up, receive an ID, and then\nenter it into the Google Analytics ID field.}\n\\end{figure}\n\nTo enable a different analytics service:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Configuration} in the Control Panel.\n\\item\n  Go to \\emph{Instance Settings} → \\emph{Analytics}.\n\\item\n  Enter the name of any additional service you want to add in the\n  \\emph{Analytics} field provided.\n\\item\n  Once you have entered the name, go to the \\emph{Site Settings} →\n  \\emph{Advanced} → \\emph{Analytics} page for the Site where you wish to\n  add analytics.\n\\item\n  Copy the JavaScript tracking code provided by your analytics platform\n  into the corresponding field for your service.\n\\end{enumerate}\n\nNow all pages on the selected Site contain the tracking script and send\nanalytics data to your analytics platform.\n\n\\section{Maps}\\label{maps}\n\nThe \\emph{Maps} option configures the maps API provider used by your\nLiferay instance when displaying geolocalized assets. Geolocalized\nassets can be displayed for documents, web content articles, DDL\nrecords, etc. Maps is available under the Advanced tab. You can read\nmore about Geolocation in\n\\href{/docs/7-2/user/-/knowledge_base/u/geolocating-assets}{Geolocating\nAssets}.\n\n\\section{Recycle Bin}\\label{recycle-bin}\n\nThe \\emph{Recycle Bin} option enables or disables the Recycle Bin for\nyour Site. You can also regulate the age (in minutes) for which content\nis able to be stored in the Recycle Bin until it is permanently deleted.\nFor a full explanation of the Recycle Bin, see\n\\href{/docs/7-2/user/-/knowledge_base/u/restoring-deleted-assets}{Restoring\nDeleted Assets}.\n\n\\section{Content Sharing}\\label{content-sharing}\n\nIf you select the \\emph{Content Sharing} tab from the Advanced tab, you\ncan configure whether sub-Sites can display content from this Site.\nAdministrators of this Site's sub-Sites can use all structures,\ntemplates, categories, widget templates, and more from this parent Site.\nEven if you initially allowed content sharing between the parent Site\nand its sub-Sites, you can disable this option and immediately revoke\ncontent sharing from all sub-Sites.\n\nYou can manage this globally by navigating to the Control Panel →\n\\emph{Configuration} → \\emph{Instance Settings} → \\emph{Content \\& Data}\n→ \\emph{Sharing} → \\emph{Content Sharing}. First, you can choose if Site\nadministrators can display content in Sites from other Sites they\nadminister. For example, suppose that a certain User is a Site\nadministrator of two Sites: \\emph{Engineering} and \\emph{Marketing}. The\ncheckbox in the Content Sharing section of Instance Settings determines\nif the Site administrator can display content from the Marketing Site in\nthe Engineering Site and vice versa.\n\nYou can also choose if child Sites can display content from parent Sites\nand configure the defaults. There are three options:\n\n\\textbf{Enabled by Default}: Child Sites can display content from parent\nSites by default, but this can be disabled by a Site administrator.\n\n\\textbf{Disabled by Default}: Child Sites cannot display content from\nparent Sites by default, but this can be enabled by a Site\nadministrator.\n\n\\textbf{Disabled}: Child Sites cannot display content from parent Sites,\nand this behavior cannot be changed by a Site administrator.\n\nThat covers your Site's advanced settings. You're now equipped to manage\nall aspects of your Site's configuration.\n\n\\chapter{Customizing Personal Sites}\\label{customizing-personal-sites}\n\nBy default, newly created users are granted a personal Site.\n\n\\begin{itemize}\n\\item\n  Users function as Site administrators of their personal Sites.\n\\item\n  Personal Sites are fully customizable but cannot have more than one\n  member.\n\\item\n  Users can have publicly available content on their Site's Public\n  Pages. This is often used for a user blog.\n\\item\n  Users can also have Private Pages where they can keep personal\n  information or use Documents and Media to have their own private file\n  repositories.\n\\end{itemize}\n\nYou can disable personal Sites by adding the following properties to\nyour \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\nlayout.user.public.layouts.enabled=false\nlayout.user.private.layouts.enabled=false\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The public and private page sets of personal Sites are\nhandled separately. You can leave one page set enabled while disabling\nthe other.\n\n\\noindent\\hrulefill\n\nIf you initially had user personal Sites enabled for your instance but\nthen disabled them, existing personal Sites remain on your Liferay\ninstance until the next time users log in, at which point they're\nremoved.\n\nYou can allow users to create personal Sites but not have them\nautomatically created for new users. To do this, add the following\nproperties to your \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\nlayout.user.public.layouts.auto.create=false\nlayout.user.private.layouts.auto.create=false\n\\end{verbatim}\n\nIf the properties \\texttt{layout.user.public.layouts.enabled},\n\\texttt{layout.user.private.layouts.enabled},\n\\texttt{layout.user.public.layouts.auto.create}, and\n\\texttt{layout.user.private.layouts.auto.create} are all set to\n\\texttt{true}, which is the default, users have personal Sites and\npublic and private pages are created automatically for new users.\n\nThere are a number of portal properties you can use to customize the\nautomatically created pages. You can customize the names of the default\npages, the applications that appear on the pages, the themes and layout\ntemplates of the default pages, and more. Please refer to the\n\\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Default\\%20User\\%20Public\\%20Layouts}{Default\nUser Public Layouts} and\n\\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Default\\%20User\\%20Private\\%20Layouts}{Default\nUser Private Layouts} sections of the \\texttt{portal.properties} file\nfor details.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} By default, users can modify the pages and applications\non their personal Sites. Administrators, however, can customize the\nmodifiable portions of personal Sites through Liferay DXP's permissions\nsystem by removing permissions from Roles. To disallow all Liferay users\nfrom modifying something, remove the relevant permission from the User\nRole.\n\n\\noindent\\hrulefill\n\nGreat! Now you know how to customize a personal site!\n\n\\chapter{Importing/Exporting Sites and\nContent}\\label{importingexporting-sites-and-content}\n\nExport/Import lets you backup and restore your Site and app data as a\nLAR (Liferay Archive). There are two primary places Export/Import is\nused: Sites and apps. You can learn more about exporting/importing app\ndata in the\n\\href{/docs/7-2/user/-/knowledge_base/u/exporting-importing-widget-data}{Exporting/Importing\nWidget Content} section. In this section, you'll learn how to export and\nimport content for Sites.\n\n\\section{Backing Up and Restoring Pages and Their\nContent}\\label{backing-up-and-restoring-pages-and-their-content}\n\nIn \\emph{Site Administration} → \\emph{Publishing}, you can find the\n\\emph{Export} and \\emph{Import} option for pages. If you click on\n\\emph{Export}, you see an interface for exporting your public or private\npages. The Export feature exports your Site's data as a single LAR file.\nSimilarly, \\emph{Import} is a similar interface for importing public or\nprivate pages from a LAR file.\n\nWhen importing data into a Site, you should use a newly created Site to\navoid conflicts between the existing data and the data being imported.\nWhen exporting Site data, you can specify exactly what data should be\nincluded in the LAR:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Site pages (you can select exactly which ones)\n\\item\n  Page settings\n\\item\n  Theme\n\\item\n  Theme settings\n\\item\n  Logo\n\\item\n  Application configurations\n\\item\n  Application content\n\\item\n  Archived setups\n\\item\n  User preferences\n\\end{itemize}\n\nA LAR file can be imported into a Site on another Liferay server. You\ncan take content from a Site in one environment (say, a development or\nQA environment) and move it all to a Site on another server with LARs.\nYou can use LARs to import data onto production servers, but you should\nnot make this a regular occurrence. If you want to regularly move pages\nfrom one server to another, you should use Liferay DXP's staging\nenvironment. See the\n\\href{/docs/7-2/user/-/knowledge_base/u/staging}{Staging} section for\nmore details.\n\nYou can export LARs to use them as a backup. If you ever have to restore\nyour Site, you must only import the latest LAR file. However, please be\ncareful! If there's content that exists both in the LAR and in the Site\nthat's importing the data, there may be a conflict, and data could be\ncorrupted. If you want to restore a Liferay Site using a LAR file,\ndelete the Site entirely, create a new Site with the same name as the\nold one, and then import the LAR file into the new Site. This way,\nthere's no chance for there to be a data conflict.\n\nSome naming collisions are handled automatically. For example, a\ncollision occurs if the LAR you're importing and the Site both have a\npage with the same friendly URL. Liferay DXP resolves the collision by\nadding a number to the end of the friendly URL and incrementing until\nthere's no collision. Similarly, if importing a LAR into a Site causes a\ncategory name collision, the imported categories are automatically\nrenamed.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} LAR files are version dependent. You can't import a LAR\nfile that was exported from one version of Liferay into a Liferay server\nthat's running a different version of Liferay. Also, note that\nperiodically exporting LARs is \\emph{not} a complete backup solution;\nplease refer to the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation}{Backing\nup a Liferay Installation} section for information on backing up\nLiferay.\n\n\\noindent\\hrulefill\n\n\\section{Page Export Example}\\label{page-export-example}\n\nHere's how the export process works:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go the \\emph{Site Administration} → \\emph{Publishing}.\n\\item\n  Click \\emph{Export}.\n\\item\n  Click \\emph{Add} (\\includegraphics{./images/icon-add.png}).\n\n  A \\emph{New Custom Export} page loads, so you can choose the pages and\n  content you want to export from your Site.\n\\item\n  Enter \\emph{Lunar Resort Version 1} for the \\emph{Title}.\n\\item\n  Under \\emph{Pages}, select public or private pages and the settings\n  you want to export.\n\\item\n  Under the \\emph{Content} category, select \\emph{All}.\n\n  Note that if you select one of the \\emph{Choose} radio selectors or\n  \\emph{Change} links, you're given checkboxes for options to choose.\n  The applications' content can also be selected for export, including\n  the Documents and Media Library, Message Boards, and Web Content\n  assets. You can even export the theme you're using. Finally, you can\n  select whether the permissions for your exported pages and content are\n  included.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/export-page-templates.png}\n  \\caption{You can configure your export options manually by selecting\n  pages, content, and permissions.}\n  \\end{figure}\n\\item\n  Click \\emph{Export}.\n\\end{enumerate}\n\nOnce you click \\emph{Export}, the menu automatically switches to the\n\\emph{Processes} tab, where you see the status of your exported LAR\nfile. You can select the \\emph{Download} icon\n(\\includegraphics{./images/icon-download.png}) to download the export to\nyour local machine. Once you have the file, you can copy it to a backup\nlocation for safekeeping or import it into another installation of\nLiferay. If you must rebuild or wish to revert back to this version of\nyour Site, you can import this file by clicking the \\emph{Import} button\nfrom the Publishing menu, browsing to it, and selecting it. You can also\ndrag a LAR file inside the dotted area, which also executes the import\nprocess.\n\n\\section{Export Templates}\\label{export-templates}\n\nInstead of manually customizing an export process every time you export\npages/content, you can use an Export Template. This provides you the\nconvenience of storing export process settings so they can be reused. If\nyou export pages frequently and usually select the same options to\nexport, you can create an export template to export with your standard\noptions.\n\nTo create an export template,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select the \\emph{Options} icon\n  (\\includegraphics{./images/icon-options.png}) from the top right\n  corner of the screen and select \\emph{Export Templates}.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n\\item\n  Assign the template a \\emph{Name} and \\emph{Description}.\n\\item\n  Fill out the configuration options for your export process.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nYour template is now available to use from the \\emph{Export Templates}\nmenu. To use the template,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Actions} (\\includegraphics{./images/icon-actions.png})\n  next to the template.\n\\item\n  Select \\emph{Export}.\n\n  This automatically fills the fields and options for exporting pages\n  and their content.\n\\item\n  Give the export a name.\n\\item\n  Click \\emph{Export} and your LAR file is generated.\n\\end{enumerate}\n\n\\chapter{Styling Apps and Assets}\\label{styling-apps-and-assets}\n\nWidget Template define custom display templates used to render widgets.\nFor example, you may want to show blog entries horizontally instead of\nvertically, or list your assets in the asset publisher application in\ndifferent sizes. In this section, you'll learn about the capabilities of\nwidget templates and how to create and configure them.\n\n\\chapter{Styling Widgets with Widget\nTemplates}\\label{styling-widgets-with-widget-templates}\n\nSuppose you're customizing the Lunar Resort Site and want to allow users\nto use Facebook or Twitter to communicate with other interested\ntravelers. You can add this functionality to an existing widget using\nwidget templates: launch a template editor, create a custom template,\nand configure your app to host that template. Widget templates let you\nre-skin your widget and give you ultimate control over its appearance\nand functionality.\n\n\\section{Creating a Widget Template}\\label{creating-a-widget-template}\n\nHere's the process of creating a widget template:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From Site Administration, click the \\emph{Site Selector} button\n  (\\includegraphics{./images/icon-compass.png}) to choose the Site where\n  you want to create the widget template.\n\\item\n  Open \\emph{Site Builder} → \\emph{Widget Templates}.\n\\end{enumerate}\n\nIf you selected the Global context, this page shows a list of sample\ntemplates available for your apps. These sample templates differ from\nthe default templates already configured in the apps. If you choose a\nSite to host your template, you must create a custom template for that\nSite's apps.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/context-selector.png}\n\\caption{The Site Administration dropdown menu lets you choose the\ncontext in which your widget template resides.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\tightlist\n\\item\n  Click the \\emph{Add} (\\includegraphics{./images/icon-add.png}) button,\n  and you're prompted to select the type of template to create.\n\\end{enumerate}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Asset Publisher}\n\\item\n  \\emph{Blogs}\n\\item\n  \\emph{Breadcrumb}\n\\item\n  \\emph{Categories Navigation}\n\\item\n  \\emph{Language Selector}\n\\item\n  \\emph{Media Gallery}\n\\item\n  \\emph{Navigation Menu}\n\\item\n  \\emph{RSS Publisher}\n\\item\n  \\emph{Site Map}\n\\item\n  \\emph{Tags Navigation}\n\\item\n  \\emph{Wiki}\n\\end{itemize}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  Enter the name and, optionally, open \\emph{Details} to provide a\n  description and a small image to use. You can select the language type\n  for your template.\n\\item\n  Within \\emph{Details} select a scripting language to use. You can use\n  FreeMarker or Velocity. FreeMarker is recommended.\n\\item\n  Use the \\emph{Script} section to create the widget template.\n\\item\n  Click \\emph{Save} when done.\n\\end{enumerate}\n\n\\section{The Template Editor}\\label{the-template-editor}\n\nOn the left side of the template editor is a palette of common variables\nused for making templates. This is a great reference when creating your\ntemplate. To place one of the variables into the template editor,\nposition your text cursor where you want it placed, and click the\nvariable name.\n\nEach variable also has a tooltip which displays a detailed description.\nBecause there are multiple kinds of widget templates, there are also\ndifferent variables for each widget template. Thus, each template has a\ndifferent set of variables only applicable for that specific template.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adt-script-editor.png}\n\\caption{Liferay offers a versatile script editor to customize your\nwidget template.}\n\\end{figure}\n\nYou can also use the autocomplete feature to add variables to your\ntemplate. It can be invoked by typing \\emph{\\$\\{} which opens a\ndrop-down menu of available variables. By clicking one of the variables,\nthe editor inserts the variable into the editor.\n\nYou can also embed same-type templates into other templates. For\nexample, suppose you have an existing Wiki widget template and would\nlike to create another similar Wiki widget template. Instead of starting\nfrom scratch, you can import the existing Wiki widget template into your\nnew one and build off of it. In other words, you can utilize widget\ntemplates as generic templates which allow for reusable code to be\nimported by Velocity or FreeMarker templates in the system.\n\n\\section{Configuring Widget\nTemplates}\\label{configuring-widget-templates}\n\nAfter you've saved your widget template, you can manage it through its\n\\emph{Actions} (\\includegraphics{./images/icon-actions.png}) button.\nThis provides several options:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Edit}: lets you modify the widget template's setup properties.\n\\item\n  \\emph{Permissions}: lets you manage the permissions \\emph{Update},\n  \\emph{Permissions}, \\emph{Delete}, and \\emph{View} for the widget\n  template.\n\\item\n  \\emph{Copy}: creates a copy of the widget template.\n\\item\n  \\emph{Delete}: deletes the widget template.\n\\end{itemize}\n\nAdditionally, your widget template generates a static URL and a WebDAV\nURL. These values access the XML source of your template. You can find\nthese URLs by clicking the widget template from the menu and expanding\nthe \\emph{Details} section. With the WebDAV URL, Site administrators can\nadd, browse, edit, and delete widget templates on a remote server. If\nyou want to learn more about what the WebDAV URL can do, visit the\narticle on\n\\href{/docs/7-2/user/-/knowledge_base/u/desktop-access-to-documents-and-media}{WebDAV\naccess}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Embedding widgets into widget templates, although\npossible, is not recommended because this could cause conflicts with\nother widgets or unexpected behavior (e.g., embedding a widget that\naggregates data to the breadcrumb). If embedding a widget into a widget\ntemplate is your only option, make sure it does not interfere with other\nwidgets.\n\n\\noindent\\hrulefill\n\nNext you must configure the widget to use the new widget template:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\emph{Configuration} page for the widget you want to modify\n  and open its \\emph{Display Settings}.\n\\item\n  Under \\emph{Display Template}, select your widget template from the\n  drop-down menu.\n\\end{enumerate}\n\nAlso, you can manage Site-specific display templates for your app: do\nthis by clicking the \\emph{Manage Display Templates for\n{[}SPECIFIC\\_SITE{]}} link next to the \\emph{Display Template} drop-down\nmenu. A window appears with a list of your configured templates only\navailable for your Site with options to add new templates or edit\nexisting templates.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adt-configuration.png}\n\\caption{In the \\emph{Configuration} menu of an app, you can edit and\nmanage available widget templates.}\n\\end{figure}\n\n\\chapter{Widget Template Example}\\label{widget-template-example}\n\nNow that you know the general functions of widget templates, you'll\ncreate your own. This brief demonstration will show you just how easy,\nyet powerful, widget templates can be for your Liferay instance.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the Media Gallery widget to a page by navigating to \\emph{Add}\n  (\\includegraphics{./images/icon-add.png}) → \\emph{Widgets} →\n  \\emph{Content Management} → \\emph{Media Gallery}.\n\\item\n  Click the widgets's \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}) → \\emph{Multiple Files\n  Upload} and select two custom photos to display. Then click\n  \\emph{Save}, and navigate back to the main application screen.\n\\item\n  Notice the default format of the pictures. To change the display\n  template for this widget, navigate to \\emph{Options}\n  (\\includegraphics{./images/icon-app-options.png}) →\n  \\emph{Configuration}.\n\\item\n  From the \\emph{Display Template} drop-down menu, select\n  \\emph{Carousel}. Then click \\emph{Save}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/adt-carousel.png}\n  \\caption{After applying the Carousel widget template, your pictures\n  are displayed as a carousel slideshow.}\n  \\end{figure}\n\n  The Media Gallery application is transformed into a carousel\n  slideshow. At this time, it's perfectly natural to be experiencing ``I\n  can conquer the world'' feelings, just as Liferay's mascot, Ray,\n  exudes in the image above. widget templates have that kind of power to\n  transform your site into an enjoyable and convenient home for users.\n\\end{enumerate}\n\nCustomizing the user interface of Liferay DXP's bundled widgets provides\nthe ultimate customization experience for Liferay users.\n\n\\chapter{Setting a Default Widget\nTemplate}\\label{setting-a-default-widget-template}\n\nYou can change the widget template for an individual widget through its\nown configuration, but to configure the default widget template for all\nwidgets of that type, you must go to \\emph{System Settings}. In the\nSystem Settings you can find a configuration for every widget in Liferay\nDXP. Any widget that supports widget templates has a \\emph{Display Style\nGroup ID} and a \\emph{Display Style} option.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adt-system-settings.png}\n\\caption{The widget template configuration in System Settings lets you\nchange the display style.}\n\\end{figure}\n\n\\begin{itemize}\n\\item\n  \\textbf{Display Style Group ID:} The Site ID where the widget template\n  is located. For Global templates use \\emph{0} for the ID.\n\\item\n  \\textbf{Display Style:} The widget template's key.\n\\end{itemize}\n\nTo enter a Display Style, you first need the \\emph{Template Key} for the\ntemplate you want to use. To get the Template Key, go to the\n\\emph{Application Display Template} list for a given Site and retrieve\nit from the widget template listing. Then enter the display style as\n\\texttt{ddmTemplate\\_{[}template-key{]}}.\n\n\\section{Default Widget Template\nExample}\\label{default-widget-template-example}\n\nFor example, configure the Language Selector widget templates like this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\emph{Product Menu}.\n\\item\n  Using the \\emph{Site Selector}, select the \\emph{Global} site.\n\\item\n  Go to \\emph{Site Builder} → \\emph{Widget Templates}\n\\item\n  Create a \\emph{Language Selector Template}.\n\\item\n  Click the \\emph{Actions} (\\includegraphics{./images/icon-actions.png})\n  button for the new widget template.\n\\item\n  Open \\emph{Details} and find the \\emph{Template Key} -\n  \\texttt{LANGUAGE-ICON-FTL}\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adt-template-key.png}\n\\caption{System Settings shows where you can find the Template Key.}\n\\end{figure}\n\nNow that you have the ID, you can change the template from System\nSettings.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings}.\n\\item\n  Find \\emph{Localization} under the \\emph{Platform} heading and select\n  \\emph{Language Selector} from the options on the left.\n\\item\n  In the \\emph{Display Style} field, enter\n  \\texttt{ddmTemplate\\_LANGUAGE-ICON-FTL}.\n\\end{enumerate}\n\nNow any Language Selector widgets are added to a page use the new\ndefaults. This doesn't affect widgets already added to a page and\nconfigured.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adt-new-default.png}\n\\caption{You can see the new default configuration.}\n\\end{figure}\n\n\\chapter{Customizing Page Options}\\label{customizing-page-options}\n\nEvery page has options to give it a unique configuration. You can handle\nthat configuration at the individual page level or configure a Page Set\nof public or private pages all at once.\n\nWhen you configure options across Sites, Page Sets, and individual\npages, there is a hierarchy that flows down. For example, you can set a\ntheme at the Site level that applies to all pages within a Site. You\ncould then set a different theme for the Private Pages set. You can even\nset a different theme for a specific page that is different than the\nmaster site configuration or the configuration for its page set.\n\nWhen configuring pages, be aware of your context, so you don't configure\nan option that you intended for the whole site just on one page---or\nchange the whole site's configuration, when you meant to change it for\nonly a specific page.\n\n\\chapter{Configuring Page Sets}\\label{configuring-page-sets}\n\nTo configure options for the entire Page Set, select the\n\\emph{Configure} icon next to the Page Set in \\emph{Pages}. Options\nconfigured for the Page Set apply to all its pages. Page Set options\noverride options set at the Site level, and customizations to an\nindividual page override those for the Page Set.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/configure-page-set.png}\n\\caption{Selecting the Page Set configuration option.}\n\\end{figure}\n\n\\chapter{Configuring Page Sets}\\label{configuring-page-sets-1}\n\nPage Set configuration starts with the \\emph{Look and Feel} tab. Here\nyou have an interface for choosing a theme for the current Site.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/page-set-look-and-feel.png}\n\\caption{The Look and Feel page set tab.}\n\\end{figure}\n\n\\section{Themes}\\label{themes}\n\nThemes can transform the entire look of your Site. They are created by\ndevelopers and are easily installed using Liferay Marketplace.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/look-and-feel-pages.png}\n\\caption{The Look and Feel interface allows you to choose a theme for\nthe current site.}\n\\end{figure}\n\nYou can apply themes to the entire Site or to individual pages. For the\nSite, go to \\emph{Pages} → the Site (public or private), and click the\nGear icon. For individual pages, click \\emph{Configure} → \\emph{Define a\nspecific look and feel for this page} option under the page's \\emph{Look\nand Feel} category. See the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{Themes}\nsection for information on creating and developing your own custom\nthemes.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/define-a-specific-look-and-feel.png}\n\\caption{You can define a specific look and feel for a page.}\n\\end{figure}\n\nMany themes include more than one color scheme, which keeps the existing\nlook and feel while giving the Site a different flavor. The Color\nSchemes option is not available for the default theme.\n\nThere are a few more configurable settings for your Page Set look and\nfeel. You can switch the bullet style between dots and arrows and you\ncan choose whether or not to show maximize/minimize application links by\ndefault. The \\emph{CSS} section lets you enter custom CSS for tweaking\nyour theme.\n\n\\section{Using a Custom Logo for a\nSite}\\label{using-a-custom-logo-for-a-site}\n\nBy default, the Liferay logo is used for your Site pages' logo. If you\nwant to use your own logo for a specific Site, select the \\emph{Logo}\ntab from the \\emph{Configure} interface and browse to the location of\nyour logo. Make sure your logo fits the space in the top left corner of\nthe theme you're using for your website. If you don't, you could wind up\nwith a Site that's difficult to navigate, as other page elements are\npushed aside to make way for the logo.\n\nIn the logo tab, you can also choose whether or not to display the Site\nname on the Site. If you check the box labeled \\emph{Show Site Name},\nthe Site name appears next to the logo. This option is enabled by\ndefault and cannot be disabled if the \\emph{Allow Site Administrators to\nset their own logo} option is disabled in \\emph{Instance Settings}.\nRemoving the Site name is not available for the default Site---you can\nconfigure this only for new Sites and user pages.\n\n\\chapter{Advanced Page Set Options}\\label{advanced-page-set-options}\n\nThere are some powerful options that should only be used by those with a\nfirm command of the technology, or they could have major unintended side\neffects. You can find these options under the \\emph{Advanced} tab.\n\n\\section{Executing JavaScript in Site\nPages}\\label{executing-javascript-in-site-pages}\n\nAt the top of the \\emph{Advanced} tab is a JavaScript editor. Code\nentered here is executed at the bottom of every page in the Site. If\nyour Site's theme uses JavaScript (as is usually the case), it's best to\nadd custom JavaScript code to the theme and \\emph{not} here. This way,\nall your Site's JavaScript code remains in one place.\n\nThis may be useful if your Site's theme does \\emph{not} use JavaScript.\nIn this case, you can place \\emph{all} of your Site's JavaScript here.\n\n\\section{Merge Public Pages}\\label{merge-public-pages}\n\nIf you have more than one Site on a specific Liferay instance, one of\nthose Sites will be the \\emph{Default Site} where visitors will be\ndirected if they access the instance's root URL. By default, visitors\nwill only see the pages of that Site in the navigation. To have another\nSite's public pages appear in the primary navigation for the Default\nSite, check the box to \\emph{Merge public pages} for that site. Be\ncareful as adding too many pages to the main navigation can make it\nbecome unwieldy very quickly.\n\n\\section{Rendering Pages for Mobile\nDevices}\\label{rendering-pages-for-mobile-devices}\n\n\\emph{Mobile Device Rules} lets you configure your page set to have\nspecific behaviors for specific mobile devices or types. Mobile device\nrules are inherited from your Public Pages, but you can define specific\nrules per page. You can edit the Look and Feel of specific pages for\nmobile devices, including the theme. This is explained in\n\\href{/docs/7-2/user/-/knowledge_base/u/mobile-device-rules}{Mobile\nDevice Rules}.\n\n\\section{Robots}\\label{robots}\n\nThe \\emph{Robots} option lets you configure \\texttt{robots.txt} rules\nfor the domain: both its public and private pages. The\n\\texttt{robots.txt} file provides instructions to search engines and\nother tools that are automatically crawling and indexing your Site.\nCommon entries here include defining some pages not to be indexed.\n\n\\section{Notifying Search Engines of Site\nPages}\\label{notifying-search-engines-of-site-pages}\n\nThe \\emph{Sitemap} option generates a sitemap you can send to some\nsearch engines so they can crawl your Site. It uses the industry\nstandard sitemap protocol.\n\nSelect a search engine link to send the sitemap to it. It's only\nnecessary to do this once per Site.\n\nIf you're interested in seeing what is sent to the search engines,\nselect the \\emph{preview} link to see the generated XML.\n\n\\chapter{Configuring Individual\nPages}\\label{configuring-individual-pages}\n\nAfter you've configured your Page Set, you can reconfigure some options\nat the individual page level. When you decide to customize a single\npage, options that were not available when initially creating a page\nappear. You can customize a page by navigating to \\emph{Pages} under\n\\emph{Build} menu and selecting \\emph{Options}\n(\\includegraphics{./images/icon-options.png}) → \\emph{Configure} next to\nthe page you want to edit from the navigation tree. Alternatively, you\ncan click the \\emph{Configure} icon on the top right of any page.\n\n\\chapter{Individual Page Settings}\\label{individual-page-settings}\n\nOn the Configure page are four tabs: General, SEO, Look and Feel, and\nAdvanced. Options selected here have no effect on the rest of the Site;\njust the page you've selected. Many of these options are the same as\nthose that configure the complete page set, so you can view more details\nin the\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-page-sets}{Configuring\nPage Sets} article.\n\nNote that many of the options are localizable, so you can provide\ntranslations based on the user's locale.\n\n\\section{General}\\label{general-2}\n\nThe \\emph{General} tab lets you configure the basic information and\ndesign for the page. You can change the \\emph{Name}, \\emph{Friendly\nURL}, and \\emph{Page Layout}.\n\n\\section{Name and Friendly URL}\\label{name-and-friendly-url}\n\nThe \\emph{Name} is the title that appears in the browser's title bar,\nand how the page is identified in the navigation. The \\emph{Friendly\nURL} defines the page's link. It is a best practice to have the URL\nmatch the name of the Page, so these two should generally be updated\ntogether.\n\n\\section{Page Layout}\\label{page-layout}\n\nFor Widget Pages, you can select a Layout Template that defines\ndroppable locations for widgets. Layout templates define a number of\nsections with columns and rows. Widgets added to a section expand (or\ncontract) horizontally to fill the space and can be stacked vertically.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/page-select-layout.png}\n\\caption{Setting a layout template for your page.}\n\\end{figure}\n\n\\section{Categorization and SEO}\\label{categorization-and-seo}\n\nManaging your page's content drastically improves your page's\norganization and user experience. The Site page's configuration options\noffers some opportunities to organize page content.\n\n\\section{Categorization}\\label{categorization-1}\n\nThe \\emph{Categorization} tab shows the categorization options. These\ntools help administrators organize the page so users can find your page\nand its content through search and navigation. For more information on\nusing tags and categories, see\n\\href{/docs/7-1/user/-/knowledge_base/u/organizing-content-with-tags-and-categories}{Organizing\nContent with Tags and Categories}.\n\n\\section{SEO}\\label{seo}\n\n\\emph{SEO} provides several ways to optimize the data the page provides\nto an indexer that's crawling the page. You can set the various meta\ntags for description, keywords and robots. There's also a separate\nRobots section for telling robots how frequently the page is updated and\nhow it should be prioritized. If the page is localized, you can select a\nbox to generate canonical links by language. If you want to set some of\nthese settings for the entire Site, you can specify them from the\nSitemaps and Robots tabs of the Manage Site Settings dialog box (see\nbelow).\n\nEach asset (web content article, blog entry, etc.) has a unique URL.\nFrom the search engine's point of view, this makes your pages rank\nhigher since any references to variations of a specific URL are\nconsidered references to the same page.\n\nYou can also configure the page to use a custom canonical URL. To do so,\nset the \\emph{Use Custom Canonical URL} toggle to \\emph{YES}, then enter\nyour desired canonical URL in the field that appears. You can define a\ncustom canonical URL for each language. If there's no value for a\nspecific language, the canonical URL for that language is controlled by\nthe global/instance-level setting.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/canonical-url-page.png}\n\\caption{Enter the custom canonical URL that you want to use for the\npage.}\n\\end{figure}\n\nYou can also configure canonical URLs at the global and instance levels.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Any custom canonical URLs set for individual pages take\nprecedent over the global and instance level settings.\n\n\\noindent\\hrulefill\n\n\\textbf{Global:} \\emph{Control Panel} → \\emph{Configuration} →\n\\emph{System Settings} → \\emph{Pages} → \\emph{SEO}\n\n\\textbf{Instance:} \\emph{Control Panel} → \\emph{Configuration} →\n\\emph{Instance Settings} → \\emph{Pages} → \\emph{SEO}\n\nNavigate to the level (global/instance) on which you want to configure\ncanonical URLs, then choose one of the following in the \\emph{Canonical\nURL} menu:\n\n\\textbf{Use Default Language URL (default):} When a user visits a page\nin any supported language, the default language's URL is used as the\ncanonical URL.\n\n\\textbf{Use Localized URL:} The page's localized URL is used as the\ncanonical URL.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/canonical-url-system.png}\n\\caption{You can also configure canonical URLs at the global and\ninstance levels.}\n\\end{figure}\n\n\\section{Look and Feel}\\label{look-and-feel-1}\n\n\\emph{Look and Feel} lets you set a page-specific theme. You can inherit\nwhat you already have configured for your Page Set's theme, or you can\ndefine a theme per page. See\n\\href{/docs/7-2/user/-/knowledge_base/u/page-set-look-and-feel}{Customizing\nthe Look and Feel of Site Pages} for more details.\n\n\\section{Advanced Settings}\\label{advanced-settings}\n\n\\emph{Advanced Settings} contains options useful for specific cases.\nSome of these are the same as the options available at the Site or Page\nSet level, but \\emph{Custom Fields}, \\emph{Embedded Widgets}, and\n\\emph{Customization Settings} are unique to the individual page\nconfiguration.\n\n\\section{Query String}\\label{query-string}\n\nYou can set a query string to provide parameters to the page. This can\nbecome useful to web content templates. You can set a target for the\npage so that it pops up in a particularly named window or appears in a\nframeset. And you can set an icon for the page that appears in the\nnavigation menu.\n\n\\section{Custom Fields}\\label{custom-fields-2}\n\n\\emph{Custom Fields} lets you edit the custom fields you already have\nconfigured for the \\emph{Page} resource. If you don't have any custom\nfields configured in your Site, this option doesn't appear. In this\ncase, navigate to the Control Panel → \\emph{Custom Fields} located under\nthe \\emph{Configuration} tab. These are metadata about the page and can\nbe anything you like, such as author or creation date. For more\ninformation on Custom Fields, see\n\\href{/docs/7-1/user/-/knowledge_base/u/setting-up}{Custom Fields}.\n\n\\section{Embedded Widgets}\\label{embedded-widgets}\n\nThis option only appears if you have embedded one or more widgets on the\npage.\n\nWidgets can be embedded on a page via web content template or fragment.\nTo learn more about this, see\n\\href{/docs/7-2/user/-/knowledge_base/u/adding-templates}{Adding\nTemplates}. You can embed a widget on a page layout or theme\nprogrammatically. If you're interested in learning more about this,\nvisit the\n\\href{develop/tutorials/-/knowledge_base/7-2/embedding-portlets-in-themes}{Embedding\nPortlets in Themes} tutorial.\n\n\\section{Customization Settings}\\label{customization-settings}\n\nThis configuration option in the \\emph{Advanced} tab lets you mark\nspecific sections of the page you want users to be able to customize.\nYou can learn more about page customizations in\n\\href{/docs/7-2/user/-/knowledge_base/u/personalizing-pages}{Personalizing\nPages}.\n\n\\section{JavaScript}\\label{javascript}\n\nThis shows a JavaScript editor for code that's executed at the bottom of\nyour page. If your Site's theme uses JavaScript (as is usually the\ncase), it's best to add custom JavaScript code to the theme instead.\nThis way, all your Site's JavaScript code remains in one place.\n\nThis configuration option is also available for Page Sets like Public\nPages and Private Pages. Visit\n\\href{/docs/7-2/user/-/knowledge_base/u/advanced-page-set-options\\#executing-javascript-in-site-pages}{Executing\nJavaScript in Site Pages} for more information on doing this for Page\nSets.\n\n\\section{Mobile Device Rules}\\label{mobile-device-rules-1}\n\nApply rules for how this page should render for various mobile devices\nhere. Create them by navigating to Site Administration menu and\nselecting \\emph{Configuration} → \\emph{Mobile Device Families}.\n\n\\chapter{Personalizing Pages}\\label{personalizing-pages}\n\nAdministrators can designate pages or sections of Widget Pages as\ncustomizable. When a user visits such a page, a notification appears\nstating that the user can customize the page. Users can make\ncustomizations only in the sections of pages designated by\nadministrators. Customizations are based on the rows and columns of a\npage layout. Page customizations are only visible to the user who made\nthe customizations. By default, Site members can make page\ncustomizations but non-Site members and guests can't.\n\n\\section{Enabling Page\nCustomizations}\\label{enabling-page-customizations}\n\nTo enable page customizations as an administrator,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Configure Page} from the \\emph{Options} button next to the\n  Page you want to let Site members modify.\n\\item\n  Select the \\emph{Advanced} tab at the top of the page and expand the\n  \\emph{Customization Settings} area.\n\\item\n  Click the \\emph{Customizable} selector button to activate\n  customizations.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/page-customizations.png}\n  \\caption{To enable page customizations, click on the \\emph{Configure\n  Page} button next to the page, expand the \\emph{Customization\n  Settings} area, and click on the \\emph{Customizable} button.}\n  \\end{figure}\n\\item\n  Select the sections of the page that should be customizable.\n\\item\n  Enable one or more of the \\emph{Customizable} sections so Site members\n  can customize sections of the page. Regions that you've designated as\n  customizable are colored blue.\n\\end{enumerate}\n\nWhen Site members visit your customizable page, they see an extended\nControl Menu with a notification saying \\emph{You can customize this\npage}. Site members can toggle whether to view or hide the customizable\nregions. If you toggle the selector to view customizable regions, the\nregions on the page are color-coded to help distinguish customizable\nvs.~non-customizable sections of the page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/color-coded-customizable-regions.png}\n\\caption{Customizable regions are colored green and non-customizable\nregions are colored red.}\n\\end{figure}\n\n\\section{Customization Permissions}\\label{customization-permissions}\n\nAdministrators must grant users permission to customize pages under the\nSite section. This can be achieved by assigning permission to a Role,\nthen assigning this Role to the appropriate users. For example, if you\nwant users to be able to customize your customizable pages, assign the\n\\emph{Customize} permission to the Role \\emph{User}. If you want Site\nmembers to be able to customize their Sites' customizable pages, accept\nthe default setting. By default, the \\emph{Customize} permission is\nassigned to the Role \\emph{Site Member}.\n\nThe \\emph{Customize} permission also lets users customize the look and\nfeel of apps and import or export app settings.\n\n\\section{Customizing Pages}\\label{customizing-pages}\n\nWith customization active, Site members can access the Add menu from the\ntop right side of the screen when viewing their customizable page, which\nlets them add apps to the customizable sections of the page. If they\nclick \\emph{View Page without my customizations}, the Add menu\ndisappears.\n\nUsers can make two kinds of customizations to customizable regions:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  They can configure applications within the customizable regions.\n\\item\n  They can add apps to or remove apps from the customizable regions.\n\\end{enumerate}\n\n\\emph{Reset My Customizations} from the \\emph{Options} button restores a\nuser's customized page to match the default page, discarding their\ncustomizations so they can start anew.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/customizable-regions.png}\n\\caption{Customizable areas are highlighted green when organizing apps\non the page.}\n\\end{figure}\n\nUsers can't change a non-instanceable app's configuration inside a\ncustomizable region since those apps are tied to the Site where they've\nbeen added.\n\n\\section{Viewing Customized Pages}\\label{viewing-customized-pages}\n\nSite members can also choose between viewing their customized page and\nviewing the default page by selecting the \\emph{Options} button\n(\\includegraphics{./images/icon-options.png}) from the Control Menu and\nclicking the \\emph{View Page without my customizations} or \\emph{View My\nCustomized Page}.\n\nAdministrators of customizable pages have the same two views as Site\nmembers: the \\emph{default page} view and the \\emph{customized page}\nview. Changes made to the default page affect all users, whereas changes\nmade to the customized page affect only the administrator who made the\nchanges. Changes made by administrators to non-customizable sections in\nthe default view are immediately applied for all users. Changes made by\nadministrators to customizable sections, however, do \\emph{not}\noverwrite users' customizations.\n\n\\section{Customization Example}\\label{customization-example}\n\nAs an administrator,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Configure} for the \\emph{Welcome} page.\n\\item\n  Go to the \\emph{Advanced} tab and activate Customizations.\n\\item\n  Set the main column of the Welcome page of the Lunar Resort Site to be\n  customizable.\n\\end{enumerate}\n\nAs a regular user,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the \\emph{Welcome} page.\n\\item\n  Click \\emph{Add} → \\emph{Widgets}.\n\\item\n  Locate the \\emph{Language Selector} widget and add it to the page.\n\\end{enumerate}\n\nThe Language Selector application lets users select their language to\nview a translation of your Site into their native language. After\nclosing the Configuration dialog box of the Language Selector app, the\ncustomized Welcome page looks like this:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/customized-portal-homepage.png}\n\\caption{In this example, the user added the Language app, and changed\nthe display style from icons to a select box.}\n\\end{figure}\n\n\\chapter{Changing Page Permissions}\\label{changing-page-permissions}\n\nPublic pages are just that: public. They can be viewed by anybody,\nlogged in or not. And private pages are only private from non-members of\nthe Site. If someone has joined your Site or is a member of your\norganization, that person can see all the private pages. If you want to\nfurther protect some content, you can modify the permissions on\nindividual pages in either page group so only certain users can view\nthem.\n\nHere's how to create a page only administrators can see:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to your Site's Site Administration dropdown and select \\emph{Site\n  Builder} → \\emph{Pages} → \\emph{Private Pages}.\n\\item\n  Create a page called \\emph{Admin Tips}.\n\\item\n  Click \\emph{Configure} from the Options button dropdown for the page\n  in the left menu.\n\\item\n  Select \\emph{Permissions} from the \\emph{Options} icon\n  (\\includegraphics{./images/icon-options.png}) in the top right corner\n  of the screen.\n\\item\n  Uncheck the \\emph{View} and \\emph{Add Discussion} permissions next to\n  the Site Member role.\n\\item\n  Click the \\emph{Save} button.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-page-permissions.png}\n\\caption{The Permissions offer a plethora of options for each role.}\n\\end{figure}\n\nCongratulations! You've changed the permissions for this page so only\nSite administrators can view it. Any users you add to this Role can now\nsee the page. Other users, even members of this Site, don't have\npermission to see it.\n\nPages are as flexible as pages you'd create manually without Liferay.\nUsing a point and click interface, you can define your Site any way you\nwant. You can create and remove pages, export and import them, set their\nlayouts, define how they are indexed by search engines, and more.\n\n\\chapter{Configuring Widgets}\\label{configuring-widgets}\n\nJust like siblings have common features inherited from their parents,\nwidgets that ship with Liferay DXP also share common features. These\ninclude look and feel, exporting/importing app data, communication,\nsharing, permissions, scoping, and configuration templates. These\nfeatures work together to facilitate information flow within Liferay DXP\nand provide an enhanced experience for your users. You'll start with\nlook and feel configuration options.\n\n\\chapter{Look and Feel Configuration}\\label{look-and-feel-configuration}\n\nTo access the look and feel configuration menu of any widget,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Options} (\\includegraphics{./images/icon-app-options.png})\n  in the top right corner of the widget.\n\\item\n  Select \\emph{Look and Feel Configuration}.\n\\end{enumerate}\n\n\\emph{Look and Feel Configuration} has six tabs:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  General\n\\item\n  Text Styles\n\\item\n  Background Styles\n\\item\n  Border Styles\n\\item\n  Margin and Padding\n\\item\n  Advanced Styling\n\\end{itemize}\n\nAfter making customizations, click \\emph{Save} and refresh your page to\napply your changes. If you don't like the effect of your changes, some\ntabs have a \\emph{Reset} button to discard changes.\n\n\\section{General Settings}\\label{general-settings}\n\nOn the General tab are the following options:\n\n\\textbf{Use Custom Title} enables changes to your widget's title. The\nvalue in the title box is displayed on widget's decorator. The title is\nlocalizable, so you can provide translations of the title for different\nlanguages.\n\n\\textbf{Application Decorators} gives you the choice between three\ndecorators: \\emph{Barebone}, \\emph{Borderless}, and \\emph{Decorate}. The\nDecorate application decorator is the default. Be careful about turning\nwidget borders off; some themes assume widget borders are turned on and\nmay not display correctly with them turned off.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/look-and-feel-portlet-configuration-menu.png}\n\\caption{The General tab of the Look and Feel Configuration menu lets\nyou define a custom widget title and select the widget contrast option\nusing decorators.}\n\\end{figure}\n\n\\section{Text Styles}\\label{text-styles}\n\n\\emph{Text Styles} configures the format of the text that appears in the\nwidget. The options include\n\n\\textbf{Font:} Choose various fonts. You can set the text to bold,\nitalics, or both.\n\n\\textbf{Size:} Set the font size anywhere from 0.1 em to 12 em, with 0.1\nem increments. 1 em is the default.\n\n\\textbf{Color:} Set to any six digit hex color code. Click on the text\nbox to open the color palette.\n\n\\textbf{Alignment:} Set to \\emph{Left}, \\emph{Center}, \\emph{Right}, or\n\\emph{Justified}.\n\n\\textbf{Text Decoration:} Set to \\emph{Underline}, \\emph{Overline}, or\n\\emph{Strikethrough}. The default text decoration is \\emph{None}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/look-and-feel-text-styles.png}\n\\caption{The Text Styles tab lets you configure the format of the text\nthat appears in the widget.}\n\\end{figure}\n\n\\textbf{Word Spacing:} Set from -1 em to 0.95 em, with 0.05 em\nincrements. 0 em is the default.\n\n\\textbf{Line Spacing:} Set from 0 em to 12 em, with 0.1 em increments. 0\nem is the default.\n\n\\textbf{Letter Spacing:} Set from -10 px to 50 px, with 1 px increments.\n0 px is the default.\n\n\\section{Background Styles}\\label{background-styles}\n\nThe Background Styles tab specifies the widget's background color. When\nyou select the text space, you're given a color palette to choose your\nbackground color or you can manually enter any six digit hex color code.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/look-and-feel-background-styles.png}\n\\caption{The Background Styles tab lets you specify the widget's\nbackground color.}\n\\end{figure}\n\n\\section{Border Styles}\\label{border-styles}\n\nThe Border Styles tab, configures your widget's border width, style, and\ncolor. For each of these attributes, leave the \\emph{Same for All}\nselector enabled to apply the same settings to top, right, bottom, and\nleft borders.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/look-and-feel-border-styles.png}\n\\caption{The Border Styles tab lets you specify a border width, style,\nand color for each side of the widget.}\n\\end{figure}\n\nFor border width, you can specify any \\% value, em value, or px value.\nFor border style, you can select Dashed, Double, Dotted, Groove, Hidden,\nInset, Outset, Ridge, or Solid. For border color, you can enter any six\ndigit hex color code, just like for the text color and background color.\nYou can also use the color palette.\n\n\\section{Margin and Padding}\\label{margin-and-padding}\n\nThe Margin and Padding tab specifies margin and padding lengths for the\nedges of your widget. Just like for border styles, leave the \\emph{Same\nfor All} selector enabled to apply the same settings to each side (top,\nright, bottom, and left) of the widget.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/look-and-feel-margin-and-padding.png}\n\\caption{The Margin and Padding tab allows you to specify margin and\npadding lengths for the sides of your widget.}\n\\end{figure}\n\nFor both padding and margin, you can specify any \\% value, em value, or\npx value.\n\n\\section{Advanced Styling}\\label{advanced-styling}\n\nThe Advanced Styling tab displays current information about your widget,\nincluding your widget's Liferay ID and CSS classes.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/look-and-feel-advanced-styling.png}\n\\caption{The Advanced Styling tab displays your widget's Liferay ID and\nallows you to enter CSS code to customize the look and feel of your\nwidget.}\n\\end{figure}\n\nYou can also enter custom CSS class names for your widget and custom CSS\ncode. Clicking the \\emph{Add a CSS rule for just this portlet} or\n\\emph{Add a CSS rule for all portlets like this one} links adds the CSS\ncode shells into your custom CSS text box. If you check the \\emph{Update\nmy styles as I type} box, your CSS code is applied dynamically to your\nwidget so you can see the effects of your edits.\n\nNext, you'll learn about communication between widgets.\n\n\\chapter{Exporting/Importing Widget\nData}\\label{exportingimporting-widget-data}\n\nYou may need to export data from a specific widget instance, without\nregard to content on the rest of the Site. There are many widgets that\nlet you export or import their data individually:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Blogs\n\\item\n  Bookmarks\n\\item\n  Dynamic Data Lists\n\\item\n  Forms\n\\item\n  Knowledge Base\n\\item\n  Message Boards\n\\item\n  Web Content\n\\item\n  Wiki\n\\item\n  And more\n\\end{itemize}\n\nExporting widget data produces a \\texttt{.lar} file that you can save\nand import into another widget of the same type. To import widget data,\nyou must select a \\texttt{.lar} file. Be careful not to confuse\nwidget-specific \\texttt{.lar} files with Site-specific \\texttt{.lar}\nfiles. See the\n\\href{/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content}{Importing/Exporting\nPages and Content} article for information on importing/exporting Site\npage data.\n\nThere are two ways to export/import widget content. You can navigate to\nthe widget's administrative area located in the Product Menu, or you can\nvisit the widget on its page. Both export/import menus work the same,\nbut the administrative area may hold content different from its widget\ncounterpart (e.g., Web Content Admin in Product Menu and Web Content\nDisplay widget do not offer same content for export/import), so be wary\nof your selection.\n\nTo export or import data from the widget's administrative area, follow\nthe steps below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the widget's designated area in the Product Menu. For\n  example, if you plan to export Web Content data, navigate to\n  \\emph{Content} → \\emph{Web Content}.\n\\item\n  Click the \\emph{Options} button\n  (\\includegraphics{./images/icon-options.png}) from the top right of\n  the page and select \\emph{Export/Import}.\n\\item\n  Select the \\emph{Export} or \\emph{Import} tab to begin configuring the\n  respective process.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/admin-app-export-import-feature.png}\n\\caption{You can access a widget's administrative \\emph{Export/Import}\nfeature by selecting its Options menu.}\n\\end{figure}\n\nTo export or import data from a widget, follow the steps below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Ensure the widget you're exporting/importing from is available on a\n  page. You can add widgets from the \\emph{Add}\n  (\\includegraphics{./images/icon-add-app.png}) → \\emph{Widgets} menu.\n\\item\n  Select the widget's \\emph{Options} button\n  (\\includegraphics{./images/icon-app-options.png}) and select\n  \\emph{Export/Import}.\n\\item\n  Select the \\emph{Export} or \\emph{Import} tab to begin configuring the\n  respective process.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/widget-export-import-feature.png}\n\\caption{You can access a widget's \\emph{Export/Import} feature by\nselecting its Options menu.}\n\\end{figure}\n\nNow that you know how to navigate to the \\emph{Export/Import} menus, you\ncan explore the export process.\n\n\\section{Exporting Widget Data}\\label{exporting-widget-data}\n\nTo export widget data, create a new export process by selecting the\n\\emph{New Export Process} tab (default). You have several export options\nto configure.\n\nFirst, you can choose to export your widget's configuration settings.\nThis exports your customized settings from your widget's \\emph{Options}\n→ \\emph{Configuration} menu. For some widgets, the configuration export\nmight also include content. For example, a Web Content Display widget\nthat shows a web content article also exports the article when exported,\neven though no content is selected. This applies when publishing a Web\nContent Display widget too; the configured article is published with the\nwidget.\n\nNext, you can select a \\emph{Date Range} of content that you want to\nexport. Content added to your widget within your specified date range is\nincluded in the \\texttt{.lar} file. The following date range choices are\navailable:\n\n\\textbf{All:} Publishes all content regardless of its creation or last\nmodification date.\n\n\\textbf{Date Range:} Publishes content based on a specified date range.\nYou can set a start and end date/time window. The content created or\nmodified within that window of time is published.\n\n\\textbf{Last\\ldots:} Publishes content based on a set amount of time\nsince the current time. For example, you can set the date range to the\npast 48 hours, starting from the current time.\n\nBy checking the \\emph{Content} box, you can choose specific content you\nwant to export. When you check the \\emph{Content} box, more options\nappear, letting you choose specific kinds of metadata to include. For\nexample, if you have a wiki page with referenced content that you don't\nwant, check the \\emph{Wiki Pages} checkbox and uncheck the\n\\emph{Referenced Content} checkbox. Another option is the selection of\ncontent types. Two familiar content types in your Liferay instance are\n\\emph{Comments} and \\emph{Ratings}. If you want to include these\nentities in your \\texttt{.lar} file, select \\emph{Change} and select\nthem from the checklist. For more information on managing content types,\nsee the\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-content-types-in-staging}{Managing\nContent Types in Staging} article.\n\nNext, you can choose to export individual deletions. This lets delete\noperations performed for content types be exported to the LAR file.\n\nFinally, you can choose whether to include permissions for your exported\ncontent. The permissions assigned for the exported widget window are\nincluded if you enable the \\emph{Export Permissions} selector.\n\nAfter you've exported your widget's data, switch to the \\emph{Current\nand Previous} tab to view ongoing export processes and the history of\npast exports. You can also download the exported \\texttt{.lar} file from\nthis tab.\n\n\\section{Importing Widget Data}\\label{importing-widget-data}\n\nTo import widget data, you can select the LAR using your file explorer\nor by dragging and dropping the file between the dotted lines.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/import-menu.png}\n\\caption{When importing widget data, you can choose a LAR file using the\nfile explorer or drag and drop the file between the dotted lines.}\n\\end{figure}\n\nYour LAR file is uploaded and displayed to you for review. Click\n\\emph{Continue}.\n\nNow that you've uploaded and confirmed your LAR file, you're given a\nsimilar screen to what you'd be offered during export. Several of these\noptions are covered in great detail in the\n\\href{/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content}{Importing/Exporting\nPages and Content} tutorial. There are some additional options\navailable: \\emph{Update Data} and \\emph{Authorship of the Content}.\nHere's options and descriptions for each section:\n\n\\textbf{Update Data}\n\n\\textbf{Mirror:} All data and content inside the imported LAR is newly\ncreated the first time while maintaining a reference to the source.\nSubsequent imports from the same source updates entries instead of\ncreating new entries.\n\n\\textbf{Mirror with overwriting:} Same as the mirror strategy, but if a\ndocument or an image with the same name is found, it is overwritten.\n\n\\textbf{Copy as New:} All data and content inside the imported LAR is\ncreated as new entries within the current Site every time the LAR is\nimported.\n\n\\textbf{Authorship of the Content}\n\n\\textbf{Use the Original Author:} Keep authorship of imported content\nwhenever possible. Use the current user as author if the original one is\nnot found.\n\n\\textbf{Use the Current User as Author:} Assign the current user as the\nauthor of all imported content.\n\nOnce you've selected the appropriate options, select \\emph{Import} and\nyour widget's data is imported and ready for use.\n\n\\chapter{Communication Between Portlet\nWidgets}\\label{communication-between-portlet-widgets}\n\nPortlet widgets can communicate with each other using public render\nparameters and events. Public render parameters are easy to use and can\nbe quite powerful. Some Liferay portlets provide a configuration UI to\nhelp you get the most out of this communication mechanism. To access\nthis UI, open your portlet's configuration window by clicking on the\n\\emph{Options} icon (\\includegraphics{./images/icon-app-options.png})\nand selecting \\emph{Configuration}. Then click on the\n\\emph{Communication} tab.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/app-communication-tab.png}\n\\caption{You can configure portlets to communicate with each other using\npublic render parameters.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If your widget isn't a portlet, this feature isn't\navailable.\n\n\\noindent\\hrulefill\n\nThe screenshot above is for the Wiki, which has six public render\nparameters: \\texttt{categoryId}, \\texttt{nodeId}, \\texttt{nodeName},\n\\texttt{resetCur}, \\texttt{tag}, and \\texttt{title}. For each of these\nparameters, you can configure the portlet to ignore the values coming\nfrom other portlets or read the value from another parameter.\n\nWhy might it be useful to ignore the values for certain parameters that\ncome from other portlets? Consider a common use case for the Wiki\napplication. The Wiki portlet is often used along with the Tags\nNavigation portlet so that when a user clicks on a tag of the latter,\nthe Wiki shows a list of pages with that tag. An administrator may want\nthe Wiki to show the front page always independently of any tag\nnavigation done through other portlets. Ignoring the values of the\nparameter coming from other widgets let this happen.\n\nReading the value of a parameter from another portlet is an advanced but\nvery powerful option that allows portlets to communicate with each other\neven if their developers didn't intend them to. For example, imagine\nthat the Wiki is used to publish information about certain countries,\nand there's another portlet that allows browsing countries for\nadministrative reasons. The second portlet has a public render parameter\ncalled \\emph{country} with the name of the country. You'd like the Wiki\nto show the information from the country that's selected in the\nadministration portlet. This can be achieved by setting the value of the\ntitle parameter of the Wiki portlet to be read from the country\nparameter of the administration portlet. Cool, isn't it?\n\n\\chapter{Sharing Widgets with Other\nSites}\\label{sharing-widgets-with-other-sites}\n\nYou can share widgets with other Sites by embedding an instance of a\nwidget running on your Site into another website, such as Facebook. This\nopens up a whole new avenue of exposure to your web site that you would\nnot have had otherwise. In fact, this is how all those Facebook games\nwork.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/collaboration-app-configuration-sharing.png}\n\\caption{The Sharing tab in your widget's Configuration menu lets you\nshare your widget in a variety of ways.}\n\\end{figure}\n\nTo share one of your widgets, open the \\emph{Configuration} dialog box\nfrom the widget's \\emph{Options} icon\n(\\includegraphics{./images/icon-app-options.png}) and select the\n\\emph{Sharing} tab. There are five sub-tabs under sharing: Any Website,\nFacebook, OpenSocial Gadget, and Netvibes.\n\n\\section{Any Web Site}\\label{any-web-site}\n\nCopy and paste the provided snippet of JavaScript code into the web site\nwhere you want to add the widget. That's all you need to do. When a user\nloads the page on the other website, the code pulls the relevant widget\nfrom your Site and displays it.\n\n\\section{Facebook}\\label{facebook}\n\nYou can add any widget as a Facebook app. To do this, you must first get\na developer key. A link for doing this is provided to you in the\nFacebook tab. You must create the application on Facebook and get the\nAPI key and canvas page URL from Facebook. Once you've done this, you\ncan copy and paste their values into the Facebook tab. Save the\nconfiguration and navigate back to the Facebook tab in Liferay DXP.\nYou're given the Callback URL, which you can copy and paste into\nFacebook. When opening your app in Facebook, the correct callback URL is\nused to render the application. You can also enable the \\emph{Allow\nusers to add {[}application-name{]} to Facebook}. Then you can navigate\nto your app's Options menu and select \\emph{Add to Facebook}.\n\n\\section{OpenSocial Gadget}\\label{opensocial-gadget}\n\nOpenSocial comprises a container and a set of APIs for social networking\nand other web applications. Liferay DXP can serve up applications to be\nused as OpenSocial Gadgets on any OpenSocial-compatible pages.\n\nTo serve a Liferay widget on an OpenSocial platform, copy and paste the\nprovided gadget URL and add it to the appropriate configuration page of\nthe OpenSocial platform you're using. Your Liferay instance serves that\nwidget directly onto that platform's page. The URL provided is unique to\nthe specific instance of the widget, so you could serve multiple\ninstances of the same widget as different OpenSocial Gadgets.\n\nFrom the Sharing tab in the Configuration menu, you can also enable the\nselector \\emph{Allow users to add {[}application-name{]} to an\nOpenSocial platform}. Click \\emph{Save} and revisit the \\emph{Options}\nbutton of your widget. A new button appears named \\emph{Add to an\nOpenSocial Platform}. When selecting this new button, the URL is\nprovided for sharing the widget to an OpenSocial platform.\n\n\\section{Netvibes}\\label{netvibes}\n\nNetvibes offers a similar environment where users can log in, create\ntheir own personal dashboard, and add customizable widgets to it. To set\nup Netvibes support for a widget, enable the \\emph{Allow users to add\n{[}application-name{]} to Netvibes pages} selector. You can then use the\nprovided URL to create a custom Netvibes widget based on the instance of\nthe Liferay widget that you're using.\n\nNext, you'll learn how to set permissions for Liferay applications.\n\n\\chapter{Widget Permissions}\\label{widget-permissions}\n\nAll of Liferay's widgets support Liferay DXP's robust, fine-grained\npermissions system. Some higher level permissions can be configured in\nthe permissions tab of the widget's configuration dialog box. You can\ngrant Roles permission to\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Add a display template\n\\item\n  Add the widget to a page\n\\item\n  Configure the app\n\\item\n  Modify the widget's permissions\n\\item\n  Modify the widget's preferences\n\\item\n  View the widget\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/widget-permissions.png}\n\\caption{Viewing the permissions configuration for a widget.}\n\\end{figure}\n\nTo set these permissions, go to the widget's \\emph{Options} icon\n(\\includegraphics{./images/icon-app-options.png}) and click select\n\\emph{Permissions}. This shows you a table of Roles. Use the check boxes\nto grant certain permissions to different Roles. Click \\emph{Save} after\nyou've made your selections.\n\nBeyond this, specific permissions are generally defined for specific\nwidgets. For example, Message Boards contains a \\emph{Ban User}\npermission. This makes no sense in the context of most other widgets.\nYou'll go over permissions for specific widgets in the sections for\nthose widgets. Next, you'll learn about widget scopes.\n\n\\chapter{Widget Scope}\\label{widget-scope}\n\nAs you learned earlier, Roles can be scoped by the instance, by Site, or\nby an Organization. A Role only takes effect within its scope. For\nexample, a Message Boards Administrator Role with complete access to the\nMessage Boards has different permissions based on the Role's scope. If\nit's a global Role, members have permission to administer message boards\nacross the entire installation. If it's a Site Role, members only have\npermission to administer message boards within the Site where they've\nbeen assigned the Role. For Organizations with Sites, Site Roles are\nautomatically assigned to Organization members based on the Organization\nRoles they have. For an Organization-scoped Message Boards administrator\nRole, members only have permission to administer message boards within\nthe Site of the Organization that assigned the Role to them.\n\nYou've also heard the word \\emph{scope} refer to the data set of a\nwidget. By default, when a widget is added to a page in a Site, it is\n\\emph{scoped} for that Site. This means its data belongs to that Site.\nIf the widget is added to a page in a different Site, it employs a\ncompletely different data set. This enables you to place a Message\nBoards widget in one Site with one set of categories and threads, and\nplace another Message Boards widget in different Site with a different\nset of categories and threads.\n\nScoping by Site means that you can only have one Message Boards widget\nper Site. If you add one Message Boards widget to a page in a Site and\nadd another Message Boards widget to a different page in the same Site,\nthe second Message Boards widget contains exactly the same data as the\nfirst. This is because, by default, the Message Boards widget is scoped\nby Site. Most of Liferay DXP's other widgets also default to being\nscoped by Site.\n\nTo avoid this limitation, many Liferay widgets can be scoped by page.\nThe data sets of page-scoped widget serve a single page, not an entire\nSite. If you set the scope of a widget to \\emph{page} instead of\n\\emph{Site}, you can add any number of these widgets to different pages,\nand then they have different sets of data. Then you can have more than\none message board per Site if you wish. Most widgets, however, default\nto the ``native'' configuration, and have their scopes set to the Site\nwhere they are placed.\n\nUnless otherwise noted, all widgets support scoping by instance\n(global), Site (default), or page. This grants you some flexibility in\nhow you want to set up your Liferay instance. You can configure the\nscope of a widget's content with just a few simple steps.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the widget's \\emph{Options} icon\n  (\\includegraphics{./images/icon-app-options.png}).\n\\item\n  Select \\emph{Configuration}.\n\\item\n  Select the \\emph{Scope} tab.\n\\item\n  Use the drop-down menu to set the scope.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/changing-widget-scope.png}\n\\caption{You can change the scope of your widget's content by navigating\nto its Configuration menu.}\n\\end{figure}\n\nOnce you've created a new scope for widgets, a button\n(\\includegraphics{./images/icon-cog.png}) with a drop-down menu appears\nin the \\emph{Content \\& Data} menu for you to select which scope to\nmanage content for. You can choose the default scope or any new scopes\nyou created for your widgets. Your selection changes the content that\nappears when you manage each type.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/widget-scope-drop-down-menu.png}\n\\caption{Use the drop-down menu under Content \\& Data to determine which\nscope to manage content for.}\n\\end{figure}\n\nThat's all it takes to change the scope for a particular widget. By\nsetting the scope to the current page, you can add as many of these\nwidgets to a Site as you want, provided they are all added to separate\npages.\n\nAnother useful feature of Liferay's widgets is Configuration Templates.\n\n\\chapter{Configuration Templates}\\label{configuration-templates}\n\nOnce you've configured a widget, Configuration Templates can save those\nsettings in a reusable template. If someone goes in and changes the\nsettings of a particular widget, it then becomes easy to revert those\nchanges back to the original configuration template. Configuration\ntemplates are only available for widgets placed on a page. Applications\navailable from the Product Menu do not provide configuration templates.\n\nTo create a configuration template, click the \\emph{Options} icon\n(\\includegraphics{./images/icon-app-options.png}) from the menu in the\nwidget's title bar and select \\emph{Configuration Templates}. If\nwidget's current settings are the ones you want to save, click the\n\\emph{Save Current Configuration as Template} button. If not, change the\nsettings until it's configured the way you want it, and then click the\nbutton.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/configuration-template.png}\n\\caption{Create a configuration template to save your app's\nconfiguration settings.}\n\\end{figure}\n\nThere is only one field to fill out. Enter a name for your template and\nclick \\emph{Save}. You should now see your configuration in the list. If\nyou ever need to revert the app to these archived settings, you can\nclick \\emph{Actions} (\\includegraphics{./images/icon-actions.png}) →\n\\emph{Apply} next to the configuration template you want to apply.\n\nUnless otherwise noted, all widgets in Liferay DXP support this feature.\nThis is particularly useful for widgets that have a lot of configuration\noptions, such as the Message Boards application.\n\n\\section{Summary}\\label{summary}\n\nYou've now explored the configuration options available for Liferay\nwidgets. You learned how to customize your widgets, export/import data,\ncommunicate between widgets, take advantage of different scopes, and\nsave configuration settings. You also examined the different uses of\nsocial applications like Facebook and Netvibes for your Liferay widgets.\nIn all, Liferay DXP gives you an abundance of options to leverage the\nfull capability of your widgets.\n\n\\chapter{Managing Members in Your\nSite}\\label{managing-members-in-your-site}\n\nUsers and Sites are important concepts. Sites are where all your content\nand pages are stored, and Users access and create that content. While\nuser management is covered in depth in our\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-users}{User Management\ntutorial}, there are other user configuration options specific to Site\nManagement:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Adding members to Sites administratively\n\\item\n  Adding members to Sites automatically\n\\item\n  Creating Teams of Site members for various functions\n\\end{itemize}\n\nThis section of tutorials shows you how to manage and configure these\nSite options for Users.\n\n\\chapter{Adding Members to Sites}\\label{adding-members-to-sites}\n\nIn \\href{/docs/7-1/user/-/knowledge_base/u/adding-sites}{Adding Sites}\nyou learned the difference between Site Membership Types and about\npublic and private pages within a Site. Now you'll learn how to add\nusers manually to Sites and how to provide options for self management.\nFor review, there are a few key reasons why Site Membership management\nis important:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Only Site Members can view a Site's Private Pages.\n\\item\n  Site Members have more permissions than guests for many widgets like\n  Message Boards and Wikis that enable them to create content and\n  collaborate on your Site.\n\\item\n  Site Members can be associated with Roles that grant Site privileges.\n\\end{enumerate}\n\n\\section{Administrating Site\nMembership}\\label{administrating-site-membership}\n\nAdministrators can manage Site members from that Site's \\emph{Site\nMembership} page.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open \\emph{Site Administration} and select the Site that you want to\n  manage members for.\n\\item\n  Click on \\emph{People} → \\emph{Memberships}\n\\end{enumerate}\n\nFrom here you can manage Site Memberships, Organization, and User Group\nassociations. You can learn more about those in the\n\\href{/docs/7-2/user/-/knowledge_base/u/users-and-organizations}{Users\nand Organizations tutorial}. Here you see a list of all of the current\nUsers of the Site and you can add or remove user memberships from the\nSite.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/orgs-add-organization-site.png}\n\\caption{The current members of the Site as displayed on the \\emph{Site\nMemberships} page.}\n\\end{figure}\n\n\\section{Adding Members to a Site}\\label{adding-members-to-a-site}\n\nFollow these steps to make an existing user a member of the Site:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{New} (\\includegraphics{./images/icon-add.png}) button\n  in the top right of the screen.\n\\item\n  Use \\emph{Filter and Order} or the \\emph{Search} function to locate\n  the User you want to add to the Site.\n\\item\n  Select the User(s) you wish and click \\emph{Done}.\n\\end{enumerate}\n\nOn the \\emph{Assign Users to This Site} screen, all Users eligible to be\nadded to the Site appear. Deactivated Users do not appear. Site members\nalso appear, but with a greyed-out checkbox.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/assign-users.png}\n\\caption{The list of users available to add to the current Site. Note\nthat the current members are visible but cannot be added or removed\nhere.}\n\\end{figure}\n\n\\section{Removing User Membership from a\nSite}\\label{removing-user-membership-from-a-site}\n\nThere are two ways to remove a user from a Site. You can remove an\nindividual member like this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Actions} (\\includegraphics{./images/icon-actions.png})\n  icon for the user that you want to remove.\n\\item\n  Select \\emph{Remove Membership}.\n\\item\n  In the pop-up that appears, confirm the removal.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/remove-user.png}\n\\caption{Selecting to remove a user.}\n\\end{figure}\n\nTo remove several users at once, you can do this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the checkbox for each user that you want to remove.\n\\item\n  In the menu at the top of the page, click the \\texttt{X} icon to\n  remove the users from the Site.\n\\item\n  In the pop-up that appears, confirm the removal.\n\\end{enumerate}\n\nRemoved Users lose access to the Site's private pages and membership in\nany Site Roles or Teams they had.\n\n\\section{Assigning Site Roles}\\label{assigning-site-roles}\n\nRoles grant permissions in Liferay DXP. Roles can be assigned for the\nentire instance or just for one specific Site or Organization. Site\nRoles assign permissions for a specific Site.\n\nYou can use the same interface options that you used to remove Users\nfrom the Site to assign them to Site Roles. If you select a User or\nUsers and click \\emph{Assign Site Roles} (either through the Actions\nmenu or the menu at the top), you are taken to the \\emph{Assign Site\nRoles} screen. From here:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select the Roles that you want to assign to the selected users.\n\\item\n  Click \\emph{Done}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/assigning-site-roles.png}\n\\caption{Assigning Site Roles.}\n\\end{figure}\n\nSite Roles are created at a global level, but when they're assigned they\nonly provide privileges for the specific Site where they were assigned.\nSince Roles are created at a global level, they cannot be created by\nSite Administrators (since Site Administrators only have Administrator\nprivileges for their Site). \\textbf{Teams} allow Site Administrators to\nassign permissions to groups of Users within their Sites. Next, you'll\nlook at more configuration for managing members of your Site.\n\n\\chapter{Creating Teams to Empower Site\nMembers}\\label{creating-teams-to-empower-site-members}\n\nIf you have an \\emph{ad hoc} group of Users who perform the same set of\ntasks in a Site, you can organize them into Site Teams, and then assign\nthe team permissions for various site-specific functions. Site Teams are\nthe preferred method for collecting permissions within a single Site.\nSome common functions to assign a Site Team include\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Moderating site Wiki content\n\\item\n  Managing Message Boards threads\n\\item\n  Writing blogs\n\\item\n  Editing a specific page in the site\n\\end{itemize}\n\nIf your Site has Message Boards, you might want to enable a subset of\nthe Site's members to moderate the categories and threads, and perhaps\nto ban abusive/offensive posters. To do this, you could create a Site\nTeam named \\emph{Lunar Resort Message Board Moderators}, define the\nteam's permissions in the Message Boards application, and assign the\ndesired Site members to the team.\n\nThe permissions assigned to a Site Team only apply to that Site. The two\nkey features of Teams are that they are limited to their Sites and that\nthey empower Site Administrators to manage permissions for their Sites\nsince Site Administrators cannot create new Roles.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To create and apply permissions for a group of users to\nuse across multiple Sites or Organizations in your Liferay instance,\nconsider aggregating the Users into a\n\\href{/docs/7-2/user/-/knowledge_base/u/user-groups}{User Group} and\nassigning the User Group permissions via\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{Roles}.\n\n\\noindent\\hrulefill\n\nTo create a team within a Site,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the Site Administration page of your Site.\n\\item\n  Select \\emph{People} → \\emph{Teams}.\n\n  It's important to note that configuring other Site membership\n  groupings, such as \\emph{Users}, \\emph{Organizations}, and \\emph{User\n  Groups} can be done in the \\emph{Site Memberships} app, which is also\n  in the Members tab. You can visit\n  \\href{/docs/7-2/user/-/knowledge_base/u/managing-users}{User\n  Management} for more information on how Site memberships work.\n\\item\n  Finally, click the \\emph{Add Team} icon\n  (\\includegraphics{./images/icon-add.png}).\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/creating-a-team.png}\n  \\caption{Creating teams within your site can foster teamwork and\n  collaboration, as team permissions enable team members to access the\n  same resources and perform the same types of tasks.}\n  \\end{figure}\n\\item\n  Enter a name and a description and click \\emph{Save}. Your new team\n  shows in the list.\n\\item\n  To add members, click on the team name link and then select \\emph{Add\n  Team Members}.\n\\end{enumerate}\n\nTo manage a team's permissions, click the \\emph{Actions} icon\n(\\includegraphics{./images/icon-actions.png}) and select\n\\emph{Permissions} for that team. Setting permissions for the team\nassigns those permissions to all the team's members. Only administrators\nwho can edit/manage the team can manage team permissions.\n\nIf you created a team whose task is to moderate the Message Boards, for\nexample, you'd want to give the team all the permissions they'd need.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Content \\& Data} →\n  \\emph{Message Boards}.\n\\item\n  Select \\emph{Home Category Permissions} from the \\emph{Options} icon\n  (\\includegraphics{./images/icon-options.png}) in the top right of the\n  screen.\n\\item\n  Find the Team in the Role column and select the appropriate\n  permissions.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/site-team-permissions-message-boards.png}\n\\caption{The Lunar Resort Message Board Moderators Site Team has\nunlimited permissions on the Message Boards application.}\n\\end{figure}\n\nThat's it! It's easy to give groups of Site Users permissions to perform\ntheir tasks.\n\nThese tutorials have introduced you to Liferay DXP Site management.\nYou've learned how to use Liferay DXP to create multiple Sites with\ndifferent membership types. You've also seen how easy it is to create\nand manage Sites and to create and manage pages within Sites. Next,\nyou'll begin working with web content.\n\n\\chapter{Managing Content}\\label{managing-content}\n\nThere are two primary ways to manage and display content in Liferay DXP\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Web Content\n\\item\n  Content Sets\n\\end{itemize}\n\nThe Web Content framework helps users who are not web developers publish\ncontent with a simple point and click interface, while enabling\ndevelopers to create complex templates with dynamic elements.\n\nA Content Set defines a list of content, and then that list can be\ndisplayed.\n\nContinue on to learn more about managing content!\n\n\\chapter{Managing Web Content}\\label{managing-web-content}\n\nWeb Content Management (WCM) helps users who are not web developers\npublish content with a simple point and click interface, while enabling\ndevelopers to create complex templates with dynamic elements. Once these\ntemplates have been deployed into Liferay DXP, your non-technical users\ncan use them to manage complex content as easily as they would manage\nbasic content.\n\nIt has these components:\n\n\\textbf{Web Content Editor:} A complete HTML editor for modifying fonts,\nadding color, inserting images, and much more.\n\n\\textbf{Structure Editor:} Define fields for structured content and more\nadvanced designs.\n\n\\textbf{Template Editor:} Import template script files or create your\nown template to inform the system how to display the content within the\nfields determined by the structure.\n\n\\textbf{Web Content Display:} Place web content on pages in your Site.\n\n\\textbf{Asset Publisher:} Aggregate and display different types of\ncontent together in one view. This is covered in more detail in\n\\href{/docs/7-2/user/-/knowledge_base/u/publishing-assets}{Publishing\nAssets}.\n\n\\textbf{Scheduler:} Schedule when content is reviewed, displayed or\nremoved. This is covered in more detail in\n\\href{/docs/7-2/user/-/knowledge_base/u/scheduling-web-content-publication}{Scheduling\nWeb Content Publication}.\n\n\\textbf{Workflow Integration:} Run your content through a review\nprocess. This is covered in more detail in the\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow}{Workflow} section.\n\n\\textbf{Staging:} Use a separate staging server or stage your content\nlocally so you can keep your changes separate from the live site. This\nis covered in more detail in the\n\\href{/docs/7-2/user/-/knowledge_base/u/staging}{Staging} section.\n\nThese tools streamline the content creation process for end users and\nare also integrated with Liferay's services so advanced template\ndevelopers can use them to query for data stored elsewhere on your\nwebsite.\n\nTo demonstrate Liferay DXP's Web Content Management features, you'll\ncreate and manage content on Liferay for the ambitious (and fictitious)\n\\emph{Lunar Resort} project. The Lunar Resort project specializes in\nfacilitating lunar vacations. It provides space shuttle transportation\nfrom the Earth to the Moon and back, offers the use of a\nstate-of-the-art recreational facility enclosed by a large, transparent\nhabitat dome, and even rents out lunar rovers.\n\n\\chapter{Publishing Basic Web\nContent}\\label{publishing-basic-web-content}\n\nWeb Content is one of many different kinds of assets, along with blog\nposts, wiki articles, message board posts, and other kinds of content.\nLike all of these assets, Liferay DXP handles Web Content using an asset\nframework that includes categories, tags, comments, ratings and more.\nPlease see\n\\href{/docs/7-2/user/-/knowledge_base/u/publishing-content-dynamically}{Publishing\nContent Dynamically} for more information on Liferay's asset framework.\n\nTo start working with Web Content, publish some basic material using Web\nContent Management's WYSIWYG editor. Then you can cover the editor's\nfeatures in greater depth and learn how to publish the content you\ncreate to a page.\n\n\\chapter{Creating Web Content}\\label{creating-web-content}\n\nTo start you'll create and publish some simple content using the WYSIWYG\neditor to the home page of the Lunar Resort's web site.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} and click the \\emph{Site Selector}\n  button (\\includegraphics{./images/icon-compass.png}).\n\n  Content is created in whichever Site is selected, so always make sure\n  that you're working on the right Site. Content can also be created as\n  Global so that any Site can access it.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/site-page-scopes.png}\n  \\caption{You can choose where to create content by navigating to the\n  Site Administration menu and selecting your Site and page scope.}\n  \\end{figure}\n\\item\n  Select the \\emph{Lunar Resort} Site.\n\\item\n  Open the \\emph{Content \\& Data} section and click on \\emph{Web\n  Content}.\n\n  Here you can create web content and organize it into folders.\n\\item\n  Click \\emph{Add} (\\includegraphics{./images/icon-add.png}) →\n  \\emph{Basic Web Content} to create a new web content article.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/web-content-add-menu.png}\n  \\caption{By default, \\emph{Basic Web Content} is the only article type\n  available. The next tutorial covers how to create new types.}\n  \\end{figure}\n\\item\n  Type \\emph{Welcome to the Lunar Resort} in the top \\emph{Title} field.\n\\item\n  In the \\emph{Summary} field under the Basic Information tab on the\n  right side, give a short description of the Lunar Resort's facilities\n  (be creative).\n\\item\n  In the \\emph{Content} field, add the body of your web content article,\n  which you'll dive into next.\n\\item\n  Click \\emph{Publish}.\n\\end{enumerate}\n\nThat's all it takes to create Web Content, but there's much more under\nthe hood of the web content editor that you'll learn about next.\n\n\\chapter{Using the Web Content\nEditor}\\label{using-the-web-content-editor}\n\nIn the previous article, you created a simple web content article, but\nthe Web Content editor can do much more than make plain text articles.\n\n\\section{Basic Editor Functions}\\label{basic-editor-functions}\n\nTo explore these options, go back to the article you just created and\nmake it better:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  On the \\emph{Web Content} page in \\emph{Site Administration}, click on\n  the title of the article.\n\\item\n  Highlight the text you entered in the \\emph{Content} field.\n\n  A number of controls appear. These let you style the text or provide a\n  link.\n\\item\n  Click on the arrows where it says \\emph{Normal} to open the\n  \\emph{Styles} dropdown and select the \\emph{Heading 1} style.\n\\end{enumerate}\n\nNext, you'll add an image to the article. Whenever you place your cursor\nin the \\emph{Content} area, the \\emph{Add} icon (\n\\includegraphics{./images/icon-wysiwyg-add.png}) appears. If you click\non it, controls for inserting an image, video, table, or horizontal line\n(\\includegraphics{./images/icon-content-insert-controls.png}) appear.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Add} (\\includegraphics{./images/icon-wysiwyg-add.png}).\n\\item\n  Select the icon that depicts a mountain silhouette to insert the\n  image.\n\\item\n  In the image file selector, select an image to add to the article.\n\n  Select an image from your computer or from the Site's Documents and\n  Media repository. If you select one from your Documents and Media\n  repository, you can access the\n  \\href{/docs/7-2/user/-/knowledge_base/u/editing-images}{image editor}\n  to make changes specifically for your article.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/image-editor-preview-window.png}\n\\caption{You can access the image editor through the item selector\nwindow.}\n\\end{figure}\n\nAfter adding an image to the web content article, click it to bring up\ncontrols (\\includegraphics{./images/icon-wysiwyg-image-controls.png})\nfor formatting it. You can also make it a link.\n\nThe same way you inserted an image in to the article, you can also\ninsert a table. Click the table to access edit controls, which let you\ndesignate the first row and/or column as table headers, and also enable\nyou to add rows, columns, and cells.\n\nIn addition to images and tables, you can insert a horizontal line as a\nseparator between between sections. You can also add video by providing\na URL.\n\n\\section{Editing the Article Source}\\label{editing-the-article-source}\n\nIf you need to work directly with the HTML, you can switch to source\nview.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  With your cursor in the \\emph{Content} field, select the \\emph{Source}\n  icon ( \\includegraphics{./images/icon-wysiwyg-source.png}) to switch.\n\\item\n  Click the regular mode icon ( \\includegraphics{./images/icon-text.png}\n  ) to go back once you're done editing HTML.\n\\end{enumerate}\n\nThe HTML editor highlights syntax, and you can switch between a dark and\nlight theme by choosing the moon and sun icons.\n\nIn HTML mode, click on the \\emph{Fullscreen} icon\n(\\includegraphics{./images/icon-enlarge.png}) to access a dual pane view\nthat shows your HTML code on the left and a preview pane on the right.\nYou can arrange the HTML and preview panes horizontally or vertically.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-editor-html.png}\n\\caption{You can view how your HTML would render by using the preview\npane.}\n\\end{figure}\n\nYou can exit the enlarged editor by clicking the \\emph{Done} button at\nthe bottom of the screen.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a few short sentences announcing the grand opening of the Lunar\n  Resort.\n\\item\n  Click \\emph{Save as Draft}.\n\n  Be sure to save your content frequently, because it is not auto-saved.\n\\end{enumerate}\n\nThe content can be localized in whatever language you want. You'll learn\nmore about localizing your content later.\n\n\\section{Web Content Options}\\label{web-content-options}\n\nThe right-side menu of the New Web Content form provides options for\ncustomizing your web content. It's organized into two tabs:\n\\emph{Properties} (basic configuration properties) and \\emph{Usages}\n(Where the web content is used on the site). Note that the \\emph{Usages}\ntab is only visible if you're editing existing web content that's been\nadded to a page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wcm-menu.png}\n\\caption{New web content can be customized in various ways using the\nmenu located to the right of the editor.}\n\\end{figure}\n\nThe available properties are listed below:\n\n\\textbf{Basic Information:} Provide a summary for the web content\narticle.\n\n\\textbf{Default Template:} Customize the web content article's template\nif it has one. To learn more about web content templates, see\n\\href{/docs/7-2/user/-/knowledge_base/u/designing-uniform-content}{Designing\nUniform Content}.\n\n\\textbf{Display Page Template:} Select a display page template to\nenhance the styling and formatting of your web content.\n\nFor example, if you had a news site with different sections---Sports,\nTechnology, Culture---you could create a display page for each section\nwith unique banners, formatting, embedded widgets, or other features. By\nselecting a display page, you would ensure that content appears on the\npage with the appropriate features. You'll work through an example of\ncreating a display page in the\n\\href{/docs/7-2/user/-/knowledge_base/u/display-pages-for-web-content}{Display\nPages for Web Content} tutorial.\n\nIf a display page template is configured, you can preview what it will\nlook like with the \\emph{Preview}\n(\\includegraphics{./images/icon-preview.png}) button located next to the\nselected display page template.\n\n\\textbf{Featured Image:} Set the image that is used for the web content\narticle's previews. You can set this image from a URL or your computer.\nIf you don't want a feature image, choose \\emph{No Image}.\n\n\\textbf{Metadata:} Organize web content articles by selecting tags,\ncategories, and priority. To learn more about tags and categories, see\n\\href{/docs/7-2/user/-/knowledge_base/u/organizing-content-with-tags-and-categories}{Organizing\nContent with Tags and Categories}.\n\n\\textbf{Friendly URL:} Set the friendly URL where the article can be\nviewed alone. If a specific display page is set, the URL links to it.\n\n\\textbf{Schedule:} Customize the date and time your content publishes\nand/or expires. To learn more about scheduling content, see\n\\href{/docs/7-2/user/-/knowledge_base/u/scheduling-web-content-publication}{Scheduling\nWeb Content Publication}.\n\n\\textbf{Search:} Disabling search for your article removes it from end\nusers' search results. Administrators can still search for it from\n\\emph{Site Administration} → \\emph{Content \\& Data} → \\emph{Web\nContent}, and the article can still be added to pages.\n\n\\textbf{Related Assets:} Determine relationships between the web content\narticle and other assets, even if they don't share any tags and aren't\nin the same category. You can connect your content to any asset that\nimplements the Related Assets feature. To learn more about defining\ncontent relationships and publishing links to those related assets, see\n\\href{/docs/7-2/user/-/knowledge_base/u/defining-content-relationships}{Defining\nContent Relationships}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/related-assets-link.png}\n\\caption{This blog entry has links to two Related Assets: an article and\na message board thread.}\n\\end{figure}\n\n\\textbf{Permissions:} Customize who has access to the content. By\ndefault, content is viewable by Anyone (Guest Role). You can limit\nviewable permissions by selecting any Role from the drop-down or in the\nlist. You can customize permissions in more detail by selecting the\n\\emph{More Options} link below the drop down button.\n\nIf your permissions are ignored, you must activate the Web Content\nArticle permissions in your System Configuration. This is enabled by\ndefault:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings}.\n\\item\n  Search or browse for \\emph{Web Content}.\n\\item\n  Check the box labeled \\emph{Article View Permissions Check Enabled}\n  under the \\emph{Virtual Instance Scope} → \\emph{Web Content} tab.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nOnce it is activated, any permissions you set in the article's\nconfiguration are checked before displaying the article.\n\n\\chapter{Publishing Web Content}\\label{publishing-web-content}\n\nIn the previous sections, you created and edited an article. Now it's\ntime to publish it.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\emph{Welcome} page of the Lunar Resort Site.\n\\item\n  Select the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  from the top Control Menu and select the \\emph{Widgets} tab.\n\\item\n  Find \\emph{Web Content Display} and add it to the page.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/add-web-content-display.png}\n\\caption{Add the Web Content Display app to a page to begin displaying\nyour new web content article.}\n\\end{figure}\n\nYou can drag a widget to the position on the page where you want your\ncontent to appear. You can have as many Web Content Display widgets on a\npage as you need, which gives you the power to lay out your content\nexactly the way you want it.\n\nNow select the content to display:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Select Web Content to make it visible} in the bottom of\n  the widget.\n\\item\n  Click \\emph{Select} under \\emph{Web Content}.\n\\item\n  Click on the article that you want to display\n\n  If your content does not immediately appear in the list, you can\n  search for the content by title, description, user name, or Site\n  (click the drop-down arrow to see all the options).\n\\end{enumerate}\n\nSelecting a web content article displays the Web Content Display's\nconfiguration page, where you can choose the User Tools and Content\nMetadata to be published in the widget. These two entities have the\nfollowing options to choose from, by default:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{User Tools}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\emph{Translations}\n  \\item\n    \\emph{Print}\n  \\end{itemize}\n\\item\n  \\textbf{Content Metadata}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\emph{Related Assets}\n  \\item\n    \\emph{Ratings}\n  \\item\n    \\emph{Comments}\n  \\item\n    \\emph{Comment Ratings}\n  \\end{itemize}\n\\end{itemize}\n\nIf you have enabled OpenOffice/LibreOffice integration, you can also\nenable document conversion for your content. Then users can download\nyour content in their format of choice. To enable OpenOffice/LibreOffice\nintegration, go to \\emph{Control Panel} → \\emph{Configuration} →\n\\emph{System Settings} → \\emph{Connectors} and check the \\emph{Server\nEnabled} box. Back in the Web Content Display's configuration page,\nconversion options are available under the \\emph{User Tools} list.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-choosing-web-content.png}\n\\caption{Publishing web content is a snap. At a minimum, you only have\nto select the content you wish to publish. You can also enable lots of\noptional features to let your users interact with your content.}\n\\end{figure}\n\n\\textbf{Translations:} Shows the available locales for your content. If\nyou're working on the page for a particular language, you can select the\ntranslation of your content that goes with your locale.\n\n\\textbf{Print:} Opens the content in a separate browser window with just\nthe content---no navigation or other widgets.\n\nBy default, guests cannot leave comments on web content. If you want to\nallow guests to comment on your web content article,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Control Panel → \\emph{Users} → \\emph{Roles}\n\\item\n  Select \\emph{Guest} → \\emph{Define Permissions}.\n\\item\n  From the left menu, select \\emph{Site Administration} → \\emph{Content\n  \\& Data} → \\emph{Web Content}.\n\\item\n  Navigate down to the Web Content Article heading and select the\n  \\emph{Add Discussion} checkbox. Click \\emph{Save}.\n\\end{enumerate}\n\nGuests can now post comments on your web content article!\n\nYou may decide you want one, some, or none of these features, which is\nwhy they're all implemented as simple selector buttons to be enabled or\ndisabled at need. Once you've selected the features you want to include\nin your Web Content Display widget, click \\emph{Save} and close the\nconfiguration window.\n\n\\section{Editing Published Content}\\label{editing-published-content}\n\nIf you must edit published content, you can do it directly from the Web\nContent Display app or from Site Administration. To edit it from the Web\nContent Display app,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select the \\emph{Options} button\n  (\\includegraphics{./images/icon-app-options.png}) from the widget's\n  top panel.\n\\item\n  Select \\emph{Edit Web Content} to launch the editor. Select \\emph{Edit\n  Template} to launch the template editor for the web content article's\n  template if it has one.\n\\end{enumerate}\n\nIf you edit the article from Site Administration, you can also view the\narticle's history and use the diff tool to compare versions.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Content \\& Data} → \\emph{Web Content} from the Product\n  Menu.\n\\item\n  Next to the article, click \\emph{Actions} icon\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{View\n  History}. This shows you\n\\end{enumerate}\n\nThis shows you all the article's versions and modified/display dates.\nThe diff tool compares these versions and highlights the differences\nbetween them.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Actions} next to a version of the article you'd like to\n  compare.\n\\item\n  Select \\emph{Compare to\\ldots{}}.\n\\item\n  Select the version with which to compare it.\n\n  The tool provides color coded highlighting to emphasize additions and\n  deletions between the two articles.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-diff-feature.png}\n\\caption{Comparing web content articles is a great feature to use during\nthe Workflow process.}\n\\end{figure}\n\nWhenever you publish updates to a web content article that's already\nbeing displayed, the content is immediately updated unless you have a\nworkflow enabled (see\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow}{Workflow} for\ndetails).\n\n\\chapter{Other Content Options}\\label{other-content-options}\n\nHere are some options and tools that you can use to enhance your content\nand user experience.\n\n\\section{Localizing Content}\\label{localizing-content}\n\nWhen you create a new web content article, you can choose a default\nlanguage. First, you must change the system configuration to enable the\noption to change the default language.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings}.\n\\item\n  Locate \\emph{Web Content} → \\emph{Administration} by scrolling or\n  using the search bar.\n\\item\n  Check the box labeled \\emph{Changeable Default Language}.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nYou must now add translations for any languages you need. Adding\ntranslations works like this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open a web content article.\n\\item\n  Click the flag icon with a country code on it next to any localizable\n  web content field.\n\\item\n  Select a language from the list.\n\\end{enumerate}\n\nWhen you select a language, all fields in the article switch to the new\nlanguage. To create the new translation, fill in the fields in the\nselected language and publish the article.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-translation.png}\n\\caption{Adding a translation to an article works like adding the\ndefault translation.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To view localizable fields in a given language, you must\nhave your Portal set to that language. This includes friendly URLs for\nthe web content as well. When you navigate to the localized friendly URL\n(e.g.~\\texttt{http://localhost:8080/web/guest/-/espanol}), the web\ncontent is always displayed in the current language. You can change the\nlanguage with the\n\\href{/docs/7-2/user/-/knowledge_base/u/personalizing-pages\\#customization-example}{Language\nSelector app}.\n\n\\noindent\\hrulefill\n\nYou can modify the language translation list by inserting\n\\texttt{locales.enabled=} followed by your preferred languages in your\n\\texttt{portal-ext.properties} file. For example,\n\\texttt{locales.enabled=ar\\_SA,nl\\_NL,hi\\_IN} offers \\emph{Arabic (Saudi\nArabia)}, \\emph{Dutch (Netherlands)}, and \\emph{Hindi (India)}.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} If you switch your Site's default language (e.g., via\nfriendly URL), but do not have the necessary translations for\nlocalizable fields, your Site's language values are used from the old\ndefault language. Therefore, you should change the default language of\nyour Site \\emph{only} when you have translated values for all\nlocalizable entities.\n\n\\noindent\\hrulefill\n\nWhen you create a new web content structure, each field you create has a\n\\emph{Localizable} checkbox displayed next to it. This enables you to\ncontrol what can and can't be changed in the translation process. For\nexample, if you don't want images or content titles to be changed when\nthe content is translated, you can make sure those fields aren't\nlocalizable. When you follow the steps above to localize content, only\nfields within the structure that had the \\emph{Localizable} box checked\nappear within the translation window.\n\n\\section{Xuggler for Embedding Video}\\label{xuggler-for-embedding-video}\n\nXuggler is a tool which generates video previews and makes it possible\nto embed videos from your Documents and Media library in web content and\nelsewhere on the site. To enable Xuggler,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the \\emph{Control Panel}.\n\\item\n  Click on \\emph{Configuration} → \\emph{Server Administration} →\n  \\emph{External Services}.\n\\item\n  Scroll to the bottom and click \\emph{Install} in the \\emph{Xuggler}\n  section.\n\n  This downloads the necessary libraries and prompts you to restart the\n  server to enable Xuggler.\n\\item\n  After the server restarts, you can enable Xuggler from the same page.\n\\end{enumerate}\n\nOnce Xuggler has been installed and enabled, you can embed a video or\naudio file in a web content article the same way you added images\npreviously.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-audio-video.png}\n\\caption{If you've installed and enabled Xuggler from the \\emph{Server\nAdministration} → \\emph{External Tools} section of the Control Panel,\nyou can add audio and video to your web content!}\n\\end{figure}\n\n\\section{XML Format Downloads}\\label{xml-format-downloads}\n\nTools like the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/importing-resources-with-a-theme}{Resource\nImporter} and Site Initiators can be deployed to build a site almost\ninstantly. Before you can use them to import Web Content, however, you\nfirst need to have the content exported individually in XML format. To\nexport the content,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Content \\& Data} → \\emph{Web\n  Content}.\n\\item\n  Start editing the article you want to download.\n\\item\n  Click the \\emph{Options} icon\n  (\\includegraphics{./images/icon-options.png}) in the top right of the\n  page and select \\emph{View Source}.\n\\end{enumerate}\n\nThis displays the raw XML source of the article. You can copy this\ncontent to save into an XML file locally.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-download.png}\n\\caption{The \\emph{View Source} button is available from the\n\\emph{Options} button.}\n\\end{figure}\n\n\\section{Subscribing to Content}\\label{subscribing-to-content}\n\nAn administrator or web content writer can subscribe to an article or\nfolder to follow changes being made to it.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Content \\& Data} → \\emph{Web Content} for your Site.\n\\item\n  Click \\emph{Actions} (\\includegraphics{./images/icon-app-options.png})\n  → \\emph{Subscribe} next to the article or folder you want to follow.\n\\end{enumerate}\n\nAnytime an asset that you follow is modified, you receive an email\nnotifying you of the change.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-subscribe.png}\n\\caption{Click the Subscribe icon in the web content entity's\n\\emph{Options} menu to begin receiving web content notifications.}\n\\end{figure}\n\nThat's pretty much all there is to basic content creation. Whole sites\nhave been created this way. But if you want to take advantage of the\nfull power of Liferay DXP's WCM, you'll want to use structures and\ntemplates or Fragments. You'll cover these topics next.\n\n\\section{Organizing Structure Names}\\label{organizing-structure-names}\n\nBy default, when you select a structure to add a new Web Content\narticle, the structures are ordered by their IDs, not their names. This\ncan be confusing, but---never fear---there's a configuration property to\nsort them alphabetically.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-default-order.png}\n\\caption{The default ordering for Web Content Structures can yield\nconfusing results.}\n\\end{figure}\n\nTo enable this property for Site Administration,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Configuration} → \\emph{System Settings} → \\emph{Web\n  Content} → \\emph{Web Content Administration}.\n\\item\n  Check the box labeled \\emph{Journal Browse by Structures Sorted by\n  Name}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-admin-alphabetical.png}\n\\caption{Web Content Administration will now display structures in\nalphabetical order.}\n\\end{figure}\n\nYou can also set this property for the Web Content Display widget. To\nenable this property for the Web Content Display,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Configuration} → \\emph{System Settings} → \\emph{Web\n  Content} → \\emph{Web Content Display}.\n\\item\n  Check the box labeled \\emph{Sort Structures by Name}.\n\\end{enumerate}\n\nAfter this option is checked, the structures are sorted alphabetically.\nNote that enabling this property can degrade performance with large\nstructure libraries.\n\n\\chapter{Designing Structured\nContent}\\label{designing-structured-content}\n\nIf you've ever launched a web site, you know that as it grows, you can\nexperience growing pains. This is the case especially if you've given\nlots of people access to the site to make whatever changes they need to\nmake. Without preset limitations, users can display content in any order\nand in any manner they desire (think huge, flashing letters in a font\nnobody can read). Content can get stale, especially if those responsible\nfor it don't maintain it like they should. And sometimes, content is\npublished that should never have seen the light of day.\n\nThankfully, Web Content Management helps you handle all of these\nsituations. You can use \\emph{Structures} to define which fields are\navailable to users when they create content. These can be coupled with\n\\emph{Templates} that define how to display that content. Content won't\nget stale, because you can take advantage of the\n\\href{/docs/7-2/user/-/knowledge_base/u/scheduling-web-content-publication}{Scheduling}\nfeature to determine when content is displayed and when it's removed.\nAdditionally, you can configure Liferay DXP's built-in\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow}{Workflow} system to\nset up a review and publishing process so only what you want winds up on\nthe live site. This gives you what you need to run everything from a\nsimple, one-page web site to an enormous, content-rich site.\n\nAll of this starts with structures.\n\n\\chapter{Creating Structured Web\nContent}\\label{creating-structured-web-content}\n\nStructures are the foundation for web content. They determine which\nfields are available to users as they create new items for display.\nStructures not only improve manageability for the administrator, they\nalso make it much easier for users to add content quickly.\n\nFor example, say you're managing an online news magazine. All your\narticles must contain the same types of information: a title, a\nsubtitle, an author, and one or more pages of text and images that\ncomprise the body of the article. With only basic content creation,\nyou'd have no way to make sure your users entered a title, subtitle, and\nauthor. You might also get articles that don't match the look and feel\nof your site. If titles are supposed to be navy blue but they come in\nfrom your writers manually set to light blue, you must spend time\nreformatting them before they are published.\n\nStructures enforce a format for your content so your writers know\nexactly what a complete article or mapped display page needs. Using\nstructures, you can provide a form for your users which spells out\nexactly what is required and can be formatted automatically using a\ntemplate or a\n\\href{/docs/7-2/user/-/knowledge_base/u/display-pages-for-web-content}{display\npage}.\n\nYou create a structure by adding form controls such as text fields, text\nboxes, HTML text areas, check boxes, select boxes and multi-selection\nlists. You can add specialized application fields such as an Image\nUploader or Documents and Media right onto the structure. Positioning\nelements is accomplished by drag-and-drop, making it easy for you to\nprototype different orders for your input fields. Additionally, elements\ncan be grouped together into blocks which can then be repeatable.\nDisplay Page creators can then map these fields to\n\\href{/docs/7-2/user/-/knowledge_base/u/content-page-elements\\#editable-elements}{editable\npage fragments} to use custom styles and formatting. Alternatively,\ntemplate writers can write a template which loops through these blocks\nand presents your content in innovative ways, such as in sliding\nnavigation bars, content that scrolls with the user, and more.\n\nNext you'll learn how you can create and edit structures through the\nManage Structures interface.\n\n\\chapter{Editing Structures}\\label{editing-structures}\n\nTo start, go to the \\emph{Structures} page.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From \\emph{Site Administration} go to \\emph{Content \\& Data} →\n  \\emph{Web Content}.\n\\item\n  Open the \\emph{Structures} tab.\n\\end{enumerate}\n\nThis page shows you all the web content structures in this Site. You can\nadd new web content structures, edit existing ones, manage the templates\nassociated with a structure, edit the permissions of a structure, and\ncopy or delete structures.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/manage-structures.png}\n\\caption{Structures are not pre-installed. You have to make your own.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\nNote: When you copy a structure, Liferay DXP generates a unique ID for\nthe copied structure, but every other attribute of the copied structure,\nincluding the name, is the same as that of the original. When you copy\nweb content structure, enter a new name for it to avoid confusing it\nwith the original. During the copy process, you're prompted to choose\nwhether to copy any detail templates or list templates associated with\nthe structure. For information on detail templates and list templates,\nplease refer to\n\\href{/docs/7-2/user/-/knowledge_base/u/dynamic-data-lists}{Dynamic Data\nLists}.\n\n\\noindent\\hrulefill\n\n\\emph{Basic Web Content}, which you used in previous exercises, lives at\nthe \\emph{Global} scope so that it is available to all Sites. This\nstructure and template are used automatically if a custom structure and\ntemplate are not added.\n\n\\section{Structure Fields}\\label{structure-fields}\n\nNow, create a new Structure:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Add} (\\includegraphics{./images/icon-add.png}).\n\\item\n  Give your Structure a name.\n\\end{enumerate}\n\nStructures are essentially a set of fields organized in a certain way.\nThe interface on this page provides an easy way to add and organize\nwhatever fields you need. Each element that you add has three icon\noptions that you can click:\n\n\\textbf{Settings:} (\\includegraphics{./images/icon-wrench.png}) Changes\nthe name and label and set other information about the field, like\nwhether or not it is required.\n\n\\textbf{Delete:} (\\includegraphics{./images/icon-trash.png}) Removes the\nfield from the structure.\n\n\\textbf{Duplicate:} (\\includegraphics{./images/icon-wysiwyg-add.png})\nDuplicates the field and all its settings and iterates the \\emph{Name}\nto avoid conflicts.\n\nWeb content structures can inherit characteristics from other\nstructures. A child structure inherits all the parent's fields and\nsettings. You can use this to make a similar structure to one that\nalready exists. For example, if you have \\emph{Sports Article} and you\nwant to create \\emph{In-depth Sports Article}, set \\emph{Sports Article}\nas the parent and the \\emph{In-dept Sports Article} inherits all its\nfields, letting you add new ones for more in-depth information.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Due to import/export operations, it's possible to have\nboth a global and a Site-scoped structure with the same\n\\texttt{structureKey}. If this happens, the Site-scoped structure takes\nprecedence, and you can't access the global structure from that Site.\n\n\\noindent\\hrulefill\n\nYou can also manually customize a structure's XML in \\emph{Source} mode.\nBy default the \\emph{View} mode is selected, but you can click the\n\\emph{Source} tab to switch. This method is for more experienced\ndevelopers.\n\nTake a moment to add, delete, and rearrange different elements.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-structure-editor.png}\n\\caption{The structure editor gives you many options to customize your\nWeb Content.}\n\\end{figure}\n\nThe following fields can be in structures:\n\n\\textbf{Boolean:} Adds a checkbox onto your structure, which stores\neither \\texttt{true} (checked) or \\texttt{false} (unchecked). Template\ndevelopers can use this as a display rule.\n\n\\textbf{Color:} Adds a way to choose a color.\n\n\\textbf{Date:} Adds a pre-formatted text field that displays a date\npicker to assist in selecting the desired data. The format for the date\nis governed by the current locale.\n\n\\textbf{Decimal:} Similar to \\emph{Number}, except that it requires a\ndecimal point (.) be present.\n\n\\textbf{Documents and Media:} Adds an existing uploaded document to\nattach to the structure. Can also upload documents into the Document\nLibrary.\n\n\\textbf{Geolocation:} Adds a map that displays a configured location.\nThe geolocation system can work in two ways: letting the system know\nyour current location (especially useful on mobile devices) and giving\nthe user directions to a another place.\n\n\\textbf{HTML:} An area that uses a WYSIWYG editor to enhance the\ncontent.\n\n\\textbf{Image:} Adds the browse image application into your structure.\nYou can select an image from the Documents and Media library or upload\nan image from your computer's storage. If uploading an image from your\npersonal computer to the web content article, it is only available for\nthat article.\n\n\\textbf{Integer:} Similar to \\emph{Number}, except that it constrains\nuser input to whole numbers.\n\n\\textbf{Link to Page:} Inserts a link to another page in the same site.\n\n\\textbf{Number:} Presents a text box that only accepts numbers as\ninputs, but puts no constraints on the kind of number entered.\n\n\\textbf{Radio:} Presents the user with a list of options to choose from\nusing radio button inputs.\n\n\\textbf{Select:} Presents a selection of options for the user to choose\nfrom using a combo box. Can be configured to allow multiple selections,\nunlike \\emph{Radio}.\n\n\\textbf{Separator:} Adds a horizontal line between fields.\n\n\\textbf{Text:} Used for items such as titles and headings.\n\n\\textbf{Text Box:} Used for the body of your content or long\ndescriptions.\n\n\\textbf{Web Content:} Provides a way to select a web content article.\n\nThese fields provide all you need to model any information type you\nwould want to use as web content. Liferay customers have used structures\nto model everything from articles, to video metadata, to wildlife\ndatabases.\n\n\\chapter{Configuring Structure\nFields}\\label{configuring-structure-fields}\n\nThere are many options available for configuring each structure field.\nSome of them relate to how the fields are displayed or how users\ninteract with them, but probably the most important field configuration\nis the \\textbf{Name}. When you create a new field, it has a random name\ngenerated that looks like \\texttt{TextField4882}. In most cases, you\nshould change this to something that is more memorable and more\ndescriptive. If you create a matching template, you don't want to have\nto remember if \\texttt{TextField4882} was the field for entering an\napplicant's name or annual salary.\n\nPractice this now.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your structure, add an \\emph{HTML} element.\n\\item\n  Hover over the field and select the \\emph{Configuration} icon\n  (\\includegraphics{./images/icon-wrench.png}).\n\\item\n  Change the \\emph{Field Label} value to \\emph{Instructions} and the\n  \\emph{Name} value to \\emph{steps}. Now your template writer has a\n  variable by which he or she can refer to this field.\n\\end{enumerate}\n\nHere's a list of all the configurable settings available for a\nstructure's fields:\n\n\\textbf{Type:} Lists the type of field placed in the definition. This is\nnot editable but is available to reference from a template.\n\n\\textbf{Field Label:} Sets the text that can be displayed with the\nfield. This is the human-readable text that the user sees.\n\n\\textbf{Show Label:} Select \\emph{Yes} to display the Field Label.\n\n\\textbf{Required:} Select \\emph{Yes} to mark the field required. If a\nfield is required, users must enter a value for it in order to submit\ncontent using this structure.\n\n\\textbf{Name:} The name of the field internally, automatically\ngenerated. Since this is the variable name that you can read the data\nfrom in a template or display page, you should enter a descriptive name.\n\n\\textbf{Predefined Value:} When a user creates a new web content article\nbased on a structure that has predefined values for various fields, the\npredefined values appear in the form as defaults for those fields.\n\n\\textbf{Tip:} Each field can have a small help icon, with a tooltip\nattached that displays helpful information. If you want to provide text\nfor the tooltip, you may enter it here.\n\n\\textbf{Indexable:} Select \\emph{Yes} to permit your field to be indexed\nfor search.\n\n\\textbf{Localizable:} Select \\emph{Yes} to permit localization for this\nfield.\n\n\\textbf{Repeatable:} Select \\emph{Yes} to make your field repeatable.\nUsers can then add as many copies of this field as they need. For\nexample, if you're creating a structure for articles, you might want a\nrepeatable Author field in case you have multiple authors for a\nparticular article.\n\n\\textbf{Multiple:} Select \\emph{Yes} to enable a multi-selection list\n(only available for the Select field).\n\n\\textbf{Options:} Changes the options available for selection. You can\nadd and remove options as well as edit each individual option's display\nname and value (only available for Radio and Select fields).\n\n\\textbf{Style:} Changes the line separator's style (only available for\nSeparator).\n\n\\section{Structure Default Values}\\label{structure-default-values}\n\nYou can define Structure Default Values for repeatable values in content\ncreated from that structure. They can also set defaults for Liferay's\nstandard asset fields (like tags, categories, and related assets) and\nthe content of the structure fields, while also setting a default\ntemplate for displaying the structure data.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/structure-actions.png}\n\\caption{You can edit default values via the \\emph{Actions} button of\nthe Manage Structures interface.}\n\\end{figure}\n\nReturning to the newspaper scenario again, suppose you want all sports\narticles to have the same display page (sports page), the same\ncategories, or the same set of tags. Instead of adding them for each\narticle or wondering if your users are adding them to every web content\narticle, you can add these characteristics once for every sports article\nby creating default values for the structure. Creating default values is\nnot part of creating a new structure, so make sure you have an existing\nstructure.\n\nTo edit a structure's default values:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Content \\& Data} → \\emph{Web\n  Content} and click on the \\emph{Structures} tab to see the structures\n  list.\n\\item\n  Find the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) for the desired\n  structure and select \\emph{Edit Default Values} from the menu to view\n  a window like the one below.\n\n  This form manages the structure settings. It duplicates the function\n  of the \\emph{Predefined Value} field setting (see above), but is much\n  more convenient for setting or editing a large number of defaults at\n  once.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/structure-default-values.png}\n\\caption{You can define values for your structure fields and the\nstandard asset metadata fields.}\n\\end{figure}\n\nIf you prefer to reset all your structure's default values, you can\nselect the \\emph{Reset Values} button. This resets all values for the\nstructure's fields and all properties shown in the right panel.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\emph{Reset Values} button is available for Liferay\nPortal 7.2 GA2+ and Liferay DXP 7.2 SP1+.\n\n\\noindent\\hrulefill\n\nEvery new web content you create with this structure is preloaded with\nthe data you inserted. Next, you'll learn about assigning permissions.\n\n\\chapter{Structure Settings}\\label{structure-settings}\n\nAfter you have created a structure and configured its fields, there is\nadditional configuration to consider including permissions and remote\naccess to managing structures.\n\n\\section{Assigning Permissions}\\label{assigning-permissions}\n\nPermissions on structures can be set just like any other\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{permission}.\nMost users should not be able to edit structures. Structures can be\ncoupled with templates or used on display pages, which require some web\ndevelopment knowledge to create. This is why only trusted developers\nshould be able to create structures. Users, of course, should be able to\nview structures. The \\emph{View} permission enables them to make use of\nthe structures to create content.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-structure-permissions.png}\n\\caption{You're able to assign structure permissions via the\n\\emph{Actions} button.}\n\\end{figure}\n\nThe best practice for structure permissions is to grant or deny them\nbased on Roles.\n\n\\section{WebDAV URL}\\label{webdav-url}\n\nThe WebDAV URL feature is available for web content structures and\ntemplates so users can upload and organize resources from both a web\ninterface and the file explorer of their desktop operating system. With\nthe WebDAV URL, site administrators can add, browse, edit, and delete\nstructures and templates on a remote server. After you complete your\nstructure, you can access the WebDAV URL by re-opening the structure or\ntemplate and clicking the \\emph{Details} section. If you'd like the see\nWebDAV in action, see\n\\href{/docs/7-2/user/-/knowledge_base/u/desktop-access-to-documents-and-media}{WebDAV\nAccess}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Some operating systems require a WebDAV server to be\nclass level 2 (to support file locking) before allowing files to be read\nor written. The Documents and Media library uses a class level 2 WebDAV\nserver but Web Content structures and templates do not. This means that\nLiferay DXP's Document and Media library supports WebDAV file locking\nbut Web Content structures and templates do not. However, on operating\nsystems which require WebDAV servers to be class level 2, it's possible\nto avoid the restriction by using third-party WebDAV clients (e.g.,\n\\href{http://cyberduck.ch}{Cyberduck}).\n\n\\noindent\\hrulefill\n\nNow that you understand how structures work, you're ready to understand\nanother key aspect of Liferay DXP's web content management system:\ntemplates.\n\n\\chapter{Designing Web Content with\nTemplates}\\label{designing-web-content-with-templates}\n\nWhile templates aren't required to display web content (i.e.~you can use\na\n\\href{https://portal.liferay.dev/docs/7-2/user/-/knowledge_base/u/display-pages-for-web-content}{Display\nPage Template} to map the structure fields), developers can create\ntemplates to display the elements of the structure in the markup they\nwant. In essence, templates are scripts that tell Liferay DXP how to\ndisplay content in the structure. Changes to the structure require\ncorresponding changes to any templates that use it, because new or\ndeleted fields produce errors on the page. Unless a template is generic\nand meant to be embedded in other templates, a template must have a\nmatching structure.\n\nLiferay DXP only supports creating web content templates in FreeMarker\n(FTL) by default. FreeMarker is the preferred, recommended language, and\nwhat you'll use in the next example.\n\n\\chapter{Adding Templates with\nStructures}\\label{adding-templates-with-structures}\n\nTo better understand templates, now you'll create a structure and an\nassociated template. First create the structure:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Content \\& Data} → \\emph{Web Content} from Site\n  Administration page and open the \\emph{Structures} tab.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n\\item\n  Name the structure \\emph{News Article} and add the following fields:\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n Field Type | &nbsp;Field Label | &nbsp;Name |\n\\end{verbatim}\n\n\\noindent\\hrulefill --------- \\textbar{} ---------- \\textbar{} ----------\n\\textbar{} Text \\textbar{} ~Title \\textbar{} ~\\texttt{title} \\textbar{}\nText Box \\textbar{} ~Abstract \\textbar{} ~\\texttt{abstract} \\textbar{}\nImage \\textbar{} ~Image \\textbar{} ~\\texttt{image} \\textbar{} HTML\n\\textbar{} ~Body \\textbar{} ~\\texttt{body} \\textbar{}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{4}\n\\tightlist\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nNow create the template and connect it to the structure.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the \\emph{Web Content} page, go to the \\emph{Templates} tab.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n\\item\n  Enter the name \\emph{News Article}.\n\\item\n  Open \\emph{Details} and make sure FreeMarker is selected as the script\n  language (it's the default).\n\\item\n  Click \\emph{Select} under \\emph{Structure}.\n\\item\n  Choose the \\emph{News Article} structure.\n\\item\n  In the \\emph{Script} area, find the \\emph{Fields} label on the left\n  and click on \\emph{Title}, \\emph{Abstract}, \\emph{Image} and\n  \\emph{Body} into the editor area. It should look like this:\n\n\\begin{verbatim}\n${title.getData()}\n${abstract.getData()}\n<#if image.getData()?? && image.getData() != \"\"> <img alt=\"${image.getAttribute(\"alt\")}\" data-fileentryid=\"${image.getAttribute(\"fileEntryId\")}\" src=\"${image.getData()}\" /> </#if>\n${body.getData()}\n\\end{verbatim}\n\\item\n  Next, add heading and \\texttt{\\textless{}p\\textgreater{}} tags and\n  align the image to center to style your elements like this:\n\n\\begin{verbatim}\n<h1>${title.getData()}</h1>\n<p>${abstract.getData()}</p>\n<#if image.getData()?? && image.getData() != \"\"> <img alt=\"${image.getAttribute(\"alt\")}\" data-fileentryid=\"${image.getAttribute(\"fileEntryId\")}\" src=\"${image.getData()}\" align=\"center\" /> </#if>\n<p>${body.getData()}</p>\n\\end{verbatim}\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nTo finish it up, add some content:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\emph{Web Content} tab.\n\\item\n  Click on the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}) and select \\emph{News\n  Article}.\n\\item\n  Insert some content and publish!\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-structures-templates-completed.png}\n\\caption{The Lunar Resort News Article is shaping up!}\n\\end{figure}\n\nAwesome! You created your own web content template!\n\n\\chapter{Embedding Widgets in\nTemplates}\\label{embedding-widgets-in-templates}\n\nYou can also embed widgets in web content templates. Core apps and\ncustom apps, instanceable or non-instanceable can be embedded in web\ncontent templates. Below is an example of embedding a Language widget in\nFreeMarker:\n\n\\begin{verbatim}\n<@liferay_portlet_ext[\"runtime\"] portletName=\"com_liferay_portal_kernel_servlet_taglib_ui_LanguageEntry\" />\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} The \\texttt{theme} variable is no longer injected into\nthe FreeMarker context. For more information about why the theme\nvariable was removed for Liferay DXP 7.0 and suggestions for updating\nyour code, visit the\n\\href{/docs/7-0/reference/-/knowledge_base/r/breaking-changes\\#taglibs-are-no-longer-accessible-via-the-theme-variable-in-freemarker}{Taglibs\nAre No Longer Accessible via the theme Variable in FreeMarker} breaking\nchange entry.\n\n\\noindent\\hrulefill\n\nIn addition to embedding widgets in templates, you can embed a template\nwithin another template. This allows for reusable code, JavaScript\nlibrary imports, scripts, or macros.\n\nBelow is an example of embedding a template in FreeMarker:\n\n\\begin{verbatim}\n<#include \"${templatesPath}/[template-key]\" />\n\\end{verbatim}\n\nThe \\emph{Template Key} can be found when editing a previously published\ntemplate.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adt-template-key.png}\n\\caption{You can find the Template Key when view the Edit page for a\ntemplate..}\n\\end{figure}\n\n\\chapter{Using Taglibs in Templates}\\label{using-taglibs-in-templates}\n\nLiferay's taglibs are also accessible to web content administrators\ndeveloping in FreeMarker. There is no need to instantiate these taglibs\nwithin your FreeMarker template; they're already provided for you\nautomatically. You can access these taglibs by indicating the TLD's file\nname with underscores.\n\nWhen you're using Liferay DXP's template editor, you can find variables\non the left side of the template editor. To place one of the variables\nonto the template editor,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Position your cursor where you want the variable placed.\n\\item\n  Click the variable name.\n\\end{enumerate}\n\nIf the variable name doesn't give you sufficient information on the\nvariable's functionality, you can hover your pointer over it for a more\ndetailed description.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-templates-create.png}\n\\caption{You can hover your pointer over a variable for a more detailed\ndescription.}\n\\end{figure}\n\nThe interactive template editor is available for the FreeMarker,\nVelocity, and XSL languages. Depending on which language you select, the\nvariable content changes so you're always adding content in the language\nyou've chosen. Another cool feature for the template editor is the\nautocomplete feature. It can be invoked by typing \\emph{\\$\\{} which\nopens a drop-down menu of available variables. By clicking one of the\nvariables, the editor inserts the variable into the template editor.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\texttt{utilLocator}, \\texttt{objectUtil}, and\n\\texttt{staticUtil} variables for FreeMarker are disabled by default.\nThese variables are vulnerable to remote code execution and privilege\nescalation, and should be used with caution, if enabled.\n\n\\noindent\\hrulefill\n\nAfter you've saved your template, Liferay DXP provides a WebDAV URL and\nstatic URL. These values access the XML source of your structure. You\ncan find these URLs by returning to your template after it's been saved\nand expanding the \\emph{Details} section. For more information on WebDAV\nand the uses of the WebDAV URL, reference the\n\\href{/docs/7-0/user/-/knowledge_base/u/publishing-files\\#desktop-access-to-documents-and-media}{WebDAV\nAccess} section.\n\nNow that you've created a handsome template and know how to use the\ntemplate editor, it's time to decide who the lucky people are that get\nto use your new template.\n\n\\chapter{Assigning Template\nPermissions}\\label{assigning-template-permissions}\n\nStructures and Templates provide direct access to Liferay's APIs which\nmakes them powerful, but it also means that they can be dangerous in the\nwrong hands. Only trusted users should be given access. The recommended\npractice is to create two Roles with access to structures and templates:\n\n\\begin{itemize}\n\\item\n  \\textbf{Content Developers} get full permission to create and edit\n  structures and templates.\n\\item\n  \\textbf{Content Creators} only need permission to view the structures\n  and templates so they can use them to create content.\n\\end{itemize}\n\nWhen creating the Roles, define them to have global permission for all\nstructures and templates across the entire instance or only for specific\nSites. For more information on creating Roles, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{Roles and\nPermissions} article.\n\n\\section{Assigning Permissions for Individual\nTemplates}\\label{assigning-permissions-for-individual-templates}\n\nYou can also control access to specific templates separately. To\ndetermine who can view and interact with a template,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\emph{Templates} tab.\n\\item\n  Click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) for a template that you\n  created and select \\emph{Permissions}.\n\\end{enumerate}\n\nHere permissions for a template can be set for Roles or Teams. Use this\noption to provide access to templates on a case by case basis for users\nthat shouldn't have access to templates on a larger level granted by a\nRole.\n\nWhether your Site is small and static or large and dynamic, Liferay's\nWeb Content Management system enables you to plan and manage it. With\ntools such as the WYSIWYG editor, structures and templates, you can\nquickly add and edit content. With Web Content Display, you can rapidly\nselect and configure what content to display. You'll find that managing\nyour site becomes far easier when using Liferay DXP's Web Content\nManagement system.\n\n\\chapter{Display Page Templates for Web\nContent}\\label{display-page-templates-for-web-content}\n\nDisplay Page Templates provide a new level of control over the look and\nfeel of your content. The templates empower marketers and designers to\ncreate stunning designs for Web Content. They use both Page Fragments\nand Web Content to provide an easy way to create beautiful layouts for\ndisplaying articles.\n\nOne great way to use Display Pages Templates is to create standardized\nformats for articles. If you examine the content on many writing\nplatforms, each article follows a similar format like this:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Header Image\n\\item\n  Title\n\\item\n  Main Body\n\\item\n  Highlighted Quote\n\\item\n  Footer with links to related articles or other content\n\\end{itemize}\n\nDisplay Pages Templates let you create standard, reusable formats like\nthis to streamline the creation of attractive content. Read on to learn\nmore!\n\n\\chapter{Creating Display Page\nTemplates}\\label{creating-display-page-templates}\n\nDisplay Page Templates are created initially in much the same way as\nContent Pages. You select any number of page fragments and add them to\nthe page. Display pages differ in that after you add the fragments, you\ncan then map editable fields in those fragments to the fields of a web\ncontent article. You can learn more about creating Page Fragments in the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-pages}{Creating\nContent Pages} article.\n\nLooking at the example of a template for a long form article, we can see\nhow Display Page Templates utilize Page Fragments. The article can have\nan image, a title (simple style text), a main body (rich text), a\nhighlighted quote (simple styled text), and then a standard footer. Your\nfirst step in creating the Display Page Template is to create a Page\nFragment which has all those fields formatted the way you want them.\nYour fragment could have these fields:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Editable header\n\\item\n  Editable Image\n\\item\n  Editable rich text\n\\item\n  Editable plain text (with block-quote styling)\n\\item\n  Non-editable footer\n\\end{itemize}\n\nTo go along with this fragment, you could have a Web Content Structure\nwith these fields:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Title (Text box)\n\\item\n  Image (Documents and Media image)\n\\item\n  Content (Web Content)\n\\item\n  Quote (Text area)\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/structure-to-fragment.png}\n\\caption{Connecting structure fields to fragment data.}\n\\end{figure}\n\nThe Display Page Template maps the fields from the Web Content Structure\nto the fragment. When the Display Page Template is assigned for an\narticle with that Structure, it appears on a display page with the\nformatting from the fragment.\n\n\\chapter{Display Page Template\nExample}\\label{display-page-template-example}\n\nCreate a new Display Page Template:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Builder} → \\emph{Pages} in Site Administration.\n\\item\n  Go to the \\emph{Display Page Templates} tab.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n\\item\n  Name your Display Page \\emph{Lunar Resort Display Page Template}, open\n  the \\emph{Content Type} dropdown and select \\emph{Web Content Article}\n  and \\emph{Basic Web Content} for the Subtype, and click \\emph{Save}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/display-page-asset-type.png}\n\\caption{Selecting the Asset type and Subtype.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/create-display-page.png}\n\\caption{The Display Page Template creation interface.}\n\\end{figure}\n\nTo build the Display Page Template, you can add any number of\nFragments---with and without editable content---to the page to build\nyour design. Fragments with editable content can have their editable\nfields mapped to be filled by a Web Content article. You can also base\nit on a specific Web Content Structure.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/display-page-with-fragments.png}\n\\caption{Editing a Display Page Template with some Fragments added.}\n\\end{figure}\n\nNotice that the example has an editable title and text body, with a\nstatic footer containing graphics and links. After you've added some\nfragments to the page, you can map them like this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the \\emph{Section Builder} → \\emph{Basic Components} tab\n  in the right menu.\n\\item\n  Add a \\emph{Heading 1} and \\emph{Text} components to the appropriate\n  areas.\n\\item\n  Click on an editable text area and click the \\emph{Map} button\n  (\\includegraphics{./images/icon-map.png}) in the dialog that appears.\n\\item\n  Select a field to map the editable fragment to. The mapped field\n  highlights purple to indicate that it's mapped.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/display-page-map-field.png}\n  \\caption{Mapping the editable fragments to structure fields.}\n  \\end{figure}\n\\item\n  Click \\emph{Publish} at the top of the page to save your work.\n\\end{enumerate}\n\nYou now have a Display Page Template with static graphics and a text\narea that's replaced with whatever content you add to it.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can map any data or metadata from a Web Content\nArticle or Structure to a Display Page Template. For the Basic Web\nContent type, this includes structure-defined fields like Summary,\nTitle, and Content, as well as metadata fields like Publish Date,\nCategories, and Tags. In a user-defined structure, all user selected\nfields appear here as well. Custom fields are also available for display\nif they apply to the content type selected.\n\n\\noindent\\hrulefill\n\nFor more information on the right panel and the Fragment options\navailable to you, see\n\\href{/docs/7-2/user/-/knowledge_base/u/content-page-elements}{Content\nPage Elements} and\n\\href{/docs/7-2/user/-/knowledge_base/u/content-page-management-interface}{Content\nPage Interface}. Content Pages and Display Page Templates share the same\nPage builder tools.\n\n\\section{Publishing with Display Page\nTemplates}\\label{publishing-with-display-page-templates}\n\nNow create a short article to display with this display page template:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Content \\& Data} → \\emph{Web Content} in Site\n  Administration.\n\\item\n  Add a Basic Web Content article.\n\\item\n  Name it \\emph{Thoughts About Space} and fill in some short content.\n\\item\n  Open the \\emph{Display Page Template} section and under the dropdown,\n  select \\emph{Specific Display Page Template}. Then click\n  \\emph{Select}.\n\\item\n  Select the \\emph{Lunar Resort Display Page Template} and click\n  \\emph{Done}.\n\\item\n  Click \\emph{Publish}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/display-page-creating-content.png}\n\\caption{Selecting the Asset type and Subtype.}\n\\end{figure}\n\nWhen published, you can view the content at its Friendly URL (you can\nfind the Friendly URL while editing a Web Content article under\n\\emph{Friendly URL}) or when you click on the content in an Asset\nPublisher with \\emph{Asset Link Behavior} set to \\emph{View in Context}.\n\nWhen editing the article, you can preview what the display page template\nwill look like with the \\emph{Preview}\n(\\includegraphics{./images/icon-preview.png}) button located next to the\nselected display page template.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/display-page-in-context.png}\n\\caption{Selecting the Asset type and Subtype.}\n\\end{figure}\n\nYou can go back and edit the display page template by navigating to\n\\emph{Site Administration} → \\emph{Site Builder} → \\emph{Pages} →\n\\emph{Display Page Templates} and clicking \\emph{Actions}\n(\\includegraphics{./images/icon-staging-bar-options.png}) → \\emph{Edit}\nnext to the display page template you want to edit. If you're viewing\nthe published display page, you can also select the \\emph{Edit} button\n(\\includegraphics{./images/icon-edit-pencil.png}) from the Control Menu.\n\nAwesome! You now know how to create and configure a display page using\ndisplay page templates.\n\n\\chapter{Managing Content Sets}\\label{managing-content-sets}\n\nA Content Set is exactly what it sounds like: a set of content items. In\nshort, an administrator can define a list of content, and then that list\ncan be displayed. The way that the Content Set is displayed is\ndetermined by the method that is used to display it. For example, if the\nContent Set is being used by a smartwatch app, it could be displayed as\na simple list of titles, and selecting a title would cause the full\narticle to display on a connected mobile device. The same Content Set\ncould be displayed in a web browser with the full content of each\narticle.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} In previous versions of Liferay DXP, you used the Asset\nPublisher to define and display either static lists of assets or dynamic\nlists based on criteria like tags, categories, or asset type. In 7.0\nContent Sets take the core idea of defining different types of asset\nlists and expand it. Content Lists are created outside of the context of\na specific application or widget and can be used and re-used across\ndifferent channels and applications.\n\n\\noindent\\hrulefill\n\n\\section{Creating and Displaying Content\nSets}\\label{creating-and-displaying-content-sets}\n\nContent Sets are created through the Site Administration interface. All\nthe features for creating and managing Content Sets are contained here.\nThey are displayed using Liferay's widgets or your own custom\napplications. Read our guides for information on\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-sets}{Creating\nContent Sets} and\n\\href{/docs/7-2/user/-/knowledge_base/u/displaying-content-sets}{Displaying\nContent Sets}\n\n\\section{Content Set Personalization}\\label{content-set-personalization}\n\nContent Sets can have variations driven by Liferay DXP's Personalization\nengine. After you create a Content Set, if you have at least one User\nSegment created, you can create a personalized experience of the Content\nSet for that Segment. To learn to harness the power of experience\npersonalization for Content Sets, see\n\\href{/docs/7-2/user/-/knowledge_base/u/content-set-personalization}{Content\nSet Personalization}.\n\n\\section{Converting Asset Publisher Configurations to Content\nSets}\\label{converting-asset-publisher-configurations-to-content-sets}\n\nYou may have already gone through a great deal of work to create a\nperfect, curated list of content through the Asset Publisher, but now\nyou want to display that list elsewhere without duplicating your work.\nYou can do that with Content Sets. Read the\n\\href{/docs/7-2/user/-/knowledge_base/u/converting-asset-publisher}{Converting\nAsset Publisher Configuration to Content Sets guide} article to learn\nmore.\n\n\\chapter{Creating Content Sets}\\label{creating-content-sets}\n\nContent Sets are created by content administrators through the Content\nSets interface in Site Administration. Content Sets can use either\nManual or Dynamic selection, and you can create any number of Content\nSets, and display them through the Asset Publisher or custom\napplications. Content Sets can also have\n\\href{/docs/7-2/user/-/knowledge_base/u/content-set-personalization}{personalized\nvariations} which provide different experiences for different users\nbased on criteria that you specify. The criteria management is shared\nwith the Asset Publisher, so for more information on each option, see\nthe official\n\\href{/docs/7-2/user/-/knowledge_base/u/publishing-content-dynamically}{Asset\nPublisher Documentation}.\n\n\\section{Creating a Manual Content\nSet}\\label{creating-a-manual-content-set}\n\nTo demonstrate the creation of a Manual Content Set, create a Content\nSet that contains a number of images to be displayed on the Frontpage of\nthe fictitious Space Program website. To prepare for this exercise,\nupload some appropriate images to \\emph{Documents and Media} to use for\nthe Content Set.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Content \\& Data} and select\n  \\emph{Content Sets}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/content-sets-empty-page.png}\n  \\caption{Content Sets is found in the Content \\& Data section of Site\n  Administration.}\n  \\end{figure}\n\\item\n  Click \\includegraphics{./images/icon-add.png} and select \\emph{Manual\n  Selection}.\n\\item\n  Name your Content Set \\emph{Space Program Images}.\n\\end{enumerate}\n\nOn the next screen, you can select the assets to include in the Content\nSet.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Select} → \\emph{Basic Document}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/content-sets-select-document.png}\n  \\caption{You can select the type of asset to add to the Content Set.}\n  \\end{figure}\n\\item\n  Now, check the boxes for each image that you want to add and click\n  \\emph{Add}.\n\\end{enumerate}\n\nNow this Content Set can be displayed anywhere on the site where it was\ncreated. You can add or remove items from the set, and it will\nautomatically update it whereever it is displayed.\n\n\\section{Creating a Dynamic Content\nSet}\\label{creating-a-dynamic-content-set}\n\nTo demonstrate the creation of a Dynamic Content Set, create a Content\nSet that contains a number of varied assets that are tagged as\n``trending.'' In order for this to work, you will need some number of\nexisting assets with the appropriate tag.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the \\emph{Content Sets} page, click\n  \\includegraphics{./images/icon-add.png} → \\emph{Dynamic Selection}.\n\\item\n  Enter \\emph{Trending} for the name and click \\emph{Save}.\n\\end{enumerate}\n\nWith Dynamic Content Sets, you can choose the \\emph{Source},\n\\emph{Scope}, \\emph{Filter}, and \\emph{Ordering} for the items in the\nset.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Leave \\emph{Source} as \\emph{Any} and \\emph{Scope} as \\emph{Current\n  Site}\n\\item\n  Open \\emph{Filter}, make sure it is set to \\emph{Contains Any of the\n  following Tags}, and then enter ``trending'' in the \\emph{Tags} box.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/content-set-trending-filter.png}\n  \\caption{Content Sets use the same filter system as the Asset\n  Publisher.}\n  \\end{figure}\n\\item\n  Open \\emph{Ordering} and set it to \\emph{Order By}: \\emph{Publish\n  Date}, \\emph{And Then By}: \\emph{Title}.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nThis will create a Content Set which will contain any items that are\ncurrently tagged as \\emph{trending} and any future items with the\n\\emph{trending} tag will be added to the Content Set automatically.\n\nNow that you have your Content Sets created, you can\n\\href{/docs/7-2/user/-/knowledge_base/u/displaying-content-sets}{display\nthem on a page}.\n\n\\chapter{Displaying Content Sets}\\label{displaying-content-sets}\n\nContent Sets are primarily displayed through the Asset Publisher. It is\ncurrently the only method to display them out of the box, but you can\ndevelop your own external applications or widgets to utilize Content\nSets. In\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-sets}{Creating\nContent Sets} you created two Content Sets. Now display them on a page.\n\n\\section{Configuring the Asset Publisher for Content\nSets}\\label{configuring-the-asset-publisher-for-content-sets}\n\nTo display the Content Sets, start with a blank page, and then add the\nnecessary Asset Publishers and configure them to display the Content\nSets.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a new \\emph{Home} page for your site as a Widget Page with a 1\n  column layout. If you're using a fresh Liferay DXP bundle, you can\n  just remove the \\emph{Hello World} widget from the sample \\emph{Home}\n  page.\n\\item\n  Open the \\emph{Add} menu and add two \\emph{Content Management} →\n  \\emph{Asset Publishers} to the page stacked vertically.\n\\item\n  Click \\includegraphics{./images/icon-app-options.png} →\n  \\emph{Configuration} for the top Asset Publisher.\n\\item\n  Under \\emph{Asset Selection} choose \\emph{Content Set}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-set-asset-selection.png}\n\\caption{The Asset Publisher has a number of options available for\nselecting its source for content.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{4}\n\\item\n  Open \\emph{Select Content Set} and click \\emph{Select}.\n\\item\n  Click on the \\emph{Space Program Images} Content Set.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nNow the images will appear at the top of the page. You can manage the\nway the content is displayed---like what metadata appears---or even\ncreate a \\emph{Widget Template} to style the content, but the items\nwhich display and the order in which they appear are determined by the\nContent Set.\n\nNow configure the bottom Asset Publisher with the other Content Set.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\includegraphics{./images/icon-app-options.png} →\n  \\emph{Configuration} for the bottom Asset Publisher.\n\\item\n  Under \\emph{Asset Selection} choose \\emph{Content Set}.\n\\item\n  Open \\emph{Select Content Set} and click \\emph{Select}.\n\\item\n  Click on the \\emph{Trending} Content Set.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-set-select-set.png}\n\\caption{Select the Content Set you want to use.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{6}\n\\tightlist\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nAgain, you can manage various display settings, but the items which\nappear and their order are determined by the Content Set criteria.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-set-dynamic-results.png}\n\\caption{You can see the results as the standard Asset Publisher output.\nYou can create Widget Templates to add more style and pizzazz here.}\n\\end{figure}\n\n\\section{Adding Items to an existing Content\nSet}\\label{adding-items-to-an-existing-content-set}\n\nTo demonstrate both the management of both static and dynamic Content\nSets, upload a new image, tag it, and add it to the static set manually.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Upload a new image, and under \\emph{Categorization} tag it as\n  \\emph{trending}.\n\\item\n  Without lifting another finger, the image is added to the top of the\n  \\emph{Trending} Content List.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-set-dynamic-add.png}\n\\caption{The result is dynamically added to the Content List wherever it\nis displayed.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  To add it to the manual set, go back to \\emph{Site Administration} →\n  \\emph{Content \\& Data} → \\emph{Site Builder}.\n\\item\n  Click on \\emph{Space Program Images} or select\n  \\includegraphics{./images/icon-options.png} → \\emph{Edit} next to\n  \\emph{Space Program Images}.\n\\item\n  Next to \\emph{Asset Entries} click \\emph{Select} → \\emph{Basic\n  Document}.\n\\item\n  Select the new image and click \\emph{Add}.\n\\item\n  Navigate back to the \\emph{Home} page to see your image added to the\n  list.\n\\end{enumerate}\n\nContent Sets are a powerful feature which provide one place to easily\ndefine content and other assets to be displayed all over your site.\nTheir reusability also means less repeated work involved in getting\ngreat content delivered to your users.\n\n\\chapter{Converting Asset Publisher Configurations to Content\nSets}\\label{converting-asset-publisher-configurations-to-content-sets-1}\n\nIn the previous two guides in this section, you've seen\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-sets}{Creating\nContent Sets} and\n\\href{/docs/7-2/user/-/knowledge_base/u/displaying-content-sets}{Displaying\nContent Sets} demonstrated. Next, try out converting an existing Asset\nPublisher configuration to a Content Set.\n\nIn this case, you have an Asset Publisher on a page, which is configured\nto display images tagged as \\emph{trending} in reverse alphabetical\norder by title. This might not be too hard to reproduce in the\n\\emph{Content Set} creator, but it's even easier to create the Content\nSet definition directly from the Asset Publisher.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\includegraphics{./images/icon-app-options.png} →\n  \\emph{Configuration} for the Asset Publisher.\n\\item\n  Click \\emph{Create a content set from this configuration}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/content-set-create-ap.png}\n  \\caption{You can generate a Content Set directly from the Asset\n  Publisher configuration.}\n  \\end{figure}\n\\item\n  Enter the title and click \\emph{Save}.\n\\end{enumerate}\n\nAnd as quickly as that you have a new Content Set that you can use with\nAsset Publishers anywhere on the site.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-set-ap-added.png}\n\\caption{The Content Set is added right alongside any existing sets.}\n\\end{figure}\n\nGreat! You converted your Asset Publisher configuration to a Content\nSet.\n\n\\chapter{Organizing Content with Tags and\nCategories}\\label{organizing-content-with-tags-and-categories}\n\nTags and categories are two important tools you can use to help organize\ninformation in Liferay DXP. These tools help users to easily find the\ncontent they're looking for through search or navigation. Tagging and\ncategorizing assets is easy. You can tag or categorize an asset at\ncreation time or when editing an existing asset. If you click on the\n\\emph{Metadata} section of the form when creating or editing an asset,\nyou'll find an interface for adding tags and categories. If no\ncategories are available to be added to the asset (e.g., if no\ncategories have been created), the \\emph{Select} option doesn't appear.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-categorization.png}\n\\caption{Here is the Web Content application's metadata section.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You'll notice in Figure 1 above that there is also a\n\\emph{Priority} field for web content. This field is not related to\ncategories and tags, but rather, specifies the order in which the web\ncontent article is listed when displayed in the Asset Publisher. To\nlearn more about the Asset Publisher, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/publishing-assets}{Publishing\nAssets} section.\n\n\\noindent\\hrulefill\n\nThe Menu (\\includegraphics{./images/icon-menu.png}) contains interfaces\nfor managing tags and categories for each site in Liferay DXP. Navigate\nto the Site Administration menu → \\emph{Categorization}, and you'll find\nthe \\emph{Tags} and \\emph{Categories} options. These options can be used\nto manage all your site's tags and categories. It is important that you\nboth tag and categorize your content when you enter it. You'll take a\ncloser look at tags and categories next.\n\n\\chapter{Tagging Content}\\label{tagging-content}\n\nTags are an important tool that can help organize information and make\nit easier for users to find the content they want. Tags are\nall-lowercase words or phrases that you can attach to any content.\nTagging content makes your search results more accurate and enables you\nto use tools like the Asset Publisher to display content in an organized\nfashion on a web page.\n\nThere are two ways to create tags: through the administrative console in\nthe Site Administration section of the Menu or on the fly as content is\ncreated. By default, tags can be created by regular users and users can\napply them to any assets they have permission to create or edit.\n\nOnly site administrators can access the \\emph{Tags} application in the\nContent section of the Site Administration area of the Menu. Here, site\nadministrators can create new tags and edit any existing site tags:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the site you want to create tags for and click\n  \\emph{Categorization} → \\emph{Tags}.\n\n  From this screen, you can view existing tags and create new ones.\n\\item\n  To create a new tag, click the \\emph{Add Tag} icon\n  (\\includegraphics{./images/icon-add.png}) and enter a name for the\n  tag.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/new-tag-interface.png}\n\\caption{The Add Tag interface is very simple, only requiring the name\nof your tag.}\n\\end{figure}\n\nThe process for adding tags during content creation is similar. For\nexample, to create tags for a new web content article:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\emph{Metadata} dropdown in a New Web Content menu.\n\\item\n  Add tags \\emph{lunar}, \\emph{moon}, and \\emph{spectacular}.\n\\end{enumerate}\n\nOnce you've created the web content with these tags, the web content is\nassociated with those tag words when they are searched or referenced.\n\nTags are not the only instance-wide mechanism for describing content:\nthe next tutorial describes categories.\n\n\\chapter{Defining Categories for\nContent}\\label{defining-categories-for-content}\n\nCategories are similar in concept to tags, but are designed for use by\nadministrators, not regular users. Hierarchies of categories can be\ncreated, and categories can be grouped together in \\emph{vocabularies}.\nWhile tags represent an \\emph{ad hoc} method for grouping content,\ncategories exist to allow administrators to organize content in a more\nofficial, hierarchical structure. Think of tags like the index of a book\nand categories like its table of contents. Both serve the same purpose:\nto help users find the information they seek.\n\nYou can add properties to categories. Category properties are a way to\nadd information to specific categories. You can think of category\nproperties as tags for your categories. Structurally, category\nproperties are just like tag properties: they are key-value pairs\nassociated with specific categories that provide information about the\ncategories.\n\nAdding vocabularies and categories is similar to adding tags:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the site where you want to create categories.\n\\item\n  Click \\emph{Categorization} → \\emph{Categories} to view the Categories\n  application.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/vocabulary-list.png}\n\\caption{After adding new vocabularies, you'll notice your vocabularies\nindicate the amount of categories existing beneath them.}\n\\end{figure}\n\nClicking on a vocabulary displays categories that have been created\nunder that vocabulary. To create a new vocabulary,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on the \\emph{Add Vocabulary} button\n  (\\includegraphics{./images/icon-add.png}).\n\\item\n  Enter a name and, optionally, a description.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nBy default, the \\emph{Allow Multiple Categories} option is enabled. This\nallows multiple categories from the vocabulary to be applied to an\nasset. If the box is disabled, only one category from the vocabulary can\nbe applied to an asset. The \\emph{Associated Asset Types} lets you\nchoose which asset types the categories of the vocabulary can be applied\nto and which asset types are \\emph{required} to have an associated asset\nfrom the vocabulary. Finally, you can configure the permissions of the\nvocabulary. By default, guests can view the vocabulary but only the\nowner can delete it, update it, or configure its permissions.\n\nCreating new categories is similar to creating new tags except that\ncategories must be added to an existing vocabulary and they can only be\ncreated by site administrators. Once created, however, regular users can\napply categories to any assets they have permission to create or edit.\nTo create a new category:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Click the \\emph{Add Category} icon\n  (\\includegraphics{./images/icon-add.png}).\n\\end{enumerate}\n\nIf you're already viewing a vocabulary:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) next to an existing\n  vocabulary and select \\emph{Add Category}.\n\\item\n  Enter a name for the new category and, optionally, a description.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nJust as with tags, you can configure the category's permissions,\nchoosing which roles (guest, site member, owner) can view the category,\napply it to an asset, delete it, update it, or configure its\npermissions. By default, categories are viewable by guests, and site\nmembers can apply categories to assets.\n\nOnce you have created some vocabularies and categories, you can take\nadvantage of the full capabilities of categories by creating a nested\nhierarchy of categories. To nest categories, select the \\emph{Actions}\nbutton for the category you want to be the parent category. Then select\n\\emph{Add Subcategory}, which adds a child category to the selected\nparent.\n\nAfter you've created a hierarchy of categories, they're available to\napply to content:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on \\emph{Web Content} in the \\emph{Content \\& Data} section of\n  Site Administration and click \\emph{Add} → \\emph{Basic Web Content}.\n\\item\n  Click on \\emph{Metadata} from the right-side menu and click\n  \\emph{Select} on the vocabulary you'd like to apply. A dialog box\n  appears with your categories.\n\\item\n  Select relevant categories by checking the box next to them, and\n  they'll be applied to the content.\n\\end{enumerate}\n\nSuppose you're running a Lunar Resort shop called Lunar Fireworks and\nyou have many web content articles describing the colors and types of\nfireworks you offer. The abundance of your articles is overwhelming, and\nas your shop grows, so too does the web content articles you're required\nto manage. You've decided to categorize your web content based on the\ncolor and type of firework, so the articles are easier to manage.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Site Administration → \\emph{Categorization} → \\emph{Categories}\n  and create vocabularies \\emph{Type} and \\emph{Color}.\n\\item\n  Make sure both vocabularies are only used for web content articles by\n  clicking the \\emph{Associated Asset Types} dropdown and selecting\n  \\emph{Web Content Article}.\n\\item\n  Create categories \\emph{Fire} and \\emph{Smoke} for the Type vocabulary\n  and \\emph{Red}, \\emph{Yellow}, and \\emph{Blue} categories for the\n  Color vocabulary.\n\\item\n  Now navigate to \\emph{Content \\& Data} → \\emph{Web Content} in Site\n  Administration and create an article called \\emph{Red Rocket}. This is\n  your best selling product, so make sure to give it a detailed\n  explanation and an awesome picture.\n\\item\n  Select the \\emph{Metadata} dropdown for your web content article and\n  select the Type → Fire and Color → Red categories.\n\\end{enumerate}\n\nWhen you publish your new web content article for your best selling\nproduct, it's organized by its type and color. Once you've organized all\nyour articles, you'll always be able to reference the type and color of\na firework, just in case you forget.\n\nThere are a few other cool features for vocabularies and categories. A\nfew of them were mentioned already when the \\emph{Allow Multiple\nCategories} and \\emph{Required} selectors for vocabularies and\ncategories were discussed. The three new features are targeted\nvocabularies, single/multi-valued vocabularies, and separated widgets\nfor every vocabulary. They're in the next tutorial.\n\n\\chapter{Targeted Vocabularies}\\label{targeted-vocabularies}\n\nYou can decide which vocabularies can be applied to an asset type and\nwhich vocabularies are required for an asset type with Targeted\nVocabularies. To configure these settings, go to the Categories\napplication in Site Administration and select a vocabulary's\n\\emph{Actions} → \\emph{Edit} button. Select the \\emph{Associated Asset\nTypes} tab to reveal a dialog box like the one below.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/targeted-vocabularies.png}\n\\caption{You can target vocabularies by checking the \\emph{Allow\nMultiple Categories} selector and then selecting the Asset Types.}\n\\end{figure}\n\nThe default value for \\emph{Associated Asset Types} is \\emph{All Asset\nTypes}. You can fine tune your choices by using the \\emph{+} and\n\\emph{-} buttons, which narrows the scope of the vocabulary to specific\nassets. In the screenshot above, notice that the vocabulary is\nconfigured to be available for Web Content articles and Blog entries,\nbut it is not required. It is mandatory, however, for Bookmark entries.\n\n\\section{Single and Multi-valued\nVocabularies}\\label{single-and-multi-valued-vocabularies}\n\nYou can also decide if users can choose one or more categories from the\nsame vocabulary to apply to an asset. If a vocabulary is single-valued\nyou can only choose one. If it allows more, you can choose several\ncategories from the vocabulary to apply to an asset.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/multi-valued-vocabularies.png}\n\\caption{Multi-valued vocabularies allow multiple categories from the\nvocabulary to be applied to an asset. Single-valued vocabularies only\nallow one category from the vocabulary to be applied. Here, the\n\\emph{Dining} and \\emph{Nightlife} categories are selected to be applied\nbut the \\emph{Scenic Adventures} category is not.}\n\\end{figure}\n\nYou can configure the single-valued or multi-valued status of a\nvocabulary through the Categories application. Edit a vocabulary and\ndeselect the \\emph{Allow Multiple Categories} selector to create a\nsingle-valued vocabulary. Use the default option to create a\nmulti-valued vocabulary.\n\n\\section{Separated Entries}\\label{separated-entries}\n\nA third feature of vocabularies and categories is that every vocabulary\nhas its own separated entry. These entries appear in the Categorization\nsection of the form for editing an asset, and they allow users to easily\nselect appropriate categories for that asset.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/separated-entries.png}\n\\caption{Vocabularies have their own entries, making it easy to select\navailable categories.}\n\\end{figure}\n\nIt's important to use tags and categories with all your content, so that\ncontent is easier for users to find.\n\nNext, you'll learn how to geo-locate assets.\n\n\\chapter{Geolocating Assets}\\label{geolocating-assets}\n\nGeolocation adds the geographic coordinates where an asset was created\nas metadata to your assets. You can add geolocation metadata to your web\ncontent, Data Lists, and Documents \\& Media. This feature is provided\nfor you out-of-the-box. However, you must first enable it in your assets\nin order to use it.\n\nLet's examine how you can enable geolocation in your web content.\n\n\\section{Geolocating Web Content}\\label{geolocating-web-content}\n\nTo use geolocation in your web content, you must create a\n\\href{/docs/7-2/user/-/knowledge_base/u/designing-uniform-content}{structure\nand template} that includes a Geolocation field.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/geo-structure.png}\n\\caption{Add a geolocation field to your structure to enable geolocation\nin your web content.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a structure with a Geolocation field like in the image above.\n\\item\n  Create a new template and select the structure you just created with\n  the geolocation field.\n\\item\n  Scroll down to the \\emph{Script} heading and locate the \\emph{Fields}\n  section. Here are \\emph{Content} and \\emph{Geolocation} snippets.\n\\item\n  Click on the snippets to add them to the template and \\emph{Save}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-geolocation-template.png}\n\\caption{Add the Content and Geolocation snippets to create your web\ncontent template quickly.}\n\\end{figure}\n\nTo set your location for the web content, you can share your location\nwith the browser, type a specific address into the address bar on the\nmap, or even drag the indicator and drop it in any point in the map and\nthe address is automatically updated to reflect the new point. Once the\nweb content is saved, the location is added as metadata to the web\ncontent.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-geo-create.png}\n\\caption{You can enter your location in the address bar, move the\nindicator to a location, or share your location with the browser.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\nNote: Depending on your browser settings, you may need to configure it\nto share your location.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/share-location-dialog.png}\n\\caption{Make sure your browser is configured to share your location.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\section{Geolocating Data Lists}\\label{geolocating-data-lists}\n\nTo use geolocation in your dynamic data lists, you must first create a\ndata definition that includes a geolocation field.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Product Menu and navigate to \\emph{Content \\& Data} →\n  \\emph{Dynamic Data Lists}.\n\\item\n  Click the \\emph{Options} menu and select \\emph{Manage Data\n  Definitions}.\n\\item\n  Click the \\emph{Add} button to create a new data definition.\n\\item\n  Enter a name, optional description, and parent data definition if you\n  have one.\n\\item\n  Scroll down and add a \\emph{Geolocation} field to the data definition,\n  along with any other fields you wish to add and \\emph{Save}.\n\\item\n  Go back to the Dynamic Data Lists screen and click the \\emph{Add}\n  button (\\includegraphics{./images/icon-add.png}) to create a new list.\n\\item\n  Enter a name and optional description.\n\\item\n  Finally, click the \\emph{Select} button and choose the newly created\n  data definition.\n\\end{enumerate}\n\nNow that your data list is complete, you can use the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-data-lists}{Data List\nDisplay portlet} to display it.\n\n\\section{Geolocating Documents and\nMedia}\\label{geolocating-documents-and-media}\n\nTo enable geolocation in Documents and Media, you must first create a\ndocument type that includes geolocation metadata. You can add\ngeolocation metadata as part of a Metadata Set or as part of the new\ndocument type. To add geolocation metadata as part of a Metadata Set:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Product Menu and navigate to \\emph{Content \\& Data} →\n  \\emph{Documents and Media}. Open the \\emph{Options} menu, and select\n  \\emph{Metadata Sets}.\n\\item\n  Click the \\emph{Add} (\\includegraphics{./images/icon-add.png}) button\n  and enter a name, optional description, and Parent Metadata Set if you\n  have one.\n\\item\n  Scroll down and add a \\emph{Geolocation} field, along with any\n  additional fields you wish to have, and \\emph{Save}.\n\\end{enumerate}\n\nTo create the new document type with geolocation:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Documents and Media}, open the \\emph{Options} menu\n  and select \\emph{Document Types}.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  and enter a name and optional description.\n\\item\n  Scroll down to the Main Metadata Fields heading and add a\n  \\emph{Geolocation} field along with any other fields you wish to have\n  for the document type.\n\\item\n  If you are using a Metadata Set, scroll down to the Additional\n  Metadata Fields heading, click the \\emph{Select Metadata Set} button.\n\\item\n  Choose your Metadata Set with the geolocation metadata and\n  \\emph{Save}.\n\\item\n  Navigate back to the \\emph{Documents and Media} screen and click the\n  \\emph{Add} button (\\includegraphics{./images/icon-add.png}) and select\n  your newly created document type.\n\\item\n  Fill out the information for the document, and just as with the web\n  content, your location is automatically obtained from the browser and\n  added to your document.\n\\end{enumerate}\n\nOnce your assets are geolocation enabled, you can use the\n\\href{/docs/7-2/user/-/knowledge_base/u/publishing-assets}{Asset\nPublisher} to display the location of the assets on a map, using the map\ndisplay template. Check out the\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-display-settings}{Configuring\nDisplay Settings} section to learn more.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/geo-map.png}\n\\caption{The Asset Publisher can display your geolocated assets on a\nmap.}\n\\end{figure}\n\n\\chapter{Publishing Content\nDynamically}\\label{publishing-content-dynamically}\n\nMost content types are Assets. In the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-web-content}{Creating\nWeb Content} articles, you examined the most common type of asset: web\ncontent. Other types of assets include blog posts, wiki articles,\nmessage board posts, bookmarks, and documents. Developers can define\ncustom asset types that use the asset framework, which provides support\nfor tags, categories, vocabularies, comments, ratings, and asset\nrelationships.\n\nThe Asset Publisher application displays assets. It has many\nconfiguration options which you'll cover in this chapter. By default,\nAsset Publisher displays abstracts (previews) of recently published\nassets with links to their full views. You can configure the Asset\nPublisher app to display a table of assets, a list of asset titles, or\nthe full content of assets. You can also make it display only certain\nkinds of assets, and you choose how many items to display in a list.\n\nYou might use Asset Publisher to display chosen content types, recent\ncontent, or content by tags and categories.\n\nThis section covers the following topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Adding relationships between assets\n\\item\n  Publishing assets\n\\item\n  Publishing RSS feeds\n\\item\n  Restoring deleted assets\n\\end{itemize}\n\nThe first thing you'll learn about is tagging and categorizing content.\n\n\\chapter{Defining Content\nRelationships}\\label{defining-content-relationships}\n\nRelated Assets are assets connected to other assets, even if they don't\nshare any tags and aren't in the same category. Here you'll focus on how\nto define relationships between assets so when you begin publishing\nassets, the Related Assets widget can display those relationships.\n\n\\section{Related Assets Widget}\\label{related-assets-widget}\n\nBy default, the Related Assets widget displays any related asset of the\nasset selected in the Asset Publisher. If you don't want to show every\nrelated asset, you can configure what content relationships to display.\nTo do this, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the Related Assets app and select the \\emph{Options} icon\n  (\\includegraphics{./images/icon-app-options.png}) in the upper right\n  corner of the application and click \\emph{Configuration}.\n\\item\n  Under the \\emph{Setup} → \\emph{Asset Selection} tab, set the type of\n  asset(s) to display using the \\emph{Asset Type} menu. The default\n  value is set to \\emph{Any}.\n\\item\n  You can narrow the scope of the app to display any single category of\n  asset type or select multiple assets from the menu.\n\n  Filter options set minimum requirements for displaying assets by their\n  categories, tags, and custom fields. Ordering and Grouping organizes\n  assets using the same criteria. Display settings customize how the app\n  shows assets: by title, in a table, by abstract, or full content. You\n  can convert assets to different document types like ODT, PDF, and RTF.\n  You can choose to show metadata fields such as author, modification\n  date, tags, and view count. You can even enable RSS subscriptions and\n  customize their display settings.\n\\item\n  When you're finished setting the Source and Filter options, click\n  \\emph{Save}.\n\\end{enumerate}\n\nNow that you've configured the Related Assets widget to display specific\ncontent types, you must define the relationships for your assets. Here's\na simple example of defining related assets for a web content article\nand then displaying those related assets.\n\nSuppose you own a gift shop at the Lunar Resort, and you want all your\nshop's assets to appear when an asset is clicked. You must define\nrelationships between your content, so when an asset is clicked, its\nrelated assets are appear alongside the clicked asset. Here's how to do\nit:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a blog entry explaining your gift shop's new apparel and a\n  photo of the moon, just so consumers are aware that you offer the\n  \\emph{only} gift shop on a desolate rock orbiting the Earth!\n\\item\n  Create a web content article describing your shop. Once you've given\n  your article a title and some content, open the \\emph{Related Assets}\n  dropdown menu. Click \\emph{Select}, choose \\emph{Blogs Entry}, and\n  select the blog you created. Click \\emph{Select} again, choose\n  \\emph{Basic Document}, and select the photo of the moon. Click\n  \\emph{Publish} to publish your web content article.\n\\item\n  Now that those assets are created, you can relate the blog entry and\n  photo to your web content article. Navigate to your article in Site\n  Administration → \\emph{Content \\& Data} → \\emph{Web Content}.\n\\item\n  You've now defined relationship with your three assets. Click the\n  \\emph{Add} icon (\\includegraphics{./images/icon-add.png}) at the top\n  of your page in the Control Menu, select \\emph{Widgets}, and add the\n  Related Assets and Asset Publisher widgets to the page. Don't panic:\n  related assets don't appear until you select an asset in the Asset\n  Publisher.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/related-assets-app-1.png}\n\\caption{Select an asset in the Asset Publisher to see its related\nassets displayed in the Related Assets application.}\n\\end{figure}\n\nOnce you select an asset, its related assets appear in the Related\nAssets app, as in the image above. If you want more detail, you can\nplace two Related Assets widgets on the page and name one \\emph{Related\nBlogs} and the other \\emph{Related Photos}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/related-assets-app-2.png}\n\\caption{Related Assets applications can be configured to display\nspecific content.}\n\\end{figure}\n\nNext, you'll learn more about how to use the Asset Publisher.\n\n\\chapter{Publishing Assets}\\label{publishing-assets}\n\nAs you create web content, remember that pieces of content are assets,\njust like message board entries and blog posts. Since the Asset\nPublisher publishes assets, it excels at publishing mixed content types\nlike images, documents, blogs, and of course, web content. This helps in\ncreating a more dynamic web site: you can place user-created wiki\nentries, blog posts, or message board messages in context with your web\ncontent. You'll examine some of the Asset Publisher's features next.\n\n\\chapter{Querying for Content}\\label{querying-for-content}\n\nThe Asset Publisher works by querying for mixed types of content on the\nfly. Since you can control what and how content is displayed from one\nlocation, the Asset Publisher helps to ``bubble up'' the most relevant\ncontent to your users.\n\nTo get to all the application's options, click the \\emph{Options} icon\n(\\includegraphics{./images/icon-app-options.png}) in the application's\nmenu. If you click the \\emph{Configuration} option and then \\emph{Setup}\n(if necessary), you can configure the Asset Publisher's settings from\nthe following three areas:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Asset Selection\n\\item\n  Display Settings\n\\item\n  Subscriptions\n\\end{itemize}\n\nAsset Selection configures which assets are displayed. You can set asset\nselection to\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Dynamic}\n\\item\n  \\emph{Manual}\n\\item\n  \\emph{Content Set}\n\\item\n  \\emph{Content Set Provider}\n\\end{itemize}\n\nDynamic displays assets based on certain rules or filters. For example,\nyou can set the Asset Publisher to display only assets of a certain type\nor to which certain tags or categories have been applied. Manual asset\nselection only displays assets that have been explicitly selected by an\nadministrator. For more information on Content Sets and their place in\nthe Asset Publisher, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/content-sets}{Managing Content\nSets} section.\n\nThe Asset Publisher supports a scope that restricts both dynamic and\nmanual asset selection. The Asset Publisher can only display assets from\nits configured scope. By default, the Asset Publisher app is scoped to\nthe site of the page to which it was added. You can, however, customize\nthe scope from the Asset Selection section of the Asset Publisher\nconfiguration window. To extend your Asset Publisher's scope,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Select} under Scope.\n\\item\n  Choose \\emph{Global} to add the global scope or \\emph{Other\n  Site\\ldots{}} to add the scope of another site.\n\\end{enumerate}\n\nThe Display Settings section of the Asset Publisher configuration window\nis for customizing how content is displayed. The Subscription section\nenables, disables, or configures email subscriptions and RSS\nsubscriptions. In the following sections, you'll explore the available\nconfigurations for the Asset Selection, Display Settings, and\nSubscriptions sections of the Asset Publisher's configuration window.\nYou'll start by learning how select content manually. You'll see that\nit's very similar to using the Web Content Display application except\nthat you can select assets of any type, not just web content articles.\n\n\\chapter{Selecting Assets}\\label{selecting-assets}\n\nYou can configure Asset Publisher to select assets manually or\ndynamically through various criteria. Within those options there is\nflexibility in what assets are displayed and how they are displayed.\n\n\\section{Selecting Assets Manually}\\label{selecting-assets-manually}\n\nTo enable manual asset selection,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the click the \\emph{Options} icon\n  (\\includegraphics{./images/icon-app-options.png}) in the widget's\n  menu.\n\\item\n  Select \\emph{Configuration}.\n\\item\n  In the Asset Publisher configuration, select \\emph{Manual} from the\n  select box beneath \\emph{Asset Selection}.\n\\end{enumerate}\n\nNow you must select a \\emph{Scope} and specific \\emph{Asset Entries}\nfrom that scope to display. You can configure multiple scopes, including\nthe global scope, from which to select assets.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-asset-publisher-manual.png}\n\\caption{Selecting assets in the Asset Publisher manually is similar to\nselecting assets in the Web Content Display application except that you\ncan select assets of any type, not just web content. You can also add\nscopes to expand the list of assets that are available to be displayed\nin the Asset Publisher.}\n\\end{figure}\n\nWhen selecting assets manually, a list of configured scopes appears\nunder the Scope heading. You can configure the scope like this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{X} button at the right to remove a scope from the\n  list.\n\\item\n  Click the \\emph{Select} button to add additional scopes to the Asset\n  Publisher's configuration.\n\\item\n  After you've added a scope, a new Select button appears under the\n  Asset Entries heading. A list of assets selected for display appears\n  in the Asset Entries section. You can select assets to be displayed by\n  clicking the appropriate \\emph{Select} button. One button appears for\n  each configured scope. By default, these are the available asset\n  types:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Blogs Entry\n  \\item\n    Bookmarks Entry\n  \\item\n    Bookmarks Folder\n  \\item\n    Calendar Event\n  \\item\n    Basic Document\n  \\item\n    Google Drive Shortcut\n  \\item\n    Documents Folder\n  \\item\n    Dynamic Data Lists Record\n  \\item\n    Knowledge Base Article\n  \\item\n    Message Boards Message\n  \\item\n    Basic Web Content\n  \\item\n    Web Content Folder\n  \\item\n    Wiki Page\n  \\end{itemize}\n\n  You can select any number of assets to be displayed. Note, however,\n  that there's a display setting called \\emph{Number of Items to\n  Display} that determines the maximum number of items to display (or,\n  if pagination is enabled, the maximum number of items to display per\n  page). The Asset Publisher can mix and match different asset types in\n  the same interface.\n\\item\n  When you're done selecting items to display, click \\emph{Save}. Any\n  selected assets are added to the list of assets that are displayed by\n  the application.\n\\end{enumerate}\n\nOnce you have your content selected, you can configure the display types\nto configure how the content appears. You'll explore the display\nsettings in more detail after we finish discussing how to select assets\nfor display.\n\nManual asset selection lets you select assets of various types from\ndifferent scopes, but it can be time-consuming to update the assets that\nshould be displayed. It's often more convenient to use the Asset\nPublisher to select content dynamically.\n\n\\section{Selecting Assets\nDynamically}\\label{selecting-assets-dynamically}\n\nThe Asset Publisher's default behavior is to select assets dynamically\naccording a set of customizable rules. These rules can be combined so\nthat they compliment each other to create a nice, refined query for your\ncontent. Assets are filtered by permissions automatically, no matter how\ncomplicated your asset selection rules are. You have the following rule\ntypes:\n\n\\textbf{Scope:} Choose the sites containing the content that should be\nselected. This works the same way as with manual asset selection: assets\ncan only be displayed if they belong to a configured scope. The\nfollowing scope options are available:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Current Site}\n\\item\n  \\emph{Global}\n\\item\n  \\emph{Other Site}\n\\end{itemize}\n\nThe Other Site scope option is unavailable for Asset Publisher\napplications configured on a page template (e.g., Content Display Page).\n\n\\textbf{Asset Type:} Choose the asset types you want, from all assets,\nto only one, or any combination in between. For example, you could\nchoose only web content, only wiki entries, or any combination of\nmultiple types.\n\n\\textbf{Filter:} Add as many filters on tags or categories as you like.\nYou can choose whether the content must contain or must not contain any\nor all of the tags or categories that you enter.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-asset-publisher-filter.png}\n\\caption{You can filter by tags and categories, and you can set up as\nmany filter rules as you need.}\n\\end{figure}\n\nOnce you've set up your filter rules for dynamically selecting content,\nyou can decide how the content is displayed.\n\nIf you've added custom User profile attributes, you can configure the\nAsset Publisher to display assets that match them. This setting\nretrieves assets that have matching categorization. These categories\nmust be from the global context. For example, suppose a User has a\ncustom field called \\emph{Location} with the type \\emph{Text}. If this\nattribute is set to \\emph{Moon}, you could create a vocabulary called\n\\emph{Location} and a category for the Location vocabulary called\n\\emph{Moon}. Then you could categorize content with \\emph{Moon} in the\n\\emph{Location} vocabulary. With this organizational setup, adding an\nAsset Publisher and specifying \\emph{Location} as the Asset Publisher's\ncustom user attribute would only display content that had been\ncategorized as \\emph{Moon}. Pretty cool, right?\n\nSee\n\\href{/docs/7-2/user/-/knowledge_base/u/defining-categories-for-content}{Defining\nCategories for Content} for further information.\n\nIn addition, you can use these advanced filters:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Show only assets with} \\textbf{\\emph{Welcome}} \\textbf{as its\n  display page} displays only assets specifically configured for the\n  \\emph{Welcome} page.\n\\item\n  \\textbf{Include tags specified in the URL?} lets you specify tags in\n  the URL for the Asset Publisher to display.\n\\end{itemize}\n\nThe \\emph{Ordering} section of the Asset Publisher precisely controls\nhow content is ordered and grouped when displayed. You can order the\nassets displayed by Asset Publisher in ascending or descending order by\nthe following attributes:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Title\n\\item\n  Create Date\n\\item\n  Modified Date\n\\item\n  Publish Date\n\\item\n  Expiration Date\n\\item\n  Priority\n\\end{itemize}\n\nSay you have a series of ``How To'' articles that you want displayed in\ndescending order based on whether the article was tagged with the\n\\emph{hammer} tag. Or suppose you want a series of video captures to\nappear in ascending order based on a category called \\emph{birds}. For\nthese use cases, you can configure the ordering and grouping settings.\n\nYou can also configure a second ordering. The second ordering is applied\nto any assets for which the first ordering wasn't sufficient. For\nexample, if you ordered assets by title and there are multiple assets\nwith the same title, the second ordering takes effect, perhaps the\npublication date.\n\nYou can establish grouping rules as well as ordering rules. You can\ngroup assets by type or by vocabulary. For example, suppose you have a\nvocabulary called \\emph{Membership Type} with two categories:\n\\emph{Premium} and \\emph{Regular}. If you group assets by Membership\nType, all assets with the Premium category appear in one group and all\nassets with the Regular category appear in another group. Grouping rules\nare applied before any ordering rules: they're a way to divide up the\ndisplayed assets into separate lists. The ordering rules are applied\nseparately to each group of assets.\n\nNote that ordering rules are only one way to control how your content\nappears. You can refine the display through many other display settings\nwhich you'll examine next.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The following actions have immediate effects in your\nAsset Publisher:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Change the value of the \\emph{Asset Selection} option.\n\\item\n  Change the value of the \\emph{Scope} option.\n\\item\n  Select, add, sort or delete asset entries (only when selecting assets\n  manually).\n\\end{itemize}\n\n\\noindent\\hrulefill\n\nOther changes happen after clicking \\emph{Save}. Next you'll learn about\nthe Asset Publisher's other configuration options.\n\n\\section{Selecting a Content Set}\\label{selecting-a-content-set}\n\n7.0 adds the Content Set feature to create predefined lists of content\nto display in the Asset Publisher. To use a Content Set,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the click the \\emph{Options} icon\n  (\\includegraphics{./images/icon-app-options.png}) in the Asset\n  Publisher's menu.\n\\item\n  Select \\emph{Configuration} from menu.\n\\item\n  Select \\emph{Manual} from the select box beneath the \\emph{Asset\n  Selection} tab.\n\\item\n  Choose the Content Set that you want to use.\n\\end{enumerate}\n\nFor more information on using Content Sets, see\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-sets}{Creating\nContent Sets}.\n\n\\chapter{Configuring Display\nSettings}\\label{configuring-display-settings}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nFrom the Asset Publisher's configuration page, open the Setup tab's\n\\emph{Display Settings} sub-tab. This section gives you precise control\nover the display of your assets. There are many options available to\nconfigure how you want your content to appear. Many of these, such as\nprinting, flags, ratings, comments, comment ratings, and social\nbookmarks work the same way they do in the Web Content Display\napplication.\n\n\\textbf{Display Template}: This selector lets you choose an application\ndisplay template to customize how the Asset Publisher displays assets.\nThese templates are in every site by default:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Abstracts:} Shows the first 200-500 characters of the content,\n  defined by the \\textbf{Abstract Length} field. This is the default\n  display template.\n\\item\n  \\emph{Table:} Displays the content in an HTML table which can be\n  styled by a theme developer.\n\\item\n  \\emph{Title List:} Displays the content's title as defined by the user\n  who entered it.\n\\item\n  \\emph{Full Content:} This display template displays the entire content\n  of the entry.\n\\end{itemize}\n\nThere's also the \\emph{Rich Summary} and \\emph{Map} display templates\nthat belong to the global scope. The Rich Summary template provides a\nsummary view of each asset along with a \\emph{Read More} link to the\narticle's full content. The Map template displays\n\\href{/docs/7-2/user/-/knowledge_base/u/geolocating-assets}{geo-localized\nassets} in either a Google Map or an Open Street Map provider. The map\nprovider can be configured in Instance Settings, and Site Settings in\nthe Advanced section.\n\n\\textbf{Abstract Length}: Select the number of characters to display for\nabstracts. The default is \\texttt{200}.\n\n\\textbf{Asset Link Behavior:} The default value is \\emph{Show Full\nContent}, which displays the full asset in the current Asset Publisher.\n\\emph{View in a Context} causes that asset to be displayed in the\napplication where the asset belongs. For example, a blog entry is\ndisplayed in Blogs where it was created. See the section below on\ndisplay pages for more information.\n\n\\textbf{Number of Items to Display}: Select the maximum number of assets\nthat can be displayed by the Asset Publisher. If pagination is enabled,\nthis number represents the maximum number of assets that can be\ndisplayed per page.\n\n\\textbf{Pagination Type}: This can be set to \\emph{None}, \\emph{Simple},\nor \\emph{Regular}. \\emph{None} displays at most the number of assets\nspecified in the \\textbf{Number of Items to Display} property.\n\\emph{Simple} adds Previous and Next buttons for browsing through pages\nof assets in the Asset Publisher. \\emph{Regular} adds more options and\ninformation including First and Last buttons, a dropdown selector for\npages, the number of items per page, and the total number of results\n(assets being displayed).\n\n\\textbf{Show Add Content Button}: When selected, an \\emph{Add New}\nbutton appears that lets users add new assets directly from the Asset\nPublisher application. This is checked by default.\n\n\\textbf{Show Metadata Descriptions:} Enables Metadata descriptions such\nas \\emph{Content Related to\\ldots{}} or \\emph{Content with tag\\ldots{}}\nto be displayed with the published assets.\n\n\\textbf{Show Available Locales:} Since content can be localized, you can\nhave different versions of it based on locale. Enabling this option\nshows the locales available, so users can view the content in their\nlanguages.\n\n\\textbf{Set as the Default Asset Publisher for This Page}: The Asset\nPublisher app is an instanceable app: multiple Asset Publishers can be\nadded to a page and each has an independent configuration. The default\nAsset Publisher for a page is the one used to display web content\nassociated with the page.\n\n\\textbf{Show only assets with Home as its display page template:}\nDisplay assets that only exist on the Home page template.\n\n\\textbf{Include tags specified in the URL:} Incorporate tags specified\nin the URL.\n\n\\textbf{Enable \\ldots{}}: Enable/disable the following options for\ndisplayed assets:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Print\n\\item\n  Flags\n\\item\n  Ratings\n\\item\n  Related Assets\n\\item\n  Subscribe\n\\item\n  Comments\n\\item\n  Comment Ratings\n\\item\n  View Count Increment\n\\end{itemize}\n\nThe Print option adds a \\emph{Print} link to the full view of an asset\ndisplayed in the Asset Publisher. Clicking \\emph{Print} opens a new\nbrowser window with a print view of the asset. Enabling flags, related\nassets, ratings, comments, comment ratings, or social bookmarks add\nlinks to the corresponding social features to the view full of the asset\nin the Asset Publisher.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} An alternate way to add flags, comments, and ratings to a\npage is through the \\emph{Page Flags}, \\emph{Page Comments}, and\n\\emph{Page Ratings} applications. Just add the applications in the\nappropriate location near the asset that should have feedback.\n\n\\noindent\\hrulefill\n\n\\textbf{Metadata:} Select various metadata types to be displayed (see\nbelow). For example, you can select tags and categories for display.\nUpon saving your configuration, the Asset Publisher displays tags and\ncategories for each displayed asset. Then users can click on the tags\nand categories to filter the displayed assets manually.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/available-metadata-fields.png}\n\\caption{You can configure the Asset Publisher to display various kinds\nof metadata about the displayed assets.}\n\\end{figure}\n\nNext you'll learn about configuring subscriptions for email and RSS\nthrough the Asset Publisher.\n\n\\chapter{Configuring Asset Publisher\nSubscriptions}\\label{configuring-asset-publisher-subscriptions}\n\nThe Asset Publisher application supports two kinds of subscriptions\nemail subscriptions. To enable subscriptions, click the Asset\nPublisher's Options icon and select \\emph{Configuration}. In the\nconfiguration window, open the Setup tab's Subscriptions tab. There you\ncan enable/disable the \\emph{Enable Email Subscription} selector.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/asset-publisher-email.png}\n\\caption{An email subscription notifies users when new assets are\npublished.}\n\\end{figure}\n\nEnabling Email Subscription adds a \\emph{Subscribe} link to the Asset\nPublisher. Users wishing to be notified of newly published assets can\nclick on this link to be added to the subscription list. Liferay DXP\nperiodically checks for new assets and sends emails to subscribed users\ninforming them about the new assets. By default, Liferay performs this\ncheck every twenty-four hours.\n\n\\chapter{Publishing RSS Feeds}\\label{publishing-rss-feeds}\n\nRSS is a family of web feed formats used to publish frequently updated\nworks such as blog entries and news articles. RSS allows users to stay\nup-to-date with your site's content without actually having to visit\nyour site. Users can use their own RSS feed readers to aggregate\ncontent, and you can also use RSS to share and aggregate content across\nsites. Next, you'll see how to create RSS feeds from Asset Publisher\nconfigurations.\n\n\\chapter{Configuring RSS Feeds}\\label{configuring-rss-feeds}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} RSS feeds are deprecated for Liferay DXP 7.2 and are\ndisabled by default. To leverage RSS feeds, you must enable this\nfeature. Go to the Control Panel → \\emph{Configuration} → \\emph{System\nSettings} → \\emph{Web Content}. Under the \\emph{System Scope} →\n\\emph{Administration} tab, check the \\emph{Show Feeds} box. For more\ninformation on deprecated apps, see\n\\href{/docs/7-2/deploy/-/knowledge_base/d/deprecated-apps-in-7-2-what-to-do\\#web-experience}{this\narticle}.\n\n\\noindent\\hrulefill\n\nTo manage a Liferay site's RSS feeds, navigate to your Site's Site\nAdministration → \\emph{Content \\& Data} → \\emph{Web Content}. Site\nadministrators can use this Web Content menu option to manage their\nsite's web content, including web content structures and templates,\nwhich you learned in the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-web-content}{Creating\nWeb Content} section. Site administrators can also use this option to\nmanage their site's RSS feeds. To add a new feed:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\emph{Feeds} tab.\n\\item\n  Click the \\emph{Add Feed} button.\n\\item\n  Enter a \\emph{Name}, select a \\emph{Target Page}, and select a\n  \\emph{Web Content Structure} for the feed.\n\\end{enumerate}\n\nA feed's target page serves two purposes:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The site the target page belongs to determines which web content\n  articles appear in the feed. For example, if the target page belongs\n  to the Marketing site, only web content articles belonging to the\n  Marketing site appear in the feed.\n\\item\n  The target page is the page where ``orphaned'' web content articles\n  are displayed. Orphaned web content articles have been published in\n  your Site but are not displayed in specific Web Content Display\n  applications. Liferay RSS feeds can provide links to any published web\n  content articles, both orphaned articles and articles that have been\n  configured to be displayed in specific Web Content Display\n  applications. For articles that have been configured to be displayed,\n  the RSS feeds' links point to the Liferay page of that app. For\n  orphaned articles, the RSS feeds' links point to the feed's target\n  page. When users click on such links for orphaned articles, the full\n  content of the orphaned article is displayed on the target page.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-new-feed.png}\n\\caption{To create a new RSS feed, you only need to specify a name,\ntarget page, and web content structure. Of course, you can also\nconfigure other features of the feed such as its permissions, web\ncontent constraints, and presentation settings.}\n\\end{figure}\n\nTo specify a target page, you must enter the target page's friendly URL.\nNote that friendly URLs don't include the host name. For example, the\nfriendly URL of a public page called \\emph{Welcome} belonging to a Site\ncalled \\emph{Marketing} might look like this:\n\\texttt{/web/marketing/welcome}. Optionally, you can specify a target\nportlet ID. This would be the portlet ID of a Web Content Display\napplication on the target page in which orphaned web content should be\ndisplayed. The application must exist or else the content isn't\ndisplayed. The URL field contains the address of your RSS feed. It\nappears after you've actually created the feed by clicking \\emph{Save}.\n\nThe final two sections of the \\emph{Add Feed} form are for customizing\nthe web content articles that appear in your feed.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Web Content Constraints selects a web content structure to filter the\n  articles that appear in your feed. This is useful since all web\n  content articles are created using web content structures.\n\\item\n  Presentation Settings customizes additional details about your feed\n  and how articles are displayed in your feed. Leave the Feed Item\n  Content set to \\emph{Web Content Description} if you want a\n  description of each article to appear in your feed. Set it to\n  \\emph{Rendered Web Content: Use Default Template} if you want the full\n  content of each article to appear in the feed. Customizing the Feed\n  Type allows you to choose which web feed language to use for your\n  feed. You can choose \\emph{Atom 1.0} (the default), \\emph{RSS 1.0}, or\n  \\emph{RSS 2.0}. Customize the \\emph{Maximum Items to Display} to\n  choose the maximum number of articles should appear in your feed at\n  one time. Leave the Order By Column set to \\emph{Modified Date} to\n  have articles arranged in order from the last time they were published\n  or modified. You can set the Order by Column to \\emph{Display Date} if\n  you want to have articles arranged in order from the time they were\n  configured to be displayed in a specific Web Content Display\n  application. Lastly, you can leave the Order by Type set to\n  \\emph{Ascending} to have the oldest articles at the top of the feed or\n  you can set it to \\emph{Descending} to have the newest articles at the\n  top of the feed.\n\\end{enumerate}\n\nWhen you're done configuring your RSS feed, click \\emph{Save} to create\nyour feed.\n\nOnce one or more feeds have been created, they'll appear in a list in\nthe Feeds tab. You can edit existing feeds using the same form used for\ncreating them. The main difference is that when you edit an existing\nfeed, the URL field is populated. Copy this URL into a new browser tab\nor window to test your feed. From the Feeds popup window, you can also\ncustomize the permissions of feeds or delete feeds.\n\nIt's possible to completely disable RSS feeds at the instance level. You\ncan do this by setting the \\texttt{rss.feeds.enabled} property to\n\\texttt{false} in your \\texttt{portal-ext.properties} file. By default,\nit's set to \\texttt{true}. If you keep the default, RSS enabled, you can\nmake several other RSS property customizations. Please refer to the\n\\href{https://docs.liferay.com/ce/portal/7.2-latest/propertiesdoc/portal.properties.html\\#RSS}{RSS\nsection} of your \\texttt{portal.properties} file for details.\n\n\\chapter{The RSS Publisher Widget}\\label{the-rss-publisher-widget}\n\nThe RSS Publisher widget displays RSS feeds. If you're looking for a\nweb-based RSS reader, look no further: just add the RSS Publisher widget\nto one your personal Site's private pages, and \\emph{voila}! You have\nyour own personal RSS reader. You can select the RSS feeds the widget\ndisplays and how it displays them. The RSS Publisher widget can also be\nplaced on Sites' public or private pages to make feeds available to\nguests or Site members, respectively. In these cases, make sure that\nonly Site administrators have permission to customize the RSS widget and\nselect feeds to be displayed.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/rss-widget-default-view.png}\n\\caption{The RSS Publisher widget lets you display RSS feeds of your\nchoosing.}\n\\end{figure}\n\n\\textbf{Note:} If you run your server behind a proxy, you must set the\nappropriate Java proxy settings (such as \\texttt{http.proxyHost=} and\n\\texttt{http.proxyPort=}) in your \\texttt{setenv} script or in your\n\\texttt{system-ext.properties}. Without these properties, the RSS\nPublisher widget can't access any RSS feeds.\n\nNote that the RSS Publisher widget is deprecated. In Liferay CE Portal\n7.1 GA2+, and Liferay DXP 7.1 FP4+, the widget is available from the\n\\emph{Add} (\\includegraphics{./images/icon-add-app.png}) →\n\\emph{Widgets} → \\emph{News} menu. However, the widget is hidden in\nearlier releases of Liferay CE Portal 7.1 and Liferay DXP 7.1. In these\nreleases, you must therefore make the widget visible via a configuration\nfile. The next section shows you how to do this.\n\n\\section{Using the RSS Publisher\nWidget}\\label{using-the-rss-publisher-widget}\n\nYou can add the RSS Publisher widget to a page from the \\emph{Add}\n(\\includegraphics{./images/icon-add-app.png}) → \\emph{Widgets} →\n\\emph{News} menu. Once you've done so, open the widget's Configuration\nmenu by clicking on the \\emph{Options} icon\n(\\includegraphics{./images/icon-app-options.png}) at the top-right\ncorner of the widget and selecting \\emph{Configuration}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/rss-widget-config.png}\n\\caption{The RSS Publisher widget's configuration lets you customize how\nthe widget displays RSS feeds.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/rss-widget-config-feeds.png}\n\\caption{You can also use the RSS Publisher widget's configuration to\nspecify which feeds to display.}\n\\end{figure}\n\nBy default, the RSS Publisher widget displays one feed. In the\n\\emph{Feeds} section, add or remove a feed via the plus or minus\nbuttons, respectively. To add a feed, enter its URL and title in the\nrespective fields. If you leave the \\emph{Title} field blank, the feed's\ndefault title is used (the \\emph{Title} field is for custom titles).\n\nIn the top section, use the following toggles to enable/disable the\ndisplay of the feed's details:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Show Feed Title\n\\item\n  Show Feed Published Date\n\\item\n  Show Feed Description\n\\item\n  Show Feed Image\n\\item\n  Show Feed Item Author\n\\end{itemize}\n\nYou can also select the number of entries and expanded entries that\nshould be displayed per feed. Expanded entries show more of an article's\nactual content than regular entries. By default, each feed shows four\nentries per feed and eight expanded entries per feed. You can set the\nfeed image alignment to control whether feed images appear to the right\nor left of the text. By default, the feed image alignment is set to\n\\emph{Right}.\n\n\\chapter{Restoring Deleted Assets}\\label{restoring-deleted-assets}\n\nHave you ever had that life-altering experience where you deleted an\nimportant file and immediately regretted deleting it? The deed is\nusually followed by a palm to the forehead or a sick feeling. Good news!\nLiferay DXP is here to turn that frown upside down with the\n\\emph{Recycle Bin} feature. With the Recycle Bin, the \\emph{Move to the\nRecycle Bin} action replaces \\emph{Delete} for certain asset types.\nContent is now temporarily stored in the Recycle Bin. This allows the\ncontent to be restored back to its original state. Recycled items can\nexpire after a certain period of time, resulting in their permanent\ndeletion. Before diving into how the Recycle Bin works, you'll look at\nhow to configure it.\n\n\\chapter{Configuring the Recycle Bin}\\label{configuring-the-recycle-bin}\n\nThe Recycle Bin supports instance-wide scope or site-specific scope. The\ninstance-wide scope of the Recycle Bin is set by adding the\n\\texttt{trash.enabled} property to your \\texttt{portal-ext.properties}\nfile. By default, the Recycle Bin is enabled instance-wide. You'll go\ninto more detail for adding this property and several others to your\nproperties file later in the section. First, you'll explore the UI and\nsee what the Recycle Bin can do.\n\nFirst, you'll configure the Recycle Bin for site-specific scoping.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Choose the Site you'd like configure for the Recycle Bin from the Site\n  Administration menu.\n\\item\n  Click \\emph{Configuration} → \\emph{Settings}.\n\\item\n  Next, select the top \\emph{Advanced} tab and click \\emph{Recycle Bin}.\n  You'll notice a few configurable options.\n\n  \\textbf{Enable Recycle Bin:} enable and disable settings for the\n  Recycle Bin's site-specific scope.\n\n  \\textbf{Trash Entries Max Age:} customize the number of minutes a file\n  is kept in the Recycle Bin until its permanent deletion (default is\n  43200 minutes, or 30 days).\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/recycle-bin-site-settings.png}\n  \\caption{The Recycle Bin offers several configurable options for your\n  site.}\n  \\end{figure}\n\\item\n  When you've finished configuring your Recycle Bin settings, click\n  \\emph{Save}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you disable the Recycle Bin while it's still holding\nrecycled items, the recycled items remain stored and reappear in the\nRecycle Bin if it is re-enabled.\n\n\\noindent\\hrulefill\n\nYou can also configure the Recycle Bin via properties in the\n\\texttt{portal.properties} file. Remember that it's a best practice not\nto edit the \\texttt{portal.properties} directly, but to create a\nseparate \\texttt{portal-ext.properties} file containing the properties\nto override. There are some additional options not available in the GUI\nthat you can set:\n\n\\texttt{trash.search.limit=500}: set the limit for results used when\nperforming searches in the Recycle Bin (default is 500).\n\n\\texttt{trash.entry.check.interval=60}: set the interval in minutes for\nhow often the trash handler runs to delete trash entries that have been\nin the Recycle Bin longer than the maximum age (default is 60).\n\nAlso, as was mentioned earlier, there are properties to enable the\nRecycle bin instance-wide and set trash entries' maximum age.\n\n\\texttt{trash.enabled=true}: set this property to \\emph{false} to\ndisable the Recycle Bin for all sites in the portal (default is\n\\emph{true}).\n\n\\texttt{trash.entries.max.age=43200}: set the number of minutes trash\nentries should be held before being permanently deleted.\n\nVisit the\n\\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Trash}{portal.properties}\nfile to view all of the configurable properties for the Recycle Bin.\n\nNext, you should make sure permissions are set properly for users who\ncan handle/view the assets in the Recycle Bin. Users who had \\emph{View}\npermissions on a document when it was recycled can also view that\ndocument in the Recycle Bin. Users who had \\emph{Update} or\n\\emph{Delete} permissions on a document when it was recycled can restore\nthe document.\n\nNow that you've successfully configured the Recycle Bin, you'll look at\nhow to use it.\n\n\\chapter{Using the Recycle Bin}\\label{using-the-recycle-bin}\n\nThe Recycle Bin is temporary storage where assets go when you delete\nthem. You can recycle several different types of assets:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Blogs\n\\item\n  Bookmarks\n\\item\n  Documents and Media\n\\item\n  Message Boards (and attachments)\n\\item\n  Web Content\n\\item\n  Wiki (and attachments)\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/recycle-bin-overview.png}\n\\caption{The Recycle Bin provides a seamless administrative experience\nfor deleting and removing content.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Attachments added to Wiki and Message Board entries do\nnot go to the Recycle Bin when they are deleted. They can be restored in\na similar fashion from the \\emph{Removed Attachments} menu within the\napplication.\n\n\\noindent\\hrulefill\n\nTo demonstrate using the Recycle Bin let's delete a web content article\nand then restore it. You'll run through two different methods of\nrestoring the file.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Site Administration and select \\emph{Content \\& Data} →\n  \\emph{Web Content}.\n\\item\n  Select the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  and click \\emph{Basic Web Content}.\n\\item\n  Enter some text for the Title and Content and click \\emph{Publish}.\n\\item\n  Click the article's \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and click \\emph{Move to\n  the Recycle Bin}.\n\n  Note that the \\emph{Delete} button is not listed. Liferay DXP avoids\n  the risk of accidental deletion of your files by funneling the content\n  through the Recycle Bin.\n\\item\n  After deleting the file, a success message appears, offering an\n  \\emph{Undo} option. Click \\emph{Undo}. The web content is retrieved\n  from the Recycle Bin and stored in its original place.\n\\item\n  Click \\emph{Move to the Recycle Bin} again.\n\\item\n  Go back to Site Administration and click Recycle Bin from the Content\n  dropdown.\n\\item\n  Find your sample web content and click its \\emph{Actions} button.\n\\item\n  You can restore or delete the content. Select \\emph{Restore}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/recycle-bin-restore.png}\n  \\caption{In the Recycle Bin, you have the option of restoring or\n  permanently deleting the content.}\n  \\end{figure}\n\\item\n  Navigate back to the Web Content screen and notice that your sample\n  web content is back to its original place.\n\\end{enumerate}\n\nThat covers the two general processes of sending and restoring content\nto/from the Recycle Bin. For other asset types, the Recycle Bin works\nsimilarly.\n\nSome applications, such as Web Content and Documents and Media, support\nfolders for organizing content. You can also send folders to the Recycle\nBin. Keep in mind that this sends any sub-folders of the deleted folder\nall the files it contains to the Recycle Bin. Folders are restored and\ndeleted the same way as a single file.\n\n\\emph{Delete} within the Recycle Bin is the permanent delete button.\nOnce you select this, your file cannot be retrieved and is gone forever.\nThere is also an \\emph{Empty the Recycle Bin} option accessible from the\n(\\includegraphics{./images/icon-options.png}) button at the top of the\nRecycle Bin screen. This permanently deletes all the files from the\nRecycle Bin.\n\n\\section{Drag and Drop}\\label{drag-and-drop}\n\nYou can also drag and drop items into the Recycle Bin. While you're in\nthe Control Panel, select an asset and drag it to the Recycle Bin\nportlet on the Control Panel menu. When you click and begin dragging the\nasset, a message appears near your cursor notifying you of the number of\nfiles ready to be moved, and the Recycle Bin is highlighted, showing you\nwhere the files can be dropped. After you drop the asset onto the\nRecycle Bin portlet, the asset is removed from its original location and\ntransferred to the Recycle Bin.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/recycle-bin-drag.png}\n\\caption{A quick and easy way of disposing your items is the drag and\ndrop method.}\n\\end{figure}\n\nAwesome! You now know how to use the Recycle Bin!\n\n\\chapter{Recycle Bin Intelligence and\nSupport}\\label{recycle-bin-intelligence-and-support}\n\nHave you ever wondered what happens to file shortcuts if their linked\nassets are recycled? What if you restore a file that has the same name\nas another file currently stored in your site/instance? The Recycle Bin\nalready knows how to handle these types of issues.\n\nWhen documents with shortcuts are moved to the Recycle Bin, the\nshortcuts are removed. This ensures that all your links and shortcuts\nwork and cuts down on maintenance time and backtracking.\n\nAnother important trait how recycled content is managed with the\n\\href{/docs/7-2/user/-/knowledge_base/u/staging}{Staging} framework.\n\nAlthough you there is only one master Recycle Bin for all asset types,\nwhen staging is enabled a \\emph{Staging} Recycle Bin is created. The\noriginal Recycle Bin, or \\emph{Live} Recycle Bin, is still viewable\nwhile in staging; however, it is never used.\n\nDuring staging, everything you recycle is sent to the Staging Recycle\nBin. This prevents staged and unstaged recycled content from mixing. For\nexample, if you have an unstaged document currently on your live site\nyou can enable staging and delete that document. If you were to turn\nstaging off and return to the live site, without separate Recycle Bins,\nthe live document would be both on your site and in the Recycle Bin!\nBecause of this, the separate Staging Recycle Bin is necessary and only\nused during the staging process. When you publish your staged material,\nthe Staging Recycle Bin content is transferred to the Live Recycle Bin.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Staging Recycle Bin saves its contents until the\nstaged material has been published to the live site. This means that you\ncan turn the staging mode on and off without losing your recycled\nmaterial.\n\n\\noindent\\hrulefill\n\nThe Recycle Bin saves you time by letting you restore content that's\nbeen recycled. Instead of recreating or re-uploading content, you'll be\ntailoring your Liferay instance to fully leverage its capabilities.\n\n\\chapter{Collaboration}\\label{collaboration}\n\nLiferay DXP contains an expansive collaboration suite that empowers\nusers to create content and communities that they couldn't create alone.\nA robust document management system is a key component of this suite. As\nusers produce digital assets---documents, videos, audio---they can store\nand share them using the Documents and Media Library. Documents and\nMedia supports file check in and check out to prevent conflicting edits\nfrom multiple users, and maintains a version history of those files. It\nalso contains its own repository, and for added flexibility can connect\nto external repositories. Once files exist in Documents and Media, users\ncan insert them in other content like blog posts and wiki articles.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-images-in-admin.png}\n\\caption{You can use the Documents and Media Library to manage and use\ndocuments in the portal.}\n\\end{figure}\n\nThe collaboration suite also contains apps that let users share\ninformation and create active communities. The Message Boards app gives\nusers a platform for discussions. The Blogs app lets users publish their\nideas using rich content. Notifications keep users informed of what's\nhappening. Social networking apps let users connect and share in ways\nthat bolster friendship and productivity. And this is just scratching\nthe surface---there are many more apps that help users communicate,\nproduce, and present.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blog-entry-abstract.png}\n\\caption{You can also make your blog entries look great.}\n\\end{figure}\n\nThe guides that follow show you how to leverage these features, and\nmore, in detail.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/message-boards-category-threads.png}\n\\caption{The Message Boards app is fantastic for facilitating\ndiscussions.}\n\\end{figure}\n\n\\chapter{Managing Documents and\nMedia}\\label{managing-documents-and-media}\n\nThe Documents and Media library stores files on the server using the\nsame type of structure that you use to store files locally. It accepts\nfiles of any kind, can serve as a virtual shared drive, and can mount\nand browse external repositories. You can organize documents using\ncustomizable document types and metadata sets and display them with\nautomatic document preview generation. Its companion app, the Media\nGallery, displays selected content from the Documents and Media library.\nIt can render image, audio, and video files.\n\nLiferay Sync synchronizes Documents and Media folders with local folders\non your devices, both your desktop machines and mobile devices.\n\nYou'll get started with Documents and Media by exploring how to publish\nfiles.\n\n\\chapter{Publishing Files}\\label{publishing-files}\n\nAs you create sites, you'll probably want to share files on them. The\nDocuments and Media library (Document Library) lets you upload and\npublish all kinds of files on your sites. Pictures, videos,\nspreadsheets, slide presentations, and more can be stored in and shared\nfrom the Document Library. Document Library instances can be scoped to a\nportal instance, site, or page, so you can work with files where they're\nrelevant.\n\nHere, you'll learn how to add files, display them, and collaborate on\nthem. You'll learn how to use both the Documents and Media Library and\nthe Media Gallery. And lastly, you'll learn how to collaborate on files\nfrom within several environments, including your browser and local\ndesktop file system.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-images-in-admin.png}\n\\caption{These documents are awesome.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-media-gallery-slideshow.png}\n\\caption{This slideshow rules.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-file-entry-details.png}\n\\caption{Viewing a file's details is fun.}\n\\end{figure}\n\n\\chapter{Adding Files to a Document\nLibrary}\\label{adding-files-to-a-document-library}\n\nThis article covers the following topics to help you get started adding\nfiles to your Document Library:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{Granting File Permissions and Roles:} Determine who can add,\n  view, and update files. Doing this before adding files ensures that\n  only those you wish can access your Document Library.\n\\item\n  \\textbf{Adding Files:} Add specific types of files and their\n  associated metadata to your Document Library.\n\\end{enumerate}\n\n\\section{Granting File Permissions and\nRoles}\\label{granting-file-permissions-and-roles}\n\nYou should carefully manage who can add, view, and update files. You can\nstore files of all kinds for various purposes. For example, you may have\none set of files intended for only specific site members and another\nintended for everyone, including guests. You can use\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{Roles and\nPermissions} to control access to Document Library files. The Document\nLibrary's folder permissions also help you organize files.\n\nFollow these steps to create a Role for managing files in your site's\nDocuments and Media:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}) and\n  navigate to \\emph{Control Panel → Users → Roles}.\n\\item\n  Select the \\emph{Site Roles} tab (or \\emph{Organization Roles}, for an\n  Organization Role) and then click the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}) to begin creating a role.\n\\item\n  Give your Role a name and a description, then click \\emph{Save}.\n\\item\n  Select your Role's \\emph{Define Permissions} tab. In the Role's\n  permission definition screen, navigate to \\emph{Site Administration} →\n  \\emph{Content \\& Data} → \\emph{Documents and Media}. In the\n  \\emph{General Permissions} section, select \\emph{Access in Site\n  Administration} and click \\emph{Save}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/dm-define-role-permissions.png}\n  \\caption{It's often helpful to define a role for specific users to\n  access Documents and Media from Site Administration.}\n  \\end{figure}\n\\item\n  Assign this Role to the Users that should manage media. For more\n  information on this and other topics related to Roles, see\n  \\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{Roles\n  and Permissions}.\n\\end{enumerate}\n\n\\section{Using the Add Menu}\\label{using-the-add-menu}\n\nFollow these steps to add files to your site's Document Library:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}), click\n  on your site's name, and navigate to \\emph{Content \\& Data} →\n  \\emph{Documents and Media}. The Documents and Media screen appears and\n  displays the Documents and Media library's \\emph{Home} (its root\n  folder). As you add files and folders to the Document Library, they're\n  listed here.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/dm-admin-home.png}\n  \\caption{The Documents and Media's \\emph{Home} folder starts empty.}\n  \\end{figure}\n\\item\n  Click the \\emph{Add} icon (\\includegraphics{./images/icon-add.png})\n  and select the type of document to add to the Document Library. You\n  can add documents, folders, and shortcuts much like you would on a\n  desktop file system. You can even configure access to an entirely\n  different repository. The Add menu's options are described below.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/dm-admin-add-menu.png}\n  \\caption{The Add menu lets you upload and add all kinds of documents\n  to the library.}\n  \\end{figure}\n\\item\n  When you're finished selecting the file to upload and filling out any\n  document type fields that are necessary, click \\emph{Publish}.\n\\end{enumerate}\n\n\\textbf{File Upload:} Upload a file to the Documents and Media library.\n\n\\textbf{Folder}: Create a new folder in the Documents and Media\nlibrary's file system.\n\n\\textbf{Multiple Files Upload:} Upload several files at once. You can\napply a single description and document type to all the files. You can\nalso\n\\href{/docs/7-2/user/-/knowledge_base/u/organizing-content-with-tags-and-categories}{categorize\nand tag} the files, and assign them default permissions.\n\n\\textbf{Repository}: Add access to an external repository.\n\n\\textbf{Shortcut}: Create a shortcut to any document that you can view.\nYou can set permissions on the shortcut to specify who can access the\noriginal document via the shortcut.\n\nAny additional items in the Add menu are\n\\href{/docs/7-2/user/-/knowledge_base/u/document-types}{document types}\ndescribed by a unique\n\\href{/docs/7-2/user/-/knowledge_base/u/metadata-sets}{metadata set}.\nWhen you add a document belonging to a document type, a form appears\nthat lets you pick the file to upload and enter the data defined by the\ndocument type's metadata set.\n\n\\chapter{Creating Folders}\\label{creating-folders}\n\nYou'll need folders to organize all but the most limited set of files.\nHere, you'll learn how to work with folders in a Document Library:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[adding-a-folder]{Adding a Folder}\n\\item\n  \\hyperref[document-type-restrictions-and-workflow]{Document Type\n  Restrictions and Workflow}\n\\item\n  \\hyperref[setting-folder-permissions]{Setting Folder Permissions}\n\\end{itemize}\n\n\\section{Adding a Folder}\\label{adding-a-folder}\n\nFollow these steps to add a folder:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}), click\n  on your site's name, and navigate to \\emph{Content \\& Data} →\n  \\emph{Documents and Media} for your site. The Documents and Media\n  screen appears and displays the Documents and Media library's\n  \\emph{Home} (its root folder).\n\\item\n  Click the \\emph{Add} icon (\\includegraphics{./images/icon-add.png})\n  and select \\emph{Folder}. The New Folder form appears.\n\\item\n  In the New Folder form, name and describe your folder. Then expand the\n  \\emph{Permissions} section.\n\\item\n  In the Permissions section, set the folder's permissions. The\n  \\emph{Viewable by} menu lets you select who has View permission for\n  the folder:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Anyone (the Guest role; this is the default option)\n  \\item\n    Site Members\n  \\item\n    Owner\n  \\end{itemize}\n\n  Click the \\emph{More Options} link to choose the other folder\n  permissions for the Guest and Site Member roles. By default, site\n  members can add files, subfolders, shortcuts, and subscribe to changes\n  to the folder's files. Guests don't have any such permissions, which\n  is typically what you want.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/dm-folder-permissions.png}\n  \\caption{Select your folder's permissions.}\n  \\end{figure}\n\\item\n  To finish creating the folder, click \\emph{Save} after making your\n  selections in the Permissions section.\n\\end{enumerate}\n\nUpon creating the folder, it appears in your Document Library. Opening\nthe folder's \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}) presents several options\nfor managing the folder. The following sections describe some of these\noptions.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-folder.png}\n\\caption{Your new folder appears in the Document Library.}\n\\end{figure}\n\n\\section{Document Type Restrictions and\nWorkflow}\\label{document-type-restrictions-and-workflow}\n\nAfter creating a folder, you can restrict what document types are\nallowed in it. You can also choose what\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow}{workflow} (if any) to\nuse for approving files added to or edited in the folder.\n\nFollow these steps to change a folder's document type restrictions and\nworkflow:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the folder's \\emph{Actions} menu\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Edit}.\n\\item\n  Expand the \\emph{Document Type Restrictions and Workflow} section. In\n  this section, choose from the following options:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Use Document Type Restrictions and Workflow of the Parent Folder\n    (the parent folder)\n  \\item\n    Define Specific Document Type Restrictions and Workflow for this\n    Folder (the current folder)\n  \\item\n    Default Workflow for this Folder (the current folder)\n  \\end{itemize}\n\\item\n  Click \\emph{Save} when you're finished.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-restrictions-workflow.png}\n\\caption{You can set the document type restrictions and workflow to use\nfor a folder's files.}\n\\end{figure}\n\n\\section{Setting Folder Permissions}\\label{setting-folder-permissions}\n\nWhen creating a folder, you can set some of its permissions via the new\nfolder form. Fine tuning a folder's permissions, however, can only be\ndone after creating the folder.\n\nFollow these steps to fine tune a folder's permissions:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the folder's \\emph{Actions} menu\n  (\\includegraphics{./images/icon-actions.png}) and select\n  \\emph{Permissions}. The Permissions window appears.\n\\item\n  In the Permissions window, set the permissions you want to use for\n  this folder. The permissions listed below are available for each role.\n\\item\n  Click \\emph{Save} when you're finished setting permissions.\n\\end{enumerate}\n\nHere are the permissions you can set:\n\n\\textbf{Delete}: Move the folder to the Recycle Bin.\n\n\\textbf{Permissions}: View and modify the folder's permissions.\n\n\\textbf{Add Subfolder}: Create folders within the folder.\n\n\\textbf{Add Shortcut}: Create a shortcut (link) to any file in the\nfolder that the role is authorized to view.\n\n\\textbf{Update}: Edit the folder's attributes and/or move the folder\nunder a new parent folder.\n\n\\textbf{Add Document}: Add a new file to the folder.\n\n\\textbf{Subscribe}: Receive email notifications when files are added to\nor modified in the folder. Note that you can specify the email sender\nand template from the Documents and Media's \\emph{Options}\n(\\includegraphics{./images/icon-options.png}) → \\emph{Configuration}\nmenu.\n\n\\textbf{View}: View the folder.\n\n\\textbf{Access}: Access the folder's contents.\n\n\\chapter{Using the Documents and Media Management\nBar}\\label{using-the-documents-and-media-management-bar}\n\nThe Documents and Media \\emph{Management Bar} is where people who manage\ndocuments go to unwind after a long day at work. Just kidding. The\nManagement Bar, as its name implies, contains tools for managing the\nfiles and folders in your Document Library. It appears above the files\nand folders in Documents and Media.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-management-bar.png}\n\\caption{The Management Bar is a great place to hang out if you're\nmanaging documents.}\n\\end{figure}\n\nIf you've added files or folders to your Document Library, then you're\nalready familiar with the Management Bar's \\emph{Add} button\n(\\includegraphics{./images/icon-add.png}). The sections that follow\ndescribe the rest of the Management Bar.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If a Document Library contains more items than it can\ndisplay at once, you can use the navigation tool that appears at the\nbottom of the window to switch your view to another page or configure\nthe page to display more items per page.\n\n\\noindent\\hrulefill\n\n\\section{View Types}\\label{view-types}\n\nThe \\emph{View Types} button is to the left of the Add button. It lets\nyou choose how to display the Document Library's items. The View Types\nbutton's icon depends on the selected view type:\n\n\\textbf{Cards} (\\includegraphics{./images/icon-view-type-cards.png}):\nShows a card-like rendering of the item. If the item isn't an image, a\ngeneric image for the item's type is displayed. For files, each card\nalso contains the file's suffix (e.g., JPG, PNG, etc.), timestamp, name,\nand \\href{/docs/7-2/user/-/knowledge_base/u/workflow}{workflow} status\n(e.g., Approved, Draft, etc.).\n\n\\textbf{List} (\\includegraphics{./images/icon-view-type-list.png}):\nShows the same information as the Cards view type, in a list with small\nfile renderings.\n\n\\textbf{Table} (\\includegraphics{./images/icon-view-type-table.png}):\nShows the same information as the other view types, in a list with no\nfile renderings. Also, the file information is in columns.\n\nThe items in all view types have an Actions menu\n(\\includegraphics{./images/icon-actions.png}). These actions are also\navailable in when viewing each item separately.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-images-in-admin.png}\n\\caption{The Cards View type shows items in large card-like renderings.}\n\\end{figure}\n\n\\section{The Info Panel}\\label{the-info-panel}\n\nTo display an info panel with the current folder's details, click the\n\\emph{Information} icon\n(\\includegraphics{./images/icon-information-dm.png}). The info panel\nslides out from the right side of the screen and contains the folder's\nname and number of items. It also has these buttons:\n\n\\textbf{Subscribe} (\\includegraphics{./images/icon-star.png}): Get\nnotifications about files added to or modified in the folder.\n\n\\textbf{Actions} (\\includegraphics{./images/icon-actions.png}): Lists\nactions you can perform on the current folder.\n\n\\section{Finding and Arranging Items}\\label{finding-and-arranging-items}\n\nThe Management Bar also contains tools that help you locate and arrange\nitems in the Document Library. The most prominent of these tools is the\n\\emph{Search} bar, where you can find files by keywords.\n\nTo the left of the Search bar, the Sort button\n(\\includegraphics{./images/icon-sort.png}) arranges items in ascending\nor descending order.\n\nYou can also arrange items via the \\emph{Filter and Order} selector\nusing these criteria:\n\n\\textbf{All:} Shows all of the current folder's immediate subfolders and\nfiles (default).\n\n\\textbf{Mine:} Shows all the current user's files (no matter their\nfolder).\n\n\\textbf{Document Type:} Shows the files of the selected document type.\nUpon choosing this option, you must select the document type you want\nfrom a popup.\n\nYou can also select from the following criteria for ordering items:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Size\n\\item\n  Downloads\n\\item\n  Modified Date (default)\n\\item\n  Create Date\n\\item\n  Title\n\\end{itemize}\n\n\\section{Selecting Items}\\label{selecting-items}\n\nThe checkbox on the left-most side of the Management Bar selects all\ncurrently displayed items. Selecting multiple items lets you act on all\nof them at once. You can also select multiple items individually by\nusing the checkboxes for each. When you select one or more items, the\nManagement Bar changes to reflect the actions you can take on the\nselected items.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-management-bar-actions.png}\n\\caption{With items selected, the Management Bar changes.}\n\\end{figure}\n\nHere are the actions you can take on the selected items:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Download (\\includegraphics{./images/icon-download.png})\n\\item\n  Move (\\includegraphics{./images/icon-move.png})\n\\item\n  Edit Tags\n\\item\n  Move to Recycle Bin (\\includegraphics{./images/icon-trash.png})\n\\end{itemize}\n\nThe Actions button (\\includegraphics{./images/icon-actions.png})\ncontains all the actions displayed in the Management Bar, plus actions\nfor file checkin and checkout. File checkout and checkin is explained in\n\\href{/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files}{Checking\nout and Editing Files}.\n\n\\chapter{Viewing File Previews}\\label{viewing-file-previews}\n\nFile previews help users browse and find media efficiently. To view a\npreview of a file, click the file's name in the Document Library. If the\nfile is an image, the image appears. If an app is installed that can\nrender a preview of the file type, a representative image of the file\nappears (e.g., the opening frame of a video file or a presentation's\nfirst slide). If there are no such preview apps for the file, a generic\nimage based on the file type appears.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-file-entry-details.png}\n\\caption{File previews let you view and manage a file.}\n\\end{figure}\n\n\\section{File Preview Apps}\\label{file-preview-apps}\n\nWhenever possible, Liferay DXP generates previews of documents added to\nthe Document Library. Out of the box, Java-based APIs generate previews.\nThe only tool available that is 100\\% Java and has a compatible license\nto be distributed with Liferay DXP is\n\\href{https://pdfbox.apache.org}{PDFBox}. A separate thread generates a\npreview for PDFs when uploaded. This process may last only a few seconds\nfor a small file. The larger the file, the longer it takes.\n\nWhile PDFBox provides a default implementation of image generation for\ndocument previews and thumbnails, you must install and configure\nadditional tools to harness the full power of document previews. These\ntools include:\n\n\\begin{itemize}\n\\item\n  \\href{http://www.openoffice.org}{OpenOffice} or\n  \\href{http://www.libreoffice.org}{LibreOffice}: Using one of these in\n  server mode lets you generate thumbnails and previews for supported\n  file types (\\texttt{.pdf}, \\texttt{.docx}, \\texttt{.odt},\n  \\texttt{.ppt}, \\texttt{.odp}, etc.), view documents in your browser,\n  and convert documents.\n\\item\n  \\href{http://www.imagemagick.org}{ImageMagick} (also requires\n  \\href{http://www.ghostscript.com}{Ghostscript}): Enables faster and\n  higher-quality previews and conversions.\n\\item\n  \\href{http://www.xuggle.com/xuggler}{Xuggler}: Enables audio and video\n  previews, lets you play audio and video files in your browser, and\n  extracts thumbnails from video files.\n\\end{itemize}\n\nAfter installing these tools, you can configure them via portal\nproperties in the Control Panel's Server Administration screen, or in a\n\\texttt{portal-ext.properties} file. To learn how to use these tools,\nsee \\href{/docs/7-2/user/-/knowledge_base/u/setting-up}{Configuring\nLiferay DXP}.\n\nWith these tools installed and configured, a customized viewer displays\nDocuments and Media content, depending on the content type. For example,\nyou can view a document with a customized viewer that lets you navigate\nthrough the document's pages. You can also view and play multimedia\ndocuments (audio or video). If the browser supports HTML5, the viewer\nuses the browser's native player. Otherwise it falls back to a Flash\nplayer.\n\n\\section{Managing Files}\\label{managing-files}\n\nYou can also manage a file from its preview. The bar above the preview\ncontains these buttons:\n\n\\textbf{Info} (\\includegraphics{./images/icon-information-dm.png}):\nOpen/close the file's info panel. This panel contains more detailed\ninformation about the file. For more information on this, see\n\\hyperref[the-info-panel]{The Info Panel}.\n\n\\textbf{Share}: Share the file with other users. For more information,\nsee \\href{/docs/7-2/user/-/knowledge_base/u/sharing-files}{Sharing\nFiles}.\n\n\\textbf{Download}: Download the file.\n\n\\textbf{Actions} (\\includegraphics{./images/icon-actions.png}): Opens a\nmenu that lets you perform these actions on the file:\n\n\\begin{itemize}\n\\item\n  \\textbf{Download}\n\\item\n  \\textbf{Edit:} Modify the file's name, description, document type,\n  categorization, and\n  \\href{/docs/7-2/user/-/knowledge_base/u/defining-content-relationships}{related\n  assets}. You can even upload a new file to replace it. Note that\n  modifying the file increments its version.\n\\item\n  \\textbf{Edit with Image Editor:} Edit the image in the Image Editor.\n  The Image Editor is explained in\n  \\href{/docs/7-2/user/-/knowledge_base/u/editing-images}{Editing\n  Images}.\n\\item\n  \\textbf{Checkout/Checkin:} Checkout prevents others from editing the\n  document while you are working on it. Other users can still view the\n  current version of the document, if they have permission. You can\n  check in the document when you're done with it.\n\\item\n  \\textbf{Move:} Relocate the file to a different parent folder.\n\\item\n  \\textbf{Permissions:} Specify which actions each role can perform on\n  the file.\n\\item\n  \\textbf{Move to Recycle Bin:} Move the file from the Documents and\n  Media library to the Recycle Bin.\n\\item\n  \\textbf{Share}\n\\end{itemize}\n\nAlso note that the \\emph{Options} menu\n(\\includegraphics{./images/icon-options.png}) at the top-right of the\nscreen contains the same actions as the Actions menu.\n\nThe comments area (below the preview area) lets you comment on and\nsubscribe to comments on the file.\n\n\\section{The Info Panel}\\label{the-info-panel-1}\n\nAs mentioned above, clicking the \\emph{Info} icon\n(\\includegraphics{./images/icon-information-dm.png}) opens the info\npanel. The top of the info panel displays the file's name, version, and\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow}{workflow status}.\nThere are two tabs in the info panel: Details, and Versions. Details is\nselected by default and shows the following:\n\n\\textbf{Owner:} The file's owner.\n\n\\textbf{Download:} A button to download the file.\n\n\\textbf{Latest Version URL:} A URL to access the newest version of the\nfile.\n\n\\textbf{WebDAV URL:} A WebDAV URL for accessing the file via a desktop.\n\n\\textbf{Document Type:} The file's document type.\n\n\\textbf{Extension:} The file's extension (e.g., JPG, PDF, etc.).\n\n\\textbf{Size:} The file's size on disk.\n\n\\textbf{Modified:} The user that last modified the file, and when it was\nlast modified.\n\n\\textbf{Created:} The user that created the file, and when it was\ncreated.\n\n\\textbf{Ratings:} The file's average user rating.\n\n\\textbf{Automatically Extracted Metadata:} Any and all metadata\nautomatically extracted from the file. When adding new documents or\nviewing existing documents, a process is triggered automatically that\nextracts the file's metadata. The library used by this process is TIKA\nand it's included out of the box. Depending on your file's type and the\nmetadata written with the file, you can find out all kinds of details.\nIn the case of audio or video files, the media's duration is displayed.\n\nTo instead view the file's version history, select the \\emph{Versions}\ntab near the top of the info panel. The info panel then changes to list\nthe different versions of the file and lets you view, download, remove,\nand revert to specific file versions. File version history actions are\nexplained in\n\\href{/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files}{Checking\nOut and Editing Files}.\n\n\\chapter{Editing Images}\\label{editing-images}\n\nEditing and re-uploading images when you only need to apply simple edits\nis tedious. Docs \\& Media contains a simple built-in image editor for\nexactly this reason. To access the image editor, locate the image you\nwant to edit. Click the Actions icon\n(\\includegraphics{./images/icon-actions.png}) and select \\emph{Edit With\nImage Editor}.\n\nYou can also access the image editor when selecting an image to insert\nin content (i.e., via an item selector). Anywhere an image is, you can\nedit it. For example, you can access the image editor via item selector\npreview windows in blog entries and web content articles. To do this,\nclick the pencil icon (\\includegraphics{./images/icon-edit-pencil.png})\nin the bottom-right corner of the preview window.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/image-editor-docs-and-media.png}\n\\caption{You can access the image editor through the Documents and Media\nrepository.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/image-editor-preview-window.png}\n\\caption{You can also access the image editor through the item selector\npreview window.}\n\\end{figure}\n\nIf you edit and save the image via the Documents and Media repository,\nthe file version is incremented a minor version (e.g., from version 1.0\nto version 1.1). You can view the image's version history (and previous\nversions) by clicking the image, clicking its \\emph{Info} button\n(\\includegraphics{./images/icon-information-dm.png}), and then selecting\nthe \\emph{Versions} tab. In contrast, if you edit and save an image via\nan item selector, a copy of the image is created and saved to the\nDocument Library.\n\nLiferay designed the image editor with quick editing in mind. It offers\na minimal, user-friendly UI. The main toolbar consists of three buttons,\neach of which contains a subset of options:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/image-editor-tools.png}\n\\caption{The image editor's UI is clear and to the point, offering only\nwhat you need.}\n\\end{figure}\n\n\\textbf{Transform} (\\includegraphics{./images/icon-transform.png})\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Rotate}: Rotate the image to the left or right, in 90 degree\n  increments.\n\\item\n  \\textbf{Resize}: Resize the image. If the lock is closed, the aspect\n  ratio is locked and changing width or height automatically adjusts the\n  other dimension to maintain the aspect ratio. When the lock is opened,\n  the width and height can be changed individually, letting the aspect\n  ratio change (this isn't recommended because the image can become\n  distorted).\n\\item\n  \\textbf{Crop}: Crop the image.\n\\end{itemize}\n\n\\textbf{Adjustment} (\\includegraphics{./images/icon-adjustment.png})\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Saturation}: Adjust the color saturation. The default value is\n  50. Values range from 0 (completely desaturated) to 100 (completely\n  saturated).\n\\item\n  \\textbf{Contrast}: Adjust the contrast. The default value is 50.\n  Values range from 0 (no contrast) to 100 (full contrast).\n\\item\n  \\textbf{Brightness}: Adjust the brightness. The default value is 50.\n  Values range from 0 (completely black) to 100 (completely white).\n\\end{itemize}\n\n\\textbf{Filter} (\\includegraphics{./images/icon-wand.png}): Apply a\nfilter to the image.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/image-editor-filters.png}\n\\caption{Select from a set of preset image filters.}\n\\end{figure}\n\nUpon editing the image in the editor, you can click the \\emph{Cancel}\nbutton to cancel the changes, or the \\emph{Apply} button to apply them.\nUpon applying the changes, the history bar appears. It lets you undo,\nredo, or reset the changes. Use the Reset button with caution; it resets\nthe image to its original state, reverting all changes made in the\neditor.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/image-editor-history-bar.png}\n\\caption{The history bar lets you undo, redo, and reset changes.}\n\\end{figure}\n\n\\chapter{Publishing Files}\\label{publishing-files-1}\n\nOnce your Document Library contains files, you may want to publish them\nin your site. Here are some ways to publish files:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Show them in a Documents and Media app.\n\\item\n  Display them in a Media Gallery.\n\\item\n  Use the Asset Publisher.\n\\item\n  Insert them in an asset like a web content article or blog entry.\n\\end{itemize}\n\nHere, you'll learn to use the Media Gallery.\n\n\\section{Using the Media Gallery}\\label{using-the-media-gallery}\n\nThe Media Gallery publishes your media files in a simple gallery-like\nstyle. It shows a large thumbnail of each media file, lets the user\ndownload files, and has slideshow capabilities. A common way to use the\nMedia Gallery is to create a separate page for displaying media and add\na Media Gallery widget to it. This way, your media takes center stage.\n\nFollow these steps to create a page that contains a Media Gallery\nwidget:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/creating-and-managing-pages}{Create\n  a page} and navigate to it in your site.\n\\item\n  At the top-right of the screen, click the \\emph{Add} icon\n  (\\includegraphics{./images/icon-add-app.png}) then navigate to\n  \\emph{Widgets} → \\emph{Content Management} and select \\emph{Add} next\n  to \\emph{Media Gallery} (alternatively, drag the Media Gallery onto\n  your page). The Media Gallery widget appears on the page.\n\\item\n  Configure the Media Gallery widget to show your files. By default, it\n  shows files from the Home folder of your site's Documents Library. To\n  choose a different folder, click the widget's Options icon\n  (\\includegraphics{./images/icon-app-options.png}) and select\n  \\emph{Configuration}.\n\n  The Configuration window appears and shows the \\emph{Setup} tab. This\n  tab contains these sections:\n\n  \\textbf{Display Settings:} Lets you show each file's actions, filter\n  the media types to display, and choose a display template for your\n  media.\n\n  \\textbf{Folders Listing:} Lets you select a Document Library folder to\n  serve as the root folder from which to display files. The root folder\n  you select becomes the highest-level folder the Media Gallery can\n  access. For example, if you create a subfolder of a parent folder, and\n  then set that subfolder as the Media Gallery's root folder, the Media\n  Gallery can no longer access the parent folder.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note**: To access the Carousel display template in Media Gallery, your \n role must have View access for that template. Since the Carousel template \n is in the Global scope, a Global-scope administrator must grant the role \n permission to view the template. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n![ You can configure the Media Gallery to use any Documents and Media folder as its root folder.](./images/dm-select-root-folder.png)\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  Configure the rest of the settings as desired in the Media Gallery\n  app's other configuration tabs:\n\n  \\textbf{Communication:} Lists public render parameters the widget\n  publishes to other widgets on the page. Other widgets can take action\n  on these parameters. For each shared parameter, you can specify\n  whether to allow communication via the parameter and select which\n  incoming parameter can can populate it.\n\n  \\textbf{Sharing:} Embed the widget instance as a widget on on any\n  website, Facebook, Netvibes, or as an OpenSocial Gadget.\n\n  \\textbf{Scope}: Specify the Document Library instance the widget uses:\n  the current site's instance (default), the global instance, or the\n  page's instance. If the page doesn't already have an instance of the\n  widget, you can select \\emph{Your Page (Create New)} to create a\n  page-scoped instance for the widget to display.\n\\item\n  Click \\emph{Save} when you're finished configuring the Media Gallery\n  widget.\n\\end{enumerate}\n\nThe Media Gallery now shows your files, with images appearing as\nthumbnails. When you click a thumbnail, a slideshow appears showing the\nselected image. Below that image, thumbnails of the folder's other\nimages are displayed. The slideshow continues until you click pause or\nview the last image. Closing the slideshow window returns you to the\npage.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-media-gallery.png}\n\\caption{The Media Gallery renders large thumbnail images of media\nfiles.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-media-gallery-slideshow.png}\n\\caption{The Media Gallery's slideshow provides a nice way to view\nimages.}\n\\end{figure}\n\n\\chapter{Checking Out and Editing\nFiles}\\label{checking-out-and-editing-files}\n\nWhen you check out a document in the Document Library, only you can make\nchanges to it until you check it back in. This prevents conflicting\nedits on the same document by multiple users. When you check out a file,\nyou can download it, replace it, move it to another Document Library\nfolder, check it in, or cancel the checkout. Checking in a file also\nincrements its version, which lets you keep track of changes.\n\nUnless you're using\n\\href{/docs/7-2/user/-/knowledge_base/u/using-liferay-sync-on-your-desktop}{Liferay\nSync} or a\n\\href{/docs/7-2/user/-/knowledge_base/u/desktop-access-to-documents-and-media}{local\ndrive mapped to the file's WebDAV URL}, follow these steps to edit a\nDocument Library file from your machine:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Checkout the file by clicking its Actions icon\n  (\\includegraphics{./images/icon-actions.png}) → \\emph{Checkout}. Upon\n  checkout, the file's status changes to Draft and a lock icon appears\n  on the file.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/dm-file-checked-out.png}\n  \\caption{The file on the right in this image is checked out.}\n  \\end{figure}\n\\item\n  Download the file by clicking its Actions icon\n  (\\includegraphics{./images/icon-actions.png}) → \\emph{Download}.\n\\item\n  Edit the file locally.\n\\item\n  Return to the Documents and Media Library and click the file's Actions\n  icon (\\includegraphics{./images/icon-actions.png}) → \\emph{Edit}. The\n  file's edit screen appears.\n\\item\n  From the file's Edit screen, select the edited local file for upload.\n\\item\n  Click \\emph{Save and Check In}. In the pop-up that appears, select\n  whether your change is a major or minor version, add any version notes\n  that you need, and click \\emph{Save}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: If you edit a file without checking it out, the file's\nedit screen displays a toggle for \\emph{Customize the Version Number\nIncrement and Describe My Changes}. Setting this to \\emph{YES} lets you\nspecify the version increment's type and description.\n\n\\noindent\\hrulefill\n\nFollow these steps to access a file's version history:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the file in the Documents and Media Library.\n\\item\n  Click the file's \\emph{Info} button\n  (\\includegraphics{./images/icon-information-dm.png}) at the top-right\n  of the screen. This opens the file's info panel.\n\\item\n  Select the \\emph{Versions} tab in the info panel.\n\\end{enumerate}\n\nEach file version has an Actions menu\n(\\includegraphics{./images/icon-actions.png}) that you can use to\nperform the following actions on that file version:\n\n\\textbf{Download}: Download the selected version of the file to your\nmachine.\n\n\\textbf{View}: View the file entry screen for the selected version of\nthe file.\n\n\\textbf{Revert}: Restores the selected file version as a new major file\nversion. Note that this option isn't available for the newest file\nversion.\n\n\\textbf{Delete Version}: Remove the file version from the Document\nLibrary. All other file versions remain intact.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-file-version-history.png}\n\\caption{The version history actions let you inspect, delete, and\nreinstate file versions.}\n\\end{figure}\n\n\\chapter{Sharing Files}\\label{sharing-files}\n\nLiferay DXP's\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{role-based\npermissions system} defines which actions users can take on assets,\nincluding\n\\href{/docs/7-2/user/-/knowledge_base/u/adding-files-to-a-document-library\\#granting-file-permissions-and-roles}{files}.\nAdministrators can let users collaborate on files by assigning the\nappropriate file permissions to a Role, and then assigning users to that\nRole. Similarly, non-administrative users can grant permissions to Roles\nfor files they own.\n\nThis Role-based permissions system sometimes falls short. For example,\nif a Role appropriate for file collaboration doesn't exist, an\nadministrator must create it and manage its users and permissions.\nNon-administrative users can't create or manage Roles. Also, if a user\nwants to share a file with one other user, it's not practical for an\nadministrator to create and manage a Role for only two users.\n\nLiferay DXP's sharing feature solves these problems by letting users\nshare files directly with each other, without involving an\nadministrator. This saves time and effort for everyone. After all,\nsharing is caring.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Administrators can disable sharing. For instructions on\nthis, see\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-sharing}{Configuring\nSharing}.\n\n\\noindent\\hrulefill\n\nWhen you share, you grant some of your own permissions for that file to\nthe receiving user. However, there are some important caveats:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  You can only grant View, Comment, or Update permissions. For example,\n  you can't grant Delete or Override Checkout permissions even if you\n  have those permissions on the file.\n\\item\n  You can only grant permissions you have on the file. For example, you\n  can't grant Update permission if you only have View and Comment\n  permissions on the file.\n\\item\n  You must grant at least View permission.\n\\item\n  Traditional Role-based permissions always take precedence over sharing\n  permissions. So although sharing can extend permissions, it can't\n  remove those granted via Roles in the portal.\n\\item\n  By default, the Guest Role has Add Discussion permission. This\n  overlaps with the Comment permission in sharing. Therefore, all users\n  can comment on a file regardless of whether the Comment permission was\n  granted via sharing. Administrators can change this by removing the\n  Add Discussion permission from the Guest Role.\n\\end{itemize}\n\nAlso note that the receiving user must be part of the same instance, but\ndoesn't have to be a member of the same Site.\n\n\\section{Sharing Files in Documents and\nMedia}\\label{sharing-files-in-documents-and-media}\n\nTo share a file, you must own that file or be an administrator. You must\nshare files via the Documents and Media app in Site Administration or\nthe Documents and Media widget on a page.\n\nFollow these steps to share a file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Using the Documents and Media widget on a page or in Site\n  Administration, navigate to the file you want to share.\n\n  To navigate to the Documents and Media app in Site Administration,\n  open the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}), click\n  your Site's name, and go to \\emph{Content \\& Data} → \\emph{Documents\n  and Media}.\n\n  To share a file via the Documents and Media widget on a page, actions\n  must be enabled for the widget. Follow these steps to enable actions:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Select \\emph{Configuration} from the widget's \\emph{Options} menu\n    (\\includegraphics{./images/icon-app-options.png}).\n  \\item\n    In the \\emph{Setup} tab's \\emph{Display Settings}, select \\emph{Show\n    Actions}.\n  \\item\n    Click \\emph{Save} and close the Configuration window.\n  \\end{itemize}\n\\item\n  Click the file's \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Share}.\n  This opens the Share dialog.\n\n  Alternatively, click the file in Documents and Media and then click\n  the \\emph{Share} button at the top-right. This opens the same Share\n  dialog.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sharing-file.png}\n  \\caption{To share a file, you must fill out the Share dialog as these\n  steps describe.}\n  \\end{figure}\n\\item\n  Enter the name or email address of the user you want to share the file\n  with. To share the file with multiple users, enter each user's email\n  address in a comma delimited list.\n\\item\n  To let receiving users also share the file, select \\emph{Allow the\n  document to be shared with other users}. Note, however, that\n  administrators can share the file regardless of your selection here.\n\\item\n  Select the file permissions to grant to receiving users. Because you\n  can only grant your own permissions for the file, some of these\n  options may be unavailable:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Update:} View, comment, and update.\n  \\item\n    \\textbf{Comments:} View and comment.\n  \\item\n    \\textbf{View:} View only.\n  \\end{itemize}\n\n  If you enabled further sharing in the previous step, note that\n  receiving users can only share the file with the permissions you grant\n  here.\n\\item\n  Click \\emph{Share}.\n\\end{enumerate}\n\n\\section{Working with Shared Files}\\label{working-with-shared-files}\n\nYou can access files shared with you in three places:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{The Documents and Media Library:} Files shared with you are\n  visible in their existing Documents and Media locations. For example,\n  if someone shares a file with you that resides in the Documents and\n  Media Library's Home folder, then you can access the file in that\n  folder.\n\\item\n  \\textbf{The Notifications app:} When a file is shared with you, you\n  get a notification in the Notifications app. Clicking the notification\n  takes you to the file in Documents and Media. For information on\n  notifications, see\n  \\href{/docs/7-2/user/-/knowledge_base/u/managing-notifications-and-requests}{Managing\n  Notifications and Requests}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sharing-notifications.png}\n  \\caption{The Notifications app contains the notifications that are\n  sent when a user shares a file with you.}\n  \\end{figure}\n\\item\n  \\textbf{The Shared Content app:} This app lists all the content shared\n  with you, and the content you shared. You can access this app from\n  your user menu. Each file has an Actions button\n  (\\includegraphics{./images/icon-actions.png}) for performing permitted\n  actions on the file (e.g., view, comment, update).\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/shared-content-app.png}\n  \\caption{The Shared Content app lists the files shared with you, and\n  the files you shared.}\n  \\end{figure}\n\\end{enumerate}\n\n\\section{Managing Shared Files}\\label{managing-shared-files}\n\nAfter sharing a file, you can unshare it or modify its permissions on a\nper-user basis. This can only be done by Administrators, the file's\nowner, or any user with Update permission and permission to share the\nfile. You can take these actions from the file's Info panel in Documents\nand Media. Follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the file in Documents and Media, then click the \\emph{Info}\n  button (\\includegraphics{./images/icon-information-dm.png}) at the\n  top-right. The file's Info panel slides out from the right.\n\\item\n  Click the \\emph{Manage Collaborators} link. This shows a list of the\n  users you shared the file with and their file permissions.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sharing-info.png}\n  \\caption{Click \\emph{Manage Collaborators} to open up the list of\n  users you shared the file with.}\n  \\end{figure}\n\\item\n  Make any changes you want to the list of collaborators. To unshare the\n  file with a user, click the \\texttt{x} icon next to that user. You can\n  also change the file permissions via the selector menu for each user.\n\\item\n  Click \\emph{Save} and close the dialog.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sharing-collaborators.png}\n\\caption{The Collaborators dialog lets you unshare a file or change the\nfile permissions for each user.}\n\\end{figure}\n\n\\chapter{Configuring Sharing}\\label{configuring-sharing}\n\nAdministrators can choose whether\n\\href{/docs/7-2/user/-/knowledge_base/u/sharing-files}{file sharing} is\nenabled at the global, instance, and Site levels.\n\n\\section{Global Configuration}\\label{global-configuration}\n\nSharing is enabled globally by default. To configure sharing globally,\nfollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Sharing}.\n\\item\n  Under \\emph{SYSTEM SCOPE}, select \\emph{Sharing}.\n\\item\n  Configure sharing via these settings:\n\n  \\textbf{Expired Sharing Entries Check Interval:} The interval in\n  minutes for how often expired sharing entries are checked for\n  deletion.\n\n  \\textbf{Enabled:} Whether sharing is enabled globally.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sharing-system.png}\n\\caption{Configure sharing globally.}\n\\end{figure}\n\nWhen sharing is enabled globally, it's also enabled by default for all\nportal instances. You can change this from \\emph{Virtual Instance\nSharing} under \\emph{VIRTUAL INSTANCE SCOPE}:\n\n\\textbf{Enabled:} Whether sharing is enabled by default for all\ninstances in the portal.\n\n\\section{Instance Configuration}\\label{instance-configuration}\n\nTo enable or disable sharing on a per-instance basis, follow these\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{Instance\n  Settings} → \\emph{Sharing}.\n\\item\n  Select \\emph{Virtual Instance Sharing} under \\emph{VIRTUAL INSTANCE\n  SCOPE}.\n\\item\n  Check or uncheck the \\emph{Enabled} checkbox to enable or disable\n  sharing, respectively.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sharing-instance.png}\n\\caption{You can enable or disable sharing for each instance.}\n\\end{figure}\n\n\\section{Site Configuration}\\label{site-configuration}\n\nTo enable or disable sharing for a Site, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Site Administration (your site's menu) → \\emph{Configuration} →\n  \\emph{Settings}.\n\\item\n  Select the \\emph{General} tab.\n\\item\n  Expand the \\emph{Sharing} section and use the toggle to enable or\n  disable sharing for the Site.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sharing-toggle.png}\n\\caption{You can enable or disable sharing for each Site.}\n\\end{figure}\n\n\\chapter{Desktop Access to Documents and\nMedia}\\label{desktop-access-to-documents-and-media}\n\nYou can access the Document Library from your desktop file manager via\n\\href{https://en.wikipedia.org/wiki/WebDAV}{WebDAV}. WebDAV is a set of\nmethods based on HTTP that let users create, edit, move, or delete files\nstored on web servers. WebDAV is supported by most major operating\nsystems and desktop environments, including Linux, macOS, and Windows.\nUsing your file manager via WebDAV doesn't bypass the functionality of\nthe web interface---Liferay DXP increments the version numbers of files\nedited and uploaded via WebDAV.\n\nTo access the Document Library folder from a file browser, you must use\nyour log-in credentials and the WebDAV URL of the folder you want to\naccess. Follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Documents and Media app that contains the folder you\n  want to access. Click the folder's Actions icon\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Access\n  from Desktop}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/dm-access-from-desktop-action.png}\n  \\caption{Select \\emph{Access from Desktop} to get the folder's WebDAV\n  URL.}\n  \\end{figure}\n\\item\n  Copy the WebDAV URL and follow the instructions for your operating\n  system:\n\n  \\textbf{Windows:} Map a network drive drive to the WebDAV URL. Enter\n  your credentials when prompted. The Document Library folder appears in\n  the network drive. From your file browser, you can now add, edit,\n  move, or delete files in this folder.\n\n  \\textbf{macOS:} In the Finder, select \\emph{Go} → \\emph{Connect to\n  Server}. In the Server Address field, enter the WebDAV URL of the\n  folder you want to access, then click \\emph{Connect} and enter your\n  credentials when prompted.\n\n  \\textbf{Linux:} In your file manager, you must slightly modify the\n  Document Library folder's WebDAV URL. For KDE's Dolphin, change the\n  URL's protocol to \\texttt{webdav://} instead of \\texttt{http://}. For\n  GNOME's Nautilus, change the URL's protocol to \\texttt{dav://} instead\n  of \\texttt{http://}. Then press \\emph{Enter} and enter your\n  credentials when prompted.\n\\end{enumerate}\n\nNow you can access the Document Library folder from your desktop file\nsystem. If you edit a file in this folder on your file system, the\nchange also shows up in the same Document Library folder in the portal.\nWhat's more, the file's minor version is incremented due to the edit.\n\n\\chapter{Linking to Google Drive™}\\label{linking-to-google-drive}\n\nYou can create Document Library files that link to files in Google\nDrive™ and Google Photos™. This lets you access your Google files from\nthe Document Library. Note that this functionality isn't available by\ndefault. To enable it, you must complete these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Install the Liferay Plugin for Google Drive™ from Liferay Marketplace.\n\\item\n  Create and/or configure a Google project capable of communicating with\n  your Liferay DXP instance. The\n  \\href{https://developers.google.com/picker/}{Google Picker API} must\n  be enabled for this project. This API lets you select Google files to\n  link to. You must also create the credentials the Google project needs\n  to communicate with your Liferay DXP instance.\n\\item\n  Configure your portal to communicate with your Google project.\n\\end{enumerate}\n\nThis article shows you how to complete these steps and finishes with an\nexample of linking to a Google file from the Document Library.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can also use Google Docs™ for online file creation\nand editing. This doesn't require a plugin and is covered in a\n\\href{/docs/7-2/user/-/knowledge_base/u/online-file-creation-and-editing-with-google-docs}{separate\nsection of the documentation}.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} The Liferay Plugin for Google Drive™ is a Labs\napplication available for Liferay CE Portal and Liferay DXP. Labs apps\nare experimental and not supported by Liferay. They're released to\naccelerate the availability of useful and cutting-edge features. This\nstatus may change without notice. Use Labs apps at your own discretion.\n\n\\noindent\\hrulefill\n\n\\section{Install the App}\\label{install-the-app}\n\nFirst, you must install the the Liferay Plugin for Google Drive™ from\nLiferay Marketplace. This app is available via the following links for\nLiferay CE Portal and Liferay DXP:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{https://web.liferay.com/marketplace/-/mp/application/105847499}{Liferay\n  Plugin for Google Drive - CE}\n\\item\n  \\href{https://web.liferay.com/marketplace/-/mp/application/98011653}{Liferay\n  Plugin for Google Drive - DXP}\n\\end{itemize}\n\nIf you need help installing apps from Marketplace, see the documentation\non\n\\href{/docs/7-2/user/-/knowledge_base/u/using-the-liferay-marketplace}{using\nMarketplace}.\n\n\\section{Configure Your Google\nProject}\\label{configure-your-google-project}\n\nFollow these steps to create and/or configure your Google project so it\ncan communicate with your Liferay DXP instance:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\href{https://console.developers.google.com}{Google API\n  Console}. If you don't have a suitable project,\n  \\href{https://support.google.com/googleapi/answer/6251787?hl=en&ref_topic=7014522}{create\n  a new one}.\n\\item\n  Enable the Google Picker API for your project. For instructions, see\n  the Google API Console documentation on\n  \\href{https://support.google.com/googleapi/answer/6158841}{enabling\n  and disabling APIs}.\n\\item\n  Create an OAuth 2 client ID in your Google project. For instructions,\n  see the Google API Console documentation on\n  \\href{https://support.google.com/googleapi/answer/6158849}{setting up\n  OAuth 2.0}. Enter these values when creating your client ID:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Application type:} Web application\n  \\item\n    \\textbf{Name:} Google Docs Hook\n  \\item\n    \\textbf{Authorized JavaScript origins}:\n    \\texttt{{[}liferay-instance-URL{]}} (e.g.,\n    \\texttt{http://localhost:8080} is the default for local development\n    machines)\n  \\item\n    \\textbf{Authorized redirect URIs}:\n    \\texttt{{[}liferay-instance-URL{]}/oath2callback}\n  \\end{itemize}\n\\item\n  Create a new API key in your Google project. For instructions, see the\n  Google API Console documentation on\n  \\href{https://support.google.com/googleapi/answer/6158862?hl=en}{creating\n  API keys}. Be sure to restrict the key to HTTP referrers (web sites),\n  and set it to accept requests from your Liferay DXP instance's URL.\n\\end{enumerate}\n\nYour new OAuth client ID and public API access key now appear on your\nGoogle project's Credentials screen. Keep this screen open to reference\nthese values as you specify them in Liferay DXP.\n\n\\section{Configure Your Portal}\\label{configure-your-portal}\n\nNow that you have a Google project set up for use with Liferay DXP, you\nmust connect your installation to that project. You can do this at two\nscopes:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Globally, for all instances in your Liferay DXP installation.\n\\item\n  At the instance scope, for one or more instances in your Liferay DXP\n  installation.\n\\end{enumerate}\n\nYou can override the global configuration for one or more instances by\nconfiguring those instances separately. Similarly, you can configure\nonly the instances you want to connect to your Google project and leave\nthe global configuration empty.\n\nFollow these steps to configure your Liferay DXP installation to connect\nto your Google project:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Note that the configuration options are the same in the global and\n  instance-level configurations.\n\n  To access the global configuration, go to \\emph{Control Panel} →\n  \\emph{Configuration} → \\emph{System Settings} → \\emph{Documents and\n  Media}.\n\n  To access the instance-level configuration, go to \\emph{Control Panel}\n  → \\emph{Configuration} → \\emph{Instance Settings} → \\emph{Documents\n  and Media}.\n\\item\n  Under \\emph{VIRTUAL INSTANCE SCOPE}, select \\emph{Google Drive}.\n\\item\n  Enter your Google project's OAuth 2 client ID and client secret into\n  the \\emph{Client ID} and \\emph{Client Secret} fields.\n\\item\n  In the field \\emph{Picker API Key}, enter the API key you created in\n  the previous section.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/google-drive-system-settings.png}\n\\caption{Enter your Google project's OAuth 2 client ID, OAuth 2 client\nsecret, and Picker API key.}\n\\end{figure}\n\n\\section{Creating Linked Files}\\label{creating-linked-files}\n\nWith the preceding configuration steps complete, you can create files in\nyour Document Library that link to files in Google Drive™ or images in\nGoogle Photos™. Follow these steps to do so:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In your Document Library, click the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}) and select \\emph{Google\n  Drive Shortcut}. The \\emph{New Google Drive Shortcut} screen appears.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/dm-new-google-drive-shortcut.png}\n  \\caption{Select \\emph{New Google Drive Shortcut} from the \\emph{Add}\n  menu in your Document Library.}\n  \\end{figure}\n\\item\n  Click the \\emph{Select File} button to open Google's file picker.\n\\item\n  Use the file picker to select a file from Google Drive™ or Google\n  Photos™.\n\\item\n  Click \\emph{Publish}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-google-select-a-file.png}\n\\caption{You can select files from Google Drive™ or your photos.}\n\\end{figure}\n\nA new file entry appears for the Google document you linked to. You can\nview the file entry as you would any file entry. The Google document's\ncontents show in the file entry's preview pane. As with any file entry,\nthe \\emph{Options} button (\\includegraphics{./images/icon-options.png})\ngives you access to the Download, Edit, Move, Permissions, Move to\nRecycle Bin, and Checkin/Checkout/Cancel Checkout options.\n\n\\chapter{Metadata Sets}\\label{metadata-sets}\n\nYou can define metadata fields that users fill out when they create or\nedit Document Library files. You do this by creating \\emph{metadata\nsets} and then associating them with document types, which wrap Document\nLibrary files and thus apply your metadata fields to the files. Although\nyou apply metadata sets via document types, metadata sets exist\nindependently and you can apply them to any number of document types.\n\n\\section{Managing Metadata Sets}\\label{managing-metadata-sets}\n\nTo see the available metadata sets, open the \\emph{Menu}\n(\\includegraphics{./images/icon-menu.png}), expand your site's menu, and\nnavigate to \\emph{Content \\& Data} → \\emph{Documents and Media}. Then\nclick the \\emph{Metadata Sets} tab. Any existing sets appear in a table.\n\nTo select a metadata set, select the checkbox to its left. To select all\nthe sets, select the checkbox in the Management Bar. With one or more\nsets selected, an \\texttt{X} icon appears in the Management Bar.\nClicking it deletes the selected metadata set(s). Note that metadata\nsets don't support the Recycle Bin. If you delete a metadata set, it's\ngone forever.\n\nThe Management Bar also contains other options for managing the metadata\nsets. The selector menu to the right of the checkbox filters the sets\nthe table displays (it's set to \\emph{All} by default). The \\emph{Order\nby} selector orders the sets by Modified Date or ID. The up and down\narrows sort the sets in ascending or descending order, respectively. You\ncan also use the Search bar to search for a set.\n\nIn the table, each metadata set has an Actions button\n(\\includegraphics{./images/icon-actions.png}) for performing the\nfollowing actions on that set:\n\n\\textbf{Edit}: Edit the set. Alternatively, click the set's name in the\ntable.\n\n\\textbf{Permissions}: Configure the set's permissions.\n\n\\textbf{Copy}: Copy the metadata set.\n\n\\textbf{Delete}: Delete the set.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-metadata-sets-list.png}\n\\caption{The Metadata Sets management window lets you view existing sets\nand create new ones for applying to document types.}\n\\end{figure}\n\n\\section{Creating Metadata Sets}\\label{creating-metadata-sets}\n\nFollow these steps to create a metadata set:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}), click\n  your Site's name and navigate to \\emph{Content \\& Data} →\n  \\emph{Documents and Media}. Then click the \\emph{Metadata Sets} tab.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n  The New Metadata Set form appears.\n\\item\n  Give your metadata set a name.\n\\item\n  Open the \\emph{Details} section of the form to give your metadata set\n  a description or select a metadata set to extend (both are optional).\n  To select a metadata set to extend, click the \\emph{Select} button for\n  \\emph{Parent Metadata Set} and then select the metadata set. When a\n  user creates a document of a document type that uses an extended\n  metadata set, the parent metadata set's fields appear above the\n  extended metadata set's.\n\\item\n  Add the metadata fields that should be part of this metadata set. To\n  do this, first select the editor's \\emph{View} tab and select the\n  \\emph{Fields} tab within it. Icons representing the field types are\n  listed on one side and the metadata set's canvas is on the other side.\n  To add a field type to the metadata set, select its icon, drag, and\n  drop it onto the canvas. The field appears on the canvas as it does\n  for users. By dragging a field onto a field that's already on the\n  canvas, you can nest the new field in the existing field. When you\n  mouse over a field on the canvas, the field action icons\n  (\\includegraphics{./images/icon-dm-metadata-actions.png}) appear.\n  Clicking the \\emph{+} icon creates a duplicate of the current field\n  and adds it below the current field. Clicking the trash can deletes\n  the field.\n\n  The following metadata fields are available:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Boolean:} A check box.\n  \\item\n    \\textbf{Color:} Specifies a color.\n  \\item\n    \\textbf{Date:} Enter a date. A valid date format is required for the\n    date field, but you don't have to enter a date manually. When you\n    select the date field a mini-calendar pops up which you can use to\n    select a date.\n  \\item\n    \\textbf{Decimal:} Enter a decimal number. The value is persisted as\n    a \\texttt{double}.\n  \\item\n    \\textbf{Documents and Media:} Select a file from a Documents and\n    Media library.\n  \\item\n    \\textbf{Geolocation:} Specify a location to associate with the\n    document.\n  \\item\n    \\textbf{HTML:} An area that uses a WYSIWYG editor to enhance the\n    content.\n  \\item\n    \\textbf{Integer:} Enter an integer. The value is persisted as an\n    \\texttt{int}.\n  \\item\n    \\textbf{Link to Page:} Link to another page in the same site.\n  \\item\n    \\textbf{Number:} Enter a decimal number or an integer. The value is\n    persisted either as a \\texttt{double} or an \\texttt{int}, depending\n    on the input's type.\n  \\item\n    \\textbf{Radio:} Displays several clickable options. The default\n    number of options is three but this is customizable. Only one option\n    can be selected at a time.\n  \\item\n    \\textbf{Select:} This is just like the radio field except that the\n    options are hidden and must be accessed from a drop-down menu.\n  \\item\n    \\textbf{Text:} Enter a single line of text.\n  \\item\n    \\textbf{Text Box:} This is just like the text field except you can\n    enter multiple lines of text or separate paragraphs.\n  \\item\n    \\textbf{Web Content:} Select web content.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/dm-metadata-set-fields.png}\n  \\caption{Add your metadata set's fields to the canvas.}\n  \\end{figure}\n\\item\n  Edit your fields to reflect their intended metadata. For example, a\n  text field's default label is \\emph{Text}. If you want to use the text\n  field as a title, for instance, then you should change the field's\n  label to \\emph{Title}. To do this, first select the field on the\n  canvas. This automatically selects the \\emph{Settings} tab on the\n  left. Alternatively, you can access the Settings tab by clicking the\n  field's wrench icon. To edit a setting value, double-click it in the\n  Settings table and enter the new value.\n\n  Labels, default values, variable names, mouse-over tips, widths, and\n  other settings can be configured for most fields. Some fields have a\n  \\emph{Required} setting for specifying whether users must populate the\n  field. If a field's \\emph{Repeatable} setting is \\emph{Yes}, users can\n  add multiple consecutive instances of the field to the document's\n  metadata.\n\n  Also note that you can translate each of a metadata set's field values\n  to any supported locales. To specify a field value for a translation,\n  select the flag that represents the locale and enter the field value\n  for the locale.\n\\item\n  Click \\emph{Save} when you're done specifying your new metadata set.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/dm-metadata-set-settings.png}\n  \\caption{Edit your metadata set's fields to match the metadata that\n  you want each field to hold.}\n  \\end{figure}\n\\end{enumerate}\n\n\\chapter{Document Types}\\label{document-types}\n\nDocument types are made of metadata fields and help users define the\npurpose of Document Library files. For example, a \\emph{Contract}\ndocument type may need metadata fields for the effective date,\nexpiration date, contract type, legal reviewer, and more. When users\ncreate Document Library files of the Contract document type, they can\nthen populate those metadata fields. Document types also help you\nintegrate files with other features like search and workflow. Search\nworks on file metadata so users can find files faster. You can also\napply workflows to specific document types. And you can more cleanly\norganize document libraries by designating folders to hold particular\ndocument types exclusively.\n\n\\section{Managing Document Types}\\label{managing-document-types}\n\nTo see the available document types, open the \\emph{Menu}\n(\\includegraphics{./images/icon-menu.png}), expand your site's menu, and\nnavigate to \\emph{Content \\& Data} → \\emph{Documents and Media}. Then\nclick the \\emph{Document Types} tab. A searchable table lists any\nexisting document types. The following actions are available for each\ndocument type via its Actions button\n(\\includegraphics{./images/icon-actions.png}):\n\n\\textbf{Edit}: Edit the document type.\n\n\\textbf{Permissions}: Set the document type's permissions.\n\n\\textbf{Delete}: Delete the document type. Note that document types\ndon't support the Recycle Bin. Once you delete a document type, it's\ngone forever.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/dm-doc-types-list.png}\n\\caption{The Document Types management window lets you view existing\ndocument types and create new ones.}\n\\end{figure}\n\n\\section{Creating Document Types}\\label{creating-document-types}\n\nFollow these steps to create a document type:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}),\n  expand your site's menu and navigate to \\emph{Content \\& Data} →\n  \\emph{Documents and Media}. Then click the \\emph{Document Types} tab.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n  The \\emph{New Document Type} form appears.\n\\item\n  Give your document type a name and a description.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/dm-doc-types-new.png}\n  \\caption{Create your new document type.}\n  \\end{figure}\n\\item\n  Define the metadata to use with your document type. You do this via\n  these sections in the form:\n\n  \\textbf{Main Metadata Fields:} These are tied directly to the document\n  type. They can be created only via the form and can't be used with\n  other document types. You create and edit these metadata fields in the\n  form the same way that you do when creating\n  \\href{/docs/7-2/user/-/knowledge_base/u/metadata-sets}{metadata sets}.\n\n  \\textbf{Additional Metadata Fields:} Select a metadata set to\n  associate with the document type. Each document type must be\n  associated with one or more metadata set. To differentiate document\n  types that use the same metadata sets, define different main metadata\n  fields.\n\\item\n  Define your document type's permissions via the form's\n  \\emph{Permissions} section. By default, anyone can view the document\n  type, including site guests. You can restrict its view, update,\n  delete, and permissions configuration to site members or the document\n  type's owner.\n\\item\n  Click \\emph{Save} when you're finished specifying your new document\n  type.\n\\end{enumerate}\n\nYour document type is now available when adding a document via the\nDocuments and Media's \\emph{Add} menu. When users create new files of\nthe document type, they're presented with metadata fields to describe\nthe document.\n\n\\chapter{Online File Creation and Editing with Google\nDocs™}\\label{online-file-creation-and-editing-with-google-docs}\n\nAlthough you can\n\\href{/docs/7-2/user/-/knowledge_base/u/adding-files-to-a-document-library\\#using-the-add-menu}{add}\nand\n\\href{/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files}{edit}\nDocuments and Media files via upload and download, Liferay DXP doesn't\ncontain a UI that lets you author or edit documents directly. You can,\nhowever, create and edit Documents and Media files online in Google\nDocs™, Google Sheets™, and Google Slides™.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} For simplicity and readability, this documentation refers\nonly to Google Docs™. The material, however, also applies to Google\nSheets™ and Google Slides™.\n\n\\noindent\\hrulefill\n\nNote that when you use Google Docs™ to create or edit a Documents and\nMedia file, that file isn't permanently stored in Google Docs™. Google\nDocs™ is only used for its editing UI. Your edits are then saved to the\nDocuments and Media Library.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/google-docs-new.png}\n\\caption{You can create new Google documents in Documents and Media.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/google-docs-edit.png}\n\\caption{You can also use Google's document editor to edit existing\nDocuments and Media files.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/google-docs-save-discard.png}\n\\caption{When using Google's document editor, you can save or discard\nyour changes via the editor's toolbar.}\n\\end{figure}\n\n\\chapter{Configuring Google Docs™\nIntegration}\\label{configuring-google-docs-integration}\n\nBefore you can use Google Docs™ to create and edit Documents and Media\nfiles, you must configure Liferay DXP to connect with an application in\nthe \\href{https://console.developers.google.com}{Google API Console}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You must be an administrator to complete these steps.\n\n\\noindent\\hrulefill\n\n\\section{Configure Your Google\nProject}\\label{configure-your-google-project-1}\n\nFirst, you must configure your Google project to use the Google Drive™\nAPI and set up OAuth 2 for use with that project.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\href{https://console.developers.google.com}{Google API\n  Console}. If you don't have a suitable project,\n  \\href{https://support.google.com/googleapi/answer/6251787?hl=en&ref_topic=7014522}{create\n  a new one}.\n\\item\n  Enable the Google Drive™ API for your project. For instructions, see\n  the Google API Console documentation on\n  \\href{https://support.google.com/googleapi/answer/6158841}{enabling\n  and disabling APIs}.\n\\item\n  Create an OAuth 2 client ID for your Google project. For instructions,\n  see the Google API Console documentation on\n  \\href{https://support.google.com/googleapi/answer/6158849}{setting up\n  OAuth 2.0}. Select \\emph{Web application} when prompted to select your\n  application type. Take note of the client ID and client secret that\n  appear---you'll need them to configure the portal to use the Google\n  Drive™ API.\n\\end{enumerate}\n\n\\section{Configuring the Portal}\\label{configuring-the-portal}\n\nNow that you have a Google project set up for use with Liferay DXP, you\nmust connect your installation to that project. You can do this at two\nscopes:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Globally, for all instances in your Liferay DXP installation.\n\\item\n  At the instance scope, for one or more instances in your Liferay DXP\n  installation.\n\\end{enumerate}\n\nYou can override the global configuration for one or more instances by\nconfiguring those instances separately. Similarly, you can configure\nonly the instances you want to connect to your Google project and leave\nthe global configuration empty.\n\nFollow these steps to configure your Liferay DXP installation to connect\nto your Google project:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Note that the configuration options are the same in the global and\n  instance-level configurations.\n\n  To access the global configuration, go to \\emph{Control Panel} →\n  \\emph{Configuration} → \\emph{System Settings} → \\emph{Documents and\n  Media}.\n\n  To access the instance-level configuration, go to \\emph{Control Panel}\n  → \\emph{Configuration} → \\emph{Instance Settings} → \\emph{Documents\n  and Media}.\n\\item\n  Under \\emph{VIRTUAL INSTANCE SCOPE}, select \\emph{Google Drive}.\n\\item\n  Enter your Google project's OAuth 2 client ID and client secret into\n  the \\emph{Client ID} and \\emph{Client Secret} fields.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To turn this feature off, delete the client ID and client\nsecret values from the form.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can ignore the \\emph{Picker API Key} field. This\nfield is unrelated to the Google Docs™ online editing features in\nLiferay DXP.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/google-drive-system-settings.png}\n\\caption{Enter your Google project's OAuth 2 client ID and client\nsecret.}\n\\end{figure}\n\n\\chapter{Creating and Editing Files with Google\nDocs™}\\label{creating-and-editing-files-with-google-docs}\n\nYou can use Google Docs™ to create and edit text documents,\nspreadsheets, or presentations for storage in the Docs and Media\nlibrary. When you finish your Google Docs™ editing session, your changes\nare automatically saved in the Documents and Media Library. You can\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[creating-files]{Create Files}\n\\item\n  \\hyperref[editing-files]{Edit Files}\n\\item\n  \\hyperref[multiple-editing-sessions]{Manage Multiple Editing Sessions}\n\\end{itemize}\n\n\\section{Authentication}\\label{authentication}\n\nThe first time you create or edit a Documents and Media file via Google\nDocs™, you must authenticate with your Google account. This links Google\nDrive™ to your portal account, so you only need to do this once. You can\nunlink your account at any time by navigating to User Menu →\n\\emph{Account Settings} → \\emph{General} → \\emph{Apps}, and clicking\n\\emph{Revoke} next to Google Drive™.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/google-docs-unlink.png}\n\\caption{You can unlink your Google account from the portal.}\n\\end{figure}\n\n\\section{Creating Files}\\label{creating-files}\n\nFollow these steps to create a new Documents and Media file via Google\nDocs™:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}), click\n  your site's name, and navigate to \\emph{Content \\& Data} →\n  \\emph{Documents and Media}.\n\\item\n  Click the \\emph{Add} icon (\\includegraphics{./images/icon-add.png})\n  and select the type of Google document to add to the Document Library.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Google Docs™\n  \\item\n    Google Slides™\n  \\item\n    Google Sheets™\n  \\end{itemize}\n\n  When you select one of these options, Liferay DXP creates a temporary\n  Documents and Media file and links it to a new Google file. Your\n  browser then redirects you to that Google file so you can create its\n  content.\n\n  Note that some actions on the temporary Documents and Media file can\n  affect its linked Google file. For more information, see\n  \\hyperref[multiple-editing-sessions]{multiple editing sessions}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/google-docs-new.png}\n  \\caption{Select the type of Google document you want to create.}\n  \\end{figure}\n\\item\n  Use the Google Docs™ editor to create your document's content. All\n  Google Docs™ features are available except for sharing.\n\\item\n  Save or discard your changes by clicking one of these toolbar buttons\n  in the Google Docs™ editor:\n\n  \\textbf{Save and Return to Liferay:} Saves your document as a new file\n  in the Documents and Media Library, deletes the Google file, and\n  returns you to the portal. The saved file's format depends on the type\n  of Google document you selected in step two above:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Google Docs™: Microsoft Word (\\texttt{.docx})\n  \\item\n    Google Slides™: Microsoft PowerPoint (\\texttt{.pptx})\n  \\item\n    Google Sheets™: Microsoft Excel (\\texttt{.xlsx})\n  \\end{itemize}\n\n  \\textbf{Discard Changes:} Returns you to the portal without saving\n  your file in the Documents and Media Library or Google Docs™.\n\n  Note that it's also possible to close the Google Docs™ window without\n  clicking either button. In this case, the editing session remains open\n  even though the window that displayed it is closed. For more\n  information, see the section below on\n  \\hyperref[multiple-editing-sessions]{multiple editing sessions}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/google-docs-save-discard.png}\n  \\caption{Save or discard your changes by using the toolbar in the\n  editor.}\n  \\end{figure}\n\\end{enumerate}\n\n\\section{Editing Files}\\label{editing-files}\n\nYou can use Google Docs™ to edit the following types of Documents and\nMedia files:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Text files (\\texttt{.docx}, \\texttt{.html}, \\texttt{.txt},\n  \\texttt{.rtf}, \\texttt{.odt})\n\\item\n  Presentation files (\\texttt{.pptx}, \\texttt{.odp})\n\\item\n  Spreadsheet files (\\texttt{.xlsx}, \\texttt{.ods}, \\texttt{.csv},\n  \\texttt{.tsv})\n\\item\n  PDF files\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Google Docs™ doesn't support older, non-XML-based\nMicrosoft Office file types (\\texttt{.doc}, \\texttt{.ppt},\n\\texttt{.xls}).\n\n\\noindent\\hrulefill\n\nFollow these steps to edit a Documents and Media file in Google Docs™:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the file in the Documents and Media Library.\n\\item\n  Click the file's Actions icon\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Edit in\n  Google Docs}. This automatically\n  \\href{/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files}{checks\n  out} the file, transfers its content to a new Google Docs™ file, and\n  redirects you to that Google Docs™ file.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/google-docs-edit.png}\n  \\caption{Select \\emph{Edit in Google Docs} from the file's Actions\n  menu.}\n  \\end{figure}\n\\item\n  Edit the file in Google Docs™. The editing process is exactly the same\n  as described above for creating files.\n\\end{enumerate}\n\n\\section{Multiple Editing Sessions}\\label{multiple-editing-sessions}\n\nWhen you create or edit a Documents and Media file in Google Docs™, you\ncan save or discard your changes by clicking \\emph{Save and Return to\nLiferay} or \\emph{Discard Changes}, respectively. If you instead close\nthe window without clicking either, the editing session still exists.\nYou can access it via the original file in Documents and Media. If the\nfile didn't exist before (e.g., you were creating a new file), it\nappears in Documents and Media as a temporary file.\n\nWhen an editing session already exists for a Documents and Media file,\nthe following actions are available via that file's Actions icon\n(\\includegraphics{./images/icon-actions.png}):\n\n\\textbf{Edit in Google Docs:} Resume editing the file in Google Docs™.\n\n\\textbf{Check in:} Saves the Google file (including any changes) to the\nDocuments and Media file, then deletes the Google file. This is\nequivalent to clicking \\emph{Save and Return to Liferay} in a Google\nDocs™ editing window.\n\n\\textbf{Cancel Checkout:} Deletes the Google file, discarding any\nchanges. This is equivalent to clicking \\emph{Discard Changes} in a\nGoogle Docs™ editing window.\n\n\\chapter{Integration with Microsoft Office\n365™}\\label{integration-with-microsoft-office-365}\n\nAlthough you can\n\\href{/docs/7-2/user/-/knowledge_base/u/adding-files-to-a-document-library\\#using-the-add-menu}{add}\nand\n\\href{/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files}{edit}\nDocuments and Media files via upload and download, Liferay DXP doesn't\ncontain a UI that lets you author or edit documents directly. You can,\nhowever, create and edit Documents and Media files online in Word™,\nExcel™, and PowerPoint™.\n\nNote that when you use Office 365™ to create or edit a Documents and\nMedia file, that file isn't permanently stored in Office 365™. Office\n365™ is only used for its editing UI. Your edits are then saved to the\nDocuments and Media Library.\n\nHere you can learn how to use it.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/office365-new.png}\n\\caption{You can create new Office 365™ documents in Documents and\nMedia.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/office365-edit.png}\n\\caption{You can also edit existing Documents and Media files in Office\n365™.}\n\\end{figure}\n\n\\chapter{Configuring Office 365™\nIntegration}\\label{configuring-office-365-integration}\n\nBefore you can use Office 365™ to create and edit Documents and Media\nfiles, you must configure Liferay DXP to connect with an application in\nthe \\href{https://portal.azure.com/}{Azure portal}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You must be an administrator to complete these steps.\n\n\\noindent\\hrulefill\n\n\\section{Register an Application with the Microsoft Identity\nPlatform}\\label{register-an-application-with-the-microsoft-identity-platform}\n\nFirst, configure your application with the Microsoft identity platform™.\nTo do so, follow the steps described in\n\\href{https://docs.microsoft.com/en-gb/graph/auth-register-app-v2}{Microsoft's\ndocumentation}.\n\nTo construct a URL for the \\emph{Redirect URI} parameter, follow this\npattern:\n\n\\begin{verbatim}\nhttps://[hostname]/o/document_library/onedrive/oauth2\n\\end{verbatim}\n\nHere's the minimum permission set needed to use Office 365™ integration:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Files.Read.All\n\\item\n  Files.ReadWrite.All\n\\end{itemize}\n\nFor more information about permissions, see\n\\href{https://docs.microsoft.com/graph/permissions-reference}{Microsoft's\ndocumentation}.\n\n\\section{Configuring Liferay DXP}\\label{configuring-liferay-dxp}\n\nNow you must connect your Liferay DXP installation with your Microsoft\nidentity platform™ application. You can do this at two scopes:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Globally, for all instances in your Liferay DXP installation.\n\\item\n  At the instance scope, for one or more instances in your Liferay DXP\n  installation.\n\\end{enumerate}\n\nYou can override the global configuration for one or more instances by\nconfiguring those instances separately. Similarly, you can configure\nonly the instances you want to connect to your application and leave the\nglobal configuration empty.\n\nFollow these steps to configure your Liferay DXP installation to connect\nto your application:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Note that the configuration options are the same in the global and\n  instance-level configurations.\n\n  To access the global configuration, go to \\emph{Control Panel} →\n  \\emph{Configuration} → \\emph{System Settings} → \\emph{Documents and\n  Media}.\n\n  To access the instance-level configuration, go to \\emph{Control Panel}\n  → \\emph{Configuration} → \\emph{Instance Settings} → \\emph{Documents\n  and Media}.\n\\item\n  Under \\emph{VIRTUAL INSTANCE SCOPE}, select \\emph{OneDrive}.\n\\item\n  Enter your application's OAuth 2 client ID and client secret in the\n  \\emph{Client ID} and \\emph{Client Secret} fields, respectively.\n\\item\n  Enter your tenant ID in the \\emph{Tenant} field. To find your tenant\n  ID, see\n  \\href{https://docs.microsoft.com/onedrive/find-your-office-365-tenant-id}{Microsoft's\n  documentation}.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Once enabled, you can disable this feature by deleting\nthe client ID, client secret, and tenant values from the form.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/onedrive-system-settings.png}\n\\caption{Enter your application's client ID, client secret, and tenant.}\n\\end{figure}\n\n\\chapter{Creating and Editing Documents and Media Files with Office\n365™}\\label{creating-and-editing-documents-and-media-files-with-office-365}\n\nYou can use Office 365™ to create and edit text documents, spreadsheets,\nor presentations for storage in the Documents and Media library. When\nyou finish your Office 365™ editing session, you must check in the\ndocument to save the changes in the Documents and Media Library.\n\nHere, you'll learn how to do these things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[authentication]{Authenticate with OneDrive™}\n\\item\n  \\hyperref[creating-files]{Create Files}\n\\item\n  \\hyperref[editing-files]{Edit Files}\n\\end{itemize}\n\n\\section{Authentication}\\label{authentication-1}\n\nThe first time you create or edit a Documents and Media file via Office\n365™, you must authenticate with your Microsoft account. This links\nOneDrive™ to your Liferay DXP account, so you only need to do this once.\nYou can unlink your account at any time by navigating to User Menu →\n\\emph{Account Settings} → \\emph{General} → \\emph{Apps}, and clicking\n\\emph{Revoke} next to OneDrive™.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/office365-unlink.png}\n\\caption{You can unlink your account from the portal.}\n\\end{figure}\n\n\\section{Creating Files}\\label{creating-files-1}\n\nFollow these steps to create a new Documents and Media file via Office\n365™:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}), click\n  your Site's name, and navigate to \\emph{Content \\& Data} →\n  \\emph{Documents and Media}.\n\\item\n  Click the \\emph{Add} icon (\\includegraphics{./images/icon-add.png})\n  and select the type of Office 365™ document to add to the Document\n  Library:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Word™\n  \\item\n    Excel™\n  \\item\n    PowerPoint™\n  \\end{itemize}\n\n  When you select one of these options, a new window opens for you to\n  enter the document's name.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/office365-new.png}\n  \\caption{Select the type of document you want to create.}\n  \\end{figure}\n\\item\n  Enter the document's name in the \\emph{Title} field, and click\n  \\emph{Save}. When you click \\emph{Save}, Liferay DXP creates a\n  temporary Documents and Media file and links it to the new Office 365™\n  file. Your browser then opens a new window with that Office 365™ file\n  so you can create its content.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/office365-creation-modal.png}\n  \\caption{Give the document a name.}\n  \\end{figure}\n\\item\n  Use the Office 365™ editor to create your document's content.\n\\item\n  Save or discard your changes to Liferay DXP:\n\n  \\textbf{Check in:} Saves the Office 365™ file to Documents and Media,\n  then deletes the file from Office 365™. The saved file's format\n  depends on the document type you selected in step two above.\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Word: Microsoft Word™ (\\texttt{.docx})\n  \\item\n    PowerPoint: Microsoft PowerPoint™ (\\texttt{.pptx})\n  \\item\n    Excel: Microsoft Excel™ (\\texttt{.xlsx})\n  \\end{itemize}\n\n  \\textbf{Cancel Checkout:} Deletes the Office 365™ file, discarding any\n  changes.\n\\end{enumerate}\n\n\\section{Editing Files}\\label{editing-files-1}\n\nYou can use Office 365™ to edit the following types of Documents and\nMedia files:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Text files (\\texttt{.doc}, \\texttt{.docx}, \\texttt{.docm},\n  \\texttt{.dot}, \\texttt{.dotx}, \\texttt{.dotm}, \\texttt{.html},\n  \\texttt{.txt}, \\texttt{.rtf}, \\texttt{.odt})\n\\item\n  Presentation files (\\texttt{.ppt}, \\texttt{.pptx}, \\texttt{.pptm},\n  \\texttt{.pps}, \\texttt{.ppsx}, \\texttt{.ppsm}, \\texttt{.pot},\n  \\texttt{.potx}, \\texttt{.potm})\n\\item\n  Spreadsheet files (\\texttt{.xls}, \\texttt{.xlsx}, \\texttt{.xlsm},\n  \\texttt{.xlt}, \\texttt{.xltx}, \\texttt{.xltm}, \\texttt{.ods},\n  \\texttt{.csv}, \\texttt{.tsv}, \\texttt{.txt}, \\texttt{.tab})\n\\end{itemize}\n\nFollow these steps to edit a Documents and Media file in Office 365™:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the file in the Documents and Media Library.\n\\item\n  Click the file's Actions icon\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Edit in\n  Office 365}. This automatically\n  \\href{/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files}{checks\n  out} the file, transfers its content to a new Office 365™ file, and\n  redirects you to that file Office 365™.\n\\item\n  Edit the file in Office 365™. The editing process is exactly the same\n  as described above for creating files.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/office365-edit.png}\n  \\caption{Select \\emph{Edit in Office 365} from the file's Actions\n  menu.}\n  \\end{figure}\n\\end{enumerate}\n\n\\chapter{Store Types}\\label{store-types}\n\nYou can change the file system (the \\emph{store}) that the Documents and\nMedia library uses to store files. This is configured in the\n\\texttt{portal-ext.properties} file by setting the\n\\texttt{dl.store.impl=}property. Configuring stores is covered in\n\\href{/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration}{the\nDocument Repository Configuration guide}. Here, you'll consider the\nramifications of different stores:\n\n\\textbf{Simple File System Store:} Uses the file system (local or a\nmounted share) to store files. This is the default store.\n\n\\textbf{Advanced File System Store:} Nests files into directories by\nversion, for faster performance and to store more files.\n\n\\textbf{DBStore (Database Storage)}: Stores files in the Liferay DXP\ndatabase. The file (stored as a blob) size limit is 1 GB. Use the Simple\nFile System Store or Advanced File System Store to store larger files.\n\n\\textbf{S3Store (Amazon Simple Storage)}: Uses Amazon's cloud-based\nstorage solution.\n\nIf you must move your files from one store to another, use the migration\nutility in \\emph{Control Panel} → \\emph{Configuration} → \\emph{Server\nAdministration} → \\emph{Data Migration}.\n\n\\section{Simple File System Store}\\label{simple-file-system-store}\n\nThe Simple File System Store is the default store. It stores Documents\nand Media files on the server's file system (local or mounted). This\nstore is heavily bound to Liferay DXP's database. The store's default\nroot folder is \\texttt{{[}Liferay\\ Home{]}/data/document\\_library}. You\ncan change this via the \\texttt{dl.store.file.system.root.dir=} property\nin a \\texttt{portal-ext.properties} file, or in the Control Panel. For\ninstructions on this, see the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration}{Document\nRepository Configuration guide}.\n\nThe Simple File System Store uses a local folder to store files. You can\nuse the file system for your clustered configuration, but you must make\nsure the folder you point the store at can handle things like concurrent\nrequests and file locking. You must therefore use a Storage Area Network\nor a clustered file system.\n\nThe Simple File System Store creates a folder structure based on primary\nkeys in Liferay DXP's database. If, for example, you upload a\npresentation with the file name \\texttt{workflow.odp} to a folder named\n\\texttt{stuff}, the store creates a folder structure like this:\n\n\\begin{verbatim}\n/companyId/folderId/numericFileEntryName/versionNumber\n\\end{verbatim}\n\n\\texttt{companyId}: The site's company ID.\n\n\\texttt{folderId}: The ID of the Documents and Media folder containing\nthe document.\n\n\\texttt{numericFileEntryName}: The document's numeric file entry name.\n\n\\texttt{versionNumber}: The document's version number.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/enterprise-file-system-store.png}\n\\caption{The Simple File System Store creates a folder structure based\non primary keys in Liferay DXP's database.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Be careful not to confuse a document's numeric file entry\nname from its document ID. Each has an independent counter. The numeric\nfile entry name is used in the folder path for storing the document, but\nthe document ID is not. The numeric file entry name can be found in the\n\\texttt{name} column of the \\texttt{DLFileEntry} table in Liferay DXP's\ndatabase; the document ID can be found in the \\texttt{fileEntryId}\ncolumn of the same table.\n\n\\noindent\\hrulefill\n\n\\section{Using the Advanced File System\nStore}\\label{using-the-advanced-file-system-store}\n\nThe Advanced File System Store, like the Simple File System Store, saves\nfiles to the local file system. It uses a slightly different folder\nstructure, however, and can overcome operating system limitations on the\nnumber of files stored in a particular folder by programmatically\ncreating a structure that can expand to millions of files. It\nalphabetically nests the files in folders. This also improves\nperformance, as there are fewer files stored per folder.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/enterprise-adv-file-system-store.png}\n\\caption{The Advanced File System Store creates a more nested folder\nstructure than the Simple File System Store.}\n\\end{figure}\n\nThe same rules apply to the Advanced File System Store as apply to the\nSimple File System Store. To cluster it, you must point the store to a\nnetwork mounted file system that all the nodes can access. That\nnetworked file system must also support concurrent requests and file\nlocking. Otherwise, you may experience data corruption issues if two\nusers attempt to write to the same file at the same time from two\ndifferent nodes.\n\nSee the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/using-the-advanced-file-system-store}{Document\nRepository Configuration guide} for instructions on using the Advanced\nFile System Store.\n\n\\section{Using Amazon Simple Storage\nService}\\label{using-amazon-simple-storage-service}\n\nAmazon's Simple Storage Service (S3) is a cloud-based storage solution\nthat you can use with Liferay DXP. It lets you store your documents to\nthe cloud seamlessly from all nodes.\n\nWhen you sign up for the service, Amazon assigns you unique keys that\nlink you to your account. In Amazon's interface, you can create\n\\emph{buckets} of data optimized by region. Once you create these to\nyour specifications, follow\n\\href{/docs/7-2/deploy/-/knowledge_base/d/using-amazon-simple-storage-service}{these\ninstructions} to connect your repository to Liferay DXP.\n\nConsult Amazon's S3 documentation for more information.\n\n\\chapter{Liferay Sync}\\label{liferay-sync}\n\nLiferay Sync synchronizes files between your Liferay DXP server and\nusers' desktop and mobile environments. With Liferay Sync, users can\npublish and access shared documents and files from their native\nenvironments without using a browser. Clients for Windows and Mac OS\ndesktops and Android and iOS mobile platforms are supported. As users\nadd and collaborate on documents and files, Liferay Sync automatically\nsynchronizes them across all configured Sync clients and your server.\nLiferay Sync integrates completely with Liferay DXP so that features\nsuch as authentication and versioning function in the supported\nenvironments. The Liferay Sync desktop client stores files locally so\nthey're always available, even when users are offline. It automatically\nsynchronizes files upon client reconnection. The Liferay Sync mobile\nclient saves storage space on users' devices by downloading only the\nfiles they choose.\n\n\\chapter{Administering Liferay Sync}\\label{administering-liferay-sync}\n\nBefore your users can use Liferay Sync with their Sites, you must\ninstall and configure it on your Liferay DXP server. The articles here\nwalk you through this and also discuss important topics like preventing\naccidental file deletion and ensuring Sync security. As such, make sure\nyou thoroughly read each article before letting your users connect to\nSync.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To install and configure Liferay Sync on your Liferay DXP\nserver, you must be an administrator.\n\n\\chapter{Installing Liferay Sync's\nPrerequisites}\\label{installing-liferay-syncs-prerequisites}\n\nLiferay Sync requires the \\emph{Liferay CE Sync Connector} app from\n\\href{https://web.liferay.com/marketplace}{Liferay Marketplace}. This\napp enables and configures Sync in your Liferay DXP instance. For\nexample, you can disable Sync across the instance or on a site-by-site\nbasis. Note that Sync is enabled by default for all Sites.\n\nFor instructions on installing Marketplace apps, see\n\\href{/docs/7-2/user/-/knowledge_base/u/using-the-liferay-marketplace}{the\nLiferay Marketplace documentation}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Liferay Sync Security module that Sync requires is\nincluded and enabled by default in Liferay DXP. You can verify this by\nensuring that the \\texttt{SYNC\\_DEFAULT} and \\texttt{SYNC\\_TOKEN}\nentries are enabled in \\emph{Control Panel} → \\emph{Configuration} →\n\\emph{Service Access Policy}.\n\n\\noindent\\hrulefill\n\nIf you want to use Sync Connector's default settings and are fine with\nSync being enabled for all your Sites, you can skip the articles that\nfollow on configuring Sync. However, before directing your users to\ninstall and configure the Sync desktop and mobile clients, \\textbf{make\nsure to read} this guide's articles on preventing accidental file\ndeletion and ensuring Sync security. You should also \\textbf{warn your\nusers} about the potential for accidental data loss.\n\n\\chapter{Configuring Liferay Sync}\\label{configuring-liferay-sync}\n\nSync Connector lets you manage how, or if, clients connect to your\nLiferay DXP server. You can also configure default file permissions on a\nper-Site basis, and manage the devices that connect to your Liferay DXP\ninstance. To access Sync Connector, select \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{Sync Connector Admin}.\n\nSync Connector Admin has three tabs:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{Settings:} Control Sync's general behavior. These settings\n  apply globally to Sync.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sync-admin-01.png}\n  \\caption{The Control Panel's Configuration section contains Sync\n  Connector Admin.}\n  \\end{figure}\n\n  \\textbf{Allow the use of Sync?:} Whether Sync is enabled.\n\n  \\textbf{Allow users to sync their personal Sites?:} Whether users can\n  sync data with their personal Sites.\n\n  \\textbf{Allow LAN Syncing?:} Whether desktop clients attempt to\n  download updates from other desktop clients on the same local network\n  before downloading from the server. This can help reduce server load\n  and increase data transfer speeds. Note that LAN syncing only works\n  with clients that also enable it.\n\n  \\textbf{Max Connections:} The maximum number of simultaneous\n  connections each client is allowed per account. For example, if Max\n  Connections is three, a client can simultaneously upload or download\n  up to three files for each account. Note, this setting operates on a\n  per client basis. If Max Connections is set to three and a user has\n  two clients connected to an account (which is possible if Sync is\n  installed on two different machines), then the user is effectively\n  allowed six simultaneous connections. While increasing Max Connections\n  can speed up file transfers it also places a heavier load on the\n  server. \\emph{Max Connections} is set to one by default.\n\n  \\textbf{Poll Interval:} The frequency in seconds that clients\n  automatically check the Liferay DXP instance for updates. For example,\n  if set to ten, connected clients check the instance for updates every\n  ten seconds. The default Poll Interval is five.\n\n  \\textbf{Max Download Rate:} The maximum transfer rate, in bytes, at\n  which clients can download. A value of 0 specifies no limit. This\n  setting takes precedence over clients' download rate setting.\n\n  \\textbf{Max Upload Rate:} The maximum transfer rate, in bytes, at\n  which clients can upload. A value of 0 specifies no limit. This\n  setting takes precedence over clients' upload rate setting.\n\n  \\textbf{Force Security Mode:} Whether to force security mode on mobile\n  clients. Security mode encrypts Sync files on the device and requires\n  a passcode when accessing the Sync mobile app.\n\\item\n  \\textbf{Sites:} Control Sync on a per-Site basis.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sync-admin-02.png}\n  \\caption{Sync Connector Admin's Sites tab lets you manage Sync on a\n  per-Site basis.}\n  \\end{figure}\n\n  For each Site in the Liferay DXP instance, the Sites tab lists each\n  Site's default file permissions (more on this in a moment) and whether\n  Sync is enabled for that Site. Sync is enabled by default for all\n  Sites. To disable Sync for a Site, click the Site's \\emph{Actions}\n  button (\\includegraphics{./images/icon-actions.png}) and select\n  \\emph{Disable Sync Site}. \\textbf{Please use caution} when disabling\n  Sync for a Site, as doing so \\textbf{deletes} files for that Site from\n  the Sync clients. Disabling Sync for a Site, however, doesn't affect\n  the Site's files on the server.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Warning:** Disabling Sync for specific Sites from Sync Connector Admin \n can result in data loss across clients. If Sync is disabled for a Site \n users are currently syncing, any files in the clients' sync folders for \n that Site are automatically deleted from their clients. If a user is \n offline when Sync is disabled for a Site, any offline changes or additions \n they make are deleted upon client reconnection. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\nYou can enable Sync for a Site by selecting *Enable Sync Site* from its \nActions button. Make sure that each Site for which Sync is enabled has a \nDocuments and Media app on at least one of its pages. If a Site doesn't have \nthe app on any of its pages and users click the *Open Website* link from \ntheir Sync menus, the error message *The requested resource was not found* \nappears. \n\nThe Sites tab also sets default file permissions for files uploaded from\nSync clients. The process for setting permissions is nearly the same as for\nenabling or disabling Sync for Sites. To set the default file permissions\nfor a single Site, click its Actions button and select *Default File\nPermissions*. This lets you select the default file permissions for that\nSite. Click *Choose* for the permissions you want to use. \n\n![ Click *Choose* to select the default file permissions for a Site in Sync.](./images/sync-admin-03.png)\n\nTo set the default file permissions for several Sites, select the checkboxes \nfor the Sites, click the *Default File Permissions* link that appears above \nthe table, and select the permissions you want to use. Default file \npermissions might behave differently than you'd expect. They control *only* \nthe permissions for new files uploaded through the Sync clients; they don't \naffect permissions for uploading or restrict document owners (the user who \noriginally uploaded a document) in any way. For example, even if you set a \nSite's default file permissions to View Only, that Site's users can still \nupload new documents to the Site. The file's owner has edit permission; the \nrest of the Site's users have the View Only permission. \n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  \\textbf{Devices:} View and manage the devices registered with Sync.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sync-admin-devices.png}\n  \\caption{Sync Connector Admin's Devices tab lists all the devices Sync\n  has registered.}\n  \\end{figure}\n\n  Each row in the Devices tab's table represents a device. The\n  \\emph{Name} column lists the user that registered the device. The\n  remaining columns list each device's location, client type, client\n  build number, last connection date, and status. Each device's Actions\n  button (\\includegraphics{./images/icon-actions.png}) manages that\n  device. You can change a device's status from Active to Inactive by\n  selecting \\emph{Actions} → \\emph{Disable Sync Device}. Inactive\n  devices can't sync. Inactive mobile devices also can't access local\n  Sync files. Once a device is Inactive, you can erase Sync files from\n  it by selecting \\emph{Actions} → \\emph{Wipe Sync Device}. This also\n  signs the device out and removes the account from the client. If the\n  device is offline, this happens when it tries to reconnect. The\n  Actions menu also enables or deletes an Inactive device. Deleting a\n  device only removes it from the list of registered devices; it can\n  still reconnect and reregister.\n\\end{enumerate}\n\n\\chapter{Preventing Accidental File Deletion in Liferay\nSync}\\label{preventing-accidental-file-deletion-in-liferay-sync}\n\nLiferay Sync's power rests in its ability to propagate between Liferay\nDXP and connected Sync clients. When a user deletes a file from a\nconnected client, Sync also deletes the file in the instance and in any\nother connected clients. Likewise, if a user deletes a file in the\ninstance, Sync also deletes the file in all connected clients. In other\nwords, anywhere a user deletes a file, Sync deletes it\n\\emph{everywhere}. But don't worry: Liferay DXP's Recycle Bin is enabled\nby default and lets you recover deleted files. You can access the\nRecycle Bin from each Site's \\emph{Site Administration} menu.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Liferay Sync automatically propagates file and folder\ndeletion through the Liferay DXP server and in all connected clients. If\nan instance or Site administrator disables the Recycle Bin, deleted\nfiles can't be recovered.\n\n\\noindent\\hrulefill\n\nLiferay DXP instance and Site administrators can, of course, disable the\nRecycle Bin. Disabling the Recycle Bin in a Site, however, leaves the\nSite vulnerable to accidental file deletions that propagate through\nSync.\n\n\\chapter{Ensuring Liferay Sync\nSecurity}\\label{ensuring-liferay-sync-security}\n\nAs an administrator, you have a stake in the security of all connections\nto and from your servers. As long as Liferay DXP is configured to use\nHTTPS, Sync clients use user-supplied credentials to communicate\nsecurely. Users can only access the documents and Sites they're\npermitted to access. To support Security Mode in the Sync mobile client\nand securely transmit files, your Liferay DXP server must also use SSL.\nThe next section demonstrates how Sync's permissions work with your\nLiferay DXP instance's permissions.\n\n\\section{Liferay Sync Permissions\nDemonstration}\\label{liferay-sync-permissions-demonstration}\n\nSync uses Liferay DXP's default permissions to determine files and\nfolders to sync with the user's devices. It can only sync files a user\ncan access. After installing the desktop Sync client, follow the steps\nbelow to test this functionality.\n\nFirst, enter \\texttt{classified\\ information} into a new text file and\nsave it on your desktop as \\texttt{secret.txt}. Then use your browser to\nsign into Liferay DXP and create a new user with the user name\n\\emph{secretagent} and the email address \\emph{secretagent@liferay.com}.\nGive this user a password and then create a new private Site called\n\\emph{Secret Site}. Create a page on the Site and add the Documents and\nMedia app to it. Then add the secretagent user to the Secret Site and\ngrant the \\emph{Site Administrator} Role to the user. Log in as\nsecretagent and navigate to the Secret Site. Then upload the\n\\texttt{secret.txt} document to the Documents and Media app. Make sure\nyou also have a user that isn't a member of the Secret Site and\ntherefore doesn't have access to any of its documents through Sync. If\nyou don't have such a user, create one now.\n\nNext, configure your Liferay Sync client to sign in with the secretagent\nuser's credentials and sync with the Secret Site. Open the Liferay Sync\nmenu from the system tray and select \\emph{Preferences}. In the\n\\emph{Accounts} tab, click the plus icon at the window's bottom left to\nadd an account. Provide the secretagent user's credentials and uncheck\nall Sites except the Secret Site. Now confirm that Sync downloaded the\n\\texttt{secret.txt} file to your new Sync folder. Open it and check that\nit contains the text \\texttt{classified\\ information}. Next, use Sync to\nconnect to your Liferay DXP instance with the user that doesn't belong\nto the Secret Site. The file doesn't sync because this user isn't a Site\nmember.\n\nNow go to Sync Connector Admin and set the Secret Site's default file\npermissions to View Only. Create a new user, add it to the Secret Site,\nand add its account in your Liferay Sync client. As with the secretagent\nuser, Sync downloads the \\texttt{secret.txt} file to this user's local\nSync folder because the user is a member of the Secret Site. Now edit\nand save this file. Even though you can edit and save it locally, the\nedits aren't synced because the Site's default file permissions are View\nOnly. After attempting the sync, a red \\emph{x} appears next to the file\nin the local Sync folder. Right click the file to see the error. It\nconfirms the user doesn't have the required permissions.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-file-permissions-error.png}\n\\caption{The upload error occurs because the user only has permission to\nview files.}\n\\end{figure}\n\nTo confirm that the error didn't propagate through Sync, open the file\nin the secretagent user's local Sync folder. It still contains the\noriginal text. Likewise, the original file remains in the Site's\nDocuments and Media portlet. To get rid of the error in the other user's\nlocal Sync folder, return there and then right click the file and select\n\\emph{Download From Server}. This replaces the file with the latest file\nin the Liferay DXP instance.\n\nNow edit \\texttt{secret.txt} in the secretagent user's local Sync\nfolder. When you check the file in the other user's local Sync folder\nand in the Liferay DXP instance, notice that Sync propagated the edits.\nThe changes were propagated because the secretagent user owns the file\nin the instance. Owners can do anything with their files, even when the\nSite's default file permissions are set to View Only.\n\n\\chapter{Using Liferay Sync on Your\nDesktop}\\label{using-liferay-sync-on-your-desktop}\n\nLiferay Sync synchronizes files between your Liferay DXP Sites and\ndesktop devices. It lets you work with your files without using a\nbrowser. The Sync clients also ensure that the files are updated with\nthe latest changes made by other users. To use Liferay Sync in your\ndesktop environment, you must install the Sync desktop client. It's\ncurrently available for Windows and Mac OS. The Sync client stores files\nlocally so that they're always available, even when you're offline.\nFiles are automatically synchronized upon your client's reconnection to\nyour Liferay DXP server.\n\nOn your desktop devices, Liferay Sync creates a new folder structure\nthat it uses to synchronize files. You can treat the files the same as\nyou do any others. Credentials, Sync folder location, and other options\nare configured in the client. Also, native desktop notification events\ninform you of what Sync is doing. The native menu and task bar\nintegration keep Sync controls within easy reach.\n\nThis guide walks you through setting up and using the Liferay Sync\nclient on your desktop. Before proceeding, check your Liferay DXP\ninstance or Site administrator to ensure that Sync is enabled for your\nSites.\n\n\\chapter{Installing and Configuring the Desktop Liferay Sync\nClient}\\label{installing-and-configuring-the-desktop-liferay-sync-client}\n\nYou can download the desktop client from the\n\\href{https://customer.liferay.com/downloads}{Liferay customer downloads\npage}. Note that you'll need a Liferay account for this. Once you've\ndownloaded the appropriate desktop client for your operating system,\ninstalling Liferay Sync on Windows or Mac OS is straightforward.\n\n\\section{Installing the Liferay Sync Desktop\nClient}\\label{installing-the-liferay-sync-desktop-client}\n\nTo install the Liferay Sync client on Windows, you must have\nadministrator privileges. Upon launching the Windows application\ninstaller, you're prompted to choose an install location. Select an\nappropriate location and click \\emph{Install}. Sync automatically starts\nafter the installation finishes. The first time Sync runs, you must\nconfigure it to connect and sync with Liferay DXP. The configuration\nsteps are shown below.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can upgrade previous versions of the desktop Liferay\nSync client to version 3.0. When doing so, however, you must set up your\naccount again in the new version of the client. Prior to upgrading, it's\ntypically best to shut down Liferay Sync, backup files from your local\nSync folder, and delete that folder.\n\n\\noindent\\hrulefill\n\nThe Liferay Sync client for Mac is packaged in a DMG file.\nDouble-clicking on the DMG file mounts it as a disk image and opens a\nwindow showing the image's contents. To install Sync, drag the Liferay\nSync icon to your Applications folder. Once it's installed, run it from\nthe Applications folder. If you're running Mac OS X 10.9 or lower,\nyou're prompted for your machine's administrator credentials to install\nthe Finder icon/context menu tool. This prompt only appears when\ninstalling or upgrading the tool.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-mac-install.png}\n\\caption{Drag the Liferay Sync icon to the Applications folder.}\n\\end{figure}\n\nNext, you'll configure the Sync client.\n\n\\section{Configuring the Liferay Sync Desktop\nClient}\\label{configuring-the-liferay-sync-desktop-client}\n\nNow that you've installed Sync, you're ready to configure it! The\nconfiguration steps for Sync on Windows and Mac are identical.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open Sync and enter your server's address along with your account\n  credentials. Click \\emph{Sign In} when you're finished.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sync-setup-01.png}\n  \\caption{The first time you run Liferay Sync, you must tell it how to\n  communicate with your Liferay DXP server.}\n  \\end{figure}\n\n  When connecting to a server via HTTPS, an error appears if the\n  certificate can't be verified. Choosing \\emph{Proceed Anyway} bypasses\n  verification and leaves the connection open to compromise. Liferay\n  Sync attempts to read the certificates specified in the Java Control\n  Panel\n  (\\href{https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/jcp.html\\#A1152831}{see\n  section 20.4.5}). If Java isn't installed, you can also put your\n  certificates in \\texttt{{[}user.home{]}/.liferay-sync-3/certificates}.\n  Liferay Sync trusts all certificates in this folder.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sync-certificate-error.png}\n  \\caption{When connecting over HTTPS, Liferay Sync produces an error if\n  it can't verify the security certificate. Choosing \\emph{Proceed\n  Anyway} bypasses verification and leaves the connection open to\n  compromise.}\n  \\end{figure}\n\\item\n  Select the Sites you want to sync with. You can search for a Site in\n  the \\emph{Search} bar above the Site list. If you want to sync all the\n  subfolders of your selected Sites, click \\emph{Proceed} and move on to\n  the next step.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sync-setup-02.png}\n  \\caption{Select the Sites you want to sync with. Clicking a Site's\n  gear icon opens another window where you can choose to sync with only\n  specific subfolders in that Site.}\n  \\end{figure}\n\n  To sync only specific folders in a Site, first click the Site's gear\n  icon. In the window that appears, all folders are selected by default.\n  Unselect the folders you don't want to sync with. Unselecting a\n  subfolder causes the parent folder's checkbox to show a minus sign,\n  indicating that you haven't selected all of the parent folder's\n  subfolders. To sync only the documents at the top of a folder's\n  hierarchy (no subfolders), unselect all of that folder's subfolders.\n  You can also do this by clicking the folder's checkbox until the minus\n  sign appears. Click \\emph{Select} when you're finished with your\n  selections, and then click \\emph{Proceed} to move on to the next step.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sync-select-folders.png}\n  \\caption{Choose the Site's subfolders that you want to sync with. The\n  checkbox with the minus sign indicates that not all of the\n  \\emph{registration} folder's subfolders are selected.}\n  \\end{figure}\n\\item\n  Specify the local folder to sync. This folder is used exclusively for\n  Sync: Sync creates it and it must not conflict with any existing local\n  folder. The Sync folder's default name is the instance's host name,\n  and its default location is the user's documents folder. For example,\n  since the instance in the following screenshots runs locally at the\n  address \\texttt{http://localhost:8080/}, Sync creates a Sync folder\n  named \\emph{localhost} in the user's documents folder. You can, of\n  course, specify any unique name and location for the Sync folder.\n  Click \\emph{Start Syncing} to begin syncing files.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Syncing to network drives is not supported because Liferay Sync \n can't reliably detect local file changes on such drives. \n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n![ Specify your local Sync folder's name and location.](./images/sync-setup-03.png)\n\\end{verbatim}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{3}\n\\item\n  Celebrate! You've successfully set up Liferay Sync! Sync congratulates\n  you on setting it up and begins to sync files from the Sites you\n  selected to your local Sync folder. Note, completing the initial\n  synchronization may take a significant amount of time, depending on\n  the amount of data being transferred. You can safely close the window\n  as syncing continues in the background. To view the local Sync folder,\n  click \\emph{Open Folder}. To open Sync's preferences, click the small\n  gray text \\emph{advanced setup} near the top-right.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sync-setup-04.png}\n  \\caption{Congratulations, you've successfully set up Liferay Sync!}\n  \\end{figure}\n\\end{enumerate}\n\n\\chapter{Using the Liferay Sync Desktop\nClient}\\label{using-the-liferay-sync-desktop-client}\n\nWhen Liferay Sync is running, its icon appears in your task bar\n(Windows) or menu bar (Mac). Clicking this icon opens a menu that lets\nyou work with and manage Liferay Sync.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-toolbar-01.png}\n\\caption{The Liferay Sync menu in the Windows task bar and Mac menu bar\ngives you quick access to Sync.}\n\\end{figure}\n\nThe top of this menu shows your Sync status. If all your selected Sites\nare synced, then your status is \\emph{Synced}.\n\nBelow your Sync status, the menu lists three shortcuts for accessing\nyour files:\n\n\\textbf{Open Sync Folder:} Select a Site to open its local Sync folder.\n\n\\textbf{View Website:} Select a Site to view the page in Liferay DXP\nthat contains its Documents and Media app.\n\n\\textbf{Recent Files:} Lists recently created and modified files in the\nrepositories you can access.\n\nNote that if you sync with two or more Liferay DXP instances, Sync shows\neach at the top of the menu instead of your Sync status. Mouse over each\ninstance to reveal a submenu with that instance's Sync status and file\nshortcuts.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-toolbar-02.png}\n\\caption{When you sync with more than one Liferay DXP instance, Sync\nshows submenus for each.}\n\\end{figure}\n\nFinally, regardless of how many Liferay DXP instances you sync with, the\nmenu lists the following three options:\n\n\\textbf{Preferences:} Open Sync's preferences.\n\n\\textbf{Help:} Open Sync's documentation.\n\n\\textbf{Quit:} Shut down Sync on your machine.\n\nNext, you'll learn how to use Sync's preferences to control how Sync\nfunctions on your machine.\n\n\\section{Using Sync Preferences}\\label{using-sync-preferences}\n\nYou can use Sync's preferences to add/remove Liferay DXP instances to\nsync with, edit connection settings, and control Sync's basic behavior.\nOpen Sync's preferences by clicking the Sync icon in the task bar\n(Windows) or menu bar (Mac OS) and selecting \\emph{Preferences}. A\npreference screen for your instance accounts displays. This is the\n\\emph{Accounts} tab in \\emph{Preferences}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-preferences-accounts-01.png}\n\\caption{The Preferences menu's \\emph{Accounts} tab lets you manage\nsyncing with Sites per account.}\n\\end{figure}\n\nThe \\emph{Accounts} tab contains the following options:\n\n\\textbf{Accounts:} The accounts you sync with. When you select an\naccount, the Sites you have permission to sync with are shown on the\nright under \\emph{Syncing Sites}. You can use the plus, minus, and\npencil icons at the bottom of the account list to add, delete, or edit\nan account, respectively. You should use caution when deleting an\naccount from your Sync client, as doing so also deletes any local files\nand folders for that account. Adding an account takes you through the\nsame set of steps you used to set up the Sync client.\n\\href{/docs/7-2/user/-/knowledge_base/u/installing-and-configuring-the-desktop-liferay-sync-client\\#configuring-the-liferay-sync-desktop-client}{Click\nhere} for instructions on this.\n\n\\textbf{Syncing Sites:} The Sites you have permission to sync with for\nthe selected account. The Sites you currently sync with are shown under\n\\emph{Selected Sites}. Other Sites available for syncing are shown under\n\\emph{Unselected Sites}. To change the Sites you sync with, click the\n\\emph{Manage Sites} button. The window that appears lets you select\nand/or unselect Sites to sync with. This window is identical to the one\nthat appeared when you first configured the client.\n\\href{/docs/7-2/user/-/knowledge_base/u/installing-and-configuring-the-desktop-liferay-sync-client\\#configuring-the-liferay-sync-desktop-client}{Click\nhere} and see step two for instructions on using it. Use caution when\nde-selecting Sites. De-selecting a Site deletes its folder on your\nmachine.\n\n\\textbf{Location:} The selected account's local Sync folder location.\nClick the \\emph{Change} button to change this folder's location.\n\nThe Preferences menu's \\emph{General} tab contains settings for the Sync\nclient's general behavior. It lists the following options:\n\n\\textbf{Launch Liferay Sync on startup:} Starts Sync automatically each\ntime your machine starts.\n\n\\textbf{Show desktop notifications:} Shows a small notification in the\ncorner of your screen when a synced file changes.\n\n\\textbf{Automatically check for updates:} Automatically check for new\nclient versions. You can click the \\emph{Check Now} button to check for\nupdates manually.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-preferences-general-01.png}\n\\caption{The Preferences menu's \\emph{General} tab contains settings for\nSync's general behavior.}\n\\end{figure}\n\nFinally, the Preferences menu's \\emph{Network} tab controls how Sync\ntransfers data with your Liferay DXP servers. It contains the following\noptions:\n\n\\textbf{Download Rate:} To limit the rate at which Sync downloads data,\nselect \\emph{Limit to} and then specify the rate.\n\n\\textbf{Upload Rate:} To limit the rate at which Sync uploads data,\nselect \\emph{Limit to} and then specify the rate.\n\n\\textbf{Enable LAN Syncing:} Whether to download updates from other\ndesktop clients on the same local network before downloading from the\nserver. This can help reduce server load and increase data transfer\nspeeds. Note that LAN syncing only works when enabled in the Liferay DXP\ninstance by the administrator, and in other clients.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-desktop-prefs-network.png}\n\\caption{The Preferences menu's \\emph{Network} tab contains settings for\nSync's data transfer behavior.}\n\\end{figure}\n\nNote that your Liferay DXP administrator can also limit the\ndownload/upload rate. In this case, Liferay DXP's settings take\nprecedent. For example, if you set a 5.0 MB/s download rate in the\nclient but Liferay DXP's download limit is 2.0 MB/s, the latter takes\nprecedence. Also, the client's rate applies across all its accounts. For\nexample, if the client connects to three accounts and its download rate\nis 5.0 MB/s, then the sum of the download rate for all three accounts\nnever exceeds 5.0 MB/s.\n\n\\chapter{Using Your Local Liferay Sync\nFolder}\\label{using-your-local-liferay-sync-folder}\n\nOnce you configure and run Sync, Sync automatically uploads to Liferay\nDXP any files you add or modify in your Sync folder. Sync also downloads\nto your Sync folder any file changes by other users. If you delete a\nfile in your Sync folder, Sync also deletes it from the server and other\nclients. You should therefore use \\textbf{extreme caution} when deleting\nfiles in your Sync folder. If, however, you accidentally delete a file,\nall is not lost! The file can still be recovered from the instance's\nRecycle Bin, which is enabled by default. Note, if the administrator has\ndisabled the Recycle Bin, recovering deleted files is impossible.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Deleting a file in your Sync folder also deletes it in\nthe Liferay DXP instance and in other clients. If you accidentally\ndelete a file, it can be recovered from the instance's Recycle Bin. The\nRecycle Bin is enabled by default. File recovery is, however, impossible\nif the instance or Site administrator has disabled the Recycle Bin.\n\n\\noindent\\hrulefill\n\nYou can run through the following exercise to familiarize yourself with\nhow to create, edit, download, and upload files with Sync. First, open\nthe Sync folder in your file manager and create a new file called\n\\texttt{README.txt}. Enter the word \\texttt{test} in this file. Next,\nmake sure you can access this file in your Site. Go to the Site you want\nto sync with and navigate to its Documents and Media app. It lists your\n\\texttt{README.txt} file.\n\nDownload the \\texttt{README.txt} file to a convenient location on your\nmachine. Open the file and check that it still says \\texttt{test}. Now\nopen the \\texttt{README.txt} file in your Sync folder and edit it so\nthat it says \\texttt{second\\ test}. Once the changes are synced, go back\nto your browser and refresh the page with your Documents and Media app.\nClick on the \\texttt{README.txt} file's name, look at the file\ninformation displayed, and check that the file's version number has been\nincremented.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-file-edit-01.png}\n\\caption{Updating a file through Liferay Sync increments the file's\nversion number. You can view a file's version number through the web\ninterface.}\n\\end{figure}\n\nIf you download and open \\texttt{README.txt} again, it now says\n\\texttt{second\\ test}. Your edit was uploaded to the Site! You can be\nconfident that this edit was also downloaded by all other Sync clients\nconnected to your Site.\n\nNow delete the \\texttt{README.txt} file from your local Sync folder.\nWhen the changes finish syncing, go back to your browser and refresh the\npage containing your Documents and Media app. The file is gone! The file\nis also deleted from the local Sync folders of all other Sync clients\nconnected to the Site. Remember this very important rule: deleting files\nin your local Sync folder deletes them \\emph{everywhere}! Next, you'll\nlearn how to use the Sync client for your mobile device.\n\n\\chapter{Using Liferay Sync on Your Mobile\nDevice}\\label{using-liferay-sync-on-your-mobile-device}\n\nLiferay Sync for Android and iOS contains most of the\n\\href{/docs/7-2/user/-/knowledge_base/u/using-liferay-sync-on-your-desktop}{desktop\nSync client}'s functionality. The mobile client can, however, only be\nconnected to one Liferay DXP account at a time. Also, mobile Sync\ndoesn't automatically download files to your device. To save storage\nspace on your device, the Sync mobile app lets you choose the files you\nwant to work with. As with the Sync desktop clients, the latest versions\nof Sync on Android and iOS provide a consistent user experience across\nplatforms. While this article details using Sync on Android, the\ninstructions also apply to Sync on iOS.\n\nYou need to download and install Sync on your Android or iOS device\nthrough its respective app store, the same as you do any other mobile\napp. To find the app, search Google Play or the App Store for\n\\emph{Liferay}. You can also download Sync from the\n\\href{https://customer.liferay.com/downloads}{Liferay customer downloads\npage}. Once you've installed the Sync app on your device, the rest of\nthe articles in this guide show you how to use it.\n\n\\chapter{Connecting Liferay Sync\nMobile}\\label{connecting-liferay-sync-mobile}\n\nWhen Liferay Sync first starts on your mobile device, press the\n\\emph{Get Started} button to begin setup. The setup screen asks for your\nlogin credentials and your server's address. Once you enter them, press\n\\emph{Sign In}. After signing in, you see a panel that shows your name,\na gear icon for accessing the app's settings, and navigation options\n\\emph{My Sites} and \\emph{My Documents}. My Sites and My Documents\nencompass the Sites in Liferay DXP that you can sync with. My Documents\nis your personal user Site, while My Sites shows the other Sites with\nwhich you can sync. No matter how deep you are in the folder hierarchy\nof a Site, swiping to the right returns you to this panel. If you're in\nthe first level of My Sites or My Documents, pressing the location bar\nat the top slides the screen slightly to the right to reveal a compact\nview of the panel. The following screenshots show both views of the\npanel.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-mobile-panel.png}\n\\caption{This panel lets you access the app's settings, as well as your\nSites and documents.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-mobile-panel-compact.png}\n\\caption{Tapping the title bar at the top of My Sites or My Documents\nopens the main Sync panel's compact view.}\n\\end{figure}\n\nPress the gear icon to access Sync's settings. Settings shows your\naccount information and an option to sign out of your Liferay DXP\ninstance. Settings also lets you toggle \\emph{Security Mode}. Security\nMode protects files stored on your device by encrypting them. Using\nSecurity Mode requires you to set up a passcode to use when accessing\nthe Sync app. Security Mode protects the files on your device and\nLiferay DXP instance in the event your device is lost or stolen. You\nshould note, however, downloading and opening files in Security Mode\ntakes slightly longer than usual because the Liferay DXP server must use\nSSL---if it didn't, your files would be transmitting in the open. Below\nthe Security Mode toggle are the app's version and a link to send app\nfeedback to Liferay.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-mobile-settings.png}\n\\caption{The Settings screen for the Sync app lets you sign out of your\nLiferay DXP instance, enable Security Mode, view the app's version, and\nsend feedback.}\n\\end{figure}\n\n\\chapter{Managing Files and Folders in Liferay Sync\nMobile}\\label{managing-files-and-folders-in-liferay-sync-mobile}\n\nWhether you're working in My Documents or My Sites, you manage files and\nfolders the same way. Pressing a Site or folder shows you a list of its\nfiles and folders. It displays each file's size and modification date.\nYou can refresh the list by pulling down from the top of the screen.\nYour current location in the navigation hierarchy also appears at the\ntop of the screen alongside a plus icon. Pressing the plus icon launches\nan upload screen for adding content in the current location. You can add\na new folder, upload a file, or launch your device's camera app to take\nand upload a picture or video. Pressing the \\emph{X} icon on the upload\nscreen's top right corner cancels any action and returns you to the\ncurrent file list.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-mobile-site.png}\n\\caption{Sync shows files and folders in a list.}\n\\end{figure}\n\nTo download a file to your device, press the file's name in the list.\nThe label that previously showed the file's size and modification date\nis replaced by a download progress indicator. When the file finishes\ndownloading, your device automatically opens it in the app you've\nconfigured to open files of that type. If you haven't configured your\ndevice to use a specific app for that file type, you're presented with a\nlist of apps on your device that can open the file. If your device\ndoesn't have an app that can open the file, Sync tells you to install\none that can. Downloaded files appear in the list with the file size in\nblue instead of gray. For example, the screenshot below shows that\n\\texttt{LiferayinAction.pdf} is on the device.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-mobile-file-downloaded.png}\n\\caption{Downloaded files appear in the list with their size in blue.}\n\\end{figure}\n\nThe Sync mobile app also lets you move, rename, and delete files and\nfolders. To the right of each file and folder in the list is a circle\nicon with three dots. Pressing this icon slides open a context menu on\nthe right that lets you move, rename, or delete that item. The\nscreenshots below show these options. Note that you should use\n\\textbf{extreme caution} when deleting files or folders. Deleting files\nor folders in the mobile Sync app also deletes them from Liferay DXP and\nacross any synced clients. Accidentally deleted files can be restored\nfrom the Recycle Bin, which is enabled by default. If the instance or\nSite administrator disables the Recycle Bin, however, recovering deleted\nfiles is impossible.\n\nWhat if you want to delete a file on your device without also deleting\nit in the instance? Currently, you can only do this by signing out of\nyour account in the app's Settings menu. Doing so removes all downloaded\nfiles from your device, but preserves them in the instance. If you're on\nAndroid, it may be possible to use a system file browser app to remove\ndownloaded files manually.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Deleting a file in the mobile Sync app deletes it in\nLiferay DXP and across any synced clients. If you accidentally delete a\nfile, the instance or Site administrator can restore it from the\ninstance's Recycle Bin. The Recycle Bin is enabled by default. If the\ninstance or Site administrator disables the Recycle Bin, however,\nrecovering deleted files is impossible.\n\n\\noindent\\hrulefill\n\nThe context menu also provides additional options for files. A small\nbadge on the file icon's top-right corner indicates the file's version\nin the Liferay DXP instance. You can also use the context menu to share\nfiles you've downloaded. Pressing the \\emph{Share} icon opens a list of\nyour device's apps capable of sharing the file. To close the context\nmenu and return to the list of files and folders, swipe to the right.\nThe following screenshot shows the options available in a file's context\nmenu.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sync-mobile-file-actions.png}\n\\caption{The badge on the file's icon shows the file's version in the\nLiferay DXP instance. You can also share files that you've downloaded.}\n\\end{figure}\n\n\\chapter{Adapting Your Media Across Multiple\nDevices}\\label{adapting-your-media-across-multiple-devices}\n\nMedia providers must consider differences between devices (phones,\nlaptops, tablets, etc.) when delivering content: not only their screen\nsize but also their bandwidth and processing capabilities. Liferay DXP's\nAdaptive Media app allows administrators to control image quality and\ndynamically adjusts uploaded media to best fit the screen being used.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} At this time, Adaptive Media only works for images in\nblog entries and web content articles.\n\n\\noindent\\hrulefill\n\nAdaptive Media integrates with Documents and Media, Blogs, and Web\nContent. It generates a set of images for use on various screens. When\nthe content is accessed, Adaptive Media checks the screen type and\nresolution and selects appropriate the appropriate image. Adaptive Media\ncomes preinstalled in Liferay DXP.\n\nIn this section, you'll learn how to manage and use Adaptive Media.\n\n\\chapter{Installing Adaptive Media}\\label{installing-adaptive-media}\n\nThe Adaptive Media app is installed in Liferay DXP by default. The\nfollowing sections describe the Adaptive Media app's modules, and how to\nprepare Adaptive Media to handle animated GIFs.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Since the Adaptive Media app is installed by default,\nit's updated via Liferay DXP Fix Packs and Liferay Portal CE GA\nreleases. Using \\href{https://web.liferay.com/marketplace}{Liferay\nMarketplace} to update the app causes an error.\n\n\\noindent\\hrulefill\n\n\\section{Adaptive Media's Modules}\\label{adaptive-medias-modules}\n\nSome modules in the Adaptive Media app are mandatory and must be enabled\nfor Adaptive Media to function, while others can be disabled. The\nAdaptive Media API modules, which export packages for the other modules\nto consume, are mandatory; disabling one also disables any other modules\nthat depend on it. Here's a list of the Adaptive Media API modules:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay Adaptive Media API\n\\item\n  Liferay Adaptive Media Content Transformer API\n\\item\n  Liferay Adaptive Media Image API\n\\item\n  Liferay Adaptive Media Image Item Selector API\n\\end{itemize}\n\nThe Adaptive Media core modules are also mandatory, and must be enabled\nto ensure that Adaptive Media works as expected:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay Adaptive Media Document Library\n\\item\n  Liferay Adaptive Media Document Library Item Selector Web\n\\item\n  Liferay Adaptive Media Document Library Web\n\\item\n  Liferay Adaptive Media Image Content Transformer\n\\item\n  Liferay Adaptive Media Image Implementation\n\\item\n  Liferay Adaptive Media Image Item Selector Implementation\n\\item\n  Liferay Adaptive Media Image JS Web\n\\item\n  Liferay Adaptive Media Image Service\n\\item\n  Liferay Adaptive Media Image Taglib\n\\item\n  Liferay Adaptive Media Image Web\n\\item\n  Liferay Adaptive Media Item Selector Upload Web\n\\item\n  Liferay Adaptive Media Web\n\\end{itemize}\n\nThe Adaptive Media Blogs modules, which ensure that images uploaded to\nblog entries can be processed and adapted, are optional. Here's a list\nof these modules:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay Adaptive Media Blogs Editor Configuration\n\\item\n  Liferay Adaptive Media Blogs Item Selector Web\n\\item\n  Liferay Adaptive Media Blogs Web\n\\item\n  Liferay Adaptive Media Blogs Web Fragment\n\\end{itemize}\n\nThe Adaptive Media Journal modules are optional. These modules apply\nAdaptive Media to web content articles:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Liferay Adaptive Media Journal Editor Configuration\n\\item\n  Liferay Adaptive Media Journal Web\n\\end{itemize}\n\nThere are two more optional modules included in Adaptive Media:\n\n\\textbf{Liferay Adaptive Media Image Content Transformer Backwards\nCompatibility:} Ensures that content created before the Adaptive Media\ninstallation can use adapted images without the need to edit that\ncontent manually. It transforms the images both at startup and when a\nuser views the content, which can negatively affect performance. We\ntherefore recommend that you run some performance tests before using\nthis module in production. You can disable this module if you don't have\nold content, are experiencing performance problems, or your old content\ndoesn't need adapted images.\n\n\\textbf{Liferay Adaptive Media Document Library Thumbnails:} Lets\nthumbnails in Documents and Media use adapted images. For this to work,\nyou must first\n\\href{/docs/7-2/user/-/knowledge_base/u/migrating-documents-and-media-thumbnails-to-adaptive-media}{migrate\nthe original thumbnails to adapted images}. We highly recommend that you\nenable this module, but it's not mandatory.\n\nGreat! Now you know the mandatory and optional modules that come with\nAdaptive Media. The next section discusses the installation requirements\nfor using animated GIFs with Adaptive Media. If you don't need to use\nGIFs, you can skip ahead to the article on adding image resolutions to\nAdaptive Media.\n\n\\section{Processing Animated GIFs}\\label{processing-animated-gifs}\n\nTo process animated GIFs, Adaptive Media uses an external tool called\n\\href{https://www.lcdf.org/gifsicle}{Gifsicle}. This tool ensures that\nthe animation works when the GIF is scaled to different resolutions. You\nmust manually install Gifsicle on the server and ensure that it's on the\n\\texttt{PATH}. Once it's installed, you must enable it in Adaptive\nMedia's\n\\href{/docs/7-2/user/-/knowledge_base/u/advanced-configuration-options}{advanced\nconfiguration options}.\n\nIf Gifsicle isn't installed and \\texttt{image/gif} is included as a\nsupported MIME type in the advanced configuration options, Adaptive\nMedia scales only a GIF's single frame. This results in a static image\nin place of the animated GIF.\n\n\\chapter{Adding Image Resolutions}\\label{adding-image-resolutions}\n\nTo use Adaptive Media, you must first define the resolutions for the\nimages delivered to users' devices. Adaptive Media then generates new\nimages scaled to fit those resolutions, while maintaining the original\naspect ratio.\n\nTo access Adaptive Media settings, open the Control Panel and go to\n\\emph{Configuration} → \\emph{Adaptive Media}. Here you can create and\nmanage resolutions.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Adaptive Media configurations apply only to the current\nLiferay DXP instance.\n\n\\noindent\\hrulefill\n\nOnce you create a resolution, Adaptive Media automatically generates\ncopies of newly uploaded images in that resolution. Images uploaded\nbefore you create the resolution aren't affected and must be adapted\nseparately (see\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-image-resolutions\\#generating-missing-adapted-images}{Generating\nMissing Adapted Images}).\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adaptive-media-image-resolutions.png}\n\\caption{Adaptive Media's image resolutions are listed in a table.}\n\\end{figure}\n\n\\section{Adding a New Image\nResolution}\\label{adding-a-new-image-resolution}\n\nThe number of image resolutions required and the values for each depend\non the use case. More resolutions may optimize image delivery, but\ngenerating more images requires additional computational resources and\nstorage space. To start, we recommend that you create resolutions to\ncover common device sizes like mobile phones, tablets, laptops, and\ndesktops. If most users use one device (e.g., all Intranet users have\nthe same company mobile phone), you can create a resolution to target\nthat device.\n\nTo add a new resolution, click the \\emph{Add} icon\n(\\includegraphics{./images/icon-add.png}) on the Adaptive Media\nconfiguration page and provide the following information:\n\n\\textbf{Name}: The resolution's name (this must be unique). This can be\nupdated if a custom \\texttt{Identifier} is defined.\n\n\\textbf{Max Width}: The generated image's maximum width. If a \\emph{Max\nHeight} is given, this field is optional. This value must be at least\n\\texttt{1}.\n\n\\textbf{Maximum Height}: The generated image's maximum height. If a\n\\emph{Max Width} is given, this field is optional. This value must be at\nleast \\texttt{1}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Adaptive Media generates images that fit the Max Width\nand Max Height, while retaining the original aspect ratio. If you only\nprovide one value (either Max Width or Max Height), the generated image\nscales proportionally to fit within the specified dimension, while\nmaintaining its original aspect ratio. This ensures that adapted images\nare not distorted.\n\n\\noindent\\hrulefill\n\n\\textbf{Add a resolution for high density displays (2x):} Defines a\nscaled up resolution for HIDPI displays. Selecting this option creates a\nnew resolution double the size of the original with the same name and\nthe suffix \\texttt{-2x}. For example, if the original resolution is\n\\texttt{400px} by \\texttt{300px} (max width by max height), the high\ndensity resolution is \\texttt{800px} by \\texttt{600px}.\n\n\\textbf{Identifier:} The resolution's ID. By default, this is\nautomatically generated from the name. You can specify a custom\nidentifier by selecting the \\emph{Custom} option and entering a new\n\\emph{ID}. Third party applications can use this ID to obtain images for\nthe resolution via Adaptive Media's APIs.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Image resolutions and their identifiers can't be updated\nif the resolution has been used to adapt images. This prevents\ninconsistencies in generated images.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adaptive-media-new-img-resolution.png}\n\\caption{The form for adding a new Adaptive Media resolution.}\n\\end{figure}\n\n\\chapter{Managing Image Resolutions}\\label{managing-image-resolutions}\n\nAdaptive Media lets you manage image resolutions and their resulting\nadapted images. For example, you can disable, enable, edit, and delete\nresolutions. You can also generate any adapted images that may be\nmissing for a resolution. This article discusses these topics and more.\n\n\\section{Disabling Image Resolutions}\\label{disabling-image-resolutions}\n\nDisabling an image resolution prevents it from generating adapted\nimages. Any images uploaded after the resolution is disabled use the\nmost appropriate resolution that's still active. Adapted images\npreviously generated by the disabled resolution are still available.\n\nTo disable an image resolution, click its \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}) and select \\emph{Disable}.\n\n\\section{Enabling Image Resolutions}\\label{enabling-image-resolutions}\n\nImage resolutions are enabled by default. If you need to enable a\ndisabled resolution, click that resolution's \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}) and select \\emph{Enable}.\n\nWhile a resolution is disabled, it doesn't generate adapted images for\nnew image uploads. After enabling a resolution, you should generate the\nadapted images that weren't generated while it was disabled (see\n\\hyperref[generating-missing-adapted-images]{Generating Missing Adapted\nImages} for instructions on this).\n\n\\section{Editing Image Resolutions}\\label{editing-image-resolutions}\n\nYou can't edit an image resolution that already has adapted images. This\nprevents odd behavior (of the adapted images---you're still free to be\nas odd as you want). This is because any changes would only be applied\nto images uploaded after the edit, creating an inconsistent set of\nadapted images. Odd indeed.\n\nTherefore, editing an image resolution is only possible if Adaptive\nMedia hasn't yet generated adapted images for it. If you must change the\nvalues of a resolution that already has adapted images, you must delete\nthat resolution and create a new one with the new values. The next\nsection discusses deleting resolutions.\n\n\\section{Deleting Image Resolutions}\\label{deleting-image-resolutions}\n\nBe careful when deleting an image resolution, as any adapted images it\ncreated are irretrievably lost and are not automatically replaced by new\nimage resolutions you create.\n\nFollow these steps to delete an image resolution:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Disable the resolution. You can't delete enabled resolutions. This\n  prevents the accidental deletion of image resolutions.\n\\item\n  To delete the resolution and all its adapted images, select\n  \\emph{Delete} from the resolution's Actions menu\n  (\\includegraphics{./images/icon-actions.png}).\n\\end{enumerate}\n\n\\section{Generating Missing Adapted\nImages}\\label{generating-missing-adapted-images}\n\nIf Adaptive Media hasn't generated all the images you need---say, if new\nimages were uploaded before a new image resolution was created or while\nthe resolution was disabled--you must generate the missing images\nmanually.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adaptive-media-coverage.png}\n\\caption{The \\emph{Adapted Images} column shows the percentage of images\nthat are adapted for each resolution.}\n\\end{figure}\n\nTo manually generate missing adapted images,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  For a single resolution, select \\emph{Adapt Remaining} from the\n  resolution's Actions menu\n  (\\includegraphics{./images/icon-actions.png}).\n\\item\n  For all resolutions at once, select \\emph{Adapt All Images} from the\n  Actions menu in the Control Menu at the top of the page.\n\\end{enumerate}\n\n\\section{The Recycle Bin and Adapted\nImages}\\label{the-recycle-bin-and-adapted-images}\n\nYou can't move adapted images directly to the Recycle Bin. But if the\noriginal image is in the Recycle Bin, the corresponding adapted images\nbehave as if they are in the Recycle Bin and users can't view them.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} URLs that point to adapted images whose original image is\nin the Recycle Bin return an error code of \\texttt{404\\ Not\\ Found}.\n\n\\noindent\\hrulefill\n\nIf the original image is restored from the Recycle Bin, the adapted\nimages are accessible again.\n\nAwesome! Now you know how to manage image resolutions in Adaptive Media.\nNext, you'll learn about creating content with adapted images.\n\n\\chapter{Creating Content with Adapted\nImages}\\label{creating-content-with-adapted-images}\n\nAdaptive Media is mostly invisible for blog and web content creators.\nOnce an image is added to the content, the app works behind the scenes\nto deliver an adapted image appropriate to the device in use. Content\ncreators select an image when adding it to their content---they don't\nhave to (and can't) select an adapted image. Adaptive Media identifies\neach adapted image in the content's HTML with a\n\\texttt{data-fileentryid} attribute that is replaced with the latest\nadapted image when the user views the content. This lets Adaptive Media\ndeliver the latest adapted images to your content, even if the content\nexisted prior to those images.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If Adaptive Media is uninstalled, the original images are\ndisplayed in the blog entries and web content articles.\n\n\\noindent\\hrulefill\n\n\\section{Including Adapted Images in\nContent}\\label{including-adapted-images-in-content}\n\nSince Adaptive Media delivers the adapted images behind the scenes,\ncontent creators should add images to\n\\href{/docs/7-2/user/-/knowledge_base/u/publishing-blogs}{blog entries}\nand \\href{/docs/7-2/user/-/knowledge_base/u/creating-web-content}{web\ncontent} as usual: by clicking the image button in the editor and then\nselecting the image in the file selector.\n\nHowever, there are some important caveats. When using the file selector\nto include an image for a blog entry, Adaptive Media works only with\nimages added from the \\emph{Blog Images}, \\emph{Documents and Media},\nand \\emph{Upload} tabs. Additionally, adapted images can only be applied\nto a blog entry's content--cover images excluded. Adaptive Media works\nfor images added to a blog entry via drag and drop, as the image is\nautomatically uploaded to the Blog Images repository, adapted, and then\nincluded in the blog entry's content. You can see this by inspecting the\nHTML and checking that the image contains the\n\\texttt{\\textless{}img\\textgreater{}} tag and \\texttt{data-fileentryid}\nattribute.\n\nFor web content articles, Adaptive Media works only with images added\nfrom the file selector's \\emph{Documents and Media} tab. Unlike blogs,\nAdaptive Media doesn't deliver adapted images for images added to web\ncontent articles via drag and drop.\n\nFor both blog entries and media content articles, Adaptive Media doesn't\nwork with images added from the file selector's \\emph{URL} tab. This is\nbecause the image is linked directly from the URL and therefore provides\nno image file for Adaptive Media to copy.\n\nNote that you can see the \\texttt{\\textless{}img\\textgreater{}} tag and\n\\texttt{data-fileentryid} attribute in the HTML of a blog entry or a web\ncontent article while you're writing it. When the content is displayed,\nthe HTML is automatically replaced and looks similar to this:\n\n\\begin{verbatim}\n<picture>\n\n    <source media=\"(max-width:850px)\" \n    srcset=\"/o/adaptive-media/image/44147/med/photo.jpeg\">\n\n    <source media=\"(max-width:1200px) and (min-width:850px)\" \n    srcset=\"/o/adaptive-media/image/44147/hd/photo.jpeg\">\n\n    <source media=\"(max-width:2000px) and (min-width:1200px)\" \n    srcset=\"/o/adaptive-media/image/44147/ultra-hd/photo.jpeg\">\n\n    <img src=\"/documents/20140/0/photo.jpeg/1992-9143-85d2-f72ec1ff77a0\">\n\n</picture>\n\\end{verbatim}\n\nThis example uses three different images, each with a different\nresolution. A \\texttt{source} tag defines each of these images. Also\nnote the original image (\\texttt{img}) is used as a fallback in case the\nadapted images aren't available.\n\n\\section{Using Adapted Images in Structured Web\nContent}\\label{using-adapted-images-in-structured-web-content}\n\nTo use adapted images in\n\\href{/docs/7-2/user/-/knowledge_base/u/designing-uniform-content}{structured\nweb content}, content creators must manually include an image field in\nthe web content's structure. Then they can reference that image field in\nthe matching template by selecting it on the left side of the editor.\nHere's an example snippet of an image field named \\texttt{Imagecrrf}\nincluded in a template:\n\n\\begin{verbatim}\n<#if Imagecrrf.getData()?? && Imagecrrf.getData() !=\"\">\n  <img data-fileentryid=\"${Imagecrrf.getAttribute(\"fileEntryId\")}\" \n  alt=\"${Imagecrrf.getAttribute(\"alt\")}\" src=\"${Imagecrrf.getData()}\" />\n</#if>\n\\end{verbatim}\n\nThis snippet includes the \\texttt{data-fileentryid} attribute to ensure\nthat Adaptive Media replaces the image with an adapted image. If you\ninspect the resulting web content's HTML in the editor's code view, you\nshould see a tag like this:\n\n\\begin{verbatim}\n<img data-fileentryid=\"37308\" \nsrc=\"/documents/20143/0/photo.jpeg/85140258-1c9d-89b8-4e45-d79d5e262318?t=1518425\" />\n\\end{verbatim}\n\nNote the \\texttt{\\textless{}img\\textgreater{}} tag with a\n\\texttt{data-fileentryid} attribute. Adaptive Media uses the file entry\nID to replace the \\texttt{\\textless{}img\\textgreater{}} element\nautomatically with a \\texttt{\\textless{}picture\\textgreater{}} element\nthat contains the available adapted images for each resolution (see the\n\\texttt{\\textless{}picture\\textgreater{}} example above).\n\n\\section{Staging Adapted Images}\\label{staging-adapted-images}\n\nAdaptive Media is fully integrated with Liferay DXP's\n\\href{/docs/7-2/user/-/knowledge_base/u/staging}{content staging} and\n\\href{/docs/7-2/user/-/knowledge_base/u/exporting-importing-widget-data}{export/import}\nfunctionality. Adaptive Media includes adapted images in staged content\nwhen published, and can update those images to match any new\nresolutions.\n\nSimilarly, when content that contains adapted images is exported,\nAdaptive Media exports those images in the LAR file. That LAR file can\nthen be imported to restore or transfer that content, along with its\nadapted images.\n\nAdaptive Media doesn't regenerate adapted images during export/import or\nthe publication of staged content. To improve performance, Adaptive\nMedia instead reuses the existing adapted images.\n\nAwesome! Now you know how create content that contains adapted images.\nYou also know how Adaptive Media includes adapted images in the\ncontent's HTML.\n\n\\chapter{Migrating Documents and Media Thumbnails to Adaptive\nMedia}\\label{migrating-documents-and-media-thumbnails-to-adaptive-media}\n\nLiferay DXP automatically generates thumbnails for images in Documents\nand Media. Once you deploy the Adaptive Media app, however, Liferay DXP\ndoesn't display thumbnails until you migrate them to Adaptive Media.\nThis article walks you through this migration process.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You must be a Portal Administrator to perform the actions\ndescribed here.\n\n\\noindent\\hrulefill\n\nYou'll get started by creating image resolutions for the thumbnails in\nAdaptive Media.\n\n\\section{Adding the Replacement Image\nResolutions}\\label{adding-the-replacement-image-resolutions}\n\nTo migrate the existing Documents and Media thumbnails, you must add new\nimage resolutions in Adaptive Media that have maximum height and maximum\nwidth values that match the values specified in the following portal\nproperties:\n\n\\begin{verbatim}\ndl.file.entry.thumbnail.max.height\n\ndl.file.entry.thumbnail.max.width\n\ndl.file.entry.thumbnail.custom1.max.height\n\ndl.file.entry.thumbnail.custom1.max.width\n\ndl.file.entry.thumbnail.custom2.max.height\n\ndl.file.entry.thumbnail.custom2.max.width\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Some of these properties may not be enabled. You need\nonly create image resolutions in Adaptive Media for the enabled\nproperties.\n\n\\noindent\\hrulefill\n\nTo create the new Image Resolutions, follow the instructions found in\nthe\n\\href{/docs/7-2/user/-/knowledge_base/u/adding-image-resolutions}{Adding\nImage Resolutions} section of the Adaptive Media user guide.\n\nNow you're ready to to create the Adaptive Media images.\n\n\\section{Creating the Adaptive Media\nImages}\\label{creating-the-adaptive-media-images}\n\nOnce the required image resolutions exist, you can convert the Documents\nand Media thumbnails to Adaptive Media images. As mentioned in\n\\href{/docs/7-2/user/-/knowledge_base/u/installing-adaptive-media}{the\nAdaptive Media installation guide}, the module \\emph{Liferay Adaptive\nMedia Document Library Thumbnails} (which is included in the Adaptive\nMedia app) enables this functionality.\n\nThere are two different ways to migrate the Documents and Media\nthumbnails to Adaptive Media:\n\n\\textbf{Adapt the images for the thumbnail image resolution:} This\nscales the existing thumbnails to the values in the Adaptive Media image\nresolutions, which can take time depending on the number of images. We\nonly recommend this approach when there isn't a large number of\nthumbnails to process, or if you prefer to generate your images from\nscratch. This approach is covered in more detail in\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-image-resolutions\\#generating-missing-adapted-images}{Generating\nMissing Adapted Images}.\n\n\\textbf{Execute a migrate process that reuses the existing thumbnails:}\nThis copies the existing thumbnails to Adaptive Media, which performs\nbetter because it avoids the computationally expensive scaling\noperation. The next section describes the steps to run this process.\n\n\\section{Running the Migration\nProcess}\\label{running-the-migration-process}\n\nThe migration process is a set of Gogo console commands. You can learn\nmore about using the Gogo console in\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{the\nFelix Gogo Shell article}.\n\nFollow these steps to migrate your thumbnails from the Gogo console:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Run the \\texttt{thumbnails:check} command. For each instance, this\n  lists how many thumbnails are pending migration.\n\\item\n  Run the \\texttt{thumbnails:migrate} command. This executes the\n  migration process, which may take a while to finish depending on the\n  number of images.\n\\item\n  Run the \\texttt{thumbnails:cleanUp} command. This deletes all the\n  original Documents and Media thumbnails and updates the count returned\n  by \\texttt{thumbnails:check}. Therefore, you should \\textbf{only} run\n  \\texttt{thumbnails:cleanUp} after running the migrate command and\n  ensuring that the migration ran successfully and no images are pending\n  migration.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you undeploy Adaptive Media at some point after\nrunning the migration process, you must regenerate the Documents and\nMedia thumbnails. To do this, navigate to \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{Server Administration} and click\n\\emph{Execute} next to \\emph{Reset preview and thumbnail files for\nDocuments and Media}.\n\n\\noindent\\hrulefill\n\nGreat! Now you know how to migrate your Documents and Media thumbnails\nto adapted images.\n\n\\chapter{Advanced Configuration\nOptions}\\label{advanced-configuration-options}\n\nAdaptive Media's advanced configuration options are available in System\nSettings. Open the Control Panel and go to \\emph{Configuration} →\n\\emph{System Settings}, then select \\emph{Adaptive Media}. There are two\nconfigurations listed under \\emph{SYSTEM SCOPE}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Images\n\\item\n  Processes\n\\end{itemize}\n\nThe \\emph{Images} configuration contains the following options:\n\n\\textbf{Supported MIME Types:} A list of the image MIME types that\nAdaptive Media supports. If an image is uploaded and its MIME type isn't\nin this list, Adaptive Media ignores the image. By default, this list\ncontains many common MIME types.\n\n\\textbf{Gifsicle:} To scale animated GIFs, Adaptive Media uses an\nexternal tool called \\href{https://www.lcdf.org/gifsicle/}{Gifsicle}.\nFirst install Gifsicle on the server, ensure that it's on the\n\\texttt{PATH} environment variable, and then click the box next to\n\\emph{Gifsicle Enabled}. If Gifsicle isn't installed and\n\\texttt{image/gif} is included as a supported MIME type, Adaptive Media\nscales only one frame of the GIF, making a static GIF.\n\n\\textbf{Max Image Size:} Maximum size of the source images that Adaptive\nMedia can use to generate adapted images. Adaptive Media will not\ngenerate adapted images for source images larger than this setting. The\ndefault value is 10 MB. To generate adapted images for all source images\nregardless of size, set this to -1. Since generating adapted images from\nlarge source images requires significant amounts of memory, you can\nspecify a lower \\emph{Max Image Size} to avoid out of memory errors.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adaptive-media-config-01.png}\n\\caption{You can configure Gifsicle and the maximum image size for\nAdaptive Media.}\n\\end{figure}\n\nThe \\emph{Processes} configuration is related to Adaptive Media's\nasynchronous processing. These values can be modified to improve\nperformance for specific scenarios or use cases. The following options\nare available:\n\n\\textbf{Max Processes:} The maximum number of processes for generating\nadapted media. The default value is \\texttt{5}.\n\n\\textbf{Core Processes:} The number of processes always available for\ngenerating adapted media. The default value is \\texttt{2}. This setting\ncan't exceed the \\emph{Max processes} setting.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Larger values for Max Processes and Core Processes may\ncause out of memory errors, as processing more images at once can\nconsume large amounts of memory. Out of memory errors can also occur if\nthe source images Adaptive Media uses to generate adapted images are\nlarge. You can restrict the maximum size of such images via the\n\\emph{Max Image Size} setting in the \\emph{Adaptive Media Image}\nconfiguration, which is described next. You should run performance tests\nto optimize these settings for the amount of memory available on your\nsystem.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/adaptive-media-config-02.png}\n\\caption{You can also configure Adaptive Media's image processing\nresources.}\n\\end{figure}\n\n\\chapter{Collaborating}\\label{collaborating}\n\nYour users can leverage a robust suite of collaboration apps to get\nthings done and form extensive communities. These apps provide all the\nfeatures that you would expect of standalone apps, but these apps are\nintegrated: they share a common look and feel, security model, and\narchitecture because of Liferay's development framework. You can use\nthem in combination with user registration and content management\nfeatures to build a well-integrated, feature-rich site.\n\nThis guide shows you how to administer and use the collaboration apps:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Blogs\n\\item\n  Message Boards\n\\item\n  Wiki\n\\item\n  Alerts and Announcements\n\\item\n  Knowledge Base\n\\item\n  Bookmarks\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blog-entry-abstract.png}\n\\caption{This blog entry looks fascinating.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/message-boards-participate-in-threads.png}\n\\caption{This is a great thread.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-page-full.png}\n\\caption{The Wiki widget displays your wiki on a Site page.}\n\\end{figure}\n\n\\chapter{Publishing Blogs}\\label{publishing-blogs}\n\nThe Blogs app's editor has a complete set of WYSIWYG controls that\nappear when and where you need them. You can also switch to source mode\nto edit your content's HTML code. In source mode, you can work with\nlight text on a dark background or dark text on a light background. You\ncan even open a dual-screen HTML editor to see your code rendered in\nreal time.\n\nThe Blogs app also contains a powerful set of tools for customizing how\nyour blogs appear. For example, display templates like Abstract or Full\nContent let you choose how much of a blog post appears on a page. You\ncan leverage the built-in display templates or create your own. You can\nalso add a cover image to each of your blog entries. Let's face it---you\nmight not be able to judge a book by its cover, but a blog post with a\nnice cover image is more likely to draw readers.\n\nRead on to learn about Liferay's blogging platform.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blog-entry-abstract.png}\n\\caption{This blog entry looks fascinating.}\n\\end{figure}\n\n\\chapter{Adding Blog Entries}\\label{adding-blog-entries}\n\nEach site contains a built-in blog instance, so you can add blog entries\nto it right away. The easiest way to do this is in the Site\nAdministration menu. Follow these steps to add a blog entry in Site\nAdministration:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the Menu button (\\includegraphics{./images/icon-menu.png}) to\n  open the product menu. Then expand the menu for your site and select\n  \\emph{Content \\& Data} → \\emph{Blogs}. This takes you to the Blogs app\n  for your site. The \\emph{Entries} tab is selected by default, which\n  lists the site's blog entries.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  to create a new blog entry. This presents the blog entry editor. Note\n  that the same form appears when editing a blog entry.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/blogs-new-entry.png}\n  \\caption{This screenshot shows some of the blog entry editor's\n  controls.}\n  \\end{figure}\n\\item\n  The first input field, \\emph{Drag \\& Drop to Upload}, is for\n  optionally adding a cover image for your entry. By default, an\n  \\href{/docs/7-2/user/-/knowledge_base/u/publishing-assets}{Asset\n  Publisher} shows this cover image as part of the blog entry's\n  abstract. You can insert any image you like in this field, either via\n  drag and drop or the \\emph{Select File} button. The latter lets you\n  choose an existing image in the blog, an image from Documents and\n  Media, or an image that you upload from your machine.\n\n  If you select an image from Documents and Media, you can make changes\n  to it with the\n  \\href{/docs/7-2/user/-/knowledge_base/u/editing-images}{Image Editor}.\n  Edits you make are applied automatically to a copy of the image, which\n  you can then use as your cover photo.\n\n  Upon upload, the image appears in the pane. To center the image, drag\n  it into place. You can also add a caption. If you want to select a\n  different image, click the \\emph{Change} icon\n  (\\includegraphics{./images/icon-change.png}). Clicking the trash can\n  icon removes the image from the blog entry.\n\\item\n  Enter a title for your blog entry. You can also add a subtitle if\n  needed.\n\\item\n  Enter your blog entry's content in the \\emph{Content} field. This\n  field is small at first, but it expands as you add content. The editor\n  displays the editing controls when you need them and hides them from\n  view when you don't. When you select text in your blog post, for\n  example, a bar with context-specific editing controls appears. This\n  keeps your canvas uncluttered so you can focus on writing. You can\n  also add images, videos, and tables in your blog entry's content. See\n  \\href{/docs/7-2/user/-/knowledge_base/u/using-the-blog-entry-editor}{Using\n  the Blog Entry Editor} for instructions on creating your blog entry's\n  content.\n\\item\n  Expand the \\emph{Categorization} panel and associate your blog entry\n  with\n  \\href{/docs/7-2/user/-/knowledge_base/u/organizing-content-with-tags-and-categories}{tags\n  and/or categories}. Although this is optional, it improves search\n  results for blog entries and gives your users more navigation options.\n  For example, you can add the Tags Navigation app to another column on\n  your blogs page, which lets users browse blog entries by tag.\n\\item\n  Expand the \\emph{Related Assets} panel and choose any other content in\n  your site that you want to associate with this blog entry. Although\n  this is optional, related assets let you tie together content on your\n  site. For example, you might want to write a blog entry about a\n  discussion that happened on the forums. To link those two assets\n  together, select the forum thread under Related Assets. For more\n  information, see the\n  \\href{/docs/7-2/user/-/knowledge_base/u/defining-content-relationships}{related\n  assets documentation}.\n\\item\n  Expand the \\emph{Configuration} panel if you want to customize your\n  blog entry's URL, abstract, or display date. You can also set whether\n  to allow pingbacks for your blog entry. For the URL, the default\n  selection of \\emph{Automatic} generates the URL for you based on the\n  blog entry's title. This URL appears in the \\emph{Blog Entry URL} text\n  box. Selecting \\emph{Custom} lets you enter your own URL. Note that if\n  you change the blog entry's URL after publishing the entry, the\n  original URL redirects to the new URL.\n\n  You can also specify the blog entry's abstract. Enter a 400 character\n  text-only abstract, or a custom abstract that contains a thumbnail\n  image and a manually written description. The \\emph{Small Image}\n  section lets you add a small image that appears when blog entries are\n  displayed in list view. Below the abstract section, you can set the\n  entry's display date and time.\n\n  Note that if you're editing an existing blog entry, the \\emph{Send\n  Email Entry Updated} toggle appears. Setting this to \\emph{YES} sends\n  an email to any subscribers when the blog entry is updated. You can\n  customize this email when\n  \\href{/docs/7-2/user/-/knowledge_base/u/configuring-the-blogs-app}{configuring\n  the Blogs app}.\n\n  Finally, you can allow \\emph{pingbacks} for the blog entry. Pingbacks\n  are XML-RPC requests that are sent automatically when you link to\n  another site. If you link to another site in your blog entry, Liferay\n  DXP sends a pingback to the other site to notify that site that you\n  linked to it. Similarly, if someone links to your blog entry, Liferay\n  DXP can receive a pingback from that site and record the link.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/blog-entry-configuration.png}\n  \\caption{When creating a blog entry, the Configuration panel lets you\n  control when and where the blog entry appears, and what to use for the\n  entry's abstract.}\n  \\end{figure}\n\\item\n  Expand the \\emph{Display Page Template} panel if you want to select a\n  \\href{/docs/7-2/user/-/knowledge_base/u/display-pages-for-web-content}{display\n  page template} for displaying your blog entry. The following options\n  are available:\n\n  \\textbf{Default Display Page Template:} Use the default display page\n  template.\n\n  \\textbf{Specific Display Page Template:} Click the \\emph{Select}\n  button to select the display page template you want to use.\n\n  \\textbf{No Display Page Template:} Do not use a display page template.\n\\item\n  Expand the \\emph{Permissions} panel to customize your blog entry's\n  permissions. Use the \\emph{Viewable by} selector to set who can view\n  the blog entry:\n\n  \\textbf{Anyone (Guest Role):} Anyone, including guests, can view the\n  entry.\n\n  \\textbf{Site members:} Only site members can view the entry.\n\n  \\textbf{Owner:} Only the entry's owner can view the entry.\n\n  Click the \\emph{More Options} link to bring up a permissions table\n  that lets you grant or revoke the following permissions for guests and\n  site members:\n\n  \\textbf{Update Discussion}: Edit another user's comment on the blog\n  entry.\n\n  \\textbf{Delete}: Move the blog entry to the\n  \\href{/docs/7-2/user/-/knowledge_base/u/using-the-recycle-bin}{Recycle\n  Bin}.\n\n  \\textbf{Permissions}: View and modify the blog entry's permissions.\n\n  \\textbf{Delete Discussion}: Delete any comments on the blog entry.\n\n  \\textbf{Update}: Edit and modify the blog entry.\n\n  \\textbf{View}: View the blog entry.\n\n  \\textbf{Add Discussion}: Comment on the blog entry.\n\\item\n  Click \\emph{Publish} to publish your blog entry. It now appears in the\n  \\emph{Entries} tab.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/blog-entries-site-admin.png}\n  \\caption{The Blogs app in Site Administration lists the site's blog\n  entries.}\n  \\end{figure}\n\\end{enumerate}\n\n\\chapter{Using the Blog Entry Editor}\\label{using-the-blog-entry-editor}\n\nWhen you\n\\href{/docs/7-2/user/-/knowledge_base/u/adding-blog-entries}{create a\nnew blog entry}, you create its content with the blog entry editor. This\neditor is simple yet powerful. Its editing tools are context-aware, only\nappearing when you need them. They aren't visible while you're writing,\nwhich lets you focus on the task at hand. But when you select text, for\nexample, a toolbar appears that contains buttons for styling the text.\nThere are similar contextual options for adding and editing images,\ntables, and other types of content. And this is all in the editor's text\nview. You can switch to code view to edit the content's HTML. Regardless\nof which view you use, your entry is automatically saved as a draft\nevery 25 seconds, so a browser crash or network interruption won't cause\nyou to lose your work.\n\nThis guide shows you how to use this editor to create and edit blog\nentries.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blogs-edit-entry.png}\n\\caption{This screenshot shows some of the blog entry editor's\ncontrols.}\n\\end{figure}\n\n\\section{Using the Editor's Text\nView}\\label{using-the-editors-text-view}\n\nWhen you create or edit a blog entry, the editor is in text view by\ndefault. Text view is a WYSIWYG editor that lets you enter and edit text\nand other types of content. To enter text, place your cursor in any text\nfield (e.g., Title, Subtitle, Content, etc.) and type or paste your\ntext. Note that the Content area expands to fit its contents.\n\nTo style or format text in the Content area, first select the text. A\ntoolbar appears above the text that contains the following options, as\nshown in the above screenshot:\n\n\\textbf{Text Style:} This selector menu, set to Normal by default, lets\nyou choose the text's style. Normal is typical body text, but you can\nalso select from different heading styles, alert or error message\nstyles, code style, and more.\n\n\\textbf{Typeface:} Select bold, italic, or underline.\n\n\\textbf{List Style:} Select a numbered or bulleted list.\n\n\\textbf{Link:} Link the selected text to a specific URL, or to an item\nin the portal (e.g.~a file in Documents and Media).\n\n\\textbf{Twitter:} Generates a link to tweet the selected text.\n\nWhen you park your cursor in the entry's content area, the \\emph{Add}\nicon (\\texttt{+}) appears. If you click this icon, it shows controls for\ninserting an image, video, table, or horizontal line\n(\\includegraphics{./images/icon-content-insert-controls.png}). Follow\nthese instructions to insert each:\n\n\\textbf{Image:} Click the mountain icon, then select or upload an image\nin the image file selector screen that appears. Alternatively, you can\ndrag-and-drop image files into the content area. You can also use the\n\\href{/docs/7-2/user/-/knowledge_base/u/editing-images}{built-in Image\nEditor} to apply simple edits to an image. Any edits you make are\nautomatically applied to a copy of the image. After you add an image to\nthe blog entry, clicking the image brings up controls for justifying it\nto the right or left side of the article.\n\n\\textbf{Video:} Click the play icon and insert the video's link. The\nvideo then appears in your content.\n\n\\textbf{Table:} Click the table (grid) icon and then choose the number\nof rows and columns in your table. When you click inside the table,\ntable editing controls appear. They let you designate the first row\nand/or column as table headers. The controls also enable you to add\nrows, columns, and cells. As you type in a cell, the column width\nautomatically adjusts to fit the content.\n\n\\textbf{Line}: Click the line icon. A simple, lightweight horizontal\nline then appears in your content. Such lines are good for separating\nsections of content in a large blog entry.\n\n\\section{Using the Editor's Code\nView}\\label{using-the-editors-code-view}\n\nTo switch to code view, click the the \\emph{Source} icon\n(\\texttt{\\textless{}/\\textgreater{}}) that appears when you place your\ncursor in the Content area. The following buttons exist at the top-right\nof code view:\n\n\\textbf{Text View} (\\includegraphics{./images/icon-text.png}): Switch\nback to text view.\n\n\\textbf{Dark/Light Theme}\n(\\includegraphics{./images/icon-dark-theme.png} /\n\\includegraphics{./images/icon-light-theme.png}): Switch the code editor\nbetween dark and light theme.\n\n\\textbf{Fullscreen} (\\includegraphics{./images/icon-enlarge.png}): Work\nin a dual-pane view that shows your HTML code on the left and a preview\npane on the right. In this view, you can arrange the HTML and preview\npanes horizontally or vertically. You can also hide the preview pane so\nthe HTML editor takes up the entire window space.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blogs-code-view.png}\n\\caption{Editing in code view lets you work with your blog entry's\nunderlying HTML.}\n\\end{figure}\n\n\\chapter{Managing Blog Entries}\\label{managing-blog-entries}\n\nThe Blogs app in Site Administration helps bloggers and blog\nadministrators manage blog entries. To access this app, click the Menu\nbutton (\\includegraphics{./images/icon-menu.png}) to open the product\nmenu, then expand the menu for your site and select \\emph{Content \\&\nData} → \\emph{Blogs}. The \\emph{Entries} tab is selected by default,\nwhich lists the site's blog entries.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blog-entries-site-admin.png}\n\\caption{The Blogs app in Site Administration lists the site's blog\nentries.}\n\\end{figure}\n\nYou can use the Management Bar to manage your site's blog entries. If\nyou've added blog entries via your site's Blogs app, then you're already\nfamiliar with the Management Bar's \\emph{Add} button\n(\\includegraphics{./images/icon-add.png}).\n\nThe sections that follow describe how to manage your blog entries.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[view-types]{View Types}\n\\item\n  \\hyperref[finding-and-arranging-blog-entries]{Finding and Arranging\n  Blog Entries}\n\\item\n  \\hyperref[selecting-blog-entries]{Selecting Blog Entries}\n\\item\n  \\hyperref[sharing-blog-entries]{Sharing Blog Entries}\n\\end{itemize}\n\n\\section{View Types}\\label{view-types-1}\n\nThe \\emph{View Types} button is to the left of the Add button. It lets\nyou choose how to display the blog entries in the Blogs app. The View\nTypes button's icon depends on the selected view type:\n\n\\textbf{Cards} (\\includegraphics{./images/icon-view-type-cards.png}):\nShows a card-like rendering of the blog entry, with the author's profile\npicture. If the entry doesn't contain a cover image, a generic rendering\nof the entry is displayed. Each card also contains the entry's\ntimestamp, title,\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow}{workflow} status\n(e.g., Approved, Draft, etc.), and an Actions menu\n(\\includegraphics{./images/icon-actions.png}).\n\n\\textbf{List} (\\includegraphics{./images/icon-view-type-list.png}):\nShows the same information as the Cards view type, in a list with the\nauthor's profile picture instead of the blog entry's cover image.\n\n\\textbf{Table} (\\includegraphics{./images/icon-view-type-table.png}):\nShows the same information as the other view types, in a list with no\nfile renderings. Also, the blog entry information is in columns.\n\n\\section{Finding and Arranging Blog\nEntries}\\label{finding-and-arranging-blog-entries}\n\nThe Management Bar also contains tools that help you locate and arrange\nblog entries. The most prominent of these tools is the \\emph{Search}\nbar, where you can find files by keywords.\n\nTo the left of the Search bar, the Sort button\n(\\includegraphics{./images/icon-sort.png}) arranges entries in ascending\nor descending order.\n\nYou can also arrange entries via the \\emph{Filter and Order} selector\nusing these criteria:\n\n\\textbf{All:} Shows all of the site's entries.\n\n\\textbf{Mine:} Shows only the current user's entries.\n\n\\textbf{Display Date:} Orders the entries by display date.\n\n\\section{Selecting Blog Entries}\\label{selecting-blog-entries}\n\nThe checkbox on the left-most side of the Management Bar selects all\ncurrently displayed blog entries. Selecting multiple entries lets you\nact on all of them at once. You can also select multiple entries\nindividually by using the checkboxes for each. When you select one or\nmore entries, the Management Bar changes to reflect the actions you can\ntake on the selected entries.\n\nClick the \\emph{Trash} button\n(\\includegraphics{./images/icon-trash.png}) to move the selected entries\nto the Recycle Bin. Unselect the checkbox to return the Management Bar\nto its normal view.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blog-management-bar-selected.png}\n\\caption{With multiple blog entries selected, the management bar changes\nto reflect the actions you can take on the selected entries.}\n\\end{figure}\n\n\\section{Sharing Blog Entries}\\label{sharing-blog-entries}\n\nYou can also share blog entries from the Blogs app in Site\nAdministration. Sharing is enabled by default, as described in\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-sharing}{Configuring\nSharing}. Sharing blog entries works the same as\n\\href{/docs/7-2/user/-/knowledge_base/u/sharing-files}{sharing files}.\n\nWhen sharing is enabled and you have sharing permission, you can share a\nblog entry by clicking its \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}) and selecting\n\\emph{Share}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blog-share.png}\n\\caption{You can share a blog entry via its Actions menu.}\n\\end{figure}\n\n\\chapter{Configuring the Blogs App}\\label{configuring-the-blogs-app}\n\nBy configuring the Blogs app in Site Administration, you can control how\nthe app behaves for all blogs in your site. To access this app, click\nthe Menu button (\\includegraphics{./images/icon-menu.png}) to open the\nproduct menu, then expand the menu for your site and select\n\\emph{Content} → \\emph{Blogs}. The \\emph{Options} menu\n(\\includegraphics{./images/icon-options.png}) at the top-right of the\nBlogs app lets you configure permissions and notifications, or\nimport/export the app's content.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blog-instance-options.png}\n\\caption{You can configure the options for your site's Blogs app.}\n\\end{figure}\n\nHere are each of the options available in this menu:\n\n\\textbf{Entries Permissions:} Configure the permissions that can be\napplied to the Blogs app. You can control which roles can add an entry,\nconfigure entry permissions, and subscribe to entries.\n\n\\textbf{Export/Import:} Export or import a LAR file that contains the\nBlogs app's content.\n\n\\textbf{Configuration:} Configure the following options for the Blogs\napp, in these tabs:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Email From:} Define the \\emph{From} field in the email\n  messages that users receive from Blogs.\n\\item\n  \\textbf{Entry Added Email:} Define a subject and body for the emails\n  sent when a new blog entry has been added.\n\\item\n  \\textbf{Entry Updated Email:} Define a subject and body for the emails\n  sent when a new blog entry has been updated.\n\\item\n  \\textbf{RSS:} Lets you enable RSS subscription and choose how blogs\n  are displayed to RSS readers. The \\emph{Maximum Items to Display}\n  selector lets you choose the total number of RSS feed entries to\n  display on the initial page. You can choose up to one hundred to be\n  displayed. The \\emph{Display Style} selector lets you choose between\n  \\emph{Full Content}, \\emph{Abstract}, and \\emph{Title} for the entry\n  display in the RSS feed. Lastly, the \\emph{Format} selector lets you\n  choose which format the RSS feed uses to deliver the entries: Atom\n  1.0, RSS 1.0, or RSS 2.0.\n\\end{itemize}\n\n\\chapter{Displaying Blogs}\\label{displaying-blogs}\n\nThe Blogs app in Site Administration lets you\n\\href{/7-2/user/-/knowledge_base/u/adding-blog-entries}{add},\n\\href{/7-2/user/-/knowledge_base/u/managing-blog-entries}{manage}, and\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-the-blogs-app}{configure}\nyour site's blogs. You can then display those blogs by adding a separate\nBlogs widget to a page. Adding the Blogs widget to a site page creates a\nshared blog for site members. Adding the widget to a user's personal\nsite (dashboard) creates a blog just for that user. The widget works the\nsame way in both cases. And of course, you can scope a blog to a page to\nproduce a blog instance for just that page.\n\nTo add a Blogs widget to a page:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the page.\n\\item\n  From the \\emph{Add} menu\n  (\\includegraphics{./images/icon-add-app.png}), open \\emph{Widgets} →\n  \\emph{Collaboration}.\n\\item\n  Drag a \\emph{Blogs} widget onto the page.\n\\end{enumerate}\n\nBy default, the Blogs widget lists abstracts of the site's recent blog\nentries. The listing shows each entry's cover image prominently. Each\nabstract in the listing also shows the number of comments, thumbs\nup/down ratings, and links to share the entry on Twitter, Facebook,\nLinkedIn, and other social networking sites. Clicking a blog entry lets\nyou view its full content, where you can also comment on the entry.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blog-entry-abstract.png}\n\\caption{Fancy a lunar spelunking trip? This blog entry's abstract lets\nyou know what you're getting into.}\n\\end{figure}\n\nThere are several display options that let you configure the listing to\nlook the way you want. To configure the widget, click the \\emph{Options}\nicon (\\includegraphics{./images/icon-app-options.png}) in its title bar\nand select \\emph{Configuration}. The display settings are in the\n\\emph{Setup} tab:\n\n\\textbf{Enable Ratings:} Whether readers can rate blog entries.\n\n\\textbf{Enable Comments:} Whether readers can comment on blog entries.\n\n\\textbf{Show View Count:} Whether to show the number views for each\nentry.\n\n\\textbf{Social Bookmarks:} The social networking sites that users can\nshare blog entries with. Only those in the \\emph{Current} column are\ndisplayed via the share buttons on each blog entry. To move social\nnetworking sites between the \\emph{Current} and \\emph{Available}\ncolumns, select the sites and use the arrows between those columns.\nSimilarly, use the up/down arrows beneath the \\emph{Current} column to\nreorder the sites as they appear on each blog entry. For more\ninformation, see\n\\href{/docs/7-2/user/-/knowledge_base/u/using-social-bookmarks}{the\nsocial bookmarks documentation}.\n\n\\textbf{Display Style:} The display style for social bookmarks.\n\\emph{Inline} is the default and displays the social bookmark icons in a\nrow. \\emph{Menu} hides them inside a single share menu.\n\n\\textbf{Maximum Items to Display:} The total number of blog entries to\ndisplay on the initial page. You can select up to 75 to display at once.\n\n\\textbf{Display Template:} The overall appearance of blog entries in the\nlisting. \\emph{Abstract} is the default, and is shown in the above\nscreenshot. You can also choose the following:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Full Content:} Displays each blog entry's full content.\n\\item\n  \\textbf{Title:} Displays only the blog entry's title.\n\\item\n  \\textbf{Basic:} A stripped-down version of the Abstract, with less\n  text and no cover image.\n\\item\n  \\textbf{Card:} Displays each blog entry in a card-like rectangle that\n  shows the cover image, title, author, post date, and a few lines of\n  text.\n\\end{itemize}\n\nTo select a different widget template or create your own, click\n\\emph{Manage Templates}. For more information, see the documentation on\n\\href{/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates}{widget\ntemplates}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blogs-cards.png}\n\\caption{The Card display template makes your blog posts look like fun\nlittle trading cards.}\n\\end{figure}\n\n\\textbf{Enable Report Inappropriate Content:} Whether to let users flag\ncontent as inappropriate, which sends an email to administrators.\n\n\\textbf{Enable Ratings for Comments:} Whether to let readers rate blog\nentry comments.\n\n\\textbf{Show Related Assets:} Whether to display related content from\nother apps/widgets in blog entries.\n\nThere are also other tabs in \\emph{Configuration}:\n\n\\textbf{Communication:} Lists public render parameters the widget\npublishes to other widgets on the page. Other apps/widgets can read and\ntake actions on these. For each shared parameter, you can specify\nwhether to allow communication using the parameter and select which\nincoming parameter can populate it.\n\n\\textbf{Sharing:} Embed the widget instance as a widget on any website,\nFacebook, Netvibes, or as an OpenSocial Gadget.\n\n\\textbf{Scope:} Specify the blog instance the widget displays: the\ncurrent site's blog (default), the global blog, or the page's blog. If\nthe page doesn't already have a blog instance, you can select scope\noption \\emph{{[}Page Name{]} (Create New)} to create a page-scoped blog\ninstance.\n\nWhen you finish setting the options, click \\emph{Save} and then close\nthe dialog box.\n\n\\chapter{Aggregating Blogs}\\label{aggregating-blogs}\n\nYou can set up a whole web site devoted just to blogging if you wish.\nThe Blogs Aggregator lets you publish entries from multiple bloggers on\none page, giving further visibility to blog entries. You can add it to a\npage from the \\emph{Collaboration} category in the \\emph{Add}\n(\\includegraphics{./images/icon-add-app.png}) → \\emph{Widgets} menu.\n\nIf you click \\emph{Configuration} from the \\emph{Options} icon\n(\\includegraphics{./images/icon-app-options.png}) in the widget's title\nbar, the Blogs Aggregator's configuration page appears. The \\emph{Setup}\ntab contains these options:\n\n\\textbf{Selection Method:} Set how the widget selects blogs for display.\nYou can choose \\emph{Users} or \\emph{Scope}. If you select Users, the\nwidget aggregates the entries of every blogger on your system. To refine\nthe aggregation, you can select an organization by which to filter the\nusers. If you select Scope, the widget aggregates the entries of users\nin the current scope. This limits the entries to members of the site\nwhere the widget resides.\n\n\\textbf{Organization:} The organization whose blogs you want to\naggregate.\n\n\\textbf{Display Style:} Select the overall appearance for blog entries\nin the widget: \\emph{Body and Image}, \\emph{Body}, \\emph{Abstract}\n(default), \\emph{Abstract without Title}, \\emph{Quote}, \\emph{Quote\nwithout Title}, and \\emph{Title}.\n\n\\textbf{Maximum Items to Display:} The maximum number of entries the\nwidget displays.\n\n\\textbf{Enable RSS Subscription:} Whether to enable an RSS feed of the\naggregated entries. This lets users subscribe to an aggregate feed of\nall your bloggers. Below this option, you can configure how you want to\ndisplay the RSS feed:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Maximum Items to Display:} The maximum number of RSS items to\n  display.\n\\item\n  \\textbf{Display Style:} The overall appearance of each entry in the\n  RSS feed: \\emph{Abstract}, \\emph{Full Content}, or \\emph{Title}.\n\\item\n  \\textbf{Format:} The language to use for your RSS feed: \\emph{Atom\n  1.0}, \\emph{RSS 1.0}, or \\emph{RSS 2.0}.\n\\item\n  \\textbf{Show Tags:} Whether to display each entry's tags.\n\\end{itemize}\n\nHere are descriptions for the other tabs in the Blogs Aggregator's\nconfiguration:\n\n\\textbf{Sharing:} Embed the widget instance as a widget on any website,\nFacebook, Netvibes, or as an OpenSocial Gadget.\n\n\\textbf{Scope:} Specify the blog instance the widget displays: the\ncurrent site's blog (default), the global blog, or the page's blog. If\nthe page doesn't already have a blog instance, you can select scope\noption \\emph{{[}Page Name{]} (Create New)} to create a page-scoped blog\ninstance.\n\nWhen you finish setting the options, click \\emph{Save} and then close\nthe dialog box. You'll notice that the Blogs Aggregator looks very much\nlike the Blogs widget, except that it shows entries from multiple blogs.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blogs-aggregator.png}\n\\caption{The Blogs Aggregator lets you display blog entries authored by\nmultiple authors from different sites.}\n\\end{figure}\n\n\\chapter{Highlighting Recent\nBloggers}\\label{highlighting-recent-bloggers}\n\nThe Recent Bloggers widget lets you highlight the work of your site's\nmost recent blog authors. This widget lists each recent author's name,\nprofile picture, and number of posts. You can add the Recent Bloggers\nwidget to a page from the \\emph{Collaboration} category in the\n\\emph{Add} (\\includegraphics{./images/icon-add-app.png}) →\n\\emph{Widgets} menu.\n\nTo access the widget's configuration options, click \\emph{Configuration}\nfrom the \\emph{Options} menu\n(\\includegraphics{./images/icon-app-options.png}) in the widget's title\nbar. The \\emph{Setup} tab appears first:\n\n\\textbf{Selection Method:} Set how the widget selects blogs authors to\nhighlight. You can choose \\emph{Users} or \\emph{Scope}. If you select\nUsers, the widget aggregates every recent blogger on your system. To\nrefine the aggregation, you can select an organization by which to\nfilter the users. If you select Scope, the widget aggregates the recent\nbloggers in the current scope. This limits the entries to members of the\nsite where the widget resides.\n\n\\textbf{Organization:} The organization whose recent bloggers you want\nto aggregate.\n\n\\textbf{Display Style:} Select how the widget displays recent bloggers:\n\\emph{User Name and Image}, or \\emph{User Name}.\n\n\\textbf{Maximum Bloggers to Display:} Select the maximum number of\nrecent bloggers the widget displays.\n\nHere are descriptions for the other tabs in the widget's configuration:\n\n\\textbf{Sharing:} Embed the widget instance as a widget on any website,\nFacebook, Netvibes, or as an OpenSocial Gadget.\n\n\\textbf{Scope:} Specify the blog instance the widget displays: the\ncurrent site's blog (default), the global blog, or the page's blog. If\nthe page doesn't already have a blog instance, you can select scope\noption \\emph{{[}Page Name{]} (Create New)} to create a page-scoped blog\ninstance.\n\nWhen you're finished setting the options, click \\emph{Save}. Then close\nthe dialog box.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/blogs-recent-bloggers.png}\n\\caption{You can show off your site or organization's most recent\nbloggers from the Recent Bloggers app.}\n\\end{figure}\n\n\\chapter{Creating Forums with Message\nBoards}\\label{creating-forums-with-message-boards}\n\nAlthough you're likely already familiar with what a modern forum can do,\nhere's a sampling of what users and administrators can do with Liferay\nDXP's Message Boards app.\n\nUsers can:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Start and reply to threads\n\\item\n  Mark a thread as a question and select an answer from the replies.\n\\item\n  Subscribe to threads\n\\item\n  Author posts in BBCode or with the standard WYSIWYG editor\n\\item\n  Assign thread priority (e.g., sticky, announcement, etc.)\n\\item\n  Attach files to a thread\n\\item\n  Rate a thread (e.g., like/dislike)\n\\item\n  And more\n\\end{itemize}\n\nAdministrators can:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Organize threads into categories and subcategories\n\\item\n  Scope a message board to a page, a Site, or the entire portal.\n\\item\n  Publish threads via RSS\n\\item\n  Rank users by the number of messages they post and assign labels to\n  these rankings (e.g., novice, legend, etc.)\n\\item\n  Create and modify thread priorities (e.g., sticky, announcement, etc.)\n\\item\n  Configure email notifications for thread activity\n\\item\n  And more\n\\end{itemize}\n\nAs you can see, there's something for everyone!\n\nThe Message Boards app also integrates with the rest of Liferay DXP's\nfeatures. In many web sites, it's obvious that there's no link between\nthe main Site and the message boards. In some cases, users are even\nrequired to register twice: once for the main Site and once for the\nmessage boards. Sometimes it's even three times: once for the Site, once\nfor the message boards, and once for the shopping cart. By providing a\nmessage boards app along with all of the other apps and widgets, Liferay\nDXP provides a unique, integrated approach to building Sites.\nAdministrators can concentrate on building their Site while the\nintegration work is done for them.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/message-boards-category-threads.png}\n\\caption{The Message Boards widget lets you explore its categories,\ninteract with message threads, and post new messages.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/message-boards-participate-in-threads.png}\n\\caption{A thread's view displays author information and thread content,\nfor the thread and all replies to the thread.}\n\\end{figure}\n\n\\chapter{Creating Message Boards}\\label{creating-message-boards}\n\nYou can create and manage message boards in the Global, Site, and Page\nscopes. Regardless of scope, you manage a message board via the Site\nAdministration menu. The following sections show you how to use this\nmenu to manage message boards in each of these scopes.\n\n\\section{Site-scoped Message Boards}\\label{site-scoped-message-boards}\n\nBy default, the Message Boards app in Site Administration is scoped to\nthe current Site. To administer this message board, open the \\emph{Menu}\n(\\includegraphics{./images/icon-menu.png}), expand the menu for your\nSite, then navigate to \\emph{Content \\& Data} → \\emph{Message Boards}.\nThe Message Boards administration screen then appears. Note that the\noptions available on this screen are the same regardless of scope. The\nnext sections show you how to change scope and then access this screen.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/message-boards-administration.png}\n\\caption{A Message Board instance starts empty, ready for you to\nconfigure for your purposes.}\n\\end{figure}\n\n\\section{Page-scoped Message Boards}\\label{page-scoped-message-boards}\n\nIf you need a page-scoped message board, you must add a Message Boards\nwidget to that page and then set its scope to the page. Follow these\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the page.\n\\item\n  From the \\emph{Add} menu\n  (\\includegraphics{./images/icon-add-app.png}), open \\emph{Widgets} →\n  \\emph{Collaboration}.\n\\item\n  Drag a \\emph{Message Boards} widget onto the page.\n\\item\n  Click the \\emph{Options} icon\n  (\\includegraphics{./images/icon-app-options.png}) in the widget's\n  title bar and select \\emph{Configuration}.\n\\item\n  From the \\emph{Scope} menu in the \\emph{Scope} tab, select the page's\n  name or \\emph{PageName (Create New)} if the page scope doesn't exist\n  yet.\n\\item\n  Click \\emph{Save}, and then close the dialog.\n\\end{enumerate}\n\nNote that you must still use the Site Administration menu to administer\na page-scoped Message Boards widget. You do so by setting the Site\nAdministration menu's active scope. Follow these steps to do this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}),\n  expand the menu for your Site, then expand \\emph{Content \\& Data}.\n\\item\n  The current scope appears just below the \\emph{Content \\& Data}\n  heading. \\emph{Default Scope} is the current Site. To change this,\n  click the gear icon\n  (\\includegraphics{./images/icon-control-menu-gear.png}) and then\n  select your desired scope. This changes the Site Administration menu\n  to reflect scope you selected. To work in a page's scope, for example,\n  select that page from the gear icon. That page's name then becomes the\n  Site Administration menu's title.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/mb-site-admin-scope.png}\n  \\caption{Select the page's scope under the \\emph{Content \\& Data} menu\n  in Site Administration.}\n  \\end{figure}\n\\item\n  Select \\emph{Message Boards} from the \\emph{Content \\& Data} menu. Any\n  changes you make here apply to the scope that you selected in the\n  previous step.\n\\end{enumerate}\n\n\\section{Globally-scoped Message\nBoards}\\label{globally-scoped-message-boards}\n\nTo manage a message board in the global scope, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}), then\n  click the compass icon (\\includegraphics{./images/icon-compass.png})\n  on the Site Administration menu. This opens the Select Site dialog.\n\\item\n  Select the \\emph{My Sites} tab, then select \\emph{Global}. This closes\n  the dialog and changes the Site Administration menu's title to\n  \\emph{Global}.\n\\item\n  Select \\emph{Message Boards} from the \\emph{Content \\& Data} menu. Any\n  changes you make here apply to the global scope.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/mb-global-scope.png}\n  \\caption{After changing to the global scope, select \\emph{Message\n  Boards} from the \\emph{Content \\& Data} menu in Site Administration.}\n  \\end{figure}\n\\end{enumerate}\n\n\\chapter{Configuring Message Boards}\\label{configuring-message-boards}\n\nBefore using a message board, configure it to your needs. First, open\nthe Message Boards app in your scope's Site Administration menu, as\ndescribed\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-message-boards}{earlier}.\nTo open the message board's configuration screen, click the message\nboard's \\emph{Options} menu\n(\\includegraphics{./images/icon-options.png}) and select\n\\emph{Configuration}. The below sections cover these tabs.\n\n\\section{General Setup}\\label{general-setup}\n\nThe \\emph{General} tab contains general settings:\n\n\\textbf{Allow Anonymous Posting:} Choose if users can post anonymously.\nUse this with caution---anonymous users tend to be mean.\n\n\\textbf{Subscribe by Default:} Choose if users are subscribed\nautomatically to threads in which they've posted.\n\n\\textbf{Message Format:} Define the markup language of users' message\nboard posts. You can choose BBCode or HTML. When creating posts, the\ntype of WYSIWYG editor presented to users depends on which option is\nenabled. Both editors have a \\emph{Source} button that lets users view a\nmessage's underlying BBCode or HTML. Users can compose messages using\neither the WYSIWYG or Source view and can switch between views during\nmessage composition by clicking the \\emph{Source} button. For security\nreasons, BBCode is preferred.\n\n\\textbf{Enable Report Inappropriate Content:} Choose if users can report\ncontent as inappropriate. This sends a message to administrators so they\ncan take action.\n\n\\textbf{Enable Ratings:} Choose if users can rate posts.\n\n\\textbf{Thread as Question by Default:} This automatically checks the\n\\emph{Mark as question} box in the new thread window. Threads marked as\nquestions display \\emph{waiting for an answer}. Replies to the original\nmessage can be marked as an answer.\n\n\\textbf{Show Recent Posts from Last:} The \\emph{Recent Posts} tab shows\nposts from the following timeframes you define here:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  24 hours\n\\item\n  7 days (default)\n\\item\n  30 days\n\\item\n  365 days\n\\end{itemize}\n\nAfter the allotted time has passed, the post expires from \\emph{Recent\nPosts}, but is still accessible everywhere else in the message board.\n\n\\section{Email Setup}\\label{email-setup}\n\nUse these tabs to configure how the Message Boards app handles email\nnotifications:\n\n\\textbf{Email From}: The name and email address that sends email\nnotifications. The default administrator account's name and email\naddress. Default values, are from the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Admin\\%20Portlet}{\\texttt{admin.email.from.name}\nand \\texttt{admin.email.from.address}}\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{portal\nproperties}). These were set in the Basic Configuration Wizard when\ninstalling Liferay DXP. Make sure to update this email address to a\nvalid one that can be dedicated to notifications.\n\n\\textbf{HTML Format:} Support HTML in these emails.\n\n\\textbf{Definition of Terms:} Shows variables you can use in the email\ntemplates you'll define next.\n\n\\textbf{Message Added Email:} Create a template for email users receive\nwhen a message is added to a topic they subscribe to.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Enabled:} Whether automatic emails are sent to subscribed\n  users.\n\\item\n  \\textbf{Subject:} Choose a prefix for the email's subject line. This\n  lets users set up message filters in their email clients for these\n  notifications.\n\\item\n  \\textbf{Body:} The message body content. Use the variables defined in\n  \\emph{Definition of Terms} to customize this content for users.\n\\item\n  \\textbf{Definition of Terms:} Shows variables you can use in the email\n  templates.\n\\end{itemize}\n\n\\textbf{Message Updated Email:} This tab is identical to the Message\nAdded Email tab, except it defines the email that users receive when a\npost is updated.\n\n\\section{Thread Priorities}\\label{thread-priorities}\n\nThe \\emph{Thread Priorities} tab defines custom priorities for message\nthreads. This lets privileged Roles tag a thread with a certain\npriority, which highlights it for users. Three priorities are defined by\ndefault:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Urgent\n\\item\n  Sticky\n\\item\n  Announcement\n\\end{itemize}\n\nTo define a thread priority, enter its name, a URL to its image icon,\nand a priority number. Threads with a higher priority are posted above\nthreads with a lower priority.\n\n\\textbf{Thread Icons}\n\n\\begin{longtable}[]{@{}\n  >{\\centering\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5526}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.4474}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\centering\n~\\textbf{Icon}\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\n\\textbf{Definition}\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\n\\includegraphics{./images/icon-message-boards-urgent.png} & Urgent \\\\\n\\includegraphics{./images/icon-message-boards-announcement.png} &\nAnnouncement \\\\\n\\includegraphics{./images/icon-message-boards-sticky.png} & Sticky \\\\\n\\includegraphics{./images/icon-message-boards-question.png} &\nQuestion \\\\\n\\end{longtable}\n\nThe localized language field lets you name the priorities in each\nlocale. You can select the locale, update the priority names for it, and\nsave your updates.\n\n\\section{User Ranks}\\label{user-ranks}\n\nThe User Ranks tab ranks users by the number of messages they have\nposted. Default ranks from 0 to 1000 are provided, but you can set\ncustom ranks here as well.\n\nYou can also use this to define message boards labels that appear on\nuser profiles. For example, you can use the message boards label\n\\emph{Moderator} for anyone who is a part of any of the Message Boards\nAdministrator groups: the Site Role, the Organization, the Organization\nRole, the regular Role, or the User Group:\n\n\\begin{verbatim}\nModerator=organization:Message Boards Administrator\n\nModerator=organization-role:Message Boards Administrator\n\nModerator=regular-role:Message Boards Administrator\n\nModerator=site-role:Message Boards Administrator\n\nModerator=user-group:Message Boards Administrator\n\\end{verbatim}\n\nAs with thread priority names, the \\emph{Localized Language} field\nlocalizes rank names.\n\n\\section{RSS}\\label{rss}\n\nMessage board threads can be published as RSS feeds. The RSS tab\nenables/disables RSS subscriptions and defines how the feeds are\ngenerated:\n\n\\textbf{Maximum Items to Display:} The number of items to display in the\nfeed.\n\n\\textbf{Display Style:} The feed's appearance. You can publish the full\ncontent, an abstract, or just the thread title.\n\n\\textbf{Format:} The feed's format: RSS 1.0, RSS 2.0, or Atom 1.0.\n\nOnce you've finished configuring your message board, make sure to\n\\emph{Save} your changes.\n\n\\chapter{Message Board Permissions}\\label{message-board-permissions}\n\nOpen the Message Boards app in your scope's Site Administration menu, as\ndescribed in\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-message-boards}{the\narticle on creating message boards}. Then click the \\emph{Options} icon\n(\\includegraphics{./images/icon-options.png}) and select the \\emph{Home\nCategory Permissions} option. This permissions screen is for granting\nand revoking access to message board functions.\n\nThe permissions enable a Role to perform the following actions:\n\n\\textbf{Permissions:} View and modify permissions.\n\n\\textbf{Add File:} Attach a file to a message.\n\n\\textbf{Ban User:} Forbid a user from participating in the message\nboard.\n\n\\textbf{Add Category:} Add a new category to the message board.\n\n\\textbf{Reply to Message:} Respond to an existing message.\n\n\\textbf{Lock Thread:} Stop any further additions or modifications to a\nthread's messages.\n\n\\textbf{Subscribe:} Receive notifications on new and modified posts.\n\n\\textbf{View:} View all the contents of message threads.\n\n\\textbf{Add Message:} Post a new thread.\n\n\\textbf{Move Thread:} Move a thread to a different category or\nsubcategory.\n\n\\textbf{Update Thread Priority:} Modify a thread's priority.\n\nConfigure the Roles with the permissions you want and \\emph{Save} your\nchanges.\n\nAfter adding a Message Boards widget to a page, you can access that\nwidget instance's general permissions. To do so, select the widget's\n\\emph{Options} menu (\\includegraphics{./images/icon-app-options.png})\nand select \\emph{Permissions}. This permissions screen lets you control\naccess to the widget instance's Permissions, Preferences, and\nConfiguration menus.\n\n\\chapter{Message Board Categories}\\label{message-board-categories}\n\nMessage Board categories organize threads by topic. This makes it easier\nto find the right topic for discussion, and can also help discussions\nstay on topic. For example, a tropical fishkeeping message board may\nhave separate categories for freshwater and saltwater topics.\n\nThis article shows you how to create and manage message board\ncategories.\n\n\\section{Adding Categories}\\label{adding-categories}\n\nFollow these steps to create a message board category:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Message Boards app in your scope's Site Administration menu,\n  as described in\n  \\href{/docs/7-2/user/-/knowledge_base/u/creating-message-boards}{Creating\n  Message Boards}.\n\\item\n  Click the \\emph{Add} icon (\\includegraphics{./images/icon-add.png})\n  and select \\emph{Category}. This opens the Add Category form.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/message-boards-add-category.png}\n  \\caption{You have several options to create a message board category\n  for your needs.}\n  \\end{figure}\n\\item\n  Enter a name and description for the category.\n\\item\n  Select the category's \\emph{Display Style}. This controls how threads\n  in the category appear. By default, you can choose these display\n  styles:\n\n  \\textbf{Default:} Classic display style for general purpose\n  discussions.\n\n  \\textbf{Question:} Threads appear in a question and answer style.\n\n  You can create custom display styles and make them available for\n  selection in this form. You must set the available display styles via\n  the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Message\\%20Boards\\%20Portlet}{portal\n  property} \\texttt{message.boards.category.display.styles}. Similarly,\n  you can set the default display style in\n  \\texttt{message.boards.category.display.styles.default}.\n\\item\n  Open the \\emph{Mailing List} section of the form and set the mailing\n  list options you want. To enable a mailing list for the category, set\n  the \\emph{Active} toggle to \\emph{YES}. To enable anonymous emails in\n  the list, set the \\emph{Allow Anonymous Emails} toggle to \\emph{YES}.\n  The default for both toggles is \\emph{NO}. For an explanation of these\n  features, see\n  \\href{/docs/7-2/user/-/knowledge_base/u/user-subscriptions-and-mailing-lists\\#mailing-lists}{the\n  documentation on mailing lists for Message Boards}.\n\\item\n  Open the \\emph{Permissions} section and set the category's\n  permissions. The \\emph{Viewable by} selector lets you pick who can\n  view the category:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Anyone (Guest Role)\n  \\item\n    Site Members\n  \\item\n    Owner\n  \\end{itemize}\n\n  To show more permissions options, click \\emph{More Options}. A table\n  appears with the rest of the category's permissions, which you can\n  assign to the Guest and Site Member roles:\n\n  \\textbf{Delete:} Remove the category.\n\n  \\textbf{Permissions:} View and modify permissions.\n\n  \\textbf{Add File:} Attach a file to any of your messages.\n\n  \\textbf{Reply to Message:} Respond to existing messages.\n\n  \\textbf{Lock Thread:} Stop any further additions or modifications to a\n  thread's messages.\n\n  \\textbf{Update:} Edit the category.\n\n  \\textbf{Subscribe:} Receive notifications on new and modified posts.\n\n  \\textbf{View:} View the category.\n\n  \\textbf{Add Message:} Post a new thread.\n\n  \\textbf{Move Thread:} Move a thread to a different category or\n  subcategory.\n\n  \\textbf{Add Subcategory:} Add a new category within this category.\n\n  \\textbf{Update Thread Priority:} Modify a thread's priority.\n\n  Note that after creating a category, you can revisit its permission\n  options by clicking the category's \\emph{Actions} icon\n  (\\includegraphics{./images/icon-actions.png}) and selecting\n  \\emph{Permissions}.\n\\item\n  Click \\emph{Save} when you're finished. Your category now appears in\n  the table.\n\\end{enumerate}\n\nAs you add categories to a message board, they appear on the message\nboard's home screen. The list displays the category names and the\nnumbers of subcategories, threads, and posts in each one.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/message-boards-home.png}\n\\caption{Categories help you organize threads so users can find topical\nthreads that interest them.}\n\\end{figure}\n\n\\section{Adding Subcategories}\\label{adding-subcategories}\n\nCategories can contain as many subcategories as you like. If, however,\nyou nest categories too deep, users can have trouble finding them.\n\nFollow these steps to add a subcategory to a category:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the category's name in the list, then click the \\emph{Add} icon\n  (\\includegraphics{./images/icon-add.png}) and select \\emph{Category}.\n  The Add Category form appears.\n\\item\n  Fill out the Add Category form with the values and settings you want\n  to use for the subcategory. This form is populated with the parent\n  category's properties by default.\n\\item\n  Click \\emph{Save} when you're finished. Your subcategory now appears\n  in the table.\n\\end{enumerate}\n\n\\section{Moving and Merging\nCategories}\\label{moving-and-merging-categories}\n\nEach category can have any number of threads, and you can add as many\ncategories and subcategories as you wish. You can also move and merge\ncategories.\n\nFollow these steps to move a category or merge it with another:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the category's \\emph{Actions} icon\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Move}.\n  This brings up the Move Category form.\n\\item\n  Select a new parent category via the \\emph{Select} button under the\n  \\emph{Parent Category} field. Note that this field is empty for\n  top-level categories.\n\\item\n  If you want to merge the category with the selected parent category,\n  select \\emph{Merge with Parent Category}.\n\\item\n  Click \\emph{Move}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/mb-move-merge.png}\n\\caption{The Move Category form lets you move and merge categories.}\n\\end{figure}\n\n\\chapter{User Subscriptions and Mailing\nLists}\\label{user-subscriptions-and-mailing-lists}\n\nThe Message Boards app notifies users of message boards activity via\nemail in two ways:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  User subscriptions\n\\item\n  Mailing lists\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Since multiple sites can use a globally scoped message\nboard, such message boards don't support user subscriptions or mailing\nlists. Make sure to use a site-scoped or page-scoped message board if\nyou need user subscriptions or a mailing list.\n\n\\noindent\\hrulefill\n\n\\section{User Subscriptions}\\label{user-subscriptions}\n\nIn the user subscriptions mechanism, the Message Boards app uses its\nconfigured \\emph{Email From} address to send email notifications to\nsubscribed users. The app can also import email replies to message board\nnotifications directly into the message board. Then, users can interact\non the message board via email without logging in to view the message\nboard directly. This is disabled by default. To enable it, add the\nfollowing line to your \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\npop.server.notifications.enabled=true\n\\end{verbatim}\n\nThe user subscription mechanism uses the POP mail protocol. When the\nMessage Boards app receives an email reply to a message board\nnotification, it posts that reply to the message board and then deletes\nit from the mail server. Deleting the message from the mail server is\nthe POP protocol's default behavior and the Message Boards app assumes\nthat your POP mail server behaves this way. Most POP clients offer an\noption to leave mail on the mail server after it downloads, but you\nshouldn't exercise this option. If you configure mail to be left on the\nmail server, the Message Boards app sends copies of each retained\nmessage along with each new email notification it sends to subscribed\nusers.\n\nWhen enabling Message Boards to import replies to email notifications,\nyou must decide whether to handle notifications with a mail server\nsubdomain. By default, the following property setting is specified in\nthe portal properties:\n\n\\begin{verbatim}\npop.server.subdomain=events\n\\end{verbatim}\n\nThis property creates a special MX (mail exchange) subdomain to receive\nall virtual instance related email (e.g., events.liferay.com). If you\ndon't want to use this approach, unset this value in a\n\\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\npop.server.subdomain= \n\\end{verbatim}\n\nDoing so tells Message Boards to use the \\emph{Email From} address\nspecified in the Message Board's configuration to receive message board\nnotification email replies. For example, the \\emph{Email From} address\ncould be set to \\emph{replies@liferay.com}.\n\nIf you're not using a mail subdomain, Message Boards parses the message\nheaders of emails from the \\emph{Email From} address to determine the\nmessage board category and message ID. If you keep the\n\\texttt{pop.server.subdomain=events} default, the email notification\naddress takes the following form:\n\n\\begin{verbatim}\nmb.[category_id][message_id]@events.liferay.com\n\\end{verbatim}\n\nIn this case, Message Boards parses the email address to find the\ncategory and message ID. Parsing the email address is safer than parsing\nmessage headers, since different email clients treat message headers\ndifferently. This is why the \\texttt{events} subdomain is enabled by\ndefault.\n\nYou can also configure the interval on which the\n\\texttt{POPNotificationListener} runs. The value is set in one minute\nincrements. The default setting is to check for new mail every minute,\nbut you can set it to whatever you like:\n\n\\begin{verbatim}\npop.server.notifications.interval=1\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: Depending on your mail provider, if you use multiple\ndevices to access email through POP, you might need to configure in your\nPOP settings something like Gmail's \\emph{recent mode}, which keeps the\nlast 30 days of email available on the server. Then, more than just the\nfirst client can receive email. To enable recent mode in Gmail, for\nexample, prefix the value of your POP client's Username or Email field\nwith \\texttt{recent:}.\n\nIf you don't use Gmail, IMAP may be a better solution for you.\n\n\\noindent\\hrulefill\n\n\\section{Mailing Lists}\\label{mailing-lists}\n\nAlternatively, the Message Boards app can use mailing lists to send\nemail notifications. Any category in a message board can have its own\nmailing list. The mailing list mechanism, unlike the user subscription\nmechanism, supports both the POP and the IMAP protocols. POP is the\ndefault, but each message board's mailing list is configured\nindependently. If you choose the IMAP protocol for a category's mailing\nlist, make sure to configure the IMAP inbox to delete messages as they\nare pulled by the email client that sends messages to the users on the\nmailing list. Otherwise, each email message retained on the server is\nsent to the mailing list each time there's a new post or update in the\ncategory.\n\nWhen a mailing list is enabled for a message board category, Message\nBoards listens to the specific email inbox that's configured for the\nmailing list. Enabling the mailing list function lets users on the\nmailing list reply to the notification messages in their email clients.\nMessage Boards pulls the messages from the email inbox it's configured\nto listen to and automatically copies those replies to the appropriate\nmessage board thread.\n\nTo enable the mailing list functionality for a category, follow these\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Set up a dedicated email address for the category.\n\\item\n  Click the category's \\emph{Actions} icon\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Edit}.\n\\item\n  In the \\emph{Mailing List} section of the form, set the \\emph{Active}\n  slider to \\emph{YES}. Several options then appear. Fill these out as\n  follows:\n\n  \\textbf{Email Address:} The email address of the account that receives\n  the messages.\n\n  \\textbf{Protocol:} Select POP or IMAP.\n\n  \\textbf{Server Name:} Your mail server's host name.\n\n  \\textbf{Server Port:} The port on which your mail service is running.\n\n  \\textbf{Use a Secure Network Connection:} Whether to use an encrypted\n  connection if your server supports it.\n\n  \\textbf{User Name:} The login name on the mail server.\n\n  \\textbf{Password:} The password for the account on the server.\n\n  \\textbf{Read Interval (Minutes):} How often to poll the server looking\n  for new messages to post.\n\n  \\textbf{Email Address (Outgoing):} The email address originating\n  messages from this category. If you want your users to be able to\n  reply to the categories using email, this should be the same address\n  as the incoming email address.\n\n  \\textbf{Use Custom Outgoing Server:} Use a different mail server than\n  global default. Fields appear for configuring the server's name, port,\n  user name, password, and secure connection.\n\\item\n  If you want to let emails from anonymous users post to the message\n  board category, set the \\emph{Allow Anonymous Emails} toggle to\n  \\emph{YES}.\n\\item\n  Click \\emph{Save} when you're finished.\n\\end{enumerate}\n\n\\chapter{Using the Message Boards}\\label{using-the-message-boards}\n\nYou can add a Message Boards widget to a page from the \\emph{Add}\n(\\includegraphics{./images/icon-add-app.png}) menu's \\emph{Widgets} →\n\\emph{Collaboration} section. The Message Boards interface is similar to\nother message boards that populate the Internet. In any case, it can't\nhurt to explore how to use Liferay DXP's Message Boards and discover its\nfeatures.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/message-boards-category-threads.png}\n\\caption{The Message Boards widget lets you explore its categories,\ninteract with message threads, and post new messages.}\n\\end{figure}\n\nThreads can be viewed many ways. At the top of the widget is a set of\ntabs:\n\n\\textbf{Categories:} The message board's categories.\n\n\\textbf{Recent Posts:} Posts from all categories, sorted by date.\n\n\\textbf{My Posts:} The current user's posts.\n\n\\textbf{My Subscriptions:} Lets users view and manage their thread\nsubscriptions.\n\n\\textbf{Statistics:} The number of categories, posts, participants, and\na list of the top contributors. You can also access this from the same\ntab in Site Administration's Message Board app.\n\nYou can also use the search bar at the top of the widget to search for\nthreads and posts. Although search works on threads and posts within\ncategories, it doesn't work on categories themselves.\n\n\\section{Posting New Threads}\\label{posting-new-threads}\n\nFollow these steps to post a new thread:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the Message Boards widget's \\emph{New Thread} button.\n  Alternatively, click the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}) and select \\emph{Thread} in\n  the Message Boards app in Site Administration. Either way, the same\n  \\emph{Add Message} form appears.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/message-boards-add-thread.png}\n  \\caption{The Add Message form lets you create a new thread.}\n  \\end{figure}\n\\item\n  Give your thread a title in the \\emph{Subject} field.\n\\item\n  Create your thread's content in the \\emph{Body} field. This field uses\n  the same editor as the Blogs app, except that it uses BBCode instead\n  of HTML. For further instructions, see the documentation on\n  \\href{/docs/7-2/user/-/knowledge_base/u/using-the-blog-entry-editor}{using\n  the editor}. Also note that you can\n  \\href{/docs/7-2/user/-/knowledge_base/u/mentioning-users}{mention}\n  other users by entering the \\texttt{@} character and their user name.\n\\item\n  If you want to add attachments, open the \\emph{Attachments} section\n  and add them via drag and drop or the \\emph{Select Files} button.\n\\item\n  If you want to associate a tag with the message, open the\n  \\emph{Categorization} section and use the \\emph{Select} button to\n  select an existing tag. You can also create a new tag by entering it\n  in the \\emph{Tags} field and clicking \\emph{Add}. See\n  \\href{/docs/7-2/user/-/knowledge_base/u/tagging-content}{the\n  documentation on tags} for more information.\n\\item\n  If you want to select an existing asset in the portal (e.g., a media\n  file, blog post, etc.) to relate to your thread, open the\n  \\emph{Related Assets} section and use the \\emph{Select} button to\n  select that asset.\n\\item\n  Open the \\emph{More Settings} section and select the settings you want\n  to use:\n\n  \\textbf{Mark as a Question:} Whether to mark this thread as a\n  question. This lets you later select a post in the thread as the\n  answer.\n\n  \\textbf{Anonymous:} Whether this thread is posted anonymously.\n\n  \\textbf{Subscribe Me:} Receive notifications for activity on the\n  thread.\n\n  \\textbf{Priority:} The thread's priority in the Message Board. By\n  default, you can choose \\emph{Urgent}, \\emph{Sticky}, or\n  \\emph{Announcement}. Additional priorities can also be\n  \\href{/docs/7-2/user/-/knowledge_base/u/configuring-message-boards}{configured}\n  in the Message Boards app in Site Administration.\n\n  \\textbf{Allow Pingbacks:} Whether\n  \\href{https://en.wikipedia.org/wiki/Pingback}{pingbacks} are allowed\n  for your thread.\n\\item\n  Open the \\emph{Permissions} section and set the thread's permissions.\n  Possible values in the \\emph{Viewable by} selector are\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Anyone (Guest Role)\n  \\item\n    Site Members\n  \\item\n    Owner\n  \\end{itemize}\n\n  You can also click the \\emph{More Options} link to select additional\n  permissions:\n\n  \\textbf{Delete:} Remove the thread.\n\n  \\textbf{Permissions:} Grant/revoke thread permissions.\n\n  \\textbf{Update:} Edit the thread.\n\n  \\textbf{Subscribe:} Receive notifications for thread activity.\n\n  \\textbf{View:} View the thread.\n\n  Note that you can revisit the thread's permissions after posting it.\n  To do so, select the thread's Actions menu\n  (\\includegraphics{./images/icon-actions.png}) and select\n  \\emph{Permissions}.\n\\item\n  Click \\emph{Publish} to publish the thread. Once it's published, it\n  appears along with the other threads in the category.\n\\end{enumerate}\n\n\\section{Participating in Message Board\nThreads}\\label{participating-in-message-board-threads}\n\nTo find message board threads that interest you, browse a message\nboard's Categories or Recent Posts tabs. In the Categories tab, you can\nview a category's thread listing by clicking the category's name. Within\na category, you can subscribe to an RSS feed and/or emails that inform\nyou about activity in that category. The Recent Posts tab also lists\nthreads, except they're the latest threads across all categories.\n\nClick a thread to view it. Messages appear in a threaded view so that\nreplies are aligned under their parent thread. This makes it easy to\nfollow conversations. Thread replies are indented under their parent\nthread.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/message-boards-participate-in-threads.png}\n\\caption{A thread's view displays author information and thread content,\nfor the thread and all replies to the thread.}\n\\end{figure}\n\nSubscribing to a thread causes Message Boards to send the user an email\nwhenever a new message is posted to the thread. If you have enabled the\nmailing list feature for the thread's category, users can reply to these\nmessages to post back to the thread without having to visit your site.\n\nMost threads get more interesting as users reply to them. Follow these\nsteps to reply to a message in a thread:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Reply} button. This opens the quick reply form, which\n  only contains a text field for entering your reply.\n\\item\n  Enter your reply in the text field. To access more options for your\n  reply, click the \\emph{Advanced Reply} link. This opens the full\n  editor from the add/edit thread form.\n\\item\n  Click \\emph{Publish} to publish your reply.\n\\end{enumerate}\n\nIn addition to replying to a message, you can rate it or flag it as\nobjectionable. A message board moderator can evaluate flagged messages\nand decide how to handle the messages and their authors.\n\n\\chapter{Managing Message Boards}\\label{managing-message-boards}\n\nMessage boards can become unwieldy if left unmanaged. The Message Boards\nin Site Administration facilitates day-to-day thread administration. You\nmay wish to assign this function to a\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{Role}\nthat you give to one or more users. This frees you to concentrate on\nother areas of your Site. For example, you can create a Role called\n\\emph{Message Board Administrator} scoped to the portal (globally), an\nOrganization, or a Site. Members of a global Role can administer Message\nBoards throughout the portal. Members of an Organization or Site-scoped\nRole can only administer Message Boards in that Organization or Site,\nrespectively.\n\nFollow these steps to create a global Role:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the Control Panel, select \\emph{Users} → \\emph{Roles}.\n\\item\n  Select or create the Role.\n\\item\n  Select the Role's \\emph{Define Permissions} tab and navigate to\n  \\emph{Site Administration} → \\emph{Content \\& Data} → \\emph{Message\n  Boards}. A screen appears that lets you configure Message Board\n  permissions.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/message-boards-role-permissions.png}\n  \\caption{Define the permissions you want to use for the message boards\n  administrators.}\n  \\end{figure}\n\\item\n  Select the permissions you want message board administrators to have,\n  then click \\emph{Save}.\n\\item\n  Add users to this Role.\n\\end{enumerate}\n\n\\section{Locking Threads}\\label{locking-threads}\n\nYou may encounter threads that you think should be preserved, but\nstopped. You can halt activity on a thread by selecting \\emph{Lock} from\nthe thread's \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}).\n\n\\section{Moving Threads}\\label{moving-threads}\n\nIf someone posts a thread to the wrong category, you can move it to the\nproper one. Follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select \\emph{Move} from the thread's \\emph{Actions} menu\n  (\\includegraphics{./images/icon-actions.png}). This opens the\n  \\emph{Move Thread} form.\n\\item\n  Click the \\emph{Select} button and select the new category.\n\\item\n  If you want to add a post explaining the move, select \\emph{Add\n  explanation post}.\n\\item\n  Click \\emph{Move} to move the thread.\n\\end{enumerate}\n\n\\section{Deleting Threads}\\label{deleting-threads}\n\nSometimes users begin discussing topics that are inappropriate or that\nreveal confidential information. In this case, administrators can delete\nthe thread from the message boards. To do so, select \\emph{Move to\nRecycle Bin} from the thread's \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}).\n\n\\section{Banning Users}\\label{banning-users}\n\nUnfortunately, message board users can be abusive. In this case, you can\nban the user from the message boards. While viewing any of the user's\nposts in any thread, select the post's \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}) and select \\emph{Ban this\nUser}.\n\nTo reinstate a banned user, you must use the Message Boards app in Site\nAdministration. Navigate to this app and select the \\emph{Banned Users}\ntab. Select the user's \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}) and select \\emph{Unban\nthis User}.\n\n\\section{Splitting Threads}\\label{splitting-threads}\n\nSometimes a thread goes on for a while and the discussion completely\nchanges into something else. In this case, you can split the thread\nwhere the discussion diverged and create a whole new thread for the new\ntopic. To split a thread at a certain post, administrators can select\nthat post's \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}) and select \\emph{Split\nThread}. This brings up a form that lets you add an explanation post to\nthe split thread. Click \\emph{OK} to split the thread.\n\n\\section{Editing Posts}\\label{editing-posts}\n\nAdministrative users can edit anyone's posts, not just their own.\nSometimes users post links to copyrighted material or unsuitable\npictures. By editing these posts, you can redact information that\nshouldn't be posted, or remove content not conforming to your terms of\nuse. You can also update the thread's priority or mark a reply as an\nanswer to a thread's question.\n\nTo edit a post, select its \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}) and select \\emph{Edit}.\n\n\\section{Post Permissions}\\label{post-permissions}\n\nPermissions can be set not only on threads, but also on individual\nposts. You can choose to limit a particular conversation or post to only\na select group of users. To do this, select the post's \\emph{Actions}\nmenu (\\includegraphics{./images/icon-actions.png}) and select\n\\emph{Permissions}. You can then choose which Roles have the following\npermissions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Delete\n\\item\n  Permissions\n\\item\n  Subscribe\n\\item\n  Update\n\\item\n  View\n\\end{itemize}\n\nUse this, for example, to let some privileged users post on a certain\nthread, while others are only allowed to view it. Other combinations of\nthese permissions are also possible.\n\n\\chapter{Mentioning Users}\\label{mentioning-users}\n\nHave you ever wanted to include another user in a discussion on the\nMessage Boards? Have you ever wanted to give kudos to a colleague in\ncontent you're writing? You can mention (notify and/or draw attention\nto) other users by entering the \\texttt{@} character in front of each\nuser's user name.\n\nWhen you mention a user, the user receives a site notification next to\nthe user's profile icon and an email, alerting the user with a link to\nthe content. You can mention users in a blog entry, a message boards\npost, or comments in any app that supports comments. A mention also\nlinks to the user's home page, so readers can find out more about that\nuser.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/mentions-at-mention-menu.png}\n\\caption{As you enter a user name after the \\texttt{@} character, links\nto users that match the text you enter are displayed. Select the user\nyou want to mention and publish your content.}\n\\end{figure}\n\nA selector appears after entering the \\texttt{@} character, listing\nusers that match the name you're entering. In the selector, users are\nrepresented by their profile picture, name, and user name. Click the\nuser you want to mention and finish editing your content.\n\nOn publishing the content, mentioned users receive a notification and an\nemail informing them that they've been mentioned. The notification and\nemail indicate the author's name and content type, and contain links to\nthe content. You can access your notifications by selecting\n\\emph{Notifications} from your user menu.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/mentions-notification-list.png}\n\\caption{Your notifications are accessible from your user menu and\nappear in a list.}\n\\end{figure}\n\nMentions are enabled by the Mentions app, which is a part of the\nCollaboration Suite. By default, the Mentions app is enabled globally.\nHowever, you can enable/disable it globally or per site. For a site to\nuse Mentions, it must be enabled for the site's Virtual Instance.\n\nTo access the global Mentions settings for your Virtual Instance, first\nopen the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}) and\nnavigate to \\emph{Control Panel} → \\emph{Configuration} → \\emph{Instance\nSettings}. Then click \\emph{Community Tools} and select \\emph{Mentions}\nunder \\emph{VIRTUAL INSTANCE SCOPE}. By default, all users are allowed\nto mention fellow site members and friends. To fine tune these options,\nselect \\emph{Define Mentions Capability for Users} and specify the\nsettings you want.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/mentions-global-instance-setting.png}\n\\caption{You can enable or disable the Mentions feature for all of the\nVirtual Instance's sites.}\n\\end{figure}\n\nFor Mentions to be available for a site, the app must be enabled for\nthat site's Virtual Instance. Site administrators can enable or disable\nMentions for a site. A site's Mentions app configuration is accessible\nfrom within the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}).\nOnce in the menu, navigate to \\emph{{[}Site Name{]}} →\n\\emph{Configuration} → \\emph{Settings}. In the \\emph{Social} tab, expand\nthe \\emph{Mentions} section. Enable or disable mentions via the toggle\nlabeled \\emph{Allow Users to Mention Other Users}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/mentions-site-setting.png}\n\\caption{Mentions can also be enabled or disabled per site.}\n\\end{figure}\n\n\\chapter{Working Together with the\nWiki}\\label{working-together-with-the-wiki}\n\nWikis are for collaboratively building a collection of information. The\nmost famous wiki on the planet is Wikipedia. It's a full encyclopedia\ndeveloped collaboratively by users from all over the world, using a\nwiki. Liferay DXP's wiki does these things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Creates multiple wikis in a single wiki app instance.\n\\item\n  Scopes wikis to a page, a site, or the entire portal.\n\\item\n  Creates and edit wikis in \\href{http://www.wikicreole.org/}{WikiCreole\n  syntax}.\n\\item\n  Attaches files to wiki articles.\n\\item\n  Associates wiki articles with other assets in the portal.\n\\item\n  And more.\n\\end{itemize}\n\nAs you can see, Liferay DXP's wiki is flexible and can be configured to\nfit nearly any use case. What's more, it's completely integrated with\nthe portal's\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-users}{user\nmanagement},\n\\href{/docs/7-2/user/-/knowledge_base/u/tagging-content}{tagging}, and\n\\href{/docs/7-2/deploy/-/knowledge_base/d/securing-product}{security}\nfeatures.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-page-full.png}\n\\caption{The Wiki widget displays your wiki on a Site page.}\n\\end{figure}\n\n\\chapter{Getting Started with Wikis}\\label{getting-started-with-wikis}\n\nThe Menu (\\includegraphics{./images/icon-menu.png}) is the best place to\nstart working with your wikis. Click the \\emph{Menu}\n(\\includegraphics{./images/icon-menu.png}), navigate to your site, and\nselect the \\emph{Content \\& Data} section. If you're updating an\nexisting page-scoped wiki app instance, you can select that page scope\nfrom the scope menu the Gear icon\n(\\includegraphics{./images/icon-control-menu-gear.png}) makes available.\nThe site's wiki app instance is available in the Default scope. Once\nyou're in the proper content scope, click \\emph{Wiki}. The Wiki\nadministration screen lets you add, modify, and delete wiki nodes. A\nWiki app instance can contain many wiki nodes. By default, it contains\none node: \\emph{Main}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-admin-empty.png}\n\\caption{The Wiki app instance has a wiki node named \\emph{Main} with a\nsingle front page. You can build on the Main node or click the Add icon\nto create a new node.}\n\\end{figure}\n\n\\section{Configuring Wikis}\\label{configuring-wikis}\n\nBefore adding to your wiki instance, you should configure it. The\ninstance's interfaces for permissions, export and import, configuration,\nand application templates are accessible from the Options menu. Click\nthe \\emph{Options} icon (\\includegraphics{./images/icon-options.png}) to\nopen this menu.\n\nThe following options are available in this menu:\n\n\\textbf{Wikis Permissions}: Specify which\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{Roles}\ncan create wiki nodes and access the Wikis Permissions screen. For\nexample, if you've created a specific Role for creating wiki nodes and\nwant to enable that Role to create new wiki nodes in this wiki\napplication instance, select the Role's check box in the \\emph{Add Node}\ncolumn and then click \\emph{Save}.\n\n\\textbf{Export / Import}: Import existing wiki content into your wiki\napp instance, or export wiki content to a file. For details, refer to\n\\href{/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content}{Importing/Exporting\nPages and Content}.\n\n\\textbf{Configuration}: Configure email notifications and RSS feeds. The\n\\emph{Email From}, \\emph{Page Added Email}, and \\emph{Page Updated\nEmail} tabs are similar to other apps' notification email settings tabs;\nthey customize who wiki emails come from and the format and text of the\nemail sent when a page is added or updated. The \\emph{RSS} tab lets you\nconfigure RSS feeds.\n\n\\section{Adding Wikis}\\label{adding-wikis}\n\nFollow these steps to create a new wiki node:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Add} icon (\\includegraphics{./images/icon-add.png}) to\n  start creating a new wiki node. The \\emph{New Wiki Node} form appears.\n\\item\n  Add a name and description for the wiki node.\n\\item\n  Open the form's \\emph{Permissions} section and define the wiki node's\n  permissions. You can select the following permissions in the\n  \\emph{Viewable by} menu:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Anyone (Guest Role)\n  \\item\n    Site Members\n  \\item\n    Owner\n  \\end{itemize}\n\n  You can also click the \\emph{More Options} link to assign permissions\n  to specific Roles.\n\\item\n  Click \\emph{Save} when you're done creating the wiki node.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-new-wiki-node.png}\n\\caption{The New Wiki Node form lets you describe your new node, set\nview permissions, and set permissions for the Guest and Site Member\nroles.}\n\\end{figure}\n\n\\section{Wiki Node Options}\\label{wiki-node-options}\n\nNext to each listed wiki node is an \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}). Here are the actions\navailable in this menu:\n\n\\textbf{Edit}: Edit the wiki's name and description.\n\n\\textbf{Permissions}: Specify which roles can add attachments to wiki\npages, add pages, delete pages, import pages, set permissions on the\nwiki node, subscribe to modifications, update existing pages, and view\nthe wiki node.\n\n\\textbf{Import Pages}: Import data from other wikis. This lets you\nmigrate from another wiki application to the Liferay DXP wiki. You might\nwant to do this if you're migrating your site from a set of disparate\napplications (i.e., a separate forum, a separate wiki, a separate\ncontent management system) to Liferay DXP, which provides all of these\nfeatures. Currently, MediaWiki is the only supported wiki.\n\n\\textbf{RSS}: Subscribe to an RSS feed using Live Bookmarks, Yahoo,\nMicrosoft Outlook, or an application on your machine.\n\n\\textbf{Subscribe}: Subscribe to a wiki node. Any time a wiki page is\nadded or updated, the portal sends you an email notification.\n\n\\textbf{View Removed Attachments}: Display attachments that have been\nremoved from the wiki node.\n\n\\textbf{Move to Recycle Bin}: Moves the wiki node to the\n\\href{/docs/7-2/user/-/knowledge_base/u/restoring-deleted-assets}{Recycle\nBin}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-options.png}\n\\caption{Each wiki node's Actions menu lists actions you can perform.}\n\\end{figure}\n\nBefore opening wiki nodes to contributors, you should consider whether\nto associate a workflow with them. For example, you could create a\nworkflow that requires an administrator's approval to publish a wiki\npage modification (add, update, or delete). You can access your site's\ndefault \\emph{Wiki Page} workflow from within the Site Administration\nMenu, by navigating to \\emph{Configuration} → \\emph{Workflow} for your\nsite. To learn how to use workflow, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow}{Workflow} section.\n\n\\chapter{Adding and Editing Wiki\nPages}\\label{adding-and-editing-wiki-pages}\n\nWiki nodes initially have no pages. When you navigate into a node for\nthe first time, a default page called \\emph{FrontPage} is created\nautomatically. To view the page, click the wiki node's name and then\nclick \\emph{FrontPage}. The FrontPage appears and shows a message that\nexplains the page is empty and needs you to add content. That message is\na link; click it to start editing the page. The wiki page editing form\nthen appears.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} See the\n\\href{/docs/7-2/user/-/knowledge_base/u/getting-started-with-wikis}{getting\nstarted article} for instructions on accessing your wiki nodes.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-empty-frontpage.png}\n\\caption{Each empty wiki page presents a default message link you can\nclick to edit the page.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-page-editor.png}\n\\caption{The wiki page editing form lets you create and edit your page's\ncontent.}\n\\end{figure}\n\nFollow these steps to use the wiki page editing form:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Enter your content in the field that contains the text \\emph{Write\n  your content here\\ldots{}}. This is a rich-text, WYSIWYG editor that\n  is almost identical to the one used in the Blogs app. The only\n  difference is that the wiki editor uses Creole instead of HTML as its\n  source. Click the link \\emph{Show Syntax Help} if you need help with\n  Creole syntax (e.g., syntax for text styling, header formatting, link\n  creation, etc.). For a detailed explanation of the rest of the editor,\n  see the\n  \\href{/docs/7-2/user/-/knowledge_base/u/using-the-blog-entry-editor}{Blogs\n  documentation}.\n\\item\n  If you want to attach files to the page, open the \\emph{Attachments}\n  section of the form and add them via drag and drop or the \\emph{Select\n  Files} button.\n\\item\n  If you want to associate a tag with the page, open the\n  \\emph{Categorization} section and enter a new or existing tag in the\n  \\emph{Tags} field. See\n  \\href{/docs/7-2/user/-/knowledge_base/u/tagging-content}{the\n  documentation on tags} for more information.\n\\item\n  If you want to select an existing asset in the portal (e.g., a media\n  file, blog post, etc.) to relate to the page, open the \\emph{Related\n  Assets} section and use the \\emph{Select} button to select that asset.\n\\item\n  In the form's \\emph{Configuration} section, you can set the page to\n  use Creole (default), plain text, or HTML. We recommend that you stick\n  with the Creole format, as it allows for a much cleaner separation of\n  content and code. You can also use the Configuration section to\n  summarize your edit, and specify whether it's a minor edit.\n\\item\n  Click \\emph{Publish} to publish the page when you're done editing it.\n\\end{enumerate}\n\nAs is common with wikis in general, if you link to a page that doesn't\nexist, clicking that link opens the new page form with a note stating\nthat the page doesn't exist and that you are creating it.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: When you create a page by clicking a link to a page that\ndoesn't exist, the new page is \\textbf{not} a child of the current page.\nThe page is created at the wiki node's root. From Wiki in Site\nAdministration, you can use the page's Move action to assign it a new\nparent page. Clicking the Move action brings up a window that lets you\nselect a new parent for the wiki page.\n\n\\noindent\\hrulefill\n\nReturn to the wiki node view to see a list of the node's top-level\npages. If you navigate to a page that has child pages, its child pages\nare listed. In these page listings, each page's Actions menu\n(\\includegraphics{./images/icon-actions.png}) lists the following\nactions you can take on the page:\n\n\\textbf{Edit}: Opens the page in the page editor.\n\n\\textbf{Permissions}: Lets you determine which roles can view, update,\ndelete, subscribe to, or set permissions on the page, and add, update,\nor delete page discussions (comments).\n\n\\textbf{Copy}: Opens a page editor window with all the content from the\nsource wiki page. You're prompted to specify a new title for it.\n\n\\textbf{Move}: Opens a dialog that lets you rename the page or assign\nthe page to a new parent page within the wiki node.\n\n\\textbf{Add Child Page}: Create a new child page of the wiki page.\n\n\\textbf{Subscribe (or Unsubscribe)}: Subscribes you to (or unsubscribes\nyou from) notifications for the wiki page's modifications.\n\n\\textbf{Print}: Print the wiki page.\n\n\\textbf{Move to Recycle Bin}: Moves the wiki page to the Recycle Bin.\n\nEach wiki page has a check box next to it. When you select a page's\ncheck box, the Management Bar changes to show an Info icon\n(\\includegraphics{./images/icon-information.png}) and Recycle Bin icon\n(\\includegraphics{./images/icon-trash.png}). To move the selected page\nto the Recycle Bin, click the Recycle Bin icon. To get additional\ninformation about the page via an info panel, click the Info icon. The\ninfo panel has a star icon that you can select to subscribe to the\npage's modifications. The info panel's Details section displays the\npage's summary, format, version, creation and modification dates, number\nof attachments, and RSS link.\n\nThere are several more features in the wiki node view's Management Bar.\nThe \\emph{Filter and Order} menu orders the pages by title or\nmodification date and filters them by page type. The arrows button sorts\nthe pages in ascending or descending order. The search bar searches for\npages.\n\nThe \\emph{View Types} button is next to the Info icon. It lets you\nchoose how to display the pages. The View Types button's icon depends on\nthe selected view type:\n\n\\textbf{List} (\\includegraphics{./images/icon-view-type-list.png}):\nShows the pages in a list with an icon representing each page. Each\npage's entry contains the name of its author, when it was last modified,\nand its \\href{/docs/7-2/user/-/knowledge_base/u/workflow}{workflow}\nstatus (e.g., Approved, Draft, etc.).\n\n\\textbf{Table} (\\includegraphics{./images/icon-view-type-table.png}):\nShows the same information as the List view type, in a smaller list with\nno page icon. Also, the page's information is in columns and includes\nthe revision number.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-node-view-in-admin.png}\n\\caption{The wiki node's view in site administration has features that\nhelp you access and learn information about a wiki node's pages.}\n\\end{figure}\n\n\\chapter{Using the Wiki on Site\nPages}\\label{using-the-wiki-on-site-pages}\n\nYou can use the Wiki on Site pages via the Wiki widget. Follow these\nsteps to add the Wiki widget to a page:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the page where you want to place a wiki.\n\\item\n  From the \\emph{Add} (\\includegraphics{./images/icon-add-app.png})\n  menu, open \\emph{Widgets} → \\emph{Wiki} and add a \\emph{Wiki} to the\n  page.\n\\end{enumerate}\n\nYour Site's wiki nodes appear in tabs across the top of the widget.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-page-full.png}\n\\caption{Users can interact with your Wiki nodes when you add the Wiki\nwidget to a page.}\n\\end{figure}\n\nTo view the Wiki widget's configuration options, click its\n\\emph{Options} icon (\\includegraphics{./images/icon-app-options.png})\nand select \\emph{Configuration}. The Configuration screen appears with\nthese tabs:\n\n\\textbf{Setup}: Lets you choose wikis to display and gives you several\noptions for displaying them. The \\emph{Show Related Assets},\n\\emph{Enable Page Ratings}, \\emph{Enable Comments}, \\emph{Enable Ratings\nfor Comments}, and \\emph{Enable Highlighting} check boxes enable or\ndisable those features for the Wiki. You can set how you want users to\ninteract with wiki documents. The \\emph{Display Template} selector menu\nlets you choose the Wiki's\n\\href{/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates}{Widget\nTemplate}. Below this, you can set which wiki nodes are visible. For\nexample, you might host two wikis on a given site, exposing one to the\npublic and keeping the other private for site members.\n\n\\textbf{Communication}: Configure communication across portlets, using\npredefined public render parameters. From here you can modify six public\nrender parameters: \\texttt{categoryId}, \\texttt{nodeId},\n\\texttt{nodeName}, \\texttt{resetCur}, \\texttt{tag}, and \\texttt{title}.\nYou can perform these actions on each parameter:\n\n\\begin{itemize}\n\\item\n  Ignore the values for this parameter that come from other portlets.\n  For example, the wiki can be used along with the tags navigation app.\n  When a user clicks on a tag in tags navigation, the wiki shows a list\n  of pages with that tag. In some cases, an administrator may want the\n  wiki to show the front page always, independently of any tag\n  navigation done through other portlets. This can be achieved by\n  selecting \\emph{Ignore}, so that the values of the parameter coming\n  from those other portlets are ignored.\n\\item\n  Read the value of a parameter from another app. This is an advanced\n  but very powerful option that lets portlets communicate without prior\n  configuration. For example, imagine that the wiki is used to publish\n  information about certain countries, and a custom app that allows\n  browsing countries for administrative reasons was written and placed\n  on the same page. You could associate to this second app a public\n  render parameter called \\emph{country} to designate the name of the\n  country. Using this procedure, you can cause the wiki to show the\n  information from the country being browsed in the other app. You can\n  do this here for the wiki by setting the value for the title parameter\n  to be read from the country parameter of the other app.\n\\end{itemize}\n\n\\textbf{Sharing}: Displays options you're likely to be familiar with\nsuch as the sections for sharing the Wiki with websites, Facebook, and\nNetVibes.\n\n\\textbf{Scope}: Set the wiki's scope. You can select the site-scoped or\nglobal-scoped instance, or select/create an instance for the page. If\nthe page doesn't already have an instance scoped to it, you can click\nthe \\emph{{[}page name{]} (Create New)} menu option to create a\npage-scoped wiki instance.\n\nOnce you set the wiki's configuration options the way you want them,\nclick \\emph{Save}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-app-configuration-scope.png}\n\\caption{Here the user has selected to create a new Wiki instance scoped\nto the current page named \\emph{Welcome}}\n\\end{figure}\n\nThe Wiki's Options menu also contains the usual widget options:\n\n\\textbf{Look and Feel Configuration:} Set the widget's\n\\href{/docs/7-2/user/-/knowledge_base/u/look-and-feel-configuration}{look\nand feel}.\n\n\\textbf{Export/Import:}\n\\href{/docs/7-2/user/-/knowledge_base/u/exporting-importing-widget-data}{Export\nor import widget data}.\n\n\\textbf{Permissions:} Set the widget's permissions.\n\n\\textbf{Configuration Templates:} Use\n\\href{/docs/7-2/user/-/knowledge_base/u/configuration-templates}{configuration\ntemplates} to store the widget's current setup or apply an existing\narchived setup.\n\n\\textbf{Remove:} Remove the widget from the page.\n\nThe Wiki displays links to all of the Wiki instance's nodes, and\nprovides links for navigating around the wiki. Click on a wiki node's\nname to begin browsing that node's pages. The following navigation links\nare listed after the wiki nodes:\n\n\\textbf{FrontPage:} The wiki node's front page article. This is shown by\ndefault when the node is initially selected.\n\n\\textbf{Recent Changes}: Shows all of the recently updated pages.\n\n\\textbf{All Pages}: A flat, alphabetical list of all pages currently\nstored in the wiki.\n\n\\textbf{Orphan Pages}: A list of pages that have no links to them. This\ncan happen if you remove a page link without realizing it's the only\nlink to that page. This area lets you review such orphaned wiki pages so\nthat you can re-link or delete them.\n\n\\textbf{Draft Pages}: A list of unpublished pages. Users can edit pages\nand save their changes as drafts. They can come back later to finish\ntheir changes and publish them.\n\nThe current wiki page's content shows in the wiki's main viewing area.\nSeveral features display above the wiki page content, depending on which\nwiki features are enabled and your permissions:\n\n\\textbf{Add Child Page:} Add a wiki page as a child of the current wiki\npage.\n\n\\textbf{Edit:} Edit the wiki page (if you have sufficient permissions).\n\n\\textbf{Details:} View the wiki page's details (if you have sufficient\npermissions). This is explained further in\n\\href{/docs/7-2/user/-/knowledge_base/u/wiki-page-details}{the\ndocumentation on page details}.\n\n\\textbf{Print:} Print the wiki page.\n\nAdditional features appear below the wiki page's content. A view counter\ndisplays the wiki page's view count. Ratings and comments also appear if\nthey're enabled.\n\n\\chapter{Wiki Page Details}\\label{wiki-page-details}\n\nWhen viewing a wiki page, you can view its details by clicking\n\\emph{Details} above the page content. Several tabs appear, to give you\naccess to several categories of information about the page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-page-details-link.png}\n\\caption{Click \\emph{Details} to view the wiki page's details.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-page-details.png}\n\\caption{The wiki page's details.}\n\\end{figure}\n\n\\section{Details}\\label{details-1}\n\nThe Details tab shows page statistics and lets you perform some actions\non the page:\n\n\\textbf{Title}: The page title.\n\n\\textbf{Format}: The page's format (Creole, HTML, MediaWiki, or plain\ntext).\n\n\\textbf{Latest Version}: The page's latest version. The wiki\nautomatically tracks page versions whenever a page is edited.\n\n\\textbf{Created By}: The user who created the page.\n\n\\textbf{Last Changed By}: The user who last modified the page.\n\n\\textbf{Attachments}: The number of attachments to the page.\n\n\\textbf{RSS Subscription}: An icon that opens a new page where you can\nsubscribe to an RSS feed using Live Bookmarks, Yahoo, Microsoft Outlook,\nor an application you can choose from your machine.\n\n\\textbf{Email Subscription}: Links that let you to subscribe to or\nunsubscribe from modifications notifications for the page and the entire\nwiki node.\n\n\\textbf{Advanced Actions}: Links that let you modify the page's\npermissions, make a copy of the page, move (rename) the page, or move\nthe page to the recycle bin.\n\n\\section{History}\\label{history}\n\nThe History tab lets you access the page's activities and versions via\ntabs:\n\n\\textbf{Activities:} Lists actions performed on the page. Each activity\nhas an icon that represents the type of action, the name of the user,\nthe action's description, date, and an \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}) to revert the action or\ncompare its resulting version to that of another action.\n\n\\textbf{Versions:} Lists all the wiki page's versions. You can revert a\npage back to a previous version by selecting \\emph{Revert} from that\nversion's \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}). You can also compare the\ndifferences between versions by selecting two versions and then clicking\nthe \\emph{Compare Versions} button.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-page-history.png}\n\\caption{The Activities tab displays the actions taken on the wiki\npage.}\n\\end{figure}\n\n\\section{Incoming/Outgoing Links}\\label{incomingoutgoing-links}\n\nThe tabs \\emph{Incoming Links} and \\emph{Outgoing Links} list incoming\nand outgoing links, respectively. These are wiki links to and from the\nwiki page. You can use this tab to examine how this page links to other\npages and how other pages link back to this page.\n\n\\section{Attachments}\\label{attachments}\n\nThe \\emph{Attachments} tab lists the name and size of each file attached\nto the page. You can attach any file to the wiki. Images are the most\ncommon type of file attached to a page. Referencing them using the\nproper WikiCreole syntax renders the image inline, which is a nice way\nto include illustrations in your wiki documents.\n\n\\chapter{Other Wiki Widgets}\\label{other-wiki-widgets}\n\nThe widgets that accompany the main Wiki widget help you display and\nnavigate particular wiki nodes. The following widgets are available:\n\n\\textbf{Page Menu:} Displays a single wiki page's outgoing links.\n\n\\textbf{Tree Menu:} Displays a wiki's page hierarchy as a tree.\n\n\\textbf{Wiki Display:} Displays a single wiki node.\n\nYou can find these widgets in the \\emph{Add}\n(\\includegraphics{./images/icon-add-app.png}) → \\emph{Widgets} →\n\\emph{Wiki} menu.\n\n\\section{Page Menu}\\label{page-menu}\n\nThe Page Menu widget displays a wiki page's outgoing links. It answers\nthe question, ``What wiki pages can I access from this page?'' After\nadding the Page Menu widget to a site page, you must set the wiki page\nit displays links from. Follow these steps to do so:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the widget's \\emph{Options} icon\n  (\\includegraphics{./images/icon-app-options.png}) and select\n  \\emph{Configuration}.\n\\item\n  In the configuration's \\emph{Setup} tab, choose the wiki node then\n  click \\emph{Save}.\n\\item\n  Still in the configuration's \\emph{Setup} tab, select the wiki page\n  then click \\emph{Save} and close the configuration dialog.\n\\end{enumerate}\n\nWhen you click a Page Menu link, the site page's Wiki or Wiki Display\nwidget displays the wiki page associated with that link.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-page-menu.png}\n\\caption{The Page Menu widget displays a wiki page's outgoing links.}\n\\end{figure}\n\n\\section{Tree Menu}\\label{tree-menu}\n\nThe Tree Menu widget displays a wiki's page hierarchy as a tree that\nlets you navigate all the wiki's pages. Much like the Page Menu setup,\nyou configure the Tree Menu widget to focus on a wiki node. You can also\nconfigure how deep users can navigate into the page hierarchy. You can\nset the \\emph{Depth} to a value from 1 to 5, or select \\emph{All} to\nallow navigation to all of the wiki node's pages.\n\nFollow these steps to configure the Tree Menu widget after adding it to\na site page:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the widget's \\emph{Options} icon\n  (\\includegraphics{./images/icon-app-options.png}) and select\n  \\emph{Configuration}.\n\\item\n  In the configuration's \\emph{Setup} tab, choose the wiki node then\n  select the depth of wiki pages to display in the hierarchy.\n\\item\n  Click \\emph{Save} and close the configuration dialog.\n\\end{enumerate}\n\nIn the Tree Menu, folder icons represent parent wiki pages and document\nicons represent child wiki pages at the end of the nodes. When you click\na parent wiki page or child wiki page, the Wiki or Wiki Display widgets\non the site page display the respective wiki page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/wiki-tree-menu.png}\n\\caption{The Tree Menu widget displays a wiki node's hierarchy to the\nconfigured depth.}\n\\end{figure}\n\n\\section{Wiki Display}\\label{wiki-display}\n\nThe Wiki Display widget lets you focus user attention on one wiki node.\nAfter adding the widget to a page, follow these steps to configure it:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the widget's \\emph{Options} icon\n  (\\includegraphics{./images/icon-app-options.png}) and select\n  \\emph{Configuration}.\n\\item\n  In the configuration's \\emph{Setup} tab, choose the wiki node then\n  click \\emph{Save}.\n\\item\n  Still in the configuration's \\emph{Setup} tab, select the wiki page\n  then click \\emph{Save} and close the configuration dialog. This page\n  serves as the entry point for the wiki.\n\\end{enumerate}\n\nThe configuration options and user interface for the Wiki Display are\nalmost identical to that of the Wiki widget.\n\n\\chapter{Sending Alerts and\nAnnouncements}\\label{sending-alerts-and-announcements}\n\nYou can use the Alerts and Announcements widgets on Site pages to\nbroadcast important information to users. The Alerts widget is designed\nfor displaying high-priority information (e.g.~planned downtime alerts,\nsecurity alerts, etc.). Each alert is therefore labeled with a red\n\\emph{Important} tag. The Announcements widget displays all other\ninformation you want to broadcast on your site. Each announcement\ntherefore lacks the red tag. To separate important alerts from more\nmundane announcements, you can place the Alerts and Announcements\nwidgets on different pages. However, you can use either widget to\ndisplay any information you wish. Besides the red tag, they function the\nsame. You can also scope your alerts and announcements to specific\ngroups of users.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/alerts-widget.png}\n\\caption{The Alerts widget provides administrators with an easy way to\ncommunicate important information to appropriate groups of users.}\n\\end{figure}\n\nThese widgets have two tabs:\n\n\\textbf{Unread:} Non-expired alerts/announcements that you haven't read.\n\n\\textbf{Read:} Alerts/announcements that have expired, or that you've\nread.\n\nClick an alert/announcement's \\emph{Actions} button\n(\\includegraphics{./images/icon-actions.png}) to edit or delete it.\n\n\\section{Creating Alerts and\nAnnouncements}\\label{creating-alerts-and-announcements}\n\nThere are two places where you can create alerts and announcements:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The \\emph{Announcements and Alerts} app. Access this app at\n  \\emph{Control Panel} → \\emph{Configuration} → \\emph{Announcements and\n  Alerts}. Announcements and alerts are in separate tabs in this app. To\n  begin creating an announcement or alert, select the appropriate tab\n  and then click the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}). This app gives\n  administrators a central location to create announcements and alerts\n  that are then displayed on Site pages by the Announcements and Alerts\n  widgets.\n\\item\n  The Announcements and Alerts widgets, after adding them to a Site page\n  from the \\emph{Add} (\\includegraphics{./images/icon-add-app.png}) →\n  \\emph{Widgets} → \\emph{News} menu. To begin creating an announcement\n  or alert, click the widget's \\emph{Add Alert} or \\emph{Add\n  Announcement} button.\n\\end{enumerate}\n\nRegardless of where you create the alert or announcement, the form for\ncreating it is the same. Follow these steps to complete the form:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Use the \\emph{Title} field to give the alert or announcement a title.\n  Then create your content in the field \\emph{Write your content\n  here\\ldots{}}. For a detailed explanation of the editor, see the\n  \\href{/docs/7-2/user/-/knowledge_base/u/using-the-blog-entry-editor}{Blogs\n  documentation}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/alerts-new-alert.png}\n  \\caption{Enter your alert or announcement's title and content.}\n  \\end{figure}\n\\item\n  Open the \\emph{Configuration} section of the form and set the\n  following options, if desired:\n\n  \\textbf{Distribution Scope:} The scope where the alert/announcement is\n  displayed. The default \\emph{General} scope sends the\n  alert/announcements to everyone. Alternatively, you can select your\n  site or specific roles as the scope.\n\n  \\textbf{URL:} A URL (optional) to include with the alert/announcement.\n  For example, an announcement about a news story could include a link\n  to the news article. The URL must be valid and begin with\n  \\texttt{http://} or \\texttt{https://}.\n\n  \\textbf{Type:} The alert/announcement type. This can be\n  \\emph{General}, \\emph{News}, or \\emph{Test}. Note that each user can\n  specify a different delivery mechanism for each type of\n  alert/announcement. See \\hyperref[user-configuration]{User\n  Configuration} for details.\n\n  \\textbf{Priority:} The announcement's priority. This can be\n  \\emph{Normal} or \\emph{Important}. Note that this is disabled for\n  alerts because alerts are always high priority.\n\n  \\textbf{Display Date:} The display date of the alert/announcement.\n  This determines when the alert/announcement is sent to users and\n  appears in the widget. By default, the \\emph{Display Immediately} box\n  is checked. This sets the display date equal to the creation date.\n  Uncheck this box to enter a custom display date. For example,\n  administrators can create alerts/announcements for display on a later\n  date. This date can be days, weeks, months, or years in the future.\n  Once the \\emph{Display Immediately} box is unchecked, clicking the\n  Display Date field opens the date-picker.\n\n  \\textbf{Expiration Date:} The date and time the alert/announcement\n  expires. Once an alert/announcement expires, the widget displays it in\n  the Read tab. Clicking the Expiration Date field opens the\n  date-picker.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/alerts-new-alert-config.png}\n  \\caption{Configure your new alert or announcement.}\n  \\end{figure}\n\\item\n  Click \\emph{Save} when you're done. Your alert/announcement then\n  appears in the widget.\n\\end{enumerate}\n\n\\section{User Configuration}\\label{user-configuration}\n\nUsers can configure how they'd like to receive announcements.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open your user menu and select \\emph{Account Settings}.\n\\item\n  On the \\emph{Preferences} tab, select \\emph{Alerts and Announcements\n  Delivery}. This shows options for customizing the delivery of alerts\n  and announcements.\n\\item\n  Select a configuration for each type of alert/announcement (General,\n  News, or Test). For each type, you can enable delivery by email and\n  SMS (text message). Note that the \\emph{Website} delivery option is\n  selected and grayed out for each alert type. This means that each\n  alert/announcement is always viewable in its respective widget on a\n  site.\n\\item\n  Click \\emph{Save} when you're finished.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/alerts-delivery.png}\n  \\caption{Each user can choose how they receive alerts and\n  announcements.}\n  \\end{figure}\n\\end{enumerate}\n\n\\section{Alert and Announcement\nRoles}\\label{alert-and-announcement-roles}\n\nYou can also create roles for users to make general announcements. For\ninstance, if you want someone specific to have strict control over\nannouncements, give that person an Announcements Role. Follow these\nsteps to create a simple Announcements Role:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Control Panel} → \\emph{Users} → \\emph{Roles}.\n\\item\n  With the \\emph{Regular Roles} tab selected, click the \\emph{Add}\n  button (\\includegraphics{./images/icon-add.png}). This opens the\n  \\emph{New Role} form.\n\\item\n  Name your Role \\emph{Announcements}, give it a description, and click\n  \\emph{Save}.\n\\item\n  Select the Role's \\emph{Define Permissions} tab then grant these\n  permissions:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    In \\emph{Control Panel} → \\emph{General Permissions}, select\n    \\emph{Add General Announcements}.\n  \\item\n    In \\emph{Site Administration} → \\emph{Applications} →\n    \\emph{Announcements}, select all the resource permissions.\n  \\end{itemize}\n\n  Click \\emph{Save} after selecting each permission. These permissions\n  let the Role add alerts and announcements.\n\\end{enumerate}\n\nNow you have a simple Announcements Role that can manage your site's\ngeneral announcements. Of course, you can adjust this Role's permissions\nas needed.\n\n\\chapter{Managing Notifications and\nRequests}\\label{managing-notifications-and-requests}\n\nIf you subscribed to a blog or message board, or if someone sent you a\nprivate message, invitation, event reminder, or mentioned you in a post,\nyou received a notification or request.\n\nTo access notifications and requests, click your user menu and select\n\\emph{Notifications}. The \\emph{Notifications List} tab is selected by\ndefault. This is where all your notifications appear. Click the\n\\emph{Requests List} tab to view and manage your requests.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/mentions-notification-list.png}\n\\caption{The \\emph{Notifications List} section displays all your\nnotifications in a paginated list.}\n\\end{figure}\n\n\\section{Managing Notifications}\\label{managing-notifications}\n\nNotifications can pile up after some time, especially if you were away\nfor a few days. The Management Bar gives you several ways to filter and\nsort your notifications.\n\nThe \\emph{Filter and Order} menu gives you the following options for\nviewing notifications:\n\n\\textbf{All:} The default option. Displays both read and unread\nnotifications.\n\n\\textbf{Unread:} Displays notifications that haven't been marked as\nread. Unread notifications are indicated with a blue border on the\nleft-hand side of the notification.\n\n\\textbf{Read:} Displays notifications that have been marked as read.\n\n\\textbf{Date:} Order notifications by date.\n\nBy default, notifications are listed by date in descending order. To\nsort notifications by ascending order, click the up/down arrow icon in\nthe management bar. Clicking the button again reverses the sort.\n\nEach notification's \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}) lets you mark the\nnotification as read/unread, or delete the notification.\n\n\\section{Managing Multiple\nNotifications}\\label{managing-multiple-notifications}\n\nYou can also manage multiple notifications at once. Select the checkbox\nnext to notifications you want to manage and choose an option from the\nManagement Bar. Select the checkbox above the notifications list to\nselect all notifications on the current page. The Management Bar shows\nthree actions for selected notifications:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Mark as Read (\\includegraphics{./images/icon-envelope-open.png})\n\\item\n  Mark as Unread (\\includegraphics{./images/icon-envelope-closed.png})\n\\item\n  Delete (\\includegraphics{./images/icon-delete.png})\n\\end{itemize}\n\n\\section{Managing Requests}\\label{managing-requests}\n\nWhen you get a request, it appears in the \\emph{Requests List} tab. In\neach request's \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}), you can click\n\\emph{Confirm} to accept, \\emph{Ignore} to decline, or \\emph{Delete} to\nremove the request.\n\n\\chapter{Using the Knowledge Base}\\label{using-the-knowledge-base}\n\nThe Knowledge Base app can be used to display professional product\ndocumentation or form complete books or guides. It even lets you import\narticle source files written in Markdown. It's\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow}{workflow-enabled}, so\nyou can require articles to be approved before publication.\nAdditionally, you can create article templates that help users follow a\ncommon outline.\n\nHere are the Knowledge Base's key features:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Navigation is built into Knowledge Base Display.\n\\item\n  The suggestions interface enables user feedback on articles.\n\\item\n  Articles are stored in folders.\n\\item\n  Metadata fields exist for the friendly URL, source URL,\n  categorization, and related assets.\n\\item\n  The \\emph{Edit on GitHub} button\n  (\\includegraphics{./images/icon-edit-on-github.png}) can take readers\n  to an article's source repository location (if you choose to use it\n  that way).\n\\item\n  Markdown source files can be imported to create and update articles.\n\\end{itemize}\n\nThe Knowledge Base has several widgets you can add to Site pages:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Knowledge Base Article\n\\item\n  Knowledge Base Display\n\\item\n  Knowledge Base Search\n\\item\n  Knowledge Base Section\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-display.png}\n\\caption{Knowledge Base Display's navigation and viewing provide a great\nreading experience.}\n\\end{figure}\n\n\\chapter{Creating Knowledge Base\nArticles}\\label{creating-knowledge-base-articles}\n\nThe Knowledge Base app in Site Administration contains everything you\nneed to create articles in the Knowledge Base. You can create articles\nby authoring them in the app's WYSIWYG editor or by importing them from\nMarkdown files (\\texttt{.markdown}, \\texttt{.md}) in a ZIP archive. The\nsections below cover both ways of creating articles.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To access Knowledge Base in Site Administration, a Role\nmust have the permission \\emph{Knowledge Base} → \\emph{Access in Site\nAdministration}. To add or act on articles, folders, or suggestions, the\nSite administrator must grant the appropriate permissions using the\nPermissions window in Knowledge Base.\n\n\\noindent\\hrulefill\n\nTo navigate to the Knowledge Base app, open the Menu\n(\\includegraphics{./images/icon-menu.png}) then go to Site\nAdministration (the menu for your Site) → \\emph{Content \\& Data} →\n\\emph{Knowledge Base}. The Knowledge Base app has three tabs:\n\n\\textbf{Articles:} Create and manage articles and folders.\n\n\\textbf{Templates:} Create and manage templates.\n\n\\textbf{Suggestions:} Manage user-submitted feedback for articles.\n\nSelect the \\emph{Articles} tab, then proceed to the sections below for\ninstructions on creating articles.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-admin-articles.png}\n\\caption{The Knowledge Base app in Site Administration lets you create\nKnowledge Base articles.}\n\\end{figure}\n\n\\section{Authoring Articles in the\nEditor}\\label{authoring-articles-in-the-editor}\n\nFollow these steps to create an article in the editor:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the Articles tab, click the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}) and choose \\emph{Basic\n  Article} or the name of an available template. This brings up the New\n  Article form.\n\\item\n  Enter a title for the article. A URL-safe version of the title you\n  provide is added to the end of the article's friendly URL. You can\n  manage the friendly URL in the \\emph{Configuration} section's\n  \\emph{Friendly URL} field.\n\\item\n  Use the WYSIWYG editor to create the article's content. To view or\n  edit the article's HTML source, click the \\emph{Source} button in the\n  editor. The sections below the editor let you add attachments and\n  tags, specify related assets, and set permissions for the article. By\n  default, View permission is granted to the Guest role, meaning anyone\n  can view your article.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/kb-admin-new-article.png}\n  \\caption{You can create and modify a Knowledge Base article's content\n  using the WYSIWYG editor.}\n  \\end{figure}\n\\item\n  Click \\emph{Publish} to submit the article for publication or click\n  \\emph{Save as Draft} to continue working on it later. Note that if\n  you've enabled workflow for the Knowledge Base, your article must be\n  approved before publication.\n\\end{enumerate}\n\nOnce the article is saved, it is converted automatically to HTML for the\nKnowledge Base. Articles are listed in a table in the Articles tab.\n\n\\section{Importing Knowledge Base\nArticles}\\label{importing-knowledge-base-articles}\n\nYou can also create new Knowledge Base articles by importing them from a\nZIP archive that contains articles in the Markdown format\n(\\texttt{.markdown}, \\texttt{.md}). For example, you could write\narticles in your favorite Markdown editor, package them in a ZIP file,\nand then import that ZIP file to create those articles in the Knowledge\nBase. The Knowledge Base can also prioritize articles by their\nfilenames' numerical prefixes. For example, the Knowledge Base would\nlist \\texttt{01-article.markdown} and \\texttt{02-article.markdown} in\nascending order by their numerical prefix (\\texttt{01}, \\texttt{02}).\nFor more information on article priority, see\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-the-knowledge-base\\#managing-knowledge-base-articles}{Managing\nKnowledge Base Articles} For detailed information on the Knowledge Base\nimporter, see the following topics:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/importing-knowledge-base-articles}{Importing\n  Knowledge Base Articles}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/knowledge-base-zip-file-requirements}{Knowledge\n  Base ZIP File Requirements}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/knowledge-base-importer-faqs}{Knowledge\n  Base Importer FAQs}\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To import articles, your Role must have the permission\n\\emph{Knowledge Base} → \\emph{Resource Permissions: Import Articles}.\n\n\\noindent\\hrulefill\n\nFollow these steps to import articles into the Knowledge Base:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the Articles tab, click the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}) and choose \\emph{Import}.\n  This brings up the Import form.\n\\item\n  Click \\emph{Browse} and select the ZIP file that contains the articles\n  you want to import.\n\\item\n  If you want to use the files' numerical prefixes to prioritize the\n  imported articles in the Knowledge Base, select \\emph{Apply numerical\n  prefixes of article files as priorities}.\n\\item\n  Click \\emph{Save} when you're finished.\n\\end{enumerate}\n\nLike all articles, imported articles are automatically converted to HTML\nfor the Knowledge Base and listed in a table with the rest of the\narticles in the Articles tab.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-admin-import.png}\n\\caption{You can import ZIP files that contain Knowledge Base articles\nin Markdown format.}\n\\end{figure}\n\n\\chapter{Managing the Knowledge Base}\\label{managing-the-knowledge-base}\n\nThe Knowledge Base app in Site Administration manages the Knowledge\nBase. To navigate to this app, open the Menu\n(\\includegraphics{./images/icon-menu.png}) then go to Site\nAdministration (the menu for your site) → \\emph{Content \\& Data} →\n\\emph{Knowledge Base}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To access Knowledge Base in Site Administration, a Role\nmust have the permission \\emph{Knowledge Base} → \\emph{Access in Site\nAdministration}. To add or act on articles, folders, or suggestions, the\nsite administrator must grant the appropriate permissions using the\nPermissions window in Knowledge Base.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-admin-articles.png}\n\\caption{You can manage Knowledge Base articles, folders, and\nsuggestions.}\n\\end{figure}\n\n\\section{Setting the Knowledge Base's\nOptions}\\label{setting-the-knowledge-bases-options}\n\nAt the top-right of the Knowledge Base app, the Options menu\n(\\includegraphics{./images/icon-options.png}) contains these options:\n\n\\textbf{Subscribe:} Get notified when Knowledge Base articles are\ncreated, updated, or deleted.\n\n\\textbf{Home Folder Permissions:} Define detailed permissions for the\nKnowledge Base app. You can choose the Roles that can perform the\nfollowing tasks:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Add/delete articles, folders, and templates\n\\item\n  Change the Knowledge Base app's permissions\n\\item\n  Subscribe to articles\n\\item\n  View templates and suggestions\n\\end{itemize}\n\n\\textbf{Export/Import:} Export or import the Knowledge Base app's\nconfiguration.\n\n\\textbf{Configuration:} Configure email notifications for article\nsubscriptions and suggestions. You can also make the Knowledge Base\napp's articles available via RSS (enabled by default), and configure the\nRSS feed's options.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-admin-options.png}\n\\caption{The Knowledge Base App's options.}\n\\end{figure}\n\n\\section{Managing Knowledge Base\nArticles}\\label{managing-knowledge-base-articles}\n\nEach article also has a \\emph{priority} value that determines its\nposition in the\n\\href{/docs/7-2/user/-/knowledge_base/u/knowledge-base-display}{Knowledge\nBase Display widget's navigation}. Each article's priority value appears\nbeneath the article's title. The Knowledge Base Display widget's\nnavigation arranges articles in ascending priority. Priority 1 is the\nhighest priority. The higher an article's priority, the higher it\nappears in the navigation. Articles are assigned the next lowest\npriority by default. This behavior can be changed via\n\\href{/docs/7-2/user/-/knowledge_base/u/knowledge-base-system-settings}{Knowledge\nBase System Settings}.\n\nTo assign articles a new priority value, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select \\emph{Move} from the Actions menu\n  (\\includegraphics{./images/icon-actions.png}) next to the article.\n\\item\n  Enter a new priority value for the article.\n\\item\n  Click \\emph{Move} to apply the new priority.\n\\end{enumerate}\n\nYou can also organize articles with folders. Follow these steps to\ncreate a folder:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the Add button (\\includegraphics{./images/icon-add.png}) and\n  select \\emph{Folder}. This opens a form for creating the new folder.\n\\item\n  Enter a name and an optional description.\n\n  By default, anyone can view the folder. You can manage this setting\n  along with the other permissions in the form's \\emph{Permissions}\n  section.\n\\item\n  Click \\emph{Save}. The folder is then listed in a table in the\n  Articles tab.\n\\end{enumerate}\n\nThe text immediately below the \\emph{Filter and Order} selector at the\ntop of the app shows your position in the folder hierarchy. Click a\nfolder's name in the hierarchy to navigate to it. You can also move\narticles into folders and create child articles. Knowledge Base also\nsupports nested folders.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-admin-folder-hierarchy.png}\n\\caption{This screenshot uses a red box to highlight the text that\nindicates the current position in the folder hierarchy.}\n\\end{figure}\n\nEach folder's Actions menu (\\includegraphics{./images/icon-actions.png})\nlets you perform the following actions on the folder:\n\n\\textbf{Edit:} Change the folder's name and description.\n\n\\textbf{Move:} Relocate the folder under a new parent folder or update\nits priority.\n\n\\textbf{Delete:} Remove the folder and its articles from the Knowledge\nBase.\n\n\\textbf{Permissions:} Grant or revoke the following permissions: add an\narticle to the folder, add a sub-folder to the folder, delete the\nfolder, move the folder, set permissions on the folder, edit (update)\nthe folder, and view the folder.\n\nYou can also delete multiple articles or folders at once. To do this,\nselect the checkbox for each item that you want to delete and click the\n\\emph{Delete} button (\\includegraphics{./images/icon-delete.png}) that\nappears in the Management Bar. You can also see the info for selected\nitems by clicking the \\emph{Info} button\n(\\includegraphics{./images/icon-information.png}) in the Management Bar.\n\n\\chapter{Knowledge Base Templates}\\label{knowledge-base-templates}\n\nTemplates give users a starting point. For example, you can create\ntemplates that contain default headers or other content for articles.\nTemplates help foster consistent formatting and content organization for\narticles. You can create and manage templates from the Knowledge Base\napp in Site Administration. To navigate to this app, open the Menu\n(\\includegraphics{./images/icon-menu.png}) and go to Site Administration\n(the menu for your Site) → \\emph{Content \\& Data} → \\emph{Knowledge\nBase}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To access Knowledge Base in Site Administration, a Role\nmust have the permission \\emph{Knowledge Base} → \\emph{Access in Site\nAdministration}. To add or act on articles, folders, or suggestions, the\nSite administrator must grant the appropriate permissions using the\nPermissions window in Knowledge Base.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-admin-templates.png}\n\\caption{The Knowledge Base app's Templates tab.}\n\\end{figure}\n\n\\section{Creating Templates}\\label{creating-templates}\n\nTo create a new template, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Templates} tab.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n  This brings up the \\emph{New Template} form.\n\\item\n  Enter a title for the template.\n\\item\n  Use the WYSIWYG editor to create the template's content. To view or\n  edit the article's HTML source, click the \\emph{Source} button\n  (\\texttt{\\textless{}/\\textgreater{}}) in the editor. You can also set\n  the template's permissions via the form's \\emph{Permissions} section.\n\\item\n  Click \\emph{Publish} to finish creating the template.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-admin-new-template.png}\n\\caption{The New Template form.}\n\\end{figure}\n\n\\section{Managing Templates}\\label{managing-templates}\n\nEach template appears in a list in the Templates tab. You can take the\nfollowing actions on each template via its Actions button\n(\\includegraphics{./images/icon-actions.png}):\n\n\\textbf{View:} Display the template. From here, you can print the\ntemplate, use it to create an article, edit it, modify its permissions,\nor delete it.\n\n\\textbf{Edit:} Change the template's title and content.\n\n\\textbf{Permissions:} Configure the template's permissions. You can\nchoose whether a Role can change permissions, update, view, or delete\nthe template.\n\n\\textbf{Delete:} Remove the template from the Knowledge Base.\n\n\\chapter{Responding to Knowledge Base\nFeedback}\\label{responding-to-knowledge-base-feedback}\n\nThe Knowledge Base app's \\emph{Suggestions} tab shows user feedback on\narticles and lets you mark progress on addressing the feedback. To\nnavigate to this app, open the Menu\n(\\includegraphics{./images/icon-menu.png}) then go to Site\nAdministration (the menu for your Site) → \\emph{Content \\& Data} →\n\\emph{Knowledge Base}.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To access Knowledge Base in Site Administration, a Role\nmust have the permission \\emph{Knowledge Base} → \\emph{Access in Site\nAdministration}. To add or act on articles, folders, or suggestions, the\nSite administrator must grant the appropriate permissions using the\nPermissions window in Knowledge Base.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-admin-suggestions.png}\n\\caption{The Suggestions tab in Knowledge Base displays each piece of\nfeedback that users leave on Knowledge Base articles.}\n\\end{figure}\n\nEach suggestion provides the link to the associated article, the user's\nfeedback, the user's name, the feedback's time stamp, and the status on\naddressing the suggestion. You can use each entry's Actions menu\n(\\includegraphics{./images/icon-actions.png}) to move the entry between\nthe \\emph{New}, \\emph{In Progress}, and \\emph{Resolved} states.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To view article suggestions, your Role must have the\npermission \\emph{Knowledge Base} → \\emph{Knowledge Base: View\nSuggestions}.\n\nTo move suggestions between the \\emph{New}, \\emph{In Progress}, and\n\\emph{Resolved} states, your Role must have the permission\n\\emph{Knowledge Base} → \\emph{Knowledge Base Article: Update}. Roles\nassigned this permission can also view and update the state of article\nsuggestions from any of the other Knowledge Base widgets.\n\n\\noindent\\hrulefill\n\nWhen you move the suggestion to a different state, an email is sent\nnotifying the user of the change. You can view and configure the\nautomated emails from the Knowledge Base app's \\emph{Options}\n(\\includegraphics{./images/icon-options.png}) → \\emph{Configuration}\nmenu.\n\n\\chapter{Knowledge Base Display}\\label{knowledge-base-display}\n\nYou can use the Knowledge Base Display widget to display your published\nKnowledge Base articles. You can customize how this widget displays\narticles, and which ones it displays. To get started, add the widget to\nthe Site page you want to display articles on:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the page and open the \\emph{Add} menu\n  (\\includegraphics{./images/icon-add-app.png}).\n\\item\n  Open the \\emph{Widgets} → \\emph{Content Management} section, then add\n  \\emph{Knowledge Base Display} to the page.\n\\end{enumerate}\n\nBy default, the Knowledge Base Display widget displays articles from the\nKnowledge Base's Home folder. To change the location of its articles,\nfollow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the widget's \\emph{Options} icon\n  (\\includegraphics{./images/icon-app-options.png}) and select\n  \\emph{Configuration}. This opens the Configuration dialog.\n\\item\n  In the \\emph{Setup} tab, select the \\emph{General} tab then click\n  \\emph{Select} in the \\emph{Article or Folder} field. This brings up\n  the \\emph{Select Entry} form.\n\\item\n  Click \\emph{Choose} next to the article or folder of articles you want\n  to display. Alternatively, you can click the \\emph{Choose This Folder}\n  button at the top of the Select Entry form to select the current\n  folder.\n\\item\n  Click \\emph{Save} and close the Configuration dialog.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-display-config-article.png}\n\\caption{Select the article or folder of articles that the Knowledge\nBase Display widget displays.}\n\\end{figure}\n\nThe Knowledge Base Display widget's Options icon\n(\\includegraphics{./images/icon-app-options.png}) also provides these\ncommon configuration options:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Look and Feel Configuration\n\\item\n  Export/Import\n\\item\n  Permissions\n\\item\n  Configuration Templates\n\\end{itemize}\n\nFor more information on these, see the section on configuring widgets in\n\\href{/docs/7-2/user/-/knowledge_base/u/web-experience-management}{Web\nExperience Management}.\n\nThe Knowledge Base Display's navigation menu and display options make it\nthe perfect candidate for a full page widget. If you display a folder of\narticles, the navigation on the left side of the widget displays links\nto all the folder's articles. The viewing area on the right side of the\nwidget displays the folder's leading article (the \\emph{priority one}\narticle). Click an article in the navigation to display it in the\nviewing area. The currently displayed article's link appears in bold in\nthe navigation. You can also move between articles by clicking the links\nwith arrows at the bottom of the widget.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-display.png}\n\\caption{Knowledge Base Display's navigation and viewing provide a great\nreading experience.}\n\\end{figure}\n\nKnowledge Base Display can also show article hierarchies. Viewing an\narticle that has child articles expands the navigation tree to show\nlinks to the child articles. Any expanded nodes collapse when you view a\ndifferent top level article.\n\nThe links at the top of the widget allow users to perform the following\nactions on an article:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Subscribe to an RSS feed of the Knowledge Base\n\\item\n  Subscribe to the current article\n\\item\n  View the current article's history\n\\item\n  Print the current article\n\\end{itemize}\n\nAdministrators have access to an additional set of links at the top of\nthe widget that lets them perform the following actions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Edit the article\n\\item\n  Add a child article\n\\item\n  Set the article's permissions\n\\item\n  Move the article\n\\item\n  Delete the article\n\\end{itemize}\n\nBelow the article's content is the rating interface, showing thumbs\nup/down icons. Users can also submit suggestions or comments below the\narticle in the text box labeled \\emph{Do you have any suggestions?}.\nAdministrators can\n\\href{/docs/7-2/user/-/knowledge_base/u/responding-to-knowledge-base-feedback}{view\nthe suggestions and mark progress on them}.\n\nIf the administrator enables the Knowledge Base app's source URL feature\n(more on this in a moment) and an article has an assigned source URL, an\n\\emph{Edit on GitHub} button\n(\\includegraphics{./images/icon-edit-on-github.png}) appears to the\nright of the article's title. This button lets users access the\narticle's source in GitHub. You can use this feature to encourage users\nto contribute fixes or improvements to articles. If you're interested in\nthis feature, you can direct your administrator to follow the\ninstructions in\n\\href{/docs/7-2/user/-/knowledge_base/u/knowledge-base-system-settings}{Knowledge\nBase System Settings}.\n\n\\section{Displaying Different Article\nSets}\\label{displaying-different-article-sets}\n\nAs an administrator, say that you've used folders to aggregate similar\narticles, and you want to provide an easy way for users to switch\nbetween these sets of articles. The Knowledge Base Display's content\nfolder feature adds a selector to the top of the navigation that lets\nusers switch between article sets.\n\nFollow these steps to set up content folders:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a folder in the Knowledge Base app in Site Administration. Then\n  create sub-folders in this folder. These sub-folders are the\n  \\emph{content folders}.\n\\item\n  Add articles to each content folder.\n\\item\n  Select \\emph{Configuration} from Knowledge Base Display's\n  \\emph{Options} menu (\\includegraphics{./images/icon-app-options.png}).\n  In the \\emph{Setup} → \\emph{General} tab, select the content folders'\n  parent folder and click \\emph{Save}.\n\\end{enumerate}\n\nThe content selector's values reflect the names of your content folders.\nSelect one to view its articles.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-display-content-selector.png}\n\\caption{Knowledge Base Display's content folder feature lets users\nswitch between different sets of articles.}\n\\end{figure}\n\nYou can also add a common prefix to the names shown in the selector:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select \\emph{Configuration} from Knowledge Base Display's\n  \\emph{Options} menu (\\includegraphics{./images/icon-app-options.png}).\n  In the Configuration dialog, select the \\emph{Setup} → \\emph{Display\n  Settings} tab.\n\\item\n  Enter the prefix into the \\emph{Content Root Prefix} field and click\n  \\emph{Save}.\n\\end{enumerate}\n\n\\chapter{Other Knowledge Base\nWidgets}\\label{other-knowledge-base-widgets}\n\nThere are other Knowledge Base widgets you can add to Site pages besides\n\\href{/docs/7-2/user/-/knowledge_base/u/knowledge-base-display}{Knowledge\nBase Display}:\n\n\\textbf{Knowledge Base Article:} Display a single article's content.\n\n\\textbf{Knowledge Base Section:} Publish articles associated with a\nspecific topic (section).\n\n\\textbf{Knowledge Base Search:} Search for articles.\n\nYou can add these widgets from \\emph{Add}\n(\\includegraphics{./images/icon-add-app.png}) → \\emph{Widgets} →\n\\emph{Content Management}.\n\n\\section{Knowledge Base Article}\\label{knowledge-base-article}\n\nKnowledge Base Article displays a single article's content. It even\nshows abstracts of child articles. You can add multiple Knowledge Base\nArticle instances to a page, and each one can show a different article.\n\nAfter adding Knowledge Base Article to a page, follow these steps to\nconfigure the widget:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click \\emph{Please configure this portlet to make it visible to all\n  users}. This opens the Configuration dialog.\n\\item\n  In the \\emph{Setup} → \\emph{General} tab, click \\emph{Select}, choose\n  an article, click \\emph{Save}, and close the Configuration dialog.\n\\end{enumerate}\n\nYou can change your selection from the widget's \\emph{Options}\n(\\includegraphics{./images/icon-app-options.png}) → \\emph{Configuration}\nmenu.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-article.png}\n\\caption{The Knowledge Base Article app is great at displaying\nindividual articles.}\n\\end{figure}\n\nKnowledge Base Article shares the same UI as the Knowledge Base Display\nto display and manage its articles. Refer to the\n\\href{/docs/7-2/user/-/knowledge_base/u/knowledge-base-display}{Knowledge\nBase Display documentation} for a detailed description of the widget's\nUI.\n\n\\section{Knowledge Base Section}\\label{knowledge-base-section}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} as of Knowledge Base 3.0.0, the Knowledge Base Sections\nwidget is deprecated and replaced by\n\\href{/docs/7-2/user/-/knowledge_base/u/organizing-content-with-tags-and-categories}{categories}.\n\n\\noindent\\hrulefill\n\nThe Knowledge Base Section widget lets you publish articles associated\nwith a specific topic (section). For example, a news Site might have the\nsections \\emph{World}, \\emph{Politics}, \\emph{Business}, and\n\\emph{Entertainment}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-section.png}\n\\caption{The Knowledge Base Section widget.}\n\\end{figure}\n\nTo use sections, an administrator must first configure the feature in\nSystem Settings, creating the section names for use in the Knowledge\nBase Section widget. This process is covered in detail in\n\\href{/docs/7-2/user/-/knowledge_base/u/knowledge-base-system-settings}{Knowledge\nBase System Settings}. When creating or editing a Knowledge Base\narticle, authors can then select the article's section in the\n\\emph{Configuration} → \\emph{Section} field.\n\nYou can add multiple instances of the Knowledge Base Section widget to a\npage. Each widget can display articles from any number of sections. You\ncan configure the widget to display article titles or abstracts. You can\nalso define whether to show pagination or section titles.\n\nFollow these steps to configure an instance of the Knowledge Base\nSection widget:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select \\emph{Configuration} from the Knowledge Base Section widget's\n  \\emph{Options} menu (\\includegraphics{./images/icon-app-options.png}).\n  This opens the widget's Configuration dialog.\n\\item\n  In the Configuration dialog's \\emph{Setup} → \\emph{General} tab,\n  select the section or sections that you want to use and click\n  \\emph{Save}.\n\\item\n  Close the Configuration dialog to see the updates.\n\\end{enumerate}\n\nThe matching articles are displayed in the app beneath their section\nheading.\n\n\\section{Knowledge Base Search}\\label{knowledge-base-search}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} as of Knowledge Base 3.0.0, the Knowledge Base Search\nwidget is deprecated and replaced by Liferay Search.\n\n\\noindent\\hrulefill\n\nEven though the Knowledge Base can show the structure of its articles,\nit may be difficult to find exactly what you're looking for by browsing.\nThat's where the Knowledge Base Search widget comes in.\n\nEnter your search term and press the \\emph{Search} button. The results\nare displayed in a table with the following criteria for each matching\narticle:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Title\n\\item\n  Author\n\\item\n  Create date\n\\item\n  Modified date\n\\item\n  Number of views\n\\end{itemize}\n\nYou can select the criteria to display in the widget's \\emph{Options}\n(\\includegraphics{./images/icon-app-options.png}) \\emph{Configuration}\ndialog.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-search.png}\n\\caption{The Knowledge Base Search widget lets you search the Knowledge\nBase for keywords.}\n\\end{figure}\n\n\\chapter{Importing Knowledge Base\nArticles}\\label{importing-knowledge-base-articles-1}\n\nAs mentioned earlier, the Knowledge Base app can import articles in\nbulk. This lets you have an offline process where articles are prepared\nahead of time before they're published. Articles are imported into the\nKnowledge Base as \\href{http://commonmark.org}{Markdown} files. Markdown\nis a text-only file format that is easy to read, yet supports all the\nthings you'd need to do to format your articles.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} To import articles, your Role must be granted the\n\\emph{Knowledge Base} → \\emph{Resource Permissions: Import Articles}\npermission.\n\n\\noindent\\hrulefill\n\nThe Knowledge Base supports a Markdown dialect known as\n\\href{http://fletcher.github.io/MultiMarkdown-4/}{Multi-Markdown}. This\ndialect extends the original Markdown with features like table\nformatting, image captions, and footnotes.\n\nFor the Knowledge Base to import your Markdown articles, they must\nadhere to these requirements:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  All source files must use the \\texttt{.markdown} or \\texttt{.md}\n  extensions.\n\\item\n  Articles must start with a top-level header (e.g.,\n  \\texttt{\\#\\ Some\\ Heading\\ ...}).\n\\item\n  Each header must have an associated, unique ID for the article's\n  friendly URL title and for anchor tags in the article's sub headers.\n  Here's an example of a top-level header that correctly specifies an\n  ID:\n\\end{itemize}\n\n\\texttt{\\#\\ Some\\ Heading\\ \\ {[}{]}(id=some-heading)}\n\nHere's Markdown source text for a simple example article:\n\n\\begin{verbatim}\n# The Moons of Mars [](id=the-moons-of-mars)\n\nAs you look up from your chaise lounge, you're sure to see our neighboring\nplanet Mars. Did you know that Mars has two moons? You might have to break \nout a pair of binoculars to see them.\n\nIts two moons are aptly named after the two sons of mythical Roman god Mars.\nTheir names are Phobos and Deimos. \n\\end{verbatim}\n\nIn the first line above, notice the header's ID assignment\n\\texttt{id=the-moons-of-mars}. On import, the ID value becomes the\nKnowledge Base article's URL title.\n\nMarkdown is something of a standard: there's\n\\href{https://help.github.com/articles/github-flavored-markdown}{Github\nFlavored Markdown}, a proposed \\href{http://www.commonmark.org}{common\nMarkdown syntax}, forums that support Markdown (reddit, StackExchange,\nand others), Markdown editors, and an\n\\href{https://tools.ietf.org/html/rfc7763}{IETF draft} for making it an\nofficial Internet media type (text/markdown). Why is there so much\ninterest in Markdown?\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  It's readable. Even if you don't know Markdown, you can read it\n  without having to filter out the syntax.\n\\item\n  It gets out of a writer's way. You don't have to worry about mousing\n  to various icons to change text into a heading or create bulleted\n  lists. Just start typing. The syntax is very intuitive.\n\\item\n  There are tools to convert it to many other formats, though it was\n  designed to convert to HTML. If your articles are in Markdown, it's\n  straightforward to publish them to the web, mobile formats (Kindle,\n  ePub), and print.\n\\item\n  Since it's only text, you can use existing tools to collaborate on\n  that text. Using services like GitHub, people can contribute to your\n  articles, and you can see all the changes that have been made to them.\n\\end{enumerate}\n\n\\chapter{Knowledge Base ZIP File\nRequirements}\\label{knowledge-base-zip-file-requirements}\n\nThe Knowledge Base importer supports article hierarchies, so Markdown\nfiles can be specified anywhere in the ZIP file's directory structure.\nThey can be nested in any number of folders. Image files are the only\nfiles supported for attachments.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Imported articles are independent of the workflow\nsettings. This means that \\textbf{imported articles are automatically\napproved.}\n\nOnly users with the \\emph{Import Articles} permission assigned to their\nRole are able to import articles. This permission can be assigned\nmanually through \\emph{Control Panel} → \\emph{Users} → \\emph{Roles}. If\nyou've upgraded from Liferay Portal 6.2, you can also assign this Role\nto every Role that was already able to add articles with a command from\nthe Gogo shell.\n\nOpen the\n\\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Gogo\nshell}. Type \\texttt{knowledgeBase:addImportArticlePermissions} and hit\nenter.\n\n\\noindent\\hrulefill\n\nThe ZIP file's articles are imported in file order (alphanumerically).\nTo designate an article's priority, add a numeric prefix to its file\nname. For example, the priorities for articles named\n\\texttt{01-file.markdown} and \\texttt{02-file.markdown} become\n\\texttt{1.0} and \\texttt{2.0}.\n\nTo designate an article to be the parent of all other articles in the\nsame source folder, end its file name with \\texttt{-intro.markdown}.\nThis creates a parent-child hierarchy. You can use the prefix\n\\texttt{00} for parent articles to place them at the top of the folder's\nfile order. The importer uses the numeric prefix of an intro file's\nfolder as its article priority.\n\nHere's the underlying logic for the \\texttt{00} prefix:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  A file prefix of \\texttt{00} for a non-intro file assigns the\n  resulting article's priority to \\texttt{1.0}.\n\\item\n  A file prefix of \\texttt{00} for a top-level intro file sets the\n  article's priority to the first folder numeric prefix found that is\n  \\texttt{1.0} or greater.\n\\end{itemize}\n\nThis convention lets you specify priorities for top-level (non-child)\narticles in your hierarchy.\n\nWhen importing, keep the checkbox labeled \\emph{Apply numerical prefixes\nof article files as priorities} selected. If a file doesn't have a\nprefix, its article gets the next available priority (the highest\ncurrent priority, plus one).\n\nBelow is an example ZIP file structure that demonstrates the features\nmentioned so far:\n\n\\textbf{ZIP File Structure Example:}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{01-winter-events/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{00-winter-excursions-intro.markdown}\n  \\item\n    \\texttt{01-star-dust-snow-shoeing.markdown}\n  \\item\n    \\texttt{02-lunar-alpine.markdown}\n  \\end{itemize}\n\\item\n  \\texttt{02-summer-events/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{00-summer-excursions-intro.markdown}\n  \\item\n    \\texttt{01-lunar-rock-scrambling.markdown}\n  \\item\n    \\texttt{02-extra-terrestrial-mountain-biking.markdown}\n  \\item\n    \\texttt{03-lunar-olympics/}\n\n    \\begin{itemize}\n    \\tightlist\n    \\item\n      \\texttt{00-lunar-olympics-intro.markdown}\n    \\item\n      \\texttt{01-zero-gravity-diving.markdown}\n    \\end{itemize}\n  \\end{itemize}\n\\item\n  \\texttt{images/}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\texttt{some-image.png}\n  \\item\n    \\texttt{another-image.jpeg}\n  \\end{itemize}\n\\end{itemize}\n\nThe above ZIP file specifies\n\\texttt{00-winter-excursions-intro.markdown} as the parent of its\nneighboring Markdown files: \\texttt{01-star-dust-snow-shoeing.markdown}\nand \\texttt{02-lunar-alpine.markdown}. Likewise,\n\\texttt{00-lunar-olympics-intro.markdown} is the parent of\n\\texttt{01-zero-gravity-diving.markdown}.\n\\texttt{00-lunar-olympics-intro.markdown} is also the peer of\n\\texttt{01-lunar-rock-scrambling.markdown} and\n\\texttt{02-extra-terrestrial-mountain-biking.markdown}, and the child of\n\\texttt{00-summer-excursions-intro.markdown}.\n\n\\textbf{ZIP Example's Resulting Relationships and Priorities}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{01-winter-events/00-winter-excursions-intro.markdown}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Article:} Winter Excursions\n  \\item\n    \\textbf{Relationship:} Peer of \\emph{Summer Excursions}\n  \\item\n    \\textbf{Priority:} \\textbf{1.0}\n  \\end{itemize}\n\\item\n  \\texttt{01-winter-events/01-star-dust-snow-shoeing.markdown}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Article:} Star Dust Snow Shoeing\n  \\item\n    \\textbf{Relationship:} Child of \\emph{Winter Excursions}\n  \\item\n    \\textbf{Priority:} 1.0\n  \\end{itemize}\n\\item\n  \\texttt{01-winter-events/02-lunar-alpine.markdown}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Article:} Lunar Alpine\n  \\item\n    \\textbf{Relationship:} Child of \\emph{Winter Excursions}\n  \\item\n    \\textbf{Priority:} 2.0\n  \\end{itemize}\n\\item\n  \\texttt{02-summer-events/00-summer-excursions-intro.markdown}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Article:} Summer Excursions\n  \\item\n    \\textbf{Relationship:} Peer of \\emph{Winter Excursions}\n  \\item\n    \\textbf{Priority:} \\textbf{2.0}\n  \\end{itemize}\n\\item\n  \\texttt{02-summer-events/01-lunar-rock-scrambling.markdown}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Article:} Lunar Rock Scrambling\n  \\item\n    \\textbf{Relationship:} Child of \\emph{Summer Excursions}\n  \\item\n    \\textbf{Priority:} 1.0\n  \\end{itemize}\n\\item\n  \\texttt{02-summer-events/02-extra-terrestrial-mountain-biking.markdown}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Article:} Extra Terrestrial Mountain Biking\n  \\item\n    \\textbf{Relationship:} Child of \\emph{Summer Excursions}\n  \\item\n    \\textbf{Priority:} 2.0\n  \\end{itemize}\n\\item\n  \\texttt{02-summer-events/03-summer-olympics/00-lunar-olympics-intro.markdown}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Article:} Lunar Olympics\n  \\item\n    \\textbf{Relationship:} Child of \\emph{Summer Excursions}\n  \\item\n    \\textbf{Priority:} 3.0\n  \\end{itemize}\n\\item\n  \\texttt{02-summer-events/03-summer-olympics/01-zero-gravity-diving.markdown}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Article:} Zero Gravity Diving\n  \\item\n    \\textbf{Relationship:} Grandchild of \\emph{Summer Excursions}\n  \\item\n    \\textbf{Relationship:} Child of \\emph{Opening Ceremonies}\n  \\item\n    \\textbf{Priority:} 1.0\n  \\end{itemize}\n\\end{itemize}\n\nZIP files must meet the following requirements:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Each ZIP file must end in the suffix \\texttt{.zip}.\n\\item\n  Each ZIP file must contain at least one Markdown source file,\n  optionally organized in folders.\n\\item\n  All referenced image files must be in a folder named \\texttt{images}\n  in the ZIP file's root.\n\\item\n  Image files must be in a supported format and must use the appropriate\n  file extensions. Supported extensions are\n  \\texttt{.bmp},\\texttt{.gif},\\texttt{.jpeg},\\texttt{.jpg}, and\n  \\texttt{.png}. They're specified via an app system setting. For\n  details, see\n  \\href{/docs/7-2/user/-/knowledge_base/u/knowledge-base-system-settings}{Knowledge\n  Base System Settings}.\n\\end{itemize}\n\nOnce you have your article ZIP file, it's time to import it.\n\nFollow these steps to import your ZIP file:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the Menu (\\includegraphics{./images/icon-menu.png}), navigate to\n  \\emph{Site Administration} (the menu for your site) → \\emph{Content} →\n  \\emph{Knowledge Base} → \\emph{Articles}.\n\\item\n  Click \\emph{Add} (\\includegraphics{./images/icon-add.png}) →\n  \\emph{Import} to bring up the importer page.\n\\item\n  Browse to the location of your file, and in most cases leave the\n  checkbox for the article priorities checked, and then click\n  \\emph{Save}.\n\\end{enumerate}\n\nYour file is uploaded, and the importer converts each source file's\nMarkdown text to HTML, applying the HTML to the resulting article. Any\nimage files that are referenced in an article and included in the ZIP\nfile are imported as attachments to the article.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-admin-import.png}\n\\caption{Selecting \\emph{Add} → \\emph{Import} in Knowledge Base brings\nup the interface for selecting a ZIP file of Markdown source files and\nimages to produce and update articles in your Knowledge Base.}\n\\end{figure}\n\nIn addition to source files and images, you can configure a base source\nURL system setting for the importer that specifies your source file's\nrepository location. Each article's \\emph{Edit on GitHub} button (if\nenabled) takes the user to the source location. The importer prefixes\neach file's path with the base source URL. This constructs a URL to the\narticle's repository source location; it looks like\n\\texttt{{[}base\\ URL{]}/{[}article\\ file\\ path{]}}. Here's an example\nbase source URL:\n\n\\begin{verbatim}\nhttps://github.com/liferay/liferay-docs/blob/master/develop/tutorials\n\\end{verbatim}\n\nThe source URL constructed from this base URL and article source file\n\\texttt{folder-1/some-article.markdown} would be:\n\n\\begin{verbatim}\nhttps://github.com/liferay/liferay-docs/blob/master/develop/tutorials/folder-1/some-article.markdown\n\\end{verbatim}\n\nYou specify the base source URL in a file called \\texttt{.METADATA} in\nthe ZIP file's root folder. The importer treats the \\texttt{.METADATA}\nfile as a standard Java properties file and uses the base source URL to\nconstruct the source URL for all of the ZIP file's resulting articles.\n\nTo use the source URL feature, your administrator must enable it via the\n\\href{/docs/7-2/user/-/knowledge_base/u/knowledge-base-system-settings}{Knowledge\nBase System Settings}.\n\n\\chapter{Knowledge Base Importer\nFAQs}\\label{knowledge-base-importer-faqs}\n\n\\begin{itemize}\n\\item\n  \\textbf{What happens when I import an existing article?}\n\n  The importer checks if the source file's leading header ID (e.g.,\n  \\texttt{\\#\\ Some\\ Heading\\ \\ {[}{]}(id=some-heading)}) matches the URL\n  title of any existing article in the Knowledge Base folder. If a match\n  is found, the importer replaces the article's content with the\n  incoming content converted from the source file. If no match is found,\n  a new article is created.\n\\item\n  \\textbf{Do I need to import all of a Knowledge Base folder's articles,\n  even if I only want to create a new article or update a subset of the\n  folder's current articles?}\n\n  No.~You can import as many or as few new or modified articles as you\n  like.\n\\item\n  \\textbf{Does the importer remove articles?}\n\n  No.~The importer only creates and updates articles. It doesn't delete\n  any existing articles. To delete an article, you must manually do so\n  via the Knowledge Base app.\n\\item\n  \\textbf{Can I update an article's priority?}\n\n  Yes. You can use the file/folder prefix convention and re-import the\n  article to update its priority.\n\\item\n  \\textbf{If I change an article's title, should I also change its\n  header ID?}\n\n  It depends on whether you've already published your article. If it\n  hasn't been published, then there are no public links to it, so it's\n  fine to change the header ID. If the article is already published, you\n  must decide whether it's worth breaking existing links to the article,\n  and worth having search engines rediscover and re-rank your article\n  based on its new friendly URL. The new friendly URL is based on the\n  new header ID.\n\\end{itemize}\n\n\\chapter{Knowledge Base System\nSettings}\\label{knowledge-base-system-settings}\n\nAdministrators can use the System Settings UI to set the Knowledge\nBase's global configuration (across sites). You can access this UI in\n\\emph{Control Panel} → \\emph{Configuration} → \\emph{System Settings} →\n\\emph{Knowledge Base}. There are five sections of Knowledge Base\nconfiguration settings:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Service\n\\item\n  Knowledge Base Article\n\\item\n  Knowledge Base Display\n\\item\n  Knowledge Base Search\n\\item\n  Knowledge Base Section\n\\end{itemize}\n\nThe \\emph{Service} section's settings apply defaults to all the\nKnowledge Base widgets, and to the Knowledge Base app in Site\nAdministration. The other sections apply to specific Knowledge Base\nwidgets and override the \\emph{Service} defaults.\n\n\\noindent\\hrulefill\n\n\\textbf{Important:} Advanced configuration of the Knowledge Base\napplication's system settings should only be performed by an Liferay DXP\nadministrator.\n\n\\noindent\\hrulefill\n\nThe Knowledge Base has several optional features that are disabled by\ndefault, but can be enabled and configured from System Settings. These\ninclude source URL, import file conventions, new article priority\nincrement, and sections.\n\n\\section{Source URL Settings}\\label{source-url-settings}\n\nThe source URL settings define the source location of Markdown files for\nimport. This should point to a source repository where the files are\nstored. GitHub is assumed as the default. Once defined, the Knowledge\nBase displays a button (default label is \\emph{Edit on GitHub}) above\neach displayed article. Users can click the button to navigate to an\narticle's source location.\n\nThe source URL settings are accessible in the \\emph{Service} section of\nthe Knowledge Base's System Settings. To enable the source URL, check\nthe \\emph{Source URL Enabled} checkbox.\n\nTo change the source URL button's label, specify a new value for the\nsetting \\emph{Source URL Edit Message Key}. Best practice is to specify\nthe value as a\n\\href{/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys}{language\nkey}. For example, if you create a language key\n\\texttt{edit-on-bitbucket=Edit\\ on\\ Bitbucket}, you can specify that\nlanguage key as the button's new label:\n\n\\begin{verbatim}\nedit-on-bitbucket\n\\end{verbatim}\n\nAlternatively, you can specify the label explicitly:\n\n\\begin{verbatim}\nEdit on Bitbucket\n\\end{verbatim}\n\n\\section{Importer File Convention\nSettings}\\label{importer-file-convention-settings}\n\nThese settings define the supported file extensions, the suffix for\nparent files, and the image folder's path within the imported ZIP files.\nThese settings are accessible in the \\emph{Service} section of the\nKnowledge Base's System Settings.\n\nThe following settings specify the importer's supported file extensions:\n\n\\textbf{Markdown Importer article extensions:} Sets the supported\narticle extensions. The default values are \\texttt{.markdown} and\n\\texttt{.md}.\n\n\\textbf{Markdown Importer Image File Extensions:} Sets the supported\nimage file extensions. The default values are \\texttt{.bmp},\n\\texttt{.gif}, \\texttt{.jpeg}, \\texttt{.jpg}, and \\texttt{.png}.\n\nFollow these steps to modify the supported file extensions:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{+} or \\emph{-} button next to the setting to add or\n  remove a supported file extension, respectively.\n\\item\n  If adding an extension, enter a new value.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nThese settings define additional article configuration options for the\nimporter:\n\n\\textbf{Markdown Importer Article Intro:} Sets the parent article's file\nsuffix. The default value is \\texttt{intro.markdown}.\n\n\\textbf{Markdown Importer Image Folder:} Sets the image folder path the\nimporter looks for in the ZIP file. The default path is\n\\texttt{/images}.\n\n\\textbf{Article Increment Priority Enabled:} Whether to increment new\narticle priorities by \\texttt{1.0}. To disable this increment so that\narticles get a flat value of \\texttt{1.0}, deselect the checkbox.\n\nAlternatively, you can enable or disable the article increment priority\nfeature for each widget in the corresponding widget's configuration menu\nin System Settings.\n\n\\section{Section Names Setting}\\label{section-names-setting}\n\nThe section names setting lets you specify names of arbitrary topics to\nattribute to articles. Using the Knowledge Base Section widget, you can\ndisplay one or more \\emph{sections} (groups) of articles. To use\nsections, you must first define them in the System Settings for the\n\\emph{Knowledge Base Section} widget.\n\nFollow these steps to make new sections available:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the \\emph{Knowledge Base Section} configuration menu.\n\\item\n  Click the plus button next to the \\emph{Admin Knowledge Base Article\n  Sections} setting to add a new field for each section you want.\n\\item\n  Enter a name for each new section and click \\emph{Update}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/kb-section-setting.png}\n\\caption{Create the sections you want to use with the Knowledge Base\nSection widget.}\n\\end{figure}\n\nOnce your sections are added, you can follow the steps in the\n\\href{/docs/7-2/user/-/knowledge_base/u/other-knowledge-base-widgets\\#knowledge-base-section}{Knowledge\nBase Section documentation} to learn how to use them.\n\n\\chapter{Inviting Members to Your\nSite}\\label{inviting-members-to-your-site}\n\nThe Invite Members widget lets site administrators send invitations to\njoin the Site. You can add this widget to a page from the menu\n\\emph{Add} (\\includegraphics{./images/icon-add-app.png}) →\n\\emph{Widgets} → \\emph{Collaboration}. Click the \\emph{Invite members}\nbutton to bring up the interface for sending invitations.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/invite-members-dialog.png}\n\\caption{You can invite users by clicking the add sign next to the\nuser's name.}\n\\end{figure}\n\nClick the plus sign next to a User or click the \\emph{Add Email Address}\nbutton to add a User to the invite list. Users that have already been\ninvited but have not yet responded appear with a check mark next to\ntheir names. You can also invite Users to the \\emph{Site Owner},\n\\emph{Site Content Reviewer}, and \\emph{Site Administrator} Roles for\nyour site by selecting that Role under the \\emph{Invite to Role}\nheading. Once you've added all the Users you want to invite and have\nselected their Roles, click the \\emph{Send Invitations} button to invite\nthem. For more information on roles, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{Roles and\nPermissions documentation}.\n\nThe Site invitation shows up under the \\emph{Requests List} tab on the\nUser's \\emph{Notifications} page. The User can then choose to\n\\emph{Confirm} or \\emph{Ignore} the invitation.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/invite-members-confirm.png}\n\\caption{You can confirm or ignore the invitation.}\n\\end{figure}\n\nWhen Users confirm such invitations, they become Site members assigned\nto the Roles you defined.\n\n\\chapter{Assets}\\label{assets}\n\nWhen you think of assets, you probably think of things like houses,\ncars, money, or gold bricks. Although Liferay DXP is capable of holding\nmany assets, they aren't financial assets. Assets in Liferay DXP are any\nkind of content. For example, images, documents, and web content are all\nassets. And although such assets may not have the tangible value of a\ngold brick, they could equal or even exceed that value when used to\nsupport an organization's day-to-day business activities. You might even\nthink of Liferay DXP as your very own Fort Knox (don't worry, we won't\ntell anyone).\n\nHere, you'll learn to configure Liferay DXP to apply\n\\href{/docs/7-2/user/-/knowledge_base/u/tagging-content}{tags} to assets\nautomatically.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auto-tagging-images.png}\n\\caption{The tags \\emph{freight car} and \\emph{electric locomotive} were\nautomatically applied to this image.}\n\\end{figure}\n\n\\chapter{Configuring Asset Auto\nTagging}\\label{configuring-asset-auto-tagging}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/tagging-content}{Tagging assets}\nis a great way to organize content. Typically, the content creator\napplies tags while creating the content. It's also possible, however, to\ntag content automatically. For example, Liferay DXP can scan an image on\nupload and apply tags that describe the image's content. This lets you\nleverage tags without requiring content creators to apply them manually.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Auto-tagging currently works only for images, text-based\ndocuments, text-based web content, and blog entries.\n\n\\noindent\\hrulefill\n\nHere, you'll learn how to configure asset auto-tagging in general. This\nis required prior to configuring auto-tagging for specific asset types,\nwhich is documented separately:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/auto-tagging-images}{Auto\n  Tagging Images}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/auto-tagging-text}{Auto\n  Tagging Text}\n\\end{itemize}\n\n\\section{Configuration Levels}\\label{configuration-levels}\n\nAuto-tagging is enabled by default. You can configure it at three\nlevels:\n\n\\textbf{Global (System):} For auto-tagging to function on any level, it\nmust be enabled globally. You can also set the default auto-tagging\nconfiguration for every portal instance.\n\n\\textbf{Instance:} When enabled globally, auto-tagging is also enabled\nby default for each portal instance. However, you can override the\nglobal auto-tagging configuration on a per-instance basis.\n\n\\textbf{Site:} When enabled for an instance, auto-tagging is also\nenabled by default for all that instance's sites. You can disable it for\nspecific sites.\n\n\\section{Global Configuration}\\label{global-configuration-1}\n\nFollow these steps to configure auto tagging globally:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Assets}.\n\\item\n  Under \\emph{SYSTEM SCOPE}, select \\emph{Asset Auto Tagging}.\n\\item\n  The following settings are available:\n\n  \\textbf{Enable Auto Tagging of Assets:} Whether asset auto tagging is\n  enabled.\n\n  \\textbf{Maximum Number of Tags:} The maximum number of tags that can\n  be automatically applied to each asset. The default value of\n  \\texttt{0} means that there is no limit.\n\\item\n  Click \\emph{Save} to save your changes.\n\\end{enumerate}\n\nTo set the default auto-tagging configuration for all instances, select\n\\emph{Asset Auto Tagging} under \\emph{VIRTUAL INSTANCE SCOPE}. The\navailable settings are exactly the same as those in the SYSTEM SCOPE.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auto-tagging-global.png}\n\\caption{You can configure auto tagging globally in the Assets section\nof System Settings.}\n\\end{figure}\n\n\\section{Instance-level\nConfiguration}\\label{instance-level-configuration}\n\nWhen enabled globally, auto-tagging is also enabled by default for each\ninstance. You can, however, disable or configure it for each instance.\n\nFollow these steps to configure auto tagging on the instance level:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{Instance\n  Settings} → \\emph{Assets}.\n\\item\n  Under \\emph{VIRTUAL INSTANCE SCOPE}, select \\emph{Asset Auto Tagging}.\n\\item\n  The settings here are identical to those in the global configuration,\n  but apply only to the current instance.\n\\item\n  Click \\emph{Save} to save your changes.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auto-tagging-instance.png}\n\\caption{You can also configure auto tagging for each instance.}\n\\end{figure}\n\n\\section{Site-level Configuration}\\label{site-level-configuration}\n\nWhen enabled for an instance, auto-tagging is also enabled by default\nfor all that instance's sites. You can, however, enable or disable it\nfor each site.\n\nFollow these steps to configure auto tagging for a site:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the \\emph{Menu} (\\includegraphics{./images/icon-menu.png}), click\n  your site's name, and navigate to \\emph{Configuration} →\n  \\emph{Settings}.\n\\item\n  In the \\emph{General} tab, expand the \\emph{Asset Auto Tagging}\n  section. Use the toggle to enable or disable auto tagging for the\n  site.\n\\item\n  Click \\emph{Save} to save your changes.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auto-tagging-site.png}\n\\caption{You can enable or disable auto-tagging for a site.}\n\\end{figure}\n\n\\chapter{Auto Tagging Images}\\label{auto-tagging-images}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/tagging-content}{Tags} help you\nfind and organize files, including images. With\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-asset-auto-tagging}{asset\nauto tagging enabled}, you can also enable image auto tagging. Image\nauto tagging automatically tags images uploaded to the Documents and\nMedia Library. This lets you use tags without requiring anyone to apply\nthem manually.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Currently, tags applied automatically are English only.\n\n\\noindent\\hrulefill\n\nImage auto tagging is disabled by default. To use it, you must do two\nthings:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Ensure that\n  \\href{/docs/7-2/user/-/knowledge_base/u/configuring-asset-auto-tagging}{asset\n  auto tagging} is enabled. Although it's enabled by default,\n  administrators can disable it.\n\\item\n  Ensure that an image auto tagging provider is enabled. These providers\n  are disabled by default. Here, you'll learn how to enable/disable\n  them.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Prior to Liferay DXP Fix Pack 1 and Liferay Portal CE\nGA2, you must configure the providers shown here in \\emph{Documents and\nMedia} instead of \\emph{Assets} (in System/Instance Settings).\n\n\\noindent\\hrulefill\n\nThree such providers are available:\n\n\\hyperref[configuring-tensorflow-image-auto-tagging]{\\textbf{TensorFlow:}}\nAn open-source library that provides machine learning capabilities.\nTensorFlow image auto-tagging in Liferay DXP is based on\n\\href{https://github.com/tensorflow/tensorflow/blob/master/tensorflow/java/src/main/java/org/tensorflow/examples/LabelImage.java}{TensorFlow's\n\\texttt{LabelImage} sample for Java}, and uses the Inception5h model.\nUse this with caution, since its accuracy is limited.\n\n\\hyperref[configuring-google-cloud-vision]{\\textbf{Google Cloud\nVision:}} Uses the \\href{https://cloud.google.com/vision/}{Google Cloud\nVision API} to automatically tag images.\n\n\\hyperref[configuring-microsoft-cognitive-services]{\\textbf{Microsoft\nCognitive Services:}} Uses\n\\href{https://azure.microsoft.com/en-us/services/cognitive-services/}{Microsoft\nCognitive Services} to automatically tag images.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auto-tagging-images.png}\n\\caption{The tags \\emph{freight car} and \\emph{electric locomotive} were\nautomatically applied to this image.}\n\\end{figure}\n\n\\section{Configuring TensorFlow Image Auto\nTagging}\\label{configuring-tensorflow-image-auto-tagging}\n\nFollow these steps to configure TensorFlow Image Auto Tagging:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Assets}.\n\\item\n  Under \\emph{VIRTUAL INSTANCE SCOPE}, select \\emph{TensorFlow Image\n  Auto Tagging}. The following settings are available:\n\n  \\textbf{Enable TensorFlow Image Auto Tagging:} Check this box to\n  select whether image auto-tagging is enabled by default in any\n  instance that has asset auto tagging enabled. Note that you can\n  override this value for specific instances, as the next section shows.\n\n  \\textbf{Confidence Threshold:} TensorFlow assigns a confidence level\n  between 0 and 1 for each tag, where 1 is the highest confidence and 0\n  is the lowest. This field sets the minimum confidence level that\n  TensorFlow needs to apply a tag. Higher values yield fewer tags\n  because TensorFlow needs more confidence before it applies a tag.\n  Likewise, lower values yield more tags.\n\\item\n  Click \\emph{Save} to save your changes.\n\\end{enumerate}\n\nYou can override these settings for each instance from \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{Instance Settings} →\n\\emph{Assets}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auto-tagging-tensorflow.png}\n\\caption{Configure TensorFlow image auto-tagging for your portal\ninstances.}\n\\end{figure}\n\nTo optimize performance, you can also control the process that runs\nTensorFlow image auto tagging:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Assets}.\n\\item\n  Under \\emph{SYSTEM SCOPE}, select \\emph{TensorFlow Image Auto Tagging\n  Process}. The following settings are available:\n\n  \\textbf{Maximum Number of Relaunches:} The maximum number of times the\n  process is allowed to crash before it is permanently disabled.\n\n  \\textbf{Maximum Number of Relaunches Time Interval:} The time in\n  seconds after which the counter is reset.\n\\item\n  Click \\emph{Save} to save your changes.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auto-tagging-tensorflow-process.png}\n\\caption{You can fine tune the process that runs the TensorFlow image\nauto tagging in the portal.}\n\\end{figure}\n\n\\section{Configuring Google Cloud\nVision}\\label{configuring-google-cloud-vision}\n\nFollow these steps to configure Google Cloud Vision image auto tagging:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Assets}.\n\\item\n  Under \\emph{VIRTUAL INSTANCE SCOPE}, select \\emph{Google Cloud Vision\n  Image Auto Tagging}. The following settings are available:\n\n  \\textbf{API Key:} The API key to use for the Google Cloud Vision API.\n  For more information, see\n  \\href{https://cloud.google.com/docs/authentication/api-keys}{Google's\n  documentation on API keys}.\n\n  \\textbf{Enabled:} Whether Google Cloud Vision image auto tagging is\n  enabled.\n\\item\n  Click \\emph{Save} to save your changes.\n\\end{enumerate}\n\nYou can override these settings for each instance from \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{Instance Settings} →\n\\emph{Assets}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auto-tagging-image-google.png}\n\\caption{The Google Cloud Vision provider requires an API key.}\n\\end{figure}\n\n\\section{Configuring Microsoft Cognitive\nServices}\\label{configuring-microsoft-cognitive-services}\n\nFollow these steps to configure Microsoft Cognitive Services image auto\ntagging:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Assets}.\n\\item\n  Under \\emph{VIRTUAL INSTANCE SCOPE}, select \\emph{Microsoft Cognitive\n  Services Image Auto Tagging}. The following settings are available:\n\n  \\textbf{API Key:} Your\n  \\href{https://azure.microsoft.com/en-us/try/cognitive-services/my-apis/?apiSlug=computer-services}{API\n  key} for the Computer Vision API V2.\n\n  \\textbf{API Endpoint:} The endpoint for the Computer Vision API V2\n  (e.g.,\n  \\texttt{https://westcentralus.api.cognitive.microsoft.com/vision/v2.0}).\n\n  \\textbf{Enabled:} Whether Microsoft Cognitive Services image auto\n  tagging is enabled.\n\n  For more information, see the\n  \\href{https://docs.microsoft.com/en-us/azure/cognitive-services/}{Microsoft\n  Cognitive Services documentation}.\n\\item\n  Click \\emph{Save} to save your changes.\n\\end{enumerate}\n\nYou can override these settings for each instance from \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{Instance Settings} →\n\\emph{Assets}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auto-tagging-image-microsoft.png}\n\\caption{The Microsoft Cognitive Services provider requires an API key\nand an endpoint.}\n\\end{figure}\n\n\\chapter{Auto Tagging Text}\\label{auto-tagging-text}\n\nWith\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-asset-auto-tagging}{asset\nauto tagging enabled}, you can also configure text auto tagging. Text\nauto tagging automatically\n\\href{/docs/7-2/user/-/knowledge_base/u/tagging-content}{tags}\ntext-based assets. This lets you use tags without requiring anyone to\nmanually apply them.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Currently, text auto tagging is only available for\ntext-based documents, text-based web content, and blog entries. Tags\napplied automatically are English only.\n\n\\noindent\\hrulefill\n\nText auto tagging is disabled by default. To use it, you must enable it:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Ensure that\n  \\href{/docs/7-2/user/-/knowledge_base/u/configuring-asset-auto-tagging}{asset\n  auto tagging} is enabled. Although it's enabled by default,\n  administrators can disable it.\n\\item\n  Ensure that a text auto tagging provider is configured and enabled for\n  the asset types you want to auto tag. You'll learn how to do this\n  here. Note that these providers aren't configured or enabled by\n  default.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Prior to Liferay DXP Fix Pack 1 and Liferay Portal CE\nGA2, you must enable these providers separately for each content type in\nSystem/Instance Settings. For example, you must enable text auto tagging\nfor documents and web content in \\emph{Documents and Media} and\n\\emph{Web Content}, respectively.\n\n\\noindent\\hrulefill\n\nThere are two text auto-tagging providers in the portal:\n\n\\hyperref[configuring-google-cloud-natural-language-text-auto-tagging]{\\textbf{Google\nCloud Natural Language Text Auto Tagging:}} Uses the\n\\href{https://cloud.google.com/natural-language/}{Google Cloud Natural\nLanguage API} to analyze and automatically tag portal content.\n\n\\hyperref[configuring-opennlp-text-auto-tagging]{\\textbf{OpenNLP Text\nAuto Tagging:}} Uses the open source\n\\href{https://opennlp.apache.org/}{Apache OpenNLP} library to analyze\nand automatically tag portal content. Three models are used: location\nname finder, organization finder, and person name finder. Use this\nprovider with caution, as its accuracy may be limited.\n\n\\section{Configuring Google Cloud Natural Language Text Auto\nTagging}\\label{configuring-google-cloud-natural-language-text-auto-tagging}\n\nFollow these steps to configure the auto-tagging provider for the Google\nCloud Natural Language API:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Assets}.\n\\item\n  Under \\emph{VIRTUAL INSTANCE SCOPE}, select \\emph{Google Cloud Natural\n  Language Text Auto Tagging}. The following settings are available:\n\n  \\textbf{API Key:} The API key to use for the Google Cloud Natural\n  Language API. For more information, see\n  \\href{https://cloud.google.com/docs/authentication/api-keys}{Google's\n  documentation on API keys}.\n\n  \\textbf{Classification Endpoint Enabled:} Whether to enable auto\n  tagging of text using the Google Cloud Natural Language API\n  Classification endpoint.\n\n  \\textbf{Confidence:} Set the classifier's confidence of the category.\n  This number represents how certain the classifier is that this\n  category represents the given text.\n\n  \\textbf{Entity Endpoint Enabled:} Whether to enable auto tagging of\n  text using the Google Cloud Natural Language API Entity endpoint.\n\n  \\textbf{Salience:} The salience score for an entity provides\n  information about the importance or centrality of that entity to the\n  entire text.\n\n  \\textbf{Enable Google Cloud Natural Language Text Auto Tagging For:}\n  The asset types to enable text auto tagging for. Use the menu to\n  select \\emph{Document}, \\emph{Blogs Entry}, or \\emph{Web Content\n  Article}. To add multiple asset types, click the \\emph{Add} icon\n  (\\includegraphics{./images/icon-add.png}) and select the asset type\n  from the menu. You can delete any additional asset types by clicking\n  the Trash icon (\\includegraphics{./images/icon-trash-list.png}).\n\\item\n  Click \\emph{Save} to save your changes.\n\\end{enumerate}\n\nYou can override these settings for each instance from \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{Instance Settings} →\n\\emph{Assets}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auto-tagging-text-google.png}\n\\caption{Configure Google Cloud Natural Language text auto tagging for\nyour portal instances.}\n\\end{figure}\n\n\\section{Configuring OpenNLP Text Auto\nTagging}\\label{configuring-opennlp-text-auto-tagging}\n\nFollow these steps to configure the OpenNLP Text Auto Tagging provider:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Assets}.\n\\item\n  Under \\emph{VIRTUAL INSTANCE SCOPE}, select \\emph{OpenNLP Text Auto\n  Tagging}. The following settings are available:\n\n  \\textbf{Confidence Threshold:} Set the minimum confidence threshold\n  (from 0 to 1, where 1 is the highest confidence) above which tags will\n  be applied. Higher values yield fewer tags because the provider needs\n  more confidence before it applies a tag. Likewise, lower values yield\n  more tags.\n\n  \\textbf{Enable OpenNLP Text Auto Tagging For:} The asset types to\n  enable text auto tagging for. Use the menu to select \\emph{Document},\n  \\emph{Blogs Entry}, or \\emph{Web Content Article}. To add multiple\n  asset types, click the \\emph{Add} icon\n  (\\includegraphics{./images/icon-add.png}) and select the asset type\n  from the menu. You can delete any additional asset types by clicking\n  the Trash icon (\\includegraphics{./images/icon-trash-list.png}).\n\\item\n  Click \\emph{Save} to save your changes.\n\\end{enumerate}\n\nYou can override these settings for each instance from \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{Instance Settings} →\n\\emph{Assets}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/auto-tagging-text-open-nlp.png}\n\\caption{Configure OpenNLP text auto tagging for your portal instances.}\n\\end{figure}\n\n\\chapter{Creating A Social Network}\\label{creating-a-social-network}\n\nLiferay DXP contains several features and widgets for leveraging its\nsocial framework. The Activities widget lets you broadcast user\nactivities on a Site. This is a good way for Site members to see what's\ngoing on in their communities. When placed on a user's private\nDashboard, the Activities widget displays the activities of that user's\nsocial connections. Users can make those social connections via the\nContacts Center app, which lets them establish connections and followers\nthroughout the portal. What's more, widgets can be exported as\nOpenSocial gadgets and/or used with Facebook.\n\nThis guide shows you how to do these things, and more.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/activities-widget.png}\n\\caption{The Activities widget shows information about asset-related\nuser activity in the current Site.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/connection-request.png}\n\\caption{Users get a notification that lets them respond to connection\nrequests.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/contacts-center.png}\n\\caption{The Contacts Center widget lets users make connections.}\n\\end{figure}\n\n\\chapter{Using the Activities Widget}\\label{using-the-activities-widget}\n\nThe core social widget is Activities. It displays information about user\nactivity on the Site where you added it. User activities tracked include\nupdates to the Documents and Media library, blog posts, message boards\nposts, wiki pages, and bookmarks. Liferay DXP also tracks information\nabout web content but only displays this information if the logged-in\nuser is a Site administrator. This widget provides a summary of recent\nSite activity. You can use it on a Site's public or private pages to\nshow what Site members have been up to, or you can use it on the public\nor private pages of a user's personal Site. When added to a personal\nSite, the Activities widget shows the activities of only that user.\n\nAdd the Activities widget to a page from the \\emph{Add}\n(\\includegraphics{./images/icon-add-app.png}) → \\emph{Widgets} →\n\\emph{Social} menu.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/activities-widget.png}\n\\caption{The Activities widget shows information about asset-related\nuser activity in the current Site.}\n\\end{figure}\n\nNote that the widget provides links to the assets listed in the feed.\nThese links don't work, however, unless there's a way to display the\nassets on the page. For example, if you click a link to a blog post in\nthe Activities widget, that page must have a Blogs widget to display\nthat blog post.\n\n\\chapter{Connecting Users}\\label{connecting-users}\n\nBy adding the Contacts Center and Activities widgets to users' private\nDashboards, administrators can let users form social connections. The\nContacts Center widget lets users form these connection types:\n\n\\textbf{Connection:} A two-way relationship formed by a user accepting a\nconnection request from another user. The Activities widget on each\nuser's private Dashboard displays the activities of the other user.\n\n\\textbf{Following:} A one-way relationship in which one user follows\nanother user. The followed user's activities appear in the Activities\nwidget on the follower's private Dashboard.\n\nTo ensure that all users have a page on their private Dashboard that\ncontains the Contacts Center and Activities widgets, you can create a\nuser group and then create a page via a Site Template. For instructions\non this, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/user-groups}{user groups\ndocumentation}. The widgets are in \\emph{Add}\n(\\includegraphics{./images/icon-add.png}) → \\emph{Widgets} →\n\\emph{Social}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/contacts-center.png}\n\\caption{The Contacts Center widget lets users make connections.}\n\\end{figure}\n\nWhen you select a user in the Contacts Center, these buttons appear\nacross the top of the widget:\n\n\\textbf{Connect:} Send a connection request to the user.\n\n\\textbf{Disconnect:} Disconnect from the user.\n\n\\textbf{Follow:} Follow the user.\n\n\\textbf{Unfollow:} Stop following the user.\n\n\\textbf{Block:} Block the user. Blocking a user only prevents that user\nfrom following you or adding you as a connection. A blocked user can\nstill send messages to and view the public profile information of the\nblocking user.\n\n\\textbf{Unblock:} Stop blocking the user.\n\n\\textbf{vCard:} Export the user's vCard and save it as a VCF file. vCard\nis a file format standard for electronic business cards.\n\nWhen you send a connection request, the user is notified and can confirm\nor ignore the request.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/connection-request.png}\n\\caption{Users get a notification that lets them respond to connection\nrequests.}\n\\end{figure}\n\n\\chapter{Exporting Widgets To Other\nWebsites}\\label{exporting-widgets-to-other-websites}\n\nYou can publish its widgets to other websites via OpenSocial. This lets\nyou provide your widget or content in the context of another website.\nRead on to find out how this is done.\n\n\\section{Sharing OpenSocial Gadgets}\\label{sharing-opensocial-gadgets}\n\nOpenSocial consists of a set of APIs for social networking. It may be\nbeneficial for you to share widgets from your server with other sites,\nsuch as \\href{http://ighome.com}{ighome.com} or\n\\href{http://igoogleportal.com}{igoogleportal.com}. These sites let\nusers customize their own pages with gadgets. Users can share Liferay\nDXP widgets on any OpenSocial-compatible site. Liferay DXP does this by\nsharing the widget's URL with the OpenSocial platform. This URL is\nunique to the user's specific widget instance. Users can therefore share\nmultiple instances of the same widget as different OpenSocial gadgets.\n\nFollow these steps to share a widget with OpenSocial:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the widget you want to share to a site page.\n\\item\n  Click the widget's \\emph{Options} icon\n  (\\includegraphics{./images/icon-app-options.png}) and select\n  \\emph{Configuration}.\n\\item\n  In the \\emph{Sharing} tab, open the \\emph{OpenSocial Gadget} section.\n\\item\n  Set the selector to \\emph{YES} for \\emph{Allow users to add {[}widget\n  name{]} to an OpenSocial platform}. In the \\emph{OpenSocial Gadget\n  URL} field, replace \\texttt{localhost:8080} with the name of your\n  public domain and port.\n\\item\n  Click \\emph{Save}.\n\\item\n  Close the dialog and click the widget's \\emph{Options} icon\n  (\\includegraphics{./images/icon-app-options.png}). There's a new\n  option available named \\emph{Add to an OpenSocial Platform}. Select\n  this option to add the widget to a page on an OpenSocial platform.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/open-social-sharing.png}\n\\caption{You can share widgets via OpenSocial.}\n\\end{figure}\n\n\\chapter{Integrating with Facebook}\\label{integrating-with-facebook}\n\nLiferay DXP provides tools for integrating your portal and its content\nwith Facebook. For example, you can use Facebook for authentication and\neven export widgets as Facebook applications. This article shows you\nhow.\n\n\\section{Facebook Sign On}\\label{facebook-sign-on}\n\nLike many websites you may visit, any site running on Liferay DXP can\nuse Facebook for sign in. This makes it easier for users to sign in to\nyour Site, since they don't need to remember another user name and\npassword.\n\n\\section{Using Your Widget as Facebook\nApplications}\\label{using-your-widget-as-facebook-applications}\n\nYou can add any Liferay DXP widget as an app on Facebook. To do this,\nyou must first get a developer key. A link for doing this is provided to\nyou in the Facebook tab in any widget's Configuration screen. You must\ncreate the app on Facebook and get the key and canvas page URL from\nFacebook. You can then copy and paste their values into the Facebook\ntab. Once you do that, your widget is available on Facebook.\n\nThis integration lets you make things like Message Boards, Calendars,\nWikis, and other content on your website available to a much larger\naudience (unless you already have a billion users on your site, in which\ncase, kudos to you). If you're a developer, you can implement your\nwidget on Liferay DXP and then publish it to Facebook.\n\n\\chapter{Using Social Bookmarks}\\label{using-social-bookmarks}\n\nSocial bookmarks appear below content as buttons for sharing that\ncontent on social networks. For example, social bookmarks appear by\ndefault in the Blogs widget below each blog post. For more information\non configuring social bookmarks in the Blogs widget, see the\ndocumentation on\n\\href{/docs/7-2/user/-/knowledge_base/u/displaying-blogs}{displaying\nblogs}.\n\nThese social bookmarks are available by default:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Twitter\n\\item\n  Facebook\n\\item\n  LinkedIn\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/social-bookmarks-menu.png}\n\\caption{The default social bookmarks appear in a menu below content.}\n\\end{figure}\n\nYou can install the Social Bookmarks app from Liferay Marketplace. This\napp is available for\n\\href{https://web.liferay.com/marketplace/-/mp/application/15194315}{Liferay\nCE Portal} and\n\\href{https://web.liferay.com/marketplace/-/mp/application/15188453}{Liferay\nDXP}. It adds the following social bookmarks:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  AddThis\n\\item\n  Delicious\n\\item\n  Digg\n\\item\n  Evernote\n\\item\n  Reddit\n\\item\n  Slashdot\n\\end{itemize}\n\nIf you need help installing apps from Liferay Marketplace, see\n\\href{/docs/7-2/user/-/knowledge_base/u/using-the-liferay-marketplace}{Using\nthe Liferay Marketplace}.\n\n\\chapter{Search}\\label{search}\n\nSites often feature lots of content split over lots of asset types. Web\ncontent articles, documents and media files, and blogs entries are just\na few examples. Most content types are \\emph{assets}. Under the hood,\nassets use the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/asset-framework}{Asset\nAPI} and have an Indexer class. Any content that has these features can\nbe searched.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-assets.png}\n\\caption{The Type Facet configuration lists the searchable\nout-of-the-box asset types.}\n\\end{figure}\n\n\\section{Elasticsearch}\\label{elasticsearch}\n\nThe default search engine is Elasticsearch, which is backed by the\nLucene search library. There's an Elasticsearch server embedded in all\nbundles, which is handy for testing and development purposes. Production\nenvironments must install a separate, remote Elasticsearch server (or\neven better, cluster of servers). For information on how to install\nElasticsearch, read the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/elasticsearch}{deployment\nguide}.\n\n\\section{Search Features}\\label{search-features}\n\nSearching is simple and straightforward. Find a search bar (there's one\nembedded in every page by default), enter a term, and click\n\\emph{Enter}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-bar.png}\n\\caption{There's a search bar embedded on all pages by default.}\n\\end{figure}\n\nAfter search is triggered, a results page appears. If there are hits to\nsearch engine documents, they appear as search results in the right hand\ncolumn. In the left hand column are search facets.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-results.png}\n\\caption{Results are displayed in the Search Results portlet.}\n\\end{figure}\n\nThe search bar, search results, and search facets make up three powerful\nfeatures in the search UI.\n\n\\section{Search Bar}\\label{search-bar}\n\nThe search bar is simple: it's where you enter \\emph{search terms}.\nSearch terms are the text you send to the search engine to match against\nthe documents in the index.\n\n\\section{Search Results and\nRelevance}\\label{search-results-and-relevance}\n\nThe search term is processed by an algorithm in the search engine, and\nsearch results are returned to users in order of relevance. Relevance is\ndetermined by a document's \\emph{score}, generated against the search\nquery. The higher the score, the more relevant a document is considered.\nThe particular relevance algorithm used is dependent on\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/current/relevance-intro.html\\#relevance-intro}{algorithms\nprovided by the search engine (Elasticsearch by default)}.\n\n\\section{Search Facets}\\label{search-facets}\n\nFacets allow users of the Search application to filter search results.\nThink of facets as buckets that hold similar search results. You might\nwant to see the results in all the buckets, but after scanning the\nresults, you might decide that the results of just one bucket better\nrepresent what you want. So what facets are included out of the box?\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Category\n\\item\n  Folder\n\\item\n  Site\n\\item\n  Tag\n\\item\n  Type\n\\item\n  User\n\\item\n  Modified\n\\item\n  Custom\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-faceted-search.png}\n\\caption{\\emph{Site} and \\emph{Type} are two of the facet sets you'll\nencounter. They let you drill down to results that contain the search\nterms you entered.}\n\\end{figure}\n\nYou've probably used something similar on any number of sites. You\nsearch for an item, are presented with a list of results, and a list of\nbuckets you can click to further drill down into the search results,\nwithout entering additional search terms. Search facets work the same\nway. Facets are, of course,\n\\href{/docs/7-2/user/-/knowledge_base/u/facets}{configurable}.\n\n\\chapter{What's New in Search}\\label{whats-new-in-search}\n\nLots of new and improved search capabilities are present in 7.0, from\nnew widgets to new APIs and infrastructure.\n\n\\section{New and Improved Widgets}\\label{new-and-improved-widgets}\n\nAdd search widgets by clicking the Add\n(\\includegraphics{./images/icon-add-widget.png}) icon on the page. Then\nexpand the Widgets → Search section.\n\n\\section{Custom Filter}\\label{custom-filter}\n\n\\textbf{New Widget}\n\nAdd a widget to the page for each of the filters you'd like applied to\nthe search results. Let search page users see and manipulate the filters\nor make them invisible and/or immutable (this is just a cool word for\n``they can't be changed'').\n\nFor example, add a custom filter to ensure that all returned results\nhave the keyword \\emph{street} in the content field.\n\n\\section{Sort}\\label{sort}\n\n\\textbf{New Widget}\n\nThe Sort widget reorders the results based on the value of certain\n\\texttt{keyword} fields in the index. For example, show results in\nalphabetic order of the Title field. The default order is determined by\nthe search engine's \\emph{Relevance} calculation.\n\nAdd more fields to the sort widget if the default options aren't enough.\nClick the widget Options\n(\\includegraphics{./images/icon-app-options.png}) menu → Configuration.\nEnter a human readable label and the \\texttt{fieldName} to sort by. Just\nmake sure it's a \\texttt{keyword} field.\n\n\\section{Search Insights}\\label{search-insights}\n\n\\textbf{Improved}\n\nPast versions of Search Insights showed you the full query string sent\nto the search engine, but now it also displays the response from the\nsearch engine with an explanation of the score for each search hit.\n\n\\section{New Search Admin\nFunctionality}\\label{new-search-admin-functionality}\n\nThe Search Admin functionality is found in Control Panel → Configuration\n→ Search.\n\n\\section{Search Engine Info}\\label{search-engine-info}\n\nThe displayed Search Engine information is enhanced, showing the client\nand node information as well as the vendor and operation mode.\n\n\\section{Field Mappings}\\label{field-mappings}\n\nThe Field Mappings tab shows the field mappings for all indexes in the\nsearch engine.\n\n\\section{Indexing Progress}\\label{indexing-progress}\n\nIndexing now displays a progress bar so you can see in the UI when the\nre-indexing action has completed.\n\n\\section{New System Settings}\\label{new-system-settings}\n\nAccess the Search System Settings at Control Panel → Configuration →\nSystem Settings → Search.\n\nThere's a new entry in the search category: \\emph{Title Field Query\nBuilder}. Use it to configure how search responds to matches on the\nTitle field of a document.\n\n\\textbf{Exact Match boost:} Give an additional boost when searched\nkeywords exactly match the \\texttt{title} field of a document.\n\n\\textbf{Maximum Expansions:} Limit the number of documents to return\nwhen matching searched keywords to the \\texttt{title} field as a phrase\nprefix. See Elasticsearch's\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-match-query-phrase.html}{Match\nPhrase Query documentation} for more information.\n\n\\section{New Infrastructure}\\label{new-infrastructure}\n\nThere are some important search infrastructure changes to know about.\n\n\\section{Elasticsearch Support}\\label{elasticsearch-support}\n\n7.0 supports Elasticsearch 6.5.x through 6.8.x, and 6.5.0 is included as\nthe embedded version to use for testing out-of-the-box search behavior.\nSee the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/elasticsearch}{deployment\nguide} for more information.\n\n\\section{Application-Specific\nIndexes}\\label{application-specific-indexes}\n\nYou'll notice more search indexes in 7.0. That's because you can now\nconfigure application-specific indexes. At the time of this writing, the\nadditional indexes are all related to the DXP Workflow Metrics feature.\nMore will likely appear in future versions, and third party developers\ncan use the \\texttt{portal-search} APIs to create their own indexes.\nIt's under development, so visit the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/search}{Search Framework\ndocumentation} frequently to discover new search infrastructure changes\nthat expose more functionality for developers.\n\n\\section{API Enhancements}\\label{api-enhancements}\n\nEnhancements to the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/search}{search framework}\nAPIs include\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Low level indexing and queries\n\\item\n  Operations directly on indexed documents (no need for the Indexer\n  framework)\n\\item\n  New Aggregation types\n\\end{itemize}\n\n\\section{Multi-Language Search}\\label{multi-language-search}\n\nLiferay DXP's support for multi-language search took a step forward,\nwith improvements to Documents and Media and Web Content. More\nimprovements are necessary in this area and will be prioritized in\nfuture releases. See the\n\\href{/docs/7-2/user/-/knowledge_base/u/searching-for-localized-content}{Multi-Language\nSearch article for more information}.\n\n\\chapter{Configuring Search Pages}\\label{configuring-search-pages}\n\nThere are multiple ways to skin the search cat (disclaimer: no actual\ncats were harmed during the writing of this article).\n\nIf you're unsure which approach to take, use the\n\\hyperref[default-search-pages]{default} configuration. It provides a\nsensible starting point that can be modified later, as needed.\n\nIf you've been using Liferay DXP for a long time and like the search\nexperience you've always used, use the\n\\hyperref[legacy-search-experience]{legacy approach}.\n\nIf you're in need of a fully customized experience,\n\\hyperref[manual-search-page-configuration]{manually configure} the\nsearch experience.\n\nAfter choosing your approach and reading here to get it up and running,\nfind the articles on the\n\\href{/docs/7-2/user/-/knowledge_base/u/searching-for-assets\\#search-bar}{Search\nBar}, \\href{/docs/7-2/user/-/knowledge_base/u/facets}{Search Facets},\nand \\href{/docs/7-2/user/-/knowledge_base/u/search-results}{Search\nResults} to understand the full suite of configuration options.\n\nSearch display pages are where users go to enter search terms and browse\nsearch results.\n\n\\section{Search Page Templates}\\label{search-page-templates}\n\nThe default search page is backed by a Search Page Template, and\nmanually configured search pages can use the template, too. The template\ncan be used in two ways:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Enable inheriting changes to the template, if you want the search page\n  to get any updates made to the template at a later date.\n\\item\n  Create the page based on the template, but independently configured\n  after the initial creation.\n\\end{enumerate}\n\nOut of the box, the Search Page Template includes these widgets:\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/searching-for-assets\\#search-bar}{Search\n  Bar}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/searching-for-assets\\#search-suggestions}{Suggestions}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/search-results}{Search\n  Results}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/configuring-search\\#widget-scoped-search-configuration}{Search\n  Options}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/site-facet}{Site Facet}: This\n  widget is hidden unless the Search Bar is configured to search the\n  \\emph{Everything} scope and results from multiple sites are returned.\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/type-facet}{Type Facet}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/tag-and-category-facets}{Tag\n  Facet}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/tag-and-category-facets}{Category\n  Facet}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/folder-facet}{Folder Facet}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/user-facet}{User Facet}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/modified-facet}{Modified\n  Facet}\n\\end{itemize}\n\nOut of the box, widgets use the \\emph{Barebone} Application Decorators:\nunless there's content to render in the widget, the widget body is\nhidden. The header is displayed if you hover over it.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-barebone-widgets.png}\n\\caption{At first glance, not much is happening on the search page. But,\nthere's more than meets the eye.}\n\\end{figure}\n\nBecause of this, when you visit a search page created from the default\nsearch page template, you won't see certain widgets fully rendered.\n\nBy contrast, when you add a search widget to a page manually, they use\nthe \\emph{Borderless} decorator (by default), which shows more of the\nwidget even when there is no content to display.\n\n\\section{Default Search Pages}\\label{default-search-pages}\n\nUsing the default site and the default theme with the default search\nsettings, the out-of-the-box search experience has two components for\nend users:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  A search bar embedded on each page.\n\\item\n  A default search page where search requests are routed.\n\\end{enumerate}\n\nBehind the scenes, The search bar widget points to a hidden search page\nwith the friendly URL \\texttt{/search}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-dest-page.png}\n\\caption{By default, the embedded search bar points to the\npre-configured \\texttt{/search} destination page.}\n\\end{figure}\n\nEnter a search term and you're redirected to the default search page,\nwhere results are displayed in the Search Results widget.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-default-page.png}\n\\caption{The default page is pre-configured with the Search Results\nwidget and the various Facet widgets to provide a full search\nexperience.}\n\\end{figure}\n\nThe default search page is based on a Search page template, but it\ndoesn't inherit changes from the page template by default. That means\nyou can customize the search page directly without changing the\ntemplate's inheritance configuration.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-page-config.png}\n\\caption{Configure the Search page. By default, it doesn't inherit\nchanges from the page template.}\n\\end{figure}\n\nIf you require just a few changes to the default page, don't abandon it\nand create one manually. Just make the configuration changes you need,\nincluding adding, configuring, and removing widgets on the page. On the\nother hand, if you want a clean break from the default search page,\nstarting from scratch is also an option.\n\n\\section{Manual Search Page\nConfiguration}\\label{manual-search-page-configuration}\n\nIt's reasonable to create the search experience from the ground up. If\nyou're working from a newly created site, it's a necessity. These steps\nshow you how to switch to a manually configured search experience in the\ndefault site, but you can skip the step on deleting the default search\npage if you're starting with a new site:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Delete the existent search page by navigating to the default site's\n  menu and clicking \\emph{Site Builder} → \\emph{Pages}. Click the Search\n  page's Actions menu icon (\\includegraphics{./images/icon-actions.png})\n  and select \\emph{Delete}. Confirm you want to delete the page, and\n  it's gone.\n\n  Once deleted, the search bar disappears from your site pages, replaced\n  by a warning message visible only to site administrators:\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/search-bar-warning.png}\n  \\caption{The search bar is only visible if it points to an existent\n  page.}\n  \\end{figure}\n\\item\n  Create a new page named whatever you want (\\emph{Finders Keepers},\n  perhaps). Make it hidden or add it to the navigation as you please\n  (the default search page is hidden from the navigation).\n\n  If you want a pre-configured search page, create it from the Search\n  page template. Find the template in the Add Site Page form. It's under\n  \\emph{Global Templates}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/search-page-template.png}\n  \\caption{There's a handy page template for creating search pages.}\n  \\end{figure}\n\\item\n  If you're creating a page not backed by the template, add and\n  configure all the widgets you need. You'll find all the available\n  search widgets in the Add Widget menu's Search section. Lay them out\n  however you want on the page.\n\\item\n  Configure the search bar at the top of the page, making sure it points\n  to your new search page's friendly URL (for example,\n  \\texttt{/finders-keepers}).\n\n  Click the Search Bar widget's Options menu\n  (\\includegraphics{./images/icon-app-options.png}).\n\n  Click \\emph{Configuration} and set the Destination Page to the search\n  page's friendly URL.\n\n  Click \\emph{Save}.\n\\end{enumerate}\n\nNow your search page is up and running.\n\n\\section{Legacy Search Experience}\\label{legacy-search-experience}\n\nIn prior versions, the search experience was encapsulated in one\napplication, \\emph{Search}. It was embedded in the default theme, just\nlike the search bar is now. It looked very similar, with only the search\nbar visible in the default view of the application. Once a search term\nis entered, the maximized view of the application is presented, with all\nthe search facets and results now in view. It looks a lot like the new\nsearch behavior, only its monolithic structure means it's difficult to\ncustomize. If you liked the old application, it's still available.\nEnable it with these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Delete the default search page. From the site menu, click \\emph{Site\n  Builder} → \\emph{Pages}. Click the Actions menu\n  (\\includegraphics{./images/icon-actions.png}) for the Search page and\n  choose \\emph{Delete}.\n\\item\n  Enable the legacy search application. Go to Control Panel →\n  Configuration → System Settings → Search → Search Web and check the\n  box for \\emph{Classic Search Widget in Front Page}.\n\\end{enumerate}\n\nNow your portal's search is backed by the legacy Search application, and\nit's embedded on each page in the default theme. To add the legacy\nSearch application to a page, open the Add Widget menu, find the Search\nwidget under the Tools category, and drag and drop it onto the page.\n\nConfigure the portal's search behavior to suit your needs. Here you've\nseen three distinct search configurations.\n\n\\chapter{Searching for Assets}\\label{searching-for-assets}\n\nAs explained in the\n\\href{/docs/7-2/user/-/knowledge_base/u/search}{Search introduction},\nall indexed assets can be returned as search results. Developers can\ncreate their own assets, so your installation might have additional\nasset types beyond the ones included by default.\n\n\\noindent\\hrulefill\n\n\\textbf{Searching for Users:} When you click an asset in the search\nresults, it's displayed in an Asset Publisher (unless the \\emph{View in\nContext} option is selected in the Search Results portlet). Users are\ndifferent, though. Think of them as invisible assets, not intended for\ndisplay in the Asset Publisher application. While Users appear as search\nresults with other indexed assets, when you click one you're taken to\nthe User's profile page. If public personal pages are disabled, clicking\non a User from the list of search results shows you a blank page.\n\n\\noindent\\hrulefill\n\n\\section{Search Bar}\\label{search-bar-1}\n\nUsers enter the search context in the search bar. Users enter search\nterms, hit the \\emph{Enter} button (or click the magnifying glass icon),\nand they're taken to a\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-search-pages}{search\npage} with various search widgets deployed.\n\nIf using the Search Bar in the legacy search portlet, users see a\nmaximized view of the search portlet displaying any results and facets\nthat apply. See the article on\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-search-pages\\#legacy-search-experience}{configuring\nsearch pages} to learn more about these options.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-bar.png}\n\\caption{The default search configuration displays a search bar in its\ndefault view, beckoning users to enter the search context.}\n\\end{figure}\n\n\\section{Entering Search Terms}\\label{entering-search-terms}\n\nLiferay's search infrastructure supports full text search as implemented\nby its supported search engines\n(\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/full-text-queries.html}{Elasticsearch}\nand \\href{http://lucene.apache.org/solr/features.html}{Solr}).\n\nFull text search compares all the words entered in a search query (for\nexample, \\emph{space vacation}) to all the words in each index document.\nA search engine like Elasticsearch calculates relevance scores to ensure\nthe best results are returned first (like a Blogs Entry titled \\emph{Is\na vacation in space right for you?}) and lots of matching results are\nreturned (anything with either the word \\emph{vacation} or \\emph{space}\nis returned).\n\nIn addition to full text search, advanced search syntax is supported.\nLiferay DXP relies on the underlying search engine for this behavior, so\nconsult the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-query-string-query.html\\#query-string-syntax}{Elasticsearch}\nor\n\\href{https://lucene.apache.org/solr/guide/6_6/query-syntax-and-parsing.html}{Solr}\ndocumentation for the details.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-advanced-syntax.png}\n\\caption{Search for text in a specific field using Elasticsearch's Query\nString syntax.}\n\\end{figure}\n\n\\section{Matching Exact Phrases: Quoted\nSearch}\\label{matching-exact-phrases-quoted-search}\n\nWhat if users want their search terms (for example, \\emph{agile\nframeworks}) to produce only results with the exact phrase, as typed? In\na regular full text search, searching \\emph{agile frameworks} returns\nsearch results containing just the terms \\emph{agile} and\n\\emph{frameworks}, and hits containing both terms but separated by other\ntext, as well as results with the exact phrase match. To ensure that\nonly hits with the exact phrase are returned, enclose it in quotes:\n\\emph{``agile frameworks''}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-quoted.png}\n\\caption{Search for exact phrase matches by enclosing search terms in\nquotes. If a user searched for \\emph{``agile frameworks''}, this result\nwould not be returned.}\n\\end{figure}\n\n\\section{Prefix Searching}\\label{prefix-searching}\n\nIf you're searching in a site for classical musicians, you might search\nfor the term \\emph{instrument}. This search returns documents with the\nfull word in them, but it also returns variants with \\emph{instrument}\nas the prefix. For example, results with \\emph{instruments},\n\\emph{instrumental}, and \\emph{instrumentation} are also returned.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-prefix.png}\n\\caption{Searching for \\emph{lever} also returns \\emph{leverage} and\n\\emph{leveraging}.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Prefix searching is available for many fields out of the\nbox, but as with most things related to search behavior, it's more\ncomplicated under the hood. The details of the field mapping, including\nthe analyzer used on the field and any transformations performed,\ndetermine the final behavior.\n\n\\noindent\\hrulefill\n\nAnother way to ensure users see results is through\n\\hyperref[search-suggestions]{search suggestions}.\n\n\\section{Configuring the Search Bar}\\label{configuring-the-search-bar}\n\nConfigure the Search Bar's behavior via its portlet configuration\nscreen.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-bar-configuration.png}\n\\caption{Configure the search bar behavior in its configuration screen.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When you configure the globally embedded Search Bar\nwidget at the top of one page, it configures the page-top Search Bar\nwidget on all pages in the site. It also overrides the destination\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-search-pages}{Search\nPage's} Search Bar portlet, if they're configured differently. However,\nit does not override Search Bar widgets manually placed on other pages.\n\n\\noindent\\hrulefill\n\nThere are several options:\n\n\\begin{description}\n\\item[\\textbf{Keywords Parameter Name}]\nEdit the parameter name for the keywords entered in the search. For\nexample, the default URL when searching for the keyword term \\emph{data}\nlooks like this:\n\nhttp://localhost:8080/web/guest/search?q=data\n\\end{description}\n\nIf you change the Keywords Parameter Name to \\emph{keyword} it looks\nlike this:\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?keyword=data\n\\end{verbatim}\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Scope}]\nChoose between three options: This Site (default), Everything, and Let\nthe User Choose. \\emph{This Site} means only the assets associated with\nthe site where the search is executed are searched. Expand the scope of\nthe search to all sites by selecting \\emph{Everything}. To let users\nchoose which scope they want to search, select \\emph{Let the User\nChoose}.\n\\end{description}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-scope.png}\n\\caption{Let the user choose which scope the search is executed for.}\n\\end{figure}\n\n\\textbf{Scope Parameter Name} : Set the URL parameter name for the scope\nwhere the search is taking place. This parameter only appears in the URL\nif the scope \\emph{Let the User Choose} is selected. The default value\nis \\emph{scope}, so searching for the word \\emph{data} produces the\ndefault URL of\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=data&scope=this-site\n\\end{verbatim}\n\nChanging \\emph{scope} to \\emph{target} would produce this URL:\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=data&target=this-site\n\\end{verbatim}\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Destination Page}]\nProvide a friendly URL to the\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-search-pages}{search\npage}. If not configured or if it points to a page that doesn't exist, a\nmessage appears for administrators that the search bar must be\nconfigured for it to appear to users.\n\\item[\\textbf{Use Advanced Search Syntax}]\nIf using Elasticsearch, enabling this allows users to enter\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-query-string-query.html\\#query-string-syntax}{Query\nString Syntax} into the Search Bar. If using Solr, consult its\ndocumentation for the\n\\href{https://lucene.apache.org/solr/guide/6_6/query-syntax-and-parsing.html}{proper\nsyntax}.\n\\end{description}\n\n\\section{Search Suggestions}\\label{search-suggestions}\n\nSuggest search terms to users when their initial queries are suboptimal.\nSpell check settings allow administrators to configure the Search\napplication so that if a user types a search term that doesn't return\nmany results (for example, a slightly misspelled werd), the user can be\nprompted to improve the search.\n\nTo configure the spell check settings,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  You must first reindex the spell check indexes. Go to \\emph{Control\n  Panel} → \\emph{Configuration} → \\emph{Search}, then click\n  \\emph{Execute} next to \\emph{Reindex all spell check indexes}.\n\\item\n  Add the Suggestions widget to the search page.\n\\item\n  Open its configuration screen. Click the widget Options button\n  (\\includegraphics{./images/icon-app-options.png}) and select\n  \\emph{Configuration}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-suggestions.png}\n\\caption{Configure the suggestion settings to allow for user input\nmistakes and help lead users to results.}\n\\end{figure}\n\nThere are three main settings here:\n\n\\textbf{Display ``Did you mean\\ldots{}'' if the number of search results\ndoes not meet the threshold.} : Present users alternate, spell checked\nsearch queries if their search did not return a minimum number of\nresults (50 by default).\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Display Related Queries}]\nIf the number of search results doesn't meet the specified threshold (50\nby default), display up to a maximum number of alternative queries (10\nby default).\n\\item[\\textbf{Add New Related Queries Based on Successful Queries}]\nIndex a user's search query if it produces a minimum number of results\n(50 by default), so it can be displayed to users as a suggestion. If the\nDisplay Related Queries setting is enabled, it's used as a related query\nfor similar search queries that don't produce enough results.\n\\end{description}\n\n\\chapter{Facets}\\label{facets}\n\nEnter a keyword in the Search Bar and click the Search button. The\ndefault search experience redirects to a page with results on the right\nand a collection of \\emph{facets} on the left.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-faceted-search.png}\n\\caption{\\emph{Site} and \\emph{Type} are two of the facet sets you'll\nencounter.}\n\\end{figure}\n\nA Facet aggregates search results by some common characteristic, with\neach facet holding search results that share something in common. After\nscanning the full list of results, a User might decide the results from\njust one facet are more appropriate (for example, all the results from a\nparticular site, or all the results that are Blogs Entries). So what\nfacets are included by default?\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Site Facet} for filtering results by their site.\n\\item\n  \\textbf{Type Facet} for filtering results by the Asset Type.\n\\item\n  \\textbf{Tag Facet} for filtering results by Tag.\n\\item\n  \\textbf{Category Facet} for filtering results by Category.\n\\item\n  \\textbf{Folder Facet} for filtering results by Folder.\n\\item\n  \\textbf{User Facet} for filtering results by the content creator.\n\\item\n  \\textbf{Modified Facet} for filtering results by the Last Modified\n  Date.\n\\item\n  \\textbf{Custom Facet} for filtering results by some other indexed\n  field. See \\href{/docs/7-2/user/-/knowledge_base/u/custom-facet}{here}\n  for more information.\n\\end{itemize}\n\nEach item in a facet (selected using the checkbox) is called a\n\\emph{Facet Term} (\\emph{term} for short).\n\nIn this tutorial, you'll explore how facets and their terms are used and\nhow to find a facet's configuration. The remaining articles show the\nconfigurations available for each facet.\n\n\\section{Using Facets}\\label{using-facets}\n\nIf you're not actually an accomplished oboe player, pretend for a\nmoment. You're visiting a site for classical musicians. You remember\nreading a great technical analysis of Johann Bach's compositions, but\nyou forgot to bookmark it (or would it be a \\emph{bachmark}?). You enter\nthe keyword \\emph{bach} into the search bar and, because Johann Bach was\na very important and famous composer, you get lots of results: too many,\nin fact. At first you're discouraged, but you remember that there's a\nsite member who produces most of the site's good technical content,\nwho's named \\emph{back2bach}. You see that his name is listed in the\nUser facet, and there aren't many results in the facet count (the number\nin parentheses next to the facet). You click into the facet and quickly\nfind the content you wanted.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-facets1.png}\n\\caption{When presented lots of search results, facets narrow down the\nresults list so users can find relevant content.}\n\\end{figure}\n\nClicking on a facet narrows down the search results. It's added to the\nfilter list in the search query, and the results list is refined by the\nselected facets.\n\n\\includegraphics{./images/search-facet-wc.png}.\n\n\\section{Multiple Facet Selection}\\label{multiple-facet-selection}\n\nFacet term selections within one facet are additive. Clicking more terms\nin the same facet expands the search results, because it's processed as\nif you want to see results matching \\emph{Term-1} OR \\emph{Term-2}, OR\netc. To remove all the term selections from a facet, click the\n\\emph{Clear} link.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-multiple-terms.png}\n\\caption{Facet terms are additive when applied in the same facet. Any\nBlogs Entry OR Web Content article matching the keyword is shown here.}\n\\end{figure}\n\nFacet term selections from different facets are exclusive. Clicking\nfacet terms from multiple facets narrows the results because they're\nprocessed as if you want to see results matching \\emph{Facet-1} AND\n\\emph{Facet-2}, AND etc. This is intuitive. The facets\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-multiple-facets.png}\n\\caption{Facet terms selected from different facets are exclusive. These\nresults must be of type Blogs Entry AND be from the User Marvin Smart.}\n\\end{figure}\n\nConsidering a case where you make two term selections in the Type Facet:\n(Blogs Entry and Web Content Article), and two term selections in the\nUser Facet (James Jeffries and Marvin Smart). What results are\ndisplayed?\n\n\\emph{Blogs Entries OR Web Content Articles AND authored by James\nJeffries OR Marvin Smart}.\n\nIf Marvin and James each created four pieces of content (two blogs and\ntwo Web Content Articles), all eight would appear in the Search Results.\nAny Blogs or Web Content created by other Users are not shown, and\nassets of other Type created by Marvin and James are not displayed.\nContent that isn't Blog Entries or Web Content Articles created by other\nUsers are obviously not searched.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-facet-selections.png}\n\\caption{Both intra-facet and inter-facet selection is possible.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The new Search Facet widgets support the multiple\nselection of facet terms. Multiple facet selection is not supported in\nthe classic Search portlet.\n\n\\noindent\\hrulefill\n\n\\section{Facets and Friendly URLs}\\label{facets-and-friendly-urls}\n\nIn the classic, monolithic Search portlet, URLs like this were not\nuncommon:\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/home?_com_liferay_portal_search_web_portlet_SearchPortlet_formDate=1529671834606&p_p_id=com_liferay_portal_search_web_portlet_SearchPortlet&p_p_lifecycle=0&p_p_state=maximized&p_p_mode=view&_com_liferay_portal_search_web_portlet_SearchPortlet_mvcPath=%2Fsearch.jsp&_com_liferay_portal_search_web_portlet_SearchPortlet_redirect=http%3A%2F%2Flocalhost%3A7011%2Fweb%2Fguest%2Fhome%3Fp_p_id%3Dcom_liferay_portal_search_web_portlet_SearchPortlet%26p_p_lifecycle%3D0%26p_p_state%3Dnormal%26p_p_mode%3Dview&_com_liferay_portal_search_web_portlet_SearchPortlet_keywords=test&_com_liferay_portal_search_web_portlet_SearchPortlet_scope=this-site\n\\end{verbatim}\n\nSearch now uses friendly search URLs for facet filtering. With the\ndefault settings, here's the default main search URL when searching for\nkeyword \\emph{test}:\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=test\n\\end{verbatim}\n\nSelecting a facet term causes a new parameter to the above URL. For\nexample, selecting \\emph{Blogs Entry} from the Type facet results in\nthis URL:\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=test&type=com.liferay.blogs.model.BlogsEntry\n\\end{verbatim}\n\nSelecting another facet term from the same facet category appends the\nsame parameter again, but with the newly selected value:\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=test&type=com.liferay.blogs.model.BlogsEntry&type=com.liferay.portal.kernel.model.User\n\\end{verbatim}\n\nThe rest of the facets work the same way. Filtering by the last hour\noption in the Last Modified facet portlet produces this URL:\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=test&modified=past-hour\n\\end{verbatim}\n\nThe parameter names are configurable for each facet.\n\nNow that you know how facets work, read about configuring each of the\nincluded facets.\n\n\\chapter{Site Facet}\\label{site-facet}\n\nThe Site Facet narrows search results down to those existing in a\ncertain site. Each Site with content matching the searched keyword\nappears as a facet term.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-site-facet.png}\n\\caption{Each Site with matching content is a facet term.}\n\\end{figure}\n\nFor the Site Facet to display multiple sites, the Search Bar must be\nconfigured to search \\emph{Everything}. See more about search scope\n\\href{/docs/7-2/user/-/knowledge_base/u/searching-for-assets\\#configuring-the-search-bar}{here}.\nIf not searching for Everything, only the current site is searched, and\nthe Site Facet has nothing to display. When this occurs, the Site Facet\nis hidden on the page.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Configuring the globally embedded page-top Search Bar to\nsearch for Everything not only configures the embedded Search Bar on all\npages. It also ensures that the Search Page's Search Bar searches\nEverything, because the page-top Search Bar's configuration overrides\nthe Search Page's Search Bar configuration. The same does not apply to\nother Search Bar widgets in the site. Each of these must be configured\nas desired.\n\nIf the global Search Bar is disabled, configure the Search Page's Search\nBar widget to search for Everything.\n\nTo configure the search scope,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Search Bar's Options menu\n  (\\includegraphics{./images/icon-options.png}) and click\n  \\emph{Configuration}.\n\\item\n  Set the Scope option to \\emph{Everything}.\n\\item\n  Click \\emph{Save} and close the pop-up.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\nThe Site Facet contains several configuration options:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-site-facet-config.png}\n\\caption{The Site Facet is configurable.}\n\\end{figure}\n\n\\begin{description}\n\\item[\\textbf{Site Parameter Name}]\nSet the URL parameter name for the Facet. The default is \\emph{site}.\nSearching for \\emph{lunar resort} and clicking on a site facet produces\nthe URL\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=lunar resort&site=20126\n\\end{verbatim}\n\\item[\\textbf{Max Terms}]\nSet the maximum number of facet terms to display, regardless of how many\nmatching terms are found for the facet.\n\\item[\\textbf{Frequency Threshold}]\nSet the minimum frequency required for terms to appear in the list of\nfacet terms. For example, if the frequency threshold of a facet is set\nto \\texttt{3}, a term with two matching results doesn't appear in the\nterm result list.\n\\item[\\textbf{Display Frequencies}]\nChoose whether or not to display the term frequencies.\n\\end{description}\n\n\\chapter{Type Facet}\\label{type-facet}\n\nThe Type Facet narrows search results down to those associated with a\ncertain Asset Type. Each Type with content matching the searched keyword\nappears as a facet term.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-type-facet.png}\n\\caption{Each Asset Type with matching content is a Type Facet term.}\n\\end{figure}\n\nBy default, all out of the box Asset Types are included as facet terms:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Wiki Page\n\\item\n  Document\n\\item\n  User\n\\item\n  Blogs Entry\n\\item\n  Form Record\n\\item\n  Documents Folder\n\\item\n  Dynamic Data Lists Record\n\\item\n  Web Content Article\n\\item\n  Message Boards Message\n\\item\n  Calendar Event\n\\item\n  Knowledge Base Article\n\\end{itemize}\n\nThe Type Facet contains several configuration options:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-type-facet-config.png}\n\\caption{The Type Facet is configurable.}\n\\end{figure}\n\n\\begin{description}\n\\item[\\textbf{Type Parameter Name}]\nSet the URL parameter name for the Facet. The default is \\emph{type}.\nSearching for \\emph{lunar resort} and clicking on a site facet produces\nthe URL\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=lunar resort&type=com.liferay.blogs.model.BlogsEntry\n\\end{verbatim}\n\\item[\\textbf{Frequency Threshold}]\nSet the maximum number of facet terms to display, regardless of how many\nmatching terms are found for the facet.\n\\item[\\textbf{Display Frequencies}]\nChoose whether or not to display the term frequencies.\n\\item[\\textbf{Current and Available}]\nAdd or remove Asset Types from the facet. To remove types, select from\nthe Current section by clicking and highlighting. Click the right arrow\nand move the Asset Type from \\emph{Current} to \\emph{Available}. Add\nAsset Types by moving them to the Current section.\n\\end{description}\n\n\\chapter{Tag and Category Facets}\\label{tag-and-category-facets}\n\nIf tags or categories were applied to an asset appearing in the result\nset, they're displayed in the Tag or Category facet, respectively. Like\nother facets with the Frequency Threshold configuration option, not all\ntags necessarily appear. By default the top 10 tags or categories are\nlisted.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-tag-facet.png}\n\\caption{Each Tag or Category with matching content is a facet term.}\n\\end{figure}\n\nTag and Category Facets contain identical configuration options:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-tag-facet-config.png}\n\\caption{Tag and Category Facets are configurable.}\n\\end{figure}\n\n\\begin{description}\n\\item[\\textbf{Tag/Category Parameter Name}]\nSet the URL parameter name for the Facet. The default is\n\\emph{tag}/\\emph{category}. Searching for \\emph{lunar resort} and\nclicking on a \\emph{moon} Tag Facet term produces the URL\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=lunar resort&tag=moon\n\\end{verbatim}\n\\item[\\textbf{Display Style}]\nChoose whether to display the facet terms in Cloud or List style.\n\\item[\\textbf{Max Terms}]\nSet the maximum number of facet terms to display, regardless of how many\nmatching terms are found for the facet.\n\\item[\\textbf{Frequency Threshold}]\nSet the minimum frequency required for terms to appear in the result\nlist. For example, if the frequency threshold of a facet is set to\n\\texttt{3}, a term with two matching results doesn't appear in the term\nresult list.\n\\item[\\textbf{Display Frequencies}]\nChoose whether or not to display the term frequencies.\n\\end{description}\n\n\\chapter{Folder Facet}\\label{folder-facet}\n\nThe Folder Facet narrows search results down to those contained in a\ncertain Asset Folder. If you search for \\emph{space}, a Folder titled\n\\emph{Space Images} doesn't necessarily show up here. The content inside\nthe folder must match the keyword. Only if its content matches the\nsearched keyword does the Folder appear in the Folder Facet.\n\nFolders of these Types appear as Folder Facet terms:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Documents and Media Folder\n\\item\n  Web Content Folder\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-folder-facet.png}\n\\caption{Each Folder with matching content is a facet term.}\n\\end{figure}\n\nThe Folder Facet contains several configuration options:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-folder-facet-config.png}\n\\caption{The Folder Facet is configurable.}\n\\end{figure}\n\n\\begin{description}\n\\item[\\textbf{Folder Parameter Name}]\nSet the URL parameter name for the Facet. The default is \\emph{folder}.\nSearching for \\emph{lunar resort} and clicking on a Folder Facet\nproduces the URL\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=lunar resort&folder=38716\n\\end{verbatim}\n\\item[\\textbf{Max Terms}]\nSet the maximum number of facet terms to display, regardless of how many\nmatching terms are found for the facet.\n\\item[\\textbf{Frequency Threshold}]\nSet the minimum frequency required for terms to appear in the result\nlist. For example, if the frequency threshold of a facet is set to\n\\texttt{3}, a term with two matching results doesn't appear in the term\nresult list.\n\\item[\\textbf{Display Frequencies}]\nChoose whether or not to display the term frequencies.\n\\end{description}\n\n\\chapter{User Facet}\\label{user-facet}\n\nThe User Facet narrows search results down to those created by a certain\nUser.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-user-facet.png}\n\\caption{Each User with matching content is a facet term.}\n\\end{figure}\n\nThe User Facet contains several configuration options:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-user-facet-config.png}\n\\caption{The User Facet is configurable.}\n\\end{figure}\n\n\\begin{description}\n\\item[\\textbf{User Parameter Name}]\nSet the URL parameter name for the Facet. The default is \\emph{user}.\nSearching for \\emph{lunar resort} and clicking on a User Facet produces\nthe URL\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=lunar resort&user=38716\n\\end{verbatim}\n\\item[\\textbf{Max Terms}]\nSet the maximum number of facet terms to display, regardless of how many\nmatching terms are found for the facet.\n\\item[\\textbf{Frequency Threshold}]\nSet the minimum frequency required for terms to appear in the result\nlist. For example, if the frequency threshold of a facet is set to\n\\texttt{3}, a term with two matching results doesn't appear in the\nfacet.\n\\item[\\textbf{Display Frequencies}]\nChoose whether or not to display the term frequencies.\n\\end{description}\n\n\\chapter{Modified Facet}\\label{modified-facet}\n\nThe Modified Facet narrows search results down to those that match the\nsearched keyword and that were created or modified during a certain time\nperiod.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-modified-facet.png}\n\\caption{Each time period with matching content is a facet term.}\n\\end{figure}\n\nIn addition to selecting a pre-configured time period, Users can select\na Custom Range, specifying a From and To date using a date picker:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-modified-facet-custom.png}\n\\caption{Users can include a Custom Range in the Modified Facet.}\n\\end{figure}\n\nThe Modified Facet supports configuration actions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Modify existing time ranges\n\\item\n  Delete existing time ranges\n\\item\n  Create new time ranges\n\\end{itemize}\n\nEdit or create time ranges using a time range alias.\n\nThe available time range aliases include:\n\n\\begin{verbatim}\npast-hour\npast-24-hours\npast-week\npast-month\npast-year\n\\end{verbatim}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-modified-facet-config.png}\n\\caption{The time ranges are set in the facet's configuration.}\n\\end{figure}\n\nEach Range has an alias and a Label.\n\nBy default, all the default ranges end in \\texttt{*}, which evaluates to\n\\emph{now}. For example, the past-week range is\n\n\\begin{verbatim}\n[past-week TO *]\n\\end{verbatim}\n\nYou're not limited to ending Ranges. Instead of the \\texttt{*}, specify\nanother time range alias as the ending point.\n\nTo set up a range from 12 months ago to one month ago,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the plus button in one of the existing ranges.\n\\item\n  Give it the label \\textbf{1-12 Months Ago}.\n\\end{enumerate}\n\nGive it a Range value of\n\n\\begin{verbatim}\n[past-year to past-month]\n\\end{verbatim}\n\nThis gives you lots of flexibility in using alternative time ranges as\nModified Facet terms.\n\n\\chapter{Custom Facet}\\label{custom-facet}\n\nAll Facets are configurable, allowing you to narrow down search results\nbased on a shared characteristic (all Blog Entries, for example). The\nCustom Facet lets you create entirely new Facets. The first thing to do\nis enter the Custom Facet's configuration screen.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-custom-facet-jobtitle.png}\n\\caption{Custom Facets must be configured first.}\n\\end{figure}\n\nThe screenshot above shows a Custom Facet with the Job Title of each\nUser that matched the search. The next screenshot shows how it was\nconfigured.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-custom-facet-config.png}\n\\caption{Configure a Custom Facet in no time.}\n\\end{figure}\n\n\\begin{description}\n\\item[\\textbf{Aggregation Field}]\nSpecify the non-analyzed keyword field whose value is used to create the\nfacet terms. If the value of the search result's\n\\texttt{jobTitle\\_sortable} field is \\emph{upsale manager}, that's what\nappears in the Custom Facet as a term.\n\\item[\\textbf{Custom Heading}]\nEnter a human readable heading for the Custom Facet.\n\\item[\\textbf{Custom Parameter Name}]\nSet the URL parameter to use for the facet. With the configuration\npictured above, searching for \\emph{jane} and clicking on \\emph{chief of\nsecurity} produces the URL\n\nhttp://localhost:8080/web/guest/search?q=jane\\&jobtitle=chief\\%20of\\%20security\n\\item[\\textbf{Max Terms}]\nSet the maximum number of facet terms to display, regardless of how many\nmatching terms are found for the facet.\n\\item[\\textbf{Frequency Threshold}]\nSet the minimum frequency required for terms to appear in the result\nlist. For example, if the frequency threshold of a facet is set to\n\\texttt{3}, a term with two matching results doesn't appear in the term\nresult list.\n\\item[\\textbf{Display Frequencies}]\nChoose whether to display the term frequencies.\n\\end{description}\n\n\\section{Finding Indexed Fields}\\label{finding-indexed-fields}\n\nTo use the Custom Facet, you must know which non-analyzed keyword field\nto specify.\n\nTo browse the entire list of available fields, inspect the field\nmappings from Control Panel → Configuration → Search. Alternatively, use\nyour search engine's API.\n\nFor Elasticsearch, access the field mappings from your terminal using\nCURL to call the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/indices-get-mapping.html}{Get\nMapping API}:\n\n\\begin{verbatim}\ncurl -X GET \"localhost:9200/_mapping/LiferayDocumentType\"?pretty\n\\end{verbatim}\n\nSolr uses the\n\\href{https://lucene.apache.org/solr/guide/6_6/schema-api.html\\#SchemaAPI-ListFields}{ListFields\nAPI}:\n\n\\begin{verbatim}\ncurl http://localhost:8983/solr/liferay/schema/\n\\end{verbatim}\n\nHere's a snippet of output from the Elasticsearch example:\n\n\\begin{verbatim}\n\"ddmStructureKey\": {\n    \"store\": true,\n    \"type\": \"keyword\"\n},\n\"ddmTemplateKey\": {\n    \"store\": true,\n    \"type\": \"keyword\"\n},\n\"defaultLanguageId\": {\n    \"store\": true,\n    \"type\": \"keyword\"\n},\n\"description\": {\n    \"store\": true,\n    \"term_vector\": \"with_positions_offsets\",\n    \"type\": \"text\"\n},\n\"discussion\": {\n    \"store\": true,\n    \"type\": \"keyword\"\n},\n\\end{verbatim}\n\nUse Custom Fields to aggregate facet terms by shared non-analyzed\nkeyword field values.\n\n\\chapter{Search Results}\\label{search-results}\n\nThe ideal search experience involves a User entering a search term,\nwaiting an infinitesimal amount of time, and having the perfectly\nmatching asset delivered at the top of a list of other extremely\nrelevant hits. Like this:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-results-perfect.png}\n\\caption{The goal is to return the perfect results to Users searching\nyour site.}\n\\end{figure}\n\nThe developers of an asset control much about how the asset's\ninformation is stored in the search engine (this process is called\n\\emph{indexing}, and how its information is searched and returned in the\nsearch results. Developers who dislike how a particular asset behaves in\nsearch can use an \\emph{Indexer Post Processor} to modify the asset's\nindexing behavior and how search queries are constructed to look up the\nassets in Liferay DXP.\n\nThe Search Results behavior configurable through the UI is covered in\nthis section:\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/display-settings}{Search\n  Results Display Settings}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/filtering-search-results-with-the-custom-filter-widget}{Filtering\n  Results}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/sorting-search-results-with-the-sort-widget}{Sorting\n  Results}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/search-results-behavior}{Search\n  Results Behavior}\n\\end{itemize}\n\n\\chapter{Display Settings}\\label{display-settings}\n\nThe Search Results widget's default display is a paginated list. Each\nlist item is a summarized hit to a search query. Click on a specific\nresult to look at it in more detail. Configure display options by\nopening the Search Results options menu\n(\\includegraphics{./images/icon-app-options.png}) and selecting\n\\emph{Configuration}.\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Enable Highlighting}]\nHighlight the search terms where they appear in the search result's\ntitle or summary.\n\\item[\\textbf{Display Selected Result in Context}]\nWhen an asset is clicked, show it in its native application. For\nexample, if you click on a blog post in the search results, you see\nwhere the Blogs Entry is posted in the Blogs application. Note that\nyou're not in the search context after clicking on a search result. When\nthis option is unchecked, the asset displays in an Asset Publisher\nwindow while still in the search context. If you have the right\npermissions, you can even edit the content directly from the Search\ncontext. Click the back arrow to return to the search results.\n\\item[\\textbf{Display Results in Document Form}]\nDisplay results as search\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/search}{documents}. Never\nuse this in production. Developers use this feature to view search\nresponses in their indexed, document-based format. Part of a developer's\njob when writing search indexers is to convert documents (the objects\nthat get indexed) to the actual object and back again. Thus, developers\ncan see how their objects are being indexed. Once enabled, click the\n\\emph{Details\\ldots{}} link below the result summary to expand the\nresult's document view.\n\\end{description}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-results-document.png}\n\\caption{Viewing a results document lets you inspect exactly what's\nbeing indexed for a particular asset. This is just a small portion of\none document.}\n\\end{figure}\n\nThe next three configurations concern results pagination.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-results-pagination.png}\n\\caption{The number of results per page and the URL parameter names used\nto control pagination behavior are configurable.}\n\\end{figure}\n\n\\begin{description}\n\\item[\\textbf{Pagination Start Parameter Name}]\nSet the name of the URL parameter for the results page. If the default\nvalue \\emph{start} is preserved, this URL displays when the User\nnavigates to the second results page after searching for \\emph{test}:\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=test&start=2\n\\end{verbatim}\n\\item[\\textbf{Pagination Delta}]\nSet the number of results to display per results page. Defaults to\n\\emph{20} unless you customized the\n\\texttt{search.container.page.default.delta} property in your\n\\texttt{portal-ext.properties} file.\n\\item[\\textbf{Pagination Delta Parameter Name}]\nSet the name of the URL parameter that stores the Pagination Delta\nvalue. This becomes visible in the browser if the User changes the\nnumber. If the User selects 10 results per page and searches for\n\\emph{test}, the Search Page is reloaded with this URL:\n\n\\begin{verbatim}\nhttp://localhost:8080/web/guest/search?q=test&delta=10\n\\end{verbatim}\n\\item[\\textbf{Federated Search Key}]\nIf this widget is participating in a search on a non-default index,\nenter the key of the alternate search index. If not set, the widget\nparticipates in the default search, against the default index\n(\\texttt{liferay-{[}comanyId{]}}. This value is usually the name of an\napplication-defined index.\n\\item[\\textbf{Fields to Display}]\nIf searching an alternate index using the Federated Search Key\nconfiguration, specify what fields to search from that index.\n\\end{description}\n\nFor further reading, check out how to\n\\href{/docs/7-2/user/-/knowledge_base/u/searching-for-assets\\#search-suggestions}{return\nsuggestions for better search terms} (for example, ``Did you\nmean\\ldots{}'') when not enough results are returned initially.\n\n\\chapter{Filtering Search Results with the Custom Filter\nWidget}\\label{filtering-search-results-with-the-custom-filter-widget}\n\nYou often need to exert control over the displayed search results. One\nviable approach is to develop your own search portlets using the Liferay\nDXP APIs. That can be overkill if you just want to make a slight\nmodification to how the search is executed, so many of the\nout-of-the-box search widgets give you this type of control without\ncoding anything (Search Options, Custom Facet, and more). In 7.0, new\nwidgets have been added: Sort and Custom Filter.\n\nWith Custom Filters, you can contribute queries to the main search\nquery, exerting control over the search results. Make the filter widgets\nvisible or invisible to the search Users, and decide if they're\nchangeable or immutable.\n\nTo explore all the options you have with the Custom Filter widget, you\nneed one on the page.\n\n\\section{Adding and Configuring Custom\nFilters}\\label{adding-and-configuring-custom-filters}\n\nTo get started with Custom Filters,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Add menu (\\includegraphics{./images/icon-add-widget.png}) for\n  the page and expand the Widgets section.\n\\item\n  From the Search section, drag a Custom Filter onto the page.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-custom-filter.png}\n\\caption{A custom filter has no impact until it's configured.}\n\\end{figure}\n\nCustom filters can do so many things, it's impossible to list them all.\nWhat follows is a widget configuration tour. Separate documentation will\nbe written to provide a how-to demonstration of Custom Filters.\n\n\\section{Custom Filter Configuration\nOptions}\\label{custom-filter-configuration-options}\n\nOpen the widget Options menu\n(\\includegraphics{./images/icon-app-options.png}) and click\n\\emph{Configuration}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-custom-filter-configuration.png}\n\\caption{Once the Custom Filter is added to the page, mold it like soft\nclay into the beautiful sculpture you've envisioned.}\n\\end{figure}\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Filter Field (text)}]\nMost often, filters operate on a specific field. Set the name of the\nindexed field to be filtered (for example, \\texttt{title}). You won't\nneed this if the Filter Query Type is set to a type that doesn't require\na field, such as \\emph{Regexp}.\n\\end{description}\n\n\\begin{quote}\nThe Query String and Script queries do not require a Filter Field to be\nset. All other queries require at least one field.\n\\end{quote}\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Filter Value (text)}]\nFor most filters, you must enter a text value here that specifies the\ntext to apply the filter on in the specified field (for example, set a\n\\emph{Match} query to the text \\emph{street} on the\n\\texttt{title\\_en\\_US} field). Some Filter Query Types require special\nnotation, as in the case of the \\emph{Regexp} filter.\n\\item[\\textbf{Filter Query Type (select list)}]\nSelect the query type to filter results by. Available types include\nBool, Exists, Fuzzy, Match, Match Phrase, Match Phrase Prefix, Multi\nMatch, Prefix, Query String, Regexp, Script, Simple Query String, Term,\nWildcard. To learn more about these queries, visit the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl.html}{Elasticsearch\ndocumentation}.\n\\item[\\textbf{Occur (select list)}]\nSet the occurrence type for the query being contributed to the search.\nOptions include Filter, must, must\\_not, and should. To understand each\ntype, see the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-bool-query.html}{Elasticsearch\ndocumentation}.\n\\item[\\textbf{Query Name (text)}]\nSet the name of the contributed query. This is unnecessary unless this\nfilter acts as a parent query to another filter that contributes child\nclauses; in that case set this filter's Query Name as the child filter's\nParent Query Name. This parent/child behavior is only available for\nfilters of type Bool.\n\\item[\\textbf{Parent Query Name (text)}]\nWhen contributing a child clause to a Bool query, set this to match the\nQuery Name configured in the parent Custom Filter widget. Otherwise,\nleave it blank.\n\\item[\\textbf{Boost (number)}]\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-term-query.html\\#term-field-params}{Boost}\nthe score of the results matching this query. Specify any whole or\ndecimal number here that makes sense. If you always want results\nmatching this at the top, set the Boost value really high (e.g.,\n\\emph{1000}).\n\\item[\\textbf{Custom Heading (text)}]\nEnter the heading to display for this filter. If not set, the Filter\nField's value is displayed.\n\\item[\\textbf{Custom Parameter Name (text)}]\nSpecify a URL parameter name for the filter. If not set, the Filter\nField's value is used.\n\\item[\\textbf{Invisible (boolean)}]\nIf checked, the widget is invisible to regular users. The Filter Value\nfrom the configuration is applied by default, but users can still filter\nfor other values via URL Parameter. Don't worry, you can shut that down\nif you need to with the Immutable setting (see below).\n\\item[\\textbf{Immutable (boolean)}]\nEnable this to ensure that the Filter Value cannot be changed by regular\nusers. The widget becomes invisible to them \\emph{and} filter values set\nvia URL parameters are not accepted. The Filter Value set in the widget\nconfiguration is applied at all times (unless it's disabled).\n\\item[\\textbf{Disabled (boolean)}]\nIf checked, the query is ignored and doesn't participate in searches.\nThis gives you a quick way to stop the filter, but keep the\nconfiguration so it can be re-enabled later.\n\\item[\\textbf{Federated Search Key (text)}]\nEnter the key of an alternate Search this widget is participating on. If\nit's set, be aware that the default Liferay DXP index isn't searched at\nall. If not set, this widget participates on the default search. Values\nin this field typically match the name of an application-defined index.\n\\end{description}\n\nThere are many use cases you can satisfy by incorporating Custom Filters\ninto your search page. Two demonstrative articles are planned to show\nyou some of the filtering capabilities at your disposal:\n\n\\begin{itemize}\n\\item\n  \\emph{Refine to One} (or \\emph{Needle in a Haystack}) will show you\n  how to add user-operated filters to the page so results can be refined\n  down to just the result they were looking for.\n\\item\n  \\emph{Complex filtering} shows you some more advanced filters and how\n  they work.\n\\end{itemize}\n\nCheck out the Custom Filter and see what it adds to your search page.\n\n\\chapter{Sorting Search Results with the Sort\nWidget}\\label{sorting-search-results-with-the-sort-widget}\n\nThe Sort widget gives Users configurable control over the order of\nreturned results: no code necessary.\n\nAdd it to a page and begin sorting results.\n\nBy default, results are sorted by the\n\\href{https://www.elastic.co/guide/en/elasticsearch/guide/master/scoring-theory.html}{relevance\nscore} returned by the search engine. Users can choose from one of the\nout-of-the-box alternative sorting strategies, or one configured by a\nsearch administrator.\n\nOut of the box, order results in these ways as an alternative to\nrelevance sorting:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  alphabetically by Title\n\\item\n  by the Modified date (oldest first)\n\\item\n  by the Create date (newest first)\n\\item\n  by the Create date (oldest first)\n\\item\n  alphabetically by the User that created each matching asset\n\\end{itemize}\n\nIf the out-of-the-box alternatives aren't enough, an administrator can\ncreate additional sort options from the widget's configuration.\n\nIt's also possible to delete unwanted sort options from the widget.\n\n\\section{Adding and Configuring the Sort\nWidget}\\label{adding-and-configuring-the-sort-widget}\n\nTo get started with the Sort widget,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Add menu (\\includegraphics{./images/icon-add-widget.png}) for\n  the page and expand the Widgets section.\n\\item\n  From the Search section, drag a Sort widget onto the page.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-sort.png}\n\\caption{Users can re-order search results with the Sort widget.}\n\\end{figure}\n\n\\section{Configuring the Sort Widget}\\label{configuring-the-sort-widget}\n\nThree things can be done from the Configuration screen:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Editing existing Sort options\n\\item\n  Deleting options\n\\item\n  Adding options\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-sort-configuration.png}\n\\caption{From the Sort widget's configuration, add, edit, or remove Sort\noptions.}\n\\end{figure}\n\nTo access the widget configuration screen, open the widget Options menu\n(\\includegraphics{./images/icon-app-options.png}) and click\n\\emph{Configuration}.\n\nEach Sort option has two fields: \\emph{Label} and \\emph{Field}.\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Label}]\nSet the displayed label for the type of sort being configured.\n\\item[\\textbf{Field}]\nThe \\texttt{fieldName} of the indexed field to sort. Most of the time\nthis is a\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/keyword.html}{keyword}\nfield. Other acceptable options are \\texttt{date} and any\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/number.html}{numeric\ndatatype}. There's even a way for persistent search administrators to\ncoerce \\texttt{text} fields into behaving with the Sort widget. Keep\nreading for details.\n\\end{description}\n\n\\subsection{Finding Sortable Fields}\\label{finding-sortable-fields}\n\nTo find the fields available for use in the Sort widget, Users with the\nproper permissions can navigate to \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{Search}. From there, open the Field\nMappings tab and browse the mappings for each index. Scroll to the\n\\texttt{properties} section of the mapping, and find any\n\\texttt{keyword} field, \\texttt{date} field, or a field with any numeric\ndatatype. The \\texttt{type} field is instructive:\n\n\\begin{verbatim}\n\"type\" : \"keyword\"\n\n\"type\" : \"date\"\n\n\"type\" : \"long\"\n\\end{verbatim}\n\nWhat if you really need to sort by a \\texttt{text} field? You can do it\nby adding a new version of the field to the index, with the type\n\\texttt{keyword}. Don't worry; you don't need to code anything to do\nthis. From the field mappings screen mentioned above, look at the\n\\texttt{firstName} field in the index called\n\\texttt{liferay-{[}companyID{]}}. In fact, look at the next entry as\nwell:\n\n\\begin{verbatim}\n\"firstName\" : {\n    \"type\" : \"text\",\n    \"store\" : true\n},\n\"firstName_sortable\" : {\n    \"type\" : \"keyword\",\n    \"store\" : true\n},\n\\end{verbatim}\n\nThere's a corresponding field with the suffix \\texttt{\\_sortable}, and\nof the correct type for sorting (\\texttt{keyword}). How did that get\nthere? Via the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Lucene\\%20Search}{portal\nproperty}\n\n\\begin{verbatim}\nindex.sortable.text.fields=firstName,jobTitle,lastName,name,screenName,title\n\\end{verbatim}\n\nAll the text fields listed here have a \\texttt{fieldName\\_sortable}\ncounterpart created automatically in the index. To add more, copy this\nvalue into a\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}}\nfile into your Liferay Home folder, add any new field names you need to\nsort by, and restart the server.\n\n\\subsection{Adding New Sort Options}\\label{adding-new-sort-options}\n\nTo sort by the new field, use the plus symbol below any option's\n\\emph{Field} configuration make sure to use the\n\\texttt{fieldName\\_sortable} version of the field in the widget\nconfiguration.\n\nTo add a new sort option that's already of the proper datatype, use the\nplus symbol below any option's \\emph{Field} configuration and fill in\nthe fields. The order of options here in the configuration screen\nmatches the order Users see in the select list while configuring the\nwidget for their search.\n\n\\subsection{Editing and Deleting Sort\nOptions}\\label{editing-and-deleting-sort-options}\n\nTo edit an existing option, edit the text in its configuration section.\n\nTo delete an existing option, use the minus symbol below its\n\\emph{Field} configuration.\n\n\\section{Controlling the Sort Order}\\label{controlling-the-sort-order}\n\nTo control the order for the sort option, add a plus or minus symbol\nafter the \\texttt{fieldName}. Look how it's done for the existing sort\noptions labeled \\emph{Created} and \\emph{Created (oldest first)} to\nunderstand how it works:\n\n\\textbf{Label:} \\emph{Created} \\textbf{Field:} \\texttt{createDate-}\n\nThe \\texttt{-} sign following the field name indicates that the order is\n\\emph{descending}. Choosing to sort with this brings search results\ncreated most recently to the top of the list.\n\n\\textbf{Label:} \\emph{Created (oldest first)} \\textbf{Field:}\n\\texttt{createDate+}\n\nThe \\texttt{+} sign following the field name indicates that the order is\n\\emph{ascending}. Choosing to sort with this brings the oldest (by\ncreation date) results to the top of the list.\n\n\\chapter{Search Results Behavior}\\label{search-results-behavior}\n\nThe previous article covered ways to display search results. This\narticle covers these additional Search Results concepts and\nconfigurations:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[filtering-results-with-facets]{Filtering search results with\n  facets}\n\\item\n  \\hyperref[search-results-relevance]{Understanding search results\n  relevance}\n\\item\n  \\hyperref[permissions-and-search-results]{The effect of permissions on\n  search results}\n\\item\n  \\hyperref[search-and-staging]{Search results in the staging\n  environment}\n\\item\n  \\hyperref[result-summaries]{Search results summaries}\n\\item\n  \\hyperref[highlighting]{Search results term highlighting}\n\\end{itemize}\n\n\\section{Filtering Results with\nFacets}\\label{filtering-results-with-facets}\n\nResults are filtered using \\emph{facets}. Most users have encountered\nsimilar filtering capabilities in other applications, particularly\nduring commerce activities. Users enter a search term, are presented\nwith a list of results and search facets, which you can think of as\nbuckets that group results together if they share a common\ncharacteristic.\n\nAdministrators can configure facets. Read about\n\\href{/docs/7-2/user/-/knowledge_base/u/facets}{configuring facets} to\nlearn more.\n\n\\section{Search Results Relevance}\\label{search-results-relevance}\n\nThe search engine decides which results appear at the top of the list\nusing the concept of \\emph{relevance}. Relevance is a score calculated\nby the search engine. There are numerous factors contributing to the\ntotal score of a returned document, and all of the implementation\ndetails of how relevance scoring works are algorithms provided by the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/current/relevance-intro.html\\#relevance-intro}{search\nengine}.\n\n\\section{Permissions and Search\nResults}\\label{permissions-and-search-results}\n\nUsers lacking\n\\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{VIEW\npermission} on an asset don't see it in the search results. A logged in\nUser with the Site Administrator role likely sees more search results\nthan a guest User to the site.\n\nIn the background, there are two rounds of permissions checks. The first\npermissions check, \\emph{pre-filtering}, happens in the search engine's\nindex. It's faster than checking database permissions information, but\noccasionally the search index can have stale permissions information. To\nensure the search engine's index has correct, up-to-date permissions\ninformation, a second, last-second permissions check,\n\\emph{post-filtering}, is performed on the results prior to their\ndisplay.\n\n\\section{Initial Permissions\nChecking}\\label{initial-permissions-checking}\n\nThe first round of search results permissions filtering adds filter\nclauses to the search query. This ensures that results return from the\nsearch engine pre-filtered, containing results the current User can\nview.\n\nThis initial permission checking is configurable at \\emph{Control Panel}\n→ \\emph{Configuration} → \\emph{System Settings} → \\emph{Search} →\n\\emph{Permission Checker}. It includes two system level settings to\nconfigure how search processes User permissions.\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Include Inherited Permissions}]\nIgnore this setting. It's deprecated, no longer used anywhere, and will\nbe removed in a future release.\n\\item[\\textbf{Permissions Term Limit}]\nLimits the number of permission search clauses added to the search query\nbefore this level of permission checking is aborted. Permission checking\nthen relies solely on the final permission filtering described below.\n\\end{description}\n\nThe only reason to limit permissions terms is performance. Users with\nadministrative access to lots of sites and organizations generate many\npermissions terms added to the query. Too many terms in a query can make\nthe search engine time out.\n\n\\section{Final Permissions Checking}\\label{final-permissions-checking}\n\nA final round of permission checking happens prior to presenting results\nin the UI. For example, the User searches for \\emph{liferay}, and the\nsearch engine returns all relevant forum posts. As the Search Results\niterates through the list of relevant forum posts, it performs one last\npermission check of the post to ensure the User can view the post and\nits categories. If a matching forum post exists in a category the User\ndoesn't have permission to view, it isn't displayed in the list of\nsearch results.\n\nThis final round of permission checking is configurable at \\emph{Control\nPanel} → \\emph{Configuration} → \\emph{System Settings} → \\emph{Search} →\n\\emph{Default Search Result Permission Filter}. It includes two\nsettings:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The first setting, Permission Filtered Search Result Accurate Count\n  Threshold, specifies the maximum number of search results to\n  permissions-filter before results are counted. A higher threshold\n  increases count accuracy, but decreases performance. Since results in\n  the currently displayed page are always checked, any value below the\n  search results pagination delta effectively disables this behavior.\n\\item\n  The second setting, Search Query Result Window Limit, sets the maximum\n  batch size for each permission checking request. . This is again\n  impacted by pagination. For example, if there are 100 results per\n  page, and a User wants to jump all the way to page 200 of the search\n  results, all results between page one and 200 must be checked to\n  ensure the User has permission. That's 20,000 results to permissions\n  check. Doing this in one trip to and form the search engine can result\n  in performance issues. Set the maximum batch size for each permission\n  checking request.\n\\end{enumerate}\n\n\\section{Search and Staging}\\label{search-and-staging}\n\nWith \\href{/docs/7-2/user/-/knowledge_base/u/staging-content}{staging},\ncontent is placed first in a preview and testing environment before\nbeing published for consumption by end Users (on the live site). Content\nadded to the search index is marked so that the search API can decipher\nwhether an item is live or not. In the live version of the site, only\ncontent that's marked for the live site is searchable.\n\nIn the staged version of the site, all content---live or staged---is\nsearchable.\n\n\\section{Result Summaries}\\label{result-summaries}\n\nA result summary includes the information from a document that the\nasset's developer felt is most useful to end Users searching for the\nasset. Each asset can have different fields included in the summary. For\nassets with text content, a common summary format includes the\n\\emph{title} and some of the \\emph{content}, with title displayed first.\nThe asset type always appears on the second line, and a snippet of the\ncontent that matches the search term is on the last line. Assets without\ncontent fields, like Documents and Media documents, display the\ndescription instead.\n\nUsers are different. Only the User's full name and the asset type (User)\nappear in User result summaries.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-results-user.png}\n\\caption{User summaries contain only the User's full name.}\n\\end{figure}\n\nFor assets that contain other assets (Web Content and Documents \\& Media\nfolders) or whose content is not amenable to display (Dynamic Data List\nRecords and Calendar Events), it makes more sense to display the title,\nasset type, and description in results summaries. There'd never be\nanything in a content field for these assets.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-results-folder.png}\n\\caption{Documents and Media and Web Content folders include titles and\ndescriptions in their summaries.}\n\\end{figure}\n\nThe asset developer determines which fields are summary-enabled, but\nthere's logic invoked at search time that determines precisely the part\nof the summary fields to display. For example, a \\texttt{content} field\ncan have a lot of text, but the summary doesn't show it all. Instead, it\nshows a relevant snippet of the field's text. If the keyword searched\nfor is present in the summary field, that portion of the field is used\nin the summary. In addition, the matching keyword is highlighted in the\nsummary.\n\n\\section{Highlighting}\\label{highlighting}\n\nBy now you've probably noticed that search terms appearing in the\nsummary are highlighted by default. If this is undesirable, disable it\nin the widget configuration screen.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-results-highlight.png}\n\\caption{Some document summaries have lots of highlights if the search\nterm matches text that appears in the summary.}\n\\end{figure}\n\nHighlighting is a helpful visual cue that hints at why the result is\nreturned, but beware. A hit can score well and thus be returned near the\ntop of the results, without having any highlights in the summary. That's\nbecause not all indexed fields appear in the summary. Consider a User\nnamed Arthur C. Clarke. He has an email address of\n\\emph{acc@authors.org}, which is searchable. Because results summaries\nfor Users only contain the full name of the User, searching for Mr.\nClarke by his email address returns the User, but no term is\nhighlighted.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-results-no-highlight.png}\n\\caption{Results that match the search term won't always have\nhighlights.}\n\\end{figure}\n\nThere are additional cases where search results don't have highlighting.\n\n\\chapter{Search Insights}\\label{search-insights-1}\n\n\\textbf{{[}Feature intended for testing and development only{]}}\n\n\\textbf{{[}Works with Elasticsearch only{]}}\n\nAdd the Search Insights Widget to the Search Page to inspect two things:\n\n\\begin{itemize}\n\\item\n  The query string that's constructed by the back-end search code when\n  the User enters a keyword\n\\item\n  The response string returned from the search engine\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The JSON returned as a request string is pruned from\nseveral Elasticsearch query defaults for clarity. To see the full\nrequest JSON that Elasticsearch processed, adjust the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/6.x/logging.html}{Elasticsearch\nserver's logging}.\n\n\\noindent\\hrulefill\n\nIn 7.0, the Insights widget now adds the response string to the widget's\noutput, and the \\emph{Enable Score Explanation} option (enabled by\ndefault) prints a relevance score explanation for each returned result.\n\nWhen a search query is processed, results are returned. The concept of\n\\emph{Relevance} determines how well results match the query. The Score\nexplanation for returned search documents helps clarify seemingly odd\nresults, letting you adjust the relevancy scoring process by making\nmatches in certain fields count for more (\\emph{boosting} the fields is\nthe term for this).\n\n\\section{Inspecting The Search Query\nString}\\label{inspecting-the-search-query-string}\n\nTo see the Search Insights widget in action, navigate to a Search Page\nin your test server and add it from the Add menu\n(\\includegraphics{./images/icon-add-widget.png}).\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-insights-default.png}\n\\caption{The Search Insights widget is helpful during testing and\ndevelopment.}\n\\end{figure}\n\nOnce you search for keywords that return Search Results, the Search\nInsights portlet displays the returned query string in all its glory.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-insights-test-search.png}\n\\caption{The full query string isn't for the faint of heart. This\nexample is clipped to spare the reader.}\n\\end{figure}\n\n\\section{Explaining Search Results}\\label{explaining-search-results}\n\nTo enable or disable the Explain option,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Search Insight widget's Configuration screen.\n\\item\n  There's just one option: \\emph{Enable Score Explanation}. It's a\n  boolean field that's enabled by default. De-select it to disable the\n  explanation of each result's relevance score.\n\\end{enumerate}\n\nUnder the hood, the Explain option in the Search Insights widget is\nexposing an Elasticsearch API:\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-explain.html}{Explain}.\nSee the Elasticsearch documentation for more details.\n\nHere's an abbreviated portion of the scoring explanation for the Search\nDocument of the Test Test User when the searched keyword was\n\\emph{test}:\n\n\\begin{verbatim}\n```json\n_explanation\":{  \n   \"value\":9.461341,\n   \"description\":\"sum of:\",\n   \"details\":[  \n      {  \n         \"value\":9.461341,\n         \"description\":\"sum of:\",\n         \"details\":[  \n            {  \n               \"value\":1.0,\n               \"description\":\"emailAddress:*test*\",\n               \"details\":[  \n\n               ]\n            },\n            {  \n               \"value\":5.0,\n               \"description\":\"userName:*test*^5.0\",\n               \"details\":[  \n\n               ]\n            },\n            {  \n               \"value\":0.72928625,\n               \"description\":\"sum of:\",\n               \"details\":[  \n                  ... \n\n            { \n               \"value\":1.0027686,\n               \"description\":\"sum of:\",\n               \"details\":[  \n                  ...\n                  {  \n            {  \n               \"value\":0.72928625,\n               \"description\":\"sum of:\",\n               \"details\":[  \n                  ...\n            {  \n               \"value\":1.0,\n               \"description\":\"screenName:*test*\",\n               \"details\":[  \n\n               ]\n            }\n         ]\n      },\n      ...\n   ]\n}}]}\n```\n\\end{verbatim}\n\nNow you're able to see the entire query string, the response string, and\nhow each returned Search Document was scored.\n\n\\chapter{Searching for Localized\nContent}\\label{searching-for-localized-content}\n\nLiferay DXP supports setting a virtual instance-wide\n\\href{/docs/7-2/user/-/knowledge_base/u/more-platform-section-instance-settings\\#localization}{default\nlanguage} and setting a In addition, many out of the box assets\n\\href{/docs/7-2/user/-/knowledge_base/u/other-content-options\\#localizing-content}{support\ntranslation}.\n\nHow an asset's fields are indexed in the search engine plays an\nimportant role in the end user's experience. Not all assets are indexed\nin a way that supports searching in a language other than the default\nlanguage. Even assets that are translatable might not support searching\nfor the content in that language.\n\nIn short, these assets contain text fields supporting localized search:\n\n\\noindent\\hrulefill\n\nAsset \\textbar{} Fields \\textbar{} Localized Search Approach \\textbar{}\nContent Page \\textbar{} \\texttt{title} \\textbar{} 2 \\textbar{} Documents\nand Media Document \\textbar{} \\texttt{content} \\textbar{} 3 \\textbar{}\nCalendar \\textbar{} \\texttt{name}, \\texttt{description} \\textbar{} 1\n\\textbar{} Calendar Booking \\textbar{} \\texttt{title},\n\\texttt{description} \\textbar{} 1 \\textbar{} Dynamic Data List Record\n\\textbar{} \\texttt{content} \\textbar{} 1 \\textbar{} Form Record\n\\textbar{} \\texttt{content} \\textbar{} 1 \\textbar{} Web Content Article\n\\textbar{} \\texttt{title}, \\texttt{content}, \\texttt{description}\n\\textbar{} 1 \\textbar{} Asset Category\\emph{ \\textbar{} \\texttt{title},\n\\texttt{description} \\textbar{} 1 \\textbar{} Asset Tag} \\textbar{}\n\\texttt{assetTagNames} \\textbar{} 1 \\textbar{} Wiki Page \\textbar{}\n\\texttt{title}, \\texttt{content} \\textbar{} 2 \\textbar{} Blogs Entry\n\\textbar{} \\texttt{content}, \\texttt{title} \\textbar{} 2 \\textbar{}\nMessage Boards Message \\textbar{} \\texttt{title}, \\texttt{content}\n\\textbar{} 2 \\textbar{}\n\n\\noindent\\hrulefill\n\n* Asset tags and categories don't have dedicated documents in the index.\nInstead, their indexed fields are added to the tagged or categorized\nasset's document.\n\nThere are three localized search approaches represented in the table:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Fully Localized: The asset itself is localizable (in other words,\n  translatable), and its translated fields are indexed into their\n  respected locales.\n\\item\n  Fully Localized for Search: Even though the asset itself is not\n  localizable/translatable, its fields are indexed into \\emph{all} the\n  supported locales in the site. This is a new approach, starting with\n  7.0.\n\\item\n  Site-Localized for search: The asset's fields are indexed with the\n  site's locale appended.\n\\end{enumerate}\n\nThere are also assets with text fields and no localization support,\nmeaning that they always index the plain field, without a locale\nappended (e.g., \\texttt{title} is not localized, but\n\\texttt{title\\_en\\_EN} is localized). That means they'll always be\nanalyzed by the default language analyzer, and do not support localized\nsearch in any capacity.\n\n\\section{What is Localized Search?}\\label{what-is-localized-search}\n\nIn localized search, fields are indexed with locale information appended\n(for example, \\texttt{en\\_US} for English, making a localized title\nfield indexed as \\texttt{title\\_en\\_US}). It's then passed to the proper\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/analysis-lang-analyzer.html}{language\nanalyzer} in the search engine so that the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/analysis.html}{analysis}\nprocess is performed properly. Each localization approach is covered\nbelow.\n\n\\section{Fully Localized}\\label{fully-localized}\n\nFully localized search works like this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  One or more of an asset's fields are localizable in the user interface\n  and database (the locale is appended based on the asset creator's\n  language selection).\n\\item\n  The fields are indexed with the appended locale and analyzed with the\n  corresponding language analyzer.\n\\item\n  At search time, properly indexed and analyzed content is returned\n  according to search engine's relevance algorithms.\n\\end{enumerate}\n\nThis is the ideal approach for assets that support translation of some\nor all fields outside of the search context.\n\n\\section{Fully Localized for Search}\\label{fully-localized-for-search}\n\nAssets fully localized for search work like this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The asset's fields are not localizable in the user interface or\n  database.\n\\item\n  For at least one text field being indexed, the asset has indexed\n  localized fields for every locale available in the site.\n\\item\n  At search time the result document is returned regardless of the\n  search locale, because the content is available in all locales of the\n  site.\n\\end{enumerate}\n\n\\section{Site-Localized Search}\\label{site-localized-search}\n\nSite-localized search works like this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  The asset's indexed fields are appended with the locale of the site\n  (set in Site Settings) and analyzed with the corresponding language\n  analyzer.\n\\item\n  If the site language is changed, reindexing is required to append the\n  proper site locale to the indexed fields and analyze with the\n  corresponding language analyzer.\n\\item\n  At search time, if content exists matching the language of the site,\n  it's properly returned according to the search engine's algorithms.\n\\end{enumerate}\n\nNot all assets support localized search, however. Refer to the table at\nthe beginning of this article for which assets and fields are localized\nfor search.\n\n\\section{Assets Supporting Localized\nSearch}\\label{assets-supporting-localized-search}\n\nWhether an asset supports localized search depends on how the asset was\nindexed in the search engine. At this time, no cohesive pan-asset\napproach to indexing assets for localized search exists. Localized\nsearch support is currently limited to the following assets:\n\n\\section{Web Content Articles:}\\label{web-content-articles}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  The \\texttt{title}, \\texttt{content}, and \\texttt{description} fields\n  for each Web Content Article support fully localized search.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** In 7.0 the default (non-localized) version of these\n fields are not indexed for Web Content Articles. Therefore, any custom\n `IndexerPostProcessor`, `ModelDocumentContributor` or\n `QueryPreFilterContributor` relying on the presence of fields `title`,\n `content` and `description` must be updated to use the localized version\n (e.g., `title_en_US`).\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{itemize}\n\\tightlist\n\\item\n  At search time, matching results (with any locale appended) can be\n  returned.\n\\end{itemize}\n\n\\section{Categories:}\\label{categories}\n\n\\begin{itemize}\n\\item\n  The \\texttt{name} and \\texttt{description} fields support fully\n  localized search.\n\\item\n  At search time, matching results (with any locale appended) can be\n  returned.\n\\end{itemize}\n\n\\section{Documents and Media File\nEntries:}\\label{documents-and-media-file-entries}\n\n\\begin{itemize}\n\\item\n  The \\texttt{content} field (which contains the content of an uploaded\n  file) supports site-localized search.\n\\item\n  No other fields are indexed with a locale. This means they're always\n  analyzed using the default language analyzer.\n\\end{itemize}\n\n\\section{Dynamic Data Mapping\nFields:}\\label{dynamic-data-mapping-fields}\n\n\\begin{itemize}\n\\item\n  Dynamic Data Mapping (DDM) Fields include all form fields created in\n  the Forms application and all fields created in Dynamic Data List Data\n  Definitions and Web Content Structures.\n\\item\n  DDM Fields support fully localized search, with the exception that\n  results can only be returned in the current display locale where the\n  search is taking place.\n\\end{itemize}\n\n\\section{Examples}\\label{examples}\n\nTo see localized search in action, refer to the examples below.\n\n\\section{Fully Localized Search for Web Content\nArticles}\\label{fully-localized-search-for-web-content-articles}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a Basic Web Content article in English:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Title: \\emph{What time is it?}\n  \\item\n    Summary: \\emph{It's soccer time!}\n  \\item\n    Content: \\emph{That's right, it's time for soccer. The 2018 World\n    Cup is behind us, and teams all over the US are gearing up for\n    soccer season. It's never too early to start practicing.}\n  \\end{itemize}\n\\item\n  Add a second article in English:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Title: \\emph{What is the best soccer team ever?}\n  \\item\n    Summary: \\emph{There are many good teams? Which is the best?}\n  \\item\n    Content: \\emph{Here are the 10 best teams in the world: 1. The Lunar\n    Resort's Club Team, Waxing Crescent FC\\ldots{}}\n  \\end{itemize}\n\\item\n  Add a Portuguese (\\emph{pt-BR}) translation for each field of the\n  second article:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Title: \\emph{Qual time de futebol é o melhor de todos os tempos?}\n  \\item\n    Summary: \\emph{Existem muitas boas equipes. Qual é o melhor?}\n  \\item\n    Content: \\emph{Aqui estao as 10 melhores equipes do mundo: 1.\n    Selecao brasileira de Futebol 2. O time do Resort Lunar, Waxing\n    Crescent FC\\ldots{}}\n  \\end{itemize}\n\\item\n  Find a search bar widget and enter \\emph{time} as the keyword.\n\n  The first article is returned, and so is the appropriate translation\n  of the article about soccer teams (because \\emph{time} in Portuguese\n  translates to the English word \\emph{team}). Note that if your search\n  context is English, searching for the word \\emph{time} returns the\n  English translation of the Web Content, which does not itself contain\n  the matched keyword. The Portuguese translation contains the matching\n  keyword, while the English translation is returned for English\n  speaking search users.\n\\end{enumerate}\n\nIn fully localized search, fields are appended with the proper locale,\nand even fields with a locale other than the User's display context are\nreturned if they contain matches to the searched keyword.\n\n\\section{Site-Localized Search for Documents and\nMedia}\\label{site-localized-search-for-documents-and-media}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a text file named \\texttt{search-test.txt} with the following\n  contents:\n\n\\begin{verbatim}\nMeu time de futebol favorito é o melhor\n\\end{verbatim}\n\\item\n  Upload it as a Basic Document to the Documents and Media application.\n\\item\n  If your site's language is currently set to English, adding this file\n  appends its content field with the \\emph{en-US} locale.\n\\item\n  Search in the site for the keyword \\emph{time}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/search-site-localized1.png}\n  \\caption{Even though the content of this DM File is written in\n  Portuguese, it was appended with the \\emph{en} locale, so it's\n  searchable in an English language site.}\n  \\end{figure}\n\n  The file is returned because even though the text in the file is\n  Portuguese, the locale appended to its \\emph{content} field is for\n  English.\n\\item\n  Now change the Site's default language to \\emph{Portuguese (Brazil)}.\n  Use Site Settings → Languages to accomplish this.\n\\item\n  Now search for \\emph{time} in the site, and the document is not\n  returned in the results, because the search is looking for the\n  \\emph{pt} locale.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/search-site-localized2.png}\n  \\caption{The uploaded DM File doesn't appear when the site language is\n  changed, because only fields with the site's locale are searched.}\n  \\end{figure}\n\\item\n  Now go to Control Panel → Configuration → Search, and click\n  \\emph{Execute} next to \\emph{Reindex all search indexes.}\n\\item\n  Search for \\emph{time} in the site's Search Bar again, and now the\n  document is returned in the results, because the content field's\n  locale was changed from \\emph{en\\_US} to \\emph{pt\\_BR} when reindexed.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/search-site-localized3.png}\n  \\caption{Once the field is reindexed with the site's locale, it can be\n  returned as a search result in the site.}\n  \\end{figure}\n\\end{enumerate}\n\nIf an asset supports site-localized search, its fields must be reindexed\nafter the site language is changed in order to be returned as search\nresults.\n\n\\chapter{Configuring Search}\\label{configuring-search}\n\n\\emph{Configuring Search} could mean lots of different things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  System scoped search configuration\n\\item\n  Reindexing to make sure the search indexes are current with the\n  database\n\\item\n  Tweaking the search widgets added to pages\n\\item\n  Creating new Search Pages\n\\item\n  Configuring the connectors that let Liferay DXP and the search engine\n  communicate\n\\end{itemize}\n\nIn fact, \\emph{Configuring Search} means all those things. This is a\nhigh level overview of what search behavior is configurable out of the\nbox, and importantly, \\emph{where} to find search configuration options.\n\n\\section{System Scoped Search\nConfiguration}\\label{system-scoped-search-configuration}\n\nSystem scoped search configurations are primarily found in\n\\href{/docs/7-2/user/-/knowledge_base/u/system-settings}{System\nSettings}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Control Panel} → \\emph{Configuration} → \\emph{System\n  Settings}.\n\\item\n  Click the \\emph{Search} category under the Platform section.\n\n  Alternatively, search for \\emph{Search}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-category-system-settings.png}\n\\caption{There are numerous system scoped entries for search in System\nSettings.}\n\\end{figure}\n\nThese system scoped configurations are available in System Settings:\n\n\\section{Default Keyword Query}\\label{default-keyword-query}\n\nThe Default Keyword Query entry contains one setting:\n\n\\texttt{disabledEntryClassNames}: The\n\\texttt{DefaultKeywordQueryContributor} code automatically adds\n\\texttt{description}, \\texttt{userName}, and \\texttt{title} fields to\nthe keyword search query. Specify the entry class names\n\\texttt{DefaultKeywordQueryContributor} should ignore.\n\n\\section{Default Search Result Permission\nFilter}\\label{default-search-result-permission-filter}\n\nThe Default Search Result Permission Filter entry allows configuration\nof \\emph{post-filtering permission checking} (database permission\nchecking that occurs after the results are returned from the search\nindex). Read\n\\href{/docs/7-2/user/-/knowledge_base/u/search-results-behavior\\#final-permissions-checking}{here}\nfor more information on these settings:\n\n\\begin{itemize}\n\\item\n  \\texttt{permissionFilteredSearchResultAccurateCountThreshold}\n\\item\n  \\texttt{searchQueryResultWindowLimit}\n\\end{itemize}\n\n\\section{Index Status Manager}\\label{index-status-manager}\n\nThe Index Status Manager entry has one setting:\n\n\\texttt{indexReadOnly}: Suspends all indexing operations and writes to\nthe search engine. Searches return only the documents already indexed.\nThis is useful for speeding up large data imports, but it should be\ndisabled and a full re-index executed once the import is finished.\n\n\\section{Indexer Writer Helper}\\label{indexer-writer-helper}\n\nThe Index Writer Helper entry contains only one valid entry. The second,\n\\texttt{indexReadOnly}, is deprecated and unused, so setting it has no\neffect. Use \\texttt{indexReadOnly} from the\n\\hyperref[index-status-manager]{Index Status Manager} instead.\n\n\\texttt{indexCommitImmediately}: When \\emph{true} (the default), each\nwrite request forces the search engine to refresh the index reader,\npotentially flushing transactions to disk. This may negatively impact\nsearch engine performance. The default behavior is to commit immediately\nfor index writing on individual assets (e.g.~add blog, update blog) but\ndelay commits for bulk index writing operations (e.g.~index all users,\nindex all form entries) until all entries have been sent to the search\nengine. Setting this to false changes the behavior for individual index\noperations, and may cause applications like Asset Publisher to exhibit a\ndelayed response when showing newly added content. See the\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/current/near-real-time.html}{Elasticsearch\ndocumentation} for more information.\n\n\\section{Index Registry}\\label{index-registry}\n\nConfigure the buffering of index requests:\n\n\\texttt{buffered}: Disable or configure the buffering of indexing\nrequests. To stop the buffering of index requests, choose\n\\emph{Disabled}.\n\n\\texttt{bufferedExecutionMode}: Allows administrators to select a\ndifferent \\texttt{IndexerRequestBufferExecutor}, used to execute a\n\\texttt{IndexerRequest}. One implementation of the executor is provided\nout of the box (\\emph{DEFAULT}). When a developer creates and deploys a\nnew \\texttt{IndexerRequestBufferExecutor} implementation, one of the\nproperties they provided is a \\texttt{buffered.execution.mode} which\nmakes the implementation selectable from System Settings.\n\n\\texttt{maximumBufferSize}: If buffering is enabled, set the Maximum\nBuffer Size so that any additional indexing requests are executed\nimmediately.\n\n\\texttt{minimumBufferAvailabilityPercentage}: When the capacity of the\nbuffer has only the specified percent of space left, the existing\nrequests in the buffer are executed in one batch and removed from the\nbuffer.\n\n\\section{Index Query Preprocessor}\\label{index-query-preprocessor}\n\nThis entry has one repeatable property (use array syntax if you're\ndefining via\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-configuration-files}{OSGi\nconfiguration file}):\n\n\\texttt{fieldNamePatterns}: Fields with names matching the patterns set\nhere are treated as non-analyzed keyword fields. Instead of scored full\ntext queries, matching is performed by non-scored wildcard queries. This\nis a resource intensive operation that degrades search engine\nperformance as indexes grow larger. For substring matching, relying on\nthe\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/analysis-ngram-tokenizer.html}{ngram\ntokenizer} usually performs better.\n\n\\section{Reindex}\\label{reindex}\n\nThis entry contains only one property:\n\n\\texttt{indexingBatchSizes}: Set the number of documents indexed per\nbatch for model types that support batch indexing. Defaults to 10000.\nFor models with large documents, decreasing this value may improve\nstability when executing a full re-index.\n\n\\section{Engine Helper}\\label{engine-helper}\n\nThis entry has one repeatable property (use array syntax if you're\ndefining via\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-configuration-files}{OSGi\nconfiguration file}):\n\n\\texttt{excludedEntryClassNames}: Exclude an asset type from being\nsearched in the catchall query constructed for the Search application.\nFor example, fields of the Organization asset must be indexed to be\nsearchable from the Users and Organizations application, but should not\nbe searched in the Search application. Thus, Organizations are added to\n\\texttt{excludedEntryClassNames}.\n\n\\section{Permission Checker}\\label{permission-checker}\n\nConfigure \\emph{pre-filtering permission checking} (permission checking\non the search index) behavior. See\n\\href{/docs/7-2/user/-/knowledge_base/u/search-results-behavior\\#initial-permissions-checking}{here}\nfor more information on these properties:\n\n\\begin{itemize}\n\\item\n  \\texttt{includeInheritedPermission}\n\\item\n  \\texttt{permissionTermsLimit}\n\\end{itemize}\n\n\\section{Title Field Query Builder}\\label{title-field-query-builder}\n\nConfigure how search responds to matches on the Title field of a\ndocument.\n\n\\textbf{Exact Match boost:} Give an additional boost when searched\nkeywords exactly match the \\texttt{title} field of a document.\n\n\\textbf{Maximum Expansions:} Limit the number of documents to return\nwhen matching searched keywords to the \\texttt{title} field as a phrase\nprefix. See Elasticsearch's\n\\href{https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-match-query-phrase.html}{Match\nPhrase Query documentation} for more information.\n\n\\section{Elasticsearch 6}\\label{elasticsearch-6}\n\nConfigure the connection between Liferay DXP and Elasticsearch 6. See\n\\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector}{here}\nfor more information on these properties:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{clusterName}\n\\item\n  \\texttt{operationMode}\n\\item\n  \\texttt{indexNamePrefix}\n\\item\n  \\texttt{numberOfIndexReplicas}\n\\item\n  \\texttt{numberOfIndexShards}\n\\item\n  \\texttt{bootstrapMlocakAll}\n\\item\n  \\texttt{logExceptionsOnly}\n\\item\n  \\texttt{retryOnConflict}\n\\item\n  \\texttt{zenDiscoveryUnicastHostsPort}\n\\item\n  \\texttt{networkHost}\n\\item\n  \\texttt{networkBindHost}\n\\item\n  \\texttt{networkPublishHost}\n\\item\n  \\texttt{transportTcpPort}\n\\item\n  \\texttt{transportAddresses}\n\\item\n  \\texttt{clientTransportSniff}\n\\item\n  \\texttt{clientTransprtIgnoreClusterName}\n\\item\n  \\texttt{clientTransportPingTimeout}\n\\item\n  \\texttt{clientTransportNodesSamplerInterval}\n\\item\n  \\texttt{HttpEnabled}\n\\item\n  \\texttt{HttpCrsEnabled}\n\\item\n  \\texttt{HttpCorsAllowOrigin}\n\\item\n  \\texttt{HttpCorsConfigurations}\n\\item\n  \\texttt{additionalConfigurations}\n\\item\n  \\texttt{additionalIndexConfigurations}\n\\item\n  \\texttt{overrideTypeMappings}\n\\item\n  \\texttt{synchronizedSearch}\n\\end{itemize}\n\n\\section{Search Web}\\label{search-web}\n\nThis entry contains one property:\n\\texttt{classicSearchPortletInFrontPage}: Revert the default search\nexperience from using the new Search Widgets to the classic Search\nPortlet that was standard in past releases. See\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-search-pages\\#legacy-search-experience}{here}\nfor more information.\n\n\\section{Search Administration}\\label{search-administration}\n\nIn \\emph{Control Panel} → \\emph{Configuration} → \\emph{Search} there are\ntwo administrative UIs: Index Actions and Field Mappings\n\n\\subsection{Index Actions}\\label{index-actions}\n\nIn Index Actions, re-index one of these:\n\n\\begin{verbatim}\n- All indexable assets\n- An individual indexable asset\n- All spell check indexes\n\\end{verbatim}\n\n\\subsection{Field Mappings}\\label{field-mappings-1}\n\nThe Field Mappings tab shows you all field mappings that are effective\nin the system, by index. Currently, you can view the mappings, copy\nthem, zoom in or out, and view them with a dark theme. Look for added\nfunctionality to this UI in future versions.\n\n\\section{Portal Properties}\\label{portal-properties}\n\nPortal properties are system scoped configurations as well. The\n\\href{https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Lucene\\%20Search}{Lucene\nSearch} portal properties configure low level search behavior. Review\nthe properties and their descriptions and determine if they apply to\nyour search requirements.\n\n\\section{Site Scoped Search\nConfiguration}\\label{site-scoped-search-configuration}\n\nSearch isn't configurable at the Site Scope by the strict definition of\n\\href{/docs/7-2/user/-/knowledge_base/u/setting-up\\#configuration-scope}{Site\nScoped Configuration}. However,\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-search-pages}{Search\nPages} influence site-specific search behavior. Commonly, Search Pages\ncontain search widgets configured to search for all content within a\nparticular Site.\n\nIn addition, the Header Search (the Search Bar embedded in every Site\npage by default), whether populated by the new Search Bar widget or the\nlegacy Search portlet, is Site scoped. Only one instance of the Header\nSearch application exists per Site, and configuring it in one page\ncontext configures it for the entire Site.\n\nBecause of the modularity of Search, there are some important\nconfiguration nuances to be aware of when using the new Search widgets:\n\n\\begin{itemize}\n\\item\n  If the Header Search uses the Search Bar widget, its configuration\n  always requires a \\emph{destination page} to be set, where Users are\n  redirected to complete their search activity, interacting with the\n  other Search widgets (Results, Facets, Suggestions etc.).\n  \\href{/docs/7-2/user/-/knowledge_base/u/configuring-search-pages}{Search\n  destination pages} are ordinary pages holding the Search widgets. You\n  can have as many pages with Search widgets across the Site as you\n  want.\n\\item\n  Unlike the legacy Search portlet, the new Search Bar widget is\n  instanceable, so one page can contain multiple Search Bar widgets\n  configured differently. All Search Bar instances must point to a\n  Search Page to be effective.\n\\item\n  \\textbf{Important}: if the destination Search Page has a Search Bar\n  widget instance besides the embedded Header Search, the configurations\n  of the Header Search take precedence over the page's widget instance.\n\n  Conversely, searching from a Search Bar widget instance on other pages\n  honors their configurations, even if they differ from the Header\n  Search configuration.\n\\end{itemize}\n\nSee the documentation on\n\\href{/docs/7-2/user/-/knowledge_base/u/searching-for-assets\\#configuring-the-search-bar}{configuring\nof a Search Bar} for more information.\n\n\\section{Widget Scoped Search\nConfiguration}\\label{widget-scoped-search-configuration}\n\nSeveral search widgets are available, and each one has its own\nconfiguration options:\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Search Results}]\nConfigure how search results are displayed. Read\n\\href{/docs/7-2/user/-/knowledge_base/u/search-results}{here} for more\ninformation.\n\\item[\\textbf{Search Bar}]\nConfigure the behavior of how search keywords are processed. See\n\\href{/docs/7-2/user/-/knowledge_base/u/searching-for-assets\\#configuring-the-search-bar}{here}\nfor more information.\n\\item[\\textbf{Search Facets}]\nConfigure each facet's behavior and URL parameters. See\n\\href{/docs/7-2/user/-/knowledge_base/u/facets}{here} for more\ninformation.\n\\item[\\textbf{Search Options}]\nThis is a special case, where configuring this widget defines page\nscoped behavior. Add the Search Option widget to a page and define two\nbooleans for the Search Page:\n\\end{description}\n\n\\begin{itemize}\n\\item\n  Allow Empty Searches: By default, failure to enter a keyword returns\n  no results. Enabling this ensure that \\emph{all} results are returned\n  when no keyword is entered in the Search Bar.\n\\item\n  Basic Facet Selection: By default, facet counts are recalculated after\n  each facet selection. Enable this to turn off facet recounting.\n\\end{itemize}\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Search Suggestions}]\nSuggest better queries and spell check queries. See\n\\href{/docs/7-2/user/-/knowledge_base/u/searching-for-assets\\#search-suggestions}{here}\nfor more information.\n\\item[\\textbf{Search Insights}]\nAdd this to the Search Page to inspect the full query string that's\nconstructed by the back-end search code when the User enters a keyword.\nOnly useful for testing and development.\n\\item[\\textbf{Custom Filter}]\nAdd a widget to the page for each of the filters you'd like applied to\nthe search results. Let search page users see and manipulate the filters\nor make them invisible and/or immutable.\n\\item[\\textbf{Sort}]\nLet Users reorder the search results based on the value of certain\n\\texttt{keyword} fields in the index. For example, show results in\nalphabetic order of the Title field. The default order is determined by\nthe search engine's \\emph{Relevance} calculation.\n\\end{description}\n\n\\chapter{Low Level Search Options: Searching Additional or Alternate\nIndexes}\\label{low-level-search-options-searching-additional-or-alternate-indexes}\n\nLow level search is a new concept in Liferay DXP version 7.2: it's a\nsearch that doesn't go through the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/model-entity-indexing-framework}{Search\nand Indexing Framework}, which is infrastructure used for searching\ndocuments in the Liferay Index.\n\nA common use case for a low level search is to query an index other than\nthe Liferay DXP index. By default,\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-search-pages}{Search\nPages} search the Liferay DXP index, but you can also search another\nindex, as long as it's in the same Elasticsearch cluster (this feature\ndoes not work with Solr).\n\nAdd the Low Level Search Options widget to a search page and configure\nit to direct the search to the alternate index. To search multiple\nindexes from the same page, you can add multiple Low Level Search\nOptions widgets and configure each one with its own Index Name and\nFederated Search Key.\n\nSearching alternate indexes is a low level operation that bypasses the\nLiferay DXP permission checking mechanisms, presenting whatever results\nthe search engine returns. For this reason, only administrators can add\nand configure the Low Level Search Options widget.\n\nTo use the Low Level Search Options widget, add it to a Search Page:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the Add menu (\\includegraphics{./images/icon-add-widget.png}) on\n  the page to open the Add Widgets menu.\n\\item\n  Drag the Low Level Search Options widget (from the Search section),\n  and drop it on the page.\n\\end{enumerate}\n\nIt doesn't do anything unless you configure it.\n\n\\section{Configuring Low Level\nSearch}\\label{configuring-low-level-search}\n\nThere are several configuration options for the widget. Access them by\nclicking the widget Options menu\n(\\includegraphics{./images/icon-app-options.png}) → Configuration, or by\nclicking the hypertext URL in the widget body:\n\n\\emph{Configure additional low level search options in this page.}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-lowlvl-options.png}\n\\caption{The Low Level Options widget has several configuration\noptions.}\n\\end{figure}\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Indexes:}]\nEnter the comma-separated names of the alternative indexes to search. Do\nnot enter the standard Liferay index name.\n\\item[\\textbf{Fields to Return:}]\nEnter the names of the stored fields to be returned from the search\nengine in a comma-separated list. Leave it blank to return all stored\nfields.\n\\item[\\textbf{Contributors to Include:}]\nEnter the ids of registered search contributors to be included in this\nsearch in a comma-separated list of each\n\\texttt{SearchRequestContributor}'s Fully Qualified Class Name (e.g.,\n\\texttt{com.liferay.docs.request.contributor.MySearchRequestContributor}).\nIf not set, all registered search contributors are applied.\n\\item[\\textbf{Contributors to Exclude:}]\nEnter the ids of registered search contributors to be excluded from this\nsearch, in a comma-separated list. If not set, all registered search\ncontributors are applied.\n\\end{description}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} These \\emph{Contributors} are components implementing the\n\\texttt{com.liferay.portal.search.spi.searcher.SearchRequestContributor}\ninterface (provided by the \\texttt{com.liferay.portal.search.spi}\nartifact), which is an extension point (SPI) that intercepts search\nrequests and adds query parts.\n\n\\noindent\\hrulefill\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Federated Search Key:}]\nEnter the key of an alternate search this widget is participating in. If\nnot set, this widget participates in the default search. This value is\nusually the name of an application-defined index.\n\\end{description}\n\n\\section{Example: Searching an Alternate\nIndex}\\label{example-searching-an-alternate-index}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Whether testing on the default search page or creating a new\n  \\href{/docs/7-2/user/-/knowledge_base/u/configuring-search-pages}{Search\n  Page}, include the following widgets (removing extra widgets\n  simplifies the exercise, but is not required for it to work):\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Low Level Search Options\n  \\item\n    Custom Filter\n  \\item\n    Search Bar\n  \\item\n    Search Results\n  \\end{itemize}\n\\item\n  Configure all the widgets to participate in an alternate search, by\n  opening the widget's Options menu\n  (\\includegraphics{./images/icon-app-options.png}) and clicking\n  \\emph{Configuration}. For each, enter \\emph{liferay-0} in the\n  Federated Search Key setting.\n\n  All the search widgets expected to react appropriately to the\n  alternate search must be configured with the Federated Search Key. The\n  following steps detail additional configuration.\n\\item\n  Make an additional configuration in the Low Level Search Options\n  widget, adding the index name of the alternate index:\n\n  Enter at least one index name in the \\emph{Indexes} setting. To follow\n  this example, use \\emph{liferay-0}.\n\\item\n  Configure the Custom Filter to use the search bar's default query\n  parameter (\\emph{q}) and add a query to the search:\n\n  Enter \\emph{title} under field name to add the title field to the\n  query.\n\n  Choose a Filter Query Type (e.g., Match) for the field.\n\n  Since you're overriding the default query to search an alternate\n  index, there's nothing in the query by default. Add any query clauses\n  using the Custom Filter widget(s).\n\\end{enumerate}\n\nIf you're using \\emph{liferay-0} in your Federated Search Key and\nIndexes settings, search for \\emph{dynamic} in the search bar. You'll\nsee results like this:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-federated.png}\n\\caption{Configure the search page to search a different index.}\n\\end{figure}\n\nNow you're able to configure the out of the box search widgets to\nparticipate in searches against any Elasticsearch index in the cluster.\n\n\\chapter{Search Tuning: Synonym Sets}\\label{search-tuning-synonym-sets}\n\nStarting with 7.0 Service Pack 1, new search tuning features are\navailable for administrative Users: Synonym Sets is one of them.\n\nSynonym Sets are mappings that you (the admin) create, so that if a User\nsearches for a certain keyword or phrase, the synonymous terms in your\nmapping are also searched. Matches to synonyms keywords are scored\nequally to matches with the exact keyword by the search engine.\n\n\\textbf{Lunar Resort Use Case:} Multiple content creators at the Lunar\nResort write blogs about a variety of topics. Consistent terminology is\na problem for some concepts. One writer might use the term ``rover'' for\nthe vehicle that travels across the moonscape, while another uses\n``lunar cart'' or ``moon ATV''. As the portal administrator, you must\nensure that the search experience is such that searching for any of\nthose keywords returns all relevant results. Synonym Sets are a key ally\nin this pursuit.\n\n\\section{Requirements and\nLimitations}\\label{requirements-and-limitations}\n\nSearch tuning features like Synonym Sets are only supported when using\nElasticsearch as the search engine. If you're using Solr, make sure you\ndisable the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-solr\\#blacklisting-elasticsearch-only-features}{search\ntuning features} (Synonym Sets and Result Rankings) when you upgrade\nyour installation to Liferay DXP Service Pack 1 (Fix Pack 2).\n\nAs of the initial release (7.0 SP-1), Synonym Sets work with fields\nindexed in two locales: English and Spanish. Thus, the assets supporting\nlocalization out-of-the-box work with Synonym Sets. Technically, this\nmeans that synonym searches operate on fields indexed with the\n\\texttt{en\\_*} and \\texttt{es\\_*} suffixes. Read the\n\\href{/docs/7-2/user/-/knowledge_base/u/searching-for-localized-content}{multi-language\nsearch documentation} to learn which native Liferay DXP assets/fields\nsupport localization in the search index. All asset types that index\ntheir data into English and Spanish are analyzed with a synonyms-aware\nanalyzer and can be found during a synonym search.\n\nThe \\texttt{=\\textgreater{}}\n\\href{https://www.elastic.co/guide/en/elasticsearch/guide/current/synonym-formats.html}{format}\nsupported in Elasticsearch is not supported through the Synonyms Set UI.\n\n\\section{Creating and Managing Synonym\nSets}\\label{creating-and-managing-synonym-sets}\n\nCreate a synonym set by adding as many synonymous keywords to a set as\nyou want. Once the synonym set is saved, any searches in the same\ncompany scope (that's any site from the Virtual Instance where the\nsynonyms were configured) take effect.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-synonym-set.png}\n\\caption{Add as many synonymous keywords to a set as you'd like.}\n\\end{figure}\n\nTo create a synonym set,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to Control Panel → Search Tuning → Synonyms.\n\\item\n  Click the Add button (\\includegraphics{./images/icon-add.png}).\n\\item\n  Enter the list of synonyms in the set. The input of a synonym is\n  accomplished by clicking \\emph{Enter} or by entering a comma.\n\\item\n  When the list is finished, click \\emph{Publish}.\n\\end{enumerate}\n\nThe available synonym sets appear and can be managed in bulk or\nindividually. The management options are to update a synonym set or\ndelete one or more synonym sets.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-synonym-sets.png}\n\\caption{Synonym sets can be managed in bulk.}\n\\end{figure}\n\nTo edit or delete a single synonym set, click the Actions button\n(\\includegraphics{./images/icon-actions.png}) for the synonym set and\nchoose Edit or Delete.\n\n\\section{Using Synonym Sets}\\label{using-synonym-sets}\n\nWhen you have a synonym set defined, the synonyms are ready for use. To\ntest them, find a Search Bar anywhere in the virtual instance and enter\na keyword from one of your synonym sets. Results matching the keyword\nand any synonym are returned in the Search Results widget.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-synonomous-result.png}\n\\caption{The Blogs Entry does not contain the word ``rover'' but it can\nbe matched because of a synonym set mapping ``cart'' as its synonym. The\nsynonym is even highlighted.}\n\\end{figure}\n\n\\section{Known Issues}\\label{known-issues}\n\nThere are several\n\\href{https://issues.liferay.com/browse/LPS-99658}{known issues} for\nSynonym Sets. These are some of the most important ones:\n\n\\href{https://issues.liferay.com/browse/LPS-100272}{LPS-100272}:\nReindexing permanently deletes all Synonym Sets. Please refer to the\nticket for a way to backup and preserve (restore) Synonym Sets across\nreindex operations.\n\n\\href{https://issues.liferay.com/browse/LPS-98126}{LPS-98126} Users can\ncreate duplicate Synonym Set entries and update other Synonym Sets\nunintentionally.\n\n\\section{Related Resources}\\label{related-resources}\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\url{https://www.elastic.co/guide/en/elasticsearch/guide/current/synonyms.html}\n\\item\n  \\url{https://www.elastic.co/guide/en/elasticsearch/reference/6.8/analyzer-anatomy.html}\n\\item\n  \\url{https://www.elastic.co/guide/en/elasticsearch/reference/6.8/analysis-synonym-graph-tokenfilter.html}\n\\item\n  \\url{http://lucene.apache.org/core/7_7_0/analyzers-common/org/apache/lucene/analysis/en/EnglishPossessiveFilter.html}\n\\end{itemize}\n\n\\chapter{Search Tuning: Customizing Search\nResults}\\label{search-tuning-customizing-search-results}\n\nStarting with 7.0 Service Pack 1, new search tuning features are\navailable for administrative Users: Custom Result Rankings is one of\nthem.\n\nResult Rankings provides a brute force method for intervening into the\nrelevance scoring of the search engine, by doing these things:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Designate that certain results should appear at the top of the results\n  if they are matched with a certain keyword. This is the idea of\n  \\emph{pinning} results to the top of the results list.\n\\item\n  By contrast, hide results that shouldn't appear in certain searches at\n  all.\n\\item\n  Add results that aren't normally returned by searching a certain\n  keyword.\n\\item\n  Re-order pinned results with a drag-and-drop interface.\n\\end{enumerate}\n\nResult Rankings let you pin, hide, and add search results for a given\nset of keywords. These rankings apply only to searches using the newer\nsearch widgets (Search Bar, Search Results, etc.). The rankings you\ncustomize do not apply to the legacy Search portlet results or to the\nindividual application search bars.\n\n\\noindent\\hrulefill\n\n\\textbf{Use Case:} At the Lunar Resort website, visitors often search\nfor activities, entering keywords like ``rover races'', ``atv rentals'',\nand ``lunar golf''. For all of these, the Lunar Resort always wants a\ncertain\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-pages}{Content\nPage} to appear at the top of the search results. This is the Activities\npage in the Lunar Resort where guests can find all of the resort's\nadventurous offerings, including lunar rover races, ATV rentals, and\ninformation about golfing packages. By contrast, the Lunar Resort does\nnot want the legal liability waiver form to appear during a search for\nfun activities: that's a bridge to be crossed when guests sign up for\nthe activity. It shouldn't pollute a search for fun activities, even\nthough it contains many of the keywords Users would search for. Result\nRankings lets you \\emph{pin} the Activities Content Page to the top of\nthe results and \\emph{hide} the liability waiver Web Content Article. In\naddition, a community member wrote a blog favorably reviewing the Lunar\nResort, and you want that content added to searches for activities at\nthe resort. This is a prime use case for Result Rankings.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-result-rankings-todo.png}\n\\caption{The Lunar Resort wants to tweak these results: pin the\nActivities page to the top, and hide the legal content entirely.}\n\\end{figure}\n\n\\section{Availability}\\label{availability}\n\nSearch tuning features like Result Rankings are only supported when\nusing Elasticsearch as the search engine. If you're using Solr, make\nsure you disable the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-solr\\#blacklisting-elasticsearch-only-features}{search\ntuning features} (Synonym Sets and Result Rankings) when you upgrade\nyour installation to Liferay DXP Service Pack 1 (Fix Pack 2).\n\nResults Rankings was added in 7.0 Service Pack 1.\n\n\\section{Requirements and\nLimitations}\\label{requirements-and-limitations-1}\n\nResult Ranking entries are configured in a Virtual Instance, but are not\napplied only to that Virtual Instance. Instead, custom rankings made in\none virtual Instance are shared across all Virtual Instances in the\ndeployment, and even across separate deployments sharing an\nElasticsearch cluster (in a multi-tenant scenario). Therefore, Result\nrankings shouldn't be used when connecting multiple Liferay DXP\ndeployments to the same Elasticsearch cluster unless you intend for the\nsame Result rankings to apply to every Liferay DXP deployment. See\n\\href{https://issues.liferay.com/browse/LPS-101291}{LPS-101291} for more\ninformation.\n\nAn existing Result Ranking cannot be renamed. Renaming requires\nrecreating the ranking under a different name. See\n\\href{https://issues.liferay.com/browse/LPS-96357}{LPS-96357} for more\ninformation.\n\n\\section{Creating and Managing Result\nRankings}\\label{creating-and-managing-result-rankings}\n\nTo manipulate result rankings, create a new \\emph{Alias} containing the\nkeywords/search terms you want to intercept. Perform a search to get\nresults (you can also do a separate search if you want to grab results\nthat haven't even been returned during a natural search for the alias\nkeywords). Once you have the results, choose to pin, hide, re-order, or\nadd results as you please.\n\nTo create a new Result Rankings Alias:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to Control Panel → Search Tuning → Result Rankings.\n\\item\n  Click the Add button (\\includegraphics{./images/icon-add.png}).\n\\item\n  On the New Ranking screen, enter one of the keywords or search phrases\n  you want to intercept (it can be a phrase, instead of just one word;\n  and don't worry, you can add more later) in the \\emph{Search Query}\n  field.\n\n  Click \\emph{Customize Results}.\n\\end{enumerate}\n\nA search query is executed. The results are displayed and the tools for\npinning, hiding, and adding results are made available. Re-ordering\nbecomes possible after at least one result is pinned. First, consider\nwhether to add one or more Aliases.\n\n\\section{Adding Aliases}\\label{adding-aliases}\n\nThe Customize Rankings screen is ready to use, but any intervention only\napplies to the search query you initially entered in the New Ranking\nscreen. To apply the customized rankings to additional search terms, add\nthem as \\emph{Aliases}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the Aliases field, enter the search term to add as an Alias.\n\n  \\textbf{Warning:} Do not use quotes in your alias terms.\n\\item\n  To submit the search term as an alias, click Enter or a comma in the\n  Aliases field. You can Add multiple aliases here.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/search-result-rankings-aliases.png}\n  \\caption{Apply your custom rankings to matched results of additional\n  search terms.}\n  \\end{figure}\n\\end{enumerate}\n\nNote that results not manipulated manually here are returned as usual\nwhen the alias term is queried for in the Search Bar.\n\nNow you can customize the rankings.\n\n\\section{Activating and Deactivating\nAliases}\\label{activating-and-deactivating-aliases}\n\n\\begin{quote}\nAvailable as of Liferay DXP 7.2, SP2\n\\end{quote}\n\nYou can activate or deactivate existing aliases as you have need for\nthem to take effect:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Find the Active alias and open its editor screen.\n\\item\n  Click the toggle switch currently labeled Active.\n\\end{enumerate}\n\nDuplicate active aliases are not allowed, but you can deactivate an\nalias and then create a duplicate. After deactivating an alias, you can\nonly reactivate it after deactivating any active duplicates.\n\n\\section{Pinning and Hiding Results}\\label{pinning-and-hiding-results}\n\nTo pin or hide rankings, hover over the result of interest: two icons\nappear, one for pinning and one for hiding. Click the one that applies.\nOtherwise click the Actions button\n(\\includegraphics{./images/icon-actions.png}), and select \\emph{Pin\nResult} or \\emph{Hide Result}. Once you select either option, it's\napplied immediately. A pinned result moves to the top of the list, and a\nhidden result disappears. Repeat the action as many times as necessary.\n\nIf you're done customizing the results, click \\emph{Save}, and it's\napplied immediately.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-result-rankings-pinned-result.png}\n\\caption{Pin results to the top of the Search Results list.}\n\\end{figure}\n\n\\section{Adding Results}\\label{adding-results}\n\nTo add a result that was not returned by searching for the first keyword\nor phrase, click the \\emph{Add Result} button and search for whichever\nasset you want to pin.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-result-rankings-add-result.png}\n\\caption{Add results that aren't normally returned.}\n\\end{figure}\n\nClick \\emph{Save} if you're done customizing results.\n\n\\section{Re-Ordering Pinned Results}\\label{re-ordering-pinned-results}\n\nTo re-order pinned results (results that are not pinned cannot be\nre-ordered), click the handle icon, drag the result, and drop it in the\npreferred location in the list.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/search-result-rankings-reorder.png}\n\\caption{Re-order the pinned rankings if you want to emphasize or\nde-emphasize certain results.}\n\\end{figure}\n\nOnce finished customizing result rankings, click \\emph{Save}.\n\n\\section{Result Rankings Scope and\nPermissions}\\label{result-rankings-scope-and-permissions}\n\nBecause configuration of Result rankings happens at the virtual instance\nscope, there are scoping and permissions behaviors to be aware of.\n\nScope is disregarded for pinned results: Pinned results existing in Site\nA always appear in searches from Site B, even if the Search Bar Scope is\nset to \\emph{This Site}.\n\nSearch from Result Rankings is global: When searching for results in\nResult Rankings admin, relevant results from all sites are returned.\n\nPermissions are applied as usual: If a User doesn't have permission to\nsee an asset, pinning it does not make it appear in the search results\nfor that User.\n\n\\section{Result Rankings Aliases versus\nSynonyms}\\label{result-rankings-aliases-versus-synonyms}\n\nSince both are new features without precedent in Liferay DXP, there can\nbe confusion over Result Rankings Aliases and Synonyms.\n\\href{/docs/7-2/user/-/knowledge_base/u/search-tuning-synonym-sets}{Synonyms}\nexpand the search to include results matched by additional (synonymous)\nkeywords, so more results are returned if there are matches to the\nsynonyms.\n\nResult Rankings Aliases are just keywords that also have the particular\nranking interventions applied to them. Only the searched keyword is\nmatched to results, and then, the pins, hides, re-ordering, and\nadditional results take effect after that.\n\nThese features don't interact in a predictable way. If you need\nsynonym-like behavior in results rankings, define aliases for the\nkeyword.\n\n\\section{Known Issues}\\label{known-issues-1}\n\nThere are several known issues and planned improvements for Result\nRankings. See\n\\href{https://issues.liferay.com/browse/LPS-99540}{LPS-99540} for the\ncomplete list.\n\n\\chapter{Forms}\\label{forms}\n\nLiferay Forms gives you robust form building capability. For a complete\nlist of the form fields available, visit the\n\\href{/docs/7-2/user/-/knowledge_base/u/form-field-types}{form fields\nreference article}.\n\nBecause the complexity of use cases for Forms varies from a single input\nfield to many pages of fields with different configurations, it makes\nsense to show you how to build and publish simple forms very quickly,\nand then show you all the additional features you can use for more\ncomplex use cases. Here's a sampling of the what the Forms application\ncan do:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Populate a Select or Radio field with a REST Data Provider\n\\item\n  Make a field appear based on the value of another field\n\\item\n  Add extra pages to the form\n\\item\n  Enable CAPTCHA for a form\n\\item\n  Store results in JSON\n\\item\n  Enable workflow for the form\n\\item\n  Redirect to a different URL after a successful form submission\n\\item\n  Send an email notification to administrators whenever a form is\n  submitted\n\\item\n  Provide a default value (entered if left alone by the user) or a\n  placeholder value (not entered if left alone by user) for each field\n\\item\n  Validate fields using a number of different criteria\n\\item\n  Redirect users to a success page after form submission\n\\item\n  Define Form Rules to create dynamic form behavior (for example, show\n  or hide a field based on input in another field).\n\\item\n  Translate form text into any supported language.\n\\item\n  Create partial forms (with fields and other elements and specific\n  configurations) and save them for reuse.\n\\item\n  Drag and drop fields onto the form layout.\n\\item\n  Duplicate a form instead of starting a similar form from scratch.\n\\end{itemize}\n\nDespite this long list of more complex options, developing a simple,\nelegant form to suit basic needs takes little effort. The next article\ncovers basic form building.\n\n\\section{Forms and Lists}\\label{forms-and-lists}\n\nWhen you need a form, what you're really looking for is data. There are\ntwo applications for building forms to collect precisely the data you\nneed:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/forms}{Liferay Forms}: The\n  primary form building application is for the simplest one or two\n  question survey to the most complex, multi-page, homeowners insurance\n  application containing rules and lists populated by a REST data\n  provider.\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/dynamic-data-lists}{Dynamic\n  Data Lists (DDL)}: Provides a user interface tool for building\n  reusable form- and list-based applications intended for display on\n  pages, using\n  \\href{/docs/7-2/user/-/knowledge_base/u/using-templates-to-display-forms-and-lists}{templates}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Kaleo Forms:} If you're a Liferay Digital Enterprise customer,\nthere's a third form building tool called\n\\href{https://help.liferay.com/hc/en-us/articles/360028821952-Kaleo-Forms}{Kaleo\nForms}. It integrates form building with workflow to create form-based\nbusiness processes, like a Conference Room Checkout Form, or a Support\nTicket Process so support tickets go through the proper channels on\ntheir way to resolution. Read more about Kaleo Forms in the workflow\n\\href{https://help.liferay.com/hc/en-us/articles/360028821952-Kaleo-Forms}{section}.\n\n\\noindent\\hrulefill\n\n\\section{Which Form Builder Should I\nUse?}\\label{which-form-builder-should-i-use}\n\nLiferay Forms (also referred to as Forms) is a relatively new\napplication, first appearing in Liferay DXP version 7.0. If you can use\nLiferay Forms for your use case, you should.\n\nSo the question ``Which form builder should I use?'' can be restated to\n``When should I use Dynamic Data Lists?''\n\n\\begin{itemize}\n\\item\n  Use Dynamic Data Lists (DDL) if you need a way for users to enter\n  data, \\emph{and} you need to display the data in the user interface.\n\\item\n  Use DDL if you need to style your lists and forms with templates.\n\\item\n  Use DDL if there's a field type you need that's not included (yet) in\n  Liferay Forms. These are the field types included in DDL that\n  \\emph{are not} in Liferay Forms at the time of this writing: - Color -\n  Geolocation - Web Content - Link to Page\n\\end{itemize}\n\nIt's important to note that these (and more!) form field types will be\nincluded in future versions of the Liferay Forms application.\n\nWhen all form building features are fully merged into Liferay Forms, the\nbest features of DDL, all the new features of Liferay Forms, and all\nfuture improvements will be in one application. Now is the time to\nfamiliarize yourself with Liferay Forms and begin using it for all your\nform building needs, except for the narrow use cases described above.\n\n\\chapter{Creating and Managing Forms}\\label{creating-and-managing-forms}\n\nThe Forms widget can do a lot of things really well, but if you just\nneed a simple form, how do you wade through all the features you don't\nneed? Is your simple survey going to make you late for that lunch outing\nyou've been planning with colleagues at that new shawarma place? No!\n\nLet's create a simple yet elegant form, give access to the intended\nusers, and get you on your way to lunch.\n\nAt The Lunar Resort, it's important to capture guests' feedback about\ntheir stay at the resort. After a (hopefully) safe journey home, guests\nshould receive an email with a link to brief survey that prompts them to\nrate their stay from a list of selections, and add any additional\ncomments they'd like in an optional field.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-guest-survey.png}\n\\caption{Get feedback from guests of The Lunar Resort.}\n\\end{figure}\n\n\\section{Viewing Forms}\\label{viewing-forms}\n\nWhether creating a form or managing existing forms, it all starts in the\nsame place: the Forms Application in your site's Content section. Access\nthis in the Menu, first choosing the site to work in (for example, The\nLunar Resort) and clicking \\emph{Content \\& Data} → \\emph{Forms}. The\nfirst thing you'll see is a list of existing forms (if there are any).\nThis list is styled by the Display Style selector next to the Add button\n(\\includegraphics{./images/icon-add.png}). By default, forms are\ndisplayed in List format.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-list-view.png}\n\\caption{Forms are displayed in List format by default.}\n\\end{figure}\n\nThere's also a Table format. Change the style for a single site right\nhere in the Forms site menu application, or change the default display\nstyle for the system scope in Control Panel → Configuration → System\nSettings → Forms (in the Content section). Click the \\emph{Forms} entry\nand find the Default Display View property. Click \\emph{Update} and your\nchanges are propagated to all sites.\n\n\\section{Building a Form}\\label{building-a-form}\n\nTo add a new form,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n  The form builder view appears.\n\\item\n  Name the form. Replace \\emph{Untitled Form} with \\emph{Guest Survey}.\n\\item\n  For the description text enter \\emph{Tell us how your stay was!}\n\\item\n  Add the fields. Click the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}) to open the sidebar if it's\n  not already opened.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-sidebar.png}\n  \\caption{You can choose from nine field types when creating forms.}\n  \\end{figure}\n\\item\n  Drag a \\emph{Select from List} field onto the form builder and\n  configure it like this:\n\n  \\textbf{Label:} \\emph{Rate your visit to The Lunar Resort.}\n\n  \\textbf{Help Text:} Leave this blank for now. If you want a subheading\n  for your field to provide additional guidance, this would be useful.\n\n  Turn on the \\emph{Required Field} selector. At a minimum, this form\n  must capture whether guests like their stay or not.\n\n  Leave the manual option checked for creating the list of selections.\n  To learn about populating the field with a data provider, read\n  \\href{/docs/7-2/user/-/knowledge_base/u/data-providers}{here}.\n\n  Add these options:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\emph{It was out of this world!}\n  \\item\n    \\emph{I had a good time.}\n  \\item\n    \\emph{I'd rather go to the beach.}\n  \\item\n    \\emph{I'll never come back.}\n  \\end{itemize}\n\n  Typing in one of the fields automatically adds another blank selection\n  line. Just leave the last one blank when you're done.\n\\item\n  To see additional options, click \\emph{Properties}. Close the sidebar\n  when finished.\n\\item\n  Add a text field, using the same procedure you used for the select\n  field.\n\n  \\textbf{Label:} \\emph{Want to tell us more?}\n\n  \\textbf{Help Text:} Leave this blank again to give the form a\n  consistent look.\n\n  \\textbf{My text field has:} Choose \\emph{Multiple Lines}. Let guests\n  prattle on about their stay if they want to.\n\n  \\textbf{Required Field:} Leave this unselected. Only require guests to\n  fill out the select field and leave this one as optional.\n\\item\n  Close the sidebar.\n\\item\n  In the form builder, you can see the way the fields are laid out on\n  the form page.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-form-builder.png}\n  \\caption{The form builder page lets you preview your form layout, add\n  a page to the form, or add some more fields.}\n  \\end{figure}\n\\item\n  When the form is finished, click \\emph{Save Form}. It's also\n  auto-saved every minute by default.\n\\item\n  Click \\emph{Publish Form}. A dedicated URL to the form is generated,\n  but nobody has the URL yet.\n\\end{enumerate}\n\nNow your form can be added to a page, and Lunar Resort guests can be\nemailed and provided with a link to the page where the form is\ndisplayed.\n\n\\section{Accessing Forms}\\label{accessing-forms}\n\nOnce the form is developed and published, there are two options for\ngetting the published form to targeted users:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Place the Form widget on a site page. This approach lets users\n  navigate to the page in the site.\n\\item\n  Copy the dedicated form URL and provide it to users (for example, via\n  email). This limits access to the form to only those users who have\n  the direct link.\n\\end{enumerate}\n\nTo display the form on a site page in The Lunar Resort site:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a page to the site (choose Full Page Application for the page type\n  if you only want the form application on the page). Call it\n  \\emph{Guest Survey}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-guest-survey-page.png}\n  \\caption{Add a page for guests to view and fill out your new form.}\n  \\end{figure}\n\\item\n  Add the Form widget to the page if you've chosen a Widget Page. If you\n  used a full page application, use the page configuration to choose\n  \\emph{Form} from the Full Page Application dropdown.\n\\item\n  Once the Form widget is on the page, click \\emph{Select Form}, choose\n  the \\emph{Guest Survey} form, and click \\emph{Save}.\n\\item\n  Close the \\emph{Form---Configuration} dialog window and your form is\n  ready for Lunar Resort site users.\n\\end{enumerate}\n\nTo display the form on a dedicated page accessed only by its link:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the form builder, click \\emph{Publish Form} if you haven't already.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-link-grayed.png}\n  \\caption{You must first publish a form before you can get a shareable\n  link.}\n  \\end{figure}\n\\item\n  Once published, click the link icon at the top right of the builder.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-link.png}\n  \\caption{Copy the link to your form.}\n  \\end{figure}\n\\item\n  Once you get the link out to users, it's showtime.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-guest-survey.png}\n  \\caption{Lunar Resort guests can use a simple form to record their\n  feelings about the resort.}\n  \\end{figure}\n\\end{enumerate}\n\nNext you'll learn how to view the form entries. Since there aren't any\nyet, fill out and submit the form a few times.\n\nNow you know the basics of creating and managing forms, but this\npresentation didn't do the Forms application justice. It's much more\npowerful than hinted at here. The remaining articles in this section\nimmerse you in more advanced form building features.\n\n\\chapter{Managing Form Entries}\\label{managing-form-entries}\n\nOnce users begin submitting form entries, you'll want to do these things\nwith them:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\hyperref[viewing-form-entries]{View form entries}\n\\item\n  \\hyperref[exporting-form-entries]{Export form entries}\n\\item\n  \\hyperref[deleting-form-entries]{Delete form entries}\n\\end{itemize}\n\nStart by learning how to access and view the entries.\n\n\\section{Viewing Form Entries}\\label{viewing-form-entries}\n\nWhen users fill out forms, they're generating data. You'll want to see\nthat data at some point.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the Menu, navigate back to the \\emph{Content} → \\emph{Forms}\n  section of The Lunar Resort site.\n\\item\n  Click the \\emph{Actions} (\\includegraphics{./images/icon-actions.png})\n  button for the form and select \\emph{View Entries}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-view-entries.png}\n  \\caption{You can view the entries right in the Forms application.}\n  \\end{figure}\n\\item\n  What if you have a lot of form fields and you can't see all the data\n  for each entry in the search container? Just click the \\emph{Actions}\n  (\\includegraphics{./images/icon-actions.png}) button for the entry and\n  select \\emph{View}. You're shown all the specifics for that form\n  entry.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-view-entry.png}\n  \\caption{You can view a single entry right in the Forms application.}\n  \\end{figure}\n\\end{enumerate}\n\nViewing entries is great, but this is serious data we're talking about.\nYou might need to get all the entries into a spreadsheet so you can work\nwith them.\n\n\\section{Exporting Form Entries}\\label{exporting-form-entries}\n\nSo you need to put your form entries in a spreadsheet to do spreadsheet\nthings with them? No problem.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Forms application in The Lunar Resort site's Content\n  \\& Data section again.\n\\item\n  Click the \\emph{Actions} (\\includegraphics{./images/icon-actions.png})\n  button and select \\emph{Export}.\n\\item\n  Choose a File Extension. You can export entries in CSV, JSON, XLS, or\n  XML formats by default. For this example, pick CSV.\n\\item\n  Click \\emph{Okay}, and open the file or save it locally. Open it with\n  your favorite spreadsheet program and verify your form entries.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-export-csv.png}\n\\caption{You can export entries as CSV, JSON, XLS, or XML.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Forms application itself has an \\emph{Import/Export}\nwindow accessible from the application's Configuration menu\n(\\includegraphics{./images/icon-options.png}). This is how you import\nand export the application configuration and its data (forms and form\nentries). The file format for this type of import and export is a LAR\nfile. For more information, see the article on\n\\href{/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content}{importing\nand exporting application content}.\n\n\\noindent\\hrulefill\n\nThere's a system level setting to determine whether administrators can\nexport entries in CSV format:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Control Panel → Configuration → System Settings and click the\n  \\emph{Forms} category in the CONTENT AND DATA section.\n\\item\n  Click the \\emph{Forms} entry under SITE SCOPE.\n\\item\n  The CSV Export property has three options:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\emph{Enabled} to enable CSV Export without a warning\n  \\item\n    \\emph{Enabled (Show Warning)} to enable CSV Export with the\n    following warning to administrators: This CSV file contains user\n    supplied inputs. Opening a CSV file in a spreadsheet program may be\n    dangerous.\n  \\item\n    \\emph{Disabled} to turn off CSV Export.\n  \\end{itemize}\n\\end{enumerate}\n\nOnce you export a batch of form entries, it can make sense to delete\nthem from the database.\n\n\\section{Deleting Form Entries}\\label{deleting-form-entries}\n\nWhat if you export a form's entries and now you want to remove them from\nthe Liferay database? It's easy to delete all of a form's entries at\nonce.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate back to the Forms application In The Lunar Resort Content\n  section.\n\\item\n  Click the \\emph{Actions} (\\includegraphics{./images/icon-actions.png})\n  button next to the Guest Survey form and select \\emph{View Entries}\n  again.\n\\item\n  Select all entries by checking the box next to \\emph{Filter and\n  Order}. An X appears in the top right corner of the Form Entries\n  screen. Click it.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-delete-entries.png}\n  \\caption{Delete all form entries in one fell swoop.}\n  \\end{figure}\n\\end{enumerate}\n\nIf you just wanted to delete a single entry, select that entry by\nchecking its box; then delete it.\n\nIf you're worried about deleting everything irrecoverably by accident,\ndon't worry. You must confirm the deletion in a dialog box that pops up\nafter clicking the trash can.\n\nNow you can create basic forms and manage the entries. Keep reading in\nthis section to learn about the many additional form building features\navailable to you.\n\n\\chapter{Form Field Types}\\label{form-field-types}\n\nA form without fields is no form at all. To meet your form-building\nneeds, Liferay Forms provides useful and highly configurable field\ntypes.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-field-types.png}\n\\caption{There are many useful out-of-the-box form field types.}\n\\end{figure}\n\n\\textbf{Paragraph:} This is static text on the form. Users do not enter\ndata into form text fields. The form creator enters text that form users\nsee displayed on the form. This is useful for longer instructions.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-paragraph.png}\n\\caption{Use Paragraph fields to enter longer instructions on Form\nPages.}\n\\end{figure}\n\n\\textbf{Text Field:} Users enter text into these fields. For example, a\nFull Name field is a text field. By default, a text field keeps all\ninput on a single line of text. To accommodate longer responses, choose\nthe multi-line setting when configuring the text field\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-and-managing-forms\\#building-a-form}{as\nin this example}. You can put limits on the text users can enter (e.g.,\nnumbers from 1-10, email addresses, etc.) by using the text field's\n\\href{/docs/7-2/user/-/knowledge_base/u/validating-text-and-numeric-fields}{validation\noptions}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-multiline.png}\n\\caption{Text fields can be single line or multi-line.}\n\\end{figure}\n\n\\textbf{Select from List:} Users select one option (or more, if\nconfigured to allow it) from a list of choices. Choices are entered\nmanually or are automatically populated by a data provider. For example,\na Country of Residence field can be selected from list field populated\nby a Countries of the World data provider.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-select-list.png}\n\\caption{Use a select from list field to let Users choose predefined\noptions.}\n\\end{figure}\n\n\\textbf{Single Selection:} Using a radio button, users select one option\nfrom a list of options displayed on the form.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-single-selection.png}\n\\caption{Single selection fields allow only one selection.}\n\\end{figure}\n\n\\textbf{Date:} Users select a date using a date picker. For example, a\nBirth Date field uses the Date field type.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-date.png}\n\\caption{Date fields show a date picker so Users enter a valid date.}\n\\end{figure}\n\n\\textbf{Multiple Selection:} Users select one or more options from check\nboxes (or toggles, if configured).\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-switcher.png}\n\\caption{A multiple selection field can use a toggle.}\n\\end{figure}\n\n\\textbf{Grid:} Using radio buttons, users select from options laid out\nin rows and columns. One selection can be made per row. This is useful\nwhen the same response metric is needed for multiple questions. For\nexample, a product survey form might ask users to rate a list of their\nproduct's characteristics as \\emph{Wonderful}, \\emph{Pretty Good},\n\\emph{Not So Good}, or \\emph{Awful}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-grid.png}\n\\caption{Grid fields use the same options (columns) for multiple\ncategories (rows).}\n\\end{figure}\n\n\\textbf{Numeric:} Users enter numeric data (integers or decimals) into\nnumeric fields. Non-number input is not accepted. For example, configure\na numeric field that accepts integers to ask users how many pets they\nhave.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-numeric.png}\n\\caption{Numeric fields accept only numeric input.}\n\\end{figure}\n\n\\textbf{Upload:} Users can select a file from the Documents and Media\nlibrary or upload a file from their local filesystems.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-upload.png}\n\\caption{: Upload fields let Users attach files to the form.}\n\\end{figure}\n\n\\chapter{Form Rules}\\label{form-rules}\n\nChickens don't follow rules well, but dogs do. If you're skeptical, try\nteaching your chicken to sit on command or herd sheep. Better yet, get a\nteam of chickens to pull a sled in the\n\\href{http://iditarod.com}{Iditarod}. The Forms application is much more\nlike the dog than the useful (southwestern omelet anyone?) but\nuntrainable chicken, and it's only getting more trainable as time\npasses.\n\nForm rules are a good example of the trainable nature of the Forms\napplication. With form rules, you can train your form fields to behave\nas you wish. There are several things you can make them do:\n\n\\begin{description}\n\\tightlist\n\\item[Show/hide]\nBased on a predefined condition, set the visibility of a form field.\n\\item[Enable/disable]\nUse a predefined condition to enable or disable a field.\n\\item[Require]\nUse a predefined condition to make a field required.\n\\item[Jump to Page]\nBased on user input, skip over some form pages directly to a relevant\npage. \\emph{This rule doesn't appear in the rule builder until a second\npage is added to the form}.\n\\item[Autofill with Data Provider]\nUse a \\href{/docs/7-2/user/-/knowledge_base/u/data-providers}{data\nprovider} to populate fields when a condition is met in another field.\n\\item[Calculate]\nPopulate a field with a calculated value using data entered in other\nfields.\n\\end{description}\n\nForm rules are for changing fields and form elements by acting on\nconditions.\n\n\\emph{If {[}condition{]} do {[}action{]}.}\n\nIf you're not already familiar with the Forms application, start\n\\href{/docs/7-2/user/-/knowledge_base/u/forms}{here}. Once you know how\nto create forms, add and configure fields, and then publish forms, come\nback here and learn about form rules.\n\n\\section{The Anatomy of a Form Rule}\\label{the-anatomy-of-a-form-rule}\n\nEach rule consists of one or more conditions and actions.\n\n\\emph{Conditions} determine whether any actions are executed.\n\n\\emph{Actions} determine what happens if the condition is met.\n\nRules are stored in the database in JSON format by default.\n\n\\section{Creating Form Rules: Rule\nBuilder}\\label{creating-form-rules-rule-builder}\n\nOnce you create a form and lay out its fields, you're ready to set up\nrules in your form:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Save the form.\n\\item\n  Open the Rule Builder by clicking the \\emph{Rules} tab at the top of\n  the \\emph{Edit Form} screen.\n\\item\n  In the rule builder view, you can now begin developing your form rule.\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  to get started.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-rule-builder.png}\n\\caption{The Rule Builder gives you a handy interface for creating\ndynamic form rules.}\n\\end{figure}\n\nBefore looking at each type of rule condition and action you can use to\ndevelop rules, consider the \\emph{OR} selector box at the right side of\nthe \\emph{Condition} (it's grayed out and unusable at first). You can\nchoose \\emph{OR} or \\emph{AND} here, depending on what relationship the\nconditions should have with the action.\n\n\\begin{description}\n\\tightlist\n\\item[OR]\nThe action is triggered if \\emph{any} of the conditions you specify\nevaluates to \\emph{true}\n\\item[AND]\nThe action is triggered only if \\emph{all} the conditions you specify\nevaluate to \\emph{true}\n\\end{description}\n\nThis box becomes usable once you click the Add button\n(\\includegraphics{./images/icon-add.png}) to add an extra condition.\n\n\\section{Conditions}\\label{conditions}\n\nConditions are the gatekeepers of form rules. If the condition's\n\\emph{if statement} evaluates to \\emph{true}, the action is triggered.\nIf it evaluates to \\emph{false}, no action happens.\n\nA condition checks whether one field's value\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Is equal to} a specific value or another field's value.\n\\item\n  \\emph{Is not equal to} a specific value or another field's value.\n\\item\n  \\emph{Contains} a specific value or another field's value.\n\\item\n  \\emph{Does not contain} a specific value or another field's value.\n\\item\n  \\emph{Is empty}. This assumes you want to do something if a field\n  \\emph{is} empty.\n\\item\n  \\emph{Is not empty}. This assumes you want to do something as long as\n  a field is \\emph{not} empty.\n\\end{itemize}\n\nOne exception to this is the User condition, which is the last option in\nthe Condition dropdown menu.\n\nThe User condition doesn't act on a field at all. It checks whether a\nUser belongs to a certain role. For example, if the condition\n\nIf \\texttt{User} \\emph{belongs to} \\texttt{Administrator}\n\nevaluates to \\emph{true}, an action is triggered.\n\nA condition is the gateway into a form rule, but actions define what\nactually happens when the condition evaluates to \\emph{true}. The\nremaining articles discuss the available actions and demonstrate their\nuse.\n\n\\chapter{Action: Show and Hide}\\label{action-show-and-hide}\n\nWith a show and hide rule, you use one or more conditions to determine\nwhether to show or hide a field if the condition evaluates to\n\\emph{true}.\n\nTo set this example up, add these fields to a form:\n\n\\begin{itemize}\n\\item\n  \\emph{I am 18 Years Old or Older}, a required single selection field\n  with two options: \\emph{Yes} and \\emph{No}.\n\\item\n  \\emph{Legal Guardian Email Address}, a text field that accepts valid\n  email addresses (use text field validation to dictate input type).\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Example:} If you're under 18 years old, you need the approval of\na legal guardian to drive a sled in a sled dog race (even if you're\nracing chickens, not dogs). The form for registering your chicken team\nasks you the age of the driver. If you enter a number less than 18, the\nLegal Guardian Email Address field appears.\n\nTo configure a Show/Hide rule,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Rules tab of the Edit Form page and click the Add\n  (\\includegraphics{./images/icon-add.png}) button.\n\\item\n  Define the rule:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    If the \\emph{I am 18 years old or older} field is equal to the Value\n    \\emph{No}, show the \\emph{Legal Guardian Email Address} field.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-rule-development.png}\n  \\caption{Build form rules quickly by defining your conditions and\n  actions.}\n  \\end{figure}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Save the rule.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-rule-list.png}\n  \\caption{Once a rule is saved, it is displayed so that you can easily\n  understand what it does.}\n  \\end{figure}\n\\end{enumerate}\n\nNow the \\emph{Legal Guardian Email Address} field is only displayed in\nthe form if the user selects \\emph{No} in the \\emph{I am 18 years old or\nolder} field.\n\n\\noindent\\hrulefill\n\nShow rules let you keep a field hidden until some condition is met.\n\n\\chapter{Action: Require}\\label{action-require}\n\nUse a require rule to make a field required based on one or more\nconditions.\n\n\\noindent\\hrulefill\n\n\\textbf{Example:} If you are following the example, you already set up a\n\\emph{show} rule, where a \\emph{Legal Guardian Email Address} field\nappears if the user selects \\emph{No} in the \\emph{I am 18 years old or\nolder} field. You also want to make the \\emph{Legal Guardian Email\nAddress} field required.\n\nTo configure a require rule,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Edit the\n  \\href{/docs/7-2/user/-/knowledge_base/u/action-show-and-hide}{Show\n  Rule} configured above. Open the Rules tab of the Edit Form page and\n  click the kebab menu (\\includegraphics{./images/icon-actions.png}) for\n  the rule, and then click \\emph{Edit}.\n\\item\n  Add an Action to the rule:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    If the \\emph{I am 18 years old or older} field is equal to\n    \\emph{No}, show the \\emph{Legal Guardian Email Address} field and\n    make it required.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-require-rule.png}\n  \\caption{Build form rules quickly by defining your conditions and\n  actions.}\n  \\end{figure}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Save the rule.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-require-rule2.png}\n  \\caption{Once a rule is saved, it is displayed so that you can easily\n  understand what it does.}\n  \\end{figure}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\nRequire rules let you require fields based on input from other fields.\n\n\\chapter{Action: Enable and Disable}\\label{action-enable-and-disable}\n\nUse an enable/disable rule to make a field editable based on one or more\nconditions.\n\n\\noindent\\hrulefill\n\n\\textbf{Example:} Part of the race registration fee pays for dog food.\nYou don't have to feed your chicken team with the provided dog food\nthough. There's a single selection field that asks \\emph{Would you like\nto use the provided dog food?}. If you select \\emph{Yes}, you can select\nhow much food, in US pounds, you'll need for your team throughout the\nrace. Since you're racing chickens, you'll select \\emph{No}, and the\n\\emph{Amount (US lb.)} field is disabled.\n\nTo follow the example, add a single selection field \\emph{Would you like\nto use the provided dog food?} with two options: \\emph{Yes} and\n\\emph{No}.\n\nAdd a numeric field called \\emph{Amount (US lb.)} and make it an\nInteger. Use field validation to make sure it's not greater than\n\\emph{100}.\n\nTo set up the enable/disable rule,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Rules tab of the Edit Form page and click the Add\n  (\\includegraphics{./images/icon-add.png}) button.\n\\item\n  Define the rule:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    If the \\emph{Would you like to use the provided dog food?} field is\n    equal to \\emph{Yes}, enable the \\emph{Amount (US lb.)} field.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-enable-rule.png}\n  \\caption{Build form rules quickly by defining your conditions and\n  actions.}\n  \\end{figure}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Save the rule.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-enable-rule2.png}\n  \\caption{Once a rule is saved, it is displayed so that you can easily\n  understand what it does.}\n  \\end{figure}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\nNow users can fill out the amount of dog food they'll need only if they\nspecify that they do indeed want to use the provided food.\n\n\\chapter{Action: Jump to Page}\\label{action-jump-to-page}\n\nUse a Jump to Page rule to navigate automatically to a specific page in\nthe form based on one or more conditions. This is useful if some pages\ndon't apply to all the form's users. Even fields marked as required on\nthe skipped pages can be successfully skipped using this rule.\n\nThis action doesn't appear in the rule builder unless the form has\nmultiple pages.\n\nTo follow the example here,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a second form page called \\emph{Team Information}.\n\\item\n  On the \\emph{first} form page, create a single selection field labeled\n  \\emph{Are you a returning racer, with the same team?} with two\n  options: \\emph{Yes} and \\emph{No}.\n\\item\n  Create a text field on the \\emph{second} form page called \\emph{Animal\n  Name}.\n\\item\n  Create a third form page called \\emph{Final Confirmation}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Example:} There's a question on the \\emph{Team Information} page\nof the dog sled race registration form that asks \\emph{Are you a\nreturning racer with the same team?} If you select \\emph{Yes}, when you\nclick the form's \\emph{Next} button, you skip to the final page of the\nform, since there's no need to fill out your animal's name again. Their\nmonogrammed T-shirts will be ready at the start of the race.\n\nTo configure the Jump to Page rule,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Rules tab of the Edit Form page and click the Add\n  (\\includegraphics{./images/icon-add.png}) button.\n\\item\n  Define the rule:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    If field \\emph{Are you a returning racer, with the same team?} is\n    equal to the Value \\emph{Yes}, Jump to Page \\emph{Final\n    Confirmation}.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-jump-to-page.png}\n  \\caption{Build form rules quickly by defining your conditions and\n  actions.}\n  \\end{figure}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Save the rule.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-jump-to-page2.png}\n  \\caption{Once a rule is saved, it is displayed so that you can easily\n  understand what it does.}\n  \\end{figure}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\nOnce the form User fills out the first page and clicks \\emph{Next}, the\nrule condition will evaluate hte answer to the field and either proceed\nto the next page or take the action of skipping to the page inidicatedd\nin the rule.\n\nIf you use an \\emph{is not equal to} condition for form fields on two\ndifferent pages, the condition is checked after leaving the page of the\nfirst form field, and evaluates to \\emph{true} since there's a value in\nthe first field and no value in the second field. It's best to use this\ncondition with fields existing on the same page.\n\n\\chapter{Action: Autofill}\\label{action-autofill}\n\nAutofill rules let you change the selection options of another field\nbased on the value entered into a related field. A\n\\href{/docs/7-2/user/-/knowledge_base/u/data-providers}{data provider's}\noutput is used to populate a field, as long as the condition is met.\n\nBefore configuring an autofill rule,\n\\href{/docs/7-2/user/-/knowledge_base/u/data-providers}{set up a data\nprovider}. That's how autofilled fields are populated. Pay careful\nattention to the input and output parameters you choose when setting up\nthe rule.\n\nTo follow this example:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Set up a data provider using the \\texttt{get-countries} JSON web\n  service. If you're running Liferay DXP at \\texttt{localhost:8080}, you\n  can access this web service here:\n\n\\begin{verbatim}\nhttp://localhost:8080/api/jsonws?contextName=&signature=%2Fcountry%2Fget-countries-0\n\\end{verbatim}\n\n  Make sure the output parameter is set to \\texttt{\\$..nameCurentValue}.\n  If you're unsure how to do this, first read the article on\n  \\href{/docs/7-2/user/-/knowledge_base/u/data-providers}{Data\n  Providers}.\n\\item\n  On the last form page, add two fields:\n\n  \\begin{itemize}\n  \\item\n    A Single Selection field called \\emph{If I win I'd like my award to\n    be:}, with two choices: \\emph{Cash} and \\emph{All Expenses Paid\n    Vacation}.\n  \\item\n    A Select from List field called \\emph{Choose a Destination Country}.\n    Under \\emph{Create List}, choose \\emph{From Autofill}.\n  \\end{itemize}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Example:} Before submitting the race registration, let users\ndecide whether they want a cash prize or an all-expenses-paid vacation.\nIf they choose the vacation, populate the Choose a Destination Country\nwith output from the data provider.\n\nTo configure an Autofill rule,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Rules tab of the Edit Form page and click the Add\n  (\\includegraphics{./images/icon-add.png}) button.\n\\item\n  Define the rule:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    If field \\emph{If I win I'd like my award to be} is equal to the\n    Value \\emph{All Expenses Paid Vacation}, Autofill the \\emph{Choose a\n    Destination Country} field from the \\emph{countries} data provider\n    (note that you might have named this differently when setting it\n    up).\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-autofill.png}\n  \\caption{Build form rules quickly by defining your conditions and\n  actions.}\n  \\end{figure}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Save the rule.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-autofill2.png}\n  \\caption{Once a rule is saved, it is displayed so that you can easily\n  understand what it does.}\n  \\end{figure}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\section{Using Inputs with Autofill}\\label{using-inputs-with-autofill}\n\nThe above example is simple, using only an Output to autofill a Select\nfrom List field if another field has a certain value. Many times, the\nresponse from the REST provider must be filtered before display in the\nSelect from List field. For this, a Data Provider Input field is\nrequired. For example, to configure an autofill rule to display the\ncountries of the world filtered by a Region field (for example,\nAmericas, Europe, or Oceania),\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create a Data Provider.\n\n  \\textbf{Name:} \\texttt{restcountries}\n\n  \\textbf{URL:}\n  \\texttt{https://restcountries.eu/rest/v2/region/\\{region\\}?fields=name}\n\n  \\textbf{Inputs:} Fill in the Label (\\emph{region}), Parameter\n  (\\emph{region}), and Type (\\emph{Text}).\n\n  \\textbf{Outputs:} fill out a Label (\\emph{name}), Path\n  (\\emph{\\$..name}), and Type (\\emph{List}).\n\n  To understand more about these values, read the\n  \\href{/docs/7-2/user/-/knowledge_base/u/data-providers}{Data Provider\n  documentation}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-autofill-data-provider.png}\n  \\caption{Create a data provider for the autofill rule.}\n  \\end{figure}\n\\item\n  Create a form with these fields:\n\n  \\textbf{Text:} Use the Label \\emph{Region}.\n\n  \\textbf{Select from List:} Label it \\emph{Country}, and choose\n  \\emph{From Autofill} under Create List.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-autofill-input-output-fields.png}\n  \\caption{Create a form with a text field and a select from list field.\n  These are used to provide the input to the data provider and be\n  autofilled by its output.}\n  \\end{figure}\n\\item\n  Configure the Autofill rule.\n\n  \\textbf{Condition:} If \\emph{Region} \\textbf{Is not Empty}\n\n  \\textbf{Action:} Do \\textbf{Autofill} From Data Provider\n  \\texttt{restcountries}, Data Provider's Input: region---\\emph{Region},\n  Data Provider's Output: name---\\emph{Country}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-autofill-rule.png}\n  \\caption{Create the autofill rule. Brag of your prowess.}\n  \\end{figure}\n\\end{enumerate}\n\nOnce you're done, publish the form and try it out, by entering a valid\nregion into the Region field, and observing that the options in the\nSelect from List Field are filtered based on the Region. The\n\\href{https://restcountries.eu}{restcountries.eu} service has these\nregions you can use: Africa, Americas, Asia, Europe, Oceania, and Polar.\n\nAutofill rules combine the power of data providers and form rules.\n\n\\chapter{Action: Calculate}\\label{action-calculate}\n\nCalculate rules let you automatically populate a numeric field by\ncalculating its value based on other fields. Calculations are limited to\nnumeric fields.\n\nTo follow the example below:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create 5 Numeric fields called \\emph{Animal Weight 1 (US lb.)},\n  \\emph{Animal Weight 2 (US lb.)}, etc.\n\\item\n  Create a Numeric field called \\emph{Total Food Required (US lb.)}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Example:} A 16-dog sled team can consume 2,000 US lb. of food\nduring the Iditarod. This equates to about 0.25 lb. of food per lb. of\nanimal, if the race lasts ten days. We'll use five numeric fields for\nanimal weight instead of sixteen here, because it's tedious to create\nsixteen fields, even with the field duplication function. When the form\nuser enters the weight of each animal the Total Food Required field\nshould be calculated based on this simple formula:\n\n\\begin{verbatim}\n Animal Weight 1, Animal Weight 2, ... = AW1, AW2, ...\n Total Food Required = TFW\n\n (AW1 + AW2 + AW3 + AW4 + AW5) * 0.25 = TFW\n\\end{verbatim}\n\nTo configure a calculate rule:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Rules tab of the Edit Form page and click the Add\n  (\\includegraphics{./images/icon-add.png}) button.\n\\item\n  Define the rule:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    If field \\emph{Animal Weight 1} is greater than 0, Calculate the sum\n    of the Animal Weight fields, multiplied by 0.25.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-calculate-rule.png}\n  \\caption{Build calculate actions with a handy calculator.}\n  \\end{figure}\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Save the rule.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-calculate-rule2.png}\n  \\caption{Once a rule is saved, it is displayed so that you can easily\n  understand what it does.}\n  \\end{figure}\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\nThe calculation is defined using the embedded calculator. Use a mix of\nnumeric field values, mathematical operators, and constants to define\ncalculation rules.\n\n\\chapter{Form Element Sets}\\label{form-element-sets}\n\nIf you're here looking for information on reusable field sets, you're in\nthe right place. We call them Element Sets in the Liferay Forms\napplication because these sets include more than just fields: they\ninclude the layout and configuration of the fields as well. In the\nfuture, additional styling elements will be available here, too.\n\nElement sets are more like composable Form fragments or reusable Form\nblocks.\n\nSometimes you might be able to create an entire form by composing\nexisting Element Sets. Your colleagues might call you lazy, but we'd\ncall you industrious.\n\n\\section{Creating Element Sets}\\label{creating-element-sets}\n\nTo create Element Sets, go to Site Menu → Content \\&Data → Forms. The\nForms view is displayed by default. Click the \\emph{Element Sets} tab,\nand any existing Element Sets appear, just like existing Forms are\ndisplayed in the Forms view. Click the \\emph{Add} button\n(\\includegraphics{./images/icon-add.png}).\n\nHere's the thing. If you know how to\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-and-managing-forms}{create\na Form}, you already know how to create an Element Set. The process is\nidentical. Drag and drop elements onto the form builder palette,\nconfiguring fields as you go.\n\nWhen you're finished, click \\emph{Save}. Element Sets aren't\npublishable, so there's no button for that.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-element-sets.png}\n\\caption{Creating Element Sets is just like creating Forms. You just\ncan't publish them.}\n\\end{figure}\n\nOnce an Element Set is saved, it's instantly available for use, even in\nthe same Element Set. That's right, you can use Element Sets to create\nElement Sets.\n\n\\section{Using Element Sets}\\label{using-element-sets}\n\nTo use an Element Set in a Form:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Form Builder.\n\\item\n  If the Add Elements sidebar isn't already displayed, open it by\n  clicking the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}).\n\\item\n  The default view in the Add Elements sidebar is Elements. Instead\n  click \\emph{Element Sets}.\n\\item\n  Drag the Element Set onto the Form Builder, just like you would any\n  single Form Element.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-add-element-set.png}\n\\caption{Add an Element Set the same way you add other Form Elements,\nlike fields.}\n\\end{figure}\n\nYou probably already guessed that the process is identical for using\nElement Sets to build other Element Sets. That's all there is to it.\nThere are just a couple more things to note:\n\n\\begin{itemize}\n\\item\n  Once an Element Set is added to a Form, there's no connection with the\n  root Element Set. You're free to move or configure the Fields and\n  Elements however you want.\n\\item\n  Editing an Element Set doesn't retroactively affect the Forms where\n  the Element Set was used.\n\\end{itemize}\n\nThink ahead. Are there some common fields you'll commonly need to\nconfigure in your Forms? If so, create them as Element Sets once and\nsave yourself repetitive work.\n\n\\chapter{Data Providers}\\label{data-providers}\n\nSelect from List fields can hold a lot of options. There are around 200\ncountries on Earth, for example. If you have unoccupied unpaid interns\nyou could ask them to type each country into the Select from List field\nmanually, or you could auto-populate your select fields using a REST web\nservice. This saves you (or your interns) the trouble of typing all\nthose options, and you can rely on someone else (hopefully a trustworthy\nexpert) to keep the data updated.\n\nWhen setting up a data provider, you're accessing a\n\\href{https://en.wikipedia.org/wiki/Representational_state_transfer}{REST\nweb service}. Use the JSON web services registered in Liferay DXP, or\nany other REST web service you can access. To find a list of the\nregistered JSON web services in Liferay DXP, navigate to\n\\url{http://localhost:8080/api/jsonws} (assuming you're running a local\nserver). Browse the available Liferay services. Many times, the services\nuseful to you in the Forms application get a list of something. Find the\n\\texttt{get-countries} JSON web service (there are two---use either one)\nand click on it, then click \\emph{Invoke}. The \\emph{Result} tab shows a\nlist of countries using JSON syntax, like this:\n\n\\begin{verbatim}\n[\n  {\n    \"a2\": \"AF\",\n    \"a3\": \"AFG\",\n    \"countryId\": \"20\",\n    \"idd\": \"093\",\n    \"mvccVersion\": \"0\",\n    \"name\": \"afghanistan\",\n    \"nameCurrentValue\": \"Afghanistan\",\n    \"number\": \"4\"\n  },\n    ...\n\\end{verbatim}\n\nThat's the record for the country Afghanistan. As you can see in the\n\\emph{URL Example} tab, the URL you entered into the data provider form\nis the same as the one generated for accessing the\n\\texttt{get-countries} JSON web service. Find the URL for any registered\nJSON web service using this same procedure.\n\nNote the field you want Users to select. With this service, it's most\nlikely \\texttt{nameCurrentValue}, because it contains the full, properly\ncapitalized name of the country.\n\n\\noindent\\hrulefill\n\n\\emph{Enabling Access to Data on the Local Network:} By default, you\ncannot configure data providers to use URLs on the local network. This\nis a good default for security in a production environment, but makes\ntesting more difficult. To enable local network access from data\nproviders, got to Control Panel → Configuration → System Settings → Data\nProviders (under Content \\& Data), and enable \\emph{Access Local\nNetwork}. You'll need to configure this if you want to follow the basic\nexample in the next section.\n\n\\noindent\\hrulefill\n\n\\section{Adding a Basic Data\nProvider}\\label{adding-a-basic-data-provider}\n\nTo add a \\emph{Countries of the World} Data Provider for use in your\nForms,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the Forms application.\n\\item\n  Click the \\emph{Data Providers} tab.\n\\item\n  Click the Add button (\\includegraphics{./images/icon-add.png}).\n\n  The REST Data Provider form loads.\n\\item\n  Fill out the Name and Description fields.\n\n  Name: \\texttt{Countries\\ of\\ the\\ World}\n\\item\n  Enter the URL and authentication tokens for the REST service. For the\n  \\texttt{get-countries} service:\n\n  URL: \\texttt{http://localhost:8080/api/jsonws/country/get-countries/}\n\n  User Name: \\texttt{adminuser@liferay.com}\n\n  Password:\\texttt{adminuserpass}\n\\item\n  In the Outputs fields, specify which field from the REST service\n  populates your select list.\n\n  Label: \\texttt{Country\\ Name}\n\n  Path: \\texttt{\\$..nameCurrentValue}\n\n  Type: \\texttt{List}\n\\item\n  Save the Data Provider.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-simple-data-provider.png}\n\\caption{Set up a simple data provider in no time.}\n\\end{figure}\n\nWhat's that \\texttt{\\$..} before \\texttt{nameCurrentValue}? It's\nJsonPath syntax to navigate the JSON data structure and specify the path\nto the output. Learn more about JsonPath\n\\href{https://github.com/json-path/JsonPath/blob/master/README.md}{here}\nand \\href{http://goessner.net/articles/JsonPath/}{here}.\n\n\\section{Using a Data Provider in a Select\nField}\\label{using-a-data-provider-in-a-select-field}\n\nOnce the Data Provider is configured, use it to populate a Select from\nList field:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the Form Builder (add a new form or edit an existing one)\n\\item\n  Drag a Select from List field onto the form.\n\\item\n  In the Create List section, choose \\emph{From Data Provider}.\n\\item\n  Choose the Data Provider and its Output Parameter:\n\n  Choose a Data Provider: \\texttt{Countries\\ of\\ the\\ World}\n\n  Choose an Output Parameter: \\texttt{Country\\ Name}\n\\item\n  Publish the form and test it.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-select-data-provider.png}\n\\caption{Form users select an option form the list populated by the Data\nProvider.}\n\\end{figure}\n\nYour Data Provider is now being used to populate a select field.\nHowever, this form should be submitted by Guest users, who don't\ncurrently have permission to see the list of results from the data\nprovider. Arrgh! Now what?\n\n\\section{Granting Data Provider\nPermissions}\\label{granting-data-provider-permissions}\n\nTo configure the data provider's permissions, go to the Forms\napplication (\\emph{Site Administration} → \\emph{Content \\& Data} →\n\\emph{Forms}). Open the Data Providers tab. For the data provider you\nwant to configure, click the Actions button\n(\\includegraphics{./images/icon-actions.png}), then \\emph{Permissions}.\n\nConfigure the permissions you need. If Guests are to fill out the form,\nthey need the \\emph{View} permission, or else they won't be able to see\nthe options provided by the data provider. Once you grant permissions,\nclick \\emph{Save}.\n\n\\section{Data Provider Configuration}\\label{data-provider-configuration}\n\nThe above instructions cover adding a basic Data Provider. Knowing more\nabout each field in the Data Provider setup form opens up more\npossibilities.\n\n\\begin{description}\n\\item[\\textbf{URL}]\nThe URL of an internal or external REST service endpoint. Consider the\nREST service at https://restcountries.eu/, which contains a REST API\nendpoint to find countries by \\texttt{region}:\n\n\\texttt{https://restcountries.eu/rest/v2/region/\\{region\\}}\n\\end{description}\n\nData Provider URLs can take two parameter types: path parameters and\nquery parameters.\n\nPath parameters are part of the URL calling the REST web service, and\nare added using the pattern\n\\texttt{https://service-url.com/service/\\{path\\_parameter\\_name\\}}:\n\nThe \\texttt{restcountries.eu} service's \\texttt{region} endpoint's path\nparameter is \\texttt{\\{region\\}}. Path parameters are mandatory parts of\nthe URL, so make sure you specify an Input (see below) with a\n\\emph{Parameter} field value matching the path parameter from the URL.\n\nQuery parameters are complementary parts of the URL that filter the\noutput of the service call, following the pattern\n\\texttt{?query\\_parameter=query\\_parameter\\_value}:\n\n\\begin{verbatim}\nhttps://restcountries.eu/rest/v2/all?fields=capital\n\\end{verbatim}\n\nUnlike path parameters, query parameters are optional.\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{User Name and Password}]\nCredentials used to authenticate to the REST Web Service, if necessary.\n\\item[\\textbf{Cache data on the first request.}]\nIf the data is cached, a second load of the select list field is much\nfaster, since a second call to the REST service provider is unnecessary.\n\\item[\\textbf{Timeout}]\nThe time (in ms) to allow the REST service call to process before\naborting the request, if a response is not returned.\n\\item[\\textbf{Inputs}]\nConfigure path or query parameters from the REST service to filter the\nREST service's response. Specify the Label, Parameter, and Type (Text or\nNumber), and choose whether the input is required to use the Data\nProvider. You can add multiple Inputs. To provide a way for users to\nspecify the input value, use an\n\\href{/docs/7-2/user/-/knowledge_base/u/action-autofill}{\\emph{Autofill}\nForm Rule}. A User enters input into one field, and their input is sent\nto the REST service. The REST service's response data is filtered by the\ninput parameter.\n\\item[\\textbf{Outputs}]\nThe Parameter to display in Select from List or Text fields with\nautocomplete enabled. You can add multiple Outputs. Outputs can be\nfiltered by inputs (see above) but can also be displayed without\nconfiguring input filtering. Specify the Label, Path, and Type (Text,\nNumber, or List). The Path field is specified in\n\\href{https://github.com/json-path/JsonPath/blob/master/README.md}{JsonPath\nsyntax}, so it must always start with a \\texttt{\\$}. The type of data\nreturned by the Path must match the type you choose in the Type field.\nUsing the \\texttt{restcountries.eu} service, specify the \\texttt{name}\nfield as an Output by entering enter \\texttt{\\$..name} in the Path\nfield.\n\\end{description}\n\nIf you have a more complex JsonPath expression to construct (for\nexample, you need the names of all countries with a population over 100\nmillion---\\texttt{\\$..{[}?(@.population\\textgreater{}100000000){]}.name}\nwith the \\texttt{restcountries.eu} service), consider using an online\nJsonPath evaluator, like \\href{http://jsonpath.herokuapp.com/}{this one}\nor \\href{https://jsonpath.com/}{this one}.\n\n\\noindent\\hrulefill\n\n\\textbf{Hint:} To display one value to the user, but persist another in\nthe database, enter both into the Paths field, separated by a semicolon:\n\n\\begin{verbatim}\n  `$..name;$..numericCode`\n\\end{verbatim}\n\nIf this is used with the \\texttt{restcountries.eu} data provider, the\nname of the country is displayed to the User, while the numeric country\ncode is stored in the database.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-data-provider-configuration.png}\n\\caption{Set up Data Providers to display data retrieved from a REST\nservice.}\n\\end{figure}\n\n\\section{Troubleshooting Data Provider\nErrors}\\label{troubleshooting-data-provider-errors}\n\nTo uncover errors arising from Data Provider failures,\n\\href{/docs/7-2/user/-/knowledge_base/u/server-administration}{configure\nlog levels} for these services:\n\n\\textbf{Category:}\n\\texttt{com.liferay.dynamic.data.mapping.data.provider.internal.DDMDataProviderInvokerImpl}\n\\emph{Level:} WARN\n\n\\textbf{Category:}\n\\texttt{com.liferay.dynamic.data.mapping.form.field.type.internal.DDMFormFieldOptionsFactoryImpl}\n\\emph{Level:} DEBUG\n\nWith Data Providers, the world's (RESTful) data is at your disposal to\nuse with the Forms application.\n\n\\chapter{Auto-Save}\\label{auto-save}\n\nLosing progress on a partially created form is bad. Make sure to save\nyour work frequently as you're creating forms. But if you forget to save\nyour work, Liferay Forms has your back.\n\nBy default, a form is auto-saved every minute. You won't notice anything\nin the form builder while the back-end auto-saves the form. You can\nchange the auto-save duration in \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{System Settings} → \\emph{Forms} (in the\nContent and Data section). To disable auto-save, set the interval to\n\\emph{0}.\n\nFor unpublished forms, an auto-save works just like a manual save. For a\npublished form, however, auto-saved data isn't automatically propagated\nto the form. You must click the \\emph{Save} button in the form builder\nto publish the changes.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-autosave-interval.png}\n\\caption{Configure the auto-save duration.}\n\\end{figure}\n\n\\chapter{Translating Forms}\\label{translating-forms}\n\nForms can be translated to any locale that Liferay DXP supports. The\nform builder specifies a translation of the form's default language.\n\nThe form's default language and the available translations are set in\nthe\n\\href{/docs/7-2/user/-/knowledge_base/u/social-settings-and-languages\\#languages}{site's\nlanguage configuration}.\n\nFollow these steps to create a form translation:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} (your site's menu) → \\emph{Content \\&\n  Data} → \\emph{Forms} and open the form to translate.\n\\item\n  Click the + icon next to the current translation language and choose\n  from the available languages.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-add-translation.png}\n  \\caption{Add a translation for the form.}\n  \\end{figure}\n\\item\n  Translate the form's title, field labels, field options, field\n  placeholder text, and any other text visible to the user.\n\\item\n  Save and publish the form.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-translate2.png}\n\\caption{Translate as much of the form as possible into each language\nyou expect users to need.}\n\\end{figure}\n\nTo fill out a translated form in a translated language,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Access the form. If a signed-in user accesses the form and a\n  translation is available in the user's language, the user sees the\n  translated form by default.\n\\item\n  To see the form in a different language, click the language icon and\n  select a language.\n\\item\n  Fill out the form as usual and click \\emph{Submit}.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Translations work differently depending on how a User\naccesses a Form:\n\nIf\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-and-managing-forms\\#accessing-forms}{accessed\nin the Form widget on a Liferay DXP page}, the Form is displayed in the\nUser's language automatically. If there's no translation available for\nthe User's language, the default language of the Form is displayed.\n\nIf accessed via direct\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-and-managing-forms\\#accessing-forms}{URL},\nthe Form translation must be selected manually.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-translate3.png}\n\\caption{Select the form's language.}\n\\end{figure}\n\n\\chapter{Autocompleting Text Fields}\\label{autocompleting-text-fields}\n\nIt's been scientifically proven that Internet users are lazy (not you,\nof course---other Internet users). For example, some users may not fill\nout your form if you make them type the entire title of their country in\nan employment application. This is especially true if they're filling\nout the form on their mobile devices. Make users' lives easier by\nconfiguring autocomplete on a form's text fields.\n\nWhy not just use a select field with a data provider to guide user\ninput? Sometimes a data provider can't encompass all possible field\nentries. For example, if your data provider doesn't include\n\\href{https://en.wikipedia.org/wiki/Principality_of_Sealand}{mythical\ncountries founded on old sea platforms}, users the Principality of\nSealand can't enter anything into the select field. Instead use a text\nfield with autocomplete so users can begin typing their country's name\nand then select it from a list when it appears. Autocomplete combines a\ntext field (accepting any response that meets your validation criteria)\nand common choices to select from. It's a win-win situation.\n\n\\section{Configuring Autocomplete}\\label{configuring-autocomplete}\n\nBefore configuring autocomplete for your text fields,\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-and-managing-forms}{create\na form and add a text field}. If you want the autocomplete options to be\npopulated by a REST data provider,\n\\href{/docs/7-2/user/-/knowledge_base/u/data-providers}{configure one}\nbefore creating your form. Now you're ready to configure autocomplete\nfor the field:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the field configuration sidebar, click the \\emph{Properties} tab.\n\\item\n  Click the \\emph{Autocomplete} switcher so it's enabled.\n\\item\n  Select a data provider or create one manually. You can set up a data\n  provider from a\n  \\href{/docs/7-2/user/-/knowledge_base/u/data-providers}{REST service},\n  or manually enter the options users should see when they start typing\n  in the text field.\n\\item\n  Save and Publish the form.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-autocomplete-manually.png}\n\\caption{You can configure a manual data provider to specify the options\nusers can select from.}\n\\end{figure}\n\nOnce users begin entering text into the field, a selection list of\noptions appears. As they enter additional text, the list is refined to\ninclude only options that contain the currently entered text. For\nexample, the imaginary users from Sealand (all two of them) begin\nreluctantly typing their country of origin by entering an \\emph{S}.\nThey're delighted to see a selection list with a bunch of countries\ncontaining the letter \\emph{S} appear for their selection convenience.\nIf they continue typing and enter \\emph{e}, the list is refined to\noptions that have \\emph{se} in their name (for example Serbia and\nSenegal). If they continue typing and enter \\emph{a}, they'll now only\nsee one option, Sealand, if it's in your data provider. Selecting it\nfrom the list after typing the first three letters is much easier than\ntyping the remaining letters.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-autocomplete-filtering.png}\n\\caption{When typing in a field with autocomplete, users are presented a\nlist of selections from the configured data provider. The displayed\nresults are filtered to include only selections containing the text\nentered by the user.}\n\\end{figure}\n\nWhat will the Forms team think of next? Configuring telepathic\nconnections to the Forms application would be nice. Then users could\njust think their form field entries into existence. Stay tuned.\n\n\\chapter{Form Success Pages}\\label{form-success-pages}\n\nAfter users submit one of your whiz-bang forms, what's next? How will\nthey know they're done and can close the browser window or tab? What if\nthey think their submission didn't go through and wonder if they need to\nfill out the whole form again? By default, submitting a form displays\nthe default success message and returns users to the form's now empty\nfirst page. Don't leave users feeling equally empty. Instead, configure\na \\emph{Success Page}. A Success Page is a terminal page showing users\nthey've finished filling out the form and their submission was\nsuccessfully received. A Success Page can even urge users to close the\nbrowser window or tab.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/your-request-completed-successfully.png}\n\\caption{The default success message alerts users when their request\ncompletes successfully.}\n\\end{figure}\n\nA Success Page is simple. It has a title in bold text and a description\nbeneath the title. A common alternative to using a Success Page is to\n\\href{/docs/7-2/user/-/knowledge_base/u/redirecting-users}{redirect\nusers to a different page in your Site}. What should you put in a\nSuccess Page? Whatever you want. If you can't think of anything\nimportant or creative to say, use the default message:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-success-page-default.png}\n\\caption{There's a default Success Page message if you can't think of\nanything else to say.}\n\\end{figure}\n\nTo configure a Success Page,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a form in \\emph{Site Administration} (your site's menu) →\n  \\emph{Content \\& Data} → \\emph{Forms}.\n\\item\n  Click on the form page's \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and choose \\emph{Add\n  Success Page}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-success-page-add.png}\n\\caption{Add a Success Page using the edit menu for the form page.}\n\\end{figure}\n\nOnce the Success Page is added to your form, fill in the \\emph{Title}\nand \\emph{Content} fields however you please. When the form is saved and\npublished, the Success Page is live for your form users.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can't preview the Success Page. Success Pages can\nonly be viewed once a form is submitted, and the \\emph{Submit} button\nisn't available in the form preview. The \\emph{Preview Form} link in the\nform builder only lets you preview the form's regular pages (use the\n\\emph{Next} button to navigate through the form).\n\nTo see what your Success Page looks like, submit a test entry of the\nform and then delete it if needed. For more information, see\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-form-entries\\#viewing-form-entries}{the\ndocumentation on viewing and managing form entries}.\n\n\\chapter{Workflow and Forms}\\label{workflow-and-forms}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow}{The workflow engine}\nis for sending a submitted asset through a workflow process before it's\npublished. Most assets are configured to use workflow at the instance or\nSite level.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-workflow-configuration.png}\n\\caption{Workflow is enabled in the Control Panel or in Site\nAdministration for most Liferay DXP assets.}\n\\end{figure}\n\nForms are different, so they don't appear in the above image. There are\nso many use cases for forms, and there could be so many per site, that a\nsite- or instance-scoped workflow configuration won't serve your needs\nwell. Instead, configure workflow for \\emph{each form} separately.\n\n\\section{Enabling Workflow in a Form}\\label{enabling-workflow-in-a-form}\n\nTo enable workflow in a form,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the form's editor by opening the Menu, selecting your Site,\n  navigating to \\emph{Content \\&Data} → \\emph{Forms}, and clicking on\n  the form you want.\n\\item\n  Click the Options button (\\includegraphics{./images/icon-options.png})\n  and choose \\emph{Settings}.\n\\item\n  The Settings window has a \\emph{Select a Workflow} drop-down. Find the\n  workflow you want, select it, and then click \\emph{Done}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/form-settings.png}\n  \\caption{Enable workflow for each form in its Settings window.}\n  \\end{figure}\n\\end{enumerate}\n\n\\section{Testing the Workflow}\\label{testing-the-workflow}\n\nTest the workflow process:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the form to a page.\n\\item\n  Click \\emph{Submit for Publication} to submit the form entry.\n\\end{enumerate}\n\nNext go find the form entry in the Forms application:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go back to the Forms application in the Menu in your Site's\n  \\emph{Content \\& Data} section.\n\\item\n  Click the Form's \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{View\n  Entries}.\n\n  The entry is currently marked \\emph{Pending}.\n\\end{enumerate}\n\nNow \\href{/docs/7-2/user/-/knowledge_base/u/reviewing-assets}{approve\nthe form record}:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{My Workflow Tasks} in the User Personal Menu.\n\\item\n  Click the \\emph{Assigned to My Roles} tab.\n\\item\n  Click on the form entry.\n\\item\n  Click the Actions button (\\includegraphics{./images/icon-actions.png})\n  and choose \\emph{Assign to Me}.\n\\item\n  Click \\emph{Done}.\n\\item\n  Click the Actions button (\\includegraphics{./images/icon-actions.png})\n  again, then click \\emph{Approve}.\n\\item\n  Click \\emph{Done} again.\n\\item\n  Navigate back to the View Entries screen for the form, and now the\n  entry is marked as \\emph{Approved}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-view-entries-status.png}\n\\caption{Each entry's status is visible in the Forms application's Form\nEntries screen.}\n\\end{figure}\n\n\\chapter{Duplicating Forms and Form\nFields}\\label{duplicating-forms-and-form-fields}\n\nRepetitive tasks are error prone. Instead of duplicating effort,\nduplicate forms and form fields.\n\nTo duplicate a form,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} (your Site's menu) → \\emph{Content \\&\n  Data} → \\emph{Forms}.\n\\item\n  Click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) for the form to\n  duplicate.\n\\item\n  Click \\emph{Duplicate}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-duplicate-form.png}\n\\caption{The Duplicate option works the same for forms and form fields.}\n\\end{figure}\n\nThe form is duplicated and automatically named \\emph{Copy of {[}Original\nForm Name{]}}. Once duplicated, you can edit the form however you want.\nWhen you duplicate a form, all configurations and form rules are\nduplicated as well.\n\nTo duplicate a form field,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} (your Site's menu) → \\emph{Content \\&\n  Data} → \\emph{Forms} and open or add a form.\n\\item\n  In the Builder view, hover over the form field to duplicate and click\n  the \\emph{Copy} icon (\\includegraphics{./images/icon-copy.png}).\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-duplicate-form-field.png}\n\\caption{You can duplicate form fields.}\n\\end{figure}\n\nThe field is duplicated and labeled \\emph{Copy of {[}Original Field\nLabel{]}}. All the form's properties, including its data provider\nconfigurations, are copied as well.\n\n\\chapter{Form Pages}\\label{form-pages}\n\nAre users more likely to abandon long forms with lots of scrolling? Are\nthey more likely to see a multi-page form and abandon it without a\nsecond look, assuming that it gets longer and more tedious with every\npassing page? Such usability questions are worth thinking about. If you\ndecide multiple pages are appropriate for your form, Liferay Forms\nsupports two pagination styles: default, and alternate.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-pagination1.png}\n\\caption{The default pagination style.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-pagination2.png}\n\\caption{The alternate pagination style as seen in the Form Builder.}\n\\end{figure}\n\nTo add a form page,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to a form's builder view.\n\\item\n  Click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) at the top-right corner\n  of the form, then click \\emph{Add New Page}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-page-actions.png}\n\\caption{You can add new pages or reset the current page from the Page\nActions menu.}\n\\end{figure}\n\nYou can also delete form pages, add\n\\href{/docs/7-2/user/-/knowledge_base/u/form-success-pages}{success\npages}, and switch pagination modes.\n\n\\chapter{Help Text, Placeholder Text, and Predefined\nValues}\\label{help-text-placeholder-text-and-predefined-values}\n\nForm fields can have help text, placeholder text, and predefined values.\n\n\\begin{itemize}\n\\item\n  \\textbf{Help Text:} Text that appears as a sub-heading to the field\n  label, but doesn't appear in the field entry area. Enter help text in\n  the Basic tab of the field's sidebar menu.\n\\item\n  \\textbf{Placeholder Text:} Text in the field entry area that isn't\n  submitted if the field is left untouched by the user.\n\\item\n  \\textbf{Predefined Value:} Text in the field entry area that is\n  submitted if the field is left untouched by the user.\n\\end{itemize}\n\nAll form field types can have help text, and all form field types that\naccept user input can have predefined values. Only text and numeric\nfields can have placeholder text.\n\nTo enter placeholder text or predefined values,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open a field's sidebar menu.\n\\item\n  Open the \\emph{Properties} tab.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-placeholder-predefined-values.png}\n\\caption{Predefined values and placeholder text are entered in the\nProperties tab.}\n\\end{figure}\n\nFor example, many forms start with a \\emph{Full Name} field. You can use\nhelp text and/or placeholder text to inform users that you need their\nfull names, regardless of length. Alternatively, if you're asking users\nto specify how many sandwiches they eat for lunch, a predefined value of\n\\emph{1} probably makes sense.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-help-placeholder-predefined.png}\n\\caption{The Full Name field here uses help text and placeholder text,\nwhile the sandwiches field uses a predefined value.}\n\\end{figure}\n\nRemember, placeholder values aren't submitted if the field is left\nblank, so you don't have to worry about getting a bunch of submissions\nfrom \\emph{Maximillian Aurelius Piroux the 11th}.\n\n\\chapter{Validating Text and Numeric\nFields}\\label{validating-text-and-numeric-fields}\n\nValidation ensures that only certain values are entered in a field.\nValidation functionality is available for text and numeric fields.\n\nTo enable validation,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a Text or Numeric field to a form in the Builder view.\n\\item\n  Open the field's \\emph{Properties} tab.\n\\item\n  Turn on the \\emph{Validation} toggle to enable validation and open its\n  configuration options.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-text-validation.png}\n\\caption{Validate data to ensure you're collecting only useful\ninformation.}\n\\end{figure}\n\n\\section{Validating Text Fields}\\label{validating-text-fields}\n\nValidation for text fields contains several options. You must first\nchoose a list of available conditions to check:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  If Input \\emph{Contains}\n\\item\n  If Input \\emph{Does Not Contain}\n\\item\n  If Input \\emph{Is not URL}\n\\item\n  If Input \\emph{Is not Email}\n\\item\n  If Input \\emph{Does not Match}\n\\end{itemize}\n\nIf the condition isn't met, an error message is displayed to the user.\nWhere you go from there depends on which condition you used.\n\n\\section{If Input Contains/Does Not\nContain}\\label{if-input-containsdoes-not-contain}\n\nWhen you validate text data to check if it contains a certain value,\nthere are two additional steps to take after selecting the condition:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Enter the text to check for.\n\\item\n  Enter an error message so users understand why their submission\n  failed.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-text-val-contains.png}\n\\caption{If \\emph{Liferay} isn't part of the field's value, an error\nmessage is displayed.}\n\\end{figure}\n\n\\section{If Input Is not URL/Email}\\label{if-input-is-not-urlemail}\n\nChecking for properly formatted URLs and emails is easy. Just choose the\ncondition from the \\emph{If Input} drop-down and enter the error\nmessage.\n\nValid URLs begin with \\texttt{http://} or \\texttt{https://}. Valid\nemails must contain \\texttt{@}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-text-val-email.png}\n\\caption{Use text field validation to make sure users enter a valid\nemail address or URL.}\n\\end{figure}\n\n\\section{If Input Does Not Match}\\label{if-input-does-not-match}\n\nThe \\emph{Does Not Match} condition is used for entering\n\\href{https://en.wikipedia.org/wiki/Regular_expression}{regular\nexpressions} to create custom validation criteria. For example, use this\nregular expression to ensure that ten consecutive numeric digits are\nentered in a phone number field:\n\n\\begin{verbatim}\n^[0-9]{10}$\n\\end{verbatim}\n\nIf you use regular expression validation, provide some explanatory text\n(e.g., help text, placeholder text, and a clear error message) to guide\nform users in entering the proper data.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-text-val-regex.png}\n\\caption{Regular expression text validation opens up countless\npossibilities.}\n\\end{figure}\n\n\\section{Validating Numeric Fields}\\label{validating-numeric-fields}\n\nNumeric field validation is similar to text field validation, but the\nconditions compare the value of the number entered to some other value.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-numeric-val2.png}\n\\caption{Numeric conditions constrain user-entered numeric data.}\n\\end{figure}\n\nAvailable conditions to check include\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Is greater than or equal to\n\\item\n  Is greater than\n\\item\n  Is not equal to\n\\item\n  Is less than or equal to\n\\item\n  Is less than\n\\end{itemize}\n\nFor example, to make sure users don't enter a number over 10, enable\nvalidation and use \\emph{Is greater than} with a value of \\emph{10}. Use\nthe message \\emph{Please enter 10 or less}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-numeric-val1.png}\n\\caption{Make sure user-entered numeric data is within reasonable\nbounds. Nobody needs 11 sandwiches for lunch.}\n\\end{figure}\n\nNote that numeric fields are text fields validated to allow only numeric\ndata entry. That's why they're in the Customized Elements section of the\nform fields list. In addition, the property \\emph{My numeric type is}\n(can be Integer or Decimal) on the Basic tab of a numeric form is\nanother form of validation.\n\n\\chapter{Enabling CAPTCHA on Form\nSubmissions}\\label{enabling-captcha-on-form-submissions}\n\n\\href{https://en.wikipedia.org/wiki/CAPTCHA}{CAPTCHA} prevents a bot\nfrom submitting forms. It's often used in\n\\href{/docs/7-2/deploy/-/knowledge_base/d/logging-into-liferay}{login\napps}, but you can also use it in the Forms app.\n\nTo enable CAPTCHA, click the form's \\emph{Options} button\n(\\includegraphics{./images/icon-options.png}) and select\n\\emph{Settings}. Enable the \\emph{Require CAPTCHA} setting, click\n\\emph{Done}, and save the form. That's all there is to it!\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-settings-captcha.png}\n\\caption{You can enable CAPTCHA for your form in the Form Settings\nwindow.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-captcha.png}\n\\caption{Once you enable CAPTCHA, your form has protection against bot\nsubmissions.}\n\\end{figure}\n\n\\chapter{Form Notifications}\\label{form-notifications}\n\nYou can configure the Forms app to send a notification email each time a\nform entry is submitted.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the form's \\emph{Form Settings} section by clicking the\n  \\emph{Options} button (\\includegraphics{./images/icon-options.png})\n  and selecting \\emph{Settings}.\n\\item\n  Click the \\emph{Email Notifications} tab, enable the option \\emph{Send\n  an Email Notification for Each Entry}, and fill out these fields:\n\n  \\textbf{From Name:} The sender's name. This could be the Site name,\n  the form name, or anything else informative to the recipient.\n\n  \\textbf{From Address:} The sender's email address. You can use\n  something like \\texttt{noreply@example.com}, so that recipients don't\n  try to reply.\n\n  \\textbf{To Address:} The recipient's email address (e.g.,\n  \\texttt{test@example.com}).\n\n  \\textbf{Subject:} The email's subject. An informative subject line\n  tells the recipient what happened. For example, An application for\n  employment was submitted in The Lunar Resort*.\n\\end{enumerate}\n\nNote that if you\n\\href{/docs/7-2/user/-/knowledge_base/u/sending-form-entries-through-a-workflow}{enabled\nworkflow for the form} and it already sends a notification, you might\nnot need to configure the Forms app to generate a notification.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-notification-email.png}\n\\caption{Configure email notifications each time a form entry is\nsubmitted.}\n\\end{figure}\n\n\\chapter{Redirecting Users}\\label{redirecting-users}\n\nWhen users submit a form, you can present them with another page\nindicating success or some other information related to their\nsubmission. Sometimes all you need is a\n\\href{/docs/7-2/user/-/knowledge_base/u/form-success-pages}{success\npage}, but other times you might want to send users to a specific URL.\n\nWhatever your use case is, follow these steps to set up a redirect URL:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the form's \\emph{Form Settings} section by clicking the\n  \\emph{Options} button (\\includegraphics{./images/icon-options.png})\n  and select \\emph{Settings}.\n\\item\n  Enter the redirect URL in the \\emph{Redirect URL} field.\n\\end{enumerate}\n\nThat's it! Now when users submit the form, they're not left wondering\nwhat to do next.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-redirect.png}\n\\caption{Redirect users after they submit a form.}\n\\end{figure}\n\n\\chapter{Form Permissions}\\label{form-permissions}\n\nTo access a form's permissions, first navigate to the Forms app in\n\\emph{Site Administration} (your site's menu) → \\emph{Content \\& Data} →\n\\emph{Forms}. Then click the form's \\emph{Actions} button\n(\\includegraphics{./images/icon-actions.png}), and select\n\\emph{Permissions}.\n\nBy default, you can grant these permissions for a form:\n\n\\textbf{Delete:} Delete the form.\n\n\\textbf{Permissions:} Access and configure the form's permissions.\n\n\\textbf{Update:} Update form entries.\n\n\\textbf{Add Form Instance Record:} Submit form entries.\n\n\\textbf{View:} View the form.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-form-permissions.png}\n\\caption{You can configure a form's permissions.}\n\\end{figure}\n\nNote that guest users can view and fill out forms by default. The\n\\emph{Guest} Role has \\emph{View} and \\emph{Add Form Instance Record}\npermissions.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} By default, all users inherit the Guest Role's\npermissions. The Guest Role represents unauthenticated visitors of your\nSite. If you want to let Guest users submit forms (the default setting),\nit makes sense that authenticated users can also. To disable automatic\ninheritance of the Guest Role's permissions, set\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#Permissions}{this\nproperty} in your \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\n `permissions.check.guest.enabled=false`\n\\end{verbatim}\n\n\\chapter{Styling Form Pages}\\label{styling-form-pages}\n\nLet's face it: nobody likes an ugly, confusing form. Styling your\n\\href{/docs/7-2/user/-/knowledge_base/u/form-pages}{form pages} lets you\nmake your forms user friendly. There are two features for styling your\nforms:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Create rows and columns for form fields.\n\\item\n  Move fields from one location to another.\n\\end{enumerate}\n\nSometimes it doesn't make sense to use the default single-column,\nvertically-oriented form. For example, a form with many fields can save\nspace by putting them in different columns. You can also use a mixed\napproach, with each row broken into a different number of columns. The\nfollowing screenshots show examples of these layouts.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-form-row.png}\n\\caption{This is the default single-column, vertically-oriented form.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-layout-multicolumn.png}\n\\caption{Putting form fields in multiple columns can give you more\nspace.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/forms-layout-mixed.png}\n\\caption{The first row is in two columns and the second row is in three\ncolumns.}\n\\end{figure}\n\nBy default, dragging a field onto a form page adds a field that occupies\nan entire row. Follow these steps to resize the field to make room for\nmore fields in the row (columns):\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Hover over the field to reveal its borders:\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-field-borders.png}\n  \\caption{Form field borders.}\n  \\end{figure}\n\\item\n  Drag the right or left edge of the field to resize it.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-field-resized.png}\n  \\caption{After resizing, the field is smaller.}\n  \\end{figure}\n\\item\n  Add a new field to the remaining space in the row.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-field-columns.png}\n  \\caption{There are now two fields in the row.}\n  \\end{figure}\n\\end{enumerate}\n\nYou can also move fields. To do so, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Hover over the field and the cursor becomes a hand.\n\\item\n  Drag the field to an open location in the builder. Available locations\n  are highlighted in blue and outlined with a dotted line. A field moved\n  to a new row fills that entire row. A field moved to an existing row\n  fills that row's remaining space.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/forms-move-field.png}\n  \\caption{You can also move fields on form pages.}\n  \\end{figure}\n\\end{enumerate}\n\n\\chapter{Dynamic Data Lists}\\label{dynamic-data-lists}\n\nDynamic data lists display forms created from field sets called\n\\emph{data definitions}. Data definitions consist of a form's field\ntypes (e.g., text, boolean, date, radio buttons, selector menus, etc.)\nand those fields' labels and settings. Data definitions effectively\nserve as data models for a dynamic data list. For example, you could\ncreate a data definition with two text fields: one for a user's name,\nand one for their comments. You could then display a form that gathers\nuser feedback via a dynamic data list that uses that data definition.\n\nTo summarize:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Data Definitions:} Define a form's fields.\n\\item\n  \\textbf{Dynamic Data Lists:} Display a form based on a data\n  definition.\n\\end{itemize}\n\nYou can create one or multiple dynamic data lists from a single data\ndefinition. The user data entered for each dynamic data list is kept\nseparate, even if the data definition is shared. For instance, you could\nuse the example data definition above to create several dynamic data\nlists, and then place them anywhere you need to get feedback from users.\nBecause each dynamic data list's form data is separate, you don't need\nto worry about trying to figure out which dynamic data list the user\ncomment came from.\n\nDynamic data lists are flexible. You don't have to restrict dynamic data\nlists to simple input. You could create something as complex as an\nentire data entry system for real estate listings, or any other simple\nlist-based application you can dream up.\n\nYou create data definitions and dynamic data lists in from the Site\nMenu's Content → Dynamic Data Lists application. Creating data\ndefinitions and lists doesn't require any coding. However, additional\nformatting can be added with\n\\href{https://freemarker.apache.org/}{FreeMarker} templates.\n\nThese tutorials show you how to create and use data definitions and\ndynamic data lists:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Creating data definitions.\n\\item\n  Creating dynamic data lists.\n\\item\n  Creating form and display templates.\n\\end{itemize}\n\n\\section{System Configuration}\\label{system-configuration}\n\nThere are two Dynamic Data Lists entries in System Settings. The Dynamic\nData Lists Service entry contains one setting:\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Add Default Structures}]\nThis is enabled by default and pre-loads several embedded data\ndefinitions to base data lists on. Once loaded on portal startup, these\ndefinitions must be deleted manually from the Site Menu → Dynamic Data\nLists application. This setting applies to the first start of a virtual\ninstance.\n\\end{description}\n\nThe Dynamic Data Lists entry contains three settings:\n\n\\begin{description}\n\\item[\\textbf{Changeable Default Language}]\nIf enabled, the default language of a data definition becomes\nchangeable.\n\\item[\\textbf{CSV Export}]\nChoose whether DDL records can be exported in CSV format with or without\na warning, or disable this option. Here's what the warning says:\n\nWarning: This CSV file contains user supplied inputs. Opening a CSV file\nin a spreadsheet program may be dangerous.\n\\item[\\textbf{Default Display View}]\nChoose whether to use a table based default view or a list based default\nview.\n\\end{description}\n\n\\chapter{Creating Data Definitions}\\label{creating-data-definitions}\n\nFollow these steps to create a data definition:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Menu (\\includegraphics{./images/icon-menu.png}) and expand\n  your site's menu (the Site Administration menu). Then select\n  \\emph{Content \\& Data} → \\emph{Dynamic Data Lists}. This opens the\n  Dynamic Data Lists screen. A table lists any existing dynamic data\n  lists.\n\\item\n  Click the \\emph{Options} button at the top-right\n  (\\includegraphics{./images/icon-options.png}) and select \\emph{Manage\n  Data Definitions}. The Data Definitions screen appears. A table lists\n  any existing data definitions. Several definitions are embedded for\n  common use cases like contacts, events, inventory, and more.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/ddl-definitions.png}\n  \\caption{The Data Definitions screen.}\n  \\end{figure}\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  to begin creating a new data definition. This opens the \\emph{New Data\n  Definition} form.\n\\item\n  Give your data definition a name. Note that the definition's name\n  appears for any users filling out a dynamic data list that uses the\n  definition. Then expand the \\emph{Details} section of the form and\n  give it a description.\n\\item\n  The Details section of the form also contains the field \\emph{Parent\n  Data Definition}. This optional field lets you select an existing data\n  definition (the parent) to form the basis of the new one (the child).\n  The child definition inherits the parent's fields and settings, which\n  you can then customize. When you create a dynamic data list from a\n  child definition, it includes the fields of the parent and child\n  definitions. This lets you use a common definition (the parent) as the\n  basis of a specialized definition (the child). For example, if you\n  were planning a rock climbing trip, you could use the default Events\n  definition as the parent of a Rock Climbing Trip definition that\n  contains fields unique to rock climbing (e.g., climbing equipment\n  availability, altitude, etc.).\n\n  To choose a parent definition, click the \\emph{Select} button below\n  the \\emph{Parent Data Definition} field and then select an existing\n  definition in the dialog that appears.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/ddl-definition-form-01.png}\n  \\caption{After naming your data definition, expand the Details section\n  of the form and give your definition a description and parent\n  definition, if desired.}\n  \\end{figure}\n\\item\n  Add the data definition's fields in the data definition designer,\n  below the form's Details section. The designer's default \\emph{View}\n  tab lets you create the definition in a WYSIWYG editor. You can click\n  the \\emph{Source} tab to work with the definition's underlying JSON,\n  but it's much easier to stick with the WYSIWYG editor.\n\n  In the \\emph{View} tab select the \\emph{Fields} tab. Icons\n  representing the field types are listed on one side and the data\n  definition's canvas is on the other side. To add a field type to the\n  definition, select its icon, drag, and drop it onto the canvas. By\n  dragging a field onto a field that's already on the canvas, you can\n  nest the new field in the existing field. When you mouse over a field\n  on the canvas, the field action icons appear. Clicking the \\emph{+}\n  icon creates a duplicate of the current field and adds it below the\n  current field. Clicking the trash can deletes the field.\n\n  The following fields are available:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Boolean:} A check box.\n  \\item\n    \\textbf{Color:} Specifies a color.\n  \\item\n    \\textbf{Date:} Enter a date. A valid date format is required for the\n    date field, but you don't have to enter a date manually. When you\n    select the date field a mini-calendar pops up which you can use to\n    select a date.\n  \\item\n    \\textbf{Decimal:} Enter a decimal number. The value is persisted as\n    a \\texttt{double}.\n  \\item\n    \\textbf{Documents and Media:} Select a file from a Documents and\n    Media library.\n  \\item\n    \\textbf{Geolocation:} Associate a location with the User's form\n    entry.\n  \\item\n    \\textbf{HTML:} An area that uses a WYSIWYG editor to write and\n    display HTML content.\n  \\item\n    \\textbf{Integer:} Enter an integer. The value is persisted as an\n    \\texttt{int}.\n  \\item\n    \\textbf{Link to Page:} Link to another page in the same site.\n  \\item\n    \\textbf{Number:} Enter a decimal number or an integer. The value is\n    persisted either as a \\texttt{double} or an \\texttt{int}, depending\n    on the input's type.\n  \\item\n    \\textbf{Radio:} Displays several clickable options. The default\n    number of options is three but this is customizable. Only one option\n    can be selected at a time.\n  \\item\n    \\textbf{Select:} This is just like the radio field except that the\n    options are hidden and must be accessed from a drop-down menu.\n  \\item\n    \\textbf{Text:} Enter a single line of text.\n  \\item\n    \\textbf{Text Box:} This is just like the text field except you can\n    enter multiple lines of text or separate paragraphs.\n  \\item\n    \\textbf{Web Content:} Select web content.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/ddl-data-definition-designer.png}\n  \\caption{Use the data definition designer to add fields to the data\n  definition.}\n  \\end{figure}\n\\item\n  Edit field labels to reflect their intended data. A text field's\n  default label is \\emph{Text}. To use the text field as a title, then\n  you should change the field's label to \\emph{Title}. First select the\n  field on the canvas. This automatically selects the \\emph{Settings}\n  tab on the left. Alternatively, you can access the Settings tab by\n  clicking the field's wrench icon. To edit a setting value,\n  double-click it in the Settings table and enter the new value. The\n  available settings are listed below.\n\n  You can translate each of a data definition's field values to any\n  supported locales. To specify a field value for a translation, select\n  the flag that represents the locale and enter the field value for the\n  locale.\n\n  The following field settings are available. Note that some of these\n  settings are only available for specific field types:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    \\textbf{Type:} The field's type (e.g., text, radio, etc.). This\n    setting can't be edited, but a display template can reference it.\n  \\item\n    \\textbf{Field Label:} The field's display name.\n  \\item\n    \\textbf{Show Label:} Whether the field label is shown.\n  \\item\n    \\textbf{Required:} Whether users must fill out the field (not\n    available for Boolean fields).\n  \\item\n    \\textbf{Name:} The field's internal identifier. You can use this\n    value in a display template to read the field's data. This value is\n    automatically generated, but you can change it if you wish.\n  \\item\n    \\textbf{Predefined Value:} The field's default value.\n  \\item\n    \\textbf{Tip:} Text to display in a tooltip.\n  \\item\n    \\textbf{Indexable:} Whether the field is indexed for search.\n  \\item\n    \\textbf{Localizable:} Whether the field can be translated.\n  \\item\n    \\textbf{Repeatable:} Whether users can make copies of the field.\n  \\item\n    \\textbf{Multiple:} Whether the user can select more than one option.\n    This is only available for Select fields.\n  \\item\n    \\textbf{Options:} The options available for selection in Radio and\n    Select fields. You can add and remove options, and edit each\n    option's display name and value.\n  \\end{itemize}\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/ddl-data-definition-settings.png}\n  \\caption{Configure the settings for each field in your data\n  definition.}\n  \\end{figure}\n\\item\n  Click \\emph{Save} when you're done. Your new data definition then\n  appears in the table with the pre-defined ones and any you've already\n  added.\n\\end{enumerate}\n\n\\chapter{Managing Data Definitions}\\label{managing-data-definitions}\n\nThere are several ways to manage your data definitions. Of course, you\ncan edit a data definition, but you can also configure its permissions,\nmanage its templates, copy it, or delete it.\n\nFollow these steps to access your data definitions:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Menu (\\includegraphics{./images/icon-menu.png}) and expand\n  your site's menu (the Site Administration menu). Then select\n  \\emph{Content \\& Data} → \\emph{Dynamic Data Lists}. This opens the\n  Dynamic Data Lists screen. A table lists any existing dynamic data\n  lists.\n\\item\n  Click the \\emph{Options} button at the top-right\n  (\\includegraphics{./images/icon-options.png}) and select \\emph{Manage\n  Data Definitions}. The Data Definitions screen appears. A table lists\n  the data definitions.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/ddl-definitions-actions.png}\n\\caption{You can copy an existing data definition, manage its templates,\nand more.}\n\\end{figure}\n\nYou can manage your data definitions via the \\emph{Actions} menu\n(\\includegraphics{./images/icon-actions.png}) for each definition:\n\n\\textbf{Edit:} Edit the data definition. The edit screen uses the same\nform for\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-data-definitions}{creating\ndata definitions}. Note that if you edit a data definition referenced\nelsewhere (e.g., by a dynamic data list or display template), then you\nmust update that reference.\n\n\\textbf{Manage Templates:} The \\emph{Manage Templates} screen creates\nand manages templates for the data definition. For details, see\n\\href{/docs/7-2/user/-/knowledge_base/u/using-templates-to-display-forms-and-lists}{Using\nTemplates to Display Forms and Lists}.\n\n\\textbf{Permissions:} Configure the data definition's permissions. Note\nthat these permissions are for an individual definition accessed through\nthe Dynamic Data Lists application in \\emph{Site Administration} →\n\\emph{Content} → \\emph{Dynamic Data Lists}. For example, if Site members\nhave View permission for a data definition, any Site member who also has\na Role that can access the Dynamic Data Lists app and its data\ndefinitions can see this definition listed in the Manage Data\nDefinitions screen. If you don't want this, remove the View permission\nfor Site Member, and Site members won't see your data definition listed\nwith the others.\n\n\\textbf{Copy:} The \\emph{Copy Data Definition} form copies the\ndefinition and its templates. In the form, give the copied definition a\nnew name and description and select whether to also copy the original\ndefinition's templates. Click \\emph{Copy} when you're done. The copied\ndefinition then appears in the Data Definitions table with existing\ndefinitions. You can create new definitions based on existing ones, and\nthen modify the copied one to suit your needs. You can, of course, edit\nany definition in the portal, but if you copy a definition instead, you\ncan still access the original.\n\n\\textbf{Delete:} Delete the definition.\n\n\\chapter{Creating Data Lists}\\label{creating-data-lists}\n\nThere are two places to create dynamic data lists:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{Site Administration:} Open the Menu and expand your Site's\n  menu (the Site Administration menu). Then select \\emph{Content \\&\n  Data} → \\emph{Dynamic Data Lists}. This opens the Dynamic Data Lists\n  screen. A table contains any existing lists. Click the \\emph{Add}\n  button (\\includegraphics{./images/icon-add.png}) to open \\emph{New\n  List} form.\n\n  To add Dynamic Data Lists, you must have permission to access the\n  Dynamic Data Lists app in Site Administration.\n\\item\n  \\textbf{Dynamic Data Lists Display widget:} Navigate to the Site page\n  where you want this widget and add it to the page from \\emph{Add}\n  (\\includegraphics{./images/icon-add-app.png}) → \\emph{Widgets} →\n  \\emph{Collaboration} → \\emph{Dynamic Data Lists Display}. Then click\n  the widget's \\emph{Add List} button. This opens the \\emph{New List}\n  form.\n\n  To do this, you must have permission to create a new list in the\n  widget.\n\\end{enumerate}\n\nEither option leads to the New List form:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Give the list a name and a description.\n\\item\n  Select the list's data definition: click \\emph{Select} under the\n  \\emph{Data Definition} field, then click the definition you want to\n  use.\n\\item\n  To use a workflow with this list, select it from the \\emph{Workflow}\n  field.\n\\item\n  To change the list's default permissions, expand the form's\n  \\emph{Permissions} section and make your selections.\n\\item\n  Click \\emph{Save} to finish creating the list. Your new list appears\n  in the table.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/ddl-add-list.png}\n  \\caption{The New List form.}\n  \\end{figure}\n\\end{enumerate}\n\n\\section{Creating List Records}\\label{creating-list-records}\n\nBy default, only administrators have permission to create list records.\nFollow these steps to give other users this permission:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to \\emph{Content \\& Data} → \\emph{Dynamic Data Lists} in Site\n  Administration.\n\\item\n  Click \\emph{Actions} (\\includegraphics{./images/icon-actions.png}) →\n  \\emph{Permissions} for the list getting the new permissions.\n\\item\n  Select \\emph{Add Record} for the Roles that should have that\n  permission, then click \\emph{Save}. Allow unauthenticated Users to add\n  records by giving Guest the Add Record permission.\n\\end{enumerate}\n\nCreate new records in a list from the same places you can create the\nlists themselves:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{Site Administration:} In Site Administration, navigate to\n  \\emph{Content \\& Data} → \\emph{Dynamic Data Lists}. Click a list in\n  the table to view any existing records, then click the \\emph{Add}\n  button (\\includegraphics{./images/icon-add.png}). This opens a form\n  based on the list's data definition, which you can then fill out and\n  submit to create a new record. To do this, you must have permission to\n  access the Dynamic Data Lists app in Site Administration.\n\\item\n  \\textbf{Dynamic Data Lists Display widget:} See above for instructions\n  on adding this widget to a page. You must then configure the widget to\n  display a list's records.\n\n  To configure the widget to display a list's records:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Click the widget's \\emph{Select List} button.\n  \\item\n    In the dialog that appears, select a list, click \\emph{Save}, then\n    close the dialog. The widget then displays the list's existing\n    records.\n  \\end{itemize}\n\n  To add a record:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Click the widget's \\emph{Add} button\n    (\\includegraphics{./images/icon-add.png}).\n  \\item\n    Fill out the form that appears and click \\emph{Publish}.\n  \\end{itemize}\n\\end{enumerate}\n\nSee the section below for more information on configuring the widget.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/ddl-widget.png}\n\\caption{Dynamic Data Lists Display widget.}\n\\end{figure}\n\n\\section{Configuring the Dynamic Data Lists Display\nWidget}\\label{configuring-the-dynamic-data-lists-display-widget}\n\nThe widget's default display template isn't exciting, but it shows the\nlist's contents, and with permission, add and/or edit list items. To\nconfigure the widget, click its \\emph{Options} menu\n(\\includegraphics{./images/icon-app-options.png}) and select\n\\emph{Configuration}. This opens the Configuration dialog, with the\n\\emph{Setup} tab selected by default. The Setup tab contains two other\ntabs:\n\n\\textbf{Lists:} Select the list that the widget displays. The currently\ndisplayed list appears at the top of the tab, while the available lists\nappear in a table. To change the widget's list, select the list from the\ntable and click \\emph{Save}.\n\n\\textbf{Optional Configuration:}\n\n\\textbf{Display Template:} Select the display template for the list.\n\n\\textbf{Form Template:} Select the form template for the list.\n\n\\textbf{Editable:} Whether users can add records to the widget's list.\n\n\\textbf{Form View:} Whether to display the Add Record form by default,\ninstead of the List View. Note that even without this selected, users\ncan still add records via the widget's \\emph{Add} button\n(\\includegraphics{./images/icon-add.png}).\n\n\\textbf{Spreadsheet View:} Whether the List View displays each record in\na row, with columns for the record attributes.\n\nWhen finished, click \\emph{Save} and close the Configuration dialog.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/ddl-widget-options.png}\n\\caption{The Dynamic Data Lists Display widget's optional\nconfiguration.}\n\\end{figure}\n\n\\chapter{Using Templates to Display Forms and\nLists}\\label{using-templates-to-display-forms-and-lists}\n\nAfter creating data definitions and lists, you can control how the form\nappears to users, and how the resulting list of records is displayed.\nYou do this by creating templates for each view (form view for\ndisplaying the form and display view for the list of records) and\nselecting them in the DDL Display portlet. For example, you might need\nto create a form with a subset of a data definition's fields. Rather\nthan creating a new definition, you can create a template that displays\nonly the fields you want from the existing definition. You could also\nuse a template to arrange fields differently, and/or with different\nlabels and configuration options.\n\nData definitions can have as many form and display templates as you care\nto create (or none, if you're satisfied with the default templates). You\nthen choose a list's template in the Dynamic Data Lists Display widget.\n\n\\section{Managing Display and Form\nTemplates}\\label{managing-display-and-form-templates}\n\nSince Display and Form Templates correspond to a particular data\ndefinition, they're accessed from the Data Definitions screen of the\nDynamic Data Lists application in Site Administration. See the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-data-definitions}{Creating\nData Definitions article} for instructions on accessing this screen.\n\nThe Data Definitions screen lists each definition in a table. To start\nworking with a definition's templates, click the definition's Actions\nbutton (\\includegraphics{./images/icon-actions.png}) and select\n\\emph{Manage Templates}. This opens a screen that lists the definition's\ntemplates. You can edit, copy, delete, or configure permissions for a\ndefinition via its Actions button\n(\\includegraphics{./images/icon-actions.png}).\n\n\\chapter{Creating Form Templates}\\label{creating-form-templates}\n\nForm templates control how the data entry form appears for a data\ndefinition. Follow these steps to create a form template for a\ndefinition:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Menu (\\includegraphics{./images/icon-menu.png}) and expand\n  your Site's menu (the Site Administration menu). Then select\n  \\emph{Content \\& Data} → \\emph{Dynamic Data Lists}. This opens the\n  Dynamic Data Lists screen. A table lists any existing dynamic data\n  lists.\n\\item\n  Click the \\emph{Options} button at the top-right\n  (\\includegraphics{./images/icon-options.png}) and select \\emph{Manage\n  Data Definitions}. The Data Definitions screen appears. A table lists\n  any existing data definitions.\n\\item\n  Click the definition's \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Manage\n  Templates}. This lists the definition's templates.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  and select \\emph{Add Form Template}. This presents the same kind of\n  graphical, drag-and-drop interface used to\n  \\href{/docs/7-2/user/-/knowledge_base/u/creating-data-definitions}{create\n  definitions}.\n\\item\n  Give your template a name, then expand the \\emph{Details} section and\n  give it a description. You can also use the \\emph{Mode} selector to\n  select which mode the template applies to (\\emph{Create} or\n  \\emph{Edit}).\n\\item\n  Scroll down to the graphical designer in the \\emph{View} tab, and make\n  your desired changes. For example, you can move or delete fields,\n  change field labels, and more.\n\\item\n  Click \\emph{Save} when you're finished.\n\\end{enumerate}\n\nAlternatively, you can create form templates from the Dynamic Data Lists\nDisplay widget:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Follow the instructions in the\n  \\href{/docs/7-2/user/-/knowledge_base/u/creating-data-lists}{Creating\n  Data Lists article} for adding and configuring the widget in a Site\n  page. Make sure to configure the widget to show the list you're\n  creating a template for.\n\\item\n  Click the widget's \\emph{Add Form Template} button. This opens the\n  same form as above for creating a form template for the list's\n  definition.\n\\end{enumerate}\n\n\\chapter{Creating Display Templates}\\label{creating-display-templates}\n\nFor every data definition, you can create as many displays as you need.\nIf you've created a form template that doesn't show all the fields of a\nparticular data definition in the data list's form view, you probably\ndon't want to display those fields in the list view, either. Modify the\nlist view using Display Templates.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you're familiar with\n\\href{/docs/7-2/user/-/knowledge_base/u/designing-web-content-with-templates}{web\ncontent templates}, display templates customize the display of a list in\nthe same way. Display templates are written in FreeMarker or Velocity,\npulling data from the data definition in the same way that web content\ntemplates pull data from their structures. Also similar to web content\ntemplates, display templates can be embedded in other display templates.\nThis allows for reusable code, JavaScript library imports, or macros\nimported by Velocity or FreeMarker templates in the system. Embedding\ndisplay templates provides a more efficient process when you have a\nmultitude of similar data definitions. Just import an embedded display\ntemplate and work off of it for your new display template.\n\n\\noindent\\hrulefill\n\nAs with\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-form-templates}{form\ntemplates}, you can create display templates from the Dynamic Data Lists\napp in Site Administration or the Dynamic Data Lists Display widget.\n\nFollow these steps to create a display template from Site\nAdministration:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the Menu (\\includegraphics{./images/icon-menu.png}) and expand\n  your Site's menu (the Site Administration menu). Then select\n  \\emph{Content} → \\emph{Dynamic Data Lists}. This opens the Dynamic\n  Data Lists screen. A table lists any existing dynamic data lists.\n\\item\n  Click the \\emph{Options} button at the top-right\n  (\\includegraphics{./images/icon-options.png}) and select \\emph{Manage\n  Data Definitions}. The Data Definitions screen appears. A table lists\n  existing data definitions.\n\\item\n  Click the definition's \\emph{Actions} button\n  (\\includegraphics{./images/icon-options.png}) and select \\emph{Manage\n  Templates}. This lists the definition's templates.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  and select \\emph{Add Display Template}.\n\\item\n  Give the template a name, expand the \\emph{Details} section of the\n  form, and give it a description. Also in the details section of the\n  form, select the templating language to use from the \\emph{Language}\n  selector. You can choose\n  \\href{https://freemarker.apache.org/index.html}{FreeMarker}, or\n  \\href{https://velocity.apache.org/}{Velocity}.\n\\item\n  In the \\emph{Script} section of the form, create your template in the\n  editor using the templating language you chose in the previous step.\n  The palette to the left of the editor contains common variables. Click\n  a variable to insert it in the editor. The editor also autocompletes.\n  In a FreeMarker template, type \\texttt{\\$\\{}, which opens an\n  autocomplete list of common variables. Select a variable to insert it\n  in the editor. Alternatively, you can upload a complete script file\n  via the \\emph{Browse} button below the editor.\n\\item\n  Click \\emph{Save} when you're done creating the template.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/ddl-template-editor.png}\n\\caption{Create your display template in the editor.}\n\\end{figure}\n\nAlternatively, you can use the Dynamic Data Lists Display widget to\ncreate display templates:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Follow the instructions in the\n  \\href{/docs/7-2/user/-/knowledge_base/u/creating-data-lists}{Creating\n  Data Lists article} for adding and configuring the widget in a site\n  page. Make sure to configure the widget to show the list's definition\n  you're making into a template.\n\\item\n  Click the widget's \\emph{Add Display Template} button. This opens the\n  same form as above for creating a display template for the list's\n  definition.\n\\end{enumerate}\n\n\\section{Display Template Editor}\\label{display-template-editor}\n\nHelper variables are available in the template editor. These provide\naccess to most of the data that you'll use in creating Display\nTemplates. The variables under the heading Data List Variables let you\ninject specific information about the data definition the template is\nbeing created for:\n\n\\textbf{Data Definition ID:} \\texttt{reserved\\_ddm\\_structure\\_id}\n\n\\textbf{Data List Description:}\n\\texttt{reserved\\_record\\_set\\_description}\n\n\\textbf{Data List ID:} \\texttt{reserved\\_record\\_set\\_id}\n\n\\textbf{Data List Name:} \\texttt{reserved\\_record\\_set\\_name}\n\n\\textbf{Template ID:} \\texttt{reserved\\_ddm\\_template\\_id}\n\nInside a template, these variables give the ID for the record set as\nwell as the name, description, and data definition.\n\nDisplay the list of records by retrieving them and assigning them to the\nhandy \\texttt{records} variable. Retrieve the list's records from\n\\texttt{DDLDisplayTemplateHelper}, which contains these functions:\n\n\\begin{verbatim}\ngetDocumentLibraryPreviewURL\n\ngetHTMLContent\n\ngetLayoutFriendlyURL\n\ngetRecords\n\nrenderRecordFieldValue\n\\end{verbatim}\n\n\\texttt{DDLDisplayTemplateHelper} performs common tasks. Use the\n\\texttt{getRecords} method to access a data definition's entries and\nassign them to a \\texttt{records} variable:\n\n\\begin{verbatim}\n<#assign records = ddlDisplayTemplateHelper.getRecords(reserved_record_set_id)>\n\\end{verbatim}\n\nThis \\emph{fetches} the records of the associated data list. You haven't\ndone anything with them yet, so your display is still empty. To list all\nthe records, use the \\emph{Data List Records} helper in the sidebar of\nthe template editor. Remember to place your cursor in the proper place\nin the template editor window, then click \\emph{Data List Records}. This\ncode appears at the cursor:\n\n\\begin{verbatim}\n<#if records?has_content>\n    <#list records as cur_record>\n        ${cur_record}\n    </#list>\n</#if>\n\\end{verbatim}\n\nThis default code snippet spits out everything in the database for the\ngiven data definition, which is ugly and practically useless:\n\n\\begin{verbatim}\n{uuid=52c4ac1c-afe7-963c-49c6-5279b7030a99, recordId=35926, groupId=20126, \ncompanyId=20099, userId=20139, userName=Test Test, versionUserId=20139, \nversionUserName=Test Test, createDate=2018-07-26 14:31:51.056, \nmodifiedDate=2018-07-26 14:31:51.058, DDMStorageId=35927, recordSetId=35922, \nrecordSetVersion=1.0, version=1.0, displayIndex=0, lastPublishDate=null}\n\\end{verbatim}\n\nHere's a simple example template that uses a list based on the embedded\nContacts data definition, and only displays the Company Name and Email\nfields in a bulleted list:\n\n\\begin{verbatim}\n<#assign records = ddlDisplayTemplateHelper.getRecords(reserved_record_set_id)>\n\n    <h1>Here are contacts by company name and email address.</h1>\n\n    <#if records?has_content>\n        <#list records as cur_record>\n            <ul>\n                <li>\n                    <#-- The below gets the Company field and wraps it in an <em> tag -->\n                    Company Name: <em>${ddlDisplayTemplateHelper.renderRecordFieldValue(cur_record.getDDMFormFieldValues(\"company\")?first, locale)}</em><br /> \n                    <#-- The below gets the Email field  and wraps it in an <em> tag --> \n                    Email: ${ddlDisplayTemplateHelper.renderRecordFieldValue(cur_record.getDDMFormFieldValues(\"email\")?first, locale)} \n                </li> \n            </ul> \n        </#list> \n    </#if>\n\\end{verbatim}\n\nHere's what it looks like:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/ddl-contacts-template.png}\n\\caption{Extract appropriate display information, rather than spitting\nout the whole object.}\n\\end{figure}\n\nNow you're prepared to make data lists beautiful using Display\nTemplates.\n\n\\chapter{Workflow}\\label{workflow}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay's workflow engine is named \\emph{Kaleo}. In Greek, Kaleo means\n``called ones,'' which is appropriate for a workflow engine that calls\nusers to participate in a review process designed for them. Workflow\nmakes it possible to define any number of simple to complex business\nprocesses/workflows, deploy them, and manage them through a portal\ninterface. The processes have knowledge of Users, Groups and Roles. You\ndon't have to write a single line of code to accomplish this: it's\ndefined in an XML document. If you're a Liferay Digital Experience\nPlatform (DXP) customer, you also have access to a visual process\nbuilder.\n\nThere are several steps to effective workflowing:\n\n\\begin{itemize}\n\\item\n  \\href{/docs/7-2/reference/-/knowledge_base/r/crafting-xml-workflow-definitions}{Designing\n  review processes in XML}\n\\item\n  (DXP only)\n  \\href{https://help.liferay.com/hc/en-us/articles/360028821892-Workflow-Designer}{Visually\n  designing review processes}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/managing-workflows\\#uploading-workflow-definitions}{Uploading\n  workflow definitions}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/activating-workflow}{Activating\n  workflow for enabled assets}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/managing-workflows}{Managing\n  Workflow definitions}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/reviewing-assets}{Sending\n  assets through review}\n\\item\n  (DXP only)\n  \\href{https://help.liferay.com/hc/en-us/articles/360029042071-Workflow-Metrics-The-Service-Level-Agreement-SLA-}{Using\n  Workflow Metrics}\n\\end{itemize}\n\nAfter all that, you'll be familiar with using Liferay's workflow engine\nto set up approval processes for any\n\\href{/docs/7-2/user/-/knowledge_base/u/activating-workflow}{workflow-enabled\ncontent}.\n\n\\section{What's New with Workflow?}\\label{whats-new-with-workflow}\n\nThere are some noteworthy enhancements to the workflow functionality:\n\n\\section{DXP Feature: Workflow\nMetrics}\\label{dxp-feature-workflow-metrics}\n\nFor DXP subscribers, the \\emph{Workflow Metrics} feature was introduced.\nConfigure one or more Service Level Agreements (SLAs; think of these as\ndeadlines) on a workflow definition's events, and workflow submissions\nare accordingly tracked and reported as on time or overdue.\n\n\\subsection{Service Level Agreements\n(SLAs)}\\label{service-level-agreements-slas}\n\nSLAs define the deadlines on a workflow process's events. They're like a\ncontract between the workflow participants and Users submitting workflow\nitems.\n\nSLAs can be formally agreed-upon deadlines between you and your\ncustomers, or informally created to meet internal goals, tracking events\nsuch as:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Total time to resolution\n\\item\n  Time to complete a specific workflow task\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-add-sla.png}\n\\caption{Use Service Level Agreements (SLAs) to define how workflow\nmetrics are reported.}\n\\end{figure}\n\nFor each workflow event to track, set the SLA duration and when the\ntimer should be paused, if at all.\n\n\\subsection{Workflow Reports}\\label{workflow-reports-1}\n\nOnce an SLA is set, workflow submissions that trigger the SLA timer are\nautomatically reported on by the workflow metrics framework, and given\nthe status \\emph{on time} or \\emph{overdue}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-report.png}\n\\caption{See Workflow Reports generated based on your SLAs.}\n\\end{figure}\n\nSee the article on\n\\href{https://help.liferay.com/hc/en-us/articles/360029042071-Workflow-Metrics-The-Service-Level-Agreement-SLA-}{Workflow\nMetrics} to learn more about SLAs and available reports.\n\n\\section{Control Panel\nReorganization}\\label{control-panel-reorganization}\n\nThe Workflow section of the Control Panel is now a top-level section\nwith its own subcategories: Process Builder and Submissions (Metrics,\ntoo, if you're a DXP subscriber). In Liferay DXP 7.1 Workflow was nested\nunder Control Panel → Configuration.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-menu.png}\n\\caption{Workflow has a top-level entry in the Control Panel.}\n\\end{figure}\n\n\\section{Workflow Definition Permissions: System\nSettings}\\label{workflow-definition-permissions-system-settings}\n\nThe Workflow System Settings category (Control Panel → Configuration →\nSystem Settings) has a new system scoped configuration entry:\n\\emph{Workflow Definitions}. There's just one configuration option, but\nit's important: Enabling it gives administrators permission to publish\nworkflows and scripts.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-publication-permission.png}\n\\caption{Explicit permission must be granted before administrators are\nallowed to publish and edit workflow definitions.}\n\\end{figure}\n\nCreate your own workflows from scratch or leverage existing workflows.\n\n\\section{Embedded Workflows}\\label{embedded-workflows}\n\nIn addition to the Single Approver definition, there are some workflow\ndefinitions that ship with Liferay DXP but are not pre-installed, since\nthey're primarily included for test cases. They can be found in the\nLiferay source code in\n\n\\begin{verbatim}\n/modules/apps/portal-workflow/portal-workflow-kaleo-runtime-impl/src/main/resources/META-INF/definitions\n\\end{verbatim}\n\nThey're also in your Liferay installation. Open your Liferay\ninstallation's\n\\texttt{osgi/portal/com.liferay.portal.workflow.kaleo.runtime.impl.jar},\nand then find and open the\n\\texttt{com.liferay.workflow.kaleo.runtime.impl-{[}version{]}.jar}. The\ndefinitions are in the \\texttt{META-INF/definitions} folder.\n\n\\chapter{Activating Workflow}\\label{activating-workflow}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAssets are integrated with the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework}{workflow\nframework} if their content is meant to be sent through review\nprocesses. Since this is most often the case, there are many out of the\nbox assets that integrate with workflow. In this article, learn how to\nselect a workflow for use with each of these workflow-enabled assets.\n\n\\section{Workflow Assets}\\label{workflow-assets}\n\nActivate a workflow for these assets in Control Panel → Workflow →\nProcess Builder, in the Configuration tab:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Blogs Entry\n\\item\n  Calendar Event\n\\item\n  Comment\n\\item\n  Knowledge Base Article\n\\item\n  Message Boards Message\n\\item\n  Page Revision\n\\item\n  User\n\\item\n  Web Content Article\n\\item\n  Wiki Page\n\\end{itemize}\n\nActivate workflow for these assets at the Site level in Site →\nConfiguration → Workflow:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Blogs Entry\n\\item\n  Calendar Event\n\\item\n  Comment\n\\item\n  Knowledge Base Article\n\\item\n  Message Boards Message\n\\item\n  Page Revision\n\\item\n  Web Content Article\n\\item\n  Wiki Page\n\\end{itemize}\n\nWhat's the difference between the Site workflow configuration and the\nControl Panel Workflow configuration? As with most scoped\nconfigurations, the higher level setting (in the Control Panel) sets the\ndefault behavior. It's overridden at the more granular level (in the\nSite menu).\n\nUser doesn't appear on the Site list because adding users is strictly a\nportal-wide administrator activity. Only assets that can be added and\nconfigured at the Site level (for example, those that are accessed from\nthe Site menu) have workflow configuration controls at the Site level.\n\n\\section{Activating Workflow in\nApplications}\\label{activating-workflow-in-applications}\n\nSome assets that are workflow-enabled are activated in their respective\napplication:\n\nActivate workflow for Web Content Folders from the folder settings menu:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-web-content-folder.png}\n\\caption{Activate workflow on Web Content folders from the folder's edit\nscreen.}\n\\end{figure}\n\nActivate workflow for Documents and Media Folders from the folder\nsettings:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-dm-folder.png}\n\\caption{Activate workflow on Documents and Media folders from the\nfolder's edit screen.}\n\\end{figure}\n\nEnable workflow on Dynamic Data List entries in each list's Add form:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-ddl.png}\n\\caption{Activate workflow for each individual Dynamic Data List.}\n\\end{figure}\n\nActivate workflow for each individual form's entries from the Form\nSettings screen:\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-form.png}\n\\caption{Activate workflow on each form's entries from the Form Settings\nwindow.}\n\\end{figure}\n\n\\section{Workflow Behavior}\\label{workflow-behavior}\n\nMost of the resources listed above behave just as you might expect with\nworkflow activated: The Publish button for the resource's \\emph{Add}\nform is replaced by a \\emph{Submit for Publication} button, and instead\nof instant publication, the asset has its status set as \\emph{Pending}\nand must proceed through the workflow before publication.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/submit-for-publication.png}\n\\caption{Instead of a Publish button, a Submit for Publication button\nappears for workflow-enabled resources.}\n\\end{figure}\n\nPage revisions are slightly different. Page revisions only occur in\n\\href{/docs/7-2/user/-/knowledge_base/u/staging-content}{staging\nenvironments} that have Page Versioning enabled. When a Page Variation\nor Site Page Variation is created, its creator must click \\emph{Submit\nfor Publication} at the top of the page, and the variation must be\napproved in the workflow before it can be published to the live Site.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/page-revision-submission.png}\n\\caption{With workflow enabled on Page Revisions, the Site administrator\nmust submit their page variation for publication before it can go live.}\n\\end{figure}\n\n\\chapter{Managing Workflows}\\label{managing-workflows}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLike other resources, workflow definitions can be added, edited, and\ndeleted. But that's just the beginning of workflow management.\n\n\\begin{itemize}\n\\item\n  Because workflow definitions can be complex works in progress, they\n  can be versioned.\n\\item\n  Unpublished drafts can be saved.\n\\item\n  Because workflow definitions are XML files, they're portable. Thus,\n  they can also be uploaded.\n\\end{itemize}\n\nStart by learning basic workflow management.\n\n\\section{Workflow Definition Publication\nPermissions}\\label{workflow-definition-publication-permissions}\n\nUsers with permission to edit or publish workflow definitions can add\n\\href{/docs/7-1/user/-/knowledge_base/u/leveraging-the-script-engine-in-workflow}{Groovy\nscripts} to the workflow. Access to the scripting engine means access to\nthe Java Virtual Machine (JVM) of the server. Users who publish (or\nedit) workflow definitions containing scripts, therefore, can get access\nto any data within the reach of the JVM, such as data contained in a\nseparate\n\\href{/docs/7-1/user/-/knowledge_base/u/virtual-instances}{Virtual\nInstance} of Liferay DXP itself.\n\nBecause of this far-reaching access, permission to create or edit\nworkflow definitions is limited to Regular Administrators of the Default\nVirtual Instance.\n\nTo grant Users with these Roles the workflow publication access in\nadditional Virtual Instances,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Make sure you understand the access you're granting these admins.\n\\item\n  Navigate to Control Panel → System Settings → Workflow → Workflow\n  Definition.\n\\item\n  Check the box for the setting \\emph{Allow Administrators to Publish\n  and Edit Workflows}.\n\\end{enumerate}\n\nThis only applies to Virtual Instances that have been added to the\nsystem. The Default Virtual Instance provides workflow publication\naccess to Regular Administrators (via Control Panel → Configuration →\nWorkflow), and, if running Liferay DXP, to Site Administrators and other\nUsers with access to the Kaleo Forms Admin applications.\n\n\\section{Adding, Editing, and\nDeleting}\\label{adding-editing-and-deleting}\n\nTo add a workflow definition,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Control Panel → Workflow → Process Builder.\n\\item\n  In the default view (Workflows), click the Add button\n  (\\includegraphics{./images/icon-add.png}).\n\\item\n  From here you're either\n  \\href{/docs/7-2/reference/-/knowledge_base/r/crafting-xml-workflow-definitions}{writing\n  an XML definition},\n  \\href{https://customer.liferay.com/documentation/7.2/admin/-/official_documentation/portal/workflow-designer}{designing\n  a definition in in the visual designer (DXP only)}, or\n  \\hyperref[uploading-workflow-definitions]{uploading an existing\n  definition}.\n\\end{enumerate}\n\nTo edit a definition,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Control Panel → Workflow → Process Builder.\n\\item\n  Click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) for the workflow, and\n  click \\emph{Edit}.\n\\end{enumerate}\n\nTo delete a definition,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Control Panel → Workflow → Process Builder.\n\\item\n  Click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) for the workflow, and\n  click \\emph{Unpublish}.\n\n  A published workflow cannot be deleted, so you must unpublish its\n  workflow definition first.\n\n  You can't unpublish a definition if it's activated for an asset. First\n  dissociate the workflow definition from any assets that use it. See\n  \\href{/docs/7-2/user/-/knowledge_base/u/activating-workflow}{here} for\n  more information.\n\\end{enumerate}\n\n\\section{Uploading Workflow\nDefinitions}\\label{uploading-workflow-definitions}\n\nIf you have a local XML definition file (perhaps you want to create a\nnew workflow based on one of the\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow\\#embedded-workflows}{embedded\nworkflows}), upload it to Liferay DXP:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to Control Panel → Workflow → Process Builder.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n\\item\n  Name the workflow.\n\\item\n  In the \\emph{Source} tab, click the hyperlink \\emph{import a file} in\n  the sentence\n\n  \\texttt{Write\\ your\\ definition\\ or\\ import\\ a\\ file}\n\\item\n  Find the XML file and upload it. Once uploaded, the definition's XML\n  appears in the workflow editor.\n\\item\n  If it's ready to publish, click \\emph{Publish}. Otherwise, \\emph{Save}\n  it and it stays Unpublished.\n\\end{enumerate}\n\nWhat's the difference between saving and publishing?\n\n\\section{Published Versus\nUnpublished}\\label{published-versus-unpublished}\n\nThe difference between a published and unpublished workflow is\nimportant:\n\n\\textbf{Published:} Validation is complete, and the workflow can be\nassigned to assets.\n\n\\textbf{Unpublished:} Validation is not performed on the unpublished\nworkflow, and it cannot be assigned to assets until it's published.\n\n\\section{Workflow Versions}\\label{workflow-versions}\n\nYou're making a simple edit to a workflow, when suddenly you remember\nyou have a meeting with your boss. Quickly you save the workflow and\nhurry off to your meeting. Congratulations! You were promoted to\nDirector of Business Productivity! You have no time to edit workflows\nnow, so your colleague must finish editing and publishing the workflow.\nUnfortunately, in all the excitement of your promotion, you forgot what\nyou changed in the workflow. It's best to revert to the prior version\nand start editing it from scratch.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the workflow editor. Go to \\emph{Control Panel} → \\emph{Workflow}\n  → \\emph{Process Builder}, and select the workflow from the list (click\n  its title to open the editor).\n\\item\n  Click the \\emph{Information} button\n  (\\includegraphics{./images/icon-information.png})\n\\item\n  There are two information panel sections: Details and Revision\n  History.\n\n  The Details screen shows information about the creation of the\n  workflow and its last modification, and a summary of the total\n  modifications.\n\n  The Revision History screen shows the workflow's current and prior,\n  restorable versions. To view an old workflow or to restore it if\n  you're sure it's the right version, click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and select either\n  \\emph{Preview} or \\emph{Restore}.\n\\item\n  When you click \\emph{Restore} and see the success message, the prior\n  version is the current version of the workflow. You can now edit the\n  restored version of the workflow.\n\\item\n  If edits are necessary, edit and click \\emph{Update}. This creates\n  another version of the workflow.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-revisions.png}\n\\caption{View and restore prior versions of a workflow.}\n\\end{figure}\n\nAlternatively, you can refer to the\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow\\#embedded-workflows}{embedded\ndefinitions} to get workflow definition ideas.\n\n\\chapter{Reviewing Assets}\\label{reviewing-assets}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nUser interaction is required at each workflow process Task Node. Where\ndo users complete tasks? In the \\emph{My Workflow Tasks} application of\nthe User menu.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-myworkflow-tasks-menu.png}\n\\caption{Users manage workflow tasks from their My Workflow Tasks\nwidget.}\n\\end{figure}\n\n\\section{Asset Submission to\nWorkflow}\\label{asset-submission-to-workflow}\n\nIf an Asset has a workflow activated, when a user clicks Submit for\nPublication, the workflow definition determines the next step. A user\nassigned a Role associated to the workflow task receives a Notification\nindicating that there is a workflow task to complete.\n\n\\textbf{Workflow Assignees Have Implicit Asset Permissions:} Users with\npermission to execute a workflow task (e.g., Users with the Portal\nContent Reviewer Role) have full resource action permissions over the\nassets they can review. These permissions apply in the My Workflow Tasks\nwidget in the User's personal page and anywhere else actions on the\nAsset can be performed.\n\nFor example, consider a User with two permissions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  The Portal Content Reviewer Role enables Users to review workflow\n  submissions and grants edit and delete permissions on the content\n  they're reviewing.\n\\item\n  Users also have permission to view Web Content Articles in the Site's\n  \\emph{Content} section.\n\\end{itemize}\n\nNeither permission explicitly grants the User management permissions on\nWeb Content Articles. Users cannot normally edit or delete a Web Content\nArticle, for example. However, if a Web Content Article is sent to the\nworkflow, Users can access the Web Content Article for review (in their\n\\emph{Assigned to Me} or \\emph{Assigned to my Roles} section of My\nWorkflow Tasks), and they can edit or delete the content while reviewing\nit in the workflow. While it's in the status \\emph{Pending}, they can\nalso edit or delete the article from Site Administration → Content → Web\nContent because of their implicit permissions granted by the workflow\nsystem. This additional permission is temporary, and the normal resource\npermissions are activated once the Web Content Article exits the\nworkflow process (for example, it's rejected or approved).\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-approved-permissions.png}\n\\caption{A User with VIEW permission on Web Content cannot manage\nApproved Articles.}\n\\end{figure}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-pending-permissions.png}\n\\caption{A User with access to Web Content in the Workflow can manage\nPending Articles.}\n\\end{figure}\n\n\\section{Assigning the Task}\\label{assigning-the-task}\n\nWorkflow Tasks can be completed only by certain users, based on the\n\\href{/docs/7-2/reference/-/knowledge_base/r/workflow-task-nodes\\#assignments}{Assignment}.\n\nAll workflow tasks assigned directly to a user are listed in the My\nWorkflow Task widget's \\emph{Assigned to Me} tab.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-assigned-to-me.png}\n\\caption{The assets assigned to a user are listed in \\emph{Assigned to\nMe}.}\n\\end{figure}\n\nIf a workflow was assigned to a Role that the user occupies, the\nworkflow's tasks appear in the \\emph{Assigned to My Roles} tab.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-assigned-to-my-roles.png}\n\\caption{The Assets assigned to Roles are listed in each associated\nuser's \\emph{Assigned to My Roles} tab.}\n\\end{figure}\n\nTo claim a task, the user must move the task into the \\emph{Assigned to\nMe} tab.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the Asset's \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Assign\n  to Me}.\n\\item\n  Add a comment in the pop-up box if necessary, and click \\emph{Done}.\n\\end{enumerate}\n\nAlternatively, assign the task to another user.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the Asset's \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Assign\n  to \\ldots{}}.\n\\item\n  Select the user to assign the task.\n\\item\n  Add a comment in the pop-up box if necessary, and click \\emph{Done}.\n\\end{enumerate}\n\n\\section{Completing the Task}\\label{completing-the-task}\n\nOnce a task is assigned, it's ready to be completed.\n\nThere's a fast way to send an asset along in the review process:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the \\emph{Assigned to Me} tab, click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and select\n  \\emph{Approve} or \\emph{Reject}.\n\n  Note that options names in this menu are identical to the workflow\n  definition's\n  \\href{/docs/7-2/reference/-/knowledge_base/r/workflow-definition-nodes}{Transition}\n  names. Your menu might have different options than the \\emph{Approve}\n  and \\emph{Reject} options in the figure below.\n\\item\n  Enter a comment if desired and click \\emph{Done}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-complete-task.png}\n\\caption{Complete Tasks right from the \\emph{Assigned to Me} list.}\n\\end{figure}\n\nHere's how to get a closer look at the Asset before sending it along in\nthe workflow:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the \\emph{Assigned to Me} tab, click the title of the Asset to\n  review.\n\n  The Task screen appears showing details about the Asset:\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/workflow-task-review.png}\n  \\caption{Inspect Assets before completing the Task.}\n  \\end{figure}\n\\item\n  Inspect the Asset to your liking (or even edit it if you have\n  permission) and click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}).\n\\item\n  Send it along in the workflow by clicking one of the Transition names\n  (for example, \\emph{Approve} or \\emph{Reject} in the Single Approver\n  Definition).\n\\end{enumerate}\n\nAnd, you're done. Once you've completed your workflow tasks, kick back\nand wait for more to come in.\n\n\\chapter{Workflow Metrics: The Service Level Agreement\n(SLA)}\\label{workflow-metrics-the-service-level-agreement-sla}\n\nA brand new feature in Liferay DXP 7.2, \\emph{Workflow Metrics} gives\ninsights into the time certain workflow events take to complete. To use\nit, set up deadlines on a workflow process's events. These deadline\nconfigurations are referred to as SLAs (Service Level Agreements). Once\ndefined, Workflow Reports measure compliance with the SLAs.\n\n\\noindent\\hrulefill\n\n\\textbf{Requires Elasticsearch:} To use Workflow Metrics, you must be\nusing Elasticsearch to index your @product data. Read\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch}{here}\nto learn about installing Elasticsearch.\n\n\\noindent\\hrulefill\n\nSLAs define the deadlines on a workflow process's events. They're like a\ncontract between the workflow participants and Users submitting workflow\nitems.\n\nWorkflow Reports shows data for all processes with SLAs, including each\nworkflow item's SLA status: on time or overdue.\n\n\\noindent\\hrulefill\n\n\\textbf{Editing a Workflow with SLAs:} Editing a workflow (e.g.,\nremoving nodes, editing a task name) with SLAs defined on it may\ninvalidate the SLA for items already in the workflow/SLA pipeline.\n\n\\textbf{Creating or Editing SLAs for Active Processes:} Editing an SLA's\nduration or defining a new SLA while items are already in the workflow\nprocess causes a recalculation for all instances currently in the\nworkflow. Completed workflow instances are not recalculated.\n\n\\noindent\\hrulefill\n\n\\section{Adding SLAs}\\label{adding-slas}\n\nTo add an SLA,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Control Panel → Workflow → Metrics.\n\\item\n  Click on the title of the Process.\n\n  The Reports UI for the process is displayed.\n\\item\n  If there's no SLA for the process, a warning message stating as much\n  appears. Click the \\emph{Add a new SLA} link from the warning to\n  access the New SLA form directly.\n\n  Alternatively, click the Options\n  (\\includegraphics{./images/icon-options.png}) menu and select\n  \\emph{SLA Settings}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/workflow-add-sla.png}\n  \\caption{Add SLAs to a workflow definition from the Metrics\n  application.}\n  \\end{figure}\n\\item\n  On the SLAs screen, click the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}).\n\\item\n  In the New SLA form, Give the SLA a Name and Description.\n\\item\n  Define the time frame for the SLA, specifying three things:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Start: When the item makes it to the event defined here, the SLA\n    timer begins counting.\n  \\item\n    Pause: If there's an event in the workflow when time should stop\n    counting, enter it here. For the Single Approver workflow, you might\n    choose to pause the SLA timer when the item is in the Update task.\n  \\item\n    Stop: Choose when the SLA is completed. If the item makes it to the\n    Stop event before the defined SLA duration (the deadline), it's\n    \\emph{On Time} according to the SLA. If it fails to make it to the\n    Stop event in the specified duration, it's \\emph{Overdue}.\n  \\end{itemize}\n\\item\n  Define the duration (i.e., the deadline) for the SLA. Fill out at\n  least one of the two time boxes.\n\n  \\textbf{Days:} Enter a whole number of days.\n\n  \\textbf{Hours:} Enter hours and minutes in the format HH:MM\n\\item\n  Once you click \\emph{Save}, you'll see the SLA listed on the SLAs\n  screen.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{System Calendar:} By default, there's an internal calendar that\nassumes the SLA duration should continue counting all the time: in other\nwords, 24 hours per day, seven days per week. If you need a different\ncalendar format, find a friendly developer to create a custom calendar.\nOfficial docs will be written for this extension point, but the basic\nidea is to implement the \\texttt{WorkflowMetricsSLACalendar} interface.\nNew implementations of this service are picked up automatically by the\nWorkflow Metrics applications, so it becomes available as soon as the\nmodule holding the service implementation is deployed. The interface has\nthree methods to implement:\n\n\\texttt{public\\ Duration\\ getDuration(\\ \\ \\ \\ LocalDateTime\\ startLocalDateTime,\\ LocalDateTime\\ endLocalDateTime);}\n\n\\texttt{public\\ LocalDateTime\\ getOverdueLocalDateTime(\\ \\ \\ \\ LocalDateTime\\ nowLocalDateTime,\\ Duration\\ remainingDuration);}\n\n\\texttt{public\\ String\\ getTitle(Locale\\ locale);}\n\nSee the \\texttt{DefaultWorkflowMetricsSLACalendar} from the\n\\texttt{portal-workflow-metrics-service} module for example code. If you\ndefine a new calendar, a new option becomes available in the Add SLA\nform, allowing you to choose from the default 24/7 calendar or any\ncustom ones you've provided (for example, make the timer run for 9 hours\nper day, 5 days per week).\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-metrics-sla-list.png}\n\\caption{Manage SLAs from the SLAs screen.}\n\\end{figure}\n\n\\subsection{Valid Start and Stop\nEvents}\\label{valid-start-and-stop-events}\n\nAny workflow task can be used as a start or end parameter for the SLA.\n\nWhen defining the tasks to act as the SLA's Start Events, choose between\nthree events:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  The start node\n\\item\n  Entry into a task\n\\item\n  Exit from a task\n\\end{itemize}\n\nWhen defining the tasks to act as the SLA's Stop Events, choose between\nthree events:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Entry into a task\n\\item\n  Exit from a task\n\\item\n  The end node\n\\end{itemize}\n\nThe SLA can be paused at any task that falls between the start node and\nthe end node, and it's defined by setting the node(s) when the SLA\nshould be paused. \\emph{The SLA timer is paused the entire time a\nworkflow item is in the specified node}.\n\n\\subsection{Durations}\\label{durations}\n\nDefine the SLA durations in at least one of the available boxes (Days\nand Hours). Here are some examples:\n\n\\begin{description}\n\\tightlist\n\\item[Example Duration: 1 day, 24 hours]\nValid configuration --- Days: \\emph{1}\n\nInvalid --- Hours: \\emph{24:00}. The Hours box must not exceed\n\\emph{23:59}.\n\\item[Example Duration: 36 hours]\nValid --- Days: \\emph{1}, Hours: \\emph{12:00}\n\nInvalid --- Days: \\emph{1.5}. Only whole numbers are accepted.\n\\item[Example Duration: 6.5 hours]\nValid --- Hours: \\emph{06:30}\n\\end{description}\n\nOnce your SLAs are configured, activate the workflow on an asset,\nstretch your fingers, and get ready for the submissions to roll in if\nyou're one of the workflow assignees. You're on the hook to get those\nworkflow items through the process within the SLA duration!\n\n\\chapter{Workflow Metrics: Reports}\\label{workflow-metrics-reports}\n\nAs soon as you enter the Metrics screen (Control Panel → Workflow →\nMetrics) you see metrics on each workflow installed in the system.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-metrics-reports1.png}\n\\caption{In this view, the only process with pending items is the Single\nApprover.}\n\\end{figure}\n\nA table view of all installed workflow processes shows you how many\nitems are Overdue, how many are On Time, and how many are Pending in the\nworkflow process.\n\nThere's more to Metrics than the overview report though. Get more\ndetailed reports by clicking on one of the workflow processes.\n\n\\noindent\\hrulefill\n\n\\textbf{Requires Elasticsearch:} To use Workflow Metrics, you must be\nusing Elasticsearch to index your @product data. Read\n\\href{/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch}{here}\nto learn about installing Elasticsearch.\n\n\\noindent\\hrulefill\n\n\\section{Understanding Reports}\\label{understanding-reports}\n\nThe Reports UI has two main views, represented as tabs: \\emph{Pending}\n(changed to \\emph{Dashboard} as of 7.0 Service Pack 1---SP1) and\n\\emph{Completed} (changed to \\emph{Performance} in SP1).\n\n\\emph{Pending/Dashboard} items are those currently in the workflow\nprocess, and include items untracked by the SLA. This might include\nitems in the paused step of the workflow, or items that are outside the\nscope of the SLA duration.\n\n\\emph{Completed/Performance} items show any item that has completed\nprocessing in the workflow. SP1 includes a new chart on this screen:\n\\emph{Completion Velocity}.\n\nWhen you first click into the metrics for a specific process, you're\npresented with two valuable reports on pending items: the Pending Items\noverview and Workload by Step.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-metrics-reports2.png}\n\\caption{See data on the Pending Items and the Workload by Step for a\nprocess.}\n\\end{figure}\n\n\\section{Pending Items}\\label{pending-items}\n\nPending Items shows you the overview of items by their SLA status. Drill\ndown by clicking on any of the statuses to see the specific items that\nare enumerated in Pending Items.\n\n\\section{Workload by Step}\\label{workload-by-step}\n\nWorkload by Step shows a breakdown of the items that are in each step of\nthe workflow process, by their SLA status (Overdue or On Time).\n\n\\section{Completed Items}\\label{completed-items}\n\nClick the \\emph{Completed} tab (\\emph{Performance} on SP1) to see the\nitems that have completed the workflow process. Workload by Step data\ndoesn't make sense in this case, because by definition, these items are\nno longer in any workflow process step. Instead, there's a\n\\emph{Completion Velocity} chart that shows the performance over time.\n\n\\section{Completion Velocity}\\label{completion-velocity}\n\nA line chart shows you the completion rate for the workflow process. The\ndefault display shows the number of completed workflow instances per\nday, for the last 30 days.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-reports-completion-velocity.png}\n\\caption{View the completion rate of items in a workflow process over\ntime.}\n\\end{figure}\n\nThe overall completion rate for the time period is displayed in the top\nright corner of the chart (as \\emph{Inst/timeUnit}), while the\ntrend-line is presented in the chart body. The overall performance\nmetric and the chart body are updated when you select a new time period;\nthe time unit changes depending on the total time period you're\nmeasuring. For some of the longer durations, the time unit is\nconfigurable:\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Today}]\nCalculate \\emph{Inst/Hour} from \\emph{00:00}, or \\emph{12:00 AM}, of the\ncurrent day until the current time (rounded to the nearest whole hour).\n\\item[\\textbf{Yesterday}]\nCalculate \\emph{Inst/Hour} From \\emph{00:00-23:59}, or \\emph{12:00 AM to\n11:59 PM}, of the previous day.\n\\item[\\textbf{Last 7 Days}]\nCalculate \\emph{Inst/Day}. The current day counts as 1 day, so this is\nfrom 6 days ago to the current day.\n\\item[\\textbf{Last 30 Days}]\nCalculate the \\emph{Inst/Week} or the \\emph{Inst/Day}. The current day\ncounts as 1 day, so this is from 29 days ago to the current day.\n\\item[\\textbf{Last 90 Days}]\nCalculate the \\emph{Inst/Month}, \\emph{Inst/Week}, or \\emph{Inst/Day}.\nThe current day counts as 1 day, so this is from 89 days ago to the\ncurrent day.\n\\item[\\textbf{Last 180 Days}]\nCalculate the \\emph{Inst/Month} or \\emph{Inst/Week}. The current day\ncounts as 1 day, so this is from 179 days ago to the current day.\n\\item[\\textbf{Last Year}]\nCalculate the \\emph{Inst/Month} or \\emph{Inst/Week}. The current day\ncounts as 1 day, so this is from 364 days (365 for a leap year) ago to\nthe current day.\n\\end{description}\n\n\\section{Items View}\\label{items-view}\n\nHover over the status you're interested in, from either the\n\\emph{Pending} or the \\emph{Completed} tabs (on SP1, these tabs were\nrenamed to \\emph{Dashboard} and \\emph{Completion}). Click into the All\nItems screen from the overview report and a more detailed table appears,\nincluding the following columns:\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{ID}]\nThis is the workflow item's numeric identifier to the system.\nImportantly, you can click this to enter the Item Detail view.\n\\item[\\textbf{Item Subject}]\nThis shows a human readable summary of the item, to help administrators\nidentify the item.\n\\item[\\textbf{Process Step}]\nThis identifies where the item is in the workflow.\n\\item[\\textbf{Created By}]\nThis shows the user name of the submitting User.\n\\item[\\textbf{Creation Date}]\nThis shows the date and time the item was submitted to the workflow.\n\\end{description}\n\nThe All Items view can be filtered so you can find the subset of items\nyou want to analyze.\n\n\\section{Filtering by SLA Status}\\label{filtering-by-sla-status}\n\nFilter items based on whether they're Overdue, On Time, or Untracked.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-metrics-reports4.png}\n\\caption{Filter by SLA status: Overdue, On Time, or Untracked.}\n\\end{figure}\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Overdue}]\nOverdue items have breached at least one SLAs defined deadline.\n\\item[\\textbf{On Time}]\nOn Time items have not breached \\emph{any} SLA deadline.\n\\item[\\textbf{Untracked}]\nUntracked items are items in the workflow process that aren't currently\nunder the purview of an SLA. The can be in a task identified as a\n\\emph{Pause} in the SLA, or perhaps outside the scope of the SLA\nentirely, if the SLA isn't defined for the entire process (Process\nBegins to Process Ends in the SLA Definition screen).\n\\end{description}\n\n\\section{Filtering by Process Status and Completion\nPeriod}\\label{filtering-by-process-status-and-completion-period}\n\nFilter items based on whether they're Pending or Completed in the\nworkflow process.\n\nIf you filter by the Completed status, you'll get an additional\nfiltering option: filter items by the Completion Period. Select from\nthese time periods:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Today\n\\item\n  Yesterday\n\\item\n  Last 7 Days\n\\item\n  Last 30 Days (default)\n\\item\n  Last 90 Days\n\\item\n  Last 180 Days\n\\item\n  Last Year\n\\item\n  All Time\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-reports-process-status-period.png}\n\\caption{Filter by Process Status and Completion Period.}\n\\end{figure}\n\n\\section{Filtering by Process Step}\\label{filtering-by-process-step}\n\nFilter items based on where they are in the workflow definition. For\nexample, in the Single Approver workflow process, you can choose to see\na report including all items in the Review task. This is different for\neach workflow definition.\n\n\\section{Combining Filters}\\label{combining-filters}\n\nUse a combination of filters to find just the items you need to see. For\nexample, below are all items in the Single Approver process's Review\ntask that have the status Completed or Pending, whether On time or\nOverdue. Untracked items aren't shown.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-metrics-reports13.png}\n\\caption{Combine filters to see just the items you want.}\n\\end{figure}\n\n\\section{Item Details}\\label{item-details}\n\nTo see the metrics for a single workflow process item, click the ID\nfield while in the All Items view. A pop-up shows you more detailed\ninformation on the item.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-reports-item-detail.png}\n\\caption{Item Details include SLA status information and whether the\nitem is Resolved or Open.}\n\\end{figure}\n\nFrom here you can view detailed information about the asset and even\nclick \\emph{Go to Submission Page}, which redirects you to the item's\nview in the Submissions section of the Control Panel.\n\nThe top of the Item Detail view is important. It shows you the\ninformation about the due date for the item in the SLA, and its SLA\ncompletion status: \\emph{Open} or \\emph{Resolved}.\n\n\\begin{description}\n\\tightlist\n\\item[\\textbf{Open}]\nThe defined SLA goals are not yet met. Open items can be of status\nOverdue or On Time.\n\\item[\\textbf{Resolved}]\nThe defined SLA goals are completed. Resolved items can be of status\nOverdue or On Time.\n\\end{description}\n\nFrom the overall metrics of a workflow process down to the details on a\nsingle item in the workflow, the new Workflow Metrics functionality\ngives you insights into the time it takes to \\emph{get things done} in\nLiferay DXP.\n\n\\chapter{Workflow Designer}\\label{workflow-designer}\n\nWith the proper permissions, users can publish assets. Even if your\nenterprise has the greatest employees in the world, many of the items\nthey want to publish must still be reviewed, for a variety of reasons.\nThe Workflow Designer lets you design workflow definitions so your\nassets go through a review process before publication.\n\nWith the Workflow Designer, you develop workflow definitions using a\nconvenient drag and drop user interface. You don't need to be familiar\nwith writing XML definitions by hand. Some of the features can be\nenhanced, however, if you're familiar with Groovy, a supported\nJava-based scripting language. All that is to say, don't be scared off\nwhen you come to a block of code in these articles. Just decide if you\nneed the feature and find someone familiar with Java or Groovy to help\nyou out.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} By default, only one workflow definition is installed:\nthe Single Approver Workflow definition. What you might not know is that\nyou have access to several others too. Look in\n\\texttt{{[}Liferay\\_Home{]}/osgi/marketplace/} and find the\n\\texttt{Liferay\\ Forms\\ and\\ Workflow\\ -\\ Liferay\\ Portal\\ Workflow\\ -\\ Impl.lpkg}.\nOpen it, find the\n\\texttt{com.liferay.portal.workflow.kaleo.runtime.impl-{[}version{]}.jar},\nand look in its \\texttt{META-INF/definitions} folder. You'll see the\nfollowing workflow definitions:\n\n\\begin{verbatim}\n category-specific-definition.xml\n legal-marketing-definition.xml\n single-approver-definition.xml\n single-approver-definition-scripted-assignment.xml\n\\end{verbatim}\n\nTo work with any of these definitions in the Workflow Designer, extract\nthem from the JAR file first. Once you have the XML files locally,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add a new workflow. Go to Control Panel → Workflow → Process Builder,\n  and click the Add button (\\includegraphics{./images/icon-add.png}).\n\\item\n  Go to the Source tab.\n\\item\n  Click \\emph{import a file} and upload the XML file.\n\\item\n  Name the definition appropriately, and click either \\emph{Save} (to\n  save it as a draft) or \\emph{Publish} (see below for more information\n  on saving and publishing).\n\\end{enumerate}\n\nNow you can begin exploring or modifying the definition.\n\n\\noindent\\hrulefill\n\nIt's time to start exploring the Workflow Designer and its features.\n\n\\chapter{Managing Workflows with the Workflow\nDesigner}\\label{managing-workflows-with-the-workflow-designer}\n\nThe Workflow Designer gives you an intuitive interface for creating\nworkflow definitions, from the simplest approval processes to the most\ncomplex business processes you can imagine. It features a drag and drop\ninterface, workflow definition versioning, and a graphical\nrepresentation of definitions and their nodes. Without the Workflow\nDesigner, you'd have to hand-craft your entire workflow definition in\nXML. With the Workflow Designer, you might never need to look at a\nsingle line of XML. Of course, the Workflow Designer also lets you\ndirectly manipulate the XML (using the \\emph{Source} tab) if you find it\nconvenient.\n\n\\section{Adding New Workflow Definitions with the Workflow\nDesigner}\\label{adding-new-workflow-definitions-with-the-workflow-designer}\n\nAccess the Workflow Designer by going to the Control Panel → Workflow →\nProcess Builder. Click the Add icon\n(\\includegraphics{./images/icon-add.png}).\n\nGive the workflow definition a title and you're ready to start designing\nyour workflow.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-designer-canvas.png}\n\\caption{The Workflow Designer's graphical interface makes designing\nworkflows intuitive.}\n\\end{figure}\n\n\\section{Saving and Publishing Workflow\nDefinitions}\\label{saving-and-publishing-workflow-definitions}\n\nFirst, look below the canvas to see the buttons that let you \\emph{Save}\nor \\emph{Publish}. Saving the definition as a draft lets you save your\nwork so it's not lost (due to a timeout, for example). It won't be\npublished (and assignable to assets), and it won't be considered a\nversion until the Publish button is clicked. Each time you save the\nworkflow as a draft, a new revision is added to the Revision history. To\nsee the Revision history and manage workflow versions, open the Info\nsidebar (\\includegraphics{./images/icon-information.png}) and click\n\\emph{Revision History}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-designer-definitions.png}\n\\caption{View a list of the current workflows that can be edited in the\nWorkflow Designer.}\n\\end{figure}\n\n\\section{Adding Nodes}\\label{adding-nodes}\n\nA new workflow is already populated with a start node, an end node, and\na transition between them. To make the workflow the way you want it, add\nnodes to the workflow.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\emph{Drag} a node from the \\emph{Nodes} palette on the right of the\n  designer and \\emph{drop} it on the canvas.\n\\item\n  You'll see it's not connected to other nodes by a transition, so right\n  now it can't be used in the workflow. Delete the existing transition\n  and then you can make new transitions to direct the \\emph{flow} of\n  your workflow (see more about transitions below if you're not sure\n  what they're for or how to use them in the Workflow Designer).\n\\end{enumerate}\n\nAlternatively, start by deleting the default transition, then click the\nedge of the start node, drag a new transition from the start node to a\nblank spot on the canvas, and release it. You're prompted to create a\nnode at that spot, because you can't have a transition without a\nstarting point and an ending point on a node.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-designer-add-node.png}\n\\caption{You can add a node by creating a transition that ends at a\nblank spot on your Designer canvas.}\n\\end{figure}\n\nThat's it. Of course, if you drag, say, a \\emph{Task} node onto the\ncanvas, it must be configured.\n\n\\section{Node Settings}\\label{node-settings}\n\nNow you know how to add nodes to the workflow definition. By default you\nhave three things added to your canvas: a start node, a transition, and\nan end node. Think of the \\emph{EndNode} as the point in the workflow\nwhere an asset reaches the \\emph{Approved} status. The \\emph{StartNode}\nis where the asset goes from the \\emph{Draft} status to \\emph{Pending}.\nYou might decide to name your nodes to reflect what's happening in each\none. To name a node, double click it, and its \\emph{Settings} appear.\nThen double click the value of the \\emph{Name} property and you can edit\nthe name. Click \\emph{Save} when you're done.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-designer-node-settings.png}\n\\caption{You can edit a node's settings.}\n\\end{figure}\n\nOf course, there's more you can do besides changing node names. Actions,\nNotifications, and Assignments can be used to make your workflow\ndefinition useful and interactive. Keep reading to learn about these\nfeatures.\n\n\\section{Related Topics}\\label{related-topics-3}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/kaleo-forms}{Kaleo Forms}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/activating-workflow}{Activating\nWorkflow}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework}{Liferay's\nWorkflow Framework}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/dynamic-data-lists}{Dynamic Data\nLists}\n\n\\chapter{Workflow Definition Nodes}\\label{workflow-definition-nodes}\n\nOnce you know the basics of\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow-designer}{creating\nworkflow definitions} with the workflow Designer, you can get into the\ndetails. In this tutorial you'll learn about Actions and Notifications,\ntwo important features your workflow nodes can use. You'll also learn\nhow to affect the processing of the workflow using Transitions, Forks,\nJoins, and Conditions.\n\nThere are several node types you can use in workflow definitions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Task nodes\n\\item\n  Fork and Join nodes\n\\item\n  Condition nodes\n\\item\n  Start nodes\n\\item\n  End nodes\n\\item\n  State nodes\n\\end{itemize}\n\nBecause they're the most complex node, and often the meat of your\nworkflow definitions, Task nodes are covered\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-tasks-in-workflow-designer}{separately}.\n\nFork, Join, and Condition nodes are discussed, along with Transitions,\nin a tutorial on\n\\href{/docs/7-2/user/-/knowledge_base/u/affecting-the-processing-of-workflow-definitions}{workflow\nprocessing}, since they're used for affecting the processing of the\nworkflow.\n\nThis tutorial discusses State nodes, Start nodes, and End nodes, along\nwith Actions and Notifications.\n\n\\section{Node Actions and\nNotifications}\\label{node-actions-and-notifications}\n\nAny node can have Actions and Notifications.\n\n\\section{Actions}\\label{actions}\n\nActions do additional processing before entering the node, after exiting\na node, or once a task node is assigned. They're configured by accessing\na node's Properties tab, then double clicking \\emph{Actions}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-designer-action.png}\n\\caption{You can add an Action to a Task node.}\n\\end{figure}\n\nThe Single Approver workflow contains an Update task with an action\nwritten in Groovy that sets the status of the asset as \\emph{denied},\nthen sets it to \\emph{pending}.\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nWorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus(\"denied\"), workflowContext);\nWorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus(\"pending\"), workflowContext);\n\\end{verbatim}\n\nWhy would the action script first set the status to one thing and then\nto another like that? Because for some assets, the \\emph{denied} status\nsends the asset creator an email notification that the item has been\ndenied.\n\nThe end node in your workflow definition has an action configured on it\nby default, on entry to the end node:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nWorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus(\"approved\"), workflowContext);\n\\end{verbatim}\n\nThis is a Groovy script that updates the status to \\emph{approved},\nsince that's usually the goal of a workflow process.\n\nYou can do something simple like the actions above, or you can be as\ncreative as you'd like.\n\nIt's good to assign a task to a user, and it's even more useful if the\nuser is notified about workflow tasks.\n\n\\section{Notifications}\\label{notifications}\n\nNotifications are sent to tell task assignees that the workflow needs\nattention or to update asset creators on the status of the process. They\ncan be sent for tasks or any other type of node in the workflow. To set\nup notifications, double click on \\emph{Notifications} in a node's\nProperties tab and create a notification.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-designer-notification.png}\n\\caption{You can send a Notification from a Task node.}\n\\end{figure}\n\nYou must specify the Notification Type, and you can choose User\nNotification or Email. You can use Freemarker if you need a template, or\nyou can choose to write a plain text message.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Instant Messenger and Private Message also appear as\nNotification Type options, but these are non-functional and will be\nremoved in a future version.\n\n\\noindent\\hrulefill\n\nHere's a basic Freemarker template that reports the name of the asset\ncreator and the type of asset in the notification:\n\n\\begin{verbatim}\n${userName} sent you a ${entryType} for review in the workflow.\n\\end{verbatim}\n\nYou can also choose to link the sending of the notification to entry\ninto the node (On Entry), when a task is assigned (On Assignment), or\nwhen the workflow processing is leaving a node (On Exit). You can\nconfigure multiple notifications on a node.\n\nCommonly, the assignment and notification settings are teamed up so a\nuser receives a notification when assigned a task in the workflow. To do\nthis, choose \\emph{Task Assignees} under Recipient Type when configuring\nthe notification.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\emph{from name} and \\emph{from address} of an email\nnotification are configurable via portal properties. Place these\nsettings into a \\texttt{portal-ext.properties} file, in your Liferay\nHome folder. Then restart the server:\n\n\\begin{verbatim}\n workflow.email.from.name=\n workflow.email.from.address=\n\\end{verbatim}\n\nThese can also be set programmatically into the\n\\texttt{WorkflowContext}, and the programmatic setting always takes\nprecedence over the system scoped portal property.\n\n\\noindent\\hrulefill\n\n\\section{Start and End Nodes}\\label{start-and-end-nodes}\n\nStart and end nodes kick off the workflow processing and bring the asset\nto its final, approved state. Often you can use the default start and\nend nodes without modification. If you want to do some more processing\n(in the case of a start node), add an action to the node using the\nProperties tab as described in the section on Actions above.\n\nEnd nodes have a default action that sets the workflow status to\nApproved using the Groovy scripting language:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nWorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus(\"approved\"), workflowContext);\n\\end{verbatim}\n\nAdd more to the action script if you need to do additional processing.\n\nBy default, there's a transition connecting the start node and end node,\nbut you'll probably want to delete it, since most workflows don't\nproceed straight from the initial state to approved.\n\n\\section{State Nodes}\\label{state-nodes}\n\nState nodes can have Notifications and Actions. The default end node\nadded by workflow Designer is a pre-configured state node that sets the\nworkflow status to Approved. Perhaps you want to create a node that sets\nthe status to \\emph{Expired}. You could create a state node for it by\ndragging one onto your workflow Designer canvas, then configuring an\naction in it that sets the status to Expired. Here's what it would look\nlike in Groovy:\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nWorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus(\"expired\"), workflowContext);\n\\end{verbatim}\n\nNext, you'll learn to do parallel processing using fork and join nodes.\n\n\\section{Related Topics}\\label{related-topics-4}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/kaleo-forms}{Kaleo Forms}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/activating-workflow}{Activating\nWorkflow}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework}{Liferay's\nWorkflow Framework}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/dynamic-data-lists}{Dynamic Data\nLists}\n\n\\chapter{Affecting the Processing of Workflow\nDefinitions}\\label{affecting-the-processing-of-workflow-definitions}\n\nWorkflow definitions all contain nodes: a Start Node, and End node, and\nat least one Task node. You might know that for the workflow to progress\nfrom one node to the other, you need Transitions. In this tutorial\nyou'll learn about using transitions to move the asset through the\nworkflow from node to node, but you'll also learn about some other\nfeatures that affect the processing of the workflow.\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Transitions\n\\item\n  Forks\n\\item\n  Joins\n\\item\n  Conditions\n\\end{itemize}\n\nStart by learning about the ever important Transition.\n\n\\section{Transitions}\\label{transitions}\n\nWhat are transitions? Workflow transitions connect one node to another.\nOn exiting the first node, processing continues to the node pointed to\nby the transition. Every time you create an arrow from one node to\nanother, Workflow Designer creates a transition.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-designer-single-approver.png}\n\\caption{You connect nodes and direct workflow processing with\ntransitions. The Single Approver workflow has transitions named Submit,\nResubmit, Reject, and Approve.}\n\\end{figure}\n\nEach node you add has a pop-up menu letting you delete the node. As you\nhover your mouse over the edges of a node, notice your mouse pointer\nchanges to a cross. The cross indicates you can connect the current node\nto another node. Hold down your mouse button and drag the mouse to start\ndrawing your transition to another node. If you stop before reaching the\nedge of the next node, a pop-up displays node types you can create and\nconnect to on-the-fly. To connect with an existing node, continue\ndragging the connector to that node.\n\nWhen developing workflows in the Workflow Designer, make sure you go\nthrough all the transitions and name them appropriately. By default,\nthese transitions get system generated names, so rename them to\nsomething more human-readable, as they're displayed to workflow users as\nlinks that send the item to the next step in the workflow.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-designer-transition-link.png}\n\\caption{In the Single Approver workflow, a user in the Review task can\nchoose to Approve or Reject the asset, which sends the asset either to\nthe EndNode or to the Update task.}\n\\end{figure}\n\nTo rename transitions, click on the arrow representing the transition\nand use the Properties tab to set the name just like you do for a node.\n\n\\section{Forks and Joins}\\label{forks-and-joins}\n\nSometimes you don't need to wait for one task to be completed before\nmoving on to another one. Instead, you want to do two or more things at\nthe same time. To do this, transition to a fork node, make two\ntransitions from the fork to your parallel tasks, and then come back\ntogether using a join node.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-designer-fork-join.png}\n\\caption{Forks and Joins are used to enable parallel processing in the\nworkflow.}\n\\end{figure}\n\nWith a regular Join node, for the workflow to proceed beyond the join,\nthe transition from both parallel executions must be invoked. However,\nif you use a Join XOR node instead, the workflow proceeds as long as the\ntransition from one of the parallel executions is invoked.\n\nKeep in mind that you must balance your fork and join nodes. In other\nwords, for every fork, there must be a join that brings the parallel\nworkflow threads back together.\n\n\\section{Conditions}\\label{conditions-1}\n\nSometimes you must inspect an asset or its execution context, and\ndepending on the result, send it to the appropriate transition. You need\na node for a script that concludes by setting a value to one of your\ntransitions.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-designer-cat-specific-condition.png}\n\\caption{The Category Specific Approval definition starts with a\nCondition node.}\n\\end{figure}\n\nFrom the \\emph{Category Specific Approval}\n(\\texttt{category-specific-definition.xml}), this is the script in the\ncondition node that starts the workflow (coming directly from the start\nnode):\n\n\\begin{verbatim}\nimport com.liferay.asset.kernel.model.AssetCategory;\nimport com.liferay.asset.kernel.model.AssetEntry;\nimport com.liferay.asset.kernel.model.AssetRenderer;\nimport com.liferay.asset.kernel.model.AssetRendererFactory;\nimport com.liferay.asset.kernel.service.AssetEntryLocalServiceUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\nimport com.liferay.portal.kernel.workflow.WorkflowHandler;\nimport com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;\n\nimport java.util.List;\n\nString className = (String)workflowContext.get(WorkflowConstants.CONTEXT_ENTRY_CLASS_NAME);\n\nWorkflowHandler workflowHandler = WorkflowHandlerRegistryUtil.getWorkflowHandler(className);\n\nAssetRendererFactory assetRendererFactory = workflowHandler.getAssetRendererFactory();\n\nlong classPK = GetterUtil.getLong((String)workflowContext.get(WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\nAssetRenderer assetRenderer = workflowHandler.getAssetRenderer(classPK);\n\nAssetEntry assetEntry = assetRendererFactory.getAssetEntry(assetRendererFactory.getClassName(), assetRenderer.getClassPK());\n\nList<AssetCategory> assetCategories = assetEntry.getCategories();\n\nreturnValue = \"Content Review\";\n\nfor (AssetCategory assetCategory : assetCategories) {\n    String categoryName = assetCategory.getName();\n\n    if (categoryName.equals(\"legal\")) {\n        returnValue = \"Legal Review\";\n\n        return;\n    }\n}\n\\end{verbatim}\n\nThis example checks the asset category to choose the processing path,\nwhether to transition to the \\emph{Legal Review} task or the\n\\emph{Content Review} task.\n\nYou may be wondering what that \\texttt{returnValue} variable is. It's\nthe variable that points from the condition to a transition, and its\nvalue must match a valid transition in the workflow definition. This\nscript looks up the asset in question, retrieves its\n\\href{/docs/7-2/user/-/knowledge_base/u/defining-categories-for-content}{asset\ncategory}, and sets an initial \\texttt{returnValue}. Then it checks to\nsee if the asset has been marked with the \\emph{legal} category. If not\nit goes through \\emph{Content Review} (the content-review task in the\nworkflow), and if it does it goes through \\emph{Legal Review} (the\nlegal-review task in the workflow).\n\nNow you're equipped with the basic knowledge to design beautiful,\neffective workflows so that your assets can be properly reviewed before\nthey're published in your sites.\n\n\\section{Related Topics}\\label{related-topics-5}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/kaleo-forms}{Kaleo Forms}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/activating-workflow}{Activating\nWorkflow}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework}{Liferay's\nWorkflow Framework}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/dynamic-data-lists}{Dynamic Data\nLists}\n\n\\chapter{Creating Tasks in the Workflow\nDesigner}\\label{creating-tasks-in-the-workflow-designer}\n\nTask nodes have several parts and are the most complex parts of a\nworkflow definition. Unlike other workflow nodes, task nodes have\nAssignments, because a User is expected to \\emph{do something} (often\napprove or reject the submitted asset) when a workflow process enters\nthe task node: the assignment specifies who that User is.\n\nCommonly, task nodes contain Notifications, Assignments, and Actions\n(defined in scripts). See more about Notifications and Actions in the\narticle on\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow-definition-nodes}{workflow\nnodes}. Task nodes and their assignments are more complex and deserve\ntheir own article (this one).\n\nTo get started, drag and drop a task node on your workflow canvas if you\nhaven't already. Open its Properties and give it a name. Then double\nclick \\emph{Actions} in the task's Properties pane.\n\nYou can define a notification (often Task Assignee is appropriate), or\nwrite a Groovy script defining an action that's triggered for your task.\n\nNext learn about creating Assignments for your task nodes.\n\n\\section{Assignments}\\label{assignments}\n\nWorkflow tasks must be completed by a User. You can choose how you want\nto configure your assignments.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-designer-assignment.png}\n\\caption{You can add an Assignment to a Task node.}\n\\end{figure}\n\nYou can choose to add assignments to specific Roles, multiple Roles of a\nRole Type (Organization, Site, or regular Role types), to the Asset\nCreator, to Resource Actions, or to specific Users. Additionally, you\ncan write a script to define the assignment.\n\nAssigning tasks to Roles, Organizations, or Asset Creators is a\nstraightforward concept, but what does it mean to assign a workflow task\nto a Resource Action? Imagine an \\emph{UPDATE} resource action. If your\nworkflow definition specifies the UPDATE action in an assignment, anyone\nwho has permission to update the type of asset being processed in the\nworkflow is assigned to the task. You can configure multiple assignments\nfor a task.\n\n\\section{Resource Action Assignments}\\label{resource-action-assignments}\n\n\\emph{Resource actions} are operations performed by Users on an\napplication or entity. For example, a User might have permission to\nupdate Message Boards Messages. This is called an UPDATE resource\naction, because the User can update the resource. If you're still\nuncertain about what resource actions are, refer to the developer\ntutorial on the\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions}{permission\nsystem} for a more detailed explanation.\n\nTo find all the resource actions that have been configured, you need\naccess to the Roles Admin application in the Control Panel (in other\nwords, you need permission for the VIEW action on the Roles resource).\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Navigate to Control Panel → Users → Roles.\n\\item\n  Add a new Regular Role. See the\n  \\href{/docs/7-2/user/-/knowledge_base/u/roles-and-permissions}{article\n  on managing roles} for more information.\n\\item\n  Once the Role is added, navigate to the Define Permissions interface\n  for the Role.\n\\item\n  Find the resource whose action you want to use for defining your\n  workflow assignment.\n\\end{itemize}\n\nHow do you go from finding the resource action to using it in the\nworkflow? Use the Workflow Designer's interface for setting up a\nresource action assignment.\n\nWhen configuring your task node's Assignment, select Resource Actions as\nthe Assignment Type, then specify the Resource Actions to use for the\nassignment (for example, UPDATE).\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/workflow-designer-resource-action-assignment.png}\n\\caption{Configure resource action assignments in the Workflow\nDesigner.}\n\\end{figure}\n\nHere's what the assignment looks like in the Source (Workflow XML) tab:\n\n\\begin{verbatim}\n<assignments>\n    <resource-actions>\n        <resource-action>UPDATE</resource-action>\n    </resource-actions>\n</assignments>\n\\end{verbatim}\n\nAs usual, assign the workflow to the appropriate workflow enabled asset.\n\nNow when the workflow proceeds to the task with the resource action\nassignment, Users with \\texttt{UPDATE} permission on the resource (for\nexample, Message Boards Messages) are notified of the task and can\nassign it to themselves (if the notification is set to Task Assignees).\nSpecifically, Users see the tasks in their \\emph{My Workflow Tasks}\napplication under the tab \\emph{Assigned to My Roles}.\n\nUse all upper case letters for resource action names. Here are some\ncommon resource actions:\n\n\\begin{verbatim}\nUPDATE\nADD\nDELETE\nVIEW\nPERMISSIONS\nSUBSCRIBE\nADD_DISCUSSION\n\\end{verbatim}\n\nYou can determine the probable resource action name from the permissions\nscreen for that resource. For example, in Message Boards, one of the\npermissions displayed on that screen is \\emph{Add Discussion}. Convert\nthat to all uppercase and replace the space with an underscore, and you\nhave the action name.\n\n\\section{Scripted Assignments}\\label{scripted-assignments}\n\nYou can also use a script to manage the assignment. Here's the script\nfor the Review task assignment in the Scripted Single Approver workflow\ndefinition\n(\\texttt{single-approver-definition-scripted-assignment.xml}):\n\n\\begin{verbatim}\nimport com.liferay.portal.kernel.model.Group;\nimport com.liferay.portal.kernel.model.Role;\nimport com.liferay.portal.kernel.service.GroupLocalServiceUtil;\nimport com.liferay.portal.kernel.service.RoleLocalServiceUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nlong companyId = GetterUtil.getLong((String)workflowContext.get(WorkflowConstants.CONTEXT_COMPANY_ID));\n\nlong groupId = GetterUtil.getLong((String)workflowContext.get(WorkflowConstants.CONTEXT_GROUP_ID));\n\nGroup group = GroupLocalServiceUtil.getGroup(groupId);\n\nroles = new ArrayList<Role>();\n\nRole adminRole = RoleLocalServiceUtil.getRole(companyId, \"Administrator\");\n\nroles.add(adminRole);\n\nif (group.isOrganization()) {\n    Role role = RoleLocalServiceUtil.getRole(companyId, \"Organization Content Reviewer\");\n\n    roles.add(role);\n}\nelse {\n    Role role = RoleLocalServiceUtil.getRole(companyId, \"Site Content Reviewer\");\n\n    roles.add(role);\n}\n\nuser = null;\n                    \n\\end{verbatim}\n\nDon't let all that code intimidate you. It's just assigning the task to\nthe \\emph{Administrator} Role, then checking whether the \\emph{group} of\nthe asset is an Organization and assigning it to the \\emph{Organization\nContent Reviewer} Role if it is. If it's not, it's assigning the task to\nthe \\emph{Site Content Reviewer} Role.\n\nNote the\n\\texttt{roles\\ =\\ new\\ ArrayList\\textless{}Role\\textgreater{}();} line\nabove. In a scripted assignment, the \\texttt{roles} variable is where\nyou specify any Roles the task is assigned to. For example, when\n\\texttt{roles.add(adminRole);} is called, the Administrator role is\nadded to the assignment.\n\n\\section{Related Topics}\\label{related-topics-6}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/kaleo-forms}{Kaleo Forms}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/activating-workflow}{Activating\nWorkflow}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework}{Liferay's\nWorkflow Framework}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/dynamic-data-lists}{Dynamic Data\nLists}\n\n\\chapter{Kaleo Forms}\\label{kaleo-forms}\n\nBusiness processes are often form-based and workflow-driven. They start\nwith entered data and progress by sending that data to other people or\ngroups. Then it's processed in some way (for example, further data is\nentered or approval is granted), and the process moves on until\ncompletion, when each interested party has seen and manipulated the\ndata. To write an app for each of these processes is laborious. It's\nmuch better to have a tool for quickly defining a process to suit each\nuse case. The process architect must define both the data that gets\ncollected and the process the data moves through to reach its final\nstate. To accomplish this, Liferay DXP already includes the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-data-definitions}{Dynamic\nData Lists app} for defining forms, and the\n\\href{/docs/7-2/user/-/knowledge_base/u/workflow-designer}{Workflow\nDesigner app} for designing workflows. The Kaleo Forms solution combines\nthe features of these apps, letting you use a single UI to design an\nintegrated process for sending forms through a workflow.\n\n\\section{Creating Kaleo Forms\nProcess}\\label{creating-kaleo-forms-process}\n\nTo start creating a Kaleo Forms Process you need to get to Kaleo Forms\nAdmin:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} (your Site's menu) → \\emph{Content \\&\n  Data} → \\emph{Kaleo Forms Admin}. The Kaleo Forms app appears with a\n  list of any defined processes.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  to open the New Process Wizard.\n\\item\n  Give the process a name and a description and click \\emph{Next}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/kaleo-forms-add.png}\n  \\caption{Add a Kaleo Forms Process to link a form with a workflow\n  definition.}\n  \\end{figure}\n\\item\n  Define the fields that can appear in your process's forms. There are\n  two ways to do this:\n\n  \\begin{itemize}\n  \\item\n    Use an existing field set. Click the field set's Actions button\n    (\\includegraphics{./images/icon-actions.png}) and select\n    \\emph{Choose}.\n  \\item\n    Create a new field set/data definition. Click the \\emph{Add Field\n    Set} button. If you need help with this, see the documentation on\n    \\href{/docs/7-2/user/-/knowledge_base/u/creating-data-definitions}{creating\n    data definitions}. After creating the field set, select it as you\n    would an existing field set.\n  \\end{itemize}\n\n  Click \\emph{Next} to move to the wizard's next step.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/kaleo-forms-fields.png}\n  \\caption{Define and choose your form's fields.}\n  \\end{figure}\n\\item\n  Select a workflow to use for your forms. To do this, click the\n  workflow's \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and select\n  \\emph{Choose}.\n\n  You can also edit an existing workflow or create a new one:\n\n  \\begin{itemize}\n  \\item\n    To edit a workflow, click its \\emph{Actions} button\n    (\\includegraphics{./images/icon-actions.png}) and select\n    \\emph{Edit}.\n  \\item\n    To begin creating a new workflow, click the \\emph{Add Workflow}\n    button.\n  \\end{itemize}\n\n  In either case, you use the same UI to edit/create the workflow. This\n  UI is called\n  \\href{/docs/7-2/user/-/knowledge_base/u/workflow-designer}{Workflow\n  Designer}. It lets you create your workflow graphically instead of via\n  code.\n\n  Once you select a workflow to use with your forms, click \\emph{Next}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/kaleo-forms-spa-order-definition.png}\n  \\caption{This example workflow has three tasks that happen\n  sequentially.}\n  \\end{figure}\n\\item\n  Select or create a form to use for each workflow task. To do this,\n  click each task's \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Assign\n  Form}. On the screen that appears, select an existing form or click\n  the \\emph{Add} button (\\includegraphics{./images/icon-add.png}) and to\n  create one.\n\n  Click \\emph{Save} when you're finished. Your process is done and\n  appears in Kaleo Forms Admin's table.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/kaleo-forms-task-forms.png}\n  \\caption{Assign a form to each task in the workflow, and for the\n  initial state.}\n  \\end{figure}\n\\end{enumerate}\n\n\\section{Adding Records to a Process}\\label{adding-records-to-a-process}\n\nTo add records to a process, click it in Kaleo Forms Admin and then\nclick the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\nThis brings up the form you assigned to the workflow's initial state.\nFill it out and click \\emph{Save}. Once submitting the initial form, the\nworkflow engine then takes over and moves through each task in the\nworkflow. Whatever Users or Roles you assigned to the tasks receive\nnotifications, and the task appears in the \\emph{Assigned to My Roles}\nsection of the My Workflow Tasks app. A notification also appears in the\nNotifications app. Once in the task, the user views and approves the\nform or clicks the \\emph{Edit} button. At this point, the workflow task\nforms you created come into play. Each assigned user fills out the form,\nsaves it, and sends it along in the workflow.\n\n\\chapter{Segmentation and\nPersonalization}\\label{segmentation-and-personalization}\n\nLiferay's Segmentation and Personalization shows the right content to\nthe right people at the right time. It provides the tools you need to\nmanage different audiences and dynamically provide personalized\nexperiences for people using your site. For example, if you're creating\na campaign to promote new financial service products, you need a way to\ndisplay offers to customers who are likely to be interested in those\noffers. You don't want to display information on a basic free checking\naccount for an ``advanced'' customer who carries a high balance across\nseveral types of accounts, but you do want to show that information to a\nvisitor who entered the site through a landing page from a promotion at\na local college. At the same time, you probably don't want to recommend\noptions for optimizing retirement account contributions to the college\nstudent, but the other customer might be a great target for that\ncampaign. By using data like user attributes or visitor interactions,\nyou can dynamically target relevant content to your site's guests.\n\n\\section{Defining Segments}\\label{defining-segments}\n\nThe first part of the equation is defining the types of segments that\nyou need. You can create Segments to capture every case. Segments are\ncomposed of different criteria. In the previous example you might have a\nsegment for \\emph{Free Checking Account Prospects} that contains\ncriteria based on user data, like customers that don't currently have an\nopen checking account; or based on user behavior, like visitors who came\nto the site through specific channels. To learn more about Segmentation\noptions, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/the-segment-editor}{overview of\nthe Segment editor}, practice\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-user-segments}{creating\nbasic Segments}, or create more complicated\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-segments-with-custom-fields-and-session-data}{Segments\nwith custom fields and session data}.\n\n\\section{Integrating Segments with Analytics\nCloud}\\label{integrating-segments-with-analytics-cloud}\n\nThere are two different stories that User Segments can tell. One is\ntargeting content to specific audiences that encourages engagement and\npositive user experiences. The other is defining groups of users and\nvisitors to analyze their behavior and interactions with your site. To\ntell the second story, you must integrate with Analytics Cloud.\n\nAnalytics Cloud is a Liferay service that provides in-depth information\non who uses your site and how they use it. Analytics Cloud is a key\ncomponent to fully utilizing Segments and Personalization, since it\nenables you to see the full picture of how users and visitors on your\nsite behave and interact with both standard and targeted content. You\ncan learn more about this in\n\\href{https://help.liferay.com/hc/en-us/articles/360029041751-Using-Analytics-Cloud-With-User-Segments}{Using\nAnalytics Cloud with User Segments}.\n\n\\section{Personalizing Experiences}\\label{personalizing-experiences}\n\nThe most important piece of the puzzle isn't defining groups or\nanalyzing user behavior. It's the final step of using the data to\nprovide users and site visitors with the best possible experience, and\ndriving campaigns and content engagement. If you strategically create\nsegments, you can then use that to enhance user experiences, and make\nsure that users see content targeted to them. Content Page\nPersonalization and Content List Personalization are two key aspects of\nthis.\n\n\\section{Content Page\nPersonalization}\\label{content-page-personalization}\n\nContent Page Personalization dynamically changes the page layout and\ncontent based on who is viewing the page. You can create\n\\emph{Experiences} for any\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-pages}{Content\nPage} which provide different text, images, widgets, and even different\nlayouts based on the segment criteria of the user viewing the page. You\ncan see a step by step demonstration of this in\n\\href{/docs/7-2/user/-/knowledge_base/u/content-page-personalization}{Content\nPage Personalization}.\n\n\\section{Content Set\nPersonalization}\\label{content-set-personalization-1}\n\n\\href{/docs/7-2/user/-/knowledge_base/u/content-sets}{Content Sets}\norganize and display content. Content Set Personalization provides\ndynamic selection of Content Sets based on User Segments. This means the\nContent Set which displays in a given context is determined by their\nsegment criteria. For example, you could use a content list to display\n``featured'' articles at the top of a page. Then you could create\nSegments containing users who should receive more specialized content,\nrather than the default. Those Segments would then see content\npersonalized to their interest rather than the default. You can see a\nstep by step demonstration of this in\n\\href{/docs/7-2/user/-/knowledge_base/u/content-set-personalization}{Content\nSet Personalization}.\n\n\\chapter{The Segment Editor}\\label{the-segment-editor}\n\nSegmentation and Personalization in 7.0 also provides an editor for\ndefining User Segments.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} and select the site that you wish to\n  create segments for.\n\\item\n  Click \\emph{People} → \\emph{Segments}.\n\\item\n  Click the \\emph{Add User Segment} button\n  (\\includegraphics{./images/icon-add.png}).\n\\end{enumerate}\n\nAt the top of the editor you can set the name, view the current members\nof the segment as it is defined, and choose to \\emph{Save} the Segment\nor \\emph{Cancel} to discard changes.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sp-editor-top.png}\n\\caption{The top portion of the Segment Editor has the segment name and\nits members.}\n\\end{figure}\n\nOn the right side of the page, there's a Properties menu with the\nfollowing options:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  User\n\\item\n  Organization\n\\item\n  Session\n\\end{itemize}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sp-segment-editor-full.png}\n\\caption{You use the Segment Editor to create new Segments.}\n\\end{figure}\n\nIn addition to the various properties, there are operations and\nconjunctions that you use to define criteria.\n\n\\section{User Properties}\\label{user-properties}\n\nUser Properties are user attributes you want to capture. This is made up\nof user metadata as defined in their accounts, but also contains certain\ngroup memberships (like Roles and User Groups) as well as information\nlike the date the user profile was last modified.\n\n\\section{Organization Properties}\\label{organization-properties}\n\nOrganization Properties is a selectable list of Organizations to include\nin your Segment. They contain similar criteria as the User selection,\nlike \\emph{Name} and \\emph{Date Modified}.\n\n\\section{Session Properties}\\label{session-properties}\n\nSession Properties contains criteria based on the user's activity,\nbrowser, and system information. You can use this to target the user's\ndevice or OS, or for activity-based criteria like if a user entered the\nwebsite through a specific campaign driven landing page.\n\n\\section{Operations and Conjunctions}\\label{operations-and-conjunctions}\n\nThere are a number of different ways to evaluate properties, and\ndifferent ways that you can relate different fields. For a comprehensive\nlist, see the\n\\href{/docs/7-2/reference/-/knowledge_base/r/defining-segmentation-criteria}{Defining\nSegment Criteria Reference}.\n\n\\chapter{Creating User Segments}\\label{creating-user-segments}\n\nTo learn how to use segmentation, you'll step through an example for\ndefining two segments for a site. The segments use user data like the\n\\emph{Job Title} field and organization membership for evaluating\nsegments. The first Segment you'll create, \\emph{American Engineers},\nuses standard fields as criteria.\n\nTo get started, navigate to the Segments page.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} for the Bank site.\n\\item\n  Open the \\emph{People} section and select \\emph{Segments}.\n\\end{enumerate}\n\n\\section{Creating a Custom Segment}\\label{creating-a-custom-segment}\n\nOn the Segments page, you'll see a list of all the currently available\nsegments, if they're available. Create a segment named \\emph{American\nEngineers} for employees of your company.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  button.\n\\item\n  Click in the top text area and enter the name \\emph{American\n  Engineers}.\n\\item\n  Open the \\emph{User} tab in the right side \\emph{Properties} menu and\n  drag the \\emph{Job Title} property to the \\emph{Conditions} area.\n\\item\n  Click on the operator field and set it to \\emph{contains}.\n\\item\n  Click on the text field and enter \\emph{Engineer}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sp-set-date.png}\n  \\caption{Setting the comparator to \\emph{contains} includes variations\n  of ``Engineer'' like ``Software Engineer'' in the segment.}\n  \\end{figure}\n\\item\n  Open the \\emph{Organization} properties and drag over an\n  \\emph{Organization} field.\n\\item\n  Set the comparator between \\emph{User Properties} and\n  \\emph{Organization Properties} to \\emph{And}.\n\\item\n  Set the \\emph{Organization} field to be \\emph{equals} and select the\n  organization.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/sp-select-orgs.png}\n  \\caption{You can prevent typos by directly selecting Organizations\n  through the interface.}\n  \\end{figure}\n\\item\n  Click \\emph{Save} to save your Segment.\n\\end{enumerate}\n\nAs you edit, a count of members meeting the criteria appears at the top\nof the page. You can click on \\emph{View Members} to see the list. This\nhelps you determine if you are correctly defining the Segment.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sp-segment-members.png}\n\\caption{You can view the list of Segment members at any time.}\n\\end{figure}\n\n\\section{Managing Segments}\\label{managing-segments}\n\nAfter you create your Segment, you can see it in the list of Segments on\nthe \\emph{People} → \\emph{Segments} page. From there you can edit the\nsegment, delete it, or change the permissions for it. You cannot delete\na segment if it's being used in an experience. Also, changing\npermissions only affects who has access to manage the Segment; it\ndoesn't change Segment membership or criteria.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{People} → \\emph{Segments}.\n\\item\n  Click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) next to the\n  \\emph{American Engineers}.\n\\end{enumerate}\n\nYou can manage the options here. You can also click on the Segment's\nname to edit it.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sp-options.png}\n\\caption{You can edit, delete or manage permissions from the options\nmenu.}\n\\end{figure}\n\nNext, you'll define a Custom Field and use it as part of your\nSegmentation criteria.\n\n\\chapter{Creating Segments with Custom Fields and Session\nData}\\label{creating-segments-with-custom-fields-and-session-data}\n\nNow that you created a segment, you can take things to the next level\nand use a Custom Field to define segment criteria.\n\n\\section{Creating a Custom Field}\\label{creating-a-custom-field}\n\nFirst, create a custom field to use for the Segment:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the \\emph{Control Panel} go to \\emph{Configuration} → \\emph{Custom\n  Fields}.\n\\item\n  Click on \\emph{User}.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  button in the top right.\n\\item\n  On the next page, click on \\emph{Dropdown}.\n\\item\n  For the \\emph{Field Name} enter \\emph{Cardholder Type}.\n\\item\n  For values, enter \\emph{None,} \\emph{Basic}, \\emph{Gold}, and\n  \\emph{Platinum} on four separate lines.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nNow any time user is created, they are prompted to enter the\n\\emph{Cardholder Type}, and existing users can select it from their user\nprofiles.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sp-create-custom-field.png}\n\\caption{You can easily create custom fields to capture whatever kind of\ndata you need.}\n\\end{figure}\n\nFor more information on adding Custom Fields, see\n\\href{/docs/7-2/user/-/knowledge_base/u/custom-fields\\#adding-custom-fields}{Adding\nCustom Fields}.\n\n\\section{Defining a Segment with a Custom\nField}\\label{defining-a-segment-with-a-custom-field}\n\nNext, use a custom field to define another segment.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From Segments Administration, click the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}).\n\\item\n  Click in the text area at the top of the page, enter the name\n  \\emph{Premium Card Prospects}.\n\\item\n  For User Properties select \\emph{Cardholder Type}.\n\\item\n  Click on the operator field and set it to \\emph{equals}.\n\\item\n  Select \\emph{Basic} from the select box.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/sp-select-custom-field.png}\n\\caption{The custom field you created is seamlessly integrated into\nsegment creation.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{8}\n\\tightlist\n\\item\n  Click \\emph{Save} to save your Segment.\n\\end{enumerate}\n\nAs you can see, segment criteria can be easily defined using Liferay\nDXP's built-in criteria or your own custom fields. Now, you'll use\nsession data to make this Segment definition even more robust.\n\n\\section{Extending a Segment With Session\nData}\\label{extending-a-segment-with-session-data}\n\nSo far, you've used criteria derived from user profiles to determine if\nthey should be members of a segment; now it's time to use session data\nto make your criteria more effective.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} For this exercise to work, you must have set a cookie on\nthe specified page.\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) next to the\n  \\emph{Premium Card Prospects} segment and select \\emph{Edit}.\n\\item\n  Click the \\emph{Session} dropdown from the Properties menu.\n\\item\n  Set the comparator for Session Properties to \\emph{Or}.\n\\item\n  Drag \\emph{Cookies} into the Session Properties section.\n\\item\n  Change the selection box to \\emph{contains}.\n\\item\n  Enter \\emph{Cards} in the Key text box.\n\\end{enumerate}\n\nNow any user who has a cookie from visiting the \\emph{Cards} page is\nevaluated as part of the \\emph{Premium Card Prospects} segment.\n\n\\chapter{Using Analytics Cloud With User\nSegments}\\label{using-analytics-cloud-with-user-segments}\n\nTo use Analytics Cloud with User Segments, you must first connect your\nDXP data source to Analytics Cloud and enable synchronization of users\nand analytics. For more information about Analytics Cloud, including\ninstructions for connecting it with DXP, see the official\n\\href{https://learn.liferay.com/analytics-cloud/latest/en/connecting-data-sources/connecting-liferay-dxp-to-analytics-cloud.html}{Analytics\nCloud Documentation}.\n\nOnce you're connected to Analytics Cloud, you can create Segments to\nanalyze user behavior.\n\n\\section{Creating a New Segment}\\label{creating-a-new-segment}\n\nSynchronization with Analytics Cloud is not instant, so once you have\nconnected Analytics Cloud and Liferay DXP, you must first wait for the\nusers and data to synchronize. After that completes, you can create\nSegments in Analytics Cloud to capture data in DXP.\n\nOnly Segments that contain at least one member are synchronized with\nLiferay DXP. This means that empty Segments created with Analytics Cloud\nare unavailable to use on Liferay DXP.\n\nSee Analytics Cloud's documentation on\n\\href{https://help.liferay.com/hc/en-us/articles/360028721412-Creating-User-Segments}{creating\nSegments} for more information.\n\nNext, you can use your new Segment to define behaviors on your server.\n\n\\section{Getting Segment Analytics}\\label{getting-segment-analytics}\n\nAfter you create and sync a Segment in Analytics Cloud, you can view it\nand customize it in Liferay DXP.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the \\emph{Segments} page.\n\\item\n  Click on the new Segment.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/segments-ac-list-item.png}\n\\caption{When you see Analytics Cloud Segments in the list of Segments,\nthey are marked with the Analytics Cloud icon.}\n\\end{figure}\n\nAnalytics are based on the criteria that you set on Analytics Cloud, but\nyou can set additional criteria here to use this Segment for\npersonalization in DXP. Changing the Segment criteria here doesn't\naffect the gathered analytics data, unless it is configured in some way\nthat restricts its members from viewing content that you are using as an\nAnalytics Cloud criteria.\n\nWhen you put it all together to provide personalized experiences and\nanalyze user behavior, you can see the true power of Segmentation.\n\n\\chapter{Personalization Experience\nManagement}\\label{personalization-experience-management}\n\nThere is no direct location for managing all aspects of Experience\nPersonalization. All the different aspects are managed from whatever\nyou're personalizing. The key integration points of personalization are\nContent Pages and Content Sets. You can see an overview of their\npersonalization options below.\n\n\\section{Managing Content Page\nPersonalization}\\label{managing-content-page-personalization}\n\nBefore you can Personalize Content Pages, you first need to\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-pages}{create\nContent Pages}. When you edit a Content Page, you can click on the\n\\emph{Experience} to manage the options for that page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/manage-content-page-experience.png}\n\\caption{You can add, edit, delete, or change priority for Experiences.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Site Builder} → \\emph{Pages}.\n\\item\n  Click the \\emph{Actions} button\n  \\includegraphics{./images/icon-actions.png} → \\emph{Edit} for any\n  Content Page.\n\\item\n  Click on the \\emph{Default} Experience to manage experiences.\n\\end{enumerate}\n\nFrom here you have three options:\n\n\\textbf{\\includegraphics{./images/icon-edit.png}} changes the name or\nselected Segment for the Experience.\n\n\\textbf{\\includegraphics{./images/icon-delete.png}} deletes the\nExperience.\n\n\\textbf{\\includegraphics{./images/icon-priority.png}} changes the\npriority of the Experience. If a user meets the criteria for more than\none Experience, the highest ordered one is displayed.\n\nWhen creating a new Experience, you must define the audience by choosing\na Segment. If your target audience for the Experience is not yet\nrepresented by a Segment, you can create one from the New Experience\ninterface.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/add-seg-from-exp.png}\n\\caption{You can add a new Segment while creating a new Experience.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Creating new Segments from the New Experience interface\nis available in Liferay DXP 7.2 Fix Pack 1+ and Liferay Portal GA2+.\n\n\\noindent\\hrulefill\n\nNext you'll learn about managing Content Set personalization.\n\n\\section{Managing Content Set\nPersonalization}\\label{managing-content-set-personalization}\n\nManaging Personalization options for Content Sets is similar. First you\nmust \\href{/docs/7-2/user/-/knowledge_base/u/content-sets}{create\nContent Sets} then you can personalize them. To create a new\nPersonalized Variation of a Content Set,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Actions} button\n  \\includegraphics{./images/icon-actions.png} → \\emph{Edit} for the\n  Content Set you wish to personalize.\n\\item\n  Next to the content set is the message \\emph{No Personalized\n  Variations yet}. Click the \\emph{New Personalized Variation} button.\n\\item\n  Select a Segment from the next page to create a variation for that\n  Segment.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/select-content-set-variation.png}\n\\caption{Select a Segment to create a variation for.}\n\\end{figure}\n\nYou can create a personalized variation for any existing Segment. Each\nnew variation copies the default Content Set, but then essentially\nfunctions as its own Content Set after that. To edit or manage a Content\nSet Variation,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on the Segment name under \\emph{Personalized Variations}.\n\\item\n  Click on the \\emph{Actions}8 button\n  (\\includegraphics{./images/icon-actions.png}) and you can select\n  \\emph{View Content} to preview the content in that set or\n  \\emph{Delete} to remove it.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/manage-content-set-segments.png}\n\\caption{You can preview or delete a Personalized Variation from the\n\\emph{Actions} menu.}\n\\end{figure}\n\n\\section{Previewing User Experiences}\\label{previewing-user-experiences}\n\nAs an administrator, when you view a page, you can preview the different\nexperiences that users can have on that page.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Simulation} button\n  (\\includegraphics{./images/icon-simulation.png}) icon from the top of\n  any page.\n\\item\n  Select a segment from the \\emph{Segments} selection to preview the\n  page as a member of that segment.\n\\end{enumerate}\n\nViewing the perspective of a segment previews for the administrator any\npersonalizations for Content Pages or Content Sets for that Segment.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/personalization-segment-preview.png}\n\\caption{You can preview different experiences from the Preview Panel.}\n\\end{figure}\n\n\\chapter{Content Page\nPersonalization}\\label{content-page-personalization-1}\n\nIn\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-segments-with-custom-fields-and-session-data}{Creating\nSegments with Customer Fields and Session Data} you created a Segment\ncalled \\emph{Premium Card Prospects}. Now, you'll use it to demonstrate\nContent Page Personalization.\n\nIf you're not familiar with Content Pages, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-pages}{Creating\nContent Pages} article before you get started here.\n\n\\section{Creating the Default Page}\\label{creating-the-default-page}\n\nFirst, you need to create the \\emph{Credit Cards} page. To do this,\ncreate a new content page, add some fragments to it, and edit the\ncontent to match. First you need to build the bones of the page:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Site Builder} → \\emph{Pages}.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png}).\n\\item\n  Select \\emph{Content Page} and name the page \\emph{Credit Cards}.\n\\item\n  Open the \\emph{Sections} → \\emph{Basic Sections} from the build menu\n  on the right side of the screen and add a \\emph{Banner} to the page.\n\\item\n  Next, click on \\emph{Section Builder}, open \\emph{Layouts}, and add a\n  three column layout to the page, above the banner.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/section-builder-layouts.png}\n\\caption{Open Layouts from the Section Builder.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{5}\n\\tightlist\n\\item\n  Open the \\emph{Basic Components} tab and add a \\emph{Card} inside each\n  of the columns.\n\\end{enumerate}\n\nNext, edit the content (this step is optional):\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on the text for the cards and the banner to edit it and provide\n  content relevant to someone looking for information about credit\n  cards.\n\\item\n  Click on each image and provide an appropriate image.\n\\item\n  Click \\emph{Publish}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/personalization-default-content.png}\n\\caption{Your final result might look something like this.}\n\\end{figure}\n\nThis is the default page that anyone visiting the site sees.\n\n\\section{Defining Custom Experiences}\\label{defining-custom-experiences}\n\nNext, define an experience specifically tailored to customers whom you\nhave identified as \\emph{Premium Card Prospects} using User Segments.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  At the top of the page, for the \\emph{Experience} click on\n  \\emph{Default} to open the experience selection dialog.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/select-experience.png}\n  \\caption{Click on the current experience to create a new one or select\n  a different existing experience.}\n  \\end{figure}\n\\item\n  Click on \\emph{New Experience}.\n\\item\n  Name it \\emph{Card Prospects} and select \\emph{Premium Card Prospects}\n  for the \\emph{Audience}.\n\\item\n  Add a \\emph{Banner} fragment above the three columns you added\n  earlier.\n\\item\n  Edit the Banner to provide information specifically related to\n  upgrading a card for an existing customer.\n\\item\n  Click \\emph{Publish}.\n\\end{enumerate}\n\nThe \\emph{Default} version of the page appears for everyone except for\nthose defined as \\emph{Premium Card Prospects}, but customers in that\nsegment have an experience curated just for them.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/personalization-prospects.png}\n\\caption{Your final result for the card prospects might look something\nlike this.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When you create a new experience, it copies the\n\\emph{Default} experience at the time that it is created. Any further\nchanges to the \\emph{Default} experience do not effect any of\nexperiences for that page.\n\n\\chapter{Content Set\nPersonalization}\\label{content-set-personalization-2}\n\nIn\n\\href{/docs/user/7-2/-/knowledge-base/u/creating-user-segments}{Creating\nUser Segments} you created a Segment called \\emph{American Engineers}.\nNow, you'll use it to demonstrate Content Set Personalization. For this\nexample, create a Content Set to be the default displayed on the\n\\emph{Home} page. Then you'll modify it to create a personalized\nvariation containing technical articles for members of the\n\\emph{American Engineers} segment.\n\nIf you're not familiar with Content Set, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-content-sets}{Creating\nContent Sets} article before you get started here.\n\n\\section{Creating and Setting the Default Content\nSet}\\label{creating-and-setting-the-default-content-set}\n\nFirst create the default Content Set and configure it on the Home page\nusing the Asset Publisher.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\emph{Site Administration} → \\emph{Content \\& Data} →\n  \\emph{Content Sets}.\n\\item\n  Click the Add button (\\includegraphics{./images/icon-add.png}) and\n  choose \\emph{Manual Selection}.\n\\item\n  Name it \\emph{Home Page Content}.\n\\item\n  For the new Content Set, click \\emph{Select} next to \\emph{Asset\n  Entries} and select \\emph{Basic Web Content}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/create-default-content-set.png}\n  \\caption{Click \\emph{Select} to add a new Asset Entries.}\n  \\end{figure}\n\\item\n  On the \\emph{Select Basic Web Content} page, check the boxes next to\n  the content you want to add and click \\emph{Add}.\n\\item\n  Navigate to the \\emph{Home} page and add an Asset Publisher to the\n  page.\n\\item\n  Open \\emph{Configuration} for the Asset Publisher.\n\\item\n  Under \\emph{Asset Selection} select \\emph{Content Set}.\n\\item\n  Under \\emph{Select Content Set} click \\emph{Select}, choose \\emph{Home\n  Page Content}, and click \\emph{Save}.\n\\end{enumerate}\n\nNow the Content Set that you configured appears in the Asset Publisher\non the \\emph{Home Page}. Next configure the Content Set for\nPersonalization.\n\n\\section{Personalizing the Content\nSet}\\label{personalizing-the-content-set}\n\nNow create the content set for engineers and configure its display.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go back to the Content Set from \\emph{Site Administration}.\n\\item\n  Click \\emph{New Personalized Variation} and select the \\emph{American\n  Engineers} segment\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/create-personalized-variation.png}\n  \\caption{Create a new Personalized Variation.}\n  \\end{figure}\n\\item\n  Click \\emph{Select} next to \\emph{Asset Entries} and select\n  \\emph{Basic Web Content}.\n\\item\n  Select articles appropriate to an engineering audience and click\n  \\emph{Add}.\n\\end{enumerate}\n\nNow anytime a member of the \\emph{American Engineers} segment views this\nContent Set being displayed, they see the personalized version and not\nthe default. Test this now, using the \\emph{Simulator}.\n\n\\chapter{Recommending Content Based on User\nBehavior}\\label{recommending-content-based-on-user-behavior}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} A/B Testing is available for Liferay DXP 7.2 SP1+.\n\n\\noindent\\hrulefill\n\nA site's content generates clicks from users. For example, if someone\nvisits a sporting goods store's site and clicks on several hunting\npromotional ads, you can deduce an interest in hunting products and can\npromote this type of content when this user visits the site again.\n\nAccomplishing this with\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-user-segments}{Segment-based\npersonalization} is possible, but that method is really targeted for\nvertical specific messaging or content with a preconceived audience. You\nmay have to create hundreds of Segments to target all combinations of\ncustomer use cases. Instead, you need an infrastructure that tracks user\nviews and displays the appropriate content based on behavior. You can\naccomplish this with \\emph{Content Recommendation}.\n\nThis is done by adding tags to content/Pages and monitoring the users\nwho visit them. When a user views a specific content type or Page, its\ntags are attached to that user as \\emph{interests}. When the user visits\nother pages, content that matches their interests is displayed to them.\nThe monitoring process is facilitated by\n\\href{https://help.liferay.com/hc/en-us/articles/360006608732-Generating-New-Business-Using-Analytics}{Analytics\nCloud}, so you must have your DXP instance synced with it. If you\nhaven't done this yet, start by\n\\href{https://help.liferay.com/hc/en-us/articles/360006653472-Adding-a-Liferay-DXP-Data-Source}{adding\nyour DXP instance as a data source}.\n\nOnce your DXP instance is synced with Analytics Cloud and you're\nleveraging Content Recommendation, a user's interests are viewable by\nnavigating to the left menu → \\emph{Individuals} → \\emph{Interests}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/content-interests.png}\n\\caption{A user's interests are stored and accessible from Analytics\nCloud.}\n\\end{figure}\n\nYou can learn more about Analytics Cloud's individual analytics\n\\href{https://help.liferay.com/hc/en-us/articles/360006946171-Profiling-Individuals}{here}.\n\nTo begin recommending content to users, you must\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Add tags to content and/or Pages.\n\\item\n  Display content based on user behavior.\n\\end{itemize}\n\nYou'll step through these processes next.\n\n\\section{Adding Tags to Track User\nBehavior}\\label{adding-tags-to-track-user-behavior}\n\nTo track user behavior and accumulate their interests, you must add tags\nto the content and Pages they visit. First, you'll add tags to web\ncontent and configure it to be viewable using a Display Page Template.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to Site Administration → \\emph{Site Builder} → \\emph{Pages} →\n  \\emph{Display Page Templates}. Select the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}), give it a name, and click\n  \\emph{Save}.\n\\item\n  In the right menu, select \\emph{Mapping}\n  (\\includegraphics{./images/icon-mapping.png}), choose the \\emph{Web\n  Content Article} content type and choose \\emph{Basic Web Content} for\n  the subtype. Then click \\emph{Save}.\n\\item\n  Add a Fragment to the Display Page and map its field to \\emph{Basic\n  Web Content}. For example, click \\emph{Section Builder} → \\emph{Basic\n  Components} and drag the \\emph{Paragraph} Fragment to the page. Then\n  click the \\emph{Map} button and select \\emph{Basic Web Content} for\n  its Source and Field.\n\\item\n  Publish the Display Page Template.\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/creating-web-content}{Begin\n  creating Basic Web Content}. Before publishing the content, navigate\n  to the \\emph{Display Page Template} section and select \\emph{Specific\n  Display Page Template} from the selector. Then select the Display Page\n  Template your created previously.\n\\item\n  Go to the \\emph{Metadata} section in the right menu. Assign tags that\n  characterize the content. These are the tags that are referenced as\n  interests when a user views the content. Then click \\emph{Publish}.\n\\end{enumerate}\n\nNow your web content is mapped to a Display Page, which allows the\nassigned tags to be tracked as interests when the web content is\nclicked. You can alter this process based on the asset types you want to\nrecommend.\n\nYou can also assign tags to a Page's SEO configuration, which would then\nbe assigned to users as interests when they visit the Page. Here's how\nto do this:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to Site Administration → \\emph{Site Builder} → \\emph{Pages}.\n\\item\n  Click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) → \\emph{Configure} for a\n  Page you want to add tags to.\n\\item\n  Select \\emph{SEO} → \\emph{Categorization} and add relevant tags to the\n  page. Then click \\emph{Save}.\n\\end{enumerate}\n\nAwesome! Now your content and Pages have tags that are assigned to users\nas interests when they visit them. These interests are assessed when\nrecommending content, which you'll learn how to leverage next.\n\n\\section{Displaying Content Based on User\nBehavior}\\label{displaying-content-based-on-user-behavior}\n\nNow that your Site's users' have their interests tracked using tags,\nyou'll want to set up an Asset Publisher to display the content based on\ntheir behavior.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Site Administration → \\emph{Content \\& Data} →\n  \\emph{Content Sets}.\n\\item\n  Select the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  and click \\emph{Dynamic Selection}. Assign a name and click\n  \\emph{Save}.\n\\item\n  Under the Content Recommendation tab, enable Content Recommendation.\n  Then save the Content Set.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/enable-content-recommendation.png}\n  \\caption{Enable Content Recommendation for your Content Set.}\n  \\end{figure}\n\n  For more information on Content Sets, see\n  \\href{/docs/7-2/user/-/knowledge_base/u/creating-content-sets}{Creating\n  Content Sets}.\n\\item\n  Add an Asset Publisher widget to a Page. Navigate to its\n  \\emph{Options} (\\includegraphics{./images/icon-app-options.png}) →\n  \\emph{Configuration} menu and select the \\emph{Content Set} asset\n  selection.\n\\item\n  Select the Content Set you want to display. Then click \\emph{Save}.\n\\end{enumerate}\n\nIn a realistic scenario, Content Sets have many assets with differing\ntags. That way, content similar to a user's interests is displayed over\nother content.\n\nGreat! Now when users have accumulated interests based on views, the\nAsset Publisher only shows content based on their interests.\n\n\\chapter{A/B Testing}\\label{ab-testing}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} A/B Testing is available for Liferay DXP 7.2 SP1+.\n\n\\noindent\\hrulefill\n\nLiferay DXP can help you hone your messages to customers by periodically\ntesting different UX and messaging schemes. You can show different\nversions of your site to different users to see which is more effective.\n\n\\emph{A/B Testing} lets you maintain a page's current UX and messaging,\nbut provide alternative page variants for a select group of visitors.\nThen the current page and page variant(s) are tested based on algorithms\nto determine which pages perform better for a given goal (e.g., bounce\nrate, clicks, etc.).\n\nFor example, a Marketing team for a bank provides a Content Page\nadvertising a new credit card. The page has been published for a few\nweeks, but a redesign might help promote the new credit card better.\nWith A/B Testing, the team can create a new page variant and display\nboth pages at random to visitors. Then they can analyze the clickthrough\nrate for the two pages and find which page is more effective. If the new\nvariant is more effective than the original page, they can publish it\nand remove the old page.\n\nIf improving your site's UX and messaging is something you're interested\nin, continue on to learn more!\n\n\\chapter{Enabling A/B Testing}\\label{enabling-ab-testing}\n\nBefore creating an A/B test, you must ensure some conditions are met:\n\n\\begin{itemize}\n\\item\n  You must have Liferay DXP connected to\n  \\href{https://learn.liferay.com/analytics-cloud/latest/en/index.html}{Analytics\n  Cloud}. To begin,\n  \\href{https://learn.liferay.com/analytics-cloud/latest/en/connecting-data-sources.html}{add\n  a Data Source}.\n\\item\n  Your page must be a Content Page, since only Content Pages (not Widget\n  Pages) support Experiences for different Segments.\n\\item\n  The Content Page you intend to test must be published.\n\\end{itemize}\n\nIf these conditions are met, A/B Testing is automatically enabled for\nany Content Page you navigate to, assuming you have the proper\npermissions. You'll learn more about how to assign permissions for A/B\nTesting next.\n\n\\section{Setting A/B Testing\nPermissions}\\label{setting-ab-testing-permissions}\n\nTo use all the features of A/B Testing, you must have \\emph{Update}\npermissions for the Content Page.\n\nTo assign \\emph{Update} permissions for a Content Page,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to Site Administration → \\emph{Site Builder} → \\emph{Pages}.\n\\item\n  Select the Actions button\n  (\\includegraphics{./images/icon-actions.png}) next to the Content Page\n  and click \\emph{Permissions}.\n\\item\n  Enable the \\emph{Update} permission for the Role(s) you want to grant\n  A/B Testing access for and click \\emph{Save}.\n\\end{enumerate}\n\nFor more information, see\n\\href{/docs/7-2/user/-/knowledge_base/u/changing-page-permissions}{Page\nPermissions} and\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-roles}{Managing Roles}.\n\n\\chapter{Creating A/B Tests}\\label{creating-ab-tests}\n\nTo begin leveraging A/B Testing, you must first create an A/B test. You\ncannot create a test on an Experience that already has an active test\nrunning.\n\nTo create an A/B test,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Content Page you want to test.\n\\item\n  Click the A/B Testing (\\includegraphics{./images/icon-ab-testing.png})\n  button from the Control Menu.\n\\item\n  Choose the Experience you want to test. This option is only available\n  when you have a custom Experience (an Experience other than the\n  default).\n\n  A test can be performed on the Default Experience as well as a\n  personalized Experience mapped to a Segment. When an Experience is\n  being used in an A/B test, it cannot be edited. Deleting a\n  Page/Experience being used in an A/B test also deletes the test for\n  that Page.\n\\item\n  Click \\emph{Create Test}.\n\\item\n  Assign the test a name and description (optional).\n\\item\n  Assign the goal you want the test to track. There are two:\n\n  \\emph{Bounce Rate}: the percentage of users who don't exhibit any\n  activities on the page (click, scroll, etc.) and then navigate away\n  from the site without visiting another page.\n\n  \\emph{Click}: the percentage of users who clicked on the page (per\n  session).\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/create-ab-test.png}\n\\caption{Fill out the form to create your A/B test.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{6}\n\\tightlist\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nYou now have an A/B test! Notice that the test's status is \\emph{Draft}.\nThis means it's not yet visible to visitors.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can only create an A/B test for one page/experience\nat a time.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The \\emph{Control} entity represents the currently\npublished Content Page.\n\n\\noindent\\hrulefill\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/new-ab-test.png}\n\\caption{You now have an A/B test, but there are additional\nconfigurations you can apply.}\n\\end{figure}\n\nYou can always edit or delete the new A/B test by clicking the\n\\emph{Actions} button (\\includegraphics{./images/icon-actions.png}) in\nthe top right of the A/B Test menu. Deleted tests are not recoverable\n(i.e., not sent to the Recycle Bin). These options are not available for\nan active test. You can also, at any time, view your A/B Testing history\nby selecting the \\emph{History} tab. This displays all completed and\nterminated A/B tests.\n\nNow it's time to create your test Variant(s). A test Variant is a\ncustomization of the Experience you want to optimize. An A/B test must\ncontain at least one Variant before it can run.\n\nTo create a Variant,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the A/B Test menu, click \\emph{Create Variant}.\n\\item\n  Give the Variant a name and click \\emph{Save}.\n\\item\n  Select the new Variant's Edit button\n  \\includegraphics{./images/icon-edit.png}. The current Control page's\n  content/formatting is copied and displayed as the baseline for the\n  Variant.\n\\item\n  Edit the Variant as desired. Then click \\emph{Save Variant}.\n\\end{enumerate}\n\nYou now have a Variant of the Control Page. You can create as many\nVariants as you want.\n\nIf you selected the Click goal, you must select the clickable element\nyou want to target on the Control and Variant pages. If you selected a\ndifferent goal, you can skip the steps below.\n\nTo configure the Click goal target,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\tightlist\n\\item\n  Click \\emph{Set Target} under the Click Goal heading of your A/B Test.\n  Any clickable element on the page is highlighted.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Only links and buttons with an ID attribute can be selected as a\n target for the Click goal.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{1}\n\\item\n  Select the element you want to set as the click target for your\n  Control and Variant pages.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/set-click-target.png}\n  \\caption{Set the click target to be tracked.}\n  \\end{figure}\n\\end{enumerate}\n\nYour Click goal is now set! You can edit the target at any time before\nstarting the test.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/click-goal-set.png}\n\\caption{Once the click target is set, you can run the A/B test.}\n\\end{figure}\n\nOnce you're finished creating Variants and configuring goals for your\nA/B test, you're ready to run the A/B test. You'll learn how to do this\nnext.\n\n\\chapter{Running A/B Tests}\\label{running-ab-tests}\n\nOnce you've created and configured your A/B test, you'll want to run it\nto begin gathering data on your Control Page and Variants. To run an A/B\ntest,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\emph{Review and Run Test} button from the A/B Test menu.\n\\item\n  Configure how your test should run. There are two configurations:\n\n  \\emph{Traffic Split}: the percentage of visitors that are randomly\n  split between the Variants when visiting the Page. A visitor randomly\n  assigned a Variant always sees the same Variant until the test is\n  finished.\n\n  \\emph{Confidence Level Required}: the accuracy of the test results\n  (i.e., when the winning Variant truly outperforms the other Variants).\n  Typically you want to have the highest confidence level possible, but\n  this impacts test duration. The higher the required confidence level,\n  the longer it takes to declare a winning Variant. Choose the\n  percentage based on your expectations.\n\n  The \\emph{Estimated Time to Declare Winner} field is also displayed.\n  This is the estimated duration the test runs. This is calculated based\n  on the selected traffic split, confidence level, and projected page\n  traffic. Your page's past traffic history is provided by Analytics\n  Cloud.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/run-ab-test.png}\n  \\caption{Configure the final parameters of your A/B test before\n  running it.}\n  \\end{figure}\n\\item\n  Select \\emph{Run}.\n\\end{enumerate}\n\nYour A/B test is now running!\n\nWhile an A/B test is running, you have two buttons available to help\nmanage the test:\n\n\\emph{Terminate Test}: terminates the test. To delete a test, you must\nterminate it first.\n\n\\emph{View Data in Analytics Cloud}: redirects you to your A/B Testing\ndashboard hosted on Analytics Cloud. Here you can view your test's\ntraffic, reports, statistics, etc. related to your test. See the\n\\href{/docs/7-2/user/-/knowledge_base/u/monitoring-a-b-test-results}{Monitoring\nA/B Test Results} article for more information.\n\nAwesome! You now have a running A/B test accumulating data based on user\ninteractions with your Page. Next, you'll learn how to monitor your A/B\ntest's results.\n\n\\chapter{Monitoring A/B Test Results}\\label{monitoring-ab-test-results}\n\nThe information from your A/B test created in Liferay DXP is\nautomatically synchronized with\n\\href{https://help.liferay.com/hc/en-us/articles/360006608732}{Analytics\nCloud}. The test results used to calculate the winning Variant based on\nuser interaction data is viewable there. You can also view your A/B\ntesting history, meaningful statistics, helpful graphs, etc. from\nAnalytics Cloud.\n\nTo navigate to your test's Analytics Cloud dashboard from Liferay DXP,\nclick the \\emph{View Data in Analytics Cloud} button in the A/B Test\nsidebar panel while it's running.\n\nLiferay DXP only displays your test's status (draft, running, etc.) and\nthe winning Variant once the test finishes. For more information on what\nyou can view/manage from Analytics Cloud in relation to your A/B test,\nsee the\n\\href{https://help.liferay.com/hc/en-us/sections/360001492292-Analyzing-Touchpoints}{A/B\nTesting Analytics} article.\n\n\\chapter{Publishing A/B Test\nVariants}\\label{publishing-ab-test-variants}\n\nOnce the A/B test has concluded and Analytics Cloud has computed the\ntest results, the status for the test displays as \\emph{Winner Declared}\n(if a winning Variant was found) or \\emph{No Winner} in the A/B Test\nsidebar panel. You're also alerted to the result via a notification,\nwhich you can view from the User Menu.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/ab-testing-winner.png}\n\\caption{If you're satisfied with the A/B test's results, publish the\nwinning Variant.}\n\\end{figure}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When the required confidence level is not met during the\ntime duration, there is no winning Variant.\n\n\\noindent\\hrulefill\n\nClick the \\emph{Publish} button to publish your winning Variant\nExperience. Once you publish a Variant, the A/B test's status is\n\\emph{Completed} and the test is finished.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/ab-test-complete.png}\n\\caption{Once you've published a Variant, the A/B test is complete.}\n\\end{figure}\n\nIf you want to publish a Variant that was not declared a winner, select\nthe Variant from the A/B Test panel and click \\emph{Publish}. You can\nselect \\emph{Discard Test} to ignore the A/B test recommendations and\nkeep the currently published Control Page.\n\nThe most productive Variant is now available to all users who visit the\nPage.\n\nAwesome! You successfully ran an A/B test and published the Variant that\nis most effective for your Site's users.\n\n\\chapter{Content Publication\nManagement}\\label{content-publication-management}\n\nToday's enterprises generate an enormous amount of content. You can use\nadvanced publishing tools to manage content for a seamless publication\nexperience.\n\nStaging lets you change your Site behind the scenes without affecting\nthe live Site. This is done by creating your Site in a temporary staging\narea (local or remote) and then publishing all the changes at once.\n\nContinue on to begin managing content publication!\n\n\\chapter{Staging}\\label{staging}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nStaging is a tool used to manage content publication. The concept of\nstaging is simple: you can modify your site behind the scenes and then\npublish all your updates in one shot. You don't want users seeing your\nweb site change before their eyes as you're modifying it, do you? The\nstaging environment lets you make changes to your site in a specialized\n\\emph{staging area} that's linked to a production environment. Typically\nthe staging site is used only by content editors and site\nadministrators, while the production environment is public. Content is\npublished from staging to production all at once.\n\nSite administrators can set up their staging environments locally or\nremotely. With Local Live staging, your staging environment and live\nenvironment are hosted on the same server. Remote Live staging has the\nstaging and live environments on separate servers. You'll learn more\nabout the differences between these two staging environments and how to\nenable them for your portal instance.\n\nYou can also leverage the Page Versioning feature. This feature works\nwith both Local Live and Remote Live staging and lets site\nadministrators create multiple variations of staged pages. This allows\nseveral different versions of sites and pages to be developed at the\nsame time. Variations can be created, merged, and published using a\nGit-like versioning system. In the next section, you'll jump in to see\nhow to enable staging.\n\n\\chapter{Enabling Staging}\\label{enabling-staging}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nYou have two different ways to set up staging:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/enabling-local-live-staging}{Local\n  Live}\n\\item\n  \\href{/docs/7-2/user/-/knowledge_base/u/enabling-remote-live-staging}{Remote\n  Live}\n\\end{itemize}\n\nWhether you enable Local Live or Remote Live staging, the interface for\nmanaging and publishing staged pages is the same.\n\nLocal Live staging lets you publish site changes quickly, since the\nstaged and live environments are on the same server. It's also easier to\nswitch between the staged and live environments using Local Live\nstaging. Since the staged content, however, is stored in the same\ndatabase as the production content, your server must have more\nresources, and the content isn't as well protected or backed up as with\nRemote Live staging. Also, you can't install new versions of widgets for\ntesting purposes in a Local Live staging environment, since only one\nversion of an widget can be installed at any given time on a single\nLiferay server.\n\nWith Remote Live staging, your staging and live environments are hosted\non separate servers, so your data is separated. This lets you deploy new\nversions of widgets and content to your staging environment without\ninterfering with your live environment. Publishing is slower, however,\nwith Remote Live staging since data must be transferred over a network.\nOf course, you also need more hardware to run a separate staging server.\n\nVisit the staging environment article (Local or Remote) that most\nclosely aligns with your goal for staging content.\n\n\\chapter{Enabling Local Live Staging}\\label{enabling-local-live-staging}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLocal Live staging places both your staging environment and your live\nenvironment on the same server. When it's enabled, a clone of the site\nis created containing copies of all of the site's existing pages. This\nmeans the staging and live environments share the same JVM, database,\nportlet data (depending on which portlets are selected when staging is\nenabled), and configurations, such as the properties set in the\n\\texttt{portal-ext.properties} file. The cloned site becomes the staging\nenvironment and the original site becomes the live environment.\n\nYou can enable local staging for a site by navigating to the\n\\emph{Publishing} → \\emph{Staging} menu. To get some hands-on experience\nwith enabling Local Live staging, you can complete a brief example which\ncreates a Local Live staging environment for your site.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Product Menu (left side) and select \\emph{Publishing}\n  → \\emph{Staging}.\n\\item\n  Select \\emph{Local Live}. You can also enable page versioning and\n  select staged content. For more information on these options, see the\n  \\href{/docs/7-2/user/-/knowledge_base/u/enabling-page-versioning-and-staged-content}{Enabling\n  Page Versioning and Staged Content} article.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nYou've officially begun the staging process!\n\nBecause Local Live staging creates a clone of your site, you should only\nactivate staging on new, clean sites. Having a few pages and some\nwidgets (like those of the example site you created) is no big deal. If\nyou've already created a large amount of content, however, enabling\nstaging can take a lot of time since it's a resource intensive\noperation. Also, if you intend to use page versioning to track the\nhistory of updates to your site, you should enable it as early as\npossible, \\emph{before} your site has many pages and lots of content.\nYour site's update history isn't saved until you enable page versioning.\nPage versioning requires staging (either Local Live or Remote Live) to\nbe enabled.\n\nIf you ever need to turn off the staging environment, return back to\n\\emph{Staging} from the Publishing dropdown. For more information on\nthis, see the\n\\href{/docs/7-2/user/-/knowledge_base/u/disabling-staging}{Disabling\nStaging} article.\n\nGreat! Now you're ready to use Local Live Staging.\n\n\\chapter{Enabling Remote Live\nStaging}\\label{enabling-remote-live-staging}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nIn Remote Live staging, a connection is established between the current\nsite and another site on a remote Liferay server. The remote site\nbecomes the live environment and the current site becomes the staging\nenvironment---an instance of Liferay used solely for staging. The remote\n(live) Liferay server and the local (staging) Liferay server should be\ncompletely separate systems. They should not, for example, share the\nsame database. When Remote Live staging is enabled, all the necessary\ninformation is transferred over the network connecting the two servers.\nContent creators use the staging server to make their changes while the\nlive server handles the incoming user traffic. When changes to the site\nare ready to be published, they are pushed over the network to the\nremote live server.\n\nBefore enabling Remove Live staging, ensure you've configured your\nLiferay server and remote server appropriately. Follow the\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-servers-for-remote-live-staging}{Configuring\nServers for Remote Live Staging} article to do this.\n\nYou can enable remote staging for a site by navigating to the\n\\emph{Publishing} → \\emph{Staging} menu. Step through the instructions\nbelow to create a Remote Live staging environment for your site.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Product Menu (left side) and select \\emph{Publishing}\n  → \\emph{Staging}.\n\\item\n  Select \\emph{Remote Live}. Additional fields appear for Remote Live\n  Connection Settings.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/remote-live-staging-settings.png}\n  \\caption{After your remote Liferay server and local Liferay server\n  have been configured to communicate with each other, you have to\n  specify a few Remote Live connection settings.}\n  \\end{figure}\n\\item\n  Enter your remote Liferay server's IP address into the Remote Host/IP\n  field. This field should match the host you specified as your\n  \\texttt{tunnel.servlet.hosts.allowed} property in the\n  \\texttt{portal-ext.properties} file. If you're configuring an IPv6\n  address, it must contain brackets when entered into the \\emph{Remote\n  Host/IP} field (e.g., \\emph{{[}0:0:0:0:0:0:0:1{]}}).\n\n  If the remote Liferay server is a cluster, you can set the Remote\n  Host/IP to the load balanced IP address of the cluster to increase the\n  availability of the publishing process. See the\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/configuring-remote-staging-in-a-clustered-environment}{Configuring\n  Remote Staging in a Clustered Environment} for details.\n\\item\n  Enter the port on which the remote Liferay instance is running into\n  the Remote Port field. You only need to enter a Remote Path Context if\n  a non-root portal servlet context is being used on the remote Liferay\n  server.\n\\item\n  Enter the ID of the site on the remote Liferay server that's for the\n  Live environment. If a site hasn't already been prepared on the remote\n  Liferay server, you can log in to the remote Liferay server and create\n  a new blank site.\n\n  After the site has been created, note the site ID so you can enter it\n  into the Remote Site ID field on your local Liferay server. You can\n  find any site's ID by selecting the site's name on the Sites page of\n  the Control Panel.\n\\item\n  Check the \\emph{Use a Secure Network Connection} field to use HTTPS\n  for the publication of pages from your local (staging) Liferay server\n  to your remote (live) Liferay server.\n\\item\n  Decide whether to enable page versioning and select staged content.\n  For more information on these options, see the\n  \\href{/docs/7-2/user/-/knowledge_base/u/enabling-page-versioning-and-staged-content}{Enabling\n  Page Versioning and Staged Content} article.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nYou've officially begun the staging process!\n\nIf you fail to configure your current and remote server properly, you\nwon't be able to enable staging and an error message appears. If you\nhave issues,\n\\href{/docs/7-2/user/-/knowledge_base/u/configuring-servers-for-remote-live-staging}{verify\nyou've configured your servers properly}.\n\nWhen a user publishes changes from the local (staging) server to the\nremote (live) server, Liferay DXP passes the user's email address,\nscreen name, or user ID to the remote server to perform a permission\ncheck. For a publishing operation to succeed, the operation must be\nperformed by a user that has identical credentials and permissions on\nboth the local (staging) and the remote (live) server. This is true\nregardless of whether the user attempts to publish the changes\nimmediately or attempts to schedule the publication for later.\n\nIf only a few users should have permission to publish changes from\nstaging to production, it's easy enough to create a few user accounts on\nthe remote server that match a selected few on the local server. The\nmore user accounts that you have to create, however, the more tedious\nthis job becomes and the more likely you are to make a mistake. And you\nnot only have to create identical user accounts, you also have to ensure\nthat these users have identical permissions. For this reason, it's\nrecommended that you use LDAP to copy selected user accounts from your\nlocal (staging) Liferay server to your remote (live) Liferay server.\nLiferay's Virtual LDAP Server application, available on Liferay\nMarketplace, makes this easy.\n\nSee the\n\\href{/docs/7-2/user/-/knowledge_base/u/disabling-staging}{Disabling\nStaging} article to learn how to turn off the staging environment.\n\nGreat! Now you're ready to use Remote Live Staging.\n\n\\chapter{Configuring Servers for Remote Live\nStaging}\\label{configuring-servers-for-remote-live-staging}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nBefore you can enable Remote Live staging for a site, you must satisfy\nsome necessary requirements:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Add the remote Liferay server to the current Liferay server's list of\n  allowed servers, and vice versa.\n\\item\n  Specify an authentication key to be shared by your current and remote\n  server.\n\\item\n  Enable each Liferay server's tunneling servlet authentication\n  verifier.\n\\item\n  Update the Tunnel Auth Verifier Configuration of your remote Liferay\n  instance.\n\\end{itemize}\n\nFollow the steps below to configure your servers for Remote Live\nstaging.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Add the following lines to your current Liferay server and remote\n  Liferay server's \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\ntunneling.servlet.shared.secret=[secret]\ntunneling.servlet.shared.secret.hex=true\n\\end{verbatim}\n\n  Liferay DXP's use of a pre-shared key between your staging and\n  production environments helps secure the remote publication process.\n  It also removes the need to send the publishing user's password to the\n  remote server for web service authentication. Using a pre-shared key\n  creates an authorization context (permission checker) from the\n  provided email address, screen name, or user ID \\emph{without} the\n  user's password.\n\\item\n  Specify the values for the servers'\n  \\texttt{tunneling.servlet.shared.secret} property.\n\n  The values for these properties depend on the chosen configured\n  encryption algorithm, since different encryption algorithms support\n  keys of different lengths. See the\n  \\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html\\#HTTP\\%20Tunneling}{HTTP\n  Tunneling} properties documentation for more information. Note that\n  the following key lengths are supported by the available encryption\n  algorithms:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    AES: 128, 192, and 256 bit keys\n  \\item\n    Blowfish: 32 - 448 bit keys\n  \\item\n    DESede (Triple DES): 56, 112, or 168 bit keys (However, Liferay\n    places an artificial limit on the minimum key length and does not\n    support the 56 bit key length)\n  \\end{itemize}\n\n  To prevent potential character encoding issues, you can use one of the\n  following two strategies:\n\n  2a. Use hexadecimal encoding (recommended). For example, if your\n  password was \\emph{abcdefghijklmnop}, you'd use the following settings\n  in your \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\ntunneling.servlet.shared.secret=6162636465666768696a6b6c6d6e6f70\ntunneling.servlet.shared.secret.hex=true\n\\end{verbatim}\n\n  2b. Use printable ASCII characters (less secure). This degrades the\n  password entropy.\n\n  If you don't use hexadecimal encoding (i.e., if you use the default\n  setting \\texttt{tunneling.servlet.shared.secret.hex=false}), the\n  \\texttt{tunneling.servlet.shared.secret} property's value \\emph{must}\n  be ASCII compliant.\n\n  Once you've chosen a key, make sure the value of your current server\n  matches the value of your remote server.\n\n  \\textbf{Important:} Do not share the key with any user. It is used\n  exclusively for communication between staging and production\n  environments. Any user with possession of the key can manage the\n  production server, execute server-side Java code, etc.\n\\item\n  Add the following line to your remote Liferay server's\n  \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\ntunnel.servlet.hosts.allowed=127.0.0.1,SERVER_IP,[STAGING_IP]\n\\end{verbatim}\n\n  The \\texttt{{[}STAGING\\_IP{]}} value must be replaced by the staging\n  server's IP addresses. If the server has multiple interfaces, each IP\n  address must also be added, which would show as a source address for\n  the http(s) requests coming from the staging server. The\n  \\texttt{SERVER\\_IP} constant can remain set for this property; it's\n  automatically replaced by the Liferay server's IP addresses.\n\n  If you're validating IPv6 addresses, you must configure the app\n  server's JVM to not force the usage of IPv4 addresses. For example, if\n  you're using Tomcat, add the \\texttt{-Djava.net.preferIPv4Stack=false}\n  attribute in the\n  \\texttt{\\$TOMCAT\\_HOME\\textbackslash{}bin\\textbackslash{}setenv.{[}bat\\textbar{}sh{]}}\n  file.\n\\item\n  Update the \\emph{TunnelAuthVerfierConfiguration} of your remote\n  Liferay instance. To do this, navigate to the Control Panel →\n  \\emph{Configuration} → \\emph{System Settings} → \\emph{API\n  Authentication} → \\emph{Tunnel }Authentication\\emph{. Click\n  }/api/liferay/do* and insert the additional IP \\emph{addresses you're\n  using in the }Hosts allowed* field. Then select \\emph{Update}.\n\n  Alternatively, you can also write this configuration into an OSGi file\n  (e.g.,\n  \\texttt{osgi/configs/com.liferay.portal.security.auth.verifier.tunnel.module.configuration.TunnelAuthVerifierConfiguration-default.config})\n  in your Liferay DXP instance:\n\n\\begin{verbatim}\nenabled=true\nhostsAllowed=127.0.0.1,SERVER_IP,[Local server IP address]\nserviceAccessPolicyName=SYSTEM_USER_PASSWORD\nurlsIncludes=/api/liferay/do\n\\end{verbatim}\n\\item\n  Restart both Liferay servers after making these configuration updates.\n  After restarting, log back in to your local Liferay instance as a site\n  administrator.\n\\end{enumerate}\n\nThat's all you need to do to configure Remote Live Staging! You can now\n\\href{/docs/7-1/user/-/knowledge_base/u/enabling-remote-live-staging}{enable\nit}!\n\nFor additional information on configuring Remote Live staging, see the\ntopics below.\n\n\\section{Applying Patches When Using Remote\nStaging}\\label{applying-patches-when-using-remote-staging}\n\nWhen applying patches to a remote staging environment, you must apply\nthem to all your servers. Having servers on different patch levels is\nnot a good practice and can lead to import failures and data corruption.\nIt is essential that all servers are updated to the same patch level to\nensure remote staging works correctly.\n\n\\section{Configuring Remote Staging's Buffer\nSize}\\label{configuring-remote-stagings-buffer-size}\n\nSimilar to Local Live staging, it is a good idea to turn remote staging\non at the beginning of your site's development for good performance.\nWhen you're using Remote Live staging, and you are publishing a large\namount of content, your publication could be slow and cause a large\namount of network traffic. Liferay DXP's system is very fast for the\namount of data being transferred over the network. This is because the\ndata transfer is completed piecemeal, instead of one large data dump.\nYou can control the size of data transactions by setting the following\nportal property in your \\texttt{portal-ext.properties} file:\n\n\\begin{verbatim}\nstaging.remote.transfer.buffer.size\n\\end{verbatim}\n\nThis property sets the file block sizes for remote staging. If a LAR\nfile used for remote staging exceeds this size, the file will be split\ninto multiple files prior to transmission and then reassembled on the\nremote server. The default buffer size is 10 megabytes.\n\n\\chapter{Enabling Page Versioning and Staged\nContent}\\label{enabling-page-versioning-and-staged-content}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nEnabling page versioning for a site lets site administrators work in\nparallel on multiple versions of the site's pages. Page versioning also\nmaintains a history of all updates to the site from the time page\nversioning was enabled. Site administrators can revert to a previous\nversion of the site at any time. This flexibility is very important in\ncases where a mistake is found and it's important to publish a fix\nquickly.\n\nYou can enable page versioning for public pages or private pages on the\nStaging Configuration page below the menu for selecting your staging\nenvironment (Local or Remote). If you've already enabled staging, you\ncan navigate to the Product Menu → \\emph{Publishing} → \\emph{Staging}\nand click the Options (\\includegraphics{./images/icon-options.png})\nbutton and select \\emph{Staging Configuration}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/staging-page-versioning-staged-content.png}\n\\caption{You can decide to use versioning and choose what content should\nbe staged.}\n\\end{figure}\n\nYou can also choose content for the staging environment to manage on the\nStaging Configuration page.\n\nChoosing content to be staged may sound self-explanatory, but content\nmust have specific attributes in Liferay DXP to use it in a staged\nenvironment. Content or an entity should be site-scoped, so they are\nalways part of a site; otherwise, they are not eligible for staging. For\nexample, page-scoped entities are only eligible for staging on published\npages. When scoped data is on a page (e.g., Web Content Display widget)\nand the page is published, the scoped data is published with it.\n\nLiferay DXP by default supports the following content groups for\nstaging:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Blogs\n\\item\n  Bookmarks\n\\item\n  Calendar\n\\item\n  Documents and Media\n\\item\n  Dynamic Data Lists\n\\item\n  Forms\n\\item\n  Knowledge Base\n\\item\n  Message Boards\n\\item\n  Mobile Device Families\n\\item\n  Polls\n\\item\n  Web Content\n\\item\n  Widget Templates\n\\item\n  Wiki\n\\end{itemize}\n\nBefore you activate staging, choose which of these widgets' data you'd\nlike to copy to staging. You'll learn about many of the collaboration\nwidgets listed under the Staged Content heading when you read the\n\\href{/docs/7-2/user/-/knowledge_base/u/collaboration}{Collaboration\nSuite's} section of articles. For now, be aware that you can enable or\ndisable staging for any of these widgets.\n\nWhy might you want to enable staging for some widget types but not\nothers? In the case of collaborative widgets, you probably \\emph{don't}\nwant to enable staging since such widgets are designed for user\ninteraction. If their content were staged, you'd have to publish your\nsite manually whenever somebody posted a message on the message boards\nto make that message appear on the live site. Generally, you want web\ncontent to be staged because end users aren't creating that kind of\ncontent---web content is the stuff you publish to your site. But widgets\nlike the Message Boards or Wiki should \\emph{not} be staged. Notice\nwhich widgets are marked for staging by default: if you enable staging\nand accept the defaults, staging is \\emph{not} enabled for the\ncollaborative widgets.\n\nThe listed widgets, or content groups, contain one or more specific\nentity. For example, selecting the Web Content widget does not mean\nyou're only selecting web content itself, but also web content folders.\n\nCertain content types can be linked together and can reference each\nother on different levels. One of the responsibilities of staging is to\ndiscover and maintain these references when publishing. Site\nadministrators and content creators have control over the process on\ndifferent levels: staging can be enabled for a content group and a\ncontent group can be selected for publication.\n\nDisabled staged content types can cause unintended problems if you're\nreferring to them on a staged site. For example, the Asset Publisher\nportlet and its preferences are always staged. If the content types it's\nset to display are not enabled for staging, the Asset Publisher can't\naccess them on a staged site. Make sure to plan for the content types\nyou'll need in your staged site.\n\nTurning Staging on and off for individual portlet data could cause data\ninconsistencies between the staging and live sites. Because of this,\nit's not possible to modify the individual portlet configuration once\nyou enable staging. In case you need adjustments later on, you must turn\nstaging off and re-enable it with your new configuration.\n\nBesides managing the widget-specific content, Liferay DXP also has\nseveral special content types such as pages or users. For instance,\npages are a part of the site and can reference other content types, but\nin a special way. The page references widgets, which means publishing a\npage also implies publishing its widgets. The content gives the backbone\nof the site; however, content alone is useless. To display content to\nthe end user, you'll need widgets as the building blocks for your site.\n\nBefore you begin exploring the Staging UI, it's important to understand\nthe publishing process for staging to make informed decisions so you use\nthe staging environment efficiently and effectively.\n\n\\chapter{Publishing Staged Content\nEfficiently}\\label{publishing-staged-content-efficiently}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nNow that you understand how staging works, you'll dive deeper into the\npublication process and some prerequisites you should follow before\npublishing. By understanding how the process works, you can make smart\nand informed decisions about how you want to publish your site's\ncontent.\n\n\\section{Understanding the Publication\nProcess}\\label{understanding-the-publication-process}\n\nIn simple terms, a publication is the process where all content,\nreferenced content, apps and their preferences, pages, etc. are\ntransferred from the staging scope to the live site. If you've enabled\nremote staging, this process involves network communication with another\nremote site. From a low level perspective, staging is an equivalence\nrelation where entities are mirrored to a different location. From a\nhigh level perspective, the staging process happens in three phases:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{Export:} processes the publication configuration, which\n  defines the site's content and apps. This phase also gathers the\n  obligatory referenced entities that are required on the live site.\n  Then everything according to the publication parameters is processed\n  into the instance's own file format, and that file is stored locally\n  or transferred to the remote live Liferay instance.\n\\item\n  \\textbf{Validation:} determines if it's possible to start the import\n  process. This phase verifies the file's version and its integrity,\n  checks for additional system information like language settings, and\n  validates there is no missing content referenced.\n\\item\n  \\textbf{Import:} makes any necessary updates or additions to the\n  site's content, layouts, and apps according to the publishing\n  parameters. If everything is verified and correct, the staged content\n  is published to your live Site.\n\\end{enumerate}\n\nThese phases are executed sequentially.\n\nA crucial factor for successfully publishing staged content is data\nintegrity. If anything is not verified during the publication process,\nthe transactional database reverts the site back to its original state,\ndiscarding the current publication. This is a necessary action to\nsafeguard against publishing incomplete information, which could break\nan otherwise well-designed live Site.\n\nIf the file system is not \\emph{database-stored} (e.g., DBStore), it's\nnot transactional and isn't reverted if a staging failure occurs. This\ncould potentially cause a discrepancy between a file and its reference\nin the database. Because of this, administrators should take great care\nwith staging the document library, making sure that regular backups of\nboth database and file system are being maintained.\n\nNext, you'll learn about staging best practices and prerequisites to\nfollow for a seamless staging experience.\n\n\\section{Planning Ahead for Staging}\\label{planning-ahead-for-staging}\n\nStaging is a complex subsystem that's flexible and scalable. Before\nadvanced users and administrators begin using it for their site, it's\nimportant to plan ahead and remember a few tips for a seamless process.\nThere are several factors to evaluate.\n\n\\begin{itemize}\n\\item\n  \\textbf{Content (amount, type, and structure):} Depending on the\n  content in your site, you can turn on staging for only the necessary\n  content types, leaving others turned off to avoid unnecessary work.\n  Publication can also be configured to publish only certain types of\n  content. See the\n  \\href{/docs/7-2/user/-/knowledge_base/u/managing-content-types-in-staging}{Managing\n  Content Types} article for more information.\n\\item\n  \\textbf{Hardware Environment:} You should plan your environment\n  according to your content types. If your site operates on large images\n  and video files, decide if a shared network drive is the best option.\n  Storing many large images in the Document Library usually requires a\n  faster network or local storage. If you're dealing with web content,\n  however, these are usually smaller and take up very little disk space.\n\\item\n  \\textbf{Customizations and Custom Logic for Your Staging Environment:}\n  Your organization's business logic is most likely implemented in an\n  app, and if you want to support staging for that app, you must\n  \\href{/docs/7-2/frameworks/-/knowledge_base/f/content-publication-management}{write\n  some code} to accomplish this. You can also consider changing default\n  UI settings by writing new JSP code if you want your staging\n  environment's look and feel to change.\n\\end{itemize}\n\nOnce you've finished planning for your site, you should turn on staging\nat the very beginning of the site creation process. This allows the site\ncreator to avoid waiting for huge publications that can take long\nperiods to execute. Taking smaller steps throughout the publication\nprocess forms an iterative creative process as the site is built from\nthe ground up, where content creators can publish their changes\nimmediately, avoiding long wait times.\n\nHere are some JVM/network configuration recommendations for Staging:\n\n\\begin{itemize}\n\\item\n  4 GB of memory\n\\item\n  20 MB/s transfer rate minimum (disk)\n\\end{itemize}\n\nNow that you know how the staging environment works and how to enable it\nfor your site, you'll begin using it in the next section.\n\n\\chapter{Using the Staging\nEnvironment}\\label{using-the-staging-environment}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAfter \\href{/docs/7-2/user/-/knowledge_base/u/enabling-staging}{enabling\nstaging} (either Local Live or Remote Live) for a site, you'll notice\nadditional options provided on the top Control Menu (Staging Bar) and\nalso in the menu to the left. These new menus help you manage staged\npages. Most of your page management options have also been removed; now\nyou can't directly edit live pages. You now must use the staging\nenvironment to make changes.\n\nClick the \\emph{Staging} button to view the staged area. Management\noptions are restored and you can access some new options related to\nstaging.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/staging-live-page.png}\n\\caption{You can see the new staging options added to the top and left\nof your screen.}\n\\end{figure}\n\nTo test out the staging environment, add the Bookmarks widget and then\nclick on \\emph{Live} from the top menu. Notice that the Bookmarks widget\nisn't there. That's because you've staged a change to the page but\nhaven't published that change yet to the live site.\n\nNext, you'll learn the basics of staging content.\n\n\\chapter{Staging Content}\\label{staging-content}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nWhen you're on the staged site, you have several options in the Staging\nBar to help start your staging conquest.\n\n\\textbf{Site Pages Variation:} lets you work in parallel on multiple\nversions of a staged site page. You can choose the site page variation\nfrom the dropdown menu or manage them from the \\emph{Options} icon\n(\\includegraphics{./images/icon-staging-bar-options.png}) in the Staging\nBar. See the\n\\href{/docs/7-2/user/-/knowledge_base/u/using-multi-and-single-page-variations}{Using\nSite Pages Variations} article for more information.\n\n\\textbf{Page Variations:} lets you work in parallel on multiple versions\nof a staged page. You can choose the page variation from the dropdown\nmenu or manage them from the \\emph{Options} icon\n(\\includegraphics{./images/icon-staging-bar-options.png}) in the Staging\nBar. See the\n\\href{/docs/7-2/user/-/knowledge_base/u/using-multi-and-single-page-variations}{Using\nSite Pages Variations} article for more information.\n\n\\textbf{Undo/Redo:} steps back/forward through recent changes to a page,\nwhich can save you the time of manually adding or removing apps if you\nmake a mistake. To access \\emph{Undo}/\\emph{Redo}, select the\n\\emph{Options} icon\n(\\includegraphics{./images/icon-staging-bar-options.png}) in the Staging\nBar.\n\n\\textbf{History:} shows the list of revisions of the page, based on\npublication dates. You can go to any change in the revision history and\nsee how the pages looked at that point. To access \\emph{History}, select\nthe \\emph{Options} icon\n(\\includegraphics{./images/icon-staging-bar-options.png}) in the Staging\nBar.\n\n\\textbf{Ready for Publication:} After you're done making changes to the\nstaged page, click this button. The status of the page changes from\n\\emph{Draft} to \\emph{Ready for Publication} and any changes you've made\ncan be published to the Live Site. When you publish a page to live, only\nthe version \\emph{Marked as Ready for Publication} is published.\n\nNow you'll step through a brief example for using the Staging Bar to\nstage and publish content.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  On the staged site, navigate to the Product Menu and select\n  \\emph{Content \\& Data} → \\emph{Web Content}.\n\\item\n  Create a Basic Web Content article and save it.\n\\item\n  Go back to your staged site's main page and navigate to the \\emph{Add}\n  (\\includegraphics{./images/icon-add-widget.png}) → \\emph{Widgets} →\n  \\emph{Content Management} menu and drag the \\emph{Web Content Display}\n  widget to the page.\n\\item\n  Select the web content article you created to display in the new\n  widget.\n\\item\n  Select the \\emph{Ready for Publication} button to confirm you're ready\n  to publish the content from the staged site to the live site. This\n  prepares the staged content for publication. If workflow is enabled\n  for any new resource, the resource must go through the workflow\n  process before it can be published to the live site.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/staging-publish-bar.png}\n  \\caption{The staging toolbar indicates whether you're able to publish\n  to the live site.}\n  \\end{figure}\n\\item\n  Click the \\emph{Publish to Live} button. A pop-up window appears with\n  configuration options for your publication.\n\\item\n  Enter the name of your publication.\n\\item\n  Observe the changes listed in the menu. This lists the changed content\n  planned for publication.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/simple-staging-publication.png}\n  \\caption{The Simple Publication menu displays the changes since last\n  publication and a way to name your publication.}\n  \\end{figure}\n\\item\n  Click the \\emph{Publish to Live} button to publish your staged results\n  to the live site.\n\\end{enumerate}\n\nAwesome! You've created content on the staged site and published it to\nyour live site. It's now available for all your site users to see!\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Although publishing content is the more well-known\nfunction, publishing a portlet is also a viable option. You can publish\nportlets residing in the Control Panel and on pages. For example, you\ncan modify a portlet's title and publish the change to live. This is\npossible because portlet configurations are always staged.\n\nTo publish a portlet that is on a page, you must publish the page first.\n\n\\noindent\\hrulefill\n\nThis example explored the Simple Publication menu. If your publication\nrequires more advanced configuration like specifying specific content,\ndates, pages, etc., you should click the \\emph{Switch to Advanced\nPublication} button. You'll explore the more advanced configuration\noptions next.\n\n\\chapter{Advanced Publication with\nStaging}\\label{advanced-publication-with-staging}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nOnce you've finished your changes on the staged site and want to publish\nthem, select the \\emph{Publish to Live} button from the Staging Bar. To\nconfigure advanced publication options, select the \\emph{Switch to\nAdvanced Publication} button. Opening the Advanced Publication menu\npresents options for scheduling a time to publish your content, editing\nthe pages/content to include in the publication, managing permissions,\netc. This lets you perform advanced editing to your publication process.\n\n\\section{Date}\\label{date}\n\nYou have two options for the Date category:\n\n\\textbf{Now:} immediately pushes any changes to the live Site.\n\n\\textbf{Schedule:} set a specific date to publish or to set up recurring\npublishing. You could use this to publish all changes made during the\nweek every Monday morning without any further intervention.\n\nThese options let you plan staging schedules in advance.\n\n\\section{Pages}\\label{pages-1}\n\nThis area of the menu gives you the option to choose which pages to\ninclude when you publish. You can choose the page group (Public or\nPrivate) to publish by selecting the \\emph{Change to Public Pages} or\n\\emph{Change to Private Pages}. You cannot publish both at the same\ntime; you must complete their publication processes separately if you\nwant to publish both page groups.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/staging-advanced-publication.png}\n\\caption{You have several ways to specify the pages you want included in\nyour publication.}\n\\end{figure}\n\nYou can also choose specific pages to publish, and the look and feel of\nthose pages.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} If you're publishing pages with a custom theme, you must\ncheck the \\emph{Theme Settings} option under the Look and Feel heading\nfor your staging configuration. If it's not checked, the default theme\nis always applied.\n\n\\noindent\\hrulefill\n\nThe \\emph{Delete Missing Pages} option under the Look and Feel heading\ndeletes all pages from the live Site that are not present on the staging\nSite. You can choose specific pages to publish (and remove) by manually\nselecting them under the Pages to Publish heading.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The Simple Publication menu displays the number of page\ndeletions tracked by the Staging framework. Keep in mind that this\nnumber counts the page deletions on the staging Site, not how many pages\nwill be deleted on the live Site. There could be an inconsistency\nbetween the number of page deletions to be published and the actual\nnumber of pages present on either of the staging and live Sites. For\nexample, pages that were deleted on the staging Site before they were\npublished.\n\n\\noindent\\hrulefill\n\nSee the \\hyperref[deletions]{Deletions} section for more information on\ncontent deletion.\n\n\\section{Content}\\label{content}\n\nThe Content area lets you select the content to be published. If you\nchoose a page to be published from the Pages menu, the portlets and\ntheir references are always published, even if you specify differently\nin the Content section.\n\nThere are other filtering sub-options for certain content types. You\nfirst must choose what content to publish based on date. Specifying a\ndate range lets you choose content to publish based on when it was\ncreated or last modified. Select the option that best fits your\nworkflow. The available options are described in more detail below:\n\n\\textbf{All:} publishes all content regardless of its creation or last\nmodification date.\n\n\\textbf{From Last Publish Date:} publishes content that was created or\nmodified since the last publish date. This is the default option.\n\n\\textbf{Date Range:} publishes content based on a specified date range.\nYou can set a start and end date/time window. The content created or\nmodified within that window of time is published.\n\n\\textbf{Last:} publishes content based on a set amount of time since the\ncurrent time. For example, you can set the date range to the past 48\nhours, starting from the current time.\n\nUnder the date options are the different types of content that can be\npublished. This list is populated based on the provided date range. For\nexample, if at least one article is created or modified in the given\ndate range, a Web Content section appears in the list, and the number of\narticles is shown next to the Web Content label. Otherwise, the Web\nContent section is absent.\n\nThe \\emph{Categories} content type is not dependent on the date range\nand is always shown in the list.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Since comments and ratings are meant for the end user,\nthey are not supported in staging and can only be added to the live\nsite.\n\n\\noindent\\hrulefill\n\nUnchecking the checkbox next to a certain content type excludes it from\nthe current publication to the live site.\n\nSome of the content types in the list, like Web Content and Documents\nand Media, have further filtering options. For instance, when the Web\nContent section is present and checked, it shows a comma-separated list\nof related items to be published, including the articles themselves. A\nsample list of related items for web content might look like this:\n\\emph{Web Content(12), Structures(3), Referenced Content, Include\nAlways, Version History}. You can remove items by clicking the\n\\emph{Change} button next to the list.\n\nSee the\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-content-types-in-staging}{Managing\nContent Types in Staging} article for more information on managing\ncontent during the publication process.\n\n\\section{Deletions}\\label{deletions}\n\nThis portion of the menu lets you delete two things:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  portlet metadata before publishing\n\\item\n  operations performed for content types\n\\end{itemize}\n\nYou have two options to manage for deletions:\n\n\\textbf{Delete Application Data Before Importing:} all data created by\nthe application is deleted before the import process. Ensure you\nunderstand the ramifications of this option before selecting it. Some\napplications in other pages may reference this data. This process cannot\nbe undone. If you are unsure, complete an export first.\n\n\\textbf{Replicate Individual Deletions:} operations performed for\ncontent in the staging environment are replicated to the target site.\nThis does not include page deletions.\n\n\\section{Permissions}\\label{permissions}\n\nThis area lets you include permissions for the pages and portlets when\nthe changes are published. Select the \\emph{Publish Permissions}\ncheckbox to enable this functionality.\n\nOnce you're finished configuring you advanced publication, select\n\\emph{Publish to Live} to publish or schedule your publication.\n\n\\chapter{Managing Content Types in\nStaging}\\label{managing-content-types-in-staging}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nWhen managing content in Staging's Advanced Publication menu, there are\nseveral factors to consider when preparing your content for publication.\nAs described in\n\\href{/docs/7-2/user/-/knowledge_base/u/advanced-publication-with-staging}{Advanced\nPublication with Staging}, you can navigate to the Content area of the\nAdvanced Publication menu to select content you want to publish. There\nare options attached to each content group (e.g., Web Content) that you\ncan manage too.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-version-history-box.png}\n\\caption{Click the \\emph{Change} button for a content group to manage\nits specific content.}\n\\end{figure}\n\nYou'll learn about some of these options and their best practices next.\n\n\\section{Referenced Content}\\label{referenced-content}\n\nThis is represented by\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Structures and templates included in web content.\n\\item\n  Documents and Media files (e.g., images) included in web content.\n\\item\n  etc.\n\\end{itemize}\n\nYou can exclude some of this content during publication or export to\nspeed up the process. These references are validated during the\npublication process or an import, so the images must be published or\nimported first.\n\n\\section{Version History}\\label{version-history}\n\nWeb content tends to be updated frequently, often more so than other\nkinds of content. Sometimes this can result in high numbers of versions.\nIf there are hundreds of versions, it can take a long time to publish\nthese articles. You can bypass this by choosing to not publish the\n\\emph{Version History} (i.e., the past versions of the web content\narticles to be published). If you disable this, only the last\n\\textbf{approved} version of each web content article is published to\nLive. This can significantly speed up the publication process.\n\nYou can set this option globally. If you navigate to the Control Panel →\n\\emph{Configuration} → \\emph{System Settings} → \\emph{Web Content} →\n\\emph{Virtual Instance Scope} → \\emph{Web Content}, you can toggle the\n\\emph{Version History by Default Enabled} checkbox. This sets the\ndefault behavior. When publishing content, it is selected by default, so\nsite administrators must manually uncheck the \\emph{Version History} box\nto publish only the latest approved version of web content articles. To\nchange the default behavior, enable the checkbox in System Settings.\n\n\\section{Previews and Thumbnails}\\label{previews-and-thumbnails}\n\nPreviews and thumbnails are generated automatically for documents.\nDisabling this, though, can greatly increase your publishing speed in\nsome cases. You should be careful about publishing previews and\nthumbnails to the live Site.\n\nImagine a scenario where a site has approximately 4000 images or\ndocuments. If the previews and thumbnails are turned on, this could end\nup in 28000 physical files on the disk. If staging is set up to publish\nthe previews and thumbnails, this would mean that instead of taking care\nof the 4000 images, it would process seven times more files! If you\nstill want to use the previews on your live environment, you can set up\nthat Liferay instance to generate them automatically.\n\nIt depends on your environment for whether you can use the publishing of\nthe previews and thumbnails. Publishing them is a heavy operation, and\nyou must also transfer the LAR file over the network if you use remote\nstaging. If you decide to generate them on the live Site, understand\nthat this could take some time, since it's a CPU intense operation.\n\n\\section{Vocabularies}\\label{vocabularies}\n\nWhen working within a site, a user may select vocabularies from both the\ncurrent site as well as the global site. While this doesn't pose an\nissue when creating content, it can cause issues when publishing.\n\nFor environments that use both global and local vocabularies, note that\nglobal vocabularies must be published to the live site through global\nsite staging. One way to avoid confusion with vocabularies is to keep\nall vocabularies local or global.\n\nIf both must be used, you can resolve the issue by ensuring that\ndependencies (e.g., categories and vocabularies) are published before\npublishing the site that depends on them (whether the dependencies are\nlocal or global).\n\nAssets like tags, categories, structures, templates, widget templates,\ndocument types, and dynamic data lists can also be shared by a parent to\nits child sites. In this case, ensure that the ancestor's dependencies\nare published before the site in question.\n\n\\section{Deletions}\\label{deletions-1}\n\nThe Staging framework gathers deletions (including trashed entities) in\na site. These deletions can be published to clean up the live Site. If\nyou plan to process it later, or if it's not a problem to have lingering\ndata on live, this can be turned off as well to save execution time\nduring the process.\n\n\\chapter{Staging Processes and\nTemplates}\\label{staging-processes-and-templates}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nWhen you make a staging publication, it's captured as a staging process\nand stored for future reference. You can manage these processes by\nnavigating to the \\emph{Staging} option located in the Product Menu's\nPublishing tab. From there, you'll see a list of staging processes that\nhave been completed. You can relaunch or clear any of these publications\nby clicking the \\emph{Actions} button\n(\\includegraphics{./images/icon-actions.png}) next to a process. If you\nclick the \\emph{Scheduled} tab from above, you'll find staging processes\nthat you've scheduled for future publication dates.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/staging-processes.png}\n\\caption{Your staging processes can be viewed at any time.}\n\\end{figure}\n\nIf you find yourself repeatedly creating similar staging processes, you\nshould think about using Publish Templates.\n\nInstead of manually having to customize a publication process every time\nyou're looking to publish pages/content, you can use a publish template.\nWith publish templates, you can select a custom template and immediately\npublish with the options you configured.\n\nFollow the steps below to create and use a publish template.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select the \\emph{Options} icon\n  (\\includegraphics{./images/icon-options.png}) from the top right\n  corner of the Staging screen and select \\emph{Publish Templates}.\n\\item\n  Click the \\emph{Add} button (\\includegraphics{./images/icon-add.png})\n  and assign the template a title and description, and then fill out the\n  configuration options as you would during a custom publication\n  process.\n\\item\n  Save your publish template. It's available to use from the\n  \\emph{Publish Templates} tab in the \\emph{Publish to Live} menu's\n  Advanced Publication area.\n\\item\n  To use the template, click the \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) next to the template and\n  select \\emph{Publish}.\n\n  This automatically sets the options for publishing pages and their\n  content. All you have to do is give the publication process a name.\n  Once you confirm the configuration settings, your staging settings are\n  published.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When staging is enabled, the options available from the\n\\emph{Publishing} tab are modified. When in the Live environment, you\ncan only access the \\emph{Export} feature. When in the Staging\nenvironment, you can only access the \\emph{Import} and \\emph{Staging}\nfeatures. The disabled features for each environment don't make sense in\nthat context. For example, you shouldn't be able to import content when\nin the live environment; it must be imported into the staged environment\nand then published before it is available in the live site.\n\n\\noindent\\hrulefill\n\nNow you know how to reference stored/scheduled staging processes and\ncreate publish templates to streamline publication.\n\n\\chapter{Disabling Staging}\\label{disabling-staging}\n\nDisabling staging doesn't take a lot of steps, but should not be done\nlightly. It's important to know the consequences of turning the staging\nenvironment off so you can decide if your circumstances really warrant\nit.\n\nThe consequences for disabling Local Live and Remote Live staging are\nslightly different, so you'll learn about both.\n\n\\section{Disabling Local Live\nStaging}\\label{disabling-local-live-staging}\n\n{This material has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nConceptually, the live site is the final approved version of your site,\nwhereas the staging site is a temporary workspace containing information\nthat is not finalized.\n\nDisabling local staging completely removes the staging environment,\nwhich means all content not published to your live site is erased.\nTherefore, before disabling staging, you must ensure all necessary\ninformation on the staged site is published or preserved elsewhere.\n\nKeep in mind that draft content types are not published, so they can be\nlost too.\n\nWhen you enabled staging there was an initial publication. Disabling\nstaging does not start a publication; the staging site is deleted. If\nthe staged site contains a large amount of content, however, those\ndeletions could take a substantial amount of time to process. For this\nreason, don't disable staging when your portal instance is busy.\n\n\\section{Disabling Remote Live\nStaging}\\label{disabling-remote-live-staging}\n\n{This material has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nDisabling remote staging does not delete the staged site; it only\ndisables the connection between the live site and remote staging site.\nThis means no data is deleted from the live or remote staging sites\nafter the connection is disabled. Since no data is erased and no\nprocesses are started, disabling remote staging is almost instantaneous.\n\nWhen Remote Live staging is enabled, certain information (e.g., which\nportlet is being staged) is recorded on both the live and staged Sites.\nFor this reason, when you disable remote staging, you must ensure the\nlive Site is still accessible so both sides can communicate that it's\ndisabled. Do not shut down your live Site and then attempt to disable\nremote staging from your staged Site; this results in errors.\n\nIf there's ever a lost network connection between the remote staged Site\nand the live Site, a message appears, informing you of the error and a\nway to forcibly disable staging. This is only an option for the staged\nsite; executing this option erases the staged site's staging\ninformation---not the content. On the contrary, the live site remains in\na locked state. A possible workaround is to create a new live site and\nimport content to it, if necessary.\n\n\\section{Steps to Disable Staging}\\label{steps-to-disable-staging}\n\nFollow the steps below to disable Local Live or Remote Live staging:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the \\emph{Publishing} → \\emph{Staging} option, which is\n  only available from the staged site.\n\\item\n  Click the \\emph{Options} icon\n  (\\includegraphics{./images/icon-options.png}) from the upper right\n  corner of the page and select \\emph{Staging Configuration}.\n\\item\n  Select the \\emph{None} radio button and click \\emph{Save}.\n\\end{enumerate}\n\nThat's it! Your staging environment is now turned off.\n\n\\chapter{Publishing Single Assets From a Staged\nSite}\\label{publishing-single-assets-from-a-staged-site}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nSometimes, stepping through the entire publication process is not\nnecessary and can be overkill. For example,\n\n\\begin{itemize}\n\\tightlist\n\\item\n  What if you created a web content article and want only to publish it\n  and nothing else?\n\\item\n  What if you want to publish a new folder of articles and their\n  dependencies, but don't want the hassle of the publication process?\n\\item\n  What if you found a typo in a document and want to fix it quickly and\n  push to the live site?\n\\end{itemize}\n\nYou're in luck! You can publish certain single assets from the staged\nSite to the live Site without creating a staging publication process,\nfrom their respective app menus:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Web Content\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Web Content\n  \\item\n    Folder\n  \\end{itemize}\n\\item\n  Documents and Media\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Document\n  \\item\n    Folder\n  \\item\n    Shortcut\n  \\item\n    Document Type\n  \\end{itemize}\n\\item\n  Blogs\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Blog\n  \\end{itemize}\n\\item\n  Bookmarks\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    Bookmark\n  \\item\n    Folder\n  \\end{itemize}\n\\end{itemize}\n\n\\textbf{Important:} Single asset publication is not supported for\npage-scoped content.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} When publishing a Web Content or Bookmarks folder, their\nrespective entries and subfolders are included. Publishing a Documents\nand Media folder works the same way, but also includes shortcuts.\n\n\\noindent\\hrulefill\n\nYou'll step through an example to see how this is done.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Make sure the Staging framework is\n  \\href{/docs/7-2/user/-/knowledge_base/u/enabling-staging}{enabled} and\n  you're on the staged Site.\n\\item\n  Create a Web Content Article in the Product Menu's \\emph{Content \\&\n  Data} → \\emph{Web Content} menu.\n\\item\n  Once you've saved the new Web Content Article, select its\n  \\emph{Actions} button (\\includegraphics{./images/icon-actions.png})\n  next to the article and select \\emph{Publish to Live}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/single-asset-publish.png}\n  \\caption{You can publish the single web content article to the live\n  site.}\n  \\end{figure}\n\\item\n  You're presented a Process Details page where you can view the\n  progress of your single asset publication request. Ensure the Web\n  Content Article is published successfully.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\begin{verbatim}\n **Note:** Sometimes the publication process doesn't start immediately\n (e.g., if there's another publication running). You can check a specific\n asset's publication progress by navigating to the *Options*\n (![Options](./images/icon-options.png)) &rarr; *Staging* &rarr;\n *Current and Previous* tab in its Site Admin app.\n\\end{verbatim}\n\n\\noindent\\hrulefill\n\nThere you have it! If you navigate to your live site's Web Content\nsection, the new article is available.\n\nSimilar to the regular staging publication process, your single asset\npublications also include associated dependencies. For example, if your\nweb content article contains an image, custom structure, and custom\ntemplate, they are all published together. The same concept applies for\nfolders---if you publish a folder containing several web content\narticles, all the articles and their associated dependencies are\npublished too.\n\nBy default, only those with permissions to publish widgets can publish\nsingle assets. Follow the steps below to modify these permissions for a\nRole:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Control Panel → \\emph{Users} → \\emph{Roles}.\n\\item\n  Select the Role you're updating.\n\\item\n  Click the \\emph{Define Permissions} tab.\n\\item\n  In the left menu, navigate to \\emph{Control Panel} → \\emph{Sites} →\n  \\emph{Sites}.\n\\item\n  Under the Resource Permissions heading, select the \\emph{Export/Import\n  Application Info} option.\n\\end{enumerate}\n\nAlso, make sure the \\emph{Publish Staging} permission is granted to the\nrole. This is required to publish assets with staging. See the\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-permissions}{Managing\nPermissions} article for more information.\n\nGreat! Now you know how to publish single assets and manage the\npermissions for who can do it.\n\n\\chapter{Organizing Pages for\nStaging}\\label{organizing-pages-for-staging}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nSay you're working on a product-oriented Site where several major\nchanges are required for a page or a set of pages over a short period of\ntime. You must work on multiple versions of the Site at the same time to\nensure everything has been properly reviewed before it goes live. With\nstaging, you can do this using \\emph{Page Variations}.\n\nIn this section, you'll explore page variations and how they're useful.\n\n\\chapter{Using Multi and Single Page\nVariations}\\label{using-multi-and-single-page-variations}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThere are two page variation options available from the\n\\href{/docs/7-2/user/-/knowledge_base/u/using-the-staging-environment}{Staging\nBar}:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Site Pages Variation:} Different variations for a set of Site\n  pages. For instance, you could use this if you had three separate\n  pages and wanted to modify these pages while keeping them together as\n  a set.\n\\item\n  \\emph{Page Variations:} Different variations for a single page. A page\n  variation can only exist inside a Site pages variation.\n\\end{itemize}\n\nYou must enable page versioning in the Staging Configuration menu before\nthe above options are available in the Staging Bar. You'll learn more\nabout this later.\n\nVariations only affect pages and not the content, which means all the\nexisting content in your staging Site is shared by all your variations.\nThe content, however, can be displayed in many different ways for each\npage variation. For example, in different Site page variations, you can\nhave different logos, a different look and feel for your pages,\ndifferent applications on these pages, different configuration of these\napplications, and even different pages. One page can exist in just one\nSite page variation or in several of them. Modifying the layout type\n(e.g., Layout, Panel, Embedded, etc.) or friendly URL of a page,\nhowever, \\textbf{does} affect every Site page variation. For example, if\na page template is modified, those modifications are propagated to the\npages configured to inherit changes from the template, overriding\nStaging's Page Variations and Site Pages Variations.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} Page templates are not recognized by the Staging\nframework. This means that existing page templates are not viewable or\neditable on a staged Site. If they're created on a staged Site, they\ncannot be preserved once staging is disabled. You can, however, export\nand import page templates.\n\n\\noindent\\hrulefill\n\nYou'll learn about enabling page versioning next.\n\n\\section{Enabling Page Versioning}\\label{enabling-page-versioning}\n\nPage Versioning is enabled on the Staging Configuration screen when\nfirst \\href{/docs/7-2/user/-/knowledge_base/u/enabling-staging}{enabling\nstaging}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/page-versioning.png}\n\\caption{You can enable page versioning for public and/or private\npages.}\n\\end{figure}\n\nYou can enable page versioning for public and private pages. When page\nversioning is enabled, the page variation options are available in the\nStaging Bar. By default, you only have one Site pages variation and page\nvariation which are both called \\emph{Main Variation}.\n\nIf you did not enable page versioning during the initial setup of your\nstaging environment, navigate to the Product Menu → \\emph{Publishing} →\n\\emph{Staging} → \\emph{Options}\n(\\includegraphics{./images/icon-options.png}) → \\emph{Staging\nConfiguration}. You can enable the page versioning options there.\n\n\\section{Using Site Pages Variations}\\label{using-site-pages-variations}\n\nSite pages variations are useful when you must plan multiple page sets\nfor your Site at once. For example, consider this scenario:\n\nIf there were separate teams in your company that needed to create three\ndrastically different page sets for your Site at the same time, they\nwould need to create three Site pages variations. For example,\n\n\\begin{itemize}\n\\tightlist\n\\item\n  The marketing team can give your Site a completely different look and\n  feel for the Holidays.\n\\item\n  The product management team can work on a version that is planned to\n  publish on the first of the year for a new product launch.\n\\item\n  The developer relations team can work on a version that is planned to\n  publish on the upcoming Hack-a-thon day.\n\\end{itemize}\n\nWith this use case, having a Site pages variation for each planned page\nset allows three ideas to be fully planned and implemented before\npublication.\n\nAnother option for this scenario is to let the product management team\nexperiment with two different ideas for the home page of the Site. This\ncan be done by creating several page variations within the current Site\npages variation of their product launch Site. You'll learn more about\npage variations later.\n\nOnce you've\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-multi-and-single-page-variations}{created\na Site pages variation}, you can now navigate to its home page and\nchange the logo, apply a new theme, move applications around, change the\norder of the pages, configure different apps, and more. The other\nvariations aren't affected. You can even delete existing pages or add\nnew ones (remember to \\emph{Mark as Ready for Publication} when you are\nfinished with your changes). When you delete a page, it is deleted only\nin the current variation. The same happens when you add a new page. If\nyou try to access a page that was deleted in the current variation,\nLiferay informs you this page is not \\emph{enabled} in this variation\nand you must enable it.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/enable-unavailable-page.png}\n\\caption{Select the \\emph{Enable} button to create a missing page in the\ncurrent Site pages variation.}\n\\end{figure}\n\nTo publish a variation to the live Site, click on \\emph{Publish to Live}\nin the staging menu and then select \\emph{Publish to Live}. Publications\ncan also be scheduled independently for different variations. For\nexample, you could have a variation called \\emph{Mondays} which is\npublished to the live Site every Monday and another one called \\emph{Day\n1} which is published to the live Site every first day of each month.\n\n\\section{Using Page Variations}\\label{using-page-variations}\n\nYou can also have variations for a single page inside a Site pages\nvariation, which lets you work in parallel on different versions of a\npage. For example, you might work on two different proposals for the\ndesign of the home page for a Holidays Site pages variation. Page\nvariations only exist inside a Site pages variation.\n\nOnce you've\n\\href{/docs/7-2/user/-/knowledge_base/u/creating-multi-and-single-page-variations}{created\na page variation}, you can choose it from the dropdown menu on the\nStaging Bar. You can always switch between different variations by\nclicking on them from the Staging Bar.\n\nOnce you've modified the page variation to the way you want, mark it as\n\\emph{Ready for Publication}. Only one page variation can be marked as\nready for publication and that is the one that gets published to the\nlive Site. To publish a variation to the live Site, click on\n\\emph{Publish to Live} in the staging menu and then select \\emph{Publish\nto Live}.\n\n\\section{Managing Variation\nPermissions}\\label{managing-variation-permissions}\n\nIt's also possible to set permissions on each variation, so certain\nusers have access to manage some, but not all variations. To do this,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Staging Bar's \\emph{Options} button\n  (\\includegraphics{./images/icon-staging-bar-options.png}) and select\n  the variation type you want to configure.\n\\item\n  Select the desired variation's \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) and select\n  \\emph{Permissions}.\n\\item\n  Configure the variation's permissions and then click \\emph{Save}.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/page-variation-permissions.png}\n\\caption{Configure the roles that can access and modify your variation.}\n\\end{figure}\n\nAwesome! You now know how to manage variation permissions!\n\n\\chapter{Creating Multi and Single Page\nVariations}\\label{creating-multi-and-single-page-variations}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nYou can create two types of page variations:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Site Pages Variation (multiple pages)\n\\item\n  Page Variation (single page)\n\\end{itemize}\n\nYou can learn more about these variations in the\n\\href{/docs/7-2/user/-/knowledge_base/u/using-multi-and-single-page-variations}{Using\nMulti and Single Page Variations} article. As an example, you'll step\nthrough creating a Site pages variation next. Both variation processes,\nhowever, are similar.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Select the \\emph{Options} icon\n  (\\includegraphics{./images/icon-staging-bar-options.png}) in the\n  Staging Bar and select the variation option. For example, select the\n  \\emph{Site Pages Variation} option. This brings you to a list of the\n  existing Site page variations for your Site.\n\\item\n  Click \\emph{Add Site Pages Variation} to create a new one.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/staging-page-variations.png}\n  \\caption{When selecting the \\emph{Site Pages Variation} link from the\n  Staging Bar, you're able to add and manage your Site pages\n  variations.}\n  \\end{figure}\n\\item\n  Set a name and description for your new Site pages variation.\n\\item\n  Set how you want your variation created. From the \\emph{Copy Pages\n  from Site Page Variation} field, you can copy content from an existing\n  variation to create your new one. There are several options to choose\n  in this selector.\n\n  \\textbf{All Site Pages Variations:} Creates a new variation that\n  contains the last version marked as ready for publication from any\n  single page existing in any other variation.\n\n  \\textbf{None (Empty Site Pages Variation):} Creates a new, empty\n  variation.\n\n  \\textbf{{[}Existing Variations{]}:} Creates a new Site page variation\n  that contains only the last version of all the pages that exist in a\n  specific variation (e.g., \\emph{Main Variation}). The current\n  variation must be marked as ready for publication.\n\n  The copy option is not available when creating a page variation. A new\n  page variation is a copy of the current Site pages variation.\n\\item\n  Click \\emph{Add} to create the Site pages variation.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} You can rename any variation after it's created, if\nnecessary. For example, edit the Main Variation and change its name to\nsomething that makes more sense in your Site, such as \\emph{Basic},\n\\emph{Master}, or \\emph{Regular}.\n\n\\noindent\\hrulefill\n\nAwesome! Your Site pages variation is created and available for\nmodification.\n\n\\chapter{Merging Site Pages\nVariations}\\label{merging-site-pages-variations}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nAnother powerful feature of Staging's Page Versioning framework is the\npossibility of \\emph{merging} Site Pages Variations. To merge two Site\nPages Variations, follow the instructions below.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the Staging Bar's \\emph{Options} button\n  (\\includegraphics{./images/icon-staging-bar-options.png}) and select\n  \\emph{Site Pages Variation}.\n\\item\n  Click the Site Pages Variation's \\emph{Actions} button\n  (\\includegraphics{./images/icon-actions.png}) you want to use as the\n  base for merging and select \\emph{Merge}.\n\\item\n  Select the Site Pages Variation to merge on top of the base Site Pages\n  Variation.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/merge-site-pages-variation.png}\n  \\caption{Select the site pages variation you'd like to merge with your\n  base variation.}\n  \\end{figure}\n\n  Merging works like this:\n\n  \\begin{itemize}\n  \\tightlist\n  \\item\n    New pages that don't exist in the base variation are added.\n  \\item\n    If a page exists in both Site Pages Variations, and at least one\n    version of the page was marked as ready for publication, the latest\n    version marked as ready is added as a new page variation in the\n    target page of the base variation. Note that older versions or page\n    variations not marked as ready for publication aren't copied.\n    Merging can be executed, however, as many times as needed and\n    creates the needed page variations in the appropriate page of the\n    base site pages variation.\n  \\item\n    Merging does not affect content and doesn't overwrite anything in\n    the base variation; it adds more versions, pages, and page\n    variations as needed.\n  \\end{itemize}\n\\end{enumerate}\n\nGreat! You've merged site pages variations!\n\n\\chapter{Managing Permissions}\\label{managing-permissions}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThe staging environment has many different options for building and\nmanaging a Site and its pages. Sometimes administrators want to limit\naccess to staging's subset of powerful features. A Role with permissions\ncan accomplish this. To create/modify a Role, complete the following\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the \\emph{Control Panel} → \\emph{Users} → \\emph{Roles}.\n\\item\n  To create a new Role, select the \\emph{Add} button\n  (\\includegraphics{./images/icon-add.png}) and complete the New Role\n  menu. Once you have a new Role created, or you've decided on the Role\n  you want to modify, select the Role's \\emph{Actions} icon\n  (\\includegraphics{./images/icon-actions.png}) and select \\emph{Edit}.\n\\item\n  From the top menu, select \\emph{Define Permissions}.\n\\end{enumerate}\n\nThe most obvious permissions for staging are the general permissions\nthat look similar to the permissions for most Liferay apps. These\npermissions are in the \\emph{Site Administration} → \\emph{Publishing} →\n\\emph{Staging} section of the Define Permissions menu. They include\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\emph{Access in Site Administration}\n\\item\n  \\emph{Add to Page}\n\\item\n  \\emph{Configuration}\n\\item\n  \\emph{Permissions}\n\\item\n  \\emph{Preferences}\n\\item\n  \\emph{View}\n\\end{itemize}\n\nAlso, there are some Site resource permissions that deal directly with\nstaging. These permissions are in the \\emph{Control Panel} →\n\\emph{Sites} → \\emph{Sites} section in the Define Permissions menu. The\nrelevant Site resource permissions related to staging are listed below:\n\n\\textbf{Add Page Variation:} Hides/shows the \\emph{Add Page Variation}\nbutton on the Staging Bar's Manage Page Variations screen.\n\n\\textbf{Add Site Pages Variation:} Hides/shows the \\emph{Add Site Pages\nVariation} button on the Staging Bar's Manage Site Page Variations\nscreen.\n\n\\textbf{Export/Import Application Info:} If the Publish Staging\npermission is not granted, hides/shows the application level\nExport/Import menu. The Configuration permission for the Export/Import\napp is also required.\n\n\\textbf{Export/Import Pages:} If the Publish Staging permission is not\ngranted, hides/shows the Export/Import app in the Site Administration\nmenu.\n\n\\textbf{Manage Staging:} Hides/shows the Staging Configuration menu in\nthe Site Administration → \\emph{Publishing} → \\emph{Staging} →\n\\emph{Options} (\\includegraphics{./images/icon-options.png}) menu.\n\n\\textbf{Publish Application Info:} Hides/shows the application level\nStaging menu.\n\n\\textbf{Publish Staging:} Hides/shows the \\emph{Publish to Live} button\non the Staging Bar and hides/shows the \\emph{Add Staging Process} button\n(\\includegraphics{./images/icon-add.png}) in the Site Administration\nmenu's Staging app. This permission automatically applies the\n\\emph{Export/Import Application Info}, \\emph{Export/Import Pages}, and\n\\emph{Publish Application Info} permission functionality regardless of\nwhether they're unselected.\n\n\\textbf{View Staging:} If Publish Staging, Manage Pages, Manage Staging,\nor Update permissions are not granted, hides/shows the Site\nAdministration menu's Staging app.\n\nNotice that some of the permissions above are related to the\nexport/import functionality. Since these permissions are directly\naffected by the Publish Staging permission, they are important to note.\nVisit the\n\\href{/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content}{Importing/Exporting\nPages and Content} section for more details on importing/exporting Site\nand page content.\n\n\\chapter{Scheduling Web Content\nPublication}\\label{scheduling-web-content-publication}\n\nLiferay's Web Content framework lets you define when your content goes\nlive. You can determine when the content is displayed, expired, and/or\nreviewed. This is an excellent way to keep your Site current and free\nfrom outdated (and perhaps incorrect) information.\n\nThe scheduler is built right into the Properties menu your users access\nwhen adding web content. To access this menu, click the \\emph{Options}\ngear (\\includegraphics{./images/icon-gear.png}) and open the Schedule\ndropdown menu.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/web-content-schedule.png}\n\\caption{The web content scheduler can be easily accessed from the right\npanel of the page.}\n\\end{figure}\n\nThe scheduler offers several configurable options:\n\n\\textbf{Display Date:} Sets (within a minute) when content will be\ndisplayed.\n\n\\textbf{Expiration Date:} Sets a date to expire the content. The default\nis one year.\n\n\\textbf{Never Expire:} Sets your content to never expire.\n\n\\textbf{Review Date:} Sets a content review date.\n\n\\textbf{Never Review:} Sets the content to never be reviewed.\n\nAs an example, you'll step through the process of scheduling a web\ncontent article.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Navigate to the Product Menu → \\emph{Content \\& Data} → \\emph{Web\n  Content}.\n\\item\n  Create a new web content article by selecting the \\emph{Add Web\n  Content} button (\\includegraphics{./images/icon-add.png}) →\n  \\emph{Basic Web Content}.\n\\item\n  Add content for your web content article.\n\\item\n  Select the \\emph{Schedule} tab from the web content's Properties menu.\n  Configure the publication schedule.\n\\item\n  Click \\emph{Publish}. Your web content article is now created and\n  abides by the scheduling parameters you've set.\n\\end{enumerate}\n\nWhen you set a Display Date for an existing article it does not affect\nprevious versions of the article. If a previous version is published, it\nremains the same until the new version is scheduled to display. However,\nthe expiration date affects all versions of the article. Once an article\nhas expired, no version of that article appears.\n\n\\noindent\\hrulefill\n\n\\textbf{Tip:} If you want only the latest version of articles to expire,\nand not every past version, go to \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{System Settings} → \\emph{Web Content} →\n\\emph{Virtual Instance Scope} → \\emph{Web Content} and uncheck\n\\emph{Expire All Article Versions Enabled}. This makes the previously\napproved version of an article appear if the latest version expires.\n\n\\noindent\\hrulefill\n\nThe scheduling feature gives you great control in managing when, and for\nhow long, your web content is displayed on your Site. Additionally, you\ncan determine when your content should be reviewed for accuracy and/or\nrelevance. This makes it possible to manage your growing inventory of\ncontent.\n\n\\chapter{Managing Apps}\\label{managing-apps}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nApps run on the platform Liferay DXP provides. The web experience\nmanagement, collaboration, and business productivity features all\nconsist of apps. Even the Control Panel consists of configuration apps.\nYou can also add to or change built-in functionality by installing other\napps. There are several ways to manage, find, and install apps. This\nsection covers these topics and more.\n\n\\chapter{Managing and Configuring\nApps}\\label{managing-and-configuring-apps}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay DXP is a platform for deploying apps that comprise modules and\ncomponents. It has conveniences for managing apps and app management\nbest practices for maximizing stability. Best practices in production\nenvironments involve stopping the server before applying changes, but in\ncases where this isn't feasible, you can ``auto deploy'' changes several\ndifferent ways.\n\nThere are two places in the Control Panel where you can manage and\nconfigure apps: the \\emph{App Manager} and the \\emph{Components}\nlisting. The App Manager manages apps in the OSGi framework. You can use\nthe App Manager to install, activate, deactivate, and delete apps. You\ncan manage apps at the app and module levels.\n\nThe Components listing views and manages apps at the OSGi component\nlevel. It differs from the App Manager by showing apps by type (portlet,\ntheme, and layout template), and setting app permissions. You can use\nthe Components listing to activate and deactivate apps, but it can't\ninstall or delete apps.\n\nStart with learning app management best practices in production, or\nwherever you want to maximize stability.\n\n\\section{Managing Apps in Production}\\label{managing-apps-in-production}\n\nNot all apps are designed to be ``auto deployed''---deployed while the\nserver is running. Deploying that way can cause instabilities, such as\nclass loading leaks and memory leaks. On production systems, avoid\n``auto deploying'' apps and configurations whenever possible.\n\nIf you're installing an app or a component configuration on a production\nsystem and stopping the server is feasible, follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Stop your server.\n\\item\n  Copy your app (\\texttt{.lpkg}, module \\texttt{.jar}, or plugin\n  \\texttt{.war}) to your \\texttt{{[}Liferay\\ \\ \\ \\ \\ Home{]}/deploy}\n  folder, or copy your component configuration (\\texttt{.config} file)\n  to the \\texttt{{[}Liferay\\ Home{]}/osgi/configs} folder. The\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  folder is typically the app server's parent folder.\n\\item\n  Start your server.\n\\end{enumerate}\n\nIf you're running in cluster, follow the instructions for\n\\href{/docs/7-2/deploy/-/knowledge_base/d/updating-a-cluster}{updating a\ncluster}.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Avoid repeatedly ``auto deploying'' new versions of\napps that aren't designed for ``auto deployment''.\n\n\\noindent\\hrulefill\n\nIf it's not feasible to stop your server or you're app \\emph{is}\ndesigned for ``auto deployment'', Liferay DXP provides several ``auto\ndeployment'' conveniences. Except where stopping/starting the server is\nexplicitly mentioned, the practices described in the rest of this\narticle and in the following articles involve ``auto deployment''.\n\n\\section{Using the App Manager}\\label{using-the-app-manager}\n\nAccess the App Manager by selecting \\emph{Control Panel} → \\emph{Apps} →\n\\emph{App Manager}. The App Manager lists your apps. The \\emph{Filter\nand Order} menu lets you filter and order by category, status, or title.\nClick the up or down arrows to perform an ascending or descending sort,\nrespectively. To search for an app or module, use the search bar. This\nis often the quickest way to find something.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/app-manager.png}\n\\caption{The App Manager lets you manage the apps, modules, and\ncomponents installed in your Liferay DXP instance.}\n\\end{figure}\n\nEach item listed in the table contains a description (if available),\nversion, and status. Here are the statuses:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\textbf{Installed:} The item is installed to Liferay DXP.\n\\item\n  \\textbf{Resolved:} The item's dependencies are active. Resolved items\n  can typically be activated. Some items, however, can't be activated\n  and are intended to remain in the Resolved state (e.g., WSDD modules\n  containing SOAP web services).\n\\item\n  \\textbf{Active:} The item is running in Liferay DXP.\n\\end{itemize}\n\nClicking each item's Actions button\n(\\includegraphics{./images/icon-actions.png}) brings up a menu that lets\nyou activate, deactivate, or uninstall that item.\n\nTo view an item's contents, click its name in the table. If you click an\napp, the app's modules are listed. If you click a module, the module's\ncomponents and portlets appear. The component level is as far down as\nyou can go without getting into the source code. At any level in the App\nManager, a link trail appears that lets you navigate back in the\nhierarchy.\n\nFor information on using the App Manager to install an app, see\n\\href{/docs/7-2/user/-/knowledge_base/u/installing-apps-manually}{Installing\nApps Manually}.\n\nNext, you'll learn how to use the Components listing.\n\n\\section{Using the Components\nListing}\\label{using-the-components-listing}\n\nAccess the components listing by selecting \\emph{Control Panel} →\n\\emph{Configuration} → \\emph{Components}. The components listing first\nshows a table containing a list of installed portlets. Select the type\nof component to view---portlets, themes, or layout templates---by\nclicking the matching tab on top of the table. To configure a component,\nselect its name in the table or select \\emph{Edit} from its Actions\nbutton (\\includegraphics{./images/icon-actions.png}). Doing either opens\nthe same configuration screen.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/components-list.png}\n\\caption{The components listing lets you manage the portlets, themes,\nand layout templates installed in your Liferay DXP instance.}\n\\end{figure}\n\nThe configuration screen lets you view a component's module ID and\nplugin ID, activate or deactivate the component, and change the\ncomponent's Add to Page permission. The component's module ID and plugin\nID appear at the top of the screen. You can activate or deactivate a\ncomponent by checking or unchecking the \\emph{Active} checkbox,\nrespectively. To change a component's Add to Page permission for a role,\nselect the role's \\emph{Change} button in the permissions table. This\ntakes you to \\emph{Control Panel} → \\emph{Users} → \\emph{Roles}, where\nyou can change the component's permissions for the selected role.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/components-configuration.png}\n\\caption{You can activate or deactivate a component, and change its\npermissions.}\n\\end{figure}\n\n\\chapter{Using the Liferay\nMarketplace}\\label{using-the-liferay-marketplace}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nLiferay Marketplace is a hub for sharing, browsing, and downloading\napps. Marketplace leverages the entire Liferay ecosystem to release and\nshare apps in a user-friendly, one-stop shop.\n\nThere are two ways to access the Marketplace.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  \\textbf{Via the website:} Using your favorite browser, navigate to the\n  Marketplace at\n  \\href{https://web.liferay.com/marketplace}{web.liferay.com/marketplace}.\n  If you're new to Marketplace, this is the easiest way to access it.\n  You can browse Marketplace without signing in with your liferay.com\n  account.\n\\item\n  \\textbf{Via the Control Panel:} In the Control Panel, navigate to\n  \\emph{Apps} → \\emph{Store}. To view Marketplace, you must sign in with\n  your liferay.com account.\n\\end{enumerate}\n\nNo matter how you access Marketplace, you'll see the same content. Note\nthat to download apps, you must have a\n\\href{https://www.liferay.com}{liferay.com} account and agree to the\nMarketplace Terms of Use.\n\nHere you'll learn how to,\n\n\\begin{itemize}\n\\item\n  \\hyperref[finding-and-purchasing-apps]{Find and purchase apps}\n\\item\n  \\hyperref[managing-purchased-apps]{Manage purchased apps}\n\\item\n  \\hyperref[renewing-a-purchased-app]{Renew purchased apps}\n\\end{itemize}\n\nStart with finding and purchasing the apps you want.\n\n\\section{Finding and Purchasing Apps}\\label{finding-and-purchasing-apps}\n\nIf you've used an app store before, Marketplace should be familiar.\nYou'll see apps in the center of the page, in the following sections:\n\n\\begin{itemize}\n\\item\n  Featured Apps: Liferay features a different set of apps each month.\n\\item\n  New and Interesting: The latest apps added to Marketplace.\n\\item\n  Most Viewed in the Past Month: The top 5 most viewed apps in the last\n  month.\n\\item\n  Themes / Site Templates: Apps that change your Liferay instance's look\n  and feel.\n\\item\n  App categories: Communication, productivity, security, etc.\n\\item\n  Weekly Stats: The newest apps, latest apps updated, and trend chart\n  for app downloads and views.\n\\end{itemize}\n\nEach section's \\emph{See All} link shows more section info. At the top\nof the page, you can search Marketplace by category, Liferay DXP\nversion, and price. To browse by category, click the \\emph{Categories}\nmenu at the top of the page.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/marketplace-homepage.png}\n\\caption{The Liferay Marketplace home page lets you browse and search\nfor apps.}\n\\end{figure}\n\nClick an app to view its details. This includes its description,\nscreenshots, price, latest version, number of downloads, a link to the\ndeveloper's website, a link to the app's license agreement, and a\npurchase button (labeled Free or Buy, depending on the price). You can\nalso view the app's version history, read reviews, or write your own\nreview.\n\nThe purchase button prompts you to choose a purchase type. You can\npurchase an app for your personal account, or for a Liferay project\nassociated with your company. If you have the necessary permissions, you\ncan also create a new project for your company. Once you select a\npurchase type, accept the EULA and Terms of Service, and click\n\\emph{Purchase}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/marketplace-app-details.png}\n\\caption{Click an app to view its details.}\n\\end{figure}\n\nOnce you purchase an app, you can download and install it.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Not all apps are designed to be ``auto\ndeployed''---deployed while the server is running. Deploying that way\ncan cause instabilities, such as class loading leaks and memory leaks.\nOn production systems, avoid ``auto deploying'' apps whenever possible.\nSee the\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-and-configuring-apps\\#managing-apps-in-production}{best\npractices for managing apps in production}.\n\n\\noindent\\hrulefill\n\nAn app downloads and installs immediately if you purchase it from the\nControl Panel. If you purchase the app on the Marketplace website,\nhowever, your receipt is displayed immediately after purchase. To\ndownload the app, click the \\emph{See Purchased} button on the bottom of\nthe receipt, and then click the \\emph{App} button to start the download.\nYou must then\n\\href{/docs/7-2/user/-/knowledge_base/u/installing-apps-manually}{install\nthe app manually}. Alternatively, you can use Marketplace from the\nControl Panel to download and install the app after purchase on the\nMarketplace website. The next section shows you how to do this.\n\nNote that sometimes administrators disable automatic app installations\nso they can manage installations manually. In this case, Marketplace\napps downloaded from the Control Panel are placed in the \\texttt{deploy}\nfolder in\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}.\nAdministrators must then manually install the app from this folder.\nManual install is also required if the server is behind a corporate\nfirewall or otherwise lacks direct Marketplace access. Regardless of how\nthe app is downloaded, the manual install process is the same. For\ndetails, see the article\n\\href{/docs/7-2/user/-/knowledge_base/u/installing-apps-manually}{Installing\nApps Manually}.\n\n\\section{Managing Purchased Apps}\\label{managing-purchased-apps}\n\n\\noindent\\hrulefill\n\n\\textbf{Important}: When uninstalling an app or module, make sure to use\nthe same agent you used to install the app. For example, if you\ninstalled it with Marketplace, uninstall it with Marketplace. If you\ninstalled it with the file system, use the\n\\href{/docs/7-2/user/-/knowledge_base/u/installing-apps-manually}{file\nsystem} to uninstall it. If you installed it with the App Manager,\nhowever, use\n\\href{/docs/7-2/user/-/knowledge_base/u/blacklisting-osgi-bundles-and-components}{Blacklisting}\nto uninstall it.\n\n\\noindent\\hrulefill\n\nThere are two places to manage your purchased apps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Your \\href{https://www.liferay.com}{liferay.com} account's home page.\n  After signing in, click the user menu at the top-right and select\n  \\emph{Account Home}. Note that your home page is distinct from your\n  profile page. Your home page is private, while your profile page is\n  public. On your home page, select \\emph{Apps} from the menu on the\n  left to view your projects. Select a project to view its registered\n  apps. Clicking an app lets you view its versions. You can download the\n  version of the app that you need. This is especially useful if you\n  need a previous version of the app, or can't download the app from the\n  Control Panel.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/marketplace-project-apps.png}\n  \\caption{You can manage your purchased apps from your liferay.com\n  account's home page.}\n  \\end{figure}\n\\item\n  From the Control Panel. Navigate to \\emph{Apps} → \\emph{Purchased} to\n  see your purchased apps. A button next to each app lets you install or\n  uninstall the app. If the app isn't compatible with your Liferay DXP\n  version, \\emph{Not Compatible} is displayed in place of the button.\n  Additional compatibility notes are also shown, such as whether a newer\n  version of the app is available. You can also search for an app here\n  by project, category, and title. Clicking the app takes you to its\n  Marketplace entry.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/marketplace-purchased.png}\n  \\caption{You can also manage your purchased apps from within a running\n  Liferay instance.}\n  \\end{figure}\n\\end{enumerate}\n\n\\section{Renewing a Purchased App}\\label{renewing-a-purchased-app}\n\nTo continue using a purchased app whose license terms are non-perpetual,\nyou must renew your app subscription, register your server to use the\napp, and generate a new activation key to use on your server. Here are\nthe steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to \\url{https://web.liferay.com/marketplace}.\n\\item\n  Click your profile picture in the upper right corner and select\n  \\emph{Purchased Apps}. The Purchased Apps page appears and shows your\n  app icons organized by project.\n\\item\n  Click your app's icon. Your app's details page appears.\n\\item\n  Click \\emph{Manage Licenses}.\n\\item\n  Select \\emph{Register New Server}.\n\\item\n  Select the most recent \\emph{Order ID} (typically the order that has\n  no registered servers).\n\\item\n  Fill in your server's details.\n\\item\n  Click \\emph{Register}.\n\\item\n  Click \\emph{Download}. The new app activation key to use on your\n  server downloads.\n\\item\n  Copy the activation key file to your \\texttt{deploy/} folder in your\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{\\texttt{{[}Liferay\\ \\ \\ \\ \\ Home{]}}}.\n\\end{enumerate}\n\nYou can continue using the application on your server.\n\n\\chapter{Installing Apps Manually}\\label{installing-apps-manually}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nBy default, apps you download from the Control Panel via Liferay\nMarketplace install automatically. But what if the app you want to\ninstall isn't on Marketplace? What if all you have is the app's file? In\nthis case, you must install the app manually. Here you'll learn how to\ninstall any app manually.\n\n\\noindent\\hrulefill\n\n\\textbf{Warning:} Not all apps are designed to be ``auto\ndeployed''---deployed while the server is running. Deploying that way\ncan cause instabilities, such as class loading leaks and memory leaks.\nOn production systems, avoid ``auto deploying'' apps whenever possible.\nSee the\n\\href{/docs/7-2/user/-/knowledge_base/u/managing-and-configuring-apps\\#managing-apps-in-production}{best\npractices for managing apps in production}.\n\n\\noindent\\hrulefill\n\n\\noindent\\hrulefill\n\n\\textbf{Important}: When uninstalling an app or module, make sure to use\nthe same agent you used to install the app. For example, if you\ninstalled it with Marketplace, uninstall it with\n\\href{/docs/7-2/user/-/knowledge_base/u/using-the-liferay-marketplace}{Marketplace}.\nIf you installed it with the file system, use the file system to\nuninstall it. If you installed it with the App Manager, however, use\n\\href{/docs/7-2/user/-/knowledge_base/u/blacklisting-osgi-bundles-and-components}{Blacklisting}\nto uninstall it.\n\n\\noindent\\hrulefill\n\n\\section{Using the Control Panel to Install\nApps}\\label{using-the-control-panel-to-install-apps}\n\nTo install an app manually from the Control Panel, navigate to\n\\emph{Control Panel} → \\emph{Apps} → \\emph{App Manager}, and select\n\\emph{Upload} from the options button\n(\\includegraphics{./images/icon-options.png}). In the Upload dialog,\nchoose the app on your machine and then click \\emph{Install}. When the\ninstall completes, close the dialog and you're ready to roll!\n\n\\section{Using Your File System to Install\nApps}\\label{using-your-file-system-to-install-apps}\n\nTo install an app manually on the Liferay DXP server, put the app in the\n\\texttt{{[}Liferay\\ Home{]}/deploy} folder (the\n\\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\nfolder is typically the app server's parent folder). That's it. The auto\ndeploy mechanism takes care of the rest.\n\nYou might now be thinking, ``Whoa there! What do you mean by `the rest?'\nWhat exactly happens here? And what if my app server doesn't support\nauto deploy?'' These are fantastic questions! When you put an app in the\n\\texttt{{[}Liferay\\ Home{]}/deploy} folder, the OSGi container deploys\nthe app to the appropriate subfolder in\n\\texttt{{[}Liferay\\ Home{]}/osgi}. By default, the following subfolders\nare used for apps matching the indicated file type:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  \\texttt{marketplace}: Marketplace LPKG packages\n\\item\n  \\texttt{modules}: OSGi modules\n\\item\n  \\texttt{war}: WAR files\n\\end{itemize}\n\nYou can, however, change these subfolders by setting the properties\n\\texttt{module.framework.base.dir} and\n\\texttt{module.framework.auto.deploy.dirs} in a\n\\href{/docs/7-2/deploy/-/knowledge_base/d/portal-properties}{\\texttt{portal-ext.properties}}\nfile. These properties define the \\texttt{{[}Liferay\\ Home{]}/osgi}\nfolder and its auto deploy subfolders, respectively. The default\nsettings for these properties in the\n\\href{https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html}{\\texttt{portal.properties}}\nfile are as follows:\n\n\\begin{verbatim}\nmodule.framework.base.dir=${liferay.home}/osgi\n\nmodule.framework.auto.deploy.dirs=\\\n    ${module.framework.base.dir}/configs,\\\n    ${module.framework.base.dir}/marketplace,\\\n    ${module.framework.base.dir}/modules,\\\n    ${module.framework.base.dir}/war\n\\end{verbatim}\n\nNote that the \\texttt{configs} subfolder isn't for apps: it's for\nconfiguration files\n\\href{/docs/7-2/user/-/knowledge_base/u/system-settings\\#exporting-and-importing-configurations}{imported\nfrom other Liferay DXP instances}.\n\nBut what happens if your app server doesn't support ``hot deploy''? No\nproblem! Liferay DXP's module framework (OSGi) enables auto deploy. Any\napp server running Liferay DXP therefore also supports this auto deploy\nmechanism.\n\n\\section{Manually Deploying an LPKG\nApp}\\label{manually-deploying-an-lpkg-app}\n\nWhen manually installing an LPKG app, the installation may hang with a\nserver log message like this:\n\n\\begin{verbatim}\n14:00:15,789 INFO  [com.liferay.portal.kernel.deploy.auto.AutoDeployScanner][AutoDeployDir:252] Processing Liferay Push 2.1.0.lpkg\n\\end{verbatim}\n\nThis happens when LPKG apps have the \\texttt{restart-required=true}\nproperty in their \\texttt{liferay-marketplace.properties} file (inside\nthe LPKG file). This property setting specifies that a server restart is\nrequired to complete the installation.\n\n\\chapter{App Types}\\label{app-types}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nThere are several different kinds of apps. Some apps can even contain\nother apps. The types of apps you can install include:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  OSGi Modules\n\\item\n  Portlets\n\\item\n  Web Plugins\n\\item\n  Templates\n\\item\n  Themes\n\\end{itemize}\n\nRead on to learn about these app types.\n\n\\section{OSGi Modules}\\label{osgi-modules}\n\nSince Liferay DXP runs on OSGi, apps can be implemented as OSGi modules.\nAn OSGi module is a JAR file adapted to run on OSGi. Although it's\npossible for a single module to implement a single app, an app typically\nconsists of multiple modules that are packaged together. Also note that\napps in OSGi modules aren't required to have a UI. For example, Liferay\nDXP can run OSGi modules that expand built-in APIs without requiring any\nuser interaction. This is crucial for developers that must leverage\ncustom APIs. By providing such an API via one or more OSGi modules, you\ncan let developers leverage your API.\n\nOSGi modules can also contain apps that have a UI: portlets. The next\nsection discusses these.\n\n\\section{Portlets}\\label{portlets}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/portlets}{Portlets} are\nsmall web applications that run in a portion of a web page. For example,\nthe built-in Blogs app is a portlet. Portlet applications, like servlet\napplications, are a Java standard implemented by various portal server\nvendors. The JSR-168 standard defines the portlet 1.0 specification, the\nJSR-286 standard defines the portlet 2.0 specification, and the JSR-362\nstandard defines the portlet 3.0 specification. A Java standard portlet\nshould be deployable on any portlet container that supports the\nstandard. Portlets are placed on the page in a certain order by the end\nuser and are served up dynamically by the portal server. This means\ncertain things that apply to servlet-based projects, such as control\nover URLs or access to the \\texttt{HttpServletRequest} object, don't\napply in portlet projects because the portal server generates these\nobjects dynamically.\n\nPortlets can be composed of OSGi modules (recommended), or contained in\nWAR files. For information on developing portlets see\n\\href{/docs/7-2/appdev/-/knowledge_base/a/web-front-ends}{Web\nFront-ends}.\n\n\\section{Web Plugins}\\label{web-plugins}\n\nWeb plugins are regular Java EE web modules designed to work with\nLiferay DXP. You can integrate with various Enterprise Service Bus (ESB)\nimplementations, as well as Single Sign-On implementations, workflow\nengines, and so on. These are implemented as web modules used by Liferay\nDXP portlets to provide functionality.\n\n\\section{Templates and Themes}\\label{templates-and-themes}\n\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/layout-templates-intro}{Templates}\nand\n\\href{/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction}{themes}\nare plugins that change Liferay DXP's appearance. Templates (layout\ntemplates) control how you can arrange portlets on a page. They make up\na page's body (the large area into which you can drag and drop\nportlets). There are several built-in layout templates. If you have a\ncomplex page layout (especially for your home page), you may wish to\ncreate a custom layout template of your own.\n\nThemes can completely transform Liferay DXP's look and feel. Most\norganizations have their own look and feel standards that apply to all\nof their web sites and applications. By using a theme plugin, an\norganization can apply these standards on Liferay DXP. There are many\navailable theme plugins on Liferay's web site and more are being added\nevery day. This makes it easy for theme developers, as they can\ncustomize existing themes instead of writing a new one from scratch.\n\n\\section{Liferay Marketplace App\nPackages}\\label{liferay-marketplace-app-packages}\n\nRegardless of app type, each\n\\href{https://web.liferay.com/marketplace}{Liferay Marketplace} app is\ndistributed in an LPKG package. The LPKG package contains Marketplace\nmetadata and the files the app needs to run. Note that it's possible for\nan LPKG package to contain multiple apps. For example, a single LPKG\npackage can contain several portlets. This is common in cases where an\napp requires a Control Panel portlet for administrators, and another\nportlet for end users.\n\n\\chapter{Blacklisting OSGi Bundles and\nComponents}\\label{blacklisting-osgi-bundles-and-components}\n\n{This document has been updated and ported to Liferay Learn and is no\nlonger maintained here.}\n\nBlacklists are used for good and evil. An evil blacklist penalizes\nunfairly; a good blacklist protects. Liferay DXP's OSGi bundle and\ncomponent blacklists are files that prevent particular bundles from\ninstalling and particular components from enabling. This saves you the\ntrouble of uninstalling and disabling them individually with the\nApplication Manager, Components list, or Gogo shell.\n\n\\section{Blacklisting Bundles}\\label{blacklisting-bundles}\n\nLiferay DXP removes any installed OSGi bundles on the blacklist.\nBlacklisted bundles therefore can't be installed. The log reports each\nbundle uninstallation.\n\nFollow these steps to blacklist bundles:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the Control Panel, navigate to \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Module Container}.\n\\item\n  In the Bundle Blacklist screen, add the bundle symbolic names (see the\n  table below) for the Module JARs, LPKG files, or WARs to uninstall.\n  Click the \\emph{Save} button when you're finished.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/bundle-blacklist-configuration.png}\n  \\caption{This blacklist uninstalls the\n  \\texttt{com.liferay.docs.greeting.api} bundle, Liferay Marketplace\n  LPKG, and \\texttt{classic-theme} WAR.}\n  \\end{figure}\n\\item\n  To export the blacklist, click its Actions button\n  (\\includegraphics{./images/icon-actions.png}) and then click\n  \\emph{Export}. The blacklist configuration file then downloads\n  (\\texttt{com.liferay.portal.bundle.blacklist.internal.BundleBlacklistConfiguration.config}).\n  Here are contents from an example file:\n\n\\begin{verbatim}\nblacklistBundleSymbolicNames=[\"com.liferay.docs.greeting.api\",\"Liferay\\ Marketplace\",\"classic-theme\"]\n\\end{verbatim}\n\\item\n  Add the bundle symbolic names of any bundles not already listed that\n  you want to prevent from installing.\n\n  \\textbf{Important}: Configuration values can't contain extra spaces.\n  Extra spaces can short-circuit lists or invalidate the configuration\n  entry.\n\\item\n  To deploy the configuration file, copy it into the folder\n  \\texttt{{[}Liferay\\_Home{]}/osgi/configs}. The\n  \\href{/docs/7-2/deploy/-/knowledge_base/d/liferay-home}{Liferay Home}\n  folder is typically the app server's parent folder.\n\\end{enumerate}\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: Blacklisting an LPKG uninstalls all of its internal\nbundles.\n\n\\noindent\\hrulefill\n\n\\textbf{Blacklist Bundle Symbolic Names}\n\n\\noindent\\hrulefill\n\n\\begin{longtable}[]{@{}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.4167}}\n  >{\\raggedright\\arraybackslash}p{(\\columnwidth - 2\\tabcolsep) * \\real{0.5833}}@{}}\n\\toprule\\noalign{}\n\\begin{minipage}[b]{\\linewidth}\\raggedright\nType\n\\end{minipage} & \\begin{minipage}[b]{\\linewidth}\\raggedright\nBundle Symbolic Name\n\\end{minipage} \\\\\n\\midrule\\noalign{}\n\\endhead\n\\bottomrule\\noalign{}\n\\endlastfoot\nModule JAR & \\texttt{Bundle-SymbolicName} in \\texttt{bnd.bnd} or\n\\texttt{MANIFEST.MF} file \\\\\nLPKG & LPKG file name without the \\texttt{.lpkg} extension \\\\\nWAR & Servlet context name in \\texttt{liferay-plugin-package.properties}\nfile or the WAR file name (minus \\texttt{.war}), if there is no servlet\ncontext name property \\\\\n\\end{longtable}\n\n\\noindent\\hrulefill\n\n\\section{Reinstalling Blacklisted\nBundles}\\label{reinstalling-blacklisted-bundles}\n\nTo reinstall and permit installation of blacklisted OSGi bundles, follow\nthese steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the configuration file\n  \\texttt{com.liferay.portal.bundle.blacklist.internal.BundleBlacklistConfiguration.config}.\n\\item\n  Remove the symbolic names of the module JARs, LPKGs, or WARs from the\n  \\texttt{blacklistBundleSymbolicNames} list and save the file.\n\\end{enumerate}\n\nTo reinstall \\emph{all} the blacklisted bundles execute one of these\noptions:\n\n\\begin{itemize}\n\\tightlist\n\\item\n  Remove the configuration file.\n\\item\n  Uninstall the bundle \\texttt{com.liferay.portal.bundle.blacklist}\n  using the\n  \\href{/docs/7-2/user/-/knowledge_base/u/managing-and-configuring-apps\\#using-the-app-manager}{Application\n  Manager} or\n  \\href{/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell}{Felix\n  Gogo Shell}.\n\\end{itemize}\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: To temporarily reinstall a bundle that's been\nblacklisted, you can remove its symbolic name from the Bundle Blacklist\nmodule in \\emph{System Settings} and click the \\emph{Update} button. If\nyou want the bundle to install on subsequent server startup, make sure\nto remove the bundle's symbolic name from any existing blacklist\nconfiguration file in the \\texttt{{[}Liferay\\_Home{]}/osgi/configs}\nfolder.\n\n\\noindent\\hrulefill\n\nThe log reports each bundle installation.\n\n\\section{Blacklisting Components}\\label{blacklisting-components}\n\nFollow these steps to blacklist components:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  In the Control Panel, navigate to \\emph{Configuration} → \\emph{System\n  Settings} → \\emph{Module Container}.\n\\item\n  In the Component Blacklist screen, add the names of components to\n  disable and click the \\emph{Save} button.\n\\end{enumerate}\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/component-blacklist-configuration.png}\n\\caption{This blacklist disables the components\n\\texttt{com.liferay.portal.security.ldap.internal.authenticator.LDAPAuth}\nand\n\\texttt{com.liferay.ip.geocoder.sample.web.internal.portlet.IPGeocoderSamplePortlet}.}\n\\end{figure}\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\setcounter{enumi}{2}\n\\item\n  To export the blacklist, click on the Component Blacklist module's\n  Actions button (\\includegraphics{./images/icon-actions.png}) and then\n  click \\emph{Export}. The blacklist configuration file then downloads\n  (\\texttt{com.liferay.portal.component.blacklist.internal.ComponentBlacklistConfiguration.config}).\n  Here are contents from an example file:\n\n\\begin{verbatim}\nblacklistComponentNames=[\"com.liferay.portal.security.ldap.internal.authenticator.LDAPAuth\",\"com.liferay.ip.geocoder.sample.web.internal.portlet.IPGeocoderSamplePortlet \"]\n\\end{verbatim}\n\\item\n  Add the names of any components not already listed (e.g., components\n  of bundles not yet installed) that you want to prevent from\n  activating.\n\n  \\textbf{Important}: Configuration values can't contain extra spaces.\n  Extra spaces can short-circuit lists or invalidate the configuration\n  entry.\n\\item\n  To deploy the configuration file, copy it into the folder\n  \\texttt{{[}Liferay\\_Home{]}/osgi/configs}. The Liferay Home folder is\n  typically the app server's parent folder.\n\\end{enumerate}\n\n\\section{Re-enabling Blacklisted\nComponents}\\label{re-enabling-blacklisted-components}\n\nTo re-enable and permit enabling of blacklisted components, follow these\nsteps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Open the configuration file\n  \\texttt{com.liferay.portal.component.blacklist.internal.ComponentBlacklistConfiguration.config}.\n\\item\n  Remove the names of the components from the\n  \\texttt{blacklistComponentNames} list and save the file.\n\\end{enumerate}\n\nTo enable \\emph{all} the blacklisted components, remove the\nconfiguration file.\n\n\\noindent\\hrulefill\n\n\\textbf{Note}: To temporarily reactivate a blacklisted component, remove\nits name from the Component Blacklist Configuration module in System\nSettings and click \\emph{Update}. If you want the component to activate\non subsequent server startup, make sure to remove the component's name\nfrom any existing component blacklist configuration file in the\n\\texttt{{[}Liferay\\_Home{]}/osgi/configs} folder.\n\n\\chapter{Polls}\\label{polls}\n\nHow can The Lunar Resort stay connected with its earthbound clientèle\nfrom 239,000 miles away? Make them feel really involved and enthusiastic\nabout the resort by asking them for feedback. You're not just creating a\npoll, you're making connections.\n\nUse Polls to find out what your site visitors are thinking and keep them\nengaged with your site's content.\n\nTwo applications make and display a poll: the \\emph{Polls} application\nin the Site Menu and the \\emph{Polls Display} widget you add to a page.\n\n\\section{Creating a Poll}\\label{creating-a-poll}\n\nFrom the Site Menu, go to \\emph{Content \\& Data} → \\emph{Polls}.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click the \\includegraphics{./images/icon-add.png} button and fill out\n  the form.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/polls-add-new-question.png}\n  \\caption{Besides the Title and the Polls Question, you must enter data\n  for each of the Choices fields when creating a new poll.}\n  \\end{figure}\n\n  \\textbf{Title:} (Required) Enter the name of the poll question.\n\n  \\textbf{Polls Question:} (Required) Enter the text of the poll\n  question.\n\n  \\textbf{Expiration Date:} Enter the date and time you want the poll to\n  expire.\n\n  \\textbf{Choices:} Enter at least two options for the poll question.\n\n  \\textbf{Add Choice:} Enter additional answer options for the poll\n  question.\n\n  \\textbf{Permissions:} Manage who can view and edit the poll.\n\\item\n  Click \\emph{Save} to add the poll to the Polls application.\n\\end{enumerate}\n\nOnce a poll is created, the Polls Display portlet publishes it until it\nexpires or is deleted. Set an expiration date for a poll by selecting\nthe day and time in the Add Poll form. The default is set to \\emph{Never\nExpire}.\n\nWhen a published poll expires, the poll results are displayed, but users\ncan't add new entries. To remove an expired poll from a page, remove the\nPoll Display portlet or configure it to show another poll question. See\nthe section below for more details about the Polls Display portlet.\n\n\\emph{Permissions} are set on individual polls. Use permissions, for\nexample, to allow some privileged users to vote on a certain poll\nquestion, while others can only view it.\n\nCreating a poll is fairly straightforward. Next, complete the two-step\nprocess and put your poll on a page.\n\n\\section{Adding a Poll to a Page}\\label{adding-a-poll-to-a-page}\n\nNow that you have created your poll question, you can present it to your\nusers:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to a page and add a Polls Display widget from \\emph{Add} →\n  \\emph{Widgets} → \\emph{Content Management}.\n\\item\n  Click \\emph{Please configure this portlet to make it visible to all\n  users.}\n\\item\n  In the dialog box that appears, select the poll to display.\n\\item\n  Click \\emph{Save}.\n\\end{enumerate}\n\nOnce the poll question has been placed on the page, perform other tasks\nusing the icons at the bottom of the portlet.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/poll-buttons.png}\n\\caption{These buttons provide shortcuts to the widget's configuration,\nas well as to some of the Polls Application's functionality.}\n\\end{figure}\n\n\\textbf{Edit Question:} Displays a similar dialog box to the one used to\ncreate the poll.\n\n\\textbf{Select Poll:} Displays the same dialog box as Configuration,\nallowing you to choose different polls from the drop-down menu.\n\n\\textbf{Add:} Allows you to create a new poll.\n\n\\section{Viewing Poll Results}\\label{viewing-poll-results}\n\nAll the polls you create appear in the Polls portlet in the Site Menu →\n\\emph{Content \\& Data} → \\emph{Polls}. When users vote in the poll, the\ndata is collected here. Click on a poll to see a breakdown of the\nresults.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/polls-results.png}\n\\caption{Selecting a poll in the Polls portlet puts the data at your\nfingertips.}\n\\end{figure}\n\nIf you click on one of the listed \\emph{Charts}, the portlet generates\nan appropriate visualization of the data.\n\nBelow this is an item called \\emph{Charts}. This option shows the poll\nresults represented in various graphs. The graphs are \\emph{Area},\n\\emph{Horizontal Bar}, \\emph{Line}, \\emph{Pie}, and \\emph{Vertical Bar}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/polls-results-vertical-bar.png}\n\\caption{This is what the vertical bar graph for the Lunar Resort poll\nresults looks like.}\n\\end{figure}\n\nThere is also a listing of the users who voted in the poll, how they\nvoted, and a time/date stamp of when their votes were cast. Registered\nusers are represented by name. Guest users have a blank \\emph{User}\nfield.\n\n\\chapter{Using the Calendar}\\label{using-the-calendar}\n\nThe Calendar widget is an updated, digitized, 3D-printed sundial. Okay,\nit's really a tool for storing and sharing scheduled events. It's a\npersonal planner for individual users, a shared calendar for an entire\nsite, or both at the same time. It can be used to create multiple\ncalendars for a single Site or User, to overlay the events stored in\nmultiple calendars for simultaneous view, to send email reminders to\nusers, and more.\n\n\\noindent\\hrulefill\n\n\\textbf{Note:} The calendar supports social activities. Whenever a\ncalendar event is added or updated, a corresponding social activity\nnotification is created. If the event was added or updated in a calendar\nthat the current user has permission to view, the social activity is\nviewable in the Activities widget.\n\n\\noindent\\hrulefill\n\n\\section{Configuring the Calendar}\\label{configuring-the-calendar}\n\nOnce the Calendar widget is on a page, open the\n\\includegraphics{./images/icon-app-options.png} menu in the widget's\nheader and click \\emph{Configuration}.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/new-calendar-configuration.png}\n\\caption{The Setup → User Settings tab provides the options you need to\nget started quickly.}\n\\end{figure}\n\nFrom the \\emph{User Settings} tab, customize the calendar's default view\nand settings.\n\n\\textbf{Time Format:} Choose \\emph{Locale}, \\emph{AM/PM}, or \\emph{24\nHour}. \\emph{Locale} is a dynamic setting that chooses whether to\ndisplay the time in \\emph{AM/PM} or \\emph{24 Hour} format, based on the\npreferences set by the User's locale. \\emph{AM/PM} displays times such\nas 8AM or 11PM. The \\emph{24 Hour} time format displays times such as\n08:00 and 23:00.\n\n\\textbf{Default Duration:} Choose an event duration. When you add a new\nevent to the calendar, the time you set here specifies how long events\nlast by default.\n\n\\textbf{Default View:} Choose \\emph{Day}, \\emph{Week}, \\emph{Month} or\n\\emph{Agenda}. This sets the default for when the calendar is first\ndisplayed, but the view can be changed by clicking the appropriate\nbutton at the top-right of the widget.\n\n\\textbf{Week Starts On:} Choose \\emph{Sunday}, \\emph{Monday}, or\n\\emph{Saturday}.\n\n\\textbf{Time Zone:} Choose a time zone or check the \\emph{Use Global\nTime Zone} box.\n\nIf you check \\emph{Use Global Time Zone}, the time displayed depends on\nwhether it's being viewed by a logged-in user or a guest. If a user is\nlogged in, the Calendar displays events using the time zone set for the\nuser in \\emph{User Personal Menu} → \\emph{Account Settings} →\n\\emph{Preferences} → \\emph{Display Settings} → \\emph{Time Zone}. If the\nCalendar is viewed by a guest or a user who is not logged in, the\nCalendar displays events using the time zone set by the portal\nadministrator in \\emph{Control Panel} → \\emph{Configuration} →\n\\emph{Instance Settings} → \\emph{Platform} → \\emph{Localization} →\n\\emph{VIRTUAL INSTANCE SCOPE} → \\emph{Time Zone}.\n\nFrom the \\emph{Display Settings} tab, set the display behavior for the\ncalendar.\n\n\\textbf{Display Scheduler Only:} By default, the list of calendars and a\nmini-calendar view (used for quickly navigating to a particular date)\nare displayed. Check this to display only the scheduler (the large\ncalendar view showing the calendar and scheduled events).\n\n\\textbf{Display User Events:} Turns off the display of the current,\nlogged in User's personal calendar and events.\n\n\\textbf{Display Scheduler's Header:} If disabled, removes the ability to\ntoggle through the calendar views (for example, Day/Week/Month/Agenda)\nand access to the Add Event button.\n\n\\textbf{Enabled Views:} If one of the available views is disabled (Day,\nWeek, Month, Agenda), it disappears from the scheduler's header.\n\n\\textbf{Maximum Days to Display:} Set the maximum number of days to\ndisplay in the Agenda view.\n\n\\textbf{Maximum Events to Display:} Set the maximum number of events to\ndisplay in the Agenda view.\n\nUse the \\emph{RSS} tab to disable RSS subscription or configure the RSS\nbehavior.\n\nEnough with configuration. Next you'll learn how to use it.\n\n\\chapter{Using the Calendar Widget}\\label{using-the-calendar-widget}\n\nThe calendar widget displays a small monthly calendar showing an\noverview of upcoming events. A larger area shows the Scheduler, a more\ndetailed calendar with a number of options: you can set it to to display\na \\emph{Day}, \\emph{Week}, or \\emph{Month}, or choose a more\nevent-oriented \\emph{Agenda} setting.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/calendar-view.png}\n\\caption{The default view is set in configuration, but a user can change\nit at any time.}\n\\end{figure}\n\nTwo calendars are included by default when the widget is first added to\na page: a personal calendar for the current user and a Site calendar for\nthe current Site. These are displayed in the widget's lower left. Next\nto each calendar is a colored box: click it to show/hide that calendar's\nevents in the main viewing area.\n\n\\section{Adding New Calendars}\\label{adding-new-calendars}\n\nTo create a new personal calendar,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on the arrow to the right of the \\emph{My Calendars} header and\n  select \\emph{Add Calendar} from the menu.\n\\item\n  Fill in the \\emph{Add Calendar} form. Give the calendar a name and\n  description, set a time zone, and decide if it's your user's\n  \\emph{default calendar}---the one that is shown automatically whenever\n  the widget is displayed. You can also pick a color, which color codes\n  events whenever multiple calendar's events are displayed at once. You\n  can also decide to enable ratings or comments on the calendar's\n  events, and configure permissions.\n\\end{enumerate}\n\nTo edit an existing calendar instead of adding a new one, select\n\\emph{Manage Calendars} from the menu.\n\nTo add or edit a Site calendar, open the menu next to the header with\nthe Site's name.\n\n\\begin{figure}\n\\centering\n\\includegraphics{./images/new-calendar-manage-calendars.png}\n\\caption{Personal and Site calendars are shown in the lower left. This\nimage shows calendars belonging to User \\emph{Test Test} and Site\n\\emph{Liferay DXP}.}\n\\end{figure}\n\n\\section{Adding Events to a Calendar}\\label{adding-events-to-a-calendar}\n\nTo add events to a calendar,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on any day in the main viewing area to open an event creation\n  pop up. If you've selected the \\emph{Day} or \\emph{Week} view, you can\n  click on the specific time when your event begins.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/new-calendar-event-popup.png}\n  \\caption{When you click anywhere on the calendar, you'll see the event\n  creation pop up appear. Click \\emph{Edit} to specify details for your\n  event.}\n  \\end{figure}\n\\item\n  Name your event and assign it to a calendar. Click \\emph{Save} to\n  create the event immediately or \\emph{Edit} to enter additional\n  information.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/new-calendar-event-details.png}\n  \\caption{You can specify event details such as the event title, start\n  date, end date, description, location, and more.}\n  \\end{figure}\n\\item\n  If you clicked \\emph{Edit}, complete the \\emph{Edit Event} form. Enter\n  start and end times and enter a description. To schedule an event that\n  reoccurs, check the \\emph{Repeat} box and fill in the \\emph{Repeat}\n  pop up.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/new-calendar-event-repeat.png}\n  \\caption{The \\emph{Repeat} box allows you to specify whether an events\n  repeats daily, weekly, monthly, or yearly, how often it repeats, and\n  when (or if) it ends.}\n  \\end{figure}\n\\end{enumerate}\n\n\\section{Additional Event Functions}\\label{additional-event-functions}\n\nAt the bottom of the \\emph{Edit Event} form, there are several collapsed\nsections: \\emph{Details}, \\emph{Invitations}, \\emph{Reminders},\n\\emph{Categorization}, and \\emph{Related Assets}.\n\n\\subsection{Details}\\label{details-2}\n\nIn the Details section, you can move the event to another calendar and\nenter a location.\n\n\\subsection{Invitations}\\label{invitations}\n\nIn the invitations section, invite Users, Sites, or Calendar Resources\n(see the next tutorial for more on resources: in brief, a resource is\nanything you might need for an event---a conference room, a vehicle,\netc.). Follow these steps:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Enter the name of an invitee (User, Site, or Resource) in the\n  \\emph{Invitations} field. Hit \\emph{Enter} to add them to the\n  \\emph{Pending} column.\n\\item\n  Check the availability of invitees by clicking the arrow next to the\n  their names and selecting \\emph{Check Availability}. This displays\n  their calendars (assuming you have permission to view them).\n\\end{enumerate}\n\nAn automated email is sent to invitees who must navigate to the calendar\nwidget to respond. See below to customize the content of the invitation.\n\nWhen invitees respond to the invitation, their names move to the\n\\emph{Accepted}, \\emph{Declined}, or \\emph{Maybe} columns.\n\n\\subsection{Reminders}\\label{reminders}\n\nSchedule up to two email reminders to send to attendees. Reminders\ntranslate the time of the event into the recipients own time zone.\n\nSee below to customize the content of the reminder email.\n\n\\subsection{Categorization}\\label{categorization-2}\n\nTag your event or assign it to a category so it appears in appropriate\nsearch results and is published by any asset publisher set to publish\ncontent assigned to the same category.\n\n\\subsection{Related Assets}\\label{related-assets}\n\nList an asset---such as an agenda or supplementary material for a\nmeeting---as related to your event. Links to related assets are\ndisplayed in the \\emph{Event Details} window.\n\n\\subsection{Saving and Drafting Changes and Updating\nPermissions}\\label{saving-and-drafting-changes-and-updating-permissions}\n\nAt the very bottom of the Edit form is a set of buttons that let you\npublish the changes, save the changes as a draft, and configure the\nevent's permissions.\n\nGiving a user permission to add, delete, or update discussion allows\nthem to make, edit and remove comments on the event. The\n\\emph{Permissions} permission allows a Role to update permissions for\nthe event.\n\n\\section{Customizing Email\nNotifications}\\label{customizing-email-notifications}\n\nTo customize email notifications for event invitations and reminders,\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the Calendar widget, click on the arrow next to a calendar and\n  select \\emph{Calendar Settings}.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/calendar-email-note.png}\n  \\caption{Email templates apply to a single calendar and all its\n  events.}\n  \\end{figure}\n\\item\n  Click on the \\emph{Notification Templates} tab. Then select either the\n  \\emph{Invite Email} or the \\emph{Reminder Email} sub-tab.\n\\item\n  Edit the email as desired. At the bottom of the screen is a glossary\n  that specifies variables for terms that were set when you created the\n  event. Use these variables to refer to event-specific information,\n  such as the event's name, date or location. It's a good idea to\n  include a link to the event (use the variable\n  \\texttt{{[}\\$EVENT\\_URL\\${]}}) as users must navigate to the calendar\n  widget in order to respond.\n\\end{enumerate}\n\nClick \\emph{Save}. Now your notifications contain the proper text.\n\nThe next article covers setting up calendar resources and porting data\nfrom one installation to another.\n\n\\chapter{Calendar Resources and\nPorting}\\label{calendar-resources-and-porting}\n\nWith calendar resources, you can invite entities other than people to\nyour events. This is beneficial for finding the availability of\nimportant resources your event requires, like a conference room, laptop,\nor, at The Lunar Resort, the Sasquatch Space Suit used to scare guests\nout on Lunar hikes.\n\nAnother important topic is porting your calendar's data from one\ninstallation of Liferay DXP to another.\n\n\\section{Calendar Resources}\\label{calendar-resources}\n\nFollow these steps to add a new calendar resource:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Click on the \\emph{Resources} tab and click the\n  \\includegraphics{./images/icon-add.png} button to add a new resource.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/calendar-resources.png}\n  \\caption{Resources are accessed from the tab menu at the top of the\n  widget.}\n  \\end{figure}\n\\item\n  Fill in the \\emph{New Resource} form. Enter a name, give it a\n  description, and choose whether to set it as active. You can also tag\n  it, assign it to categories, and configure its permissions. Click\n  \\emph{Save}.\n\\end{enumerate}\n\nThe resource has its own calendar that was generated automatically (this\nis how users can check its availability when creating events). Just as\nwith Users, however, resources can have more than one calendar. Follow\nthese steps to assign a new calendar to the resource:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  Go to the widget's \\emph{Resources} tab, click the\n  \\includegraphics{./images/icon-actions.png} button next to the\n  resource, and select \\emph{View Calendars}.\n\\item\n  Click \\emph{Add Calendar} and continue just as if you were creating a\n  calendar for a user or a Site.\n\\end{enumerate}\n\nOnce a resource is created, invite it to your events just as you would\nan attendee.\n\n\\section{Exporting and Importing Calendar\nData}\\label{exporting-and-importing-calendar-data}\n\nLike other Liferay Applications, the calendar allows data to be exported\nor imported as\n\\href{/docs/7-2/user/-/knowledge_base/u/exporting-importing-widget-data}{LAR\nfiles}. As with all LAR files, data can only be ported between\ninstallations of the same version.\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the calendar widget, click the\n  \\includegraphics{./images/icon-app-options.png} button in the widget\n  header and select \\emph{Export/Import}.\n\\item\n  Enter a name for the LAR file (or use the default).\n\n  Under \\emph{Application}, choose whether to include the widget's\n  configuration in the LAR.\n\n  Under \\emph{Content}, choose how much historical data to export and\n  select the content types (calendars, resources, and events) to\n  include. You can also choose whether to include comments and ratings.\n\n  Check the appropriate boxes to select whether to include deletions and\n  permissions in the LAR.\n\\item\n  Click \\emph{Export}. When a success message displays (this may take a\n  few moments) you can click on the LAR's filename to download it.\n\n  \\begin{figure}\n  \\centering\n  \\includegraphics{./images/calendar-lar.png}\n  \\caption{This LAR is ready to be downloaded.}\n  \\end{figure}\n\\end{enumerate}\n\nFollow these steps to import a LAR:\n\n\\begin{enumerate}\n\\def\\labelenumi{\\arabic{enumi}.}\n\\item\n  From the calendar widget, click the\n  \\includegraphics{./images/icon-app-options.png} button in the widget\n  header and select \\emph{Export/Import}.\n\\item\n  Click the \\emph{Import} tab.\n\\item\n  Click \\emph{Choose File} or else drag-and-drop a LAR into the area\n  surrounded by a dotted line. Click \\emph{Continue}.\n\\item\n  Decide how much data you want to import:\n\n  Under \\emph{Application}, check the box to import the configuration\n  stored in the LAR or leave in unchecked to keep your current\n  configuration.\n\n  Under \\emph{Content}, decide which content types (calendars,\n  resources, and events) to import, and whether to include comments and\n  ratings.\n\n  Choose whether to import permissions and deletions, and decide whether\n  to delete your widget's existing data before the import.\n\\item\n  In the collapsible \\emph{Update Data} section, choose how data will be\n  updated.\n\n  \\textbf{Mirror:} The data will be imported along with a reference to\n  its source. This allows data to be updated rather than duplicated if\n  the same LAR is imported more than once.\n\n  \\textbf{Copy as New:} All data is imported as new entries. Repeat\n  imports will produce duplicates.\n\\item\n  In the \\emph{Authorship of the Content} section, choose whether to\n  keep the original author of the imported content (where available) or\n  to list the current user as the author.\n\\item\n  Click \\emph{Import}.\n\\end{enumerate}\n\nYour calendar is set up and ready to go! Better check it to see what's\nnext on the agenda.\n"
  },
  {
    "path": "book/using-liferay-dxp-72.tex",
    "content": "\\documentclass[11pt,openright,twoside]{memoir}\n\n% \\usepackage{ucs}\n\\usepackage[english]{babel}\n\\usepackage{fontspec}\n\\usepackage{graphicx}\n\\usepackage{calc}\n\\usepackage{hyperref}\n\\usepackage{enumerate}\n\\usepackage{ctable}\n%\\usepackage[labelformat=empty,font=small]{caption}\n\\usepackage{titlesec}\n\\usepackage{float}\n\\usepackage{morefloats}\n\\usepackage{wrapfig}\n\\usepackage{longtable}\n\\usepackage{geometry}\n\\usepackage{framed}\n\\usepackage{animate}\n\\defaultfontfeatures{Ligatures=TeX}\n\n% Begin stuff from Pandoc template \n\n\\usepackage{amsmath,amssymb}\n\\usepackage{iftex}\n\\ifPDFTeX\n  \\usepackage[T1]{fontenc}\n  \\usepackage[utf8]{inputenc}\n  \\usepackage{textcomp} % provide euro and other symbols\n\\else % if luatex or xetex\n  \\usepackage{unicode-math} % this also loads fontspec\n  \\defaultfontfeatures{Scale=MatchLowercase}\n  \\defaultfontfeatures[\\rmfamily]{Ligatures=TeX,Scale=1}\n\\fi\n\\usepackage{lmodern}\n\\ifPDFTeX\\else\n  % xetex/luatex font selection\n\\fi\n% Use upquote if available, for straight quotes in verbatim environments\n\\IfFileExists{upquote.sty}{\\usepackage{upquote}}{}\n\\IfFileExists{microtype.sty}{% use microtype if available\n  \\usepackage[]{microtype}\n  \\UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts\n}{}\n\\makeatletter\n\\@ifundefined{KOMAClassName}{% if non-KOMA class\n  \\IfFileExists{parskip.sty}{%\n    \\usepackage{parskip}\n  }{% else\n    \\setlength{\\parindent}{0pt}\n    \\setlength{\\parskip}{6pt plus 2pt minus 1pt}}\n}{% if KOMA class\n  \\KOMAoptions{parskip=half}}\n\\makeatother\n\\usepackage{xcolor}\n\\usepackage{color}\n\\usepackage{fancyvrb}\n\\newcommand{\\VerbBar}{|}\n\\newcommand{\\VERB}{\\Verb[commandchars=\\\\\\{\\}]}\n\\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\{\\}}\n% Add ',fontsize=\\small' for more characters per line\n\\newenvironment{Shaded}{}{}\n\\newcommand{\\AlertTok}[1]{\\textcolor[rgb]{1.00,0.00,0.00}{\\textbf{#1}}}\n\\newcommand{\\AnnotationTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textbf{\\textit{#1}}}}\n\\newcommand{\\AttributeTok}[1]{\\textcolor[rgb]{0.49,0.56,0.16}{#1}}\n\\newcommand{\\BaseNTok}[1]{\\textcolor[rgb]{0.25,0.63,0.44}{#1}}\n\\newcommand{\\BuiltInTok}[1]{\\textcolor[rgb]{0.00,0.50,0.00}{#1}}\n\\newcommand{\\CharTok}[1]{\\textcolor[rgb]{0.25,0.44,0.63}{#1}}\n\\newcommand{\\CommentTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textit{#1}}}\n\\newcommand{\\CommentVarTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textbf{\\textit{#1}}}}\n\\newcommand{\\ConstantTok}[1]{\\textcolor[rgb]{0.53,0.00,0.00}{#1}}\n\\newcommand{\\ControlFlowTok}[1]{\\textcolor[rgb]{0.00,0.44,0.13}{\\textbf{#1}}}\n\\newcommand{\\DataTypeTok}[1]{\\textcolor[rgb]{0.56,0.13,0.00}{#1}}\n\\newcommand{\\DecValTok}[1]{\\textcolor[rgb]{0.25,0.63,0.44}{#1}}\n\\newcommand{\\DocumentationTok}[1]{\\textcolor[rgb]{0.73,0.13,0.13}{\\textit{#1}}}\n\\newcommand{\\ErrorTok}[1]{\\textcolor[rgb]{1.00,0.00,0.00}{\\textbf{#1}}}\n\\newcommand{\\ExtensionTok}[1]{#1}\n\\newcommand{\\FloatTok}[1]{\\textcolor[rgb]{0.25,0.63,0.44}{#1}}\n\\newcommand{\\FunctionTok}[1]{\\textcolor[rgb]{0.02,0.16,0.49}{#1}}\n\\newcommand{\\ImportTok}[1]{\\textcolor[rgb]{0.00,0.50,0.00}{\\textbf{#1}}}\n\\newcommand{\\InformationTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textbf{\\textit{#1}}}}\n\\newcommand{\\KeywordTok}[1]{\\textcolor[rgb]{0.00,0.44,0.13}{\\textbf{#1}}}\n\\newcommand{\\NormalTok}[1]{#1}\n\\newcommand{\\OperatorTok}[1]{\\textcolor[rgb]{0.40,0.40,0.40}{#1}}\n\\newcommand{\\OtherTok}[1]{\\textcolor[rgb]{0.00,0.44,0.13}{#1}}\n\\newcommand{\\PreprocessorTok}[1]{\\textcolor[rgb]{0.74,0.48,0.00}{#1}}\n\\newcommand{\\RegionMarkerTok}[1]{#1}\n\\newcommand{\\SpecialCharTok}[1]{\\textcolor[rgb]{0.25,0.44,0.63}{#1}}\n\\newcommand{\\SpecialStringTok}[1]{\\textcolor[rgb]{0.73,0.40,0.53}{#1}}\n\\newcommand{\\StringTok}[1]{\\textcolor[rgb]{0.25,0.44,0.63}{#1}}\n\\newcommand{\\VariableTok}[1]{\\textcolor[rgb]{0.10,0.09,0.49}{#1}}\n\\newcommand{\\VerbatimStringTok}[1]{\\textcolor[rgb]{0.25,0.44,0.63}{#1}}\n\\newcommand{\\WarningTok}[1]{\\textcolor[rgb]{0.38,0.63,0.69}{\\textbf{\\textit{#1}}}}\n\n\\makeatletter\n\\def\\maxwidth{\\ifdim\\Gin@nat@width>\\linewidth\\linewidth\\else\\Gin@nat@width\\fi}\n\\def\\maxheight{\\ifdim\\Gin@nat@height>\\textheight\\textheight\\else\\Gin@nat@height\\fi}\n\\makeatother\n% Scale images if necessary, so that they will not overflow the page\n% margins by default, and it is still possible to overwrite the defaults\n% using explicit options in \\includegraphics[width, height, ...]{}\n\\setkeys{Gin}{width=\\maxwidth,height=\\maxheight,keepaspectratio}\n% Set default figure placement to htbp\n\\makeatletter\n\\def\\fps@figure{htbp}\n\\makeatother\n\\setlength{\\emergencystretch}{3em} % prevent overfull lines\n\\providecommand{\\tightlist}{%\n  \\setlength{\\itemsep}{0pt}\\setlength{\\parskip}{0pt}}\n\\setcounter{secnumdepth}{-\\maxdimen} % remove section numbering\n\\ifLuaTeX\n  \\usepackage{selnolig}  % disable illegal ligatures\n\\fi\n\\IfFileExists{bookmark.sty}{\\usepackage{bookmark}}{\\usepackage{hyperref}}\n\\IfFileExists{xurl.sty}{\\usepackage{xurl}}{} % add URL line breaks if available\n\\urlstyle{same}\n\\makeatletter\n\\newsavebox\\pandoc@box\n\\newcommand*\\pandocbounded[1]{% scales image to fit in text height/width\n  \\sbox\\pandoc@box{#1}%\n  \\Gscale@div\\@tempa{\\textheight}{\\dimexpr\\ht\\pandoc@box+\\dp\\pandoc@box\\relax}%\n  \\Gscale@div\\@tempb{\\linewidth}{\\wd\\pandoc@box}%\n  \\ifdim\\@tempb\\p@<\\@tempa\\p@\\let\\@tempa\\@tempb\\fi% select the smaller of both\n  \\ifdim\\@tempa\\p@<\\p@\\scalebox{\\@tempa}{\\usebox\\pandoc@box}%\n  \\else\\usebox{\\pandoc@box}%\n  \\fi%\n\\makeatother\n}\n% End stuff from Pandoc template \n\n%\\geometry{paperwidth=191mm,paperheight=235mm,\n%    hmargin={20mm,20mm},vmargin={20mm,20mm}}\n\\geometry{letterpaper, hmargin={1in, 1in}, vmargin={1in,1in}}\n\n\\setmainfont{Source Serif Pro}\n\\setsansfont{Source Sans Pro}\n\\setmonofont{IosevkaTermSlab Nerd Font}\n\\newfontfamily\\sectionfont{Source Sans Pro}\n\\newfontfamily\\subsectionfont{Source Sans Pro}\n\\newfontfamily\\subsubsectionfont{Source Sans Pro}\n\\newfontfamily\\captionfont{Source Sans Pro}\n\\newcommand{\\hruleafter}[1]{#1\\hrule}\n\\titleformat{\\section}{\\large\\bfseries\\sffamily\\sectionfont}{\\thesection}{1em}{\\hruleafter}\n\\titleformat*{\\subsection}{\\bfseries\\sffamily\\subsectionfont}\n\\titleformat*{\\subsubsection}{\\itshape\\subsubsectionfont}\n\\titlespacing*{\\section} {0pt}{10ex}{2ex}\n\\captionnamefont{\\small\\sffamily}\n\\captiontitlefont{\\small\\sffamily}\n\\aliaspagestyle{part}{empty}\n\n\\makeatletter\n\\g@addto@macro\\@verbatim\\scriptsize\n\\makeatother\n\n\\newlength{\\imgwidth}\n\\newlength{\\drop}% for my convenience\n\n\\newcommand*{\\titleGM}{\\begingroup% Gentle Madness\n\\drop = 0.1\\textheight\n%\\vspace*{\\baselineskip}\n\\vfill\n\\hbox{%\n\\hspace*{0.2\\textwidth}%\n\\rule{1pt}{\\textheight}\n\\hspace*{0.05\\textwidth}%\n\\parbox[b]{0.75\\textwidth}{\n\\vbox{%\n\\vspace{\\drop}\n{\\noindent\\HUGE\\bfseries Using\\\\[0.5\\baselineskip]\nLiferay DXP 7.2}\\\\[2\\baselineskip]\n{\\Large\\itshape A Complete Guide}\\\\[4\\baselineskip]\n{\\Large THE LIFERAY DOCUMENTATION TEAM}\\par\n{\\small Richard Sezov, Jr.}\\par\n{\\small Jim Hinkey}\\par\n{\\small Stephen Kostas}\\par\n{\\small Jesse Rao}\\par\n{\\small Cody Hoag}\\par\n{\\small Nicholas Gaskill}\\par\n{\\small Michael Williams}\\par\n\\vspace{0.25\\textheight}\n{\\noindent Liferay Press}\\\\[\\baselineskip]\n}% end of vbox\n}% end of parbox\n}% end of hbox\n\\vfill\n\\null\n\\endgroup}\n\n\\makeatletter\n\\newcommand\\thickhrulefill{\\leavevmode \\leaders \\hrule height 1ex \\hfill \\kern \\z@}\n\\setlength\\midchapskip{10pt}\n\\makechapterstyle{VZ14}{\n  \\renewcommand\\chapternamenum{}\n  \\renewcommand\\printchaptername{}\n  \\renewcommand\\chapnamefont{\\sffamily\\Large\\scshape}\n  \\renewcommand\\printchapternum{%\n    \\chapnamefont\\null\\thickhrulefill\\quad\n    \\@chapapp\\space\\thechapter\\quad\\thickhrulefill}\n  \\renewcommand\\printchapternonum{%\n    \\par\\thickhrulefill\\par\\vskip\\midchapskip\n    \\hrule\\vskip\\midchapskip\n  }\n  \\renewcommand\\chaptitlefont{\\sffamily\\Huge\\scshape\\centering}\n  \\renewcommand\\afterchapternum{%\n    \\par\\nobreak\\vskip\\midchapskip\\hrule\\vskip\\midchapskip}\n  \\renewcommand\\afterchaptertitle{%\n    \\par\\vskip\\midchapskip\\hrule\\nobreak\\vskip\\afterchapskip}\n}\n\\makeatother\n\n\\newcommand\\scalegraphics[1]{%   \n    \\settowidth{\\imgwidth}{\\includegraphics{#1}}%\n    \\setlength{\\imgwidth}{\\minof{\\imgwidth}{\\textwidth}}%\n    \\includegraphics[width=\\imgwidth]{#1}%\n}\n\n\n\\usepackage{fancybox}\n\\newenvironment{roundedframe}{%\n\\def\\FrameCommand{%\n\\cornersize*{20pt}%\n\\setlength{\\fboxsep}{5pt}%\n\\ovalbox}%\n\\MakeFramed{\\advance\\hsize-\\width \\FrameRestore}}%\n{\\endMakeFramed}\n\n\n\\author{Richard L. Sezov, Jr. }\n\\title{Using Liferay DXP}\n\\date{12/12/2020}\n\n\\begin{document}\n\n\\pagestyle{empty}\n\n\\titleGM\n\nUsing Liferay DXP 7.2\n\nby The Liferay Documentation Team \n\nCopyright \\copyright 2020 by Liferay, Inc.\\\\[2\\baselineskip]\nThis work is offered under the following license: \\\\[2\\baselineskip]\nCreative Commons Attribution-Share Alike Unported\n\n\\scalegraphics{./images/cc-by-sa.png}\n\nYou are free:\n\n\\begin{enumerate}[1.]\n\\item\n  to share---to copy, distribute, and transmit the work\n\\item\n  to remix---to adapt the work\n\\end{enumerate}\n\nUnder the following conditions:\n\n\\begin{enumerate}[1.]\n\\item\n  Attribution. You must attribute the work in the manner specified by\n  the author or licensor (but not in any way that suggests that they\n  endorse you or your use of the work).\n\\item\n  Share Alike. If you alter, transform, or build upon this work, you may\n  distribute the resulting work only under the same, similar or a\n  compatible license.\n\\end{enumerate}\n\nThe full version of this license is here:\n\n\\href{http://creativecommons.org/licenses/by-sa/3.0}{http://creativecommons.org/licenses/by-sa/3.0}\n\nThis book was created out of material from the \\href{https://github.com/liferay/liferay-docs}{Liferay \n    Docs} repository. Where the content of this book and the repository differ, \nthe site is more up to date. \n\n\\clearpage\n\n\\frontmatter\n\\pagestyle{plain}\n\\pagenumbering{roman}\n\\chapterstyle{VZ14}\n\n\\tableofcontents\n\n\\chapter{Preface}\n\nWelcome to the world of Liferay DXP! This book was written for anyone who has\nany part in setting up, using, or maintaining a web site built on Liferay\nDXP. For the end user, it contains everything you need to know about using\nthe applications included with Liferay. For the administrator, you'll learn all\nyou need to know about setting up your site with users, sites, organizations,\nand user groups, as well as how to manage your site's security with roles. For\nserver admins, it guides you step-by-step through the installation,\nconfiguration, and optimization of Liferay DXP, including setting it up in a\nclustered, enterprise-ready environment. Use this book as a handbook for\neverything you need to do to get your Liferay DXP installation running\nsmoothly, and then keep it by your side as you configure and maintain your\nLiferay-powered web site.\n\n\\section{Conventions}\n\nThe information contained herein has been organized in a way that makes it easy\nto locate information. The book has two parts. The first part, \n\\textit{Using Liferay Portal}, describes how to configure and use a freshly \ninstalled Liferay Portal. The second part, \\textit{Deploying Liferay Portal}, \nis for administrators who want to install Liferay Portal and optimize its \nperformance. \n\nSections are broken up into multiple levels of headings, and these are\ndesigned to make it easy to find information.\n\nSource code and configuration file directives are presented monospaced, as\nbelow.\n\n\\begin{verbatim}\n\n  Source code appears in a non-proportional font. \n\n\\end{verbatim}\n\n\\textit{Italics} represent links or buttons to be clicked on in a user interface.\n\n\\texttt{Monospaced type} denotes Java classes, code, or properties within the text.\n\n\\textbf{Bold} describes field labels and portlets.\n\nPage headers denote the chapters and the section within the chapter.\n\n\\section{Publisher Notes}\n\nIt is our hope that this book is valuable to you, and that it becomes an indispensable resource as you work with Liferay DXP. If you need assistance beyond what is covered in this book, Liferay offers training\\footnote{https://learn.liferay.com}, consulting\\footnote{https://www.liferay.com/consulting}, and support\\footnote{https://help.liferay.com} services to fill any need that you might have. \n\nFor up-to-date documentation on the latest versions of Liferay, please see the documentation pages on Liferay Learn.\\footnote{https://learn.liferay.com} \n\nAs always, we welcome feedback. If there is any way you think we could make this book better, please feel free to mention it on our forums or in the feedback on Liferay Learn. You can also\nuse any of the email addresses on our Contact Us page.\\footnote{\\href{http://www.liferay.com/contact-us}{https://www.liferay.com/contact-us}} We are here to serve you, our users and customers, and to help make your experience using Liferay DXP the best it can be.\n\n\\mainmatter\n\\pagenumbering{gobble}\n\n\\part{Using Liferay DXP}\n\n\\pagestyle{headings}\n\\pagenumbering{arabic}\n\n\\include{user/user}\n\n\\part{Deploying Liferay DXP}\n\n\\include{user/deployment} \n\n\\end{document}\n"
  },
  {
    "path": "build-common.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"Dev Site Documentation Management\" basedir=\".\">\n\t<property name=\"project.dir\" value=\".\" />\n\n\t<path id=\"lib.classpath\">\n\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t</path>\n\n\t<path id=\"project.classpath\">\n\t\t<path refid=\"lib.classpath\" />\n\t</path>\n\n\t<property environment=\"env\" />\n\n\t<property file=\"${project.dir}/build.${USERNAME}.properties\" />\n\t<property file=\"${project.dir}/build.${user.name}.properties\" />\n\t<property file=\"${project.dir}/build.${env.COMPUTERNAME}.properties\" />\n\t<property file=\"${project.dir}/build.${env.HOST}.properties\" />\n\t<property file=\"${project.dir}/build.${env.HOSTNAME}.properties\" />\n\t<property file=\"${project.dir}/build.properties\" />\n\n\t<property name=\"build.dir\" value=\"./build\"/>\n\t<property name=\"dist.dir\" value=\"./dist\"/>\n\t<property name=\"metadata.filename\" value=\".METADATA\"/>\n\t<property name=\"temp.dir\" value=\"temp\"/>\n\t<property name=\"temp.dir2\" value=\"prepare-temp\"/>\n\n\t<!-- taskdefs -->\n\n\t<taskdef name=\"addtoc\" classname=\"com.liferay.documentation.util.AddTOCTask\">\n\t\t<classpath>\n\t\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t\t<!-- Use below instead of above fileset when testing changes to classes\n\t\t\t<pathelement path=\"${project.dir}/code/liferay-doc-utils/classes\"/>\n\t\t\t-->\n\n\t\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t\t</classpath>\n\t</taskdef>\n\t\n\t<taskdef name=\"checkarticleimgssite\" classname=\"com.liferay.documentation.util.CheckArticleImagesTask\">\n\t\t<classpath>\n\t\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t\t<!-- Use below instead of above fileset when testing changes to classes\n\t\t\t<pathelement path=\"${project.dir}/code/liferay-doc-utils/classes\"/>\n\t\t\t-->\n\n\t\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t\t</classpath>\n\t</taskdef>\n\n\t<taskdef name=\"checkheaders\" classname=\"com.liferay.documentation.util.CheckHeadersTask\">\n\t\t<classpath>\n\t\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t\t<!-- Use below instead of above fileset when testing changes to classes\n\t\t\t<pathelement path=\"${project.dir}/code/liferay-doc-utils/classes\"/>\n\t\t\t-->\n\n\t\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t\t</classpath>\n\t</taskdef>\n\n\t<taskdef name=\"checkimgssite\" classname=\"com.liferay.documentation.util.CheckImagesTask\">\n\t\t<classpath>\n\t\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t\t<!-- Use below instead of above fileset when testing changes to classes\n\t\t\t<pathelement path=\"${project.dir}/code/liferay-doc-utils/classes\"/>\n\t\t\t-->\n\n\t\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t\t</classpath>\n\t</taskdef>\n\n\t<taskdef name=\"checkintros\" classname=\"com.liferay.documentation.util.CheckIntrosTask\">\n\t\t<classpath>\n\t\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t\t<!-- Use below instead of above fileset when testing changes to classes\n\t\t\t<pathelement path=\"${project.dir}/code/liferay-doc-utils/classes\"/>\n\t\t\t-->\n\n\t\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t\t</classpath>\n\t</taskdef>\n\n\t<taskdef name=\"checklinks\" classname=\"com.liferay.documentation.util.CheckLinksTask\">\n\t\t<classpath>\n\t\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t\t<!-- Use below instead of above fileset when testing changes to classes\n\t\t\t<pathelement path=\"${project.dir}/code/liferay-doc-utils/classes\"/>\n\t\t\t-->\n\n\t\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t\t</classpath>\n\t</taskdef>\n\n\t<taskdef name=\"convertheaders\" classname=\"com.liferay.documentation.util.ConvertHeadersTask\">\n\t\t<classpath>\n\t\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t\t<!-- Use below instead of above fileset when testing changes to classes\n\t\t\t<pathelement path=\"${project.dir}/code/liferay-doc-utils/classes\"/>\n\t\t\t-->\n\n\t\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t\t</classpath>\n\t</taskdef>\n\n\t<taskdef name=\"convertlinks\" classname=\"com.liferay.documentation.util.ConvertLinksTask\">\n\t\t<classpath>\n\t\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t\t<!-- Use below instead of above fileset when testing changes to classes\n\t\t\t<pathelement path=\"${project.dir}/code/liferay-doc-utils/classes\"/>\n\t\t\t-->\n\n\t\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t\t</classpath>\n\t</taskdef>\n\n\t<taskdef name=\"convertsidebars\" classname=\"com.liferay.documentation.util.ConvertSidebarsTask\">\n\t\t<classpath>\n\t\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t\t<!-- Use below instead of above fileset when testing changes to classes\n\t\t\t<pathelement path=\"${project.dir}/code/liferay-doc-utils/classes\"/>\n\t\t\t-->\n\n\t\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t\t</classpath>\n\t</taskdef>\n\n\t<taskdef name=\"distdiff\" classname=\"com.liferay.documentation.util.CheckLatestCommitTask\">\n\t\t<classpath>\n\t\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t\t<!-- Use below instead of above fileset when testing changes to classes\n\t\t\t<pathelement path=\"${project.dir}/code/liferay-doc-utils/classes\"/>\n\t\t\t-->\n\n\t\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t\t</classpath>\n\t</taskdef>\n\n\t<taskdef name=\"numberheaders\" classname=\"com.liferay.documentation.util.NumberHeadersTask\">\n\t\t<classpath>\n\t\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t\t<!-- Use below instead of above fileset when testing changes to classes\n\t\t\t<pathelement path=\"${project.dir}/code/liferay-doc-utils/classes\"/>\n\t\t\t-->\n\n\t\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t\t</classpath>\n\t</taskdef>\n\n\t<taskdef name=\"numberimgssite\" classname=\"com.liferay.documentation.util.NumberImagesTask\">\n\t\t<classpath>\n\t\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t\t<!-- Use below instead of above fileset when testing changes to classes\n\t\t\t<pathelement path=\"${project.dir}/code/liferay-doc-utils/classes\"/>\n\t\t\t-->\n\n\t\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t\t</classpath>\n\t</taskdef>\n\n\t<taskdef name=\"concatmarkdown\" classname=\"com.liferay.documentation.util.ConcatMarkdownTask\">\n\t  <classpath>\n\t    <fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\n\t    <fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t  </classpath>\n\n\t</taskdef>\n\n\n\t<!-- targets -->\n\n\t<target name=\"add-article-to-temp\" depends=\"process-article-to-temp, check-images-dist\" description=\"Processes the article (-Darticle=...) and its parent article structure, adding the target article and all related parent articles and images to temp/\">\n\t</target>\n\n\t<target name=\"article-to-html\" depends=\"prepare, check-article-images\" description=\"Converts a single Markdown article to an HTML file.\">\n\t\t<echo message=\"... converting ${article} to html file ${build.dir}/${article}.html\"/>\n\t\t<java\n\t\t\tclassname=\"com.liferay.documentation.util.MarkdownToHtml\"\n\t\t\tclasspathref=\"project.classpath\"\n\t\t\tfailonerror=\"true\"\n\t\t\tfork=\"true\"\n\t\t>\n\t\t\t<arg value=\"${article}\"/>\n\t\t\t<arg value=\"${build.dir}/${article}.html\"/>\n\t\t</java>\n\t</target>\n\n\t<target name=\"add-toc\" description=\"Adds the Table of Contents syntax for each CE Markdown article.\">\n\t\t<addtoc docdir=\"${doc.dir}\" producttype=\"ce\">\n\t\t</addtoc>\n\t</target>\n\n\t<target name=\"add-toc-dist\" description=\"Adds the Table of Contents syntax for each Markdown article when generating Zip.\">\n\t\t<addtoc docdir=\"${doc.dir}/${temp.dir}\" producttype=\"dist\">\n\t\t</addtoc>\n\t</target>\n\n\t<target name=\"add-toc-dxp\" description=\"Adds the Table of Contents syntax for each DXP Markdown article.\">\n\t\t<addtoc docdir=\"${doc.dir}\" producttype=\"dxp\">\n\t\t</addtoc>\n\t</target>\n\n\t<target name=\"article-to-html-dxp\" depends=\"prepare, check-article-images-dxp\" description=\"Converts a single Markdown DXP article to an HTML file.\">\n\t\t<echo message=\"... converting ${article} to html file ${build.dir}/${article}.html\"/>\n\t\t<java\n\t\t\tclassname=\"com.liferay.documentation.util.MarkdownToHtml\"\n\t\t\tclasspathref=\"project.classpath\"\n\t\t\tfailonerror=\"true\"\n\t\t\tfork=\"true\"\n\t\t>\n\t\t\t<arg value=\"${article}\"/>\n\t\t\t<arg value=\"${build.dir}/${article}.html\"/>\n\t\t</java>\n\t</target>\n\n\t<target name=\"check-headers\" description=\"Check the headers of Markdown articles found in ${doc.dir}/articles\">\n\t\t<checkheaders docdir=\"${doc.dir}\" producttype=\"ce\">\n\t\t</checkheaders>\n\t</target>\n\n\t<target name=\"check-headers-dist\" description=\"Check the headers of Markdown articles found in ${doc.dir}/articles and ${doc.dir}/articles-dxp\">\n\t\t<checkheaders docdir=\"${doc.dir}/${temp.dir}\" producttype=\"dist\">\n\t\t</checkheaders>\n\t</target>\n\n\t<target name=\"check-headers-dxp\" description=\"Check the headers of Markdown articles found in ${doc.dir}/articles\">\n\t\t<checkheaders docdir=\"${doc.dir}\" producttype=\"dxp\">\n\t\t</checkheaders>\n\t</target>\n\n\t<target name=\"concat-markdown\" description=\"Concatenates the Markdown files in a specific folder for preparation to convert to other formats.\">\n\t  <concatmarkdown docdir=\"${doc.dir}\">\n\t  </concatmarkdown>\n\t  <move file=\"./book.markdown\" tofile=\"${dist.dir}/book/text/${doc.dir}.markdown\" />\n\t  <copy todir=\"${dist.dir}/images\">\n            <fileset dir=\"images\" includes=\"*.*\"/>\n        </copy>\n\t</target>\n\n\t<target name=\"check\" depends=\"add-toc, check-images, check-intros, number-headers, number-images, check-headers, check-links\" description=\"Checks for necessary requirements before CE distribution.\">\n\n\t</target>\n\n\t<target name=\"check-dxp\" depends=\"add-toc-dxp, check-images-dxp, check-intros-dxp, number-headers-dxp, number-images-dxp, check-headers-dxp, check-links-dxp\" description=\"Checks for necessary requirements before DXP distribution.\">\n\n\t</target>\n\n\t<target name=\"check-article-images\" description=\"Verifies all images referenced in a single Markdown article.\">\n\t\t<checkarticleimgssite docdir=\"${doc.dir}\" article=\"${article}\" imagedir=\"images\">\n\t\t</checkarticleimgssite>\n\t</target>\n\n\t<target name=\"check-article-images-dxp\" description=\"Verifies all images referenced in a single Markdown article.\">\n\t\t<checkarticleimgssite docdir=\"${doc.dir}\" article=\"${article}\" imagedir=\"images-dxp\">\n\t\t</checkarticleimgssite>\n\t</target>\n\n\t<target name=\"check-images\" description=\"Verifies all images referenced in a project's Markdown articles found in ${doc.dir}/articles.\">\n\t\t<checkimgssite docdir=\"${doc.dir}\" producttype=\"ce\">\n\t\t</checkimgssite>\n\t</target>\n\n\t<target name=\"check-images-dist\" description=\"Verifies all images referenced in a project's Markdown articles.\">\n\t\t<mkdir dir=\"${temp.dir}/images\"/>\n\t\t<checkimgssite docdir=\"${doc.dir}/${temp.dir}\" producttype=\"dist\" resolveimages=\"true\">\n\t\t</checkimgssite>\n\t</target>\n\n\t<target name=\"check-images-dxp\" description=\"Verifies all images referenced in a project's Markdown articles found in ${doc.dir}/articles.\">\n\t\t<checkimgssite docdir=\"${doc.dir}\" producttype=\"dxp\">\n\t\t</checkimgssite>\n\t</target>\n\n\t<target name=\"check-intros\" description=\"Verifies all Learning Path article folders are accompanied with an introduction Markdown article.\">\n\t\t<checkintros docdir=\"${doc.dir}\">\n\t\t</checkintros>\n\t</target>\n\n\t<target name=\"check-intros-dist\" description=\"Verifies all Learning Path article folders are accompanied with an introduction Markdown article.\">\n\t\t<checkintros docdir=\"${doc.dir}/${temp.dir}\">\n\t\t</checkintros>\n\t</target>\n\n\t<target name=\"check-intros-dxp\" description=\"Verifies all Learning Path article folders are accompanied with an introduction Markdown article.\">\n\t\t<property name=\"base.filepath\" value=\"${doc.dir}\" />\n\t\t<mkdir dir=\"${temp.dir}\"/>\n\t\t<copy todir=\"${temp.dir}\">\n\t\t\t<fileset dir=\"${project.dir}/${language.dir}/${purpose.dir}/${base.filepath}\">\n\t\t\t\t<include name=\"articles/**/*.markdown\"/>\n\t\t\t\t<include name=\"articles-dxp/**/*.markdown\"/>\n\t\t\t</fileset>\n\t\t</copy>\n\t\t<move todir=\"${temp.dir}/articles\" failonerror=\"false\">\n\t\t\t<fileset dir=\"${temp.dir}/articles-dxp\"/>\n\t\t</move>\n\t\t<checkintros docdir=\"${doc.dir}/${temp.dir}\">\n\t\t</checkintros>\n\t\t<delete dir=\"${temp.dir}\"/>\n\t</target>\n\n    <target name=\"clean-dist\" description=\"Deletes dist directory\">\n        <echo message=\"... deleting dist directory\"/>\n        <delete dir=\"${dist.dir}\"/>\n    </target>\n\n    <target name=\"clean-images\" description=\"Deletes images from ${doc.dir}/images and ${doc.dir}/images-dxp that are not referenced in any of the markdown files in any subdirectory of ${doc.dir}/articles and ${doc.dir}/articles-dxp\">\n\t\t\t<java\n\t\t\t\tclassname=\"com.liferay.documentation.util.CleanImages\"\n\t\t\t\tclasspathref=\"project.classpath\"\n\t\t\t\tfailonerror=\"true\"\n\t\t\t\tfork=\"true\"\n\t\t\t>\n\t\t\t\t<arg value=\"${doc.dir}\"/>\n\t\t\t</java>\n\t</target>\n\n    <target name=\"clean-temp\">\n        <delete dir=\"${temp.dir}\"/>\n    </target>\n\n\t<target name=\"compare-portal-source\" depends=\"prepare\" description=\"Generates reports comparing a Liferay 6 source directory (-DoldDir) to a Liferay 7+ source directory (-DnewDir).\">\n\t\t<property name=\"movedClassesFile\" value=\"${build.dir}/moved-classes.html\"/>\n\t\t<property name=\"removedClassesFile\" value=\"${build.dir}/removed-classes.html\"/>\n\t\t<java\n\t\t\tclassname=\"com.liferay.documentation.movedclassreporter.MovedClassReporterMain\"\n\t\t\tclasspathref=\"project.classpath\"\n\t\t\tfailonerror=\"true\"\n\t\t\tfork=\"true\"\n\t\t>\n\t\t\t<arg value=\"${oldDir}\"/>\n\t\t\t<arg value=\"${newDir}\"/>\n\t\t\t<arg value=\"${movedClassesFile}\"/>\n\t\t\t<arg value=\"${removedClassesFile}\"/>\n\t\t</java>\n\t</target>\n\n\t<target name=\"compare-with-older-branch\" description=\"Compares your current branch with an older branch, specified by -Dolder.branch=someOlderBranch.\">\n\t\t<java\n\t\t\tclassname=\"com.liferay.documentation.util.GitCompare\"\n\t\t\tclasspathref=\"project.classpath\"\n\t\t\tfailonerror=\"true\"\n\t\t\tfork=\"true\"\n\t\t>\n\t\t\t<arg value=\"${doc.dir}\"/>\n\t\t\t<arg value=\"${purpose.dir}\"/>\n\t\t\t<arg value=\"${older.branch}\"/>\n\t\t</java>\n\t</target>\n\n\t<target name=\"create-metadata-file\" description=\"Builds a metadata file.\">\n\t\t<!-- Use base.filepath property to distinguish between tutorials and learning-paths directories. The build.xml file\n\t\tin the learning-paths directory overwrites this value with \"learning-paths/${doc.dir}\" -->\n\t\t<property name=\"base.filepath\" value=\"${doc.dir}\" />\n\n\t\t<property name=\"metadata.file\" value=\"${project.dir}/${language.dir}/${purpose.dir}/${base.filepath}/${metadata.filename}\"/>\n\t\t<delete file=\"${metadata.file}\" failonerror=\"false\"/>\n\t\t<concat destfile=\"${metadata.file}\">base.source.url=https://github.com/${repo.owner}/${repo.name}/blob/${repo.branch}/${language.dir}/${purpose.dir}/${base.filepath}</concat>\n\t</target>\n\n\t<target name=\"convert-headers\" description=\"Converts legacy header IDs to new format. Run this before running the number-headers task for the first time.\">\n\t\t<convertheaders docdir=\"${doc.dir}\">\n\t\t</convertheaders>\n\t</target>\n\n\t<target name=\"convert-links\" description=\"Converts legacy links to new format.\">\n\t\t<convertlinks docdir=\"${doc.dir}\">\n\t\t</convertlinks>\n\t</target>\n\n\t<target name=\"convert-sidebars\" description=\"Converts legacy sidebars to new format.\">\n\t\t<convertsidebars docdir=\"${doc.dir}\">\n\t\t</convertsidebars>\n\t</target>\n\n\t<target name=\"dist-article-ce\" depends=\"clean-temp, add-article-to-temp, dist-temp\" description=\"Creates a ZIP file for importing the specified article (-Darticle=...), its images, and supporting structure to a Knowledge Base.\">\n\t</target>\n\n\t<target name=\"dist-ce\" depends=\"dist-ce-temp, prepare-dist, add-toc, number-headers, number-images\" description=\"Processes the CE articles and images and zips them up for importing.\">\n\t\t<zip destfile=\"${dist.dir}/${product.abbrev}-${product.community}-${product.version}-${purpose.dir}-${doc.dir}-${language.dir}.zip\">\n\t\t\t<fileset dir=\"${temp.dir}\" includes=\"articles/\" />\n\t\t\t<fileset dir=\"${temp.dir}\" includes=\"images/\" />\n\t\t\t<fileset dir=\"./\" includes=\"${metadata.filename}\" />\n\t\t</zip>\n\n\t\t<delete dir=\"${temp.dir}\"/>\n\t\t<delete file=\"${metadata.file}\"/>\n\t</target>\n\t\n\t<target name=\"dist-ce-temp\" description=\"Processes the article tokens and copies the CE articles and images to temp/ for zipping up.\">\n\t\t<property file=\"${project.dir}/release-site.properties\" />\n\t\t<property name=\"base.filepath\" value=\"${doc.dir}\" />\n\t\t<mkdir dir=\"${temp.dir}\"/>\n\t\t<copy todir=\"${temp.dir}\">\n\t\t\t<fileset dir=\"${project.dir}/${language.dir}/${purpose.dir}/${base.filepath}\">\n\t\t\t\t<include name=\"articles/**/*.markdown\"/>\n\t\t\t</fileset>\n\t\t\t<filterchain>\n\t\t\t\t<filterreader classname=\"org.apache.tools.ant.filters.ReplaceTokens\">\n\t\t\t\t\t<param type=\"token\" name=\"${product.token}\" value=\"${product.name}\"/>\n\t\t\t\t\t<param type=\"token\" name=\"${product.token.app.reference}\" value=\"${product.app.reference}\"/>\n                    <param type=\"token\" name=\"${product.token.commerce}\" value=\"${product.commerce.name}\"/>\n                    <param type=\"token\" name=\"${product.token.commerce.version}\" value=\"${product.commerce.name.version}\"/>\n\t\t\t\t\t<param type=\"token\" name=\"${product.token.ide}\" value=\"${product.ide.name}\"/>\n\t\t\t\t\t<param type=\"token\" name=\"${product.token.platform.reference}\" value=\"${product.platform.reference}\"/>\n                    <param type=\"token\" name=\"${product.token.version}\" value=\"${product.name.version}\"/>\n\t\t\t\t</filterreader>\n\t\t\t</filterchain>\n\t\t</copy>\n\t\t<mkdir dir=\"${temp.dir}/images\"/>\n\t\t<copy todir=\"${temp.dir}\">\n\t\t\t<fileset dir=\"${project.dir}/${language.dir}/${purpose.dir}/${base.filepath}\">\n\t\t\t\t<include name=\"images/**\"/>\n\t\t\t</fileset>\n\t\t</copy>\n\t</target>\n\n\t<target name=\"dist-diff-ce\" depends=\"dist-ce\" description=\"Generates files from differing commits based on CE content.\">\n\t\t<property file=\"${project.dir}/release-site.properties\" />\n\t\t<distdiff\n\t\t\tdocdir=\"${doc.dir}\"\n\t\t\tdoclocation=\"${language.dir}/${purpose.dir}/${doc.dir}\"\n\t\t\tdistdir=\"${dist.dir}\"\n\t\t\tzipname=\"${dist.dir}/${product.abbrev}-${product.community}-${product.version}-${purpose.dir}-${doc.dir}-${language.dir}\"\n\t\t\tdxpparam=\"false\"\n\t\t>\n\t\t</distdiff>\n\n\t\t<delete dir=\"${dist.dir}/${product.abbrev}-${product.community}-${product.version}-${purpose.dir}-${doc.dir}-${language.dir}\"/>\n\t\t<delete file=\"${dist.dir}/${product.abbrev}-${product.community}-${product.version}-${purpose.dir}-${doc.dir}-${language.dir}.zip\"/>\n\t</target>\n\n\t<target name=\"dist-diff-dxp\" depends=\"dist-dxp\" description=\"Generates files from differing commits based on CE and DXP content.\">\n\t\t<property file=\"${project.dir}/release-site.properties\" />\n\t\t<distdiff\n\t\t\tdocdir=\"${doc.dir}\"\n\t\t\tdoclocation=\"${purpose.dir}/${doc.dir}\"\n\t\t\tdistdir=\"${dist.dir}\"\n\t\t\tzipname=\"${dist.dir}/${product.abbrev}-${product.enterprise}-${product.version}-${purpose.dir}-${doc.dir}-${language.dir}\"\n\t\t\tdxpparam=\"true\"\n\t\t>\n\t\t</distdiff>\n\n\t\t<delete dir=\"${dist.dir}/${product.abbrev}-${product.enterprise}-${product.version}-${purpose.dir}-${doc.dir}-${language.dir}\"/>\n\t\t<delete file=\"${dist.dir}/${product.abbrev}-${product.enterprise}-${product.version}-${purpose.dir}-${doc.dir}-${language.dir}.zip\"/>\n\t</target>\n\n\t<target name=\"dist-dxp\" depends=\"dist-dxp-temp, prepare-dist, add-toc-dxp, number-headers-dxp, number-images-dxp\" description=\"Processes the article tokens and copies the DXP articles and images to temp/ for zipping up.\">\n\t\t<zip destfile=\"${dist.dir}/${product.abbrev}-${product.enterprise}-${product.version}-${purpose.dir}-${doc.dir}-${language.dir}.zip\">\n\t\t\t<fileset dir=\"${temp.dir}\" includes=\"articles/\" />\n\t\t\t<fileset dir=\"${temp.dir}\" includes=\"images/\" />\n\t\t\t<fileset dir=\"./\" includes=\"${metadata.filename}\" />\n\t\t</zip>\n\n\t\t<delete dir=\"${temp.dir}\"/>\n\t\t<delete file=\"${metadata.file}\"/>\n\t</target>\n\n\t<target name=\"dist-dxp-temp\" description=\"Zips up the folder's CE and DXP articles and images for importing.\">\n\t\t<property file=\"${project.dir}/release-site.properties\" />\n\t\t<property name=\"base.filepath\" value=\"${doc.dir}\" />\n\t\t<mkdir dir=\"${temp.dir}\"/>\n\t\t<copy todir=\"${temp.dir}\">\n\t\t\t<fileset dir=\"${project.dir}/${language.dir}/${purpose.dir}/${base.filepath}\">\n\t\t\t\t<include name=\"articles/**/*.markdown\"/>\n\t\t\t\t<include name=\"articles-dxp/**/*.markdown\"/>\n\t\t\t</fileset>\n\t\t\t<filterchain>\n\t\t\t\t<filterreader classname=\"org.apache.tools.ant.filters.ReplaceTokens\">\n\t\t\t\t\t<param type=\"token\" name=\"${product.token}\" value=\"${product.name.enterprise}\"/>\n\t\t\t\t\t<param type=\"token\" name=\"${product.token.app.reference}\" value=\"${product.app.enterprise.reference}\"/>\n                    <param type=\"token\" name=\"${product.token.commerce}\" value=\"${product.commerce.name.enterprise}\"/>\n                    <param type=\"token\" name=\"${product.token.commerce.version}\" value=\"${product.commerce.name.enterprise.version}\"/>\n\t\t\t\t\t<param type=\"token\" name=\"${product.token.ide}\" value=\"${product.ide.name.enterprise}\"/>\n\t\t\t\t\t<param type=\"token\" name=\"${product.token.platform.reference}\" value=\"${product.platform.enterprise.reference}\"/>\n                    <param type=\"token\" name=\"${product.token.version}\" value=\"${product.name.enterprise.version}\"/>\n\t\t\t\t</filterreader>\n\t\t\t</filterchain>\n\t\t</copy>\n\t\t<mkdir dir=\"${temp.dir}/images\"/>\n\t\t<!-- Cannot use filtering for images (due to corruption), so must copy separately -->\n\t\t<copy todir=\"${temp.dir}\">\n\t\t\t<fileset dir=\"${project.dir}/${language.dir}/${purpose.dir}/${base.filepath}\">\n\t\t\t\t<include name=\"images/**\"/>\n\t\t\t\t<include name=\"images-dxp/**\"/>\n\t\t\t</fileset>\n\t\t</copy>\n\n\t\t<move todir=\"${temp.dir}/articles\" failonerror=\"false\">\n\t\t\t<fileset dir=\"${temp.dir}/articles-dxp\"/>\n\t\t</move>\n\t\t<move todir=\"${temp.dir}/images\" failonerror=\"false\">\n\t\t\t<fileset dir=\"${temp.dir}/images-dxp\"/>\n\t\t</move>\n\t</target>\n\n\t<target name=\"dist-diffs\" depends=\"prepare-dist\" description=\"Zips up the document's articles and images that are new or modified, as specified in the git-modified-list.txt file.\">\n\t\t<mkdir dir=\"${dist.dir}\"/>\n\t\t<distdiff docdir=\"${doc.dir}\" purposedir=\"${purpose.dir}\">\n\t\t</distdiff>\n\n        <!-- Until the Markdown converter no longer requires intro files that need to be added manually to the diffs.zip, include ALL images in the ZIP. -->\n\t\t<zip destfile=\"${dist.dir}/diffs.zip\" update=\"true\">\n\t\t\t<fileset dir=\"./\" includes=\"images/\" />\n\t\t\t<fileset dir=\"./\" includes=\"${metadata.filename}\" />\n\t\t</zip>\n\t</target>\n\n\t<target name=\"dist-temp\"  depends=\"prepare-dist, number-headers\" description=\"Prepares the temp files and zips them up for importing.\">\n\t\t<property file=\"${project.dir}/release-site.properties\" />\n\t\t<zip destfile=\"${dist.dir}/${product.abbrev}-${product.community}-${product.version}-${purpose.dir}-${doc.dir}-${language.dir}.zip\">\n\t\t\t<fileset dir=\"${temp.dir}\" includes=\"articles/\" />\n\t\t\t<fileset dir=\"${temp.dir}\" includes=\"images/\" />\n\t\t\t<fileset dir=\"./\" includes=\"${metadata.filename}\" />\n\t\t</zip>\n\t</target>\n\n\t<target name=\"number-headers\" description=\"Numbers the headers of Markdown articles found in ${doc.dir}/articles\">\n\t\t<numberheaders docdir=\"${doc.dir}\" producttype=\"ce\">\n\t\t</numberheaders>\n\t</target>\n\n\t<target name=\"number-headers-dist\" description=\"Numbers the headers of Markdown articles.\">\n\t\t<numberheaders docdir=\"${doc.dir}/${temp.dir}\" producttype=\"dist\">\n\t\t</numberheaders>\n\t</target>\n\n\t<target name=\"number-headers-dxp\" description=\"Numbers the headers of Markdown articles.\">\n\t\t<numberheaders docdir=\"${doc.dir}\" producttype=\"dxp\">\n\t\t</numberheaders>\n\t</target>\n\n\t<target name=\"number-images\" description=\"numbers the images in a markdown chapter file\">\n\t\t<numberimgssite docdir=\"${doc.dir}\" producttype=\"ce\">\n\t\t</numberimgssite>\n\t</target>\n\n\t<target name=\"number-images-dist\" description=\"Numbers the images of Markdown articles found in ${doc.dir}/articles and ${doc.dir}/articles-dxp.\">\n\t\t<numberimgssite docdir=\"${doc.dir}/${temp.dir}\" producttype=\"dist\">\n\t\t</numberimgssite>\n\t</target>\n\n\t<target name=\"number-images-dxp\" description=\"Numbers the images of Markdown articles found in ${doc.dir}/articles and ${doc.dir}/articles-dxp.\">\n\t\t<numberimgssite docdir=\"${doc.dir}\" producttype=\"dxp\">\n\t\t</numberimgssite>\n\t</target>\n\n\t<target name=\"prepare\" description=\"Creates build directory.\">\n\t\t<!-- Use base.filepath property to distinguish between tutorials and learning-paths directories. The build.xml file\n\t\tin the learning-paths directory overwrites this value with \"learning-paths/${doc.dir}\" -->\n\t\t<property name=\"base.filepath\" value=\"${doc.dir}\" />\n\n\t\t<echo message=\"... creating build directory\"/>\n\t\t<mkdir dir=\"${project.dir}/${language.dir}/${purpose.dir}/${base.filepath}/${build.dir}\"/>\n\t\t<touch file=\"${project.dir}/${language.dir}/${purpose.dir}/${base.filepath}/${build.dir}/${article}.html\" mkdirs=\"true\"/>\n\t\t<mkdir dir=\"${build.dir}/images\"/>\n\t\t<copy todir=\"${build.dir}/images\" overwrite=\"true\">\n\t\t\t<fileset dir=\"./images\"/>\n\t\t</copy>\n\t\t<mkdir dir=\"${build.dir}/images-dxp\"/>\n\t\t<copy todir=\"${build.dir}/images-dxp\" overwrite=\"true\">\n\t\t\t<fileset dir=\"./images-dxp\"/>\n\t\t</copy>\n\t</target>\n\n\t<target name=\"prepare-dist\" depends=\"add-toc-dist, check-images-dist, check-intros-dist, clean-dist, number-headers-dist, number-images-dist, check-headers-dist, create-metadata-file\" description=\"Prepares the document for distribution.\">\n\t\t<echo message=\"... creating ${dist.dir} directory\"/>\n\t\t<mkdir dir=\"${dist.dir}\"/>\n\t</target>\n\n\t<target name=\"check-links\" description=\"Checks Community (LDN) URL links to see if they're broken.\">\n\t\t<property file=\"${project.dir}/release-site.properties\" />\n\t\t<checklinks\n\t\t\tapilinks=\"${api.links}\"\n\t\t\tdocdir=\"${doc.dir}\"\n\t\t\tlegacylinks=\"${legacy.links}\"\n\t\t\tappreferencesite=\"${product.app.reference}\"\n\t\t\tplatformreferencesite=\"${product.platform.reference}\"\n\t\t\tapptoken=\"${product.token.app.reference}\"\n\t\t\tplatformtoken=\"${product.token.platform.reference}\"\n\t\t\tdxpcheck=\"false\"\n\t\t>\n\t\t</checklinks>\n\t</target>\n\n\t<target name=\"check-links-dxp\" description=\"Checks Community (LDN) and DXP (Customer Portal) URL links to see if they're broken.\">\n\t\t<property file=\"${project.dir}/release-site.properties\" />\n\t\t<checklinks\n\t\t\tapilinks=\"${api.links}\"\n\t\t\tdocdir=\"${doc.dir}\"\n\t\t\tlegacylinks=\"${legacy.links}\"\n\t\t\tappreferencesite=\"${product.app.enterprise.reference}\"\n\t\t\tplatformreferencesite=\"${product.platform.enterprise.reference}\"\n\t\t\tapptoken=\"${product.token.app.reference}\"\n\t\t\tplatformtoken=\"${product.token.platform.reference}\"\n\t\t\tdxpcheck=\"true\"\n\t\t>\n\t\t</checklinks>\n\t</target>\n\n\t<target name=\"process-article-to-temp\"  description=\"Generates the target article's parent structure and then processes the articles' tokens. The resulting articles are copied (no images) to temp/.\">\n\t\t<property file=\"${project.dir}/release-site.properties\" />\n\t\t<property name=\"base.filepath\" value=\"${doc.dir}\" />\n\t\t<mkdir dir=\"${temp.dir}\"/>\n\t\t<mkdir dir=\"${temp.dir2}\"/>\n\t\t<java\n\t\t\tclassname=\"com.liferay.documentation.util.FindParentIntros\"\n\t\t\tclasspathref=\"project.classpath\"\n\t\t\tfailonerror=\"true\"\n\t\t\tfork=\"true\"\n\t\t>\n\t\t\t<arg value=\"${article}\"/>\n\t\t\t<arg value=\"${doc.dir}\"/>\n\t\t\t<arg value=\"${temp.dir2}\"/>\n\t\t</java>\n\t\t<copy todir=\"${temp.dir}\">\n\t\t\t<fileset dir=\"${temp.dir2}\"/>\n\t\t\t<filterchain>\n\t\t\t\t<filterreader classname=\"org.apache.tools.ant.filters.ReplaceTokens\">\n\t\t\t\t\t<param type=\"token\" name=\"${product.token}\" value=\"${product.name}\"/>\n\t\t\t\t\t<param type=\"token\" name=\"${product.token.app.reference}\" value=\"${product.app.reference}\"/>\n\t\t\t\t\t<param type=\"token\" name=\"${product.token.ide}\" value=\"${product.ide.name}\"/>\n\t\t\t\t\t<param type=\"token\" name=\"${product.token.platform.reference}\" value=\"${product.platform.reference}\"/>\n\t\t\t\t\t<param type=\"token\" name=\"${product.token.version}\" value=\"${product.name.version}\"/>\n\t\t\t\t</filterreader>\n\t\t\t</filterchain>\n\t\t</copy>\n\t\t<delete dir=\"${temp.dir2}\"/>\n\t</target>\n\n\t<target name=\"report-moved-portal-service-classes\" depends=\"prepare\" description=\"Compares two portal source trees to generate an HTML report of classes moved from the portal-service folder.\">\n\t\t<echo message=\"... comparing portal-service classes from ${old.src.dir} with those from ${new.src.dir}\"/>\n\t\t<java\n\t\t\tclassname=\"com.liferay.documentation.movedclassreporter.MovedClassReporterMain\"\n\t\t\tclasspathref=\"project.classpath\"\n\t\t\tfailonerror=\"true\"\n\t\t\tfork=\"true\"\n\t\t>\n\t\t\t<arg value=\"${old.src.dir}\"/>\n\t\t\t<arg value=\"${new.src.dir}\"/>\n\t\t\t<arg value=\"${build.dir}/portal-service-moved-classes.html\"/>\n\t\t</java>\n\t</target>\n</project>"
  },
  {
    "path": "build.properties",
    "content": "##\n## DO NOT EDIT THIS FILE.\n##\n## To update the properties of this file, create a separate properties file\n## named \"build.${user.name}.properties\" with the properties to overwrite.\n##\n\n##\n## System settings\n##\n\n    lang=en\n    build.dir=./build\n\n##\n## Pandoc\n##\n\n    pandoc.app=/home/${user.name}/.cabal/bin/pandoc\n\n##\n## Operating Systems\n##\n\n    os.apple=Mac OS X\n    os.unix=Linux,FreeBSD,Solaris,SunOS\n    os.windows=Windows 95,Windows 98,Windows NT,Windows 2000,Windows 2003,Windows XP,Windows Vista,Windows 7\n\n##\n## Markdown Metadata\n##\n\n    title=\n    author=\n    date=\n\n##\n## Repository\n##\n\n    repo.owner=liferay\n    repo.name=liferay-docs\n    repo.branch=master\n"
  },
  {
    "path": "build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"Liferay Documentation\" default=\"usage\" basedir=\".\">\n\t<property name=\"project.dir\" value=\".\" />\n\n\t<property name=\"build.dir\" value=\"${project.dir}/build\"/>\n\n\t<path id=\"lib.classpath\">\n\t\t<fileset dir=\"${project.dir}/lib\" includes=\"*.jar\" />\n\t\t<fileset dir=\"${project.dir}/code/liferay-doc-utils/lib\" includes=\"*.jar\" />\n\t</path>\n\n\t<path id=\"project.classpath\">\n\t\t<path refid=\"lib.classpath\" />\n\t</path>\n\n    <target name=\"compare-portal-source\" description=\"Reports classes moved from portal-service.jar to modules by comparing a Liferay 6 source directory (-DoldLiferayDir) to a Liferay 7+ source directory (-DnewLiferayDir).\">\n\t\t<property name=\"movedClassesFile\" value=\"${build.dir}/moved-classes.html\"/>\n\t\t<mkdir dir=\"${build.dir}\"/>\n\t\t<java\n\t\t\tclassname=\"com.liferay.documentation.movedclassreporter.MovedClassReporterMain\"\n\t\t\tclasspathref=\"project.classpath\"\n\t\t\tfailonerror=\"true\"\n\t\t\tfork=\"true\"\n\t\t>\n\t\t\t<arg value=\"${oldLiferayDir}\"/>\n\t\t\t<arg value=\"${newLiferayDir}\"/>\n\t\t\t<arg value=\"${movedClassesFile}\"/>\n\t\t</java>\n\t</target>\n\n\t<target name=\"usage\">\n\t\t<echo message=\"Welcome to the Liferay Documentation project. To show target descriptions, run ant -projecthelp\"/>\n\t</target>\n\n</project>\n"
  },
  {
    "path": "code/liferay-book-utils/.pydevproject",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<?eclipse-pydev version=\"1.0\"?><pydev_project>\n<pydev_pathproperty name=\"org.python.pydev.PROJECT_SOURCE_PATH\">\n<path>/${PROJECT_DIR_NAME}</path>\n</pydev_pathproperty>\n<pydev_property name=\"org.python.pydev.PYTHON_PROJECT_VERSION\">python 3.0</pydev_property>\n<pydev_property name=\"org.python.pydev.PYTHON_PROJECT_INTERPRETER\">Default</pydev_property>\n</pydev_project>\n"
  },
  {
    "path": "code/liferay-book-utils/src/fix-latex.py",
    "content": "#!/usr/bin/python\n# encoding: utf-8\n'''\n -- \n\n is a description\n\nIt defines classes_and_methods\n\n@author:     Rich Sezov\n\n@copyright:  2014 Liferay, Inc. All rights reserved.\n\n@license:    license\n\n'''\n\nimport argparse\n\nparser = argparse.ArgumentParser()\nparser.add_argument(\"output\", help=\"The file name to be written\")\nparser.add_argument(\"file\", help=\"file to be fixed\")\n\nargs = parser.parse_args()\n\noutfile = open(args.output, \"w\")\ninfile = open(args.file, \"r\")\ncontent = infile.readlines()\n\nfor i in content:\n    \n    if i.startswith(\"\\caption\"):\n        eff = i.find(\"F\")\n        prefix = i[:eff]\n        suffix = i[eff:]\n        i = prefix + \"\\\\\" + \"\\\\\" + suffix\n    \n    if i.startswith(\"+sidebar\"):\n        i = \"\\\\begin{roundedframe}\\n\\\\begin{wrapfigure}{l}{0.12\\\\textwidth}\\n\\\\includegraphics{../../images/01-tip.png}\\n\\end{wrapfigure}\\n\"\n        \n    if i.startswith(\"-sidebar\"):\n        i = \"\\end{roundedframe}\"\n        \n    #if i.startswith(\"\\href\"):\n    #    print (i)\n    #    print(\"Found href; ignoring.\")\n    #    i = \"}\"\n    \n    if i.startswith(\"\\index\"):\n        i=\"\"\n\n    outfile.write(i)     \n        "
  },
  {
    "path": "code/liferay-book-utils/src/format-ebook.py",
    "content": "'''\nCreated on Jan 23, 2015\n\n@author: rsezov\n'''\nimport argparse\n\nparser = argparse.ArgumentParser()\nparser.add_argument(\"output\", help=\"The file name to be written\")\nparser.add_argument(\"file\", help=\"file to be fixed\")\n\nargs = parser.parse_args()\n\noutfile = open(args.output, \"w\")\ninfile = open(args.file, \"r\")\ncontent = infile.readlines()\ncount = 0\n\nfor i in content:\n    \n    if i.startswith(\"+sidebar\"):\n        i = '<div class=\"sidebar\" id=\"sidebar' + str(count) + '\"><div class=\"sidebar-image\"></div><div class=\"sidebar-text\">'\n        \n    if i.startswith(\"-sidebar\"):\n        i = '</div></div>\\n'\n    \n    outfile.write(i)\n    count = count + 1\n    \ninfile.close()\noutfile.close()\n    "
  },
  {
    "path": "code/liferay-book-utils/src/merge-books.py",
    "content": "import argparse\n\nparser = argparse.ArgumentParser()\nparser.add_argument(\"output\", help=\"The file name to be written\")\nparser.add_argument(\"files\", nargs=\"+\", help=\"files to be merged\")\n\nargs = parser.parse_args()\n\noutfile = open(args.output, \"w\")\nchapterCount = 0\n\nfor i in args.files:\n    infile = open(i, \"r\")\n    content = infile.readlines()\n\n    if chapterCount == 0:\n       # We're in the first file and we only need to count the chapters \n        for j in content:\n            \n            if j.startswith(\"![Figure\"):\n                # find the chapter number\n                colon = j.find(\":\")\n                e = j.find(\"e\")\n                dot = j.find(\".\")\n                prefix = j[e+1:dot]\n                suffix = j[dot+1:colon]\n                \n                chapter = prefix.strip(\" \")\n                print (\"Chapter: \" + chapter)\n                chapterCount = int(chapter)\n\n        outfile.writelines(content)\n    \n    else:\n        # We're in another file and we need to increment the chapters\n        for j in content:\n            \n            whiteLength = len(j) - len(j.lstrip())\n            whiteSpace=\"\"\n            if (whiteLength > 0) and (j != \"\\n\"):                \n                for k in range(whiteLength):\n                    whiteSpace = whiteSpace + \" \"\n                    \n                j = j.lstrip()\n                print (str(whiteLength) + \" white space characters found; \" + str(len(whiteSpace)) + \" characters added.\")\n\n            if j.startswith(\"![Figure\"):\n                colon = j.find(\":\")\n                e = j.find(\"e\")\n                dot = j.find(\".\")\n                chNum = j[e+1:dot]\n                figNum = j[dot+1:colon]\n                chapter = int(chNum.strip(\" \"))\n                prefix = j[:e+1]\n                suffix = j[colon:]\n\n                if chapter < chapterCount:\n                    chapter = chapter + chapterCount\n                    \n                #imageLocation = suffix.index(\"(../../\")\n                #imageDir = suffix.index(\"/images/\")\n                #newLocation = \"(../../../deployment\"\n                #beginSuffix = suffix[:imageLocation]\n                #endSuffix = suffix[imageDir:]\n                #suffix = beginSuffix + newLocation + endSuffix\n\n                j = prefix + \" \" + str(chapter) + \".\" + figNum + suffix\n                \n            j = whiteSpace + j\n\n            outfile.write(j)\n\n    infile.close()\n\noutfile.close()\n\n"
  },
  {
    "path": "code/liferay-doc-utils/.gitignore",
    "content": "/dist/\n/bin/\n"
  },
  {
    "path": "code/liferay-doc-utils/build.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<project default=\"deploy\">\n\t<property name=\"project.dir\" value=\".\" />\n\n\t<property name=\"build.dir\" value=\"${project.dir}/classes\" />\n\t<property name=\"docs.lib.dir\" value=\"${project.dir}/../../lib\" />\n\t<property name=\"jar.file\" value=\"liferay-doc-utils.jar\" />\n\t<property name=\"lib.dir\" value=\"${project.dir}/lib\" />\n\t<property name=\"src.dir\" value=\"${project.dir}/src\" />\n\t<property name=\"deploy.dir\" value=\"${project.dir}/../../lib\" />\n\n\t<path id=\"lib.classpath\">\n\t\t<fileset dir=\"${lib.dir}\" includes=\"*.jar\" />\n\t\t<fileset dir=\"${docs.lib.dir}\" excludes=\"${jar.file}\" includes=\"*.jar\" />\n\t</path>\n\n\t<path id=\"project.classpath\">\n\t\t<pathelement path=\"${classpath}\" />\n\t\t<path refid=\"lib.classpath\" />\n\t</path>\n\n\t<!-- targets -->\n\n\t<target name=\"clean\" description=\"cleans up the classes\" >\n\t\t<delete dir=\"${build.dir}\" />\n\t</target>\n\n\t<target name=\"compile\" description=\"compiles the code\">\n\t\t<mkdir dir=\"${build.dir}\" />\n\n\t\t<antcall target=\"compile-java\">\n\t\t\t<param name=\"javac.classpathref\" value=\"project.classpath\" />\n\t\t\t<param name=\"javac.destdir\" value=\"${build.dir}\" />\n\t\t\t<param name=\"javac.srcdir\" value=\"${src.dir}\" />\n\t\t</antcall>\n\t</target>\n\n\t<target name=\"compile-java\">\n\t\t<copy todir=\"${javac.destdir}\">\n\t\t\t<fileset dir=\"${javac.srcdir}\" excludes=\"**/*.java\" />\n\t\t</copy>\n\n\t\t<javac\n\t\t\tclasspathref=\"${javac.classpathref}\"\n\t\t\tdestdir=\"${javac.destdir}\"\n\t\t\tincludeAntRuntime=\"false\"\n\t\t\tsrcdir=\"${javac.srcdir}\"\n\t\t\tdebug=\"on\"\n\t\t/>\n\t</target>\n\n\t<target name=\"deploy\" depends=\"clean, compile\" description=\"compiles the\n    code, creates a runnable JAR file, and deploys it to liferay-docs/lib\">\n\t\t<mkdir dir=\"${deploy.dir}\" />\n        <jar destfile=\"${deploy.dir}/${jar.file}\"\n\t\t\tfilesetmanifest=\"mergewithoutmain\">\n            <manifest>\n                <attribute name=\"Main-Class\" value=\"com.liferay.documentation.util.DocUtils\"/>\n                <attribute name=\"Class-Path\" value=\".\"/>\n            </manifest>\n            <fileset dir=\"${build.dir}\"/>\n            <zipfileset excludes=\"META-INF/*.SF\" src=\"${lib.dir}/commons-io-2.0.1.jar\"/>\n        </jar>\n\t</target>\n\n</project>\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/movedclassreporter/BasicClassInfo.java",
    "content": "package com.liferay.documentation.movedclassreporter;\n\nimport java.io.File;\n\npublic class BasicClassInfo {\n\n\tpublic BasicClassInfo(File file, String pkg) {\n\t\tthis.file = file;\n\t\tthis.name = file.getName();\n\t\tthis.pkg = pkg;\n\n\t\tthis.pkgEnd = pkg;\n\t\tint lastDot = pkg.lastIndexOf(\".\");\n\t\tif (lastDot != -1) {\n\t\t\tthis.pkgEnd = pkg.substring(lastDot);\n\t\t}\n\t}\n\n\tpublic File getFile() {\n\t\treturn file;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic String getPkg() {\n\t\treturn pkg;\n\t}\n\n\tpublic String getPkgEnd() {\n\t\treturn pkgEnd;\n\t}\n\n\tprivate File file;\n\tprivate String name;\n\tprivate String pkg;\n\tprivate String pkgEnd;\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/movedclassreporter/MovedClassInfo.java",
    "content": "package com.liferay.documentation.movedclassreporter;\n\npublic class MovedClassInfo {\n\tpublic MovedClassInfo(BasicClassInfo basicClassInfo) {\n\t\tthis.basicClassInfo = basicClassInfo;\n\t\tthis.name = basicClassInfo.getName();\n\t\tthis.packageOld = basicClassInfo.getPkg();\n\t}\n\n\tpublic BasicClassInfo getBasicClassInfo() {\n\t\treturn this.basicClassInfo;\n\t}\n\n\tpublic String getGroup( ) {\n\t\treturn group;\n\t}\n\n\tpublic void setGroup(String group) {\n\t\tthis.group = group;\n\t}\n\n\tpublic String getPackageNew() {\n\t\treturn packageNew;\n\t}\n\n\tpublic void setPackageNew(String packageNew) {\n\t\tthis.packageNew = packageNew;\n\t}\n\n\tpublic String getModule() {\n\t\treturn module;\n\t}\n\n\tpublic void setModule(String module) {\n\t\tthis.module = module;\n\t}\n\n\tpublic String getModuleVersion() {\n\t\treturn moduleVersion;\n\t}\n\n\tpublic void setModuleVersion(String moduleVersion) {\n\t\tthis.moduleVersion = moduleVersion;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getPackageOld() {\n\t\treturn packageOld;\n\t}\n\n\tBasicClassInfo basicClassInfo;\n\tprivate String name;\n\tprivate String packageOld;\n\tprivate String packageNew;\n\tprivate String module;\n\tprivate String moduleVersion;\n\tprivate String group;\n\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/movedclassreporter/MovedClassReporterMain.java",
    "content": "package com.liferay.documentation.movedclassreporter;\n\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.FilenameFilter;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport org.jdom2.Document;\nimport org.jdom2.Element;\nimport org.jdom2.output.Format;\nimport org.jdom2.output.XMLOutputter;\n\n\npublic class MovedClassReporterMain {\n\n\tprivate static final String PORTAL_KERNEL_JAR = \"portal-kernel.jar\";\n\n\tprivate static final String PORTAL_SERVICE_JAR = \"portal-service.jar\";\n\n\tprivate static final String USAGE = \"Usage: MovedClassReporter oldLiferayDir newLiferayDir movedClassesOutputFile\";\n\n\tpublic static void main(String[] args) {\n\t\tif (args == null || args.length < 3) {\n\t\t\tSystem.out.println(USAGE);\n\t\t\treturn;\n\t\t}\n\n\t\tFile oldDir = new File(args[0]);\n\t\tFile newDir = new File(args[1]);\n\t\tFile movedClassesOutputFile = new File(args[2]);\n\n\t\tif (!oldDir.isDirectory()) {\n\t\t\tSystem.out.println(\"oldLiferayDir is not a directory: \" + args[0]);\n\t\t}\n\n\t\tif (!newDir.isDirectory()) {\n\t\t\tSystem.out.println(\"newLiferayDir is not a directory: \" + args[1]);\n\t\t}\n\n\t\t// Create a list of all portal-kernel classes\n\n\t\tFile newKernelSrcDir = new File(newDir + \"/\" + PORTAL_KERNEL_SRC);\n\t\tif (!newKernelSrcDir.exists()) {\n\t\t\tSystem.out.println(\"Expected folder not found: \" + newKernelSrcDir.getPath());\n\t\t\tSystem.out.println(USAGE);\n\t\t\treturn;\n\t\t}\n\n\t\tSystem.out.println(\"New kernel dir: \" + newKernelSrcDir.getPath());\n\n\t\tArrayList<File> newKernelClasses = new ArrayList<File>();\n\n\t\tMovedClassReporterMain.listJavaFiles(newKernelSrcDir, newKernelClasses);\n\n\t\tSystem.out.println(\"New kernel dir class count: \" + newKernelClasses.size());\n\n\n\t\t// Create list of portal-service/portal-kernel class files\n\n\t\tFile oldKernelSrcDir = new File(oldDir.getPath() + \"/\" + PORTAL_SERVICE_SRC);\n\t\tString oldJar = PORTAL_SERVICE_JAR;\n\n\t\t// If portal-service doesn't exist, use old portal-kernel\n\t\tif (!oldKernelSrcDir.exists()) {\n\t\t\toldKernelSrcDir = new File(oldDir.getPath() + \"/\" + PORTAL_KERNEL_SRC);\n\t\t\toldJar = PORTAL_KERNEL_JAR;\n\n\t\t\tif (!oldKernelSrcDir.exists()) {\n\t\t\t\tSystem.out.println(\"No portal-service or portal-kernel source files in oldLiferayDir: \" + oldDir.getPath());\n\t\t\t\tSystem.out.println(USAGE);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tSystem.out.println(\"Old kernel dir: \" + oldKernelSrcDir.getPath());\n\n\t\tArrayList<File> oldKernelClasses = new ArrayList<File>();\n\n\t\tMovedClassReporterMain.listJavaFiles(oldKernelSrcDir, oldKernelClasses);\n\n\t\tSystem.out.println(\"Old kernel dir class count: \" + oldKernelClasses.size());\n\n\n\t\t// Create list of service class BasicClassInfo objects\n\n\t\tList<BasicClassInfo> serviceBasicClassInfos = new ArrayList<BasicClassInfo>();\n\n\t\tfor (File file : oldKernelClasses) {\n\t\t\tString pkg = \"\";\n\t\t\ttry {\n\t\t\t\tpkg = getPackage(file);\n\t\t\t\tBasicClassInfo info = new BasicClassInfo(file, pkg);\n\t\t\t\tserviceBasicClassInfos.add(info);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Add to a new list of BasicClassInfo objects all service classes not in new kernel\n\t\tList<MovedClassInfo> classesNotInKernel = new ArrayList<MovedClassInfo>();\n\n\t\tfor (BasicClassInfo serviceInfo: serviceBasicClassInfos) {\n\t\t\tString serviceFileName = serviceInfo.getName();\n\n\t\t\tboolean matched = false;\n\t\t\tfor (File newKernelClass : newKernelClasses) {\n\t\t\t\tif (serviceFileName.equals(newKernelClass.getName())) {\n\t\t\t\t\tmatched = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!matched) {\n\t\t\t\tMovedClassInfo formerClass =\n\t\t\t\t\tnew MovedClassInfo(serviceInfo);\n\t\t\t\tclassesNotInKernel.add(formerClass);\n\t\t\t}\n\t\t}\n\n\t\tSystem.out.println(\"classesNotInKernel: \" + classesNotInKernel.size());\n\n\n\t\tArrayList<File> moduleFiles = new ArrayList<File>();\n\n\t\tFile modulesAppsDir = new File(newDir + \"/\" + MODULES_APPS);\n\t\tlistJavaFiles(modulesAppsDir, moduleFiles);\n\n\t\tFile modulesCoreDir = new File(newDir + \"/\" + MODULES_CORE);\n\t\tlistJavaFiles(modulesCoreDir, moduleFiles);\n\n\t\tFile modulesUtilDir = new File(newDir + \"/\" + MODULES_UTIL);\n\t\tlistJavaFiles(modulesUtilDir, moduleFiles);\n\n\t\tSystem.out.println(\"moduleFiles: \" + moduleFiles.size());\n\n\t\t// Create list of module class BasicClassInfo objects\n\t\tList<BasicClassInfo> moduleBasicClassInfos = new ArrayList<BasicClassInfo>();\n\t\tfor (File file : moduleFiles) {\n\t\t\tString pkg = \"\";\n\t\t\ttry {\n\t\t\t\tpkg = getPackage(file);\n\t\t\t\tBasicClassInfo info = new BasicClassInfo(file, pkg);\n\t\t\t\tmoduleBasicClassInfos.add(info);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\t// Find the module class info for each moved class and store in MovedClassInfo objects\n\n\t\tList<MovedClassInfo> movedClasses = new ArrayList<MovedClassInfo>();\n\t\tList<MovedClassInfo> removedClasses = new ArrayList<MovedClassInfo>();\n\n\t\tfinal int portletPkgLen = \"com.liferay.portlet.\".length();\n\n\t\tfor (MovedClassInfo classNotInKernel : classesNotInKernel) {\n\t\t\tfinal String className = classNotInKernel.getName();\n\t\t\tfinal String oldPackageName = classNotInKernel.getPackageOld();\n\n\t\t\t// Construct expected new prefix\n\t\t\tString newPrefix = \"\";\n\t\t\tif (oldPackageName.startsWith(\"com.liferay.portlet.dynamicdatalists\")) {\n\t\t\t\tnewPrefix = \"com.liferay.dynamic.data.lists\";\n\t\t\t} \n\t\t\telse if (oldPackageName.startsWith(\"com.liferay.portlet.dynamicdatamapping\")) {\n\t\t\t\tnewPrefix = \"com.liferay.dynamic.data.mapping\";\n\t\t\t} \n\t\t\telse if (oldPackageName.startsWith(\"com.liferay.portlet.\")) {\n\t\t\t\t// construct new prefix using portlet type\n\t\t\t\tString newPrefixEnd = \"\";\n\t\t\t\tint end = oldPackageName.indexOf(\".\", portletPkgLen);\n\t\t\t\tif (end != -1) {\n\t\t\t\t\tnewPrefixEnd = oldPackageName.substring(portletPkgLen, end);\n\t\t\t\t} else {\n\t\t\t\t\tnewPrefixEnd = oldPackageName.substring(portletPkgLen);\n\t\t\t\t}\n\t\t\t\tnewPrefix = \"com.liferay.\" + newPrefixEnd;\n\t\t\t} \n\n\t\t\t// If expecting a new prefix, match it\n\t\t\tboolean matched = false;\n\t\t\tList<BasicClassInfo> matchingClassInfos = new ArrayList<BasicClassInfo>();\n\t\t\tfor (BasicClassInfo moduleClassInfo : moduleBasicClassInfos) {\n\t\t\t\t\n\t\t\t\tif (moduleClassInfo.getName().equals(className)) {\n\t\t\t\t\tmatchingClassInfos.add(moduleClassInfo);\n\t\t\t\t\tif (!newPrefix.isEmpty()) {\n\t\t\t\t\t\tif (moduleClassInfo.getPkg().startsWith(newPrefix)) {\n\t\t\t\t\t\t\tmatched = true;\n\n\t\t\t\t\t\t\tclassNotInKernel.setPackageNew(moduleClassInfo.getPkg());\n\t\t\t\t\t\t\textractModuleInfo(moduleClassInfo, classNotInKernel);\n\n\t\t\t\t\t\t\tmovedClasses.add(classNotInKernel);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!matched) {\n\t\t\t\tif (matchingClassInfos.isEmpty()) {\n\t\t\t\t\tremovedClasses.add(classNotInKernel);\n\t\t\t\t}\n\t\t\t\telse if (matchingClassInfos.size() == 1) {\n\t\t\t\t\t// Match it with the only other class with the same name\n\t\t\t\t\tmatched = true;\n\n\t\t\t\t\tBasicClassInfo moduleClassInfo = matchingClassInfos.get(0);\n\t\t\t\t\tclassNotInKernel.setPackageNew(moduleClassInfo.getPkg());\n\t\t\t\t\textractModuleInfo(moduleClassInfo, classNotInKernel);\n\t\t\t\t\tmovedClasses.add(classNotInKernel);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Find the best match\n\t\t\t\t\tfor (BasicClassInfo moduleClassInfo : matchingClassInfos) {\n\t\t\t\t\t\tif (moduleClassInfo.getPkgEnd().equals(classNotInKernel.getBasicClassInfo().getPkgEnd())) {\n\t\t\t\t\t\t\t// It's a match since it has the same ending package name\n\t\t\t\t\t\t\tmatched = true;\n\n\t\t\t\t\t\t\tclassNotInKernel.setPackageNew(moduleClassInfo.getPkg());\n\t\t\t\t\t\t\textractModuleInfo(moduleClassInfo, classNotInKernel);\n\t\t\t\t\t\t\tmovedClasses.add(classNotInKernel);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!matched) {\n\t\t\t\t\t\tremovedClasses.add(classNotInKernel);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tSystem.out.println(\"movedClasses: \" + movedClasses.size());\n\t\tsortByClassName(movedClasses);\n\t\tremoveJavaSuffix(movedClasses);\n\n\t\tSystem.out.println(\"removedClasses: \" + removedClasses.size());\n\n\t\tString oldDirName = oldDir.getName();\n\t\tString newDirName = newDir.getName();\n\t\t\n\t\tTemplateProcessor movedClassesTemplateProcessor = new TemplateProcessor();\n\t\tmovedClassesTemplateProcessor.processMovedClassesTemplate(movedClasses, movedClassesOutputFile, oldJar, oldDirName, newDirName);\n\t\tSystem.out.println(\"Reported moved classes to HTML file: \" + movedClassesOutputFile.getPath());\n\n\t\t// Write moved classes to XML\n\n\t\tElement root = new Element(\"moved-class-report\");\n\n\t\tElement oldVersion = new Element(\"old-source-version\");\n\t\toldVersion.addContent(oldDir.getPath());\n\t\troot.addContent(oldVersion);\n\n\t\tElement newVersion = new Element(\"new-source-version\");\n\t\tnewVersion.addContent(newDir.getPath());\n\t\troot.addContent(newVersion);\n\t\t\n\t\tfor (MovedClassInfo movedClass : movedClasses) {\n\t\t\tElement movedClassElem = new Element(\"moved-class\");\n\n\t\t\tElement className = new Element(\"class-name\");\n\t\t\tclassName.addContent(movedClass.getName());\n\t\t\tmovedClassElem.addContent(className);\n\t\t\t\n\t\t\tElement oldStuff = new Element(\"old\");\n\n\t\t\tElement oldPackage = new Element(\"package\");\n\t\t\toldPackage.addContent(movedClass.getPackageOld());\n\t\t\toldStuff.addContent(oldPackage);\n\n\t\t\tmovedClassElem.addContent(oldStuff);\n\n\t\t\tElement newStuff = new Element(\"new\");\n\t\t\t\n\t\t\tElement newPackage = new Element(\"package\");\n\t\t\tnewPackage.addContent(movedClass.getPackageNew());\n\t\t\tnewStuff.addContent(newPackage);\n\n\t\t\tElement newArtifactGroup = new Element(\"artifact-group\");\n\t\t\tnewArtifactGroup.addContent(movedClass.getGroup());\n\t\t\tnewStuff.addContent(newArtifactGroup);\n\t\t\t\n\t\t\tElement newArtifactId = new Element(\"artifact-id\");\n\t\t\tnewArtifactId.addContent(movedClass.getModule());\n\t\t\tnewStuff.addContent(newArtifactId);\n\t\t\t\n\t\t\tElement newArtifactVersion = new Element(\"artifact-version\");\n\t\t\tnewArtifactVersion.addContent(movedClass.getModuleVersion());\n\t\t\tnewStuff.addContent(newArtifactVersion);\n\t\t\t\n\t\t\tmovedClassElem.addContent(newStuff);\n\t\t\troot.addContent(movedClassElem);\n\t\t}\n\t\t\n\t\tDocument doc = new Document();\n\t\tdoc.setRootElement(root);\n\n\t\tString movedClassesXMLFilePath;\n\t\tint dotIndex = movedClassesOutputFile.getPath().indexOf(\".htm\");\n\t\tif (dotIndex != -1) {\n\t\t\tmovedClassesXMLFilePath = movedClassesOutputFile.getPath().substring(0, dotIndex) + \".xml\";\n\t\t}\n\t\telse {\n\t\t\tmovedClassesXMLFilePath = movedClassesOutputFile.getPath() + \".xml\";\n\t\t}\n\t\t\n\t\tFile movedClassesXMLFile = new File(movedClassesXMLFilePath);\n\n\t\tXMLOutputter outter = new XMLOutputter();\n\t\toutter.setFormat(Format.getPrettyFormat());\n\t\ttry {\n\t\t\toutter.output(doc, new FileWriter(movedClassesXMLFile));\n\t\t\tSystem.out.println(\"Reported moved classes to XML file: \" + movedClassesXMLFile.getPath());\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tprivate static void extractModuleInfo(BasicClassInfo classInfo, MovedClassInfo formerClass) {\n\t\tString bundleName = \"\";\n\t\tString bundleVersion = \"\";\n\t\tFile moduleFile = classInfo.getFile();\n\t\tFile currentDir = moduleFile.getParentFile();\n\t\twhile (currentDir != null) {\n\t\t\tif (currentDir.getName().equals(\"src\")) {\n\t\t\t\tFile moduleDir = currentDir.getParentFile();\n\n\t\t\t\tFilenameFilter filter = new FilenameFilter() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic boolean accept(File dir, String name) {\n\t\t\t\t\t\tif (name.equals(\"bnd.bnd\")) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tFile[] bndFiles = moduleDir.listFiles(filter);\n\t\t\t\tif ((bndFiles != null) && bndFiles.length > 0) {\n\t\t\t\t\tFile bndFile = bndFiles[0];\n\t\t\t\t\t\n\t\t\t\t\ttry {\n\n\t\t\t\t\t\tLineNumberReader in =\n\t\t\t\t\t\t\t\tnew LineNumberReader(new FileReader(bndFile));\n\t\t\t\t\t\tString line;\n\t\t\t\t\t\twhile ((line = in.readLine()) != null) {\n\t\t\t\t\t\t\tif (line.startsWith(\"Bundle-SymbolicName:\")) {\n\t\t\t\t\t\t\t\tString[] tokens = line.split(\" \");\n\t\t\t\t\t\t\t\tif (tokens.length > 1) {\n\t\t\t\t\t\t\t\t\tbundleName = tokens[1].trim();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (line.startsWith(\"Bundle-Version:\")) {\n\t\t\t\t\t\t\t\tString[] tokens = line.split(\" \");\n\t\t\t\t\t\t\t\tif (tokens.length > 1) {\n\t\t\t\t\t\t\t\t\tbundleVersion = tokens[1].trim();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (!bundleName.isEmpty() && !bundleVersion.isEmpty()) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tin.close();\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (bundleName.isEmpty()) {\n\t\t\t\t\t\tSystem.out.println(\"ERROR: Could not fill bundle info for \" + bndFile.getPath());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tSystem.out.println(\"Couldn't find a parent folder with a bnd.bnd file.\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcurrentDir = currentDir.getParentFile();\n\t\t}\n\n\t\tformerClass.setGroup(GROUP_COM_LIFERAY);\n\t\tformerClass.setModule(bundleName);\n\t\tformerClass.setModuleVersion(bundleVersion);\n\t}\n\n\tprivate static void sortByClassName(\n\t\t\tList<MovedClassInfo> formerPortalServiceClasses) {\n\t\tformerPortalServiceClasses.sort(new Comparator<MovedClassInfo>() {\n\n\t\t\t@Override\n\t\t\tpublic int compare(MovedClassInfo classA,\n\t\t\t\t\tMovedClassInfo classB) {\n\n\t\t\t\treturn classA.getName().compareTo(classB.getName());\n\t\t\t}\n\n\t\t}\n\t\t);\n\t}\n\n\tprivate static void sortByPackageName(\n\t\t\tList<MovedClassInfo> formerPortalServiceClasses) {\n\t\tformerPortalServiceClasses.sort(new Comparator<MovedClassInfo>() {\n\n\t\t\t@Override\n\t\t\tpublic int compare(MovedClassInfo classA,\n\t\t\t\t\tMovedClassInfo classB) {\n\n\t\t\t\tint rval = classA.getPackageOld().compareTo(classB.getPackageOld());\n\n\t\t\t\tif (rval == 0) {\n\t\t\t\t\trval = classA.getName().compareTo(classB.getName());\n\t\t\t\t}\n\n\t\t\t\treturn rval;\n\t\t\t}\n\n\t\t}\n\t\t);\n\t}\n\n\tprivate static void removeJavaSuffix(\n\t\t\tList<MovedClassInfo> formerPortalServiceClasses) {\n\t\tfor (MovedClassInfo movedClass : formerPortalServiceClasses) {\n\t\t\tString name = movedClass.getName();\n\t\t\tint x = name.indexOf(\".java\");\n\t\t\tif (x != -1) {\n\t\t\t\tname = name.substring(0, x);\n\t\t\t\tmovedClass.setName(name);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static String getPackage(File serviceClass) throws Exception {\n\t\tString pkgStr = \"package\";\n\t\tint pkgStrLen = pkgStr.length();\n\t\tString pkg = \"\";\n\n\t\ttry {\n\t\t\tLineNumberReader in =\n\t\t\t\t\tnew LineNumberReader(new FileReader(serviceClass));\n\t\t\tString line;\n\t\t\twhile ((line = in.readLine()) != null) {\n\t\t\t\tif (line.startsWith(\"package\")) {\n\t\t\t\t\tString str = line.substring(pkgStrLen);\n\t\t\t\t\tstr = str.trim();\n\t\t\t\t\tint semiIndex = str.indexOf(';');\n\t\t\t\t\tif (semiIndex > 0) {\n\t\t\t\t\t\tstr = str.substring(0, semiIndex);\n\t\t\t\t\t}\n\n\t\t\t\t\tpkg = str;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tin.close();\n\t\t} catch (IOException e) {\n\t\t\tthrow new Exception(e.getLocalizedMessage());\n\t\t}\n\n\t\treturn pkg;\n\t}\n\n\tprivate static void listBndFiles(String directoryName, ArrayList<File> files) {\n\t    File directory = new File(directoryName);\n\n\t    // get all the files from a directory\n\t    File[] fList = directory.listFiles();\n\t    if (fList == null) {\n\t    \treturn;\n\t    }\n\n\t    for (File file : fList) {\n\t        if (file.isFile()) {\n\t        \tif (file.getName().equals(\"bnd.bnd\")) {\n\t        \t\tfiles.add(file);\n\t        \t}\n\t        } else if (file.isDirectory()) {\n\t        \tlistBndFiles(file.getAbsolutePath(), files);\n\t        }\n\t    }\n\t}\n\n\tprivate static void listJavaFiles(File directory, ArrayList<File> files) {\n\n\t    // get all the files from a directory\n\t    File[] fList = directory.listFiles();\n\t    if (fList == null) {\n\t    \treturn;\n\t    }\n\n\t    for (File file : fList) {\n\t        if (file.isFile()) {\n\t        \tif (file.getName().endsWith(\".java\")) {\n\t        \t\tfiles.add(file);\n\t        \t}\n\t        } else if (file.isDirectory()) {\n\t            listJavaFiles(file, files);\n\t        }\n\t    }\n\t}\n\n\tpublic static final String GROUP_COM_LIFERAY = \"com.liferay\";\n\n\tpublic static final String MODULES_APPS = \"modules/apps\";\n\n\tpublic static final String MODULES_CORE = \"modules/core\";\n\n\tpublic static final String MODULES_UTIL = \"modules/util\";\n\n\tpublic static final String PORTAL_KERNEL_SRC = \"portal-kernel/src\";\n\n\tpublic static final String PORTAL_SERVICE_SRC = \"portal-service/src\";\n\n\tpublic static final String SRC_MAIN_JAVA = \"src/main/java\";\n\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/movedclassreporter/TemplateProcessor.java",
    "content": "package com.liferay.documentation.movedclassreporter;\n\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport freemarker.cache.ClassTemplateLoader;\nimport freemarker.template.Configuration;\nimport freemarker.template.Template;\nimport freemarker.template.TemplateException;\n\npublic class TemplateProcessor {\n\n\tpublic void processMovedClassesTemplate(List<MovedClassInfo> movedClasses, File article, String oldJar, String oldDirName, String newDirName) {\n\t\t// Write report using FreeMarker template\n\t\tConfiguration cfg = new Configuration(Configuration.VERSION_2_3_23);\n\t\ttry {\n\t\t\tcfg.setDefaultEncoding(\"UTF-8\");\n\n\t\t\tClassTemplateLoader ctl = new ClassTemplateLoader(getClass(), TPL_ROOT);\n\t\t\t\n\t\t\tcfg.setTemplateLoader(ctl);\n\n\t\t\tMap root = new HashMap();\n\n\t\t\troot.put(\"oldJar\", oldJar);\n\t\t\troot.put(\"oldSrc\", oldDirName);\n\t\t\troot.put(\"newSrc\", newDirName);\n\t\t\troot.put(\"movedClasses\", movedClasses);\n\t\t\t\n\t\t\tTemplate temp = cfg.getTemplate(\"moved-classes.ftl\");\n\n\t\t\tFileWriter out = new FileWriter(article);\n\t\t\ttemp.process(root, out);\n\t\t} catch (IOException e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t} catch (TemplateException e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t}\n\n\tpublic static final String TPL_ROOT =\n\t\t\"/com/liferay/documentation/movedclassreporter/dependencies/\";\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/movedclassreporter/dependencies/moved-classes.ftl",
    "content": "\nTo leverage the benefits of modularization, many classes from <em>${oldJar}</em>\nhave been moved into application and framework API modules. The table below\nprovides details about these classes and the modules they've moved to. Package\nchanges and each module's group, artifact ID, and version are listed, to\nfacilitate \n<a href=\"/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies\">configuring dependencies</a>. \n\n<style>\ntable, th, td {\n    border: 1px solid black;\n    border-collapse: collapse;\n}\nth, td {\n    padding: 5px;\n    text-align: left;\n}\ncaption {\n    text-align: left;\n}\n</style>\n<table style=\"width:100%\">\n\n  <caption>\n    <b>Classes Moved from ${oldJar} to Modules</b>\n\t<p>\n      This information was generated based on comparing classes in\n      <em>${oldSrc}</em> to classes in <em>${newSrc}</em>.\n    </p>\n  </caption>\n  <tr>\n    <th>Class</th>\n    <th>Package</th>\n    <th>Group ID, Artifact ID,<br>\n        and Version</th>\n  </tr>\n\n<#list movedClasses as movedClass>\n  <tr>\n    <td>${movedClass.name}</td>\n    <td>\n\t  <em>Old:</em> ${movedClass.packageOld}<br>\n\t  <em>New:</em> ${movedClass.packageNew}\n\t</td>\n    <td>${movedClass.group}<br>\n        ${movedClass.module}<br>\n        ${movedClass.moduleVersion}\n    </td>\n  </tr>\n</#list>\n\n</table>\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/AddTOCTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\n\npublic class AddTOCTask extends Task {\n\n\t@Override\n\tpublic void execute() throws BuildException {\n\n\t\tString docDir = _docdir;\n\t\tString productType = _productType;\n\n\t\tList<String> dirTypes = new ArrayList<String>();\n\t\tdirTypes.add(\"\");\n\n\t\tif (productType.equals(\"dxp\")) {\n\t\t\tdirTypes.add(\"-dxp\");\n\t\t}\n\n\t\tfor (String dirType : dirTypes) {\n\n\t\t\tList<String> fileList = DocsUtil.getMarkdownFileList(docDir, dirType);\n\t\t\tList<String> fileListNoTOC = new ArrayList<String>();\n\n\t\t\ttry {\n\t\t\t\tfileListNoTOC = getFilesWithNoTOC(fileList);\n\t\t\t\taddTOCs(fileListNoTOC);\n\t\t\t} catch (IOException e) {\n\t\t\t\tthrow new BuildException(e.getLocalizedMessage());\n\t\t\t}\n\n\t\t}\n\t}\n\n\tprivate static void addTOCs(List<String> fileListNoTOC) throws IOException {\n\n\t\tfor (int i = 0; i < fileListNoTOC.size(); i++) {\n\t\t\tString filenameNoTOC = fileListNoTOC.get(i);\n\t\t\tFile inFile = new File(filenameNoTOC);\n\t\t\tFile outFile = new File(filenameNoTOC);\n\t\t\tString outFileTmp = outFile + \".tmp\";\n\n\t\t\tSystem.out.println(\"Adding TOC syntax for \" + filenameNoTOC);\n\n\t\t\tLineNumberReader in =\n\t\t\t\t\tnew LineNumberReader(new FileReader(inFile));\n\n\t\t\tBufferedWriter out =\n\t\t\t\t\tnew BufferedWriter(new FileWriter(outFileTmp));\n\n\t\t\tString line;\n\t\t\tboolean tocAdded = false;\n\t\t\twhile ((line = in.readLine()) != null) {\n\n\t\t\t\tif (line.startsWith(\"#\") && !line.startsWith(\"##\") && !tocAdded) {\n\t\t\t\t\tout.append(line);\n\t\t\t\t\tout.append(\"\\n\\n\");\n\t\t\t\t\tout.append(tocSyntax);\n\t\t\t\t\tout.append(\"\\n\");\n\n\t\t\t\t\ttocAdded = true;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tout.append(line);\n\t\t\t\t\tout.append(\"\\n\");\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tin.close();\n\n\t\t\tout.flush();\n\t\t\tout.close();\n\n\t\t\t// Replace original file with modified temp file\n\n\t\t\tFileUtils.copyFile(\n\t\t\t\t\tnew File(outFileTmp),\n\t\t\t\t\tnew File(filenameNoTOC));\n\n\t\t\tFileUtils.forceDelete(new File(outFileTmp));\n\t\t}\n\t}\n\n\tprivate static List<String> getFilesWithNoTOC(List<String> fileList)\n\t\t\tthrows IOException {\n\n\t\tList<String> fileListNoTOC = new ArrayList<String>();\n\n\t\tfor (int i = 0; i < fileList.size(); i++) {\n\t\t\tString filename = fileList.get(i);\n\t\t\tFile inFile = new File(filename);\n\n\t\t\tLineNumberReader in =\n\t\t\t\t\tnew LineNumberReader(new FileReader(inFile));\n\n\t\t\tString line;\n\t\t\tboolean tocExists = false;\n\t\t\t//Integer tocLineNum = null;\n\t\t\tint tocLineNum = -2;\n\n\t\t\twhile ((line = in.readLine()) != null) {\n\n\t\t\t\tif (line.startsWith(tocSyntax)) {\n\n\t\t\t\t\ttocExists = true;\n\t\t\t\t\ttocLineNum = in.getLineNumber();\n\t\t\t\t}\n\t\t\t\tif (in.getLineNumber() == (tocLineNum + 1) && tocExists) {\n\t\t\t\t\tif (!line.equals(\"\")) {\n\t\t\t\t\t\tin.close();\n\t\t\t\t\t\tthrow new BuildException(\"Filename: \" + filename + \":\" +\n\t\t\t\t\t\t\t\tin.getLineNumber() +  \"The line following the TOC syntax should \" +\n\t\t\t\t\t\t\t\t\"be blank.\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!tocExists) {\n\t\t\t\tfileListNoTOC.add(filename);\n\t\t\t}\n\n\t\t\tin.close();\n\t\t}\n\n\t\treturn fileListNoTOC;\n\t}\n\n\tpublic void setDocdir(String docdir) {\n\t\t_docdir = docdir;\n\t}\n\n\tpublic void setProductType(String productType) {\n\t\t_productType = productType;\n\t}\n\n\tprivate static String tocSyntax = \"[TOC levels=1-4]\";\n\n\tprivate String _docdir;\n\tprivate String _productType;\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/CheckArticleImagesTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\n\npublic class CheckArticleImagesTask extends Task {\n\n\t@Override\n\tpublic void execute() throws BuildException {\n\t\tList<String> errors = new ArrayList<String>();\n\n\t\tSystem.out.println(\"Start checking image sources ...\");\n\n\t\tFile dir = new File(\"../\" + _docdir);\n\t\tif (!dir.exists()) {\n\t\t\tthrow new BuildException(\"docdir \" + dir.getAbsolutePath() +\n\t\t\t\t\" could not be found\");\n\t\t}\n\n\t\tif (!dir.isDirectory()) {\n\t\t\tthrow new BuildException(\"docdir \" + dir.getAbsolutePath() +\n\t\t\t\t\" is not a directory\");\n\t\t}\n\n\t\tif (!_article.endsWith(\".markdown\") && !_article.endsWith(\".md\")) {\n\t\t\tthrow new BuildException(\"Article isn't a Markdown file\");\n\t\t}\n\n\t\tFile article = new File(\"../\" + _docdir + \"/\" + _article);\n\n\t\tif (!article.exists()) {\n\t\t\tthrow new BuildException(\"Article doesn't exist: \" + article.getPath());\n\t\t}\n\n\t\tint dirsUp = 0;\n\t\tFile tmpFile = article.getParentFile();\n\t\tString fileName = article.getName();\n\t\twhile (!_docdir.equals(tmpFile.getName())) {\n\t\t\ttmpFile = tmpFile.getParentFile();\n\t\t\tdirsUp++;\n\t\t}\n\n\t\tif (!_docdir.equals(tmpFile.getName())) {\n\t\t\tthrow new BuildException(\"File not in docdir\");\n\t\t}\n\n\t\ttry {\n\t\t\tSystem.out.println(\"inside \" + article.getPath());\n\n\t\t\tLineNumberReader in =\n\t\t\t\tnew LineNumberReader(new FileReader(article));\n\t\t\tString line;\n\t\t\ttry {\n\t\t\t\twhile ((line = in.readLine()) != null) {\n\t\t\t\t\tList<String> newErrors = CheckImageUtil.checkImgSrc(\n\t\t\t\t\t\tline, _article, in.getLineNumber(), dirsUp, _imagedir);\n\n\t\t\t\t\tif (!newErrors.isEmpty()) {\n\t\t\t\t\t\terrors.addAll(newErrors);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\tthrow new BuildException(e.getLocalizedMessage());\n\t\t\t}\n\t\t} catch (FileNotFoundException e) {\n\t\t\tthrow new BuildException(e.getLocalizedMessage());\n\t\t}\n\n\t\t// Check for errors\n\n\t\tif (!errors.isEmpty()) {\n\n\t\t\t// Print errors\n\t\t\t\n\t\t\tSystem.out.println(\"ERROR - Missing image source files ...\");\n\t\t\tfor (String error : errors) {\n\t\t\t\tSystem.out.println(error);\n\t\t\t}\n\n\t\t\tthrow new BuildException(\"Missing image source files\");\n\t\t}\n\n\t\tSystem.out.println(\"Finished checking image sources.\");\n\t}\n\n\tpublic void setArticle(String article) {\n\t\t_article = article;\n\t}\n\n\tpublic void setDocdir(String docdir) {\n\t\t_docdir = docdir;\n\t}\n\n\tpublic void setImagedir(String imagedir) {\n\t\t_imagedir = imagedir;\n\t}\n\n\tprivate String _docdir;\n\tprivate String _article;\n\tprivate String _imagedir = \"images\";\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/CheckHeadersTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.FilenameFilter;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.regex.Pattern;\n\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\n\npublic class CheckHeadersTask extends Task {\n\n\t@Override\n\tpublic void execute() throws BuildException {\n\n\t\tString docDir = _docdir;\n\t\tString productType = _productType;\n\n\t\tList<String> dirTypes = new ArrayList<String>();\n\t\tdirTypes.add(\"\");\n\n\t\tif (productType.equals(\"dxp\")) {\n\t\t\tdirTypes.add(\"-dxp\");\n\t\t}\n\n\t\tfor (String dirType : dirTypes) {\n\n\t\t\tFile articlesDir = new File(\"../\" + docDir + \"/articles\" + dirType);\n\t\t\tFile docSetDir = new File(\"../\" + docDir);\n\n\t\t\tif (!articlesDir.exists()) {\n\t\t\t\tif (!dirType.contains(\"dxp\")) {\n\t\t\t\t\tthrow new BuildException(\"FAILURE - no articles directory \" + articlesDir);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tList<File> docSetDirFolders = new ArrayList<File>();\n\t\t\tQueue<File> q = new LinkedList<File>();\n\n\t\t\tFile articlesDirContents[] = articlesDir.listFiles();\n\t\t\tfor (File f : articlesDirContents) {\n\t\t\t\tif (f.isDirectory()) {\n\t\t\t\t\tq.add(f);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\twhile (!q.isEmpty()) {\n\t\t\t\tFile f = q.remove();\n\t\t\t\tdocSetDirFolders.add(f);\n\t\t\t\tFile[] files = f.listFiles();\n\n\t\t\t\tfor (File file : files) {\n\t\t\t\t\tif (file.isDirectory()) {\n\t\t\t\t\t\tq.add(file);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdocSetDirFolders.add(articlesDir);\n\t\t\tdocSetDirFolders.add(docSetDir);\n\n\t\t\tFile docSetDirFoldersArray[] = docSetDirFolders.toArray(\n\t\t\t\t\tnew File[docSetDirFolders.size()]);\n\n\t\t\tList<String> fileList = new ArrayList<String>();\n\n\t\t\tfor (int i = 0; i < docSetDirFoldersArray.length; i++) {\n\n\t\t\t\tFile files[] = docSetDirFoldersArray[i].listFiles(\n\t\t\t\t\t\tnew FilenameFilter() {\n\t\t\t\t\t\t\tString filePatternArg =\n\t\t\t\t\t\t\t\t\t\"([^\\\\\\\\\\\\[\\\\]\\\\|:;%<>]+).markdown\";\n\n\t\t\t\t\t\t\tPattern fileNamePattern = Pattern.compile(\n\t\t\t\t\t\t\t\t\tfilePatternArg);\n\n\t\t\t\t\t\t\tpublic boolean accept(File file, String name) {\n\t\t\t\t\t\t\t\treturn (fileNamePattern.matcher(name).matches());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t);\n\n\t\t\t\tfor (int j = 0; j < files.length; j++) {\n\t\t\t\t\tfileList.add(files[j].getPath());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < fileList.size(); i++) {\n\t\t\t\tString filename = fileList.get(i);\n\t\t\t\tFile inFile = new File(filename);\n\n\t\t\t\ttry {\n\t\t\t\t\tLineNumberReader in = new LineNumberReader(\n\t\t\t\t\t\t\tnew FileReader(inFile));\n\n\t\t\t\t\tString line;\n\t\t\t\t\tString titleLine = null;\n\t\t\t\t\tString titleLineError1 = null;\n\t\t\t\t\tString titleLineError2 = null;\n\t\t\t\t\tint counter = 0;\n\t\t\t\t\tboolean headerSyntaxExists = false;\n\n\t\t\t\t\twhile ((line = in.readLine()) != null) {\n\t\t\t\t\t\tif (counter == 2) {\n\t\t\t\t\t\t\theaderSyntaxExists = true;\n\n\t\t\t\t\t\t\ttitleLine = Files.readAllLines(Paths.get(filename)).get(in.getLineNumber());\n\t\t\t\t\t\t\ttitleLineError1 = Files.readAllLines(Paths.get(filename)).get(in.getLineNumber() - 1);\n\t\t\t\t\t\t\ttitleLineError2 = Files.readAllLines(Paths.get(filename)).get(in.getLineNumber() + 1);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (line.startsWith(\"---\")) {\n\t\t\t\t\t\t\tcounter++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (titleLine != null) {\n\n\t\t\t\t\t\t// Check whether the markdown file starts with the proper single #\n\t\t\t\t\t\t// header. \n\t\t\t\t\t\t// If it doesn't, throw an exception identifying the file\n\n\t\t\t\t\t\tif (!titleLine.startsWith(\"# \")) {\n\n\t\t\t\t\t\t\tString message;\n\n\t\t\t\t\t\t\tif (titleLineError1.startsWith(\"# \") || titleLineError2.startsWith(\"# \")) {\n\t\t\t\t\t\t\t\tmessage = \"FAILURE - \" + filename +\n\t\t\t\t\t\t\t\t\t\t\": File's single # header is spaced incorrectly.\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tmessage = \"FAILURE - \" + filename +\n\t\t\t\t\t\t\t\t\t\t\": File does not start with a single # for a header\";\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (titleLine.startsWith(\"<!--\")) {\n\n\t\t\t\t\t\t\t\tin.close();\n\n\t\t\t\t\t\t\t\tthrow new BuildException(message);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (titleLine.startsWith(\"<\")) {\n\n\t\t\t\t\t\t\t\t// Allow non-comment tags\n\n\t\t\t\t\t\t\t\tcontinue;\n\t\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tin.close();\n\n\t\t\t\t\t\t\tthrow new BuildException(message);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!headerSyntaxExists) {\n\t\t\t\t\t\tString message = \"FAILURE - \" + filename +\n\t\t\t\t\t\t\t\t\": File does not start with proper header syntax.\";\n\t\t\t\t\t\tin.close();\n\t\t\t\t\t\tthrow new BuildException(message);\n\t\t\t\t\t}\n\n\t\t\t\t\tin.close();\n\n\t\t\t\t\t// Throw exception\n\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tthrow new BuildException(e.getLocalizedMessage());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSystem.out.println(\"Finished checking headers in articles\" + dirType);\n\t\t}\n\t}\n\n\tpublic void setDocdir(String docdir) {\n\t\t_docdir = docdir;\n\t}\n\n\tpublic void setProductType(String productType) {\n\t\t_productType = productType;\n\t}\n\n\tprivate String _docdir;\n\tprivate String _productType;\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/CheckImageUtil.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class CheckImageUtil {\n\t\n\tpublic static List<String> checkImgSrc(\n\t\t\tString content, String article, int line, int dirsUp, String imageFolder) {\n\n\t\tList<String> warnings = new ArrayList<String>();\n\n\t\tint pos = -1;\n\t\twhile ((pos=content.indexOf(\"![\")) != -1) {\n\t\t\tint x = content.indexOf(\"](\", pos);\n\t\t\tint y = content.indexOf(\")\", x);\n\n\t\t\tif (y != -1) {\n\t\t\t\tString imgSrc = content.substring(x + 2, y);\n\n\t\t\t\tStringBuffer imagePathStart = new StringBuffer(dirsUp);\n\t\t\t\tfor (int i = 0; i < dirsUp; i++) {\n\t\t\t\t\timagePathStart.append(\"../\");\n\t\t\t\t}\n\n\t\t\t\tString targetImagePath = imagePathStart.toString() +\n\t\t\t\t\timageFolder + \"/\";\n\t\t\t\tif (!imgSrc.startsWith(targetImagePath)) {\n\t\t\t\t\tStringBuffer sb = new StringBuffer(6);\n\t\t\t\t\tsb.append(\"Invalid image path: \");\n\t\t\t\t\tsb.append(imgSrc);\n\t\t\t\t\tsb.append(\" in \");\n\t\t\t\t\tsb.append(article);\n\t\t\t\t\tsb.append(\":\");\n\t\t\t\t\tsb.append(line);\n\t\t\t\t\twarnings.add(sb.toString());\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\timgSrc = imgSrc.replace(targetImagePath, \"./\" + imageFolder + \"/\");\n\n\t\t\t\t\tif (!imgSrc.contains(\"CDATA\") &&\n\t\t\t\t\t\t!(new File(imgSrc).exists())) {\n\n\t\t\t\t\t\tStringBuffer sb = new StringBuffer(\"\\t\");\n\t\t\t\t\t\tsb.append(imgSrc);\n\t\t\t\t\t\tsb.append(\" in \");\n\t\t\t\t\t\tsb.append(article);\n\t\t\t\t\t\tsb.append(\":\");\n\t\t\t\t\t\tsb.append(line);\n\t\t\t\t\t\twarnings.add(sb.toString());\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcontent = content.substring(y + 1);\n\t\t\t}\n\t\t\telse if (x != -1){\n\t\t\t\tcontent = content.substring(x + 2);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcontent = content.substring(pos + 2);\n\t\t\t}\n\t\t}\n\n\t\treturn warnings;\n\t}\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/CheckImagesTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.Set;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\n\npublic class CheckImagesTask extends Task {\n\n\t/**\n\t * Checks for missing images, extra images, or faulty file paths in the\n\t * document directory.\n\t * \n\t * Any of these problems results in a build failure.\n\t */\n\t@Override\n\tpublic void execute() throws BuildException {\n\t\tSystem.out.println(\"Start checking image sources ...\");\n\n\t\tFile docDir = new File(\"../\" + _docdir);\n\t\tif (!docDir.exists()) {\n\t\t\tthrow new BuildException(\"docdir \" + docDir.getAbsolutePath() +\n\t\t\t\t\" could not be found\");\n\t\t}\n\t\tif (!docDir.isDirectory()) {\n\t\t\tthrow new BuildException(\"docdir \" + docDir.getAbsolutePath() +\n\t\t\t\t\" is not a directory\");\n\t\t}\n\n\t\tFile[] docDirFiles = docDir.listFiles();\n\t\tif (docDirFiles.length == 0) {\n\t\t\tthrow new BuildException(\"docdir \" + docDir.getAbsolutePath() +\n\t\t\t\t\" is empty\");\n\t\t}\n\t\t\n\t\t// Get articles\n\t\tString productType = _productType;\n\t\tList<String> dirTypes = new ArrayList<String>();\n\t\tdirTypes.add(\"\");\n\n\t\tif (productType.equals(\"dxp\")) {\n\t\t\tdirTypes.add(\"-dxp\");\n\t\t}\n\t\tif (productType.equals(\"dist\")) {\n\t\t\ttry {\n\t\t\t\treplaceImagePaths(docDir);\n\t\t\t} catch (IOException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t\t\n\t\tfor (String dirType : dirTypes) {\n\n\t\t\tFile articleDir = new File(docDir.getAbsolutePath() + \"/articles\" + dirType);\n\t\t\tFile[] articleDirFiles = articleDir.listFiles();\n\t\t\tList<File> articles = new ArrayList<File>();\n\n\t\t\tif (!articleDir.exists()) {\n\t\t\t\tif (!dirType.contains(\"dxp\")) {\n\t\t\t\t\tthrow new BuildException(\"FAILURE - no articles directory \" + articleDir);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\n\t\t\tQueue<File> q = new LinkedList<File>();\n\t\t\tfor (File f : articleDirFiles) {\n\t\t\t\tq.add(f);\n\t\t\t}\n\t\t\n\t\t\twhile (!q.isEmpty()) {\n\t\t\t\tFile f = q.remove(); \n\t\t\t\n\t\t\t\tif (f.isDirectory()) {\n\t\t\t\t\tFile[] files = f.listFiles();\n\t\t\t\t\n\t\t\t\t\tfor (File file : files) {\n\t\t\t\t\t\tq.add(file);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (f.getName().endsWith(\".markdown\")) {\n\t\t\t\t\t\tarticles.add(f);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\n\t\t\t// Get a map of articles to lists of referenced images\n\t\t\tMap<File, List<String>> imagePathsMap = new HashMap<File, List<String>>();\n\t\t\n\t\t\tfor (File article : articles) {\n\t\t\t\tList<String> imagePaths = getImagePaths(article);\n\t\t\t\n\t\t\t\timagePathsMap.put(article, imagePaths);\n\t\t\t}\n\t\t\n\t\t\t// Get list of images\n\t\t\tFile imgDir = new File(docDir.getAbsolutePath() + \"/images\" + dirType);\n\t\t\tif (!imgDir.exists()) {\n\t\t\t\tif (!dirType.contains(\"dxp\")) {\n\t\t\t\t\tthrow new BuildException(\"imgdir \" + imgDir.getAbsolutePath() +\n\t\t\t\t\t\t\t\" could not be found\");\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!docDir.isDirectory()) {\n\t\t\t\tthrow new BuildException(\"imgdir \" + imgDir.getAbsolutePath() +\n\t\t\t\t\t\t\" is not a directory\");\n\t\t\t}\n\t\t\n\t\t\tFile[] imagesArray = imgDir.listFiles();\n\t\t\n\t\t\tList<File> images = Arrays.asList(imagesArray);\n\t\t\n\t\t\tString imageDestination = \"..\" + File.separator + _docdir + File.separator + \"images\";\n\n\t\t\tcheckImages(images, imagePathsMap, _resolveImages, imageDestination);\n\n\t\t\tSystem.out.println(\"Finished checking image sources in articles\" + dirType);\n\t\t}\n\t}\n\n\tpublic void setResolveImages(boolean resolveImages) {\n\t\t_resolveImages = resolveImages;\n\t}\n\n\tpublic void setDocdir(String docdir) {\n\t\t_docdir = docdir;\n\t}\n\t\n\tpublic void setProductType(String productType) {\n\t\t_productType = productType;\n\t}\n\t\n\t/**\n\t * Compares the actual images in the document directory to the images\n\t * reference by the Markdown files in the document directory.\n\t * \n\t * Missing images, extra images, or faulty file paths result in a build\n\t * failure.\n\t * \n\t * @param images the actual images in the document directory\n\t * @param imagePathsMap a map from each Markdown file in the document\n\t *        directory to a list of the image references in the Markdown file\n\t * @param resolveImages whether to attempt to copy images from the\n\t *        document's <code>images</code> folder into the images destination\n\t *        folder\n\t * @param imageDestination the destination folder for images\n\t */\n\tprivate static void checkImages(List<File> images, Map<File,\n\t\t\tList<String>> imagePathsMap, boolean resolveImages,\n\t\t\tString imageDestination) {\n\t\tList<String> imagePaths = new ArrayList<String>();\n\t\t\n\t\tCollection<List<String>> imagePathsLists = imagePathsMap.values();\n\n\t\tfor (List<String> imagePathsList : imagePathsLists) {\n\t\t\timagePaths.addAll(imagePathsList);\n\t\t}\n\t\t\n\t\tList<String> referencedImageNames = new ArrayList<String>();\n\n\t\tfor (String imagePath : imagePaths) {\n\t\t\timagePath = getFileName(imagePath);\n\t\t\t\n\t\t\treferencedImageNames.add(imagePath);\n\t\t}\n\t\t\n\t\tList<String> imageNames = new ArrayList<String>();\n\t\t\n\t\tfor (File image : images) {\n\t\t\tString imageName = image.getName();\n\n\t\t\tif (imageName.startsWith(\".\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\timageNames.add(imageName);\n\t\t}\n\t\t\n\t\tList<String> errors = new ArrayList<String>();\n\t\tList<String> warnings = new ArrayList<String>();\n\n\t\tSet<File> articles = imagePathsMap.keySet();\n\n\t\t// Report missing images\n\t\tSet<String> missingImages = new HashSet<String>();\n\n\t\tfor (File article : articles) {\n\t\t\tList<String> imagePathsList = imagePathsMap.get(article);\n\n\t\t\tfor (String imagePath : imagePathsList) {\n\t\t\t\tString imageFileName = getFileName(imagePath);\n\n\t\t\t\tif (!imageNames.contains(imageFileName)) {\n\n\t\t\t\t\tFile source = new File(\"images\" + File.separator + imageFileName);\n\n\t\t\t\t\tFile dest = new File(imageDestination + File.separator + imageFileName);\n\n\t\t\t\t\tif (resolveImages && source.exists()) {\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tSystem.out.println(\"Copying \" + imageFileName + \" from \" + source + \" to \" + dest);\n\t\t\t\t\t\t\t    FileUtils.copyFile(source, dest);\n\t\t\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t\t    e.printStackTrace();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!dest.exists()) {\n\t\t\t\t\t\tString articlePath = article.getPath();\n\t\t\t\t\t\tString articleString = articlePath.substring(articlePath.indexOf(\"..\"), articlePath.length());\n\n\t\t\t\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\t\t\t\tsb.append(articleString);\n\t\t\t\t\t\tsb.append(\": References missing image: \");\n\t\t\t\t\t\tsb.append(imageFileName);\n\n\t\t\t\t\t\terrors.add(sb.toString());\n\n\t\t\t\t\t\tmissingImages.add(imagePath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Report extra images\n\t\tfor (String imageName : imageNames) {\n\t\t\tif (!referencedImageNames.contains(imageName)) {\n\t\t\t\twarnings.add(\"Extra image: \" + imageName);\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Report faulty image paths \n\t\tfor (File article : articles) {\n\t\t\tString parentPath = article.getParent();\n\t\t\t\n\t\t\tList<String> imagePathsList = imagePathsMap.get(article);\n\t\t\t\n\t\t\tfor (String imagePath : imagePathsList) {\n\t\t\t\tif (missingImages.contains(imagePath)) {\n\t\t\t\t\t// Already reported as missing\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tFile image = new File(parentPath + \"/\" + imagePath);\n\t\t\t\t\n\t\t\t\tif (!image.exists() || image.isDirectory()) {\n\t\t\t\t\tString articlePath = article.getPath();\n\t\t\t\t\tString articleString = articlePath.substring(articlePath.indexOf(\"..\"), articlePath.length());\n\n\t\t\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\t\t\tsb.append(articleString);\n\t\t\t\t\tsb.append(\": Faulty image path: \");\n\t\t\t\t\tsb.append(imagePath);\n\n\t\t\t\t\terrors.add(sb.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!errors.isEmpty()) {\n\t\t\tfor (String error : errors) {\n\t\t\t\tSystem.err.println(\"ERROR - \" + error);\n\t\t\t}\n\t\t\t\n\t\t\tthrow new BuildException(\"Missing images, extra images, or faulty image paths\");\n\t\t}\n\t\t\n\t\tif (!warnings.isEmpty()) {\n\t\t\tfor (String warning : warnings) {\n\t\t\t\tSystem.err.println(\"WARNING - \" + warning);\n\t\t\t}\n\t\t\t\n\t\t}\n\t}\n\t\n\tprivate static String getFileName(String path) {\n\t\tif (!path.contains(\"/\")) {\n\t\t\treturn path;\n\t\t}\n\t\t\n\t\tint index = path.lastIndexOf(\"/\");\n\t\t\n\t\tString fileName = path.substring(index);\n\n\t\tfileName = fileName.replace(\"/\", \"\");\n\t\t\n\t\treturn fileName;\n\t}\n\t\n\t/**\n\t * Returns a list of the relative paths of the images referenced in a\n\t * Markdown file.\n\t * \n\t * @param  article the Markdown file\n\t * @return a list of the relative paths of the images referenced in a\n\t *         Markdown file (may be empty)\n\t */\n\tprivate static List<String> getImagePaths(File article) {\n\t\tList<String> imagePaths = new ArrayList<String>();\n\t\t\n\t\tif (article.isDirectory()) {\n\t\t\treturn imagePaths;\n\t\t}\n\t\t\n\t\tif (!article.getName().endsWith(\".markdown\")) {\n\t\t\treturn imagePaths;\n\t\t}\n\t\t\n\t\tList<String> lines = null;\n\t\ttry {\n\t\t\tlines = Files.readAllLines(Paths.get(article.getCanonicalPath()), StandardCharsets.UTF_8);\n\t\t}\n\t\tcatch (IOException ioe) {\n\t\t\tSystem.err.println(ioe.getLocalizedMessage());\n\n\t\t\treturn imagePaths;\n\t\t}\n\t\t\n\t\tif (lines == null) {\n\t\t\treturn imagePaths;\n\t\t}\n\t\t\n\t\t// Match lines containing expressions of the form ![...](...)\n\t\tString regex = \".*!\\\\[.*\\\\]\\\\(.*\\\\).*\";\n\t\tint lineNumber = 0;\n\n\t\tfor (String line : lines) {\n\t\t\tlineNumber++;\n\t\t\tline = line.trim();\n\t\t\t\n\t\t\tif (line.matches(regex)) {\n\t\t\t\tint begin = line.lastIndexOf(\"(\");\n\t\t\t\tline = line.substring(begin);\n\t\t\t\tint end = line.indexOf(\")\");\n\n\t\t\t\ttry {\n\t\t\t\t\tline = line.substring(0, end);\n\t\t\t\t}\n\t\t\t\tcatch (StringIndexOutOfBoundsException e) {\n\t\t\t\t\tthrow new BuildException(\"ERROR: The following article has multiple \"\n\t\t\t\t\t\t\t+ \"opening parentheses on a line. Please move the \"\n\t\t\t\t\t\t\t+ \"last opening parenthesis to a new line.\\n\"\n\t\t\t\t\t\t\t+ \"ARTICLE:LINE - \" + article.getPath() + \":\" + lineNumber);\n\t\t\t\t}\n\t\t\t\tline = line.replace(\"(\", \"\");\n\t\t\t\tline = line.replace(\")\", \"\");\n\t\t\t\t\n\t\t\t\timagePaths.add(line);\n\t\t\t}\n\n\t\t\t// Check for <img> elements\n\n\t\t\tif (line.contains(\"<img\") && line.contains(\"../images\") && !line.contains(\"<![CDATA[\")) {\n\t\t\t\tint begin = line.indexOf(\"<img\");\n\t\t\t\tint end = line.indexOf(\">\", begin);\n\t\t\t\tint src = line.indexOf(\"src\", begin);\n\t\t\t\tint images = line.indexOf(\"images\");\n\n\t\t\t\tif (src > 0 &&\n\t\t\t\t\timages > src) {\n\n\t\t\t\t\tif (end < 0 ||\n\t\t\t\t\t\t(end > 1 && images < end)) {\n\n\t\t\t\t\t\tint quote1 = line.indexOf(\"\\\"\", src);\n\t\t\t\t\t\tint quote2 = line.indexOf(\"\\\"\", quote1 +1);\n\n\t\t\t\t\t\tString srcImage = line.substring(quote1 + 1, quote2);\n\n\t\t\t\t\t\timagePaths.add(srcImage);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn imagePaths;\n\t}\n\n\tprivate void replaceImagePaths(File docDir) throws IOException {\n\t\tFile articleDir = new File(docDir.getAbsolutePath() + \"/articles\");\n\t\tFile[] articleDirFiles = articleDir.listFiles();\n\t\tList<File> articles = new ArrayList<File>();\n\t\t\n\t\tQueue<File> q = new LinkedList<File>();\n\t\tfor (File f : articleDirFiles) {\n\t\t\tq.add(f);\n\t\t}\n\t\n\t\twhile (!q.isEmpty()) {\n\t\t\tFile f = q.remove(); \n\t\t\n\t\t\tif (f.isDirectory()) {\n\t\t\t\tFile[] files = f.listFiles();\n\t\t\t\n\t\t\t\tfor (File file : files) {\n\t\t\t\t\tq.add(file);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (f.getName().endsWith(\".markdown\")) {\n\t\t\t\t\tarticles.add(f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tfor (File article : articles) {\n\t\t\tString articlePath = article.getAbsolutePath();\n\t\t\tFile markdownfile = new File(articlePath);\n\n\t\t\tString source = FileUtils.readFileToString(markdownfile);\n\t\t\t\n\t\t\tString find = \"../images-dxp/\";\n\t\t\tString replace = \"../images/\";\n\t\t\tString imagePath = source.replaceAll(find, replace);\n\t\t\t\n\t\t\tFileUtils.writeStringToFile(markdownfile, imagePath);\n\t\t}\n\n\t}\n\n\tprivate String _docdir;\n\tprivate String _productType;\n\tprivate boolean _resolveImages;\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/CheckIntrosTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\n\npublic class CheckIntrosTask extends Task {\n\t\n\t@Override\n\tpublic void execute() throws BuildException {\n\n\t\tFile dir = new File(\"../\" + _docdir);\n\t\tif (!dir.exists()) {\n\t\t\tthrow new BuildException(\"docdir \" + dir.getAbsolutePath() +\n\t\t\t\t\t\" could not be found\");\n\t\t}\n\n\t\tFile articleDir = new File(dir.getAbsolutePath() + \"/articles\");\n\t\tFile[] articleFolders = articleDir.listFiles(new FileFilter() {\n\t\t    @Override\n\t\t    public boolean accept(File file) {\n\t\t        return file.isDirectory();\n\t\t    }\n\t\t});\n\n\t\tfor (File articleFolder : articleFolders) {\n\t\t\tcheckFolderForIntros(articleFolder);\n\t\t}\n\n\t\tif (!errorDirs.isEmpty()) {\n\t\t\tSystem.out.println(\"ERROR - Missing introduction markdown articles\"\n\t\t\t\t\t+ \" ...\");\n\n\t\t\tfor (File errorDir : errorDirs){\n\t\t\t\tSystem.out.println(errorDir);\t\t\t\n\t\t\t}\n\t\t\tthrow new BuildException(\"Missing introduction markdown articles\");\n\t\t}\n\n\t\tSystem.out.println(\"Finished checking for introduction markdown \"\n\t\t\t\t+ \"articles.\");\n\t}\n\t\n\tpublic void setDocdir(String docdir) {\n\t\t_docdir = docdir;\n\t}\n\n\tprivate void checkFolderForIntros(File folder) {\n\n\t\tboolean containsIntro = false;\n\n\t\tfor (File fileEntry : folder.listFiles()) {\n\t\t\tif (fileEntry.isDirectory()) {\n\t\t\t\tcheckFolderForIntros(fileEntry);\n\t\t\t}\n\t\t\telse if (!containsIntro) {\n\n\t\t\t\tif (fileEntry.getName().endsWith(\"introduction.markdown\") ||\n\t\t\t\t\t\tfileEntry.getName().endsWith(\"intro.markdown\")) {\n\n\t\t\t\t\tcontainsIntro = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!containsIntro) {\n\t\t\terrorDirs.add(folder);\n\t\t}\n\t}\n\n\tprivate String _docdir;\n\tprivate List<File> errorDirs = new ArrayList<File>();\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/CheckLatestCommitTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Scanner;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\nimport java.util.zip.ZipOutputStream;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\nimport org.eclipse.jgit.api.Git;\nimport org.eclipse.jgit.diff.DiffEntry;\nimport org.eclipse.jgit.diff.DiffFormatter;\nimport org.eclipse.jgit.lib.ObjectId;\nimport org.eclipse.jgit.lib.ObjectReader;\nimport org.eclipse.jgit.lib.Ref;\nimport org.eclipse.jgit.lib.Repository;\nimport org.eclipse.jgit.storage.file.FileRepositoryBuilder;\nimport org.eclipse.jgit.treewalk.CanonicalTreeParser;\nimport org.eclipse.jgit.util.io.DisabledOutputStream;\n\n/**\n * Finds the changed files (added, deleted, modified, and renamed) that occurred\n * since the last publication date. This is done by comparing two Git commits\n * and finding what changed between them. These files are gathered in a Zip file\n * and formatted for publication to Liferay's Knowledge Base portlet.\n *\n * @author Cody Hoag\n */\npublic class CheckLatestCommitTask extends Task {\n\n\t@Override\n\tpublic void execute() throws BuildException {\n\n\t\tString docDir = _docDir;\n\t\tString docLocation = _docLocation;\n\t\tString distDir = _distDir;\n\t\tString zipName = _zipName;\n\t\tString dxpParam = _dxpParam;\n\t\tboolean dxp = Boolean.parseBoolean(dxpParam);\n\n\t\tFile dir = new File(\"../\" + docDir);\n\t\tString absDir = dir.getAbsolutePath();\n\t\tFile commitFile = new File(absDir + \"/last-publication-commit.txt\");\n\n\t\tString docLanguage = docLocation.substring(0, docLocation.indexOf(\"/\"));\n\n\t\ttry {\n\t\tString headCommit = getHeadCommit();\n\n\t\t// Create new metadata file with current HEAD commit, if one doesn't exist.\n\t\tif (!commitFile.exists()) {\n\t\t\tSystem.out.println(\"No previous publication commit available. Creating ./last-publication-commit.txt file. \"\n\t\t\t\t\t+ \"Subsequent dists will generate Zip with only modified files.\");\n\n\t\t\tgenerateLatestCommitFile(headCommit);\n\t\t\tSystem.exit(0);\n\t\t}\n\n\t\t// If a metadata file exists, copy the last published commit and find all\n\t\t// modified files since that commit's publication.\n\n\t\tString lastPublishedCommit = FileUtils.readFileToString(commitFile);\n\n\t\tif (!headCommit.equals(lastPublishedCommit)) {\n\t\t\tList<String> modifiedFiles = getModifiedFiles(lastPublishedCommit, docLanguage, docLocation, docDir, distDir, dxp);\n\n\t\t\t// build out Zip with these new modified file paths\n\n\t\t\tSet<String> modifiedImages = new HashSet<String>();\n\t\t\tSet<String> modifiedArticles = new HashSet<String>();\n\n\t\t\t// Separate modified/new MD files and images\n\t\t\tfor (String file : modifiedFiles) {\n\t\t\t\tif (!file.contains(\"images\") && !file.contains(\"articles\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse if (file.endsWith(\".png\") || file.endsWith(\".jpg\") || file.endsWith(\".gif\")) {\n\t\t\t\t\tmodifiedImages.add(file);\n\t\t\t\t}\n\t\t\t\telse if (file.endsWith(\".markdown\") || file.endsWith(\".md\")) {\n\t\t\t\t\tmodifiedArticles.add(file);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Unzip dist Zip\n\t\t\tunzipFile(docDir, zipName);\n\t\t\tFile unzippedDir = new File(\"../\" + docDir + \"/\" + zipName);\n\t\t\t\n\t\t\t// Find all MD and image files in directory\n\t\t\tSet<File> allZipArticles = getMarkdownFiles(unzippedDir);\n\t\t\tSet<File> allZipImages = getImageFiles(unzippedDir);\n\t\t\t\n\t\t\t// Convert modified file paths to consistent paths pointing to dist zip files\n\t\t\tSet<String> modifiedZipArticles = mapModFilesToZipFiles(modifiedArticles, allZipArticles, \"articles\");\n\t\t\tSet<String> modifiedZipImages = mapModFilesToZipFiles(modifiedImages, allZipImages, \"images\");\n\t\t\t\n\t\t\tSet<String> articlesWithModifiedImages = getArticlesWithModifiedImages(allZipArticles, modifiedZipImages, docDir);\n\n\t\t\tmodifiedZipArticles.addAll(articlesWithModifiedImages);\n\t\t\t\n\t\t\t// Find and add all modified/new MD files' intro file. A modified file must\n\t\t\t// be accompanied with hierarchy of parent intros to import correctly.\n\t\t\tSet<String> introFiles = getIntroFiles(modifiedZipArticles);\n\t\t\tmodifiedZipArticles.addAll(introFiles);\n\n\t\t\t// Scan each MD file for remainder of images to include in ZIP file. When\n\t\t\t// re-importing a new MD file, all of its images must also be re-imported.\n\t\t\tSet<String> markdownImages = scanMarkdownForAllImages(modifiedZipArticles);\n\n\t\t\tmodifiedZipImages.addAll(markdownImages);\n\n\t\t\tString metadataFile = zipPrePath + \".METADATA\";\n\n\t\t\t\ttry {\n\t\t\t\t\tSystem.out.println(\"Creating\" + zipName + \"-diffs-only.zip\");\n\t\t\t\t\tFileOutputStream fileOutputStream = new FileOutputStream(zipName + \"-diffs-only.zip\");\n\t\t\t\t\tZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);\n\n\t\t\t\t\tfor (String markdown : modifiedZipArticles) {\n\t\t\t\t\t\taddToZipFile(markdown, zipOutputStream, distDir);\n\t\t\t\t\t}\n\t\t\t\t\tfor (String image : modifiedZipImages) {\n\t\t\t\t\t\taddToZipFile(image, zipOutputStream, distDir);\n\t\t\t\t\t}\n\n\t\t\t\t\taddToZipFile(metadataFile, zipOutputStream, distDir);\n\n\t\t\t\t\tzipOutputStream.close();\n\t\t\t\t\tfileOutputStream.close();\n\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\n\t\t\tgenerateLatestCommitFile(headCommit);\n\t\t}\n\t\telse {\n\t\t\tSystem.out.println(\"There are no new files to publish!\");\n\t\t\tSystem.exit(0);\n\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new BuildException(e.getLocalizedMessage());\n\t\t}\n\t}\n\n\tpublic void setDocDir(String docDir) {\n\t\t_docDir = docDir;\n\t}\n\n\tpublic void setDocLocation(String docLocation) {\n\t\t_docLocation = docLocation;\n\t}\n\n\tpublic void setDxpParam(String dxpParam) {\n\t\t_dxpParam = dxpParam;\n\t}\n\n\tpublic void setDistDir(String distDir) {\n\t\t_distDir = distDir;\n\t}\n\n\tpublic void setZipName(String zipName) {\n\t\t_zipName = zipName;\n\t}\n\n\t/**\n\t * Adds the modified file to a distributable Zip file.\n\t *\n\t * @param  modFile the modified file\n\t * @param  zipOutputStream the output stream used to add files to the Zip\n\t * @param  distDir the folder where the Zip is generated (e.g., {@code dist})\n\t * @throws FileNotFoundException if the modified file was not found\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static void addToZipFile(String modFile, ZipOutputStream zipOutputStream, String distDir)\n\t\t\tthrows FileNotFoundException, IOException {\n\n\t\tString uniformDistDir = distDir.replaceAll(\"/\", Matcher.quoteReplacement(File.separator));\n\n\t\tint folderIndex = modFile.indexOf(uniformDistDir) + uniformDistDir.length() + 1;\n\t\tint begIndex = modFile.indexOf(File.separator, folderIndex) + 1;\n\t\tString destModFile = modFile.substring(begIndex, modFile.length());\n\n\t\tSystem.out.println(\"Adding \" + destModFile + \" to zip file\");\n\n\t\tFile file = new File(modFile);\n\t\tFileInputStream fileInputStream = new FileInputStream(file);\n\t\tZipEntry zipEntry = new ZipEntry(destModFile);\n\t\tzipOutputStream.putNextEntry(zipEntry);\n\n\t\tbyte[] bytes = new byte[1024];\n\t\tint len;\n\t\twhile ((len = fileInputStream.read(bytes)) >= 0) {\n\t\t\tzipOutputStream.write(bytes, 0, len);\n\t\t}\n\n\t\tzipOutputStream.closeEntry();\n\t\tfileInputStream.close();\n\t}\n\n\t/**\n\t * Returns the parent intro articles for the given Markdown files.\n\t *\n\t * @param markdownFiles the Markdown files to find intros for\n\t * @return the parent intro articles\n\t */\n\tprivate static Set<String> getIntroFiles(Set<String> markdownFiles) {\n\n\tSet<String> fileList = new HashSet<String>();\n\t\n\tfor (String markdownFile : markdownFiles) {\n\n\t\t\n\t\tFile article = new File(markdownFile);\n\t\tFile parentDir = article.getParentFile();\n\n\t\tboolean containsIntro = true;\n\n\t\twhile (containsIntro) {\n\t\t\t\n\t\t\tFile[] parentFiles = parentDir.listFiles();\n\n\t\t\tcontainsIntro = false;\n\n\t\t\tfor (File file : parentFiles) {\n\t\t\t\tif (file.getName().endsWith(\"introduction.markdown\") ||\n\t\t\t\t\t\tfile.getName().endsWith(\"intro.markdown\")) {\n\n\t\t\t\t\tfileList.add(file.toString());\n\t\t\t\t\tcontainsIntro = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tparentDir = parentDir.getParentFile();\n\t\t}\n\t}\n\t\n\t\treturn fileList;\n\t}\n\n\t/**\n\t * Returns all the Markdown files in the given folder.\n\t *\n\t * @param  dir the folder in which to find Markdown files\n\t * @return the Markdown files\n\t */\n\tprivate static Set<File> getMarkdownFiles(File dir) {\n\n\t\tSet<File> chFiles = new HashSet<File>();\n\t\tFile articleDir = new File(dir.getAbsolutePath() + \"/articles\");\n\t\tFile[] articles = (File[])ArrayUtils.addAll(articleDir.listFiles());\n\n\t\tfor (File article : articles) {\n\n\t\t\tif (article.getName().contains(\".\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tFile[] allFiles = article.listFiles();\n\n\t\t\tfor (File file : allFiles) {\n\n\t\t\t\tif (!file.toString().endsWith(\"markdown\") && !file.toString().endsWith(\"md\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tchFiles.add(file);\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn chFiles;\n\t}\n\n\t/**\n\t * Writes the provided head Git commit number to a {@code .txt} file stored\n\t * in the {@code docDir}. This is referenced every time the associated Ant\n\t * target is executed, serving as the last published Git commit.\n\t *\n\t * @param headCommit   the head commit on the current Git branch\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static void generateLatestCommitFile(String headCommit)\n\t\t\tthrows IOException {\n\n\t\tPrintWriter writer = new PrintWriter(\"last-publication-commit.txt\", \"UTF-8\");\n\t\twriter.print(headCommit);\n\t\twriter.close();\n\t}\n\n\t/**\n\t * Returns the Markdown articles containing the given modified images.\n\t *\n\t * @param  zipMarkdownFiles the articles to scan for modified images\n\t * @param  modifiedImages the modified images to search for\n\t * @param  docDir the file's parent folder (e.g., {@code tutorials})\n\t * @return the Markdown articles containing the given modified images\n\t */\n\tprivate static Set<String> getArticlesWithModifiedImages(Set<File> zipMarkdownFiles, Set<String> modifiedImages, String docDir) {\n\n\t\tSet<String> zipMarkdownFilesWithImageFinal = new HashSet<String>();\n\n\t\tfor (String imgPath : modifiedImages) {\n\n\t\t\t// Scan directory's MD files for modified/new image\n\t\t\tSet<File> zipMarkdownFilesWithImage = scanMarkdownForImage(imgPath, zipMarkdownFiles);\n\n\t\t\t// Add the set of MD files that contain the image to a master set\n\t\t\tfor (File file : zipMarkdownFilesWithImage) {\n\t\t\t\tzipMarkdownFilesWithImageFinal.add(file.toString());\n\t\t\t}\n\t\t}\n\n\t\treturn zipMarkdownFilesWithImageFinal;\n\t\t\n\t}\n\n\t/**\n\t * Returns the most current Git commit associated with the current\n\t * Git repository.\n\t *\n\t * @return the most current Git commit\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static String getHeadCommit() throws IOException {\n\n\t\tRepository repo = openGitRepository();\n\t\tRef head = repo.getRef(\"HEAD\");\n\t\tString headCommit = head.getObjectId().getName();\n\t\trepo.close();\n\n        return headCommit;\n\t}\n\n\t/**\n\t * Returns all the images residing in the given folder.\n\t *\n\t * @param  dir the folder to grab images from\n\t * @return the images residing in the given folder\n\t */\n\tprivate static Set<File> getImageFiles(File dir) {\n\n\t\tSet<File> imageFiles = new HashSet<File>();\n\t\tFile imageDir = new File(dir.getAbsolutePath() + \"/images\");\n\t\tFile[] images = (File[])ArrayUtils.addAll(imageDir.listFiles());\n\t\t\n\t\tfor (File img : images) {\n\t\t\tif (img.toString().endsWith(\".png\") || img.toString().endsWith(\".jpg\") ||\n\t\t\t\t\timg.toString().endsWith(\".gif\")) {\n\t\t\t\timageFiles.add(img);\n\t\t\t}\n\t\t}\n\n\t\treturn imageFiles;\n\t}\n\n\t/**\n\t * Returns the files that were modified since the last published Git commit.\n\t *\n\t * @param  commit the last published Git commit\n\t * @param  docLocation the folder path following {@code liferay-docs} (e.g,\n\t *         {@code develop/tutorials}\n\t * @param  docDir the parent folder of where the Ant task was executed (e.g.,\n\t *         {@code tutorials})\n\t * @param  distDir the folder where the Zip is generated (e.g., {@code dist})\n\t * @param  dxp whether to include DXP-only modified files\n\t * @return the files that were modified since the last published Git commit\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static List<String> getModifiedFiles(String commit, String docLanguage, String docLocation, String docDir, String distDir, boolean dxp)\n\t\t\tthrows IOException {\n\n\t\tRepository repo = openGitRepository();\n\t\tGit git = new Git(repo);\n\t\tObjectReader reader = git.getRepository().newObjectReader();\n\n\t\tCanonicalTreeParser oldTreeIter = new CanonicalTreeParser();\n\t\tObjectId oldTree = git.getRepository().resolve(commit + \"^{tree}\");\n\t\toldTreeIter.reset(reader, oldTree);\n\n\t\tCanonicalTreeParser newTreeIter = new CanonicalTreeParser();\n\t\tObjectId newTree = git.getRepository().resolve(\"HEAD^{tree}\");\n\t\tnewTreeIter.reset(reader, newTree);\n\n\t\tDiffFormatter diffFormatter = new DiffFormatter(DisabledOutputStream.INSTANCE);\n\t\tdiffFormatter.setRepository(git.getRepository());\n\t\tdiffFormatter.setDetectRenames(true);\n\t\tList<DiffEntry> entries = diffFormatter.scan(oldTreeIter, newTreeIter);\n\n\t\tList<String> modifiedFiles = new ArrayList<String> ();\n\t\tList<String> deletedFiles = new ArrayList<String> ();\n\t\t//HashMap<String, String> renamedFiles = new HashMap<String, String> ();\n\n\t\t// Be sure to convert single nested folders (e.g., user) so they're searchable\n\t\tif (docLocation.equals(docLanguage + \"/./\" + docDir)) {\n\t\t\tdocLocation = docLanguage + \"/\" + docDir;\n\t\t}\n\n\t\tfor (DiffEntry entry : entries) {\n\n\t\t\tString entryPath = entry.getNewPath();\n\t\t\t\n\t\t\tif (entryPath.startsWith(docLocation)) {\n\n\t\t\t\tif (!dxp && \n\t\t\t\t\t\t(entryPath.contains(\"/articles-dxp/\") ||\n\t\t\t\t\t\tentryPath.contains(\"/images-dxp/\"))) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (dxp && entryPath.contains(\"/articles-dxp/\")) {\n\t\t\t\t\tentryPath = entryPath.replace(\"/articles-dxp/\", \"/articles/\");\n\t\t\t\t}\n\n\t\t\t\tif (dxp && entryPath.contains(\"/images-dxp/\")) {\n\t\t\t\t\tentryPath = entryPath.replace(\"/images-dxp/\", \"/images/\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//if (entry.getChangeType().toString().equals(\"RENAME\")) {\n\t\t\t\t//\trenamedFiles.put(entry.getOldPath(), entryPath);\n\t\t\t\t//\tmodifiedFiles.add(entryPath);\n\t\t\t\t//}\n\t\t\t\t//else {\n\t\t\t\tmodifiedFiles.add(entryPath);\n\t\t\t\t//}\n\t\t\t}\n\t\t\telse if (entry.getOldPath().startsWith(docLocation) &&\n\t\t\t\t\tentry.getChangeType().toString().equals(\"DELETE\")) {\n\t\t\t\tdeletedFiles.add(entry.getOldPath());\n\t\t\t}\n\t\t}\n\n\t\tif (modifiedFiles.isEmpty()) {\n\t\t\tSystem.out.println(\"There are no new files to publish!\");\n\t\t\tSystem.exit(0);\n\t\t}\n\n\t\tif (!deletedFiles.isEmpty()) {\n\t\t\twriteDeletedTextFile(deletedFiles, distDir);\n\t\t}\n\n\t\trepo.close();\n\n\t\treturn modifiedFiles;\n\t}\n\n\t/**\n\t * Converts the modified file paths as they're stored in the Github repo to\n\t * the regular distributable Zip's file path. This ensures that the articles\n\t * have been overwritten with their DXP counterparts and/or the tokens have\n\t * been applied.\n\t *\n\t * @param  modifiedFiles the modified files to convert\n\t * @param  zipFiles the Zip files, which are used solely to extract its\n\t *         general path\n\t * @param  fileType the file type (article or image)\n\t * @return the converted file paths\n\t */\n\tprivate static Set<String> mapModFilesToZipFiles(Set<String> modifiedFiles, Set<File> zipFiles, String fileType) {\n\t\t\n\t\t// Zip:\n\t\t// .\\dist\\lp-ce-7.1-discover-portal\\articles\\210-setting-up\\08-custom-fields.markdown\n\t\t\n\t\t// modified\n\t\t// discover/portal/articles/210-setting-up/08-custom-fields.markdown\n\t\t\n\t\tFile zipFile = (File) zipFiles.toArray()[0];\n\t\tString zipFileString = zipFile.toString();\n\t\tint endIndex = zipFileString.indexOf(fileType + File.separator);\n\t\tzipPrePath = zipFileString.substring(0, endIndex);\n\t\t\n\t\tSet<String> convertedFiles = new HashSet<String>();\n\t\t\n\t\tfor (String modifiedFile : modifiedFiles) {\n\t\t\tint begIndex = modifiedFile.indexOf(\"/\" + fileType) + 1;\n\t\t\tString partialPath = modifiedFile.substring(begIndex, modifiedFile.length());\n\t\t\tString uniformPath = partialPath.replaceAll(\"/\", Matcher.quoteReplacement(File.separator));\n\n\t\t\tconvertedFiles.add(zipPrePath + uniformPath);\n\t\t}\n\n\t\treturn convertedFiles;\n\t}\n\n\t/**\n\t * Opens the Git repository connection.\n\t *\n\t * @return the Git repository\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static Repository openGitRepository() throws IOException {\n\n\t\tFileRepositoryBuilder repoBuilder = new FileRepositoryBuilder();\n\t\tRepository repo = repoBuilder.readEnvironment().findGitDir().build();\n\n\t\treturn repo;\n\t}\n\n\t/**\n\t * Returns the images displayed in the given Markdown articles. When an\n\t * article is republished, its images must also be included.\n\t *\n\t * @param  modifiedArticles the modified Markdown articles\n\t * @return the images displayed in the given Markdown articles\n\t */\n\tprivate static Set<String> scanMarkdownForAllImages(Set<String> modifiedArticles) {\n\t\t\n\t\tSet<File> markdownImages = new HashSet<File>();\n\t\tSet<String> markdownImagesString = new HashSet<String>();\n\t\t\n\t\tString zipFile = (String) modifiedArticles.toArray()[0];\n\n\t\tint endIndex = zipFile.indexOf(\"articles\" + File.separator);\n\t\tString zipPrePath = zipFile.substring(0, endIndex);\n\n\t\tfor (String modifiedArticle : modifiedArticles) {\n\n\t\t\tScanner scanner = null;\n\t\t\tFile file = new File(modifiedArticle);\n\t\t\t\n\t\t\ttry {\n\t\t\t\tscanner = new Scanner(file);\n\n\t\t\t\twhile (scanner.hasNextLine()) {\n\t\t\t\t\tString lineFromFile = scanner.nextLine();\n\n\t\t\t\t\t// Match lines containing expressions of the form ![...](...)\n\t\t\t\t\tString regex = \".*!\\\\[.*\\\\]\\\\(.*\\\\).*\";\n\n\t\t\t\t\tlineFromFile = lineFromFile.trim();\n\n\t\t\t\t\tif (lineFromFile.matches(regex)) {\n\t\t\t\t\t\tint begin = lineFromFile.indexOf(\"../images\");\n\n\t\t\t\t\t\tif (begin < 0) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbegin = begin + 3;\n\t\t\t\t\t\tint end = lineFromFile.indexOf(\")\", begin);\n\n\t\t\t\t\t\tString img = lineFromFile.substring(begin, end);\n\n\t\t\t\t\t\tFile markdownImage = new File(img);\n\n\t\t\t\t\t\tmarkdownImages.add(markdownImage);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (FileNotFoundException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t\t\n\t\tfor (File markdownImage : markdownImages) {\n\t\t\tmarkdownImagesString.add(zipPrePath + markdownImage.toString());\n\t\t}\n\n\t\treturn markdownImagesString;\n\t}\n\n\t/**\n\t * Returns the Markdown articles that reference the given image.\n\t *\n\t * @param  imgPath the image's path to search for\n\t * @param  files the files in which to search for the image\n\t * @return the Markdown articles that reference the given image\n\t */\n\tprivate static Set<File> scanMarkdownForImage(String imgPath, Set<File> files) {\n\n\t\tint imgStart = imgPath.lastIndexOf(File.separator) + 1;\n\t\tString img = imgPath.substring(imgStart, imgPath.length());\n\n\t\tSet<File> filesWithImage = new HashSet<File>();\n\n\t\tfor (File file : files) {\n\n\t\t\tScanner scanner = null;\n\t\t\ttry {\n\t\t\t\tscanner = new Scanner(file);\n\n\t\t\t\twhile (scanner.hasNextLine()) {\n\t\t\t\t\tString lineFromFile = scanner.nextLine();\n\n\t\t\t\t\tif (lineFromFile.contains(img)) { \n\t\t\t\t\t\tfilesWithImage.add(file);\n\t\t\t\t\t\tSystem.out.println(\"New image \" + img + \" found in file \" + file.getName());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (FileNotFoundException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn filesWithImage;\n\t}\n\n\t/**\n\t * Unzips the Zip file. This is used to unzip the generated distributable\n\t * Zip to extract already parsed (DXP overrides and token generations\n\t * applied) Markdown articles and images.\n\t *\n\t * @param  docDir the parent folder of where the Ant task was executed (e.g.,\n\t *         {@code tutorials})\n\t * @param  zipName the Zip file to unzip\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static void unzipFile(String docDir, String zipName)\n\t\t\tthrows IOException {\n\n\t\tbyte[] buffer = new byte[1024];\n\t\tint bytesRead = 0;\n\n\t\tFile zipFile = new File(\"../\" + docDir + \"/\" + zipName + \".zip\");\n\t\tFile destinationDir = new File(\"../\" + docDir + \"/\" + zipName);\n\t\tZipFile zip = new ZipFile(zipFile);\n\t\tEnumeration<? extends ZipEntry> zipEntries = zip.entries();\n\n\t\twhile (zipEntries.hasMoreElements()) {\n\t\t\tZipEntry entry = zipEntries.nextElement();\n\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tFile newDir = new File(destinationDir, entry.getName());\n\t\t\t\tnewDir.mkdirs();\n\t\t\t} else {\n\t\t\t\tBufferedInputStream inputStream = new BufferedInputStream(zip.getInputStream(entry));\n\t\t\t\tFile outputFile = new File(destinationDir, entry.getName());\n\t\t\t\tBufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outputFile));\n\n\t\t\t\twhile ((bytesRead = inputStream.read(buffer)) != -1) {\n\t\t\t\t\toutputStream.write(buffer, 0, bytesRead);\n\t\t\t\t}\n\n\t\t\t\tinputStream.close();\n\t\t\t\toutputStream.close();\n\t\t\t}\n\t\t}\n\t\tzip.close();\n\t\t//zipFile.delete();\n\t}\n\n\t/**\n\t * Writes deleted files to a {@code .txt} file. This is useful to notify the\n\t * publisher of the files that must be manually deleted from the Knowledge\n\t * Base portlet, since the import of a Zip only adds files or modifies\n\t * existing files, but cannot delete them.\n\t *\n\t * @param  deletedFiles the files that were deleted in the Git repo since\n\t *         the last publication\n\t * @param  distDir the folder where the Zip is generated (e.g., {@code dist})\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static void writeDeletedTextFile(List<String> deletedFiles, String distDir)\n\t\t\tthrows IOException {\n\n\t\tPrintWriter writer = new PrintWriter(distDir + \"/delete-files.txt\", \"UTF-8\");\n\t\twriter.println(\"DELETED:\\n\");\n\n\t\tfor (String file : deletedFiles) {\n\t\t\twriter.println(file);\n\t\t}\n\n\t\t//writer.println(\"\");\n\t\t//writer.println(\"\\nRENAMED:\\n\");\n\n\t\t//for (Map.Entry<String, String> entry : renamedFiles.entrySet()) {\n\t\t//\twriter.println(\"Old article to delete: \" + entry.getKey() + \" (renamed/moved to: \" + entry.getValue() + \")\");\n\t\t//}\n\n\t\twriter.close();\n\t}\n\n\tprivate static String zipPrePath;\n\n\tprivate String _distDir;\n\tprivate String _docDir;\n\tprivate String _docLocation;\n\tprivate String _dxpParam;\n\tprivate String _zipName;\n}"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/CheckLinksTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.net.HttpURLConnection;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\n\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\nimport org.htmlparser.Parser;\nimport org.htmlparser.filters.NodeClassFilter;\nimport org.htmlparser.tags.LinkTag;\nimport org.htmlparser.util.NodeList;\nimport org.htmlparser.util.ParserException;\n\n/**\n * @author Cody Hoag\n */\npublic class CheckLinksTask extends Task {\n\n\t@Override\n\tpublic void execute() throws BuildException {\n\n\t\tcheckApiLinks = _apiLinks;\n\t\tcheckDxpLinks = _dxpCheck;\n\t\tcheckLegacyLinks = _legacyLinks;\n\n\t\tString docDir = _docdir;\n\n\t\tappToken = _appToken;\n\t\tplatformToken = _platformToken;\n\n\t\tplatformReferenceSite = _platformReferenceSite;\n\t\tappReferenceSite = _appReferenceSite;\n\n\t\t// e.g., docDir = tutorials\n\t\tFile currentArticleDir = new File(\"../\" + docDir + \"/articles\");\n\t\tList<File> currentArticles = findCurrentDirArticles(currentArticleDir);\n\n\t\tif (checkDxpLinks) {\n\t\t\tcurrentArticles = addDxpOnlyArticles(currentArticles, docDir);\n\t\t}\n\n\t\tassignReferencedDirArticles(articleDirs);\n\n\t\ttry {\n\t\tuserGuideHeaders = findHeaders(userGuideArticles);\n\t\tdeploymentGuideHeaders = findHeaders(deploymentGuideArticles);\n\t\tdistributeGuideHeaders = findHeaders(distributeGuideArticles);\n\t\tappDevHeaders = findHeaders(appDevArticles);\n\t\tframeworksDevHeaders = findHeaders(frameworksDevArticles);\n\t\tcustomizationDevHeaders = findHeaders(customizationDevArticles);\n\t\ttutorialsHeaders = findHeaders(tutorialsArticles);\n\t\treferenceDevHeaders = findHeaders(referenceDevArticles);\n\n\t\tfor (File article : currentArticles) {\n\n\t\t\tLineNumberReader in = new LineNumberReader(new FileReader(article));\n\t\t\tString line = null;\n\n\t\t\twhile ((line = in.readLine()) != null) {\n\n\t\t\t\tif (line.contains(\"](/develop/\") || line.contains(\"](/discover/\")) {\n\t\t\t\t\tlogInvalidUrl(article, in.getLineNumber(), line, false);\n\t\t\t\t}\n\n\t\t\t\tif (line.contains(\"](/docs/\" + PORTAL_VERSION) ||\n\t\t\t\t\tline.contains(\"](/docs/\" + PORTAL_VERSION_LEGACY_1) ||\n\t\t\t\t\tline.contains(\"](/docs/\" + PORTAL_VERSION_LEGACY_2)) {\n\n\t\t\t\t\tint urlsInLine = countStrings(line);\n\n\t\t\t\t\tif (urlsInLine < 2) {\n\n\t\t\t\t\t\tString header = extractHeader(line, article, in);\n\n\t\t\t\t\t\tString primaryHeader = null;\n\t\t\t\t\t\tvalidUrl = true;\n\n\t\t\t\t\t\tif (header.contains(\"#\")) {\n\t\t\t\t\t\t\tString[] splitHeaders = header.split(\"#\");\n\n\t\t\t\t\t\t\tprimaryHeader = splitHeaders[0];\n\t\t\t\t\t\t\tString secondaryHeader = splitHeaders[1];\n\n\t\t\t\t\t\t\tvalidUrl = isUrlValid(line, article, in, primaryHeader, secondaryHeader, 0, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (header.equals(\"\")) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\n\t\t\t\t\t\t\tprimaryHeader = header;\n\t\t\t\t\t\t\tvalidUrl = isUrlValid(line, article, in, primaryHeader, null, 0, false);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!validUrl) {\n\t\t\t\t\t\t\tlogInvalidUrl(article, in.getLineNumber(), line, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\telse {\n\t\t\t\t\t\tcheckMultiLinks(article, line, in);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (line.contains(\"](#\")) {\n\n\t\t\t\t\tString sublinkStart = \"](#\";\n\t\t\t\t\tint subHeadersInLine = countStrings(line);\n\n\t\t\t\t\tif (subHeadersInLine < 2) {\n\t\t\t\t\t\tString secondaryHeader = extractSubHeader(line, article, in);\n\n\t\t\t\t\t\tvalidUrl = isSubUrlValid(article, secondaryHeader);\n\n\t\t\t\t\t\tif (!validUrl) {\n\t\t\t\t\t\t\tlogInvalidUrl(article, in.getLineNumber(), line, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tcheckMultiSubLinks(article, line, in, sublinkStart);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\tif (checkApiLinks && line.contains(\"/javadocs/\")\n\t\t\t\t\t\t&& line.contains(\"/com/liferay/\")) {\n\n\t\t\t\t\tvalidUrl = isApiUrlValid(article, in, line);\n\n\t\t\t\t\tif (!validUrl) {\n\t\t\t\t\t\tlogInvalidUrl(article, in.getLineNumber(), line, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tin.close();\n\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new BuildException(e.getLocalizedMessage());\n\t\t}\n\t\tif (resultsNumber > 0) {\n\t\t\tthrow new BuildException(\"\\n\\n**Total Broken Links: \" + resultsNumber + \"**\\n\");\n\t\t}\n\t\telse {\n\t\t\tSystem.out.println(\"\\nNo Broken Links!\");\n\t\t}\n\t}\n\n\tpublic void setApiLinks(boolean apiLinks) {\n\t\t_apiLinks = apiLinks;\n\t}\n\n\tpublic void setDocdir(String docdir) {\n\t\t_docdir = docdir;\n\t}\n\n\tpublic void setLegacyLinks(boolean legacyLinks) {\n\t\t_legacyLinks = legacyLinks;\n\t}\n\n\tpublic void setAppReferenceSite(String appReferenceSite) {\n\t\t_appReferenceSite = appReferenceSite;\n\t}\n\n\tpublic void setPlatformReferenceSite(String platformReferenceSite) {\n\t\t_platformReferenceSite = platformReferenceSite;\n\t}\n\n\tpublic void setAppToken(String appToken) {\n\t\t_appToken = appToken;\n\t}\n\n\tpublic void setPlatformToken(String platformToken) {\n\t\t_platformToken = platformToken;\n\t}\n\n\tpublic void setDxpCheck(boolean dxpCheck) {\n\t\t_dxpCheck = dxpCheck;\n\t}\n\n\t/**\n\t * Adds new DXP articles and applies DXP overrides to the article list.\n\t *\n\t * @param  articles the CE articles\n\t * @param  path the partial folder path with the DXP articles to acquire\n\t * @param  currentDir whether the DXP articles are being returned for the\n\t *         folder the command was executed from\n\t * @return the new list of articles containing the new DXP articles and DXP\n\t *         overrides\n\t */\n\tprivate static List<File> addDxpOnlyArticles(List<File> articles, String path) {\n\n\t\tList<File> dxpArticles = getDxpArticles(path);\n\n\t\tarticles = includeDxpOverrides(articles, dxpArticles);\n\n\t\tList<File> dxpArticlesToDelete = new ArrayList<File>();\n\n\t\t// DXP articles that override existing CE articles are removed from DXP list so\n\t\t// they're not added as new articles\n\t\tfor (File dxpArticle : dxpArticles) {\n\t\t\tif (fileOverrides.contains(dxpArticle)) {\n\t\t\t\tdxpArticlesToDelete.add(dxpArticle);\n\t\t\t}\n\t\t}\n\n\t\tfor (File dxpArticleToDelete : dxpArticlesToDelete) {\n\t\t\tdxpArticles.remove(dxpArticleToDelete);\n\t\t}\n\n\t\tfor (File dxpArticle : dxpArticles) {\n\t\t\tarticles.add(dxpArticle);\n\t\t}\n\n\t\treturn articles;\n\t}\n\n\t/**\n\t * Assembles the header ID for the given heading. This is used for secondary\n\t * headers since their header IDs do not exist in the Markdown article.\n\t *\n\t * @param  heading the heading to assemble an ID for\n\t * @param  idCount the number of matching header IDs found. This is added to\n\t *         the end of a header ID to differentiate it from other matching\n\t *         headers (e.g., liferay-home-2).\n\t * @return the header ID for the given heading\n\t */\n\tprivate static String assembleId(String heading, int idCount) {\n\n\t\tString count = \"\";\n\t\tif (idCount > 0) {\n\t\t\tcount = \"-\" + idCount;\n\t\t}\n\n\t\tStringBuffer sb = new StringBuffer(heading);\n\t\tsb.append(count);\n\n\t\tString finalHeaderId = sb.toString();\n\n\t\tif (finalHeaderId.contains(\"--\")) {\n\t\t\tfinalHeaderId = finalHeaderId.replaceAll(\"--\", \"-\");\n\t\t}\n\n\t\treturn finalHeaderId;\n\t}\n\n\t/**\n\t * Returns all the possible referenced headers. This method scans the\n\t * current line and assigns it to the appropriate header list.\n\t *\n\t * @param  line the line containing a relative URL\n\t * @param  lineIndex the header's index on the line. This is useful when\n\t *         there are multiple relative links on one line.\n\t * @return the referenced headers\n\t */\n\tprivate static ArrayList<List<String>> assignDirHeaders(String line, int lineIndex) {\n\n\t\tArrayList<List<String>> headers = new ArrayList<List<String>>();\n\n\t\tString lineSubstring = line.substring(lineIndex, line.length());\t\t\n\n\t\t// The first link in the substring will have its headers applied.\n\t\tif (lineSubstring.contains(userGuideLinkFolder + findStr)) {\n\t\t\theaders = userGuideHeaders;\n\t\t}\n\n\t\telse if (lineSubstring.contains(deploymentGuideLinkFolder + findStr)) {\n\t\t\theaders = deploymentGuideHeaders;\n\t\t}\n\n\t\telse if (lineSubstring.contains(distributeGuideLinkFolder + findStr)) {\n\t\t\theaders = distributeGuideHeaders;\n\t\t}\n\n\t\telse if (lineSubstring.contains(appDevLinkFolder + findStr)) {\n\t\t\theaders = appDevHeaders;\n\t\t}\n\n\t\telse if (lineSubstring.contains(customizationDevLinkFolder + findStr)) {\n\t\t\theaders = customizationDevHeaders;\n\t\t}\n\n\t\telse if (lineSubstring.contains(frameworksDevLinkFolder + findStr)) {\n\t\t\theaders = frameworksDevHeaders;\n\t\t}\n\n\t\telse if (lineSubstring.contains(tutorialsDevLinkFolder + findStr)) {\n\t\t\theaders = tutorialsHeaders;\n\t\t}\n\n\t\telse if (lineSubstring.contains(referenceDevLinkFolder + findStr)) {\n\t\t\theaders = referenceDevHeaders;\n\t\t}\n\n\t\treturn headers;\n\t}\n\n\t/**\n\t * Finds the articles from other possible referenced directories and sets\n\t * them to the appropriate local variables.\n\t * \n\t * @param dirs the popular directories residing in the\n\t *        <code>liferay-docs</code> repository\n\t */\n\tprivate static void assignReferencedDirArticles(String[] dirs) {\n\n\t\tfor (String articleDir : articleDirs) {\n\n\t\t\tif (articleDir.equals(userGuideDir)) {\n\t\t\t\tuserGuideArticles = findArticles(userGuideDir);\n\t\t\t}\n\n\t\t\tif (articleDir.equals(deploymentGuideDir)) {\n\t\t\t\tdeploymentGuideArticles = findArticles(deploymentGuideDir);\n\t\t\t}\n\n\t\t\tif (articleDir.equals(distributeGuideDir)) {\n\t\t\t\tdistributeGuideArticles = findArticles(distributeGuideDir);\n\t\t\t}\n\n\t\t\tif (articleDir.equals(appDevDir)) {\n\t\t\t\tappDevArticles = findArticles(appDevDir);\n\t\t\t}\n\n\t\t\tif (articleDir.equals(customizationDevDir)) {\n\t\t\t\tcustomizationDevArticles = findArticles(customizationDevDir);\n\t\t\t}\n\n\t\t\tif (articleDir.equals(frameworksDevDir)) {\n\t\t\t\tframeworksDevArticles = findArticles(frameworksDevDir);\n\t\t\t}\n\n\t\t\tif (articleDir.equals(tutorialsDir)) {\n\t\t\t\ttutorialsArticles = findArticles(tutorialsDir);\n\t\t\t}\n\n\t\t\tif (articleDir.equals(referenceDevDir)) {\n\t\t\t\treferenceDevArticles = findArticles(referenceDevDir);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks the line that contains multiple relative links.\n\t *\n\t * @param  article the article containing the line\n\t * @param  line the line containing multiple relative links\n\t * @param  in the line number reader\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static void checkMultiLinks(File article, String line, LineNumberReader in)\n\t\t\tthrows IOException {\n\n\t\t// Extract headers into map with <header, index> pairs\n\t\tLinkedHashMap<String, Integer> headerMaps = extractMultiStrings(line, article, in, findStr, 2);\n\n\t\tIterator<?> it = headerMaps.entrySet().iterator();\n\n\t\t// Iterating through header maps, which contain header and index information\n\t\t// used for validating lines with multiple links\n\t    while (it.hasNext()) {\n\n\t    \t@SuppressWarnings(\"rawtypes\")\n\t\t\tMap.Entry pair = (Map.Entry)it.next();\n\t        it.remove();\n\n\t\t\tString header = pair.getKey().toString();\n\t\t\tString headerValue = pair.getValue().toString();\n\t\t\tint headerIndex = Integer.parseInt(headerValue);\n\n\t\t\t// Find version for each header so we can accurately check them\n\t\t\tString substringLineStart = line.substring(headerIndex);\n\t\t\tint headerStart = substringLineStart.indexOf(findStr) + findStr.length();\n\t\t\tString version = substringLineStart.substring(headerStart, headerStart + 3);\n\t\t\tboolean differingDefaultVersion = false;\n\n\t\t\tif (!version.equals(PORTAL_VERSION)) {\n\t\t\t\tdifferingDefaultVersion = true;\n\t\t\t}\n\n\t\t\t// end of >1 logic\n\n\t\t\tString primaryHeader = null;\n\t\t\tvalidUrl = true;\n\n\t\t\tif (header.contains(\"#\")) {\n\t\t\t\tString[] splitHeaders = header.split(\"#\");\n\n\t\t\t\tprimaryHeader = splitHeaders[0];\n\t\t\t\tString secondaryHeader = splitHeaders[1];\n\n\t\t\t\tvalidUrl = isUrlValid(line, article, in, primaryHeader, secondaryHeader, headerIndex, differingDefaultVersion);\n\t\t\t}\n\t\t\telse if (header.equals(\"\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse {\n\n\t\t\t\tprimaryHeader = header;\n\t\t\t\tvalidUrl = isUrlValid(line, article, in, primaryHeader, null, headerIndex, differingDefaultVersion);\n\t\t\t}\n\n\t\t\tif (!validUrl) {\n\t\t\t\tlogInvalidUrl(article, in.getLineNumber(), line, false);\n\t\t\t\tSystem.out.println(\"Invalid Header: \" + header + \"\\n\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Checks the line that contains multiple subheader relative links.\n\t *\n\t * @param  article the article containing the line\n\t * @param  line the line containing multiple subheader relative links\n\t * @param  in the line number reader\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static void checkMultiSubLinks(File article, String line, LineNumberReader in,\n\t\t\tString sublinkStart) throws IOException {\n\n\t\tLinkedHashMap<String, Integer> headerMaps = extractMultiStrings(line, article, in, sublinkStart, 0);\n\n\t\tIterator<?> it = headerMaps.entrySet().iterator();\n\n\t\t// Iterating through header maps, which contain header and index information\n\t\t// used for validating lines with multiple links\n\t    while (it.hasNext()) {\n\n\t    \t@SuppressWarnings(\"rawtypes\")\n\t\t\tMap.Entry pair = (Map.Entry)it.next();\n\t        it.remove();\n\n\t\t\tString secondaryHeader = pair.getKey().toString();\n\n\t\t\tvalidUrl = isSubUrlValid(article, secondaryHeader);\n\n\t\t\tif (!validUrl) {\n\t\t\t\tlogInvalidUrl(article, in.getLineNumber(), line, false);\n\t\t\t\tSystem.out.println(\"Invalid Subheader: #\" + secondaryHeader + \"\\n\");\n\t\t\t}\n\t    }\n\t}\n\n\t/**\n\t * Returns the number of specific strings in the line.\n\t *\n\t * @param  line the line to count the number of specific strings\n\t * @return the number of specific strings in the line\n\t */\n\tprivate static int countStrings(String line) {\n\n\t\tint lastIndex = 0;\n\t\tint count = 0;\n\n\t\twhile(lastIndex != -1){\n\n\t\t    lastIndex = line.indexOf(findStr,lastIndex);\n\n\t\t    if(lastIndex != -1){\n\t\t        count ++;\n\t\t        lastIndex += findStr.length();\n\t\t    }\n\t\t}\n\t\treturn count;\n\t}\n\n\t/**\n\t * Returns <code>true</code> if the link's folder and folder starting letter\n\t * match. For example, the link below returns <code>true</code> because the\n\t * <code>reference</code> string matches the second folder prefix\n\t * <code>r</code>.\n\t * \n\t * <p>\n\t * <pre>\n\t * <code>\n\t * /docs/7-2/reference/-/knowledge_base/r/test\n\t * </code>\n\t * </pre>\n\t * </p>\n\t *\n\t * <p>\n\t * If the second folder prefix for the above link was <code>f</code>, this\n\t * method would return <code>false</code>.\n\t * </p>\n\t *\n\t * @param  line the line containing the relative link\n\t * @param  lineIndex the header's index on the line. This is useful when\n\t *         there are multiple relative links on one line.\n\t * @return <code>true</code> if the link's folder and folder starting letter\n\t *         match; <code>false</code> otherwise\n\t */\n\tprivate static boolean doesDocFoldersMatch(String line, int lineIndex) {\n\t\t\n\t\tString lineSubstring = line.substring(lineIndex, line.length());\n\t\tboolean foldersMatch = false;\n\t\t\n\t\tint begIndex = lineSubstring.indexOf(\"/docs/\") + 10;\n\t\t//int endIndex = lineSubstring.indexOf(\"/\", begIndex);\n\t\tString folder1 = lineSubstring.substring(begIndex, begIndex + 1);\n\t\t\n\t\tint begIndex2 = lineSubstring.indexOf(findStr) + findStr.length();\n\t\t\n\t\tString folder2 = lineSubstring.substring(begIndex2, begIndex2 + 1);\n\t\t\n\t\tif (folder1.equals(folder2)) {\n\t\t\tfoldersMatch = true;\n\t\t}\n\n\t\treturn foldersMatch;\n\t}\n\n\t/**\n\t * Returns the header ID contained in the given line. For example, the\n\t * following line:\n\t * \n\t * <p>\n\t * <pre>\n\t * <code>\n\t * [here](/docs/7-2/deploy/-/knowledge_base/d/installing-liferay-portal#liferay-home)\n\t * </code>\n\t * </pre>\n\t * </p>\n\t *\n\t * returns\n\t *\n\t * <p>\n\t * <pre>\n\t * <code>\n\t * installing-liferay-portal#liferay-home\n\t * </code>\n\t * </pre>\n\t * </p>\n\t *\n\t * @param  line the line from which to extract the header\n\t * @param  article the article containing the line\n\t * @param  in the line number reader\n\t * @return the header ID\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static String extractHeader(String line, File article, LineNumberReader in)\n\t\t\tthrows IOException {\n\n\t\tint strIndex = line.indexOf(findStr);\n\t\tint begIndex = strIndex + findStr.length() + 2;\n\t\tint endIndex = line.indexOf(\")\", begIndex);\n\n\t\tString header = \"\";\n\n\t\ttry {\n\t\t\theader = line.substring(begIndex, endIndex);\n\t\t} catch(Exception e) {\n\t\t\tlogInvalidUrl(article, in.getLineNumber(), line, true);\n\t\t}\n\n\t\treturn header;\n\t}\n\n\t/**\n\t * Returns the article's heading found on the line.\n\t *\n\t * @param  line the line to extract the heading from\n\t * @return the article's heading\n\t */\n\tprivate static String extractHeading(String line) {\n\n\t\tint indexOfFirstHeaderChar = line.indexOf(\"# \") + 2;\n\n\t\tString heading = line.substring(indexOfFirstHeaderChar);\n\t\theading = heading.trim();\n\n\t\t// Replace each spaced dash, space, dot, and slash with a dash\n\n\t\theading = heading.replace(\" - \", \"-\");\n\t\theading = heading.replace(' ', '-');\n\t\theading = heading.replace('_', '-');\n\t\theading = heading.toLowerCase();\n\n\t\t// Filter out characters other than dashes, letters, and digits\n\n\t\tStringBuffer headingSb = new StringBuffer();\n\t\tfor (int i = 0; i < heading.length(); i++) {\n\t\t\tchar ch = heading.charAt(i);\n\n\t\t\tif (ch == '-' || Character.isLetterOrDigit(ch)) {\n\t\t\t\theadingSb.append(ch);\n\t\t\t}\n\t\t}\n\t\theading = headingSb.toString();\n\t\treturn heading;\n\t}\n\n\t/**\n\t * Returns a map of headers paired with their line indexes. This method is\n\t * used to extract multiple headers (from relative links) that are contained\n\t * on one line.\n\t *\n\t * @param  line the line from which to extract the header\n\t * @param  article the article containing the line\n\t * @param  in the line number reader\n\t * @param  indexCorrection the number used to modify the index used when\n\t *         searching for the next specific string on the line\n\t * @return the multiple headers contained on the line paired with their indexes\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static LinkedHashMap<String, Integer> extractMultiStrings(String line, File article, LineNumberReader in,\n\t\t\tString searcherString, int indexCorrection) throws IOException {\n\n\t\t// Find all relevant headers\n\t\tLinkedHashMap<String, Integer> headerMap = new LinkedHashMap<String, Integer>();\n\t\tString originalLine = line;\n\n\t\twhile(line.contains(searcherString)){\n\n\t\t\tint strIndex = line.indexOf(searcherString);\n\t\t\tint begIndex = strIndex + searcherString.length() + indexCorrection;\n\t\t\tint endIndex = line.indexOf(\")\", begIndex);\n\t\t\tint headerIndex = originalLine.length() - line.length();\n\n\t\t\tString header = \"\";\n\n\t\t\ttry {\n\t\t\t\theader = line.substring(begIndex, endIndex);\n\t\t\t} catch(Exception e) {\n\t\t\t\tlogInvalidUrl(article, in.getLineNumber(), line, true);\n\t\t\t}\n\n\t\t\tline = line.substring(endIndex, line.length());\n\n\t\t\theaderMap.put(header, headerIndex);\n\n\t\t}\n\n\t\treturn headerMap;\n\t}\n\n\t/**\n\t * Returns the fully qualified LDN URL from the given line.\n\t *\n\t * @param  line the line from which to extract the URL\n\t * @param  lineNumber the line number\n\t * @param  fileName the article's name\n\t * @return the LDN URL\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static String extractLdnUrl(String line, int lineNumber, File article)\n\t\t\tthrows IOException {\n\n\t\tint begIndex = line.indexOf(\"](/\") + 2;\n\t\tint endIndex = line.indexOf(\")\", begIndex);\n\t\tString endLdnUrl = null;\n\n\t\ttry{\n\t\t\tendLdnUrl = line.substring(begIndex, endIndex);\n\t\t} catch (StringIndexOutOfBoundsException e) {\n\t\t\tendLdnUrl = line.substring(begIndex, line.length());\n\t\t\tlogInvalidUrl(article, lineNumber, line, true);\n\t\t}\n\n\t\tldnArticle = endLdnUrl;\n\n\t\tString begLdnUrl = \"https://dev.liferay.com\";\n\n\t\tString ldnUrl = begLdnUrl.concat(endLdnUrl);\n\n\t\treturn ldnUrl;\n\t}\n\n\t/**\n\t * Returns the sub-header ID contained in the given line. A sub-header is a\n\t * header characterizing a subsection in the article.\n\t *\n\t * @param  line the line from which to extract the URL\n\t * @param  article the article containing the line\n\t * @param  in the line number reader\n\t * @return the sub-header ID\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static String extractSubHeader(String line, File article, LineNumberReader in)\n\t\t\tthrows IOException {\n\n\t\tint begIndex = line.indexOf(\"](#\") + 3;\n\t\tint endIndex = line.indexOf(\")\", begIndex);\n\n\t\tString header = \"\";\n\n\t\ttry {\n\t\t\theader = line.substring(begIndex, endIndex);\n\t\t} catch(Exception e) {\n\t\t\tlogInvalidUrl(article, in.getLineNumber(), line, true);\n\t\t}\n\n\t\treturn header;\n\t}\n\n\t/**\n\t * Returns the article characterized by the primary header.\n\t *\n\t * @param  articles the articles to search for the primary header\n\t * @param  primaryHeader the header to search the articles for\n\t * @return the article characterized by the primary header\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static File findArticleMatchingLink(List<File> articles, String primaryHeader)\n\t\t\tthrows IOException {\n\n\t\tFile matchingArticle = null;\n\n\t\tfor (File article: articles) {\n\n\t\t\tLineNumberReader in = new LineNumberReader(new FileReader(article));\n\t\t\tString line = null;\n\n\t\t\twhile ((line = in.readLine()) != null) {\n\n\t\t\t\tif (line.startsWith(headerSyntax + primaryHeader)) {\n\t\t\t\t\tmatchingArticle = article;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tin.close();\n\n\t\t\tif (matchingArticle != null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn matchingArticle;\t\n\t}\n\n\t/**\n\t * Returns the Markdown articles contained in the given path.\n\t *\n\t * @param  path the partial path for the articles (e.g.,\n\t *         <code>developer/tutorials</code>\n\t * @return the Markdown articles\n\t */\n\tprivate static List<File> findArticles(String path) {\n\n\t\tFile dir = new File(\"../\" + path + \"/articles\");\n\n\t\t// Some 'articles' folders are nested in one folder while others are\n\t\t// nested two folders deep. This if statement assigns those articles\n\t\t// that are nested two folders deep.\n\t\tif(!dir.exists()) {\n\t\t\tdir = new File(\"../../\" + path + \"/articles\");\n\t\t}\n\n\t\tList<File> articles = new ArrayList<File>();\n\n\t\tif(!dir.exists()) {\n\t\t\tSystem.out.println(\"Warning: Folder \" + dir.getPath() + \" does not exist. Update\"\n\t\t\t\t\t+ \" check-links folder paths.\");\n\n\t\t\treturn articles;\n\t\t}\n\n\t\tFile[] dirFiles = dir.listFiles();\n\n\t\tQueue<File> q = new LinkedList<File>();\n\t\tfor (File f : dirFiles) {\n\t\t\tq.add(f);\n\t\t}\n\n\t\twhile (!q.isEmpty()) {\n\t\t\tFile f = q.remove(); \n\n\t\t\tif (f.isDirectory()) {\n\t\t\t\tFile[] files = f.listFiles();\n\n\t\t\t\tfor (File file : files) {\n\t\t\t\t\tq.add(file);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (f.getName().endsWith(\".markdown\")) {\n\t\t\t\t\tarticles.add(f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (checkDxpLinks) {\n\t\t\tarticles = addDxpOnlyArticles(articles, path);\n\t\t}\n\n\t\treturn articles;\n\t}\n\n\t/**\n\t * Returns the Markdown articles located in the current directory.\n\t *\n\t * @param  articleDir the current directory from which the Ant task was\n\t *         executed\n\t * @return the current directory's Markdown articles\n\t */\n\tprivate static List<File> findCurrentDirArticles(File articleDir) {\n\n\t\tFile[] articleDirFiles = articleDir.listFiles();\n\t\tList<File> articles = new ArrayList<File>();\n\n\t\tQueue<File> q = new LinkedList<File>();\n\t\tfor (File f : articleDirFiles) {\n\t\t\tq.add(f);\n\t\t}\n\n\t\twhile (!q.isEmpty()) {\n\t\t\tFile f = q.remove(); \n\n\t\t\tif (f.isDirectory()) {\n\t\t\t\tFile[] files = f.listFiles();\n\n\t\t\t\tfor (File file : files) {\n\t\t\t\t\tq.add(file);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (f.getName().endsWith(\".markdown\")) {\n\t\t\t\t\tarticles.add(f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn articles;\n\t}\n\n\t/**\n\t * Returns the headers (primary and secondary) of the given articles. The\n\t * headers are returned as an array list: primary headers contained in index\n\t * <code>0</code> and secondary headers contained in index <code>1</code>.\n\t *\n\t * @param  articles the articles for which to find headers\n\t * @return the primary and secondary headers\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static ArrayList<List<String>> findHeaders(List<File> articles) throws IOException {\n\n\t\tList<String> primaryHeaders = new ArrayList<String>();\n\t\tList<String> secondaryHeaders = new ArrayList<String>();\n\n\t\tfor (File article : articles) {\n\n\t\t\tLineNumberReader in = new LineNumberReader(new FileReader(article));\n\t\t\tString line = null;\n\t\t\tString header;\n\n\t\t\twhile ((line = in.readLine()) != null) {\n\n\t\t\t\tif (line.startsWith(headerSyntax)) {\n\n\t\t\t\t\tint begIndex = headerSyntax.length();\n\t\t\t\t\tint endIndex = line.length();\n\n\t\t\t\t\theader = line.substring(begIndex, endIndex);\n\n\t\t\t\t\tprimaryHeaders.add(header);\n\t\t\t\t}\n\t\t\t\telse if (line.startsWith(\"## \") || line.startsWith(\"### \") ||\n\t\t\t\t\t\tline.startsWith(\"#### \") || line.startsWith(\"##### \")) {\n\n\t\t\t\t\tString headerKeyValue = generateVirtualSecondaryHeader(line, article.getCanonicalPath());\n\t\t\t\t\tsecondaryHeaders.add(headerKeyValue);\n\t\t\t\t}\n\t\t\t\telse if (line.contains(\"<a name=\" + quotation)) {\n\n\t\t\t\t\tint begIndex = line.indexOf(\"<a name=\");\n\t\t\t\t\tint quotStart = line.indexOf(quotation, begIndex);\n\t\t\t\t\tint quotClose = line.indexOf(quotation, quotStart + 1);\n\n\t\t\t\t\tString htmlHeaderAnchor = line.substring(quotStart + 1, quotClose);\n\n\t\t\t\t\tsecondaryHeaders.add(htmlHeaderAnchor + article.getCanonicalPath());\n\t\t\t\t}\n\t\t\t}\n\t\t\tin.close();\n\t\t}\n\n\t\tArrayList<List<String>> headers = new ArrayList<List<String>>();\n\t\theaders.add(primaryHeaders);\n\t\theaders.add(secondaryHeaders);\n\n\t\treturn headers;\n\t}\n\n\t/**\n\t * Returns the generated virtual secondary header ID for the line's\n\t * sub-header.\n\t *\n\t * @param  line the line containing the sub-header\n\t * @param  filename the article name\n\t * @return the generated virtual secondary header ID\n\t */\n\tprivate static String generateVirtualSecondaryHeader(String line, String filename) {\n\n\t\tString heading = extractHeading(line);\n\n\t\tint idCount = 0;\n\t\tString generatedSecondaryHeader = null;\n\t\twhile (true) {\n\n\t\t\tgeneratedSecondaryHeader = assembleId(heading, idCount);\n\n\t\t\t\tif (!secondaryKeyValues.contains(generatedSecondaryHeader + filename)) {\n\n\t\t\t\t\t// Heading is unique for the document set. Map the ID to\n\t\t\t\t\t// the filename.\n\n\t\t\t\t\t//Easiest way to check/track unique key-value pairs\n\t\t\t\t\tsecondaryKeyValues.add(generatedSecondaryHeader + filename);\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\tidCount++;\n\t\t}\n\n\t\t return generatedSecondaryHeader + filename;\n\t}\n\n\t/**\n\t * Returns the DXP articles contained in the folder.\n\t *\n\t * @param  path the partial folder path with the DXP articles to acquire\n\t * @param  currentDir whether the DXP articles are being returned for the\n\t *         folder the command was executed from\n\t * @return the DXP articles contained in the folder\n\t */\n\tprivate static List<File> getDxpArticles(String path) {\n\n\t\tList<File> dxpArticles = new ArrayList<File>();\n\t\tFile dxpArticleDir = new File(\"\");\n\n\t\t// Ensure \"articles-dxp\" folder exists\n\t\tdxpArticleDir = new File(\"../\" + path + \"/articles-dxp\");\n\n\t\t// Some 'articles' folders are nested in one folder while others are\n\t\t// nested two folders deep. This if statement assigns those articles\n\t\t// that are nested two folders deep.\n\t\tif(!dxpArticleDir.exists()) {\n\t\t\tdxpArticleDir = new File(\"../../\" + path + \"/articles-dxp\");\n\t\t}\n\n\t\ttry {\n\t\t\tdxpArticles = findCurrentDirArticles(dxpArticleDir);\n\t\t} catch(NullPointerException e) {\n\t\t\tSystem.out.println(\"No DXP articles in \" + dxpArticleDir.getParent());\n\t\t}\n\n\t\treturn dxpArticles;\n\t}\n\n\t/**\n\t * Returns the folder represented by the link's directory header in the line\n\t * (e.g., <code>developer/frameworks</code>).\n\t *\n\t * @param  line the line containing the relative link\n\t * @param  lineIndex the header's index on the line. This is useful when\n\t *         there are multiple relative links on one line.\n\t * @return the folder represented by the link's directory header\n\t */\n\tprivate static String getHeaderDir(String line, int lineIndex) {\n\n\t\tString lineSubstring = line.substring(lineIndex, line.length());\n\t\tString path = \"\";\n\n\t\t// The first link in the substring will have its headers applied.\n\t\tif (lineSubstring.contains(userGuideLinkFolder + findStr)) {\n\t\t\tpath = userGuideDir;\n\t\t}\n\n\t\telse if (lineSubstring.contains(deploymentGuideLinkFolder + findStr)) {\n\t\t\tpath = deploymentGuideDir;\n\t\t}\n\n\t\telse if (lineSubstring.contains(distributeGuideLinkFolder + findStr)) {\n\t\t\tpath = distributeGuideDir;\n\t\t}\n\n\t\telse if (lineSubstring.contains(appDevLinkFolder + findStr)) {\n\t\t\tpath = appDevDir;\n\t\t}\n\n\t\telse if (lineSubstring.contains(customizationDevLinkFolder + findStr)) {\n\t\t\tpath = customizationDevDir;\n\t\t}\n\n\t\telse if (lineSubstring.contains(frameworksDevLinkFolder + findStr)) {\n\t\t\tpath = frameworksDevDir;\n\t\t}\n\n\t\telse if (lineSubstring.contains(tutorialsDevLinkFolder + findStr)) {\n\t\t\tpath = tutorialsDir;\n\t\t}\n\n\t\telse if (lineSubstring.contains(referenceDevLinkFolder + findStr)) {\n\t\t\tpath = referenceDevDir;\n\t\t}\n\n\t\treturn path;\n\t}\n\n\t/**\n\t * Overrides the CE articles with their DXP article counterparts.\n\t *\n\t * @param  articles the CE articles\n\t * @param  dxpArticles the DXP articles\n\t * @return the new list of articles containing the DXP article overrides\n\t */\n\tprivate static List<File> includeDxpOverrides(List<File> articles, List<File> dxpArticles) {\n\n\t\tif (dxpArticles != null) {\n\n\t\t\tList<String> currentArticlePathStrings = new ArrayList<String>();\n\t\t\tList<String> currentDxpArticlePathStrings = new ArrayList<String>();\n\n\t\t\t// Find DXP-only articles and convert paths to strings\n\t\t\tfor (File articleDxp : dxpArticles) {\n\n\t\t\t\tString articleDxpPathString = articleDxp.getPath();\n\t\t\t\tarticleDxpPathString = articleDxpPathString.replace(File.separator + \"articles-dxp\" + File.separator, File.separator + \"articles\" + File.separator);\n\n\t\t\t\tcurrentDxpArticlePathStrings.add(articleDxpPathString);\n\t\t\t}\n\n\t\t\t// Convert regular article paths to strings\n\t\t\tfor (File article : articles) {\n\n\t\t\t\tString articlePathString = article.getPath();\n\n\t\t\t\tcurrentArticlePathStrings.add(articlePathString);\n\t\t\t}\n\n\t\t\tList<Integer> articleIndexes = new ArrayList<Integer>();\n\n\t\t\t// Find article indexes that should be replaced with DXP override\n\t\t\tfor (String articleDxpPath : currentDxpArticlePathStrings) {\n\t\t\t\tint count = 0;\n\t\t\t\tfor (String articlePath : currentArticlePathStrings) {\n\n\t\t\t\t\tif (articlePath.equals(articleDxpPath)) {\n\t\t\t\t\t\tarticleIndexes.add(count);\n\t\t\t\t\t}\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Apply DXP overrides to current article list\n\t\t\tfor (int i = 0; i < articleIndexes.size(); i++) {\n\t\t\t\tFile fileOverride =articles.get(articleIndexes.get(i));\n\t\t\t\tString fileOverrideString = fileOverride.getPath();\n\n\t\t\t\tfileOverrideString = fileOverrideString.replace(File.separator + \"articles\" + File.separator, File.separator + \"articles-dxp\" + File.separator);\n\t\t\t\tfileOverride = new File(fileOverrideString);\n\n\t\t\t\tfileOverrides.add(fileOverride);\n\n\t\t\t\tarticles.set(articleIndexes.get(i), fileOverride);\n\t\t\t}\n\t\t}\n\n\t\treturn articles;\n\t}\n\n\t/**\n\t * Returns <code>true</code> if the API URL is valid. This method is used to\n\t * check URLs hosted on docs.liferay.com.\n\t *\n\t * @param  article the article containing the API URL\n\t * @param  in the line number reader\n\t * @param  line the line containing the API URL\n\t * @return <code>true</code> if the API URL is valid; <code>false</code>\n\t *         otherwise\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static boolean isApiUrlValid(File article, LineNumberReader in, String line)\n\t\t\tthrows IOException {\n\n\t\tboolean validAPIURL = true;\n\n\t\tint begIndex = line.indexOf(\"](\") + 2;\n\t\tint endIndex = line.indexOf(\")\", begIndex);\n\n\t\tString urlString = line.substring(begIndex, endIndex);\n\n\t\turlString = urlString.replace(\"@\" + platformToken + \"@\", platformReferenceSite);\n\t\turlString = urlString.replace(\"@\" + appToken + \"@\", appReferenceSite);\n\n\t\tURL url = null;\n\n\t\ttry {\n\t\t\turl = new URL(urlString);\n\t\t} catch (MalformedURLException e) {\n\t\t\t// ignore this because docs.liferay.com creates many URLs that work\n\t\t\t// but throw the MalformedURLException\n\t\t}\n\n\t\ttry {\n\t\t\tHttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();\n\t\t\turlConnection.setRequestMethod(\"GET\");\n\t\t\turlConnection.connect() ; \n\t\t\tint code = urlConnection.getResponseCode();\n\n\t\t\tif (code == 404) {\n\t\t\t\tvalidAPIURL = false;\n\t\t\t}\n\t\t} catch (NullPointerException e) {\n\t\t\tlogInvalidUrl(article, in.getLineNumber(), line, true);\n\t\t}\n\n\t\treturn validAPIURL;\n\t}\n\n\t/**\n\t * Returns <code>true</code> if the LDN URL is valid. This method is used to\n\t * check legacy URLs hosted on LDN.\n\t *\n\t * @param  url the URL to check\n\t * @param  fileName the article's name\n\t * @param  lineNumber the line number\n\t * @return <code>true</code> if the LDN URL is valid; <code>false</code>\n\t *         otherwise\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static boolean isLdnUrlValid(String url, File article, int lineNumber)\n\t\t\tthrows IOException {\n\n\t\tNodeList list = new NodeList();\n\t\tboolean validLDNURL = false;\n\n\t\ttry {\n\t\t\tParser htmlParser = new Parser(url);\n\t\t\tlist = htmlParser.extractAllNodesThatMatch(new NodeClassFilter(LinkTag.class));\n\t\t} catch (ParserException e) {\n\t\t\tlogInvalidUrl(article, lineNumber, ldnArticle, false);\n\t\t}\n\n\t\tList<String> results = new LinkedList<String>();\n\n\t\tfor (int i = 0; i < list.size(); i++) {\n\n\t\t\tLinkTag link = (LinkTag) list.elementAt(i);\n\t\t\tString linkString = link.getLink();\n\t\t\tresults.add(linkString);\n\t\t}\n\n\t\tfor (String x : results) {\n\t\t\tif (x.contains(\"2Fsearch&#x25;2Fsearch&#x26;_3_redirect&#x3d;\")) {\n\t\t\t\tlogInvalidUrl(article, lineNumber, ldnArticle, false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tvalidLDNURL = true;\n\t\t\t}\n\t\t}\n\n\t\treturn validLDNURL;\n\t}\n\n\t/**\n\t * Returns <code>true</code> if the sub-URL is valid. A sub-URL is a link\n\t * to a section existing in the same article.\n\t *\n\t * @param  article the article containing the sub-URL\n\t * @param  secondaryHeader the header ID for the section that is linked\n\t * @return <code>true</code> if the sub-URL is valid; <code>false</code>\n\t *         otherwise\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static boolean isSubUrlValid(File article, String secondaryHeader)\n\t\t\tthrows IOException {\n\n\t\tboolean validUrl = false;\n\t\tLineNumberReader in = new LineNumberReader(new FileReader(article));\n\t\tString line = null;\n\n\t\tif (secondaryKeyValues.contains(secondaryHeader + article.getCanonicalPath())) {\n\t\t\tvalidUrl = true;\n\t\t}\n\n\t\twhile ((line = in.readLine()) != null) {\n\n\t\t\tif (line.contains(\"<a name=\" + quotation + secondaryHeader + quotation + \">\")) {\n\t\t\t\tvalidUrl = true;\n\t\t\t}\n\t\t\telse if (line.contains(\"<div\") && line.contains(\"id=\" + quotation + secondaryHeader + quotation + \">\")) {\n\t\t\t\tvalidUrl = true;\n\t\t\t}\n\t\t}\n\n\t\tin.close();\n\n\t\treturn validUrl;\n\t}\n\n\t/**\n\t * Returns <code>true</code> if the URL is valid. This method is used to\n\t * check the current version of documentation by matching URLs with their\n\t * header IDs contained in the local repo.\n\t *\n\t * @param  line the line containing the URL\n\t * @param  article the article containing the URL\n\t * @param  in the line number reader\n\t * @param  primaryHeader the primary header ID\n\t * @param  secondaryHeader the secondary header ID\n\t * @param  lineIndex the header's index on the line. This is useful when\n\t *         there are multiple relative links on one line.\n\t * @param  differingDefaultVersion whether the link version differs from\n\t *         <code>PORTAL_VERSION</code>\n\t * @return <code>true</code> if the URL is valid; <code>false</code>\n\t *         otherwise\n\t * @throws IOException if an IO exception occurred\n\t */\n\tprivate static boolean isUrlValid(String line, File article, LineNumberReader in,\n\t\t\tString primaryHeader, String secondaryHeader, int lineIndex,\n\t\t\tboolean differingDefaultVersion) throws IOException {\n\n\t\tboolean validURL = false;\n\t\tArrayList<List<String>> headers = new ArrayList<List<String>>();\n\n\t\theaders = assignDirHeaders(line, lineIndex);\n\n\t\t// Check 7.2 portal and 1.1 commerce links from local liferay-docs repo\n\t\tif ((line.contains(\"/\" + PORTAL_VERSION + \"/\") || line.contains(\"/\" + COMMERCE_VERSION + \"/\")) &&\n\t\t\t\t!differingDefaultVersion) {\n\n\t\t\tboolean docFoldersMatch = doesDocFoldersMatch(line, lineIndex);\n\n\t\t\t// If headers is empty, the assignDirHeaders method could not match the\n\t\t\t// relative URL with the header list (e.g., developer/user). This only\n\t\t\t// happens when the first folder is valid but its subfolder isn't.\n\t\t\tif (headers.isEmpty()) {\n\n\t\t\t\t// Allow linking to parent folders of site\n\t\t\t\tif (line.contains(\"/docs/\" + PORTAL_VERSION + \"/\" + userGuideLinkFolder + \")\") ||\n\t\t\t\t\tline.contains(\"/docs/\" + PORTAL_VERSION + \"/\" + deploymentGuideLinkFolder + \")\") ||\n\t\t\t\t\tline.contains(\"/docs/\" + PORTAL_VERSION + \"/\" + distributeGuideLinkFolder + \")\") ||\n\t\t\t\t\tline.contains(\"/docs/\" + PORTAL_VERSION + \"/\" + appDevLinkFolder + \")\") ||\n\t\t\t\t\tline.contains(\"/docs/\" + PORTAL_VERSION + \"/\" + customizationDevLinkFolder + \")\") ||\n\t\t\t\t\tline.contains(\"/docs/\" + PORTAL_VERSION + \"/\" + frameworksDevLinkFolder + \")\") ||\n\t\t\t\t\tline.contains(\"/docs/\" + PORTAL_VERSION + \"/\" + tutorialsDevLinkFolder + \")\") ||\n\t\t\t\t\tline.contains(\"/docs/\" + PORTAL_VERSION + \"/\" + referenceDevLinkFolder + \")\")) {\n\n\t\t\t\t\tvalidURL = true;\n\t\t\t\t}\n\t\t\t\t// else, invalid link\n\t\t\t}\n\t\t\telse if (!docFoldersMatch) {\n\t\t\t\t// invalid URL\n\t\t\t}\n\t\t\telse if (secondaryHeader == null) {\n\t\t\t\tif (headers.get(0).contains(primaryHeader)) {\n\t\t\t\t\tvalidURL = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\n\t\t\t\tString linkDir = getHeaderDir(line, lineIndex);\n\t\t\t\tList<File> articles = findArticles(linkDir);\n\t\t\t\tFile matchingArticle = findArticleMatchingLink(articles, primaryHeader);\n\t\t\t\tif (headers.get(0).contains(primaryHeader) &&\n\t\t\t\t\t\theaders.get(1).contains(secondaryHeader + matchingArticle.getCanonicalPath())) {\n\t\t\t\t\tvalidURL = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check legacy URLs by checking remote LDN site. These links must be\n\t\t// published to LDN before this tool can verify them.\n\t\telse if (checkLegacyLinks && (line.contains(\"/\" + PORTAL_VERSION_LEGACY_1 + \"/\") ||\n\t\t\t\tline.contains(\"/\" + PORTAL_VERSION_LEGACY_2 + \"/\"))) {\n\n\t\t\tString ldnUrl = extractLdnUrl(line, in.getLineNumber(), article);\n\t\t\tvalidURL = isLdnUrlValid(ldnUrl, article, in.getLineNumber());\n\t\t}\n\t\telse {\n\t\t\tvalidURL = true;\n\t\t}\n\n\t\treturn validURL;\n\t}\n\n\t/**\n\t * Writes a message to the console specifying the article, line, and line\n\t * number for the invalid/corrupt URL.\n\t *\n\t * @param article the article containing the incorrect URL\n\t * @param lineNumber the line number of the line containing the incorrect\n\t *        URL\n\t * @param line the line containing the incorrect URL\n\t * @param corruptUrlFormat whether the reported URL is caused by corrupt\n\t *        formatting\n\t */\n\tprivate static void logInvalidUrl(File article, int lineNumber, String line,\n\t\t\tboolean corruptUrlFormat) {\n\n\t\tString message = null;\n\n\t\tif (corruptUrlFormat) {\n\t\t\tmessage = \"CORRUPT URL FORMATTING\";\n\t\t}\n\t\telse {\n\t\t\tmessage = \"INVALID URL\";\n\t\t}\n\n\t\tresultsNumber = resultsNumber + 1;\n\n\t\tSystem.out.println(resultsNumber + \". \" + \"**\" + message + \"**\\n File: \" +\n\t\t\t\tarticle.getPath() + \":\" + lineNumber + \"\\n\" +\n\t\t\t\t\" Line: \" + line + \"\\n\");\n\n\t}\n\n\tprivate boolean _apiLinks;\n\tprivate String _docdir;\n\tprivate boolean _legacyLinks;\n\tprivate String _appReferenceSite;\n\tprivate String _platformReferenceSite;\n\tprivate String _appToken;\n\tprivate String _platformToken;\n\tprivate boolean _dxpCheck;\n\n\tprivate static String appReferenceSite;\n\tprivate static String appToken;\n\tprivate static boolean checkApiLinks;\n\tprivate static boolean checkDxpLinks;\n\tprivate static boolean checkLegacyLinks;\n\tprivate static List<File> fileOverrides = new ArrayList<File>();\n\tprivate static String findStr = \"/-/knowledge_base/\";\n\tprivate static String headerSyntax = \"header-id: \";\n\tprivate static List<String> secondaryKeyValues = new ArrayList<String>();\n\tprivate static String ldnArticle;\n\tprivate static String platformReferenceSite;\n\tprivate static String platformToken;\n\tprivate static char quotation = '\"';\n\tprivate static int resultsNumber = 0;\n\tprivate static boolean validUrl;\n\n\t// Versions\n\n\tprivate static String COMMERCE_VERSION = \"1-1\";\n\tprivate static String PORTAL_VERSION = \"7-2\";\n\tprivate static String PORTAL_VERSION_LEGACY_1 = \"7-1\";\n\tprivate static String PORTAL_VERSION_LEGACY_2 = \"7-0\";\n\n\t// User Guide\n\n\tprivate static String userGuideDir = \"user\";\n\tprivate static String userGuideLinkFolder = \"user\";\n\tprivate static List<File> userGuideArticles = new ArrayList<File>();\n\tprivate static ArrayList<List<String>> userGuideHeaders = new ArrayList<List<String>>();\n\n\t// Deployment Guide\n\n\tprivate static String deploymentGuideDir = \"deployment\";\n\tprivate static String deploymentGuideLinkFolder = \"deploy\";\n\tprivate static List<File> deploymentGuideArticles = new ArrayList<File>();\n\tprivate static ArrayList<List<String>> deploymentGuideHeaders = new ArrayList<List<String>>();\n\n\tprivate static String distributeGuideDir = \"distribute/publish\";\n\tprivate static String distributeGuideLinkFolder = \"publish\";\n\tprivate static List<File> distributeGuideArticles = new ArrayList<File>();\n\tprivate static ArrayList<List<String>> distributeGuideHeaders = new ArrayList<List<String>>();\n\n\t// Dev Guide\n\n\tprivate static String appDevDir = \"developer/appdev\";\n\tprivate static String appDevLinkFolder = \"appdev\";\n\tprivate static List<File> appDevArticles = new ArrayList<File>();\n\tprivate static ArrayList<List<String>> appDevHeaders = new ArrayList<List<String>>();\n\n\tprivate static String customizationDevDir = \"developer/customization\";\n\tprivate static String customizationDevLinkFolder = \"customization\";\n\tprivate static List<File> customizationDevArticles = new ArrayList<File>();\n\tprivate static ArrayList<List<String>> customizationDevHeaders = new ArrayList<List<String>>();\n\n\tprivate static String frameworksDevDir = \"developer/frameworks\";\n\tprivate static String frameworksDevLinkFolder = \"frameworks\";\n\tprivate static List<File> frameworksDevArticles = new ArrayList<File>();\n\tprivate static ArrayList<List<String>> frameworksDevHeaders = new ArrayList<List<String>>();\n\n\tprivate static String tutorialsDir = \"developer/tutorials\";\n\tprivate static String tutorialsDevLinkFolder = \"tutorials\";\n\tprivate static List<File> tutorialsArticles = new ArrayList<File>();\n\tprivate static ArrayList<List<String>> tutorialsHeaders = new ArrayList<List<String>>();\n\n\tprivate static String referenceDevDir = \"developer/reference\";\n\tprivate static String referenceDevLinkFolder = \"reference\";\n\tprivate static List<File> referenceDevArticles = new ArrayList<File>();\n\tprivate static ArrayList<List<String>> referenceDevHeaders = new ArrayList<List<String>>();\n\n\tprivate static String[] articleDirs = {appDevDir, customizationDevDir, frameworksDevDir, userGuideDir,\n\t\t\tdeploymentGuideDir, distributeGuideDir, tutorialsDir, referenceDevDir};\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/CleanImages.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.LineNumberReader;\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Queue;\n\npublic class CleanImages {\n\n\tpublic static void main(String[] args) throws Exception {\n\n\t\tSystem.out.println(\"Start checking for unused images ...\");\n\n\t\tString docDir = args[0];\n\n\t\tString[] dirTypes = {\"\", \"-dxp\"};\n\n\t\tfor (String dirType : dirTypes) {\n\n\t\t\tFile articleDir = new File(\"../\" + docDir + \"/articles\" + dirType);\n\n\t\t\tArrayList<String> imageNames = new ArrayList<String>();\n\n\t\t\tFile[] articleDirFiles = articleDir.listFiles();\n\t\t\tList<File> articles = new ArrayList<File>();\n\n\t\t\tQueue<File> q = new LinkedList<File>();\n\t\t\tfor (File f : articleDirFiles) {\n\t\t\t\tq.add(f);\n\t\t\t}\n\n\t\t\twhile (!q.isEmpty()) {\n\t\t\t\tFile f = q.remove(); \n\n\t\t\t\tif (f.isDirectory()) {\n\t\t\t\t\tFile[] files = f.listFiles();\n\n\t\t\t\t\tfor (File file : files) {\n\t\t\t\t\t\tq.add(file);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (f.getName().endsWith(\".markdown\")) {\n\t\t\t\t\t\tarticles.add(f);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (File mkdFile : articles) {\n\n\t\t\t\tif (!mkdFile.getName().endsWith(\".markdown\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t//Useful for debugging:\n\t\t\t\t//System.out.println(\" \" + mkdFile.getName());\n\n\t\t\t\tLineNumberReader in = new LineNumberReader(new FileReader(\n\t\t\t\t\t\tmkdFile));\n\t\t\t\tString line = null;\n\t\t\t\tString imageString = \"../../images\";\n\t\t\t\tint imageClosingChar;\n\n\t\t\t\twhile ((line = in.readLine()) != null) {\n\t\t\t\t\tif (line.contains(imageString)) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Check each line for images; if more than one image on\n\t\t\t\t\t\t// line, check each image\n\t\t\t\t\t\twhile (line.contains(imageString)) {\n\n\t\t\t\t\t\t\tint imageIndex = line.indexOf(imageString);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (line.contains(\"<img src=\")) {\n\t\t\t\t\t\t\t\timageClosingChar = line.indexOf(\"\\\"\", imageIndex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\timageClosingChar = line.indexOf(\")\", imageIndex);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tString lineSubstring = line.substring(0, imageClosingChar);\n\n\t\t\t\t\t\t\timageNames.add(extractImageName(lineSubstring, dirType));\n\n\t\t\t\t\t\t\tline = line.substring(lineSubstring.length());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tin.close();\n\t\t\t}\n\n\t\t\tFile imagesDir = new File(\"../\" + docDir + \"/images\" + dirType);\n\t\t\tFile[] imageFiles = imagesDir.listFiles();\n\n\t\t\tfor (File imageFile : imageFiles) {\n\t\t\t\t\n\t\t\t\tif (imageFile.getName().endsWith(\".markdown\") || \n\t\t\t\t\t\timageFile.getName().endsWith(\".txt\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (!imageNames.contains(imageFile.getName())) {\n\t\t\t\t\tSystem.out.println(\"Deleting unused image: \"\n\t\t\t\t\t\t\t+ imageFile.getName());\n\t\t\t\t\timageFile.delete();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static String extractImageName(String line, String dirType) {\n\n\t\tint imageBegIndex;\n\n\t\tif (dirType.equals(\"\")) {\n\n\t\t\t// \"../../images/\" length\n\t\t\timageBegIndex = 13;\n\t\t}\n\t\telse {\n\n\t\t\t// \"../../images-dxp/\" length\n\t\t\timageBegIndex = 17;\n\t\t}\n\n\t\tString imageName = line.substring(\n\t\t\t\tline.lastIndexOf(\"../../images\") + imageBegIndex, line.length());\n\t\t\n\t\treturn imageName;\n\t}\n}\n\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/ConcatMarkdownTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\n\npublic class ConcatMarkdownTask extends Task {\n\t\n\tpublic void execute() throws BuildException {\n\t\t\n\t\tFile docDir = new File(\"../\" + _docdir + \"/articles\");\n\t\t\n\t\tString book = \"\"; \n\t\tint chapter = 0;\n\t\tint count=1;\n\t\t\n\t\ttry {\n\t\t\t\n\t\t\tList<File>files = collectFiles(docDir);\n\t\t\t\n\t\t\tfor (File file : files) {\n\t\t\t\t\n\t\t\t\tif (file.isDirectory()) {\n\t\t\t\t\t\n\t\t\t\t\t// save the chapter number\n\t\t\t\t\tchapter = Integer.parseInt(file.getName().substring(0,2));\n\t\t\t\t\tSystem.out.println(\"Chapter \" + chapter);\n\t\t\t\t\t\n\t\t\t\t} else {\n\t\t\t\t\tLineNumberReader in =\n\t\t\t\t\t\t\tnew LineNumberReader(new FileReader(file));\n\t\t\t\t\t\n\t\t\t\t\tString line;\n\t\t\t\t\t\n\t\t\t\t\t\t\t;\n\t\t\t\t\twhile ((line = in.readLine()) != null) {\n\n\t\t\t\t\t\tif (line.startsWith(\"#\")) {\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (!file.getName().startsWith(\"00\")) {\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tline = \"#\" + line;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (line.contains(\"+$\")) {\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tline = \"+sidebar\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (line.contains(\"$$\")) {\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tline = \"-sidebar\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (line.startsWith(\"![Figure\")) {\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tline = numberImage(line, chapter, count);\n\t\t\t\t\t\t\tcount = count + 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tbook = book + line + \"\\n\";\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tin.close();\n\t\t\t\t\tcount = 1;\n\t\t\t\t\t\n\t\t\t\t\t//book = book + FileUtils.readFileToString(file);\n\t\t\t\t\tbook = book + \"\\n\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}\n\t\t\t\n\t\t\tFile fullBook = new File(\"book.markdown\");\n\t\t\t\n\t\t\tFileUtils.writeStringToFile(fullBook, book);\n\t\t\t\n\t\t\tstripHeaderIds(fullBook.getAbsolutePath());\n\t\t\t\n\t\t\n\t\t} catch (IOException e) {\n\t\t\n\t\t\tSystem.out.println(\"Error: \" + e.getMessage());\n\t\t}\n\t\t\n\t\t\n\t}\n\t\n\tprivate String numberImage (String line, int chapter, int imageNum) {\n\t\t\n\t\t//find the colon\n\t\t//int colon = line.indexOf(\":\");\n\t\t//find the e\n\t\tint e = line.indexOf(\"e\");\n\t\t//find the period\n\t\tint period = line.indexOf(\".\");\n\t\t\n\t\tString prefix = line.substring(0, e+1);\n\t\tSystem.out.println(prefix);\n\t\tString suffix = line.substring(period, line.length());\n\t\tSystem.out.println(suffix);\n\t\tline = prefix + \" \" + chapter + suffix;\n\t\n\t\treturn line;\n\t}\n\t\n\tpublic List<File> collectFiles(File dir) throws FileNotFoundException, IOException {\n\t\tvalidateDirectory(dir);\n\t\tList<File> result = getFileListingNoSort(dir);\n\t\tCollections.sort(result);\n\n\t\tArrayList<File> results = new ArrayList<File>();\n\t\tfor (File file : result) {\n\t\t\tString fileName = file.getCanonicalPath();\n\t\t\tif (!fileName.contains(\".svn\")) {\n\t\t\t\tresults.add(file);\n\t\t\t\tSystem.out.println(file.getName());\n\t\t\t}\n\t\t}\n\t\treturn results;\n\n\t}\n\t\n\tprivate List<File> getFileListingNoSort(\n\t\t\tFile aStartingDir) throws FileNotFoundException {\n\t\tList<File> result = new ArrayList<File>();\n\t\tFile[] filesAndDirs = aStartingDir.listFiles();\n\t\tList<File> filesDirs = Arrays.asList(filesAndDirs);\n\t\tfor (File file : filesDirs) {\n\t\t\tresult.add(file); //always add, even if directory\n\t\t\tif (!file.isFile()) {\n\t\t\t\t//must be a directory\n\t\t\t\t//recursive call!\n\t\t\t\tList<File> deeperList = getFileListingNoSort(file);\n\t\t\t\tresult.addAll(deeperList);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate void stripHeaderIds(String markdownFilePath) throws IOException {\n\t\tFile markdownFile = new File(markdownFilePath);\n\t\tString source = FileUtils.readFileToString(markdownFile);\n\n\t\tString find = \"\\\\[\\\\]\\\\(id=[^\\\\s]+?\\\\)\";\n\t\tSystem.out.println(\"find: \" + find);\n\t\tString replace = \"\";\n\n\t\tPattern pattern = Pattern.compile(find);\n\t\tMatcher matcher = pattern.matcher(source);\n\t\tString output = matcher.replaceAll(replace);\n\n\t\tFileUtils.writeStringToFile(markdownFile, output);\n\t}\n\n\tprivate void validateDirectory(\n\t\t\tFile aDirectory) throws FileNotFoundException {\n\t\tif (aDirectory == null) {\n\t\t\tthrow new IllegalArgumentException(\"Directory should not be null.\");\n\t\t}\n\t\tif (!aDirectory.exists()) {\n\t\t\tthrow new FileNotFoundException(\"Directory does not exist: \" + aDirectory);\n\t\t}\n\t\tif (!aDirectory.isDirectory()) {\n\t\t\tthrow new IllegalArgumentException(\"Is not a directory: \" + aDirectory);\n\t\t}\n\t\tif (!aDirectory.canRead()) {\n\t\t\tthrow new IllegalArgumentException(\"Directory cannot be read: \" + aDirectory);\n\t\t}\n\t}\n\t\n\tpublic void setDocdir(String docdir) {\n\t\t_docdir = docdir;\n\t}\n\t\n\tprivate String _docdir; \n\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/ConvertHeadersTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\n\npublic class ConvertHeadersTask extends Task {\n\n\tpublic void execute() throws BuildException {\n\n\t\tString docDir = _docdir;\n\n\t\tList<String> ceFileList = DocsUtil.getMarkdownFileList(docDir, \"\");\n\t\tList<String> dxpFileList = DocsUtil.getMarkdownFileList(docDir, \"-dxp\");\n\t\t\n\t\tList<String> fileList = new ArrayList<String>();\n\t\tfileList.addAll(ceFileList);\n\t\tfileList.addAll(dxpFileList);\n\t\t\n\t\tfor (int i = 0; i < fileList.size(); i++) {\n\t\t\tString filename = fileList.get(i);\n\t\t\tFile inFile = new File(filename);\n\t\t\tFile outFile = new File(filename);\n\t\t\tString outFileTmp = outFile + \".tmp\";\n\n\t\t\ttry {\n\t\t\t\tLineNumberReader in =\n\t\t\t\t\t\tnew LineNumberReader(new FileReader(inFile));\n\t\t\t\tBufferedWriter out =\n\t\t\t\t\t\tnew BufferedWriter(new FileWriter(outFileTmp));\n\n\t\t\t\tString line;\n\t\t\t\tString headerPrefix = \" [](id=\";\n\t\t\t\tString newHeaderIdPrefix = \"header-id: \";\n\t\t\t\twhile ((line = in.readLine()) != null) {\n\t\t\t\t\t\n\t\t\t\t\tif (line.startsWith(newHeaderIdPrefix)) {\n\t\t\t\t\t\tout.append(line);\n\t\t\t\t\t\tout.append(\"\\n\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (line.startsWith(\"#\") && !line.startsWith(\"##\") &&\n\t\t\t\t\t\t\tline.contains(headerPrefix)) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tint idStartIndex = line.indexOf(headerPrefix);\n\t\t\t\t\t\tint idEndIndex = line.indexOf(\")\", idStartIndex);\n\t\t\t\t\t\t\n\t\t\t\t\t\tString id = line.substring(idStartIndex + headerPrefix.length(), idEndIndex);\n\n\t\t\t\t\t\tline = line.substring(0, idStartIndex);\n\t\t\t\t\t\t\n\t\t\t\t\t\tout.append(\"---\\n\");\n\t\t\t\t\t\tout.append(newHeaderIdPrefix + id + \"\\n\");\n\t\t\t\t\t\tout.append(\"---\\n\\n\");\n\t\t\t\t\t\t\n\t\t\t\t\t\tSystem.out.println(\"Converted header(s) for \" + filename);\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.startsWith(\"##\") && line.contains(headerPrefix)) {\n\t\t\t\t\t\tint idStartIndex = line.indexOf(\" [](id=\");\n\t\t\t\t\t\tline = line.substring(0, idStartIndex);\n\t\t\t\t\t}\n\t\t\t\t\tout.append(line);\n\t\t\t\t\tout.append(\"\\n\");\n\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tin.close();\n\t\t\t\t\n\t\t\t\tout.flush();\n\t\t\t\tout.close();\n\n\t\t\t\t// Replace original file with modified temp file\n\n\t\t\t\tFileUtils.copyFile(\n\t\t\t\t\t\tnew File(outFileTmp),\n\t\t\t\t\t\tnew File(filename));\n\n\t\t\t\tFileUtils.forceDelete(new File(outFileTmp));\n\t\t\t} catch (IOException e) {\n\t\t\t\tthrow new BuildException(e.getLocalizedMessage());\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\n\tpublic void setDocdir(String docdir) {\n\t\t_docdir = docdir;\n\t}\n\n\tprivate String _docdir;\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/ConvertLinksTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\n\npublic class ConvertLinksTask  extends Task {\n\n\tpublic void execute() throws BuildException {\n\n\t\tString docDir = _docdir;\n\n\t\tList<String> ceFileList = DocsUtil.getMarkdownFileList(docDir, \"\");\n\t\tList<String> dxpFileList = DocsUtil.getMarkdownFileList(docDir, \"-dxp\");\n\n\t\tList<String> fileList = new ArrayList<String>();\n\t\tfileList.addAll(ceFileList);\n\t\tfileList.addAll(dxpFileList);\n\n\t\tfor (int i = 0; i < fileList.size(); i++) {\n\t\t\tString filename = fileList.get(i);\n\t\t\tFile inFile = new File(filename);\n\t\t\tFile outFile = new File(filename);\n\t\t\tString outFileTmp = outFile + \".tmp\";\n\t\t\t\n\t\t\ttry {\n\t\t\t\tLineNumberReader in =\n\t\t\t\t\t\tnew LineNumberReader(new FileReader(inFile));\n\t\t\t\tBufferedWriter out =\n\t\t\t\t\t\tnew BufferedWriter(new FileWriter(outFileTmp));\n\t\t\t\n\t\t\t\tString line;\n\t\t\t\tString findStr = \"/-/knowledge_base/\";\n\t\t\t\t\n\t\t\t\twhile ((line = in.readLine()) != null) {\n\t\t\t\t\t\n\t\t\t\t\t// old link syntax checks\n\t\t\t\t\tif (line.contains(\"](/discover/portal/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tString oldLink = getOldLink(\"/discover/portal/\", line, findStr);\n\t\t\t\t\t\tString version = getVersion(line, findStr);\n\t\t\t\t\t\tString headerID = getHeaderID(line, findStr, version);\n\n\t\t\t\t\t\tString newLink = \"/docs/\" + version + \"/user\" + findStr +\n\t\t\t\t\t\t\t\t\"u/\" + headerID;\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(oldLink, newLink);\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/discover/deployment/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tString oldLink = getOldLink(\"/discover/deployment/\", line, findStr);\n\t\t\t\t\t\tString version = getVersion(line, findStr);\n\t\t\t\t\t\tString headerID = getHeaderID(line, findStr, version);\n\t\t\t\t\t\t\n\t\t\t\t\t\tString newLink = \"/docs/\" + version + \"/deploy\" + findStr +\n\t\t\t\t\t\t\t\t\"d/\" + headerID;\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(oldLink, newLink);\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/develop/tutorials/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tString oldLink = getOldLink(\"/develop/tutorials/\", line, findStr);\n\t\t\t\t\t\tString version = getVersion(line, findStr);\n\t\t\t\t\t\tString headerID = getHeaderID(line, findStr, version);\n\t\t\t\t\t\t\n\t\t\t\t\t\tString newLink = \"/docs/\" + version + \"/tutorials\" + findStr +\n\t\t\t\t\t\t\t\t\"t/\" + headerID;\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(oldLink, newLink);\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/develop/reference/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tString oldLink = getOldLink(\"/develop/reference/\", line, findStr);\n\t\t\t\t\t\tString version = getVersion(line, findStr);\n\t\t\t\t\t\tString headerID = getHeaderID(line, findStr, version);\n\t\t\t\t\t\t\n\t\t\t\t\t\tString newLink = \"/docs/\" + version + \"/reference\" + findStr +\n\t\t\t\t\t\t\t\t\"r/\" + headerID;\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(oldLink, newLink);\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\t// new syntax check links\n\t\t\t\t\telse if (line.contains(\"](/developer/reference/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tString oldLink = getOldLink(\"/developer/reference/\", line, findStr);\n\t\t\t\t\t\tString version = getVersion(line, findStr);\n\t\t\t\t\t\tString headerID = getHeaderID(line, findStr, version);\n\t\t\t\t\t\t\n\t\t\t\t\t\tString newLink = \"/docs/\" + version + \"/reference\" + findStr +\n\t\t\t\t\t\t\t\t\"r/\" + headerID;\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(oldLink, newLink);\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/developer/appdev/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tString oldLink = getOldLink(\"/developer/appdev/\", line, findStr);\n\t\t\t\t\t\tString version = getVersion(line, findStr);\n\t\t\t\t\t\tString headerID = getHeaderID(line, findStr, version);\n\t\t\t\t\t\t\n\t\t\t\t\t\tString newLink = \"/docs/\" + version + \"/appdev\" + findStr +\n\t\t\t\t\t\t\t\t\"a/\" + headerID;\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(oldLink, newLink);\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/developer/frameworks/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tString oldLink = getOldLink(\"/developer/frameworks/\", line, findStr);\n\t\t\t\t\t\tString version = getVersion(line, findStr);\n\t\t\t\t\t\tString headerID = getHeaderID(line, findStr, version);\n\t\t\t\t\t\t\n\t\t\t\t\t\tString newLink = \"/docs/\" + version + \"/frameworks\" + findStr +\n\t\t\t\t\t\t\t\t\"f/\" + headerID;\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(oldLink, newLink);\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/developer/customization/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tString oldLink = getOldLink(\"/developer/customization/\", line, findStr);\n\t\t\t\t\t\tString version = getVersion(line, findStr);\n\t\t\t\t\t\tString headerID = getHeaderID(line, findStr, version);\n\t\t\t\t\t\t\n\t\t\t\t\t\tString newLink = \"/docs/\" + version + \"/customization\" + findStr +\n\t\t\t\t\t\t\t\t\"c/\" + headerID;\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(oldLink, newLink);\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/developer/tutorials/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tString oldLink = getOldLink(\"/developer/tutorials/\", line, findStr);\n\t\t\t\t\t\tString version = getVersion(line, findStr);\n\t\t\t\t\t\tString headerID = getHeaderID(line, findStr, version);\n\t\t\t\t\t\t\n\t\t\t\t\t\tString newLink = \"/docs/\" + version + \"/tutorials\" + findStr +\n\t\t\t\t\t\t\t\t\"t/\" + headerID;\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(oldLink, newLink);\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/user/-/knowledge_base/7-\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tString oldLink = getOldLink(\"/user/\", line, findStr);\n\t\t\t\t\t\tString version = getVersion(line, findStr);\n\t\t\t\t\t\tString headerID = getHeaderID(line, findStr, version);\n\t\t\t\t\t\t\n\t\t\t\t\t\tString newLink = \"/docs/\" + version + \"/user\" + findStr +\n\t\t\t\t\t\t\t\t\"u/\" + headerID;\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(oldLink, newLink);\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/deploy/-/knowledge_base/7-\") || \n\t\t\t\t\t\t\tline.contains(\"](/deployment/-/knowledge_base/7-\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tString oldLink;\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (line.contains(\"deployment\")) {\n\t\t\t\t\t\t\toldLink = getOldLink(\"/deployment/\", line, findStr);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\toldLink = getOldLink(\"/deploy/\", line, findStr);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tString version = getVersion(line, findStr);\n\t\t\t\t\t\tString headerID = getHeaderID(line, findStr, version);\n\t\t\t\t\t\t\n\t\t\t\t\t\tString newLink = \"/docs/\" + version + \"/deploy\" + findStr +\n\t\t\t\t\t\t\t\t\"d/\" + headerID;\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(oldLink, newLink);\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\t// newest syntax check links\n\t\t\t\t\telse if (line.contains(\"](/docs/7-2/reference/-/knowledge_base/reference/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(findStr + \"reference\", findStr + \"r\");\n\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/docs/7-2/appdev/-/knowledge_base/appdev/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(findStr + \"appdev\", findStr + \"a\");\n\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/docs/7-2/frameworks/-/knowledge_base/frameworks/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(findStr + \"frameworks\", findStr + \"f\");\n\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/docs/7-2/customization/-/knowledge_base/customization/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(findStr + \"customization\", findStr + \"c\");\n\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/docs/7-2/tutorials/-/knowledge_base/tutorials/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(findStr + \"tutorials\", findStr + \"t\");\n\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/docs/7-2/user/-/knowledge_base/user/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(findStr + \"user\", findStr + \"u\");\n\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse if (line.contains(\"](/docs/7-2/deploy/-/knowledge_base/deploy/\")) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tline = line.replace(findStr + \"deploy\", findStr + \"d\");\n\n\t\t\t\t\t\tSystem.out.println(\"Converted link for \" + filename + \":\" + in.getLineNumber());\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\n\t\t\t\tout.append(line);\n\t\t\t\tout.append(\"\\n\");\n\t\t\t}\n\n\t\t\t\tin.close();\n\n\t\t\t\tout.flush();\n\t\t\t\tout.close();\n\n\t\t\t\t// Replace original file with modified temp file\n\n\t\t\t\tFileUtils.copyFile(\n\t\t\t\t\t\tnew File(outFileTmp),\n\t\t\t\t\t\tnew File(filename));\n\n\t\t\t\tFileUtils.forceDelete(new File(outFileTmp));\n\n\t\t\t} catch (IOException e) {\n\t\t\t\tthrow new BuildException(e.getLocalizedMessage());\n\t\t\t}\n\t\t}\t\n\t}\n\n\tpublic void setDocdir(String docdir) {\n\t\t_docdir = docdir;\n\t}\n\t\n\tprivate static String getHeaderID(String line, String findStr, String version) {\n\t\t\n\t\tint headerStartIndex = line.indexOf(version) + 4;\n\t\tint headerEndIndex = line.indexOf(\")\", headerStartIndex);\n\t\tString headerID = line.substring(headerStartIndex, headerEndIndex);\n\n\t\treturn headerID;\n\t}\n\n\tprivate static String getOldLink(String docType, String line, String findStr) {\n\t\t\n\t\tint startIndex = line.indexOf(docType);\n\t\tint endIndex = line.indexOf(\")\", startIndex);\n\t\t\n\t\tString oldLink = line.substring(startIndex, endIndex);\n\n\t\treturn oldLink;\n\t}\n\t\n\tprivate static String getVersion(String line, String findStr) {\n\t\t\n\t\tint versionIndex = line.indexOf(findStr) + findStr.length();\n\t\tString version = line.substring(versionIndex, versionIndex + 3);\n\n\t\treturn version;\n\t}\n\n\tprivate String _docdir;\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/ConvertSidebarsTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\n\npublic class ConvertSidebarsTask extends Task {\n\n\tpublic void execute() throws BuildException {\n\n\t\tString docDir = _docdir;\n\n\t\tList<String> ceFileList = DocsUtil.getMarkdownFileList(docDir, \"\");\n\t\tList<String> dxpFileList = DocsUtil.getMarkdownFileList(docDir, \"-dxp\");\n\n\t\tList<String> fileList = new ArrayList<String>();\n\t\tfileList.addAll(ceFileList);\n\t\tfileList.addAll(dxpFileList);\n\n\t\tfor (int i = 0; i < fileList.size(); i++) {\n\t\t\tString filename = fileList.get(i);\n\t\t\tFile inFile = new File(filename);\n\t\t\tFile outFile = new File(filename);\n\t\t\tString outFileTmp = outFile + \".tmp\";\n\n\t\t\ttry {\n\t\t\t\tLineNumberReader in =\n\t\t\t\t\t\tnew LineNumberReader(new FileReader(inFile));\n\n\t\t\t\tString line;\n\t\t\t\tboolean sidebarsExist = false;\n\t\t\t\t\n\t\t\t\tList<Integer> sidebarStartIndexes = new ArrayList<Integer>();\n\t\t\t\tList<Integer> sidebarEndIndexes = new ArrayList<Integer>();\n\t\t\t\tList<String> sidebarSpacing = new ArrayList<String>();\n\t\t\t\t\n\t\t\t\twhile ((line = in.readLine()) != null) {\n\n\t\t\t\t\tif (line.contains(sidebarPrefix)) {\n\t\t\t\t\t\tsidebarStartIndexes.add(in.getLineNumber());\n\t\t\t\t\t\tsidebarsExist = true;\n\t\t\t\t\t\t\n\t\t\t\t\t\tString sidebarWhitespace = line.substring(0, line.indexOf(sidebarPrefix));\n\t\t\t\t\t\tsidebarSpacing.add(sidebarWhitespace);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (line.contains(sidebarSuffix) && !line.contains(\"+\")) {\n\t\t\t\t\t\tsidebarEndIndexes.add(in.getLineNumber());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tin.close();\n\t\t\t\t\n\t\t\t\tif (sidebarsExist) {\n\t\t\t\t\tboolean sidebar = false;\n\t\t\t\t\tint j = 0;\n\n\t\t\t\t\tLineNumberReader in2 =\n\t\t\t\t\t\t\tnew LineNumberReader(new FileReader(inFile));\n\n\t\t\t\t\tBufferedWriter out =\n\t\t\t\t\t\t\tnew BufferedWriter(new FileWriter(outFileTmp));\n\n\t\t\t\t\twhile ((line = in2.readLine()) != null) {\n\n\t\t\t\t\t\tint lineNumber = in2.getLineNumber();\n\t\t\t\t\t\tint sidebarStartIndex = sidebarStartIndexes.get(j);\n\t\t\t\t\t\tint sidebarEndIndex;\n\t\t\t\t\t\t\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tsidebarEndIndex = sidebarEndIndexes.get(j);\n\t\t\t\t\t\t} catch (IndexOutOfBoundsException e) {\n\t\t\t\t\t\t\tthrow new BuildException(\"Malformed legacy sidebar in\" + filename);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (lineNumber == sidebarStartIndex) {\n\t\t\t\t\t\t\tsidebar = true;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (lineNumber == (sidebarStartIndex + 1)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (lineNumber == (sidebarEndIndex - 1)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (lineNumber == sidebarEndIndex) {\n\t\t\t\t\t\t\tsidebar = false;\n\t\t\t\t\t\t\tboolean moreSidebars = true;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tsidebarStartIndexes.get(j+1);\n\t\t\t\t\t\t\t} catch (IndexOutOfBoundsException e) {\n\t\t\t\t\t\t\t\tmoreSidebars = false;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (moreSidebars) {\n\t\t\t\t\t\t\t\tj++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (sidebar && (lineNumber > sidebarStartIndexes.get(j) + 1) &&\n\t\t\t\t\t\t\t\t(lineNumber < sidebarEndIndexes.get(j))) {\n\n\t\t\t\t\t\t\tString codeSpace = \"\";\n\t\t\t\t\t\t\tString whitespace = \" \";\n\t\t\t\t\t\t\tint charStart = 0;\n\t\t\t\t\t\t\tint sidebarSpacingLength = sidebarSpacing.get(j).length();\n\n\t\t\t\t\t\t\tPattern p = Pattern.compile(\"[^\\\\s]\");\n\t\t\t\t\t\t\tMatcher m = p.matcher(line);\n\n\t\t\t\t\t\t\tif (m.find()) {\n\t\t\t\t\t\t\t\tcharStart = m.start();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (charStart > sidebarSpacingLength) {\n\n\t\t\t\t\t\t\t\tint spaceDiff = charStart - sidebarSpacingLength;\n\n\t\t\t\t\t\t\t\tcodeSpace = StringUtils.repeat(whitespace, spaceDiff);\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tString modifiedLine = sidebarSpacing.get(j) + \"| \" + codeSpace + line.trim();\n\n\t\t\t\t\t\t\tout.append(modifiedLine);\n\t\t\t\t\t\t\tout.append(\"\\n\");\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tout.append(line);\n\t\t\t\t\t\tout.append(\"\\n\");\n\t\t\t\t\t}\n\n\n\t\t\t\t\tin2.close();\n\n\t\t\t\t\tout.flush();\n\t\t\t\t\tout.close();\n\n\t\t\t\t\t// Replace original file with modified temp file\n\n\t\t\t\t\tFileUtils.copyFile(\n\t\t\t\t\t\t\tnew File(outFileTmp),\n\t\t\t\t\t\t\tnew File(filename));\n\n\t\t\t\t\tFileUtils.forceDelete(new File(outFileTmp));\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\tthrow new BuildException(e.getLocalizedMessage());\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\n\tpublic void setDocdir(String docdir) {\n\t\t_docdir = docdir;\n\t}\n\n\tprivate static String sidebarPrefix = \"+$$$\";\n\tprivate static String sidebarSuffix = \"$$$\";\n\t\n\tprivate String _docdir;\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/DistDiffTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.FilenameFilter;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Scanner;\nimport java.util.Set;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\n\npublic class DistDiffTask extends Task {\n\n\t@Override\n\tpublic void execute() throws BuildException {\n\n\t\tFile dir = new File(\"../\" + _docdir);\n\t\tif (!dir.exists()) {\n\t\t\tthrow new BuildException(\"docdir \" + dir.getAbsolutePath() +\n\t\t\t\t\t\" could not be found\");\n\t\t}\n\n\t\tString absDir = dir.getAbsolutePath();\n\t\tList<String> diffs = null;\n\t\tFile modifiedList = new File(absDir + \"/git-modified-list.txt\");\n\n\t\ttry {\n\t\t\tdiffs = FileUtils.readLines(modifiedList, \"UTF-8\");\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\tString txtPath;\n\t\t\n\t\tif (System.getProperty(\"user.dir\").contains(_purposedir + \"\\\\learning-paths\") ||\n\t\t\t\tSystem.getProperty(\"user.dir\").contains(_purposedir + \"/learning-paths\")) {\n\t\t\ttxtPath = _purposedir + \"/learning-paths/\" + _docdir + \"/\";\n\t\t}\n\n\t\telse {\n\t\t\ttxtPath = _purposedir + \"/\" + _docdir + \"/\";\n\t\t}\n\n\t\tSet<String> files = new HashSet<String>();\n\t\tIterator<String> it = diffs.iterator();\n\n\t\t// Remove lines in the .txt file that are not related to diff entries,\n\t\t// or are diff entry deletions\n\t\twhile (it.hasNext()) {\n\t\t\tString s = it.next();\n\n\t\t\tif (!s.contains(\"DiffEntry\") || s.contains(\"DiffEntry[DELETE\")) {\n\t\t\t\tit.remove();\n\t\t\t}\n\t\t}\n\n\t\t// Grab readable directory paths and add them to list of strings\n\t\tfor (String diff : diffs) {\n\t\t\tint x = diff.indexOf(txtPath);\n\t\t\tint y = x + txtPath.length();\n\t\t\tint z = diff.indexOf(\"]\", x);\n\t\t\tString fileDir = diff.substring(y, z);\n\n\t\t\tfiles.add(fileDir);\n\t\t}\n\n\t\tSet<String> images = new HashSet<String>();\n\t\tSet<String> markdownArticles = new HashSet<String>();\n\n\t\t// Separate modified/new MD files and images\n\t\tfor (String file : files) {\n\t\t\tif (file.endsWith(\".png\")) {\n\t\t\t\timages.add(file);\n\t\t\t}\n\t\t\telse if (file.endsWith(\".markdown\") || file.endsWith(\".md\")) {\n\t\t\t\tmarkdownArticles.add(file);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tSet<File> chFiles = new HashSet<File>();\n\t\tSet<File> filesWithImageFinal = new HashSet<File>();\n\t\tList<String> filesWithImageString = new ArrayList<String>();\n\t\tList<String> filesWithImagePath = new ArrayList<String>();\n\n\t\t// Find all MD files in directory\n\t\tfindMarkdownFiles(dir, chFiles);\n\n\t\tfor (String img : images) {\n\n\t\t\t// Scan directory's MD files for modified/new image\n\t\t\tSet<File> filesWithImage = new HashSet<File>();\n\t\t\tscanMarkdownForImage(img, chFiles, filesWithImage);\n\n\t\t\t// Add the set of MD files that contain the image to a master set\n\t\t\tfor (File file : filesWithImage) {\n\t\t\t\tfilesWithImageFinal.add(file);\n\t\t\t\tSystem.out.println(\"New image \" + img + \" found in file \" + file.getName());\t\t\t\n\t\t\t}\n\t\t}\n\n\t\t// Convert the set of MD files to a list of strings\n\t\tfor (File file : filesWithImageFinal) {\n\t\t\tfilesWithImageString.add(file.toString());\n\t\t}\n\n\t\t// Convert the list of MD files (the modified images' MD files) to\n\t\t// readable directory paths\n\t\tfor (String file : filesWithImageString) {\n\t\t\tint x = file.indexOf(_docdir, file.indexOf(_docdir) + 1);\n\t\t\tint y = x + _docdir.length() + 1;\n\t\t\tString filePath = file.substring(y, file.length());\n\t\t\tfilesWithImagePath.add(filePath);\n\t\t}\n\n\t\tmarkdownArticles.addAll(filesWithImagePath);\n\n\t\tSet<String> markdownArticlesFinal = new HashSet<String>();\n\n\t\t// Make sure all dashes are the same, so MD sets are unique\n\t\tfor (String article : markdownArticles) {\n\t\t\tarticle = article.replace(\"\\\\\", \"/\");\n\t\t\tmarkdownArticlesFinal.add(article);\n\t\t}\n\n\t\tSet<File> markdownImages = new HashSet<File>();\n\t\tSet<File> markdownFiles = new HashSet<File>();\n\t\tSet<String> markdownImagesString = new HashSet<String>();\n\n\t\t// Convert list of MD String files to regular files so we can scan for images\n\t\tfor (String file : markdownArticles) {\n\t\t\tFile markdownFile = new File(file);\n\t\t\tmarkdownFiles.add(markdownFile);\t\t\t\t\n\t\t}\n\t\t\n\t\t// Find and add all modified/new MD files' intro file\n\t\tfindIntroFiles(markdownFiles, markdownArticlesFinal);\n\n\t\t// Scan each MD file for remainder of images to include in ZIP file. When\n\t\t// re-importing a new MD file, all of its images must also be re-imported.\n\t\tscanMarkdownForAllImages(markdownFiles, markdownImages);\n\n\t\tfor (File markdownImage : markdownImages) {\n\t\t\tmarkdownImagesString.add(markdownImage.toString());\n\t\t}\n\n\t\tmarkdownImagesString.addAll(images);\n\n\t\tSet<String> markdownImagesFinal = new HashSet<String>();\n\n\t\t// Make sure all dashes are the same, so image sets are unique\n\t\tfor (String image : markdownImagesString) {\n\t\t\timage = image.replace(\"\\\\\", \"/\");\n\t\t\tmarkdownImagesFinal.add(image);\n\t\t}\n\n\t\tif (files.isEmpty()) {\n\t\t\tSystem.out.println(\"No zip file was produced because there are no available diffs\");\n\t\t}\n\t\telse {\n\t\t\ttry {\n\t\t\t\tSystem.out.println(\"Creating ../dist/diffs.zip file\");\n\t\t\t\tFileOutputStream fileOutputStream = new FileOutputStream(\"dist/diffs.zip\");\n\t\t\t\tZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);\n\n\t\t\t\tfor (String markdown : markdownArticlesFinal) {\n\t\t\t\t\taddToZipFile(markdown, zipOutputStream);\n\t\t\t\t}\n\t\t\t\tfor (String image : markdownImagesFinal) {\n\t\t\t\t\taddToZipFile(image, zipOutputStream);\n\t\t\t\t}\n\n\t\t\t\tzipOutputStream.close();\n\t\t\t\tfileOutputStream.close();\n\n\t\t\t} catch (FileNotFoundException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t} catch (IOException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setDocdir(String docdir) {\n\t\t_docdir = docdir;\n\t}\n\n\tpublic void setPurposedir(String purposedir) {\n\t\t_purposedir = purposedir;\n\t}\n\n\tprivate static void addToZipFile(String modFile, ZipOutputStream zipOutputStream)\n\t\t\tthrows FileNotFoundException, IOException {\n\n\t\tSystem.out.println(\"Adding \" + modFile + \" to zip file\");\n\n\t\tFile file = new File(modFile);\n\t\tFileInputStream fileInputStream = new FileInputStream(file);\n\t\tZipEntry zipEntry = new ZipEntry(modFile);\n\t\tzipOutputStream.putNextEntry(zipEntry);\n\n\t\tbyte[] bytes = new byte[1024];\n\t\tint len;\n\t\twhile ((len = fileInputStream.read(bytes)) >= 0) {\n\t\t\tzipOutputStream.write(bytes, 0, len);\n\t\t}\n\n\t\tzipOutputStream.closeEntry();\n\t\tfileInputStream.close();\n\t}\n\t\n\tprivate static void findIntroFiles(Set<File> markdownFiles, Set<String> markdownArticlesFinal) {\n\t\t\n\t\tSet<File> introFiles = new HashSet<File>();\n\n\t\tfor (File file : markdownFiles) {\n\t\t\tif (!file.toString().contains(\"intro.markdown\") ||\n\t\t\t\t\t!file.toString().contains(\"introduction.markdown\")) {\n\n\t\t\t\tFile parentDir = file.getParentFile();\n\t\t\t\tFile[] dirIntroFiles = parentDir.listFiles(new FilenameFilter() {\n\t\t\t\t\tpublic boolean accept(File dir, String name) {\n\t\t\t\t\t\treturn name.equals(\"intro.markdown\") ||\n\t\t\t\t\t\t\t\tname.equals(\"introduction.markdown\");\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tfor (File introFile : dirIntroFiles) {\n\t\t\t\t\tintroFiles.add(introFile);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (File intro : introFiles) {\n\t\t\tmarkdownArticlesFinal.add(intro.toString().replace(\"\\\\\", \"/\"));\n\t\t}\n\t}\n\n\tprivate static void findMarkdownFiles(File dir, Set<File> chFiles) {\n\n\t\tFile articleDir = new File(dir.getAbsolutePath() + \"/articles\");\n\t\tFile articleDxpDir = new File(dir.getAbsolutePath() + \"/articles-dxp\");\n\t\tFile[] articles = (File[])ArrayUtils.addAll(articleDir.listFiles(), articleDxpDir.listFiles());\n\n\t\tfor (File article : articles) {\n\n\t\t\tif (article.getName().contains(\".\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tFile[] allFiles = article.listFiles();\n\n\t\t\tfor (File file : allFiles) {\n\n\t\t\t\tif (!file.toString().endsWith(\"markdown\") && !file.toString().endsWith(\"md\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tchFiles.add(file);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void scanMarkdownForAllImages(Set<File> modifiedMarkdownFiles, Set<File> markdownImages) {\n\t\tfor (File file : modifiedMarkdownFiles) {\n\n\t\t\tScanner scanner = null;\n\t\t\ttry {\n\t\t\t\tscanner = new Scanner(file);\n\n\t\t\t\twhile (scanner.hasNextLine()) {\n\t\t\t\t\tString lineFromFile = scanner.nextLine();\n\n\t\t\t\t\tif (lineFromFile.contains(\".png\")) { \n\t\t\t\t\t\tint w = lineFromFile.indexOf(\".png\");\n\t\t\t\t\t\tint x = w + 4;\n\t\t\t\t\t\tint y = lineFromFile.indexOf(\"../../images\");\n\n\t\t\t\t\t\tif (y < 0) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tint z = y + 6;\n\t\t\t\t\t\tString img = lineFromFile.substring(z, x);\n\n\t\t\t\t\t\tFile markdownImage = new File(img);\n\n\t\t\t\t\t\tmarkdownImages.add(markdownImage);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (FileNotFoundException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void scanMarkdownForImage(String img, Set<File> chFiles, Set<File> filesWithImage) {\n\t\tfor (File file : chFiles) {\n\n\t\t\tScanner scanner = null;\n\t\t\ttry {\n\t\t\t\tscanner = new Scanner(file);\n\n\t\t\t\twhile (scanner.hasNextLine()) {\n\t\t\t\t\tString lineFromFile = scanner.nextLine();\n\n\t\t\t\t\tif (lineFromFile.contains(img)) { \n\t\t\t\t\t\tfilesWithImage.add(file);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (FileNotFoundException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate String _docdir;\n\tprivate String _purposedir;\n}"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/DocsUtil.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.File;\nimport java.io.FilenameFilter;\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.regex.Pattern;\n\nimport org.apache.tools.ant.BuildException;\n\npublic class DocsUtil {\n\n\tpublic static List<String> getMarkdownFileList(String docDir, String dirType) {\n\t\t\n\t\tFile articlesDir = new File(\"../\" + docDir + \"/articles\" + dirType);\n\t\tFile docSetDir = new File(\"../\" + docDir);\n\n\t\tif (!articlesDir.exists() && !dirType.contains(\"dxp\")) {\n\t\t\tthrow new BuildException(\"FAILURE - bad articles directory \" + articlesDir);\n\t\t}\n\t\telse if (!articlesDir.exists() && dirType.contains(\"dxp\")) {\n\t\t\treturn new ArrayList<String>();\n\t\t}\n\n\t\tList<File> docSetDirFolders = new ArrayList<File>();\n\t\tQueue<File> q = new LinkedList<File>();\n\n\t\tFile articlesDirContents[] = articlesDir.listFiles();\n\t\tfor (File f : articlesDirContents) {\n\t\t\tif (f.isDirectory()) {\n\t\t\t\tq.add(f);\n\t\t\t}\n\t\t}\n\t\n\t\twhile (!q.isEmpty()) {\n\t\t\tFile f = q.remove();\n\t\t\tdocSetDirFolders.add(f);\n\t\t\tFile[] files = f.listFiles();\n\t\t\n\t\t\tfor (File file : files) {\n\t\t\t\tif (file.isDirectory()) {\n\t\t\t\t\tq.add(file);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdocSetDirFolders.add(articlesDir);\n\t\tdocSetDirFolders.add(docSetDir);\n\t\n\t\tFile docSetDirFoldersArray[] = docSetDirFolders.toArray(new File[docSetDirFolders.size()]);\n\t\n\t\tList<String> fileList = new ArrayList<String>();\n\n\t\tfor (int i = 0; i < docSetDirFoldersArray.length; i++) {\n\t\t\tFile files[] = docSetDirFoldersArray[i].listFiles(new FilenameFilter() {\n\t\t\t\tString filePatternArg =\n\t\t\t\t\t\t\"([^\\\\\\\\\\\\[\\\\]\\\\|:;%<>]+).markdown\";\n\t\t\t\tPattern fileNamePattern = Pattern.compile(filePatternArg);\n\n\t\t\t\tpublic boolean accept(File file, String name) {\n\t\t\t\t\treturn (fileNamePattern.matcher(name).matches());\n\t\t\t\t}\n\t\t\t});\n\t\t\n\t\t\tfor (int j = 0; j < files.length; j++) {\n\t\t\t\tfileList.add(files[j].getPath());\n\t\t\t}\n\t\t\n\t\t}\n\t\t\n\t\treturn fileList;\n\t}\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/FindParentIntros.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.io.FileUtils;\n\npublic class FindParentIntros {\n\n\tpublic static void main(String[] args) throws IOException {\n\n\t\t// E.g. articles\\100-tooling\\05-maven\\01-installing-liferay-maven-artifacts.markdown\n\t\tString markdownFilePath = args[0];\n\n\t\t// E.g. tutorials\n\t\tString docDir = args[1];\n\n\t\tString tempDir = args[2];\n\n\t\tList<File> fileList = new ArrayList<File>();\n\t\tFile article = new File(markdownFilePath);\n\n\t\tfileList.add(article);\n\n\t\tFile parentDir = article.getParentFile();\n\n\t\tboolean containsIntro = true;\n\n\t\twhile (containsIntro) {\n\n\t\t\tFile[] parentFiles = parentDir.listFiles();\n\n\t\t\tcontainsIntro = false;\n\n\t\t\tfor (File file : parentFiles) {\n\t\t\t\tif (file.getName().endsWith(\"introduction.markdown\") ||\n\t\t\t\t\t\tfile.getName().endsWith(\"intro.markdown\")) {\n\n\t\t\t\t\tfileList.add(file);\n\n\t\t\t\t\tcontainsIntro = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tparentDir = parentDir.getParentFile();\n\t\t}\n\n\t\tfor (File introFile : fileList) {\n\n\t\t\tString introFileString = introFile.toString();\n\t\t\tint fileStart = introFileString.lastIndexOf(File.separator);\n\t\t\tString filePathExcludingFileName = introFileString.substring(0, fileStart);\n\t\t\tString path = \"../\" + docDir + \"/\" + tempDir + \"/\" + filePathExcludingFileName;\n\n\t\t\tFile file = new File(path);\n\t\t\tfile.mkdirs();\n\n\t\t\tFile dest = new File(path);\n\n\t\t\ttry {\n\t\t\t    FileUtils.copyFileToDirectory(introFile, dest);\n\t\t\t} catch (IOException e) {\n\t\t\t    e.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/GitCompare.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.Calendar;\nimport java.util.List;\n\nimport org.eclipse.jgit.api.Git;\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.diff.DiffEntry;\nimport org.eclipse.jgit.lib.ObjectReader;\nimport org.eclipse.jgit.lib.Ref;\nimport org.eclipse.jgit.lib.Repository;\nimport org.eclipse.jgit.revwalk.RevCommit;\nimport org.eclipse.jgit.revwalk.RevTree;\nimport org.eclipse.jgit.revwalk.RevWalk;\nimport org.eclipse.jgit.storage.file.FileRepositoryBuilder;\nimport org.eclipse.jgit.treewalk.AbstractTreeIterator;\nimport org.eclipse.jgit.treewalk.CanonicalTreeParser;\n\npublic class GitCompare {\n\n\tpublic static void main(String[] args) throws GitAPIException, IOException  {\n\n\t\tString docdir = args[0];\n\t\tString purposedir = args[1];\n\t\tString olderBranch = args[2];\n\t\t\n\t\tRepository repo = openGitRepository();\n\t\tString importBranch = repo.getBranch();\n\t\tAbstractTreeIterator oldTreeParser = gitTreeParser(repo, \"refs/heads/\" + olderBranch);\n\t\tAbstractTreeIterator importTreeParser = gitTreeParser(repo, \"refs/heads/\" + importBranch);\n\n\t\tList<DiffEntry> diff = new Git(repo).diff().setOldTree(oldTreeParser).setNewTree(importTreeParser).call();\n\n\t\tSystem.out.println(\"Creating ../git-modified-list.txt file\");\n\t\tPrintWriter writer = new PrintWriter(\"git-modified-list.txt\", \"UTF-8\");\n\t\taddTimeStamp(\"Comparing your \" + importBranch + \" branch to your older branch \"\n\t\t\t\t+ olderBranch + \" .\\nGenerated on\", writer);\n\t\tSystem.out.println(\"Comparing your \" + importBranch + \" branch to your older branch \"+ olderBranch);\n\n\t\tboolean newDiff = false;\n\t\tString baseFilePath;\n\t\t\n\t\tif (System.getProperty(\"user.dir\").contains(purposedir + \"\\\\learning-paths\") ||\n\t\t\t\tSystem.getProperty(\"user.dir\").contains(purposedir + \"/learning-paths\")) {\n\t\t\tbaseFilePath = purposedir + \"/learning-paths/\" + docdir;\n\t\t}\n\t\t\n\t\telse {\n\t\t\tbaseFilePath = purposedir + \"/\" + docdir + \"/\";\n\t\t}\n\n\t\tfor (DiffEntry entry : diff) {\n\t\t\tString stringEntry = entry.toString();\n\n\t\t\tif (stringEntry.contains(baseFilePath)) {\n\t\t\t\twriter.println(stringEntry);\n\t\t\t\tSystem.out.println(stringEntry);\n\t\t\t\tnewDiff = true;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!newDiff) {\n\t\t\tSystem.out.println(\"There are no additions/modifications in ../\" + baseFilePath);\n\t\t\twriter.println(\"There are no new additions/modifications to report\");\n\t\t}\n\n\t\twriter.close();\n\t\trepo.close();\n\t}\n\n\tprivate static void addTimeStamp(String message, PrintWriter writer) {\n\n\t    Calendar calendar = Calendar.getInstance();\n\t    writer.printf(\"%s %tc\\n\\n\", message, calendar);\n\t  }\n\n\tprivate static AbstractTreeIterator gitTreeParser(Repository repo, String ref)\n\t\t\tthrows IOException {\n\n\t\tRef gitHead = repo.getRef(ref);\n\t\tRevWalk revWalk = new RevWalk(repo);\n\t\tRevCommit gitCommit = revWalk.parseCommit(gitHead.getObjectId());\n\t\tRevTree gitTree = revWalk.parseTree(gitCommit.getTree().getId());\n\t\tCanonicalTreeParser treeParser = new CanonicalTreeParser();\n\t\tObjectReader newObjectReader = repo.newObjectReader();\n\n\t\ttry {\n\t\t\ttreeParser.reset(newObjectReader, gitTree.getId());\n\t\t}\n\t\tfinally {\n\t\t\tnewObjectReader.release();\n\t\t}\n\t\trevWalk.dispose();\n\t\treturn treeParser;\n\t}\n\n\tprivate static Repository openGitRepository() throws IOException {\n\n\t\tFileRepositoryBuilder repoBuilder = new FileRepositoryBuilder();\n\t\tRepository repo = repoBuilder.readEnvironment().findGitDir().build();\n\n\t\treturn repo;\n\t}\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/MarkdownToHtml.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.documentation.util;\n\nimport java.io.BufferedReader;\nimport java.io.BufferedWriter;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.IOException;\n\nimport com.liferay.knowledge.base.markdown.converter.MarkdownConverter;\nimport com.liferay.knowledge.base.markdown.converter.internal.flexmark.LiferayFlexmarkConverter;\n\n\n\npublic class MarkdownToHtml {\n\n\tpublic static void main(String[] args) throws IOException {\n\t\tif (args == null || args.length < 2) {\n\t\t\tthrow new IllegalArgumentException(\"Requires 2 arguments: markdownFile htmlFile\");\n\t\t}\n\n\t\tString markdownFile = args[0];\n\t\tString htmlFile = args[1];\n\n        StringBuilder sb = new StringBuilder();\n\n\t    BufferedReader br = new BufferedReader(new FileReader(markdownFile));\n\n\t    String line = br.readLine();\n\n\t    while (line != null) {\n\t    \tsb.append(line);\n\t    \tsb.append(\"\\n\");\n\t    \tline = br.readLine();\n\t\t}\n\n\t    br.close();\n\n\t    MarkdownConverter converter = new LiferayFlexmarkConverter();\n\n\t    String html = converter.convert(sb.toString());\n\n\t\tBufferedWriter out =\n\t\t\tnew BufferedWriter(new FileWriter(htmlFile));\n\n\t\tout.append(html);\n\t\tout.append(\"\\n\");\n\t\tout.flush();\n\t\tout.close();\n\t}\n\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/NumberHeadersTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.FilenameFilter;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.regex.Pattern;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\n\npublic class NumberHeadersTask extends Task {\n\n\t@Override\n\tpublic void execute() throws BuildException {\n\n\t\tString docDir = _docdir;\n\t\tString productType = _productType;\n\n\t\tboolean dxpBuild = false;\n\n\t\tceFileList = getFileList(docDir, \"\");\n\t\t\n\t\tList<String> dirTypes = new ArrayList<String>();\n\t\tdirTypes.add(\"\");\n\n\t\tif (productType.equals(\"dxp\")) {\n\t\t\tFile articlesDxpDir = new File(\"../\" + docDir + \"/articles-dxp\");\n\t\t\t\n\t\t\tif (articlesDxpDir.exists()) {\n\t\t\t\tdirTypes.add(\"-dxp\");\n\t\t\t\tdxpBuild = true;\n\t\t\t\n\t\t\t\tdxpFileList = getFileList(docDir, \"-dxp\");\n\t\t\t}\n\n\t\t}\n\t\t\n\t\tif (ceFileList.size() == 0 && dxpFileList.size() == 0) {\n\t\t\tthrow new BuildException(\n\t\t\t\t\t\"FAILURE - No files in this directory\");\n\t\t}\n\n\t\tList<String> duplicateFiles = new ArrayList<String>();\n\n\t\tif (dxpBuild) {\n\t\t\tduplicateFiles = getDuplicateFiles(docDir, dirTypes);\n\t\t}\n\n\t\tfor (String dirType : dirTypes) {\n\t\t\t\n\t\t\tSystem.out.println(\n\t\t\t\t\t\"Numbering headers for files in ../\" + docDir + \"/articles\" +\n\t\t\t\t\t\t\tdirType + \" ...\");\n\t\t\t\n\t\t\tList<String> fileList = new ArrayList<String>();\n\t\t\t\n\t\t\tif (dirType.contains(\"dxp\")) {\n\t\t\t\tfileList = dxpFileList;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfileList = ceFileList;\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < fileList.size(); i++) {\n\t\t\t\tString filename = fileList.get(i);\n\t\t\t\tFile inFile = new File(filename);\n\t\t\t\tFile outFile = new File(filename);\n\t\t\t\tString outFileTmp = outFile + \".tmp\";\n\t\t\t\t\n\t\t\t\tfoundDuplicateIds = false;\n\t\t\t\t\n\t\t\t\tif (dirType.contains(\"dxp\")) {\n\t\t\t\t\toverrideFile = isOverrideFile(filename, duplicateFiles);\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tLineNumberReader in =\n\t\t\t\t\t\t\tnew LineNumberReader(new FileReader(inFile));\n\t\t\t\t\tBufferedWriter out =\n\t\t\t\t\t\t\tnew BufferedWriter(new FileWriter(outFileTmp));\n\n\t\t\t\t\tString line;\n\t\t\t\t\tint titleHeaderLineNum = -2;\n\t\t\t\t\tboolean headerValidated = false;\n\t\t\t\t\twhile ((line = in.readLine()) != null) {\n\n\t\t\t\t\t\tint lineNum = in.getLineNumber();\n\n\t\t\t\t\t\tif (line.startsWith(\"#\") && !line.startsWith(\"##\") && !headerValidated) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t\ttitleHeaderLineNum = in.getLineNumber();\n\t\t\t\t\t\t\tline = line.trim();\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// search for header ID; if header DNE, generate header\n\t\t\t\t\t\t\tString headerIdLine = getHeaderIdLine(inFile);\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (headerIdLine == null) {\n\t\t\t\t\t\t\t\tString headerId = generateNewHeader(filename, line, in.getLineNumber());\n\t\t\t\t\t\t\t\tout.append(\"---\\n\");\n\t\t\t\t\t\t\t\tout.append(headerIdPrefix + headerId + \"\\n\");\n\t\t\t\t\t\t\t\tout.append(\"---\\n\\n\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// validate existing header\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tvalidateHeaderId(filename, headerIdLine, in.getLineNumber(), true);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\theaderValidated = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (lineNum == titleHeaderLineNum + 1) {\n\t\t\t\t\t\t\tif (!line.equals(\"\")) {\n\t\t\t\t\t\t\t\tthrow new BuildException(\"Filename: \" + filename +\n\t\t\t\t\t\t\t\t\t\t\" Line following main title header must be blank.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tout.append(line);\n\t\t\t\t\t\tout.append(\"\\n\");\n\t\t\t\t\t}\n\t\t\t\t\tin.close();\n\t\t\t\t\n\t\t\t\t\tout.flush();\n\t\t\t\t\tout.close();\n\n\t\t\t\t\t// Replace original file with modified temp file\n\n\t\t\t\t\tFileUtils.copyFile(\n\t\t\t\t\t\t\tnew File(outFileTmp),\n\t\t\t\t\t\t\tnew File(filename));\n\n\t\t\t\t\tFileUtils.forceDelete(new File(outFileTmp));\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tthrow new BuildException(e.getLocalizedMessage());\n\t\t\t\t}\n\n\t\t\t\tif (foundDuplicateIds && !overrideFile) {\n\t\t\t\t\tthrow new BuildException(\"FAILURE - Duplicate header IDs exist\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\t\n\t\t// Make sure override files have same header IDs as original\n\t\t\n\t\tif(!duplicateFiles.isEmpty()) {\n\t\t\tfor(String duplicateFile : duplicateFiles) {\n\t\t\t\tString duplicateFileDxp = duplicateFile;\n\t\t\t\tduplicateFileDxp = duplicateFileDxp.replace(\n\t\t\t\t\t\t\"\\\\articles\\\\\", \"\\\\articles-dxp\\\\\");\n\t\t\t\tduplicateFileDxp = duplicateFileDxp.replace(\n\t\t\t\t\t\t\"/articles/\", \"/articles-dxp/\");\n\t\t\t\t\n\t\t\t\ttry {\n\t\t\t\t\tcheckOverrideHeaders(duplicateFile, duplicateFileDxp);\n\t\t\t\t\tif (foundDuplicateIds) {\n\t\t\t\t\t\tthrow new BuildException(\"FAILURE - Duplicate header IDs exist\");\n\t\t\t\t\t}\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// This clears the header IDs stored; this avoids false reports of\n\t\t// duplicated IDs if the task is run again during the same process.\n\t\tIDS.clear();\n\t}\n\n\tprivate static String assembleId(String heading, int idCount) {\n\n\t\tString count = \"\";\n\t\tif (idCount > -1) {\n\t\t\tcount = \"-\" + idCount;\n\t\t}\n\n\t\tint idLength = heading.length() + count.length();\n\t\tif (idLength >  MAX_ID_LEN) {\n\t\t\theading = heading.substring(\n\t\t\t\t0,\n\t\t\t\t(heading.length() - Math.abs(idLength -  MAX_ID_LEN)));\n\t\t}\n\n\t\tStringBuffer sb = new StringBuffer(heading);\n\t\tsb.append(count);\n\t\n\t\tString finalHeaderId = sb.toString();\n\t\t\n\t\tif (finalHeaderId.contains(\"--\")) {\n\t\t\tfinalHeaderId = finalHeaderId.replaceAll(\"--\", \"-\");\n\t\t}\n\n\t\treturn finalHeaderId;\n\t}\n\t\n\tprivate static void checkOverrideHeaders(String duplicateFile, String duplicateFile2) \n\t\t\tthrows IOException {\n\t\t\n\t\tFile inFile = new File(duplicateFile);\n\t\tFile outFile = new File(duplicateFile);\n\t\tFile inFile2 = new File(duplicateFile2);\n\t\tFile outFile2 = new File(duplicateFile2);\n\t\tString outFileTmp = outFile + \".tmp\";\n\t\tString outFileTmp2 = outFile2 + \".tmp\";\n\t\tFile outFileTmpFile = new File(outFileTmp);\n\t\tFile outFileTmpFile2 = new File(outFileTmp2);\n\t\t\n\t\tLineNumberReader in =\n\t\t\t\tnew LineNumberReader(new FileReader(inFile));\n\t\tBufferedWriter out =\n\t\t\t\tnew BufferedWriter(new FileWriter(outFileTmp));\n\t\tLineNumberReader in2 =\n\t\t\t\tnew LineNumberReader(new FileReader(inFile2));\n\t\tBufferedWriter out2 =\n\t\t\t\tnew BufferedWriter(new FileWriter(outFileTmp2));\n\t\t\n\t\tString headerIdLineCe = getHeaderIdLine(inFile);\n\t\tString headerIdLineDxp = getHeaderIdLine(inFile2);\n\n\t\tboolean equalHeaders = false;\n\t\t\n\t\tif (filenamesWithPresetHeader.contains(duplicateFile) && \n\t\t\t\tfilenamesWithPresetHeader.contains(duplicateFile2)) {\n\t\t\t\n\t\t\tif (headerIdLineCe.equals(headerIdLineDxp)) {\n\t\t\t\tequalHeaders = true;\n\t\t\t}\n\t\t\telse {\n\t\t\t\theaderIdLineDxp = headerIdLineCe;\n\t\t\t}\n\t\t\t\n\t\t}\n\t\telse if (filenamesWithPresetHeader.contains(duplicateFile) && \n\t\t\t\tfilenamesWithoutPresetHeader.contains(duplicateFile2)) {\n\t\t\t\n\t\t\theaderIdLineDxp = headerIdLineCe;\n\t\t}\n\t\telse if (filenamesWithoutPresetHeader.contains(duplicateFile) && \n\t\t\t\tfilenamesWithPresetHeader.contains(duplicateFile2)) {\n\t\t\t\n\t\t\theaderIdLineCe = headerIdLineDxp;\n\n\t\t\t// Check cases where there are two DXP override files with matching\n\t\t\t// header IDs. This case is not checked with the general validation\n\t\t\t// logic. Add new CE header to ID list and validate it.\n\t\t\tIDS.put(getHeaderId(headerIdLineCe), inFile.getName());\n\n\t\t\t// Disable overrideFile flag, so validation for the new header ID can\n\t\t\t// process.\n\t\t\toverrideFile = false;\n\t\t\tvalidateHeaderId(inFile.getName(), headerIdLineCe, in.getLineNumber(), false);\n\t\t}\n\t\telse {\n\t\t\theaderIdLineDxp = headerIdLineCe;\n\t\t}\n\t\t\n\t\tif (!equalHeaders) {\n\t\t\tString line;\n\t\t\t\n\t\t\twhile ((line = in.readLine()) != null) {\n\t\t\t\tif (line.startsWith(headerIdPrefix)) {\n\t\t\t\t\tout.append(headerIdLineCe);\n\t\t\t\t\tout.append(\"\\n\");\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tout.append(line);\n\t\t\t\t\tout.append(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tin.close();\t\n\t\t\tout.flush();\n\t\t\tout.close();\n\n\t\t\tFileUtils.copyFile(\n\t\t\t\t\toutFileTmpFile,\n\t\t\t\t\tnew File(duplicateFile));\n\n\t\t\tFileUtils.forceDelete(outFileTmpFile);\n\n\t\t\twhile ((line = in2.readLine()) != null) {\n\t\t\t\tif (line.startsWith(headerIdPrefix)) {\n\t\t\t\t\tout2.append(headerIdLineDxp);\n\t\t\t\t\tout2.append(\"\\n\");\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tout2.append(line);\n\t\t\t\t\tout2.append(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tin2.close();\n\t\t\tout2.flush();\n\t\t\tout2.close();\n\n\t\t\tFileUtils.copyFile(\n\t\t\t\t\toutFileTmpFile2,\n\t\t\t\t\tnew File(duplicateFile2));\n\n\t\t\tFileUtils.forceDelete(outFileTmpFile2);\n\t\t}\n\t\telse {\n\t\t\tin.close();\t\n\t\t\tout.flush();\n\t\t\tout.close();\n\t\t\t\n\t\t\tFileUtils.forceDelete(outFileTmpFile);\n\t\t\t\n\t\t\tin2.close();\t\n\t\t\tout2.flush();\n\t\t\tout2.close();\n\t\t\t\n\t\t\tFileUtils.forceDelete(outFileTmpFile2);\n\t\t}\n\t}\n\n\tprivate static String extractHeading(String line, int indexOfFirstHeaderChar) {\n\t\tString heading2 = line.substring(indexOfFirstHeaderChar);\n\n\t\t// Applicable for legacy IDs from 7.1 and earlier.\n\t\t// This ignores old header ID syntax.\n\t\tif (heading2.contains(\" [](id=\")) {\n\t\t\theading2 = line.substring(indexOfFirstHeaderChar, line.indexOf(\" [](id=\"));\n\t\t}\n\t\theading2 = heading2.trim();\n\n\t\t// Replace each spaced dash, space, dot, and slash with a dash\n\n\t\theading2 = heading2.replace(\" - \", \"-\");\n\t\theading2 = heading2.replace(' ', '-');\n\t\theading2 = heading2.replace('.', '-');\n\t\theading2 = heading2.replace('/', '-');\n\t\theading2 = heading2.toLowerCase();\n\n\t\t// Filter out characters other than dashes, letters, and digits\n\n\t\tStringBuffer headingSb = new StringBuffer();\n\t\tfor (int i = 0; i < heading2.length(); i++) {\n\t\t\tchar ch = heading2.charAt(i);\n\n\t\t\tif (ch == '-' || Character.isLetterOrDigit(ch)) {\n\t\t\t\theadingSb.append(ch);\n\t\t\t}\n\t\t}\n\t\theading2 = headingSb.toString();\n\t\treturn heading2;\n\t}\n\t\n\tprivate static String generateNewHeader(String filename, String line, int lineNum) {\n\n\t\tfilenamesWithoutPresetHeader.add(filename);\n\n\t\t// Find the start of the header text\n\n\t\tint indexOfFirstHeaderChar = -1;\n\t\tfor (int i = 0; i < line.length(); i++) {\n\t\t\tchar ch = line.charAt(i);\n\t\t\tif (ch != '#' && ch != ' ' && ch != '\\t') {\n\t\t\t\tindexOfFirstHeaderChar = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tString heading = null;\n\t\tif (indexOfFirstHeaderChar > 0) {\n\t\t\theading = extractHeading(line, indexOfFirstHeaderChar);\n\t\t}\n\t\telse {\n\t\t\tthrow new BuildException(\"WARNING - \"  + filename + \":\" +\n\t\t\t\tlineNum + \" is missing header text.\");\n\t\t}\n\n\t\tint idCount = -1;\n\t\tString newHeading = null;\n\t\twhile (true) {\n\n\t\t\tnewHeading = assembleId(heading, idCount);\n\n\t\t\t\tif (IDS.get(newHeading) == null) {\n\n\t\t\t\t\t// Heading is unique for the document set\n\n\t\t\t\t\t// Map the ID to the filename\n\n\t\t\t\t\tIDS.put(newHeading, filename);\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\tidCount++;\n\t\t}\n\n\t\treturn newHeading;\n\t}\n\n\tprivate static String getArticlesDirName(String filename) {\n\t\tint index1 = filename.indexOf(\"articles\");\n\t\tint index2;\n\t\tif (filename.contains(\"\\\\\")) {\n\t\t\tindex2 = filename.indexOf(\"\\\\\", index1);\n\t\t}\n\t\telse {\n\t\t\tindex2 = filename.indexOf(\"/\", index1);\n\t\t}\n\t\t\n\t\tfilename = filename.substring(index2 + 1);\n\t\t\n\t\treturn filename;\n\t}\n\n\tprivate static List<String> getDuplicateFiles(String docDir,\n\t\t\tList<String> dirTypes) {\n\t\t\n\t\tList<String> convertedFileList = new ArrayList<String>();\n\t\tList<String> duplicateFiles = new ArrayList<String>();\n\t\t\n\t\tfor (String f : dxpFileList) {\n\t\t\tf = f.replace(\"\\\\articles-dxp\\\\\", \"\\\\articles\\\\\");\n\t\t\tf = f.replace(\"/articles-dxp/\", \"/articles/\");\n\t\t\tconvertedFileList.add(f);\n\t\t}\n\t\t\n\t\tduplicateFiles = new ArrayList<String>(ceFileList);\n\t\tduplicateFiles.retainAll(convertedFileList);\n\t\t\n\t\treturn duplicateFiles;\n\t}\n\t\n\tprivate static List<String> getFileList(String docDir, String dirType)\n\t\t\tthrows BuildException {\n\t\t\n\t\tFile articlesDir = new File(\"../\" + docDir + \"/articles\" + dirType);\n\t\tFile docSetDir = new File(\"../\" + docDir);\n\n\t\tif (!articlesDir.exists() || !articlesDir.isDirectory()) {\n\t\t\tthrow new BuildException(\n\t\t\t\t\t\"FAILURE - bad articles directory \" + articlesDir);\n\t\t}\n\t\tList<File> docSetDirFolders = new ArrayList<File>();\n\t\tQueue<File> q = new LinkedList<File>();\n\n\t\tFile articlesDirContents[] = articlesDir.listFiles();\n\t\tfor (File f : articlesDirContents) {\n\t\t\tif (f.isDirectory()) {\n\t\t\t\tq.add(f);\n\t\t\t}\n\t\t}\n\t\n\t\twhile (!q.isEmpty()) {\n\t\t\tFile f = q.remove();\n\t\t\tdocSetDirFolders.add(f);\n\t\t\tFile[] files = f.listFiles();\n\t\t\n\t\t\tfor (File file : files) {\n\t\t\t\tif (file.isDirectory()) {\n\t\t\t\t\tq.add(file);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdocSetDirFolders.add(articlesDir);\n\t\tdocSetDirFolders.add(docSetDir);\n\t\n\t\tFile docSetDirFoldersArray[] = docSetDirFolders.toArray(new File[docSetDirFolders.size()]);\n\t\n\t\tList<String> fileList = new ArrayList<String>();\n\n\t\tfor (int i = 0; i < docSetDirFoldersArray.length; i++) {\n\t\t\tFile files[] = docSetDirFoldersArray[i].listFiles(new FilenameFilter() {\n\t\t\t\tString filePatternArg =\n\t\t\t\t\t\t\"([^\\\\\\\\\\\\[\\\\]\\\\|:;%<>]+).markdown\";\n\t\t\t\tPattern fileNamePattern = Pattern.compile(filePatternArg);\n\n\t\t\t\tpublic boolean accept(File file, String name) {\n\t\t\t\t\treturn (fileNamePattern.matcher(name).matches());\n\t\t\t\t}\n\t\t\t});\n\t\t\n\t\t\tfor (int j = 0; j < files.length; j++) {\n\t\t\t\tfileList.add(files[j].getPath());\n\t\t\t}\n\t\t\n\t\t}\n\t\t\n\t\treturn fileList;\n\t}\n\t\n\tprivate static String getHeaderId(String headerIdLine) {\n\n\t\tint idStartIndex = headerIdLine.indexOf(headerIdPrefix) + headerIdPrefix.length();\n\t\tint idEndIndex = headerIdLine.length();\n\n\t\tString id = null;\n\t\tif (idStartIndex > 0 && idEndIndex > (idStartIndex + 1)) {\n\t\t\tid = headerIdLine.substring(idStartIndex, idEndIndex);\n\t\t}\n\t\t\n\t\treturn id;\n\t}\n\n\tprivate static String getHeaderIdLine(File file) throws IOException {\n\n\t\tString headerIdLine;\n\n\t\tLineNumberReader in = new LineNumberReader(new FileReader(file));\n\n\t\twhile ((headerIdLine = in.readLine()) != null) {\n\t\t\tif (headerIdLine.startsWith(headerIdPrefix)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tin.close();\n\n\t\tif (headerIdLine != null) {\n\t\t\theaderIdLine = headerIdLine.trim();\n\t\t}\n\n\t\treturn headerIdLine;\n\t}\n\n\tprivate static boolean isOverrideFile(String filename, List<String> duplicateFiles) {\n\t\t\n\t\toverrideFile = false;\n\t\t\n\t\tfilename = getArticlesDirName(filename);\n\t\t\n\t\tfor (String f : duplicateFiles) {\n\t\t\tif (f.contains(filename)) {\n\t\t\t\toverrideFile = true;\n\t\t\t}\n\t\t}\n\n\t\treturn overrideFile;\n\t}\n\t\n\tprivate static void validateHeaderId(String filename, String headerIdLine, int lineNum, boolean presetHeader) {\n\n\t\tif (presetHeader) {\n\t\t\tfilenamesWithPresetHeader.add(filename);\n\t\t}\n\t\t// Extract the header ID\n\n\t\tString id = getHeaderId(headerIdLine);\n\n\t\tif (id.length() > MAX_ID_LEN) {\n\t\t\tStringBuilder sb =\n\t\t\t\tnew StringBuilder(\"FAILURE - ID longer than \");\n\t\t\tsb.append(MAX_ID_LEN);\n\t\t\tsb.append(\" chars in \");\n\t\t\tsb.append(filename);\n\t\t\tsb.append(\" - \");\n\t\t\tsb.append(id);\n\t\t\tthrow new BuildException(sb.toString());\n\t\t}\n\t\t\n\t\t// Check if the primary ID is already in use\n\n\t\tString filename2 = IDS.get(id);\n\n\t\tif (filename2 != null && !overrideFile) {\n\n\t\t\t//print error\n\n\t\t\tSystem.out.println(\"Dup id:\" + id + \" file:\" +\n\t\t\t\t\tfilename + \" line:\" + lineNum + \" (already used by file:\" +\n\t\t\t\t\tfilename2 + \")\");\n\n\t\t\tfoundDuplicateIds = true;\n\t\t}\n\t\telse {\n\n\t\t\t// Add the ID\n\n\t\t\tIDS.put(id, filename);\n\t\t}\n\t}\n\n\tprivate static boolean foundDuplicateIds;\n\n\tprivate static boolean overrideFile;\n\n\tprivate static final int MAX_ID_LEN = 75;\n\n\tprivate static HashMap<String, String> IDS = new HashMap<String, String>();\n\t\n\tprivate static List<String> ceFileList = new ArrayList<String>();\n\t\n\tprivate static List<String> dxpFileList = new ArrayList<String>();\n\n\tprivate static List<String> filenamesWithPresetHeader = new ArrayList<String>();\n\t\n\tprivate static List<String> filenamesWithoutPresetHeader = new ArrayList<String>();\n\n\tprivate static String headerIdPrefix = \"header-id: \";\n\n\tpublic void setDocdir(String docdir) {\n\t\t_docdir = docdir;\n\t}\n\n\tpublic void setProductType(String productType) {\n\t\t_productType = productType;\n\t}\n\n\tprivate String _docdir;\n\tprivate String _productType;\n}\n"
  },
  {
    "path": "code/liferay-doc-utils/src/com/liferay/documentation/util/NumberImagesTask.java",
    "content": "package com.liferay.documentation.util;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.tools.ant.BuildException;\nimport org.apache.tools.ant.Task;\n\npublic class NumberImagesTask extends Task {\n\t\n\t@Override\n\tpublic void execute() throws BuildException {\n\t\t\nString productType = _productType;\n\n\t\tList<String> dirTypes = new ArrayList<String>();\n\t\tdirTypes.add(\"\");\n\n\t\tif (productType.equals(\"dxp\")) {\n\t\t\tdirTypes.add(\"-dxp\");\n\t\t}\n\n\t\tfor (String dirType : dirTypes) {\n\n\t\t\tFile docDir = new File(\"../\" + _docDir);\n\t\t\tFile articleDir = new File(docDir.getAbsolutePath() + \"/articles\" + dirType);\n\t\t\tSystem.out.println(\"Numbering images for files in \"\n\t\t\t\t\t+ articleDir.getPath() + \" ...\");\n\n\t\t\tif (!articleDir.exists()) {\n\t\t\t\tif (!dirType.contains(\"dxp\")) {\n\t\t\t\t\tthrow new BuildException(\"FAILURE - no articles directory \" + articleDir);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tFile[] articleDirFiles = articleDir.listFiles();\n\t\t\tList<File> articles = new ArrayList<File>();\n\n\t\t\tQueue<File> q = new LinkedList<File>();\n\t\t\tfor (File f : articleDirFiles) {\n\t\t\t\tq.add(f);\n\t\t\t}\n\n\t\t\twhile (!q.isEmpty()) {\n\t\t\t\tFile f = q.remove(); \n\n\t\t\t\tif (f.isDirectory()) {\n\t\t\t\t\tFile[] files = f.listFiles();\n\n\t\t\t\t\tfor (File file : files) {\n\t\t\t\t\t\tq.add(file);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (f.getName().endsWith(\".markdown\")) {\n\t\t\t\t\t\tarticles.add(f);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (File article : articles) {\n\t\t\t\tString articlePath = article.getAbsolutePath();\n\n\t\t\t\ttry {\n\t\t\t\t\tresetImages(articlePath);\n\t\t\t\t\tnumberImages(articlePath);\n\t\t\t\t}\n\t\t\t\tcatch (IOException ie) {\n\t\t\t\t\tthrow new BuildException(ie.getLocalizedMessage());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setDocDir(String docDir) {\n\t\t_docDir = docDir;\n\t}\n\n\tpublic void setProductType(String productType) {\n\t\t_productType = productType;\n\t}\n\n\tprivate void numberImages(String markdownFilePath) throws IOException {\n\t\tFile markdownfile = new File(markdownFilePath);\n\n\t\tString source = FileUtils.readFileToString(markdownfile);\n\t\tString find = \"\\\\!\\\\[Figure [x|X]:\";\n\t\tint fignum = 1;\n\t\tString replace = \"\\\\!\\\\[Figure \" + fignum + \":\";\n\n\t\t//\n\t\t// Compiles the given regular expression into a pattern\n\t\t//\n\t\tPattern pattern = Pattern.compile(find);\n\n\t\t//\n\t\t// Creates a matcher that will match the given input against the pattern\n\t\t//\n\t\tMatcher matcher = pattern.matcher(source);\n\n\t\t//\n\t\t// Replaces the first subsequence of the input sequence that matches the\n\t\t// pattern with the given replacement string\n\t\t//\n\t\tString output = matcher.replaceFirst(replace);\n\n\t\t//\n\t\t// Loops through file replacing all remaining matching subsequences of\n\t\t// the input sequence with the appropriate figure numbers\n\t\t//\n\t\twhile (matcher.find()) {\n\t\t\tmatcher = pattern.matcher(output);\n\t\t\tfignum++;\n\t\t\treplace = \"\\\\!\\\\[Figure \" + fignum + \":\";\n\t\t\toutput = matcher.replaceFirst(replace);\n\t\t}\n\n\t\t//\n\t\t// Saves the updated file, replaces the original\n\t\t//\n\t\tFileUtils.writeStringToFile(markdownfile, output);\n\t}\n\n\tprivate void resetImages(String markdownFilePath) throws IOException {\n\t\t// Linux example: \"/home/$USER/workspace/01-example-chapter.markdown\"\n\t\t// Windows example: \"E:\\liferay-docs\\discover\\portal\\articles\\01-what-is-liferay\\00-what-is-liferay-intro.markdown\"\n\t\tFile markdownfile = new File(markdownFilePath); // example: \"/home/$USER/workspace/01-example-chapter.markdown\"\n\n\t\tString source = FileUtils.readFileToString(markdownfile);\n\n\t\tString find = \"\\\\!\\\\[Figure \" + \"[0-9]:\";\n\t\tString replace = \"\\\\!\\\\[Figure \" + \"x:\";\n\n\t\tPattern pattern = Pattern.compile(find);\n\t\tMatcher matcher = pattern.matcher(source);\n\t\tString output = matcher.replaceAll(replace);\n\n\t\tfind = \"\\\\!\\\\[Figure \" + \"[0-9][0-9]:\";\n\n\t\tpattern = Pattern.compile(find);\n\t\tmatcher = pattern.matcher(output);\n\t\toutput = matcher.replaceAll(replace);\n\n\t\t//\n\t\t// Saves the updated file, replaces the original\n\t\t//\n\t\tFileUtils.writeStringToFile(markdownfile, output);\n\n\t\t/* This code is for saving the output to a new file so the original is not overwritten; useful for debugging\n\n\t\tString markdownfileoutputpath = args[1];\n\t\tFile markdownfileoutput = new File(markdownfileoutputpath);\n\t\tFileUtils.writeStringToFile(markdownfileoutput, output);\n\n\t\t */\n\t}\n\n\tprivate String _docDir;\n\tprivate String _productType;\n}\n"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/.gitignore",
    "content": "/bundles\n/.gradle\n"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/build.gradle",
    "content": ""
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/configs/common/.touch",
    "content": ""
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/configs/dev/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/configs/local/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/configs/prod/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.cfg",
    "content": "operationMode=REMOTE\ntransportAddresses=127.0.0.1:9300"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/configs/prod/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/configs/uat/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.cfg",
    "content": "operationMode=REMOTE\ntransportAddresses=127.0.0.1:9300\nlogExceptionsOnly=false"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/configs/uat/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Thu Jan 14 10:53:42 CST 2016\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.10-bin.zip\n"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/gradle.properties",
    "content": "#liferay.workspace.bundle.artifact.group=com.liferay\n#liferay.workspace.bundle.artifact.name=portal-tomcat-bundle\nliferay.workspace.bundle.artifact.version=7.0-ce-b6-20160202170907104\n#liferay.workspace.bundle.maven.url=https://liferay-test-01.ci.cloudbees.com/job/liferay-bundle-publishing/lastSuccessfulBuild/artifact/build/m2_repo/\n#liferay.workspace.environment=local\n#liferay.workspace.home.dir=bundles\n#liferay.workspace.modules.default.repository.enabled=false\n#liferay.workspace.modules.dir=modules\n#liferay.workspace.plugins.sdk.dir=plugins-sdk\n#liferay.workspace.themes.dir=themes\n\nmicrosoft.translator.client.id=\nmicrosoft.translator.client.secret=\n"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/modules/.touch",
    "content": ""
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/modules/com.liferay.docs.samplecontent/README.markdown",
    "content": "# Sample Content Portlet\n\nThe Sample Content portlet allows you to create sample content for Liferay. This\nsample content includes users, organizations, and user groups. The specific\nentities that are created are specified in JSON files: `users.json`,\n`organizations.json`, and `user-groups.json`. Here's some sample contents for these\nfiles:\n\n`users.json`:\n\n\t{\"Users\": [\n\t\t{\n\t\t\"Screen Name\": \"jjeffries\",\n\t\t\"First Name\": \"James\",\n\t\t\"Last Name\": \"Jeffries\",\n\t\t\"Male\": true,\n\t\t\"Job Title\": \"Lunar Associate\",\n\t\t\"Email Address\": \"jjeffries@lunarresort.com\",\n\t\t\"User Groups\": [\"List Creators\", \"Message Board Administrators\"],\n\t\t\"Organizations\": [\"Recreation Department\", \"Lunar Golf Instructors\"]\n\t\t},\n\t\t{\n\t\t\"Screen Name\": \"msmart\",\n\t\t\"First Name\": \"Marvin\",\n\t\t\"Last Name\": \"Smart\",\n\t\t\"Male\": true,\n\t\t\"Job Title\": \"Lunar Associate\",\n\t\t\"Email Address\": \"msmart@lunarresort.com\",\n\t\t\"User Groups\": [\"List Creators\", \"Message Board Administrators\"],\n\t\t\"Organizations\": [\"Physical Plant Department\", \"Grounds Crew\"]\n\t\t}\n\t]}\n\n`organizations.json`:\n\n\t{\"Organizations\": [\n\t\t{\n\t\t\"Name\": \"Lunar Resort\",\n\t\t\"Organizations\": [\n\t\t\t{\n\t\t\t\"Name\": \"Physical Plant Department\",\n\t\t\t\"Organizations\": [\n\t\t\t\t{\n\t\t\t\t\"Name\": \"Grounds Crew\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\"Name\": \"Janitorial Crew\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\"Name\": \"Mechanical Crew\"\n\t\t\t\t}\n\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\"Name\": \"Recreation Department\",\n\t\t\t\"Organizations\": [\n\t\t\t\t{\n\t\t\t\t\"Name\": \"Lunar Golf Instructors\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\"Name\": \"Lunar Rover Instructors\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\"Name\": \"Lunar Sherpas\"\n\t\t\t\t}\n\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\"Name\": \"Sales Department\",\n\t\t\t\"Organizations\": [\n\t\t\t\t{\n\t\t\t\t\"Name\": \"Up-sale Group\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\"Name\": \"Souvenir and Memorabilia Group\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\"Name\": \"Retail Group\"\n\t\t\t\t}\n\t\t\t]\n\t\t\t}\n\t\t]\n\t\t}\n\t]}\n\n`user-groups.json`:\n\n\t{\"User Groups\": [\n\t\t{\n\t\t\"Name\": \"List Creators\",\n\t\t\"Description\": \"The List Creators user group can manage dynamic data lists in the Lunar Resort.\"\n\t\t},\n\t\t{\n\t\t\"Name\": \"Message Board Administrators\",\n\t\t\"Description\": \"The Message Board Administrators user group can manage message boards in the Lunar Resort.\"\n\t\t}\n\t]}"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/modules/com.liferay.docs.samplecontent/bnd.bnd",
    "content": "Bundle-Name: Sample Content Portlet\nBundle-SymbolicName: com.liferay.docs.samplecontent\nBundle-Version: 1.0.0.${tstamp}\n\nImport-Package: \\\n\tjavax.portlet;version='[2.0,3)',\\\n\tjavax.servlet;version='[2.5,4)',\\\n\t*\n\nPrivate-Package: \\\n\tcom.liferay.docs.samplecontent.portlet"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/modules/com.liferay.docs.samplecontent/build.gradle",
    "content": "dependencies {\n\tcompile 'com.liferay.portal:com.liferay.portal.kernel:2.0.0'\n\tcompile 'javax.portlet:portlet-api:2.0'\n\tcompile 'javax.servlet:servlet-api:2.5'\n\tcompile 'org.osgi:org.osgi.core:6.0.0'\n\tcompile 'org.osgi:org.osgi.compendium:5.0.0'\n}\n\nversion = '1.0.0'\n"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/modules/com.liferay.docs.samplecontent/src/main/java/com/liferay/docs/samplecontent/portlet/SampleContentPortlet.java",
    "content": "package com.liferay.docs.samplecontent.portlet;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.util.Calendar;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Queue;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSONArray;\nimport com.liferay.portal.kernel.json.JSONException;\nimport com.liferay.portal.kernel.json.JSONFactoryUtil;\nimport com.liferay.portal.kernel.json.JSONObject;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.upload.UploadPortletRequest;\nimport com.liferay.portal.kernel.util.LocaleUtil;\nimport com.liferay.portal.kernel.util.StringPool;\nimport com.liferay.portal.kernel.model.Organization;\nimport com.liferay.portal.kernel.model.Role;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.UserGroup;\nimport com.liferay.portal.kernel.service.OrganizationLocalService;\nimport com.liferay.portal.kernel.service.RoleLocalService;\nimport com.liferay.portal.kernel.service.UserGroupLocalService;\nimport com.liferay.portal.kernel.service.UserLocalService;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\n@Component(\n\timmediate = true,\n\tproperty = {\n\t\t\"com.liferay.portlet.display-category=category.sample\",\n\t\t\"com.liferay.portlet.instanceable=true\",\n\t\t\"javax.portlet.display-name=Sample Content Portlet\",\n\t\t\"javax.portlet.security-role-ref=power-user,user\",\n\t\t\"javax.portlet.init-param.template-path=/\",\n\t\t\"javax.portlet.init-param.view-template=/view.jsp\",\n\t\t\"javax.portlet.resource-bundle=content.Language\"\n\t},\n\tservice = Portlet.class\n)\npublic class SampleContentPortlet extends MVCPortlet {\n\t\n\tpublic void addUsers(ActionRequest request, ActionResponse response) {\n\t\tlong companyId = PortalUtil.getDefaultCompanyId();\n\t\tRole adminRole = null;\n\t\ttry {\n\t\t\tadminRole = _roleLocalService.getRole(companyId, \"Administrator\");\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\t_log.error(pe);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t\tList<User> adminUsers = _userLocalService.getRoleUsers(adminRole.getRoleId());\n\t\tlong adminUserId = adminUsers.get(0).getUserId();\n\n\t\tUploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(request);\t\n\t\t\n\t\tString fileName = \"usersFile\";\n\t\tFile file = uploadRequest.getFile(fileName);\t\n\t\t\n\t\tStringBuilder out = new StringBuilder();\n\t\tBufferedReader br = null;\n\t\ttry {\n\t\t\tbr = new BufferedReader(new FileReader(file));\n\t\t\twhile (br.ready()){\n\t\t\t\tout.append(br.readLine());\n\t\t\t}\n\t\t\tbr.close();\t\n\t\t}\n\t\tcatch (IOException fnfe) {\n\t\t\t_log.error(fnfe);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t\tString jsonString = out.toString();\n\t\t\n\t\tJSONObject jsonObject = null;\n\t\ttry {\n\t\t\tjsonObject = JSONFactoryUtil.createJSONObject(jsonString);\n\t\t}\n\t\tcatch (JSONException je) {\n\t\t\t_log.error(je);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\n\t\tJSONArray users = jsonObject.getJSONArray(\"Users\");\n\t\tfor (int i = 0; i < users.length(); i++) {\n\t\t\tString screenName = users.getJSONObject(i).getString(\"Screen Name\");\n\t\t\tString firstName = users.getJSONObject(i).getString(\"First Name\");\n\t\t\tString lastName = users.getJSONObject(i).getString(\"Last Name\");\n\t\t\tboolean male = users.getJSONObject(i).getBoolean(\"Male\");\n\t\t\tString jobTitle = users.getJSONObject(i).getString(\"Job Title\");\n\t\t\tString emailAddress = users.getJSONObject(i).getString(\"Email Address\");\n\n\t\t\tJSONArray userGroups = users.getJSONObject(i).getJSONArray(\"User Groups\");\n\t\t\tint userGroupsLength = userGroups.length();\n\t\t\tlong[] userGroupIds = new long[userGroupsLength];\n\t\t\tfor (int j = 0; j < userGroupsLength; j++) {\n\t\t\t\tString userGroupName = userGroups.getString(j);\n\t\t\t\tUserGroup userGroup = null;\n\t\t\t\ttry {\n\t\t\t\t\tuserGroup = _userGroupLocalService.getUserGroup(companyId, userGroupName);\n\t\t\t\t}\n\t\t\t\tcatch (PortalException pe) {\n\t\t\t\t\t_log.error(pe);\n\t\t\t\t\t\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlong userGroupId = userGroup.getUserGroupId();\n\t\t\t\tuserGroupIds[j] = userGroupId;\n\t\t\t}\n\n\t\t\tJSONArray organizations = users.getJSONObject(i).getJSONArray(\"Organizations\");\n\t\t\tint organizationsLength = organizations.length();\n\t\t\tlong[] organizationIds = new long[organizationsLength];\n\t\t\tfor (int j = 0; j < organizationsLength; j++) {\n\t\t\t\tString organizationName = organizations.getString(j);\n\t\t\t\tOrganization organization = null;\n\t\t\t\ttry {\n\t\t\t\t\torganization = _organizationLocalService.getOrganization(companyId, organizationName);\n\t\t\t\t}\n\t\t\t\tcatch (PortalException pe) {\n\t\t\t\t\t_log.error(pe);\n\t\t\t\t\t\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlong organizationId = organization.getOrganizationId();\n\t\t\t\torganizationIds[j] = organizationId;\n\t\t\t}\n\t\t\t\n\t\t\ttry {\n\t\t\t\t_userLocalService.addUser(adminUserId, companyId, false, \"liferay\", \"liferay\", false, screenName, emailAddress, 0L, StringPool.BLANK, LocaleUtil.getDefault(), firstName, StringPool.BLANK, lastName, 0L, 0L, male, Calendar.JANUARY, 1, 1970, jobTitle, new long[0], organizationIds, new long[0], userGroupIds, false, null);\n\t\t\t}\n\t\t\tcatch (PortalException pe) {\n\t\t\t\t_log.error(pe);\n\t\t\t\t\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void addOrganizations(ActionRequest request, ActionResponse response) {\n\t\tlong companyId = PortalUtil.getDefaultCompanyId();\n\t\tRole adminRole = null;\n\t\ttry {\n\t\t\tadminRole = _roleLocalService.getRole(companyId, \"Administrator\");\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\t_log.error(pe);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t\tList<User> adminUsers = _userLocalService.getRoleUsers(adminRole.getRoleId());\n\t\tlong adminUserId = adminUsers.get(0).getUserId();\n\n\t\tUploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(request);\t\n\t\t\n\t\tString fileName = \"organizationsFile\";\n\t\tFile file = uploadRequest.getFile(fileName);\t\n\t\t\n\t\tStringBuilder out = new StringBuilder();\n\t\tBufferedReader br = null;\n\t\ttry {\n\t\t\tbr = new BufferedReader(new FileReader(file));\n\t\t\twhile (br.ready()){\n\t\t\t\tout.append(br.readLine());\n\t\t\t}\n\t\t\tbr.close();\t\n\t\t}\n\t\tcatch (IOException fnfe) {\n\t\t\t_log.error(fnfe);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t\tString jsonString = out.toString();\n\t\t\n\t\tJSONObject jsonObject = null;\n\t\ttry {\n\t\t\tjsonObject = JSONFactoryUtil.createJSONObject(jsonString);\n\t\t}\n\t\tcatch (JSONException je) {\n\t\t\t_log.error(je);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\n\t\tJSONArray orgs = jsonObject.getJSONArray(\"Organizations\");\n\n\t\tQueue<JSONOrgWrapper> queue = new LinkedList<JSONOrgWrapper>();\n\t\tfor (int i = 0; i < orgs.length(); i++) {\n\t\t\tJSONOrgWrapper jsonOrgWrapper = new JSONOrgWrapper(orgs.getJSONObject(i), StringPool.BLANK);\n\n\t\t\tqueue.add(jsonOrgWrapper);\n\t\t}\n\t\t\n\t\twhile (!queue.isEmpty()) {\n\t\t\tJSONOrgWrapper jsonOrgWrapper = queue.remove();\n\t\t\tString orgName = jsonOrgWrapper.getJSONObject().getString(\"Name\");\n\t\t\tJSONArray subOrgs = jsonOrgWrapper.getJSONObject().getJSONArray(\"Organizations\");\n\t\t\t\n\t\t\tString parentOrgName = jsonOrgWrapper.getParentOrgName();\n\t\t\tif (StringPool.BLANK.equals(parentOrgName)) {\n\t\t\t\ttry {\n\t\t\t\t\t_organizationLocalService.addOrganization(adminUserId, 0L, orgName, true);\n\t\t\t\t}\n\t\t\t\tcatch (PortalException pe) {\n\t\t\t\t\t_log.error(pe);\n\t\t\t\t\t\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlong parentOrgId = _organizationLocalService.getOrganizationId(companyId, parentOrgName);\n\t\t\t\ttry {\n\t\t\t\t\t_organizationLocalService.addOrganization(adminUserId, parentOrgId, orgName, true);\n\t\t\t\t}\n\t\t\t\tcatch (PortalException pe) {\n\t\t\t\t\t_log.error(pe);\n\t\t\t\t\t\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (subOrgs == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\tfor (int i = 0; i < subOrgs.length(); i++) {\n\t\t\t\tJSONOrgWrapper jsonSubOrgWrapper = new JSONOrgWrapper(subOrgs.getJSONObject(i), orgName);\n\t\t\t\tqueue.add(jsonSubOrgWrapper);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void addUserGroups(ActionRequest request, ActionResponse response) {\n\t\tlong companyId = PortalUtil.getDefaultCompanyId();\n\t\tRole adminRole = null;\n\t\ttry {\n\t\t\tadminRole = _roleLocalService.getRole(companyId, \"Administrator\");\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\t_log.error(pe);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t\tList<User> adminUsers = _userLocalService.getRoleUsers(adminRole.getRoleId());\n\t\tlong adminUserId = adminUsers.get(0).getUserId();\n\n\t\tUploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(request);\t\n\t\t\n\t\tString fileName = \"userGroupsFile\";\n\t\tFile file = uploadRequest.getFile(fileName);\t\n\t\t\n\t\tStringBuilder out = new StringBuilder();\n\t\tBufferedReader br = null;\n\t\ttry {\n\t\t\tbr = new BufferedReader(new FileReader(file));\n\t\t\twhile (br.ready()){\n\t\t\t\tout.append(br.readLine());\n\t\t\t}\n\t\t\tbr.close();\t\n\t\t}\n\t\tcatch (IOException fnfe) {\n\t\t\t_log.error(fnfe);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t\tString jsonString = out.toString();\n\t\t\n\t\tJSONObject jsonObject = null;\n\t\ttry {\n\t\t\tjsonObject = JSONFactoryUtil.createJSONObject(jsonString);\n\t\t}\n\t\tcatch (JSONException je) {\n\t\t\t_log.error(je);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\n\t\tJSONArray userGroups = jsonObject.getJSONArray(\"User Groups\");\n\t\tfor (int i = 0; i < userGroups.length(); i++) {\n\t\t\tString name = userGroups.getJSONObject(i).getString(\"Name\");\n\t\t\tString description = userGroups.getJSONObject(i).getString(\"Description\");\n\n\t\t\ttry {\n\t\t\t\t_userGroupLocalService.addUserGroup(adminUserId, companyId, name, description, null);\n\t\t\t}\n\t\t\tcatch (PortalException pe) {\n\t\t\t\t_log.error(pe);\n\t\t\t\t\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic UserLocalService getUserLocalService() {\n\t\treturn _userLocalService;\n\t}\n\n\tpublic OrganizationLocalService getOrganizationLocalService() {\n\t\treturn _organizationLocalService;\n\t}\n\n\tpublic UserGroupLocalService getUserGroupLocalService() {\n\t\treturn _userGroupLocalService;\n\t}\n\n\tpublic RoleLocalService getRoleLocalService() {\n\t\treturn _roleLocalService;\n\t}\n\n\t@Reference\n\tpublic void setUserLocalService(UserLocalService userLocalService) {\n\t    _userLocalService = userLocalService;\n\t}\n\n\t@Reference\n\tpublic void setOrganizationLocalService(OrganizationLocalService organizationLocalService) {\n\t    _organizationLocalService = organizationLocalService;\n\t}\n\n\t@Reference\n\tpublic void setUserGroupLocalService(UserGroupLocalService userGroupLocalService) {\n\t    _userGroupLocalService = userGroupLocalService;\n\t}\n\n\t@Reference\n\tpublic void setRoleLocalService(RoleLocalService roleLocalService) {\n\t    _roleLocalService = roleLocalService;\n\t}\n\t\n\tprivate static final Log _log = LogFactoryUtil.getLog(SampleContentPortlet.class);\n\t\n\tprivate UserLocalService _userLocalService;\n\tprivate OrganizationLocalService _organizationLocalService;\n\tprivate UserGroupLocalService _userGroupLocalService;\n\tprivate RoleLocalService _roleLocalService;\n\n\tprivate class JSONOrgWrapper {\n\t\t\n\t\tpublic JSONOrgWrapper(JSONObject jsonObject, String parentOrgName) {\n\t\t\t_jsonObject = jsonObject;\n\t\t\t_parentOrgName = parentOrgName;\n\t\t}\n\t\t\n\t\tpublic JSONObject getJSONObject() {\n\t\t\treturn _jsonObject;\n\t\t}\n\t\t\n\t\tpublic String getParentOrgName() {\n\t\t\treturn _parentOrgName;\n\t\t}\n\t\t\n\t\tprivate JSONObject _jsonObject;\n\t\tprivate String _parentOrgName;\n\t\t\n\t}\n\n}\n"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/modules/com.liferay.docs.samplecontent/src/main/resources/META-INF/resources/init.jsp",
    "content": "<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\" %>\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\" %>\n\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\" %>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\" %>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\" %>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\" %>\n\n<%@ page import=\"com.liferay.portal.kernel.util.GetterUtil\" %>\n\n<portlet:defineObjects />\n\n<liferay-theme:defineObjects />\n"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/modules/com.liferay.docs.samplecontent/src/main/resources/META-INF/resources/view.jsp",
    "content": "<%@ include file=\"init.jsp\" %>\n\n<portlet:actionURL name=\"addUsers\" var=\"addUsersURL\"></portlet:actionURL>\n<portlet:actionURL name=\"addOrganizations\" var=\"addOrganizationsURL\"></portlet:actionURL>\n<portlet:actionURL name=\"addUserGroups\" var=\"addUserGroupsURL\"></portlet:actionURL>\n\n<liferay-ui:tabs names=\"users,organizations,user-groups\" refresh=\"false\" tabsValues=\"users,organizations,user-groups\">\n    <liferay-ui:section>\n\t\t<p>If your users JSON file specifies organization and user group\n\t\tassociations, make sure to add the organizations and user groups before\n\t\tadding the users.</p>\n\t\t<aui:form action=\"<%= addUsersURL %>\" name=\"<portlet:namespace />addUsersForm\" enctype=\"multipart/form-data\" method=\"post\">\n\t\t\t<aui:input name=\"usersFile\" label=\"Select Users JSON File\" type=\"file\" />\n\t\t\t\n\t\t\t<aui:button name=\"addUsers\" value=\"Add Users\" type=\"submit\" />\n\t\t</aui:form>\n    </liferay-ui:section>\n    <liferay-ui:section>\n\t\t<aui:form action=\"<%= addOrganizationsURL %>\" name=\"<portlet:namespace />addOrganizationsForm\" enctype=\"multipart/form-data\" method=\"post\">\n\t\t\t<aui:input name=\"organizationsFile\" label=\"Select Organizations JSON File\" type=\"file\" />\n\t\t\t\n\t\t\t<aui:button name=\"addOrganizations\" value=\"Add Organizations\" type=\"submit\" />\n\t\t</aui:form>\n    </liferay-ui:section>\n    <liferay-ui:section>\n\t\t<aui:form action=\"<%= addUserGroupsURL %>\" name=\"<portlet:namespace />addUserGroupsForm\" enctype=\"multipart/form-data\" method=\"post\">\n\t\t\t<aui:input name=\"userGroupsFile\" label=\"Select User Groups JSON File\" type=\"file\" />\n\t\t\t\n\t\t\t<aui:button name=\"addUserGroups\" value=\"Add User Groups\" type=\"submit\" />\n\t\t</aui:form>\n    </liferay-ui:section>\n</liferay-ui:tabs>"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/modules/com.liferay.docs.samplecontent/src/main/resources/content/Language.properties",
    "content": "javax.portlet.title.com_liferay_docs_samplecontent_SampleContentPortlet=com.liferay.docs.samplecontent JSP Portlet\njavax.portlet.display-name.com_liferay_docs_samplecontent_SampleContentPortlet=com.liferay.docs.samplecontent JSP\njavax.portlet.short-title.com_liferay_docs_samplecontent_SampleContentPortlet=com.liferay.docs.samplecontent JSP\njavax.portlet.keywords.com_liferay_docs_samplecontent_SampleContentPortlet=com.liferay.docs.samplecontent,jsp\ncom_liferay_docs_samplecontent_SampleContentPortlet.caption=Hello from com.liferay.docs.samplecontent JSP!"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/modules/com.liferay.docs.samplecontent/src/main/resources/organizations.json",
    "content": "{\"Organizations\": [\n\t{\n\t\"Name\": \"Lunar Resort\",\n\t\"Organizations\": [\n\t\t{\n\t\t\"Name\": \"Physical Plant Department\",\n\t\t\"Organizations\": [\n\t\t\t{\n\t\t\t\"Name\": \"Grounds Crew\"\n\t\t\t},\n\t\t\t{\n\t\t\t\"Name\": \"Janitorial Crew\"\n\t\t\t},\n\t\t\t{\n\t\t\t\"Name\": \"Mechanical Crew\"\n\t\t\t}\n\t\t]\n\t\t},\n\t\t{\n\t\t\"Name\": \"Recreation Department\",\n\t\t\"Organizations\": [\n\t\t\t{\n\t\t\t\"Name\": \"Lunar Golf Instructors\"\n\t\t\t},\n\t\t\t{\n\t\t\t\"Name\": \"Lunar Rover Instructors\"\n\t\t\t},\n\t\t\t{\n\t\t\t\"Name\": \"Lunar Sherpas\"\n\t\t\t}\n\t\t]\n\t\t},\n\t\t{\n\t\t\"Name\": \"Sales Department\",\n\t\t\"Organizations\": [\n\t\t\t{\n\t\t\t\"Name\": \"Up-sale Group\"\n\t\t\t},\n\t\t\t{\n\t\t\t\"Name\": \"Souvenir and Memorabilia Group\"\n\t\t\t},\n\t\t\t{\n\t\t\t\"Name\": \"Retail Group\"\n\t\t\t}\n\t\t]\n\t\t}\n\t]\n\t}\n]}"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/modules/com.liferay.docs.samplecontent/src/main/resources/user-groups.json",
    "content": "{\"User Groups\": [\n\t{\n\t\"Name\": \"List Creators\",\n\t\"Description\": \"The List Creators user group can manage dynamic data lists in the Lunar Resort.\"\n\t},\n\t{\n\t\"Name\": \"Message Board Administrators\",\n\t\"Description\": \"The Message Board Administrators user group can manage message boards in the Lunar Resort.\"\n\t}\n]}"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/modules/com.liferay.docs.samplecontent/src/main/resources/users.json",
    "content": "{\"Users\": [\n\t{\n\t\"Screen Name\": \"jjeffries\",\n\t\"First Name\": \"James\",\n\t\"Last Name\": \"Jeffries\",\n\t\"Male\": true,\n\t\"Job Title\": \"Lunar Associate\",\n\t\"Email Address\": \"jjeffries@lunarresort.com\",\n\t\"User Groups\": [\"List Creators\", \"Message Board Administrators\"],\n\t\"Organizations\": [\"Recreation Department\", \"Lunar Golf Instructors\"]\n\t},\n\t{\n\t\"Screen Name\": \"msmart\",\n\t\"First Name\": \"Marvin\",\n\t\"Last Name\": \"Smart\",\n\t\"Male\": true,\n\t\"Job Title\": \"Lunar Associate\",\n\t\"Email Address\": \"msmart@lunarresort.com\",\n\t\"User Groups\": [\"List Creators\", \"Message Board Administrators\"],\n\t\"Organizations\": [\"Physical Plant Department\", \"Grounds Crew\"]\n\t}\n]}"
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/settings.gradle",
    "content": "buildscript {\n\tdependencies {\n\t\tclasspath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.workspace\", version: \"1.0.12\"\n\t}\n\n\trepositories {\n\t\tmaven {\n\t\t\turl \"http://cdn.repository.liferay.com/nexus/content/groups/public\"\n\t\t}\n\t}\n}\n\napply plugin: \"com.liferay.workspace\""
  },
  {
    "path": "code/sample-content-portlet-lr-workspace/themes/.touch",
    "content": ""
  },
  {
    "path": "copyright.txt",
    "content": "<!-- \nCopyright (c) 2017 Liferay, Inc. All rights reserved. \n\nCreative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA-3.0)\n\nThis is a human-readable summary of the Legal Code (the full license): \nhttp://creativecommons.org/licenses/by-sa/3.0/legalcode\n\nYou are free: \n    - to Share--to copy, distribute and transmit the work\n    - to Remix--to adapt the work\n    - to make commercial use of the work\n\nUnder the following conditions: \n    - Attribution--You must attribute the work in the manner specified \n      by the author or licensor (but not in any way that suggests that \n      they endorse you or your use of the work). \n    - Share Alike--If you alter, transform, or build upon this work, \n      you may distribute the resulting work only under the same or \n      similar license to this one. \n      \nWith the understanding that: \n    - Waiver--Any of the above conditions can be waived if you get \n      permission from the copyright holder. \n    - Public Domain--Where the work or any of its elements is in the \n      public domain under applicable law, that status is in no way \n      affected by the license. \n    - Other Rights--In no way are any of the following rights affected \n      by the license: \n      \n          * Your fair dealing or fair use rights, or other applicable \n            copyright exceptions and limitations; \n          * The author's moral rights; \n          * Rights other persons may have either in the work itself or \n            in how the work is used, such as publicity or privacy rights. \n            \n    - Notice--For any reuse or distribution, you must make clear to \n      others the license terms of this work. The best way to do this is \n      with a link to this web page: \n      http://creativecommons.org/licenses/by-sa/3.0\n-->\n"
  },
  {
    "path": "en/build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"Language Folder - English\" basedir=\".\">\n\t<property name=\"language.dir\" value=\"en\" />\n\n\t<import file=\"../build-common.xml\" />\n\n</project>"
  },
  {
    "path": "en/deployment/articles/01-deploying-liferay/01-deploying-liferay-intro.markdown",
    "content": "---\nheader-id: deploying-product\n---\n\n# Deploying @product@\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n@product@ is one of the most flexible applications on the market today with\nrespect to database and application server support. It supports a wide variety\nof databases and application servers, freeing you to use the ones you want.\n@product@ also scales very well. You can install it on a shared hosting account,\non a multi-node cluster running a commercial application server, or on anything\nin between. In fact, @product@ is used successfully in all of these scenarios\nevery day. \n\nYou'll find that because of @product@'s flexibility in its deployment options,\nit is also easy to install. If you already have an application server, you can\nuse your application server's deployment tools to install @product@. If you\ndon't already have an application server, Liferay provides several application\nserver bundles from which to choose. These are pre-configured application\nservers with @product@ already installed on them. With a small amount of\nconfiguration, these can be made into production-ready systems.\n\nThere are some preparations to make before installing. You must create\na database and install a supported Java Development Kit (JDK). It can also be\nworthwhile to pre-configure or gather information for configuring a data source,\nmail session, and more. You'll get guidance for these preparations. \n\nLastly, you'll install and deploy @product@ for the first time and then set up\nMarketplace. You can continue configuring and tuning as you desire. \n\nRead on to obtain the @product@ installer that's right for you. \n"
  },
  {
    "path": "en/deployment/articles/01-deploying-liferay/02-obtaining-liferay.markdown",
    "content": "---\nheader-id: obtaining-product\n---\n\n# Obtaining @product@\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nBefore you begin, you should answer a few questions. \n\n-   Which version of @product@ will you install?\n-   Is your installation for development purposes or are you preparing to run in\n    production?\n-   Are you installing @product@ in a clustered environment? \n-   Which application server do you want @product@ to run on?\n-   Are you installing on an existing application server? \n\nHere you'll determine the installation that's best for you and download it. \n\nAnyone can download @product@ from\n[liferay.com](https://www.liferay.com).\nClicking *Resources &rarr; Community Downloads* takes you to the\n[Community Downloads page](https://www.liferay.com/downloads-community),\nwhere you can access Liferay Portal CE or a trial of the enterprise supported\nLiferay DXP. The installers are available in several different formats. The\nformats include a Liferay Tomcat bundle (@product@ bundled with Tomcat) as well\nas a @product@ `.war` (WAR) file for installing @product@ on an existing\napplication server of choice. \n\nLiferay enterprise subscribers can download Liferay DXP from the\n[Help Center](https://help.liferay.com/hc). \n\n@product@ runs on a wide variety of application servers (Check the\n[Support page](https://help.liferay.com/hc/categories/360000894391-Product-Support)\nfor a complete listing). Here are the ways to install @product@:\n\n-   [Install a Liferay Tomcat bundle](#liferay-tomcat-bundle)\n    (Tomcat application server with @product@ pre-installed).\n\n-   [Install the Liferay WAR](#downloading-the-liferay-war-and-dependency-jars)\n    (and supporting libraries) onto an existing application server.\n\nSince the Liferay Tomcat bundle is the easiest way, it's described first. \n\n## Liferay Tomcat Bundle\n\nThe Liferay Tomcat bundle includes the Tomcat application server with @product@\npre-installed. If you prefer using another application server with @product@,\nyou must install it manually. If you don't currently have an application server\npreference, consider starting with the Tomcat bundle. Tomcat is one of the most\nlightweight and straightforward bundles to configure.\n\n| **Note:** Application server bundles for proprietary application servers such\n| as WebLogic or WebSphere aren't available because the licenses for these\n| servers don't allow for redistribution. @product@'s commercial offering,\n| however, runs just as well on these application servers as it does on the\n| others. \n\nBundles are released as 7-Zip (`.7z`) and gzipped (`.tar.gz`) compressed file\narchive formats. \n[Installing @product@](/docs/7-2/deploy/-/knowledge_base/d/installing-product)\ndemonstrates installing @product@ from a bundle. Follow its instructions once\nyou've prepared for installing @product@ (see the next article). \n\n@product@ bundles might not be appropriate for you. Here are some reasons for\ninstalling the @product@ WAR manually instead of using a bundle. \n\n-   There is no @product@ bundle for the application server you want to use. \n\n-   You're installing @product@ in a clustered environment. \n\n-   You're installing to an existing application server.\n\nManual installation is described next. \n\n## Downloading the Liferay WAR and Dependency JARs\n\nManual installation requires installing the @product@ WAR and dependency JARs\nonto the application server. These files are available to download for\n[DXP](https://customer.liferay.com/downloads) or [Portal\nCE](https://www.liferay.com/downloads-community):\n\n-   @product@ WAR file\n-   Dependencies ZIP file\n-   OSGi Dependencies ZIP file\n\nAfter preparing for install (next), follow the @product@ installation steps for\nyour application server. There are specific configuration and script changes\nrequired on each application. The installation article titles follow this \nformat, with *[Application Server]* replaced by the application server name. \n\n*Installing @product@ on [Application Server]*\n"
  },
  {
    "path": "en/deployment/articles/01-deploying-liferay/03-preparing-for-install.markdown",
    "content": "---\nheader-id: preparing-for-install\n---\n\n# Preparing for Install\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay/configuring-a-database.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n@product@ doesn't require much to deploy. You need a Java Development Kit (JDK)\nand a database. Several configuration topics (e.g.,\n[search engine integration](/docs/7-2/deploy/-/knowledge_base/d/installing-a-search-engine),\n[document repository configuration](/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration),\n[security management](/docs/7-2/deploy/-/knowledge_base/d/securing-product),\n[clustering](/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering),\nand more) can be addressed *after* deploying @product@. \n\n| **Note:** If you are installing @product@ to multiple machines (e.g., in a \n| [cluster](/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering))\n| or prefer centralizing configuration in a file, using portal properties in a\n| [`[LIFERAY_HOME]/portal-ext.properties` file](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\n| is the recommended way to configure. The install preparation topics here and\n| the configuration topics throughout this guide demonstrate using applicable\n| portal properties. \n\n| **Note:** `LIFERAY_HOME` is the location from which @product@ launches \n| applications, applies configurations, loads JAR files, and generates logs.\n| Liferay Home is customizable and can differ between application servers. The\n| [Liferay Home reference](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\n| describes its folder structure. \n\nStart preparing for @product@ install by installing a supported Java\nDevelopment Kit. \n\n## JDK Requirements\n\n@product@ deployment requires a JDK. The \n[Support page](https://help.liferay.com/hc/categories/360000894391-Product-Support)\nlists the supported JDKs from various vendors. You must use one of these JDK\nversions:\n\n- JDK 8\n- JDK 11\n\nJDK 11 is backwards compatible with JDK 8 applications. Applications and\ncustomizations developed on JDK 8 run on JDK 8 or JDK 11 runtimes. This makes\nJDK 8 best for developing on @product-ver@. \n\n## JVM Requirements\n\n@product@ requires that the application server JVM use the GMT time zone and\nUTF-8 file encoding. Include these JVM arguments to set the required values. \n\n```properties\n-Dfile.encoding=UTF8 -Duser.timezone=GMT\n```\n\nOn JDK 11, it's recommended to add this JVM argument to display four-digit years.\n\n```properties\n-Djava.locale.providers=JRE,COMPAT,CLDR\n```\n\n| **Note:** Since JDK 9, the Unicode Common Locale Data Repository (CLDR) is the\n| default locales provider. CLDR, however, is not providing years in a\n| four-digit format (see\n| [LPS-87191](https://issues.liferay.com/browse/LPS-87191)).\n| The setting `java.locale.providers=JRE,COMPAT,CLDR` works around this issue by\n| using JDK 8's default locales provider. \n\nThe recommended maximum heap size is 2GB. Setting the minimum heap size to the\nmaximum heap size value minimizes garbage collections. \n\n```properties \n-Xms2560m -Xmx2560m\n```\n\nIf you're using JDK 11, you may see *Illegal Access* warnings like these:\n\n```\nWARNING: An illegal reflective access operation has occurred\nWARNING: Illegal reflective access by com.liferay.petra.reflect.ReflectionUtil (file:/Users/malei/dev/project/bundles/master-bundles/tomcat-9.0.10/lib/ext/com.liferay.petra.reflect.jar) to field java.lang.reflect.Field.modifiers\nWARNING: Please consider reporting this to the maintainers of com.liferay.petra.reflect.ReflectionUtil\nWARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations\nWARNING: All illegal access operations will be denied in a future release\n```\n\nThis is a known issue: [LPS-87421](https://issues.liferay.com/browse/LPS-87421). As a workaround, you can eliminate these warnings by adding these properties after your application server JVM options:\n\n```properties\n --add-opens=java.base/java.io=ALL-UNNAMED \\\n --add-opens=java.base/java.lang.reflect=ALL-UNNAMED \\\n --add-opens=java.base/java.lang=ALL-UNNAMED \\\n --add-opens=java.base/java.net=ALL-UNNAMED \\\n --add-opens=java.base/java.nio=ALL-UNNAMED \\\n --add-opens=java.base/java.text=ALL-UNNAMED \\\n --add-opens=java.base/java.util=ALL-UNNAMED \\\n --add-opens=java.base/sun.nio.ch=ALL-UNNAMED \\\n --add-opens=java.desktop/java.awt.font=ALL-UNNAMED \\\n --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED \\\n --add-opens=java.xml/com.sun.org.apache.xerces.internal.parsers=ALL-UNNAMED\n```\n\nIf you're using JDK 11 on Linux or UNIX and are activating @product@ using an LCS 5.0.0 client, you may see an error like this:\n\n```\nERROR [LCS Worker 2][BaseScheduledTask:92] java.lang.reflect.InaccessibleObjectException: Unable to make public long com.sun.management.internal.OperatingSystemImpl.getOpenFileDescriptorCount() accessible: module jdk.management does not\n \"opens com.sun.management.internal\" to unnamed module @1a3325e5\njava.lang.reflect.InaccessibleObjectException: Unable to make public long com.sun.management.internal.OperatingSystemImpl.getOpenFileDescriptorCount() accessible: module jdk.management does not \"opens com.sun.management.internal\" to unnamed module @1a3325e5\nat java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:\nat java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:\nat java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:198)\nat java.base/java.lang.reflect.Method.setAccessible(Method.java:192)\n```\n\nTo workaround this issue, add this property after your application server JVM options:\n\n```properties\n --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED\n```\n\nIt's time to prepare your database. \n\n## Preparing a Database\n\nThe recommended way to set up your @product@ database is also the simplest.\n@product@ takes care of just about everything. Here are the steps:\n\n1.  Create a blank database encoded with the character set UTF-8. @product@ is a\n    multilingual application and needs UTF-8 encoding to display all of its\n    supported character sets.\n\n    | **Note:** If you plan to migrate from one database vendor to another,\n    | [configure the database to use the default query result order you expect for entities @product@ lists](/docs/7-2/deploy/-/knowledge_base/d/sort-order-changed-with-a-different-database). \n\n2.  Create a database user for accessing this database. Grant this database user\n    all rights, including the rights to create and drop tables, to the blank\n    @product@ database.\n\n@product@ uses this database user's credentials to connect to the database\neither\n[directly](#using-the-built-in-data-source)\nor\n[through its application server](#using-a-data-source-on-your-application-server). \nAfter you've configured the database connection, @product@ creates its tables in\nthe database automatically, complete with indexes.\n\nThis is the recommended way to set up @product@. It enables @product@ to\nmaintain its database automatically during upgrades or when various @product@\nplugins that create database tables of their own are installed. This method is\nby far the best way to set up your database. \n\n| **Warning:** If you're using an Oracle database, use the `ojdbc8.jar` driver \n| library with at least Oracle 12.2.0.1.0 JDBC 4.2 versioning because\n| [data truncation issues](https://issues.liferay.com/browse/LPS-79229)\n| have been detected reading data from CLOB columns. \n\nYou can connect @product@ with your database using @product@'s built-in data\nsource (recommended) or using a data source you create on your app server. \n\n### Using the Built-in Data Source\n\nYou can configure the built-in data source from the\n[Basic Configuration page](/docs/7-2/deploy/-/knowledge_base/d/installing-product#using-the-setup-wizard)\n(available when @product@ starts up the first time) or by specifying it using \nportal properties. \n\nHere's how set it using portal properties:\n\n1.  Create a \n    [`portal-ext.properties` file](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\n    if you haven't created one already. \n\n2.  Copy a set of `jdbc.*` properties from one of the\n    [JDBC templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates)\n    into your `portal-ext.properties` file.\n\n3.  Modify the `jdbc.*` property values to specify your database and database \n    user credentials. \n\n4.  Put the `portal-ext.properties` file into your\n    [LIFERAY_HOME](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\n    once you've established it based on your installation. \n\n@product@ connects to the data source on startup. \n\nAs an alternative to the built-in data source, you can use your application\nserver's data source. \n\n### Using a Data Source on Your Application Server\n\nHere's how to use your application server's data source: \n\n1.  Create your data source based on the instructions in the *Installing \n    @product@ on [Application Server]* article (for your application server)\n    and your application server's documentation.\n\n2.  Create a\n    [`portal-ext.properties` file](/docs/7-2/deploy/-/knowledge_base/d/portal-properties),\n    if you haven't created one already. \n\n3.  Add the `jdbc.default.jndi.name` property set to the data source's JNDI \n    name. Here's an example:\n\n    ```properties\n    jdbc.default.jndi.name=jdbc/LiferayPool\n    ```\n\n4.  Put the `portal-ext.properties` file into your\n    [LIFERAY_HOME](/docs/7-2/deploy/-/knowledge_base/d/liferay-home),\n    once you've established your LIFERAY_HOME based on your installation. \n\n@product@ connects to your data source on startup.\n\nAllowing the database user you're using to initialize the @product@ database to\ncontinue with all database rights is recommended. If you're fine with that user\nhaving the recommended permissions, skip the next section on limiting\ndatabase access. \n\n### Limiting Database Access\n\n| **Warning:** The instructions below are not ideal for @product@ installations\n| The following procedure is documented so that enterprises with more\n| restrictive standards can install @product@ with stricter (but sub-optimal)\n| database settings. If it's at all possible, allow the database user that\n| initializes the database to continue using the database with the same\n| recommended permissions. The start of this section\n| ([Database Preparation](#preparing-a-database)) \n| describes the recommended procedure for initializing the @product@ database\n| and preserving that user's permissions for maintaining the @product@ database\n| and updating the database as plugin installations and plugin updates require. \n\nEven though it's recommended for @product@ to use the same database user to\ncreate and maintain its database automatically, your organizations might insist\non revoking database initialization and maintenance permissions from that user\nonce the database is initialized. If permissions for Select, Insert, Update and\nDelete operations are the only ones you allow for that user, you must initialize\nand maintain the database manually (even though it's not recommended). Here is\nthe manual procedure: \n\n1.  Create a new, blank, database for @product@.\n\n2.  Grant full rights for the @product@ database user to do anything to the \n    database. \n\n3.  Install @product@ and start it so that it automatically populates the\n    database.\n\n4.  Once the database has been populated with the @product@ tables, remove all\n    permissions from that user except permissions to perform Select, Insert,\n    Update and Delete operations. \n\nThere are some caveats to running @product@ like this. Many plugins create new\ntables when they're deployed. Additionally, you must run the database upgrade\nfunction to upgrade @product@. If the @product@ database user doesn't have\nadequate rights to create/modify/drop tables in the database, you must grant\nthose rights to that user before you deploy one of these plugins or start\nupgrading @product@. Once the tables are created or the upgrade completes, you\ncan remove those rights until the next deploy or upgrade. Additionally, your own\ndevelopers might create plugins that must create their own tables. These are\njust like @product@ plugins that do the same thing, and they can only be\ninstalled if @product@ can create database tables. Installing these plugins\nrequires granting the @product@ database user rights to create tables in the\ndatabase before you attempt to install the plugins. \n\n@product@ has many more configurable features; but they\ncan wait until *after* deployment. The\n[Configuring @product@](/docs/7-2/deploy/-/knowledge_base/d/configuring-product)\nsection explains them. \n\nNow it's time to install @product@. \n"
  },
  {
    "path": "en/deployment/articles/01-deploying-liferay/04-installing-liferay.markdown",
    "content": "---\nheader-id: installing-product\n---\n\n# Installing @product@\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay/installing-a-liferay-tomcat-bundle.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nNow that you've\n[prepared for installing @product@](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install),\nyou can install and deploy it! This involves uncompressing the archive (the\n7-Zip or gzip bundle file), possibly copying a JDBC driver to your application\nserver, and starting the application server. Lastly, @product@ provides a setup\nwizard to configure portal essentials. \n\n| **Note:** Since bundles are the easiest way to complete an installation, the \n| installation steps here assume you're installing a @product@ bundle. If you \n| plan to install @product@ manually, please refer to the *Installing @product@ \n| on [Application Server]* article for the application server you're\n| installing on. \n\n## Extracting a @product@ Bundle \n\nExtract your @product@ bundle to the appropriate location on your server.\nThis folder is the\n[*Liferay Home*](/docs/7-2/deploy/-/knowledge_base/d/liferay-home). \n\n## Installing the JDBC Driver \n\nIf you're using a supported open source database or if you're setting up\n@product@ to use the embedded HSQL database for demo purposes, you can skip this\nstep. Otherwise, copy your database's JDBC driver `.jar` file to the folder your\napplication server documentation specifies. On Tomcat, for example, the driver\nbelongs in the `[Tomcat]/lib/ext` folder. \n\n## Running @product@ for the First Time \n\nStart your application server using the start script bundled with your\napplication server. For example, the Tomcat bundle provides the `startup.sh`\nscript in `$CATALINA_HOME/bin`.\n\n| **Note:** @product@ writes log files to folder `[Liferay Home]/logs`. \n\n| **Important:** @product@ requires that the application server JVM use the GMT \n| time zone and UTF-8 file encoding. They're preset in your @product@ bundle. \n\nThe first time @product@ starts, it creates all of its database tables. On\ncompleting startup, it launches a web browser that displays the Basic\nConfiguration page (the setup wizard). If for some reason your browser doesn't\nload the Basic Configuration page automatically, open your browser and navigate\nto your app server's address and port (for example, http://localhost:8080).\n\n## Using the Setup Wizard \n\nThe Basic Configuration page provides a convenient way to configure these\nthings:\n\n-   Portal name and default locale\n-   Administrator user\n-   Database \n\n![Figure 1: Supply the information for your portal and your portal's default administrator user on the Basic Configuration page.](../../images/basic-configuration1.png)\n\n### Portal \n\nSupply this basic portal information:\n\n**Portal Name:** name the installation you're powering with @product@.\n\n**Default Language:** choose your portal's default locale and click the *Change*\nbutton. This immediately localizes your portal content, including the Basic\nConfiguration page. \n\n**Time Zone:** select your @product@ instance's default time zone. \n\n### Administrator User \n\nFor the administrator, supply the following information:\n\n**First Name:** the default administrator user's first name\n\n**Last Name:** the default administrator user's last name\n\n**Email:** the default administrator user's email address\n\n| **Note:** the administrator user's email domain is used as the @product@ \n| instance's default domain (i.e., the\n| [`company.default.web.id`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Company) [portal property](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)).\n\n### Database \n\nThis section lets you connect to @product@'s built-in data source.\n\n| **Important:** If you haven't created a database for @product@, create one \n| now following\n| [database preparation instructions](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install#preparing-a-database)\n| in the preceding article. \n\nHSQL is selected as the default database, but it's primarily for demonstration\nor trial purposes. \n\nClick the *Change* link if you want to use @product@'s built-in data source and\nconfigure it to use the\n[database you created earlier](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install#preparing-a-database). \n\nThe database configuration section also has an *Add Sample Data* checkbox for\nadding sample data to your database. This data includes Users, Sites, and\nOrganizations for demo purposes. If you're installing @product@ on your own\nmachine to explore features, the sample data may be useful. If, however, you're\ninstalling @product@ on a real server, start with a clean system by leaving this\ncheckbox unselected. \n\n| **Warning:** HSQL should not be used in production @product@ instances. \n| Configure @product@ to use a different database; specify that database via the\n| Basic Configuration page here or using portal properties. See\n| [Database Preparation](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install#preparing-a-database)\n| for details.\n\nOnce you've filled out the Basic Configuration form, click *Finish\nConfiguration*. The setup wizard creates a\n`[LIFERAY_HOME]/portal-setup-wizard.properties` file which stores the settings\nthat you entered. When you begin customizing your portal's configuration,\nhowever, use a\n[`portal-ext.properties` file](/docs/7-2/deploy/-/knowledge_base/d/portal-properties).\nThe\n[Portal properties reference documentation](@platform-ref@/7.2-latest/propertiesdoc)\nlists the default properties and describes all the properties you can set for\n@product@. \n\n| **Tip:** The wizard is a helpful tool, especially if you're setting\n| up @product@ for the first time. If you're a veteran and you already have your\n| various properties set up, you can disable the setup wizard. If you disable \n| the setup wizard, you must configure everything manually from a portal\n| properties file (e.g., `[LIFERAY_HOME]/portal-ext.properties`). To disable the\n| setup wizard, set `setup.wizard.enabled=false` in your portal properties file.\n| Note that property values in `portal-setup-wizard.properties` (the file the\n| setup wizards creates in Liferay Home) override property values in\n| `portal-ext.properties`.\n\nOn finishing basic configuration, @product@ prompts you to restart your server.\nWhen you restart your application server, @product@ initializes the database you\nspecified. \n\nNow that @product@ is up and running, you can continue configuring it as\ndesired. Here are some suggestions:\n\n1.  [Configure your mail session](/docs/7-2/deploy/-/knowledge_base/d/configuring-mail),\n    if you haven't already configured it. \n\n2.  Install the Marketplace plugin, if it isn't already installed. If your \n    machine has  restricted access to the public network or if you restricted\n    the @product@ database user's permissions after initializing the database\n    (not recommended), you can still set up Marketplace by following the\n    [Marketplace setup instructions](/docs/7-2/deploy/-/knowledge_base/d/setting-up-marketplace-and-portal-security). \n\n3.  Read the\n    [Configuring @product@](/docs/7-2/deploy/-/knowledge_base/d/configuring-product)\n    articles for guidance in configuring @product@'s default time zone, locales,\n    logging, search engine, document repository, and more.\n\nYou're on your way to providing your organization with terrific experiences on\n@product@. \n"
  },
  {
    "path": "en/deployment/articles/01-deploying-liferay/05-installing-liferay-on-tomcat.markdown",
    "content": "---\nheader-id: installing-product-on-tomcat\n---\n\n# Installing @product@ on Tomcat\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay/installing-liferay-on-an-application-server/installing-on-tomcat.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n@product-ver@ bundled with Tomcat 9 is available on the [Help\nCenter](https://customer.liferay.com/downloads) (DXP) or the [Community\nDownloads page](https://www.liferay.com/downloads-community) (Portal CE). The\nTomcat bundle contains JARs, scripts, and configuration files required for\ndeploying @product-ver@. Copying these files from the @product@ Tomcat bundle\nfacilitates installing @product@ on an existing Tomcat application server.\n\nWhether you copy bundle files (recommended) or download and create the files,\nyou must download these files for [DXP](https://customer.liferay.com/downloads)\nor [Portal CE](https://www.liferay.com/downloads-community):\n\n- @product@ WAR file\n- Dependencies ZIP file\n- OSGi Dependencies ZIP file\n\n| **Important:**\n| [Prepare for the install](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install)\n| before continuing.\n\nHere are the basic steps for installing @product@ on Tomcat:\n\n- [Installing dependencies to your application server](#installing-dependencies)\n- [Configuring your application server for @product@](#configuring-tomcat)\n- [Deploying the @product@ WAR file to your application server](#deploying-product)\n\n[*Liferay Home*](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\nis Tomcat server's parent folder. `$TOMCAT_HOME` refers to your Tomcat server\nfolder. It is usually named `tomcat-[version]` or `apache-tomcat-[version]`.\n\n## Installing Dependencies\n\n@product@ depends on many JARs included by @product@ Tomcat bundle. Some of the\nbundle's JARs are not strictly required but can still be useful. If you don't\nhave a bundle, you can download the Liferay JARs by downloading the\n*Dependencies* archive and the *OSGi Dependencies* archive, and you can download\nthe third-party JARs as described below.\n\n1.  Create the folder `$TOMCAT_HOME/lib/ext` if it doesn't exist and extract the\n    JARs from the dependencies ZIP to it.\n\n2.  Copy the following JARs from a @product@ Tomcat bundle (or download them) to\n    the `$TOMCAT_HOME/lib/ext` folder:\n\n    - [`activation.jar`](http://www.oracle.com/technetwork/java/javase/jaf-136260.html)\n    - [`ccpp.jar`](http://mvnrepository.com/artifact/javax.ccpp/ccpp/1.0)\n    - [`jms.jar`](http://www.oracle.com/technetwork/java/docs-136352.html)\n    - [`jta.jar`](http://www.oracle.com/technetwork/java/javaee/jta/index.html)\n    - [`jutf7.jar`](http://mvnrepository.com/artifact/com.beetstra.jutf7/jutf7)\n    - [`mail.jar`](http://www.oracle.com/technetwork/java/index-138643.html)\n    - [`persistence.jar`](http://mvnrepository.com/artifact/org.eclipse.persistence/javax.persistence/2.1.1)\n    - [`support-tomcat.jar`](http://mvnrepository.com/artifact/com.liferay.portal/com.liferay.support.tomcat)\n\n3.  Copy the JDBC driver for your database to the `$CATALINA_BASE/lib/ext`\n    folder.\n\n    | **Note:** The [Liferay DXP Compatibility Matrix](https://web.liferay.com/documents/14/21598941/Liferay+DXP+7.2+Compatibility+Matrix/b6e0f064-db31-49b4-8317-a29d1d76abf7?) specifies supported databases and environments.\n\n4.  Create an `osgi` folder in your Liferay Home. Extract the folders (i.e.,\n    `configs`, `core`, and more) from OSGi ZIP file to the `osgi` folder. The\n    `osgi` folder provides the necessary modules for @product@'s OSGi runtime.\n\n## Configuring Tomcat\n\nConfiguring Tomcat to run @product@ includes\n\n- Setting environment variables\n- Specifying a web application context for @product@\n- Setting properties and descriptors\n\nOptionally, if you're not using @product@'s built-in data source or mail\nsession, you can configure Tomcat to manage them:\n\n- [Data source](#database-configuration)\n- [Mail session](#mail-configuration)\n\nStart with configuring Tomcat to run @product@.\n\n1.  If you have a @product@ Tomcat bundle, copy the `setenv.bat`, `setenv.sh`,\n    `startup.bat`, `startup.sh`, `shutdown.bat`, and `shutdown.sh` files from it\n    to your `$CATALINA_BASE/bin` folder. If not, create the  `setenv.bat` and\n    `setenv.sh`scripts.\n\n    The scripts set JVM options for Catalina, which is Tomcat's servlet\n    container. Among these options is the location of the Java runtime\n    environment. If this environment is not available on your server globally,\n    you must set its location in in these files so Tomcat can run. Do this by\n    pointing the `JAVA_HOME` environment variable to a @product@-supported JRE:\n\n    ```bash\n    export JAVA_HOME=/usr/lib/jvm/java-8-jdk\n    export PATH=$JAVA_HOME/bin:$PATH\n    ```\n\n\n    Then configure Catalina's JVM options to support @product@.\n\n    Unix:\n\n    ```bash\n    CATALINA_OPTS=\"$CATALINA_OPTS -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Dorg.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=false -Duser.timezone=GMT -Xms2560m -Xmx2560m -XX:MaxMetaspaceSize=512m\"\n    ```\n\n    Windows:\n\n    ```bash\n    set \"CATALINA_OPTS=%CATALINA_OPTS% -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Dorg.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=false -Duser.timezone=GMT -Xms2560m -Xmx2560m -XX:MaxMetaspaceSize=512m\"\n    ```\n\n    This sets the file encoding to UTF-8, prefers an IPv4 stack over IPv6,\n    prevents Tomcat from working around garbage collection bugs relating to\n    static or final fields (these bugs don't exist in @product@ and working\n    around them causes problems with the logging system), sets the time zone to\n    GMT, gives the JVM 2GB of RAM, and limits Metaspace to 512MB.\n\n    | **Important:** @product@ requires that the application server JVM use the\n    | GMT time zone and UTF-8 file encoding.\n\n    | **Note:** On JDK 11, it's recommended to add this JVM argument to display\n    | four-digit years.\n    |\n    | ```properties\n    | -Djava.locale.providers=JRE,COMPAT,CLDR\n    | ```\n\n    After installation, tune your system (including these JVM options) for\n    performance.\n\n2.  If you have a @product@ Tomcat bundle, copy its\n    `$CATALINA_BASE/conf/Catalina/localhost/ROOT.xml` file to the corresponding\n    location in your application server. Create the file path if it doesn't\n    exist. If you don't have a @product@ Tomcat bundle, create a `ROOT.xml`\n    file.\n\n    The `ROOT.xml` file specifies a web application context for @product@.\n    `ROOT.xml` looks like this:\n\n    ```xml\n    <Context crossContext=\"true\" path=\"\">\n\n        <!-- JAAS -->\n\n        <!--<Realm\n            className=\"org.apache.catalina.realm.JAASRealm\"\n            appName=\"PortalRealm\"\n            userClassNames=\"com.liferay.portal.kernel.security.jaas.PortalPrincipal\"\n            roleClassNames=\"com.liferay.portal.kernel.security.jaas.PortalRole\"\n        />-->\n\n        <!--\n        Uncomment the following to disable persistent sessions across reboots.\n        -->\n\n        <!--<Manager pathname=\"\" />-->\n\n        <!--\n        Uncomment the following to not use sessions. See the property\n        \"session.disabled\" in portal.properties.\n        -->\n\n        <!--<Manager className=\"com.liferay.support.tomcat.session.SessionLessManagerBase\" />-->\n\n        <Resources>\n            <PreResources\n                base=\"${catalina.base}/lib/ext/portal\"\n                className=\"com.liferay.support.tomcat.webresources.ExtResourceSet\"\n                webAppMount=\"/WEB-INF/lib\"\n            />\n        </Resources>\n    </Context>\n    ```\n\n     Setting `crossContext=\"true\"` lets multiple web applications use the same\n     class loader. This configuration includes commented instructions and tags\n     for configuring a JAAS realm, disabling persistent sessions, and disabling\n     sessions entirely.\n\n3.  Provide Catalina access to the JARs in `$CATALINA_BASE/lib/ext` by opening\n    your `$CATALINA_BASE/conf/catalina.properties` file and appending this\n    value to the `common.loader` property:\n\n    ```properties\n    ,\"${catalina.home}/lib/ext/global\",\"${catalina.home}/lib/ext/global/*.jar\",\"${catalina.home}/lib/ext\",\"${catalina.home}/lib/ext/*.jar\"\n    ```\n\n4.  Make sure to use UTF-8 URI encoding consistently. If you have a @product@\n    Tomcat bundle, copy the `$CATALINA_BASE/conf/server.xml` file to your\n    server. If not, open your `$CATALINA_BASE/conf/server.xml` file and add the\n    attribute `URIEncoding=\"UTF-8\"` to HTTP and AJP connectors that use\n    `redirectPort=8443`. Here are examples:\n\n    Old:\n\n    ```xml\n    <Connector port=\"8080\" protocol=\"HTTP/1.1\" connectionTimeout=\"20000\" redirectPort=\"8443\" />\n    ```\n\n    New:\n\n    ```xml\n    <Connector port=\"8080\" protocol=\"HTTP/1.1\" connectionTimeout=\"20000\" redirectPort=\"8443\" URIEncoding=\"UTF-8\" />\n    ```\n\n    Old:\n\n    ```xml\n    <Connector port=\"8009\" protocol=\"AJP/1.3\" redirectPort=\"8443\" />\n    ```\n\n    New:\n\n    ```xml\n    <Connector port=\"8009\" protocol=\"AJP/1.3\" redirectPort=\"8443\" URIEncoding=\"UTF-8\" />\n    ```\n\n5.  Refrain from writing access logs (optional) by commenting out the access\n    log `Valve` element in `$CATALINA_BASE/conf/server.xml`. It's commented out\n    here:\n\n    ```xml\n    <!-- <Valve className=\"org.apache.catalina.valves.AccessLogValve\"\n           directory=\"logs\"\n           prefix=\"localhost_access_log\" suffix=\".txt\"\n           pattern=\"%h %l %u %t &quot;%r&quot; %s %b\" /> -->\n    ```\n\n6.  Optionally, set the following log levels in your\n    `$CATALINA_HOME/conf/logging.properties` file:\n\n    ```properties\n    org.apache.catalina.startup.Catalina.level=INFO\n    org.apache.catalina.startup.ClassLoaderFactory.level=SEVERE\n    org.apache.catalina.startup.VersionLoggerListener.level=WARNING\n    org.apache.level=WARNING\n    ```\n\n7.  In `$CATALINA_HOME/conf/web.xml`, set the JSP compiler to Java 8 and set\n    @product@'s `TagHandlerPool` class to manage the JSP tag pool. Do this by\n    adding the following elements above the `jsp` servlet element's\n    `<load-on-startup>` element.\n\n    ```xml\n    <init-param>\n        <param-name>compilerSourceVM</param-name>\n        <param-value>1.8</param-value>\n    </init-param>\n    <init-param>\n        <param-name>compilerTargetVM</param-name>\n        <param-value>1.8</param-value>\n    </init-param>\n    <init-param>\n        <param-name>tagpoolClassName</param-name>\n        <param-value>com.liferay.support.tomcat.jasper.runtime.TagHandlerPool</param-value>\n    </init-param>\n    ```\n\n8.  In `$CATALINA_HOME/conf/web.xml`, specify whether the application server\n    should look for extra metadata, such as annotations in the application's\n    JARs and classes. Setting `web-app` element's attribute\n    `metadata-complete=\"true\"` tells the application server there's no extra\n    metadata. The application server starts up faster this way. The default is\n    to check for extra metadata.\n\n9.  If you're on Unix, Linux, or Mac OS, make the shell scripts in your\n    `$CATALINA_HOME/bin` and `$CATALINA_BASE/bin` folders executable by running\n    this command in each folder:\n\n    ```bash\n    chmod a+x *.sh\n    ```\n\n**Checkpoint:**\n\nYour application server is configured to run @product@.\n\n1.  The file encoding, user time-zone, and preferred protocol stack are set in\n    your `setenv.sh`.\n\n2.  The default memory available and Metaspace limit are set.\n\n3.  `$CATALINA_BASE/conf/Catalina/localhost/ROOT.xml` declares the web\n    application context.\n\n4.  The `common.loader` property in `$CATALINA_BASE/conf/catalina.properties`\n    grants Catalina access to the JARs in `$CATALINA_BASE/lib/ext`.\n\n5.  `$CATALINA_BASE/conf/server.xml` sets UTF-8 encoding.\n\n6.  `$CATALINA_BASE/conf/server.xml` doesn't declare any valve for writing host\n    access logs. (optional)\n\n7.  `$CATALINA_HOME/conf/logging.properties` sets the desired log levels.\n\n8.  `$CATALINA_HOME/conf/web.xml` sets the tag handler pool and sets Java 8 as\n    the JSP compiler.\n\n9.  `$CATALINA_HOME/conf/web.xml` specifies for the application server\n    to refrain from looking for extra metadata. (optional)\n\n10. The scripts in Tomcat's `bin` folders are executable.\n\n### Database Configuration\n\nThe easiest way to handle your database configuration is to let @product@ manage\nyour data source. If you want to use the\n[built-in data source (recommended)](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install#using-the-built-in-data-source),\nskip this section.\n\nIf you want Tomcat to manage your data source, follow these steps:\n\n1.  Make sure your database server is installed and working. If it's installed\n    on a different machine, make sure your @product@ machine can access it.\n\n2.  Open `$CATALINA_BASE/conf/Catalina/localhost/ROOT.xml` and add your data\n    source as a `Resource` in your web application `Context`:\n\n    ```xml\n    <Context...>\n        ...\n        <Resource\n            name=\"jdbc/LiferayPool\"\n            auth=\"Container\"\n            type=\"javax.sql.DataSource\"\n            driverClassName=\"[place the database driver class here]\"\n            url=\"[place the URL to your database here]\"\n            username=\"[place your user name here]\"\n            password=\"[place your password here]\"\n            maxTotal=\"100\"\n            maxIdle=\"30\"\n            maxWaitMillis=\"10000\"\n        />\n    </Context>\n    ```\n\n   Make sure to replace the database URL, user name, and password with the appropriate values. For example JDBC connection values, please see [Database Templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates)\n\n3.  In a `portal-ext.properties` file in your Liferay Home, specify your data\n    source:\n\n    ```properties\n    jdbc.default.jndi.name=jdbc/LiferayPool\n    ```\n\nYou created a data source for Tomcat to manage and configured @product@ to use\nit. Mail session configuration is next.\n\n### Mail Configuration\n\nAs with database configuration, the easiest way to configure mail is to let\n@product@ handle your mail session. If you want to use @product@'s\n[built-in mail session](/docs/7-2/deploy/-/knowledge_base/d/configuring-mail),\nskip this section.\n\nIf you want to manage your mail session with Tomcat, follow these steps:\n\n1.  Open `$CATALINA_BASE/conf/Catalina/localhost/ROOT.xml` and add your mail\n    session as a `Resource` in your web application `Context`. Make sure to\n    replace the example mail session values with your own.\n\n    ```xml\n    <Context...>\n        ...\n        <Resource\n            name=\"mail/MailSession\"\n            auth=\"Container\"\n            type=\"javax.mail.Session\"\n            mail.pop3.host=\"pop.gmail.com\"\n            mail.pop3.port=\"110\"\n            mail.smtp.host=\"smtp.gmail.com\"\n            mail.smtp.port=\"465\"\n            mail.smtp.user=\"user\"\n            mail.smtp.password=\"password\"\n            mail.smtp.auth=\"true\"\n            mail.smtp.starttls.enable=\"true\"\n            mail.smtp.socketFactory.class=\"javax.net.ssl.SSLSocketFactory\"\n            mail.imap.host=\"imap.gmail.com\"\n            mail.imap.port=\"993\"\n            mail.transport.protocol=\"smtp\"\n            mail.store.protocol=\"imap\"\n        />\n    </Context>\n    ```\n\n2.  In your `portal-ext.properties` file in Liferay Home, reference your mail\n    session:\n\n    ```properties\n    mail.session.jndi.name=mail/MailSession\n    ```\n\nYou've created a mail session for Tomcat to manage and configured @product@ to\nuse it.\n\n## Deploying @product@\n\nNow you're ready to deploy @product@ using the @product@ WAR file.\n\n1.  If you are manually installing @product@ on a clean Tomcat server, delete\n    the contents of the `$CATALINA_BASE/webapps/ROOT` folder. This removes\n    the default Tomcat home page.\n\n2.  Extract the @product@ `.war` file contents to\n    `$CATALINA_BASE/webapps/ROOT`.\n\n    It's time to launch @product@ on Tomcat!\n\n3.  Start Tomcat by navigating to `$CATALINA_HOME/bin` and executing\n    `./startup.sh`. Alternatively, execute `./catalina.sh run` to tail\n    @product@'s log file. The log audits startup activities and is useful for\n    debugging deployment.\n\nCongratulations on successfully installing and deploying @product@ on Tomcat!\n"
  },
  {
    "path": "en/deployment/articles/01-deploying-liferay/06-installing-liferay-portal-on-wildfly.markdown",
    "content": "---\nheader-id: installing-product-on-wildfly\n---\n\n# Installing @product@ on Wildfly\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay/installing-liferay-on-an-application-server/installing-on-wildfly.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nInstalling @product@ on Wildfly 11 takes three steps:\n\n-   [Installing dependencies to your application server](#installing-dependencies)\n-   [Configuring your application server for @product@](#configuring-wildfly)\n-   [Deploying the @product@ WAR file to your application server](#deploying-product)\n\n| **Important:** Before installing @product@, familiarize yourself with\n| [preparing for install](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install).\n\nNow, [download the @product@ WAR and Dependency\nJARs](/docs/7-2/deploy/-/knowledge_base/d/obtaining-product#downloading-the-liferay-war-and-dependency-jars):\n\n-   @product@ WAR file\n-   Dependencies ZIP file\n-   OSGi Dependencies ZIP file\n\nNote that [*Liferay Home*](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) is\nthe folder containing your Wildfly server folder. After installing and deploying\n@product@, the Liferay Home folder contains the Wildfly server folder as well as\n`data`, `deploy`, `logs`, and `osgi` folders. `$WILDFLY_HOME` refers to your\nWildfly server folder. It is usually named `wildfly-[version]`.\n\n## Installing Dependencies\n\n@product@ depends on a driver for your database and the JARs in the Dependencies\nZIP and OSGi Dependencies ZIP files you downloaded. Here's how to install them:\n\n1.  Create the folder `$WILDFLY_HOME/modules/com/liferay/portal/main` if it\n    doesn't exist and extract the Dependencies ZIP JARs to it.\n\n2.  Download your database driver `.jar` file and copy it into the same folder.\n\n    | **Note:** The [Liferay DXP Compatibility Matrix](https://web.liferay.com/documents/14/21598941/Liferay+DXP+7.2+Compatibility+Matrix/b6e0f064-db31-49b4-8317-a29d1d76abf7?) specifies supported databases and environments.\n\n3.  Create the file `module.xml` in the\n    `$WILDFLY_HOME/modules/com/liferay/portal/main` folder and insert this\n    configuration:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n\n    <module xmlns=\"urn:jboss:module:1.0\" name=\"com.liferay.portal\">\n        <resources>\n            <resource-root path=\"com.liferay.petra.concurrent.jar\" />\n            <resource-root path=\"com.liferay.petra.executor.jar\" />\n            <resource-root path=\"com.liferay.petra.function.jar\" />\n            <resource-root path=\"com.liferay.petra.io.jar\" />\n            <resource-root path=\"com.liferay.petra.lang.jar\" />\n            <resource-root path=\"com.liferay.petra.memory.jar\" />\n            <resource-root path=\"com.liferay.petra.nio.jar\" />\n            <resource-root path=\"com.liferay.petra.process.jar\" />\n            <resource-root path=\"com.liferay.petra.reflect.jar\" />\n            <resource-root path=\"com.liferay.petra.string.jar\" />\n            <resource-root path=\"com.liferay.registry.api.jar\" />\n            <resource-root path=\"hsql.jar\" />\n            <resource-root path=\"[place your database vendor's jar here]\" />\n            <resource-root path=\"portal-kernel.jar\" />\n            <resource-root path=\"portlet.jar\" />\n        </resources>\n        <dependencies>\n            <module name=\"javax.api\" />\n            <module name=\"javax.mail.api\" />\n            <module name=\"javax.servlet.api\" />\n            <module name=\"javax.servlet.jsp.api\" />\n            <module name=\"javax.transaction.api\" />\n        </dependencies>\n    </module>\n    ```\n\n    Replace `[place your database vendor's jar here]` with the driver JAR for your database.\n\n4.  Create an `osgi` folder in your [Liferay\n    Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) folder. Extract the\n    OSGi Dependencies ZIP file that you downloaded into the `[Liferay\n    Home]/osgi` folder.\n\n    The `osgi` folder provides the necessary modules for @product@'s OSGi\n    runtime.\n\n## Running @product@ on Wildfly in Standalone Mode vs. Domain Mode\n\nWildfly can be launched in either *standalone* mode or *domain* mode. Domain\nmode allows multiple application server instances to be managed from a single\ncontrol point. A collection of such application servers is known as a *domain*.\nFor more information on standalone mode vs. domain mode, please refer to the\nsection on this topic in the\n[Wildfly Admin Guide](https://docs.jboss.org/author/display/WFLY/Admin+Guide#AdminGuide-Operatingmodes).\n@product@ fully supports Wildfly in standalone mode but not in domain mode.\n\nYou can run @product@ on Wildfly in domain mode, but this method is not fully\nsupported. In particular, @product@'s hot-deploy does not work with a managed\ndeployment, since Wildfly manages the content of a managed deployment by copying\nfiles (exploded or non-exploded). This prevents JSP hooks and Ext plugins from\nworking as intended. For example, JSP hooks don't work on Wildfly running in\nmanaged domain mode, since @product@'s JSP override mechanism relies on the\napplication server. Since JSP hooks and Ext plugins are deprecated, however, you\nmay not be using them.\n\nThe command line interface is recommended for domain mode deployments.\n\n| **Note:** This does not prevent @product@ from running in a clustered\n| environment on multiple Wildfly servers. You can set up a cluster of @product@\n| instances running on Wildfly servers running in standalone mode. Please refer\n| to the [@product@ clustering articles](/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering)\n| for more information.\n\n## Configuring Wildfly\n\nConfiguring Wildfly to run @product@ includes these things:\n\n-   Setting environment variables\n-   Setting properties and descriptors\n-   Removing unnecessary configurations\n\nOptionally, you can configure Wildfly to manage @product@'s data source and mail\nsession.\n\nStart with configuring Wildfly to run @product@.\n\nMake the following modifications to\n`$WILDFLY_HOME/standalone/configuration/standalone.xml`:\n\n1.  Locate the closing `</extensions>` tag. Directly beneath that tag, insert\n    the following system properties:\n\n    ```xml\n    <system-properties>\n        <property name=\"org.apache.catalina.connector.URI_ENCODING\" value=\"UTF-8\" />\n        <property name=\"org.apache.catalina.connector.USE_BODY_ENCODING_FOR_QUERY_STRING\" value=\"true\" />\n    </system-properties>\n    ```\n\n2.  Add the following `<filter-spec>` tag within the `<console-handler>` tag,\n    directly below the `<level name=\"INFO\"/>` tag:\n\n    ```xml\n    <filter-spec value=\"not(any(match(&quot;WFLYSRV0059&quot;),match(&quot;WFLYEE0007&quot;)))\" />\n    ```\n\n3.  Add a timeout for the deployment scanner by setting\n    `deployment-timeout=\"600\"` as seen in the excerpt below.\n\n    ```xml\n    <subsystem xmlns=\"urn:jboss:domain:deployment-scanner:2.0\">\n        <deployment-scanner deployment-timeout=\"600\" path=\"deployments\" relative-to=\"jboss.server.base.dir\" scan-interval=\"5000\" runtime-failure-causes-rollback=\"${jboss.deployment.scanner.rollback.on.failure:false}\"/>\n    </subsystem>\n    ```\n\n4.  Add the following JAAS security domain to the security subsystem\n    `<security-domains>` defined in element `<subsystem\n    xmlns=\"urn:jboss:domain:security:2.0\">`.\n\n    ```xml\n    <security-domain name=\"PortalRealm\">\n        <authentication>\n            <login-module code=\"com.liferay.portal.security.jaas.PortalLoginModule\" flag=\"required\" />\n        </authentication>\n    </security-domain>\n    ```\n\n5.  Remove the welcome content code snippets:\n\n    ```xml\n    <location name=\"/\" handler=\"welcome-content\"/>\n    ```\n\n    and\n\n    ```xml\n    <handlers>\n        <file name=\"welcome-content\" path=\"${jboss.home.dir}/welcome-content\"/>\n    </handlers>\n    ```\n\n6.  Find the `<jsp-config/>` tag and set the `development`, `source-vm`, and\n    `target-vm` attributes in the tag. Once finished, the tag should look like\n    this:\n\n    ```xml\n    <jsp-config development=\"true\" source-vm=\"1.8\" target-vm=\"1.8\" />\n    ```\n\n**Checkpoint:**\n\nBefore continuing, verify the following properties have been set in the\n`standalone.xml` file:\n\n1.  The new `<system-property>` is added.\n\n2.  The new `<filter-spec>` is added.\n\n3.  The `<deployment-timeout>` is set to `600`.\n\n4.  The new `<security-domain>` is created.\n\n5.  Welcome content is removed.\n\n6.  The `<jsp-config>` tag contains its new attributes.\n\nNow you must configure your JVM and startup scripts.\n\nIn the `$WILDFLY_HOME/bin/` folder, modify your standalone domain's\nconfiguration script file `standalone.conf` (`standalone.conf.bat` on Windows):\n\n- Set the file encoding to `UTF-8`\n- Set the user time zone to `GMT`\n- Set the preferred protocol stack\n- Increase the default amount of memory available.\n\n| **Important:** For @product@ to work properly, the application server JVM must\n| use the `GMT` time zone and `UTF-8` file encoding.\n\nMake the following edits as applicable for your operating system:\n\n**Windows:**\n\n1.  Comment out the initial `JAVA_OPTS` assignment like this:\n\n    ```bash\n    rem set \"JAVA_OPTS=-Xms64M -Xmx512M -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=2560m\"\n    ```\n\n2.  Add the following `JAVA_OPTS` assignment one line above the `:JAVA_OPTS_SET`\n    line found at end of the file:\n\n    ```bash\n    set \"JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Djboss.as.management.blocking.timeout=480 -Duser.timezone=GMT -Xms2560m -Xmx2560m -XX:MaxMetaspaceSize=512m -XX:MetaspaceSize=200m\"\n    ```\n\n**Unix:**\n\n1.  Below the `if [ \"x$JAVA_OPTS\" = \"x\" ];` statement, replace this `JAVA_OPTS`\n    statement:\n\n    ```bash\n    JAVA_OPTS=\"-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true\"\n    ```\n\n    with this:\n\n    ```bash\n    JAVA_OPTS=\"-Djava.net.preferIPv4Stack=true\"\n    ```\n\n2.  Add the following statement to the bottom of the file:\n\n    ```bash\n    JAVA_OPTS=\"$JAVA_OPTS -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Djboss.as.management.blocking.timeout=480 -Duser.timezone=GMT -Xms2560m -Xmx2560m -XX:MaxMetaspaceSize=512m -XX:MetaspaceSize=200m\"\n    ```\n\nThis sets the file encoding to UTF-8, prefers an IPv4 stack over IPv6, sets the\ntime zone to GMT, gives the JVM 2GB of RAM, and limits Metaspace to 512MB.\n\nOn JDK 11, it's recommended to add this JVM argument to display four-digit\nyears.\n\n```bash\n-Djava.locale.providers=JRE,COMPAT,CLDR\n```\n\nAfter installation, tune your system (including these JVM options) for\nperformance.\n\n| **Important:** For @product@ to work properly, the application server JVM must\n| use the `GMT` time zone and `UTF-8` file encoding.\n\n| **Note:** If you plan on using the IBM JDK with your Wildfly server, you must\n| complete some additional steps. First, navigate to the\n| `$WILDFLY_HOME/modules/com/liferay/portal/main/module.xml` file and insert the\n| following dependency within the `<dependencies>` element:\n|\n|     <module name=\"ibm.jdk\" />\n|\n| Then navigate to the\n| `$WILDFLY_HOME/modules/system/layers/base/sun/jdk/main/module.xml` file and\n| insert the following path names inside the `<paths>...</paths>` element:\n|\n|     <path name=\"com/sun/crypto\" />\n|     <path name=\"com/sun/crypto/provider\" />\n|     <path name=\"com/sun/org/apache/xml/internal/resolver\" />\n|     <path name=\"com/sun/org/apache/xml/internal/resolver/tools\" />\n|\n| The added paths resolve issues with deployment exceptions and image uploading\n| problems.\n\n**Checkpoint:**\n\nYou've configured the application server's JVM settings.\n\n1.  The file encoding, user time-zone, preferred protocol stack have been set in\n    the `JAVA_OPTS` in the `standalone.conf.bat` file.\n\n2.  The default amount of memory available has been increased.\n\nThe prescribed script modifications are now complete for your @product@\ninstallation on Wildfly. Next you'll configure your database.\n\n### Database Configuration\n\nThe easiest way to handle database configuration is to let @product@ manage your\ndata source. The\n[Basic Configuration](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install#using-the-built-in-data-source)\npage lets you configure @product@'s built-in data source. If you want to use the\nbuilt-in data source, skip this section.\n\nIf using WildFly to manage the data source, follow these steps:\n\n1. Add the data source inside the `$WILDFLY_HOME/standalone/configuration/standalone.xml` file's `<datasources>` element:\n\n    ```xml\n    <datasource jndi-name=\"java:jboss/datasources/ExampleDS\" pool-name=\"ExampleDS\" enabled=\"true\" jta=\"true\" use-java-context=\"true\" use-ccm=\"true\">\n        <connection-url>[place the URL to your database here]</connection-url>\n        <driver>[place your driver name here]</driver>\n        <security>\n            <user-name>[place your user name here]</user-name>\n            <password>[place your password here]</password>\n        </security>\n    </datasource>\n    ```\n\n    Make sure to replace the database URL, user name, and password with the appropriate values. For example JDBC connection values, please see [Database Templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates)\n\n    | **Note:** If the data source `jndi-name` must be changed, edit the `datasource` element in the `<default-bindings>` tag.\n\n1. Add the driver to the `standalone.xml` file's `<drivers>` element also found within the `<datasources>` element.\n\n    ```xml\n    <drivers>\n        <driver name=\"[name of database driver]\" module=\"com.liferay.portal\">\n            <driver-class>[JDBC driver class]</driver-class>\n        </driver>\n    </drivers>\n    ```\n\n    A final data sources subsystem that uses MySQL should look like this:\n\n    ```xml\n    <subsystem xmlns=\"urn:jboss:domain:datasources:1.0\">\n        <datasources>\n            <datasource jndi-name=\"java:jboss/datasources/ExampleDS\" pool-name=\"ExampleDS\" enabled=\"true\" jta=\"true\" use-java-context=\"true\" use-ccm=\"true\">\n                <connection-url>jdbc:mysql://localhost/lportal</connection-url>\n                <driver>mysql</driver>\n                <security>\n                    <user-name>root</user-name>\n                    <password>root</password>\n                </security>\n            </datasource>\n            <drivers>\n                <driver name=\"mysql\" module=\"com.liferay.portal\">\n                    <driver-class>com.mysql.cj.jdbc.Driver</driver-class>\n                </driver>\n            </drivers>\n        </datasources>\n    </subsystem>\n    ```\n\n3.  In a\n    [`portal-ext.properties`](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\n    file in your Liferay Home, specify your data source:\n\n    ```properties\n    jdbc.default.jndi.name=java:jboss/datasources/ExampleDS\n    ```\n\nNow that you've configured your data source, the mail session is next.\n\n### Mail Configuration\n\nAs with database configuration, the easiest way to configure mail is to let\n@product@ handle your mail session. If you want to use @product@'s built-in mail\nsession, skip this section and\n[configure the mail session](/docs/7-2/deploy/-/knowledge_base/d/configuring-mail)\nin the Control Panel.\n\nIf you want to manage your mail session with Wildfly, follow these steps:\n\n1.  Specify your mail subsystem in the\n    `$WILDFLY_HOME/standalone/configuration/standalone.xml` file like this:\n\n    ```xml\n    <subsystem xmlns=\"urn:jboss:domain:mail:3.0\">\n        <mail-session jndi-name=\"java:jboss/mail/MailSession\" name=\"mail-smtp\">\n            <smtp-server ssl=\"true\" outbound-socket-binding-ref=\"mail-smtp\" username=\"USERNAME\" password=\"PASSWORD\"/>\n       </mail-session>\n    </subsystem>\n    ...\n    <socket-binding-group name=\"standard-sockets\" default-interface=\"public\" port-offset=\"${jboss.socket.binding.port-offset:0}\">\n    ...\n    <outbound-socket-binding name=\"mail-smtp\">\n            <remote-destination host=\"[place SMTP host here]\" port=\"[place SMTP port here]\"/>\n        </outbound-socket-binding>\n    </socket-binding-group>\n    ```\n\n2.  In your\n    [`portal-ext.properties`](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\n    file in Liferay Home, reference your mail session:\n\n    ```properties\n    mail.session.jndi.name=java:jboss/mail/MailSession\n    ```\n\nNext, you'll deploy @product@ to your Wildfly app server.\n\n## Deploying @product@\n\nNow you're ready to deploy @product@ using the @product@ WAR file.\n\n1.  If the folder `$WILDFLY_HOME/standalone/deployments/ROOT.war` already exists\n    in your Wildfly installation, delete all of its subfolders and files.\n    Otherwise, create a new folder called\n    `$WILDFLY_HOME/standalone/deployments/ROOT.war`.\n\n2.  Unzip the @product@ `.war` file into the `ROOT.war` folder.\n\n3.  To trigger deployment of `ROOT.war`, create an empty file named\n    `ROOT.war.dodeploy` in your `$WILDFLY_HOME/standalone/deployments/` folder.\n    On startup, Wildfly detects this file and deploys it as a web application.\n\n4.  Start the Wildfly application server by navigating to `$WILDFLY_HOME/bin`\n    and running `standalone.bat` or `standalone.sh`.\n\nCongratulations; you've deployed @product@ on Wildfly!\n\n| **Note:** After deploying @product@, you may see excessive warnings and log\n| messages, such as the ones below, involving `PhaseOptimizer`. These are\n| benign and can be ignored. Make sure to adjust your app server's logging level\n| or log filters to avoid excessive benign log messages.\n|\n|     May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n|     WARNING: Skipping pass gatherExternProperties\n|     May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n|     WARNING: Skipping pass checkControlFlow\n|     May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n|     INFO: pass supports: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, modules, exponent operator (**), async function, trailing comma in param list]\n|     current AST contains: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, exponent operator (**), async function, trailing comma in param list, object literals with spread, object pattern rest]\n"
  },
  {
    "path": "en/deployment/articles/01-deploying-liferay/12-setting-up-marketplace-and-portal-security.markdown",
    "content": "---\nheader-id: setting-up-marketplace-and-portal-security\n---\n\n# Setting Up Marketplace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/installing-and-managing-apps/getting-started/using-marketplace.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[Liferay Marketplace](https://www.liferay.com/marketplace) is more than just a\nstore for Liferay applications. Under the hood, it provides both the store and\n@product@'s application deployment features. For this reason, you must ensure\nthat Marketplace can run and configure itself.\n\nHere are some scenarios to work around to ensure Marketplace works successfully:\n\n-   Server is Firewalled without Access to the Internet\n-   Limited Database Access\n\nThe firewall scenario is discussed first.\n\n## Server is Firewalled without Access to the Internet\n\nYour server might be behind a firewall that prevents access to the Internet. Or\nyour security policy might not allow direct download and installation from the\nInternet. In these cases, you have two options:\n\n1.  From an Internet-enabled computer, download the [Marketplace\n    plugin](https://www.liferay.com/marketplace/download). Then deploy the\n    plugin (`.lpkg` file) by copying it into the `deploy` folder in [Liferay\n    Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home).\n\n2.  Alternately, once you have the downloaded `.lpkg` file, deploy it using the\n    [App Manager](/docs/7-2/user/-/knowledge_base/u/managing-and-configuring-apps).\n\nNext you'll learn how to work around database access restrictions.\n\n## Limited Database Access\n\nSome production environments do not have the necessary database permissions for\n@product@, apps, modules, and plugins to maintain their tables. In these cases:\n\n1.  Grant the @product@ database user temporary full rights to the database.\n\n2.  Install @product@ and start it so that it populates its database.\n\n3.  Once the database is created, remove the permissions for creating tables and\n    dropping tables from the @product@ database user.\n\nSee the [database preparation\ninstructions](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install#limiting-database-access)\nfor more information. Note that many sophisticated @product@ apps---not just the\nMarketplace app---require new tables when deployed. If your environment\nrestricts database access, you may need to repeat the above steps whenever you\ndeploy a new app.\n\nYou've prepared @product@ for installing Marketplace and additional apps.\n"
  },
  {
    "path": "en/deployment/articles/01-deploying-liferay/14-document-repository-configuration/00-document-repository-configuration-intro.markdown",
    "content": "---\nheader-id: document-repository-configuration\n---\n\n# Document Repository Configuration\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/file-storage.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nYou can configure file storage in several ways. Each option is a *store* which\ncan be configured through the\n[`portal-ext.properties`](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\nfile by setting the [`dl.store.impl=`\nproperty](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Document%20Library%20Service). \n\nThe default store is called Simple File Store. It stores [documents and\nmedia](/docs/7-2/user/-/knowledge_base/u/managing-documents-and-media) files on\na file system (local or mounted). The store's default root folder is `[Liferay\nHome]/data/document_library`. You can specify a different root directory from\nwithin [System Settings](/docs/7-2/user/-/knowledge_base/u/system-settings).  \n\n1.  Access System Settings by opening the *Menu*\n    (![Menu](../../../images/icon-menu.png)) and navigating to *Control Panel\n    &rarr; Configuration &rarr; System Settings*.\n\n2.  In the *Platform* section, click *File Storage*. The File Storage page \n    appears. \n\n3.  Click *Simple File System Store*.\n\n4.  For the store's *Root directory* value, specify its absolute path or its \n    path relative to [Liferay\n    Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home).\n\n5.  Click the *Save* button.\n\nThe document library store switches immediately to the new folder. \n\n![Figure 1: The File Storage page in System Settings lets you configure document repository storage.](../../../images/file-storage.png)\n\nYou can also use an entirely different method for storing documents and media\nfiles:\n\n**Simple File System Store**: uses the file system (local or a mounted share) to\nstore files.\n\n**Advanced File System Store**: in addition to using the file system (local or a\nmounted share) to store files, Advanced File System Store nests the files into\nfolders by version, for faster performance and to store more files.\n\n**S3 Store (Amazon Simple Storage)**: uses Amazon's cloud-based storage \nsolution. \n\n**DBStore (Database Storage)**: stores the files in the database. DBStore's file\n(stored as a blob) size is 1 gigabyte. To store files larger than 1 gigabyte,\nuse Simple File System Store or Advanced File System Store. \n\nThese articles explain details for each one. \n"
  },
  {
    "path": "en/deployment/articles/01-deploying-liferay/14-document-repository-configuration/01-using-simple-file-system-store.markdown",
    "content": "---\nheader-id: using-the-simple-file-system-store\n---\n\n# Using the Simple File System Store\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/file-storage/other-file-store-types/simple-file-system-store.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe simple file storage implementation is the default store. It uses a local\nfolder to store files. You can use the file system for your\n[clustered](/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering)\nconfiguration, but the folder you're pointing to must be shared by all nodes and\nhandle concurrent requests and file locking. For this reason, you need to use a\nStorage Area Network or a clustered file system.\n\nThe file system store was the first store used in @product@ and is heavily bound\nto the @product@ database. By default, documents are stored in a\n`document_library` subfolder of the `data` folder. Of course, you can change\nthis path to anything you want in [System\nSettings](/docs/7-2/user/-/knowledge_base/u/system-settings). \n\nThe Simple File System store uses this folder path format for storing documents:\n\n```bash\n/companyId/folderId/numericFileEntryName/versionNumber\n```\n\nThe first folder name is the site's company ID. The second folder name is the\nDocuments and Media folder's ID where the document resides. The third folder\nname is the document's numeric file entry name. Finally, the fourth name is\na version number used for storing multiple versions of the document.\n\n| **Note:** A document's numeric file entry name is distinct from the document \n| ID; don't confuse the two! Each has an independent counter. The numeric file\n| entry name is used in the folder path for storing the document but the \n| document ID is not. The numeric file entry name is in the `name` column of the\n| `DLFileEntry` table in @product@'s database; the document ID is in the\n| `fileEntryId` column of the same table.\n\n| **Warning:** If a database transaction rollback occurs in the Document \n| Library, file system changes that have occurred since the start of the\n| transaction aren't reversed. Inconsistencies between Document Library files \n| and those in the file system store can occur and may require manual\n| synchronization.\n\nThe Simple File System Store binds documents very closely to @product@, and may\nnot be exactly what you want. If you've been using the default settings for\na while and must migrate your documents, there's a migration utility in the\nControl Panel in *Server Administration* &rarr; *Data Migration*. The utility\nfacilitates moving documents from one store implementation to another. \n"
  },
  {
    "path": "en/deployment/articles/01-deploying-liferay/14-document-repository-configuration/02-using-advanced-file-system-store.markdown",
    "content": "---\nheader-id: using-the-advanced-file-system-store\n---\n\n# Using the Advanced File System Store\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/file-storage.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe advanced file system store is similar to the simple file system\nstore (the default store). Like that store, it saves files to the local file\nsystem---which, of course, could be a remote file system mount. It uses a\nslightly different folder structure to store files, which is pictured below. \n\n![Figure 1: The advanced file system store creates a more nested folder structure than the file system store.](../../../images/enterprise-adv-file-system-store.png)\n\nSo what makes the advanced file system store *advanced*? Several operating\nsystems have limitations on the number of files that can be stored in a\nparticular folder. The advanced file system store overcomes this limitation by\nprogrammatically creating a structure that can expand to millions of files, by\nalphabetically nesting the files in folders. This not only allows for more files\nto be stored, but also improves performance as there are fewer files stored per\nfolder. \n\nThe same rules apply to the advanced file system store as apply to the simple\nfile system store. To [cluster](/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering) this, you must point the store to a network\nmounted file system that all the nodes can access, and that networked file\nsystem must support concurrent requests and file locking. Otherwise, you may\nexperience data corruption issues if two users attempt to write to the same file\nat the same time from two different nodes.\n\nTo use the advanced file system store, follow these steps:\n\n1.  Configure `portal-ext.properties` with this property: \n\n    ```properties\n    dl.store.impl=com.liferay.portal.store.file.system.AdvancedFileSystemStore\n    ```\n\n2.  Restart @product@.\n\n3.  In the Control Panel, navigate to *Configuration* &rarr; *System\n    Settings* &rarr; *File Storage*. \n\n4.  In the *Advanced File System Store* screen, configure the store your way.\n\n5.  Click *Save*.\n \n@product@ is using the advanced file system store. \n\n| **Warning:** If a database transaction rollback occurs in the Document \n| Library, file system changes that have occurred since the start of the\n| transaction aren't reversed. Inconsistencies between Document Library files\n| and those in the file system store can occur and may require manual\n| synchronization.\n\nYou may decide the advanced file system store for whatever reason doesn't serve\nyour needs. If this is the case, you can of course mount other file systems into\nthe documents and media library. In addition to this, you can also redefine the\n@product@ store to use one of the other supported protocols. S3 store is next. \n"
  },
  {
    "path": "en/deployment/articles/01-deploying-liferay/14-document-repository-configuration/03-using-s3-store.markdown",
    "content": "---\nheader-id: using-amazon-simple-storage-service\n---\n\n# Using Amazon Simple Storage Service\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/file-storage/other-file-store-types/amazon-s3-store.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAmazon's simple storage service (S3) is a cloud-based storage solution that you\ncan use with Documents and Media. All you need is an account, and you can store\nyour documents to the cloud from all nodes, seamlessly. \n\nWhen you sign up for the service, Amazon assigns you unique keys that link you\nto your account. In Amazon's interface, you can create \"buckets\" of data\noptimized by region. \n\nHere are the steps for configuring @product@ to use your S3 account for file\nstorage:\n\n1.  Amazon S3 requires a `SAXParser` from the application server to operate. If\n    you are using an app server like Apache Tomcat that have one, you must\n    include this property in a\n    [`system-ext.properties`](/docs/7-2/deploy/-/knowledge_base/d/system-properties)\n    file: \n\n    ```properties\n    org.xml.sax.driver=com.sun.org.apache.xerces.internal.parsers.SAXParser\n    ```\n\n2.  Place your `system-ext.properties` file in a folder that resides in your \n    @product@ installation's class path (e.g., `/WEB-INF/classes/`).\n\n3.  Set the following property in a\n    [`portal-ext.properties`](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\n    file in your [Liferay\n    Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) folder: \n\n    ```properties\n    dl.store.impl=com.liferay.portal.store.s3.S3Store\n    ```\n\n4.  Restart @product@.\n\n5.  In the Control Panel, navigate to *Configuration* &rarr; *System\n    Settings* &rarr; *File Storage*.\n\n6.  In the *S3 Store Configuration* screen, configure the store your way.\n\n7.  Click *Save*.\n\nYour @product@ instance is using the Amazon S3 store. \n\n| **Warning:** If a database transaction rollback occurs in a Document Library\n| that uses a file system based store, file system changes that have occurred\n| since the start of the transaction aren't reversed. Inconsistencies between\n| Document Library files and those in the file system store can occur and may\n| require manual synchronization. All stores except DBStore are vulnerable to \n| this limitation.\n\n| **Note:** No action is required to support AWS Signature Version 4 request\n| authorization. \n\nConsult the Amazon Simple Storage documentation for additional details on using\nAmazon's service. \n"
  },
  {
    "path": "en/deployment/articles/01-deploying-liferay/14-document-repository-configuration/04-using-the-dbstore.markdown",
    "content": "---\nheader-id: using-the-dbstore\n---\n\n# Using the DBStore\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/file-storage/other-file-store-types/dbstore.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nYou can store Documents and Media files in your @product@ database using\nDBStore. DBStore's maximum file (stored as a blob) size is 1 gigabyte. To store\nfiles larger than that, use Simple File System Store or Advanced File System\nStore. \n\nHere are the DBStore configuration steps:\n\n1.  Set the following property in a\n    [`portal-ext.properties`](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\n    file in your [Liferay\n    Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) folder: \n\n    ```properties\n    dl.store.impl=com.liferay.portal.store.db.DBStore\n    ```\n\n2.  Restart @product@. \n\nDocuments and Media now uses @product@'s database via DBStore. \n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/01-configuring-liferay-portal-intro.markdown",
    "content": "---\nheader-id: configuring-product\n---\n\n# Configuring @product@\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/setting-up-liferay.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nOnce you have @product@ installed, it's time to configure it to the specifics of\nyour environment. This means doing things like setting the time zone and\nlanguage, configuring mail, configuring a cluster, configuring a Content\nDelivery Network, tuning, and more. These topics and more are discussed here. \n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/02-configuring-mail.markdown",
    "content": "---\nheader-id: configuring-mail\n---\n\n# Configuring Mail\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/setting-up-liferay/configuring-mail.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n@product@ uses a mail server and SMTP to email notifications. @product@'s\nbuilt-in mail session is the easiest way to configure mail and it's recommended.\nYou can configure the built-in mail session before or after deploying @product@.\nAlternatively, you can configure @product@ to use a mail session on the\napplication server. \n\nCreating a mail session in @product@ or on the application server requires this information: \n\n-   Incoming POP Server and port\n-   POP User Name\n-   POP Password\n-   Outgoing SMTP Server and port\n-   SMTP User Name\n-   SMTP Password\n-   All JavaMail properties you want to use\n\nBuilt-in mail session setup is recommended and easiest. \n\n## Configuring @product@'s Built-in Mail Session \n\nThe built-in mail session setup can be done using either of these methods:\n\n-   Control Panel\n\n-   Portal properties\n\n### Built-in Mail Session in the Control Panel \n\nAfter deploying @product@, you can configure the mail session from the Control Panel. \n\n1.  Sign in as the administrative user (the user you specified on the\n    [Basic Configuration page](/docs/7-2/deploy/-/knowledge_base/d/installing-product#using-the-setup-wizard)). \n\n2.  Navigate to *Control Panel &rarr; Configuration &rarr; Server Administration\n    &rarr; Mail*.\n\n3.  Fill out the form. You're asked for the following information: \n\n    **Incoming POP Server:** The hostname for a server running the Post Office\n    Protocol. @product@ checks this mailbox for incoming messages, such as\n    message board replies. \n\n    **Incoming Port:** The port on which the POP server is listening. \n\n    **Use a Secure Network Connection:** Use an encrypted connection when \n    connecting to the POP server. \n\n    **User Name:** The user ID @product@ should use to log into the POP server. \n\n    **Password:** The password @product@ should use to log into the POP server. \n\n    **Outgoing SMTP Server:** The hostname for a server running the Simple Mail\n    Transfer Protocol. @product@ uses this server to send emails, such as \n    password change emails and other notifications. \n\n    **Outgoing Port:** The port on which the SMTP server is listening. \n\n    **Use a Secure Network Connection:** Use an encrypted connection when \n    connecting to the SMTP server. \n\n    **User Name:** The user ID @product@ should use to log into the SMTP server.\n\n    **Password:** The password @product@ should use to log into the SMTP server.\n\n    **Manually specify additional JavaMail properties to override the above\n    configuration:** If there are additional properties you need to specify, \n    supply them here. \n\n4.  Click *Save*. \n\n@product@ connects to the mail session immediately. \n\n### Built-in Mail Session Portal Properties \n\nIf you prefer specifying your mail session offline or before deploying\n@product@, use portal properties. \n\n1.  Create a\n    [`portal-ext.properties` file](/docs/7-2/deploy/-/knowledge_base/d/portal-properties),\n    if you haven't already created one. \n\n2.  Copy these default property settings into your `portal-ext.properties` file:\n\n    ```properties\n    mail.session.mail=false\n    mail.session.mail.pop3.host=localhost\n    mail.session.mail.pop3.password=\n    mail.session.mail.pop3.port=110\n    mail.session.mail.pop3.user=\n    mail.session.mail.smtp.auth=false\n    mail.session.mail.smtp.host=localhost\n    mail.session.mail.smtp.password=\n    mail.session.mail.smtp.port=25\n    mail.session.mail.smtp.user=\n    mail.session.mail.store.protocol=pop3\n    mail.session.mail.transport.protocol=smtp\n    ```\n\n3.  Replace the default mail session values with your own. \n\n4.  Put the `portal-ext.properties` file into your\n    [LIFERAY_HOME](/docs/7-2/deploy/-/knowledge_base/d/liferay-home),\n    once you've established it based on your installation. \n\n@product@ connects to the mail session on the next startup. \n\n## Configuring a Mail Session on the Application Server \n\nYou can manage a mail session for @product@ on your application server. Here's how:\n\n1.  Create a mail session on your application server, following your application\n    server documentation.\n\n2.  Point @product@ to that mail session using the Control Panel or \n    portal properties. Here are instructions for both:\n\n    -   Configure the JNDI name in the *Mail* page at *Control Panel &rarr; \n        Configuration &rarr; Server Administration &rarr; Mail*. \n    -   Set a `mail.session.jndi.name` portal property in a\n        `[LIFERAY_HOME]/portal-ext.properties` file. Here's an example property:\n\n        ```properties\n            mail.session.jndi.name=mail/MailSession\n        ```\n\nLastly, configure your instance's email senders. \n\n## Configuring default email senders\n\nEmail senders are the default name and email address @product@ uses to send administrative emails and announcement emails. \n\nDefault email senders are configured in the\n[`portal-ext.properties` file](/docs/7-2/deploy/-/knowledge_base/d/portal-properties).  \n\n-   Admin email configuration:\n\n    ```properties\n    admin.email.from.name=Joe Bloggs\n    admin.email.from.address=test@domain.invalid\n    ```\n\n-   Announcements email configuration:\n\n    ```properties\n    announcements.email.to.name=\n    announcements.email.to.address=noreply@domain.invalid\n    ```\n\n1.  Replace the names and email addresses above with your values. \n\n| **Note:** Following emails are blacklisted by default and cannot be used \n| in any @product@ installation:\n| \n| - `noreply@liferay.com`\n| - `test@liferay.com`\n| - `noreply@domain.invalid`\n| - `test@domain.invalid`\n|\n| If you use them, @product@ logs a `WARN` trace:\n|\n| `Email xxx will be ignored because it is included in mail.send.blacklist`\n\n2.  Restart your server. \n\nCongratulations on configuring mail for @product@. \n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/03-locales-and-encoding.markdown",
    "content": "---\nheader-id: locales-and-encoding-configuration\n---\n\n# Locales and Encoding Configuration\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/setting-up-liferay/initial-instance-localization.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nYou can display content based on language, time zone, \"right to left\" (that is,\nlanguages such as Hebrew, Arabic, and Persian), and you can localize user names\nand titles. Administrators can localize specific core UI messages so that the\nmessages display in certain languages.\n\n## Time Zones\n\nYou can set time zones in the Control Panel and theoretically in the JVM (but\nthis must be set to GMT: see below). \n\nTime zone configuration and default language customization are done in the\nControl Panel, at the Instance level. \n\n1.  Navigate to the *Control Panel* &rarr; *Configuration*. \n\n2.  Click *Instance Settings*. \n\n3.  Click on the *Miscellaneous* tab. \n\n![Figure 1: You can change the default and available languages and the time zone in Instance Settings.](../../images/instance-locales.png)\n\nThe central left and right arrows let you add or remove available languages and\nlocales. You can also set these as properties in your `portal-ext.properties`\nfile in your\n[Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\nfolder. The `portal.properties` reference document's \n[Company](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Company) \nsection defines the default locale. The \n[Languages and Time Zones](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Languages%20and%20Time%20Zones)\nsection defines the available and current locales.\n\n```properties\ncompany.default.locale=en_GB \n```\n\n| **Note:** The `company.default.locale` portal property is only intended for \n| use on initial startup. To change the language settings on an existing \n| instance, open the Control Panel and navigate to *Configuration* &rarr; \n| *Instance Settings* and select the Localization category under the PLATFORM \n| heading. Under the Language entry you can change the default language, as well \n| as define the current locales. \n\nAs an example, the above property changes the locale to English, Great Britain. \n \n## Set the JVM Time Zone to GMT\n\nIf you set the time zone in the JVM, it causes issues such as Calendar Events\nand Web Content articles displaying the wrong dates. This happens because the\nsystem assumes each date stored in the database is stored in GMT time. When the\nsystem needs to display one stored date to the end users, the display date is\ncalculated by the application server's current date. This date is affected by\nthe configured JVM level time zone and the stored GMT format date. To make sure\nthe display date is calculated correctly, the time zone must be configured to\nGMT at the JVM level. Otherwise, an incorrect time zone offset at the JVM level\ncauses the display date to be wrongly calculated and displayed. \n\n## Friendly URLs and Locales\n\nIn addition to configuring instance settings, you can also define unique URLs\nfor specific languages using the `I18nServlet` by editing Portal's `web.xml`\nfile: \n\n```xml\n<servlet-mapping>\n\t<servlet-name>I18n Servlet</servlet-name>\n\t<url-pattern>/ar/*</url-pattern>\n</servlet-mapping>\n  .\n  .\n.\n<servlet-mapping>\n\t<servlet-name>I18n Servlet</servlet-name>\n\t<url-pattern>/de/*</url-pattern>\n</servlet-mapping>\n```\n\nThe defaults should be sufficient for nearly all circumstances. Because\n`web.xml` changes require stopping and possibly redeploying @product@ (depending\non your app server), test the defaults and make sure you really need to modify\nthese settings. If you're clustered, you must make these changes on all nodes.\n\n## Modifying Language Keys\n\nDevelopers can add or modify certain core UI messages (e.g. *Your request\ncompleted successfully.*) by \n[modifying the language keys](/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys) \nthat ship by default. \n\n### Right to Left\n\nFor languages that are displayed right to left, use the following language\nproperties settings:\n\n```properties\nlang.dir=rtl\nlang.line.begin=right\nlang.line.end=left\n```\n\nTo display right to left by default,\n[override these properties globally](/docs/7-2/customization/-/knowledge_base/c/overriding-global-language-keys). \n\n### Localizing User Names\n\nUsers can change the prefix and suffix values for a locale. For example, for\nSpanish, the `language_es.properties` file contains these values: \n\n```properties\nlang.user.name.field.names=prefix,first-name,last-name\nlang.user.name.prefix.values=Sr,Sra,Sta,Dr,Dra\nlang.user.name.required.field.names=last-name\n```\n\nFor more information, see \n[Using Liferay Language Settings](/docs/7-2/frameworks/-/knowledge_base/f/using-liferays-localization-settings).\n\n## Related Topics\n\n[Using Liferay Language Settings](/docs/7-2/frameworks/-/knowledge_base/f/using-liferays-localization-settings)\n\n[Overriding Global Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-global-language-keys)\n\n[Overriding a Module's Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys)\n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/04-liferay-portal-clustering/01-intro.markdown",
    "content": "---\nheader-id: liferay-clustering\n---\n\n# @product@ Clustering\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/setting-up-liferay/clustering-for-high-availability.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n@product@ can serve everything from the smallest to the largest web sites. Out\nof the box, it's configured optimally for a single server environment. If one\nserver isn't sufficient to serve your site's high traffic needs, @product@\nscales to the size you need. \n\n![Figure 1: @product@ is designed to scale to as large an installation as you need.](../../../images/clustering-enterprise-configuration.png) \n\n@product@ works well in clusters of multiple machines (horizontal cluster) or in\nclusters of multiple VMs on a single machine (vertical cluster), or any mixture\nof the two. Once you have @product@ installed on more than one application\nserver node, there are several optimizations that must be made. At a minimum,\n@product@ should be configured in the following way for a clustered environment:\n\n1.  [All nodes should point to the same database or database  cluster.](/docs/7-2/deploy/-/knowledge_base/d/point-all-nodes-to-the-same-database) \n\n2.  [Documents and Media repositories must have the same configuration and be accessible to all nodes of the cluster.](/docs/7-2/deploy/-/knowledge_base/d/configure-documents-and-media-the-same-for-all-nodes) \n\n3.  [Search should be on a separate search server that is optionally clustered.](/docs/7-2/deploy/-/knowledge_base/d/clustering-search) \n\n4.  [Cluster Link must be enabled so the cache replicates across all nodes of the cluster.](/docs/7-2/deploy/-/knowledge_base/d/enabling-cluster-link) \n\n5.  [Applications must be auto-deployed to each node individually.](/docs/7-2/deploy/-/knowledge_base/d/auto-deploy-to-all-nodes) \n\nMany of these configuration changes can be made by adding or modifying\nproperties in your `portal-ext.properties` file. Remember that this file\noverrides the\n[defaults](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html)\nin the `portal.properties` file. It's a best practice to copy the relevant\nsection you want to modify from `portal.properties` into your\n`portal-ext.properties` file, and then modify the values there. \n\n| **Note:** This documentation describes a @product@-specific cluster\n| configuration without getting into specific implementations of third party\n| software, such as Java EE application servers, HTTP servers, and load\n| balancers. Please consult your documentation for those components of your\n| cluster to configure those components. Before creating a @product@ cluster,\n| make sure your OS is not defining the hostname of your box to the local \n| network at 127.0.0.1. \n\nEach step defined above is covered below to give you a step by step process for\ncreating your cluster. Start with making all Nodes point to the same database. \n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/04-liferay-portal-clustering/02-point-all-nodes-to-the-same-liferay-portal-database.markdown",
    "content": "---\nheader-id: point-all-nodes-to-the-same-database\n---\n\n# Point all Nodes to the Same @product@ Database\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/setting-up-liferay/clustering-for-high-availability/database-configuration-for-cluster-nodes.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nEach node should have a data source that points to one @product@ database\n(or a database cluster) that all the nodes share. This means, of course,\n@product@ cannot (and should not) use the embedded HSQL database that is\nshipped with the bundles (but you already knew that, right?). And, of course,\nthe database server should be on a separate system from the @product@ server.\n\n## Read-Writer Database Configuration\n\nTo improve database performance, you can use a read-writer database configuration. Instead of using the same data source for read and read-write operations, this strategy uses a separate data source for each operation type. DXP's Aspect Oriented Programming (AOP) transaction infrastructure directs read transactions to the read data source and read-write transactions to the write data source.\n\nConnections to separate read and read-write [data sources](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#JDBC) are configured using JDBC or JNDI [Portal Properties](/docs/7-2/deploy/-/knowledge_base/d/portal-properties) (e.g., in a [`portal-ext.properties` file](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)), as explained in the following sections. The data sources should use separate instances of the DXP database, where the read-write database instance is replicated to the read database instance.\n\n### JDBC\n\nEdit your `portal-ext.properties` file following these steps to connect directly to your separate read and write data sources using [JDBC](/docs/7-2/deploy/-/knowledge_base/d/database-templates):\n\n1. Set the default connection pool provider. For provider information, see the [JDBC properties reference](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#JDBC). The default setting specifies [HikariCP](https://github.com/brettwooldridge/HikariCP) as the pool provider:\n\n    ```properties\n    jdbc.default.liferay.pool.provider=hikaricp\n    ```\n\n1. Configure JDBC connections to your separate read and write data sources. Here's an example:\n\n    ```properties\n    jdbc.read.driverClassName=[place your driver name here]\n    jdbc.read.url=[place the URL to your \"read\" database here]\n    jdbc.read.username=[place your user name here]\n    jdbc.read.password=[place your password here]\n\n    jdbc.write.driverClassName=[place your driver name here]\n    jdbc.write.url=[place the URL to your \"read-write\" database here]\n    jdbc.write.username=[place your user name here]\n    jdbc.write.password=[place your password here]\n    ```\n\n    For example JDBC connection values, please see [Database Templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates).\n\n1. Configure DXP to use the write data source (the data source whose prefix is `jdbc.write.`) to create the [Counter](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Counter) data source. A separate data source is always dedicated to the counter.\n\n    ```properties\n    counter.jdbc.prefix=jdbc.write.\n    ```\n\n1. Optionally validate the data connections to make sure bad connections are handled gracefully.\n\n    Some connection pools used with JDBC4 (check your driver's JDBC version) validate connections automatically. Other connection pools may require additional, vendor-specific connection validation properties---specify them in a Portal Properties file. Refer to your connection pool provider documentation for connection validation details.\n\n1. Enable the read-writer database configuration by copying the default [`spring.configs` and `spring.infrastructure.configs` Portal Properties](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Spring) to your `portal-ext.properties` file and adding the following Spring configuration file paths to them.\n\n    Add to `spring.configs`:\n\n    ```\n    META-INF/dynamic-data-source-spring.xml\n    ```\n\n    Add to `spring.infrastructure.configs`:\n\n    ```\n    META-INF/dynamic-data-source-infrastructure-spring.xml\n    ```\n\n    For more information, see the [Spring configuration Portal Properties](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Spring).\n\n### JNDI\n\nEdit your `portal-ext.properties` file following these steps to connect to your read and write data sources on your app server using JNDI:\n\n1. Set your read and write JNDI data source user names and passwords.\n\n    ```properties\n    jdbc.read.jndi.name=[place your \"read\" data source JNDI name here]\n\n    jdbc.read.username=*[place your user name here]\n    jdbc.read.password=[place your password here]\n\n    jdbc.write.jndi.name=[place your \"read-write\" data source JNDI name here]\n\n    jdbc.write.username=[place your user name here]\n    jdbc.write.password=[place your password here]\n    ```\n\n1. Configure DXP to use the write data source (the data source whose prefix is `jdbc.write.`) to create the [Counter](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Counter) data source. A separate data source is always dedicated to the counter.\n\n    ```properties\n    counter.jdbc.prefix=jdbc.write.\n    ```\n\n1. Optionally validate the data connections to make sure bad connections are handled gracefully.\n\n    Some connection pools used with JDBC4 (check your driver's JDBC version) validate connections automatically. Other connection pools may require additional, vendor-specific connection validation properties---specify them in a Portal Properties file. Refer to your connection pool provider documentation for connection validation details.\n\n1. Enable the read-writer database configuration by copying the default [`spring.configs` and `spring.infrastructure.configs` Portal Properties](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Spring) to your `portal-ext.properties` file and add the following Spring configuration file paths to them.\n\n    Add to `spring.configs`:\n\n    ```\n    META-INF/dynamic-data-source-spring.xml\n    ```\n\n    Add to `spring.infrastructure.configs`:\n\n    ```\n    META-INF/dynamic-data-source-infrastructure-spring.xml\n    ```\n\n    For more information, see the [Spring configuration Portal Properties](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Spring).\n\nDXP uses a read data source, a write data source, and a counter data source the next time it starts."
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/04-liferay-portal-clustering/03-configure-documents-and-media-the-same-for-all-nodes.markdown",
    "content": "---\nheader-id: configure-documents-and-media-the-same-for-all-nodes\n---\n\n# Configure Documents and Media the Same for all Nodes\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/setting-up-liferay/clustering-for-high-availability/database-configuration-for-cluster-nodes.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nIn a cluster, Documents and Media must use the same\n[document repository configuration](/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration)\non all nodes. \n\nNote if you are using the `File System` or `Advanced File System` stores, the\nfile system must be accessible from all nodes (i.e., a network share), support\nconcurrent requests, and file locking.\n\n**Checkpoint**: Verify sharing works by executing these steps:\n\n1.  On Node 1 upload a document to the Documents and Media.\n\n2.  On Node 2 download the document. The download should be successful.\n\n3.  Repeat the test with reversed roles.\n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/04-liferay-portal-clustering/04-clustering-search.markdown",
    "content": "---\nheader-id: clustering-search\n---\n\n# Clustering Search\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/using-search/installing-and-upgrading-a-search-engine/introduction-to-installing-a-search-engine.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nSearch should always run on a separate environment from your @product@ server.\n@product@ supports \n[Elasticsearch](/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch), \n\n<!--Uncomment when Solr adapters is available. \nor \nSolr \nand either of those environments\n-->\n\nwhich can also be clustered. \n\nFor more information on how to cluster Elasticsearch, see \n[Elasticsearch's distributed cluster setup](https://www.elastic.co/guide/en/elasticsearch/guide/current/distributed-cluster.html). \n\nOnce @product@ servers have been properly configured as a cluster and the same\nfor Elasticsearch, change @product@ from *embedded* mode to *remote* mode. On\nthe first connection, the two sets of clustered servers communicate with each\nother the list of all IP addresses; in case of a node going down, the proper\nfailover protocols enable. Queries and indexes can continue to be sent for all\nnodes.\n\n<!-- Uncomment when Solr adapter is available. \nFor more information on how to cluster Solr, see \n[Apache Solr Cloud](https://cwiki.apache.org/confluence/display/solr/SolrCloud)\ndocumentation. \n\nOnce @product@ servers have been properly configured as a cluster, deploy the\nLiferay Solr 5 Adapter on all nodes. (This app is available for download from\nLiferay Marketplace\n[here](https://web.liferay.com/marketplace/-/mp/application/78803899).) Create a\nSolr Cloud (cluster) managed by _Apache Solr Zookeeper_. Connect the @product@\ncluster to Zookeeper and finish the final configurations to connect the two\nclusters.\n-->\n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/04-liferay-portal-clustering/05-enabling-cluster-link.markdown",
    "content": "---\nheader-id: enabling-cluster-link\n---\n\n# Enabling Cluster Link\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/setting-up-liferay/clustering-for-high-availability/configuring-cluster-link.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nEnabling Cluster Link automatically activates distributed caching.  The cache is\ndistributed across multiple @product@ nodes running concurrently. Cluster Link\ndoes\n[Ehcache](http://www.ehcache.org)\nreplication. The Ehcache global settings are in the\n[`portal.properties` file](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Ehcache).\n\nBy default Liferay does not copy cached entities between nodes. If an entity is\ndeleted or changed, for example, Cluster Link sends a *remove* message to the\nother nodes to invalidate this entity in their local caches. Requesting that\nentity on another node results in a cache *miss*; the entity is then retrieved\nfrom the database and put into the local cache. Entities added to one node's\nlocal cache are not copied to local caches of the other nodes. An attempt to\nretrieve a new entity on a node which doesn't have that entity cached results in\na cache *miss*. The miss triggers the node to retrieve the entity from the\ndatabase and store it in its local cache.\n\n![Figure 1: @product@'s cache algorithm is extremely efficient. ](../../../images/clustering-cache-efficient-algorithm.png)\n\nHere are the Cluster Link topics:\n\n- [Enabling Cluster Link](#enabling-cluster-link)\n- [Multicast Over UDP](#multicast-over-udp)\n- [Unicast Over TCP](#unicast-over-tcp)\n- [Using Different Control and Transport Channel Ports](#using-different-control-and-transport-channel-ports)\n- [Modifying the Cache Configuration with a Module](#modifying-the-cache-configuration-with-a-module)\n- [Conclusion](#conclusion)\n\n\n## Enabling Cluster Link\n\nTo enable Cluster Link, add this [portal\nproperty](/docs/7-2/deploy/-/knowledge_base/d/portal-properties) to a\n`portal-ext.properties` file:\n\n```properties\ncluster.link.enabled=true\n```\n\nThe\n[Cluster Link portal properties](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Cluster%20Link)\nprovide a default configuration that you can override to fit your needs.\n\nMany of the defaults use `localhost`, instead of a real address. In some\nconfigurations, however, `localhost` is bound to the internal loopback network\n(`127.0.0.1` or `::1`), rather than the host's real address. If for some reason\nyou need this configuration, you can make DXP auto detect the real address with\nthis property:\n\n```properties\ncluster.link.autodetect.address=www.google.com:80\n```\n\nSet it to connect to some other host that's contactable by your server. By\ndefault, it points to Google, but this may not work if your server is behind a\nfirewall. If you use each host's real address, you don't need to set the\nauto-detect address.\n\nCluster Link depends on\n[JGroups](http://www.jgroups.org)\nand provides an API for nodes to communicate. It can\n\n- Send messages to all nodes in a cluster\n- Send messages to a specific node\n- Invoke methods and retrieve values from all, some, or specific nodes\n- Detect membership and notify when nodes join or leave\n\nCluster Link contains an enhanced algorithm that provides one-to-many type\ncommunication between the nodes. This is implemented by default with JGroups's\nUDP multicast, but unicast and TCP are also available.\n\n## Multicast Over UDP\n\nWhen you enable Cluster Link, @product@'s default clustering configuration is\nenabled. This configuration defines IP multicast over UDP. @product@ uses two\ngroups of [channels from\nJGroups](http://www.jgroups.org/manual4/index.html#_channel) to implement this:\na control group and a transport group. If you want to customize the [channel\nproperties](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Cluster%20Link),\nyou can do so in `portal-ext.properties`:\n\n```properties\ncluster.link.channel.name.control=[your control channel name]\ncluster.link.channel.properties.control=[your control channel properties]\n```\n\nPlease see\n[JGroups's documentation](http://www.jgroups.org/manual4/index.html#protlist)\nfor channel properties. The default configuration sets many properties whose\nsettings are discussed there.\n\nMulticast broadcasts to all devices on the network. Clustered environments on\nthe same network communicate with each other by default. Messages and\ninformation (e.g., scheduled tasks) sent between them can lead to unintended\nconsequences. Isolate such cluster environments by either separating them\nlogically or physically on the network, or by configuring each cluster's\n`portal-ext.properties` to use different sets of\n[multicast group address and port values](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Multicast).\n\nJGroups sets a bind address automatically. If you want to set a manual address,\nyou can do this. By default, these are set to `localhost`:\n\n```properties\ncluster.link.bind.addr[\"cluster-link-control\"]=localhost\ncluster.link.bind.addr[\"cluster-link-udp\"]=localhost\n```\n\nIn some configurations, however, `localhost` is bound to the internal loopback\nnetwork (`127.0.0.1` or `::1`), rather than the host's real address. If for some\nreason you need this configuration, you can make @product@ auto detect its real\naddress with this property:\n\n```properties\ncluster.link.autodetect.address=www.google.com:80\n```\n\nSet it to connect to some other host that's contactable by your server. By\ndefault, it points to Google, but this may not work if your server is behind a\nfirewall. If you set the address manually using the properties above, you don't\nneed to set the auto-detect address.\n\nYour network configuration may preclude the use of multicast over TCP, so below\nare some other ways you can get your cluster communicating. Note that these\nmethods are all provided by JGroups.\n\n### Checkpoint:\n\n1.  If you are binding the IP address instead of using `localhost`, make sure\n    the right IP addresses are declared using these properties:\n\n    ```properties\n    cluster.link.bind.addr[\"cluster-link-control\"]=localhost\n    cluster.link.bind.addr[\"cluster-link-udp\"]=localhost\n    ```\n\n3.  Test your load and then optimize your settings if necessary.\n\n## Unicast over TCP\n\nIf your network configuration or the geographical distance between nodes prevents you from using UDP Multicast clustering, you can configure TCP Unicast. You must use this if you have a firewall separating any of your nodes or if your nodes are in different geographical locations.\n\n1.  Add a parameter to your app server's JVM on each node:\n\n    ```bash\n    -Djgroups.bind_addr=[node_ip_address]\n    ```\n\n    Use the node's IP address.\n\n2.  Select a discovery protocol for the nodes to use to find each other. Here are the protocol choices:\n\n    - TCPPing\n    - JDBCPing\n    - S3_Ping\n    - Rackspace_Ping\n\n    If you aren't sure which one to choose, use TCPPing. The rest of these steps use TCPPing\n\n3.  Extract the `tcp.xml` file from `$LIFERAY.HOME/osgi/marketplace/Liferay Foundation - Liferay Portal - Impl.lpkg/com​.​liferay​.​portal​.​cluster​.​multiple​-​[version].​jar/lib​/​jgroups​-​[version].​Final​.​jar/tcp.xml` to a location accessible to DXP, such as a folder called `jgroups` in the DXP web application's `WEB-INF/classes` folder.\n\n    ```\n    WEB-INF/classes/jgroups/tcp.xml\n    ```\n\n4.  In the `tcp.xml` file, set the TCP bind port to an unused port on your node. Here's an example:\n\n    ```xml\n    <TCP bind_port=\"7800\"/>\n    ```\n\n5.  In the `tcp.xml` file, make each node discoverable to TCPPing by specifying its IP address and an unused port on that node. Building off of the previous step, here's an example `<TCPPing>` element:\n\n    ```xml\n    <TCP bind_port=\"7800\"/>\n    <TCPPING async_discovery=\"true\"\n        initial_hosts=\"192.168.224.154[7800],192.168.224.155[7800]\"\n        port_range=\"0\"/>\n    ```\n\n    **Regarding Initial Hosts:**\n\n    - An alternative to specifying initial hosts in a TCP XML file is to specify them to your app server using a JVM argument like this: `-Djgroups.tcpping.initial_hosts=192.168.224.154[7800],192.168.224.155[7800]`.\n    - Make sure the initial hosts value accounts for all your nodes. If `initial_hosts` is not specified in a TCP XML file or in a JVM argument, `localhost` is the initial host.\n\n6.  Copy your `tcp.xml` file to each node, making sure to set the TCP bind port to the node's bind port. On the node with IP address `192.168.224.155`, for example, configure TCPPing like this:\n\n    ```xml\n    <TCP bind_port=\"7800\"/>\n    <TCPPING async_discovery=\"true\"\n        initial_hosts=\"192.168.224.154[7800],192.168.224.155[7800]\"\n        port_range=\"0\"/>\n    ```\n\n7. Modify the [Cluster Link properties](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Cluster%20Link) in the node's `portal-ext.properties` file to enable Cluster Link and point to the TCP XML file for each Cluster Link channel:\n\n```properties\ncluster.link.enabled=true\ncluster.link.channel.properties.control=/jgroups/tcp.xml\ncluster.link.channel.properties.transport.0=/jgroups/tcp.xml\n```\n\nThe JGroups configuration demonstrated above is typically all that Unicast over TCP requires. However, in a very specific case, if *(and only if)* cluster nodes are deployed across multiple networks, then the parameter `external_addr` must be set on each host to the external (public IP) address of the firewall. This kind of configuration is usually only necessary when nodes are geographically separated. By setting this, clustered nodes deployed to separate networks (e.g. separated by different firewalls) can communicate together. This configuration may be flagged in security audits of your system. See [JGroups documentation](http://www.jgroups.org/manual4/index.html#_transport_protocols) for more information.\n\n> **Note:** The `singleton_name` TCP attribute was deprecated in JGroups v4.0.0 and has therefore been removed since Liferay DXP 7.2 SP1 and Liferay Portal CE GA2 which use JGroups v 4.1.1-Final.\n\nYou're now set up for Unicast over TCP clustering! Repeat either TCPPING process for each node you want to add to the cluster.\n\n### JDBC Ping\n\nRather than use TCP Ping to discover cluster members, you can use a central database accessible by all the nodes to help them find each other. Cluster members write their own and read the other members' addresses from this database. To enable this configuration, replace the `TCPPING` tag with the corresponding `JDBC_PING` tag:\n\n```xml\n<JDBC_PING\n    connection_url=\"[place the URL to your database here]\"\n    connection_username=\"[place your user name here]\"\n    connection_password=\"[place your password here]\"\n    connection_driver=\"[place your driver name here]\"/>\n```\n\nFor example JDBC connection values, please see [Database Templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates). For further information about JDBC Ping, please see the [JGroups Documentation](http://www.jgroups.org/manual4/index.html#DiscoveryProtocols).\n\n### S3 Ping\n\nAmazon S3 Ping can be used for servers running on Amazon's EC2 cloud service. Each node uploads a small file to an S3 bucket, and all the other nodes read the files from this bucket to discover the other nodes. When a node leaves, its file is deleted.\n\nTo configure S3 Ping, replace the `TCPPING` tag with the corresponding `S3_PING` tag:\n\n```xml\n<S3_PING\n    secret_access_key=\"[SECRETKEY]\"\n    access_key=\"[ACCESSKEY]\"\n    location=\"ControlBucket\"/>\n```\n\nSupply your Amazon keys as values for the parameters above. For further information about S3 Ping, please see the [JGroups Documentation](http://www.jgroups.org/manual4/index.html#_s3_ping).\n\n### Other Pings\n\nJGroups supplies other means for cluster members to discover each other, including Rackspace Ping, BPing, File Ping, and others. Please see the [JGroups Documentation](http://www.jgroups.org/manual4/index.html#DiscoveryProtocols) for information about these discovery methods.\n\nThe control and transport channels can be configured to use different ports.\n\n## Using Different Control and Transport Channel Ports\n\nUsing separate control and transport channel ports lets you monitor control and transport traffic and helps you isolate information to diagnose problems. These steps use Unicast over TCPPing to demonstrate the approach.\n\n1.  Add a parameter to your app server's JVM on each node:\n\n    ```bash\n    -Djgroups.bind_addr=[node_ip_address]\n    ```\n\n2.  Extract the `tcp.xml` file from `$LIFERAY.HOME/osgi/marketplace/Liferay Foundation - Liferay Portal - Impl.lpkg/com​.​liferay​.​portal​.​cluster​.​multiple​-​[version].​jar/lib​/​jgroups​-​[version].​Final​.​jar/tcp.xml` to a location accessible to DXP, such as a folder called `jgroups` in the DXP web application's `WEB-INF/classes` folder.  \n\n3.  Make a copy of the `tcp.xml` in the same location and rename both files, designating one for the control channel and the other for the transport channel. For example, you could use these file names:\n\n    -   `tcp-control.xml`\n\n    -   `tcp-transport.xml`\n\n5.  Modify the [Cluster Link properties](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Cluster%20Link) in the node's `portal-ext.properties` file to enable Cluster Link and point to the TCP XML file for each Cluster Link channel:\n\n    ```properties\n    cluster.link.enabled=true\n    cluster.link.channel.properties.control=/jgroups/tcp-control.xml\n    cluster.link.channel.properties.transport.0=/jgroups/tcp-transport.xml\n    ```\n\n6.  Modify each `tcp-*.xml` file's the TCP and TCPPing elements to account for each node's IP address and bind port.\n\n    If you're vertically clustering (i.e., you have multiple servers running on the same physical or virtual system), every channel must use a unique unused bind port for discovery communication. In each `tcp-*.xml` file, assign the TCP tag's `bind_port` attribute to a unique, unused port.\n\n    For example, your first two nodes might assign these bind ports:\n\n    | Node   | Properties File     | Port   |\n    | :----- | :------------------ | :----- |\n    | Node 1 | `tcp-control.xml`   | `7800` |\n    | Node 1 | `tcp-transport.xml` | `7801` |\n    | Node 2 | `tcp-control.xml`   | `7802` |\n    | Node 2 | `tcp-transport.xml` | `7803` |\n\n    Here are example TCP and TCPPing elements using the bind ports on nodes running on the same system (i.e., same IP address):\n\n    **Node 1 `tcp-control.xml`**\n\n    ```xml\n    <TCP bind_port=\"7800\"/>\n    <TCPPING async_discovery=\"true\"\n        initial_hosts=\"192.168.224.154[7800],192.168.224.154[7802]\"\n        port_range=\"0\"/>\n    ```\n\n    **Node 1 `tcp-transport.xml`**\n\n    ```xml\n    <TCP bind_port=\"7801\"/>\n    <TCPPING async_discovery=\"true\"\n        initial_hosts=\"192.168.224.154[7801],192.168.224.154[7803]\"\n        port_range=\"0\"/>\n    ```\n\n    **Node 2 `tcp-control.xml`**\n\n    ```xml\n    <TCP bind_port=\"7802\"/>\n    <TCPPING async_discovery=\"true\"\n        initial_hosts=\"192.168.224.154[7800],192.168.224.154[7802]\"\n        port_range=\"0\"/>\n    ```\n\n    **Node 2 `tcp-transport.xml`**\n\n    ```xml\n    <TCP bind_port=\"7803\"/>\n    <TCPPING async_discovery=\"true\"\n        initial_hosts=\"192.168.224.154[7801],192.168.224.154[7803]\"\n        port_range=\"0\"/>\n    ```\n\nIf you have added entities that can be cached or you want to tune the cache configuration for your system, you can do so using a module.\n\n## Modifying the Cache Configuration with a Module\n\nIt's recommended to test your system under a load that best simulates the kind\nof traffic your system must handle. If you serve a lot of message\nboard messages, your script should reflect that. If web content is the core of\nyour site, your script should reflect that too.\n\nAs a result of a load test, you may find that the default distributed cache\nsettings aren't optimized for your site. In this case, tweak the settings using\na module. Follow instructions for\n[Overriding Cache](/docs/7-2/frameworks/-/knowledge_base/f/overriding-cache).\n\nYou can install the module on each node and change the settings without taking\ndown the cluster. This is a great benefit, but beware: since Ehcache doesn't\nallow for changes to cache settings while the cache is alive, reconfiguring a\ncache while the server is running flushes the cache.\n\n## Conclusion\n\nOnce you've configured your cluster, you can start it. A log file message shows\nyour cluster's  name (e.g., `cluster=liferay-channel-control`):\n\n```bash\n-------------------------------------------------------------------\nGMS: address=oz-52865, cluster=liferay-channel-control, physical address=192.168.1.10:50643\n-------------------------------------------------------------------\n```\n\nCongratulations! Your cluster is using Cluster Link.\n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/04-liferay-portal-clustering/06-auto-deploy-to-all-nodes.markdown",
    "content": "---\nheader-id: auto-deploy-to-all-nodes\n---\n\n# Auto Deploy to All Nodes\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/setting-up-liferay/clustering-for-high-availability.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAll modules and WAR files you deploy onto the cluster must be deployed to all\ncluster nodes. Because @product@ \n[installs applications as OSGi bundles](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator), \nyou cannot rely on your application server's means of installing WAR files (even\nif you only intend to install WAR files) to deploy an application to the entire\ncluster. Instead, place the application in each node's auto deploy folder (e.g.,\n`[Liferay Home]/deploy`). \n\nThis, as you might imagine, can be done with a script. Write a shell script that\nuploads applications to each node using sftp or some other service. This way,\nwhen you deploy an application, it uploads to each node's auto deploy folder and\ninstalls to @product@ on each node. \n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/05-updating-a-cluster/01-intro.markdown",
    "content": "---\nheader-id: updating-a-cluster\n---\n\n# Updating a Cluster\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/maintaining-clustered-installations.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nMaintaining a\n[cluster](/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering)\nis a big responsibility. It includes deploying new and updated plugins and\nmodules, applying\n[fix packs](/docs/7-2/deploy/-/knowledge_base/d/maintaining-liferay), \nmaking configuration changes, and more. Maximizing server uptime and minimizing\nrisks take priority when applying changes. @product@ supports using standard\ncluster maintenance techniques. \n\n-   [Rolling restarts](/docs/7-2/deploy/-/knowledge_base/d/using-rolling-restarts): \n    Nodes are shut down and updated one at a time. \n-   [Blue-green deployment](/docs/7-2/deploy/-/knowledge_base/d/other-cluster-update-techniques):\n    Blue-green involves duplicating the current environment (*blue*\n    environment), updating the duplicate (*green* environment), and cutting over\n    users to the updated environment (green). \n\nThe techniques are compared below. \n\n**Cluster Update Techniques**\n\n| Update | &nbsp;Rolling Restart| &nbsp;Blue-green |\n| ------ | :------------------- | :--------------- |\n| Plugin/module installation | Supported | Supported | \n| Plugin/module update (backward-compatible data/schema changes) | Supported | Supported | \n| Plugin/module update (non-backward-compatible data/schema changes) [1](#one) | Not supported | Supported | \n| Fix pack installation and removal (revertable fix pack) | Supported | Supported | \n| Fix pack installation (non-revertible fix pack) | Not supported | Supported | \n| Cluster code changes [2](#two) | Not supported | Supported | \n| Portal property changes | Supported | Supported | \n| System Setting changes via configuration admin files | Supported | Supported | \n| Application server updates | Supported | Supported | \n| JVM setting changes | Supported | Supported | \n| New Java version (minor) | Supported | Supported | \n| New Java version (major) | Not supported | Supported | \n\n[<a name=\"one\">1</a>] Data and data schema changes that are not \nbackward-compatible include, but are not limited to these:\n\n-   Modifying data in existing columns\n-   Dropping columns\n-   Changing column types\n-   Changing data formats used in columns (such as changing from XML to JSON)\n-   Updating a Service Builder service module's data schema to a version \n    outside of the module's required data schema range. \n    <!-- Add back link for 'required data schema range' once \n    creating-an-upgrade-process-for-your-app#specifying-the-schema-version \n    article is available\n    -->\n    A module's `Liferay-Require-SchemaVersion` (specified in its `bnd.bnd`) must\n    match the module's schema version value in the `Release_` table. Installing\n    a module with a new schema version updates the `Release_` table with\n    that schema version and triggers a data upgrade process. If you install such\n    a module on one node, the schema version in the `Release_` table no longer\n    matches the `Liferay-Require-SchemaVersion` of the modules on the other\n    nodes, and the module's Service Builder services become unavailable until\n    the module is installed on the other nodes. Such changes cannot be reverted:\n    the database must be restored from a backup. These schema version changes\n    must be applied while all nodes are shut down. \n\n[<a name=\"two\">2</a>] Cluster communication must stay intact. For this reason, cluster code must not\nbe updated in rolling restarts. The Customer Portal identifies DXP fix packs\nthat contain such changes as non-revertible. Here are packages you must not\nchange in rolling restarts:\n    \n- `com.liferay.portal.kernel.cluster`\n- `com.liferay.portal.kernel.cluster.*`\n- `com.liferay.portal.kernel.exception.NoSuchClusterGroupException`\n- `com.liferay.portal.kernel.scheduler.multiple`\n- `com.liferay.portal.kernel.scheduler.multiple.*`\n- `com.liferay.portal.cache.multiple`\n- `com.liferay.portal.cache.multiple.*`\n- `com.liferay.portal.scheduler.multiple`\n- `com.liferay.portal.scheduler.multiple.*`\n    \nSince eligible changes should be done with rolling restarts, it's explained\nfirst. \n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/05-updating-a-cluster/02-using-rolling-restarts.markdown",
    "content": "---\nheader-id: using-rolling-restarts\n---\n\n# Rolling Restarts\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/maintaining-clustered-installations/rolling-restarts.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe rolling restart cluster maintenance process involves shutting down and\nupdating nodes one at a time (while the other nodes are running) until they're\nall updated. It maximizes uptime while you update your cluster. Rolling restarts\ncan be used in container and image based environments. \n\n| **Note:** Rolling restart does not include concepts for blue-green (separate,\n| but identical environments) architectures, as these concepts specifically\n| address multi-cluster style developments.\n\nHere are the rolling restart steps:\n\n1.  Shut down one cluster node (JVM instance). \n\n2.  Update/modify the deployment for that node (see the maintenance scenarios \n    that follow). \n\n3.  Start the node. \n\n4.  Repeat these steps for all other cluster nodes. \n\nMaintenance scenarios vary in how they behave in rolling restarts. For example,\nUI changes in a plugin update are only visible on the updated nodes. Users on\nnodes that haven't been updated don't see the UI changes. Maintenance scenarios\nmight have specific cases that cannot be performed in rolling restarts---the\nscenario descriptions mention these cases. \n\nThe maintenance scenarios eligible for rolling restart are described below. \n\n## New Modules and Plugins\n\nFor a new plugin or module (one that does not already exist in the cluster) to\nbe eligible for rolling restart it must not modify data, or delete or rename\ndatabase columns in a way that breaks compatibility with existing plugins or\nmodules. \n\n## Updating Existing Modules and Plugins\n\nFor a new version of an existing plugin or module to be eligible for rolling\nrestart, it must not modify data or delete or rename database columns in a way\nthat breaks compatibility with the existing version of the plugin or module. \n\n## Applying Fix Packs (DXP only)\n\nThe Customer Portal identifies\n[fix packs](/docs/7-2/deploy/-/knowledge_base/d/maintaining-liferay)\nthat are not revertible, and therefore ineligible for rolling restart. All other\nfix packs are eligible. \n\n## Reverting Fix Packs (DXP only)\n\nRevertible fix packs can be removed in rolling restarts. \n\n## Portal Properties controlled by `portal-ext.properties`\n \n[Portal Properties](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html) \nfile changes can be applied in rolling restarts. \n\n## System Settings controlled by Configuration Admin Files\n\n[System configuration](/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files)\nfiles can be applied in rolling restarts. \n\n## Application Server or JVM setting modifications\n\nModifications to application server and JVM settings can be done in rolling\nrestarts. \n\n## Java Version Updates\n\nMinor version updates of Java can be applied in rolling restarts. Major version\nupdates are not supported in rolling restarts, and should instead be done when\nall cluster nodes are shut down. \n\nAll rolling restart eligible updates can be applied using the rolling restart\nsteps listed earlier. Other updates must be done differently as described next. \n\n## Related Topics\n\n[@product@ Clustering](/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering)\n\n[Maintaining @product@](/docs/7-2/deploy/-/knowledge_base/d/maintaining-liferay)\n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/05-updating-a-cluster/03-other-cluster-update-techniques.markdown",
    "content": "---\nheader-id: other-cluster-update-techniques\n---\n\n# Blue-Green Deployment\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/maintaining-clustered-installations/blue-green-deployments.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nBlue-green is a deployment technique in which you duplicate your production\nenvironment (the *blue* environment) and modify the duplicate (the *green*\nenvironment) with software and data changes. When you've successfully tested the\ngreen environment, you cut users over from the blue environment to the green\nenvironment. Blue-green eliminates system down time. \n\nData schema and data changes require special attention. Custom plugin/module\ndata schema changes that break compatibility with existing code must be\nintroduced over several releases in which the data is transitioned and\nmaintained in old and new columns until the old columns are unnecessary. \n\nData and schema changes require these steps:\n\n1.  Create a new column.\n\n2.  Copy the data to the new column.\n\n3.  Maintain both columns until the old column is no longer used by any cluster \n    nodes. \n\n4.  Delete the column in the next release. \n\nFor more information, refer to these blue-green deployment articles: \n\n-   [BlueGreenDeployment](http://martinfowler.com/bliki/BlueGreenDeployment.html)\n\n-   [Implementing Blue-Green Deployments with AWS](https://www.thoughtworks.com/insights/blog/implementing-blue-green-deployments-aws)\n\n## Related Topics\n\n[Rolling Restarts](/docs/7-2/deploy/-/knowledge_base/d/using-rolling-restarts)\n\n[@product@ Clustering](/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering)\n\n[Maintaining @product@](/docs/7-2/deploy/-/knowledge_base/d/maintaining-liferay)\n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/06-configuring-remote-staging-clustered.markdown",
    "content": "---\nheader-id: configuring-remote-staging-in-a-clustered-environment\n---\n\n# Configuring Remote Staging in a Clustered Environment\n\n[TOC levels=1-4]\n\nIf you're running @product@ as a\n[clustered environment](/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering)\nand you want to use remote staging, you must configure it properly for a\nseamless experience. In this tutorial, you'll learn how to set up remote staging\nin an example clustered environment scenario. The example environment assumes\nyou have\n\n- a Staging instance with database configurations and a file repository\n  different from the cluster nodes.\n- a balancer responsible for managing the traffic flow between the cluster's\n  nodes.\n- two nodes that call two Liferay app servers (e.g., *App Server 1* and *App\n  Server 2*), both of which are connected to the same database.\n\n![Figure 1: This is the assumed setup for your clustered environment.](../../images/remote-staging-clustering.png)\n\nThe steps below also assume your web tier, application tier, and cluster\nenvironment are already configured. You may need to adjust the configurations in\nthis tutorial to work with your specific configuration.\n\nLet's begin!\n\n1.  You must secure the communication made between your nodes and Staging\n    server. Add the following property to both app servers' and Staging server's\n    `portal-ext.properties` file:\n\n        tunneling.servlet.shared.secret=[secret]\n\n    This secret key denies other portals access to your configured portal\n    servers. If you'd like to set your secret key using hexadecimal encoding,\n    also set the following property in your `portal-ext.properties` files:\n\n        tunneling.servlet.shared.secret.hex=true\n\n    | **Note:** The following key lengths are supported by the available\n    | encryption algorithms:\n    | \n    | - *AES:* 128, 192, and 256-bit keys\n    | - *Blowfish:* 32-448 bit keys\n    | - *DESede (Triple DES):* 56, 112, or 168 bit keys (Liferay places an\n    |   artificial limit on the minimum key length and does not support the 56-bit\n    |   key length)\n    | \n    | For example, you can use [OpenSSL](https://www.openssl.org/) to generate a\n    | 128-bit AES key:\n    | \n    |     openssl enc -aes-128-cbc -k abc123 -P -md sha1\n\n2.  You must allow the connection between the configured IPs of your app servers\n    and the Staging server. Open your remote Liferay server's\n    `portal-ext.properties` file and add the following properties:\n\n        tunnel.servlet.hosts.allowed=127.0.0.1,SERVER_IP,[STAGING_IP]\n        tunnel.servlet.https.required=false\n\n    The `[STAGING_IP]` variable must be replaced by the staging server's IP\n    addresses. The `SERVER_IP` constant can remain set for this property; it's\n    automatically replaced by the Liferay server's IP addresses.\n\n3. If you're validating IPv6 addresses, you must configure the app server's JVM\n    to not force the usage of IPv4 addresses. For example, if you're using\n    Tomcat, add the following attribute in the\n    `$TOMCAT_HOME\\bin\\setenv.[bat|sh]` file.\n\n            `-Djava.net.preferIPv4Stack=false`\n\n4.  Restart both app servers for the new properties to take effect.\n\n5.  Configure the *TunnelAuthVerifier* property for your nodes' app servers.\n    There are two ways to do this:\n\n    - **Use a `.config` file (recommended):** In the `$LIFERAY_HOME/osgi/configs`\n      folder of one of your node @product@ instances, create (if necessary) a\n      `com.liferay.portal.security.auth.verifier.tunnel.module.configuration.TunnelAuthVerifierConfiguration-default.config`\n      file and insert the properties listed below. Creating one `.config` file\n      configures all cluster nodes the same way. For more information on\n      `.config` files, see the\n      [Understanding System Configuration Files](/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files)\n      article.\n\n            enabled=true\n            hostsAllowed=127.0.0.1,SERVER_IP,STAGING_IP\n            serviceAccessPolicyName=SYSTEM_USER_PASSWORD\n            urlsIncludes=/api/liferay/do\n\n    - **Via System Settings:** Navigate to the *Control Panel* &rarr;\n      *Configuration* &rarr; *System Settings* &rarr; *Foundation* &rarr;\n      *Tunnel Auth Verifiers*. Click on the */api/liferay/do* configuration\n      entry and add the Staging IP address to the *Hosts allowed* field. If you\n      choose to configure the *TunnelAuthVerifier* this way, you **must** do\n      this for all nodes (e.g., App Server 1 and App Server 2).\n\n6.  On your Staging instance, navigate to the Site Administration portion of the\n    Product Menu and select *Publishing* &rarr; *Staging*. Then select *Remote\n    Live*.\n\n    ![Figure 2: When selecting the Remote Staging radio button, you're given a list of options to configure.](../../images/remote-staging-menu.png)\n\n7.  For the Remote Host/IP field, insert the balancer's IP of your web tier.\n    Configuring the Staging instance with the balancer's IP ensures the\n    availability of the environment at the time of publication from staging to\n    live.\n\n8.  Enter the port on which the balancer is running into the Remote Port field.\n\n9.  Insert the remote site ID of your app servers into the Remote Site ID field.\n    The site ID of all your app servers are the same since they are configured\n    for the same database and are shared between nodes.\n\n    Navigate to the Site Administration portion of the Product Menu and select\n    *Configuration* &rarr; *Site Settings* to find the site ID.\n\n10. Save the Remote Live settings.\n\nThat's it! You've configured remote staging in your clustered environment.\n"
  },
  {
    "path": "en/deployment/articles/02-configuring-liferay/07-content-delivery-network.markdown",
    "content": "---\nheader-id: content-delivery-network\n---\n\n# Content Delivery Network\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/setting-up-liferay/using-a-cdn.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nA Content Delivery Network (CDN) is an network of servers deployed in multiple\ndata centers that contain your static content. When users hit your site, that\nstatic content is loaded from a server with geographical proximity to the user,\nspeeding up requests. \n\nHere, you'll first discover the perks of using a CDN and learn about general\nguidelines for using a CDN with @product@. Then, you'll configure a CDN. It's\ntime to distribute your content around the world!\n\n## Using CDN for Performance Enhancements\n\nA CDN serves static web resources to users. These resources (images, CSS files,\nJavaScript files, etc.) are stored on multiple servers around the world. When\nrequested, the resources are retrieved from the server nearest to the user.\n\nThe CDN functions as a caching proxy. This means that once static content is\ncopied to a local server, it is stored in a cache for quick and easy retrieval.\nThis drastically improves latency time, because browsers can download static\nresources from a local server down the street instead of halfway around the\nworld. A user's request to the CDN for content is directed to specific server\nmachine based on an algorithm that finds the server closest to the user. The\nfigure below shows a visual representation of using geographical proximity to\nimprove latency.\n\n![Figure 1: The red lines on the map represent the required distances traveled by requests from a server to the user. Using CDN allows a user to request static resources from a much closer local server, improving download times.](../../images/cdn-map.png)\n\nBecause of the reduced wait time for requests and reduced load on your\napplication server, a CDN is a great option to improve performance. Using a CDN\nwith @product@, however, has some restrictions.\n\n## Liferay CDN Requirements\n\n@product@ only works with CDNs that can dynamically retrieve requested\nresources. Dynamic resources change over time or via interaction with end users\nand thus cannot be cached. For this reason, check with your CDN provider to make\nsure you don't have to upload anything manually in order for the CDN to work.\nThe CDN must automatically fetch the content.\n\nThe CDN must work like a transparent proxy. A request first goes to the CDN. If\nthe CDN doesn't have the requested resource, the CDN makes an identical request\nback to the origin (@product@), caches the resource, then serves the resource.\n\nOnce you're using a CDN (see below), it serves both portal resources and plugin\nresources (e.g., theme resources or JavaScript files referenced from a plugin's\n`liferay-portlet.xml` file). The CDN only serves resources that are included in\na plugin. It does not serve resources that are dynamically loaded from external\nsources.\n\nTo get the CDN URL for a resource, developers should replace the portal host in\nthe resource path with `themeDisplay.getCDNDynamicResourcesHost()`. Prefix\nresources with the CDN host name. Don't manually upload any resources to the CDN\nor put anything on the CDN which requires permission checking or complex policy\naccess.\n\nThere are several portal properties for configuring your CDN to suit your\nneeds. You'll learn how to do this next.\n\n## Configuring @product@ to Use a CDN\n\nNow that you understand what a CDN accomplishes and how it's used, it's time to\nset one up for yourself. You can set your CDN and its properties using two\ndifferent methods:\n\n1. By editing your portal properties file\n\n2. By using the Control Panel\n\nTo configure your CDN via a properties file, create a\n`portal-ext.properties` file in your\n[Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\nfolder and set the appropriate\n[Content Delivery Network properties](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Content%20Delivery%20Network).\n\nOnce you configure your CDN host, @product@ generates URLs to the static assets\nthat replace the old host with your new CDN host so they are automatically\ncached and served afterwards by the CDN.\n\nTo configure your CDN in the Control Panel, navigate to *Control Panel* &rarr;\n*Configuration* &rarr; *Instance Settings*. In the main configuration, there are\nthree fields related to CDNs:\n\n- *CDN Host HTTP*\n- *CDN Host HTTPS*\n- *Enable CDN Dynamic Resources*\n\n![Figure 2: The Control Panel lets you configure your portal's CDN.](../../images/cdn-control-panel.png)\n\nThese properties are exactly the same as the ones you can specify in your\n`portal-ext.properties`. Make sure to visit the Content Delivery Network section\nof the\n[portal.properties](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Content%20Delivery%20Network)\nreference document if you don't know how to fill in the CDN fields. Once you're\nfinished, click *Save* and your old host is replaced with your new CDN host for\nstatic content.\n\nAs you can see, configuring a CDN is easy and can drastically reduce latency\ntime and improve performance.\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/01-intro.markdown",
    "content": "---\nheader-id: installing-a-search-engine\n---\n\n# Installing a Search Engine\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/using-search/installing-and-upgrading-a-search-engine/installing-a-search-engine.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nA search engine is a critical component of your @product@ installation. If\nyou're here, you probably know the basics already and want to configure a\nsearch engine for your @product@ deployment.\n\nElasticsearch, a highly scalable, full-text search engine, is installed by\ndefault, as an embedded server. Elasticsearch is well-supported and almost\ncertainly meets any search and indexing need you have, but you must not use the\n[embedded version in your production deployment](/docs/7-2/deploy/-/knowledge_base/d/elasticsearch#embedded-vs-remote-operation-mode).\n\nLearn to configure a remote Elasticsearch server or cluster\n[here](/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch).\n\n[Solr](http://lucene.apache.org/solr)\nis another capable and popular search engine supported in @product@.\n\nLearn to configure a remote Solr server or cluster\n[here](/docs/7-2/deploy/-/knowledge_base/d/installing-solr). But first, make\nsure you understand the disparity in functionality between the supported search\nengines.\n\n## Choosing a Search Engine\n\nElasticsearch and Solr are both supported, but there are limitations to\nLiferay's Solr integration. To make use of some features, you must choose\nElasticsearch.\n\n### End User Feature Limitations of Liferay's Solr Integration\n\n- [Liferay Commerce](https://learn.liferay.com/commerce-2.x/index.html)\n- [Workflow Metrics](https://help.liferay.com/hc/en-us/articles/360029042071-Workflow-Metrics-The-Service-Level-Agreement-SLA-)\n- [Custom Filter search widget](/docs/7-2/user/-/knowledge_base/u/filtering-search-results-with-the-custom-filter-widget)\n- [The Low Level Search Options widget](/docs/7-2/user/-/knowledge_base/u/low-level-search-options-searching-additional-or-alternate-indexes)\n- [Search Tuning: Customizing Search Results](https://help.liferay.com/hc/en-us/articles/360034473872-Search-Tuning-Customizing-Search-Results)\n- [Search Tuning: Synonyms](https://help.liferay.com/hc/en-us/articles/360034473852-Search-Tuning-Synonym-Sets)\n\n### Developer Feature Limitations of Liferay's Solr Integration\n\nImplementation for the following APIs may be added in the future, but they are\nnot currently supported by Liferay's Solr connector.\n\n- From Portal Core (Module: `portal-kernel`, Artifact:\n    `com.liferay.portal.kernel`):\n    - `com.liferay.portal.kernel.search.generic.NestedQuery`\n    - `com.liferay.portal.kernel.search.filter`:\n        - `ComplexQueryPart`\n        - `GeoBoundingBoxFilter`\n        - `GeoDistanceFilter`\n        - `GeoDistanceRangeFilter`\n        - `GeoPolygonFilter`\n- From the Portal Search API (Module: `portal-search-api`, Artifact:\n    `com.liferay.portal.search.api`):\n    - `com.liferay.portal.search.filter`:\n        - `ComplexQueryPart`\n        - `TermsSetFilter`\n    - `com.liferay.portal.search.geolocation.*`\n    - `com.liferay.portal.search.highlight.*`\n    - `com.liferay.portal.search.query.function.*`\n    - `com.liferay.portal.search.query.*`:\n    - `com.liferay.portal.search.script.*`\n    - `com.liferay.portal.search.significance.*`\n    - `com.liferay.portal.search.sort.*`: only `Sort`,`FieldSort`, and\n        `ScoreSort` are supported\n- Portal Search Engine Adapter API (Module: `portal-search-engine-adapter-api`,\n    Artifact: `com.liferay.portal.search.engine.adapter.api`)\n    - `com.liferay.portal.search.engine.adapter.cluster.*`\n    - `com.liferay.portal.search.engine.adapter.document.UpdateByQueryDocumentRequest`\n    - `com.liferay.portal.search.engine.adapter.index.*`: only `RefreshIndexRequest` is supported\n    - `com.liferay.portal.search.engine.adapter.search.*`:\n        - `MultisearchSearchRequest`\n        - `SuggestSearchRequest`\n    - `com.liferay.portal.search.engine.adapter.snapshot.*`\n\nLiferay Commerce requires the `TermsSetFilter` implementation, only available\nin the Elasticsearch connector.\n\n### Elasticsearch Java Distribution Compatibility\n\nAnother factor to consider in your search engine selection is JDK version. The\nsearch engine and @product@ must use the same Java version and distribution\n(e.g., Oracle Open JRE 1.8.0_201). Consult the\n[Elasticsearch compatibility matrix](https://www.elastic.co/support/matrix#matrix_jvm)\nand the\n[@product@ compatibility matrix](https://help.liferay.com/hc/en-us/sections/360002103292-Compatibility-Matrix)\nto learn more about supported JDK distributions and versions. This consideration\nis not necessary for Solr, because no JVM level serialization happens between\nthe servers. All communication occurs at the HTTP level.\n\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/02-elasticsearch/01-elasticsearch-intro.markdown",
    "content": "---\nheader-id: elasticsearch\n---\n\n# Elasticsearch\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/using-search/installing-and-upgrading-a-search-engine/elasticsearch.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nElasticsearch is an open source, highly scalable, full-text search and\nanalytics engine.\n\nBy default, Elasticsearch runs as an embedded search engine, which is useful\nfor development and testing but is not supported in production. In production\nenvironments you must run Elasticsearch in remote mode, as a separate server or\ncluster. This guide walks you through the process of configuring Elasticsearch\nin remote mode.\n\n![Figure 1: To see information about the currently connected search engine, go to _Control Panel_ &rarr; _Configuration_ &rarr; _Search_.](../../../images/search-admin-engineinfo.png)\n\n| **Note:** Although Elasticsearch 6.5 is shipped as the embedded Elasticsearch\n| server version, Elasticsearch 7 is the most recent supported Elasticsearch\n| version for @product-ver@. Installing Elasticsearch 7 requires that you are\n| running Service Pack 1/Fix Pack 2 or later (GA2 or later for CE users).\n| Elasticsearch 6.8.x is also supported. See the [compatibility matrix for exact versions](https://help.liferay.com/hc/en-us/articles/360016511651).\n\nIf you'd rather use Solr, it's also supported. See the documentation on\n[Installing Solr](/docs/7-2/deploy/-/knowledge_base/d/installing-solr) \nif you're interested.\n\nTo get up and running quickly with Elasticsearch as a remote server, refer to\nthe \n[Installing Elasticsearch article](/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch).\nIncluded there are basic instructions for installing and configuring\nElasticsearch in a single server environment. Additional articles include more\ndetails and information on configuring and tuning Elasticsearch. \n\nThese terms are useful to understand as you read this guide:\n\n-  *Elasticsearch Home* refers to the root folder of your unzipped Elasticsearch\n   installation (for example, `elasticsearch-7.4.1`). \n\n-  [*Liferay Home*](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\n   refers to the root folder of your @product@ installation. It contains the\n   `osgi`, `deploy`, `data`, and `license` folders, among others.\n\n## Embedded vs. Remote Operation Mode\n\nWhen you start @product@, this message is displayed in the log: \n\n    2019-04-29 09:59:02.276 WARN  [Elasticsearch initialization thread][EmbeddedElasticsearchConnection:288] Liferay is configured to use embedded Elasticsearch as its search engine. Do NOT use embedded Elasticsearch in production. Embedded Elasticsearch is useful for development and demonstration purposes. Refer to the documentation for details on the limitations of embedded Elasticsearch. Remote Elasticsearch connections can be configured in the Control Panel.\n\nWhen you start @product@, Elasticsearch is already running in embedded mode.\n@product@ runs an Elasticsearch node in the same JVM so it's easy to test-drive\nwith minimal configuration. Running both servers in the same process has\ndrawbacks:\n\n-  Elasticsearch must use the same JVM options as @product@.\n-  @product@ and Elasticsearch compete for the same system resources. \n\n| **Note:** While it's not a supported production configuration, installing\n| Kibana to monitor the embedded Elasticsearch server is useful during\n| development and testing. Just be aware that you must install the\n| [OSS only Kibana build](https://www.elastic.co/downloads/kibana-oss).\n\nYou wouldn't run an embedded database like HSQL in production, and you shouldn't\nrun Elasticsearch in embedded mode in production either. Instead, run\nElasticsearch in *remote operation mode*, as a standalone server or cluster of\nserver nodes.\n\n## Troubleshooting Elasticsearch Integration\n\nSometimes things don't go as planned. If you've set up @product@ with\nElasticsearch in remote mode, but @product@ can't connect to Elasticsearch, check\nthese things:\n\n**Cluster name:** \n: The value of the `cluster.name` property in `elasticsearch.yml` must match\nthe `clusterName` property configured in the @product@ Elasticsearch connector.\n\n**Transport address:** \n: The value of the `transportAddresses` property in the Elasticsearch connector\nconfiguration must contain at least one valid host and port where an\nElasticsearch node is running. If @product@ is running in embedded mode, and\nyou start a standalone Elasticsearch node or cluster, it detects that port\n`9300` is taken and switches to port `9301`. If you then set Liferay's\nElasticsearch connector to remote mode, it continues to look for Elasticsearch\nat the default port (`9300`).\n\nThe following articles cover the Liferay Connector to Elasticsearch's\nconfiguration options in more detail.\n\n**Cluster Sniffing (Additional Configurations):**\n: Elasticsearch clusters can have multiple node \n[types](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-node.html#modules-node).\n[Cluster sniffing](https://www.elastic.co/guide/en/elasticsearch/client/java-api/7.x/transport-client.html), \nenabled by default in the @product@ connector, looks for `data` nodes\nconfigured in the `transportAddresses` property. If none are available, the\nconnector may throw a `NoNodeAvailableException` in the console log. If cluster\nsniffing is to remain enabled, be sure that your configuration allows for at\nleast one `data` node's transport address to be \"sniffable\" at all times to\navoid this error.\n\nTo disable cluster sniffing, add `clientTransportSniff=false` to the `.config`\nfile or un-check the Client Transport Sniff property in System Settings.\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/02-elasticsearch/02-preparing-to-install.markdown",
    "content": "---\nheader-id: preparing-to-install-elasticsearch\n---\n\n# Preparing to Install Elasticsearch\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/using-search/installing-and-upgrading-a-search-engine/elasticsearch/getting-started-with-elasticsearch.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nBy default, @product-ver@ and its \n[embedded Elasticsearch engine](/docs/7-2/deploy/-/knowledge_base/d/elasticsearch#embedded-vs-remote-operation-mode)\nrun in the same JVM. Although this enables out-of-the-box search, it's only\nsupported for development. For production use, Elasticsearch must run in a\nseparate JVM. See the \n[installation guide](/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch)\nfor information on installing a remote Elasticsearch cluster. \n\nBecause search engines benefit heavily from caching, their JVM memory profiles\ndiffer substantially from those of a JVM running @product@. Therefore, the two\napplications should always be kept separate in production environments. \n\nThe following sections provide a synopsis of Elasticsearch configurations for \n@product-ver@. Prior to deployment, we strongly recommend reading \n[Elastic's documentation on production deployment](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index.html). \n\n## Sizing Your Deployment \n\nWhen sizing your Elasticsearch deployment, carefully consider CPU, memory, disk,\nand network capacity. To scale effectively and avoid using lots of machines,\ndeploy Elasticsearch on medium to large machines (for example, machines with two\nto eight CPUs). Avoid running multiple Elasticsearch JVMs on the same operating\nsystem. \n\n## CPU \n\nWe recommend allocating at least eight total CPU cores to the Elasticsearch\nengine, assuming only one Elasticsearch JVM is running on the machine. \n\n## Memory \n\nAt least 16 GB of memory is recommended, with 64 GB preferred. The precise\nmemory allocation required depends on how much data is indexed. For index sizes\n500 GB to 1 TB, 64 GB of memory suffices. \n\n## Disk \n\nSearch engines store their indexes on disk, so disk I/O capacity can impact\nsearch performance. Deploy Elasticsearch on SSD whenever possible. Otherwise use\nhigh-performance traditional hard disks (for example, 15k RPM). In either case,\nconsider using RAID 0.\n\nAvoid using Network Attached Storage (NAS) whenever possible as the network\noverhead can be large. If you're using public cloud infrastructure like Amazon\nWeb Services, use instance local storage instead of network storage, such as\nElastic Block Store (EBS). \n\nMaintain 25 percent more disk capacity than the total size of your indexes. If\nyour index is 60 GB, make sure you have at least 75 GB of disk space available.\nTo estimate the disk space you need, you can index a representative sample of\nyour production content and multiply that size by the fraction of your\nproduction content that it represents. For example, index 25 percent of your\nproduction content and then multiply the resulting index size by four. Keep in\nmind that indexing a 1 MB file doesn't result in 1 MB of disk space in the\nsearch index. \n\n## Cluster Size \n\nWhile @product@ can work with an Elasticsearch cluster comprised of one or two\nnodes, the minimum cluster size recommended by Elastic for fault tolerance is\nthree nodes.\n\n## Networking \n\nElasticsearch relies on clustering and sharding to deliver fast, accurate search\nresults, and thus requires a fast and reliable network. Most modern data centers\nprovide 1 GbE or 10 GbE between machines. \n\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/02-elasticsearch/03-installing-elasticsearch.markdown",
    "content": "---\nheader-id: installing-elasticsearch\n---\n\n# Installing Elasticsearch\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/using-search/installing-and-upgrading-a-search-engine/elasticsearch/installing-elasticsearch.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n@product@ uses Elasticsearch to index its content. By default, it's installed as\nan embedded service. It works, but it's not a supported configuration for\na production server. Feel free to use it while testing or developing, but when\nyou're ready to put your site in production, you must run Elasticsearch as\na standalone process. This is better anyway, because it frees you to design your\ninfrastructure the way you want it. If you've got hardware or a VM to spare, you\ncan separate your search infrastructure from @product@ and reap some performance\ngains by putting search on a separate box. If you're more budget-conscious, you\ncan still increase performance by running Elasticsearch in a separate,\nindividually tunable JVM on the same box. \n\nBefore installing Elasticsearch, refer to \n[Preparing to Install Elasticsearch](/docs/7-2/deploy/-/knowledge_base/d/preparing-to-install-elasticsearch) \nfor guidance on configuring the servers to support an Elasticsearch deployment\nproperly. \n\nHere's an overview of the installation steps: \n\n1. Download a supported version of Elasticsearch. See\n   [Elastic's](https://www.elastic.co) website. \n\n2. Install Elasticsearch by extracting its archive to the system where you want\n   it to run. \n\n3. Install some required Elasticsearch plugins.\n\n4. Name your Elasticsearch cluster. \n\n5. Configure @product@ to connect to your Elasticsearch cluster. \n\n6. Restart @product@ and reindex your search and spell check indexes.\n\n| **Prerequisites:** Before continuing, make sure you have set the\n| [`JAVA_HOME` environment variable](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/).\n| \n| If you have multiple JDKs installed, make sure Elasticsearch and @product@ are\n| using the same version and distribution (e.g., Oracle Open JDK 1.8.0_201). You\n| can specify this in `[Elasticsearch Home]/bin/elasticsearch.in.sh`:\n| `JAVA_HOME=/path/to/java`.\n\n| **Replacing the Default Elasticsearch 6 Connector:** If you're installing\n| Elasticsearch 6, use the connector application installed by default. If you're installing\n| Elasticsearch 7, you'll need to download the connector from Liferay Marketplace\n| for either\n| [CE](https://web.liferay.com/en/marketplace/-/mp/application/170642090) and\n| [DXP](https://web.liferay.com/en/marketplace/-/mp/application/170390307).\n| Always refer to the [compatibility matrix to find the exact versions\n| supported](https://www.liferay.com/documents/10182/246659966/Liferay+DXP+7.2+Compatibility+Matrix.pdf/ed234765-db47-c4ad-7c82-2acb4c73b0f9).\n| Before installing the connector, blacklist the Elasticsearch 6 connector and\n| APIs. The [upgrade\n| documentation](/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-elasticsearch-7#blacklisting-elasticsearch-6)\n| holds detailed blacklisting steps.\n\nWhen you perform these steps, you'll have a basic, production-ready instance of\n@product@ and Elasticsearch up and running. But that's just the beginning of\nyour server/connector configuration:\n\n- Read about [Configuring Elasticsearch](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector) for @product@ in more detail.\n- Learn how to [Secure Elasticsearch](/docs/7-2/deploy/-/knowledge_base/d/installing-liferay-enterprise-search-security).\n- [Liferay Enterprise Search] Learn how to configure [Monitoring](/docs/7-2/deploy/-/knowledge_base/d/installing-liferay-enterprise-search-monitoring).\n\nFor complete information on compatibility, check the\n[@product@ compatibility matrix](https://help.liferay.com/hc/en-us/articles/360028982631-Liferay-DXP-7-2-Compatibility-Matrix)\nand the\n[Liferay Enterprise Search compatibility matrix](https://help.liferay.com/hc/en-us/articles/360016511651#Liferay-Enterprise-Search)\nif you have a subscription.\n\n### Step One: Download a Supported Version of Elasticsearch\n\nIf @product@ isn't running, start it. \n\nVisit port 9200 on localhost to access the embedded Elasticsearch: \n\n    http://localhost:9200\n\nA JSON document is returned that looks similar to this: \n\n```json\n{\n  \"name\" : \"01BT8H4\",\n  \"cluster_name\" : \"LiferayElasticsearchCluster\",\n  \"cluster_uuid\" : \"ziPGEBeSToGHc7lVqaYHnA\",\n  \"version\" : {\n    \"number\" : \"6.5.0\",\n    \"build_flavor\" : \"unknown\",\n    \"build_type\" : \"unknown\",\n    \"build_hash\" : \"816e6f6\",\n    \"build_date\" : \"2018-11-09T18:58:36.352602Z\",\n    \"build_snapshot\" : false,\n    \"lucene_version\" : \"7.5.0\",\n    \"minimum_wire_compatibility_version\" : \"5.6.0\",\n    \"minimum_index_compatibility_version\" : \"5.0.0\"\n  },\n  \"tagline\" : \"You Know, for Search\"\n}\n```\n\nThe version of Elasticsearch that's running is the value of the `number` field.\nIn this example, it's 6.5.0. You can install the embedded version, but it might\nnot be the most up-to-date version of Elasticsearch that's supported with\n@product@. Consult the\n[Compatibility Matrix](https://help.liferay.com/hc/en-us/articles/360016511651)\nfor definitive information on what's supported. \n\n| **Note:** Although the embedded server uses Elasticsearch 6.5, Elasticsearch\n| 6.8.x has been tested with @product-ver@ GA1, and is fully supported. If you've\n| upgraded to @product-ver@ Service Pack 1/Fix Pack 2 (or GA2 for CE users),\n| Elasticsearch 7 is supported through the Liferay Connector to Elasticsearch\n| 7, which can be downloaded from Liferay Marketplace for both \n| [CE](https://web.liferay.com/en/marketplace/-/mp/application/170642090) and \n| [DXP](https://web.liferay.com/en/marketplace/-/mp/application/170390307).\n| Always refer to the [compatibility matrix to find the exact versions supported](https://help.liferay.com/hc/en-us/articles/360016511651).\n\nShut down the @product@ server. In a local, single-machine testing environment,\nif you continue without shutting down, the Elasticsearch server you're about to\ninstall and start throws errors in the log if its cluster name and HTTP port\nmatch the already-running embedded Elasticsearch server. An alternative to\nshutting down @product@ is to use a different cluster name (i.e., not\n`LiferayElasticsearchCluster`) and HTTP port (i.e., not `9200`) in the remote\nElasticsearch server.\n\nWhen you know the version of Elasticsearch you need, go to\n[Elastic's](https://www.elastic.co) website and download that version. \n\n### Step Two: Install Elasticsearch \n\nMost of this step entails deciding where you want to run Elasticsearch. Do you\nwant to run it on the same machine as @product@, or do you want to run it on its\nown hardware? The answer to this question comes down to a combination of the\nresources you have available and the size of your installation. Regardless of\nwhat you decide, either way you get the benefit of a separately tunable search\ninfrastructure. \n\nOnce you have a copy of the right version of Elasticsearch, extract it to\na folder on the machine where you want it running. That's it! \n\n### Step Three: Install Elasticsearch Plugins \n\nInstall the following required Elasticsearch plugins:\n\n-  `analysis-icu`\n-  `analysis-kuromoji`\n-  `analysis-smartcn`\n-  `analysis-stempel`\n\nTo install these plugins, navigate to Elasticsearch Home and enter\n\n    ./bin/elasticsearch-plugin install [plugin-name]\n\nReplace *[plugin-name]* with the Elasticsearch plugin's name.\n\n### Step Four: Name Your Elasticsearch Cluster \n\nA *cluster* in Elasticsearch is a collection of nodes (servers) identified as a\ncluster by a shared cluster name. The nodes work together to share data and\nworkload. A one node cluster is discussed here; to create a multi-node cluster,\nplease refer to [Elastic's documentation](https://www.elastic.co/guide/index.html). \n\nNow that you've installed Elastic, it sits in a folder on your machine, which is\nreferred to here as `[Elasticsearch Home]`. To name your cluster, you'll define\nthe cluster name in both Elasticsearch and in @product@. First, define it in\nElasticsearch. Edit the following file: \n\n    [Elasticsearch Home]/config/elasticsearch.yml\n\nUncomment the line that begins with `cluster.name`. Set the cluster name to\nwhatever you want to name your cluster: \n\n```yml\ncluster.name: LiferayElasticsearchCluster\n```\n\nOf course, this isn't a very imaginative name; you may choose to name your\ncluster `finders_keepers` or something else you can remember more easily. Save\nthe file. \n\n| **Elasticsearch 6.x:** On Elasticsearch 6.x, you must also disable X-Pack\n| Security unless you have a Liferay Enterprise Search subscription. Add this to\n| `elasticsearch.yml`: `xpack.security.enabled: false`.\n\nNow you can start Elasticsearch. Run the executable for your operating system\nfrom the `[Elasticsearch Home]/bin` folder: \n\n```bash\n./elasticsearch\n```\n\nElasticsearch starts, and one of its status messages includes a transport address: \n\n```sh\n[2019-04-01T16:55:50,127][INFO ][o.e.t.TransportService   ] [HfkqdKv] publish_address {127.0.0.1:9300}, bound_addresses {[::1]:9300}, {127.0.0.1:9300}\n```\n\nTake note of this address; you'll need to give it to your @product@ server so it\ncan find Elasticsearch on the network. \n\n### Step Five: Configure @product@ to Connect to your Elasticsearch Cluster \n\nNow that you're ready to configure @product@, start it if you haven't already,\nlog in, and then click on *Control Panel* &rarr; *Configuration* &rarr; *System\nSettings* &rarr; *Search*. Enter the term *elasticsearch* in the search bar and\nclick the *Elasticsearch [Version]* entry from the list of settings (at the\ntime of writing, the version will either be *6* or *7*). Now you can configure\nit. Here are the configuration options to change: \n\n**Cluster Name:** Enter the name of the cluster as you defined it in\nElasticsearch. \n\n**Operation Mode:** Defaults to EMBEDDED. Change it to REMOTE to connect to a\nstandalone Elasticsearch. \n\n**Transport Addresses:** Enter a delimited list of transport addresses for\nElasticsearch nodes. Here, you'll enter the transport address from the\nElasticsearch server you started. The default value is `localhost:9300`, which\nwill work. \n\nWhen finished, click *Save*. You're almost done. \n\n### Step Six: Restart @product@ and Reindex \n\nIf you're doing a local test installation, you probably only changed the\nOperation Mode in the connector configuration, so there's no need to restart;\nskip to re-indexing. If you've made more configuration changes in the\nconnector's configuration, stop and restart @product@. When it's back up, log in\nas an administrative user and click on *Control Panel* &rarr; *Configuration*\n&rarr; *Search* and click the *Execute* button for *Reindex all search indexes*\nand then *Reindex all spell check indexes*. When you do that, you'll see some\nmessages scroll up in the Elasticsearch log. \n\nWhen restarting @product@, `update_mappings` messages will appear in the\nElasticsearch logs:\n\n```sh\n[2019-04-01T17:08:57,462][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-0/m27eNsekTAyP27zDOjGojw] update_mapping [LiferayDocumentType]\n[2019-04-01T17:08:57,474][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-0/m27eNsekTAyP27zDOjGojw] update_mapping [LiferayDocumentType]\n[2019-04-01T17:08:58,393][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-0/m27eNsekTAyP27zDOjGojw] update_mapping [LiferayDocumentType]\n[2019-04-01T17:08:58,597][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-0/m27eNsekTAyP27zDOjGojw] update_mapping [LiferayDocumentType]\n[2019-04-01T17:09:07,040][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/OJidpYkgR5OcCD5dgWB8Aw] update_mapping [LiferayDocumentType]\n```\n\nOnce you reindex, more log messages appear in Elasticsearch:\n\n```sh\n[2019-04-01T17:11:17,338][INFO ][o.e.c.m.MetaDataDeleteIndexService] [HfkqdKv] [liferay-20101/OJidpYkgR5OcCD5dgWB8Aw] deleting index\n[2019-04-01T17:11:17,389][INFO ][o.e.c.m.MetaDataCreateIndexService] [HfkqdKv] [liferay-20101] creating index, cause [api], templates [], shards [1]/[0], mappings [LiferayDocumentType]\n[2019-04-01T17:11:17,471][INFO ][o.e.c.r.a.AllocationService] [HfkqdKv] Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[liferay-20101][0]] ...]).\n[2019-04-01T17:11:17,520][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:19,047][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:19,133][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:19,204][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:19,249][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:21,215][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:21,262][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:21,268][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:21,275][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:21,282][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n[2019-04-01T17:11:21,373][INFO ][o.e.c.m.MetaDataMappingService] [HfkqdKv] [liferay-20101/Meacn_uxR06g0tCJonS4eA] update_mapping [LiferayDocumentType]\n```\n\nReindexing the spell check dictionaries produces log messages like these:\n\n```sh\n2019-04-29 14:02:22.034 INFO  [liferay/search_writer/SYSTEM_ENGINE-11][BaseSpellCheckIndexWriter:278] Start indexing dictionary for com/liferay/portal/search/dependencies/spellchecker/en_US.txt\n2019-04-29 14:02:34.166 INFO  [liferay/search_writer/SYSTEM_ENGINE-11][BaseSpellCheckIndexWriter:299] Finished indexing dictionary for com/liferay/portal/search/dependencies/spellchecker/en_US.txt\n2019-04-29 14:02:34.167 INFO  [liferay/search_writer/SYSTEM_ENGINE-11][BaseSpellCheckIndexWriter:278] Start indexing dictionary for com/liferay/portal/search/dependencies/spellchecker/es_ES.txt\n2019-04-29 14:02:39.379 INFO  [liferay/search_writer/SYSTEM_ENGINE-11][BaseSpellCheckIndexWriter:299] Finished indexing dictionary for com/liferay/portal/search/dependencies/spellchecker/es_ES.txt\n```\n\nFor additional confirmation that @product@ recognizes the remote search engine,\nnavigate to the Search Control Panel application and note the subtle change\nthere: the vendor name is now simply _Elasticsearch_, whereas prior to the\ninstallation of the remote Elasticsearch server, it said _Elasticsearch\n(Embedded)_.\n\n![Figure 1: To see information about the currently connected search engine, go to _Control Panel &rarr; Configuration &rarr; Search_.](../../../images/search-admin-engineinfo-remote.png)\n\nFor additional details refer to the [Elasticsearch installation guide](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/getting-started-install.html).\n\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/02-elasticsearch/04-configuring-the-connector.markdown",
    "content": "---\nheader-id: configuring-the-liferay-elasticsearch-connector\n---\n\n# Configuring the Liferay Elasticsearch Connector\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/using-search/installing-and-upgrading-a-search-engine/elasticsearch/connecting-to-elasticsearch.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nFor detailed Elasticsearch configuration information, refer to the\n[Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/settings.html).\n\nThe name of your Elasticsearch cluster is important. When you're running\nElasticsearch in remote mode, the cluster name is used by @product@ to recognize\nthe Elasticsearch cluster. To learn about setting the Elasticsearch cluster name\non the @product@ side, refer below to the section called Configuring the Liferay\nElasticsearch Connector.\n\n| **Note:** The `http.enabled` setting in Elasticsearch corresponds to the\n| `httpEnabled` setting in the Liferay Connector to Elasticsearch 6 application.\n| As this setting was [deprecated in Elasticsearch\n| 6.3](https://www.elastic.co/guide/en/elasticsearch/reference/6.5/release-notes-6.3.0.html),\n| the connector's corresponding setting is now also deprecated. This setting was\n| only used for configuring the embedded Elasticsearch server, so its deprecation\n| should have minimal impact to production deployments.\n\nElasticsearch's configuration files are written in [YAML](http://www.yaml.org)\nand kept in the `[Elasticsearch Home]/config` folder. The main configuration\nfile is `elasticsearch.yml`, used for configuring Elasticsearch modules.\n\nTo set the name of the Elasticsearch cluster, open `[Elasticsearch\nHome]/config/elasticsearch.yml` and specify\n\n```yml\ncluster.name: LiferayElasticsearchCluster\n```\n\nSince `LiferayElasticsearchCluster` is the default name given to the cluster in\nthe @product@ Elasticsearch connector, this works just fine. Of course, you can\nname your cluster whatever you want (we humbly submit the recommendation\n`clustery_mcclusterface`).<sup>[1](#footnote1)</sup> Configure your node name\nusing the same syntax (setting the `node.name` property). There's no client\nsetting for this, it exists only in each Elasticsearch node's\n`elasticsearch.yml` file.\n\nIf you'd rather work from the command line than in the configuration file,\nnavigate to Elasticsearch Home and enter\n\n```bash\n./bin/elasticsearch --cluster.name clustery_mcclusterface --node.name nody_mcnodeface\n```\n\nFeel free to change the node name or the cluster name. Once you configure\nElasticsearch to your liking, start it up.\n\n## Starting Elasticsearch \n\nStart Elasticsearch by navigating to Elasticsearch Home and typing \n\n```bash\n./bin/elasticsearch\n```\n\nif you run Linux, or \n\n```bash\n\\bin\\elasticsearch.bat\n```\n\nif you run Windows.\n\nTo run as a daemon in the background, add the `-d` switch to either command:\n\n```bash\n./bin/elasticsearch -d\n```\n\nOnce both Elasticsearch and @product@ are installed and running, introduce them\nto each other. \n\n## Configuring the Liferay Elasticsearch Connector \n\nThe Elasticsearch connector provides integration between Elasticsearch and the\nportal. Before configuring the connector, make sure Elasticsearch is running.\n\nThere are two ways to configure the connector: \n\n1. [Use the System Settings application in the Control Panel.](#configuring-the-connector-in-the-control-panel) \n\n2. [Manually create an OSGi configuration file.](#configuring-the-connector-with-an-osgi-config-file) \n\nIt's convenient to configure the Elasticsearch connector from System Settings,\nbut this is often only possible during development and testing. If you're not\nfamiliar with System Settings, read about it\n[here](/docs/7-2/user/-/knowledge_base/u/system-settings).\nRemember that you can generate configuration files for deployment to other\nsystems by configuring System Settings, and then exporting the `.config` file\nwith your configuration.\n\n### Configuring the Connector in the Control Panel \n\nTo configure the Elasticsearch connector from the System Settings application,\n\n1.  Start @product@.\n\n2.  Navigate to *Control Panel* &rarr; *Configuration* &rarr; *System Settings*\n    &rarr; *Platform*. \n\n3.  Find the *Elasticsearch* entry (scroll down and browse to it or use the\n    search box) and click the Actions icon\n    (![Actions](../../../images/icon-actions.png)), then *Edit*.\n\n    ![Figure 1: Use the System Settings application in @product@'s Control Panel to configure the Elasticsearch connector.](../../../images/cfg-elasticsearch-sys-settings.png)\n\n4.  Make any edits to the configuration and click *Save*.\n\n    ![Figure 2: Configure the Elasticsearch connector's settings. Make sure you set the Operation Mode to *Remote*.](../../../images/cfg-elasticsearch-sys-settings2.png)\n\n| **Note:** If you switch operation modes (`EMBEDDED` &rarr; `REMOTE`), you must\n| trigger a re-index. Navigate to *Control Panel* &rarr; *Configuration* &rarr;\n| *Search*, and click *Execute* next to *Reindex all search indexes.*\n\n### Configuring the Connector with an OSGi `.config` File \n\nWhen preparing a system for production deployment, you want to use a repeatable\ndeployment process. Therefore, it's best to use the OSGi configuration file,\nwhere your configuration is maintained in a controlled source.\n\nFollow these steps to configure the Elasticsearch connector using a configuration\nfile:\n\n1. Create the following file:\n    \n        [Liferay_Home]/osgi/configs/com.liferay.portal.search.elasticsearch7.configuration.ElasticsearchConfiguration.config\n\n    | **Elasticsearch 6:** The name of the `.config` file for the Elasticsearch\n    | 6 connector is\n    | `com.liferay.portal.search.elasticsearch6.configuration.ElasticsearchConfiguration.config`\n\n2. Add configurations to the file, in the format `propertyName=\"Value\"`. For\n   example,\n\n        operationMode=\"REMOTE\"\n        # If running Elasticsearch from a different computer:\n        #transportAddresses=\"ip.of.elasticsearch.node:9300\"\n        # Highly recommended for all non-production usage (e.g., practice, tests, diagnostics):\n        #logExceptionsOnly=\"false\"\n\n3. Start @product@ or re-index if already running.\n\nAs you can see from the System Settings entry for Elasticsearch, there are a lot\nmore configuration options available that help you tune your system for optimal\nperformance. \n\nWhat follows here are some known good configurations for clustering\nElasticsearch. These, however, can't replace the manual process of tuning,\ntesting under load, and tuning again, so we encourage you to examine the\n[Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/important-settings.html) \nand go through that process once you have a working configuration. \n\n## Configuring a Remote Elasticsearch Host \n\nIn production systems Elasticsearch and @product@ are installed on different\nservers. To make @product@ aware of the Elasticsearch cluster, set\n\n    transportAddresses=[IP address of Elasticsearch Node]:9300\n\nHere's an example that sets the IP address of two nodes in the Elasticsearch\ncluster:\n\n    transportAddresses=[\"192.168.1.1:9300\",\"192.168.1.2:9300\"]\n\nSet this in the Elasticsearch connector's OSGi configuration file. List as many\nor as few Elasticsearch nodes in this property as you want. This tells\n@product@ the IP address or host name where search requests should be sent. If\nusing System Settings, set the value in the *Transport Addresses* property.\n\n| **Note:** In an Elasticsearch cluster you can list the transport addresses for\n| multiple Elasticsearch nodes as a comma-separated list in the\n| `transportAddresses` property. If you set only one transport address, @product@\n| loses contact with Elasticsearch if that node goes down.\n\nOn the Elasticsearch side, set the `network.host` property in your\n`elaticsearch.yml` file. This property simultaneously sets both the *bind host*\n(the host where Elasticsearch listens for requests) and the *publish host*\n(the host name or IP address Elasticsearch uses to communicate with other\nnodes). See\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-network.html)\nfor more information.\n\n## Clustering Elasticsearch in Remote Operation Mode \n\nTo cluster Elasticsearch, first set `node.max_local_storage_nodes` to\nbe something greater than `1`. When you run the Elasticsearch start script,\na new local storage node is added to the cluster. If you want four nodes running\nlocally, for example, run `./bin/elasticsearch` four times. See\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-node.html#max-local-storage-nodes)\nfor more information.\n\nConfigure the number of shards and replicas in the Elasticsearch 6 connector,\nusing the `indexNumberOfShards` and `indexNumberOfReplicas` properties to\nspecify the number of primary shards and number of replica shards, respectively.\nElasticsearch's default configuration works for a cluster of up to ten nodes,\nsince the default number of shards is `5` and the default number of replica\nshards is `1`.\n\n| **Note:** Elasticsearch uses the [Zen Discovery\n| Module](https://www.elastic.co/guide/en/elasticsearch/reference/6.x/modules-discovery-zen.html)\n| by default, which provides unicast discovery. Additionally, nodes in the cluster\n| communicate using the [Transport\n| Module](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-transport.html),\n| through TCP. See the Elasticsearch documentation for the available properties\n| (to be set in the `elasticsearch.yml` file), and the @product@ Elasticsearch\n| connector's settings for the connector's available settings.\n| \n| At a minimum, provide the list of hosts (as `host:port`)  to act as gossip\n| routers during unicast discovery in the `elasticsearch.yml`:\n| \n|     discovery.zen.ping.unicast.hosts: [\"node1.ip.address\", \"node2.ip.address\"]\n| \n| For example,\n| \n|     discovery.zen.ping.unicast.hosts: [\"10.10.10.5\", \"10.10.10,.5:9305\"]\n| \n| For more information on configuring an Elasticsearch cluster, see the\n| documentation on \n| [Elasticsearch Index Settings](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index-modules.html).\n\n\n## Elasticsearch Connector System Settings, By Operation Mode \n\nSome of the settings available for the Elasticsearch connector are applicable\nfor only one operation mode (REMOTE or EMBEDDED). Refer to the table below:\n\nConnector Setting/Operation Mode | EMBEDDED | REMOTE |\n------------------------------| :----: | :----: |\n`clusterName` | x | x\n`operationMode` | x | x\n`indexNamePrefix` | x | x\n`indexNumberOfReplicas*` | x | x\n`indexNumberOfShards*` | x | x\n`bootstrapMlockAll` | x | \\-\n`logExceptionsOnly` | x | x\n`retryOnConflict` | x | x\n`discoveryZenPingUnicastHostsPort` | x | \\-\n`networkHost` | x | \\-\n`networkBindHost` | x | \\-\n`networkPublishHost` | x | \\-\n`transportTcpPort` | x | \\-\n`transportAddresses` | \\- | x\n`clientTransportSniff` | \\- | x\n`clientTransportIgnoreClusterName` | \\- | x\n`clientTransportPingTimeout*` | \\- | x\n`clientTransportNodesSamplerInterval` | \\- | x\n`httpEnabled` | x | \\-\n`httpCORSEnabled` | x | \\-\n`httpCORSAllowOrigin` | x | \\-\n`httpCORSConfigurations` | x | \\-\n`additionalConfigurations` | x | x\n`additionalIndexConfigurations` | x | x\n`additionalTypeMappings` | x | x\n`overrideTypeMappings` | x | x\n\n<a name=\"footnote1\">1</a> This is, of course, a nod to all those fans of [Boaty Mcboatface](http://www.theatlantic.com/international/archive/2016/05/boaty-mcboatface-parliament-lessons/482046). \n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/02-elasticsearch/05-advanced-configuration.markdown",
    "content": "---\nheader-id: advanced-configuration-of-the-liferay-elasticsearch-connector\n---\n\n# Advanced Configuration of the Liferay Elasticsearch Connector\n\n[TOC levels=1-4]\n\nThe default configuration for Liferay's Elasticsearch connector module is set\nin a Java class called `ElasticsearchConfiguration`.\n\nWhile the Elasticsearch connector has a lot of configuration options out of the\nbox, you might find an Elasticsearch configuration you need that isn't provided\nby default. In this case, add the configuration options you need. If something\nis configurable for Elasticsearch, it's configurable using the Elasticsearch\nconnector.\n\n## Adding Settings and Mappings to the Liferay Elasticsearch Connector\n\nThink of the available configuration options as being divided into two groups:\nthe most common ones that are easily configured, and more complex\nconfigurations requiring a more brute-force approach: these include the\n`additionalConfigurations`, `additionalIndexConfigurations`,\n`additionalTypeMappings`, and `overrideTypeMappings` settings. \n\n[Figure 1: You can add Elasticsearch configurations to the ones currently available in System Settings.](../../../images/cfg-elasticsearch-additional-configs.png)\n\n### Additional Configurations\n\nThe `additionalConfigurations` configuration defines extra settings (in YAML)\nfor the embedded Elasticsearch. This is only useful for testing environments\nusing the embedded Elasticsearch server. Any node settings normally set in\n`elasticsearch.yml` can be declared here. See the\n[Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index.html) \nfor a description of all possible node settings.\n\n### Adding Index Configurations\n\nThe `additionalIndexConfigurations` configuration defines extra settings (in\nJSON or YAML) that are applied to the @product@ index when it's created. For\nexample, you can create custom analyzers and filters using this setting. For a\ncomplete list of available settings, see the \n[Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index-modules.html).\n\nHere's an example that shows how to configure \n[analysis](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index-modules-analysis.html)\nthat can be applied to a field or a dynamic template (see\n[below](#overriding-type-mappings) for an example application to a dynamic\ntemplate).\n\n```json\n{  \n    \"analysis\": {\n        \"analyzer\": {\n            \"kuromoji_liferay_custom\": {\n                \"filter\": [\n                    \"cjk_width\",\n                    \"kuromoji_baseform\",\n                    \"pos_filter\"\n                ],\n                \"tokenizer\": \"kuromoji_tokenizer\"\n            }\n        },\n        \"filter\": {\n            \"pos_filter\": {\n                \"type\": \"kuromoji_part_of_speech\"\n            }\n        }\n    }\n}\n```\n\n### Adding Type Mappings\n\n`additionalTypeMappings` defines extra mappings for the `LiferayDocumentType`\ntype definition. These are applied when the index is created. Add the mappings\nusing JSON syntax. For more information see\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/mapping.html)\nand\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/indices-put-mapping.html).\nUse `additionalTypeMappings` for new field (`properties`) mappings and new\ndynamic templates, but don't try to override existing mappings. If any of the\nmappings set here overlap with existing mappings, index creation fails. Use\n`overrideTypeMappings` to replace default mappings.\n\nAs with dynamic templates, you can add sub-field mappings to @product@'s type\nmapping. These are referred to as\n[properties](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/properties.html)\nin Elasticsearch.\n\n```json\n{ \n    \"LiferayDocumentType\": {  \n        \"properties\": {   \n            \"fooName\": {\n                \"index\": \"true\",\n                \"store\": \"true\",\n                \"type\": \"keyword\"\n            }\n        }   \n    }\n}\n```\n\nSee\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/mapping-types.html)\nfor more details on Elasticsearch's field datatypes.\n\nThe above example shows how a `fooName` field might be added to @product@'s type\nmapping. Because `fooName` is not an existing property in the mapping, it \nworks fine. If you try to override an existing property mapping, index\ncreation fails. Instead use the `overrideTypeMappings` setting to override\n`properties` in the mapping.\n\nTo see that your additional mappings have been added to the\n`LiferayDocumentType`, use `curl` to access this URL after saving your\nadditions and re-indexing:\n\n```bash\ncurl http://[HOST]:[ES_PORT]/liferay-[COMPANY_ID]/_mapping/LiferayDocumentType?pretty\n```\n\nHere's what it would look like for an Elasticsearch instance running on\n`localhost:9200`, with a @product@ Company ID of `20116`:\n\n```bash\ncurl http://localhost:9200/liferay-20116/_mapping/LiferayDocumentType?pretty\n```\n\nIn the above URL, `liferay-20116`is the index name. Including it indicates that\nyou want to see the mappings that were used to create the index with that name.\n\n### Overriding Type Mappings\n\nUse `overrideTypeMappings` to override @product@'s default type mappings. This\nis an advanced feature that should be used only if strictly necessary. If you\nset this value, the default mappings used to define the Liferay Document Type in\n@product@ source code (for example, `liferay-type-mappings.json`) are ignored\nentirely, so include the whole mappings definition in this property, not just\nthe segment you're modifying. To make a modification, find the entire list of\nthe current mappings being used to create the index by navigating to the URL\n\n    http://[HOST]:[ES_PORT]/liferay-[COMPANY_ID]/_mapping/LiferayDocumentType?pretty\n\nCopy the contents in as the value of this property (either into System Settings\nor your OSGi configuration file). Leave the opening curly brace `{`, but delete\nlines 2-4 entirely:\n\n```json\n\"liferay-[COMPANY_ID]\": {\n    \"mappings\" : {\n        \"LiferayDocumentType\" : {\n```\n\nThen, from the end of the mappings, delete the concluding three curly braces.\n\n            }\n        }\n    }\n\nNow modify whatever mappings you'd like. The changes take effect once you save\nthe changes and trigger a re-index from Server Administration. \n\nHere's a partial example, showing a \n[dynamic template](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/dynamic-templates.html)\nthat uses the analysis configuration from `additionalIndexConfigurations` to\nanalyze all string fields that end with `_ja`. You'd include this with all the\nother default mappings, replacing the provided `template_ja` with this custom\none:\n\n```json\n{\n    \"LiferayDocumentType\": {\n        \"dynamic_templates\": [\n            {\n                \"template_ja\": {\n                    \"mapping\": {\n                        \"analyzer\": \"kuromoji_liferay_custom\",\n                        \"index\": \"analyzed\",\n                        \"store\": \"true\",\n                        \"term_vector\": \"with_positions_offsets\",\n                        \"type\": \"string\"\n                    },\n                    \"match\": \"\\\\w+_ja\\\\b|\\\\w+_ja_[A-Z]{2}\\\\b\",\n                    \"match_mapping_type\": \"string\",\n                    \"match_pattern\": \"regex\"\n                }\n                ...\n            }\n        ]\n    }\n}\n```\n\n## Multi-line YAML Configurations\n\nIf you configure the settings from the last section using an OSGi configuration\nfile, you might find yourself needing to write YAML snippets that span multiple\nlines. The syntax for that is straightforward and just requires appending each\nline with `\\n\\`, like this:\n\n```yaml\nadditionalConfigurations=\\\n                    cluster.routing.allocation.disk.threshold_enabled: false\\n\\\n                    cluster.service.slow_task_logging_threshold: 600s\\n\\\n                    index.indexing.slowlog.threshold.index.warn: 600s\\n\\\n                    index.search.slowlog.threshold.fetch.warn: 600s\\n\\\n                    index.search.slowlog.threshold.query.warn: 600s\\n\\\n                    monitor.jvm.gc.old.warn: 600s\\n\\\n                    monitor.jvm.gc.young.warn: 600s\n```\n\nFrom simple configurations to overriding existing type mappings, Elasticsearch\nand Liferay's connector to Elasticsearch are configurable.\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/02-elasticsearch/06-elasticsearch-connector-settings-reference.markdown",
    "content": "---\nheader-id: elasticsearch-connector-settings-reference\n---\n\n# Elasticsearch Connector Settings: Reference\n\n[TOC levels=1-4]\n\nElasticsearch is the default search engine for @product-ver@. The connection is\nmanaged through the *Liferay Connector to Elasticsearch [Version]*, and is configurable\nthrough System Settings or an OSGi configuration file named\n\n    com.liferay.portal.search.elasticsearch6.configuration.ElasticsearchConfiguration.config\n\nIf you are using Elasticsearch 7, your configuration file must be named\n\n    com.liferay.portal.search.elasticsearch7.configuration.ElasticsearchConfiguration.config\n\nDeploy the file to `[Liferay_Home]/osgi/configs` and a listener auto-detects it.\n\nThe list below is all the configuration settings for Liferay's default\nElasticsearch connector, in the order they appear in the System Settings\napplication (The _Elasticsearch [Version]_ entry under the _Search_ category):\n\n`clusterName=LiferayElasticsearchCluster`\n: A String value that sets the name of the cluster to integrate with. This name\nshould match the remote cluster when Operation Mode is set to remote.  (See\nalso: remote operation mode)\n\n`operationMode=EMBEDDED`\n: There are two operation modes you can choose from: EMBEDDED or REMOTE. Set to\nREMOTE to connect to a remote standalone Elasticsearch cluster. Set to EMBEDDED\nto start Liferay with an internal Elasticsearch instance. Embedded operation\nmode is unsupported for production environments.\n\n`indexNamePrefix=liferay-`\n: Set a String value to use as the prefix for the search index name. The default\nvalue should not be changed under normal conditions. If you change it, you must\nalso perform a *reindex all* operation for the portal and then manually delete\nthe old index using the Elasticsearch administration console.\n\n`indexNumberOfReplicas=`\nSet the number of replicas for each index. If left unset, no replicas are used.\nA full reindex is required to make changes take effect.\n\n`indexNumberOfShards=`\nSet the number of index shards to use when a Liferay index is created. If left\nunset, a single shard is used. A full reindex is required to make changes take\neffect. \n\n`bootstrapMlockAll=false`\n: A boolean setting that, when set to `true`, tries to lock the process address\nspace into RAM, preventing any Elasticsearch memory from being swapped out (see\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/setup-configuration-memory.html#bootstrap-memory_lock))\nfor more information)\n\n`logExceptionsOnly=true`\n: A boolean setting that, when set to true, only logs exceptions from\nElasticsearch, and does not rethrow them.\n\n`retryOnConflict=5`\n: Set an int value for the number of retries to attempt if a version conflict\noccurs because the document was updated between getting it and updating it (see\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/docs-update.html#docs-update-api-query-params)\nfor more information).\n\n`discoveryZenPingUnicastHostsPort=9300-9400`\n: Set a String value for the range of ports to use when building the value for\ndiscovery.zen.ping.unicast.hosts. Multiple Elasticsearch nodes on a range of\nports can act as gossip routers at the same computer (see\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-discovery-hosts-providers.html)\nfor more information).\n\n`networkHost=`\n: Set this String value to instruct the node to bind to this hostname or IP\naddress and publish (advertise) this host to other nodes in the cluster. This is\na shortcut which sets the bind host and the publish host at the same time (see\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-network.html#common-network-settings)\nfor more information).\n\n`networkBindHost=`\n: Set the String value of the network interface(s) a node should bind to in order\nto listen for incoming requests (see\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-network.html#advanced-network-settings)\nfor more information).\n\n`networkPublishHost=`\n: Set the String value of a single interface that the node advertises to other\nnodes in the cluster, so that those nodes can connect to it (see\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-network.html#advanced-network-settings)\nfor more information).\n\n`transportTcpPort=`\n: Set the String value for the port to bind for communication between nodes.\nAccepts a single value or a range\n(see [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-transport.html#_tcp_transport)\nfor more information).\n\n`transportAddresses=localhost:9300`\n: Set the String values for the addresses of the remote Elasticsearch nodes to\nconnect to. This value is required when Operation Mode is set to remote (see\n[here](https://www.elastic.co/guide/en/elasticsearch/client/java-api/7.x/transport-client.html)\nfor more information). Specify as many or few nodes as you see fit.\n\n`clientTransportSniff=true`\n: Set this boolean to true to enable cluster sniffing and dynamically discover\navailable data nodes in the cluster\n(see [here](https://www.elastic.co/guide/en/elasticsearch/client/java-api/7.x/transport-client.html)\nfor more information).\n\n`clientTransportIgnoreClusterName=false`\n: Set this boolean to true to ignore cluster name validation of connected nodes\n(see [here](https://www.elastic.co/guide/en/elasticsearch/client/java-api/7.x/transport-client.html)\nfor more information).\n\n`clientTransportPingTimeout=`\nThe time (in seconds) the client node waits for a ping response from a node. If\nunset, the default Elasticsearch `client.transport.ping_timeout` is used.\n\n`clientTransportNodesSamplerInterval=`\n: Set this String value to instruct the client node on how often to sample / ping\nthe nodes listed and connected (see\n[here](https://www.elastic.co/guide/en/elasticsearch/client/java-api/7.x/transport-client.html)\nfor more information).\n\n`httpEnabled=true`\n: Set this boolean to false to disable the http layer entirely on nodes which are\nnot meant to serve REST requests directly. As this setting was \n[deprecated in Elasticsearch 6.3](https://www.elastic.co/guide/en/elasticsearch/reference/6.7/release-notes-6.3.0.html#deprecation-6.3.0), the connector's corresponding setting is now also deprecated. This setting was only used for configuring the embedded Elasticsearch server, so its deprecation should have minimal impact to production deployments.\n\n`httpCORSEnabled=true`\n: Set this boolean to false to disable cross-origin resource sharing, i.e. whether\na browser on another origin can do requests to Elasticsearch. If disabled, web\nfront end tools like elasticsearch-head may be unable to connect (see\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-http.html#_settings)\nfor more information).\n\n`httpCORSAllowOrigin=/https?:\\\\/\\\\/localhost(:[0-9]+)?/`\n: Set the String origins to allow when HTTP CORS is enabled (see\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-http.html#_settings)\nfor more information).\n\n`httpCORSConfigurations=`\n: Set the String values for custom settings for HTTP CORS, in YML format\n(`elasticsearch.yml`) (see\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/modules-http.html#_settings)\nfor more information).\n\n`additionalConfigurations=`\n: Set the String values for custom settings for embedded Elasticsearch, in YML\nformat. See: Adding Settings to the Liferay Elasticsearch Connector\n\n`additionalIndexConfigurations=`\n: Set the String values for custom settings for the Liferay index, in JSON or YML\nformat (refer to the Elasticsearch Create Index API for more information).\nSee: Adding Settings to the Liferay Elasticsearch Connector\n\n`additionalTypeMappings=`\n: Set the String values for custom mappings for the `LiferayDocumentType`, in JSON\nformat (refer to the Elasticsearch Put Mapping API for more information)\nSee: Adding Settings to the Liferay Elasticsearch Connector\n\n`overrideTypeMappings=` \nSettings here override @product@'s default type mappings. This\nis an advanced feature that should be used only if strictly necessary. If you\nset this value, the default mappings used to define the Liferay Document Type in\n@product@ source code (for example, `liferay-type-mappings.json`) are ignored\nentirely, so include the whole mappings definition in this property, not just\nthe segment you're modifying.\n\n## Configurations only Affecting the Embedded Elasticsearch Server\n\nThese settings (defined above) are only meant to use while configuring the\nembedded Elasticsearch server. Configuring these will elicit no effect on\nremote Elasticsearch installations:\n\n- `bootstrapMlockAll`\n- `discoveryZenPingUnicastHostsPort`\n- `networkHost`\n- `networkBindHost`\n- `networkPublishHost` \n- `transportTcpPort` \n- `httpEnabled`\n- `httpCORSEnabled` \n- `httpCORSAllowOrigin` \n- `httpCORSConfigurations` \n\nYou can easily configure these settings in the System Setting application, or\nas mentioned above, you can specify them in a deployable OSGi `.config` file.\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/02-elasticsearch/07-security.markdown",
    "content": "---\nheader-id: installing-liferay-enterprise-search-security\n---\n\n# Installing Liferay Enterprise Search Security\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/using-search/installing-and-upgrading-a-search-engine/elasticsearch/securing-elasticsearch.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe very first thing you must do to secure Elasticsearch is enable X-Pack\nSecurity. After that you can begin configuring authentication and Transport\nLayer Security.\n\n| **Elasticsearch 6.x:** If you're using Elasticsearch 6, you'll need a Liferay\n| Enterprise Search (LES) subscription to use X-Pack. Starting with the Liferay\n| Connector to Elasticsearch 7 (available on Liferay Marketplace), X-Pack security\n| is included by default. X-Pack monitoring still requires LES.\n\n## Enabling X-Pack Security\n\nTo enable security, add this setting in `elasticsearch.yml`:\n\n```yaml\nxpack.security.enabled: true\n```\n\nNow you can set up X-Pack users.\n\n## Setting Up X-Pack Users\n\nIn a system using X-Pack Security and X-Pack Monitoring, these built-in\nX-Pack users are important:\n\n- `kibana`\n- `elastic`\n\nSet the passwords for all X-Pack's \n[built-in users](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/built-in-users.html).\nThe `setup-passwords` command is the simplest method to set the built-in users'\nfirst-use passwords for the first time. To update a password subsequently, use\nKibana's UI or the \n[Change Password API](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-change-password.html).\n\nThe `interactive` argument sets the passwords for all built-in users. The\nconfiguration shown in these articles assumes you set all passwords to\n*liferay*. Of course, that's not recommended for production systems.\n\n```bash\n./bin/elasticsearch-setup-passwords interactive\n```\n\nElastic's \n[setup-passwords command](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/setup-passwords.html) \ndocumentation describes additional options.\n\nSince you're securing Elasticsearch, remember the `elastic` user's password. \n\nEnable transport layer security on each node.\n\n## Enabling Transport Layer Security\n\nThe following instructions for enabling TLS use `liferay` as the password\nwhenever one is needed. Use your own passwords for your installation. \n\n| **Important:** Elasticsearch and @product@ must share the keys and certificates\n| used to configure TLS. Copy them between servers and point to the local copy in\n| the corresponding configuration files.\n\n### Generate Node Certificates\n\n[Generate a node certificate](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/configuring-tls.html#node-certificates)\nfor each node. Alternatively, use a Certificate Authority to obtain node\ncertificates.\n\n1.  Create a certificate authority, using \n    [X-Pack's `certutil`](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/certutil.html)\n    command:\n\n    ```bash\n    ./bin/elasticsearch-certutil ca --pem --ca-dn CN=localhost\n    ```\n\n    This generates a ZIP file. Unzip the contents in the `[Elasticsearch\n    Home]/config/certs` folder.\n\n2.  Generate X.509 certificates and private keys using the CA from Step 1:\n\n    ```bash\n    ./bin/elasticsearch-certutil cert --pem --ca-cert /path/to/ca.crt --ca-key /path/to/ca.key --dns localhost --ip 127.0.0.1 --name localhost\n    ```\n\n    This generates another ZIP file. Extract the contents in the\n    `[Elasticsearch Home]/config/certs` folder.\n\n| **Note:** The `certutil` command defaults to using the *PKSC#12* format for\n| certificate generation. Since Kibana does not work with PKSC#12 certificates,\n| the `--pem` option (generates the certificate in PEM format) is important if\n| you're using X-Pack monitoring.\n\n**Checkpoint:** You now have the following files in your `[Elasticsearch Home]/config/certs` folder:\n\n```bash\nca.crt\nca.key\nlocalhost.crt\nlocalhost.key\n```\n\n### Enable TLS for Elasticsearch 7\n\n[Enable TLS](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/configuring-tls.html#enable-ssl) \non each node via its `elasticsearch.yml`.\n\n1.  Enable transport layer TLS with these settings in `elasticsearch.yml` for\n    inter-node communication:\n\n    ```yaml\n    xpack.security.transport.ssl.enabled: true\n    ```\n\n2.  Add the certificate, key and certificate authority paths to each node's\n    `elasticsearch.yml`:\n\n    ```yaml\n    xpack.security.transport.ssl.certificate: certs/localhost.key\n    xpack.security.transport.ssl.certificate_authorities: [ \"certs/ca.crt\" ]\n    xpack.security.transport.ssl.key: certs/localhost.crt\n    xpack.security.transport.ssl.verification_mode: certificate\n    ```\n\n    The example paths above assume you added the certificate to `Elasticsearch\n    Home/config/`. \n\n3.  Enable TLS on the HTTP layer to encrypt client communication:\n\n    ```yaml\n    xpack.security.http.ssl.enabled: true\n    ```\n4.  Configure the certificate, key, and certificate authority paths to each\n    node's `elasticsearch.yml`:\n\n    ```yaml\n    xpack.security.http.ssl.certificate_authorities: [ \"certs/ca.crt\" ]\n    xpack.security.http.ssl.certificate: certs/localhost.crt\n    xpack.security.http.ssl.key: certs/localhost.key\n    xpack.security.http.ssl.verification_mode: certificate\n    ```\n\n### Elasticsearch 6 TLS\n\nThe settings on Elasticsearch 6 were slightly different than those presented\nabove for Elasticsearch 7.\n[Enable TLS](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/configuring-tls.html#enable-ssl)\non each node via its `elasticsearch.yml`.\n\n1.  Add the certificate, key and certificate authority paths to each node's\n    `elasticsearch.yml`:\n\n        xpack.ssl.certificate: certs/localhost.crt\n        xpack.ssl.certificate_authorities: [ \"certs/ca.crt\" ]\n        xpack.ssl.key: certs/localhost.key\n        xpack.ssl.verification_mode: certificate \n\n    The example paths above assume you added the certificate to `Elasticsearch\n    Home/config/`. \n\n2.  Enable transport layer TLS with these settings in `elasticsearch.yml`:\n\n        xpack.security.transport.ssl.enabled: true\n\n3.  Enable TLS on the HTTP layer to encrypt client communication:\n\n        xpack.security.http.ssl.enabled: true\n\nAfter X-Pack is installed and TLS is enabled, configure the X-Pack Security\nadapter in @product@.\n\n### Example Elasticsearch Security Configuration\n\nFor ease of copying and pasting, here is the complete Elasticsearch\nconfiguration (`elasticsearch.yml`) used in this guide (with the Elasticsearch 6\nexample commented out):\n\n```yaml\n# For Elasticsearch 7.3/7.4\ncluster.name: LiferayElasticsearchCluster\n\n# X-Pack Security\nxpack.security.enabled: true\n\n## TLS/SSL settings for Transport layer\nxpack.security.transport.ssl.enabled: true\nxpack.security.transport.ssl.verification_mode: certificate \nxpack.security.transport.ssl.key: certs/localhost.key\nxpack.security.transport.ssl.certificate: certs/localhost.crt\nxpack.security.transport.ssl.certificate_authorities : [ \"certs/ca.crt\" ]\n\n# TLS/SSL settings for HTTP layer\nxpack.security.http.ssl.enabled: true\nxpack.security.http.ssl.verification_mode: certificate \nxpack.security.http.ssl.key: certs/localhost.key\nxpack.security.http.ssl.certificate: certs/localhost.crt\nxpack.security.http.ssl.certificate_authorities : [ \"certs/ca.crt\" ]\n\n# Comment out when Kibana and Liferay's X-Pack Monitoring are also configured\n#xpack.monitoring.collection.enabled: true\n\n# For Elasticsearch 6.5/6.8\n#cluster.name: LiferayElasticsearchCluster\n#\n# X-Pack Security\n#xpack.security.enabled: true\n#\n# Enable TLS/SSL\n#xpack.security.transport.ssl.enabled: true # To enable Transport level SSL for internode-communication\n#xpack.security.http.ssl.enabled: true # To enable HTTP level SSL required by Kibana\n#\n## General TLS/SSL settings for both Transport and HTTP levels\n#xpack.ssl.verification_mode: certificate \n#xpack.ssl.key: certs/localhost.key\n#xpack.ssl.certificate: certs/localhost.crt\n#xpack.ssl.certificate_authorities : [ \"certs/ca.crt\" ]\n#\n# Comment out when Kibana and Liferay's X-Pack Monitoring are also configured\n#xpack.monitoring.collection.enabled: true\n```\n\nFor both Elasticsearch 6 and Elasticsearch 7, the Liferay Connector settings\nare the same.\n\n## Install and Configure the Liferay Enterprise Search Security app\n\nIf you have a Liferay Enterprise Search subscription, \n[download](https://web.liferay.com/group/customer/dxp/downloads/enterprise-search) \nthe Liferay Enterprise Search Security app . Install the LPKG\nfile by copying it into the `Liferay Home/deploy` folder. \n\nTo configure the X-Pack adapter, navigate to *Control Panel* &rarr;\n*Configuration* &rarr; *System Settings*. Find the *Search* category and click\non the *X-Pack Security* entry. You can enter the property values here, but it's\nmore common to use a \n[configuration file](/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files)\ndeployed to `[Liferay Home]/osgi/configs`. For the X-Pack security connector, create a file\ncalled\n\n```\ncom.liferay.portal.search.elasticsearch7.configuration.XPackSecurityConfiguration.config\n```\n\nThe exact contents of the file depend on your X-Pack setup. To configure the\nadapter according to the Elasticsearch setup documented here, populate the file\nlike this:\n\n```properties\nsslKeyPath=\"/path/to/localhost.key\"\nsslCertificatePath=\"/path/to/localhost.crt\"\ncertificateFormat=\"PEM\"\nrequiresAuthentication=\"true\"\nusername=\"elastic\"\npassword=\"liferay\"\nsslCertificateAuthoritiesPaths=\"/path/to/ca.crt\"\ntransportSSLVerificationMode=\"certificate\"\ntransportSSLEnabled=\"true\"\n```\n\nThe `password` should match what you set during the X-Pack password\nsetup above. \n\nThe certificate and key files referenced here are the same ones used on the\nElasticsearch server. Copy them to the @product@ server and update their paths\nin the configuration accordingly.\n\nEnable authentication by setting `requiresAuthentication` to `true` and providing the\ncredentials for the Elasticsearch user. For TLS, enable transport TLS, set the\ncertificate verification mode and certificate format, and provide the path to\nthe certificate, key, and certificate authority. Of course, the exact values\ndepend on your X-Pack configuration. \n\nHere's the complete list of configuration options for the X-Pack Connector:\n\n- `sslKeyPath`\n- `sslCertificatePath`\n- `sslCertificateAuthoritiesPaths`\n- `certificateFormat`\n- `requiresAuthentication`\n- `username`\n- `password`\n- `transportSSLVerificationMode`\n- `transportSSLEnabled`\n- `sslKeystorePath`\n- `sslKeystorePassword`\n- `sslTruststorePath`\n- `sslTruststorePassword`\n\nWhen you're finished configuring X-Pack Security, restart Elasticsearch. These\nsteps require a full cluster restart.\n\n### Disabling Elasticsearch Deprecation Logging\n\nSome Elasticsearch APIs used by Liferay's Elasticsearch 6 connector were\ndeprecated as of Elasticsearch 6.6 and 6.7. This can result WARN log entries in\nElasticsearch's deprecation log when @product@ is configured with Elasticsearch\n6.8.x and X-Pack Security is enabled:\n\n```sh\n2019-07-16T14:47:05,779][WARN ][o.e.d.c.j.Joda           ] [\node_name]'y' year should be replaced with 'u'. Use 'y' for year-of-era. Prefix your date format with '8' to use the new specifier.\n[2019-07-16T14:47:06,007][WARN ][o.e.d.c.s.Settings       ] [\node_name][xpack.ssl.certificate] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version.\n[2019-07-16T14:47:06,007][WARN ][o.e.d.c.s.Settings       ] [\node_name][xpack.ssl.certificate_authorities] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version.\n[2019-07-16T14:47:06,008][WARN ][o.e.d.c.s.Settings       ] [\node_name][xpack.ssl.key] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version.\n[2019-07-16T14:47:06,463][WARN ][o.e.d.x.c.s.SSLService   ] [\node_name]SSL configuration [xpack.http.ssl] relies upon fallback to another configuration for [key configuration, trust configuration], which is deprecated.\n[2019-07-16T14:47:06,464][WARN ][o.e.d.x.c.s.SSLService   ] [\node_name]SSL configuration [xpack.security.transport.ssl.] relies upon fallback to another configuration for [key configuration, trust configuration], which is deprecated.\n```\n\nThese warnings do not signal any functional issues, and can be disabled (see\n[here](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/logging.html#deprecation-logging)\nto learn how).\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/02-elasticsearch/08-backing-up-es.markdown",
    "content": "---\nheader-id: backing-up-elasticsearch\n---\n\n# Backing Up Elasticsearch\n\n[TOC levels=1-4]\n\n[Elasticsearch replicas](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index-modules.html#index-modules-settings)\nprotect against a node going down, but they won't help you with a catastrophic\nfailure. Only good backup practices can help you then.\n\nBack up and restore your Elasticsearch cluster in three steps: \n\n1.  Configure a repository\n\n2.  Make a snapshot of the cluster\n\n3.  Restore from the snapshot\n\nFor more detailed information, refer to the \n[Elasticsearch administration guide](https://www.elastic.co/guide/en/elasticsearch/guide/master/administration.html),\nand in particular to the documentation on the \n[Snapshot and Restore module](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/snapshot-restore.html).\n\n## Creating a Repository\n\nFirst [create a repository](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/snapshots-register-repository.html)\nto store your snapshots. Several repository types are supported:\n\n- Shared file system, such as a Network File System or NAS\n- Amazon S3\n- HDFS (Hadoop Distributed File System)\n- Azure Cloud\n\nIf using a shared file system repository type, first register the path to the\nshared file system in each node's `elasticsearch.yml` using \n[the path.repo setting](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/snapshots-register-repository.html#snapshots-filesystem-repository).\n\n```yaml\npath.repo: [\"path/to/shared/file/system/\"]\n```\n\nOnce the path to the folder hosting the repository is registered (make sure the\nfolder exists), create the repository with a PUT command. For example,\n\n```bash\ncurl -X PUT \"localhost:9200/_snapshot/test_backup\" -H 'Content-Type: application/json' -d'\n{\n  \"type\": \"fs\",\n  \"settings\": {\n    \"location\": \"/path/to/shared/file/system/\"\n  }\n}'\n```\n\nReplace `localhost:9200` with the proper `hostname:port` combination for your\nsystem, replace `test_backup` with the name of the repository to create, and use\nthe absolute path to your shared file system in the `location`.\n\nIf the repository is set up successfully, you see this message:\n\n```json\n{\"acknowledged\":true}\n```\n\nOnce the repository exists, you can start creating snapshots.\n\n## Taking Snapshots of the Cluster\n\nThe easiest snapshot approach is to create a \n[snapshot of all the indexes in your cluster](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/snapshots-take-snapshot.html). \nFor example,\n\n```bash\ncurl -XPUT localhost:9200/_snapshot/test_backup/snapshot_1\n```\n\nIf `{\"accepted\":true}` appears in the terminal, the snapshot was a success.\n\nIt's possible to be more selective when taking snapshots. For example, if you\nuse LES Monitoring, you can exclude the monitoring indexes. Explicitly declare\nthe indexes to include in the snapshot. For example,\n\n```bash\ncurl -XPUT localhost:9200/_snapshot/test_backup/snapshot_2\n{ \"indices\": \"liferay-0,liferay-20116\" }\n```\n\n**Note:** For a list of all the Elasticsearch indexes, use this command:\n\n```bash\ncurl -X GET \"localhost:9200/_cat/indices?v\"\n```\n\nThis shows the index metrics:\n\n```bash\nhealth status index         uuid                   pri rep docs.count docs.deleted store.size pri.store.size\ngreen  open   liferay-20099 obqiNE1_SDqfuz7rincrGQ   1   0        195            0    303.1kb        303.1kb\ngreen  open   liferay-47206 3YEjtye1S9OVT0i0EZcXcw   1   0          7            0     69.7kb         69.7kb\ngreen  open   liferay-0     shBWwpkXRxuAmGEaE475ug   1   0        147            1    390.9kb        390.9kb\n```\n\nIt's important to note that Elasticsearch uses a *smart snapshots* approach. To\nunderstand what that means, consider a single index. The first snapshot includes\na copy of the entire index, while subsequent snapshots only include the delta\nbetween the first, complete index snapshot and the current state of the index.\n\nEventually you'll end up with a lot of snapshots in your repository, and no\nmatter how cleverly you name the snapshots, you may forget what some snapshots\ncontain. For this purpose, the Elasticsearch API provides getting information\nabout any snapshot. For example:\n\n```bash\ncurl -XGET localhost:9200/_snapshot/test_backup/snapshot_1\n```\n\nreturns\n\n```json\n{\"snapshots\":[\n    {\"snapshot\":\"snapshot_1\",\n    \"uuid\":\"WlSjvJwHRh-xlAny7zeW3w\",\n    \"version_id\":6.80399,\n    \"version\":\"6.8.2\",\n    \"indices\":[\"liferay-20099\",\"liferay-0\",\"liferay-47206\"],\n    \"state\":\"SUCCESS\",\n    \"start_time\":\"2018-08-15T21:40:17.261Z\",\n    \"start_time_in_millis\":1534369217261,\n    \"end_time\":\"2018-08-15T21:40:17.482Z\",\n    \"end_time_in_millis\":1534369217482,\n    \"duration_in_millis\":221,\n    \"failures\":[],\n    \"shards\":{\n        \"total\":3,\n        \"failed\":0,\n        \"successful\":3\n        \n        }\n    }\n]}\n```\n\nThere's lots of useful information here, including which indexes were\nincluded in the snapshot.\n\nIf you want to get rid of a snapshot, use the `DELETE` command.\n\n```bash\ncurl -XDELETE localhost:9200/_snapshot/test_backup/snapshot_1\n```\n\nYou might trigger creation of a snapshot and regret it (for example, you didn't\nwant to include all the indexes in the snapshot). If your snapshots contain\na lot of data, this can cost time and resources. To cancel the ongoing creation\nof a snapshot, use the same `DELETE` command.  The snapshot process is\nterminated and the partial snapshot is deleted from the repository.\n\n## Restoring from a Snapshot\n\nWhat good is a snapshot if you can't use it to \n[restore your search indexes](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/snapshots-restore-snapshot.html) \nin case of catastrophic failure? Use the `_restore` API to restore all the\nsnapshot's indexes:\n\n```bash\ncurl -XPOST localhost:9200/_snapshot/test_backup/snapshot_1/_restore\n```\n\nRestore only specific indexes from a snapshot by passing in the `indices`\noption, and rename the indexes using the `rename_pattern` and\n`rename_replacement` options:\n\n```bash\ncurl -XPOST\nlocalhost:9200/_snapshot/test_backup/snapshot_1/_restore\n{\n    \"indices\": \"liferay-20116\",\n    \"rename_pattern\": \"liferayindex_(.+)\",\n    \"rename_replacement\": \"restored_liferayindex_$1\"\n}\n```\n\nThis restores only the index named `liferay-20116index_1` from the snapshot. The\n`rename...` settings specify that the beginning `liferayindex_` are replaced\nwith `restored_liferayindex_`, so `liferay-20116index_1` becomes\n`restored_liferay-20116index_1`.\n\nAs with the process for taking snapshots, an errant restored index can be\ncanceled with the `DELETE` command:\n\n```bash\ncurl -XDELETE localhost:9200/restored_liferay-20116index_3\n```\n\nNobody likes catastrophic failure on a production system, but Elasticsearch's\nAPI for taking snapshots and restoring indexes can help you rest easy knowing\nthat your search cluster can be restored if disaster strikes. For more details\nand options, read Elastic's documentation on the [Snapshot and Restore\nModule](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/snapshot-restore.html).\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/02-elasticsearch/09-upgrading-to-elasticsearch_65.markdown",
    "content": "---\nheader-id: upgrading-to-elasticsearch-6-5\n---\n\n# Upgrading to Elasticsearch 6.5\n\n[TOC levels=1-4]\n\nElasticsearch 6.5.x is the default, most up-to-date supported version of\nElasticsearch for @product-ver@. If you're upgrading @product@ and still running\nElasticsearch 6.1, it's time to upgrade your Elasticsearch servers too. If\nyou're setting up a new system and not already running a remote Elasticsearch\n6.1.x server, follow the \n[installation guide](/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch) to install\nElasticsearch and the \n[configuration guide](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector)\nto configure the Elasticsearch adapter. Here, you'll learn to upgrade an\nexisting Elasticsearch 6.1.x server (or cluster) to Elasticsearch 6.5.x: \n\n1.  [Install and configure Elasticsearch 6.5.x](/docs/7-2/deploy/-/knowledge_base/d/elasticsearch).\n\n2. Disable X-Pack Security in `elasticsearch.yml` unless you have an Liferay\n   Enterprise Search subscription, which gives you access to the Liferay\n   Enterprise Search Security app:\n\n    ```yml\n    xpack.security.enabled: false\n    ```\n\n3.  Configure the bundled Liferay Connector to Elasticsearch 6.\n\n4.  Re-index all search  and spell check indexes.\n\n| **Before Proceeding,** back up your existing data before upgrading\n| Elasticsearch. If something goes wrong during or after the upgrade, roll back\n| to the previous version using the uncorrupted index snapshots. See\n| [here](/docs/7-2/deploy/-/knowledge_base/d/backing-up-elasticsearch) for more\n| information.\n\nLearn about configuring Elasticsearch [here](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector).\n\n## Re-index\n\nOnce the Elasticsearch adapter is installed and talking to the Elasticsearch\ncluster, navigate to *Control Panel* &rarr; *Configuration* &rarr; *Server\nAdministration*, and click *Execute* for the *Reindex all search indexes* entry.\n\nYou must also re-index the spell check indexes.\n\n## Reverting to Elasticsearch 6.1 \n\nStuff happens. If that stuff involves an unrecoverable failure during the\nupgrade to Elasticsearch 6.5, roll back to Elasticsearch 6.1 and regroup.\n\nSince your 6.1 and 6.5 are currently two separate installations, this procedure\nis straightforward:\n\n1.  Stop the Liferay Connector to Elasticsearch 6.\n\n2.  Stop Elasticsearch 6.5 and make sure that the Elasticsearch 6.1\n    `elasticsearch.yml` and the connector app are configured to use the same\n    port (9200 by default).\n\n3.  Start the Elasticsearch server, and then restart the Liferay Connector to\n    Elasticsearch 6.\n\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/02-elasticsearch/10-upgrading-to-elasticsearch_68.markdown",
    "content": "---\nheader-id: upgrading-to-elasticsearch-6-8\n---\n\n# Upgrading to Elasticsearch 6.8\n\n[TOC levels=1-4]\n\nElasticsearch 6.8.x is supported for @product-ver@. If you're upgrading\n@product@ and still running Elasticsearch 6.1, it's time to upgrade your\nElasticsearch servers too. If you're setting up a new system and not already\nrunning a remote Elasticsearch 6.1.x server, follow the \n[installation guide](/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch) to install\nElasticsearch and the \n[configuration guide](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector)\nto configure the Elasticsearch adapter. Here, you'll learn to upgrade an\nexisting Elasticsearch 6.1.x server (or cluster) to Elasticsearch 6.8.x:\n\n1.  [Install and configure Elasticsearch 6.8.x](/docs/7-2/deploy/-/knowledge_base/d/elasticsearch).\n\n2. Disable X-Pack Security in `elasticsearch.yml` unless you have an Liferay Enterprise Search subscription which gives you access to the LES Security app:\n\n    ```yml\n    xpack.security.enabled: false\n    ```\n\n3.  Configure the bundled Liferay Connector to Elasticsearch 6.\n\n4.  Re-index all search  and spell check indexes.\n\n| **Before Proceeding,** back up your existing data before upgrading\n| Elasticsearch. If something goes wrong during or after the upgrade, roll back\n| to the previous version using the uncorrupted index snapshots. See\n| [here](/docs/7-2/deploy/-/knowledge_base/d/backing-up-elasticsearch) for more\n| information.\n\nLearn about configuring Elasticsearch [here](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector).\n\n## Re-index\n\nOnce the Elasticsearch adapter is installed and talking to the Elasticsearch\ncluster, navigate to *Control Panel* &rarr; *Configuration* &rarr; *Server\nAdministration*, and click *Execute* for the *Reindex all search indexes* entry.\n\nYou must also re-index the spell check indexes.\n\n## Reverting to Elasticsearch 6.1 \n\nStuff happens. If that stuff involves an unrecoverable failure during the\nupgrade to Elasticsearch 6.8, roll back to Elasticsearch 6.1 and regroup.\n\nSince your 6.1 and 6.8 are currently two separate installations, this procedure\nis straightforward:\n\n1.  Stop the Liferay Connector to Elasticsearch 6.\n\n2.  Stop Elasticsearch 6.8 and make sure that the Elasticsearch 6.1\n    `elasticsearch.yml` and the connector app are configured to use the same\n    port (9200 by default).\n\n3.  Start the Elasticsearch server, and then restart the Liferay Connector to\n    Elasticsearch 6.\n\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/02-elasticsearch/11-upgrading-to-elasticsearch-73.markdown",
    "content": "---\nheader-id: upgrading-to-elasticsearch-7\n---\n\n# Upgrading to Elasticsearch 7\n\n[TOC levels=1-4]\n\nElasticsearch 7 is supported for @product-ver@. If you're upgrading\n@product@ and still running Elasticsearch 6, consider upgrading your\nElasticsearch servers too. If you're setting up a new system and not already\nrunning a remote Elasticsearch 6 server, follow the \n[installation guide](/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch) to install\nElasticsearch and the \n[configuration guide](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector)\nto configure the Elasticsearch adapter.\n\n| **Before Proceeding,** back up your existing data before upgrading\n| Elasticsearch. If something goes wrong during or after the upgrade, roll back\n| to the previous version using the uncorrupted index snapshots. See\n| [here](/docs/7-2/deploy/-/knowledge_base/d/backing-up-elasticsearch) for more\n| information.\n\nHere, you'll learn to upgrade an\nexisting Elasticsearch 6 server (or cluster) to Elasticsearch 7: \n\n1.  [Install and configure Elasticsearch 7](/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch).\n\n2.  Back up the application specific indexes for Workflow Metrics and Result\n    Rankings.\n\n3. In @product-ver@, security is now provided out of the box. If you're using\n   X-Pack security, enable it (it's disabled by default):\n\n    ```yml\n    xpack.security.enabled: true\n    ```\n\n4.  Blacklist the bundled Liferay Connector to Elasticsearch 6.\n\n5.  Install and configure the Liferay Connector to Elasticsearch 7.\n\n6.  Re-index all search  and spell check indexes.\n\n| **Known Issue:** See\n| [LPS-103938](https://issues.liferay.com/browse/LPS-103938). The Liferay\n| Connector to Elasticsearch 7 throws an exception in the log when the LPKG\n| file is deployed. There are no known functional impacts. If unexpected errors\n| occur, re-start the @product@ server.\n\nLearn about configuring Elasticsearch [here](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector).\n\n## Backing up Application-Specific Indexes\n\nTo preserve data stored in application-specific indexes, use a\n[rolling upgrade](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/rolling-upgrades.html)\nfor each index you need to preserve across the upgrade.\n\n| **Synonym Sets:** If you follow the workaround for the bug\n| [LPS-100272](https://issues.liferay.com/browse/LPS-100272), your Synonym sets\n| are preserved across the upgrade, as they are stored in the index settings\n| directly, and not in their own index.\n\n## Blacklisting Elasticsearch 6\n\nTo blacklist Elasticsearch 6,\n\n1.  Create a configuration file named\n\n    ```bash\n    com.liferay.portal.bundle.blacklist.internal.BundleBlacklistConfiguration.config\n    ```\n\n2.  Give it these contents:\n\n    ```properties\n    blacklistBundleSymbolicNames=[ \\\n        \"com.liferay.portal.search.elasticsearch6.api\", \\\n        \"com.liferay.portal.search.elasticsearch6.impl\", \\\n        \"com.liferay.portal.search.elasticsearch6.spi\", \\\n        \"com.liferay.portal.search.elasticsearch6.xpack.security.impl\", \\\n        \"Liferay Connector to X-Pack Security [Elastic Stack 6.x] - Impl\", \\\n        \"Liferay Enterprise Search Security  - Impl\" \\\n    ]\n    ```\n\n## Re-index\n\nOnce the Elasticsearch adapter is installed and talking to the Elasticsearch\ncluster, navigate to *Control Panel* &rarr; *Configuration* &rarr; *Search*,\nand click *Execute* for the *Reindex all search indexes* entry.\n\nYou must also re-index the spell check indexes.\n\n## Reverting to Elasticsearch 6\n\nStuff happens. If that stuff involves an unrecoverable failure during the\nupgrade to Elasticsearch 7, roll back to Elasticsearch 6 and regroup.\n\nSince your Elasticsearch 6 and 7 are currently two separate installations, this\nprocedure takes only a few steps:\n\n1.  Stop the Liferay Connector to Elasticsearch 6.\n\n2.  Stop Elasticsearch 7 and make sure that the Elasticsearch 6\n    `elasticsearch.yml` and the connector app are configured to use the same\n    port (9200 by default).\n\n3.  Start the Elasticsearch server, and then restart the Liferay Connector to\n    Elasticsearch 6.\n\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/03-solr/01-installing-solr-intro.markdown",
    "content": "---\nheader-id: installing-solr\n---\n\n# Installing Solr\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/using-search/installing-and-upgrading-a-search-engine/solr/installing-solr.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nSolr is a popular enterprise search platform built on Apache Lucene. It's\nreliable, scalable, and fault tolerant. Read more about it\n[here](http://lucene.apache.org/solr/).\n\n[Elasticsearch](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector)\nis the default search engine that ships with @product@, and some Liferay Search\nfeatures are only available on Elasticsearch. It's valid, however, to use Solr\ninstead. In particular, if you've already been using Solr with a previous\nversion of @product@, or your platform (for example, your OS or JVM) \n[isn't supported by Elasticsearch](https://www.elastic.co/support/matrix), you might\nchoose to use Solr to search and index your @product@ data.\n\nThere are circumstances that force you to use Elasticsearch instead of Solr.\nRead\n[here](/docs/7-2/deploy/-/knowledge_base/d/installing-a-search-engine#choosing-a-search-engine)\nfor more information.\n\nLiferay DXP 7.2, Fix Pack 1, supports Solr 7.5.x through the Liferay\nConnector to Solr 7 application, version 2.0.0.\n\nLiferay DXP 7.2, Service Pack 1/Fix Pack 2 and later, supports Solr 7.5.x\nthrough the Liferay Connector to Solr 7 application, version 2.0.1.\n\nLiferay Portal CE 7.2, GA2 and later (not available at time of writing), support\nSolr 7.5.x through the Liferay CE Connector to Solr 7 application.\n\n| **Upgrading to Service Pack 1 or Fix Pack 2 (or later) requires installation of\n| a new Solr connector:** If you were running version 2.0.0 of the Liferay\n| Connector to Solr 7 application, and you want to install Service Pack 1/Fix Pack\n| 1 (or later), you must install version 2.0.1 of the Liferay Connector to Solr 7\n| application.\n\n## Blacklisting Elasticsearch-Only Features\n\nBefore installing Solr, you must \n[blacklist](/docs/7-2/user/-/knowledge_base/u/blacklisting-osgi-bundles-and-components) \ncertain DXP \n[features that only work with Elasticsearch](/docs/7-2/deploy/-/knowledge_base/d/installing-a-search-engine#choosing-a-search-engine). \n\n1.  Create a configuration file named\n\n    ```sh\n    com.liferay.portal.bundle.blacklist.internal.BundleBlacklistConfiguration.config\n    ```\n\n2.  Give it these contents:\n\n    ```properties\n    blacklistBundleSymbolicNames=[\"com.liferay.portal.search.tuning.web.api\",\"com.liferay.portal.search.tuning.web\",\"com.liferay.portal.search.tuning.synonyms.web\",\"com.liferay.portal.search.tuning.rankings.web\"]\n    ```\n\n3. Place the file in `Liferay Home/osgi/configs`. \n\nIt is required during the Solr installation process to also \n[stop the Elasticsearch Connectors](https://portal.liferay.dev/docs/7-2/deploy/-/knowledge_base/d/installing-solr-basic-installation#stopping-the-elasticsearch-connector) \nthat ship with @product@. If you're ready to blacklist those bundles now, use\nthese contents in the blacklist configuration file:\n\n```properties\n    blacklistBundleSymbolicNames=[\"com.liferay.portal.search.tuning.web.api\",\"com.liferay.portal.search.tuning.web\",\"com.liferay.portal.search.tuning.synonyms.web\",\"com.liferay.portal.search.tuning.rankings.web\",\"com.liferay.portal.search.elasticsearch6.spi\",\"com.liferay.portal.search.elasticsearch6.api\",\"com.liferay.portal.search.elasticsearch6.impl\",\"Liferay Enterprise Search Monitoring \",\"Liferay Enterprise Search Security \"]\n```\n\nThe Liferay Enterprise Search bundles must be excluded if you don't have a LES subscription.\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/03-solr/02-installing-solr-basic.markdown",
    "content": "---\nheader-id: installing-solr-basic-installation\n---\n\n# Installing Solr: Basic Installation\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/using-search/installing-and-upgrading-a-search-engine/solr/installing-solr.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThere are two ways to install the Liferay Connector to Solr 7:\n\n1. Navigate to \n   [Liferay Marketplace](https://web.liferay.com/marketplace/)\n   and download the app that corresponds to your portal.\n\n<!-- Update links when available\n   **Liferay Portal CE:** [Liferay CE Connector to Solr 7](https://web.liferay.com/marketplace/-/mp/application/118014614) \n\n   **Liferay DXP:** [Liferay Connector to Solr 7](https://web.liferay.com/marketplace/-/mp/application/117931595)\n-->\n\n   Once the app LPKG is downloaded, copy it to `Liferay_Home/osgi/marketplace`.\n\n2. In your running portal instance, navigate to *Control Panel* &rarr; *Apps*\n   &rarr; *Store*. Sign in using your credentials, search for Solr Search\n   Engine, and purchase (it's free) the Liferay Connector to Solr 7 entry.\n\nAs you proceed, remember these terms: \n\n*Solr Home*: The center of the Solr system (pun intended). This directory is\n`solr-[version]/server/solr`.\n\n*Liferay Home*: The root folder of your @product@ installation. It contains\nthe `osgi`, `deploy`, `data`, and `license` folders, among others.\n\nThere are two installation steps:\n\n1.  Installing and configuring Solr 7.\n\n2.  Installing and configuring the Solr 7 connector for @product@.\n\nBefore configuring @product@ for Solr, install and set up Solr.\n\n## Installing and Configuring Solr 7\n\nTo install and properly configure Solr for @product@:\n\n1.  Download \n    [Solr](http://archive.apache.org/dist/lucene/solr/7.5.0/) \n    and unzip it.\n\n2.  Navigate to `solr-[version]/server/solr`. This is Solr Home.\n\n3.  Create a new folder called `liferay`.\n\n4.  In the `liferay` folder, create two new folders: `conf` and `data`.\n\n5.  Copy the contents of `Solr_Home/configsets/_default/conf`\n    to `Solr_Home/liferay/conf`.\n\n6.  Open the Liferay Connector to Solr 7's LPKG file with an archive manager.\n\n    Open the `com.liferay.portal.search.solr7.impl.jar` file, and extract \n\n        META-INF/resources/solrconfig.xml\n\n    and\n\n        META-INF/resources/schema.xml\n\n    to\n\n        Solr_Home/liferay/conf\n\n    This replaces the current `solrconfig.xml` and `schema.xml` files with ones\n    that tell Solr how to index data coming from @product@.\n\n7.  Create a `core.properties` file in `Solr_Home/liferay` and add this\n    configuration:\n\n    ```properties\n    config=solrconfig.xml\n    dataDir=data\n    name=liferay\n    schema=schema.xml\n    ```\n\n8.  Checkpoint: your `Solr_Home/liferay` folder now has this structure:\n\n    ```sh\n    liferay\n    ├── conf\n    │   ├── lang\n    │   │   ├── contractions_ca.txt\n    │   │   ├── ....txt\n    │   ├── managed-schema\n    │   ├── params.json\n    │   ├── protwords.txt\n    │   ├── schema.xml\n    │   ├── solrconfig.xml\n    │   ├── stopwords.txt\n    │   └── synonyms.txt\n    ├── core.properties\n    └── data\n    ```\n\n8.  Start the Solr server by entering\n\n        ./bin/solr start -f\n\n    from the top-level folder of your Solr installation (`solr-[version]`).\n\n9.  The Solr server listens on port `8983` by default. Navigate to\n    `http://localhost:8983/solr/#/~cores` (assuming you're testing locally with\n    `localhost` as your host), and confirm that the `liferay` core is available.\n\nSolr is now installed. Next install and configure the Solr connector.\n\n## Installing and Configuring the Liferay Solr Adapter\n\nSince Elasticsearch is the default search engine, the Elasticsearch connector is\nalready installed and running. You must stop it before configuring the Solr\nconnector.\n\n### Stopping the Elasticsearch Connector\n\nStop the Elasticsearch connector bundle using the App Manager, the Felix Gogo\nshell, or the bundle blacklist. If you're a Liferay DXP customer, use the\nblacklist feature as described below. The App Manager and Gogo shell rely on the\n`osgi/state` folder to \"remember\" the state of the bundle. If you delete this\nfolder (recommended during patching) the Elasticsearch connector is reinstalled\nand started automatically. \n\nNavigate to Control Panel &rarr; Apps &rarr; App Manager.\n\nOnce in the App Manager, search for *elasticsearch*. Find the Liferay \nConnector to Elasticsearch 6 module and click the Actions\n((![Actions](../../../images/icon-actions.png))) button. Choose the Deactivate option.\nThis leaves the bundle installed, but stops it in the OSGi runtime.\n\nAlternatively, use the \n[Felix Gogo shell](/developer/tutorials/-/knowledge_base/7-2/using-the-felix-gogo-shell) \nto stop the Elasticsearch connector. Enter\n\n    lb elasticsearch\n\nYou'll see two active bundles for the Liferay Connector to Elasticsearch 6:\nan API and an IMPL bundle. \n\n```sh\nID|State      |Level|Name\n476|Active     |   10|Liferay (CE) Connector to Elasticsearch 6 - API (3.0.0)\n478|Active     |   10|Liferay Portal Search Elasticsearch 6 API (3.0.4)\n480|Active     |   10|Liferay Portal Search Elasticsearch 6 SPI (3.2.1)\n706|Active     |   10|Liferay (CE) Connector to Elasticsearch 6 - Impl (3.0.0)\n707|Active     |   10|Liferay Portal Search Elasticsearch 6 Implementation (3.0.15)\n\n```\n\nStop the API bundle by entering \n\n    stop [bundle ID]\n\nIn the example above, the `[bundle ID]` is `476`. \n\n**Liferay DXP:** DXP customers should\n[blacklist](/docs/7-2/user/-/knowledge_base/u/blacklisting-osgi-bundles-and-components)\nthe Elasticsearch, Shield, and Marvel plugins.\n\n1.  Create a \n\n        com.liferay.portal.bundle.blacklist.internal.BundleBlacklistConfiguration.config\n\n    file with these contents:\n\n    ```properties\n    blacklistBundleSymbolicNames=[\"com.liferay.portal.search.elasticsearch6.spi\",\"com.liferay.portal.search.elasticsearch6.api\",\"com.liferay.portal.search.elasticsearch6.impl\",\"Liferay Enterprise Search Monitoring \",\"Liferay Enterprise Search Security \"]\n    ```\n\n    If the LES Security and Monitoring LPKG files are installed, you must blacklist these too.\n\n2.  Place the file in `Liferay Home/osgi/configs`.\n\n### Install and Configure the Solr Connector\n\nNow you're ready to install the connector:\n\n1.  Start @product@, then deploy the Solr connector by copying the LPKG you\n    downloaded to `Liferay_Home/deploy`.\n\n    You'll see a `STARTED` message in your @product@ log once the Solr connector is\n    installed. Here's what the log message looks like:\n\n    ```sh\n    2018-11-06 19:59:49.396 INFO  [pipe-start 943 944][BundleStartStopLogger:39] STARTED com.liferay.portal.search.solr7.api_2.0.5 [943]\n    2018-11-06 19:59:49.490 INFO  [pipe-start 943 944][BundleStartStopLogger:39] STARTED com.liferay.portal.search.solr7.impl_2.0.11 [944]\n    ```\n\n2.  To re-index against Solr, navigate to *Control Panel* &rarr; *Configuration*\n    &rarr; *Search*, and click *Execute* next to the *Reindex all\n    search indexes* option.\n\n   ![Figure 1: Once the Solr connector is installed, you can re-index your @product@ data against your Solr server.](../../../images/solr-reindex.png)\n\nIn production deployments, specify your edits to the Solr connector's default\nconfigurations using a configuration file deployed to the `Liferay_Home/osgi/configs`\nfolder. Name the file \n\n    com.liferay.portal.search.solr7.configuration.SolrConfiguration.config\n\nDuring testing and development, use the Solr 7 System Settings entry Control\nPanel &rarr; Configuration &rarr; System Settings for editing the default\nconfigurations.\n\n![Figure 2: You can configure Solr from @product@'s System Settings application. This is most useful during development and testing.](../../../images/solr-system-settings.png)\n\nThe next article covers clustering Solr with SolrCloud.\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/03-solr/03-installing-solr-solrcloud.markdown",
    "content": "---\nheader-id: high-availability-with-solrcloud\n---\n\n# Installing Solr: High Availability with SolrCloud\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/using-search/installing-and-upgrading-a-search-engine/solr/installing-solr.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nUse SolrCloud if you need a cluster of Solr servers. Note that to use SolrCloud\nin production, you should set up an \n[external ZooKeeper ensemble](https://cwiki.apache.org/confluence/display/solr/Setting+Up+an+External+ZooKeeper+Ensemble).\n[ZooKeeper](http://zookeeper.apache.org/) is a centralized coordination service\nfor managing distributed systems like your SolrCloud cluster.\n\nThe steps included here should be considered the bare minimum of what must be\ndone to configure SolrCloud with @product@. For example, these instructions cover\nconfiguring SolrCloud on a single machine, whereas a production environment\nwould feature multiple physical or virtual machines. These instructions also\nassume you've followed the earlier section on *Installing and Configuring Solr\n7*. Refer to the [SolrCloud guide for more information](https://cwiki.apache.org/confluence/display/solr/SolrCloud).\n\n1. Stop the Solr server if it's running.\n\n2. Navigate to the `Solr_Home/configsets` folder and create a folder called \n\n        liferay_configs\n\n3. Copy the `conf` folder from `Solr_Home/liferay` to the `liferay_configs`\n   folder you just created.\n\n    The `configset/liferay_configs` folder contains the SolrCloud\n    @product@ collection configuration and is uploaded to ZooKeeper. By copying\n    the `conf` folder from the `liferay` server configured earlier, you're using\n    the `schema.xml` and `solrconfig.xml` files provided with the Liferay Solr\n    Adapter.\n\n4. Next launch an interactive SolrCloud session to configure your SolrCloud\n   cluster. Use this command:\n\n        ./bin/solr -e cloud\n\n5. Complete the setup wizard. These steps demonstrate creating a two-node\n   cluster:\n\n    -  Enter `2` for the number of nodes.\n    -  Specify ports `8983` and `7574` (the defaults). Both nodes are\n       started with the start commands printed in the log:\n\n       ```sh\n        Starting up Solr on port 8983 using command:\n        \"bin/solr\" start -cloud -p 8983 -s \"example/cloud/node1/solr\"\n\n        Waiting up to 180 seconds to see Solr running on port 8983 [|]  [-]  \n        Started Solr server on port 8983 (pid=8846). Happy searching!\n\n            \n        Starting up Solr on port 7574 using command:\n        \"bin/solr\" start -cloud -p 7574 -s \"example/cloud/node2/solr\" -z localhost:9983\n\n        Waiting up to 180 seconds to see Solr running on port 7574 [|]  [/]  \n        Started Solr server on port 7574 (pid=9026). Happy searching!\n        ```\n\n    -  Name the collection *liferay*.\n    -  Split the collection into two shards.\n    -  Specify two replicas per shard.\n    -  When prompted to choose a configuration, enter *liferay_configs*. You\n       should see a log message that concludes like this when the cluster has\n       been started:\n\n   ```sh\n    SolrCloud example running, please visit http://localhost:8983/solr\n    ```\n\nNow you have a new collection called *liferay* in your local SolrCloud cluster.\nVerify its status by running the *status* command:\n\n    ./bin/solr status\n\nYou'll see log output like this:\n\n```sh\nFound 2 Solr nodes: \n\nSolr process 12828 running on port 8983\nINFO  - 2019-07-18 16:46:35.137; org.apache.solr.util.configuration.SSLCredentialProviderFactory; Processing SSL Credential Provider chain: env;sysprop\n{\n  \"solr_home\":\"/home/russell/liferay-bundles/liferay-portal-7.2.10-ga1/solr-7.5.0/example/cloud/node1/solr\",\n  \"version\":\"7.5.0 b5bf70b7e32d7ddd9742cc821d471c5fabd4e3df - jimczi - 2018-09-18 13:07:55\",\n  \"startTime\":\"2019-07-18T20:44:13.138Z\",\n  \"uptime\":\"0 days, 0 hours, 2 minutes, 22 seconds\",\n  \"memory\":\"56.4 MB (%11.5) of 490.7 MB\",\n  \"cloud\":{\n    \"ZooKeeper\":\"localhost:9983\",\n    \"liveNodes\":\"2\",\n    \"collections\":\"1\"}}\n\n\nSolr process 12995 running on port 7574\nINFO  - 2019-07-18 16:46:35.848; org.apache.solr.util.configuration.SSLCredentialProviderFactory; Processing SSL Credential Provider chain: env;sysprop\n{\n  \"solr_home\":\"/home/russell/liferay-bundles/liferay-portal-7.2.10-ga1/solr-7.5.0/example/cloud/node2/solr\",\n  \"version\":\"7.5.0 b5bf70b7e32d7ddd9742cc821d471c5fabd4e3df - jimczi - 2018-09-18 13:07:55\",\n  \"startTime\":\"2019-07-18T20:44:16.847Z\",\n  \"uptime\":\"0 days, 0 hours, 2 minutes, 19 seconds\",\n  \"memory\":\"108.2 MB (%22.1) of 490.7 MB\",\n  \"cloud\":{\n    \"ZooKeeper\":\"localhost:9983\",\n    \"liveNodes\":\"2\",\n    \"collections\":\"1\"}}\n```\n\nTo stop Solr while running in SolrCloud mode, use the *stop* command, like this:\n\n    bin/solr stop -all\n\n## Configure the Solr Adapter for SolrCloud\n\nThere's only one thing left to do: specify the client type as *CLOUD* in\nLiferay's Solr connector.\n\n1. From System Settings or your OSGi configuration file, set the *Client Type*\n   to *CLOUD*.\n\n    ```properties\n    clientType=\"CLOUD\"\n    ```\n\n2. Start @product@ if it's not running already.\n\n![Figure 1: From the Solr 7 System Settings entry, set the _Client Type_ to _Cloud_.](../../../images/solr-client-type.png)\n\nNow you can configure @product@ for Solr and Solr for @product@. Remember that\nElasticsearch is the default search engine, so if you're not constrained to use\nSolr or already a Solr expert, consider Elasticsearch for your search engine\nrequirements. If you do use Solr, tell all your colleagues that your @product@\ninstallation's search capability is Solr powered (pun intended).\n"
  },
  {
    "path": "en/deployment/articles/03-installing-a-search-engine/03-solr/04-solr-settings.markdown",
    "content": "---\nheader-id: solr-connector-settings\n---\n\n# Solr Connector Settings\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/using-search/installing-and-upgrading-a-search-engine/solr/installing-solr.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nSolr can be configured for use with @product-ver@. Liferay Marketplace includes\na Solr connector app called the Liferay Connector to Solr 7. The connector is\nconfigurable through System Settings or an OSGi configuration file named\n`com.liferay.portal.search.solr7.configuration.SolrConfiguration.config` and\ndeployed to `[Liferay_Home]/osgi/configs`.\n\nThe list below is all the configuration settings for Liferay's Solr connector, in\nthe order they appear in the System Settings application:\n\n## Solr 7\n\n`authenticationMode=BASIC`\n: A String with the value of *BASIC* or *CERT*. Use BASIC when connecting using\nthe [Basic Authentication\nplugin](https://cwiki.apache.org/confluence/display/solr/Basic+Authentication+Plugin),\notherwise select CERT to connect using [2-way SSL\nauthentication](https://cwiki.apache.org/confluence/display/solr/Enabling+SSL).\n\n`clientType=REPLICATED`\n: A String with the value of *REPLICATED* or *CLOUD*. Use the default\n(REPLICATED) when connecting to a single-node Solr server. Specify CLOUD to\nconnect to SolrCloud (see the next section, titled *High Availability with\nSolrCloud* for more information).\n\n`logExceptionsOnly=true`\n: A boolean value that, when set to true, only logs exceptions from Solr,\nwithout rethrowing them.\n\n`readURL=http://localhost:8983/solr/liferay`\n: A String array with the URLs to which Liferay will send search requests. This\nwill be different from the `writeURL` if you use separate servers for indexing\n(write) and searching (read).\n\n`writeURL=http://localhost:8983/solr/liferay`\n: A String array with the URLs to which Liferay will send indexing requests.\nThis is different from the `readURL` if you use separate servers for\nindexing (write) and searching (read).\n\n`zkHost=localhost:9983`\n: A String with the ZooKeeper host and port. This is required when using the\nadapter in CLOUD mode.\n\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/01-liferay-security-intro.markdown",
    "content": "---\nheader-id: securing-product\n---\n\n# Securing @product@\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n[TOC levels=1-4]\n\nLiferay follows the OWASP Top 10 (2013) and CWE/SANS Top 25 lists to ensure\n@product@ is as secure as possible. Following these recommendations protects the\nproduct against known kinds of attacks and security vulnerabilities. For\nexample, @product@'s persistence layer is generated and maintained by the\nService Builder framework which prevents SQL Injection using Hibernate and\nparameter based queries.\n\nTo prevent Cross Site Scripting (XSS), user-submitted values are escaped on\noutput. To support integration features, @product@ doesn't encode input. Data is\nstored in the original form as submitted by the user. @product@ includes built-in\nprotection against CSRF attacks, Local File Inclusion, Open Redirects,\nUploading and serving files of dangerous types, Content Sniffing, Clickjacking,\nPath Traversal, and many other common attacks.\n\nTo stay on top, @product@ also contains fixes for state-of-the-art attacks and\ntechniques to improve product security. For example, @product@ uses PBKDF2 to\nstore passwords. @product@ also contains mitigation for Quadratic Blowup XXE\nattack, Rosetta Flash vulnerability, Reflected File Download, and other kinds\nof attacks.\n\nThis section of tutorials shows you how to configure various security and login\nfeatures, such as LDAP, single sign-on, Service Access Policies, and more. What\nfollows is an overview of what's available. \n\n## Authentication Overview\n\n@product@ user authentication can take place using any of a variety of prepared\nsolutions:\n\n- Form authentication using the Sign In Portlet with extensible adapters for\n  checking and storing credentials (portal database, LDAP)\n- Single-Sign-On (SSO) solutions - NTLM, CAS, SiteMinder, OpenSSO, OpenID,\n  Facebook\n- [SAML plugin ](https://www.liferay.com/marketplace/-/mp/application/15188711)\n- JAAS integration with application server\n\nNote: Although Liferay's SSO solutions are incompatible with WebDAV, they can\nbe used with Liferay Sync. See the\n[Publishing Files](/docs/7-1/user/-/knowledge_base/u/publishing-files) \narticle for more information on WebDAV and Liferay Sync.\n\nYou can authenticate and authorize apps remotely using the `AuthVerifier` layer:\n\n- Password based HTTP Basic + Digest authentication\n- Token based OAuth plugin\n- Portal session based solution for JavaScript applications\n\nBoth user authentication and remote application authentication are\n[extensible](/docs/7-2/frameworks/-/knowledge_base/f/authentication-pipelines).\nDevelopers can create custom Login portlets and plugins, extend the default\nLogin portlet `auth.pipeline`, create `AutoLogin` extensions for SSO, or create\ncustom `AuthVerifier` implementations.\n\n## Authorization and Permission Checking\n\nThere are several adjustable authorization layers in place to prevent\nunauthorized or unsecured access to data:\n\n- Remote IP and HTTPS transport check to limit access to @product@'s Java\n  servlets\n- Extensible Access Control Policies layer to perform any portal service\n  related authorization check\n- Extensible role-based permission framework for almost any portal entity or\n  data (stored in the portal database or elsewhere)\n- Portlet Container security checks to control portlet access\n- Remote IP check for portal remote API authentication methods\n- Service Access Policies to control access to portal remote API\n\n## Additional Security Features\n\nUsers can be assigned to sites, teams, user groups, or organizations. Custom\nroles can be created, permissions can be assigned to those roles, and those\nroles can be applied to users. Roles are scoped to apply only with a specific\ncontext like a site, an organization, or globally. See the \n[Roles and Permissions](/docs/7-1/user/-/knowledge_base/u/roles-and-permissions) documentation for more information.\n\n| Note: @product@ relies on the application server for sanitizing CRLF in\n| HTTP headers. You should, therefore, make sure this is configured properly in\n| your application server, or you may experience false positives in reports from\n| automatic security verification software such as Veracode. There is one\n| exception to this for Resin, which does not have support for this feature. In\n| this case, @product@ sanitizes HTTP headers.\n\nThere are additional security plugins available from \n[Liferay Marketplace](https://www.liferay.com/marketplace). \nFor example, you can find an Audit plugin for tracking user actions or an\nAntiSamy plugin for clearing HTML from XSS vectors.\n\nThere are many ways to fine-tune or disable various security features. Here\nare a few examples of these kinds of configuration actions:\n\n- Disable the Sign In portlet's *Create Account* link\n- Configure @product@'s HTTPS web server address\n- Configure the list of allowed servers to which users can be redirected\n- Configure the list of portlets that can be accessed from any page\n- Configure the file types allowed to be uploaded and downloaded\n\n## Secure Configuration and Run Recommendations\n\n@product@ is built using the \"secure by default\" concept in mind. It's not\nrecommended to disable built-in protections or to allow all values in security\nwhite-lists. Such acts may lead to security misconfiguration and an insecure\ndeployment.\n\nAlso, customers are advised to deploy security patches as described on the\n[customer portal](https://www.liferay.com/group/customer/products/portal/security-vulnerability).\n\nFor community and CE deployments, the stay secure by always using the latest\ncommunity version, which contains all previous security patches. \n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/02-logging-in-to-liferay.markdown",
    "content": "---\nheader-id: logging-into-liferay\n---\n\n# Logging into @product@\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/authentication-basics.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nOne of the primary functions of a security system is to make pages, content, and\nweb applications accessible only to the appropriate users. A student\nlogging into a university portal should not be able to access the same resources\na professor can. A patient logging into a health care portal should not be able\nto access a doctor's resources. Some content (at least a login page) should be\navailable to everybody, including unauthenticated users (called *guest* users).\nTo learn more about how @product@ restricts access to portal resources to\ndifferent users, please see the \n[Roles and Permissions](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions) \ndocumentation. \n\n## Authentication Types\n\nThere are three authentication types: by email address, screen\nname, or user ID. To choose an authentication type, navigate to the Control\nPanel, click on *Configuration* &rarr; *Instance Settings* &rarr; *Platform*\n&rarr; *User Authentication* and use the *How do users authenticate?* selector\nto make a selection. Alternatively, add the following lines to your\n`portal-ext.properties` file, uncomment the appropriate line, comment out the\nothers, and restart your server.\n\n```properties\ncompany.security.auth.type=emailAddress\n#company.security.auth.type=screenName\n#company.security.auth.type=userId\n```\n\nThe default authentication type is by email address, but you can choose screen\nnames or user IDs instead. Users choose screen names when they create their\naccounts or administrators can choose them. User IDs are auto-generated when the\naccount is created. Regardless of which authentication type is configured, users\nmust always enter a password. For information on\nadding restrictions on the kinds of passwords that are allowed or required\n(e.g., to require a minimum password length or require special characters),\nplease see the [Password Policies](/docs/7-2/user/-/knowledge_base/u/password-policies) \ndocumentation. \n\n## The Sign In Portlet\nSign In portlet is how users log in. By default, the Sign In portlet can\ncreate new accounts or request a password reset. The default home page contains\na Sign In portlet. You can access this page at\n[http://localhost:8080/web/guest/home](http://localhost:8080/web/guest/home).\n\n![Figure 1: By default, the Sign In portlet allows users to log in, create a new account, or request a password reset.](../../images/sign-in-portlet.png)\n\nIf the Sign In portlet doesn't appear on any page, you can still access it here:\n\n[localhost:8080/c/portal/login](localhost:8080/c/portal/login)\n\nBy default, guest users can create accounts by clicking on the *Create Account*\nlink in the Sign In portlet, completing the form, and submitting it. If a user\nhas an account but has forgotten its password, the user can click the *Forgot\nPassword* link to request a password reset. \n\nBoth the *Create Account*\nform and the *Forgot Password* form include a CAPTCHA-based text verification\nfield. Using [CAPTCHA](http://www.captcha.net) prevents bots from submitting\nthese forms. \n\nYou can use\n[reCAPTCHA](https://www.google.com/recaptcha/intro/index.html) instead of\nCAPTCHA. One advantage of reCAPTCHA is that it can allow visually impaired\nusers to pass the test. To use reCAPTCHA, navigate to the Control Panel, then\nclick on *Configuration* &rarr; *System Settings* &rarr; *CAPTCHA*.\n\nYou can prevent guest users from creating new user accounts, if your site\nrequires users be registered by administrators. Navigate to the Control Panel\n&rarr; *Configuration* &rarr; *Instance Settings* &rarr;\n*Platform* &rarr; *User Authentication* and uncheck the *Allow strangers to\ncreate accounts?* box. You can also prevent users from requesting forgotten\npasswords or from requesting password reset links by unchecking the appropriate\nboxes. With these options, the Create Account and Forgot Password links no\nlonger appear in the Sign-In portlet. \n\nRemember that the Sign In portlet is the default way for users to log in, but\nit's not the only way. User accounts can be imported from and exported to LDAP\ndirectories. You can use single-sign-on (SSO) solutions or token-based\nauthentication, which allows remote web applications to authenticate. Please\nrefer to the other articles in this section for more information. Finally,\nremember that user authentication and remote application authentication\nmechanisms are\n[extensible](/docs/7-2/frameworks/-/knowledge_base/f/authentication-pipelines). \n\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/03-service-access-policies.markdown",
    "content": "---\nheader-id: service-access-policies\n---\n\n# Service Access Policies\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/securing-web-services/setting-service-access-policies.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\n*Service access policies* comprise a layer of web service security that defines\nservices or service methods that can be invoked remotely. You can apply many of\nthem at once to produce a combined effect. To help you understand how service\naccess policies fit into the big picture, here's a summary of @product@'s web\nservice security layers:\n\n**IP permission layer:** The IP address from which a web service invocation\nrequest originates must be white-listed in the portal properties file. A web\nservice invocation coming from a non-whitelisted IP address automatically fails.\n\n**Service access policy layer:** Methods corresponding to a web service\ninvocation request must be whitelisted by each service access policy that's in\neffect. You can use wildcards to reduce the number of service classes and\nmethods that must be explicitly whitelisted.\n\n**Authentication/verification layer (browser-only):** If a web service invocation\nrequest comes from a browser, the request must include an authentication token.\nThis authentication token is the value of the `p_auth` URL parameter. The token\nis generated by @product@ and associated with your browser session. The `p_auth`\nparameter is automatically supplied when you invoke a @product@ web service via\nthe JSON web services API page or via JavaScript using `Liferay.Service(...)`.\nIf @product@ cannot associate the caller's authentication token with a User, the\nweb service invocation request fails.\n\n**User permission layer:** Properly implemented web services have permission\nchecks. The user invoking a web service must have permission to invoke the\nservice.\n\n![Figure 1: To get to a service, a request must pass through the door lock of user permissions, the padlock of the verification layer, the brick wall of service access policies, and finally the safe of predefined IP permissions.](../../images/service-access-policies-security-layers.png)\n\nNote that service access policies respect the permissions system. If a service\naccess policy grants a user access to a remote service, the user must still have\nthe appropriate permissions to invoke that service.\n\nService access policies are especially useful when remote applications such as\nmobile devices or Liferay Sync instances must access web services.\nAdministrators can use service access policies to ensure that these devices can\nonly invoke remote services from approved lists that can be modified at runtime.\n\n## Managing Service Access Policies\n\nNavigate to the Control Panel and click on *Service Access Policy* under the\nConfiguration heading. Here, you can see the default service access policies and\nadd new ones. When creating or editing service access policies, keep these\npoints in mind:\n\n- Service access policy names must be unique per portal instance.\n- Service access policy names can include only these allowed characters:\n\n        0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#:@-./_\n\n- Service access policy titles can be localized; service access policy names\n  cannot be localized.\n- Allowed service signatures must be entered one per line. Wildcards (`*`) are\n  allowed for both class names and method names. The `#` symbol must be used to\n  separate a class name from a method name. \n\nFor example, `com.liferay.portal.kernel.service.UserService` allows any method\nfrom the `UserService` class to be invoked.\n`com.liferay.document.library.kernel.service.DLAppService#get*` allows any\nmethod from the `DLAppService` that starts with `get` to be invoked. Thus,\n\n```java\ncom.liferay.portal.kernel.service.UserService\ncom.liferay.document.library.kernel.service.DLAppService#get*\n```\n\nallows any method from the `UserService` class to be invoked and any method\nfrom the `DLAppService` whose name starts with `get` to be invoked.\n\nThere are 16 service access policies that are enabled by default. Six of these\nhave to do with the system: \n\n**ASSET_ENTRY_DEFAULT:** Allows the view counter for assets to be updated when an\nasset is retrieved.\n\n**CALENDAR_DEFAULT:** Makes it possible to search public events\nin the calendar.\n\n**SYNC_DEFAULT:** Allows only the\n`com.liferay.sync.service.SyncDLObjectService.getSyncContext` method. It applies\nto every Liferay Sync request, including unauthenticated Sync requests.\n\n**SYNC_TOKEN:** Allows `com.liferay.sync.service.*`, meaning that any API\nfunction that's a method of a class in this package can be invoked. It applies\nto Sync requests which are accompanied by an authentication token.\n\n**SYSTEM_DEFAULT:** Allows access to country/region services by JavaScript\ncalls, so users can switch languages on the fly. Applies to every request,\nincluding unauthenticated requests.\n\n**SYSTEM_USER_PASSWORD:** Allows any method to be invoked. Of course, since API\nfunctions include permission checks, this call works only if the user has the\nrequired permission. It applies to requests for which\n`AuthVerifierResult.isPasswordBasedAuthentication` is `true`: i.e., whenever\nuser authentication took place using a password. If you want to completely\ndisallow certain API functions from being invoked, you can change the\n`SYSTEM_USER_PASSWORD` policy to something more restrictive than `*`.\n\n`SYNC_DEFAULT` and `SYSTEM_DEFAULT`, as their names suggest, are default\nservice access policies. Default service access policies are applied to all\nincoming requests, including unauthenticated requests. \n\nThe other 10 policies have to do with OAuth and JSON web services: \n\n**OAUTH2_analytics.read/write:** Integrates with \n[Liferay Analytics Cloud](https://www.liferay.com/products/analytics-cloud), \nallowing it access to JSON web services. \n\n**OAUTH2_everything/read/documents/userprofile/write:** The Everything policies\ngrant access to all the JSON web services for various reasons. Everything is\neverything: all JSON web services (matches `*`). The others match method\nsignatures appropriate to their description. For example, OAUTH2_everything.read\nmatches all methods starting with `fetch`, `get`, `has`, `is`, or `search`. \n\n**OAUTH_READ/WRITE:** These provide access to JSON web services via the OAuth\n1.0a plugin. \n\nThe default configuration makes available corresponding scopes that provide\naccess to all web services shipped with the system. The scopes must be assigned\nto OAuth 1 or 2 applications before they become usable. Administrators should\nreview the ones you want to use and disable the others. \n\nYou can create new default service access policies: \n\n1.  Navigate to the *Configuration* &rarr; *Service Access Policy* section of\n    the Control Panel. \n \n2.  Click *Add* (![add](../../images/icon-add.png)). \n \n3.  Give your policy a name. \n\n4.  Flip the switch to enable your policy.\n\n5.  If you want the policy applied to unauthenticated requests as well as\n    authenticated requests, flip the switch labeled *Default*. \n\n6.  Give your policy a localized title. \n\n7.  Under Allowed Service Signatures, start typing the fully qualified name of\n    a service class that's installed. Code completion helps you find the class.\n    For example, if you're creating a policy for Liferay's Knowledge Base\n    application, you could enter\n    `com.liferay.knowledge.base.service.KBArticleService`. \n\n8.  Under Method Name, start typing a service method call. Again, code\n    completion helps you. For Knowledge Base, you could enter `getKBArticle`. \n\n9.  To specify another service or method, click the plus icon to add another\n    entry. \n\n10. When done, click *Save*. \n\n| **Note:** If you know all the method signatures ahead of time, you can click\n| *Switch to Advanced Mode* and enter them all in one field on separate lines.\n\nLiferay applications can declare their own default policies (the `SYNC_DEFAULT`\npolicy is a good example). This policy can then be changed or disabled by\nadministrators. In this case, the plugin can still verify that the policy exists\nso there is no need to redefine or update it.\n\nBy default, Liferay's tunneling servlet uses the `SYSTEM_USER_PASSWORD` service\naccess policy. You can, however, create your own policy for the tunneling\nservlet and use the property `service.access.policy.name` for the\n`TunnelingServletAuthVerifier` to specify that your policy should be used\ninstead.\n\n## Service Access Policy Module \n\nLiferay's service access policy functionality is provided by the Service Access\nPolicy module. This module includes the following important classes:\n\n- `com.liferay.portal.kernel.security.service.access.policy.ServiceAccessPolicy`:\n  defines the public interface for `ServiceAccessPolicy`.\n- `com.liferay.portal.kernel.security.service.access.policy.ServiceAccessPolicyManager`: defines the public interface for retrieving instances of `ServiceAccessPolicy`.\n- `com.liferay.portal.kernel.security.service.access.policy.ServiceAccessPolicyManagerUtil`: bridges service access policy functionality to the parts of Liferay's core that have not yet been modularized.\n- `com.liferay.portal.kernel.security.service.access.policy.ServiceAccessPolicyThreadLocal`: makes `ServiceAccessPolicy` instances active.\n\nLiferay's Service Access Policy module resides in the\n`modules/apps/service-access-policy` folder in the source code. When running,\nthese three bundles provide the service access policy functionality (they're in\nthe `[Liferay Home]/osgi/modules` folder):\n\n- `com.liferay.service.access.policy.api.jar`\n- `com.liferay.service.access.policy.service.jar`\n- `com.liferay.service.access.policy.web.jar`\n\nThese modules provide the service access policy management UI that's accessible\nfrom the Control Panel. They also provide the interface and default\nimplementation for `ServiceAccessPolicy`.\n\nTo configure the Service Access Policy module, navigate to the Control Panel,\nclick on *System Settings*, and find the *Service Access Policies* module in the\nSecurity section. Click on its name to edit it. Here, you can edit the default\nservice access policy configuration. You can also force a default policy to be\napplied even when no policies are applied by the `AuthVerifier`.\n\nThere's also an `AuthenticatedAccessControlPolicy`. This policy doesn't do\nanything if a `ServiceAccessPolicyManager` implementation is present. If the\nservice access policy module is disabled, however, the\n`AuthenticatedAccessControlPolicy` provides a fallback that still requires\nauthenticated access for web services.\n\n## Summary\n\nGreat! Now you know service access policies can restrict access to @product@'s web\nservices. Custom service access policies can be created by portal\nadministrators. They are applied by the portal's token authenticator, e.g., by\nOAuth.\n\n## Related Topics\n\n[Creating Service Access Policies](/docs/7-2/frameworks/-/knowledge_base/f/service-access-policies) \n\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/04-auth-verifiers.markdown",
    "content": "---\nheader-id: authentication-verifiers\n---\n\n# Authentication Verifiers\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/securing-web-services/using-authentication-verifiers.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nThe Authentication Verification Layer is a centralized and extensible way to\nauthenticate remote invocations of @product@'s API.\n\nThe main responsibilities of the authentication verification layer are to\n\n1. Verify provided credentials using registered `AuthVerifier` instances\n\n2. Create portal authorization contexts based on verification results\n\nIf no available `AuthVerifier` can verify request credentials, an authorization\ncontext supporting non-authenticated access is created for a guest user. This\nallows each API to expose only a single API endpoint. In contrast, legacy (prior\nto 6.2) versions of @product@ exposed two API endpoints for each API: the\n`/api/endpoint` URI was for non-authenticated access and the URI\n`/api/secure/endpoint` was for authenticated access.\n\nThere are built-in `AuthVerifier` implementations for the most common\nsituations, such as when remote clients use HTTP Basic or HTTP Digest\nauthentication, send credentials in request parameters, send authenticated\n`JSESSIONID`s, or use shared secrets to establish trust. Other `AuthVerifier`\nimplementations can be deployed as modules containing implementations of the\n`AuthVerifier` interface that are registered as services in the OSGi runtime.\n\nNote: The authentication verification layer's focus is on verifying\nauthentication, not on providing credentials. It does NOT issue tokens,\ncredentials, or display Sign In portlets. Instead, the layer verifies existing\ncredentials and authenticated sessions and is therefore a complement to\nauthentication endpoints. To ensure backwards compatibility, however, the\ndefault implementations support requests providing user name and password\ncredentials. Thus, the authentication verification layer stands on the border\nbetween authentication and authorization.\n\n## Authentication Verification Process Overview\n\nThis layer and surrounding processes are provided by the `AuthVerifierFilter`\nclass that implements the `javax.servlet.Filter` interface.\n\n**Step 1: Verify Request Credentials**\n\nThe layer uses the chain of responsibility design pattern to support both\nbuilt-in and third party `AuthVerifier` implementations. Each `AuthVerifier`\ncan provide configurations where it specifies mapped URLs and other properties.\n\nEach incoming request is matched against all registered `AuthVerifier`s to\nselect the final list of `AuthVerifier`s that is used to process the request.\nIt's the responsibility of each `AuthVerifier` to verify the incoming request\ncredentials.\n\n**Step 2: Create an Authorization Context**\n\nWhen a request is processed by all matching `AuthVerifier`s, @product@ creates an\nauthorization context for the resolved user.\n\nThis encompasses setting the `HttpServletRequest` `remoteUser` to return the\nresolved user ID setting `ThreadLocal`s to the resolved user.\n\nThe resolved user can be the user returned by one of the `AuthVerifier`\ninstances or a guest user if no instance was able to verify the provided\ncredentials. \n`AuthVerifier`s are created by developers, and are processed automatically as\nlong as they're registered in the OSGi runtime. Each Auth Verifier gets its own\nconfiguration in *Control Panel* &rarr; *System Settings* &rarr; *Security*\n&rarr; *API Authentication*. Configuration for Auth Verifiers that ship with \nthe product include\n\n- Basic Auth Header \n- Digest Authentication \n- HTTP Tunnel Extender \n- Image Request\n- Portal Sessions \n- Request Parameter\n- Tunnel Auth \n\nThe following Auth Verifiers are enabled by default and can be used to access\nremote API out-of-the-box:\n\n- Basic Auth Header\n- Portal Sessions \n\n## Basic Auth Header\n\nThis Auth Verifier allows the remote client to authenticate using \n[HTTP Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication). \nConfigure it by providing URL paths that should be authenticated this way. \nWhen Force Basic Authentication field is checked then HTTP Basic Authentication is \nrequired.\n\nThe default URLs are `/api/*,/xmlrpc*` for web services. The mapping excludes \n`/api/liferay*` to prevent accessing `TunnelServlet`. For more information please \nsee  Tunnel Authentication Verifiers.\n\n## Digest Auth Header\n\nThis Auth Verifier allows the remote client to authenticate using \n[HTTP Digest Authentication](https://en.wikipedia.org/wiki/Digest_access_authentication). \nConfigure it by providing URL paths that should be authenticated this way. \nWhen Force Digest Authentication field is checked then HTTP Basic Authentication is \nrequired.\n\nThis Auth Verifier is not enabled by default.\n\n## HTTP Tunnel Extender\n\nAs Liferay embraced modularity, this extender was written to enable modules to\nbe part of `TunnelServlet`. It maps `TunnelServlet` and\n`TunnelingServletAuthVerifier` to the module servlet context. Modules with\n`Http-Tunnel` in the manifest can make use of the Tunnel Servlet, and can expose\nthe API via `/o/_module_/api/liferay/do`. \n\nConfigure it by setting client IP addresses allowed to tunnel. For more\ninformation, please see \n[the properties documentation](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#HTTP%20Tunneling)\nas well as \n[remote staging](/docs/7-2/user/-/knowledge_base/u/enabling-remote-live-staging).\n\nNote that this is not a recommended way to export remote APIs; it's far\nbetter to expose remote services using JAX-RS or Liferay JSON Web Service \ntechnologies.\n\n## Image Request Authentication Verifier\n\nWhen connected to LibreOffice/OpenOffice, the Office process must download\nimages from @product@ to render docs with images. To do this, a \n[JWT Token](https://jwt.io) is created to access the images securely. \n\nConfigure this by setting the Hosts Allowed, URLs included, and URLs excluded if\nnecessary. \n\nThis Auth Verifier is not enabled by default.\n\n## Portal Sessions Auth Verifiers\n\nEnables JavaScript in a browser to access Liferay JSON Web Services using an\nexisting portal session.\n\nIn the default configuration, the URLs included field shields access to the\nlegacy JSON remote services layer:\n`/api/json*,/api/jsonws*,/c/portal/json_service*`.\n\n## Request Parameter Auth Verifiers\n\nFor backwards compatibility with `RequestParameterAutoLogin` you can\nauthenticate and access portal endpoints with credentials inside HTTP request\nparameters `parameterAutoLoginLogin` and `parameterAutoLoginPassword`.\n\nThis Auth Verifier is not enabled by default.\n\n## Tunnel Authentication Verifiers\n\n`TunnelServlet` is a legacy remote API endpoint mapped at `/api/liferay/do` to\nprovide access to the portal remote services. The Tunnel Auth Verifier allows\ntrusted remote clients authenticated access using any user ID provided, on\nbehalf of the user.\n\nAn example of a trusted remote client is the Staging remote publishing feature.\n\nTrusted remote clients authenticate using a shared secret stored in the portal\nproperty `tunneling.servlet.shared.secret`. The default value is empty and\nforbids all access. \n\nEven though the default configuration is enabled by default, access is\nlimited to localhost only. Configure it by setting client IP addresses allowed \nto tunnel. For more information, please see \n[the properties documentation](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#HTTP%20Tunneling)\nas well as \n[remote staging](/docs/7-2/user/-/knowledge_base/u/enabling-remote-live-staging).\n\n## Related Topics\n\n[Service Access Policies](/docs/7-2/deploy/-/knowledge_base/d/service-access-policies)\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/05-ldap/01-ldap-servers-intro.markdown",
    "content": "---\nheader-id: ldap\n---\n\n# LDAP\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/users-and-permissions/connecting-to-a-user-directory/connecting-to-an-ldap-directory.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nLDAP is a common user store for @product@. You can configure LDAP at the\nsystem scope in System Settings or at the instance scope in Instance settings.\nUsers can be imported from LDAP or exported to LDAP. To access LDAP\nconfiguration settings, navigate to *Control Panel &rarr; Configuration* &rarr;\n*Instance Settings*. At the bottom of the list on the left, click *Servers*. \n\nClick the *Add* button to add an LDAP server connection. If you have more than\none LDAP server, you can arrange the servers by order of preference using the\nup/down arrows. Regardless of how many LDAP servers you add, each server has the\nsame configuration options.\n\n**Server Name:** Enter a name for your LDAP server.\n\n**Default Values:** Several common directory servers appear here. If you\nuse one of these, select it. The rest of the form is populated with default\nvalues for that directory.\n\n## Connection\n\nThese settings cover the connection to LDAP.\n\n**Base Provider URL:** The link to the LDAP server. Make sure the @product@ server\ncan communicate with the LDAP server. If there is a firewall between the two\nsystems, make sure the appropriate ports are opened.\n\n**Base DN:** The Base Distinguished Name for your LDAP directory. It is\nusually modeled after your organization. It may look similar to this:\n`dc=companynamehere,dc=com`.\n\n**Principal:** The default LDAP administrator user ID is populated here. If your\nadministrator ID differs, use that credential instead. You need an\nadministrative credential because @product@ uses this ID to synchronize\nuser accounts to and from LDAP.\n\n**Credentials:** This is the password for the LDAP administrative user.\n\nThis is all you need to make a regular connection to an LDAP directory. The rest\nof the configuration, however, may need to be customized, as it represents \"best\nguesses\" as to correct defaults. The default attribute mappings usually provide\nenough data to synchronize back to the @product@ database when a user attempts\nto log in. To test the connection to your LDAP server, click the *Test LDAP\nConnection* button.\n\n## Checkpoint\n\nBefore proceeding to fine tune @product@'s LDAP connections, ensure the\nfollowing steps have been taken:\n\n1.  The LDAP connection is enabled. Depending on your needs, LDAP authentication\n    may be required so that only users who have been bound may log in.\n\n2.  *Export/Import*: for users in a clustered environment, Enable Import/Export\n    on Startup should be disabled so that there are no massive imports on every\n    node upon start up. \n\n3.  When adding the LDAP server, the *Server Name*, *Default Values*,\n    *Connection* values are correct. It is always a good idea to click the *Test\n    LDAP Connection* before saving.\n\n## Instance Settings vs. System Settings\n\nYou can define an LDAP server connection at the System Settings scope as well.\nBecause this user interface is auto-generated, it's not as helpful as the one in\nInstance Settings. For this reason, you should define and troubleshoot your\nsettings in Instance Settings first. If you decide you want your LDAP connection\nat the system scope, you can copy your configuration from Instance Settings and\nthen delete the server from Instance Settings. \n\nOf course, you can also configure LDAP servers at the system scope using OSGi\n`.config` files. The easiest way to do this is to use the GUI and export the\nconfiguration. Then you can use the resulting `.config` file anywhere you need\nit (such as other nodes in a cluster). \n\n**Note:** To use `config` files for LDAP server configuration, you must\nspecify the Virtual Instance ID (in the source, the variable name is\n`companyId`) in the exported configuration file, because servers are defined\nat the instance scope, not the system scope. To do this, specify the virtual\ninstance ID somewhere in the file like this: \n\n```properties\ncompanyId=1234\n```\nYou can find your Virtual Instance ID in Control Panel &rarr; Configuration\n&rarr; Virtual Instances.\n\n## Security\n\nIf you run your LDAP directory in SSL mode to encrypt credential information on\nthe network, you must perform extra steps to share the encryption key and\ncertificate between the two systems.\n\nFor example, if your LDAP directory is Microsoft Active Directory on\nWindows Server 2003, you'd share the certificate like this:\n\nClick *Start* &rarr; *Administrative Tools* &rarr; *Certificate Authority*.\nHighlight the machine that is the certificate authority, right-click on it, and\nclick *Properties*. From the General menu, click *View Certificate*. Select the\nDetails view, and click *Copy To File*. Use the resulting wizard to save the\ncertificate as a file. You must also import the certificate into the *cacerts\nkeystore* like this: \n\n    keytool -import -trustcacerts -keystore /some/path/java-8-jdk/jre/lib/security/cacerts -storepass changeit -noprompt -alias MyRootCA -file /some/path/MyRootCA.cer\n\nThe `keytool` utility ships as part of the Java SDK.\n\nOnce this is done, go back to the LDAP page in the Control Panel. Modify the\nLDAP URL in the Base DN field to the secure version by changing the protocol to\n`ldaps` and the port to `636` like this:\n\n    ldaps://myLdapServerHostname:636\n\nSave the changes. Communication to LDAP is now encrypted. \n\n## Configuring LDAP Import/Export\n\nThe other settings configure mappings between LDAP and @product@ so users and\ngroups can be imported. \n\n### Users\n\nThis section contains settings for finding users in your LDAP directory.\n\n**Authentication Search Filter:** Use this search filter box to determine\nthe search criteria for user logins. By default, @product@ uses users' email\naddresses for their login names. The value here must use the authentication\nmethod you use. For example, if you changed @product@'s authentication method to\nuse screen names instead of the email addresses, you would modify the search\nfilter so it can match the entered log in name:\n\n    (cn=@screen_name@)\n\n**Import Search Filter:** Depending on the LDAP schema, there are different\nways to identify the user. The default setting is usually fine:\n\n    (objectClass=inetOrgPerson)\n\nIf you want to search for only a subset of users or users that have\ndifferent LDAP object classes, you can change this.\n\n**User Mapping:** Next, you can define mappings from LDAP attributes to Liferay\nfields. Though LDAP user attributes may be different from LDAP server to LDAP\nserver, there are five fields @product@ requires to be mapped for the user\nto be recognized:\n\n-   *Screen Name* (e.g., `uid` or `cn`)\n-   *Password* (e.g., `userPassword`)\n-   *Email Address* (e.g., `mail` or `email`)\n-   *First Name* (e.g., `name` or `givenName`)\n-   *Last Name* (e.g., `sn`)\n\n**Note:** If you intend to create or import users with no email addresses, \nyou must set `users.email.address.required=false` in `portal-ext.properties`.\nWith this set, Liferay auto-generates an email address combining the user ID\nplus the suffix defined in the property `users.email.address.auto.suffix=`.\nFinally, make sure to set Liferay and LDAP authentication to something other\nthan email address.\n\nIf you want to import LDAP groups as @product@ user groups, make sure define\na mapping for the @product@ group field so that membership information is\npreserved:\n\n  -   *Group* (e.g., *member*)\n\nThe other LDAP user mapping fields are optional.\n\nThe Control Panel provides default mappings for commonly used LDAP attributes.\nYou can also add your own mappings.\n\n**Test LDAP Users:** Once you have your attribute mappings set up (see above),\nclick the *Test LDAP Users* button and @product@ attempts to pull LDAP users and\nmatch them with their mappings as a preview.\n\n![Figure 1: You should see a list of users when you click the *Test LDAP Users* button.](../../../images/testing-ldap-users.png)\n\n### Groups\n\nThis section contains settings for mapping LDAP groups to @product@ user groups.\n\n**Import Search Filter:** This is the filter for mapping LDAP groups to\n@product@ user groups. For example, \n\n    (objectClass=groupOfNames)\n\nEnter the LDAP group attributes you want retrieved for this mapping. The\nfollowing attributes can be mapped. The *Group Name* and *User* fields are\nrequired, the *Description* is optional.\n\n  - *Group Name* (e.g., `cn` or `o`)\n\n  - *Description* (e.g., `description`)\n\n  - *User* (e.g., `member`)\n\n**Test LDAP Groups:** Click the *Test LDAP Groups* button to display a list of\nthe groups returned by your search filter.\n\n### Export\n\nThis section contains settings for exporting user data from LDAP.\n\n**Users DN:** Enter the location in your LDAP tree where the users are stored.\n@product@ exports the users to this location.\n\n**User Default Object Classes:** Users are exported with the listed default\nobject classes. To find out what your default object classes are, use an LDAP\nbrowser tool such as Apache Directory Studio to locate a user and view the\nObject Class attributes stored in LDAP for that user.\n\n**Groups DN:** Enter the location in your LDAP tree where the groups are stored.\nWhen @product@ does an export, it exports the groups to this location.\n\n**Group Default Object Classes:** When a group is exported, the group is created\nwith the listed default object classes. To find out what your default object\nclasses are, use an LDAP browser tool such as [Apache Directory Studio](https://directory.apache.org/studio) to locate\na group and view the Object Class attributes stored in LDAP for that group.\n\nWhen you've set all your options and tested your connection, click *Save*. \n\n| **Note:** If a user changes a value like a password in @product@, that change is\n| passed to the LDAP server, provided @product@ has enough schema access to make\n| the change.\n\nNow you know how to connect an LDAP server to @product@ and how to\nconfigure user import behavior, export behavior, and other LDAP settings.\n\n## Related Topics\n\n[@product@ Security Overview](/docs/7-0/deploy/-/knowledge_base/d/liferay-portal-security-overview)\n[Logging into @product@](/docs/7-0/deploy/-/knowledge_base/d/logging-in-to-liferay)\n\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/05-ldap/02-configuring-ldap.markdown",
    "content": "---\nheader-id: configuring-ldap\n---\n\n# Configuring LDAP\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/users-and-permissions/connecting-to-a-user-directory/configuring-user-import-and-export.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to configure import settings, export settings,\nand related LDAP configuration settings. \n\nTo access LDAP configuration settings, navigate to *Control Panel &rarr;\nConfiguration* &rarr; *Instance Settings* &rarr; *Security* &rarr; *LDAP*. There\nare four categories on the left: Export, General, Import, and Servers. The\nServers category was covered in [the last article](/docs/7-2/deploy/-/knowledge_base/d/ldap). \nThe rest are covered below. \n\n## Export\n\n**Enable Export:** Check this box to export user accounts to LDAP. A listener\ntracks changes made to the `User` object and pushes updates to the LDAP server\nwhenever a `User` object is modified. Note that by default on every login,\nfields such as `lastLoginDate` are updated. When export is enabled, this causes\na user export every time the user logs in. You can prevent updates to users'\n`lastLoginDate` fields from triggering LDAP user exports by setting the\nfollowing property in your `portal-ext.properties` file:\n\n    users.update.last.login=false\n\n**Enable Group Export:** Export groups to LDAP. \n\n## General\n\n**Enabled:** Check this box to enable LDAP Authentication.\n\n**Required:** Check this box if LDAP authentication is required. Users can't log\nin unless they can bind to the LDAP directory successfully. Uncheck this box if\nusers with @product@ accounts but no LDAP accounts can log in.\n\n**Use LDAP Password Policy:** @product@ uses its own password policy by default.\nThis can be configured on the Control Panel's Password Policies page. Check the\n*Use LDAP Password Policy* box if you want to use the password policies defined\nby your LDAP directory. Once this is enabled, the Password Policies tab states\nthat you are not using a local password policy. You must now use your LDAP\ndirectory's mechanism for setting password policies. @product@ cannot enforce\nthese policies; the best it can do is pass through the messages returned by your\nLDAP server. It does this by parsing the messages in the LDAP controls the\nserver returns. By default, @product@ is configured to parse the messages\nreturned by the Fedora Directory Server. If you use a different LDAP server, you\nmust customize the messages in *System Settings* &rarr; *Security* &rarr; *LDAP*\n&rarr; *Connection*. \n\n**Method:** Choose *Bind* (the default) or *Password Compare*. Bind does\na standard LDAP bind; Password Compare attempts to compare Liferay and LDAP\npasswords using the encryption algorithm specified in the field below. Password\nCompare is rarely used. \n\n**Password Encryption Algorithm:** Choose the password encryption algorithm your\nLDAP server uses to encrypt passwords so they can be compared if using the\nPassword Compare bind method. This is rarely used. \n\n## Import\n\nYou can import user data from LDAP directories using the following options:\n\n**Enable Import:** Check this box to do a mass import from your LDAP\ndirectories. Otherwise, Users are imported as they log in. \n\n![Figure 1: Ziltoid and Rex have been imported because they logged in.](../../../images/imported-ldap-users.png)\n\n**Enable Import on Startup:** Check this box to do the mass import when\n@product@ starts. Note: this box only appears if you check **Enable Import**,\ndescribed above. Definitely leave this unchecked if you have a @product@\ncluster, or all your nodes will do a mass import when each of them starts up.\n\n**Import Interval:** When mass importing users, import users every X minutes. \n\n**Import Method:** Set either User or Group. If you set this to User, @product@\nimports all users from the location specified in the server connection. If you\nset this to Group, @product@ searches all the groups and imports the users in\neach group. If you have users who do not belong to any groups, they are not\nimported. \n\n**Lock Expiration Time:** Set the account lock expiration time for LDAP User\nimport. The default is one day. \n\n**Import User Sync Strategy:** Set the strategy used to sync user accounts.\nOptions are Auth Type (i.e., the way the user authenticates, like with screen\nname) and UUID (requires a UUID attribute in LDAP). \n\n**Enable User Password on Import:** Assign a default password (see below) when\nusers are imported, so they can be synced between the two systems. \n\n**Autogenerate User Password on Import:** Create a random password on user\nimport. \n\n**Default User Password:** Enter the default password users are assigned when\nthey first log in via LDAP. \n\n**Enable Group Cache on Import:** Cache the imported groups so import isn't\nslowed by database access. \n\n**Create Role per Group on Import:** For every LDAP group, create\na corresponding Liferay Role. \n\n## Servers\n\n**LDAP Servers:** @product@ supports connections to multiple LDAP servers. Use\nthe *Add* button to add LDAP servers. Each LDAP server has\nseveral configuration options [explained here](/docs/7-2/deploy/-/knowledge_base/d/ldap). \n\nOnce you've finished configuring LDAP, click the *Save* button. \n\n### LDAP Options Available in System Settings\n\nAlthough most LDAP configuration can be done from Instance Settings, there are\nseveral configuration parameters that are only available in System Settings.\nThere are also settings duplicated from the ones in Instance Settings. These\nchange the *default* settings for new virtual instances (see note below). \n\n| **Note**: When you make a change in System Settings, it takes effect for the\n| virtual instance you're in. If after changing a setting you create a new\n| virtual instance, that virtual instance inherits the settings of the one it was\n| created from as defaults. For example, say you have virtual instances named A,\n| B, and C. From A, you modify *Error password history keywords*. This change\n| appears only in A, not in B or C. Then from A, you create virtual instance D.\n| The change to *Error password history keywords* appears in D (not B or C),\n| since D defaults to A's settings because you created it from A.\n\nIf you must change any of these options, navigate to *Control Panel* &rarr;\n*Configuration* &rarr; *System Settings*. Go to the *Security* section and\nfind the entries with LDAP in the title. The only new settings here are in the\n*Connection* entry.\n\nUse the *Connection* entry to manage error properties like *Error password age\nkeywords* which lets you set a list of phrases from error messages which can\npossibly be returned by the LDAP server. When a user binds to LDAP, the server\nreturns *controls* with its response of success or failure. These controls\ncontain a message describing the error or the information that is returned with\nthe response. Though the controls are the same across LDAP servers, the messages\ncan be different. The properties described here contain snippets of words from\nthose messages and work with Red Hat's Fedora Directory Server. If you are not\nusing that server, the word snippets may not work with your LDAP server. If they\ndon't, you can replace the values of these properties with phrases from your\nserver's error messages. This enables @product@ to recognize them.\n\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/06-token-based-authentication.markdown",
    "content": "---\nheader-id: token-based-single-sign-on-authentication\n---\n\n# Token-based Single Sign On Authentication\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/configuring-sso/token-based-authentication.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nToken-based SSO authentication was introduced in @product@ 7.0 to standardize\nsupport for Shibboleth, SiteMinder, Oracle OAM, or any other SSO product that\nworks by propagating a token via one of the following mechanisms:\n\n- HTTP request parameter\n- HTTP request header\n- HTTP cookie\n- Session attribute\n\nSince these providers have a built-in web server module, you should use the\nToken SSO configuration. \n\nThe authentication token contains the @product@ user's screen name or email\naddress, whichever @product@ has been configured to use for the particular\ncompany (portal instance). Recall that @product@ supports three authentication\nmethods:\n\n- By email address\n- By screen name\n- By user ID\n\nToken-based authentication only supports email address and screen name. If\n@product@ is configured to use user ID when a token-based authentication is\nattempted, the `TokenAutoLogin` class logs this warning:\n\n    Incompatible setting for: company.security.auth.type\n\nPlease note that the above sources are fully trusted. \n\nFurthermore, you must use a security mechanism external to @product@, such as a\nfronting web server like Apache. The chosen fronting solution must prevent\nmalicious @product@ user impersonation that otherwise might be possible by\nsending HTTP requests directly to @product@ from the client's web browser.\n\nToken-based authentication is disabled by default. To manage token-\nbased SSO authentication, navigate to Control Panel &rarr;\n*System Settings*, &rarr; *Security* &rarr; *SSO*. Token Based SSO appears in\nVirtual Instance Scope at the bottom. Here are the configuration options for the\nToken Based SSO module:\n\n**Enabled:** Check this box to enable token-based SSO authentication.\n\n**Import from LDAP:** Check this box to import users automatically from LDAP if\nthey don't exist.\n\n**User token name:** Set equal to the name of the token. This is retrieved\nfrom the specified location. (Example: SM_USER)\n\n**Token location:** Set this to the location of the user token. As mentioned\nearlier, the options are:\n\n- HTTP request parameter\n- HTTP request header\n- HTTP cookie\n- Session attribute\n\n**Authentication cookies:** Set this to the cookie names that must be removed\nafter logout. (Example: `SMIDENTITY`, `SMSESSION`)\n\n**Logout redirect URL:** When user logs out of @product@, the user is\nredirected to this URL.\n\nRemember to click *Save* to activate Token Based SSO.\n\n## Required SiteMinder Configuration\n\nIf you use SiteMinder, note that @product@ sometimes uses the tilde character in\nits URLs. By default, SiteMinder treats the tilde character (and others) as bad\ncharacters and returns an HTTP 500 error if it processes a URL containing any of\nthem. To avoid this issue, change this default setting in the SiteMinder\nconfiguration to this one:\n\n\tBadUrlChars       //,./,/.,/*,*.,\\,%00-%1f,%7f-%ff,%25\n\nThe configuration above is the same as the default except the `~` was removed\nfrom the bad URL character list. Restart SiteMinder to make your configuration\nupdate take effect. For more information, please refer to SiteMinder's\n[documentation](https://support.ca.com/cadocs/0/CA%20SiteMinder%20r6%200%20SP6-ENU/Bookshelf_Files/HTML/index.htm?toc.htm?258201.html)\n\n## Summary\n\n@product@'s token-based SSO authentication mechanism is highly flexible\nand compatible with any SSO solution that provides it with a valid @product@\nuser's screen name or email address. These include Shibboleth and SiteMinder.\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/07-openid-connect-authentication.markdown",
    "content": "---\nheader-id: authenticating-with-openid-connect\n---\n\n# Authenticating with OpenID Connect\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/configuring-sso/using-openid-connect.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nOpenID Connect is a lightweight authentication layer built on top of the \n[OAuth 2.0](/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0) \nauthorization protocol. It compliments local accounts by enabling users to\nauthenticate using accounts they have on other systems. Users who avoid signing\nup for new accounts can then use an account they already have to sign into your\nwebsite. By using OpenID Connect, you *delegate* user authentication to other\nproviders, making it easy for users with existing accounts to authenticate to\nyour system. \n\n| **Note:** You can add multiple providers to your installation, but @product@\n| can't be an OpenID Connect provider.\n\nBecause OpenID Connect is built on OAuth 2.0, its token flow is similar. OAuth\n2.0 is only an authorization protocol, so it sends an *access token* that grants\naccess to particular APIs. OpenID Connect adds to this an *identity token* that\npasses user information like name and email, provided the user has authenticated\nand granted permission. \n\n## Creating a Client in OpenID Connect Provider\n\nTo use OpenID Connect, you must first register it as a client in your provider.\nThis is an OAuth 2.0 client. The process varies by provider: \n\n1.  Navigate to the provider's website and create a client. \n\n2.  During the creation process, you must supply an *authorized redirect URL*\n    that can process the tokens sent from the provider. @product@'s URL is\n\n        https://[server.domain]/c/portal/login/openidconnect\n\n3.  The provider sends several pieces of information. Some of these, like\n    the Discovery Endpoint, Authorization Endpoint, or Issuer URL are the same\n    regardless of the client. The two pieces of information unique to your\n    request are the `client_id` and the `client_secret`. \n\nCollect the information from the provider. You'll need it create the provider\nnext. \n\n## Configuring an OpenID Connect Provider Connection\n\nGo to *Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr;\n*Security* &rarr; *SSO* and select ***OpenID Connect Provider*** under the *System Scope* and follow these steps: \n\n1.  Add the provider by clicking the *Add* button. \n\n2.  Use the information you received from the provider to fill out the form: \n\n**Provider Name:** This name appears in the Sign-In Portlet when users use\nOpenID Connect to log in. \n\n**OpenID Client ID:** Provide the OAuth 2.0 Client ID you received from your\nprovider. \n\n**OpenID Connect Client Secret:** Provide the OAuth 2.0 Client Secret you\nreceived from your provider. \n\n**Scopes:** Leave the default, which requests the user name and the email. Your\nprovider may offer other scopes of user information. \n\n**Discovery Endpoint:** Other URLs may be obtained from this URL, and they vary\nby provider. \n\n**Discovery Endpoint Cache in Milliseconds:** Cache the endpoints (URLs)\ndiscovered for this amount of time. \n\n**Authorization Endpoint:** This URL points to the provider's URL for\nauthorizing the user (i.e., signing the user in). \n\n**Issuer URL:** The provider's URL that points to information about the provider\nwho is issuing the user information. \n\n**JWKS URI:** A URL that points to the provider's JSON Web Key Set that contains\nthe public keys that can verify the provider's tokens. \n\n**ID Token Signing Algorithms:** Set the supported ID token algorithms manually.\nNormally, this is \"discovered\" at the discovery endpoint. You can add as many of\nthese as you need. \n\n**Subject Types:** A Subject Identifier is a unique and never reassigned\nidentifier the provider uses to establish who the user is, and is consumed by\nthe client (i.e., @product@). There are two types: public (provides the same\nvalue to all clients) and private (provides a different value to each client). \n\n**Token Endpoint:** The provider's URL where tokens can be requested. \n\n**User Information Endpoint:** The OAuth 2.0 protected URL from which user\ninformation can be obtained. \n\nOnce you've filled out the form, click *Save*, and you're ready to enable OpenID\nConnect authentication. \n\n| System Settings configuration file:\n| \n|     com.liferay.portal.security.sso.openid.connect.internal.configuration.OpenIdConnectProviderConfiguration-[name].config\n| \n| where `[name]` is a descriptive, but unique name for example `provider1`.\n\n## Enabling OpenID Connect Authentication\n\n1. Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings*\n   &rarr; *Security* &rarr; *SSO* and select ***OpenID Connect*** under *Virtual Instance Scope*. \n\n2. Click the *Enabled* check box, and then click *Save*. \n\n**Note:** You can also enable OpenID Connect authentication for the given\nvirtual instance through the *Control Panel* &rarr; *Configuration* &rarr;\n*Instance Settings* &rarr; *OpenID Connect* tab.\n\n| System Settings configuration file:\n| \n|     com.liferay.portal.security.sso.openid.connect.configuration.OpenIdConnectConfiguration.config\n\nNow users can sign in with OpenID Connect. \n\n## Signing In With OpenID Connect\n\nThere's a new link in the Sign-In Portlet for signing in with OpenID Connect: \n\n1.  From the Sign-In Portlet, click the OpenID Connect link at the bottom. \n\n2.  Choose a provider and click *Sign In*. \n\n3.  This takes you to your provider's sign in page. Enter your credentials and\n    log in. \n\n4.  Upon successful authentication, you're redirected back to @product@ in an\n    authenticated state. \n\nOpenID is a standards-based, secure way to authenticate users from other\nsystems. \n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/08-openam-authentication.markdown",
    "content": "---\nheader-id: opensso-single-sign-on-authentication\n---\n\n# OpenAM Single Sign On Authentication\n\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/configuring-sso/using-openam.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nOpenAM is an open source single sign-on solution that comes from the code base\nof Sun's System Access Manager product. @product@ integrates with OpenAM,\nallowing you to use OpenAM to integrate @product@ into an infrastructure that\ncontains a multitude of different authentication schemes against different\nrepositories of identities.\n\nNote that OpenAM relies on cookie sharing between applications. Thus, in order\nfor OpenAM to work, **all applications that require SSO must be in the same web\ndomain**. You should  also add the following property if you have enabled\nHTTPOnly cookies due to the way some web containers (like Apache Tomcat™) parse\ncookies that contain special characters: \n\n```properties\ncom.iplanet.am.cookie.encode=true\n```\n\nYou can install OpenAM on the same or different server as @product@. Be sure to\nreview the context path and server hostname for your OpenAM server. \n\nIf you want to install OpenAM on the same server as @product@, you must deploy\nthe OpenAM `.war`, downloadable from \n[here](https://backstage.forgerock.com/downloads/browse/am/archive/productId:openam).\nOtherwise, follow the instructions at the \n[OpenAM 13 site](https://backstage.forgerock.com/docs/openam/13/install-guide/) to install\nOpenAM. \n\n| **Note**: OpenAM 12 and below work with @product@, but are at end of life.\n| Because of this, we recommend only OpenAM 13 for production use.\n\nOnce you have it installed, create the @product@\nadministrative user in it. Users are mapped back and forth by screen names. By\ndefault, the @product@ administrative user has a screen name of *test*, so if\nyou were to use that account, register the user in OpenAM with the ID of *test*\nand the email address specified in the [`admin.email.from.address`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Admin%20Portlet) [portal property](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)).\nOnce you have the user set up, log in to OpenAM using this user.\n\nIn the same browser window, log in to @product@ as the administrative user (using\nthe previous admin email address). Go to the Control Panel and click\n*Configuration* &rarr; *Instance Settings* &rarr; *Security* &rarr;\n*SSO*. Then choose *OpenSSO* in the list on the left.\n\n![Figure 1: OpenSSO Configuration.](../../images/opensso-configuration.png)\n\nModify the three URL fields (Login URL, Logout URL, and\nService URL) so they point to your OpenAM server (in other words, only modify the host\nname portion of the URLs), check the *Enabled* box, and click *Save*.\n@product@ then redirects users to OpenAM when they request the `/c/portal/login`\nURL *for example, when they click on the *Sign In* link).\n\n@product@'s OpenAM configuration can be applied at either the system scope or at\nthe instance scope. To configure the OpenAM SSO module at the system scope,\nnavigate to the Control Panel, click on *Configuration* &rarr; *System Settings*\n&rarr; *Security* &rarr; *SSO* &rarr; *OpenSSO*. Click on it and you'll find\nthese settings to configure. The values configured here provide the default\nvalues for all portal instances. Enter them in the same format as you would when\ninitializing a Java primitive type with a literal value.\n\nProperty Label | Property Key | Description | Type\n----- | ----- | ----- | -----\n**Version** | `version` | OpenAM version to use (12 and below or 13) | `String`\n**Enabled** | `enabled` | Check this box to enable OpenAM authentication. Note that OpenAM works only if LDAP authentication is also enabled and @product@'s authentication type is set to screen name. | `boolean`\n**Import from LDAP** | `importFromLDAP` | If this is checked, users authenticated from OpenAM that do not exist in @product@ are imported from LDAP. LDAP must be enabled. | `boolean`\n**Login URL** | `loginURL` | The URL to the login page of the OpenAM server | `String`\n**Logout URL** | `logoutURL` | The URL to the logout page of the OpenAM server | `String`\n**Service URL** | `serviceURL` | The URL by which OpenAM can be accessed to use the authenticated web services. If you are using OpenAM Express 8 or higher, you need to have the server running Java 6. | `String`\n**Screen Name Attribute** | `screenNameAttr` | The name of the attribute on the OpenAM representing the user's screen name | `String`\n**Email Address Attribute** | `emailAddressAttr` | The name of the attribute on the OpenAM representing the user's email address | `String`\n**First Name Attribute** | `firstNameAttr` | The name of the attribute on the OpenAM representing the user's first name | `String`\n**Last Name Attribute** | `lastNameAttr` | The name of the attribute on the OpenAM representing the user's last name | `String`\n\nTo override these default settings for a particular portal instance, navigate\nto the Control Panel and click *Configuration* &rarr; *Instance Settings* &rarr;\n*Security* &rarr; *SSO*. Then choose *OpenSSO* in the list on the left.\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/09-cas-authentication.markdown",
    "content": "---\nheader-id: cas-central-authentication-service-single-sign-on-authentication\n---\n\n# CAS (Central Authentication Service) Single Sign On Authentication\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/configuring-sso/authenticating-with-cas.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nCAS is an authentication system originally created at Yale University. It is a\nwidely used open source single sign-on solution and was the first SSO product to\nbe supported by @product@. @product@'s CAS module includes the CAS client, so\nthere's no need to install it separately.\n\n| **Note:** @product@ supports CAS 3.3.x. If you use a later version of CAS, it\n| is best to use CAS's support for standards such as OpenID Connect or SAML to\n| interface with @product@.\n\nThe CAS Server application requires your server to have a properly configured\nSecure Socket Layer (SSL) certificate. To generate one yourself, use the\n`keytool` utility that comes with the JDK. First generate the key, then export\nthe key into a file. Finally, import the key into your local Java key store. For\npublic, Internet-based production environments, you must purchase a signed key\nfrom a recognized certificate authority or have your key signed by a recognized\ncertificate authority. For Intranets, you should have your IT department\npre-configure users' browsers to accept the certificate so they don't get\nwarning messages about the certificate.\n\nTo generate a key, use the following command:\n\n```bash\nkeytool -genkey -alias tomcat -keypass changeit -keyalg RSA\n```\n\nInstead of the password in the example (`changeit`), use a password you can\nremember. If you are not using Tomcat, you may want to use a different alias as\nwell. For first and last names, enter `localhost` or the host name of your\nserver. It cannot be an IP address.\n\nTo export the key to a file, use the following command:\n\n```bash\nkeytool -export -alias tomcat -keypass changeit -file server.cert\n```\n\nFinally, to import the key into your Java key store, use the following command:\n\n```bash\nkeytool -import -alias tomcat -file server.cert -keypass changeit -keystore $JAVA_HOME/jre/lib/security/cacerts\n```\n\nIf you are on a Windows system, replace `$JAVA_HOME` above with `%JAVA_HOME%`.\nOf course, all of this must be done on the system where CAS is running.\n\nOnce your CAS server is up and running, configure @product@ to use it.\nCAS configuration can be applied either at the system scope or at the scope of\na portal instance. To configure the CAS SSO module at the system or instance\nscope, navigate to the Control Panel, click on *Configuration* &rarr; *System\nSettings* (or *Instance Settings*) &rarr; *Security* &rarr; *SSO*. The values\nconfigured in System Settings provide the default values for all portal\ninstances. Enable CAS authentication and then modify the URL properties to point\nto your CAS server.\n\n**Enabled:** Check this box to enable CAS single sign-on.\n\n**Import from LDAP:** A user may be authenticated from CAS and not yet exist in\n@product@. Select this to automatically import users from LDAP if they do not\nexist in @product@. For this to work, LDAP must be enabled.\n\nThe rest of the settings are various URLs with defaults included. Change\n*localhost* in the default values to point to your CAS server. When you are\nfinished, click *Save*. After this, when users click the *Sign In* link, they\nare directed to the CAS server to sign in to @product@.\n\nFor some situations, it might be more convenient to specify the system\nconfiguration via files on the disk. To do so, create the following\nfile:\n\n    {LIFERAY_HOME}/osgi/configs/com.liferay.portal.security.sso.cas.configuration.CASConfiguration.cfg\n\nThe format of this file is the same as any properties file. The key to use for\neach property that can be configured is shown below. Enter values in the same\nformat as you would when initializing a Java primitive type with a literal\nvalue.\n\nProperty Label | Property Key | Description | Type\n----- | ----- | ----- | -----\n**Enabled** | `enabled` | Check this box to enable CAS SSO authentication. | `boolean`\n**Import from LDAP** | `importFromLDAP` | Users authenticated from CAS that do not exist in @product@ are imported from LDAP. LDAP must be enabled separately. | `boolean`\n**Login URL** | `loginURL` | Set the CAS server login URL. | `String`\n**Logout on session expiration** | `logoutOnSessionExpiration` | If checked, browsers with expired sessions are redirected to the CAS logout URL. | `boolean`\n**Logout URL** | `logoutURL` | The CAS server logout URL. Set this if you want @product@'s logout function to trigger a CAS logout | `String`\n**Server Name** | `serverName` | The name of the @product@ instance (e.g., `liferay.com`). If the provided name includes the protocol (`https://`, for example) then this will be used together with the path `/c/portal/login` to construct the URL to which the CAS server will provide tickets. If no scheme is provided, the scheme normally used to access the @product@ login page will be used. | `String`\n**Server URL** | `serviceURL` | If provided, this will be used as the URL to which the CAS server provides tickets. This overrides any URL constructed based on the Server Name as above. | `String`\n**No Such User Redirect URL** | `noSuchUserRedirectURL` | Set the URL to which to redirect the user if the user can authenticate with CAS but cannot be found in @product@. If import from LDAP is enabled, the user is redirected if the user could not be found or could not be imported from LDAP. | `String`\n\nTo override system defaults for a particular portal instance, navigate to the\nControl Panel, click on *Configuration* &rarr; *Instance Settings*, click on\n*Authentication* on the right and then on *CAS* at the top.\n\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/11-ntlm-authentication.markdown",
    "content": "---\nheader-id: ntlm-single-sign-on-authentication\n---\n\n#  NTLM Single Sign On Authentication\n\n[TOC levels=1-4]\n\nNTLM (NT LAN Manager) is a suite of Microsoft protocols that provide\nauthentication, integrity, and confidentiality for users. Though Microsoft has\nadopted Kerberos in modern versions of Windows server, NTLM is still used when\nauthenticating to a workgroup. @product@ now supports NTLM v2\nauthentication. NTLM v2 is more secure and has a stronger authentication process\nthan NTLMv1.\n\n| **Note:** NTLM authentication was deprecated in @product-ver@ and was removed.\n| You can still install it from Marketplace [here](https://web.liferay.com/marketplace/-/mp/application/125668266) \n| or [here](https://web.liferay.com/marketplace/-/mp/application/125668305). \n\nNote that in order to use NTLM SSO, @product@'s portal instance authentication\ntype must be set to screen name. \n\n| **Note:** To USE NTLM with @product@, you must configure your browser. Consult\n| your browser vendor's documentation for the details.\n\nMost importantly, all users *must* be imported from an Active Directory server.\nNTLM (and Kerberos) works only if the users are in the AD; otherwise any SSO\nrequests initiated by @product@ fail.\n\n\nNTLM configuration can be applied either at the system scope or at the scope of\na portal instance. To configure the NTLM SSO module at the system scope,\nnavigate to the Control Panel, click on *Configuration* &rarr; *System Settings*\n&rarr; *Security* &rarr; *SSO* &rarr; NTLM. The values configured there provide\nthe default values for all portal instances. Enter values in the same format as\nyou would when initializing a Java primitive type with a literal value.\n\nProperty Label | Property Key | Description | Type\n---- | ---- | ---- | ----\n**Enabled** | `enabled` | Check this box to enable NTLN SSO authentication. Note that NTLM will only work if @product@'s authentication type is set to screen name. | `boolean`\n**Domain Controller** | `domainController` | Enter the IP address of your domain controller. This is the server that contains the user accounts you want to use with @product@. | `String`\n**Domain Controller Name** | `domainControllerName` | Specify the domain controller NetBIOS name. | `String`\n**Domain** | `domain` | Enter the domain / workgroup name | `String`\n**Service Account** | `serviceAccount` | You need to create a service account for NTLM. This account will be a computer account, not a user account. | `String`\n**Service Password** | `serviceAccount` | Enter the password for the service account. | `String`\n**Negotiate Flags** | `negotiateFlags` | Only available at system level. Set according to the client's requested capabilities and the server's ServerCapabilities. See [here](http://msdn.microsoft.com/en-us/library/cc717152%28v=PROT.10%29.aspx) | `String`\n\n\nNote the AD's name and IP address correspond to the `domainControllerName` and\n`domainController` settings. The `Service Account` is for the _NTLM_ account\n(registered with NTLM), not the @product@ user account.\n\nTo override system defaults for a particular portal instance, navigate to the\nControl Panel, click on *Configuration* &rarr; *Instance Settings*, click on\n*Authentication* and then on *NTLM*.\n\n\n## Summary\n\nNTLM authentication is often highly desirable in Intranet scenarios where the\nIT department has control over what software is running on client devices and\nthus can ensure NTLM compatibility. In an Active Directory based network /\ndomain, it is hard to beat the user experience that NTLM authentication can\nprovide.\n\nPlease remember that in order to use NTLM SSO, your @product@ instance\nauthentication type must be set to screen name *and* that all users have been\nimported from your active directory. If this is not acceptable for your\n@product@ implementation, then another SSO solution (such as CAS) can be used as\na broker between your portal and the NTLM authentication process.\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/12-openid-authentication.markdown",
    "content": "---\nheader-id: openid-single-sign-on-authentication\n---\n\n# OpenID Single Sign On Authentication\n\n[TOC levels=1-4]\n\nOpenID is a single sign-on standard implemented by multiple vendors.  Users can\nregister for an ID with the vendor they trust. The credential issued by that\nvendor can be used by all the web sites that support OpenID. Some high profile\nOpenID vendors are Google, Paypal, Amazon, and Microsoft. Please see the [OpenID\nsite](http://www.openid.net/) for a more complete list.\n\n| **Note:** OpenID is deprecated in @product-ver@ and has been removed. You can\n| still install it from Marketplace [here](https://web.liferay.com/marketplace/-/mp/application/125668346) \n| or [here](https://web.liferay.com/marketplace/-/mp/application/125668379). \n\nWith OpenID, users don't have to register for a new account on every site which\nrequires an account. Users register on *one* site (the OpenID provider's site)\nand then use those credentials to authenticate to many web sites which support\nOpenID. Web site owners sometimes struggle to build communities because users\nare reluctant to register for *another* account.  Supporting OpenID removes that\nbarrier, making it easier for site owners to build their communities. All the\naccount information is kept with the OpenID provider, making it much easier to\nmanage this information and keep it up to date.\n\n@product@ can act as an OpenID consumer, allowing users to automatically\nregister and sign in with their OpenID accounts. Internally, the product uses\n[OpenID4Java](https://github.com/jbufu/openid4java) to implement the feature.\n\n## OpenID at the System Scope\n\nOpenID is enabled by default in @product@ but can be disabled or enabled at either\nthe system scope or portal instance scope. To configure the OpenID SSO module at\nthe system level, navigate to the Control Panel and click on *Configuration*\n&rarr; *System Settings* &rarr; *Security* &rarr; *SSO*. There's only a single\nconfiguration setting. Check the *Enabled* box to enable OpenID at the system\nscope (for all portal instances), uncheck it to disable it at the system scope.\n\n## OpenID at the Instance Scope\n\nTo configure the OpenID SSO module at the portal instance scope, navigate to the\nControl Panel and click on *Configuration* &rarr; *Instance Settings*, then on\n*Authentication* &rarr; *OpenID*. There's only a single configuration setting.\nCheck the *Enabled* box to enable OpenID for the current portal instance,\nor uncheck it to disable it for the current portal instance.\n\nRegardless of whether OpenID is enabled at the System or Instance scope, users\ncan see the OpenID icon when they sign into @product@. Click *Sign In*. The\nOpenID icon is displayed at the lower left.\n\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/13-kerberos.markdown",
    "content": "---\nheader-id: authenticating-with-kerberos\n---\n\n# Authenticating with Kerberos\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/configuring-sso/authenticating-with-kerberos.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nYou can use Kerberos to authenticate Microsoft Windows &trade; accounts with\n@product@. This is done completely through configuration by using a combination\nof @product@'s LDAP support and a web server that supports the Kerberos\nprotocol. \n\nNote that this configuration is preferred above \n[NTLM](/docs/7-1/deploy/-/knowledge_base/d/ntlm-single-sign-on-authentication)\nbecause security vulnerabilities persist. \n\nWhile it's beyond the scope of this article to explain how to set up Kerberos\nand Active Directory on a Windows &trade; server, we can describe the minimum\nprerequisites for setting up Liferay authentication: \n\n1.  A Windows &trade; server with Active Directory and DNS set up so the AD\n    server and @product@ can resolve each other on the network. In other words,\n    they must be able to ping each other *by name*. \n\n2.  An administrative user in AD @product@ can use to bind to AD. \n\n3.  A Kerberos keytab file exported via the `ktpass` command containing the\n    cryptographic information the @product@ server needs to bind to AD. \n\n4.  A web server in front of @product@ that supports Kerberos, such as Apache,\n    NGNIX, or IIS. The web server must also support injecting a header to be used \n    as a token in the @product@ configuration (see below). \n\n5.  Of course, you need a @product@ installation that can also resolve by name\n    the other servers. It should never run on the Active Directory server.\n\nWhen you have all of these prerequisites in place, you're ready to configure\nKerberos authentication. \n\n## How Kerberos Authentication Works\n\nFrom the prerequisites, you may be able to guess that there are several moving\nparts to how SSO works with Kerberos. \n\n![Figure 1: Kerberos authentication requires a web server in front of your @product@ server.](../../images/kerberos.png)\n\nFirst, a properly configured web browser sends a negotiate request using\nencrypted Windows user data. To configure this, the browser must recognize the\nsite as a trusted site (explained below). The web server's Kerberos module uses\nthe keytab file to bind over the Kerberos protocol to AD and verify the user\ninformation. If all is okay, the AD server confirms the connection with a valid\nresponse. \n\nThe web server you choose must support both the Kerberos protocol and the\ninjection of a custom header into the request that @product@ can later read.\nWhen the web server forwards the request to @product@, it reads the header to\nobtain the user data and authenticate the user. \n\nNext, you'll learn how to get all of this working. \n\n## Configuring Kerberos Authentication\n\nThere are four components to configure: a user keytab from Active\nDirectory, a web server in front of your application server, @product@, and your\nWindows&trade; clients. \n\n### Creating the User Keytab\n\n1.  Create a user so @product@ can bind to Active Directory. \n\n2.  Generate a Kerberos keytab file using `ktpass`: \n\n        ktpass -princ HTTP/[web server host name]@[domain] -mapuser [user name]@[domain] -crypto ALL -ptype KRB5_NT_PRINCIPAL -pass [password] -out c:\\kerberos.keytab\n\n    For example: \n\n        ktpass -princ HTTP/mywebserver.intdomain.local@INTDOMAIN.LOCAL -mapuser Marta@INTDOMAIN.LOCAL -crypto ALL -ptype KRB5_NT_PRINCIPAL -pass password-for-Marta -out c:\\kerberos.keytab\n\n3.  Ensure that the AD domain controller and the web server can see each other\n    on the network via DNS configuration or `hosts` file. \n\n### Configuring Your Web Server\n\n1.  Configure Kerberos authentication. On Linux, this involves installing `krb5`\n    and configuring it to match your realm that's already configured for Active\n    Directory. The example domain for the user configured in step two above\n    would look like this: \n\n        [libdefaults]\n        \tdefault_realm = INTDOMAIN.LOCAL\n\n        [domain_realm]\n            mywebserver.intdomain.local = INTDOMAIN.LOCAL\n            intdomain.local = INTDOMAIN.LOCAL\n            .intdomain.local = INTDOMAIN.LOCAL\n\n        [realms]\n        INTDOMAIN.LOCAL = {\n            admin_server = winserver.intdomain.local\n            kdc = winserver.intdomain.local\n        }\n\n2.  Copy the keytab file you generated on your AD server to your web server. \n\n3.  Configure your web server, making sure you set the correct server name,\n    Kerberos service name, Kerberos authentication realms, and the path to the\n    keytab file. For example, if you're using the Apache HTTP server, the\n    configuration might look like this: \n\n    ```apache\n    LoadModule headers_module /usr/lib/apache2/modules/mod_headers.so\n    LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so\n    LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so\n    LoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so\n    LoadModule proxy_ajp_module /usr/lib/apache2/modules/mod_proxy_ajp.so\n    LoadModule auth_kerb_module /usr/lib/apache2/modules/mod_auth_kerb.so\n\n    <VirtualHost *:10080>\n        <Proxy *>\n            Order deny,allow\n            Allow from all\n        </Proxy>\n        ProxyRequests     Off\n        ProxyPreserveHost On\n        ProxyPass / ajp://localhost:8009/\n        ProxyPassReverse / ajp://localhost:8009/\n        ServerName mywebserver.intdomain.local\n        <Location />\n                    Order allow,deny\n                    Allow from all\n                    AuthType Kerberos\n                    KrbServiceName HTTP/mywebserver.intdomain.local@INTDOMAIN.LOCAL\n                    AuthName \"Domain login\"\n                    KrbAuthRealms INTDOMAIN.LOCAL\n                    Krb5KeyTab /etc/apache2/kerberos.keytab\n                    require valid-user\n                    KrbMethodNegotiate  On\n                    KrbMethodK5Passwd   Off\n                    #KrbLocalUserMapping On\n\n                    # Below directives put logon name of authenticated user into http header X-User-Global-ID\n                    RequestHeader unset X-User-Global-ID\n                    RewriteEngine On\n                    RewriteCond   %{LA-U:REMOTE_USER} (.+)\n                    RewriteRule   /.* - [E=RU:%1,L,NS]\n                    RequestHeader set X-User-Global-ID %{RU}e\n\n                    # Remove domain suffix to get the simple logon name\n                    # RequestHeader edit X-User-Global-ID \"@INTDOMAIN.LOCAL$\" \"\"\n\n        </Location>\n    </VirtualHost>\n    Listen 10080\n    ```\nThe last line is commented out based on user preference. If you want the domain\nremoved from the user name when saved in @product@, uncomment it. Otherwise,\nleave it commented out to store the domain with the user name. \n\n### Connecting @product@ to Active Directory over LDAP\n\n1.  Finally, configure @product@ to access Active Directory via the LDAP\n    protocol. Change authentication to be by Screen Name by selecting it in\n    Configuration &rarr; Instance Settings &rarr; Authentication &rarr; General. \n\n2.  Connect @product@ to AD over LDAP by going to Configuration &rarr; Instance\n    Settings &rarr; Authentication &rarr; LDAP and adding an LDAP server.\n    Provide the information appropriate to your installation: \n\n    **Base Provider URL:** Your AD server on the proper port. \n\n    **Base DN:** Your domain configuration. The example above might be\n    `DC=INTDOMAIN.DC=LOCAL`. \n\n    **Principal/Credentials:** Supply the credentials for the user exported to\n    the keytab file. \n\n    **Authentication Search Filter:** Supply the appropriate search filter to\n    return user objects. For example,\n    `(&(objectCategory=person)(sAMAccountName=*))`\n\n    **UUID:** Supply what uniquely identifies a user, such as `sAMAccountName`. \n\n    **Screen Name:** Supply the field that should be mapped to @product@'s\n    screen name field, such as `sAMAccountName`. \n\n    **Password:** Supply the field that contains the user's password, such as\n    `userPassword`. \n\n3.  Be sure to test the connection, save, and enable the configuration. \n\n4.  Finally, configure the token for single sign-on at Configuration &rarr;\n    System Settings &rarr; Security &rarr; SSO &rarr; Token Based SSO. Make sure\n    the User Token Name matches *exactly* the token you configured in your web\n    server. Click the *Enabled* and *Import from LDAP* boxes and click *Save*. \n\nExcellent! You've configured your servers. All that's left is to configure your\nclients. \n\n### Configuring your Clients\n\nYou must do two things: make your computer log into the domain and configure\nyour @product@ server as a trusted Internet site. \n\n1.  Join your computer to your domain. In keeping with the example above, you'd\n    make your computer a member of the `INTDOMAIN.LOCAL` domain. \n\n2.  Log in as a user in that domain. \n\n3.  Internet Explorer, Edge, and Chrome use the Windows&trade; settings for\n    trusted sites. If you use these browsers, go to Internet Options &rarr;\n    Security &rarr; Local Intranet Sites and add your @product@ server's URL.\n    For example, add `http://mywebserver.intdomain.local:10080`. \n\n4. Firefox can be configured by typing `about:config` in its address bar. Search\n   for the below two preferences and add the @product@ server's URL as the value\n   for both: \n\n   - `network.negotiate-auth.delegation-uris`\n   - `network.negotiate-auth.trusted-uris`\n\nAfter configuring these things, test your configuration by accessing @product@\nthrough the web server's URL. Since you are already logged into your client\nmachine, you should be automatically logged into @product@ without\na user/password prompt. \n\nCongratulations on configuring Kerberos with @product@!\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/14-cors.markdown",
    "content": "---\nheader-id: configuring-cors\n---\n\n# Configuring CORS\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/securing-web-services/setting-up-cors.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nCORS stands for Cross-Origin Resource Sharing. An Origin is a web server at\na different domain, and a Resource is some asset stored on the server, like an\nimage, PDF, or HTML file. Sometimes you must request resources stored on another\norigin. This is called a cross-origin request, and web servers have policies to\nallow or deny such requests. \n\nFor example, browsers themselves don't allow cross-origin AJAX-style requests\nfrom scripts to help mitigate \n[cross-site scripting](https://en.wikipedia.org/wiki/Cross-site_scripting)\nattacks. These APIs follow a *same origin* policy. But for certain resources, it\ncan be convenient to allow @product@ to serve them to different origins. \n\nFor example, if you manage images in Docs & Media, you may want to allow\ncross-origin requests for them. You can enable CORS for matching URLs in\n@product@ or for JAX-RS application resources. \n\n## Enabling CORS for @product@ Services\n\nYou'll find the settings in Configuration &rarr; System Settings &rarr; Security\n&rarr; Security Tools &rarr; Portal Cross Resource Origin Sharing (CORS): \n\n1.  Click *Add* to create a configuration entry. \n\n2.  Fill out the fields on the form. When finished, click *Save*. \n\n![Figure 1: The CORS system settings provide a way to configure CORS headers for Liferay services.](../../images/CORS-portal.png)\n\n**Enabled:** Check this box to enable the entry. \n\n**Name:** Give the configuration entry a name. \n\n**URL Pattern:** Use the Plus button to add as many patterns as you need. Define\npatterns that match URLs to the resources you want to share. For example, if you\nhave many attachments in the Knowledge Base application, you could define\nthis pattern: \n\n    /knowledge_base/*\n\nThis would define resources stored in the Knowledge Base as applicable to the\npolicy you set in the response header below. \n\n**CORS Response Headers:** Use the Plus button to add as many headers as you\nneed. Define policies for any of the [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#CORS) \nhere. \n\nYou can also use a [configuration file](/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files)\nto configure CORS. \n\n## Enabling CORS for JAX-RS Applications\n\nYou'll find the settings in Configuration &rarr; System Settings &rarr; Security\n&rarr; Security Tools &rarr; Web Contexts Resource Origin Sharing (CORS): \n\n1.  Click *Add* to create a configuration entry. \n\n2.  Fill out the fields on the form. When finished, click *Save*. \n\n![Figure 2: There's a separate system settings category for CORS web contexts.](../../images/CORS-jax-rs.png)\n\n**Dynamic Web Context OSGi Filter:** Define an LDAP-style [filter](https://osgi.org/specification/osgi.cmpn/7.0.0/service.http.whiteboard.html) \nto define which JAX-RS whiteboard applications the CORS headers in this entry\napply to. This is the default filter: \n\n```properties\n(&(!(liferay.cors=false))(osgi.jaxrs.name=*))\n```\nIt applies CORS headers to all deployed JAX-RS whiteboard applications without\na `liferay.cors=false` property. This helps during development, but in\nproduction you should use the narrowest configuration possible. \n\n**URL Pattern:** Use the Plus button to add as many patterns as you need. Define\npatterns that match URLs to the web services you want to access. \n\n**CORS Response Headers:** Use the Plus button to add as many headers as you\nneed. Define policies for any of the [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#CORS) \nhere. \n\n[JAX-RS](/docs/7-2/frameworks/-/knowledge_base/f/jax-rs) developers can use the\n`@CORS` annotation to set policies for their deployed applications. \n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/15-antisamy.markdown",
    "content": "---\nheader-id: antisamy\n---\n\n# AntiSamy\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/using-antisamy.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\n@product@ includes an [AntiSamy](https://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project) \nmodule that protects against user-entered malicious code. If your site allows\nusers to post content, such as in message boards, blogs, or other applications,\nthey could include malicious code either intentionally or unintentionally. The\nAntiSamy module filters HTML/CSS fragments and removes suspect JavaScript code\nfrom them. \n\nThe module leverages the powerful\n[OWASP AntiSamy library](https://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project)\nto enforce a content policy that's been effective for the auction site\neBay. The AntiSamy module adds an OWASP AntiSamy implementation to your\nportal's list of existing sanitizer implementations. @product@ uses the\nAntiSamy sanitizer and any existing configured sanitizers to scrub user input\nto blogs entries, calendar events, message boards posts, wiki pages, and web\ncontent articles.\n\nAntiSamy is enabled by default. \n\n![Figure 1: @product@'s AntiSamy configuration options allow you to specify both a blacklist and a whitelist.](../../images/antisamy.png)\n\n## Configuring AntiSamy\n\nAntiSamy uses both a blacklist and a whitelist, so you can define subsets of\nentities that should be sanitized or not sanitized. The whitelist prevents\ncontent of that type from being filtered, while the blacklist filters content of\nthat type. \n\nBy default, everything is sanitized except for `JournalArticle`,\n`BlogsEntry`, and `FragmentEntry`. The assumption is that users posting these\nkinds of content are trusted, while users posting message boards or wiki\narticles may not be trusted. If this is not the configuration you want, you can\nchange it: \n\n1.  Navigate to *Control Panel* &rarr; *System Settings* &rarr; *Security Tools*\n    &rarr; *AntiSamy Sanitizer*.\n\n2.  Enter a package path you want to sanitize into the *Blacklist* field. \n\n3.  Use the plus (+) button to add further Blacklist fields if you need them. \n\n4.  Use the plus (+) button to add further Whitelist fields if you need them. \n\n5.  Enter a package path you don't want sanitized into a *Whitelist* field. \n\n6.  If you want to remove a package path from the configuration, click the trash\n    can icon. \n\n7.  When finished, click *Save*. \n\n## Using Wildcards\n\nYou can use wildcards in the configuration. For example, if you only want to\nsanitize message board posts and nothing else, you can\n\n1. Configure the whitelist to `*`\n\n2. Configure the blacklist to `com.liferay.message.boards.*`\n\nThe whitelist and the blacklist work together. Without the blacklist,\nthe above configuration's whitelist must include every content type except\n`com.liferay.message.boards`, which would be a daunting task to configure.\n\nUse AntiSamy to ensure user-generated content stays safe for other users to\nview. \n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/15-oauth2/01-oauth2-intro.markdown",
    "content": "---\nheader-id: oauth-2-0\n---\n\n# OAuth 2.0\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/headless-delivery/using-oauth2.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nOAuth 2.0 is an industry-standard authorization protocol. Users can seamlessly\nshare select credentials from your Liferay-based website with various clients.\nYou've seen this before: any time you see a \"This site wants to access:\" button\n(followed by a list of things like email address, friends list, etc.) from\nGoogle or Facebook, or you authorize a third-party Twitter client, that's OAuth\n2.0 in action. It works by authorizing password-less access to portions of\nuser-owned resources (such as an email address, a user profile picture, or\nsomething else from your account) and other permissioned resources.\n\nOAuth 2.0's design encrypts all authorization transport through HTTPS, which\nprevents data passed between the systems from being intercepted. \n\n## Flow of OAuth 2.0\n\nOAuth 2.0 takes advantage of web standards wherever possible: transport is\nencrypted with HTTPS; tokens are implemented as HTTP headers; data is passed via\nweb services. \n\nHere's how OAuth 2.0 works: \n\n![Figure 1: OAuth 2.0 takes advantage of web standards.](../../../images/oauth-flow.png)\n\n1.  A user accesses a third-party application that supports authorization via\n    credentials from a Liferay-based website. In the application (web or\n    mobile), the user requests authorization via OAuth, sending the browser or\n    app to the Liferay-based website. When using PKCE (explained below), the\n    application also generates a code verifier and sends a code challenge that \n    is created by applying a transformation to it. \n\n2.  The user authenticates and is shown the resources the application wants\n    permission to access. When the user gives permission by clicking *Allow*,\n    Liferay generates an authorization code that's sent to the application over\n    HTTPS. \n\n3.  The application then requests a more permanent authorization token and\n    sends the code with the request (along with the PKCE code verifier). \n\n4.  If the authorization code matches (and the transformed PKCE code verifier\n    matches the previously sent code challenge), Liferay cryptographically\n    generates an authorization token for this user and application combination.\n    It sends the token to the application over HTTPS. Initial authorization is\n    now complete! \n\n5.  When the application must retrieve data, it sends the token with the request\n    to prove it's authorized to have that data.\n\n6.  Provided the token matches what Liferay has for that user and application,\n    access is granted to retrieve the data. \n\nThat description throws around a lot of terms. Definitions provided below. \n\n## OAuth 2.0 Terminology\n\n**Authentication:** Providing credentials so a system can verify who you are by\nmatching those credentials with what it has stored. OAuth is not an\nauthentication protocol. \n\n**Authorization:** Granting access to resources stored on another system. OAuth\nis an authorization protocol. \n\n**Application:** Any client (web, mobile, etc.) that must be authorized to have\naccess to resources. Applications must be registered by administrators before\nusers can authorize access to their resources. \n\n**Client:** Almost synonymous with *application*, except that applications can\nhave variants, such as web and mobile. These variants are clients. \n\n**Client ID:** An identifier given to a client so it can be recognized. \n\n**Client Secret:** A previously agreed-upon text string that identifies a client\nas a legitimate client. \n\n**Access Token:** A cryptographically generated text string that identifies\na user/client combination for access to that User's resources. \n\n**Response Type:** OAuth 2.0 supports several response types. Pictured and\ndescribed above is the most common: the authorization code. Other response types\nare *password* (logging in with a user name and password), and *client\ncredentials* (headless predefined application access). \n\n**Scope:** A list of items that define what the application wants to access.\nThis list is sent during the initial authorization request (or otherwise\ndefaults to scopes selected in the application registration) so users can grant\nor deny access to their resources. \n\n**Callback URI:** Also called a Redirection Endpoint URI. After authorization is\ncomplete, the authorization server (i.e., Liferay) sends the client to this\nlocation. \n\n## Creating an Application\n\nWhen you have an application that can use OAuth 2.0 for authorization, you must\nregister that application so @product@ can recognize it. Do this by accessing\n*Control Panel* &rarr; *Configuration* &rarr; *OAuth2 Administration*: \n\n1.  Click the *Add* (![add](../../../images/icon-add.png)) button. \n\n2.  Fill out the form (description below). \n\n3.  Click *Save* to save the application. \n\n![Figure 2: Adding an application registers it so users can authorize access to their data.](../../../images/oauth-new-application.png)\n\n**Name:** Give the application a recognizable title. \n\n**Website URL:** Add a link to the application's website. \n\n**Callback URIs:** Enter at least one (line-separated) URI where users should\nbe redirected after they authorize (or refuse to authorize) access to their\naccounts. This should link to a handler for whichever Allowed Authorization\nTypes you support (see below). \n\n**Client Profile:** Choose a template that filters the authorization types\nthat are appropriate (secure) for that profile. For example, if your application\nis a web application, choose *Web Application*, and these authorization types \nare available and selected automatically: Authorization Code, Client Credentials, \nRefresh Token, and Resource Owner Password Credentials. These are OAuth 2 \"flows\" \ndocumented in the \n[OAuth2 RFC 6749 Standards Document](https://tools.ietf.org/html/rfc6749). \nIf you want to select authorization types manually, select *Other*. \n\n**Allowed Authorization Types:** Select the defined OAuth 2 \n[protocol flows](https://tools.ietf.org/html/rfc6749#section-1.2) your\napplication supports. Several common combinations are defined for you in the\nvarious Client Profiles above. \n\nAfter you save the form, it reappears with additional fields: \n\n**Client ID:** The system generates this for you; it's an identifier for your\napplication, so that @product@ knows what application is being authorized to\naccess user data. \n\n**Client Secret:** Click the *pencil* icon to generate a client secret. The\nsecret identifies the client during the authorization process (see figure\n1 above). Not all client profiles require a client secret, because some are\nincapable of keeping it secret! This is when the aforementioned PKCE code\nchallenge and verifier is needed.\n\n**Icon:** Upload an icon that your application's users identify with your\napplication. This is displayed on the authorization screen. \n\n**Privacy Policy URL:** Add a link to your application's privacy policy. \n\n**Token Introspection:** Allow your application to retrieve metadata from the\ntoken by requesting it from @product@. This implements \n[RFC 7662](https://tools.ietf.org/html/rfc7662).\n\nExcellent! Now you know how to add OAuth2 authorization for your application to\n@product@! Next, you must define scopes of user data the application can access.\n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/15-oauth2/02-scopes.markdown",
    "content": "---\nheader-id: oauth2-scopes\n---\n\n# OAuth2 Scopes\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/headless-delivery/using-oauth2/oauth2-scopes.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nIn OAuth 2.0, applications are granted access to limited subsets of user data.\nThese are called *scopes* (not to be confused with Liferay scopes). They are\ncreated in two ways: \n\n1.  By administrators, by creating a Service Access Policy for the scope\n\n2.  By developers, by creating a JAX-RS endpoint. By default, scopes are \n    generated based on the HTTP verbs supported by the JAX-RS endpoint.\n    A special annotation overrides this behavior and registers specific scopes. \n\n## Creating a Scope for a JSONWS Service\n\nThe most common way to create a scope is to create a \n[Service Access Policy](/docs/7-2/deploy/-/knowledge_base/d/service-access-policies)\nprefixed with the name `OAUTH2_`. This naming convention causes the policy to appear\nin the OAuth application configuration screen as a scope. \n\nFor example, say the application needs access to a user's profile information to\nretrieve the email address. To grant the application access to this, go to\n*Control Panel* &rarr; *Configuration* &rarr; *Service Access Policy*, and\ncreate the policy pictured below. \n\n![Figure 1: A Service Access Policy defines a scope for OAuth 2.0 applications.](../../../images/oauth-service-access-policy.png)\n\nNote that the policy is not a default policy, and that it grants access only to\none method in the `UserService`. This is a JSONWS web service generated by\nService Builder. You can view a list of all available services in your\ninstallation at this URL: \n\n    http://[host]:[port]/api/jsonws/\n\nOnce you create a policy and name it with the `OAUTH2_` prefix, it appears in\nthe *Scopes* tab in OAuth2 Administration. \n\n![Figure 2: Scopes named with the proper prefix appear in the Scopes tab of your application configuration.](../../../images/oauth-scopes-tab.png)\n\nNow you can select it and save your application. \n\n## Creating the Authorization Page\n\nThis step is optional. Users need an interface to authorize access to their\naccounts, and one is provided automatically. If, however, you want to customize\nthe page, you can create an authorization page in your Site. \n\n1.  Go to *Control Panel* &rarr; *System Settings* &rarr; *Security* &rarr;\n    *OAuth2*. Click the bottom item on the left, labeled *Authorize Screen*. \n\n2.  Two defaults appear. The first is the URL to the authorize page. By default,\n    it's `/group/guest/authorize-oauth2-application`. This corresponds to the\n    default site's URL and a page on that site called\n    `authorize-oauth2-application`. \n\n3.  If you have customized the name and URL of your default site, make the\n    appropriate change here so the URL matches the page you'll create in that\n    site next. Click *Save*. \n\n4.  Go to your Site's *Build* &rarr; *Pages* screen. Click the\n    ![add](../../../images/icon-add.png) button and choose *Private Page*. This forces\n    users to log in. \n\n5.  Choose the *Full Page Application* type. \n\n6.  Give the page the same name you configured in step 2. \n\n7.  Uncheck the box labeled *Add this Page to the following Menus:*. You don't\n    want this page showing up in your Site navigation. \n\n8.  On the page that appears next, verify the Friendly URL matches the URL you\n    configured in step 2. \n\n9.  Under *Full Page Application*, choose *OAuth2 Authorize Portlet*. \n\n10. Click *Save*. \n\nExcellent! Users can use the default or the UI of your design to go through the\nauthorization process. Now that you have the UI and you understand scopes, it's\ntime to make the authorization process happen in your application. \n"
  },
  {
    "path": "en/deployment/articles/04-securing-liferay/15-oauth2/03-authorizing-access.markdown",
    "content": "---\nheader-id: authorizing-account-access-with-oauth2\n---\n\n# Authorizing Account Access with OAuth2\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/headless-delivery/using-oauth2/authorizing-account-access-with-oauth2.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[TOC levels=1-4]\n\nOnce you have an application registered, you can start authorizing users. To do\nthat, you must construct the URL to the authorization server (@product@). The\nauthorization server asks users to authorize the requested permissions to their\nresources, defined as you saw in the previous tutorial as scopes. \n\n## Authorization Code Flow\n\nThe most common OAuth flow is the Authorization Code flow, used for web\napplications. The URL for this requires the following request parameters: \n\n- `response_type` \n- `client_id` \n\nTo construct a URL for this authorization, follow this pattern: \n\n    https://[hostname]/o/oauth2/authorize?response_type=code&client_id=[client ID]\n\nThe client ID comes from registering the application. It's automatically\ngenerated (you can change it if you edit the application). \n\nIMPORTANT: Sometimes the phrase \"web application\" is used loosely, implying\napplications where the above URL is requested from the web browser directly. If\nthis happens, you'd leak the client secret, compromising the security of the\ngrant flow and the application. In such cases, select the \"User Agent\nApplication\" client profile instead when registering your application. This\nmakes a secure alternative available to your application: PKCE Extended\nAuthorization Code flow (see below).\n\nOnce the user has authorized the requested permissions to their resources, the\nauthorization server returns an authorization code to your application at its\nregistered callback URI (A.K.A. redirect URI) as a query string parameter. \n\n\t[your callback URI]?code=[authorization server generated code]\n\nYour application must then exchange this authorization code for an access token\nby sending a POST request following this pattern:\n\n\thttp://localhost:8080/o/oauth2/token\n\nWith the following parameters in the body (encoded as \n`application/x-www-form-urlencoded`):\n\n\tclient_id=[client ID]\n\tclient_secret=[client secret]\n\tgrant_type=authorization_code\n\tcode=[authorization server generated code]\n\tredirect_uri=[registered callback URI]\n\nIn the body of HTTP response to this request, you receive JSON like this:\n\n```json\n{\n\t\"access_token\": \"[authorization server generated access token]\",\n\t\"token_type\": \"Bearer\",\n\t\"expires_in\": 600,\n\t\"scope\": \"[the scopes that were authorized by the user]\",\n\t\"refresh_token\": \"[authorization server generated refresh token]\"\n}\n```\n\nFrom this you should extract and persist the access token. If you intend to use \nthe token for an indefinite amount of time (beyond 600 seconds from the above \nexample) you also need the refresh token. This can be used in conjunction with \nthe Refresh Token Flow to obtain a new access token with the same permissions,\nwithout further user authorization.  The authorization server only issues\nRefresh Tokens if your application registration is registered for this flow.\n\n## PKCE Extended Authorization Code Flow\n\nThis flow is the same as above with the addition of the Proof Key for Code\nExchange (PKCE). It requires another request parameter: `code_challenge`. This\nflow is for clients like smartphone applications that may not have sole access\nto the URL (and thus the request parameters) redirected to by the authorization\nserver after the user authorization. It protects against a malicious application\non the same system authorizing itself by reading the response code. To do this,\nthe client application sends a *code challenge* with the authorization request:\na string it has generated and which it only knows. To generate this string it\nmust first create another secret string known as the *Code Verifier*, and then\napply a transformation to it. After authorization, the code verifier is sent\nwith the authorization code, validating the client. \n\nFor more detail on how to do this, please refer to the \n[PKCE specification](https://tools.ietf.org/html/rfc7636).\n\nTo support this flow, you must have defined PKCE as an Allowed Authorization\nType when you created the application. This is part of the Native Application \nand User Agent Application client profiles. To request an authorization code \nusing PKCE, use a URL containing the `code_challenge` request parameter: \n\n    https://[hostname]/o/oauth2/authorize?response_type=code&client_id=[client ID]&code_challenge=[PKCE code challenge]\n\nThe rest of the process is identical to Authorization Code flow, except that\nwhen making the final request to get the access token, you must also provide the\nfollowing parameter:\n\n\tcode_verifier=[Code Verifier that was transformed and sent as code_challenge previously]\n\n## Client Credentials and Resource Owner Flows\n\nThere are two other, less used flows. If you have a scenario where two servers\nexchange agreed upon, non user-centric data, you can bypass the Allow/Deny\nscreen for users and authorize the client. This is called the Client Credentials\nflow, and you'd use this URL pattern: \n\n    https://[hostname]/o/oauth2/token?grant_type=client_credentials&client_id=[client ID]&client_secret=[client secret]\n\nA final flow, where users trust the application with their passwords is rare,\nbut possible. This is called the Resource Owner Password flow, and its URL\npattern looks like this: \n\n    https://[hostname]/o/oauth2/token?grant_type=password&client_id=[client ID]&client_secret=[client secret]&username=[user@emailaddress.com]&password=\n\nUsers are prompted for their passwords, and upon successful log in, receive an\nauthorization code. \n\n## Token Use\n\nAll flows above result in an access token that's sent by the authorization\nserver (@product@) to the client application. This token is sent in the response\nfor the client application to store and send along with any future request for\ndata. \n\nFor example, say the authorization code `946856e2b5ddf0928f6fc55f657bab73` was\nsent to the client application. When the client requests data, this code must be\nsent in each request header. Using a command line HTTP client such as Curl, you\ncould send a request like this: \n\n    curl -H 'Authorization: Bearer 946856e2b5ddf0928f6fc55f657bab73' 'https://[hostname]/o/api/sample2'\n\nOAuth 2.0 provides a convenient way for client applications to be granted access\nto particular services (scopes) by users without sharing credential information. \n\n## Revoking Access\n\nOnce access is granted, users or administrators are free to revoke access\nwhenever they wish. If this happens to a client, the token becomes invalid and\nthe client must ask the user for authorization again. This puts users in control\nof what has access to their data, and they can exercise this control at any\ntime. \n\n![Figure 1: Users have complete control over what applications have access to their data in their account profiles.](../../../images/oauth-user-apps.png)\n\nIn their account areas, users can click *OAuth2 Connected Applications* and see\na list of applications they've allowed to access their accounts. From here,\nthey can revoke access by clicking the *Remove Access* item in the Action menu\nor the *Remove Access* button in the detail screen for the application. \n\nAdministrators can view the authorizations in the Authorizations tab of any app\nin *Control Panel* &rarr; *Configuration* &rarr; *OAuth2 Administration*. \n\n![Figure 2: All authorizations for an app appear in the Authorizations tab for the app.](../../../images/oauth-revoke-access.png)\n\nClicking the *Revoke* button on any listed authorization revokes that\napplication's access to that user's account. \n\n## Summary\n\nOAuth 2.0 provides a complete and secure authorization flow for users, without\ntheir having to share any credential information. Once applications are created\nin the system, secure tokens provide access to particular scopes of information,\nand this access can be revoked at any time, making OAuth 2.0 a convenient method\nfor users and developers alike to access the information they need. \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/01-upgrading-to-liferay-7-2-intro.markdown",
    "content": "---\nheader-id: upgrading-to-product-ver\n---\n\n# Upgrading to @product-ver@\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/upgrade-basics.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nUpgrading to @product-ver@ involves migrating your installation and [code (your\ntheme and custom\napps)](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver) to\nthe new version. Here you'll learn how to upgrade your installation.\n\nHere are the installation upgrade paths:\n\n| Upgrade Path                                   | Description                |\n| ---------------------------------------------- | -------------------------- |\n| Liferay Portal 5.x and 6.0.x &rarr; Liferay Portal 6.2 &rarr; @product@ 7.2 | Support life ended for Liferay Portal 5.0, 5.1, 5.2, and 6.0 |\n| Liferay Portal 6.1.x &rarr; @product@ 7.1 &rarr; @product@ 7.2      | Support life ended for Liferay Portal 6.1 |\n| Liferay Portal 6.2+ &rarr; @product@ 7.2       |                            |\n| @product@ 7.0+ &rarr; @product@ 7.2            |                            |\n\n| **Note:** Themes and custom apps from Liferay Portal 6.0 through @product@ \n| 7.1 can be upgraded directly to @product@ 7.2. See the\n| [code upgrade instructions](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver)\n| for details. \n\nHere are the upgrade steps: \n\n| **Note:** You can \n| [prepare a new Liferay server for data upgrade](/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database)\n| in parallel with the steps up to and including the step to\n| [upgrading the @product@ data](/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-product-data). \n\n1.  {.root}[If You're Upgrading to Liferay Portal 6.2, Follow the Liferay Portal 6.2 Upgrade Instructions First](/docs/6-2/deploy/-/knowledge_base/d/upgrading-liferay){.title}\n\n2.  [If You're Upgrading a Sharded Environment, Follow the Instructions for Upgrading It](/docs/7-2/deploy/-/knowledge_base/d/upgrading-a-sharded-environment){.title}\n\n    Upgrading a sharded installation to @product-ver@ requires migrating it to\n    as many non-sharded @product@ installations (servers) as you have shards.{.summary}\n\n3.  [If You're a Upgrading a Cluster, Read Those Instructions First](/docs/7-2/deploy/-/knowledge_base/d/updating-a-cluster){.title}\n\n    If you're updating a cluster, read those instructions first and apply them\n    to your upgrade.{.summary}\n\n4.  [Plan for Handling the Deprecated Applications](/docs/7-2/deploy/-/knowledge_base/d/planning-for-deprecated-applications){.title}\n\n    Every application deprecation has different ramifications. Learn how the\n    deprecations might affect your site and decide how to replace the\n    functionality you use from those applications.{.summary}\n\n5.  [Test Upgrading a @product@ Backup Copy](/docs/7-2/deploy/-/knowledge_base/d/test-upgrading-a-product-backup-copy){.title}\n\n    Here you'll prune a backup copy of your database and upgrade the data.\n    You'll learn how to use the upgrade tool and resolve upgrade issues. The\n    notes and scripts you assemble as you prune and upgrade the database copy\n    are invaluable for correctly and efficiently upgrading the @product@\n    database you'll use with @product-ver@.{.summary}\n\n    1.  [Copy the Production Installation to a Test Server](/docs/7-2/deploy/-/knowledge_base/d/test-upgrading-a-product-backup-copy#copy-the-production-installation-to-a-test-server){.title}\n\n        You'll use the installation copy to test data changes.{.summary}\n \n    2. [Copy the Production Database Backup](/docs/7-2/deploy/-/knowledge_base/d/test-upgrading-a-product-backup-copy#copy-the-production-backup-to-the-test-database){.title}\n\n        Copy the production backup to the test database and save the copy logs\n        for analysis.{.summary}\n\n    3.  [Remove Duplicate Web Content and Structure Field Names](/docs/7-2/deploy/-/knowledge_base/d/pruning-the-database){.title}\n\n    4.  [Find and Remove Unused Objects](/docs/7-2/deploy/-/knowledge_base/d/pruning-the-database){.title}\n\n        You may have intermediate versions of objects (e.g., `JournalArticle`\n        objects) that you don't need. Remove them and objects that only\n        reference them.{.summary}\n\n    5.  [Test @product@ with its Pruned Database Copy](/docs/7-2/deploy/-/knowledge_base/d/pruning-the-database#test-with-the-pruned-database-copy){.title}\n\n        Make sure @product@ continues to work successfully. If it's broken,\n        start over with a fresh database backup and prune it more carefully.{.summary}\n\n    6.  [Install the New  @product@ Version on a Test Server](/docs/7-2/deploy/-/knowledge_base/d/upgrading-your-test-server-and-database){.title}\n\n        Install the @product@ version you're upgrading to, to use its upgrade\n        tool.{.summary}\n        \n    7.  [Tune Your Database for the Upgrade](/docs/7-2/deploy/-/knowledge_base/d/tuning-for-the-data-upgrade){.title}\n\n    8.  [Upgrade the Liferay Data, then Return Here](/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-product-data){.title}\n\n    9.  [If the Upgrade Took too Long, Prune a Fresh Database Backup More and Upgrade Its Data](/docs/7-2/deploy/-/knowledge_base/d/test-upgrading-a-product-backup-copy#copy-the-production-backup-to-the-test-database){.title}\n\n    10. [Test the Upgraded Instance](/docs/7-2/deploy/-/knowledge_base/d/pruning-the-database){.title}\n\n        Make sure @product@ continues to work successfully. If it's broken,\n        start over with a fresh database backup and prune it more carefully.{.summary}\n\n    11. Checkpoint: You've pruned and upgraded your production database copy. \n        You're ready to prepare for upgrading the production database. \n\n6.  [Prepare to Upgrade the @product@ Database](/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database){.title}\n\n    Preparing for the production database upgrade involves pruning and testing\n    it, upgrading your Marketplace apps, publishing staged changes, and\n    synchronizing a complete data and configuration backup.{.summary}\n\n    1.  [Remove All Noted Unused Objects](/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database#remove-all-unused-objects-you-identified-earlier){.title}\n\n        Remove all unused objects you noted from pruning your test database.{.summary}\n\n    2.  [Test @product@](/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database#test-using-the-pruned-database){.title}\n\n    3.  [Upgrade Your Marketplace Apps](/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database#upgrade-your-marketplace-apps){.title}\n\n    4.  [Publish All Staged Changes](/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database#publish-all-staged-changes-to-production){.title}\n\n    5.  [Synchronize a Complete @product@ Backup](/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database#synchronize-a-complete-backup){.title}\n\n        Synchronize a complete backup of your production @product@ server\n        installation and pruned production database.{.summary}\n\n    6.  Checkpoint: You're ready to prepare a @product-ver@ server for \n        upgrading a production database. \n\n7.  [Prepare a New @product@ Server for Data Upgrade](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade){.title}\n\n    Set up a production server with @product-ver@, configured to use your\n    document repository and @product@ database. You'll migrate your portal and\n    system properties too. (Note, this step can be done in parallel with any of\n    the previous steps.){.summary}\n\n    1.  [Request an Upgrade Patch From Liferay Support \\(Liferay DXP Only\\)](/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database#synchronize-a-complete-backup){.title}\n\n    2.  [Install the @product@ Version You're Upgrading To](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade#install-liferay){.title}\n\n    3.  [Install the Latest Upgrade Patch or Fix Pack \\(Liferay DXP Only\\)](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade#install-the-latest-upgrade-patch-or-fix-pack-liferay-dxp-only){.title}\n\n    4.  [Migrate Your OSGi Configurations \\(@product@ 7.0+\\)](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade#migrate-your-osgi-configurations-70){.title}\n\n    5.  [Migrate Your Portal Properties](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade#migrate-your-portal-properties){.title}\n\n        Migrate your portal properties to your new @product-ver@ server.{.summary}\n\n        1.  [Update Your Portal Properties](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade#update-your-portal-properties){.title}\n\n            Some of the portal properties have new values or have been removed\n            or replaced. Update your properties for @product-ver@.{.summary}\n\n        2.  [Convert Applicable Properties to OSGi Configurations](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade#convert-applicable-properties-to-osgi-configurations){.title}\n\n            Many applications are configured using OSGi Configuration (Config\n            Admin) instead of portal properties. Convert your existing\n            properties to their OSGi Configuration replacements.{.summary}\n\n    6.  [Configure Your Documents and Media File Store](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade#configure-your-documents-and-media-file-store){.title}\n\n        The upgrade tool upgrades your Documents and Media file store too.\n        Update your Documents and Media file store configuration and specify it\n        for the upgrade tool.{.summary}\n\n    7.  [Disable Indexing](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade#disable-indexing){.title}\n\n        Improve the data upgrade performance by disabling indexing.{.summary}\n\n    8.  Checkpoint: You've prepared a new @product@ server for executing the \n        data upgrade \n\n8.  [Upgrade the @product@ data](/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-product-data){.title}\n\n    This section explains the data upgrade options, upgrade configuration,\n    and the upgrade process.{.summary}\n\n    1.  [Tune Your Database for the Upgrade](/docs/7-2/deploy/-/knowledge_base/d/tuning-for-the-data-upgrade){.title}\n\n    2.  [Configure the Data Upgrade](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-data-upgrade){.title}\n\n        Configure the data upgrade, including the data store and whether to\n        automatically upgrade the modules.{.summary}\n\n    3.  [Upgrade the Core](/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-core-using-the-upgrade-tool){.title}\n\n        1.  [Run the Data Upgrade Tool](/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-core-using-the-upgrade-tool#upgrade-tool-usage){.title}\n\n            Run the data upgrade tool. Resolve any core upgrade issues.{.summary}\n\n        2.  [Issues Upgrading to 7.0 or Lower? Restore the Database Backup](/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database#synchronize-a-complete-backup){.title}\n\n            If the issues were with upgrades to Liferay 7.0 or lower, get a\n            clean start by restoring the pruned production database backup.{.summary}\n\n        3.  [Upgrade Your Resolved Issues](/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-core-using-the-upgrade-tool){.title}\n\n            If there were issues upgrading to 7.2, resolve them and restart the\n            data upgrade tool; continue if there were no issues.{.summary}\n\n    4.  [Upgrade the Liferay Modules](/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell){.title}\n\n        Learn how to use Gogo Shell to upgrade the Liferay modules, if you \n        didn't upgrade them automatically with the core.{.summary}\n\n        1.  [Upgrade Modules that are Ready for Upgrade](/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell#command-usage){.title}\n\n            Discover which modules are ready for upgrade and upgrade them.{.summary}\n\n        2.  [Check Module Upgrade Status and Resolve Any Module Upgrade Issues](/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell#checking-upgrade-status){.title}\n\n        3.  Checkpoint: You've completed upgrading the Liferay data. It's time\n            to get your server ready for production.{.summary}\n\n9.  [Execute the Post-Upgrade Tasks](/docs/7-2/deploy/-/knowledge_base/d/executing-post-upgrade-tasks){.title}\n\n    Now that your database is upgraded, clean up remnants of upgrading by\n    restoring your database optimizations, enabling and regenerating your search\n    indexes, and more.{.summary}\n\n    1.  [Remove the Database Tuning](/docs/7-2/deploy/-/knowledge_base/d/executing-post-upgrade-tasks#tuning-your-database-for-production){.title}\n\n    2.  [Re-enable and Re-Index the Search Indexes](/docs/7-2/deploy/-/knowledge_base/d/executing-post-upgrade-tasks#re-enabling-search-indexing-and-re-indexing-search-indexes){.title}\n\n    3.  [Update Web Content Permissions \\(7.0 and lower\\)](/docs/7-2/deploy/-/knowledge_base/d/executing-post-upgrade-tasks#enabling-web-content-view-permissions){.title}\n\n    4.  [Address Any Deprecated Apps That Still Need Handling](/docs/7-2/deploy/-/knowledge_base/d/planning-for-deprecated-applications){.title}\n\n10.  Checkpoint: You've completed the upgrade and post-upgrade tasks\n\nFollow the steps above to upgrade your @product@ installation to @product-ver@.\nThey link upgrade topic details to help complete a safe, successful upgrade. \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/02-planning-for-deprecated-apps.markdown",
    "content": "---\nheader-id: planning-for-deprecated-applications\n---\n\n# Planning for Deprecated Applications\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/reference/maintenance-mode-and-deprecations-in-7-2.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAs @product@ evolves so do Liferay applications. They may, for example, be\ndeprecated in favor of newer and better Liferay applications. The deprecations\nmight call for migrating application data to a new application or completely\nremoving the application and data. Before upgrading, examine the deprecations:\n\n-   [7.2 deprecations](/docs/7-2/deploy/-/knowledge_base/d/deprecated-apps-in-7-2-what-to-do)\n\n-   [7.1 deprecations](/docs/7-1/deploy/-/knowledge_base/d/deprecated-apps-in-7-1-what-to-do)\n\nDetermine how and when to address the deprecations in your upgrade plan. \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/03-test-ugrading-a-liferay-backup-copy/01-test-upgrading-a-liferay-backup-copy-intro.markdown",
    "content": "---\nheader-id: test-upgrading-a-product-backup-copy\n---\n\n# Test Upgrading a @product@ Backup Copy\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/upgrade-basics.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nBefore upgrading your production Liferay instance, you should do a trial run\n(even multiple runs) to make sure that you upgrade successfully and efficiently.\nHere's the process:\n\n-   [Preparing a test server and\n    database](#preparing-a-test-server-and-database): This involves copying your\n    current production installation to a test server and copying your production\n    data backup to a test database. After you prune data from the test database\n    (next step) you'll test against it. \n\n-   [Pruning the\n    database](/docs/7-2/deploy/-/knowledge_base/d/pruning-the-database):\n    Free your database of duplicate and unused objects. By removing them you can\n    reduce upgrade time and improve your server's performance. \n\n-   [Upgrading your test server and\n    database](/docs/7-2/deploy/-/knowledge_base/d/upgrading-your-test-server-and-database):\n    First you'll optimize your database for the data upgrade. Taking time to do\n    this can save upgrade time. Then you'll do an upgrade test run (or several\n    test runs) on a the pruned database copy. After going through the upgrade\n    process, resolving any issues, and testing the upgraded server successfully,\n    you can confidently upgrade your production database. \n\n| **Tip:** These steps and\n| [preparing a new @product@ server](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade)\n| can be done in parallel to save time. \n\nNow prepare your test environment. \n\n## Preparing a Test Server and Database\n\nUsing a new separate server and database let's you safely test upgrading. \n\n### Copy the Production Installation to a Test Server\n\nPrepare a test server to use a copy of your production installation. Your test\nserver must use the same Liferay version you're using on production. Configure\nyour server to use a new empty database for testing data upgrades. \n\n### Copy the Production Backup to the Test Database\n\nImport data from your [production database\nbackup](/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation)\nto the new empty database. \n\n| **Important:** Make sure to save the data import log---you'll examine it in\n| the next steps. \n\nNext you'll prune your database of unneeded data. \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/03-test-ugrading-a-liferay-backup-copy/02-pruning-the-database.markdown",
    "content": "---\nheader-id: pruning-the-database\n---\n\n# Pruning the Database\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/upgrade-stability-and-performance/database-pruning-for-faster-upgrades.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAccumulating unneeded site data is common. For example, you may have many unused\nversions of Web Content articles or Documents and Media files. If you're done\nrevising them and don't need the intermediate revisions, you can remove them.\nThis saves you space and upgrade time. Here you'll remove unneeded data and then\ntest your server. \n\n## Remove Duplicate Web Content Structure Field Names\n\nIf you've used Web Content Management extensively, you might have structures\nwithout unique field names. Find and remove duplicate field names before\nupgrading. If you upgraded to Liferay Portal 6.2 previously and skipped doing\nthis, you'll encounter this error: \n\n    19:29:35,298 ERROR [main][VerifyProcessTrackerOSGiCommands:221] com.liferay.portal.verify.VerifyException: com.liferay.dynamic.data.mapping.validator.DDMFormValidationException$MustNotDuplicateFieldName: The field name page cannot be defined more than once\n    com.liferay.portal.verify.VerifyException: com.liferay.dynamic.data.mapping.validator.DDMFormValidationException$MustNotDuplicateFieldName: The field name page cannot be defined more than once\n \nIf this is the case, roll back to your previous backup of Liferay Portal 6.2 and\nfind and remove duplicate field names. \n\n## Find and Remove Unused Objects\n\nIn the UI or using database queries, identify unused objects. Then remove them\nvia Liferay's UI or using Liferay's API through the \n[script console](/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console)\nor a portlet you create. \n\n| **Important**: You should only use Liferay's UI or API because they account \n| for relationships between @product@ objects. \n|\n| Never use SQL directly on your database to remove records. Your SQL might miss\n| object relationships, orphaning objects and causing performance problems.\n\nHere are some common places to check for unused objects.\n\n### Objects From the Large/Populated Tables\n\nTable rows are mapped to @product@ objects. Large tables with many records might\ncontain lots of unused objects. The greater the table size and the records per\ntable, the longer upgrading takes. \n\nFinding and removing unused objects associated with such tables reduces upgrade\ntimes. Your data import log (from the previous step) can provide valuable table\ninformation. Database engines show this information in different ways. Your\ndatabase import log might look like this:\n\n    Processing object type SCHEMA\\_EXPORT/TABLE/TABLE\\_DATA\n\n    imported \"LIFERAY\".\"JOURNALARTICLE\" 13.33 GB 126687 rows\n\n    imported \"LIFERAY\".\"RESOURCEPERMISSION\" 160.9 MB 1907698 rows\n\n    imported \"LIFERAY\".\"PORTLETPREFERENCES\" 78.13 MB 432285 rows\n\n    imported \"LIFERAY\".\"LAYOUT\" 52.05 MB 124507 rows\n\n    imported \"LIFERAY\".\"ASSETENTRY\" 29.11 MB 198809 rows\n\n    imported \"LIFERAY\".\"MBMESSAGE\" 24.80 MB 126185 rows\n\n    imported \"LIFERAY\".\"PORTALPREFERENCES\" 4.091 MB 62202 rows\n\n    imported \"LIFERAY\".\"USER\\_\" 17.32 MB 62214 rows\n    \n    ...\n\nSeveral items stand out in the example database import:\n\n-   The `JOURNALARTICLE` table makes up 98% of the database size.\n-   There are many `RESOURCEPERMISSION` records.\n-   There are many `PORTLETPREFERENCES` records.\n\nSearch for unused objects associated with the tables that stand out and use\nLiferay's API (e.g., the UI or\n[script console](/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console))\nto delete the objects. \n\n### Common Object Types Worth Checking \n\nSome object types should be checked for unused objects. Here are some reasons\nfor checking them:\n\n-   Removing them frees related unused objects for removal\n-   They're version objects that aren't worth keeping\n\nCheck these object types: \n\n-   **Sites**: Remove sites you don't need. When you remove a site,\n    remove its related objects:\n\n    -   Layouts\n\n    -   Portlet preferences\n\n    -   File entries (document library objects)\n\n    -   Asset Entries\n\n    -   Tags\n\n    -   Vocabularies and categories\n\n    -   Expando fields and their values\n\n    -   `ResourcePermission` objects\n\n    -   (and everything else)\n\n-   **Instances**: Unused instances are rare, but since they are the highest\n    object in the hierarchy, removing their objects can optimize upgrades\n    considerably:\n\n    -   Sites (and all their related content)\n\n    -   Users\n\n    -   Roles\n\n    -   Organizations\n\n    -   Global `ResourcePermission` objects\n\n-   **Intermediate web content versions:** @product@ generates a new web\n    content version after any modification (including translations). Consider\n    removing versions you don't need. Removing a Journal Article, for example,\n    also removes related objects such as image files (`JournalArticleImage`)\n    that are part of the content. Removing unneeded image files frees space in\n    your database and file system. For more details, see [Example: Removing\n    Intermediate Journal Article\n    Versions](/docs/7-2/deploy/-/knowledge_base/d/example-removing-intermediate-journal-article-versions).  \n\n-   **Document versions**: As with Journal Articles, if you don't need \n    intermediate document versions, delete them. This saves space both in the\n    database and on the file system, space that no longer needs to be upgraded. \n\n-   **Layouts:** Layouts are site pages, and they affect upgrade performance\n    because they relate to other entities such as portlet preferences,\n    permissions, assets, ratings, and more. Remove unneeded layouts. \n\n-   **Roles**: Remove any Roles you don't need. Deleting them also deletes\n    related `ResourceBlockPermission` and `ResourcePermission` objects.\n\n-   **Users:** If you have Users that aren't active anymore, remove them.\n\n-   **Vocabularies**: Remove any unused vocabularies. Note that removing a\n    vocabulary also removes its categories.\n\n-   **Orphaned data**: Check for unused objects that are not connected to\n    anything. Here are some examples:\n\n    -   `DLFileEntries` with no file system data.\n\n    -   `ResourcePermission` objects associated to a Role, Layout, User, portlet\n        instance, etc. that no longer exists.\n\n    -   `PortletPreference` objects associated with a portlet or layout that\n        no longer exists. This is common in environments with many embedded\n        portlets. These portlet instances have a different lifecycle and aren't\n        deleted when the portlet is removed from a template.\n\nIf you want to see an example of removing intermediate object versions, read\n[Example: Removing Intermediate Journal Article\nVersions](/docs/7-2/deploy/-/knowledge_base/d/example-removing-intermediate-journal-article-versions)\nand then return here. \n\nNext, you'll test @product@ with its pruned database. \n\n## Test with the Pruned Database Copy\n\nFind and resolve any issues related to the objects you removed. You can always\nrestart pruning a new copy of your production database if you can't resolve an\nissue. \n\n| **Warning:** the upgrade to @product@ 7.2 moves Web Content images to the \n| Document Library and then deletes their former table `JournalArticleImage`.\n| Make sure the images show in the upgraded Web Content articles. \n\nOnce you've successfully tested @product@ with its pruned database copy, you can\nupgrade the database to @product-ver@. \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/03-test-ugrading-a-liferay-backup-copy/03-example-removing-intermediate-journal-article-versions.markdown",
    "content": "---\nheader-id: example-removing-intermediate-journal-article-versions\n---\n\n# Example: Removing Intermediate Journal Article Versions\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/upgrade-stability-and-performance/example-removing-intermediate-journal-article-versions.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThese instructions and code samples demonstrate removing intermediate Journal\nArticle versions. In the \n[script console](/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console),\nyou can remove unneeded object versions by executing Java or Groovy\ncode. \n\nHere are example steps for removing intermediate Journal Article versions: \n\n1.  Decide how many of the latest versions to keep. You must keep the original\n    version and the most recent version, but you may keep older recent versions\n    too. For example, you may want to keep the two latest versions or just the\n    latest. \n\n2.  Find a method for deleting the entity versions. @product@ [app \n    APIs](@app-ref@/apps/) and [com.lifieray.portal.kernel\n    API](@platform-ref@/7.2-latest/javadocs/portal-kernel/) are available at\n    [@platform-ref@](@platform-ref@).\n\n    If it's a [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder) entity,\n    examine the `delete*` methods in the entity's `*LocalServiceUtil` class. For\n    example, this `deleteArticle` in\n    [`JournalArticleLocalServiceUtil`](@app-ref@/web-experience/latest/javadocs/com/liferay/journal/service/JournalArticleLocalServiceUtil.html#deleteArticle-long-java.lang.String-double-java.lang.String-com.liferay.portal.kernel.service.ServiceContext-)\n    deletes a Journal Article version:\n\n    ```java\n    deleteArticle(long groupId, java.lang.String articleId, double version, \n        java.lang.String articleURL, \n        com.liferay.portal.kernel.service.ServiceContext serviceContext)\n    ```\n\n3.  Aggregate the entity versions to delete and the information required to \n    delete them. For example, get all the Journal Article versions in range that\n    match your removal criteria and associate their entity IDs and group IDs\n    with them---the `deleteArticle` method requires the entity ID and group ID. \n\n    The entity object (e.g., `JournalArticle`) typically has a version field.\n    `JournalArticleResource` has each Journal Article's article ID (the entity's\n    ID) and group ID. `JournalArticleResource` is our key to getting each\n    `JournalArticle`, which can have multiple versions. Here are steps for\n    identifying the Journal Article versions to delete:\n\n    1.  Get all the `JournalArticleResource` objects. \n\n    ```java\n    List<JournalArticleResource> journalArticleResources = \n        JournalArticleLocalServiceUtil.getJournalArticleResources(start, end);\n    ```\n\n    2.  Get each Journal Article version's workflow status via the \n        `JournalArticle` object associated with each `JournalArticleResource`.\n        Dynamic Query is an efficient way to get exactly the data you want \n        (and nothing more) from each object.\n        \n        <!--Add back link for 'Dynamic Query' once dynamic-query article is available-->\n\n    ```java \n\tfor (JournalArticleResource\n\t\tjournalArticeResource : journalArticleResources) {\n\n\t\tList<Double> journalArticlesVersionsToDelete =\n\t\t\tnew ArrayList<Double>();\n\n        DynamicQuery dq =\n            DynamicQueryFactoryUtil.forClass(JournalArticle.class)\n                .setProjection(ProjectionFactoryUtil.projectionList()\n                    .add(ProjectionFactoryUtil.property(\"id\"))\n                    .add(ProjectionFactoryUtil.property(\"version\"))\n                    .add(ProjectionFactoryUtil.property(\"status\")))\n                .add(PropertyFactoryUtil.forName(\"groupId\")\n                    .eq(journalArticeResource.getGroupId()))\n                .add(PropertyFactoryUtil.forName(\"articleId\")\n                    .eq(journalArticeResource.getArticleId()))\n                .addOrder(OrderFactoryUtil.asc(\"version\"));\n\n        List<Object[]> result =\n            JournalArticleLocalServiceUtil.dynamicQuery(dq);\n\n        // See the next step for the sample code that goes here\n    }\n    ```\n\n    3.  For each `JournalArticleResource` (there's one for each Journal Article \n        entity), build a list of intermediate versions in range of the first or\n        latest versions you want to keep and whose status qualifies them for\n        deletion. For example, you may want to delete intermediate article\n        versions that are approved or expired (i.e.,\n        [WorkflowConstants.STATUS_APPROVED or\n        WorkflowConstants.STATUS_EXPIRED](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/workflow/WorkflowConstants.html)).\n        The `MIN_NUMBER_FIRST_VERSIONS_KEPT` and\n        `MIN_NUMBER_LATEST_VERSIONS_KEPT` variables here mark the minimum and\n        maximum number of first (oldest) and latest (newest) versions to keep. \n\n    ```java \n    List<Double> journalArticlesVersionsToDelete =\n\t\tnew ArrayList<Double>();\n\n\tfor (int i=0; i < result.size(); i++) {\n\t\tlong id = (long) result.get(i)[0];\n\t\tdouble version = (double) result.get(i)[1];\n\t\tint status = (int) result.get(i)[2];\n\n\t\tif ((status == WorkflowConstants.STATUS_APPROVED) || (status == WorkflowConstants.STATUS_EXPIRED) {\n\n\t\t\tif (i < MIN_NUMBER_FIRST_VERSIONS_KEPT) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (i >= (result.size() -\n\t\t\t\tMIN_NUMBER_LATEST_VERSIONS_KEPT)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tjournalArticlesVersionsToDelete.add(version);\n\t\t}\n\t}\n\n    // See the next step for the sample code that goes here\n    ```\n\n4.  Lastly, delete each Journal Article matching the versions you aggregated. \n\n    ```java\n    for (double version : journalArticlesVersionsToDelete) {\n    {\n        JournalArticleLocalServiceUtil.deleteArticle(journalArticeResource.getGroupId(),\n            journalArticeResource.getArticleId(), \n            journalArticlesVersionsToDelete(i), null, null);\n    }\n    ```\n\nYou can write similar code to remove intermediate versions of other entities. \n\n| **Tip:** Print the version (and any other information of interest) of each\n| object you're removing. You can also comment out the object deletion call and\n| read the printout of versions to be removed as a test before committing to\n| deleting them. \n\nAfter you've pruned your database, test it with @product@. \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/03-test-ugrading-a-liferay-backup-copy/04-upgrading-your-test-server-and-database.markdown",
    "content": "---\nheader-id: upgrading-your-test-server-and-database\n---\n\n# Upgrading Your Test Server and Database\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/upgrade-basics.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAfter you've [pruned your database and tested it\nsuccessfully](/docs/7-2/deploy/-/knowledge_base/d/pruning-the-database),\nit's ready for upgrade. Here you'll install @product-ver@ and migrate your\ncurrent installation files to it and upgrade them. Then you'll optimize your\ndatabase for the upgrade and upgrade your data. Lastly, you'll test this\nupgraded test environment. You may run into issues that require you to start\nagain with backup of your pruned database. After you're satisfied with the test\nupgrade, you can prepare for upgrading production. Start with preparing\n@product-ver@ on a test server. \n\n## Install Liferay on a Test Server and Configure It to Use the Pruned Database \n\n[Prepare a new test server with @product-ver@](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade). \nConfigure it to use the pruned database copy---keep the original backup in case\nyou want to restart test upgrades on a copy of it. You'll use the new test\nserver's Liferay upgrade tool next. \n\n## Tune Your Database for the Upgrade \n\n[Tune your database for the upgrade](/docs/7-2/deploy/-/knowledge_base/d/tuning-for-the-data-upgrade). \n\n## Upgrade the Database \n\nUpgrade the database to @product-ver@ (see\n[Upgrade the Database](/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-product-data));\nthen return here. \n\nIf the upgrade took too long, search the upgrade log to identify more unused\nobjects. Then retry these steps with a fresh copy of the production database. \n\n## Test the Upgraded Portal and Resolve Any Issues \n\nTest this upgraded @product-ver@ instance and resolve any issues. If you can't\nresolve an issue, retry these steps with a fresh copy of the production\ndatabase. \n\n## Checkpoint: You've Pruned and Upgraded a Production Database Copy \n\nBy removing unused objects from @product@ in your test environment, you've made\nupgrading feasible to do in production. You identified unused objects,\ndocumented/scripted removing them, and successfully upgraded the @product@\ndatabase copy. \n\nIt's time to prepare your production environment for upgrading. \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/04-preparing-to-upgrade-the-liferay-database.markdown",
    "content": "---\nheader-id: preparing-to-upgrade-the-product-database\n---\n\n# Preparing to Upgrade the @product@ Database\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/upgrade-basics.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAfter testing the upgrade on a copy of your production database, you can apply\nwhat you learned to your production database. \n\n| **Tip:** This step and\n| [preparing a new @product@ server](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade)\n| can be done in parallel to save time. \n\n## Remove All Unused Objects You Identified Earlier\n\nPreviously you identified and removed unused objects from a copy of your\n@product@ production database backup. In the same way (in the script console or\nUI) you removed the unused objects from the backup, remove them from your\npre-upgrade production database. \n\n## Test Using the Pruned Database \n\nFind and resolve any issues related to the objects you removed. By removing the\nobjects from production and testing your changes before upgrading, you can more\neasily troubleshoot issues, knowing that they're not related to upgrade\nprocesses. \n\n## Upgrade Your Marketplace Apps \n\nUpgrade each Marketplace app (Kaleo, Calendar, Notifications, etc.) that you're\nusing to its latest version for your @product@ installation. Before proceeding\nwith the upgrade, troubleshoot any issues regarding these apps.\n\n## Publish all Staged Changes to Production \n\nIf you have\n[local/remote staging enabled](/docs/7-2/user/-/knowledge_base/u/enabling-staging)\nand have content or data saved on the staged site, \n[publish](/docs/7-2/user/-/knowledge_base/u/publishing-staged-content-efficiently)\nit to the live site. If you skip this step, you must run a full publish (or\nmanually publish changes) after the upgrade, since the system won't know what\ncontent changed since the last publishing date.\n\n## Synchronize a Complete Backup \n\n[Completely back up your @product@ installation, pruned production database, and document repository](/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation). \n\nIt's time to prepare a new @product@ server. \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/05-preparing-a-new-liferay-server.markdown",
    "content": "---\nheader-id: preparing-a-new-product-server-for-data-upgrade\n---\n\n# Preparing a New @product@ Server for Data Upgrade\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/configuration-and-infrastructure/migrating-configurations-and-properties.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nTo upgrade your @product@ database, prepare a new server for hosting\n@product-ver@. You'll use this server to run the database upgrade and run\n@product-ver@. Then you can run your production server while you're configuring\na new server to host @product-ver@ exactly the way you want. \n\n| **Note:** these steps can be done in parallel with any of the upgrade \n| preparation steps: planning for deprecated apps, testing upgrades on a \n| @product@ backup copy, or preparing to upgrade the @product@ database. \n\nGet the latest fixes for @product-ver@ by requesting an upgrade patch. \n\n## Request an Upgrade Patch from Liferay Support (Liferay DXP only)\n\nAn *upgrade patch* contains the latest fix pack and hot fixes planned for the\nnext service pack. Upgrade patches provide the latest fixes available for your\ndata upgrade. \n\n## Install Liferay\n\n[Install @product@ on your application server](/docs/7-2/deploy/-/knowledge_base/d/deploying-product)\nor\n[use @product@ bundled with your application server of choice](/docs/7-2/deploy/-/knowledge_base/d/installing-product). \n\n| **Important:** Do not start your application server. It's not ready to start \n| until after the @product@ database upgrade. \n\n## Install the Latest Upgrade Patch or Fix Pack (Liferay DXP only)\n\nInstall the upgrade patch (if you requested it from Liferay Support) or the \n[latest Fix Pack](https://help.liferay.com/hc/en-us/articles/360028810452-Patching-Liferay-DXP). \n\n## Migrate Your OSGi Configurations (7.0+)\n\nCopy your\n[OSGi configuration files](/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files)\n(i.e., `.config` files) to your new server's `[Liferay Home]/osgi/configs`\nfolder. \n\n## Migrate Your Portal Properties \n\nIt is likely that you have overridden portal properties to customize your\ninstallation. If so, you must update the properties files (e.g.,\n`portal-setup-wizard.properties` and `portal-ext.properties`) to @product-ver@.\nFor features that use OSGi Config Admin, you must convert your properties to\nOSGi configurations. As you do this, you must account for property changes in\nall versions of @product@ since your current version up to and including\n@product-ver@. Start with updating your portal properties. \n\n### Update Your Portal Properties \n\nIf you're coming from a version prior to Liferay Portal 6.2, start with these\nproperty-related updates:\n\n-   If you're on Liferay Portal 6.1,\n    [adapt your properties to the new defaults that Liferay Portal 6.2 introduced](/docs/6-2/deploy/-/knowledge_base/d/upgrading-liferay#review-the-liferay-6). \n\n-   If you're on Liferay 6.0.12, \n    [migrate the Image Gallery](/docs/6-2/deploy/-/knowledge_base/d/upgrading-liferay#migrate-your-image-gallery-images).\n\n-   If you have a sharded environment,\n    [configure your upgrade to generate a non-sharded environment](/docs/7-2/deploy/-/knowledge_base/d/upgrading-a-sharded-environment).\n\n-   Liferay's image sprite framework is deprecated as of 7.2 and is disabled by \n    default. The framework requires scanning plugins for image sprites. If you\n    don't use the framework, there's no need for it to scan for images sprites.\n    If you use the framework, enable it by overriding the default\n    `sprite.enabled` portal property (new in 7.2) value with the following\n    setting in a\n    [`portal-ext.properties`](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\n    file: \n\n    ```properties\n    sprite.enabled=true\n    ```\n\n| **Note:** You can build image sprites using any framework you like and deploy\n| them in your plugins. \n\nWhen a new version of @product@ is released, there are often changes to default\nsettings, and this release is no different. If you rely on the defaults from\nyour old version, you should review the changes and decide to keep the defaults\nfrom your old version or accept the defaults of the new. \n\nBecause no existing properties changed from 7.1 to 7.2, here's a list of the 6.2\nproperties that have changed in 7.2: \n\n```properties\nusers.image.check.token=false\norganizations.types=regular-organization,location\norganizations.rootable[regular-organization]=true\norganizations.children.types[regular-organization]=regular-organization,location\norganizations.country.enabled[regular-organization]=false\norganizations.country.required[regular-organization]=false\norganizations.rootable[location]=false\norganizations.country.enabled[location]=true\norganizations.country.required[location]=true\nlayout.set.prototype.propagate.logo=true\neditor.wysiwyg.portal-web.docroot.html.taglib.ui.discussion.jsp=simple\nweb.server.servlet.check.image.gallery=true\nblogs.trackback.enabled=true\ndiscussion.comments.format=bbcode\ndiscussion.max.comments=0\ndl.file.entry.thumbnail.max.height=128\ndl.file.entry.thumbnail.max.width=128\n```\n\nThis property was removed:\n\n```properties\norganizations.children.types[location]\n```\n\nThe latest\n[portal properties reference](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html)\nprovides property details and examples. Some properties are replaced by OSGi\nconfigurations. \n\n### Convert Applicable Properties to OSGi Configurations \n\nProperties in modularized features have changed and must now be deployed\nseparately in\n[OSGi configuration files](/docs/7-2/user/-/knowledge_base/u/system-settings#exporting-and-importing-configurations) \n(OSGi Config Admin).\n\nUse the\n[`blade upgradeProps`](/docs/7-2/reference/-/knowledge_base/r/blade-cli)\ncommand to scan your `portal-ext.properties` file to discover which properties\nare now set via OSGi Config Admin. You can also check the upgrade log from\nprevious attempts for traces like these:\n\n```properties\n2019-03-09 17:05:17.678 ERROR [main][VerifyProperties:161] Portal property \"layout.first.pageable[link_to_layout]\" is obsolete\n2019-03-09 17:05:17.679 ERROR [main][VerifyProperties:136] Portal property \"journal.article.check.interval\" was modularized to com.liferay.journal.web as \"check.interval\"\n```\n\n| **Tip:** The Control Panel's *Configuration &rarr; System Settings* screens \n| are the most accurate way to create `.config` files. Use them to\n| [export a screen's configuration](/docs/7-2/user/-/knowledge_base/u/system-settings#exporting-and-importing-configurations)\n| to a `.config` file. \n\n## Update Your Database Driver \n\nInstall the recommended database driver and update your database connection\ndriver specified in your `portal-ext.properties`. See the [Database\nTemplates](/docs/7-2/deploy/-/knowledge_base/d/database-templates). \n\n## Configure Your Documents and Media File Store \n\nGeneral document store configuration (e.g., `dl.store.impl=[File Store Impl\nClass]`) continues to be done using `portal-ext.properties`. But here's what's\nchanged for document storage:\n\n-   Store implementation class package names changed from \n    `com.liferay.portlet.documentlibrary.store.*` in Liferay Portal 6.2 to\n    `com.liferay.portal.store.*` in @product@ 7.0+. Make sure your\n    `portal-ext.properties` file sets `dl.store.impl` in one of these ways:\n\n    ```properties\n    dl.store.impl=com.liferay.portal.store.file.system.FileSystemStore\n    dl.store.impl=com.liferay.portal.store.db.DBStore\n    dl.store.impl=com.liferay.portal.store.file.system.AdvancedFileSystemStore\n    dl.store.impl=com.liferay.portal.store.s3.S3Store\n    ```\n\n-   JCR Store was deprecated in @product@ 7.0. The\n    [Document Repository\n    Configuration](/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration)\n    documentation describes other store options. [Migrate to a supported\n    document store](/docs/7-2/user/-/knowledge_base/u/server-administration)\n    before upgrading your data. \n\n-   CMIS Store was deprecated since 7.0.10 Fix Pack 14 and was removed in \n    @product@ 7.2. The [Document Repository\n    Configuration](/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration)\n    documentation describes other store options. [Migrate to a supported\n    document store](/docs/7-2/user/-/knowledge_base/u/server-administration)\n    before upgrading your data. \n\n-   Since @product@ 7.0, document store type-specific configuration (e.g., \n    specific to Simple File Store, Advanced File Store, S3, etc.) is done in the\n    Control Panel at *Configuration &rarr; System Settings &rarr; File Storage*\n    or using OSGi configuration files (`.config` files). Type specific\n    configuration is no longer done using `portal-ext.properties`. \n\nFor example, these steps to create a `.config` file specifying a root file\nlocation for a Simple File Store or Advanced File Store:\n \n1.  Create a `.config` file named after your store implementation class.\n\n    Simple File Store: \n    `com.liferay.portal.store.file.system.configuration.FileSystemStoreConfiguration.config`\n\n    Advanced File Store:\n    `com.liferay.portal.store.file.system.configuration.AdvancedFileSystemStoreConfiguration.config`\n \n2.  Set the following `rootDir` property and replace \n    `{document_library_path}` with  your file store's path.\n\n    ```properties\n    rootDir=\"{document_library_path}\"\n    ```\n\n3.  Copy the `.config` file to your `[Liferay Home]/osgi/configs` folder.\n\nThe\n[Document Repository Configuration](/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration)\nprovides more document store configuration details. \n\n## Configure Kerberos in place of NTLM \n\nIf you're using NTLM to authenticate Microsoft Windows &trade; accounts with\n@product@, switch to using\n[Kerberos](/docs/7-2/deploy/-/knowledge_base/d/authenticating-with-kerberos).\nSecurity vulnerabilities persist with NTLM. NTLM has been deprecated and removed\nfrom the bundle, but you can still [build and deploy the module](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/portal-security-sso-ntlm).\n\n## Disable Indexing\n\nBefore starting the upgrade process in your new installation, you must disable\nindexing to prevent upgrade process performance issues that arise when the\nindexer attempts to re-index content. \n\nTo disable indexing, create a file called\n`com.liferay.portal.search.configuration.IndexStatusManagerConfiguration.config`\nin your `[Liferay Home]/osgi/configs` folder and add the following content: \n\n```properties\nindexReadOnly=\"true\"\n```\n\nAfter you complete the upgrade, re-enable indexing by removing the `.config`\nfile or setting `indexReadOnly=\"false\"`. \n\nYour new @product-ver@ server is ready for upgrading your database. \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/06-upgrading-the-liferay-database/01-upgrading-the-liferay-database-intro.markdown",
    "content": "---\nheader-id: upgrading-the-product-data\n---\n\n# Upgrading the @product@ Data\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/upgrade-basics.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nNow you're ready to upgrade the @product@ data. The upgrade processes update the\ndatabase schema for the core and your installed modules. Verification processes\ntest the upgrade. Configured verifications for the core and modules run\nafterwards, but can be run manually too. \n\nHere are the ways to upgrade:\n\n-   **Upgrade everything in one shot**:\n    Use the upgrade tool to upgrade the core and all the modules. \n\n-   **Upgrade the core and the modules separately**:\n    Use the upgrade tool (recommended) or\n    [Gogo shell](/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell) to upgrade the core. Then use Gogo shell to upgrade each module. \n\nIf you are upgrading from Liferay Portal 6.2 or earlier, use the upgrade tool to\nupgrade everything. It's the easiest, most comprehensive way to upgrade from\nthose versions. Since version 7.0, however, @product@'s modular framework lets\nyou upgrade modules---even the core---individually. A helpful practice for large\ndatabases is to focus first on upgrading the core and your most important\nmodules; then back up your database before continuing upgrades. Upgrading is\na flexible process that adjusts to your preferences.\n\n| **Note:** Liferay enterprise subscribers can use the upgrade tool to execute \n| upgrades for fix packs. Since @product@ 7.1, a fix pack's micro upgrade\n| processes in the core (database schema micro version changes) are not\n| mandatory. This means you can install a fix pack (i.e., core code) without\n| having to execute the database schema micro version changes. You can execute\n| micro version changes when you want, even outside of major or minor version\n| upgrades. Before using the upgrade tool to execute a fix pack's micro upgrade\n| process, however, you must shut down the server, install the fix pack, and \n| [back up the @product@ database, installation, and Document Library store](/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation). \n| \n| Module micro database schema version changes in fix packs execute\n| automatically on server startup unless the\n| [`autoUpgrade` setting](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-data-upgrade)\n| is `false` (the default is `true`). \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/06-upgrading-the-liferay-database/02-tuning-for-the-data-upgrade.markdown",
    "content": "---\nheader-id: tuning-for-the-data-upgrade\n---\n\n# Tuning for the Data Upgrade\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/upgrade-stability-and-performance/database-tuning-for-upgrades.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nUpgrading impacts the database differently from daily running in production.\nBecause of this, you should tune your database for the upgrade process before\nyou run it, and then re-apply your production settings after the upgrade\ncompletes. \n\n-   Data upgrades execute many more update statements (`INSERT`, `UPDATE`, and\n    `DELETE`) and less `SELECT` statements than production instances. When\n    upgrading, tune your database for executing updates. \n\n-   Data upgrades should be done in safe environments completely separate from \n    production servers and should use database backup copies. If upgrade errors\n    occur or you make mistakes, they don't impact production, and you can always\n    restart using your database backup copy. \n\nThe data upgrade tuning instructions given here are a starting point for tuning\nyour @product@ data upgrade. They account for data upgrade activities and a safe\ndata upgrade environment: \n\n-   Deactivate data integrity measures that impact performance. Restore the \n    backup if failures occur. \n\n-   Make commit-related transaction I/O operations asynchronous. \n\n-   Increase the interval to flush commits to disk. \n\n-   Minimize transaction logging.\n\n| **Note:** These options worked well for us on specific versions of each \n| database. Please consult your database vendor's documentation for \n| information on how to optimize executing updates on your specific database \n| version. \n\n| **Important:** Test your database configuration to determine tuning that's \n| best for your system, and consult your DBA as appropriate. **Never** use\n| database upgrade configurations in production. Always restore your production\n| database settings before starting your @product@ server for production use \n| with the database. \n\n| **Warning:** Some database properties and configurations are global and affect\n| schemas in the same database. \n\nThese configurations were optimal for upgrading data in a Liferay 6.2 EE\ninstallation that had these characteristics: \n\n-   3.2 GB database \n\n-   15 GB Document Library \n\n-   Content translated in 3 languages\n\n-   Record count for most populated entities:\n\n    -   1,694,000 rating entries\n    -   1,605,000 permissions (`ResourcePermission` objects)\n    -   871,000 assets (`AssetEntry` objects)\n    -   400,000 users \n    -   400,000 sites (`Group` objects)\n    -   402,000 images \n    -   259,000 message forum threads and posts \n    -   200,000 documents \n    -   193,000 portlet preferences \n    -   103,000 web content pieces (`JournalArticle` objects) \n    -   50,600 pages \n    -   3,276 journal article images \n    -   3,100 document folders \n\nStart with configuring the database upgrade tool's Java process. \n\n## Tuning the Database Upgrade Java Process\n\nMake sure to provide adequate memory for the database upgrade tool's Java\nprocess. 15GB was appropriate for the test scenario. Also make sure to set the\nfile encoding to UTF-8 and the time zone to GMT. Here are the Java process\nsettings:\n\n-   Xmx 15 GB RAM \n-   File encoding UTF-8 \n-   User time zone GMT\n\nHere is the `db_upgrade.sh` command:\n\n```bash\ndb_upgrade.sh -j \"-Xmx15000m -Dfile.encoding=UTF-8 -Duser.timezone=GMT\"\n```\n\nIt's time to tune your database transaction engine. \n\n## Tuning the Database Transaction Engine for Executing Updates \n\nMany more update statements are executed during data upgrade than in production.\nHere's how to optimize each database's transaction engine for the updates. \n\n### IBM DB2 \n\nPlease consult IBM's official DB2 documentation. \n\n### MariaDB\n\nIn addition to the default database configuration, turn off InnoDB double-write.\n\n### Microsoft SQL Server \n\nIn addition to the default database configuration, set\n[transaction durability](https://docs.microsoft.com/en-us/sql/relational-databases/logs/control-transaction-durability)\nto `FORCED`. \n\n### MySQL \n\nIn addition to the default database configuration, turn off [InnoDB\ndouble-write](https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_doublewrite). \n\n### Oracle Database \n\nThe default configuration works well. It configures [asynchronous I/O to\ndisk](https://docs.oracle.com/database/121/REFRN/GUID-FD8D1BD2-0F85-4844-ABE7-57B4F77D1608.htm#REFRN10048)\nautomatically. \n\n### PostgreSQL \n\nIn addition to the default database configuration, turn off [synchronous\ncommits](https://www.postgresql.org/docs/10/wal-async-commit.html). \n\n## Tuning the Database Transaction Log\n\nIn production, transaction logs mark safe states to roll back to. In data\nupgrades, however, the safe state is the original data backup. Since transaction\nlogging is insignificant for data upgrades, it should be disabled or minimized.\nHere are log tuning instructions for each database. \n\n### IBM DB2 \n\nPlease consult IBM's official DB2 documentation. \n\n### MariaDB\n\nIn addition to the default database configuration, set the InnoDB flush log at\ntransaction commit to `0`. \n\n### Microsoft SQL Server \n\nUse the default database configuration. \n\n### MySQL \n\nIn addition to the default database configuration, set the [InnoDB flush log at\ntransaction\ncommit](https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit)\nto `0`. \n\n### Oracle Database \n\nUse the default database configuration. \n\n### PostgreSQL \n\nIn addition to the default database configuration, Set the [write ahead log\nwriter delay](https://www.postgresql.org/docs/10/wal-async-commit.html) to\n`1000` milliseconds. \n\nCongratulations! You have a starting point to plan your own @product@ data\nupgrade project. Remember, optimal tuning depends on your data, infrastructure\nconditions, and database vendor. Analyze your data, tune for upgrade, and time\nyour test upgrades. Use this information to determine the best database and Java\nprocess configuration for your @product@ data upgrade. \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/06-upgrading-the-liferay-database/03-configuring-the-data-upgrade.markdown",
    "content": "---\nheader-id: configuring-the-data-upgrade\n---\n\n# Configuring the Data Upgrade\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/reference/database-upgrade-tool-reference.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe upgrade tool provides the easiest way to upgrade the core and installed\nmodules. You can use text files or the tool's command line interface to\nconfigure your upgrade. The upgrade tool can upgrade everything---the core\nand all the modules---together or separately.\n\n@product-ver@ bundles include the upgrade tool. If you installed @product-ver@\nmanually, you can download the upgrade tool separately.\n\n-   *Liferay DXP 7.2*: Go to the\n    [*Downloads* page](https://customer.liferay.com/group/customer/downloads)\n    and select the *DXP 7.2* product and the *Product/Service Packs* file type.\n    In the listing that appears, click *Download* for the *Liferay DXP Upgrade\n    Client*.\n\n-   *Liferay Portal CE 7.2*: Go to the\n    [*Downloads* page](https://www.liferay.com/downloads-community)\n    and select *Download* for *Liferay Portal Tools for 7.2*.\n\nBefore starting the data upgrade process, configure the upgrade tool for the\ncore upgrade and specify whether the upgrade tool should upgrade non-core module\ndata automatically.\n\n## Configuring the Core Upgrade\n\nThe core upgrade requires configuration. You can configure it at runtime via the\ncommand line interface or pre-configure it in these files in `[Liferay\nHome]/tools/portal-tools-db-upgrade-client/`:\n\n-   `app-server.properties`: Specifies the server's location and libraries.\n-   `portal-upgrade-database.properties`: Configures the database connection.\n-   `portal-upgrade-ext.properties`: Sets the rest of the portal properties that\n    the upgrade requires. You might want to copy your current portal properties\n    (except your database properties) into this file. Before copying your\n    current properties, make sure you've\n    [updated the portal properties for @product-ver@](/docs/7-2/deploy/-/knowledge_base/d/preparing-to-upgrade-the-product-database).\n\nEach file's properties are described next.\n\n### Configuring app-server.properties\n\nSpecify the following information to configure @product-ver@'s app server:\n\n`dir:` the absolute path of the application server folder. *(required)*\n\n`extra.lib.dirs:` a comma delimited list of extra directories containing any\nbinaries or resources to add to the class path. Use all absolute paths OR all\npaths relative to `dir`. *(required)*\n\n`global.lib.dir:` the application server's global library directory. Use\nthe absolute path or a path relative to `dir`. *(required)*\n\n`portal.dir:` the directory where portal is installed in your app server. Use\nthe absolute path or a path relative to `dir`. *(required)*\n\n`server.detector.server.id:` ID of a supported application server.\n(*required*) Here are the IDs:\n\n- `jboss`\n- `jonas`\n- `resin`\n- `tomcat`\n- `weblogic`\n- `websphere`\n- `wildfly`\n\nRelative paths must use Unix style format. The following properties, for\nexample, are for Windows and use relative paths:\n\n```properties\ndir=D:\\\nextra.lib.dirs=Liferay/liferay-portal-master/tomcat-9.0.10/bin\nglobal.lib.dir=Liferay/liferay-portal-master/tomcat-9.0.10/lib\nportal.dir=Liferay/liferay-portal-master/tomcat-9.0.10/webapps/ROOT\nserver.detector.server.id=tomcat\n```\n\nThese properties, for example, are for Linux and use all absolute paths:\n\n```properties\ndir=/\nextra.lib.dirs=/home/user/liferay/liferay-portal-master/tomcat-9.0.10/bin\nglobal.lib.dir=/home/user/liferay/liferay-portal-master/tomcat-9.0.10/lib\nportal.dir=/home/user/liferay/liferay-portal-master/tomcat-9.0.10/webapps/ROOT\nserver.detector.server.id=tomcat\n```\n\n### Configuring portal-upgrade-database.properties\n\nSpecify the following information to configure the database you're upgrading.\nNote that these properties correspond exactly to the\n[JDBC portal properties](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#JDBC)\nyou'd use in a `portal-ext.properties` file.\n\n`jdbc.default.driverClassName` *(required)*\n\n`jdbc.default.url` *(required)*\n\n`jdbc.default.username` *(required)*\n\n`jdbc.default.password` *(required)*\n\n### Configuring portal-upgrade-ext.properties\n\nSpecify the following information to configure the upgrade:\n\n`liferay.home:` The [Liferay home folder](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) *(required)*\n\n`dl.store.impl:` The implementation for persisting documents to the document\nlibrary store. This property is mandatory if you're using a `*FileSystemStore`\nimplementation. If you\n[updated this property in your `portal-ext.properties`](/docs/7-2/deploy/-/knowledge_base/d/preparing-a-new-product-server-for-data-upgrade),\ncopy it here. Otherwise, set the property one of these ways:\n\n```properties\ndl.store.impl=com.liferay.portal.store.file.system.FileSystemStore\ndl.store.impl=com.liferay.portal.store.db.DBStore\ndl.store.impl=com.liferay.portal.store.file.system.AdvancedFileSystemStore\ndl.store.impl=com.liferay.portal.store.s3.S3Store\n```\n\n`hibernate.jdbc.batch_size:` The JDBC batch size used to improve performance;\nset to *250* by default *(optional)*\n\n### Example Upgrade Configuration\n\nHere's an example interaction with the upgrade tool's command line interface:\n\n    Please enter your application server (tomcat):\n    tomcat\n    Please enter your application server directory (../../tomcat-8.0.32):\n\n    Please enter your extra library directories (../../tomcat-8.0.32/bin):\n\n    Please enter your global library directory (../../tomcat-8.0.32/lib):\n\n    Please enter your portal directory (../../tomcat-8.0.32/webapps/ROOT):\n\n    [ db2 mariadb mysql oracle postgresql sqlserver sybase ]\n    Please enter your database (mysql):\n    mariadb\n    Please enter your database host (localhost):\n\n    (etc.)\n\nThe command line interface creates the configuration files based on your input.\nYou can put this information into configuration files to configure the tool\nmanually.\n\nHere are example upgrade configuration files that you can customize and copy\ninto `[Liferay Home]/tools/portal-tools-db-upgrade-client/`:\n\n-   `app-server.properties`:\n\n    ```properties\n\tdir=../../tomcat-8.0.32\n\tglobal.lib.dir=/lib\n\tportal.dir=/webapps/ROOT\n\tserver.detector.server.id=tomcat\n\textra.lib.dirs=/bin\n    ```\n\n-   `portal-upgrade-database.properties`:\n\n    ```properties\n    jdbc.default.url=jdbc:mysql://lportal62?characterEncoding=UTF-8&dontTrackOpenResources=true&holdResultsOpenOverStatementClose=true&serverTimezone=GMT&useFastDateParsing=false&useUnicode=true\n    jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n    jdbc.default.username=root\n    jdbc.default.password=\n    ```\n\n-   `portal-upgrade-ext.properties`:\n\n    ```properties\n    liferay.home=/home/user/servers/liferay7\n    module.framework.base.dir=/home/user/servers/liferay7/osgi\n    dl.store.impl=com.liferay.portal.store.file.system.FileSystemStore\n    ```\n\nNext, decide if the upgrade tool should upgrade non-core modules automatically.\n\n## Configuring Non-Core Module Data Upgrades\n\nYou can configure the upgrade tool to upgrade all installed modules\nautomatically or to open a Gogo shell (after core upgrade completes) for you to\nexecute module upgrades manually.\n\nIf the upgrade tool's `autoUpgrade` property is set to `true` (the default\nsetting), upgrade processes for all installed modules are run too.\n\nIf you set `autoUpgrade=\"false\"` in a file called\n`com.liferay.portal.upgrade.internal.configuration.ReleaseManagerConfiguration.config`\nand copy the file into the `[Liferay Home]/osgi/configs` folder, the upgrade\ntool opens Gogo shell after the core upgrade. In the Gogo shell, you can\n[administer module upgrades](/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell).\n\nIt's time to run the upgrade tool.\n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/06-upgrading-the-liferay-database/04-upgrading-the-core-using-the-upgrade-tool.markdown",
    "content": "---\nheader-id: upgrading-the-core-using-the-upgrade-tool\n---\n\n# Upgrading the Core Using the Upgrade Tool\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/upgrade-basics/using-the-database-upgrade-tool.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe upgrade tool provides the easiest way to upgrade the core and installed\nmodules. Here's how to use it. \n\n## Upgrade Tool Usage\n\nThe `db_upgrade.sh` script in the  `[Liferay Home]/tools/portal-tools-db-upgrade-client`\nfolder (`db_upgrade.bat` on Windows) invokes the upgrade tool. \n\nThis command prints the upgrade tool usage: \n\n    db_upgrade.sh --help\n\nThis configuration prevents automatic module upgrade, but causes the upgrade\ntool to open a Gogo shell for\n[upgrading modules](/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell)\nafter finishing the core upgrade. \n \nHere are the tool's default Java parameters:\n\n    -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.timezone=GMT -Xmx2048m \n\nThe `-j` option overrides the JVM parameters. For example, these options set the\nJVM memory to 10GB, which is a good starting point for this process type:\n\n    db_upgrade.sh -j \"-Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.timezone=GMT -Xmx10240m\"\n\nThe `-l` option specifies the tool's log file name: \n\n    db_upgrade.sh -l \"output.log\"\n\nHere are all the upgrade tool command line options:\n\n**--help** or **-h**: Prints the tool's help message.\n\n**--jvm-opts** or **-j** + **[arg]**: Sets any JVM options for the upgrade \nprocess.\n\n**--log-file** or **-l** + **[arg]**: Specifies the tool's log file name---the \ndefault name is `upgrade.log`.\n\n**--shell** or **-s**: Automatically connects you to the Gogo shell after\nfinishing the upgrade process.\n\n| **Note:** Only execute the upgrade process on a server with ideal memory, CPU,\n| and database connection configurations. If executing an upgrade remotely using\n| `ssh`, make sure to guard against interruptions: \n| \n| -   If you're executing the upgrade using `ssh`, ignore hangups (connection\n|     loss) by using `nohup` or something similar. \n| -   On the machine you're connecting from, disable settings that shutdown or \n|     sleep that machine. \n| \n| The upgrade process continues on the server even if you lose connection to it.\n| If you lose connection, reconnect and monitor upgrade status via the log\n| (default log file is `upgrade.log`). If you're using an earlier version of\n| @product-ver@ and upgrade execution is interrupted, check your log file for\n| where execution stopped. \n| \n| -   If execution stopped during an upgrade process for Core 7.1 or higher, or \n|     any module upgrade process, restart the upgrade tool to continue the \n|     upgrade from that point. You can also use Gogo shell to\n|     [check module upgrade status](/docs/7-2/deploy/-/knowledge_base/d/upgrading-modules-using-gogo-shell#checking-upgrade-status)\n|     and continue upgrading modules. \n| -   If execution stopped during an upgrade process for Core 7.0 or lower, you \n|     must\n|     [restore the data from a backup](/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation)\n|     and start the upgrade again. \n\n| **Warning:** To prevent the tool's expanded command from growing too large for\n| Windows, execute the upgrade tool script from the `[Liferay\n| Home]/tools/portal-tools-db-upgrade-client` folder.\n\nIt's time to upgrade your core data using the upgrade tool. \n\n## Running and Managing the Core Upgrade\n\nStart the upgrade tool, as the previous section explains. Here are the core\nupgrade stages:\n\n1.  Show the upgrade patch level\n\n2.  Execute the core upgrade processes\n\n3.  Execute the core verifiers\n\nMonitor the upgrade via the upgrade tool log file (default file is\n`upgrade.log`). If a core upgrade process fails, analyze the failure and resolve\nit. If a core upgrade step for @product@ 7.1 (or newer) fails, executing the\nupgrade tool again starts it from that step. \n\nIf you configured the upgrade tool to upgrade non-core modules, the tool opens a\nGogo shell and starts upgrading them. The Gogo shell lets you upgrade modules,\ncheck module upgrade status, verify upgrades, and restart module upgrades. Read\non to learn how to use Gogo shell commands to complete @product@ upgrades. \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/06-upgrading-the-liferay-database/05-upgrading-modules-using-gogo-shell.markdown",
    "content": "---\nheader-id: upgrading-modules-using-gogo-shell\n---\n\n# Upgrading Modules Using Gogo Shell\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/upgrade-stability-and-performance/upgrading-modules-using-gogo-shell.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay's Gogo shell can upgrade and verify individual modules. It's\na fine-grained approach to upgrading the core and non-core modules. If you\nhaven't already upgraded your non-core modules using the upgrade tool or if\nthere are modules you need to revisit upgrading, you can upgrade them using\nGogo Shell. \n\n| **Note**: You must\n| [Configure the core upgrade](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-data-upgrade)\n| before using Gogo shell commands to upgrade the core. \n\nBelow is a list of commands.\n\n## Command Usage\n\nIf you ran the upgrade tool and it opened Gogo shell, you're already connected.\nOtherwise, you can execute commands using the\n[Gogo Shell\nportlet](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell).\n\nHere are the commands:\n\n`exit` or `quit:` Exits the Gogo shell\n\n`upgrade:help:` Displays upgrade commands\n\n`upgrade:check:` Lists upgrades pending execution because they failed in \nthe past or the module hasn't reached its final version\n\n`upgrade:execute {module_name}:` Executes upgrades for that module\n\n`upgrade:executeAll:` Executes all pending module upgrade processes\n\n`upgrade:list:` Lists all registered upgrades\n\n`upgrade:list {module_name}:` Lists the module's required upgrade steps\n\n`upgrade:list | grep Registered:` Lists registered upgrades and their versions\n\n`verify:help:` Displays verify commands\n\n`verify:check {module_name}:` Lists the latest execution result for the\nmodule's verify process\n\n`verify:checkAll:` Lists the latest execution results for all verify processes\n\n`verify:execute {module_name}:` Executes the module's verifier\n\n`verify:executeAll:` Executes all verifiers\n\n`verify:list:` Lists all registered verifiers\n\nThere are many useful [Liferay commands and standard commands available in Gogo\nshell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell).\nThe following sections describe Liferay upgrade commands. \n\n## Listing module upgrade processes\n\nBefore upgrading modules, you should find which have unresolved dependencies,\nwhich are resolved and available to upgrade, and examine the module upgrade\nprocesses. \n\nExecuting `upgrade:list` in the Gogo shell lists the modules whose upgrade\ndependencies are satisfied. These modules can be upgraded. \n\nIf a module is active but not listed, its dependencies must be upgraded. The\nGogo shell command `scr:info [upgrade_step_class_qualified_name]` shows the\nupgrade step class's unsatisfied dependencies. Here's an example `scr:info`\ncommand:\n\n    scr:info com.liferay.journal.upgrade.JournalServiceUpgrade\n\nInvoking `upgrade:list [module_name]` lists the module's upgrade processes, in\nno particular order.  For example, executing `upgrade:list\ncom.liferay.bookmarks.service` (for the Bookmarks Service module), lists this:\n\n    Registered upgrade processes for com.liferay.bookmarks.service 1.0.0\n            {fromSchemaVersionString=0.0.0, toSchemaVersionString=1.0.0, upgradeStep=com.liferay.portal.spring.extender.internal.context.ModuleApplicationContextExtender$ModuleApplicationContextExtension$1@6e9691da}\n            {fromSchemaVersionString=0.0.1, toSchemaVersionString=1.0.0-step-3, upgradeStep=com.liferay.bookmarks.upgrade.v1_0_0.UpgradePortletId@5f41b7ee}\n            {fromSchemaVersionString=1.0.0-step-1, toSchemaVersionString=1.0.0, upgradeStep=com.liferay.bookmarks.upgrade.v1_0_0.UpgradePortletSettings@53929b1d}\n            {fromSchemaVersionString=1.0.0-step-2, toSchemaVersionString=1.0.0-step-1, upgradeStep=com.liferay.bookmarks.upgrade.v1_0_0.UpgradeLastPublishDate@3e05b7c8}\n            {fromSchemaVersionString=1.0.0-step-3, toSchemaVersionString=1.0.0-step-2, upgradeStep=com.liferay.bookmarks.upgrade.v1_0_0.UpgradeClassNames@6964cb47}\n\nAn application's upgrade step class names typically reveal their intention. For\nexample, the example's `com.liferay.bookmarks.upgrade.v1_0_0.UpgradePortletId`\nupgrade step class updates the app's portlet ID. The other example upgrade step\nclasses update class names, the `LastPublishDate`, and `PortletSettings`.  The\nexample's step from `0.0.0` to `1.0.0` upgrades the module from an empty\ndatabase.\n\nTo examine a module's upgrade process better, you can sort the listed upgrade\nsteps mentally or in a text editor. Here's the upgrade step order for a\nBookmarks Service module to be upgraded from Liferay Portal 6.2 (the module's\ndatabase exists) to schema version `1.0.0`: \n\n-   `0.0.1` to `1.0.0-step-3`\n-   `0.0.1-step-3` to `1.0.0-step-2`\n-   `0.0.1-step-2` to `1.0.0-step-1`\n-   `0.0.1-step-1` to `1.0.0`\n\nThe overall module upgrade process starts at version `0.0.1` and finishes at version\n`1.0.0`. The first step starts on the initial version (`0.0.1`) and finishes on\nthe target version's highest step (`step-3`). The last step starts on the target\nversion's lowest step (`step-1`) and finishes on the target version (`1.0.0`). \n\nOnce you understand the module's upgrade process, you can execute it with\nconfidence. \n\n## Executing module upgrades\n\nExecuting `upgrade:execute [module_name]` upgrades the module. You might run\ninto upgrade errors that you must resolve. Executing the command again starts\nthe upgrade from the last successful step. \n\nYou can check upgrade status by executing `upgrade:list [module_name]`. For\nexample, entering `upgrade:list com.liferay.iframe.web` outputs this:\n\n    Registered upgrade processes for com.liferay.iframe.web 0.0.1\n\t   {fromSchemaVersionString=0.0.1, toSchemaVersionString=1.0.0, upgradeStep=com.liferay.iframe.web.upgrade.IFrameWebUpgrade$1@1537752d}\n\nThe first line lists the module's name and current version. The example module's\ncurrent version is `0.0.1`. The `toSchemaVersionString` value is the target\nversion. \n\nExecuting `upgrade:list [module_name]` on the module after successfully\nupgrading it shows the module's name followed by the version you targeted. \n\nFor example, if you successfully upgraded `com.liferay.iframe.web` to version\n`1.0.0`, executing `upgrade:list com.liferay.iframe.web` shows the module's\nversion is `1.0.0`:\n\n    Registered upgrade processes for com.liferay.iframe.web 1.0.0\n\t   {fromSchemaVersionString=0.0.1, toSchemaVersionString=1.0.0, upgradeStep=com.liferay.iframe.web.upgrade.IFrameWebUpgrade$1@1537752d}\n\nFor module upgrades that don't complete, you can check their status and resolve\ntheir issues. \n\n## Checking upgrade status\n\nThe command `upgrade:check` lists modules that have impending upgrades. \n\nFor example, if module  `com.liferay.dynamic.data.mapping.service` failed in a\nstep labeled `1.0.0-step-2`, executing `upgrade:check` shows this: \n\n    Would upgrade com.liferay.dynamic.data.mapping.service from 1.0.0-step-2 to\n    1.0.0 and its dependent modules\n\nModules often depend on other modules to complete upgrading. Executing `scr:info\n[upgrade_step_class_qualified_name]` shows the upgrade step class's\ndependencies. You must upgrade modules on which your module depends. \n\nTo resolve and activate a module, its upgrade must complete. The\n[Apache Felix Dependency Manager](http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager/tutorials/leveraging-the-shell.html)\nGogo shell command `dm wtf` reveals unresolved dependencies. If your module\nrequires a certain data schema version (e.g., its `bnd.bnd` specifies\n`Liferay-Require-SchemaVersion: 1.0.2`) but the module hasn't completed upgrade\nto that version, `dm wtf` shows that the schema version is not registered. \n\n    1 missing dependencies found.\n    -------------------------------------\n    The following service(s) are missing:\n     * com.liferay.portal.kernel.model.Release (&(release.bundle.symbolic.name=com.liferay.journal.service)(release.schema.version=1.0.2)) is not found in the service registry\n\nThe `dm wtf` command can also help detect errors in portlet definitions and\ncustom portlet `schemaVersion` fields. \n\nBrowsing the @product@ database `Release_` table can help you determine a\nmodule's upgrade status too. The core's `servletContextName` field value is\n`portal`. If the core's `schemaVersion` field matches your new @product@ version\n(e.g., `7.2.1` for Liferay Portal CE GA2) and the `verified` field is `1`\n(true), the core upgrade completed successfully. \n\nEach module has one `Release_` table record, and the value for its\n`schemaVersion` field must be `1.0.0` or greater (`1.0.0` is the initial version\nfor @product-ver@ modules, except for those that were previously traditional\nplugins intended for Liferay Portal version 6.2 or earlier). \n\n## Executing verify processes\n\nSome modules have verify processes. These make sure the upgrade executed\nsuccessfully. Verify processes in the core are automatically executed after\nupgrading @product@. You can also execute them by configuring the\n[`verify.*` portal properties](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Verify)\nand restarting your server.\n\nTo check for available verify processes, enter the Gogo shell command\n`verify:list`. To run a verify process, enter `verify:execute\n[verify_qualified_name]`. \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/07-executing-post-upgrade-tasks.markdown",
    "content": "---\nheader-id: executing-post-upgrade-tasks\n---\n\n# Executing Post-Upgrade Tasks\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/upgrade-basics/post-upgrade-considerations.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nSince you optimized your system for upgrading, after the upgrade is complete\nyou must re-optimize it for production. \n\n## Tuning Your Database for Production \n\nPrior to upgrading your @product@ database, you tuned it for upgrade. Now that\nupgrade is complete, restore the production database tuning you used previously.\n\n## Re-enabling Search Indexing and Re-indexing Search Indexes\n\nMake sure to re-enable search indexing by removing the\n`com.liferay.portal.search.configuration.IndexStatusManagerConfiguration.config`\nfile from your `[Liferay Home]/osgi/configs` folder or setting this property in\nit: \n\n```properties\nindexReadOnly=\"false\"\n```\n\nThen re-index @product@'s search indexes. Don't just do this blindly,\nhowever. By default, @product@ ships with an embedded configuration for\nElasticsearch. This configuration works great for demo purposes, but is not\nsupported in production. Make sure to\n[install and configure a standalone Elasticsearch instance to run in production](/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch).\n\n## Enabling Web Content View Permissions\n\nPrior to @product@ 7.1, all users could view web content articles by default.\nNow view permissions are checked by default. Here are options for opening view\npermissions:\n\nOption 1: Edit view permissions per web content article per role. \n\nOption 2: Open view permissions for all web content articles by going to\n*System Settings &rarr; Web Experience &rarr; Web Content* and deselecting\n*Article view permissions check enabled*. \n\nOnce you've configured search, re-indexed your search index, and set web content\nview permissions, your upgraded system is ready for action! Congratulations! \n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/08-upgrading-a-sharded-environment.markdown",
    "content": "---\nheader-id: upgrading-a-sharded-environment\n---\n\n# Upgrading a Sharded Environment\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/other-upgrade-scenarios/upgrading-a-sharded-environment.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nSince Liferay DXP 7.0, Liferay removed its own physical partitioning implementation (also known as sharding) in favor of the capabilities provided natively by database vendors. Upgrading a sharded installation to DXP 7.0 or higher requires migrating it to as many non-sharded Liferay DXP installations (servers) as you have shards. These steps guide you through configuring the new Liferay DXP servers to use your formerly sharded data.\n\n| **Note:** Liferay continues to support its logical partitioning capabilities\n| (also known as\n| [virtual instances](/docs/7-2/user/-/knowledge_base/u/setting-up-a-virtual-instance))\n| for the foreseeable future.\n\n| For any further assistance with sharding contact your Liferay account manager\n| or Liferay Support.\n\n## Add Configurations Before the Data Upgrade\n\nIn addition to other configurations, you will need to set more properties to migrate your shards to virtual instances for your data upgrade.\n\nHere is how to configure the upgrade to migrate from sharding:\n\n1. Copy all of the shard JDBC connection properties from `portal-ext.properties` to`portal-upgrade-database.properties`. For example, JDBC connections for a default shard and two non-default shards might look like this:\n\n    ```properties\n    jdbc.default.driverClassName=[the database driver class name]\n    jdbc.default.url=[the URL to the default database shard]\n    jdbc.default.username=[the user name]\n    jdbc.default.password=[the password]\n\n    jdbc.one.driverClassName=[the database driver class name]\n    jdbc.one.url=[the URL to database shard one]\n    jdbc.one.username=[the user name]\n    jdbc.one.password=[the password]\n\n    jdbc.two.driverClassName=[the database driver class name]\n    jdbc.two.url=[the URL to database shard two]\n    jdbc.two.username=[the user name]\n    jdbc.two.password=[the password]\n    ```\n\n1. Set the JDBC _default_ connection properties in each server's `portal-upgrade-database.properties` to specify the associated shard.\n\n    * Add the original JDBC properties for the respective non-default shard database. For example, shard `one`'s original properties might start with `jdbc.one`:\n\n    ```properties\n    jdbc.one.driverClassName=[the database driver class name]\n    jdbc.one.url=[the URL to database shard one]\n    jdbc.one.username=[the user name]\n    jdbc.one.password=[the password]\n    ```\n\n    * Rename the properties to start with `jdbc.default`. For example:\n\n    ```properties\n    jdbc.default.driverClassName=[the database driver class name]\n    jdbc.default.url=[the URL to database shard one]\n    jdbc.default.username=[the user name]\n    jdbc.default.password=[the password]\n    ```\n\n## Upgrade and Update Properties\n\nWhen you perform the database upgrade, upgrade the default shard first, and then each of the non-default shards. See [Using the Database Upgrade Tool](/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-product-data) for more information on running the database upgrade.\n\nAfter the database upgrade has been completed, make the following configuration changes to your application servers:\n\n1. In each server's `portal-ext.properties`, use the JDBC _default_ properties you specified in the `portal-upgrade-database.properties` (see the _default_ properties above).\n\n1. Remove the non-default shard JDBC properties from the default shard server's `portal-ext.properties` file, leaving only the default shard database `jdbc.default` properties. For example:\n\n    Old JDBC properties:\n\n    ```properties\n    jdbc.default.driverClassName=[the database driver class name]\n    jdbc.default.url=[the URL to the default database shard]\n    jdbc.default.username=[the user name]\n    jdbc.default.password=[the password]\n\n    jdbc.one.driverClassName=[the database driver class name]\n    jdbc.one.url=[the URL to database shard one]\n    jdbc.one.username=[the user name]\n    jdbc.one.password=[the password]\n\n    jdbc.two.driverClassName=[the database driver class name]\n    jdbc.two.url=[the URL to database shard two]\n    jdbc.two.username=[the user name]\n    jdbc.two.password=v[the password]\n    ```\n\n    New JDBC properties:\n\n    ```properties\n    jdbc.default.driverClassName=[the database driver class name]\n    jdbc.default.url=[the URL to your database]\n    jdbc.default.username=[the user name]\n    jdbc.default.password=[the password]\n    ```\n\nOnce you have completed all of these steps, you have migrated off of a sharded environment to virtual instances on separate Liferay DXP servers together with your DXP upgrade.\n\nCongratulations! You have migrated off of a sharded environment to virtual\ninstances on separate @product@ servers. You have also upgraded to\n@product-ver@. Your virtual instances are ready for action.\n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/09-migrating-from-audience-targeting/01-migrating-from-audience-targeting-intro.markdown",
    "content": "---\nheader-id: migrating-from-audience-targeting-to-segmentation-and-personalization\n---\n\n# Migrating From Audience Targeting to Segmentation and Personalization\n\n[TOC levels=1-4]\n\n@product-ver@ integrates all the Audience Targeting app's features into\nLiferay's core as Segmentation and Personalization. This enables better\nintegration with other applications and provides developers with easier access\nto Segmentation and Personalization features. Audience Targeting users must\nmigrate their user segments into the new Segments application. There are three\nsteps to the migration process:\n\n1.  Upgrade to @product-ver@.\n\n2.  Migrate custom rules.\n\n3.  Migrate behavior-based features.\n\nFirst, to upgrade to the latest version of @product@, follow the\n[upgrade guide](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver).\nMost of your Audience Targeting configuration is automatically transferred\ninto the new engine.\n\nNext, any custom rules that were created in Audience Targeting must be\nre-evaluated. Some custom rules may have an out-of-the-box equivalent now, while\nothers must be migrated. If a rule must be re-implemented, follow the\n[Segmentation and Personalization development guide](/docs/7-2/frameworks/-/knowledge_base/f/segmentation-personalization).\nYou can check\n[the list of rules that are automatically migrated](/docs/7-2/deploy/-/knowledge_base/d/migrating-user-segments)\nto see how much additional work you have in store. You must also\n[migrate display widgets](/docs/7-2/deploy/-/knowledge_base/d/manually-migrating-from-audience-targeting)\nsince the new Personalization features use different tools.\n\nFinally, you must migrate behavior-based features, but since Audience\nTargeting's analytics features are now part of Analytics Cloud, there isn't a\ndirect path to upgrade. See the \n[Analytics Cloud documentation](https://help.liferay.com/hc/en-us/articles/360006947671-Creating-Segments).\n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/09-migrating-from-audience-targeting/02-migrating-user-segments.markdown",
    "content": "---\nheader-id: migrating-user-segments\n---\n\n# Migrating User Segments\n\n[TOC levels=1-4]\n\nIn Audience Targeting, a user segment represents a subset of users. A user\nsegment is defined by one or more rules that users must match to belong to that\nuser segment. In @product-ver@, segments work in a similar way, but they\nare defined by criteria instead of rules. Segment criteria are sets of fields\ndefined by different user actions or properties (profile information,\norganization information, session information) that can be combined through\noperations (like equals, not equals, contains, not contains, greater than, and\nless than) and conjunctions (AND, OR) to define complex filters. \n\nDue to the similarities between Audience Targeting user segments and\n@product-ver@ Segments, certain data can be migrated automatically as part of\nthe upgrade process.\n\n## Upgrade Process\n\nAs a result of the upgrade process,\n\n- All Audience Targeting User Segments appear under the new Segments\n  administration in 7.2, with the same name.\n- For every segment, those Audience Targeting rules with an equivalent in\n  @product@ 7.2 have been migrated into the corresponding criteria fields (see\n  Table below).\n- Audience Targeting tables have been removed from your @product@ Database.\n\n| Audience Targeting Rule | @product@ 7.2. Segment Criteria Field | Upgrade Path |\n| ---                     | ---                     | --- |\n| Browser                 | Browser                 | Automated. Use user agent field with `contains` operation as an alternative |\n| Custom Field            | Custom Field            | Automated |\n| Language                | Language                | Automated |\n| Last Login Date         | Last Sign In Date       | Automated |\n| Organization Member     | Organization            | Automated |\n| OS                      | User Agent              | Automated |\n| Previous Visited Site   | Not Available           | Automated |\n| Regular Role            | Role                    | Automated |\n| Site Member             | Site                    | Automated |\n| User Group Member       | User Group              | Automated |\n| Age                     | Not Available           | Suggested: custom field |\n| Facebook (various)      | Not Available           | Suggested: custom field |\n| Gender                  | Not Available           | Suggested: custom field |\n| Score Points            | Not Available           | Suggested: cookie |\n| Visited Page/Content    | Not Available           | Suggested: cookie |\n\nHere's an example user segment as it would appear in Audience Targeting for\n@product@ 7.1:\n\n![Figure 1: A @product@ 7.1 Audience Targeting Segment.](../../../images/migrating-audience-targeting-segment.png)\n\nAnd here is the same segment migrated to Liferay 7.2:\n\n![Figure 2: A @product@ 7.2 Segment](../../../images/migrating-new-segment.png)\n\nFor those Audience Targeting rules without a direct equivalent, a manual\nmigration is required. If you have any these rules, you can learn about your\nnext steps in \n[Manual Migration ](/docs/7-2/deploy/-/knowledge_base/d/manually-migrating-from-audience-targeting).\n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/09-migrating-from-audience-targeting/03-manually-migrating-from-audience-targeting.markdown",
    "content": "---\nheader-id: manually-migrating-from-audience-targeting\n---\n\n# Manually Migrating from Audience Targeting\n\n[TOC levels=1-4]\n\nAs explained in the previous article, some Audience Targeting rules do not have \na direct equivalent in @product@ 7.2 and, therefore, they cannot be \nmigrated automatically. Here are the recommended solutions for each rule type.\n\n## User Attribute Rules\n\nSome User Attributes, like Gender or Age, do not have a direct equivalent in \n@product-ver@. User Attributes retrieved from external sources like Facebook \nalso do not have a replacement. To replace these, you must create a\n[custom user field](/docs/7-2/user/-/knowledge_base/u/creating-segments-with-custom-fields-and-session-data)\nand use that to define your new Segment.\n\n## Session Rules\n\nFor Session attributes that do not have a direct equivalent, the recommended \nsolution is to use a URL field for the current URL or a previously visited URL\non your site as criteria, or to use a Cookie for more advanced session tracking\nneeds.\n\n## Behavior Rules\n\nIn @product-ver@ analytics is now managed through Analytics Cloud. You can \nlearn more about creating behavior based rules in the\n[Analytics Cloud documentation](https://help.liferay.com/hc/en-us/articles/360006947671-Creating-Segments).\n\n## Migrating Custom Rules\n\nAudience Targeting segmentation features could be extended using custom rules. \nAs part of the upgrade planning process, the function of any such rules should \nbe re-evaluated with the new Segmentation features of @product-ver@ in mind. \n\nFirst, check the\n[Segmentation reference](/docs/7-2/reference/-/knowledge_base/r/defining-segmentation-criteria)\nif any new criteria fields can replace their function. In particular, custom\nfields, URL fields, and cookies might help you migrate your custom rules with\nlittle to no additional development. \n\nIf none of them cover your requirements, follow the development guide for \ninstructions on\n[how to add new criteria fields and contributors](/docs/7-2/frameworks/-/knowledge_base/f/segmentation-personalization).\n\n## Migrating Display Portlets\n\nWith Audience Targeting, you could display personalized content with the User\nSegment Display Content portlet or by using Asset Publisher with the\n[Segments filter enabled](https://help.liferay.com/hc/en-us/articles/360018174271-Using-the-Audience-Targeting-Widgets-).\nIn @product-ver@, you must choose the most appropriate personalization option\nfor your use cases. \n\n### User Segment Content Display\n\nThe User Segment Content Display portlet was used to display existing content \nbased on segment membership rules. In @product-ver@, you can cover the same use \ncase by defining manual content sets with variations for your different \naudiences and applying it to an asset publisher. See the documentation for \n[creating personalized Content Sets](/docs/7-2/user/-/knowledge_base/u/content-set-personalization).\nWith this feature, you can assign any number of assets to the Content List for\nthe given audience, and then use the Asset Publisher to define how content is\ndisplayed on the page. \n\n### Asset Publisher Personalization\n\nFinally, if you want to display a dynamic list of content for your different\naudiences based on a filter in the same way you did with in Audience Targeting\nwith the Segments filter in the Asset Publisher, you can create a dynamic\ncontent set with variations for your audiences and apply it to an asset\npublisher.\n\n<!-- TODO: [Link to Personalized Content Sets, dynamic section]. -->\n\nIn addition, the new\n[Experience-based Content Page personalization](/docs/7-2/user/-/knowledge_base/u/content-page-personalization)\nfeature may fulfill a use case that you were previously solving with one of the\nmethods previously available.\n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/98-deprecated-apps-in-7-2-what-to-do.markdown",
    "content": "---\nheader-id: deprecated-apps-in-7-2-what-to-do\n---\n\n# Deprecated Apps in 7.2: What to Do\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/reference/maintenance-mode-and-deprecations-in-7-2.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nDuring the development of any software product, it's sometimes necessary to stop\ndevelopment on or remove outdated or unpopular features. @product-ver@ is no\ndifferent. In @product-ver@, Liferay has deprecated several apps and features.\n\nThere are three types of deprecated apps:\n\n1.  Deprecated apps that remain in @product@, but will be removed in a future\n    release. (Availability: *Bundled*)\n\n2.  Deprecated apps that have been removed from @product@, yet are still\n    available for download via [Liferay\n    Marketplace](https://web.liferay.com/marketplace) (Availability:\n    *Marketplace*)\n\n3.  Deprecated apps that have been removed from @product@ and aren't available\n    for download. (Availability: *Removed*)\n\n| **Note:** All apps deprecated by Liferay are no longer in active development.\n| You should therefore plan to stop using these apps. Such apps, however, may\n| still be available for download.\n\n| **Note:** For information on apps deprecated in @product@ 7.1, please see\n| [Deprecated Apps in 7.1: What to Do](/docs/7-1/deploy/-/knowledge_base/d/deprecated-apps-in-7-1-what-to-do)\n\nHere are the apps deprecated in @product-ver@.\n\n## Foundation\n\n| App |  Availability |  Notes |\n| --- | ------------- | ------ |\n| AlloyUI | Bundled | Replaced by [MetalJS](https://metaljs.com/) (temporary) exposed as [ClayUI tag](/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs) equivalents. |\n| CMIS Store | Removed | Migrate to another [Document Repository Store option](/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration). Before [upgrading to @product-ver@](/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver), migrate your document store data using [Data Migration in Server Administration](/docs/7-2/user/-/knowledge_base/u/server-administration). |\n| JCRStore | Removed | Migrate to another [Document Repository Store option](/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration). Before [upgrading to @product-ver@](/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver), migrate your document store data using [Data Migration in Server Administration](/docs/7-2/user/-/knowledge_base/u/server-administration). |\n| Legacy Search Portlet | Bundled | Will be removed in a future release. Replaced by the [Search widgets](/docs/7-2/user/-/knowledge_base/u/search). |\n| Liferay Mobile Device Detection Enterprise | Removed | Contact 51Degrees for up-to-date definitions. |\n| Sprite framework | Bundled | Liferay's image sprite framework is deprecated and is disabled by default via the `sprite.enabled` [portal property](/docs/7-2/deploy/-/knowledge_base/d/portal-properties). You can still build image sprites using any framework you like and deploy them in your plugins. |\n\n## Personalization\n\n| App |  Availability |  Notes |\n| --- | ------------- | ------ |\n| Audience Targeting | Removed | Replaced by [Personalization](/docs/7-2/user/-/knowledge_base/u/segmentation-and-personalization). |\n\n## Web Experience\n\n| App |  Availability |  Notes |\n| --- | ------------- | ------ |\n| RSS Publisher | Bundled | See [the article](/docs/7-1/user/-/knowledge_base/u/the-rss-publisher-widget) on enabling and using this widget. |\n| User Group Pages (Copy Mode) | Bundled | See the [Legacy User Group Sites Beahavior](/docs/7-1/user/-/knowledge_base/u/user-group-sites#legacy-user-group-sites-behavior) instructions on how to enable it. |\n| Resources Importer | Bundled | Deprecated as of 7.1 with no direct replacement |\n\n## Forms\n\n| App | Availability | Notes |\n| --- | ------------------ | ----------- |\n| Web Form | Removed | Final version released for 7.0. |\n\n## Security\n\n| App |  Availability |  Notes |\n| --- | ------------------ | ----------- |\n| Central Authentication Service (CAS) | Bundled | Migrate to [SAML based authentication](https://help.liferay.com/hc/en-us/articles/360028711032-Introduction-to-Authenticating-Using-SAML). |\n| Google Login | Marketplace | Replaced by [OpenID Connect](/docs/7-2/deploy/-/knowledge_base/d/authenticating-with-openid-connect). |\n| NTLM | Marketplace | Replaced by [Kerberos](/docs/7-2/deploy/-/knowledge_base/d/authenticating-with-kerberos). |\n| OpenAM / OpenSSO | Bundled | Migrate to [SAML based authentication](https://help.liferay.com/hc/en-us/articles/360028711032-Introduction-to-Authenticating-Using-SAML). |\n| OpenID | Marketplace | Replaced by [OpenID Connect](/docs/7-2/deploy/-/knowledge_base/d/authenticating-with-openid-connect). |\n\n## User and System Management\n\n| App |  Availability |\n| --- | ------------------ |\n| Live Users | Enabled through the [`live.users.enabled`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html) [portal property](/docs/7-2/deploy/-/knowledge_base/d/portal-properties). |\n\n## Related Topics\n\n[Apps in Maintenance Mode](/docs/7-2/deploy/-/knowledge_base/d/apps-in-maintenance-mode)\n"
  },
  {
    "path": "en/deployment/articles/05-upgrading-to-liferay-7-2/99-apps-in-maintenance-mode.markdown",
    "content": "---\nheader-id: apps-in-maintenance-mode\n---\n\n# Apps in Maintenance Mode\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/upgrading-liferay/reference/maintenance-mode-and-deprecations-in-7-2.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAt a designated time, Liferay may cease enhancing a product or capability. This\nis called *maintenance mode*. During this mode, Liferay actively supports and\nprovides bug fixes for the product or capability in accordance with the\nsubscribers' subscription level and the end of service life policies of the\ncompatible Liferay DXP version. Maintenance mode does not necessarily mean that\ndeprecation in a future Liferay DXP version is planned for the product or\ncapability; it only means that enhancements aren't being made for the current\nLiferay DXP development cycle.\n\nAs of Liferay DXP 7.2, these products and capabilities have transitioned into\nmaintenance mode:\n\n-   Liferay Connected Services\n-   Liferay Connector to OAuth 1.0a\n-   Liferay Drools\n-   Liferay Mobile Experience (Liferay Screens, Liferay Mobile SDK, Liferay Push)\n-   Liferay Reports\n-   Liferay Sync\n-   Staging\n\n## Related Topics\n\n[Deprecated Apps in 7.2: What to do?](/docs/7-2/deploy/-/knowledge_base/d/deprecated-apps-in-7-2-what-to-do)\n"
  },
  {
    "path": "en/deployment/articles/06-maintaining-liferay/01-maintaining-liferay-intro.markdown",
    "content": "---\nheader-id: maintaining-liferay\n---\n\n# Maintaining @product@\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nOnce you have a @product@ installation, there are some things you must do to\nkeep it running smoothly. Backing up your installation in case of a hardware\nfailure protects your data and helps you get your system back in working order\nquickly. And if you're a DXP customer, patching your system regularly brings the\nlatest bug fixes to your running instance.\n\n| Upgrading @product-ver@ to a new GA version can require\n| [data upgrade](/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver).\n| Until you perform all required data upgrades (if any), @product@ startup fails\n| with messages like these:\n| \n| ```bash\n| 2019-03-06 17:22:35.025 INFO  [main][StartupHelper:72] There are no patches installed\n| You must first upgrade to @product@ 7210\n| 2019-03-06 17:22:35.098 ERROR [main][MainServlet:277] java.lang.RuntimeException: You must first upgrade to @product@ 7201\n| ```\n\nRead on to learn about how to keep your system running well. \n"
  },
  {
    "path": "en/deployment/articles/06-maintaining-liferay/02-backing-up-a-liferay-installation.markdown",
    "content": "---\nheader-id: backing-up-a-liferay-installation\n---\n\n# Backing up a @product@ Installation\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/backing-up.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nOnce you have an installation of @product@ running, you should implement a\ncomprehensive backup plan. In case some kind of catastrophic hardware failure\noccurs, you'll be thankful to have backups and procedures for restoring\n@product@ from one of them. @product@ isn't very different from other Java web\napplications that might be running on your application server. Nevertheless,\nthere are some specific components you should include in your backup plan.\n\nThe recommended backup plan includes backing up these things:\n\n-   Source code\n-   @product@'s file System\n-   @product@'s database\n\n## Backing up Source Code\n\nIf you have extended @product@ or have written any plugins, they should be\nstored in a source code repository such as Git, Subversion, or CVS, unless\nyou're Linus Torvalds, and then tarballs are okay too (that's a joke). You\nshould back up your source code repository on a regular basis to preserve your\nongoing work. This probably goes without saying in your organization since\nnobody wants to lose source code that's taken months to produce. Thus you should\ninclude source code in your @product@ backup plan.\n\nNext, let's examine the @product@ installation items you should back up.\n\n## Backing up @product@'s File System\n\nThe\n[Liferay Home folder](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\nstores @product@'s properties configuration files, such as `portal-setup-\nwizard.properties` and `portal-ext.properties`. You should absolutely back them\nup. In fact, it's best to back up your entire application server and Liferay\nHome folder contents.\n\n@product@ stores configuration files, search indexes, and cache information in\nLiferay Home's `/data` folder. If you're using the File System store or the\nAdvanced File System store, the documents and media repository is also stored\nhere by default. It's always important to back up your `/data` folder.\n\nThe files that comprise @product@'s OSGi runtime are stored in Liferay Home's\n`/osgi` folder. It contains all of the app and module JAR files deployed to\n@product@. The `/osgi` folder also contains other required JAR files,\nconfiguration files, and log files. It's also important to back up your `/osgi`\nfolder.\n\nLiferay Home's `/logs` folder contains @product@'s log files. If a problem\noccurs on @product@, the @product@ log files often provide valuable information\nfor determining what went wrong. The `/data`, `/osgi`, and `/logs` folders are\nall contained in the Liferay Home folder. Thus, if you're backing up both your\napplication server folder and your Liferay Home folder, you're in good shape.\n\nRemember that if you've configured the document library to store files to a\nlocation other than the default location, you should also back up that location.\n\nThat covers the @product@ file system locations you should back up. Next, let's\ndiscuss how to back up @product@'s database.\n\n## Backing up @product@'s Database\n\n@product@'s database is the central repository for all of the portal's\ninformation. It's the most important component to back up. You can back up the\ndatabase live (if your database allows this) or by exporting (dumping) the\ndatabase into a file and then backing up the exported file. For example, MySQL\nships with a `mysqldump` utility which lets you export the entire database and\ndata into a large SQL file. This file can then be backed up. On restoring the\ndatabase you can import this file into the database to recreate the database\nstate to that of the time you exported the database.\n\nIf you're storing @product@'s Documents and Media Library files to a Jackrabbit\nJSR-170 repository database, you should back it up. If you've placed your search\nindex into a database (not recommended; see the\n[@product@ Clustering](/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering) \narticle for information on using Cluster Link or Solr), you should back up that\ndatabase too. \n\nIf you wish to avoid re-indexing your content after restoring your database,\nback up your search indexes. This is easiest to do if you have a separate\nElastic or Solr environment on which your index is stored. If you're in a\nclustered configuration and you're replicating indexes, you'll need to back up\neach index replica.\n\nRestoring your application server, your Liferay Home folder, the locations of\nany file system-based media repositories, and your database from a backup system\nshould give you a functioning portal. Restoring search indexes should avoid the\nneed to re-index when you bring your site back up after a catastrophic failure.\nGood, consistent backup procedures are key to recovering successfully from a\nhardware failure.\n"
  },
  {
    "path": "en/deployment/articles/07-monitoring-liferay/01-monitoring-liferay-intro.markdown",
    "content": "---\nheader-id: monitoring-product\n---\n\n# Monitoring @product@\n\n[TOC levels=1-4]\n\nThese articles show you how to monitor @product@. Monitoring vital statistics\nsuch as Java memory heaps, garbage collection, database connection pools, and\nthe application server helps you optimize performance. Better monitoring means\nbetter tuning and thus avoids dangerous runtime scenarios like out of memory\nerrors and wasted heap space.\n\nYou'll learn basic monitoring techniques, such as \n\n- Using the Visual VM tool and the JMX Console\n- Garbage Collection\n\nRead on to learn more about monitoring @product@! \n"
  },
  {
    "path": "en/deployment/articles/07-monitoring-liferay/02-monitoring-gc-and-the-jvm.markdown",
    "content": "---\nheader-id: monitoring-garbage-collection-and-the-jvm\n---\n\n# Monitoring Garbage Collection and the JVM\n\n[TOC levels=1-4]\n\nAlthough the \n[tuning parameters](/docs/7-2/deploy/-/knowledge_base/d/tuning-guidelines)\ngive you a good start to JVM tuning, you must monitor GC performance to ensure\nyou have the best settings to meet your needs. There are several tools to help\nyou monitor Oracle JVM performance. \n\n## VisualVM\n\n[VisualVM](https://visualvm.github.io/)\nprovides a centralized console for viewing Oracle JVM performance information\nand its Visual GC plugin shows garbage collector activities.\n\n![Figure 1: VisualVM's Visual GC plugin shows the garbage collector in real-time.](../../images-dxp/visual-vm-gc.png)\n\n| **Note:** Oracle's JDK has VisualVM bundled (`$JAVA_HOME/bin/jvisualvm`).\n| However, always download and use the latest version from VisualVM's\n| [official website](https://visualvm.github.io/).\n\n## JMX Console\n\nThis tool helps display various statistics like @product@'s distributed cache\nperformance, application server thread performance, JDBC connection pool usage,\nand more. \n\n| **Note:** The JMX Console is the preferred tool for monitoring application \n| server performance.\n\nTo enable JMX connections, add these JVM arguments:\n\n```bash\n-Dcom.sun.management.jmxremote=true\n-Dcom.sun.management.jmxremote.port=5000\n-Dcom.sun.management.jmxremote.authenticate=false\n-Dcom.sun.management.jmxremote.ssl=false\n```\n\nIf you're running JMX Console from a another machine, add these JVM arguments too:\n\n```bash\n-Dcom.sun.management.jmxremote.local.only=false\n-Dcom.sun.management.jmxremote.rmi.port=5000\n-Djava.rmi.server.hostname=[place IP address here]\n```\n\n![Figure 2: VisualVM monitors the JVM using Java Management Extensions.](../../images-dxp/visual-vm-jmx.png)\n\n## Garbage Collector Verbose Logging\n\nAdd these JVM arguments to activate verbose logging for the JVM garbage\ncollector.\n\n```bash\n-verbose:gc -Xloggc:/tmp/liferaygc1.log -XX:+PrintGCDetails \n-XX:+PrintGCCause -XX:+PrintGCApplicationConcurrentTime \n-XX:+PrintGCApplicationStoppedTime\n```\n\nExamining these logs helps you tune the JVM properly. \n\n| **Note:** Adding these JVM arguments generates a heap dump if an\n| `OutOfMemoryError` occurs. The dump is written to the heap dump path \n| specified. Specify the path to use:\n| \n| `-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/heap/dump/path/`\n\nGarbage collector log files can grow huge. You can use additional arguments like\nthe following ones to rotate the log to a new log file when the current log\nfile reaches a maximum size: \n\n```bash\n-XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 \n-XX:GCLogFileSize=50M\n```\n\nThese arguments rotate the logs to up to `10` log files with a maximum size of\n`50M` each. \n\nNow you can monitor garbage collection in the JVM and tune it for top\nperformance. \n"
  },
  {
    "path": "en/deployment/articles/100-reference/01-deployment-reference-intro.markdown",
    "content": "---\nheader-id: deployment-reference\n---\n\n# Deployment Reference\n\n[TOC levels=1-4]\n\nHere you'll find definitions, default settings, templates, and more. Here are\nsome of the topics:\n\n-   [Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home):\n    From this location, @product@ launches applications, applies configurations,\n    loads JAR files, and generates logs. \n\n-   [Portal properties](/docs/7-2/deploy/-/knowledge_base/d/portal-properties): \n    Use a `portal-ext.properties` file (or another qualified properties file) to\n    configure @product@ and override features. \n\n-   [System properties](/docs/7-2/deploy/-/knowledge_base/d/system-properties):\n    Examine @product@ default system configuration. \n\n-   [Database templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates):\n    Use these portal properties templates to specify the @product@ database. \n\n-   [Elasticsearch settings](/docs/7-2/deploy/-/knowledge_base/d/elasticsearch-connector-settings-reference):\n    Examine the configuration settings for Liferay's default Elasticsearch\n    adapter. \n"
  },
  {
    "path": "en/deployment/articles/100-reference/02-liferay-home.markdown",
    "content": "---\nheader-id: liferay-home\n---\n\n# Liferay Home\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/reference/liferay-home.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n*Liferay Home* is the location from which @product@ launches applications, \napplies configurations, loads JAR files, and generates logs. \n\n-   *In a @product@ bundle,* Liferay Home is the installation's top-level folder\n    and it contains the application server.\n\n-   *In a manual installation,* the Liferay Home folder varies by application\n    server. If you're doing a manual installation, please refer to the article\n    covering that app server (e.g., *Installing @product@ on [app server]*) for\n    the Liferay Home location. \n\nBundles contain this folder structure regardless of application server: \n\n-   **[Liferay Home]**\n    -   **[Application Server]**: This folder is named after the application\n        server where @product@ is installed. \n    -   `data` (if HSQL database is selected): Stores an embedded HSQL database,\n        @product@'s file repository,\n        and search indexes. The embedded HSQL database is configured by default,\n        but it's intended for demonstration and trial\n        purposes only. The\n        [Portal property `jdbc.default.url`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#JDBC)\n        sets the Hypersonic embedded HSQL database location.\n    -   `deploy`: To auto-deploy plugins, copy them to this folder. \n        It supports application `.lpkg` files from Liferay Marketplace, plugin\n        `.war` files, and plugin `.jar` files. The\n        [Portal property `auto.deploy.deploy.dir`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Auto%20Deploy)\n        sets the auto-deploy location.\n    -   `license`: @product@'s copyright and version files are here.\n    -   `logs`: Log files go here. Examine them as you diagnose\n        problems. `portal-impl.jar`'s\n        `portal-impl/src/META-INF/portal-log4j.xml` file sets the log file\n        location. To override the log file location, you must\n        [use an `ext-impl/src/META-INF/portal-log4j-ext.xml` file in an Ext plugin](/docs/7-0/tutorials/-/knowledge_base/t/advanced-customization-with-ext-plugins#using-advanced-configuration-files).\n    -   `osgi`: All the JAR files and a few configuration files for the \n        OSGi runtime belong in this folder. The\n        [Portal property `module.framework.base.dir`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Module%20Framework)\n        sets the OSGi folder location. Here are its subfolders:\n\n        -   `configs`: Component configuration files.\n        -   `core`: @product@'s core modules.\n        -   `marketplace`: Marketplace applications and application suites.\n        -   `modules`: Modules you've deployed.\n        -   `portal`: @product@'s non-core modules.\n        -   `state`: Contains OSGi internal state files for such things as OSGi \n            bundle installation, bundle storage, and more.\n        -   `target-platform`: Target platform index.\n        -   `test`: Modules that support test integration.\n        -   `war`: WAR plugins you've deployed.\n    -   `patching-tool`: (Liferay DXP only) This folder contains patches and \n        a utility for installing the patches.\n    -   `tools`: For @product@ upgrade and target platform indexer.\n    -   `work`: Module Jasper work files.\n\n| **Note:** If @product@ cannot create resources in the \n| Liferay Home folder or if it finds itself running on certain application\n| servers, it creates a folder called `liferay` in the home folder of the\n| operating system user that is running @product@. In this case, that `liferay`\n| folder becomes Liferay Home. For example, if the operating system user's name\n| is jbloggs, the `liferay` folder path is `/home/jbloggs/liferay` or\n| `C:\\Users\\jbloggs\\liferay`.\n"
  },
  {
    "path": "en/deployment/articles/100-reference/03-portal-properties.markdown",
    "content": "---\nheader-id: portal-properties\n---\n\n# Portal Properties\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/reference/portal-properties.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nYou can set portal properties to configure and override @product@ features. Your\ninstallation's `portal-impl.jar` embeds the default properties file: \n\n<p>\n<span style=\"font-size:18px;\">\n<a href=\"@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html\">\n<code>portal.properties</code>\n<span class=\"opens-new-window-accessible\"> (Opens New Window)</span>\n</a>\n</span>\n</p>\n\nOverriding a portal property requires creating an *extension* portal properties\nfile that specifies the properties you're overriding. \n\n| **Note:** In a portal properties extension file, specify only the properties\n| you're overriding. \n\nHere's an example of setting Portal's data source to a MySQL database by adding\noverride properties in a `[Liferay Home]/portal-ext.properties` file:\n\n```properties\njdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\njdbc.default.url=jdbc:mysql://localhost/lportal?characterEncoding=UTF-8&dontTrackOpenResources=true&holdResultsOpenOverStatementClose=true&serverTimezone=GMT&useFastDateParsing=false&useUnicode=true\njdbc.default.username=jbloggs\njdbc.default.password=pass123\n```\n\nThe\n[`include-and-override`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Properties Override)\nproperty specifies portal property files that override the defaults. It\nspecifies the order the files are read---the last file read takes highest\npriority. \n\nProperties file prioritization (highest to lowest):\n\n1. `[Liferay Home]/portal-setup-wizard.properties`\n2. `[user home]/portal-setup-wizard.properties`\n3. `[Liferay Home]/portal-ext.properties`\n4. `[user home]/portal-ext.properties`\n5. `[Liferay Home]/portal-bundle.properties`\n6. `[user home]/portal-bundle.properties`\n7. `[Liferay Home]/portal.properties`\n8. `portal-impl.jar/portal.properties`\n\n`[Liferay Home]/portal-ext.properties` is the most commonly used file for\noverriding the defaults. \n"
  },
  {
    "path": "en/deployment/articles/100-reference/04-system-properties.markdown",
    "content": "---\nheader-id: system-properties\n---\n\n# System Properties\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/reference/system-properties.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nSystem properties configure the @product@ system. Your\ninstallation's `portal-impl.jar` embeds the default properties file: \n\n<p>\n<span style=\"font-size:18px;\">\n<a href=\"@platform-ref@/7.2-latest/propertiesdoc/system.properties.html\">\n<code>system.properties</code>\n<span class=\"opens-new-window-accessible\"> (Opens New Window)</span>\n</a>\n</span>\n</p>\n"
  },
  {
    "path": "en/deployment/articles/100-reference/05-database-templates.markdown",
    "content": "---\nheader-id: database-templates\n---\n\n# Database Templates\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/reference/database-templates.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nBelow are templates (example\n[portal properties](/docs/7-2/deploy/-/knowledge_base/d/portal-properties))\nfor configuring various databases as a built-in data source for @product@.\n\n| **Note:** The [Liferay DXP Compatibility Matrix](https://web.liferay.com/documents/14/21598941/Liferay+DXP+7.2+Compatibility+Matrix/b6e0f064-db31-49b4-8317-a29d1d76abf7?) specifies supported databases and environments.\n\n## MariaDB\n\n```properties\njdbc.default.driverClassName=org.mariadb.jdbc.Driver\njdbc.default.url=jdbc:mariadb://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\njdbc.default.username=\njdbc.default.password=\n```\n\n## MySQL\n\n| **Note:** MySQL Connector/J 8.0 is highly recommended for use with MySQL\n| Server 8.0 and 5.7.\n\n```properties\njdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\njdbc.default.url=jdbc:mysql://localhost/lportal?characterEncoding=UTF-8&dontTrackOpenResources=true&holdResultsOpenOverStatementClose=true&serverTimezone=GMT&useFastDateParsing=false&useUnicode=true\njdbc.default.username=\njdbc.default.password=\n```\n\n## PostgreSQL\n\n```properties\njdbc.default.driverClassName=org.postgresql.Driver\njdbc.default.url=jdbc:postgresql://localhost:5432/lportal\njdbc.default.username=sa\njdbc.default.password\n```\n\nSee the\n[default portal properties](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#JDBC)\nfor more database templates.\n"
  },
  {
    "path": "en/deployment/articles/99-troubleshooting-deployments/01-troubleshooting-deployments-intro.markdown",
    "content": "---\nheader-id: troubleshooting-deployments\n---\n\n# Troubleshooting Deployments\n\n[TOC levels=1-4]\n\nWhen coding on any platform, you can sometimes run into issues that have no\nclear resolution. This can be particularly frustrating. If you have issues\nbuilding, deploying, or running apps and modules, you want to resolve them\nfast. These frequently asked questions and answers help you troubleshoot and\ncorrect problems. \n\nClick a question to view the answer.\n\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">Why did the entity sort order change when I migrated to a new database type?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">  \n    <p><a href=\"/docs/7-2/deploy/-/knowledge_base/d/sort-order-changed-with-a-different-database\">Your new database uses a different default query result order--you should be able to configure a different order</a>.</p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">How can I use files to configure components?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">  \n    <p>See <a href=\"/docs/7-2/deploy/-/knowledge_base/d/using-files-to-configure-product-modules\">Using Files to Configure Module Components</a>. </p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">The application server and database started, but @product@ failed to connect to the database. What happened and how can I fix this?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">  \n    <p>@product@ initialization can fail while attempting to connect to a database server that isn't ready. <a href=\"/docs/7-2/deploy/-/knowledge_base/d/portal-failed-to-initialize-because-the-database-wasnt-ready\">Configuring startup to retry JDBC connections</a> facilitates connecting @product@ to databases. </p>\n  </div>\n</div>\n"
  },
  {
    "path": "en/deployment/articles/99-troubleshooting-deployments/database-not-ready.markdown",
    "content": "---\nheader-id: portal-failed-to-initialize-because-the-database-wasnt-ready\n---\n\n# @product@ Failed to Initialize Because the Database Wasn't Ready\n\n[TOC levels=1-4]\n\nIf you start your database server and application server at the same time,\n@product@ might try connecting to the data source before the database is ready.\nBy default, @product@ doesn't retry connecting to the database; it just fails.\nBut there is a way to avoid this situation: database connection retries.\n\n1.  Create a `portal-ext.properties` file in your\n    [Liferay Home](/docs/7-1/deploy/-/knowledge_base/d/liferay-home)\n    folder.\n\n2.  Set the property `retry.jdbc.on.startup.max.retries` equal to the number of\n    times to retry connecting to the data source. \n\n3.  Set property `retry.jdbc.on.startup.delay` equal to the number of seconds\n    to wait before retrying connection.\n\nIf at first the connection doesn't succeed, @product@ uses the retry settings to\ntry again. \n\n## Related Topics\n\n[Connecting to JNDI Data Sources](/docs/7-2/appdev/-/knowledge_base/a/connecting-to-data-sources-using-jndi)\n"
  },
  {
    "path": "en/deployment/articles/99-troubleshooting-deployments/sort-order-changed-with-different-database.markdown",
    "content": "---\nheader-id: sort-order-changed-with-a-different-database\n---\n\n# Sort Order Changed with a Different Database\n\n[TOC levels=1-4]\n\nIf you've been using @product@, but are switching it to use a different database\ntype, consult your database vendor documentation to understand your old and new\ndatabase's default query result order. The default order is either\ncase-sensitive or case-insensitive. This affects entity sort order in @product@.\n\nHere are some examples of ascending alphabetical sort order. \n\nCase-sensitive:\n\n    111\n    222\n    AAA\n    BBB\n    aaa\n    bbb\n\nCase-insensitive:\n\n    111\n    222\n    AAA\n    aaa\n    BBB\n    bbb\n\nYour new database's default query result order might differ from your current\ndatabase's order. \n\nConsult your vendor's documentation to configure the order the way you want. \n"
  },
  {
    "path": "en/deployment/articles/99-troubleshooting-deployments/using-files-to-config-components.markdown",
    "content": "---\nheader-id: using-files-to-configure-product-modules\n---\n\n# Using Files to Configure Module Components\n\n[TOC levels=1-4]\n\n@product@ uses \n[Felix File Install](http://felix.apache.org/documentation/subprojects/apache-felix-file-install.html)\nto monitor file system folders for new/updated configuration files, and the\n[Felix OSGi implementation](http://felix.apache.org/)\nof\n[Configuration Admin](http://felix.apache.org/documentation/subprojects/apache-felix-config-admin.html)\nto let you use files to configure module service components. \n\nTo learn how to work with configuration files, first review \n[Understanding System Configuration Files](/docs/7-1/user/-/knowledge_base/u/understanding-system-configuration-files). \n\n## Configuration File Formats\n\nThere are two different configuration file formats: \n\n-   `.cfg`: An older, simple format that only supports `String` values as \n    properties. \n-   `.config`: A format that supports strings, type information, and other\n    non-string values in its properties. \n\nAlthough @product@ supports both formats, use `.config` files for their\nflexibility and ability to use type information. Since `.cfg` files lack type\ninformation, if you want to store anything but a `String`, you must use\nproperties utility classes to cast `String`s to intended types (and you must\ncarefully document properties that aren't `String`s). `.config` files eliminate\nthis need by allowing type information. The articles below explain the file\nformats: \n\n-   [Understanding System Configuration Files](/docs/7-1/user/-/knowledge_base/u/understanding-system-configuration-files)\n-   [Configuration file (`.config`) syntax](https://sling.apache.org/documentation/bundles/configuration-installer-factory.html#configuration-files-config)\n-   [Properties file(`.cfg`) syntax](https://sling.apache.org/documentation/bundles/configuration-installer-factory.html#property-files-cfg)\n\n## Naming Configuration Files\n\nBefore you [create a configuration\nfile](/docs/7-1/user/-/knowledge_base/u/creating-configuration-files), follow\nthese steps to determine whether multiple instances of the component can be\ncreated or if the component is intended to be a singleton:\n\n1.  Deploy the component's module if you haven't done so already. \n\n2.  In @product@'s UI, go to *Control Panel* &rarr; *Configuration* &rarr; \n    *System Settings*. \n\n3.  Find the component's settings by searching or browsing for the component. \n\n4.  If the component's settings page has a section called *Configuration \n    Entries*, you can create multiple instances of the component configured \n    however you like. Otherwise, you should treat the component as a singleton. \n\n![Figure 1: You can create multiple instances of components whose System Settings page has a *Configuration Entries* section.](../../images/system-settings-page-lists-configuration-entries.png)\n\n*All* configuration file names must start with the component's PID (PID stands\nfor *persistent identity*) and end with `.config` or `.cfg`. \n\nFor example, this class uses  [Declarative\nServices](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services)  to\ndefine a component:\n\n```java\npackage com;\n@Component\nclass Foo {}\n```\n\nThe component's PID is `com.Foo`. All the component's configuration files must\nstart with the PID `com.Foo`. \n\nFor each non-singleton component instance you want to create or update with a\nconfiguration, you must use a uniquely named configuration file that starts with\nthe component's PID and ends with `.config` or `.cfg`. Creating configurations\nfor multiple component instances requires that the configuration files use\ndifferent *subnames*. A subname is the part of a configuration file name after\nthe PID and before the suffix `.config` or `.cfg`. Here's the configuration file\nname pattern for non-singleton components: \n\n-   `[PID]-[subname1].config`\n-   `[PID]-[subname2].config`\n-   etc. \n\nFor example, you could configure two different instances of the component \n`com.Foo` by using configuration files with these names: \n\n-   `com.Foo-one.config`\n-   `com.Foo-two.config`\n\nEach configuration file creates and/or updates an instance of the component that\nmatches the PID. The subname is arbitrary---it doesn't have to match a specific\ncomponent instance. This means you can use whatever subname you like. For\nexample, these configuration files are just as valid as the two above: \n\n-   `com.Foo-puppies.config`\n-   `com.Foo-kitties.config`\n\nUsing the subname `default`, however, is @product@'s convention for configuring \na component's first instance. The file name pattern is therefore \n\n    [PID]-default.config\n\nA singleton component's configuration file must also start with `[PID]` and end\nwith `.config` or `.cfg`. Here's the common pattern used for singleton component\nconfiguration file names: \n\n    [PID].config\n\nWhen you're done creating a configuration file, you can\n[deploy it](/docs/7-1/user/-/knowledge_base/u/understanding-system-configuration-files#deploying-a-configuration-file). \n\n## Resolving Configuration File Deployment Failures\n\nThe following `IOException` hints that the configuration file has a syntax\nissue: \n\n    Failed to install artifact: [path to .config or .cfg file]\n    java.io.IOException: Unexpected token 78; expected: 61 (line=0, pos=107)\n\nTo resolve this, fix the \n[configuration file's syntax](#configuration-file-formats). \n\nGreat! Now you know how to configure module components using configuration \nfiles. \n\n## Related Articles\n\n[Understanding System Configuration Files](/docs/7-1/user/-/knowledge_base/u/understanding-system-configuration-files)\n"
  },
  {
    "path": "en/deployment/articles-dxp/01-deploying-liferay/07-installing-liferay-portal-on-jboss-eap.markdown",
    "content": "---\nheader-id: installing-product-on-jboss-eap\n---\n\n# Installing @product@ on JBoss EAP\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay/installing-liferay-on-an-application-server/installing-on-jboss-eap.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nInstalling @product@ on JBoss EAP 7.1 takes three steps:\n\n- Installing dependencies to your application server\n- Configuring your application server for @product@\n- Installing the @product@ WAR file to your application server\n\n| **Important:** Before installing @product@, familiarize yourself with\n| [preparing for install](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install).\n\nNow, [download the @product@ WAR and Dependency\nJARs](/docs/7-2/deploy/-/knowledge_base/d/obtaining-product#downloading-the-liferay-war-and-dependency-jars):\n\n-   @product@ WAR file\n-   Dependencies ZIP file\n-   OSGi Dependencies ZIP file\n\nNot that [*Liferay Home*](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\nis the folder containing your JBoss server folder. After installing and\ndeploying @product@, the Liferay Home folder contains the JBoss server folder as\nwell as `data`, `deploy`, `logs`, and `osgi` folders. `$JBOSS_HOME` refers to\nyour JBoss server folder. This folder is usually named `jboss-eap-[version]`.\n\n## Installing Dependencies\n\n@product@ depends on several Liferay-specific and third-party JARs.\nDownload and install the required JARs as described below.\n\n1.  Create the folder `$JBOSS_HOME/modules/com/liferay/portal/main` if it\n    doesn't exist and extract the JARs from the dependencies ZIP to it.\n\n2.  Download your database driver `.jar` file and copy it into the same folder.\n\n    | **Note:** The [Liferay DXP Compatibility Matrix](https://web.liferay.com/documents/14/21598941/Liferay+DXP+7.2+Compatibility+Matrix/b6e0f064-db31-49b4-8317-a29d1d76abf7?) specifies supported databases and environments.\n\n3.  Create the file `module.xml` in the\n    `$JBOSS_HOME/modules/com/liferay/portal/main` folder and insert this\n    configuration:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n\n    <module xmlns=\"urn:jboss:module:1.0\" name=\"com.liferay.portal\">\n        <resources>\n            <resource-root path=\"com.liferay.petra.concurrent.jar\" />\n            <resource-root path=\"com.liferay.petra.executor.jar\" />\n            <resource-root path=\"com.liferay.petra.function.jar\" />\n            <resource-root path=\"com.liferay.petra.io.jar\" />\n            <resource-root path=\"com.liferay.petra.lang.jar\" />\n            <resource-root path=\"com.liferay.petra.memory.jar\" />\n            <resource-root path=\"com.liferay.petra.nio.jar\" />\n            <resource-root path=\"com.liferay.petra.process.jar\" />\n            <resource-root path=\"com.liferay.petra.reflect.jar\" />\n            <resource-root path=\"com.liferay.petra.string.jar\" />\n            <resource-root path=\"com.liferay.registry.api.jar\" />\n            <resource-root path=\"hsql.jar\" />\n            <resource-root path=\"[place your database vendor's jar here]\" />\n            <resource-root path=\"portal-kernel.jar\" />\n            <resource-root path=\"portlet.jar\" />\n        </resources>\n        <dependencies>\n            <module name=\"javax.api\" />\n            <module name=\"javax.mail.api\" />\n            <module name=\"javax.servlet.api\" />\n            <module name=\"javax.servlet.jsp.api\" />\n            <module name=\"javax.transaction.api\" />\n        </dependencies>\n    </module>\n    ```\n\n    Replace `[place your database vendor's jar here]` with the driver JAR for your database.\n\n4.  Create an `osgi` folder in your [Liferay\n    Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) folder. Extract the\n    OSGi Dependencies ZIP file that you downloaded into the `[Liferay\n    Home]/osgi` folder.\n\n    The `osgi` folder provides the necessary modules for @product@'s OSGi\n    runtime.\n\n**Checkpoint:**\n\n1. The dependencies files have been unzipped into the `$JBOSS_HOME/modules/com/liferay/portal/main` folder and a database jar.\n1. The `module.xml` contains all JARs in the `<resource-root-path>` elements.\n1. The `osgi` dependencies have been unzipped into the `osgi` folder.\n\n## Running @product@ on JBoss EAP in Standalone Mode vs. Domain Mode\n\nJBoss EAP can be launched in either *standalone* mode or *domain* mode. Domain\nmode allows multiple application server instances to be managed from a single\ncontrol point. A collection of such application servers is known as a *domain*.\nFor more information on standalone mode vs. domain mode, please refer to the\nsection on this topic in the [JBoss EAP Product\nDocumentation](https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.1/html/introduction_to_jboss_eap/overview_of_jboss_eap#operating_modes).\n\n@product@ supports JBoss EAP when it runs in standalone mode but not when it\nruns in domain mode. @product@'s hot-deploy does not work with a managed\ndeployment, since JBoss manages the content of a managed deployment by copying\nfiles (exploded or non-exploded). This prevents JSP hooks and Ext plugins from\nworking as intended. For example, JSP hooks don't work on JBoss EAP running in\nmanaged domain mode, since @product@'s JSP override mechanism relies on the\napplication server. Since JSP hooks and Ext plugins are deprecated, however, you\nmay not be using them.\n\nThe command line interface is recommended for domain mode deployments.\n\n| **Note:** This does not prevent @product@ from running in a clustered\n| environment on multiple JBoss servers. You can set up a cluster of @product@\n| instances running on JBoss EAP servers running in standalone mode. Please\n| refer to the [@product@ clustering articles](/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering)\n| for more information.\n\n## Configuring JBoss\n\nConfiguring JBoss to run @product@ includes these things:\n\n- Setting environment variables\n- Setting properties and descriptors\n- Removing unnecessary configurations\n\nOptionally, you can configure JBoss to manage @product@'s data source and mail\nsession.\n\nStart with configuring JBoss to run @product@.\n\nMake the following modifications to\n`$JBOSS_HOME/standalone/configuration/standalone.xml`:\n\n1.  Locate the closing `</extensions>` tag. Directly beneath that tag, insert\n    these system properties:\n\n    ```xml\n    <system-properties>\n        <property name=\"org.apache.catalina.connector.URI_ENCODING\" value=\"UTF-8\" />\n        <property name=\"org.apache.catalina.connector.USE_BODY_ENCODING_FOR_QUERY_STRING\" value=\"true\" />\n    </system-properties>\n    ```\n\n2.  Add the following `<filter-spec>` tag within the `<console-handler>` tag,\n    directly below the `<level name=\"INFO\"/>` tag:\n\n    ```xml\n    <filter-spec value=\"not(any(match(&quot;WFLYSRV0059&quot;),match(&quot;WFLYEE0007&quot;)))\" />\n    ```\n\n3.  Add a timeout for the deployment scanner by setting\n    `deployment-timeout=\"600\"` as seen in the excerpt below.\n\n    ```xml\n    <subsystem xmlns=\"urn:jboss:domain:deployment-scanner:2.0\">\n        <deployment-scanner deployment-timeout=\"600\" path=\"deployments\" relative-to=\"jboss.server.base.dir\" scan-interval=\"5000\"/>\n    </subsystem>\n    ```\n\n4.  Add the following JAAS security domain to the security subsystem\n    `<security-domains>` defined in element `<subsystem\n    xmlns=\"urn:jboss:domain:security:2.0\">`.\n\n    ```xml\n    <security-domain name=\"PortalRealm\">\n        <authentication>\n            <login-module code=\"com.liferay.portal.security.jaas.PortalLoginModule\" flag=\"required\" />\n        </authentication>\n    </security-domain>\n    ```\n\n5.  Remove the welcome content code snippets:\n\n    ```xml\n    <location name=\"/\" handler=\"welcome-content\"/>\n    ```\n\n    and\n\n    ```xml\n    <handlers>\n        <file name=\"welcome-content\" path=\"${jboss.home.dir}/welcome-content\"/>\n    </handlers>\n    ```\n\n6.  Find the `<jsp-config/>` tag and set the `development`, `source-vm`, and\n    `target-vm` attributes in the tag. Once finished, the tag should look like\n    this:\n\n    ```xml\n    <jsp-config development=\"true\" source-vm=\"1.8\" target-vm=\"1.8\"/>\n    ```\n\n**Checkpoint:**\n\nBefore continuing, verify the following properties have been set in the\n`standalone.xml` file:\n\n1.  The new `<system-property>` is added.\n\n2.  The new `<filter-spec>` is added.\n\n3.  The `<deployment-timeout>` is set to `360`.\n\n4.  The new `<security-domain>` is created.\n\n5.  Welcome content is removed.\n\n6.  The `<jsp-config>` tag contains its new attributes.\n\nNow you should configure your JVM and startup scripts.\n\nIn the `$WILDFLY_HOME/bin/` folder, modify your standalone domain's\nconfiguration script file `standalone.conf` (`standalone.conf.bat` on Windows):\n\n- Set the file encoding to `UTF-8`\n- Set the user time zone to `GMT`\n- Set the preferred protocol stack\n- Increase the default amount of memory available.\n\n| **Important:** For @product@ to work properly, the application server JVM must\n| use the `GMT` time zone and `UTF-8` file encoding.\n\nMake the following edits as applicable to your operating system:\n\n**Windows**\n\n1.  Comment out the initial `JAVA_OPTS` assignment as demonstrated in the\n    following line:\n\n    ```bash\n    rem set \"JAVA_OPTS=-Xms1G -Xmx1G -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=2560m\"\n    ```\n\n2.  Add the following `JAVA_OPTS` assignment one line above the `:JAVA_OPTS_SET`\n    line found at end of the file:\n\n    ```bash\n    set \"JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Djboss.as.management.blocking.timeout=480 -Duser.timezone=GMT -Xms2560m -Xmx2560m -XX:MaxMetaspaceSize=768m\"\n    ```\n\n**Unix**\n\n1.  Below the `if [ \"x$JAVA_OPTS\" = \"x\" ];` statement, replace this `JAVA_OPTS`\n    statement:\n\n    ```bash\n    JAVA_OPTS=\"-Xms1303m -Xmx1303m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=2560m -Djava.net.preferIPv4Stack=true\"\n    ```\n\n    with this:\n\n    ```bash\n    JAVA_OPTS=\"-Djava.net.preferIPv4Stack=true\"\n    ```\n\n2.  Add the following statement to the bottom of the file:\n\n    ```bash\n    JAVA_OPTS=\"$JAVA_OPTS -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=true -Djboss.as.management.blocking.timeout=480 -Duser.timezone=GMT -Xms2560m -Xmx2560m -XX:MaxMetaspaceSize=512m\"\n    ```\n\n\nOn JDK 11, it's recommended to add this JVM argument to display four-digit\nyears.\n\n```bash\n-Djava.locale.providers=JRE,COMPAT,CLDR\n```\n\n| **Note:** If you plan on using the IBM JDK with your JBoss server, you must\n| complete some additional steps. First, navigate to the\n| `$JBOSS_HOME/modules/com/liferay/portal/main/module.xml` file and insert the\n| following dependency within the `<dependencies>` element:\n|\n|     <module name=\"ibm.jdk\" />\n|\n| Then navigate to the\n| `$JBOSS_HOME/modules/system/layers/base/sun/jdk/main/module.xml` file and\n| insert the following path names inside the `<paths>...</paths>` element:\n|\n|     <path name=\"com/sun/crypto\" />\n|     <path name=\"com/sun/crypto/provider\" />\n|     <path name=\"com/sun/image/codec/jpeg\" />\n|     <path name=\"com/sun/org/apache/xml/internal/resolver\" />\n|     <path name=\"com/sun/org/apache/xml/internal/resolver/tools\" />\n|\n| The added paths resolve issues with portal deployment exceptions and image\n| uploading problems.\n\n**Checkpoint:**\n\nAt this point, you've finished configuring the application server's JVM\nsettings.\n\n1.  The file encoding, user time-zone, preferred protocol stack have been set in\n    the `JAVA_OPTS` in the `standalone.conf.bat` file.\n\n2.  The default amount of memory available has been increased.\n\nThe prescribed script modifications are now complete for your @product@\ninstallation on JBoss. Next you'll configure the database and mail.\n\n## Database Configuration\n\nThe easiest way to handle your database configuration is to let @product@ manage\nyour data source. The [Basic\nConfiguration](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install#using-the-built-in-data-source)\npage lets you configure @product@'s built-in data source. If you want to use the\nbuilt-in data source, skip this section.\n\nThis section demonstrates configuring a MySQL database. If you're using a\ndifferent database, modify the data source and driver snippets as necessary.\n\nIf using JBoss to manage the data source, follow these steps:\n\n1. Add the data source inside the `$JBOSS_HOME/standalone/configuration/standalone.xml` file's the `<datasources>` element.\n\n    ```xml\n    <datasource jndi-name=\"java:jboss/datasources/ExampleDS\" pool-name=\"ExampleDS\" enabled=\"true\" jta=\"true\" use-java-context=\"true\" use-ccm=\"true\">\n        <connection-url>[place the URL to your database here]</connection-url>\n        <driver>[place the driver name here]</driver>\n        <security>\n            <user-name>[place your user name here]</user-name>\n            <password>[place your password here]</password>\n        </security>\n    </datasource>\n    ```\n\n    Make sure to replace the database URL, user name, and password with the appropriate values.\n\n    | **Note:** If the data source `jndi-name` must be changed, edit the `datasource` element in the `<default-bindings>` tag.\n\n2.  Add your driver to the `standalone.xml` file's `<drivers>` element also\n    found within the `<datasources>` element.\n\n    ```xml\n    <drivers>\n        <driver name=\"[name of driver must match name above]\" module=\"com.liferay.portal\">\n            <driver-class>[place your JDBC driver class here]</driver-class>\n        </driver>\n    </drivers>\n    ```\n\n    A final data sources subsystem that uses MySQL should look like this:\n\n    ```xml\n    <subsystem xmlns=\"urn:jboss:domain:datasources:5.0\">\n        <datasources>\n            <datasource jndi-name=\"java:jboss/datasources/ExampleDS\" pool-name=\"ExampleDS\" enabled=\"true\" jta=\"true\" use-java-context=\"true\" use-ccm=\"true\">\n                <connection-url>jdbc:mysql://localhost/lportal</connection-url>\n                <driver>mysql</driver>\n                <security>\n                    <user-name>root</user-name>\n                    <password>root</password>\n                </security>\n            </datasource>\n            <drivers>\n                <driver name=\"mysql\" module=\"com.liferay.portal\"/>\n            </drivers>\n        </datasources>\n    </subsystem>\n    ```\n\n3.  In a\n    [`portal-ext.properties`](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\n    file in your Liferay Home, specify your data source:\n\n    ```properties\n    jdbc.default.jndi.name=java:jboss/datasources/ExampleDS\n    ```\n\nNow that you've configured your data source, the mail session is next.\n\n## Mail Configuration\n\nAs with database configuration, the easiest way to configure mail is to let\n@product@ handle your mail session. If you want to use @product@'s built-in mail\nsession, skip this section and\n[configure the mail session](/docs/7-2/deploy/-/knowledge_base/d/configuring-mail)\nin the Control Panel.\n\nIf you want to manage your mail session with JBoss, follow these steps:\n\n1.  Specify your mail subsystem in the\n    `$JBOSS_HOME/standalone/configuration/standalone.xml` file like this:\n\n    ```xml\n    <subsystem xmlns=\"urn:jboss:domain:mail:3.0\">\n        <mail-session jndi-name=\"java:jboss/mail/MailSession\" >\n            <smtp-server ssl=\"true\" outbound-socket-binding-ref=\"mail-smtp\">\n                <login username=\"[place user name here]\" password=\"[place password here]\"/>\n            </smtp-server>\n       </mail-session>\n    </subsystem>\n    ...\n    <socket-binding-group name=\"standard-sockets\" default-interface=\"public\" port-offset=\"${jboss.socket.binding.port-offset:0}\">\n    ...\n    <outbound-socket-binding name=\"mail-smtp\">\n            <remote-destination host=\"[place SMTP mail host here]\" port=\"[place mail port here]\"/>\n        </outbound-socket-binding>\n    </socket-binding-group>\n    ```\n\n2.  In your `portal-ext.properties` file in Liferay Home, reference your mail\n    session:\n\n    ```properties\n    mail.session.jndi.name=java:jboss/mail/MailSession\n    ```\n\nYou've got mail! Next, you'll deploy @product@ to your JBoss app server.\n\n## Deploy Liferay\n\nNow you're ready to deploy @product@ using the @product@ WAR file.\n\n1.  If the folder `$JBOSS_HOME/standalone/deployments/ROOT.war` already exists\n    in your JBoss installation, delete all of its subfolders and files.\n    Otherwise, create a new folder called\n    `$JBOSS_HOME/standalone/deployments/ROOT.war`.\n\n2.  Unzip the @product@ `.war` file into the `ROOT.war` folder.\n\n3.  To trigger deployment of `ROOT.war`, create an empty file named\n    `ROOT.war.dodeploy` in  your `$JBOSS_HOME/standalone/deployments/` folder.\n    On startup, JBoss detects this file and deploys it as a web application.\n\n4.  Start the JBoss application server by navigating to `$JBOSS_HOME/bin`\n    and running `standalone.bat` or `standalone.sh`.\n\nCongratulations; you've now deployed @product@ on JBoss!\n\n| After deploying @product@, you may see excessive warnings and log messages,\n| such as the ones below, involving `PhaseOptimizer`. These are benign and can\n| be ignored. Make sure to adjust your app server's logging level or log\n| filters to avoid excessive benign log messages.\n|\n|     May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n|     WARNING: Skipping pass gatherExternProperties\n|     May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n|     WARNING: Skipping pass checkControlFlow\n|     May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n|     INFO: pass supports: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, modules, exponent operator (**), async function, trailing comma in param list]\n|     current AST contains: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, exponent operator (**), async function, trailing comma in param list, object literals with spread, object pattern rest]\n"
  },
  {
    "path": "en/deployment/articles-dxp/01-deploying-liferay/08-installing-liferay-on-weblogic.markdown",
    "content": "---\nheader-id: installing-liferay-dxp-on-weblogic-12c-r2\n---\n\n# Installing @product@ on WebLogic 12c R2\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay/installing-liferay-on-an-application-server/installing-on-weblogic.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAlthough you can install @product@ in a WebLogic Admin Server, this isn't\nrecommended. It's a best practice to install web apps, including @product@, in\na WebLogic Managed server. Deploying to a Managed Server lets you start or shut\ndown @product@ more quickly and facilitates transitioning into a cluster\nconfiguration. This article therefore focuses on installing @product@ in\na Managed Server.\n\nBefore getting started, create your Admin and Managed Servers. See\n[WebLogic's documentation](http://www.oracle.com/technetwork/middleware/weblogic/documentation/index.html)\nfor instructions on setting up and configuring Admin and Managed Servers.\n\nAlso familiarize yourself with [preparing for\ninstall](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install).\n\nNow, [download the @product@ WAR and Dependency\nJARs](/docs/7-2/deploy/-/knowledge_base/d/obtaining-product#downloading-the-liferay-war-and-dependency-jars):\n\n-   @product@ WAR file\n-   Dependencies ZIP file\n-   OSGi Dependencies ZIP file\n\n## Configuring WebLogic's Node Manager\n\nWebLogic's Node Manager starts and stops managed servers.\n\nIf you're running WebLogic on a UNIX system other than Solaris or Linux, use the Java Node Manager, instead of the native version of the Node Manager, by configuring these Node Manager properties in the `domains/your_domain_name/nodemanager/nodemanager.properties` file:\n\n```properties\nNativeVersionEnabled=false\n\nStartScriptEnabled=true\n```\n\n| **Note:**   By default, SSL is used with Node Manager. If you want to disable SSL during development, for example, set `SecureListener=false` in your `nodemanager.properties` file.\n\nSee Oracle's [Configuring Java Node Manager](https://docs.oracle.com/middleware/1212/wls/NODEM/java_nodemgr.htm#NODEM173) documentation for details.\n\n## Configuring WebLogic\n\nNext, you must set some variables in two WebLogic startup scripts. These\nvariables and scripts are as follows. Be sure to use `set` instead of `export`\nif you're on Windows.\n\n1.  `your-domain/startWebLogic.[cmd|sh]`: This is the Admin Server's startup\n    script.\n\n2.  `your-domain/bin/startWebLogic.[cmd|sh]`: This is the startup script for\n    Managed Servers.\n\n    Add the following variables to both `startWebLogic.[cmd|sh]` scripts:\n\n    ```bash\n    export DERBY_FLAG=\"false\"\n    export JAVA_OPTIONS=\"${JAVA_OPTIONS} -Dfile.encoding=UTF-8 -Duser.timezone=GMT -da:org.apache.lucene... -da:org.aspectj...\"\n    export MW_HOME=\"/your/weblogic/directory\"\n    export USER_MEM_ARGS=\"-Xmx2560m -Xms2560m\"\n    ```\n\n    | **Important:** For @product@ to work properly, the application server JVM\n    | must use the `GMT` time zone and `UTF-8` file encoding.\n\n    The `DERBY_FLAG` setting disables the Derby server built in to WebLogic, as\n    @product@ doesn't require this server. The remaining settings support\n    @product@'s  memory requirements, UTF-8 requirement, Lucene usage, and\n    Aspect Oriented  Programming via AspectJ. Also make sure to set `MW_HOME` to\n    the directory  containing your WebLogic server on your machine. For example:\n\n    ```bash\n    export MW_HOME=\"/Users/ray/Oracle/wls12210\"\n    ```\n\n3.  Some of the settings are also found in the\n    `your-domain/bin/SetDomainEnv.[cmd|sh]` . Add the following variables\n    (Windows):\n\n    ```bash\n    set WLS_MEM_ARGS_64BIT=-Xms2560m -Xmx2560m\n    set WLS_MEM_ARGS_32BIT=-Xms2560m -Xmx2560m\n    ```\n\n    or on Mac or Linux:\n\n    ```bash\n    WLS_MEM_ARGS_64BIT=\"-Xms2560m -Xmx2560m\"\n    export WLS_MEM_ARGS_64BIT\n\n    WLS_MEM_ARGS_32BIT=\"-Xms2560m -Xmx2560m\"\n    export WLS_MEM_ARGS_32BIT\n    ```\n\n4.  Set the Java file encoding to UTF-8 in\n    `your-domain/bin/SetDomainEnv.[cmd|sh]` by appending `-Dfile.encoding=UTF-8`\n    ahead of your other Java properties:  \n\n    ```bash\n    JAVA_PROPERTIES=\"-Dfile.encoding=UTF-8 ${JAVA_PROPERTIES} ${CLUSTER_PROPERTIES}\"\n    ```\n\n5.  You must also ensure that the Node Manager sets @product@'s memory\n    requirements when starting the Managed Server. In the Admin Server's console\n    UI, navigate to the Managed Server you want to deploy @product@ to and\n    select the *Server Start* tab. Enter the following parameters into the\n    *Arguments* field:\n\n    ```bash\n    -Xmx2560m -Xms2560m -XX:MaxMetaspaceSize=512m\n    ```\n\n    Click *Save* when you're finished.\n\nNext, you'll set some @product@-specific properties for your @product@ installation.\n\n## Setting @product@ Properties\n\nBefore installing @product@, you must set the  [*Liferay\nHome*](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) folder's location via\nthe `liferay.home` property in a\n[`portal-ext.properties`](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\nfile. You can also use this file to override [other @product@\nproperties](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html)\nthat you may need.\n\nFirst, decide which folder you want to serve as Liferay Home. In WebLogic, your\ndomain's folder is generally Liferay Home, but you can choose any folder on your\nmachine. Then create your `portal-ext.properties` file and add the\n`liferay.home` property:\n\n```properties\nliferay.home=/full/path/to/your/liferay/home/folder\n```\n\nRemember to change this file path to the location on your machine that you want\nto serve as Liferay Home.\n\nNow that you've created your `portal-ext.properties` file, you must put it\ninside the @product@ WAR file. Expand the @product@ WAR file and place\n`portal-ext.properties` in the `WEB-INF/classes` folder. Later, you can deploy\nthe expanded archive to WebLogic. Alternatively, you can re-WAR the expanded\narchive for later deployment. In either case, @product@ reads your property\nsettings once it starts up.\n\nIf you need to make any changes to `portal-ext.properties` after @product@\ndeploys, you can find it in your domain's `autodeploy/ROOT/WEB-INF/classes`\nfolder. Note that the `autodeploy/ROOT` folder contains the @product@\ndeployment.\n\nNext, you'll install @product@'s dependencies.\n\n## Installing @product@ Dependencies\n\nYou must now install @product@'s dependencies. Recall that earlier you\ndownloaded two ZIP files containing these dependencies. Install their contents\nnow:\n\n1.  Unzip the Dependencies ZIP file and place its contents in your WebLogic\n    domain's `lib` folder.\n\n2.  Unzip the OSGi Dependencies ZIP file and place its contents in the\n    `Liferay_Home/osgi` folder (create this folder if it doesn't exist).\n\nYou must also add your database's driver JAR file to your domain's `lib` folder.\n\n| **Note:** Although Hypersonic is fine for testing purposes, **do not**\n| use it for production @product@ instances.\n\n| **Note:** The [Liferay DXP Compatibility Matrix](https://web.liferay.com/documents/14/21598941/Liferay+DXP+7.2+Compatibility+Matrix/b6e0f064-db31-49b4-8317-a29d1d76abf7?) specifies supported databases and environments.\n\nNext, you'll configure your database.\n\n## Database Configuration\n\nUse the following procedure if you want WebLogic to manage your [database](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install#using-the-built-in-data-source) for @product@. You can skip this section if you want to use @product@'s built-in\nHypersonic database.\n\n1.  Log in to your AdminServer console.\n\n2.  In the *Domain Structure* tree, find your domain and navigate to *Services*\n    &rarr; *JDBC* &rarr; *Data Sources*.\n\n3.  To create a new data source, click *New*. Fill in the *Name* field with\n    `Liferay Data Source` and the *JNDI Name* field with `jdbc/LiferayPool`.\n    Select your database type and driver. For example, MySQL is *MySQL's Driver\n    (Type 4) Versions:using com.mysql.cj.jdbc.Driver*. Click *Next* to continue.\n\n4.  Accept the default settings on this page and click *Next* to move on.\n\n5.  Fill in your database information for your MySQL database.\n\n6.  If using MySQL, add the text\n    `?useUnicode=true&characterEncoding=UTF-8&\\useFastDateParsing=false` to the\n    URL line and test the connection.\n\n    | **Tip:** For more example URLs, see the `jdbc.default.url` values in [Database Templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates).\n\n    If the connection works, click *Next*.\n\n7.  Select the target for the data source and click *Finish*.\n\n8.  You must now tell @product@ about the JDBC data source. Create a\n    `portal-ext.propreties` file in your Liferay Home directory, and add the\n    line:\n\n    ```propreties\n    jdbc.default.jndi.name=jdbc/LiferayPool\n    ```\n\nAlternatively, you can make the above configuration strictly via properties in\nthe `portal-ext.properties` file. Please see the [Database Templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates) for example properties.\n\nNext, you'll configure your mail session.\n\n## Mail Configuration\n\nIf you want WebLogic to manage your [mail session](/docs/7-2/deploy/-/knowledge_base/d/configuring-mail), use the following procedure.\nIf you want to use Liferay's built-in mail session (recommended), skip this\nsection.\n\n1.  Start WebLogic and log in to your Admin Server's console.\n\n2.  Select *Services* &rarr; *Mail Sessions* from the *Domain Structure* box on\n    the left hand side of your Admin Server's console UI.\n\n3.  Click *New* to begin creating a new mail session.\n\n4.  Name the session *LiferayMail* and give it the JNDI name `mail/MailSession`.\n    Then fill out the *Session Username*, *Session Password*, *Confirm Session\n    Password*, and *JavaMail Properties* fields as necessary for your mail\n    server. See the\n    [WebLogic documentation](http://docs.oracle.com/middleware/1221/wls/FMWCH/pagehelp/Mailcreatemailsessiontitle.html)\n    for more information on these fields. Click *Next* when you're done.\n\n5.  Choose the Managed Server that you'll install @product@ on, and click\n    *Finish*. Then shut down your Managed and Admin Servers.\n\n6.  With your Managed and Admin servers shut down, add the following property to\n    your `portal-ext.properties` file in Liferay Home:\n\n    ```propreties\n    mail.session.jndi.name=mail/MailSession\n    ```\n\n@product@ references your WebLogic mail session via this property setting. If\nyou've already deployed @product@, you can find your `portal-ext.properties`\nfile in your domain's `autodeploy/ROOT/WEB-INF/classes` folder.\n\nYour changes take effect upon restarting your Managed and Admin servers.\n\n## Deploying @product@\n\nAs mentioned earlier, although you can deploy @product@ to a WebLogic Admin\nServer, you should instead deploy it to a WebLogic Managed Server. Dedicating\nthe Admin Server to managing other servers that run your apps is a best\npractice.\n\nFollow these steps to deploy @product@ to a Managed Server:\n\n1.  Make sure the Managed Server you want to deploy @product@ to is shut down.\n\n2.  In your Admin Server's console UI, select *Deployments* from the *Domain\n    Structure* box on the left hand side. Then click *Install* to start a new\n    deployment.\n\n3.  Select the @product@ WAR file or its expanded contents on your file system.\n    Alternatively, you can upload the WAR file by clicking the *Upload your\n    file(s)* link. Click *Next*.\n\n4.  Select *Install this deployment as an application* and click *Next*.\n\n5.  Select the Managed Server you want to deploy @product@ to and click *Next*.\n\n6.  If the default name is appropriate for your installation, keep it.\n    Otherwise, give it a name of your choosing and click *Next*.\n\n7.  Click *Finish*. After the deployment finishes, click *Save* if you want to\n    save the configuration.\n\n8.  Start the Managed Server where you deployed @product@. @product@ precompiles\n    all the JSPs and then launches.\n\nNice work! Now you're running @product@ on WebLogic.\n\n| After deploying @product@, you may see excessive warnings and log messages,\n| such as the ones below, involving `PhaseOptimizer`. These are benign and can\n| be ignored. Make sure to adjust your app server's logging level or log filters\n| to avoid excessive benign log messages.\n|\n|     May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n|     WARNING: Skipping pass gatherExternProperties\n|     May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n|     WARNING: Skipping pass checkControlFlow\n|     May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n|     INFO: pass supports: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, modules, exponent operator (**), async function, trailing comma in param list]\n|     current AST contains: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, exponent operator (**), async function, trailing comma in param list, object literals with spread, object pattern rest]\n"
  },
  {
    "path": "en/deployment/articles-dxp/01-deploying-liferay/09-installing-liferay-on-websphere.markdown",
    "content": "---\nheader-id: installing-liferay-dxp-on-websphere\n---\n\n# Installing @product@ on WebSphere\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay/installing-liferay-on-an-application-server/installing-on-websphere.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nIBM &reg; WebSphere &reg; is a trademark of International Business Machines\nCorporation, registered in many jurisdictions worldwide.\n\n| **Tip:** Throughout this installation and configuration process, WebSphere\n| prompts you to click *Save* to apply changes to the Master Configuration. Do | so intermittently to save your changes.\n\nFor @product@ to work correctly, WebSphere 9 (Fix Pack 11 is the latest) must be\ninstalled. You can find more information about this fix pack\n[here](http://www-01.ibm.com/support/docview.wss?uid=swg24043005).\n\nPlease also note that @product@ doesn't support the WebSphere Application\nLiberty Profile.\n\n| **Important:** Before installing @product@, familiarize yourself with\n| [preparing for install](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install).\n\nNow, [download the @product@ WAR and Dependency\nJARs](/docs/7-2/deploy/-/knowledge_base/d/obtaining-product#downloading-the-liferay-war-and-dependency-jars):\n\n-   @product@ WAR file\n-   Dependencies ZIP file\n-   OSGi Dependencies ZIP file\n\nNote that the [*Liferay Home*\nfolder](docs/7-2/deploy/-/knowledge_base/d/liferay-home) is important to the\noperation of @product@. In Liferay Home, @product@ creates certain files and\nfolders that it needs to run. On WebSphere, Liferay Home is typically `[Install\nLocation]/WebSphere/AppServer/profiles/your-profile/liferay`.\n\nWithout any further ado, get ready to install @product@ in WebSphere!\n\n## Preparing WebSphere for @product@\n\nWhen the application server binaries have been installed, start the *Profile\nManagement Tool* to create a profile appropriate for @product@.\n\n1.  Click *Create...*, choose *Application Server*, and then click *Next*.\n\n2.  Click the *Advanced* profile creation option and then click *Next*. You need\n    the advanced profile to  specify your own values for settings such as the\n    location of the profile and names of the profile, node and host, to assign\n    your own ports, or to optionally choose whether to deploy the administrative\n    console and sample application and also add web-server definitions for IBM\n    HTTP Server. See the WebSphere documentation for more information about\n    these options.\n\n    ![Figure 1: Choose the Advanced profile option to specify your own settings.](../../images-dxp/websphere-01-profile.png)\n\n3.  Check the box *Deploy the administrative console*. This gives you a\n    web-based UI for working with your application server. Skip the default\n    applications. You'd only install these on a development machine. Click\n    *Next*.\n\n4.  Set the profile name and location. Ensure you specify a performance tuning\n    setting other than *Development*, since you're installing a production\n    server. See the WebSphere documentation for more information about\n    performance tuning settings. Click *Next*.\n\n5.  Choose node, server, and host names for your server. These are specific\n    to your environment. Click *Next*.\n\n6.  Administrative security in WebSphere is a way to restrict who has access to\n    the administrative tools. You may want to have it enabled in your\n    environment so that a user name and password are required to administer the\n    WebSphere server. See WebSphere's documentation for more information. Click\n    *Next*.\n\n7.  Each profile needs a security certificate, which comes next in the wizard.\n    If you don't have certificates already, choose the option to generate a\n    personal certificate and a signing certificate and click *Next*.\n\n8.  Once the certificates are generated, set a password for your keystore. Click\n    *Next*.\n\n9.  Now you can customize the ports this server profile uses. Be sure to choose\n    ports that are open on your machine. When choosing ports, the wizard detects\n    existing WebSphere installations and if it finds activity, it increments\n    ports by one.\n\n10. Choose whether to start this profile when the machine starts. Click *Next*.\n\n11. WebSphere ships with IBM HTTP Server, which is a re-branded version of\n    Apache. Choose whether you want a web server definition, so that this JVM\n    receives requests forwarded from the HTTP server. See WebSphere's\n    documentation for details on this. When finished, click *Next*.\n\n12. The wizard then shows you a summary of what you selected, enabling you to\n    keep your choices or go back and change something. When you're satisfied,\n    click *Next*.\n\nWebSphere then creates your profile and finishes with a message telling you the\nprofile was created successfully. Awesome! Your profile is complete. Now there\nare a few things you need to configure in your application server.\n\n![Figure 2: Example of the settings before creating the profile.](../../images-dxp/websphere-02-profile.png)\n\n### Configuring the WebSphere Application Server\n\nIn this version of WebSphere, servlet filters are not initialized on web\napplication startup, but rather, on first access. This can cause problems when\ndeploying certain apps to @product@. To configure servlet filters to initialize\non application startup (i.e., deployment), set the following `webcontainer`\nproperties in your WebSphere application server:\n\n```properties\ncom.ibm.ws.webcontainer.initFilterBeforeInitServlet = true\ncom.ibm.ws.webcontainer.invokeFilterInitAtStartup = true\n```\n\nTo set `webcontainer` properties in the WebSphere application server, follow the\ninstructions  [here in WebSphere's\ndocumentation](http://www-01.ibm.com/support/docview.wss?rss=180&uid=swg21284395).\n\n### Setting up JVM Parameters for Liferay DXP\n\nNext, in the WebSphere profile you created for @product@, you must set an\nargument that supports @product@'s Java memory requirements. You'll modify this\nfile:\n\n```\n[Install Location]/WebSphere/AppServer/profiles/your-profile/config/cells/your-cell/nodes/your-node/servers/your-server/server.xml\n```\n\nAdd `maximumHeapSize=\"2560\"` inside the `jvmEntries` tag. For example:\n\n```xml\n<jvmEntries xmi:id=\"JavaVirtualMachine_1183122130078\" ... maximumHeapSize=\"2560\">\n```\n\n| **Note:** The JVM parameters used here are defaults intended for initial\n| deployment of production systems. Administrators should change the settings to\n| values that best address their specific environments. These must be tuned\n| depending on need.\n\nAdministrators can set the UTF-8 properties in the `<jvmEntries\ngenericJvmArguments=.../>` attribute in `server.xml`. This is required or else\nspecial characters will not be parsed correctly. Set the maximum and minimum\nheap sizes to `2560m` there too. Add the following inside the `jvmEntries` tag:\n\n```xml\n<jvmEntries xmi:id=\"JavaVirtualMachine_1183122130078\" ...genericJvmArguments=\"-Dfile.encoding=UTF-8 -Duser.timezone=GMT -Xms2560m -Xmx2560m\">\n```\n\n| **Important:** For @product@ to work properly, the application server JVM must\n| use the `GMT` time zone and `UTF-8` file encoding.\n\nAlternately, you can set the UTF-8 properties from the WebSphere Admin Console.\n(See below.)\n\n### Removing the secureSessionCookie Tag\n\nIn the same profile, you should delete a problematic `secureSessionCookie` tag\nthat can cause @product@ startup errors. Note that this is just a default\nsetting; once @product@ is installed, you should tune it appropriately based on\nyour usage.\n\nIn `[Install Location]/WebSphere/AppServer/profiles/your-profile/config/cells/your-cell/cell.xml`,\nDelete the `secureSessionCookie` tag containing\n`xmi:id=\"SecureSessionCookie_1\"`.\n\nIf this tag is not removed, an error similar to this may occur:\n\n```\nWSVR0501E: Error creating component com.ibm.ws.runtime.component.CompositionUnitMgrImpl@d74fa901\ncom.ibm.ws.exception.RuntimeWarning: com.ibm.ws.webcontainer.exception.WebAppNotLoadedException: Failed to load webapp: Failed to load webapp: SRVE8111E: The application, LiferayEAR, is trying to modify a cookie which matches a pattern in the restricted programmatic session cookies list [domain=*, name=JSESSIONID, path=/].\n```\n\n## Installing @product@'s Dependencies\n\nYou must now install @product@'s dependencies. Recall that earlier you\ndownloaded two ZIP files containing these dependencies. Install their contents\nnow:\n\n1.  Unzip the Dependencies ZIP file and place its contents in your WebSphere\n    application server's `[Install Location]/WebSphere/AppServer/lib/ext`\n    folder. If you have a JDBC database driver `JAR`, copy it to this location\n    as well.\n\n    | **Note:** The [Liferay DXP Compatibility Matrix](https://web.liferay.com/documents/14/21598941/Liferay+DXP+7.2+Compatibility+Matrix/b6e0f064-db31-49b4-8317-a29d1d76abf7?) specifies supported databases and environments.\n\n2.  From the same archive, copy `portlet.jar`into `[Install\n    Location]/WebSphere/AppServer/javaext` for WebSphere 9.0.0.x. WebSphere\n    already contains an older version of `portlet.jar` which must be overridden\n    at the highest class loader level. The new `portlet.jar` (version 3) is\n    backwards-compatible.\n\n3.  Unzip the OSGi Dependencies ZIP file and place its contents in the\n    `[Liferay Home]/osgi` folder (create this folder if it doesn't exist). This\n    is typically `[Install\n    Location]/WebSphere/AppServer/profiles/your-profile/liferay/osgi`.\n\n### Ensuring that @product@'s portlet.jar is loaded first\n\nIn addition to placing the `portlet.jar` in the correct folder, you must\nconfigure the `config.ini` file so that it is loaded first. Navigate to\n`/IBM/WebSphere/AppServer/configuration/config.ini`.\n\n1.  Find the property `com.ibm.CORBA,com.ibm`\n\n2.  Insert the property\n    `javax.portlet,javax.portlet.filter,javax.portlet.annotations`\n    after `com.ibm.CORBA` and before `com.ibm`.\n\n3.  Save the file.\n\nOnce you've installed these dependencies and configured the `config.ini` file,\nstart the server profile you created for @product@. Once it starts, you're ready\nto configure your database.\n\n## Database Configuration\n\nIf you want WebSphere to manage the database connections, follow the\ninstructions below. Note this is not necessary if you plan to use @product@'s\nstandard database configuration; in that case, skip this section. See the [Using\nthe Built-in Data\nSources](/docs/7-2/deploy/-/knowledge_base/d/preparing-for-install#using-the-built-in-data-source)\nsection for more article.\n\nYou'll set your database information in @product@'s setup wizard after the\ninstall.\n\n| **Note:** Although @product@'s embedded database is fine for testing purposes,\n| you **should not** use it for production @product@ instances.\n\n![Figure 3: WebSphere JDBC providers](../../images-dxp/websphere-jdbc-providers.png)\n\n1.  Start WebSphere.\n\n2.  Open the Administrative Console and log in.\n\n3.  Click *Resources &rarr; JDBC Providers*.\n\n4.  Select a scope and then click *New*.\n\n5.  Select your database type, provider type, and implementation type. If you\n    select a predefined database, the wizard fills in the name and description\n    fields for you. If the database you want to use isn't listed, select\n    *User-defined* from the *Database type* field and then fill in the\n    *Implementation Class Name*. For example, if you use MySQL, select *Database\n    type* &rarr; *User-defined*, and then enter\n    `com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource` in\n    *Implementation Class Name*. Click *Next* when you are finished.\n\n6.  Clear any text in the class path settings. You already copied the necessary\n    JARs to a location on the server's class path. Click *Next*.\n\n7.  Review your settings and click *Finish*. The final configuration should look\n    like this:\n\n    ![Figure 4: Completed JDBC provider configurations.](../../images-dxp/websphere-03.png)\n\n8.  Click your new provider configuration when it appears in the table, and then\n    click *Data Sources* under *Additional Properties*. Click *New*.\n\n9.  Enter *liferaydatabasesource* in the *Data source name* field and\n    `jdbc/LiferayPool` in the *JNDI name* field. Click *Next*.\n\n10. Click *Next* in the remaining screens of the wizard to accept the default\n    values. Then review your changes and click *Finish*.\n\n11. Click the data source when it appears in the table and then click *Custom\n    Properties*. Now click the *Show Filter Function* button. This is the second\n    from last of the small icons under the *New* and *Delete* buttons.\n\n12. Type *user* into the search terms and click *Go*.\n\n    ![Figure 5: Modifying data source properties in WebSphere](../../images-dxp/websphere-database-properties.png)\n\n13. Select the *user* property and give it the value of the user name to your\n    database. Click *OK* and save to master configuration.\n\n14. Do another filter search for the *url* property. Give this property a value\n    that points to your database. For example, a MySQL URL would look like this:\n\n    ```properties\n    jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT&useFastDateParsing=false\n    ```\n\n    | **Tip:** For more example URLs, see the `jdbc.default.url` values in\n    | [Database Templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates).\n\n    Click *OK* and save to master configuration.\n\n15. Do another filter search for the *password* property. Enter the password for\n    the user ID you added earlier as the value for this property. Click *OK* and\n    save to master configuration.\n\n16. Go back to the data source page by clicking it in the breadcrumb trail.\n    Click the *Test Connection* button. It should connect successfully.\n\nOnce you've set up your database, you can set up your mail session.\n\n## Mail Configuration\n\nIf you want WebSphere to manage your mail sessions, use the following procedure.\nIf you want to use @product@'s built-in mail sessions, you can skip this\nsection. See the [Configuring\nMail](/docs/7-2/deploy/-/knowledge_base/d/configuring-mail) article on how to\nuse @product@'s built-in mail sessions.\n\n### Creating a WebSphere-Managed Mail Session (Optional)\n\n1.  Click *Resources &rarr; Mail &rarr; Mail Providers*.\n\n2.  Click the Built-In Mail Provider for your node and server.\n\n3.  Click *Mail Sessions* and then click the *New* button.\n\n4.  Give your mail session a name of *liferaymail* and a JNDI name of\n    `mail/MailSession`. Fill in the correct information for your mail server in\n    the sections *Outgoing Mail Properties* and *Incoming Mail Properties*.\n    Click *OK* and then save to the master configuration.\n\n5.  Click your mail session when it appears in the table and select *Custom\n    Properties* under the *Additional Properties* section. Set any other\n    JavaMail properties required by your mail server, such as the protocol,\n    ports, whether to use SSL, and so on.\n\n6.  Click *Security &rarr; Global Security* and de-select *Use Java 2 security\n    to restrict application access to local resources* if it is selected. Click\n    *Apply*.\n\nNote that you may also need to retrieve a SSL certificate from your mail server\nand add it to WebSphere's trust store. See WebSphere's documentation for\ninstructions on this.\n\n### Verifying WebSphere Mail Provider\n\nTo validate that the mail session has been configured correctly, there are a\nnumber of ways to test this once the WAR has been deployed, the server has\nstarted, and the user has signed in as the system administrator. One quick way\nto validate is to create a new user with a valid email account. The newly\ncreated user should receive an email notification. The logs should display that\nthe SMTP server has been pinged with the correct port number listed.\n\n## Enable Cookies for HTTP Sessions\n\nWebSphere restricts cookies to HTTPS sessions by default. If you're\nusing HTTP instead, this prevents users from signing in to @product@ and\ndisplays the following error in the console:\n\n```\n20:07:14,021 WARN  [WebContainer : 1][SecurityPortletContainerWrapper:341]\nUser 0 is not allowed to access URL http://localhost:9081/web/guest/home and\nportlet com_liferay_login_web_portlet_LoginPortlet\n```\n\nThis occurs because @product@ can't use the HTTPS cookie when you use HTTP. The\nend result is that new sessions are created on each page refresh. Follow these\nsteps to resolve this issue in WebSphere:\n\n1.  Click *Application Servers* &rarr; *server1* &rarr; *Session Management*\n   &rarr; Enable Cookies\n\n2.  De-select *Restrict cookies to HTTPS sessions*\n\n3.  Click *Apply*\n\n4.  Click *Save*\n\n## Enable UTF-8\n\nIf you did not add the `-Dfile.encoding=UTF-8` property in the `server.xml`, you\ncan do so in the Administrative Console.\n\n1.  Click *Application Servers* &rarr; *server1* &rarr; *Process definition*.\n\n2.  Click *Java Virtual Machine* under *Additional Properties*.\n\n3.  Enter `-Dfile.encoding=UTF-8` in the *Generic JVM arguments* field.\n\n4.  Click *Apply* and then *Save* to master configuration.\n\nOnce the changes have been saved, @product@ can parse special characters if\nthere is localized content.\n\n## Deploy @product@\n\nNow you're ready to deploy @product@!\n\n1.  In WebSphere's administrative console, click *Applications* &rarr; *New\n    Application* &rarr; *New Enterprise Application*.\n\n2.  Browse to the @product@ `.war` file, select it, and click *Next*.\n\n3.  Leave *Fast Path* selected and click *Next*. Ensure that *Distribute\n    Application* has been checked and click *Next* again.\n\n4.  Choose the WebSphere runtimes and/or clusters where you want @product@\n    deployed. Click *Next*.\n\n5.  Select the virtual host to deploy @product@ on and click *Next*.\n\n6.  Map @product@ to the root context (`/`) and click *Next*.\n\n7.  Select the *metadata-complete attribute* setting that you want to use and\n    click *Next*.\n\n8.  Ensure that you have made all the correct choices and click *Finish*. When\n    @product@ has installed, click *Save to Master Configuration*.\n\n   ![Figure 6: Review your deployment options before deploying.](../../images-dxp/websphere-deploy-dxp.png)\n\nYou've now installed @product@!\n\n## Setting the JDK Version for Compiling JSPs\n\n@product@ requires that its JSPs are compiled to the Java 8 bytecode format. To\nensure that WebSphere does this, shut down WebSphere after you've deployed the\n@product@ `.war` file. Navigate to the `WEB_INF` folder and add the following\nsetting to the `ibm-web-ext.xml` or in most cases the `ibm-web-ext.xmi` file:\n\n```xml\n<jsp-attribute name=\"jdkSourceLevel\" value=\"18\" />\n```\n\nThe exact path to the `ibm-web-ext.xmi` file depends on your WebSphere\ninstallation location and @product@ version, but here's an example:\n\n```bash\n/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/config/cells/localhostNode01Cell/applications/liferayXX.ear/deployments/liferayXX/liferayXX.war/WEB-INF/ibm-web-ext.xmi\n```\n\nNote that the @product@ `.war` comes pre-packaged with the `ibm-web-ext.xmi`\nfile; this format is functionally the same as `.xml` and WebSphere recognizes\nboth formats. For more general information on how WebSphere compiles JSPs see\nIBM's official documentation for [WebSphere Application Server\n9.0.0.x](https://www.ibm.com/support/knowledgecenter/en/SSEQTP_9.0.0/com.ibm.websphere.base.doc/ae/rweb_jspengine.html).\n\n## Start @product@\n\n1.  If you plan to use @product@'s [setup\n\n    wizard](/docs/7-2/deploy/-/knowledge_base/d/installing-product#using-the-setup-wizard),\n    skip to the next step. If you wish to use WebSphere's data source and mail\n    session, create a file called `portal-ext.properties` in your Liferay Home\n    folder. Place the following configuration in the file:\n\n    ```properties\n    jdbc.default.jndi.name=jdbc/LiferayPool\n    mail.session.jndi.name=mail/MailSession\n    setup.wizard.enabled=false\n    ```\n2.  In the WebSphere administrative console, navigate to *Enterprise\n    Applications*, select the @product@ application, and click *Start*. While\n    @product@ is starting, WebSphere displays a spinning graphic.\n\n3.  In @product@'s setup wizard, select and configure your database type. Click\n    *Finish* when you're done. @product@ then creates the tables it needs in the\n    database.\n\nCongratulations! You've installed @product@ on WebSphere!\n\n| After deploying @product@, you may see excessive warnings and log messages, such\n| as the ones below, involving `PhaseOptimizer`. These are benign and can be\n| ignored. Make sure to adjust your app server's logging level or log filters to\n| avoid excessive benign log messages.\n|\n|     May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n|     WARNING: Skipping pass gatherExternProperties\n|     May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n|     WARNING: Skipping pass checkControlFlow\n|     May 02, 2018 9:12:27 PM com.google.javascript.jscomp.PhaseOptimizer$NamedPass process\n|     INFO: pass supports: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, modules, exponent operator (**), async function, trailing comma in param list]\n|     current AST contains: [ES3 keywords as identifiers, getters, reserved words as properties, setters, string continuation, trailing comma, array pattern rest, arrow function, binary literal, block-scoped function declaration, class, computed property, const declaration, default parameter, destructuring, extended object literal, for-of loop, generator, let declaration, member declaration, new.target, octal literal, RegExp flag 'u', RegExp flag 'y', rest parameter, spread expression, super, template literal, exponent operator (**), async function, trailing comma in param list, object literals with spread, object pattern rest]\n"
  },
  {
    "path": "en/deployment/articles-dxp/01-deploying-liferay/11-activating-liferay-dxp.markdown",
    "content": "---\nheader-id: activating-liferay-dxp\n---\n\n# Activating Liferay DXP\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/setting-up-liferay/activating-liferay-dxp.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThere are two ways to activate your Liferay DXP instance: \n\n-   With an XML activation key that you request and receive from Liferay \n    Support. \n\n-   Online activation through Liferay Connected Services (LCS). Liferay DXP 7.0 \n    introduced LCS as a way to activate Liferay DXP instances. LCS can also \n    install fix packs, monitor each instance's performance, and help \n    administrators automatically manage Liferay DXP subscriptions. See the \n    [LCS documentation](/docs/7-1/deploy/-/knowledge_base/d/managing-liferay-dxp-with-liferay-connected-services) \n    for instructions on activating your instances with LCS. \n\n| **Note:** You must use LCS for activation of Elastic subscriptions. Otherwise,\n| you don't have to use LCS for activation. You can instead request an XML\n| activation key from Liferay Support.\n\n"
  },
  {
    "path": "en/deployment/articles-dxp/01-deploying-liferay/13-trial-plugin-installation.markdown",
    "content": "---\nheader-id: trial-plugin-installation\n---\n\n# Trial Plugin Installation\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/installing-and-managing-apps/installing-apps/accessing-ee-plugins-during-a-trial-period.html) and is no longer maintained here.</span>\n</aside>\n\nFor Liferay customers who are evaluating @product@ on a trial basis, **the\nplugins can be accessed from within the *Apps* &rarr; *Store* (i.e.,\nMarketplace) section of the Control Panel in your product installation**. \n\n## Installation Process\n\nFollow the steps below to install a trial plugin:\n\n1.  Register a `liferay.com` account (LRDC) account by visiting Liferay's home\n    page (if necessary). Do this by clicking *Sign In/Create Account* button\n    from the top right Profile button.\n\n    ![Figure 1: Hover over the Profile button and click *Sign In/Create Account*.](../../images-dxp/liferay-com-sign-in.png)\n\n2.  Start your @product@ instance (trial license is OK).\n\n3.  After signing in as an Admin in your @product@ trial server, go to the\n    Control Panel &rarr; *Apps* &rarr; *Store* and sign in to the Marketplace\n    using your `liferay.com` (LRDC) account credentials. Authorize Marketplace\n    to access your local account. \n\n    ![Figure 2: Click the *Store* link and authorize Marketplace to access your local account.](../../images-dxp/dxp-store-link.png)\n\n4.  Once signed into the Store, click on the *Purchased* link, and then click on\n    the *EE* tab.\n\n    Here you can see a list of @product@ plugins that are installed, as well as\n    options to update or install certain plugins. See [Using the Liferay\n    Marketplace](/docs/7-2/user/-/knowledge_base/u/using-the-liferay-marketplace)\n    for details. \n\nNext are answers to some common questions. \n\n## FAQ\n\n**Q:** Where are the *@product@ Trial Plugins*?\n\n**A:** There is no such thing. The @product@ plugins in Liferay Marketplace are\nthe same ones that you get to try out with your @product@ trial license for your\nportal. The @product@ license (trial or official @product@ subscriber) gives you\naccess to the @product@ plugins. Also, there is no difference code-wise or\nrelease-wise between a @product@ trial installation and a regular @product@\nnon-trial installation. The only difference is the license.\n\n**Q:** Why can't I go to liferay.com/marketplace? Why can't I *purchase* from\nthe Marketplace site?\n\n**A:** DXP trial users must use the Marketplace from within the product's\nControl Panel (instructions above). You do not need to *purchase* any DXP\nplugins because if you access Marketplace from within the Control Panel,\nMarketplace sees that you have a DXP license installed and gives access to DXP\nplugins. Official DXP subscription customers (i.e., non trial) can log into\n`liferay.com` with their designated DXP subscriber login and access all DXP\nplugins through the Marketplace website. \n\n**Q:** Why are the plugins under the Purchased tab? If I click on the *DXP \nMarketplace* link, it does not let me get the DXP plugins.\n\n**A:** Once you're signed into the Store, click on the *Purchased* tab, then \nclick on the *EE* tab.\n\n**Q:** What happens when DXP trial customers become official Liferay Digital\nExperience subscribers?\n\n**A:** They can still complete the above process, or they can also visit the\n[Liferay Marketplace website](https://www.liferay.com/marketplace).\n\n**Q:** Do DXP trial customers get the DXP source code?\n\n**A:** No, they can only install the plugin. The DXP source code becomes\navailable once they are official @product@ Enterprise subscribers.\n\n**Q:** Can this process of installing DXP plugins be used from Liferay Portal CE\n(Community Edition)?\n\n**A:** No, the Marketplace must detect that you are running @product@.\n"
  },
  {
    "path": "en/deployment/articles-dxp/02-configuring-liferay/04-liferay-portal-clustering/04-clustering-search.markdown",
    "content": "---\nheader-id: clustering-search\n---\n\n# Clustering Search\n\n[TOC levels=1-4]\n\nSearch should always run on a separate environment from your @product@ server.\n@product@ supports \n[Elasticsearch](/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch), \nand \n[Solr](/docs/7-2/deploy/-/knowledge_base/d/installing-solr). \nBoth can also be clustered. \n\nFor more information on how to cluster Elasticsearch, see \n[Elasticsearch's distributed cluster setup](https://www.elastic.co/guide/en/elasticsearch/guide/current/distributed-cluster.html). \n\nOnce @product@ servers have been properly configured as a cluster and the same\nfor Elasticsearch, change @product@ from *embedded* mode to *remote* mode. On\nthe first connection, the two sets of clustered servers communicate with each\nother the list of all IP addresses; in case of a node going down, the proper\nfailover protocols enable. Queries and indexes can continue to be sent for all\nnodes.\n\nFor more information on how to cluster Solr, see \n[Apache Solr Cloud](https://cwiki.apache.org/confluence/display/solr/SolrCloud)\ndocumentation. \n\nOnce @product@ servers have been properly configured as a cluster, deploy the\nLiferay Connector to Solr on all nodes. (This app is available for download\nfrom Liferay Marketplace) Create a Solr Cloud (cluster) managed by _Apache Solr\nZookeeper_. Connect the @product@ cluster to Zookeeper and finish the final\nconfigurations to connect the two clusters.\n"
  },
  {
    "path": "en/deployment/articles-dxp/02-configuring-liferay/08-dxp-configuration-and-tuning/01-intro.markdown",
    "content": "---\nheader-id: tuning-guidelines\n---\n\n# Tuning Guidelines\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/setting-up-liferay/tuning-liferay.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAlthough setting names may differ, these concepts apply to most application\nservers. To keep things simple, Tomcat is used as the example. For other\napplication servers, consult the provider's documentation for specific settings.\n\nHere are the tuning topics:\n\n- Database Connection Pool\n- Deactivating Development Settings in the JSP Engine\n- Thread Pool\n\n## Database Connection Pool\n\nThe database connection pool should be roughly 30-40% of the thread pool size.\nIt provides a connection whenever @product@ needs to retrieve data from the\ndatabase (e.g., user login). If the pool size is too small, requests queue in\nthe server waiting for database connections. If the size is too large, however,\nidle database connections waste resources. As with thread pools, monitor\nthese settings and adjust them based on your performance tests.\n\nIn Tomcat, the connection pools are configured in the Resource elements in\n`$CATALINA_HOME/conf/Catalina/localhost/ROOT.xml`. Liferay Engineering tests\nwith this configuration:\n\n    <Resource auth=\"Container\"         \n        description=\"Digital Enterprise DB Connection\"   \n        driverClass=\"[place the driver class name here]\"   \n        maxPoolSize=\"75\"   \n        minPoolSize=\"10\"           \n        acquireIncrement=\"5\"   \n        name=\"jdbc/LiferayPool\"  \n        user=\"[place your user name here]\"   \n        password=\"[place your password here]\"           \n        factory=\"org.apache.naming.factory.BeanFactory\"\n        type=\"com.mchange.v2.c3p0.ComboPooledDataSource\"\n        jdbcUrl=\"[place the URL to your database here]\"/>\n\nThis configuration starts with 10 threads and increments by 5 as needed to a\nmaximum of 75 connections in the pool.\n\nThere are a variety of database connection pool providers, including DBCP, C3P0,\nHikariCP, and Tomcat. You may also configure the Liferay JDBC settings in your\n[`portal-ext.properties`](https://docs.liferay.com/ce/portal/7.2-latest/propertiesdoc/portal.properties.html)\nfile. For example JDBC connection values, please see [Database Templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates)\n\n## Deactivating Development Settings in the JSP Engine\n\nMany application servers' JSP Engines are in development mode by default.\nDeactivate these settings prior to entering production:\n\n**Development mode:** This makes the JSP container poll the file system for\nchanges to JSP files. Since you won't change JSPs on the fly like this in\nproduction, turn off this mode.\n\n**Mapped File:** Generates static content with one print statement versus one\nstatement per line of JSP text.\n\nTo disable these in Tomcat, for example, update the\n`$CATALINA_HOME/conf/web.xml` file's JSP servlet configuration to this:\n\n    <servlet>   \n        <servlet-name>jsp</servlet-name>\n        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>   \n        <init-param>    \n            <param-name>development</param-name>    \n            <param-value>false</param-value>   \n        </init-param>   \n        <init-param>    \n            <param-name>mappedFile</param-name>    \n            <param-value>false</param-value>   \n        </init-param>   \n        <load-on-startup>3</load-on-startup>\n    </servlet>\n\nDevelopment mode and mapped files are disabled.\n\n## Thread Pool\n\nEach request to the application server consumes a worker thread for the duration\nof the request. When no threads are available to process requests, the request\nis queued to wait for the next available worker thread. In a finely tuned\nsystem, the number of threads in the thread pool are balanced with the total\nnumber of concurrent requests. There should not be a significant amount of\nthreads left idle to service requests.\n\nUse an initial thread pool setting of 50 threads and then monitor it within your\napplication server's monitoring consoles. You may wish to use a higher number\n(e.g., 250) if your average page times are in the 2-3 second range. Too few\nthreads in the thread pool might queue excessive requests; too many threads can\ncause excessive context switching.\n\nIn Tomcat, the thread pools are configured in the\n`$CATALINA_HOME/conf/server.xml` file's `Connector` element. The\n[Apache Tomcat documentation](https://tomcat.apache.org/tomcat-9.0-doc/config/http.html)\nprovides more details. Liferay Engineering tests with this configuration:\n\n    <Connector maxThreads=\"75\" minSpareThreads=\"50\"\n        maxConnections=\"16384\" port=\"8080\"     \n        connectionTimeout=\"600000\" redirectPort=\"8443\"\n        URIEncoding=\"UTF-8\"  socketBuffer=\"-1\"     \n        maxKeepAliveRequests=\"-1\" address=\"xxx.xxx.xxx.xxx\"/>\n\nAdditional tuning parameters around Connectors are available, including the\nconnector types, the connection timeouts, and TCP queue. Consult your\napplication server's documentation for further details.\n"
  },
  {
    "path": "en/deployment/articles-dxp/02-configuring-liferay/08-dxp-configuration-and-tuning/02-jvm-tuning.markdown",
    "content": "---\nheader-id: java-virtual-machine-tuning\n---\n\n# Java Virtual Machine Tuning\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/setting-up-liferay/tuning-your-jvm.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nJava Virtual Machine (JVM) tuning primarily focuses on adjusting the garbage\ncollector and the Java memory heap. We used Oracle's 1.8 JVM for the reference\narchitecture. You may choose other supported JVM versions and implementations.\nPlease consult the \n[Liferay DXP Compatibility Matrix](https://web.liferay.com/group/customer/dxp/support/compatibility-matrix)\nfor additional compatible JVMs.\n\nHere are the JVM tuning topics:\n\n- [Garbage Collector](#garbage-collector)\n- [Code Cache](#code-cache)\n- [Java Heap](#java-heap)\n- [JVM Advanced Options](#jvm-advanced-options)\n\nGarbage collection is first. \n\n## Garbage Collector\n\nChoosing the appropriate garbage collector (GC) helps improve the responsiveness\nof your @product@ deployment. Use the concurrent low pause collectors:\n\n    -XX:+UseParNewGC -XX:ParallelGCThreads=16 -XX:+UseConcMarkSweepGC\n    -XX:+CMSParallelRemarkEnabled -XX:+CMSCompactWhenClearAllSoftRefs\n    -XX:CMSInitiatingOccupancyFraction=85 -XX:+CMSScavengeBeforeRemark\n \nYou may choose from other available GC algorithms, including parallel throughput\ncollectors and G1 collectors. Start tuning using parallel collectors in the new\ngeneration and concurrent mark sweep (CMS) in the old generation.\n\n**Note:** the `ParallelGCThreads` value (e.g., `ParallelGCThreads=16`) varies \nbased on the type of CPUs available. Set the value according to CPU\nspecification. On Linux machines, report the number of available CPUs by running\n`cat /proc/cpuinfo`.\n\n**Note:** There are additional \"new\" algorithms like G1, but Liferay\nEngineering's tests for G1 indicated that it does not improve performance. Since\nyour application performance may vary, you should add G1 to your testing and\ntuning plans. \n\n## Code Cache\n\nJava's just-in-time (JIT) compiler generates native code to improve performance.\nThe default size is `48m`. This may not be sufficient for larger applications.\nToo small a code cache reduces performance, as the JIT isn't able to optimize\nhigh frequency methods. For @product@,  start with `64m` for the initial code\ncache size.\n\n    -XX:InitialCodeCacheSize=64m -XX:ReservedCodeCacheSize=96m\n \nExamine the efficacy of the parameter changes by adding the following logging\nparameters:\n\n    -XX:+PrintCodeCache -XX:+PrintCodeCacheOnCompilation\n\n## Java Heap\n\nWhen most people think about tuning the Java memory heap, they think of setting\nthe maximum and minimum memory of the heap. Unfortunately, most deployments\nrequire far more sophisticated heap tuning to obtain optimal performance,\nincluding tuning the young generation size, tenuring durations, survivor spaces,\nand many other JVM internals.\n\nFor most systems, it's best to start with at least the following memory\nsettings:\n\n    -server -XX:NewSize=700m -XX:MaxNewSize=700m -Xms2048m -Xmx2048m -XX:MetaspaceSize=512m\n    -XX:MaxMetaspaceSize=512m -XX:SurvivorRatio=6 -XX:TargetSurvivorRatio=9 -XX:MaxTenuringThreshold=15\n\nOn systems that require large heap sizes (e.g., above 4GB), it may be beneficial\nto use large page sizes. You can activate large page sizes like this:\n\n    -XX:+UseLargePages -XX:LargePageSizeInBytes=256m\n \nYou may choose to specify different page sizes based on your application\nprofile.\n\n**Note:** To use large pages in the JVM, you must configure your underlying\noperating system to activate them. In Linux, run `cat /proc/meminfo` and look at\n\"huge page\" items. \n\n| **Caution:** Avoid allocating more than 32GB to your JVM heap. Your heap size\n| should be commensurate with the speed and quantity of available CPU resources.\n\n## JVM Advanced Options\n\nThe following advanced JVM options were also applied in the Liferay benchmark\nenvironment:\n\n    -XX:+UseLargePages -XX:LargePageSizeInBytes=256m \n    -XX:+UseCompressedOops -XX:+DisableExplicitGC -XX:-UseBiasedLocking \n    -XX:+BindGCTaskThreadsToCPUs -XX:UseFastAccessorMethods\n\nPlease consult your JVM documentation for additional details on advanced JVM\noptions.\n\nCombining the above recommendations together, makes this configuration:\n\n    -server -XX:NewSize=1024m -XX:MaxNewSize=1024m -Xms4096m\n    -Xmx4096m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m\n    -XX:SurvivorRatio=12 -XX:TargetSurvivorRatio=90\n    -XX:MaxTenuringThreshold=15 -XX:+UseLargePages \n    -XX:LargePageSizeInBytes=256m -XX:+UseParNewGC \n    -XX:ParallelGCThreads=16 -XX:+UseConcMarkSweepGC \n    -XX:+CMSParallelRemarkEnabled -XX:+CMSCompactWhenClearAllSoftRefs\n    -XX:CMSInitiatingOccupancyFraction=85 -XX:+CMSScavengeBeforeRemark \n    -XX:+UseLargePages -XX:LargePageSizeInBytes=256m\n    -XX:+UseCompressedOops -XX:+DisableExplicitGC -XX:-UseBiasedLocking\n    -XX:+BindGCTaskThreadsToCPUs -XX:+UseFastAccessorMethods\n    -XX:InitialCodeCacheSize=32m -XX:ReservedCodeCacheSize=96m\n \n| **Caution:** The above JVM settings should formulate a starting point for your\n| performance tuning. Every system's final parameters vary due to many factors,\n| including number of current users and transaction speed.\n\nMonitor the garbage collector statistics to ensure your environment has\nsufficient allocations for metaspace and also for the survivor spaces. Using the\nconfiguration above in the wrong environment could result in dangerous runtime\nscenarios like out of memory failures. Improperly tuned survivor spaces also\nlead to wasted heap space.\n"
  },
  {
    "path": "en/deployment/articles-dxp/03-installing-a-search-engine/03-enterprise-search/01-les-intro.markdown",
    "content": "---\nheader-id: installing-liferay-enterprise-search\n---\n\n# Installing Liferay Enterprise Search\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/using-search/liferay-enterprise-search.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nA Liferay Enterprise Search (LES) subscription gets you additional features\nbeyond what's available out of the box with your @product@ subscription. It\nincludes\n\n- Liferay Enterprise Search Security\\*\n- Liferay Enterprise Search Monitoring\n- Liferay Enterprise Search Learning to Rank\n\n\\* A LES subscription is not necessary if using Elasticsearch 7 via the _Liferay Connector to\n    Elasticsearch 7_as X-Pack's security features are bundled. See the [LES\n    compatibility matrix](https://help.liferay.com/hc/en-us/articles/360016511651#Liferay-Enterprise-Search)\n    for more information.\n\nX-Pack is an \n[Elasticsearch extension](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/setup-xpack.html)\nfor securing and monitoring Elasticsearch clusters. If you use Elasticsearch,\nyou should secure it with X-Pack. The security features of X-Pack include\nauthenticating access to the Elasticsearch cluster's data and encrypting\nElasticsearch's internal and external communications. These are necessary\nsecurity features for most production systems. A LES subscription gets you\naccess to two connectors if you're using Elasticsearch 6: monitoring and\nsecurity. Elasticsearch 7 bundles these security features, and Liferay has\nfollowed suit. Therefore, security is bundled with the Liferay Connector\nto Elasticsearch 7, and no LES subscription is necessary. Because of this, the\ndocumentation for\n[installing Liferay Enterprise Search Security](/docs/7-2/deploy/-/knowledge_base/d/installing-liferay-enterprise-search-security) \non @product@ has been moved from the LES documentation section (this section) to\nthe\n[Elasticsearch](/docs/7-2/deploy/-/knowledge_base/d/elasticsearch) \ninstallation and configuration guide. Contact\n[Liferay's Sales department for more information](https://www.liferay.com/contact-us#contact-sales).\n\nHere's an overview of using the LES applications with @product@:\n\n1.  Get an [Enterprise Search subscription](https://help.liferay.com/hc/en-us/articles/360014400932).\n\n2.  You'll receive a license for X-Pack monitoring. Install it on your\n    Elasticsearch servers.\n\n    **Note:** If using Elasticsearch 6, you'll also need a LES subscription for\n    X-Pack security.\n\n3.  Download and install the Liferay Enterprise Search apps you purchased. Find\n    them in the [Help Center Downloads\n    page](https://customer.liferay.com/en/downloads), choosing Enterprise Search\n    from the Product drop-down menu.\n\n4.  Configure the connectors with the proper credentials, encryption\n    information, and settings.\n\n5.  Restart Elasticsearch. These steps require a full cluster restart.\n\nMore detailed installation instructions are available in the article for each\nLES feature.\n\nElastic's documentation explains additional configuration options, features,\nand the architecture of\n[X-Pack](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/configuring-security.html). \n\n| **Note:** Out of the box, X-Pack comes with a [30-day\n| trial](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/start-trial.html).\n| This can be useful if there's a delay between your subscription and receipt of\n| your production X-Pack license.\n\nNow configure security, monitoring, and/or Learning to Rank, depending on your\nneeds.\n"
  },
  {
    "path": "en/deployment/articles-dxp/03-installing-a-search-engine/03-enterprise-search/02-monitoring/01-monitoring-intro.markdown",
    "content": "---\nheader-id: installing-liferay-enterprise-search-monitoring\n---\n\n# Installing Liferay Enterprise Search Monitoring\n\n[TOC levels=1-4]\n\nFirst configure security if you're using X-Pack's security features. Then come\nback here for instructions on installing and configuring Kibana (the monitoring\nserver) with X-Pack so that Elasticsearch (secured with X-Pack), Kibana\n(secured with X-Pack), and @product@ can communicate effortlessly and securely.\nA Liferay Enterprise Search (LES) subscription is necessary for this\nintegration.  Contact \n[Liferay's Sales department for more information](https://www.liferay.com/contact-us#contact-sales).\n\n| **The same monitoring connector is used for Elasticsearch 6 and 7**: When first\n| created, the Liferay Enterprise Search Monitoring app  was\n| intended to be used only with Elasticsearch 6. However, testing revealed that\n| the same connector works with Elasticsearch 7, so a new connector is not\n| necessary if you're using Elasticsearch 7.\n\nTo install X-Pack monitoring,\n\n1.  Tell Elasticsearch to enable data collection.\n\n2.  Download and install Kibana.\n\n3.  Configure Kibana with the proper security settings.\n\n4.  Install the Liferay Enterprise Search Monitoring app.\n\n5.  Configure the connector to communicate with Elasticsearch.\n\nThis document assumes you're enabling security (with authentication and encrypted communication) *and* monitoring for\nElasticsearch 7, but differences in the process for Elasticsearch 6 are noted\nwhere necessary.\n\n## Enable Encrypting Communication (TLS/SSL) in Elasticsearch and in @product@\n\nStart by following the steps in this [article](/docs/7-2/deployment/-/knowledge_base/u/installing-liferay-enterprise-search-security) to enable TLS/SSL in your Elasticsearch and @product@ installation.\n\nThen continue by enabling data collection in Elasticsearch.\n\n## Enable Data Collection\n\nMonitoring is enabled on Elasticsearch by default, but data collection isn't.\nEnable data collection by adding this line to `elasticsearch.yml`.\n\n```yaml\nxpack.monitoring.collection.enabled: true\n```\n\nNow install Kibana.\n\n## Install Kibana\n\nMake sure to install the correct version of Kibana. Check the \n[Liferay Enterprise Search compatibility matrix](https://help.liferay.com/hc/en-us/articles/360016511651#Liferay-Enterprise-Search)\nfor details.\n\n1.  [Download Kibana](https://www.elastic.co/downloads/kibana)\n    and extract it. The root folder is referred to as *Kibana Home*.\n\n2.  Tell Kibana where to send monitoring data by setting Elasticsearch's URL in\n    `kibana.yml`:\n\n    ```yaml\n    elasticsearch.hosts: [ \"https://localhost:9200\" ]\n    ```\n\n    On 6.5 and below, use\n\n    ```yaml\n    elasticsearch.url: \"https://localhost:9200\"\n    ```\n\n    If TLS/SSL is not enabled on Elasticsearch, this is an `http` URL, otherwise use `https`.\n\n3.  If not using X-Pack security, start Kibana by opening a command prompt to \n    Kibana Home and entering this command:\n\n    ```bash\n    ./bin/kibana\n    ```\n\nIf you're using X-Pack's security features on the Elasticsearch server, there's\nadditional configuration required before starting Kibana.\n\n### Configure Kibana with Authentication\n\nIf X-Pack requires authentication to access the Elasticsearch cluster, follow\nthese steps or refer to \n[Elastic's documentation](https://www.elastic.co/guide/en/kibana/7.x/monitoring-xpack-kibana.html). \n\n1.  Set the password for the built-in `kibana` user in `[Kibana\n    Home]/config/kibana.yml`:\n\n    ```yaml\n    elasticsearch.username: \"kibana\"\n    elasticsearch.password: \"liferay\"\n    ```\n\n    Use your `kibana` user password from your X-Pack setup. Once Kibana is\n    installed, you can change the built-in user passwords from the\n    *Management* user interface.\n\n2.  If you're not encrypting communication with the Elasticsearch cluster, start\n    Kibana from Kibana home. \n\n    ```bash\n    ./bin/kibana\n    ```\n\n3.  Go to `http://localhost:5601` and make sure you can sign in as a \n    [user](https://www.elastic.co/guide/en/elasticsearch/reference/current/realms.html)\n    who has the `kibana_user` \n    [role](https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-roles.html) \n    or a superuser (like the `elastic` user).\n\n### Configuring Kibana with Encryption (TLS/SSL)\n\nFollow these steps to configure Kibana if X-Pack encrypts communication with the\nElasticsearch cluster. Consult \n[Elastic's guide](https://www.elastic.co/guide/en/kibana/7.x/using-kibana-with-security.html#using-kibana-with-security)\nfor more information.\n\n1. Copy the `[Elasticsearch Home]/config/certs` folder into the `[Kibana Home]/config/` folder.\n\n    This example reuses the certificate files [created for Elasticsearch\n    itself](/docs/7-2/deployment/-/knowledge_base/u/installing-liferay-enterprise-search-security).\n    If you wish to generate a separate certificate for your Kibana instance, make\n    sure it is signed by the same CA as the Elasticsearch node certificates.\n\n2. Add these settings to `kibana.yml`:\n\n    ```yaml\n    xpack.security.encryptionKey: \"xsomethingxatxleastx32xcharactersx\"\n    xpack.security.sessionTimeout: 600000\n\n    elasticsearch.hosts: [ \"https://localhost:9200\" ]\n\n    elasticsearch.ssl.verificationMode: certificate\n    elasticsearch.ssl.certificateAuthorities: [ \"config/certs/ca.crt\" ]\n    elasticsearch.ssl.certificate: config/certs/localhost.crt\n    elasticsearch.ssl.key: config/certs/localhost.key\n\n    server.ssl.enabled: true\n    server.ssl.certificateAuthorities: [ \"config/certs/ca.crt\" ]\n    server.ssl.certificate: config/certs/localhost.crt\n    server.ssl.key: config/certs/localhost.key\n    ```\n\nElasticsearch/Kibana 6.5 and below use a  different property for specifying the\nhost URL. Replace the `elasticsearch.hosts` property with\n\n```yaml\nelasticsearch.url: \"https://localhost:9200\"\n```\n\nFor more information about monitoring and security best practices in a clustered\nenvironment, refer to \n[Elastic's documentation](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/setup-xpack.html).\n\nAfter this step you can access Kibana at `https://localhost:5601` and sign in\nwith a Kibana user. The last step is to connect Kibana to @product@.\n\n## Configuring the Liferay Enterprise Search Monitoring app\n\nIf you have a LES subscription,\ndownload the Liferay Enterprise Search Monitoring app . Install\nthe LPKG file by copying it into the `Liferay Home/deploy` folder. \n\n1.  Once the connector is installed and Kibana and Elasticsearch are securely\n    configured, create a \n    [configuration file](/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files)\n    named\n\n        com.liferay.portal.search.elasticsearch6.xpack.monitoring.web.internal.configuration.XPackMonitoringConfiguration.config\n\n2.  Place these settings in the `.config` file:\n\n        kibanaPassword=\"liferay\"\n        kibanaUserName=\"elastic\"\n        kibanaURL=\"https://localhost:5601\"\n\n    The values depend on your Kibana configuration. For example, use a URL such\n    as `kibanaURL=\"http://localhost:5601\"` if you are not using X-Pack Security\n    TLS/SSL features.\n\n    Alternatively, configure the monitoring adapter from\n    [System Settings](/docs/7-2/user/-/knowledge_base/u/system-settings).\n    Navigate to *Control Panel* &rarr; *Configuration* &rarr; *System Settings* \n    and find the X-Pack Monitoring entry in the Search category. All the\n    configuration options for the monitoring connector appear there.\n\n3.  Deploy this configuration file to `Liferay Home/osgi/configs`, and your \n    running instance applies the settings. There's no need to restart the\n    server.\n\n4.  There are two more settings to add to Kibana itself. The first forbids \n    Kibana from rewriting requests prefixed with `server.basePath`. The second\n    sets Kibana's base path for the Monitoring portlet to act as a proxy for\n    Kibana's monitoring UI. Add this to `kibana.yml`:\n\n    ```yaml\n    server.rewriteBasePath: false\n    server.basePath: \"/o/portal-search-elasticsearch-xpack-monitoring/xpack-monitoring-proxy\"\n    ```\n\n    Note that once you set the `server.basePath`, you cannot access the Kibana\n    UI through Kibana's URL (e.g., `https://localhost:5601`). All access to the\n    Kibana UI is through the Monitoring portlet, which is only accessible to\n    signed in @product@ users. Navigate directly to the portlet using this URL:\n\n    [http://localhost:8080/o/portal-search-elasticsearch-xpack-monitoring/xpack-monitoring-proxy/app/monitoring](http://localhost:8080/o/portal-search-elasticsearch-xpack-monitoring/xpack-monitoring-proxy/app/monitoring)\n\n5.  Because you're using the Monitoring portlet in @product@ as a proxy to\n    Kibana's UI, if you are using X-Pack Security with TLS/SSL, you must configure the\n    application server's startup JVM parameters to recognize a valid\n    *truststore* and *password*.\n\n    First, navigate to Elasticsearch Home and generate a PKSC#12 certificate\n    from the CA you created when setting up X-Pack security:\n\n    ```bash\n    ./bin/elasticsearch-certutil cert --ca-cert /path/to/ca.crt --ca-key /path/to/ca.key --ip 127.0.0.1 --dns localhost --name localhost --out /path/to/Elasticsearch_Home/config/localhost.p12\n    ```\n\n    Next use the `keytool` command to generate a truststore:\n\n    ```bash\n    keytool -importkeystore -deststorepass liferay -destkeystore /path/to/truststore.jks -srckeystore /path/to/Elasticsearch_Home/config/localhost.p12 -srcstoretype PKCS12 -srcstorepass liferay\n    ```\n\n    Add the trustore path and password to your application server's startup JVM\n    parameters. Here are example truststore and path parameters for appending to\n    a Tomcat server's `CATALINA_OPTS`:\n\n    ```properties\n    -Djavax.net.ssl.trustStore=/path/to/truststore.jks -Djavax.net.ssl.trustStorePassword=liferay\n    ```\n\nRestart @product@ and Kibana.\n\n## Monitoring in @product@\n\nOnce Kibana and X-Pack are successfully installed and configured and all the\nservers are running, add the X-Pack Monitoring portlet to a page:\n\n1.  Open the *Add* menu on a page and choose *Widgets*\n\n2.  Search for *monitoring* and drag the *X-Pack Monitoring* widget from\n    the Search category onto the page.\n\nSee the Elastic documentation for information on \n[monitoring Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/es-monitoring.html).\n\n## Example Kibana Configuration\n\nHere are the `kibana.yml` properties demonstrated in this article, for\nconvenient copy/pasting:\n\n```yaml\n# X-Pack Security enabled (Basic Auth)\nelasticsearch.username: \"kibana\"\nelasticsearch.password: \"liferay\"\n\n# When TLS/SSL is enabled in X-Pack Security\nxpack.security.encryptionKey: \"xsomethingxatxleastx32xcharactersx\"\nxpack.security.sessionTimeout: 600000\n\n# If on Elasticsearch 6.5 or below, replace the next property with:\n# elasticsearch.url: \"http://localhost:9200\"\nelasticsearch.hosts: [ \"https://localhost:9200\" ]\n\n# Enable TLS/SSL for out-bound traffic: from Kibana to Elasticsearch\nelasticsearch.ssl.verificationMode: certificate\nelasticsearch.ssl.certificateAuthorities: [ \"config/certs/ca.crt\" ]\nelasticsearch.ssl.certificate: config/certs/localhost.crt\nelasticsearch.ssl.key: config/certs/localhost.key\n\n# Enable TLS/SSL for in-bound traffic: from browser or\n#  DXP (X-Pack Monitoring widget, proxy) to Kibana\nserver.ssl.enabled: true\nserver.ssl.certificateAuthorities: [ \"config/certs/ca.crt\" ]\nserver.ssl.certificate: config/certs/localhost.crt\nserver.ssl.key: config/certs/localhost.key\n\n# To use Kibana inside the X-Pack Monitoring widget\nserver.rewriteBasePath: false\nserver.basePath: \"/o/portal-search-elasticsearch-xpack-monitoring/xpack-monitoring-proxy\"\n```\n"
  },
  {
    "path": "en/deployment/articles-dxp/03-installing-a-search-engine/03-enterprise-search/03-ltr/01-learning-to-rank-intro.markdown",
    "content": "---\nheader-id: liferay-enterprise-search-learning-to-rank\n---\n\n# Liferay Enterprise Search: Learning to Rank\n\n[TOC levels=1-4]\n\nSearch engines like Elasticsearch have well-tuned relevance algorithms, good for\ngeneral search purposes. Sometimes, this \"generally good\" relevance scoring just\nisn't good enough. You can attain more perfect search results by employing\nmachine learning. \n\nLearning to Rank harnesses machine learning to improve search result rankings.\nIt combines the expertise of data scientists with machine learning to produce\na smarter scoring function that's applied to search queries.\n\n@product-ver@, Service Pack 1/Fix Pack 2 and later, supports Learning to Rank\nthrough its support of Elasticsearch versions 6.x and 7.4.x. It requires a\n[LES](https://help.liferay.com/hc/en-us/articles/360014400932) \nsubscription. It's important to understand that the\n[Elasticsearch Learning to Rank plugin](https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/index.html)\nis not produced by Elastic, and there is not a pre-built plugin for all of\n@product@'s supported Elasticsearch versions. \n\n## Disabling Learning to Rank on a Search Page\n\nLearning to Rank does not work with the\n[Sort widget](/docs/7-2/user/-/knowledge_base/u/sorting-search-results-with-the-sort-widget).\n\nIf you must use Learning to Rank in your @product@ instance, but want to disable\nit on a particular Search page (perhaps to use the Sort widget), you can:\n\n1.  Add a\n    [Low Level Search Options](/docs/7-2/user/-/knowledge_base/u/low-level-search-options-searching-additional-or-alternate-indexes)\n    widget to the Search page.\n\n2.  Open the widget's Configuration screen by clicking \n\n    _Configure additional low level search options in this page._\n\n3.  In the _Contributors to Exclude_ field, enter\n\n    `com.liferay.portal.search.learning.to.rank`\n\nNow the Learning to Rank re-scoring process is skipped for queries entered into\nthe page's Search Bar, and results are sortable in the Sort widget and returned\nusing the default relevance algorithm.\n\n## Prerequisites \n\nThere are some prerequisites for using Learning to Rank to re-score Liferay\nqueries sent to Elasticsearch:\n\n- If using Elasticsearch 7, @product-ver@ Service Pack 1/Fix Pack 2 or later is\n  required, with the appropriate Elasticsearch Connector version installed.\n\n- If using Elasticsearch 6, @product-ver@ Fix Pack 3 or later is\n  required, with the appropriate Elasticsearch Connector version installed.\n\n- A [Liferay Enterprise Search](https://help.liferay.com/hc/en-us/articles/360014400932) \n  (LES) subscription is required for Learning to Rank. Once you have a\n  subscription,\n  [download the Liferay Enterprise Search Learning to Rank](https://customer.liferay.com/downloads)\n  LPKG file and\n  [install it to your @product@ server.](/docs/7-2/user/-/knowledge_base/u/installing-apps-manually#installing-apps-manually)\n\n- A remote Elasticsearch server, with your data indexed into it.\n\n- The corresponding version of the [Elasticsearch Learning to Rank](https://github.com/o19s/elasticsearch-learning-to-rank) plugin installed into Elasticsearch.\n\n- A \n    [trained model](https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/training-models.html)\n    uploaded into the Learning to Rank plugin. \n\nTo understand more about the compatibility requirements for LES, see its\n[compatibility matrix](https://help.liferay.com/hc/en-us/articles/360016511651#Liferay-Enterprise-Search).\n\nHow does Learning to Rank work?\n\n### Technical Overview\n\nIn a normal search, the User sends a query to the search engine via Liferay\nDXP's [Search Bar](/docs/7-2/user/-/knowledge_base/u/searching-for-assets#search-bar).\nThe order of returned results is dictated by the search engine's\n[relevance scoring algorithm](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/index-modules-similarity.html#bm25).\n\nHere's where Learning to Rank intervenes and makes that process different:\n\n1.  User enters a query into the search bar.\n\n2.  Liferay sends the query to Elasticsearch, and retrieves the first 1000\n    results as usual, using the search engine's relevance algorithm. \n\n3.  The top 1000 results are not returned as search hits, but are used by Elasticsearch for\n    [re-scoring](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-request-body.html#request-body-search-rescore)\n    via the\n    [rescore functionality](https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/searching-with-your-model.html#rescore-top-n-with-sltr).\n\n4.  The results are re-scored by the \n    [SLTR query](https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/searching-with-your-model.html),\n    which includes the keywords and the trained model to use for re-scoring.\n\n5.  Once the trained model re-ranks the results, they're returned in Liferay's\n    [Search Results](/docs/7-2/user/-/knowledge_base/u/search-results)\n    in their new order.\n\nThough it's just a sub-bullet point in the ordered list above, much of the work\nin this paradigm is in creating and honing the trained model. That's beyond the\nscope of Liferay's role, but we'll help you get all the pieces in place to\norchestrate the magic of machine learning on your Liferay queries. Here's a\nbrief overview of what constitutes _model training_.\n\n### Model Training\n\nA useful trained model is produced when a good judgment list and a good feature\nset are fed to a Learning to Rank algorithm (this is the machine learning part\nof the puzzle). Therefore, it's incumbent on you to assemble\n\n- The Learning to Rank algorithm you wish to use for creating a training\n  model. This demonstration uses\n  [RankLib](https://sourceforge.net/p/lemur/wiki/RankLib/).\n\n- A _judgment list_, containing a graded list of search results. The algorithm\n  is designed to produce a model that honors the ordering of the judgment\n  list.\n\n- A feature set, containing all the _features_ you're handing to the Learning to\n  Rank algorithm, which it uses in conjunction with the judgment list to\n  produce a reliable model. An example feature set for @product@ data is shown\n  in the next article.\n\n\n[Judgment lists](https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/core-concepts.html#judgments-expression-of-the-ideal-ordering)\nare lists of graded search results.\n\n[Features](https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/core-concepts.html#features-the-raw-material-of-relevance) \nare the variables that the algorithm uses to create a function that can\nscore results in a smarter way. If you don't give enough, or the correct,\nrelevant features, your model will not be \"smart\" enough to provide improved\nresults.\n\nIn the next article you'll see the steps required to configure Learning to Rank\nwith @product@.\n"
  },
  {
    "path": "en/deployment/articles-dxp/03-installing-a-search-engine/03-enterprise-search/03-ltr/02-configuring-learning-to-rank.markdown",
    "content": "---\nheader-id: configuring-learning-to-rank\n---\n\n# Configuring Learning to Rank\n\n[TOC levels=1-4]\n\nBefore beginning, you must have a remote\n[Elasticsearch 6 or 7](/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-elasticsearch-7) \ncluster communicating with @product-ver@. See the\n[compatibility matrix for more information](https://help.liferay.com/hc/en-us/articles/360016511651#Liferay-Enterprise-Search)\n\n| **Helpful hint:** Use\n| [Suggestions](/docs/7-2/user/-/knowledge_base/u/searching-for-assets#search-suggestions)\n| to discover the most common queries (this can be one way to decide which queries\n| to create Learning to Rank models for).\n\n## Step 1: Install the Learning to Rank Plugin on Elasticsearch\n\nSee \n[the Elasticsearch Learning to Rank plugin documentation](https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/#installing)\nto learn about installing the Learning to Rank plugin.\n\nYou'll be running a command like this one, depending on the plugin version\nyou're installing:\n\n```sh\n./bin/elasticsearch-plugin install http://es-learn-to-rank.labs.o19s.com/ltr-1.1.0-es6.5.4.zip\n```\n\nIf using X-Pack security in your Elasticsearch cluster, there \n[may be additional steps.](https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/x-pack.html)\n\n## Step 2: Training and Uploading a Model\n\nDetailed instructions on training models is outside the scope of this guide.\nThis requires the intervention of data scientists, who can recommend\nappropriate tools and models. Use what works for you. In doing so, you'll\nalmost certainly be compiling\n[Judgment lists](https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/core-concepts.html#judgments-expression-of-the-ideal-ordering)\nand\n[feature sets](https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/building-features.html)\nthat can be used by the training tool you select to generate a model that \nproduces good search results. This can be a long journey, but once you get to the\nend of it, you'll want to upload the model to the Learning to Rank plugin.\n\n### Upload the Model to the Learning to Rank Plugin\n\nYou'll upload the model using a `POST` request, but first you need to make sure\nyou have a `_ltr` index and a feature set uploaded to the Learning to Rank\nplugin. Use Kibana (or even better, the\n[Monitoring widget](/docs/7-2/deploy/-/knowledge_base/d/installing-liferay-enterprise-search-monitoring)),\nto make these tasks easier.\n\n1.  If you don't already have an `_ltr` index, create one:\n\n    ```http\n    PUT _ltr\n    ```\n\n2.  Add a feature set to the `_ltr` index. In this example the set is called\n    `liferay`:\n\n    ```json\n    POST _ltr/_featureset/liferay\n    {\n      \"featureset\": {\n        \"name\": \"liferay\",\n        \"features\": [\n          {\n            \"name\": \"title\",\n            \"params\": [\n              \"keywords\"\n            ],\n            \"template\": {\n              \"match\": {\n                \"title_en_US\": \"{{keywords}}\"\n              }\n            }\n          },\n          {\n            \"name\": \"content\",\n            \"params\": [\n              \"keywords\"\n            ],\n            \"template\": {\n              \"match\": {\n                \"content_en_US\": \"{{keywords}}\"\n              }\n            }\n          },\n          {\n            \"name\": \"asset tags\",\n            \"params\": [\n              \"keywords\"\n            ],\n            \"template\": {\n              \"match\": {\n                \"assetTagNames\": \"{{keywords}}\"\n              }\n            }\n          }\n        ]\n      }\n    }\n    ```\n    Take note of the syntax used here, since it's required.\n\n3.  Add the trained model to the feature set: \n\n    ```json\n    POST _ltr/_featureset/liferay/_createmodel\n    {\n      \"model\": {\n        \"name\": \"linearregression\",\n        \"model\": {\n          \"type\": \"model/ranklib\",\n          \"definition\": \"\"\"\n    ## Linear Regression\n    ## Lambda = 1.0E-10\n    0:-0.717621803830712 1:-0.717621803830712 2:-2.235841905601106 3:19.546816765721594\n    \"\"\"\n        }\n      }\n    }\n    ```\n\nThis is a very high level set of instructions, because there's not much to do in\n@product@. To learn in more detail about what's required, see the\n[Learning to Rank plugin's documentation](https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/index.html).\n\nKeep reworking those judgment lists!\n\n## Step 3: Enable Learning to Rank\n\nEnable Learning to Rank from Control Panel &rarr; Configuration &rarr; System\nSettings &rarr; Search &rarr; Learning to Rank. There's a simple on/off\nconfiguration and a text field where you must enter the name of the trained\nmodel to apply to search queries.\n\nThe model in the previous step was named `linearregression`, so that's what\nyou'd enter. \n\n![Figure 1: Enable Learning to Rank in @product@ from the System Settings entry.](../../../../images-dxp/search-learning-to-rank.png)\n\nThat's all the configuration required to get the Elasticsearch Learning to Rank\nplugin ingesting a trained model, a feature set, and search queries from\n@product@.\n"
  },
  {
    "path": "en/deployment/articles-dxp/04-securing-liferay/10-saml/01-intro.markdown",
    "content": "---\nheader-id: authenticating-using-saml\n---\n\n# Authenticating Using SAML\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/configuring-sso/authenticating-with-saml.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe SAML (Security Assertion Markup Language) adapter provides Single Sign On\n(SSO) and Single Log Off (SLO) in your deployment. Each @product@ instance\nserves as either the Service Provider (SP) or the Identity Provider (IdP). An\nidentity provider is a trusted provider that provides single sign-on for users\nto access other websites. A service provider is a website that hosts\napplications and grants access only to identified users with proper credentials.\n\n| **Note:** A single @product@ instance is *either* the SP or the IdP in your SSO\n| setup; it can't be both. You can, however, use separate instances for both\n| purposes (for example, one instance is the SP and another is the IdP).\n\nBelow is background on how SAML works. To jump right to its configuration, see\nthe articles on \n[Setting Up SAML as an Identity Provider](/docs/7-2/deploy/-/knowledge_base/d/setting-up-liferay-as-a-saml-identity-provider)\nor \n[Setting Up SAML as a Service Provider](/docs/7-2/deploy/-/knowledge_base/d/setting-up-liferay-as-a-saml-service-provider) \nfor instructions on using the \n[SAML adapter](https://web.liferay.com/marketplace/-/mp/application/15188711). Use\nthe instructions to make the conceptual magic from this article come to life!\n\n## What's new in Liferay Connector to SAML 2.0\n\nThe `5.0.0` version of the application for @product@ brings some long-awaited\nimprovements:\n\n* @product@ acting as a Service Provider (SP) can now connect to multiple\n  Identity Providers (IdP).\n* Developers have an extension point for customizing which Identity Providers to\n  users can use to sign in.\n* Support for other Signature Algorithms (like `SHA-256`)\n* Signature method algorithm URL's can now be blacklisted from the metadata (for\n  example, disabling `SHA-1`: `http://www.w3.org/2000/09/xmldsig#rsa-sha1`)\n\n| **Note:** If you're migrating from a Liferay SAML adapter prior to version\n| 3.1.0, your portal properties are automatically migrated to System Settings configurations. Please see the\n| [Configuring SAML](/docs/7-2/deploy/-/knowledge_base/d/configuring-saml)\n| article for details on settings.\n\n## Important SAML Paths\n\nFor reference, here are a few important SAML paths.\n\nThis URL is the default location of the metadata XML file:\n\n    [host]:[port]/c/portal/saml/metadata\n\nNote that when configuring SAML, no importing of SAML certificates is required.\n@product@ reads certificates from the SAML metadata XML file. If you want a\nthird-party application like Salesforce to read a Liferay SAML certificate, you\ncan export the @product@ certificate from the keystore. The default keystore\nfile is \n\n```bash\n[Liferay Home]/data/keystore.jks \n```\n\nYou can change this path in System Settings &rarr; SSO &rarr; SAML Configuration\n&rarr; Key Store Path.\n\n## Single Sign On\n\nBoth the IdP and the SP can initiate the Single Sign On process, and the SSO\nflow is different depending on each one. Regardless of how it's initiated, SSO\nis configured for HTTPS between the SP and IdP, so all transport-level\ncommunication is encrypted. SAML requests are signed using certificates\nconfigured in @product@, using the SAML Web Browser SSO profile as defined in\nthe [SAML 2.0 specification](http://saml.xml.org/saml-specifications). In all\ncases, responses are sent using HTTP-POST or HTTP-Redirect. HTTP-POST is\npreferred because it reduces the risk that the URL is too long for a browser to\nhandle. \n\nConsider IdP initiated SSO first.\n\n### Identity Provider Initiated SSO\n\nSometimes a user enters the SSO cycle by sending a request directly from the\nbrowser to the IdP.\n\n![Figure 1: Identity Provider Initiated SSO](../../../images-dxp/saml-idp-initiated-sso.png)\n\n#### The SSO Request to the IdP\n\nIf @product@ is the IdP, the IdP initiated SSO URL\n\n- Must specify the path as `/c/portal/saml/sso`. \n- Must include the `entityId` parameter which is the identifier to a\n  previously configured Service Provider Connection (SPC). \n- May include a `RelayState` parameter which contains a URL\n  encoded value where the user is redirected upon successful\n  authentication. This URL should point to a location on the desired SPC\n  (according to the \n  [SAML 2.0 standards section 3.4.3](https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf), \n  this value *must not* exceed 80 bytes in length). It is useful to specify a\n  landing page after SSO has been executed.\n\nFor non-@product@ IdPs (Siteminder, ADFS, etc.), consult the vendor's\ndocumentation on constructing IdP initiated SSO URLs.\n\nIf the IdP determines that the user isn't authenticated, it prompts the user\nwith the appropriate login screen. \n\n#### The SSO Response from the IdP\n\nUpon successful authentication, the IdP constructs a SAML Response. It includes\nattribute statements configured in the designated Service Provider Connection\n(SPC; see the\n[next article](/docs/7-2/deploy/-/knowledge_base/d/setting-up-liferay-as-a-saml-identity-provider)\non setting up the SPC in @product@'s SAML adapter).\n\nThe IdP sends the response to the Assertion Consumer Service URL. The request\ncontains two parameters: `SAMLResponse` and `RelayState`.\n\n| **Note:** The method for sending the SAML response (for example, HTTP-POST) and\n| the Assertion Consumer Service URL are usually imported as part of the SAML\n| metadata XML provided by the SP. In @product@, you import the SP's metadata in the\n| SAML Adapter's Service Provider Connections tab.\n\n#### The SP Processes the SSO Response\n\nThe SP validates and processes the SAML Response. @product@'s SAML solution\nrequires signed `SAMLResponse` messages. This signature process ensures proper\nidentification for the IdP and prevents potential SAML Response spoofing.\n\n- If one @product@ instance is the IdP and another is the\n  SP, make sure the SAML metadata XML file imported into the SP contains the\n  IdP's certificate. \n- If @product@ is the IdP and another application is the SP,\n  export the certificate from the IdP and import it into the SP's certificate\n  store.\n\nIf a `RelayState` is included in the SAML Response, the user is redirected to\nit. Otherwise the home page of the SP is served.\n\n### Service Provider Initiated SSO\n\nMost of the time, authentication requests come from the Service Provider.\n\n![Figure 2: Service Provider Initiated SSO](../../../images-dxp/saml-sp-initiated-sso.png)\n\n#### The SSO Request to the SP\n\nWhen the user's browser requests a protected resource or login URL on the SP,\nit triggers the SP initiated SSO process. When @product@ is the SAML SP, SSO is\ninitiated either by requesting `/c/portal/login` URL or a protected resource\nthat requires authentication (for example, a document not viewable by the Guest\nRole). If the user requests a protected resource, its URL is recorded in the\n`RelayState` parameter. If the user requested `/c/portal/login`, the\n`RelayState` can be set by providing the `redirect` parameter. Otherwise, if\nthe [portal property](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html)\n`auth.forward.by.last.path` is set to `true`, the last accessed path is set as\nthe `RelayState`. For non-@product@ SPs, consult the vendor documentation on\ninitiating SSO.\n\n#### The AuthnRequest to the IdP\n\nThe SP looks up the IdP's Single Sign On service URL and sends an\n`AuthnRequest`. When @product@ is the SP it looks up the configured SAML Identity\nProvider Connection and sends a SAML `AuthnRequest` to the IdP's Single Sign On\nservice URL as defined in the SAML metadata XML document. @product@ supports\nsending and receiving the `AuthnRequest` using HTTP-POST or HTTP-Redirect\nbinding. HTTP-POST is preferred.\n\nIf the user doesn't have an active session or if `ForceAuthn` was requested by\nthe SP, the user must authenticate by providing credentials. When @product@ is\nthe IdP, authentication occurs in the Login Portlet. @product@ decodes and\nverifies the `AuthnRequest` before requesting the user to authenticate.\n\n#### The SSO Response from the IdP\n\nAfter authentication, a SAML Response is constructed, sent to the Assertion\nConsumer Service URL of the SP, and verified. The IdP automatically makes this\nchoice based on the SP metadata. \n\nWhen @product@ is configured as the IdP, any attributes configured on the Service\nProvider Connection are included in the response as attribute statements. The\nAssertion Consumer Service URL is looked up from the SAML metadata XML of the\nSP. \n\nWhen @product@ is configured as the SP, response and assertion signatures are\nverified. @product@ requires the sender to be authenticated. This is done via\nwhole message signature from the issuing IdP. Responses missing the\nsignature are considered unauthenticated and the response is rejected. For\nnon-@product@ SP or IdP vendors, consult their documentation.\n\nThe user is redirected to the requested resource or to the URL contained in the\n`RelayState` parameter (for example, the last page the user accessed before\ninitiating SSO).\n\n## Single Log Off\n\nThe Single Log Off request is sent from the user's browser to the IdP or\nan SP, and the SLO flow differs in each case. First consider IdP initiated\nSLO.\n\n### Identity Provider Initiated SLO\n\n![Figure 3: Identity Provider Initiated SLO](../../../images-dxp/saml-idp-initiated-slo.png)\n\n#### The SLO Request to the IdP\n\nAn IdP initiated SLO request is sent directly to the IdP by the user's browser.\nWhen @product@ is the IdP, the IdP initiated SSO URL must specify the URL\npath as \n\n`/c/portal/logout`\n\nIf the user is signed on to any configured SP, the SAML plugin takes over the\nlogout process, displaying all the signed on services. The single logout screen\ndisplays the authentication status of each SP and whether any SPs can't be\nlogged out of (for example, if the SP is down or doesn't support SLO). For\nnon-@product@ IdPs (Siteminder, ADFS, etc.) consult the vendor's documentation on\nconstructing IdP initiated SLO URLs.\n\nThe IdP sends a SAML `LogoutRequest` to the SP.\n\n- When @product@ is configured as the IdP, the `LogoutRequest` is sent using\n  either HTTP-POST, HTTP-Redirect, or SOAP binding. HTTP-Post binding is\n  preferred but in its absence, the first available SLO endpoint with supported\n  binding is selected.\n- When @product@ is configured as the SP, supported bindings for `LogoutRequest`\n  are HTTP-Post, HTTP-Redirect, or SOAP.\n- For other IdPs or SPs, please consult the vendor's documentation.\n\n#### The SLO Response from the SP\n\nThe SP delivers a `LogoutResponse` to the IdP. \n\nThe IdP sends a SAML `LogoutRequest` to the second SP.\n\nThe second SP then delivers the `LogoutResponse` to the IdP. The process is\nrepeated for all SPs the user is logged into. When @product@ is the IdP,\n@product@ logs the user out after the last SP has delivered its `LogoutResponse`\nor has timed out.\n\n### Service Provider Initiated SLO\n\n![Figure 4: Service Provider Initiated SLO](../../../images-dxp/saml-sp-initiated-slo.png)\n\n#### The SLO Request to the SP\n\nIn SP initiated SLO, the user's browser sends a logout request directly to the\nSP. When @product@ is configured as the SP, the SLO is initiated by requesting\nthis logout URL:\n\n    /c/portal/logout\n\nFor other SPs, consult the vendor's documentation on initiating SLO.\n\nA SAML `LogoutRequest` is sent to the Single Log Out service URL of the IdP.\n\n-  If @product@ serves as the SP, the `LogoutRequest` is sent to the IdP\n   configured by the IdP Connections tab of the SAML provider (see the [next\n   article](/docs/7-2/deploy/-/knowledge_base/d/setting-up-liferay-as-a-saml-identity-provider)\n   to set up the IdP Connection) and the SLO service URL defined in the SAML\n   metadata. \n\n-  When @product@ is the IdP, if the user has logged on to other SPs, the user\n   is presented with a single logout screen with the status of each SP logout,\n   flagging any that can't be looged out of (some SPs might not support SLO or\n   are currently down). If there are no other SPs to log out of, the SAML\n   session terminates and the IdP destroys its session.\n\n#### The SLO Response from the SP\n\nIf the user is logged in to additional SPs (beyond just the initiating SP), the\nIdP sends the SAML `LogoutRequest` to each one. When @product@ is the IdP, the\n`LogoutResponse` is sent using either HTTP-Post, HTTP-Redirect, or SOAP binding.\n\nEach SP delivers its `LogoutResponse` to the IdP. When @product@ is the SP, the\n`LogoutResponse` is sent using either HTTP-Post, HTTP-Redirect or direct\nresponse to SOAP request.\n\nAfter all additional SPs deliver their `LogoutResponse`s to the IdP, the IdP\ndestroys its SSO session. When @product@ is the IdP, once the last SP has\ndelivered its `LogoutResponse` or has timed out, the IdP destroys the @product@\nsession, logging out the user.\n\nFinally, the IdP sends a `LogoutResponse` to the SP that initiated SLO. The\ninitiating SP terminates its SAML session and logs the user out.\n\n## Related Topics\n\n- [Setting Up SAML as an Identity Provider](/docs/7-2/deploy/-/knowledge_base/d/setting-up-liferay-as-a-saml-identity-provider)\n- [Setting Up SAML as a Service Provider](/docs/7-2/deploy/-/knowledge_base/d/setting-up-liferay-as-a-saml-service-provider) \n- [Token-Based SSO Authentication](/docs/7-2/deploy/-/knowledge_base/d/token-based-single-sign-on-authentication)\n"
  },
  {
    "path": "en/deployment/articles-dxp/04-securing-liferay/10-saml/02-setting-up-identity-provider.markdown",
    "content": "---\nheader-id: setting-up-liferay-as-a-saml-identity-provider\n---\n\n# Setting up @product@ as a SAML Identity Provider\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/configuring-sso/authenticating-with-saml.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAn identity provider is a trusted provider that provides single sign-on for\nusers to access other websites. A service provider is a website that hosts\napplications and grants access only to identified users with proper credentials.\nLiferay Portal 6.1 EE and later versions support SAML 2.0 integration via the \n[Liferay Connector to SAML 2.0](https://web.liferay.com/marketplace/-/mp/application/15188711)\napplication. It is provided from Liferay Marketplace and allows @product@ to act\nas a SAML 2.0 identity provider or as a service provider.\n\n| **Important:** You can set @product@ up as an Identity Provider or as a Service\n| Provider. Each single @product@ instance can serve as an identity provider or as\n| a service provider, but **not both**. Both configurations are covered in this\n| article.\n\n## Storing Your Keystore\n\nYour first step is to determine where to store your keystore. You have two\noptions:\n\n- In the file system\n- In the Documents and Media library\n \nThe file system keystore manager is used by default and the default location is\nthe `[Liferay Home]/data` directory (you can change the location in System\nSettings &rarr; SSO &rarr; SAML Configuration &rarr; Key Store Path). To use\nDocuments and Media library storage for your keystore instead of file system\nstorage, go to *Control Panel* &rarr; *System Settings* &rarr; *Security* &rarr;\n*SSO* &rarr; *SAML KeyStoreManager Implementation Configuration*. Select from\nthe two options: *Filesystem Keystore Manager* or *Document Library Keystore\nManager*.\n\nIf you use Document Library storage, you can use any number of \n[back-end file stores](/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration).\nThese are protected not only by the system where the key is stored, but\nalso by @product@'s permissions system.\n\n## Configuring @product@ as a SAML Identity Provider\n\n1.  To access the SAML Admin interface, click on *Control Panel* &rarr;\n    *Configuration* and then on *SAML Admin*.\n\n2.  To begin configuring @product@ to use SAML, select a SAML role for @product@ and\n    choose an entity ID.\n\n    ![Figure 1: Select a SAML role for Liferay and enter an entity ID.](../../../images-dxp/saml-initial-config.png)\n\n    Select the *Identity Provider* SAML role. Enter your own entity ID. Then\n    click *Save*. A new Certificate and Private Key section appears. \n\n3.  The Certificate and Private Key section lets you create a keystore for SAML.\n    Click on *Create Certificate* and enter the following information:\n \n    - Your common name (your first and last name)\n    - The name of your organization\n    - The name of your organizational unit\n    - The name of your city or locality\n    - The name of your state or province\n    - The name of your country\n    - The length in days that your keystore will remain valid (how long before\n      the keystore expires)\n    - The key algorithm (RSA is the default)\n    - The key length in bits (2048 is the default)\n    - The key password\n\n    Click *Save*.\n\n    When you create the certificate and private key, you also create a keystore\n    if one doesn't already exist. As described above, this keystore has two\n    storage options: file system storage (the default) and Documents and Media\n    storage. By default, the certificate uses the `SHA256` algorithm for\n    encryption and is fingerprinted and self-signed via RSA and `SHA256`. \n \n4.  After you click *Save*, you can click *Replace Certificate* at any time to\n    replace the current certificate with a new one if your old one has expired\n    or if you want to change the key's password. \n\n    ![Figure 2: The General tab of the SAML Admin portlet displays information about the current certificate and private key and allows administrators to download the certificate or replace the certificate.](../../../images-dxp/saml-keystore-info.png) \n\n    Three more tabs now appear: \n\n    **General:** For enabling or disabling a SAML IdP and managing the required\n    keystore.\n\n    **Identity Provider:** Contains IdP options, such as whether to enable SSL.\n    If SSL has been enabled, then SAML requests are not approved unless they\n    are also encrypted.\n\n    **Service Provider Connections:** This tab manages any Service Providers\n    connected to this @product@ instance.\n \n    See below for more information on the Identity Provider and Service\n    Provider Connections tabs.\n\n5.  After you save your certificate and private key information,\n    check the *Enabled* box at the top of the General tab and click *Save*.\n    You successfully set @product@ up as a SAML Identity Provider!\n\n## Changing the Identity Provider Settings\n\nTo configure @product@'s SAML Identity Provider Settings, navigate to the *Identity\nProvider* tab of the SAML Admin Control Panel entry.\n\nThe *Identity Provider* tab includes these options:\n\n**Sign Metadata?:** Check this box to ensure the metadata XML file that's\nproduced is signed.\n\n**SSL Required:** Check this box to reject any SAML messages that are *not*\nsent over SSL. This affects URLs in the generated metadata. \n\n**Require Authn Request Signature?:** When this box is checked, each Authn\nRequest must be signed by the sending Service Provider. In most cases, this\nshould be enabled. \n\n**Session Maximum Age:** Specify the maximum duration of the SAML SSO session\nin seconds. If this property is not set or is set to `0`, the SSO session has an\nunlimited duration. The SSO session maximum duration can be longer than the\nportal session maximum duration. If the portal session expires before the SSO\nsession expires, the user is logged back in to @product@ automatically. SSO\nsession expiration does not trigger a single logout from all service providers.\nYou can use the session maximum age, for example, to force users to sign in\nagain after a certain period of time.\n\n**Session Timeout:** Specify the maximum idle time of the SAML SSO session.\nEven if the session maximum age is unlimited, the SSO session expires whenever\nthe user's idle time reaches the limit set by the session timeout property.\n\n## Checkpoint\n\nBefore adding a Service Provider (SP), verify you've completed these tasks:\n\n1. A SAML keystore has been generated. It can be stored in one of two locations:\n   the `data` folder or in the Documents and Media library.\n\n2. On the *Identity Provider* tab, the following settings have been set:\n\n    a. **Sign Metadata** has been checked. \n \n    b. **SSL Required** - checked if SSL is active elsewhere. SSL is disabled by \n       default. \n\n    c. **Authn Request Signature Required:** has been checked. \n \n    d. **Session Maximum Age:** has been set. If set to `0`, then the SSO has an \n       unlimited duration. \n\n    e. **Session Timeout:** Specify the maximum idle time of the SAML SSO session. \n\n3. Once the *Enabled* checkbox has been checked, the IdP is live, and you can\n   generate the required metadata. This URL is the default location of\n   @product@'s metadata XML file:\n\n        [host]:[port]/c/portal/saml/metadata \n\nIf this URL does not display correctly, then the SAML instance has not been\nenabled. Use the URL or click *Save* in the browser to generate an actual `XML` file.\n\nYour Identity Provider is now set up. Next, you must register a Service\nProvider. \n"
  },
  {
    "path": "en/deployment/articles-dxp/04-securing-liferay/10-saml/03-registering-a-service-provider.markdown",
    "content": "---\nheader-id: registering-a-saml-service-provider\n---\n\n# Registering a SAML Service Provider\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/configuring-sso/authenticating-with-saml.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nSetting up @product@ as a SAML Identity Provider is only useful if you\ncan connect to one or more SAML Service Providers. Navigate to the Service\nProvider Connections tab of the SAML Admin Control Panel entry and click \nthe *Add Service Provider* button to add a SAML Service Provider.\n\nThe New Service Provider page includes these options:\n\n**Name:** The name of the Service Provider with which to connect. The name can\nbe anything; it's purely cosmetic.\n\n**Entity ID:** The Service Provider's entity ID. This value must match the\nentity ID declared in the Service Provider metadata.\n\n**Enabled:** Check this box to activate the Service Provider connection.\n\n**Assertion Lifetime:** Defines the number of seconds after which the SAML\nassertion issued by the Identity Provider should be considered expired.\n\n**Force Encryption:** If the SP does not provide a public key for encrypting the\nassertions, abort the single sign-on. \n\n**Metadata:** Provide a URL to the Service Provider metadata XML file or\nmanually upload the Service Provider metadata XML file. If you provide a URL,\nthe XML file is retrieved and periodically polled for updates. The update\ninterval can be configured in System Settings with the Runtime Metadata Refresh\nInterval (`saml.metadata.refresh.interval` if using a `config` file) property\nwhich specifies a number of seconds. If fetching the metadata XML file by URL\nfails, you can't enable the Service Provider connection. If the Identity\nProvider server cannot access the metadata via URL, you can upload the XML file\nmanually. In this case, the metadata XML file is not updated automatically. \n\n**Name Identifier Format:** Choose the Name Identifier Format used in the SAML\nResponse. This should be set according to what the Service Provider expects to\nreceive. For Liferay Service Providers, any selection other than email address\nindicates that the Name Identifier refers to screen name. The formats don't have\nany special meaning to Liferay Identity Providers. The `NameID` value is defined\nby the Name Identifier attribute. See the next option.\n\n**Name Identifier Attribute Name:** This specifies which attribute of the\n@product@ `User` object to use as the `NameID` value. Possible values include\n`emailAddress`, `screenName` and `uuid`. Additionally, you can prefix the name\nwith `static:` or `expando:`. If you use the prefix `static`, the value is\nwhatever comes after `static:`. If you use the prefix `expando`, the value is\nwhatever custom field is specified after `expando:`. For example, `expando:SSN`\nwould look up the `User` custom field with the name `SSN`.\n\n**Attributes Enabled:** Include and resolve assertion attributes.\n\n**Attributes Namespace Enabled:** When this box is checked, the attribute names\nare namespaced like this:\n\n    urn:liferay:user:expando:\n    urn:liferay:user:\n    urn:liferay:groups:\n    urn:liferay:organizationRole:\n    urn:liferay:organization:\n    urn:liferay:roles:\n    urn:liferay:siteRole:\n    urn:liferay:userGroupRole:\n    urn:liferay:userGroups:\n\n**Attributes:** Enter a list of attributes to include in the assertion, one per\nline. Each line is an expression that gets parsed. Examples: \n\n    organizations\n    organizationRoles\n    roles\n    siteRoles\n    userGroups\n    static:[attributeName]=[attributeValue]\n    expando:[userCustomFieldName] \n\nNote that the full namespace depends on the attribute name. Attribute\nnamespaces can be useful. Use them when attribute names from different\nnamespaces might conflict. For example, `expando:user` vs\n`urn:liferay:roles:user`.\n\n**Keep Alive URL:** If users are logged into several @product@ SP instances via\na @product@ IdP, their sessions can be kept alive as long as they keep a\nbrowser window open to one of them. Configure this only if the SP is @product@.\nThe URL is `https://[SP host name]/c/portal/saml/keep_alive`. \n\n## Checkpoint\n\nVerify your settings are correct by connecting the @product@-based IdP to its\nfirst SP. SPs connect to only one IdP, so if the first one doesn't work, the\nrest won't either. \n\n1. Provide a general name for the SP.\n\n2. The `Entity ID` name must be identical to the one declared in the Service\n   Provider metadata.\n\n3. Check the *Enabled* checkbox.\n\n4. Set a value for the *Assertion Lifetime*.\n\n5. Choose whether encryption should be required (recommended). \n\n6. Make sure the SP's metadata has been provided either as a URL or an XML file \n   has been uploaded.\n\n7. Make sure *Name Identifier Format* and *Name Identifier Attribute Name* have \n   been set.\n\n8. Make sure *Attributes Namespace Enabled* has been set.\n\nIf you don't have a Service Provider to add right now, that's fine. In the next\nsection, you'll learn how to set @product@ up as a SAML Service Provider. The\nsame instance can't be both, but after you set up another @product@ instance as\na Service Provider, come back to this one and add the Service Provider according\nto the instructions above.\n"
  },
  {
    "path": "en/deployment/articles-dxp/04-securing-liferay/10-saml/04-setting-up-service-provider.markdown",
    "content": "---\nheader-id: setting-up-liferay-as-a-saml-service-provider\n---\n\n# Setting up @product@ as a SAML Service Provider\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/configuring-sso/authenticating-with-saml.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nMany of these steps are similar to configuring @product@ as a SAML Identity\nProvider. As a reminder, a single @product@ installation can be configured as a\nSAML Identify Provider *or* as a SAML Service Provider but not as both. If your\n@product@ installation is already a SAML Identity Provider, use a *different*\n@product@ installation as a SAML Service Provider.\n\n| **Note:** If you have a third party IdP with @product@ as the SP, all\n| messages coming from the IdP must be signed. If they're not, an error message\n| appears and communication between the IdP and @product@ fails.\n\n1.  Install the Liferay Connector to SAML 2.0 app. To confirm successful deployment,\n    look for the *SAML Admin* entry in the Configuration section of the Control\n    Panel.\n\n2.  To begin configuring @product@ to use SAML, you must select a SAML role for\n    @product@ and choose an entity ID. Select the *Service Provider* SAML role.\n    Choose your own entity ID. Then click *Save* and a new section entitled\n    Certificate and Private Key appears.\n\n3.  The Certificate and Private Key section is for creating a keystore for SAML.\n    Click *Create Certificate* and enter the following information:\n\n    - Your common name (your first and last name)\n    - The name of your organization\n    - The name of your organizational unit\n    - The name of your city or locality\n    - The name of your state or province\n    - The name of your country\n    - The length in days that your keystore will remain valid (how long before\n      the keystore expires)\n    - The key algorithm (RSA is the default)\n    - The key length in bits (2048 is the default)\n    - The key password\n\n    Remember that the keystore has two storage options: file system storage (the\n    default) and Documents and Media storage.By default, the certificate uses\n    the SHA256 algorithm for encryption and is fingerprinted and self-signed via\n    MD5 and SHA1. When you enter all the required information, click *Save*.\n\n4.  After you clicked *Save*, check that you can view information about your\n    certificate or download your certificate. If you can, you successfully\n    created a keystore. After you create a keystore, additional options\n    appear. There are three tabs:\n\n    **General**: Enables or disables SAML SP and manages the required\n    keystore.\n \n    **Service Provider**: This tab manages basic and advanced configurations for\n    the SP.\n \n    **Identity Provider Connection**: This tab manages connections to the IdP.\n    There can be multiple IdP connections.\n\n5.  You can also generate an encryption certificate. This is a separate key for\n    encrypting assertions. If you want assertions encrypted, you must generate\n    a key for this. The procedure is exactly the same as generating your\n    certificate in step 3 above. \n\n5.  Next, you must configure an Identity Provider connection. Click on the\n    *Identity Provider Connections* tab. Enter a name for the Identity Provider,\n    enter its entity ID, and enter its metadata URL. If you have already\n    followed the previous instructions and configured a separate @product@\n    installation as an Identify provider, you'd enter the following information:\n\n    - Name: *Liferay IdP*\n    - Entity ID: [ID of IdP]\n\t- Clock Skew: Set the tolerance in milliseconds between SP and IdP.\n\t- Force Authn: Whether the IdP should force reauthentication regardless of\n      context.\n    - Metadata URL: http://localhost:8080/c/portal/saml/metadata (test this URL\n      first)\n\t- Name Identifier Format: See settings. \n\t- Attribute Mapping: See settings. \n\t- Keep Alive URL: See settings. \n\n    **Important**: The Liferay Connector to SAML 2.0 app supports using *either*\n    a URL to a SAML IdP metadata file *or* an actual (uploaded) SAML metadata\n    XML file. The value entered in the *Metadata URL* field is persisted to the\n    database only when there a metadata URL and there is no specified metadata\n    XML file. Otherwise, @product@ keeps the original metadata URL in the\n    database. This behavior ensures that once a metadata URL has been specified,\n    there is always a metadata URL saved in the database. This way, if a portal\n    administrator forgets the previously entered metadata URL or its format, he\n    or she can look at the displayed metadata URL and choose to modify the\n    displayed metadata URL or overwrite the previously saved metadata URL by\n    specifying a metadata XML file.\n\n6.  Finally, after you save your certificate and private key information and\n    configure an Identity Provider connection, check the *Enabled* box at the top\n    of the General tab and click *Save*. @product@ is now a SAML Service Provider!\n\nNote that the SAML Service Provider session is tied to the normal session on\nthe application server. Session expiration on the application server terminates\nthe session on the Service Provider but does not initiate single logout. \n\nYou can add multiple IdP connections. To add another Identity Provider, click\n*Add Identity Provider* again and enter the details for the other provider. When\nusers log in, they are asked to choose an identity provider, so be sure to name\nthe providers so users can recognize them. \n\n## Checkpoint\n\n1. A SAML keystore has been generated.\n\n2. Verify the connection to the IdP. \n\n    a.  *Name*: generic name for the IdP. \n \n    b.  *Entity ID*: the same name of the IdP. If the IdP is another @product@\n        instance, then it is the same name as the above example. \n\n    c. *Metadata URL*: The IdP's metadata as a URL or as an XML file.\n\n    d. If the IdP is another @product instance, ensure its corresponding\n    Service Provider Connection for this SP is enabled.\n\n3. On the *General* tab, the *Enabled* checkbox has been checked.\n\n4. Once *Enabled* checkbox has been checked, the service provider's metadata\n   becomes available:\n\n        [host]:[port]/c/portal/saml/metadata\n\n## Setting Up @product@ as a SAML Service Provider in a Clustered Environment\n\nYou can use the Liferay Connector to SAML 2.0 app as an SSO solution for a\nclustered @product@ environment. If your multi-node cluster is behind a load\nbalancer, you must enable all the nodes as SPs, and they must share the\nsame keystore manager. \n\nIf using the Filesystem Keystore Manager (the default):\n\n1.  Configure each node of your \n    [@product@ cluster](/docs/7-2/deploy/-/knowledge_base/d/liferay-clustering) \n    as a SAML service provider as above. \n\n2.  Copy the keystore file (`[Liferay Home]/data/keystore.jks`, by default) from\n    the first node to the remaining nodes. This file is the Java keystore that's\n    created by the SAML Provider app. The keystore contains the valid or\n    self-signed certificate managed by the SAML connector app.\n\n3.  Verify that the service provider metadata has been generated to be used\n    either as a URL or an XML file. The metadata is the same for all nodes\n    because of the same database back-end. The IdP's request goes through the\n    load balancer.\n\n4.  At this point, all  nodes have the same SAML SP configuration and each of\n    them can respond to web requests and handle the SAML protocol. To test your\n    SSO solution, sign into @product@ via your load balancer, navigate to a few\n    pages of a few different sites, and then log out.\n\nIf using the Document Library Keystore Manager, skip step 2 because the keystore\nfile is stored in the database shared by all the nodes.\n\nNow you know how to configure @product@ either as a SAML identity provider\nor a service provider. You also know how to configure SAML in a\nclustered environment. \n"
  },
  {
    "path": "en/deployment/articles-dxp/04-securing-liferay/10-saml/05-configuring-sp-and-idp-connections.markdown",
    "content": "---\nheader-id: changing-the-settings-for-service-provider-and-identity-provider-connection\n---\n\n# Changing the Settings for Service Provider and Identity Provider Connections\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/configuring-sso/authenticating-with-saml.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nTo change the SAML Service Provider Settings, navigate to the Service Provider\ntab. \n\nThe Service Provider tab includes these options:\n\n**Require Assertion Signature?:** Check this box to require SAML assertions\nto be individually signed in addition to the entire SAML message.\n\n| **Note:** Individual assertions need not be signed as long as the SAML response\n| itself is signed. The SP and IdP should always communicate over `https` to have\n| encryption at the transport level.\n| \n| If you believe man-in-the-middle attacks are possible, the SAML response can be\n| signed. The only reason to sign the assertions is if the SAML response is not\n| signed. In this case, assertions should not only be signed but also encrypted.\n\n**Clock Skew:** Clock skew is a tolerance in milliseconds used by the Service\nProvider for mitigating time differences between the clocks of the Identity\nProvider and the Service Provider. This usually only matters when assertions\nhave been made to expire very quickly.\n\n**LDAP Import Enabled:** Check this box to import user information from the\nconfigured LDAP connection based on the resolved `NameID`. LDAP connections can be\nconfigured from Instance Settings.\n\n**Sign Authn Requests:** Check this box to sign the `AuthnRequest` even if the\nIdentity Provider metadata indicates that it's not required.\n\n**Sign Metadata:** Check this box to sign the metadata XML file.\n\n**SSL Required:** Check this box to reject SAML messages that are not sent\nover HTTPS. This does not affect how URLs are generated.\n\n## Changing the SAML Identity Provider Connection Settings\n\nTo configure @product@'s SAML Identity Provider Settings, navigate to the\nIdentity Provider Connection tab of the SAML Admin portlet and click the *Edit*\naction button on the IdP you want to configure. \n\n**Name:** The name of the Identity Provider with which to connect.\n\n**Entity ID:** The Identity Provider's entity ID. This value must match the\nentity ID declared in the Identity Provider metadata.\n\n**Enabled:** Check the box to enable this IdP. \n\n**Clock Skew:** Clock skew is a tolerance in milliseconds used by the Service\nProvider for mitigating time differences between the clocks of the Identity\nProvider and the Service Provider. This usually only matters when assertions\nhave been made to expire very quickly.\n\n**Force Authn:** Check this box to have the Service Provider ask the Identity\nProvider to re-authenticate the user before verifying the user.\n\n**Metadata:** You can provide a URL to the Identity Provider metadata XML file\nor you can manually upload it. If you provide a URL, the XML file is\nautomatically retrieved and periodically polled for updates. You can change the\nupdate interval in System Settings by modifying the Runtime Metadata Refresh\nInterval property which specifies a number of seconds. If fetching the metadata\nXML file by URL fails, you can't enable the Identity Provider connection. If the\nmetadata is inaccessible via URL, you can upload the XML file manually. In this\ncase, the metadata XML file is not updated automatically. \n\n**Name Identifier Format:** Choose the Name Identifier Format used in the SAML\nResponse. Set this according to what the Service Provider expects to receive.\nFor Liferay Service Providers, selections other than email address indicate\nthat the Name Identifier refers to screen name. The formats don't have any\nspecial meaning to Liferay Identity Providers. The Name Identifier attribute\ndefines the `NameID` value.\n\n**Attribute Mapping:** Attribute mapping is done from the attribute name or\nfriendly name in the SAML Response to the @product@ attribute name. For example,\nif you want to map a response attribute named `mail` to the @product@ attribute\n`emailAddress`, enter the following mapping:\n\n    mail=emailAddress\n\nAvailable @product@ attributes are: `emailAddress`, `screenName`, `firstName`,\n`lastName`, `modifiedDate`, and `uuid`.\n\n**Keep Alive URL:** If users are logged into several @product@ SP instances via\na @product@ IdP, their sessions can be kept alive as long as they keep a browser\nwindow open to one of them. Configure this only if the IdP is @product@. The URL\nis `https://[IdP host name]/c/portal/saml/keep_alive`. On the @product@ IdP,\nconfigure this URL the same way, but point back to this SP. \n\nSave your changes when you are finished configuring the @product@ instance as a\nservice provider. There is no need to restart the server: the changes are\napplied immediately.\n\nMake the above configurations through the SAML Control Panel interface and not\nvia properties. Some features of the Liferay Connector to SAML 2.0 app are not\navailable as properties.\n\n| **Limitation:** The Liferay SAML app can only be used with a single virtual\n| host. Technically, this means that in the SAML metadata for @product@, only one\n| binding can be added in this form:\n| \n| ```xml\n| <md:EntityDescriptor>\n| ...\n| <md:SPSSODescriptor>\n| ...\n| <md:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"https://portal.domain.com/c/portal/saml/acs\" index=\"1\" isDefault=\"true\" />\n| ...\n| </md:SPSSODescriptor>\n| </md:EntityDescriptor>\n\n"
  },
  {
    "path": "en/deployment/articles-dxp/04-securing-liferay/10-saml/06-configuring-saml.markdown",
    "content": "---\nheader-id: configuring-saml\n---\n\n# Configuring SAML\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n   <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/configuring-sso/authenticating-with-saml.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThere are some ways of configuring the SAML plugin outside the UI. This is done\nvia OSGi configuration files and by uploading metadata XML to configure how\nconnections are negotiated. \n\n## OSGi Configuration Properties\n\nAs noted in the previous tutorials, anything related to configuring SP\nconnections must be done through the SAML Admin UI where configurations are\nsaved to Liferay's database. SP connections can no longer be made via properties\nfiles as they were in versions prior to 3.1.0. \n\n| **Note:** Don't use OSGi `.config` files or @product@'s System Settings Control\n| Panel application to configure SAML providers (IdP or SP). The System Settings\n| UI is auto-generated, and is for advanced administrators. It does not perform the\n| enhanced validation on the fields that the SAML Admin UI performs, so it could\n| allow administrators to create invalid configurations.\n\nThis is a portal instance scoped configuration which can be managed via OSGi\nConfiguration Admin. The affected properties are those in the\n`SAMLProviderConfiguration` metatype:\n\n    - `keyStoreCredentialPassword()`\n    - `keyStoreEncryptionCredentialPassword()`\n    - `assertionSignatureRequired()`\n    - `authnRequestSignatureRequired()`\n    - `clockSkew()`\n    - `defaultAssertionLifetime()`\n    - `entityId()`\n\t- `enabled()`\n\t- `ldapImportEnabled`\n\t- `role()`\n\t- `sessionMaximumAge`\n    - `sessionTimeout()`\n    - `signAuthnRequest()`\n    - `signMetadata()`\n    - `sslRequired()`\n    - `allowShowingTheLoginPortlet()`\n\nThe SAML Admin UI remains the place for creating the portal instance scoped\nconfiguration instances.\n\nNote that there is also a system wide configuration, represented by the\n`SamlConfiguration` metatype. \n\nIf you used Liferay 6.2, please note that the following system wide properties\nwere removed:\n\n    `saml.metadata.paths` (served no purpose after removal of SP connection defaults)\n    `saml.runtime.metadata.max.refresh.delay`\n    `saml.runtime.metadata.min.refresh.delay`\n\nThe latter two properties were replaced with the single property\n`com.liferay.saml.runtime.configuration.SamlConfiguration.getMetadataRefreshInterval()`.\n\nNote also the introduction of the *SAML KeyStoreManager Implementation\nConfiguration* in *Control Panel* &rarr; *System Settings* &rarr; Security\n&rarr; SSO. The options for this configuration are explained above in the\nSetting up @product@ as a SAML Identity Provider section.\n\nIn the latest version of the plugin, the `SHA256` algorithm is the default\nencryption algorithm used in the configuration and to generate keys. The default\nconfiguration tries `SHA256`, then `SHA384`, then `SHA512` before falling back to\n`SHA1`. Because `SHA1` is potentially vulnerable, you can blacklist it using this\nproperty: \n\n```properties\nblacklisted.algorithms=[\"blacklisted_algorithm_url\", \"another_blacklisted_algorithm_url\"]\n```\n\nTo blacklist `SHA1`, therefore, you'd have this configuration: \n\n```properties\nblacklisted.algorithms=[\"http://www.w3.org/2000/09/xmldsig#sha1\"]\n```\n\nPlace these in a config file with this name: \n\n```bash\ncom.liferay.saml.opensaml.integration.internal.bootstrap.SecurityConfigurationBootstrap.config\n```\n\nThere's a lot more granularity in how connections are negotiated if you\nconfigure the metadata XML. \n\n## Configuring Negotiation Via metadata.xml\n\nIf the default negotiation configuration doesn't work for you, you can craft\nyour own configuration and upload it. Before doing this, visit your host's\nmetadata URL and save a copy of the configuration in case you need it later: \n\n    http://[hostname]/c/portal/saml/metadata\n\nFor example, if you're stuck connecting to\na legacy IdP that only supports `SHA1`, you can upload a configuration that\ndisables the other algorithms: \n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"samlidp\">\n  <md:IDPSSODescriptor WantAuthnRequestsSigned=\"true\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n    <md:Extensions>\n      <alg:SigningMethod xmlns:alg=\"urn:oasis:names:tc:SAML:metadata:algsupport\" Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/>\n    </md:Extensions>\n    <md:KeyDescriptor use=\"signing\">\n      <ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n        <ds:X509Data>\n          <ds:X509Certificate>... omitted ...</ds:X509Certificate>\n        </ds:X509Data>\n      </ds:KeyInfo>\n    </md:KeyDescriptor>\n    <md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"http://localhost:8080/c/portal/saml/slo\"/>\n    <md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"http://localhost:8080/c/portal/saml/slo\"/>\n    <md:SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"http://localhost:8080/c/portal/saml/sso\"/>\n    <md:SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"http://localhost:8080/c/portal/saml/sso\"/>\n  </md:IDPSSODescriptor>\n</md:EntityDescriptor>\n```\n\nNotice that in the configuration above, the `<md:Extensions>` block has only one\nsigning algorithm: `SHA1`. \n\n| **Note:** Since the default configuration falls back to `SHA1`, you shouldn't\n| need to do this unless your legacy system can't negotiate via the fallback\n| mechanism. Also note that if you blacklisted `SHA1`, this won't work. Due to\n| [vulnerabilities in `SHA1`](https://en.wikipedia.org/wiki/SHA-1), it's best to\n| avoid using it altogether if possible. \n\nIf you've changed your metadata configuration, you can go back to the default\nconfiguration if you saved it before making the change. If you didn't, you can\nprovide a URL instead of an uploaded XML file to one of your peers' metadata\nconfigurations. \n"
  },
  {
    "path": "en/deployment/articles-dxp/06-maintaining-liferay/01-patching-liferay-dxp/01-patching-liferay-dxp-intro.markdown",
    "content": "---\nheader-id: patching-liferay\n---\n\n# Patching @product@\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/patching-dxp-7-3-and-earlier.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nWhile we strive for perfection with every @product@ release, the reality of the\nhuman condition dictates that releases may not be as perfect as originally\nintended. But we've planned for that. Included with every @product@ bundle is a\nPatching Tool that handles installing two types of patches: fix packs and\nhotfixes. \n\n| **Important:** Make sure to\n| [back up your @product@ installation and database](/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation)\n| regularly, especially before patching. The patching tool installs code changes\n| and some of these make data changes (if necessary) automatically on startup.\n| \n| Certain fix packs (service packs) can include data/schema\n| micro changes---they're optional and revertible. Module upgrades and any \n| micro changes they include are applied at server startup by default, or can be \n| applied manually by\n| [disabling the `autoUpgrade` property](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-data-upgrade#configuring-non-core-module-data-upgrades).\n| Server startup skips all Core micro changes. Instead, you\n| can apply them using the [upgrade\n| tool](/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver)\n| before server startup.\n\n| **Important:** Installing the latest service pack on top of @product@ 7.2 \n| GA1/FP1 requires running the\n| [data upgrade tool](/docs/7-2/deploy/-/knowledge_base/d/upgrading-the-product-data).\n| Examine the\n| [@product@ upgrade instructions](/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver)\n| to determine preparations, testing, and post upgrade steps that are \n| appropriate for you. To eliminate system down time during upgrade, consider \n| using the \n| [Blue-green deployment technique](/docs/7-2/deploy/-/knowledge_base/d/other-cluster-update-techniques). \n|\n| @product@ 7.2 FP2/SP1's data schema change adds version columns to several \n| tables.\n| [Hibernate's optimistic locking system](https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch05.html#d0e2225)\n| uses the columns to preserve data integrity during concurrent data \n| modifications. \n\n| **Note:** [Patching a cluster](/docs/7-2/deploy/-/knowledge_base/d/updating-a-cluster)\n| requires additional considerations.\n"
  },
  {
    "path": "en/deployment/articles-dxp/06-maintaining-liferay/01-patching-liferay-dxp/02-patching-basics/01-patching-basics-intro.markdown",
    "content": "---\nheader-id: patching-basics\n---\n\n# Patching Basics\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/patching-dxp-7-3-and-earlier/understanding-patch-types-for-dxp-7-3-and-earlier.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay ships @product-ver@ fixes through three different channels: \n\n- Fix packs \n- Hotfixes \n- Service Packs \n\n## Fix Packs\n\nThe latest fixes that patch the core are bundled together weekly into fix packs\nthat are provided to all of Liferay's customers. Fix packs include fixes for\nboth the core and the applications and modules that ship with @product@. The\nfixes address regressions or obvious bugs and don't require you to make\nadditional changes. Each fix pack contains all previous fix packs since the last\nservice pack. \n\n[Security Fix Packs](https://help.liferay.com/hc/en-us/articles/360035038331) \nare a special fix pack type for deploying critical security fixes quickly\nwithout changing fix pack levels. \n\nFixes that don't fit these requirements are considered for service packs or hot\nfixes. \n\n## Hotfixes\n\nA hotfix is provided to customers when they contact Liferay about an emergency\nsituation, and Liferay's support team---working with the customer---determines\nthat the problem is a product issue that must be fixed very quickly. Support\nfixes the bug and provides a hotfix to the customer immediately. This is\na short-term fix. Hotfixes can patch the core, the applications, and modules. \n\n## Service Packs\n\nService packs are built on top of the original @product@ release and repackaged\nwith the latest fix pack, Patching Tool, and modules. Since a service pack is a\nfix pack, it contains all previous fix packs since the last service pack. Each\none includes the most recent patches and updates. \n\nService packs can also include changes that have these characteristics:\n\n- Require more extensive testing. \n- Require some of your attention, such as updating your documentation.\n- Improve the product. \n\nRather than updating existing @product@ systems with service packs, you should\n\n1.  Keep systems up-to-date with fix packs (according to your own deployment\n    schedule).\n\n2.  Install the latest Marketplace updates frequently.\n\n3.  Update the Patching Tool when necessary.\n\nThis method updates the installation to the service pack levels, while allowing\nscheduled deployments and avoiding full environment rebuilds. \n\n## How Patches are Tested\n\nLiferay extensively tests service packs, fix packs, and hotfixes to ensure high\nquality. Fixes in fix packs go through both automated regression testing and\nmanual testing. Hotfixes receive similar automated testing, and the support\nengineer who fixes a reported issue tests it. \n\nBefore releasing a service pack, Liferay runs test suites on the packaged\nservice pack. \n"
  },
  {
    "path": "en/deployment/articles-dxp/06-maintaining-liferay/01-patching-liferay-dxp/03-using-the-patching-tool/01-using-the-patching-tool-intro.markdown",
    "content": "---\nheader-id: patching-tool\n---\n\n# Using the Patching Tool\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/reference/installing-the-patching-tool.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe Patching Tool installs, removes, compares, and prepares @product@ patches.\nIt is pre-installed in @product@ bundles, easy to install into @product@ manual\ninstallations, and easy to update. The Patching Tool's executable scripts\nfacilitate patching. \n\nHere are the essentials to get started using the Patching Tool:\n\n- [Installing the Patching Tool](#installing-the-patching-tool) (for manual installations only)\n- [Executables](#executables)\n\n## Installing the Patching Tool\n\n@product@ bundles come with the Patching Tool pre-installed (in `[Liferay\nHome]/patching-tool`) and pre-configured with the default settings. Skip this\nsection if you're using a bundle. \n\nIf you installed @product@ manually, however, you must also install the Patching\nTool manually. \n\n1.  Download the Patching Tool from the\n    [Customer Portal](https://customer.liferay.com/downloads?p_p_id=com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_productAssetCategoryId=118191019&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_fileTypeAssetCategoryId=118191066).\n\n2.  Unzip the Patching Tool to your\n    [Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\n    folder (recommended) or to another folder. \n\nAfter installing the Patching Tool, you must\n[configure it to use your @product@ installation](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-patching-tool).\nThe `patching-tool` folder you extracted from the Patching Tool ZIP file\ncontains the Patching Tool, including its executable scripts. \n\n## Executables\n\nThe Unix shell and Windows batch scripts distributed with the Patching Tool make\nit easier to use. On Unix systems, run\n\n```bash\n./patching-tool.sh parameters\n```\n\nOn Windows, run\n\n```bash\npatching-tool parameters\n```\n\nThe Windows command `patching-tool` is used in the examples that follow. On\nUnix, replace the name of the executable before running the commands. \n\nInstalling patches is next! \n"
  },
  {
    "path": "en/deployment/articles-dxp/06-maintaining-liferay/01-patching-liferay-dxp/03-using-the-patching-tool/02-installing-patches.markdown",
    "content": "---\nheader-id: installing-patches\n---\n\n# Installing Patches\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/patching-dxp-7-3-and-earlier/installing-patches-for-dxp-7-3-and-earlier.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nBefore installing any patches, you must shut down your server. On Windows\noperating systems, files in use are locked by the OS, and can't be patched. On\nUnix-style systems, you can usually replace files that are running, but the old\nones reside in memory. For these reasons, it is best to shut down @product@\nbefore installing patches. \n\nLiferay distributes all patches (fix packs and hotfixes) as ZIP files. When you\ndownload a patch, either from a\n[Help Center](https://help.liferay.com/hc)\nticket (hotfix) or from the\n[Customer Portal](https://customer.liferay.com/downloads)\n(fix pack), place it in the Patching Tool's `patches` folder (e.g., `[Liferay\nHome]/patching-tool/patches`) without unzipping it. To list your installed\npatches and available local patches, execute this command:\n\n```bash\npatching-tool info\n```\n\nThis displays a list of patches you've already installed, along with a list of\npatches that *can* be installed from what's in the `patches` folder. \n\nTo install the available patches, use the following steps. First, \nissue the following command: \n\n```bash\npatching-tool install\n```\n\nTo make sure the all changed OSGi bundles replace the existing ones, delete the\n`osgi/state` folder from the \n[Liferay Home folder](/docs/7-2/deploy/-/knowledge_base/d/liferay-home). \n\n| **Note**: The `osgi/state` folder contains OSGi bundle state information. If an\n| OSGi bundle's changes in a hot fix or fix pack are internal only, they are\n| invisible to the OSGi framework, that OSGi bundle stays installed, and its state\n| information stays unchanged. Hot fixes, for example, may contain in-place\n| changes that do not use the API. The framework cannot detect such changes.\n| A fix pack's changes may also be transparent to the framework. For these\n| reasons, deleting the `osgi/state` folder after applying fix packs and hot fixes\n| is recommended.\n\n| **Important**: The `osgi/state` folder should ONLY be deleted when working in a\n| development environment or when applying a fix pack or hot fix.\n\nIf there are new database indexes created by the patch, the Patching Tool tells\nyou to update them. To get the list, run this command:\n\n```bash\npatching-tool index-info\n```\n\nSince there's no database connection at patching time, the indexes must be\ncreated at portal startup. If the server has permissions to modify the database\nindexes, instruct @product@ to create the indexes automatically at startup by\nadding this setting to your `portal-ext.properties` file:\n\n```properties\ndatabase.indexes.update.on.startup=true\n```\n\nOtherwise, you must create the indexes manually. Check the \n`patching-tool index-info` command output for more details.\n\nAfter installing patches, you can execute the `patching-tool info` command to\nverify them. \n\n| **Note:** If there are any issues with the installed patches, verify that there\n| aren't any remaining files from the previous patch installation of a fix pack or\n| hotfix within the application server cache.\n\nDuring the installation, `patching-backup-deps.zip` and `patching-backup.zip`\nfiles are created and stored in the web application's `WEB-INF` folder. These\nfiles are required to restore the @product@'s original state; removing them\ndisables patching. \n\n| **Note:** When installing patches, @product@'s `web.xml` is always overwritten\n| by the one contained in the patch. If you've customized `web.xml`, you must\n| re-implement your customizations after installing a patch.\n\nThe `patching-backup.zip` file is necessary for installing future patches,\nbecause the Patching Tool reverts the installed fix pack before installing a new\none. To revert the installed fix pack, it examines the contents of the\n`patching-backup.zip` to determine the changes that it needs to revert. \n\n## Handling Hotfixes and Patches\n\nAs stated previously, hotfixes are short term fixes provided as quickly as\npossible, and fix packs are larger bundles of hotfixes provided to all customers\nat regular intervals. If you already have a hotfix installed and the fix pack\nthat contains that hotfix is released, the Patching Tool can manage this for\nyou. Fix packs always supersede hotfixes; so when you install your fix pack, the\nhotfix it contains is uninstalled and the fix pack version is installed in\nits place. \n\nThe Patching Tool applies fixes to fix packs automatically. If a new (fixed)\nversion of a fix pack is released, install it with the Patching Tool. The\nPatching Tool uninstalls the old fix pack and installs the new version in its\nplace. \n\n## Fix Pack Dependencies\n\nSome hotfixes depend on fix packs. If you attempt to install a hotfix that\ndepends on a fix pack, the Patching Tool notifies you. Go to the \n[Customer Portal](hhttps://customer.liferay.com/downloads)\nand obtain the hotfix dependency. Once all the necessary patches are available\nin the `patches` folder, the Patching Tool installs them. \n\n## Updating the Patching Tool\n\nWhen a patch you're trying to install requires a Patching Tool update, the\nPatching Tool tells you. To update the Patching Tool, download the latest one\nfrom the\n[Customer Portal](https://customer.liferay.com/downloads?p_p_id=com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_productAssetCategoryId=118191019&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_fileTypeAssetCategoryId=118191066).\nOverwrite the existing Patching Tool by unzipping the new one to the\n`patching-tool` folder's parent folder. \n\n## Cleaning Up\n\nAfter you've performed your patching procedure (whether you've installed or\n[removed patches](/docs/7-2/deploy/-/knowledge_base/d/working-with-patches#uninstalling-patches)),\nit's important to clean up @product@'s cache of deployed code. This ensures that\nyou're using the revision you've just installed the patches for when you start\nthe server. This is really easy to do. \n\nTo clear out the cached code, remove the contents of the `[Liferay Home]/work`\nfolder. Now you're ready to start your server.\n"
  },
  {
    "path": "en/deployment/articles-dxp/06-maintaining-liferay/01-patching-liferay-dxp/03-using-the-patching-tool/03-working-with-patches.markdown",
    "content": "---\nheader-id: working-with-patches\n---\n\n# Working with Patches\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/patching-dxp-7-3-and-earlier/advanced-patching-for-dxp-7-2.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nHere are some things you might need to do with patches:\n\n- Report Patch Levels to Liferay Support\n- Uninstall Patches\n- Show collisions between patches and deployed plugins\n- Separate Patches from your Installation\n\nStart with reporting patch levels to Liferay Support.\n\n## Including support-info in Support Tickets\n\nProviding your environment's information (e.g., hardware architecture) and patch\nlevel to Liferay Support is critical for reproducing your issues. Write your\nsupport information (including your patch level) to a file by executing this\ncommand: \n\n```bash\npatching-tool support-info\n```\n\nThe support information is written to file\n`patching-tool-support-info-actual-timestamp.txt` in your `patching-tool`\nfolder. Please upload this file to the\n[Help Center](https://help.liferay.com/hc)\nticket.\n\n## Uninstalling Patches\n\nHave you noticed that the Patching Tool only seems to have an `install` command?\nThis is because patches are managed not by the command, but by what appears in\nthe `patches` folder. You manage the patches you have installed by adding or\nremoving patches from this folder.\n\nHere's how to uninstall (remove) a patch:\n\n1.  Remove the patch from your `patches` folder. \n\n2.  Run the `patching-tool install` command. \n\nTo revert ALL patches, run this command:\n\n```bash\npatching-tool revert\n```\n\nNow you know how to remove and revert patches. You can also \ncompare patch levels. See the [reference guide](/docs/7-2/deploy/-/knowledge_base/d/comparing-patch-levels) \nfor a list of the available commands. \n\n## Showing collisions between patches and deployed plugins\n\nSome patches update files you might have customized via a plugin. The\n`patching-tool list-collisions` command lists differences (collisions) between\ninstalled patch files and your plugin's version of them. Here's the command:\n\n```bash\npatching-tool list-collisions\n```\n\nIt is an alias for the following diff command:\n\n```bash\npatching-tool diff collisions files _base\n```\n\n`_base` is the literal patch level name. Collisions are only listed for\ninstalled patches that contain source code files. \n\n| **Note:** As of Patching Tool 2.0.9, `patching-tool list-collisions` lists only\n| JSP file collisions in fragment bundles.\n\n## Separating Patches from the Installation\n\nThe Patching Tool's `separate` command helps reduce the patched @product@\ninstallation size. If the installation has been patched, you can make it smaller\nby moving the restore files out of it.\n\nPatched installations are large because the restore files are stored inside the\nweb application's `WEB-INF` folder by default. These files are required for\npatching the installation again.\n\nIf these files are removed, subsequent patching processes fail. Because of\nthis, Liferay added an option to separate the patching files from the\ninstallation while still preserving and restoring them safely when new patches\narrive. To do this, use this command: \n\n```bash\npatching-tool separate [separation_name] \n```\n\nThis command produces a `liferay-patching-files-[separation-name].zip` file in\nthe Patching Tool's `patches` folder. It contains the necessary files and\nmetadata for patching, verification, and validation. Once you create this file,\nthe patch files are removed from their default location and are now only\navailable in this file. You can store this file elsewhere to reduce your\ninstallation's size. \n\n**WARNING:** If the product is separated from its patches in this way, you\ncannot run most of the Patching Tool commands until the patches are restored.\n\nAfter the separation process only the following commands can be used:\n\n- `auto-discovery`\n- `info`\n- `setup`\n\nAny other command returns this:\n\n```bash\nThis installation does not include data for patching. Please copy the\nliferay-patching-files-[separation-name].zip file into the 'patches' directory\nand run patching-tool setup. \n```\n\nThis is how you restore the patch files to your system. Details below. \n\n## Restoring the Separated Patch Files\n \nWhen you need to patch @product@ again, you must restore the separated patch\nartifact. To do this, copy the `liferay-patching-files-[separation-name].zip`\nback to the Patching Tool's `patches` folder and run `patching-tool setup`\ncommand.\n\nThe command finds the necessary patching artifact and restores the patch files\nto the installation. After that, the Patching Tool works like it did prior to\nseparating the patches. \n"
  },
  {
    "path": "en/deployment/articles-dxp/06-maintaining-liferay/01-patching-liferay-dxp/04-configuring-the-patching-tool/01-configuring-the-patching-tool-intro.markdown",
    "content": "---\nheader-id: configuring-the-patching-tool\n---\n\n# Configuring the Patching Tool\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/reference/configuring-the-patching-tool.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe Patching Tool installs @product@ patches. It ships with prepackaged\n@product@ bundles. If any of the following scenarios describes your @product@\ninstallation, however, you must configure the Patching Tool manually:\n\n- Installed @product@ manually on an existing application server\n- Customized your @product@ folder structure\n- Running in a cluster\n\nIf none of the above scenarios describe your installation, you can skip this\nsection.\n\nIf you installed @product@ manually, you must also install the Patching Tool\nmanually. Download it from the\n[Customer Portal](https://customer.liferay.com/downloads?p_p_id=com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_productAssetCategoryId=118191019&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_fileTypeAssetCategoryId=118191066).\nUnzipping it to your\n[Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\nfolder is the easiest way to use it. \n\nRead on to configure the Patching Tool for your environment. \n"
  },
  {
    "path": "en/deployment/articles-dxp/06-maintaining-liferay/01-patching-liferay-dxp/04-configuring-the-patching-tool/02-patching-tool-basic-configuration.markdown",
    "content": "---\nheader-id: patching-tool-basic-configuration\n---\n\n# Patching Tool Basic configuration\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/reference/configuring-the-patching-tool.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThere are two ways to configure the Patching Tool:\n\n1.  Automatically by executing the `auto-discovery` command\n\n2.  Manually by editing the configuration file (see\n    [Patching Tool Advanced Configuration](/docs/7-2/deploy/-/knowledge_base/d/patching-tool-advanced-configuration))\n\nAutomatic configuration generates the configuration files by looking for\n@product@ files in the local file system. By default the Patching Tool looks for\nthem in its parent folder. To start the process, run this command in your\nPatching Tool folder (`patching-tool`):\n\n```bash\npatching-tool auto-discovery\n```\n\nIf @product@ is not installed in the parent folder, specify its location: \n\n```bash\npatching-tool auto-discovery /opt/liferay-dxp\n```\n\nIf you specified the wrong location of @product@ or it is not in the parent\nfolder, the Patching Tool can't find the\n[Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\nand reports an error like this: \n\n```bash\nThe .liferay-home has not been detected in the given directory tree.\n\nConfiguration:\npatching.mode=binary\nwar.path=../tomcat-9.0.17/webapps/ROOT/\nglobal.lib.path=../tomcat-9.0.17/lib/ext/\nliferay.home=**[please enter manually]**\n\nThe configuration hasn't been saved. Please save this to the default.properties file.\n```\n\nHere are ways to resolve the Liferay Home issue:\n\n- Specify the Liferay Home path in the `default.properties` file.\n- If the Liferay Home is in the Patching Tool's tree, create a `.liferay-home` \n  file in the Liferay Home folder and re-run the  auto-discovery process. \n\nWhen the Patching Tool is configured, running `patching-tool info` reports\nproduct version information. \n\nThat's it! Now that you've installed and configured the Patching Tool, you're\nready to download and install patches. \n"
  },
  {
    "path": "en/deployment/articles-dxp/06-maintaining-liferay/01-patching-liferay-dxp/04-configuring-the-patching-tool/03-patching-tool-advanced-configuration.markdown",
    "content": "---\nheader-id: patching-tool-advanced-configuration\n---\n\n# Patching Tool Advanced Configuration\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/reference/configuring-the-patching-tool.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nBy default, the Patching Tool's configuration file called `default.properties`\nis in the tool's folder.\n\nA Patching Tool configuration file typically looks like this:\n\n```properties\npatching.mode=binary\nwar.path=../tomcat-9.0.17/webapps/ROOT/\nglobal.lib.path=../tomcat-9.0.17/lib/ext/\nliferay.home=../\n```\n\nThe properties above (described fully in [Patching Tool Configuration Properties](/docs/7-2/deploy/-/knowledge_base/d/patching-tool-configuration-properties)) \ndefine the location of\n[Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home),\nthe patching mode (binary or source), the path to where WAR files are deployed\nin the app server, and the global library path. The tool's auto-discovery bases\nthe OSGi module framework paths on the Liferay Home. If, however, you changed\nthe OSGi module framework paths to something different than those under the\ndefault folder `[Liferay Home]/osgi`, you must manually specify the following\nproperties: \n\n```properties\nmodule.framework.core.path=path_to_modules_core_dir\nmodule.framework.marketplace.path=path_to_modules_marketplace_dir\nmodule.framework.modules.path=path_to_modules_modules_dir\nmodule.framework.portal.path=path_to_modules_portal_dir\nmodule.framework.static.path=path_to_modules_static_dir\n```\n\nUsing auto-discovery and working with the default profile `default.properties`\nis the easiest way to use the Patching Tool, and is great for smaller, single\nserver installations. But many @product@ installations serve millions of pages\nper day, and the Patching Tool has been designed for this as well. So if you're\nrunning a small, medium, or large cluster of @product@ machines, you can use the\nPatching Tool profiles to manage patching for all of them. \n\n## Using Profiles with the Patching Tool\n\nYou can create profiles for multiple runtimes by running auto-discovery or\ncreating them manually. To auto-discover other runtimes, run the Patching Tool\nwith parameters like this: \n\n```bash\n./patching-tool.sh [name of profile] auto-discovery [path/to/Liferay Home]\n```\n\nThis runs the same discovery process, but on the path you specify. It writes the\nprofile information to a file called `[name of profile].properties`.\nAlternatively, you can manually create profile property files in your\n`patching-tool` folder. \n\nSee [Patching Tool configuration properties](/docs/7-2/deploy/-/knowledge_base/d/patching-tool-configuration-properties) \n(profile properties) for a complete list of the available configuration \nproperties. \n\nYou can have as many profiles as you want and use the same Patching Tool to\npatch all of them. This helps to keep all your installations in sync.\n"
  },
  {
    "path": "en/deployment/articles-dxp/06-maintaining-liferay/01-patching-liferay-dxp/04-configuring-the-patching-tool/04-installing-patches-on-the-liferay-war.markdown",
    "content": "---\nheader-id: installing-patches-on-the-liferay-de-war\n---\n\n# Installing patches on the @product-ver@ WAR\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/patching-dxp-7-3-and-earlier/installing-patches-for-dxp-7-3-and-earlier.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nIf you\n[installed @product@ manually](/docs/7-1/deploy/-/knowledge_base/d/installing-product-on-tomcat)\nas a WAR file on a supported application server, you must apply patches to the\nWAR file and supporting files and re-deploy them. This article shows you how to\ndo that.\n\n## Prerequisites\n\nDownload the necessary artifacts from the \n[Customer Portal:](https://customer.liferay.com/downloads)\n\n- @product@ WAR file (`liferay-dxp-[version].war`)\n- Dependencies ZIP file (`liferay-dxp-dependencies-[version].zip`)\n- OSGi JARs ZIP file (`liferay-dxp-osgi-[version].zip`) \n- [Latest Patching Tool](https://customer.liferay.com/downloads?p_p_id=com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_productAssetCategoryId=118191019&_com_liferay_osb_customer_downloads_display_web_DownloadsDisplayPortlet_fileTypeAssetCategoryId=118191066)\n\n## Install the patch on the @product@ WAR and artifacts\n\n1.  Create an arbitrary folder. Unzip the dependency artifacts and the \n    Patching Tool into it. The folder contents should look like this:\n\n    - `[patching-home]/`\n        - `liferay-dxp-dependencies-[version]/` &larr; Unzipped Dependencies\n        - `osgi/` &larr; Unzipped OSGi JARs\n        - `patching-tool/` &larr; Unzipped Patching Tool\n        - `liferay-dxp-[version].war/` &larr; @product@ WAR File\n\n2.  Create the default profile configuration file in the Patching Tool folder:\n    `patching-home/patching-tool/default.properties`. The contents should look\n    like this:\n\n```properties\npatching.mode=binary\nwar.path=../../patching-home/liferay-dxp-[version].war\nglobal.lib.path=../../patching-home/liferay-dxp-dependencies-[version]\nliferay.home=../../patching-home\n```\n\n    If you're using a different OSGi folder structure, you can specify it as\n    the [Patching Tool Advanced Configuration](/docs/7-2/deploy/-/knowledge_base/d/patching-tool-advanced-configuration)\n    documentation describes: \n\n```properties\nmodule.framework.core.path=/osgi-home/osgi/core\nmodule.framework.marketplace.path=/osgi-home/osgi/marketplace\nmodule.framework.modules.path=/osgi-home/osgi/modules\nmodule.framework.portal.path=/osgi-home/osgi/portal\nmodule.framework.static.path=/osgi-home/osgi/static\t\n```\n\n3.  Download the patch (fix pack or hotfix) to install and put it in a folder \n    called `patches` in your Patching Tool folder \n    (i.e. `[patching-home]/patching-tool/patches`).\n\n4.  Execute the Patching Tool's `info` command:\n\n```bash\n/patching-home/patching-tool> patching-tool info\nLoading product and patch information...\nProduct information:\n  * installation type: binary\n  * build number: 7210\n  * service pack version:\n    - available SP version: Not available\n    - installable SP version: Not available\n  * patching-tool version: 2.0.12\n  * time: 2019-06-03 18:30Z\n  * host: 91WRQ72 (8 cores)\n  * plugins: no plugins detected\n\nCurrently installed patches: -\n...\n```\n\n5.  Install the patch. \n\n```bash\n/patching-home/patching-tool> patching-tool.sh  install\nOne patch is ready to be installed. Applying dxp...\nCleaning up: [1%..10%..20%..30%..40%..50%..60%..70%..80%..90%..100%]\nInstalling patches: [1%..10%..20%..30%..40%..50%..60%..70%..80%..90%...100%]\nThe installation was successful. One patch is installed on the system.\n```\n\nGreat! You have successfully patched the artifacts, and they are ready to be\ndeployed on any supported Application Server.\n\n## Related Topics\n\n[Patching Tool Advanced Configuration](/docs/7-2/deploy/-/knowledge_base/d/patching-tool-advanced-configuration)\n\n[Deploying @product@](/docs/7-2/deploy/-/knowledge_base/d/deploying-product)\n"
  },
  {
    "path": "en/deployment/articles-dxp/06-maintaining-liferay/01-patching-liferay-dxp/05-keeping-up-with-fix-packs/01-keeping-up-with-fix-packs-intro.markdown",
    "content": "---\nheader-id: keeping-up-with-fix-packs-and-service-packs\n---\n\n# Keeping up with Fix packs and Service Packs\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/patching-dxp-7-3-and-earlier/understanding-patch-types-for-dxp-7-3-and-earlier.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe *Announcements* section on\n[Liferay's Help Center page](https://help.liferay.com/hc)\nlists all fix pack updates, security alerts, product releases, and system updates. The\napproximate frequency of fix pack and service pack releases is explained\n[here](/docs/7-2/deploy/-/knowledge_base/d/patching-basics).\nThe *Receive Notifications* sidebar lets you subscribe to the latest updates on\nproducts, patches, and system improvements. \n\nClick *Downloads* on the Liferay Digital Experience Platform page to access:\n\n-   Latest Release\n-   Fix Packs\n-   Service Packs Archive\n-   Security Advisories\n-   Patching Tool\n\nClick *Support Information* to access the compatibility matrix, support FAQs,\nand more. \n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/01-intro.markdown",
    "content": "---\nheader-id: managing-liferay-dxp-with-liferay-connected-services\n---\n\n# Managing Liferay DXP with Liferay Connected Services\n\n[TOC levels=1-4]\n\nLiferay Connected Services (LCS) is a set of tools and services for managing and\nmonitoring your @product@ instances. LCS can help you install fix packs, monitor\nyour instances' performance, activate your instances, and help you manage your\nsubscriptions. In other words, LCS is like a butler for the mansion that is\n@product@. It's like having a single butler that can serve several mansions at once! \n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nBefore going any further, you should take note of a few key terms used \nthroughout this guide: \n\n**Project:** Represents a group of users belonging to a company or \norganization. For example, a project can consist of all the users from a \nproject team or business unit, or it can include the entire company. \n\n**Environment**: Represents a physical cluster of servers or a virtual or\nlogical aggregation of servers. \n\n**Server**: Describes a concrete @product@ instance. It can be a standalone \nserver or a cluster node. \n\nAs you go through this guide, you'll cover the following topics: \n\n-   [Getting Started](/docs/7-2/deploy/-/knowledge_base/d/getting-started-with-lcs)\n-   [LCS Preconfiguration](/docs/7-2/deploy/-/knowledge_base/d/lcs-preconfiguration)\n-   [Registering Your @product@ Server with LCS](/docs/7-2/deploy/-/knowledge_base/d/activating-your-liferay-dxp-server-with-lcs)\n-   [Using LCS](/docs/7-2/deploy/-/knowledge_base/d/using-lcs)\n-   [Troubleshooting Your LCS Connection](/docs/7-2/deploy/-/knowledge_base/d/troubleshooting-your-lcs-connection)\n\nYou'll get started with the configuration steps required to use LCS with \n@product@. \n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/02-getting-started.markdown",
    "content": "---\nheader-id: getting-started-with-lcs\n---\n\n# Getting Started with LCS\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nTo use LCS, you must register a server in an LCS environment. An LCS environment \nrepresents a physical cluster of servers or a virtual or logical aggregation of \nservers. Each environment is part of an LCS project. An LCS project represents a \ngroup of users belonging to a company or organization. For example, a project \ncan consist of all the users from a project team or business unit, or it can \ninclude the entire company. \n\nLCS projects don't initially contain any environments. You must therefore create \none before you can register any servers in LCS. The first time you log in to \n[lcs.liferay.com](https://lcs.liferay.com), \nLCS presents you with a wizard that walks you through the environment creation \nprocess. Click *Get Started* to begin. \n\n![Figure 1: Click *Get Started* to begin the wizard.](../../images-dxp/lcs-onboarding-00.png)\n\nEach of these steps corresponds to a step in the wizard: \n\n1.  Select the LCS project for your new environment. You can select any of \n    your available LCS projects. Note that each project lists its available \n    subscriptions and whether it supports elastic subscriptions. \n\n    ![Figure 2: Select the LCS project you want to create the environment in, and click *Next*.](../../images-dxp/lcs-onboarding-01.png)\n\n2.  Name and describe the environment. The name is mandatory, but the \n    description is optional. Although you can enter anything you wish in these \n    fields, it's best to choose a name and description that accurately identify \n    the environment (e.g., Development, Production, Test, etc.). Note that you \n    can change these values after creating the environment. \n\n    ![Figure 3: Name and describe the environment, then click *Next*.](../../images-dxp/lcs-onboarding-02.png)\n\n3.  Select the environment's subscription type from the project's available\n    subscriptions. Even if you won't use LCS to activate the servers defined for\n    this environment, you must still select a subscription type. Also note that\n    you can't change this selection after creating the environment. \n\n    ![Figure 4: Select the environment's subscription type, then click *Next*.](../../images-dxp/lcs-onboarding-03.png)\n\n4.  Select whether servers that connect to this environment are part of a \n    cluster. LCS provides additional tools in clustered environments that help\n    you manage the cluster. For example, clustered environments show\n    cluster-specific metrics, and fix packs apply to all cluster nodes. There\n    are a few things to keep in mind if you set the environment as clustered: \n\n    -   You can't change this setting after creating the environment.\n    -   Each clustered environment can only support nodes that belong to a \n        single cluster. To connect a different cluster's nodes, you must create \n        a separate clustered environment exclusively for those nodes. \n    -   You must set the portal property `cluster.link.enabled` to `true` in any \n        servers that connect to a clustered environment. \n\n    ![Figure 5: Select whether this is a clustered environment, then click *Next*.](../../images-dxp/lcs-onboarding-04.png)\n\n5.  Select whether the environment allows elastic subscriptions. Elastic \n    subscriptions let you register an unlimited amount of servers. This is \n    critical for auto-scaling situations in which servers are created and \n    destroyed automatically in response to demand. Elastic environments are also \n    useful for bringing additional servers online on a temporary basis for any \n    other purpose, such as business continuity planning. For more information, \n    see \n    [the documentation on elastic subscriptions](/docs/7-2/deploy/-/knowledge_base/d/managing-liferay-dxp-subscriptions#elastic-subscriptions). \n    Also note that you can't change this selection after creating the \n    environment. \n\n    ![Figure 6: Select whether this is an elastic environment, then click *Next*.](../../images-dxp/lcs-onboarding-05.png)\n\n6.  Enable the LCS service you want to use with servers that connect to this \n    environment. The following service is available: \n\n    **Liferay Instance Activation:** Enabling this lets LCS activate any \n    @product@ instances that connect to the environment. If you disable this \n    service, you must activate via an XML file from Liferay support, and \n    such instances must run version 5.0.0 or newer of the LCS client app. \n\n    Note that you **must** use LCS for activation of Elastic subscriptions. \n    Otherwise, you don't have to use LCS for activation. \n\n    Portal Analytics, Fix Pack Management and Portal Properties Analysis have been removed from the list of available services. For more information about this change, please read [this article](https://help.liferay.com/hc/en-us/articles/360037317691-Liferay-Connected-Services-Feature-Deprecation-Update-March-2020)\n\n    ![Figure 7: Enable or disable the LCS services you want to use for servers that connect to the environment, then click *Next*.](../../images-dxp/lcs-onboarding-06.png)\n\n7.  A completed form presents your selections. Review them and make any changes \n    that you want. When you're finished, click *Create Environment*. \n\n    ![Figure 8: This form contains each of your selections from the previous steps. Make any changes you want, then click *Create Environment*.](../../images-dxp/lcs-onboarding-07.png)\n\nAfter creating your environment, the wizard shows a screen that lets you\ndownload the LCS client app, download the environment's token file, and go to\nyour project's dashboard in LCS. Before registering a server in your new\nenvironment, however, you must complete the \n[preconfiguration steps](/docs/7-2/deploy/-/knowledge_base/d/lcs-preconfiguration) \nfor that server. \n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/03-lcs-preconfiguration.markdown",
    "content": "---\nheader-id: lcs-preconfiguration\n---\n\n# LCS Preconfiguration\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nBefore registering your server with LCS, there are a few things you must \nconfigure. The sections in this guide walk you through these steps: \n\n1.  [Downloading the LCS Client App](#downloading-the-lcs-client-app)\n2.  [Preconfiguring LCS to Connect Through a Proxy](#preconfiguring-lcs-to-connect-through-a-proxy)\n3.  [Ensuring Access to LCS](#ensuring-access-to-lcs)\n4.  [NTP Server Synchronization](#ntp-server-synchronization)\n5.  [Configuring WebSphere](#configuring-websphere): \n    This is only necessary if you're running @product@ on the WebSphere \n    application server. \n6.  [Installing the LCS Client App](#installing-the-lcs-client-app)\n\n[The last section](#upgrading-the-lcs-client-app) \nin this guide shows you how to upgrade the LCS client app once your server is \nregistered with LCS. We highly recommend that you upgrade the app whenever \nLiferay releases a new version of it. \n\n| **Note:** You must use LCS for activation of Elastic subscriptions. Otherwise, \n| you don't have to use LCS for activation. You can instead request an XML \n| activation key from Liferay Support. \n\n## Downloading the LCS Client App\n\nThe LCS client app is included in each @product@ bundle and autodeploys when the \nbundle starts. The included version of the app, however, may be outdated. To get\nthe latest version of the LCS client app, you must first download it via Liferay\nMarketplace. \n\n| **Note:** Even though Liferay Marketplace and this guide use the term \n| *purchase*, the LCS client app is free of charge. The purchase process for a \n| free app in Liferay Marketplace adds the app to your Liferay project, much \n| like downloading a free app in a mobile app store adds the app to your \n| account. \n\nUse these steps to purchase and download the app (if you've already purchased \nthe app, you can skip to step 3 to download it): \n\n1.  Navigate to \n    [the LCS client app in Liferay Marketplace](https://web.liferay.com/marketplace/-/mp/application/71774947). \n    Sign in to Marketplace, then click the LCS client app's *Free* button. \n\n    ![Figure 1: Click the app's *Free* button to begin the purchase process.](../../images-dxp/lcs-client-app-marketplace.png)\n\n2.  Select your project, accept the license agreement, and then click the\n    *Purchase* button. Marketplace then displays your receipt. \n\n    ![Figure 2: Liferay Marketplace displays your receipt for the LCS client app.](../../images-dxp/lcs-client-app-receipt.png)\n\n3.  On the receipt, click *See Purchased*. This shows where you can download the\n    LCS client app. To download the app, click the *App* button next to the\n    latest version of the app. \n\n    | **Note:** If you must download the LCS client app later, such as when\n    | [upgrading it](#upgrading-the-lcs-client-app), \n    | select *Purchased Apps* from the User menu at the top-right of Liferay \n    | Marketplace. On the Purchased Apps screen, select the project you \n    | associated with the LCS client app and then select the app. This takes you \n    | to the same downloads page shown in the screenshot. \n\n    ![Figure 3: Click the *App* button next to the version of the app you want to download.](../../images-dxp/lcs-client-download-page.png)\n\nGreat! You've successfully downloaded the LCS client app. Before installing it,\nhowever, there are a few additional pre-configuration steps you should complete.\nThese appear next; then you'll learn how to install the app. \n\n| **Note:** If your server connects to the Internet through a proxy, you must \n| configure your server or the LCS client app **before** deploying the app. The\n| following section contains instructions on this. If your server doesn't \n| connect through a proxy, skip this section. \n\n## Preconfiguring LCS to Connect Through a Proxy\n\nIf your server connects to the Internet through a proxy, you must set some \nproperties **before** deploying the LCS client app. There are two ways to do \nso---chose only one: \n\n1.  [As JVM app server arguments](#jvm-app-server-arguments). \n\n2.  [As LCS client app properties](#lcs-client-app-properties). \n\n| **Note:** Use only one of these methods to configure your server to connect \n| through a proxy. \n\n### JVM App Server Arguments\n\nTo set the proxy properties in your server, set them as JVM app server \narguments. Set these properties to the appropriate values for your proxy: \n\n```properties\n-Dhttp.proxyHost=\n-Dhttp.proxyPort=\n-Dhttp.proxyUser=\n-Dhttp.proxyPassword=\n-Dhttps.proxyHost=\n-Dhttps.proxyPort=\n```\n\nNote that the `user`, `password`, and `https` properties are only needed if your \nproxy requires authentication. \n\n### LCS Client App Properties\n\nTo set the proxy properties via the LCS client app, you must create and deploy \na config file containing the properties. Follow these steps to do so: \n\n1.  Create the config file \n    `com.liferay.lcs.client.configuration.LCSConfiguration.config`. In the steps \n    that follow, you'll set the proxy properties in this file. \n\n2.  Set these `proxy*` properties to the appropriate values for your proxy: \n\n    ```properties\n    proxyHostName=\"\"\n    proxyHostPort=\"\"\n    ```\n\n3.  If your proxy requires authentication, pass the credentials via these \n    properties: \n\n    ```properties\n    proxyHostLogin=\"\"\n    proxyHostPassword=\"\"\n    ```\n\n4.  If your proxy requires NTLM authentication, you must also populate these \n    properties: \n\n    ```properties\n    proxyAuthType=\"ntlm\"\n    proxyDomain=\"\"\n    proxyWorkstation=\"\"\n    ```\n\n    Be sure to set `proxyDomain` and `proxyWorkstation` to the appropriate \n    values for your proxy. Note that you can leave `proxyWorkstation` blank if \n    you don't need it. \n\n5.  Deploy the config file to `osgi/configs`. \n\n## Ensuring Access to LCS\n\nFor the LCS client app to work, it must be able to access the following DNS \nnames. If your server is behind a proxy and/or a firewall, then you must open \naccess to these: \n\n- `lcs.liferay.com`\n- `lcs-gateway.liferay.com`\n\nAs an added security measure, you can also restrict traffic to HTTPS. \n\nThe next section discusses NTP server synchronization. \n\n## NTP Server Synchronization\n\nFor LCS to work properly, the application server running @product@ should be \nsynchronized with a time server. If it's not, you may get log errors similar to \nthese: \n\n    ERROR [pool-6-thread-3][HandshakeTask:68] java.lang.RuntimeException: Handshake expired. \n    Check that the server is synchronized with an NTP server. \n\n    WARN [liferay/hot_deploy-1][LCSHotDeployMessageListener:186] LCS portlet is not connected \n    java.lang.RuntimeException: com.liferay.jsonwebserviceclient.JSONWebServiceInvocationException: \n    com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'oauth_problem': was expecting \n    ('true', 'false' or 'null')_ at [Source: oauth_problem=timestamp_refused&oauth_acceptable_timestamps=1477311475-1477312075; \n    line: 1, column: 14] [Sanitized]\n\nFor information on how to synchronize your application server with a time \nserver, see your application server's documentation. \n\n## Configuring WebSphere\n\nIBM &reg; WebSphere &reg; is a trademark of International Business Machines \nCorporation, registered in many jurisdictions worldwide. \n\nIf you're running the WebSphere application server, then there are some \nadditional configuration steps you must take before deploying the LCS client \napp: \n\n1.  Shut down the application server. \n\n2.  Add these properties in a `portal-ext.properties` file: \n\n    ```properties\n    module.framework.properties.org.osgi.framework.bootdelegation=\\\n        __redirected,\\\n        com.sun.ccpp,\\\n        com.sun.ccpp.*,\\\n        com.liferay.aspectj,\\\n        com.liferay.aspectj.*,\\\n        com.liferay.portal.servlet.delegate,\\\n        com.liferay.portal.servlet.delegate*,\\\n        com.sun.crypto.*,\\\n        com.sun.image.*,\\\n        com.sun.jmx.*,\\\n        com.sun.jna,\\\n        com.sun.jndi.*,\\\n        com.sun.mail.*,\\\n        com.sun.management.*,\\\n        com.sun.media.*,\\\n        com.sun.msv.*,\\\n        com.sun.org.*,\\\n        com.sun.syndication,\\\n        com.sun.tools.*,\\\n        com.sun.xml.*,\\\n        com.yourkit.*,\\\n        com.ibm.crypto.*,\\\n        sun.*,\\\n        javax.validation,\\\n        javax.validation.*,\\\n        jdk.*,\\\n        weblogic.jndi,\\\n        weblogic.jndi.*\\\n    ```\n\n3.  In your @product@ installation, delete the `osgi/state` folder. \n\n4.  Start the application server. \n\n5.  Navigate to the WebSphere console in a browser. \n\n6.  Select your server and navigate to *Java and Process Management* &rarr; \n    *Process Definition* &rarr; *Additional Properties*. \n\n7.  Select *Java Virtual Machine* &rarr; *Custom Properties*. \n\n8.  Click *New*, and enter the following:\n\n    -   Name: `com.ibm.crypto.provider.DoRSATypeChecking`\n    -   Value: `false`\n\n9.  Click *Save*, then *OK* to apply changes to the master configuration. \n\nNote that for LCS client app versions prior to 5.0.0, you must also change the \nvalue of the `digital.signature.algorithm.provider` property in the app's \n`portlet.properties` file to `IBMJCE`: \n\n```properties\ndigital.signature.algorithm.provider=IBMJCE\n```\n\n## Installing the LCS Client App\n\nOnce you've addressed the above pre-configuration steps, you're ready to install\nthe LCS client app. Follow these steps to install the app:\n\n1.  In your \n    [Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) \n    folder (usually the parent folder of the application server's folder), \n    delete this file: \n\n        osgi/marketplace/Liferay Connected Services Client.lpkg\n\n2.  Place the new `Liferay Connected Services Client.lpkg` in \n    `osgi/marketplace`. \n\nGreat! Now you're all set to \n[register your server with LCS](/docs/7-2/deploy/-/knowledge_base/d/activating-your-liferay-dxp-server-with-lcs). \n\nThe next section shows you how to upgrade the LCS client app. We highly \nrecommend that you do this whenever Liferay releases a new version of the app. \n\n## Upgrading the LCS Client App\n\nYour server should always be running the latest version of the LCS client app. \nThere are two ways to upgrade the app, depending on the exact LCS\npre-configuration steps you followed: \n\n1.  Via Liferay Marketplace *inside* @product@. Use this method if you don't\n    need to configure the LCS client app (e.g., to connect through a proxy)\n    before it deploys. \n\n    | **Note:** If you choose this method and have a clustered environment, you \n    | must perform the upgrade separately on each node in your cluster. \n    | Therefore, you may prefer to upgrade manually as detailed in the next step \n    | to ensure that all your nodes are running the exact same version of the \n    | LCS client app. \n\n    To perform the upgrade, first navigate to *Control Panel* &rarr; *Apps* \n    &rarr; *Purchased*. Apps needing an update are listed first. Click *Update* \n    next to the LCS client app. Note that you may need to restart your server \n    for the upgrade to complete. \n\n2.  Manually, after downloading the LCS client app's LPKG file to your machine. \n    Use this method if you must pre-configure the LCS client app to connect\n    through a proxy. \n\n    | **Note:** If you used JVM app server arguments to configure your server to \n    | connect through a proxy, then you don't need to pre-configure the LCS \n    | client app to connect through the same proxy. \n\n    To update the LCS client app manually, follow the previous sections in this\n    guide for downloading and pre-configuring the app. Then deploy it to\n    `[Liferay Home]/deploy` as you would any other app. \n\nContact Liferay Support if you need additional assistance with the upgrade \nprocess. \n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/04-registration.markdown",
    "content": "---\nheader-id: activating-your-liferay-dxp-server-with-lcs\n---\n\n# Registering Your Liferay DXP Server with LCS\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nFollow these steps to register your @product@ server with LCS: \n\n1.  Ensure that you've completed the \n    [LCS preconfiguration steps](/docs/7-2/deploy/-/knowledge_base/d/lcs-preconfiguration). \n\n2.  Log in to \n    [lcs.liferay.com](https://lcs.liferay.com). \n    This takes you to your company's LCS project. If your company has multiple\n    projects, from the menu to the right of the Dashboard tab select the project\n    that's getting a new server. \n\n    ![Figure 1: Select your LCS project from the menu highlighted by the red box in this screenshot.](../../images-dxp/lcs-select-project.png)\n\n3.  Select or create the environment in which to register this server. If you're \n    using LCS for activation, upon connection to LCS your server consumes an \n    activation key from the subscription type assigned to the environment. Note \n    that a subscription type can only be assigned to an environment when \n    creating the environment. If you have sufficient permissions in your \n    company's project, you can \n    [create a new environment](/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-environments#creating-environments) \n    by selecting *Add Environment*. \n\n    ![Figure 2: You must register your server in an LCS environment. The red box in this screenshot highlights environments.](../../images-dxp/lcs-registration-select-environment.png)\n\n4.  Select the environment's *Registration* tab. This is where you manage and \n    download the \n    [environment's token file](/docs/7-2/deploy/-/knowledge_base/d/understanding-environment-tokens), \n    that registers servers in the environment. \n\n    In the Registration tab's *Services* section, change the Liferay Instance Activation setting, \n    if needed. Note that if you change this option \n    and there are servers already registered in the environment, you must \n    regenerate the token file and use it to reconnect those servers to LCS.\n    You'll regenerate and/or download the token in the next step. \n\n     Additionally, If you disable this service, you must activate via an XML file from Liferay support, and \n    such instances must run version 5.0.0 or newer of the LCS client app. \n\n    Liferay Instance Activation is either enabled or disabled for all servers that connect to this \n    environment. If Portal Property Analysis is selected, you can prevent LCS environment.\n\n    ![Figure 3: An environment's Registration tab lets you manage the token file used to register your server in the environment.](../../images-dxp/lcs-registration.png)\n\n5.  What you do now depends on what you did in the previous step: \n\n    **Changes to Liferay Instance Activation:** Regenerate and download the token. \n    Regenerating a token causes all servers using the old token to disconnect \n    from LCS. You must reconnect them using the new token. \n\n    **No changes to LCS service selections:** Download the token. \n\n6.  Place the token file in your server's `[Liferay Home]/data` folder. Note \n    that \n    [Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) \n    is usually the parent folder of the application server's folder. If your \n    server is running, it should connect to LCS in about one minute. If your \n    server isn't running, it connects to LCS on startup. \n\n7.  Celebrate! Your @product@ server is registered in LCS. If for some reason it \n    isn't, see the \n    [LCS troubleshooting article](/docs/7-2/deploy/-/knowledge_base/d/troubleshooting-your-lcs-connection). \n\n| **Note:** You may be wondering what happens if LCS goes offline. Don't worry, \n| this doesn't cause a rift in the space-time continuum. LCS is deployed on\n| a global cloud infrastructure set up for automatic failure recovery. The\n| potential for non-availability is very low. In the event of an outage, \n| however, registered servers maintain a local copy of their uptime information \n| to transmit to LCS when it comes back online. If you use LCS for activation, \n| active subscriptions have a 30-day grace period to re-establish connectivity \n| and remain valid. This is ample time for LCS to come back online. \n\n## Determining Your Server's LCS Connection Status\n\nIn @product@, you can view your LCS connection status in the LCS client app.\nAccess the client by clicking *Control Panel* &rarr; *Configuration* &rarr;\n*Liferay Connected Services*. \n\nHere's a full description of what a connected LCS client app displays: \n\n**Connection Uptime:** The duration of the client's connection with LCS.\n\n**Last Message Received:** The time the LCS client received the latest \nconnection message from LCS. These messages occur only upon \nconnection/reconnection and are unrelated to server metrics. It's therefore \ncommon for a long period of time to pass before the client receives another \nsuch message for a reconnection event. \n\n**Services:** The LCS services enabled for this server. Note that all \nservers in an environment use the same set of LCS services. LCS services \ncan't be controlled on a server-by-server basis. \n\nNote: Portal Analytics, Fix Pack Management and Portal Properties Analysis have been removed from the list of available services. \nFor more information about this change, please read \n[this article](https://help.liferay.com/hc/en-us/articles/360037317691-Liferay-Connected-Services-Feature-Deprecation-Update-March-2020)\n\n**Project Home:** A link to this server's LCS project. \n\n**Environment:** A link to this server's LCS environment. \n\n**Server Dashboard:** A link to the server on LCS. \n\n![Figure 4: The server is connected to LCS.](../../images-dxp/lcs-server-connected.png)\n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/05-using-lcs/01-intro.markdown",
    "content": "---\nheader-id: using-lcs\n---\n\n# Using LCS\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n \nOnce your @product@ server is connected to LCS, you can get down to the business \nthat LCS is designed for---managing your servers. If you're not \nalready there, log in with your account on \n[lcs.liferay.com](https://lcs.liferay.com). \nThis is where you'll manage environments, register servers and more. \n\nThis articles in this section each detail one or more of LCS's features: \n\n-   [**What LCS Stores About Your Liferay DXP Servers:**](/docs/7-2/deploy/-/knowledge_base/d/what-lcs-stores-about-your-liferay-dxp-servers) \n    For LCS to work, the LCS servers must store certain information about your \n    servers. Sensitive data, however, isn't stored on the LCS servers. This \n    article describes the data that LCS does and doesn't store. \n\n-   [**Managing LCS Users in Your Project:**](/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-users-in-your-project) \n    Learn how to manage your LCS project's users by assigning them roles. \n\n-   [**Using the Dashboard:**](/docs/7-2/deploy/-/knowledge_base/d/using-the-dashboard) \n    Learn how to manage your LCS projects and access your environments and \n    servers in LCS. \n\n-   [**Managing LCS Environments:**](/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-environments) \n    Learn how to create and manage your LCS project's environments. This \n    includes instructions on generating tokens for an environment's servers. \n\n-   [**Managing LCS Servers:**](/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-servers) \n    Learn how to manage your servers in LCS. This includes viewing server \n    status and editing server settings.\n\n-   [**Managing Your LCS Account:**](/docs/7-2/deploy/-/knowledge_base/d/managing-your-lcs-account) \n    Learn how to manage your LCS account. This includes setting general account \n    preferences, managing LCS web notifications, and configuring LCS to send you \n    notification emails when specific events occur in your LCS projects. \n\n-   [**Managing Liferay DXP Subscriptions:**](/docs/7-2/deploy/-/knowledge_base/d/managing-liferay-dxp-subscriptions) \n    Learn how to view and manage your @product@ subscriptions for the servers in \n    your LCS project. \n\n-   [**Understanding Environment Tokens:**](/docs/7-2/deploy/-/knowledge_base/d/understanding-environment-tokens)\n    Learn about the environment tokens that you use to connect your servers to \n    LCS. \n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/05-using-lcs/02-lcs-property-storage.markdown",
    "content": "---\nheader-id: what-lcs-stores-about-your-liferay-dxp-servers\n---\n\n# What LCS Stores About Your Liferay DXP Servers\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nTo protect your users' privacy, LCS only stores system-specific data. LCS\ndoesn't gather or store data on your users. \n\nBy default, LCS stores the following information about your server:\n\n-   Portal build number and edition\n-   Patching Tool Version\n-   LCS Client Build Number \n-   Application Server Name\n-   Database Name\n-   File Encoding\n-   OS Name and Version\n-   Timezone\n-   IP Address\n-   Java Version and Java Options\n-   Number of Processor Cores\n-   File System Usage\n-   Memory Usage \n\nThe other data LCS stores depends on the services you enabled in your environment \ntoken, and whether your server was connected before certain services were removed. For more information on this, see \n[Registering Servers with LCS](/docs/7-2/deploy/-/knowledge_base/d/activating-your-liferay-dxp-server-with-lcs).\nIf you enabled the following services, LCS gathered and stored the data listed\nfor each: \n\n-   **Portal analytics:** \n\n    -   Portal and portlet metrics\n    -   JVM metrics\n    -   Cache and server metrics \n\n-   **Fix pack management:**\n\n    -   Patches installed on the server \n\n-   **Portal properties analysis:** \n\n    -   `portal.properties` (except sensitive data) \n\nSensitive data is any key-value pair that contains user names or passwords. For\nexample, LCS did not store the following properties because they contain\nsensitive data: \n\n    omniadmin.users\n    ldap.security.credentials.0, ldap.security.credentials.1, ldap.security.credentials.2 ...\n    facebook.connect.app.secret\n    auth.token.shared.secret\n    auth.mac.shared.key\n    captcha.engine.recaptcha.key.private\n    amazon.secret.access.key\n    tunneling.servlet.shared.secret\n    microsoft.translator.client.secret\n    dl.store.s3.secret.key\n    auto.deploy.glassfish.jee.dm.passwd\n\nLCS also did not store properties that end in `.password`, besides the following \nnon-sensitive properties: \n\n    portal.jaas.plain.password\n    portal.jaas.strict.password\n    login.create.account.allow.custom.password\n\nLCS also allowed you to prevent it from analyzing specific properties of your \nchoosing, by defining blacklisted properties.\n\nLCS is no longer gathering or storing the data listed above, that was associated with enabled services.\n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/05-using-lcs/03-managing-lcs-users.markdown",
    "content": "---\nheader-id: managing-lcs-users-in-your-project\n---\n\n# Managing LCS Users in Your Project\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nThe Users section of LCS is where you manage the LCS users that are part of your \nproject. It's here that you can grant or revoke LCS Roles. To manage users, \nfirst click the *Users* tab just below the Dashboard tab on the upper-left of \nyour screen. \n\n| **Note:** You can't add users to your project via the LCS UI or the LCS client \n| app. To add users to your project, you must contact Liferay support. \n\n![Figure 1: The Users tab lets you manage the LCS users in your project.](../../../images-dxp/lcs-users.png)\n\nThe *Users* tab displays a list of the users in your project. This list includes \neach user's name, email, image, LCS Roles, and a *Manage Roles* button. Each LCS \nuser must have an assigned Role. The following Roles are available: \n\n**LCS Administrator:** All LCS functionality is available to administrators.\nThis is the only Role that can manage other users' Roles. \n\n**LCS Environment Manager:** All LCS functionality is available in the scope \nof an environment, with the exception of managing other users. \n\n**LCS Environment Viewer:** Has read-only access in the scope of an \nenvironment.\n\nYou should note that each of these LCS Roles assume users already have the LCS\nUser Role in their Liferay.com accounts. The LCS User Role is granted\nautomatically the first time a user logs into LCS. The actions that can be\nperformed by each of the LCS Roles are detailed in the below permissions matrix. \n\n**LCS Permissions Matrix**\n\nAction | &nbsp;LCS Administrator | &nbsp;LCS Environment Manager | &nbsp;LCS Environment Viewer |\n------ | ----------------------- | ----------------------------- | ---------------------------- |\nAccess LCS | true | true | true |\nAccess Any Environment | true | false | false |\nAccess a Particular Environment | true | true | true |\nManage Users | true | false | false |\nCreate and Delete Environments | true | false | false |\nEdit Any Environment | true | false | false |\nEdit a Particular Environment | true | true | false |\nServer Registration in Any Environment | true | false | false |\nServer Registration in a Particular Environment | true | true | false |\nInstall Fix Packs in Any Environment | true | false | false |\nInstall Fix Packs in a Particular Environment | true | true | false |\n\nNow that you know what Roles are available in an LCS project and what they do, \nyou're ready to learn how to manage them.\n\n## Managing LCS Roles\n\nFollow these steps to manage a user's LCS Roles:\n\n1.  Click the user's *Manage Roles* button. \n\n2.  To revoke a Role, click *Revoke Role* for that Role. \n\n3.  To assign a Role, choose the Role (and environment, if applicable) and click \n    *Assign*. \n\n| **Note:** A user can't have an environment Role (e.g., LCS Environment \n| Manager, LCS Environment Viewer) and the LCS Administrator Role at the same \n| time. \n\n![Figure 2: You can assign or revoke a user's LCS Roles.](../../../images-dxp/lcs-user-roles.png)\n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/05-using-lcs/04-lcs-dashboard.markdown",
    "content": "---\nheader-id: using-the-dashboard\n---\n\n# Using the Dashboard\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nThe LCS Dashboard shows a project's environments and servers. If you're not\nalready at the Dashboard, click it near the upper left-hand corner of your LCS\nsite. Clicking *Dashboard* takes you to the project view. From there, you can\nget to the environment view and the server view. Each of these views gives you\na different look into certain aspects of your LCS project. You'll start with the\nproject view. \n\n## Using the Project View\n\nYou can get to the project view at any time by clicking the *Dashboard* tab near\nthe upper left-hand corner of your LCS site. The project appears to the right of\nthis tab, with a drop-down arrow for switching between projects if you have more\nthan one. You can also switch between projects from the user menu at the top\nright of the Dockbar. The project view contains a Status table that lists status\nmessages for each server in your project. For example, a status message appears\nfor a server when the server is offline. Status messages also appear for servers\nwhen fix packs are available, monitoring is unavailable, the patching tool is\nunavailable, or other events occur that relate to LCS. \n\n![Figure 1: The LCS project view shows an overview of your LCS project.](../../../images-dxp/lcs-project-view.png)\n\nLCS lists the environments in your project on the left side of the screen. You \ncan also create new environments here by clicking the *Add Environment* tab \n(more on this shortly). To view an environment's settings, click the\nenvironment's gear icon. Clicking an environment shows more information about\nit. This takes you to the environment view. Also note that each environment's\nicon indicates the environment's type and status: \n\n**Red icon:** There's a problem with one or more of the environment's servers. \n\n**Green icon:** The environment's servers are operating properly. \n\n**Icon with a circle:** The environment's servers are clustered. \n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/05-using-lcs/05-lcs-environments.markdown",
    "content": "---\nheader-id: managing-lcs-environments\n---\n\n# Managing LCS Environments\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nEnvironments are the key components of your LCS project. When you register a \nserver in LCS, you do so in an environment. An environment is therefore the \ngateway to managing and monitoring your servers in LCS. \n\n## Creating Environments\n\nThe first time you log in to LCS, a wizard walks you through each step required \nto create your project's first environment. The \n[getting started article](/docs/7-2/deploy/-/knowledge_base/d/getting-started-with-lcs) \nexplains this in detail. You can create additional environments via the same \nwizard or a simple form. \n\nTo create an environment, click the *Add Environment* button from the Dashboard. \nThis opens the New Environment form. Each section in this form corresponds to a \nstep in the wizard. If you want to use the wizard instead, click the \n*Open Wizard* link at the top of the form. See the \n[getting started article](/docs/7-2/deploy/-/knowledge_base/d/getting-started-with-lcs) \nfor a description of each setting in the form and wizard. \n\n| **Note:** When creating an environment, make your selections carefully for the \n| *Subscription Type*, *Cluster*, and *Elastic* fields. You can't change them \n| after creating the environment. \n\n![Figure 1: The New Environment form lets you create environments.](../../../images-dxp/lcs-new-environment.png)\n\n## Working with Environments\n\nClicking an environment on the left-hand side of the Dashboard takes you to the\nenvironment view, which lets you manage an environment in your LCS project. \n\nThe UI is segmented into three tabs: \n\n1.  **Fix Packs:** View and apply fix packs for the environment's servers. This \n    tab only appears if a server is registered in the environment. A table \n    displays the following information for each fix pack: \n\n    -   **Name:** The fix pack's name.\n    -   **Status:** The fix pack's status.\n    -   **Server:** The server the fix pack can be applied to.\n    -   **Size:** The fix pack's size. This only appears if the server is \n        running. \n    -   **Download:** A button to download the fix pack to the server. This only \n        appears if the server is running. \n\n    Once a fix pack downloads, LCS prompts you to restart your server, which \n    installs any downloaded fix packs. Note that you must start your server with \n    the privileges required to write to the disk location where patches are \n    stored and processed (the `patching-tool` folder). To use LCS to install fix \n    packs across a cluster, follow the same procedure. LCS downloads and\n    installs fix packs simultaneously across all nodes---you don't have to\n    handle each separately. \n\n2.  **Registration:** Generate and download \n    [*environment tokens*](/docs/7-2/deploy/-/knowledge_base/d/understanding-environment-tokens) \n    that connect your servers to LCS. \n\n3.  **Environment Settings:** Change the environment's name, location, and\n    description. You can also see if the environment allows clustered servers\n    and view the environment's subscription type. Click the *Save* button to\n    save any changes you make in the Environment Settings tab. You can also\n    delete the environment by clicking *Delete Environment*. \n\n![Figure 2: The LCS environment view shows an overview of an LCS environment.](../../../images-dxp/lcs-environment-view.png)\n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/05-using-lcs/06-lcs-servers.markdown",
    "content": "---\nheader-id: managing-lcs-servers\n---\n\n# Managing LCS Servers\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nClicking a server in the Dashboard or environment view takes you to the server \nview. Server view provides detailed information about a server. To protect your users' privacy, LCS doesn't \ngather, store, or analyze user data. \n\nServer view is segmented into six tabs: \n\n**Page Analytics:** This service has been disabled, if you enabled it earlier you can see here the past history for metrics on page views and load times.\n\n**Snapshot Metrics:** This service has been disabled, if you enabled it earlier you can see here the past history for application, JVM, and server metrics.\n\n**Fix Packs:** This service has been disabled, if you enabled it earlier you can see here the past history for the server's available and installed fix packs.\n\n**Portal Properties:** This service has been disabled, if you enabled it earlier you can see here the past history for your portal's properties and their settings.\n\n**Details:** Displays general information about your @product@ installation, \nJava version, and hardware.\n \n**Server Settings:** View or change your server's name, location, and \ndescription. You can also unregister the server from LCS. \n\n| **Note:** LCS only supported Snapshot Metrics for servers running on Tomcat or \n| WebLogic. On other application servers you may see a console message \n| indicating that LCS doesn't support server metrics for your application \n| server. You may also see a benign `NullPointerException` for the LCS \n| `TaskSchedulerServiceImpl` and `ScheduleTasksCommand`. \n\n## Details\n\nThe *Details* tab shows general information about your server. There are three \ntabs under Details: *Software*, *Java*, and *Hardware*. Each shows information, \nrespectively, about your @product@ installation, Java installation, and \nhardware. This information is useful to the Liferay Support team in the event \nyou need their assistance. \n\n![Figure 1: The Details tab shows information about your server.](../../../images-dxp/lcs-server-details.png)\n\n## Server Settings\n\nFinally, the *Server Settings* tab lets you view and edit your server's name,\nlocation, and description. You can also unregister your server from LCS. \n\n![Figure 2: You can use the Server Settings tab to give your server a fun name.](../../../images-dxp/lcs-server-settings.png)\n\n## Page Analytics\n\nPage Analytics appears by default when you enter server view. Page Analytics\nshows page views and load times for the selected site and time period. By\ndefault, all sites are selected. You can select a specific site from the *Site*\nselector menu. You can also select a different time period in the *Period* and\n*Ending At* fields. The arrows next to the *Ending At* field move the selected\ntime period up or down, respectively, by one period. For example, if you select\n*One Hour* in the *Period* field, pressing the right arrow next to *Ending At*\nmoves the selected time period up by one hour. Note that at the beginning of the\ncurrent time period, it can take up to 15 minutes for data to become available.\nAlso note that data is available for three months from the time LCS collected\nit. \n\nBy default, load times and page views for all pages are plotted against time in \nseparate graphs. Below these graphs, a table displays summary statistics of data\nover the same time period, for each page. If you click a page in the table, the\ngraphs plot the data for just that page. If you can't find the page you're\nlooking for, you can search for it in the *Search* box at the top of the table.\nTo plot data for all pages again, click the *All Pages* row at the bottom of the\ntable. \n\nLoad times are also color coded to indicate speed. The *Load Times* graph's \nbackground is red for values above 3,000 ms, orange for values from 2,000 to \n3,000 ms, and green for values less than 2,000 ms. Likewise, the table displays \nall load times greater than 3,000 ms in red text. \n\n![Figure 3: The Page Analytics interface in the LCS Server view.](../../../images-dxp/lcs-page-analytics-01.png)\n\n## Snapshot Metrics\n\nTo view other metrics and statistics of your server's performance, click the \n*Snapshot Metrics* tab near the top of the page. These metrics are broken down \ninto three main categories: *Application*, *JVM*, and *Server*. Application is \nselected by default when you click the Snapshot Metrics button. \n\nThe Application category also has three other categories: *Pages*, *Portlets*, \nand *Cache*. Pages lists the frequency that specific pages load, along with\ntheir average load times. Portlets lists the same statistics, but for specific\nportlets in your server. The Cache category lists Liferay Single VM metrics and\nHibernate metrics. The following screenshot shows the statistics in the Portlets\ncategory.\n\n![Figure 4: The LCS application metrics show portlet performance statistics, like frequency of use and average load time.](../../../images-dxp/lcs-server-metrics-application-portlets.png)\n\nThe JVM category, as its name indicates, shows statistics about the JVM running \non your server. This includes data on the garbage collector and memory. The \nnumber of runs, total time, and average time are listed for each garbage \ncollector item. The memory metrics are presented in a bar chart that shows the \nusage of the PS Eden Space, Code Cache, Compressed Class Space, PS Old Gen, PS \nSurvivor Space, and Metaspace. \n\n![Figure 5: The LCS JVM metrics show performance data for memory and the garbage collector.](../../../images-dxp/lcs-server-metrics-jvm.png)\n\nServer is the third category in Snapshot Metrics. The Server category shows \nadditional information about how your server is running. For example, horizontal \nbar graphs show the number of current threads running on your server, as well as \nthe JDBC connection pools. \n\n![Figure 6: The LCS server metrics show current threads and JDBC connection pools.](../../../images-dxp/lcs-metrics-server.png)\n\nNote that in Snapshot Metrics, the application and garbage collector metrics are \nbased on data collected by LCS from server registration to the present. Memory \nand server metrics, however, show only the current state. \n\n## Fix Packs\n\nTo view your server's fix packs, click the Fix Packs tab near the top of the \npage. The available and installed fix packs appear in separate tables. The \navailable fix packs table functions exactly like the Fix Packs table in \nenvironment view for downloading and installing fix packs. \n\n![Figure 7: The Fix Packs tab displays your server's available and installed fix packs.](../../../images-dxp/lcs-server-fix-packs.png)\n\n## Portal Properties\n\nThe *Portal Properties* tab lets you view your portal's property values in\na searchable table. This gives you a convenient display for your portal property\nsettings. The properties in this table are organized into the following\ncategories: \n\n**Default Values:** The default values for your portal's properties. \n\n**Custom Values:** Any custom values you've set for your portal's properties. \nThis includes any property values you change via a `portal-ext.properties` \nfile.\n\n**Dynamic Properties:** Any property values set at runtime. For example, the \n[Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) \nfolder's location depends on your configuration. To specify this folder when\nsetting any properties that require it, use `${liferay.home}` instead of an\nabsolute directory path. \n\nYou can display any combination of these categories by selecting the \ncorresponding checkboxes from the gear icon next to the search box at the \ntop-right of the table. For example, by checking the *Show Default Values* and \n*Show Custom Values* checkboxes, the table shows your portal's default and \ncustom property values. To show only the custom values, select only *Show Custom \nValues*. \n\n![Figure 8: Click the gear icon to select the type of portal properties to show in the table.](../../../images-dxp/lcs-server-portal-properties.png)\n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/05-using-lcs/07-lcs-account.markdown",
    "content": "---\nheader-id: managing-your-lcs-account\n---\n\n# Managing Your LCS Account\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nTo manage your LCS account, select *My Account* from the user menu in the\nDockbar. This takes you to a UI with four tabs: \n\n**Projects:** Displays your LCS projects in a searchable table that lists the \nadministrator's email address for each project. \n\n**Email Notifications:** Configure LCS to send you emails when specific \nevents occur in your LCS projects by adding rules that define what events\ntrigger a notification. There are no rules by default. Click the *Add Rule*\nbutton to define one. \n\nFirst specify the project, environment, and server for the notification. \nNote that you have the option of selecting all environments and servers in a\nproject. Then check the checkbox for each event that you want to trigger an \nemail notification. For example, if you create a notification rule with *The \nserver unexpectedly shuts down* selected for all servers and environments in\nyour project, then LCS sends you an email if any server in your project goes\noffline without a normal shut down event. Click *Save* when you're done defining\nthe notification rule. It then appears in a table along with other existing\nrules. Each has Edit and Delete Action buttons. \n\n![Figure 1: You can add rules to determine the events that trigger notifications.](../../../images-dxp/lcs-add-notification-rule.png)\n\n**Notification History:** Displays your web notification history in a \nsearchable table. You can also select the date range from which to display \nnotifications. \n\n**Preferences:** Manage your LCS account's preferences. You can change your\naccount's language, time zone, and default LCS project. Your default LCS project\nis the one shown each time you log in to LCS. \n\n![Figure 2: You can change your LCS account's general preferences.](../../../images-dxp/lcs-account-preferences.png)\n\n## Using Web Notifications\n\nLCS also displays web notifications under the bell icon in the Dockbar. A red\nbadge on this icon shows your unread notification count. LCS and Liferay Support\nsend these notifications. For example, LCS generates notifications when a server\nshuts down or some other event requiring your attention occurs. To mark\na notification as read, click the small *x* icon next to it. To mark all\nnotifications as read, click the *Mark All as Read* button. To mark\nnotifications as unread again, click the *Undo* button that appears. To see your\nnotification history, click the *Notifications History* button. You can also\naccess your notification history by selecting *My Account* from the user menu in\nthe Dockbar. \n\n![Figure 3: Web notifications let you know what's happening in your LCS projects.](../../../images-dxp/lcs-user-web-notifications.png)\n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/05-using-lcs/08-lcs-subscriptions.markdown",
    "content": "---\nheader-id: managing-liferay-dxp-subscriptions\n---\n\n# Managing Liferay DXP Subscriptions\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nLCS lets you use and view your @product@ subscriptions. Recall that when you \n[create an environment](/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-environments#creating-environments), \nyou assign its subscription type and choose whether LCS activates servers that \nconnect to that environment. If you use LCS for activation, registering a server \nin that environment consumes an activation key from the environment's \nsubscription type. You can also view your project's available activation keys \nand see how they're being used. \n\nDepending on your subscription agreement, LCS also lets you register servers via \n*elastic subscriptions*. Elastic subscriptions let you register an unlimited \nnumber servers. This is invaluable in auto-scaling environments, where servers \nare automatically created and destroyed in response to load. Note that to use \nelastic subscriptions, you must set the environment as elastic when you create \nit. Also note that LCS only uses elastic subscriptions for servers that exceed \nthe number that the environment's subscription type allows. In other words, LCS \nuses the environment's regular subscriptions before any elastic subscriptions. \n\nYou can access these features from the *Subscriptions* tab on the upper-left of \nthe LCS site. This tab contains two other tabs: *Details* and *Elastic \nSubscriptions*. \n\n![Figure 1: LCS lets you view and manage your subscriptions.](../../../images-dxp/lcs-subscriptions.png)\n\nThere are four tables in the *Details* tab: \n\n1.  **Subscriptions:** Shows a list of the available subscriptions in your LCS \n    project. For each subscription, this table shows the following information: \n\n    - Subscription Type\n    - Start Date\n    - Expiration Date\n    - Support End Date\n    - Platform\n    - Product\n    - Processor Cores Allowed\n    - Activation Keys\n    - Used Activation Keys\n\n    Note that *Processor Cores Allowed* shows the number of processor cores that \n    the subscription allows for each server. \n\n2.  **Subscriptions Summary:** Shows how your subscriptions are currently used \n    in your project. For each subscription type, this table shows the number of \n    activation keys allowed, used, and available. \n\n3.  **Project Environments:** Shows your project's environments and their \n    assigned subscription types. Each environment must have a subscription type. \n\n4.  **Project Servers:** Shows the environment and subscription type for each \n    server in your LCS project. \n\nIf any of the information in these tables is missing or incorrect, contact \nLiferay Support. \n\n| **Note:** If you don't use LCS for activating your servers, then you can \n| register as many servers as you want in LCS. \n\n| **Note:** If you try to activate a server that exceeds the number of processor \n| cores that your subscription allows per server, the activation fails and the \n| server is locked down. A console error also indicates the server's core count. \n| You can compare this with your subscription's processor cores allowed in LCS's \n| Subscriptions table. To activate the server, you can either reduce the number \n| of cores it uses (e.g., by deploying to different server hardware, or reducing \n| the number of virtual processors in a VM or container), or contact Liferay \n| Sales to increase the number of processor cores that your subscription allows \n| per server. \n\n## Decommissioning Servers\n\nTo decommission a server and free its activation key for reuse, select the \nserver's environment on the left and then select the server. In the server's \n*Server Settings* tab, select *Unregister*. Also note that when you shut down a \nserver normally, its activation key is immediately freed for reuse. If the \nserver crashes or its shutdown is forced (e.g., kill), its activation key is \nfreed for reuse within six minutes. \n\n## Elastic Subscriptions\n\nElastic subscriptions let you register an unlimited number of servers. This is \ncrucial for auto-scaling environments where servers are created and destroyed \nautomatically. You can view data on your elastic servers from the \n*Subscriptions* tab's *Elastic Subscriptions* tab. \n\n| **Note:** To register elastic servers in an environment, that environment must \n| be set as elastic when it's created. For more information, see the \n| [documentation on creating environments](/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-environments#creating-environments). \n\n![Figure 2: The *Elastic Subscriptions* tab shows details about your project's elastic servers.](../../../images-dxp/lcs-elastic-subscriptions.png)\n\nThe *Elastic Subscriptions* tab displays the number of elastic servers online \nand the uptime details for each. A graph shows the number of elastic servers \nonline each day, while a table lists each elastic server's start time, end time, \nand duration. The total duration for servers is below the table. To download a \nreport of the table's data, click *Download Report*. Also, you can use the \n*Environment* and *Month* selectors above the graph to select the environment \nand month to show data from, respectively. The data in both the graph and the \ntable reflect your selections here. \n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/05-using-lcs/09-lcs-tokens.markdown",
    "content": "---\nheader-id: understanding-environment-tokens\n---\n\n# Understanding Environment Tokens\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nTo register a server in an environment, you must use that environment's token \nfile. LCS Administrators and Environment Managers can generate and distribute \nthis file. It contains all the information the LCS client app needs to register \nthe server in the environment. When the server starts up, it uses the token to \nconnect to LCS. If you use LCS for activation, the server automatically consumes \nan activation key from the environment's subscription upon connection. This \nmakes it possible to activate servers automatically on startup with no \ninteraction required. \n\n| **Note:** For instructions on using and managing your environment tokens, see \n| the instructions on \n| [registering your server with LCS](/docs/7-2/deploy/-/knowledge_base/d/activating-your-liferay-dxp-server-with-lcs). \n\nThere are a few things to keep in mind when using environment tokens: \n\n-   Each environment can have only one token file. If you regenerate the token, \n    servers using the old file are disconnected from LCS and can't reconnect \n    until receiving the new file. If the server disconnects due to token \n    regeneration and is running version 4.0.2 or later of the LCS client app, \n    the server enters a 30-day grace period during which it functions normally. \n    This gives the administrator time to use the new token file to reconnect to \n    LCS. Servers running earlier versions of the LCS client app present users \n    with an error page until the administrator reconnects with the new token. \n\n-   Use caution when distributing the token file, as anyone can use it to \n    connect to your environment (and consume an activation key in your \n    subscription if you're using LCS for activation). \n\n-   Minimal information (server name, location, etc...) is used to register a \n    server with LCS. You can change this information from \n    [the server view in LCS](/docs/7-2/deploy/-/knowledge_base/d/managing-lcs-servers) \n    at any time. \n\n-   Environment tokens connect using OAuth. Using an environment token overrides \n    the OAuth authorization cycle. If LCS Administrators or Environment Managers \n    have never registered servers in LCS, the first time they do so an OAuth \n    authorization entry is created in LCS. If they've previously registered \n    servers in LCS, their existing credentials are used when they create a token \n    file. \n\n-   If the credentials of the LCS user who generated the token become invalid, \n    you must generate a new token and use it to reconnect to LCS. An LCS user's \n    credentials become invalid if the user leaves the LCS project or becomes an \n    LCS Environment Manager or LCS Environment Viewer in a different \n    environment. \n\nSo why bother with environment tokens at all? Besides simplifying the LCS \nconnection process, environment tokens are valuable in auto-scaling environments \nwhere algorithms create and destroy servers automatically. In this situation, \nhaving clients that activate and configure themselves is crucial. \n\n| **Note**: If your auto-scaling environment creates new server nodes from a \n| server in a system image, that server can't require human interaction during \n| setup. When creating such an image, you must change any portal property \n| settings that prevent automatic setup. By default, @product@'s setup wizard \n| requires human interaction. You must therefore set the `setup.wizard.enabled` \n| property to `false` if you want your auto-scaling environment to create new \n| nodes from the server. \n"
  },
  {
    "path": "en/deployment/articles-dxp/08-lcs/06-lcs-troubleshooting.markdown",
    "content": "---\nheader-id: troubleshooting-your-lcs-connection\n---\n\n# Troubleshooting Your LCS Connection\n\n[TOC levels=1-4]\n\n| **Note:** LCS is deprecated and will be shut down on December 31, 2021. \n| Customers who activate LCS are advised to replace it with our latest activation\n| key type which is suitable for virtualized environments. \n|\n| For further information, please see [Changes to Liferay Product Activation](https://help.liferay.com/hc/en-us/articles/4402347960845-Changes-to-Liferay-Product-Activation).\n\nIf you use LCS to activate @product@, your server must maintain its connection \nto LCS at all times. If this connection is interrupted, your server enters a \ngrace period to allow for reconnection. Lengthy interruptions, however, can \naffect your server's uptime. \n\n| **Note:** You must use LCS for activation of Elastic subscriptions. Otherwise, \n| you don't have to use LCS for activation. You can instead request an XML \n| activation key from Liferay Support. \n\nThe following sections in this document provide some background information and \nhelp you troubleshoot problems with your server's LCS connection: \n\n[**LCS Grace Periods:**](#lcs-grace-periods) \nDescribes how grace periods work in LCS. You should read this section before \nattempting any troubleshooting steps. \n\n[**Troubleshooting:**](#troubleshooting) \nPresents troubleshooting steps for specific problems. \n\n[**Increasing Log Levels:**](#increasing-log-levels) \nIf you contact Liferay Support, you'll be asked to increase your server's log \nlevels and then provide your log files. This section shows you how to do this. \n\n| **Note:** The odds of LCS being unavailable are low. LCS is deployed on a \n| global cloud infrastructure set up for automatic failure recovery. \n| Notifications also let the LCS team react quickly to any downtime. During LCS \n| updates and new version releases, however, LCS is unavailable for a few \n| minutes while changes are applied. \n\n## LCS Grace Periods\n\nThere are 2 grace period types in LCS: \n\n1.  **Connection Grace Period:** Occurs when your activated LCS connection is\n    interrupted. This gives you time to re-establish the connection. \n\n2.  **Subscription Grace Period:** Occurs when your subscription is about to\n    expire. This gives you time to renew the subscription. \n\n| **Note:** These grace periods only apply to servers previously connected and \n| activated in LCS. If the subscription check or connection fails when a server \n| attempts to connect to LCS for the first time, that server doesn't enter a \n| grace period. It's therefore important to verify that an active subscription \n| is available before connecting a new server to LCS. To do this, check the \n| [Subscriptions tab](/docs/7-2/deploy/-/knowledge_base/d/managing-liferay-dxp-subscriptions) \n| in LCS. \n\n### Connection Grace Period\n\nIf your server's LCS connection is interrupted, the server continues to run and \nenters a grace period that lasts for up to 30 days to allow for reconnection. \nDuring this grace period, @product@ displays a warning message to\nadministrators. Upon seeing this message, administrators should contact Liferay\nSupport and follow the troubleshooting steps below. LCS automatically restores \nyour server's activation upon reconnection (you shouldn't need to restart the \nserver). If for some reason the connection can't be restored, Liferay Support \nwill provide an alternative way to activate your server. \n\n![Figure 1: A warning message is displayed to administrators if the server can't connect to LCS to validate the subscription.](../../images-dxp/lcs-grace-period.png)\n\nWhile disconnected from LCS, the LCS client app continually attempts to \nreconnect. If reconnection continues to fail, ensure that your server can access \n`lcs.liferay.com` and `lcs-gateway.liferay.com`. If the LCS client app stops \nattempting to reconnect, there will be no activity in the logs. In this case, \nyou can force reconnection by redeploying the app. Follow these steps to do so: \n\n1.  In your server's \n    [Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) \n    folder (usually the parent folder of the application server's folder), \n    remove this file: \n\n        osgi/marketplace/Liferay Connected Services Client.lpkg\n\n2.  Place `Liferay Connected Services Client.lpkg` in `[Liferay Home]/deploy`. \n    If you \n    [connect to LCS through a proxy](/docs/7-2/deploy/-/knowledge_base/d/lcs-preconfiguration#preconfiguring-lcs-to-connect-through-a-proxy), \n    and configured this inside the LCS client app, make sure the app you deploy \n    is also configured to do so. \n\nYou should also ensure that you've enabled email notifications in LCS for server \ndisconnection events. To do this, you must create a notification rule that sends \nan email whenever the server shuts down unexpectedly. The documentation on \n[managing your LCS account](/docs/7-2/deploy/-/knowledge_base/d/managing-your-lcs-account) \nexplains how to do this. \n\n### Subscription Grace Period\n\nAt least 90 days before the subscription expires, Liferay will reach out to \nbegin the renewal process. 30 days before expiration, Liferay Support sends \nwarning messages through \nthe Help Center, \n[the LCS site](https://lcs.liferay.com), \nand \n[the Customer Portal](https://www.liferay.com/group/customer). \nAfter the expiration date, your servers may be placed in an additional grace \nperiod, which is communicated through the same support channels. If the \nrenewal isn't completed during this grace period, then the subscription becomes \ninactive and the @product@ instance enters the 30-day grace period. As soon as \nthe renewal is processed, the instance activates and any error or warning \nmessages disappear within 24 hours. Note that by using XML activation keys \n(provided by Liferay Support upon request), you can continue to use your \n@product@ instances even after a subscription has expired. \n\n![Figure 2: LCS sends you a notification prior to the expiration of your subscription.](../../images-dxp/lcs-support-expiration.png)\n\n## Troubleshooting\n\nIf you encounter issues with LCS, the Liferay Support team is here to help. If \nyou need support, open a \n[Help Center](https://help.liferay.com/hc)\nticket. You can begin troubleshooting the following scenarios, which the Liferay\nSupport team can also assist you with. \n\n| **Note:** Before troubleshooting specific issues or contacting Liferay \n| Support, make sure that you've followed the LCS \n| [preconfiguration](/docs/7-2/deploy/-/knowledge_base/d/lcs-preconfiguration) \n| and \n| [registration](/docs/7-2/deploy/-/knowledge_base/d/activating-your-liferay-dxp-server-with-lcs) \n| steps correctly. \n\n### Server Can't Reach LCS\n\nIf your server can't reach LCS, verify that you can access the public sites\nrequired by LCS: \n\n-   [`lcs.liferay.com`](https://lcs.liferay.com/) \n    should be viewable in a browser. \n-   `lcs-gateway.liferay.com` should respond on port 443: \n\n        curl -vk -I \"https://lcs-gateway.liferay.com\"\n        telnet lcs-gateway.liferay.com 443\n\n### Subscription Issues\n\nFor issues related to your subscription, first review the documentation on \n[managing your subscription](/docs/7-2/deploy/-/knowledge_base/d/managing-liferay-dxp-subscriptions). \nSubscription errors usually involve one of these problems:\n\n-   Your server can reach LCS, but can't locate a subscription. \n-   Your server can reach LCS and locate a subscription, but activating your \n    server would exceed the subscription's number of activation keys or cores. \n\nIn either case, you must verify that a subscription is available and that you're \nnot exceeding its number of activation keys or cores. You can find this \ninformation on the LCS site's Subscriptions page, as described in \n[the documentation on managing subscriptions](/docs/7-2/deploy/-/knowledge_base/d/managing-liferay-dxp-subscriptions). \nIf the environment in which you're trying to activate a server isn't assigned \nthe subscription you want to use, then you must create a new environment and \nassign it the correct subscription. Once assigned, you can't change an \nenvironment's subscription. Follow \n[the initial registration steps](/docs/7-2/deploy/-/knowledge_base/d/activating-your-liferay-dxp-server-with-lcs) \nfor instructions on creating a new environment and activating a new server. \n\n| **Note:** When shutting down servers, you must ensure that the LCS site \n| receives the server shutdown commands. Otherwise, LCS may not release that \n| server's activation key for reuse and attempts to activate additional servers \n| may exceed the subscription's number of activation keys. There's a higher \n| likelihood of this happening in rolling deployments and/or when using \n| containers. For more information, see the \n| [KB article on properly unregistering subscriptions](https://help.liferay.com/hc/en-us/articles/360018261011). \n\n### Invalid Token\n\nIf the token is invalid, first review the documentation on \n[environment tokens](/docs/7-2/deploy/-/knowledge_base/d/understanding-environment-tokens). \nThe following table lists causes and solutions for invalid tokens. \n\n| &nbsp;Cause | &nbsp;Solution |\n| ----------- | -------------- |\n| The LCS user who generated the token no longer has permissions. This happens when the user leaves the LCS project or becomes an LCS Environment Manager or LCS Environment Viewer in a different environment. | Regenerate the token. |\n| The token's file name is changed after download. | Download the token again from LCS. |\n| The token is regenerated. | Use the regenerated token. |\n\n## Increasing Log Levels\n\nIf you contact Liferay Support, you're asked to increase your server's log \nlevels and then provide your log files. You can find these log files in \n`[Liferay Home]/logs` \n([Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) \nis usually the parent folder of the application server's folder). There are 2 \ntypes of log files in this folder: \n\n1.  **Liferay log files:** The files `liferay.[date].log` and \n    `liferay.[date].xml` are the logs for your @product@ installation. Note that \n    LOG and XML files for the same date contain the same information--the only \n    difference is the file format. \n\n2.  **LCS log files:** The `lcs-portlet-[date].log` files are the LCS client \n    app's logs. Note that if there's only a single LCS log file, it may appear \n    without a date as `lcs-portlet.log`. When you increase the log levels as \n    described in the following sections, the additional log messages are written \n    to these LCS log files. \n\nThere are 2 ways to increase the log levels: \n\n1.  **In your @product@ instance's Control Panel:** This is a temporary \n    configuration that resets upon shutting down the server. Note that if the \n    server isn't activated, you can't access the Control Panel. In that case,\n    Liferay Support can provide an XML activation key. \n\n2.  **In a Log4j configuration:** This is a permanent configuration that \n    persists through server shutdown and restart. \n\nThe following sections cover both options. \n\n### Control Panel\n\nFollow these steps to increase the log levels via the Control Panel: \n\n1.  Navigate to *Control Panel* &rarr; *Configuration* &rarr; *Server \n    Administration*. \n\n2.  Click the *Log Levels* tab. \n\n3.  Search for \"lcs\". \n\n4.  Change the log level for each matching entry to DEBUG. \n\n5.  While in the Control Panel, you should also navigate to *Configuration* \n    &rarr; *Liferay Connected Services* and take a screenshot of what you see \n    there. This is useful to Liferay Support. \n\n### Log4j\n\nFollow these steps to increase the log levels via Log4j: \n\n1.  Download the latest LCS client as instructed in the \n    [LCS preconfiguration article](/docs/7-2/deploy/-/knowledge_base/d/lcs-preconfiguration). \n    The app downloads as `Liferay Connected Services Client.lpkg`. If you don't \n    want to download the latest client, you can use the one already installed in \n    your server: it's in `[Liferay Home]/osgi/marketplace` (just make sure to \n    shut down your server before following the rest of the steps in this \n    section). Recall that the \n    [Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) \n    folder is usually the parent folder of the application server's folder. \n\n2.  Expand the LPKG file, then expand the `lcs-portlet-[version].war` file \n    inside it. \n\n3.  Inside the `WAR` file, replace the contents of \n    `WEB-INF\\classes\\META-INF\\portal-log4j.xml` with the following\n    configuration: \n\n        <?xml version=\"1.0\"?>\n        <!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">\n\n        <log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\">\n                <appender class=\"org.apache.log4j.rolling.RollingFileAppender\" name=\"RollingFileAppender\">\n                        <rollingPolicy class=\"org.apache.log4j.rolling.TimeBasedRollingPolicy\">\n                                <param name=\"ActiveFileName\" value=\"@liferay.home@/logs/lcs-portlet.log\" />\n                                <param name=\"FileNamePattern\" value=\"@liferay.home@/logs/lcs-portlet.%d{yyyy-MM-dd}.log.zip\" />\n                        </rollingPolicy>\n\n                        <layout class=\"org.apache.log4j.EnhancedPatternLayout\">\n                                <param name=\"ConversionPattern\" value=\"%d{yyyy/MM/dd HH\\:mm\\:ss} %-5p [%t][%c{1}:%L] %m%n\" />\n                        </layout>\n                </appender>\n\n                <category name=\"com.liferay.lcs.task.scheduler\">\n                        <priority value=\"ALL\" />\n                </category>\n\n                <logger additivity=\"false\" name=\"com.liferay.lcs\">\n                        <level value=\"ALL\" />\n                        <appender-ref ref=\"RollingFileAppender\" />\n                </logger>\n        </log4j:configuration>\n\n4.  Save the file and repackage the WAR and LPKG (make sure not to change the \n    names of these files). \n\n5.  Make sure your server is shut down. \n\n6.  In your installation's \n    [Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) \n    folder, delete the existing LCS client app: \n\n        osgi/marketplace/Liferay Connected Services Client.lpkg\n\n7.  Place the `Liferay Connected Services Client.lpkg` that you repackaged in \n    step 4 in `osgi/marketplace`. \n\n8.  Start your server. \n\nIf you need assistance with the issues in this guide, or any other issues with \nLCS, contact Liferay Support. \n"
  },
  {
    "path": "en/deployment/articles-dxp/100-reference/06-comparing-patch-levels.markdown",
    "content": "---\nheader-id: comparing-patch-levels\n---\n\n# Comparing Patch Levels\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-dxp-installation/patching-liferay/advanced-patching/custom-code-and-patch-compatibility.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nIf you're a developer, the Patching Tool can show you what changed between\ndifferent @product@ patches and versions. These commands show you information\nabout the different patch levels:\n\n`patching-tool diff`: Prints the differences between two patch levels. At least\none stored patch level must be available. This command accepts options for\nfiltering the output: \n\n- `source`: Shows the source differences between the two patch levels.\n- `files`: Shows a list of the modified files.\n- `fixed-issues`: Shows a list of LPS/LPE issues from our issue tracking system.\n- `html`: Specify this along with one of the filtering options (`source`, \n    `files`, or `fixed-issues`) and after the patch levels, to write the\n    differences to an HTML file\n    (`<stored-name-1>-<stored-name-2>-[type]-diff.html`) in the `diffs` folder.\n    Additions are colored green and deletions are colored red.\n- `collisions`: Shows a list of modified files which collide with deployed plugins.\n\nFor detailed usage information, run `patching-tool help diff`.\n\n`patching-tool store`: Manages patching level information for the `diff`\ncommand. Your patches must contain source code to store the patch level and to\nprepare usable information for the `diff` command. Here are the `store` command\noptions: \n\n- `info`: Prints the list of patches which make up the stored patch level.\n- `add`: Stores the patch level that can be found in the patches directory.\n- `update`: Adds or updates patch level information.\n- `rm`: Removes previously stored patch level information.\n\nFor detailed usage information, run `patching-tool help store`.\n"
  },
  {
    "path": "en/deployment/articles-dxp/100-reference/07-patching-tool-configuration-properties.markdown",
    "content": "---\nheader-id: patching-tool-configuration-properties\n---\n\n# Patching Tool Configuration Properties\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/maintaining-a-liferay-installation/reference/configuring-the-patching-tool.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nHere are the Patching Tool configuration properties. See \n[Configuring the Patching Tool](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-patching-tool) \nfor more information on configuring the Patching Tool. \n\n**patching.mode:** This can be `binary` (the default) or `source` if you're\npatching a source tree. Patches contain both binary and source patches. If your\ndevelopment team extends @product@, have them patch their source tree. \n\n**patches.folder:** Specify where to store patches. The default location is\n`./patches`. \n\n**war.path:** Specify the location of the @product@ installation inside your\napplication server. Alternatively, you can specify a `.war` file here, and you\ncan patch a @product@ `.war` for installation to your application server. \n\n**global.lib.path:** Specify the location for storing `.jar` files on the global\nclasspath. If you're not sure, search for `portal-kernel.jar`; it's on\nthe global classpath. This property is only valid if your `patching.mode` is\n`binary`. \n\n**liferay.home:** Specify the default location for the `data`, `osgi`, and \n`tools` folders.\n\n**source.path:** Specify the location of your @product@ source tree. This \nproperty is only valid if your `patching.mode` is `source`. \n\nService Pack detection is available behind a proxy server. To configure your\nproxy, use the following settings, making sure to replace `[PROXY_IP_ADDRESS]`\nwith your proxy server's IP address and replace the port numbers with yours: \n\n```properties\n### Proxy settings\n\n# HTTP Proxy\n\n#proxy.http.host=[PROXY_IP_ADDRESS]\n#proxy.http.port=80\n#proxy.http.user=user\n#proxy.http.password=password\n\n# HTTPS Proxy\n\nproxy.https.host=[PROXY_IP_ADDRESS]\nproxy.https.port=80\nproxy.https.user=user\nproxy.https.password=password\n\n# SOCKS Proxy\n\n#proxy.socks.host=[PROXY_IP_ADDRESS]\n#proxy.socks.port=1080\n#proxy.socks.user=user\n#proxy.socks.password=password\n```\n"
  },
  {
    "path": "en/deployment/build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"deployment reference\" basedir=\".\">\n\t<property name=\"project.dir\" value=\"../../../liferay-docs\" />\n\n\t<import file=\"../build.xml\" />\n\n\t<property name=\"doc.dir\" value=\"deployment\"/>\n    <property name=\"purpose.dir\" value=\".\" />\n\t<property name=\"doc.type\" value=\"article\"/>\n</project>\n"
  },
  {
    "path": "en/deployment/drawings/README.markdown",
    "content": "Use this directory to hold any miscellaneous files, such as graphical drawings,\nused in making the articles or images.\n"
  },
  {
    "path": "en/developer/appdev/articles/01-application-development/01-application-development-intro.markdown",
    "content": "---\nheader-id: application-development\n---\n\n# Application Development\n\n[TOC levels=1-4]\n\nWriting applications on Liferay's standards-based platform makes your life\neasier. Whether you create headless services for clients to access, full-blown\nweb applications with beautiful UIs, or anything in between, @product@\nstreamlines the process to help you get your job done faster. \n\nLiferay's framework embraces your existing tools and build environments like\n[Maven](https://maven.apache.org) and [Gradle](https://gradle.org). You can work\nwith the standard technologies you know and leverage Liferay's APIs for\nDocuments, Permissions, Search, or Content when you need them. Here's a high\nlevel view of what you can do: \n\n-   **Deployment of existing standards-based apps:** If you have an existing app \n    built outside of @product@, you can deploy it on @product@. The Liferay\n    Bundler Generator and Liferay npm Bundler provide the project scaffolding\n    and packaging to deploy [Angular](https://angular.io/), [React](https://reactjs.org/), and [Vue](https://vuejs.org/) web front-ends as Widgets.\n    Spring Portlet MVC app conversion to [PortletMVC4Spring](https://github.com/liferay/portletmvc4spring) requires only a few\n    steps. JSF applications work almost as-is. Portlet 3.0 or 2.0 compliant\n    portlets deploy on @product@. \n\n-   **Back-end Java services, web services, and REST services:** Service Builder\n    is an object-relational mapper where you describe your data model in\n    a single `xml` file. From this, you can generate the tables, a Java API for\n    accessing your data model, and web services. On top of these, REST Builder\n    generates OpenAPI-based REST services your client applications can call. \n\n-   **Authentication and single-sign on (SSO):** OAuth 2.0, OpenID Connect, and\n    SAML are built-in and ready to go. \n\n-   **Front-end web development using Java EE and/or JavaScript:** Use Java EE\n    standard Portlet technology (JSR 168, JSR 286, JSR 362) with CDI and/or JSF.\n    Prefer Spring? [PortletMVC4Spring](https://github.com/liferay/portletmvc4spring) brings the Spring MVC Framework to Liferay. \n    Rather have a client-side app? Write it in [Angular](https://angular.io/),\n    [React](https://reactjs.org/), or [Vue](https://vuejs.org/). Been using\n    @product@ for a while? Liferay MVC Portlet is better than ever. \n\n-   **Frameworks and APIs for every need:** Be more productive by using\n    Liferay's built-in and well-tested APIs that cover often-used features like\n    file management(upload/download), permissions, comments, out-of-process\n    messaging, or UI elements such as data tables and item selectors. @product@\n    offers many APIs for every need, from an entire workflow framework to\n    a streamlined way of getting request parameters.\n\n-   **Tool freedom:** Liferay provides Maven archetypes, [Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace),\n    [Gradle](/docs/7-2/reference/-/knowledge_base/r/gradle-plugins) and [Maven](/docs/7-2/reference/-/knowledge_base/r/maven-plugins) plugins, a [Yeoman](http://yeoman.io/)-based [theme generator](/docs/7-2/reference/-/knowledge_base/r/theme-generator), and [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli)\n    to integrate with any development workflow. On top of that, you can use our\n    [IntelliJ plugin](/docs/7-2/reference/-/knowledge_base/r/intellij) or the\n    Eclipse-based [Liferay Developer Studio](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio) if you need \n    a full-blown development environment. \n\n-   **Developer community:** The\n    [@product@ community](https://liferay.dev)\n    is helpful and active. \n\n## Getting Started with Liferay Development\n\nWant to see what it's like to develop an app on @product@? Here's a quick tour. \n\n### Create Your Object Model and Database in One Shot\n\nYou don't need a database to work with Liferay, but if your app uses one, you\ncan design it and your object model at the same time with Liferay's\nobject-relational mapper, [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder). \nYou define your object model in a single `xml` file: \n\n```xml\n<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_0_0.dtd\">\n<service-builder auto-namespace-tables=\"true\" package-path=\"com.liferay.docs.guestbook\">\n    <author>liferay</author>\n    <namespace>GB</namespace>\n    <entity name=\"Guestbook\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n\n        <column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n        <column name=\"name\" type=\"String\" />\n\n        <finder name=\"Name\" return-type=\"Collection\"/>\n            <finder-column name=\"name\" />\n        </finder>\n\n    </entity>\n\n\t<entity name=\"Entry\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n\t\n\t    <column name=\"entryId\" primary=\"true\" type=\"long\" />\n        <column name=\"name\" type=\"String\" />\n        <column name=\"email\" type=\"String\" />\n        <column name=\"message\" type=\"String\" />\n        <column name=\"guestbookId\" type=\"long\" />\n\n        <finder name=\"Email\" return-type=\"Collection\" />\n            <finder-column name=\"email\" />\n        </finder>\n\n    </entity>\n\n</service-builder>\n```\n\nService Builder generates your object model, database, SOAP, and JSON web\nservices automatically. Java classes are ready for you to implement your\nbusiness logic around generated CRUD operations. The web services are mapped to\nyour business logic. If you want a REST interface, you can create one. \n\n### Create a REST Interface\n\n[REST Builder](/docs/7-2/appdev/-/knowledge_base/a/rest-builder) helps you define\nREST interfaces for your APIs, using [OpenAPI/Swagger](https://swagger.io/docs/specification/about/). \nCreate your [YAML definition](https://swagger.io/docs/specification/basic-structure/) \nfile for your REST interface along with a configuration file defining where Java\nclasses, a client, and tests should be generated, and you have REST endpoints\nready to call your API. \n\nNext, you need a client. You can use @product@ in headless mode and write your\nweb and mobile clients any way you want. Or you can create your web clients on\nLiferay's platform and take advantage of its many tools and APIs that speed up\ndevelopment. \n\n### Create a Web Client\n\n@product@ is an ideal platform upon which to build a web client. Its Java\nEE-based technology means you can pick from the best it has to offer: Spring MVC\nusing [PortletMVC4Spring](https://github.com/liferay/portletmvc4spring), the new\nbackwards-compatible Portlet 3, JSF using [Liferay Faces](https://liferayfaces.org), \nor the venerable OSGi-based [Liferay MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet). \nIf you're a front-end developer, deploy your Angular, React, or Vue-based\nfront-end applications to run as widgets next to the rest of @product@'s\ninstalled applications. \n\n### Use Liferay's Frameworks\n\nYour apps need features. Liferay has implemented tons of common functionality\nyou can use in your applications. The [Liferay-UI](@platform-ref@/7.2-latest/taglibs/util-taglib/liferay-ui/tld-summary.html) tag library \nhas tons of web components like Search Container (a sortable data table),\npanels, buttons, and more. Liferay's [Asset Framework](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework)\ncan publish data from your application in context wherever users need it---as\na notification, a related asset, as tagged or categorized data, or as relevant\ndata based on a [user segment](/docs/7-2/user/-/knowledge_base/u/creating-user-segments). Need to provide file upload/download? Use the \n[Documents API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). Need a robust permissions system? Use [Liferay permissions](/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions).\nWant users to submit comments? Use Liferay's [comments](/docs/7-2/frameworks/-/knowledge_base/f/adding-comments-to-your-app).\nNeed to process data outside the request/response? Use the Message Bus. \n<!-- Add Message Bus link back once article is available -->\nShould users select items from a list? Use the [Item Selector](/docs/7-2/frameworks/-/knowledge_base/f/item-selector).\n\n## Next Steps\n\nSo what's next? [Download](/download) @product@ and [create your first\nproject](/docs/7-2/reference/-/knowledge_base/r/creating-a-project)!  Have a\nlook at our [back-end](/docs/7-2/appdev/-/knowledge_base/a/service-builder),\n[REST Builder](/docs/7-2/appdev/-/knowledge_base/a/rest-builder), and\n[front-end](/docs/7-2/appdev/-/knowledge_base/a/web-front-ends) docs, examine\nwhat Liferay's [frameworks](/docs/7-2/frameworks/-/knowledge_base/f/frameworks)\nhave to offer, and then go create the beautiful things that only you can make. \n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/01-web-front-ends-intro.markdown",
    "content": "---\nheader-id: web-front-ends\n---\n\n# Developing Web Front-Ends\n\n[TOC levels=1-4]\n\nLiferay's open development framework removes barriers so developers can write\napplications faster. If you already have an application, you can deploy it on\n@product@:\n\n- Java-based standards (CDI, JSF, Portlets, Spring)\n- Front-end standards (Angular, React, Vue)\n\nIf you plan to write a new application and deploy it on @product@, you can use\nthe frameworks you know along with the build tools (Gradle, Maven) you know.\nLiferay also offers its own development framework called MVC Portlet that it\nuses to develop applications. When you want to integrate with\n[Liferay services](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\nand frameworks such as permissions, assets, and indexers, you'll find that these\neasily and seamlessly blend with your application to provide a great user\nexperience.\n\nRegardless of your development strategy for applications, you'll find @product@\nto be a flexible platform that supports anything you need to write.\n\n## Using Popular Frameworks\n\nLiferay gives you a head start on developing and deploying apps that use these\npopular Java and JavaScript-based technologies:\n\n-   [Angular Widget](/docs/7-2/appdev/-/knowledge_base/a/developing-an-angular-application)\n-   [React Widget](/docs/7-2/appdev/-/knowledge_base/a/developing-a-react-application)\n-   [Vue Widget](/docs/7-2/appdev/-/knowledge_base/a/developing-a-vue-application)\n-   [Liferay MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet)\n-   [PortletMVC4Spring Portlet](/docs/7-2/appdev/-/knowledge_base/a/portletmvc4spring)\n-   [JSF Portlet](/docs/7-2/appdev/-/knowledge_base/a/jsf-portlet)\n<!---   [Bean Portlet](/docs/7-2/appdev/-/knowledge_base/a/bean-portlet) TODO uncomment when Bean Portlet is available. jhinkey -->\n\n| **Note:** The Reference section describes\n| [sample projects](/docs/7-2/reference/-/knowledge_base/r/sample-projects) and\n| [project templates](/docs/7-2/reference/-/knowledge_base/r/project-templates)\n| for creating UIs using other technologies.\n\nAngular, React, and Vue applications are written the same as you would outside of\n@product@---using\n[npm](https://www.npmjs.com/)\nand the webpack dev server. The Liferay JS Generator creates a portlet bundle\n(project) for developing and deploying each type of app. The bundle\nproject comes with npm commands for building, testing, and deploying the app. It\npackages the app's dependencies (including JavaScript packages), deploys the\nbundle as a JAR, and installs the bundle to @product@'s run time environment,\nmaking your app available as a widget.\n\nYou can also develop web front-ends using Java EE standards.\n@product@ supports the\n[JSR 362](https://jcp.org/en/jsr/detail?id=362)\nPortlet 3.0 standard which is backwards-compatible with the\n[JSR 286](http://jcp.org/en/jsr/detail?id=286)\nPortlet 2.0 standard from the Java Community Process (JCP). Each portlet\nframework has benefits you may wish to consider.\n\nBean Portlet is the only framework containing all of the Portlet 3 features:\n\n-   Contexts and Dependency Injection (CDI)\n-   Extended method annotations\n-   Explicit render state\n-   Action, render, and resource parameters\n-   Asynchronous support\n\nIf you're a JavaServer Faces (JSF) developer, the\n[Liferay Faces Bridge](/docs/7-1/reference/-/knowledge_base/r/understanding-liferay-faces-bridge)\nsupports deploying JSF web apps as portlets without writing portlet-specific\nJava code. It also contains innovative features that make it possible to\nleverage the power of JSF 2.x inside a portlet application.\n\nIf Spring is your thing, Spring Portlet MVC portlets are easy to configure and\ndeploy on @product@. You can continue using Spring features, including Spring\nbeans and Spring dependency injection.\n\nLast but not least, Liferay MVC Portlet continues to be a favorite with\nexperienced Liferay developers, and makes portlet development easy for Liferay\nnewcomers. It leverages OSGi Declarative Services (DS) for injecting\ndependencies and defining configurable extension points. Since @product@ core\nand Liferay-written apps use DS, gaining experience with DS helps you\ndevelop @product@ extensions and customizations. Liferay MVC Portlet works\nseamlessly with many Liferay frameworks, such as MVC commands, Service Builder,\nand more.\n\nNo matter which development framework you choose, you'll be able to get an app\nup and running fast.\n\n## Getting Started\n\nIf you have an existing app that uses one the frameworks described above, your\nfirst step is to deploy it to @product@. Most deployments involve configuration\nsteps that you can complete in an hour or less.\n\nYou can also build apps from scratch using the tools you like or leveraging\nLiferay's tool offering. Liferay provides templates for creating all kinds of\napps and samples that you can examine and modify to fit your needs.\n\nOnce your app is functional, you can improve your app by integrating it with\nLiferay frameworks:\n\n-   Localization\n-   Permissions\n-   Search and indexing\n-   Asset publishing\n-   Workflow\n-   Staging\n-   Data export and import\n\nLiferay provides frameworks that integrate these features fast. As you develop\napps on @product@, you'll enjoy using what you know, discover frameworks and\ntools that boost your productivity, and have fun creating rich, full-featured\napplications.\n\nIf you're experienced with developing one of the listed app types, feel free to\njump ahead to it. Otherwise, Angular Widgets is next.\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/02-angular-widget/01-developing-an-angular-application-intro.markdown",
    "content": "---\nheader-id: developing-an-angular-application\n---\n\n# Developing an Angular Application\n\n[TOC levels=1-4]\n\nRunning an existing Angular app on @product@ makes the app available as a\nwidget for using on site pages. You can [adapt your existing Angular app](/docs/7-2/reference/-/knowledge_base/r/adapting-existing-apps-to-run-on-product), \nbut this doesn't give you access to the bundler and its various loaders to \ndevelop your project further in @product@. To have access to all of @product@'s \nfeatures, you must use the Liferay JS Generator and Liferay npm Bundler to merge \nyour files into a portlet bundle, adapt your routes and CSS, and deploy your \nbundle. \n\n![Figure 1: Apps like this Guestbook app are easy to migrate to @product@.](../../../images/appdev-angular-app-migrated.png)\n\nFollow these steps:\n\n1.  Using [npm](https://www.npmjs.com), install the Liferay JS Generator:\n\n        npm install -g yo generator-liferay-js\n\n2.  Generate an Angular-based portlet bundle project for deploying your app to \n    your \n    [@product@ installation](/deployment/docs/installing-product). \n\n        yo liferay-js\n\n    Select `Angular based portlet` and opt for generating sample code. Here's the bundle's structure: \n\n    -   `[my-angular-portlet-bundle]`\n        -   `assets/` &rarr; CSS, HTML templates, and resources\n            -   `css/` &rarr; CSS files\n                -   `styles.css` &rarr; Default CSS file\n            -   `app/` &rarr; HTML templates\n                -   `app.component.html` &rarr; Root component template\n        - `features/` &rarr; @product@ bundle features\n            -   `localization/` &rarr; Resource bundles\n                -   `Language.properties` &rarr; Default language keys\n        -   `src/` &rarr; JavaScript an TypeScript files\n            -   `app/` &rarr; Application modules and Components\n                -   `app.component.ts` &rarr; Main component\n                -   `app.module.ts` &rarr; Root module\n                -   `dynamic.loader.ts` &rarr; Loads an Angular component \n                    dynamically for the portlet to attach to\n            -   `types/`\n                -   `LiferayParams.ts` &rarr; Parameters passed by @product@ to\n                    the JavaScript module\n            -   `index.ts` &rarr; Main module invoked by the \"bootstrap\" module to initialize the portlet\n            -   `polyfills.ts` &rarr; Fills in browser JavaScript implementation\n                gaps\n        -   `package.json` &rarr; npm bundle configuration\n        -   `README.md`\n        -   `.npmbuildrc` &rarr; Build configuration\n        -   `.npmbundlerrc` &rarr; Bundler configuration\n        -   `tsconfig.json` &rarr; TypeScript configuration\n\n3.  Copy your app files, matching the types listed below, into your new project.\n\n    | File type | Destination | Comments |\n    | --------- | ----------- | -------- |\n    | HTML | `assets/app/` | Merge your main component with the existing `app.component.html`. |\n    | CSS  | `assets/css/` | Overwrite `styles.css`. |\n    | TypeScript and JavaScript | `src/app/` |  Merge with all files **except** `app.module.ts`---the root module merge is explained in a later step. |\n\n4.  Update your component class `templateUrl`s to use the `web-context` value \n    declared in your project's `.npmbundlerrc`  file. Here's the format: \n\n        templateUrl: `/o/[web-context]/app/[template]`\n\n    Here's an example:\n\n        templateUrl: '/o/my-angular-guestbook/app/add-entry/add-entry.component.html'\n\n5.  Update your bundle to use portlet-level styling. \n\n    -   Import all component CSS files through the CSS file (default is \n        `styles.css`) your bundle's `package.json` file sets for your portlet.\n        Here's the default setting:\n\n        ```json\n        \"portlet\": {\n\t\t    \"com.liferay.portlet.header-portlet-css\": \"/css/styles.css\",\n        ...\n        }\n        ```\n\n    -   Remove `selector` and `styleUrls` properties from your component \n        classes. \n\n6.  In your routing module's `@NgModule` decorator, configure the router option\n    `useHash: true`. This tells Angular to use client-side routing in the form\n    of `.../#/[route]`, which prevents client-side parameters (i.e., anything\n    after `#`) from being sent back to @product@. \n\n    For example, your routing module class `@NgModule` decorator might look like\n    this:\n\n    ```javascript\n    @NgModule({\n      imports: [RouterModule.forRoot(routes, {useHash: true})],\n      exports: [RouterModule]\n    })\n    export class AppRoutingModule { }\n    ```\n\n7.  Also in your routing module, export your view components for your root \n    module (discussed next) to use. For example,\n\n    ```javascript\n    export const routingComponents = [ViewComponent1, ViewComponent2]\n    ```\n\n8.  Merge your root module with `src/app/app.module.ts`, configuring it to \n    dynamically load components. \n\n    | **Note:** Components must be loaded dynamically to attach to the portlet's\n    | DOM. The DOM is determined at run time when the portlet's page is\n    | rendered. \n\n    -   Import the `routingComponents` constant and the app routing module class\n        from your app routing module. For example,\n\n        ```javascript\n        import { AppRoutingModule, routingComponents } from './app-routing.module';\n        ```\n\n    -   Specify the base href for the router to use in the navigation URLs. \n\n        ```javascript\n        import { APP_BASE_HREF } from '@angular/common';\n        ...\n        \n        @NgModule({\n            ...\n            providers: [{provide: APP_BASE_HREF, useValue: '/'}]\n        })\n        ```\n\n    -   Declare the `routingComponents` constant in your `@NgModule` decorator. \n\n        ```javascript\n        @NgModule({\n          declarations: [\n              routingComponents,\n              ...\n          ],\n          ...\n        })\n        ```\n\n    -   Make sure your `@NgModule` `bootstrap` property has no components. All \n        components are loaded dynamically using the `entryComponents` array\n        property. The empty `ngDoBootstrap()` method nullifies the default\n        bootstrap implementation. \n\n        ```javascript\n        @NgModule({\n          ...\n    \t    entryComponents: [AppComponent],\n            bootstrap: [],\n            ...\n        })\n        export class AppModule {\n            ngDoBootstrap() {}\n            ...\n        }\n        ```\n\n    Your root module `app.module.ts` should look like this: \n\n    ```javascript\n    import { APP_BASE_HREF } from '@angular/common';\n    import { AppRoutingModule, routingComponents } from './app-routing.module';\n    // more imports ...\n\n    @NgModule({\n      declarations: [\n        AppComponent,\n        routingComponents, \n        // more declarations ...\n      ],\n      imports: [\n        AppRoutingModule,\n        // more imports ...\n      ],\n      entryComponents: [AppComponent],\n      providers: [{provide: APP_BASE_HREF, useValue: '/'}],\n      bootstrap: [],\n      // more properties ...\n    })\n    export class AppModule {\n        ngDoBootstrap() {}\n\n        // ...\n    }\n    ```\n\n9.  Merge your app `package.json` file's `dependencies` and `devDependencies` \n    into the bundle's `package.json`.\n\n    | **Note:** To work around build errors caused by the `rxjs` dependency, set\n    | the dependency to version `\"6.0.0\"`. See\n    | [LPS-92848](https://issues.liferay.com/browse/LPS-92848)\n    | for details. \n \n10. Finally, deploy your bundle:\n\n        npm run deploy\n\nCongratulations! Your Angular app is deployed and now available as a widget that\nyou can add to site pages. \n\nThe Liferay npm Bundler confirms the deployment:\n\n    Report written to liferay-npm-bundler-report.html\n    Deployed my-angular-guestbook-1.0.0.jar to c:\\git\\bundles\n\nThe @product@ console confirms your bundle started: \n\n    2019-03-22 20:17:53.181 INFO  [fileinstall-C:/git/bundles/osgi/modules][BundleStartStopLogger:39] STARTED my-angular-guestbook_1.0.0 [1695]\n\nTo find your widget, select the *Add* icon\n(![Add](../../../images/icon-add-app.png)),\nnavigate to *Widgets* and then the category you specified to the Liferay Bundle\nGenerator (*Sample* is the default category). \n\n## Related Topics\n\n[Web Services](/docs/7-2/frameworks/-/knowledge_base/f/web-services)\n\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n\n[Localization](/docs/7-2/frameworks/-/knowledge_base/f/localization)\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/03-react-widget/01-developing-react-application-intro.markdown",
    "content": "---\nheader-id: developing-a-react-application\n---\n\n# Developing a React Application\n\n[TOC levels=1-4]\n\nRunning an existing React app on @product@ makes the app available as a \nwidget for using on site pages. You can [adapt your existing React app](/docs/7-2/reference/-/knowledge_base/r/adapting-existing-apps-to-run-on-product), \nbut this doesn't give you access to the bundler and its various loaders to \ndevelop your project further in @product@. To have access to all of @product@'s \nfeatures, you must use the Liferay JS Generator and Liferay npm Bundler to merge \nyour files into a portlet bundle, update your static resource paths, and deploy \nyour bundle. \n\n![Figure 1: Apps like this Guestbook app are easy to migrate to @product@.](../../../images/appdev-react-app-migrated.png)\n\nFollow these steps:\n\n1.  Using [npm](https://www.npmjs.com/), install the Liferay JS Generator:\n\n        npm install -g yo generator-liferay-js\n\n2.  Generate a React based portlet bundle project for deploying your app to \n    your \n    [@product@ installation](/deployment/docs/installing-product). \n\n        yo liferay-js\n\n    Select `React based portlet` and opt for generating sample code. Here's the \n    bundle's structure: \n\n    -   `my-react-portlet-bundle`\n        -   `assets/` &rarr; CSS and resources\n            -   `css/` &rarr; CSS files\n                - `styles.css` &rarr; Default CSS file\n        -   `features/` &rarr; @product@ bundle features\n            -   `localization` &rarr; Resource bundles\n                -   `Language.properties` &rarr; Default language keys\n        -   `src/` &rarr; JavaScript and React component files\n            -   `AppComponent.js` &rarr; Sample React component that you can remove\n            -   `index.js` &rarr; Main module used to initialize the portlet\n        -   `.babelrc` &rarr; Babel configuration\n        -   `.npmbuildrc` &rarr; Build configuration\n        -   `.npmbundlerrc` &rarr; Bundler configuration\n        -   `package.json` &rarr; npm bundle configuration\n        -   `README.md`\n\n3.  Copy your app files, matching the types listed below, into your new project.\n\n    | File type | Destination | Comments |\n    | --------- | ----------- | -------- |\n    | CSS  | `assets/css/` | Overwrite `styles.css`. |\n    | JavaScript | `src/` |  Merge with all files **except** `index.js`---the main module merge is explained in a later step. |\n    | Static resources | `assets/` |  Include resources such as image files here |\n\n4.  Update your bundle to use portlet-level styling.\n\n    -   Import all component CSS files through the CSS file (default is \n        `styles.css`) your bundle's `package.json` file sets for your portlet.\n        Here's the default setting:\n\n       ```json\n        \"portlet\": {\n\t\t    \"com.liferay.portlet.header-portlet-css\": \"/css/styles.css\",\n            ...\n        }\n       ```\n\n    -   Remove any CSS imports you have in your JS files\n\n5.  Update any static resource references to use the `web-context` value \n    declared in your project's `.npmbundlerrc`  file, and remove any imports for \n    the resource. For example, if you have an image file called `logo.png` in your \n    `assets` folder, you would use the format below. Note that the `assets` \n    folder is not included in the path.\n\n    Here is the format:\n \n    ```html\n    /o/[web-context]/[resource]\n    ```\n\n    Here's an example image resource:\n\n    ```html\n    <img alt=\"React logo\" src=\"/o/react-guestbook-migrated/logo.png\">\n    ```\n\n6.  Merge your entry module with `src/index.js`, configuring it to dynamically \n    load components. \n\n    | **Note:** Components must be loaded dynamically to attach to the portlet's\n    | DOM. The DOM is determined at run time when the portlet's page is\n    | rendered. \n\n    -   Use the `HashRouter` for routing between component views, as @product@ \n        requires hash routing for proper portal navigation:\n \n       ```javascript\n        import { HashRouter as Router } from 'react-router-dom';\n       ```\n\n    -   Place your code inside the `main()` function.\n\n    -   Render your app inside the `portletElementId` element that is passed in \n        the `main()` function. This is required to render the React app inside\n        the portlet.\n\n    Your entry module `index.js` should look like this. \n\n    ```javascript\n    import React from 'react';\n    import ReactDOM from 'react-dom';\n    //import './index.css';//removed for Portal Migration\n    import App from './App';\n    import { HashRouter as Router } from 'react-router-dom';\n\n    export default function main({portletNamespace, contextPath, \n    portletElementId}) {\n          ReactDOM.render((\n            <Router>\n              <App/>\n            </Router>\n          ), document.getElementById(portletElementId));\n    }\n    ```\n\n7.  Merge your app `package.json` file's `dependencies` and `devDependencies` \n    into the bundle's `package.json`.\n \n8.  Finally, deploy your bundle:\n\n        npm run deploy\n\nCongratulations! Your React app is deployed and now available as a widget that \nyou can add to site pages. \n\nThe Liferay npm Bundler confirms the deployment:\n\n    Report written to liferay-npm-bundler-report.html\n    Deployed my-react-guestbook-1.0.0.jar to c:\\git\\bundles\n\nThe @product@ console confirms your bundle started: \n\n    2019-03-22 20:17:53.181 INFO  \n    [fileinstall-C:/git/bundles/osgi/modules][BundleStartStopLogger:39] \n    STARTED my-react-guestbook_1.0.0 [1695]\n\nTo Find your widget, click the *Add* icon \n(![Add](../../../images/icon-add-app.png)), \nnavigate to *Widgets* and then the category you specified to the Liferay Bundle \nGenerator (*Sample* is the default category). \n\n## Related Topics\n\n[Web Services](/docs/7-2/frameworks/-/knowledge_base/f/web-services)\n\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n\n[Localization](/docs/7-2/frameworks/-/knowledge_base/f/localization)\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/04-vue-widget/01-developing-a-vue-application-intro.markdown",
    "content": "---\nheader-id: developing-a-vue-application\n---\n\n# Developing a Vue Application\n\n[TOC levels=1-4]\n\nRunning an existing Vue app on @product@ makes the app available as a\nwidget for using on site pages. You can [adapt your existing Vue app](/docs/7-2/reference/-/knowledge_base/r/adapting-existing-apps-to-run-on-product), \nbut this doesn't give you access to the bundler and its various loaders to \ndevelop your project further in @product@. To have access to all of @product@'s \nfeatures, you must use the Liferay JS Generator and Liferay npm Bundler to merge \nyour files into a portlet bundle, update your static resource paths, and deploy \nyour bundle. The steps below demonstrate how to prepare a Vue app that uses \nsingle file components (`.vue` files) with multiple views. \n\n![Figure 1: Vue Apps like this Guestbook App are easy to deploy, and they look great in @product@.](../../../images/appdev-vue-migrated.png)\n\n| **Note:** if you have a tree of components expressed as `.vue` templates, only \n| the root one will be available as a true AMD module. \n\nFollow these steps:\n\n1.  Using [npm](https://www.npmjs.com/), install the Liferay JS Generator:\n\n        npm install -g yo generator-liferay-js\n\n2.  Generate a Vue based portlet bundle project: \n\n        yo liferay-js\n\n    Select `Vue based portlet` and opt for generating sample code. Here's the \n    bundle's structure: \n\n    -   `my-vue-portlet-bundle`\n        -   `assets/` &rarr; CSS and resources\n            -   `css/` &rarr; CSS not included in `.vue` files.\n        - `features/` &rarr; @product@ bundle features\n            -   `localization/` &rarr; Resource bundles\n                -   `Language.properties` &rarr; Default language keys\n            -   `settings.json` &rarr; Placeholder System Settings\n        -   `src/` &rarr; JavaScript and Vue files\n            -   `index.js` &rarr; Main module used to initialize the portlet\n        -   `.babelrc` &rarr; Babel configuration\n        -   `.npmbuildrc` &rarr; Build configuration\n        -   `.npmbundlerrc` &rarr; Bundler configuration\n        -   `package.json` &rarr; npm bundle configuration\n        -   `README.md`\n\n3.  Copy your app files, matching the types listed below, into your new project.\n\n    | File type | Destination | Comments |\n    | --------- | ----------- | -------- |\n    | CSS  | `assets/css/` | Overwrite `styles.css`. |\n    | Static resources | `assets` |  Include resources such as image files here |\n    | VUE and JS| `src` | Merge your main component with the existing `index.js`. More info on that below. |\n\n4.  Update your bundle to use portlet-level styling. \n\n    -   If you have internal CSS included with `<style>` tags in your `.vue` \n        files, import `.index.css` in `/assets/styles.css`. This is generated by \n        the modified build script further down:\n\n        ```css\n        @import '../index.css';\n        ```\n\n    -   Import all custom CSS files (i.e. CSS not included in `.vue` files) \n        through the CSS file (default is `styles.css`) your bundle's \n        `package.json` file sets for your portlet.\n        Here's the default setting:\n\n        ```json\n        \"portlet\": {\n\t\t    \"com.liferay.portlet.header-portlet-css\": \"/css/styles.css\",\n        ...\n        }\n        ```\n\n5.  Update any static resource references to use the `web-context` value \n    declared in your project's `.npmbundlerrc`  file. Here's the format: \n\n    ```\n    /o/[web-context]/[resource]\n    ```\n\n    Here's an example image resource:\n\n    ```html\n    <img alt=\"Vue logo\" src=\"/o/vue-guestbook-migrated/logo.png\">\n    ```\n\n6.  Merge your entry module with `src/index.js`, following these steps to \n    dynamically load components. \n\n    | **Note:** Components must be loaded dynamically to attach to the portlet's\n    | DOM. The DOM is determined at runtime when the portlet's page is\n    | rendered. \n\n    -   Use Vue's runtime + compiler module \n        (`import Vue from 'vue/dist/vue.common';`) so you don't have to process \n        templates during build time. This is imported by default at the top of \n        the file.\n\n    -   Remove the sample content from the `main()` function (i.e. the `node` \n        constant and its use), and replace it with your router code.\n\n    -   Make these updates to the `new Vue` instance:\n    \n        -   Remove the default data properties (the ones you just removed in the \n            sample content), and set the render element to `portletElementId`. \n            This is required and ensures that your app is rendered inside the \n            portlet.\n        -   Add the router.\n        -   Add a render function that mounts your component wrapper to the Vue \n            instance and displays it.\n            \n        Your updated configuration should look like this:\n    \n        ```javascript\n        new Vue({\n          el: `#${portletElementId}`,\n          render: h => h(App),\n          router\n        })\n        ```\n\n    Your entry module `index.js` should look like this. \n\n    ```javascript\n    import Vue from 'vue/dist/vue.common';\n    import App from './App.vue'\n    import VueRouter from 'vue-router'\n    //Component imports\n\n    export default function main({portletNamespace, contextPath, portletElementId}) {\n\n      Vue.config.productionTip = false\n\n      Vue.use(VueRouter)\n\n      const router = new VueRouter({\n          routes: [\n              {\n                ...\n              }\n          ]\n      })\n      new Vue({\n        el: `#${portletElementId}`,\n        render: h => h(App),\n        router\n      })\n    }\n    ```\n\n7.  Merge your app `package.json` file's `dependencies` and `devDependencies` \n    into the project's `package.json`, and replace the `babel-cli` and \n    `babel-preset-env` dev dependencies with the newer `\"@babel/cli\": \"^7.0.0\"` \n    and `\"@babel/preset-env\": \"^7.4.2\"` packages instead. Also include the \n    `\"vueify\": \"9.4.1\"` dev dependency.\n\n8.  Update the `.babelrc` file to use `@babel/preset-env` instead of \n    `env`:\n\n    ```json\n    \"presets\": [\"@babel/preset-env\"]\n    ```\n\n9.  If you're using `.vue` files, replace the build script in the `package.json` \n    with the one below to use `vue-cli-service`. The updated build script uses \n    vue-cli to access the main entrypoint for the app (`index.js` in the example \n    below) and combines all the Vue templates and JS files into one single file \n    named `index.common.js` and generates an `index.css` file for any internal \n    CSS included with `<style>` tags in `.vue` files:\n\n    ```json\n    \"scripts\": {\n      \"build\": \"babel --source-maps -d build src && vue-cli-service build --dest \n      build/ --formats commonjs --target lib --name index ./src/index.js && npm \n      run copy-assets && liferay-npm-bundler\",\n      \"copy-assets\": \"lnbs-copy-assets\",\n      \"deploy\": \"npm run build && lnbs-deploy\",\n      \"start\": \"lnbs-start\"\n    }\n    ```\n\n10. Update the `main` entry of the `package.json` to match the new \n    [CommonJS](http://www.commonjs.org/) \n    file name specified in the previous step:\n \n    ```json\n    \"main\": \"index.common\"\n    ```\n \n11. Finally, deploy your portlet bundle:\n\n        npm run deploy\n\nCongratulations! Your Vue app is deployed and now available as a widget that you \ncan add to site pages. \n\nThe liferay-npm-bundler confirms the deployment:\n\n    Report written to liferay-npm-bundler-report.html\n    Deployed my-vue-guestbook-1.0.0.jar to c:\\git\\bundles\n\nThe @product@ console confirms your bundle started: \n\n    2019-03-22 20:17:53.181 INFO  \n    [fileinstall-C:/git/bundles/osgi/modules][BundleStartStopLogger:39] \n    STARTED my-vue-guestbook_1.0.0 [1695]\n\nFind your widget by selecting the *Add* icon\n(![Add](../../../images/icon-add-app.png))\nand navigating to *Widgets* and the category you specified to the Liferay Bundle\nGenerator (*Sample* is the default category). \n\n## Related Topics\n\n[Web Services](/docs/7-2/frameworks/-/knowledge_base/f/web-services)\n\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n\n[Localization](/docs/7-2/frameworks/-/knowledge_base/f/localization)\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/05-liferay-mvc-portlet/01-liferay-mvc-portlet-intro.markdown",
    "content": "---\nheader-id: liferay-mvc-portlet\n---\n\n# Liferay MVC Portlet\n\n[TOC levels=1-4]\n\nIf you're an experienced developer, this is not the first time you've heard\nabout Model View Controller. If there are so many implementations of MVC\nframeworks in Java, why did Liferay create yet another one? Stay with us and\nyou'll see that Liferay MVC Portlet provides these benefits:\n\n-   It's lightweight, as opposed to many other Java MVC frameworks.\n-   There are no special configuration files that need to be kept in sync with\n    your code.\n-   It's a simple extension of\n    [`GenericPortlet`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/GenericPortlet.html).\n-   You avoid writing a bunch of boilerplate code, since Liferay's MVC Portlet\n    framework only looks for some pre-defined parameters when the `init()`\n    method is called.\n-   The controller can be broken down into MVC command classes, each of which\n    handles the controller code for a particular\n    [portlet phase](/docs/7-2/frameworks/-/knowledge_base/f/portlets) (render, action,\n    and resource serving phases).\n-   An MVC command class can serve multiple portlets.\n-   Liferay's portlets use it. That means there are plenty of robust\n    implementations to reference when you need to design or troubleshoot your\n    Liferay applications.\n\nThe Liferay MVC Portlet framework is light and easy to use. The default\n[`MVCPortlet` project](/docs/7-2/reference/-/knowledge_base/r/using-the-mvc-portlet-template)\ntemplate generates a fully configured and working project.\n\nHere, you'll learn how MVCPortlet works by covering these topics:\n\n- [MVC layers and modularity](#mvc-layers-and-modularity)\n- [Liferay MVC command classes](#liferay-mvc-command-classes)\n- [Liferay MVC portlet component](#liferay-mvc-portlet-component)\n- [Simple MVC portlets](#a-simpler-mvc-portlet)\n\nReview how each layer of the Liferay MVC portlet framework helps you separate\nthe concerns of your application.\n\n## MVC Layers and Modularity\n\nIn MVC, there are three layers, and you can probably guess what they are.\n\n**Model:** The model layer holds the application data and logic for manipulating\nit.\n\n**View:** The view layer contains logic for displaying data.\n\n**Controller:** The middle man in the MVC pattern, the Controller contains logic\nfor passing the data back and forth between the view and the model layers.\n\n@product@'s applications are divided into multiple discrete modules. With\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder), the\nmodel layer is generated into a `service` and an `api` module. That accounts for\nthe model in the MVC pattern. The view and the controller layers share a module,\nthe `web` module.\n\nGenerating the skeleton for a [multi-module Service Builder-driven MVC\napplication](/docs/7-2/reference/-/knowledge_base/r/using-the-service-builder-template)\nsaves you lots of time and gets you started on the more important (and\ninteresting, if we're being honest) development work.\n\n## Liferay MVC Command Classes\n\nIn a larger application, your `-Portlet` class can become monstrous and unwieldy\nif it holds all of the controller logic. Liferay provides MVC command classes to\nbreak up your controller functionality.\n\n-   **[`MVCActionCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCActionCommand.html):**\n    Use `-ActionCommand` classes to hold each of your portlet actions, which are\n    invoked by action URLs.\n-   **[`MVCRenderCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html):**\n    Use `-RenderCommand` classes to hold a `render` method that dispatches to\n    the appropriate JSP, by responding to render URLs.\n-   **[`MVCResourceCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html):**\n    Use `-ResourceCommand` classes to serve resources based on resource URLs.\n\nThere must be some confusing configuration files to keep everything wired\ntogether and working properly, right? Wrong: it's all easily managed in the\n`-Portlet` class's\n[`@Component`](https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html)\nannotation.\n\n## Liferay MVC Portlet Component\n\nWhether or not you plan to split up the controller into MVC command classes, the\nportlet `@Component` annotation configures the portlet. Here's a simple portlet\ncomponent as an example:\n\n```java\n@Component(\n    property = {\n        \"com.liferay.portlet.css-class-wrapper=portlet-hello-world\",\n        \"com.liferay.portlet.display-category=category.sample\",\n        \"com.liferay.portlet.icon=/icons/hello_world.png\",\n        \"com.liferay.portlet.preferences-owned-by-group=true\",\n        \"com.liferay.portlet.private-request-attributes=false\",\n        \"com.liferay.portlet.private-session-attributes=false\",\n        \"com.liferay.portlet.remoteable=true\",\n        \"com.liferay.portlet.render-weight=50\",\n        \"com.liferay.portlet.use-default-template=true\",\n        \"javax.portlet.display-name=Hello World\",\n        \"javax.portlet.expiration-cache=0\",\n        \"javax.portlet.init-param.always-display-default-configuration-icons=true\",\n        \"javax.portlet.name=\" + HelloWorldPortletKeys.HELLO_WORLD,\n        \"javax.portlet.resource-bundle=content.Language\",\n        \"javax.portlet.security-role-ref=guest,power-user,user\",\n        \"javax.portlet.supports.mime-type=text/html\"\n    },\n    service = Portlet.class\n)\npublic class HelloWorldPortlet extends MVCPortlet {\n}\n```\n\nThe `javax.portlet.name` property is required.  When using MVC commands, the\n`javax.portlet.name` property value links particular portlet URL/command\ncombinations to the correct portlet.\n\n| **Important:** Make your portlet name unique, considering how\n| [@product@ uses the name to create the portlet's ID](/docs/7-2/reference/-/knowledge_base/r/portlet-descriptor-to-osgi-service-property-map#ten).\n\nThere can be some confusion over exactly what kind of `Portlet.class`\nimplementation you're publishing with a component. The service registry expects\nthis to be the\n[`javax.portlet.Portlet`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/Portlet.html)\ninterface. Import that, and not, for example,\n`com.liferay.portal.kernel.model.Portlet`.\n\n| **Note:** The DTD [liferay-portlet-app_7_2_0.dtd](@platform-ref@/7.2-latest/definitions/liferay-portlet-app_7_2_0.dtd.html)\n| defines all the Liferay-specific attributes you can specify as properties in\n| your portlet components.\n|\n| Consider the `<css-class-wrapper>` element from the above link as an example.\n| To specify that property in your component, use this syntax in your property\n| list:\n|\n| `\"com.liferay.portlet.css-class-wrapper=portlet-hello-world\",`\n|\n| The properties namespaced with `javax.portlet.` are elements of the\n| [`portlet.xml` descriptor](https://docs.liferay.com/portlet-api/3.0/portlet-app_3_0.xsd).\n\n## A Simpler MVC Portlet\n\nIn simpler applications, you don't use MVC commands. Your\nportlet render URLs specify JSP paths in `mvcPath` parameters.\n\n```javascript\n<portlet:renderURL var=\"addEntryURL\">\n\t<portlet:param name=\"mvcPath\" value=\"/entry/edit_entry.jsp\" />\n\t<portlet:param name=\"redirect\" value=\"<%= redirect %>\" />\n</portlet:renderURL>\n```\n\nAs you've seen, Liferay's MVC Portlet framework gives you a well-structured\ncontroller layer that takes very little time to implement. With all your free\ntime, you could\n\n-   Learn a new language\n-   Take pottery classes\n-   Lift weights\n-   Work on your application's business logic\n\nIt's entirely up to you.\n\nTo get into the details of creating an MVC Portlet application, continue with\n[Creating an MVC\nPortlet](/docs/7-2/appdev/-/knowledge_base/a/creating-an-mvc-portlet). "
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/05-liferay-mvc-portlet/02-creating-an-mvc-portlet.markdown",
    "content": "---\nheader-id: creating-an-mvc-portlet\n---\n\n# Creating an MVC Portlet\n\n[TOC levels=1-4]\n\nGenerating MVC portlet projects is a snap using Liferay's project templates.\nHere you'll generate an MVC Portlet project and deploy the portlet to @product@.\n\n1.  Generate an [MVC Portlet project](/docs/7-2/reference/-/knowledge_base/r/using-the-mvc-portlet-template)\n    using a Gradle or Maven.\n\n    Here's the resulting folder structure for an MVC Portlet class named\n    `MyMvcPortlet` in a base package `com.liferay.docs.mvcportlet`:\n\n    - `my-mvc-portlet-project` &rarr; Arbitrary project name.\n        - `gradle`\n            - `wrapper`\n                - `gradle-wrapper.jar`\n                - `gradle-wrapper.properties`\n        - `src`\n            - `main`\n                - `java`\n                    - `com/liferay/docs/mvcportlet`\n                        - `constants`\n                            - `MyMvcPortletKeys.java` &rarr; Declares portlet constants.\n                        -  `portlet`\n                            - `MyMvcPortlet.java` &rarr; MVC Portlet class.\n                - `resources`\n                    - `content`\n                        - `Language.properties` &rarr; Resource bundle\n                    - `META-INF`\n                        - `resources`\n                            - `init.jsp` &rarr; Imports classes and taglibs and defines commonly used objects from the theme and the portlet.\n                            - `view.jsp` &rarr; Default view template.\n        - `bnd.bnd` &rarr; OSGi bundle metadata.\n        - `build.gradle`\n        - `gradlew`\n\n    The Maven-generated project includes a `pom.xml` file and does not include\n    the Gradle-specific files, but otherwise is exactly the same.\n\n    Here's the resulting MVC Portlet class:\n\n```java\npackage com.liferay.docs.mvcportlet.portlet;\n\nimport com.liferay.docs.mvcportlet.constants.MyMvcPortletKeys;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport javax.portlet.Portlet;\nimport org.osgi.service.component.annotations.Component;\n\n@Component(\n\timmediate = true,\n\tproperty = {\n\t\t\"com.liferay.portlet.display-category=category.sample\",\n\t\t\"com.liferay.portlet.instanceable=true\",\n\t\t\"javax.portlet.display-name=my-mvc-portlet-project Portlet\",\n\t\t\"javax.portlet.init-param.template-path=/\",\n\t\t\"javax.portlet.init-param.view-template=/view.jsp\",\n\t\t\"javax.portlet.name=\" + MyMvcPortletKeys.MyMvc,\n\t\t\"javax.portlet.resource-bundle=content.Language\",\n\t\t\"javax.portlet.security-role-ref=power-user,user\"\n\t},\n\tservice = Portlet.class\n)\npublic class MyMvcPortlet extends MVCPortlet {\n}\n```\n\nThe class extends\n[`MVCPortlet`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCPortlet.html).\nThe\n[`@Component`](https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html)\nannotation and `service = Portlet.class` attribute makes the class an OSGi\nDeclarative Services component that provides the\n[`javax.portlet.Portlet`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/Portlet.html)\nservice type. The `immediate = true` attribute activates the service immediately\non the portlet's deployment.\n\n2.  Set any portlet configuration or Liferay portlet configuration values\n    using `javax.portlet.*` and `com.liferay.portlet.*` `@Component` annotation properties\n    [`javax.portlet.*` and `com.liferay.portlet.*` `@Component` annotation properties](/docs/7-2/reference/-/knowledge_base/r/portlet-descriptor-to-osgi-service-property-map)\n    respectively.\n\n    Here are the example component's properties:\n\n    -   `\"com.liferay.portlet.display-category=category.sample\"`: Sets the\n        Widget's category to \"Sample\".\n\n    -   `\"com.liferay.portlet.instanceable=true\"`: Activates the component\n        immediately when its bundle installs.\n\n    -   `\"javax.portlet.display-name=my-mvc-portlet-project Portlet\"`: Sets the\n        portlet's Widget name.\n\n    -   `\"javax.portlet.init-param.template-path=/\"`: The path under\n        `src/main/resources/META-INF/resources/` where the templates reside.\n\n    -   `\"javax.portlet.init-param.view-template=/view.jsp\"`: Default view\n        template.\n\n    -   `\"javax.portlet.name=\" + MyMvcPortletKeys.MyMvc`: The portlet's unique\n        identity.\n\n    -   `\"javax.portlet.resource-bundle=content.Language\"`: Sets the portlet's\n        [resource bundle](/docs/7-2/frameworks/-/knowledge_base/f/localization)\n        to the `content/Language*.properties` file(s) in the\n        `src/main/resources/` folder.\n\n    -   `\"javax.portlet.security-role-ref=power-user,user\"`: Makes the @product@\n        virtual instance's power user and user Roles available for defining the\n        portlet's permissions.\n\n    | **Note:** To opt-in to Portlet 3.0 features, set the component property\n    | `\"javax.portlet.version=3.0\"`.\n\n3.  The portlet renders content via the view template\n    `src/main/resources/META-INF/resources/view.jsp` by default.\n\n4.  Build your project.\n\n    *Gradle:*\n\n    ```bash\n    gradlew jar\n    ```\n\n    *Maven:*\n\n    ```bash\n    mvn clean package\n    ```\n\n4.  Deploy the project [using your build\n    environment](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project) or\n    by building the project JAR and copying it to the `deploy/` folder in your\n    [Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home).\n\nThe MVC Portlet is now available in the @product@ UI, in the Widget category you\nassigned it.\n\n![Figure 1: The example portlet shows a message defined by the language property `yourmvc.caption=Hello from YourMVC!` in the Language.properties file.](../../../images/default-mvc-portlet-on-page.png)\n\nCongratulations on creating and deploying an MVC Portlet!\n\n## Related Topics\n\n[Writing MVC Portlet Controller Code](/docs/7-2/appdev/-/knowledge_base/a/writing-mvc-portlet-controller-code)\n\n[Configuring the View Layer](/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer)\n\n[MVC Action Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-action-command)\n\n[MVC Render Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-render-command)\n\n[MVC Resource Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-resource-command)\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/05-liferay-mvc-portlet/03-writing-mvc-portlet-controller-code.markdown",
    "content": "---\nheader-id: writing-mvc-portlet-controller-code\n---\n\n# Writing MVC Portlet Controller Code\n\n[TOC levels=1-4]\n\nIn MVC, your controller is a traffic director: it provides data to the right\nfront-end view for display to the user, and it takes data the user entered in\nthe front-end and passes it to the right back-end service. For this reason, the\ncontroller must process requests from the front-end, and it must determine the\nright front-end view to pass data back to the user. \n\nIf you have a small application that's not heavy on controller logic, you can\nput all your controller code in the `-Portlet` class. If you have more complex\nneeds (lots of actions, complex render logic to implement, or maybe even some\nresource serving code), consider breaking the controller into [MVC Render\nCommand classes](/docs/7-2/appdev/-/knowledge_base/a/mvc-render-command), [MVC\nAction Command classes](/docs/7-2/appdev/-/knowledge_base/a/mvc-action-command),\nand\n[MVC Resource Command classes](/docs/7-2/appdev/-/knowledge_base/a/mvc-resource-command). \nHere you'll implement controller logic for small applications, where all the\ncontroller code is in the `-Portlet` class. It involves these things: \n\n- Action methods\n- Render logic\n- Setting and retrieving request parameters and attributes\n\nStart with creating action methods. \n\n## Action Methods\n\nYour portlet class can act as your controller by itself and process requests\nusing action methods. Here's a sample action method: \n\n```java\npublic void addGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException, SystemException {\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n        Guestbook.class.getName(), request);\n\n    String name = ParamUtil.getString(request, \"name\");\n\n    try {\n        _guestbookService.addGuestbook(serviceContext.getUserId(),\n                name, serviceContext);\n\n        SessionMessages.add(request, \"guestbookAdded\");\n\n    } catch (Exception e) {\n        SessionErrors.add(request, e.getClass().getName());\n\n        response.setRenderParameter(\"mvcPath\",\n            \"/html/guestbook/edit_guestbook.jsp\");\n    }\n\n}\n```\n\nThis action method has one job: call a service to add a guestbook. If the call\nsucceeds, the message `\"guestbookAdded\"` is associated with the request and\nadded to the \n[`SessionMessages` object](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/servlet/SessionMessages.html).\nIf an exception is thrown, it's caught, and the class name is associated with\nthe request and added to the \n[`SessionErrors` object](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/servlet/SessionErrors.html),\nand the response is set to render `edit_guestbook.jsp`. Setting the `mvcPath`\nrender parameter is a Liferay `MVCPortlet` framework convention that denotes the\nnext view to render to the user. \n\nWhile action methods respond to user actions, render logic determines the view\nto display to the user. Render logic is next. \n\n## Render Logic\n\nHere's how MVC Portlet determines which view to render. Note the `init-param`\nproperties you set in your component:\n\n```java\n\"javax.portlet.init-param.template-path=/\",\n\"javax.portlet.init-param.view-template=/view.jsp\",\n```\n\nThe `template-path` property tells the MVC framework where your JSP files live.\nIn the above example, `/` means that the JSP files are in your project's root\n`resources` folder. That's why it's important to follow Liferay's standard\nfolder structure. The `view-template` property directs the default rendering to\n`view.jsp`.\n\nHere's the path of a hypothetical Web module's resource folder:\n\n```\ndocs.liferaymvc.web/src/main/resources/META-INF/resources\n```\n\nBased on that resource folder, the `view.jsp` file is found at\n\n```\ndocs.liferaymvc.web/src/main/resources/META-INF/resources/view.jsp\n```\n\nand that's the application's default view. When the portlet's `init` method\n(e.g., your portlet's override of `MVCPortlet.init()`) is called, Liferay reads the\ninitialization parameters you specify and directs rendering to the default JSP.\nThroughout the controller, you can render different views (JSP files) by setting\nthe render parameter `mvcPath` like this:\n\n```java\nactionResponse.setRenderParameter(\"mvcPath\", \"/error.jsp\");\n```\n\nYou can avoid render logic by using initialization parameters and render\nparameters, but most of the time you'll override the portlet's `render` method.\nHere's an example:\n\n```java\n@Override\npublic void render(RenderRequest renderRequest,\n        RenderResponse renderResponse) throws PortletException, IOException {\n\n    try {\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n                Guestbook.class.getName(), renderRequest);\n\n        long groupId = serviceContext.getScopeGroupId();\n\n        long guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n        List<Guestbook> guestbooks = _guestbookService\n                .getGuestbooks(groupId);\n\n        if (guestbooks.size() == 0) {\n            Guestbook guestbook = _guestbookService.addGuestbook(\n                    serviceContext.getUserId(), \"Main\", serviceContext);\n\n            guestbookId = guestbook.getGuestbookId();\n\n        }\n\n        if (!(guestbookId > 0)) {\n            guestbookId = guestbooks.get(0).getGuestbookId();\n        }\n\n        renderRequest.setAttribute(\"guestbookId\", guestbookId);\n\n    } catch (Exception e) {\n\n        throw new PortletException(e);\n    }\n\n    super.render(renderRequest, renderResponse);\n\n}\n```\n\nThis render logic provides the view layer with data to display to the user. The\n`render` method above sets the render request attribute `guestbookId` with the\nID of a guestbook to display. If guestbooks exist, it chooses the first.\nOtherwise, it creates a guestbook and sets it to display. Lastly the method\npasses the render request and render response objects to the base class via its\n`render` method. \n\n| **Note:** Are you wondering how to call\n| [Service Builder services](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n| in @product-ver@? In short, obtain a reference to the service by annotating\n| one of  your fields of that service type with the `@Reference`\n| [Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services)\n| annotation.\n| \n| `\n| @Reference\n| private GuestbookService _guestbookService;`\n| \n| Once done, you can call the service's methods.\n| \n| `_guestbookService.addGuestbook(serviceContext.getUserId(), \"Main\",\n|         serviceContext);`\n\nBefore venturing into the view layer, the next section demonstrates ways to pass\ninformation between the controller and view layers. \n\n## Setting and Retrieving Request and Response Parameters and Attributes\n\nA handy utility class called\n[`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html)\nfacilitates retrieving parameters from an [`ActionRequest`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/ActionRequest.html)\nor a\n[`RenderRequest`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/RenderRequest.html).\n\nFor example, this JSP passes a parameter named `guestbookId` in an action\nURL.\n\n```markup\n<portlet:actionURL name=\"doSomething\" var=\"doSomethingURL\">\n    <portlet:param name=\"guestbookId\" \n            value=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n</portlet:actionURL>\n```\n\nThe `<portlet:actionURL>` tag's `name` attribute maps the action URL to a\ncontroller action method named `doSomething`. Triggering an action URL invokes\nthe corresponding method in the controller. \n\nThe controller's `doSomething` method referenced in this example gets the\n`guestbookId` parameter value from the `ActionRequest`.\n\n```java\nlong guestbookId = ParamUtil.getLong(actionRequest, \"guestbookId\");\n```\n\nTo pass information back to the view layer, the controller code can set render\nparameters on response objects. \n\n```java\nactionResponse.setRenderParameter(\"mvcPath\", \"/error.jsp\");\n```\n\nThe code above sets a parameter called `mvcPath` to JSP path `/error.jsp`.\nThis causes the controller's render method to redirect the user to that JSP. \n\nYour controller class can also set attributes into response objects using the\n`setAttribute` method. \n\n```java\nrenderResponse.setAttribute(\"guestbookId\", guestbookId);\n```\n\nJSPs can use Java code in scriptlets to interact with the request object. \n\n```javascript\n<%\n    long guestbookId = Long.valueOf((Long) renderRequest\n            .getAttribute(\"guestbookId\"));\n%>\n```\n\nPassing information back and forth from your view and controller is important,\nbut there's more to the view layer than that. The view layer is up next. \n\n## Related Topics \n\n[Creating an MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/creating-an-mvc-portlet)\n\n[Configuring the View Layer](/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer)\n\n[MVC Action Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-action-command)\n\n[MVC Render Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-render-command)\n\n[MVC Resource Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-resource-command)\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/05-liferay-mvc-portlet/04-configuring-the-view-layer.markdown",
    "content": "---\nheader-id: configuring-the-view-layer\n---\n\n# Configuring the View Layer\n\n[TOC levels=1-4]\n\nThis section briefly covers how to get your view layer working, from organizing\nyour imports in one JSP file, to creating URLs that direct processing to\nmethods in your portlet class. \n\n| Note: As you create JSPs, you can\n| [apply Clay styles to your app to match Liferay's apps](/docs/7-1/tutorials/-/knowledge_base/t/applying-clay-styles-to-your-app).\n\n## Using the init.jsp \n\nLiferay's  practice puts all Java imports, tag library declarations, and\nvariable initializations into a JSP called `init.jsp`. If you use \n[Blade CLI](/docs/7-1/tutorials/-/knowledge_base/t/blade-cli) or \n[Liferay @ide@](/docs/7-1/tutorials/-/knowledge_base/t/liferay-ide)\nto create a module based on the `mvc-portlet` project template, these taglib\ndeclarations and initializations are added automatically to your `init.jsp`:\n\n```markup\n<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\" %>\n\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\" %>\n\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\" %>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\" %>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\" %>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\" %>\n\n<liferay-theme:defineObjects />\n\n<portlet:defineObjects />\n```\n\nHere are the tag libraries it gives you:\n\n-   [`c`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/c/tld-frame.html): JSTL core tags. \n-   [`portlet`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/portlet/tld-frame.html): Standard portlet component tags. \n-   [`aui`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/tld-summary.html): [AlloyUI](https://alloyui.com/) component tags.\n-   [`liferay-portlet`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/liferay-portlet/tld-frame.html): Liferay portlet component tags. \n-   [`liferay-theme`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/liferay-theme/tld-frame.html): Liferay theme component tags. \n-   [`liferay-ui`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/liferay-ui/tld-frame.html): Liferay UI component tags. \n\nThese tags make portlet and Liferay objects available:\n\n-   `<portlet:defineObjects />`: Implicit Java variables that reference \n    Portlet API objects. The objects available are limited to those available in\n    the current portlet request. For details, see the `defineObjects` tag in\n    [JSR-286](https://jcp.org/en/jsr/detail?id=286). \n\n-   `<liferay-theme:defineObjects />`: Implicit Java variables that \n    reference Liferay objects. \n\nTo use all that the `init.jsp` has, include it in your other JSPs:\n\n```markup\n<%@include file=\"/html/init.jsp\"%>\n```\n\nA JSP uses render URLs to display other pages and action URLs to invoke\ncontroller methods. \n\n## Using Render URLs\n\nA render URL attached to a UI component action displays another page. For\nexample, this render URL displays the JSP `/path/to/foo.jsp`. \n\n```markup\n<portlet:renderURL var=\"adminURL\">\n    <portlet:param name=\"mvcPath\" value=\"/path/to/foo.jsp\" />\n</portlet:renderURL>\n```\n\nHere's how to use a render URL:\n\n1.  Add a\n    [`<portlet:renderURL>`](@platform-ref@/7.2-latest/taglibs/util-taglib/portlet/renderURL.html)\n    to your JSP. \n\n2.  Name the render URL via a `var` attribute in the `<portlet:renderURL>` tag.\n    The `<portlet:renderURL>` tag constructs the URL and assigns it to the\n    variable. For example, this render URL is assigned to the variable named\n    `adminURL`: \n\n    ```markup\n    <portlet:renderURL var=\"adminURL\">\n       ...\n    </portlet:renderURL>\n    ```\n\n3.  As sub-element to the `<portlet:renderURL>` tag, add a \n    [`<portlet:param>`](@platform-ref@/7.2-latest/taglibs/util-taglib/portlet/param.html)\n    tag with the following attributes:\n\n    `name=\"mvcPath\"`: Your controller's `render` method forwards processing to\n    the JSP at the path specified in the `value`.\n\n    `value=\"/path/to/foo.jsp\"`: The path to the JSP to render. Replace the value\n    `/path/to/foo.jsp` with your JSP path. \n\n    ```markup\n    <portlet:renderURL var=\"adminURL\">\n        <portlet:param name=\"mvcPath\" value=\"/path/to/foo.jsp\" />\n    </portlet:renderURL>\n    ```\n\n4.  To invoke the render URL, assign its variable (`var`) to a UI component \n    action, such as a button or navigation bar item action. \n\nInvoking the UI component causes the controller's render method to display the\n`mvcPath` parameter's JSP. \n\n## Using Action URLs\n\nAction methods are different because they invoke an action (i.e., code), rather\nthan link to another page. For example, this action URL invokes a\ncontroller method called `doSomething` and passes a parameter called `redirect`.\nThe `redirect` parameter contains the path of the JSP to render after invoking\nthe action: \n\n```markup\n<portlet:actionURL name=\"doSomething\" var=\"doSomethingURL\">\n    <portlet:param name=\"redirect\" value=\"<%= redirect %>\" />\n</portlet:actionURL>\n```\n\nHere's how to use an action URL:\n\n1.  Add a\n    [`<portlet:actionURL>`](@platform-ref@/7.2-latest/taglibs/util-taglib/portlet/actionURL.html)\n    to your JSP. \n\n2.  Add a `name` and `var` attribute to the `<portlet:actionURL>`. The\n    `<portlet:actionURL>` tag constructs the URL and assigns it to the `var`\n    variable.\n\n    `name`: Controller action to invoke. \n\n    `var`: Variable to assign the action URL to. \n\n    ```markup\n    <portlet:actionURL name=\"doSomething\" var=\"doSomethingURL\">\n       ...\n    </portlet:actionURL>\n    ```\n\n3.  As sub-element to the `<portlet:actionURL>` tag, add a \n    [`<portlet:param>`](@platform-ref@/7.2-latest/taglibs/util-taglib/portlet/param.html)\n    tag that has the following attributes:\n\n    `name=\"redirect\"`: Tells the portlet to redirect to the JSP associated with \n    this parameter. \n\n    `value=\"/path/to/foo.jsp\"`: Redirects the user to this JSP path after\n    invoking the action. Replace the value `/path/to/bar.jsp` with your JSP\n    path. \n\n    ```markup\n    <portlet:actionURL name=\"doSomething\" var=\"doSomethingURL\">\n        <portlet:param name=\"redirect\" value=\"/path/to/bar.jsp\" />\n    </portlet:actionURL>\n    ```\n\n4.  To invoke the action URL, assign its variable (`var`) to a UI component \n    action, such as a button or navigation bar item action. \n\nCongratulations! Your portlet is ready for action. \n\nThese simple examples demonstrate how Liferay MVC Portlet facilitates\ncommunication between a smaller application's view layer and controller. \n\n## Related Topics \n\n[Writing MVC Portlet Controller Code](/docs/7-2/appdev/-/knowledge_base/a/writing-mvc-portlet-controller-code)\n\n[MVC Action Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-action-command)\n\n[MVC Render Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-render-command)\n\n[MVC Resource Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-resource-command)\n\n[Front-end Taglibs](/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs)\n\n[Liferay JavaScript APIs](https://portal.liferay.dev/docs/7-2/reference/-/knowledge_base/r/liferay-javascript-apis)\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/05-liferay-mvc-portlet/05-mvc-action-command.markdown",
    "content": "---\nheader-id: mvc-action-command\n---\n\n# MVC Action Command\n\n[TOC levels=1-4]\n\nLiferay's MVC Portlet framework enables you to handle\n[MVCPortlet](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet)\n[actions](/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer#using-action-urls)\nin separate classes. This facilitates managing action logic in portlets that\nhave many actions. Each action URL in your portlet's JSPs invokes an appropriate\naction command class. \n\nHere are the steps:\n\n1.  [Configure your JSPs](/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer)\n    to use action URLs via\n    [`<portlet:actionURL>`](@platform-ref@/7.2-latest/taglibs/util-taglib/portlet/actionURL.html)\n    tags. For example, the\n    [action-command-portlet](https://github.com/liferay/liferay-blade-samples/blob/7.1/gradle/apps/action-command-portlet/src/main/resources/META-INF/resources/view.jsp)\n    sample uses this action URL:\n\n    ```markup\n    <liferay-portlet:actionURL name=\"greet\" var=\"greetURL\" />\n    ```\n\n    Name the action URL via its `name` attribute. Your `*MVCActionCommand` class\n    maps to this name. Assign the `var` attribute a variable name. \n\n2.  Assign the action URL variable (`var`) to a UI component. Acting on the UI \n    component invokes the action. For  example, the sample's `greetURL` action\n    URL variable triggers on submitting this form: \n\n    ```markup\n    <aui:form action=\"<%= greetURL %>\" method=\"post\" name=\"fm\">\n    \t<aui:input name=\"name\" type=\"text\" />\n\n    \t<aui:button-row>\n    \t\t<aui:button type=\"submit\"></aui:button>\n    \t</aui:button-row>\n    </aui:form>\n    ```\n\n3.  Create a class that implements the\n    [`MVCActionCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCActionCommand.html)\n    interface, or that extends the\n    [`BaseMVCActionCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/BaseMVCActionCommand.html)\n    class. The latter may save you time, since it already implements\n    `MVCActionCommand`. \n\n    | **Tip:** Naming your `*MVCActionCommand` class after the action it \n    | performs makes the action mappings more obvious for maintaining the code.\n    | For example, if your action class edits some kind of entry, you could name\n    | its class `EditEntryMVCActionCommand`. If your application has several MVC\n    | command classes, naming them this way helps differentiate them. \n\n4.  Annotate your class with an\n    [`@Component`](https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html)\n    annotation, like this one:\n\n    ```java\n    @Component(\n        property = {\n            \"javax.portlet.name=your_portlet_name_YourPortlet\",\n            \"mvc.command.name=/your/jsp/action/url\"\n        },\n        service = MVCActionCommand.class\n    )\n    ```\n\n5.  Set a `javax.portlet.name` property to your portlet's internal ID. \n\n    Note, you can apply MVC Command classes to multiple portlets by setting\n    a `javax.portlet.name` property for each portlet. For example, the\n    `javax.portlet.name` properties in this component apply it to three\n    specific portlets. \n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS,\n            \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_ADMIN,\n            \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_AGGREGATOR,\n            \"mvc.command.name=/blogs/edit_entry\"\n        },\n        service = MVCActionCommand.class\n    )\n    public class EditEntryMVCActionCommand extends BaseMVCActionCommand {\n        ...\n    }\n    ```\n\n6.  Set the `mvc.command.name` property to your `<portlet:actionURL>` tag's \n    `name`. This maps your class to the action URL of the same name. \n\n7.  Register your class as an `MVCActionCommand` service by setting the\n    `service` attribute to `MVCActionCommand.class`. \n\n8.  Implement your action logic by overriding the appropriate method of the \n    class you're implementing or extending. \n\n    -   [`MVCActionCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCActionCommand.html)\n       implementations override the `processAction` method. \n\n    -   [`BaseMVCActionCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/BaseMVCActionCommand.html)\n        extensions override the `doProcessAction` method. \n\n    Here's an example of overriding `MVCActionCommand`'s `processAction` method.\n    This action logic gets the name parameter from the `ActionRequest` and adds\n    it to the session messages and to an `ActionRequest` attribute. \n\n    ```java\n    @Override\n    public boolean processAction(\n    \t\tActionRequest actionRequest, ActionResponse actionResponse)\n    \tthrows PortletException {\n\n    \t_handleActionCommand(actionRequest);\n\n    \treturn true;\n    }\n\n    private void _handleActionCommand(ActionRequest actionRequest) {\n    \tString name = ParamUtil.get(actionRequest, \"name\", StringPool.BLANK);\n\n    \tif (_log.isInfoEnabled()) {\n    \t\t_log.info(\"Hello \" + name);\n    \t}\n\n    \tString greetingMessage = \"Hello \" + name + \"! Welcome to OSGi\";\n\n    \tactionRequest.setAttribute(\"GREETER_MESSAGE\", greetingMessage);\n\n    \tSessionMessages.add(actionRequest, \"greetingMessage\", greetingMessage);\n    }\n\n    private static final Log _log = LogFactoryUtil.getLog(\n    \tGreeterActionCommand.class);\n    ```\n\nCongratulations! You've created an `MVCActionCommand` that handles your portlet\nactions. \n\n## Related Topics\n\n[Creating an MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/creating-an-mvc-portlet)\n\n[Configuring the View Layer](/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer)\n\n[MVC Render Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-render-command)\n\n[MVC Resource Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-resource-command)\n\n[MVC Command Overrides](/docs/7-2/customization/-/knowledge_base/c/overriding-liferay-mvc-commands)\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/05-liferay-mvc-portlet/06-mvc-render-command.markdown",
    "content": "---\nheader-id: mvc-render-command\n---\n\n# MVC Render Command\n\n[TOC levels=1-4]\n\n`MVCRenderCommand`s are classes that respond to\n[MVCPortlet](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet) [render\nURLs](/docs/7-2/appdev/-/knowledge_base/a/writing-mvc-portlet-controller-code#render-logic).\nIf your render logic is simple and you want to implement all of your render\nlogic in your portlet class, see [Writing MVC Portlet Controller\nCode](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet). If your render\nlogic is complex or you want clean separation between render paths, use\n`MVCRenderCommand`s. Each render URL in your portlet's JSPs invokes an\nappropriate render command class. \n\nHere are the steps:\n\n1.  [Configure your JSPs](/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer)\n    to generate render URLs via\n    [`<portlet:renderURL>`](@platform-ref@/7.2-latest/taglibs/util-taglib/portlet/renderURL.html)\n    tags. \n\n    For example, this\n    [render-command-portlet](https://github.com/liferay/liferay-blade-samples/tree/7.1/gradle/apps/render-command-portlet)\n    sample render URL invokes an MVC render command named `/blade/render`. \n\n    ```markup\n    <portlet:renderURL var=\"bladeRender\">\n    \t<portlet:param name=\"mvcRenderCommandName\" value=\"/blade/render\" />\n    </portlet:renderURL>\n    ```\n\n2.  Name the render URL via its `<portlet:param>` named \n    `mvcRenderCommandName`. The render URL and `*MVCRenderCommand` class\n    (demonstrated later) map to the `mvcRenderCommandName` value. \n\n3.  Assign the `<portlet:renderURL>`'s `var` attribute a variable name to \n    pass to a UI component. \n\n4.  Assign the render URL variable (`var`) to a UI component. When the user \n    triggers the UI component, the `*MVCRenderCommand` class that matches the\n    render URL handles the render request.\n\n    For example, the render URL with the variable `bladeRender` triggers on users clicking this button.\n\n    ```markup\n    <aui:button href=\"<%= bladeRender %>\" value=\"goto page render\" />\n    ```\n\n5.  Create a class that implements the \n    [`MVCRenderCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html)\n    interface. \n\n6.  Annotate the class with an\n    [`@Component`](https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html)\n    annotation, like this one:\n\n    ```java\n    @Component(\n    \tproperty = {\n    \t\t\"javax.portlet.name=com_liferay_blade_samples_portlet_rendercommand_BladeRenderPortlet\",\n    \t\t\"mvc.command.name=/blade/render\"\n    \t},\n    \tservice = MVCRenderCommand.class\n    )\n    ```\n\n7.  Set a `javax.portlet.name` property to your portlet's internal ID. \n\n8.  Set a `mvc.command.name` property to your `<portlet:renderURL>` tag \n    `mvcRenderCommandName` portlet parameter value. This maps your class\n    to the render URL. \n\n9.  Register your class as an `MVCRenderCommand` service by setting the\n    `service` attribute to `MVCRenderCommand.class`. \n\n    Note, you can apply MVC Command classes to multiple portlets by setting\n    a `javax.portlet.name` property for each portlet and apply MVC Command\n    classes to  multiple command names by setting an `mvc.command.name`\n    property for each command name. For example, this component's\n    `javax.portlet.name` properties and `mvc.command.name` properties apply\n    it to two specific portlets and two specific command names. \n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n           \"javax.portlet.name=\" + HelloWorldPortletKeys.HELLO_MY_WORLD,\n           \"javax.portlet.name=\" + HelloWorldPortletKeys.HELLO_WORLD,\n           \"mvc.command.name=/hello/edit_super_entry\",\n           \"mvc.command.name=/hello/edit_entry\"\n        },\n        service = MVCRenderCommand.class\n    )\n    ```\n\n10. Implement your render logic in a method that overrides\n    [`MVCRenderCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html)'s\n    `render` method. Some `*MVCRenderCommand`s, such as the one below, always\n    render the same JSP.\n\n    ```java\n    public class BlogsViewMVCRenderCommand implements MVCRenderCommand {\n\n        @Override\n        public String render(\n            RenderRequest renderRequest, RenderResponse renderResponse) {\n\n            return \"/blogs/view.jsp\";\n        }\n\n    }\n    ```\n\nAs you can see, MVC render commands are easy to implement and can respond to\nmultiple command names for multiple portlets. \n\n## Related Topics\n\n[Creating an MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/creating-an-mvc-portlet)\n\n[Configuring the View Layer](/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer)\n\n[MVC Resource Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-resource-command)\n\n[MVC Action Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-action-command)\n\n[MVC Command Overrides](/docs/7-2/customization/-/knowledge_base/c/overriding-liferay-mvc-commands)\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/05-liferay-mvc-portlet/07-mvc-resource-command.markdown",
    "content": "---\nheader-id: mvc-resource-command\n---\n\n# MVC Resource Command\n\n[TOC levels=1-4]\n\nWhen using Liferay's [MVCPortlet\nframework](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet), you can\ncreate resource URLs in your JSPs to retrieve images, XML, or any other kind of\nresource from a @product@ instance. The resource URL then invokes the\ncorresponding MVC resource command class (`*MVCResourceCommand`) that processes\nthe resource request and response. \n\nHere how to create your own MVC Resource Command:\n\n1.  [Configure your JSPs](/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer)\n    to generate resource URLs via\n    [`<portlet:resourceURL>`](@platform-ref@/7.2-latest/taglibs/util-taglib/portlet/resourceURL.html)\n    tags. \n\n    For example, this\n    [resource-command-portlet](https://github.com/liferay/liferay-blade-samples/tree/7.1/gradle/apps/resource-command-portlet)\n    sample resource URL invokes an MVC resource command named `/blade/captcha`.\n\n    ```markup\n    <portlet:resourceURL id=\"/blade/captcha\" var=\"captchaURL\" />\n    ```\n\n2.  Name the resource URL via its `id` attribute. \n\n3.  Assign the resource URL's `var` attribute a variable name to pass to a \n    UI component. \n\n4.  Assign the resource URL variable (`var`) to a UI component, such as a button\n    or icon. When the user triggers the UI component, the `*MVCResourceCommand`\n    class that matches the resource URL handles the resource request.\n\n    For example, the sample's resource URL is triggered when the user clicks on\n    this\n    [`liferay-captcha`](@app-ref@/foundation/latest/taglibdocs/liferay-captcha/captcha.html)\n    component:\n\n    ```markup\n    <liferay-captcha:captcha url=\"<%= captchaURL %>\" />\n    ```\n\n5.  Create a class that implements the\n    [`MVCResourceCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html)\n    interface, or that extends the\n    [`BaseMVCResourceCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/BaseMVCResourceCommand.html)\n    class. The latter may save you time, since it already implements\n    `MVCResourceCommand`. \n\n    | **Tip:** Naming your `*MVCResourceCommand` class after the resource it \n    | provides makes the resource mappings more obvious for maintaining the\n    | code. For example, if your resource URL serves a captcha, you could name\n    | its class `CaptchaMVCResourceCommand`. If your application has several MVC\n    | command classes, naming them this way helps differentiate them. \n\n6.  Annotate your class with an\n    [`@Component`](https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html)\n    annotation, like this one:\n\n    ```java\n    @Component(\n        property = {\n            \"javax.portlet.name=your_portlet_name_YourPortlet\",\n            \"mvc.command.name=/your/jsp/resource/url\"\n        },\n        service = MVCResourceCommand.class\n    )\n    public class YourMVCResourceCommand extends BaseMVCResourceCommand {\n        // your resource handling code\n    }\n    ```\n\n    1.  Set a `javax.portlet.name` property to your portlet's internal ID. \n \n    2.  Set the `mvc.command.name` property to your `<portlet:resourceURL>` \n        tag's `id`. This maps your class to the resource URL of the same name. \n\n    3.  Register your class as an `MVCResourceCommand` service by setting the\n        `service` attribute to `MVCResourceCommand.class`. \n\n\n7.  Implement your resource logic by overriding the appropriate method of the \n    class you're implementing or extending. \n\n    -   [`MVCResourceCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html)\n       implementations override the `serveResource` method. \n\n    -   [`BaseMVCResourceCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/BaseMVCResourceCommand.html)\n        extensions override the `doServeResource` method. \n\nFor example, the\n[resource-command-portlet](https://github.com/liferay/liferay-blade-samples/tree/7.1/gradle/apps/resource-command-portlet)'s\n`CaptchaMVCResourceCommand` class implements the `MVCResourceCommand` interface\nwith only a single method: `serveResource`. \n\n```java\n@Component(\n\timmediate = true,\n\tproperty = {\n\t\t\"javax.portlet.name=com_liferay_blade_samples_portlet_resourcecommand_CaptchaPortlet\",\n\t\t\"mvc.command.name=/blade/captcha\"\n\t},\n\tservice = MVCResourceCommand.class\n)\npublic class CaptchaMVCResourceCommand implements MVCResourceCommand {\n\n\t@Override\n\tpublic boolean serveResource(\n\t\t\tResourceRequest resourceRequest, ResourceResponse resourceResponse)\n\t\tthrows PortletException {\n\n\t\tif (_log.isInfoEnabled()) {\n\t\t\t_log.info(\"get captcha resource \");\n\t\t}\n\n\t\ttry {\n\t\t\tCaptchaUtil.serveImage(resourceRequest, resourceResponse);\n\n\t\t\treturn false;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\t_log.error(e.getMessage(), e);\n\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tCaptchaMVCResourceCommand.class);\n\n}\n```\n\nThis `serveResource` method processes the resource request and response via the\n`javax.portlet.ResourceRequest` and `javax.portlet.ResourceResponse` parameters,\nrespectively. Note that the `try` block uses the helper class\n[`CaptchaUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/captcha/CaptchaUtil.html)\nto serve the CAPTCHA image. Though you don't have to create such a helper class,\ndoing so often simplifies your code. \n\nGreat! Now you know how to use `MVCResourceCommand` to process resources in your\nLiferay MVC Portlets. \n\n## Related Topics\n\n[Creating an MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/creating-an-mvc-portlet)\n\n[Configuring the View Layer](/docs/7-2/appdev/-/knowledge_base/a/configuring-the-view-layer)\n\n[MVC Render Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-render-command)\n\n[MVC Action Command](/docs/7-2/appdev/-/knowledge_base/a/mvc-action-command)\n\n[MVC Command Overrides](/docs/7-2/customization/-/knowledge_base/c/overriding-liferay-mvc-commands)\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/06-portletmvc4pring/01-portlet-mvc-for-spring-intro.markdown",
    "content": "---\nheader-id: portletmvc4spring\n---\n\n# PortletMVC4Spring\n\n[TOC levels=1-4]\n\nPortletMVC4Spring is a way to develop portlets using the Spring Framework and\nthe Model View Controller (MVC) pattern. While the Spring Framework supports\ndeveloping *servlet-based* web applications using\n[Spring Web MVC](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html),\nPortletMVC4Spring supports developing *portlet-based* applications using MVC.\nYou can build \n[Spring Framework](https://spring.io/projects/spring-framework)\n@product@ portlets with features like these:\n\n-   Inversion of control (IoC) / dependency injection (DI)\n-   Annotations\n-   Security\n-   Binding and validation\n-   Multi-part file upload\n-   ... and more\n\nYou'll learn these things about PortletMVC4Spring: \n\n-   **Developing a Portlet Using PortletMVC4Spring:** Demonstrates creating and \n    deploying a portlet using PortletMVC4Spring. \n\n-   **Annotation-based Controller Development:** Shows how to implement \n    controllers using plain old Java objects (POJOs) and annotations. \n\n| **Background:** The PortletMVC4Spring project began as Spring Portlet MVC and \n| was part of the\n| [Spring Framework](https://spring.io/projects/spring-framework).\n| When the project was pruned from version 5.0.x of the Spring Framework under\n| [SPR-14129](https://github.com/spring-projects/spring-framework/issues/18701),\n| it became necessary to fork and rename the project. This made it possible to\n| improve and maintain the project for compatibility with the latest versions of\n| the Spring Framework and the Portlet API.\n|\n| [Liferay](http://www.liferay.com)\n| adopted Spring Portlet MVC in March of 2019 and the project was renamed to\n| **PortletMVC4Spring**.\n\nIf you're familiar with Spring Web MVC, it's helpful to compare it with\nPortletMVC4Spring. Portlet workflow differs from servlet workflow because\na request to the portlet can have two distinct phases: the `ACTION_PHASE` and\nthe `RENDER_PHASE`. The `ACTION_PHASE` is executed only once and is where any\nback-end changes or actions occur, such as making changes in a database. The\n`RENDER_PHASE` presents the portlet's content to the user each time the display\nis refreshed. Thus for a single request, the `ACTION_PHASE` is executed only\nonce, but the `RENDER_PHASE` may be executed multiple times. This provides (and\nrequires) a clean separation between the activities that modify the system's\npersistent state and the activities that generate content. The Portlet 2.0\nSpecification added two more phases: The event phase and the resource phase,\nboth of which are supported by annotation-driven dispatching. \n\nPortletMVC4Spring provides annotations that support requests from the render,\naction, event, and resource serving portlet phases; Spring Web MVC provides only\na `@RequestMapping` annotation. Where a Spring Web MVC controller might have\na single handler method annotated with `@RequestMapping`, an equivalent\nPortletMVC4Spring controller might have multiple handler methods, each using\none of the phase annotations: `@ActionMapping`, `@EventMapping`,\n`@RenderMapping`, or `@ResourceMapping`.\n\nThe PortletMVC4Spring framework uses a `DispatcherPortlet` that dispatches\nrequests to handlers, with configurable handler mappings and view resolution,\njust as the `DispatcherServlet` in the web framework does. \n\n| **Note:** For more information on portlets, portlet specifications, and how \n| portlets differ from servlets, see\n| [Portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets). \n\nLiferay also provides full-featured sample portlets that demonstrate using JSP\nand\n[Thymeleaf](https://www.thymeleaf.org)\nview templates. They exercise many features that form-based\nportlet applications typically require. \n\n![Figure 1: This PortletMVC4Spring portlet enables users to enter job applications. It uses the Spring features mentioned above and handles requests from multiple portlet phases.](../../../images/portletmvc4spring-applicant-jsp-app.png)\n\nThe samples are available here: \n\n| Source Code   | Maven Central |\n| ------------- | ------------- |\n| [applicant-jsp-portlet](https://github.com/liferay/portletmvc4spring/tree/master/demo/applicant-jsp-portlet)  |  [com.liferay.portletmvc4spring.demo.applicant.jsp.portlet.war](https://search.maven.org/search?q=a:com.liferay.portletmvc4spring.demo.applicant.jsp.portlet) |\n| [applicant-thymeleaf-portlet](https://github.com/liferay/portletmvc4spring/tree/master/demo/applicant-thymeleaf-portlet)  |  [com.liferay.portletmvc4spring.demo.applicant.thymeleaf.portlet.war](https://search.maven.org/search?q=a:com.liferay.portletmvc4spring.demo.applicant.thymeleaf.portlet) | \n\nNow that you have a basic understanding of PortletMVC4Spring portlets and how\nthey compare to Spring Web MVC applications, it's time to develop a\nPortletMVC4Spring portlet. \n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/06-portletmvc4pring/02-developing-a-portlet-using-portletmvc4spring.markdown",
    "content": "---\nheader-id: developing-a-portlet-using-portletmvc4spring\n---\n\n# Developing a Portlet Using PortletMVC4Spring\n\n[TOC levels=1-4]\n\nPortletMVC4Spring compliments the Spring Web framework and MVC design pattern by\nproviding annotations that map portlet requests to Controller classes and\nmethods. Here you'll develop and deploy a portlet application that uses\nPortletMVC4Spring, Spring, and JSP/JSPX templates. \n\n![Figure 1: The archetype's sample portlet prints a greeting (e.g., *Hello, Joe Bloggs*) on submitting a first and last name.](../../../images/portletmvc4Spring-developing.png)\n\nFollow these steps to create and deploy your portlet application: \n\n1.  Create a PortletMVC4Spring project. See\n    [PortletMVC4Spring Project Anatomy](/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-project-anatomy)\n    for the project structure and commands for generating PortletMVC4Spring\n    projects. Here's the Maven command for generating the JSP/JSPX project.\n    This article lists sample code from the generated project. \n\n    ```bash\n    mvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay.portletmvc4spring.archetype \\\n    -DarchetypeArtifactId=com.liferay.portletmvc4spring.archetype.form.jsp.portlet \\\n    -DarchetypeVersion=5.1.0 \\ \n    -DgroupId=com.mycompany \\ \n    -DartifactId=com.mycompany.my.form.jsp.portlet\n    ```\n\n2.  In your project's Gradle build file or Maven POM, add your app's\n    dependencies. Here are the PortletMVC4Spring development dependencies:\n    \n    **Gradle:**\n\n    ```groovy\n    compile group: 'com.liferay.portletmvc4spring', name: 'com.liferay.portletmvc4spring.framework', version: '5.1.0'\n    compile group: 'com.liferay.portletmvc4spring', name: 'com.liferay.portletmvc4spring.security', version: '5.1.0'\n    providedCompile group: 'javax.portlet', name: 'portlet-api', version: '3.0.0'\n    ```\n\n    **Maven:**\n\n    ```xml\n    <dependency>\n        <groupId>com.liferay.portletmvc4spring</groupId>\n        <artifactId>com.liferay.portletmvc4spring.framework</artifactId>\n        <version>5.1.0</version>\n    </dependency>\n    <dependency>\n        <groupId>com.liferay.portletmvc4spring</groupId>\n        <artifactId>com.liferay.portletmvc4spring.security</artifactId>\n        <version>5.1.0</version>\n    </dependency>\n    <dependency>\n        <groupId>javax.portlet</groupId>\n        <artifactId>portlet-api</artifactId>\n        <version>3.0.0</version>\n        <scope>provided</scope>\n    </dependency>\n    ```\n\n    At this point you can develop your Model, View, or Controller components, in\n    any order. \n\n3.  Create your Model class(es) in a package for models (e.g.,\n    `java/[my-package-path]/dto`). The sample Model class is `User`. \n\n    ```java \n    public class User implements Serializable {\n\n    \tprivate static final long serialVersionUID = 1234273427623725552L;\n\n    \t@NotBlank\n    \tprivate String firstName;\n\n    \t@NotBlank\n    \tprivate String lastName;\n\n    \tpublic String getFirstName() {\n    \t\treturn firstName;\n    \t}\n\n    \tpublic void setFirstName(String firstName) {\n    \t\tthis.firstName = firstName;\n    \t}\n\n    \tpublic String getLastName() {\n    \t\treturn lastName;\n    \t}\n\n    \tpublic void setLastName(String lastName) {\n    \t\tthis.lastName = lastName;\n    \t}\n    }\n    ```\n\n4.  Create your View using a Spring Web-supported template type. If you didn't \n    generate your project using the archetype mentioned above, specify a View\n    resolver for template type in your\n    `spring-context/portlet-application-context.xml` portlet application\n    context. (See\n    [PortletMVC4Spring Configuration Files](/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-configuration-files)\n    for details). \n\n    The sample `user.jspx` template renders a form for submitting a user's first\n    and last name. \n    \n    ```xml\n    <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n    <jsp:root xmlns:jsp=\"http://java.sun.com/JSP/Page\"\n    \t\t  xmlns:portlet=\"http://xmlns.jcp.org/portlet_3_0\"\n    \t\t  xmlns:spring=\"http://www.springframework.org/tags\"\n    \t\t  xmlns:form=\"http://www.springframework.org/tags/form\"\n    \t\t  version=\"2.1\">\n    \t<jsp:directive.page contentType=\"text/html\" pageEncoding=\"UTF-8\" />\n    \t<portlet:defineObjects/>\n    \t<link href=\"${contextPath}/resources/css/main.css\" rel=\"stylesheet\" type=\"text/css\"/>\n    \t<portlet:actionURL var=\"mainFormActionURL\"/>\n    \t<form:form id=\"${namespace}mainForm\" action=\"${mainFormActionURL}\" class=\"user-form\" method=\"post\" modelAttribute=\"user\">\n    \t\t<p class=\"caption\">\n    \t\t\t<spring:message code=\"personal-information\" />\n    \t\t</p>\n    \t\t<fieldset>\n    \t\t\t<div class=\"form-group\">\n    \t\t\t\t<form:label for=\"${namespace}firstName\" path=\"firstName\">\n    \t\t\t\t\t<spring:message code=\"first-name\" />\n    \t\t\t\t</form:label>\n    \t\t\t\t<form:input id=\"${namespace}firstName\" cssClass=\"form-control\" path=\"firstName\"/>\n    \t\t\t\t<form:errors path=\"firstName\" cssClass=\"portlet-msg-error\"/>\n    \t\t\t</div>\n    \t\t\t<div class=\"form-group\">\n    \t\t\t\t<form:label for=\"${namespace}lastName\" path=\"lastName\">\n    \t\t\t\t\t<spring:message code=\"last-name\" />\n    \t\t\t\t</form:label>\n    \t\t\t\t<form:input id=\"${namespace}lastName\" cssClass=\"form-control\" path=\"lastName\"/>\n    \t\t\t\t<form:errors path=\"lastName\" cssClass=\"portlet-msg-error\"/>\n    \t\t\t</div>\n    \t\t</fieldset>\n    \t\t<hr />\n    \t\t<spring:message code=\"submit\" var=\"submit\" />\n    \t\t<input class=\"btn btn-primary\" value=\"${submit}\" type=\"submit\"/>\n    \t</form:form>\n    </jsp:root>\n    ``` \n\n    To invoke actions in your Controller, associate action URLs with your\n    templates. The sample template associates the action URL variable\n    `mainFormActionURL` with its form element. \n    \n    ```xml \n    <portlet:actionURL var=\"mainFormActionURL\"/>\n    <form:form id=\"${namespace}mainForm\" action=\"${mainFormActionURL}\" class=\"user-form\" method=\"post\" modelAttribute=\"user\">\n        ...\n    ```\n\n    A `<form:form/>` element's `modelAttribute` attribute targets an application\n    Model. The sample template targets the application's `user` Model. \n\n5.  Style your portlet by adding CSS to a stylesheet (e.g.,\n    `webapp/resources/css/main.css`) and linking your template to it. \n\n    ```xml \n    <link href=\"${contextPath}/resources/css/main.css\" rel=\"stylesheet\" type=\"text/css\"/>\n    ```\n\n6.  Define your portlet's messages in a properties file (e.g.,\n    `src/main/resources/content/[portlet].properties`). The sample `user.jspx`\n    template references some of these properties: \n\n    ```properties \n    first-name=First Name\n    greetings=Greetings, {0} {1}!\n    javax.portlet.display-name=com.mycompany.my.form.jsp.portlet\n    javax.portlet.keywords=com.mycompany.my.form.jsp.portlet\n    javax.portlet.short-title=com.mycompany.my.form.jsp.portlet\n    javax.portlet.title=com.mycompany.my.form.jsp.portlet\n    last-name=Last Name\n    personal-information=Personal Information\n    submit=Submit\n    todays-date-is=Today''s date is {0}\n    ```\n\n7.  Create a Controller class to handle portlet requests. Here's an example:\n\n    ```java \n    @Controller\n    @RequestMapping(\"VIEW\")\n    public class MyController {\n        ...\n    }\n    ```\n\n    The\n    [`@Controller`](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Controller.html)\n    annotation applies the Spring Controller component stereotype. The Spring\n    Framework scans Controller classes for Controller annotations. \n\n    The\n    [`@RequestMapping(\"VIEW\")`](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html)\n    annotation marks the class's public methods as request handler methods for\n    the portlet's\n    [VIEW mode](/docs/7-2/frameworks/-/knowledge_base/f/portlets). \n\n8.  In your Controller, apply `@RenderMapping` annotations to methods for \n    handling portlet render requests. Import the annotation\n    `com.liferay.portletmvc4spring.bind.annotation.RenderMapping` and make sure\n    each handler method returns a string that matches the name of a template to\n    render. Here are the sample's render request handler methods:\n\n    ```java\n    @RenderMapping\n\tpublic String prepareView() {\n\t\treturn \"user\";\n\t}\n\n\t@RenderMapping(params = \"javax.portlet.action=success\")\n\tpublic String showGreeting(ModelMap modelMap) {\n\n\t\tDateFormat dateFormat = new SimpleDateFormat(\"EEEE, MMMM d, yyyy G\");\n\n\t\tCalendar todayCalendar = Calendar.getInstance();\n\n\t\tmodelMap.put(\"todaysDate\", dateFormat.format(todayCalendar.getTime()));\n\n\t\treturn \"greeting\";\n\t}\n    ```\n\n    The `@RenderMapping` annotation causes the `prepareView` method above to be\n    invoked if no other handler methods match the request. `prepareView` renders\n    the `user` template (i.e., `user.jspx`). \n\n    The `@RenderMapping(params = \"javax.portlet.action=success\")` annotation\n    causes the `showGreeting` method to be invoked if the render request has the\n    parameter setting `javax.portlet.action=success`. `showGreeting` renders the\n    `greeting` template (i.e., `greeting.jspx`). \n\n6.  In your Controller, apply `@ActionMapping` annotations to your portlet \n    action request handling methods. Import the annotation\n    `com.liferay.portletmvc4spring.bind.annotation.ActionMapping`. \n\n    The sample Controller's action handler method below is annotated with\n    `@ActionMapping`, making it the default action handler if no other action\n    handlers match the request. Since this portlet only has one action handler,\n    the `submitApplicant` method handles all of the portlet's action requests. \n\n    ```java \n    @ActionMapping\n    public void submitApplicant(@ModelAttribute(\"user\") User user, BindingResult bindingResult, ModelMap modelMap,\n                                Locale locale, ActionResponse actionResponse, SessionStatus sessionStatus) {\n\n        localValidatorFactoryBean.validate(user, bindingResult);\n\n        if (!bindingResult.hasErrors()) {\n\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"firstName=\" + user.getFirstName());\n                logger.debug(\"lastName=\" + user.getLastName());\n            }\n\n            MutableRenderParameters mutableRenderParameters = actionResponse.getRenderParameters();\n\n            mutableRenderParameters.setValue(\"javax.portlet.action\", \"success\");\n\n            sessionStatus.setComplete();\n        }\n    }\n    ```\n\n    The [`@ModelAttribute`](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html)\n    annotation in method parameter `@ModelAttribute(\"user\") User user`\n    associates the View's `user` Model (comprising a first name and last name)\n    to the `User` object passed to this method. \n\n    Note, the `submitApplicant` method sets the `javax.portlet.action` render\n    parameter to `success`---the previous render handler method `showGreeting`\n    matches this request parameter. \n\n7.  Configure any additional resources and beans in the project's \n    descriptors and Spring context files respectively. (See\n    [PortletMVC4Spring Configuration Files](/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-configuration-files)\n    for details).\n\n8.  Build the project WAR using Gradle or Maven. \n\n9.  Deploy the WAR by copying it to your `[Liferay-Home]/deploy` folder. \n\n@product@ logs the deployment and the portlet is now available in the @product@\nUI. Find your portlet by selecting the *Add* icon\n(![Add](../../../images/icon-add-app.png))\nand navigating to *Widgets* and the category you specified to the Liferay Bundle\nGenerator (*Sample* is the default category). \n\nCongratulations! You created and deployed a PortletMVC4Spring Portlet. \n\n## Related Topics \n\n[PortletMVC4Spring Project Anatomy](/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-project-anatomy)\n\n[PortletMVC4Spring Annotations](/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-annotations)\n\n[PortletMVC4Spring Configuration Files](/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-configuration-files)\n\n[Migrating to PortletMVC4Spring](/docs/7-2/appdev/-/knowledge_base/a/migrating-to-portletmvc4spring)\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/06-portletmvc4pring/99-migrating-to-portletmvc4spring.markdown",
    "content": "---\nheader-id: migrating-to-portletmvc4spring\n---\n\n# Migrating to PortletMVC4Spring\n\n[TOC levels=1-4]\n\nTo continue developing a portlet to use Spring Framework version 5.0 onward,\nmigrate it from Spring Portlet MVC to\n[PortletMVC4Spring](/docs/7-2/appdev/-/knowledge_base/a/portletmvc4spring). Here\nare the steps: \n\n1.  In your `pom.xml` or `build.gradle` descriptor, use the Spring Framework \n    version 5.1.x artifacts by replacing dependencies on the\n    `spring-webmvc-portlet` artifact with the\n    `com.liferay.portletmvc4spring.framework` artifact. \n\n    Maven:\n\n    ```xml\n    <dependency>\n        <groupId>com.liferay.portletmvc4spring</groupId>\n        <artifactId>com.liferay.portletmvc4spring.framework</artifactId>\n        <version>5.1.0</version>\t\n    </dependency>\n    <dependency>\n        <groupId>com.liferay.portletmvc4spring</groupId>\n        <artifactId>com.liferay.portletmvc4spring.security</artifactId>\n        <version>5.1.0</version>\t\n    </dependency>\n    ```\n\n    Gradle:\n\n    ```groovy\n    compile group: 'com.liferay.portletmvc4spring', name: 'com.liferay.portletmvc4spring.framework', version: '5.1.0'\n\tcompile group: 'com.liferay.portletmvc4spring', name: 'com.liferay.portletmvc4spring.security', version: '5.1.0'\n    ```\n\n2.  In your `WEB-INF/portlet.xml` descriptor, replace uses of\n    `org.springframework.web.portlet.DispatcherPortlet` with \n    [`com.liferay.portletmvc4spring.DispatcherPortlet`](https://liferay.github.io/portletmvc4spring/apidocs/com/liferay/portletmvc4spring/DispatcherPortlet.html). \n\n3.  Replace uses of the Spring Portlet MVC\n    [`AnnotationMethodHandlerAdapter`](https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.html)\n    class with the PortletMVC4Spring\n    [`PortletRequestMappingHandlerAdapter`](https://liferay.github.io/portletmvc4spring/apidocs/com/liferay/portletmvc4spring/mvc/method/annotation/PortletRequestMappingHandlerAdapter.html)\n    class. `PortletRequestMappingHandlerAdapter` uses the `HandlerMethod` infrastructure that [Spring Web MVC\n    5.1.x](https://docs.spring.io/spring/docs/5.1.x/spring-framework-reference/web.html#spring-web)\n    is based on. \n\n4.  If you specified `AnnotationMethodHandlerAdapter` as a `<bean>` in a Spring\n    configuration descriptor, replace its fully-qualified class name \n    `org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter`\n    with\n    `com.liferay.portletmvc4spring.mvc.method.annotation.PortletRequestMappingHandlerAdapter`. \n\n    Also address these bean property changes:\n \n    -   [`customModelAndViewResolver`](https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.html#setCustomModelAndViewResolver-org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver-)\n        (no longer available)\n\n    -   [`customArgumentResolver`](https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.html#setCustomArgumentResolver-org.springframework.web.bind.support.WebArgumentResolver-)\n        (no longer available)\n\n    -   [`customArgumentResolvers`](https://liferay.github.io/portletmvc4spring/apidocs/com/liferay/portletmvc4spring/mvc/method/annotation/PortletRequestMappingHandlerAdapter.html#setCustomArgumentResolvers-java.util.List-)\n        (specify a list of\n        [`HandlerMethodArgumentResolver`](https://docs.spring.io/spring/docs/5.1.4.RELEASE/javadoc-api/org/springframework/web/method/support/HandlerMethodArgumentResolver.html)\n        instead of a list of\n        [`WebArgumentResolver`](https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/bind/support/WebArgumentResolver.html))\n \n5.  If you're using [Apache Commons\n    Fileupload](https://commons.apache.org/proper/commons-fileupload/), \n    update your Spring configuration descriptor:\n\n    1.  Replace this legacy bean:\n\n        ```xml\n        <bean id=\"portletMultipartResolver\"\n        \tclass=\"org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver\" />\n        ```\n\n        With this new one from PortletMVC4Spring:\n\n        ```xml\n        <bean id=\"portletMultipartResolver\"\n        \tclass=\"com.liferay.portletmvc4spring.multipart.CommonsPortletMultipartResolver\" />\n        ```\n\n        | **Note:** Alternatively, you can use the native Portlet 3.0 file upload\n        | support that PortletMVC4Spring provides by setting the\n        | `portletMultipartResolver` `<bean>` element's `class` to\n        | `com.liferay.portletmvc4spring.multipart.StandardPortletMultipartResolver`\n\n    2.  Remove these dependencies from your `pom.xml` or `build.gradle` \n        descriptor:\n\n        ```xml\n        <dependency>\n        \t<groupId>commons-fileupload</groupId>\n        \t<artifactId>commons-fileupload</artifactId>\n        </dependency>\n        <dependency>\n        \t<groupId>commons-io</groupId>\n        \t<artifactId>commons-io</artifactId>\n        </dependency>\n        ```\n\n5.  Throughout your project, replace all uses of the \n    `org.springframework.web.portlet` package path with\n    `com.liferay.portletmvc4spring`. \n\n6.  Continue [developing your portlet using\n    PortletMVC4Spring](/docs/7-2/appdev/-/knowledge_base/a/developing-a-portlet-using-portletmvc4spring).\n\n7.  [Build and deploy your\n    project](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project).\n\nCongratulations! You migrated your project from Spring Portlet MVC to\nPortletMVC4Spring. \n\n## Related Topics \n\n[PortletMVC4Spring](/docs/7-2/appdev/-/knowledge_base/a/portletmvc4spring)\n\n[Developing a Portlet Using\nPortletMVC4Spring](/docs/7-2/appdev/-/knowledge_base/a/developing-a-portlet-using-portletmvc4spring)\n\n[Configuring\nDependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies)\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/07-jsf-portlet/01-jsf-portlet-intro.markdown",
    "content": "---\nheader-id: jsf-portlet\n---\n\n# JSF Portlet\n\n[TOC levels=1-4]\n\nDo you want to develop MVC-based portlets using the Java EE standard? Do you\nwant to use a portlet development framework with a UI component model that makes\nit easy to develop sophisticated, rich UIs? Or have you been writing  web apps\nusing JSF that you'd like to use in @product@? If you answered *yes* to any of\nthese questions, you're in luck! You can use the JSF portlet technology in\n@product@ by leveraging the Liferay Faces project, which provides all these\ncapabilities and more. \n\nLiferay Faces is an umbrella project that provides support for the\nJavaServer&#8482; Faces (JSF) standard in @product@. It encompasses the\nfollowing projects:\n\n- [Liferay Faces Bridge](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-bridge)\n  lets you deploy JSF web apps as portlets without writing portlet-specific Java\n  code. It also contains innovative features that make it possible to leverage\n  the power of JSF 2.x inside a portlet application. Liferay Faces Bridge\n  implements the JSR 329/378 Portlet Bridge Standard.\n- [Liferay Faces Alloy](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-alloy)\n  lets you use AlloyUI components in a way that is consistent with JSF\n  development.\n- [Liferay Faces Portal](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-portal)\n  lets you leverage Liferay-specific utilities and UI components in JSF\n  portlets.\n\nFor a comprehensive demo for the JSF component suite, visit the\n[Liferay Faces Developer site](https://faces.liferay.dev).\n\nIf you're new to JSF, you may want to know its strengths, its weaknesses,\nand how it stacks up to developing portlets with CSS/JavaScript. \n\nHere are some good reasons to use JSF and Liferay Faces:\n\n- JSF is the Java EE standard for developing web applications that use the\n  Model/View/Controller (MVC) design pattern. As a standard, the specification\n  is actively maintained by the Java Community Process (JCP), and the Oracle\n  reference implementation (Mojarra) has frequent releases. Software Architects\n  often choose standards like JSF because they are supported by Java EE\n  application server vendors and have a guaranteed service life according to\n  Service Level Agreements (SLAs).\n- JSF was first introduced in 2003 and is a mature technology for\n  developing web applications that are (arguably) easy to maintain.\n- JSF Portlet Bridges (like Liferay Faces Bridge) are also standardized by the\n  JCP and make it possible to deploy JSF web applications as portlets without\n  writing portlet-specific Java code.\n- Support for JSF (via Liferay Faces) is included with Liferay DXP support.\n- JSF is a unique framework in that it provides a UI component model that makes\n  it easy to develop sophisticated, rich user interfaces.\n- JSF has built-in Ajax functionality that provides automatic updates to the\n  browser by replacing elements in the DOM.\n- JSF is designed with many extension points that make a variety of integrations\n  possible.\n- There are several JSF component suites available including Liferay Faces\n  Alloy, Primefaces, ICEfaces, and RichFaces. Each of these component suites\n  fortify JSF with a variety of UI components and complimentary technologies.\n- JSF is a good choice for server-side developers that need to build web user\n  interfaces. This enables server-side developers to focus on their core\n  competencies rather than being experts in HTML/CSS/JavaScript.\n- JSF provides the Facelets templating engine which makes it possible to create\n  reusable UI components that are encapsulated as markup.\n- JSF provides good integration with HTML5 markup\n- JSF provides the Faces Flows feature which makes it easy for developers to\n  create wizard-like applications that flow from view-to-view. \n- JSF has good integration with dependency injection frameworks such as CDI and\n  Spring that make it easy for developers to create beans that are placed within\n  a scope managed by a container: `@RequestScoped`, `@ViewScoped`,\n  `@SessionScoped`, `@FlowScoped`\n- Since JSF is a stateful technology, the framework encapsulates the\n  complexities of managing application state so the developer doesn't have\n  to write state management code. It is also possible to use JSF in a stateless\n  manner, but some of the features of application state management become\n  effectively disabled.\n\nThere are some reasons not to use JSF. For example, if you are a front-end\ndeveloper who makes heavy use of HTML/CSS/JavaScript, you might find that JSF UI\ncomponents render HTML in a manner that gives you less control over the overall\nHTML document. Sticking with a JavaScript framework may be better for you. Or,\nperhaps standards aren't a major consideration for you or you may simply prefer\ndeveloping portlets using your current framework.\n\nWhether you develop your next portlet application with JSF and Liferay Faces or\nwith HTML/CSS/JavaScript is entirely up to you. But you probably want to learn\nmore about Liferay Faces and try it out for yourself.\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/07-jsf-portlet/02-developing-a-jsf-portlet-application.markdown",
    "content": "---\nheader-id: developing-a-jsf-portlet-application\n---\n\n# Developing a JSF Portlet Application\n\n[TOC levels=1-4]\n\nTo run an existing JSF web app on @product@, you must leverage the Liferay Faces\nproject. The\n[Liferay Faces Bridge](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-bridge)\nenables you to deploy JSF web apps as portlets without writing portlet-specific\ncode. You must also provide portlet-specific descriptor files to make it\ncompatible with the @product@ platform. The easiest way to do this is by\ngenerating a new Liferay JSF Portlet project and migrating your code to it. Then\nyou can deploy your new JSF portlet project to @product@.\n\nFollow these steps:\n\n1.  Create a new JSF portlet project. The following Maven archetypes are\n    available:\n\n    - `com.liferay.faces.archetype.alloy.portlet` (Liferay Faces Alloy portlet)\n    - `com.liferay.faces.archetype.bootsfaces.portlet` (Liferay BootsFaces\n      portlet)\n    - `com.liferay.faces.archetype.butterfaces.portlet` (Liferay ButterFaces\n      portlet)\n    - `com.liferay.faces.archetype.icefaces.portlet` (Liferay ICEFaces portlet)\n    - `com.liferay.faces.archetype.jsf.portlet` (Liferay JSF portlet)\n    - `com.liferay.faces.archetype.primefaces.portlet` (Liferay PrimeFaces\n      portlet)\n    - `com.liferay.faces.archetype.richfaces.portlet` (Liferay RichFaces\n      portlet)\n\n    Choose the archetype that matches your web app's JSF component suite. For\n    example,\n\n    ```bash\n    mvn archetype:generate \\\n        -DarchetypeGroupId=com.liferay.faces.archetype \\\n        -DarchetypeArtifactId=com.liferay.faces.archetype.jsf.portlet \\\n        -DarchetypeVersion=5.0.6 \\\n        -DgroupId=com.mycompany \\\n        -DartifactId=com.mycompany.my.jsf.portlet\n    ```\n\n    The above archetypes support both Gradle and Maven development by providing\n    a `build.gradle` and `pom.xml`, respectively. For more information, visit\n    [faces.liferay.dev](https://faces.liferay.dev).\n\n    Here's the resulting project structure for a JSF Standard portlet:\n\n    - [liferay-jsf-portlet]/ &rarr; Arbitrary project name\n        - `src/`\n            - `main/`\n                - `java/[my-package-path]/`\n                    - `bean/` &rarr; Sub-package for managed Java beans\n                      (optional)\n                    - `dto/` &rarr; Sub-package for model (data transfer\n                      object) classes (optional)\n                - `resources/` &rarr; Resources to include in the class path\n                    - `i18n.properties` &rarr; Internationalization\n                      configuration\n                    - `log4j.properties` &rarr; Log4J logging configuration\n                - `webapp/`\n                    - `resources/`\n                        - `images/` &rarr; Images\n                    - `WEB-INF/`\n                        - `resources/` Frontend files (e.g., CSS, JS, XHTML,\n                          etc.) that shouldn't be accessed directly by the\n                          browser\n                            - `css/` &rarr; Stylesheets\n                        - `views/` &rarr; View templates\n                        - `faces-config.xml` &rarr; JSF application\n                          configuration file\n                        - `liferay-display.xml` &rarr; Portlet display\n                          configuration\n                        - `liferay-plugin-package.properties` &rarr; Packaging\n                           descriptor\n                        - `liferay-portlet.xml` &rarr; Liferay-specific portlet\n                          configuration\n                        - `portlet.xml` &rarr; Portlet configuration\n                        - `web.xml` &rarr; Web application configuration\n        - `test/java/` &rarr; Test source files\n\n2.  Update your dependencies as desired. The generated portlet already includes\n    the required artifacts required to deploy a simple JSF portlet to @product@.\n    For example, the\n    [Liferay Faces Bridge](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-bridge)\n    artifacts look like this:\n\n    **Maven:**\n\n    ```xml\n    <dependencies>\n        <dependency>\n            <groupId>com.liferay.faces</groupId>\n            <artifactId>com.liferay.faces.bridge.ext</artifactId>\n            <version>5.0.4</version>\n            <scope>runtime</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.liferay.faces</groupId>\n            <artifactId>com.liferay.faces.bridge.impl</artifactId>\n            <version>4.1.3</version>\n            <scope>runtime</scope>\n        </dependency>\n    </dependencies>\n    ```\n\n    **Gradle:**\n\n    ```groovy\n    dependencies {\n        runtime group: 'com.liferay.faces', name: 'com.liferay.faces.bridge.ext', version: '5.0.4'\n        runtime group: 'com.liferay.faces', name: 'com.liferay.faces.bridge.impl', version: '4.1.3'\n    }\n    ```\n\n3.  Copy your Java classes to the new `java/[my-package-path]/` folder.\n\n4.  Copy your view templates to the new `src/main/webapp/WEBINF/views` folder.\n\n5.  Add your frontend files (e.g., CSS, JS, etc.) that shouldn't be accessed\n    directly by the browser to the `webapp/WEB-INF/resources/` folder. For\n    example, your web app's CSS files would reside in the\n    `webapp/WEB-INF/resources/css` folder.\n\n6.  Add your image files to the `webapp/resources/images` folder.\n\n7.  Add localized messages to the `resources/i18n.properties` file. The messages\n    in the `i18n.properties` file can be accessed via the Expression Language\n    using the implicit `i18n` object provided by Liferay Faces Util. The `i18n`\n    object can access messages both from a resource bundle defined in the\n    portlet's `portlet.xml` file, and from @product@'s `Language.properties`\n    file.\n\n8.  Configure your portlet's logging configuration as desired. The\n    `log4j.properties` file in the `src/main/resources` folder sets properties\n    for the Log4j logging utility defined in your JSF portlet (i.e.,\n    `faces-config.xml`).\n\n9.  Replace your new JSF portlet's `webapp/WEB-INF/faces-config.xml` with your\n    web app's `faces-config.xml` file. The `faces-config.xml` file is a JSF\n    portlet's application configuration file, which is used to register and\n    configure objects and navigation rules.\n\n10. Replace your new JSF portlet's `webapp/WEB-INF/web.xml` with your web app's\n    `web.xml` file. The `web.xml` file serves as a deployment descriptor that\n    provides necessary configurations for your JSF portlet to deploy and\n    function in @product@.\n\n    Make sure the Faces Servlet is configured in your `web.xml`:\n\n    ```xml\n    <servlet>\n        <servlet-name>Faces Servlet</servlet-name>\n        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>\n        <load-on-startup>1</load-on-startup>\n    </servlet>\n    ```\n\n    This is required to initialize JSF and should be defined in all JSF portlets\n    deployed to @product@.\n\n11. Modify your `webapp/WEB-INF/portlet.xml` as desired. The `portlet.xml`\n    descriptor describes the portlet to the portlet container. For example, it\n    describes portlet info, security settings, etc. Also, the\n    `javax.portlet.faces.GenericFacesPortlet` is defined here, which handles\n    invocations to your JSF portlet and makes your portlet, since it relies on\n    Liferay Faces Bridge, easy to develop by acting as a turnkey implementation.\n\n    The `init-param` is also defined here, which ensures your portlet is visible\n    when deployed to @product@ by pointing to your default view template:\n\n    ```xml\n    <init-param>\n        <name>javax.portlet.faces.defaultViewId.view</name>\n        <value>/WEB-INF/views/view.xhtml</value>\n    </init-param>\n    ```\n\n12. Modify your `webapp/WEB-INF/liferay-portlet.xml` as desired. It specifies\n    additional information @product@ uses to enhance your portlet: supported\n    security roles, portlet icon, CSS and JavaScript locations, and more. The\n    [liferay-portlet-app DTD](@platform-ref@/7.2-latest/definitions/liferay-portlet-app_7_2_0.dtd.html)\n    defines the `liferay-portlet.xml` elements.\n\n13. Modify your `webapp/WEB-INF/liferay-display.xml` as desired. It configures\n    characteristics for displaying your portlet. For example, this\n    `liferay-display.xml` snippet specifies the Widget category in the Add\n    Widget menu:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <!DOCTYPE display PUBLIC \"-//Liferay//DTD Display 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-display_7_2_0.dtd\">\n\n    <display>\n    <category name=\"category.sample\">\n        <portlet id=\"jsf-portlet\" />\n    </category>\n    </display>\n    ```\n\n14. Modify your `webapp/WEB-INF/liferay-plugin-package.properties` as desired.\n    It describes the portlet application's packaging and version information\n    and specifies any required OSGi metadata. For example, this\n    `liferay-plugin-package.properties` snippet tells the OSGi container not to\n    scan for CDI annotations in @product@.\n\n    ```properties\n    -cdiannotations:\n    ```\n\n    This is required for JSF portlets leveraging CDI deployed to @product@. They\n    must reference their own included CDI implementation.\n\n    On deploying the WAR file, the\n    [WAB Generator](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\n    adds the specified OSGi metadata to the resulting web application bundle\n    (WAB) that's deployed to @product@'s runtime framework.\n\n    The\n    [liferay-plugin-package reference document](@platform-ref@/7.2-latest/propertiesdoc/liferay-plugin-package_7_2_0.properties.html)\n    describes the `liferay-plugin-package.properties` file.\n\n15. [Build and deploy your project](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project).\n\n@product@ logs the deployment.\n\n```\n2019-05-30 14:10:59.405 INFO  [com.liferay.portal.kernel.deploy.auto.AutoDeployScanner][AutoDeployDir:261] Processing guestbook-jsf-portlet.war\n...\n2019-05-30 14:11:11.401 INFO  [fileinstall-C:/liferay-ce-portal-7.2.0-ga1/osgi/war][BaseDeployer:877] Deploying guestbook-jsf-portlet.war\n...\n2019-05-30 14:11:26.379 INFO  [fileinstall-C:/liferay-ce-portal-7.2.0-ga1/osgi/war][BundleStartStopLogger:39] STARTED guestbook-jsf-portlet_7.2.0.1 [2155]\n...\n2019-05-30 14:11:67.569 INFO  [fileinstall-C:/liferay-ce-portal-7.2.0-ga1/osgi/war][PortletHotDeployListener:288] 1 portlet for guestbook-jsf-portlet is available for use\n```\n\nThe portlet is now available in the @product@ UI. Find your portlet by selecting\nthe *Add* icon (![Add](../../../images/icon-add-app.png)) and navigating to\n*Widgets* and the category you specified (*Sample* is the default category).\n\nGreat! You've successfully developed a Liferay JSF portlet and migrated your\nweb app logic to it.\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/08-bean-portlet/01-bean-portlet-intro.markdown",
    "content": "---\nheader-id: bean-portlet\n---\n\n# Bean Portlet\n\n[TOC levels=1-4]\n\n| **Important:** Bean Portlet is in development and is not yet available.\n\nPortlet 3.0, the\n[JSR 362](https://jcp.org/en/jsr/detail?id=362) standard, features\na new style of portlet development called Bean Portlets that use Contexts and\nDependency Injection (CDI). Bean Portlets fully leverage\n[all the new Portlet 3.0 features](https://portals.apache.org/pluto/v301/v3Features.html)\nin compliant portals, and are fully supported in @product@.\n\nBean Portlets are plain old Java objects (POJOs): they don't need to extend\nanything. Portlet descriptors declare them to be portlets.\n\nConfiguration annotations, phase method annotations, and CDI are some of the\nfeatures you'll use in Portlet 3.0.\n\n## Portlet Configuration Annotations\n\nThe\n[`@PortletConfiguration`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/PortletConfiguration.html)\nannotation describes your portlet to the portlet container. You can use the\nannotation instead of or in addition to the traditional `portlet.xml` descriptor\nfile. The `@PortletConfiguration` annotation describes your portlet in the\nportlet code instead of a separate file.\n\n| **Note:** You can configure Bean Portlets using configuration annotations,\n| descriptors, or both. If using annotations and descriptors, the descriptors\n| take precedence.\n\nThis example portlet was generated using the\n`com.liferay.project.templates.cdi.bean.portlet` archetype, and it uses\n`@PortletConfiguration` and `@LiferayPortletConfiguration` annotations:\n\n```java\nimport com.mycompany.constants.FooPortletKeys;\n\nimport com.liferay.bean.portlet.LiferayPortletConfiguration;\n\nimport javax.portlet.annotations.LocaleString;\nimport javax.portlet.annotations.PortletConfiguration;\n\n@PortletConfiguration(\n    portletName = FooPortletKeys.Foo,\n    title = @LocaleString(value = FooPortletKeys.Foo))\n@LiferayPortletConfiguration(\n    portletName = FooPortletKeys.Foo,\n    properties = {\n        \"com.liferay.portlet.display-category=category.sample\",\n        \"com.liferay.portlet.instanceable=true\"\n    }\n)\npublic class FooPortlet {\n    ...\n}\n```\n\n`@PortletConfiguration`'s `portletName` attribute names the portlet. It's the\nonly required attribute. The `title` attribute typically uses a nicer looking\nname (e.g., uses spaces and capitalization). The `title` above is assigned\nthe key constant `FooPortletKeys.Foo`. You can also localize a title to one or\nmore languages using an array of\n[`@LocaleString`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/LocaleString.html)\nannotations, each specifying a different value for the `locale` element.\n\nThe `@LiferayPortletConfiguration` annotation specifies additional\nLiferay-specific configuration properties. For example, the\n`com.liferay.portlet.display-category` property lets you assign the Widget\ncategory where users will find your portlet. Setting the\n`com.liferay.portlet.instanceable=true` enables adding multiple instances of the\nportlet to a page.\n\n| **Note:** The `@PortletConfiguration` and `@LiferayPortletConfiguration`\n| annotations are respectively synonymous with the `javax.portlet.*` and\n| `com.liferay.portlet.*` properties in the OSGi `@Component` annotation (used\n| in\n| [Liferay MVC Portlets](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet),\n| for example).\n| If you're familiar with the `portlet.xml` and `liferay-portlet.xml`\n| descriptors, the\n| [Portlet Descriptor to OSGi Service Property Map](/docs/7-2/reference/-/knowledge_base/r/portlet-descriptor-to-osgi-service-property-map)\n| shows you the OSGi `@Component` property equivalent. There's an\n| `@PortletConfiguration` or `@LiferayPortletConfiguration` equivalent setting\n| for each `@Component` property.\n\nTo opt-in to Portlet 3.0 features, add the following\n[`@PortletApplication`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/PortletApplication.html)\nannotation to the class.\n\n```java\n@PortletApplication(version=\"3.0\")\n```\n\nOnce you've configured your portlet, you should declare the objects it uses\n(depends on).\n\n## Dependency Injection\n\nBean Portlets use the `@Inject` CDI annotation (by default) to inject\ndependencies. Apply the annotation to a field you want injected with an object\nof the specified type. This example portlet injects the portlet's\n`PortletConfig` object.\n\n```java\nimport javax.inject.Inject;\n\nimport javax.portlet.PortletConfig;\n\npublic class FooPortlet {\n\n    @Inject\n    PortletConfig portletConfig;\n\n    // Invoke methods on portletConfig ...\n\n}\n```\n\n| **Note:**\n| [OSGi Integration](/docs/7-2/frameworks/-/knowledge_base/f/osgi-cdi-integration)\n| allows you to use OSGi services (e.g., Liferay's\n| [`UserLocalService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/service/UserLocalService.html))\n| in your Bean Portlets.\n\nPortlet 3.0 defines annotations for declaring methods that handle portlet\nphases.\n\n## Portlet Phase Methods\n\nPhase method annotations apply methods for handling a portlet's phases. You can\nadd them to methods in any class anywhere in the portlet WAR. There's no\nmandatory method naming convention: assign a phase annotation to the\nmethods you want to invoke to process the phase. Here are the annotations:\n\n| Phase    | Annotation |\n| ---------------- | ---------- |\n| Header (new)     | [`@HeaderMethod`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/HeaderMethod.html) |\n| Render           | [`@RenderMethod`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/RenderMethod.html) |\n| Action           | [`@ActionMethod`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/ActionMethod.html) |\n| Event            | [`@EventMethod`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/EventMethod.html)  |\n| Resource-serving | [`@ServeResourceMethod`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/ServeResourceMethod.html) |\n\nYou can specify resource dependencies, such as CSS, in the Header phase prior to\nthe Render phase. It helps you avoid loading the same resources multiple times.\n\nYou'll definitely want to define a Render method. For example, here's a method\ninvoked during the Render phase:\n\n```java\nimport javax.portlet.annotations.RenderMethod;\n\n@RenderMethod(\n    include = \"/WEB-INF/jsp/view.jsp\",\n    portletNames = {FooPortletKeys.Foo})\npublic String doView() {\n    return \"Hello from \" + portletConfig.getPortletName();\n}\n```\n\nThe `@RenderMethod` annotation sets the method to be invoked during the Render\nphase of the WAR's portlets matching any of the names listed for the\n`portletNames` attribute.\n\nThe example Render method produces this content:\n\n1.  A string greeting `\"Hello from \" + portletConfig.getPortletName()`\n\n2.  The JSP template `/WEB-INF/jsp/view.jsp`---the `@RenderMethod` annotation's\n    `include` attribute references it.\n\nThese are just a few of the Portlet 3.0 features that facilitate developing\napplications. This section covers more Portlet 3.0 features and Bean Portlet\ndemonstrations. Creating and deploying your own Bean Portlet is next.\n"
  },
  {
    "path": "en/developer/appdev/articles/02-web-front-ends/08-bean-portlet/02-creating-a-bean-portlet.markdown",
    "content": "---\nheader-id: creating-a-bean-portlet\n---\n\n# Creating a Bean Portlet\n\n[TOC levels=1-4]\n\n| **Important:** Bean Portlet is in development and is not yet available.\n\nYour first step in developing a Bean Portlet is to create one. Here you'll\ngenerate a Bean Portlet project and deploy your Bean Portlet to @product@.\n\n1.  Generate a Bean Portlet project using a Maven command like this:\n\n        mvn archetype:generate \\\n        -DarchetypeGroupId=com.liferay \\\n        -DarchetypeArtifactId=com.liferay.project.templates.cdi.bean.portlet \\\n        -DarchetypeVersion=1.0.0 \\\n        -DgroupId=com.mycompany \\\n        -DartifactId=com.mycompany.demo.bean.portlet\n\n    Here's the resulting folder structure for a Bean Portlet class named `Foo`:\n\n    -   `com.mycompany.demo.bean.portlet` &rarr; Arbitrary project name.\n        -   `src/main/java/`\n            -   `com.mycompany.constants.FooPortletKeys` &rarr; Declares portlet\n                constants.\n            -   `com.mycompany.portlet.FooPortlet` &rarr; Bean Portlet class.\n        -   `src/main/webapp/WEB-INF/`\n            -   `jsp/view.jsp` &rarr; Default view template.\n            -   `beans.xml` &rarr; Signals CDI to scan the portlet for\n                annotations.\n\n        - `pom.xml` &rarr; Specifies the project's dependencies and packaging.\n\n    Here's the example Bean Portlet class:\n\n    ```java\n    package com.mycompany.portlet;\n\n    import com.mycompany.constants.FooPortletKeys;\n\n    import com.liferay.bean.portlet.LiferayPortletConfiguration;\n\n    import javax.inject.Inject;\n\n    import javax.portlet.PortletConfig;\n    import javax.portlet.annotations.LocaleString;\n    import javax.portlet.annotations.PortletConfiguration;\n    import javax.portlet.annotations.RenderMethod;\n\n    @PortletConfiguration(\n    \tportletName = FooPortletKeys.Foo,\n    \ttitle = @LocaleString(value = FooPortletKeys.Foo))\n    @LiferayPortletConfiguration(\n    \tportletName = FooPortletKeys.Foo,\n    \tproperties = {\n    \t\t\"com.liferay.portlet.display-category=category.sample\",\n    \t\t\"com.liferay.portlet.instanceable=true\"\n    \t}\n    )\n    public class FooPortlet {\n\n    \t@Inject\n    \tPortletConfig portletConfig;\n\n    \t@RenderMethod(\n    \t\tinclude = \"/WEB-INF/jsp/view.jsp\",\n    \t\tportletNames = {FooPortletKeys.Foo})\n    \tpublic String doView() {\n    \t\treturn \"Hello from \" + portletConfig.getPortletName();\n    \t}\n\n    }\n    ```\n\n2.  Set any portlet configuration or Liferay portlet configuration values\n    using\n    [`@PortletConfiguration` and `Liferay@PortletConfiguration` attributes](/docs/7-2/reference/-/knowledge_base/r/portlet-descriptor-to-osgi-service-property-map).\n\n3.  Inject any CDI beans using the `@Inject` annotation.\n\n4.  Update your render method `doView` (it's annotated with\n    `@RenderMethod`). It displays the template `WEB-INF/jsp/view.jsp` by\n    default.\n\n5.  Add any other logic you like to your portlet class.\n\n6.  Build your portlet:\n\n        mvn clean package\n\n7.  Deploy your portlet by copying the portlet WAR to your `[Liferay\n    Home]/deploy` folder. The\n    [WAB Generator](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\n    converts the WAR to an OSGi Web Application Bundle (WAB) and installs it to\n    Liferay's OSGi container.\n\n@product@ logs the deployment.\n\n    INFO  [main][HotDeployImpl:226] Deploying com.mycompany.demo.bean.portlet from queue\n    INFO  [main][PluginPackageUtil:1001] Reading plugin package for com.mycompany.demo.bean.portlet\n    ...\n    INFO  [main][PortletHotDeployListener:181] 1 bean portlets for com.mycompany.demo.bean.portlet are available for use\n\nThe Bean Portlet is now available in the @product@ UI. The example portlet is in\nthe Widget category you assigned it.\n\n![Figure 1: The Foo portlet prints the message returned from `doView` method and shows the included JSP's contents.](../../../images/portlet-3-portlet.png)\n\nCongratulations on creating and deploying a Bean Portlet!\n\n## Related Topics\n\n[OSGi CDI Integration](/docs/7-2/frameworks/-/knowledge_base/f/osgi-cdi-integration)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/01-service-builder-intro.markdown",
    "content": "---\nheader-id: service-builder\n---\n\n# Service Builder\n\n[TOC levels=1-4]\n\nAn application without reliable business logic or persistence isn't much of an\napplication at all. Unfortunately, writing your own persistence code often takes\na great deal of time. Service Builder is an object-relational mapping tool that\ncan generate your model, persistence, and service layers from a single `xml`\nfile. Once generated, the code is completely customizable: you can write your\nown persistence code along with custom SQL if necessary. Regardless of how you\nproduce your persistence code, you can then use Service Builder to implement\nyour app's business logic. \n\nThis section demonstrates using Service Builder to\n\n-   Generate and customize your persistence framework\n\n-   Implement your business logic\n\nWhen you configure your model and its relationships in your `service.xml` file\nand run Service Builder, it generates these layers of code: \n\n-   **Model layer:** defines objects to represent your project's entities. \n\n-   **Persistence layer:** saves entities to and retrieves entities from the \n    database and updates entities. \n\n-   **Service layer:** a blank layer ready for you to create your API and\n    business logic. . \n\nHere are some key features these layers contain: \n\n-   Stubbed-out classes for implementing custom business logic \n\n-   Hibernate configurations \n\n-   Configurable caching support \n\n-   Flexibility and support for adding custom SQL queries and dynamic queries \n\n| **Note:** You don't have to use Service Builder for your back-end services on\n| @product. It's entirely possible to use your persistence framework of choice, \n| such as JPA or Hibernate. Note that \"under the hood,\" Service Builder uses\n| Hibernate. \n\n## Customization via Implementation Classes\n\nEach entity Service Builder generates contains these *implementation* classes: \n\n-   **Entity implementation** (`*Impl.java`): Is responsible for customizing \n    the entity. \n\n-   **Local service implementation** (`*LocalServiceImpl.java`): Is responsible \n    for calling the persistence layer to retrieve and store data entities. Local\n    services contain the business logic and access the persistence layer. They\n    can be invoked by client code running in the same Java Virtual Machine. \n\n-   **Remote service implementation** (`*ServiceImpl.java`): Generated if \n    the `service.xml` is configured for remote services. Remote services usually\n    contain permission checking code and are meant to be accessible from\n    outside the JVM. Service Builder automatically generates code that makes the\n    remote services available via JSON or SOAP, and you can also create your own\n    remote APIs through [REST Builder](/docs/7-2/appdev/-/knowledge_base/a/rest-builder) \n    or [JAX-RS](/docs/7-2/frameworks/-/knowledge_base/f/jax-rs). \n\nThese classes are where you implement custom business logic. They're the only\nclasses generated by Service Builder intended for customization. \n\n## Hibernate Configurations \n\nService Builder uses the Hibernate persistence framework for object-relational\nmapping. Service Builder hides the complexities of Hibernate, while still giving\nyou access to technology like dynamic queries and custom SQL. You can take\nadvantage of Object-Relational Mapping (ORM) in your projects without having to\nmanually set up a Hibernate environment or make any configurations. \n\n## Caching \n\nService Builder caches objects at three levels: *entity*, *finder*, and\n*Hibernate*. By default, Liferay uses Ehcache as an underlying cache provider\nfor each of these cache levels. However, this is configurable via \n[portal properties](/docs/7-2/deploy/-/knowledge_base/d/portal-properties). All \nyou must do to enable entity and finder caching in your project is to set the\n`cache-enabled=true` attribute of your entity's `<entity>` element in your\n`service.xml` file. \n[Liferay Clustering](/docs/7-2/deploy/-/knowledge_base/d/enabling-cluster-link) describes\nLiferay caching in a cluster. \n\n## Dynamic Query and Custom SQL Query\n\nService Builder  automates many of the common tasks associated with creating\ndatabase persistence code but it doesn't prevent you\nfrom creating [custom SQL queries](/docs/7-2/appdev/-/knowledge_base/a/custom-sql). \nYou can define custom SQL queries in an XML file and implement finder methods to\nrun the queries. If you have some crazy join to do, Service Builder gets out of\nyour way. You can also use [dynamic query](/docs/7-2/appdev/-/knowledge_base/a/dynamic-query) \nto access Hibernate's criteria API. \n\nService Builder is used exclusively throughout @product@ and its applications,\nso it's well-tested and robust. It saves lots of development time, both initial\ndevelopment time and time that would have to be spent maintaining, extending, or\ncustomizing a project. Now\n[create your own Service Builder project](/docs/7-2/appdev/-/knowledge_base/a/creating-a-service-builder-project). \n\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/02-creating-a-service-builder-project/02-creating-a-service-builder-project-intro.markdown",
    "content": "---\nheader-id: creating-a-service-builder-project\n---\n\n# Creating a Service Builder Project\n\n[TOC levels=1-4]\n\nTo use [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder),\nyou must generate the projects where you'll configure your object-relational\nmap. There's an API project and an implementation project. \n\n-   `[project]/[project]-api/` &rarr; Service interfaces.\n\n-   `[project]/[project]-service/` &rarr; Service implementations and \n    supporting files. \n\nHere's how to create a Service Builder project. \n\n1.  Decide on a project name. If the project is part of an application, name the\n    project after the application. \n\n2.  [Create a project](/docs/7-2/reference/-/knowledge_base/r/creating-a-project) \n    using\n    [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli) \n    and the\n    [`service-builder` project template](/docs/7-2/reference/-/knowledge_base/r/using-the-service-builder-template),\n    passing your project name as a parameter. For example, here are Gradle and\n    Maven commands for creating a Service Builder project called `guestbook`. \n\n    Gradle: \n\n    ```bash\n    blade create -t service-builder -p com.liferay.docs.guestbook guestbook\n    ```\n\n    Maven:\n\n    ```bash\n    mvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service.builder \\\n    -DgroupId=com.liferay \\\n    -DartifactId=guestbook \\\n    -Dpackage=com.liferay.docs.guestbook \\\n    -Dversion=1.0 \\\n    -DapiPath=com.liferay.api.path \\\n    -DliferayVersion=7.2\n    ```\n\n| **Note:** To use the Spring dependency \n| injector instead of the \n| [Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services)\n| dependency injector, \n| use the `--dependency-injector spring` option (Blade CLI) or\n| `-DdependencyInjector=spring` (Maven). \n\nA message like this one reports project creation success:\n\n```\nSuccessfully created project bookmarks in C:\\workspaces_liferay\\72-ws\\modules\n```\n\nBlade CLI generates the parent project folder and sub-folders for the `*-api`\nand `*-service` module projects. \n\n-   `guestbook/`\n    -   `guestbook-api/`\n        -   `bnd.bnd`\n        -   `build.gradle`\n    -   `guestbook-service/`\n        -   `bnd.bnd`\n        -   `build.gradle`\n        -   `service.xml` &rarr; Service definition file.\n    -   `build.gradle`\n\nCongratulations! You've created your Service Builder project. The `service.xml`\nfile is where you'll define your model objects (entities) and services. \n\n## Related Topics \n\n[Service Builder Samples](/docs/7-2/reference/-/knowledge_base/r/blade-cli)\n\n[Service Builder Gradle Plugin](/docs/7-2/reference/-/knowledge_base/r/service-builder-gradle-plugin)\n\n[Service Builder Maven Plugin](/docs/7-2/reference/-/knowledge_base/r/service-builder-plugin)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/01-creating-the-service-xml-file-intro.markdown",
    "content": "---\nheader-id: creating-the-service-xml-file\n---\n\n# Creating the service.xml File\n\n[TOC levels=1-4]\n\nTo define a service for your portlet project, you must create a `service.xml`\nfile. The DTD (Document Type Declaration) file\n[liferay-service-builder_7_2_0.dtd](https://docs.liferay.com/ce/portal/7.2-latest/definitions/liferay-service-builder_7_2_0.dtd.html)\nspecifies the format and requirements of the XML to use. \n\nA `service.xml` was created for you when you\n[created your Service Builder project](/docs/7-2/appdev/-/knowledge_base/a/creating-a-service-builder-project). \nIt's in your `*-service` module's root folder with an `entity` element named\n*Foo*. This is (obviously) an example entity, but you can use it as a pattern\nfor creating your own. \n\n[Liferay @ide@](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio) \nprovides a Diagram mode and a Source mode to give you different\nperspectives of the service information in your `service.xml` file.\n\n- **Diagram mode** facilitates creating and visualizing relationships between \n  service entities.\n- **Source mode** brings up the `service.xml` file's raw XML content in the \n  editor.\n\nIf you use Liferay @ide@, you can switch between these modes. Of course, you\ndon't have to use Liferay @ide@ to work on Liferay projects. \n\nNext, you'll specify your service's global information. \n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/03-defining-global-service-info/01-defining-global-service-information-intro.markdown",
    "content": "---\nheader-id: defining-global-service-information\n---\n\n# Defining Global Service Information\n\n[TOC levels=1-4]\n\nA service's global information applies to all its entities. It contains the \n\n- [Dependency Injector](#dependency-injector)\n- [Package path](#package-path)\n- [Namespace options](#namespace-options)\n- [Multiversion concurrency control](#multiversion-concurrency-control-mvcc)\n- [Author](#author)\n\n## Dependency Injector\n\nThe default dependency injector is\n[OSGi Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services).\nThis makes Service Builder work consistently the way other modules do. Prior\nversions of Liferay used Spring. The only difference is how you inject the\nservices when you \n[go to use them later](/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services). \n\nDeclarative Services Dependency Injector: \n\n```xml\n<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"ds\"\n\t\t package-path=\"com.liferay.docs.guestbook\">\n```\n\nSpring Dependency Injector: \n\n```xml\n<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"spring\"\n\t\t package-path=\"com.liferay.docs.guestbook\">\n```\n\n| **Important:** When a project is created using the\n| [Service Builder template](/docs/7-2/reference/-/knowledge_base/r/using-the-service-builder-template), \n| the Declarative Services dependency injector and its dependencies are \n| configured for the project by default. To use the Spring\n| dependency injector instead, create the project using \n| the Service Builder template and the `--dependency-injector spring` option \n| (Blade CLI) or `-DdependencyInjector=spring` (Maven). \n\n| **Note:** Prior to @product@ 7.2, Spring was the sole dependency injector. The\n| services were Spring beans. Liferay's Spring bean framework accommodates \n| Spring beans referencing each other: for example, Spring bean A has a Spring\n| bean B field and vice versa. When Spring is the dependency injector, the base\n| services Service Builder generates include local service and persistence fields\n| of all the `service.xml`'s entities. This causes circular references. Since OSGi\n| Declarative Services doesn't accommodate circular references, Service Builder\n| does not create these fields in the base classes when DS is the dependency\n| injector. For more details, see \n| [Understanding the Code](understanding-the-code-generated-by-service-builder).\n\n## Package Path\n\nThe package path specifies the package where the service and persistence\nclasses are generated. The package path for Guestbook ensures that the `*-api`\nmodule's service classes are generated in the `com.liferay.docs.guestbook` package.\nThe persistence classes are generated in a package of the same name in the\n`*-service` module. A later article \n[describes the package content](/docs/7-2/appdev/-/knowledge_base/a/running-service-builder). \n\n## Multiversion concurrency control (MVCC)\n\nThe `service-builder` element's `mvcc-enabled` attribute is `false` by default.\nSetting `mvcc-enabled=\"true\"` (hint: edit `service.xml` in *Source* view)\nenables\n[multiversion concurrency control](https://en.wikipedia.org/wiki/Multiversion_concurrency_control)\n(MVCC) for all of the service's entities. In systems, concurrent updates are\ncommon. Without MVCC people may read or overwrite data from an invalid state\nunknowingly. With MVCC, each modification is made upon a given base version\nnumber. When Hibernate receives the update, it generates an `update` SQL\nstatement that uses a `where` clause to make sure the current data version is\nthe version you expect. \n\nIf the current data version\n \n- **matches the expected version**, your data operation is based on up-to-date \n  data and is accepted.\n\n- **doesn't match the expected version**, the data you're operating on is\n  outdated. @product@ rejects your data operation and throws an exception,\n  which you can catch to help the user handle the exception (e.g., suggest\n  retrying the operation). \n\n**Important:** Enable MVCC for all your services by setting \n`mvcc-enabled=\"true\"` in your `<service-builder/>` element. When invoking\nservice entity updates (e.g., `fooService.update(object)`), make sure to do so\nin transactions. Propagate rejected transactions to the UI for the user to\nhandle. \n\n```xml\n<service-builder dependency-injector=\"ds\"\n\t\t package-path=\"com.liferay.docs.guestbook\"\n\t\t mvcc-enabled=\"true\">\n```\n\n## Namespace Options\n\nService Builder names the database tables using the service namespace. For\nexample, *GB* could serve as the namespace for a Guestbook application\nservice.\n\n```xml\n<namespace>GB</namespace>\n```\n\nService Builder uses the namespace in the following SQL scripts it\ngenerates in your `src/main/resources/sql` folder:\n\n- `indexes.sql`\n- `sequences.sql`\n- `tables.sql`\n\n| **Note:** The generated SQL script folder location is configurable. For \n| example, if you're using Gradle, you can define the `sqlDir` setting in the\n| project's Gradle `build.gradle` file or Maven `pom.xml` file, the same way \n| the `databaseNameMaxLength` setting is applied in the examples below. \n\nService Builder uses the SQL scripts to create database tables for all the\nentities the `service.xml` defines. The database table names have the namespace\nprepended when they are created. Since the example namespace value is\n`GB`, the database table names created for the entities start with\n`GB__` as their prefix. Each Service Builder project's namespace must be\nunique. Separate plugins should use separate namespaces and should not use\na namespace already used by Liferay entities (such as `Users` or `Groups`).\nCheck the table names in Liferay's database to see the namespaces already in\nuse.\n\n**Warning:** Use caution when assigning namespace values. Some databases have\nstrong restrictions on database table and column name lengths. The Service \nBuilder \n[Gradle](/docs/7-2/reference/-/knowledge_base/r/service-builder-gradle-plugin#task-properties)\nand \n[Maven](/docs/7-2/reference/-/knowledge_base/r/service-builder-plugin#available-parameters)\nplugin parameter `databaseNameMaxLength` sets the maximum length you can use for\nyour table and column names. Here are paraphrased examples of setting\n`databaseNameMaxLength` in build files:\n\n**Gradle `build.gradle`**\n\n```groovy\nbuildService {\n    ...\n    databaseNameMaxLength = 64\n    ...\n}\n```\n\n**Maven `pom.xml`**\n\n```xml \n<configuration>\n    ...\n    <databaseNameMaxLength>64</databaseNameMaxLength>\n    ...\n</configuration>\n```\n\n## Author\n\nAs the last piece of global information, enter your name as the service's\n*author* in your `service.xml` file. Service Builder adds `@author` annotations\nwith the specified name to all the Java classes and interfaces it generates.\nSave your `service.xml` file. Next, you'll add entities for your services. \n\n```xml\n<author>Liferay</author>\n```\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/04-defining-service-entities/01-defining-service-entities-intro.markdown",
    "content": "---\nheader-id: defining-service-entities\n---\n\n# Defining Service Entities\n\n[TOC levels=1-4]\n\nEntities are the heart and soul of a service. They represent the map between the\nmodel objects in Java and your database fields and tables. Service Builder maps\nyour Java model to the entities you define automatically, giving you a facility\nfor taking Java objects and persisting them. For the Guestbook application, two\nentities are created according to its `service.xml`: one for Guestbooks\nand one for Guestbook Entries.\n\nHere's a summary of the `Guestbook` entity information:\n\n- **Name:** `Guestbook`\n- **Local service:** *yes*\n- **Remote service:** *yes* \n\nAnd here's what is used for the `GuestbookEntry` entity:\n\n- **Name:** `GuestbookEntry`\n- **Local service:** *yes*\n- **Remote service:** *yes* \n\nHere's how you define entities: \n\n```xml\n<entity name=\"Guestbook\" uuid=\"true\" local-service=\"true\" remote-service=\"true\">\n</entity>\n\n<entity name=\"GuestbookEntry\" uuid=\"true\" local-service=\"true\" remote-service=\"true\">\n</entity>\n```\n\nThe entity's database table name includes the entity name prefixed with the\nnamespace. The Guestbook example creates one database table named\n`GW_Guestbook` and another named `GB_GuestbookEntry`. \n\nSetting *Local Service* (the `local-service` attribute) to `true` instructs\nService Builder to generate local interfaces for the entity's services. Local\nservices can only be invoked from the Liferay server on which they're deployed. \n\nSetting *Remote Service* (the `remote-service` attribute) to `true` instructs\nService Builder to generate remote interfaces for the service. You can build\na fully-functional application without generating remote services. In that case,\nyou could set your entity local services to `true` and remote services to\n`false`. If, however, you want to enable remote access to your application's\nservices, set both local service and remote service to `true`.\n\n| **Tip:** Suppose you have an existing Data Access Object (DAO) service for an\n| entity built using some other framework such as JPA. You can set local service\n| to `false` and remote service to `true` so that the methods of your remote\n| `-Impl` class can call the methods of your existing DAO. This enables your\n| entity to integrate with Liferay's permission-checking system and provides\n| access to the web service APIs generated by Service Builder. This is a very\n| handy, quite powerful, and often used feature of Liferay.\n\nNow that you've seen how to create your application's entities, you'll learn\nhow to describe their attributes using entity *columns*. \n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/05-defining-columns-and-attribs/01-defining-the-columns-attributes-for-each-service-entity-intro.markdown",
    "content": "---\nheader-id: defining-the-columns-attributes-for-each-service-entity\n---\n\n# Defining the Columns (Attributes) for Each Service Entity\n\n[TOC levels=1-4]\n\nAn entity's columns represent its attributes. These attributes map table fields\nto Java object fields. To add attributes for your entity, add `<column />` tags\nto your entity definition: \n\n```xml\n<column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n```\n\nService Builder creates a database field for each column you add to the\n`service.xml` file. It maps a database field type appropriate to the Java type\nspecified for each column, and it does this across all the databases Liferay\nsupports. Once Service Builder runs, it generates a Hibernate\nconfiguration that handles the object-relational mapping. Service Builder\nautomatically generates getter/setter methods in the model class for these\nattributes. The column's name specifies the name used in the getters and setters\nthat are created for the entity's Java field. The column's type indicates the\nJava type of this field for the entity. If a column's `primary` (i.e., primary\nkey) attribute is set to `true`, the column becomes part of the primary key for\nthe entity. If only one column has `primary` set to `true`, that column\nrepresents the entire primary key for the entity. This is the case in the\nGuestbook application. If you define multiple columns with the `primary`\nattribute set to true, the combination of columns makes up a compound primary\nkey for the entity.\n\n| **Note:** The\n| [Implementing an Add Method](/docs/7-2/appdev/-/knowledge_base/a/implementing-an-add-method#step-3-generate-a-primary-key)\n| article demonstrates how to generate unique primary keys for entity instances.\n\n## Create Entity Columns\n\nDefine the columns you need for your first entity. The Guestbook entity is\nsimple: it has only two attributes; a primary key and a name: \n\n```xml\n<column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n<column name=\"name\" type=\"String\" />\n```\n\n**Note**: On deploying a `*service` module, Service Builder automatically \ngenerates indexes for all entity primary keys. \n\nCreate a column for each attribute of your entity or entities, using the Java\ntype you'll use in your application. Service Builder handles mapping it to SQL\nfor you. \n\n## Support Multi-tenancy\n\nIn addition to columns for your entity's primary key and attributes, add portal\ninstance ID and site ID columns. Then you can support Liferay's multi-tenancy\nfeatures, so that each portal instance and each Site in a portal instance can\nhave independent sets of your application's data. To hold the site's ID, add\na column called `groupId` of type `long`. To hold the portal instance's ID, add\na column called `companyId` of type `long`: \n\n```xml\n<!-- Group instance -->\n\n<column name=\"groupId\" type=\"long\" />\n<column name=\"companyId\" type=\"long\" />\n```\n\n## Workflow Fields\n\nYou can support Liferay's [workflow system](/docs/7-2/user/-/knowledge_base/u/workflow)\nby adding the fields it needs to track an entity's progress: \n\n```xml\n<!-- Status fields -->\n\n<column name=\"status\" type=\"int\" />\n<column name=\"statusByUserId\" type=\"long\" />\n<column name=\"statusByUserName\" type=\"String\" />\n<column name=\"statusDate\" type=\"Date\" />\n```\n\n## Audit Entities\n\nFinally, you can add columns to help audit your entities. To track each entity\ninstance's owner, add a column called `userId` of type `long`. Create a column\nnamed `createDate` of type `Date` to note an entity instance's creation date.\nAdd a column named `modifiedDate` of type `Date` to track the last time an\nentity instance was modified.\n\n```xml\n<!-- Audit fields -->\n\n<column name=\"userId\" type=\"long\" />\n<column name=\"userName\" type=\"String\" />\n<column name=\"createDate\" type=\"Date\" />\n<column name=\"modifiedDate\" type=\"Date\" />\n```\n\nGreat! Your entities have columns that not only represent their attributes, but\nalso support multi-tenancy, workflow, and auditing. Next, you'll learn how to\nspecify the relationship service entities. \n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/06-defining-relationships-between-entities/01-defining-relationships-between-service-entities-intro.markdown",
    "content": "---\nheader-id: defining-relationships-between-service-entities\n---\n\n# Defining Relationships Between Service Entities\n\n[TOC levels=1-4]\n\nRelationships between database entities or Java objects are necessary for most\napplications. The Guestbook application, therefore, defines a relationship\nbetween a Guestbook and its entries.\n\nAs mentioned earlier, each entry must belong to a particular Guestbook.\nTherefore, each `GuestbookEntry` entity must relate to a `Guestbook`\nentity. \n\nCreate the `GuestbookEntry` entity's fields: \n\n```xml\n<entity name=\"GuestbookEntry\" local-service=\"true\" uuid=\"true\" remote-service=\"true\">\n\n\t<column name=\"entryId\" primary=\"true\" type=\"long\" />\n\t<column name=\"name\" type=\"String\" />\n\t<column name=\"email\" type=\"String\" />\n\t<column name=\"message\" type=\"String\" />\n\t<column name=\"guestbookId\" type=\"long\" />\n```\n\nNote the last field in the list is the `guestbookId` field. Since it's the same\nname as the `Guestbook` object's primary key, a relationship is created between\nthe two objects. If you're using Liferay @ide@, you can see this relationship in\nits diagram mode. \n\n![Figure 1: Relating entities is a snap in Liferay @ide@'s *Diagram* mode for `service.xml`.](../../../../images/service-builder-relate-entities.png)\n\nCongratulations! You've related two entities. \n\nNext, add the instance, audit, and status fields mentioned from the previous\nstep to enable Liferay's multi-tenancy, audit, and workflow features. \n\nNow that your entity columns are in place and entity relationships are\nestablished, you can specify the default order in which the entity instances are\nretrieved from the database. \n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/07-defining-service-entity-instance-order/01-defining-ordering-of-service-entity-instances-intro.markdown",
    "content": "---\nheader-id: defining-ordering-of-service-entity-instances\n---\n\n# Defining Ordering of Service Entity Instances\n\n[TOC levels=1-4]\n\nOften, you want to retrieve multiple instances of a given entity and list them\nin a particular order. The `service.xml` file lets you specify the default order\nof your entities. \n\nSuppose you want to return `GuestbookEntry` entities by their creation date.\nIt's easy to specify these default orderings:\n\n```xml\n<order>\n    <order-column name=\"createDate\" order-by=\"desc\" />\n</order>\n```\n\nYou can enter `asc` or `desc` for ascending or descending order. \n\nNow that you know how to order your service entities, the last thing to do is to\ndefine the finder methods for retrieving entity instances from the database. \n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/08-defining-service-entity-finder-methods/01-defining-service-entity-finder-methods-intro.markdown",
    "content": "---\nheader-id: defining-service-entity-finder-methods\n---\n\n# Defining Service Entity Finder Methods\n\n[TOC levels=1-4]\n\nFinder methods retrieve entity objects from the database based on specified\nparameters. For each finder defined, Service Builder generates several methods\nto fetch, find, remove, and count entity instances based on the finder's\nparameters. \n\nWhen supporting Liferay's multi-tenancy, it's important to be able to find its\nentities per Site. \n\n## Creating Finders\n\nFinders are easy to create: \n\n```xml\n<finder name=\"GroupId\" return-type=\"Collection\">\n   <finder-column name=\"groupId\" />\n</finder> \n```\n\nThe example above is among the simplest of finders, and is one you should always\nadd if you're supporting multi-tenancy. This finder returns a collection of\nobjects that belong to the Site on which your application has been placed.\nService Builder generates finder-related methods (e.g., `fetchByGroupId`,\n`findByGroupId`, `removeByGroupId`, `countByGroupId`) for the your entities in\nthe `*Persistence` and `*PersistenceImpl` classes. The first of these classes is\nthe interface; the second is its implementation. For example, the Guestbook\napplication generates its entity finder methods in the `-Persistence`\nclasses found in the\n`/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence` folder\nand the `-PersistenceImpl` classes in the\n`/guestbook/src/main/java/com/liferay/docs/service/persistence/impl`\nfolder.\n\nYou're not limited to finding by one column, however; you can create\nmulti-column finders: \n\n```xml\n<finder name=\"G_S\" return-type=\"Collection\">\n\t<finder-column name=\"groupId\" />\n\t<finder-column name=\"status\" />\n</finder>\n```\n\n| **Important**: DO NOT create finders that use entity primary key as parameters.\n| They're unnecessary as Service Builder automatically generates\n| `findByPrimaryKey` and `fetchByPrimaryKey` methods for all entity primary keys.\n| On deploying a `*service` module, Service Builder creates indexes for all entity\n| primary key columns and finder columns. Adding finders that use entity primary\n| keys results in attempts to create multiple indexes for the same\n| columns---Oracle DB, for example, reports these attempts as errors.\n\nNow you know to configure Service Builder to create finder methods for your\nentity. Terrific!\n\nNow that you've specified the service for your project, you're ready to *build*\nthe service by running Service Builder. It's time to\n[run Service Builder and examine the code it generates](/docs/7-2/appdev/-/knowledge_base/a/running-service-builder). \n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/09-running-service-builder/01-running-service-builder-and-understanding-the-generated-code-intro.markdown",
    "content": "---\nheader-id: running-service-builder\n---\n\n# Running Service Builder\n\n[TOC levels=1-4]\n\nHere you'll learn how to run Service Builder. If want to use Service Builder in \nyour application but haven't yet\n[created a `service.xml` file that defines an object-relational map for you application](/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file),\nmake sure to do so before proceeding. \n\nOpen a command line and navigate to your application folder (the folder that\ncontains your `*-api` and `*-service` modules). \n\n## Gradle \n\nTo build your services using Gradle, enter the following command:\n\n```bash\nblade gw buildService\n```\n\nor\n\n```bash\ngradlew buildService\n```\n\nBlade's `gw` command works in any project that has a Gradle Wrapper available to\nit. Projects generated using Liferay project templates have a Gradle Wrapper. \n\n| **Note:** Liferay Workspace's Gradle Wrapper script is in the workspace root\n| folder. If your application project folder is located at\n| `[workspace]/modules/[application]`, for example, the Gradle Wrapper is\n| available at `../../gradlew`.\n\n## Maven\n\nIf you're using Maven, build the services by running the following command:\n\n```bash\nmvn service-builder:build\n```\n\n**Important:** The `mvn service-builder:build` command only works if you're\nusing the `com.liferay.portal.tools.service.builder` plugin version 1.0.145+.\nMaven projects using an earlier version of the Service Builder plugin should\nupdate their POM accordingly. More information is available on\n[using Maven to run Service Builder](/docs/7-2/reference/-/knowledge_base/r/using-service-builder-in-a-maven-project).\n\nOn successfully building the services, Service Builder prints the message `BUILD\nSUCCESSFUL`. Many generated files appear in your project. They represent a model\nlayer, service layer, and persistence layer for your entities. Don't worry about\nthe number of generated files---they're explained in the next article, where you\ncan review the code Service Builder generates for your entities.\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/10-understanding-the-code-service-builder-generates/01-understanding-the-code-generated-by-service-builder-intro.markdown",
    "content": "---\nheader-id: understanding-the-code-generated-by-service-builder\n---\n\n# Understanding the Code Generated by Service Builder\n\n[TOC levels=1-4]\n\n[Service Builder generates code](/docs/7-2/appdev/-/knowledge_base/a/running-service-builder)\nto support your entities. The files listed under Local Service and Remote\nService below are only generated for an entity that has both `local-service` and\n`remote-service` attributes set to `true`. Service Builder generates services\nfor these entities in your application's `*-api` and `*-service` modules in the\npackages you specified in `service.xml`. For example, here are the package\npaths for Liferay's Bookmarks application:\n\n- `/guestbook-api/src/main/java/com/liferay/docs/guestbook`\n- `/guestbook-service/src/main/java/com/liferay/docs/guestbook`\n\nThe `guestbook-api` module's interfaces define the Guestbook application API.\nThe `*-api` module interfaces define the application's persistence layer,\nservice layer, and model layer. Whenever you compile and deploy the `*-api`\nmodule, all its classes and interfaces are packaged in a `.jar` file called\n`PROJECT_NAME-api.jar` in the module's `build/libs` folder. Deploying this JAR\nto Liferay *defines* the API as OSGi services. \n\nThe `guestbook-service` module classes implement the `guestbook-api` module\ninterfaces. The `*-service` module provides the OSGi service implementations to\ndeploy to Liferay's OSGi framework. \n\nNext, examine the classes and interfaces generated for the entities you\nspecified. Similar classes are generated for each entity, depending on how each\nentity is specified in the `service.xml`. Here are the three types of\ncustomizable classes:\n\n- `*LocalServiceImpl`\n- `*ServiceImpl`\n- `*Impl`\n\nThe `*` represents the entity name in the classes listed above. \n\nHere are the persistence, service, and model classes:\n\n- Persistence\n    - `[ENTITY_NAME]Persistence`: Persistence interface that defines CRUD\n      methods for the entity such as `create`, `remove`, `countAll`, `find`,\n      `findAll`, etc. \n    - `[ENTITY_NAME]PersistenceImpl`: Persistence implementation class that\n      implements `[ENTITY_NAME]Persistence`. \n    - `[ENTITY_NAME]Util`: Persistence utility class that wraps\n      `[ENTITY_NAME]PersistenceImpl` and provides direct access to the database\n      for CRUD operations. This utility should only be used by the service\n      layer; in your portlet classes, use the `[ENTITY_NAME]` class by\n      referencing it with the\n      [`@Reference` annotation](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services). \n\n    ![Figure 1: Service Builder generates these persistence classes and interfaces for an example entity called *Event*. You shouldn't (and you won't need to) customize any of these classes or interfaces.](../../../../images/service-builder-persistence-diagram.png)\n\n- Local Service (generated for an entity only if the entity's `local-service`\n  attribute is set to `true` in `service.xml`)\n    - `[ENTITY_NAME]LocalService`: Local service interface. \n    - `[ENTITY_NAME]LocalServiceImpl` (**LOCAL SERVICE IMPLEMENTATION**): Local\n      service implementation. This is the only class in the local service that\n      you should modify: it's where you add your business logic. For\n      any methods added here, Service Builder adds corresponding methods\n      to the `[ENTITY_NAME]LocalService` interface the next time you run it.\n    - `[ENTITY_NAME]LocalServiceBaseImpl`: Local service base implementation.\n      This is an abstract class. Service Builder injects a number of instances\n      of various service and persistence classes into this class. `@abstract`\n    - `[ENTITY_NAME]LocalServiceUtil`: Local service utility class \n      which wraps `[ENTITY_NAME]LocalServiceImpl`. This class is generated for\n      backwards compatibility purposes only. Use the `*LocalService` class by\n      referencing it with the\n      [`@Reference` annotation](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services). \n    - `[ENTITY_NAME]LocalServiceWrapper`: Local service wrapper which implements\n      `[ENTITY_NAME]LocalService`. This class is designed to be extended and it\n      lets you\n      [customize the entity's local services](/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers).\n\n![Figure 2: Service Builder generates these service classes and interfaces. Only the [ENTITY_NAME]LocalServiceImpl (e.g., EventLocalServiceImpl for the Event entity) allows custom methods to be added to the service layer.](../../../../images/service-builder-service-diagram.png)\n\n- Remote Service (generated for an entity only if an entity's `remote-service`\n  attribute is *not* set to `false` in `service.xml`)\n    - `[ENTITY_NAME]Service`: Remote service interface. \n    - `[ENTITY_NAME]ServiceImpl` (**REMOTE SERVICE IMPLEMENTATION**): Remote\n      service implementation. This is the only class in the remote service\n      that you should modify manually. Here, you can write code that adds\n      additional security checks and invokes the local services. For any custom\n      methods added here, Service Builder adds corresponding methods to the\n      `[ENTITY_NAME]Service` interface the next time you run it. \n    - `[ENTITY_NAME]ServiceBaseImpl`: Remote service base implementation. This \n      is an abstract class.  `@abstract`\n    - `[ENTITY_NAME]ServiceUtil`: Remote service utility class \n      which wraps `[ENTITY_NAME]ServiceImpl`. This class is generated for\n      backwards compatibility purposes only. Use the `*Service` class by\n      referencing it with the\n      [`@Reference` annotation](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services).\n    - `[ENTITY_NAME]ServiceWrapper`: Remote service wrapper which implements\n      `[ENTITY_NAME]Service`. This class is designed to be extended and it lets\n      you\n      [customize the entity's remote services](/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers).\n    - `[ENTITY_NAME]ServiceSoap`: SOAP utility which the remote \n      `[ENTITY_NAME]ServiceUtil` remote service utility can access.\n    - `[ENTITY_NAME]Soap`: SOAP model, similar to `[ENTITY_NAME]ModelImpl`.\n      `[ENTITY_NAME]Soap` is serializable; it does not implement\n      `[ENTITY_NAME]`. \n\n- Model\n    - `[ENTITY_NAME]Model`: Base model interface. This interface and its\n      `[ENTITY_NAME]ModelImpl` implementation serve only as a container for the \n      default property accessors Service Builder generates. Any helper methods\n      and all application logic should be added to `[ENTITY_NAME]Impl`. \n    - `[ENTITY_NAME]ModelImpl`: Base model implementation. \n    - `[ENTITY_NAME]`: `[ENTITY_NAME]` model interface which extends\n      `[ENTITY_NAME]Model`. \n    - `[ENTITY_NAME]Impl`:  (**MODEL IMPLEMENTATION**) Model implementation. You\n      can use this class to add helper methods and application logic to your\n      model. If you don't add any helper methods or application logic, only the\n      auto-generated field getters and setters are available. Whenever you\n      add custom methods to this class, Service Builder adds corresponding\n      methods to the `[ENTITY_NAME]` interface the next time you run it.\n    - `[ENTITY_NAME]Wrapper`: Wrapper, wraps `[ENTITY_NAME]`. This class is \n      designed to be extended and it lets you\n      [customize the entity](/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers).\n\n![Figure 3: Service Builder generates these model classes and interfaces. Only `[ENTITY_NAME]Impl`  (e.g., `EventImpl` for the Event entity) allows custom methods to be added to the service layer.](../../../../images/service-builder-model-diagram.png)\n\n| **Note:** `*Util` classes are generated for backwards compatibility purposes\n| only. Your module applications should avoid calling the util classes. Use the\n| non-util classes instead--you can reference them using the\n| [`@Reference` annotation](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services).\n\nEach file that Service Builder generates is assembled from an associated\nFreeMarker template. The FreeMarker templates are in the\n[portal-tools-service-builder module's](https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-service-builder) `src/main/resources/com/liferay/portal/tools/service/builder/dependencies/` \nfolder. For example, Service Builder uses the `service_impl.ftl` template to\ngenerate the `*ServiceImpl.java` classes. \n\nYou can modify any `*Impl` class Service Builder generates. The most common are\n`*LocalServiceImpl`, `*ServiceImpl` and `*Impl`. If you modify the other\nclasses, Service Builder overwrites the changes the next time you run it.\nWhenever you add methods to, remove methods from, or change a method signature\nof a `*LocalServiceImpl` class, `*ServiceImpl` class, or `*Impl` class, you\nshould run Service Builder again to regenerate the affected interfaces and the\nservice JAR. \n\n| **Note:** Service Builder may generate code that requires adding\n| dependencies to your `*-service` module build file. \n\n| **Note:** When `spring` is the dependency injector (see *Dependency\n| Injector* in\n| [Defining Global Service Information](/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information)),\n| the `-LocalServiceBaseImpl` classes Service Builder generates include\n| `-LocalService` and `-Persistence` member fields of all the `service.xml`'s \n| entities. `-LocalServiceImpl` classes inherit these fields and are Spring \n| beans. The Spring beans can reference each other. For example, Spring bean A \n| can have a Spring bean B field and vice versa. Liferay's `spring` dependency\n| injector accommodates Spring bean circular references. The `ds` dependency\n| injector does not accommodate circular references. \n|\n| When using `ds` as the dependency injector, `-LocalServiceImpl` classes are \n| OSGi Declarative Services. Such services start only after all the other\n| services they reference have started. If declarative service A has a\n| declarative service B member field and vice versa, neither service can start.\n| For this reason, the `-LocalServiceBaseImpl` classes Service Builder generates\n| don't include `-LocalService` member fields of the `service.xml`'s other\n| entities. When using the `ds` dependency injector, you must make sure member\n| fields you add to service classes don't create circular dependencies. \n\nCongratulations! You've generated your application's initial model, persistence,\nand service layers and you understand the generated code. \n\n**Related Topics**\n\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n\n[Running Service Builder](/docs/7-2/appdev/-/knowledge_base/a/running-service-builder)\n\n[Understanding Service Context](/docs/7-2/frameworks/-/knowledge_base/f/understanding-servicecontext)\n\n[Creating Local Services](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/11-iterative-development/01-iterative-development-intro.markdown",
    "content": "---\nheader-id: iterative-development\n---\n\n# Iterative Development\n\n[TOC levels=1-4]\n\nAs you develop an application, you might need to add fields to your\ndatabase. This is a normal process of iterative development: you get an idea for\na new feature, or it's suggested to you, and that feature requires additional\ndata in the database. **New fields added to `service.xml` are not automatically\nadded to the database.** To add the fields, you must do one of two things: \n\n1.  Write an\n    [upgrade process](/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes) \n    to modify the tables and preserve the data, or\n\n2.  Run the `cleanServiceBuilder`\n    [Gradle task](/docs/7-2/reference/-/knowledge_base/r/db-support-gradle-plugin)\n    (also supported on Maven and Ant), which drops your tables so they get\n    re-created the next time your app is deployed. The\n    [Maven DB Support Plugin](/docs/7-2/reference/-/knowledge_base/r/db-support-plugin)\n    reference article explains how to run this command from a Maven\n    project.\n\nUse the first option if you have a released application and you must preserve\nuser data. Use the second option if you're adding new columns during\ndevelopment. \n\n## Related Topics\n\n[Upgrade Processes](/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes) \n\n[Gradle DB Support Plugin](/docs/7-2/reference/-/knowledge_base/r/db-support-gradle-plugin)\n\n[Maven DB Support Plugin](/docs/7-2/reference/-/knowledge_base/r/db-support-plugin)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/12-customizing-model-entities-with-model-hints/01-customizing-model-entities-with-model-hints-intro.markdown",
    "content": "---\nheader-id: customizing-model-entities-with-model-hints\n---\n\n# Customizing Model Entities With Model Hints\n\n[TOC levels=1-4]\n\nOnce you've used Service Builder to define model entities, you may want to\nfurther refine how users enter that data. For example, model hints can define a\ncalendar field with selectable dates only in the future. Model hints specify\nentity data restrictions and other formatting. \n\nYou define model hints in a file called `portlet-model-hints.xml`. The\n`portlet-model-hints.xml` file goes in the service module's\n`src/main/resources/META-INF` folder. \n\nModel hints define two things:\n\n1. How entities are presented to users\n\n2. The size of database columns\n\nAs Liferay renders your form fields, it customizes the form's input fields based\non your configuration. \n\n| **Note:** If you chose Spring as the dependency injector, Service Builder generates \n| a number of XML configuration files in your\n| service module's `src/main/resources/META-INF` folder. Service Builder uses\n| most of these files to manage Spring and Hibernate configurations. Don't modify\n| the Spring or Hibernate configuration files; changes to them are overwritten\n| when Service Builder runs. You can, however, safely edit the\n| `portlet-model-hints.xml` file.\n\nSince the Guestbook doesn't have much of a model hints file, as an example, consider the\n[Bookmarks app service module's](https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.bookmarks.service/)\nmodel hints file:\n\n```xml\n<?xml version=\"1.0\"?>\n\n<model-hints>\n    <model name=\"com.liferay.bookmarks.model.BookmarksEntry\">\n        <field name=\"uuid\" type=\"String\" />\n        <field name=\"entryId\" type=\"long\" />\n        <field name=\"groupId\" type=\"long\" />\n        <field name=\"companyId\" type=\"long\" />\n        <field name=\"userId\" type=\"long\" />\n        <field name=\"userName\" type=\"String\" />\n        <field name=\"createDate\" type=\"Date\" />\n        <field name=\"modifiedDate\" type=\"Date\" />\n        <field name=\"folderId\" type=\"long\" />\n        <field name=\"treePath\" type=\"String\">\n            <hint name=\"max-length\">4000</hint>\n        </field>\n        <field name=\"name\" type=\"String\">\n            <hint name=\"max-length\">255</hint>\n        </field>\n        <field name=\"url\" type=\"String\">\n            <hint-collection name=\"URL\" />\n            <validator name=\"required\" />\n            <validator name=\"url\" />\n        </field>\n        <field name=\"description\" type=\"String\">\n            <hint-collection name=\"TEXTAREA\" />\n        </field>\n        <field name=\"visits\" type=\"int\" />\n        <field name=\"priority\" type=\"int\">\n            <hint name=\"display-width\">20</hint>\n        </field>\n        <field name=\"lastPublishDate\" type=\"Date\" />\n        <field name=\"status\" type=\"int\" />\n        <field name=\"statusByUserId\" type=\"long\" />\n        <field name=\"statusByUserName\" type=\"String\" />\n        <field name=\"statusDate\" type=\"Date\" />\n    </model>\n    <model name=\"com.liferay.bookmarks.model.BookmarksFolder\">\n        ...\n    </model>\n</model-hints>\n```\n\nThe root-level element is `model-hints`. Model entities are represented by\n`model` sub-elements of the `model-hints` element. Each `model` element must\nhave a `name` attribute specifying the fully-qualified class name. Models\nhave `field` elements representing their entity's columns. Lastly, `field`\nelements must have a name and a type. Each `field` element's name and type maps\nto the name and type specified for the entity's column in the service module's\n`service.xml` file. Service Builder generates all these elements for you, based\non the `service.xml`. \n\nTo add hints to a field, add a `hint` child element. For example, you can add\na `display-width hint` to specify the pixel width to use in displaying the\nfield. The default pixel width is `350`. To show a `String` field with 50\npixels, you could nest a `hint` element named `display-width` and give it\na value of `50`. \n\nTo see the effect of a hint on a field,\n[run Service Builder](/docs/7-2/appdev/-/knowledge_base/a/running-service-builder)\nagain and\n[redeploy your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project).\nNote that changing `display-width` doesn't limit the number of characters a\nuser can enter into the `name` field; it only controls the field's width in the\nAlloyUI input form. \n\nTo configure the maximum size of a model field's database column (i.e., the\nmaximum number of characters that can be saved for the field), use the \n`max-length` hint. The default `max-length` value is `75` characters. If you\nwant the `name` field to persist up to 100 characters, add a `max-length` hint \nto that field: \n\n```xml\n<field name=\"name\" type=\"String\">\n    <hint name=\"display-width\">50</hint>\n    <hint name=\"max-length\">100</hint>\n</field>\n```\n\nRemember to run Service Builder and redeploy your project after updating the \n`portlet-model-hints.xml` file. \n\n## Model Hint Types\n\nSo far, you've seen a few different hints. The following table describes the\nportlet model hints available for use.\n\n**Model Hint Values and Descriptions**\n\n|      Name       | Value Type | Description | Default |\n|-----------------|:--------|:---------- |:-----|\n| `auto-escape`       | boolean | sets whether text values should be escaped via `HtmlUtil.escape` | true |\n| `autoSize`          | boolean | displays the field in a for scrollable text area | false |\n| `day-nullable`      | boolean | allows the day to be null in a date field | false |\n| `default-value`     | String  | sets the default value of the form field rendered using the aui taglib | (empty String) |\n| `display-height`    | integer | sets the display height of the form field rendered using the aui taglib | 15 |\n| `display-width`     | integer | sets the display width of the form field rendered using the aui taglib | 350 |\n| `editor`            | boolean | sets whether to provide an editor for the input | false |\n| `max-length`        | integer | sets the maximum column size for SQL file generation | 75 |\n| `month-nullable`    | boolean | allows the month to be null in a date field | false |\n| `secret`            | boolean | sets whether to hide the characters input by the user | false |\n| `show-time`         | boolean | sets whether to show the time along with the date | true |\n| `upper-case`        | boolean | converts all characters to upper case | false |\n| `year-nullable`     | boolean | allows a date field's year to be null | false |\n| `year-range-delta`  | integer | specifies the number of years to display from today's date in a date field rendered with the aui taglib | 5 |\n| `year-range-future` | boolean | sets whether to include future dates | true |\n| `year-range-past`   | boolean | sets whether to include past dates | true |\n\n<!--\nI wanted to get a complete list of model hints used in portal. I found the\nModelHints interface and then searched for where (*.java, *.js, *.jsp) it was\nreferenced. It led me to portal-web/docroot/html/taglib/ui/input_field/page.jsp.\nI found some more hints and added them to the table above.\n\nI'm not sure what the `check-tab` hint does. Would be worth asking the UI team.\n\n- Jim -->\n\n| **Note**: The aui taglib is fully supported and not related to AlloyUI (the\n| JavaScript library) that's deprecated.\n\n| **Note**: You can use a mix of Clay and aui tags in a form. Model hints,\n| however, affect aui tags only.\n\nNote that Liferay has its own model hints file (`portal-model-hints.xml`). It's\nin `portal-impl.jar`'s `META-INF` folder. This file contains many hint examples,\nso you can reference it when creating `portlet-model-hints.xml` files. \n\n## Default Hints\n\nYou can use the `default-hints` element to define a list of hints to apply to \nevery field of a model. For example, adding the following element inside a\nmodel element applies a `display-width` of 300 pixels to each field: \n\n```xml\n<default-hints>\n    <hint name=\"display-width\">300</hint>\n</default-hints>\n```\n\n## Hint Collections\n\nYou can define `hint-collection` elements inside the `model-hints` root-level\nelement to define a list of hints to apply together. A hint collection must have\na name. For example, Liferay's `portal-model-hints.xml` defines the following\nhint collections:\n\n```xml\n<hint-collection name=\"CLOB\">\n    <hint name=\"max-length\">2000000</hint>\n</hint-collection>\n<hint-collection name=\"EDITOR\">\n    <hint name=\"editor\">true</hint>\n    <hint name=\"max-length\">2000000</hint>\n</hint-collection>\n<hint-collection name=\"EMAIL-ADDRESS\">\n    <hint name=\"max-length\">254</hint>\n</hint-collection>\n<hint-collection name=\"HOSTNAME\">\n    <hint name=\"max-length\">200</hint>\n</hint-collection>\n<hint-collection name=\"SEARCHABLE-DATE\">\n    <hint name=\"month-nullable\">true</hint>\n    <hint name=\"day-nullable\">true</hint>\n    <hint name=\"year-nullable\">true</hint>\n    <hint name=\"show-time\">false</hint>\n</hint-collection>\n<hint-collection name=\"TEXTAREA\">\n    <hint name=\"display-height\">105</hint>\n    <hint name=\"display-width\">500</hint>\n    <hint name=\"max-length\">4000</hint>\n</hint-collection>\n<hint-collection name=\"URL\">\n    <hint name=\"max-length\">4000</hint>\n</hint-collection>\n```\n\nYou can apply a hint collection to a model field by referencing the hint\ncollection's name. For example, if you define a `SEARCHABLE-DATE` collection\nlike the one above in your `model-hints` element, you can apply it to your \nmodel's date field by using a `hint-collection` element that references the \ncollection by its name:\n\n```xml\n<field name=\"date\" type=\"Date\">\n    <hint-collection name=\"SEARCHABLE-DATE\" />\n</field>\n```\n\nSuppose you want to use a couple of model hints in your project. Start by\nproviding users with an editor for filling in their comment fields. To apply\nthe same hint to multiple entities, define it as a hint collection. Then\nreference the hint collection in each entity. \n\nTo define a hint collection, add a `hint-collection` element inside the\n`model-hints` root element in your `portlet-model-hints.xml` file. For example:\n\n```xml\n<hint-collection name=\"COMMENT-TEXTAREA\">\n    <hint name=\"display-height\">105</hint>\n    <hint name=\"display-width\">500</hint>\n    <hint name=\"max-length\">4000</hint>\n</hint-collection>\n```\n\nTo reference a hint collection for a specific field, add the `hint-collection`\nelement inside the field's `field` element:\n\n```xml\n<field name=\"comment\" type=\"String\">\n    <hint-collection name=\"COMMENT-TEXTAREA\" />\n</field>\n```\n\nAfter defining hint collections and adding hint collection references, rebuild\nyour services, redeploy your project, and check that the hints defined in your\nhint collection have taken effect. \n\nNice work! Now you can not only influence how your model's input fields are\ndisplayed, but you can also can set its database table column sizes. You can\norganize hints, insert individual hints directly into your fields, apply a set\nof default hints to all of a model's fields, or define collections of hints to\napply at either of those scopes. \n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/13-configuring-service-properties/01-configuring-service-properties-intro.markdown",
    "content": "---\nheader-id: configuring-service-properties\n---\n\n# Configuring service.properties\n\n[TOC levels=1-4]\n\nService Builder generates a `service.properties` file in your `*-service`\nmodule's `src/main/resources` folder. @product@ uses this file's properties\nto alter your service's database schema. You should not modify this file\ndirectly, but rather make any necessary property overrides in a\n`service-ext.properties` file in that same folder.\n\nHere are some of the properties the `service.properties` file includes:\n\n- `build.namespace`: This is the\n  [namespace you defined in your `service.xml`](/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file).\n  Liferay distinguishes different modules from each other using their\n  namespaces.\n- `build.number`: Liferay distinguishes your module's different Service Builder \n  builds. Each time you deploy a distinct Service Builder build to Liferay,\n  Liferay increments this number.\n- `build.date`: This is the time of your module's latest Service Builder build.\n- `include-and-override`: The default value of this property defines\n  `service-ext.properties` as an override file for `service.properties`.\n\n| **Note**: In Liferay Portal 6.x Service Builder portlets, the\n| `build.auto.upgrade` property in `service.properties` applies Liferay Service\n| schema changes upon rebuilding services and redeploying the portlets. This\n| property was deprecated in Liferay 7.0.\n| \n| The Build Auto Upgrade feature is now different and is set in a global property\n| `schema.module.build.auto.upgrade` in the file\n| `[Liferay_Home]/portal-developer.properties`. \n\nAwesome! You now have all the tools necessary to set up your own\n`service-ext.properties` file.\n\n## Related Topics\n\n[Service Builder?](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n\n[Creating Local Services](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/98-connecting-service-builder-to-an-external-database/01-connecting-service-builder-to-an-external-database-intro.markdown",
    "content": "---\nheader-id: connecting-service-builder-to-an-external-database\n---\n\n# Connecting Service Builder to an External Database\n\n[TOC levels=1-4]\n\nIf you want to use a database separate from @product@'s, follow these steps:\n\n1. Specify your database and a data source name in your `service.xml`.\n\n1. Create the database manually.\n\n1. Define the data source.\n\n1. Connect your Service Builder module to the data source. \n\n1. Run Service Builder. \n\nThere are two different ways to create the connection:\n\n1.  **`DataSourceProvider`:** This approach involves implementing a\n    [`DataSourceProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/jdbc/DataSourceProvider.html)\n    [`ServiceProviderInterface`](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html)\n    (SPI). This way requires the fewest files and steps and works regardless of\n    whether your Service Builder module uses the `ds` or `spring`\n    [dependency injector](/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information#dependency-injector).\n\n2.  **Spring Beans:** Configure the connection using Spring XML files. This \n    approach only works with Service Builder modules that use the `spring` \n    [dependency injection option](/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information#dependency-injector). \n\n| **Note:** All entities defined in a Service Builder module's\n| [`service.xml`](/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file)\n| file are bound to the same data source. Binding different entities to\n| different data sources requires defining the entities in separate Service\n| Builder modules and configuring each of the modules to use a different data\n| source.\n\n| **Warning:** If your Service Builder services require nested transactions, \n| using an external data source may not be appropriate. Transactions between\n| separate data sources cannot be fully nested. Rollbacks may not propagate\n| between services that use an external data source and @product@ services (or\n| another app's services) that use a different data source. \n\nSince `DataSourceProvider` is the easiest, most versatile approach, it's\nexplained first. \n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/98-connecting-service-builder-to-an-external-database/02-datasource-connect-using-a-datasourceprovider.markdown",
    "content": "---\nheader-id: connecting-the-data-source-using-a-datasourceprovider\n---\n\n# Connecting the Data Source Using a DataSourceProvider\n\n[TOC levels=1-4]\n\nConnecting to an external database by creating and registering a\n`DataSourceProvider` as a JDK\n[`ServiceProviderInterface`](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html)\n(SPI) is the easiest way. This approach works regardless of whether your Service\nBuilder module uses the `ds` or `spring`\n[dependency injection option](/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information#dependency-injector)\nand it requires the fewest files and steps. \n\n| **Note:** All entities defined in a Service Builder module's\n| [`service.xml`](/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file) \n| file are bound to the same data source. Binding different entities to\n| different data sources requires defining the entities in separate Service\n| Builder modules and configuring each of the modules to use a different data\n| source.\n\n| **Warning:** If your Service Builder services require nested transactions, \n| using an external data source may not be appropriate for you. Transactions\n| between separate data sources cannot be fully nested. Rollbacks may not\n| propagate between a module that uses an external data source and @product@\n| services (or another app's services) that use a different data source. \n\nHere are the steps:\n\n1.  In your `service.xml` file, specify the same arbitrary data source name for all of the entities, a unique table name for each entity, and  a database column name for each column. Here's an example: \n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\"\n        \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n    <service-builder dependency-injector=\"spring\" package-path=\"com.liferay.example\" >\n        <namespace>TestDB</namespace>\n        <entity local-service=\"true\" name=\"Foo\" table=\"testdata\" data-source=\"extDataSource\"\n                remote-service=\"false\" uuid=\"false\">\n               <column name=\"id\" db-name=\"id\" primary=\"true\" type=\"long\" />\n               <column name=\"foo\" db-name=\"foo\" type=\"String\" />\n               <column name=\"bar\" db-name=\"bar\" type=\"long\" />\n        </entity>\n    </service-builder>\n    ```\n\n    Note the example's `<entity>` tag attributes: \n\n    *`data-source`*: The `liferayDataSource` alias `ext-spring.xml` specifies.\n\n    *`table`*: Your entity's database table. \n\n    Also note that your entity's `<column>`s must have a *`db-name`* attribute set to the column name.\n\n1. [Manually create the database](https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay/configuring-a-database.html) you defined in your `service.xml`.\n\n1.  Define the data source. One way is to use\n    [portal properties](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\n    in a `portal-ext.properties` file. Distinguish your data source from\n    Liferay's default data source by giving it a prefix other than\n    `jdbc.default.`. This example uses prefix `jdbc.ext.`. \n\n    ```properties\n    jdbc.ext.driverClassName=org.mariadb.jdbc.Driver\n    jdbc.ext.password=userpassword\n    jdbc.ext.url=jdbc:mariadb://localhost/external?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n    jdbc.ext.username=yourusername\n    ```\n\n1.  Restart your server if you defined your data source using portal properties.\n\n1.  Connect your Service Builder module to the data source by implementing the\n    [`DataSourceProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/jdbc/DataSourceProvider.html)\n    interface. Since the `DataSourceProvider` must be visible to your\n    `*-service` module class loader, it's common to put the `DataSourceProvider`\n    in the `*-service` module. \n\n    This example uses\n    [`DataSourceFactoryUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/jdbc/DataSourceFactoryUtil.html)\n    to create a data source from\n    [portal properties](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\n    that have the prefix `jdbc.ext.`. \n\n    ```java\n    package com.liferay.external.data.source.test.internal;\n    \n    import com.liferay.portal.kernel.dao.jdbc.DataSourceFactoryUtil;\n    import com.liferay.portal.kernel.dao.jdbc.DataSourceProvider;\n    import com.liferay.portal.kernel.util.PropsUtil;\n\n    import javax.sql.DataSource;\n\n    public class DataSourceProviderImpl implements DataSourceProvider {\n\n    \t@Override\n    \tpublic DataSource getDataSource() {\n    \t\ttry {\n    \t\t\treturn DataSourceFactoryUtil.initDataSource(\n    \t\t\t\tPropsUtil.getProperties(\"jdbc.ext.\", true));\n    \t\t}\n    \t\tcatch (Exception e) {\n    \t\t\tthrow new RuntimeException(e);\n    \t\t}\n    \t}\n\n    }\n    ```\n\n1.  Register the implementation as a JDK\n    [`ServiceProviderInterface`](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html)\n    (SPI) in a\n    `/META-INF/services/com.liferay.portal.kernel.dao.jdbc.DataSourceProvider`\n    file in your `*-service` module. For example, this file registers the\n    `DataSourceProvider` implementation from the previous step.\n\n    ```\n    com.liferay.external.data.source.test.internal.DataSourceProviderImpl\n    ````\n\n1.  [Run Service Builder](/docs/7-2/appdev/-/knowledge_base/a/running-service-builder). \n\n1.  [Deploy](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project)\n    your `-service` module. If your `DataSourceProvider` is in a \n    different project, deploy it too. \n\nCongratulations! Your module's Service Builder services are persisting data to\nyour external data source. \n\n## Related Topics\n\n[Connecting to JNDI Data Sources](/docs/7-2/appdev/-/knowledge_base/a/connecting-to-data-sources-using-jndi)\n\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n\n[Business Logic with Service Builder](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/98-connecting-service-builder-to-an-external-database/03-datasource-connect-using-spring-beans.markdown",
    "content": "---\nheader-id: connecting-the-data-source-using-spring-beans\n---\n\n# Connecting the Data Source Using Spring Beans\n\n[TOC levels=1-4]\n\nSometimes you want to use a database other than @product@'s. To do this, its\ndata source must be defined in `portal-ext.properties` or configured as a JNDI\ndata source on the app server. Here you'll connect\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\nto a data source using Spring XML files. This approach only works with Service\nBuilder modules that use the `spring`\n[dependency injection option](/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information#dependency-injector).\nHere are the steps:\n\n1. Specify your database and a data source name in your `service.xml`.\n\n1. Create the database manually.\n\n1. Define the data source.\n\n1. Create a Spring bean that points to the data source.\n\n1. Run Service Builder.\n\n| **Note:** All entities defined in a Service Builder module's\n| [`service.xml`](/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file)\n| file are bound to the same data source. Binding different entities to\n| different data sources requires defining the entities in separate Service\n| Builder modules and configuring each of the modules to use a different data\n| source.\n\n| **Warning:** If your Service Builder services require nested transactions,\n| using an external data source may not be appropriate for you. Transactions\n| between separate data sources cannot be fully nested. Rollbacks may not\n| propagate between a module that uses an external data source and @product@\n| services (or another app's services) that use a different data source.\n\n| **Important:** Connecting to an external data source using JNDI is broken in\n| Portal CE 7.2 GA1 and GA2, and in DXP 7.2 releases prior to FP5/SP2. See\n| [LPS-107733](https://issues.liferay.com/browse/LPS-107733) for details.\n\n## Specify Your Database and a Data Source Name in Your `service.xml`\n\nIn your `service.xml` file, specify the same arbitrary data source name for all of the entities, a unique table name for each entity, and  a database column name for each column. Here's an example:\n\n```xml\n<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\"\n    \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"spring\" package-path=\"com.liferay.example\" >\n    <namespace>TestDB</namespace>\n    <entity local-service=\"true\" name=\"Foo\" table=\"testdata\" data-source=\"extDataSource\"\n            remote-service=\"false\" uuid=\"false\">\n           <column name=\"id\" db-name=\"id\" primary=\"true\" type=\"long\" />\n           <column name=\"foo\" db-name=\"foo\" type=\"String\" />\n           <column name=\"bar\" db-name=\"bar\" type=\"long\" />\n    </entity>\n</service-builder>\n```\n\nNote the example's `<entity>` tag attributes:\n\n*`data-source`*: The `liferayDataSource` alias `ext-spring.xml` specifies.\n\n*`table`*: Your entity's database table.\n\nAlso note that your entity's `<column>`s must have a *`db-name`* attribute set to the column name.\n\n## Create the Database Manually\n\n[Create the database](https://learn.liferay.com/dxp/latest/en/installation-and-upgrades/installing-liferay/configuring-a-database.html) per the database specification in your `service.xml`.\n\nNext, use portal properties to set your data source.\n\n## Define the Data Source\n\nIf the application server defines the data source using JNDI, skip this step.\nOtherwise, specify the data source in a\n[`portal-ext.properties` file](/docs/7-2/deploy/-/knowledge_base/d/portal-properties).\nDistinguish it from Liferay's default data source by giving it a prefix other\nthan `jdbc.default.`. This example uses prefix `jdbc.ext.`:\n\n```properties\njdbc.ext.driverClassName=org.mariadb.jdbc.Driver\njdbc.ext.password=userpassword\njdbc.ext.url=jdbc:mariadb://localhost/external?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\njdbc.ext.username=yourusername\n```\n\nRestart your server if you defined your data source using portal properties.\n\n## Connect Your Service Builder Module to the Data Source Via a Spring Bean\n\nTo do this, create a parent context extension (e.g.,`ext-spring.xml`) in your\n`*-service` module's `src/main/resources/META-INF/spring` folder or in\nyour traditional portlet's `WEB-INF/src/META-INF` folder. Create this\nfolder if it doesn't exist already.\n\nDefine the following elements:\n\n1.  A data source factory Spring bean for the data source. It's different based\n    on the type.\n\n    -   **JNDI**: Specify an arbitrary property prefix and prepend the prefix\n        to a JNDI name property key. Here's an example:\n\n    ```xml\n    <bean class=\"com.liferay.portal.dao.jdbc.spring.DataSourceFactoryBean\"\n        id=\"liferayDataSourceFactory\">\n        <property name=\"propertyPrefix\" value=\"custom.\" />\n        <property name=\"properties\">\n            <props>\n                <prop key=\"custom.jndi.name\">jdbc/externalDataSource</prop>\n            </props>\n        </property>\n    </bean>\n    ```\n\n    -   **Portal Properties**: Specify a property prefix that matches the\n        prefix (e.g., `jdbc.ext.`) you used in `portal-ext.properties`.\n\n    ```xml\n    <bean class=\"com.liferay.portal.dao.jdbc.spring.DataSourceFactoryBean\"\n        id=\"liferayDataSourceFactory\">\n        <property name=\"propertyPrefix\" value=\"jdbc.ext.\" />\n    </bean>\n    ```\n\n2.  A Liferay data source bean that refers to the data source factory Spring\n    bean.\n\n3.  An alias for the Liferay data source bean. Name the alias after the data source name you specified in the `service.xml`.\n\n    Here's an example `ext-spring.xml` that points to a JNDI data source:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n\n    <beans default-destroy-method=\"destroy\" default-init-method=\"afterPropertiesSet\"\n       xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\">\n\n       <!-- To define an external data source, the liferayDataSource Spring bean\n           must be overridden. Other default Spring beans like liferaySessionFactory\n           and liferayTransactionManager may optionally be overridden.\n\n           liferayDataSourceFactory refers to the data source configured on the\n           application server. -->\n       <bean class=\"com.liferay.portal.dao.jdbc.spring.DataSourceFactoryBean\"\n           id=\"liferayDataSourceFactory\">\n           <property name=\"propertyPrefix\" value=\"custom.\" />\n           <property name=\"properties\">\n               <props>\n                   <prop key=\"custom.jndi.name\">jdbc/externalDataSource</prop>\n               </props>\n           </property>\n       </bean>\n\n       <!-- The data source bean refers to the factory to access the data source.\n       -->\n       <bean\n           class=\"org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy\"\n           id=\"liferayDataSource\">\n           <property name=\"targetDataSource\" ref=\"liferayDataSourceFactory\" />\n       </bean>\n\n       <!-- In service.xml, we associated our entity with the extDataSource. To\n           associate the extDataSource with our overridden liferayDataSource, we define\n           this alias. -->\n       <alias alias=\"extDataSource\" name=\"liferayDataSource\" />\n    </beans>\n    ```\n\nThe `liferayDataSourceFactory` above refers to a JNDI data source named\n`jdbc/externalDataSource`. If the data source is in a `portal-ext.properties`\nfile, the bean requires only a `propertyPrefix` property that matches the data\nsource property prefix.\n\nThe data source bean `liferayDataSource` is overridden with one that refers to\nthe `liferayDataSourceFactory` bean. The override affects this bundle (module or\n[Web Application Bundle](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator))\nonly.\n\nThe alias `extDataSource` refers to the `liferayDataSource` data source bean.\n\n| **Important:** The `alias` element's `alias` attribute value must match the\n| data source name specified in the `service.xml`. For example, the alias\n| attribute value above is `extDataSource`.\n\n| **Note**: To use an external data source in multiple Service Builder\n| bundles, you must override the `liferayDataSource` bean in each bundle.\n\n## Run Service Builder\n\n[Run Service Builder](/docs/7-2/appdev/-/knowledge_base/a/running-service-builder) and [deploy](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project) your `-service` module.\nNow your Service Builder services use the data source. You can\n[use the services in your business logic](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder)\nas you always have regardless of the underlying data source.\n\nCongratulations! You've connected Service Builder to your external data source.\n\n## Related Topics\n\n[Sample Service Builder Application Using External Database via JNDI](/docs/7-2/reference/-/knowledge_base/r/service-builder-application-using-external-database-via-jndi)\n\n[Sample Service Builder Application Using External Database via JDBC](/docs/7-2/reference/-/knowledge_base/r/service-builder-application-using-external-database-via-jdbc)\n\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n\n[Business Logic with Service Builder](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/03-creating-o-r-map/99-migrating-a-service-builder-module-from-spring-di-to-osgi-ds/01-migrating-a-service-builder-module-from-spring-di-to-osgi-ds-intro.markdown",
    "content": "---\nheader-id: migrating-a-service-builder-module-from-spring-di-to-osgi-ds\n---\n\n# Migrating a Service Builder Module from Spring DI to OSGi DS\n\n[TOC levels=1-4]\n\nPrior to @product@ 7.2, Service Builder modules could only use Spring for\ndependency injection (DI). Now \n[OSGi Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services) (DS) is\nthe default dependency injection mechanism for new Service Builder modules. It's\neasier to learn and fosters loose coupling between services. If you have an\nexisting Service Builder module that uses Spring DI, you can modify it to use\nDS. \n\nHere are the conversion steps:\n\n1.  Prepare your project for DS \n\n2.  Update your Spring bean classes \n\n3.  Resolve any circular dependencies \n\nNow prepare your project. \n\n## Step 1: Prepare Your Project for DS\n\nPrepare your project's metadata, dependencies, and `service.xml` for DS. \n\n1.  Enable the DS annotation option for your inherited dependencies by \n    adding this line to your `bnd.bnd` file:\n\n    ```\n    -dsannotations-options: inherit\n    ```\n\n2.  Since DS Service Builder modules use the AOP API, add it as a compile \n    dependency in `build.gradle`: \n\n    ```\n    compileOnly group: \"com.liferay:com.liferay.portal.aop.api\", version: \"1.0.0\"\n    ```\n\n3.  Add the `dependency-injector=\"ds\"` attribute to your `service.xml` \n    file's `<service-builder>` element: \n\n    ```xml\n    <service-builder dependency-injector=\"ds\" >\n    ```\n\n## Step 2: Update Your Spring Bean Classes\n\nSome of your \n[non-generated Spring bean classes](/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder)\nmust be updated to use DS. \n\n1.  Add the [`@Component`](https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html)\n    annotation to your `*LocalServiceImpl`, `*ServiceImpl`, and `*FinderImpl`\n    classes. \n\n2.  If the class implements a `*Finder` interface, declare the component as \n    that service type. Example: \n\n    ```java\n    @Component(service = MyFinder.class)\n    ```\n\n3.  If the class implements a remote or local service, declare the component\n    as the `com.liferay.portal.aop.AopService` service type. Example:\n\n    ```java\n    @Component(service = AopService.class)\n    ```\n\n4.  If it's a remote service (i.e., `-ServiceImpl` instead of\n    `-LocalServiceImpl`), enable JSON web services by setting these \n    properties in your `@Component` annotation:\n\n    -   `json.web.service.context.name`\n    -   `json.web.service.context.path`\n\n    Set them to the same values as the properties in your remote service\n    interface's\n    [`@OSGiBeanProperties`](https://docs.liferay.com/ce/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/spring/osgi/OSGiBeanProperties.html)\n    annotation. \n\n5.  If it's a local service, enable [`PersistedModelLocalService`](https://docs.liferay.com/ce/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/service/PersistedModelLocalService.html)\n    service tracking by setting the `@Component` property\n    `model.class.name` to the service entity's fully qualified class name. \n\n6.  Replace all the `@ServiceReference` and `@BeanReference` field annotations \n    with the DS\n    [`@Reference`](https://osgi.org/javadoc/r6/cmpn/org/osgi/service/component/annotations/Reference.html)\n    annotation. \n\n7.  Use the `@Reference` field annotation to access any other services you need.\n \n8.  [Run Service Builder](/docs/7-2/appdev/-/knowledge_base/a/running-service-builder)\n    to regenerate the interfaces based on your implementation changes. \n\n9.  Replace the following methods:\n\n    -   `afterPropertiesSet() {...}` &rarr; `activate() {...}` and annotate with\n        [`@Activate`](https://osgi.org/javadoc/r6/cmpn/org/osgi/service/component/annotations/Activate.html).\n\n    -   `destroy() {...}` &rarr; `deactivate() {...}` and annotate with \n        [`@Deactivate`](https://osgi.org/javadoc/r6/cmpn/org/osgi/service/component/annotations/Deactivate.html). \n\nNext, you'll work out any remaining references you need. \n\n## Step 3: Resolve Any Circular Dependencies\n\nCircular dependencies occur in a module if two or more of its DS services refer\nto each another (either directly or indirectly). A direct reference occurs, for\nexample, when service `A` references service `B`, and `B` references `A`. Here's\nwhat the service components might look like:\n\n`AImpl.java`:\n\n```java\n@Component(service = A.class)\npublic class AImpl implements A {\n    @Reference\n    private B _b;\n}\n```\n\n`BImpl.java`:\n\n```java\n@Component(service = B.class)\npublic class BImpl implements B {\n    @Reference\n    private A _a;\n} \n```\n\n`AImpl` and `BImpl` directly depend on each other.  This circular dependency\nprevents each service component from resolving. DS service activation requires\nthat all of a service's dependencies (references) be satisfied. \n\n**Note:** Service resolution is independent and separate from module (OSGi \nbundle) resolution:\n\n-   Module resolution is determined by the module's manifest.\n-   Modules resolve before any of their services become active.\n-   Services inside a module cannot activate if the module cannot resolve.\n-   A module can resolve even if none of its services activate.\n\nThe example above demonstrates a very small circle, composed of only two\nclasses, but a circle can compose more classes. For example, `A` references `B`,\n`B` references `C`, `C` references `A`. Detecting and resolving such a\ndependency can be complicated. \n\nThere is no general, correct way to detect and resolve circular dependencies;\ncases vary. However, Liferay provides tools that facilitate detecting circular\ndependencies and examining the DS service components involved.\n\n-   `system:check`: This\n    [Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\n    command provides several checks, including one that detects inactive\n    service components whose required references are unresolved.\n\n-   `scr:info [component]`: Execute this\n    [Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\n    command on an unresolved component to report its unresolved references. \n\n| **Note:** Service resolution in DS dependency injection (DI) is different \n| than in services that use Liferay's Spring DI. In the latter case, all Spring\n| beans in the same module act as a single bundle of services that activate\n| together and can bind together before activation. DS doesn't have this \n| feature. With DS, each component in a module is its own service and must\n| resolve on its own. \n\nCongratulations on converting your service module to use Declarative Services. \n\n## Related Topics \n\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n\n[Understanding the Code Service Builder Generates](/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder)\n\n[Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/04-business-logic-with-service-builder/01-business-logic-with-service-builder-intro.markdown",
    "content": "---\nheader-id: business-logic-with-service-builder\n---\n\n# Business Logic with Service Builder\n\n[TOC levels=1-4]\n\nOnce you've\n[defined your application's entities](/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file)\nand\n[run Service Builder](/docs/7-2/appdev/-/knowledge_base/a/running-service-builder)\nto generate your service and persistence layers, you can begin adding business\nlogic. Each entity generated by Service Builder contains a model implementation,\nlocal service implementation, and optionally a remote service implementation\nclass. Your application's business logic can be implemented in these classes.\nThe generated service layer contains default methods that call CRUD operations\nfrom the persistence layer. Once you've added your business logic, running Service\nBuilder again regenerates your application's interfaces and makes your new logic\navailable for invocation.\n\nThe heart of your service is its `*LocalServiceImpl` class. This class is your\nentity's local service extension point. Local services are invoked from your\napplication or by other applications running on the same instance as your\napplication. \n\nCreating services takes these steps: \n\n1.  Deciding to Create Local and Remote Services.\n\n2.  Implementing the `add` Method.\n\n3.  Implementing the `update` and `delete` Methods.\n\n4.  Implementing `get` and `get*Count` Methods\n\n5.  Implementing Other Business Logic\n\n6.  Integrating with Liferay's Services.\n\nStart with deciding the service types you need. \n\n[Defining your object model](/docs/7-2/appdev/-/knowledge_base/a/creating-the-service-xml-file)\ninvolves choosing whether to generate local and or remote service interfaces.\nLocal services can only be invoked from the Liferay server on which they're\ndeployed. Remote services are accessible to clients outside of the Liferay\nserver. Before implementing local or remote services, consider the best\npractices described here:\n\n1.  If you plan to have remote services, enable local services too.\n\n2.  Implement your business logic in `*LocalServiceImpl`. \n\n3.  Create corresponding remote services methods in your \n    `*ServiceImpl`.\n\n4.  Use the remote service methods to call the local service, wrapping the calls\n    in permission checks. \n\n5.  In your application, call only the remote services. This ensures that\n    your service methods are secured and that you don't have to duplicate\n    permissions code.\n\nIf you are turning on local or remote services in your `service.xml` file just\nnow, make sure to\n[run Service Builder](/docs/7-2/appdev/-/knowledge_base/a/running-service-builder)\nagain to generate the service interfaces. \n\nNow you're ready to implement your business logic. \n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/04-business-logic-with-service-builder/02-creating-local-services/01-implementing-an-add-method-intro.markdown",
    "content": "---\nheader-id: implementing-an-add-method\n---\n\n# Implementing an Add Method\n\n[TOC levels=1-4]\n\nYour `*LocalServiceImpl` represents your service layer, where you create the\nbusiness logic that operates on your application's data and then calls the\npersistence layer to persist, retrieve, or delete your data, using the object\nmodel defined in `service.xml`. \n\nOne of the first methods you'll likely implement is one that creates entities.\nLiferay's convention is to implement this in an `add*` method, where the part\nafter `add` is the entity name (or a shortened version of it). Here are the\nsteps for implementing an `add*` method:\n\n1.  Declare an `add*` method with parameters for creating the entity.\n\n2.  Validate the parameters.\n\n3.  Generate a primary key.\n\n4.  Create an entity instance.\n\n5.  Populate the entity attributes.\n\n6.  Persist the entity.\n\n7.  Return the entity instance.\n\nThis article refers to the Guestbook application's `addGuestbookEntry` method\nfrom `GuestbookEntryLocalServiceImpl`. To keep things simple, we have excluded\nthe code that integrates with Liferay services, such as assets, social\nbookmarks, and more. \n\nHere's the Guestbook application's `addGuestbookEntry` method:\n\n```java\npublic GuestbookEntry addEntry(long userId, long guestbookId, String name, String email, String message,\n        ServiceContext serviceContext) throws PortalException {\n\n    long groupId = serviceContext.getScopeGroupId();\n\n    User user = userLocalService.getUserById(userId);\n\n    Date now = new Date();\n\n    validate(name, email, message);\n\n    long entryId = counterLocalService.increment();\n\n    GuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\n    entry.setUuid(serviceContext.getUuid());\n    entry.setUserId(userId);\n    entry.setGroupId(groupId);\n    entry.setCompanyId(user.getCompanyId());\n    entry.setUserName(user.getFullName());\n    entry.setCreateDate(serviceContext.getCreateDate(now));\n    entry.setModifiedDate(serviceContext.getModifiedDate(now));\n    entry.setExpandoBridgeAttributes(serviceContext);\n    entry.setGuestbookId(guestbookId);\n    entry.setName(name);\n    entry.setEmail(email);\n    entry.setMessage(message);\n\n    guestbookEntryPersistence.update(entry);\n\n    // Calls to other Liferay frameworks go here\n\n    return entry;\n}\n```\n\nThis method uses the parameters to create `GuestbookEntry`. It validates the\nparameters, creates an entry with a generated entry ID (primary key), populates\nthe entry, persists the entry, and returns it. You can refer to this method as\nyou create your own `add*` method. Note that there's no real business logic\nhere; it's a simple application that takes data the user entered, validates it,\nand then persists it to the database. \n\n## Step 1: Declare an add method with parameters for creating the entity\n\nCreate a public method for *adding* (creating) your application's entity. Make\nit a public method that returns the entity it creates. \n\n    public [ENTITY] add[ENTITY](...) {\n        \n    } \n\nFor example, here's the `addEntry` method signature:\n\n```java\npublic GuestbookEntry addEntry(long userId, long guestbookId, \n    String name, String email, String message, \n    ServiceContext serviceContext) throws PortalException {\n    ...\n    }\n```\n\nThis method specifies all the parameters needed to create and populate a\n`GuestbookEntry` as you specified them in your `service.xml` file. It throws\na `PortalException` in case the parameters are invalid or a processing exception\noccurs (more on this in a later step). \n\nMake sure to account for primary keys of other related entities. For example,\nthe `addEntry` method above includes a parameter `long guestbookId` to associate\nthe new `GuestbookEntry` to a `Guestbook`. \n\n## Step 2:  Validate the parameters\n\nValidate the parameters as needed. You might need to make sure a parameter is\nnot empty or `null`, or that a parameter value is within a valid range. Throw a\n[`PortalException`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/exception/PortalException.html)\nor an extension of `PortalException` for any invalid parameters.\n\nFor example, the `addEntry` method invokes the following `validate` method to\ncheck if the URL parameter is `null`.\n\n```java\nprotected void validate(String name, String email, String entry) throws PortalException {\n\n    if (Validator.isNull(name)) {\n        throw new GuestbookEntryNameException();\n    }\n\n    if (!Validator.isEmailAddress(email)) {\n        throw new GuestbookEntryEmailException();\n    }\n\n    if (Validator.isNull(entry)) {\n        throw new GuestbookEntryMessageException();\n    }\n}\n```\n\nNext, generate a primary key for the entity instance you're creating. \n\n## Step 3: Generate a primary key\n\nEntities must each have a unique primary key. Liferay's\n[`CounterLocalService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/counter/kernel/service/CounterLocalService.html) \ngenerates them per entity. Every `*BaseLocalServiceImpl` has a\n`counterLocalService` field that references a `CounterLocalService` object for\nthe entity. Invoke the counter service's `increment` method to generate a\nprimary key for your entity instance.\n\n```java\nlong id = counterLocalService.increment();\n```\n\nNow you have a unique ID for your entity instance. Always generate primary keys\nin this way, as it ensures your code is compatible with all the databases\nLiferay supports.\n\n## Step 4: Create an entity instance\n\nThe `*Peristence` instance associated with your entity has a `create(long id)`\nmethod that constructs an entity instance with the given ID. Every\n`*BaseLocalServiceImpl` has a `*Persistence` field that references a\n`*Persistence` object for the entity. For example,\n`GuestbookEntryLocalServiceImpl` as a child of\n`GuestbookEntryLocalServiceBaseImpl` has a field `guestbookEntryPersistence`,\nwhich is a reference to a `GuestbookEntryPersistence` instance. \n\n    @Reference\n    protected GuestbookEntryPersistence guestbookEntryPersistence;\n\n`GuestbookEntryLocalServiceImpl`'s `addEntry` method creates a `GuestbookEntry`\ninstance using this call:\n\n    GuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\nTo create an instance of your entity, invoke the `create` method on the\n`*Persistence` field associated with the entity, making sure to pass in the\nentity primary key you generated in the previous step.\n\n     [ENTITY_NAME] entity = [ENTITY_NAME]Persistence.create(id);\n\nIt's time to populate the new entity instance. \n\n## Step 5: Populate the entity attributes\n\nUse the `add*` method parameter values and the entity's setter methods to\npopulate your entity's attributes. For example, here are the `GuestbookEntry`\nattribute assignments:\n\n```java\nentry.setUuid(serviceContext.getUuid());\nentry.setUserId(userId);\nentry.setGroupId(groupId);\nentry.setCompanyId(user.getCompanyId());\nentry.setUserName(user.getFullName());\nentry.setCreateDate(serviceContext.getCreateDate(now));\nentry.setModifiedDate(serviceContext.getModifiedDate(now));\nentry.setExpandoBridgeAttributes(serviceContext);\nentry.setGuestbookId(guestbookId);\nentry.setName(name);\nentry.setEmail(email);\nentry.setMessage(message);\n```\n\nNote that the `ServiceContext` is commonly used to carry an entity's UUID and\nthe `User` is associated to a company. \n\n## Step 6: Persist the entity\n\nIt's time to store the entity. Invoke the `*Persistence` field's `update`\nmethod, passing in the entity object. For example, here's how the new\n`GuestbookEntry` is persisted:\n\n```java\nguestbookEntryPersistence.update(entry);\n```\n\nYour entity is persisted for the application. \n\n## Step 7: Return the entity\n\nFinally, return the entity you just created so the caller can use it. \n\n[Run Service Builder](/docs/7-2/appdev/-/knowledge_base/a/running-service-builder)\nto propagate your new service method to the `*LocalService` interface. \n\nYou've implemented your local service's `add*` method to create and persist your\napplication's entities.\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/04-business-logic-with-service-builder/02-creating-local-services/02-implementing-an-update-method.markdown",
    "content": "---\nheader-id: implementing-update-and-delete-methods\n---\n\n# Implementing Update and Delete Methods\n\n[TOC levels=1-4]\n\nAfter you've implementing an\n[`add*` method](/docs/7-2/appdev/-/knowledge_base/a/implementing-an-add-method) for \ncreating service entities, you'll want to create\n[`update*`](#implementing-an-update-method)\nand\n[`delete*`](#implementing-a-delete-method)\nmethods for updating and deleting them. The main\ndifference between these and the `add*` method is they must know which entity\nthey're updating or deleting. \n\n## Implementing an Update Method\n\nAn `update*` method for a local service resembles an\n[`add*` method](/docs/7-2/appdev/-/knowledge_base/a/implementing-an-add-method)\nmost because it has parameters for setting entity attribute values. Create an\n`update*` method this way:\n\n1.  Declare an `update*` method with parameters for updating the entity.\n\n2.  Validate the parameters.\n\n3.  Retrieve the entity instance, if necessary.\n\n4.  Update the entity attributes.\n\n5.  Persist the updated entity.\n\n6.  Run Service Builder.\n\nThe following code snippets from \n`GuestbookEntryLocalServiceImpl`'s `updateEntry` method are helpful to examine. \n\n```java\npublic GuestbookEntry updateEntry(long userId, long guestbookId, long entryId, String name, String email, String message,\n        ServiceContext serviceContext) throws PortalException, SystemException {\n\n    Date now = new Date();\n\n    validate(name, email, message);\n\n    GuestbookEntry entry = getGuestbookEntry(entryId);\n\n    User user = userLocalService.getUserById(userId);\n\n    entry.setUserId(userId);\n    entry.setUserName(user.getFullName());\n    entry.setModifiedDate(serviceContext.getModifiedDate(now));\n    entry.setName(name);\n    entry.setEmail(email);\n    entry.setMessage(message);\n    entry.setExpandoBridgeAttributes(serviceContext);\n\n    guestbookEntryPersistence.update(entry);\n\n    // Integrate with Liferay frameworks here.\n\n    return entry;\n}\n```\n\nThis method has all the makings of a good `update*` method:\n\n- parameter for looking up the entity instance\n- parameters for updating the entity attributes\n- parameter validation\n- entity attribute updates\n- entity persistence\n- returns the entity instance\n\nRefer to the example method above as you follow the steps to create your own\n`update*` method. \n\n### Step 1: Declare an Update Method with Parameters for Updating the Entity\n\nCreate a public method for updating your application's entity. \n\n    public [ENTITY] update[ENTITY](...)\n        throws PortalException {\n        \n    } \n \nReplace `[ENTITY]` with your entity's name or nickname.  Create a parameter list\nthat satisfies the entity attributes you're updating. Include an entity instance\nparameter or an ID parameter for fetching the entity instance.\n\nFor example, the `GuestbookEntryLocalServiceImpl`'s `updateEntry` method\nsignature has an ID parameter (`entryId`) for fetching the `GuestbookEntry`\nentity instance. Also it has parameters `folderId`, `name`, `url`, and\n`description` for updating the `GuestbookEntry`'s respective attributes. \n\n```java\npublic GuestbookEntry updateEntry(long userId, long guestbookId, long entryId, String name, String email, String message,\n        ServiceContext serviceContext) throws PortalException, SystemException {\n```\n\nNote, user ID, group ID, and service context parameters are useful for\nintegrating with Liferay's services. More on that later. \n\n### Step 2: Validate the Parameters\n\nSimilar to validating the\n[`add*` method](/docs/7-2/appdev/-/knowledge_base/a/implementing-an-add-method)\nparameters, validate your `update*` parameters. Your `add*` and `update*`\nmethods might be able to use the same validation code. Throw a \n[`PortalException`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/exception/PortalException.html)\nor an extension of `PortalException` for any invalid parameters. \n\n### Step 3: Retrieve the Entity Instance\n\nIf you're passing in an entity instance, you can update it directly. Otherwise,\npass in the entity ID (the primary key).  The `*Persistence` class Service\nBuilder injects into `*BaseLocalServiceImpl` classes has a\n`findByPrimaryKey(long)` method that retrieves instances by ID. For example, the\n`GuestbookEntryLocalServiceImpl` retrieves the `GuestbookEntry` with the primary\nkey `entryId`. \n\n    GuestbookEntry entry = guestbookEntryPersistence.findByPrimaryKey(\n        entryId);\n \nInvoke the `findByPrimaryKey(long id)` method of your `*Persistence` class to\nretrieve the entity instance that matches your primary key parameter. \n\n     [ENTITY] entity = [ENTITY]Persistence.findByPrimaryKey(id);\n\nIt's time to update the entity attributes. \n\n### Step 4: Update the Entity Attributes\n\nInvoke the entity's setter methods to replace its attribute values. \n\n### Step 5: Persist and Return the Updated Entity Instance\n\nPersist the updated entity to the database and return the instance to the\ncaller. \n\n    [ENTITY]Persistence.update(entity);\n    \n    ...\n\n    return entity;\n\n### Step 6: Run Service Builder\n\nFinally, run Service Builder to propagate your new service method to the\n`*LocalService` interface. \n\nYou've created a service method to update your entity. If you thought that was\neasy, implementing a `delete*` method is even easier. \n\n## Implementing a Delete Method\n\nThe `remove` method of an entity's `*Persistence` class deletes an entity\ninstance from the database. Use it in your local service's `delete*` method.\nHere's what a `delete*` method looks like:\n\n    public [ENTITY] delete[ENTITY](ENTITY entity) throws PortalException\n    {\n        [ENTITY]Persistence.remove(entity);\n\n        // Clean up related to additional Liferay services goes here ... \n\n        return entity;\n    }\n\nMake sure to replace `[ENTITY]` with your entity's name or nickname. \n\nFor example, here's paraphrased code from \n`GuestbookEntryLocalServiceImpl`'s `deleteEntry` method:\n\n    public GuestbookEntry deleteEntry(GuestbookEntry entry)\n        throws PortalException {\n\n        guestbookEntryPersistence.remove(entry);\n\n        // Clean up related to additional Liferay services goes here ...  \n\n        return entry;\n    }\n\nAfter implementing your `delete*` method, run Service Builder to propagate your \nnew service method to the `*LocalService` interface. \n\n## Related Topics\n\n[Implementing an add method](/docs/7-2/appdev/-/knowledge_base/a/implementing-an-add-method)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/04-business-logic-with-service-builder/02-creating-local-services/03-implementing-methods-to-get-and-count-entities.markdown",
    "content": "---\nheader-id: implementing-methods-to-get-and-count-entities\n---\n\n# Implementing Methods to Get and Count Entities\n\n[TOC levels=1-4]\n\nService Builder generates `findBy*` methods and `countBy*` methods in your\n[`*Persistence` classes](/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder)\nbased on your `service.xml` file's\n[finders](/docs/7-2/appdev/-/knowledge_base/a/defining-service-entity-finder-methods).\nYou can leverage finder methods in your local services to get and count\nentities.\n\n-   [Getters](#getter-methods): `get*` methods return entity instances matching \n    criteria.\n-   [Counters](#counter-methods): `get*Count` methods return the number of \n    instances matching criteria\n\nStart with getting entities that match criteria. \n\n## Getter Methods\n\nThe `findByPrimaryKey` methods and `findBy*` methods search for and return\nentity instances based on criteria. Your local service implementation must only\nwrap calls to the finder methods that get what you want.\n\nHere's how to create a method that gets an entity based on an ID (primary key):\n\n1.  Create a method using this format:\n\n        public [ENTITY] get[ENTITY_NAME](long id) {\n            return [ENTITY]Persistence.findByPrimaryKey(id);\n        }\n\n2.  Replace `[ENTITY]` and `[ENTITY_NAME]` with the respective entity type and\n    entity name (or nickname).\n\n3.  Run Service Builder to propagate the method to your local service \n    interface. \n\nHere's how to get entities based on criteria:\n\n1.  Identify the criteria for finding the entity instance(s). \n\n2.  If there is no\n    [`finder` element](/docs/7-2/appdev/-/knowledge_base/a/defining-service-entity-finder-methods)\n    for the criteria, create one for it and run Service Builder. \n\n3.  Determine the\n    [`*Persistence` class](/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder)\n    `findBy*` method you want to call. Depending on your `finder` element\n    columns, Service Builder might overload the method to include these\n    parameters:\n \n    -   `int start` and `int end` parameters for specifying a range of \n        entities.\n    -   `com.liferay.portal.kernel.util.OrderByComparator orderByComparator` \n        parameter for arranging the matching entities. \n\n4.  Specify your `get*` method signature, making sure to account for the \n    `*Persistence` class `findBy*` method parameters you must satisfy. Use this \n    method format: \n\n        public List<[ENTITY]> get[DESCRIBE_THE_ENTITIES](...) {\n            \n        }\n\n    Replace `[ENTITY]` with the entity type. Replace `[DESCRIBE_THE_ENTITIES]`\n    with a descriptive name for the entities you're getting. \n\n6.  Call the `*Persistence` class `findBy*` method and return the list of \n    matching entities. \n\n7.  Run Service Builder. \n\nFor example, `getGuestbookEntries` from `GuestbookEntryLocalServiceImpl`\nreturns a range of `GuestbookEntry`s associated with a `Group` primary key: \n\n```java\npublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId) {\n    return guestbookEntryPersistence.findByG_G(groupId, guestbookId);\n}\n```\n\nNow you know how to leverage finder methods to get entities. Methods that count\nentities are next.\n\n## Counter Methods\n\nCounting entities is just as easy as getting them. Your `*Persistence` class\n`countBy*` methods do all the work. Service Builder generates `countBy*` methods\nbased on each finder and its columns. \n\n1.  Identify the criteria for entity instances you're counting and determine\n    the\n    [`*Persistence` class](/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder)\n    `countBy*` method that satisfies the criteria.\n\n2.  Create a `get*Count` method signature following this format:\n\n        public int get[DESCRIBE_THE_ENTITIES]Count(...) {\n            \n        }\n\n    Replace `[DESCRIBE_THE_ENTITIES]` with a descriptive name for the entities\n    you're counting.\n\n3.  Call the `*Persistence` class' `countBy` method and return the value. For \n    example, the method `getEntriesCount` from\n    `GuestbookEntryLocalServiceImpl`\n    returns the number of `GuestbookEntry`s that are associated with a group\n    (matching `groupId`) and a guestbook (matching `guestbookId`). \n\n```java\npublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n    return guestbookEntryPersistence.countByG_G(groupId, guestbookId);\n}\n```\n\nNow your local service can get entities matching your criteria and return quick\nentity counts. \n\n## Service Method Prefixes and Transactional Aspects\n\nService Builder applies transactions to services by adding `@Transactional`\nannotations to the `*LocalService` and `*Service` interfaces and their methods.\nBy default, Service Builder applies read-only transactions (e.g.,\n`@Transactional (readOnly = true ...)`) to service methods prefixed with any of\nthese words: \n\n- `dynamicQuery`\n- `fetch`\n- `get`\n- `has`\n- `is`\n- `load`\n- `reindex`\n- `search`\n\nSince these methods operate in read-only transactions, @product@ optimizes\ntheir performance. Transactional service methods that don't have the read-only\nsetting operate in regular transactions. \n\n| **Note:** A method implementation can override its interface's \n| `@Transactional` annotation attributes. For example, applying\n| `@Transactional (readOnly = false ...)` to a method implementation makes\n| it operate in a transaction that is not read only. \n\n**Important:** In methods that operate in read-only transactions, invoking a \nservice method that persists data (adds, updates, or deletes data) must be done\nvia the service object. Using the service object ensures that the defined\ntransactional behavior is applied. \n\n```java\nsomeService.addSomething();\n``` \n\nFor example, this `*LocalServiceImpl`'s getter method adds (*persists*) a\n`ClassName` object if no object with that value exists. \n\n```java \npublic ClassName getClassName(String value) {\n    if (Validator.isNull(value)) {\n        return _nullClassName;\n    }\n\n    ClassName className = _classNames.get(value);\n\n    if (className == null) {\n        try {\n            className = classNameLocalService.addClassName(value);\n            ...\n        }\n        ...\n    }\n    ...\n}\n```\n\nUsing the service object `classNameLocalService` to invoke its  `addClassName`\nmethod applies the service method's transaction (the regular transaction\nspecified for the method in the `*Service` interface). If the `addClassName`\nmethod was invoked WITHOUT using the service object, the `ClassName` object\nwould not persist because the method's regular transaction would not be applied.\n\n## Related Topics\n\n[Creating Local Services](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder)\n\n[Implementing an Add Method](/docs/7-2/appdev/-/knowledge_base/a/implementing-an-add-method)\n\n[Defining Service Entity Finder Methods](/docs/7-2/appdev/-/knowledge_base/a/defining-service-entity-finder-methods)\n\n[Understanding the Code Generated by Service Builder](/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/04-business-logic-with-service-builder/02-creating-local-services/04-implementing-any-other-business-logic.markdown",
    "content": "---\nheader-id: implementing-any-other-business-logic\n---\n\n# Implementing Any Other Business Logic\n\n[TOC levels=1-4]\n\nThis section's earlier local service articles focus on CRUD methods: methods\nthat **c**reate (add), **r**ead (get), **u**pdate, and **d**elete entities. But\nyou might also need methods that provide business logic. \n\nSince the Guestbook application doesn't have any business logic, the Bookmarks\napplication, which is extremely similar, (Bookmark Folders and Bookmark entries\ninstead of Guestbooks and Guestbook entries) is used here to illustrate simple\nbusiness logic. Bookmarks application users *open* bookmarks (navigate to a\nURLs) by clicking on them. \n[`BookmarksEntryLocalServiceImpl`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/bookmarks/bookmarks-service/src/main/java/com/liferay/bookmarks/service/impl/BookmarksEntryLocalServiceImpl.java)'s \n`openEntry` method supports this functionality: \n\n```java\npublic BookmarksEntry openEntry(long userId, BookmarksEntry entry) {\n    entry.setVisits(entry.getVisits() + 1);\n\n    bookmarksEntryPersistence.update(entry);\n\n    assetEntryLocalService.incrementViewCounter(\n        userId, BookmarksEntry.class.getName(), entry.getEntryId(), 1);\n\n    return entry;\n}\n```\n\nThe `openEntry` method tracks and persists the number of visits to the\n`BookmarksEntry`'s URL, increments the number of views for the `BookmarksEntry`\nas an asset, and  returns the `BookmarksEntry`. This method implements required\nbusiness logic that compliments the CRUD methods. \n\n*Convenience methods* might also be appropriate for your app. They're easier to\nuse because they typically have these characteristics: \n\n- Shorter parameter list\n- Intuitive name\n\nShort parameter lists are easier to satisfy, and methods that have intuitive\nnames are easier to find in Javadoc.\n\nFor example, the Bookmarks application lets users move bookmarks to different\nfolders. Moving a bookmark can be done using the service's `updateEntry(...)`\nmethod, but its long parameter list is overkill since all the operation requires\nis the bookmarks entry and the folder where it's going. Compare the following\n`update*` method call to a convenience method call. \n\n**Update method**:\n\n```java\nbookmarksEntryLocalService.updateEntry(userId, entryId, groupId, folderId, name, url, description, serviceContext);\n```\n\n**Convenience method**:\n\n```java\nbookmarksEntryLocalService.moveEntry(entryId, folderId);\n```\n\nHere's the [`moveEntry`\nmethod](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/bookmarks/bookmarks-service/src/main/java/com/liferay/bookmarks/service/impl/BookmarksEntryLocalServiceImpl.java):\n\n```java\npublic BookmarksEntry moveEntry(long entryId, long parentFolderId)\n    throws PortalException {\n\n    BookmarksEntry entry = getBookmarksEntry(entryId);\n\n    entry.setFolderId(parentFolderId);\n    entry.setTreePath(entry.buildTreePath());\n\n    bookmarksEntryPersistence.update(entry);\n\n    return entry;\n}\n```\n\nThe `moveEntry` method retrieves the `BookmarksEntry` entity by its ID, assigns\nit a new parent folder, updates its tree path, persists all the entity's\nchanges, and returns the entity. Convenience methods like this one facilitate\nupdating a subset of the entity's attributes. \n\nAfter implementing your custom business methods, \n[run Service Builder](/docs/7-2/appdev/-/knowledge_base/a/running-service-builder)\nto propagate them to the interface.\n\nIn your local services, you can implement business logic methods that suit your\napplication. \n\n**Related Topics**\n\n[Creating Local Services](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder)\n\n[Invoking Local Services](/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services)\n\n[Invoking Services from Spring Service Builder Code](/docs/7-2/appdev/-/knowledge_base/a/invoking-services-from-spring-service-builder-code)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/04-business-logic-with-service-builder/02-creating-local-services/05-integrating-with-liferay-frameworks.markdown",
    "content": "---\nheader-id: integrating-with-liferays-frameworks\n---\n\n# Integrating with Liferay's Frameworks\n\n[TOC levels=1-4]\n\nNew car buyers expect certain standard features: power windows, cruise control,\nfloor mats (at least the cheap ones), and so on. Similarly, users expect\napplications to have certain features, and those features should behave\nconsistently across applications. \n\nFor example, a user might expect the app's content can be shared on social\nnetworks, tagged and rated, and discussed in comments. Liferay's frameworks\nimplement these features users expect to see. Integrating with the frameworks is\neasy, and the frameworks provide intuitive, consistent user experiences. \n\nHere are some of Liferay's most popular frameworks:\n\n[Permissions](/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions):\nDefines resources and permissions for entities and actions that can be performed\non them.\n\n[Configurable Applications](/docs/7-2/frameworks/-/knowledge_base/f/configurable-applications):\nProvide configuration screens in the Control Panel. \n\n[Workflow](/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework):\nEquips entities for reviewing in workflows before publishing. \n\n[Item Selector](/docs/7-2/frameworks/-/knowledge_base/f/item-selector): \nProvides a consistent experience for browsing and selecting entities. \n\n[Asset Framework](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework): \nProvides a way to make entity data generic, so common tasks can be performed on\nthem. This enables users to tag, categorize, rate, prioritize, and comment on\nanything that has been asset-enabled. Users can relate entities to each other as\nassets, and entities can be published in the Asset Publisher. \n\n-   [Tags and Categories](/docs/7-2/frameworks/-/knowledge_base/f/implementing-asset-categorization-and-tagging):\n    Enables users to tag entities and categorize them into logical hierarchies. \n-   [Priority](/docs/7-2/frameworks/-/knowledge_base/f/implementing-asset-priority):\n    Users can ascribe numerical priorities to entities. \n-   [Related Assets](/docs/7-2/frameworks/-/knowledge_base/f/relating-assets):\n    Users can associate one entity with another as an asset. \n-   [Asset Renderer](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework):\n    Enables entities appearing in Asset Publisher queries. \n-   [Comments](/docs/7-2/frameworks/-/knowledge_base/f/adding-comments-to-your-app):\n    Lets users comment on entities.\n-   [Ratings](/docs/7-2/frameworks/-/knowledge_base/f/rating-assets):\n    Enables rating systems, such as five stars or thumbs up/down, on entities. \n-   [Flags](/docs/7-2/frameworks/-/knowledge_base/f/flagging-inappropriate-asset-content):\n    Users can flag entity content as inappropriate. \n-   [Social Bookmarks](/docs/7-2/frameworks/-/knowledge_base/f/applying-social-bookmarks):\n    Users can share entity content on Twitter, Facebook, and more. \n\n[Export/Import](/docs/7-2/frameworks/-/knowledge_base/f/export-import):\nExport entity data to and import entity data from files (`.lpkg` files).\nExported data can be imported to another portal instance or saved for later use. \n\n[Staging](/docs/7-2/frameworks/-/knowledge_base/f/staging): \nModify content behind the scenes without affecting the live site, and then\npublish to the live site when the content is ready. \n\n[Search](/docs/7-2/frameworks/-/knowledge_base/f/search): \nIndex entities for searching. \n\n[Recycle Bin](/docs/7-2/frameworks/-/knowledge_base/f/moving-entities-to-the-recycle-bin): \nInstead of deleting entities, put them into the Recycle Bin. Entities can be\nrestored from the Recycle Bin or deleted permanently (manually or per\na schedule). \n\n## Related Topics\n\n[Internationalization](/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys)\n\n[JavaScript Module Loaders](/docs/7-2/frameworks/-/knowledge_base/f/javascript-module-loaders)\n\n[Java Taglibs](/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs)\n\n[Upgrade Processes](/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/04-business-logic-with-service-builder/03-invoking-local-services.markdown",
    "content": "---\nheader-id: invoking-local-services\n---\n\n# Invoking Local Services\n\n[TOC levels=1-4]\n\nOnce you deploy your services module, those services are available in the\ncontainer. Service Builder generates local and remote service classes as OSGi\nDeclarative Services (DS) components. These components are accessible to other\nDS components, so you can invoke them from other components, such as your web\napplication. Here's how:\n\n1. Add a reference to the local service component.\n\n2. Call the component's methods.\n\nThere's a Blade sample called \n[Basic Service Builder](/docs/7-2/reference/-/knowledge_base/r/service-builder-samples).\nIts `basic-web` module has a `Portlet` service component that demonstrates\nreferencing a local service component. This module also has JSPs that invoke the\ncomponent's methods. Your first step is to add a reference to the local service\ncomponent object.\n\n## Step 1: Reference the Local Service Component\n\nYour application's Service Builder-generated local services are DS components\nthat you can inject into your application's other DS components (classes\nannotated with `@Component`)\n[using the `@Reference` annotation](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services).\nThe `basic-web` module's `JSPPortlet` class is a `Portlet` service component\nthat references the `FooLocalService` local service as a DS component.\n\n```java\n@Reference\nprivate volatile FooLocalService _fooLocalService;\n```\n\nThe OSGi service registry wires the service implementation object to your class\nthat references it. The `JSPPortlet` sample class declares the\n`_fooLocalService` field to be volatile, but making a field volatile is\ncompletely optional. \n\n| **Note**: If you chose Spring as the dependency injector, Service Builder\n| generates `*LocalServiceImpl`, `*ServiceImpl`, `*PersistenceImpl`, and\n| `[ENTITY_NAME]Impl` classes for your entities as Service Builder Spring\n| Beans---not OSGi Declarative Services. \n| [Service Builder Spring Beans must use means other than the `@Reference` annotation to reference Liferay services and OSGi services](/docs/7-2/appdev/-/knowledge_base/a/invoking-services-from-spring-service-builder-code).\n\n**Important:** You should never invoke `*LocalServiceImpl` objects directly. You\nshould only invoke them indirectly through their `*LocalService` service\ninterface. The OSGi service registry wires the service implementation object to\nyour class. \n\nYou can make a service object available to JSPs by associating it with a\n`RenderRequest` attribute. For example, the `JSPPortlet`'s `render` method\nassociates the `FooLocalService` object with an attribute called\n`fooLocalService`. \n\n```java\n@Override\npublic void render(RenderRequest request, RenderResponse response)\n    throws IOException, PortletException {\n\n    //set service bean\n    request.setAttribute(\"fooLocalService\", getFooLocalService());\n\n    super.render(request, response);\n}\n\npublic FooLocalService getFooLocalService() {\n    return _fooLocalService;\n}\n```\n\nIf your JSP declares the `<portlet:defineObjects />` tag, it can retrieve the\nservice object from the `RenderRequest` attribute. For example, the\n`JSPPortlet`'s `init.jsp` file retrieves the `FooLocalService` object from the\n`\"fooLocalService\"` attribute. \n\n```markup\n...\n<%@\npage import=\"com.liferay.blade.samples.servicebuilder.service.FooLocalService\" %>\n...\n\n<liferay-theme:defineObjects />\n\n<portlet:defineObjects />\n\n<%\n...\n\n//get service bean\nFooLocalService fooLocalService = (FooLocalService)request.getAttribute(\"fooLocalService\");\n%>\n```\n\nAll JSPs that include the above `init.jsp` can use the `fooLocalService`\nvariable to invoke the local service component's methods. \n\n## Step 2: Call the Component's Methods\n\nNow that you have the service component object, you can invoke its methods as\nyou would any Java object's methods. \n\nThe `basic-web` sample module's `view.jsp` and `edit_foo.jsp` files include the\n`init.jsp` shown in the previous section. Therefore, they can access the\n`fooLocalService` variable which references the service component object. The\n`view.jsp` file uses the component's `getFoosCount` method and `getFoos` method\nin a Liferay Search Container that lists `Foo` instances. \n\n```markup\n<liferay-ui:search-container\n    total=\"<%= fooLocalService.getFoosCount() %>\"\n>\n    <liferay-ui:search-container-results\n        results=\"<%= fooLocalService.getFoos(searchContainer.getStart(), searchContainer.getEnd()) %>\"\n    />\n    ...\n</liferay-ui:search-container>\n```\n\nThe `edit_foo.jsp` file calls `getFoo(long id)` to retrieve a `Foo` entity based\non the entity instance's ID.\n\n```markup\nlong fooId = ParamUtil.getLong(request, \"fooId\");\nFoo foo = null;\nif (fooId > 0) {\n    foo = fooLocalService.getFoo(fooId);\n}\n```\n\n| **Important:** When invoking service entity updates (e.g., \n| `fooService.update(object)`) for services that have MVCC enabled, make sure to \n| do so in transactions. Propagate rejected transactions to the UI for the user \n| to handle. For details, see \n| [Multiversion concurrency control (MVCC)](/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information#multiversion-concurrency-control-mvcc).\n\nUsing the `@Reference` annotation, you can inject your application's OSGi DS\ncomponents (such as a portlet DS component) with instances of your application's\nService Builder-generated local service components. Also you can provide your\nJSPs access to the component instances via `RenderRequest` attributes. \n\n## Related Topics\n\n[Creating Local Services](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder)\n\n[Invoking Local Services](/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services)\n\n[Invoking Local Services from Spring Service Builder Code](/docs/7-2/appdev/-/knowledge_base/a/invoking-services-from-spring-service-builder-code)\n\n[OSGi Services and Dependency Injection with Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/04-business-logic-with-service-builder/04-invoking-services-from-spring-service-builder-code.markdown",
    "content": "---\nheader-id: invoking-services-from-spring-service-builder-code\n---\n\n# Invoking Services from Spring Service Builder Code\n\n[TOC levels=1-4]\n\nWhen using Spring as the dependency injector, all the services created within\na Service Builder application are wired using an internal Spring Application\nContext. This uses AOP proxies to adapt the services for transactions, indexing,\nand security. In a module's `module-spring.xml` Spring Application Context file,\nService Builder defines each entity's `*LocalServiceImpl`, `*ServiceImpl`, and\n`*PersistenceImpl` classes as Spring Beans. For example, Service Builder defines\nSpring Beans for the `Foo` entity in the\n[Liferay Blade Service Builder `basic-service` sample module's](/docs/7-2/reference/-/knowledge_base/r/service-builder-samples)\n`src/main/resources/META-INF/spring/module-spring.xml` file:\n\n```xml\n<?xml version=\"1.0\"?>\n\n<beans\n    default-destroy-method=\"destroy\"\n    default-init-method=\"afterPropertiesSet\"\n    xmlns=\"http://www.springframework.org/schema/beans\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"\n>\n    <bean class=\"com.liferay.blade.samples.servicebuilder.service.impl.FooLocalServiceImpl\" id=\"com.liferay.blade.samples.servicebuilder.service.FooLocalService\" />\n    <bean class=\"com.liferay.blade.samples.servicebuilder.service.impl.FooServiceImpl\" id=\"com.liferay.blade.samples.servicebuilder.service.FooService\" />\n    <bean class=\"com.liferay.blade.samples.servicebuilder.service.persistence.impl.FooPersistenceImpl\" id=\"com.liferay.blade.samples.servicebuilder.service.persistence.FooPersistence\" parent=\"basePersistence\" />\n</beans>\n```\n\nHere's a summary of the beans the example context defines:\n\n **Interface ID** | **Implementation Class** |\n :------ | :--------- |\n `com.liferay.blade.samples.servicebuilder.service.FooLocalService` | `com.liferay.blade.samples.servicebuilder.service.impl.FooLocalServiceImpl` |\n `com.liferay.blade.samples.servicebuilder.service.FooService` | `com.liferay.blade.samples.servicebuilder.service.impl.FooServiceImpl` |\n `com.liferay.blade.samples.servicebuilder.service.persistence.FooPersistence` | `com.liferay.blade.samples.servicebuilder.service.persistence.impl.FooPersistenceImpl` |\n \nSince these classes are Spring Beans and NOT OSGi Declarative Services\ncomponents, they don't use the `@Reference` Declarative Services annotation to\ninject themselves. Here are the recommended Liferay annotations a Service\nBuilder Spring Bean can use.\n\n-   Use `@BeanReference` to reference a Spring Bean that is in the \n    Application Context.\n-   Use `@ServiceReference` to reference an OSGi service.\n\n| **Important:** When invoking service entity updates (e.g.,\n| `fooService.update(object)`) for services that have MVCC enabled, make sure to\n| do so in transactions. Propagate rejected transactions to the UI for the user to\n| handle. For details, see\n| [Multiversion concurrency control (MVCC)](/docs/7-2/appdev/-/knowledge_base/a/defining-global-service-information#multiversion-concurrency-control-mvcc).\n\nThe `@BeanReference` annotation is explained first. \n\n## Referencing a Spring Bean that is in the Application Context\n\nA Service Builder Spring Bean class, such as a `*LocalServiceImpl` class, should\nuse Liferay's `@BeanReference` annotation to access other Spring Beans the\nmodule's Spring Application Context defines.\n\nFor example, if your service module's `service.xml` file defines local services\nfor entities named `Foo` and `Bar`, Service Builder generates a\n`module-spring.xml` file that defines local service Spring Beans for both\nentities. To inject the `BarLocalService` Spring Bean into the\n`FooLocalServiceImpl` class, for example, the `FooLocalServiceImpl` class must\ndeclare a `BarLocalService` field and apply an `@BeanReference` annotation to\nit. \n\n    @BeanReference\n    private BarLocalService _barLocalService;\n\nThe `@BeanReference` tells Liferay's AOP to treat the bean reference for use in\ntransactions, search indexing, or security, if needed. The referencing class can\ninvoke the Spring Bean class's methods.\n\nBesides the services Service Builder makes available for your application,\nService Builder Spring Bean classes can also access any service published in the\nOSGi Registry. This means the following services are available:\n\n- Beans defined in Liferay's core\n- Beans created in other module app contexts\n- Services declared using OSGi Declarative Services\n- Services registered using the OSGi low level API\n\nThese are all OSGi services. The next section demonstrates a Service Builder\nSpring Bean referencing OSGi services.\n\n## Referencing OSGi Services\n\nIn many cases, your Service Builder code (Spring Beans) must use external\nservices. Liferay's `@ServiceReference` annotation lets Liferay Spring Beans\nreference OSGi services. \n\nSuppose you're building an application with a simple entity your service module\ndefines in its `service.xml` file. The application must send an SMS every time\na new entity is created, and the `SMSService` is provided by a module installed\nin the system.\n\nYour `*LocalServiceImpl` (Spring Bean) could use an `@ServiceReference`\nannotation to reference the *external* service.\n\n    @ServiceReference\n    private SMSService _smsService;\n\nThis annotation retrieves a reference to the OSGi service and provides some nice\nbenefits. None of the Spring context is created until the `SMSService` service\nis available. Likewise, if the `SMSService` suddenly disappears, the whole\nSpring Application Context is destroyed. This makes Liferay Spring apps robust\nand versatile.\n\nFortunately, Service Builder generates this kind of code for every entity your\n`service.xml` file references. For example, the\n[Liferay Blade Service Builder sample project](/docs/7-2/reference/-/knowledge_base/r/service-builder-samples)\n`basic-service` module's `service.xml` file defines a `Foo` entity that\nreferences an `AssetEntry` entity:\n\n```xml\n<reference entity=\"AssetEntry\" package-path=\"com.liferay.portlet.asset\" />\n```\n\nService Builder generated the `FooLocalServiceBaseImpl` class (the base class is\npart of the `FooLocalServiceImpl` class's hierarchy), which references the\n`AssetEntry` entity's local service `AssetEntryLocalService` using a field\nannotated with `@ServiceReference`:\n\n```java\n@ServiceReference(type = com.liferay.asset.kernel.service.AssetEntryLocalService.class)\nprotected com.liferay.asset.kernel.service.AssetEntryLocalService assetEntryLocalService;\n```\n\nGreat! You now know how to add a reference to any OSGi service to a Service\nBuilder Spring Bean. You also know how to add a reference to any other Spring\nBean in the Application Context of your Service Builder Spring Bean.\n\n## Related Topics\n\n[Invoking Local Services](/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services)\n\n[Service Trackers](/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/09-advanced-queries/01-advanced-query-intro.markdown",
    "content": "---\nheader-id: advanced-queries\n---\n\n# Advanced Queries\n\n[TOC levels=1-4]\n\nService Builder doesn't limit you to what you can cook up with `<finder />`\nelements in `service.xml`. If simple finders aren't sufficient for getting data\nout of your application, you can use Liferay's Dynamic Query API, which wraps\nHibernate's Criteria API, or your own SQL to make exactly the queries you need. \n\nThough you can use custom SQL queries with Service Builder to retrieve data\nfrom the database, sometimes it's more convenient to build queries dynamically\nat runtime. You can do this with Liferay's Dynamic Query API, which wraps\nHibernate's Criteria API. The Dynamic Query API lets you build queries without\nwriting any SQL. It helps you think in terms of objects and member variables\ninstead of tables and columns. Complex queries can be significantly easier to\nunderstand and maintain than the equivalent custom SQL (or HQL) queries. While\nyou technically don't need to know SQL to construct Dynamic Queries, you still\nmust take care to construct efficient queries. For information on Hibernate's\nCriteria API, please see \n[Hibernate's manual](http://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/chapters/query/criteria/Criteria.html).\n\nWhichever way you decide to implement your custom queries, this guide shows you\nhow. Here are the steps: \n\n1.  If using SQL, [create your SQL query](/docs/7-2/appdev/-/knowledge_base/a/custom-sql).\n\n2.  [Define a custom finder method](/docs/7-2/appdev/-/knowledge_base/a/defining-a-custom-finder-method).\n\n3.  [Implement your finder using Dynamic Query API or SQL](/docs/7-2/appdev/-/knowledge_base/a/dynamic-query).\n\n4.  [Add a method to your `*LocalServiceImpl` class that invokes your finder](/docs/7-2/appdev/-/knowledge_base/a/accessing-your-custom-finder-method-from-the-service-layer). \n\nOnce you've taken these steps, you can access your custom finder as a service\nmethod. Note: You can create multiple or overloaded `findBy*` finder methods in\nyour `*FinderImpl` class. Next, you'll examine these steps in more detail.\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/09-advanced-queries/02-custom-sql.markdown",
    "content": "---\nheader-id: custom-sql\n---\n\n# Custom SQL\n\n[TOC levels=1-4]\n\nService Builder creates finder methods that retrieve entities by their\nattributes: their column values. When you add a column as a parameter for the\nfinder in your `service.xml` file and run Service Builder, it generates the\nfinder method in your persistence layer and adds methods to your service layer\nthat invoke the finder. If your queries are simple enough, consider using \n[Dynamic Query](/docs/7-2/appdev/-/knowledge_base/a/dynamic-query) to access\nLiferay's database. If you want to do something more complicated like JOINs, you\ncan write your own custom SQL queries. Here, you'll learn how.\n\nThe Guestbook application has two tables, one for guestbooks and one\nfor guestbook entries. The entry entity's foreign key to its guestbook is the\nguestbook's ID. That is, the entry entity table, `GB_GuestbookEntry`, tracks an\nentry's guestbook by its long integer ID in the table's `guestbookId` column. If\nyou want to find a guestbook entry based on its name, message, and guestbook\nname, you must access the *name* of the entry's guestbook. Of course, with SQL\nyou can join the entry and guestbook tables to include the guestbook name.\nService Builder lets you do this by specifying the SQL as *Liferay custom SQL*\nand invoking it in your service via a *custom finder method*.\n\nUsing Custom SQL in Service Builder is the same as using dynamic queries; it\njust takes an additional first step to place the SQL you want to run in an XML\nfile. If you plan to use dynamic queries instead, skip the rest of this tutorial\nand move on to the next one. \n\n## Specify Your Custom SQL\n\nAfter you've tested your SQL, you must specify it in a particular file for\nLiferay to access it. `CustomSQL` class (from module\n[`com.liferay.portal.dao.orm.custom.sql.api`](https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.portal.dao.orm.custom.sql.api/))\nretrieves SQL from a file called `default.xml` in your service module's\n`src/main/resources/META-INF/custom-sql/` folder. You must create the\n`custom-sql` folder and create the `default.xml` file in that `custom-sql`\nfolder. The `default.xml` file must adhere to the following format:\n\n```xml\n<custom-sql>\n    <sql id=\"[fully-qualified class name + method]\">\n    SQL query wrapped in <![CDATA[...]]>\n    No terminating semi-colon\n    </sql>\n</custom-sql>\n```\n\nCreate a `custom-sql` element for every SQL query you want in your application,\nand give each query a unique ID. The recommended convention to use for the\nID value is the fully-qualified class name of the finder followed by a dot (`.`)\ncharacter and the name of the finder method. More detail on the finder class and\nfinder methods is provided in the next step.\n\nFor example, in the Guestbook application, you could use the following ID value\nto specify a query:\n\n```java\ncom.liferay.docs.guestbook.service.persistence.EntryFinder.findByEntryNameEntryMessageGuestbookName\n```\n\nCustom SQL must be wrapped in character data (`CDATA`) for the `sql` element.\nImportantly, do not terminate the SQL with a semi-colon. Following these rules,\nthe `default.xml` file of the Guestbook application specifies an SQL query that\njoins the `GB_GuestbookEntry` and `GB_Guestbook` tables:\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<custom-sql>\n    <sql id=\"com.liferay.docs.guestbook.service.persistence.EntryFinder.findByEntryNameEntryMessageGuestbookName\">\n        <![CDATA[\n            SELECT GB_GuestbookEntry.*\n            FROM GB_GuestbookEntry\n            INNER JOIN \n                GB_Guestbook ON GB_GuestbookEntry.guestbookId = GB_Guestbook.guestbookId\n            WHERE\n                (GB_GuestbookEntry.name LIKE ?) AND\n                (GB_GuestbookEntry.message LIKE ?) AND\n                (GB_Guestbook.name LIKE ?)\n        ]]>\n    </sql>\n</custom-sql>\n```\n\nNow that you've specified some custom SQL, the next step is to implement a\nfinder method to invoke it. The method name for the finder should match the ID\nyou just specified for the `sql` element.\n\nCongratulations on developing a custom SQL query and custom finder for your\napplication!\n\n## Related Topics\n\n[Customizing Liferay Services](/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/09-advanced-queries/03-defining-a-custom-finder-method.markdown",
    "content": "---\nheader-id: defining-a-custom-finder-method\n---\n\n# Defining a Custom Finder Method\n\n[TOC levels=1-4]\n\nDynamic queries and custom SQL belong in finder methods. You implement them and\nthen make them available through an interface. This article demonstrates\ndefining the finder method in an implementation class, generating its interface\nand tying the implementation to the interface. \n\nAn example of this is a Guestbook application with two entities: guestbook and\nentry. Each entry belongs to a guestbook so the entry entity has\na `guestbookId` field as a foreign key. If you need a finder to search for\nguestbook entries by entry name and guestbook name, you'd add a finder method\nto `GuestbookFinderImpl` and name it `findByEntryNameGuestbookName`. The full\nmethod signature would be `findByEntryNameGuestbookName(String entryName,\nString guestbookName)`. The steps are below. \n\n1.  Create a `[Entity]FinderImpl` class in the `[package \n    path].service.persistence.impl` package of your service module's\n    `src/main/java` folder. Recall that you specify the `[package path]` in your\n    `service.xml` file. Here's an example:\n\n    ```xml\n    <service-builder package-path=\"com.liferay.docs.guestbook\">\n        ...\n    </service-builder>\n    ```\n\n2.  Define a `findBy*` finder method in the class you created. Make sure to add \n    any required arguments to your finder method signature.\n\n4.  Run Service Builder to generate the appropriate interface in the `[package \n    path].service.persistence` package in the `service` folder of your API and\n    service modules.\n\n    For example, after adding `findByEntryNameGuestbookName(String entryName,\n    String guestbookName)` to `GuestbookFinderImpl` and running Service Builder,\n    the interface\n    `com.liferay.docs.guestbook.service.persistence.GuestbookFinder` is\n    generated.\n\n5.  Make the finder class a component (annotated with [`@Component`](https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html)) that implements the `GuestbookFinder` interface. For example, the class declaration should look like this:\n\n    ```java\n    @Component(service = GuestbookFinder.class)\n    public class GuestbookFinderImpl extends BasePersistenceImpl<Guestbook> implements GuestbookFinder\n    ```\n\nYour next step is to implement the query in your finder. You can do this via the\nDynamic Query API or Custom SQL. The next tutorial covers Dynamic Query. To\nsimply call custom SQL you have written, create a finder method to run your SQL: \n\n```java\npublic List<Entry> findByEntryNameEntryMessageGuestbookName(\n    String entryName, String entryMessage, String guestbookName,\n    int begin, int end) {\n\n    Session session = null;\n    try {\n        session = openSession();\n\n        String sql = _customSQL.get(\n            getClass(),\n            FIND_BY_ENTRYNAME_ENTRYMESSAGE_GUESTBOOKNAME);\n\n        SQLQuery q = session.createSQLQuery(sql);\n        q.setCacheable(false);\n        q.addEntity(\"GB_Entry\", EntryImpl.class);\n\n        QueryPos qPos = QueryPos.getInstance(q);\n        qPos.add(entryName);\n        qPos.add(entryMessage);\n        qPos.add(guestbookName);\n\n        return (List<Entry>) QueryUtil.list(q, getDialect(), begin, end);\n    }\n    catch (Exception e) {\n        try {\n            throw new SystemException(e);\n        }\n        catch (SystemException se) {\n            se.printStackTrace();\n        }\n    }\n    finally {\n        closeSession(session);\n    }\n\n    return null;\n}\n\npublic static final String FIND_BY_ENTRYNAME_ENTRYMESSAGE_GUESTBOOKNAME =\n    EntryFinder.class.getName() +\n        \".findByEntryNameEntryMessageGuestbookName\";\n\n@Reference\nprivate CustomSQL _customSQL;\n```\n\nThe custom finder method opens a new Hibernate session and uses Liferay's\n`CustomSQL.get(Class<?> clazz, String id)` method to get the custom SQL to use\nfor the database query. The `FIND_BY_ENTRYNAME_ENTRYMESSAGE_GUESTBOOKNAME`\nstatic field contains the custom SQL query's ID. The\n`FIND_BY_EVENTNAME_EVENTDESCRIPTON_LOCATIONNAME` string is based on the\nfully-qualified class name of the `*Finder` interface (`EventFinder`) and the\nname of the finder method (`findByEntryNameEntryMessageGuestbookName`).\n\nAwesome! You've implemented your finder class, and if you're using custom SQL,\nyou've even implemented a method to call your finder. If you're using Dynamic\nQuery, the next tutorial shows you how to implement a dynamic query finder\nmethod. \n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/09-advanced-queries/03-dynamic-query.markdown",
    "content": "---\nheader-id: dynamic-query\n---\n\n# Dynamic Query\n\n[TOC levels=1-4]\n\nOnce you've\n[defined your custom finder method](/docs/7-2/appdev/-/knowledge_base/a/defining-a-custom-finder-method), \nyou can use the Dynamic Query API to implement your query in it. Here's what you\nmust do in your finder method:\n\n1.  [Open a Hibernate Session](#using-a-hibernate-session)\n\n2.  [Create a dynamic query using these Hibernate features](#creating-dynamic-queries): \n \n    -   *Restrictions*: Similar to `where` clauses of an SQL query, \n        restrictions limit results based on criteria. \n    -   *Projections*: Modify the kind of results the query returns. \n    -   *Orders*: Organize results.\n\n3.  [Execute the Dynamic Query and return the results](#executing-the-dynamic-query) \n\nBefore implementing a dynamic query in your own finder method, it can be helpful\nto examine an example. The following example method uses multiple dynamic\nqueries and all the Hibernate features. Instructions for implementing your own\nfinder method follow the example. \n\n## Example Finder Method: findByGuestbookNameEntryName\n\nThis finder method for the Guestbook application retrieves a list of Guestbook\nentries that have a specific name and that also belong to a Guestbook of a\nspecific name:\n\n```java\npublic List<Entry> findByEntryNameGuestbookName(String entryName, String guestbookName) {\n\n    Session session = null;\n    try {\n        session = openSession();\n\n        ClassLoader classLoader = getClass().getClassLoader();\n\n        DynamicQuery guestbookQuery = DynamicQueryFactoryUtil.forClass(Guestbook.class, classLoader)\n            .add(RestrictionsFactoryUtil.eq(\"name\", guestbookName))\n            .setProjection(ProjectionFactoryUtil.property(\"guestbookId\"));\n\n        Order order = OrderFactoryUtil.desc(\"modifiedDate\");\n\n        DynamicQuery entryQuery = DynamicQueryFactoryUtil.forClass(Entry.class, classLoader))\n            .add(RestrictionsFactoryUtil.eq(\"name\", entryName))\n            .add(PropertyFactoryUtil.forName(\"guestbookId\").in(guestbookQuery))\n            .addOrder(order);\n\n        List<Entry> entries = _entryLocalService.dynamicQuery(entryQuery);\n\n        return entries;\n    }\n    catch (Exception e) {\n        try {\n            throw new SystemException(e);\n        }\n        catch (SystemException se) {\n            se.printStackTrace();\n        }\n    }\n    finally {\n        closeSession(session);\n    }\n}\n```\n\nThe method first opens a Hibernate session. While the session is open in the\n`try` block, it creates and executes a dynamic query, which returns results (a\nlist of guestbook `Entry` objects) if all goes well. \n\nThe finder method has two distinct dynamic queries.\n\n1.  The first query retrieves a list of guestbook IDs corresponding to guestbook\n    names that match the `guestbookName` parameter of the finder method.\n\n2.  The second query retrieves a list of guestbook entries with entry names that\n    match the `entryName` parameter and have `guestbookId` foreign keys\n    belonging to the list returned by the first query.\n\nHere's the first query:\n\n```java\nDynamicQuery guestbookQuery = DynamicQueryFactoryUtil.forClass(Guestbook.class, classLoader))\n    .add(RestrictionsFactoryUtil.eq(\"name\", guestbookName))\n    .setProjection(ProjectionFactoryUtil.property(\"guestbookId\"));\n```\n\nBy default, `DynamicQueryFactoryUtil.forClass(Guestbook.class, classLoader))`\nreturns a query that retrieves a list of all guestbook entities. Adding the\n`.add(RestrictionsFactoryUtil.eq(\"name\", guestbookName))` restriction limits the\nresults to only those guestbooks whose guestbook names match the `guestbookName`\nparameter. The `.setProjection(ProjectionFactoryUtil.property(\"guestbookId\"))`\nprojection changes the result set from a list of guestbook entries to a list of\nguestbook IDs. This is useful since guestbook IDs are much less expensive to\nretrieve than full guestbook entities, and the entry query only needs the\nguestbook IDs. \n\nNext appears an order:\n\n```java\nOrder order = OrderFactoryUtil.desc(\"modifiedDate\");\n```\n\nThis arranges the results list in descending order of the query entity's\n`modifiedDate` attribute. Thus the most recently modified entities (guestbook\nentries, in our example) appear first and the least recently modified entities\nappear last.\n\nHere's the second query:\n\n```java\nDynamicQuery entryQuery = DynamicQueryFactoryUtil.forClass(Entry.class, classLoader))\n    .add(RestrictionsFactoryUtil.eq(\"name\", entryName))\n    .add(PropertyFactoryUtil.forName(\"guestbookId\").in(guestbookQuery))\n    .addOrder(order);\n```\n\nBy default, `DynamicQueryFactoryUtil.forClass(Entry.class, classLoader))`\nreturns a list of all guestbook entry entities. The\n`.add(RestrictionsFactoryUtil.eq(\"name\", entryName))` restriction limits the\nresults to only those guestbook entries whose names match the finder method's\n`entryName` parameter.\n[`PropertyFactoryUtil`](@platform-ref@/7.0-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/PropertyFactoryUtil.html)\nis a Liferay utility class whose method `forName(String propertyName)` returns\nthe specified property. This property can be passed to another Liferay dynamic\nquery. This is exactly what happens in the following line of our example:\n\n```java\n.add(PropertyFactoryUtil.forName(\"guestbookId\").in(guestbookQuery))\n```\n\nHere, the code makes sure that the guestbook IDs (foreign keys) of the entry\nentities in the `entityQuery` belong to the list of guestbook IDs returned\nby the `guestbookQuery`. Declaring that an entity property in one query must\nbelong to the result list of another query is a way to use the dynamic query API\nto create complex queries, similar to SQL joins.\n\nLastly, the order defined earlier is applied to the entries returned by the\n`findByEntryNameGuestbookName` finder method:\n\n```java\n.addOrder(order);\n```\n\nThis orders the list of guestbook entities by the `modifiedDate` attribute, from\nmost recent to least recent.\n\nLastly, the dynamic query is invoked on the `EntryLocalService` instance. It\nreturns a list of `Entry` objects which are then returned by the finder method. \n\n```java\nList<Entry> entries = _entryLocalService.dynamicQuery(entryQuery);\n\nreturn entries;\n```\n\nIt's time to implement your finder method to use Dynamic Query. Start with\nopening and managing a Hibernate session. \n\n## Using a Hibernate Session\n\nYour first step in implementing your custom finder method in your `*FinderImpl`\nclass is to open a new Hibernate session. Since your `*FinderImpl` class extends\n`BasePersistenceImpl<Entity>`, and `BasePersistenceImpl<Entity>` contains a\nsession factory object and an\n[`openSession`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/service/persistence/impl/BasePersistenceImpl.html#openSession--)\nmethod, you can invoke the `openSession` method of your `*FinderImpl`'s\nparent class to open a new Hibernate session. The structure of your finder\nmethod should look like this:\n\n```java\npublic List<Entity> findBy-(...) {\n\n    Session session = null;\n    try {\n            session = openSession();\n            \n            /*\n            create a dynamic\n            query to retrieve and return the desired list of entity\n            objects\n            */\n    }\n    catch (Exception e) {\n            // Exception handling\n    }\n    finally {\n            closeSession(session);\n    }\n\n    return null;\n    /*\n    Return null only if there was an error returning the\n    desired list of entity objects in the try block\n    */\n\n}\n```\n\nNext, in the try block, create your dynamic query objects. \n\n## Creating Dynamic Queries\n\nIn Liferay, you don't create criteria objects directly from the\nHibernate session. Instead, you create dynamic query objects using Liferay's\n[`DynamicQueryFactoryUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/DynamicQueryFactoryUtil.html)\nservice. Thus, instead of\n\n```java\nCriteria entryCriteria = session.createCriteria(Entry.class);\n```\n\nyou use\n\n```java\nDynamicQuery entryQuery = DynamicQueryFactoryUtil.forClass(Entry.class, classLoader));\n```\n\nIn your finder method, initialize your dynamic query for your entity class. \n\nMost features of Hibernate's Criteria API, including restrictions, projections,\nand orders, can be used on Liferay dynamic query objects. Each criteria can be\napplied to your query. The restriction criteria type is described first. \n\n### Restriction Criteria\n\nRestrictions in Hibernate's Criteria API roughly correspond to the `where`\nclause of an SQL query: they offer a variety of ways to limit the results\nreturned by the query. You can use restrictions, for example, to cause a query\nto return only results where a certain field has a particular value, or a value\nin a certain range, or a non-null value, etc.\n\nWhen you need to add restrictions to a dynamic query, don't call Hibernate's\n`Restrictions` class directly. Instead, use the\n[`RestrictionsFactoryUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/RestrictionsFactoryUtil.html)\nservice. `RestrictionsFactoryUtil` has the same methods that you're used to from\nHibernate's `Restrictions` class: `in`, `between`, `like`, `eq`, `ne`, `gt`,\n`ge`, `lt`, `le`, etc.\n\nThus, instead of using this call to specify that a guestbook must have a certain\nname,\n\n```java\nentryCriteria.add(Restrictions.eq(\"name\", guestbookName));\n```\n\nyou use\n\n```java\nentryQuery.add(RestrictionsFactoryUtil.eq(\"name\", guestbookName));\n```\n\nThe restriction above limits the results to guestbook entries whose `name`\nattribute matches the value of the variable `guestbookName`. Add the\nrestrictions you need to get the results you want. \n\nProjections are the next criteria type. They let you transform the query results\nto return the field type you desire. \n\n### Projection Criteria\n\nProjections in Hibernate's Criteria API let you modify the kind of results\nreturned by a query. For example, if you don't want your query to return a list\nof entity objects (the default), you can set a projection on a query to return\nonly a list of the values of a certain entity field, or fields. You can also use\nprojections on a query to return the maximum or minimum value of an entity\nfield, or the sum of all the values of a field, or the average, etc. For more\ninformation on restrictions and projections, please refer to Hibernate's\n[documentation](http://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/querycriteria.html).\n\nSimilarly, to set projections, create properties via Liferay's\n[PropertyFactoryUtil](@platform-ref@/7.0-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/PropertyFactoryUtil.html)\nservice instead of through Hibernate's `Property` class. Thus, instead of \n\n```java\nentryCriteria.setProjection(Property.forName(\"guestbookId\"));\n```\n\nyou use\n\n```java\nentryQuery.setProjection(PropertyFactoryUtil.forName(\"guestbookId\"));\n```\n\nThe projection above specifies the `guestbookId` entity field to changes the\nresult set to a list of those field values. If you want to return a specific\nfield type from your entities, add a projection for it. \n\nThe last criteria type lets you organize results your way. \n\n### Order Criteria\n\nOrders in Hibernate's Criteria API let you control the order of the elements in\nthe list a query returns. You can choose the property or properties to which an\norder applies as well as whether they're in ascending or descending order.\n\nThis code creates an order by the entity's `modifiedDate` attribute:\n\n```java\nOrder order = OrderFactoryUtil.desc(\"modifiedDate\");\n```\n\nWhen you apply this order, the results are arranged in descending order of the\nquery entity's `modifiedDate` attribute. Thus the most recently modified\nentities (guestbook entries, in our example) appear first and the least recently\nmodified entities appear last.\n \nLike Hibernate criteria, Liferay's dynamic queries are *chain-able*: you can add\ncriteria to, set projections on, and add orders to Liferay's dynamic query\nobjects just by appending the appropriate method calls to the query object. For\nexample, the following snippet demonstrates chaining a restriction criterion and\na projection to a dynamic query object declaration:\n\n```java\nDynamicQuery guestbookQuery = DynamicQueryFactoryUtil.forClass(Guestbook.class)\n    .add(RestrictionsFactoryUtil.eq(\"name\", guestbookName))\n    .setProjection(ProjectionFactoryUtil.property(\"guestbookId\"));\n```\n\nIt's time to execute your dynamic query.\n\n## Executing the Dynamic Query\n\nIn the previous article, you ran Service Builder after\n[defining your custom finder](/docs/7-2/appdev/-/knowledge_base/a/defining-a-custom-finder-method).\nService Builder\ngenerated a `dynamicQuery(DynamicQuery dynamicQuery)` method in your\n`*LocalServiceBaseImpl` class. Using a `*LocalService` instance, invoke\n`dynamicQuery` method, passing it your dynamic query. Here's an example dynamic\nquery execution.\n\n```java\nList<Entity> entities = _someLocalService.dynamicQuery(entityQuery);\n\nreturn entities;\n```\n\nThe dynamic query execution returns a list of entities and the finder method\nreturns that list. \n\n| **Note:** Service Builder not only generates a `public List\n| dynamicQuery(DynamicQuery dynamicQuery)` method in `*LocalServiceBaseImpl` but\n| it also generates `public List dynamicQuery(DynamicQuery dynamicQuery, int\n| start, int end)` and `public List dynamicQuery(DynamicQuery dynamicQuery, int\n| start, int end, OrderByComparator orderByComparator)` methods. You can go back\n| to\n| [defining custom finder methods](/docs/7-2/appdev/-/knowledge_base/a/defining-a-custom-finder-method)\n| and either modify your finder method or create overloaded versions of it to take\n| advantage of these extra methods and their parameters. The `int start` and `int\n| end` parameters are useful when paginating a result list. `start` is the lower\n| bound of the range of model entity instances and `end` is the upper bound. The\n| `OrderByComparator orderByComparator` is the comparator by which to order the\n| results.\n\nTo use the overloaded `dynamicQuery` methods of your `*LocalServiceBaseImpl`\nclass in the (optionally overloaded) custom finders of your `*FinderImpl` class,\njust choose the appropriate methods for running the dynamic queries: \n`dynamicQuery(entryQuery)`, or\n`dynamicQuery(entryQuery, start, end)` or\n`dynamicQuery(entryQuery, start, end, orderByComparator)`.\n\nGreat! You've now created a finder method using Liferay's Dynamic Query API.\nYour last step is to add a service method that calls your finder.\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/09-advanced-queries/05-accessing-your-custom-finder-method-from-the-service-layer.markdown",
    "content": "---\nheader-id: accessing-your-custom-finder-method-from-the-service-layer\n---\n\n# Accessing Your Custom Finder Method from the Service Layer\n\n[TOC levels=1-4]\n\nSo far, you've created a `*FinderImpl` class, defined a `findBy*` finder method\nin that class, and implemented the finder method using Dynamic Query or custom\nSQL. Now how do you call your finder method from the service layer?\n\nWhen you ran Service Builder (if you haven't run it yet; run it now), the\n`*Finder` interface was generated (e.g., `GuestbookFinder`). For proper\nseparation of concerns, only a local or remote service implementation (i.e.,\n`*LocalServiceImpl` or `*ServiceImpl`) in your service module should invoke the\n`*Finder` class. The portlet classes in your application's web module invoke the\nbusiness logic of the services published from your application's service module.\nThe services, in turn, access the data model using the persistence layer's\nfinder classes.\n\n| **Note:** In previous versions of @product@, your finder methods were\n| accessible via `*FinderUtil` utility classes. Finder methods are now injected\n| into your app's local services, removing the need to call finder utilities.\n\nYou'll add a method in the `*LocalServiceImpl` class that invokes the finder\nmethod implementation via the `*Finder` class. Then you'll rebuild your\napplication's service layer so that the portlet classes and JSPs in your web\nmodule can access the services.\n\nFor example, for the Guestbook application, you'd add the following method to\nthe `GuestbookEntryLocalServiceImpl` class:\n\n```java\npublic List<GuestbookEntry> findByEntryNameGuestbookName(String entryName,\n    String guestbookName) throws SystemException {\n\n    return entryFinder.findByEntryNameGuestbookName(String entryName,\n        String guestbookName);\n}\n```\n\nAfter you've added your `findBy*` method to your `*LocalServiceImpl` class, run\nService Builder to generate the interface and make the finder method available\nin the `EntryLocalService` class.\n\nNow you can indirectly call the finder method from your portlet class or from a\nJSP by calling `_entryLocalService.findByEntryNameGuestbookName(...)`!\n\nCongratulations on following the process of developing custom queries in\na custom finder and exposing it as a service for your portlet!\n\n## Related Topics\n\n[Creating Local Service](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder)\n\n[Invoking Local Services](/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services)\n"
  },
  {
    "path": "en/developer/appdev/articles/03-service-builder/09-advanced-queries/06-actionable-dynamic-query.markdown",
    "content": "---\nheader-id: actionable-dynamic-queries\n---\n\n# Actionable Dynamic Queries\n\n[TOC levels=1-4]\n\nSuppose you have over a million users, and you want to perform some kind of mass\nupdate to some of them. One approach might be to use a dynamic query to retrieve\nthe list of users in question. Once loaded into memory, you could loop through\nthe list and update each user. However, with over a million users, the memory\ncost of such an operation would be too great. In general, retrieving large\nnumbers of Service Builder entities using dynamic queries requires too much\nmemory and time. \n\nLiferay actionable dynamic queries solve this problem. Actionable dynamic\nqueries use a pagination strategy to load only small numbers of entities into\nmemory at a time and perform processing (i.e., perform an *action*) on each\nentity. So instead of trying to use a dynamic query to load a million users into\nmemory and then perform some processing on each of them, a much better strategy\nis to use an actionable dynamic query. This way, you can still process your\nmillion users, but only small numbers are loaded into memory at a time. \n\nHere's how to use actionable dynamic query:\n\n1.  Get an\n    [`ActionableDynamicQuery`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/ActionableDynamicQuery.html)\n    from your `*LocalService` by invoking its `getActionableDynamicQuery` \n    method. \n\n2.  Add query criteria and constraints, using the query's `setAddCriteriaMethod`\n    and `setAddOrderCriteriaMethod` methods. \n\n3.  Set an action to perform on the matching entities, using \n    `setPerformActionMethod`. \n\n4.  Execute the action on each matching entity, by invoking the query's \n    `performActions` method. \n\nThis method from a sample portlet creates an actionable dynamic query, adds a\nquery restriction and an action, and executes the query:\n\n```java\nprotected void massUpdate() {\n    ActionableDynamicQuery adq = _barLocalService.getActionableDynamicQuery();\n    \n    adq.setAddCriteriaMethod(new ActionableDynamicQuery.AddCriteriaMethod() {\n        \n        @Override\n        public void addCriteria(DynamicQuery dynamicQuery) {\n            dynamicQuery.add(RestrictionsFactoryUtil.lt(\"field3\", 100));\n        }\n        \n    });\n    \n    adq.setPerformActionMethod(new ActionableDynamicQuery.PerformActionMethod<Bar>() {\n        \n        @Override\n        public void performAction(Bar bar) {\n            int field3 = bar.getField3();\n            field3++;\n            bar.setField3(field3);\n            _barLocalService.updateBar(bar);\n        }\n        \n    });\n    \n    try {\n        adq.performActions();\n    }\n    catch (Exception e) {\n        e.printStackTrace();\n    }\n}\n```\n \nThe example method demonstrates executing an actionable dynamic query on `Bar`\nentities that match certain criteria. \n\n1.  Retrieve an \n    [`ActionableDynamicQuery`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/ActionableDynamicQuery.html)\n    from local service `BarLocalService`.\n\n    ```java\n    ActionableDynamicQuery adq = _barLocalService.getActionableDynamicQuery();\n    ```\n\n    | **Note:** Service Builder generates method `getActionableDynamicQuery()` in\n    | each entity's `*LocalService` interface and implements it in each entity's\n    | `*BaseLocalServiceImpl` class.\n    | \n    |     @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n    |     public ActionableDynamicQuery getActionableDynamicQuery();\n\n2.  Set query criteria to match `field3` values less than `100`.\n\n    ```java\n    adq.setAddCriteriaMethod(new ActionableDynamicQuery.AddCriteriaMethod() {\n\n         @Override\n         public void addCriteria(DynamicQuery dynamicQuery) {\n             dynamicQuery.add(RestrictionsFactoryUtil.lt(\"field3\", 100));\n         }\n\n     });\n     ```\n\n3.  Set an action to perform. The action increments the matching entity's \n    `field3` value. \n\n    ```java\n    adq.setPerformActionMethod(new ActionableDynamicQuery.PerformActionMethod<Bar>() {\n\n         @Override\n         public void performAction(Bar bar) {\n             int field3 = bar.getField3();\n             field3++;\n             bar.setField3(field3);\n             _barLocalService.updateBar(bar);\n         }\n\n     });\n     ```\n\n4.  Execute the action on each matching entity. \n\n    ```java\n    try {\n        adq.performActions();\n    }\n    catch (Exception e) {\n        e.printStackTrace();\n    }\n    ```\n\nActionable dynamic queries let you act on large numbers of entities in smaller\ngroups. It's an efficient and high performing way to update entities. \n\n## Related Topics\n\n[Creating Local Service](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder)\n\n[Invoking Local Services](/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services)\n"
  },
  {
    "path": "en/developer/appdev/articles/04-rest-builder/01-intro.markdown",
    "content": "---\nheader-id: rest-builder\n---\n\n# REST Builder\n\n[TOC levels=1-4]\n\n| **Note:** This documentation is in beta. Stay tuned for more to come! \n\n@product@'s headless REST APIs follow the \n[OpenAPI](https://swagger.io/docs/specification/about/) \nspecification and let your apps consume RESTful web services. These APIs are \ndeveloped using a mixture of the Contract First and Contract Last development \napproaches. This presents a best-of-both-worlds approach to API development. For \nmore detailed information, see \n[Headless REST APIs](/docs/7-2/frameworks/-/knowledge_base/f/headless-rest-apis). \n\nHere, you'll learn how to use Liferay's \n[REST Builder tool](https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-rest-builder) \nto create headless REST APIs for your own apps. REST Builder is an API generator \nthat consumes OpenAPI profiles and generates the API scaffolding: JAX-RS \nendpoints, parsing, XML generation, and advanced features like filtering or \nmultipart (binary file) support. The developer only has to fill in the resource \nimplementations, calling @product@'s remote services. \n\nRead on to learn how to generate REST services with REST Builder! \n"
  },
  {
    "path": "en/developer/appdev/articles/04-rest-builder/02-generating-apis-rest-builder.markdown",
    "content": "---\nheader-id: generating-apis-with-rest-builder\n---\n\n# Generating APIs with REST Builder\n\n[TOC levels=1-4]\n\n| **Note:** This documentation is in beta. Stay tuned for more to come!\n\nFollow these steps to use REST Builder to create a headless REST API for your\napp:\n\n1.  Create a [project](/docs/7-2/reference/-/knowledge_base/r/creating-a-project).\n\n2.  Install REST Builder. For instructions on this, see\n    [REST Builder Gradle Plugin](/docs/7-2/reference/-/knowledge_base/r/rest-builder-gradle-plugin).\n\n3.  Run `gradlew clean deploy`. Note that your Gradle wrapper may not be in your\n    app's project directory, so you may need to use `..` to locate it (e.g.,\n    `../../../gradlew clean deploy`).\n\n4.  Create the `*-api` and `*-impl` projects with the usual\n    files (`build.gradle`, `bnd.bnd`). Also create a `rest-config.yaml` with the\n    author, paths, and packages. For example, here's the `rest-config.yaml` for\n    Liferay's `headless-delivery` API:\n\n    ```yaml\n    apiDir: \"../headless-delivery-api/src/main/java\"\n    apiPackagePath: \"com.liferay.headless.delivery\"\n    application:\n        baseURI: \"/headless-delivery\"\n        className: \"HeadlessDeliveryApplication\"\n        name: \"Liferay.Headless.Delivery\"\n    author: \"Javier Gamarra\"\n    clientDir: \"../headless-delivery-client/src/main/java\"\n    testDir: \"../headless-delivery-test/src/testIntegration/java\"\n    ```\n\n5.  In your `*-impl` module's root folder, write your OpenAPI profile in YAML.\n    You can use the\n    [Swagger Editor](https://editor.swagger.io/)\n    to validate syntax and ensure compliance with the OpenAPI specification.\n\n6.  In your `*-impl` module folder, run `gradlew buildREST` (make\n    sure you locate your Gradle wrapper as instructed in step two above).\n\n7.  REST Builder generates the interfaces with the JAX-RS endpoints. It also\n    generates a `*ResourceImpl` class where you must implement the business\n    logic for each service.\n\n8.  After implementing the business logic for each service, deploy your modules.\n    Your APIs are then available at this URL:\n\n        http://[host]:[port]/o/[APPLICATION_CLASSNAME]/[OPEN_API_VERSION]/\n\n    You can also execute `jaxrs:check` in the OSGi console to see all the JAX-RS\n    endpoints.\n\n## Related Topics\n\n[REST Builder](/docs/7-2/appdev/-/knowledge_base/a/rest-builder)\n\n[Headless REST APIs](/docs/7-2/frameworks/-/knowledge_base/f/headless-rest-apis)\n\n[REST Builder Gradle Plugin](/docs/7-2/reference/-/knowledge_base/r/rest-builder-gradle-plugin)"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/01-troubleshooting-application-development-issues-intro.markdown",
    "content": "---\nheader-id: troubleshooting-application-development-issues\n---\n\n# Troubleshooting Application Development Issues\n\n[TOC levels=1-4]\n\nWhen coding on any platform, you can sometimes run into issues that have no\nclear resolution. This can be particularly frustrating. If you have issues\nbuilding, deploying, or running apps and modules, you want to resolve them\nfast. These frequently asked questions and answers help you troubleshoot and\ncorrect problems.\n\nHere are the troubleshooting sections:\n\n-   [Modules](#modules)\n-   [Services and Components](#services-and-components)\n-   [Front-end](/docs/7-2/appdev/-/knowledge_base/a/troubleshooting-front-end-development-issues)\n\nClick a question to view the answer.\n\n## Modules\n\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">How can I configure dependencies on Liferay artifacts?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>See <a href=\"/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies\">Configuring Dependencies</a>. </p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">What are optional package imports and how can I specify them?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>When developing modules, you can declare <em>optional</em> package imports. An optional package import is one your module can use if it's available, but can still function without it. <a href=\"/docs/7-2/appdev/-/knowledge_base/a/declaring-optional-import-package-requirements\">Specifying optional package imports</a> is straightforward. </p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">How can I connect to a JNDI data source from my module?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>Connecting to an application server's JNDI data sources from Liferay's OSGi environment is almost the same as connecting to them from the Java EE environment. In an OSGi environment, the only difference is that you must <a href=\"/docs/7-2/appdev/-/knowledge_base/a/connecting-to-data-sources-using-jndi\">use @product@'s class loader to load the application server's JNDI classes</a>. </p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">My module has an unresolved requirement. What can I do?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>If one of your bundles imports a package that no other bundle in the Liferay OSGi runtime exports, @product@ reports an unresolved requirement:</p>\n    <pre><code>! could not resolve the bundles: ...\n    Unresolved requirement: Import-Package: ...\n    ...\n    Unresolved requirement: Require-Capability ...\n    </code></pre>\n    <p>To satisfy the requirement, <a href=\"/docs/7-2/appdev/-/knowledge_base/a/resolving-bundle-requirements\">find a module that provides the capability, add it to your build file's dependencies, and deploy it</a>. </p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">An IllegalContextNameException reports that my bundle's context name does not follow Bundle-SymbolicName syntax. How can I fix the context name?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p><a href=\"/docs/7-2/appdev/-/knowledge_base/a/resolving-bundle-symbolicname-syntax-issues\">Adjust the <code>Bundle-SymbolicName</code> to adhere to the syntax</a>. </p>\n  </div>\n</div>\n\n<!-- https://github.com/liferay/liferay-docs/blob/7.1.x/en/develop/tutorials/articles/380-troubleshooting/10-js-css-changes.markdown needs to be ported to 7.2+ jhinkey\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">Why aren't my module's JavaScript and CSS changes showing?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p><a href=\"/docs/7-2/appdev/-/knowledge_base/a/why-arent-my-modules-javascript-and-css-changes-showing\">Incorrect component properties or stale browser cache can prevent JavaScript and CSS changes from showing</a>. </p>\n  </div>\n</div>\n-->\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">How can I adjust my module's logging?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>See <a href=\"/docs/7-2/appdev/-/knowledge_base/a/adjusting-module-logging\">Adjusting Module Logging</a>. </p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">How can I implement logging in my module or plugin?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p><a href=\"/docs/7-2/appdev/-/knowledge_base/a/implementing-logging\">Use Simple Logging Facade for Java (SLF4J) to log messages</a>.</p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">After creating a relational mapping between Service Builder entities, my portlet is using too much memory. What can I do?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p><a href=\"/docs/7-2/appdev/-/knowledge_base/a/disabling-cache-for-table-mapper-tables\">Disabling the cache related to the entity mapping lowers memory usage.</a>.</p>\n</div>\n</div>\n\n## Services and Components\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">How can I see what's happening in the OSGi container?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p><a href=\"/docs/7-2/appdev/-/knowledge_base/a/system-check\">Run a System Check.</a>. </p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">How can I detect unresolved OSGi components?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>module components that use Service Builder use Dependency Manager (DM) and most other module components use Declarative Services (DS). <a href=\"/docs/7-2/appdev/-/knowledge_base/a/detecting-unresolved-osgi-components\">Gogo shell commands and tools help you find and inspect unsatisfied component references for DM and DS components</a>. </p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">What is the safest way to call OSGi services from non-OSGi code?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>See <a href=\"/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker\n\">Calling Non-OSGi Code that Uses OSGi Services</a>. </p>\n  </div>\n</div>\n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/adjusting-module-logging.markdown",
    "content": "---\nheader-id: adjusting-module-logging\n---\n\n# Adjusting Module Logging\n\n[TOC levels=1-4]\n\n@product@ uses [Log4j](http://logging.apache.org/log4j/1.2/) logging\nservices. Here are the ways to configure logging for module classes and class\nhierarchies.\n\n-   *Log Levels* in\n    [@product@'s UI](/docs/7-2/user/-/knowledge_base/u/server-administration)\n-   Configure Log4j for multiple modules in a\n    `[anyModule]/src/main/resources/META-INF/module-log4j.xml` file.\n-   Configure Log4j for a specific module in a\n    `[Liferay Home]/osgi/log4j/[symbolicNameOfBundle]-log4j-ext.xml` file.\n-   Configure Log4j for an OSGi fragment host module in a\n    `/META-INF/module-log4j-ext.xml` file\n\nHere's an example Log4j XML configuration:\n\n```xml\n<?xml version=\"1.0\"?>\n<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\">\n    <category name=\"org.foo\">\n        <priority value=\"DEBUG\" />\n    </category>\n</log4j:configuration>\n```\n\nUse `category` elements to specify each class or class hierarchy to log messages\nfor. Set the `name` attribute to that class name or root package. The example\ncategory sets logging for the class hierarchy starting at package `org.foo`. Log\nmessages at or above the `DEBUG` log level are printed for classes in `org.foo`\nand classes in packages starting with `org.foo`.\n\nSet each category's `priority` element to the log\n[level](http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Level.html)\n(priority) you want.\n\n-   ALL\n-   TRACE\n-   DEBUG\n-   INFO\n-   WARN\n-   ERROR\n-   FATAL\n-   OFF\n\nThe log messages are printed to Liferay log files in `[Liferay_Home]/logs`.\n\nYou can see examples of module logging in several Liferay sample projects. For\nexample, the [action-command-portlet](https://github.com/liferay/liferay-blade-samples/tree/master/gradle/apps/action-command-portlet),\n[document-action](https://github.com/liferay/liferay-blade-samples/tree/master/gradle/extensions/document-action), and\n[service-builder/jdbc](https://github.com/liferay/liferay-blade-samples/tree/master/gradle/apps/service-builder/jdbc)\nsamples (among others) leverage module logging.\n\n| **Note:** If the log level configuration isn't appearing (e.g., you set the \n| log level to `ERROR` but you're still getting `WARN` messages), make sure the \n| log configuration file name prefix matches the module's symbolic name. If you\n| have bnd installed, output from command `bnd print [path-to-bundle]` includes\n| the module's symbolic name\n| ([Here](https://github.com/bndtools/bnd/wiki/Install-bnd-on-the-command-line)\n| are instructions for installing bnd for the command line).\n\nThat's it for module log configuration. You're all set to print the information\nyou want.\n\n## Related Topics\n\n[Implementing Logging](/docs/7-2/appdev/-/knowledge_base/a/implementing-logging)\n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/artifact-versions-dependencies.markdown",
    "content": "---\nheader-id: identifying-liferay-artifact-versions-for-dependencies\n---\n\n# Identifying Liferay Artifact Versions for Dependencies\n\n[TOC levels=1-4]\n\nWhen you're developing an application using Liferay APIs or tools---for example,\nyou might create a Service Builder application or use Message Bus or Asset\nFramework---you must determine which versions of Liferay artifacts (modules,\napps, etc.) you application's modules must specify as dependencies. To learn\nhow to find Liferay artifacts and  configure dependencies on them, see\n[Configuring Dependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies).\n\n## Related Topics\n\n[Configuring Dependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies)\n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/bsn-naming-syntax-issues.markdown",
    "content": "---\nheader-id: resolving-bundle-symbolicname-syntax-issues\n---\n\n# Resolving Bundle-SymbolicName Syntax Issues\n\n[TOC levels=1-4]\n\nLiferay's OSGi Runtime framework sometimes throws an\n`IllegalContextNameException`. Often, this is because an OSGi bundle's\n`Bundle-SymbolicName` manifest header has a space in it.\n\nThe `Bundle-SymbolicName` uniquely identifies the bundle---along with the\n`Bundle-Version` manifest header---and cannot contain spaces. To follow naming\nbest practices, use a reverse-domain name in your `Bundle-SymbolicName`. For\nexample, a module with the domain `troubleshooting.liferay.com` would be\nreversed to `com.liferay.troubleshooting.`.\n\nThere are three ways to specify a bundle's `Bundle-SymbolicName`:\n\n1.  `Bundle-SymbolicName` header in a bundle's `bnd.bnd` file.\n\n2.  `Bundle-SymbolicName` header in a plugin WAR's\n    `liferay-plugin-package.properties` file.\n\n3.  Plugin WAR file name, if the WAR's `liferay-plugin-package.properties` has\n    no `Bundle-SymbolicName` header.\n\nFor plugin WARs, specifying the `Bundle-SymbolicName` in the\n`liferay-plugin-package.properties` file is preferred. \n\nFor example, if you deploy a plugin WAR that has no `Bundle-SymbolicName` header\nin its `liferay-plugin-package.properties`, the [WAB\nGenerator](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\nuses the WAR's name as the WAB's `Bundle-SymbolicName`. If the WAR's name has a\nspace in it (e.g., `space-program-theme v1.war`) an\n`IllegalContextNameException` occurs on deployment.\n\n    org.apache.catalina.core.ApplicationContext.log The context name 'space-program-theme v1' does not follow Bundle-SymbolicName syntax.\n    org.eclipse.equinox.http.servlet.internal.error.IllegalContextNameException: The context name 'space-program-theme v1' does not follow Bundle-SymbolicName syntax.\n\nHowever you set your a `Bundle-SymbolicName`, refrain from using spaces.\n\n## Related Topics\n\n[Using the WAB Generator](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/calling-osgi-services-instead-of-static-service-utils.markdown",
    "content": "---\nheader-id: calling-non-osgi-code-that-uses-osgi-services\n---\n\n# Calling Non-OSGi Code that Uses OSGi Services\n\n[TOC levels=1-4]\n\n@product@'s static service utilities (e.g., `UserServiceUtil`,\n`CompanyServiceUtil`, `GroupServiceUtil`, etc.) are examples of non-OSGi code\nthat use OSGi services. Service Builder generates them for backwards\ncompatibility purposes only. If you're tempted to call a `*ServiceUtil` class or\nyour existing code calls one, access the `*Service` directly instead using one\nthese alternatives:\n\n-   If your class is a Declarative Services component, use an\n    [`@Reference` annotation](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services)\n    to access the `*Service` class.\n\n-   If your class isn't a Declarative Services component, use a\n    [`ServiceTracker`](/docs/7-2/frameworks/-/knowledge_base/f/service-trackers-for-osgi-services)\n    to access the `*Service` class. \n\nYou can check the state of @product@'s services in \n[the Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell).\nThe `scr:list` Gogo shell command shows all Declarative Services components,\nincluding inactive ones from unsatisfied dependencies. To find unsatisfied\ndependencies for Service Builder services, use the Dependency Manager's\n`dependencymanager:dm wtf` command. Note that these commands only show\ncomponents that haven't been activated because of unsatisfied dependencies. They\ndon't show pure service trackers that are waiting for a service because of\nunsatisfied dependencies. \n\n## Related Topics\n\n[Detecting Unresolved OSGi Components](/docs/7-2/appdev/-/knowledge_base/a/detecting-unresolved-osgi-components)\n\n[Felix Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/connecting-to-jndi-data-sources.markdown",
    "content": "---\nheader-id: connecting-to-data-sources-using-jndi\n---\n\n# Connecting to JNDI Data Sources\n\n[TOC levels=1-4]\n\nConnecting to an application server's JNDI data sources from @product@'s OSGi\nenvironment is almost the same as connecting to them from the Java EE\nenvironment. In an OSGi environment, the only difference is that you must use\n@product@'s class loader to load the application server's JNDI classes. The\nfollowing code demonstrates this.\n\n```java\nThread thread = Thread.currentThread();\n\n// Get the thread's class loader. You'll reinstate it after using\n// the data source you look up using JNDI\n\nClassLoader origLoader = thread.getContextClassLoader();\n\n// Set Liferay's class loader on the thread\n\nthread.setContextClassLoader(PortalClassLoaderUtil.getClassLoader());\n\ntry {\n\n        // Look up the data source and connect to it\n\n        InitialContext ctx = new InitialContext();\n        DataSource datasource = (DataSource)\n            ctx.lookup(\"java:comp/env/jdbc/TestDB\");\n\n        Connection connection = datasource.getConnection();\n        Statement statement = connection.createStatement();\n\n        // Execute SQL statements here ...\n\n        connection.close();\n}\ncatch (NamingException ne) {\n\n    ne.printStackTrace();\n}\ncatch (SQLException sqle) {\n\n\tsqle.printStackTrace();\n}\nfinally {\n       // Switch back to the original context class loader\n\n       thread.setContextClassLoader(origLoader);\n}\n```\n\nThe example code sets @product@'s classloader on the thread to access the JNDI\nAPI. \n\n```java\nthread.setContextClassLoader(PortalClassLoaderUtil.getClassLoader());\n```\n\nIt uses JNDI to look up the data source.\n\n```java\nInitialContext ctx = new InitialContext();\nDataSource datasource = (DataSource)\n    ctx.lookup(\"java:comp/env/jdbc/TestDB\"); \n```\n\nAfter working with the data source, the code reinstates the thread's\noriginal classloader.\n\n```java\nthread.setContextClassLoader(origLoader);\n```\n\nHere are the class imports for the example code:\n\n```java\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport javax.naming.InitialContext;\nimport javax.naming.NamingException;\nimport javax.sql.DataSource;\nimport com.liferay.portal.kernel.util.PortalClassLoaderUtil;\n```\n\nYour applications can use similar code to access a data source. Make sure to\nsubstitute `jdbc/TestDB` with your data source name. \n\n| **Note**: An OSGi bundle's attempt to connect to a JNDI data source without\n| using @product@'s classloader results in a `java.lang.ClassNotFoundException`.\n| For example, here's an exception from attempting to use Apache Tomcat's JNDI \n| API without using @product@'s classloader:\n| \n|     javax.naming.NoInitialContextException: Cannot instantiate class:\n|     org.apache.naming.java.javaURLContextFactory [Root exception is\n|     java.lang.ClassNotFoundException:\n|     org.apache.naming.java.javaURLContextFactory]\n\nAn easier way to work with databases is to connect to them using Service Builder. \n\n<!--\nadd link back for 'connect to them using Service Builder' once article is available \n-->\n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/detecting-unresolved-osgi-components.markdown",
    "content": "---\nheader-id: detecting-unresolved-osgi-components\n---\n\n# Detecting Unresolved OSGi Components\n\n[TOC levels=1-4]\n\n@product@ includes \n[Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell) \ncommands that come in handy when trying to diagnose a problem due to an \nunresolved OSGi component. The specific tools to use depend on the component \nframework of the unresolved component. Most @product@ components are developed \nusing Declarative Services (DS), also known as SCR (Service Component Runtime). \nAn exception to this is @product@'s Service Builder services, which are\nDependency Manager (DM) components. Both \n[Declarative Services](http://felix.apache.org/documentation/subprojects/apache-felix-service-component-runtime.html) \nand \n[Dependency Manager](http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html) \nare Apache Felix projects. \n\nThe unresolved component troubleshooting instructions are divided into these\nsections:\n\n-   [Declarative Services Components](#declarative-services-components)\n    -   [Declarative Services Unsatisfied Component Scanner](#declarative-services-unsatisfied-component-scanner)\n    -   [ds:unsatisfied Command](#dsunsatisfied-command)\n-   [Service Builder Components](#service-builder-components)\n    -   [Unavailable Component Scanner](#unavailable-component-scanner)\n    -   [dm na Command](#dm-na-command)\n    -   [`ServiceProxyFactory`](#serviceproxyfactory)\n\n## Declarative Services Components\n\nStart with DS, since most @product@ components, apart from Service Builder\ncomponents, are DS components. Suppose one of your bundle's components has an\nunsatisfied service reference. How can you detect this? Two ways: \n\n-   Enable a\n    [Declarative Services Unsatisfied Component Scanner](#declarative-services-unsatisfied-component-scanner)\n    to report unsatisfied references automatically or \n\n-   Use the\n    [Gogo shell command `ds:unsatisfied`](#dsunsatisfied-command)\n    to check for them manually.\n\n### Declarative Services Unsatisfied Component Scanner\n\nHere's how to enable the unsatisfied component scanner:\n\n1.  Create a file\n    `com.liferay.portal.osgi.debug.declarative.service.internal.configuration.UnsatisfiedComponentScannerConfiguration.cfg`. \n\n2.  Add the following content:\n\n    ```properties\n    unsatisfiedComponentScanningInterval=5\n    ```\n\n3.  Copy the file into `[LIFERAY_HOME]/osgi/configs`.\n\nThe scanner detects and logs unsatisfied service component references. The log\nmessage describes the bundle, the referencing DS component class, and the\nreferenced component. \n\nHere's an example scanner message: \n\n```\n11:18:28,881 WARN  [Declarative Service Unsatisfied Component Scanner][UnsatisfiedComponentScanner:91]\nBundle {id: 631, name: com.liferay.blogs.web, version: 2.0.0}\n    Declarative Service {id: 3333, name: com.liferay.blogs.web.internal.portlet.action.EditEntryMVCRenderCommand, unsatisfied references:\n        {name: ItemSelectorHelper, target: null}\n    }\n```\n\nThe message above warns that the `com.liferay.blogs.web` bundle's DS component\n`com.liferay.blogs.web.internal.portlet.action.EditEntryMVCRenderCommand` has an\nunsatisfied reference to a component of type `ItemSelectorHelper`. The\nreferencing component's ID (SCR ID) is `3333` and its bundle ID is `631`. \n\n### ds:unsatisfied Command\n\nAnother way to detect unsatisfied component references is to invoke the Gogo\nshell command `ds:unsatisfied`. \n\n-   `ds:unsatisfied` shows all unsatisfied DS components\n-   `ds:unsatisfied [BUNDLE_ID]` shows the bundle's unsatisfied DS components\n\nTo view more detailed information about the unsatisfied DS component, pass the\ncomponent's ID to the command `scr:info [component ID]`. For example, the\nfollowing command does this for a component with ID `1701`: \n\n```bash\ng! scr:info 1701\n*** Bundle: org.foo.bar.command (507)\nComponent Description:\n    Name: org.foo.bar.command\n    Implementation Class: org.foo.bar.command.FooBarCommand\n    Default State: enabled\n    Activation: delayed\n    Configuration Policy: optional\n    Activate Method: activate\n    Deactivate Method: deactivate\n    Modified Method: -\n    Configuration Pid: [org.foo.bar.command]\n    Services:\n        org.foo.bar.command.DuckQuackCommand\n    Service Scope: singleton\n    Reference: Duck\n        Interface Name: org.foo.bar.api.Foo\n        Cardinality: 1..1\n        Policy: static\n        Policy option: reluctant\n        Reference Scope: bundle\n    Component Description Properties:\n        osgi.command.function = foo\n        osgi.command.scope = bar\n    Component Configuration:\n        ComponentId: 1701\n        State: unsatisfied reference\n        UnsatisfiedReference: Foo\n        Target: null\n        (no target services)\n        Component Configuration Properties:\n        component.id = 1701\n        component.name = org.foo.bar.command\n        osgi.command.function = foo\n        osgi.command.scope = bar\n```\n\nIn the `Component Configuration` section, `UnsatisfiedReference` lists the\nunsatisfied reference's type. This bundle's component isn't working because it's\nmissing a `Foo` service. Now you can focus on why `Foo` is unavailable. The\nsolution may be as simple as starting or deploying a bundle that provides the\n`Foo` service. \n\n## Service Builder Components\n\nService Builder modules are implemented using Spring. @product@ uses\n[the Apache Felix Dependency Manager](http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html) \nto manage Service Builder module OSGi components via the \n[Portal Spring Extender](https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.portal.spring.extender/) \nmodule.\n\nWhen developing a Liferay Service Builder application, you might sometimes have\nan unresolved Spring-related OSGi component. This can occur if you update your\napplication's database schema but forget to trigger an upgrade. \n\n<!-- Uncomment once article is available\n(for information\non creating database upgrade processes for your @product@ applications, see the\ntutorial \nCreating an Upgrade Process for Your App). \n-->\n\nThese features detect unresolved Service Builder related components. \n\n-   [Unavailable Component Scanner](#unavailable-component-scanner)\n-   [dm na Command](#dm-na-command)\n-   [ServiceProxyFactory](#serviceproxyfactory)\n\n### Unavailable Component Scanner\n\nThe\n[OSGi Debug Spring Extender](https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.portal.osgi.debug.spring.extender/)\nmodule's Unavailable Component Scanner reports missing components in modules\nthat use Service Builder. Here's how to enable the scanner:\n\n1.  Create the configuration file\n    `com.liferay.portal.osgi.debug.spring.extender.internal.configuration.UnavailableComponentScannerConfiguration.cfg`.\n\n2.  In the configuration file, set the time interval (in seconds) between scans:\n\n    `unavailableComponentScanningInterval=5`\n\n3.  Copy the file into `[LIFERAY_HOME]/osgi/configs`.\n\nThe scanner reports Spring extender dependency manager component status on the\nset interval. If all components are registered, the scanner sends a confirmation\nmessage. \n\n```\n11:10:53,817 INFO  [Spring Extender Unavailable Component Scanner][UnavailableComponentScanner:166] All Spring extender dependency manager components are registered\n```\n\nIf a component is unavailable, it warns you:\n\n```\n11:13:08,851 WARN  [Spring Extender Unavailable Component Scanner][UnavailableComponentScanner:173] Found unavailable component in bundle com.liferay.screens.service_1.0.28 [516].\nComponent ComponentImpl[null com.liferay.portal.spring.extender.internal.context.ModuleApplicationContextRegistrator@1541eee] is unavailable due to missing required dependencies: ServiceDependency[interface com.liferay.blogs.service.BlogsEntryService null].\n```\n\nComponent unavailability, such as what's reported above, can occur when DS\ncomponents and Service Builder components are published and used in the same\nmodule. Use separate modules to publish DS components and Service Builder\ncomponents. \n\n### dm na Command\n\nDependency Manager's\n[Gogo shell command `dm`](http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager/tutorials/leveraging-the-shell.html)\nlists all Service Builder components, their required services, and whether each\nrequired service is available. \n\nTo list unresolved components only execute this Gogo shell command:\n\n```bash\ndm na\n```\n\nThe `na` option stands for \"not available.\" \n\n### ServiceProxyFactory\n\n@product@'s logs report unresolved Service Builder components too. For example,\n@product@ logs an error when a Service Proxy Factory can't create a new instance\nof a Service Builder based entity because a service component is unresolved. \n\nThe following code demonstrates using a\n[`ServiceProxyFactory` class](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ServiceProxyFactory.html)\nto create a new entity instance:\n\n```java\nprivate static volatile MessageBus _messageBus =\n    ServiceProxyFactory.newServiceTrackedInstance(\n        MessageBus.class, MessageBusUtil.class, \"_messageBus\", true);\n```\n \nThis message alerts you to the unavailable service: \n\n```\n11:07:35,139 ERROR [localhost-startStop-1][ServiceProxyFactory:265] Service \"com.liferay.portal.kernel.messaging.sender.SingleDestinationMessageSenderFactory\" is unavailable in 60000 milliseconds while setting field \"_singleDestinationMessageSenderFactory\" for class \"com.liferay.portal.kernel.messaging.sender.SingleDestinationMessageSenderFactoryUtil\", will retry...\n```\n\nBased on the message above, there's no bundle providing the service\n`com.liferay.portal.kernel.messaging.sender.SingleDestinationMessageSenderFactory`. \n\nNow you can detect unresolved components, DS and DM components, automatically\nusing scanners, manually using Gogo shell commands, and programmatically using a\n`ServiceProxyFactory`. \n\n## Related Topics\n\n[System Check](/docs/7-2/appdev/-/knowledge_base/a/system-check)\n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/disabling-cache-on-table-mapper-tables.markdown",
    "content": "---\nheader-id: disabling-cache-for-table-mapper-tables\n---\n\n# Disabling Cache for Table Mapper Tables\n\n[TOC levels=1-4]\n\nService Builder creates relational mappings between entities. It uses mapping\ntables to associate the entities. In your `service.xml` file, both entities have \na `mapping-table` column attribute of the format `mapping-table=\"table1_table2\"`. \nFor example, a `service.xml` that maps `AssetEntry`s to `AssetCategory`s has an \n`AssetCategory` entity with this column: \n\n```xml\n<column entity=\"AssetEntry\" \nmapping-table=\"AssetEntries_AssetCategories\" \nname=\"entries\" type=\"Collection\" />\n```\n\nand an `AssetEntry` entity element with this column: \n\n```xml\n<column entity=\"AssetCategory\" \nmapping-table=\"AssetEntries_AssetCategories\" \nname=\"categories\" type=\"Collection\" />\n```\n\nBy default, a table mapper cache is associated with each mapping table. The\ncache optimizes object retrieval. In some cases, however, it's best to disable a\ntable mapper cache. \n\n## Why would I want to disable cache on a table mapper?\n\nSuper-large entity tables can result in a memory-hogging table mapper cache. For\nthis reason, consider disabling cache on a table mapper. \n\nThe\n[`table.mapper.cacheless.mapping.table.names` Portal property](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Table%20Mapper)\ndisables cache for table mappers associated with the specified mapping tables.\nHere's the default property setting:\n\n```properties\n##\n## Table Mapper\n##\n\n  #\n  # Set a list of comma delimited mapping table names that will not be using\n  # cache in their table mappers.\n  #\n  table.mapper.cacheless.mapping.table.names=\\\n    Users_Groups,\\\n    Users_Orgs,\\\n    Users_Roles,\\\n    Users_Teams,\\\n    Users_UserGroups\n```\n\nAll of the disabled caches above pertain to the `User` object because the table\nmappers tend to be much too large to have a useful cache---each `User` can\nhave several entries in each related table. \n\nPotential race conditions retrieving objects from the cache is another reason to\ndisable a table mapper.\n\nFor example,\n[LPS-84374](https://issues.liferay.com/browse/LPS-84374) describes a race\ncondition in which a custom entity's table mapper cache can be cleared while in\nuse, causing transactional rollbacks. Publishing `AssetEntry`s clears all\nassociated table mapper caches. If they're published at the same time getter\nmethods are retrieving objects from the `AssetEntries_AssetCategories` mapping\ntable, transaction rollbacks occur. \n\n## Disabling a Table Mapper Cache\n\nAdding a mapping table name to the `table.mapper.cacheless.mapping.table.names`\nPortal property disables the associated table mapper cache.\n\n1.  In your\n    [`[Liferay_Home]/portal-ext.properties` file](/docs/7-1/deploy/-/knowledge_base/d/liferay-home), \n    add the current `table.mapper.cacheless.mapping.table.names` property\n    setting. The setting is in your @product@ installation's\n    `portal-impl.jar/portal.properties` file.\n\n2.  Append your mapping table name to the list. For example, to disable the\n    cache associated with a mapping table named `AssetEntries_AssetCategories`,\n    add that name to the list. \n\n    ```properties\n    table.mapper.cacheless.mapping.table.names=\\\n        Users_Groups,\\\n        Users_Orgs,\\\n        Users_Roles,\\\n        Users_Teams,\\\n        Users_UserGroups,\\\n        AssetEntries_AssetCategories\n    ```\n\n3.  Restart the @product@ instance to delete the table mapper cache. \n\nYou've disabled an unwanted table mapper cache. \n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/implementing-logging.markdown",
    "content": "---\nheader-id: implementing-logging\n---\n\n# Implementing Logging\n\n[TOC levels=1-4]\n\n@product-ver@ uses the Log4j logging framework, but it may be replaced in the\nfuture. It's a best practice to use [Simple Logging Facade for Java\n\\(SLF4J\\)](https://www.slf4j.org/) to log messages in your modules and\ntraditional plugins. SLF4J is already integrated into @product@, so you can\nfocus on logging messages. \n\nHere's how to use SLF4J to log messages in a class:\n\n1.  Add a private static SLF4J\n    [`Logger` field](https://www.slf4j.org/apidocs/org/slf4j/Logger.html). \n\n    ```java\n    private static Logger _logger;\n    ```\n\n2.  Instantiate the logger. \n\n    ```java\n    _logger = LoggerFactory.getLogger(this.getClass().getName());\n    ```\n \n3.  Throughout your class, log messages where noteworthy things happen. \n\n    For example, \n\n    ```java\n    _logger.debug(\"...\");\n    _logger.warn(\"...\");\n    _logger.error(\"...\");\n    ...\n    ```\n\n    Use `Logger` methods appropriate for each message:\n \n    -  `trace`: Provides more information than debug. This is the most verbose\n       message level. \n    -  `debug`: Event and application information helpful for debugging.\n    -  `info`: High level events.\n    -  `warn`: Information that might, but does not necessarily, indicate a\n       problem.\n    -  `error`: Normal errors. This is the least verbose message level.\n\nLog verbosity should correlate with the log level set for the class or package.\nMake sure you provide additional information at log levels expected to be more\nverbose, such as `info` and `debug`.\n\nYou're all set to add logging to your modules and traditional plugins. \n\n## Related Topics\n\n[Adjusting Module Logging](/docs/7-2/appdev/-/knowledge_base/a/adjusting-module-logging)\n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/optional-imports.markdown",
    "content": "---\nheader-id: declaring-optional-import-package-requirements\n---\n\n# Declaring Optional Import Package Requirements\n\n[TOC levels=1-4]\n\nWhen developing modules, you can declare *optional* dependencies. An optional\ndependency is one your module can use if available, but can still function\nwithout it. \n\n| **Important:** Try to avoid optional dependencies. The best module designs\n| rely on normal dependencies. If an optional dependency seems desirable, your\n| module may be trying to provide more than one distinct type of functionality.\n| In such a situation, it's best to split it into multiple modules that provide\n| smaller, more focused functionality.\n\nIf you decide that your module requires an optional dependency, follow these \nsteps to add it:\n\n1.  In your module's `bnd.bnd` file, declare the package your module\n    optionally depends on:\n\n    ```\n    Import-Package: com.liferay.demo.foo;resolution:=\"optional\"\n    ```\n\n    Note that you can use either an optional or dynamic import. The differences \n    are explained \n    [here](https://osgi.org/specification/osgi.core/7.0.0/framework.module.html#i2548181). \n\n2.  Create a component to use the optional package: \n\n    ```java\n    import com.liferay.demo.foo.Foo; // A class from the optional package\n\n    @Component(\n        enabled = false // instruct declarative services to ignore this component by default\n    )\n    public class OptionalPackageConsumer implements Foo {...}\n    ```\n\n3.  Create a second component to be a controller for the first. The second\n    component checks the class loader for the optional class on the classpath. \n    If it's not there, this means you must catch any `ClassNotFoundException`.\n    For example: \n\n    ```java\n    @Component\n    public class OptionalPackageConsumerStarter {\n        @Activate\n        void activate(ComponentContext componentContext) {\n            try {\n                Class.forName(com.liferay.demo.foo.Foo.class.getName());\n\n                componentContext.enableComponent(OptionalPackageConsumer.class.getName());\n            }\n            catch (Throwable t) {\n                _log.warn(\"Could not find {}\", t.getMessage()); // Could use _log.info instead\n            }\n        }\n    }\n    ```\n\nIf the class loader check in the controller component is successful, the client \ncomponent is enabled. This check is automatically performed whenever there are \nany wiring changes to the module containing these components (Declarative \nServices components are always restarted when there are wiring changes). \n\nIf you install the module when the optional dependency is missing from\n@product@'s OSGi runtime, your controller component catches\na `ClassNotFoundException` and logs a warning or info message (or takes whatever\nother action you implement to handle this case). If you install the optional\ndependency, refreshing your module triggers the OSGi bundle lifecycle events\nthat trigger your controller's `activate` method and the check for the optional\ndependency. Since the dependency exists, your client component uses it. \n\nNote that you can refresh a bundle from\n[Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\nwith this command: \n\n    equinox:refresh [bundle ID] \n\n## Related Topics\n\n[Configuring Dependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies)\n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/resolving-bundle-requirements.markdown",
    "content": "---\nheader-id: resolving-bundle-requirements\n---\n\n# Resolving Bundle Requirements\n\n[TOC levels=1-4]\n\nIf one of your bundles needs a package that is not exported by any other bundle\nin the Liferay OSGi runtime, you get a bundle exception. Here's an example\nexception:\n\n```\n! could not resolve the bundles: [com.liferay.messaging.client.command-1.0.0.201707261701 org.osgi.framework.BundleException: Could not resolve module: com.liferay.messaging.client.command [1]\nUnresolved requirement: Import-Package: com.liferay.messaging.client.api; version=\"[1.0.0,2.0.0)\"\n-> Export-Package: com.liferay.messaging.client.api; bundle-symbolic-name=\"com.liferay.messaging.client.provider\"; bundle-version=\"1.0.0.201707261701\"; version=\"1.0.0\"; uses:=\"org.osgi.framework\"\ncom.liferay.messaging.client.provider [2]\nUnresolved requirement: Import-Package: com.liferay.messaging; version=\"[1.0.0,2.0.0)\"\n-> Export-Package: com.liferay.messaging; bundle-symbolic-name=\"com.liferay.messaging.api\"; bundle-version=\"1.0.0\"; version=\"1.0.0\"; uses:=\"com.liferay.petra.concurrent\"\ncom.liferay.messaging.api [12]\nUnresolved requirement: Import-Package: com.liferay.petra.io; version=\"[1.0.0,2.0.0)\"\n-> Export-Package: com.liferay.petra.io; bundle-symbolic-name=\"com.liferay.petra.io\"; bundle-version=\"1.0.0\"; version=\"1.0.0\"\ncom.liferay.petra.io [16]\nUnresolved requirement: Require-Capability osgi.extender; filter:=\"(osgi.extender=osgi.serviceloader.processor)\"\n```\n\nThe first line states *could not resolve the bundles*. What follows is a string\nof requirements that Liferay's OSGi Runtime could not resolve.\n\nThe bundle exception message follows this general pattern:\n\n-   Module A has an unresolved requirement (package or capability) `aaa.bbb`.\n-   Module B provides `aaa.bbb` but has an unresolved requirement `ccc.ddd`.\n-   Module C provides `ccc.ddd` but has an unresolved requirement `eee.fff`.\n-   etc.\n-   Module Z provides `www.xxx` but has an unresolved  requirement `yyy.zzz`.\n\nThe pattern stops at the final unsatisfied requirement. The last module's\ndependencies are key to resolving the bundle exception. There are two possible\ncauses:\n\n1.  A dependency that satisfies the final requirement might be missing from the\n    build file.\n\n2.  A dependency that satisfies the final requirement might not be deployed.\n\nBoth cases require deploying a bundle that provides the missing requirement. \n\nThe example bundle exception concludes that module `com.liferay.petra.io`\nrequires capability `osgi.extender;\nfilter:=\"(osgi.extender=osgi.serviceloader.processor)\"`. To resolve the\nrequirement, make sure all of `com.liferay.petra.io`'s dependencies are\ndeployed. \n\nThe `com.liferay.petra.io` module's `build.gradle` file lists its dependencies: \n\n```groovy\ndependencies {\n    provided group: \"com.liferay\", name: \"com.liferay.petra.concurrent\", version: \"1.0.0\"\n    provided group: \"com.liferay\", name: \"com.liferay.petra.memory\", version: \"1.0.0\"\n    provided group: \"org.apache.aries.spifly\", name: \"org.apache.aries.spifly.dynamic.bundle\", version: \"1.0.8\"\n    provided group: \"org.slf4j\", name: \"slf4j-api\", version: \"1.7.2\"\n    testCompile group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"default\"\n}\n```\n\nThen use\n[Felix Gogo Shell's `lb command`](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\nto verify the dependencies are in Liferay's OSGi Runtime:\n\n```bash\nlb\nSTART LEVEL 1\n   ID|State      |Level|Name\n    0|Active     |    0|OSGi System Bundle (3.10.100.v20150529-1857)|3.10.100.v20150529-1857\n    1|Active     |    1|com.liferay.messaging.client.command (1.0.0.201707261923)|1.0.0.201707261923\n    2|Active     |    1|com.liferay.messaging.client.provider (1.0.0.201707261927)|1.0.0.201707261927\n    3|Active     |    1|Apache Felix Configuration Admin Service (1.8.8)|1.8.8\n    4|Active     |    1|Apache Felix Log Service (1.0.1)|1.0.1\n    5|Active     |    1|Apache Felix Declarative Services (2.0.2)|2.0.2\n    6|Active     |    1|Meta Type (1.4.100.v20150408-1437)|1.4.100.v20150408-1437\n    7|Active     |    1|org.osgi:org.osgi.service.metatype (1.3.0.201505202024)|1.3.0.201505202024\n    8|Active     |    1|Apache Felix Gogo Command (0.16.0)|0.16.0\n    9|Active     |    1|Apache Felix Gogo Runtime (0.16.2)|0.16.2\n   10|Active     |    1|Apache Felix Gogo Runtime (1.0.0)|1.0.0\n...\n```\n\nThe dependency module `org.apache.aries.spifly.dynamic.bundle` is missing\nfrom the runtime bundle list. The `org.apache.aries.spifly.dynamic.bundle`\nmodule's `MANIFEST.MF` file shows it provides the requirement capability\n`osgi.extender; filter:=\"(osgi.extender=osgi.serviceloader.processor)\"`:\n\n```\nProvide-Capability: osgi.extender;osgi.extender=\"osgi.serviceloader.regi\n strar\";version:Version=\"1.0\",osgi.extender;osgi.extender=\"osgi.servicel\n oader.processor\";version:Version=\"1.0\"\n```\n\nThis capability `osgi.extender; filter:=\"(osgi.extender=osgi.serviceloader.processor)\"`\nis the unresolved\nrequirement we identified earlier. Deploying this missing bundle\n`org.apache.aries.spifly.dynamic.bundle` satisfies the example module's\nrequirement and allows the module to resolve and install. \n\nYou can resolve your bundle exceptions by following steps similar to these. \n\n| Note: Bndtools's *Resolve* button can resolve bundle dependencies \n| automatically. You specify the bundles your application requires and Bndtools\n| adds transitive dependencies from your configured artifact repository.\n\n## Related Topics\n\n[Configuring Dependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies)\n\n[Adding Third Party Libraries to a Module](/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module)\n\n[Felix Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\n\n[Finding Artifacts](/docs/7-2/customization/-/knowledge_base/c/finding-artifacts)\n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/resolving-cnfe-and-cdnfe-in-osgi-bundles.markdown",
    "content": "---\nheader-id: resolving-classnotfoundexception-and-noclassdeffounderror-in-osgi-bundles\n---\n\n# Resolving ClassNotFoundException and NoClassDefFoundError in OSGi Bundles\n\n[TOC levels=1-4]\n\n`ClassNotFoundException` and `NoClassDefFoundError` are common, well known\nexceptions:\n\n-   `ClassNotFoundException` is thrown when looking up a class that isn't on the\n    classpath or using an invalid name to look up a class that isn't on the\n    runtime classpath. \n-   `NoClassDefFoundError` occurs when a compiled class references\n    another class that isn't on the runtime classpath.\n\nIn OSGi environments, however, there are additional cases where a\n`ClassNotFoundException` or `NoClassDefFoundError` can occur:\n\n1.  The missing class belongs to a module dependency that's an OSGi module. \n2.  The missing class belongs to a module dependency that's *not* an OSGi \n    module. \n3.  The missing class belongs to a global library, either at the @product@ \n    web app scope or the application server scope. \n4.  The missing class belongs to a Java runtime package.\n\nThis tutorial explains how to handle each case.\n\n## Case 1: The Missing Class Belongs to an OSGi Module\n\nIn this case, there are two possible causes: \n\n1.  **The module doesn't import the class's package:** For a module (or WAB) to \n    consume another module's exported class, the consuming module must import \n    the exported package that contains the class. To do this, you add an\n    `Import-Package` header in the consuming module's `bnd.bnd` file. If the\n    consuming module tries to access the class without importing the package, a \n    `ClassNotFoundException` or `NoClassDefFoundError` occurs. \n\n    Check the package name and make sure the consuming module imports the right\n    package. If the import is correct but you still get the exception or\n    error, the class might no longer exist in the package. \n\n2.  **The class no longer exists in the imported package:** Modules are changed\n    frequently in OSGi runtime environments. If you reference another module's\n    class that its developer removed, a `NoClassDefFoundError` or\n    `ClassNotFoundException` occurs. [Semantic Versioning](http://semver.org)\n    guards against this scenario: removing a class from an exported package\n    constitutes a new major version for that package. Neglecting to increment\n    the package's major version breaks dependent modules. \n\n    For example, say a module that consumes the class `com.foo.Bar` specifies\n    the package import `com.foo;version=[1.0.0, 2.0.0)`. The module uses\n    `com.foo` versions from `1.0.0` up to (but not including) `2.0.0`. The\n    first part of the version number (the `1` in `1.0.0`) represents the\n    *major* version. The consuming module doesn't expect any *major* breaking\n    changes, like a class removal. Removing `com.foo.Bar` from `com.foo`\n    without incrementing the  package to a new major version (e.g., `2.0.0`)\n    causes a `ClassNotFoundException` or `NoClassDefFoundError` when other\n    modules look  up or reference that class. \n\n    You have limited options when the class no longer exists in the package: \n\n    -   Adapt to the new API. To learn how to do this, read the \n        package's/module's Javadoc, release notes, and/or formal documentation. \n        You can also ask the author or search forums. \n\n    -   Revert to the module version you used previously. Deployed module \n        versions reside in `[Liferay_Home]/osgi/`. For details, see\n        [Backing up Liferay Installations](/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation). \n\n    Do what you think is best to get your module working properly. \n\nNow you know how to resolve common situations involving `ClassNotFoundException` \nor `NoClassDefFoundError`. For additional information on `NoClassDefFoundError`, \nsee OSGi Enroute's article \n[What is NoClassDefFoundError?](http://enroute.osgi.org/faq/class-not-found-exception.html). \n\n## Case 2: The Missing Class Doesn't Belong to an OSGi Module\n\nIn this case, you have two options: \n\n1.  Convert the dependency into an OSGi module so it can export the missing \n    class. Converting a non-OSGi `JAR` file dependency into an OSGi module that \n    you can deploy alongside your application is the ideal solution, so it\n    should be your first choice. \n\n2.  Embed the dependency in your module by embedding the dependency `JAR` file's\n    packages as private packages in your module. If you want to embed a non-OSGi\n    `JAR` file in your application, see [Resolving Third Party Library Package\n    Dependencies](https://portal.liferay.dev/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module). \n\n## Case 3: The Missing Class Belongs to a Global Library\n\nIn this case, you can configure @product@ so the OSGi system module exports the\nmissing class's package. Then your module can import it. You should **NOT**,\nhowever, undertake this lightly. If Liferay intended to make a global library\navailable for use by developers, the system module would already export this\nlibrary! Proceed only if you have no other solution, and watch out for\nunintended consequences. There are two ways to export the package: \n\n1.  In your `portal-ext.properties` file, use the property\n    [`module.framework.system.packages.extra`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Module%20Framework)\n    to specify the packages to export. Preserve the property's current list. \n\n2.  If the package you need is from a @product@ JAR, you might be able to add \n    the module to the list of exported packages in\n    `[LIFERAY_HOME]/osgi/core/com.liferay.portal.bootstrap.jar`'s\n    `META-INF/system.packages.extra.bnd` file. Try this option \n    only if the first option doesn't work. \n\nIf the package you need is from a @product@ module, (i.e., it's **NOT** \nfrom a global library), you can add the package to that module's `bnd.bnd` \nexports. You should **NOT**, however, undertake this lightly. The package would\nalready be exported if Liferay intended for it to be available. \n\n## Case 4: The Missing Class Belongs to a Java Runtime Package\n\n`rt.jar` (the JRE library) has non-public packages. If your module imports one\nof them, configure @product@'s system bundle to export the package to the module\nframework. \n\n1.  Add the current\n    [`module.framework.system.packages.extra` property setting](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Module%20Framework)\n    to a\n    [`portal-ext.properties` file](/docs/7-2/deploy/-/knowledge_base/d/portal-properties). \n    Your server's current setting is in the @product@ web application's\n    `/WEB-INF/lib/portal-impl.jar/portal.properties` file. \n\n2.  In your `portal-ext.properties` file, append the required Java runtime \n    package to the end of the  `module.framework.system.packages.extra`\n    property's package list. \n\n3.  Restart your server. \n\nYour module should resolve and install. \n\n## Related Topics\n\n[Backing up Liferay Installations](/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation)\n\n[Resolving Third Party Library Package Dependencies](https://portal.liferay.dev/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module)\n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/system-check.markdown",
    "content": "---\nheader-id: system-check\n---\n\n# System Check\n\n[TOC levels=1-4]\n\nDuring development, all kinds of strange things can happen in the OSGi\ncontainer. Liferay's `system:check` [Gogo\nshell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\ncommand can help you see what's happening. You can enable it to run as the last\nPortal startup step and you can execute it any time in Gogo shell. \n\n`system:check` aggregates these commands:\n\n-  [`ds:unsatisfied`](/docs/7-2/appdev/-/knowledge_base/a/detecting-unresolved-osgi-components#dsunsatisfied-command):\n    Reports unsatisfied Declarative Service components.\n\n-  [`dm na`](/docs/7-2/appdev/-/knowledge_base/a/detecting-unresolved-osgi-components#dm-na-command):\n    Reports unsatisfied Dependency Manager service components, including Service\n    Builder services. \n\nSystem checking functionality from future Liferay tools will be added to\n`system:check`.\n\n[Developer mode](/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes)\nruns `system:check` automatically on every startup. \n\nYou can enable `system:check` to run on startup outside of developer mode by\nsetting this property in your `portal-ext.properties` file:\n\n```properties\nmodule.framework.properties.initial.system.check.enabled=true\n```\n\nAs stated previously, you can run the `system:check` command any time in\n[Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell).\nEnjoy detecting unresolved components and other issues fast using\n`system:check`.\n\n## Related Topics\n\n[Detecting Unresolved OSGi Components](/docs/7-2/appdev/-/knowledge_base/a/detecting-unresolved-osgi-components)\n\n[Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\n"
  },
  {
    "path": "en/developer/appdev/articles/99-troubleshooting-application-development-issues/troubleshooting-front-end-dev.markdown",
    "content": "---\nheader-id: troubleshooting-front-end-development-issues\n---\n\n# Troubleshooting Front-End Development Issues\n\n[TOC levels=1-4]\n\nFront-end development involves many moving parts. Sometimes it's hard to tell \nwhat may be causing the issues you run into along the way. This can be \nparticularly frustrating. These frequently asked questions and answers help you \ntroubleshoot and correct problems arising during front-end development.\n\nHere are the troubleshooting sections:\n\n- [CSS](#css)\n- [Modules](#modules)\n- [Portlets](#portlets)\n\nClick a question to view the answer.\n\n## CSS\n\n<div class=\"ldn-faq-question\" id=\"broken-css-angular-app\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">Why are my CSS templates not applied in my Angular app?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>A known <a href=\"https://github.com/angular/angular/issues/4974\">bug</a> with Angular causes absolute URLs for CSS files not to be recognized.</p>\n    <p>Due to the nature of portals, a relative URL is not an option either because the app can be placed on any page.</p>\n    <p>To fix this, you can either provide the CSS with a theme or themelet, or you can specify the path to the CSS file with the <code>com.liferay.portlet.header-portlet-css</code> property in the portlet containing your Angular code.</p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\" id=\"portal-css-broken-ie\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">Why is Liferay Portal's CSS broken in Internet Explorer?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>By default CSS files are minified in the browser. This can cause issues in Internet Explorer. You can disable this behavior by including <code>theme.css.fast.load=false</code> and <code>minifier.enabled=false</code> in your <code>portal-ext.properties</code> file. </p>\n  </div>\n</div>\n\n## Modules\n\n<div class=\"ldn-faq-question\" id=\"jquery-anonymous-module-error\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">Why does my JQuery module throw an anonymous module error when I try to load it?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>If you're using an external library that you host, you must disable the <i>Expose Global</i> option as described in the <a href=\"/docs/7-2/frameworks/-/knowledge_base/f/using-external-javascript-libraries#using-libraries-that-you-host\">Using External JavaScript Libraries</a> tutorial.</p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\" id=\"source-maps-not-showing\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">Why are my source maps not showing for my Angular or Typescript module?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>This is due to <a href=\"https://issues.liferay.com/browse/LPS-83052\">LPS-83052</a>.</p>\n    <p>To solve this, activate the <a href=\"https://www.typescriptlang.org/docs/handbook/compiler-options.html\"><code>inlineSources</code> compiler option</a> via argument or your <code>tsconfig.json</code> file.</p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\" id=\"disable-bundler-analytics\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">I'm using the liferay-npm-bundler for multiple projects. How can I disable analytics tracking for the liferay-npm-bundler in my projects?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>There are a couple options you can use to disable reporting:</p>\n    <ul>\n      <li><p>Use the <code>--no-tracking</code> flag in your <code>package.json</code>'s build script to disable reporting:</p>\n      <p><pre><code>liferay-npm-bundler --no-tracking</code></pre></p>\n      </li>\n      <li>\n      <p>Create a <code>.liferay-npm-bundler-no-tracking</code> file in your project's root folder, or any of its ancestors, to disable reporting.</p>\n      <p>This equates to answering <code>No</code> to the <code>May liferay-npm-bundler anonymously report usage statistics to improve the tool over time?</code> question.</p>\n      </li>\n    </ul>\n  </div>\n</div>\n\n## Portlets\n\n<div class=\"ldn-faq-question\" id=\"angular-react-vue-portlet-disable-spa\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">I want to use a custom router in my Angular/React/Vue portlet. How can I disable the default Senna JS SPA engine in my portlet?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>By default, the <a href=\"/docs/7-2/frameworks/-/knowledge_base/f/automatic-single-page-applications#what-is-sennajs\">Senna JS SPA engine</a> is enabled in your portlets and sites. This disables full page reloads during portlet navigation.</p>\n    <p>If you want to use a custom router in your portlet instead, follow the <a href=\"/docs/7-2/frameworks/-/knowledge_base/f/automatic-single-page-applications#disabling-spa\">instructions in the SPA documentation</a> to blacklist your portlet from SPA.</p>\n  </div>\n</div>\n"
  },
  {
    "path": "en/developer/appdev/build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"Application Development articles\" basedir=\".\">\n\t<property name=\"project.dir\" value=\"../../../../liferay-docs\" />\n\n\t<import file=\"../build.xml\" />\n\n\t<property name=\"doc.dir\" value=\"appdev\"/>\n\t<property name=\"doc.type\" value=\"article\"/>\n\n</project>\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/.gitignore",
    "content": "build/*\ndist/*\nnode_modules/*\nliferay-npm-bundler-report.html\n.webpack/*"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/.npmbuildrc",
    "content": "{\n\t\"liferayDir\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.1.1-ga2-20181105121645556\\\\liferay-ce-portal-7.1.1-ga2\",\n\t\"webpack\": {\n\t\t\"rules\": [\n\t\t\t{\n\t\t\t\t\"test\": \"src\\\\\\\\.*\\\\.ts$\",\n\t\t\t\t\"use\": \"ts-loader\"\n\t\t\t}\n\t\t],\n\t\t\"extensions\": [\n\t\t\t\".ts\",\n\t\t\t\".js\"\n\t\t],\n\t\t\"mainModule\": \"index.ts\"\n\t}\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/.npmbundlerrc",
    "content": "{\n\t\"create-jar\": {\n\t\t\"output-dir\": \"dist\",\n\t\t\"features\": {\n\t\t\t\"js-extender\": true,\n\t\t\t\"web-context\": \"/my-angular-guestbook\"\n\t\t}\n\t},\n\t\"dump-report\": true,\n\t\"process-serially\": true\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/README.md",
    "content": "# my-angular-guestbook\n\nMy Angular Guestbook"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/assets/.placeholder",
    "content": ""
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/assets/app/add-entry.component.html",
    "content": "<!--\n<form>\n <fieldset>\n   <label>Name\n     <input #name />\n   </label><br/>\n   <label>Message\n     <input #message />\n   </label>\n </fieldset>\n \n <div class=\"button-footer\">\n   <button class=\"main-btn\" (click)=\"add(name.value, message.value)\">Add</button>\n   <button class=\"plain-btn\" (click)=\"goBack()\">Cancel</button>\n </div>\n</form>\n-->\n<form>\n  <div class=\"form-group\">\n    <label for=\"addEntryNameInput\">Name</label>\n    <input #name class=\"form-control\" id=\"addEntryNameInput\" aria-label=\"name\">\n  </div>\n  <div class=\"form-group mb-4\">\n    <label for=\"addEntryMessageInput\">Message</label>\n    <input #message class=\"form-control\" id=\"addEntryMessageInput\" aria-label=\"message\">\n  </div>\n  <button class=\"btn btn-primary mr-2\" (click)=\"add(name.value, message.value)\">Save</button>\n  <button class=\"btn btn-outline-secondary\" (click)=\"goBack()\">Cancel</button>\n</form>"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/assets/app/app.component.html",
    "content": "<h1>{{title}}</h1>\n\n<!--<router-outlet></router-outlet> removed for portal migration-->\n<nav>\n  <a routerLink=\"/view-guestbook\" routerLinkActive=\"active\">View Guestbook</a>\n</nav>\n<router-outlet></router-outlet>"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/assets/app/view-guestbook.component.html",
    "content": "<table class=\"table table-striped table-bordered mb-4\">\n  <thead class=\"thead-light\">\n    <tr>\n      <th>Name</th>\n      <th>Message</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr *ngFor=\"let entry of entries\">\n      <td>{{entry.name}}</td>\n      <td>{{entry.message}}</td>\n    </tr>\n  </tbody>\n</table>\n\n<button class=\"btn btn-outline-secondary\" routerLink=\"/add-entry\">Add Entry</button>\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/assets/css/add-entry.component.css",
    "content": ".button-footer {\n  display: flex;\n  margin-top: 1.5rem;\n}\n\n.button-footer > button {\n  margin-right: 1rem;\n  cursor: pointer;\n  display: inline-block;\n  border-radius: 0.25rem;\n}\n\ninput {\n  display: block\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/assets/css/app.component.css",
    "content": "nav > a {\n  color: #333;\n  background-color: #fff;\n  border-color: #ccc;\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/assets/css/styles.css",
    "content": "/*@import \"add-entry.component.css\";\n@import \"app.component.css\";\n@import \"view-guestbook.component.css\";*/\n\n.tag {\n\tfont-weight: bold;\n}\n\n.value {\n\tfont-style: italic;\n}\n\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/assets/css/view-guestbook.component.css",
    "content": "table {\n  font-family: arial, sans-serif;\n  border-collapse: collapse;\n  width: 100%;\n}\n\ntd, th {\n  border: 1px solid #000;\n  text-align: left;\n  padding: 8px;\n}\n\ntr:nth-child(even) {\n  background-color: #dddddd;\n}\n\n.button-footer {\n  display: flex;\n  margin-top: 1.5rem;\n}\n\n.button-footer > button {\n  margin-right: 1rem;\n  cursor: pointer;\n  display: inline-block;\n  border-radius: 0.25rem;\n}\n\ninput {\n  display: block\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/package.json",
    "content": "{\n\t\"name\": \"my-angular-guestbook\",\n\t\"version\": \"1.0.0\",\n\t\"description\": \"My Angular Guestbook\",\n\t\"devDependencies\": {\n\t\t\"liferay-npm-bundler\": \"^2.6.1\",\n\t\t\"liferay-npm-build-support\": \"^2.6.1\",\n\t\t\"copy-webpack-plugin\": \"^4.5.4\",\n\t\t\"webpack\": \"^4.0.0\",\n\t\t\"webpack-cli\": \"^3.0.0\",\n\t\t\"webpack-dev-server\": \"^3.0.0\",\n\t\t\"@angular-devkit/build-angular\": \"~0.12.0\",\n\t\t\"@angular/cli\": \"~7.2.3\",\n\t\t\"@angular/compiler-cli\": \"^7.2.0\",\n\t\t\"@angular/language-service\": \"^7.2.0\",\n\t\t\"@types/jasmine\": \"~2.8.8\",\n\t\t\"@types/jasminewd2\": \"~2.0.3\",\n\t\t\"@types/node\": \"~8.9.4\",\n\t\t\"codelyzer\": \"~4.5.0\",\n\t\t\"jasmine-core\": \"~2.99.1\",\n\t\t\"jasmine-spec-reporter\": \"~4.2.1\",\n\t\t\"karma\": \"~3.1.1\",\n\t\t\"karma-chrome-launcher\": \"~2.2.0\",\n\t\t\"karma-coverage-istanbul-reporter\": \"~2.0.1\",\n\t\t\"karma-jasmine\": \"~1.1.2\",\n\t\t\"karma-jasmine-html-reporter\": \"^0.2.2\",\n\t\t\"protractor\": \"~5.4.0\",\n\t\t\"ts-node\": \"~7.0.0\",\n\t\t\"tslint\": \"~5.11.0\",\n\t\t\"typescript\": \"~3.2.2\",\n\t\t\"ts-loader\": \"^5.0.0\"\n\t},\n\t\"dependencies\": {\n\t\t\"@angular/animations\": \"~7.2.0\",\n\t\t\"@angular/common\": \"~7.2.0\",\n\t\t\"@angular/compiler\": \"~7.2.0\",\n\t\t\"@angular/core\": \"~7.2.0\",\n\t\t\"@angular/forms\": \"~7.2.0\",\n\t\t\"@angular/http\": \"~7.2.0\",\n\t\t\"@angular/platform-browser\": \"~7.2.0\",\n\t\t\"@angular/platform-browser-dynamic\": \"~7.2.0\",\n\t\t\"@angular/router\": \"~7.2.0\",\n\t\t\"angular-in-memory-web-api\": \"^0.8.0\",\n\t\t\"core-js\": \"^2.5.4\",\n\t\t\"rxjs\": \"~6.3.3\",\n\t\t\"tslib\": \"^1.9.0\",\n\t\t\"zone.js\": \"^0.8.26\"\n\t},\n\t\"scripts\": {\n\t\t\"build\": \"tsc && npm run copy-assets && liferay-npm-bundler\",\n\t\t\"copy-assets\": \"lnbs-copy-assets\",\n\t\t\"deploy\": \"npm run build && lnbs-deploy\",\n\t\t\"start\": \"lnbs-start\"\n\t},\n\t\"portlet\": {\n\t\t\"com.liferay.portlet.display-category\": \"category.sample\",\n\t\t\"com.liferay.portlet.header-portlet-css\": \"/css/styles.css\",\n\t\t\"com.liferay.portlet.instanceable\": true,\n\t\t\"javax.portlet.name\": \"my_angular_guestbook\",\n\t\t\"javax.portlet.security-role-ref\": \"power-user,user\",\n\t\t\"javax.portlet.display-name\": \"My Angular Guestbook\"\n\t},\n\t\"main\": \"index.js\"\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/add-entry/add-entry.component.spec.ts",
    "content": "import { async, ComponentFixture, TestBed } from '@angular/core/testing';\n\nimport { AddEntryComponent } from './add-entry.component';\n\ndescribe('AddEntryComponent', () => {\n  let component: AddEntryComponent;\n  let fixture: ComponentFixture<AddEntryComponent>;\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      declarations: [ AddEntryComponent ]\n    })\n    .compileComponents();\n  }));\n\n  beforeEach(() => {\n    fixture = TestBed.createComponent(AddEntryComponent);\n    component = fixture.componentInstance;\n    fixture.detectChanges();\n  });\n\n  it('should create', () => {\n    expect(component).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/add-entry/add-entry.component.ts",
    "content": "import { Component, OnInit } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { Location } from '@angular/common';\nimport { GuestbookEntry} from '../guestbook-entry';\nimport { EntryService } from '../entry.service';\nimport { Router } from '@angular/router';\n\n@Component({\n  //selector: 'app-add-entry',\n  templateUrl: '/o/my-angular-guestbook/app/add-entry.component.html'\n  //styleUrls: ['./add-entry.component.css']\n})\nexport class AddEntryComponent implements OnInit {\n\n  entries: GuestbookEntry[];\n\n  entriesLength: number;\n\n  constructor(\n    private entryService: EntryService,\n    private route: ActivatedRoute,\n    private location: Location,\n    private router: Router\n  ) { }\n\n  ngOnInit() {\n    this.getEntries();\n    this.getEntriesLength();\n  }\n\n  getEntries(): void {\n    this.entryService.getEntries()\n        .subscribe(entries => this.entries = entries);\n  }\n\n  getEntriesLength(): void {\n    this.entryService.getEntries()\n        .subscribe(entries => this.entriesLength = entries.length + 1);\n  }\n\n  goBack(): void {\n    //this.location.back(); //disabled for portal migration (didn't work)\n    this.router.navigateByUrl('/view-guestbook');\n  }\n   \n   add(name: string, message: string): void { \n     var id: number;\n     id = this.entriesLength;\n     name = name.trim();\n     message = message.trim();\n     if (!name || !message) { \n       alert(\"You must fill in all fields.\"); \n     }\n     else {\n       /*this.entryService.addEntry({ id, name, message } as GuestbookEntry)\n         .subscribe(entry => {\n         this.entries.push(entry);\n       });*/ //disabled for portal migration\n       this.goBack();\n       this.entries.push({id: id, name: name, message: message}); // enabled for portal migration\n     }\n   }\n\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/app-routing.module.ts",
    "content": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\nimport { AddEntryComponent }      from './add-entry/add-entry.component';\nimport { ViewGuestbookComponent }      from './view-guestbook/view-guestbook.component';\n\nconst routes: Routes = [\n  { path: '', redirectTo: '/view-guestbook', pathMatch: 'full' },\n  { path: 'add-entry', component: AddEntryComponent },\n  { path: 'view-guestbook', component: ViewGuestbookComponent }\n];\n\n@NgModule({\n  imports: [RouterModule.forRoot(routes,\n  {useHash: true, enableTracing: false}//added for portal migration\n  )],\n  exports: [RouterModule]\n})\nexport class AppRoutingModule { }\nexport const routingComponents = [ViewGuestbookComponent, AddEntryComponent] //added for portal migration"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/app.component.spec.ts",
    "content": "import { TestBed, async } from '@angular/core/testing';\nimport { RouterTestingModule } from '@angular/router/testing';\nimport { AppComponent } from './app.component';\n\ndescribe('AppComponent', () => {\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [\n        RouterTestingModule\n      ],\n      declarations: [\n        AppComponent\n      ],\n    }).compileComponents();\n  }));\n\n  it('should create the app', () => {\n    const fixture = TestBed.createComponent(AppComponent);\n    const app = fixture.debugElement.componentInstance;\n    expect(app).toBeTruthy();\n  });\n\n  it(`should have as title 'angular-guestbook'`, () => {\n    const fixture = TestBed.createComponent(AppComponent);\n    const app = fixture.debugElement.componentInstance;\n    expect(app.title).toEqual('angular-guestbook');\n  });\n\n  it('should render title in a h1 tag', () => {\n    const fixture = TestBed.createComponent(AppComponent);\n    fixture.detectChanges();\n    const compiled = fixture.debugElement.nativeElement;\n    expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular-guestbook!');\n  });\n});\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/app.component.ts",
    "content": "import { Component } from '@angular/core';\n//import {ViewGuestbookComponent} from './view-guestbook/view-guestbook.component'\n\n@Component({\n\ttemplateUrl: '/o/my-angular-guestbook/app/app.component.html'\n})\nexport class AppComponent {\n\t\n\ttitle = 'Guestbook';\n\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/app.module.ts",
    "content": "import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { APP_BASE_HREF } from '@angular/common'; // newly added for migration to portal\n\nimport { AppRoutingModule, routingComponents } from './app-routing.module';\nimport { AppComponent } from './app.component';\nimport { AddEntryComponent } from './add-entry/add-entry.component';\nimport { ViewGuestbookComponent } from './view-guestbook/view-guestbook.component';\nimport { FormsModule } from '@angular/forms'; // <-- NgModel lives here\n\n//import { HttpClientModule }    from '@angular/common/http'; //disabled for portal migration\n//import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; //disabled for portal migration\n//import { InMemoryDataService }  from './in-memory-data.service'; //disabled for portal migration\n\n@NgModule({\n  declarations: [\n    AppComponent,\n    routingComponents, //added for portal migration\n    AddEntryComponent,\n    ViewGuestbookComponent,\n  ],\n  imports: [\n    BrowserModule,\n    AppRoutingModule,\n    FormsModule\n    //HttpClientModule //disabled for portal migration\n\n// The HttpClientInMemoryWebApiModule module intercepts HTTP requests\n// and returns simulated server responses.\n// Remove it when a real server is ready to receive requests.\n/*HttpClientInMemoryWebApiModule.forRoot(\n  InMemoryDataService, { dataEncapsulation: false }\n)*/ //disabled for portal migration\n  ],\n  entryComponents: [AppComponent], //added for portal migration\n  providers: [{provide: APP_BASE_HREF, useValue: '/'}],//newly added for portal migration\n  bootstrap: []// removed AppComponent for portal migration\n})\nexport class AppModule {\n\t// Avoid bootstraping any component statically because we need to attach to\n\t// the portlet's DOM, which is different for each portlet instance and,\n\t// thus, cannot be determined until the page is rendered (during runtime).\n\tngDoBootstrap() {}\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/dynamic.loader.ts",
    "content": "import {\n\tApplicationRef,\n\tComponentFactoryResolver,\n\tInjector,\n\tNgZone,\n\tType,\n} from '@angular/core';\n\nimport LiferayParams from '../types/LiferayParams'\n\n/**\n * This class loads an Angular component dinamically so that we can attach it to\n * the portlet's DOM, which is different for each portlet instance and thus, \n * cannot be determined until the page is rendered (during runtime).\n */\nexport class DynamicLoader {\n\tconstructor(private injector: Injector) {}\n\n\tloadComponent<T>(component: Type<T>, params: LiferayParams) {\n\t\tconst node = document.getElementById(params.portletElementId);\n\t\t\n\t\t(<NgZone>this.injector.get(NgZone)).run(() => {\n\t\t\tconst componentFactory = this.injector\n\t\t\t\t.get(ComponentFactoryResolver)\n\t\t\t\t.resolveComponentFactory(component);\n\t\t\tconst componentRef = componentFactory.create(\n\t\t\t\tthis.injector,\n\t\t\t\t[],\n\t\t\t\tnode,\n\t\t\t);\n\t\t\tcomponentRef.instance.params = params;\n\t\t\tthis.injector.get(ApplicationRef).attachView(componentRef.hostView);\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/entry.service.spec.ts",
    "content": "import { TestBed } from '@angular/core/testing';\n\nimport { EntryService } from './entry.service';\n\ndescribe('EntryService', () => {\n  beforeEach(() => TestBed.configureTestingModule({}));\n\n  it('should be created', () => {\n    const service: EntryService = TestBed.get(EntryService);\n    expect(service).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/entry.service.ts",
    "content": "import { Injectable } from '@angular/core';\nimport { GuestbookEntry } from './guestbook-entry';\nimport { ENTRIES } from './mock-guestbook-entries';\nimport { Observable, of } from 'rxjs';\n//import { HttpClient, HttpHeaders } from '@angular/common/http'; //removed for portal migration\n//import { catchError, map, tap } from 'rxjs/operators'; disabled for portal migration\n\n/*const httpOptions = {\n  headers: new HttpHeaders({ 'Content-Type': 'application/json' })\n};*/ //disabled for portal migration\n\n@Injectable({\n  providedIn: 'root'\n})\nexport class EntryService {\n\n//private entriesUrl = 'api/entries';  // URL to web api // disabled for portal migration\n\n  constructor(\n  //private http: HttpClient\n  ) { }\n  \n  /** GET heroes from the server */\n  getEntries (): Observable<GuestbookEntry[]> {\n    this.log(`retrieved guestbook entries...`);\n    return of(ENTRIES);\n    /*return this.http.get<GuestbookEntry[]>(this.entriesUrl)\n    .pipe(\n      tap(_ => this.log('fetched entries')),\n      catchError(this.handleError('getEntries', []))\n    );*/ //disabled for portal migration\n  }\n\n  /** POST: add a new hero to the server */\n  addEntry (entry: GuestbookEntry): void { //removed return type Observable<GuestbookEntry> for portal migration\n    //console.log(`added entry w/ id=${entry.id}, name=${entry.name} and message=${entry.message}`);\n    this.log(`added entry w/ id=${entry.id}, name=${entry.name} and message=${entry.message}`);\n    /*return this.http.post<GuestbookEntry>(this.entriesUrl, entry, httpOptions).pipe(\n      tap((entry: GuestbookEntry) => this.log(`added entry w/ id=${entry.id}, name=${entry.name} and message=${entry.message}`)),\n      catchError(this.handleError<GuestbookEntry>('addEntry'))\n    )*/ //disabled for portal migration\n  }\n\n  /** Log a HeroService message with the MessageService */\n  private log(message: string) {\n    //this.messageService.add(`HeroService: ${message}`);\n    console.log(`EntryService: ${message}`);\n  }\n\n  /**\n   * Handle Http operation that failed.\n   * Let the app continue.\n   * @param operation - name of the operation that failed\n   * @param result - optional value to return as the observable result\n   *//*\n  private handleError<T> (operation = 'operation', result?: T) {\n    return (error: any): Observable<T> => {\n  \n      // TODO: send the error to remote logging infrastructure\n      console.error(error); // log to console instead\n  \n      // TODO: better job of transforming error for user consumption\n      this.log(`${operation} failed: ${error.message}`);\n  \n      // Let the app keep running by returning an empty result.\n      return of(result as T);\n    };\n  }*/ // removed for portal migration\n\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/guestbook-entry.ts",
    "content": "export class GuestbookEntry {\n  id: number;\n  name: string;\n  message: string;\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/in-memory-data.service.spec.ts",
    "content": "import { TestBed } from '@angular/core/testing';\n\nimport { InMemoryDataService } from './in-memory-data.service';\n\ndescribe('InMemoryDataService', () => {\n  beforeEach(() => TestBed.configureTestingModule({}));\n\n  it('should be created', () => {\n    const service: InMemoryDataService = TestBed.get(InMemoryDataService);\n    expect(service).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/in-memory-data.service.ts",
    "content": "import { InMemoryDbService } from 'angular-in-memory-web-api';\nimport { GuestbookEntry } from './guestbook-entry';\nimport { Injectable } from '@angular/core';\n\n@Injectable({\n  providedIn: 'root',\n})\nexport class InMemoryDataService implements InMemoryDbService {\n  createDb() {\n    const entries = [\n      { id: 1, name: 'Joe Bloggs', message: 'Had an awesome Time!' },\n      { id: 2, name: 'Jane Bloggs', message: 'Great event!' },\n      { id: 3, name: 'Bill Bloggs', message: 'Had a good time.' },\n      { id: 4, name: 'Bob Nosester', message: 'Great atmosphere!' },\n      { id: 5, name: 'Martha Nosester', message: 'Lovely aromas.' }\n    ];\n    return {entries};\n  }\n\n  // Overrides the genId method to ensure that a hero always has an id.\n  // If the heroes array is empty,\n  // the method below returns the initial number (11).\n  // if the heroes array is not empty, the method below returns the highest\n  // hero id + 1.\n  genId(entries: GuestbookEntry[]): number {\n    return entries.length > 0 ? Math.max(...entries.map(entry => entry.id)) + 1 : 1;\n  }\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/mock-guestbook-entries.ts",
    "content": "import { GuestbookEntry } from './guestbook-entry';\n\nexport const ENTRIES: GuestbookEntry[] = [\n  { id: 1, name: 'Joe Bloggs', message: 'Had an awesome Time!' },\n  { id: 2, name: 'Jane Bloggs', message: 'Great event!' },\n  { id: 3, name: 'Bill Bloggs', message: 'Had a good time.' },\n  { id: 4, name: 'Bob Nosester', message: 'Great atmosphere!' },\n  { id: 5, name: 'Martha Nosester', message: 'Lovely aromas.' }\n];"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/view-guestbook/view-guestbook.component.spec.ts",
    "content": "import { async, ComponentFixture, TestBed } from '@angular/core/testing';\n\nimport { ViewGuestbookComponent } from './view-guestbook.component';\n\ndescribe('ViewGuestbookComponent', () => {\n  let component: ViewGuestbookComponent;\n  let fixture: ComponentFixture<ViewGuestbookComponent>;//changed type from viewComponent to ViewGuestbookComponent for portal migration\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      declarations: [ ViewGuestbookComponent ]\n    })\n    .compileComponents();\n  }));\n\n  beforeEach(() => {\n    fixture = TestBed.createComponent(ViewGuestbookComponent);\n    component = fixture.componentInstance;\n    fixture.detectChanges();\n  });\n\n  it('should create', () => {\n    expect(component).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/app/view-guestbook/view-guestbook.component.ts",
    "content": "import { Component, OnInit } from '@angular/core';\nimport { GuestbookEntry} from '../guestbook-entry';\nimport { EntryService } from '../entry.service';\n\n@Component({\n  selector: 'app-view-guestbook',\n  templateUrl: '/o/my-angular-guestbook/app/view-guestbook.component.html'\n  //styleUrls: ['/o/my-angular-guestbook/app//view-guestbook/view-guestbook.component.css']\n})\nexport class ViewGuestbookComponent implements OnInit {\n\n  entries: GuestbookEntry[];\n\n  constructor(private entryService: EntryService) { }\n\n  ngOnInit() {\n    this.getEntries();\n  }\n\n  getEntries(): void {\n    this.entryService.getEntries()\n        .subscribe(entries => this.entries = entries);\n  }\n\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/index.ts",
    "content": "import './polyfills';\n\n\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\n\nimport { AppComponent } from './app/app.component';\nimport { AppModule } from './app/app.module';\nimport { DynamicLoader } from './app/dynamic.loader';\n\nimport LiferayParams from './types/LiferayParams'\n\n/**\n * This is the actual method that initializes the portlet. It is invoked by the \n * \"bootstrap\" module.\n * \n * @param  {LiferayParams} params an object with values of interest to the \n * \t\t\t\t\t\t\t\t\tportlet\n */\nexport default function(params: LiferayParams) {\n\t\n\tplatformBrowserDynamic()\n\t\t.bootstrapModule(AppModule)\n\t\t.then((injector: any) => {\n\t\t\t// Load the bootstrap component dinamically so that we can attach it\n\t\t\t// to the portlet's DOM, which is different for each portlet\n\t\t\t// instance and, thus, cannot be determined until the page is\n\t\t\t// rendered (during runtime).\n\t\t\tconst dynamicLoader = new DynamicLoader(injector);\n\n\t\t\tdynamicLoader.loadComponent(AppComponent, params);\n\t\t});\n\t\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/polyfills.ts",
    "content": "// Import needed polyfills for the application\nimport 'core-js/es7/reflect';\nimport 'zone.js/dist/zone';\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/src/types/LiferayParams.ts",
    "content": "/**\n * This is the structure of the parameters passed by Liferay to the JS module.\n *\n * See https://tinyurl.com/js-ext-portlet-entry-point for the most recent \n * information on the structure of this type.\n */\nexport default interface LiferayParams {\n\tportletElementId: string;\n\tcontextPath: string;\n\tportletNamespace: string;\n\tconfiguration: any;\n}\n\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/after/my-angular-guestbook/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"emitDecoratorMetadata\": true,\n\t\t\"experimentalDecorators\": true,\n\t\t\"inlineSources\": true,\n\t\t\"lib\": [\"es2015\", \"dom\"],\n\t\t\"module\": \"commonjs\",\n\t\t\"moduleResolution\": \"node\",\n\t\t\"noImplicitAny\": true,\n\t\t\"noStrictGenericChecks\": true,\n\t\t\"outDir\": \"build\",\n\t\t\"sourceMap\": true,\n\t\t\"suppressImplicitAnyIndexErrors\": true,\n\t\t\"target\": \"es5\",\n\t\t\"typeRoots\": [\"./node_modules/@types/\"]\n\t},\n\t\"include\": [\"./src/**/*.ts\"]\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/.editorconfig",
    "content": "# Editor configuration, see https://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.md]\nmax_line_length = off\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/.gitignore",
    "content": "# See http://help.github.com/ignore-files/ for more about ignoring files.\n\n# compiled output\n/dist\n/tmp\n/out-tsc\n\n# dependencies\n/node_modules\n\n# profiling files\nchrome-profiler-events.json\nspeed-measure-plugin.json\n\n# IDEs and editors\n/.idea\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# IDE - VSCode\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n.history/*\n\n# misc\n/.sass-cache\n/connect.lock\n/coverage\n/libpeerconnection.log\nnpm-debug.log\nyarn-error.log\ntestem.log\n/typings\n\n# System Files\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/README.md",
    "content": "# AngularGuestbook\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.2.3.\n\n## Development server\n\nRun `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.\n\n## Code scaffolding\n\nRun `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.\n\n## Build\n\nRun `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/angular.json",
    "content": "{\n  \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n  \"version\": 1,\n  \"newProjectRoot\": \"projects\",\n  \"projects\": {\n    \"angular-guestbook\": {\n      \"root\": \"\",\n      \"sourceRoot\": \"src\",\n      \"projectType\": \"application\",\n      \"prefix\": \"app\",\n      \"schematics\": {},\n      \"architect\": {\n        \"build\": {\n          \"builder\": \"@angular-devkit/build-angular:browser\",\n          \"options\": {\n            \"outputPath\": \"dist/angular-guestbook\",\n            \"index\": \"src/index.html\",\n            \"main\": \"src/main.ts\",\n            \"polyfills\": \"src/polyfills.ts\",\n            \"tsConfig\": \"src/tsconfig.app.json\",\n            \"assets\": [\n              \"src/favicon.ico\",\n              \"src/assets\"\n            ],\n            \"styles\": [\n              \"src/styles.css\"\n            ],\n            \"scripts\": []\n          },\n          \"configurations\": {\n            \"production\": {\n              \"fileReplacements\": [\n                {\n                  \"replace\": \"src/environments/environment.ts\",\n                  \"with\": \"src/environments/environment.prod.ts\"\n                }\n              ],\n              \"optimization\": true,\n              \"outputHashing\": \"all\",\n              \"sourceMap\": false,\n              \"extractCss\": true,\n              \"namedChunks\": false,\n              \"aot\": true,\n              \"extractLicenses\": true,\n              \"vendorChunk\": false,\n              \"buildOptimizer\": true,\n              \"budgets\": [\n                {\n                  \"type\": \"initial\",\n                  \"maximumWarning\": \"2mb\",\n                  \"maximumError\": \"5mb\"\n                }\n              ]\n            }\n          }\n        },\n        \"serve\": {\n          \"builder\": \"@angular-devkit/build-angular:dev-server\",\n          \"options\": {\n            \"browserTarget\": \"angular-guestbook:build\"\n          },\n          \"configurations\": {\n            \"production\": {\n              \"browserTarget\": \"angular-guestbook:build:production\"\n            }\n          }\n        },\n        \"extract-i18n\": {\n          \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n          \"options\": {\n            \"browserTarget\": \"angular-guestbook:build\"\n          }\n        },\n        \"test\": {\n          \"builder\": \"@angular-devkit/build-angular:karma\",\n          \"options\": {\n            \"main\": \"src/test.ts\",\n            \"polyfills\": \"src/polyfills.ts\",\n            \"tsConfig\": \"src/tsconfig.spec.json\",\n            \"karmaConfig\": \"src/karma.conf.js\",\n            \"styles\": [\n              \"src/styles.css\"\n            ],\n            \"scripts\": [],\n            \"assets\": [\n              \"src/favicon.ico\",\n              \"src/assets\"\n            ]\n          }\n        },\n        \"lint\": {\n          \"builder\": \"@angular-devkit/build-angular:tslint\",\n          \"options\": {\n            \"tsConfig\": [\n              \"src/tsconfig.app.json\",\n              \"src/tsconfig.spec.json\"\n            ],\n            \"exclude\": [\n              \"**/node_modules/**\"\n            ]\n          }\n        }\n      }\n    },\n    \"angular-guestbook-e2e\": {\n      \"root\": \"e2e/\",\n      \"projectType\": \"application\",\n      \"prefix\": \"\",\n      \"architect\": {\n        \"e2e\": {\n          \"builder\": \"@angular-devkit/build-angular:protractor\",\n          \"options\": {\n            \"protractorConfig\": \"e2e/protractor.conf.js\",\n            \"devServerTarget\": \"angular-guestbook:serve\"\n          },\n          \"configurations\": {\n            \"production\": {\n              \"devServerTarget\": \"angular-guestbook:serve:production\"\n            }\n          }\n        },\n        \"lint\": {\n          \"builder\": \"@angular-devkit/build-angular:tslint\",\n          \"options\": {\n            \"tsConfig\": \"e2e/tsconfig.e2e.json\",\n            \"exclude\": [\n              \"**/node_modules/**\"\n            ]\n          }\n        }\n      }\n    }\n  },\n  \"defaultProject\": \"angular-guestbook\"\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/e2e/protractor.conf.js",
    "content": "// Protractor configuration file, see link for more information\n// https://github.com/angular/protractor/blob/master/lib/config.ts\n\nconst { SpecReporter } = require('jasmine-spec-reporter');\n\nexports.config = {\n  allScriptsTimeout: 11000,\n  specs: [\n    './src/**/*.e2e-spec.ts'\n  ],\n  capabilities: {\n    'browserName': 'chrome'\n  },\n  directConnect: true,\n  baseUrl: 'http://localhost:4200/',\n  framework: 'jasmine',\n  jasmineNodeOpts: {\n    showColors: true,\n    defaultTimeoutInterval: 30000,\n    print: function() {}\n  },\n  onPrepare() {\n    require('ts-node').register({\n      project: require('path').join(__dirname, './tsconfig.e2e.json')\n    });\n    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));\n  }\n};"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/e2e/src/app.e2e-spec.ts",
    "content": "import { AppPage } from './app.po';\nimport { browser, logging } from 'protractor';\n\ndescribe('workspace-project App', () => {\n  let page: AppPage;\n\n  beforeEach(() => {\n    page = new AppPage();\n  });\n\n  it('should display welcome message', () => {\n    page.navigateTo();\n    expect(page.getTitleText()).toEqual('Welcome to angular-guestbook!');\n  });\n\n  afterEach(async () => {\n    // Assert that there are no errors emitted from the browser\n    const logs = await browser.manage().logs().get(logging.Type.BROWSER);\n    expect(logs).not.toContain(jasmine.objectContaining({\n      level: logging.Level.SEVERE,\n    }));\n  });\n});\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/e2e/src/app.po.ts",
    "content": "import { browser, by, element } from 'protractor';\n\nexport class AppPage {\n  navigateTo() {\n    return browser.get('/') as Promise<any>;\n  }\n\n  getTitleText() {\n    return element(by.css('app-root h1')).getText() as Promise<string>;\n  }\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/e2e/tsconfig.e2e.json",
    "content": "{\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"../out-tsc/app\",\n    \"module\": \"commonjs\",\n    \"target\": \"es5\",\n    \"types\": [\n      \"jasmine\",\n      \"jasminewd2\",\n      \"node\"\n    ]\n  }\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/package.json",
    "content": "{\n  \"name\": \"angular-guestbook\",\n  \"version\": \"0.0.0\",\n  \"scripts\": {\n    \"ng\": \"ng\",\n    \"start\": \"ng serve\",\n    \"build\": \"ng build\",\n    \"test\": \"ng test\",\n    \"lint\": \"ng lint\",\n    \"e2e\": \"ng e2e\"\n  },\n  \"private\": true,\n  \"dependencies\": {\n    \"@angular/animations\": \"~7.2.0\",\n    \"@angular/common\": \"~7.2.0\",\n    \"@angular/compiler\": \"~7.2.0\",\n    \"@angular/core\": \"~7.2.0\",\n    \"@angular/forms\": \"~7.2.0\",\n    \"@angular/platform-browser\": \"~7.2.0\",\n    \"@angular/platform-browser-dynamic\": \"~7.2.0\",\n    \"@angular/router\": \"~7.2.0\",\n    \"angular-in-memory-web-api\": \"^0.8.0\",\n    \"core-js\": \"^2.5.4\",\n    \"rxjs\": \"~6.3.3\",\n    \"tslib\": \"^1.9.0\",\n    \"zone.js\": \"~0.8.26\"\n  },\n  \"devDependencies\": {\n    \"@angular-devkit/build-angular\": \"~0.12.0\",\n    \"@angular/cli\": \"~7.2.3\",\n    \"@angular/compiler-cli\": \"~7.2.0\",\n    \"@angular/language-service\": \"~7.2.0\",\n    \"@types/node\": \"~8.9.4\",\n    \"@types/jasmine\": \"~2.8.8\",\n    \"@types/jasminewd2\": \"~2.0.3\",\n    \"codelyzer\": \"~4.5.0\",\n    \"jasmine-core\": \"~2.99.1\",\n    \"jasmine-spec-reporter\": \"~4.2.1\",\n    \"karma\": \"~3.1.1\",\n    \"karma-chrome-launcher\": \"~2.2.0\",\n    \"karma-coverage-istanbul-reporter\": \"~2.0.1\",\n    \"karma-jasmine\": \"~1.1.2\",\n    \"karma-jasmine-html-reporter\": \"^0.2.2\",\n    \"protractor\": \"~5.4.0\",\n    \"ts-node\": \"~7.0.0\",\n    \"tslint\": \"~5.11.0\",\n    \"typescript\": \"~3.2.2\"\n  }\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/add-entry/add-entry.component.css",
    "content": ".button-footer {\n  display: flex;\n  margin-top: 1.5rem;\n}\n\n.button-footer > button {\n  margin-right: 1rem;\n  cursor: pointer;\n  display: inline-block;\n  border-radius: 0.25rem;\n}\n\ninput {\n  display: block\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/add-entry/add-entry.component.html",
    "content": "<form>\n <fieldset>\n   <label>Name\n     <input #name />\n   </label><br/>\n   <label>Message\n     <input #message />\n   </label>\n </fieldset>\n \n <div class=\"button-footer\">\n   <button class=\"main-btn\" (click)=\"add(name.value, message.value)\">Add</button>\n   <button class=\"plain-btn\" (click)=\"goBack()\">Cancel</button>\n </div>\n</form>"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/add-entry/add-entry.component.spec.ts",
    "content": "import { async, ComponentFixture, TestBed } from '@angular/core/testing';\n\nimport { AddEntryComponent } from './add-entry.component';\n\ndescribe('AddEntryComponent', () => {\n  let component: AddEntryComponent;\n  let fixture: ComponentFixture<AddEntryComponent>;\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      declarations: [ AddEntryComponent ]\n    })\n    .compileComponents();\n  }));\n\n  beforeEach(() => {\n    fixture = TestBed.createComponent(AddEntryComponent);\n    component = fixture.componentInstance;\n    fixture.detectChanges();\n  });\n\n  it('should create', () => {\n    expect(component).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/add-entry/add-entry.component.ts",
    "content": "import { Component, OnInit } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { Location } from '@angular/common';\nimport { GuestbookEntry} from '../guestbook-entry';\nimport { EntryService } from '../entry.service';\n\n@Component({\n  selector: 'app-add-entry',\n  templateUrl: './add-entry.component.html',\n  styleUrls: ['./add-entry.component.css']\n})\nexport class AddEntryComponent implements OnInit {\n\n  entries: GuestbookEntry[];\n\n  entriesLength: number;\n\n  constructor(\n    private entryService: EntryService,\n    private route: ActivatedRoute,\n    private location: Location\n  ) { }\n\n  ngOnInit() {\n    this.getEntries();\n    this.getEntriesLength();\n  }\n\n  getEntries(): void {\n    this.entryService.getEntries()\n        .subscribe(entries => this.entries = entries);\n  }\n\n  getEntriesLength(): void {\n    this.entryService.getEntries()\n        .subscribe(entries => this.entriesLength = entries.length + 1);\n  }\n\n  goBack(): void {\n    this.location.back();\n  }\n   \n   add(name: string, message: string): void { \n     var id: number;\n     id = this.entriesLength;\n     name = name.trim();\n     message = message.trim();\n     if (!name || !message) { \n       alert(\"You must fill in all fields.\"); \n     }\n     else {\n       this.entryService.addEntry({ id, name, message } as GuestbookEntry)\n       .subscribe(entry => {\n         this.entries.push(entry);\n       });\n       //this.entries.push({id: id, name: name, message: message});\n       this.goBack();\n     }\n   }\n\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/app-routing.module.ts",
    "content": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\nimport { AddEntryComponent }      from './add-entry/add-entry.component';\nimport { ViewGuestbookComponent }      from './view-guestbook/view-guestbook.component';\n\nconst routes: Routes = [\n  { path: '', redirectTo: '/view-guestbook', pathMatch: 'full' },\n  { path: 'add-entry', component: AddEntryComponent },\n  { path: 'view-guestbook', component: ViewGuestbookComponent }\n];\n\n@NgModule({\n  imports: [RouterModule.forRoot(routes)],\n  exports: [RouterModule]\n})\nexport class AppRoutingModule { }\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/app.component.css",
    "content": "nav > a {\n  color: #333;\n  background-color: #fff;\n  border-color: #ccc;\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/app.component.html",
    "content": "<h1>{{title}}</h1>\n\n<router-outlet></router-outlet>\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/app.component.spec.ts",
    "content": "import { TestBed, async } from '@angular/core/testing';\nimport { RouterTestingModule } from '@angular/router/testing';\nimport { AppComponent } from './app.component';\n\ndescribe('AppComponent', () => {\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [\n        RouterTestingModule\n      ],\n      declarations: [\n        AppComponent\n      ],\n    }).compileComponents();\n  }));\n\n  it('should create the app', () => {\n    const fixture = TestBed.createComponent(AppComponent);\n    const app = fixture.debugElement.componentInstance;\n    expect(app).toBeTruthy();\n  });\n\n  it(`should have as title 'angular-guestbook'`, () => {\n    const fixture = TestBed.createComponent(AppComponent);\n    const app = fixture.debugElement.componentInstance;\n    expect(app.title).toEqual('angular-guestbook');\n  });\n\n  it('should render title in a h1 tag', () => {\n    const fixture = TestBed.createComponent(AppComponent);\n    fixture.detectChanges();\n    const compiled = fixture.debugElement.nativeElement;\n    expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular-guestbook!');\n  });\n});\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/app.component.ts",
    "content": "import { Component } from '@angular/core';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html',\n  styleUrls: ['./app.component.css']\n})\nexport class AppComponent {\n  title = 'Guestbook';\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/app.module.ts",
    "content": "import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { AppRoutingModule } from './app-routing.module';\nimport { AppComponent } from './app.component';\nimport { AddEntryComponent } from './add-entry/add-entry.component';\nimport { ViewGuestbookComponent } from './view-guestbook/view-guestbook.component';\nimport { FormsModule } from '@angular/forms'; // <-- NgModel lives here\n\nimport { HttpClientModule }    from '@angular/common/http';\nimport { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';\nimport { InMemoryDataService }  from './in-memory-data.service';\n\n@NgModule({\n  declarations: [\n    AppComponent,\n    AddEntryComponent,\n    ViewGuestbookComponent,\n  ],\n  imports: [\n    BrowserModule,\n    AppRoutingModule,\n    FormsModule,\n    HttpClientModule,\n\n// The HttpClientInMemoryWebApiModule module intercepts HTTP requests\n// and returns simulated server responses.\n// Remove it when a real server is ready to receive requests.\nHttpClientInMemoryWebApiModule.forRoot(\n  InMemoryDataService, { dataEncapsulation: false }\n)\n  ],\n  providers: [],\n  bootstrap: [AppComponent]\n})\nexport class AppModule { }\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/entry.service.spec.ts",
    "content": "import { TestBed } from '@angular/core/testing';\n\nimport { EntryService } from './entry.service';\n\ndescribe('EntryService', () => {\n  beforeEach(() => TestBed.configureTestingModule({}));\n\n  it('should be created', () => {\n    const service: EntryService = TestBed.get(EntryService);\n    expect(service).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/entry.service.ts",
    "content": "import { Injectable } from '@angular/core';\nimport { GuestbookEntry } from './guestbook-entry';\n//import { ENTRIES } from './mock-guestbook-entries';\nimport { Observable, of } from 'rxjs';\nimport { HttpClient, HttpHeaders } from '@angular/common/http';\nimport { catchError, map, tap } from 'rxjs/operators';\n\nconst httpOptions = {\n  headers: new HttpHeaders({ 'Content-Type': 'application/json' })\n};\n\n@Injectable({\n  providedIn: 'root'\n})\nexport class EntryService {\n\nprivate entriesUrl = 'api/entries';  // URL to web api\n\n  constructor(\n  private http: HttpClient) { }\n  \n  /** GET heroes from the server */\n  getEntries (): Observable<GuestbookEntry[]> {\n    return this.http.get<GuestbookEntry[]>(this.entriesUrl)\n    .pipe(\n      tap(_ => this.log('fetched entries')),\n      catchError(this.handleError('getEntries', []))\n    );\n  }\n\n  /** POST: add a new hero to the server */\n  addEntry (entry: GuestbookEntry): Observable<GuestbookEntry> {\n    //console.log(`added entry w/ id=${entry.id}, name=${entry.name} and message=${entry.message}`);\n    return this.http.post<GuestbookEntry>(this.entriesUrl, entry, httpOptions).pipe(\n      tap((entry: GuestbookEntry) => this.log(`added entry w/ id=${entry.id}, name=${entry.name} and message=${entry.message}`)),\n      catchError(this.handleError<GuestbookEntry>('addEntry'))\n    )\n  }\n\n  /** Log a HeroService message with the MessageService */\n  private log(message: string) {\n    //this.messageService.add(`HeroService: ${message}`);\n    console.log(`EntryService: ${message}`);\n  }\n\n  /**\n   * Handle Http operation that failed.\n   * Let the app continue.\n   * @param operation - name of the operation that failed\n   * @param result - optional value to return as the observable result\n   */\n  private handleError<T> (operation = 'operation', result?: T) {\n    return (error: any): Observable<T> => {\n  \n      // TODO: send the error to remote logging infrastructure\n      console.error(error); // log to console instead\n  \n      // TODO: better job of transforming error for user consumption\n      this.log(`${operation} failed: ${error.message}`);\n  \n      // Let the app keep running by returning an empty result.\n      return of(result as T);\n    };\n  }\n\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/guestbook-entry.ts",
    "content": "export class GuestbookEntry {\n  id: number;\n  name: string;\n  message: string;\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/in-memory-data.service.spec.ts",
    "content": "import { TestBed } from '@angular/core/testing';\n\nimport { InMemoryDataService } from './in-memory-data.service';\n\ndescribe('InMemoryDataService', () => {\n  beforeEach(() => TestBed.configureTestingModule({}));\n\n  it('should be created', () => {\n    const service: InMemoryDataService = TestBed.get(InMemoryDataService);\n    expect(service).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/in-memory-data.service.ts",
    "content": "import { InMemoryDbService } from 'angular-in-memory-web-api';\nimport { GuestbookEntry } from './guestbook-entry';\nimport { Injectable } from '@angular/core';\n\n@Injectable({\n  providedIn: 'root',\n})\nexport class InMemoryDataService implements InMemoryDbService {\n  createDb() {\n    const entries = [\n      { id: 1, name: 'Joe Bloggs', message: 'Had an awesome Time!' },\n      { id: 2, name: 'Jane Bloggs', message: 'Great event!' },\n      { id: 3, name: 'Bill Bloggs', message: 'Had a good time.' },\n      { id: 4, name: 'Bob Nosester', message: 'Great atmosphere!' },\n      { id: 5, name: 'Martha Nosester', message: 'Lovely aromas.' }\n    ];\n    return {entries};\n  }\n\n  // Overrides the genId method to ensure that a hero always has an id.\n  // If the heroes array is empty,\n  // the method below returns the initial number (11).\n  // if the heroes array is not empty, the method below returns the highest\n  // hero id + 1.\n  genId(entries: GuestbookEntry[]): number {\n    return entries.length > 0 ? Math.max(...entries.map(entry => entry.id)) + 1 : 1;\n  }\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/mock-guestbook-entries.ts",
    "content": "import { GuestbookEntry } from './guestbook-entry';\n\nexport const ENTRIES: GuestbookEntry[] = [\n  { id: 1, name: 'Joe Bloggs', message: 'Had an awesome Time!' },\n  { id: 2, name: 'Jane Bloggs', message: 'Great event!' },\n  { id: 3, name: 'Bill Bloggs', message: 'Had a good time.' },\n  { id: 4, name: 'Bob Nosester', message: 'Great atmosphere!' },\n  { id: 5, name: 'Martha Nosester', message: 'Lovely aromas.' }\n];"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/view-guestbook/view-guestbook.component.css",
    "content": "table {\n  font-family: arial, sans-serif;\n  border-collapse: collapse;\n  width: 100%;\n}\n\ntd, th {\n  border: 1px solid #000;\n  text-align: left;\n  padding: 8px;\n}\n\ntr:nth-child(even) {\n  background-color: #dddddd;\n}\n\n.button-footer {\n  display: flex;\n  margin-top: 1.5rem;\n}\n\n.button-footer > button {\n  margin-right: 1rem;\n  cursor: pointer;\n  display: inline-block;\n  border-radius: 0.25rem;\n}\n\ninput {\n  display: block\n}"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/view-guestbook/view-guestbook.component.html",
    "content": "<table class=\"entries\">\n  <tr>\n    <th>Name</th>\n    <th>Message</th>\n  </tr>\n  <tr *ngFor=\"let entry of entries\" (click)=\"onSelect(entry)\">\n    <td>{{entry.name}}</td>\n    <td>{{entry.message}}</td>\n  </tr>\n</table>\n\n<div class=\"button-footer\">\n  <button routerLink=\"/add-entry\">Add Entry</button>\n</div>"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/view-guestbook/view-guestbook.component.spec.ts",
    "content": "import { async, ComponentFixture, TestBed } from '@angular/core/testing';\n\nimport { ViewGuestbookComponent } from './view-guestbook.component';\n\ndescribe('ViewGuestbookComponent', () => {\n  let component: ViewGuestbookComponent;\n  let fixture: ComponentFixture<ViewComponent>;\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      declarations: [ ViewGuestbookComponent ]\n    })\n    .compileComponents();\n  }));\n\n  beforeEach(() => {\n    fixture = TestBed.createComponent(ViewGuestbookComponent);\n    component = fixture.componentInstance;\n    fixture.detectChanges();\n  });\n\n  it('should create', () => {\n    expect(component).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/app/view-guestbook/view-guestbook.component.ts",
    "content": "import { Component, OnInit } from '@angular/core';\nimport { GuestbookEntry} from '../guestbook-entry';\nimport { EntryService } from '../entry.service';\n\n@Component({\n  selector: 'app-view-guestbook',\n  templateUrl: './view-guestbook.component.html',\n  styleUrls: ['./view-guestbook.component.css']\n})\nexport class ViewGuestbookComponent implements OnInit {\n\n  selectedEntry: GuestbookEntry;\n  onSelect(entry: GuestbookEntry): void {\n    this.selectedEntry = entry;\n  }\n\n  entries: GuestbookEntry[];\n\n  constructor(private entryService: EntryService) { }\n\n  ngOnInit() {\n    this.getEntries();\n  }\n\n  getEntries(): void {\n    this.entryService.getEntries()\n        .subscribe(entries => this.entries = entries);\n  }\n\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/assets/.gitkeep",
    "content": ""
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/browserslist",
    "content": "# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers\n# For additional information regarding the format and rule options, please see:\n# https://github.com/browserslist/browserslist#queries\n#\n# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed\n\n> 0.5%\nlast 2 versions\nFirefox ESR\nnot dead\nnot IE 9-11"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/environments/environment.prod.ts",
    "content": "export const environment = {\n  production: true\n};\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/environments/environment.ts",
    "content": "// This file can be replaced during build by using the `fileReplacements` array.\n// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.\n// The list of file replacements can be found in `angular.json`.\n\nexport const environment = {\n  production: false\n};\n\n/*\n * For easier debugging in development mode, you can import the following file\n * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.\n *\n * This import should be commented out in production mode because it will have a negative impact\n * on performance if an error is thrown.\n */\n// import 'zone.js/dist/zone-error';  // Included with Angular CLI.\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>AngularGuestbook</title>\n  <base href=\"/\">\n\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\">\n</head>\n<body>\n  <app-root></app-root>\n</body>\n</html>\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/karma.conf.js",
    "content": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-file.html\n\nmodule.exports = function (config) {\n  config.set({\n    basePath: '',\n    frameworks: ['jasmine', '@angular-devkit/build-angular'],\n    plugins: [\n      require('karma-jasmine'),\n      require('karma-chrome-launcher'),\n      require('karma-jasmine-html-reporter'),\n      require('karma-coverage-istanbul-reporter'),\n      require('@angular-devkit/build-angular/plugins/karma')\n    ],\n    client: {\n      clearContext: false // leave Jasmine Spec Runner output visible in browser\n    },\n    coverageIstanbulReporter: {\n      dir: require('path').join(__dirname, '../coverage'),\n      reports: ['html', 'lcovonly', 'text-summary'],\n      fixWebpackSourcePaths: true\n    },\n    reporters: ['progress', 'kjhtml'],\n    port: 9876,\n    colors: true,\n    logLevel: config.LOG_INFO,\n    autoWatch: true,\n    browsers: ['Chrome'],\n    singleRun: false\n  });\n};"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/main.ts",
    "content": "import { enableProdMode } from '@angular/core';\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\n\nimport { AppModule } from './app/app.module';\nimport { environment } from './environments/environment';\n\nif (environment.production) {\n  enableProdMode();\n}\n\nplatformBrowserDynamic().bootstrapModule(AppModule)\n  .catch(err => console.error(err));\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/polyfills.ts",
    "content": "/**\n * This file includes polyfills needed by Angular and is loaded before the app.\n * You can add your own extra polyfills to this file.\n *\n * This file is divided into 2 sections:\n *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.\n *   2. Application imports. Files imported after ZoneJS that should be loaded before your main\n *      file.\n *\n * The current setup is for so-called \"evergreen\" browsers; the last versions of browsers that\n * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),\n * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.\n *\n * Learn more in https://angular.io/guide/browser-support\n */\n\n/***************************************************************************************************\n * BROWSER POLYFILLS\n */\n\n/** IE9, IE10, IE11, and Chrome <55 requires all of the following polyfills.\n *  This also includes Android Emulators with older versions of Chrome and Google Search/Googlebot\n */\n\n// import 'core-js/es6/symbol';\n// import 'core-js/es6/object';\n// import 'core-js/es6/function';\n// import 'core-js/es6/parse-int';\n// import 'core-js/es6/parse-float';\n// import 'core-js/es6/number';\n// import 'core-js/es6/math';\n// import 'core-js/es6/string';\n// import 'core-js/es6/date';\n// import 'core-js/es6/array';\n// import 'core-js/es6/regexp';\n// import 'core-js/es6/map';\n// import 'core-js/es6/weak-map';\n// import 'core-js/es6/set';\n\n/** IE10 and IE11 requires the following for NgClass support on SVG elements */\n// import 'classlist.js';  // Run `npm install --save classlist.js`.\n\n/** IE10 and IE11 requires the following for the Reflect API. */\n// import 'core-js/es6/reflect';\n\n/**\n * Web Animations `@angular/platform-browser/animations`\n * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.\n * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).\n */\n// import 'web-animations-js';  // Run `npm install --save web-animations-js`.\n\n/**\n * By default, zone.js will patch all possible macroTask and DomEvents\n * user can disable parts of macroTask/DomEvents patch by setting following flags\n * because those flags need to be set before `zone.js` being loaded, and webpack\n * will put import in the top of bundle, so user need to create a separate file\n * in this directory (for example: zone-flags.ts), and put the following flags\n * into that file, and then add the following code before importing zone.js.\n * import './zone-flags.ts';\n *\n * The flags allowed in zone-flags.ts are listed here.\n *\n * The following flags will work for all browsers.\n *\n * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame\n * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick\n * (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames\n *\n *  in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js\n *  with the following flag, it will bypass `zone.js` patch for IE/Edge\n *\n *  (window as any).__Zone_enable_cross_context_check = true;\n *\n */\n\n/***************************************************************************************************\n * Zone JS is required by default for Angular itself.\n */\nimport 'zone.js/dist/zone';  // Included with Angular CLI.\n\n\n/***************************************************************************************************\n * APPLICATION IMPORTS\n */\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/styles.css",
    "content": "/* You can add global styles to this file, and also import other style files */\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/test.ts",
    "content": "// This file is required by karma.conf.js and loads recursively all the .spec and framework files\n\nimport 'zone.js/dist/zone-testing';\nimport { getTestBed } from '@angular/core/testing';\nimport {\n  BrowserDynamicTestingModule,\n  platformBrowserDynamicTesting\n} from '@angular/platform-browser-dynamic/testing';\n\ndeclare const require: any;\n\n// First, initialize the Angular testing environment.\ngetTestBed().initTestEnvironment(\n  BrowserDynamicTestingModule,\n  platformBrowserDynamicTesting()\n);\n// Then we find all the tests.\nconst context = require.context('./', true, /\\.spec\\.ts$/);\n// And load the modules.\ncontext.keys().map(context);\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/tsconfig.app.json",
    "content": "{\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"../out-tsc/app\",\n    \"types\": []\n  },\n  \"exclude\": [\n    \"test.ts\",\n    \"**/*.spec.ts\"\n  ]\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/tsconfig.spec.json",
    "content": "{\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"../out-tsc/spec\",\n    \"types\": [\n      \"jasmine\",\n      \"node\"\n    ]\n  },\n  \"files\": [\n    \"test.ts\",\n    \"polyfills.ts\"\n  ],\n  \"include\": [\n    \"**/*.spec.ts\",\n    \"**/*.d.ts\"\n  ]\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/src/tslint.json",
    "content": "{\n    \"extends\": \"../tslint.json\",\n    \"rules\": {\n        \"directive-selector\": [\n            true,\n            \"attribute\",\n            \"app\",\n            \"camelCase\"\n        ],\n        \"component-selector\": [\n            true,\n            \"element\",\n            \"app\",\n            \"kebab-case\"\n        ]\n    }\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/tsconfig.json",
    "content": "{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"baseUrl\": \"./\",\n    \"outDir\": \"./dist/out-tsc\",\n    \"sourceMap\": true,\n    \"declaration\": false,\n    \"module\": \"es2015\",\n    \"moduleResolution\": \"node\",\n    \"emitDecoratorMetadata\": true,\n    \"experimentalDecorators\": true,\n    \"importHelpers\": true,\n    \"target\": \"es5\",\n    \"typeRoots\": [\n      \"node_modules/@types\"\n    ],\n    \"lib\": [\n      \"es2018\",\n      \"dom\"\n    ]\n  }\n}\n"
  },
  {
    "path": "en/developer/appdev/code/angular-guestbook/before/angular-guestbook/tslint.json",
    "content": "{\n  \"rulesDirectory\": [\n    \"codelyzer\"\n  ],\n  \"rules\": {\n    \"arrow-return-shorthand\": true,\n    \"callable-types\": true,\n    \"class-name\": true,\n    \"comment-format\": [\n      true,\n      \"check-space\"\n    ],\n    \"curly\": true,\n    \"deprecation\": {\n      \"severity\": \"warn\"\n    },\n    \"eofline\": true,\n    \"forin\": true,\n    \"import-blacklist\": [\n      true,\n      \"rxjs/Rx\"\n    ],\n    \"import-spacing\": true,\n    \"indent\": [\n      true,\n      \"spaces\"\n    ],\n    \"interface-over-type-literal\": true,\n    \"label-position\": true,\n    \"max-line-length\": [\n      true,\n      140\n    ],\n    \"member-access\": false,\n    \"member-ordering\": [\n      true,\n      {\n        \"order\": [\n          \"static-field\",\n          \"instance-field\",\n          \"static-method\",\n          \"instance-method\"\n        ]\n      }\n    ],\n    \"no-arg\": true,\n    \"no-bitwise\": true,\n    \"no-console\": [\n      true,\n      \"debug\",\n      \"info\",\n      \"time\",\n      \"timeEnd\",\n      \"trace\"\n    ],\n    \"no-construct\": true,\n    \"no-debugger\": true,\n    \"no-duplicate-super\": true,\n    \"no-empty\": false,\n    \"no-empty-interface\": true,\n    \"no-eval\": true,\n    \"no-inferrable-types\": [\n      true,\n      \"ignore-params\"\n    ],\n    \"no-misused-new\": true,\n    \"no-non-null-assertion\": true,\n    \"no-redundant-jsdoc\": true,\n    \"no-shadowed-variable\": true,\n    \"no-string-literal\": false,\n    \"no-string-throw\": true,\n    \"no-switch-case-fall-through\": true,\n    \"no-trailing-whitespace\": true,\n    \"no-unnecessary-initializer\": true,\n    \"no-unused-expression\": true,\n    \"no-use-before-declare\": true,\n    \"no-var-keyword\": true,\n    \"object-literal-sort-keys\": false,\n    \"one-line\": [\n      true,\n      \"check-open-brace\",\n      \"check-catch\",\n      \"check-else\",\n      \"check-whitespace\"\n    ],\n    \"prefer-const\": true,\n    \"quotemark\": [\n      true,\n      \"single\"\n    ],\n    \"radix\": true,\n    \"semicolon\": [\n      true,\n      \"always\"\n    ],\n    \"triple-equals\": [\n      true,\n      \"allow-null-check\"\n    ],\n    \"typedef-whitespace\": [\n      true,\n      {\n        \"call-signature\": \"nospace\",\n        \"index-signature\": \"nospace\",\n        \"parameter\": \"nospace\",\n        \"property-declaration\": \"nospace\",\n        \"variable-declaration\": \"nospace\"\n      }\n    ],\n    \"unified-signatures\": true,\n    \"variable-name\": false,\n    \"whitespace\": [\n      true,\n      \"check-branch\",\n      \"check-decl\",\n      \"check-operator\",\n      \"check-separator\",\n      \"check-type\"\n    ],\n    \"no-output-on-prefix\": true,\n    \"use-input-property-decorator\": true,\n    \"use-output-property-decorator\": true,\n    \"use-host-property-decorator\": true,\n    \"no-input-rename\": true,\n    \"no-output-rename\": true,\n    \"use-life-cycle-interface\": true,\n    \"use-pipe-transform-interface\": true,\n    \"component-class-suffix\": true,\n    \"directive-class-suffix\": true\n  }\n}\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/.babelrc",
    "content": "{\n\t\"presets\": [\"env\", \"react\"]\n}\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/.gitignore",
    "content": "build/*\ndist/*\nnode_modules/*\nliferay-npm-bundler-report.html\n.webpack/*"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/.npmbuildrc",
    "content": "{\n\t\"liferayDir\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.1.2-ga3-ldn\\\\liferay-ce-portal-7.1.2-ga3\",\n\t\"webpack\": {\n\t\t\"rules\": [\n\t\t\t{\n\t\t\t\t\"test\": \"src\\\\\\\\.*\\\\.js$\",\n\t\t\t\t\"use\": \"babel-loader\"\n\t\t\t}\n\t\t]\n\t}\n}"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/.npmbundlerrc",
    "content": "{\n\t\"create-jar\": {\n\t\t\"output-dir\": \"dist\",\n\t\t\"features\": {\n\t\t\t\"js-extender\": true,\n\t\t\t\"web-context\": \"/my-generated-react-guestbook-app\"\n\t\t}\n\t},\n\t\"dump-report\": true,\n\t\"process-serially\": true\n}"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/README.md",
    "content": "# my-generated-react-guestbook-app\n\nMy Generated React Guestbook App"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/assets/.placeholder",
    "content": ""
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/assets/css/App.css",
    "content": ".App {\n  text-align: center;\n}\n\n.App-logo {\n  animation: App-logo-spin infinite 20s linear;\n  height: 40vmin;\n  pointer-events: none;\n}\n\n.App-header {\n  background-color: #282c34;\n  min-height: 100vh;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-size: calc(10px + 2vmin);\n  color: white;\n}\n\n.App-link {\n  color: #61dafb;\n}\n\n@keyframes App-logo-spin {\n  from {\n    transform: rotate(0deg);\n  }\n  to {\n    transform: rotate(360deg);\n  }\n}\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/assets/css/index.css",
    "content": "body {\n  margin: 0;\n  padding: 0;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n    \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n    sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n  font-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\",\n    monospace;\n}"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/assets/css/styles.css",
    "content": "@import \"App.css\";\n@import \"index.css\";\n\n.test {\n  color: red;\n}"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/package.json",
    "content": "{\n\t\"name\": \"my-generated-react-guestbook-app\",\n\t\"version\": \"1.0.0\",\n\t\"description\": \"My Generated React Guestbook App\",\n\t\"devDependencies\": {\n\t\t\"liferay-npm-bundler\": \"^2.6.2\",\n\t\t\"liferay-npm-build-support\": \"^2.6.2\",\n\t\t\"copy-webpack-plugin\": \"^4.5.4\",\n\t\t\"webpack\": \"^4.0.0\",\n\t\t\"webpack-cli\": \"^3.0.0\",\n\t\t\"webpack-dev-server\": \"^3.0.0\",\n\t\t\"babel-cli\": \"^6.26.0\",\n\t\t\"babel-preset-env\": \"^1.7.0\",\n\t\t\"babel-preset-react\": \"^6.0.0\",\n\t\t\"babel-loader\": \"^7.0.0\"\n\t},\n\t\"dependencies\": {\n\t\t\"react\": \"^16.8.1\",\n\t\t\"react-dom\": \"^16.8.1\",\n\t\t\"react-router-dom\": \"^4.3.1\"\n\t},\n\t\"scripts\": {\n\t\t\"build\": \"babel --source-maps -d build src && npm run copy-assets && liferay-npm-bundler\",\n\t\t\"copy-assets\": \"lnbs-copy-assets\",\n\t\t\"deploy\": \"npm run build && lnbs-deploy\",\n\t\t\"start\": \"lnbs-start\"\n\t},\n\t\"portlet\": {\n\t\t\"com.liferay.portlet.display-category\": \"category.sample\",\n\t\t\"com.liferay.portlet.header-portlet-css\": \"/css/styles.css\",\n\t\t\"com.liferay.portlet.instanceable\": true,\n\t\t\"javax.portlet.name\": \"my_generated_react_guestbook_app\",\n\t\t\"javax.portlet.security-role-ref\": \"power-user,user\",\n\t\t\"javax.portlet.display-name\": \"My Generated React Guestbook App\"\n\t},\n\t\"main\": \"index.js\"\n}\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/src/App.js",
    "content": "import React from 'react';\nimport { Route, Switch, Redirect } from 'react-router-dom';\nimport EntryForm from './add-entry';\nimport Guestbook from './view-guestbook';\n\nclass App extends React.Component {\n  constructor(props) {\n    super(props);\n    this.addEntry = this.addEntry.bind(this);\n    this.resetRedirect = this.resetRedirect.bind(this);\n    this.state = {\n      entries: [\n        { id: 1, name: 'Joe Bloggs', message: 'Had an awesome Time!' },\n        { id: 2, name: 'Jane Bloggs', message: 'Great event!' },\n        { id: 3, name: 'Bill Bloggs', message: 'Had a good time.' },\n        { id: 4, name: 'Bob Nosester', message: 'Great atmosphere!' },\n        { id: 5, name: 'Martha Nosester', message: 'Lovely aromas.' }\n      ],\n      redirect: false\n    };\n  }\n\n  addEntry(name, message){\n    const newEntry = {id: this.state.entries.length + 1, name: name, message: message};\n    \n    if(name !== '' && message !== ''){\n      this.setState({\n        entries: this.state.entries.concat(newEntry),\n        redirect: true\n      });\n    }\n  }\n  \n  resetRedirect(){\n    this.setState({\n      redirect: false\n    });\n  }\n  \n  render(){\n    return (\n      <Switch>\n        <Route exact path=\"/\" render={() => (<Redirect to=\"/view-guestbook\"/>)}/>\n        <Route path=\"/view-guestbook\" render={() => < Guestbook entries={this.state.entries} resetRedirect={this.resetRedirect}/>}/>\n        <Route path=\"/add-entry\" render={() => this.state.redirect ? (< Redirect to=\"/view-guestbook\"/>) : (< EntryForm addMethod={this.addEntry}/>)}/>\n      </Switch>\n    )\n  }\n}\n\nexport default App;\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/src/App.test.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\n\nit('renders without crashing', () => {\n  const div = document.createElement('div');\n  ReactDOM.render(<App />, div);\n  ReactDOM.unmountComponentAtNode(div);\n});\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/src/add-entry.js",
    "content": "import React from 'react';\nimport { Link } from 'react-router-dom';\n\nclass EntryForm extends React.Component {\n  constructor(props) {\n    super(props);\n    this.state = {\n      name: '',\n      message: ''\n    };\n  }\n\n  handleChange(event) {\n    this.setState({[event.target.name]: event.target.value});\n  }\n  \n  handleClick(event) {\n    if(this.state.name !== '' && this.state.message !== ''){\n      this.props.addMethod(this.state.name, this.state.message);\n    }\n    else {\n      alert('you must fill in all fields');\n    }\n  }\n  \n  render() {\n\n      return (\n        <form>\n          <div className=\"form-group\">\n            <label htmlFor=\"addEntryNameInput\">Name</label>\n            <input className=\"form-control\" id=\"addEntryNameInput\" value={this.state.name} onChange={this.handleChange.bind(this)} name=\"name\" aria-label=\"name\" />\n          </div>\n          <div className=\"form-group mb-4\">\n            <label htmlFor=\"addEntryMessageInput\">Message</label>\n            <input className=\"form-control\" id=\"addEntryMessageInput\" value={this.state.message} onChange={this.handleChange.bind(this)} name=\"message\" aria-label=\"message\" />\n          </div>\n          <button type=\"button\" className=\"btn btn-primary mr-2\" onClick={this.handleClick.bind(this)}>Save</button>\n          <Link to=\"/view-guestbook\" className=\"btn btn-outline-secondary\">Cancel</Link>\n        </form>\n      );\n  }\n}\n\nexport default EntryForm;"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/src/index.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\n//import './index.css';//removed for Portal Migration\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\nimport { HashRouter as Router } from 'react-router-dom';\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: http://bit.ly/CRA-PWA\n//serviceWorker.unregister();\n\nexport default function main({portletNamespace, contextPath, portletElementId}) {\n    \n      ReactDOM.render((\n        <div className=\"mx-auto\">\n          <div className=\"ml-3 mr-3 mt-3\">\n            <img src=\"/o/my-generated-react-guestbook-app/logo.svg\" className=\"App-logo\" alt=\"logo\" />\n            <Router>\n              <App/>\n            </Router>\n          </div>\n        </div>\n      ), document.getElementById(portletElementId));\n    \n}\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: http://bit.ly/CRA-PWA\nserviceWorker.unregister();"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/src/serviceWorker.js",
    "content": "// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read http://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n  window.location.hostname === 'localhost' ||\n    // [::1] is the IPv6 localhost address.\n    window.location.hostname === '[::1]' ||\n    // 127.0.0.1/8 is considered localhost for IPv4.\n    window.location.hostname.match(\n      /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n    )\n);\n\nexport function register(config) {\n  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n    // The URL constructor is available in all browsers that support SW.\n    const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n    if (publicUrl.origin !== window.location.origin) {\n      // Our service worker won't work if PUBLIC_URL is on a different origin\n      // from what our page is served on. This might happen if a CDN is used to\n      // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n      return;\n    }\n\n    window.addEventListener('load', () => {\n      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n      if (isLocalhost) {\n        // This is running on localhost. Let's check if a service worker still exists or not.\n        checkValidServiceWorker(swUrl, config);\n\n        // Add some additional logging to localhost, pointing developers to the\n        // service worker/PWA documentation.\n        navigator.serviceWorker.ready.then(() => {\n          console.log(\n            'This web app is being served cache-first by a service ' +\n              'worker. To learn more, visit http://bit.ly/CRA-PWA'\n          );\n        });\n      } else {\n        // Is not localhost. Just register service worker\n        registerValidSW(swUrl, config);\n      }\n    });\n  }\n}\n\nfunction registerValidSW(swUrl, config) {\n  navigator.serviceWorker\n    .register(swUrl)\n    .then(registration => {\n      registration.onupdatefound = () => {\n        const installingWorker = registration.installing;\n        if (installingWorker == null) {\n          return;\n        }\n        installingWorker.onstatechange = () => {\n          if (installingWorker.state === 'installed') {\n            if (navigator.serviceWorker.controller) {\n              // At this point, the updated precached content has been fetched,\n              // but the previous service worker will still serve the older\n              // content until all client tabs are closed.\n              console.log(\n                'New content is available and will be used when all ' +\n                  'tabs for this page are closed. See http://bit.ly/CRA-PWA.'\n              );\n\n              // Execute callback\n              if (config && config.onUpdate) {\n                config.onUpdate(registration);\n              }\n            } else {\n              // At this point, everything has been precached.\n              // It's the perfect time to display a\n              // \"Content is cached for offline use.\" message.\n              console.log('Content is cached for offline use.');\n\n              // Execute callback\n              if (config && config.onSuccess) {\n                config.onSuccess(registration);\n              }\n            }\n          }\n        };\n      };\n    })\n    .catch(error => {\n      console.error('Error during service worker registration:', error);\n    });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n  // Check if the service worker can be found. If it can't reload the page.\n  fetch(swUrl)\n    .then(response => {\n      // Ensure service worker exists, and that we really are getting a JS file.\n      const contentType = response.headers.get('content-type');\n      if (\n        response.status === 404 ||\n        (contentType != null && contentType.indexOf('javascript') === -1)\n      ) {\n        // No service worker found. Probably a different app. Reload the page.\n        navigator.serviceWorker.ready.then(registration => {\n          registration.unregister().then(() => {\n            window.location.reload();\n          });\n        });\n      } else {\n        // Service worker found. Proceed as normal.\n        registerValidSW(swUrl, config);\n      }\n    })\n    .catch(() => {\n      console.log(\n        'No internet connection found. App is running in offline mode.'\n      );\n    });\n}\n\nexport function unregister() {\n  if ('serviceWorker' in navigator) {\n    navigator.serviceWorker.ready.then(registration => {\n      registration.unregister();\n    });\n  }\n}\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/after/react-guestbook-app-migrated/src/view-guestbook.js",
    "content": "import React from 'react';\nimport { Link } from 'react-router-dom';\n\nclass Guestbook extends React.Component {\n\n  render() {\n    \n    return (\n      <div className=\"guestbook\">\n        <h1>Guestbook</h1>\n        <table className=\"table table-striped table-bordered mb-4\">\n          <thead className=\"thead-light\">\n            <tr>\n              <th>Name</th>\n              <th>Message</th>\n            </tr>\n          </thead>\n          <CreateTableData entryData={this.props.entries}/>\n        </table>\n        <div>\n          <Link to=\"/add-entry\" onClick={this.props.resetRedirect} className=\"btn btn-outline-secondary\">Add Entry</Link>\n        </div>\n      </div>\n    );\n  }\n}\nconst CreateTableData = props => {\n\n    const entries = props.entryData.map((row, index) => {\n          \n      return (\n        <tr key={index}>\n          <td>{row.name}</td>\n          <td>{row.message}</td>\n        </tr>\n      )\n    });\n    \n    return <tbody>{entries}</tbody>;\n\n}\n\n\nexport default Guestbook;"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# production\n/build\n\n# misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/README.md",
    "content": "This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).\n\n## Available Scripts\n\nIn the project directory, you can run:\n\n### `npm start`\n\nRuns the app in the development mode.<br>\nOpen [http://localhost:3000](http://localhost:3000) to view it in the browser.\n\nThe page will reload if you make edits.<br>\nYou will also see any lint errors in the console.\n\n### `npm test`\n\nLaunches the test runner in the interactive watch mode.<br>\nSee the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.\n\n### `npm run build`\n\nBuilds the app for production to the `build` folder.<br>\nIt correctly bundles React in production mode and optimizes the build for the best performance.\n\nThe build is minified and the filenames include the hashes.<br>\nYour app is ready to be deployed!\n\nSee the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.\n\n### `npm run eject`\n\n**Note: this is a one-way operation. Once you `eject`, you can’t go back!**\n\nIf you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.\n\nInstead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.\n\nYou don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.\n\n## Learn More\n\nYou can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).\n\nTo learn React, check out the [React documentation](https://reactjs.org/).\n\n### Code Splitting\n\nThis section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting\n\n### Analyzing the Bundle Size\n\nThis section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size\n\n### Making a Progressive Web App\n\nThis section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app\n\n### Advanced Configuration\n\nThis section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration\n\n### Deployment\n\nThis section has moved here: https://facebook.github.io/create-react-app/docs/deployment\n\n### `npm run build` fails to minify\n\nThis section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/package.json",
    "content": "{\n  \"name\": \"my-react-guestbook-app\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"dependencies\": {\n    \"react\": \"^16.8.1\",\n    \"react-dom\": \"^16.8.1\",\n    \"react-router-dom\": \"^4.3.1\",\n    \"react-scripts\": \"2.1.5\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test\",\n    \"eject\": \"react-scripts eject\"\n  },\n  \"eslintConfig\": {\n    \"extends\": \"react-app\"\n  },\n  \"browserslist\": [\n    \">0.2%\",\n    \"not dead\",\n    \"not ie <= 11\",\n    \"not op_mini all\"\n  ]\n}\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <link rel=\"shortcut icon\" href=\"%PUBLIC_URL%/favicon.ico\" />\n    <!-- Bootstrap CSS -->\n    <link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css\" integrity=\"sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO\" crossorigin=\"anonymous\">\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"\n    />\n    <meta name=\"theme-color\" content=\"#000000\" />\n    <!--\n      manifest.json provides metadata used when your web app is installed on a\n      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/\n    -->\n    <link rel=\"manifest\" href=\"%PUBLIC_URL%/manifest.json\" />\n    <!--\n      Notice the use of %PUBLIC_URL% in the tags above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n    <title>React App</title>\n  </head>\n  <body>\n    <noscript>You need to enable JavaScript to run this app.</noscript>\n    <div id=\"root\"></div>\n    <!--\n      This HTML file is a template.\n      If you open it directly in the browser, you will see an empty page.\n\n      You can add webfonts, meta tags, or analytics to this file.\n      The build step will place the bundled scripts into the <body> tag.\n\n      To begin the development, run `npm start` or `yarn start`.\n      To create a production bundle, use `npm run build` or `yarn build`.\n    -->\n  </body>\n</html>\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/public/manifest.json",
    "content": "{\n  \"short_name\": \"React App\",\n  \"name\": \"Create React App Sample\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\",\n      \"sizes\": \"64x64 32x32 24x24 16x16\",\n      \"type\": \"image/x-icon\"\n    }\n  ],\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"theme_color\": \"#000000\",\n  \"background_color\": \"#ffffff\"\n}\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/src/App.css",
    "content": ".App {\n  text-align: center;\n}\n\n.App-logo {\n  animation: App-logo-spin infinite 20s linear;\n  height: 40vmin;\n  pointer-events: none;\n}\n\n.App-header {\n  background-color: #282c34;\n  min-height: 100vh;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-size: calc(10px + 2vmin);\n  color: white;\n}\n\n.App-link {\n  color: #61dafb;\n}\n\n@keyframes App-logo-spin {\n  from {\n    transform: rotate(0deg);\n  }\n  to {\n    transform: rotate(360deg);\n  }\n}\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/src/App.js",
    "content": "import React from 'react';\nimport { Route, Switch, Redirect } from 'react-router-dom';\nimport EntryForm from './add-entry';\nimport Guestbook from './view-guestbook';\n\nclass App extends React.Component {\n  constructor(props) {\n    super(props);\n    this.addEntry = this.addEntry.bind(this);\n    this.resetRedirect = this.resetRedirect.bind(this);\n    this.state = {\n      entries: [\n        { id: 1, name: 'Joe Bloggs', message: 'Had an awesome Time!' },\n        { id: 2, name: 'Jane Bloggs', message: 'Great event!' },\n        { id: 3, name: 'Bill Bloggs', message: 'Had a good time.' },\n        { id: 4, name: 'Bob Nosester', message: 'Great atmosphere!' },\n        { id: 5, name: 'Martha Nosester', message: 'Lovely aromas.' }\n      ],\n      redirect: false\n    };\n  }\n\n  addEntry(name, message){\n    \n    const newEntry = {id: this.state.entries.length + 1, name: name, message: message};\n    \n    if(name !== '' && message !== ''){\n      this.setState({\n        entries: this.state.entries.concat(newEntry),\n        redirect: true\n      });\n    }\n  }\n  \n  resetRedirect(){\n    this.setState({\n      redirect: false\n    });\n  }\n  \n  render(){\n    return (\n      <Switch>\n        <Route exact path=\"/\" render={() => (<Redirect to=\"/view-guestbook\"/>)}/>\n        <Route path=\"/view-guestbook\" render={() => < Guestbook entries={this.state.entries} resetRedirect={this.resetRedirect}/>}/>\n        <Route path=\"/add-entry\" render={() => this.state.redirect ? (< Redirect to=\"/view-guestbook\"/>) : (< EntryForm addMethod={this.addEntry}/>)}/>\n      </Switch>\n    )\n  }\n}\n\nexport default App;\n\n/*\nimport React, { Component } from 'react';\nimport { Route, Switch, Redirect } from 'react-router-dom';\nimport EntryForm from './add-entry';\nimport Guestbook from './view-guestbook';\n\nclass App extends Component {\n  \n  state = {\n    entries: [\n      { id: 1, name: 'Joe Bloggs', message: 'Had an awesome Time!' },\n      { id: 2, name: 'Jane Bloggs', message: 'Great event!' },\n      { id: 3, name: 'Bill Bloggs', message: 'Had a good time.' },\n      { id: 4, name: 'Bob Nosester', message: 'Great atmosphere!' },\n      { id: 5, name: 'Martha Nosester', message: 'Lovely aromas.' }\n    ],\n    redirect: false\n  };\n  \n  addEntry = (name, message) => {\n    const {entries} = this.state;\n    \n    const newEntry = {id: this.state.entries.length + 1, name: name, message: message};\n    \n    if(name !== '' && message !== ''){\n      this.setState({\n        entries: entries.concat(newEntry),\n        redirect: true\n      });\n    }\n  }\n  \n  resetRedirect = () => {\n    this.setState({\n      redirect: false\n    });\n  }\n  \n  render() {\n    return (\n      <Switch>\n        <Route exact path=\"/\" render={() => (<Redirect to=\"/view-guestbook\"/>)}/>\n        <Route path=\"/view-guestbook\" render={() => < Guestbook entries={this.state.entries} resetRedirect={this.resetRedirect}/>}/>\n        <Route path=\"/add-entry\" render={() => this.state.redirect ? (< Redirect to=\"/view-guestbook\"/>) : (< EntryForm addMethod={this.addEntry}/>)}/>\n      </Switch>\n    )\n  }\n}\n\nexport default App;\n*/"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/src/App.test.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\n\nit('renders without crashing', () => {\n  const div = document.createElement('div');\n  ReactDOM.render(<App />, div);\n  ReactDOM.unmountComponentAtNode(div);\n});\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/src/add-entry.js",
    "content": "import React from 'react';\nimport { Link } from 'react-router-dom';\n\nclass EntryForm extends React.Component {\n  constructor(props) {\n    super(props);\n    this.state = {\n      name: '',\n      message: ''\n    };\n  }\n\n  handleChange(event) {\n    this.setState({[event.target.name]: event.target.value});\n  }\n  \n  handleClick(event) {\n    if(this.state.name !== '' && this.state.message !== ''){\n      this.props.addMethod(this.state.name, this.state.message);\n    }\n    else {\n      alert('you must fill in all fields');\n    }\n  }\n  \n  render() {\n\n      return (\n        <form>\n          <div className=\"form-group\">\n            <label htmlFor=\"addEntryNameInput\">Name</label>\n            <input className=\"form-control\" id=\"addEntryNameInput\" value={this.state.name} onChange={this.handleChange.bind(this)} name=\"name\" aria-label=\"name\" />\n          </div>\n          <div className=\"form-group mb-4\">\n            <label htmlFor=\"addEntryMessageInput\">Message</label>\n            <input className=\"form-control\" id=\"addEntryMessageInput\" value={this.state.message} onChange={this.handleChange.bind(this)} name=\"message\" aria-label=\"message\" />\n          </div>\n          <button type=\"button\" className=\"btn btn-primary mr-2\" onClick={this.handleClick.bind(this)}>Save</button>\n          <Link to=\"/view-guestbook\" className=\"btn btn-outline-secondary\">Cancel</Link>\n        </form>\n      );\n  }\n}\n\nexport default EntryForm;"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/src/index.css",
    "content": "body {\n  margin: 0;\n  padding: 0;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n    \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n    sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n  font-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\",\n    monospace;\n}\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/src/index.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\nimport { HashRouter as Router } from 'react-router-dom';\n\nReactDOM.render((\n  <div className=\"mx-auto\">\n    <div className=\"ml-3 mr-3 mt-3\">\n      <Router>\n        <App/>\n      </Router>\n    </div>\n  </div>\n), document.getElementById('root'));\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: http://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/src/serviceWorker.js",
    "content": "// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read http://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n  window.location.hostname === 'localhost' ||\n    // [::1] is the IPv6 localhost address.\n    window.location.hostname === '[::1]' ||\n    // 127.0.0.1/8 is considered localhost for IPv4.\n    window.location.hostname.match(\n      /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n    )\n);\n\nexport function register(config) {\n  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n    // The URL constructor is available in all browsers that support SW.\n    const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n    if (publicUrl.origin !== window.location.origin) {\n      // Our service worker won't work if PUBLIC_URL is on a different origin\n      // from what our page is served on. This might happen if a CDN is used to\n      // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n      return;\n    }\n\n    window.addEventListener('load', () => {\n      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n      if (isLocalhost) {\n        // This is running on localhost. Let's check if a service worker still exists or not.\n        checkValidServiceWorker(swUrl, config);\n\n        // Add some additional logging to localhost, pointing developers to the\n        // service worker/PWA documentation.\n        navigator.serviceWorker.ready.then(() => {\n          console.log(\n            'This web app is being served cache-first by a service ' +\n              'worker. To learn more, visit http://bit.ly/CRA-PWA'\n          );\n        });\n      } else {\n        // Is not localhost. Just register service worker\n        registerValidSW(swUrl, config);\n      }\n    });\n  }\n}\n\nfunction registerValidSW(swUrl, config) {\n  navigator.serviceWorker\n    .register(swUrl)\n    .then(registration => {\n      registration.onupdatefound = () => {\n        const installingWorker = registration.installing;\n        if (installingWorker == null) {\n          return;\n        }\n        installingWorker.onstatechange = () => {\n          if (installingWorker.state === 'installed') {\n            if (navigator.serviceWorker.controller) {\n              // At this point, the updated precached content has been fetched,\n              // but the previous service worker will still serve the older\n              // content until all client tabs are closed.\n              console.log(\n                'New content is available and will be used when all ' +\n                  'tabs for this page are closed. See http://bit.ly/CRA-PWA.'\n              );\n\n              // Execute callback\n              if (config && config.onUpdate) {\n                config.onUpdate(registration);\n              }\n            } else {\n              // At this point, everything has been precached.\n              // It's the perfect time to display a\n              // \"Content is cached for offline use.\" message.\n              console.log('Content is cached for offline use.');\n\n              // Execute callback\n              if (config && config.onSuccess) {\n                config.onSuccess(registration);\n              }\n            }\n          }\n        };\n      };\n    })\n    .catch(error => {\n      console.error('Error during service worker registration:', error);\n    });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n  // Check if the service worker can be found. If it can't reload the page.\n  fetch(swUrl)\n    .then(response => {\n      // Ensure service worker exists, and that we really are getting a JS file.\n      const contentType = response.headers.get('content-type');\n      if (\n        response.status === 404 ||\n        (contentType != null && contentType.indexOf('javascript') === -1)\n      ) {\n        // No service worker found. Probably a different app. Reload the page.\n        navigator.serviceWorker.ready.then(registration => {\n          registration.unregister().then(() => {\n            window.location.reload();\n          });\n        });\n      } else {\n        // Service worker found. Proceed as normal.\n        registerValidSW(swUrl, config);\n      }\n    })\n    .catch(() => {\n      console.log(\n        'No internet connection found. App is running in offline mode.'\n      );\n    });\n}\n\nexport function unregister() {\n  if ('serviceWorker' in navigator) {\n    navigator.serviceWorker.ready.then(registration => {\n      registration.unregister();\n    });\n  }\n}\n"
  },
  {
    "path": "en/developer/appdev/code/react-guestbook/before/my-react-guestbook-app/src/view-guestbook.js",
    "content": "import React from 'react';\nimport { Link } from 'react-router-dom';\n\nclass Guestbook extends React.Component {\n\n  render() {\n    \n    return (\n      <div className=\"guestbook\">\n        <h1>Guestbook</h1>\n        <table className=\"table table-striped table-bordered mb-4\">\n          <thead className=\"thead-light\">\n            <tr>\n              <th>Name</th>\n              <th>Message</th>\n            </tr>\n          </thead>\n          <CreateTableData entryData={this.props.entries}/>\n        </table>\n        <div>\n          <Link to=\"/add-entry\" onClick={this.props.resetRedirect} className=\"btn btn-outline-secondary\">Add Entry</Link>\n        </div>\n      </div>\n    );\n  }\n}\nconst CreateTableData = props => {\n\n    const entries = props.entryData.map((row, index) => {\n          \n      return (\n        <tr key={index}>\n          <td>{row.name}</td>\n          <td>{row.message}</td>\n        </tr>\n      )\n    });\n    \n    return <tbody>{entries}</tbody>;\n\n}\n\n\nexport default Guestbook;"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/.babelrc",
    "content": "{\n\t\"presets\": [\"@babel/preset-env\"]\n}\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/.gitignore",
    "content": "build/*\ndist/*\nnode_modules/*\nliferay-npm-bundler-report.html\n.webpack/*"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/.npmbuildrc",
    "content": "{\n\t\"liferayDir\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.1.2-ga3-ldn\\\\liferay-ce-portal-7.1.2-ga3\\\\\",\n\t\"webpack\": {\n\t\t\"rules\": [\n\t\t\t{\n\t\t\t\t\"test\": \"src\\\\\\\\.*\\\\.js$\",\n\t\t\t\t\"use\": \"babel-loader\"\n\t\t\t}\n\t\t]\n\t}\n}"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/.npmbundlerrc",
    "content": "{\n\t\"create-jar\": {\n\t\t\"output-dir\": \"dist\",\n\t\t\"features\": {\n\t\t\t\"js-extender\": true,\n\t\t\t\"web-context\": \"/vue-guestbook-migrated\"\n\t\t}\n\t},\n\t\"config\": {\n\t\t\"imports\": {\n\t\t\t\"vuejs-provider\": {\n\t\t\t\t\"vue\": \"^2.0.0\"\n\t\t\t}\n\t\t}\n\t},\n\t\"dump-report\": true\n}"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/README.md",
    "content": "# vue-guestbook-migrated\n\nVue Guestbook Migrated"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/assets/.placeholder",
    "content": ""
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/assets/css/custom.css",
    "content": ".vue-app-logo {\n  height: 40vmin;\n}\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/assets/css/styles.css",
    "content": "@import \"custom.css\";\n@import \"../index.css\";\n\n.tag {\n\tfont-weight: bold;\n}\n\n.value {\n\tfont-style: italic;\n}\n\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/package.json",
    "content": "{\n\t\"name\": \"vue-guestbook-migrated\",\n\t\"version\": \"1.0.0\",\n\t\"description\": \"Vue Guestbook Migrated\",\n\t\"devDependencies\": {\n\t\t\"liferay-npm-bundler\": \"^2.6.2\",\n\t\t\"liferay-npm-build-support\": \"^2.6.2\",\n\t\t\"copy-webpack-plugin\": \"^4.5.4\",\n\t\t\"webpack\": \"^4.0.0\",\n\t\t\"webpack-cli\": \"^3.0.0\",\n\t\t\"webpack-dev-server\": \"^3.0.0\",\n\t\t\"@babel/cli\": \"^7.0.0\",\n\t\t\"@babel/preset-env\": \"^7.4.2\",\n\t\t\"babel-loader\": \"^7.0.0\",\n\t\t\"@vue/cli-plugin-babel\": \"^3.4.0\",\n\t\t\"@vue/cli-plugin-eslint\": \"^3.4.0\",\n\t\t\"@vue/cli-service\": \"^3.4.0\",\n\t\t\"babel-eslint\": \"^10.0.1\",\n\t\t\"eslint\": \"^5.13.0\",\n\t\t\"eslint-plugin-vue\": \"^5.1.0\",\n\t\t\"vue-template-compiler\": \"^2.6.1\",\n\t\t\"vueify\": \"9.4.1\"\n\t},\n\t\"dependencies\": {\n\t\t\"vue\": \"^2.6.6\",\n\t\t\"vue-router\": \"^3.0.2\"\n\t},\n\t\"eslintConfig\": {\n\t\t\"root\": true,\n\t\t\"env\": {\n\t\t\t\"node\": true\n\t\t},\n\t\t\"extends\": [\n\t\t\t\"plugin:vue/essential\",\n\t\t\t\"eslint:recommended\"\n\t\t],\n\t\t\"rules\": {},\n\t\t\"parserOptions\": {\n\t\t\t\"parser\": \"babel-eslint\"\n\t\t}\n\t},\n\t\"scripts\": {\n\t\t\"build\": \"babel --source-maps -d build src && vue-cli-service build --dest build/ --formats commonjs --target lib --name index ./src/index.js && npm run copy-assets && liferay-npm-bundler\",\n\t\t\"copy-assets\": \"lnbs-copy-assets\",\n\t\t\"deploy\": \"npm run build && lnbs-deploy\",\n\t\t\"start\": \"lnbs-start\"\n\t},\n\t\"portlet\": {\n\t\t\"com.liferay.portlet.display-category\": \"category.sample\",\n\t\t\"com.liferay.portlet.header-portlet-css\": \"/css/styles.css\",\n\t\t\"com.liferay.portlet.instanceable\": true,\n\t\t\"javax.portlet.name\": \"vue_guestbook_migrated\",\n\t\t\"javax.portlet.security-role-ref\": \"power-user,user\",\n\t\t\"javax.portlet.display-name\": \"Vue Guestbook Migrated\"\n\t},\n\t\"main\": \"index.common\"\n}\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/src/Api.js",
    "content": "// import axios from 'axios';\n\nexport default class Api {\n    static getEntries() {\n        // This is a mock web service call\n        // return axios.get(`https://localhost:8080/o/api/p/gb/entry`);\n\n        // This returns mock data\n        return [\n          { id: 1, name: 'Joe Bloggs', message: 'Had an awesome Time!' },\n          { id: 2, name: 'Jane Bloggs', message: 'Great event!' },\n          { id: 3, name: 'Bill Bloggs', message: 'Had a good time.' },\n          { id: 4, name: 'Bob Nosester', message: 'Great atmosphere!' },\n          { id: 5, name: 'Martha Nosester', message: 'Lovely aromas.' }\n        ]\n\n        // Here's a service to try too\n        // return axios.get(`https://jsonplaceholder.typicode.com/users`);\n    }\n\n    static addEntry(entry) {\n        return fetch('http://localhost:8080/o/api/p/gb/entry', {\n            method: 'POST',\n            body: JSON.stringify(entry),\n            headers: {\n              \"Content-type\": \"application/json; charset=UTF-8\"\n            }\n          })\n          .then(response => response.json())\n          .then(json => this.console.log(json))\n    }\n}\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/src/App.vue",
    "content": "<template>\n  <div id=\"app\">\n    <img alt=\"Vue logo\" class=\"vue-app-logo\" src=\"/o/vue-guestbook-migrated/logo.png\">\n    <router-view :entries=\"entries\" :add=\"add\"/>\n  </div>\n</template>\n\n<script>\nimport ViewGuestbook from './components/ViewGuestbook.vue';\nimport AddEntry from './components/AddEntry.vue';\nimport api from './Api';\n\nexport default {\n  name: 'app',\n  components: {\n    ViewGuestbook,\n    AddEntry\n  },\n  data: function() {\n    return {\n      entries: [],\n      entryId: 6\n    }\n  },\n  created: function() {\n      // api.getEntries().then(response => {\n      //   this.loading = false;\n      //   this.entries = response.data;\n      // }, error => {\n      //   this.loading = false;\n      //   this.console.error(error);\n      // });\n\n      // TODO For mock data, replace method body with the line above. \n      this.entries = api.getEntries();\n  },\n  methods: {\n    add: function (name, message) {\n      if(name !== '' && message !== ''){\n        this.entries.push({\n          id: this.entryId++,\n          name: name,\n          message: message\n        });\n        \n        this.$router.push({ path: '/view-guestbook' })\n      }\n      else {\n        alert('you must fill in all fields');\n      }\n    }\n  }\n}\n</script>\n\n<style>\n.test-class{\n  background-color: red;\n  width: 900px;\n  height: auto;\n}\n</style>\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/src/components/AddEntry.vue",
    "content": "<template>\n  <div class=\"ml-2 mr-2\">\n    <form>\n      <fieldset>\n        <div class=\"form-group\">\n          <label for=\"addEntryNameInput\">Name</label>\n          <input class=\"form-control\" id=\"addEntryNameInput\" name=\"name\" v-model=\"name\" aria-label=\"name\" />\n        </div>\n        <div class=\"form-group mb-4\">\n          <label for=\"addEntryMessageInput\">Message</label>\n          <input class=\"form-control\" id=\"addEntryMessageInput\" name=\"message\" v-model=\"message\" aria-label=\"message\" />\n        </div>\n      </fieldset>\n\n      <div class=\"button-footer\">  \n        <button type=\"button\" class=\"btn btn-primary mr-2\" v-on:click=\"add(name, message)\">Save</button>\n        <routerLink to=\"/view-guestbook\" class=\"btn btn-outline-secondary\">Cancel</routerLink>\n      </div>\n    </form>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'AddEntry',\n  data: function() {\n    return {\n    name: '',\n    message: '' \n    }\n  },\n  props: ['add']\n}\n</script>"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/src/components/ViewGuestbook.vue",
    "content": "<template>\n  <div class=\"ml-2 mr-2\">\n    <h1>Guestbook</h1>\n    <table class=\"table table-striped table-bordered mb-4\">\n      <thead class=\"thead-light\">\n        <tr>\n          <th>Name</th>\n          <th>Message</th>\n        </tr>\n      </thead>\n      <tbody>\n        <tr v-for=\"entry in entries\" :key=\"entry.id\">\n          <td>{{entry.name}}</td>\n          <td>{{entry.message}}</td>\n        </tr>\n      </tbody>\n    </table>\n    <div class=\"button-footer\">\n      <routerLink to=\"/add-entry\" class=\"btn btn-outline-secondary\">Add Entry</routerLink>\n    </div>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'ViewGuestbook',\n  props: ['entries']\n}\n</script>"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/after/vue-guestbook-migrated/src/index.js",
    "content": "import Vue from 'vue/dist/vue.common'; // use Vue's runtime + compiler module\nimport App from './App.vue'\nimport VueRouter from 'vue-router'\nimport AddEntry from './components/AddEntry.vue'\nimport ViewGuestbook from './components/ViewGuestbook.vue'\n\nexport default function main({portletNamespace, contextPath, portletElementId}) {\n\n  Vue.config.productionTip = false\n\n  Vue.use(VueRouter)\n\n  const router = new VueRouter({\n      routes: [\n          {\n              path: \"/\",\n              redirect: {\n                  name: \"ViewGuestbook\"\n              }\n          },\n          {\n              path: '/view-guestbook',\n              name: 'ViewGuestbook',\n              component: ViewGuestbook\n          },\n          {\n              path: '/add-entry',\n              name: 'AddEntry',\n              component: AddEntry\n          }\n      ]\n  })\n  new Vue({\n    el: `#${portletElementId}`,\n    render: h => h(App),\n    router\n  })\n}"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/before/vue-guestbook/.gitignore",
    "content": ".DS_Store\nnode_modules\n/dist\n\n# local env files\n.env.local\n.env.*.local\n\n# Log files\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Editor directories and files\n.idea\n.vscode\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw*\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/before/vue-guestbook/README.md",
    "content": "# vue-guestbook\n\n## Project setup\n```\nnpm install\n```\n\n### Compiles and hot-reloads for development\n```\nnpm run serve\n```\n\n### Compiles and minifies for production\n```\nnpm run build\n```\n\n### Run your tests\n```\nnpm run test\n```\n\n### Lints and fixes files\n```\nnpm run lint\n```\n\n### Customize configuration\nSee [Configuration Reference](https://cli.vuejs.org/config/).\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/before/vue-guestbook/babel.config.js",
    "content": "module.exports = {\n  presets: [\n    '@vue/app'\n  ]\n}\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/before/vue-guestbook/package.json",
    "content": "{\n  \"name\": \"vue-guestbook\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"serve\": \"vue-cli-service serve\",\n    \"build\": \"vue-cli-service build\",\n    \"lint\": \"vue-cli-service lint\"\n  },\n  \"dependencies\": {\n    \"vue\": \"^2.6.6\",\n    \"vue-router\": \"^3.0.2\"\n  },\n  \"devDependencies\": {\n    \"@vue/cli-plugin-babel\": \"^3.5.0\",\n    \"@vue/cli-plugin-eslint\": \"^3.5.0\",\n    \"@vue/cli-service\": \"^3.5.0\",\n    \"babel-eslint\": \"^10.0.1\",\n    \"eslint\": \"^5.8.0\",\n    \"eslint-plugin-vue\": \"^5.0.0\",\n    \"vue-template-compiler\": \"^2.5.21\"\n  },\n  \"eslintConfig\": {\n    \"root\": true,\n    \"env\": {\n      \"node\": true\n    },\n    \"extends\": [\n      \"plugin:vue/essential\",\n      \"eslint:recommended\"\n    ],\n    \"rules\": {},\n    \"parserOptions\": {\n      \"parser\": \"babel-eslint\"\n    }\n  },\n  \"postcss\": {\n    \"plugins\": {\n      \"autoprefixer\": {}\n    }\n  },\n  \"browserslist\": [\n    \"> 1%\",\n    \"last 2 versions\",\n    \"not ie <= 8\"\n  ]\n}\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/before/vue-guestbook/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\">\n    <!-- Bootstrap CSS -->\n    <link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css\" integrity=\"sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO\" crossorigin=\"anonymous\">\n    <link rel=\"icon\" href=\"<%= BASE_URL %>favicon.ico\">\n    <title>vue-guestbook</title>\n  </head>\n  <body>\n    <noscript>\n      <strong>We're sorry but vue-guestbook doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>\n    </noscript>\n    <div id=\"app\"></div>\n    <!-- built files will be auto injected -->\n  </body>\n</html>\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/before/vue-guestbook/src/Api.js",
    "content": "// import axios from 'axios';\n\nexport default class Api {\n    static getEntries() {\n        // This is a mock web service call\n        // return axios.get(`https://localhost:8080/o/api/p/gb/entry`);\n\n        // This returns mock data\n        return [\n          { id: 1, name: 'Joe Bloggs', message: 'Had an awesome Time!' },\n          { id: 2, name: 'Jane Bloggs', message: 'Great event!' },\n          { id: 3, name: 'Bill Bloggs', message: 'Had a good time.' },\n          { id: 4, name: 'Bob Nosester', message: 'Great atmosphere!' },\n          { id: 5, name: 'Martha Nosester', message: 'Lovely aromas.' }\n        ]\n\n        // Here's a service to try too\n        // return axios.get(`https://jsonplaceholder.typicode.com/users`);\n    }\n\n    static addEntry(entry) {\n        return fetch('http://localhost:8080/o/api/p/gb/entry', {\n            method: 'POST',\n            body: JSON.stringify(entry),\n            headers: {\n              \"Content-type\": \"application/json; charset=UTF-8\"\n            }\n          })\n          .then(response => response.json())\n          .then(json => this.console.log(json))\n    }\n}\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/before/vue-guestbook/src/App.vue",
    "content": "<template>\n  <div id=\"app\">\n    <img alt=\"Vue logo\" src=\"./assets/logo.png\">\n    <router-view :entries=\"entries\" :add=\"add\"/>\n  </div>\n</template>\n\n<script>\nimport ViewGuestbook from './components/ViewGuestbook.vue';\nimport AddEntry from './components/AddEntry.vue';\nimport api from './Api';\n\nexport default {\n  name: 'app',\n  components: {\n    ViewGuestbook,\n    AddEntry\n  },\n  data: function() {\n    return {\n      entries: [],\n      entryId: 6\n    }\n  },\n  created: function() {\n      // api.getEntries().then(response => {\n      //   this.loading = false;\n      //   this.entries = response.data;\n      // }, error => {\n      //   this.loading = false;\n      //   this.console.error(error);\n      // });\n\n      // TODO For mock data, replace method body with the line above. \n      this.entries = api.getEntries();\n  },\n  methods: {\n    add: function (name, message) {\n      if(name !== '' && message !== ''){\n        this.entries.push({\n          id: this.entryId++,\n          name: name,\n          message: message\n        });\n        \n        this.$router.push({ path: '/view-guestbook' })\n      }\n      else {\n        alert('you must fill in all fields');\n      }\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/before/vue-guestbook/src/components/AddEntry.vue",
    "content": "<template>\n  <div class=\"ml-2 mr-2\">\n    <form>\n      <fieldset>\n        <div class=\"form-group\">\n          <label for=\"addEntryNameInput\">Name</label>\n          <input class=\"form-control\" id=\"addEntryNameInput\" name=\"name\" v-model=\"name\" aria-label=\"name\" />\n        </div>\n        <div class=\"form-group mb-4\">\n          <label for=\"addEntryMessageInput\">Message</label>\n          <input class=\"form-control\" id=\"addEntryMessageInput\" name=\"message\" v-model=\"message\" aria-label=\"message\" />\n        </div>\n      </fieldset>\n\n      <div class=\"button-footer\">  \n        <button type=\"button\" class=\"btn btn-primary mr-2\" v-on:click=\"add(name, message)\">Save</button>\n        <routerLink to=\"/view-guestbook\" class=\"btn btn-outline-secondary\">Cancel</routerLink>\n      </div>\n    </form>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'AddEntry',\n  data: function() {\n    return {\n    name: '',\n    message: '' \n    }\n  },\n  props: ['add']\n}\n</script>"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/before/vue-guestbook/src/components/ViewGuestbook.vue",
    "content": "<template>\n  <div class=\"ml-2 mr-2\">\n    <h1>Guestbook</h1>\n    <table class=\"table table-striped table-bordered mb-4\">\n      <thead class=\"thead-light\">\n        <tr>\n          <th>Name</th>\n          <th>Message</th>\n        </tr>\n      </thead>\n      <tbody>\n        <tr v-for=\"entry in entries\" :key=\"entry.id\">\n          <td>{{entry.name}}</td>\n          <td>{{entry.message}}</td>\n        </tr>\n      </tbody>\n    </table>\n    <div class=\"button-footer\">\n      <routerLink to=\"/add-entry\" class=\"btn btn-outline-secondary\">Add Entry</routerLink>\n    </div>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'ViewGuestbook',\n  props: ['entries']\n}\n</script>"
  },
  {
    "path": "en/developer/appdev/code/vue-guestbook/before/vue-guestbook/src/main.js",
    "content": "import Vue from 'vue'\nimport App from './App.vue'\nimport VueRouter from 'vue-router'\nimport AddEntry from './components/AddEntry.vue'\nimport ViewGuestbook from './components/ViewGuestbook.vue'\n\nVue.config.productionTip = false\n\nVue.use(VueRouter)\n\nconst router = new VueRouter({\n    routes: [\n        {\n            path: \"/\",\n            redirect: {\n                name: \"ViewGuestbook\"\n            }\n        },\n        {\n            path: '/view-guestbook',\n            name: 'ViewGuestbook',\n            component: ViewGuestbook\n        },\n        {\n            path: '/add-entry',\n            name: 'AddEntry',\n            component: AddEntry\n        }\n    ]\n})\n\nnew Vue({\n  render: h => h(App),\n  router\n}).$mount('#app')\n"
  },
  {
    "path": "en/developer/build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"Developer\" basedir=\".\">\n\n\t<import file=\"../build.xml\" />\n\n\t<property name=\"purpose.dir\" value=\"developer\"/>\n\n</project>\n"
  },
  {
    "path": "en/developer/customization/articles/01-liferay-customization-intro.markdown",
    "content": "---\nheader-id: liferay-customization\n---\n\n# Liferay Customization\n\n[TOC levels=1-4]\n\n@product@ is highly customizable. Its modular architecture contains components\nyou can extend and override dynamically. This section explains @product@'s\narchitecture and customization fundamentals and demonstrates overriding and\nextending @product@ components and applications using APIs. \n\n-   [Fundamentals](/docs/7-2/customization/-/knowledge_base/c/fundamentals)\n    include understanding and configuring dependencies, packaging, and\n    deployment. Here you'll work with module JARs, plugin WARs, components, and\n    Java packages in @product@. \n\n-   [Architecture](/docs/7-2/customization/-/knowledge_base/c/architecture)\n    dives deep into how @product@ uses modularity and OSGi to provide the core,\n    application modules, component services, and extension points. Learning the\n    architecture helps you develop better customizations fast, and it empowers\n    you to build extension points into your own applications.\n\n-   Built-in customization features, including\n    [Widget Templates](/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates)\n    and\n    [Web Experience Management](/docs/7-2/user/-/knowledge_base/u/web-experience-management)\n    help you customize content and pages faster. All this is done from within\n    the @product@ UI.\n\n-   Application customization articles (listed after the Architecture articles)\n    demonstrate modifying Liferay applications via their APIs and extension points. \n\nStart with [Fundamentals](/docs/7-2/customization/-/knowledge_base/c/fundamentals). \n"
  },
  {
    "path": "en/developer/customization/articles/02-fundamentals/01-fundamentals-intro.markdown",
    "content": "---\nheader-id: fundamentals\n---\n\n# Fundamentals\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/liferay-internals/fundamentals.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe fundamentals of developing on @product@ and customizing it are perhaps best\nlearned in the context of projects. It's in projects that you configure access\nto @product@'s API, extend and override @product@ features, and package your\nsoftware for deployment. Projects are developed as WARs or OSGi JARs, but are\nall installed to Liferay's OSGi framework as OSGi bundles. These bundles can\ndepend on external Java packages, share Java packages, and be manipulated at run\ntime via Apache Gogo Shell. The fundamentals are explained in the context of\nprojects so that you understand them in a practical sense and can apply them\nright away. Here are the fundamental topics:\n\n-   **WARs Versus OSGi JAR** explains fundamental differences between the WAR\n    and OSGi JAR structures and how they're deployed in @product@.\n\n-   **Configuring Dependencies** demonstrates how to identify and configure\n    Liferay artifacts and third-party artifacts to use their Java packages in\n    your projects.\n\n-   **Importing and Exporting Packages** shows how to import the packages your\n    projects need and export packages your projects provide. Liferay's tooling\n    detects package use and specifies package imports automatically.\n\n-   **Semantic Versioning** shows how @product@ uses a standard for ascribing\n    meaning to major, minor, and micro versions of modules and Java packages.\n\n-   **Deploying WARs (WAB Generator)** explains how Liferay's WAB Generator\n    deploys WAR applications as OSGi Web Application Bundles (WABs).\n\n-   **Gogo Shell** enables you to examine components, debug issues, and manage\n    deployments.\n\n<!-- Uncomment when 'Architecture' section is available. Jim\n| **Note:** The\n| Architecture section\n| provides conceptual diagrams and explains @product@ services and components,\n| and applications, and the @product@ core.\n-->\n\nStart with understanding how WAR and OSGi JAR project structures are used in\ndevelopment.\n"
  },
  {
    "path": "en/developer/customization/articles/02-fundamentals/02-configuring-dependencies/01-configuring-dependencies-intro.markdown",
    "content": "---\nheader-id: configuring-dependencies\n---\n\n# Configuring Dependencies\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/liferay-internals/fundamentals/configuring-dependencies.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n@product@'s modular environment lets modules provide and consume capabilities\nvia Java packages. To leverage packages from other modules or traditional\nlibraries in your project, you must configure them as dependencies. Here you'll\nlearn how to find artifacts (modules or libraries) and configure dependencies on\nthem.\n\n-   [Finding Artifacts](/docs/7-2/customization/-/knowledge_base/c/finding-artifacts)\n    explains how to use the Application Manager, Gogo Shell, and @product@\n    reference documentation to find artifacts deployed on @product@ and\n    available in repositories.\n\n-   [Specifying Dependencies](/docs/7-2/customization/-/knowledge_base/c/specifying-dependencies)\n    demonstrates specifying artifacts to Maven and Gradle build frameworks. It\n    shows you how to determine whether @product@ already exports packages from\n    an artifact and how to configure such artifacts as compile-time\n    dependencies.\n\n-   [Resolving Third-Party Library Package Dependencies](/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module)\n    provides a workflow for using packages that are only available in\n    traditional library JARs (JARs that aren't OSGi modules). It involves\n    minimizing transitive dependencies so you can resolve dependencies quicker\n    and prevent bloating your project with unnecessary JARs.\n\nYour first step is to find the artifacts you need.\n"
  },
  {
    "path": "en/developer/customization/articles/02-fundamentals/02-configuring-dependencies/02-finding-artifacts.markdown",
    "content": "---\nheader-id: finding-artifacts\n---\n\n# Finding Artifacts\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/liferay-internals/fundamentals/configuring-dependencies/finding-artifacts.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nUsing external artifacts in your project requires configuring their\ndependencies. To do this, look up the artifact's attributes and plug them into\ndependency entries for your build system (either \n[Gradle](https://gradle.org/)\nor\n[Maven](https://maven.apache.org/)).\nYour build system downloads the dependency artifacts your project needs to\ncompile successfully. \n\nBefore specifying an artifact as a dependency, you must first find its\nattributes. Artifacts have these attributes: \n\n-   *Group ID*: Authoring organization \n-   *Artifact ID*: Name/identifier \n-   *Version*: Release number \n\nHere you'll learn how to find artifact attributes to specify artifact dependencies. \n\n## Finding Core Artifact Attributes\n\nEach Liferay artifact is a JAR file whose `META-INF/MANIFEST.MF` file specifies\nOSGi bundle metadata the artifact's attributes. For example, these two OSGi\nheaders specify the artifact ID and version: \n\n    Bundle-SymbolicName:  [artifact ID]\n    Bundle-Version: [version]\n\n| **Important:** Artifacts in @product@ fix packs override @product@\n| installation artifacts. Subfolders of a fix pack ZIP file's `binaries` folder\n| hold the artifacts. If an installed fix pack provides an artifact you depend | on, specify the version of that fix pack artifact in your dependency.\n\nThis table lists each core @product@ artifact's group ID and artifact ID and\nwhere to find the artifact's manifest, which lists the artifact version: \n\n*Core @product@ Artifacts*:\n\n| File          | Group ID | Artifact ID | Version | Origin | \n| :------------ | :--------------- | :-------- | :--------- | :------ |\n| `portal-kernel.jar` | `com.liferay.portal` | `com.liferay.portal.kernel` | (see JAR's `MANIFEST.MF`) | fix pack ZIP, @product@ installation, or @product@ dependencies ZIP |\n| `portal-impl.jar` | `com.liferay.portal` |  `com.liferay.portal.impl` | (see JAR's `MANIFEST.MF`) | fix pack ZIP or @product@ `.war` |\n| `util-bridges.jar` | `com.liferay.portal` | `com.liferay.util.bridges` | (see JAR's `MANIFEST.MF`) |  fix pack ZIP or @product@ `.war` |\n| `util-java.jar` | `com.liferay.portal` |  `com.liferay.util.java` | (see JAR's `MANIFEST.MF`) |  fix pack ZIP or @product@ `.war` |\n| `util-slf4j.jar` | `com.liferay.portal` |  `com.liferay.util.slf4j` | (see JAR's `MANIFEST.MF`) | fix pack ZIP or  @product@ `.war` |\n| `util-taglibs.jar` | `com.liferay.portal` | `com.liferay.util.taglib` | (see JAR's `MANIFEST.MF`) | fix pack ZIP or @product@ `.war` |\n| `com.liferay.*` JAR files | `com.liferay` | (see JAR's `MANIFEST.MF`) | (see JAR's `MANIFEST.MF`) | fix pack ZIP, @product@ installation, @product@ dependencies ZIP, or the OSGi ZIP |\n\nNext, you'll learn how to find @product@ app and independent module artifact\nattributes. \n\n## Finding Liferay App and Independent Artifacts\n\nIndependent modules and @product@ app modules aren't part of the  @product@\ncore. You must still, however, find their artifact attributes if you  depend on\nthem. The resources below provide the  artifact details for @product@'s apps and\nindependent modules: \n\n| Resource | Artifact Type |\n| :-------- | :-------------- |\n| [App Manager](#app-manager) | Deployed modules |\n| [Reference Docs](#reference-docs) | @product@ modules (per release) |\n| [Maven Central](#maven-central) | All artifact types: @product@ and third party, module and non-module |\n\n| **Important**: `com.liferay` is the group ID for all of Liferay's apps and\n| independent modules.\n\nThe App Manager is the best source for information on deployed modules. You'll \nlearn about it next. \n\n### App Manager\n\n[The App Manager](/docs/7-2/user/-/knowledge_base/u/managing-and-configuring-apps#using-the-app-manager) \nknows what's deployed on your Liferay instance. Use it to find deployed module\nattributes. \n\n1.  In @product@, navigate to *Control Panel* &rarr; *Apps* &rarr; *App \n    Manager*. \n\n2.  Search for the module by its display name, symbolic name, or related\n    keywords. You can also browse for the module in its app. Whether browsing\n    or searching, the App Manager shows the module's artifact ID and version\n    number. \n\n![Figure 1: You can inspect deployed module artifact IDs and version numbers.](../../../images/configuring-dependencies-search-app-manager-for-module.png)\n\n![Figure 2: The App Manager aggregates Liferay and independent modules.](../../../images/configuring-dependencies-indep-modules-in-app-manager.png)\n\nIf you don't know a deployed module's group ID, use the \n[Felix Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell) \nto find it: \n\n1.  Navigate to the Gogo Shell portlet in the Control Panel &rarr;\n    *Configuration* &rarr; *Gogo Shell*. Enter commands in the Felix Gogo Shell\n    command prompt. \n\n2.  Search for the module by its display name (e.g., `Liferay Blogs API`) or\n    a keyword. In the results, note the module's number. You can use it in the\n    next step. For example, Gogo command results in the figure below show the\n    Liferay Blogs API module number. \n\n    ![Figure 3: Results from this Gogo command show that the module's number is `1173`.](../../../images/configuring-deps-gogo-grep-for-module.png)\n\n3.  List the module's manifest headers by passing the module number to the \n    `headers` command. In the results, note the `Bundle-Vendor` value: you'll \n    match it with an artifact group in a later step: \n\n    ![Figure 4: Results from running the `headers` command show the module's bundle vendor and bundle version.](../../../images/configuring-deps-gogo-module-info.png)\n\n5.  On\n    [Maven Central](https://search.maven.org/)\n    or\n    [MVNRepository](https://mvnrepository.com),\n    search for the module by its artifact ID. \n\n6.  Determine the group ID by matching the `Bundle-Vendor` value from step 3 \n    with a group listed that provides the artifact. \n\nNext, @product@'s reference documentation provides @product@ app artifact\nattributes. \n\n### Reference Docs\n\n@product@'s app Javadoc lists each app module's artifact ID, version number, and\ndisplay name. This is the best place to look up @product@ app modules that\naren't yet deployed to your @product@ instance. \n\n| **Note:** To find artifact information on a Core @product@ artifact, refer to\n| the previous section *Finding Core @product@ Artifact Attributes*.\n\nFollow these steps to find a @product@ app module's attributes in the Javadoc: \n\n1.  Navigate to Javadoc for an app module class. If you don't have a link to the\n    class's Javadoc, find it by browsing \n    [@app-ref@](@app-ref@). \n\n2.  Copy the class's package name. \n\n3.  Navigate to the *Overview* page. \n\n4.  On the *Overview* page, search for the package name you copied in step 2. \n\nThe heading above the package name shows the module's artifact ID, version \nnumber, and display name. Remember, the group ID for all app modules is \n`com.liferay`. \n\n![Figure 5: @product@ app Javadoc overviews list each app module's display name, followed by its group ID, artifact ID, and version number in a colon-separated string. It's a Gradle artifact syntax.](../../../images/intro-configuring-dependencies-module-info-in-javadoc-overview.png)\n\n| **Note**: Module version numbers aren't currently included in any tag library\n| reference docs.\n\nNext, you'll learn how to look up artifacts on MVNRepository and Maven  Central. \n\n### Maven Central\n\nMost artifacts, regardless of type or origin, are on \n[MVNRepository](https://mvnrepository.com/)\nand\n[Maven Central](https://search.maven.org/).\nThese sites can help you find artifacts based on class packages. It's common to\ninclude an artifact's ID in the start of an artifact's package names. For\nexample, if you depend on the class\n`org.osgi.service.component.annotations.Component`, search for the package name\n`org.osgi.service.component.annotations` on one of the Maven sites. \n\n| **Note:** Make sure to follow the instructions listed earlier to determine the\n| version of Liferay artifacts you need.\n\nNow that you know the artifact's attributes, you can configure a  dependency on\nit. \n\n## Related Topics \n\n[Specifying Dependencies](/docs/7-2/customization/-/knowledge_base/c/specifying-dependencies)\n\n[Importing Packages](/docs/7-2/customization/-/knowledge_base/c/importing-packages)\n\n[Exporting Packages](/docs/7-2/customization/-/knowledge_base/c/exporting-packages)\n\n[Resolving Third Party Library Package Dependencies](/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module)\n\n[Deploying WARs \\(WAB Generator\\)](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\n"
  },
  {
    "path": "en/developer/customization/articles/02-fundamentals/02-configuring-dependencies/03-specifying-dependencies.markdown",
    "content": "---\nheader-id: specifying-dependencies\n---\n\n# Specifying Dependencies\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/liferay-internals/fundamentals/configuring-dependencies/specifying-dependencies.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nCompiling your project and deploying it to @product@ requires satisfying its\ndependencies on external artifacts. After\n[finding the attributes of an artifact](/docs/7-2/customization/-/knowledge_base/c/finding-artifacts), \nset a dependency for it in your build file. Here's how: \n\n1.  Determine whether @product@ provides the Java packages you use from the \n    artifact. These files list the packages @product@ exports:\n\n    -   `modules/core/portal-bootstrap/system.packages.extra.bnd` file in the\n        [GitHub repository](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/core/portal-bootstrap/system.packages.extra.bnd).\n        It lists exported packages on separate lines, making them easy to read. \n\n    -   `META-INF/system.packages.extra.mf` file in\n        `[LIFERAY_HOME]/osgi/core/com.liferay.portal.bootstrap.jar`. The file is\n        available in @product@ bundles. It lists exported packages in a\n        paragraph wrapped at 70 columns--they're harder to read here than in the\n        `system.packages.extra.bnd` file. \n\n2.  If @product@ exports all the packages you use from the artifact, specify the\n    artifact as a compile-only dependency. This prevents your build framework\n    from bundling the artifact with your project. Here's how to make the\n    dependency compile-only:\n\n    **Gradle:** Add the `compileOnly` directive to the dependency\n    \n    **Maven:** Add the `<scope>provided</scope>` element to the dependency. \n\n3.  Add a dependency entry for the artifact. Here's the artifact terminology for\n    the Gradle and Maven build frameworks:\n\n*Artifact Terminology*\n\n| Framework | Group ID  | Artifact ID  | Version   |\n| :-------- | :-------- | :----------- | :-------- |\n| Gradle    | `group`   | `name`       | `version` |\n| Maven     | `groupId` | `artifactId` | `version` |\n\nHere is an example dependency on Liferay's Journal API module for Gradle,\nand Maven: \n\n*Gradle (`build.gradle` entry):*\n\n```groovy\ndependencies {\n    compileOnly group: \"com.liferay\", name: \"com.liferay.journal.api\", version: \"1.0.1\"\n    ...\n}\n```\n\n*Maven (`pom.xml` entry):*\n\n```xml\n<dependency>\n    <groupId>com.liferay</groupId>\n    <artifactId>com.liferay.journal.api</artifactId>\n    <version>1.0.1</version>\n    <scope>provided</scope>\n</dependency>\n```\n\n| **Important:**\n| [@product@ exports many third-party packages](/docs/7-2/reference/-/knowledge_base/r/third-party-packages-portal-exports).\n| Deploy your module to check if @product@ or another module in your Liferay\n| instance's OSGi runtime framework provides the package you need. If it's\n| provided already, specify the corresponding dependency as being \"provided\".\n| Here's how to specify a provided dependency:\n| \n| Maven: `<scope>provided</scope>`\n|  \n| Gradle: `providedCompile`\n|\n| Don't deploy a provided package's JAR again or embed the JAR in  your project.\n| Exporting the same package from different JARs leads to \"split package\" \n| issues, whose side affects differ from case to case. If the package is in a\n| third-party library (not an OSGi module), refer to\n| [Resolving Third\nParty Library Dependencies](/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module). \n|\n| If you're developing a WAR that requires a different version of a third-party \n| package that\n| [@product@ or another module exports](/docs/7-2/reference/-/knowledge_base/r/third-party-packages-portal-exports),\n| specify that package in your\n| [`Import-Package:` list](/docs/7-2/customization/-/knowledge_base/c/importing-packages).\n| If the package provider is an OSGi module, publish its exported packages by \n| deploying that module. Otherwise, follow the instructions for\n| [adding a third-party library (not an OSGi module)](/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module). \n\nNice! You know how to specify artifact dependencies. Now that's a skill you can\ndepend on! \n\n## Related Topics \n\n[Finding Artifacts](/docs/7-2/customization/-/knowledge_base/c/finding-artifacts)\n\n[Importing Packages](/docs/7-2/customization/-/knowledge_base/c/importing-packages)\n\n[Exporting Packages](/docs/7-2/customization/-/knowledge_base/c/exporting-packages)\n\n[Resolving Third Party Library Package Dependencies](/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module)\n\n[Deploying WARs \\(WAB Generator\\)](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\n"
  },
  {
    "path": "en/developer/customization/articles/02-fundamentals/02-configuring-dependencies/04-resolving-third-party-library-package-dependencies.markdown",
    "content": "---\nheader-id: adding-third-party-libraries-to-a-module\n---\n\n# Resolving Third Party Library Package Dependencies\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/liferay-internals/fundamentals/configuring-dependencies/resolving-third-party-library-package-dependencies.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay's OSGi framework lets you build applications composed of multiple OSGi\nbundles (modules). For the framework to assemble the modules into a working\nsystem, the modules must resolve their Java package dependencies. In a perfect\nworld, every Java library would be an OSGi module, but many libraries aren't. So\nhow do you resolve the packages your project needs from non-OSGi third party\nlibraries?\n\nHere is the main workflow for resolving third party Java library packages:\n\n**Option 1 - Find an OSGi module of the library**: Projects, such as\n[Eclipse Orbit](https://www.eclipse.org/orbit/)\nand\n[ServiceMix Bundles](https://servicemix.apache.org/developers/source/bundles-source.html), \nconvert hundreds of traditional Java libraries to OSGi modules. Their artifacts\nare available at these locations:\n\n-   [Eclipse Orbit downloads \\(select a build\\)](https://download.eclipse.org/tools/orbit/downloads/)\n-   [ServiceMix Bundles](https://mvnrepository.com/artifact/org.apache.servicemix.bundles)\n\nDeploying the module to Liferay's OSGi framework lets you share it on the\nsystem. If you find a module for the library you need,\n[deploy](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project) it. Then\n[add a compile-only\ndependency](/docs/7-2/customization/-/knowledge_base/c/specifying-dependencies)\nfor it in your project. When you deploy your project, the OSGi framework wires\nthe dependency module to your project's module or web application bundle (WAB).\nIf you don't find an OSGi module based on the Java library, follow Option 2. \n\n| **Tip:** Refrain from embedding library JARs that provide the same \n| [packages that @product@ or existing modules provide already](/docs/7-2/reference/-/knowledge_base/r/third-party-packages-portal-exports). \n \n| **Note:** If you're developing a WAR that requires a different version of a\n| third-party package that\n| [@product@ or another module exports](/docs/7-2/reference/-/knowledge_base/r/third-party-packages-portal-exports),\n| specify that package in your\n| [`Import-Package:` list](/docs/7-2/customization/-/knowledge_base/c/importing-packages).\n| If the package provider is an OSGi module, publish its exported packages by \n| deploying that module. Otherwise, rename the third-party library (not an OSGi \n| module) differently from the\n| [JAR that the WAB generator excludes](/docs/7-2/customization/-/knowledge_base/c/understanding-excluded-jars)\n| and embed the JAR in your project. \n\n**Option 2 - Resolve the Java packages privately in your project**: Copy\n*required packages* only from libraries into your project, if you can or embed\n*libraries* wholesale, if you must. The rest of this article shows you how to\ndo these things. \n\n| **Note:** Features for manipulating library packages are only available to \n| module projects that use bnd  and the `com.liferay.plugin` plugin, such as\n| [Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)\n| modules. WAR projects must embed libraries wholesale into their classpath. \n\n| **Note**: Liferay's Gradle plugin `com.liferay.plugin` automates several third\n| party library configuration steps. The plugin is automatically applied to\n| [Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)\n| Gradle module projects created using\n| [Liferay @ide@](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio)\n| or\n| [Liferay Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli).\n| \n| To leverage the `com.liferay.plugin` plugin outside of Liferay Workspace, add\n| code like the listing below to your Gradle project and update the version of \n| the `com.liferay.gradle.plugins` artifact to the latest version found in the \n| repository:\n| \n|     buildscript {\n|         dependencies {\n|             classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins\", version: \"4.0.4\"\n|         }\n| \n|         repositories {\n|             maven {\n|                 url \"https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/\"\n|             }\n|         }\n|     }\n| \n|     apply plugin: \"com.liferay.plugin\"\n| \n| If you use Gradle without the `com.liferay.plugin` plugin, you must\n| [embed the third party libraries wholesale](#embedding-libraries-using-gradle).\n\nThe recommended package resolution workflow is next. \n\n## Library Package Resolution Workflow\n\nWhen you depend on a library JAR, much of the time you only need parts of it.\nExplicitly specifying only the Java packages you need makes your module more\nmodular. This also keeps other modules that depend on your module from\nincorporating unneeded packages. \n\nHere's a configuration workflow for module projects that minimizes dependencies\nand Java package imports:\n\n1.  Add the library as a compile-only dependency (e.g., `compileOnly` in\n    Gradle, `<scope>provided</scope>` in Maven).\n\n2.  Copy only the library packages you need by specifying them in a conditional\n    package instruction (`Conditional-Package`) in your `bnd.bnd` file. Here are\n    some examples:\n\n    `Conditional-Package: foo.common*` adds packages your module uses such as\n    `foo.common`, `foo.common-messages`, `foo.common-web` to your module's\n    class path.\n\n    `Conditional-Package: foo.bar.*` adds packages your module uses such as\n    `foo.bar` and all its sub-packages (e.g., `foo.bar.baz`, `foo.bar.biz`,\n    etc.) to your module's class path.\n\n    [Deploy your\n    project](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project). If a\n    class your module needs or class its dependencies need isn't found, go back\n    to main workflow **Step 1 - Find an OSGi module version of the library** to\n    resolve it. \n\n    **Important**: Resolving packages by using compile-only dependencies and\n    conditional package instructions assures you use only the packages you need\n    and avoids unnecessary transitive dependencies. It's recommended to use the\n    steps up to this point, as much as possible, to resolve required packages.  \n\n3.  If a library package you depend on requires non-class files (e.g., DLLs,\n    descriptors) from the library, then you might need to\n    [embed the library wholesale in your module](#embedding-libraries-in-a-project).\n    This adds the entire library to your module's classpath.\n\nNext you'll learn how to embed libraries in your module project. \n\n## Embedding Libraries in a Project\n\nYou can use Gradle or Maven to embed libraries in your project. Below are\nexamples for adding\n[Apache Shiro](https://shiro.apache.org)\nusing both build utilities. \n\n### Embedding Libraries Using Gradle\n\nOpen your module's `build.gradle` file and add the library as a dependency in\nthe `compileInclude` configuration:\n\n```groovy\ndependencies {\n    compileInclude group: 'org.apache.shiro', name: 'shiro-core', version: '1.1.0'\n}\n```\n\nThe `com.liferay.plugin` plugin's `compileInclude` configuration is transitive.\nThe `compileInclude` configuration embeds the artifact and all its dependencies\nin a `lib` folder in the module's JAR. Also, it adds the artifact JARs to the\nmodule's `Bundle-ClassPath` manifest header. \n\n**Note**: The `compileInclude` configuration does not download transitive\n[optional dependencies](https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html).\nIf your module requires such artifacts, add them as you would another third party library.\n\n**Note:** If the library you've added as a dependency in your `build.gradle`\nfile has transitive dependencies, you can reference them by name in an\n`-includeresource:` instruction without having to add them explicitly to the\ndependency list. See how it's used in the Maven section next. \n\n### Embedding a Library Using Maven\n\nFollow these steps:\n\n1.  Open your project's `pom.xml` file and add the library as a dependency in \n    the `provided` scope:\n\n    ```xml\n    <dependency>\n      <groupId>org.apache.shiro</groupId>\n      <artifactId>shiro-core</artifactId>\n      <version>1.1.0</version>\n      <scope>provided</scope>\n    </dependency>\n    ```\n\n2.  Open your module's `bnd.bnd` file and add the library to an\n    `-includeresource` instruction: \n\n        -includeresource: META-INF/lib/shiro-core.jar=shiro-core-[0-9]*.jar;lib:=true\n\n    This instruction adds the `shiro-core-[version].jar` file as an included\n    resource in the module's `META-INF/lib` folder. The\n    `META-INF/lib/shiro-core.jar` is your module's embedded library. The\n    expression `[0-9]*` helps the build tool match the library version \n    to make available on the module's class path. The `lib:=true` directive adds\n    the embedded JAR to the module's class path via the `Bundle-Classpath`\n    manifest header.\n\nLastly, if after embedding a library you get unresolved imports when trying to\ndeploy to Liferay, you might need to blacklist some imports: \n\n```    \nImport-Package:\\\n    !foo.bar.baz,\\\n    *\n```\n\nThe `*` character represents all packages that the module refers to explicitly. Bnd detects the referenced packages.\n\nCongratulations! Resolving all of your module's package dependencies, especially\nthose from traditional Java libraries, is a quite an accomplishment. \n\n## Related Topics\n\n[Importing Packages](/docs/7-2/customization/-/knowledge_base/c/importing-packages)\n\n[Exporting Packages](/docs/7-2/customization/-/knowledge_base/c/exporting-packages)\n\n[Creating a Project](/docs/7-2/reference/-/knowledge_base/r/creating-a-project)\n"
  },
  {
    "path": "en/developer/customization/articles/02-fundamentals/02-configuring-dependencies/05-understanding-excluded-jars.markdown",
    "content": "---\nheader-id: understanding-excluded-jars\n---\n\n# Understanding Excluded JARs\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/developing-applications/reference/jars-excluded-from-wabs.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[Portal property\n`module.framework.web.generator.excluded.paths`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Module%20Framework)\ndeclares JARs that are stripped from all @product@ [generated\nWABs](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator).\nThese JARs are excluded from web application bundles (WABs) because @product@\nprovides them already. All JARs listed for this property are excluded from a\nWAB, even if the WAB lists the JAR in a `portal-dependency-jars` property in its\n[`liferay-plugin-package.properties`](@platform-ref@/7.2-latest/propertiesdoc/liferay-plugin-package_7_2_0.properties.html)\nfile. \n\nIf your WAR requires different versions of the packages @product@ exports, you\nmust include them in JARs named differently from the ones\n`module.framework.web.generator.excluded.paths` excludes. \n\nFor example, @product@'s\n[`system.packages.extra`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/core/portal-bootstrap/system.packages.extra.bnd)\nmodule exports Spring Framework version 4.1.9 packages:\n\n```\nExport-Package:\\\n    ...\n    org.springframework.*;version='4.1.9',\\\n    ...\n```\n\n@product@ uses the `module.framework.web.generator.excluded.paths` portal\nproperty to exclude their JARs.\n\n```properties\nmodule.framework.web.generator.excluded.paths=\\\n    ...\n    WEB-INF/lib/spring-aop.jar,\\\n    WEB-INF/lib/spring-aspects.jar,\\\n    WEB-INF/lib/spring-beans.jar,\\\n    WEB-INF/lib/spring-context.jar,\\\n    WEB-INF/lib/spring-context-support.jar,\\\n    WEB-INF/lib/spring-core.jar,\\\n    WEB-INF/lib/spring-expression.jar,\\\n    WEB-INF/lib/spring-jdbc.jar,\\\n    WEB-INF/lib/spring-jms.jar,\\\n    WEB-INF/lib/spring-orm.jar,\\\n    WEB-INF/lib/spring-oxm.jar,\\\n    WEB-INF/lib/spring-tx.jar,\\\n    WEB-INF/lib/spring-web.jar,\\\n    WEB-INF/lib/spring-webmvc.jar,\\\n    WEB-INF/lib/spring-webmvc-portlet.jar,\\\n    ...\n```\n\nTo use a different Spring Framework version in your WAR, you must name the\ncorresponding Spring Framework JARs differently from the glob-patterned JARs\n`module.framework.web.generator.excluded.paths` lists. \n\nFor example, to use Spring Framework version 3.0.7's Spring AOP JAR, include it\nin your plugin's `WEB-INF/lib` but name it something other than\n`spring-aop.jar`. Adding the version to the JAR name (i.e.,\n`spring-aop-3.0.7.RELEASE.jar`) differentiates it from the excluded JAR and\nprevents it from being stripped from the WAB (the bundled WAR). \n\n## Related Topics \n\n[Configuring Dependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies)\n\n[Deploying WARs \\(WAB Generator\\)](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\n"
  },
  {
    "path": "en/developer/customization/articles/02-fundamentals/03-felix-gogo-shell/01-felix-gogo-shell-intro.markdown",
    "content": "---\nheader-id: using-the-felix-gogo-shell\n---\n\n# Using the Felix Gogo Shell\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/liferay-internals/fundamentals/using-the-gogo-shell.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe Gogo shell provides a way to interact with @product@'s module framework. You\ncan\n\n- dynamically install/uninstall bundles\n- examine package dependencies\n- examine extension points\n- list service references\n- etc.\n\nThere are two ways you can access the Gogo shell.\n\nThe recommended way to access the Gogo shell for a production environment is\nthrough the Control Panel. Accessing it there is the most secure way to use the\nGogo shell. You can set permissions in your @product@ instance to only give\ncertain people access to it. The Gogo shell is extremely powerful and should\nonly be given to trusted admins, as you can manipulate the platform's core\nfunctionality. You can access the Gogo shell in the Control Panel by navigating\nto *Configuration* &rarr; *Gogo Shell*.\n\nYou can also interact with @product@'s module framework via a local telnet\nsession. This is only recommended when you're developing your @product@\ninstance. This is not recommended for production environments. \n \nTo open the Gogo shell via telnet, execute the following command: \n\n```bash\ntelnet localhost 11311\n```\n\nRunning this command requires a local running instance of @product@ and your\nmachine's telnet command line utilities enabled. You must also have\n[Developer Mode enabled](/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes#enabling-developer-mode-manually).\n\nTo disconnect the session, execute the `disconnect` command. Avoid using the\nfollowing commands, which stop the OSGi framework:\n\n- `close`\n- `exit`\n- `shutdown`\n\nIf you have [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli)\ninstalled and the telnet capability enabled, you can run the Gogo shell via\nBlade command too:\n\n```bash\nblade sh <gogoShellCommand>\n```\n\nHere are some useful Gogo shell commands:\n\n`b [BUNDLE_ID]`: lists information about a specific bundle including the\nbundle's symbolic name, bundle ID, data root, registered (provided) and used\nservices, imported and exported packages, and more\n\n`diag [BUNDLE_ID]`: lists information about why the specified bundle is not\nworking (e.g., unresolved dependencies, etc.)\n\n`headers [BUNDLE_ID]`: lists metadata about the bundle from the bundle's\n`MANIFEST.MF` file\n\n`help`: lists all the available Gogo shell commands. Notice that each command\nhas two parts to its name, separated by a colon. For example, the full name of\nthe `help` command is `felix:help`. The first part is the command scope while\nthe second part is the command function. The scope allows commands with the same\nname to be disambiguated. E.g., scope allows the `felix:refresh` command to be\ndistinguished from the `equinox:refresh` command.\n\n`help [COMMAND_NAME]`: lists information about a specific command including a\ndescription of the command, the scope of the command, and information about any\nflags or parameters that can be supplied when invoking the command.\n\n`inspect capability service [BUNDLE_ID]`: lists services exposed by a bundle\n\n`install [PATH_TO_JAR_FILE]`: installs the specified bundle into Liferay's\nmodule framework\n\n`lb`: lists all of the bundles installed in Liferay's module framework. Use\nthe `-s` flag to list the bundles using the bundles' symbolic names.\n\n`packages [PACKAGE_NAME]`: lists all of the named package's dependencies \n\n`scr:list`: lists all of the components registered in the module framework\n(*scr* stands for service component runtime)\n\n`scr:info [COMPONENT_NAME]`: lists information about a specific component\nincluding the component's description, services, properties, configuration,\nreferences, and more.\n\n`services`: lists all of the services that have been registered in Liferay's\nmodule framework\n\n`start [BUNDLE_ID]`: starts the specified bundle\n\n`stop [BUNDLE_ID]`: stops the specified bundle\n\n`uninstall [BUNDLE_ID]`: uninstalls the specified bundle from Liferay's module\nframework. This does not remove the specified bundle from Liferay's module\nframework; it's hidden from Gogo's `lb` command, but is still present. Adding\na new version of the uninstalled bundle, therefore, will not reinstall it; it\nwill update the currently hidden uninstalled version. To remove a\nbundle from Liferay's module framework permanently, manually delete it from the\n`LIFERAY_HOME/osgi` folder. For more information on the `uninstall` command, see\nOSGi's\n[uninstall](https://osgi.org/javadoc/r6/core/org/osgi/framework/Bundle.html#uninstall\\(\\))\ndocumentation.\n\nFor more information about the Gogo shell, visit\n[Apache's official documentation](http://felix.apache.org/documentation/subprojects/apache-felix-gogo.html).\n"
  },
  {
    "path": "en/developer/customization/articles/02-fundamentals/04-importing-packages/01-importing-packages-intro.markdown",
    "content": "---\nheader-id: importing-packages\n---\n\n# Importing Packages\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/liferay-internals/fundamentals/importing-packages.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nPlugins often must use Java classes from packages outside of themselves. Another\nOSGi bundle (a module or an OSGi Web Application Bundle) in the OSGi framework\nmust\n[export](/docs/7-2/customization/-/knowledge_base/c/exporting-packages)\na package for your plugin to import it.\n\nWhen an OSGi bundle (bundle) is set up to import packages, the OSGi framework\nfinds other registered bundles that export the needed packages and wires them to\nthe importing bundle. At run time, the importing bundle gets the class from the\nwired bundle that exports the class's package.\n\nFor this to happen, a bundle's `META-INF/MANIFEST.MF` file must specify the\n[`Import-Package`](https://bnd.bndtools.org/heads/import_package.html) OSGi manifest header with a comma-separated list of the Java\npackages it needs. For example, if a bundle needs classes from the\n`javax.portlet` and `com.liferay.portal.kernel.util` packages, it must specify\nthem like so:\n\n```\nImport-Package: javax.portlet,com.liferay.portal.kernel.util,*\n```\n\nThe `*` character represents all packages that the module refers to explicitly. Bnd detects the referenced packages.\n\nImport packages must sometimes be specified manually, but not always.\nConveniently, @product@\n[project templates](/docs/7-2/reference/-/knowledge_base/r/project-templates)\nand\n[tools](/docs/7-2/reference/-/knowledge_base/r/tooling)\nautomatically detect the packages a bundle uses and add them to the package\nimports in the bundle's manifest. Here are the different package import\nscenarios:\n\n- [Automatic Package Import Generation](#automatic-package-import-generation)\n\n- [Manually Adding Package Imports](#manually-adding-package-imports)\n\nLet's explore how package imports are specified in these scenarios.\n\n## Automatic Package Import Generation\n\n[Gradle and Maven module projects](/docs/7-2/reference/-/knowledge_base/r/project-templates)\ncreated using\n[Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli),\n[Liferay's Maven archetypes](/docs/7-2/reference/-/knowledge_base/r/maven),\nor\n[Liferay @ide@](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio)\nuse\n[bnd](http://bnd.bndtools.org/).\nOn building such a project's module JAR, bnd detects the packages the module\nuses and generates a `META-INF/MANIFEST.MF` file whose `Import-Package` header\nspecifies the packages.\n\n| **Note:** Liferay's Maven module archetypes use the `bnd-maven-plugin`.\n| Liferay's Gradle module project templates use\n| [a third-party Gradle plugin](https://github.com/TomDmitriev/gradle-bundle-plugin)\n| to invoke bnd.\n\nFor example, suppose you're developing a Liferay module using Maven or Gradle.\nIn most cases, you specify your module's dependencies in your `pom.xml` or\n`build.gradle` file. At build time, the Maven or Gradle module plugin reads your\n`pom.xml` or `build.gradle` file and bnd adds the required `Import-Package`\nheaders to your module JAR's `META-INF/MANIFEST.MF`.\n\nHere's an example dependencies section from a module's `build.gradle` file:\n\n```groovy\ndependencies {\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"2.0.0\"\n    compileOnly group: \"javax.portlet\", name: \"portlet-api\", version: \"2.0\"\n    compileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\", version: \"1.3.0\"\n}\n```\n\nAnd here's the `Import-Package` header that's generated in the module JAR's\n`META-INF/MANIFEST.MF` file:\n\n    Import-Package: com.liferay.portal.kernel.portlet.bridges.mvc;version=\n    \"[1.0,2)\",com.liferay.portal.kernel.util;version=\"[7.0,8)\",javax.nami\n    ng,javax.portlet;version=\"[2.0,3)\",javax.servlet,javax.servlet.http,j\n    avax.sql\n\nNote that your build file need only specify artifact dependencies. bnd examines\nyour module's class path to determine which packages from those artifacts\ncontain classes your application uses and imports the packages. The examination\nincludes all classes found in the class path--even those from embedded\n[third party library JARs](/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module).\n\nRegarding classes used by a plugin WAR,\n[Liferay's WAB Generator](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\ndetects their use in the WAR's JSPs, descriptor files, and classes (in\n`WEB-INF/classes` and embedded JARs). The WAB Generator searches the `web.xml`,\n`liferay-web.xml`, `portlet.xml`, `liferay-portlet.xml`, and `liferay-hook.xml`\ndescriptor files. It adds package imports for classes that are neither found in\nthe plugin's `WEB-INF/classes` folder nor in its embedded JARs.\n\n| **Note:** Packages for Java APIs, such as Java Portlet, aren't semantically\n| versioned but have Portable Java Contracts. Each API's contract specifies the\n| JSR it satisfies. Bundles that use these APIs must specify requirements on the\n| API contracts. The contract requirement specifies your bundle's relationship\n| with the imported API packages. If the system you're running does *not*\n| provide the exact contract, your bundle does not resolve. Resolving the\n| missing package is better than handling an incompatibility failure during\n| execution.\n|\n| -   **Blade CLI and Liferay @ide@ module projects** specify Portable Java\n|     Contracts automatically! For example, if your Blade CLI or Liferay @ide@\n|     module uses the Java Portlet API and you compile against the Java Portlet\n|     2.0 artifact, a contract requirement for the package is added to your\n|     module's manifest.\n|\n| -   **Module projects that use bnd but are not created using Blade CLI or\n|     Liferay @ide@** must specify contracts in their `bnd.bnd` file. For\n|     example, here are contract instructions for Java Portlet and Java Servlet\n|     APIs:\n|\n|         -contract: JavaPortlet,JavaServlet\n|\n|     At build time, bnd adds the contract instructions to your module's\n|     manifest. It adds a requirement for the first version of the API found in\n|     your classpath and *removes* version range information from\n|     `Import-Package` entries for corresponding API packages---the package\n|     version information isn't needed.\n|\n| -   **Projects that don't use bnd** must specify contracts in their OSGi\n|     bundle manifest. For example, here's the specified contract for\n|     `JavaPortlet` 2.0, which goes in your `META-INF/MANIFEST.MF` file:\n|\n|         Import-Package: javax.portlet\n|         Require-Capability: osgi.contract;filter:=(&(osgi.contract=JavaPortlet)(version=2.0))\n|\n| For Portable Java Contract details, see\n| [Portable Java Contract Definitions](https://www.osgi.org/portable-java-contract-definitions/).\n\n## Manually Adding Package Imports\n\nThe WAB Generator and bnd don't add package imports for classes referenced in\nthese places:\n\n-   Unrecognized descriptor file\n-   Custom or unrecognized descriptor element or attribute\n-   Reflection code\n-   Class loader code\n\nIn such cases, you must manually determine these packages and specify an `Import-Package` OSGi header that includes these packages and the packages that Bnd detects automatically. The `Import-Package` header belongs in the location appropriate to your project type:\n\n| Project type | `Import-Package` header location |\n| :----------- | :------------------------------- |\n| Module (uses bnd)     | `[project]/bnd.bnd` |\n| Module (doesn't use bnd) | `[module JAR]/META-INF/MANIFEST.MF` |\n| Traditional Liferay plugin WAR | `WEB-INF/liferay-plugin-package.properties` |\n\nHere's an example of adding a package called `com.liferay.docs.foo` to the list of referenced packages that Bnd detects automatically:\n\n```\nImport-Package:\\\n    com.liferay.docs.foo,\\\n    *\n```\n\n| **Note:** The\n| [WAB Generator](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\n| refrains from adding WAR project embedded\n| third-party JARs to a WAB if\n| [@product@ already exports the JAR's packages](/docs/7-2/customization/-/knowledge_base/c/understanding-excluded-jars).\n|\n| If your WAR requires a different version of a third-party package that\n| @product@ exports, specify that package in your `Import-Package:` list. Then\n| if the package provider is an OSGi module, publish its exported packages by\n| deploying the module. If the package provider is not an OSGi module, follow\n| the instructions for\n| [adding third-party libraries](/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module).\n\nPlease see the [`Import-Package`](https://bnd.bndtools.org/heads/import_package.html) header documentation for more information.\n\nCongratulations! Now you can import all kinds of packages for your modules and\nplugins to use.\n\n## Related Topics\n\n[Configuring Dependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies)\n\n[Deploying WARs \\(WAB Generator\\)](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\n\n[Project Templates](/docs/7-2/reference/-/knowledge_base/r/project-templates)\n\n[Liferay's Maven Archetypes](/docs/7-2/reference/-/knowledge_base/r/maven)\n\n[Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli)\n\n[Liferay @ide@](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio)\n"
  },
  {
    "path": "en/developer/customization/articles/02-fundamentals/05-exporting-packages/01-exporting-packages-intro.markdown",
    "content": "---\nheader-id: exporting-packages\n---\n\n# Exporting Packages\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/liferay-internals/fundamentals/exporting-packages.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAn OSGi bundle's Java packages are private by default. To expose a package, you\nmust explicitly export it. This way you share only the classes you want to\nshare. Exporting a package in your OSGi bundle (bundle) manifest makes all the\npackage's classes available for other bundles to\n[import](/docs/7-2/customization/-/knowledge_base/c/importing-packages).\n\nTo export a package, add it to your module's or plugin's `Export-Package` OSGi\nheader. A header exporting `com.liferay.petra.io` and\n`com.liferay.petra.io.unsync` would look like this:\n\n    Export-Package:\\\n    com.liferay.petra.io,\\\n    com.liferay.petra.io.unsync\n\nThe correct location for the header depends on your project's type:\n\n| Project Type | `Export-Package` header location |\n| :----------- | :------------------------------- |\n| Module JAR (uses bnd)     | `[project]/bnd.bnd` |\n| Module JAR (doesn't use bnd) | `[module JAR]/META-INF/MANIFEST.MF` |\n| Plugin WAR | `WEB-INF/liferay-plugin-package.properties` |\n\nModule projects created using\n[Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli),\n[Liferay's Maven archetypes](/docs/7-2/reference/-/knowledge_base/r/maven),\nor\n[Liferay @ide@](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio)\nuse\n[bnd](http://bnd.bndtools.org/).\nOn building such a project's module JAR, bnd propagates the OSGi headers from\nthe project's `bnd.bnd` file to the JAR's `META-INF/MANIFEST.MF`.\n\nIn module projects that don't use bnd, you must manually add package exports to\nan `Export-Package` header in the module JAR's `META-INF/MANIFEST.MF`.\n\nIn plugin WAR projects, you must add package exports to an\n`Export-Package` header in the project's `WEB-INF/liferay-plugin-package.properties`. On\ncopying the WAR into the `[Liferay Home]/deploy` folder, the\n[WAB Generator](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\npropagates the OSGi headers from the WAR's `liferay-plugin-package.properties`\nfile to the `META-INF/MANIFEST.MF` file in the generated Web Application Bundle (WAB).\n\n| **Note:** bnd makes a module's exported packages *substitutable*. That is, the\n| OSGi framework can substitute your module's exported package with a compatible\n| package of the same name, but potentially different version, that's exported\n| from a different OSGi bundle. bnd enables this for your module by\n| automatically making your module import every package it exports. In this way,\n| your module can work on its own, but can also work in conjunction with bundles\n| that provide a different (compatible) version, or even the same version, of\n| the package. A package from another bundle might provide better \"wiring\"\n| opportunities with other bundles.\n| [Peter Kriens' blog post](http://blog.osgi.org/2007/04/importance-of-exporting-nd-importing.html)\n| provides more details on how substitutable exports works.\n\n| **Important:** Don't export the same package from different JARs. Multiple\n| exports of the same package leads to \"split package\" issues, whose side\n| affects differ from case to case.\n\nNow you can share your module's or plugin's terrific [EDITOR: or terrible!]\npackages with other OSGi bundles!\n\n## Related Topics\n\n[Configuring Dependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies)\n\n[Deploying WARs \\(WAB Generator\\)](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\n\n[Project Templates](/docs/7-2/reference/-/knowledge_base/r/project-templates)\n\n[Liferay's Maven Archetypes](/docs/7-2/reference/-/knowledge_base/r/maven)\n\n[Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli)\n\n[Liferay @ide@](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio)\n\n[Semantic Versioning](/docs/7-2/customization/-/knowledge_base/c/semantic-versioning)\n"
  },
  {
    "path": "en/developer/customization/articles/02-fundamentals/06-semantic-versioning/01-semantic-versioning-intro.markdown",
    "content": "---\nheader-id: semantic-versioning\n---\n\n# Semantic Versioning\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/liferay-internals/fundamentals/semantic-versioning.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[Semantic Versioning](https://semver.org)\nis a three tiered versioning system that increments version numbers based on the\ntype of API change introduced to a releasable software component. It's a\nstandard way of communicating programmatic compatibility of a package or module\nfor dependent consumers and API implementations. If a package is\nprogrammatically (i.e., semantically) incompatible with a project,\n[bnd](http://bnd.bndtools.org)\n(used when building\n[Liferay generated module projects](/docs/7-2/reference/-/knowledge_base/r/creating-a-project))\nfails that project's build immediately.\n\nThe semantic version format looks like this:\n\n    MAJOR.MINOR.MICRO\n\nCertain events force each tier to increment:\n\n- *MAJOR:* an incompatible, API-breaking change is made\n- *MINOR:* a change that affects only providers of the API, or new backwards-\n  compatible functionality is added\n- *MICRO:* a backwards-compatible bug fix is made\n\nFor more details on semantic versioning, see the official\n[Semantic Versioning](https://semver.org/)\nsite and\n[OSGi Alliance's Semantic Versioning](http://www.osgi.org/wp-content/uploads/SemanticVersioning1.pdf)\ntechnical whitepaper.\n\nAll of @product@'s modules use Semantic Versioning.\n\nFollowing Semantic Versioning is especially important because @product@ is\na modular platform containing hundreds of independent OSGi modules. With many\nindependent modules containing a slew of dependencies, releasing new package\nversions can quickly become terrifying. With this complex intertwined system of\ndependencies, you must meticulously manage your own project's API versions to\nensure compatibility for those who leverage it. With Semantic Versioning's\nstraightforward system and the help of\n[Liferay tooling](/docs/7-2/reference/-/knowledge_base/r/tooling),\nmanaging your module project's versions is easy.\n\n## Baselining Your Project\n\nFollowing Semantic Versioning manually seems deceptively easy. There's a sad\nhistory of good-intentioned developers updating their projects' semantic\nversions manually, only to find out later they made a mistake. The truth is,\nit's hard to anticipate the ramifications of a simple update. To avoid this, you\ncan *baseline* your project after it has been updated. Baselining verifies that\nthe Semantic Versioning rules are obeyed by your project. This can catch many\nobvious API changes that are not so obvious to humans. Care must always be\ntaken, however, when making any kind of code change because this tool is not\nsmart enough to identify compatibility changes not represented in the signatures\nof Java classes or interfaces, or in API *use* changes (e.g., assumptions about\nmethod call order, or changes to input and/or output encoding). Baseline, as the\nname implies, does give you a certain measure of *baseline* comfort that a large\nclass of compatibility issues won't sneak past you.\n\nYou can use Liferay's Baseline Gradle plugin to provide baselining capabilities.\nAdd it to your Gradle build configuration and execute the following command:\n\n    ./gradlew baseline\n\nSee the\n[Baseline Gradle Plugin](/docs/7-2/reference/-/knowledge_base/r/baseline-gradle-plugin)\narticle for configuration details. This plugin is not provided in\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)\nby default.\n\nWhen you run the `baseline` command, the plugin baselines your new module\nagainst the latest released non-snapshot module (i.e., the baseline). That is,\nit compares the public exported API of your new module with the baseline. If\nthere are any changes, it uses the OSGi Semantic Versioning rules to calculate\nthe minimum new version. If your new module has a lower version, errors are\nthrown.\n\nWith baselining, your project's Semantic Versioning is as accurate as its API\nexpresses.\n\n## Managing Artifact and Dependency Versions\n\nThere are two ways to track your project's artifact and dependency versions with\nSemantic Versioning:\n\n- Range of versions\n- Exact version (one-to-one)\n\nYou should track a range of versions if you intend to build your project for\nmultiple versions of @product@ and maintain maximum compatibility. In other\nwords, if several versions of a package work for an app, you can configure the\napp to use any of them. What's more, bnd automatically determines the\nsemantically compatible range of each package a module depends on and records\nthe range to the module's manifest.\n\nFor help with version range syntax, see the\n[OSGi Specifications](https://osgi.org/specification/osgi.core/7.0.0/framework.module.html#i3189032).\n\nA version range for imported packages in an OSGi bundle's `bnd.bnd` looks like\nthis:\n\n    Import-Package: com.liferay.docs.test; version=\"[1.0.0,2.0.0)\"\n\nPopular build tools also follow this syntax. In Gradle, a version range for\na dependency looks like this:\n\n    compile group: \"com.liferay.portal\", name: \"com.liferay.portal.test\", version: \"[1.0.0,2.0.0)\"\n\nIn Maven, it looks like this:\n\n```xml\n<groupId>com.liferay.portal</groupId>\n<artifactId>com.liferay.portal.test</artifactId>\n<version>[1.0.0,2.0.0)</version>\n```\n\nSpecifying the latest release version can also be considered a range of versions\nwith no upper limit. For example, in Gradle, it's specified as `version:\n\"latest.release\"`. This can be done in Maven 2.x with the usage of the version\nmarker `RELEASE`. This is not possible if you're using Maven 3.x. See\n[Gradle](https://gradle.org/docs)\nand\n[Maven](http://maven.apache.org/guides/)'s\nrespective docs for more information.\n\nTracking a range of versions comes with a price. It's hard to reproduce old\nbuilds when you're debugging an issue. It also comes with the risk of differing\nbehaviors depending on the version used. Also, relying on the latest release\ncould break compatibility with your project if a major change is introduced. You\nshould proceed with caution when specifying a range of versions and ensure your\nproject is tested on all included versions.\n\nTracking a dependency's exact version is much safer, but is less flexible. This\nmight limit you to a specific version of @product@. You would also be locked in\nto APIs that only exist for that specific version. This means your module is\nmuch easier to test and has less chance for unexpected failures.\n\n| **Note:** When specifying package versions in your `bnd.bnd` file, exact\n| versions are typically specified like this: `version=\"1.1.2\"`. However, this\n| syntax is technically a range; it is interpreted as [1.1.2, &#8734;).\n| Therefore, if a higher version of the package is available, it's used instead\n| of the version you specified. For these cases, it may be better to specify a\n| version range for compatible versions that have been tested. If you want to\n| specify a true exact match, the syntax is like this: `[1.1.2]`. See the\n| [Version Range](https://osgi.org/specification/osgi.core/7.0.0/framework.module.html#i3189032)\n| section in the OSGi specifications for more info.\n|\n| Gradle and Maven use exact versions when only one version is specified.\n\nYou now know the pros and cons for tracking dependencies as a range and as an\nexact match.\n\n## Related Topics\n\n[Importing Packages](/docs/7-2/customization/-/knowledge_base/c/importing-packages)\n\n[Exporting Packages](/docs/7-2/customization/-/knowledge_base/c/exporting-packages)\n\n[Configuring Dependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies)\n"
  },
  {
    "path": "en/developer/customization/articles/02-fundamentals/07-deploying-wars-wab-generator/01-deploying-wars-wab-generator-intro.markdown",
    "content": "---\nheader-id: deploying-wars-wab-generator\n---\n\n# Deploying WARs (WAB Generator)\n\n[TOC levels=1-4]\n\nYou can create applications for @product@ as Java EE-style Web Application\nARchive (WAR) artifacts or as Java ARchive (JAR) OSGi bundle artifacts. Bean\nPortlets, PortletMVC4Spring Portlets, and JSF Portlets must be packaged as WAR\nartifacts because their frameworks are designed for Java EE. Therefore, they\nexpect a WAR layout and require Java EE resources such as the `WEB-INF/web.xml`\ndescriptor.\n\nLiferay provides a way for these WAR-styled plugins to be deployed and treated\nlike OSGi modules by Liferay's OSGi runtime. They can be converted to *WABs*.\n\n@product@ supports the OSGi Web Application Bundle (WAB) standard for deployment\nof Java EE style WARs. Simply put, a WAB is an archive that has a WAR layout and\ncontains a `META-INF/MANIFEST.MF` file with the `Bundle-SymbolicName` OSGi\ndirective. A WAB is an OSGi bundle. Although the project source has a WAR\nlayout, the artifact filename may end with either the `.jar` or `.war`\nextension.\n\nLiferay only supports the use of WABs that have been auto-generated by the WAB\nGenerator. The WAB Generator transforms a traditional WAR-style plugin into a\nWAB during deployment. So what exactly does the WAB Generator do to a WAR file\nto transform it into a WAB?\n\nThe WAB Generator detects packages referenced in the plugin WAR's JSPs,\ndescriptor files, and classes (in `WEB-INF/classes` and embedded JARs). The\ndescriptor files include `web.xml`, `liferay-web.xml`, `portlet.xml`,\n`liferay-portlet.xml`, and `liferay-hook.xml`. The WAB Generator verifies\nwhether the detected packages are in the plugin's `WEB-INF/classes` folder or in\nan embedded JAR in the `WEB-INF/lib` folder. Packages that aren't found in\neither location are added to an `Import-Package` OSGi header in the WAB's\n`META-INF/MANIFEST.MF` file. \n\nTo import a package that is only referenced in the following types of locations,\nyou must add an `Import-Package` OSGi header to the plugin's\n`WEB-INF/liferay-plugin-package.properties` file and add the package to that\nheader's list of values. \n\n-   Unrecognized descriptor file\n-   Custom or unrecognized descriptor element or attribute\n-   Reflection code\n-   Class loader code\n\n## WAR versus WAB Structure \n\nThe WAB folder structure and WAR folder structure differ. Consider the following\nfolder structure of a WAR-style portlet.\n\n**WAR**\n\n-   `my-war-portlet`\n    -   `src`\n        -   `main`\n            -   `java`\n            -   `webapp`\n                -   `WEB-INF`\n                    -   `classes`\n                    -   `lib`\n                    -   `resources`\n                    -   `views`\n                    -   `liferay-display.xml`\n                    -   `liferay-plugin-package.properties`\n                    -   `liferay-portlet.xml`\n                    -   `portlet.xml`\n                    -   `web.xml`\n\nWhen a WAR-style portlet is deployed to @product@ and processed by the WAB\nGenerator, the portlet's folder structure is transformed. \n\n**WAB**\n\n-   `my-war-portlet-that-is-now-a-wab`\n    -   `META-INF`\n        -   `MANIFEST.MF`\n    -   `WEB-INF`\n        -   `classes`\n        -   `lib`\n        -   `resources`\n        -   `views`\n        -   `liferay-display.xml`\n        -   `liferay-plugin-package.properties`\n        -   `liferay-portlet.xml`\n        -   `portlet.xml`\n        -   `web.xml`\n\nThe major difference is the addition of the `META-INF/MANIFEST.MF` file. The WAB\nGenerator automatically generates an OSGi-ready manifest file. If you want to\naffect the content of the manifest file, you can place bnd directives and OSGi\nheaders directly into your plugin's `liferay-plugin-package.properties` file.\n\n| **Note:** Adding a `bnd.bnd` file or a build-time plugin (e.g.,\n| `bnd-maven-plugin`) to your WAR plugin is pointless, because the generated WAB\n| cannot use them. \n\n## Deploying a WAR \n\nTo deploy a WAB based on your WAR plugin, copy your WAR plugin to your @product@\ninstance's `deploy/` folder in your  [`[Liferay\nHome]`](/docs/7-2/deploy/-/knowledge_base/d/liferay-home). \n\n## Saving a Copy of the WAB\n\nOptionally, save the WAB to a local folder. This gives you the opportunity to\ninspect the generated WAB. To store generated WABs, add the following portal\nproperties to a `[Liferay Home]/portal-ext.properties` file. Then restart\n@product@:\n\n```properties\nmodule.framework.web.generator.generated.wabs.store=true\nmodule.framework.web.generator.generated.wabs.store.dir=${module.framework.base.dir}/wabs\n```\n\nThese properties instruct the WAB generator to store generated WABs in your\nLiferay instance's `osgi/wabs/` folder. The generated WABs have the same\nstructure as the example WAB structure listed above. The\n[Module Framework Web Application Bundles](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Module%20Framework%20Web%20Application%20Bundles)\nproperties section explains more details. \n\nAwesome! You have deployed your WAR plugin as a WAB and you know how to save a\ncopy of the WAB to examine it!\n\n## Related Topics \n\n[Creating a Project](/docs/7-2/reference/-/knowledge_base/r/creating-a-project)\n\n[Deploying a Project](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project)\n\n[Developing Web Front-Ends](/docs/7-2/appdev/-/knowledge_base/a/web-front-ends)\n"
  },
  {
    "path": "en/developer/customization/articles/03-architecture/01-architecture-intro.markdown",
    "content": "---\nheader-id: architecture\n---\n\n# Architecture\n\n[TOC levels=1-4]\n\n@product@ architecture comprises these parts:\n\n**Core:** Bootstraps @product@ and its frameworks. The Core provides a runtime\nenvironment for managing services, UI components, and customizations.\n\n**Services:** Liferay and custom functionality is exposed via Java APIs and web\nAPIs.\n\n**UI:** The optional web application UI for adding portals, sites, pages,\nwidgets, and content.\n\nYou can use the @product@ UI and services together or focus solely on using\nservices via\n[REST web APIs](/docs/7-2/frameworks/-/knowledge_base/f/headless-rest-apis).\n\n![Figure 1: @product@ portals and Sites contain content and widgets. @product@ can also be used \"headless\"---without the UI.](../../images/architecture-options.png)\n\nThe architecture satisfies these requirements:\n\n- Supports using common development technologies\n\n- Leverages development standards\n\n- Facilitates swapping components\n\n- Starts fast and performs well\n\n- Its runtime is easy to configure and inspect\n\nThe Core supports UI and service deployments and orchestrates wiring them\ntogether.\n\n## Core\n\n@product@ is a web application that runs on your application server. The Core\nbootstraps the application and\n[Liferay's built-in frameworks](/docs/7-2/frameworks/-/knowledge_base/f/frameworks).\n\nThere are frameworks for these things and more:\n\n- [Adaptive Media](/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media)\n- [Application Configuration](/docs/7-2/frameworks/-/knowledge_base/f/configurable-applications)\n- [Application Security](/docs/7-2/frameworks/-/knowledge_base/f/application-security)\n- [Asset Framework](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework)\n- [File Management](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api)\n- [Localization](/docs/7-2/frameworks/-/knowledge_base/f/localization)\n- [Search](/docs/7-2/frameworks/-/knowledge_base/f/search)\n- [Segmentation and Personalization](/docs/7-2/frameworks/-/knowledge_base/f/segmentation-personalization)\n- [Upgrade Processes](/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes)\n- [Web Fragments](/docs/7-2/frameworks/-/knowledge_base/f/page-fragments)\n- [Workflow](/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework)\n\nThe Core provides the component runtime environment for the frameworks,\nservices, and UI. Here are some component examples:\n\n-   [Services](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n-   [Service customizations](/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers)\n-   [Portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets)\n    (templates, controllers, and resources)\n-   [JavaScript applications](/docs/7-2/appdev/-/knowledge_base/a/web-front-ends)\n    (templates, routers, and resources)\n-   [JSP customization via Portlet Filters](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters)\n-   [Theme]((/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction))\n-   [Shared Language Keys](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-language-module)\n-   [Navigation components](/docs/7-2/frameworks/-/knowledge_base/f/screen-navigation-framework)\n\nThe following figure shows these component types in the runtime environment.\n\n![Figure 2: The Core provides a runtime environment for components, such as the ones here. New component implementations can extend or replace existing implementations dynamically.](../../images/component-runtime-environment.png)\n\nThe runtime environment supports adding, replacing, and customizing components\non-the-fly. This makes the following scenarios possible:\n\n**Replacement:** If the `ServiceC Impl 2` component has a higher ranking than\nexisting component `ServiceC Impl 1`, `ServiceC Impl 2` is used in its place.\n\n**Customization:** The `PortletA Filter` intercepts and modifies requests to and\nresponses from `PortletA`, affecting the content `PortletA` displays.\n\nComponent WAR and module JAR projects install as\n[OSGi bundles](https://www.osgi.org/)\n(modules). @product@'s OSGi framework defines the module lifecycle, enforces\ndependencies, defines the class loading structure, and provides an API and CLI\n([Felix Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell))\nfor managing modules and components. The Core is configured via\n[portal properties files](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\nand\n[Server Administration panels](/docs/7-2/user/-/knowledge_base/u/server-administration).\n\nThe service components provide business functionality.\n\n## Services\n\nBusiness logic is implemented in services deployed to the component runtime\nenvironment. Built-in Core services and framework services operate on Liferay\nmodels such as Users, Roles, Web Content, Documents and Media, and more. You can\nwrite and deploy custom services to introduce new models and functionality.\nService components can access each other in @product@ via\n[dependency injection](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services).\n\nFront-end applications invoke the services to do work. You can deploy Java-based\napplications that call services directly using the\n[Java APIs](/docs/7-2/reference/-/knowledge_base/r/java-apis), and\nany web-based (Java and non-Java) application, whether deployed on @product@ or\nnot, can use the web APIs, which include\n[headless REST APIs](/docs/7-2/appdev/-/knowledge_base/a/generating-apis-with-rest-builder)\nthat conform to the\n[OpenAPI](https://swagger.io/docs/specification/about/)\nstandard and include\n[plain web/REST services](/docs/7-2/frameworks/-/knowledge_base/f/web-services).\nThe following figure shows @product@ applications and external clients invoking\nLiferay services.\n\n![Figure 3: Remote and @product@ applications can invoke services via REST web APIs. @product@ Java-based portlets can also invoke services via Java APIs.](../../images/apps-invoking-services.png)\n\nLiferay services are built using\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\nand made REST-ful using\n[REST Builder](/docs/7-2/appdev/-/knowledge_base/a/rest-builder).\nThe services are easy to\n[override and extend](/docs/7-2/customization/-/knowledge_base/c/overriding-osgi-services)\ntoo.\n\n@product@ also provides a web-based UI, which makes content and service\nfunctionality available in browsers.\n\n## UI\n\n[@product@'s UI](/docs/7-2/user/-/knowledge_base/u/the-liferay-distinction)\nhelps people do work,\n[collaborate](/docs/7-2/user/-/knowledge_base/u/collaboration),\nand\n[enjoy content](/docs/7-2/user/-/knowledge_base/u/web-experience-management).\nThe UI consists of\n\n-   [@product@ application](/docs/7-2/user/-/knowledge_base/u/the-liferay-distinction):\n    The web application for managing Portals, Sites, Users, Pages, Widgets, and\n    more. \n\n-   [Applications](/docs/7-2/appdev/-/knowledge_base/a/application-development):\n    Widgets that provide a user interface for services already deployed. \n\n-   [Themes](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction):\n    Plugins for styling Sites with a unique look and feel.\n\nThe UI concepts article digs deeper into developing and customizing UI\ncomponents.\n\nAs you can see, the @product@ architecture supports developing services, UI\ncomponents, and customizations. The architecture section covers Core, service,\nand UI topics. Next, we dive into the Core to describe class loading,\nmodularity, and more. But you can jump ahead to any service or UI architecture\ntopics, if you like. Enjoy exploring the @product@ architecture!\n"
  },
  {
    "path": "en/developer/customization/articles/03-architecture/02-classloader-hierarchy.markdown",
    "content": "---\nheader-id: liferay-portal-classloader-hierarchy\n---\n\n# Liferay Portal Classloader Hierarchy\n\n[TOC levels=1-4]\n\nAll @product@ applications live in its OSGi container. Portal is a web \napplication deployed on your application server. Portal's Module Framework \nbundles (modules) live in the OSGi container and have classloaders. All the \nclassloaders from Java's Bootstrap classloader to classloaders for bundle \nclasses and JSPs are part of a hierarchy. \n\nThis article explains Liferay's classloader hierarchy and describes how it works \nin the following contexts: \n\n-   Web application, such as Liferay Portal, deployed on the app server\n-   OSGi bundle deployed in the Module Framework\n\nThe following diagram shows @product@'s classloader hierarchy.\n\n![Figure 1.0: Here is Liferay's classloader hierarchy.](../../images/portal-classloader-hierarchy.png)\n\nHere are the classloader descriptions:\n\n-   **Bootstrap**: The JRE's classes (from packages `java.*`) and Java \n    extension classes (from `$JAVA_HOME/lib/ext`). No matter the context, \n    loading all `java.*` classes is delegated to the Bootstrap classloader. \n\n-   **System**: Classes configured on the `CLASSPATH` and or passed in via the \n    application server's Java classpath (`-cp` or `-classpath`) parameter. \n\n-   **Common**: Classes accessible globally to web applications on the \n    application server. \n\n-   **Web Application**: Classes in the application's `WEB-INF/classes` folder \n    and `WEB-INF/lib/*.jar`. \n\n-   **Module Framework**: Liferay's OSGi module framework classloader which \n    is used to provide controlled isolation for the module framework bundles. \n\n-   **bundle**: Classes from a bundle's packages or from packages other \n    bundles export. \n\n-   **JSP**: A classloader that aggregates the following bundle and \n    classloaders:\n    -   Bundle that contains the JSPs' classloader\n    -   JSP servlet bundle's classloader\n    -   Javax Expression Language (EL) implementation bundle's classloader\n    -   Javax JSTL implementation bundle's classloader\n\n-   **Service Builder**: Service Builder classes\n\nThe classloader used depends on context. Classloading rules vary between \napplication servers. Classloading in web applications and OSGi bundles differs \ntoo. In all contexts, however, the Bootstrap classloader loads classes from \n`java.*` packages. \n\nClassloading from a web application perspective is up next. \n\n## Web Application Classloading Perspective\n\nApplication servers dictate where and in what order web applications, such as \n@product@, search for classes and resources. Application servers such as \n[Apache Tomcat](https://tomcat.apache.org/tomcat-9.0-doc/class-loader-howto.html) \nenforce the following default search order:\n\n1.  Bootstrap classes\n2.  Web app's `WEB-INF/classes`\n3.  web app's `WEB-INF/lib/*.jar`\n4.  System classloader\n5.  Common classloader\n\nFirst, the web application searches Bootstrap. If the class/resource isn't \nthere, the web application searches its own classes and JARs. If the \nclass/resource still isn't found, it checks the System classloader and then \nCommon classloader.  Except for the web application checking its own classes and \nJARs, it searches the hierarchy in parent-first order. \n\nApplication servers such as \n[Oracle WebLogic](https://docs.oracle.com/cd/E19501-01/819-3659/beadf/index.html) \nand IBM WebSphere have additional classloaders. They may also have a \ndifferent classloader hierarchy and search order. Consult your application \nserver's documentation for classloading details. \n\n## Other Classloading Perspectives\n\n[Bundle Classloading Flow](/docs/7-2/customization/-/knowledge_base/c/bundle-classloading-flow) \nexplains classloading from an OSGi bundle perspective. \n\nClassloading for JSPs and Service Builder classes is similar to that of web \napplications and OSGi bundle classes. \n\nYou now know @product@'s classloading hierarchy, understand it in context of web \napplications, and have references to information on other classloading \nperspectives. \n\n## Related Topics\n\n[Bundle Classloading Flow](/docs/7-2/customization/-/knowledge_base/c/bundle-classloading-flow)\n"
  },
  {
    "path": "en/developer/customization/articles/03-architecture/03-portal-startup-phases.markdown",
    "content": "---\nheader-id: liferay-startup-phases\n---\n\n# @product@ Startup Phases\n\n[TOC levels=1-4]\n\nKnowing Liferay's startup phases helps you troubleshoot startup failures. By \nlearning the phase triggered events, you can listen for phases and act on them. \nThis article describes the startup phases and identifies how to \n[implement actions for phase events](#acting-on-events). \n\nStartup consists of these main phases:\n\n1.  **Portal Context Initialization Phase:** focuses on low level tasks without \n    a web context. \n\n2.  **Main Servlet Initialization Phase:** focuses on the portlet container and \n    the @product@ web application's UI features such as Struts, Themes, and \n    more. \n\nThe Portal Context Initialization Phase sets the stage for the Main Servlet \nInitialization Phase. \n\n### Portal Context Initialization Phase\n\nThe Portal Context Initialization phase runs first with these tasks: \n\n1.  Set up low level utilities such as logging and those in \n    [`PortalUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/PortalUtil.html) \n    and \n    [`InitUtil`](@platform-ref@/7.2-latest/javadocs/portal-impl/com/liferay/portal/util/InitUtil.html). \n\n2.  OSGi framework is initialized. \n\n3.  Spring Phase 1: INFRASTRUCTURE beans specified by the Spring context files \n    listed in Portal property \n    [`spring.infrastructure.configs`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Spring) \n    are loaded. \n\n4.  INFRASTRUCTURE beans are published as \n    [OSGi services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services). \n\n5.  OSGi framework starts. \n\n    1.  Static bundles are installed and started. \n    2.  Dynamic bundles are started. \n\n6.  OSGi framework starts the runtime. \n\n7.  Spring Phase 2: MAIN\n\n    1.  Load Spring beans specified by the Spring context files listed in Portal \n        property \n        [`spring.configs`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Spring). \n    2.  A \n        [`ModuleServiceLifecycle` event service](#moduleservicelifecycle-events) \n        with a service property `module.service.lifecycle` value \n        `spring.initialized` (i.e., \n        [`SPRING_INITIALIZED`](@platform-ref@/7.2-latest/javadocs/portal-kernel/constant-values.html#com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle.SPRING_INITIALIZED))\n        registers. \n\n8.  MAIN Spring beans are published as \n    [OSGi services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services). \n\n### Main Servlet Initialization Phase\n\nHere's the phase's activity sequence:\n\n1.  The \n    [`ModuleServiceLifecycle` event service](#moduleservicelifecycle-events) \n    is updated with the service property `module.service.lifecycle` value \n    `database.initialized` (i.e., \n    [`DATABASE_INITIALIZED`](@platform-ref@/7.2-latest/javadocs/portal-kernel/constant-values.html#com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle.DATABASE_INITIALIZED)). \n\n2.  The \n    [Global Startup event](#portal-startup-events) \n    fires. \n\n3.  For each portal instance, the \n    [Application Startup events](#portal-startup-events) \n    fire. \n\n4.  The \n    [`ModuleServiceLifecycle` event service](#moduleservicelifecycle-events) \n    is updated with the service property `module.service.lifecycle` value \n    `portal.initialized` (i.e., \n    [`PORTAL_INITIALIZED`](@platform-ref@/7.2-latest/javadocs/portal-kernel/constant-values.html#com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle.PORTAL_INITIALIZED)).\n\nNow that you're acquainted with the startup phases, you can concentrate on the \nevents they fire. \n\n## Acting on Events\n\nThe ways to act on events depends on the event type. These subsections describe \nthe event types. \n\n### ModuleServiceLifecycle Events\n\n[You can wait for and act on `ModuleServiceLifecycle` event services.](/docs/7-2/customization/-/knowledge_base/c/waiting-on-lifecycle-events)\n\n### Portal Startup Events\n\nIn your `liferay-portal-ext.properties` file, you can override the following \nproperties and add your own \n[`LifecycleAction`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/events/LifecycleAction.html) \nclasses to the list of action classes to invoke on the events. \n\n**Global Startup Event** runs once when @product@ initializes. The \n[`global.startup.events` property](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Startup%20Events) \ndefines the event's default actions. \n\n**Application Startup Events** runs once for each Site instance @product@ \ninitializes. The \n[`application.startup.events` property](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Startup%20Events) \ndefines the event's default actions. \n\n## Related Topics\n\n[Waiting on Lifecycle Events](/docs/7-2/customization/-/knowledge_base/c/waiting-on-lifecycle-events)\n\n[OSGi Services and Dependency Injection with Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services)\n"
  },
  {
    "path": "en/developer/customization/articles/03-architecture/04-benefits-of-modularity.markdown",
    "content": "---\nheader-id: the-benefits-of-modularity\n---\n\n# The Benefits of Modularity\n\n[TOC levels=1-4]\n\nDictionary.com defines \n[modularity](http://www.dictionary.com/browse/modularity)\nas *the use of individually distinct functional units, as in assembling an \nelectronic or mechanical system.* The distinct functional units are called \n*modules*. \n\nNASA's Apollo spacecraft, for example, comprised three modules, each with a \ndistinct function: \n\n- *Lunar Module*: Carried astronauts from the Apollo spacecraft to the \n    moon's surface and back. \n- *Service Module*: Provided fuel for propulsion, air conditioning, and \n    water. \n- *Command Module*: Housed the astronauts and communication and navigation \n    controls. \n\n![Figure 1: The Apollo spacecraft's modules collectively took astronauts to the moon's surface and back to Earth.](../../images/modularity_apollo_spacecraft_diagram.png)\n\nThe spacecraft and its modules exemplified these modularity characteristics: \n\n-   **Distinct functionality**: Each module provides a distinct function \n    (purpose); modules can be combined to provide an entirely new collective function. \n\n    The Apollo spacecraft's modules were grouped together for a distinct \n    collective function: take astronauts from the Earth's atmospheric rim, to \n    the moon's surface, and back to Earth. The previous list identifies each \n    module's distinct function. \n\n-   **Dependencies**: Modules can require capabilities other modules satisfy. \n\n    The Apollo modules had these dependencies:\n\n    -   Lunar Module depended on the Service Module to get near the moon.\n\n    -   Command Module depended on the Service Module for power  and oxygen.\n \n    -   Service Module depended on the Command Module for instruction.\n\n-   **Encapsulation**: Modules hide their implementation details but publicly \n    define their capabilities and interfaces. \n\n    Each Apollo module was commissioned with a contract defining its \n    capabilities and interface, while each module's details were encapsulated \n    (hidden) from other modules. NASA integrated the modules based on their \n    interfaces. \n\n-   **Reusability**: A module can be applied to different scenarios.\n\n    The Command Module's structure and design were reusable. NASA used different \n    versions of the Command Module, for example,  throughout the Apollo program, \n    and in the Gemini Program, which focused on Earth orbit. \n\nNASA used modularity to successfully complete over a dozen missions to the moon. \nCan modularity benefit software too? Yes! The following sections show you how:\n\n- [Modularity benefits for software](#modularity-benefits-for-software)\n- [Example: How to design a modular application](#example-designing-a-modular-application)\n\n## Modularity Benefits for Software\n\nJava applications have predominantly been monolithic: they're developed in large \ncode bases. In a monolith, it's difficult to avoid tight coupling of classes. \nModular application design, conversely, facilitates loose coupling, making the \ncode easier to maintain. It's much easier and more fun to develop small amounts \nof cohesive code in modules. Here are some key benefits of developing modular \nsoftware. \n\n### Distinct Functionality\n\nIt's natural to focus on developing one piece of software at a time. In a \nmodule, you work on a small set of classes to define and implement the module's \nfunction. Keeping scope small facilitates writing high quality, elegant code. \nThe more cohesive the code, the easier it is to test, debug, and maintain. \nModules can be combined to provide a new function, distinguishable from each \nmodule's function. \n\n### Encapsulation\n\nA module encapsulates a function (capability). Module implementations are hidden \nfrom consumers, so you can create and modify them as you like. Throughout a \nmodule's lifetime, you can fix and improve the implementation or swap in an \nentirely new one. You make the changes behind the scenes, transparent to \nconsumers. A module's contract defines its capability and interface, making the \nmodule easy to understand and use. \n\n### Dependencies\n\nModules have requirements and capabilities. The interaction between modules is a \nfunction of the capability of one satisfying the requirement of another and so \non. Modules are published to artifact repositories, such as Maven Central. \nModule versioning schemes let you specify dependencies on particular module \nversions or version ranges. \n\n### Reusability\n\nModules that do their job well are hot commodities. They're reusable across \nprojects, for different purposes. As you discover helpful reliable modules, \nyou'll use them again and again. \n\nIt's time to design a modular application. \n\n## Example: Designing a Modular Application\n\nApplication design often starts out simple but gets more complex as you \ndetermine capabilities the application requires. If a third party library \nalready provides the capability, you can \n[deploy it with your app](/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module). \nYou can otherwise implement the capability yourself. \n\nAs you design various aspects of your app to support its function, you must \ndecide how those aspects fit into the code base. Putting them in a single \nmonolithic code base often leads to tight coupling, while designating separate \nmodules for each aspect fosters loose coupling. Adopting a modular approach to \napplication design lets you reap the modularity benefits. \n\nFor example, you can apply modular design to a speech recognition app. Here are \nthe app's function and required capabilities:\n\n*Function*: interface with users to translate their speech into text for the \n  computer to understand. \n\n*Required capabilities*:\n\n- Translates user words to text \n- Uses a selected computer voice to speak to users. \n- Interacts with users based on a script of instructions that include\n    questions, commands, requests, and confirmations. \n\nYou could create modules to provide the required capabilities:\n\n- *Speech to text*: Translates spoken words to text the computer understands. \n- *Voice UI*: Interacts with users based on stored questions, commands, and \n    confirmations. \n- *Instruction manager*: Stores and provides the application's questions, \n    commands, and confirmations. \n- *Computer voice*: Stores and provides computer voices for users to choose \n    from. \n\nThe following diagram contrasts a monolithic design for the speech recognition \napplication with a modular design. \n\n![Figure 2: The speech recognition application can be implemented in a single monolithic code base or in modules, each focused on a particular function.](../../images/modularity-benefits-application-design-example.png)\n\nDesigning the app as a monolith lumps everything together. There are no initial \nboundaries between the application aspects, whereas the modular design \ndistinguishes the aspects. \n\nDevelopers can create the modules in parallel, each one with its own particular \ncapability. Designing applications that comprise modules fosters writing \ncohesive pieces of code that represent capabilities. Each module's capability \ncan potentially be *reused* in other scenarios too. \n\nFor example, the *Instruction manager* and *Computer voice* modules can be \n*reused* by a navigation app. \n\n![Figure 3: The *Instruction manager* and *Computer voice* modules designed for the speech recognition app can be used (or *reused*) by a navigation app.](../../images/modularity-benefits-module-reuse.png)\n\nHere are the benefits of designing the speech recognition app as modules:\n\n- Each module represents a capability that contributes to the app's overall \n    function. \n- The app depends on modules, that are easy to develop, test, and maintain. \n- The modules can be reused in different applications. \n\nIn conclusion, modularity has literally taken us to the moon and back. It \nbenefits software development too. The example speech recognition application \ndemonstrated how to design an app that comprises modules. \n\nNext you'll learn how OSGi facilitates creating modules that provide and consume \nservices. \n"
  },
  {
    "path": "en/developer/customization/articles/03-architecture/05-osgi-and-modularity.markdown",
    "content": "---\nheader-id: osgi-and-modularity\n---\n\n# OSGi and Modularity\n\n[TOC levels=1-4]\n\nModularity makes writing software, especially as a team, fun! Here are some \nbenefits to modular development on DXP: \n\n- @product@'s runtime framework is lightweight, fast, and secure. \n- The framework uses the OSGi standard. If you have experience using OSGi with \n  other projects, you can apply your existing knowledge to developing on DXP. \n- Modules publish services to and consume services from a service registry. \n  Service contracts are loosely coupled from service providers and consumers, \n  and the registry manages the contracts automatically. \n- Modules' dependencies are managed automatically by the container, dynamically \n  (no restart required). \n- The container manages module life cycles dynamically. Modules can be \n  installed, started, updated, stopped, and uninstalled while Liferay is \n  running, making deployment a snap. \n- Only a module's classes whose packages are explicitly exported are publicly \n  visible; OSGi hides all other classes by default. \n- Modules and packages are semantically versioned and declare dependencies on \n  specific versions of other packages. This allows two applications that \n  depend on different versions of the same packages to each depend on their \n  own versions of the packages. \n- Team members can develop, test, and improve modules in parallel. \n- You can use your existing developer tools and environment to develop modules. \n\nThere are many benefits to modular software development with OSGi, and we can \nonly scratch the surface here. Once you start developing modules, you might find \nit hard to go back to developing any other way. \n\n## Modules\n\nIt's time to see what module projects look like and see @product@'s modular \ndevelopment features in action. To keep things simple, only project code and \nstructure are shown: you can \n[create modules](/docs/7-2/reference/-/knowledge_base/r/creating-a-project) \nlike these anytime. \n\nThese modules collectively provide a command that takes a String and uses it in \na greeting. Consider it \"Hello World\" for modules. \n\n### API\n\nThe API module is first. It defines the contract that a provider implements and \na consumer uses. Here is its structure: \n\n- `greeting-api`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/greeting/api`\n                    - `Greeting.java`\n    - `bnd.bnd`\n    - `build.gradle`\n\nVery simple, right? Beyond the Java source file, there are only two other files:\na Gradle build script (though you can use any build system you want), and a \nconfiguration file called `bnd.bnd`. The `bnd.bnd` file describes and configures \nthe module: \n\n```properties\nBundle-Name: Greeting API\nBundle-SymbolicName: com.liferay.docs.greeting.api\nBundle-Version: 1.0.0\nExport-Package: com.liferay.docs.greeting.api\n```\n\nThe module's name is *Greeting API*. Its symbolic name--a name that ensures \nuniqueness--is `com.liferay.docs.greeting.api`. Its semantic version is declared \nnext, and its package is *exported*, which means it's made available to other \nmodules. This module's package is just an API other modules can implement. \n\nFinally, there's the Java class, which in this case is an interface: \n\n```java\npackage com.liferay.docs.greeting.api;\n\nimport aQute.bnd.annotation.ProviderType;\n\n@ProviderType\npublic interface Greeting {\n\n        public void greet(String name);\n\n}\n```\n\nThe interface's `@ProviderType` annotation tells the service registry that \nanything implementing the interface is a provider. The interface's one method \nasks for a `String` and doesn't return anything. \n\nThat's it! As you can see, creating modules is not very different from creating \nother Java projects. \n\n### Provider\n\nAn interface only defines an API; to do something, it must be implemented. This \nis what the provider module is for. Here's what a provider module for the \nGreeting API looks like: \n\n- `greeting-impl`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/greeting/impl`\n                    - `GreetingImpl.java`\n    - `bnd.bnd`\n    - `build.gradle`\n\nIt has the same structure as the API module: a build script, a `bnd.bnd` \nconfiguration file, and an implementation class. The only differences are the \nfile contents. The `bnd.bnd` file is a little different: \n\n```properties\nBundle-Name: Greeting Impl\nBundle-SymbolicName: com.liferay.docs.greeting.impl\nBundle-Version: 1.0.0\n```\n\nThe bundle name, symbolic name, and version are all set similarly to the API. \n\nFinally, there's no `Export-Package` declaration. A client (which is the third \nmodule you'll create) just wants to use the API: it doesn't care how its \nimplementation works as long as the API returns what it's supposed to return. \nThe client, then, only needs to declare a dependency on the API; the service \nregistry injects the appropriate implementation at runtime. \n\nPretty cool, eh? \n\nAll that's left, then, is the class that provides the implementation: \n\n```java\npackage com.liferay.docs.greeting.impl;\n\nimport com.liferay.docs.greeting.api.Greeting;\n\nimport org.osgi.service.component.annotations.Component;\n\n@Component(\n    immediate = true,\n    property = {\n    },\n    service = Greeting.class\n)\npublic class GreetingImpl implements Greeting {\n\n    @Override\n    public void greet(String name) {\n        System.out.println(\"Hello \" + name + \"!\");\n    }\n\n}\n```\n\nThe implementation is simple. It uses the `String` as a name and prints a hello \nmessage. A better implementation might be to use Liferay's API to collect all \nthe names of all the users in the system and send each user a greeting \nnotification, but the point here is to keep things simple. You should \nunderstand, though, that there's nothing stopping you from replacing this \nimplementation by deploying another module whose Greeting implementation's \n`@Component` annotation specifies a higher service ranking property (e.g., \n`\"service.ranking:Integer=100\"`). \n\nThis `@Component` annotation defines three options: `immediate = true`, an empty \nproperty list, and the service class that it implements. The `immediate = true` \nsetting means that this module should not be lazy-loaded; the service registry \nloads it as soon as it's deployed, instead of when it's first used. Using the \n`@Component` annotation declares the class as a Declarative Services component, \nwhich is the most straightforward way to create components for OSGi modules. A \ncomponent is a POJO that the runtime creates automatically when the module \nstarts. \n\nTo compile this module, the API it's implementing must be on the classpath. If \nyou're using Gradle, you'd add the `greetings-api` project to your \n`dependencies { ... }` block. In a \n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace) \nmodule, the dependency looks like this: \n\n```groovy\ncompileOnly project (':modules:greeting-api')\n```\n\nThat's all there is to a provider module. \n\n### Consumer\n\nThe consumer or client uses the API that the API module defines and the provider \nmodule implements. DXP has many different kinds of consumer modules. \n[Portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets) \nare the most common consumer module type, but since they are a topic all by \nthemselves, this example stays simple by creating an command for the Apache \nFelix Gogo shell. Note that consumers can, of course, consume many different \nAPIs to provide functionality. \n\nA consumer module has the same structure as the other module types: \n\n- `greeting-command`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/greeting/command`\n                    - `GreetingCommand.java`\n    - `bnd.bnd`\n    - `build.gradle`\n\nAgain, you have a build script, a `bnd.bnd` file, and a Java class. This \nmodule's `bnd.bnd` file is almost the same as the provider's: \n\n```properties\nBundle-Name: Greeting Command\nBundle-SymbolicName: com.liferay.docs.greeting.command\nBundle-Version: 1.0.0\n```\n\nThere's nothing new here: you declare the same things you declared for the \nprovider. \n\nYour Java class has a little bit more going on: \n\n```java\npackage com.liferay.docs.greeting.command;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.greeting.api.Greeting;\n\n@Component(\n    immediate = true,\n    property = {\n        \"osgi.command.scope=greet\",\n        \"osgi.command.function=greet\"\n    },\n    service = Object.class\n)\npublic class GreetingCommand {\n\n    public void greet(String name) {\n        Greeting greeting = _greeting;\n\n        greeting.greet(name);\n    }\n\n    @Reference\n    private Greeting _greeting;\n\n}\n```\n\nThe `@Component` annotation declares the same attributes, but specifies \ndifferent properties and a different service. As in Java, where every class is a \nsubclass of `java.lang.Object` (even though you don't need to specify it by \ndefault), in Declarative Services, the runtime needs to know the type of class \nto register. Because you're not implementing any particular type, your parent \nclass is `java.lang.Object`, so you must specify that class as the service. \nWhile Java doesn't require you to specify `Object` as the parent when you're \ncreating a class that doesn't inherit anything, Declarative Services does. \n\nThe two properties define a command scope and a command function. All commands \nhave a scope to define their context, as it's common for multiple APIs to have \nsimilar functions, such as `copy` or `delete`. These properties specify you're \ncreating a command called `greet` in a scope called `greet`. While you get no \npoints for imagination, this sufficiently defines the command. \n\nSince you specified `osgi.command.function=greet` in the `@Component` \nannotation, your class must have a method named `greet`, and you do. But how \ndoes this `greet` method work? It obtains an instance of the `Greeting` OSGi \nservice and invokes its `greet` method, passing in the `name` parameter. How is \nan instance of the `Greeting` OSGi service obtained? The `GreetingCommand` class \ndeclares a private service bean, `_greeting` of type `Greeting`. This is the \nOSGi service type that the provider module registers. The `@Reference` \nannotation tells the OSGi runtime to instantiate the service bean with a service \nfrom the service registry. The runtime binds the `Greeting` object of type \n`GreetingImpl` to the private field `_greeting`. The `greet` method uses the \n`_greeting` field value. \n\nJust like the provider, the consumer needs to have the API on its classpath in \norder to compile, but at runtime, since you've declared all the dependencies \nappropriately, the container knows about these dependencies, and provides them \nautomatically. \n\nIf you were to \n[deploy these modules to a DXP instance](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project), \nyou'd be able to attach to the \n[Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell) \nand execute a command like this:\n\n```bash\ngreet:greet \"Captain\\ Kirk\"\n```\n\nThe shell would then return your greeting: \n\n```bash\nHello Captain Kirk!\n```\n\nThis most basic of examples should make it clear that module-based development \nis easy and straightforward. The API-Provider-Consumer contract fosters loose \ncoupling, making your software easy to manage, enhance, and support. \n\n## A Typical Liferay Application\n\nIf you look at a typical application from Liferay's source, you'll generally \nfind at least four modules: \n\n- An API module\n- A Service (provider) module\n- A Test module\n- A Web (consumer) module\n\nThis is exactly what you'll find for some smaller applications, like the \nMentions application that lets users mention other users with the `@username` \nnomenclature in comments, blogs, or other applications. Larger applications like \nthe Documents and Media library have more modules. In the case of the Documents \nand Media library, there are separate modules for different document storage \nback-ends. In the case of the Wiki, there are separate modules for different \nWiki engines. \n\nEncapsulating capability variations as modules facilitates extensibility. If you \nhave a document storage back-end that Liferay doesn't yet support, you can \nimplement Liferay's document storage API for your solution by developing a \nmodule for it and thus extend Liferay's Documents and Media library. If there's \na Wiki dialect that you like better than what Liferay's wiki provides, you can \nwrite a module for it and extend Liferay's wiki. \n\nAre you excited yet? Are you ready to start developing? Here are some resources \nfor you to learn more. \n\n## Related Topics\n\n[Liferay Dev Studio](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio)\n\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)\n\n[Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli)\n\n[Maven](/docs/7-2/reference/-/knowledge_base/r/maven)\n\n[Upgrading Code to 7.2](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver)\n"
  },
  {
    "path": "en/developer/customization/articles/03-architecture/06-module-lifecycle.markdown",
    "content": "---\nheader-id: module-lifecycle\n---\n\n# Module Lifecycle\n\n[TOC levels=1-4]\n\nIn OSGi, all components, Java classes, resources, and descriptors are deployed \nvia modules. The `MANIFEST.MF` file describes the module's physical \ncharacteristics, such as the packages it exports and imports. The module's \ncomponent description files specify its functional characteristics (i.e., the \nservices its components offer and consume). Also modules and their components \nhave their own lifecycles and administrative APIs. Declarative Services and \nshell tools give you fine-grained control over module and component deployment. \n\nSince a module's contents depend on its activation, consider the activation \nsteps: \n\n1.  *Installation*: Copying the module JAR into @product@'s \n    `[Liferay Home]/deploy` folder installs the module to the OSGi framework, \n    marking the module `INSTALLED`. \n\n2.  *Resolution*: Once all the module's requirements are met (e.g., all packages \n    it imports are available), the framework publishes the module's exported \n    packages and marks the module `RESOLVED`. \n\n3.  *Activation*: Modules are activated *eagerly* by default. That is, they're \n    started in the framework and marked `ACTIVE` on resolution. An active \n    module's components are enabled. If a module specifies a `lazy` activation \n    policy, as shown in the manifest header below, it's activated only after \n    another module requests one of its classes. \n\n    ```properties\n    Bundle-ActivationPolicy: lazy\n    ```\n\nThe figure below illustrates the module lifecycle. \n\n![Figure 1: This state diagram illustrates the module lifecycle.](../../images/module-state-diagram.png) \n\nThe [Apache Felix Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell) \nlets you manage the module lifecycle. You can install/uninstall modules \nand start/stop them. You can update a module and notify dependent modules \nto use the update. Liferay's tools, including [Liferay @ide@](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio), \n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace), \nand [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli) \noffer similar shell commands that use the OSGi Admin API. \n\nOn activating a module, its components are enabled. But only *activated* \ncomponents can be used. Component activation requires all its referenced \nservices be satisfied. That is, all services it references must be registered. \nThe highest ranked service that matches a reference is bound to the component. \nWhen the container finds and binds all the services the component references, it \nregisters the component. It's now ready for activation. \n\nComponents can use *delayed* (default) or *immediate* activation policies. To \nspecify immediate activation, the developer adds the attribute `immediate=true` \nto the `@Component` annotation. \n\n```java\n@Component(\n    immediate = true,\n    ...\n)\n```\n\nUnless immediate activation is specified, the component's activation is delayed. \nThat is, the component's object is created and its classes are loaded once the \ncomponent is requested. In this way, delayed activation can improve startup \ntimes and conserve resources. \n\nGogo Shell's [Service Component Runtime commands](http://felix.apache.org/documentation/subprojects/apache-felix-service-component-runtime.html#shell-command)\nlet you manage components:\n\n-   `scr:list [bundleID]`: Lists the module's (bundle's) components.\n\n-   `scr:info [componentID|fullClassName]`: Describes the component, including \n    its status and the services it provides.\n\n-   `scr:enable [componentID|fullClassName]`: Enables the component.\n\n-   `scr:disable [componentID|fullClassName]`: Disables the component. It's \n    disabled on the server (or current server node in a cluster) until \n    the server is restarted. \n\nService references are static and reluctant by default. That is, an injected \nservice remains bound to the referencing component until the service is \ndisabled. Alternatively, you can specify *greedy* service policies for \nreferences. Every time a higher ranked matching service is registered, the \nframework unbinds the lower ranked service from the component (whose service \npolicy is greedy) and binds the new service in its place automatically. Here's a \n`@Reference` annotation that uses a greedy policy:\n\n```java\n@Reference(policyOption = ReferencePolicyOption.GREEDY)\n```\n\nDeclarative Services annotations let you specify component activation and \nservice policies. Gogo Shell commands let you control modules and components. \n\n## Related Topics\n\n[Creating a Project](/docs/7-2/reference/-/knowledge_base/r/creating-a-project)\n\n[Upgrading Code to 7.2](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver)\n"
  },
  {
    "path": "en/developer/customization/articles/03-architecture/06-ui-concepts/01-ui-architecture-intro.markdown",
    "content": "---\nheader-id: ui-architecture\n---\n\n# UI Architecture\n\n[TOC levels=1-4]\n\n[@product@'s UI](/docs/7-2/user/-/knowledge_base/u/the-liferay-distinction)\nis a portal for adding sites, pages, widgets, and content. It helps\npeople do work,\n[collaborate](/docs/7-2/user/-/knowledge_base/u/collaboration),\nand\n[share content](/docs/7-2/user/-/knowledge_base/u/web-experience-management).\n\nThe UI comprises the following parts:\n\n-   Content: images, videos, and text.\n\n-   [Applications](/docs/7-2/appdev/-/knowledge_base/a/application-development):\n    Widgets and portlets that expose functionality for accomplishing tasks.\n\n-   [Themes](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction):\n    Plugins that use CSS, FreeMarker templates, HTML, and JavaScript to provide\n    a site's overall look and feel.\n\n-   Product navigation sidebars and panels: Use these for administering sites.\n\n## Content\n\n@product@'s built-in applications help you publish images, video, forms, markup\ntext, and more to site pages.\n[Documents and Media](/docs/7-2/user/-/knowledge_base/u/managing-documents-and-media)\nstores images, videos, and documents to use throughout your site. The\n[Web Experience Management](/docs/7-2/user/-/knowledge_base/u/web-experience-management)\nsuite helps you create, maintain, and organize content.\n[Liferay Forms](/docs/7-2/user/-/knowledge_base/u/forms)\ngives you robust form building capability.\n[Message Boards](/docs/7-2/user/-/knowledge_base/u/creating-forums-with-message-boards)\nfacilitate lively discussions and\n[Blogs](/docs/7-2/user/-/knowledge_base/u/publishing-blogs)\nlet users express themselves with markup text and images. These are just a few\nof the built-in applications for adding site content.\n\n## Applications\n\n@product@ applications provide content and help users accomplish tasks. They're\n[developed the same way](/7-2/appdev/-/knowledge_base/a/web-front-ends)\nas other web applications, and @product@ can combine multiple applications on\none page.\n\n@product@ supports developing JavaScript-based applications using popular\nfront-end frameworks:\n\n-   [Angular](/docs/7-2/appdev/-/knowledge_base/a/developing-an-angular-application)\n-   [React](/docs/7-2/appdev/-/knowledge_base/a/developing-a-react-application)\n-   [Vue](/docs/7-2/appdev/-/knowledge_base/a/developing-a-vue-application)\n\nJava-based portlet applications use the latest portlet standards and frameworks,\nincluding ones familiar to experienced Liferay portlet developers:\n\n-   [Liferay MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet)\n-   [PortletMVC4Spring](/docs/7-2/appdev/-/knowledge_base/a/portletmvc4spring)\n-   [JSF Portlet](/docs/7-2/appdev/-/knowledge_base/a/jsf-portlet)\n<!---   [Bean Portlet](/docs/7-2/appdev/-/knowledge_base/a/bean-portlet) TODO uncomment when Bean Portlet is available. jhinkey -->\n\nIn the UI, applications are referred to as Widgets and categorized for users to\nadd to pages. Administrative applications are available in the product menu\npanels.\n\n![Figure 1: Widget pages offer users functionality. Widgets are organized into a page template's rows and columns. This template has two columns: a smaller left column and larger right column. On this page, users select tags in the Tag Cloud widget and the matching tagged images show the Asset Publisher widget.](../../../images/architecture-ui-widgets.png)\n\n## Themes\n\nA\n[theme](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction)\nstyles a site with a unique look and feel. It's developed as a WAR project that\nincludes CSS, JavaScript, and markup content. You can develop themes using\nwhatever tools you prefer, but @product@ offers\n[Bootstrap](https://getbootstrap.com/)-based\ncomponents and\n[theme tooling](/docs/7-2/frameworks/-/knowledge_base/f/developing-themes)\nto create and deploy themes in no time.\n\n![Figure 2: You can select an attractive theme and apply it to your site.](../../../images/architecture-ui-themes.png)\n\nHere's a quick demonstration of developing a theme:\n\n1.  Create a theme using the\n    [Theme Generator](/docs/7-2/reference/-/knowledge_base/r/theme-generator).\n    The theme extends the base theme you specified to the Theme\n    Generator---Liferay's\n    [Styled theme](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-styled)\n    is the default.\n\n2.  Run\n    [`gulp build`](https://portal.liferay.dev/docs/7-2/frameworks/-/knowledge_base/f/building-your-themes-files)\n    to generate the base theme files to the `build` folder subfolders:\n\n    -   `templates`: FreeMarker templates specify site page markup.\n        `portal_normal.ftl` is the central file; it includes templates that\n        define the page parts (e.g., header, navigation, footer). The\n        `init.ftl` file defines default variables available to the templates.\n\n    -  `css`: SCCS files that provide styling.\n\n    -  `font`: Font Awesome and Glyphicons fonts.\n\n    -  `js`: JavaScript files; `main.js` is the Styled theme's\n        JavaScript.\n\n    -  `images`: Image files.\n\n3.  Override aspects of the base theme by copying relevant files from the\n    `build` subfolders to folders of the same name in your `src` folder. The\n    [Theme Anatomy Guide](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide)\n    describes all the files. Here's an example of a customized\n    `portal_normal.ftl`:\n\n```markup\n<html class=\"${root_css_class}\">\n<head></head>\n  <body class=\"${body_class}\">\n    <header class=\"${header_css_class}\">\n      <a class=\"${logo_css_class} href=\"${site_url}\"><img src=\"${site_logo}\"/></a>\n      <#include \"${full_templates_path}/navigation.ftl\" />\n    </header>\n    <section>\n      ${portlets}\n    </section>\n      <#include \"${full_templates_path}/footer.ftl\" />\n  </body>\n</html>\n```\n\n4.  Add custom styling using your theme's `_custom.scss` file (i.e.,\n    `src/css/_custom.scss`).\n    @product@ supports\n    [Bootstrap](https://getbootstrap.com/),\n    as well as\n    [Sass](https://sass-lang.com/),\n    so you can use Bootstrap utilities in your markup and Sass nesting,\n    variables, and more in your CSS files. This snippet styles the logo:\n\n```sass\n.logo {\n  margin-left: 15px;\n\n  img {\n    height: auto;\n  }\n\n  @include media-breakpoint-down(md) {\n    text-align: center;\n    width: 100%;\n  }\n}\n```\n\n![Figure 3: You can provide custom styling using the theme's `_custom.sccs` file.](../../../images/architecture-ui-portal-dev-logo.png)\n\n5.  Deploy your theme by executing `gulp deploy`.\n\nThe theme is available to\n[apply](/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes)\nto your site.\n\n\nFor details,\n[Theme Components](/docs/7-2/customization/-/knowledge_base/c/theme-components)\nbreaks down a theme's parts, and the\n[Themes section](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction)\nprovides theme development details.\n\n## Product Navigation Sidebars and Panels\n\nThe product navigation sidebars and panels enable administrators to build sites,\nadd pages, apply themes, and configure the portal. It's also where you can\nprovide administrative functionality for your custom applications. The\nnavigation sidebars and panels are customizable.\n\n![Figure 4: Liferay facilitates integrating custom administrative functionality through navigation menus and administrative applications.](../../../images/architecture-ui-menus-and-panel-app.png)\n\nAs you can see, @product@'s UI is highly flexible and customizable. Here's where\nto learn more:\n\n- [Theme Components](/docs/7-2/customization/-/knowledge_base/c/theme-components):\n  Explains available mechanisms and extensions for customizing and theming\n  pages, content, and applications.\n\n- [Understanding the Page Structure](/docs/7-2/customization/-/knowledge_base/c/understanding-the-page-structure):\n  Describes how the page's UI is organized and introduces tools for populating\n  and developing each section.\n"
  },
  {
    "path": "en/developer/customization/articles/03-architecture/06-ui-concepts/02-theme-components.markdown",
    "content": "---\nheader-id: theme-components\n---\n\n# Theme Components\n\n[TOC levels=1-4]\n\nThis guide provides an overview of the following theme development and\ncustomization topics:\n\n- [Theme Templates](#theme-templates)\n- [CSS Frameworks and Extensions](#css-frameworks-and-extensions)\n- [Theme Customizations and Extensions](#theme-customizations-and-extensions)\n- [Portlet Customizations and Extensions](#portlet-customizations-and-extensions)\n\n## Theme Templates and Utilities\n\nThe default FreeMarker templates provide helpful utilities and handle key pieces\nof page layout (page) functionality: \n\n- `portal_normal.ftl`: Similar to a static site's `index.html`, this file is \n  the hub for all the theme templates and provides the overall markup for the\n  page.\n- `init.ftl`: Contains variables commonly used throughout the theme templates. \n  Refer to it to look up theme objects. For convenience, the\n  [FreeMarker Variable Reference Guide](/docs/7-2/reference/-/knowledge_base/r/freemarker-variable-reference-guide)\n  lists the objects. \n  **DO NOT override this file**.\n- `init_custom.ftl`: Used to override FreeMarker variables in `init.ftl` and to\n  define new variables, such as \n  [theme settings](/docs/7-2/frameworks/-/knowledge_base/f/making-configurable-theme-settings).\n- `portlet.ftl`: Controls the theme's portlets. If your theme uses \n  [Portlet Decorators](/docs/7-2/frameworks/-/knowledge_base/f/theming-portlets#portlet-decorators), \n  modify this file to create application decorator-specific theme settings. \n- `navigation.ftl`: Contains the navigation markup. To customize pages in the\n  navigation, you must use the `liferay.navigation_menu` macro. Then you can\n  leverage\n  [widget templates](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/site-navigation/site-navigation-menu-web/src/main/resources/com/liferay/site/navigation/menu/web/portlet/template/dependencies)\n  for the navigation menu. Note that `navigation.ftl` also defines the hamburger\n  icon and `navbar-collapse` class that provides the simplified navigation\n  toggle for mobile viewports, as shown in the snippet below for the Classic\n  theme:\n\n```markup\n<#if has_navigation && is_setup_complete>\n  <button aria-controls=\"navigationCollapse\" aria-expanded=\"false\" \n  aria-label=\"Toggle navigation\" class=\"navbar-toggler navbar-toggler-right\" \n  data-target=\"#navigationCollapse\" data-toggle=\"collapse\" type=\"button\">\n    <span class=\"navbar-toggler-icon\"></span>\n  </button>\n\n  <div aria-expanded=\"false\" class=\"collapse navbar-collapse\" id=\"navigationCollapse\">\n    <@liferay.navigation_menu default_preferences=\"${preferences}\" />\n  </div>\n</#if>\n```\n\n![Figure 1: The collapsed navbar provides simplified user-friendly navigation for mobile devices.](../../../images/portal-layout-mobile-nav.png)\n\n- `portal_pop_up.ftl`: Controls pop up dialogs for the\n  theme's portlets. Similar to `portal_normal.ftl`, `portal_pop_up.ftl` provides\n  the markup template for all pop-up dialogs, such as a portlet's Configuration \n  menu. It also has access to the FreeMarker variables defined in `init.ftl` and \n  `init_custom.ftl`.\n\n![Figure 2: Each theme template provides a portion of the page's markup and functionality.](../../../images/portal-layout-theme-templates.png)\n\n- [`FTL_Liferay.ftl`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-template/portal-template-freemarker/src/main/resources/FTL_liferay.ftl): \n    Provides [macros](/docs/7-2/reference/-/knowledge_base/r/product-freemarker-macros) \n    for commonly used portlets and theme resources. \n\n- `taglib-mappings.properties`: Maps the portal taglibs to FreeMarker macros.\n  Taglibs can quickly create common UI components. This properties file is \n  provided separately for each app taglib. For convenience, these FreeMarker\n  macros appear in the [FreeMarker Taglib Mappings reference guide](/docs/7-2/reference/-/knowledge_base/r/freemarker-taglib-macros). \n  See the [Taglib reference](/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs) \n  for more information on using each taglib in your theme templates. \n\n## CSS Frameworks and Extensions\n\nThemes are integrated with [SASS](https://sass-lang.com/), so you can take full \nadvantage of Sass mixins, nesting, partials, and variables in your CSS. \n\nAlso important to note is \n[Clay CSS](https://clayui.com/), \nthe web implementation of Liferay's \n[Lexicon design language](https://lexicondesign.io/). \nAn extension of Bootstrap, Clay CSS fills the gaps between Bootstrap and the \nneeds of @product@, providing additional components and CSS patterns that you \ncan use in your themes. Clay base, Liferay's Bootstrap API extension, along with \nAtlas, a custom Bootstrap theme, creates @product@'s Classic theme. See \n[Customizing Atlas and Clay Base Themes](/docs/7-2/frameworks/-/knowledge_base/f/customizing-atlas-and-clay-base-themes) \nfor more information.\n\n## Theme Customizations and Extensions\n\nThe theme templates, along with the CSS, provide much of the overall look and \nfeel for the page, but additional extension points/customizations are available. \nThe following extensions and mechanisms are available for themes:\n\n- **Color Schemes:** Specifies configurable color scheme settings Administrators\n  can configure via the Look and Feel menu. See the \n  [color scheme tutorial](/docs/7-2/frameworks/-/knowledge_base/f/creating-color-schemes-for-your-theme)\n  for more information.\n- **Configurable Theme Settings:** Administrators can configure\n  theme aspects that change frequently, such as the visibility of\n  certain elements, changing a daily quote, etc. See the \n  [Configurable Theme Settings tutorial](/docs/7-2/frameworks/-/knowledge_base/f/making-configurable-theme-settings)\n  for more information. \n- **Context Contributor:** Exposes Java variables and functionality for use in\n  FreeMarker templates. This allows non-JSP templating languages in themes,\n  widget templates, and any other templates. See the \n  [Context Contributors tutorial](/docs/7-2/frameworks/-/knowledge_base/f/injecting-additional-context-variables-and-functionality-into-your-theme-templates)\n  or more information.\n- **Theme Contributor:** A package containing UI resources, not attached to a \n  theme, that you want to include on every page. See the \n  [Theme Contributors tutorial](/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site) \n  for more information. \n- **Themelet:** Small, extendable, and reusable pieces of code containing CSS\n  and JavaScript. They can be shared with other developers to provide common\n  components for themes. See \n  [Generating Themelets](/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator)\n  for more information.\n\n## Portlet Customizations and Extensions\n\nYou can customize portlets with these mechanisms and extensions:\n\n- **Portlet FTL Customizations:** Customize the base template markup for all \n  portlets. See the \n  [Theming Portlets](/docs/7-2/frameworks/-/knowledge_base/f/theming-portlets) \n  for more information.\n- **Widget Templates:** Provides an alternate display style \n  for a portlet. Note that not all portlets support widget templates. See the \n  [Widget Templates User Guide](/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates) \n  for more information.\n- **Portlet Decorator:** Customizes the exterior decoration for a portlet. \n  See [Portlet Decorators](/docs/7-2/frameworks/-/knowledge_base/f/theming-portlets#portlet-decorators) \n  for more information.\n- **Web Content Template:** Defines how structures are displayed for web content. \n  See the \n  [Web Content Templates User Guide articles](/docs/7-2/user/-/knowledge_base/u/designing-web-content-with-templates) \n  for more information.\n\n![Figure 3: There are several extension points for customizing portlets](../../../images/portal-layout-portlet-customizations.png)\n\n## Related Topics\n\n- [Understanding the Page Structure](/docs/7-2/customization/-/knowledge_base/c/understanding-the-page-structure)\n- [Installing the Theme Generator and Creating a Theme](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme)\n- [Developing Themes](/docs/7-2/frameworks/-/knowledge_base/f/developing-themes)\n"
  },
  {
    "path": "en/developer/customization/articles/03-architecture/06-ui-concepts/03-understanding-the-page-layout.markdown",
    "content": "---\nheader-id: understanding-the-page-structure\n---\n\n# Understanding the Page Structure\n\n[TOC levels=1-4]\n\nUnderstanding the page's structure is crucial to targeting the correct markup \nfor styling, organizing your content, and creating your site. Your page layout \nis unique to the requirements and design for your site. The Unstyled theme's \ndefault page layout is organized into three key sections in its \n`portal_normal.ftl` template:\n\n- **Header:** Contains the navigation, site logo and title (if shown), and\n  sign-in link when the user isn't logged in.\n- **Main Content:** Contains the portlets or fragments for the page.\n- **Footer:** contains additional information, such as the copyright or author.\n\n![Figure 1: The page layout is broken into three key sections.](../../../images/portal-layout-sections.png)\n\n## Portlets or Fragments\n\nThe `#content` `Section` makes up the majority of the page. Portlets or \nfragments are contained inside the `#main-content` `div`. @product@ ships with \na default set of applications that provide common functionality, such as forums \nand Wikis, documents and media, blogs, and more. For more information on using \n@product@ and its native portlets, see the \n[User & Admin documentation](/documentation/user). \nYou can also create custom portlets for your site. Portlets can be added via the\nAdd Menu (referred to as widget), included in a sitemap through the \n[Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/importing-resources-with-a-theme),\nor they can be \n[embedded in the page's theme](/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes).\nSee the \n[portlet tutorials section](/docs/7-2/frameworks/-/knowledge_base/f/portlets) for more information\non creating and developing portlets. \n\nYou can target the elements and IDs shown in the table below to style the page:\n\n| Element | ID | Description |\n| --- | --- | --- |\n| `div` | `#wrapper` | The container div for the page contents | \n| `header` | `#banner` | The page's header |\n| `section` | `#content` > `#main-content` | The main contents of the page (portlets or fragments) |\n| `footer` | `#footer` | The page's footer |\n\n![Figure 2: Each section of the page has elements and IDs that you can target for styling.](../../../images/portal-layout-elements.png)\n\nAs shown in the diagram above, you can also add \n[fragments](/docs/7-2/frameworks/-/knowledge_base/f/page-fragments) \nto a page. Fragments are components---composed of CSS, JavaScript, and\nHTML---that provide key pieces of functionality for the page (i.e. a carousel or\nbanner). @product@ provides an editor for creating collections of fragments that\nyou can then add to the page. These fragments can be edited on the page to suit\nyour vision. \n\n## Layout Templates, Page Templates, and Site Templates\n\nThe page layout within the `#content` Section is determined by the \n[Layout Template](/docs/7-2/frameworks/-/knowledge_base/f/layout-templates-intro). \nSeveral layout templates are included out-of-the-box. You can also \n[create custom layout templates manually](/docs/7-2/frameworks/-/knowledge_base/f/layout-templates-intro) \nor with the \n[Liferay Theme Generator's layout sub-generator](/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator). \n\nLayout templates can be pre-configured depending on the \n[page type](/docs/7-2/user/-/knowledge_base/u/creating-pages) \nyou choose when the page is created. Along with setting the types of portlets to \ninclude on the page, the page template may also define the default layout \ntemplate for the page. Climbing further up the scope chain, you can create \n[Site Templates](/docs/7-2/user/-/knowledge_base/u/building-sites-from-templates), \nwhich can define the pages, page templates, layout templates, and theme(s) to \nuse for site pages. \n\n## Product Navigation Sidebars and Panels\n\nThe main page layout also contains a few notable sidebars an administrative user \ncan trigger through the Control Menu. These are listed below:\n\n- **Add Menu:** For adding portlets (widgets) and fragments (if applicable) to \n  the page\n- **Control Menu:** Provides the main navigation for accessing the Add Menu, \n  Product Menu, and Simulation Panel\n- **Product Menu:** Contains administrative apps, configuration settings, and \n  user account settings, profile, and dashboard page\n- **Simulation Panel:** Simulates how the page appears on different devices\n\n![Figure 3: Remember to account for the product navigation sidebars and panels when styling your site.](../../../images/portal-layout-nav-control-menu.png)\n\n![Figure 4: The Add Menu pushes the main contents to the left.](../../../images/portal-layout-nav-add-menu.png)\n\n![Figure 5: The Product Menu pushes the main contents to the right.](../../../images/portal-layout-nav-product-menu.png)\n\n![Figure 6: The Simulation Panel pushes the main contents to the left.](../../../images/portal-layout-nav-simulation-panel.png)\n\nWhen styling the page, you must keep the navigation menus in mind, especially \nfor absolutely positioned elements, such as a fixed navbar. If the user is \nlogged in and can view the Control Menu, the fixed navbar must have a top margin \nequal to the Control Menu's height. \n\nSee the [Product Navigation articles](/docs/7-2/customization/-/knowledge_base/c/product-navigation) \nfor more information on customizing these menus. \n\n## Related Topics\n\n- [Creating Layout Templates with the Layouts Sub-generator](/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator)\n- [Bundling Layout Templates with a Theme](/docs/7-2/frameworks/-/knowledge_base/f/including-layout-templates-with-a-theme)\n- [Installing the Liferay Theme Generator and Creating a Theme](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme)\n"
  },
  {
    "path": "en/developer/customization/articles/03-architecture/07-bundle-classloading-flow.markdown",
    "content": "---\nheader-id: bundle-classloading-flow\n---\n\n# Bundle Classloading Flow\n\n[TOC levels=1-4]\n\nThe OSGi container searches several places for imported classes. It's important \nto know where it looks and in what order. @product@'s classloading flow for OSGi \nbundles follows the OSGi Core specification. It's straightforward, but complex. \nThe figure below illustrates the flow and this article walks you through it. \n\n![Figure 1: This flow chart illustrates classloading in a bundle.](../../images/bundle-classloading-flow-chart.png)\n\nHere is the algorithm for classloading in a bundle: \n\n1.  If the class is in a `java.*` package, delegate loading to the parent \n    classloader. Otherwise, continue. \n\n2.  If the class is in the OSGi Framework's boot delegation list, delegate \n    loading to the parent classloader. Otherwise, continue. \n\n3.  If the class is in one of the packages the bundle imports from a wired \n    exporter, the exporting bundle's classloader loads it. A *wired exporter* is \n    another bundle's classloader that previously loaded the package. If the \n    class isn't found, continue. \n\n4.  If the class is imported by one of the bundle's required bundles, the \n    required bundle's classloader loads it. \n\n5.  If the class is in the bundle's classpath (manifest header \n    `Bundle-ClassPath`), the bundle's classloader loads it. Otherwise, continue. \n\n6.  If the class is in the bundle's fragments classpath, the bundle's \n    classloader loads it. \n\n7.  If the class is in a package that's dynamically imported using \n    `DynamicImport-Package` and a wire is established with the exporting bundle, \n    the exporting bundle's classloader loads it. Otherwise, the class isn't \n    found. \n\nCongratulations! Now you know how @product@ finds and loads classes for OSGi \nbundles. \n"
  },
  {
    "path": "en/developer/customization/articles/03-architecture/08-finding-extension-points.markdown",
    "content": "---\nheader-id: finding-extension-points\n---\n\n# Finding Extension Points\n\n[TOC levels=1-4]\n\n@product@ provides many features that help users accomplish their tasks. \nSometimes, however, you may find it necessary to [customize a built-in feature](/docs/7-2/customization/-/knowledge_base/c/liferay-customization).\nIt's easy to **find** an area you want to customize, but it may seem like a \ndaunting task to figure out **how** to customize it. @product@ was developed for \neasy customization, meaning it has many extension points you can use to add \nyour own flavor. \n\nThere's a process you can follow that makes finding an extension point a breeze. \n\n1.  Locate the bundle (module) that provides the functionality you want to \n    change. \n2.  Find the components available in the module. \n3.  Discover the extension points for the components you want to customize. \n\nThis article demonstrates finding an extension point. It steps through a simple \nexample that locates an extension point for importing LDAP users. The example \nincludes using @product@'s \n[Application Manager](/docs/7-2/user/-/knowledge_base/u/managing-and-configuring-apps#using-the-app-manager) \nand \n[Felix Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell). \n\n## Locate the Related Module and Component\n\nFirst think of words that describe the application behavior you want to change. \nThe right keywords can help you easily track down the desired module and its \ncomponent. Consider the example for importing LDAP users. Some candidate \nkeywords for finding the component are *import*, *user*, *security, *and *LDAP*. \n\nThe easiest way to discover the module responsible for a particular Liferay \nfeature is to use the Application Manager. The Application Manager lists apps \nand their included modules/components in an easy-to-use interface. It even lists \nthird party apps! You'll use your keywords to target the applicable component. \n\n1.  Open the App Manager by navigating to *Control Panel* &rarr; *Apps* &rarr; \n    *App Manager*. The top level lists independent apps and independent modules. \n\n2.  Navigate the apps and modules to find components that might provide your \n    desired extension point. Remember to check for your keywords in element \n    names. The keyword *security* is found in the Liferay CE Portal Security \n    app. Select it.\n\n3.  The Security application has several modules to inspect. Select the \n    *Liferay Portal Security LDAP Implementation* module. \n\n    ![Figure 1: The module name can be found using the App Manager.](../../images/ldapimplementation-module.png)\n\n4.  Search through the components, applying your keywords as a guide. Copy the \n    component name you think best fits the functionality you want to customize; \n    you'll inspect it later using the Gogo shell. \n\n    ![Figure 2: The component name can be found using the App Manager.](../../images/usermodellistener-component.png)\n\n    | **Note:** When using the Gogo shell later, understand that it can take \n    | several tries to find the component you're looking for; Liferay's naming \n    | conventions facilitate finding extension points in a manageable time frame. \n\nNext, you'll use the Gogo shell to inspect the component for extension points. \n\n## Finding Extension Points in a Component\n\nOnce you have the component that relates to the functionality you want to \nextend, you can use the Gogo shell's Service Component Runtime (SCR) commands to \ninspect it. You can execute SCR commands using \n[Liferay Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli) or in \n[Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell). \nThis article assumes you're using the Gogo shell. \n\nExecute the following command:\n\n```bash\nscr:info [COMPONENT_NAME]\n```\n\nFor the LDAP example component you copied previously, the command would look \nlike this:\n\n```bash\nscr:info com.liferay.portal.security.ldap.internal.messaging.UserImportMessageListener\n```\n\nThe output includes a lot of information. For this exercise, you're interested \nin services the component references. They are extension points. For \nexample, here's the reference for the service that imports LDAP users:\n\n```bash\n- _ldapUserImporter: \n  com.liferay.portal.security.ldap.exportimport.LDAPUserImporter \n  SATISFIED \n  1..1 \n  dynamic+greedy\n    target=(*) scope=bundle (1 binding):\n    * Bound to [7764] from bundle 1754 (com.liferay.portal.security.ldap.impl:2.0.4)\n```\n\nThe `LDAPUserImporter` is the extension point for customizing the LDAP user \nimport process! If none of the references satisfy what you're looking for, \nsearch other components from the App Manager. \n\nIf you plan on overriding the referenced service, you'll need to understand the \nreference's policy and policy option. In the example, the policy is `dynamic` \nand the policy option is `greedy`. If the policy is `static` and the policy \noption is `reluctant`, binding a new higher ranking service in place of a bound \nservice requires reactivating the component or changing the target. For \ninformation on the other policies and policy options, visit the \n[OSGi specification](https://osgi.org/download/r6/osgi.enterprise-6.0.0.pdf), in \nparticular, sections 112.3.5 and 112.3.6. See \n[Overriding OSGi Services](/docs/7-2/customization/-/knowledge_base/c/overriding-osgi-services) \nto learn how to override a component's service reference. \n\n**Important** Not all Liferay extension points are available as referenced \nservices. Service references are common in Declarative Services (DS) components, \nbut extension points can be exposed in other ways too. Here's a brief list of \nother potential extension points in @product@:\n\n- Instances of `org.osgi.util.tracker.ServiceTracker<S, T>`\n- Uses of Liferay's `Registry.getServiceTracker`\n- Uses of Liferay's `ServiceTrackerMap` or `ServiceTrackerCollection`\n- Any other component framework or whiteboard implementation (e.g., HTTP, \n  JAX-RS) that supports tracking services; Blueprint, Apache Dependency Manager, \n  etc. could also introduce extension points. \n\nThere you have it! In the App Manager, you used keywords to find the module \ncomponent whose behavior you wanted to change. Then you used Gogo shell to find \nthe component extension point for implementing your customization. \n"
  },
  {
    "path": "en/developer/customization/articles/100-troubleshooting-customizations/01-troubleshooting-customizations-intro.markdown",
    "content": "---\nheader-id: troubleshooting-faq\n---\n\n# Troubleshooting Customizations\n\n[TOC levels=1-4]\n\nWhen coding on any platform, you can sometimes run into issues that have no\nclear resolution. This can be particularly frustrating. If you have issues\nbuilding, deploying, or running apps and modules, you want to resolve them\nfast. These frequently asked questions and answers help you troubleshoot and\ncorrect problems. \n\nClick a question to view the answer.\n\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">Why aren't my fragment's JSP overrides showing?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">  \n    <p><a href=\"/docs/7-2/customization/-/knowledge_base/c/why-arent-jsp-overrides-i-made-using-fragments-showing\">Make sure your <code>Fragment-Host</code>'s bundle version is compatible with the host's bundle version</a>. </p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">Why doesn't the package I use from the fragment host resolve?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">  \n    <p><a href=\"/docs/7-2/customization/-/knowledge_base/c/why-is-a-package-i-use-from-the-fragment-host-unresolved\">Refrain from importing (<code>Import-Package: ...</code>) host packages that the host doesn't export</a>. </p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\" id=\"cacheable-web-content-taglibs\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">Why does my web content break when I refresh the page?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>Some taglibs, such as the <code>liferay-map</code> taglib, have limitations when used in a cacheable template (e.g., FreeMarker and Velocity). For instance, when the <code>liferay-map</code> taglib is used in a cacheable template and the user refreshes the page, the map does not show. </p>\n    <p>One possible workaround is to disable cache for the template by editing it and unchecking the cacheable option. Alternatively, you can disable cache for all templates by navigating to <code>System Settings</code>&rarr;<code>Template Engines</code> and setting <code>Resource Modification Check</code> to <code>0</code>. </p>\n    <p>As best practice, however, we recommend that you don't use taglibs in cacheable web content. </p>\n  </div>\n</div>\n"
  },
  {
    "path": "en/developer/customization/articles/100-troubleshooting-customizations/jsp-fragment-unresolved-requirement.markdown",
    "content": "---\nheader-id: why-is-a-package-i-use-from-the-fragment-host-unresolved\n---\n\n# Why doesn't the package I use from the fragment host resolve?\n\n[TOC levels=1-4]\n\nAn OSGi fragment can access all of the fragment host's packages---it \ndoesn't need to import them from another bundle. bnd adds external packages the\nfragment uses (even ones in the fragment host) to the fragment's\n`Import-Package: [package],...` OSGi manifest header. That's fine for packages\nexported to the OSGi runtime. The problem is, however, when bnd tries to import\na host's internal package (a package the host doesn't export). The OSGi runtime\ncan't activate the fragment because the internal package remains an `Unresolved\nrequirement`---a fragment shouldn't import a fragment host's packages. \n\nResolve the issue by explicitly excluding host packages that the host doesn't\nexport.\n\nFor example, this fragment bundle's JSP uses classes from the fragment host\nbundle's internal package\n`com.liferay.portal.search.web.internal.custom.facet.display.context`: \n\n```javascript\n<%@\npage import=\"com.liferay.portal.search.web.internal.custom.facet.display.context.CustomFacetDisplayContext\" %><%@\npage import=\"com.liferay.portal.search.web.internal.custom.facet.display.context.CustomFacetTermDisplayContext\" %>\n```\n\nSince the example host bundle doesn't export the package, the fragment bundle\ncan avoid importing the package by using an OSGi manifest header, like the one\nbelow, to explicitly exclude the package from package imports:\n\n```\nImport-Package: !com.liferay.portal.search.web.internal.*,*\n```\n"
  },
  {
    "path": "en/developer/customization/articles/100-troubleshooting-customizations/jsp-fragments.markdown",
    "content": "---\nheader-id: why-arent-jsp-overrides-i-made-using-fragments-showing\n---\n\n# Why Aren't JSP overrides I Made Using Fragments Showing?\n\n[TOC levels=1-4]\n\n| **Important:** It's strongly recommended to\n| [customize JSPs using @product@'s API](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps).\n| Since overriding a JSP using an OSGi fragment is not based on APIs there's no\n| way to guarantee that it will fail gracefully. Instead, if your customization \n| is buggy (because of your code or because of a change in Liferay), you are\n| most likely to find out at runtime, where functionality breaks and nasty log\n| errors greet you. Overriding a JSP using a fragment should only be used as a\n| last resort.\n\nThe fragment module must specify the exact version of the host module. A \n@product@ upgrade might have changed some JSPs in the host module, prompting a \nversion update. If this occurs, check that your JSP customizations are \ncompatible with the updated host JSPs and then update your fragment module's \ntargeted version to match the host module. \n\nFor example, this `bnd.bnd` file from a fragment module uses `Fragment-Host` to \nspecify the host module and host module version: \n\n```\nBundle-Name: custom-login-jsp\nBundle-SymbolicName: custom.login.jsp\nBundle-Version: 1.0.0\nFragment-Host: com.liferay.login.web;bundle-version=\"1.1.18\"\n```\n\n[Finding versions of deployed modules](/docs/7-2/customization/-/knowledge_base/c/finding-artifacts)\nis straightforward.  \n\n## Related Topics\n\n[JSP Overrides using Portlet Filters](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters)\n\n[Customizing JSPs](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps)\n\n[Finding Artifacts](/docs/7-2/customization/-/knowledge_base/c/finding-artifacts)\n"
  },
  {
    "path": "en/developer/customization/articles/100-troubleshooting-customizations/using-osgi-services-from-ext-plugins.markdown",
    "content": "---\nheader-id: using-osgi-services-from-ext-plugins\n---\n\n# Using OSGi Services from EXT Plugins\n\n[TOC levels=1-4]\n\n[`ServiceTrackers`](/docs/7-2/frameworks/-/knowledge_base/f/service-trackers-for-osgi-services)\nare the best way for Ext plugins to access OSGi services. They account for the\npossibility of OSGi services coming and going. \n\n## Related Topics\n\n[Detecting Unresolved OSGi Components](/docs/7-2/appdev/-/knowledge_base/a/detecting-unresolved-osgi-components)\n\n[Felix Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\n"
  },
  {
    "path": "en/developer/customization/articles/110-contributing/01-contributing-to-liferay-portal-intro.markdown",
    "content": "---\nheader-id: contributing-to-liferay-portal\n---\n\n# Contributing to Liferay Portal\n\n[TOC levels=1-4]\n\nLiferay Portal is developed by its community consisting of users, enthusiasts,\nemployees, customers, partners, and others. We strongly encourage you to\ncontribute to Liferay's open source projects by implementing new features,\nenhancing existing features, and fixing bugs. We also welcome your participation\nin our forums, chat, writing documentation, and translating existing\ndocumentation.\n\nLiferay Portal is known for its innovative top quality features. To maintain\nthis reputation, all code changes are reviewed by a core set of project\nmaintainers. We encourage you to join our\n[Slack Chat](https://liferay-community.slack.com)\nand introduce yourself to the core maintainer(s) and engage them as you\ncontribute to the areas they maintain.\n\nDeveloping features and fixes requires cloning the source tree and building\nLiferay Portal. \n\n## Building Liferay Portal from source\n\nThe first step to contributing to Liferay Portal is to clone the\n`liferay-portal` repo from GitHub and build the platform from source code.\n\nPlease follow the instructions for\n[building Liferay Portal from source code](https://portal.liferay.dev/participate/fix-a-bug/building-liferay-source). \n\nTo better understand the code structure, please also read\n[How the source is organized](https://portal.liferay.dev/participate/fix-a-bug/how-the-source-is-organized).\n\n## Tooling\n\n[Liferay tooling](/docs/7-2/reference/-/knowledge_base/r/tooling)\nfacilitates creating customizations and debugging code.\nConsider using these Liferay development tools:\n\n-   [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli): \n    a command line interface used to build and manage Liferay Workspaces and\n    Liferay Portal projects. This CLI is intended for Gradle or Maven \n    development.\n-   [Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace):\n    a generated Gradle/Maven environment built to hold and manage Liferay Portal\n    projects.\n-   [Liferay Dev Studio](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio):\n    an Eclipse-based IDE supporting development for Liferay Portal.\n-   [Liferay IntelliJ Plugin](/docs/7-2/reference/-/knowledge_base/r/intellij):\n    a plugin providing support for Liferay Portal development with IntelliJ \n    IDEA.\n-   [Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/theme-generator):\n    a generator that creates themes, layouts templates, and themelets for \n    Liferay Portal development.\n-   [Liferay JS Generator](/docs/7-2/reference/-/knowledge_base/r/js-generator):\n    a generator that creates JavaScript portlets with JavaScript tooling.\n\nThe\n[Configure an IDE for use with the Liferay Source](https://portal.liferay.dev/participate/fix-a-bug/ide-support)\npage, explains how to set up the project in your favorite IDE. \n\n## Additional Resources\n\n[Liferay Community Site](https://liferay.dev)\n\n[Liferay Community Slack Chat](https://liferay-community.slack.com/)\n\n[Liferay Community Slack Chat Self Invite](https://liferay.dev/chat)\n\n[Contributor License Agreement](https://www.liferay.com/legal/contributors-agreement)\n\n[General GitHub documentation](http://help.github.com/)\n\n[GitHub pull request documentation](http://help.github.com/send-pull-requests/)\n"
  },
  {
    "path": "en/developer/customization/articles/50-creating-model-listeners/01-creating-model-listeners-intro.markdown",
    "content": "---\nheader-id: model-listeners\n---\n\n# Model Listeners\n\n[TOC levels=1-4]\n\nModel Listeners implement the \n[`ModelListener` interface](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/ModelListener.html). \nThey are used to listen for persistence events on models and do something in \nresponse (either before or after the event). \n\nModel listeners are designed to perform lightweight actions in response to a \n`create`, `remove`, or `update` attempt on an entity's database table or a \nmapping table (for example, `users_roles`). Here are some supported use cases:\n\n-  Audit Listener: In a separate database, record information about updates to \n   an entity's database table.\n-  Cache Clearing Listener: Clear caches that you've added to improve the \n   performance of custom code.\n-  Validation Listener: Perform additional validation on a model's attribute \n   values before they are persisted to the database.\n-  Entity Update Listener: Do some additional processing when an entity table is \n   updated. For example, notify users when changes are made to their account.\n\nThere are also use cases that are not recommended, since they're likely to break \nunpredictably and give you headaches:\n\n-  Setting a model's attributes in an `onBeforeUpdate` call. If some other \n   database table has already been updated with the values before your model \nlistener is invoked, your database gets out of sync. To change how an entity's \nattributes are set, consider using a [service wrapper](/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers) \ninstead.\n-  Wrapping a model. Model listeners are not called when fetching records from \n   the database.\n-  Creating worker threads to do parallel processing and querying data you \n   updated via your listener. Model listeners are called *before* the database \ntransaction is complete (even the `onAfter...` methods), so the queries could be \nexecuted before the database transaction completes. \n\nIf there is no existing listener on the model, your model listener is the only \none that runs. However, there can be multiple listeners on a single model, and \nthe order in which the listeners run cannot be controlled. \n\nYou can create a model listener in a module by doing two simple things:\n\n-  Implement `ModelListener`\n-  Register the service in Liferay's OSGi runtime\n\n## Creating a Model Listener Class\n\nCreate a `-ModelListener` class that extends the \n[`BaseModelListener` class](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/BaseModelListener.html). \n\n```java\npackage ...;\n\nimport ...;\n\npublic class CustomEntityListener extends BaseModelListener<CustomEntity> {\n\n    // Override one or more methods from the ModelListener interface.\n    \n}\n```\n\nIn the body of the class, override any methods from the \n[`ModelListener` interface](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/ModelListener.html). \nThe available methods are listed and described at the end of this article. \n\nIn your model listener class, the parameterized type (for example, \n`CustomEntity` in the snippet above) tells the listener's \n`ServiceTrackerCustomizer` which model class to register the listener against. \n\n## Register the Model Listener Service\n\nRegister the service with Liferay's OSGi runtime for immediate activation. If \nusing Declarative Services, set `service= ModelListener.class` and \n`immediate=true` in the Component:\n\n```java\n@Component(\n    immediate = true,\n    service = ModelListener.class\n)\n```\n\nThat's all there is to preparing a model listener. Now learn what model events \nyou can respond to. \n\n## Listening For Persistence Events\n\nThe `ModelListener` interface provides lots of opportunity to listen for model\nevents:\n\n-  **`onAfterAddAssociation`:** If there's an association between two models (if\n   they have a mapping table), use this method to do something after an\nassociation record is added.\n-  **`onAfterCreate`:** Use this method to do something after the persistence\n   layer's `create` method is called.\n-  **`onAfterRemove`:** Use this method to do something after the persistence\n   layer's `remove` method is called.\n-  **`onAfterRemoveAssociation`:** If there's an association between two models\n   (if they have a mapping table), do something after an association record is\nremoved.\n-  **`onAfterUpdate`:** Use this method to do something after the persistence\n   layer's `update` method is called.\n-  **`onBeforeAddAssociation`:** If there's an association between two models \n   (if they have a mapping table), do something before an addition to the\n   mapping table.\n-  **`onBeforeCreate`:** Use this method to do something before the persistence\n   layer's `create` method is called.\n-  **`onBeforeRemove`:** Use this method to do something before the persistence\n   layer's `remove` method is called.\n-  **`onBeforeRemoveAssociation`:** If there's an association between two models\n(if\n   they have a mapping table), do something before a removal from the mapping\ntable.\n-  **`onBeforeUpdate`:** Use this method to do something before the persistence\n   layer's `update` method is called.\n\nLook in Liferay source file \n`portal-kernel/src/com/liferay/portal/kernel/service/persistence/impl/BasePersistenceImpl.java`, \nparticularly the `remove` and `update` methods, and you'll see how model \nlisteners are accounted for before (for the `onBefore...` case) and after (for \nthe `onAfter...` case) the model persistence event. \n\nNow that you know how to create model listeners, keep in mind that they're \nuseful as standalone projects or inside of your application. If your application \nneeds to do something (like add a custom entity) every time a User is added in \nLiferay, you can include the model listener inside your application. \n\n## Related Topics\n\n- [Upgrading Model Listener Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-model-listener-hooks)\n- [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n- [Service Builder Persistence](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n"
  },
  {
    "path": "en/developer/customization/articles/50-customizing-jsps/01-overriding-jsps-intro.markdown",
    "content": "---\nheader-id: customizing-jsps\n---\n\n# Customizing JSPs\n\n[TOC levels=1-4]\n\nThere are several different ways to customize JSPs in portlets and the core. \n@product@'s API provides the safest ways to customize them. If you customize a \nJSP by other means, new versions of the JSP can render your customization \ninvalid and leave you with runtime errors. It's highly recommended to use one of \nthe API-based ways. \n\n## Using @product@'s API to Override a JSP\n\nHere are API-based approaches to overriding JSPs in @product@:\n\n **Approach** | **Description** | **Cons/Limitations** |\n :----------- | :-------------- | :-------------- |\n[Dynamic includes](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps-with-dynamic-includes) | Adds content at dynamic include tags. | Limited to JSPs that have `dynamic-include` tags (or tags whose classes inherit from `IncludeTag`). Only inserts content in the JSPs at the dynamic include tags. |\n[Portlet filters](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters) | Modifies portlet requests and/or responses to simulate a JSP customization. | Although this approach doesn't directly customize a JSP, it achieves the effect of a JSP customization. |\n\n<!-- TODO - Include inlined content approach after getting more info. - Jim\nInlined content | Some @product@ JSPs include content from other JSPs that you can modify. This \"inlines\" the content from the other JSPs into specific places in the root JSP. | Limited to JSPs that inline other JSPs. |\n-->\n\n## Overriding a JSP Without Using @product@'s API\n\nIt's strongly recommended to customize JSPs using @product@'s API, as the \nprevious section describes. Since overriding a JSP using an OSGi fragment or a \nCustom JSP Bag is not based on APIs there's no way to guarantee that they'll \nfail gracefully. Instead, if your customization is buggy (because of your code \nor because of a change in Liferay), you are most likely to find out at runtime, \nwhere functionality breaks and nasty log errors greet you. These approaches \nshould only be used as a last resort. \n\nIf you're maintaining a JSP customization that uses one of these approaches, you \nshould know how they work. This section describes them and links to their \ntutorials. \n\nHere are ways to customize JSPs without using @product@'s API:\n\n **Approach** | **Description** | **Cons/Limitations** |\n :----------- | :-------------- | :-------------- |\n[OSGi fragment](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-osgi-fragments) | Completely overrides a module's JSP using an OSGi fragment | Changes to the original JSP or module can cause runtime errors. |\n[Custom JSP bag](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-custom-jsp-bag) | Completely override a @product@ core JSP or one of its corresponding `-ext.jsp` files. | For @product@ core JSPs only. Changes to the original JSP or module can cause runtime errors. |\n\nAll the JSP customization approaches are available to you. It's time to \ncustomize some JSPs! \n"
  },
  {
    "path": "en/developer/customization/articles/50-customizing-jsps/02-customizing-jsps-with-dynamic-includes.markdown",
    "content": "---\nheader-id: customizing-jsps-with-dynamic-includes\n---\n\n# Customizing JSPs with Dynamic Includes\n\n[TOC levels=1-4]\n\nThe\n[`liferay-util:dynamic-include` tag](@platform-ref@/7.2-latest/taglibs/util-taglib/liferay-util/dynamic-include.html) \nis placeholder into which you can inject content. Every JSP's dynamic include \ntag is an extension point for inserting content (e.g., JavaScript code, HTML, \nand more). To do this, create a module that has content you want to insert, \nregister that content with the dynamic include tag, and deploy your module. \n\n| **Note**: If the JSP you want to customize has no `liferay-util:dynamic-include`\n| tags (or tags whose classes inherit from `IncludeTag`), you must use a\n| different customization approach, such as\n| [portlet filters](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters).\n\nBlogs entries contain a good example of how dynamic includes work.  For\nreference, you can download the [example\nmodule](https://portal.liferay.dev/documents/113763090/114000186/example-dynamic-include-blogs-master.zip). \n\n\nFollow these steps:\n\n1.  Find the `liferay-util:dynamic-include` tag where you want to insert content \n    and note the tag's key. \n\n    The Blogs app's `view_entry.jsp` has a dynamic include tag at the top and \n    another at the very bottom. \n\n    ```markup\n    <%@ include file=\"/blogs/init.jsp\" %>\n\n    <liferay-util:dynamic-include key=\"com.liferay.blogs.web#/blogs/view_entry.jsp#pre\" />\n\n        ... JSP content is here\n\n    <liferay-util:dynamic-include key=\"com.liferay.blogs.web#/blogs/view_entry.jsp#post\" />\n    ```\n\n    Here are the Blogs view entry dynamic include keys:\n\n    - `key=\"com.liferay.blogs.web#/blogs/view_entry.jsp#pre\"`\n    - `key=\"com.liferay.blogs.web#/blogs/view_entry.jsp#post\"`\n\n2.  [Create a module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project)\n    (e.g., `blade create my-dynamic-include`). The module will \n    hold your dynamic include implementation. \n\n3.  Specify compile-only dependencies, like these Gradle dependencies, in your \n    module build file:\n\n    ```properties\n    dependencies {\n    \tcompileOnly group: \"javax.portlet\", name: \"portlet-api\", version: \"2.0\"\n    \tcompileOnly group: \"javax.servlet\", name: \"javax.servlet-api\", version: \"3.0.1\"\n    \tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\", version: \"1.0.0\"\n    \tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"2.0.0\"\n    \tcompileOnly group: \"org.osgi\", name: \"osgi.cmpn\", version: \"6.0.0\"\n    }\n    ```\n\n4.  Create an OSGi component class that implements the \n    [`DynamicInclude` interface](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/servlet/taglib/DynamicInclude.html). \n\n    Here's an example dynamic include implementation for Blogs:\n\n    ```java\n    import java.io.IOException;\n    import java.io.PrintWriter;\n\n    import javax.servlet.http.HttpServletRequest;\n    import javax.servlet.http.HttpServletResponse;\n\n    import org.osgi.service.component.annotations.Component;\n\n    import com.liferay.portal.kernel.servlet.taglib.DynamicInclude;\n\n    @Component(\n    \timmediate = true,\n    \tservice = DynamicInclude.class\n    )\n    public class BlogsDynamicInclude implements DynamicInclude {\n\n    \t@Override\n    \tpublic void include(\n    \t\t\tHttpServletRequest request, HttpServletResponse response,\n    \t\t\tString key)\n    \t\tthrows IOException {\n\n    \t\tPrintWriter printWriter = response.getWriter();\n\n    \t\tprintWriter.println(\n    \t\t\t\"<h2>Added by Blogs Dynamic Include!</h2><br />\");\n    \t}\n\n    \t@Override\n    \tpublic void register(DynamicIncludeRegistry dynamicIncludeRegistry) {\n    \t\tdynamicIncludeRegistry.register(\n    \t\t\t\"com.liferay.blogs.web#/blogs/view_entry.jsp#pre\");\n    \t}\n\n    }\n    ```\n\n    Giving the class an `@Component` annotation that has the service attribute \n    `service = DynamicInclude.class` makes the class a `DynamicInclude` service \n    component. \n\n    ```java\n    @Component(\n        immediate = true,\n        service = DynamicInclude.class\n    )\n    ```\n\n    In the `include` method, add your content. The example `include` method \n    writes a heading. \n\n    ```java\n    @Override\n    public void include(\n            HttpServletRequest request, HttpServletResponse response,\n            String key)\n        throws IOException {\n\n        PrintWriter printWriter = response.getWriter();\n\n        printWriter.println(\n        \"<h2>Added by Blogs Dynamic Include!</h2><br />\");\n    }\n    ```\n\n    In the `register` method, specify the dynamic include tag to use. The \n    example register method targets the dynamic include at the top of the Blogs \n    `view_entry.jsp`. \n\n    ```java\n    @Override\n    public void register(DynamicIncludeRegistry dynamicIncludeRegistry) {\n    \tdynamicIncludeRegistry.register(\n    \t\t\"com.liferay.blogs.web#/blogs/view_entry.jsp#pre\");\n    }\n    ```\n\nOnce you've [deployed your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project), \nthe JSP dynamically includes your content. Congratulations on injecting dynamic \ncontent into a JSP! \n"
  },
  {
    "path": "en/developer/customization/articles/50-customizing-jsps/03-jsp-overrides-using-portlet-filters.markdown",
    "content": "---\nheader-id: jsp-overrides-using-portlet-filters\n---\n\n# JSP Overrides Using Portlet Filters\n\n[TOC levels=1-4]\n\nPortlet filters let you intercept portlet requests before they're processed and \nportlet responses after they're processed but before they're sent back to the \nclient. You can operate on the request and / or response to modify the JSP \ncontent. Unlike dynamic includes, portlet filters give you access to all the \ncontent sent back to the client. \n\nThis demonstration uses a portlet filter to modify content in Liferay's Blogs\nportlet. For reference, you can download the [example\nmodule](https://portal.liferay.dev/documents/113763090/114000186/example-portlet-filter-customize-jsp-master.zip). \n\nFollow these steps:\n\n1.  Create a new module and make sure it specifies these compile-only\n    dependencies, shown here in Gradle format:\n\n    ```properties\n    dependencies {\n        compileOnly group: \"javax.portlet\", name: \"portlet-api\", version: \"2.0\"\n        compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\", version: \"3.0.1\"\n        compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"2.0.0\"\n        compileOnly group: \"org.osgi\", name: \"osgi.cmpn\", version: \"6.0.0\"\n    }\n    ```\n\n2.  Create an OSGi component class that implements the \n    `javax.portlet.filter.RenderFilter` interface. \n\n    Here's an example portlet filter implementation for Blogs:\n\n    ```java\n    import java.io.IOException;\n\n    import javax.portlet.PortletException;\n    import javax.portlet.RenderRequest;\n    import javax.portlet.RenderResponse;\n    import javax.portlet.filter.FilterChain;\n    import javax.portlet.filter.FilterConfig;\n    import javax.portlet.filter.PortletFilter;\n    import javax.portlet.filter.RenderFilter;\n    import javax.portlet.filter.RenderResponseWrapper;\n\n    import org.osgi.service.component.annotations.Component;\n\n    import com.liferay.portal.kernel.util.PortletKeys;\n\n    @Component(\n        immediate = true,\n        property = {\n                \"javax.portlet.name=\" + PortletKeys.BLOGS\n        },\n        service = PortletFilter.class\n    )\n    public class BlogsRenderFilter implements RenderFilter {\n\n        @Override\n        public void init(FilterConfig config) throws PortletException {\n\n        }\n\n        @Override\n        public void destroy() {\n\n        }\n\n        @Override\n        public void doFilter(RenderRequest request, RenderResponse response, FilterChain chain)\n                throws IOException, PortletException {\n            \n            RenderResponseWrapper renderResponseWrapper = new BufferedRenderResponseWrapper(response);\n\n            chain.doFilter(request, renderResponseWrapper);\n\n            String text = renderResponseWrapper.toString();\n            \n            if (text != null) {\n                String interestingText = \"<input  class=\\\"field form-control\\\"\";\n\n                int index = text.lastIndexOf(interestingText);\n\n                if (index >= 0) {\n                    String newText1 = text.substring(0, index);\n                    String newText2 = \"\\n<p>Added by Blogs Render Filter!</p>\\n\";\n                    String newText3 = text.substring(index);\n                    \n                    String newText = newText1 + newText2 + newText3;\n                    \n                    response.getWriter().write(newText);\n                }\n            }\n        }\n\n    }\n    ```\n\n3.  Make your class a `PortletFilter` service component by giving it the \n    `@Component` annotation that has the service attribute `service \n    = PortletFilter.class`. Target the portlet whose content you're overriding \n    by assigning it a javax.portlet.name property that's the same as your \n    portlet's key. Here's the example `@Component` annotation:\n\n    ```java\n    @Component(\n       immediate = true,\n       property = {\n               \"javax.portlet.name=\" + PortletKeys.BLOGS\n       },\n       service = PortletFilter.class\n    )\n    ```\n\n4.  Override the `doFilterMethod` to operate on the request or response to \n    produce the content you want. The example appends a paragraph stating `Added \n    by Blogs Render Filter!` to the portlet content:\n\n    ```java\n    @Override\n    public void doFilter(RenderRequest request, RenderResponse response, FilterChain chain)\n            throws IOException, PortletException {\n\n        RenderResponseWrapper renderResponseWrapper = new BufferedRenderResponseWrapper(response);\n\n        chain.doFilter(request, renderResponseWrapper);\n\n        String text = renderResponseWrapper.toString();\n\n        if (text != null) {\n            String interestingText = \"<input  class=\\\"field form-control\\\"\";\n\n            int index = text.lastIndexOf(interestingText);\n\n            if (index >= 0) {\n                String newText1 = text.substring(0, index);\n                String newText2 = \"\\n<p>Added by Blogs Render Filter!</p>\\n\";\n                String newText3 = text.substring(index);\n\n                String newText = newText1 + newText2 + newText3;\n\n                response.getWriter().write(newText);\n            }\n        }\n    }\n    ```\n\n    The example uses a `RenderResponseWrapper` extension class called \n    `BufferedRenderResponseWrapper`. `BufferedRenderResponseWrapper` is a helper \n    class whose `toString` method returns the current response text and whose \n    `getWriter` method lets you write data to the response before it's sent back \n    to the client. \n\n    ```java\n    import java.io.CharArrayWriter;\n    import java.io.IOException;\n    import java.io.OutputStream;\n    import java.io.PrintWriter;\n\n    import javax.portlet.RenderResponse;\n    import javax.portlet.filter.RenderResponseWrapper;\n\n    public class BufferedRenderResponseWrapper extends RenderResponseWrapper {\n\n        public BufferedRenderResponseWrapper(RenderResponse response) {\n            super(response);\n\n            charWriter = new CharArrayWriter();\n        }\n\n        public OutputStream getOutputStream() throws IOException {\n            if (getWriterCalled) {\n                throw new IllegalStateException(\"getWriter already called\");\n            }\n\n            getOutputStreamCalled = true;\n\n            return super.getPortletOutputStream();\n        }\n\n        public PrintWriter getWriter() throws IOException {\n            if (writer != null) {\n                return writer;\n            }\n\n            if (getOutputStreamCalled) {\n                throw new IllegalStateException(\"getOutputStream already called\");\n            }\n\n            getWriterCalled = true;\n\n            writer = new PrintWriter(charWriter);\n\n            return writer;\n        }\n\n        public String toString() {\n            String s = null;\n\n            if (writer != null) {\n                s = charWriter.toString();\n            }\n\n            return s;\n        }\n\n        protected CharArrayWriter charWriter;\n        protected PrintWriter writer;\n        protected boolean getOutputStreamCalled;\n        protected boolean getWriterCalled;\n\n    }\n    ```\n\nOnce you've [deployed your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project), \nthe portlet's JSP shows your custom content. \n\nYour portlet filter operates directly on portlet response content. Unlike \ndynamic includes, portlet filters let you work with all of a JSP's content. \n"
  },
  {
    "path": "en/developer/customization/articles/50-customizing-jsps/04-jsp-overrides-using-fragments.markdown",
    "content": "---\nheader-id: jsp-overrides-using-osgi-fragments\n---\n\n# JSP Overrides Using OSGi Fragments\n\n[TOC levels=1-4]\n\nYou can completely override JSPs using OSGi fragments. This approach is powerful \nbut can make things unstable when the host module is upgraded: \n\n1.  By overriding an entire JSP, you might not account for new content or new \n    widgets essential to new host module versions. \n2.  Fragments are tied to a specific host module version. If the host module is \n    upgraded, the fragment detaches from it. In this scenario, the original \n    JSPs are still available and the module is functional (but lacks your JSP \n    enhancements). \n3.  Liferay cannot guarantee that JSPs overridden by fragments can be upgraded. \n\nUsing OSGi fragments to override JSPs is a bad practice, equivalent to using Ext \nplugins to customize @product@. They should only be used as a last resort. \nLiferay's API based approaches to overriding JSPs (i.e., [Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps-with-dynamic-includes)\nand [Portlet Filters](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters)), \non the other hand, provide more stability as they customize specific parts of \nJSPs that are safe to override. Also, the API based approaches don't limit your \noverride to a specific host module version. If you are maintaining existing JSP \noverrides that use OSGi fragments, however, this tutorial explains how they \nwork. \n\nAn OSGi fragment that overrides a JSP requires these two things:\n\n-  The host module's symbolic name and version in the OSGi header \n   `Fragment-Host` declaration.\n\n-  The original JSP with any modifications you need to make.\n\nFor more information about fragment modules, you can refer to section 3.14 of\nthe \n[OSGi Alliance's core specification document](https://osgi.org/specification/osgi.core/7.0.0/framework.module.html).\n\n## Declaring a Fragment Host\n\nThere are two players in this game: the fragment and the host. The fragment is \na parasitic module that attaches itself to a host. That sounds harsh, so let's \ncompare the fragment-host relationship to the relationship between a pilot fish \nand a huge, scary shark. It's symbiotic, really. Your fragment module benefits \nby not doing much work (like the pilot fish who benefits from the shark's \nhunting prowess). In return, the host module gets whatever benefits you've \nconjured up in your fragment's JSPs (for the shark, it gets free dental \ncleanings!). To the OSGi runtime, your fragment is part of the host module. \n\nYour fragment must declare two things to the OSGi runtime regarding the host \nmodule:\n\n1.  The Bundle Symbolic Name of the host module. This is the module containing \n    the original JSP.\n\n2.  The exact version of the host module to which the fragment belongs.\n\nBoth are declared using the OSGi manifest header `Fragment-Host`.\n\n```properties\nFragment-Host: com.liferay.login.web;bundle-version=\"[1.0.0,1.0.1)\"\n```\n\nSupplying a specific host module version is important. If that version of the \nmodule isn't present, your fragment won't attach itself to a host, and that's a \ngood thing. A new version of the host module might have changed its JSPs, so if \nyour now-incompatible version of the JSP is applied to the host module, you'll \nbreak the functionality of the host. It's better to detach your fragment \nand leave it lonely in the OSGi runtime than it is to break the functionality of \nan entire application. \n\n## Provide the Overridden JSP\n\nThere are two possible naming conventions for targeting the host original JSP: \n`portal` or `original`. For example, if the original JSP is in the folder \n`/META-INF/resources/login.jsp`, then the fragment bundle should contain a JSP \nwith the same path, using the following pattern:\n\n```markup\n<liferay-util:include \n    page=\"/login.original.jsp\" (or login.portal.jsp) \n    servletContext=\"<%= application %>\" \n/>\n```\n\nAfter that, make your modifications. Just make sure you mimic the host module's \nfolder structure when overriding its JAR. If you're overriding Liferay's login \napplication's `login.jsp` for example, you'd put your own `login.jsp` in \n\n```markup\nmy-jsp-fragment/src/main/resources/META-INF/resources/login.jsp\n```\n\nIf you must post-process the output, you can update the pattern to include \n@product@'s buffering mechanism. Below is an example that overrides the original \n`create_account.jsp`:\n\n```markup\n<%@ include file=\"/init.jsp\" %>\n\n<liferay-util:buffer var=\"html\">\n    <liferay-util:include page=\"/create_account.portal.jsp\" \n    servletContext=\"<%= application %>\"/>\n</liferay-util:buffer>\n\n<liferay-util:buffer var=\"openIdFieldHtml\"><aui:input name=\"openId\" \ntype=\"hidden\" value=\"<%= ParamUtil.getString(request, \"openId\") %>\" />\n</liferay-util:buffer>\n\n<liferay-util:buffer var=\"userNameFieldsHtml\"><liferay-ui:user-name-fields />\n</liferay-util:buffer>\n\n<liferay-util:buffer var=\"errorMessageHtml\">\n    <liferay-ui:error \n    exception=\"<%= com.liferay.portal.kernel.exception.NoSuchOrganizationException.class %>\" message=\"no-such-registration-code\" />\n</liferay-util:buffer>\n\n<liferay-util:buffer var=\"registrationCodeFieldHtml\">\n            <aui:input name=\"registrationCode\" type=\"text\" value=\"\">\n                    <aui:validator name=\"required\" />\n            </aui:input>\n</liferay-util:buffer>\n\n<%\n    html = com.liferay.portal.kernel.util.StringUtil.replace(html, \n      openIdFieldHtml, openIdFieldHtml + errorMessageHtml);\n    html = com.liferay.portal.kernel.util.StringUtil.replace(html, \n      userNameFieldsHtml, userNameFieldsHtml + registrationCodeFieldHtml);\n%>\n\n<%=html %>\n```\n\n## Using Fragment Host Internal Packages\n\nTo use an internal (unexported) host package, the fragment must explicitly\nexclude the package from its `Import-Package:` manifest header. For example,\nthis `Import-Package` header excludes packages that match\n`com.liferay.portal.search.web.internal.*`.\n\n```groovy\nImport-Package: !com.liferay.portal.search.web.internal.*,*\n```\n\nUnless you explicitly exclude the package, bnd adds the package to the\n`Import-Package:` header. Attempting to start the fragment while requiring an\nunexported package fails because the package is an unresolved requirement. For\nthis reason, make sure to exclude such packages from your fragment's\n`Import-Package:` header. \n\nEach fragment has full access to the host packages, including its internal\n(unexported) packages already. \n\nNow you can easily modify the JSPs of any application in Liferay.\n\n![Figure 1: Liferay's applications are swimming in the OSGi runtime, waiting for your fragment modules to clean their teeth, so to speak.](../../images/sharks.jpg)\n<!--https://commons.wikimedia.org/wiki/File:Carcharhinus_perezi_bahamas_feeding.jpg-->\n\n<!--\nAdd back once sample is ported:\n\nsee the [Module JSP Override sample project](/docs/7-2/reference/-/knowledge_base/r/module-jsp-override) \nfor an example of a JSP-modifying fragment in action.\n--> \n\n\n## Related Topics\n\n- [JSP Overrides Using Portlet Filters](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters)\n"
  },
  {
    "path": "en/developer/customization/articles/50-customizing-jsps/05-core-jsp-overrides-using-customjspbag.markdown",
    "content": "---\nheader-id: jsp-overrides-using-custom-jsp-bag\n---\n\n# JSP Overrides Using Custom JSP Bag\n\n[TOC levels=1-4]\n\nLiferay's API based approaches to overriding JSPs (i.e., [Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps-with-dynamic-includes) \nand [Portlet Filters](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters)) \nare the best way to override JSPs in apps and in the core. You can also use \nCustom JSP Bags to override core JSPs. But the approach is not as stable as the \nAPI based approaches. If your Custom JSP Bag's JSP is buggy (because of your \ncode or because of a change in Liferay), you are most likely to find out at \nruntime, where functionality breaks and nasty log errors greet you. Using \nCustom JSP Bags to override JSPs is a bad practice, equivalent to using Ext \nplugins to customize @product@. If you're maintaining existing Custom JSP Bags, \nhowever, this tutorial explains how they work. \n\n| **Important:** Liferay cannot guarantee that JSPs overridden using Custom JSP \n| Bag can be upgraded.\n\nA Custom JSP Bag module must satisfy these criteria: \n\n-   Provides and specifies a custom JSP for the JSP you're extending.\n\n-   Includes a [`CustomJspBag`](@platform-ref@/7.2-latest/javadocs/portal-impl/com/liferay/portal/deploy/hot/CustomJspBag.html) \n    implementation for serving the custom JSPs.\n\nThe module provides transportation for this code into Liferay's OSGi runtime. \nAfter you [create your new module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project), \ncontinue with providing your custom JSP. \n\n## Providing a Custom JSP\n\nCreate your JSPs to override @product@ core JSPs. If you're using the Maven \n[Standard Directory Layout](https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html),\nplace your JSPs under `src/main/resources/META-INF/jsps`. For example, if you're \noverriding \n\n    portal-web/docroot/html/common/themes/bottom-ext.jsp \n\nplace your custom JSP at\n\n    [your module]/src/main/resources/META-INF/jsps/html/common/themes/bottom-ext.jsp\n\n| **Note:** If you place custom JSPs somewhere other than\n| `src/main/resources/META-INF/jsps` in your module, assign that location to a\n| `-includeresource: META-INF/jsps=` directive in your module's `bnd.bnd` file.\n| For example, if you place custom JSPs in a folder `src/META-INF/custom_jsps` in\n| your module, specify this in your `bnd.bnd`:\n| \n|     -includeresource: META-INF/jsps=src/META-INF/custom_jsps\n\n## Implement a Custom JSP Bag\n\n@product@ (specifically the [`CustomJspBagRegistryUtil` class](@platform-ref@/7.2-latest/javadocs/portal-impl/com/liferay/portal/deploy/hot/CustomJspBagRegistryUtil.html)) \nloads JSPs from [`CustomJspBag`](@platform-ref@/7.2-latest/javadocs/portal-impl/com/liferay/portal/deploy/hot/CustomJspBag.html)\nservices. Here are steps for implementing a custom JSP bag. \n\n1.  In your module, create a class that implements [`CustomJspBag`](@platform-ref@/7.2-latest/javadocs/portal-impl/com/liferay/portal/deploy/hot/CustomJspBag.html).\n\n2.  Register your class as an OSGi service by adding an `@Component` annotation \n    to it, like this: \n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n        \t\"context.id=BladeCustomJspBag\",\n          \"context.name=Test Custom JSP Bag\",\n        \t\"service.ranking:Integer=100\"\n        }\n    )\n    ```\n\n    - **`immediate = true`:** Makes the service available on module activation. \n    -  **`context.id`:** Your custom JSP bag class name. Replace \n    `BladeCustomJspBag` with your class name.\n    -  **`context.name`:** A more human readable name for your service. Replace \n    it with a name of your own. \n    -  **`service.ranking:Integer`:** A priority for your implementation. The\n    container chooses the implementation with the highest priority.\n\n3.  Implement the `getCustomJspDir` method to return the folder path in your \n    module's JAR  where the JSPs reside (for example, `META-INF/jsps`). \n\n    ```java\n    @Override\n    public String getCustomJspDir() {\n        return \"META-INF/jsps/\";\n    }\n    ```\n\n4.  Create an `activate` method and the following fields. The method adds the \n    URL paths of all your custom JSPs to a list when the module is activated.\n\n    ```java\n    @Activate\n    protected void activate(BundleContext bundleContext) {\n    \t_bundle = bundleContext.getBundle();\n\n    \t_customJsps = new ArrayList<>();\n\n    \tEnumeration<URL> entries = _bundle.findEntries(\n    \t\tgetCustomJspDir(), \"*.jsp\", true);\n\n    \twhile (entries.hasMoreElements()) {\n    \t\tURL url = entries.nextElement();\n\n    \t\t_customJsps.add(url.getPath());\n    \t}\n    }\n\n    private Bundle _bundle;\n    private List<String> _customJsps;\n    ```\n\n5.  Implement the `getCustomJsps` method to return the list of this module's \n    custom JSP URL paths.\n\n    ```java\n    @Override\n    public List<String> getCustomJsps() {\n        return _customJsps;\n    }\n    ```\n\n6.  Implement the `getURLContainer` method to return a new \n    `com.liferay.portal.kernel.url.URLContainer`. Instantiate the URL container \n    and override its `getResources` and `getResource` methods. The \n    `getResources` method looks up all the paths to resources in the container \n    by a given path. It returns a `HashSet` of `Strings` for the matching custom \n    JSP paths. The `getResource` method returns one specific resource by its \n    name (the path included).\n\n    ```java\n    @Override\n    public URLContainer getURLContainer() {\n        return _urlContainer;\n    }\n\n    private final URLContainer _urlContainer = new URLContainer() {\n\n        @Override\n        public URL getResource(String name) {\n            return _bundle.getEntry(name);\n        }\n\n        @Override\n        public Set<String> getResources(String path) {\n            Set<String> paths = new HashSet<>();\n\n            for (String entry : _customJsps) {\n                if (entry.startsWith(path)) {\n                   paths.add(entry);\n                }\n            }\n\n            return paths;\n        }\n\n    };\n    ```\n\n7.  Implement the `isCustomJspGlobal` method to return `true`.\n\n    ```java\n    @Override\n    public boolean isCustomJspGlobal() {\n        return true;\n    }\n    ```\n\nNow your module provides custom JSPs and a custom JSP bag implementation. When \nyou deploy it, @product@ uses its custom JSPs in place of the core JSPs they \noverride. \n\n## Extend a JSP\n\nIf you want to add something to a core JSP, see if it has an empty `-ext.jsp` \nand override that instead of the whole JSP. It keeps things simpler and more \nstable, since the full JSP might change significantly, breaking your \ncustomization in the process. By overriding the `-ext.jsp`, you're only relying \non the original JSP including the `-ext.jsp`. For an example, open \n`portal-web/docroot/html/common/themes/bottom.jsp`, and scroll to the end. \nYou'll see this:\n\n```markup\n<liferay-util:include page=\"/html/common/themes/bottom-ext.jsp\" />\n```\n\nIf you must add something to `bottom.jsp`, override `bottom-ext.jsp`. \n\nSince @product@ 7.0, the content from the following JSP files formerly in \n`html/common/themes` are inlined to improve performance.\n \n- `body_bottom-ext.jsp`\n- `body_top-ext.jsp`\n- `bottom-ext.jsp`\n- `bottom-test.jsp`\n\nThey're no longer explicit files in the code base. But you can still create them \nin your module to add functionality and content. \n\nRemember, this type of customization is a last resort. Your override may break \ndue to the nature of this implementation, and core functionality in Liferay can \ngo down with it. If the JSP you want to override is in another module, refer to \nthe API based approaches to overriding JSPs mentioned at the beginning of the \narticle. \n\n## Site Scoped JSP Customization\n\nIn Liferay Portal 6.2, you could use [Application Adapters](/docs/6-2/tutorials/-/knowledge_base/t/customizing-sites-and-site-templates-with-application-adapters) \nto scope your core JSP customizations to a specific Site. Since the majority of \nJSPs were moved into modules for @product@ 7.0, the use case for this has shrunk \nconsiderably. If you must scope a core JSP customization to a Site, prepare an \napplication adapter [as you would have for Liferay Portal 6.2](/docs/6-2/tutorials/-/knowledge_base/t/customizing-sites-and-site-templates-with-application-adapters), \nand deploy it to @product-ver@. It will still work. However, note that this \napproach is deprecated in @product-ver@ and won't be supported at all in Liferay \n8.0. \n\n<!-- Uncomment once we cover scoping to a site\nIf you're interested in scoping a module's JSP customization to a site, that's\nanother story. See the documentation on [using Dynamic Include](/docs/7-1/tutorials/-/knowledge_base/t/customizing-jsps-with-dynamic-includes).\n-->\n\n## Related Topics\n\n- [Upgrading Core JSP Hooks](/docs/7-1/tutorials/-/knowledge_base/t/upgrading-core-jsp-hooks)\n- [JSP Overrides Using Portlet Filters](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters)\n"
  },
  {
    "path": "en/developer/customization/articles/50-customizing-jsps/06-overriding-inline-content-using-jsps.markdown",
    "content": "---\nheader-id: overriding-inline-content-using-jsps\n---\n\n# Overriding Inline Content Using JSPs\n\n[TOC levels=1-4]\n\nSome @product@ core content, such as tag library tags, can only be overridden \nusing JSPs ending in `.readme`. The suffix `.readme` facilitates finding them. \nThe code from these JSPs is now inlined (brought into @product@ Java source \nfiles) to improve performance. @product@ ignores JSP files with the `.readme` \nsuffix. If you add code to a JSP `.readme` file and remove the `.readme` suffix, \n@product@ uses that JSP instead of the core inline content. This tutorial shows \nyou how to make these customizations. \n\n| **Important:** This type of customization is a last resort. Your override may \n| break due to the nature of this implementation, and core functionality can go \n| down with it. Liferay cannot guarantee that content overridden using JSP \n| `.readme` files can be upgraded. \n\n| **Warning:** Modifying a @product@ tag library tag affects all uses of that tag \n| in your @product@ installation. \n\nHere's how to override inline content using JSPs:\n\n1.  Create a [Custom JSP Bag](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-custom-jsp-bag)\n    for deploying your JSP. Note the module folder you're storing the JSPs in: \n    the default folder is `[your module]/src/main/resources/META-INF/jsps/`\n\n    | **Note:** you can develop your JSP anywhere, but a Custom JSP Bag module \n    | provides a straightforward way to build and deploy it.\n\n2.  Download the @product@ source code or browse the source code on \n    [GitHub (Liferay Portal CE)](https://github.com/liferay/liferay-portal/tree/7.2.x). \n\n3.  Search the source code for a `.jsp.readme` file that overrides the tag \n    you're customizing. \n\n    | **Note:** Files ending in `-ext.jsp.readme` let you prepend or\n    | append new content to existing content. Examples include the\n    | `bottom-test.jsp.readme`, `bottom-ext.jsp.readme`,\n    | `body_top-ext.jsp.readme`, and `body_bottom-ext.jsp.readme` files in\n    | the @product@ application's `portal-web/docroot/html/common/themes` folder.\n\n4.  Copy the `.jsp.readme` file into your project and drop the `.readme` suffix. \n    Use the same relative file path @product@ uses for the `.jsp.readme` file. \n    For example, if the file in @product@ is\n\n        portal-web/docroot/html/taglib/aui/fieldset/start.jsp.readme\n\n    use file path \n\n        [your module]/src/main/resources/META-INF/jsps/html/taglib/aui/fieldset/start.jsp\n\n5.  Familiarize yourself with the current UI content and logic, so you can \n    override it appropriately. Tag library tag content logic, for example, is in \n    the respective `*Tag.java` file under \n    `util-taglib/src/com/liferay/taglib/[tag library]/`. \n\n6.  Develop your new logic, keeping in mind the current inline logic you're \n    replacing. \n\n7.  Deploy your JSP. \n\n@product@ uses your JSP in place of the current inline logic. If you want \nto walk through an example override, continue with this tutorial. Otherwise, \ncongratulations on a modified `.jsp.readme` file to override core inline \ncontent! \n\n## Example: Overriding the fieldset Taglib Tag\n\nThis example demonstrates changing the `liferay:aui` tag library's `fieldset` \ntag. Browsing the @product@ web application or the source code at \n`portal-web/docroot/html/taglib/aui/fieldset` reveals these files:\n\n- `start.jsp.readme`\n- `end.jsp.readme` \n\nThey can override the logic that creates the start and end of the `fieldset` \ntag. The `FieldsetTag.java` class's `processStart` and `processEnd` methods \nimplement the current inline content. Here's the \n[`processStart`](https://github.com/liferay/liferay-portal/blob/7.2.x/util-taglib/src/com/liferay/taglib/aui/FieldsetTag.java#L86-L141)\nmethod:\n\n```java\n@Override\nprotected int processStartTag() throws Exception {\n  JspWriter jspWriter = pageContext.getOut();\n\n  jspWriter.write(\"<fieldset class=\\\"fieldset \");\n  jspWriter.write(GetterUtil.getString(getCssClass()));\n  jspWriter.write(\"\\\" \");\n\n  String id = getId();\n\n  if (id != null) {\n    jspWriter.write(\"id=\\\"\");\n    jspWriter.write(id);\n    jspWriter.write(\"\\\" \");\n  }\n\n  jspWriter.write(\n    InlineUtil.buildDynamicAttributes(getDynamicAttributes()));\n\n  jspWriter.write(StringPool.GREATER_THAN);\n\n  String lable = getLabel();\n\n  if (lable != null) {\n    jspWriter.write(\n      \"<legend class=\\\"fieldset-legend\\\"><span class=\\\"legend\\\">\");\n\n    MessageTag messageTag = new MessageTag();\n\n    messageTag.setKey(lable);\n    messageTag.setLocalizeKey(getLocalizeLabel());\n\n    messageTag.doTag(pageContext);\n\n    String helpMessage = getHelpMessage();\n\n    if (helpMessage != null) {\n      IconHelpTag iconHelpTag = new IconHelpTag();\n\n      iconHelpTag.setMessage(helpMessage);\n\n      iconHelpTag.doTag(pageContext);\n    }\n\n    jspWriter.write(\"</span></legend>\");\n  }\n\n  if (getColumn()) {\n    jspWriter.write(\"<div class=\\\"row\\\">\");\n  }\n  else {\n    jspWriter.write(\"<div class=\\\"\\\">\");\n  }\n\n  return EVAL_BODY_INCLUDE;\n}\n```\n\nThe code above does this:\n\n1.  Write `<fieldset class=\\\"fieldset `starting tag. \n\n2.  Write the CSS class name attribute. \n\n3.  If the tag has an ID, add the `id` as an attribute. \n\n4.  Write the tag's dynamic attribute (map). \n\n5.  Close the starting `fieldset` tag. \n\n6.  Get the tag's `label` attribute. \n\n7.  Write the starting `legend` element. \n\n8.  Use `getLocalizeLabel()` to add the localized label in the `legend`.\n\n9.  If there's a help message (retrieved from `getHelpMessage()`), write it in \n    an `icon-help-tag`. \n\n10. Write the closing `legend` tag. \n\n11. If there's a column attribute, write `<div class=\\\"row\\\">`; else write \n    `<div class=\\\"\\\">`. \n\nReplicating the current logic in your custom JSP helps you set up the tag \nproperly for customizing. The `init.jsp` for `fieldset` initializes all the \nvariables required to create the starting tag. You can use the variables in the \n`start.jsp`. The logic from `FieldsetTag`'s `processStart` method converted to \nJSP code for `start.jsp` (renamed from `start.jsp.readme`) would look like this:\n\n```markup\n<%@ include file=\"/html/taglib/aui/fieldset/init.jsp\" %>\n\n<fieldset class=\"fieldset <%= cssClass %>\" <%= Validator.isNotNull(id) ? \"id=\\\"\" + id + \"\\\"\" : StringPool.BLANK %> <%= InlineUtil.buildDynamicAttributes(dynamicAttributes) %>>\n\t<c:if test=\"<%= Validator.isNotNull(label) %>\">\n\t\t<legend class=\"fieldset-legend\">\n\t\t\t<span class=\"legend\">\n\t\t\t\t<liferay-ui:message key=\"<%= label %>\" localizeKey=\"<%= localizeLabel %>\" />\n\n\t\t\t\t<c:if test=\"<%= Validator.isNotNull(helpMessage) %>\">\n\t\t\t\t\t<liferay-ui:icon-help message=\"<%= helpMessage %>\" />\n\t\t\t\t</c:if>\n\t\t\t</span>\n\t\t</legend>\n\t</c:if>\n\n\t<div class=\"<%= column ? \"row\" : StringPool.BLANK %>\">\n```\n\n| **Tip:** A `*Tag.java` file's history might reveal original JSP code that was \n| inlined. For example, the logic from `fieldset` tag's \n| [`start.jsp`](https://github.com/liferay/liferay-portal/blob/df22ba66eff49b76404cfda908d3cd024efbebd9/portal-web/docroot/html/taglib/aui/fieldset/start.jsp)\n| was inlined in\n| [this commit](https://github.com/liferay/liferay-portal/commit/7fba0775bcc1d1a0bc4d107cabfb41a90f15937c#diff-2ad802b4c0d8f7a2da45b895e89d6e46).\n\nOn deploying the `start.jsp`, the `fieldset` tags render the same as they did \nbefore. This is expected because it uses the same logic as `FieldsetTag`'s \n`processStart` method. \n\n![Figure 1: @product@'s home page's search and sign in components are in a `fieldset`.](../../images/jsp-readme-inline-fieldset.png)\n\nThe `fieldset` starting logic is ready for customization. To test that this \nworks, you'll print the word *test* surrounded by asterisks before the end of \nthe `fieldset` tag's starting logic. Insert this line before the `start.jsp`'s \nlast `div` tag: \n\n```markup\n<c:out value=\"**********test**********\"/>\n```\n\nRedeploy the JSP and refresh the page to see the text printed above the \n`fieldset`'s fields. \n\n![Figure 2: Before the `fieldset`'s nested fields, it prints *test* surrounded by asterisks.](../../images/jsp-readme-override-inline-fieldset.png)\n\nYou know how to override specific @product@ core inline content using Liferay's \n`.jsp.readme` files. \n\n## Related Topics\n\n- [Customizing JSPs with Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps-with-dynamic-includes)\n- [JSP Overrides Using Portlet Filters](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters)\n"
  },
  {
    "path": "en/developer/customization/articles/50-customizing-widgets/01-intro.markdown",
    "content": "---\nheader-id: customizing-widgets\n---\n\n# Customizing Widgets\n\n[TOC levels=1-4]\n\nIt would be nice to apply display changes to specific widget instances without\nhaving to create a hook (e.g., HTML-related change) or change a theme (e.g.,\nCSS-related change). Ideally, you should be able to enable authorized users to\napply custom display interfaces to widgets.\n\nBe of good cheer! That's precisely what\n[Widget Templates](/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates)\nprovide. Now you can customize the way widgets appear on a page, removing\nlimitations to the way content is displayed. With Widget Templates, you can\ndefine display templates to render asset-centric widgets. Some default widgets\nalready have templating capabilities (e.g., *Web Content* and *Dynamic Data\nLists*), in which you can add as many display options (or templates) as you\nwant. You can also add them to your own applications.\n\nSome portlets that already support Widget Templates are\n\n- *Asset Publisher*\n- *Blogs*\n- *Breadcrumb*\n- *Categories Navigation*\n- *Language Selector*\n- *Media Gallery*\n- *Navigation Menu*\n- *RSS Publisher*\n- *Site Map*\n- *Tags Navigation*\n- *Wiki*\n\nTo leverage the Widget Template API, follow these steps: \n\n- register your portlet to use Widget Templates\n- define your display template definitions\n- define permissions\n- expose the Widget Template functionality to users\n\nThe detailed steps are in the\n[Implementing Widget Templates](/docs/7-2/customization/-/knowledge_base/c/implementing-widget-templates)\narticle. Here's a high level overview of what you'll do. \n\n## Implementing the TemplateHandler Interface\n\nTo register your portlet to use Widget Templates, you must implement the\n[`TemplateHandler`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/template/TemplateHandler.html)\ninterface. Read the interface's Javadoc for more information on each method\nprovided by the interface.\n\nEach of the methods in this class have a significant role in defining and\nimplementing Widget Templates for your custom portlet. The list below highlights\nsome of the methods defined specifically for Widget Templates:\n\n`getClassName()`: Defines the type of entry your portlet is rendering.\n\n`getName()`: Declares the name of your Widget Template type (typically,\nthe name of the portlet).\n\n`getResourceName()`: Specifies which resource is using the Widget\nTemplate (e.g., a portlet) for permission checking. This method must return\nthe portlet's fully qualified portlet ID (e.g.,\n`com_liferay_wiki_web_portlet_WikiPortlet`).\n\n`getTemplateVariableGroups()`: Defines the variables exposed in the\ntemplate editor.\n\n`getTemplatesConfigPath()`: Defines the configuration file containing the\ndisplay template definition.\n\nNext, you must define your display template definition(s).\n\n## Defining Display Template Definitions\n\nOnce you've registered your portlet to use Widget Templates, you should create\nthe display template definitions. These are used to style the content displayed\nin the widget.\n\nYou must create a `portlet-display-templates.xml` configuration file to define\nthe definitions and point to their styled templated (e.g., FreeMarker). Then\nyou must create the templates. These template definitions are available to apply\nfrom a widget's Configuration menu.\n\nNext, you define permissions for your portlet's Widget Templates.\n\n## Defining Permissions\n\nYou must define permissions for your Widget Templates; without permissions,\nanyone in the Site could access and change your widget's display templates.\nConfiguring permissions lets administrative users grant permissions only to the\nRoles that should create and manage display templates.\n\nThis is done by creating a `default.xml` file in your portlet defining the\npermissions you want to enforce, wiring it up with your portlet, and\nconfiguring them for use in @product@. You can visit\n[this article](/docs/7-2/customization/-/knowledge_base/c/implementing-widget-templates)\nfor step-by-step instructions on how to complete this.\n\nNext, you'll learn how to expose Widget Template selection for users.\n\n## Exposing the Widget Template Selection\n\nTo expose the Widget Template option to your users, use the\n`<liferay-ui:ddm-template-selector>` tag in the JSP file that controls\nyour portlet's configuration. This tag requires the following parameters:\n\n`className`: your entity's class name.\n\n`contextObjects`: accepts a `Map<String, Object>` with any object you want\nto the template context.\n\n`displayStyle`: your portlet's display style.\n\n`displayStyleGroupId`: your portlet's display style group ID.\n\n`entries`: accepts a list of your entities (e.g., `List<YourEntity>`).\n\nThe variables `displayStyle` and `displayStyleGroupId` are preferences that your\nportlet stores when you use this taglib and your portlet uses the\n[`BaseJSPSettingsConfigurationAction`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BaseJSPSettingsConfigurationAction.html)\nor\n[`DefaultConfigurationAction`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/DefaultConfigurationAction.html).\nOtherwise, you must obtain the value of those parameters and store them\nmanually in your configuration class.\n\n## Recommendations for Using Widget Templates\n\nYou can harness a lot of power by leveraging the Widget Template API. Be\ncareful, for with great power, comes great responsibility! Here are some\npractices you can use to optimize your portlet's performance and security.\n \nFirst let's talk about security. You may want to hide some classes or packages\nfrom the template context to limit the operations that Widget Templates can\nperform. @product@ provides some system settings, which can be accessed by\nnavigating to *Control Panel* &rarr; *Configuration* &rarr; *System Settings*\n&rarr; *Template Engines* &rarr; *FreeMarker Engine*, to define the restricted\nclasses, packages, and variables. In particular, you may want to add\n`serviceLocator` to the list of default values assigned to the FreeMarker Engine\nRestricted variables.\n\nWidget Templates introduce additional processing tasks when your portlet is\nrendered. To minimize negative effects on performance, make your templates as\nminimal as possible by focusing on their presentation, while using the existing\nAPI for complex operations. The best way to make Widget Templates efficient is\nto know your template context well, and understand what you can use from it.\nFortunately, you don't need to memorize the context information, thanks to\n@product@'s advanced template editor!\n\nTo navigate to the template editor for Widget Templates, go to the Site Admin\nmenu and select *Configuration* &rarr; *Widget Templates* and then click *Add*\nand select the specific portlet on which you decide to create a custom template.\n\nThe template editor provides fields, general variables, and utility variables\ncustomized for the portlet you chose. These variable references are on the\nleft-side panel of the template editor. Place your cursor where you want the\nvariable placed and click the desired variable to insert it. You can learn more\nabout the template editor in \n[Styling Widgets with Widget Templates](/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates).\n\nFinally, don't forget to run performance tests and tune the template cache\noptions by modifying the *Resource modification check* field in *System\nSettings* &rarr; *Template Engines* &rarr; *FreeMarker Engine*.\n\nWidget Templates provide power to your portlets by providing infinite ways of\nediting your portlet to create new interfaces for your users. Be sure to\nconfigure your FreeMarker templates appropriately for the most efficient\ncustomization process.\n\nContinue on to add support for Widget Templates in your portlet.\n"
  },
  {
    "path": "en/developer/customization/articles/50-customizing-widgets/02-implementing-widget-templates.markdown",
    "content": "---\nheader-id: implementing-widget-templates\n---\n\n# Implementing Widget Templates\n\n[TOC levels=1-4]\n\n[Widget Templates](/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates)\nare ways to customize how a widget looks. You can create templates for a\nwidget's display and then choose which template is active. \n\n![Figure 1: By using a custom display template, your portlet's display can be customized.](../../images/widget-template-dropdown.png)\n\nTo add Widget Template support to your portlet, follow the steps below.\n\n1.  Create and register a custom `*PortletDisplayTemplateHandler` component.\n    Liferay provides the\n    [`BasePortletDisplayTemplateHandler`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portletdisplaytemplate/BasePortletDisplayTemplateHandler.html)\n    as a base implementation for you to extend. You can check the\n    [`TemplateHandler`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/template/TemplateHandler.html)\n    interface Javadoc to learn about each template handler method.\n\n    The `@Component` annotation ties your handler to a specific portlet by\n    setting the property `javax.portlet.name` to your portlet's name. The same\n    property should be found in your portlet class. For example,\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"javax.portlet.name=\"+ AssetCategoriesNavigationPortletKeys.ASSET_CATEGORIES_NAVIGATION\n        },\n        service = TemplateHandler.class\n    )\n    ```\n\n    The Site Map widget sets the `@Component` annotation like this:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = \"javax.portlet.name=\" + SiteNavigationSiteMapPortletKeys.SITE_NAVIGATION_SITE_MAP,\n        service = TemplateHandler.class\n    )\n    public class SiteNavigationSiteMapPortletDisplayTemplateHandler\n        extends BasePortletDisplayTemplateHandler {\n    }\n    ```\n\n    You'll continue stepping through the Site map widget's `TemplateHandler`\n    implementation next.\n\n2.  Override the base class' `getClassName()`, `getName(...)`, and\n    `getResourceName()` methods:\n\n    ```java\n    @Override\n    public String getClassName() {\n        return LayoutSet.class.getName();\n    }\n\n    @Override\n    public String getName(Locale locale) {\n        String portletTitle = _portal.getPortletTitle(\n            SiteNavigationSiteMapPortletKeys.SITE_NAVIGATION_SITE_MAP,\n            ResourceBundleUtil.getBundle(locale, getClass()));\n\n        return LanguageUtil.format(locale, \"x-template\", portletTitle, false);\n    }\n\n    @Override\n    public String getResourceName() {\n        return SiteNavigationSiteMapPortletKeys.SITE_NAVIGATION_SITE_MAP;\n    }\n    ```\n\n    These methods return the template handler's class name, the template\n    handler's name (via\n    [resource bundle](/docs/7-2/frameworks/-/knowledge_base/f/localization)),\n    and the resource name associated with the Widget Template, respectively.\n\n3.  Override the `getTemplateVariableGroups(...)` method to return your widget\n    template's script variable groups. These are used to display hints in the\n    template editor palette.\n\n    ```java\n    @Override\n    public Map<String, TemplateVariableGroup> getTemplateVariableGroups(\n            long classPK, String language, Locale locale)\n        throws Exception {\n\n        Map<String, TemplateVariableGroup> templateVariableGroups =\n            super.getTemplateVariableGroups(classPK, language, locale);\n\n        TemplateVariableGroup templateVariableGroup =\n            templateVariableGroups.get(\"fields\");\n\n        templateVariableGroup.empty();\n\n        templateVariableGroup.addCollectionVariable(\n            \"pages\", List.class, PortletDisplayTemplateConstants.ENTRIES,\n            \"page\", Layout.class, \"curPage\", \"getName(locale)\");\n        templateVariableGroup.addVariable(\n            \"site-map-display-context\",\n            SiteNavigationSiteMapDisplayContext.class, \"siteMapDisplayContext\");\n\n        return templateVariableGroups;\n    }\n    ```\n\n    For this example, the *Pages* and *Site Map Display Context* fields are\n    added to the default variables in the template editor palette.\n\n    ![Figure 2: You can click a variable to add it to the template editor.](../../images/widget-template-fields.png)\n\n4.  Set your display template configuration file path:\n\n    ```java\n    @Override\n    protected String getTemplatesConfigPath() {\n        return \"com/liferay/site/navigation/site/map/web/portlet/template\" +\n            \"/dependencies/portlet-display-templates.xml\";\n    }\n    ```\n\n    This method returns the XML file containing the display template definitions\n    available for your portlet. You'll create this file next.\n\n5.  Create your `portlet-display-templates.xml` file to define your display\n    template definitions. For example,\n\n    ```xml\n    <?xml version=\"1.0\"?>\n\n    <root>\n        <template>\n            <template-key>site-map-multi-column-layout-ftl</template-key>\n            <name>portlet-display-template-name-multi-column-layout</name>\n            <description>portlet-display-template-description-multi-column-layout-sitemap</description>\n            <language>ftl</language>\n            <script-file>com/liferay/site/navigation/site/map/web/portlet/template/dependencies/portlet_display_template_multi_column_layout.ftl</script-file>\n            <cacheable>false</cacheable>\n        </template>\n    </root>\n    ```\n\n    This defined template option is read and presented to the user through the\n    widget's Configuration menu. Navigate to the Site Map widget's Configuration\n    menu and you can confirm the *Multi Column Layout* option is available.\n\n    ![Figure 3: You can choose the Widget Template you want to apply from the widget's Configuration menu.](../../images/widget-config-display.png)\n\n    This template is created using FreeMarker. You'll create this template\n    option next.\n\n6.  Create your template script file that you specified in the previous step.\n    For the Site Map widget, its Multi Column Layout option is configured in a\n    FreeMarker template like this:\n\n    ```markup\n    <#if entries?has_content>\n        <@liferay_aui.row>\n            <#list entries as entry>\n                <#if layoutPermission.containsWithoutViewableGroup(permissionChecker, entry, \"VIEW\")>\n                    <@liferay_aui.col width=25>\n                        <div class=\"results-header\">\n                            <h3>\n                                <a\n\n                                <#assign layoutType = entry.getLayoutType() />\n\n                                <#if layoutType.isBrowsable()>\n                                    href=\"${portalUtil.getLayoutURL(entry, themeDisplay)}\"\n                                </#if>\n\n                                >${entry.getName(locale)}</a>\n                            </h3>\n                        </div>\n\n                        <@displayPages\n                            depth=1\n                            pages=entry.getChildren(permissionChecker)\n                        />\n                    </@liferay_aui.col>\n                </#if>\n            </#list>\n        </@liferay_aui.row>\n    </#if>\n\n    <#macro displayPages\n        depth\n        pages\n    >\n        <#if pages?has_content && ((depth < displayDepth?number) || (displayDepth?number == 0))>\n            <ul class=\"child-pages\">\n                <#list pages as page>\n                    <li>\n                        <a\n\n                        <#assign pageType = page.getLayoutType() />\n\n                        <#if pageType.isBrowsable()>\n                            href=\"${portalUtil.getLayoutURL(page, themeDisplay)}\"\n                        </#if>\n\n                        >${page.getName(locale)}</a>\n\n                        <@displayPages\n                            depth=depth + 1\n                            pages=page.getChildren(permissionChecker)\n                        />\n                    </li>\n                </#list>\n            </ul>\n        </#if>\n    </#macro>\n    ```\n\n    This template definition enforces page permissions, formats how the pages\n    are displayed (multi column), and provides clickable links for each page.\n\n7.  Your widget must define permissions for creating and managing display\n    templates. Add the action key `ADD_PORTLET_DISPLAY_TEMPLATE` to your\n    portlet's `/src/main/resources/resource-actions/default.xml` file:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action Mapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n    <resource-action-mapping>\n        ...\n        <portlet-resource>\n            <portlet-name>yourportlet</portlet-name>\n            <permissions>\n                <supports>\n                    <action-key>ADD_PORTLET_DISPLAY_TEMPLATE</action-key>\n                    <action-key>ADD_TO_PAGE</action-key>\n                    <action-key>CONFIGURATION</action-key>\n                    <action-key>VIEW</action-key>\n                </supports>\n                ...\n            </permissions>\n        </portlet-resource>\n        ...\n    </resource-action-mapping>\n    ```\n\n8.  If your widget hasn't defined Liferay permissions before, create a file\n    named `portlet.properties` in the `/resources` folder and add the following\n    contents providing the path to your `default.xml`:\n\n    ```properties\n    include-and-override=portlet-ext.properties\n    resource.actions.configs=resource-actions/default.xml\n    ```\n\n9.  Now expose the Widget Template selector to your users. Include the\n    `<liferay-ddm:template-selector>` tag in the JSP file you're using to\n    control your portlet's configuration.\n\n    For example, it may be helpful for you to insert a\n    `<liferay-frontend:fieldset>` in your configuration JSP file like this:\n\n    ```markup\n    <liferay-frontend:fieldset\n        collapsible=\"<%= true %>\"\n        label=\"templates\"\n    >\n        <div class=\"display-template\">\n            <liferay-ddm:template-selector\n                classNameId=\"<%= YourEntity.class.getName() %>\"\n                displayStyle=\"<%= displayStyle %>\"\n                displayStyleGroupId=\"<%= displayStyleGroupId %>\"\n                refreshURL=\"<%= PortalUtil.getCurrentURL(request) %>\"\n                showEmptyOption=\"<%= true %>\"\n            />\n        </div>\n    </liferay-frontend:fieldset>\n    ```\n\n    In this JSP, the `<liferay-ddm:template-selector>` tag specifies the Display\n    Template drop-down menu to be used in the widget's Configuration menu.\n\n10. You must now extend your view code to render your portlet using the selected\n    Widget Template. \n\n    First, initialize the Java variables needed for the Widget Template: \n\n    ```markup\n    <%\n    String displayStyle = GetterUtil.getString(portletPreferences.getValue(\"displayStyle\", StringPool.BLANK));\n    long displayStyleGroupId = GetterUtil.getLong(portletPreferences.getValue(\"displayStyleGroupId\", null), scopeGroupId);\n    %>\n    ```\n\n    Next, you can test if the Widget Template is configured, grab the entities\n    to be rendered, and render them using the Widget Template. The tag\n    `<liferay-ddm:template-renderer>` aids with this process. It automatically\n    uses the selected template or renders its body if no template is selected.\n\n    Here's some example code that demonstrates implementing this:\n\n    ```markup\n    <liferay-ddm:template-renderer\n        className=\"<%= YourEntity.class.getName() %>\"\n        contextObjects=\"<%= contextObjects %>\"\n        displayStyle=\"<%= displayStyle %>\"\n        displayStyleGroupId=\"<%= displayStyleGroupId %>\"\n        entries=\"<%= yourEntities %>\"\n    >\n\n        <%-- The code that renders the default view should be inserted here. --%>\n\n    </liferay-ddm:template-renderer>\n    ```\n\n    In this step, you initialized variables dealing with the display settings \n    (`displayStyle` and `displayStyleGroupId`) and passed them to the tag along\n    with other parameters.\n\n    As an example, the Site Map widget implements the\n    `<liferay-ddm:template-renderer>` tag in its `view.jsp` like this:\n\n    ```markup\n    <liferay-ddm:template-renderer\n        className=\"<%= LayoutSet.class.getName() %>\"\n        contextObjects=\"<%= contextObjects %>\"\n        displayStyle=\"<%= siteNavigationSiteMapPortletInstanceConfiguration.displayStyle() %>\"\n        displayStyleGroupId=\"<%= siteNavigationSiteMapDisplayContext.getDisplayStyleGroupId() %>\"\n        entries=\"<%= siteNavigationSiteMapDisplayContext.getRootLayouts() %>\"\n    >\n        <%= siteNavigationSiteMapDisplayContext.buildSiteMap() %>\n    </liferay-ddm:template-renderer>\n    ```\n\n    This logic builds the site's navigation map when the widget is added to a\n    page.\n\nAwesome! Your portlet now supports Widget Templates! Once your script is\nuploaded and saved, Users with the specified Roles can select the template when\nthey're configuring the display settings of your portlet on a\npage. You can visit the\n[Styling Widgets with Widget Templates](/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates)\nsection for more details on using Widget Templates.\n"
  },
  {
    "path": "en/developer/customization/articles/50-dynamic-includes/01-dynamic-include-extension-points-intro.markdown",
    "content": "---\nheader-id: dynamic-includes\n---\n\n# Dynamic Includes\n\n[TOC levels=1-4]\n\nDynamic includes expose extension points in JSPs for injecting additional \nHTML, adding resources, modifying editors, and more. Several dynamic includes \nare available. Once you know the dynamic include's key, you can use it to \n[create a module to inject your content](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps-with-dynamic-includes). \n\nThis section of tutorials lists the available dynamic include keys, along with a \ndescription of their use cases and a code example. \n\nThe following extension points are covered in this section:\n\nExtension Point | Purpose |\n:---------: | :--------------: |\n[bottom](/docs/7-2/customization/-/knowledge_base/c/bottom-jsp-dynamic-includes) | Load additional HTML or scripts in the bottom of the theme's body |\n[top_head](/docs/7-2/customization/-/knowledge_base/c/top-head-jsp-dynamic-includes) | Load additional links in the theme's head |\n[top_js](/docs/7-2/customization/-/knowledge_base/c/top-js-dynamic-include) | Load additional JS files in the theme's head |\n[WYSIWYG](/docs/7-2/customization/-/knowledge_base/c/wysiwyg-editor-dynamic-includes) | Add resources to the editor, listen to events, update the configuration, etc. |\n"
  },
  {
    "path": "en/developer/customization/articles/50-dynamic-includes/02-wysiwyg-editor-dynamic-includes.markdown",
    "content": "---\nheader-id: wysiwyg-editor-dynamic-includes\n---\n\n# WYSIWYG Editor Dynamic Includes\n\n[TOC levels=1-4]\n\nAll WYSIWYG editors share the same dynamic include extension points for these \nthings:\n\n- Adding resources, plugins, etc. to the editor:\n \n    com.liferay.frontend.editor.`editorType`.web#`editorName`#additionalResources\n \n- Accessing the editor instance to listen to events, configure it, etc:\n \n    com.liferay.frontend.editor.`editorType`.web#`editorName`#onEditorCreate \n\nThe table below shows the `editorType`, variable, and `editorName`s for each editor:\n\n  editorType |  variable | editorName  |\n:---------: | :--------------: | :---------: |\n  alloyeditor | alloyEditor   | alloyeditor       |\n  &nbsp;            | &nbsp;              | alloyeditor_bbcode |\n  &nbsp;            | &nbsp;              | alloyeditor_creole |\n  ckeditor    | ckEditor      | ckeditor |\n  &nbsp;            | &nbsp;              | ckeditor_bbcode |\n  &nbsp;            | &nbsp;              | ckeditor_creole |\n  tinymce     | tinyMCEEditor | tinymce |\n  &nbsp;            | &nbsp;              | tinymce_simple |\n\nThe example below alerts the user when he/she pastes content into the CKEditor.\n\n`*DynamicInclude` Java Class:\n\n```java\n@Component(immediate = true, service = DynamicInclude.class)\npublic class CKEditorOnEditorCreateDynamicInclude implements DynamicInclude {\n\n\t@Override\n\tpublic void include(\n\t\t\tHttpServletRequest request, HttpServletResponse response,\n\t\t\tString key)\n\t\tthrows IOException {\n\n\t\tBundle bundle = _bundleContext.getBundle();\n\n\t\tURL entryURL = bundle.getEntry(\n\t\t\t\"/META-INF/resources/ckeditor/extension/ckeditor_alert.js\");\n\n\t\tStreamUtil.transfer(\n\t\t\tentryURL.openStream(), response.getOutputStream(), false);\n\t}\n\n\t@Override\n\tpublic void register(\n\t\tDynamicInclude.DynamicIncludeRegistry dynamicIncludeRegistry) {\n\n\t\tdynamicIncludeRegistry.register(\n\t\t\t\"com.liferay.frontend.editor.ckeditor.web#ckeditor#onEditorCreate\");\n\t}\n\n\t@Activate\n\tprotected void activate(BundleContext bundleContext) {\n\t\t_bundleContext = bundleContext;\n\t}\n\n\tprivate BundleContext _bundleContext;\n\n}\n```\n\nExample JavaScript:\n\n```javascript\n// ckEditor variable is already available in the execution context\nckEditor.on(\n    'paste',\n    function(event) {\n        event.stop();\n\n        alert('Please, do not paste code here!');\n    }\n);\n```\n\nNow you know how to use the WYSIWYG editor dynamic includes.\n\n## Related Topics\n\n- [Bottom JSP Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/bottom-jsp-dynamic-includes)\n- [Top Head JSP Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/top-head-jsp-dynamic-includes)\n- [Top JS Dynamic Include](/docs/7-2/customization/-/knowledge_base/c/top-js-dynamic-include)\n"
  },
  {
    "path": "en/developer/customization/articles/50-dynamic-includes/03-top-head-jsp-dynamic-include.markdown",
    "content": "---\nheader-id: top-head-jsp-dynamic-includes\n---\n\n# Top Head JSP Dynamic Includes\n\n[TOC levels=1-4]\n\nThe `top_head.jsp` dynamic includes load additional links in the theme's head. \nIt uses the following keys: \n\nLoad additional links in the theme's head before the existing ones:\n\n    /html/common/themes/top_head.jsp#pre\n\nAlternatively, you can load additional links in the theme's head, after the \nexisting ones:\n\n    /html/common/themes/top_head.jsp#post\n\nThe example below injects a link into the top of the `top_head.jsp`:\n\n```java\n@Component(immediate = true, service = DynamicInclude.class)\npublic class CssTopHeadDynamicInclude extends BaseDynamicInclude {\n\n\t@Override\n\tpublic void include(\n\t\t\tHttpServletRequest request, HttpServletResponse response,\n\t\t\tString key)\n\t\tthrows IOException {\n\n\t\tPrintWriter printWriter = response.getWriter();\n\n\t\tString content = \n    \"<link href=\\\"http://localhost:8080/o/my-custom-dynamic-include/css/mentions.css\\\" \n    rel=\\\"stylesheet\\\" \n    type = \\\"text/css\\\" />\";\n    \n\t\tprintWriter.println(content);\n\t}\n\n\t@Override\n\tpublic void register(DynamicIncludeRegistry dynamicIncludeRegistry) {\n\t\tdynamicIncludeRegistry.register(\"/html/common/themes/top_head.jsp#pre\");\n\t}\n  \n}\n```\n\nPage Source:\n\n```html\n<head>\n  ...\n  <link href=\"http://localhost:8080/o/my-custom-dynamic-include/css/mentions.css\" rel=\"stylesheet\" type=\"text/css\">\n  ...\n</head>\n```\n\nNote that the link's `href` attribute's value \n`/o/my-custom-dynamic-include/` is provided by the OSGi module's \n`Web-ContextPath` (`/my-custom-dynamic-include` in the example). \n\nNow you know how to use the `top_head.jsp` dynamic includes. \n\n## Related Topics\n\n- [Bottom JSP Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/bottom-jsp-dynamic-includes)\n- [Top JS Dynamic Include](/docs/7-2/customization/-/knowledge_base/c/top-js-dynamic-include)\n- [WYSIWYG Editor Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/wysiwyg-editor-dynamic-includes)\n"
  },
  {
    "path": "en/developer/customization/articles/50-dynamic-includes/04-top-js-dynamic-include.markdown",
    "content": "---\nheader-id: top-js-dynamic-include\n---\n\n# Top JS Dynamic Include\n\n[TOC levels=1-4]\n\nThe `top_js.jspf` dynamic include adds additional JavaScript files to the \ntheme's head. For example, you can use this extension point to include a JS \nlibrary that you need present in the theme's head:\n\n    /html/common/themes/top_js.jspf#resources\n\nThe example below injects a JavaScript file into the top of the `top_js.jspf`:\n\n`*DynamicInclude` Java Class:\n\n```java\n@Component(immediate = true, service = DynamicInclude.class)\npublic class JSTopHeadDynamicInclude extends BaseDynamicInclude {\n\n  @Override\n\tpublic void include(\n\t\t\tHttpServletRequest request, HttpServletResponse response,\n\t\t\tString key)\n\t\tthrows IOException {\n\n    PrintWriter printWriter = response.getWriter();\n\n    String content = \"<script charset=\\\"utf-8\\\" src=\\\"/o/my-custom-dynamic-include/my_example_javascript.js\\\" async />\";\n\n    printWriter.println(content);\n\t}\n\n\t@Override\n\tpublic void register(\n\t\tDynamicInclude.DynamicIncludeRegistry dynamicIncludeRegistry) {\n\n\t\tdynamicIncludeRegistry.register(\n      \"/html/common/themes/top_js.jspf#resources\"\n    );\n\t}\n}\n```\n\nPage Source:\n\n```html\n<head>\n  ...\n  <script charset=\"utf-8\" src=\"/o/my-custom-dynamic-include/my_example_javascript.js\" async>...</script>\n  ...\n</head>\n```\n\nNote that the JavaScript `src` attribute's value \n`/o/my-custom-dynamic-include/...` is provided by the OSGi module's \n`Web-ContextPath` (`/my-custom-dynamic-include` in the example). \n \nNow you know how to use the `top_js.jspf` dynamic include. \n\n## Related Topics\n\n- [Bottom JSP Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/bottom-jsp-dynamic-includes)\n- [Top Head JSP Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/top-head-jsp-dynamic-includes)\n- [WYSIWYG Editor Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/wysiwyg-editor-dynamic-includes)\n"
  },
  {
    "path": "en/developer/customization/articles/50-dynamic-includes/05-bottom-jsp-dynamic-include.markdown",
    "content": "---\nheader-id: bottom-jsp-dynamic-includes\n---\n\n# Bottom JSP Dynamic Includes\n\n[TOC levels=1-4]\n\nThe `bottom.jsp` dynamic includes load additional HTML or scripts in the bottom \nof the theme's body. The following keys are available:\n\nLoad additional HTML or scripts in the bottom of the theme's body, before the \nexisting ones:\n\n    /html/common/themes/bottom.jsp#pre\n\nAlternatively, load HTML or scripts in the bottom of the theme's body, after the \nexisting ones:\n\n    /html/common/themes/bottom.jsp#post \n \nThe example below includes an additional script for the Simulation panel in the \nbottom of the theme's body, after the existing ones. \n\n`SimulationDeviceDynamicInclude` Java class:\n\n```java\n@Component(immediate = true, service = DynamicInclude.class)\npublic class SimulationDeviceDynamicInclude extends BaseDynamicInclude {\n\n\t@Override\n\tpublic void include(\n\t\t\tHttpServletRequest request, HttpServletResponse response,\n\t\t\tString key)\n\t\tthrows IOException {\n\n\t\tPrintWriter printWriter = response.getWriter();\n\n\t\tprintWriter.print(_TMPL_CONTENT);\n\t}\n\n\t@Override\n\tpublic void register(DynamicIncludeRegistry dynamicIncludeRegistry) {\n\t\tdynamicIncludeRegistry.register(\"/html/common/themes/bottom.jsp#post\");\n\t}\n\n\tprivate static final String _TMPL_CONTENT = StringUtil.read(\n\t\tSimulationDeviceDynamicInclude.class,\n\t\t\"/META-INF/resources/simulation_device_dynamic_include.tmpl\");\n\n}\n```\n\n`simulation_device_dynamic_include.tmpl`:\n\n```javascript\n<script type=\"text/javascript\">\n\t// <![CDATA[\n\t\tAUI().use(\n\t\t\t'aui-base',\n\t\t\tfunction(A) {\n\t\t\t\tvar frameElement = window.frameElement;\n\n\t\t\t\tif (frameElement && frameElement.getAttribute('id') === 'simulationDeviceIframe') {\n\t\t\t\t\tA.getBody().addClass('lfr-has-simulation-panel');\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t// ]]>\n</script>\n```\n\nWhen the Simulation panel is open, the script adds the \n`lfr-has-simulation-panel` class to the theme's body. \n\nPage Source:\n\n```html\n<body class=\"controls-visible has-control-menu closed  yui3-skin-sam guest-site signed-in public-page site lfr-has-simulation-panel\" id=\"senna_surface1\">\n```\n\nNow you know how to use the `bottom.jsp` dynamic includes. \n\n## Related Topics\n\n- [Top Head JSP Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/top-head-jsp-dynamic-includes)\n- [Top JS Dynamic Include](/docs/7-2/customization/-/knowledge_base/c/top-js-dynamic-include)\n- [WYSIWYG Editor Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/wysiwyg-editor-dynamic-includes)\n"
  },
  {
    "path": "en/developer/customization/articles/50-lifecycle-events/01-waiting-on-lifecycle-events-intro.markdown",
    "content": "---\nheader-id: waiting-on-lifecycle-events\n---\n\n# Waiting on Lifecycle Events\n\n[TOC levels=1-4]\n\nLiferay registers lifecycle events like portal and database initialization \ninto the OSGi service registry. Your OSGi Component or non-component class \ncan listen for these events by way of their service registrations. The \n[`ModuleServiceLifecycle` interface](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/module/framework/ModuleServiceLifecycle.html)\ndefines these names for the lifecycle event services:\n\n-   [DATABASE_INITIALIZED](@platform-ref@/7.2-latest/javadocs/portal-kernel/constant-values.html#com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle.DATABASE_INITIALIZED)\n-   [PORTAL_INITIALIZED](@platform-ref@/7.2-latest/javadocs/portal-kernel/constant-values.html#com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle.PORTAL_INITIALIZED)\n-   [SPRING_INITIALIZED](@platform-ref@/7.2-latest/javadocs/portal-kernel/constant-values.html#com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle.SPRING_INITIALIZED)\n\nHere you'll learn how to wait on lifecycle event services to act on them from \nwithin a component or non-component class. \n\n## Taking action from a component\n\n[Declarative Services (DS)](https://osgi.org/specification/osgi.cmpn/7.0.0/service.component.html) \nfacilitates waiting for OSGi services and acting on them once they're available. \n\nHere's a component whose `doSomething` method is invoked once the \n`ModuleServiceLifecycle.PORTAL_INITIALIZED` lifecycle event service and other \nservices are available. \n\n```java\n@Component\npublic class MyXyz implements XyzApi {\n\n    // Plain old OSGi service\n    @Reference\n    private SomeOsgiService _someOsgiService;\n\n    // Service Builder generated service\n    @Reference\n    private DDMStructureLocalService _ddmStructureLocalService;\n\n    // Liferay lifecycle service\n    @Reference(target = ModuleServiceLifecycle.PORTAL_INITIALIZED)\n    private ModuleServiceLifecycle _portalInitialized;\n\n    @Activate\n    public void doSomething() {\n        // `@Activate` method is only executed once all of\n        // `_someOsgiService`,\n        // `_ddmStructureLocalService` and\n        // `_portalInitialized`\n        // are set.\n    }\n}\n```\n\nHere's how to act on services in your component:\n\n1.  For each lifecycle event service and OSGi service your component uses, add a \n    field of that service type and add an `@Reference` annotation to that field. \n    The OSGi framework binds the services to your fields. This field, for \n    example, binds to a standard OSGi service. \n\n    ```java\n    @Reference\n    SomeOsgiService _someOsgiService;\n    ```\n\n2.  To bind to a particular lifecycle event service, target its name as the \n    [`ModuleServiceLifecycle` interface](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/module/framework/ModuleServiceLifecycle.html)\n    defines. This field, for example, targets database initialization. \n\n    ```java\n    @Reference(target = ModuleServiceLifecycle.DATABASE_INITIALIZED)\n    ModuleServiceLifecycle _dataInitialized;\n    ```\n\n3.  Create a method that's triggered on the event(s) and add the `@Activate` \n    annotation to that method. It's invoked when all the service objects are \n    bound to the component's fields. \n\nYour component fires (via its `@Activate` method) after all its service \ndependencies resolve. DS components are the easiest way to act on lifecycle \nevent services. \n\n## Taking action from a non-component class\n\nClasses that aren't DS components can use a \n`org.osgi.util.tracker.ServiceTracker` or \n`org.osgi.util.tracker.ServiceTrackerCustomizer` as a \n[service callback handler](/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker#creating-a-service-tracker-that-tracks-service-events-using-a-callback-handler)\nfor the lifecycle event. If you depend on multiple services, add logic to your\n`ServiceTracker` or `ServiceTrackerCustomizer` to coordinate taking action when\nall the services are available. \n\nTo target a lifecycle event service, create a service tracker that filters on\nthat service. Use `org.osgi.framework.FrameworkUtil` to create an\n`org.osgi.framework.Filter` that specifies the service. Then pass that filter as\na parameter to the service tracker constructor. For example, this service\ntracker filters on the lifecycle service\n`ModuleServiceLifecycle.PORTAL_INITIALIZED`.\n\n```java\nimport org.osgi.framework.Filter;\nimport org.osgi.framework.FrameworkUtil;\n\nFilter filter = FrameworkUtil.createFilter(\n    String.format(\n        \"(&(objectClass=%s)%s)\",\n        ModuleServiceLifecycle.class.getName(),\n        ModuleServiceLifecycle.PORTAL_INITIALIZED));\n\nnew ServiceTracker<>(bundleContext, filter, null);\n```\n\nActing on lifecycle event services in this way requires service callback\nhandling and some boilerplate code. Using DS components is easier and more\nelegant, but at least service trackers provide a way to work with lifecycle\nevents outside of DS components. \n\n## Related Topics\n\n[Service Trackers](/docs/7-1/tutorials/-/knowledge_base/t/service-trackers)\n\n[@product@ Startup Phases](/docs/7-1/reference/-/knowledge_base/r/liferay-startup-phases)\n"
  },
  {
    "path": "en/developer/customization/articles/50-liferay-forms/01-forms-intro.markdown",
    "content": "---\nheader-id: liferay-forms\n---\n\n# Liferay Forms\n\n[TOC levels=1-4]\n\nThe [Liferay Forms](/docs/7-2/user/-/knowledge_base/u/forms) application is\na full-featured form building tool for collecting data. There's lots of built-in\nfunctionality. For the pieces you're missing, there are extension points.\n\nThis section of articles shows developers how to\n\n1.  Store form entry data in an alternative format. The default storage type is\n    JSON.\n\n2.  [Coming Soon] Create new form field types.\n\n## Liferay Forms Extension Points\n\nHere's a compilation of the Liferay Forms application's extension points that\nare ready for your customization:\n\n- Create a Form Storage Adapter by implementing a \n    [`StorageAdapter`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/storage/StorageAdapter.java) \n    or by extending the Abstract implementation,\n    [`BaseStorageAdapter`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/storage/BaseStorageAdapter.java).\n- Create a Form Field Type by implementing a\n    [`DDMFormFieldType`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/form/field/type/DDMFormFieldType.java),\n    [`DDMFormFieldTypeSettings`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/form/field/type/DDMFormFieldTypeSettings.java),\n    and a\n    [`DDMFormFieldTemplateContextContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/form/field/type/DDMFormFieldTemplateContextContributor.java).\n- Create custom validation rules for form fields by implementing a\n    [DDMFormFieldValueValidator](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/form/field/type/DDMFormFieldValueValidator.java).\n"
  },
  {
    "path": "en/developer/customization/articles/50-liferay-forms/02-form-storage-adapters.markdown",
    "content": "---\nheader-id: form-storage-adapters\n---\n\n# Form Storage Adapters\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/process-automation/forms/developer-guide/understanding-form-storage-adapters.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nWhen a User adds a form record, the Forms API routes the processing of the\nrequest through the storage adapter API. The same is true for the other *CRUD*\noperations performed on form entries (read, update, and delete operations). The\ndefault implementation of the storage service is called `JSONStorageAdapter`,\nand as its name implies, it implements the `StorageAdapter` interface to provide\nJSON storage of form entry data.\n\nThe Dynamic Data Mapping (DDM) backend can *adapt* to other data storage formats\nfor form records. Want to store your data in XML? YAML? No problem. Because the\nstorage API is separated from the regular service calls used to populate the\ndatabase table for form entries, a developer can even choose to store form data\noutside the Liferay database.\n<!--A Diagram?-->\n\nDefine your own format to save form entries by writing your own implementation\nof the `StorageAdapter` interface. The interface follows the *CRUD* approach, so\nimplementing it requires that you write methods to create, read, update and\ndelete form values.\n\n| **Note:** The `StorageAdapter` interface and it's abstract implementation, |\n| `BaseStorageAdapter`, are deprecated in @product-ver@. In the future your code\n| should be migrated to implement the `DDMStorageAdapter` interface. If you need a\n| storage adapter, the current extension of `BaseStorageAdapter` (demonstrated in\n| this documentation), is still the way to create one, but be aware that it will\n| not be available in a future version.\n\nA newly added storage adapter can only be used with new Forms. All existing\nForms continue to use the adapter selected (JSON by default) at the time of\ntheir creation, and a different storage adapter cannot be selected.\n\nThe example storage adapter in this tutorial serializes form data to be stored\nin a simple file, stored on the file system.\n\n![Figure 1: Choose a Storage Type for your form records.](../../images/forms-storage-type.png)\n\n## Storage Adapter Methods\n\nBefore handling the CRUD logic, write a `getStorageType` method.\n\n`getStorageType`\n: Return a human readable String, as `getStorageType` determines what appears\nin the UI when the form creator is selecting a storage type for their form. The\nString value you return here is added to the `StorageAdapterRegistry`'s Map of\nstorage adapters. \n\n### The CRUD Methods\n\n`doCreate`:\nReturn a `long` that identifies each form record with a unique file ID. Almost\nas important is to validate the form values being sent through the storage\nadapter API. This is as simple as calling\n`DDMFormValuesValidator.validate(ddmFormValues)`. In addition, you'll interact\nwith at least two other DDM services to get the form the values are associated\nwith, and to make sure they're linked: `DDMStructureVersionLocalService` and\n`DDMStorageLinkLocalService`. Lastly, the form values in the `DDMFormValues`\nobject must be serialized (converted) into the right storage format. If JSON\nworks for your use case, feel free to use the `DDMFormValuesJSONSerializer`\nservice in the Liferay Forms code, as demonstrated in the following article.\nOtherwise you'll need to provide your own serialization service for the form\nvalues.\n\n`doGetDDMFormValues`\n: Return the form values (`DDMFormValues`) for a form. You'll call the\n`deserialize` method after retrieving them, to take them from the storage format\n(e.g., JSON) to a proper `DDMFormValues` object. You can use the Liferay Forms\n`DDMFormValuesJSONDeserializer` if you're retrieving JSON data.\n\n`doUpdate`\n: A request to update the values comes from a User in the Liferay Forms\napplication, so call the validator again, serialize the values into the proper\nformat, and save them.\n\n`doDeleteByClass`\n: When a delete request is made on a form record directly, delete the form\nvalues in whatever format they're currently being stored in (this is entirely\ndependent on your own application of the storage adapter). In addition, retrieve\nand delete the DDM class storage link using `DDMStorageLinkLocalService`.\n\n`doDeleteByDDMStrcuture`\n: When a delete request is made on an entire form, delete all the form records\nassociated with it. In addition, take the form's `ddmStructureId` and delete all\nthe DDM structure storage links that were created for it.\n\n### Validating Form Entries\n\nBecause the Storage Adapter handles User entered data during the `add` and\n`update` operations, it's important to validate that the entries include only\nappropriate data. Add a `validate` method to the `StorageAdapter`, calling the\nLiferay Forms' `DDMFormValuesValidator` method to do the heavy lifting. \n\n```java\nprotected void validate(\n      DDMFormValues ddmFormValues, ServiceContext serviceContext)\n\tthrows Exception {\n\n\tboolean validateDDMFormValues = GetterUtil.getBoolean(\n\t\tserviceContext.getAttribute(\"validateDDMFormValues\"), true);\n\n\tif (!validateDDMFormValues) {\n\t\treturn;\n\t}\n\n\t_ddmFormValuesValidator.validate(ddmFormValues);\n}\n```\n\nMake sure to do three things:\n\n1.  Retrieve the value of the `boolean validateDDMFormValues` attribute from the\n    service context.\n\n2.  If `validateDDMFormValues` is false, exit the validation without doing\n    anything.\n\n    When a User accesses a form at its dedicated link, there's a periodic\n    auto-save process of in-progress form values. There's no need to validate\n    this data until the User hits the *Submit* button on the form, so the\n    auto-save process sets the `validateDDMFormValues` attribute to `false`.\n\n3.  Otherwise, call the validate method from the `DDMFormValuesValidator`\n    service.\n\nAll the Java code for the logic discussed here is shown in the next article,\n[Creating Form Storage Adapters](/docs/7-2/customization/-/knowledge_base/c/creating-a-form-storage-adapter).\n\n## Enabling the Storage Adapter\n\nThe storage adapter is enabled at the individual form level. Create a new form,\nand select the Storage Adapter _before saving or publishing the form_. If you\nwait until first Saving the Form, the default Storage Adapter is already\nassigned to the Form, and this setting is no longer editable.\n\n1.  Go to the Site Menu &rarr; Content &rarr; Forms, and click the *Add* button\n    (![Add](../../images/icon-add.png)).\n\n2.  In the Form Builder view, click the *Options* button\n    (![Options](../../images/icon-options.png)) and open the *Settings*\n    window. \n\n3.  From the select list field called *Select a Storage Type*, choose the\n    desired type and click _Done_.\n\nNow all the form's entries are stored in the desired format.\n"
  },
  {
    "path": "en/developer/customization/articles/50-liferay-forms/03-creating-form-storage-adapters.markdown",
    "content": "---\nheader-id: creating-a-form-storage-adapter\n---\n\n# Creating a Form Storage Adapter\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/process-automation/forms/developer-guide/writing-a-form-storage-adapter.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThere's only one class to create when implementing a Form Storage Adapter, and\nit extends the base `StorageAdapter` implementation.\n\n```java\n@Component(service = StorageAdapter.class)\npublic class FileSystemStorageAdapter extends BaseStorageAdapter {\n```\n\nThe only method without a base implementation in the abstract class is\n`getStorageType`. For file system storage, it can return `\"File System\"`.\n\n```java\n@Override\npublic String getStorageType() {\n    return \"File System\";\n}\n```\n\n## Storage Adapter CRUD Operations\n\nThe CRUD operations must be created to properly handle the Form Records.\n\n### Create\n\nNext override the `doCreateMethod` to return a `long` that identifies each form\nrecord with a unique file ID: \n\n```java\n@Override\nprotected long doCreate(\n    long companyId, long ddmStructureId, DDMFormValues ddmFormValues, \n    ServiceContext serviceContext)\n    throws Exception {\n\n    validate(ddmFormValues, serviceContext);\n\n    long fileId = _counterLocalService.increment();\n\n    DDMStructureVersion ddmStructureVersion =\n        _ddmStructureVersionLocalService.getLatestStructureVersion(\n            ddmStructureId);\n\n    long classNameId = PortalUtil.getClassNameId(\n        FileSystemStorageAdapter.class.getName());\n\n    _ddmStorageLinkLocalService.addStorageLink(\n        classNameId, fileId, ddmStructureVersion.getStructureVersionId(),\n        serviceContext);\n\n    saveFile(\n        ddmStructureVersion.getStructureVersionId(), fileId, ddmFormValues);\n\n    return fileId;\n}\n\n@Reference\nprivate CounterLocalService _counterLocalService;\n\n@Reference\nprivate DDMStorageLinkLocalService _ddmStorageLinkLocalService;\n\n@Reference\nprivate DDMStructureVersionLocalService _ddmStructureVersionLocalService;\n```\n\nThese are the utility methods invoked in the create method:\n\n```java\nprivate File getFile(long structureId, long fileId) {\n    return new File(\n        getStructureFolder(structureId), String.valueOf(fileId));\n}\n\nprivate File getStructureFolder(long structureId) {\n    return new File(String.valueOf(structureId));\n}\n\nprivate void saveFile(\n        long structureVersionId, long fileId, DDMFormValues formValues)\n    throws IOException {\n\n    String serializedDDMFormValues = _ddmFormValuesJSONSerializer.serialize(\n        formValues);\n\n    File formEntryFile = getFile(structureVersionId, fileId);\n\n    FileUtil.write(formEntryFile, serializedDDMFormValues);\n}\n\n@Reference\nprivate DDMFormValuesJSONSerializer _ddmFormValuesJSONSerializer;\n```\n\n### Read\n\nTo retrieve the form record's values from the `File` object where they were\nwritten, override `doGetDDMFormValues`:\n\n```java\n@Override\nprotected DDMFormValues doGetDDMFormValues(long classPK) throws Exception {\n    DDMStorageLink storageLink =\n        _ddmStorageLinkLocalService.getClassStorageLink(classPK);\n\n    DDMStructureVersion structureVersion =\n        _ddmStructureVersionLocalService.getStructureVersion(\n            storageLink.getStructureVersionId());\n\n    String serializedDDMFormValues = FileUtil.read(\n        getFile(structureVersion.getStructureVersionId(), classPK));\n\n    return _ddmFormValuesJSONDeserializer.deserialize(\n        structureVersion.getDDMForm(), serializedDDMFormValues);\n}\n\n@Reference\nprivate DDMFormValuesJSONDeserializer _ddmFormValuesJSONDeserializer;\n```\n\n### Update\n\nOverride the `doUpdate` method so the record's values can be overwritten. This\nexample calls the `saveFile`  utility method provided earlier:\n\n```java\n@Override\nprotected void doUpdate(\n        long classPK, DDMFormValues ddmFormValues,\n        ServiceContext serviceContext)\n    throws Exception {\n\n    validate(ddmFormValues, serviceContext);\n\n    DDMStorageLink storageLink =\n        _ddmStorageLinkLocalService.getClassStorageLink(classPK);\n\n    saveFile(\n        storageLink.getStructureVersionId(), storageLink.getClassPK(),\n        ddmFormValues);\n}\n```\n\n### Delete\n\nOverride the `doDeleteByClass` method to delete the `File` representing the form\nrecord, using the `classPK`, and to delete the class storage links:\n\n```java\n@Override\nprotected void doDeleteByClass(long classPK) throws Exception {\n    DDMStorageLink storageLink =\n        _ddmStorageLinkLocalService.getClassStorageLink(classPK);\n\n    FileUtil.delete(getFile(storageLink.getStructureId(), classPK));\n\n    _ddmStorageLinkLocalService.deleteClassStorageLink(classPK);\n}\n```\n\nProvide form record deletion logic to be called when deleting all the records\nand storage links associated with a form (using its `ddmStructureId`):\n\n```java\n@Override\nprotected void doDeleteByDDMStructure(long ddmStructureId)\n    throws Exception {\n\n    FileUtil.deltree(getStructureFolder(ddmStructureId));\n\n    _ddmStorageLinkLocalService.deleteStructureStorageLinks(ddmStructureId);\n}\n```\n\n## Beyond CRUD: Validation\n\nAdd a `validate` method to the `StorageAdapter`:\n\n```java\nprotected void validate(\n    DDMFormValues ddmFormValues, ServiceContext serviceContext)\n    throws Exception {\n\n    boolean validateDDMFormValues = GetterUtil.getBoolean(\n        serviceContext.getAttribute(\"validateDDMFormValues\"), true);\n\n    if (!validateDDMFormValues) {\n        return;\n    }\n\n    _ddmFormValuesValidator.validate(ddmFormValues);\n}\n```\n\nDeploy your storage adapter and it's ready to use.\n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-language-keys/01-overriding-language-keys-intro.markdown",
    "content": "---\nheader-id: overriding-language-keys\n---\n\n# Overriding Language Keys\n\n[TOC levels=1-4]\n\nCore and portlet module `Language*.properties` files implement site \ninternationalization. They're fully customizable, too. This section demonstrates \nthis in the following topics:\n\n-   [Overriding Liferay's Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys) \n-   [Overriding a Module's Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys)\n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-language-keys/02-overriding-liferays-language-keys.markdown",
    "content": "---\nheader-id: overriding-global-language-keys\n---\n\n# Overriding Global Language Keys\n\n[TOC levels=1-4]\n\nLanguage files contain [translations of your application's user interface \nmessages](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application). \nBut you can also override the default language keys globally and in other \napplications (including your own). Here are the steps for overriding \nlanguage keys:\n\n1.  [Determine the language keys to override](#determine-the-language-keys-to-override)\n2.  [Override the keys in a new language properties file](#override-the-keys-in-a-new-language-properties-file)\n3.  [Create a Resource Bundle service component](#create-a-resource-bundle-service-component)\n\n| **Note:** Many applications that were once part of Liferay Portal 6.2 are now \n| modularized. Their language keys might have been moved out of Liferay's language \n| properties files and into one of the application's modules. The process for \n| [overriding a module's language keys](/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys) \n| is different from the process for overriding Liferay's language keys. \n\n## Determine the language keys to override\n\nSo how do you find global language keys? They're in the \n`Language[xx_XX].properties` files in the source code or your bundle. \n\n-   From the source:\n\n    `/portal-impl/src/content/Language[xx_XX].properties`\n\n-   From a bundle:\n\n    `portal-impl.jar`\n\nAll language properties files contain properties you can override, like the \nlanguage settings properties:\n\n```properties\n##\n## Language settings\n##\n\n...\nlang.user.name.field.names=prefix,first-name,middle-name,last-name,suffix\nlang.user.name.prefix.values=Dr,Mr,Ms,Mrs\nlang.user.name.required.field.names=last-name\nlang.user.name.suffix.values=II,III,IV,Jr,Phd,Sr\n...\n```\n\nThere are also many simple keys you can override to update default messages and\nlabels.\n\n```properties\n##\n## Category titles\n##\n\ncategory.admin=Admin\ncategory.alfresco=Alfresco\ncategory.christianity=Christianity\ncategory.cms=Content Management\n...\n```\n\nFor example, Figure 1 shows a button that uses Liferay's `publish` default \nlanguage key. \n    \n    `publish=Publish`\n \n![Figure 1: Messages displayed in Liferay's user interface can be customized.](../../images/standard-publish.png)\n\nNext, you'll learn how to override this key. \n\n## Override the keys in a new language properties file\n\nOnce you know the keys to override, create a language properties file for the \nlocale you want (or the default `Language.properties` file) in your module's \n`src/main/resources/content` folder. In your file, define the keys your way. For \nexample, you could override the `publish` key. \n\n```properties\npublish=Publish Override\n```\n\nTo enable your change, you must create a resource bundle service component to \nreference your language file. \n\n### Create a Resource Bundle service component\n\nIn your module, create a class that extends `java.util.ResourceBundle` for the \nlocale you're overriding. Here's an example resource bundle class for the \n`en_US` locale:\n\n```java\n@Component(\n    property = { \"language.id=en_US\" }, \n    service = ResourceBundle.class\n)\npublic class MyEnUsResourceBundle extends ResourceBundle {\n\n    @Override\n    protected Object handleGetObject(String key) {\n        return _resourceBundle.getObject(key);\n    }\n\n    @Override\n    public Enumeration<String> getKeys() {\n        return _resourceBundle.getKeys();\n    }\n\n    private final ResourceBundle _resourceBundle = ResourceBundle.getBundle(\n        \"content.Language_en_US\", UTF8Control.INSTANCE);\n\n}\n```\n\nThe class's `_resourceBundle` field is assigned a `ResourceBundle`. The call to \n`ResourceBundle.getBundle` needs two parameters. The `content.Language_en_US` \nparameter is the language file's qualified name with respect to the module's \n`src/main/resources` folder. The second parameter is a `control` that sets the \nlanguage syntax of the resource bundle. To use language syntax identical to \nLiferay's syntax, import Liferay's \n`com.liferay.portal.kernel.language.UTF8Control` class and set the second \nparameter to `UTF8Control.INSTANCE`. \n\nThe class's `@Component` annotation declares it an OSGi `ResourceBundle` service \ncomponent. It's `language.id` property designates it for the `en_US` locale. \n\n```java\n@Component(\n    property = { \"language.id=en_US\" }, \n    service = ResourceBundle.class\n)\n```\n\nThe class overrides these methods:\n\n-   **`handleGetObject`:** Looks up the key in the module's resource bundle \n    (which is based on the module's language properties file) and returns the \n    key's value as an `Object`. \n\n-   **`getKeys`:** Returns an `Enumeration` of the resource bundle's keys. \n\nYour resource bundle service component redirects the default language keys to \nyour module's language key overrides. \n\n| **Note**: Global language key overrides for multiple locales require a separate \n| module for each locale. Each module's `ResourceBundle` extension class (like the \n| `MyEnUsResourceBundle` class above) must specify its locale in the `language.id` \n| component property definition and in the language file qualified name parameter. \n| For example, here is what they look like for the Spanish locale. \n| \n| Component definition:\n| \n| ```java\n| @Component(\n|     property = { \"language.id=es_ES\" },\n|     service = ResourceBundle.class\n| )\n| ```\n| \n| Resource bundle assignment:\n| \n| ```java\n| private final ResourceBundle _resourceBundle = ResourceBundle.getBundle(\n|     \"content.Language_es_ES\", UTF8Control.INSTANCE);\n| ```\n\n**Important**: If your module [uses language keys from another module](/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module)\nand [overrides any of that other module's keys](/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module),\nmake sure to use OSGi headers to specify the capabilities your module requires \nand provides. This lets you prioritize resource bundles from the modules. \n\nTo see your Liferay language key overrides in action, \n[deploy your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project) \nand visit the portlets and pages that use the keys. \n\n![Figure 2: This button uses the overridden `publish` key.](../../images/localized-publish.png)\n\nThat's all there is to overriding Liferay's language keys. \n\n## Related Topics\n\n- [Upgrading Core Language Key Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-core-language-key-hooks)\n- [Overriding a Module's Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys)\n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-language-keys/03-overriding-a-modules-language-keys.markdown",
    "content": "---\nheader-id: overriding-a-modules-language-keys\n---\n\n# Overriding a Module's Language Keys\n\n[TOC levels=1-4]\n\nWhat do you do if the language keys you want to modify are in one of Liferay's \napplications or another module whose source code you don't control? Since module \nlanguage keys are in the respective module, the process for overriding a \nmodule's language keys is different from \n[the process of overriding Liferay's language keys](/docs/7-2/customization/-/knowledge_base/c/overriding-global-language-keys). \n\nHere is the process:\n\n1.  [Find the module and its metadata and language keys](#find-the-module-and-its-metadata-and-language-keys)\n2.  [Write your custom language key values](#write-custom-language-key-values) \n3.  [Prioritize your module's resource bundle](#prioritize-your-modules-resource-bundle)\n\n## Find the module and its metadata and language keys\n\nIn [Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell), \nlist the bundles and grep for keyword(s) that match the portlet's display name. \nLanguage keys are in the portlet's web module (bundle). When you find the \nbundle, note its ID number.\n\nTo find the Blogs portlet, for example, your Gogo commands and output might look \nlike this:\n\n    g! lb | grep Blogs\n      152|Active     |    1|Liferay Blogs Service (1.0.2)\n      184|Active     |    1|Liferay Blogs Editor Config (2.0.1)\n      202|Active     |    1|Liferay Blogs Layout Prototype (2.0.2)\n      288|Active     |    1|Liferay Blogs Recent Bloggers Web (1.0.2)\n      297|Active     |    1|Liferay Blogs Item Selector Web (1.0.2)\n      374|Active     |    1|Liferay Blogs Item Selector API (2.0.1)\n      448|Active     |    1|Liferay Blogs API (3.0.1)\n      465|Active     |    1|Liferay Blogs Web (1.0.6)\n    true\n\nList the bundle's headers by passing its ID to the `headers` command. \n\n    g! headers 465\n\n    Liferay Blogs Web (465)\n    -----------------------\n    Manifest-Version = 1.0\n    Bnd-LastModified = 1459866186018\n    Bundle-ManifestVersion = 2\n    Bundle-Name = Liferay Blogs Web\n    Bundle-SymbolicName = com.liferay.blogs.web\n    Bundle-Version: 1.0.6\n    ... \n    Web-ContextPath = /blogs-web\n    g! \n\nNote the `Bundle-SymbolicName`, `Bundle-Version`, and `Web-ContextPath`. The \n`Web-ContextPath` value, following the `/`, is the servlet context name. \n\n**Important**: Record the servlet context name, bundle symbolic name and \nversion, as you'll use them to create the resource bundle loader later in the \nprocess. \n\nFor example, here are those values for Liferay Blogs Web module:\n\n- Bundle symbolic name: `com.liferay.blogs.web`\n- Bundle version: `4.0.16`\n- Servlet context name: `blogs-web`\n\nNext find the module's JAR file so you can examine its language keys. Liferay \nfollows this module JAR file naming convention:\n\n    [bundle symbolic name]-[version].jar\n\nFor example, the Blogs Web version 4.0.16 module is in\n`com.liferay.blogs.web-4.0.16.jar`.\n\nHere's where to find the module JAR:\n\n-   Liferay's [Nexus repository](https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/)\n-  `[Liferay Home]/osgi/modules`\n-   Embedded in an application's or application suite's LPKG file in `[Liferay\n    Home]/osgi/marketplace`.\n\nThe language property files are in the module's `src/main/resources/content` \nfolder. Identify the language keys you want to override in the \n`Language[_xx].properties` files. \n\nCheckpoint: Make sure you have the required information for overriding the \nmodule's language keys:\n\n-   Language keys\n-   Bundle symbolic name\n-   Servlet context name\n\nNext you'll write new values for the language keys. \n\n## Write custom language key values\n\nCreate a new module to hold a resource bundle loader and your custom language \nkeys. \n\nIn your module's `src/main/resources/content` folder, create \n[language properties files](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application) \nfor each locale whose keys you want to override. In each language properties \nfile, specify your language key overrides. \n\nNext you'll prioritize your module's language keys as a resource bundle for the \ntarget module. \n\n## Prioritize Your Module's Resource Bundle\n\nNow that your language keys are in place, use OSGi manifest headers to specify \nthe language keys are for the target module. To compliment the target module's \nresource bundle, you'll aggregate your resource bundle with the target module's \nresource bundle. You'll list your module first to prioritize its resource bundle \nover the target module resource bundle.  Here's an example of module \n`com.liferay.docs.l10n.myapp.lang` prioritizing its resource bundle over target \nmodule `com.liferay.blogs.web`'s resource bundle:\n\n```properties\nProvide-Capability:\\\nliferay.resource.bundle;resource.bundle.base.name=\"content.Language\",\\\nliferay.resource.bundle;resource.bundle.aggregate:String=\"(bundle.symbolic.name=com.liferay.docs.l10n.myapp.lang),(bundle.symbolic.name=com.liferay.blogs.web)\";bundle.symbolic.name=com.liferay.blogs.web;resource.bundle.base.name=\"content.Language\";service.ranking:Long=\"2\";\\\nservlet.context.name=blogs-web\n```\n\nThe example `Provide-Capability` header has two parts: \n\n1.  `liferay.resource.bundle;resource.bundle.base.name=\"content.Language\"` \n    declares that the module provides a resource bundle with the base name \n    `content.language`. \n\n2.  The `liferay.resource.bundle;resource.bundle.aggregate:String=...` directive \n    specifies the list of bundles with resource bundles to aggregate, the \n    target bundle, the target bundle's resource bundle name, and this service's \n    ranking:\n\n    -   `\"(bundle.symbolic.name=com.liferay.docs.l10n.myapp.lang),(bundle.symbolic.name=com.liferay.blogs.web)\"`:\n        The service aggregates resource bundles from bundles \n        `com.liferay.docs.l10n.myapp.lang` and `com.liferay.blogs.web`. \n        Aggregate as many bundles as desired. Listed bundles are prioritized in \n        descending order. \n    -   `bundle.symbolic.name=com.liferay.blogs.web;resource.bundle.base.name=\"content.Language\"`:\n        Override the `com.liferay.blogs.web` bundle's resource bundle named \n        `content.Language`. \n    -   `service.ranking:Long=\"2\"`: The resource bundle's service ranking is \n        `2`. The OSGi framework applies this service if it outranks all other \n        resource bundle services that target `com.liferay.blogs.web`'s \n        `content.Language` resource bundle. \n    -   `servlet.context.name=blogs-web`: The target resource bundle is in \n        servlet context `blogs-web`. \n\n[Deploy your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project) \nto see the language keys you've overridden. \n\n| **Tip:** If your override isn't showing, use \n| [Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell) \n| to check for competing resource bundle services. It may be that another service \n| outranks yours. To check for competing resource bundle services whose aggregates \n| include `com.liferay.blogs.web`'s resource bundle, for example, execute this \n| Gogo Shell command:\n| \n|     services \"(bundle.symbolic.name=com.liferay.login.web)\"\n| \n| Search the results for resource bundle aggregate services whose ranking is \n| higher.\n\nNow you can modify the language keys of modules in Liferay's OSGi runtime. \nRemember, language keys you want to override might actually be in Liferay's \ncore. You can [override Liferay's language keys](/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys) \ntoo. \n\n## Related Topics\n\n- [Upgrading Core Language Key Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-core-language-key-hooks)\n- [Overriding Global Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-global-language-keys)\n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-liferay-services/01-overriding-liferay-services-intro.markdown",
    "content": "---\nheader-id: overriding-service-builder-services-service-wrappers\n---\n\n# Overriding Liferay Services (Service Wrappers)\n\n[TOC levels=1-4]\n\nWhy might you need to customize Liferay services? Perhaps you've added a new \nfield to Liferay's `User` object and you want its value to be saved whenever the \n`addUser` or `updateUser` methods of Liferay's API are called. Or maybe you want \nto add some additional logging functionality to some Liferay APIs or other \nservices built using [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder). \nWhatever your case may be, Liferay's service wrappers provide easy-to-use \nextension points for customizing Liferay's services. \n\nTo create a module that overrides one of Liferay's services, use [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli) \nto create a `servicewrapper` project type with the command below (replace the \nclass and package names with your own):\n\n```bash\nblade create -t service-wrapper -p com.liferay.docs.serviceoverride \n-c UserLocalServiceOverride -s \ncom.liferay.portal.kernel.service.UserLocalServiceWrapper service-override\n``` \n\nAs an example, here's the `UserLocalServiceOverride` class that's generated with \nthe Service Wrapper Template:\n\n```java\npackage com.liferay.docs.serviceoverride;\n\nimport com.liferay.portal.kernel.service.UserLocalServiceWrapper;\nimport com.liferay.portal.kernel.service.ServiceWrapper;\nimport org.osgi.service.component.annotations.Component;\n\n@Component(\n    immediate = true,\n    property = {\n    },\n    service = ServiceWrapper.class\n)\npublic class UserLocalServiceOverride extends UserLocalServiceWrapper {\n\n    public UserLocalServiceOverride() {\n        super(null);\n    }\n\n}\n```\n\nNotice that you must specify the fully qualified class name of the service \nwrapper class that you want to extend. The `service` argument was used in full \nin this import statement:\n\n```java\nimport com.liferay.portal.service.UserLocalServiceWrapper;\n```\n\nThis import statement, in turn, allowed the short form of the service wrapper \nclass name to be used in the class declaration of your component class:\n\n```java\npublic class UserLocalServiceOverride extends UserLocalServiceWrapper {...}\n```\n\nThe bottom line is that when using `blade create` to create a service wrapper \nproject, you must specify a fully qualified class name as the `service` \nargument. (This is also true when using `blade create` to create a service \nproject.) For information about creating service projects, please see \n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder). \n\nThe generated `UserLocalServiceOverride` class does not actually customize any \nLiferay service. Before you can test that your service wrapper module actually \nworks, you need to override at least one service method. \n\nOpen your `UserLocalServiceOverride` class and add the following methods:\n\n```java\n@Override\npublic int authenticateByEmailAddress(long companyId, String emailAddress,\n        String password, Map<String, String[]> headerMap,\n        Map<String, String[]> parameterMap, Map<String, Object> resultsMap)\n    throws PortalException {\n\n    System.out.println(\n        \"Authenticating user by email address \" + emailAddress);\n    return super.authenticateByEmailAddress(companyId, emailAddress, password,\n        headerMap, parameterMap, resultsMap);\n}\n\n@Override\npublic User getUser(long userId) throws PortalException {\n    System.out.println(\"Getting user by id \" + userId);\n    return super.getUser(userId);\n}\n```\n\nEach of these methods overrides a Liferay service method. These implementations \nmerely execute a few print statements that before executing the original service \nimplementations. \n\nLastly, you must add the following method to the bottom of your service wrapper \nso it can find the appropriate service it's overriding on deployment. \n\n```java\n@Reference(unbind = \"-\")\nprivate void serviceSetter(UserLocalService userLocalService) {\n    setWrappedService(userLocalService);\n}\n```\n\n[Build and deploy your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project). \nCongratulations! You've created and deployed a Liferay service wrapper! \n\n## Related Topics\n\n- [Upgrading Service Wrappers](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-service-wrapper-hooks)\n- [Installing Blade CLI](/docs/7-2/reference/-/knowledge_base/r/installing-blade-cli)\n- [Creating Projects with Blade CLI](/docs/7-1/tutorials/-/knowledge_base/t/creating-projects-with-blade-cli)\n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-lpkg-files/01-overriding-lpkg-files-intro.markdown",
    "content": "---\nheader-id: overriding-lpkg-files\n---\n\n# Overriding lpkg Files\n\n[TOC levels=1-4]\n\nApplications are delivered through Liferay Marketplace as *lpkg* files. This is \na simple compressed file format that contains .jar files for deploying to \n@product@. If you want to examine an application from Marketplace, all you have \nto do is unzip its .lpkg file to reveal its .jar files. \n\nAfter examining an application, you may want to [customize](/docs/7-2/customization/-/knowledge_base/c/liferay-customization) \none of its .jars. Make your customization in a copy of the .jar, but don't \ndeploy it the way you'd normally deploy an application. By overriding the .lpkg \nfile, you can update application modules without modifying the original .lpkg \nfile. Here are the steps: \n\n1.  Shut down @product@. \n\n2.  Create a folder called `override` in the [`Liferay Home]/osgi/marketplace` \n    folder](/docs/7-2/deploy/-/knowledge_base/d/liferay-home). \n\n3.  Name your updated .jar the same as the .jar in the original .lpkg, minus the \n    version information. For example, if you're overriding the \n    `com.liferay.amazon.rankings.web-1.0.5.jar` from the `Liferay CE Amazon \n    Rankings.lpkg`, you'd name your .jar `com.liferay.amazon.rankings.web.jar`. \n\n4.  Copy this .jar into the `override` folder you created in step one. \n\nThis works for applications from Marketplace, but there's also the static .lpkg \nthat contains core Liferay technology and third-party utilities (such as the \nservlet API, Apache utilities, etc.). To customize or patch any of these .jar \nfiles, follow this process: \n\n1.  Make your customization and package it in a .jar file. \n\n2.  Name your .jar the same as the original .jar, minus the version information. \n    For example, a customized `com.liferay.portal.profile-1.0.4.jar` should be \n    `com.liferay.portal.profile.jar`. \n\n3.  Copy the .jar into the `[Liferay Home]/osgi/static` folder. \n\nNow start @product@. Note that any time you add and remove .jars this way, \n@product@ must be shut down and then restarted for the changes to take effect. \n\nIf you must roll back your customizations, delete the overriding .jar files:\n@product@ uses the original .jar on its next startup. \n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-mvc-commands/01-overriding-mvc-commands-intro.markdown",
    "content": "---\nheader-id: overriding-liferay-mvc-commands\n---\n\n# Overriding Liferay MVC Commands\n\n[TOC levels=1-4]\n\nMVC Commands are used to break up the controller layer of \n[Liferay MVC applications](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet) \ninto smaller, more digestible code chunks.\n\nSometimes you'll want to override an MVC command, whether it's in a Liferay \napplication or another Liferay MVC application whose source code you don't own. \nSince MVC commands are components registered in the OSGi runtime, you can simply \npublish your own customization of the component, give it a higher service \nranking, and deploy it. \n\nAll existing components that reference the original MVC command service \ncomponent (using a greedy reference policy) switch to reference your new one. \nAny existing [reluctant references to the original command must be configured to \nreference the new one](/docs/7-2/customization/-/knowledge_base/c/overriding-osgi-services). \nOnce they're configured with the new service component, their JSP's command URLs \ninvoke the new custom MVC command. \n\nHere are the customization options available for each Liferay MVC Command type:\n\n- MVCActionCommand: [Add logic](/docs/7-2/customization/-/knowledge_base/c/overriding-mvcactioncommand)\n- MVCRenderCommand:\n    - [Add logic](/docs/7-2/customization/-/knowledge_base/c/overriding-mvcrendercommand#adding-logic-to-an-existing-mvc-render-command)\n    - [Redirect to a different JSP](/docs/7-2/customization/-/knowledge_base/c/overriding-mvcrendercommand#redirecting-to-a-new-jsp)\n- MVCResourceCommand: [Add logic](/docs/7-2/customization/-/knowledge_base/c/overriding-mvcresourcecommand) \n\nThis section demonstrates each MVC command customization option. Since the \nsteps for adding logic are generally the same across MVC command types, start \nwith [adding logic](/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands). \n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-mvc-commands/02-adding-logic-to-mvc-commands.markdown",
    "content": "---\nheader-id: adding-logic-to-mvc-commands\n---\n\n# Adding Logic to MVC Commands\n\n[TOC levels=1-4]\n\nYou can completely override MVC commands, or any OSGi service for that matter, \nbut *adding logic* to the commands is the better option. Discarding necessary \nlogic is bad. Conversely any logic you copy from the original might not work in \nnew versions of the portlet. Adding custom logic while continuing to invoke \nthe original logic decouples the custom class from the original implementation. \nKeeping the new logic separate form the original logic keeps the code clean, \nmaintainable, and easy to understand. \n\nHere are the steps for adding logic to MVC commands:\n\n1.  [Implement the interface](#step-1-implement-the-interface)\n2.  [Publish as a component](#step-2-publish-as-a-component)\n3.  [Refer to the original implementation](#step-3-refer-to-the-original-implementation)\n4.  [Add the logic, and call the original](#step-4-add-the-logic)\n\n## Step 1: Implement the interface\n\nImplement the respective MVC Command interface either directly or by extending \nan existing base class that implements it. Extending a base class for the \ninterface relieves you from implementing logic that should typically be a part \nof most command implementations. For example, to add logic to the Blogs \nportlet's `EditEntryMVCActionCommand`, you would extend base class \n`BaseMVCActionCommand`. \n\n```java\npublic class CustomBlogsMVCActionCommand extends BaseMVCActionCommand {...}\n```\n\nCheck the MVC command interfaces for existing base classes:\n\n- [`MVCActionCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCActionCommand.html)\n- [`MVCRenderCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html)\n- [`MVCResourceCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html)\n\nNext make your class a service component. \n\n## Step 2: Publish as a component\n\nThe Declarative Services `@Component` annotation facilitates customizing MVC \ncommands. All the customization options require publishing your MVC command \nclass as a component. For example, this `@Component` annotation declares an \n`MVCActionCommand` service. \n\n```java\n@Component(\n    immediate = true,\n    property = { \n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_ADMIN, \n        \"mvc.command.name=/blogs/edit_entry\",\n        \"service.ranking:Integer=100\" \n    }, \n    service = MVCActionCommand.class\n)\npublic class CustomBlogsMVCActionCommand extends BaseMVCActionCommand {\n    ...\n} \n```\n\nIt publishes `CustomBlogsMVCActionCommand` as a service component for the \n`MVCActionCommand` class. Upon resolving, it's activated immediately because \n`immediate = true`. The component is invoked in the Blogs Admin portlet by the \ncommand URL `/blogs/edit_entry`. Its service ranking of `100` prioritizes it \nahead of the original service component, whose ranking is `0`. \n\nHere's what you need to specify in an `@Component` annotation for your custom \nMVC command:\n\n-   `javax.portlet.name`: for each portlet you want the customization to affect. \n    JSPs in these portlets can invoke the MVC command via applicable command URL \n    tags. You can specify the same portlets as the original MVC command or a \n    subset of those portlets. \n\n-   `mvc.command.name`: this property declares the command URL that maps to this \n    custom MVC command component. \n\n-   `service.ranking:Integer`: set this property to a higher integer than the \n    original service implementation's ranking. The ranking tells the OSGi \n    runtime which service to use, in cases where multiple components register \n    the same service, with the same properties. The higher the integer you \n    specify here, the more weight your component carries. Liferay's service \n    implementations typically have a `0` ranking. \n\n-   `service`: this attribute specifies the service (interface) to override. \n\n-   `immediate`: set this attribute to `true` to activate your component \n    immediately  upon resolution.\n\nYou can refer back to this list as you add `@Component` annotations to your \ncustom MVC commands. \n\nNext reference the original implementation. \n\n## Step 3: Refer to the original implementation\n\nUse a field annotated with `@Reference` to fetch a reference to the original MVC \ncommand component. If there are no additional customizations on the original \ncomponent, this reference will be for the original MVC command type. For \nexample, this field references the original MVC command component \n`EditEntryMVCActionCommand`.\n\n```java\n@Reference(\n    target = \"(component.name=com.liferay.blogs.web.internal.portlet.action.EditEntryMVCActionCommand)\")\nprotected MVCActionCommand mvcActionCommand;\n```\n\nHere's how to add the reference:\n\n1.  Declare the field as the MVC command interface type that it is. For example, \n    the `mvcActionCommand` field is type `MVCActionCommand`. \n\n2.  Add the `@Reference` annotation. \n\n3.  In the annotation, define a `target` attribute that filters on a \n    `component.name` equal to the default service implementation class's fully \n    qualified name. \n\nWhen your custom component resolves, the OSGi runtime assigns the targeted \nservice to your field. It's time to add your custom logic. \n\n## Step 4: Add the logic\n\nAdding the logic involves overriding the primary method of the base class \nyou're extending or the interface you're implementing. In your method override, \nadd your new logic AND then invoke the original implementation. For example, \nthe following method overrides `BaseMVCActionCommand`'s method \n`doProcessAction`.\n\n```java\n@Override\nprotected void doProcessAction(\n\tActionRequest actionRequest, ActionResponse actionResponse)\nthrows Exception {\n    // Add custom logic here \n    ...\n    \n    // Call the original service implementation \n    mvcActionCommand.processAction(actionRequest, actionResponse);\n}\n```\n\nThe method above defines custom logic and then invokes the original service it \nreferenced in the previous step. \n\nIf you use this approach, your extension will continue to work with new versions \nof the original portlet, because no coupling exists between the original portlet \nlogic and your customization. The command implementation class can change. Make \nsure to keep your reference updated to the name of the current implementation \nclass. \n\nCongratulations on adding logic to your existing MVC command. \n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-mvc-commands/03-overriding-mvcrendercommands.markdown",
    "content": "---\nheader-id: overriding-mvcrendercommand\n---\n\n# Overriding MVCRenderCommands\n\n[TOC levels=1-4]\n\nYou can override [`MVCRenderCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html) \nfor any portlet that uses Liferay's MVC framework and publishes an \n`MVCRenderCommand` component. \n\nFor example, Liferay's Blogs application has a class called \n`EditEntryMVCRenderCommand`, with this component:\n\n```java\n@Component(\n    immediate = true,\n    property = {\n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS,\n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_ADMIN,\n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_AGGREGATOR,\n        \"mvc.command.name=/blogs/edit_entry\"\n    },\n    service = MVCRenderCommand.class\n)\n```\n\nThis MVC render command can be invoked from any of the portlets specified by \nthe `javax.portlet.name` parameter, by calling a render URL that names the \nMVC command:\n\n```markup\n<portlet:renderURL var=\"addEntryURL\">\n\t<portlet:param name=\"mvcRenderCommandName\" value=\"/blogs/edit_entry\" />\n\t<portlet:param name=\"redirect\" value=\"<%= viewEntriesURL %>\" />\n</portlet:renderURL>\n```\n\nWhat if you want to override the command, but not for all of the portlets listed \nin the original component? In your override component, just list the \n`javax.portlet.name` of the portlets where you want the override to take effect. \nFor example, if you want to override the `/blogs/edit_entry` MVC render command \njust for the Blogs Admin portlet (the Blogs Application accessed in the site \nadministration section of Liferay), your component could look like this:\n\n```java\n@Component(\n  immediate = true,\n  property = {\n     \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_ADMIN,\n     \"mvc.command.name=/blogs/edit_entry\",\n     \"service.ranking:Integer=100\"\n  },\n  service = MVCRenderCommand.class\n)\n```\n\nNote the last property listed, `service.ranking`. It's used to tell the OSGi \nruntime which service to use, in cases where there are multiple components \nregistering the same service, with the same properties. The higher the integer \nyou specify here, the more weight your component carries. In this case, the \noverride component is used instead of the original one, since the default value \nfor this property is `0`. \n\nAfter that, it's up to you to do whatever you'd like. MVC render commands can be \ncustomized for these purposes:\n\n- [Adding Logic to an Existing MVC Render Command](#adding-logic-to-an-existing-mvc-render-command)\n- [Redirecting to a new JSP](#redirecting-to-a-new-jsp)\n\nStart by exploring how to add logic to an existing MVC render command. \n\n## Adding Logic to an Existing MVC Render Command\n\nYou can add logic to an MVC render command following the \n[general steps for MVC commands](/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands). \nSpecifically for MVC render commands, you must directly implement the \n[`MVCRenderCommand` interface](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html) \nand override its `render` method. \n\nFor example, this custom MVC render command has a placeholder (i.e., at comment \n`//Do something here`) for adding logic to the `render` method: \n\n```java\npublic CustomEditEntryRenderCommand implements MVCRenderCommand {\n\t@Override\n\tpublic String render(RenderRequest renderRequest, \n                        RenderResponse renderResponse)\n           throws PortletException {\n\n        //Do something here\n\n\t\treturn mvcRenderCommand.render(renderRequest, renderResponse);\n\t}\n\n    @Reference(target = \n          \"(component.name=com.liferay.blogs.web.internal.portlet.action.EditEntryMVCRenderCommand)\")\n      protected MVCRenderCommand mvcRenderCommand;\n}\n```\n\nThe example references an `EditEntryMVCRenderCommand` implementation of \n`MVCRenderCommand`. In the `render` method, you'd replace the placeholder with \nnew logic and then invoke the original implementation's logic by calling its \n`render` method. \n\nSometimes, you might need to redirect the request to an entirely new JSP. You \ncan do that from a custom MVC render command module too. \n\n## Redirecting to a New JSP\n\n`MVCRenderCommand`'s `render` method returns a JSP path as a String. By default, \nthe JSP must live in the original module, so you cannot simply specify a path to \na custom JSP in your override module. To redirect it to a JSP in your new \nmodule, you must make the method skip dispatching to the original JSP \naltogether, by using the constant \n[`MVCRenderConstants.MVC_PATH_VALUE_SKIP_DISPATCH` class](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderConstants.html). \nThen you need to initiate your own dispatching process, directing the request to \nyour JSP path. Here's how that might look in practice:\n\n```java\npublic class CustomEditEntryMVCRenderCommand implements MVCRenderCommand {\n\n    @Override\n    public String render(\n        RenderRequest renderRequest, RenderResponse renderResponse) throws\n            PortletException {\n\n        System.out.println(\"Rendering custom_edit_entry.jsp\");\n\n        RequestDispatcher requestDispatcher =\n            servletContext.getRequestDispatcher(\"/custom_edit_entry.jsp\");\n\n        try {\n            HttpServletRequest httpServletRequest = \n                PortalUtil.getHttpServletRequest(renderRequest);\n            HttpServletResponse httpServletResponse = \n                PortalUtil.getHttpServletResponse(renderResponse);\n\n            requestDispatcher.include\n                (httpServletRequest, httpServletResponse);\n        } catch (Exception e) {\n            throw new PortletException\n                (\"Unable to include custom_edit_entry.jsp\", e);\n        }\n\n        return MVCRenderConstants.MVC_PATH_VALUE_SKIP_DISPATCH;\n    }\n\n    @Reference(target = \"(osgi.web.symbolicname=com.custom.code.web)\")\n    protected ServletContext servletContext;\n}\n```\n\nThe servlet context provides access to the request dispatcher. A servlet context \nis automatically created for portlets. It can be created for other modules by \nincluding the following line in your `bnd.bnd` file:\n\n```properties\nWeb-ContextPath: /custom-code-web\n```\n\nFollow these steps to fetch the portlet's servlet context in your custom MVC \nrender command:\n\n1.  Add a `ServletContext` field.\n\n    ```java\n    protected ServletContext servletContext;\n    ```\n\n2.  Add the `@Reference` annotation to the field and set the annotation to \n    filter on the portlet's module. By convention, Liferay puts portlets in \n    modules whose symbolic names end in `.web`. For example, this servlet \n    context reference filters on a module whose symbolic name is \n    `com.custom.code.web`.\n\n    ```java\n    @Reference(target = \"(osgi.web.symbolicname=com.custom.code.web)\")\n    protected ServletContext servletContext;\n    ```\n\nImplement your `render` method this way:\n\n1.  Get a request dispatcher to your module's custom JSP:\n\n    ```java\n    RequestDispatcher requestDispatcher =\n        servletContext.getRequestDispatcher(\"/custom_edit_entry.jsp\");\n    ```\n\n2.  Include the HTTP servlet request and response in the request dispatcher.\n\n    ```java\n    try {\n        HttpServletRequest httpServletRequest = \n            PortalUtil.getHttpServletRequest(renderRequest);\n        HttpServletResponse httpServletResponse = \n            PortalUtil.getHttpServletResponse(renderResponse);\n\n        requestDispatcher.include\n            (httpServletRequest, httpServletResponse);\n    } catch (Exception e) {\n        throw new PortletException\n            (\"Unable to include custom_edit_entry.jsp\", e);\n    }\n    ```\n\n3.  Return the request dispatcher via the constant \n    `MVC_PATH_VALUE_SKIP_DISPATCH`.\n\n    ```java\n    return MVCRenderConstants.MVC_PATH_VALUE_SKIP_DISPATCH;\n    ```\n\nAfter deploying your module, the\n[portlets targeted by your custom `MVCRenderCommand` component](/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands#step-2-publish-as-a-component) \nrender your new JSP. \n\n## Related Topics\n\n- [Adding Logic to MVC Commands](/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands)\n- [Converting StrutsActionWrappers to MVCCommands](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-struts-action-hooks)\n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-mvc-commands/04-override-mvcactioncommands.markdown",
    "content": "---\nheader-id: overriding-mvcactioncommand\n---\n\n# Overriding MVCActionCommands\n\n[TOC levels=1-4]\n\nIn case you want add to a Liferay MVC action command, you can. The OSGi \nframework lets you override MVC action commands if you follow the instructions \nfor [adding logic to MVC commands](/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands). \nIt involves [registering your custom MVC action command as an OSGi component](/docs/7-1/tutorials/-/knowledge_base/t/adding-logic-to-mvc-commands#publish-as-a-component) \nwith the same properties as the original, but with a higher service ranking. \n\nCustom MVC action commands typically extend the [`BaseMVCActionCommand` class](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/BaseMVCActionCommand.html), \nand override its `doProcessAction` method, which returns `void`. Add your logic \nto the original behavior of the action method by getting a reference to the \noriginal service, and calling it after your own logic. \n\n<!--Add link back for 'getting a reference to the original service' once \nadding-logic-to-mvc-commands#refer-to-the-original-implementation article is available\n\nAnd add link back for 'calling it after your own logic' once \nadding-logic-to-mvc-commands#add-the-logic article is available\n-->\n\nFor example, this \n`MVCActionCommand` override checks whether the `delete` action is invoked on a \nblog entry, and prints a message to the log, before continuing with the original \nprocessing:\n\n```java\n@Component(\n    property = { \n        \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS_ADMIN, \n        \"mvc.command.name=/blogs/edit_entry\",\n        \"service.ranking:Integer=100\" \n    }, \n    service = MVCActionCommand.class\n)\npublic class CustomBlogsMVCActionCommand extends BaseMVCActionCommand {\n\n    @Override\n    protected void doProcessAction\n        (ActionRequest actionRequest, ActionResponse actionResponse) \n        throws Exception {\n\n        String cmd = ParamUtil.getString(actionRequest, Constants.CMD);\n\n        if (cmd.equals(Constants.DELETE)) {\n            System.out.println(\"Deleting a Blog Entry\");\n        }\n\n        mvcActionCommand.processAction(actionRequest, actionResponse);\n    }\n\n    @Reference(\n        target = \"(component.name=com.liferay.blogs.web.internal.portlet.action.EditEntryMVCActionCommand)\")\n    protected MVCActionCommand mvcActionCommand;\n\n}\n```\n\nAdding MVC action command logic before existing logic is straightforward and \nmaintains loose coupling between new and old code. \n\n## Related Topics\n\n- [Adding Logic to MVC Commands](/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands)\n- [Overriding MVCRenderCommands](/docs/7-2/customization/-/knowledge_base/c/overriding-mvcrendercommand)\n- [Converting StrutsActionWrappers to MVCCommands](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-struts-action-hooks)\n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-mvc-commands/05-override-mvcresourcecommands.markdown",
    "content": "---\nheader-id: overriding-mvcresourcecommand\n---\n\n# Overriding MVCResourceCommands\n\n[TOC levels=1-4]\n\nIf you need to add functionality to a Liferay MVC resource command, you can. The \nLiferay MVC command framework supports customizing MVC resource commands. It \nfollows the process for [adding logic to MVC commands](/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands) \nand it is similar to the ones described for `MVCRenderCommand` and \n`MVCActionCommand`. There's a couple things to keep in mind:\n\n-  The service to specify in your component is `MVCResourceCommand.class`\n\n-  As with overriding `MVCRenderCommand`, there's no base implementation class \n   to extend. Implement the [`MVCResourceCommand` interface](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html) \n   yourself. \n\n-  Keep your code decoupled from the original code by adding your logic to the \n   original `MVCResourceCommand`'s logic by getting a reference to the original \n   and returning a call to its `serveResource` method:\n\n```java\nreturn mvcResourceCommand.serveResource(resourceRequest, resourceResponse);\n```\n\nThe following example overrides the behavior of \n`com.liferay.login.web.portlet.action.CaptchaMVCResourceCommand`, from the \nLiferay's Login portlet's `login-web` module. It simply prints a line in the \nconsole and then executes the original logic: returning the Captcha image for \nthe account creation screen. \n\n```java\n@Component(\n    property = { \n        \"javax.portlet.name=\" + LoginPortletKeys.LOGIN,\n        \"mvc.command.name=/login/captcha\"\n    }, \n    service = MVCResourceCommand.class\n)\npublic class CustomCaptchaMVCResourceCommand implements MVCResourceCommand {\n\n    @Override\n    public boolean serveResource\n        (ResourceRequest resourceRequest, ResourceResponse resourceResponse) {\n\n        System.out.println(\"Serving login captcha image\");\n\n        return mvcResourceCommand.serveResource(resourceRequest, resourceResponse);\n    }\n\n    @Reference(target = \n        \"(component.name=com.liferay.login.web.internal.portlet.action.CaptchaMVCResourceCommand)\")\n    protected MVCResourceCommand mvcResourceCommand;\n}\n```\n\nAnd that, as they say, is that. Even if you don't own the source code of an \napplication, you can [override its MVC commands](/docs/7-2/customization/-/knowledge_base/c/overriding-liferay-mvc-commands) \njust by knowing the component class name. \n\n## Related Topics\n\n- [Adding Logic to MVC Commands](/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands)\n- [Overriding MVCRenderCommands](/docs/7-2/customization/-/knowledge_base/c/overriding-mvcrendercommand)\n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-osgi-services/01-overriding-osgi-services-intro.markdown",
    "content": "---\nheader-id: overriding-osgi-services\n---\n\n# Overriding OSGi Services\n\n[TOC levels=1-4]\n\nComponents register as services with the OSGi service registry. A service \ncomponent's availability, ranking, and attributes determine whether components \nreferring to the service type bind to that particular service. @product@'s OSGI \ncontainer is a dynamic environment in which services come and go and can be \noverridden, which means that if there's a service whose behavior you want to \nchange, you can override it. Here are the steps for overriding a service:\n\n1.  [Get the service and service reference details](/docs/7-2/customization/-/knowledge_base/c/examining-an-osgi-service-to-override)\n\n2.  [Create a custom service](/docs/7-2/customization/-/knowledge_base/c/creating-a-custom-osgi-service)\n\n3.  [Configure components to use your custom service](/docs/7-2/customization/-/knowledge_base/c/reconfiguring-components-to-use-your-service)\n\n| **Note:** The \n| [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder) \n| services in \n| [portal-impl](@platform-ref@/7.2-latest/javadocs/portal-impl/) \n| are Spring beans that Liferay makes available as OSGi services.\n\nStart with examining the service you want to override. \n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-osgi-services/02-examining-an-service-to-override.markdown",
    "content": "---\nheader-id: examining-an-osgi-service-to-override\n---\n\n# Examining an OSGi Service to Override\n\n[TOC levels=1-4]\n\nCreating and injecting a custom service in place of an existing service requires \nthree things: \n\n- Understanding the service interface \n- The existing service \n- The references to the service \n\nYour custom service must implement the service interface, match \nreferences you want, and might need to invoke the existing service. \n\nGetting components to adopt your custom service immediately can require \nreconfiguring their references to the service. Here you'll flesh out service \ndetails to make these decisions. \n\n## Gathering Information on a Service\n\n1.  Since component service references are extension points, start with \n    determining the service you want to override and components that use that \n    service. \n    \n    <!--replace step 1 text with text below once article is available\n    Since component service references are extension points, start with \n    following the steps provided in \n    Finding Extension Points \n    to determine the service you want to override and components that use that \n    service.\n    -->\n\n2.  Once you know the service and components that use it, use Gogo Shell's \n    Service Component Runtime (SCR) to inspect the components and get the \n    service and reference details. The \n    [Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell) \n    command `scr:info [componentName]` lists the component's attributes and \n    service references. \n\nHere's an example `scr:info` command and results (abbreviated with `...`) that\ndescribe component `override.my.service.reference.OverrideMyServiceReference`\n(from sample module\n[override-my-service-reference](https://portal.liferay.dev/documents/113763090/114000186/override-my-service-reference.zip))\nand its reference to a service of type\n`override.my.service.reference.service.api.SomeService`: \n\n    > scr:info override.my.service.reference.OverrideMyServiceReference \n\n    ...\n    Component Description:\n        Name: override.my.service.reference.portlet.OverrideMyServiceReferencePortlet\n    ...\n    Reference: _someService\n        Interface Name: override.my.service.reference.service.api.SomeService\n        Cardinality: 1..1\n        Policy: static\n        Policy option: reluctant\n        Reference Scope: bundle\n    ...\n    Component Configuration:\n    ComponentId: 2399\n    State: active\n    SatisfiedReference: _someService\n      Target: null\n      Bound to:        6840\n          Properties:\n            component.id = 2400\n            component.name = override.my.service.reference.service.impl.SomeServiceImpl\n            objectClass = [override.my.service.reference.service.api.SomeService]\n            service.bundleid = 524\n            service.id = 6840\n            service.scope = bundle\n    ...\n \nThe `scr:info` results, like the ones above, contain information relevant to \ninjecting a custom service. Here's what you'll do with the information: \n\n1.  [Copy the service interface name](#step-1-copy-the-service-interface-name) \n\n2.  [Copy the existing service name](#step-2-copy-the-existing-service-name) \n\n3.  [Gather reference configuration details (if reconfiguration is necessary)](#step-3-gather-reference-configuration-details-if-reconfiguration-is-needed)\n\nStart with the service interface. \n\n## Step 1: Copy the Service Interface Name\n\nThe reference's *Interface Name* is the service interface's fully qualified \nname.\n\n    ...\n    Reference: _someService\n        Interface Name: override.my.service.reference.service.api.SomeService\n        ...\n\n**Copy and save the interface name**, because it's the type your custom service \nmust implement. \n\n| Javadocs for @product@ service interfaces are at these locations:\n| \n| - [@product@ core Javadocs](@platform-ref@/7.2-latest/javadocs/)\n| - [@product@ app Javadocs](@app-ref@)\n| - [MVNRepository](https://mvnrepository.com/)\n|   and\n|   [Maven Central](https://search.maven.org/)\n|   (for Liferay and non-Liferay artifact Javadocs).\n\n## Step 2: Copy the Existing Service Name\n\nIf you want to invoke the existing service along with your custom service, get \nthe existing service name. \n\nThe `src:info` result's Component Configuration section lists the existing \nservice's fully qualified name. For example, the \n`OverrideMyServiceReferencePortlet` component's references `_someService` is \nbound to a service component whose fully qualified name is \n`override.my.service.reference.service.impl.SomeServiceImpl`. \n\n    Component Configuration:\n    ...\n    SatisfiedReference: _someService\n      ...\n      Bound to:        6840\n          Properties:\n            ...\n            component.name = override.my.service.reference.service.impl.SomeServiceImpl\n\n**Copy the `component.name`** so you can reference the service in your \n[custom service](/docs/7-2/customization/-/knowledge_base/c/creating-a-custom-osgi-service).\n\nHere's an example of referencing the service above. \n\n```java\n@Reference  (\n    target = \"(component.name=override.my.service.reference.service.impl.SomeServiceImpl)\"\n)\nprivate SomeService _defaultService;\n```\n\n## Step 3: Gather Reference Configuration Details (if reconfiguration is needed)\n\nThe service reference's policy and policy option determine a component's \nconditions for adopting a particular service. \n\n- If the reference's policy option is `greedy`, it binds to the matching, \n  highest ranking service right away. The reference need not be reconfigured to \n  adopt your service. \n\n- If policy is `static` and its policy option is `reluctant`, however, the \n  component requires one of the following conditions to switch from using the \n  existing service it's referencing to using the matching, highest ranking \n  service (i.e., you'll rank your custom service highest):\n\n   1. The component is reactivated\n   2. The component's existing referenced service is unavailable\n   3. The component's reference is modified so that it does not match the\n      existing service but matches your service\n\n[Reconfiguring the reference](/docs/7-2/customization/-/knowledge_base/c/reconfiguring-components-to-use-your-service) \ncan be the quickest way for the component to adopt a new service. \n\n**Gather these details:** \n\n- *Component name:* Find this at *Component Description* &rarr; *Name*. For example,\n\n        Component Description:\n            Name: override.my.service.reference.portlet.OverrideMyServiceReferencePortlet\n            ...\n\n- *Reference name:* The *Reference* value (e.g., `Reference: _someService`).\n\n- *Cardinality:* Number of service instances the reference can bind to. \n\n| **Note**: Declarative Services makes all components configurable through OSGi \n| Configuration Admin. Each `@Reference` annotation in the source code has a name \n| property, either *explicitly* set in the annotation or *implicitly* derived from \n| the name of the member on which the annotation is used. \n| \n| -   If no reference name property is used and the `@Reference` is on a field, \n|     then the reference name is the field name. If `@Reference` is on a field \n|     called `_someService`, for example, then the reference name is \n|     `_someService`. \n| -   If the `@Reference` is on a method, then heuristics derive the reference \n|     name. Method name suffix is used and prefixes such as `set`, `add`, and \n|     `put` are ignored. If `@Reference` is on a method called \n|     `setSearchEngine(SearchEngine se)`, for example, then the reference name is \n|     `SearchEngine`. \n\nAfter [creating your custom service](/docs/7-2/customization/-/knowledge_base/c/creating-a-custom-osgi-service) \n(next), you'll use the details you collected here to [configure the component \nto use your custom service](/docs/7-2/customization/-/knowledge_base/c/reconfiguring-components-to-use-your-service). \n\nCongratulations on getting the details required for overriding the OSGi service! \n\n## Related Topics\n\n- [OSGi Services and Dependency Injection with Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services)\n- [Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-osgi-services/03-creating-a-custom-osgi-service.markdown",
    "content": "---\nheader-id: creating-a-custom-osgi-service\n---\n\n# Creating a Custom OSGi Service\n\n[TOC levels=1-4]\n\nIt's time to implement your OSGi service. Make sure to [examine the service and \nservice reference details](/docs/7-2/customization/-/knowledge_base/c/examining-an-osgi-service-to-override), \nif you haven't done so already. Here you'll create a custom service that \nimplements the service interface, declares it an OSGi service of that type, and \nmakes it the best match for binding with other components. \n\nThe example custom service `CustomServiceImpl` implements service interface\n(from sample module\n[`overriding-service-reference`](https://portal.liferay.dev/documents/113763090/114000186/overriding-service-reference.zip))\n`SomeService`, declares itself an OSGi service of the `SomeService` service\ntype, and even delegates work to the existing service. Examine the example code\nbelow as you follow the steps for creating your custom service:\n\n```java\n@Component(\n    property = {\n        \"service.ranking:Integer=100\"\n    },\n    service = SomeService.class\n)\npublic class CustomServiceImpl implements SomeService {\n\n    @Override\n    public String doSomething() {\n\n        StringBuilder sb = new StringBuilder();\n        sb.append(this.getClass().getName());\n        sb.append(\", which delegates to \");\n        sb.append(_defaultService.doSomething());\n\n        return sb.toString();\n    }\n\n    @Reference  (\n        target = \"(component.name=override.my.service.reference.service.impl.SomeServiceImpl)\"\n    )\n    private SomeService _defaultService;\n}\n```\n\nHere are the steps to create a custom OSGi service:\n\n1.  [Create a module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project). \n\n2.  Create your custom service class so that it `implements` the \n    [service interface](/docs/7-2/customization/-/knowledge_base/c/examining-an-osgi-service-to-override#step-1-copy-the-service-interface-name)\n    you want. In the example above, `CustomServiceImpl implements SomeService`.\n    Step 5 (later) demonstrates implementing the interface methods. \n\n3.  Make your class a Declarative Services component that is the best match for \n    references to the service interface:\n\n    - Use an `@Component` annotation and `service` attribute to make your \n      classes a Declarative Services (DS) component. This declares your class \n      to be an OSGi service that can be made available in the OSGi service \n      registry. The example class above is a DS service component of service \n      type `SomeService.class`. \n\n    - Use a `service.ranking:Integer` component property to rank your service \n      higher than existing services. The `\"service.ranking:Integer=100\"` \n      property above sets the example's ranking to `100`. \n\n4.  If you want to invoke the existing service implementation, declare a field \n    that uses a Declarative Services reference to the existing service. Use the \n    [`component.name` you copied when you examined the service](/docs/7-2/customization/-/knowledge_base/c/examining-an-osgi-service-to-override#step-2-copy-the-existing-service-name) \n    to target the existing service. The example above refers to an existing \n    service like this:\n\n    ```java\n    @Reference  (\n        target = \"(component.name=override.my.service.reference.service.impl.SomeServiceImpl)\"\n    )\n    private SomeService _defaultService;\n    ```\n\n    The field lets you invoke the existing service in your custom service. \n\n5.  Override the interface's methods. Optionally, delegate work to the existing \n    service implementation (see previous step). \n\n    The example custom service's `doSomething` method delegates work to the \n    original service implementation. \n\n6.  Register your custom service with the OSGi runtime framework by \n    [deploying your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project).\n\nComponents that reference the service type you implemented and whose reference \npolicy option is `greedy` bind to your custom service immediately. Components \nbound to an existing service and whose reference policy option is `reluctant` \ncan be dynamically reconfigured to use your service. That's demonstrated next. \n\n## Related Topics\n\n[OSGi Services and Dependency Injection with Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services)\n"
  },
  {
    "path": "en/developer/customization/articles/50-overriding-osgi-services/04-reconfiguring-components-to-use-your-osgi-service-reference.markdown",
    "content": "---\nheader-id: reconfiguring-components-to-use-your-service\n---\n\n# Reconfiguring Components to Use Your OSGi Service\n\n[TOC levels=1-4]\n\nIn many cases, assigning your [custom service (service)](/docs/7-2/customization/-/knowledge_base/c/creating-a-custom-osgi-service) \na higher ranking convinces components to unbind from their current service and \nbind to yours. In other cases, components keep using their current service. Why \nis that? And how do you make components adopt your service? \nThe component's [service reference policy option](/docs/7-2/customization/-/knowledge_base/c/examining-an-osgi-service-to-override#step-3-gather-reference-configuration-details-if-reconfiguration-is-needed) \nis the key to determining the service. \n\nHere are the policy options:\n\n`greedy`: The component uses the matching, highest ranking service as soon as \nit's available. \n\n`reluctant`: The component uses the matching, highest ranking service available \nin the following events:\n\n-   the component is (re)activated\n-   the component's existing referenced service becomes unavailable\n-   the component's reference is modified so that it no longer matches the \n    existing bound service \n\nIn short, references with greedy policy options adopt your higher ranking \nservice right away, while ones with reluctant policy options require particular \nevents. What's great is that  @product@'s Configuration Admin lets you use \nconfiguration files (config files) or the API to swap in service reference \nchanges on the fly. Here you'll use a config file to reconfigure a service \nreference to use your custom service immediately. \n\nThis article uses example modules `override-my-service-reference` and \n`overriding-service-reference` to demonstrate reconfiguring a service reference, \nbinding the component to a different service. you can apply the steps below to \nconfigure your own customization.\n<!--TODO: Uncomment once zips are ported to the new site.\nYou can download the modules and \nbuild them using Gradle (bundled with each module) or you can apply the steps \nbelow to configure your own customization. Executing `gradlew jar` in each \nexample module root generates the module JAR to the `build/libs` folder. \n-->\n\n-   `override-my-service-reference` \n    ([download](https://portal.liferay.dev/documents/113763090/114000186/override-my-service-reference.zip)):\n    This module's portlet component `OverrideMyServiceReferencePortlet`'s field \n    `_someService` references a service of type `SomeService`. The reference's \n    policy is static and reluctant. By default, it binds to an implementation \n    called `SomeServiceImpl`. \n\n-   `overriding-service-reference`\n    ([download](https://portal.liferay.dev/documents/113763090/114000186/overriding-service-reference.zip)):\n    Provides a custom `SomeService` implementation called `CustomServiceImpl`. \n    The module's configuration file overrides \n    `OverrideMyServiceReferencePortlet`'s `SomeService` reference so that it \n    binds to `CustomServiceImpl`. \n\nYou're ready to reconfigure a component's service reference to target your \ncustom service.\n\n## Reconfiguring the Service Reference\n\n@product@'s Configuration Admin lets you use configuration files to swap in \nservice references on the fly. \n\n1.  [Create a system configuration file](/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files) \n    named after the referencing component. Follow the name convention \n    `[component].config`, replacing `[component]` with the [component name](/docs/7-2/customization/-/knowledge_base/c/examining-an-osgi-service-to-override#step-3-gather-reference-configuration-details-if-reconfiguration-is-needed). \n    The configuration file name for the example component `override.my.service.reference.portlet.OverrideMyServiceReferencePortlet` \n    is: \n\n        override.my.service.reference.portlet.OverrideMyServiceReferencePortlet.config\n\n2.  In the configuration file, add a reference target entry that filters on your\n    custom service. Follow this format for the entry:\n\n    ```properties\n    [reference].target=[filter]\n    ```\n\n    Replace `[reference]` with the name of the reference you're overriding. \n    Replace `[filter]` with service properties that filter on your custom \n    service. \n\n    This example filters on the `component.name` service property:\n\n    ```properties\n    _someService.target=\"(component.name\\=overriding.service.reference.service.CustomServiceImpl)\" \n    ```\n    \n    This example filters on the `service.vendor` service property:\n\n    ```properties\n    _someService.target=\"(service.vendor\\=Acme, Inc.)\"\n    ```\n\n3.  Optionally, you can add a `cardinality.minimum` entry to specify the number \n    of services the reference can use. Here's the format:\n\n    ```properties\n    [reference].cardinality.minimum=[int]\n    ```\n\n    Here's an example cardinality minimum:\n\n    ```properties\n    _someService.cardinality.minimum=1\n    ```\n\n4.  Deploy the configuration by copying the configuration file into the folder\n    `[Liferay_Home]/osgi/configs`. \n\nExecuting `scr:info` on your component shows that the custom service is now \nbound to the reference. \n\nFor example, executing \n`scr:info override.my.service.reference.portlet.OverrideMyServiceReferencePortlet` \nreports the following information:\n\n    ...\n    Component Description:\n      Name: override.my.service.reference.portlet.OverrideMyServiceReferencePortlet\n      ...\n      Reference: _someService\n        Interface Name: override.my.service.reference.service.api.SomeService\n        Cardinality: 1..1\n        Policy: static\n        Policy option: reluctant\n        Reference Scope: bundle\n        ...\n      Component Configuration:\n        ComponentId: 2399\n        State: active\n        SatisfiedReference: _someService\n          Target: (component.name=overriding.service.reference.CustomServiceImpl)\n          Bound to:        6841\n              Properties:\n                _defaultService.target = (component.name=overriding.service.reference.service.CustomServiceImpl)\n                component.id = 2398\n                component.name = overriding.service.reference.service.CustomServiceImpl\n                objectClass = [override.my.service.reference.service.api.SomeService]\n                service.bundleid = 525\n                service.id = 6841\n                service.scope = bundle\n          Component Configuration Properties:\n            _someService.target = (component.name=overriding.service.reference.service.CustomServiceImpl)\n            ...\n\nThe example component's `_someService` reference targets the custom service \ncomponent `overriding.service.reference.service.CustomServiceImpl`. \n`CustomServiceImpl` references default service `SomeServiceImpl` to delegate \nwork to it. \n\n![Figure 1: Because the example component's service reference is overridden by the configuration file deployment, the portlet indicates it's calling the custom service.](../../images/overriding-service-refs-result.png)\n\n@product@ processed the configuration file and injected the service reference, \nwhich in turn bound the custom service to the referencing component! \n\n## Related Topics\n\n- [OSGi Services and Dependency Injection with Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services)\n- [Using Felix Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\n"
  },
  {
    "path": "en/developer/customization/articles/50-portlet-filters/01-portlet-filters-intro.markdown",
    "content": "---\nheader-id: portlet-filters\n---\n\n# Portlet Filters\n\n[TOC levels=1-4]\n\nPortlet filters intercept requests and responses at the start of the\n[portlet request processing phase](/docs/7-2/frameworks/-/knowledge_base/f/portlets). \nPortlet filters are commonly used for these things: \n\n- Transform content\n- Add or modify request and response attributes\n- Suspend a portlet phase to get user input\n- Audit portlet activity\n\nThe \n[`javax.portlet.filter`](http://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/filter/package-frame.html)\npackage defines a portlet filter interface for each phase. Here are the steps\nfor developing a portlet filter: \n\n1.  Implement the\n    [portlet filter interface](http://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/filter/package-frame.html)\n    for the phase it's intercepting. Here are common interface methods to\n    override: \n\n    `doFilter`: Here's where you take action. This method is invoked at the \n    start of the portlet request processing phase. The request and response\n    parameters provide access to portlet content and attributes. The\n    `FilterChain` parameter can be used to invoke the next filter in the phase. \n\n    `init`: Initialize the filter. The `FilterConfig` parameter can be used to \n    prepare the filter. \n\n    `destroy`: Perform any filter cleanup. \n\n2.  Target the desired portlet(s). \n\n3.  Choose how to prioritize the filter among other filters in the phase: \n\n    -   OSGi Declarative Service Component portlet filters use a service ranking\n        property. High ranking filters execute before lower ones. \n    -   `<filter-mapping>` element order in a portlet application's `portlet.xml` \n        file. \n    -   The `ordinal` element value of a filter class annotated with \n        `@PortletLifecycleFilter`. Low ordinal value filters execute before\n        higher ones. \n\nBelow is demonstrated applying multiple filters to a portlet's render phase. The\nfilters are\n[OSGi Declarative Service (DS) Components](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services),\nbut filters can also be applied to a portlet using a `portlet.xml` descriptor or\na `@PortletLifecycleFilter` annotation. See the Portlet 3.0 Specification for\ndetails. The sample code is available\n[here](https://portal.liferay.dev/learn/code-samples/-/cs/list/7.2/java8/workspace-gradle/modules/applications/portlets/render-filter-portlet). \n\n## Sample Portlet\n\nThe sample portlet `MembersListPortlet` is a\n[Liferay MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet)\nthat lists names and email addresses when users click its *Load Users*\nbutton. The information is based on `Person` objects that the portlet class\npasses to the View template via a request attribute called\n`MembersListPortlet.MEMBERLIST_ATTRIBUTE`. \n\n```java\npublic void loadUsers(ActionRequest actionRequest, ActionResponse actionResponse) {\n\n    actionRequest.setAttribute(MembersListPortlet.MEMBERLIST_ATTRIBUTE, createStaticUserList());\n}\n```\n\nTwo render filters are applied to the portlet:\n\n1.  Render filter 1 hides parts of the user email addresses (e.g., for \n    privacy) by modifying the request object.\n\n2.  Render filter 2 logs portlet render phase statistics. \n\nAdding the `MemberList` portlet to a page and clicking the `Load Users` button\nrenders each `Person`'s name and partially hidden email address, thanks to the\nfilter `EncodingPersonEmailsRenderFilter`.\n\n```\nSievert Shayne\n\nSievert.Sha...@...mple.com\n\nVida Jonas\n\nVida.Jo...@...mple.com\n...\n```\n\nIf you set the portlet's log level to `debug`, it prints the render phase\nstatistics. \n\n```\nPortlet com_liferay_code_samples_portal_modules_applications_portlets_render_filter_MembersListPortlet rendered in 7791 ms\nPortlet com_liferay_code_samples_portal_modules_applications_portlets_render_filter_MembersListPortlet rendered 2 times with an average 356135 ms render time\n```\n\nThe first filter modifies portlet content via the request object. \n\n## Render filter 1 hides parts of user email addresses\n\n`EncodingPersonEmailsRenderFilter` is a `RenderFilter` that hides parts of user\nemail addresses by modifying a request attribute. Here is the class:\n\n```java\n@Component(\n        immediate = true,\n        property = {\n                \"javax.portlet.name=\" + MembersListPortlet.MEMBERSLIST_PORTLET_NAME,\n                \"service.ranking:Integer=1\" \n        },\n        service = PortletFilter.class\n)\npublic class EncodingPersonEmailsRenderFilter implements RenderFilter {\n\n    @Override\n    public void doFilter(RenderRequest request, RenderResponse response, FilterChain chain)\n            throws IOException, PortletException {\n\n        //This is executed before the portlet render\n        Optional.ofNullable((List<Person>)request.getAttribute(MembersListPortlet.MEMBERLIST_ATTRIBUTE))\n                .ifPresent(personList ->\n                        request.setAttribute(MembersListPortlet.MEMBERLIST_ATTRIBUTE, ofuscateEmails(personList)));\n\n        // Invoke the rest of the filters in the chain\n        //  (it also invokes the Portlet render method if this is the last filter in the chain\n        chain.doFilter(request, response);\n\n    }\n\n    private List<Person> ofuscateEmails(List<Person> list) {\n        return list.stream()\n                .map(this::ofuscatePersonEmail)\n                .collect(Collectors.toList());\n    }\n\n    private Person ofuscatePersonEmail(Person person) {\n        return new Person(person.getName(),\n                          person.getEmail().replaceFirst(\"(.+)(...)@(...)(.*)\", \"$1...@...$4\"));\n\n    }\n\n    @Override\n    public void init(FilterConfig filterConfig) throws PortletException {\n\n    }\n\n    @Override\n    public void destroy() {\n\n    }\n}\n```\n\nThe `@Component` annotation declares the filter to be an OSGi DS Component. Here\nare its elements and properties:\n\n`immediate = true` sets the component ready to start upon being installed. \n\n`service = PortletFilter.class` defines the component to be a \n`PortletFilter` service. \n\n`javax.portlet.name = + MembersListPortlet.MEMBERSLIST_PORTLET_NAME` \nlinks the filter to the target portlet. Note, multiple portlets can be\nlisted. \n\n`service.ranking:Integer=1` sets the filter to execute after filters that \nare ranked higher than `1`. \n \n`EncodingPersonEmailsRenderFilter` *implements* the\n[`RenderFilter`](http://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/filter/RenderFilter.html)\ninterface, overriding the `doFilter`, `init`, and `destroy` methods. \n\n`doFilter` modifies the attribute  `MembersListPortlet.MEMBERLIST_ATTRIBUTE`'s\nlist of `Person`s by replacing parts of their email addresses with ellipses\n(`...`). It delegates the `ofuscatePersonEmail` method to do the modifications.\nThen `doFilter` invokes `chain.doFilter(request, response)` to execute the next\n`RenderFilter` or next portlet processing phase. \n\n| **Note:** Filters can also intercept and block the execution of a portlet \n| phase.  In the `doFilter` method, this is usually done by throwing an\n| exception or by not calling the next element in the filter chain. \n\n## RenderFilter 2 Logs Statistics\n\n`MembersListStatsRenderFilter` is a `RenderFilter` that logs the number of times\nthe portlet is rendered and the average render time. Here's the code:\n\n```java\n@Component(\n        immediate = true,\n        property = {\n                \"javax.portlet.name=\" + MembersListPortlet.MEMBERSLIST_PORTLET_NAME,\n                \"service.ranking:Integer=100\"\n        },\n        service = PortletFilter.class\n)\npublic class MembersListStatsRenderFilter implements RenderFilter {\n\n    //Thread safe - accumulator that keeps the number of times the portlet has been rendered\n    private final LongAdder hits = new LongAdder();\n\n    //Thread safe accumulator that keeps total time spent rendering the portlet.\n    private final LongAdder accumulatedTimeMs = new LongAdder();\n\n    @Override\n    public void doFilter(RenderRequest request, RenderResponse response, FilterChain chain) throws IOException, PortletException {\n\n        long startTime = System.nanoTime();\n\n        chain.doFilter(request, response);\n\n        long renderTime = (System.nanoTime() - startTime) / 1000;\n        hits.increment();\n        accumulatedTimeMs.add(renderTime);\n\n        if (LOG.isDebugEnabled()) {\n            long totalHits = hits.longValue();\n            long averageRenderTimeNs = accumulatedTimeMs.longValue() / totalHits;\n            LOG.debug(\"Portlet \" + MembersListPortlet.MEMBERSLIST_PORTLET_NAME + \" rendered in \" + renderTime + \" ms\");\n            LOG.debug(\"Portlet \" + MembersListPortlet.MEMBERSLIST_PORTLET_NAME + \" rendered \" + hits.longValue()\n                    + \" times with an average \" + averageRenderTimeNs + \" ms render time\");\n        }\n    }\n\n    ...\n\n    private static final Log LOG = LogFactoryUtil.getLog(MembersListStatsRenderFilter.class);\n}\n```\n\nAs with `EncodingPersonEmailsRenderFilter`, it's an OSGi DS Component that is a\n`PortletFilter` service, starts upon installation, applies to the\n`MembersListPortlet`, and has a service ranking. Since its ranking is `100`, it\nis executed before render filter `EncodingPersonEmailsRenderFilter`. \n\n`MembersListStatsRenderFilter`'s `doFilter()` method audits the render phase in\nthese ways:\n\n1.  Notes the render phase start time. \n\n2.  Executes `chain.doFilter(request, response)` to invoke all of the other \n    `RenderFilter`s in the `FilterChain`. \n\n3.  Increments the number of times the portlet renders.\n\n4.  Calculates the average render time.\n\n5.  Logs the times rendered and average render time. \n\nConsider creating your own filters to intercept portlet processing phases. \n\n## Related Topics \n\n[Portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets)\n\n[JSP Overrides Using Portlet Filters](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-portlet-filters)\n\n[Liferay MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet)\n"
  },
  {
    "path": "en/developer/customization/articles/50-product-navigation/01-intro.markdown",
    "content": "---\nheader-id: product-navigation\n---\n\n# Product Navigation\n\n[TOC levels=1-4]\n\n@product@'s product navigation consists of the main menus you use to customize,\nconfigure, and navigate the system. When you edit a page, switch to a different\nSite scope, access a User's credentials, etc., you're using the default\nnavigation menus. Customizing a default menu can help give your Liferay instance\na unique touch. You can extend and customize the default product navigation to\nfit your need.\n\nThere are four product navigation sections that you can extend:\n\n- Product Menu\n- Control Menu\n- Simulation Menu\n- User Personal Menu\n\n![Figure 1: The main product navigation menus include the Product Menu, Control Menu, Simulation Menu and User Personal Menu.](../../images/product-navigation-summary.png)\n\nThe Product Menu is on the left, and displays the Control Panel and Site\nAdministration functionality. The Control Menu is on top, offering navigation to\nthe Product Menu, Simulation Menu (the right menu), and the *Add* button. When\ncertain settings are enabled (e.g., Staging, Page Customization, etc.), more\ntools are offered. The Simulation Menu offers options to simulate your Site's\nlook for different scenarios (devices, user segments, etc.). Finally, the User\nPersonal Menu holds selectable items containing a user's own account settings.\n\nYou'll learn more about each of these product navigation sections next.\n\n## Product Menu\n\nBy default, Liferay's Product Menu consists of two main sections: Control Panel\nand Site Administration. These sections are called *Panel Categories*. For\ninstance, the Control Panel is a single Panel Category, and when clicking on it,\nyou see six child Panel Categories: *Users*, *Sites*, *Apps*, *Configuration*,\nand *Workflow*. Clicking a child Panel Category shows *panel apps*.\n\nThe Product Menu is intuitive and easy to use---but you can still change it any\nway you want. You can reorganize the Panel Categories and apps, or add\ncompletely new categories and populate them with custom Panel Apps. You'll\nlearn how to provide new or modified Panel Categories and Panel Apps for the\nProduct Menu. For more information, read the\n[Customizing the Product Menu](/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu)\narticles.\n\n## Control Menu\n\nThe Control Menu is the most visible and accessible menu. For example, on your \nhome page, the Control Menu offers default options for accessing the Product\nMenu, Simulation Menu, and Add Menu. You can think of this menu as the gateway\nto configuring options in @product@.\n\n![Figure 2: The Control Menu has three configurable areas: left, right, and middle. It also displays the title and type of page that you are currently viewing.](../../images/control-menu-home.png)\n\nIf you navigate away from the home page, the Control Menu adapts and provides\nhelpful functionality for whatever option you're using. For example, if you\nnavigate to Site Administration &rarr; *Content & Data* &rarr; *Web Content*,\nyou see a Control Menu with different functionality tailored for that option.\n\n![Figure 3: When switching your context to web content, the Control Menu adapts to provide helpful options for that area.](../../images/control-menu-web-content.png)\n\nThe default Control Menu contains three categories representing the left,\nmiddle, and right portions of the menu. You can create navigation entries for\neach category. For more information, read the\n[Customizing the Control Menu](/docs/7-2/customization/-/knowledge_base/c/customizing-the-control-menu)\narticles.\n\n## Simulation Menu\n\nWhen testing how pages and apps appear for users, it's important to simulate\ntheir views in as many ways as possible. The Simulation Menu on the right-side\nof the main page allows this, and you can extend the menu if you need to\nsimulate something that it does not provide.\n\n![Figure 4: The Simulation Menu offers a device preview application.](../../images/simulation-menu-preview.png)\n\nThere are few differences between the Simulation Menu and Product Menu, mostly\nbecause they extend the same base classes. The Simulation Menu, by default, is\nmade up of only one Panel Category and one Panel App. Liferay provides the\n[`SimulationPanelCategory`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/product-navigation/product-navigation-simulation-web/src/main/java/com/liferay/product/navigation/simulation/web/internal/application/list/SimulationPanelCategory.java)\nclass, a hidden category needed to hold the `DevicePreviewPanelApp`. This is the\napp and functionality you see in the Simulation Menu by default.\n\nFor more information, read the\n[Extending the Simulation Menu](/docs/7-2/customization/-/knowledge_base/c/extending-the-simulation-menu)\narticle.\n\n## User Personal Menu\n\nThe User Personal Menu displays options unique to the current user. By default,\nthis menu appears as an avatar button that expands the User Settings sub-menu\njust below the Control Menu. In a custom theme, the User Personal Menu could\nappear anywhere in the interface.\n\n![Figure 5: By default, the User Personal Menu contains the signed-in user's avatar, which opens the user's settings when selected.](../../images/user-personal-menu.png)\n\nAlthough Liferay's default User Personal Menu is bare-bones, you can\nadd more functionality to fit your needs. Unlike other product navigation menus\n(e.g., Product Menu), the User Personal Bar does not require the\nextension/creation of Panel Categories and Panel Apps. It uses another common\nLiferay framework for providing functionality:\n[Portlet Providers](/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes).\n\nThe User Personal Menu can be seen as a placeholder in every Liferay theme. By\ndefault, Liferay provides one sample *User Personal Bar* portlet that fills that\nplaceholder, but the portlet Liferay provides can be replaced by other portlets.\n\n| **Note:** You can add the User Personal Bar to your theme by adding the\n| following snippet into your `portal_normal.ftl`:\n| \n| ```\n| <@liferay.user_personal_bar />\n| ```\n\nFor more information, read the\n[Customizing the User Personal Bar and Menu](/docs/7-2/customization/-/knowledge_base/c/customizing-the-user-personal-bar-and-menu)\narticle.\n"
  },
  {
    "path": "en/developer/customization/articles/50-product-navigation/02-customizing-the-product-menu/01-customizing-the-product-menu-intro.markdown",
    "content": "---\nheader-id: customizing-the-product-menu\n---\n\n# Customizing the Product Menu\n\n[TOC levels=1-4]\n\nCustomizing the Product Menu can be completed by adding Panel Categories and\nPanel Apps.\n\n| **Note:** The Product Menu cannot be changed by applying a new theme. To\n| change the layout/style of the Product Menu, you must create and deploy a\n| theme contributor. See the\n| [Theme Contributors](/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site)\n| article for more details.\n\nTo create these entities, you must implement the\n[`PanelCategory`](@app-ref@/application-list/latest/javadocs/com/liferay/application/list/PanelCategory.html)\nand\n[`PanelApp`](@app-ref@/application-list/latest/javadocs/com/liferay/application/list/PanelApp.html)\ninterfaces.\n\n## PanelCategory Interface\n\nThe `PanelCategory` interface requires you to implement the following methods:\n\n- `getNotificationCount`: returns the number of notifications to be shown in\n  the Panel Category.\n- `include`: renders the body of the Panel Category.\n- `includeHeader`: renders the Panel Category header.\n- `isActive`: whether the panel is selected.\n- `isPersistState`: whether to persist the Panel Category's state to the\n  database. This saves the state of the Panel Category when navigating away from\n  the menu.\n\nYou can reduce the number of methods you must implement if you extend a base\nclass that already implements the `PanelCategory` interface. The recommended way\nto do this is by extending the\n[`BasePanelCategory`](@app-ref@/application-list/latest/javadocs/com/liferay/application/list/BasePanelCategory.html)\nor\n[`BaseJSPPanelCategory`](@app-ref@/application-list/latest/javadocs/com/liferay/application/list/BaseJSPPanelCategory.html)\nabstract classes. Typically, the `BasePanelCategory` is extended for basic\ncategories (e.g., the Control Panel category) that only display the category\nname. To add more complex functionality, you can then provide a custom UI for\nyour panel using any front-end technology by implementing the `include()` or\n`includeHeader()` from the `PanelCategory` interface.\n\nIf you plan to use JSPs as the front-end technology, extend a base class called\n`BaseJSPPanelCategory` that already implements the methods `include()` and\n`includeHeader()` for you.\n \n| **Note:** In this article, example JSPs describe how to provide functionality\n| to Panel Categories and Panel Apps. JSPs, however, are not the only way to provide\n| front-end functionality to your categories/apps. You can create your own class\n| implementing `PanelCategory` to use other technologies such as FreeMarker.\n\nMore information on provided base classes for your `PanelCategory`\nimplementation are described next.\n\n### BasePanelCategory\n\nIf you need something simple for your Panel Category like a name, extending\n`BasePanelCategory` is probably sufficient. For example, the\n[`ControlPanelCategory`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/product-navigation/product-navigation-control-panel/src/main/java/com/liferay/product/navigation/control/panel/internal/application/list/ControlPanelCategory.java)\nextends `BasePanelCategory` and specifies a `getLabel` method to set and display\nthe Panel Category name.\n\n```java\n@Override\npublic String getLabel(Locale locale) {\n    return LanguageUtil.get(locale, \"control-panel\");\n}\n```\n\n### BaseJSPPanelCategory\n\nIf you need more complex functionality, extend `BaseJSPPanelCategory` and use\nJSPs to render the Panel Category. For example, the\n[`SiteAdministrationPanelCategory`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/product-navigation/product-navigation-site-administration/src/main/java/com/liferay/product/navigation/site/administration/internal/application/list/SiteAdministrationPanelCategory.java)\nspecifies the `getHeaderJspPath` and `getJspPath` methods. You could create\na JSP with the UI you want to render and specify its path in methods like these:\n\n```java\n@Override\npublic String getHeaderJspPath() {\n    return \"/sites/site_administration_header.jsp\";\n}\n\n@Override\npublic String getJspPath() {\n    return \"/sites/site_administration_body.jsp\";\n}\n```\n\nOne JSP renders the Panel Category's header (displayed when panel is collapsed)\nand the other its body (displayed when panel is expanded).\n\nNext, you'll learn about the `PanelApp` interface.\n\n## PanelApp Interface\n\nThe `PanelApp` interface requires you to implement the following methods:\n\n- `getNotificationCount`: returns the number of notifications for the user.\n- `getPortlet`: returns the portlet associated with the application.\n- `getPortletId`: returns the portlet's ID associated with the application.\n- `getPortletURL`: returns the URL used to render a portlet based on the servlet\n  request attributes.\n- `include`: Returns `true` if the application successfully renders.\n- `setGroupProvider`: sets the group provider associated with the application.\n- `setPortlet`: sets the portlet associated with the application.\n\nYou can reduce the number of methods you must implement if you extend a base\nclass that already implements the `PanelCategory` interface. The recommended way\nto do this is by extending the\n[`BasePanelApp`](@app-ref@/application-list/latest/javadocs/com/liferay/application/list/BasePanelApp.html)\nor\n[`BaseJSPPanelApp`](@app-ref@/application-list/latest/javadocs/com/liferay/application/list/BaseJSPPanelApp.html)\nabstract classes. If you want to use JSPs to render that UI, extend\n`BaseJSPPanelApp`. This provides additional methods you can use to incorporate\nJSP functionality into your app's listing in the Product Menu.\n\n| **Note:** JSPs are not the only way to provide front-end functionality to your\n| Panel Apps. You can create your own class implementing `PanelApp` to use other\n| technologies such as FreeMarker.\n\nThe `BlogsPanelApp` is a simple example of how to specify your portlet as a\nPanel App. This class extends `BasePanelApp`, overriding the `getPortletId` and\n`setPortlet` methods. These methods specify and set the Blogs portlet as a Panel\nApp.\n\nThis is how those methods look for the Blogs portlet:\n\n```java\n@Override\npublic String getPortletId() {\n    return BlogsPortletKeys.BLOGS_ADMIN;\n}\n\n@Override\n@Reference(\n    target = \"(javax.portlet.name=\" + BlogsPortletKeys.BLOGS_ADMIN + \")\",\n    unbind = \"-\"\n)\npublic void setPortlet(Portlet portlet) {\n    super.setPortlet(portlet);\n}\n```\n\nEach Panel App must belong to a portlet and each portlet can have at most one\nPanel App. If more than one Panel App is needed, another portlet must be\ncreated. By default, the Panel App only appears if the user has permission to\nview the associated portlet.\n\nContinue on the learn about creating custom Panel Categories and Panel Apps.\n"
  },
  {
    "path": "en/developer/customization/articles/50-product-navigation/02-customizing-the-product-menu/02-adding-custom-panel-categories.markdown",
    "content": "---\nheader-id: adding-custom-panel-categories\n---\n\n# Adding Custom Panel Categories\n\n[TOC levels=1-4]\n\nAs you navigate the Product Menu, you can see that Panel Apps like *Web Content*\nand *Settings* are organized into Panel Categories such as *Content & Data* and\n*Configuration*. This article explains how to add new Panel Categories to the\nmenu. Adding new Panel Apps is covered in the next section.\n\nThere are three steps to creating a new category:\n\n1.  Create the OSGi structure and metadata.\n\n2.  Implement Liferay's Frameworks.\n\n3.  Define the Panel Category.\n\n## Creating the OSGi Module\n\nFirst you must create the project.\n\n1.  Create an OSGi module using your favorite third party tool, or use\n    [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli). Blade CLI\n    offers a \n    [Panel App](/docs/7-2/reference/-/knowledge_base/r/panel-app-template)\n    template, which is for creating a Panel Category and Panel App.\n\n2.  Create a unique package name in the module's `src` directory and create\n    a new Java class in that package. To follow naming conventions, give your\n    class a unique name followed by `PanelCategory` (e.g.,\n    `ControlPanelCategory`).\n\n## Implementing Liferay's Frameworks\n\nNext, you must connect your OSGi module to Liferay's frameworks and use those to\ndefine information about your entry. This takes only two steps: \n\n1.  Insert the `@Component` annotation declaring the panel category keys\n    directly above the class's declaration:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"panel.category.key=\" + [Panel Category Key],\n            \"panel.category.order:Integer=[int]\"\n        },\n        service = PanelCategory.class\n    )\n    ```\n\n    You can view an example of a similar `@Component` annotation for the\n    `UserPanelCategory` class below:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"panel.category.key=\" + PanelCategoryKeys.ROOT,\n            \"panel.category.order:Integer=200\"\n        },\n        service = PanelCategory.class\n    )\n    ```\n\n    The `property` element designates two properties that should be assigned for\n    your category. The `panel.category.key` specifies the parent category for\n    your custom category. You can find popular parent categories to assign in\n    the [`PanelCategoryKeys`](@app-ref@/application-list/latest/javadocs/com/liferay/application/list/PanelCategoryKeys.html)\n    class. For instance, if you wanted to create a child category in the Control\n    Panel, you could assign `PanelCategoryKeys.CONTROL_PANEL`. Likewise, if you\n    wanted to create a root category, like the Control Panel or Site\n    Administration, you could assign `PanelCategoryKeys.ROOT`.\n    \n    The `panel.category.order:Integer` property specifies the order in which\n    your category is displayed. The higher the number (integer), the lower your\n    category is listed among other sibling categories assigned to a parent.\n\n    | **Note:** To insert a Panel Category between existing categories in the\n    | default menu, you must know the `panel.category.order:Integer` property\n    | for the existing categories. For example, the Product Menu's two main\n    | sections---Control Panel and Site Administration---have\n    | `panel.category.order:Integer` properties of 100 and 200, respectively. A\n    | new panel inserted between Control Panel and Site Administration would\n    | need a `panel.category.key` of ROOT and a `panel.category.order:Integer`\n    | of 150.\n\n    Finally, your `service` element should specify the `PanelCategory.class`\n    service.\n\n2.  Implement the `PanelCategory` interface. See the\n    [`PanelCategory` Interface](/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu#panelcategory-interface)\n    section for more details. Extending one of the provided base classes\n    ([BasePanelCategory](/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu#basepanelcategory)\n    or\n    [BaseJSPPanelCategory](/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu#basejsppanelcategory))\n    is a popular way to implement the `PanelCategory` interface.\n\n3.  If you elect to leverage JSPs, you must also specify the servlet context\n    from where you are loading the JSP files. If this is inside an OSGi module,\n    make sure your `bnd.bnd` file has defined a web context path:\n\n    ```\n    Bundle-SymbolicName: com.sample.my.module.web\n    Web-ContextPath: /my-module-web\n    ```\n\n    Then reference the Servlet context using the symbolic name of your module\n    like this:\n\n    ```java\n    @Override\n    @Reference(\n        target = \"(osgi.web.symbolicname=com.sample.my.module.web)\",\n        unbind = \"-\"\n    )\n    public void setServletContext(ServletContext servletContext) {\n        super.setServletContext(servletContext);\n    }\n    ```\n\nExcellent! You've successfully created a custom Panel Category to display in the\nProduct Menu. In many cases, a Panel Category holds Panel Apps for users to\naccess. You'll learn how to add a Panel App to a Panel Category next.\n"
  },
  {
    "path": "en/developer/customization/articles/50-product-navigation/02-customizing-the-product-menu/03-adding-custom-panel-apps.markdown",
    "content": "---\nheader-id: adding-custom-panel-apps\n---\n\n# Adding Custom Panel Apps\n\n[TOC levels=1-4]\n\nAfter you have created a Panel Category, create a Panel App to go in it:\n\n1.  Create an OSGi module using your favorite third party tool, or use\n    [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli). Blade CLI\n    offers a\n    [Panel App](/docs/7-2/reference/-/knowledge_base/r/panel-app-template)\n    template to help generate a basic Panel Category and Panel App.\n\n2.  Create a unique package name in the module's `src` directory, and create a\n    new Java class in that package. To follow naming conventions, give your class\n    a unique name followed by *PanelApp* (e.g., `JournalPanelApp`).\n\n3.  Directly above the class's declaration, insert the following annotation:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"panel.app.order:Integer=INTEGER\"\n            \"panel.category.key=\" + PANEL_CATEGORY_KEY,\n        },\n        service = PanelApp.class\n    )\n    ```\n\n    You can view an example of a similar `@Component` annotation for the\n    `JournalPanelApp` class below.\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"panel.app.order:Integer=100\",\n            \"panel.category.key=\" + PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT\n        },\n        service = PanelApp.class\n    )\n    ```\n\n    These properties and attributes are similar to those discussed in\n    the previous\n    [article](/docs/7-2/customization/-/knowledge_base/c/adding-custom-panel-categories).\n    The `panel.category.key` assigns your Panel App to a Panel Category. The\n    `panel.app.order:Integer` property specifies the order your Panel App\n    appears among other Panel Apps in the same category. For example, if you\n    want to add a Panel App to Site Administration &rarr; *Content & Data*, add\n    the following property:\n\n    ```java\n    \"panel.category.key=\" + PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT\n    ```\n\n    Visit the\n    [PanelCategoryKeys](@app-ref@/application-list/latest/javadocs/com/liferay/application/list/constants/PanelCategoryKeys.html)\n    class for keys you can use to specify default Panel Categories in Liferay.\n\n    Set the `service` attribute to `PanelApp.class`. \n\n4.  Implement the `PanelApp` interface. See the\n    [`PanelApp` Interface](/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu#panelapp-interface)\n    section for more details. Extending one of the provided base classes\n    ([BasePanelApp](@app-ref@/application-list/latest/javadocs/com/liferay/application/list/BasePanelApp.html)\n    or\n    [BaseJSPPanelApp](@app-ref@/application-list/latest/javadocs/com/liferay/application/list/BaseJSPPanelApp.html))\n    is a popular way to implement the `PanelApp` interface. See the\n    [PanelApp Interface](/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu#panelapp-interface)\n    section for more information.\n\n5.  If you elect to leverage JSPs, you must also specify the servlet context\n    from where you are loading the JSP files. If this is inside an OSGi module,\n    make sure your `bnd.bnd` file has defined a web context path:\n\n    ```\n    Bundle-SymbolicName: com.sample.my.module.web\n    Web-ContextPath: /my-module-web\n    ```\n\n    Then reference the Servlet context using the symbolic name of your module\n    like this:\n\n    ```java\n    @Override\n    @Reference(\n        target = \"(osgi.web.symbolicname=com.sample.my.module.web)\",\n        unbind = \"-\"\n    )\n    public void setServletContext(ServletContext servletContext) {\n        super.setServletContext(servletContext);\n    }\n    ```\n\nNow you know how to add or modify a Panel App in the Product Menu. Not only does\nLiferay provide a simple solution to add new Panel Categories and Panel Apps, it\nalso gives you the flexibility to add a more complex UI to the Product Menu\nusing any technology.\n"
  },
  {
    "path": "en/developer/customization/articles/50-product-navigation/03-customizing-the-control-menu/01-customizing-the-control-menu-intro.markdown",
    "content": "---\nheader-id: customizing-the-control-menu\n---\n\n# Customizing the Control Menu\n\n[TOC levels=1-4]\n\nLiferay's Control Menu consists of three main sections: Sites (left portion),\nTools (middle portion), and User (right portion).\n\n![Figure 1: This image shows where your entry will reside depending on the category you select.](../../../images/control-menu-areas.png)\n\n| **Note:** You can add the Control Menu to a theme by adding the following\n| snippet into your `portal_normal.ftl`:\n| \n| ```\n| <@liferay.control_menu />\n| ```\n| \n| The other product navigation menus (e.g., Product Menu, Simulation Menu) are\n| included in this tag, so specifying the above snippet embeds all three menus\n| into your theme. Embedding the User Personal Menu is slightly different. Visit\n| the\n| [Customizing the User Personal Bar and Menu](/docs/7-2/customization/-/knowledge_base/c/customizing-the-user-personal-bar-and-menu)\n| article for more information.\n\nYou can reference a sample Control Menu Entry by visiting the\n[Control Menu Entry](/docs/7-2/reference/-/knowledge_base/r/control-menu-entry-template)\narticle.\n\n## ProductNavigationControlMenuEntry Interface\n\nTo create a control menu entry, you must implement the\n[`ProductNavigationControlMenuEntry`](@app-ref@/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/ProductNavigationControlMenuEntry.html)\ninterface. It's recommended to implement this interface by extending the\n[`BaseProductNavigationControlMenuEntry`](@app-ref@/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/BaseProductNavigationControlMenuEntry.html)\nor\n[`BaseJSPProductNavigationControlMenuEntry`](@app-ref@/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/BaseJSPProductNavigationControlMenuEntry.html)\nabstract classes. \n\n\n These base classes are\ncovered in more\ndetail next.\n\n### BaseProductNavigationControlMenuEntry\n\nTypically, the `BaseProductNavigationControlMenuEntry` is extended for basic\nentries that only display a link with text or a simple icon. If you want to\nprovide a more complex UI with buttons or a sub-menu, you can override the\n`include()` and `includeBody()` methods.\n\nThe\n[`IndexingProductNavigationControlMenuEntry`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-web/src/main/java/com/liferay/portal/search/web/internal/product/navigation/control/menu/IndexingProductNavigationControlMenuEntry.java)\nis a simple example for providing text and an icon. It extends the\n`BaseProductNavigationControlMenuEntry` class and is used when Liferay is\nindexing. The indexing entry is displayed in the *Tools* (middle) area of the\nControl Menu with a *Refresh* icon and text stating *The Portal is currently\nindexing*.\n\n### BaseJSPProductNavigationControlMenuEntry\n\nIf you use JSPs for generating the UI, you can extend\n`BaseJSPProductNavigationControlMenuEntry` to save time when creating/modifying\na control menu entry.\n\nThe\n[`ProductMenuProductNavigationControlMenuEntry`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/product-navigation/product-navigation-product-menu-web/src/main/java/com/liferay/product/navigation/product/menu/web/internal/product/navigation/control/menu/ProductMenuProductNavigationControlMenuEntry.java)\ncreates an entry that appears in the *Sites* (left) area of the Control Menu.\nThis class extends the `BaseJSPProductNavigationControlMenuEntry` class. This\nprovides several more methods that use JSPs to define your entry's UI. There are\ntwo methods to notice:\n\n```java\n@Override\npublic String getBodyJspPath() {\nreturn \"/portlet/control_menu/product_menu_control_menu_entry_body.jsp\";\n}\n\n@Override\npublic String getIconJspPath() {\nreturn \"/portlet/control_menu/product_menu_control_menu_entry_icon.jsp\";\n}\n```\n\nThe `getIconJspPath()` method provides the Product Menu icon\n(![Menu Closed](../../../images/icon-menu.png) &rarr; ![Menu Open](../../../images/icon-menu-open.png)),\nand the `getBodyJspPath()` method adds the UI body for the entry outside of the\nControl Menu. The latter method must be used when providing a UI outside the\nControl Menu. You can test this by opening and closing the Product Menu on the\nhome page.\n\nFinally, if you provide functionality that is exclusively inside the Control\nMenu, the `StagingProductNavigationControlMenuEntry` class calls its JSP like\nthis:\n\n```java\n@Override\npublic String getIconJspPath() {\nreturn \"/control_menu/entry.jsp\";\n}\n```\n\nThe `entry.jsp` is returned, which embeds the Staging Bar portlet into the\nControl Menu.\n\nNext, you'll step through the process of customizing the Control Menu.\n"
  },
  {
    "path": "en/developer/customization/articles/50-product-navigation/03-customizing-the-control-menu/02-customizing-the-control-menu.markdown",
    "content": "---\nheader-id: creating-control-menu-entries\n---\n\n# Creating Control Menu Entries\n\n[TOC levels=1-4]\n\nNow you'll create entries to customize the Control Menu. Make sure to read\n[Adding Custom Panel Categories](/docs/7-2/customization/-/knowledge_base/c/adding-custom-panel-categories)\nbefore beginning this article. This article assumes you know how to create a \nPanel Category. Creating a Control Menu Entry follows the same pattern as\ncreating a Panel Category:\n\n1.  Create the OSGi structure and metadata.\n\n2.  Implement Liferay's Frameworks.\n\n3.  Define the Control Menu Entry. \n\n## Creating the OSGi Module\n\nFirst you must create the project. \n\n1.  Create a generic OSGi module. Your module must contain a Java class, \n    `bnd.bnd` file, and build file (e.g., `build.gradle` or `pom.xml`). You'll \n    create your Java class next if your project does not already define one.\n\n2.  Create a unique package name in the module's `src` directory and create a\n    new Java class in that package. Give your class a unique name followed by \n    *ProductNavigationControlMenuEntry* \n    (e.g.,`StagingProductNavigationControlMenuEntry`).\n\n## Implementing Liferay's Frameworks\n\nNext, you need to connect your OSGi module to Liferay's frameworks and use those\nto define information about your entry.\n\n1.  Directly above the class's declaration, insert this code:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"product.navigation.control.menu.category.key=\" + [Control Menu Category],\n            \"product.navigation.control.menu.category.order:Integer=[int]\"\n        },\n        service = ProductNavigationControlMenuEntry.class\n    )\n    ```\n\n    The `product.navigation.control.menu.category.key` property specifies your\n    entry's category. The default Control Menu provides three categories: Sites\n    (left portion), Tools (middle portion), and User (right portion).\n\n    To specify the category, reference the appropriate key in the\n    [ProductNavigationControlMenuCategoryKeys](@app-ref@/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/constants/ProductNavigationControlMenuCategoryKeys.html)\n    class. For example, this property places your entry in the middle portion of\n    the Control Menu:\n\n    ```java\n    \"product.navigation.control.menu.category.key=\" + ProductNavigationControlMenuCategoryKeys.TOOLS\n    ```\n\n    Like Panel Categories, you must specify an integer to place your entry in\n    the category. Entries are ordered from left to right: an entry with order\n    `1` appears to the left of an entry with order `2`. If the order is\n    not specified, it's chosen at random based on which service was registered\n    first in the OSGi container.\n\n    Finally, your `service` element should specify the\n    `ProductNavigationControlMenuEntry.class` service.\n\n2.  Implement the [`ProductNavigationControlMenuEntry`](@app-ref@/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/ProductNavigationControlMenuEntry.html)\n    interface. You can also extend the\n    [`BaseProductNavigationControlMenuEntry`](@app-ref@/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/BaseProductNavigationControlMenuEntry.html)\n    or\n    [`BaseJSPProductNavigationControlMenuEntry`](@app-ref@/product-navigation/latest/javadocs/com/liferay/product/navigation/control/menu/BaseJSPProductNavigationControlMenuEntry.html)\n    abstract classes. See the\n    [Customizing the Control Menu](/docs/7-2/customization/-/knowledge_base/c/customizing-the-control-menu)\n    article for more information on these classes.\n\n3.  If you elect to leverage JSPs, you must specify the servlet context for the\n    JSP files. If this is inside an OSGi module, make sure your `bnd.bnd` file\n    defines a web context path:\n\n    ```\n    Bundle-SymbolicName: com.sample.my.module.web\n    Web-ContextPath: /my-module-web\n    ```\n\n    And then reference the Servlet context using the symbolic name of your\n    module:\n\n    ```java\n    @Override\n    @Reference(\n        target = \"(osgi.web.symbolicname=com.sample.my.module.web)\",\n        unbind = \"-\"\n    )\n    public void setServletContext(ServletContext servletContext) {\n        super.setServletContext(servletContext);\n    }\n    ```\n\n4.  Part of creating the entry is defining when it appears. The Control Menu\n    shows different entries depending on the displayed page. You can specify\n    when your entry appears with the `isShow(HttpServletRequest)` method.\n\n    For example, the `IndexingProductNavigationControlMenuEntry` class queries\n    the number of indexing jobs when calling `isShow`. If the query count is\n    `0`, the indexing entry doesn't appear in the Control Menu:\n\n    ```java\n    @Override\n    public boolean isShow(HttpServletRequest request) throws PortalException {\n        int count = _indexWriterHelper.getReindexTaskCount(\n            CompanyConstants.SYSTEM, false);\n\n        if (count == 0) {\n            return false;\n        }\n\n        return super.isShow(request);\n    }\n    ```\n\n    The `StagingProductNavigationControlMenuEntry` class selects the pages to\n    appear. The staging entry never appears if the page is an administration\n    page (e.g., *Site Administration*, *Control Panel*, etc.):\n\n    ```java\n    @Override\n    public boolean isShow(HttpServletRequest request) throws PortalException {\n        ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(\n            WebKeys.THEME_DISPLAY);\n\n        Layout layout = themeDisplay.getLayout();\n\n        // This controls if the page is an Administration Page\n\n        if (layout.isTypeControlPanel()) {\n            return false;\n        }\n\n        // This controls if Staging is enabled\n\n      if (!themeDisplay.isShowStagingIcon()) {\n            return false;\n        }\n\n        return true;\n    }\n    ```\n\nExcellent! You've created your entry in one of the three default sections in the\nControl Menu.\n"
  },
  {
    "path": "en/developer/customization/articles/50-product-navigation/03-customizing-the-control-menu/03-defining-icons-tooltips.markdown",
    "content": "---\nheader-id: defining-icons-and-tooltips\n---\n\n# Defining Icons and Tooltips\n\n[TOC levels=1-4]\n\nWhen creating a Control Menu entry, you can use an icon in addition to or in\nplace of text. You can also use tooltips to provide a more in depth\nexplanation.\n\n## Control Menu Entry Icons\n\nYou can provide a Lexicon or CSS icon in your `*ControlMenuEntry`. To use a \nLexicon icon, you should override the methods in \n`ProductMenuProductNavigationControlMenuEntry` like this one:\n\n```java\npublic String getIconCssClass(HttpServletRequest request) {\n    return \"\";\n}\n\npublic String getIcon(HttpServletRequest request) {\n    return \"lexicon-icon\";\n}\n\npublic String getMarkupView(HttpServletRequest request) {\n    return \"lexicon\";\n}\n```\n\nLikewise, you can use a CSS icon by overriding the\n`ProductMenuProductNavigationControlMenuEntry` methods like this one:\n\n```java\npublic String getIconCssClass(HttpServletRequest request) {\n    return \"icon-css\";\n}\n\npublic String getIcon(HttpServletRequest request) {\n    return \"\";\n}\n\npublic String getMarkupView(HttpServletRequest request) {\n    return \"\";\n}\n```\n\nYou can find these icons documented\n[here](https://clayui.com/docs/components/icons.html).\n\n### Control Menu Entry Tooltips\n\nTo provide a tooltip for the Control Menu entry, create a `getLabel` method like\nthis:\n\n```java\n@Override\npublic String getLabel(Locale locale) {\n    ResourceBundle resourceBundle = ResourceBundleUtil.getBundle(\n        \"content.Language\", locale, getClass());\n\n    return LanguageUtil.get(\n        resourceBundle, \"the-portal-is-currently-reindexing\");\n}\n```\n\nYou need to create a `Language.properties` to store your labels. You can learn \nmore about resource bundles in the \n[Localization](/docs/7-2/frameworks/-/knowledge_base/f/localization) articles.\n"
  },
  {
    "path": "en/developer/customization/articles/50-product-navigation/04-extending-the-simulation-menu/01-extending-the-simulation-menu-intro.markdown",
    "content": "---\nheader-id: extending-the-simulation-menu\n---\n\n# Extending the Simulation Menu\n\n[TOC levels=1-4]\n\nTo provide your own functionality in the Simulation Menu, you must create a\nPanel App in `SimulationPanelCategory`. If you want to add extensive\nfunctionality, you can even create additional Panel Categories in the menu to\ndivide up your Panel Apps. This article covers the simpler case of creating a\nPanel App for the already present hidden category.\n\nBefore beginning, make sure you're accustomed to using Panel Categories and\nPanel Apps. This is covered in detail in the\n[Customizing the Product Menu](/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu)\narticles. Once you know how to create Panel Categories and Panel Apps, continue\nwith this article.\n\n1.  Follow the steps documented in \n    [Adding Custom Panel Apps](/docs/7-2/customization/-/knowledge_base/c/adding-custom-panel-apps)\n    for creating custom Panel Apps. Once you've created the foundation \n    of your Panel App, move on to learn how to tweak it so it customizes the\n    Simulation Menu.\n\n    You can generate a Simulation Panel App by using Blade CLI's\n    [Simulation Panel Entry template](/docs/7-2/reference/-/knowledge_base/r/simulation-panel-entry-template).\n    You can also refer to the\n    [Simulation Panel App sample](/docs/7-2/reference/-/knowledge_base/r/simulation-panel-app)\n    for a working example.\n\n2.  Since this article assumes you're providing more functionality to the\n    existing simulation category, set the simulation category in the\n    `panel.category.key` of the `@Component` annotation:\n\n    ```java\n    \"panel.category.key=\" + SimulationPanelCategory.SIMULATION\n    ```\n\n    To use this constant, you must add a dependency on \n    [`com.liferay.product.navigation.simulation`](https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.product.navigation.simulation/).\n\n    Be sure to also specify the order to display your new Panel App,\n    which was explained in [Adding Custom Panel Apps](/docs/7-2/customization/-/knowledge_base/c/adding-custom-panel-apps).\n\n3.  This article assumes you're using JSPs. \n    Therefore, you should extend the [`BaseJSPPanelApp`](@app-ref@/application-list/latest/javadocs/com/liferay/application/list/BaseJSPPanelApp.html)\n    abstract class, which implements the [`PanelApp`](@app-ref@/application-list/latest/javadocs/com/liferay/application/list/PanelApp.html)\n    interface and also provides additional methods necessary for specifying JSPs\n    to render your Panel App's UI. Remember that you can also implement your own\n    `include()` method to use any front-end technology you want, if you want to\n    use a technology other than JSP (e.g., FreeMarker).\n\n4.  Define your simulation view. For instance, in `DevicePreviewPanelApp`, the\n    `getJspPath` method points to the `simulation-device.jsp` file in the\n    `resources/META-INF/resources` folder, where the device simulation interface\n    is defined. Optionally, you can also add your own language keys, CSS, or\n    JavaScript resources in your simulation module.\n\n    The right servlet context is also provided by implementing this method:\n\n    ```java\n    @Override\n    @Reference(\n        target = \"(osgi.web.symbolicname=com.liferay.product.navigation.simulation.device)\",\n        unbind = \"-\"\n    )\n    public void setServletContext(ServletContext servletContext) {\n        super.setServletContext(servletContext);\n    }\n    ```\n\n    As explained in [Customizing The Product Menu](/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu),\n    a Panel App should be associated with a portlet. This makes the Panel App \n    visible only when the user has permission to view the portlet.\n    This Panel App is associated to the Simulation Device portlet using these\n    methods:\n\n    ```java\n    @Override\n    public String getPortletId() {\n        return ProductNavigationSimulationPortletKeys.\n            PRODUCT_NAVIGATION_SIMULATION;\n    }\n\n    @Override\n    @Reference(\n        target = \"(javax.portlet.name=\" + ProductNavigationSimulationPortletKeys.PRODUCT_NAVIGATION_SIMULATION + \")\",\n        unbind = \"-\"\n    )\n    public void setPortlet(Portlet portlet) {\n        super.setPortlet(portlet);\n    }\n    ```\n\n    Segments also provides a good example of how to extend the Simulation Menu.\n    When segments are available, the Simulation Menu is extended to offer\n    personalization options. You can simulate particular experiences directly\n    from the Simulation Menu. Its Panel App class is similar to\n    `DevicePreviewPanelApp`, except it points to a different portlet and JSP.\n    For more information on Segments, see the\n    [Segmentation and Personalization](/docs/7-2/user/-/knowledge_base/u/segmentation-and-personalization)\n    section.\n\n    ![Figure 1: The Simulation Menu also displays Segments to help simulate different user experiences.](../../../images/segments-preview.png)\n\n5.  You can combine your simulation options with the device simulation options \n    by interacting with the device preview iFrame. To retrieve the device \n    preview frame in an `aui:script` block of your custom simulation view's \n    JavaScript, you can use this code:\n\n    ```js\n    var iframe = A.one('#simulationDeviceIframe');\n    ```\n\n    Then you can modify the device preview frame URL like this:\n\n    ```js\n    iframe.setAttribute('src', newUrlWithCustomParameters);\n    ```\n\nNow that you know how to extend the necessary Panel Categories and Panel Apps to\nmodify the Simulation Menu,\n[create a module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project) \nof your own and customize the Simulation Menu so it's most helpful for your \nneeds.\n"
  },
  {
    "path": "en/developer/customization/articles/50-product-navigation/05-providing-the-user-personal-bar/01-customizing-the-user-personal-menu-intro.markdown",
    "content": "---\nheader-id: customizing-the-user-personal-bar-and-menu\n---\n\n# Customizing the User Personal Bar and Menu\n\n[TOC levels=1-4]\n\nThe User Personal Bar is a portlet, but it's also an important concept in\n@product@. In a fresh bundle using the default theme, it's the section of screen\noccupied by the User's avatar and the Personal Menu.\n\n![Figure 1: By default, the User Personal Bar contains the signed-in user's avatar, which opens the Personal Menu when selected.](../../../images/user-personal-bar.png)\n\nThe User Personal Bar holds only the Personal Menu by default, but it can also\ncontain any functionality you want (even additional portlets). The User Personal\nBar is included by default in every Liferay theme, but you can replace it with a \n[portlet](/docs/7-2/customization/-/knowledge_base/c/using-a-custom-portlet-in-place-of-the-user-personal-bar)\nor customize it by adding entries to the existing portlet's menu.\n\nThis section covers these topics:\n\n- Replacing the default User Personal Bar portlet with a custom portlet.\n- Customizing the default User Personal Bar.\n\n## Displaying the Personal Menu\n\nStarting with @product-ver@, the Personal Menu is no longer part of the\nProduct Menu, but is instead included in the User Personal Bar. To display the\nexisting User Personal Bar in your own theme, embed the portlet into your theme\nby adding the following snippet into `portal_normal.ftl`:\n\n```markup\n<@liferay.user_personal_bar />\n```\n\nYou'll use the same snippet even if you're replacing the default User Personal\nBar portlet with your own.\n\nIf you use a custom portlet to provide the User Personal Bar, but wish to\ninclude the default Personal Menu, make sure to render it by using this tag in\nyour portlet's JSP:\n\n```markup\n<liferay-product-navigation:personal-menu\n    expanded=\"<%= true %>\"\n    label=\"<%= userAvatar %>\"\n/>\n```\n\n| **Note:** The recommended way to display the Personal Menu is by embedding the\n| User Personal Bar in a theme. If this is not practical, a workaround exists: go\n| to *Control Panel* &rarr; *Configuration* &rarr; *Instance Settings* &rarr;\n| *Users* and select *Personal Menu*. Enable the *Show in Control Menu* toggle\n| and click *Update*.\n| \n| This places a button to expand the Personal Menu in the Control Menu. It\n| appears on every site and page in your virtual instance, including sites that\n| have the User Personal Bar embedded in the theme. So, to avoid multiple User \n| Personal Bars appearing on the page, you should use only *one* of these \n| mechanisms to display the User Personal Bar.\n\nUnlike the Product Menu, the Personal Menu can be customized without creating\npanel categories and panel apps. See \n[Customizing the Personal Menu](/docs/7-2/customization/-/knowledge_base/c/customizing-the-personal-menu)\nfor details.\n\n"
  },
  {
    "path": "en/developer/customization/articles/50-product-navigation/05-providing-the-user-personal-bar/02-using-a-custom-portlet-as-the-user-personal-bar.markdown",
    "content": "---\nheader-id: using-a-custom-portlet-in-place-of-the-user-personal-bar\n---\n\n# Using a Custom Portlet in Place of the User Personal Bar\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to write the single Java class required to\nreplace the default User Personal Bar with a custom portlet. Writing the\nportlet itself is up to each developer's needs. See the documentation on\n[portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets)\nif you need guidance.\n\n1.  [Create an OSGi module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project).\n\n2.  Create a unique package name in the module's `src` directory and create a\n    new Java class in that package.\n\n3.  Above the class declaration, insert the following annotation:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"model.class.name=\" + PortalUserPersonalBarApplicationType.UserPersonalBar.CLASS_NAME,\n            \"service.ranking:Integer=10\"\n        },\n        service = ViewPortletProvider.class\n    )\n    ```\n\n     The `model.class.name` property must be set to the class name of the entity\n     type you want the portlet to handle. In this case, you want your portlet to\n     be provided based on whether it can be displayed in the User Personal Bar.\n\n     You may recall from the \n     [Portlet Providers](/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes)\n     articles that you can request portlets in several different ways (e.g.,\n     *Edit*, *Browse*, etc.).\n\n     You should also specify the service rank for your new portlet so it\n     overrides the default. Make sure to set the `service.ranking:Integer`\n     property to a number that is ranked higher than the portlet being used by\n     default.\n\n     Since you want to display your portlet instead of the User Personal Bar,\n     the `service` element should be `ViewPortletProvider.class`.\n\n4.  Update the class's declaration to extend the \n    [`BasePortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BasePortletProvider.html)\n    abstract class and implement `ViewPortletProvider`:\n\n    ```java\n    public class ExampleViewPortletProvider extends BasePortletProvider implements ViewPortletProvider {\n\n    }\n    ```\n\n5.  Specify the portlet you want in the User Personal Bar by declaring the\n    following method in your class:\n\n    ```java\n    @Override\n    public String getPortletName() {\n        return PORTLET_NAME;\n    }\n    ```\n\n    Replace the `PORTLET_NAME` text with the portlet to provide when one is\n    requested by the theme template. For example, the default portlet uses\n    `com_liferay_product_navigation_user_personal_bar_web_portlet_ProductNavigationPersonalBarPortlet`\n\nIf you want to inspect the entire module used for Liferay's User Personal Bar,\nsee the\n[product-navigation-user-personal-bar-web](https://github.com/liferay/liferay-portal/tree/7.2.0-ga1/modules/apps/product-navigation/product-navigation-user-personal-bar-web) module.\n\n## Related Topics\n\n- [Customizing the Product Menu](/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu)\n- [Customizing the Control Menu](/docs/7-2/customization/-/knowledge_base/c/customizing-the-control-menu)\n"
  },
  {
    "path": "en/developer/customization/articles/50-product-navigation/05-providing-the-user-personal-bar/03-customizing-the-personal-menu.markdown",
    "content": "---\nheader-id: customizing-the-personal-menu\n---\n\n# Customizing the Personal Menu\n\n[TOC levels=1-4]\n\nThe Personal Menu is a portlet in @product@, and is the only item occupying the\nUser Personal Bar out of the box. You can add entries to the Personal Menu by\nimplementing the `PersonalMenuEntry`\n[interface](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/product-navigation/product-navigation-personal-menu-api/src/main/java/com/liferay/product/navigation/personal/menu/PersonalMenuEntry.java). \nIf you're adding a portlet entry to the Personal Menu, the process is\nslightly different. Both approaches are covered below.\n\n## Adding an Entry to the Personal Menu\n\nFollow these steps. `SignOutPersonalMenuEntry.java` is used as an example \nthroughout these steps:\n\n1.  [Create an OSGi module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project)\n    and place a new Java class into a package in its `src` folder.\n\n2.  In the `@Component` annotation, specify the two properties shown below to \n    place your new entry in the Personal Menu:\n\n    - `product.navigation.personal.menu.group`: determines the section where the \n      entry will be placed.\n\n    - `product.navigation.personal.menu.entry.order`: determines the order of \n      entries within each section. Note that sections are not labelled. To \n      create a new section, assign the `group` property a value other than those \n      for the four default sections (100, 200, 300, and 400).\n\n    ![Figure 1: The Personal Menu is organized into four sections.](../../../images/user-personal-menu-sections.png)\n\n    Here's an example:\n\n    ```java\n    @Component(\n    \timmediate = true,\n    \tproperty = {\n        \"product.navigation.personal.menu.group:Integer=400\",\n    \t\t\"product.navigation.personal.menu.entry.order:Integer=100\"\n    \t},\n    \tservice = PersonalMenuEntry.class\n    )\n    public class SignOutPersonalMenuEntry implements PersonalMenuEntry {\n    ```\n\n3.  Include the interface's methods. `SignoutPersonalMenuEntry` uses `getLabel`\n    and `getPortletURL`, which are the only two that are mandatory. `getLabel`\n    retrieves a language key to label the entry in the UI:\n\n    ```java\n    \t@Override\n    \tpublic String getLabel(Locale locale) {\n    \t\treturn LanguageUtil.get(locale, \"sign-out\");\n    \t}\n    ```\n\n    `getPortletURL` returns the URL for the portlet or page you want to access \n    with the entry:\n\n    ```java\n    \tpublic String getPortletURL(HttpServletRequest httpServletRequest)\n    \t\tthrows PortalException {\n\n    \t\tThemeDisplay themeDisplay =\n    \t\t\t(ThemeDisplay)httpServletRequest.getAttribute(\n    \t\t\t\tWebKeys.THEME_DISPLAY);\n\n    \t\treturn themeDisplay.getURLSignOut();\n    \t}\n\n    }\n    ```\n\nThat's all you need to implement the interface. However, the `PersonalMenuEntry` \ninterface includes a number of other methods that you can use if you need them:\n\n`getIcon`: identify an icon to display in the entry.\n\n`isActive`: indicate whether the entry is currently active.\n\n`isShow`: write logic to determine under what circumstances the entry is displayed.\n\nLearn how to add a portlet entry to the Personal Menu next. \n\n## Adding a Portlet Entry to the Personal Menu\n\nIf you're adding a portlet to the Personal Menu, you can extend the\n`BasePersonalMenuEntry` class to save time. Follow these steps:\n\n1.  [Create an OSGi module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project)\n    and place a new Java class into a package in its `src` folder.\n\n2.  In the `@Component` annotation, specify the two properties shown below to \n    place your new entry in the Personal Menu:\n\n    - `product.navigation.personal.menu.group`: determines the section where the \n      entry will be placed.\n\n    - `product.navigation.personal.menu.entry.order`: determines the order of \n      entries within each section. Note that sections are not labelled. To \n      create a new section, assign the `group` property a value other than those \n      for the four default sections (100, 200, 300, and 400).\n\n    An example is shown below:\n\n    ```java\n    @Component(\n    \timmediate = true,\n    \tproperty = {\n    \t\t\"product.navigation.personal.menu.entry.order:Integer=100\",\n    \t\t\"product.navigation.personal.menu.group:Integer=300\"\n    \t},\n    \tservice = PersonalMenuEntry.class\n    )\n    public class MyAccountPersonalMenuEntry extends BasePersonalMenuEntry {\n    ```\n\n3.  Override the `getPortletId()` method to provide the portlet's ID, as shown \n    in the example below:\n    \n    ```java\n    public class MyAccountPersonalMenuEntry extends BasePersonalMenuEntry {\n\n    \t@Override\n    \tpublic String getPortletId() {\n    \t\treturn UsersAdminPortletKeys.MY_ACCOUNT;\n    \t}\n\n    }\n    ```\n\n    The `BasePersonalMenuEntry` class automatically determines the label, \n    portlet URL, state, and visibility based on the portlet ID. \n\nOnce you've completed your implementation and deployed your module, your new\nentry is displayed in the personal menu. \n\n## Related Topics\n\n- [Customizing the Product Menu](/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu)\n- [Customizing the Control Menu](/docs/7-2/customization/-/knowledge_base/c/customizing-the-control-menu)\n"
  },
  {
    "path": "en/developer/customization/articles/99-customization-with-ext/01-intro.markdown",
    "content": "---\nheader-id: customization-with-ext\n---\n\n# Customization with Ext\n\n[TOC levels=1-4]\n\n**Customization with Ext projects is deprecated and should only be used if\nabsolutely necessary.**\n\nThere are two types of Ext projects deployable to @product@:\n\n- *Ext plugins*: used to customize @product@'s core functionality. For example,\n  overwriting a class in a core artifact like `com.liferay.portal.kernel`.\n- *Ext modules*: used to customize OSGi modules. For example, overwriting a JSP\n  in the `com.liferay.login.web` module (see the\n  [Login Web Ext sample](/docs/7-2/reference/-/knowledge_base/r/login-web-ext)).\n\nExt projects are powerful tools used to extend @product@'s default core and/or\nmodule projects. They, however, increase the complexity of your @product@\ninstance because they are a fork of the default project. Ext projects are not\nrecommended unless there is absolutely no other way to accomplish your task.\n\n@product-ver@ provides many extension points that let you customize almost every\ndetail of @product@. If there's a way to customize what you want with an\nextension point, do it that way instead. See the\n[More Extensible, Easier to Maintain](/docs/7-1/tutorials/-/knowledge_base/t/benefits-of-liferay-7-for-liferay-6-developers#more-extensible-easier-to-maintain)\nsection for more details on the advantages of using @product@'s extension\npoints.\n\nBefore deciding to use an Ext project, weigh the cost. Ext projects let you use\ninternal APIs and even let you overwrite @product@ core files. This puts\nyour deployment at risk of being incompatible with security, performance, or\nfeature updates released by Liferay. When upgrading to a new version of\n@product@ (even if it's a maintenance version or a service pack), you have to\nreview all changes and manually modify your Ext projects to merge your changes\nwith @product@'s.\n\nBefore diving into Ext projects, first consider if an Ext project is even\nnecessary at all.\n\n## Making the Decision to Use Ext Projects\n\nThere are many parts of @product@ that now provide an extension point via OSGi\nbundle. You should follow this three step process to decide whether an Ext\nproject is necessary:\n\n1.  Find the OSGi extension point that you need. \n\n    <!--Uncomment once article is available\n    You can follow the\n    Finding Extension Points\n    tutorial as a guide.\n    -->\n\n2.  If an OSGi extension point does not exist, use an Ext project.\n\n3.  Research new extension points after every release of @product@. When a new\n    version of @product@ provides the extension point you need, always use the\n    extension point to replace the existing Ext project.\n\nSo how do you find an OSGi extension point?\n\nYour first step is to examine the custom projects that extend popular @product@\nextension points stored in the\n[Liferay Blade Samples](https://github.com/liferay/liferay-blade-samples)\nrepository. For\nmore information on these sample projects, see \n[Sample Projects](/docs/7-2/reference/-/knowledge_base/r/sample-projects). \nUsable extension points are also documented throughout Liferay's Developer \nNetwork categorized by the @product@ section involved. For example, \n[Overriding MVC Commands](/docs/7-2/customization/-/knowledge_base/c/overriding-liferay-mvc-commands)\ndescribes how to extend a @product@ extension point. Want to learn how to \n[customize JSPs](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps)?\nThose processes are documented too!\n\nYou're now equipped to make an informed decision on using Ext projects.\n\n## Licensing and Contributing\n\n@product@ is Open Source software licensed under the \n[LGPL 2.1 license](http://www.gnu.org/licenses/lgpl-2.1.html).\nIf you reuse any code snippet and redistribute it, whether publicly or to a\nspecific customer, make sure your modifications are compliant with the license.\nOne common way is to make the source code of your modifications are available to\nthe community under the same license. Make sure you read the license text\nyourself to find the option that best fits your needs. \n\nIf your goal in making changes is fixing a bug or improving @product@, it could\nbe of interest to a broader audience. Consider contributing it back to the\nproject. That benefits all users of the product including you, since you won't\nhave to maintain the changes with each newly released version of @product@. You\ncan notify Liferay of bugs or improvements at\n[issues.liferay.com](http://issues.liferay.com). Check out Liferay's\n[Participation](/participate/feedback/overview) information to learn all the\nways that you can contribute to Liferay projects. \n\nIn summary, an Ext project is a powerful way to extend @product@. There are no\nlimits to what you can customize, so use it carefully. Before using an Ext\nproject, see if you can implement all or part of the desired functionality\nthrough\n[Widget Templates](/docs/7-2/user/-/knowledge_base/u/styling-apps-and-assets)\nor extension points, without introducing the complexity that's inherent with Ext\nprojects. If you need to use an Ext project, make your customization as small as\npossible and follow the instructions in this section carefully to avoid issues.\n"
  },
  {
    "path": "en/developer/customization/articles/99-customization-with-ext/02-customizing-core-functionality-with-ext/01-intro.markdown",
    "content": "---\nheader-id: customizing-core-functionality-with-ext\n---\n\n# Customizing Core Functionality with Ext\n\n[TOC levels=1-4]\n\n| **Ext plugins are deprecated for @product-ver@ and should only be used if\n| absolutely necessary.**\n| \n| The following app servers should be used for Ext plugin development in\n| @product@:\n| \n| - Tomcat 9.x\n| \n| In most cases, Ext plugins are not necessary. There are, however, certain\n| cases that require the use of an Ext plugin. Liferay only supports the\n| following Ext plugin use cases:\n| \n| - Providing custom implementations for any beans declared in @product@'s\n|   Spring files (when possible, use\n|   [service wrappers](/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers)\n|   instead of an Ext plugin). @product-ver@ removed many beans, so make sure\n|   your overridden beans are still relevant if converting your legacy Ext\n|   plugin.\n| - Overwriting a class in a @product-ver@ core JAR. For a list of core JARs,\n|   see the\n|   [Finding Core @product@ Artifacts](/docs/7-2/customization/-/knowledge_base/c/finding-artifacts)\n|   section.\n| - Modifying @product@'s `web.xml` file.\n| - Adding to @product@'s `web.xml` file.\n| \n| **Note:** In previous versions of Liferay Portal, you needed an Ext plugin to\n| specify classes as portal property values (e.g.,\n| `global.starup.events.my.custom.MyStartupAction`), since the custom class had\n| to be added to the portal class loader. This is no longer the case in\n| @product-ver@ since all lifecycle events can use OSGi services with no need to\n| edit these legacy properties.\n\nExt plugins are used to customize @product@'s core functionality. You can learn\nmore about what the core encompasses in the\n[Finding Core @product@ Artifacts](/docs/7-2/customization/-/knowledge_base/c/finding-artifacts)\narticle section. In this section, you'll learn how to\n\n- [Create an Ext plugin](/docs/7-2/customization/-/knowledge_base/c/creating-an-ext-plugin)\n- [Develop an Ext plugin](/docs/7-2/customization/-/knowledge_base/c/developing-an-ext-plugin)\n- [Deploy an Ext plugin](/docs/7-2/customization/-/knowledge_base/c/deploying-an-ext-plugin)\n- [Redeploy an Ext plugin](/docs/7-2/customization/-/knowledge_base/c/redeploying-an-ext-plugin)\n\nYou can also dive into the\n[Anatomy of an Ext Plugin](/docs/7-2/customization/-/knowledge_base/c/anatomy-of-an-ext-plugin)\nto familiarize yourself with its structure.\n\nYou'll start by creating an Ext plugin.\n"
  },
  {
    "path": "en/developer/customization/articles/99-customization-with-ext/02-customizing-core-functionality-with-ext/02-creating-an-ext-plugin.markdown",
    "content": "---\nheader-id: creating-an-ext-plugin\n---\n\n# Creating an Ext Plugin\n\n[TOC levels=1-4]\n\nAn Ext plugin is a powerful tool for extending @product@. Because it increases\t\t\nthe complexity of your @product@ installation, you should only use an Ext plugin\t\t\nif you're sure you can't accomplish your goal in a different way. You can\t\t\ncreate Ext plugins using the pre-configured `war-core-ext` project\ntemplate/archetype. See the\n[`war-core-ext`](/docs/7-2/reference/-/knowledge_base/r/war-core-ext)\nproject template article for information on how to create an Ext plugin, its\nfolder structure, and other important details.\n\nIt's recommended to create and develop your Ext plugin in a\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace).\nWorkspace is preconfigured with an `ext` folder, which applies important\nsettings (via the `LiferayExtPlugin`) to your Ext plugin when it's deployed to\n@product@. You'll learn more about this in the\n[Set Up the Build Environment](/docs/7-2/customization/-/knowledge_base/c/developing-an-ext-plugin#set-up-the-build-environment)\nsection.\n\nNext you'll learn the anatomy of an Ext plugin.\n"
  },
  {
    "path": "en/developer/customization/articles/99-customization-with-ext/02-customizing-core-functionality-with-ext/03-anatomy-of-an-ext-plugin.markdown",
    "content": "---\nheader-id: anatomy-of-an-ext-plugin\n---\n\n# Anatomy of an Ext Plugin\n\n[TOC levels=1-4]\n\nThere are two ways you can structure your Ext plugin. The\n[`war-core-ext`](/docs/7-2/reference/-/knowledge_base/r/war-core-ext)\nproject template/archetype creates the default layout:\n\n- `[project name]-ext/`\n    - `src/`\n        - `extImpl/`\n            - `resources/`\n        - `extKernel/`\n            - `resources/`\n        - `extUtilBridges/`\n            - `resources/`\n        - `extUtilJava/`\n            - `resources/`\n        - `extUtilTaglib/`\n            - `resources/`\n        - `main/`\n            - `webapp/`\n                - `WEB-INF/`\n                    - `ext-web/`\n                        - `docroot/`\n\nYou can also follow the legacy layout that was used for Ext plugins created with\nthe Plugins SDK in past versions:\n\n- `[project name]-ext/`\n    - `docroot/`\n        - `WEB-INF/`\n            - `ext-impl/`\n                - `src/`\n            - `ext-kernel/`\n                - `src/`\n            - `ext-util-bridges/`\n                - `src/`\n            - `ext-util-java/`\n                - `src/`\n            - `ext-util-taglib/`\n                - `src/`\n            - `ext-web/`\n                - `docroot/`\n                    - `WEB-INF/`\n\nAlthough the folder names are slightly different, they work the same. This\narticle will refer to the default structure and naming. Here are detailed\nexplanations of the subfolders: \n\n- `extImpl`: Contains the custom implementation classes and classes that\n  override core classes from `portal-impl.jar`. \n\n- `extKernel`: Contains classes that should be available to other plugins. In\n  advanced scenarios, this folder can be used to hold classes that overwrite\n  classes from `portal-kernel.jar`. \n\n- `extUtilBridges`, `extUtilJava` and `extUtilTaglib`: These folders are needed\n  only in scenarios where you must customize these Liferay libraries:\n  `util-bridges.jar`, `util-java.jar` and `util-taglib.jar`, respectively. If\n  you're not customizing any of these libraries, you can ignore these folders. \n\n- `main/webapp/WEB-INF/ext-web/docroot`: Contains the web application's\n  configuration files, including `WEB-INF/struts-config-ext.xml`, which lets you\n  customize Liferay's core struts classes. Note that for @product-ver@, there\n  are very few entities left to override in the `struts-config.xml` file. Any\n  JSPs that you're customizing also belong here. \n\nBy default, several files are also added to the plugin. Here are the most \nsignificant files: \n\n- `build.gradle`/`pom.xml`: The build file for the Ext plugin project. \n\n- [`src/main/webapp/WEB-INF/liferay-plugin-package.properties`](@platform-ref@/7.1-latest/propertiesdoc/liferay-plugin-package_7_1_0.properties.html):\n  Contains plugin-specific properties, including the plugin's display name,\n  version, author, and license type. \n\n- `src/main/webapp/WEB-INF/ext-web/docroot/WEB-INF` files: \n\n    - `liferay-portlet-ext.xml`: This file is similar to `portlet-ext.xml`, but\n     is for additional definition elements specific to Liferay. The\n     `liferay-portlet.xml` file contains very few definition elements in\n     @product-ver@ because portlets were modularized and moved out of core. To\n     override the remaining definition elements, copy the complete definition of\n     the desired portlet from `liferay-portlet.xml` in Liferay's source code,\n     then apply the necessary changes.\n\n    - `portlet-ext.xml`: Used to overwrite the definition of a build-in portlet.\n     The `portlet.xml` file contains very few portlet configurations in\n     @product-ver@ because portlets were modularized and moved out of core. To\n     override this file, copy the complete definition of the desired portlet\n     from `portlet-custom.xml` in Liferay's source code, then apply the\n     necessary changes.\n\n    - `struts-config-ext.xml` and `tiles-defs-ext.xml`: These files are used to\n     customize the struts actions used by core portlets. Since most portlets\n     were modularized and moved out of core in @product-ver@, the\n     `struts-config.xml` and `tiles-defs.xml` files are sparsely used.\n\n    - `web.xml`: Used to overwrite web application configurations and servlet\n     information for @product-ver@.\n\n| **Note:** After creating an Ext plugin, remove the files from\n| `docroot/WEB-INF/ext-web/docroot/WEB-INF` that you don't need to customize.\n| Removing files you're not customizing prevents incompatibilities that could\n| manifest from @product@ updates.\n\nGreat! Now you're familiar with an Ext plugin's folder structure and its most\nsignificant files.\n"
  },
  {
    "path": "en/developer/customization/articles/99-customization-with-ext/02-customizing-core-functionality-with-ext/04-developing-an-ext-plugin.markdown",
    "content": "---\nheader-id: developing-an-ext-plugin\n---\n\n# Developing an Ext Plugin\n\n[TOC levels=1-4]\n\nAn Ext plugin changes @product@ itself when the plugin is deployed; it's not a\nseparate component that you can easily remove at any time. For this reason, the\nExt plugin development process is different from other project types. It's\nimportant to remember that once an Ext plugin is deployed, some of its files are\ncopied *inside* the Liferay installation; the only way to remove the changes is\nby *redeploying* an unmodified @product@ application. You're also responsible\nfor checking that patches and fix packs do not conflict with your Ext plugin.\nAdditionally, Ext plugins aren't hot deployable. To deploy an\t Ext plugin, you\nmust restart your server. Additional steps are also required to\tdeploy or\nredeploy to production systems.\n\nIt's strongly recommended to only develop/deploy one Ext plugin. This means that\nall your customizations should live inside one Ext plugin. Liferay Workspace\ndoes not check for conflicts among multiple Ext plugins stored in the `/ext`\nfolder, so do **not** develop/deploy multiple Ext plugins at once.\n\nYou can deploy and redeploy your Ext plugin during your development phase.\nRedeployment involves manually removing the Ext plugin and *cleaning* your\napplication server (i.e., remove it ); this is recommended so any changes made\nto the Ext plugin during development are properly applied, and files removed\nfrom your plugin by previous changes aren't left behind in the @product@\napplication. Because of this added complexity, you should use another plugin\ntype to accomplish your goals whenever possible. \n\nNow it's time to set up the build environment. \n\n## Set Up the Build Environment\n\nIf you're leveraging\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace),\nyou should create/develop your Ext module project in the `/ext` folder\n(default); you can specify a different Ext folder name in workspace's\n`gradle.properties` by adding\n\n```properties\nliferay.workspace.ext.dir=EXT_DIR\n```\n\nIf you are developing an Ext module project in standalone mode (not associated\nwith Liferay Workspace), you must declare the Ext Gradle plugin in your\n`build.gradle`:\n\n```gradle\napply plugin: 'com.liferay.ext.plugin'\n```\n\nNext you'll explore an Ext plugin's advanced configuration files.\n\n## Using Advanced Configuration Files\n\n@product@ uses several internal configuration files for its own architecture; in\naddition, there are configuration files for the libraries and frameworks\n@product@ depends on (e.g., Spring). Configuration could be accomplished using\nfewer files with more properties in each, but maintenance and use is made easier\nby splitting up the configuration properties into several files. For advanced\ncustomization needs, it may be useful to override the configuration specified in\nmultiple configuration files. @product@ provides a clean way to do this from an\nExt plugin without modifying the original files. \n\nAll the configuration files in @product@ are listed below by their path in an\nExt plugin folder. Here are descriptions of what each file is for and the path\nto the original file in @product@: \n\n- `extImpl/resources/META-INF/ext-model-hints.xml`\n    - **Description:** Overrides the default properties of the fields of\n      the data models used by @product@'s core portlets. These properties\n      determine how the form fields for each model are rendered. \n    - **Original file in @product@:**\n      `portal-impl/src/META-INF/portal-model-hints.xml` \n- `extImpl/resources/META-INF/ext-spring.xml`\n    - **Description:** Overrides the Spring configuration used by\n      @product@ and any of its core portlets. It's most commonly used to\n      configure specific data sources or swap the implementation of a default\n      service with a custom one.\n    - **Original file in @product@:** `portal-impl/src/META-INF/*-spring.xml`\n- `extImpl/resources/META-INF/portal-log4j-ext.xml`\n    - **Description:** Allows overriding the Log4j configuration. It can be used\n      to configure appenders for log file location, naming, and rotation. See\n      the\n      [Log4j XML Configuration Primer](https://wiki.apache.org/logging-log4j/Log4jXmlFormat). \n      [Increasing or decreasing the log level of a class or class hierarchy](/docs/7-2/appdev/-/knowledge_base/a/adjusting-module-logging)\n      is best done outside of an Ext plugin, in @product@'s' UI or a Log4j XML\n      file in a module or the `osgi/log4j` folder. \n    - **Original file in Liferay:** `portal-impl/src/META-INF/portal-log4j.xml`\n- `ext-web/docroot/WEB-INF/portlet-ext.xml`\n    - **Description:** Overrides the core portlets' declarations. It's most commonly\n      used to change the init parameters or the roles specified. \n    - **Original file in @product@:** `portal-web/docroot/WEB-INF/portlet-custom.xml`\n- `ext-web/docroot/WEB-INF/liferay-portlet-ext.xml`\n    - **Description:** Overrides the Liferay-specific core portlets'\n      declarations. It core portlets included in @product@. Refer to the\n      [liferay-portlet-app_7_0_0.dtd](@platform-ref@/7.2-latest/definitions/liferay-portlet-app_7_2_0.dtd.html)\n      file for details on all the available options. Use this file with care;\n      the code of the portlets may be assuming some of these options to be set\n      to certain values. \n    - **Original file in @product@:** `portal-web/docroot/WEB-INF/liferay-portlet.xml`\n- `ext-web/docroot/WEB-INF/liferay-display.xml`\n    - **Description:** Overrides the portlets that are shown in the interface\n      for adding applications and the categories in which they're organized.\n    - **Original file in @product@:** `portal-web/docroot/WEB-INF/liferay-display.xml`\n\nNext you'll deploy your Ext plugin. \n"
  },
  {
    "path": "en/developer/customization/articles/99-customization-with-ext/02-customizing-core-functionality-with-ext/05-deploying-an-ext-plugin.markdown",
    "content": "---\nheader-id: deploying-an-ext-plugin\n---\n\n# Deploying an Ext Plugin\n\n[TOC levels=1-4]\n\nDeploying an Ext plugin is similar to deploying any other @product@ project. For\nexample, you'll step through deploying an Ext plugin in Liferay Workspace.\n\n1.  From your Ext plugin root folder, run `blade deploy` (Gradle or Maven) or\n    `mvn verify` (Maven only).\n\n    The `deploy` target creates a `.war` file with your changes and then deploys\n    it to your server. A `BUILD SUCCESSFUL` message indicates your plugin is now\n    being deployed. Your console window running @product@ shows a message like\n    this:\n\n    ```bash\n    Extension environment for sample-ext has been applied. You must\n    reboot the server and redeploy all other plugins.\n    ```\n\n2.  Restart the server so that it detects and publishes your Ext plugin. Your\n    server must be restarted after the initial deployment and every\n    redeployment.\n\n    If any changes applied through the Ext plugin affect the deployment process\n    itself, you must redeploy all other plugins. Even if the Ext plugin doesn't\n    affect the deployment process, it's a best practice to redeploy all your\n    other plugins following initial deployment of the Ext plugin. \n\n    When the server starts, it detects the `.war` file, inspects it, and copies\n    its contents to the appropriate destinations inside @product@.\n\n3.  Once your server restarts, open @product@ to see the customizations you\n    made with your Ext plugin.\n\nFrequently, you'll want to add further customizations to your original Ext\nplugin. You can make unlimited customizations to an Ext plugin that has already\nbeen deployed, but the redeployment process is different from other project\ntypes. You'll learn more on redeploying your Ext plugin next.\n"
  },
  {
    "path": "en/developer/customization/articles/99-customization-with-ext/02-customizing-core-functionality-with-ext/06-redeploying-an-ext-plugin.markdown",
    "content": "---\nheader-id: redeploying-an-ext-plugin\n---\n\n# Redeploying an Ext Plugin\n\n[TOC levels=1-4]\n\nAfter editing an Ext plugin, you must follow a slightly different process to\nredeploy your Ext plugin. This section assumes you're redeploying an Ext plugin\nin Liferay Workspace.\n\n1.  Stop the @product@ server. \n\n2.  Delete your Ext plugin bundle, which resides in your app server's `webapps`\n    folder.\n\n3.  (Optional) If you removed part(s) of your plugin, if there are changes to\n    your plugin that can affect plugin deployment, or if you want to start with\n    a clean @product@ environment, you must **also** clean your application\n    server. You can clean the application server by deleting your Liferay Home\n    and regenerating the bundle. This is done in Liferay Workspace by calling\n    `blade server init`.\n\n4.  From your Ext plugin root folder, run `blade deploy` (Gradle or Maven) or\n    `mvn verify` (Maven only).\n\n    The `deploy` target creates a `.war` file with your changes and then deploys\n    it to your server. A `BUILD SUCCESSFUL` message indicates your plugin is now\n    being deployed. Your console window running @product@ shows a message\n    like this:\n\n    ```bash\n    Extension environment for sample-ext has been applied. You must\n    reboot the server and redeploy all other plugins.\n    ```\n\n5.  After your plugin is published to @product@, verify that your customizations\n    are available.\n\nYou're all set to redeploy Ext plugins!\n"
  },
  {
    "path": "en/developer/customization/articles/99-customization-with-ext/03-customizing-osgi-modules-with-ext/01-intro.markdown",
    "content": "---\nheader-id: customizing-osgi-modules-with-ext\n---\n\n# Customizing OSGi Modules with Ext\n\n[TOC levels=1-4]\n\nAn Ext module is a powerful tool for extending @product@'s OSGi modules. For\nexample, if you want to overwrite a default module's JSP to display a different\nview, you can create an Ext module to customize the original module's JSP (for\nexample, see the\n[Login Web Ext sample](/docs/7-2/reference/-/knowledge_base/r/login-web-ext)).\nBecause this increases the complexity of your @product@ installation, you should\nonly use an Ext module if you're sure you can't accomplish your goal in a\ndifferent way (e.g., leveraging an extension point).\n\nThe following app servers should be used for Ext module development in\n@product@:\n\n- Tomcat 9.x\n\nIn this section, you'll learn how to\n\n- [Create an Ext module](/docs/7-2/customization/-/knowledge_base/c/creating-an-ext-module)\n- [Develop an Ext module](/docs/7-2/customization/-/knowledge_base/c/developing-an-ext-module)\n- [Deploy an Ext module](/docs/7-2/customization/-/knowledge_base/c/deploying-an-ext-module)\n\nYou'll start by creating an Ext module.\n"
  },
  {
    "path": "en/developer/customization/articles/99-customization-with-ext/03-customizing-osgi-modules-with-ext/02-creating-an-ext-module.markdown",
    "content": "---\nheader-id: creating-an-ext-module\n---\n\n# Creating an Ext Module\n\n[TOC levels=1-4]\n\nYou can create an Ext module based off the pre-configured `modules-ext` project\ntemplate/archetype. See the\n[`modules-ext`](/docs/7-2/reference/-/knowledge_base/r/modules-ext-template)\nproject template article for information on how to create an Ext module, its\nfolder structure, and other important details.\n\nIt's recommended to create and develop your Ext module in a\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace).\nWorkspace is preconfigured with an `ext` folder, which applies important\nsettings (via the `LiferayOSGiExtPlugin`) to your Ext module when it's deployed\nto @product@. You'll learn more about this in the next section.\n\nYou can also create an Ext module leveraging the `modules-ext` project template\nin Dev Studio or IntelliJ. \n\n## Dev Studio\n\nTo create an Ext module in Dev Studio,\n\n1.  Right click your Liferay Workspace and select *New* &rarr; *Liferay Module\n    Ext Project*.\n\n2.  Define the project name and the original module name (i.e., BSN form, like\n    `com.liferay.login.web`) you intend to override.\n\n    ![Figure 1: You must define your Ext module's name and the module you intend to override.](../../../images/ext-module-dev-studio.png)\n\n    Note, you must have the `target.platform.index.sources` property enabled in\n    your workspace's `gradle.properties` file to browse for the original module\n    name.\n\n3.  Click *Finish* to create the Module Ext project.\n\nAwesome! You've created an Ext module project in Dev Studio!\n\n## IntelliJ\n\nTo create an Ext module in IntelliJ,\n\n1.  Go to *File* &rarr; *New* &rarr; *Liferay Module*.\n\n2.  Select the *Liferay Modules Ext* option from the left menu.\n\n3.  Define the original module name (i.e., BSN form, like\n    `com.liferay.login.web`) you intend to override. Then click *Next*.\n\n    ![Figure 2: You must define the module you intend to override.](../../../images/ext-module-intellij.png)\n\n    Note, you must have the `target.platform.index.sources` property enabled in\n    your workspace's `gradle.properties` file to browse for the original module\n    name.\n\n4.  Define the module name.\n\n5.  Click *Finish*.\n\nGreat! You know how to create an Ext module and are familiar with its folder\nstructure and most significant files. Next, you'll learn how to develop your Ext\nmodule to customize @product@. \n"
  },
  {
    "path": "en/developer/customization/articles/99-customization-with-ext/03-customizing-osgi-modules-with-ext/03-developing-an-ext-module.markdown",
    "content": "---\nheader-id: developing-an-ext-module\n---\n\n# Developing an Ext Module\n\n[TOC levels=1-4]\n\nYou can create your own Ext module project by\n\n- Declaring the original module name and version.\n- Providing the source code that will replace the original.\n\nTo declare the original module in the `build.gradle` file properly (only\nsupports Gradle), you must specify the original module's Bundle Symbolic Name\nand the original module's exact version. For example, overriding the\n`com.liferay.login.web` module would be configured like this:\n\n```gradle\noriginalModule group: \"com.liferay\", name: \"com.liferay.login.web\", version: \"3.0.4\"\n```\n\nIf you're leveraging\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace),\nyou should put your Ext module project in the `/ext` folder (default); you can\nspecify a different Ext folder name in workspace's `gradle.properties` by adding\n\n```properties\nliferay.workspace.ext.dir=EXT_DIR\n```\n\nIf you are developing an Ext module project in standalone mode (not associated\nwith Liferay Workspace), you must declare the Ext Gradle plugin in your\n`build.gradle`:\n\n```gradle\napply plugin: 'com.liferay.osgi.ext.plugin'\n```\n\nThen you must provide your own code intended to replace the original one. **Be\nsure to mimic the original module's folder structure when overriding its JAR.**\n\nThe following file types can be overlaid with an Ext module:\n\n- CSS\n- Java\n- JavaScript\n- Language files (`Language.properties`)\n- Scss\n- Soy\n- etc.\n\nThe\n[Ext Gradle Plugin](https://github.com/liferay/liferay-portal/blob/master/modules/sdk/gradle-plugins/src/main/java/com/liferay/gradle/plugins/LiferayOSGiExtPlugin.java)\nhelps compile your code into the JAR. For example, `.scss` files are compiled\ninto `.css` files, which are included in your module's JAR file artifact. This\nis done by the `buildCSS` task.\n\nOnce you're finished developing your Ext module, you must deploy it. Continue on\nto learn how.\n"
  },
  {
    "path": "en/developer/customization/articles/99-customization-with-ext/03-customizing-osgi-modules-with-ext/04-deploying-an-ext-module.markdown",
    "content": "---\nheader-id: deploying-an-ext-module\n---\n\n# Deploying an Ext Module\n\n[TOC levels=1-4]\n\nBefore deploying your Ext module, you must stop the original bundle you intend\nto override. This is because an Ext module's generated JAR includes the original\nbundle source plus your modified source files.\n\n| **Note:** Stopping the original bundle before deploying your Ext module is only\n| necessary if you've already started @product@.\n\nFollow the instructions below to deploy your Ext module to a @product@ instance:\n\n1.  Connect to your portal instance using\n    [Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell).\n\n2.  Search for the bundle ID of the original bundle to override. To find the\n    `com.liferay.login.web` bundle, execute this command:\n\n    ```bash\n    lb -s | grep com.liferay.login.web\n    ```\n\n    This returns output similar to this:\n\n    ```bash\n    423|Active   |   10|com.liferay.login.web (3.0.4)\n    ```\n\n    Make note of the ID (e.g., `423`).\n\n3.  Stop the bundle:\n\n    ```bash\n    stop 423\n    ```\n\nOnce the original bundle is stopped, deploy the Ext module. Note that you cannot\nleverage Blade or Gradle's `deploy` command to do this. The `deploy` command\ndeploys the module to the `osgi\\marketplace\\override` folder by default, which\ndoes not configure Ext modules properly for usage. You should build and copy the\nExt module's JAR to the `deploy` folder manually.\n\nYou're all set to deploy Ext modules!\n"
  },
  {
    "path": "en/developer/customization/articles-dxp/50-workflow/01-workflow-intro.markdown",
    "content": "---\nheader-id: customizing-workflow\n---\n\n# Customizing Workflow\n\n[TOC levels=1-4]\n\nLiferay's workflow engine calls users to participate in a review process\ndesigned for them. Out of the box, workflow makes it possible to define simple\nto complex business processes/workflows, deploy them, and manage them through\na portal interface.\n\nWorkflow is flexible, in that you can design workflow processes in XML to suit\nyour business needs.\n\nIn @product@ version 7.2, a new set of workflow features was introduced around\nthe concept of\n[Workflow Metrics](/docs/7-2/customization/-/knowledge_base/c/creating-sla-calendars).\n\nThe embedded calendar that ships out of the box can be replaced by your own\ncustom calendar service. More customization points will likely be added in the\nfuture.\n"
  },
  {
    "path": "en/developer/customization/articles-dxp/50-workflow/02-sla-calendars.markdown",
    "content": "---\nheader-id: creating-sla-calendars\n---\n\n# Creating SLA Calendars\n\n[TOC levels=1-4]\n\nBy default, an internal calendar assumes the\n[SLA deadline clock](/docs/7-2/customization/-/knowledge_base/c/creating-sla-calendars)\nshould continue counting all the time: in other words, 24 hours per day, seven\ndays per week. If you need a different calendar format, provide your own\nimplementation of the `WorkflowMetricsSLACalendar` interface. New\nimplementations of this service are picked up automatically by the Workflow\nMetrics application, so they become available as soon as the module holding the\nservice implementation is deployed. The interface has three methods to\nimplement:\n\n```java\npublic interface WorkflowMetricsSLACalendar {\n\n\tpublic Duration getDuration(\n\t\tLocalDateTime startLocalDateTime, LocalDateTime endLocalDateTime);\n\n\tpublic LocalDateTime getOverdueLocalDateTime(\n\t\tLocalDateTime nowLocalDateTime, Duration remainingDuration);\n\n\tpublic String getTitle(Locale locale);\n\n}\n```\n\nIf you define a new calendar, a new option becomes available in the Add SLA\nform, allowing you to choose from the default 24/7 calendar or any custom ones\nyou've provided. For example, you can make the timer run for 8 hours per day,\nfrom 9-17 by a 24-hour clock, for 5 days per week. If you need to, you can even\nstop the calendar from counting during lunch hours!\n\n![Figure 1: Write a Custom SLA Calendar if the default, 24/7 calendar isn't sufficient.](../../images/workflow-custom-sla-calendar.png)\n\n## Dependencies\n\nAlong with some artifacts you're probably used to depending on (like\n`com.liferay.portal.kernel`), you'll need the\n`com.liferay.portal.workflow.metrics.sla.api-[version].jar` artifact. For @product@\nversion 7.2.10-GA1, here's an example Gradle build dependency declaration:\n\n```groovy\ncompileOnly group: \"com.liferay\", name: \"com.liferay.portal.workflow.metrics.sla.api\", version: \"1.1.0\"\ncompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"4.4.0\"\ncompileOnly group: \"javax.servlet\", name: \"javax.servlet-api\", version: \"3.0.1\"\ncompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\", version: \"1.3.0\"\n```\n\n## Implementation Steps\n\nImplement a\n`com.liferay.portal.workflow.metrics.sla.calendar.WorkflowMetricsSLACalendar` to\ndefine your own SLA calendar logic. When you're finished, use the created\ncalendar when creating the \n[SLA definition](/docs/7-2/customization/-/knowledge_base/c/creating-sla-calendars).\n\n1.  Declare the component and the class:\n\n    ```java\n    import com.liferay.portal.kernel.language.Language;\n    import com.liferay.portal.workflow.metrics.sla.calendar.WorkflowMetricsSLACalendar;\n    import java.time.Duration;\n    import java.time.LocalDateTime;\n    import java.util.Locale;\n\n    import org.osgi.service.component.annotations.Component;\n    import org.osgi.service.component.annotations.Reference;\n\n\n    @Component(property = \"sla.calendar.key=default\")\n    public class DefaultWorkflowMetricsSLACalendar\n        implements WorkflowMetricsSLACalendar {\n    ```\n\n    The component property `sla.calendar.key` is required to identify this calendar.\n\n2.  Override `getDuration` to return the time `Duration` when elapsed SLA time\n    should be computed. The start and end dates that this method receives\n    represent the time a workflow task has been running. For example, given\n    a task that started at _2019-05-13T16:00:00_ and finished at\n    _2019-05-13T18:00:00_, then The 24/7 calendar returns 2 elapsed hours, while\n    a 9-17 weekdays calendar returns 1 hour as the elapsed time.\n\n    ```java\n    @Override\n    public Duration getDuration(\n        LocalDateTime startLocalDateTime, LocalDateTime endLocalDateTime) {\n\n        return Duration.between(startLocalDateTime, endLocalDateTime);\n    }\n    ```\n\n3.  `getOverdueLocalDateTime` must return the date (as a `LocalDateTime`) when\n    this SLA is considered overdue given the parameter values. For example,\n    given that `nowLocalDateTime`=_2019-05-13T17:00:00_ and\n    `remainingDuration`=_24H_, The 24/7 calendar returns a `localDateTime` of\n    _2019-05-14T17:00:00_ as the overdue date. Given the same parameters, the\n    9-17 weekdays calendar should return _2019-05-17T09:00:00_. The remaining\n    duration of time left in the SLA is available in the method as a `Duration`\n    object; your job is to write logic that considers your calendar and create\n    a `LocalDateTime` with the proper overdue date/time.\n\n    ```java\n    @Override\n    public LocalDateTime getOverdueLocalDateTime(\n        LocalDateTime nowLocalDateTime, Duration remainingDuration) {\n\n        return nowLocalDateTime.plus(remainingDuration);\n    }\n    ```\n\n4.  Use `getTitle` to provide the title for the given locale. Make sure you\n    [properly localize](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application)\n    this extension by providing a `Language.properties` file and any\n    `Language_xx.properties` files for translation of the value. At runtime, the\n    User's locale is used to return the correct translation.\n\n    ```java\n    @Override\n    public String getTitle(Locale locale) {\n        return _language.get(locale, \"default\");\n    }\n\n    @Reference\n    private Language _language;\n    ```\n\nIf the 24/7 default calendar works for you, use it. Otherwise create your own\n`WorkflowMetricsSLACalendar`s.\n"
  },
  {
    "path": "en/developer/customization/articles-dxp/99-customization-with-ext/02-customizing-core-functionality-with-ext/01-intro.markdown",
    "content": "---\nheader-id: customizing-core-functionality-with-ext\n---\n\n# Customizing Core Functionality with Ext\n\n[TOC levels=1-4]\n\n| **Ext plugins are deprecated for @product-ver@ and should only be used if\n| absolutely necessary.**\n| \n| The following app servers should be used for Ext plugin development in\n| @product@:\n| \n| - Tomcat 9.x\n| \n| In most cases, Ext plugins are not necessary. There are, however, certain\n| cases that require the use of an Ext plugin. Liferay only supports the\n| following Ext plugin use cases:\n| \n| - Providing custom implementations for any beans declared in @product@'s\n|   Spring files (when possible, use\n|   [service wrappers](/docs/7-2/customization/-/knowledge_base/c/overriding-service-builder-services-service-wrappers)\n|   instead of an Ext plugin). @product-ver@ removed many beans, so make sure\n|   your overridden beans are still relevant if converting your legacy Ext\n|   plugin\n|   ([how to](/docs/7-2/customization/-/knowledge_base/c/extending-core-classes-using-spring-with-ext-plugins)).\n| - Overwriting a class in a @product-ver@ core JAR. For a list of core JARs,\n|   see the\n|   [Finding Core @product@ Artifacts](/docs/7-2/customization/-/knowledge_base/c/finding-artifacts#finding-core-artifact-attributes)\n|   section\n|   ([how to](/docs/7-2/customization/-/knowledge_base/c/overriding-core-classes-with-ext-plugins)).\n| - Modifying @product@'s `web.xml` file\n|   ([how to](/docs/7-2/customization/-/knowledge_base/c/modifying-the-web-xml-with-ext-plugins)).\n| - Adding to @product@'s `web.xml` file\n|   ([how to](/docs/7-2/customization/-/knowledge_base/c/adding-to-the-web-xml-with-ext-plugins)).\n| \n| **Note:** In previous versions of Liferay Portal, you needed an Ext plugin to\n| specify classes as portal property values (e.g.,\n| `global.starup.events.my.custom.MyStartupAction`), since the custom class had\n| to be added to the portal class loader. This is no longer the case in\n| @product-ver@ since all lifecycle events can use OSGi services with no need to\n| edit these legacy properties.\n\nExt plugins are used to customize @product@'s core functionality. You can learn\nmore about what the core encompasses in the\n[Finding Core @product@ Artifacts](/docs/7-2/customization/-/knowledge_base/c/finding-artifacts#finding-core-artifact-attributes)\narticle section. In this section, you'll learn how to\n\n- [Create an Ext plugin](/docs/7-2/customization/-/knowledge_base/c/creating-an-ext-plugin)\n- [Develop an Ext plugin](/docs/7-2/customization/-/knowledge_base/c/developing-an-ext-plugin)\n- [Deploy an Ext plugin](/docs/7-2/customization/-/knowledge_base/c/deploying-an-ext-plugin)\n- [Redeploy an Ext plugin](/docs/7-2/customization/-/knowledge_base/c/redeploying-an-ext-plugin)\n\nYou can also dive into the\n[Anatomy of an Ext Plugin](/docs/7-2/customization/-/knowledge_base/c/anatomy-of-an-ext-plugin)\nto familiarize yourself with its structure.\n\nYou'll start by creating an Ext plugin.\n"
  },
  {
    "path": "en/developer/customization/articles-dxp/99-customization-with-ext/04-extending-core-classes-using-spring-with-ext-plugins.markdown",
    "content": "---\nheader-id: extending-core-classes-using-spring-with-ext-plugins\n---\n\n# Extending Core Classes Using Spring with Ext Plugins\n\n[TOC levels=1-4]\n\nA supported use case for using Ext plugins in @product@ is extending its core\nclasses (e.g., `portal-impl`, `portal-kernel`, etc.) using Spring. You can\nreference the\n[Finding Core Liferay Portal Artifacts](/docs/7-2/customization/-/knowledge_base/c/finding-artifacts#finding-core-artifact-attributes)\nsection for help distinguishing core classes. Make sure you've reviewed the\ngeneralized\n[Customization with Ext Plugins](/docs/7-2/customization/-/knowledge_base/c/customizing-core-functionality-with-ext)\nsection before creating an Ext plugin.\n\nAs an example, you'll create a sample Ext plugin that extends the\n[PortalImpl](https://docs.liferay.com/ce/portal/7.2-latest/javadocs/portal-impl/com/liferay/portal/util/PortalImpl.html)\ncore class residing in the `portal-impl.jar`. You'll override the\n`PortalImpl.getComputerName()` method via Spring bean, which returns your\nserver's node name. The Ext plugin will override this method and modify the\nserver's returned node name.\n\n1.  Navigate to your Liferay Workspace's root folder and run the following\n    command:\n\n    ```bash\n    blade create -t war-core-ext portal-impl-extend-spring-ext\n    ```\n\n    Your Ext plugin is generated and now resides in the workspace's `/ext`\n    folder with the name you assigned.\n\n2.  Displaying the server node name in your @product@ installation is set to\n    `false` by default. You'll need to enable this property. To do this,\n    navigate into your Liferay bundle's root folder and create a\n    `portal-ext.properties` file. In that file, insert the following property:\n\n    ```properties\n    web.server.display.node=true\n    ```\n\n    Now your server's node name will be displayed once your Liferay bundle is\n    restarted.\n\n3.  In the `/extImpl/java` folder, create the folder structure representing the\n    package name you want your new class to reside in (e.g.,\n    `com/liferay/portal/util`). Then create your new Java class:\n\n    ```java\n    package com.liferay.portal.util;\n\n    public class SamplePortalImpl extends PortalImpl {\n\n        @Override\n        public String getComputerName() {\n            return \"SAMPLE_EXT_INSTALLED_\" + super.getComputerName();\n        }\n    }\n    ```\n\nThe method defined in the extension class overrides the\n`PortalImpl.getComputerName()` method. The `\"SAMPLE_EXT_INSTALLED_\"` String is\nnow prefixed to your server's node name.\n\n4.  In your Ext plugin's `/extImpl/resources` folder, create a\n    `META-INF/ext-spring.xml` file. In this file, insert the following code:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n\n    <beans\n        default-destroy-method=\"destroy\"\n        default-init-method=\"afterPropertiesSet\"\n        xmlns=\"http://www.springframework.org/schema/beans\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\"\n    >\n\n        <bean class=\"com.liferay.portal.util.SamplePortalImpl\" id=\"com.liferay.portal.util.PortalImpl\" />\n    </beans>\n    ```\n\nSince you plan on modifying a core service class, you can inject its extension\nclass via a Spring bean. This will ensure your new class is recognized. Assign\nyour extension class's fully defined class name (e.g.,\n`com.liferay.portal.util.SamplePortalImpl`) to the bean tag's `class` attribute\nand the fully defined original class name (e.g.,\n`com.liferay.portal.util.PortalImpl`) to the bean tag's `id` attribute.\n\nWhen your Ext plugin is deployed, your new service (e.g., `SamplePortalImpl`)\nwill extend the core `PortalImpl` class.\n\nAwesome! You've created an Ext plugin that extends a core class in @product@!\nFollow the instructions in the\n[Deploy the Plugin](/docs/7-2/customization/-/knowledge_base/c/deploying-an-ext-plugin)\narticle to deploy it to your server.\n"
  },
  {
    "path": "en/developer/customization/articles-dxp/99-customization-with-ext/05-overriding-core-classes-with-ext-plugins.markdown",
    "content": "---\nheader-id: overriding-core-classes-with-ext-plugins\n---\n\n# Overriding Core Classes with Ext Plugins\n\n[TOC levels=1-4]\n\nA supported use case for using Ext plugins in @product@ is overriding its core\nclasses (e.g., `portal-impl`, `portal-kernel`, etc.). You can reference the\n[Finding Core Liferay Portal Artifacts](/docs/7-2/customization/-/knowledge_base/c/finding-artifacts#finding-core-artifact-attributes)\nsection for help distinguishing core classes. Make sure you've reviewed the\ngeneralized\n[Customization with Ext Plugins](/docs/7-2/customization/-/knowledge_base/c/customizing-core-functionality-with-ext)\nsection before creating an Ext plugin.\n\nAs an example, you'll create a sample Ext plugin that overwrites the\n[PortalImpl](https://docs.liferay.com/ce/portal/7.2-latest/javadocs/portal-impl/com/liferay/portal/util/PortalImpl.html)\ncore class residing in the `portal-impl.jar`. You'll edit the\n`PortalImpl.getComputerName()` method, which returns your server's node name.\nThe Ext plugin will override the entire `PortalImpl` class, adding the method\nmodifying the server's returned node name.\n\n1.  Navigate to your Liferay Workspace's root folder and run the following\n    command:\n\n    ```bash\n    blade create -t war-core-ext portal-impl-override\n    ```\n\n    Your Ext plugin is generated and now resides in the workspace's `/ext`\n    folder with the name you assigned.\n\n2.  Displaying the server node name in your @product@ installation is set to\n    `false` by default. You'll need to enable this property. To do this,\n    navigate into your Liferay bundle's root folder and create a\n    `portal-ext.properties` file. In that file, insert the following property:\n\n    ```properties\n    web.server.display.node=true\n    ```\n\n    Now your server's node name will be displayed once your Liferay bundle is\n    restarted.\n\n3.  In the `/extImpl/java` folder, create the folder structure matching the\n    class's folder structure you'd like to override (e.g.,\n    `com/liferay/portal/util`). Then create the new Java class that will\n    override the existing core class; your new class must have the same name as\n    the original.\n\n4.  Copy all of the original class's (e.g., `PortalImpl`) logic into your new\n    class. Then modify the method you want to customize. For this example, you\n    want to edit the `getComputerName()` method. Therefore, replace it with the\n    method below:\n\n    ```java\n    @Override\n    public String getComputerName() {\n        return \"sample_portalimpl_ext_installed_successfully_\" + _computerName;\n    }\n    ```\n\n    The method defined in the new class overrides the\n    `PortalImpl.getComputerName()` method. The\n    `sample_portalimpl_ext_installed_successfully_` String is now prefixed to\n    your server's node name.\n\nWhen your Ext plugin is deployed, your new Java class will override the core\n`PortalImpl` class.\n\nAwesome! You've created an Ext plugin that overrides a core class in @product@!\nFollow the instructions in the\n[Deploy the Plugin](/docs/7-2/customization/-/knowledge_base/c/deploying-an-ext-plugin)\narticle to deploy it to your server.\n"
  },
  {
    "path": "en/developer/customization/articles-dxp/99-customization-with-ext/06-adding-to-the-web-xml-with-ext-plugins.markdown",
    "content": "---\nheader-id: adding-to-the-web-xml-with-ext-plugins\n---\n\n# Adding to the web.xml with Ext Plugins\n\n[TOC levels=1-4]\n\nA supported use case for using Ext Plugins in @product@ is adding additional\nfunctionality to its `web.xml` file. Before beginning, make sure you've reviewed\nthe generalized\n[Customization with Ext Plugins](/docs/7-2/customization/-/knowledge_base/c/customizing-core-functionality-with-ext)\nsection.\n\nAs an example, you'll create a sample Ext plugin that adds to your @product@'s\nexisting `web.xml` file (e.g., in the `/tomcat-[version]/webapps/ROOT/WEB-INF`\nfolder). You'll add a new printout in the console during startup.\n\n1.  Navigate to your Liferay Workspace's root folder and run the following\n    command:\n\n    ```bash\n    blade create -t war-core-ext add-printout\n    ```\n\n    Your Ext plugin is generated and now resides in the workspace's `/ext`\n    folder with the name you assigned.\n\n2.  For your @product@ installation to recognize new functionality in the\n    `web.xml`, you must create a class that implements the\n    [ServletContextListener](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/ServletContextListener.html)\n    interface. This class will initialize a servlet context event for which\n    you'll add your new functionality. In the `extImpl/java` folder, create the\n    folder structure representing the package name you want your new class to\n    reside in (e.g., `com/liferay/portal/servlet/context`). Then create your new\n    Java class:\n\n    ```java\n    package com.liferay.portal.servlet.context;\n\n    import javax.servlet.ServletContextEvent;\n    import javax.servlet.ServletContextListener;\n\n    public class ExtAddEntryWebXmlPortalContextLoaderListener\n            implements ServletContextListener {\n\n        public void contextDestroyed(ServletContextEvent servletContextEvent) {\n        }\n\n        public void contextInitialized(ServletContextEvent servletContextEvent) {\n            System.out.println(\"EXT_ADD_ENTRY_WEBXML_INSTALLED_SUCCESSFULLY\");\n        }\n    }\n    ```\n\n    The above class includes two methods that initialize and destroy your\n    servlet context event. Be sure to add the new `web.xml`'s functionality when\n    the portal context is initializing. To add a printout verifying the Ext\n    plugins installation, a simple print statement was defined in the\n    `contextInitialized(...)` method:\n\n    ```java\n    System.out.println(\"EXT_ADD_ENTRY_WEBXML_INSTALLED_SUCCESSFULLY\");\n    ```\n\n3.  Now that you've defined a servlet context event, you should add a listener\n    to your `web.xml` that listens for it. In the `ext-web/docroot/WEB-INF`\n    folder, open the `web.xml` file, which was generated for you by default.\n\n4.  Add the following <listener> tag between the <web-app> tags:\n\n    ```xml\n    <listener>\n        <listener-class>com.liferay.portal.servlet.context.ExtAddEntryWebXmlPortalContextLoaderListener</listener-class>\n    </listener>\n    ```\n\nExcellent! Now when your Ext plugin is deployed, your @product@ installation\nwill create a `ServletContextListener` instance, which will initialize a custom\nservlet context event. This event will be recognized by the `web.xml` file,\nwhich will add the new functionality to your @product@ installation. Follow the\ninstructions in the\n[Deploy the Plugin](/docs/7-2/customization/-/knowledge_base/c/deploying-an-ext-plugin)\narticle for help deploying the Ext plugin to your server.\n"
  },
  {
    "path": "en/developer/customization/articles-dxp/99-customization-with-ext/07-modifying-the-web-xml-with-ext-plugins.markdown",
    "content": "---\nheader-id: modifying-the-web-xml-with-ext-plugins\n---\n\n# Modifying the web.xml with Ext Plugins\n\n[TOC levels=1-4]\n\nA supported use case for using Ext Plugins in @product@ is modifying its\n`web.xml` file. Before beginning, make sure you've reviewed the generalized\n[Customization with Ext Plugins](/docs/7-2/customization/-/knowledge_base/c/customizing-core-functionality-with-ext)\nsection.\n\nAs an example, you'll create a sample Ext plugin that modifies @product@'s\nexisting `web.xml file` (e.g., in the `/tomcat-[version]/webapps/ROOT/WEB-INF`\nfolder). You'll modify the session timeout configuration, which is set to 30\n(minutes) by default:\n\n```xml\n<session-config>\n    <session-timeout>30</session-timeout>\n    <cookie-config>\n        <http-only>true</http-only>\n    </cookie-config>\n</session-config>\n```\n\nThe Ext plugin will update the session timeout to one minute.\n\n1.  Navigate into your Liferay Workspace's `/ext` folder and run the following\n    command:\n\n    ```bash\n    blade create -t war-core-ext modify-session-timeout\n    ```\n\n    Your Ext plugin is generated and now resides in the workspace's `/ext`\n    folder with the name you assigned.\n\n2.  In the `ext-web/docroot/WEB-INF` folder, open the `web.xml` file, which was\n    generated for you by default.\n\n3. Insert the following logic between the `<web-app>` tags:\n\n    ```xml\n    <session-config>\n        <session-timeout>1</session-timeout>\n        <cookie-config>\n            <http-only>true</http-only>\n        </cookie-config>\n    </session-config>\n    ```\n\nNotice that the `<session-timeout>` tag has been updated to `1`.\n\n| **Note:** You can configure an uninterrupted session by setting the\n| `<session-timeout>` tag to `-1`. Leaving a session permanently active is a\n| risk and is not recommended for production environments, but is useful for\n| testing.\n\nThat's it! Now when your Ext plugin is deployed, your @product@ installation\nwill timeout after one minute of inactivity. Follow the instructions in the\n[Deploy the Plugin](/docs/7-2/customization/-/knowledge_base/c/deploying-an-ext-plugin)\narticle for help deploying the Ext plugin to your server.\n"
  },
  {
    "path": "en/developer/customization/build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"Developer Customization articles\" basedir=\".\">\n\t<property name=\"project.dir\" value=\"../../../../liferay-docs\" />\n\n\t<import file=\"../build.xml\" />\n\n\t<property name=\"doc.dir\" value=\"customization\"/>\n\t<property name=\"doc.type\" value=\"article\"/>\n\n</project>\n"
  },
  {
    "path": "en/developer/frameworks/articles/01-frameworks-intro.markdown",
    "content": "---\nheader-id: frameworks\n---\n\n# Frameworks\n\n[TOC levels=1-4]\n\nTo make your applications more fully featured and to develop them faster, you\ncan make use of Liferay's development frameworks. These help you create\ncommonly used features---like search, tagging, and comments---without having to\ndevelop them from scratch. And since these features are tried and tested, you\ncan rest assured knowing they're bug free. \n\nHere are just a few frameworks you'll find here: \n\n[**A fully-fledged permissions system:**](/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions) \nImplement permissions the way they're\nimplemented with the applications that ship with @product@ for a consistent,\nseamless, and robust experience. \n\n[**Assets:**](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework) \nPublish data from your application across the system, making it\navailable to those who need it, and enabling other features like tagging,\ncategorizing, and comments. \n\n[**Search:**](/docs/7-2/frameworks/-/knowledge_base/f/search) \nIf you have a data-driven application, you can add search\ncapabilities by integrating with Liferay's search indexer. \n\n[**A configuration system with auto-generated or custom UI:**](/docs/7-2/frameworks/-/knowledge_base/f/configurable-applications) \nAre you providing user-configurable options in your application? Make use of\nLiferay's configuration system and provide a clean and consistent experience\nfor your users. \n\n[**File management:**](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api) \nWill your applications work with files? Use Liferay's Documents and Media API\nto manage them. \n\n[**Import/Export:**](/docs/7-2/frameworks/-/knowledge_base/f/content-publication-management) \nUse Liferay's import/export system to make your application's\ndata portable or to stage it for publication to production systems. \n\n[**Web Fragments:**](/docs/7-2/frameworks/-/knowledge_base/f/page-fragments) \nProvide your content managers with dynamic chunks of\nfunctionality they can use as building blocks for web pages. \n\n[**Workflow:**](/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework) \nRun the data from your application through an approval process. \n\nThis really just scratches the surface. From \n[pop-up list selectors](/docs/7-2/frameworks/-/knowledge_base/f/item-selector) to a [social\nnetworking API](/docs/7-2/frameworks/-/knowledge_base/f/social-api), \nas a Liferay developer, you have access to tons of frameworks\nthat make your life easier. \n"
  },
  {
    "path": "en/developer/frameworks/articles/application-security/01-intro.markdown",
    "content": "---\nheader-id: application-security\n---\n\n# Application Security\n\n[TOC levels=1-4]\n\nLiferay's development framework provides an application security platform with\nyears of experience behind it. You don't need to roll your own security for your\napplications. Instead, you can specify security for your applications using\nLiferay's framework. \n\nBeyond security for applications, there are many ways to extend the default\nsecurity model by customizing the authentication process. This group of\ntutorials teaches you about them: \n\n- Resources, Roles, and Permissions\n- Custom SSO Providers \n- Authentication Pipelines \n- Service Access Policies \n- Authentication Verifiers \n\nRead on to learn about implementing Liferay's security framework! \n"
  },
  {
    "path": "en/developer/frameworks/articles/application-security/02-application-permissions/01-application-permissions-intro.markdown",
    "content": "---\nheader-id: defining-application-permissions\n---\n\n# Defining Application Permissions\n\n[TOC levels=1-4]\n\nWhen you're writing an application, there are almost always parts of the\napplication or its data that should be protected by permissions. Some users\nshould access all the functions or data, but most users shouldn't. \n\nOn many platforms, developers have to create the security model themselves. On\n@product@, an application security model has been provided for you; you only\nneed to make use of it. \n\nFortunately, no matter what your application does, access to it and to its\ncontent can be controlled with permissions. Read on to learn about Liferay's\npermissions system and how add permissions to your application.\n\nThe permissions system has three parts: *Resources*, *Actions*, and\n*Permissions*. \n\n**Action**: An operation that can be performed by a user. For example, users can\nperform these actions on the Bookmarks application: `ADD_TO_PAGE`,\n`CONFIGURATION`, and `VIEW`. Users can perform these actions on Bookmarks entry\nentities: `ADD_ENTRY`, `DELETE`, `PERMISSIONS`, `UPDATE`, and `VIEW`. \n\n**Resource**: A generic representation of any application or entity on which an\naction can be performed. Resources are used for permission checking. For\nexample, resources could include the RSS application with instance ID `hF5f`,\na globally scoped Wiki page, a Bookmarks entry of the site X, and a Message\nBoards post with the ID `5052`.\n\n**Permission**: A flag that determines whether an action can be performed\non a resource. In the database, resources and actions are saved in pairs. Each\nentry in the `ResourceAction` table contains both the name of a portlet or\nentity and the name of an action. For example, the `VIEW` action with respect to\n*viewing the Bookmarks application* is associated with the\n`com_liferay_bookmarks_web_portlet_BookmarksPortlet` portlet ID. The `VIEW`\nactions with respect to *viewing a Bookmarks Folder* or *viewing a Bookmarks\nentry* are associated with the `com.liferay.bookmarks.model.BookmarksFolder` and\n`com.liferay.bookmarks.model.BookmarksEntry` entities, respectively.\n\nTo do permissions, therefore, you define *Users* (Roles) who have *Permission*\nto perform *Actions* on *Resources*. User definition is done by administrators\nonce your application is deployed; developers define resources, actions, and\ndefault permissions. \n\nYou can implement permissions in your applications in four steps that spell the\nacronym *DRAC*: \n\n1. <b>D</b>efine all resources and their permissions. \n\n2. <b>R</b>egister all defined resources in the permissions system. \n\n3. <b>A</b>ssociate the necessary permissions with resources.\n\n4. <b>C</b>heck permission before returning resources. \n\nThe next four tutorials show these steps in detail. \n\n"
  },
  {
    "path": "en/developer/frameworks/articles/application-security/02-application-permissions/02-defining-permissions.markdown",
    "content": "---\nheader-id: defining-resources-and-permissions\n---\n\n# Defining Resources and Permissions\n\n[TOC levels=1-4]\n\nYour first step in implementing permissions is to define the resources and the\npermissions that protect them. There are two different kinds of resources:\n*portlet resources* and *model resources*. \n\nPortlet resources represent portlets. The names of portlet resources are the\nportlet IDs from the portlets' `@Component` properties or if you're using a WAR\nfile, `portlet.xml` files. Model resources refer to model objects, usually\npersisted as entities to a database. The names of model resources are their\nfully qualified class names. In the XML displayed below, permission\nimplementations are first defined for the *portlet* resource and then for the\n*model* resources.\n\nModel resources represent models, such as blog entries. Resources are named\nusing the fully qualified class names of the entities they represent. \n\n| **Note:** For each resource, there are four scopes to which the permissions can\n| be applied: company, group, group-template, or individual. Because these are\n| called *portlet resources* here and in the code, this can be confusing. The\n| other scopes are mostly used internally for various Liferay constructs (such as\n| Sites or Categories).\n\nYou define resources and their permissions using an XML file. By convention,\nthis file is called `default.xml` and exists in a module's\n`src/main/resources/resource-actions` folder. \n\nBecause of the two different types of resources, you'll have two of these files:\none in your portlet module to define the portlet resources and one in your\nservice module to define the model resources. \n\n## Defining Portlet Resource Permissions\n\nDefine the portlet resources first; here's an example using Liferay's Blogs\napplication. \n\n1.  Start with the DTD declaration: \n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action Mapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_0_0.dtd\">\n    ```\n\n2.  The root tag contains all the resources to be declared: \n\n    ```xml\n    <resource-action-mapping>\n\n    </resource-action-mapping> \n    ```\n\n3.  Inside these tags, define your resources. The Blogs application defines two\n    portlet resources: \n\n    ```xml\n    <portlet-resource>\n        <portlet-name>com_liferay_blogs_web_portlet_BlogsAdminPortlet</portlet-name>\n        <permissions>\n            <supports>\n                <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n                <action-key>CONFIGURATION</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n                <action-key>CONFIGURATION</action-key>\n            </guest-unsupported>\n        </permissions>\n    </portlet-resource>\n    <portlet-resource>\n        <portlet-name>com_liferay_blogs_web_portlet_BlogsPortlet</portlet-name>\n        <permissions>\n            <supports>\n                <action-key>ADD_PORTLET_DISPLAY_TEMPLATE</action-key>\n                <action-key>ADD_TO_PAGE</action-key>\n                <action-key>CONFIGURATION</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>ADD_PORTLET_DISPLAY_TEMPLATE</action-key>\n                <action-key>CONFIGURATION</action-key>\n            </guest-unsupported>\n        </permissions>\n    </portlet-resource>\n    ```\n\nThe Blogs application comprises two portlets: the Blogs portlet itself and\nthe Blogs Admin portlet that appears in the Site menu for administrators.\nDefine your portlets by their names, and then list the permissions for the\nportlet. The Blogs portlet, for example, supports four permissions:\n`ADD_PORTLET_DISPLAY_TEMPLATE`, `ADD_TO_PAGE`, `CONFIGURATION`, and `VIEW`. The Blogs\nAdmin portlet has an additional permission: `ACCESS_IN_CONTROL_PANEL`, which\ndefines who can see the entry in the Site menu. \n\nOnce you've defined permissions at the portlet level, you can set default\npermissions for different types of users. The DTD allows for site member and\nguest defaults. Since guests are users that aren't logged in, there's also\na `guest-unsupported` tag for defining permissions guests can *never* have (in\nother words, the user must be logged in and identifiable). \n\nThat's all there is to it! Your next task is to define permissions for your\nmodel resources. \n\n## Defining Model Resource Permissions\n\nDefining permissions for models is a similar process. Create a `default.xml`\nfile in your service module's `src/main/resources/resource-actions` folder. In\nthis file, you must define top-level function permissions and individual entity\npermissions using the same `<model-resource>` tag. \n\nThis can be confusing, so some explanation is in order. Model permissions for\nwhat Liferay calls the *root model* are defined separately from permissions on\nstored entities, which Liferay calls the *model*. This makes sense when you\nthink about the functions users can perform: \n\n- Creating something new \n- Editing something that exists\n\nCreating something new (like adding a new Blog entry) is different from\naccessing something that exists. A Blog owner should be able to create or edit\na Blog entry, but a User or guest should have read permission for existing\nentries and no permission to create them. \n\nPermission to create something new that doesn't yet exist is a *root model*\npermission, whether that functionality is exposed in a portlet or not.\nPermission on an existing resource is a *model* permission. \n\nNow you're ready to define both your root model and model permissions. \n\n1.  First, create the skeleton for your file: \n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action Mapping 7.1.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_0_0.dtd\">\n\n    <resource-action-mapping>\n\n    </resource-action-mapping> \n    ```\n\n2.  Inside the `<resource-action-mapping>` tags, use a `<model-resource>` tag to\n    define permissions for the root model:\n\n    ```xml\n    <model-resource>\n        <model-name>com.liferay.blogs</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_blogs_web_portlet_BlogsAdminPortlet</portlet-name>\n            <portlet-name>com_liferay_blogs_web_portlet_BlogsPortlet</portlet-name>\n        </portlet-ref>\n        <root>true</root>\n        <weight>1</weight>\n        <permissions>\n            <supports>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>SUBSCRIBE</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>SUBSCRIBE</action-key>\n            </site-member-defaults>\n            <guest-defaults />\n            <guest-unsupported>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>SUBSCRIBE</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n    ```\n\n    The model name (`com.liferay.blogs`) is just a package name. The\n    `<root>true</root>` tag defines this as a root model. The `<weight>` tag\n    defines the order of these permissions in the GUI. The permissions defined\n    are ADD_ENTRY (add a Blog entry), PERMISSIONS (set permissions on Blog\n    entries), and SUBSCRIBE (receive notifications when Blog entries are\n    created). These are all root model permissions, because no primary key in\n    the database can be assigned to any of these functions. \n\n3.  Finally, define your model permissions: \n\n    ```xml\n    <model-resource>\n        <model-name>com.liferay.blogs.model.BlogsEntry</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_blogs_web_portlet_BlogsAdminPortlet</portlet-name>\n            <portlet-name>com_liferay_blogs_web_portlet_BlogsPortlet</portlet-name>\n        </portlet-ref>\n        <weight>2</weight>\n        <permissions>\n            <supports>\n                <action-key>ADD_DISCUSSION</action-key>\n                <action-key>DELETE</action-key>\n                <action-key>DELETE_DISCUSSION</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>UPDATE</action-key>\n                <action-key>UPDATE_DISCUSSION</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>ADD_DISCUSSION</action-key>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>ADD_DISCUSSION</action-key>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>DELETE</action-key>\n                <action-key>DELETE_DISCUSSION</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>UPDATE</action-key>\n                <action-key>UPDATE_DISCUSSION</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n    ```\n\nNote the lack of a `<root>` tag, the fully qualified class name for the\nmodel, and the permissions that operate on an entity with a primary key. \n\n## Enabling Your Permissions Configuration\n\nYour last step is to enable your permission definitions. Each module that\ncontains a `default.xml` permissions definition file must also have\na `portlet.properties` file with a property that defines where to find the\npermissions definition file. For your service and your web modules, create\na `portlet.properties` file in `src/main/resources` and make sure it has this\nproperty: \n\n```properties\nresource.actions.configs=resource-actions/default.xml\n```\n\nOnce you've defined portlet permissions, root model permissions, and model\npermissions, you've completed step 1 (the *D* in DRAC). Congratulations! You're\nnow ready to *register* the resources you've now defined in the permissions\nsystem. \n"
  },
  {
    "path": "en/developer/frameworks/articles/application-security/02-application-permissions/03-registering-permissions.markdown",
    "content": "---\nheader-id: registering-permissions\n---\n\n# Registering Permissions\n\n[TOC levels=1-4]\n\nDefining permissions was your first step; now you're ready to register the\npermissions you've defined. You must register your entities both in the database\nand in the permissions service running in the OSGi container. \n\n## Registering Permissions Resources in the Database\n\nAll this takes is a call to Liferay's resource service in your service layer. If\nyou're using Service Builder, this is very easy to do. \n\n1.  Open your `-LocalServiceImpl` class. \n\n2.  In your method that adds an entity, add a call to add a resource with the\n    entity. For example, Liferay's Blogs application adds resources this way: \n \n    ```java\n    resourceLocalService.addResources(\n    \tentry.getCompanyId(), entry.getGroupId(), entry.getUserId(),\n    \tBlogsEntry.class.getName(), entry.getEntryId(), false,\n    \taddGroupPermissions, addGuestPermissions);\n    ```\n\n    This method requires passing in the company ID, the group ID, the user ID,\n    the entity's class name, the entity's primary key, and some boolean\n    settings. In order, these settings define \n\n    - Whether the permission is a portlet resource\n    - Whether the default group permissions defined in `default.xml` should be\n      added\n    - Whether the default guest permissions defined in `default.xml` should be\n      added\n\nNote that the resource local service is injected automatically into your Service\nBuilder-generated service. \n\nIf you're not using Service Builder, but you are using OSGi modules for your\napplication, you should be able to inject the resource service with an\n`@Reference` annotation. If you're building a WAR-style plugin, you need\na [service tracker](/docs/7-2/frameworks/-/knowledge_base/f/service-trackers-for-osgi-services) to\ngain access to the service. Note that your model classes must also implement\nLiferay's `ClassedModel` interface. \n\nSimilarly, when you delete an entity, you should also delete its associated\nresource. Here's how the Blogs application does it in its `deleteEntry()`\nmethod: \n\n```java\nresourceLocalService.deleteResource(\n\tentry.getCompanyId(), BlogsEntry.class.getName(),\n\tResourceConstants.SCOPE_INDIVIDUAL, entry.getEntryId());\n```\n\nAs with adding resources, the method needs to know the entity's company ID,\nclass, and primary key. Most of the time, its scope is an individual entity of\nyour own choosing. Other scopes available as constants are for company, group,\nor group template (site template). These are used internally for those objects,\nso you'd only use them if you were customizing functionality for creating and\ndeleting them. \n\nNow you're ready to register your entities with the permissions service. \n\n## Registering Entities to the Permissions Service\n\nThe permissions service that's running must know about your entities and how to\ncheck permissions for them. This requires creating a permissions registrar\nclass. \n\n1.  In your service bundle, create a package that by convention ends in\n    `internal.security.permission.resource`. For example, the Blogs\n    application's package is named\n    `com.liferay.blogs.internal.security.permission.resource`. \n\n2.  Create a class in this package called `[Entity\n    Name]ModelResourcePermissionRegistrar`. For example, the Blogs application's\n    class is named `BlogsEntryModelResourcePermissionRegistrar`. \n\n3.  This class is a component class that requires overriding the `activate`\n    method to register the permissions logic you want for your entities. For\n    example, this is how the Blogs application registers its permissions: \n\n    ```java\n    @Component(immediate = true)\n    public class BlogsEntryModelResourcePermissionRegistrar {\n\n        @Activate\n        public void activate(BundleContext bundleContext) {\n            Dictionary<String, Object> properties = new HashMapDictionary<>();\n\n            properties.put(\"model.class.name\", BlogsEntry.class.getName());\n\n            _serviceRegistration = bundleContext.registerService(\n                ModelResourcePermission.class,\n                ModelResourcePermissionFactory.create(\n                    BlogsEntry.class, BlogsEntry::getEntryId,\n                    _blogsEntryLocalService::getEntry, _portletResourcePermission,\n                    (modelResourcePermission, consumer) -> {\n                        consumer.accept(\n                            new StagedModelPermissionLogic<>(\n                                _stagingPermission, BlogsPortletKeys.BLOGS,\n                                BlogsEntry::getEntryId));\n                        consumer.accept(\n                            new WorkflowedModelPermissionLogic<>(\n                                _workflowPermission, modelResourcePermission,\n                                BlogsEntry::getEntryId));\n                    }),\n                properties);\n        }\n\n        @Deactivate\n        public void deactivate() {\n            _serviceRegistration.unregister();\n        }\n\n        @Reference\n        private BlogsEntryLocalService _blogsEntryLocalService;\n\n        @Reference(target = \"(resource.name=\" + BlogsConstants.RESOURCE_NAME + \")\")\n        private PortletResourcePermission _portletResourcePermission;\n\n        private ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n        @Reference\n        private StagingPermission _stagingPermission;\n\n        @Reference\n        private WorkflowPermission _workflowPermission;\n\n    }\n    ```\n\nWe call these types of classes Registrars because the classes' job is to configure, \nregister and unregister the `ModelResourcePermission`.\n\n1.  The `model.class.name` is set in the properties so that other modules' service \n    trackers can find this model resource permission by its type when it's needed. \n    Liferay has several service trackers checking for model resource permissions. \n    The `service.ranking` property can also be set to a value greater than zero to \n    override other module's model resource permissions. \n\n2.  This registrar uses two portal-kernel permission logic classes for Staging\n    and Workflow. Custom logic classes can be reused or composed inline since\n    `ModelResourcePermissionLogic` is a `@FunctionalInterface`. Permission logic\n    classes are executed in order of when they are accepted in the `Consumer`.\n\n3.  `ModelResourcePermissionLogic` classes return `true` when users have\n    permission for the action, `false` when they are denied permission for the\n    action, and `null` when wanting to delegate responsibility to the next\n    permission logic. If all permission logics return null then the\n    `PermissionChecker.hasPermission` method is called to determine if the\n    action is allowed for the user.\n\nThis class uses an `@Reference` with the target filter to inject the appropriate\n`PortletResourcePermission`. `BlogsConstants.RESOURCE_NAME` evaluates to\n`com.liferay.blogs`, which is defined in the `default.xml` you created earlier.\nIf you were to reference this `ModelResourcePermission`, you'd use a target filter\nmatching the `model.class.name` property set in the `activate` method.\n\nNote that you specify your entity's class, primary key, and the entity itself\nfor the factory so it can create permission objects specific to your entity. \n\nGreat! You've now completed step 2 in *DRAC* by registering your permissions.\nNow you're ready to provide users the interface to associate permissions with\nresources. \n\n"
  },
  {
    "path": "en/developer/frameworks/articles/application-security/02-application-permissions/04-associate-permissions-resources.markdown",
    "content": "---\nheader-id: associating-permissions-with-resources\n---\n\n# Associating Permissions with Resources\n\n[TOC levels=1-4]\n\nNow that you've defined and registered permissions, you must expose the\npermissions interface so users can set permissions. \n\nTo allow permissions to be configured for model resources, you must add the\npermissions interface to the UI. Add these two Liferay UI tags to your JSP:\n\n1.  `<liferay-security:permissionsURL>`: Returns a URL to the permission\n    settings configuration page. \n\n2.  `<liferay-ui:icon>`: Shows an icon to the user. These are defined in the\n    theme and one of them (see below) is used for permissions. \n\nThe Blogs application uses these tags like this: \n\n```markup\n<liferay-security:permissionsURL\n            modelResource=\"<%= BlogsEntry.class.getName() %>\"\n            modelResourceDescription=\"<%= BlogsEntryUtil.getDisplayTitle(resourceBundle, entry) %>\"\n            resourceGroupId=\"<%= String.valueOf(entry.getGroupId()) %>\"\n            resourcePrimKey=\"<%= String.valueOf(entry.getEntryId()) %>\"\n            var=\"permissionsEntryURL\"\n            windowState=\"<%= LiferayWindowState.POP_UP.toString() %>\"\n        />\n\n<liferay-ui:icon\n    label=\"<%= true %>\"\n    message=\"permissions\"\n    method=\"get\"\n    url=\"<%= permissionsEntryURL %>\"\n    useDialog=\"<%= true %>\"\n/>\n```\n\nFor the `<liferay-security:permissionsURL />` tag, specify these attributes: \n\n`modelResource`: The fully qualified class name of the entity class. This\nclass name gets translated into a more readable name as specified in\n`Language.properties`. \n\n`Language.properties`: The entity class in the example above is the Blogs entry\nclass for which the fully qualified class name is\n`com.liferay.blogs.model.BlogsEntry`.\n\n`modelResourceDescription`: You can enter anything that best describes this\nmodel instance. In the example above, the Blog title is used for the model\nresource description. \n\n`resourcePrimKey`: Your entity's primary key. \n\n`var`: The name of the variable to which the resulting URL string is assigned.\nThe variable is then passed to the `<liferay-ui:icon>` tag so the permission\nicon has the proper URL link.\n\nThere's an optional attribute called `redirect` that's available if you want to\noverride the default behavior of the upper right arrow link. That's it; now your\nusers can configure the permission settings for model resources! \n\nYou've completed step 3 in *DRAC*. Your next step is to check for permissions in\nthe appropriate areas of your application. \n"
  },
  {
    "path": "en/developer/frameworks/articles/application-security/02-application-permissions/05-check-permissions.markdown",
    "content": "---\nheader-id: checking-permissions\n---\n\n# Checking Permissions\n\n[TOC levels=1-4]\n\nNow that you've defined your permissions, registered resources in the database\nand with the OSGi container, and enabled users to associate permissions with\nresources, you're ready to add permission checks in the appropriate places in\nyour application. This takes three steps: \n\n1.  Add permission checks to your service calls. \n\n2.  Create permission helper classes in your web module. \n\n3.  Add permission checks to your web application. \n\nThese things are covered next. \n\n## Add Permission Checks to Your Service Calls\n\nA best practice is to create methods in your `-ServiceImpl` classes that call\nthe same methods in your `-LocalServiceImpl` classes, but wrap those calls in\npermission checks. If you expose your services as web services, then any client\ncalling those services must have permission to call the service. In this\nway, you separate your business logic (contained in the `-LocalServiceImpl`\nclass) from your permissions logic (contained in the `-ServiceImpl` class). \n\n1.  Open your entity's `-ServiceImpl` class. \n\n2.  Use the `ModelResourcePermissionFactory` and the\n    `PortletResourcePermissionFactory` to reference permission checkers that can\n    check permissions as you've defined them in `default.xml`. Here's how the\n    Blogs portlet does this: \n\n    ```java\n    private static volatile ModelResourcePermission<BlogsEntry>\n        _blogsEntryFolderModelResourcePermission =\n            ModelResourcePermissionFactory.getInstance(\n                BlogsEntryServiceImpl.class,\n                \"_blogsEntryFolderModelResourcePermission\", BlogsEntry.class);\n    private static volatile PortletResourcePermission\n        _portletResourcePermission =\n            PortletResourcePermissionFactory.getInstance(\n                BlogsEntryServiceImpl.class, \"_portletResourcePermission\",\n                BlogsConstants.RESOURCE_NAME);\n    ```\n\n    You declare the class, the variable, and for the portlet resource, the\n    resource name from `default.xml`. In the Blogs application,\n    `BlogsConstants.RESOURCE_NAME` is a `String` with the value\n    `com.liferay.blogs`. \n\n    You must use `ModelResourcePermissionFactory.getInstance()` in the service\n    because Service Builder is wired with Spring, so `@Reference` can't be used.\n    Make sure to use the correct service class and the name of the field that's\n    being set (in this case `\"_blogsEntryFolderModelResourcePermission\"`),\n    because it's set with reflection when the service is registered. If you get\n    the field wrong, it'll be set wrong. The field must be `static` and\n    `volatile`, and should never be used outside of `-ServiceImpl` classes. \n\n3.  Check permissions in the appropriate places. For example, adding a blog\n    entry requires the `ADD_ENTRY` permission, so the Blogs application does\n    this: \n\n    ```java\n    @Override\n    public BlogsEntry addEntry(\n            String title, String subtitle, String description, String content,\n            int displayDateMonth, int displayDateDay, int displayDateYear,\n            int displayDateHour, int displayDateMinute, boolean allowPingbacks,\n            boolean allowTrackbacks, String[] trackbacks,\n            String coverImageCaption, ImageSelector coverImageImageSelector,\n            ImageSelector smallImageImageSelector,\n            ServiceContext serviceContext)\n        throws PortalException {\n\n        _portletResourcePermission.check(\n            getPermissionChecker(), serviceContext.getScopeGroupId(),\n            ActionKeys.ADD_ENTRY);\n\n        return blogsEntryLocalService.addEntry(\n            getUserId(), title, subtitle, description, content,\n            displayDateMonth, displayDateDay, displayDateYear, displayDateHour,\n            displayDateMinute, allowPingbacks, allowTrackbacks, trackbacks,\n            coverImageCaption, coverImageImageSelector, smallImageImageSelector,\n            serviceContext);\n    }\n    ```\n\n    The check throws an exception if it fails, preventing the local service call\n    that adds the entry. A convention Liferay uses is to place the action keys\n    from `default.xml` as constants in an `ActionKeys` class. If `ActionKeys`\n    doesn't have an action key appropriate for your application, extend\n    Liferay's class and add your own keys. \n\nAdd permission checks where necessary to protect your application's functions at\nthe service level. Next, you'll learn how to create permission helper classes\nfor your web module. \n\n## Create Permission Helper Classes in Your Web Module\n\nA helper class can make it easier to check permissions in your portlet\napplication. You can create helper classes for both portlet permissions and \nmodel permissions. Here's how to create a portlet permission helper: \n\n1.  Create a package with the suffix\n    `web.internal.security.permission.resource`. For example, the Blogs\n    application has the package\n    `com.liferay.blogs.web.internal.security.permission.resource`. \n\n2.  Create a component class with at least one static method for checking\n    permissions. For example, here's the `BlogsPermission` class: \n\n    ```java\n    @Component(immediate = true)\n    public class BlogsPermission {\n\n        public static boolean contains(\n            PermissionChecker permissionChecker, long groupId, String actionId) {\n\n            return _portletResourcePermission.contains(\n                permissionChecker, groupId, actionId);\n        }\n\n        @Reference(\n            target = \"(resource.name=\" + BlogsConstants.RESOURCE_NAME + \")\",\n            unbind = \"-\"\n        )\n        protected void setPortletResourcePermission(\n            PortletResourcePermission portletResourcePermission) {\n\n            _portletResourcePermission = portletResourcePermission;\n        }\n\n        private static PortletResourcePermission _portletResourcePermission;\n\n    }\n    ```\n\n    Note the `@Reference` annotation that tells the OSGi container to supply an\n    object via the permission registrar you created previously. The\n    `_portletResourcePermission` field is static, while the setter method is an\n    instance method: this is how Liferay avoids having service references in\n    JSPs. \n\nThe procedure for creating a model permission helper is similar: \n\n1.  In the same package, create a component class with at least one static\n    method for checking permissions. For example, here's the\n    `BlogsEntryPermission` class: \n\n    ```java\n    @Component(immediate = true)\n    public class BlogsEntryPermission {\n\n        public static boolean contains(\n                PermissionChecker permissionChecker, BlogsEntry entry,\n                String actionId)\n            throws PortalException {\n\n            return _blogsEntryFolderModelResourcePermission.contains(\n                permissionChecker, entry, actionId);\n        }\n\n        public static boolean contains(\n                PermissionChecker permissionChecker, long entryId, String actionId)\n            throws PortalException {\n\n            return _blogsEntryFolderModelResourcePermission.contains(\n                permissionChecker, entryId, actionId);\n        }\n\n        @Reference(\n            target = \"(model.class.name=com.liferay.blogs.model.BlogsEntry)\",\n            unbind = \"-\"\n        )\n        protected void setEntryModelPermission(\n            ModelResourcePermission<BlogsEntry> modelResourcePermission) {\n\n            _blogsEntryFolderModelResourcePermission = modelResourcePermission;\n        }\n\n        private static ModelResourcePermission<BlogsEntry>\n            _blogsEntryFolderModelResourcePermission;\n\n    }\n    ```\n\n    As you can see, this class is almost the same as the portlet permission\n    class. The real difference is in the `@Reference` annotation that specifies\n    the fully qualified class name of the model, rather than the resource name\n    from `default.xml`. \n\n2.  Save both files. \n\nNow you're ready to use these helper classes to check permissions in your web\nmodule. \n\n## Add Permission Checks to Your Web Application\n\nYou can use the permission helper classes to check for permissions before\ndisplaying UI elements. If the element never appears, a user can't access it\n(though you should also protect your services as described above). Here's how to\ndo that: \n\n1.  When you have a function you want to protect, wrap it in an `if` statement\n    that uses the permission helper class. For example, the Blogs application\n    has many functions protected by permissions, including `ADD_ENTRY` and\n    `SUBSCRIBE`. Clearly, only blog owners should be able to add blog entries.\n    The button for this, therefore, should only appear if a user has permission\n    to add entries: \n\n    ```markup\n    <c:if test=\"<%= BlogsPermission.contains(permissionChecker, scopeGroupId, ActionKeys.ADD_ENTRY) %>\">\n        <div class=\"button-holder\">\n            <portlet:renderURL var=\"editEntryURL\" windowState=\"<%= WindowState.MAXIMIZED.toString() %>\">\n                <portlet:param name=\"mvcRenderCommandName\" value=\"/blogs/edit_entry\" />\n                <portlet:param name=\"redirect\" value=\"<%= currentURL %>\" />\n            </portlet:renderURL>\n\n            <aui:button href=\"<%= editEntryURL %>\" icon=\"icon-plus\" value=\"add-blog-entry\" />\n        </div>\n    </c:if>\n    ```\n\n2.  Do this for any function. For example, the Permissions function you added in\n    [step 3](/docs/7-2/frameworks/-/knowledge_base/f/associating-permissions-with-resources)\n    should definitely be protected by permissions: \n\n    ```markup\n    <c:if test=\"<%= BlogsEntryPermission.contains(permissionChecker, entry, ActionKeys.PERMISSIONS) %>\">\n        <liferay-security:permissionsURL\n            modelResource=\"<%= BlogsEntry.class.getName() %>\"\n            modelResourceDescription=\"<%= BlogsEntryUtil.getDisplayTitle(resourceBundle, entry) %>\"\n            resourceGroupId=\"<%= String.valueOf(entry.getGroupId()) %>\"\n            resourcePrimKey=\"<%= String.valueOf(entry.getEntryId()) %>\"\n            var=\"permissionsEntryURL\"\n            windowState=\"<%= LiferayWindowState.POP_UP.toString() %>\"\n        />\n\n        <liferay-ui:icon\n            label=\"<%= true %>\"\n            message=\"permissions\"\n            method=\"get\"\n            url=\"<%= permissionsEntryURL %>\"\n            useDialog=\"<%= true %>\"\n        />\n    </c:if>\n    ```\n\n    This prevents anyone without the permission to set permissions from seeing\n    the permissions button. Say that three times fast! \n\nThat's all there is to it! You've now learned all the steps in *DRAC*: \n\n1.  Define permissions \n\n2.  Register permissions\n\n3.  Associate permissions with resources\n\n4.  Check permissions\n\nFollow these steps, and your applications can take advantage of Liferay's\nintegrated and well-tested permissions system. \n\n"
  },
  {
    "path": "en/developer/frameworks/articles/application-security/02-application-permissions/06-using-portal-roles-in-a-portlet.markdown",
    "content": "---\nheader-id: using-portal-roles-in-a-portlet\n---\n\n# Using JSR Roles in a Portlet\n\n[TOC levels=1-4]\n\nRoles in @product@ are the primary means for granting or restricting access\nto content. If you've decided *not* to use Liferay's permissions system, you can\nuse the basic system offered by the JSR 168, 286, and 362 specifications that\nmap Roles in a portlet to Roles provided by the portal. \n\n## JSR Portlet Security\n\nThe portlet specification defines a means to specify Roles used by portlets in\ntheir `docroot/WEB-INF/portlet.xml` descriptors. The Role names themselves,\nhowever, are not standardized. When these portlets run in @product@, the Role\nnames defined in the portlet must be mapped to Roles that exist in the Portal.\n\nFor example, consider a Guestbook project that contains two portlets: The\nGuestbook portlet and the Guestbook Admin portlet. The WAR version of the\nGuestbook project's `portlet.xml` file references the *administrator*, *guest*,\n*power-user*, and *user* Roles: \n\n```xml\n<?xml version=\"1.0\"?>\n\n<portlet-app xmlns=\"http://xmlns.jcp.org/xml/ns/portlet\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/portlet http://xmlns.jcp.org/xml/ns/portlet/portlet-app_3_0.xsd\" version=\"3.0\">\n\t<portlet>\n\t\t<portlet-name>guestbook-war</portlet-name>\n\t\t<display-name>guestbook-war</display-name>\n\t\t<portlet-class>com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet</portlet-class>\n\t\t<init-param>\n\t\t\t<name>template-path</name>\n\t\t\t<value>/</value>\n\t\t</init-param>\n\t\t<init-param>\n\t\t\t<name>view-template</name>\n\t\t\t<value>/view.jsp</value>\n\t\t</init-param>\n\t\t<expiration-cache>0</expiration-cache>\n\t\t<supports>\n\t\t\t<mime-type>text/html</mime-type>\n\t\t</supports>\n\t\t<resource-bundle>content.Language</resource-bundle>\n\t\t<portlet-info>\n\t\t\t<title>guestbook-war</title>\n\t\t\t<short-title>guestbook-war</short-title>\n\t\t\t<keywords>guestbook-war</keywords>\n\t\t</portlet-info>\n\t\t<security-role-ref>\n\t\t\t<role-name>administrator</role-name>\n\t\t</security-role-ref>\n\t\t<security-role-ref>\n\t\t\t<role-name>guest</role-name>\n\t\t</security-role-ref>\n\t\t<security-role-ref>\n\t\t\t<role-name>power-user</role-name>\n\t\t</security-role-ref>\n\t\t<security-role-ref>\n\t\t\t<role-name>user</role-name>\n\t\t</security-role-ref>\n\t</portlet>\n</portlet-app>\n```\n\nAn OSGi-based `guestbook-web` module project defines Roles without an XML file,\nin the portlet class's `@Component` annotation: \n\n```java\n@Component(\n\timmediate = true,\n\tproperty = {\n\t\t\"com.liferay.portlet.display-category=category.sample\",\n\t\t\"com.liferay.portlet.instanceable=true\",\n\t\t\"javax.portlet.init-param.template-path=/\",\n\t\t\"javax.portlet.init-param.view-template=/view.jsp\",\n\t\t\"javax.portlet.name=\" + GuestbookPortletKeys.Guestbook,\n\t\t\"javax.portlet.resource-bundle=content.Language\",\n\t\t\"javax.portlet.security-role-ref=power-user,user\"\n\t},\n\tservice = Portlet.class\n)\n```\nIf you are using an OSGi-based MVC Portlet, you must use Liferay's permissions\nsystem, as the only way to map JSR-362 Roles to Liferay Roles is to place them\nin the Liferay WAR file's `portlet.xml`. \n\nYour `portlet.xml` Roles must be mapped to specific Roles that have been created.\nThese mappings allow @product@ to resolve conflicts between Roles with the same\nname that are from different portlets (e.g. portlets from different developers). \n\n| **Note:** Each Role named in a portlet's `<security-role-ref>` element is given\n| permission to add the portlet to a page.\n\n## Mapping Portlet Roles to Portal Roles\n\nTo map the Roles to @product@, you must use the\n`docroot/WEB-INF/liferay-portlet.xml` Liferay-specific configuration file. For\nan example, see the mapping defined in the Guestbook project's\n`liferay-portlet.xml` file. \n\n```xml\n<role-mapper>\n    <role-name>administrator</role-name>\n    <role-link>Administrator</role-link>\n</role-mapper>\n<role-mapper>\n    <role-name>guest</role-name>\n    <role-link>Guest</role-link>\n</role-mapper>\n<role-mapper>\n    <role-name>power-user</role-name>\n    <role-link>Power User</role-link>\n</role-mapper>\n<role-mapper>\n    <role-name>user</role-name>\n    <role-link>User</role-link>\n</role-mapper>\n```\n\nIf a portlet definition references the Role `power-user`, that portlet is mapped\nto the Liferay Role called *Power User* that's already in Liferay's database. \n\nAs stated above, there is no standardization with portal Role names. If you\ndeploy a portlet with Role names different from the above default Liferay names,\nyou must add the names to the `system.roles` property in your\n`portal-ext.properties` file: \n\n```properties\nsystem.roles=my-role,your-role,our-role\n```\n\nThis prevents Roles from being created accidentally. \n\nOnce Roles are mapped to the portal, you can use methods as defined in the\nportlet specification: \n\n- `getRemoteUser()`\n- `isUserInRole()`\n- `getUserPrincipal()`\n\nFor example, you can use the following code to check if the current User has\nthe `power-user` Role:\n\n```java\nif (renderRequest.isUserInRole(\"power-user\")) {\n    // ...\n}\n```\n\nBy default, Liferay doesn't use the `isUserInRole()` method in any built-in\nportlets. Liferay uses its own permission system directly to achieve more\nfine-grained security. If you don't intend on deploying your portlets to other\nportal servers, we recommend using Liferay's permission system, because it\noffers a much more robust way of tailoring your application's permissions. \n\n## Related Topics\n\n[Liferay Permissions](/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions)\n\n[Asset Framework](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework)\n\n[Portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets)\n\n[Understanding ServiceContext](/docs/7-2/frameworks/-/knowledge_base/f/understanding-servicecontext)\n"
  },
  {
    "path": "en/developer/frameworks/articles/application-security/04-authentication-pipelines/01-intro.markdown",
    "content": "---\nheader-id: authentication-pipelines\n---\n\n# Authentication Pipelines\n\n[TOC levels=1-4]\n\nThe authentication process is a pipeline through which users can be validated by\none or several systems. As a developer, you can authenticate users to anything\nyou wish, rather than be limited by what @product@ supports out of the box. \n\nHere's how authentication works under most circumstances: \n\n1.  Users provide their credentials to the Login Portlet to begin an\n    authenticated session in a browser. \n\n2.  Alternatively, credentials are provided to @product@'s API endpoints, where\n    they are sent in an HTTP BASIC Auth header. \n\n3.  Alternatively, credentials can be provided by another system. These are\n    managed by `AutoLogin` components. \n\n4.  Credentials are checked by default against the database, but they can be\n    delegated to other systems instead of or in addition to it. This is called\n    an *Authentication Pipeline*. You can add `Authenticator`s to the pipeline \n    to support any system. \n\n5.  You can also customize the Login Portlet to support whatever user interface\n    any of these systems need. This gives you full flexibility over the entire\n    authentication process. \n\nThis structure lets you support an authentication mechanism and/or accept\ncredentials from a system that @product@ doesn't yet support. If you don't like\nthe user interface for signing in, you can replace it with your own. \n\nThese tutorials guide you through these customizations. You'll discover three\nkinds of customizations: \n\n- **Auto Login:** the easiest of the three, this enables authentication to\n  @product@ using credentials provided in the HTTP header from another system. \n\n- **Authentication Pipelines:** if you must check credentials against other\n  systems instead of or in addition to @product@'s database, you can create a\n  pipeline. \n\n- **Custom Login Portlet:** if you want to change the user's sign-in experience\n  completely, you can implement your own Login portlet. \n\nRead on to discover how to customize your users' sign-in experience. \n\n"
  },
  {
    "path": "en/developer/frameworks/articles/application-security/04-authentication-pipelines/02-auto-login.markdown",
    "content": "---\nheader-id: auto-login\n---\n\n# Auto Login\n\n[TOC levels=1-4]\n\nWhile @product@ supports a wide variety of \n[authentication mechanisms](/docs/7-2/deploy/-/knowledge_base/d/securing-product), \nyou may use a home-grown system or some other product to authenticate users. To \ndo so, you can write an Auto Login component to support your authentication \nsystem. \n\nAuto Login components can check if the request contains something (a cookie, an\nattribute) that can be associated with a user in any way. If the component can\nmake that association, it can authenticate that user. \n\n## Creating an Auto Login Component\n\nCreate a \n[Declarative Services component](/docs/7-2/reference/-/knowledge_base/r/creating-a-project). \nThe component should implement the \n`com.liferay.portal.kernel.security.auto.login.AutoLogin` interface. Here's an \nexample template: \n\n```java\nimport com.liferay.portal.kernel.security.auto.login.AutoLogin;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.osgi.service.component.annotations.Component;\n\n@Component(immediate = true)\npublic class MyAutoLogin implements Autologin {\n\n    public String[] handleException(\n            HttpServletRequest request, HttpServletResponse response,\n            Exception e)\n        throws AutoLoginException {\n\n        /* This method is no longer used in the interface and can be \n      left empty */\n\n    }\n\n    public String[] login(\n            HttpServletRequest request, HttpServletResponse response)\n        throws AutoLoginException {\n\n        /* Your Code Goes Here */\n\n    }\n\n}\n```\n\nAs you can see, you have access to the `HttpServletRequest` and the \n`HttpServletResponse` objects. If your sign-on solution places anything here \nthat identifies a user such as a cookie, an attribute, or a parameter, you can\nretrieve it and take whatever action you need to retrieve the user information \nand authenticate that user. \n\nFor example, say that there's a request attribute that contains the encrypted \nvalue of a user key. This can only be there if the user has authenticated with\na third party system that knew the value of the user key, encrypted it, and\nadded it as a request attribute. You could write code that reads the value,\ndecrypts it using the same pre-shared key, and uses the value to look up and\nauthenticate the user. \n\nThe `login` method is where this all happens. This method must return a `String` \narray with three items in this order: \n\n- The user ID\n- The user password\n- A boolean flag that's `true` if the password is encrypted and `false` if it's\n  not (`Boolean.TRUE.toString()` or `Boolean.FALSE.toString()`). \n\nSending redirects is an optional `AutoLogin` feature. Since `AutoLogin`s are \npart of the servlet filter chain, you have two options. Both are implemented by \nsetting attributes in the request. Here are the attributes: \n\n- `AutoLogin.AUTO_LOGIN_REDIRECT`: This key causes `AutoLoginFilter` to stop the\n  filter chain's execution and redirect immediately to the location specified\n  in the attribute's value. \n\n- `AutoLogin.AUTO_LOGIN_REDIRECT_AND_CONTINUE`: This key causes\n  `AutoLoginFilter` to set the redirect and continue executing the remaining\n  filters in the chain. \n\nAuto Login components are useful ways of providing an authentication mechanism \nto a system that @product@ doesn't yet support. You can write them fairly \nquickly to provide the integration you need. \n\n## Related Topics\n\n[Password-Based Authentication Pipelines](/docs/7-2/frameworks/-/knowledge_base/f/password-based-authentication-pipelines)\n\n[Writing a Custom Login Portlet](/docs/7-2/frameworks/-/knowledge_base/f/writing-a-custom-login-portlet)\n"
  },
  {
    "path": "en/developer/frameworks/articles/application-security/04-authentication-pipelines/03-authentication-pipelines.markdown",
    "content": "---\nheader-id: password-based-authentication-pipelines\n---\n\n# Password-Based Authentication Pipelines\n\n[TOC levels=1-4]\n\nBy default, once a user submits credentials, those credentials are checked\nagainst @product@'s database, though you can also delegate authentication to an\nLDAP server. To use some other system in your environment instead of or in\naddition to checking credentials against the database, you can write an\n`Authenticator` and insert it as a step in the authentication pipeline. \n\nBecause the `Authenticator` is checked by the Login Portlet, you can't use this\napproach if the user must be redirected to the external system or needs a token\nto authenticate. In those cases, you should use an \n[Auto Login](/docs/7-2/frameworks/-/knowledge_base/f/auto-login) or an \n[Auth Verifier](/docs/7-2/deploy/-/knowledge_base/d/authentication-verifiers). \n\n`Authenticator`s let you do these things: \n\n- Log into @product@ with a user name and password maintained in an external \n  system \n- Make secondary user authentication checks \n- Perform additional processing when user authentication fails \n\nRead on to learn how to create an `Authenticator`. \n\n## Anatomy of an Authenticator\n\n`Authenticator`s are implemented for various steps in the authentication \npipeline. Here are the steps: \n\n1.  `auth.pipeline.pre`: Comes before default authentication to the database. In\n    this step, you can skip credential validation against the database.\n    Implemented by `Authenticator`. \n\n2.  Default (optional) authentication to the database. \n\n3.  `auth.pipeline.post`: Further (secondary, tertiary) authentication checks.\n    Implemented by `Authenticator`. \n\n4.  `auth.failure`: Perform additional processing after authentication fails.\n    Implemented by `AuthFailure`. \n\nTo create an `Authenticator`, create a module and add a component that \nimplements the interface: \n\n```java\n@Component(\n    immediate = true, property = {\"key=auth.pipeline.post\"},\n    service = Authenticator.class\n)\npublic class MyCustomAuth implements Authenticator {\n\n    public int authenticateByEmailAddress(\n            long companyId, String emailAddress, String password,\n            Map<String, String[]> headerMap, Map<String, String[]> parameterMap)\n        throws AuthException {\n\nreturn Authenticator.SUCCESS;\n}\n\n    public int authenticateByScreenName(\n            long companyId, String screenName, String password,\n            Map<String, String[]> headerMap, Map<String, String[]> parameterMap)\n        throws AuthException {\n\nreturn Authenticator.SUCCESS;\n    }\n\n    public int authenticateByUserId(\n            long companyId, long userId, String password,\n            Map<String, String[]> headerMap, Map<String, String[]> parameterMap)\n        throws AuthException {\n\nreturn Authenticator.SUCCESS;\n    }\n}\n```\n\nThis example has been stripped down so you can see its structure. First, note \nthe `@Component` annotation's contents: \n\n- `immediate = true`: sets the component to start immediately\n- `key=auth.pipeline.post`: sets the `Authenticator` to run in the \n  `auth.pipeline.post` phase. To run the `auth.pipeline.pre` phase, substitute \n  `auth.pipeline.pre`. \n- `service = Authenticator.class`: implements the `Authenticator` service. All \n  `Authenticator`s must do this. \n\nThe three methods below the annotation run based on how you've configured \nauthentication: by email address (the default), by screen name, or by user ID.\nAll the methods throw an `AuthException` in case the `Authenticator` can't\nperform its task: if the system it's authenticating against is unavailable or if\nsome dependency can't be found. The methods in this barebones example return\nsuccess in all cases. If you deploy its module, it has no effect. Naturally,\nyou'll want to provide more functionality. Next is an example that shows you how\nto do that. \n\n## Creating an Authenticator\n\nThis example is an `Authenticator` that only allows users whose email addresses \nend with *@liferay.com* or *@example.com*. You can implement this using one \nmodule that does everything. If you think other modules might use the\nfunctionality that validates the email addresses, you should create two modules:\none to implement the `Authenticator` and one to validate email addresses. This\nexample shows the two module approach. \n\nTo create an `Authenticator`, create a module for your implementation. The most \nappropriate Blade template for this is the \n[service template](/docs/7-2/reference/-/knowledge_base/r/using-the-service-template). \nOnce you have the module, creating the `Activator` is straightforward: \n\n1.  Add the `@Component` annotation to bind your `Activator` to the appropriate\n    authentication pipeline phase. \n\n2.  Implement the `Authenticator` interface and provide the functionality you\n    need. \n\n3.  Deploy your module. If you're using \n    [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli), do this via \n    `blade deploy`. \n\nFor this example, you'll do this twice: once for the email address validator\nmodule and once for the `Authenticator` itself. The `Authenticator` project\ncontains the interface for the validator, and the validator project contains the\nimplementation. Here's what the `Authenticator` module structure looks like: \n\n![Figure 1: The Authenticator module contains the validator's interface and the authenticator.](../../../images/auth-pipeline-authenticator-project.png)\n\nSince the `Authenticator` is the most relevant, examine it first: \n\n```java\npackage com.liferay.docs.emailaddressauthenticator;\n\nimport java.util.Map;\n\nimport com.liferay.docs.emailaddressauthenticator.validator.EmailAddressValidator;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.security.auth.AuthException;\nimport com.liferay.portal.kernel.security.auth.Authenticator;\nimport com.liferay.portal.kernel.service.UserLocalService;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\nimport org.osgi.service.component.annotations.ReferenceCardinality;\nimport org.osgi.service.component.annotations.ReferencePolicy;\n\n@Component(\n    immediate = true,\n    property = {\"key=auth.pipeline.post\"},\n    service = Authenticator.class\n)\npublic class EmailAddressAuthenticator implements Authenticator {\n\n    @Override\n    public int authenticateByEmailAddress(long companyId, String emailAddress,\n            String password, Map<String, String[]> headerMap,\n            Map<String, String[]> parameterMap) throws AuthException {\n        \n        return validateDomain(emailAddress);\n    }\n\n    @Override\n    public int authenticateByScreenName(long companyId, String screenName,\n            String password, Map<String, String[]> headerMap,\n            Map<String, String[]> parameterMap) throws AuthException {\n        \n        String emailAddress = \n            _userLocalService.fetchUserByScreenName(companyId, screenName).getEmailAddress();\n        \n        return validateDomain(emailAddress);\n    }\n\n    @Override\n    public int authenticateByUserId(long companyId, long userId,\n            String password, Map<String, String[]> headerMap,\n            Map<String, String[]> parameterMap) throws AuthException {\n        \n        String emailAddress = \n            _userLocalService.fetchUserById(userId).getEmailAddress();\n        \n        return validateDomain(emailAddress);\n    }\n    \n    private int validateDomain(String emailAddress) throws AuthException {\n        \n        if (_emailValidator == null) {\n            \n            String msg = \"Email address validator is unavailable, cannot authenticate user\";\t\t\t\n            _log.error(msg);\n            \n            throw new AuthException(msg);\n        }\n        \n        if (_emailValidator.isValidEmailAddress(emailAddress)) {\t\t\n            return Authenticator.SUCCESS;\n        }\n        return Authenticator.FAILURE;\n    }\n    \n    @Reference\n    private volatile UserLocalService _userLocalService;\n    \n    @Reference(\n        policy = ReferencePolicy.DYNAMIC,\n        cardinality = ReferenceCardinality.OPTIONAL\n    )\n    private volatile EmailAddressValidator _emailValidator;\n    \n    private static final Log _log = LogFactoryUtil.getLog(EmailAddressAuthenticator.class);\n}\n```\n\nThis time, rather than stubs, the three authentication methods contain \nfunctionality. The `authenticateByEmailAddress` method directly checks the email \naddress provided by the Login Portlet. The other two methods, \n`authenticateByScreenName` and `authenticateByUserId` call `UserLocalService` to\nlook up the user's email address before checking it. The OSGi container injects\nthis service because of the `@Reference` annotation. Note that the validator is\nalso injected in this same manner, though it's configured not to fail if the\nimplementation can't be found. This allows this module to start regardless of\nits dependency on the validator implementation. In this case, this is safe\nbecause the error is handled by throwing an `AuthException` and logging the\nerror. \n\nWhy would you want to do it this way? To err gracefully. Because this is an \n`auth.pipeline.post` `Authenticator`, you presumably have other `Authenticator`s \nchecking credentials before this one. If this one isn't working, you want to \ninform administrators with an error message rather than catastrophically failing \nand preventing users from logging in. \n\nThe only other Java code in this module is the Interface for the validator: \n\n```java\npackage com.liferay.docs.emailaddressauthenticator.validator;\n\nimport aQute.bnd.annotation.ProviderType;\n\n@ProviderType\npublic interface EmailAddressValidator {\n\n    public boolean isValidEmailAddress(String emailAddress);\n}\n```\n\nThis defines a single method for checking the email address. \n\nNext, you'll address the validator module. \n\n![Figure 2: The validator project implements the Validator Interface and depends on the authenticator module. ](../../../images/auth-pipeline-validator-project.png)\n\nThis module contains only one class. It implements the Validator interface: \n\n```java\npackage com.liferay.docs.emailaddressvalidator.impl;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\nimport org.osgi.service.component.annotations.Component;\nimport com.liferay.docs.emailaddressauthenticator.validator.EmailAddressValidator;\n\n@Component(\n    immediate = true,\n    property = {\n    },\n    service = EmailAddressValidator.class\n)\npublic class EmailAddressValidatorImpl implements EmailAddressValidator {\n\n    @Override\n    public boolean isValidEmailAddress(String emailAddress) {\n\n        if (_validEmailDomains.contains(\n            emailAddress.substring(emailAddress.indexOf('@')))) {\n\n            return true;\n        }\n        return false;\n    }\n\n    private Set<String> _validEmailDomains = \n        new HashSet<String>(Arrays.asList(new String[] {\"@liferay.com\", \"@example.com\"}));\n}\n```\n\nThis code checks to make sure that the email address is from the *@liferay.com* \nor *@example.com* domains. The only other interesting part of this module is the \nGradle build script, because it defines a compile-only dependency between the \ntwo projects. This is divided into two files: a `settings.gradle` and a \n`build.gradle`. \n\nThe `settings.gradle` file defines the location of the project (the \n`Authenticator`) the validator depends on: \n\n```groovy\ninclude ':emailAddressAuthenticator'\nproject(':emailAddressAuthenticator').projectDir = new File(settingsDir, '../com.liferay.docs.emailAddressAuthenticator')\n```\n\nSince this project contains the interface, it must be on the classpath at \ncompile time, which is when `build.gradle` is running: \n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins\", version: \"3.0.23\"\n    }\n\n    repositories {\n        mavenLocal()\n\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.plugin\"\n\ndependencies {\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"2.0.0\"\n    compileOnly group: \"org.osgi\", name: \"org.osgi.compendium\", version: \"5.0.0\"\n\n    compileOnly project(\":emailAddressAuthenticator\")\n}\n\nrepositories {\n    mavenLocal()\n\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\nNote the line in the dependencies section that refers to the `Authenticator` \nproject defined in `settings.gradle`. \n\nWhen these projects are deployed, the `Authenticator` you defined runs, \nenforcing logins for the two domains specified in the validator. \n\n## Related Topics\n\n[Auto Login](/docs/7-2/frameworks/-/knowledge_base/f/auto-login)\n\n[Writing a Custom Login Portlet](/docs/7-2/frameworks/-/knowledge_base/f/writing-a-custom-login-portlet)\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/application-security/04-authentication-pipelines/04-custom-login-portlet.markdown",
    "content": "---\nheader-id: writing-a-custom-login-portlet\n---\n\n# Writing a Custom Login Portlet\n\n[TOC levels=1-4]\n\nIf you need to customize your users' authentication experience completely, you\ncan write your own Login Portlet. The mechanics of this on the macro level are\nno different from writing any other portlet, so if you need to familiarize\nyourself with that, please see the \n[portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets). \n\nThis tutorial shows only the relevant parts of a \n[Liferay MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet) that\nauthenticates the user. You'll learn how to call the \n[authentication pipeline](/docs/7-2/frameworks/-/knowledge_base/f/password-based-authentication-pipelines) \nand then redirect the user to a location of your choice. \n\n## Authenticating to @product@\n\n| **Note:** When developing a login portlet, set the session timeout portal\n| property like this:\n| \n|     session.timeout.auto.extend.offset=45\n| \n| This is needed because the default (as of\n| [LPS-68543](https://issues.liferay.com/browse/LPS-68543)) setting is `0`,\n| causing the browser to execute an `extend_session` call. This may force users\n| attempting to log in to make the attempt twice.\n\nIt has only one view, which is used for logging in or showing the user who is\nalready logged in: \n\n```markup\n<%@ include file=\"/init.jsp\" %>\n\n<p>\n    <b><liferay-ui:message key=\"myloginportlet_MyLogin.caption\"/></b>\n</p>\n\n<c:choose>\n    <c:when test=\"<%= themeDisplay.isSignedIn() %>\">\n\n        <%\n        String signedInAs = HtmlUtil.escape(user.getFullName());\n\n        if (themeDisplay.isShowMyAccountIcon() && (themeDisplay.getURLMyAccount() != null)) {\n            String myAccountURL = String.valueOf(themeDisplay.getURLMyAccount());\n\n            signedInAs = \"<a class=\\\"signed-in\\\" href=\\\"\" + HtmlUtil.escape(myAccountURL) + \"\\\">\" + signedInAs + \"</a>\";\n        }\n        %>\n\n        <liferay-ui:message arguments=\"<%= signedInAs %>\" key=\"you-are-signed-in-as-x\" translateArguments=\"<%= false %>\" />\n    </c:when>\n    <c:otherwise>\n    \n        <%\n        String redirect = ParamUtil.getString(request, \"redirect\");\n        %>\n    \n        <portlet:actionURL name=\"/login/login\" var=\"loginURL\">\n            <portlet:param name=\"mvcRenderCommandName\" value=\"/login/login\" />\n        </portlet:actionURL>\n        \n        <aui:form action=\"<%= loginURL %>\" autocomplete='on' cssClass=\"sign-in-form\" method=\"post\" name=\"loginForm\">\n        \n            <aui:input name=\"saveLastPath\" type=\"hidden\" value=\"<%= false %>\" />\n            <aui:input name=\"redirect\" type=\"hidden\" value=\"<%= redirect %>\" />\n                    \n            <aui:input autoFocus=\"true\" cssClass=\"clearable\" label=\"email-address\" name=\"login\" showRequiredLabel=\"<%= false %>\" type=\"text\" value=\"\">\n                <aui:validator name=\"required\" />\n            </aui:input>\n\n            <aui:input name=\"password\" showRequiredLabel=\"<%= false %>\" type=\"password\">\n                <aui:validator name=\"required\" />\n            </aui:input>\n            \n            <aui:button-row>\n                <aui:button cssClass=\"btn-lg\" type=\"submit\" value=\"sign-in\" />\n            </aui:button-row>\n                \n        </aui:form>\n    </c:otherwise>\n</c:choose>\n```\n\nNote that in the form, authentication by email address (the default setting) is\nhard-coded, as this is an example project. The current page is sent as a hidden\nfield on the form so the portlet can redirect the user to it, but you can of\ncourse set this to any value you want. \n\nThe portlet handles all processing of this form using a single Action Command \n<!--Add link back for 'Action Command' once MVC Action Command article is available-->\n(imports left out for brevity): \n\n```markup\n@Component(\n    property = {\n        \"javax.portlet.name=MyLoginPortlet\",\n        \"mvc.command.name=/login/login\"\n    },\n    service = MVCActionCommand.class\n)\npublic class MyLoginMVCActionCommand extends BaseMVCActionCommand {\n\n    @Override\n    protected void doProcessAction(ActionRequest actionRequest,\n            ActionResponse actionResponse) throws Exception {\n\n        ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(\n            WebKeys.THEME_DISPLAY);\n        \n        HttpServletRequest request = PortalUtil.getOriginalServletRequest(\n            PortalUtil.getHttpServletRequest(actionRequest));\n        \n        HttpServletResponse response = PortalUtil.getHttpServletResponse(\n            actionResponse);\n\n        String login = ParamUtil.getString(actionRequest, \"login\");\n        String password = actionRequest.getParameter(\"password\");\n        boolean rememberMe = ParamUtil.getBoolean(actionRequest, \"rememberMe\");\n        String authType = CompanyConstants.AUTH_TYPE_EA;\n        \n        AuthenticatedSessionManagerUtil.login(\n            request, response, login, password, rememberMe, authType);\n        \n        actionResponse.sendRedirect(themeDisplay.getPathMain());\n    }\n\n}\n```\n\nThe only tricky/unusual code here is the need to grab the `HttpServletRequest` \nand the `HttpServletResponse`. This is necessary to call @product@'s API for \nauthentication. At the end of the Action Command, the portlet sends a redirect \nthat sends the user to the same page. You can of course make this any page you \nwant. \n\nImplementing your own login portlet gives you complete control over the\nauthentication process. \n\n## Related Topics\n\n[Password-Based Authentication Pipelines](/docs/7-2/frameworks/-/knowledge_base/f/password-based-authentication-pipelines)\n\n[Auto Login](/docs/7-2/frameworks/-/knowledge_base/f/auto-login)\n"
  },
  {
    "path": "en/developer/frameworks/articles/application-security/06-service-access-policies.markdown",
    "content": "---\nheader-id: service-access-policies\n---\n\n# Service Access Policies\n\n[TOC levels=1-4]\n\nService access policies provide web service security beyond user authentication\nto remote services. Together with\n[permissions](/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions),\nservice access policies limit remote service access by remote client\napplications. This forms an additional security layer that protects user data\nfrom unauthorized access and modification.\n\nTo connect to a web service, remote clients must authenticate using credentials\nin that instance. This grants the remote client the permissions assigned to\nthose credentials in the @product@ installation. Service access policies further\nlimit the remote client's access to the services specified in the policy.\nWithout such policies, authenticated remote clients are treated like users: they\ncan call any remote API and read or modify data on behalf of the authenticated\nuser. Since remote clients are often intended for a specific use case, granting\nthem access to everything the user has permissions for poses a security risk.\n\nFor example, consider a mobile app (client) that displays a user's appointments\nfrom the Liferay Calendar app. This client app doesn't need access to the API\nthat updates the user profile, even though the user has such permissions on the\nserver. The client app doesn't even need access to the Calendar API methods that\ncreate, update, and delete appointments. It only needs access to the remote\nservice methods for finding and retrieving appointments. A service access policy\non the server can restrict the client's access to only these service methods.\nSince the client doesn't perform other operations, having access to them is\na security risk if the mobile device is lost or stolen or the client app is\ncompromised by an attacker.\n\n## How Service Access Policies Work\n\nA remote client's request to a web service contains the user's credentials or an\nauthorization token. An authentication module recognizes the client based on the\ncredentials/token and grants the appropriate service access policy to the\nrequest. The service access policy authorization layer then processes all\ngranted policies and lets the request access the remote service(s) permitted by\nthe policy.\n\n![Figure 1: The authorization module maps the credentials or token to the proper Service Access Policy.](../../images/service-access-policies-arch.png)\n\nService Access policies are created in the Control Panel by\nadministrators. If you want to start creating policies yourself, see\n[this article on service access policies](/docs/7-2/deploy/-/knowledge_base/d/service-access-policies)\nthat documents creating them in the UI.\n\nThere may be cases, however, when your server-side Liferay app must use the\nservice access policies API:\n\n- It uses [custom remote API authentication](/docs/7-2/frameworks/-/knowledge_base/f/auto-login)\n  (tokens) and require certain services to be available for clients using\n  the tokens.\n\n- It requires its services be made available to guest users, with no authentication\n  necessary.\n\n- It contains a [remote service authorization layer](/docs/7-2/frameworks/-/knowledge_base/f/password-based-authentication-pipelines)\n  that needs to drive access to remote services based on granted\n  privileges.\n\n## API Overview\n\nLiferay provides an Interface and a `ThreadLocal` if you don't want to roll your own\npolicies. If you want to get low level, an API is provided that\nLiferay itself has used to implement \n[Liferay Sync](/docs/7-2/user/-/knowledge_base/u/administering-liferay-sync). \n\n1. The Interface and `ThreadLocal` are available in the \n   [package `com.liferay.portal.kernel.security.service.access.policy`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/security/service/access/policy/package-summary.html).\n   This package provides classes for basic access to policies. For example, you\n   can use the [singleton\n   `ServiceAccessPolicyManagerUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/security/service/access/policy/ServiceAccessPolicyManagerUtil.html)\n   to obtain Service Access Policies configured in the system. You can also use\n   the [`ServiceAccessPolicyThreadLocal`\n   class](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/security/service/access/policy/ServiceAccessPolicyThreadLocal.html)\n   to set and obtain Service Access Policies granted to the current request\n   thread.\n\n   At this level, you can get a list of the configured policies to let your\n   app/client choose a policy for accessing services. Also, apps like OAuth\n   can offer a list of available policies during the authorization step in\n   the OAuth workflow and allow the user to choose the policy to assign to\n   the remote application. You can also grant a policy to a current request\n   thread. When a remote client accesses an API, something must tell the\n   Liferay instance which policies are assigned to this call. This\n   something is in most cases an [`AuthVerifier`\n   implementation](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/security/auth/verifier/AuthVerifier.html).\n   For example, in the case of the OAuth app, an `AuthVerifier` implementation\n   assigns the policy chosen by the user in the authorization step.\n\n2. The API ships with the product as OSGi modules:\n\n- `com.liferay.portal.security.service.access.policy.api.jar`\n- `com.liferay.portal.security.service.access.policy.service.jar`\n- `com.liferay.portal.security.service.access.policy.web.jar`\n\n   These OSGi modules are active by default, and you can use them to manage\n   Service Access Policies programmatically. Each module publishes a list of\n   packages and services that can be consumed by other OSGi modules.\n\nYou can use both tools to develop a token verification module (a module that\nimplements custom security token verification for use in authorizing remote\nclients) for your app to use. For example, this module may contain a JSON Web\nToken implementation for @product@'s remote API. A custom token verification\nmodule must use the Service Access Policies API during the remote API/web\nservice call to grant the associated policy during the request. The module\n\n- can use `com.liferay.portal.security.service.access.policy.api.jar`\n  and `com.liferay.portal.security.service.access.policy.service.jar` to\n  create policies programmatically.\n\n- should use the method\n  `ServiceAccessPolicyThreadLocal.addActiveServiceAccessPolicyName()` to\n  grant the associated policy during a web service request.\n\n- can use `ServiceAccessPolicyManagerUtil` to display list of\n  supported policies when authorizing the remote application, to associate\n  the token with an existing policy.\n\n## Service Access Policy Example\n\n[Liferay Sync's](https://www.liferay.com/supporting-products/liferay-sync)\n`sync-security` module is a service access policy module. It uses\n`com.liferay.portal.security.service.access.policy.service` to create the\n`SYNC_DEFAULT` and `SYNC_TOKEN` policies programmatically. For service calls to\nSync's remote API, these policies grant access to Sync's\n`com.liferay.sync.service.SyncDLObjectService#getSyncContext` and\n`com.liferay.sync.service.*`, respectively. Here's the code in the\n`sync-security` module that defines and creates these policies:\n\n```java\n@Component(immediate = true)\npublic class SyncSAPEntryActivator {\n\n    // Define the policies\n    public static final Object[][] SAP_ENTRY_OBJECT_ARRAYS = new Object[][] {\n        {\n            \"SYNC_DEFAULT\",\n            \"com.liferay.sync.service.SyncDLObjectService#getSyncContext\", true\n        },\n        {\"SYNC_TOKEN\", \"com.liferay.sync.service.*\", false}\n    };\n\n    ...\n\n    // Create the policies\n    protected void addSAPEntry(long companyId) throws PortalException {\n            for (Object[] sapEntryObjectArray : SAP_ENTRY_OBJECT_ARRAYS) {\n                String name = String.valueOf(sapEntryObjectArray[0]);\n                String allowedServiceSignatures = String.valueOf(\n                    sapEntryObjectArray[1]);\n                boolean defaultSAPEntry = GetterUtil.getBoolean(\n                    sapEntryObjectArray[2]);\n\n                SAPEntry sapEntry = _sapEntryLocalService.fetchSAPEntry(\n                    companyId, name);\n\n                if (sapEntry != null) {\n                    continue;\n                }\n\n                Map<Locale, String> map = new HashMap<>();\n\n                map.put(LocaleUtil.getDefault(), name);\n\n                _sapEntryLocalService.addSAPEntry(\n                    _userLocalService.getDefaultUserId(companyId),\n                    allowedServiceSignatures, defaultSAPEntry, true, name, map,\n                    new ServiceContext());\n            }\n    }\n\n    ...\n\n}\n```\n\nThis class creates the policies when the module starts. Note that this module is\nincluded and enabled by default. You can access these and other policies in\n*Control Panel* &rarr; *Configuration* &rarr; *Service Access Policy*.\n\nThe `sync-security` module must then grant the appropriate policy when\nneeded. Since every authenticated call to Liferay Sync's remote API\nrequires access to `com.liferay.sync.service.*`, the module must\ngrant the `SYNC_TOKEN` policy to such calls. The module does this\nwith the method\n`ServiceAccessPolicyThreadLocal.addActiveServiceAccessPolicyName`, as\nshown in this code snippet:\n\n```java\nif ((permissionChecker != null) && permissionChecker.isSignedIn()) {\n    ServiceAccessPolicyThreadLocal.addActiveServiceAccessPolicyName(\n        String.valueOf(\n            SyncSAPEntryActivator.SAP_ENTRY_OBJECT_ARRAYS[1][0]));\n}\n```\n\nNow every authenticated call to Sync's remote API, regardless of\nauthentication method, has access to `com.liferay.sync.service.*`. To\nsee the full code example,\n[click here](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/sync/sync-security/src/main/java/com/liferay/sync/security/servlet/filter/SyncAuthFilter.java).\n\nNice! Now you know how to integrate your apps with the Service Access\nPolicies.\n"
  },
  {
    "path": "en/developer/frameworks/articles/assets/01-asset-framework-intro.markdown",
    "content": "---\nheader-id: asset-framework\n---\n\n# Asset Framework\n\n[TOC levels=1-4]\n\nThe asset framework is behind many of Liferay's most powerful features. It \nprovides tools for displaying and interacting with various types of content and \ndata. For example, if you build an event management application that\ndisplays a list of upcoming events, you can use the asset framework to let users\nadd tags, categories, or comments to make entries more self-descriptive. Using \nthe asset framework is also the first step for integrating other important \nframeworks like Segmentation and Personalization or Workflow.\n\nAs background, the term *asset* refers to any type of content: text, a file,\na URL, an image, documents, blog entries, bookmarks, wiki pages, or anything you\ncreate in your applications. \n\nThe asset framework tutorials assume that you've used Liferay's Service Builder\nto generate your persistence layer, that you've implemented permissions on the\nentities that you're persisting, and that you've enabled them for search and\nindexing. You can learn more about Liferay's Service Builder and how to use it\nin the\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\ntutorial section. After that is completed, you can get started asset enabling \nyour application.\n\nThis section explores how to leverage the asset framework's various features. \nHere are some features that you'll give your users as you implement them in \nyour app: \n\n-  Extensively render your assets.\n-  Associate tags to custom content types. Users can create and assign new\n   tags or use existing tags. \n-  Associate categories to custom content types. \n-  Manage tags from the Control Panel. Administrators can even merge tags. \n-  Manage categories from the Control Panel. This includes the ability to\n   create category hierarchies. \n-  Relate assets to one another. \n\nThere are several steps to creating an asset and taking full advantage of the \nasset framework.\n\n## Persistence Operations for Assets \n\nTo use Liferay's asset framework with an entity, you must inform the \nasset framework about each entity instance you create, modify, and delete. In\nthis sense, it's similar to informing \n[Liferay's permissions framework](/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions)\nabout a new resource. All you have to do is invoke a method of the asset\nframework that associates an `AssetEntry` with the entity so Liferay can keep\ntrack of the entity as an asset. When it's time to update the entity, you update\nthe asset at the same time. \n\nTo leverage assets, you must also implement indexers for your portlet's\nentities. Liferay's asset framework uses indexers to manage assets. \n\n## Rendering an Asset\n\nOnce you add your asset to the framework, you can render the asset\nusing the Asset Publisher application. The default render, however, only\ndisplays the asset's title and description text. Anything else requires\nadditional coding. For instance, you might want these additional things:\n\n-  An edit feature for modifying an asset.\n-  View an asset in its original context (e.g., a blog\n   in the Blogs application; a post in the Message Boards application).\n-  Embed images, videos, and audio.\n-  Restrict access to users who do not have permissions to interact with the\n   asset.\n-  Allow users to comment on the asset. \n\nYou can dictate your asset's rendering capabilities by providing the *Asset\nRenderer* framework. There are two prerequisites for asset enabling an \napplication:\n\n1.  The application must store asset data. Applications that store a data model\n    meet this requirement.\n\n2.  The application must contain at least one non-instanceable portlet. `Edit` \n    links for the asset cannot be generated without a non-instanceable portlet.\n \nSome applications may consist of only one non-instanceable portlet, while others\nmay consist of a both instanceable and non-instanceable portlets. If your \napplication does not currently include a non-instanceable portlet, adding a \nconfiguration interface through a panel app both enhances the usability of \nthe application, and meets the requirement for adding a non-instanceable \nportlet to the application. \n\n<!--Uncomment once article is available\nSee our tutorial on \nAdding Custom Panel Apps\nto learn how to add one.\n-->\n\nAfter you have met all the prerequisites, there are two things you must do to \nget your asset renderer functioning properly for your asset:\n\n1.  Create an asset renderer for your custom asset.\n\n2.  Create an asset renderer factory to create an instance of the asset renderer\n    for each asset entity.\n\n## Asset Features\n\nOnce you have done the necessary work to persist your assets and render them,\nyou can enable Tags, Categories, and Related Assets.\n\n### Tags and Categories\n\nTags and Categories are two ways that you can organize and connect assets. Tags\nare simple *ad hoc* groups. Any two assets with the same tag are connected by\nthat tag. Categories are a form of hierarchical organization where an\nadministrator can define a number of categories for organization content,\nimages, or other types of assets and use those categories to help users find\nwhat they're looking for.\n\n![Figure 1: Adding category and tag input options lets authors aggregate and label custom entities.](../../images/asset-fw-categories-and-tags-options.png)\n\n### Relating Assets\n\nRelating assets connects individual pieces of content across your site or\nportal. This helps users discover related content, particularly when there's an\nabundance of other available content. For example, assets related to a web\ncontent article appear alongside that entry in the Asset Publisher application.\n\n![Figure 2: You and your users can find it helpful to relate assets to entities, such as this blogs entry.](../../images/asset-related-content-asset-publisher.png)\n\n## Implementing Asset Priority\n\nThe \n[Asset Publisher](/docs/7-2/user/-/knowledge_base/u/publishing-assets) \nlets you order assets by priority. For this to work, however, users must be able\nto set the asset's priority when creating or editing the asset. For example,\nwhen creating or editing web content, users can assign a priority in the\nMetadata section's Priority field. \n\n![Figure 3: The Priority field lets users set an asset's priority.](../../images/web-content-categorization.png)\n\nReady to implement assets? The rest of the tutorials show you how. \n"
  },
  {
    "path": "en/developer/frameworks/articles/assets/02-adding-updating-and-deleting-assets.markdown",
    "content": "---\nheader-id: adding-updating-and-deleting-assets\n---\n\n# Adding, Updating, and Deleting Assets\n\n[TOC levels=1-4]\n\nThis section shows you how to enable assets for your custom entities and\nimplement indexes for them. It's time to get started! \n\n## Preparing Your Project for the Asset Framework\n\nIn your project's `service.xml` file, add an asset entry entity reference for\nyour custom entity. Add the following `reference` tag before your custom\nentity's closing `</entity>` tag.\n\n```xml\n<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetEntry\" />\n```\n\nThen [run Service Builder.](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n\nNow you're ready to implement adding and updating assets!\n\n## Adding and Updating Assets\n\nYour `-LocalServiceImpl` Java class inherits from its parent base class an\n`AssetEntryLocalService` instance; it's assigned to the variable \n`assetEntryLocalService`. To add your custom entity as a Liferay asset, you \nmust invoke the `assetEntryLocalService`'s `updateEntry` method. \n\nHere's what the [`updateEntry`](@platform-ref@/7.2-latest/javadocs/portal-impl/com/liferay/portlet/asset/service/impl/AssetEntryLocalServiceImpl.html#updateEntry-long-long-java.util.Date-java.util.Date-java.lang.String-long-java.lang.String-long-long:A-java.lang.String:A-boolean-boolean-java.util.Date-java.util.Date-java.util.Date-java.util.Date-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-int-int-java.lang.Double-)\nmethod's signature looks like:\n\n```java\nAssetEntry updateEntry(\n    long userId, long groupId, Date createDate, Date modifiedDate,\n    String className, long classPK, String classUuid, long classTypeId,\n    long[] categoryIds, String[] tagNames, boolean listable,\n    boolean visible, Date startDate, Date endDate, Date publishDate,\n    Date expirationDate, String mimeType, String title,\n    String description, String summary, String url, String layoutUuid,\n    int height, int width, Double priority)\nthrows PortalException\n```\n\nHere are descriptions of each of the `updateEntry` method's parameters: \n\n`userId`: identifies the user updating the content. \n\n`groupId`: identifies the scope of the created content. If your content\ndoesn't support scopes (extremely rare), pass `0` as the value. \n\n`createDate`: the date the entity was created.\n\n`modifiedDate`: the date of this change to the entity.\n\n`className`: identifies the entity's class. The recommended convention\nis to use the name of the Java class that represents your content type. For\nexample, you can pass in the value returned from\n`[YourClassName].class.getName()`. \n\n`classPK`: identifies the specific entity instance, distinguishing it\nfrom other instances of the same type. It's usually the primary key of the\ntable where the entity is stored.\n\n`classUuid`: serves as a secondary identifier that's guaranteed  to\nbe universally unique. It correlates entity instances across scopes. It's\nespecially useful if your content is exported and imported across separate\nportals. \n\n`classTypeId`: identifies the particular variation of this class, if it has\nany variations. Otherwise, use `0`. \n\n`categoryIds`: represent the categories selected for the entity.\nThe asset framework stores them for you. \n\n`tagNames`: represent the tags selected for the entity.\nThe asset framework stores them for you.\n\n`listable`: specifies whether the entity can be shown in dynamic lists of \ncontent (such as asset publisher configured dynamically). \n\n`visible`: specifies whether the entity is approved. \n\n`startDate`: the entity's publish date. You can use it to specify when an\nAsset Publisher should show the entity's content.\n\n`endDate`: the date the entity is taken down. You can use it to specify\nwhen an Asset Publisher should stop showing the entity's content.\n\n`publishDate`: the date the entity will start to be shown. \n\n`expirationDate`: the date the entity will no longer be shown. \n\n`mimetype`: the Multi-Purpose Internet Mail Extensions type, such as\n[ContentTypes.TEXT_HTML](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ContentTypes.html#TEXT_HTML),\nused for the content.\n\n`title`: the entity's name.\n\n`description`: a `String`-based textual description of the entity.\n\n`summary`: a shortened or truncated sample of the entity's content. \n\n`url`: a URL to optionally associate with the entity. \n\n`layoutUuid`: the universally unique ID of the layout of the entry's\ndefault display page.\n\n`height`: this can be set to `0`.\n\n`width`: this can be set to `0`.\n\n`priority`: specifies how the entity is ranked among peer entity instances.\nLow numbers take priority over higher numbers.\n\nThe following code from Liferay's Wiki application's\n[`WikiPageLocalServiceImpl`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/wiki/wiki-service/src/main/java/com/liferay/wiki/service/impl/WikiPageLocalServiceImpl.java)\nJava class demonstrates invoking the `updateEntry` method on the wiki page\nentity called `WikiPage`. In your `add-` method, you could invoke `updateEntry`\nafter adding your entity's resources. Likewise, in your `update-` method, you\ncould invoke `updateEntry` after calling the `super.update-` method. The code\nbelow is called in the `WikiPageLocalServiceImpl` class's `updateStatus(...)`\nmethod.\n\n```java\nAssetEntry assetEntry = assetEntryLocalService.updateEntry(\n    userId, page.getGroupId(), page.getCreateDate(),\n    page.getModifiedDate(), WikiPage.class.getName(),\n    page.getResourcePrimKey(), page.getUuid(), 0,\n    assetCategoryIds, assetTagNames, true, true, null, null,\n    page.getCreateDate(), null, ContentTypes.TEXT_HTML,\n    page.getTitle(), null, null, null, null, 0, 0, null);\n\nIndexer<JournalArticle> indexer = IndexerRegistryUtil.nullSafeGetIndexer(\n    WikiPage.class);\n\nindexer.reindex(page);\n```\n\nImmediately after invoking the `updateEntry` method, you must update the\nrespective asset and index the entity instance. The above code calls the indexer\nto index (or re-index, if updating) the entity. That's all there is to it.\n\n| **Tip:** The current user's ID and the scope group ID are commonly made\n| available in service context parameters. If the service context you use contains\n| them, then you can access them in calls like these:\n| \n|  long userId = serviceContext.getUserId();\n|  long groupId = serviceContext.getScopeGroupId();\n\nNext, you'll learn what's needed to delete an entity that's associated with an\nasset. \n\n## Deleting Assets\n\nWhen deleting your entities, you should delete the associated assets and indexes\nat the same time. This cleans up stored asset and index information, which keeps\nthe Asset Publisher from showing information for the entities you've deleted.\n\nIn your `-LocalServiceImpl` Java class, open your `delete-` method. After the\ncode that deletes the entity's resource, delete the entity instance's asset\nentry and index. \n\nHere's some code which deletes an asset entry and an index associated with a\nportlet's entity. \n\n```java\nassetEntryLocalService.deleteEntry(\n    ENTITY.class.getName(), assetEntry.getEntityId());\n\nIndexer<ENTITY> indexer = IndexerRegistryUtil.nullSafeGetIndexer(ENTITY.class);\nindexer.delete(assetEntry);\n```\n\nIn your `-LocalServiceImpl` class, you can write similar code. Replace the\n*ENTITY* class name and variable with your entity's name.\n\n| **Important:** For Liferay's Asset Publisher application to show your entity,\n| the entity must have an Asset Renderer.\n| \n| Note also that an Asset Renderer is how you show a user the components of your\n| entity in the Asset Publisher. On deploying your portlet with asset, indexer,\n| and asset rendering implementations in place, an Asset Publisher can show your\n| custom entities!\n\n![Figure 1: It can be useful to show custom entities, like this wiki page entity, in a JSP or in an Asset Publisher.](../../images/basic-asset-in-asset-publisher.png)\n\nGreat! Now you know how to add, update, and delete assets in your apps!\n"
  },
  {
    "path": "en/developer/frameworks/articles/assets/03-rendering-an-asset/01-creating-asset-renderer-intro.markdown",
    "content": "---\nheader-id: creating-an-asset-renderer\n---\n\n# Creating an Asset Renderer\n\n[TOC levels=1-4]\n\nIn this tutorial, you'll learn how to create an `Asset Renderer` and associate \nyour JSP templates with it, along with configuring several other options by \nstudying a Liferay asset: Blogs.\n\nThe Blogs application offers many different ways to access and render a blogs \nasset. You'll learn how a blogs asset provides an edit feature, comment section,\noriginal context viewing (i.e., viewing an asset from the Blogs application),\nworkflow, and more. You'll also learn how it uses JSP templates to display\nvarious blog views. The Blogs application is an extensive example of how an\nasset renderer can be customized to fit your needs. \n\nTo learn how an asset renderer is created, you'll create the pre-existing\n[`BlogsEntryAssetRenderer`](@app-ref@/collaboration/latest/javadocs/com/liferay/blogs/web/asset/BlogsEntryAssetRenderer.html)\nclass, which configures the asset renderer framework for the Blogs application.\n\n1.  Create a new package in your existing project for your asset-related\n    classes. For instance, the `BlogsEntryAssetRenderer` class resides in the\n    `com.liferay.blogs.web` module's `com.liferay.blogs.web.asset` package.\n\n2.  Create your `-AssetEntry` class for your application in the new `-.asset`\n    package and have it implement the\n    [`AssetEntry`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/asset/kernel/model/AssetEntry.html)\n    interface. Consider the `BlogsEntryAssetRenderer` class as an example:\n\n    ```java\n    public class BlogsEntryAssetRenderer\n        extends BaseJSPAssetRenderer<BlogsEntry> implements TrashRenderer {\n    ```\n\n    The `BlogsEntryAssetRenderer` class extends the\n    [`BaseJSPAssetRenderer`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/asset/kernel/model/BaseJSPAssetRenderer.html),\n    which is an extension class intended for those who plan on using JSP\n    templates to generate their asset's HTML. The `BaseJSPAssetRenderer` class\n    implements the `AssetRenderer` interface. You'll notice the asset renderer\n    also implements the\n    [`TrashRenderer`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/trash/TrashRenderer.html)\n    interface. This is a common practice for many applications, so they can use\n    @product@'s Recycle Bin.\n\n3.  Define the asset renderer class's constructor, which typically sets the\n    asset object to use in the asset renderer class.\n\n    ```java\n    public BlogsEntryAssetRenderer(\n        BlogsEntry entry, ResourceBundleLoader resourceBundleLoader) {\n\n        _entry = entry;\n        _resourceBundleLoader = resourceBundleLoader;\n    }\n    ```\n\n    The `BlogsEntryAssetRenderer` also sets the resource bundle loader, which\n    loads the language keys for a module. You can learn more about the resource\n    bundle loader in the\n    [Overriding Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys)\n    tutorial.\n\n    Also, make sure to define the `_entry` and `_resourceBundleLoader` fields in\n    the class:\n\n    ```java\n    private final BlogsEntry _entry;\n    private final ResourceBundleLoader _resourceBundleLoader;\n    ```\n\n4.  Now that your class declaration and constructor are defined for the blogs\n    asset renderer, you must begin connecting your asset renderer to your\n    asset. The following getter methods accomplish this:\n\n    ```java\n    @Override\n    public BlogsEntry getAssetObject() {\n        return _entry;\n    }\n\n    @Override\n    public String getClassName() {\n        return BlogsEntry.class.getName();\n    }\n\n    @Override\n    public long getClassPK() {\n        return _entry.getEntryId();\n    }\n\n    @Override\n    public long getGroupId() {\n        return _entry.getGroupId();\n    }\n\n    @Override\n    public String getType() {\n        return BlogsEntryAssetRendererFactory.TYPE;\n    }\n\n    @Override\n    public String getUuid() {\n        return _entry.getUuid();\n    }\n    ```\n\n    The `getAssetObject()` method sets the `BlogsEntry` that was set in the\n    constructor as your asset to track. Likewise, the `getType()` method\n    references the blogs asset renderer factory for the type of asset your\n    asset renderer renders. Of course, the asset renderer type is `blog`, which\n    you'll set in the factory later. \n\n5.  Your asset renderer must link to the portlet that owns the entity. In the\n    case of a blogs asset, its portlet ID should be linked to the Blogs\n    application.\n\n    ```java\n    @Override\n    public String getPortletId() {\n        AssetRendererFactory<BlogsEntry> assetRendererFactory =\n            getAssetRendererFactory();\n\n        return assetRendererFactory.getPortletId();\n    }\n    ```\n\n    The `getPortletId()` method instantiates an asset renderer factory for a\n    `BlogsEntry` and retrieves the portlet ID for the portlet used to display\n    blogs entries.\n\n6.  If you want to enable workflow for your asset, add the following method\n    similar to what was done for the Blogs application:\n\n    ```java\n    @Override\n    public int getStatus() {\n        return _entry.getStatus();\n    }\n    ```\n\n    This method retrieves the workflow status for the asset.\n\n7.  Another feature many developers want for their asset is comments. This is\n    enabled for the Blogs application with the following method:\n\n    ```java\n    @Override\n    public String getDiscussionPath() {\n        if (PropsValues.BLOGS_ENTRY_COMMENTS_ENABLED) {\n            return \"edit_entry_discussion\";\n        }\n        else {\n            return null;\n        }\n    }\n    ```\n\n    A comments section is an available option if it returns a non-null value. \n    For the comments section to display for your asset, you must enable it in \n    the Asset Publisher's *Options* \n    (![Options](../../../images/icon-options.png)) &rarr;\n    *Configuration* &rarr; *Setup* &rarr; *Display Settings* section.\n\n8.  At a minimum, you should create a title and summary for your asset. Here's\n    how the `BlogsEntryAssetRenderer` does it:\n\n    ```java\n    @Override\n    public String getSummary(\n        PortletRequest portletRequest, PortletResponse portletResponse) {\n\n        int abstractLength = AssetUtil.ASSET_ENTRY_ABSTRACT_LENGTH;\n\n        if (portletRequest != null) {\n            abstractLength = GetterUtil.getInteger(\n                portletRequest.getAttribute(\n                    WebKeys.ASSET_ENTRY_ABSTRACT_LENGTH),\n                AssetUtil.ASSET_ENTRY_ABSTRACT_LENGTH);\n        }\n\n        String summary = _entry.getDescription();\n\n        if (Validator.isNull(summary)) {\n            summary = HtmlUtil.stripHtml(\n                StringUtil.shorten(_entry.getContent(), abstractLength));\n        }\n\n        return summary;\n    }\n\n    @Override\n    public String getTitle(Locale locale) {\n        ResourceBundle resourceBundle =\n            _resourceBundleLoader.loadResourceBundle(\n                LanguageUtil.getLanguageId(locale));\n\n        return BlogsEntryUtil.getDisplayTitle(resourceBundle, _entry);\n    }\n    ```\n\n    These two methods return information about your asset, so the asset \n    publisher can display it. The title and summary can be anything. \n\n    The `getSummary(...)` method for Blogs returns the abstract description for\n    a blog asset. If the abstract description does not exist, the content of the\n    blog is used as an abstract. You'll learn more about abstracts and other\n    content specifications later. \n\n    The `getTitle(...)` method for Blogs uses the resource bundle loader you\n    configured in the constructor to load your module's resource bundle and\n    return the display title for your asset.\n\n9.  If you want to provide a unique URL for your asset, you can specify a URL\n    title. A URL title is the URL used to access your asset directly (e.g.,\n    localhost:8080/-/this-is-my-blog-asset). You can do this by providing the\n    following method:\n\n    ```java\n    @Override\n    public String getUrlTitle() {\n        return _entry.getUrlTitle();\n    }\n    ```\n\n10. Insert the `isPrintable()` method, which enables the Asset Publisher's\n    printing capability for your asset.\n\n    ```java\n    @Override\n    public boolean isPrintable() {\n        return true;\n    }\n    ```\n\n    This displays a Print icon when your asset is displayed in the Asset\n    Publisher. For the icon to appear, you must enable it in the Asset\n    Publisher's *Options* &rarr;\n    *Configuration* &rarr; *Setup* &rarr; *Display Settings* section.\n\n    ![Figure 1: Enable printing in the Asset Publisher to display the Print icon for your asset.](../../../images/asset-publisher-printing.png)\n\n11. If your asset is protected by permissions, you can set permissions for\n    the asset via the asset renderer. See the logic below for an example used \n    in the `BlogsEntryAssetRenderer` class:\n\n    ```java\n    @Override\n    public long getUserId() {\n        return _entry.getUserId();\n    }\n\n    @Override\n    public String getUserName() {\n        return _entry.getUserName();\n    }\n\n    public boolean hasDeletePermission(PermissionChecker permissionChecker) {\n        return BlogsEntryPermission.contains(\n            permissionChecker, _entry, ActionKeys.DELETE);\n    }\n\n    @Override\n    public boolean hasEditPermission(PermissionChecker permissionChecker) {\n        return BlogsEntryPermission.contains(\n            permissionChecker, _entry, ActionKeys.UPDATE);\n    }\n\n    @Override\n    public boolean hasViewPermission(PermissionChecker permissionChecker) {\n        return BlogsEntryPermission.contains(\n            permissionChecker, _entry, ActionKeys.VIEW);\n    }\n    ```\n\n    Before you can check if a user has permission to view your asset, you must\n    use the `getUserId()` and `getUserName()` to retrieve the entry's user ID \n    and username, respectively. Then there are three boolean permission \n    methods that check if the user can view, edit, or delete your blogs \n    entry. These permissions are for specific entity instances. Global\n    permissions for blog entries are implemented in the factory, which you'll\n    do later.\n\nAwesome! You've learned how to set up the blogs asset renderer to\n\n- connect to an asset\n- connect to the asset's portlet\n- use workflow management\n- use a comments section\n- retrieve the asset's title and summary\n- generate the asset's unique URL\n- display a print icon\n- check permissions for the asset\n\nNow you need to create the templates to render the HTML. The \n`BlogsEntryAssetRenderer` is configured to use JSP templates to generate HTML \nfor the Asset Publisher. You'll learn more about how to do this next.\n"
  },
  {
    "path": "en/developer/frameworks/articles/assets/03-rendering-an-asset/02-jsp-templates-asset-renderer.markdown",
    "content": "---\nheader-id: configuring-jsp-templates-for-an-asset-renderer\n---\n\n# Configuring JSP Templates for an Asset Renderer\n\n[TOC levels=1-4]\n\nAn asset can be displayed in several different ways in the Asset Publisher.\nThere are three templates to implement provided by the `AssetRenderer`\ninterface:\n\n- `abstract`\n- `full_content`\n- `preview`\n\nBesides these supported templates, you can also create JSPs for buttons for\ndirect access and manipulation of the asset. For example,\n\n- Edit\n- View\n- View in Context\n\nThe `BlogsEntryAssetRenderer` customizes the `AssetRenderer`'s provided JSP\ntemplates and adds a few other features using JSPs. You'll inspect how the blogs\nasset renderer is put together to satisfy JSP template development requirements.\n\n1.  Add the `getJspPath(...)` method to your asset renderer. This method should\n    return the path to your JSP, which is rendered inside the Asset Publisher.\n    This is how the `BlogsEntryAssetRenderer` uses this method:\n\n    ```java\n    @Override\n    public String getJspPath(HttpServletRequest request, String template) {\n        if (template.equals(TEMPLATE_ABSTRACT) ||\n            template.equals(TEMPLATE_FULL_CONTENT)) {\n\n            return \"/blogs/asset/\" + template + \".jsp\";\n        }\n        else {\n            return null;\n        }\n    }\n    ```\n\n    Blogs assets provide `abstract.jsp` and `full_content.jsp` templates. This\n    means that a blogs asset can render a blog's abstract description or the\n    blog's full content in the Asset Publisher. Those templates are located in\n    the `com.liferay.blogs.web` module's\n    `src/main/resources/META-INF/resources/blogs/asset` folder. You could create\n    a similar folder for your JSP templates used for this method. The other\n    template provided by the `AssetRenderer` interface, `preview.jsp`, is not\n    customized by the blogs asset renderer, so its default template is\n    implemented.\n\n    You must create a link to display the full content of the asset. You'll\n    do this later.\n\n2.  Now that you've added the path to your JSP, you must include that JSP. Since\n    the `BlogsEntryAssetRenderer` class extends the `BaseJSPAssetRenderer`,\n    it already has an `include(...)` method to render a specific JSP. You\n    must override this method to set an attribute in the request to use in\n    the blog's views:\n\n    ```java\n    @Override\n    public boolean include(\n            HttpServletRequest request, HttpServletResponse response,\n            String template)\n        throws Exception {\n\n        request.setAttribute(WebKeys.BLOGS_ENTRY, _entry);\n\n        return super.include(request, response, template);\n    }\n    ```\n\n    The attribute includes the blogs entry object. Adding the blog object this\n    way is not mandatory; you could obtain the blog entry directly from the\n    view. Using the `include(...)` method, however, follows the best practice\n    for MVC portlets.\n\n    ![Figure 1: The abstract and full content views are rendered differently for blogs.](../../../images/blogs-asset-views.png)\n\nTerrific! You've learned how to apply JSPs supported by the Asset Publisher for\nyour asset. That's not all you can do with JSP templates, however! The asset\nrenderer framework provides several other methods that let you render convenient\nbuttons for your asset.\n\n1.  Blogs assets provide an Edit button for editing the asset. Provide this by\n    adding the following method to the `BlogsEntryAssetRenderer` class:\n\n    ```java\n    @Override\n    public PortletURL getURLEdit(\n            LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse)\n        throws Exception {\n\n        Group group = GroupLocalServiceUtil.fetchGroup(_entry.getGroupId());\n\n        PortletURL portletURL = PortalUtil.getControlPanelPortletURL(\n            liferayPortletRequest, group, BlogsPortletKeys.BLOGS, 0, 0,\n            PortletRequest.RENDER_PHASE);\n\n        portletURL.setParameter(\"mvcRenderCommandName\", \"/blogs/edit_entry\");\n        portletURL.setParameter(\"entryId\", String.valueOf(_entry.getEntryId()));\n\n        return portletURL;\n    }\n    ```\n\n    The Asset Publisher loads the blogs asset using the Blogs application. Then\n    the `edit_entry.jsp` template generates the HTML for an editing UI. Once the\n    necessary edits are made to the asset, it can be saved from the Asset\n    Publisher. Pretty cool, right?\n\n2.  You can specify how to view your asset by providing methods similar to the\n    methods outlined below in the `BlogsEntryAssetRenderer` class:\n\n    ```java\n    @Override\n    public String getURLView(\n            LiferayPortletResponse liferayPortletResponse,\n            WindowState windowState)\n        throws Exception {\n\n        AssetRendererFactory<BlogsEntry> assetRendererFactory =\n            getAssetRendererFactory();\n\n        PortletURL portletURL = assetRendererFactory.getURLView(\n            liferayPortletResponse, windowState);\n\n        portletURL.setParameter(\"mvcRenderCommandName\", \"/blogs/view_entry\");\n        portletURL.setParameter(\"entryId\", String.valueOf(_entry.getEntryId()));\n        portletURL.setWindowState(windowState);\n\n        return portletURL.toString();\n    }\n\n    @Override\n    public String getURLViewInContext(\n        LiferayPortletRequest liferayPortletRequest,\n        LiferayPortletResponse liferayPortletResponse,\n        String noSuchEntryRedirect) {\n\n        return getURLViewInContext(\n            liferayPortletRequest, noSuchEntryRedirect, \"/blogs/find_entry\",\n            \"entryId\", _entry.getEntryId());\n    }\n    ```\n\n    The `getURLView(...)` method generates a URL that displays the full content\n    of the asset in the Asset Publisher. This is assigned to the clickable asset\n    name. The `getURLViewInContext(...)` method provides a similar URL assigned\n    to the asset name, but the URL redirects to the original context of the\n    asset (e.g., viewing a blogs asset in the Blogs application). Deciding which\n    view to render is configurable by navigating to the Asset Publisher's \n    *Options* &rarr; *Configuration*\n    &rarr; *Setup* &rarr; *Display Settings* section and choosing between *Show\n    Full Content* and *View in Context* for the Asset Link Behavior drop-down\n    menu.\n\nThe Blogs application provides `abstract` and `full_content` JSP templates that\noverride the ones provided by the `AssetRenderer` interface. The third template,\n`preview`, could also be customized. You can view the default `preview.jsp`\ntemplate rendered in the *Add* &rarr; *Content* menu.\n\n![Figure 2: The `preview` template displays a preview of the asset in the Content section of the Add menu.](../../../images/preview-template-asset-renderer.png)\n\nYou've learned all about implementing the `AssetRenderer`'s provided templates\nand customizing them to fit your needs. Next, you'll put your asset renderer\ninto action by creating a factory.\n"
  },
  {
    "path": "en/developer/frameworks/articles/assets/03-rendering-an-asset/03-creating-factory-asset-renderer.markdown",
    "content": "---\nheader-id: creating-a-factory-for-the-asset-renderer\n---\n\n# Creating a Factory for the Asset Renderer\n\n[TOC levels=1-4]\n\nYou've successfully created an asset renderer, but you must create a factory\nclass to generate asset renderers for each asset instance. For example, the\nblogs asset renderer factory instantiates `BlogsEntryAssetRenderer` for each\nblogs asset displayed in an Asset Publisher. \n\nYou'll continue the blogs asset renderer example by creating the blogs asset\nrenderer factory.\n\n1.  Create an `-AssetRenderFactory` class in the same folder as its asset\n    renderer class. For blogs, the\n    [`BlogsEntryAssetRendererFactory`](@app-ref@/collaboration/latest/javadocs/com/liferay/blogs/web/asset/BlogsEntryAssetRendererFactory.html)\n    class resides in the `com.liferay.blogs.web` module's\n    `com.liferay.blogs.web.asset` package. The factory class should extend the\n    `BaseAssetRendererFactory` class and the asset type should be specified as\n    its parameter. You can see how this was done in the\n    `BlogsEntryAssetRendererFactory` class below\n\n    ```java\n    public class BlogsEntryAssetRendererFactory\n        extends BaseAssetRendererFactory<BlogsEntry> {\n    ```\n\n2.  Create an `@Component` annotation section above the class declaration. This\n    annotation is responsible for registering the factory instance for the\n    asset.\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\"javax.portlet.name=\" + BlogsPortletKeys.BLOGS},\n        service = AssetRendererFactory.class\n    )\n    public class BlogsEntryAssetRendererFactory\n        extends BaseAssetRendererFactory<BlogsEntry> {\n    ```\n\n    There are a few annotation elements you should set:\n\n    - The `immediate` element directs the factory to start in @product@ when\n      its module starts.\n    - The `property` element sets the portlet that is associated with the asset.\n      The Blogs portlet is specified, since this is the Blogs asset renderer\n      factory. \n    - The `service` element should point to the `AssetRendererFactory.class`\n      interface.\n\n    | **Note:** If you're using a Java EE portlet WAR, you must register the asset\n    | renderer factory in the portlet's `liferay-portlet.xml` file. In an \n    | OSGi-based Liferay MVC portlet, the registration\n    | process is completed automatically by OSGi using the `@Component`\n    | annotation.\n\n3.  Create a constructor for the factory class that presets private attributes\n    of the factory.\n\n    ```java\n    public BlogsEntryAssetRendererFactory() {\n        setClassName(BlogsEntry.class.getName());\n        setLinkable(true);\n        setPortletId(BlogsPortletKeys.BLOGS);\n        setSearchable(true);\n    }\n    ```\n\n    `linkable`: other assets can select blogs assets as their related\n    assets.\n\n    `searchable`: blogs can be found when searching for assets.\n\n    Setting the class name and portlet ID links the asset renderer factory to\n    the entity.\n\n4.  Create the asset renderer for your asset. This is done by calling its\n    constructor.\n\n    ```java\n    @Override\n    public AssetRenderer<BlogsEntry> getAssetRenderer(long classPK, int type)\n        throws PortalException {\n\n        BlogsEntry entry = _blogsEntryLocalService.getEntry(classPK);\n\n        BlogsEntryAssetRenderer blogsEntryAssetRenderer =\n            new BlogsEntryAssetRenderer(entry, _resourceBundleLoader);\n\n        blogsEntryAssetRenderer.setAssetRendererType(type);\n        blogsEntryAssetRenderer.setServletContext(_servletContext);\n\n        return blogsEntryAssetRenderer;\n    }\n    ```\n\n    For blogs, the asset is retrieved by calling the Blogs application's local\n    service. Then the asset renderer is instantiated using the blogs asset and\n    resource bundle loader. Next, the type and servlet context is\n    set for the asset renderer. Finally, the configured asset renderer is\n    returned.\n\n    There are a few variables in the `getAssetRenderer(...)` method you must\n    create. You'll set those variables and learn what they're doing next.\n\n    a. You must get the entry by calling the Blogs application's local service.\n       You can instantiate this service by creating a private field and setting\n       it using a setter method:\n\n    ```java\n    @Reference(unbind = \"-\")\n    protected void setBlogsEntryLocalService(\n        BlogsEntryLocalService blogsEntryLocalService) {\n\n        _blogsEntryLocalService = blogsEntryLocalService;\n    }\n\n    private BlogsEntryLocalService _blogsEntryLocalService;\n    ```\n\n    The setter method is annotated with the `@Reference` tag. \n    \n    <!--Uncomment once article is available\n    Visit the\n    Invoking Local Services\n    tutorial for more information.\n    -->\n\n    b. You must specify the resource bundle loader since it was specified in\n       the `BlogsEntryAssetRenderer`'s constructor:\n\n    ```java\n    @Reference(\n        target = \"(bundle.symbolic.name=com.liferay.blogs.web)\", unbind = \"-\"\n    )\n    public void setResourceBundleLoader(\n        ResourceBundleLoader resourceBundleLoader) {\n\n        _resourceBundleLoader = resourceBundleLoader;\n    }\n\n    private ResourceBundleLoader _resourceBundleLoader;\n    ```\n\n    Make sure the `osgi.web.symbolicname` in the `target` property of the\n    `@Reference` annotation is set to the same value as the\n    `Bundle-SymbolicName` defined in the `bnd.bnd` file of the module the\n    factory resides in.\n\n    c. The asset renderer `type` integer is set for the asset renderer, but why\n       an integer? @product@ needs to differentiate when it should display the\n       latest *approved* version of the asset, or the latest version, even if\n       it's unapproved (e.g., unapproved versions would be displayed for\n       reviewers of the asset in a workflow). For these situations, the asset\n       renderer factory should receive either\n\n       - `0` for the latest version of the asset\n       - `1` for the latest approved version of the asset\n\n    d. Since the Blogs application provides its own JSPs, it must pass a\n    reference of the servlet context to the asset renderer. This is always\n    required when using custom JSPs in an asset renderer:\n\n    ```java\n    @Reference(\n        target = \"(osgi.web.symbolicname=com.liferay.blogs.web)\", unbind = \"-\"\n    )\n    public void setServletContext(ServletContext servletContext) {\n        _servletContext = servletContext;\n    }\n\n    private ServletContext _servletContext;\n    ```\n\n5.  Set the type of asset that the asset factory associates with and provide\n    a getter method to retrieve that type. Also, provide another getter to\n    retrieve the blogs entry class name, which is required:\n\n    ```java\n    public static final String TYPE = \"blog\";\n\n    @Override\n    public String getType() {\n        return TYPE;\n    }\n\n    @Override\n    public String getClassName() {\n        return BlogsEntry.class.getName();\n    }\n    ```\n\n6.  Set the Lexicon icon for the asset:\n\n    ```java\n    @Override\n    public String getIconCssClass() {\n        return \"blogs\";\n    }\n    ```\n\n    You can find a list of all available Lexicon icons \n    [here](https://liferay.design/lexicon/core-components/icons/).\n\n7.  Add methods that generate URLs to add and view the asset.\n\n    ```java\n    @Override\n    public PortletURL getURLAdd(\n        LiferayPortletRequest liferayPortletRequest,\n        LiferayPortletResponse liferayPortletResponse, long classTypeId) {\n\n        PortletURL portletURL = PortalUtil.getControlPanelPortletURL(\n            liferayPortletRequest, getGroup(liferayPortletRequest),\n            BlogsPortletKeys.BLOGS, 0, 0, PortletRequest.RENDER_PHASE);\n\n        portletURL.setParameter(\"mvcRenderCommandName\", \"/blogs/edit_entry\");\n\n        return portletURL;\n    }\n\n    @Override\n    public PortletURL getURLView(\n        LiferayPortletResponse liferayPortletResponse,\n        WindowState windowState) {\n\n        LiferayPortletURL liferayPortletURL =\n            liferayPortletResponse.createLiferayPortletURL(\n                BlogsPortletKeys.BLOGS, PortletRequest.RENDER_PHASE);\n\n        try {\n            liferayPortletURL.setWindowState(windowState);\n        }\n        catch (WindowStateException wse) {\n        }\n\n        return liferayPortletURL;\n    }\n    ```\n\n    If you're paying close attention, you may have noticed the `getURLView(...)`\n    method was also implemented in the `BlogsEntryAssetRenderer` class. The\n    asset renderer's `getURLView(...)` method creates a URL for the specific\n    asset instance, whereas the factory uses the method to create a generic URL\n    that only points to the application managing the assets (e.g., Blogs\n    application).\n\n11. Set the global permissions for all blogs assets:\n\n    ```java\n    @Override\n    public boolean hasAddPermission(\n            PermissionChecker permissionChecker, long groupId, long classTypeId)\n        throws Exception {\n\n        return BlogsPermission.contains(\n            permissionChecker, groupId, ActionKeys.ADD_ENTRY);\n    }\n\n    @Override\n    public boolean hasPermission(\n            PermissionChecker permissionChecker, long classPK, String actionId)\n        throws Exception {\n\n        return BlogsEntryPermission.contains(\n            permissionChecker, classPK, actionId);\n    }\n    ```\n\nGreat! You've finished creating the Blogs application's asset renderer factory!\nNow you have the knowledge to implement an asset renderer and produce an asset\nrenderer for each asset instance using a factory!\n"
  },
  {
    "path": "en/developer/frameworks/articles/assets/04-entering-and-displaying-tags-and-categories.markdown",
    "content": "---\nheader-id: implementing-asset-categorization-and-tagging\n---\n\n# Implementing Asset Categorization and Tagging\n\n[TOC levels=1-4]\n\nNow it's time to get started with Tags and Categories.\n\n## Adding Tags and Categories\n\nYou can use the following tags in the JSPs you provide for adding/editing custom\nentities. Here's what the tags look like in the\n[edit_entry.jsp](https://github.com/liferay/liferay-portal/blob/master/modules/apps/blogs/blogs-web/src/main/resources/META-INF/resources/blogs/edit_entry.jsp) \nfor the Blogs portlet: \n\n```markup\n<liferay-ui:asset-categories-error />\n<liferay-ui:asset-tags-error />\n...\n<aui:fieldset-group markupView=\"lexicon\">\n    ...\n    <aui:fieldset collapsed=\"<%= true %>\" collapsible=\"<%= true %>\" label=\"categorization\">\n        <liferay-asset:asset-categories-selector name=\"categories\" type=\"assetCategories\" />\n\n        <liferay-asset:asset-tags-selector name=\"tags\" type=\"assetTags\" />\n    </aui:fieldset>\n    ...\n</aui:fieldset-group>\n```\n\nThe `liferay-asset:asset-categories-selector` and \n`liferay-asset:asset-tags-selector` tags generate form controls that let users \nbrowse/select categories for the entity, browse/select tags, and/or create new \ntags to associate with the entity. \n\nThe `liferay-ui:asset-categories-error` and `liferay-ui:asset-tags-error` tags\nshow messages for errors occurring during the asset category or tag\ninput process. The `aui:fieldset` tag uses a container that lets users hide\nor show the category and tag input options.\n\nFor styling purposes, the `aui:fieldset-group` tag is given the `lexicon`\nmarkup view.\n\n## Displaying Tags and Categories\n\nTags and categories should be displayed with the content of the asset. Here's\nhow to display the tags and categories: \n\n```markup\n<liferay-asset:asset-categories-available\n    className=\"<%= [AssetEntry].class.getName() %>\"\n    classPK=\"<%= entry.getEntryId() %>\"\n>\n    <div class=\"entry-categories\">\n        <liferay-asset:asset-categories-summary\n            className=\"<%= [AssetEntry].class.getName() %>\"\n            classPK=\"<%= entry.getEntryId() %>\"\n            portletURL=\"<%= renderResponse.createRenderURL() %>\"\n        />\n    </div>\n</liferay-asset:asset-categories-available>\n\n...\n\n<liferay-asset:asset-tags-available\n    className=\"<%= [AssetEntry].class.getName() %>\"\n    classPK=\"<%= entry.getEntryId() %>\"\n>\n    <div class=\"entry-tags\">\n        <liferay-asset:asset-tags-summary\n            className=\"<%= [AssetEntry].class.getName() %>\"\n            classPK=\"<%= entry.getEntryId() %>\"\n            portletURL=\"<%= renderResponse.createRenderURL() %>\"\n        />\n    </div>\n</liferay-asset:asset-tags-available>\n```\n\nThe `portletURL` parameter is used for both tags and categories. Each tag that\nuses this parameter becomes a link containing the `portletURL` *and* `tag` or\n`categoryId` parameter value. To implement this, you must implement the look-up\nfunctionality in your portlet code. Do this by reading the values of those two\nparameters and using `AssetEntryService` to query the database for entries\nbased on the specified tag or category. \n\nDeploy your changes and add/edit a custom entity in your UI. Your form shows the\ncategorization and tag input options in a panel that the user can hide/show. \n\nGreat! Now you know how to make category and tag input options available to your\napp's content authors. \n"
  },
  {
    "path": "en/developer/frameworks/articles/assets/05-relating-assets.markdown",
    "content": "---\nheader-id: relating-assets\n---\n\n# Relating Assets\n\n[TOC levels=1-4]\n\nAfter you complete\n[Adding, Updating, and Deleting Assets](/docs/frameworks/7-2/-/knowledge_base/frameworks/adding-updating-and-deleting-assets)\nfor your application you can go ahead and begin relating your assets!\n\n## Relating Assets in the Service Layer \n\nFirst, you must make some modifications to your portlet's service layer. You\nmust implement persisting your entity's asset relationships. \n\n1.  In your portlet's `service.xml`, put the following line of code below any\n    finder method elements and then run Service Builder:\n    \n    <!-- Add link back for 'run service builder' once article is available-->\n\n    ```xml\n    <reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetLink\" />\n    ```\n\n2.  Modify the `add-`, `delete-`, and `update-` methods in your\n    `-LocalServiceImpl` to persist the asset relationships. You'll use your\n    `-LocalServiceImpl`'s `assetLinkLocalService` instance variable to execute\n    persistence actions. \n\n    For example, consider the Wiki application. When you update wiki assets and\n    statuses, both methods utilize the `updateLinks` via your instance variable\n    `assetLinkLocalService`. Here's the `updateLinks` invocation in the Wiki\n    application's `WikiPageLocalServiceImpl.updateStatus(...)` method:\n\n    ```java\n    assetLinkLocalService.updateLinks(\n        userId, assetEntry.getEntryId(), assetLinkEntryIds,\n        AssetLinkConstants.TYPE_RELATED);\n    ```\n\n    To call the `updateLinks` method, you must pass in the current user's ID, the\n    asset entry's ID, the asset link entries' IDs, and the link type. Invoke\n    this method after creating the asset entry. If you assign to an\n    `AssetEntry` variable (e.g., one called `assetEntry`) the value returned\n    from invoking `assetEntryLocalService.updateEntry`, you can get the asset\n    entry's ID for updating its asset links. Lastly, in order to specify the\n    link type parameter, make sure to import\n    `com.liferay.portlet.asset.model.AssetLinkConstants`. \n\n3.  In your `-LocalServiceImpl` class' `delete-` method, you must delete the\n    asset's relationships before deleting the asset. For example, you could\n    delete your existing asset link relationships by using the following code:\n\n    ```java\n    AssetEntry assetEntry = assetEntryLocalService.fetchEntry(\n        ENTITY.class.getName(), ENTITYId);\n\n    assetLinkLocalService.deleteLinks(assetEntry.getEntryId());\n    ```\n\nMake sure to replace the *ENTITY* place holders for your custom `-delete`\nmethod.\n\nSuper! Now your portlet's service layer can handle related assets. Even so,\nthere's still nothing in your portlet's UI that lets your users relate assets.\nYou'll take care of that in the next step.\n\n## Relating Assets in the UI\n\nThe UI for linking assets should be in the JSP where users create and edit your\nentity. This way only content creators can relate other assets to the entity.\nRelated assets are implemented in the JSP by using the Liferay UI tag\n`liferay-ui:input-asset-links` inside a collapsible panel. This code is\nplaced inside the `aui:fieldset` tags of the JSP. \n\n1.  Add the `liferay-asset:input-asset-links` tag to your form. Here's how it's\n    added in the Blogs application: \n\n    ```markup\n    <aui:fieldset collapsed=\"<%= true %>\" collapsible=\"<%= true %>\" label=\"related-assets\">\n            <liferay-asset:input-asset-links\n                className=\"<%= [AssetEntry].class.getName() %>\"\n                classPK=\"<%= entryId %>\"\n            />\n    </aui:fieldset>\n    ```\n\n    The following screenshot shows the Related Assets menu for an application. Note\n    that it is contained in a collapsible panel titled Related Assets.\n\n    ![Figure 1: Your portlet's entity is now available in the Related Assets *Select* menu.](../../images/related-assets-select-menu.png)\n\n2.  Unfortunately, the Related Assets menu shows your entity's fully qualified\n    class name. To replace it with a simplified name for your entity, add\n    a language key with the fully qualified class name for the key\n    and the name you want for the value. Put the language key in file\n    `docroot/WEB-INF/src/content/Language.properties` in your portlet. You can\n    refer to the \n    [Overriding Language Keys](/docs/frameworks/7-2/-/knowledge_base/frameworks/overriding-language-keys)\n    tutorial for more documentation on using language properties.\n\n    Upon redeploying your portlet, the value you assigned to the fully qualified\n    class name in your `Language.properties` file shows in the Related Assets menu. \n\nAwesome! Now content creators and editors can relate the assets of your\napplication. The next thing you need to do is reveal any such related assets to\nthe rest of your application's users. After all, you don't want to give everyone\nedit access just so they can view related assets!\n\n## Showing Related Assets\n\nYou can show related assets in your application's view of that entity or, if\nyou've implemented asset rendering for your custom entity, you can show related\nassets in the full content view of your entity for users to view in an Asset\nPublisher portlet.\n\n1.  You must get the `AssetEntry` object associated with your entity: \n\n    ```markup\n    <%\n    long insultId = ParamUtil.getLong(renderRequest, \"insultId\");\n    Insult ins = InsultLocalServiceUtil.getInsult(insultId);\n    AssetEntry assetEntry = AssetEntryLocalServiceUtil.getEntry(Insult.class.getName(), ins.getInsultId());\n    %>\n    ```\n\n2.  Use the `liferay-asset:asset-links` tag to show the entity's related assets.\n    For this tag, you retrieve the `assetEntryId` from the `assetEntry` object, \n    retrieve your asset's `className`, and get the entity's primary key \n    (`classPK`) from the specific `entry`. The tag then retrieves any other \n    assets linked to your asset.\n\n\n    ```markup\n    <liferay-asset:asset-links\n        assetEntryId=\"<%= (assetEntry != null) ? assetEntry.getEntryId() : 0 %>\"\n        className=\"<%= [myAssetEntry].class.getName() %>\"\n        classPK=\"<%= entry.getEntryId() %>\"\n    />\n    ```\n\nGreat! Now you have the JSP that lets your users view related assets. Related\nassets, if you've created any yet, should be visible near the bottom of the\npage.\n\nExcellent! Now you know how to implement related assets in your apps.\n"
  },
  {
    "path": "en/developer/frameworks/articles/assets/06-implementing-asset-priority.markdown",
    "content": "---\nheader-id: implementing-asset-priority\n---\n\n# Implementing Asset Priority\n\n[TOC levels=1-4]\n\nThis asset priority field isn't enabled when you create an asset. You must \nmanually add support for it. You'll learn how below.\n\n## Add the Priority Field to Your JSP\n\nIn the JSP for adding and editing your asset, add the following input field \nthat lets users set the asset's priority. This example also validates the input \nto make sure the value the user sets is a number higher than zero: \n\n```markup\n<aui:input label=\"priority\" name=\"assetPriority\" type=\"text\" value=\"<%= priority %>\">\n    <aui:validator name=\"number\" />\n\n    <aui:validator name=\"min\">[0]</aui:validator>\n</aui:input>\n```\n\nThat's it for the view layer! Now when users create or edit your asset, they can\nenter its priority. Next, you'll learn how to use that value in your service\nlayer. \n\n## Using the Priority Value in Your Service Layer\n\nTo make the priority value functional, you must retrieve it from the view and\nadd it to the asset in your database. The priority value is automatically\navailable in your service layer via the `ServiceContext` variable\n`serviceContext`. Retrieve it with `serviceContext.getAssetPriority()`, and then\npass it as the last argument to the `assetEntryLocalService.updateEntry` call in\nyour `-LocalServiceImpl`. You can see an example of this in \n[the `BlogsEntryLocalServiceImpl` class](https://github.com/liferay/liferay-portal/blob/master/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/service/impl/BlogsEntryLocalServiceImpl.java)\nof @product@'s Blogs app. The `updateAsset` method takes a `priority` argument,\nwhich it passes as the last argument to its \n`assetEntryLocalService.updateEntry` \ncall: \n\n```java\n@Override\npublic void updateAsset(\n        long userId, BlogsEntry entry, long[] assetCategoryIds,\n        String[] assetTagNames, long[] assetLinkEntryIds, Double priority)\n    throws PortalException {\n\n    ...\n\n    AssetEntry assetEntry = assetEntryLocalService.updateEntry(\n        userId, entry.getGroupId(), entry.getCreateDate(),\n        entry.getModifiedDate(), BlogsEntry.class.getName(),\n        entry.getEntryId(), entry.getUuid(), 0, assetCategoryIds,\n        assetTagNames, true, visible, null, null, null, null,\n        ContentTypes.TEXT_HTML, entry.getTitle(), entry.getDescription(),\n        summary, null, null, 0, 0, priority);\n\n    ...\n}\n```\n\nThe `BlogsEntryLocalServiceImpl` class calls this `updateAsset` method when \nadding or updating a blog entry. Note that `serviceContext.getAssetPriority()` \nretrieves the priority: \n\n```java\nupdateAsset(\n        userId, entry, serviceContext.getAssetCategoryIds(),\n        serviceContext.getAssetTagNames(),\n        serviceContext.getAssetLinkEntryIds(),\n        serviceContext.getAssetPriority());\n```\n\nSweet! Now you know how to enable priorities for your app's assets. \n"
  },
  {
    "path": "en/developer/frameworks/articles/back-end-frameworks/01-intro.markdown",
    "content": "---\nheader-id: back-end-frameworks\n---\n\n# Back-end Frameworks\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/developing-applications/core-frameworks.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay's powerful back-end frameworks provide essential services behind the\nscenes. Here are some of the frameworks: \n\n-   [Portlet Providers](#portlet-providers)\n-   [Data Scopes](#data-scopes)\n-   [Message Bus](#message-bus)\n\nYou can use these frameworks to provide important functionality to your\napplications. \n\n## Portlet Providers\n\nSome apps perform the same operations on different entity types. For example, \nthe Asset Publisher lets users browse, add, preview, and view various entities \nas assets including documents, web content, blogs, and more. The entities vary, \nbut the operations and surrounding business logic stay the same. Apps like the \nAsset Publisher rely on the Portlet Providers framework to fetch portlets to \noperate on the entities. In this way, the framework lets you focus on entity \noperations and frees you from concern about portlets that carry out those \noperations. \n\n### Portlet Provider Classes\n\nPortlet Provider classes are components that implement the \n[`PortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProvider.html) \ninterface, and are associated with an entity type. Once you've registered a \nPortlet Provider, you can invoke the\n[`PortletProviderUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProviderUtil.html) \nclass to retrieve the portlet ID or portlet URL from that Portlet Provider. \n\nAs an example, examine the \n[`WikiEditPortletProvider`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/wiki/wiki-web/src/main/java/com/liferay/wiki/web/internal/portlet/WikiEditPortletProvider.java)\nclass: \n\n```java\n@Component(\n    immediate = true,\n    property = {\n        \"model.class.name=com.liferay.wiki.model.WikiPage\",\n        \"service.ranking:Integer=100\"\n    },\n    service = EditPortletProvider.class\n)\npublic class WikiEditPortletProvider\n    extends BasePortletProvider implements EditPortletProvider {\n\n    @Override\n    public String getPortletName() {\n        return WikiPortletKeys.WIKI;\n    }\n\n}\n```\n\n`WikiEditPortletProvider` extends \n[`BasePortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BasePortletProvider.html), \ninheriting its `getPortletURL` methods. `WikiEditPortletProvider` must, however, \nimplement `PortletProvider`'s `getPortletName` method, which returns the \nportlet's name `WikiPortletKeys.WIKI`. \n\n| **Note:** If you're creating a Portlet Provider for one of Liferay's portlets, \n| your `getPortletName` method should return the portlet name from that \n| portlet's `*PortletKeys` class, if such a class exists. \n\nThe `@Component` annotation for `WikiEditPortletProvider` specifies these \nelements and properties: \n\n-   `immediate = true` activates the component immediately upon installation.\n-   `\"model.class.name=com.liferay.wiki.model.WikiPage\"` specifies the entity \n    type the portlet operates on. \n-   `\"service.ranking:Integer=100\"` sets the component's rank to `100`, \n    prioritizing it above all Portlet Providers that specify the same\n    `model.class.name` value but have a lower rank. \n-   `service = EditPortletProvider.class` reflects the subinterface \n    `PortletProvider` class this class implements \n    ([`EditPortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/EditPortletProvider.html)). \n\nFor step-by-step instructions on creating a Portlet Provider class, see \n[Creating Portlet Providers](/docs/7-2/frameworks/-/knowledge_base/f/creating-portlet-providers). \nFor instructions on using Portlet Providers to retrieve a portlet, see \n[Retrieving Portlets](/docs/7-2/frameworks/-/knowledge_base/f/retrieving-portlets). \n\n## Data Scopes\n\nApps can restrict their data to specific *scopes*. Scopes provide a context for \nthe application's data. \n\n**Global:** One data set throughout a portal instance. \n\n**Site:** One data set for each Site. \n\n**Page:** One data set for each Page on a Site. \n\nFor example, a Site-scoped app has one set of data on one Site and a completely\ndifferent set of data for another Site. For a detailed explanation of scopes,\nsee the user guide article \n[Widget Scope](/docs/7-2/user/-/knowledge_base/u/widget-scope). \nTo give your applications scope, you must manually add support for it. For \ninstructions on this, see \n[Enabling and Accessing Data Scopes](/docs/7-2/frameworks/-/knowledge_base/f/enabling-and-accessing-data-scopes). \n\n### Accessing the Site Scope Across Apps\n\nThere may be times when you must access a different app's Site-scoped data from \nyour app that is scoped to a page or the portal. For example, web content \narticles can be created in the page, Site, or portal scope. \n[Structures and Templates](/docs/7-2/user/-/knowledge_base/u/designing-uniform-content) \nfor such articles, however, exist only in the Site scope. Other techniques \nreturn your app's scope, which might not be the Site scope. What a pickle! Never\nfear, the \n[`ThemeDisplay`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/theme/ThemeDisplay.html) \nmethod `getSiteGroupId()` is here! This method always gets the Site scope, no \nmatter your app's current scope. For an example of using this method, see \n[Enabling and Accessing Data Scopes](/docs/7-2/frameworks/-/knowledge_base/f/enabling-and-accessing-data-scopes). \n\n## Message Bus\n\nIf you must ever do data processing outside the scope of the web's\nrequest/response, look no further than the Message Bus. It's conceptually\nsimilar to Java Messaging Service (JMS) Topics, but sacrifices transactional,\nreliable delivery capabilities, making it much lighter-weight. @product@ uses\nMessage Bus in many places: \n\n- Auditing\n- Search engine integration\n- Email subscriptions\n- Monitoring\n- Document Library processing\n- Background tasks\n- Cluster-wide request execution\n- Clustered cache replication\n\nYou can use it too! Here are some of Message Bus's most important features:\n\n-   publish/subscribe messaging \n-   request queuing and throttling\n-   flow control\n-   multi-thread message processing\n\nThere are also tools, such as the Java SE's JConsole, that can monitor Message \nBus activities. \n\n![Figure 1: JConsole shows statistics on Message Bus messages sent, messages pending, and more.](../../images/message-bus-jconsole.png)\n"
  },
  {
    "path": "en/developer/frameworks/articles/back-end-frameworks/02-creating-portlet-providers.markdown",
    "content": "---\nheader-id: creating-portlet-providers\n---\n\n# Creating Portlet Providers\n\n[TOC levels=1-4]\n\nFollow these steps to create your own \n[Portlet Provider](/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks#portlet-providers): \n\n1.  [Create an OSGi module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project).\n\n2.  Create a `PortletProvider` class in your module. Use the recommended class \n    naming convention:\n\n    `[Entity] + [Action] + PortletProvider`\n\n    For example, here's a Portlet Provider class for viewing a `LanguageEntry`: \n\n    `LanguageEntryViewPortletProvider`\n\n3.  Extend \n    [`BasePortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BasePortletProvider.html) \n    if you want to use its `getPortletURL` method implementations. \n \n4.  Implement one or more \n    [`PortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProvider.html) \n    subinterfaces that match your action(s): \n\n    -   [`AddPortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/AddPortletProvider.html)\n    -   [`BrowsePortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BrowsePortletProvider.html)\n    -   [`EditPortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/EditPortletProvider.html)\n    -   [`ManagePortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/ManagePortletProvider.html)\n    -   [`PreviewPortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PreviewPortletProvider.html)\n    -   [`ViewPortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/ViewPortletProvider.html)\n\n3.  Make the class an OSGi Component by adding an annotation like this one: \n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"model.class.name=CLASS_NAME\",\n            \"service.ranking:Integer=10\"\n        },\n        service = {INTERFACE_1.class, ...}\n    )\n    ```\n\n    The `immediate = true` element specifies that the component should be\n    activated immediately upon installation.\n\n    Assign to the `model.class.name` property the fully qualified class name of \n    the entity the portlet operates on. Here's an example `model.class.name` \n    property for the `WikiPage` entity: \n\n        \"model.class.name=com.liferay.wiki.model.WikiPage\"\n\n    Assign the `service` element to the `PortletProvider` subinterface(s) you're\n    implementing (e.g., `ViewPortletProvider.class`, \n    `BrowsePortletProvider.class`, etc.). This example sets `service` to \n    `EditPortletProvider.class`: \n\n        service = EditPortletProvider.class\n\n4.  If you're overriding an existing Portlet Provider, rank your Portlet \n    Provider higher by specifying a `service.ranking:Integer` property with a \n    higher integer value: \n\n        property = {\n            ...,\n            \"service.ranking:Integer=10\"\n        }\n\n5.  Implement the provider methods you want. Be sure to implement the \n    `PortletProvider` method `getPortletName`. If you didn't extend \n    `BasePortletProvider`, implement `PortletProvider`'s `getPortletURL` methods \n    too. \n\n6.  [Deploy your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project). \n\nNow your Portlet Provider is available to return the ID and URL of the portlet\nthat provides the desired behaviors. For more information on this, see \n[Retrieving Portlets](/docs/7-2/frameworks/-/knowledge_base/f/retrieving-portlets). \n\n## Related Topics\n\n[Portlet Providers](/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks#portlet-providers)\n\n[Retrieving Portlets](/docs/7-2/frameworks/-/knowledge_base/f/retrieving-portlets)\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/back-end-frameworks/03-retrieving-portlets.markdown",
    "content": "---\nheader-id: retrieving-portlets\n---\n\n# Retrieving Portlets\n\n[TOC levels=1-4]\n\nWhen a \n[Portlet Provider](/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks#portlet-providers) \nexists for an entity, you can use the \n[`PortletProviderUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProviderUtil.html) \nclass to retrieve the ID or URL of the portlet that performs the entity action \nyou want. \n\nThe Portlet Provider framework's\n[`PortletProvider.Action`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProvider.Action.html) \nEnum defines these action types: \n\n-   `ADD`\n-   `BROWSE`\n-   `EDIT`\n-   `MANAGE`\n-   `PREVIEW`\n-   `VIEW`\n\nThe action type and entity type are key parameters in fetching a portlet's ID or \nURL. \n\n## Fetching a Portlet ID\n\nTo get the ID of the portlet that performs an action on an entity, pass that \nentity and action as arguments to the \n[`PortletProviderUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProviderUtil.html) \nmethod `getPortletId`. For example, this call gets the ID of a portlet for \nviewing Recycle Bin entries: \n\n```java\nString portletId = PortletProviderUtil.getPortletId(\n    \"com.liferay.portlet.trash.model.TrashEntry\", \n    PortletProvider.Action.VIEW);\n```\n\nThe `com.liferay.portlet.trash.model.TrashEntry` entity specifies Recycle Bin \nentries, and `PortletProvider.Action.VIEW` specifies the view action. \n\nHow and where you use the portlet ID depends on your needs---there's no typical \nuse case or set of steps to follow. One example is how the Asset Publisher uses \nthe Portlet Provider framework to add a previewed asset to a page; it adds the \nasset to a portlet and adds that portlet to the page. The Asset Publisher uses \nthe `liferay-asset:asset_display` tag library tag whose \n`asset_display/preview.jsp` shows an *Add* button for adding the portlet. If the \npreviewed asset is a Blogs entry, for example, the framework returns a blogs \nportlet ID or URL for adding the portlet to the current page. Here's the \nrelevant code from the \n[`asset_display/preview.jsp`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/asset/asset-taglib/src/main/resources/META-INF/resources/asset_display/preview.jsp#L75-L91): \n\n```markup\n<%\nMap<String, Object> data = new HashMap<String, Object>();\n\n<!-- populate the data map -->\n\nString portletId = PortletProviderUtil.getPortletId(assetEntry.getClassName(), PortletProvider.Action.ADD);\n\ndata.put(\"portlet-id\", portletId);\n\n<!-- add more to the data map -->\n%>\n\n<c:if test=\"<%= PortletPermissionUtil.contains(permissionChecker, layout, portletId, ActionKeys.ADD_TO_PAGE) %>\">\n    <aui:button cssClass=\"add-button-preview\" data=\"<%= data %>\" value=\"add\" />\n</c:if>\n```\n\nThis code invokes \n`PortletProviderUtil.getPortletId(assetEntry.getClassName(), PortletProvider.Action.ADD)` \nto get the ID of a portlet that adds and displays the asset of the underlying \nentity class. \n\nThe JSP puts the portlet ID into the `data` map:\n\n```java\ndata.put(\"portlet-id\", portletId);\n```\n\nThen it passes the `data` map to a new *Add* button that adds the portlet to\nthe page:\n\n```markup\n<aui:button cssClass=\"add-button-preview\" data=\"<%= data %>\" value=\"add\" />\n```\n\n## Fetching a Portlet URL\n\nTo get the URL of the portlet that performs an action on an entity, call one of \n[`PortletProviderUtil`'s](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/PortletProviderUtil.html) \n`getPortletURL` methods. These methods return a `javax.portlet.PortletURL` based \non an `HttpServletRequest` or `PortletRequest`. You can also specify a `Group`, \nthe entity's class name, and the action. \n\nHow you call these methods depends on your use case---there's no typical set of \nsteps to follow. As an example, when the Asset Publisher is configured in Manual \nmode, the user can use an Asset Browser to select asset entries. The \n`asset-publisher-web` module's \n[`configuration/asset_entries.jsp`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/asset/asset-publisher-web/src/main/resources/META-INF/resources/configuration/asset_entries.jsp#L123)\nfile uses `PortletProviderUtil`'s `getPortletURL` method (at the end of the code \nbelow) to generate a corresponding Asset Browser URL: \n\n```java\nList<AssetRendererFactory<?>> assetRendererFactories = \n    ListUtil.sort(\n        AssetRendererFactoryRegistryUtil.getAssetRendererFactories(\n            company.getCompanyId()),\n            new AssetRendererFactoryTypeNameComparator(locale));\n\nfor (AssetRendererFactory<?> curRendererFactory : assetRendererFactories) {\n        long curGroupId = groupId;\n\n        if (!curRendererFactory.isSelectable()) {\n            continue;\n        }\n\n        PortletURL assetBrowserURL = PortletProviderUtil.getPortletURL(\n            request, curRendererFactory.getClassName(),\n            PortletProvider.Action.BROWSE);\n```\n\n## Related Topics\n\n[Portlet Providers](/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks#portlet-providers)\n\n[Creating Portlet Providers](/docs/7-2/frameworks/-/knowledge_base/f/creating-portlet-providers)\n"
  },
  {
    "path": "en/developer/frameworks/articles/back-end-frameworks/04-enabling-accessing-scopes.markdown",
    "content": "---\nheader-id: enabling-and-accessing-data-scopes\n---\n\n# Enabling and Accessing Data Scopes\n\n[TOC levels=1-4]\n\nApps can restrict their data to specific scopes (e.g., Global, Site, Page). \nHere, you'll learn how to \n\n-   [Enable Scoping](#enabling-scoping) \n-   [Access Your App's Scope](#accessing-your-apps-scope)\n-   [Access the Site Scope](#accessing-the-site-scope)\n\nFor more detailed information about scoping, see \n[Data Scopes](/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks#data-scopes). \n\n## Enabling Scoping\n\n1.  Scope your app's entities. In your service layer, your entities must have a \n    `companyId` attribute of type `long` to enable scoping by portal instance, \n    and a `groupId` attribute of type `long` to enable scoping by Site. Using \n    [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder) \n    is the simplest way to do this. For instructions on this, see \n    [Service Builder Persistence](/docs/7-2/appdev/-/knowledge_base/a/creating-a-service-builder-project) \n    and \n    [Business Logic with Service Builder](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder). \n\n2.  To enable scoping in your app, set the property \n    `\"com.liferay.portlet.scopeable=true\"` in your portlet class's `@Component` \n    annotation. For example, the\n    [Web Content Display Portlet's portlet class](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/journal/journal-content-web/src/main/java/com/liferay/journal/content/web/internal/portlet/JournalContentPortlet.java) \n    sets this component property: \n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            ...\n            \"com.liferay.portlet.scopeable=true\",\n            ...,\n        },\n        service = Portlet.class\n    )\n    public class JournalContentPortlet extends MVCPortlet {\n        ...\n    }\n    ```\n\n## Accessing Your App's Scope\n\nUsers can typically set an app's scope to a page, a Site, or the entire portal. \nTo handle your app's data, you must access it in its current scope. Your app's\nscope is available in these ways: \n\n1.  Via the `scopeGroupId` variable injected in JSPs that use the\n    `<liferay-theme:defineObjects />` tag. This variable contains your app's\n    current scope. For example, the Liferay Bookmarks app's\n    [`view.jsp`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/bookmarks/bookmarks-web/src/main/resources/META-INF/resources/bookmarks/view.jsp#L122-L125) \n    uses its `scopeGroupId` to retrieve the bookmarks and total number of \n    bookmarks in the current scope: \n\n    ```markup\n    ...\n    total = BookmarksEntryServiceUtil.getGroupEntriesCount(scopeGroupId, groupEntriesUserId);\n\n    bookmarksSearchContainer.setTotal(total);\n    bookmarksSearchContainer.setResults(BookmarksEntryServiceUtil.getGroupEntries(scopeGroupId, groupEntriesUserId, bookmarksSearchContainer.getStart(), bookmarksSearchContainer.getEnd()));\n    ...\n    ```\n\n2.  By calling the `getScopeGroupId()` method on the request's \n    [`ThemeDisplay`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/theme/ThemeDisplay.html). \n    This method returns your app's current scope. For example, the Liferay Blogs\n    app's\n    [`EditEntryMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-web/src/main/java/com/liferay/blogs/web/internal/portlet/action/EditEntryMVCActionCommand.java#L350-L362) \n    class does this in its `subscribe` and `unsubscribe` methods: \n\n    ```java\n    protected void subscribe(ActionRequest actionRequest) throws Exception {\n        ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(\n            WebKeys.THEME_DISPLAY);\n\n        _blogsEntryService.subscribe(themeDisplay.getScopeGroupId());\n    }\n\n    protected void unsubscribe(ActionRequest actionRequest) throws Exception {\n        ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(\n            WebKeys.THEME_DISPLAY);\n\n        _blogsEntryService.unsubscribe(themeDisplay.getScopeGroupId());\n    }\n    ```\n\n    If you know your app always needs the portal instance ID, use \n    `themeDisplay.getCompanyId()`. \n\n3.  By calling the `getScopeGroupId()` method on a \n    [`ServiceContext`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/service/ServiceContext.html) \n    object. See \n    [Understanding Service Context](/docs/7-2/frameworks/-/knowledge_base/f/understanding-servicecontext)\n    for an example and more details. If you know your app always needs the \n    portal instance ID, use the `ServiceContext` object's `getCompanyId()` \n    method. \n\n## Accessing the Site Scope\n\nTo access the Site scope regardless of your app's current scope, use the \n[`ThemeDisplay`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/theme/ThemeDisplay.html) \nmethod `getSiteGroupId()`. For more information on this use case, see \n[Accessing the Site Scope Across Apps](/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks#accessing-the-site-scope-across-apps). \n\nFor example, the Web Content app's \n[`edit_feed.jsp`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/journal/journal-web/src/main/resources/META-INF/resources/edit_feed.jsp#L40)\nuses the `getSiteGroupId()` method to get the Site ID, which is required to \nretrieve Structures: \n\n```java\nddmStructure = DDMStructureLocalServiceUtil.fetchStructure(themeDisplay.getSiteGroupId(), \n    PortalUtil.getClassNameId(JournalArticle.class), ddmStructureKey, true);\n```\n\n## Related Topics\n\n[Data Scopes](/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks#data-scopes)\n\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n\n[Service Builder Project](/docs/7-2/appdev/-/knowledge_base/a/creating-a-service-builder-project)\n\n[Business Logic with Service Builder](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder)\n"
  },
  {
    "path": "en/developer/frameworks/articles/back-end-frameworks/05-message-bus/01-intro.markdown",
    "content": "---\nheader-id: using-the-message-bus\n---\n\n# Using the Message Bus\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/developing-applications/core-frameworks/message-bus.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nHere, you'll learn how to use the \n[Message Bus](/docs/7-2/frameworks/-/knowledge_base/f/back-end-frameworks#message-bus) \nto send and receive messages in the portal. The following topics are covered: \n\n-   [Messaging Destinations](#messaging-destinations)\n-   [Message Listeners](#message-listeners)\n-   [Sending Messages](#sending-messages)\n\n## Messaging Destinations\n\nIn Message Bus, you send messages to destinations. A destination is a named \nlogical (not physical) location. Sender classes send messages to destinations, \nwhile listener classes wait to receive messages at the destinations. In this \nway, the sender and recipient don't need to know each other---they're loosely\ncoupled. \n\n### Destination Configuration\n\nEach destination has a name and type and can have several other attributes. The \ndestination type determines these things: \n\n-   Whether there's a message queue. \n-   The kinds of threads involved with a destination. \n-   The message delivery behavior to expect at the destination. \n\nHere are the primary destination types:\n\n**Parallel Destination**\n\n-   Messages sent here are queued.\n-   Multiple worker threads from a thread pool deliver each message to a \n    registered message listener. There's one worker thread per message per \n    message listener. \n\n**Serial Destination**\n\n-   Messages sent here are queued. \n-   Worker threads from a thread pool deliver the messages to each registered \n    message listener, one worker thread per message. \n\n**Synchronous Destination**\n\n-   Messages sent here are directly delivered to message listeners. \n-   The thread sending the message here also delivers the message to all message \n    listeners. \n\nPreconfigured destinations exist for various purposes. The \n[`DestinationNames`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationNames.html)\nclass defines `String` constants for each. For example, \n`DestinationNames.HOT_DEPLOY` (value is `\"liferay/hot_deploy\"`) is for \ndeployment event messages. Since destinations are tuned for specific purposes, \ndon't modify them. \n\nDestinations are based on \n[`DestinationConfiguration`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationConfiguration.html) \ninstances. The configuration specifies the destination type, name, and these \ndestination-related attributes: \n\n**Maximum Queue Size**: Limits the number of the destination's queued messages. \n\n**Rejected Execution Handler**: A \n[`RejectedExecutionHandler`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/concurrent/RejectedExecutionHandler.html) \ninstance can take action (e.g., log warnings) regarding rejected messages when \nthe destination queue is full. \n\n**Workers Core Size**: Initial number of worker threads for processing messages. \n\n**Workers Max Size**: Limits the number of worker threads for processing \nmessages. \n\nThe `DestinationConfiguration` class provides these static methods for creating \nthe various types of configurations. \n\n-   `createParallelDestinationConfiguration(String destinationName)`\n-   `createSerialDestinationConfiguration(String destinationName)`\n-   `createSynchronousDestinationConfiguration(String destinationName)`\n\nYou can also use the `DestinationConfiguration` \n[constructor](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationConfiguration.html#DestinationConfiguration-java.lang.String-java.lang.String-) \nto create a configuration for any destination type, even your own. \n\nFor instructions on creating your own destination, see \n[Creating a Destination](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-destination). \n\n## Message Listeners\n\nIf you're interested in messages sent to a destination, you need to *listen* for \nthem. That is, you must create and register a message listener for the \ndestination. \n\nTo create a message listener, implement the\n[`MessageListener`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageListener.html) \ninterface and override its `receive(Message)` method to process messages your \nway. \n\n```java\npublic void receive(Message message) {\n    // Process messages your way\n}\n```\n\nHere are the ways to register your listener with Message Bus: \n\n**Automatic Registration as a Component**: Publish the listener to the OSGi \nregistry as a \n[Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services) \ncomponent that specifies a destination. Message Bus automatically wires the \nlistener to the destination. \n\n**Registering via MessageBus**: Obtain and use a \n[`MessageBus`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageBus.html) \nreference to directly register the listener to a destination. \n\n**Registering Directly to a Destination**: Obtain a reference to a specific \ndestination and use it to directly register the listener with that destination. \n\nFor instructions on these topics, see \n[Registering Message Listeners](/docs/7-2/frameworks/-/knowledge_base/f/registering-message-listeners). \n\n## Sending Messages\n\nMessage Bus lets you send messages to destinations that have any number of \nlistening classes. As a message sender you don't need to know the message \nrecipients. Instead, you focus on creating message content (payload) and sending \nmessages to destinations. \n\nYou can also send messages in a synchronous or asynchronous manner. The \nsynchronous option waits for a response that the message was received or that it \ntimed out. The asynchronous option gives you the \"fire and forget\" behavior; \nsend the message and continue processing without waiting for a response. \n\nSee these topics for instructions on creating and sending messages: \n\n- [Creating a Message](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-message) \n- [Sending a Message](/docs/7-2/frameworks/-/knowledge_base/f/sending-a-message) \n- [Sending Messages Across a Cluster](/docs/7-2/frameworks/-/knowledge_base/f/sending-messages-across-a-cluster) \n\n"
  },
  {
    "path": "en/developer/frameworks/articles/back-end-frameworks/05-message-bus/02-creating-destination.markdown",
    "content": "---\nheader-id: creating-a-destination\n---\n\n# Creating a Destination\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/developing-applications/core-frameworks/message-bus.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n[Message Bus destinations](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus#messaging-destinations) \nare based on destination configurations and registered as OSGi services. Message \nBus detects the destination services and manages their associated destinations. \n\nHere are the steps for creating a destination. The example configurator class\nthat follows demonstrates these steps.\n\n1.  Create an `activate(BundleContext)` method in your component. Then create a \n    [`BundleContext`](https://osgi.org/javadoc/r4v43/core/org/osgi/framework/BundleContext.html) \n    instance variable and set it to the `activate` method's `BundleContext`: \n\n    ```java\n    @Activate\n    protected void activate(BundleContext bundleContext) {\n\n        _bundleContext = bundleContext;\n\n    }\n\n    private final BundleContext _bundleContext;\n    ```\n\n    You'll create and register your destination inside this `activate` method. \n    This ensures that the destination is available upon service activation. Once \n    the destination is registered, Message Bus detects its service and manages\n    the destination. \n\n2.  Create a destination configuration by using one of \n    [`DestinationConfiguration`'s](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationConfiguration.html) \n    static `create*` methods or its constructor. Set any attributes that apply \n    to the destinations you'll create with the destination configuration. \n\n    For example, this code uses the `DestinationConfiguration` constructor to \n    create a destination configuration for parallel destinations. It then sets \n    the destination configuration's maximum queue size and \n    [`RejectedExecutionHandler`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/concurrent/RejectedExecutionHandler.html): \n\n    ```java\n    @Activate\n    protected void activate(BundleContext bundleContext) {\n        ...\n\n        // Create a DestinationConfiguration for parallel destinations.\n\n        DestinationConfiguration destinationConfiguration =\n            new DestinationConfiguration(\n                DestinationConfiguration.DESTINATION_TYPE_PARALLEL,\n                    \"myDestinationName\");\n\n        // Set the DestinationConfiguration's max queue size and\n        // rejected execution handler.\n\n        destinationConfiguration.setMaximumQueueSize(_MAXIMUM_QUEUE_SIZE);\n\n        RejectedExecutionHandler rejectedExecutionHandler =\n            new CallerRunsPolicy() {\n\n                @Override\n                public void rejectedExecution(\n                    Runnable runnable, ThreadPoolExecutor threadPoolExecutor) {\n\n                    if (_log.isWarnEnabled()) {\n                        _log.warn(\n                            \"The current thread will handle the request \" +\n                                \"because the graph walker's task queue is at \" +\n                                    \"its maximum capacity\");\n                    }\n\n                    super.rejectedExecution(runnable, threadPoolExecutor);\n                }\n\n        };\n\n        destinationConfiguration.setRejectedExecutionHandler(\n            rejectedExecutionHandler);\n\n    }\n    ```\n\n3.  Create the destination by invoking the \n    [`DestinationFactory`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationFactory.html)\n    method `createDestination(DestinationConfiguration)`, passing in the \n    destination configuration from the previous step. \n\n    For example, this code does so via a `DestinationFactory` reference: \n\n    ```java\n    @Activate\n    protected void activate(BundleContext bundleContext) {\n        ...\n\n        Destination destination = _destinationFactory.createDestination(\n            destinationConfiguration);\n\n    }\n    ...\n\n    @Reference\n    private DestinationFactory _destinationFactory;\n    ```\n\n4.  Register the destination as an OSGi service by invoking the `BundleContext` \n    method `registerService` with these parameters: \n\n    -   The destination class `Destination.class`.\n    -   Your `Destination` object.\n    -   A `Dictionary` of properties defining the destination, including the \n        `destination.name`. \n\n    ```java\n    @Activate\n    protected void activate(BundleContext bundleContext) {\n        ...\n\n        Dictionary<String, Object> properties = new HashMapDictionary<>();\n\n        properties.put(\"destination.name\", destination.getName());\n\n        ServiceRegistration<Destination> serviceRegistration =\n            _bundleContext.registerService(\n                Destination.class, destination, properties);\n    }\n    ```\n\n5.  Manage the destination object and service registration resources using a\n    collection such as a `Map<String, ServiceRegistration<Destination>>`.\n    Keeping references to these resources is helpful for when you're ready to\n    unregister and destroy them. \n\n    ```java\n    @Activate\n    protected void activate(BundleContext bundleContext) {\n        ...\n\n        _serviceRegistrations.put(destination.getName(), \n            serviceRegistration);\n\n    }\n    ...\n\n    private final Map<String, ServiceRegistration<Destination>>\n        _serviceRegistrations = new HashMap<>();\n    ```\n\n6.  Add a `deactivate` method that unregisters and destroys any destinations for \n    this component. This ensures there aren't any active destinations for this \n    component when the service deactivates: \n\n    ```java\n    @Deactivate\n    protected void deactivate() {\n\n        // Unregister and destroy destinations\n\n        for (ServiceRegistration<Destination> serviceRegistration : \n            _serviceRegistrations.values()) {\n\n            Destination destination = _bundleContext.getService(\n                serviceRegistration.getReference());\n\n            serviceRegistration.unregister();\n\n            destination.destroy();\n\n        }\n\n        _serviceRegistrations.clear();\n\n    }\n    ```\n\nHere's the full messaging configurator component class that contains the code in \nthe above steps: \n\n```java\n@Component (\n    immediate = true,\n    service = MyMessagingConfigurator.class\n)\npublic class MyMessagingConfigurator {\n\n    @Activate\n    protected void activate(BundleContext bundleContext) {\n\n        _bundleContext = bundleContext;\n\n        // Create a DestinationConfiguration for parallel destinations.\n\n        DestinationConfiguration destinationConfiguration =\n            new DestinationConfiguration(\n                DestinationConfiguration.DESTINATION_TYPE_PARALLEL,\n                    \"myDestinationName\");\n\n        // Set the DestinationConfiguration's max queue size and\n        // rejected execution handler.\n\n        destinationConfiguration.setMaximumQueueSize(_MAXIMUM_QUEUE_SIZE);\n\n        RejectedExecutionHandler rejectedExecutionHandler =\n            new CallerRunsPolicy() {\n\n                @Override\n                public void rejectedExecution(\n                    Runnable runnable, ThreadPoolExecutor threadPoolExecutor) {\n\n                    if (_log.isWarnEnabled()) {\n                        _log.warn(\n                            \"The current thread will handle the request \" +\n                                \"because the graph walker's task queue is at \" +\n                                    \"its maximum capacity\");\n                    }\n\n                    super.rejectedExecution(runnable, threadPoolExecutor);\n                }\n\n            };\n\n        destinationConfiguration.setRejectedExecutionHandler(\n            rejectedExecutionHandler);\n\n        // Create the destination\n\n        Destination destination = _destinationFactory.createDestination(\n            destinationConfiguration);\n\n        // Add the destination to the OSGi service registry\n\n        Dictionary<String, Object> properties = new HashMapDictionary<>();\n\n        properties.put(\"destination.name\", destination.getName());\n\n        ServiceRegistration<Destination> serviceRegistration =\n            _bundleContext.registerService(\n                Destination.class, destination, properties);\n\n        // Track references to the destination service registrations \n\n        _serviceRegistrations.put(destination.getName(),    \n            serviceRegistration);\n    }\n\n    @Deactivate\n    protected void deactivate() {\n\n        // Unregister and destroy destinations this component unregistered\n\n        for (ServiceRegistration<Destination> serviceRegistration : \n            _serviceRegistrations.values()) {\n\n            Destination destination = _bundleContext.getService(\n                serviceRegistration.getReference());\n\n            serviceRegistration.unregister();\n\n            destination.destroy();\n\n        }\n\n        _serviceRegistrations.clear();\n\n    }\n\n    private final BundleContext _bundleContext;\n\n    @Reference\n    private DestinationFactory _destinationFactory;\n\n    private final Map<String, ServiceRegistration<Destination>>\n        _serviceRegistrations = new HashMap<>();\n}\n```\n\n## Related Topics\n\n[Message Bus Destinations](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus#messaging-destinations)\n"
  },
  {
    "path": "en/developer/frameworks/articles/back-end-frameworks/05-message-bus/03-message-bus-event-listeners.markdown",
    "content": "---\nheader-id: message-bus-event-listeners\n---\n\n# Message Bus Event Listeners\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/developing-applications/core-frameworks/message-bus/listening-for-registration-events.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nWhen \n[using Message Bus](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus), \nyou may wish to listen for events that take place within the Message Bus \nframework itself, independent of messages. For example, you can listen for when \n[destinations](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus#messaging-destinations) \nand \n[message listeners](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus#message-listeners) \nare added or removed. Here, you'll learn how. \n\n## Listening for Destinations\n\nMessage Bus notifies event listeners when destinations are added and removed. To \nregister these listeners, publish a \n[`MessageBusEventListener`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageBusEventListener.html) \ninstance to the OSGi service registry (e.g., via an `@Component` annotation).\n\nHere's an example implementation of `MessageBusEventListener`. Use the \n`destinationAdded` and `destinationDestroyed` methods to implement any logic \nthat you want to run when a destination is added or removed, respectively: \n\n```java\n@Component(\n    immediate = true,\n    service = MessageBusEventListener.class\n)\npublic class MyMessageBusEventListener implements MessageBusEventListener {\n\n    void destinationAdded(Destination destination) {\n        ...\n    }\n\n    void destinationDestroyed(Destination destination) {\n        ...\n    }\n}\n```\n\n## Listening for Message Listeners\n\nMessage Bus notifies \n[`DestinationEventListener`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationEventListener.html) \ninstances when message listeners for destinations are either registered or \nunregistered. To register an event listener to a destination, publish a \n`DestinationEventListener` service to the OSGi service registry, making sure to \nspecify the destination's `destination.name` property. \n\n```java\n@Component(\n    immediate = true,\n    property = {\"destination.name=myCustom/Destination\"},\n    service = DestinationEventListener.class\n)\npublic class MyDestinationEventListener implements DestinationEventListener {\n\n    void messageListenerRegistered(String destinationName,\n                                   MessageListener messageListener) {\n        ...\n    }\n\n    void messageListenerUnregistered(String destinationName,\n                                   MessageListener messageListener) {\n        ...\n    }\n}\n```\n\n## Related Topics\n\n[Using the Message Bus](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus)\n"
  },
  {
    "path": "en/developer/frameworks/articles/back-end-frameworks/05-message-bus/04-registering-message-listeners.markdown",
    "content": "---\nheader-id: registering-message-listeners\n---\n\n# Registering Message Listeners\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/developing-applications/core-frameworks/message-bus/listening-for-messages.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThere are three ways to register a \n[message listener](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus#message-listeners) \nwith the Message Bus: \n\n1.  [Automatic Registration as a Component](#automatic-registration-as-a-component)\n2.  [Registering via a MessageBus Reference](#registering-via-a-messagebus-reference)\n3.  [Registering Directly to the Destination](#registering-directly-to-the-destination)\n\nAutomatic registration as a component is the preferred way to register message \nlisteners to destinations. You might want to use the other two ways if, for \nexample, you want to create some special proxy wrappers. \n\n| **Note**: The \n| [`DestinationNames`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/DestinationNames.html) \n| class defines `String` constants for @product@'s preconfigured destinations. \n\n## Automatic Registration as a Component\n\nYou can specify a message listener in the \n[Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services) \n`@Component` annotation: \n\n```java\n@Component (\n    immediate = true,\n    property = {\"destination.name=myCustom/Destination\"},\n    service = MessageListener.class\n)\npublic class MyMessageListener implements MessageListener {\n    ...\n\n   public void receive(Message message) {\n       // Handle the message\n   }\n}\n```\n\nThe Message Bus listens for \n[`MessageListener`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageListener.html) \nservice components like this one to publish themselves to the OSGi service \nregistry. The attribute `immediate = true` tells the OSGi framework to activate \nthe component as soon as its dependencies resolve. Message Bus wires each \nregistered listener to the destination its `destination.name` property \nspecifies. If the destination is not yet registered, Message Bus queues the \nlistener until the destination registers. \n\n## Registering via a MessageBus Reference\n\nYou can use a \n[`MessageBus`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageBus.html) \nreference to directly register message listeners to destinations. Here's a \nregistrator that demonstrates this: \n\n```java\n@Component (\n    immediate = true,\n    service = MyMessageListenerRegistrator.class\n)\npublic class MyMessageListenerRegistrator {\n    ...\n\n    @Activate\n    protected void activate() {\n\n        _messageListener = new MessageListener() {\n\n            public void receive(Message message) {\n                // Handle the message\n            }\n        };\n\n        _messageBus.registerMessageListener(\"myDestinationName\",  \n            _messageListener);\n    }\n\n    @Deactivate\n    protected void deactivate() {\n        _messageBus.unregisterMessageListener(\"myDestinationName\",  \n            _messageListener);\n    }\n\n    @Reference\n    private MessageBus _messageBus;\n\n    private MessageListener _messageListener;\n}\n```\n\nThe `_messageBus` field's `@Reference` annotation binds it to the `MessageBus` \ninstance. The `activate` method creates the listener and uses the Message Bus to \nregister the listener to a destination named `\"myDestination\"`. When this \nregistrator component is destroyed, the `deactivate` method unregisters the \nlistener. \n\n## Registering Directly to the Destination\n\nYou can use a \n[`Destination`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/Destination.html) \nreference to register a listener to that destination. Here's a registrator that \ndemonstrates this: \n\n```java\n@Component (\n    immediate = true,\n    service = MyMessageListenerRegistrator.class\n)\npublic class MyMessageListenerRegistrator {\n    ...\n\n    @Activate\n    protected void activate() {\n\n        _messageListener = new MessageListener() {\n\n            public void receive(Message message) {\n                // Handle the message\n            }\n        };\n\n        _destination.register(_messageListener);\n    }\n\n    @Deactivate\n    protected void deactivate() {\n\n        _destination.unregister(_messageListener);\n    }\n\n    @Reference(target = \"(destination.name=someDestination)\")\n    private Destination _destination;\n\n    private MessageListener _messageListener;\n}\n```\n\nThe `_destination` field's `@Reference` annotation binds it to a destination\nnamed `someDestination`. The `activate` method creates the listener and\nregisters it to the destination. When this registrator component is destroyed,\nthe `deactivate` method unregisters the listener. \n\n## Related Topics\n\n[Using the Message Bus](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus)\n"
  },
  {
    "path": "en/developer/frameworks/articles/back-end-frameworks/05-message-bus/05-creating-a-message.markdown",
    "content": "---\nheader-id: creating-a-message\n---\n\n# Creating a Message\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/developing-applications/core-frameworks/message-bus.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nBefore you can \n[send a message](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus#sending-messages) \nvia the Message Bus, you must first create it. Here's how to create a message: \n\n1.  Call the\n    [`Message`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/Message.html) \n    constructor to create a new `Message`: \n\n    ```java\n    Message message = new Message();\n    ```\n\n2.  Populate the message with a `String` or `Object` payload:\n\n    -   String payload: `message.setPayload(\"Message Bus is great!\")`\n\n    -   Object payload: `message.put(\"firstName\", \"Joe\")`\n\n3.  To receive responses at a particular location, set both of these attributes: \n\n    -  Response destination name: `setResponseDestinationName(String)`\n\n    -  Response ID: `setResponseId(String)`\n\n## Related Topics\n\n[Sending a Message](/docs/7-2/frameworks/-/knowledge_base/f/sending-a-message)\n\n[Sending Messages Across a Cluster](/docs/7-2/frameworks/-/knowledge_base/f/sending-messages-across-a-cluster) \n\n[Using the Message Bus](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus) \n"
  },
  {
    "path": "en/developer/frameworks/articles/back-end-frameworks/05-message-bus/06-sending-a-message.markdown",
    "content": "---\nheader-id: sending-a-message\n---\n\n# Sending a Message\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/developing-applications/core-frameworks/message-bus.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nOnce you've \n[created a message](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-message), \nthere are three ways to send it with the \n[Message Bus](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus): \n\n-   [Directly with `MessageBus`](#directly-with-messagebus)\n-   [Asynchronously with `SingleDestinationMessageSender`](#asynchronously-with-singledestinationmessagesender) \n-   [Synchronously with `SynchronousMessageSender`](#synchronously-with-synchronousmessagesender)\n\n## Directly with MessageBus\n\nTo send a message directly with \n[`MessageBus`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageBus.html), \nfollow these steps: \n\n1.  Get a `MessageBus` reference: \n\n    ```java\n    @Reference\n    private MessageBus _messageBus;\n    ```\n\n2.  [Create a message](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-message). \n    For example: \n\n    ```java\n    Message message = new Message();\n    message.put(\"myId\", 12345);\n    message.put(\"someAttribute\", \"abcdef\");\n    ```\n\n3.  Call the `MessageBus` reference's `sendMessage` method with the destination \n    and message: \n\n    ```java\n    _messageBus.sendMessage(\"myDestinationName\", message);\n    ```\n\nHere's a class that contains this example: \n\n```java\n@Component(\n    immediate = true,\n    service = SomeServiceImpl.class\n)\npublic class SomeServiceImpl {\n    ...\n\n    public void sendSomeMessage() {\n\n        Message message = new Message();\n        message.put(\"myId\", 12345);\n        message.put(\"someAttribute\", \"abcdef\");\n        _messageBus.sendMessage(\"myDestinationName\", message);\n    }\n\n    @Reference\n    private MessageBus _messageBus;\n}\n```\n\n## Asynchronously with SingleDestinationMessageSender\n\nThe \n[`SingleDestinationMessageSender`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/sender/SingleDestinationMessageSender.html) \ninterface wraps the Message Bus to send messages asynchronously. Follow these \nsteps to use this interface to send asynchronous messages: \n\n1.  Create a \n    [`SingleDestinationMessageSenderFactory`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/sender/SingleDestinationMessageSenderFactory.html) \n    reference: \n\n    ```java\n    @Reference\n    private SingleDestinationMessageSenderFactory _messageSenderFactory;\n    ```\n\n2.  Create a `SingleDestinationMessageSender` by calling the \n    `SingleDestinationMessageSenderFactory` reference's \n    `createSingleDestinationMessageSender` method with the message's \n    destination: \n\n    ```java\n    SingleDestinationMessageSender messageSender = \n       _messageSenderFactory.createSingleDestinationMessageSender(\"myDestinationName\");\n    ```\n\n3.  [Create a message](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-message). \n    For example: \n\n    ```java\n    Message message = new Message();\n    message.put(\"myId\", 12345);\n    message.put(\"someValue\", \"abcdef\");\n    ```\n\n4.  Send the message by calling the `SingleDestinationMessageSender` instance's \n    `send` method with the message: \n\n    ```java\n    messageSender.send(message);\n    ```\n\nHere's a class that contains this example: \n\n```java\n@Component(\n    immediate = true,\n    service = SomeServiceImpl.class\n)\npublic class SomeServiceImpl {\n    ...\n\n    public void sendSomeMessage() {\n\n        SingleDestinationMessageSender messageSender = \n           _messageSenderFactory.createSingleDestinationMessageSender(\"myDestinationName\");\n\n        Message message = new Message();\n        message.put(\"myId\", 12345);\n        message.put(\"someValue\", \"abcdef\");\n\n        messageSender.send(message);\n    }\n\n    @Reference\n    private SingleDestinationMessageSenderFactory _messageSenderFactory;\n}\n```\n\n### Synchronously with SynchronousMessageSender\n\n[`SynchronousMessageSender`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/sender/SynchronousMessageSender.html) \nsends a message to the Message Bus and blocks until receiving a response or the \nresponse times out. A `SynchronousMessageSender` has these \n[operating modes](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/sender/SynchronousMessageSender.Mode.html):\n\n`DEFAULT`: Delivers the message in a separate thread and also provides timeouts, \nin case the message is not delivered properly. \n\n`DIRECT`: Delivers the message in the same thread of execution and blocks until \nit receives a response. \n\nFollow these steps to send a synchronous message with \n`SynchronousMessageSender`: \n\n1.  Get a \n    [`SingleDestinationMessageSenderFactory`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/sender/SingleDestinationMessageSenderFactory.html) \n    reference: \n\n    ```java\n    @Reference\n    private SingleDestinationMessageSenderFactory _messageSenderFactory;\n    ```\n\n2.  Create a `SingleDestinationSynchronousMessageSender` by calling the \n    `SingleDestinationMessageSenderFactory` reference's \n    `createSingleDestinationSynchronousMessageSender` method with the \n    destination and operating mode. Note that this example uses the `DEFAULT` \n    mode: \n\n    ```java\n    SingleDestinationSynchronousMessageSender messageSender = \n        _messageSenderFactory.createSingleDestinationSynchronousMessageSender(\n            \"myDestinationName\", SynchronousMessageSender.Mode.DEFAULT);\n    ```\n\n3.  [Create a message](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-message). \n    For example: \n\n    ```java\n    Message message = new Message();\n    message.put(\"myId\", 12345);\n    message.put(\"someValue\", \"abcdef\");\n    ```\n\n4.  Send the message by calling the `SingleDestinationSynchronousMessageSender` \n    instance's `send` method with the message: \n\n    ```java\n    messageSender.send(message);\n    ```\n\nHere's a class that contains this example: \n\n```java\n@Component(\n    immediate = true,\n    service = SomeServiceImpl.class\n)\npublic class SomeServiceImpl {\n    ...\n\n    public void sendSomeMessage() {\n\n        Message message = new Message();\n        message.put(\"myId\", 12345);\n        message.put(\"someAttribute\", \"abcdef\");\n\n        SingleDestinationSynchronousMessageSender messageSender = \n            _messageSenderFactory.createSingleDestinationSynchronousMessageSender(\n                \"myDestinationName\", SynchronousMessageSender.Mode.DEFAULT);\n\n        messageSender.send(message);\n\n    }\n\n    @Reference\n    private SingleDestinationMessageSenderFactory _messageSenderFactory;\n}\n```\n\n## Related Topics\n\n[Creating a Message](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-message) \n\n[Sending Messages Across a Cluster](/docs/7-2/frameworks/-/knowledge_base/f/sending-messages-across-a-cluster) \n\n[Using the Message Bus](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus) \n"
  },
  {
    "path": "en/developer/frameworks/articles/back-end-frameworks/05-message-bus/07-sending-messages-cluster.markdown",
    "content": "---\nheader-id: sending-messages-across-a-cluster\n---\n\n# Sending Messages Across a Cluster\n\n[TOC levels=1-4]\n\nTo ensure a message sent to a destination is received by all cluster nodes, you \nmust register a \n[`ClusterBridgeMessageListener`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cluster/messaging/ClusterBridgeMessageListener.html) \nat that destination. This bridges the local destination to the cluster and \nensures that messages sent to the destination are distributed across the \ncluster's JVMs. You should do this in a registrator class, like those shown in \n[Registering Message Listeners](/docs/7-2/frameworks/-/knowledge_base/f/registering-message-listeners). \n\nFollow these steps to create a registrator class that registers a \n`ClusterBridgeMessageListener` to a destination: \n\n1.  Create the registrator class as an OSGi component: \n\n    ```java\n    @Component(\n        immediate = true,\n        service = MyMessageListenerRegistrator.class\n    )\n    public class MyMessageListenerRegistrator {\n        ...\n    }\n    ```\n\n2.  Create a \n    [`MessageListener`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/MessageListener.html) \n    variable: \n\n    ```java\n    private MessageListener _clusterBridgeMessageListener;\n    ```\n\n3.  Create a \n    [`Destination`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/messaging/Destination.html) \n    reference and set its `destination.name` property to your destination. For \n    example, this reference is for the destination `liferay/live_users`: \n\n    ```java\n    @Reference(target = \"(destination.name=liferay/live_users)\")\n    private Destination _destination;\n    ```\n\n4.  In the registrator's `activate` method, create a new \n    `ClusterBridgeMessageListener` and set it to the `MessageListener` variable \n    you created earlier. Then set the `ClusterBridgeMessageListener`'s priority \n    and register the `ClusterBridgeMessageListener` to the destination: \n\n    ```java\n    @Activate\n    protected void activate() {\n\n        _clusterBridgeMessageListener = new ClusterBridgeMessageListener();\n        _clusterBridgeMessageListener.setPriority(Priority.LEVEL5)\n        _destination.register(_clusterBridgeMessageListener);\n    }\n    ```\n\n    The \n    [`Priority`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cluster/Priority.html)\n    enum has ten levels (`Level1` through `Level10`, with `Level10` being the \n    most important). Each level is a priority queue for sending messages through \n    the cluster. This is similar in concept to thread priorities: \n    `Thread.MIN_PRIORITY`, `Thread.MAX_PRIORITY`, and `Thread.NORM_PRIORITY`. \n\n5.  In the registrator's `deactivate` method, unregister the \n    `ClusterBridgeMessageListener` from the destination: \n\n    ```java\n    @Deactivate\n    protected void deactivate() {\n\n        _destination.unregister(_clusterBridgeMessageListener);\n    }\n    ```\n\nHere's the full registrator class for this example: \n\n```java\n@Component(\n    immediate = true,\n    service = MyMessageListenerRegistrator.class\n)\npublic class MyMessageListenerRegistrator {\n    ...\n\n    @Activate\n    protected void activate() {\n\n        _clusterBridgeMessageListener = new ClusterBridgeMessageListener();\n        _clusterBridgeMessageListener.setPriority(Priority.LEVEL5)\n        _destination.register(_clusterBridgeMessageListener);\n    }\n\n    @Deactivate\n    protected void deactivate() {\n\n        _destination.unregister(_clusterBridgeMessageListener);\n    }\n\n    @Reference(target = \"(destination.name=liferay/live_users)\")\n    private Destination _destination;\n\n    private MessageListener _clusterBridgeMessageListener;\n}\n```\n\n## Related Topics\n\n[Registering Message Listeners](/docs/7-2/frameworks/-/knowledge_base/f/registering-message-listeners)\n\n[Sending a Message](/docs/7-2/frameworks/-/knowledge_base/f/sending-a-message)\n\n[Using the Message Bus](/docs/7-2/frameworks/-/knowledge_base/f/using-the-message-bus)\n"
  },
  {
    "path": "en/developer/frameworks/articles/cache-configuration/01-cache-configuration-intro.markdown",
    "content": "---\nheader-id: cache-configuration\n---\n\n# Cache Configuration\n\n[TOC levels=1-4]\n\nCaching makes specified data readily available in memory. It costs memory but\nimproves performance. You can experiment with cache to determine what's good for\nyour system. If your site serves lots of web content articles, for example, you\nmay want to increase the limit on how many you can cache. \n\nLiferay's cache configuration framework uses\n[Ehcache](https://www.ehcache.org/).\nIt's an independent framework used by @product@'s data access and template\nengine components. It manages two pools: \n\n**Multi-VM:** Cache is replicated among cluster nodes. `EntityCache` and \n`FinderCache` (described next) are in this pool because they must synchronize\nwith data on all nodes.\n\n**Single-VM:** Cache is managed uniquely per VM and isn't replicated among\nnodes. Single-VM cache is for objects and references that you don't need/want\nreplicated among nodes. \n\nHere are ways you can configure the Ehcache: \n\n-   [Overriding Cache](/docs/7-2/frameworks/-/knowledge_base/f/overriding-cache):\n    Tuning existing cache. \n-   [Caching Data](/docs/7-2/frameworks/-/knowledge_base/f/caching-data): \n    Implementing cache for custom data. \n\nStart learning the Liferay cache configuration basics here.\n\n## Cache Types\n\nYou can cache any classes you like. Conveniently, @product@ caches\n[service entities](/docs/7-2/appdev/-/knowledge_base/a/defining-service-entities)\nand\n[service entity finder results](/docs/7-2/appdev/-/knowledge_base/a/defining-service-entity-finder-methods)\nautomatically by default.\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\ngenerates their caching code in the\n[service persistence layer](/docs/7-2/appdev/-/knowledge_base/a/understanding-the-code-generated-by-service-builder).\nThe code operates on these cache types:\n\n**`EntityCache`:** Holds service entities by primary keys. The caching code maps \nentity primary keys to implementation objects. An entity's\n`*PersistenceImpl.fetchByPrimaryKey` method uses `EntityCache`.\n\n**`FinderCache`:** Holds parameterized service entity search results. The caching \ncode associates\n[service entity finder](/docs/7-2/appdev/-/knowledge_base/a/defining-service-entity-finder-methods)\nquery parameter values with matching entity results. There's code for caching\nentities, paginated entity lists, and non-paginated entity lists that match your\nfinder parameters. An entity's `fetchByValue`, `findByValue`, `countByValue`,\n`findAll`, and `countAll` methods use the FinderCache. \n\n## Cache Configuration\n\n@product@ designates separate cache configurations for multi-VM and single-VM\nenvironments. Default `EntityCache` and `FinderCache` are specified\nprogrammatically, while Liferay's global cache configuration and custom cache\nconfigurations are specified via files. All configurations adhere to the\n[Ehcache XSD](http://www.ehcache.org/ehcache.xsd).\n\nLiferay's global cache configuration is processed first on startup. Cache\nconfigurations in modules and WARs are processed as they're deployed after the\ninitial global cache configuration. \n\n### Initial Global Cache Configuration\n\nLiferay's portal cache implementation LPKG file \n(`Liferay [version] Foundation - Liferay [version] Portal Cache - Impl.lpkg`)\nfound in the `[Liferay_Home]/osgi/marketplace` folder contains the initial\nglobal cache configuration. The LPKG file's\n`com.liferay.portal.cache.ehcache.impl-[version].jar` holds the configuration\nfiles:\n\n-   `liferay-multi-vm.xml`: Maps to the multi-VM pool.\n-   `liferay-single-vm.xml`: Maps to the single-VM pool.\n\n### Module Cache Configuration\n\nModules can configure (add or override) cache using configuration files in their\n`src/main/resources/META-INF` folder:\n\n-   `module-multi-vm.xml`: Maps to the multi-VM cache manager.\n-   `module-single-vm.xml`: Maps to the single-VM cache manager.\n\nFor example, the @product@ Web Experience suite's `com.liferay.journal.service`\nmodule uses the following\n[`module-multi-vm.xml`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/journal/journal-service/src/main/resources/META-INF/module-multi-vm.xml)\nto create a cache named `com.liferay.journal.util.JournalContent` in the\nmulti-VM pool. \n\n```xml\n<ehcache\n    dynamicConfig=\"true\"\n    monitoring=\"off\"\n    name=\"module-multi-vm\"\n    updateCheck=\"false\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"http://www.ehcache.org/ehcache.xsd\"\n>\n    <cache\n        eternal=\"false\"\n        maxElementsInMemory=\"10000\"\n        name=\"com.liferay.journal.util.JournalContent\"\n        overflowToDisk=\"false\"\n        timeToIdleSeconds=\"600\"\n    >\n    </cache>\n</ehcache>\n```\n\nPortlet WARs can configure cache too. \n \n### Portlet WAR Cache Configuration \n\nEhcache configuration in a portlet WAR has these requirements: \n\n1.  The Ehcache configuration XML file must be in the application context (e.g.,\n    any path under `WEB-INF/src`).\n\n2.  The `portlet.properties` file must specify the cache file location. Either \n    of the two properties is used and is assigned the cache file path, relative\n    to the application context root (e.g., `WEB-INF/src`). \n\n```properties \nehcache.single.vm.config.location=path/to/single/vm/config/file\nehcache.multi.vm.config.location=path/to/multi/vm/config/file \n```\n\nFor example, here's the\n[`test-cache-configuration-portlet`](https://github.com/liferay/liferay-plugins/blob/7.0.x/portlets/test-cache-configuration-portlet)\nWAR's structure:\n\n-   `docroot/WEB-INF/src/`\n    -   `ehcache/`\n        -   [`liferay-single-vm-ext.xml`](https://github.com/liferay/liferay-plugins/blob/7.0.x/portlets/test-cache-configuration-portlet/docroot/WEB-INF/src/ehcache/liferay-single-vm-ext.xml)\n        -   [`liferay-multi-vm-clustered-ext.xml`](https://github.com/liferay/liferay-plugins/blob/7.0.x/portlets/test-cache-configuration-portlet/docroot/WEB-INF/src/ehcache/liferay-multi-vm-clustered-ext.xml)\n    -   `portlet.properties`\n\nThe `portlet.properties` file specifies these properties:\n\n```properties \nehcache.single.vm.config.location=ehcache/liferay-single-vm-ext.xml\nehcache.multi.vm.config.location=ehcache/liferay-multi-vm-clustered-ext.xml\n```\n\n## Cache Names and Registration\n\nA cache is identified by its name (e.g., `<cache name=\"com.liferay.docs.MyClass\"\n... />`). If a module provides a cache configuration with the name of an\nexisting cache, the existing cache is overridden. If a module provides a cache\nconfiguration with a new name, a new cache is added.\n\nHere's what happens behind the scenes: Liferay's cache manager checks the\nconfigurations. If a cache with the name already exists, the cache manager\nremoves it from Ehcache's cache registry and registers a new Ehcache into\nEhcache's cache registry. If the name is new, the Liferay cache manager just\nregisters a new Ehcache.\n\nCache names are arbitrary except for `EntityCache` and `FinderCache`. \n\n### EntityCache Names \n\n`EntityCache` uses this naming convention:\n\n`PREFIX + ENTITY_IMPL_CLASS_NAME`\n\nwhere the `PREFIX` is always this:\n\n```\ncom.liferay.portal.kernel.dao.orm.EntityCache.\n```\n\nFor example, the cache name for the `com.liferay.portal.kernel.model.User`\nentity starts with the `PREFIX` and ends with the implementation class name\n`com.liferay.portal.model.impl.UserImpl`:\n\n```\ncom.liferay.portal.kernel.dao.orm.EntityCache.com.liferay.portal.model.impl.UserImpl\n```\n\n### FinderCache Names \n\n`FinderCache` uses this naming convention:\n\n`PREFIX + ENTITY_IMPL_CLASS_NAME + [\".LIST1\"|\".LIST2\"]`\n\nwhere the `PREFIX` is always this:\n\n```\ncom.liferay.portal.kernel.dao.orm.FinderCache.\n```\n\nHere are the `FinderCache` types and their name patterns.\n\n| Type | Pattern | Example |\n| ---- | ------- | ------- |\n| Entity instances matching query parameters.  | `PREFIX + ENTITY_IMPL_CLASS_NAME` | `com.liferay.portal.kernel.dao.orm.FinderCache.com.liferay.portal.model.impl.ClassNameImpl` |\n| Paginated lists of entity instances matching query parameters.  | `PREFIX + ENTITY_IMPL_CLASS_NAME + \".List1\"` | `com.liferay.portal.kernel.dao.orm.FinderCache.com.liferay.portal.model.impl.ClassNameImpl.List1` |\n| Non-paginated lists of entity instances matching query parameters.  | `PREFIX + ENTITY_IMPL_CLASS_NAME + \".List2\"` | `com.liferay.portal.kernel.dao.orm.FinderCache.com.liferay.portal.model.impl.ClassNameImpl.List2` |\n\nNow that you have a basic understanding of cache in Liferay, continue with\noverriding an existing cache configuration or caching custom data. \n"
  },
  {
    "path": "en/developer/frameworks/articles/cache-configuration/02-overriding-cache.markdown",
    "content": "---\nheader-id: overriding-cache\n---\n\n# Overriding Cache\n\n[TOC levels=1-4]\n\n@product@ pre-configures cache for service entities, service entity finder\nresults, and cache for several other classes. You can tune existing cache to meet\nyour needs. For example, it may help to write cache overflow elements to disk,\nincrease the maximum number of cached elements, or make other adjustments. Using\na module and only one XML file, you can override cache configurations\ndynamically. \n\n| **Warning:** Modifying an Ehcache element flushes its cache. \n\nHere is how to override a cache configuration: \n\n1.  Identify the name of the cache you want to override. Existing cache \n    configurations and statistics (hit/miss counts and percentages) can be\n    examined at runtime through JMX. Using a tool that supports JMX analysis,\n    you can examine @product@'s cache configurations in the MBean of\n    `net.sf.ehcache`. Please note that the caches listed in the MBean are more\n    than what @product@'s cache configuration files specify because some caches\n    are created purely through Java code.\n\n    ![Figure 1: Caches configured in @product@ can be examined using JMX tools such as Zulu Mission Control  \\(Portal Process &rarr; MBean server  &rarr; MBean Browser\\)](../../images/zulu-mission-control.png)\n\n    | **Note:** See\n    | [Cache Names and Registration](/docs/7-2/frameworks/-/knowledge_base/f/cache-configuration#cache-names-and-registration)\n    | to identify `EntityCache` and the different kinds of `FinderCache` instances \n    | associated with service entities. \n\n    Some cache configurations can also be viewed statically in their deployment\n    artifacts or source code.\n\n    -   `liferay-*-vm.xml` files in the\n        `Liferay [version] Foundation - Liferay [version] Portal Cache - Impl.lpkg` file.\n\n    -   `module-*-vm.xml` files in modules or Liferay LPKG files.\n\n2.  If you don't own the existing project that specifies the cache or you want \n    to use a different project to configure the cache, create a module project.\n    Otherwise, edit the cache in the existing project. These instructions\n    demonstrate adding the cache configuration to a new module project. \n\n    | **Tip:** create new projects using the\n    | [API project template](/docs/7-2/reference/-/knowledge_base/r/api-template)\n    | and remove the Java class generated in the `src/main/java/` folder.\n\n3.  In the `src/main/resources/META-INF` folder, add an XML file for the type of\n    cache (multi-VM or single-VM) you're overriding.\n\n    `module-multi-vm.xml` file:\n\n    ```xml\n    <ehcache\n        dynamicConfig=\"true\"\n        monitoring=\"off\"\n        name=\"module-multi-vm\"\n        updateCheck=\"false\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:noNamespaceSchemaLocation=\"http://www.ehcache.org/ehcache.xsd\"\n    >\n        <!-- cache elements go here -->\n    </ehcache>\n    ```\n\n    `module-single-vm.xml` file:\n\n    ```xml\n    <ehcache\n        dynamicConfig=\"true\"\n        monitoring=\"off\"\n        name=\"module-single-vm\"\n        updateCheck=\"false\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:noNamespaceSchemaLocation=\"http://www.ehcache.org/ehcache.xsd\"\n    >\n        <!-- cache elements go here -->\n    </ehcache>\n    ```\n\n4.  In the `<ehcache/>` element, add a `<cache/>` element and set its `name`\n    attribute to the name of the cache you're overriding.\n\n5.  Specify all existing `<cache/>` element attributes you want to preserve. \n    Hint: view the attributes in an MBean browser, as mentioned earlier. \n\n6.  Add or modify attributes to meet your needs. The `<cache/>` element \n    attributes are described in the\n    [ehcache.xsd](http://www.ehcache.org/ehcache.xsd)\n    and\n    [Ehcache documentation](http://www.ehcache.org/documentation/2.8/configuration/index.html). \n\n7.  [Deploy the project](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project). \n\nCongratulations! Your cache modification is in effect. \n\n## Related Topics \n\n[Caching Data](/docs/7-2/frameworks/-/knowledge_base/f/caching-data)\n"
  },
  {
    "path": "en/developer/frameworks/articles/cache-configuration/03-caching-data.markdown",
    "content": "---\nheader-id: caching-data\n---\n\n# Caching Data\n\n[TOC levels=1-4]\n\n[Liferay's caching framework](/docs/7-2/frameworks/-/knowledge_base/f/cache-configuration)\nhelps you use Ehcache to cache any data. The [`SingleVMPool`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/SingleVMPool.html)\nand\n[`MultiVMPool`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/MultiVMPool.html)\nclasses use Liferay's \n[`PortalCache`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/PortalCache.html)\nutility. Storing and retrieving cached data objects is as easy as using a hash\nmap: you associate a key with every cache value. The following steps demonstrate\nimplementing data caching. \n\n| **Note:** If you want to modify cache for Service Builder Service Entities or \n| Entity Finder results, see\n| [Overriding Cache](/docs/7-2/frameworks/-/knowledge_base/f/overriding-cache).\n\n\n## Step 1: Determine Cache Pool Requirements \n\nThere are cache pools for single-VM and multi-VM environments. The pool types\nand some Ehcache features require using `Serializable` values. \n\n1.  Determine whether to create a cache\n    [in a single VM or across multiple VMs](/docs/7-2/frameworks/-/knowledge_base/f/cache-configuration)\n    (e.g., in a clustered environment).\n\n2.  Determine if it's necessary to serialize the data you're caching. \n\n    -   [`MultiVMPool`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/MultiVMPool.html)\n        requires both the cache key and cache value to be \n        [`Serializable`](https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html). \n\n    -   [`SingleVMPool`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/SingleVMPool.html)\n        typically requires only cache keys to be\n        [`Serializable`](https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html). \n        Note that some Ehache features, such as `overflowToDisk`, require\n        `Serializable` values too. \n\n## Step 2: Implement a Cache Key \n\nCache keys must be unique,\n[`Serializable`](https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html)\nobjects. They should relate to the values being cached. For example, in\n@product@'s `JournalContentImpl`, a `JournalContentKey` instance relates to each\ncached `JournalArticleDisplay` object. Here's the `JournalContentKey` class:\n\n```java\nprivate static class JournalContentKey implements Serializable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tJournalContentKey journalContentKey = (JournalContentKey)obj;\n\n\t\tif ((journalContentKey._groupId == _groupId) &&\n\t\t\tObjects.equals(journalContentKey._articleId, _articleId) &&\n\t\t\t(journalContentKey._version == _version) &&\n\t\t\tObjects.equals(\n\t\t\t\tjournalContentKey._ddmTemplateKey, _ddmTemplateKey) &&\n\t\t\t(journalContentKey._layoutSetId == _layoutSetId) &&\n\t\t\tObjects.equals(journalContentKey._viewMode, _viewMode) &&\n\t\t\tObjects.equals(journalContentKey._languageId, _languageId) &&\n\t\t\t(journalContentKey._page == _page) &&\n\t\t\t(journalContentKey._secure == _secure)) {\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint hashCode = HashUtil.hash(0, _groupId);\n\n\t\thashCode = HashUtil.hash(hashCode, _articleId);\n\t\thashCode = HashUtil.hash(hashCode, _version);\n\t\thashCode = HashUtil.hash(hashCode, _ddmTemplateKey);\n\t\thashCode = HashUtil.hash(hashCode, _layoutSetId);\n\t\thashCode = HashUtil.hash(hashCode, _viewMode);\n\t\thashCode = HashUtil.hash(hashCode, _languageId);\n\t\thashCode = HashUtil.hash(hashCode, _page);\n\n\t\treturn HashUtil.hash(hashCode, _secure);\n\t}\n\n\tprivate JournalContentKey(\n\t\tlong groupId, String articleId, double version,\n\t\tString ddmTemplateKey, long layoutSetId, String viewMode,\n\t\tString languageId, int page, boolean secure) {\n\n\t\t_groupId = groupId;\n\t\t_articleId = articleId;\n\t\t_version = version;\n\t\t_ddmTemplateKey = ddmTemplateKey;\n\t\t_layoutSetId = layoutSetId;\n\t\t_viewMode = viewMode;\n\t\t_languageId = languageId;\n\t\t_page = page;\n\t\t_secure = secure;\n\t}\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate final String _articleId;\n\tprivate final String _ddmTemplateKey;\n\tprivate final long _groupId;\n\tprivate final String _languageId;\n\tprivate final long _layoutSetId;\n\tprivate final int _page;\n\tprivate final boolean _secure;\n\tprivate final double _version;\n\tprivate final String _viewMode;\n\n}\n```\n\n`JournalContentKey`s constructor populates fields that collectively define\nunique keys for each piece of journal content. \n\nNote a cache key's characteristics:\n\n1.  A key instance's field values relate to the cached data and distinguish it \n    from other data instances.\n\n2.  A key follows `Serializable` class best practices. \n\n    -   Overrides `Object`'s `equals` and `hashcode` methods. \n    -   Includes a private static final long `serialVersionUID` field. It is to\n        be incremented when a new version of the class is incompatible with\n        previous versions. \n\nYour cache key class is ready for caching data values. \n\n## Step 3: Implement Cache Logic\n\nWhen your application creates or requests the data type you're caching, you must\nhandle getting existing data from cache and putting new/updated data into the\ncache. @product@'s caching classes are easy to inject into a \n[Declarative Services (DS) Component](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services),\nbut you can access them using\n[`ServiceTracker`](/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker)s\ntoo. These steps use fictitious key and value classes: `SomeKey` and `SomeValue`. \n\n1.  Name your cache. Cache names are arbitrary, but they must be unique in the \n    cache pool, and typically identify the data type being cached. \n\n```java \nprotected static final String CACHE_NAME = SomeValue.class.getName();\n```\n\n2.  Access the VM pool you're using.\n    [`MultiVMPool`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/MultiVMPool.html)\n    and\n    [`SingleVMPool`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/SingleVMPool.html)\n    are Declarative  Service (DS) components. To  access a pool from a DS\n    component, apply the\n    [`@Reference`](https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Reference.html)\n    annotation to a pool field (see below). Otherwise, use a\n    [`ServiceTracker`](/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker)\n    to access the pool. \n\n```java\n@Reference\nprivate MultiVMPool _multiVMPool;\n```\n\n3.  Declare a private static\n    [`PortalCache`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/cache/PortalCache.html)\n    instance. \n\n```java \nprivate static PortalCache<SomeKey, SomeValue> _portalCache;\n```\n\n4.  Initialize your `PortalCache` when your class is being activated or \n    initialized. If you're using a DS component, initialize the cache in your\n    component's activation method (annotated with\n    [`@Activate`](https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Activate.html)).\n    Get the cache from your VM pool using your cache name. For example, this DS\n    component's activation method gets a cache from the multi-VM pool. \n\n```java \n@Activate\npublic void activate() {\n    _portalCache =\n        (PortalCache<SomeKey, SomeValue>)\n            _multiVMPool.getPortalCache(CACHE_NAME);\n    ...\n}\n```\n\n5.  Similarly, remove your cache when your class instance is deactivated or \n    destroyed. If you're using a DS component, remove the cache in your\n    deactivation method (annotated with\n    [`@Deactivate`](https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Deactivate.html)).\n    Use the VM pool to remove the cache. \n\n```java \n@Deactivate\npublic void deactivate() {\n    _multiVMPool.removePortalCache(CACHE_NAME);\n}\n```\n\n6.  In your code that uses the cached data, implement your caching logic. \n    Here's some example code:\n\n```java \nSomeKey key = new SomeKey(...); \n\nSomeValue value = _portalCache.get(\n    key);\n\nif (value == null) {\n    value = createSomeValue(...);\n\n    _portalCache.put(key, value);\n}\n\n// continue using the data \n...\n```\n\nThe code above constructs a key based on the data being used. Then, the key is\nused to check the `PortalCache` for the data. If the cache doesn't have data\nassociated with the key, data is created and put it into the cache. The code\ncontinues using the cached data. Use similar logic for the data you are caching. \n\nConfiguring the cache and deploying your project is next. \n\n## Step 4: Configure the Cache \n\nIt's time to specify your Ehcache configuration. \n\n1.  Depending on the VM pool you're using, start your XML file in one of the\n    following ways. \n\nMulti VM file:\n\n```xml\n<ehcache\n    dynamicConfig=\"true\"\n    monitoring=\"off\"\n    name=\"module-multi-vm\"\n    updateCheck=\"false\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"http://www.ehcache.org/ehcache.xsd\"\n>\n    <!-- cache elements go here -->\n</ehcache>\n```\n\nSingle VM file: \n\n```xml\n<ehcache\n    dynamicConfig=\"true\"\n    monitoring=\"off\"\n    name=\"module-single-vm\"\n    updateCheck=\"false\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"http://www.ehcache.org/ehcache.xsd\"\n>\n    <!-- cache elements go here -->\n</ehcache>\n```\n\n2.  Add a `<cache>` element for the cache you're creating. Although the cache\n    name is arbitrary, using a name-spaced name such as a fully qualified class\n    name is a best practice. \n\n    Configure your `<cache>` element to fit your caching requirements. The\n    [ehcache.xsd](http://www.ehcache.org/ehcache.xsd)\n    and\n    [Ehcache documentation](http://www.ehcache.org/documentation/2.8/configuration/index.html)\n    describe the `<cache>` attributes.\n\n    For example, the Liferay Web Experience suite's `com.liferay.journal.service`\n    module uses this\n    [`module-multi-vm.xml`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/journal/journal-service/src/main/resources/META-INF/module-multi-vm.xml)\n    file to configure its cache named `com.liferay.journal.util.JournalContent`. \n\n```xml\n<ehcache\n    dynamicConfig=\"true\"\n    monitoring=\"off\"\n    name=\"module-multi-vm\"\n    updateCheck=\"false\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"http://www.ehcache.org/ehcache.xsd\"\n>\n    <cache\n        eternal=\"false\"\n        maxElementsInMemory=\"10000\"\n        name=\"com.liferay.journal.util.JournalContent\"\n        overflowToDisk=\"false\"\n        timeToIdleSeconds=\"600\"\n    >\n    </cache>\n</ehcache>\n```\n\n3.  Deploy your project. \n\nCongratulations! Your data cache is in effect. \n\n## Related Topics \n\n[Overriding Cache](/docs/7-2/frameworks/-/knowledge_base/f/overriding-cache)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/00-intro.markdown",
    "content": "---\nheader-id: collaboration\n---\n\n# Collaboration\n\n[TOC levels=1-4]\n\nUnderlying the \n[collaboration suite](/docs/7-2/user/-/knowledge_base/u/collaboration) \nis a set of powerful APIs that add collaboration features to your apps. For \nexample, if your app contains a custom content type, you can use the \ncollaboration suite's social API to enable comments and ratings for that \ncontent. You can also integrate your app with the Documents and Media Library, \nand much more. \n\nHere are a few of the things you can do with the collaboration suite's APIs. \n\n[TOC levels=4 hierarchy]\n\n## Item Selector\n\nAn *Item Selector* is a UI component for selecting entities in a user-friendly\nmanner. Many Liferay apps use Item Selectors to select items such as images, \nvideos, audio files, documents, and pages. For example, the Documents and Media \nItem Selector selects files. \n\n![Figure 1: Item Selectors select different kinds of entities.](../../images/item-selector-dialog-02.png)\n\nThe Item Selector API provides a framework for you to use, extend, and create \nItem Selectors in your apps. \n\nHere are some use cases for the Item Selector API: \n\n1.  Selecting entities with an Item Selector. \n\n2.  Configuring an Item Selector to select your app's custom entity. \n\n3.  Adding a new *selection view* to customize the selection experience.\n\n## Adaptive Media\n\nThe \n[Adaptive Media](/docs/7-2/user/-/knowledge_base/u/adapting-your-media-across-multiple-devices) \napp tailors the size and quality of images to the device displaying them. For \nexample, you can configure Adaptive Media to send large, high-resolution images \nonly to devices that can display them. Other devices get images that consume \nless bandwidth and processing power. \n\nBy default, Adaptive Media integrates with Documents and Media, Blogs, and Web \nContent. You can also integrate it with your apps. Adaptive Media contains a \ntaglib that displays the adapted image matching the file version you supply. You \ncan also use Adaptive Media's finder API if you need to get adapted images that \nmatch other criteria (e.g., a specific resolution, a range of attributes, etc.). \nYou can even customize the image scaling that Adaptive Media uses to produce \nadapted images. \n\n## Social API\n\nUsers interact with content via @product@'s social features. For example, users \ncan provide feedback on content, share that content with others, subscribe to \nreceive notifications, and more. Use the social API to enable such functionality \nin your apps. \n\nHere's an example of some functionality you can add to your apps via the social \nAPI: \n\n**Social Bookmarks:** Share content on social media. You can also create new \nsocial bookmarks if one doesn't exist for your social network of choice. \n\n**Comments:** Comment on content.\n\n**Ratings:** Rate content. Administrators can also change the rating type (e.g., \nlikes, stars, thumbs, etc.). \n\n**Flags:** Flag inappropriate content. \n\n## Documents and Media API\n\nUsers can use, manage, and share files in the Documents and Media Library. For \nexample, users can embed files in content, organize them in folders, edit and \ncollaborate on them with other users, and more. See the \n[user guide](/docs/7-2/user/-/knowledge_base/u/managing-documents-and-media) \nfor more information on the Documents and Media Library's features. \n\nA powerful API underlies the Documents and Media Library's functionality. You \ncan leverage this API in your apps. For example, you could create an app that \nuploads files to the Documents and Media Library. Your app could even update, \ndelete, and copy files. \n\nHere's an example of some things you can do with the Documents and Media API: \n\n-   Create files, folders, and shortcuts.\n-   Delete entities.\n-   Update entities.\n-   Check out files for editing, and check them back in.\n-   Copy and move entities.\n-   Get entities.\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/01-item-selector/00-intro.markdown",
    "content": "---\nheader-id: item-selector\n---\n\n# Item Selector\n\n[TOC levels=1-4]\n\nAn *Item Selector* is a UI component for selecting entities in a user-friendly \nmanner. \n\nHere's what you'll learn to do with Item Selectors: \n\n1.  Select Entities. \n\n2.  Create Custom Item Selector Criteria.\n\n3.  Create Custom Item Selector Views. \n\n![Figure 1: Item Selectors select entities.](../../../images/item-selector-dialog-02.png)\n\n## Understanding the Item Selector API's Components\n\nBefore working with the Item Selector API, you should learn about its \ncomponents. You'll work with these components as you leverage the API in your \napps: \n\n**Selection View:** A class that shows entities of particular types from \ndifferent sources. For example, an Item Selector configured to show images might \nshow selection views from Documents and Media, a third-party image provider, or \na drag-and-drop UI. Selection views are the framework's key components. \n\n**Markup:** A markup file that renders the selection view. You can choose from\nJSP, FreeMarker, or even pure HTML and JavaScript. \n\n**Return Type:** A class that represents the data type that entity selections \nreturn. For example, if users select images and you want to return the selected \nimage's URL, then you need a URL return type. Each return type class must \nimplement \n[`ItemSelectorReturnType`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorReturnType.html). \nSuch classes are named after the return type's data and suffixed with \n`ItemSelectorReturnType`. For example, the URL return type class is \n`URLItemSelectorReturnType`. The return type class is an API that connects the \nreturn type to the Item Selector's views. The Item Selector uses the return type \nclass, which is empty and returns no information, as an identifier. The view \nensures that the proper information is returned. If you create your own return \ntype, you should specify its data type and format in Javadoc. \n\n**Criterion:** A class that represents the selected entity. For example, if\nusers select images, you need an image criterion class. Each criterion class\nmust implement\n[`ItemSelectorCriterion`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorCriterion.html).\nSuch classes are named for the entity they represent and suffixed with\n`ItemSelectorCriterion`. For example, the criterion class for images is\n`ImageItemSelectorCriterion`. If you create your own criterion class, extend\n[`BaseItemSelectorCriterion`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/BaseItemSelectorCriterion.html).\nThis base class implements `ItemSelectorCriterion` and provides methods that\nhandle the Item Selector's return types. Your criterion class can therefore be\nempty, unless you also want to use it to pass information to the view. \n\nNote that criterion and return types together form an Item Selector's \n*criteria*. The Item Selector uses its criteria to decide which selection views \nto show. \n\n| **Note:** For a list of the criterion classes and return types that @product@\n| provides, see\n| [Item Selector Criterion and Return Types](/docs/7-2/reference/-/knowledge_base/r/item-selector-criterion-and-return-types).\n\n**Criterion Handler:** A class that gets the appropriate selection view. Each \ncriterion requires a criterion handler. Criterion handler classes extend \n[`BaseItemSelectorCriterionHandler`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/BaseItemSelectorCriterionHandler.html) \nwith the criterion's entity as a type argument. Criterion handler classes are \nnamed after the criterion's entity and suffixed by \n`ItemSelectorCriterionHandler`. For example, the image criterion handler class \nis `ImageItemSelectorCriterionHandler` and extends \n`BaseItemSelectorCriterionHandler<ImageItemSelectorCriterion>`. \n\n![Figure 2: Item Selector views (selection views) are determined by the return type and criterion, and rendered by the markup.](../../../images/item-selector-architecture.png)\n\n## Getting an Item Selector\n\nTo use an Item Selector with your criteria, you must get that Item Selector's \nURL. The URL is needed to open the Item Selector dialog in your UI. To get this \nURL, you must get an `ItemSelector` reference and call its \n[`getItemSelectorURL`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelector.html#getItemSelectorURL-com.liferay.portal.kernel.portlet.RequestBackedPortletURLFactory-java.lang.String-com.liferay.item.selector.ItemSelectorCriterion...-) \nmethod with the following parameters: \n\n`RequestBackedPortletURLFactory`: A factory that creates portlet URLs. \n\n`ItemSelectedEventName`: A unique, arbitrary JavaScript event name that the Item \nSelector triggers when the entity is selected. \n\n`ItemSelectorCriterion`: The criterion (or an array of criterion objects) that \nspecifies the type of entities to make available in the Item Selector. \n\nKeep these points in mind when getting an Item Selector's URL: \n\n-   You can invoke the URL object's `toString` method to get its value. \n\n-   You can configure an Item Selector to use any number of criterion. The \n    criterion can use any number of return types. \n\n-   The order of the Item Selector's criteria determines the selection view \n    order. For example, if you pass the Item Selector an \n    `ImageItemSelectorCriterion` followed by a `VideoItemSelectorCriterion`, the \n    Item Selector displays the image selection views first. \n\n-   The return type order is also significant. A view uses the first return type \n    it supports from each criterion's return type list. \n\n## Understanding Custom Selection Views\n\nThe default selection views may provide everything you need for your app. Custom \nselection views are required, however, for certain situations. For example, you \nmust create a custom selection view for your users to select images from an \nexternal image provider. \n\nThe selected entity type determines the view the Item Selector presents. The \nItem Selector can also render multiple views for the same entity type. For \nexample, several selection views are available for images. Each selection view \nis a tab in the UI that corresponds to the image's location. An \n`*ItemSelectorCriterion` class represents each selection view. \n\n![Figure 3: An entity type can have multiple selection views.](../../../images/item-selector-tabs.png)\n\n### The Selection View's Class\n\nThe criterion and return types determine the selection view's class. This class \nis an `ItemSelectorView` component class that implements \n[`ItemSelectorView`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html)\nparameterized with the view's criterion. Remember these things when creating\nthis class: \n\n-   Configure the title by implementing the \n    [`getTitle`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html#getTitle-java.util.Locale-) \n    method to return the localized title of the tab to display in the Item \n    Selector dialog. \n\n-   Configure the search options by implementing the \n    [`isShowSearch()`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html#isShowSearch--) \n    method to return whether your view should show the search field. To \n    implement search, this method must return `true`. The `renderHTML` method \n    indicates whether a user performed a search based on the value of the \n    `search` parameter. You can get the user's search keywords as follows: \n\n        String keywords = ParamUtil.getString(request, \"keywords\");\n\n-   Make your view visible by implementing the \n    [`isVisible()`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html#isVisible-com.liferay.portal.kernel.theme.ThemeDisplay-) \n    method to return `true`. Note that you can use this method to add \n    conditional logic to disable the view. \n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/01-item-selector/01-selecting-entities.markdown",
    "content": "---\nheader-id: selecting-entities-with-an-item-selector\n---\n\n# Selecting Entities with an Item Selector\n\n[TOC levels=1-4]\n\nThe steps here show you how to get and use an Item Selector to select entities\nin your app. For an explanation of the Item Selector API and more information on\nthese steps, see the [Item Selector introduction](/docs/7-2/frameworks/-/knowledge_base/f/item-selector).\n\n## Get an Item Selector\n\nFirst, you must get an Item Selector for your use case. Follow these steps:\n\n1.  Determine the criterion and return types for the Item Selector. The\n    criterion corresponds to the selected entity type, and the return types\n    correspond to the data you expect to receive from those selections. For a\n    list of the criterion and return types that @product@ provides, see\n    [Item Selector Criterion and Return Types](/docs/7-2/reference/-/knowledge_base/r/item-selector-criterion-and-return-types).\n    For example, if you need an Item Selector that selects images and returns\n    their URLs, use `ImageItemSelectorCriterion` and\n    `URLItemSelectorReturnType`. You can\n    [create](/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-criterion-and-return-types)\n    criterion and/or return types if there aren't existing ones for your use\n    case.\n\n2.  Use Declarative Services to get an `ItemSelector` OSGi Service Component:\n\n    ```java\n    import com.liferay.item.selector.ItemSelector;\n    import org.osgi.service.component.annotations.Reference;\n\n    ...\n\n    @Reference\n    private ItemSelector _itemSelector\n    ```\n\n    The component annotations are available in the module\n    [`org.osgi.service.component.annotations`](http://mvnrepository.com/artifact/org.osgi/org.osgi.service.component.annotations).\n\n3.  Create the factory you'll use to create the Item Selector's URL. To do this,\n    invoke the `RequestBackedPortletURLFactoryUtil.create` method with the\n    current request object. The request can be an `HttpServletRequest` or\n    `PortletRequest`:\n\n    ```java\n    RequestBackedPortletURLFactory requestBackedPortletURLFactory =\n        RequestBackedPortletURLFactoryUtil.create(request);\n    ```\n\n4.  Create a list of return types expected for the entity. For example, the\n    return types list here consists of `URLItemSelectorReturnType`:\n\n    ```java\n    List<ItemSelectorReturnType> desiredItemSelectorReturnTypes =\n        new ArrayList<>();\n    desiredItemSelectorReturnTypes.add(new URLItemSelectorReturnType());\n    ```\n\n5.  Create an object for the criterion. This example creates a new\n    `ImageItemSelectorCriterion`:\n\n    ```java\n    ImageItemSelectorCriterion imageItemSelectorCriterion =\n        new ImageItemSelectorCriterion();\n    ```\n\n6.  Use the criterion's `setDesiredItemSelectorReturnTypes` method to set the\n    return types list to the criterion:\n\n    ```java\n    imageItemSelectorCriterion.setDesiredItemSelectorReturnTypes(\n        desiredItemSelectorReturnTypes);\n    ```\n\n7.  Call the Item Selector's `getItemSelectorURL` method to get an Item Selector\n    URL for the criterion. The method requires the URL factory, an arbitrary\n    event name, and a series of criterion instances (one, in this case):\n\n    ```java\n    PortletURL itemSelectorURL = _itemSelector.getItemSelectorURL(\n        requestBackedPortletURLFactory, \"sampleTestSelectItem\",\n        imageItemSelectorCriterion);\n    ```\n8.  Add the `itemSelectorURL` to the request to be able to retrieve it from the JSP: `<code/>request.setAttribute(\"itemSelectorURL\", itemSelectorURL.toString())</code>\"`\n\n\n## Using the Item Selector Dialog\n\nTo open the Item Selector in your UI, you must use the JavaScript component\n`LiferayItemSelectorDialog` from\n[AlloyUI's](http://alloyui.com)\n`liferay-item-selector-dialog` module. The component listens for the item\nselected event that you specified for the Item Selector URL. The event returns\nthe selected element's information according to its return type.\n\nFollow these steps to use the Item Selector's dialog in a JSP:\n\n1.  Declare the AUI tag library:\n\n    ```java\n    <%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n    ```\n\n2.  Define the UI element you'll use to open the Item Selector dialog. For\n    example, this creates a *Choose* button with the ID `chooseImage`:\n\n    ```markup\n    <aui:button name=\"chooseImage\" value=\"Choose\" />\n    ```\n\n3.  Get the Item Selector's URL:\n\n    ```java\n    <%\n    String itemSelectorURL = GetterUtil.getString(request.getAttribute(\"itemSelectorURL\"));\n    %>\n    ```\n\n3.  Add the `<aui:script>` tag and set it to use the\n    `liferay-item-selector-dialog` module:\n\n    ```markup\n    <aui:script use=\"liferay-item-selector-dialog\">\n\n    </aui:script>\n    ```\n\n4.  Inside the `<aui:script>` tag, attach an event handler to the UI element you\n    created in step two. For example, this attaches a click event and a function\n    to the *Choose* button:\n\n    ```javascript\n    <aui:script use=\"liferay-item-selector-dialog\">\n\n        $('#<portlet:namespace />chooseImage').on(\n        'click',\n          function(event) {\n            <!-- function logic goes here -->\n          }\n        );\n\n    </aui:script>\n    ```\n\n    Inside the function, you must create a new instance of the\n    `LiferayItemSelectorDialog` AlloyUI component and configure it to use the\n    Item Selector. The next steps walk you through this.\n\n5.  Create the function's logic. First, create a new instance of the\n    Liferay Item Selector dialog:\n\n    ```javascript\n    var itemSelectorDialog = new A.LiferayItemSelectorDialog(\n        {\n            ...\n        }\n    );\n    ```\n\n6.  Inside the braces of the `LiferayItemSelectorDialog` constructor, first set\n    set the `eventName` attribute. This makes the dialog listen for the item\n    selected event. The event name is the Item Selector's event name that\n    you specified in your Java code (the code that gets the Item Selector URL):\n\n    ```javascript\n    eventName: 'ItemSelectedEventName',\n    ```\n\n7.  Immediately after the `eventName` setting, set the `on` attribute to\n    implement a function that operates on the selected item change. For example,\n    this function sets its variables for the newly selected item. The\n    information available to parse depends on the return type(s). As the comment\n    below indicates, you must add the logic for using the selected element:\n\n    ```javascript\n    on: {\n            selectedItemChange: function(event) {\n                var selectedItem = event.newVal;\n\n                if (selectedItem) {\n                    var itemValue = JSON.parse(\n                    selectedItem.value\n                    );\n                    itemSrc = itemValue.url;\n\n                    <!-- use item as needed -->\n                }\n            }\n    },\n    ```\n\n8.  Immediately after the `on` setting, set the `title` attribute to the\n    dialog's title:\n\n    ```javascript\n    title: '<liferay-ui:message key=\"select-image\" />',\n    ```\n\n9.  Immediately after the `title` setting, set the `url` attribute to the\n    previously retrieved Item Selector URL. This concludes the attribute\n    settings inside the `LiferayItemSelectorDialog` constructor:\n\n    ```javascript\n    url: '<%= itemSelectorURL.toString() %>'\n    ```\n\n10. To conclude the logic of the function from step four, open the Item Selector\n    dialog by calling its `open` method:\n\n    ```javascript\n    itemSelectorDialog.open();\n    ```\n\nWhen the user clicks the *Choose* button, a new dialog opens, rendering the Item\nSelector with the views that support the criterion and return type(s).\n\nHere's the complete example code for these steps:\n\n```javascript\n<%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n\n<aui:button name=\"chooseImage\" value=\"Choose\" />\n\n<%\nString itemSelectorURL = GetterUtil.getString(request.getAttribute(\"itemSelectorURL\"));\n%>\n\n<aui:script use=\"liferay-item-selector-dialog\">\n\n    $('#<portlet:namespace />chooseImage').on(\n        'click',\n        function(event) {\n            var itemSelectorDialog = new A.LiferayItemSelectorDialog(\n                {\n                    eventName: 'ItemSelectedEventName',\n                    on: {\n                            selectedItemChange: function(event) {\n                                var selectedItem = event.newVal;\n\n                                if (selectedItem) {\n                                    var itemValue = JSON.parse(\n                                    selectedItem.value\n                                    );\n                                    itemSrc = itemValue.url;\n\n                                    <!-- use item as needed -->\n                                }\n                            }\n                    },\n                    title: '<liferay-ui:message key=\"select-image\" />',\n                    url: '<%= itemSelectorURL.toString() %>'\n                }\n            );\n            itemSelectorDialog.open();\n        }\n    );\n</aui:script>\n```\n\n## Related Topics\n\n[Item Selector](/docs/7-2/frameworks/-/knowledge_base/f/item-selector)\n\n[Creating Custom Criterion and Return Types](/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-criterion-and-return-types)\n\n[Creating Custom Item Selector Views](/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-item-selector-views)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/01-item-selector/02-creating-criterion-return-types.markdown",
    "content": "---\nheader-id: creating-custom-criterion-and-return-types\n---\n\n# Creating Custom Criterion and Return Types\n\n[TOC levels=1-4]\n\nIf an existing criterion or return type doesn't fit your use case, you can\ncreate them. The steps here show you how. For more detailed information on Item\nSelector criterion and return types, see the \n[Item Selector introduction](/docs/7-2/frameworks/-/knowledge_base/f/item-selector). \n\n1.  Create your criterion class by extending \n    [`BaseItemSelectorCriterion`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/BaseItemSelectorCriterion.html). \n    Name the class after the entity it represents and suffix it with \n    `ItemSelectorCriterion`. You can use the class to pass information to the \n    view if needed. Otherwise, your criterion class can be empty. If you pass \n    information to the view, any fields in your criterion class should be \n    serializable and you should expose an empty public constructor. \n\n    For example, \n    [`JournalItemSelectorCriterion`](@app-ref@/web-experience/latest/javadocs/com/liferay/journal/item/selector/criterion/JournalItemSelectorCriterion.html) \n    is the criterion class for `Journal` entities (Web Content) and passes \n    primary key information to the view: \n\n    ```java\n    public class JournalItemSelectorCriterion extends BaseItemSelectorCriterion {\n\n            public JournalItemSelectorCriterion() {\n            }\n    \n            public JournalItemSelectorCriterion(long resourcePrimKey) {\n                    _resourcePrimKey = resourcePrimKey;\n            }\n    \n            public long getResourcePrimKey() {\n                    return _resourcePrimKey;\n            }\n    \n            public void setResourcePrimKey(long resourcePrimKey) {\n                    _resourcePrimKey = resourcePrimKey;\n            }\n    \n            private long _resourcePrimKey;\n    \n    }\n    ```\n\n2.  Create a criterion handler by creating an OSGi component class that \n    implements \n    [`BaseItemSelectorCriterionHandler`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/BaseItemSelectorCriterionHandler.html). \n    This example creates a criterion handler for the `TaskItemSelectorCriterion` \n    class. The `@Activate` and `@Override` tokens are required to activate this \n    OSGi component: \n\n    ```java\n    @Component(service = ItemSelectorCriterionHandler.class)\n    public class TaskItemSelectorCriterionHandler extends \n        BaseItemSelectorCriterionHandler<TaskItemSelectorCriterion> {\n\n        public Class <TaskItemSelectorCriterion> getItemSelectorCriterionClass() {\n            return TasksItemSelectorCriterionHandler.class;\n        }\n\n        @Activate\n        @Override\n        protected void activate(BundleContext bundleContext) {\n                super.activate(bundleContext);\n        }\n\n    }\n    ```\n\n3.  If you need a new return type, create it by implementing \n    [`ItemSelectorReturnType`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorReturnType.html). \n    Name your return type class after the return type's data and suffix it with \n    `ItemSelectorReturnType`. Specify the data and its format in Javadoc. Return \n    type classes need no content. For example, here's a return type for a task: \n\n    ```java\n    /**\n    * This return type should return the task ID and the user who\n    * created the task as a string.\n    *\n    * @author Joe Bloggs\n    */\n    public class TaskItemSelectorReturnType implements ItemSelectorReturnType{\n\n    }\n    ```\n\n## Related Topics\n\n[Item Selector](/docs/7-2/frameworks/-/knowledge_base/f/item-selector)\n\n[Creating Custom Item Selector Views](/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-item-selector-views)\n\n[Selecting Entities with an Item Selector](/docs/7-2/frameworks/-/knowledge_base/f/selecting-entities-with-an-item-selector)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/01-item-selector/03-creating-custom-views.markdown",
    "content": "---\nheader-id: creating-custom-item-selector-views\n---\n\n# Creating Custom Item Selector Views\n\n[TOC levels=1-4]\n\nYou can create your own selection view if an Item Selector doesn't contain the \none you need. The steps here show you how. For more information on custom \nselection views and the Item Selector API, see the \n[Item Selector introduction](/docs/7-2/frameworks/-/knowledge_base/f/item-selector). \n\n## Configuring Your Selection View's OSGi Module\n\nFirst, you must configure your selection view's OSGi module: \n\n1.  Add these dependencies to your module's `build.gradle`: \n\n    ```groovy\n    dependencies {\n            compileOnly group: \"com.liferay\", name: \"com.liferay.item.selector.api\", version: \"2.0.0\"\n            compileOnly group: \"com.liferay\", name: \"com.liferay.item.selector.criteria.api\", version: \"2.0.0\"\n            compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.impl\", version: \"2.0.0\"\n            compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"2.0.0\"\n            compileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\", version: \"2.0.0\"\n            compileOnly group: \"javax.portlet\", name: \"portlet-api\", version: \"2.0\"\n            compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\", version: \"3.0.1\"\n            compileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\", version: \"1.3.0\"\n    }\n    ```\n\n2.  Add your module's information to the `bnd.bnd` file. For example, this \n    configuration adds the information for a module called `My Custom View`: \n\n        Bundle-Name: My Custom View\n        Bundle-SymbolicName: com.liferay.docs.my.custom.view\n        Bundle-Version: 1.0.0\n \n3.  Add a `Web-ContextPath` to your `bnd.bnd` to point to your module's \n    resources: \n\n        Include-Resource:\\\n                META-INF/resources=src/main/resources/META-INF/resources\n        Web-ContextPath: /my-custom-view\n\n    If you don't have a `Web-ContextPath`, your module won't know where your \n    resources are. The `Include-Resource` header points to the relative path for \n    the module's resources. \n\n## Implementing Your Selection View's Class\n\nFollow these steps to implement your selection view's class: \n\n1.  Create an `ItemSelectorView` component class that implements \n    [`ItemSelectorView`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html) \n    with the criterion as a type argument. In the `@Component` annotation, set \n    the `item.selector.view.order` property to the order you want the view to \n    appear in when displayed alongside other selector views (lower values get \n    higher priority). \n\n    This example selector view class is for images, so it implements \n    `ItemSelectorView` with \n    [`ImageItemSelectorCriterion`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/criteria/image/criterion/ImageItemSelectorCriterion.html) \n    as a type argument. The `@Component` annotation sets the \n    `item.selector.view.order` property to `200` and registers the class as an \n    `ItemSelectorView` service: \n\n    ```java\n    @Component(\n        property = {\"item.selector.view.order:Integer=200\"},\n        service = ItemSelectorView.class\n    )\n    public class SampleItemSelectorView\n        implements ItemSelectorView<ImageItemSelectorCriterion> {...\n    ```\n\n2.  Create getter methods for the criterion class, servlet context, and return \n    types. Do this by implementing the methods \n    [`getItemSelectorCriterionClass()`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html#getItemSelectorCriterionClass--), \n    `getServletContext()`, and \n    [`getSupportedItemSelectorReturnTypes()`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html#getSupportedItemSelectorReturnTypes--), \n    respectively: \n\n    ```java\n    @Override\n    public Class<ImageItemSelectorCriterion> getItemSelectorCriterionClass() \n    {\n        return ImageItemSelectorCriterion.class;\n    }\n\n    @Override            \n    public ServletContext getServletContext() {\n        return _servletContext;\n    }\n\n    @Override            \n    public List<ItemSelectorReturnType> getSupportedItemSelectorReturnTypes() {\n        return _supportedItemSelectorReturnTypes;\n    }\n    ```\n\n3.  Configure the selection view's title, search options, and visibility \n    settings. Here's an example configuration for the `Sample Selector` \n    selection view: \n\n    ```java\n    @Override\n    public String getTitle(Locale locale) {\n        return \"Sample Selector\";\n    }\n\n    @Override\n    public boolean isShowSearch() {\n        return false;\n    }\n\n    @Override\n    public boolean isVisible(ThemeDisplay themeDisplay) {\n        return true;\n    }\n    ```\n\n    See [The Selection View's Class](/docs/7-2/frameworks/-/knowledge_base/f/item-selector#the-selection-views-class)\n    for more information on these methods. \n\n4.  Implement the \n    [`renderHTML`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/ItemSelectorView.html#renderHTML-javax.servlet.ServletRequest-javax.servlet.ServletResponse-T-javax.portlet.PortletURL-java.lang.String-boolean-) \n    method to set your view's render settings and render its markup. \n\n    Here's an example implementation of a `renderHTML` method that points to a \n    JSP file (`sample.jsp`) to render the view. Note that \n    `itemSelectedEventName` is passed as a request attribute so it can be used \n    in the view markup. The view markup is specified via the `ServletContext` \n    method `getRequestDispatcher`. Although this example uses a JSP, you can \n    render the markup in another language such as FreeMarker. \n\n    ```java\n    @Override\n    public void renderHTML(\n        ServletRequest request, ServletResponse response,\n        ImageItemSelectorCriterion itemSelectorCriterion,\n        PortletURL portletURL, String itemSelectedEventName,\n        boolean search\n    )\n    throws IOException, ServletException {\n\n        request.setAttribute(_ITEM_SELECTED_EVENT_NAME,\n            itemSelectedEventName);\n\n        ServletContext servletContext = getServletContext();\n\n        RequestDispatcher requestDispatcher =\n            servletContext.getRequestDispatcher(\"/sample.jsp\");\n\n        requestDispatcher.include(request, response);\n    }\n    ```\n\n5.  Use the `@Reference` annotation to reference your module's class \n    for the `setServletContext` method. In the annotation, use the `target` \n    parameter to specify the available services for the servlet context. This \n    example uses the `osgi.web.symbolicname` property to specify the \n    `com.liferay.selector.sample.web` class as the default value. You should \n    also use the `unbind = _` parameter to specify that there's no unbind method \n    for this module. In the method body, set the servlet context variable: \n\n    ```java\n    @Reference(\n        target =\n        \"(osgi.web.symbolicname=com.liferay.item.selector.sample.web)\",\n        unbind = \"-\"\n    )\n    public void setServletContext(ServletContext servletContext) {\n        _servletContext = servletContext;\n    }\n    ```\n\n6.  Define the `_supportedItemSelectorReturnTypes` list with the return types \n    that this view supports (you referenced this list in step two). This example \n    adds \n    [`URLItemSelectorReturnType`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/criteria/URLItemSelectorReturnType.html) \n    and \n    [`FileEntryItemSelectorReturnType`](@app-ref@/collaboration/latest/javadocs/com/liferay/item/selector/criteria/FileEntryItemSelectorReturnType.html) \n    to the list of supported return types (you can use more if needed). More \n    return types means that the view is more reusable. Also note that this \n    example defines its servlet context variable at the bottom of the file: \n\n    ```java\n    private static final List<ItemSelectorReturnType>\n        _supportedItemSelectorReturnTypes =\n        Collections.unmodifiableList(\n            ListUtil.fromArray(\n                new ItemSelectorReturnType[] {\n                    new FileEntryItemSelectorReturnType(),\n                    new URLItemSelectorReturnType()\n                }));\n\n     private ServletContext _servletContext;\n    ```\n\nFor a real-world example of a view class, see \n[`SiteNavigationMenuItemItemSelectorView`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/site-navigation/site-navigation-item-selector-web/src/main/java/com/liferay/site/navigation/item/selector/web/internal/SiteNavigationMenuItemItemSelectorView.java). \n\n## Writing Your View Markup\n\nYou can write your view markup however you wish---there's no typical or average \ncase. You can write it with taglibs, AUI components, or even pure HTML and\nJavaScript. The markup must do two key things: \n\n-   Render the entities for the user to select. \n-   When an entity is selected, pass the return type information via a \n    JavaScript event. \n\nThe example view class in the previous section passes the JavaScript event name \nas a request attribute in the `renderHTML` method. You can therefore use this \nevent name in the markup: \n\n```javascript\nLiferay.fire(\n        `<%= {ITEM_SELECTED_EVENT_NAME} %>',\n\n        {\n            data:{\n                the-data-your-client-needs-according-to-the-return-type\n            }\n        }\n);\n```\n\nFor a complete, real-world example, see \n[`layouts.jsp`](https://github.com/liferay/liferay-portal/blob/7.0.x/modules/apps/web-experience/layout/layout-item-selector-web/src/main/resources/META-INF/resources/layouts.jsp)\nfor the module \n[`com.liferay.layout.item.selector.web`](https://github.com/liferay/liferay-portal/tree/7.0.x/modules/apps/web-experience/layout/layout-item-selector-web). \nEven though this example is for a previous version of @product@, it still \napplies to @product-ver@. Here's a walkthrough of this `layouts.jsp` file: \n\n1.  First, some variables are defined. Note that \n    `LayoutItemSelectorViewDisplayContext` is an optional class that contains \n    additional information about the criteria and view: \n\n    ```java\n    <%\n    LayoutItemSelectorViewDisplayContext layoutItemSelectorViewDisplayContext = \n        (LayoutItemSelectorViewDisplayContext)request.getAttribute(\n        BaseLayoutsItemSelectorView.LAYOUT_ITEM_SELECTOR_VIEW_DISPLAY_CONTEXT);\n\n    LayoutItemSelectorCriterion layoutItemSelectorCriterion = \n        layoutItemSelectorViewDisplayContext.getLayoutItemSelectorCriterion();\n\n    Portlet portlet = PortletLocalServiceUtil.getPortletById(company.getCompanyId(), \n        portletDisplay.getId());\n    %>\n    ```\n\n2.  This snippet imports a CSS file for styling and places it in the `<head>` of \n    the page: \n\n    ```markup\n    <liferay-util:html-top>\n            <link href=\"<%= PortalUtil.getStaticResourceURL(\n            request, application.getContextPath() + \"/css/main.css\", \n            portlet.getTimestamp()) \n            %>\" rel=\"stylesheet\" type=\"text/css\" />\n    </liferay-util:html-top>\n    ```\n\n    You can learn more about using the `liferay-util` taglibs in \n    [Using the Liferay Util Taglib](/docs/7-2/reference/-/knowledge_base/r/using-the-liferay-util-taglib). \n\n3.  This snippet creates the UI to display the layout entities. It uses the \n    [`liferay-layout:layouts-tree`](@app-ref@/layout/latest/taglibdocs/liferay-layout/layouts-tree.html) \n    taglib along with the \n    [Lexicon](https://lexicondesign.io/) \n    design language to create \n    [cards](https://clayui.com/docs/components/cards.html): \n\n    ```html\n    <div class=\"container-fluid-1280 layouts-selector\">\n        <div class=\"card-horizontal main-content-card\">\n                <div class=\"card-row card-row-padded\">\n                        <liferay-layout:layouts-tree\n                                checkContentDisplayPage=\"<%= layoutItemSelectorCriterion.isCheckDisplayPage() %>\"\n                                draggableTree=\"<%= false %>\"\n                                expandFirstNode=\"<%= true %>\"\n                                groupId=\"<%= scopeGroupId %>\"\n                                portletURL=\"<%= layoutItemSelectorViewDisplayContext.getEditLayoutURL() %>\"\n                                privateLayout=\"<%= layoutItemSelectorViewDisplayContext.isPrivateLayout() %>\"\n                                rootNodeName=\"<%= layoutItemSelectorViewDisplayContext.getRootNodeName() %>\"\n                                saveState=\"<%= false %>\"\n                                selectedLayoutIds=\"<%= layoutItemSelectorViewDisplayContext.getSelectedLayoutIds() %>\"\n                                selPlid=\"<%= layoutItemSelectorViewDisplayContext.getSelPlid() %>\"\n                                treeId=\"treeContainer\"\n                        />\n                </div>\n        </div>\n    </div>\n     ```\n \n    This renders the following UI: \n\n    ![Figure 1: The Layouts Item Selector view uses Lexicon and Liferay Layout taglibs to create the UI.](../../../images/layouts-item-selector-view.png)\n\n4.  This portion of the `aui:script` returns the path for the page:\n\n    ```javascript\n    <aui:script use=\"aui-base\">\n        var LString = A.Lang.String;\n\n        var getChosenPagePath = function(node) {\n                var buffer = [];\n\n                if (A.instanceOf(node, A.TreeNode)) {\n                        var labelText = LString.escapeHTML(node.get('labelEl').text());\n\n                        buffer.push(labelText);\n\n                        node.eachParent(\n                                function(treeNode) {\n                                        var labelEl = treeNode.get('labelEl');\n\n                                        if (labelEl) {\n                                                labelText = LString.escapeHTML(labelEl.text());\n\n                                                buffer.unshift(labelText);\n                                        }\n                                }\n                        );\n                }\n\n                return buffer.join(' > ');\n        };\n    ```\n\n5.  The following snippet passes the return type data when the layout (entity) \n    is selected. Note the `url` and `uuid` variables retrieve the URL or UUID \n    for the layout: \n\n    ```javascript\n    var setSelectedPage = function(event) {\n            var disabled = true;\n\n            var messageText = '<%= UnicodeLanguageUtil.get(request, \"there-is-no-selected-page\") %>';\n\n            var lastSelectedNode = event.newVal;\n\n            var labelEl = lastSelectedNode.get('labelEl');\n\n            var link = labelEl.one('a');\n\n            var url = link.attr('data-url');\n            var uuid = link.attr('data-uuid');\n\n            var data = {};\n\n            if (link && url) {\n                    disabled = false;\n\n                    data.layoutpath = getChosenPagePath(lastSelectedNode);\n    ```\n\n6.  This checks if the return type information is a URL or a UUID. It then sets \n    the value for the JSON object's `data` attribute accordingly. The last line \n    adds the `CKEditorFuncNum` for the editor to the JSON object's `data` \n    attribute: \n\n    ```markup\n            <c:choose>\n                    <c:when test=\"<%= Objects.equals(layoutItemSelectorViewDisplayContext.getItemSelectorReturnTypeName(), URLItemSelectorReturnType.class.getName()) %>\">\n                            data.value = url;\n                    </c:when>\n                    <c:when test=\"<%= Objects.equals(layoutItemSelectorViewDisplayContext.getItemSelectorReturnTypeName(), UUIDItemSelectorReturnType.class.getName()) %>\">\n                            data.value = uuid;\n                    </c:when>\n            </c:choose>\n    }\n\n    <c:if test=\"<%= Validator.isNotNull(layoutItemSelectorViewDisplayContext.getCkEditorFuncNum()) %>\">\n            data.ckeditorfuncnum: <%= layoutItemSelectorViewDisplayContext.getCkEditorFuncNum() %>;\n    </c:if>\n    ```\n\n    The `data-url` and `data-uuid` attributes are in the HTML for the Layouts \n    Item Selector. The HTML for an instance of the Layouts Item Selector is \n    shown here: \n\n    ![Figure 2: The URL and UUID can be seen in the `data-url` and `data-uuid` attributes of the Layout Item Selector's HTML.](../../../images/layouts-item-selector-html.png)\n\n7.  The JavaScript trigger event specified in the Item Selector return type is \n    fired, passing the data JSON object with the required return type \n    information: \n\n    ```javascript\n            Liferay.Util.getOpener().Liferay.fire(\n                    '<%= layoutItemSelectorViewDisplayContext.getItemSelectedEventName() %>',\n                    {\n                            data: data\n                    }\n            );\n    };\n    ```\n\n8.  Finally, the layout is set to the selected page: \n\n    ```javascript\n        var container = A.one('#<portlet:namespace />treeContainerOutput');\n\n        if (container) {\n                container.swallowEvent('click', true);\n\n                var tree = container.getData('tree-view');\n\n                tree.after('lastSelectedChange', setSelectedPage);\n        }\n    </aui:script>\n    ```\n\nYour new selection view is automatically rendered by the Item Selector in every \napp that uses the criterion and return types you defined, without modifying \nanything in those apps. \n\n## Related Topics\n\n[Item Selector](/docs/7-2/frameworks/-/knowledge_base/f/item-selector)\n\n[Creating Custom Criterion and Return Types](/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-criterion-and-return-types)\n\n[Selecting Entities with an Item Selector](/docs/7-2/frameworks/-/knowledge_base/f/selecting-entities-with-an-item-selector)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/00-intro.markdown",
    "content": "---\nheader-id: documents-and-media-api\n---\n\n# Documents and Media API\n\n[TOC levels=1-4]\n\nA powerful API underlies the \n[Documents and Media library](/docs/7-2/user/-/knowledge_base/u/managing-documents-and-media). \nYou can leverage this API in your own apps. For example, you could create an app \nthat lets users upload files to the Documents and Media library. Your app could \neven let users update, delete, and copy files. \n\nHere, you'll learn how to use the Documents and Media library's API. Note that \nthis is a large API and it may seem daunting at first. To keep backwards \ncompatibility, the API has different entry points and multiple methods or \nclasses with similar functionality. Fortunately, you don't need to learn all of \nthem. The content here focuses on the API's most useful classes and methods. \n\nAlso note that the Documents and Media app is itself a consumer of this \nAPI---Liferay's developers used the API to implement the app's functionality.\nTherefore, code from this app is used as an example of how to use the API. \n\n## Getting Started with the Documents and Media API\n\nBefore you start using the Documents and Media API, you must learn these things: \n\n[**Key Interfaces:**](#key-interfaces) \nThe interfaces you'll use most while using the API. \n\n[**Getting a Service Reference:**](#getting-a-service-reference) \nA service reference is required for calling the API's services. \n\n[**Specifying Repositories:**](#specifying-repositories) \nHow to specify which Documents and Media repository to work with. \n\n[**Specifying Folders:**](#specifying-folders)\nHow to specify which Documents and Media folder to work with. \n\n## Key Interfaces\n\nThe Documents and Media API contains several key interfaces: \n\n**Documents and Media Services:** These interfaces expose all the available \nDocuments and Media functionality: \n\n-   [`DLAppLocalService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppLocalService.html): \n    The local service. \n-   [`DLAppService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html): \n    The remote service. This service wraps the local service methods in \n    permission checks. \n\n    Note that Liferay used \n    [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder) \n    to create these services. Because the remote service contains permission \n    checks, it's a best practice to call it instead of the local service. See \n    <!--add link back for 'best practice' once \n    creating-remote-services#using-service-builder-to-generate-remote-services \n    article is available\n    -->\n    below for instructions on getting a service reference. \n\n**Entity Interfaces:** These interfaces represent entities in the Documents and \nMedia library. Here are the primary ones you'll use: \n\n-   `FileEntry`: Represents a file. \n-   `Folder`: Represents a folder. \n-   `FileShortcut`: Represents a shortcut to a file. \n\n## Getting a Service Reference\n\nBefore you can do anything with the Documents and Media API, you must get a \nservice reference. If you're using OSGi modules, use the `@Reference` annotation \nto \n[get a service reference in an OSGi component via Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services).\nFor example, this code gets a reference to `DLAppService`: \n\n```java\n@Reference\nprivate DLAppService _dlAppService;\n```\n\nIf you're using a standard web module (WAR file), use a \n[Service Tracker](/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker) \nto get a reference to the service instead. \n\nGetting the reference this way ensures that you leverage OSGi's \n[dependency management](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies)\nfeatures. If you must use the Documents and Media services outside of an OSGi\ncomponent (e.g., in a JSP), then you can use the services' static `*Util` \nclasses: \n\n-   [`DLAppServiceUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppServiceUtil.html)\n-   [`DLAppLocalServiceUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppLocalServiceUtil.html)\n\n## Specifying Repositories\n\nMany methods in the Documents and Media API contain a `repositoryId` parameter\nthat identifies the Documents and Media repository where the operation is\nperformed. A Site (group) can have multiple repositories, but only one can be\naccessed via the portal UI. This is called the Site repository, which is \neffectively a Site's default repository. To access this repository via the API, \nprovide the group ID as the `repositoryId`. \n\nYou can also get the `repositoryId` via file (`FileEntry`), folder (`Folder`), \nand file shortcut (`FileShortcut`) entities. Each of these entities has a \n`getRepositoryId` method that gets its repository's ID. For example, this code \ngets the repository ID of the `FileEntry` object `fileEntry`: \n\n```java\nlong repositoryId = fileEntry.getRepositoryId();\n```\n\nThere may also be cases that require a `Repository` object. You can get one by \ncreating a `RepositoryProvider` reference and passing the repository ID to its \n`getRepository` method: \n\n```java\n@Reference\nprivate RepositoryProvider repositoryProvider;\n\nRepository repository = repositoryProvider.getRepository(repositoryId);\n```\n\nEven if you only have an entity ID (e.g., a file or folder ID), you can still \nuse `RepositoryProvider` to get a `Repository` object. To do so, call the \n`RepositoryProvider` method for the entity type with the entity ID as its \nargument. For example, this code gets a folder's `Repository` by calling the \n`RepositoryProvider` method `getFolderRepository` with the folder's ID: \n\n```java\nRepository repository = repositoryProvider.getFolderRepository(folderId);\n```\n\nSee the `RepositoryProvider` \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/repository/RepositoryProvider.html) \nfor a list of the methods for other entity types. \n\nNote that there are ways to create repositories programmatically, including \nrepositories private to specific apps. For simplicity, however, the examples \nhere access the default site repository. \n\n## Specifying Folders\n\nMany API methods require the ID of a folder that they perform operations in or \non. For example, such methods may contain parameters like `folderId` or \n`parentFolderId`. Also note that you can use the constant \n`DLFolderConstants.DEFAULT_PARENT_FOLDER_ID` to specify the root folder of your \ncurrent repository. \n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/01-creating-entities/00-intro.markdown",
    "content": "---\nheader-id: creating-files-folders-and-shortcuts\n---\n\n# Creating Files, Folders, and Shortcuts\n\n[TOC levels=1-4]\n\nA primary use case for the Docs &amp; Media API is to create files, folders, and \nfile shortcuts in the Documents and Media library. \n\nIf you've used other Liferay APIs, the Docs &amp; Media API follows the same \nconventions. In general, methods that do similar things have similar names. When \nyou must create an entity (whatever it is), look for methods that follow the \npattern `add[ModelName]`, where `[ModelName]` is the name of the entity's data \nmodel object. As the \n[intro](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api)\nexplains, you'll use \n[`DLAppService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html) \nto access the API. This service object contains the methods for adding these \nentities: \n\n-   [Files](#files)\n-   [Folders](#folders)\n-   [File Shortcuts](#file-shortcuts)\n\n## Files\n\nTo create files (`FileEntry` entities) in the Documents and Media library, you \nmust use the \n[`DLAppService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html) \ninterface's `addFileEntry` methods. There are three such methods, and they \ndiffer by the data type used to create the file. Click each method to see a \nfull description of the method and its parameters: \n\n-   [`addFileEntry(..., byte[] bytes, ...)`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#addFileEntry-long-long-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-byte:A-com.liferay.portal.kernel.service.ServiceContext-) \n\n-   [`addFileEntry(..., File file, ...)`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#addFileEntry-long-long-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.io.File-com.liferay.portal.kernel.service.ServiceContext-)\n\n-   [`addFileEntry(..., InputStream is, long size, ...)`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#addFileEntry-long-long-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.io.InputStream-long-com.liferay.portal.kernel.service.ServiceContext-)\n\nNote that the following arguments are optional: \n\n`sourceFileName`: This keeps track of the uploaded file. It infers the content \ntype if that file has an extension. \n\n`mimeType`: Defaults to a binary stream. If omitted, Documents and Media tries \nto infer the type from the file extension. \n\n`description`: The file's description to display in the portal. \n\n`changeLog`: Descriptions for file versions. \n\n`is` and `size`: In the method that takes an `InputStream`, you can use `null` \nfor the `is` parameter. If you do this, however, you must use `0` for the `size` \nparameter. \n\nFor step-by-step instructions on creating files with `addFileEntry`, see \n[Creating Files](/docs/7-2/frameworks/-/knowledge_base/f/creating-files). \n\n## Folders\n\nTo create folders (`Folder` entities) in the Documents and Media library, you \nmust use the \n[`DLAppService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html) \ninterface's `addFolder` method: \n\n```java\naddFolder(long repositoryId, \n        long parentFolderId, \n        String name, \n        String description, \n        ServiceContext serviceContext)\n```\n\nSee this method's \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#addFolder-long-long-java.lang.String-java.lang.String-com.liferay.portal.kernel.service.ServiceContext-) \nfor a description of the parameters. Note that the `description` parameter is \noptional. \n\nFor step-by-step instructions on creating folders with `addFolder`, see \n[Creating Folders](/docs/7-2/frameworks/-/knowledge_base/f/creating-folders). \n\n### Folders and External Repositories\n\nBy creating a folder that acts as a proxy for an external repository (e.g., \nSharePoint), you can effectively mount that repository inside a Site's default \nrepository. When users enter this special folder, they see the external \nrepository. These folders are called *mount points*. You can create one via the \nAPI by setting the Service Context's `mountPoint` attribute to `true`, and then \n<!--Add link back for 'Service Context's' once understanding-servicecontext \narticle is available\n-->\nusing that Service Context in the `addFolder` method: \n\n```java\nserviceContext.setAttribute(\"mountPoint\", true);\n```\n\nNote that the `repositoryId` of such a folder indicates the external repository \nthe folder points to---not the repository the folder exists in. Also, mount \npoint folders can only exist in the default Site repository. \n\n## File Shortcuts\n\nTo create file shortcuts (`FileShortcut` entities) in the Documents and Media \nlibrary, you must use the \n[`DLAppService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html) \ninterface's `addFileShortcut` method: \n\n```java\naddFileShortcut(long repositoryId, \n                long folderId, \n                long toFileEntryId, \n                ServiceContext serviceContext)\n```\n\nSee this method's \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#addFileShortcut-long-long-long-com.liferay.portal.kernel.service.ServiceContext-) \nfor a description of the parameters. Note that all this method's parameters are \nmandatory. \n\nKeep these things in mind when creating shortcuts: \n\n-   You can create a shortcut to a file in a different Site, if that file and \n    its resulting shortcut are in the same portal instance. \n-   You can't create folder shortcuts. \n-   Shortcuts can only exist in the default Site repository. If you try to \n    invoke `addFileShortcut` with an external repository's ID (e.g., \n    a SharePoint repository), the operation fails. Because not all repositories\n    have the same features, the Documents and Media API only supports the\n    common denominators for all repositories: files and folders. \n\nFor step-by-step instructions on creating file shortcuts with `addFileShortcut`, \nsee \n[Creating File Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/creating-file-shortcuts). \n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/01-creating-entities/01-creating-files.markdown",
    "content": "---\nheader-id: creating-files\n---\n\n# Creating Files\n\n[TOC levels=1-4]\n\nTo create a file via the Documents and Media API, use one of the overloaded \n`addFileEntry` methods in \n[`DLAppService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html). \nThe steps here show you how to do this, using the method that contains \n`InputStream` as an example. For detailed information on this and other \n`addFileEntry` methods, see \n[Creating Files, Folders, and Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/creating-files-folders-and-shortcuts). \nFor general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to create a file via the Documents and Media API: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the `addFileEntry` method's arguments. Since \n    it's common to create a file with data submitted by the end user, you can \n    extract the data from the request. This example does so via \n    [`UploadPortletRequest`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/upload/UploadPortletRequest.html) \n    and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can get the data any way you wish: \n\n    ```java\n    long repositoryId = ParamUtil.getLong(uploadPortletRequest, \"repositoryId\");\n    long folderId = ParamUtil.getLong(uploadPortletRequest, \"folderId\");\n    String sourceFileName = uploadPortletRequest.getFileName(\"file\");\n    String title = ParamUtil.getString(uploadPortletRequest, \"title\");\n    String description = ParamUtil.getString(uploadPortletRequest, \"description\");\n    String changeLog = ParamUtil.getString(uploadPortletRequest, \"changeLog\");\n    boolean majorVersion = ParamUtil.getBoolean(uploadPortletRequest, \"majorVersion\");\n\n    try (InputStream inputStream = uploadPortletRequest.getFileAsStream(\"file\")) {\n\n        String contentType = uploadPortletRequest.getContentType(\"file\");\n        long size = uploadPortletRequest.getSize(\"file\");\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n                DLFileEntry.class.getName(), uploadPortletRequest);\n    }\n    ```\n<!--Uncomment once article is available\n    For more information on `ServiceContext`, see \n    Understanding ServiceContext. \n-->\n\n3.  Call the service reference's `addFileEntry` method with the data from the \n    previous step. Note that this example does so inside the previous step's \n    try-with-resources statement: \n\n    ```java\n    try (InputStream inputStream = uploadPortletRequest.getFileAsStream(\"file\")) {\n\n        ...\n\n        FileEntry fileEntry = _dlAppService.addFileEntry(\n                            repositoryId, folderId, sourceFileName, contentType, title, \n                            description, changeLog, inputStream, size, serviceContext);\n    }\n    ```\n\n    The method returns a `FileEntry` object, which this example sets to a \n    variable for later use. Note, however, that you don't have to do this. \n\nYou can find the full code for this example in the `updateFileEntry` method of \n@product@'s \n[`EditFileEntryMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`FileEntry` actions that the Documents and Media app supports. Also note that \nthis `updateFileEntry` method, as well as the rest of \n`EditFileEntryMVCActionCommand`, contains additional logic to suit the specific \nneeds of the Documents and Media app. \n\n## Related Topics\n\n[Updating Files](/docs/7-2/frameworks/-/knowledge_base/f/updating-files)\n\n[Deleting Files](/docs/7-2/frameworks/-/knowledge_base/f/deleting-files)\n\n[Moving Folders and Files](/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files)\n\n[Creating Folders](/docs/7-2/frameworks/-/knowledge_base/f/creating-folders)\n\n[Creating File Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/creating-file-shortcuts)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/01-creating-entities/02-creating-folders.markdown",
    "content": "---\nheader-id: creating-folders\n---\n\n# Creating Folders\n\n[TOC levels=1-4]\n\nTo create folders (`Folder` entities) in the Documents and Media library, you \nmust use the \n[`DLAppService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html) \ninterface's `addFolder` method. The steps here show you how to do this. For more \ndetailed information, see \n[Creating Files, Folders, and Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/creating-files-folders-and-shortcuts). \nFor general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to create a folder with the `DLAppService` method \n`addFolder`: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the `addFolder` method's arguments. Since \n    it's common to create a folder with data submitted by the end user, you can \n    extract the data from the request. This example does so via \n    `javax.portlet.ActionRequest` and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html): \n\n    ```java\n    long repositoryId = ParamUtil.getLong(actionRequest, \"repositoryId\");\n    long parentFolderId = ParamUtil.getLong(actionRequest, \"parentFolderId\");\n    String name = ParamUtil.getString(actionRequest, \"name\");\n    String description = ParamUtil.getString(actionRequest, \"description\");\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n                DLFolder.class.getName(), actionRequest);\n    ```\n\n<!--Uncomment once article is available\n    For more information on `ServiceContext`, see the tutorial \n    Understanding ServiceContext. \n-->\n\n3.  Call the service reference's `addFolder` method with the data from the \n    previous step: \n\n    ```java\n    Folder folder = _dlAppService.addFolder(\n                            repositoryId, parentFolderId, name, description, \n                            serviceContext);\n    ```\n\n    The method returns a `Folder` object, which this example sets to a variable \n    for later use. Note, however, that you don't have to do this. \n\nYou can find the full code for this example in the `updateFolder` method of \n@product@'s \n[`EditFolderMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFolderMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`Folder` actions that the Documents and Media app supports. Also note that \nthis `updateFolder` method, as well as the rest of `EditFolderMVCActionCommand`, \ncontains additional logic to suit the specific needs of the Documents and Media \napp. \n\n## Related Topics\n\n[Updating Folders](/docs/7-2/frameworks/-/knowledge_base/f/updating-folders)\n\n[Deleting Folders](/docs/7-2/frameworks/-/knowledge_base/f/deleting-folders)\n\n[Copying Folders](/docs/7-2/frameworks/-/knowledge_base/f/copying-folders)\n\n[Moving Folders and Files](/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/01-creating-entities/03-creating-file-shortcuts.markdown",
    "content": "---\nheader-id: creating-file-shortcuts\n---\n\n# Creating File Shortcuts\n\n[TOC levels=1-4]\n\nTo create file shortcuts (`FileShortcut` entities) in the Documents and Media \nlibrary, you must use the \n[`DLAppService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html) \ninterface's `addFileShortcut` method. The steps here show you how to do this. \nFor more detailed information, see \n[Creating Files, Folders, and Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/creating-files-folders-and-shortcuts). \nFor general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to create a file shortcut with the `DLAppService` method \n`addFileShortcut`: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the `addFileShortcut` method's arguments. \n    Since it's common to create a file shortcut with data submitted by the end \n    user, you can extract the data from the request. This example does so via \n    `javax.portlet.ActionRequest` and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can get the data any way you wish: \n\n    ```java\n    long repositoryId = ParamUtil.getLong(actionRequest, \"repositoryId\");\n    long folderId = ParamUtil.getLong(actionRequest, \"folderId\");\n    long toFileEntryId = ParamUtil.getLong(actionRequest, \"toFileEntryId\");\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n                        DLFileShortcutConstants.getClassName(), actionRequest);\n    ```\n\n<!--Uncomment once article is available\n    For more information on `ServiceContext`, see the tutorial \n    Understanding ServiceContext.\n--> \n\n3.  Call the service reference's `addFileShortcut` method with the data from the \n    previous step: \n\n    ```java\n    FileShortcut fileShortcut = _dlAppService.addFileShortcut(\n                                        repositoryId, folderId, toFileEntryId, \n                                        serviceContext);\n    ```\n\n    The method returns a `FileShortcut` object, which this example sets to a \n    variable for later use. Note, however, that you don't have to do this. \n\nYou can find the full code for this example in the `updateFileShortcut` method \nof @product@'s \n[`EditFileShortcutMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileShortcutMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`FileShortcut` actions that the Documents and Media app supports. Also note that \nthis `updateFileShortcut` method, as well as the rest of \n`EditFileShortcutMVCActionCommand`, contains additional logic to suit the \nspecific needs of the Documents and Media app. \n\n## Related Topics\n\n[Deleting File Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/deleting-file-shortcuts)\n\n[Updating File Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/updating-file-shortcuts)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/02-deleting-entities/00-intro.markdown",
    "content": "---\nheader-id: deleting-entities\n---\n\n# Deleting Entities\n\n[TOC levels=1-4]\n\nYou can delete entities with the Documents and Media API. Note that the exact \nmeaning of *delete* depends on the portal configuration and the delete operation \nyou choose. This is because the \n[Recycle Bin](/docs/7-2/user/-/knowledge_base/u/restoring-deleted-assets), \nwhich is enabled by default, can be used to recover deleted items. Deletions via \n[`DLAppService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html),\nhowever, are permanent. To send items to the Recycle Bin, you must use the\nCapabilities API. \n\nHere, you'll learn about deleting these entities: \n\n-   [Files](#files)\n-   [File Versions](#file-versions)\n-   [File Shortcuts](#file-shortcuts)\n-   [Folders](#folders)\n\nYou'll also learn about using the \n[Recycle Bin](#recycle-bin). \n\n## Files\n\nThere are two `DLAppService` methods you can use to delete files: \n\n-   [`deleteFileEntry(long fileEntryId)`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#deleteFileEntry-long-)\n\n-   [`deleteFileEntryByTitle(long repositoryId, long folderId, String title)`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#deleteFileEntryByTitle-long-long-java.lang.String-)\n\nThese methods differ only in how they identify a file for deletion. The \ncombination of the `folderId` and `title` parameters in `deleteFileEntryByTitle` \nuniquely identify a file because it's impossible for two files in the same \nfolder to share a name. For step-by-step instructions on using these methods, \nsee \n[Deleting Files](/docs/7-2/frameworks/-/knowledge_base/f/deleting-files). \n\n## File Versions\n\nWhen a file is modified, Documents and Media creates a new file version and \nleaves the previous version intact. Over time, old file versions can accumulate \nand consume storage space. Fortunately, you can use the Documents and Media API \nto delete them. Note, however, that there's no way to send file versions to the \nRecycle Bin---once you delete them, they're gone forever. \n\nYou can delete file versions with the `DLAppService` method `deleteFileVersion`: \n\n```java\ndeleteFileVersion(long fileEntryId, String version)\n```\n\nSee this method's \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#deleteFileVersion-long-java.lang.String-) \nfor a description of the parameters. For step-by-step instructions on using this \nmethod, see \n[Deleting File Versions](/docs/7-2/frameworks/-/knowledge_base/f/deleting-file-versions). \n\n### Identifying File Versions\n\nSince there may be many versions of a file, it's useful to programmatically \nidentify old versions for deletion. You can do this with \n[`FileVersionVersionComparator`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/util/comparator/FileVersionVersionComparator.html). \n\nThe following example creates such a comparator and uses its `compare` method to \nidentify old file versions. The code does so by iterating through each \n[approved](/docs/7-2/user/-/knowledge_base/u/workflow) \nversion of the file (`fileVersion`). Each iteration uses the `compare` method to \ntest that file version (`fileVersion.getVersion()`) against the same file's \ncurrent version (`fileEntry.getVersion()`). If this comparison is greater than \n`0`, then the iteration's file version (`fileVersion`) is old and is deleted by \n`deleteFileVersion`: \n\n```java\nFileVersionVersionComparator comparator = new FileVersionVersionComparator();\n\nfor (FileVersion fileVersion: fileEntry.getVersions(WorkflowConstants.STATUS_APPROVED)) {\n\n    if (comparator.compare(fileEntry.getVersion(), fileVersion.getVersion()) > 0) {\n\n        dlAppService.deleteFileVersion(fileVersion.getFileEntryId(), fileVersion.getVersion());\n    }\n}\n```\n\n## File Shortcuts\n\nTo delete file shortcuts, use the `DLAppService` method \n[`deleteFileShortcut`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#deleteFileShortcut-long-) \nwith the ID of the shortcut you want to delete: \n\n```java\ndeleteFileShortcut(long fileShortcutId)\n```\n\nFor step-by-step instructions on using this method, see \n[Deleting File Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/deleting-file-shortcuts). \n\n## Folders\n\nDeleting folders is similar to deleting files. There are two methods you can use \nto delete a folder. Click each method to see its Javadoc: \n\n-   [`deleteFolder(long folderId)`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#deleteFolder-long-) \n\n-   [`deleteFolder(long repositoryId, long parentFolderId, String name)`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#deleteFolder-long-long-java.lang.String-) \n\nWhich method you use is up to you---they both delete a folder. For step-by-step \ninstructions on using these methods, see \n[Deleting Folders](/docs/7-2/frameworks/-/knowledge_base/f/deleting-folders). \n\n## Recycle Bin\n\nInstead of deleting entities, you can move them to the \n[Recycle Bin](/docs/7-2/user/-/knowledge_base/u/restoring-deleted-assets). \nNote that the Recycle Bin isn't part of the Documents and Media API. Although \nyou can use the Recycle Bin API directly, in the case of Documents and Media \nit's better to use the Capabilities API. This is because some third-party \nrepositories (e.g., SharePoint) don't support Recycle Bin functionality. The \nCapabilities API lets you verify that the repository you're working in supports \nthe Recycle Bin. It's therefore a best practice to always use the Capabilities \nAPI when moving entities to the Recycle Bin. \n\nFor step-by-step instructions on this, see \n[Moving Entities to the Recycle Bin](/docs/7-2/frameworks/-/knowledge_base/f/moving-entities-to-the-recycle-bin). \n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/02-deleting-entities/01-deleting-files.markdown",
    "content": "---\nheader-id: deleting-files\n---\n\n# Deleting Files\n\n[TOC levels=1-4]\n\nTo delete a file with the Documents and Media API, you must use one of the two \n`deleteFileEntry*` methods discussed in \n[Deleting Entities](/docs/7-2/frameworks/-/knowledge_base/f/deleting-entities). \nThe steps here show you how. For general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to delete a file: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the arguments of the `deleteFileEntry*` \n    method you wish to use. Since it's common to delete a file specified by the \n    end user, you can extract the data you need from the request. This example \n    does so via `javax.portlet.ActionRequest` and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can get the data any way you wish. Also note that this example gets \n    only the file entry ID because it uses `deleteFileEntry`: \n\n    ```java\n    long fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\n    ```\n\n    If you want to use `deleteFileEntryByTitle` instead, you can also get the \n    repository ID, folder ID, and title from the request. \n\n3.  Call the service reference's `deleteFileEntry*` method you wish to use with \n    the data from the previous step. This example calls `deleteFileEntry` with \n    the file entry's ID: \n\n    ```java\n    _dlAppService.deleteFileEntry(fileEntryId);\n    ```\n\nYou can find the full code for this example in the `deleteFileEntry` method of \n@product@'s \n[`EditFileEntryMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`FileEntry` actions that the Documents and Media app supports. Also note that \nthis `deleteFileEntry` method, as well as the rest of \n`EditFileEntryMVCActionCommand`, contains additional logic to suit the specific \nneeds of the Documents and Media app. \n\n## Related Topics\n\n[Moving Entities to the Recycle Bin](/docs/7-2/frameworks/-/knowledge_base/f/moving-entities-to-the-recycle-bin)\n\n[Creating Files](/docs/7-2/frameworks/-/knowledge_base/f/creating-files)\n\n[Updating Files](/docs/7-2/frameworks/-/knowledge_base/f/updating-files)\n\n[Moving Folders and Files](/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/02-deleting-entities/02-deleting-file-versions.markdown",
    "content": "---\nheader-id: deleting-file-versions\n---\n\n# Deleting File Versions\n\n[TOC levels=1-4]\n\nTo delete a file version with the Documents and Media API, you must use the \n`deleteFileVersion` method discussed in \n[Deleting Entities](/docs/7-2/frameworks/-/knowledge_base/f/deleting-entities). \nThe steps here show you how. For general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to use `deleteFileVersion` to delete a file version:\n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the file entry ID and version for the file you want to delete. Since \n    it's common to delete a file version specified by the end user, you can \n    extract these parameters from the request. This example does so via \n    `javax.portlet.ActionRequest` and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can do this any way you wish: \n\n    ```java\n    long fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\n    String version = ParamUtil.getString(actionRequest, \"version\");\n    ```\n\n3.  Use the service reference to call the `deleteFileVersion` method with the \n    file entry ID and version from the previous step: \n\n    ```java\n    _dlAppService.deleteFileVersion(fileEntryId, version);\n    ```\n\nYou can find the full code for this example in the `deleteFileEntry` method of \n@product@'s \n[`EditFileEntryMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`FileEntry` actions that the Documents and Media app supports. Also note that \nthis `deleteFileEntry` method, as well as the rest of \n`EditFileEntryMVCActionCommand`, contains additional logic to suit the specific \nneeds of the Documents and Media app. \n\n## Related Topics\n\n[Deleting Files](/docs/7-2/frameworks/-/knowledge_base/f/deleting-files)\n\n[Deleting File Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/deleting-file-shortcuts)\n\n[Deleting Folders](/docs/7-2/frameworks/-/knowledge_base/f/deleting-folders)\n\n[Moving Entities to the Recycle Bin](/docs/7-2/frameworks/-/knowledge_base/f/moving-entities-to-the-recycle-bin)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/02-deleting-entities/03-deleting-file-shortcuts.markdown",
    "content": "---\nheader-id: deleting-file-shortcuts\n---\n\n# Deleting File Shortcuts\n\n[TOC levels=1-4]\n\nTo delete a file shortcut with the Documents and Media API, you must use the \n`deleteFileShortcut` method discussed in \n[Deleting Entities](/docs/7-2/frameworks/-/knowledge_base/f/deleting-entities). \nThe steps here show you how. For general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to delete a file shortcut: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the file shortcut's ID. Since it's common to delete a file shortcut \n    specified by the end user, you can extract its ID from the request. This \n    example does so via `javax.portlet.ActionRequest` and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can do this any way you wish: \n\n    ```java\n    long fileShortcutId = ParamUtil.getLong(actionRequest, \"fileShortcutId\");\n    ```\n\n3.  Use the service reference to call the `deleteFileShortcut` method with the \n    file shortcut ID from the previous step: \n\n    ```java\n    _dlAppService.deleteFileShortcut(fileShortcutId);\n    ```\n\nYou can find the full code for this example in the `deleteFileShortcut` method \nof @product@'s \n[`EditFileShortcutMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileShortcutMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`FileShortcut` actions that the Documents and Media app supports. Also note that \nthis `deleteFileShortcut` method, as well as the rest of \n`EditFileShortcutMVCActionCommand`, contains additional logic to suit the \nspecific needs of the Documents and Media app. \n\n## Related Topics\n\n[Moving Entities to the Recycle Bin](/docs/7-2/frameworks/-/knowledge_base/f/moving-entities-to-the-recycle-bin)\n\n[Creating File Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/creating-file-shortcuts)\n\n[Updating File Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/updating-file-shortcuts)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/02-deleting-entities/04-deleting-folders.markdown",
    "content": "---\nheader-id: deleting-folders\n---\n\n# Deleting Folders\n\n[TOC levels=1-4]\n\nTo delete a folder with the Documents and Media API, you must use one of the two \n`deleteFolder` methods discussed in \n[Deleting Entities](/docs/7-2/frameworks/-/knowledge_base/f/deleting-entities). \nThe steps here show you how. For general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to delete a folder: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the arguments of the `deleteFolder` method \n    you wish to use. Since it's common to delete a folder specified by the end \n    user, you can extract the data you need from the request. This example does \n    so via `javax.portlet.ActionRequest` and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can get the data any way you wish. Also note that this example gets \n    only the folder ID because the next step deletes the folder with \n    `deleteFolder(folderId)`: \n\n    ```java\n    long folderId = ParamUtil.getLong(actionRequest, \"folderId\");\n    ```\n\n    If you want to use the other `deleteFolder` method, you can also get the \n    repository ID, parent folder ID, and folder name from the request. \n\n3.  Call the service reference's `deleteFolder` method you wish to use with the \n    data from the previous step. This example calls `deleteFolder` with the \n    folder's ID: \n\n    ```java\n    _dlAppService.deleteFolder(folderId);\n    ```\n\nYou can find the full code for this example in the `deleteFolders` method of \n@product@'s \n[`EditFolderMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFolderMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`Folder` actions that the Documents and Media app supports. Also note that this \n`deleteFolders` method, as well as the rest of `EditFolderMVCActionCommand`, \ncontains additional logic to suit the specific needs of the Documents and Media \napp. \n\n## Related Topics\n\n[Moving Entities to the Recycle Bin](/docs/7-2/frameworks/-/knowledge_base/f/moving-entities-to-the-recycle-bin)\n\n[Creating Folders](/docs/7-2/frameworks/-/knowledge_base/f/creating-folders)\n\n[Updating Folders](/docs/7-2/frameworks/-/knowledge_base/f/updating-folders)\n\n[Copying Folders](/docs/7-2/frameworks/-/knowledge_base/f/copying-folders)\n\n[Moving Folders and Files](/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files)\n\n[Deleting Files](/docs/7-2/frameworks/-/knowledge_base/f/deleting-files)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/02-deleting-entities/05-moving-to-recycle-bin.markdown",
    "content": "---\nheader-id: moving-entities-to-the-recycle-bin\n---\n\n# Moving Entities to the Recycle Bin\n\n[TOC levels=1-4]\n\nFollow these steps to use the Capabilities API to move an entity to the Recycle \nBin. For an explanation of why you should use the Capabilities API for this, see \n[Deleting Entities](/docs/7-2/frameworks/-/knowledge_base/f/deleting-entities). \n\n1.  Verify that the repository supports the Recycle Bin. Do this by calling the \n    [repository object's](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api#specifying-repositories) \n    `isCapabilityProvided` method with `TrashCapability.class` as its argument. \n    This example does so in `if` statement's condition: \n\n    ```java\n    if (repository.isCapabilityProvided(TrashCapability.class)) {\n\n        // The code to move the entity to the Recycle Bin\n        // You'll write this in the next step\n    }\n    ```\n\n2.  Move the entity to the Recycle Bin if the repository supports it. To do \n    this, first get a `TrashCapability` reference by calling the repository \n    object's `getCapability` method with `TrashCapability.class` as its \n    argument. Then call the `TrashCapability` method that moves the entity to \n    the Recycle Bin. For example, this code calls `moveFileEntryToTrash` to move \n    a file to the Recycle Bin: \n\n    ```java\n    if (repository.isCapabilityProvided(TrashCapability.class)) {\n\n        TrashCapability trashCapability = repository.getCapability(TrashCapability.class);\n        trashCapability.moveFileEntryToTrash(user.getUserId(), fileEntry);\n    }\n    ```\n\n    See the `TrashCapability` \n    [Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/repository/capabilities/TrashCapability.html) \n    for information on the methods you can use to move other types of entities \n    to the Recycle Bin. \n\n## Related Topics\n\n[Deleting Files](/docs/7-2/frameworks/-/knowledge_base/f/deleting-files)\n\n[Deleting Folders](/docs/7-2/frameworks/-/knowledge_base/f/deleting-folders)\n\n[Deleting File Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/deleting-file-shortcuts)\n\n[Moving Folders and Files](/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/03-updating-entities/00-intro.markdown",
    "content": "---\nheader-id: updating-entities\n---\n\n# Updating Entities\n\n[TOC levels=1-4]\n\nLike \n[creating](/docs/7-2/frameworks/-/knowledge_base/f/creating-files-folders-and-shortcuts) \nand \n[deleting](/docs/7-2/frameworks/-/knowledge_base/f/deleting-entities) \nentities, updating entities is a key task when working with Documents and Media. \nThe methods in the Documents and Media API for creating and updating entities \nare similar. There are, however, a few important differences. \n\nHere, you'll learn about updating these entities: \n\n-   [Files](#files)\n-   [Folders](#folders)\n-   [File Shortcuts](#file-shortcuts)\n\n## Files\n\nUpdating a file is a bit more complicated than \n[creating one](/docs/7-2/frameworks/-/knowledge_base/f/creating-files). \nThis is due to the way the update operation handles a file's metadata and \ncontent. To update only a file's content, you must also supply the file's \nexisting metadata. Otherwise, the update operation could lose the metadata. The \nopposite, however, isn't true. You can modify a file's metadata without \nre-supplying the content. In such an update, the file's content is automatically \ncopied to the new version of the file. To make this easier to remember, follow \nthese rules when updating files: \n\n-   Always provide all metadata. \n-   Only provide the file's content when you want to change it. \n\n[`DLAppService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html) \nhas three `updateFileEntry` methods that you can use to update a file. These \nmethods differ only in the file content's type. Click each method to see its \nJavadoc, which contains a full description of its parameters: \n\n-   [`updateFileEntry(..., byte[] bytes, ...)`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#updateFileEntry-long-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-boolean-byte:A-com.liferay.portal.kernel.service.ServiceContext-)\n\n-   [`updateFileEntry(..., File file, ...)`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#updateFileEntry-long-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-boolean-java.io.File-com.liferay.portal.kernel.service.ServiceContext-)\n\n-   [`updateFileEntry(..., InputStream is, long size, ...)`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#updateFileEntry-long-java.lang.String-java.lang.String-java.lang.String-java.lang.String-java.lang.String-boolean-java.io.InputStream-long-com.liferay.portal.kernel.service.ServiceContext-)\n\nKeep these things in mind when using these methods: \n\n-   To retain the original file's title and description, you must provide those \n    parameters to `updateFileEntry`. Omitting them deletes any existing \n    title and description. \n\n-   If you supply `null` in place of the file's content (e.g., `bytes`, `file`, \n    or `is`), the update automatically uses the file's existing content. Do this\n    only if you want to update the file's metadata. \n\n-   If you use `false` for the `majorVersion` parameter, the update increments \n    the file version by `0.1` (e.g., from `1.0` to `1.1`). If you use `true` for \n    this parameter, the update increments the file version to the next `.0` \n    value (e.g., from `1.0` to `2.0`, `1.1` to `2.0`, etc.). \n\nFor a step-by-step guide on using these `updateFileEntry` methods, see \n[Updating Files](/docs/7-2/frameworks/-/knowledge_base/f/updating-files). \n\n## Folders\n\nYou can use the Documents and Media API to \n[copy or move](/docs/7-2/frameworks/-/knowledge_base/f/copying-and-moving-entities) \nfolders to a different location. Options for in-place folder updates, however, \nare limited. You can only update a folder's name and description. You can do \nthis with the `DLAppService` method `updateFolder`: \n\n```java\nupdateFolder(long folderId, String name, String description, ServiceContext serviceContext)\n```\n\nAll parameters except the description are mandatory. For a full description of \nthis method and its parameters, see its \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#updateFolder-long-java.lang.String-java.lang.String-com.liferay.portal.kernel.service.ServiceContext-). \nFor step-by-step instructions on using this method, see \n[Updating Folders](/docs/7-2/frameworks/-/knowledge_base/f/updating-folders). \n\n## File Shortcuts\n\nYou can update a file shortcut (`FileShortcut` entities) to change the file it \npoints to or the folder it resides in. Do this via the `DLAppService` method \n`updateFileShortcut`: \n\n```java\nupdateFileShortcut(long fileShortcutId, long folderId, long toFileEntryId, ServiceContext serviceContext)\n```\n\nAll of this method's parameters are mandatory. To retain any of the shortcut's \noriginal values, you must provide them to this method. For a full description of \nthe parameters, see the method's \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#updateFileShortcut-long-long-long-com.liferay.portal.kernel.service.ServiceContext-). \nFor step-by-step instructions on using this method, see \n[Updating File Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/updating-file-shortcuts). \n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/03-updating-entities/01-updating-files.markdown",
    "content": "---\nheader-id: updating-files\n---\n\n# Updating Files\n\n[TOC levels=1-4]\n\nTo update a file with the Documents and Media API, you must use one of the three \n`updateFileEntry` methods discussed in \n[Updating Entities](/docs/7-2/frameworks/-/knowledge_base/f/updating-entities). \nThe steps here show you how. For general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nNote that the example in these steps uses the `updateFileEntry` method that \ncontains `InputStream`, but you can adapt the example to the other methods if \nyou wish: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the `updateFileEntry` method's arguments. \n    Since it's common to update a file with data submitted by the end user, you \n    can extract the data from the request. This example does so via \n    [`UploadPortletRequest`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/upload/UploadPortletRequest.html) \n    and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can get the data any way you wish: \n\n    ```java\n    long repositoryId = ParamUtil.getLong(uploadPortletRequest, \"repositoryId\");\n    long folderId = ParamUtil.getLong(uploadPortletRequest, \"folderId\");\n    String sourceFileName = uploadPortletRequest.getFileName(\"file\");\n    String title = ParamUtil.getString(uploadPortletRequest, \"title\");\n    String description = ParamUtil.getString(uploadPortletRequest, \"description\");\n    String changeLog = ParamUtil.getString(uploadPortletRequest, \"changeLog\");\n    boolean majorVersion = ParamUtil.getBoolean(uploadPortletRequest, \"majorVersion\");\n\n    try (InputStream inputStream = uploadPortletRequest.getFileAsStream(\"file\")) {\n\n        String contentType = uploadPortletRequest.getContentType(\"file\");\n        long size = uploadPortletRequest.getSize(\"file\");\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n                DLFileEntry.class.getName(), uploadPortletRequest);\n    }\n    ```\n\n<!--Uncomment once article is available\n    For more information on `ServiceContext`, see \n    Understanding ServiceContext. \n-->\n\n3.  Call the service reference's `updateFileEntry` method with the data from the \n    previous step. Note that this example does so inside the previous step's \n    try-with-resources statement: \n\n    ```java\n    try (InputStream inputStream = uploadPortletRequest.getFileAsStream(\"file\")) {\n\n        ...\n\n        FileEntry fileEntry = _dlAppService.updateFileEntry(\n                                fileEntryId, sourceFileName, contentType, title,\n                                description, changeLog, majorVersion, inputStream, size,\n                                serviceContext);\n    }\n    ```\n\n    The method returns a `FileEntry` object, which this example sets to a \n    variable for later use. Note, however, that you don't have to do this. \n\nYou can find the full code for this example in the `updateFileEntry` method of \n@product@'s \n[`EditFileEntryMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`FileEntry` actions that the Documents and Media app supports. Also note that \nthis `updateFileEntry` method, as well as the rest of \n`EditFileEntryMVCActionCommand`, contains additional logic to suit the specific \nneeds of the Documents and Media app. \n\n## Related Topics\n\n[Creating Files](/docs/7-2/frameworks/-/knowledge_base/f/creating-files)\n\n[Deleting Files](/docs/7-2/frameworks/-/knowledge_base/f/deleting-files)\n\n[Moving Folders and Files](/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/03-updating-entities/02-updating-folders.markdown",
    "content": "---\nheader-id: updating-folders\n---\n\n# Updating Folders\n\n[TOC levels=1-4]\n\nTo update a folder with the Documents and Media API, you must use the \n`updateFolder` method discussed in \n[Updating Entities](/docs/7-2/frameworks/-/knowledge_base/f/updating-entities). \nThe steps here show you how. For general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to update a folder: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the `updateFolder` method's arguments. Since \n    it's common to update a folder with data submitted by the end user, you can \n    extract the data from the request. This example does so via \n    `javax.portlet.ActionRequest` and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can get the data any way you wish: \n\n    ```java\n    long folderId = ParamUtil.getLong(actionRequest, \"folderId\");\n    String name = ParamUtil.getString(actionRequest, \"name\");\n    String description = ParamUtil.getString(actionRequest, \"description\");\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n                DLFolder.class.getName(), actionRequest);\n    ```\n\n<!--Uncomment once article is available\n    For more information on `ServiceContext`, see the tutorial \n    Understanding ServiceContext. \n-->\n\n3.  Call the service reference's `updateFolder` method with the data from the \n    previous step: \n\n    ```java\n    _dlAppService.updateFolder(folderId, name, description, serviceContext);\n    ```\n\nYou can find the full code for this example in the `updateFolder` method of \n@product@'s \n[`EditFolderMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFolderMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`Folder` actions that the Documents and Media app supports. Also note that \nthis `updateFolder` method, as well as the rest of `EditFolderMVCActionCommand`, \ncontains additional logic to suit the specific needs of the Documents and Media \napp. \n\n## Related Topics\n\n[Creating Folders](/docs/7-2/frameworks/-/knowledge_base/f/creating-folders)\n\n[Deleting Folders](/docs/7-2/frameworks/-/knowledge_base/f/deleting-folders)\n\n[Copying Folders](/docs/7-2/frameworks/-/knowledge_base/f/copying-folders)\n\n[Moving Folders and Files](/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/03-updating-entities/03-updating-file-shortcuts.markdown",
    "content": "---\nheader-id: updating-file-shortcuts\n---\n\n# Updating File Shortcuts\n\n[TOC levels=1-4]\n\nTo update a file shortcut with the Documents and Media API, you must use the \n`updateFileShortcut` method discussed in \n[Updating Entities](/docs/7-2/frameworks/-/knowledge_base/f/updating-entities). \nThe steps here show you how. For general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to update a file shortcut:\n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the `updateFileShortcut` method's arguments. \n    Since it's common to update a file shortcut with data submitted by the end \n    user, you can extract the data from the request. This example does so via \n    `javax.portlet.ActionRequest` and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can get the data any way you wish: \n\n    ```java\n    long fileShortcutId = ParamUtil.getLong(actionRequest, \"fileShortcutId\");\n    long folderId = ParamUtil.getLong(actionRequest, \"folderId\");\n    long toFileEntryId = ParamUtil.getLong(actionRequest, \"toFileEntryId\");\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n                DLFileShortcutConstants.getClassName(), actionRequest);\n    ```\n\n<!-- Uncomment once article is available\n    For more information on `ServiceContext`, see the tutorial \n    Understanding ServiceContext. \n-->\n\n3.  Call the service reference's `updateFileShortcut` method with the data from \n    the previous step: \n\n    ```java\n    _dlAppService.updateFileShortcut(\n            fileShortcutId, folderId, toFileEntryId, serviceContext);\n    ```\n\nYou can find the full code for this example in the `updateFileShortcut` method \nof @product@'s \n[`EditFileShortcutMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileShortcutMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`FileShortcut` actions that the Documents and Media app supports. Also note that \nthis `updateFileShortcut` method, as well as the rest of \n`EditFileShortcutMVCActionCommand`, contains additional logic to suit the \nspecific needs of the Documents and Media app. \n\n## Related Topics\n\n[Creating File Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/creating-file-shortcuts)\n\n[Deleting File Shortcuts](/docs/7-2/frameworks/-/knowledge_base/f/deleting-file-shortcuts)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/04-file-checkout/00-intro.markdown",
    "content": "---\nheader-id: file-checkout-and-checkin\n---\n\n# File Checkout and Checkin\n\n[TOC levels=1-4]\n\nUsers can \n[check out files](/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files) \nfrom the Document Library for editing. Only the user who checked out the file \ncan edit it. This prevents conflicting edits on the same file from multiple \nusers. The Documents and Media API allows these checkin/checkout operations: \n\n-   [File Checkout](file-checkout) \n-   [File Checkin](file-checkin) \n-   [Canceling a Checkout](canceling-a-checkout) \n\n## File Checkout\n\nHere's what happens when you check out a file: \n\n-   A private working copy of the file is created that only you and \n    administrators can access. Until you check the file back in or cancel your \n    changes, any edits you make are stored in the private working copy. \n\n-   Other users can't change or edit any version of the file. This state remains \n    until you cancel or check in your changes. \n\nThe main `DLAppService` method for checking out a file is this \n`checkOutFileEntry` method: \n\n```java\ncheckOutFileEntry(long fileEntryId, ServiceContext serviceContext)\n```\n\nIf this method throws an exception, then you should assume the checkout failed \nand repeat the operation. For a full description of the method and its \nparameters, see its \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#checkOutFileEntry-long-com.liferay.portal.kernel.service.ServiceContext-). \nFor step-by-step instructions on using this method, see \n[Checking Out Files](/docs/7-2/frameworks/-/knowledge_base/f/checking-out-files). \n\n### Fine-tuning Checkout\n\nYou can control how the checkout is performed by setting the following \nattributes in the `checkOutFileEntry` method's `ServiceContext` parameter: \n\n-   `manualCheckInRequired`: By default, the system automatically checks out/in \n    a file when a user edits it. Setting this attribute to `true` prevents this,\n    therefore requiring manual checkout and checkin. \n\n-   `existingDLFileVersionId`: The system typically reuses the private working\n    copy across different checkout/checkin sequences. There's little chance for \n    conflicting edits because only one user at a time can access the private \n    working copy. To force the system to create a new private working copy each\n    time, omit this attribute or set it to `0`. \n\n-   `fileVersionUuid`: This is used by \n    [staging](/docs/7-2/user/-/knowledge_base/u/staging), \n    but can be ignored for normal use. Setting this attribute causes the system \n    to create the new private working copy version with the given UUID. \n\nTo set these attributes, use the `ServiceContext` method \n[`setAttribute(String name, Serializable value)`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/service/ServiceContext.html#setAttribute-java.lang.String-java.io.Serializable-). \nHere's an example of setting the `manualCheckInRequired` attribute to `true`: \n\n```java\nserviceContext.setAttribute(\"manualCheckInRequired\", Boolean.TRUE)\n```\n\n## File Checkin\n\nAfter checking out and editing a file, you must check it back in for other users \nto see the new version. Once you do so, you can't access the private working \ncopy. The next time the file is checked out, the private working copy's contents \nare overwritten. \n\nThe `DLAppService` method for checking in a file is `checkInFileEntry`: \n\n```java\ncheckInFileEntry(long fileEntryId, boolean majorVersion, String changeLog, \n                ServiceContext serviceContext)\n```\n\nFor a full description of the method and its parameters, see its \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#checkInFileEntry-long-boolean-java.lang.String-com.liferay.portal.kernel.service.ServiceContext-). \nThis method uses the private working copy to create a new version of the file. \nAs \n[Updating Files](/docs/7-2/frameworks/-/knowledge_base/f/updating-files) \nexplains, the `majorVersion` parameter's setting determines how the file's \nversion number is incremented. \n\nFor step-by-step instructions on using this method, see \n[Checking In Files](/docs/7-2/frameworks/-/knowledge_base/f/checking-in-files). \n\n## Canceling a Checkout\n\nYou can also cancel a checkout. Use caution with this operation---it discards \nany edits made since checkout. If you're sure you want to cancel a checkout, do \nso with the `DLAppService` method `cancelCheckOut`: \n\n```java\ncancelCheckOut(long fileEntryId)\n```\n\nFor a full description of this method and its parameter, see its \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#cancelCheckOut-long-). \nIf you invoke this method without error, you can safely assume that it discarded \nthe private working copy and unlocked the file. Other users should now be able \nto check out and edit the file. \n\nFor step-by-step instructions on using this method, see \n[Canceling a Checkout](/docs/7-2/frameworks/-/knowledge_base/f/canceling-a-checkout). \n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/04-file-checkout/01-file-checkout.markdown",
    "content": "---\nheader-id: checking-out-files\n---\n\n# Checking Out Files\n\n[TOC levels=1-4]\n\nTo check out a file with the Documents and Media API, use the \n`checkOutFileEntry` method discussed in \n[File Checkout and Checkin](/docs/7-2/frameworks/-/knowledge_base/f/file-checkout-and-checkin). \nThe steps here show you how. For general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to check out a file: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the `checkOutFileEntry` method's arguments. \n    Since it's common to check out a file in response to an action by the end \n    user, you can extract the data from the request. This example does so via \n    `javax.portlet.ActionRequest` and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can get the data any way you wish: \n\n    ```java\n    long fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(actionRequest);\n    ```\n\n<!--Uncomment once article is available\n    For more information on `ServiceContext`, see the tutorial \n    Understanding ServiceContext. \n-->\n\n3.  Call the service reference's `checkOutFileEntry` method with the data from \n    the previous step: \n\n    ```java\n    _dlAppService.checkOutFileEntry(fileEntryId, serviceContext);\n    ```\n\nYou can find the full code for this example in the `checkOutFileEntries` method \nof @product@'s \n[`EditFileEntryMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`FileEntry` actions that the Documents and Media app supports. Also note that \nthis `checkOutFileEntries` method, as well as the rest of \n`EditFileEntryMVCActionCommand`, contains additional logic to suit the specific \nneeds of the Documents and Media app. \n\n## Related Topics\n\n[Checking In Files](/docs/7-2/frameworks/-/knowledge_base/f/checking-in-files)\n\n[Canceling a Checkout](/docs/7-2/frameworks/-/knowledge_base/f/canceling-a-checkout)\n\n[Updating Files](/docs/7-2/frameworks/-/knowledge_base/f/updating-files)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/04-file-checkout/02-file-checkin.markdown",
    "content": "---\nheader-id: checking-in-files\n---\n\n# Checking In Files\n\n[TOC levels=1-4]\n\nTo check in a file with the Documents and Media API, use the \n`checkInFileEntry` method discussed in \n[File Checkout and Checkin](/docs/7-2/frameworks/-/knowledge_base/f/file-checkout-and-checkin). \nThe steps here show you how. For general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to use `checkInFileEntry` to check in a file: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the `checkInFileEntry` method's arguments. \n    Since it's common to check in a file in response to an action by the end \n    user, you can extract the data from the request. This example does so via \n    `javax.portlet.ActionRequest` and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can get the data any way you wish: \n\n    ```java\n    long fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\n    boolean majorVersion = ParamUtil.getBoolean(actionRequest, \"majorVersion\");\n    String changeLog = ParamUtil.getString(actionRequest, \"changeLog\");\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(actionRequest);\n    ```\n\n<!--Uncomment once article is available\n    For more information on `ServiceContext`, see the tutorial \n    Understanding ServiceContext.\n--> \n\n3.  Call the service reference's `checkInFileEntry` method with the data from \n    the previous step: \n\n    ```java\n    _dlAppService.checkInFileEntry(\n            fileEntryId, majorVersion, changeLog, serviceContext);\n    ```\n\nYou can find the full code for this example in the `checkInFileEntries` method \nof @product@'s \n[`EditFileEntryMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`FileEntry` actions that the Documents and Media app supports. Also note that \nthis `checkInFileEntries` method, as well as the rest of \n`EditFileEntryMVCActionCommand`, contains additional logic to suit the specific \nneeds of the Documents and Media app. \n\n## Related Topics\n\n[Checking Out Files](/docs/7-2/frameworks/-/knowledge_base/f/checking-out-files)\n\n[Canceling a Checkout](/docs/7-2/frameworks/-/knowledge_base/f/canceling-a-checkout)\n\n[Updating Files](/docs/7-2/frameworks/-/knowledge_base/f/updating-files)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/04-file-checkout/03-cancel-checkout.markdown",
    "content": "---\nheader-id: canceling-a-checkout\n---\n\n# Canceling a Checkout\n\n[TOC levels=1-4]\n\nTo cancel a checkout with the Documents and Media API, use the \n`cancelCheckOut` method discussed in \n[File Checkout and Checkin](/docs/7-2/frameworks/-/knowledge_base/f/file-checkout-and-checkin). \nThe steps here show you how. For general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to cancel a checkout: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the ID of the file whose checkout you want to cancel. Since it's common \n    to cancel a checkout in response to a user action, you can extract the file \n    ID from the request. This example does so via `javax.portlet.ActionRequest` \n    and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can get it any way you wish: \n\n    ```java\n    long fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\n    ```\n\n3.  Call the service reference's `cancelCheckOut` method with the file's ID: \n\n    ```java\n    _dlAppService.cancelCheckOut(fileEntryId);\n    ```\n\nYou can find the full code for this example in the `cancelFileEntriesCheckOut` \nmethod of @product@'s \n[`EditFileEntryMVCActionCommand`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-web/src/main/java/com/liferay/document/library/web/internal/portlet/action/EditFileEntryMVCActionCommand.java) \nclass. This class uses the Documents and Media API to implement almost all the \n`FileEntry` actions that the Documents and Media app supports. Also note that \nthis `cancelFileEntriesCheckOut` method, as well as the rest of \n`EditFileEntryMVCActionCommand`, contains additional logic to suit the specific \nneeds of the Documents and Media app. \n\n## Related Topics\n\n[Checking Out Files](/docs/7-2/frameworks/-/knowledge_base/f/checking-out-files)\n\n[Checking In Files](/docs/7-2/frameworks/-/knowledge_base/f/checking-in-files)\n\n[Updating Files](/docs/7-2/frameworks/-/knowledge_base/f/updating-files)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/05-copying-moving-entities/00-intro.markdown",
    "content": "---\nheader-id: copying-and-moving-entities\n---\n\n# Copying and Moving Entities\n\n[TOC levels=1-4]\n\nAlthough the Documents and Media API can copy and move entities, these \noperations have some important caveats and limitations. Keep these things in \nmind when copying entities: \n\n-   There's no way to copy files---you can only copy folders. However, copying a \n    folder also copies its contents, which can include files. \n-   Folders can only be copied within their current repository. \n\nThe move operation doesn't have these restrictions. It's possible to move files \nand folders between different repositories. In general, however, the move \noperation is a bit more complicated than the copy operation. For example, the \nAPI's behavior changes depending on whether you move entities to a different \nrepository or within the same one. \n\nHere, you'll learn about the following: \n\n-   [Copying Folders](#copying-folders)\n-   [Moving Folders and Files](#moving-folders-and-files)\n\n## Copying Folders\n\nThe Documents and Media API can copy folders within a repository. You can't, \nhowever, copy a folder between different repositories. Note that copying a \nfolder also copies its contents. \n\nTo copy a folder, use the `DLAppService` method `copyFolder`: \n\n```java\ncopyFolder(long repositoryId, long sourceFolderId, long parentFolderId, String name, \n        String description, ServiceContext serviceContext)\n```\n\nFor a full description of the method and its parameters, see its \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#copyFolder-long-long-long-java.lang.String-java.lang.String-com.liferay.portal.kernel.service.ServiceContext-). \n\nFor step-by-step instructions on using this method, see \n[Copying Folders](/docs/7-2/frameworks/-/knowledge_base/f/copying-folders). \n\n## Moving Folders and Files\n\nThe move operation is more flexible than the copy operation. Copying only works \nwith folders, and you can't copy between repositories. The move operation, \nhowever, works with files and folders within or between repositories. \n\n| **Note:** Depending on the repository implementation, you may get unexpected\n| behavior when moving folders between repositories. Moving a folder also moves\n| its contents via separate move operations for each item in the folder. In some\n| repository implementations, if any move sub-operation fails, the parent move\n| operation also fails. In other repository implementations, the results of\n| successful sub-operations remain even if others fail, which leaves a partially\n| complete move of the whole folder.\n\nTo move a folder, use the `DLAppService` method `moveFolder`: \n\n```java\nmoveFolder(long folderId, long parentFolderId, ServiceContext serviceContext)\n```\n\nFor a full description of this method and its parameters, see its \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#moveFolder-long-long-com.liferay.portal.kernel.service.ServiceContext-). \nThis method is similar to `copyFolder`, but it can't change the folder's name or \ndescription, and it can move folders between repositories. Folder contents are \nmoved with the folder. \n\nThe operation for moving a file is almost identical to moving a folder. To move \na file, use the `DLAppService` method `moveFileEntry`: \n\n```java\nmoveFileEntry(long fileEntryId, long newFolderId, ServiceContext serviceContext)\n```\n\nFor a full description of this method and its parameters, see its \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#moveFileEntry-long-long-com.liferay.portal.kernel.service.ServiceContext-). \n\nFor step-by-step instructions on using `moveFolder` and `moveFileEntry`, see \n[Moving Folders and Files](/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files). \n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/05-copying-moving-entities/01-copying-folders.markdown",
    "content": "---\nheader-id: copying-folders\n---\n\n# Copying Folders\n\n[TOC levels=1-4]\n\nTo copy a folder with the Documents and Media API, use the `copyFolder` method \ndiscussed in \n[Copying and Moving Entities](/docs/7-2/frameworks/-/knowledge_base/f/copying-and-moving-entities). \nThe steps here show you how. For general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to use `copyFolder` to copy a folder: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the `copyFolder` method's arguments. How you \n    do this depends on your use case. The copy operation in this example takes \n    place in the default Site repository and retains the folder's existing name \n    and description. It therefore needs the folder's group ID (to specify the \n    default site repository), name, and description. Also note that because the \n    destination folder in this example is the repository's root folder, the \n    parent folder ID isn't needed---@product@ supplies a constant for specifying \n    a repository's root folder. \n\n    In the following code, \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html) \n    gets the folder's ID from the request (`javax.portlet.ActionRequest`), and \n    the service reference's \n    [`getFolder`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#getFolder-long-) \n    method gets the corresponding folder object. The folder's `getGroupId()`, \n    `getName()`, and `getDescription()` methods then get the folder's group ID, \n    name, and description, respectively: \n\n    ```java\n    long folderId = ParamUtil.getLong(actionRequest, \"folderId\");\n\n    Folder folder = _dlAppService.getFolder(folderId);\n    long groupId = folder.getGroupId();\n    String folderName = folder.getName();\n    String folderDescription = folder.getDescription();\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n                DLFolder.class.getName(), actionRequest);\n    ```\n\n<!--Uncomment once article is available\n    For more information on `ServiceContext`, see the tutorial \n    Understanding ServiceContext. \n-->\n\n3.  Call the service reference's `copyFolder` method with the data from the \n    previous step. Note that this example uses the \n    [`DLFolderConstants`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/model/DLFolderConstants.html) \n    constant `DEFAULT_PARENT_FOLDER_ID` to specify the repository's root folder \n    as the destination folder: \n\n    ```java\n    _dlAppService.copyFolder(\n            groupId, folderId, DLFolderConstants.DEFAULT_PARENT_FOLDER_ID, \n            folderName, folderDescription, serviceContext);\n    ```\n\nNote that you can change any of these values to suit your copy operation. For \nexample, if your copy takes place in a repository other than the default Site \nrepository, you would specify that repository's ID in place of the group ID. You \ncould also specify a different destination folder, and/or change the new \nfolder's name and/or description. \n\n## Related Topics\n\n[Creating Folders](/docs/7-2/frameworks/-/knowledge_base/f/creating-folders)\n\n[Updating Folders](/docs/7-2/frameworks/-/knowledge_base/f/updating-folders)\n\n[Deleting Folders](/docs/7-2/frameworks/-/knowledge_base/f/deleting-folders)\n\n[Moving Folders and Files](/docs/7-2/frameworks/-/knowledge_base/f/moving-folders-and-files)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/05-copying-moving-entities/02-moving-folders-files.markdown",
    "content": "---\nheader-id: moving-folders-and-files\n---\n\n# Moving Folders and Files\n\n[TOC levels=1-4]\n\nTo move folders and files with the Documents and Media API, use the `moveFolder` \nand `moveFileEntry` methods discussed in \n[Copying and Moving Entities](/docs/7-2/frameworks/-/knowledge_base/f/copying-and-moving-entities). \nThe steps here show you how. For general information on using the API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to use `moveFolder` and `moveFileEntry` to move a folder and \na file, respectively. This example does both to demonstrate the procedures: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n        @Reference\n        private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the method arguments. Since moving folders \n    and files is typically done in response to a user action, you can get the \n    data from the request. This example does so via \n    `javax.portlet.ActionRequest` and \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html), \n    but you can get the data any way you wish: \n\n    ```java\n    // Get the folder IDs\n    long folderId = ParamUtil.getLong(actionRequest, \"folderId\");\n    long newFolderId = ParamUtil.getLong(actionRequest, \"newFolderId\");\n\n    // Get the file ID\n    long fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\n\n    ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            DLFileEntry.class.getName(), actionRequest);\n    ```\n\n<!--uncomment once article is available\n    For more information on `ServiceContext`, see the tutorial \n    Understanding ServiceContext. \n-->\n\n3.  Call the service reference's method(s). This example calls `moveFolder` to \n    move a folder (`folderId`) to a different folder (`newFolderId`). It then \n    calls `moveFileEntry` to move a file (`fileEntryId`) to the same destination \n    folder: \n\n    ```java\n    _dlAppService.moveFolder(folderId, newFolderId, serviceContext);\n\n    _dlAppService.moveFileEntry(fileEntryId, newFolderId, serviceContext);\n    ```\n\n## Related Topics\n\n[Copying Folders](/docs/7-2/frameworks/-/knowledge_base/f/copying-folders)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/06-getting-entities/00-intro.markdown",
    "content": "---\nheader-id: getting-entities\n---\n\n# Getting Entities\n\n[TOC levels=1-4]\n\nThe Documents and Media API contains many methods for getting entities from a \nrepository. Most methods in `DLAppService` get single entities (e.g., a file or \nfolder), a collection of entities that match certain characteristics, or the \nnumber of such entities. Because there are so many similar methods for getting \nentities, they aren't all described here. You can find full descriptions for all \n`DLAppService` methods in its \n[reference documentation](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html). \n\nHere, you'll learn about getting these entities: \n\n-   [Files](#files)\n-   [Folders](#folders)\n-   [Multiple Entity Types](#multiple-entity-types)\n\n## Files\n\nGetting files is one of the most common tasks you'll perform with the Documents \nand Media API. There are two main method families for getting files: \n\n`getFileEntries`: Gets files from a specific repository. \n\n`getGroupFileEntries`: Gets files from a Site (group), regardless of repository. \n\nSince these method families are common, their methods share many parameters: \n\n`repositoryId`: The ID of the repository to get files from. To specify the \ndefault Site repository, use the `groupId` (Site ID). \n\n`folderId`: The ID of the folder to get files from. Note that these methods \ndon't traverse the folder structure---they only get files directly from the \nspecified folder. To specify the repository's root folder, use the constant \n`DLFolderConstants.DEFAULT_PARENT_FOLDER_ID`. \n\n`start` and `end`: Integers that specify the lower and upper bounds, \nrespectively, of collection items to include in a page of results. If you don't \nwant to use pagination, use `QueryUtil.ALL_POS` for these parameters. \n\n`obc`: The comparator to use to order collection items. Comparators are \n[`OrderByComparator`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/OrderByComparator.html) \nimplementations that sort collection items. \n\n`fileEntryTypeId`: The ID of the file type to retrieve. Use this to retrieve \nfiles of a specific type. \n\n`mimeTypes`: The MIME types of the files to retrieve. Use this to retrieve files \nof the specified MIME types. You can specify MIME types via the constants in \n[`ContentTypes`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ContentTypes.html). \n\nNote that the `obc` parameter must be an implementation of `OrderByComparator`. \nAlthough you can implement your own comparators, @product@ already contains a \nfew useful implementations in the package \n[`com.liferay.document.library.kernel.util.comparator`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/util/comparator/package-summary.html): \n\n`RepositoryModelCreateDateComparator`: Sorts by creation date. \n\n`RepositoryModelModifiedDateComparator`: Sorts by modification date. \n\n`RepositoryModelReadCountComparator`: Sorts by number of views. \n\n`RepositoryModelSizeComparator`: Sorts by file size. \n\n`RepositoryModelTitleComparator`: Sorts by title. \n\nSee \n[Getting Files](/docs/7-2/frameworks/-/knowledge_base/f/getting-files) \nfor step-by-step instructions on using the above method families. \n\n## Folders\n\nThe Documents and Media API can get folders in a similar way to getting files.\nThe main difference is that folder retrieval methods may have an additional\nargument to tell the system whether to include *mount folders*. Mount folders\nare mount points for external repositories (e.g. Alfresco or SharePoint) that\nappear as regular folders in a Site's default repository. They let users \nnavigate seamlessly between repositories. To account for this, some folder \nretrieval methods include the boolean parameter `includeMountFolders`. Setting \nthis parameter to `true` includes mount folders in the results, while omitting \nit or setting it to `false` excludes them. \n\nFor example, to get a list of a parent folder's subfolders from a repository, \nincluding any mount folders, use this \n[`getFolders`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#getFolders-long-long-boolean-) \nmethod: \n\n```java\ngetFolders(long repositoryId, long parentFolderId, boolean includeMountFolders)\n```\n\nNote that there are several other `getFolders` methods in `DLAppService`. Use \nthe one that best matches your use case. See \n[Getting Folders](/docs/7-2/frameworks/-/knowledge_base/f/getting-folders) \nfor step-by-step instructions on using these `getFolders` methods. \n\n## Multiple Entity Types\n\nThere are also methods in the Documents and Media API that retrieve lists \ncontaining several entity types. These methods use many of the same parameters \nas those already described for retrieving files and folders. For example, the \n[`getFileEntriesAndFileShortcuts`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#getFileEntriesAndFileShortcuts-long-long-int-int-int-) \nmethod gets files and shortcuts from a given repository and folder. Its `status` \nparameter specifies a \n[workflow](/docs/7-2/user/-/knowledge_base/u/workflow) \nstatus. As before, the `start` and `end` parameters control pagination of the \nentities: \n\n```java\ngetFileEntriesAndFileShortcuts(long repositoryId, long folderId, int status, int start, int end)\n```\n\nFor step-by-step instructions on calling this method and others like it, see \n[Getting Multiple Entity Types](/docs/7-2/frameworks/-/knowledge_base/f/getting-multiple-entity-types). \nTo see all such methods, see the `DLAppService` \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html). \n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/06-getting-entities/01-getting-files.markdown",
    "content": "---\nheader-id: getting-files\n---\n\n# Getting Files\n\n[TOC levels=1-4]\n\nTo get files with the Documents and Media API, use a method from the \n`getFileEntries` or `getGroupFileEntries` method families discussed in \n[Getting Entities](/docs/7-2/frameworks/-/knowledge_base/f/getting-entities). \nThe steps here show you how, using this \n[`getFileEntries`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#getFileEntries-long-long-java.lang.String:A-int-int-com.liferay.portal.kernel.util.OrderByComparator-) \nmethod as an example: \n\n```java\nList<FileEntry> getFileEntries(\n        long repositoryId, \n        long folderId, \n        String[] mimeTypes, \n        int start, \n        int end, \n        OrderByComparator<FileEntry> obc\n)\n```\n\nFor general information on using the Documents and Media API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to get a list of files. This example uses the above \n`getFileEntries` method to get all the PNG images from the root folder of a \nSite's default repository, sorted by title: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the method's arguments. You can do this any \n    way you wish. As the next step describes, @product@ provides constants and a \n    comparator for all the arguments this example needs besides the group ID. \n    This example gets the group ID by using \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html) \n    with the request (`javax.portlet.ActionRequest`): \n\n    ```java\n    long groupId = ParamUtil.getLong(actionRequest, \"groupId\");\n    ```\n\n    It's also possible to get the group ID via the \n    [`ThemeDisplay`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/theme/ThemeDisplay.html). \n    Calling the `ThemeDisplay` method `getScopeGroupId()` gets the ID of your \n    app's current site (group): \n\n    ```java\n    ThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);\n    long groupId = themeDisplay.getScopeGroupId();\n    ```\n\n<!--Uncomment once article is available\n    For more information, see \n    Data Scopes. \n-->\n\n3.  Use the data from the previous step to call the service reference method you\n    want to use to get the files. This example calls the above `getFileEntries`\n    method with the group ID from the previous step, and constants and a \n    comparator for the remaining arguments: \n\n    ```java\n    List<FileEntry> fileEntries = \n            _dlAppService.getFileEntries(\n                    groupId, \n                    DLFolderConstants.DEFAULT_PARENT_FOLDER_ID, \n                    new String[] {ContentTypes.IMAGE_PNG}, \n                    QueryUtil.ALL_POS, \n                    QueryUtil.ALL_POS, \n                    new RepositoryModelTitleComparator<>()\n            );\n    ```\n\n    Here's a description of the arguments used in this example: \n\n    `groupId`: Using the group ID as the repository ID specifies that the \n    operation takes place in the default site repository. \n\n    `DLFolderConstants.DEFAULT_PARENT_FOLDER_ID`: Uses the \n    [`DLFolderConstants`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/model/DLFolderConstants.html) \n    constant `DEFAULT_PARENT_FOLDER_ID` to specify the repository's root folder. \n\n    `new String[] {ContentTypes.IMAGE_PNG}`: Uses the \n    [`ContentTypes`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ContentTypes.html) \n    constant `IMAGE_PNG` to specify PNG images. \n\n    `QueryUtil.ALL_POS`: Uses the \n    [`QueryUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/QueryUtil.html) \n    constant `ALL_POS` for the start and end positions in the results. This \n    specifies all results, bypassing pagination. \n\n    `new RepositoryModelTitleComparator<>()`: Creates a new \n    [`RepositoryModelTitleComparator`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/util/comparator/RepositoryModelTitleComparator.html), \n    which sorts the results by title. \n\nRemember, this is just one of many `getFileEntries` and `getGroupFileEntries` \nmethods. To see all such methods, see the `DLAppService` \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html). \n\n## Related Topics\n\n[Getting Folders](/docs/7-2/frameworks/-/knowledge_base/f/getting-folders)\n\n[Getting Multiple Entity Types](/docs/7-2/frameworks/-/knowledge_base/f/getting-multiple-entity-types)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/06-getting-entities/02-getting-folders.markdown",
    "content": "---\nheader-id: getting-folders\n---\n\n# Getting Folders\n\n[TOC levels=1-4]\n\nTo get folders with the Documents and Media API, use one of the `getFolders` \nmethods in `DLAppService`. This is discussed in more detail in \n[Getting Entities](/docs/7-2/frameworks/-/knowledge_base/f/getting-entities). \nThe steps here show you how to call these `getFolders` methods. As an example, \nthis method is used to get a parent folder's subfolders: \n\n```java\ngetFolders(long repositoryId, long parentFolderId, boolean includeMountFolders)\n```\n\nFor general information on using the Documents and Media API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nFollow these steps to call a `getFolders` method: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the method's arguments any way you wish.\n    This `getFolders` method needs a repository ID, a parent folder ID, and\n    a boolean value that indicates whether to include mount folders in the\n    results. To specify the default site repository, you can use the group ID\n    as the repository ID. This example gets the group ID from the request\n    (`javax.portlet.ActionRequest`) via \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html): \n\n    ```java\n    long groupId = ParamUtil.getLong(actionRequest, \"groupId\");\n    ```\n\n    It's also possible to get the group ID via the \n    [`ThemeDisplay`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/theme/ThemeDisplay.html). \n    Calling the `ThemeDisplay` method `getScopeGroupId()` gets the ID of your \n    app's current Site (group). \n    \n    <!--Uncomment once article is available\n    For more information, see \n    Data Scopes. \n    -->\n\n    ```java\n    ThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);\n    long groupId = themeDisplay.getScopeGroupId();\n    ```\n\n    Note that getting the parent folder ID isn't necessary because this example \n    uses the root folder, for which @product@ provides a constant. Also, the \n    boolean value can be provided directly---it doesn't need to be retrieved \n    from somewhere. \n\n3.  Call the service reference's `getFolders` method with the data from the \n    previous step and any other values you want to provide. Note that this \n    example uses `DLFolderConstants.DEFAULT_PARENT_FOLDER_ID` to specify the \n    repository's root folder as the parent folder. It also uses `true` to \n    include any mount folders in the results: \n\n    ```java\n    _dlAppService.getFolders(groupId, DLFolderConstants.DEFAULT_PARENT_FOLDER_ID, true)\n    ```\n\nThis is one of many methods you can use to get folders. The rest are listed in \nthe `DLAppService` \n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html). \n\n## Related Topics\n\n[Getting Files](/docs/7-2/frameworks/-/knowledge_base/f/getting-files)\n\n[Getting Multiple Entity Types](/docs/7-2/frameworks/-/knowledge_base/f/getting-multiple-entity-types)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/02-documents-media-api/06-getting-entities/03-getting-multiple-types.markdown",
    "content": "---\nheader-id: getting-multiple-entity-types\n---\n\n# Getting Multiple Entity Types\n\n[TOC levels=1-4]\n\nThere are several methods in \n[`DLAppService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html) \nthat get lists containing multiple entity types. This is discussed in more \ndetail in \n[Getting Entities](/docs/7-2/frameworks/-/knowledge_base/f/getting-entities). \nThe steps here show you how to use the \n[`getFileEntriesAndFileShortcuts`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/service/DLAppService.html#getFileEntriesAndFileShortcuts-long-long-int-int-int-) \nmethod, but you can apply them to other such methods as well. \nFor general information on using the Documents and Media API, see \n[Documents and Media API](/docs/7-2/frameworks/-/knowledge_base/f/documents-and-media-api). \n\nNote that the example in these steps gets all the files and shortcuts in the \ndefault Site repository's root folder: \n\n1.  Get a reference to `DLAppService`: \n\n    ```java\n    @Reference\n    private DLAppService _dlAppService;\n    ```\n\n2.  Get the data needed to populate the method's arguments any way you wish. To\n    specify the default Site repository, you can use the group ID as the\n    repository ID. This example gets the group ID from the request \n    (`javax.portlet.ActionRequest`) via \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html): \n\n    ```java\n    long groupId = ParamUtil.getLong(actionRequest, \"groupId\");\n    ```\n\n    Getting the parent folder ID, workflow status, and start and end parameters \n    isn't necessary because @product@ provides constants for them. The next step \n    shows this in detail. \n\n3.  Call the service reference method with the data from the previous step and \n    any other values you want to provide. This example calls \n    `getFileEntriesAndFileShortcuts` with the group ID from the previous step \n    and constants for the remaining arguments: \n\n    ```java\n    _dlAppService.getFileEntriesAndFileShortcuts(\n            groupId, \n            DLFolderConstants.DEFAULT_PARENT_FOLDER_ID, \n            WorkflowConstants.STATUS_APPROVED, \n            QueryUtil.ALL_POS, \n            QueryUtil.ALL_POS\n    )\n    ```\n\n    Here's a description of the arguments used in this example: \n\n    -   `groupId`: Using the group ID as the repository ID specifies that the \n        operation takes place in the default site repository. \n    -   `DLFolderConstants.DEFAULT_PARENT_FOLDER_ID`: Uses the \n        [`DLFolderConstants`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/document/library/kernel/model/DLFolderConstants.html) \n        constant `DEFAULT_PARENT_FOLDER_ID` to specify the repository's root \n        folder. \n    -   `WorkflowConstants.STATUS_APPROVED`: Uses the \n        [`WorkflowConstants`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/workflow/WorkflowConstants.html) \n        constant `STATUS_APPROVED` to specify only files/folders that have been \n        approved via workflow. \n    -   `QueryUtil.ALL_POS`: Uses the \n        [`QueryUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/orm/QueryUtil.html) \n        constant `ALL_POS` for the start and end positions in the results. This \n        specifies all results, bypassing pagination. \n\n## Related Topics\n\n[Getting Files](/docs/7-1/frameworks/-/knowledge_base/frameworks/getting-files)\n\n[Getting Folders](/docs/7-1/frameworks/-/knowledge_base/frameworks/getting-folders)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/03-adaptive-media/00-intro.markdown",
    "content": "---\nheader-id: adaptive-media\n---\n\n# Adaptive Media\n\n[TOC levels=1-4]\n\nThe \n[Adaptive Media](/docs/7-2/user/-/knowledge_base/u/adapting-your-media-across-multiple-devices) \napp tailors the size and quality of images to the device displaying them. Here, \nyou'll learn about these things:\n\n-   [The Adaptive Media Taglib](#the-adaptive-media-taglib)\n-   [Adaptive Media's Finder API](#adaptive-medias-finder-api)\n-   [Image Scaling in Adaptive Media](#image-scaling-in-adaptive-media)\n\n## The Adaptive Media Taglib\n\nTo display adapted images in your apps, Adaptive Media offers a convenient tag \nlibrary in the module \n[`com.liferay.adaptive.media.image.taglib`](https://github.com/liferay/com-liferay-adaptive-media/tree/master/adaptive-media-image-taglib). \nThe only mandatory attribute for the taglib is `fileVersion`. It indicates the \nfile version of the adapted image to display. The taglib uses this file version \nto query Adaptive Media's finder API and display the adapted image appropriate \nfor the device making the request. You can also add as many attributes as \nneeded, such as `class`, `style`, `data-sample`, and so on. Any attributes you \nadd are then added to the adapted images in the markup the taglib renders. \n\nFor step-by-step instructions on using this taglib, see \n[Displaying Adapted Images in Your App](/docs/7-2/frameworks/-/knowledge_base/f/displaying-adapted-images-in-your-app). \n\n## Adaptive Media's Finder API\n\nIf you need more control than the taglib offers for finding adapted images, you \ncan query Adaptive Media's finder API directly. For example, if you have an app \nthat needs a specific image in a specific dimension, it's best to query Adaptive \nMedia's finder API directly. You can then display the image however you like \n(e.g., with an HTML `<img>` tag). \n\nAdaptive Media's finder API lets you write queries that get adapted images based \non certain search criteria and filters. For example, you can get adapted images \nthat match a file version or resolution, or are ordered by an attribute like \nimage width. You can even get adapted images that match approximate attribute \nvalues. \n\n### Calling the API\n\nThe entry point to Adaptive Media's API is \n[`AMImageFinder`](@app-ref@/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/finder/AMImageFinder.html). \nTo use it, you must first inject the OSGi component in your class (which must \nalso be an OSGi component) as follows: \n\n```java\n@Reference\nprivate AMImageFinder _amImageFinder;\n```\n\nThis makes an `AMImageFinder` instance available. It has one method, \n`getAdaptiveMediaStream`, that returns a stream of \n[`AdaptiveMedia`](@app-ref@/adaptive-media/latest/javadocs/com/liferay/adaptive/media/AdaptiveMedia.html) \nobjects. This method takes a `Function` that creates an \n[`AMQuery`](@app-ref@/adaptive-media/latest/javadocs/com/liferay/adaptive/media/finder/AMQuery.html) \n(the query for adapted images) via \n[`AMImageQueryBuilder`](@app-ref@/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/finder/AMImageQueryBuilder.html), \nwhich can search adapted images based on different attributes (e.g., width, \nheight, order, etc.). The `AMImageQueryBuilder` methods you call depend on the \nexact query you want to construct. \n\nFor example, here's a general `getAdaptiveMediaStream` call:\n\n```java\nStream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n    _amImageFinder.getAdaptiveMediaStream(\n        amImageQueryBuilder -> amImageQueryBuilder.methodToCall(arg).done());\n```\n\nThe argument to `getAdaptiveMediaStream` is a lambda expression that returns an \n`AMQuery` constructed via `AMImageQueryBuilder`. Note that `methodToCall(arg)` \nis a placeholder for the `AMImageQueryBuilder` method you want to call and its \nargument. The exact call depends on the criteria you want to use to select \nadapted images. The `done()` call that follows this, however, isn't a \nplaceholder--it creates and returns the `AMQuery` regardless of which \n`AMImageQueryBuilder` methods you call. \n\nFor more information on creating `AMQuery` instances, see the \n`AMImageQueryBuilder` \n[Javadoc](@app-ref@/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/finder/AMImageQueryBuilder.html). \n\nFor step-by-step instructions on calling Adaptive Media's API, see \n[Finding Adapted Images](/docs/7-2/frameworks/-/knowledge_base/f/finding-adapted-images). \n\n### Adaptive Media API Constants\n\nWhen calling the Adaptive Media API, there are some constants you can use for \nspecifying common attributes: \n\n-   `AMImageAttribute.AM_IMAGE_ATTRIBUTE_WIDTH`: image width\n-   `AMImageAttribute.AM_IMAGE_ATTRIBUTE_HEIGHT`: image height\n-   `AMImageQueryBuilder.SortOrder.ASC`: ascending sort\n-   `AMImageQueryBuilder.SortOrder.DESC`: descending sort\n\n### Approximate Attributes\n\nAdaptive Media also lets you get adapted images that match approximate attribute \nvalues. For example, you can ask for adapted images whose height is around \n200px, or whose size is around 100kb. The API returns a stream with elements \nordered by how close they are to the specified attribute. For example, imagine \nthat there are four image resolutions that have adapted images with the heights \n150px, 350px, 600px, and 900px. Searching for adapted images whose height is \napproximately 400px returns this order in the stream: 350px, 600px, 150px, \n900px. \n\nSo how close, exactly, is *close*? It depends on the attribute. In the case of \nwidth, height, and length, a numeric comparison orders the images. In the case \nof content type, file name, or UUID, the comparison is more tricky because these \nattributes are strings and thus delegated to Java's \n[`String.compareTo`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#compareTo-java.lang.String-) \nmethod. \n\n## Image Scaling in Adaptive Media\n\nAs described in \n[Adaptive Media's user guide](/docs/7-2/user/-/knowledge_base/u/adapting-your-media-across-multiple-devices), \nAdaptive Media scales images to match the image resolutions defined by the \n@product@ administrator. The default scaling is usually suitable, but Adaptive \nMedia contains an extension point that lets you replace the way it scales \nimages. The \n[`AMImageScaler`](@app-ref@/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/scaler/AMImageScaler.html) \ninterface defines Adaptive Media's image scaling logic. Out of the box, Adaptive \nMedia provides two implementations of this interface: \n\n[`AMDefaultImageScaler`](https://github.com/liferay/com-liferay-adaptive-media/blob/master/adaptive-media-image-impl/src/main/java/com/liferay/adaptive/media/image/internal/scaler/AMDefaultImageScaler.java): \nThe default image scaler. It's always enabled and uses `java.awt` for its image \nprocessing and scaling. \n\n[`AMGIFImageScaler`](https://github.com/liferay/com-liferay-adaptive-media/blob/master/adaptive-media-image-impl/src/main/java/com/liferay/adaptive/media/image/internal/scaler/AMGIFImageScaler.java): \nA scaler that works only with GIF images. It depends on the installation of the \nexternal tool \n[gifsicle](https://www.lcdf.org/gifsicle/) \nin the @product@ instance. This scaler is disabled by default. Administrators \ncan enable it in *Control Panel* &rarr; *System Settings*. \n\nYou must register image scalers in @product@'s OSGi container using the \n`AMImageScaler` interface. Each scaler must also set the `mime.type` property to \nthe MIME type it handles. For example, if you set a scaler's MIME type to \n`image/jpeg`, then that scaler can only handle `image/jpeg` images. If you \nspecify the special MIME type `*`, the scaler can process any image. Note that \n`AMDefaultImageScaler` is registered using `mime.type=*`, while \n`AMGIFImageScaler` is registered using `mime.type=image/gif`. Both scalers, like \nall scalers, implement `AMImageScaler`. \n\nYou can add as many image scalers as you need, even for the same MIME type. \nHowever, Adaptive Media uses only one scaler per image, using this process to \ndetermine the best one: \n\n1.  Select only the image scalers registered with the same MIME type as the \n    image. \n\n2.  Select the enabled scalers from those selected in the first step \n    (the `AMImageScaler` method `isEnabled()` returns `true` for enabled \n    scalers). \n\n3.  Of the scalers selected in the second step, select the one with the highest \n    `service.ranking`. \n\nIf these steps return no results, they're repeated with the special MIME type \n`*`. Also note that if an image scaler is registered for specific MIME types and \nhas a higher `service.ranking`, it's more likely to be chosen than if it's \nregistered for the special MIME type `*` or has a lower `service.ranking`. \n\nFor step-by-step instructions on creating your own image scaler, see \n[Creating an Image Scaler](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-image-scaler). \n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/03-adaptive-media/01-displaying-adapted-images.markdown",
    "content": "---\nheader-id: displaying-adapted-images-in-your-app\n---\n\n# Displaying Adapted Images in Your App\n\n[TOC levels=1-4]\n\nFollow these steps to display adapted images in your app with the Adaptive Media \n[taglib](https://github.com/liferay/com-liferay-adaptive-media/tree/master/adaptive-media-image-taglib). \nFor more information, see \n[The Adaptive Media Taglib](/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media#the-adaptive-media-taglib). \n\n1.  Include the taglib dependency in your project. For example, if you're using \n    Gradle you must add the following line in your project's `build.gradle` \n    file: \n\n    ```groovy\n    provided group: \"com.liferay\", name: \"com.liferay.adaptive.media.image.taglib\", version: \"1.0.0\"\n    ```\n\n2.  Declare the taglib in your JSP: \n\n    ```markup\n    <%@ taglib uri=\"http://liferay.com/tld/adaptive-media-image\" prefix=\"liferay-adaptive-media\" %>\n    ```\n\n3.  Use the taglib wherever you want the adapted image to appear in your app's \n    JSP files: \n\n    ```markup\n    <liferay-adaptive-media:img class=\"img-fluid\" fileVersion=\"<%= fileEntry.getFileVersion() %>\" />\n    ```\n\n    For example, this `view.jsp` uses the taglib to display the adapted images \n    in a grid with the `col-md-6` \n    [column container class](/docs/7-2/frameworks/-/knowledge_base/f/layout-templates-intro): \n\n    ```markup\n    <%@ include file=\"/init.jsp\" %>\n\n    <div class=\"container\">\n\n    <%\n    String[] mimeTypes = {\"image/bmp\", \"image/gif\", \"image/jpeg\", \"image/pjpeg\", \"image/png\", \"image/tiff\", \"image/x-citrix-jpeg\", \"image/x-citrix-png\", \"image/x-ms-bmp\", \"image/x-png\", \"image/x-tiff\"};\n\n    List<FileEntry> fileEntries = DLAppServiceUtil.getFileEntries(scopeGroupId, DLFolderConstants.DEFAULT_PARENT_FOLDER_ID, mimeTypes);\n\n    int columns = 0;\n\n    for (FileEntry fileEntry : fileEntries) {\n            boolean row = ((columns % 2) == 0);\n    %>\n\n            <c:if test=\"<%= row %>\">\n                    <c:if test=\"<%= columns != 0 %>\">\n                            </div>\n                    </c:if>\n\n                    <div class=\"row\">\n            </c:if>\n\n            <div class=\"col-md-6\">\n                    <liferay-adaptive-media:img class=\"img-fluid\" fileVersion=\"<%= fileEntry.getFileVersion() %>\" />\n            </div>\n\n            <%\n            columns++;\n    }\n    %>\n\n    </div>\n    ```\n\nLooking at the generated markup, you can see that it uses the `<picture>` \ntag as described in \n[Creating Content with Adapted Images](/docs/7-2/user/-/knowledge_base/u/creating-content-with-adapted-images). \n\n![Figure 1: The Adaptive Media Samples app shows all the site's adapted images.](../../../images/adaptive-media-sample.png)\n\n## Related Topics\n\n[Adaptive Media](/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media)\n\n[Finding Adapted Images](/docs/7-2/frameworks/-/knowledge_base/f/finding-adapted-images)\n\n[Creating an Image Scaler](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-image-scaler)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/03-adaptive-media/02-finding-adapted-images.markdown",
    "content": "---\nheader-id: finding-adapted-images\n---\n\n# Finding Adapted Images\n\n[TOC levels=1-4]\n\nIf you need more control than the \n[Adaptive Media taglib](/docs/7-2/frameworks/-/knowledge_base/f/displaying-adapted-images-in-your-app) \noffers for finding adapted images to display in your app, you can query Adaptive \nMedia's finder API directly. The steps here show you how for these scenarios: \n\n-   [Getting Adapted Images for File Versions](#getting-adapted-images-for-file-versions)\n-   [Getting the Adapted Images for a Specific Image Resolution](#getting-the-adapted-images-for-a-specific-image-resolution)\n-   [Getting Adapted Images in a Specific Order](#getting-adapted-images-in-a-specific-order)\n-   [Using Approximate Attributes](#using-approximate-attributes)\n-   [Using the Adaptive Media Stream](#using-the-adaptive-media-stream)\n\nFor background information on these topics, see \n[Adaptive Media's Finder API](/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media#adaptive-medias-finder-api). \n\n## Getting Adapted Images for File Versions\n\nFollow these steps to get adapted images for file versions. Note that the method \ncalls here only return adapted images for \n[enabled image resolutions](/docs/7-2/user/-/knowledge_base/u/managing-image-resolutions): \n\n1.  Get an `AMImageFinder` reference: \n\n    ```java\n    @Reference\n    private AMImageFinder _amImageFinder;\n    ```\n\n2.  To get adapted images for a specific file version, call the \n    [`AMImageQueryBuilder`](@app-ref@/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/finder/AMImageQueryBuilder.html) \n    method `forFileVersion` with a \n    [`FileVersion`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/repository/model/FileVersion.html) \n    object as an argument: \n\n    ```java\n    Stream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n        _amImageFinder.getAdaptiveMediaStream(\n            amImageQueryBuilder -> amImageQueryBuilder.forFileVersion(fileVersion).done());\n    ```\n\n3.  To get the adapted images for the latest approved file version, use the \n    `forFileEntry` method with a \n    [`FileEntry`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/repository/model/FileEntry.html) \n    object: \n\n    ```java\n    Stream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n        _amImageFinder.getAdaptiveMediaStream(\n            amImageQueryBuilder -> amImageQueryBuilder.forFileEntry(fileEntry).done());\n    ```\n\nTo get adapted images regardless of status (enabled/disabled image resolutions), \ninvoke the `withConfigurationStatus` method with the constant \n`AMImageQueryBuilder.ConfigurationStatus.ANY`: \n\n```java\nStream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n    _amImageFinder.getAdaptiveMediaStream(\n        amImageQueryBuilder -> amImageQueryBuilder.forFileVersion(fileVersion)\n            .withConfigurationStatus(AMImageQueryBuilder.ConfigurationStatus.ANY).done());\n```\n\nUse the constant `AMImageQueryBuilder.ConfigurationStatus.DISABLED` to get \nadapted images for only disabled image resolutions. \n\n## Getting the Adapted Images for a Specific Image Resolution\n\nBy providing an image resolution's UUID to `AMImageFinder`, you can get that \nresolution's adapted images. This UUID is defined when \n[adding the resolution](/docs/7-2/user/-/knowledge_base/u/adding-image-resolutions) \nin the Adaptive Media app. To get a resolution's adapted images, you must pass \nthat resolution's UUID to the `forConfiguration` method. \n\nFollow these steps to get adapted images for an image resolution: \n\n1.  Get an `AMImageFinder` reference: \n\n    ```java\n    @Reference\n    private AMImageFinder _amImageFinder;\n    ```\n\n2.  Call the \n    [`AMImageQueryBuilder.ConfigurationStep`](@app-ref@/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/finder/AMImageQueryBuilder.ConfigurationStep.html) \n    method `forConfiguration` with the image resolution's UUID. For example, \n    this code gets the adapted images that match a file version, and belong to \n    an image resolution with the UUID `hd-resolution`. It returns the adapted \n    images regardless of whether the resolution is enabled or disabled: \n\n    ```java\n    Stream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n        _amImageFinder.getAdaptiveMediaStream(\n            amImageQueryBuilder -> amImageQueryBuilder.forFileVersion(fileVersion)\n                .forConfiguration(\"hd-resolution\").done());\n    ```\n\n## Getting Adapted Images in a Specific Order\n\nIt's also possible to define the order in which `getAdaptiveMediaStream` returns \nadapted images. Follow these steps to do so: \n\n1.  Get an `AMImageFinder` reference: \n\n    ```java\n    @Reference\n    private AMImageFinder _amImageFinder;\n    ```\n\n2.  Call the `orderBy` method with your sort criteria just before calling the \n    `done()` method. The `orderBy` method takes two arguments: the first \n    specifies the image attribute to sort by (e.g., width/height), while the \n    second specifies the sort order (e.g., ascending/descending). The Adaptive \n    Media API provides \n    [constants](/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media#adaptive-media-api-constants) \n    that you can use for these arguments. \n\n    For example, this code gets all the adapted images regardless of whether the \n    image resolution is enabled, and puts them in ascending order by image \n    width: \n\n    ```java\n    Stream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n        _amImageFinder.getAdaptiveMediaStream(\n            amImageQueryBuilder -> amImageQueryBuilder.forFileVersion(_fileVersion)\n                .withConfigurationStatus(AMImageQueryBuilder.ConfigurationStatus.ANY)\n                .orderBy(AMImageAttribute.AM_IMAGE_ATTRIBUTE_WIDTH, AMImageQueryBuilder.SortOrder.ASC)\n                .done());\n    ```\n\n## Using Approximate Attributes\n\nYou can use the API to get adapted images that match approximate attribute \nvalues. Follow these steps to do so: \n\n1.  Get an `AMImageFinder` reference: \n\n    ```java\n    @Reference\n    private AMImageFinder _amImageFinder;\n    ```\n\n2.  Call the `with` method with your search criteria just before calling the \n    `done()` method. The `with` method takes two arguments: the image attribute \n    and that attribute's approximate value. For example, this code gets adapted \n    images whose height is approximately 400px: \n\n    ```java\n    Stream<AdaptiveMedia<AMImageProcessor>> adaptiveMediaStream =\n        _amImageFinder.getAdaptiveMediaStream(\n            amImageQueryBuilder -> amImageQueryBuilder.forFileVersion(_fileVersion)\n                .with(AMImageAttribute.AM_IMAGE_ATTRIBUTE_HEIGHT, 400).done());\n    ```\n\n## Using the Adaptive Media Stream\n\nThe Adaptive Media stream flows like a babbling brook through the sands of time. \nJust kidding; it's not like that at all. Once you have the \n[`AdaptiveMedia`](@app-ref@/adaptive-media/latest/javadocs/com/liferay/adaptive/media/AdaptiveMedia.html) \nstream, you can get the information you need from it. For example, this code \nprints the URI for each adapted image: \n\n```java\nadaptiveMediaStream.forEach(\n    adaptiveMedia -> {\n        System.out.println(adaptiveMedia.getURI());\n    }\n);\n```\n\nYou can also get other values and attributes from the `AdaptiveMedia` stream. \nHere are a few examples: \n\n```java\n// Get the InputStream \nadaptiveMedia.getInputStream()\n\n// Get the content length\nadaptiveMedia.getValueOptional(AMAttribute.getContentLengthAMAttribute())\n\n// Get the image height\nadaptiveMedia.getValueOptional(AMImageAttribute.AM_IMAGE_ATTRIBUTE_HEIGHT)\n```\n\n## Related Topics\n\n[Adaptive Media](/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media)\n\n[Displaying Adapted Images in Your App](/docs/7-2/frameworks/-/knowledge_base/f/displaying-adapted-images-in-your-app)\n\n[Creating an Image Scaler](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-image-scaler)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/03-adaptive-media/03-image-scaler.markdown",
    "content": "---\nheader-id: creating-an-image-scaler\n---\n\n# Creating an Image Scaler\n\n[TOC levels=1-4]\n\nAdaptive Media scales images to match the image resolutions defined by the \n@product@ administrator. The default scaling is usually suitable, but you can \ncustomize it by creating an image scaler. The steps here show you how. For \ndetailed information on these steps, see \n[Image Scaling in Adaptive Media](/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media#image-scaling-in-adaptive-media). \n\nFollow these steps to create a custom image scaler. The example scaler in these \nsteps customizes the scaling of PNG images: \n\n1.  Create your scaler class to implement `AMImageScaler`. You must also \n    annotate your scaler class with `@Component`, setting `mime.type` properties \n    for each of the scaler's MIME types, and registering an `AMImageScaler` \n    service. If there's more than one scaler for the same MIME type, you must \n    also set the `@Component` annotation's `service.ranking` property. For your \n    scaler to take precedence over other scalers of the same MIME type, its \n    service ranking property must be higher than that of the other scalers. If \n    `service.ranking` isn't set, it defaults to `0`. \n\n    | **Note:** The `service.ranking` property isn't set for the image scalers \n    | included with Adaptive Media (`AMDefaultImageScaler` and \n    | `AMGIFImageScaler`). Their service ranking therefore defaults to `0`. To \n    | replace either scaler, you must set your scaler to the same MIME type and \n    | give it a service ranking higher than `0`. \n\n    This example image scaler scales PNG and x-PNG images and has a service \n    ranking of `100`: \n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\"mime.type=image/png\", \"mime.type=image/x-png\", \"service.ranking:Integer=100\"},\n        service = {AMImageScaler.class}\n    )\n    public class SampleAMPNGImageScaler implements AMImageScaler {...\n    ```\n\n    This requires these imports: \n\n    ```java\n    import com.liferay.adaptive.media.image.scaler.AMImageScaler;\n    import org.osgi.service.component.annotations.Component;\n    ```\n\n2.  Implement the `isEnabled()` method to return `true` when you want to enable \n    the scaler. In many cases, you always want the scaler enabled, so you can \n    simply return `true` in this method. This is the case with the image scaler \n    in this example: \n\n    ```java\n    @Override\n    public boolean isEnabled() {\n        return true;\n    }\n    ```\n\n    This method gets more interesting when the scaler depends on other tools or \n    features. For example, the `isEnabled()` method in `AMGIFImageScaler` \n    determines whether gifsicle is enabled. This scaler must only be enabled \n    when the tool it depends on, gifsicle, is also enabled: \n\n    ```java\n    @Override\n    public boolean isEnabled() {\n        return _amImageConfiguration.gifsicleEnabled();\n    }\n    ```\n\n3.  Implement the `scaleImage` method. This method contains the scaler's \n    business logic and must return an `AMImageScaledImage` instance. For \n    example, the `scaleImage` implementation in this example uses \n    `AMImageConfigurationEntry` to get the maximum height and width values for \n    the scaled image, and `FileVersion` to get the image to scale. The scaling \n    is done via a private inner class, assuming that the methods `_scalePNG`, \n    `_getScalePNGHeight`, `_getScalePNGWidth`, and `_getScalePNGSize` implement \n    the actual scaling: \n\n    ```java\n    @Override\n    public AMImageScaledImage scaleImage(FileVersion fileVersion,\n        AMImageConfigurationEntry amImageConfigurationEntry) {\n\n        Map<String, String> properties = amImageConfigurationEntry.getProperties();\n\n        int maxHeight = GetterUtil.getInteger(properties.get(\"max-height\"));\n        int maxWidth = GetterUtil.getInteger(properties.get(\"max-width\"));\n\n        try {\n            InputStream inputStream = \n                _scalePNG(fileVersion.getContentStream(false), maxHeight, maxWidth);\n\n            int height = _getScalePNGHeight();\n            int width = _getScalePNGWidth();\n            long size = _getScalePNGSize();\n\n            return new AMImageScaledImageImpl(inputStream, height, width, size);\n        }\n        catch (PortalException pe) {\n            throw new AMRuntimeException.IOException(pe);\n        }\n    }\n\n    private class AMImageScaledImageImpl implements AMImageScaledImage {\n\n        @Override\n        public int getHeight() {\n            return _height;\n        }\n\n        @Override\n        public InputStream getInputStream() {\n            return _inputStream;\n        }\n\n        @Override\n        public long getSize() {\n            return _size;\n        }\n\n        @Override\n        public int getWidth() {\n            return _width;\n        }\n\n        private AMImageScaledImageImpl(InputStream inputStream, int height, \n            int width, long size) {\n\n            _inputStream = inputStream;\n            _height = height;\n            _width = width;\n            _size = size;\n        }\n\n        private final int _height;\n        private final InputStream _inputStream;\n        private final long _size;\n        private final int _width;\n\n    }\n    ```\n\n    This requires these imports: \n\n    ```java\n    import com.liferay.adaptive.media.exception.AMRuntimeException;\n    import com.liferay.adaptive.media.image.configuration.AMImageConfigurationEntry;\n    import com.liferay.adaptive.media.image.scaler.AMImageScaledImage;\n    import com.liferay.portal.kernel.exception.PortalException;\n    import com.liferay.portal.kernel.repository.model.FileVersion;\n    import com.liferay.portal.kernel.util.GetterUtil;\n    import java.io.InputStream;\n    import java.util.Map;\n    ```\n\n## Related Topics\n\n[Adaptive Media](/docs/7-2/frameworks/-/knowledge_base/f/adaptive-media)\n\n[Displaying Adapted Images in Your App](/docs/7-2/frameworks/-/knowledge_base/f/displaying-adapted-images-in-your-app)\n\n[Finding Adapted Images](/docs/7-2/frameworks/-/knowledge_base/f/finding-adapted-images)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/05-social-api/00-intro.markdown",
    "content": "---\nheader-id: social-api\n---\n\n# Social API\n\n[TOC levels=1-4]\n\nYou can use the social API to integrate @product@'s social features with your \napps. Here, you'll learn about the following topics: \n\n-   [Social Bookmarks](#social-bookmarks)\n-   [Adding Comments to Your App](/docs/7-2/frameworks/-/knowledge_base/f/adding-comments-to-your-app)\n-   [Ratings](#ratings)\n-   [Flagging Inappropriate Asset Content](/docs/7-2/frameworks/-/knowledge_base/f/flagging-inappropriate-asset-content)\n\n## Social Bookmarks\n\nTo apply social bookmarks to your app's content, you must use the \n`liferay-social-bookmarks` taglib. This taglib contains the \n`liferay-social-bookmarks:bookmarks` tag, which adds the social bookmarks \ncomponent. This tag contains these attributes: \n\n`className`: The entity's class name. \n\n`classPK`: The entity's primary key. \n\n`displayStyle`: The social bookmarks' display style. Possible values are \n`inline`, which displays them in a row, and `menu`, which hides them in a menu. \n\n`title`: A title for the content being shared. This attribute is often populated \nby calling the entity's `getTitle()` method (or other method that retrieves the \ntitle). \n\n`types`: A comma-delimited list of the social media services to use (e.g., \n`facebook,twitter`). To use every social media service available in the portal, \nomit this attribute or use `<%= null %>` for its value. \n\n`url`: A URL to the portal content being shared. The `PortalUtil` method \n`getCanonicalURL` is often called to populate this attribute. This method \nconstructs an SEO-friendly URL from the page's full URL. For more information, \nsee the method's\n[Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/PortalUtil.html#getCanonicalURL-java.lang.String-com.liferay.portal.kernel.theme.ThemeDisplay-com.liferay.portal.kernel.model.Layout-). \n\nFor instructions on using this tag, see \n[Applying Social Bookmarks](/docs/7-2/frameworks/-/knowledge_base/f/applying-social-bookmarks). \nFor instructions on creating your own social bookmarks, see \n[Creating Social Bookmarks](/docs/7-2/frameworks/-/knowledge_base/f/creating-social-bookmarks). \n\n![Figure 1: With `displayStyle` set to `inline`, the first three social bookmarks appear in a row and the rest appear in a menu.](../../../images/social-bookmarks-inline.png)\n\n![Figure 2: With `displayStyle` set to `menu`, all social bookmarks appear in the *Share* menu.](../../../images/social-bookmarks-menu.png)\n\n## Ratings\n\n[The asset framework](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework) \nsupports a content rating system. This feature appears in many of @product@'s \nbuilt-in apps. For example, users can rate articles published in the Blogs app. \nThere are three different rating types: \n\n-   Likes\n-   Stars (five, by default)\n-   Thumbs (up/down)\n\nTo enable ratings in your app, you must use the `liferay-ui:ratings` tag and set \nits `type` attribute to the rating type (`like`, `stars`, or `thumbs`). For \ninstructions on this, see \n[Rating Assets](/docs/7-2/frameworks/-/knowledge_base/f/rating-assets). \n\n### Rating Type Selection\n\nAdmins can select the rating type for an app's entities via the Control Panel \nand Site Administration. Portal admins can set the default rating type for the \nportal, while Site admins can override the default rating type for their Site. \n\nA ratings-enabled app must define its rating type in an OSGi component that \nimplements the \n[`PortletRatingsDefinition`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/definition/PortletRatingsDefinition.html) \ninterface. This class declares the usage of ratings (specifying the portlet and \nthe entity) and the default rating type (that can be overridden by portal and \nsite admins). This interface has two methods that you must implement: \n\n`getDefaultRatingsType`: Returns the entity's default rating type, which portal \nand site admins can override. You can do this via the \n[`RatingsType`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/RatingsType.html) \nenum, which contains `LIKE`, `STARS`, or `THUMBS`. \n\n`getPortletId`: Returns the portlet ID of the main portlet that uses the entity. \nYou can do this via the \n[`PortletKeys`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/PortletKeys.html) \nenum, which defines many constants that correspond to the portlet IDs of the \nbuilt-in portlets. \n\nTo add support for rating type selection in your app, follow the instructions in \n[Implementing Rating Type Selection](/docs/7-2/frameworks/-/knowledge_base/f/implementing-rating-type-selection). \nOnce you've done so, you can configure the default rating type via the Control \nPanel at *Configuration* &rarr; *Instance Settings* &rarr; *Social*. To override \nthe default values for a site, go to Site Administration (your Site's menu) \n&rarr; *Configuration* &rarr; *Site Settings* &rarr; *Social*. \n\n### Rating Value Transformation\n\nThe database stores normalized rating values. This permits switching between \nrating types without modifying the underlying data. When administrators change \nan entity's rating type, its best match is computed. Here's a list of the \ndefault transformations between rating types: \n\n1.  When changing from stars to: \n\n    **Like:** A value of 3, 4, or 5 stars is considered a like; a value of 1 \n    or 2 stars is omitted. \n\n    **Thumbs up/down:** A value of 3, 4, or 5 stars is considered a thumbs up; \n    a value of 1 or 2 stars is considered a thumbs down.\n\n2.  When changing from thumbs up/down to: \n\n    **Like:** A like is considered a thumbs up. \n\n    **Stars:** A thumbs down is considered 1 star; a thumbs up is considered 5 \n    stars. \n\n3.  When changing from like to: \n\n    **Stars:** A like is considered 5 stars. \n\n    **Thumbs up/down:** A like is considered a thumbs up. \n\nThere may be some cases, however, where you want to apply different criteria to \ndetermine the new rating values. A mechanism exists that permits this, but it \nmodifies the stored rating values. To define such transformations, create an \nOSGi component that implements \n[`RatingsDataTransformer`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/transformer/RatingsDataTransformer.html). \n\n| **Note:** The portal doesn't provide a default `RatingsDataTransformer` \n| implementation. Unless you provide such an implementation, the stored rating \n| values always remain the same while the portal interprets existing values for \n| the selected rating type. \n\nWhen implementing `RatingsDataTransformer`, implement the `transformRatingsData` \nmethod to transform the data. This method's arguments include the `RatingsType` \nvariables `fromRatingsType` and `toRatingsType`, which contain the rating type \nto transform from and to, respectively. These values let you write your custom \ntransformation's logic. You can write this logic by implementing the interface \n`ActionableDynamicQuery.PerformActionMethod` as an anonymous inner class in the \n`transformRatingsData` method, implementing the `performAction` method with your \ntransformation's logic. \n\nFor instructions on implementing `RatingsDataTransformer`, see \n[Customizing Rating Value Transformation](/docs/7-2/frameworks/-/knowledge_base/f/customizing-rating-value-transformation). \n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/05-social-api/01-applying-social-bookmarks.markdown",
    "content": "---\nheader-id: applying-social-bookmarks\n---\n\n# Applying Social Bookmarks\n\n[TOC levels=1-4]\n\nWhen you enable social bookmarks, icons for sharing on Twitter, Facebook, and \nLinkedIn appear below your app's content. Taglibs provide the markup you need \nto add this feature to your app. \n\n![Figure 1: These social bookmarks are in the inline display style.](../../../images/social-bookmarks-inline.png)\n\nFollow these steps to add social bookmarks to your app: \n\n1.  Make sure your entity is \n    [asset enabled](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework). \n\n2.  In your project's `build.gradle` file, add a dependency to the module \n    [`com.liferay.social.bookmarks.taglib`](https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.social.bookmarks.taglib/): \n\n    ```groovy\n    compileOnly group: \"com.liferay\", name: \"com.liferay.social.bookmarks.taglib\", version: \"1.0.0\"\n    ```\n\n3.  Choose a view in which to show the social bookmarks. For example, you can \n    display them in one of your app's views. However, note that you don't need \n    to implement social bookmarks in your app's \n    [asset renderers](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-asset-renderer). \n    The Asset Publisher displays social bookmarks in asset renderers by default. \n\n4.  In your view's JSP, include the `liferay-social-bookmarks` taglib \n    declaration: \n\n    ```markup\n    <%@ taglib uri=\"http://liferay.com/tld/social-bookmarks\" prefix=\"liferay-social-bookmarks\" %>\n    ```\n\n5.  Get an instance of your entity. You can do this however you wish. This \n    example uses \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html) \n    to get the entity's ID from the render request, then uses the entity's \n    `-LocalServiceUtil` class to create an entity object: \n\n    ```java\n    <%\n    long entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n    entry = EntryLocalServiceUtil.getEntry(entryId);\n    %>\n    ```\n\n6.  Use the `liferay-social-bookmarks:bookmarks` tag to add the social bookmarks \n    component. See \n    [Social Bookmarks](/docs/7-2/frameworks/-/knowledge_base/f/social-api#social-bookmarks) \n    for information on this tag's attributes. Here's an example of using this \n    tag to add social bookmarks for a blog entry in the Blogs app: \n\n    ```markup\n    <liferay-social-bookmarks:bookmarks\n            className=\"<%= BlogsEntry.class.getName() %>\"\n            classPK=\"<%= entry.getEntryId() %>\"\n            displayStyle=\"inline\"\n            title=\"<%= entry.getTitle() %>\"\n            types=\"facebook,twitter\"\n            url=\"<%= PortalUtil.getCanonicalURL(bookmarkURL.toString(), themeDisplay, layout) %>\"\n    />\n    ```\n\n## Related Topics\n\n[Social Bookmarks](/docs/7-2/frameworks/-/knowledge_base/f/social-api#social-bookmarks)\n\n[Creating Social Bookmarks](/docs/7-2/frameworks/-/knowledge_base/f/creating-social-bookmarks)\n\n[Asset Framework](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/05-social-api/02-creating-social-bookmarks.markdown",
    "content": "---\nheader-id: creating-social-bookmarks\n---\n\n# Creating Social Bookmarks\n\n[TOC levels=1-4]\n\nBy default, @product@ contains social bookmarks for Twitter, Facebook, and \nLinkedIn. You can also create your own social bookmark by registering a \ncomponent that implements the \n[`SocialBookmark`](@app-ref@/social/latest/javadocs/com/liferay/social/bookmarks/SocialBookmark.html) \ninterface from the module \n`com.liferay.social.bookmarks.api`. The steps here show you how to do this. \n\n## Implementing the SocialBookmark Interface\n\nFollow these steps to implement the `SocialBookmark` interface: \n\n1.  Create your `*SocialBookmark` class and register a component that defines \n    the `social.bookmarks.type` property. This property's value is what you \n    enter for the `liferay-social-bookmarks:bookmarks` tag's `type` attribute \n    when you use your social bookmark. \n\n    For example, here's the definition for a Twitter social bookmark class: \n\n    ```java\n    @Component(immediate = true, property = \"social.bookmarks.type=twitter\")\n    public class TwitterSocialBookmark implements SocialBookmark {...\n    ```\n\n2.  Create a \n    [`ResourceBundleLoader`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ResourceBundleLoader.html) \n    reference to help localize the social bookmark's name. \n\n    ```java\n    @Reference(\n            target = \"(bundle.symbolic.name=com.liferay.social.bookmark.twitter)\"\n    )\n    private ResourceBundleLoader _resourceBundleLoader;\n    ```\n\n3.  Implement the `getName` method to return the social bookmark's name as a \n    string. This method takes a \n    [`Locale`](https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html) \n    object that you can use for localization via \n    [`LanguageUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/language/LanguageUtil.html) \n    and \n    [`ResourceBundle`](https://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html): \n\n    ```java\n    @Override\n    public String getName(Locale locale) {\n        ResourceBundle resourceBundle = _resourceBundleLoader.loadResourceBundle(locale);\n\n        return LanguageUtil.get(resourceBundle, \"twitter\");\n    }\n    ```\n\n4.  Implement the `getPostURL` method to return the share URL. This method \n    constructs the share URL from a title and URL, and uses \n    [`URLCodec`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/URLCodec.html) \n    to encode the title in the URL: \n\n    ```java\n    @Override\n    public String getPostURL(String title, String url) {\n        return String.format(\n            \"https://twitter.com/intent/tweet?text=%s&tw_p=tweetbutton&url=%s\", \n            URLCodec.encodeURL(title), url);\n    }\n    ```\n\n5.  Create a `ServletContext` reference: \n\n    ```java\n    @Reference(\n            target = \"(osgi.web.symbolicname=com.liferay.social.bookmark.twitter)\"\n    )\n    private ServletContext _servletContext;\n    ```\n\n6.  Implement the `render` method, which is called when the inline display style \n    is selected. Typically, this method renders a link to the share URL (e.g., a \n    share button), but you can use it for whatever you need. To keep a \n    consistent look and feel with the default social bookmarks, you can use a \n    [Clay icon](/docs/7-2/reference/-/knowledge_base/r/clay-icons). \n\n    This example gets a `RequestDispatcher` for the JSP that contains a Clay \n    icon (`page.jsp`), and then includes that JSP in the response: \n\n    ```java\n    @Override\n    public void render(\n                    String target, String title, String url, HttpServletRequest request,\n                    HttpServletResponse response)\n            throws IOException, ServletException {\n\n            RequestDispatcher requestDispatcher =\n                    _servletContext.getRequestDispatcher(\"/page.jsp\");\n\n            requestDispatcher.include(request, response);\n    }\n    ```\n\n## Creating Your JSP\n\nThe `page.jsp` file referenced in the above `SocialBookmark` implementation uses \n[a Clay link](/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links) \n(`clay:link`) to specify and style the Twitter icon included with Clay. Follow \nthese steps to create a JSP for your own social bookmark: \n\n1.  Add the `clay` and `liferay-theme` taglib declarations: \n\n    ```markup\n    <%@ taglib uri=\"http://liferay.com/tld/clay\" prefix=\"clay\" %>\n    <%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\" %>\n    ```\n\n2.  Import \n    [`GetterUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/GetterUtil.html) \n    and `SocialBookmark`: \n\n    ```markup\n    <%@ page import=\"com.liferay.portal.kernel.util.GetterUtil\" %>\n    <%@ page import=\"com.liferay.social.bookmarks.SocialBookmark\" %>\n    ```\n\n3.  From the request, get a `SocialBookmark` instance and the social bookmark's \n    title and URL: \n\n    ```java\n    <%\n    SocialBookmark socialBookmark = (SocialBookmark)request.getAttribute(\"liferay-social-bookmarks:bookmark:socialBookmark\");\n    String title = GetterUtil.getString((String)request.getAttribute(\"liferay-social-bookmarks:bookmark:title\"));\n    String url = GetterUtil.getString((String)request.getAttribute(\"liferay-social-bookmarks:bookmark:url\"));\n    %>\n    ```\n\n    The title and URL are set via the `liferay-social-bookmarks` taglib when \n    [applying the social bookmark](/docs/7-2/frameworks/-/knowledge_base/f/applying-social-bookmarks). \n\n4.  Add the Clay link. See the `clay:link` \n    [documentation](https://clayui.com/docs/components/link.html) \n    for a full description of its attributes. \n\n    ```markup\n    <clay:link\n            buttonStyle=\"secondary\"\n            elementClasses=\"btn-outline-borderless btn-sm lfr-portal-tooltip\"\n            href=\"<%= socialBookmark.getPostURL(title, url) %>\"\n            icon=\"twitter\"\n            title=\"<%= socialBookmark.getName(locale) %>\"\n    />\n    ```\n\n    This example sets the following `clay:link` attributes: \n\n    `buttonStyle`: This example renders the \n    [button's type](/docs/7-2/reference/-/knowledge_base/r/clay-buttons) \n    as a secondary button. \n\n    `elementClasses`: The custom CSS to use for styling the button (optional). \n\n    `href`: The button's URL. You should specify this by calling your \n    `SocialBookmark` instance's `getPostURL` method. \n\n    `icon`: The button's icon. This example specifies the Twitter icon included \n    in Clay (`twitter`). \n\n    `title`: The button's title. This example uses the `SocialBookmark` \n    instance's `getName` method. \n\nTo see a complete, real-world example of a social bookmark implementation, see \n[Liferay's Twitter social bookmark code](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/social/social-bookmark-twitter). \n\n## Related Topics\n\n[Applying Social Bookmarks](/docs/7-2/frameworks/-/knowledge_base/f/applying-social-bookmarks)\n\n[Using the Clay Taglib in Your Portlets](/docs/7-2/reference/-/knowledge_base/r/using-the-clay-taglib-in-your-portlets)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/05-social-api/03-adding-comments.markdown",
    "content": "---\nheader-id: adding-comments-to-your-app\n---\n\n# Adding Comments to Your App\n\n[TOC levels=1-4]\n\nLiferay provides taglibs that enable comments on your app's content. Here, \nyou'll learn how to use these taglibs, using a sample Guestbook app as an \nexample. \n\nFollow these steps to enable commenting on your app's content: \n\n1.  Make sure your entity is \n    [asset enabled](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework). \n\n2.  Choose a read-only view of the entity you want to enable comments on. You \n    can display the comments component in your app's view, or if you've \n    [implemented asset rendering](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-asset-renderer) \n    you can display it in the full content view in the Asset Publisher app. \n\n3.  Include the `liferay-ui`, `liferay-comment`, and `portlet` taglib \n    declarations in your JSP: \n\n    ```markup\n    <%@ taglib prefix=\"liferay-ui\" uri=\"http://liferay.com/tld/ui\" %>\n    <%@ taglib prefix=\"liferay-comment\" uri=\"http://liferay.com/tld/comment\" %>\n    <%@ taglib prefix=\"portlet\" uri=\"http://java.sun.com/portlet_2_0\" %>\n    ```\n\n4.  Use \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html) \n    to get the entity's ID from the render request. Then create an entity object \n    using the `-LocalServiceUtil` class. Here's an example that does this for a \n    guestbook entry in the example Guestbook app:\n\n    ```java\n    <%\n    long entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n    entry = EntryLocalServiceUtil.getEntry(entryId);\n    %>\n    ```\n\n5.  Create a collapsible panel for the comments using the \n    `liferay-ui:panel-container` and `liferay-ui:panel` tags. This lets users \n    hide the discussion area: \n\n    ```markup\n    <liferay-ui:panel-container extended=\"<%=false%>\"\n      id=\"guestbookCollaborationPanelContainer\" persistState=\"<%=true%>\">\n      <liferay-ui:panel collapsible=\"<%=true%>\" extended=\"<%=true%>\"\n        id=\"guestbookCollaborationPanel\" persistState=\"<%=true%>\"\n        title=\"Collaboration\">\n    ```\n\n6.  Create a URL for the discussion using the `portlet:actionURL` tag: \n\n    ```markup\n    <portlet:actionURL name=\"invokeTaglibDiscussion\" var=\"discussionURL\" />\n    ```\n\n7.  Use the `liferay-comment:discussion` tag to add the discussion. To let the \n    user return to the JSP after making a comment, set the tag's `redirect` \n    attribute to the current URL. You can use \n    `PortalUtil.getCurrentURL((renderRequest))` to get the current URL from the \n    `request` object. In this example, the current URL was earlier set to the \n    `currentURL` variable: \n\n    ```markup\n        <liferay-comment:discussion className=\"<%=Entry.class.getName()%>\"\n          classPK=\"<%=entry.getEntryId()%>\"\n          formAction=\"<%=discussionURL%>\" formName=\"fm2\"\n          ratingsEnabled=\"<%=true%>\" redirect=\"<%=currentURL%>\"\n          userId=\"<%=entry.getUserId()%>\" />\n\n      </liferay-ui:panel>\n    </liferay-ui:panel-container>\n    ```\n\nIf you haven't already connected your portlet's view to the JSP for your entity, \nsee \n[Configuring JSP Templates for an Asset Renderer](/docs/7-2/frameworks/-/knowledge_base/f/configuring-jsp-templates-for-an-asset-renderer). \n\n## Related Topics\n\n[Asset Framework](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework)\n\n[Rating Assets](/docs/7-2/frameworks/-/knowledge_base/f/rating-assets)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/05-social-api/05-rating-assets.markdown",
    "content": "---\nheader-id: rating-assets\n---\n\n# Rating Assets\n\n[TOC levels=1-4]\n\nIn only a few lines of code, you can use a taglib to enable ratings for your \napp's content. The steps here show you how. For more information on this taglib \nand ratings in general, see \n[Ratings](/docs/7-2/frameworks/-/knowledge_base/f/social-api#ratings). \n\n![Figure 1: Users can rate content to let others know how they really feel about it.](../../../images/social-ratings-thumbs.png)\n\nFollow these steps to enable ratings in your app. Note that these steps use a \nsample Guestbook app as an example. This app lets users leave simple messages in \na guestbook. \n\n1.  Make sure your entity is \n    [asset enabled](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework). \n\n2.  Choose a read-only view of the entity for which you want to enable ratings. \n    You can display ratings in one of your portlet's views, or if you've \n    [implemented asset rendering](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-asset-renderer) \n    you can display them in the full content view in the Asset Publisher app. \n\n3.  In the JSP, include the `liferay-ui` taglib declaration:\n\n    ```markup\n    <%@ taglib prefix=\"liferay-ui\" uri=\"http://liferay.com/tld/ui\" %>\n    ```\n\n4.  Use \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html) \n    to get the entity's ID from the render request. Then create an entity object \n    using the `-LocalServiceUtil` class. Here's an example that does this for a \n    guestbook entry in the example Guestbook app: \n\n    ```java\n    <%\n    long entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n    entry = EntryLocalServiceUtil.getEntry(entryId);\n    %>\n    ```\n\n5.  Use the `liferay-ui:ratings` tag to add the ratings component for the \n    entity. This example uses the stars rating type: \n\n    ```markup\n    <liferay-ui:ratings className=\"<%=Entry.class.getName()%>\"\n        classPK=\"<%=entry.getEntryId()%>\" type=\"stars\" />\n    ```\n\n## Related Topics\n\n[Ratings](/docs/7-2/frameworks/-/knowledge_base/f/social-api#ratings)\n\n[Implementing Rating Type Selection](/docs/7-2/frameworks/-/knowledge_base/f/implementing-rating-type-selection)\n\n[Customizing Rating Value Transformation](/docs/7-2/frameworks/-/knowledge_base/f/customizing-rating-value-transformation)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/05-social-api/09-rating-type-selection.markdown",
    "content": "---\nheader-id: implementing-rating-type-selection\n---\n\n# Implementing Rating Type Selection\n\n[TOC levels=1-4]\n\nFor administrators to change your app's rating type (e.g. likes, stars, thumbs), \nyou must implement rating type selection. The steps here show you how. For a \ndetailed explanation of these steps and rating type selection, see \n[Rating Type Selection](/docs/7-2/frameworks/-/knowledge_base/f/social-api#rating-type-selection). \n\n1.  Implement the \n    [`PortletRatingsDefinition`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/definition/PortletRatingsDefinition.html) \n    interface, registering the class as an OSGi component. In the `@Component` \n    annotation, set the `model.class.name` property to the fully qualified name \n    of the class that will use this rating definition. This example rating \n    definition is for a blog entry, so the `model.class.name` property is set to \n    `com.liferay.portlet.blogs.model.BlogsEntry`: \n\n    ```java\n    @Component(\n        property = {\n            \"model.class.name=com.liferay.portlet.blogs.model.BlogsEntry\"\n        }\n    )\n    public class BlogsPortletRatingsDefinition implements PortletRatingsDefinition {...\n    ```\n\n2.  Implement the `PortletRatingsDefinition` methods `getDefaultRatingsType` and \n    `getPortletId` to return the entity's default rating type and the portlet ID \n    of the main portlet that uses the entity, respectively. In this example, the \n    rating type is thumbs and the portlet ID is for the Blogs portlet: \n\n    ```java\n    @Override\n    public RatingsType getDefaultRatingsType() {\n        return RatingsType.THUMBS;\n    }\n\n    @Override\n    public String getPortletId() {\n        return PortletKeys.BLOGS;\n    }\n    ```\n\n## Related Topics\n\n[Rating Type Selection](/docs/7-2/frameworks/-/knowledge_base/f/social-api#rating-type-selection)\n\n[Rating Assets](/docs/7-2/frameworks/-/knowledge_base/f/rating-assets)\n\n[Customizing Rating Value Transformation](/docs/7-2/frameworks/-/knowledge_base/f/customizing-rating-value-transformation)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/05-social-api/10-customizing-rating-transformation.markdown",
    "content": "---\nheader-id: customizing-rating-value-transformation\n---\n\n# Customizing Rating Value Transformation\n\n[TOC levels=1-4]\n\nTo customize rating value transformation, you must create an OSGi component that \nimplements \n[`RatingsDataTransformer`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/transformer/RatingsDataTransformer.html). \nThe steps here show you how. For a detailed explanation of these steps and \nrating value transformation, see \n[Rating Value Transformation](/docs/7-2/frameworks/-/knowledge_base/f/social-api#rating-value-transformation). \n\n1.  Create an OSGi component class that implements `RatingsDataTransformer`: \n\n    ```java\n    @Component\n    public class DummyRatingsDataTransformer implements RatingsDataTransformer {...\n    ```\n\n2.  In this class, implement the `transformRatingsData` method. Note that it \n    contains the `RatingsType` variables `fromRatingsType` and `toRatingsType`: \n\n    ```java\n    @Override\n    public ActionableDynamicQuery.PerformActionMethod transformRatingsData(\n            final RatingsType fromRatingsType, final RatingsType toRatingsType)\n        throws PortalException {\n\n    }\n    ```\n\n3.  In the `transformRatingsData` method, implement the interface \n    `ActionableDynamicQuery.PerformActionMethod` as an anonymous inner class: \n\n    ```java\n    return new ActionableDynamicQuery.PerformActionMethod() {\n\n    };\n    ```\n\n4.  In the anonymous `ActionableDynamicQuery.PerformActionMethod` \n    implementation, implement the `performAction` method to perform your \n    transformation: \n\n    ```java\n    @Override\n    public void performAction(Object object)\n        throws PortalException {\n\n        if (fromRatingsType.getValue().equals(RatingsType.LIKE) &&\n            toRatingsType.getValue().equals(RatingsType.STARS)) {\n\n            RatingsEntry ratingsEntry = (RatingsEntry) object;\n\n            ratingsEntry.setScore(0);\n\n            RatingsEntryLocalServiceUtil.updateRatingsEntry(\n                ratingsEntry);\n        }\n    }\n    ```\n\n    This example irreversibly transforms the rating type from likes to stars, \n    resetting the value to `0`. The `if` statement uses the `fromRatingsType` \n    and `toRatingsType` values to specify that the transformation only occurs \n    when going from likes to stars. The transformation is performed via \n    `RatingsEntry` and its `-LocalServiceUtil`. After getting a \n    [`RatingsEntry`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/model/RatingsEntry.html) \n    object, its `setScore` method sets the rating score to `0`. The \n    [`RatingsEntryLocalServiceUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/ratings/kernel/service/RatingsEntryLocalServiceUtil.html) \n    method `updateRatingsEntry` then updates the `RatingsEntry` in the database. \n\nHere's the complete class for this example: \n\n```java\n@Component\npublic class DummyRatingsDataTransformer implements RatingsDataTransformer {\n    @Override\n    public ActionableDynamicQuery.PerformActionMethod transformRatingsData(\n            final RatingsType fromRatingsType, final RatingsType toRatingsType)\n        throws PortalException {\n\n        return new ActionableDynamicQuery.PerformActionMethod() {\n\n            @Override\n            public void performAction(Object object)\n                throws PortalException {\n\n                if (fromRatingsType.getValue().equals(RatingsType.LIKE) &&\n                    toRatingsType.getValue().equals(RatingsType.STARS)) {\n\n                    RatingsEntry ratingsEntry = (RatingsEntry) object;\n\n                    ratingsEntry.setScore(0);\n\n                    RatingsEntryLocalServiceUtil.updateRatingsEntry(\n                        ratingsEntry);\n                }\n            }\n        };\n    }\n\n}\n```\n\n## Related Topics\n\n[Rating Value Transformation](/docs/7-2/frameworks/-/knowledge_base/f/social-api#rating-value-transformation)\n\n[Implementing Rating Type Selection](/docs/7-2/frameworks/-/knowledge_base/f/implementing-rating-type-selection)\n\n[Rating Assets](/docs/7-2/frameworks/-/knowledge_base/f/rating-assets)\n"
  },
  {
    "path": "en/developer/frameworks/articles/collaboration/05-social-api/11-flagging-assets.markdown",
    "content": "---\nheader-id: flagging-inappropriate-asset-content\n---\n\n# Flagging Inappropriate Asset Content\n\n[TOC levels=1-4]\n\nThe asset framework supports a system for flagging inappropriate content in \napps. The steps here show you how to enable it in your app. \n\n![Figure 1: Users can flag objectionable content.](../../../images/social-flags.png)\n\nFollow these steps to enable content flagging in your app: \n\n1.  Make sure your entity is \n    [asset enabled](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework). \n\n2.  Choose a read-only view of the entity you want to enable flags on. You can \n    display flags in one of your app's views, or if you've \n    [implemented asset rendering](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-asset-renderer) \n    you can display it in the full content view in the Asset Publisher app. \n\n3.  In your JSP, include the `liferay-flags` taglib declaration: \n\n    ```markup\n    <%@ taglib prefix=\"liferay-flags\" uri=\"http://liferay.com/tld/flags\" %>\n    ```\n\n4.  Use \n    [`ParamUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ParamUtil.html) \n    to get the entity's ID from the render request. Then use your \n    `-LocalServiceUtil` class to create an entity object: \n\n    ```java\n    <%\n    long entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n    entry = EntryLocalServiceUtil.getEntry(entryId);\n    %>\n    ```\n\n5.  Use the \n    [`liferay-flags:flags`](@app-ref@/collaboration/latest/taglibdocs/liferay-flags/flags.html) \n    tag to add the flags component: \n\n    ```markup\n    <liferay-flags:flags\n    \tclassName=\"<%= Entry.class.getName() %>\"\n    \tclassPK=\"<%= entry.getEntryId() %>\"\n    \tcontentTitle=\"<%= title %>\"\n    \tmessage=\"flag-this-content\"\n    \treportedUserId=\"<%= reportedUserId %>\"\n    />\n    ```\n\n    The `reportedUserId` attribute specifies the ID of the user who flagged the \n    asset. \n\n## Related Topics\n\n[Rating Assets](/docs/7-2/frameworks/-/knowledge_base/f/rating-assets)\n\n[Social API](/docs/7-2/frameworks/-/knowledge_base/f/social-api)\n\n[Asset Framework](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework)\n"
  },
  {
    "path": "en/developer/frameworks/articles/configuration/01-configuration-intro.markdown",
    "content": "---\nheader-id: configurable-applications\n---\n\n# Configurable Applications\n\n[TOC levels=1-4]\n\nMany applications must be configurable, whether by end users or administrators.\nA configuration solution must support use cases ranging from setting a location\nfor a weather display to more complex cases like settings for a mail or time\nsheet application. \n\nThe Portlet standard's portlet preferences API can be used for portlet\nconfiguration, but it's intended for storing user preferences. This limits its\nusefulness for enabling administrator configuration; plus it can only be used\nwith portlets. Instead, application developers tend to create ad hoc\nconfiguration methods. But this isn't necessary. \n\n@product@'s configuration API is easy to use and is not limited to portlets.\nWhen you define configuration options in a Java interface, Liferay's\nconfiguration framework auto-generates a UI, sparing you the trouble of\ndeveloping an interface for your users to select configuration options.\n\n| **Note:** To see a working application configuration, deploy the\n| `configuration-action` [Blade\n| sample](https://github.com/liferay/liferay-blade-samples/tree/master/gradle/apps/configuration-action)\n| and navigate to System Settings (*Control Panel* &rarr; *Configuration* &rarr;\n| *System Settings*). In the Platorm section's Third Party category, click the\n| *Message display configuration* entry.\n|\n| Add the *Blade Message Portlet* to a page to test your configuration choices.\n\nComplete these three high level tasks to integrate your application with the\nconfiguration framework: \n\n1.  Provide a way to set configurations in the UI.\n\n2.  Set the scope where the application is configured.\n\n3.  Read configuration values in your business logic.\n\n## Using a Configuration Interface\n\nYou can take care of the first two steps by \n[Creating A Configuration Interface](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-configuration-interface).\nThis Java interface does a number of things:\n\n-   Just by existing, it gives you a UI in *System Settings*, so you don't\n    have to write one yourself. Score!\n\n-   It defines the configuration options that will appear in the UI.\n\n-   It defines the type {`int`, `String`, etc.) of values each configuration\n    takes.\n\n-   It defines the scope of your configuration. Bonus in @product-ver@: if your\n    configuration is scoped to anything other than `SYSTEM`, you get an\n    additional UI generated for you in *Instance Settings*. More on scope in\n    a minute.\n\n-   It categorizes your configuration screen so that it can be easily found in\n    *System Settings* and *Instance Settings*. If you skip this the screen will\n    be put in a default location.\n\nA few things you need to know:\n\n**Typed Configuration**\n: The method described here uses *typed* configuration. The application\nconfiguration isn't just a list of key-value pairs. Values can have types, like\n`Integer`, a list of `Strings`, a URL, etc. You can even use your own types,\nalthough that's beyond the scope of this tutorial. Typed configurations are\neasier to use than untyped configurations, and they prevent many programmatic\nerrors. Configuration options should be programmatically explicit, so developers\ncan use autocomplete in modern IDEs to find out all configuration options of\na given application or one of its components.\n\n**Configuration Scope** \n: Scope defines where a configuration value applies. Here are the most common\nconfiguration scopes:\n\n-   `SYSTEM`: Configuration values apply throughout the system.\n\n-   `COMPANY`: One set of configuration values is stored for each\n    virtual instance, so each instance can be configured individually.\n\n-   `GROUP`: Each group can be configured individually.\n\n-   `PORTLET_INSTANCE`: this refers to apps that can be placed on a page as\n    a widget. Each widget can be configured individually.\n\n**Configuration UIs** : When you create a configuration interface of any sort,\na UI is generated for you in *System Settings*. If your configuration is scoped\nto `COMPANY`, `GROUP`, or `PORTLET_INSTANCE`, an additional UI is generated in\n*Instance Settings*. Note that while `GROUP` and `PORTLET_INSTANCE`\nconfigurations appear in the Instance Settings UI, they can only be used to set\ndefaults for the current instance. No corresponding UI is auto-generated to\nconfigure the app at the Site or Portlet level.\n\n| **Note:** An Instance Settings UI is not currently generated for factory\n| configurations. You can track the progress of this issue\n| [here](https://issues.liferay.com/browse/LPS-94490).\n\n**Default Configurations**\n: Default values for any scoped configuration can be set at any wider scope.\nFor example, if your configuration is scoped to the `GROUP`, you can set\na system-wide default in *System Settings, an instance-wide default in\n*Instance Settings*, or both. Any configuration at a narrower scope will always\noverride a configuration at a wider scope.\n\nRead more about configuration scope\n[here](/docs/7-2/user/-/knowledge_base/u/system-settings#configuration-scope).\n\nWhen you complete your configuration interface, you're done with steps 1 and\n2 above.\n\n## Reading Configuration Values\n\nThe final step is to make your app read the configuration values that users\nenter. There are a number of ways to do that:\n\nIf your configuration is scoped to `COMPANY` or `GROUP` you must use\n[`ConfigurationProvider`](/docs/7-2/frameworks/-/knowledge_base/f/reading-scoped-configuration-values)\nThis allows your app to read different configuration values from each site,\nvirtual instance, or whatever the configuration is scoped to.\n\nIf your configuration is scoped to `PORTLET_INSTANCE`, you can still use\n`ConfigurationProvider`, but using `PortletDisplay` is simpler and more\nconvenient. See \n[`PortletDisplay`](/docs/7-2/frameworks/-/knowledge_base/f/reading-scoped-configuration-values#accessing-the-portlet-instance-configuration-through-the-portletdisplay). \n\nIf you only want your app to be configurable at the `SYSTEM` scope, you have\na few options. `ConfigurationProvider` will work fine, but there are\nalternatives that---since they don't need to query multiple sources---can yield\nmodest performance benefits. Which one you use depends on what kind of class\nyou're using to read configuration values. Here are your options:\n\n-   Read with an [MVC portlet's JSP](/docs/7-2/frameworks/-/knowledge_base/f/reading-unscoped-configuration-values-from-an-mvc-portlet#accessing-the-configuration-from-a-jsp)\n\n-   With an [MVC Portlet's Portlet Class](/docs/7-2/frameworks/-/knowledge_base/f/reading-unscoped-configuration-values-from-an-mvc-portlet#accessing-the-configuration-from-the-portlet-class)\n\n-   With any other [Component Class](/docs/7-2/frameworks/-/knowledge_base/f/reading-unscoped-configuration-values-from-a-component)\n\n\n## Further Customization\n\nAt this point you may be asking, \"But what if I don't *like* the auto-generated\nUI?\" Relax. There are a number of ways you can customize it, or even suppress it\nentirely so you can put your own UI in its place.\n\n-   Implement the `ConfigurationFormRenderer` \n    [interface](/docs/7-2/frameworks/-/knowledge_base/f/configuration-form-renderer)\n    to customize the auto-generated UI in system settings.\n\n-   If you need more flexibility---perhaps your app needs multiple configuration\n    screens, or maybe you've already written a configuration UI and just want to\n    insert it without bothering to write a configuration interface---implement\n    the `ConfigurationScreen` interface to implement your own.\n\n-   If you're using a configuration interface but you don't want a UI to be\n    generated---maybe you're using a `ConfigurationScreen` implementation\n    instead, or maybe you just want configuration to be handled programatically\n    or by [.config file](/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files)\n    ---you can [just leave it out](/docs/frameworks/-/knowledge_base/7-2/customizing-the-system-settings-user-interface#excluding-a-configuration-ui-from-system-settings).\n\n-   If you want the UI to render only under certain\n    circumstances, you can write logic to \n    [do that, too](/docs/7-2/frameworks/-/knowledge_base/f/customizing-the-configuration-user-interface#excluding-a-configuration-ui).\n\nEnough conceptual stuff. You're ready to get started with some code. If you\nalready have an app that was configurable under an earlier version of\n@product@, see \n[Upgrading a Legacy App](/docs/7-2/frameworks/-/knowledge_base/f/upgrading-a-legacy-app).\n"
  },
  {
    "path": "en/developer/frameworks/articles/configuration/02-creating-a-configuration-interface.markdown",
    "content": "---\nheader-id: creating-a-configuration-interface\n---\n\n# Creating A Configuration Interface\n\n[TOC levels=1-4]\n\nFirst, you'll learn how to create a configuration with no scope declaration.\nThis automatically scopes your configuration to `SYSTEM`.\n\n1.  Create a Java interface to represent the configuration and its default\n    values. Using a Java interface allows for an advanced type system for each\n    configuration option. Here is the configuration interface for the Liferay\n    Forms application:\n\n    ```java\n    @Meta.OCD(\n        id = \"com.liferay.dynamic.data.mapping.form.web.configuration.DDMFormWebConfiguration\",\n        localization = \"content/Language\", name = \"ddm-form-web-configuration-name\"\n    )\n    public interface DDMFormWebConfiguration {\n\n        @Meta.AD(\n            deflt = \"1\", description = \"autosave-interval-description\",\n            name = \"autosave-interval-name\", required = false\n        )\n        public int autosaveInterval();\n\n        @Meta.AD(\n            deflt = \"descriptive\", name = \"default-display-view\",\n            optionLabels = {\"Descriptive\", \"List\"},\n            optionValues = {\"descriptive\", \"list\"}, required = false\n        )\n        public String defaultDisplayView();\n\n\n    }\n    ```\n\n    This defines two configuration options, the autosave interval (with a default\n    of one minute) and the default display view, which can be descriptive or\n    list, but defaults to descriptive. Here's what the two Java annotations in\n    the above snippet do:\n\n    **Meta.OCD:** Registers this class as a configuration with a specific\n    id. **The ID must be the fully qualified configuration class name.**\n\n    **Meta.AD:** Specifies [optional metadata](http://bnd.bndtools.org/chapters/210-metatype.html) \n    about the field, such as whether it's a required field or if it has\n    a default value.  Note that if you set a field as required and don't specify\n    a default value, the system administrator must specify a value in order for\n    your application to work properly. Use the `deflt` property to specify\n    a default value.\n\n    | **Note:** You can dynamically populate select field options with the \n    | [`ConfigurationFieldsOptionProvider` interface](/docs/7-2/frameworks/-/knowledge_base/f/dynamically-populating-select-list-fields-in-the-configuration-ui)\n\n    The fully-qualified name of the `Meta` class above is\n    `aQute.bnd.annotation.metatype.Meta`. For more information about this class and\n    the `Meta.OCD` and `Meta.AD` annotations, please refer to the \n    [bndtools documentation](http://bnd.bndtools.org/chapters/210-metatype.html).\n\n2.  To use the `Meta.OCD` and `Meta.AD` annotations in your modules, you must\n    [specify a dependency](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies)\n    on the bnd library. We recommend using bnd version 3. Here's an example of\n    how to include this dependency in a Gradle project: \n\n    ```groovy\n    dependencies {\n        compile group: \"biz.aQute.bnd\", name: \"biz.aQute.bndlib\", version: \"3.1.0\"\n    }\n    ```\n\n| **Note:** The annotations `@Meta.OCD` and `@Meta.AD` are part of the bnd\n| library, but as of OSGi standard version R6, they're included in the OSGi core\n| under the names `@ObjectClassDefinition` and `@AttributeDefinition`. The OSGi\n| annotations can be used for simple cases like the one described in this\n| tutorial. However, a key difference between the two libraries is that the bnd\n| annotations are available at runtime, while the OSGi annotations are not.\n| Because runtime availability is necessary for some of the Liferay-specific features\n| described below, we recommend defaulting to the bnd annotations.\n\n| **Also Note:** Your project depends on a `-metatype: *` declaration in its\n| metadata. If you're in a [Liferay\n| Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)(or\n| otherwise applying the [workspace plugin to your\n| build](/docs/7-2/reference/-/knowledge_base/r/gradle-plugins)), it's added\n| automatically at build time. Otherwise, add it manually in your module's\n| `bnd.bnd`. It's required to provide information about your app's configuration\n| options so that a configuration UI can be generated.\n\nWhen you register a configuration interface, a UI is auto-generated for it in\n*System Settings* &rarr; *Platform* &rarr; *Third Party*. That's the default\nlocation; read the next section to learn how to move it somewhere more\nintuitive.\n"
  },
  {
    "path": "en/developer/frameworks/articles/configuration/03-categorizing-configurations.markdown",
    "content": "---\nheader-id: categorizing-the-configuration\n---\n\n# Categorizing the Configuration\n\n[TOC levels=1-4]\n\nBy default, the configuration UI for your app is generated in *System Settings*\n&rarr; *Platform* &rarr; *Third Party*. You probably don't really want it\nthere; by categorizing your configuration you can place it somewhere intuitive\nand easy to find.\n\n| **Note:** If you\n| [scope](/docs/7-2/frameworks/-/knowledge_base/f/scoping-configurations) your\n| configuration so that a UI is generated in Instance Settings as well, your\n| categorization will apply to that UI also.\n\nYou have two options: 1) locate your configuration UI in an existing\ncategory and section, or 2) create your own.\n\nHere are the default  System Settings sections. All available categories are\nnested beneath these sections:\n\n1.  Content and Data\n\n2.  Platform\n\n3.  Security \n\n4.  Commerce\n\n5.  Other\n\n| **Note:** Sections appear if they contain at least one configuration category.\n| Categories appear if they contain at least one configuration. The visible\n| sections and categories depend on the deployed modules.\n\n## Specifying a Configuration Category\n\nSpecify the category for your UI by placing an `@ExtendedObjectClassDefinition`\nannotation in your configuration interface. This example, which appears\nright before the interface's `@Meta.OCD` annotation, places the UI in the\n`dynamic-data-mapping` category in the Content management section:\n\n```java\n@ExtendedObjectClassDefinition(\n    category = \"dynamic-data-mapping\",\n    scope = ExtendedObjectClassDefinition.Scope.GROUP\n)\n```\n\nThis annotation does two things:\n\n-   Specifies the `dynamic-data-mapping` category in the Content Management section.\n\n-   Sets the scope of the configuration. You'll learn more about this \n    [next](/docs/7-2/frameworks/-/knowledge_base/f/scoping-configurations).\n\nThe fully qualified class name of the `@ExtendedObjectClassDefinition` class is\n`com.liferay.portal.configuration.metatype.annotations.ExtendedObjectClassDefinition`.\n\nNote: The infrastructure used by System Settings assumes the `configurationPid`\nis the same as the fully qualified class name of the interface. If they don't\nmatch, it can't provide any information through\n`ExtendedObjectClassConfiguration`.\n\nThe `@ExtendedObjectClassDefinition` annotation is distributed through the\n`com.liferay.portal.configuration.metatype` module, which you can \n[configure as a dependency](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies). \n\n## Creating New Sections and Categories\n\nIf you don't like the default sections and categories, you can create your own\nby implementing the `ConfigurationCategory` interface.\n\nHere's code that creates the *Content and Data* section and the *Dynamic\nData Mapping* category:\n\n```java\n@Component(service = ConfigurationCategory.class)\npublic class DynamicDataMappingConfigurationCategory\n\timplements ConfigurationCategory {\n\n\t@Override\n\tpublic String getCategoryIcon() {\n\t\treturn _CATEGORY_ICON;\n\t}\n\n\t@Override\n\tpublic String getCategoryKey() {\n\t\treturn _CATEGORY_KEY;\n\t}\n\n\t@Override\n\tpublic String getCategorySection() {\n\t\treturn _CATEGORY_SECTION;\n\t}\n\n\tprivate static final String _CATEGORY_ICON = \"dynamic-data-mapping\";\n\n\tprivate static final String _CATEGORY_KEY = \"dynamic-data-mapping\";\n\n\tprivate static final String _CATEGORY_SECTION = \"content-and-data\";\n\n}\n\n```\n\nThe `getCategorySection` method returns the String with the new section's key.\nSimilarly, `getCategoryKey` returns the key for the new category. Provide\nlocalized values for these keys in your module's\n`src/main/resources/content/Language.properties` file.\n\n| **Note:** the language keys for categories and sections must follow\n| a specific format. Prefix each section language key with `category-section.` and\n| each category language key with `category.` For example:\n| \n| `category-section.content-and-data=Content and Data`\n| `category.dynamic-data-mapping=Dynamic Data Mapping`\n\nNext you'll specify the scope of your application's configuration.\n"
  },
  {
    "path": "en/developer/frameworks/articles/configuration/04-scoping-configurations.markdown",
    "content": "---\nheader-id: scoping-configurations\n---\n\n# Scoping Configurations\n\n[TOC levels=1-4]\n\nHere's how to scope a configuration: \n\n1.  Set the scope in the configuration interface.\n\n2.  Enable the configuration for scoped retrieval by creating a configuration\n    bean declaration.\n\n## Step 1: Setting the Configuration Scope\n\nUse the `@ExtendedObjectClassDefinition` annotation to specify the\nconfiguration's scope. The scope you choose must match how the configuration\nobject is retrieved through the \n[configuration provider](/docs/7-2/frameworks/-/knowledge_base/f/reading-scoped-configuration-values).\nPass one of these valid scope options to `@ExtendedObjectClassDefinition`:\n\n`Scope.SYSTEM`: for system scope\n`Scope.COMPANY`: for virtual instance scope\n`Scope.GROUP`: for site scope\n`Scope.PORTLET_INSTANCE`: for the portlet instance scope\n\nHere is an example:\n\n```java\n@ExtendedObjectClassDefinition(\n    category = \"dynamic-data-mapping\",\n    scope = ExtendedObjectClassDefinition.Scope.GROUP\n)\n@Meta.OCD(\n    id = \"com.liferay.dynamic.data.mapping.form.web.configuration.\n        DDMFormWebConfiguration\",\n    localization = \"content/Language\", \n    name = \"ddm-form-web-configuration-name\"\n)\n\npublic interface DDMFormWebConfiguration {\n```\n\n## Step 2: Enabling the Configuration for Scoped Retrieval\n\nTo create a configuration bean declaration:\n\n1.  Register the configuration class by implementing `ConfigurationBeanDeclaration`.\n\n    ```java\n    @Component\n    public class JournalGroupServiceConfigurationBeanDeclaration\n        implements ConfigurationBeanDeclaration {\n    ```\n\n2.  This class has one method that returns the class of the configuration\n    interface you created. It enables the system to keep track of configuration\n    changes as they happen, making requests for the configuration very fast.\n\n    ```java\n    @Override\n    public Class<?> getConfigurationBeanClass() {\n        return JournalGroupServiceConfiguration.class;\n    }\n    ```\n\nThat's all there is to it. Now the configuration is scoped and supports scoped\nretrieval via `ConfigurationProvider`. See the next section for details on\nretrieval.\n"
  },
  {
    "path": "en/developer/frameworks/articles/configuration/05-reading-configurations-configuration-provider.markdown",
    "content": "---\nheader-id: reading-scoped-configuration-values\n---\n\n# Reading Scoped Configuration Values\n\n[TOC levels=1-4]\n\nIf your configuration is scoped to anything other than `SYSTEM`, you have two\noptions for reading configuration values.\n\n-   Use `ConfigurationProvider`. This works for any kind of configuration,\n    and is the only way to read configuration values at the `COMPANY` and\n    `GROUP` scopes.\n\n-   Use `PortletDisplay`. This is the recommended approach for configurations\n    at the `PORTLET_INSTANCE` scope, but only works at that scope.\n\n## Using the Configuration Provider\n\nWhen using the Configuration Provider, instead of receiving the configuration\ndirectly, the class that wants to access it must \n\n1.  Receive a `ConfigurationProvider` to obtain the configuration.\n\n2.  Be registered with a `ConfigurationBeanDeclaration`.\n\nThe tutorial on \n[scoping configurations](/docs/7-2/frameworks/-/knowledge_base/f/scoping-configurations) \ndemonstrates how to register the configuration with a `ConfigurationBeanDeclaration`.\n\nAfter registering with a `ConfigurationBeanDeclaration`, you're ready to use a\n`ConfigurationProvider` to retrieve the scoped configuration. Here's how you\nobtain a reference to it:\n\n1.  Here's the approach for components:\n\n    ```java\n    @Reference(unbind = \"-\")\n    protected void setConfigurationProvider(ConfigurationProvider configurationProvider) {\n        _configurationProvider = configurationProvider;\n    }\n    ```\n\n2.  Here's the approach for Service Builder services:\n\n    ```java\n    @ServiceReference(type = ConfigurationProvider.class)\n    protected ConfigurationProvider configurationProvider;\n    ```\n\n3.  For Spring beans, it is possible to use the same mechanism as for Service\n    Builder services (`@ServiceReference`). \n    \n    <!--Uncomment once article is available\n    Check the documentation on \n    how to integrate Spring beans with OSGi services\n    for more details.\n    -->\n\n4.  For anything else, call the same methods from the utility class,\n    `ConfigurationProviderUtil`. Be sure you call the utility methods in\n    contexts where the portal is guaranteed to be initialized prior to the\n    method call. This class is useful in the \n    [scripting console](/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console),\n    for example. Here's an example method that uses the utility class. It comes\n    from the export-import service, which is only called during the import and\n    export of content from a running portal:\n\n    ```java\n  \tprotected boolean isValidateLayoutReferences() throws PortalException {\n  \t\tlong companyId = CompanyThreadLocal.getCompanyId();\n\n  \t\tExportImportServiceConfiguration exportImportServiceConfiguration =\n  \t\t\tConfigurationProviderUtil.getCompanyConfiguration(\n  \t\t\t\tExportImportServiceConfiguration.class, companyId);\n\n  \t\treturn exportImportServiceConfiguration.validateLayoutReferences();\n  \t}\n    ```\n\nTo retrieve the configuration, use one of the following methods of the provider:\n\n`getCompanyConfiguration()`\n: Used when you want to support different configurations per virtual instance.\nIn this case, the configuration is usually entered by an admin through *Control\nPanel* &rarr; *Configuration* &rarr; *Instance Settings*. \n\n`getGroupConfiguration()`\n: Used when you want to support different configurations per site (or, if\ndesired, per page scope). Usually this configuration is specified by an admin\nthrough the Configuration menu option in an app accessing through the site\nadministration menu. That UI is developed as a portlet configuration view.\n\n`getPortletInstanceConfiguration()`\n: Used to obtain the configuration for a specific portlet instance. Most often\nyou should not be using this directly. Use the convenience method in\n`PortletDisplay` instead as shown below.\n\n`getSystemConfiguration`\n: Used to obtain the configuration for the system scope. These settings are\nspecified by an admin via the System Settings application or with an OSGi\nconfiguration file.\n\nHere are a couple real world examples from Liferay's source code:\n\n```java\nJournalGroupServiceConfiguration configuration =\n    configurationProvider.getGroupConfiguration(\n        JournalGroupServiceConfiguration.class, groupId);\n\nMentionsGroupServiceConfiguration configuration =\n  _configurationProvider.getCompanyConfiguration(\n     MentionsGroupServiceConfiguration.class, entry.getCompanyId());\n```\n\nNext, you'll learn a nifty way to to access a portlet instance configuration\nfrom a JSP.\n\n## Accessing the Portlet Instance Configuration Through the `PortletDisplay`\n\nOften you must access portlet instance settings from a JSP or from a Java class\nthat isn't an OSGi component. To read the settings in these cases, a\nmethod was added to `PortletDisplay`, which is available as a request object.\nHere is an example of how to use it:\n\n```java\nRSSPortletInstanceConfiguration rssPortletInstanceConfiguration =\n    portletDisplay.getPortletInstanceConfiguration(\n        RSSPortletInstanceConfiguration.class);\n```\n\nAs you can see, it knows how to find the values and returns a typed bean\ncontaining them just by passing the configuration class.\n"
  },
  {
    "path": "en/developer/frameworks/articles/configuration/06-reading-configurations-portlet.markdown",
    "content": "---\nheader-id: reading-unscoped-configuration-values-from-an-mvc-portlet\n---\n\n# Reading Unscoped Configuration Values from an MVC Portlet\n\n[TOC levels=1-4]\n\nIf your configuration is scoped to `SYSTEM` or is unscoped (which amounts to\nthe same thing), you have a couple of options for reading configuration values.\nThere are two ways to do this:\n\n-   Add a configuration to the request and read it from the view layer\n    (commonly a JSP).\n\n-   Read values directly from the portlet class.\n\nThis tutorial uses dummy code from a portlet we'll call the Example\nConfiguration Portlet. The import statements are included in the code snippets\nso that you can see the fully qualified class names (FQCNs) of all the classes\nthat are used.\n\n## Accessing the Configuration Object in the Portlet Class\n\nWhether you need the configuration values in the portlet class or the JSPs, the\nfirst step is to get access to the configuration object in the `*Portlet`\nclass.\n\n1.  Imports first:\n\n    ```java\n    package com.liferay.docs.exampleconfig;\n\n    import java.io.IOException;\n    import java.util.Map;\n\n    import javax.portlet.Portlet;\n    import javax.portlet.PortletException;\n    import javax.portlet.RenderRequest;\n    import javax.portlet.RenderResponse;\n\n    import org.osgi.service.component.annotations.Activate;\n    import org.osgi.service.component.annotations.Component;\n    import org.osgi.service.component.annotations.Modified;\n\n    import com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\n\n    import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil;\n    ```\n\n2.  MVC Portlet classes are Component classes. If you have a Bean Portlet or\n    PortletMVC4Spring class, the configuration below goes in `portlet.xml` and\n    `liferay-portlet.xml`. To mate the configuration with the Component, provide\n    the `configurationPid` property with the FQCN of the configuration class. \n\n    ```java\n    @Component(\n        configurationPid = \"com.liferay.docs.exampleconfig.ExampleConfiguration\",\n        immediate = true,\n        property = {\n            \"com.liferay.portlet.display-category=category.sample\",\n            \"com.liferay.portlet.instanceable=true\",\n            \"javax.portlet.security-role-ref=power-user,user\",\n            \"javax.portlet.init-param.template-path=/\",\n            \"javax.portlet.init-param.view-template=/view.jsp\",\n            \"javax.portlet.resource-bundle=content.Language\"\n        },\n        service = Portlet.class\n    )\n    public class ExampleConfigPortlet extends MVCPortlet {\n    ```\n\n    Note that you can specify more than one configuration PID here by enclosing\n    the values in curly braces (`{}`) and placing commas between each PID.\n\n3.  Write an `activate`  method annotated with `@Activate` and `@Modified`.\n    This ensures that the method is invoked when the Component is started, and\n    again whenever the configuration is changed.\n\n    ```java\n    @Activate\n    @Modified\n    protected void activate(Map<String, Object> properties) {\n        _configuration = ConfigurableUtil.createConfigurable(\n        ExampleConfiguration.class, properties);\n    }\n\n    private volatile ExampleConfiguration _configuration;\n    ```\n\nA volatile field `_configuration` is created by the `createConfigurable` method.\nNow the field can be used to retrieve configuration values or to set the values\nin the request, so they can be retrieved in the application's JSPs.\n\n## Accessing the Configuration from a JSP\n\nIn the case of reading from a JSP, add the configuration object to the request\nobject so its values can be read from the JSPs that comprise the application's\nview layer. \n\n1.  Add the configuration object to the request. Here's what it looks like in a\n    simple portlet's `doView` method:\n\n    ```java\n    @Override\n    public void doView(RenderRequest renderRequest,\n        RenderResponse renderResponse) throws IOException, PortletException {\n\n        renderRequest.setAttribute(\n            ExampleConfiguration.class.getName(), _configuration);\n\n        super.doView(renderRequest, renderResponse);\n    }\n    ```\n\n    The main difference between this example and the component class covered in\n    the\n    [next section](/docs/7-2/frameworks/-/knowledge_base/f/reading-unscoped-configuration-values-from-a-component) \n    is that this class is a portlet class and it sets the configuration object\n    as a request attribute in its `doView()` method. \n\n2.  Read configuration values from a JSP. First add these imports to the top of\n    your `view.jsp` file:\n\n    ```markup\n    <%@ page import=\"com.liferay.docs.exampleconfig.ExampleConfiguration\" %>\n    <%@ page import=\"com.liferay.portal.kernel.util.GetterUtil\" %>\n    ```\n\n3.  In the JSP, obtain the configuration object from the request object and read\n    the desired configuration value from it. Here's a `view.jsp` file that does\n    this:\n\n    ```markup\n    <%@ include file=\"/init.jsp\" %>\n\n    <p>\n        <b>Hello from the Example Configuration portlet!</b>\n    </p>\n\n    <%\n    ExampleConfiguration configuration = (ExampleConfiguration) GetterUtil.getObject(\n        renderRequest.getAttribute(ExampleConfiguration.class.getName()));\n\n    String favoriteColor = configuration.favoriteColor();\n    %>\n\n    <p>Favorite color: <span style=\"color: <%= favoriteColor %>;\"><%= favoriteColor %></span></p\n    ```\n\nThe example code here would make the application display a message like this:\n\n    Favorite color: blue\n\nThe word <font color=\"blue\">*blue*</font> is written in blue text. Note that *blue* is\ndisplayed by default since you specified it as the default in your\n`ExampleConfiguration` interface. If you go to *Control Panel* &rarr;\n*Configuration* &rarr; *System Settings* &rarr; *Platform* &rarr; *Third Party*\nand click on the *Example configuration* link, you can find the `Favorite color`\nsetting and change its value. The JSP reads the configuration, and refreshing\nthe UI reflects this update.\n\n## Accessing the Configuration from the Portlet Class \n\nNow that you've seen a detailed example of accessing the configuration values in\na JSP, there's not much more to cover when accessing the configuration directly\nin the `-Portlet` class. Wherever you require the value of a configuration\nproperty, call `_configuration.propertyName` and you have access to the\ncurrently configured value. For example, this code compares the `favoriteColor`\nconfiguration value with a `userFavoriteColor` that's fetched from the request\nobject: \n\n```java\npublic boolean isFavoriteColorMatched {\n\n    String userFavoriteColor = ParamUtil.getString(request, \"userFavoriteColor\");\n\n    if (_configuration.favoriteColor() == userFavoriteColor) {\n\n        SessionMessages.add(request, \"congratulateUser\");\n\n        return true;\n    }\n\n    return false;\n}\n```\n\nIt returns true and adds a success message if the two Strings match each other,\nbut you can do anything that makes sense for your application's controller\nlogic.\n\nThat's all there is to reading configuration values in a Portlet. The next\nsection covers reading configuration values from an OSGi Component class that\nis not part of a portlet.\n"
  },
  {
    "path": "en/developer/frameworks/articles/configuration/07-reading-configurations-component.markdown",
    "content": "---\nheader-id: reading-unscoped-configuration-values-from-a-component\n---\n\n# Reading Unscoped Configuration Values from a Component\n\n[TOC levels=1-4]\n\nFollow these steps to read `SYSTEM` scoped or unscoped configuration values from\na Component that isn't part of a portlet:\n\n1.  First set the `configurationPid` Component property as the fully qualified\n    class name of the configuration class:\n\n    ```java\n    @Component(configurationPid = \"com.liferay.dynamic.data.mapping.form.web.configuration.DDMFormWebConfiguration\")\n    ```\n\n2.  Then provide an `activate` method, annotated with `@Activate` to ensure the\n    method is invoked as soon as the Component is started, and `@Modified` so\n    it's invoked whenever the configuration is modified.\n\n    ```java\n    @Activate\n    @Modified\n    protected void activate(Map<String, Object> properties) {\n        _formWebConfiguration = ConfigurableUtil.createConfigurable(\n            DDMFormWebConfiguration.class, properties);\n    }\n\n    private volatile DDMFormWebConfiguration _formWebConfiguration;\n    ```\n\n    The `activate()` method calls the method\n    `ConfigurableUtil.createConfigurable()` to convert a map of the\n    configuration's properties to a typed class, which is easier to handle. The\n    configuration is stored in a `volatile` field. Don't forget to make it\n    `volatile` to prevent thread safety problems.\n\n3.  Once the activate method is set up, retrieve particular properties from the\n    configuration wherever they're needed:\n\n    ```java \n    public void orderCar(String model) {\n        order(\"car\", model, _configuration.favoriteColor());\n    }\n    ```\n\n    This is dummy code: don't try to find it in the Liferay source code. The\n    String configuration value of `favoriteColor` is passed to the `order`\n    method call, presumably so that whatever model car is ordered gets ordered\n    in the configured favorite color.\n\n| **Note:** The bnd library also provides a class called\n| `aQute.bnd.annotation.metatype.Configurable` with a `createConfigurable()`\n| method. You can use that instead of Liferay's\n| `com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil` without\n| any problems. Liferay's developers created the `ConfigurableUtil` class to\n| improve the performance of bnd's implementation, and it's used in internal code.\n| Feel free to use whichever method you prefer. \n\nWith very few lines of code, you have a configurable application that\ndynamically changes its configuration, has an auto-generated UI, and uses a\nsimple API to access the configuration.\n"
  },
  {
    "path": "en/developer/frameworks/articles/configuration/08-customizing-the-ui/01-customizing-the-configuration-user-interface-intro.markdown",
    "content": "---\nheader-id: customizing-the-configuration-user-interface\n---\n\n# Customizing the Configuration User Interface\n\n[TOC levels=1-4]\n\nThere are three ways to customize a configuration UI.\n\n-   Provide a custom form for a configuration object. This modifies the\n    auto-generated UI.\n\n-   Write a completely custom configuration UI. This is useful especially if you\n    aren't using the Configuration Admin service or any of Liferay's\n    Configuration APIs.\n\n-   Exclude a configuration object. You'll want this option if you're using\n    a configuration interface but don't wan't a UI generated for you.\n\n## Providing Custom Configuration Forms\n\nCustomize your auto-generated UI by implementing the `ConfigurationFormRender`\ninterface. To write this interface, you must refer to your configuration\ninterface. For this example, refer to this configuration interface from\nLiferay's Currency Converter application:\n\n```java\n@ExtendedObjectClassDefinition(category = \"localization\")\n@Meta.OCD(\n    id = \"com.liferay.currency.converter.web.configuration.CurrencyConverterConfiguration\",\n    localization = \"content/Language\",\n    name = \"currency-converter-configuration-name\"\n)\npublic interface CurrencyConverterConfiguration {\n\n    @Meta.AD(deflt = \"GBP|CNY|EUR|JPY|USD\", name = \"symbols\", required = false)\n    public String[] symbols();\n}\n```\n\nThis example defines one configuration option, `symbols`, which takes an array\nof values. \n\nImplement `ConfigurationFormRenderer`'s three methods:\n\n1.  `getPid`: Return the configuration object's ID. This is defined in the `id`\n    property in the `*Configuration` class's `@Meta.OCD` annotation.\n\n2.  `getRequestParameters`: Read the parameters sent by the custom form and put\n    them in a Map whose keys should be the method names of the Configuration\n    interface.\n\n3.  `render`: Render the custom form's fields, using your desired method (for\n    example, JSPs or another template mechanism). The `<form>` tag itself is\n    provided automatically and shouldn't be included in the\n    `ConfigurationFormRenderer`.\n\nHere's a complete `ConfigurationFormRenderer` implementation:\n\n```java\n@Component(immediate = true, service = ConfigurationFormRenderer.class)\npublic class CurrencyConverterConfigurationFormRenderer\n    implements ConfigurationFormRenderer {\n\n    @Override\n    public String getPid() {\n        return \"com.liferay.currency.converter.web.configuration.CurrencyConverterConfiguration\";\n    }\n\n    @Override\n    public void render(HttpServletRequest request, HttpServletResponse response)\n            throws IOException {\n            \n        String formHtml = \"<input name=\\\"mysymbols\\\" />\";\n\n        PrintWriter writer = response.getWriter();\n\n        writer.print(formHtml);\n\n    }\n\n    @Override\n    public Map<String, Object> getRequestParameters(\n            HttpServletRequest request) {\n\n        Map<String, Object> params = new HashMap<>();\n\n        String[] mysymbols = ParamUtil.getParameterValues(request, \"mysymbols\");\n\n        params.put(\"symbols\", mysymbols);\n\n        return params;\n    }\n}\n```\n\nThe above example generates a custom rendering (HTML) for the form in the\n`render()` method and reads the information entered in the custom form in the\n`getRequestParameters()` method.\n\nTo see a complete demonstration, including JSP markup, read the dedicated\ntutorial on creating a \n[configuration form renderer](/docs/7-2/frameworks/-/knowledge_base/f/configuration-form-renderer).\n\n## Creating a Completely Custom Configuration UI\n\nYou get more flexibility if you create a completely custom UI using\na `ConfigurationScreen` implementation.\n\nAt a high level you must\n\n1.  Write a Component that declares itself an implementation of the\n    `ConfigurationScreen` interface.\n\n2.  Implement `ConfigurationScreen`'s methods.\n\n3.  Create the UI by hand.\n\nHere's an example implementation:\n\n```java\n@Component(immediate = true, service = ConfigurationScreen.class) \npublic class SampleConfigurationScreen implements ConfigurationScreen {\n```\n\nFirst declare the class an implementation of `ConfigurationScreen`.\n\n```java\n@Override \npublic String getCategoryKey() { \n\n    return \"third-party\"; \n\n}\n\n@Override \npublic String getKey() { \n\n    return \"sample-configuration-screen\"; \n\n}\n\n@Override \npublic String getName(Locale locale) { \n\n    return \"Sample Configuration Screen\"; \n\n}\n```\n\nSecond, set the category key, the configuration entry's key, and its localized\nname. This example puts the configuration entry, keyed\n`sample-configuration-screen`, into the `third-party` System Settings section.\nThe String that appears in System Settings is _Sample Configuration Screen_.\n\n```java\n@Override \npublic String getScope() { \n\n    return \"system\"; \n\n}\n```\n\nThird, set the \n[configuration scope](/docs/7-2/frameworks/-/knowledge_base/f/scoping-configurations).\n\n```java\n@Override \npublic void render(HttpServletRequest request, HttpServletResponse response) \n        throws IOException {\n\n    _jspRenderer.renderJSP( _servletContext, request, response,\n    \"/sample_configuration_screen.jsp\"); \n\n}\n\n@Reference private JSPRenderer _jspRenderer;\n\n@Reference(\n    target =\"(osgi.web.symbolicname=com.liferay.currency.converter.web)\", \n    unbind = \"-\")\nprivate ServletContext _servletContext;\n```\n\nThe most important step is to write the `render` method. This example relies on\nthe `JSPRenderer` service to delegate rendering to a JSP.\n\nIt's beyond the scope of this tutorial to write the JSP markup. A separate\ntutorial will provide a complete demonstration of the `ConfigurationScreen` and\nimplementation and the JSP markup to demonstrate its usage.\n\n## Excluding a Configuration UI\n\nIf you don't want a UI to be generated for you, you have two options.\n\n-   If you don't want a UI generated no matter what, use the `generateUI` property.\n\n-   If you only want the UI to render under specific circumstances (defined by\n    logic you'll write yourself), use the configuration visibility SPI.\n\n### Using `generateUI`\n\nTo turn off auto-generating at all scopes, include the\n`ExtendedObjectClassDefinition` annotation property `generateUI` in your\nconfiguration interface. The property defaults to `true`; here is an example\nsetting it to `false`:\n\n```java\n@ExtendedObjectClassDefinition(generateUI=false)\n@Meta.OCD(\n  id = \"com.foo.bar.LowLevelConfiguration\",\n)\npublic interface LowLevelConfiguration {\n\n  public String[] foo();\n  public String bar();\n\n}\n```\n\nNow no UI is auto-generated for this configuration. You can still manage\nthe configuration via a `ConfigurationScreen` implementation, a \n[.config file](/docs/7-2/user/-/knowledge_base/u/understanding-system-configuration-files),\nor programmatically.\n\n### Using the Configuration Visibility SPI\n\nThe configuration visibility SPI involves implementing a single interface,\n`ConfigurationVisibilityController`. You can see the whole interface\n[here](https://github.com/liferay/liferay-portal/blob/48cd71b35a2d3b66e88f47685be7186cb7c52075/modules/apps/configuration-admin/configuration-admin-api/src/main/java/com/liferay/configuration/admin/display/ConfigurationVisibilityController.java).\n\nTo implement the interface, you must identify your configuration interface\nusing an `@Component` property, then write your own logic for the interface's\nonly method, `isVisible`. Here is a sample implementation from Liferay's source\ncode:\n\n```java\n@Component(\n\timmediate = true,\n\tproperty = \"configuration.pid=com.liferay.sharing.internal.configuration.SharingCompanyConfiguration\",\n\tservice = ConfigurationVisibilityController.class\n)\npublic class SharingCompanyConfigurationVisibilityController\n\timplements ConfigurationVisibilityController {\n\n\t@Override\n\tpublic boolean isVisible(\n\t\tExtendedObjectClassDefinition.Scope scope, Serializable scopePK) {\n\n\t\tSharingConfiguration systemSharingConfiguration =\n\t\t\t_sharingConfigurationFactory.getSystemSharingConfiguration();\n\n\t\treturn systemSharingConfiguration.isEnabled();\n\t}\n\n\t@Reference\n\tprivate SharingConfigurationFactory _sharingConfigurationFactory;\n\n}\n```\n\nNote that the property `configuration.pid` identifies the configuration\ninterface of the UI to be hidden. In this example, the configuration UI \nonly renders when `systemSharingConfiguration.isEnabled` returns `true`.\n"
  },
  {
    "path": "en/developer/frameworks/articles/configuration/08-customizing-the-ui/02-configuration-form-renderer.markdown",
    "content": "---\nheader-id: configuration-form-renderer\n---\n\n# Configuration Form Renderer\n\n[TOC levels=1-4]\n\nTo replace an application's auto-generated configuration screen with a form\nbuilt from scratch, you follow these steps:\n\n1.  Use a `DisplayContext` class to transfer data between back-end code and the\n    desired JSP markup.\n2.  Implement the `ConfigurationFormRenderer` interface.\n3.  Render the configuration form. This tutorial demonstrates the use of a JSP\n    and the previously created `DisplayContext` class.\n\nA generalized discussion on System Settings UI customization is found in a\n[separate section](/docs/7-2/frameworks/-/knowledge_base/f/customizing-the-configuration-user-interface).\n\nThis article demonstrates replacing the configuration UI for the _Language\nTemplate_ System Settings entry, found in *Control Panel* &rarr; *Configuration*\n&rarr; *System Settings* &rarr; *Localization* &rarr; *Language Template*. The same\nsteps apply when replacing your custom application's auto-generated UI. \n\n![Figure 1: The auto-generated UI for the Language Template configuration screen is sub-optimal. A select list with more human readable options is preferable.](../../../images/sys-settings-lang-template-default.png)\n\nSpecifically, the text input field labeled *DDM Template Key* in the\nauto-generated UI is replaced with a select list field type called *Language\nSelection Style*, populated with all possible DDM Template Keys.\n\n## Creating a `DisplayContext`\n\nA `DisplayContext` class is a POJO that simplifies and minimizes the use of Java\nlogic in JSPs. Display context usage isn't required, but it's a nice convention\nto follow. It's a kind of data transfer object, where the `DisplayContext`'s\nsetters are called from the Java class providing the render logic (in this case\nthe `ConfigurationFormRenderer`'s `render` method), and the getters are called\nfrom the JSP, removing the need for Java logic to be written inside the JSP\nitself.\n\nFor this example, create a `LanguageTemplateConfigurationDisplayContext` class\nwith these contents:\n\n```java\npublic class LanguageTemplateConfigurationDisplayContext {\n\n    public void addTemplateValue(\n        String templateKey, String templateDisplayName) {\n\n        _templateValues.add(new String[] {templateKey, templateDisplayName});\n    }\n\n    public String getCurrentTemplateName() {\n        return _currentTemplateName;\n    }\n\n    public String getFieldLabel() {\n        return _fieldLabel;\n    }\n\n    public List<String[]> getTemplateValues() {\n        return _templateValues;\n    }\n\n    public void setCurrentTemplateName(String currentTemplateName) {\n        _currentTemplateName = currentTemplateName;\n    }\n\n    public void setFieldLabel(String fieldLabel) {\n        _fieldLabel = fieldLabel;\n    }\n\n    private String _currentTemplateName;\n    private String _fieldLabel;\n    private final List<String[]> _templateValues = new ArrayList<>();\n\n}\n```\n\nNext implement the `ConfigurationFormRenderer`.\n\n## Implementing a `ConfigurationFormRenderer`\n\nFirst create the component and class declarations. Set the `service` property\nto `ConfigurationFormRenderer.class`:\n\n```java\n@Component(\n    configurationPid = \"com.liferay.site.navigation.language.web.configuration.SiteNavigationLanguageWebTemplateConfiguration\",\n    immediate = true, service = ConfigurationFormRenderer.class\n)\npublic class LanguageTemplateConfigurationFormRenderer\n    implements ConfigurationFormRenderer {\n```\n\nNext, write an `activate` method (decorated with `@Activate` and `@Modified`)\nto convert a map of the configuration's properties to a typed class. The\nconfiguration is stored in a volatile field. Don't forget to make it volatile\nto prevent thread safety problems. See the article on \n[reading configuration values from a component class](/docs/7-2/frameworks/-/knowledge_base/f/reading-unscoped-configuration-values-from-a-component) \nfor more information.\n\n```java\n@Activate\n@Modified\npublic void activate(Map<String, Object> properties) {\n    _siteNavigationLanguageWebTemplateConfiguration =\n        ConfigurableUtil.createConfigurable(\n            SiteNavigationLanguageWebTemplateConfiguration.class,\n            properties);\n}\n\nprivate volatile SiteNavigationLanguageWebTemplateConfiguration\n    _siteNavigationLanguageWebTemplateConfiguration;\n```\n\nNext override the `getPid` and `getRequestParameters` methods:\n\n```java\n@Override\npublic String getPid() {\n    return \"com.liferay.site.navigation.language.web.configuration.\" +\n        \"SiteNavigationLanguageWebTemplateConfiguration\";\n}\n```\n\nReturn the full configuration ID, as specified in the `*Configuration` class's\n`@Meta.OCD` annotation.\n\n```java\n@Override\npublic Map<String, Object> getRequestParameters(\n    HttpServletRequest request) {\n\n    Map<String, Object> params = new HashMap<>();\n\n    String ddmTemplateKey = ParamUtil.getString(request, \"ddmTemplateKey\");\n\n    params.put(\"ddmTemplateKey\", ddmTemplateKey);\n\n    return params;\n}\n```\n\nIn the `getRequestParameters` method, map the parameters sent by the custom form\n(obtained from the request) to the keys of the fields in the Configuration\ninterface.\n\nProvide the render logic via the overridden `render` method. The rendering\napproach demonstrated here uses a JSP. Recall that it's backed by a\n`DisplayContext` class set into the request object. The values set from this\n`render` method are available in the JSP via the `DisplayContext` object's\ngetters. \n\nLoop through the DDM Template Keys for the given `groupId` and set them into\nthe display context with the `addTemplateKey` method. Then set the other\nnecessary values that the JSP needs. In this case, set the title, the field\nlabel, and the redirect URL. Finally, call `renderJSP` and pass in the\n`servletContext`, request, response, and the path to the JSP: \n\n```java\n@Override\npublic void render(HttpServletRequest request, HttpServletResponse response)\n    throws IOException {\n\n    Locale locale = request.getLocale();\n\n    LanguageTemplateConfigurationDisplayContext\n        languageTemplateConfigurationDisplayContext =\n            new LanguageTemplateConfigurationDisplayContext();\n\n    languageTemplateConfigurationDisplayContext.setCurrentTemplateName(\n        _siteNavigationLanguageWebTemplateConfiguration.ddmTemplateKey());\n\n    long groupId = 0;\n\n    long companyId = _portal.getCompanyId(actionRequest);\n\n    Group group = _groupLocalService.fetchCompanyGroup(companyId);\n\n    if (group != null) {\n        groupId = group.getGroupId();\n    }\n\n    List<DDMTemplate> ddmTemplates = _ddmTemplateLocalService.getTemplates(\n        groupId, _portal.getClassNameId(LanguageEntry.class));\n\n    for (DDMTemplate ddmTemplate : ddmTemplates) {\n        languageTemplateConfigurationDisplayContext.addTemplateValue(\n            ddmTemplate.getTemplateKey(), ddmTemplate.getName(locale));\n    }\n\n    languageTemplateConfigurationDisplayContext.setFieldLabel(\n        LanguageUtil.get(\n            ResourceBundleUtil.getBundle(\n                locale, LanguageTemplateConfigurationFormRenderer.class),\n            \"language-selection-style\"));\n\n    request.setAttribute(\n        LanguageTemplateConfigurationDisplayContext.class.getName(),\n        languageTemplateConfigurationDisplayContext);\n\n    _jspRenderer.renderJSP(\n        _servletContext, request, response,\n        \"/configuration/site_navigation_language_web_template.jsp\");\n}\n```\n\nSpecify the required service references at the bottom of the class. Be careful\nto target the proper servlet context, passing the `bundle-SymbolicName` of the\nmodule (found in its `bnd.bnd` file) into the `osgi.web.symbolicname` property\nof the reference target:\n\n```java\n@Reference\nprivate DDMTemplateLocalService _ddmTemplateLocalService;\n\n@Reference\nprivate GroupLocalService _groupLocalService;\n\n@Reference\nprivate JSPRenderer _jspRenderer;\n\n@Reference\nprivate Portal _portal;\n\n@Reference(\n    target = \"(osgi.web.symbolicname=com.liferay.site.navigation.language.web)\",\n    unbind = \"-\"\n)\nprivate ServletContext _servletContext;\n```\n\nOnce the configuration form renderer is implemented, you can write the JSP\nmarkup for the form.\n\n## Writing the JSP Markup\n\nNow write the JSP:\n\n```markup\n<%@ include file=\"/init.jsp\" %>\n\n<%\nLanguageTemplateConfigurationDisplayContext\n    languageTemplateConfigurationDisplayContext = (LanguageTemplateConfigurationDisplayContext)request.getAttribute(LanguageTemplateConfigurationDisplayContext.class.getName());\n\nAdmin: Instance Settings    String currentTemplateName = languageTemplateConfigurationDisplayContext.getCurrentTemplateName();\n%>\n\n<aui:select label=\"<%= HtmlUtil.escape(languageTemplateConfigurationDisplayContext.getFieldLabel()) %>\" name=\"ddmTemplateKey\" value=\"<%= currentTemplateName %>\">\n\n    <%\n    for (String[] templateValue : languageTemplateConfigurationDisplayContext.getTemplateValues()) {\n    %>\n\n        <aui:option label=\"<%= templateValue[1] %>\" selected=\"<%= currentTemplateName.equals(templateValue[0]) %>\" value=\"<%= templateValue[0] %>\" />\n\n    <%\n    }\n    %>\n\n</aui:select>\n```\n\nThe opening scriptlet gets the display context object from the request so that\nall its getters are invoked whenever information from the back-end is required.\nRight away, the `getCurrentTemplateName` method is called, since the current\ntemplate name is needed for the first option's `ddmTemplateKey` display value as\nsoon as the form is rendered. This happens in the `<aui:select>` tag. There's\njust a bit of logic used to create an option for each of the available DDM\ntemplates that can be chosen.\n\nSo what does this example look like when all is said and done?\n\n![Figure 2: A select list provides a more user friendly configuration experience than a text field.](../../../images/sys-settings-lang-template-custom.png)\n\nNow, administrators encountering the Language Template entry in System Settings\nwon't be handicapped by not knowing the available DDM Template Keys. Providing\nthe available values in a select field wildly enhances the user experience.\n"
  },
  {
    "path": "en/developer/frameworks/articles/configuration/08-customizing-the-ui/03-configuration-forms.markdown",
    "content": "---\nheader-id: using-ddm-form-annotations-in-configuration-forms\n---\n\n# Using DDM Form Annotations in Configuration Forms\n\n[TOC levels=1-4]\n\nThe auto-generated configuration form you get by just creating a \n[configuration interface](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-configuration-interface)\ncan be too simplistic for some configurations. To enhance it, use the Dynamic\nData Mapping (DDM) Form Annotations.\n\nTo use DDM Annotations in configuration forms, \n\n1.  Configure the module dependencies.\n\n2.  Write a `ConfigurationForm` class, including just the fields that you want\n    to leverage the enhanced forms capability. This is similar to the\n    configuration interface, but with field annotations from the Liferay\n    [Dynamic Data Mapping API](https://github.com/liferay/liferay-portal/tree/7.2.0-ga1/modules/apps/dynamic-data-mapping/dynamic-data-mapping-api/src/main/java/com/liferay/dynamic/data/mapping/annotations) \n    rather than the bndtools metatype specification. The fields here must match\n    fields defined in the configuration interface.\n\n3.  Implement a [`ConfigurationDDMFormDeclaration`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/configuration-admin/configuration-admin-api/src/main/java/com/liferay/configuration/admin/definition/ConfigurationDDMFormDeclaration.java) \n    to mark your configuration as having a `ConfigurationForm`.\n\nThis article assumes you already have an auto-generated configuration UI.\n\nNote that the example code here splits up the Configuration interface and the\nConfiguration Form interface. If you'd rather mash these together into one\nclass, you can. This approach might make sense if you have a small number of\nfields in your configuration and a simple form to create. If you have numerous\nconfiguration fields and/or a complex form to create, or if you're taking an\nexisting configuration and extending it to use the DDM Form annotations, you can\nconsider separating the classes, as shown here.\n\n### Step 1: Declare the Dependencies\n\nIn the `build.gradle` file, add `compileOnly` dependencies on the\n`dynamic-data-mapping-api` and `configuration-admin-api` module artifacts:\n\n```groovy\ncompileOnly group: \"com.liferay\", name: \"com.liferay.dynamic.data.mapping.api\", version: \"5.2.0\"\ncompileOnly group: \"com.liferay\", name: \"com.liferay.configuration.admin.api\", version: \"2.0.2\"\n```\n\n### Step 2: Write the Configuration Form\n\nThis step requires annotating the class with `@DDMForm` to set up the form, and\nannotating each method with `@DDMFormField`. Begin by creating the class body,\nannotating each configuration field (interface method) with `@DDMFormField`:\n\n```java\npublic interface MyFooConfigurationForm {\n\n\t@DDMFormField(\n\t\tlabel = \"%label-key-for-field-1\",\n\t\ttip = \"%description-key-for-field-1\",\n\t\tproperties = {\n\n\t\t\t\"placeholder=%enter-a-value\",\n\t\t\t\"tooltip=%some-tooltip-text\"\n\t\t}\n\t)\n\tpublic String[] textArrayValues();\n\n\t@DDMFormField(\n\t\tlabel = \"%date\",\n\t\ttip = \"%date-description\",\n\t\ttype = \"date\")\n\tpublic String date();\n\n\t@DDMFormField(\n\t\tlabel = \"%select\",\n\t\toptionLabels = {\"%foo\", \"%bar\"},\n\t\toptionValues = {\"foo\", \"bar\"},\n\t\ttype = \"select\")\n\tpublic String select();\n\n\t@DDMFormField(\n\t\tlabel = \"%numeric\",\n\t\tproperties = {\n\t\t\t\"placeholder=%milliseconds\",\n\t\t\t\"tooltip=%enter-an-integer-between-1000-and-30000\"\n\t\t},\n\t\tvalidationErrorMessage = \"%please-enter-an-integer-between-1000-and-30000-milliseconds\",\n\t\tvalidationExpression = \"(numeric >= 1000) && (numeric <= 30000)\",\n\t\ttype = \"numeric\")\n\tpublic String numeric();\n\n\t@DDMFormField(\n\t\tlabel = \"%checkbox\",\n\t\tproperties = \"showAsSwitcher=true\")\n\tpublic boolean checkbox();\n\n}\n```\n\nOnce the field annotations are in place, lay out the form itself, right above\nthe class declaration. This example shows the layout of the\n`UserFileUploadsConfigurationForm`, so that you can see the resulting form via\nthe below screenshot:\n\n```java\n@DDMForm\n@DDMFormLayout(\n\tpaginationMode = com.liferay.dynamic.data.mapping.model.DDMFormLayout.SINGLE_PAGE_MODE,\n\tvalue = {\n\t\t@DDMFormLayoutPage(\n\t\t\t{\n\t\t\t\t@DDMFormLayoutRow(\n\t\t\t\t\t{\n\t\t\t\t\t\t@DDMFormLayoutColumn(\n\t\t\t\t\t\t\tsize = 12,\n\t\t\t\t\t\t\tvalue = {\n\t\t\t\t\t\t\t\t\"imageCheckToken\", \"imageDefaultUseInitials\",\n\t\t\t\t\t\t\t\t\"imageMaxSize\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t),\n\t\t\t\t@DDMFormLayoutRow(\n\t\t\t\t\t{\n\t\t\t\t\t\t@DDMFormLayoutColumn(\n\t\t\t\t\t\t\tsize = 6, value = \"imageMaxHeight\"\n\t\t\t\t\t\t),\n\t\t\t\t\t\t@DDMFormLayoutColumn(size = 6, value = \"imageMaxWidth\")\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t\t}\n\t\t)\n\t}\n)\npublic interface MyFooConfigurationForm {\n```\n\n![Figure 1: The DDM annotations are used to lay out this configuration form.](../../../images/configuration-ddm-form.png)\n\nNext, you must make sure the configuration framework knows about your slick\nform.\n\n### Step 3: Write the Form Declaration\n\nCreate a new implementation of `ConfigurationDDMFormDeclaration` to register\nyour new configuration form class:\n\n```java\npackage com.liferay.docs.my.foo.configuration.definition;\n\nimport com.liferay.configuration.admin.definition.ConfigurationDDMFormDeclaration;\nimport org.osgi.service.component.annotations.Component;\n...\n\n@Component(\n\timmediate = true,\n\tproperty = \"configurationPid=com.liferay.docs.my.foo.configuration.MyFooConfiguration\",\n\tservice = ConfigurationDDMFormDeclaration.class\n)\npublic class MyFooConfigurationDDMFormDeclaration\n\timplements ConfigurationDDMFormDeclaration {\n\n\t@Override\n\tpublic Class<?> getDDMFormClass() {\n\t\treturn MyFooConfigurationForm.class;\n\t}\n\n}\n```\n\nThe `configurationPid` must match the fully qualified class name of the\nconfiguration interface.\n\nNow your configuration class is backed by the form-building power of Liferay's\nnative [Forms application](/docs/7-2/user/-/knowledge_base/u/forms).\n\nTo see how this is done for one of Liferay's own configurations, check out all\nof the configuration classes for the User Images configuration (Control Panel\n&rarr; Configuration &rarr; System Settings &rarr; User Images):\n\n[`UserFileUploadsConfigurationForm`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/users-admin/users-admin-api/src/main/java/com/liferay/users/admin/configuration/definition/UserFileUploadsConfigurationForm.java)\n\n[`UserFileUploadsConfiguration.java`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/users-admin/users-admin-api/src/main/java/com/liferay/users/admin/configuration/UserFileUploadsConfiguration.java)\n\n[`UserFileUploadsConfigurationBeanDeclaration.java`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/users-admin/users-admin-web/src/main/java/com/liferay/users/admin/web/internal/configuration/definition/UserFileUploadsConfigurationBeanDeclaration.java)\n\n[`UserFileUploadsConfigurationDDMFormDeclaration.java`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/users-admin/users-admin-web/src/main/java/com/liferay/users/admin/web/internal/configuration/definition/UserFileUploadsConfigurationDDMFormDeclaration.java)\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/configuration/10-upgrading-a-legacy-app.markdown",
    "content": "---\nheader-id: upgrading-a-legacy-app\n---\n\n# Upgrading a Legacy App\n\n[TOC levels=1-4]\n\nIf you have an app that was made configurable under an earlier version of\n@product@, you can upgrade without having to reconfigure any of your app's\ninstances.\n\nIf you have an app that was configurable using the mechanisms of Liferay Portal\n6.2 and before, refer to \n[Transitioning from Portlet Preferences to the Configuration API](/docs/7-0/tutorials/-/knowledge_base/t/transitioning-from-portlet-preferences-to-the-configuration-api).\n\nIf you have an app with a configuration interface scoped to anything other than\n`SYSTEM` and a custom UI for saving configuration values to\n`PortletPreferences`, you have two options:\n\n-   Keep using your custom UI. Deactivate the auto-generated UI in *Instance\n    Settings* by setting the scope in your configuration interface to `SYSTEM`.\n    This is quick and easy, but won't make your code easier to maintain in the\n    long term.\n\n    For other ways to disable the auto-generated UI, see\n    [Excluding a Configuration UI](/docs/7-2/frameworks/-/knowledge_base/f/customizing-the-configuration-user-interface#excluding-a-configuration-ui)\n\n-   Write an Upgrade Process to convert your configuration values in\n    `PortletPreferences` to an instance-scoped OSGi configuration, using the\n    `saveCompanyConfiguration` method in the `ConfigurationProvider` interface.\n\n| You don't have to use `saveCompanyConfiguration`, but doing so meets all\n| the necessary requirements for an upgrade process: it must be a factory\n| instance with a factory PID of `Unknown macro:[base-pid].scoped`, and it\n| must contain a `companyId` property.\n\nThen remove your custom UI. If you're reading configuration values\nusing `ConfigurationProvider`'s `getCompanyConfiguration` method, the\nauto-generated UI picks up where you left off, with no need to reconfigure\nanything.\n"
  },
  {
    "path": "en/developer/frameworks/articles/configuration/11-dynamically-populating-select-fields.markdown",
    "content": "---\nheader-id: dynamically-populating-select-list-fields-in-the-configuration-ui\n---\n\n# Dynamically Populating Select List Fields in the Configuration UI\n\n[TOC levels=1-4]\n\nYou've always been able to provide a select list for your configuration options\nby entering each label and value directly in the `@Meta.AD` annotation of the\n[Configuration interface](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-configuration-interface).\n\n```java\n@Meta.AD(\n    deflt = \"enabled-with-warning\", name = \"csv-export\",\n    optionLabels = {\"enabled\", \"enabled-with-warning\", \"disabled\"},\n    optionValues = {\"enabled\", \"enabled-with-warning\", \"disabled\"},\n    required = false\n)\npublic String csvExport();\n```\n\nNow, thanks to the [`ConfigurationFieldOptionsProvider`\ninterface](@app-ref@/configuration-admin/latest/javadocs/com/liferay/configuration/admin/definition/ConfigurationFieldOptionsProvider.html),\nyou can populate select list configurations dynamically, using custom logic. \n\nFollow these steps to populate the select list fields dynamically in your \nconfiguration UI:\n\n1.  Use an `@Component` annotation to register the\n    `ConfigurationFieldOptionsProvider.class` service and include two\n    properties:\n\n    `configuration.field.name`: The name of the attribute in the configuration \n    interface\n\n    `configuration.pid`: The ID of the corresponding configuration interface \n    (usually the fully qualified class name)\n\n    For example,\n\n    ```java\n    @Component(\n    \tproperty = {\n    \t\t\"configuration.field.name=enabledClassNames\",\n    \t\t\"configuration.pid=com.liferay.asset.auto.tagger.google.cloud.natural.language.internal.configuration.GCloudNaturalLanguageAssetAutoTaggerCompanyConfiguration\",\n    \t\t\"configuration.pid=com.liferay.asset.auto.tagger.opennlp.internal.configuration.OpenNLPDocumentAssetAutoTaggerCompanyConfiguration\"\n    \t},\n    \tservice = ConfigurationFieldOptionsProvider.class\n    )\n    ```\n\n2.  Implement the `ConfigurationFieldOptionsProvider` interface:\n\n    ```java    \n    public class MyConfigurationFieldOptionsProvider implements \n    ConfigurationFieldOptionsProvider {\n        ..\n    }\n    ```\n\n3.  The `getOptions` method returns a list of `Option`s consisting of the\n    label and value fields. The labels provided here are translated to\n    `optionLabels`, and the values as `optionValues`, in the configuration\n    interface. \n\n    ```java    \n    public List<Option> getOptions() {\n    \tList<AssetRendererFactory<?>> assetRendererFactories =\n    \t\tAssetRendererFactoryRegistryUtil.getAssetRendererFactories(\n    \t\t\tCompanyThreadLocal.getCompanyId());\n\n    \tStream<AssetRendererFactory<?>> stream =\n    \t\tassetRendererFactories.stream();\n\n    \treturn stream.filter(\n    \t\tassetRendererFactory -> {\n    \t\t\tTextExtractor textExtractor =\n    \t\t\t\t_textExtractorTracker.getTextExtractor(\n    \t\t\t\t\tassetRendererFactory.getClassName());\n\n    \t\t\treturn textExtractor != null;\n    \t\t}\n    \t).map(\n    \t\tassetRendererFactory -> new Option() {\n\n    \t\t\t@Override\n    \t\t\tpublic String getLabel(Locale locale) {\n    \t\t\t\treturn assetRendererFactory.getTypeName(locale);\n    \t\t\t}\n\n    \t\t\t@Override\n    \t\t\tpublic String getValue() {\n    \t\t\t\treturn assetRendererFactory.getClassName();\n    \t\t\t}\n\n    \t\t}\n    \t).collect(\n    \t\tCollectors.toList()\n    \t);\n    }\n    ```\n\n    This code gets a list of `AssetRendererFactory` objects and iterates through\n    the list, populating a new list of `Option`s, using the asset's type name as\n    the label and the class name as the value. It comes from the\n    [`EnabledClassNamesConfigurationFieldOptionsProvider`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/asset/asset-auto-tagger-service/src/main/java/com/liferay/asset/auto/tagger/internal/configuration/admin/definition/EnabledClassNamesConfigurationFieldOptionsProvider.java),\n    which populates the configuration field labeled _Enable Google Cloud Natural\n    Language Text Auto Tagging For_ with all the asset types that have\n    registered a `TextExtractor`.\n\n    ![Figure 1: The select list in the Google Cloud Natural Language Text Auto Tagging entry is populated programmatically, using the `ConfigurationFieldOptionsProvider`.](../../images/configuration-field-options-provider.png)\n\nThe `ConfigurationFieldOptionsProvider` allows you to populate select lists with\nconfiguration options defined by your custom logic.\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/01-intro.markdown",
    "content": "---\nheader-id: content-publication-management\n---\n\n# Content Publication Management\n\n[TOC levels=1-4]\n\nManaging content publication is primarily controlled by two frameworks:\n\n<!-- - Change Lists -->\n- [Export/Import](#exportimport)\n- [Staging](#staging)\n\nThese features give you the power to plan page publication and manage content.\nYou can leverage the APIs offered by these frameworks to manage your app's\npublication process.\n\nThe Export/Import and Staging frameworks are closely tied together. They both\nimplement the same interfaces and share the same extension points. By\nimplementing one of these frameworks in your app, you automatically leverage the\nother. There are a few simple configurations that can be set to customize them\nseparately. You'll learn about this later.\n\nExport/Import can be viewed as the base feature with Staging built on top of it\n(although they're implemented together). You can visit the\n[Export/Import](/docs/7-2/frameworks/-/knowledge_base/f/export-import)\nframework's articles for the base APIs that both it and the Staging frameworks\nshare. You must implement these to implement Staging. Reference the\n[Staging](/docs/7-2/frameworks/-/knowledge_base/f/staging) framework's\narticles for additional configuration pertaining only to it.\n\nHere are a few of the things you can do with the Export/Import and Staging APIs. \n\n## Export/Import\n\nThe Export/Import feature adds another dimension to your application by\nproviding a framework for producing reusable content and importing content from\nother places. By creating [LAR files (Liferay ARchive)](/docs/7-2/reference/-/knowledge_base/r/liferay-archive-lar-file), \nyou can export your data, import it to another system, or even use this feature\nto back up your content.\n\n![Figure 1: Leveraging the Export/Import feature in your app is useful for sharing content.](../../images/export-import-preview.png)\n\nExport/Import is a default feature for many of @product@'s out-of-the-box apps.\nIt offers an intuitive GUI for managing export/import processes and tracking the\nhistory of previous export/imports. You can also easily pick subsets of data to\nexport based on content type, date range, and configurations. Importing content\nis done using a modern drag-and-drop interface or by selecting the LAR file from\nyour file system.\n\n## Staging\n\nStaging gives you a test environment where you can modify your site and\ntest different configurations without changing your live site. When you implement\nStaging in your app, its content can be tracked by the staged environment,\nallowing users to stage your app's data before releasing it to the world.\n\nHere's an example of some functionality you can add to your app with Staging's\nAPIs:\n\n- Local Staging environment tracking\n- Remote Staging environment tracking\n- Single asset publishing\n- Content tracking on page variations\n\nContinue on to learn more about the frameworks that bring content publication\nmanagement to life!\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/02-export-import/01-intro.markdown",
    "content": "---\nheader-id: export-import\n---\n\n# Export/Import\n\n[TOC levels=1-4]\n\nThe Export/Import feature exports content from the portal and imports external\ncontent into the portal. Your application is much more site\nadministrator-friendly if users can export/import your application's assets. For\nexample, if you want to export your application's assets to use on another\ninstallation or you must clear its data but save a copy, you can implement\nthe export feature. Implementing the import feature lets you bring your\nassets/data back into your application.\n\nHere's what you'll learn to do with the Export/Import framework: \n\n- Create Staged Models\n- Develop Portlet Data Handlers\n- Develop Staged Model Data Handlers\n- Provide entity-specific local services for Export/Import framework\n- Listen to export/import events\n- Initiate new export/import processes programmatically\n\n## Staged Models\n\nTo track an entity of an application with the Export/Import framework, you must\nimplement the\n[`StagedModel`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/StagedModel.html)\ninterface in the app's model classes. It provides the behavior contract for\nentities during the Export/Import and Staging processes. There are two ways to\ncreate staged models for your application's entities:\n\n- [Generate them using Service Builder](/docs/7-2/frameworks/-/knowledge_base/f/generating-staged-models-using-service-builder)\n- [Implement the appropriate interfaces manually](/docs/7-2/frameworks/-/knowledge_base/f/creating-staged-models-manually)\n\nUsing Service Builder to generate your staged models is the easiest way to\ncreate staged models for your app. You define the necessary columns in your\n`service.xml` file and set the `uuid` attribute to `true`. Then you run\nService Builder, which generates the required code for your new staged models.\n\nImplementing the necessary staged model logic *manually* should be done if you\n**don't** want to extend your model with special attributes only required to\ngenerate Staging logic (i.e., not needed by your business logic). In this case,\nyou should adapt your business logic to meet the Staging framework's needs.\n\nSee the\n[Developing Staged Models](/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models)\nsection for more information on the Staged Model architecture.\n\n## Data Handlers\n\nYou must implement Data Handlers to use the Export/Import framework to process\n[LAR files](/docs/7-2/reference/-/knowledge_base/r/liferay-archive-lar-file)\nin your application. There are two types of data handlers:\n\n- Portlet Data Handlers\n- Staged Model Data Handlers\n\nA Portlet Data Handler imports/exports portlet specific data to a LAR file.\nThese classes query and coordinate between staged model data handlers. They also\nconfigure the Export/Import and Staging UI options.\n\nA Staged Model Data Handler supplies information about a staged model (entity)\nto the Export/Import framework, defining a display name for the UI, deleting an\nentity, etc. It also exports referenced content.\n\nVisit the\n[Developing Data Handlers](/docs/7-2/frameworks/-/knowledge_base/f/developing-data-handlers)\nsection for more information.\n\n## Provide Entity Specific Local Services\n\nWhen creating data handlers, you must leverage your app's local services to\nperform Export/Import and Staging related tasks for its entities. When these\nframeworks operate on entities (i.e., staged models), it often cannot manage\nimportant information from the entity's local services alone; instead, you're\nforced to reinvent basic functionality so the framework can access it. This is\ncaused by services not sharing a common ancestor (i.e., interface or base\nclass).\n\nThe *Staged Model Repository* framework removes this barrier by linking an app's\nstaged model to a local service.\n\n![Figure 1: Staged Model Repositories provide a Staging-specific layer of functionality for your local services.](../../../images/staged-model-repository.png)\n\nThis lets the Staging framework call a staged model repository independently\nbased on the entity being processed. This gives you access to entity-specific\nmethods tailored specifically for the staged model data you're handling.\n\n## Export/Import Event Listeners\n\nThe `ExportImportLifecycleListener` framework is for \nlistening for certain staging or export/import events (like export successes and\nimport failures) during the publication process so you can take some action. You\ncan also listen for processes comprised of many events and take action when\nthese processes are initiated. For example, you can listen for when\n\n- Staging has started\n- A portlet export has failed\n- An entity export has succeeded\n\nAfter an event is triggered, you can take an action like these: \n\n- Print information about the event to your console \n- When an import process has completed, clear the cache.\n\nFor a complete list of events you can listen for, see\n[`ExportImportLifecycleConstants`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lifecycle/ExportImportLifecycleConstants.html).\n\nYou must extend one of the two Base listener classes:\n\n- [`BaseExportImportLifecycleListener`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lifecycle/BaseExportImportLifecycleListener.html):\n  listens for specific *events* during a lifecycle. For example, if a layout\n  export fails, you might take some action. \n\n- [`BaseProcessExportImportLifecycleListener`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lifecycle/BaseProcessExportImportLifecycleListener.html):\n  listens for *processes* during a lifecycle. A process usually consists of many\n  individual events. For example, if a site publication fails, you might take\n  some action. Methods provided by this base class are only run once when the\n  desired process action occurs.\n\nWhat's the difference between events and processes? \n\n**Events:** particular actions that occur during processing (example event\nlistener:\n[`CacheExportImportLifecycleListener`](@app-ref@/web-experience/latest/javadocs/com/liferay/exportimport/lifecycle/CacheExportImportLifecycleListener.html)).\n\n**Processes:** longer running groups of events (example process listener:\n[`ExportImportProcessCallbackLifecycleListener`](@app-ref@/web-experience/latest/javadocs/com/liferay/exportimport/lifecycle/ExportImportProcessCallbackLifecycleListener.html)).\n\nUse the listener type that is most appropriate for your use case.\n\n## Export/Import Processes\n\nYou can start the process programmatically instead of through the UI. This lets\nyou provide new interfaces or mimic the functionality of these features in your\nown application.\n\nTo initiate an export/import or staging process, you must pass in an\n[`ExportImportConfiguration`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/model/ExportImportConfiguration.html)\nobject. This object encapsulates many parameters and settings that are required\nwhile the export/import is running. Having one single object with all your\nnecessary data makes executing these frameworks quick and easy.\n\nFor example, when implementing export, you must call services offered by the\n[`ExportImportService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/service/ExportImportService.html)\ninterface. All the methods in this interface require an\n`ExportImportConfiguration` object. You can generate these\nconfiguration objects, so you can easily pass them in your service methods.\n\nThere are three factory classes that are useful to create an\n`ExportImportConfiguration` object:\n\n- [`ExportImportConfigurationSettingsMapFactory`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/configuration/ExportImportConfigurationSettingsMapFactory.html):\n  provides many `build` methods to create settings maps for various scenarios\n  like importing, exporting, and publishing layouts and portlets. For example,\n  you can reference\n  [`UserGroupLocalServiceImpl.exportLayouts(...)`](@platform-ref@/7.2-latest/javadocs/portal-impl/com/liferay/portal/service/impl/UserGroupLocalServiceImpl.html#exportLayouts-long-java.util.Map-)\n  and\n  [`GroupLocalServiceImpl.addDefaultGuestPublicLayoutsByLAR(...)`](@platform-ref@/7.2-latest/javadocs/portal-impl/com/liferay/portal/service/impl/GroupLocalServiceImpl.html#addDefaultGuestPublicLayoutsByLAR-com.liferay.portal.kernel.model.Group-java.io.File-).\n- [`ExportImportConfigurationFactory`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/configuration/ExportImportConfigurationFactory.html):\n  This factory builds `ExportImportConfiguration` objects used for default\n  local/remote publishing.\n- [`ExportImportConfigurationParameterMapFactory`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/configuration/ExportImportConfigurationParameterMapFactory.html):\n  This factory builds parameter maps, which are required during export/import\n  and publishing.\n\nThere are two important service interfaces that primarily use\n`ExportImportConfiguration` objects for exporting, importing, and staging:\n[`ExportImportLocalService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/service/ExportImportLocalService.html)\nand\n[`StagingLocalService`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/service/StagingLocalService.html).\n\n| **Note:** If you're not calling the export/import or staging service methods\n| from an OSGi module, you should not use the interface. The Liferay\n| OSGi container automatically handles interface referencing, which is why\n| using the interface is permitted for modules. If you're calling\n| export/import or staging service methods outside of a module, you should use\n| their service Util classes (e.g., `ExportImportLocalServiceUtil`).\n\nIt's also important to know that `ExportImportConfiguration` is a @product@\nentity, similar to `User` or `Group`. This means that the\n`ExportImportConfiguration` framework offers local and remote services, models,\npersistence classes, and more.\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/02-export-import/02-developing-staged-models/01-intro.markdown",
    "content": "---\nheader-id: developing-staged-models\n---\n\n# Developing Staged Models\n\n[TOC levels=1-4]\n\nTo track an entity of an application with the Export/Import framework, you must\nimplement the\n[`StagedModel`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/StagedModel.html)\ninterface in the app's model classes. It provides the behavior contract for \nentities during the Staging process. For example, the Bookmarks application\nmanages\n[`BookmarksEntry`](@app-ref@/collaboration/latest/javadocs/com/liferay/bookmarks/model/BookmarksEntry.html)s\nand\n[`BookmarksFolder`](@app-ref@/collaboration/latest/javadocs/com/liferay/bookmarks/model/BookmarksFolder.html)s,\nand both implement the `StagedModel` interface. Once you've configured your\nstaged models, you can create staged model data handlers, which supply\ninformation about a staged model (entity) and its referenced content to the\nExport/Import and Staging frameworks. See the\n[Developing Data Handlers](/docs/7-2/frameworks/-/knowledge_base/f/developing-data-handlers)\nsection for more information.\n\nThere are two ways to create staged models for your application's entities:\n\n- [Using Service Builder to generate the required Staging implementations](/docs/7-2/frameworks/-/knowledge_base/f/generating-staged-models-using-service-builder)\n- [Implementing the required Staging interfaces manually](/docs/7-2/frameworks/-/knowledge_base/f/creating-staged-models-manually)\n\nYou can follow step-by-step procedures for creating staged models for your\nentities by visiting their respective articles.\n\nContinue on to learn more about Staged Models!\n\n## Staged Model Interfaces\n\nThe\n[`StagedModel`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/StagedModel.html)\ninterface must be implemented by your app's model classes, but this is typically\ndone through inheritance by implementing one of the interfaces that extend the\nbase interface:\n\n- [`StagedAuditedModel`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/StagedAuditedModel.html)\n- [`StagedGroupedModel`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/StagedGroupedModel.html)\n\nYou must implement these when you want to use certain features of the Staging\nframework like automatic group mapping or entity level *Last Publish Date*\nhandling. So how do you choose which is right for you?\n\nThe `StagedAuditedModel` interface provides all the audit fields to the model\nthat implements it. You can check the\n[`AuditedModel`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/AuditedModel.html)\ninterface for the specific audit fields provided. The `StagedAuditedModel`\ninterface is for models that function independently from the group concept\n(sometimes referred to as company models). If your model is a group model, you\nshould not implement the `StagedAuditedModel` interface.\n\nThe `StagedGroupedModel` interface must be implemented for group models. For\nexample, if your application requires the `groupId` column, your model is a\ngroup model. If your model satisfies both the `StagedGroupModel` and\n`StagedAuditedModel` requirements, it should implement `StagedGroupedModel`.\nYour model should only implement the `StagedAuditedModel` if it doesn't fulfill\nthe grouped model needs, but does fulfill the audited model needs. If your model\ndoes not fulfill either the `StagedAuditedModel` or `StagedGroupedModel`\nrequirements, you should implement the base `StagedModel` interface.\n\nAs an example for extending your model class, you can visit the\n[`BookmarksEntryModel`](@app-ref@/collaboration/latest/javadocs/com/liferay/bookmarks/model/BookmarksEntryModel.html)\nclass, which extends the `StagedGroupedModel` interface; this is done because\nbookmark entries are group models.\n\n```java\npublic interface BookmarksEntryModel extends BaseModel<BookmarksEntry>,\n    ShardedModel, StagedGroupedModel, TrashedModel, WorkflowedModel {\n```\n\nThose are the differences between staged model interfaces. \n\n## Staged Model Attributes\n\nOne of the most important attributes used by the Staging framework is the UUID\n(Universally Unique Identifier). This attribute must be set to `true` in your\n`service.xml` file for Service Builder to recognize your model as an eligible\nstaged model. The UUID differentiates entities between environments. Because the\nUUID always remains the same, it's unique across multiple systems. Why is this\nso important?\n\nSuppose you're using\n[remote staging](/docs/7-2/user/-/knowledge_base/u/enabling-remote-live-staging)\nand you create a new entity on your local staging site and publish it to your\nremote live site. When you go back to modify the entity on your\nlocal site and want to publish those changes, the UUID shows that the local and\nremote entities are the same. The Staging framework can thus recognize the\noriginal entity on the remote site and update it. The UUID provides that. \n\nIn addition to the UUID, there are several columns that must be defined in your\n`service.xml` file for Service Builder to define your model as a staged model:\n\n- `companyId`\n- `createDate`\n- `modifiedDate`\n\nIf you want a staged grouped model, also include the `groupId` and\n`lastPublishDate` columns. If you want a staged audited model, include the\n`userId` and `userName` columns.\n\n## Adapting Your Business Logic to Build Staged Models\n\nWhat if you don't want to extend your model with special attributes your\nbusiness logic doesn't need (removing the ability to leverage Service Builder's\nauto-generation of staged models)? In this case, you should adapt your\nbusiness logic to meet the Staging framework's needs. Liferay provides the\n[`ModelAdapterBuilder`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/adapter/builder/ModelAdapterBuilder.html)\nframework, which lets you adapt your model classes to staged models.\n\nAs an example, assume you have a completed app and you want it to work with\nStaging. Your app, however, does not require a UUID for any of its entities, and\ntherefore, does not provide them. Instead of configuring your app to handle\nUUIDs just for the sake of generating staged models, you can leverage the Model\nAdapter Builder to build your staged models.\n\nAnother example for building staged models from scratch is for applications that\nuse REST services instead of the database to access their attributes. Since this\nkind of app pulls its attributes from a remote system, it is more convenient to\nbuild your staged models yourself instead of relying on Service Builder, which\nis database driven.\n\nTo adapt your model classes to staged models, follow the steps outlined below:\n\n1.  Create a `Staged[Entity]` interface that extends the model-specific\n    interface (e.g., `[Entity]`) and the appropriate staged model interface\n    (e.g., `StagedModel`). This class serves as the Staged Model Adapter.\n\n2.  Create a `Staged[Entity]Impl` class that implements the `Staged[Entity]`\n    interface and provides necessary logic for your entity model to be\n    recognized as a staged model.\n\n3.  Create a `Staged[Entity]ModelAdapterBuilder` class that implements\n    `ModelAdapterBuilder<[Entity], Staged[Entity]>`. This class adapts the\n    original model to the newly created Staged Model Adapter.\n\n4.  Adapt your existing model and call one of the provided APIs to export or\n    import the entity automatically.\n\n![Figure 1: The Staged Model Adapter class extends your entity and staged model interfaces.](../../../../images/staged-model-adapter-diagram.png)\n\n![Figure 2: The Model Adapter Builder gets an instance of the model and outputs a staged model.](../../../../images/model-adapter-builder-diagram.png)\n\nTo step through the process for leveraging the Model Adapter Builder for an\nexisting app, see \n[Creating Staged Models Manually](/docs/7-2/frameworks/-/knowledge_base/f/creating-staged-models-manually).\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/02-export-import/02-developing-staged-models/02-generating-staged-models-using-service-builder.markdown",
    "content": "---\nheader-id: generating-staged-models-using-service-builder\n---\n\n# Generating Staged Models Using Service Builder\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n    <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/web/guest/w/dxp/building-applications/data-frameworks/service-builder/service-builder-basics/generating-model-persistence-and-service-code\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nInstead of having to create staged models for your app manually, you can\nleverage\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder) to\ngenerate the necessary staged model logic for you. If your app doesn't\nuse Liferay's Service Builder, you must configure it in your project. This\n<!--\nAdd link back for 'configure it in your project' once\ndefining-an-object-relational-map-with-service-builder article is available\n-->\ntutorial assumes you have a Service Builder project with `*api` and `*service`\nmodules. If you want to follow along with this tutorial, download the\nstaged-model-example\nService Builder project (Gradle-based).\n\n<!-- TODO: Update the example above when we can upload projects to new site.-->\n\nYou'll track the Service Builder-generated changes applied to an entity model\nfile to observe how staged models are assigned to your entity. Keep in mind the\nspecific\n[staged attributes](/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models#staged-model-attributes)\nnecessary for each staged model. Depending on the attributes defined in your\n`service.xml` file, Service Builder assigns your entity model to a specific\nstaged model type.\n\n1.  Navigate to your project's `*service` module at the command line. Run\n    Service Builder (e.g., `gradlew buildService`) to generate your project's\n    models based on the current `service.xml` configuration.\n\n2.  Open your project's `[Entity]Model.java` interface and observe the inherited\n    interfaces.\n\n    ```java\n    public interface FooModel extends BaseModel<Foo>, ShardedModel, StagedModel {\n    ```\n\n    Your model was generated as a staged model! This is because the\n    `service.xml` file sets the UUID to `true` and defines the `companyId`,\n    `createDate`, and `modifiedDate` columns. There is much more logic generated\n    for your app behind the scenes, but this shows that Service Builder deemed\n    your entity eligible for the Staging and Export/Import frameworks.\n\n3.  Add the `userId` and `userName` columns to your `service.xml` file:\n\n    ```xml\n    <column name=\"userId\" type=\"long\" />\n    <column name=\"userName\" type=\"String\" />\n    ```\n\n4.  Rerun Service Builder and observe your `[Entity]Model.java` interface again:\n\n    ```java\n    public interface FooModel extends BaseModel<Foo>, GroupedModel, ShardedModel,\n        StagedAuditedModel {\n    ```\n\n    Your model is now a staged audited model!\n\n5.  Add the `lastPublishDate` column to your `service.xml` file:\n\n    ```xml\n    <column name=\"lastPublishDate\" type=\"Date\" />\n    ```\n\n6.  Rerun Service Builder and observe your `[Entity]Model.java` interface again:\n\n    ```java\n    public interface FooModel extends BaseModel<Foo>, ShardedModel,\n        StagedGroupedModel {\n    ```\n\n    Your model is now a staged grouped model! The `groupId` column is also\n    required to extend the `StagedGroupedModel` interface, but it was already\n    defined in the original `service.xml` file.\n\nFantastic! You've witnessed firsthand how easy it is to generate staged models\nusing Service Builder."
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/02-export-import/02-developing-staged-models/03-creating-staged-models-manually.markdown",
    "content": "---\nheader-id: creating-staged-models-manually\n---\n\n# Creating Staged Models Manually\n\n[TOC levels=1-4]\n\nThere are times when using\n[Service Builder to generate your staged models](/docs/7-2/frameworks/-/knowledge_base/f/generating-staged-models-using-service-builder)\nis not practical. In these cases, you should create your staged models manually.\nMake sure to read the\n[Adapting Your Business Logic to Build Staged Models](/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models#adapting-your-business-logic-to-build-staged-models)\nsection to determine if creating staged models manually is beneficial for your\nuse case.\n\nIn this tutorial, you'll explore how the Asset Link framework (a @product@\nframework used for\n[relating assets](/docs/7-2/user/-/knowledge_base/u/defining-content-relationships))\nmanually creates staged models. This framework is separate from Export/Import\nand is referenced solely as an example for how to leverage the\n[ModelAdapterBuilder](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/adapter/builder/ModelAdapterBuilder.html)\nframework, which lets you adapt your model classes to staged models.\n\nFollow the steps below to leverage the Model Adapter Builder in your app.\n\n1.  Create a new interface that extends one of the\n    [staged model interfaces](/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models#staged-model-interfaces)\n    and your model specific interface. For example,\n\n    ```java\n    public interface StagedAssetLink extends AssetLink, StagedModel {\n\n    }\n    ```\n\n    | **Note:** Staged model interfaces typically follow the `Staged[Entity]`\n    | naming convention. The Asset Link framework uses a generic entity called\n    | `AssetLink`.\n\n2.  Define methods required for your model to qualify as a staged model. For\n    asset links, methods for retrieving entry UUIDs (among others) are defined:\n\n    ```java\n    public String getEntry1Uuid();\n\n    public String getEntry2Uuid();\n    ```\n\n    | **Note:** Asset links do not provide UUIDs by default; however, they still\n    | need to be tracked in the Staging and Export/Import frameworks. Therefore,\n    | they require staged models. Since they don't provide a UUID, Service\n    | Builder cannot generate staged models for asset links. The Asset Link\n    | framework has to create staged models differently using the Model Adapter\n    | Builder.\n\n    These will be implemented by a new implementation class later. \n\n2.  Create an implementation class that implements your new `Staged[Entity]`.\n    For example, the Asset Link framework does this:\n\n    ```java\n    public class StagedAssetLinkImpl implements StagedAssetLink {\n\n    }\n    ```\n\n    This class provides necessary logic for your entity model to be recognized\n    as a staged model. Below is a subset of logic in the example\n    `StagedAssetLinkImpl` class used to populate UUIDs for asset link entries:\n\n    ```java\n    public StagedAssetLinkImpl(AssetLink assetLink) {\n         _assetLink = assetLink;\n\n        ...\n\n         populateUuid();\n    }\n\n    @Override\n    public String getEntry1Uuid() {\n        if (Validator.isNotNull(_entry1Uuid)) {\n            return _entry1Uuid;\n        }\n\n        populateEntry1Attributes();\n\n        return _entry1Uuid;\n    }\n\n    @Override\n    public String getEntry2Uuid() {\n        if (Validator.isNotNull(_entry2Uuid)) {\n                return _entry2Uuid;\n        }\n\n        populateEntry2Attributes();\n\n        return _entry2Uuid;\n    }\n\n    protected void populateEntry1Attributes() {\n\n        ...\n\n        AssetEntry entry1 = AssetEntryLocalServiceUtil.fetchAssetEntry(\n            _assetLink.getEntryId1());\n\n        ...\n\n        _entry1Uuid = entry1.getClassUuid();\n    }\n\n    protected void populateEntry2Attributes() {\n\n        ...\n\n        AssetEntry entry2 = AssetEntryLocalServiceUtil.fetchAssetEntry(\n            _assetLink.getEntryId2());\n\n        ...\n\n        _entry2Uuid = entry2.getClassUuid();\n    }\n\n    protected void populateUuid() {\n\n        ...\n\n        String entry1Uuid = getEntry1Uuid();\n        String entry2Uuid = getEntry2Uuid();\n\n        ...\n\n        _uuid = entry1Uuid + StringPool.POUND + entry2Uuid;\n            }\n    }\n\n    private AssetLink _assetLink;\n    private String _entry1Uuid;\n    private String _entry2Uuid;\n    private String _uuid;\n    ```\n\n    This logic retrieves asset link entries and populates UUIDs for them usable\n    by the Staging and Export/Import frameworks. With the newly generated UUIDs,\n    asset link model classes can be converted to staged models.\n\n3.  Create a Model Adapter Builder class and implement the\n    [ModelAdapterBuilder](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/adapter/builder/ModelAdapterBuilder.html)\n    interface. You should define the entity type and your Staged Model Adapter\n    class when implementing the interface:\n\n    ```java\n    public class StagedAssetLinkModelAdapterBuilder\n        implements ModelAdapterBuilder<AssetLink, StagedAssetLink> {\n\n        @Override\n        public StagedAssetLink build(AssetLink assetLink) {\n            return new StagedAssetLinkImpl(assetLink);\n        }\n\n    }\n    ```\n\n    For the `StagedAssetLinkModelAdapterBuilder`, the entity type is `AssetLink`\n    and the Staged Model Adapter is `StagedAssetLink`. Your app should follow a\n    similar design. The Model Adapter Builder outputs a new instance of the\n    `Staged[Entity]Impl` object.\n\n4.  Now you need to adapt your existing business logic to call the provided\n    APIs. You can call the\n    [ModelAdapterUtil](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/adapter/ModelAdapterUtil.html)\n    class to create an instance of your Staged Model Adapter:\n\n    ```java\n    StagedAssetLink stagedAssetLink = ModelAdapterUtil.adapt(\n        assetLink, AssetLink.class, StagedAssetLink.class);\n    ```\n\n    Once you've created\n    [Staged Model Data Handlers](/docs/7-2/frameworks/-/knowledge_base/f/creating-staged-model-data-handlers),\n    you can begin exporting/importing your now Staging-compatible entities:\n\n    ```java\n    StagedModelDataHandlerUtil.exportStagedModel(\n        portletDataContext, stagedAssetLink);\n    ```\n\nAwesome! You've successfully adapted your business logic to build staged models!\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/02-export-import/03-developing-data-handlers/01-intro.markdown",
    "content": "---\nheader-id: developing-data-handlers\n---\n\n# Developing Data Handlers\n\n[TOC levels=1-4]\n\nA common requirement for data driven applications is to import and export\ndata. This *could* be accomplished by accessing your database directly and\nrunning SQL queries to export/import data; however, this has several drawbacks:\n\n- Working with different database vendors might require customized SQL scripts.\n- Access to the database may be tightly controlled, restricting the ability to\n  export/import on demand.\n- You'd have to come up with your own means of storing and parsing the data. \n\nLiferay provides data handlers as a convenient and reliable way to export/import\nyour data (as a LAR file).\n\nThere are two types of data handlers:\n\n- Portlet Data Handlers\n- Staged Model Data Handlers\n\nA Portlet Data Handler imports/exports portlet specific data to a LAR file.\nThese classes only have the role of querying and coordinating between staged\nmodel data handlers. For example, the Bookmarks application's portlet data\nhandler tracks system events dealing with Bookmarks entities. It also configures\nthe Export/Import UI options for the Bookmarks application.\n\nA Staged Model Data Handler supplies information about a staged model (entity)\nto the Export/Import framework, defining a display name for the UI, deleting an\nentity, and exporting referenced content. For example, if a Bookmarks entry\nresides in a Bookmarks folder, the `BookmarksEntry` staged model data handler\ninvokes the export of the `BookmarksFolder`.\n\n![Figure 1: The Data Handler framework uses portlet data handlers and staged model data handlers to track and export/import portlet and staged model information, respectively.](../../../../images/data-handler-diagram.png)\n\nYou're not required to implement a staged model data handler for every entity in\nyour application, but they're necessary for any entity you want to export/import\nor have the staging framework track.\n\n| **Note:** Creating data handlers for your app means it's automatically tracked\n| by the Staging framework. You can further customize how Staging handles your\n| app, but creating staged models and data handlers is what registers your app\n| for Staging.\n\nBefore implementing data handlers, make sure your application is ready for the\nExport/Import and Staging frameworks by creating\n[staged models](/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models).\n\n## Understanding the `PortletDataHandler` Interface\n\nA Portlet Data Handler imports/exports portlet specific data to a LAR file.\nThese classes query and coordinate between\n[staged model data handlers](/docs/7-2/frameworks/-/knowledge_base/f/creating-staged-model-data-handlers).\n\nTo create a portlet data handler for your staged model, you must implement the\n[`PortletDataHandler`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/PortletDataHandler.html)\ninterface by extending the\n[`BasePortletDataHandler`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/BasePortletDataHandler.html)\nclass. Visit the API reference documentation for this interface/class for useful\ninformation on the methods provided.\n\nSome guidelines for implementing the `PortletDataHandler` interface are provided\nbelow:\n\nThe `@Component` annotation section above the implementation class's declaration\nregisters the class as a portlet data handler in the OSGi service registry.\nThere are a few annotation attributes you should set:\n\n`immediate`: activates the component immediately once its provided module has\nstarted.\n\n`property`: sets various properties for the component service. You must\nassociate the portlet you wish to handle with this service so they\nfunction properly in the export/import environment. You should have one\nportlet data handler for each portlet (e.g., Bookmarks and Bookmarks\nAdmin).\n\n`service`: points to the `PortletDataHandler.class` interface.\n\nThe `activate` method sets what the portlet data handler controls. It also\nconfigures the portlet's Export/Import and Staging UI. This method is called\nduring initialization of the component by the\n[`@Activate`](https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Activate.html)\nannotation; it's invoked after dependencies are set and before services are\nregistered. Five callable `set` methods are described below:\n\n`setDataPortletPreferences`: sets portlet preferences your app should\nhandle.\n\n`setDeletionSystemEventStagedModelTypes`: sets the staged model deletions\nthat the portlet data handler should track. For example, the Bookmarks app\ntracks Bookmark entries and folders.\n\n`setPublishToLiveByDefault`: controls whether your app is selected to\npublish on the Publication screen by default.\n\n`setExportControls`: adds fine grained controls over export/import\nbehavior rendered in the Export/Import UI. This also sets the\n`setImportControls` method. For example, the Bookmarks app adds a\ncheckbox to select Bookmarks content (entries) to export.\n\n`setStagingControls`: adds fine-grained controls over staging behavior\nrendered in the Staging UI. For example, this enables your app's checkboxes in\nthe Content section displayed during publication.\n\nThe `doExportData` method checks if anything should be exported. For example,\nthe Bookmarks app uses this method to check if the user selected Bookmarks\nentries for export by leveraging the `portletDataContext`. Later, the\n`ExportImportActionableDynamicQuery` framework runs a query against\nbookmarks folders and entries to find ones which should be exported to the\nLAR file.\n\nThe `-ActionableDynamicQuery` classes are generated automatically by Service\nBuilder and are available in an app's local services. It queries the database\nsearching for certain Export/Import-specific parameters (e.g., `createDate`\nand `modifiedDate`), and based on those parameters, finds a list of exportable\nrecords from the staged model data handler.\n\nThe `doImportData` method queries for entity data in the imported LAR file\nthat should be added to the database. This is done by extracting XML elements\nfrom the LAR file by using utility methods in the\n[`StagedModelDataHandlerUtil`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/StagedModelDataHandlerUtil.html)\nclass. The extracted elements tell @product@ what data to import from the LAR\nfile.\n\nThe `doPrepareManifestSummary` method calculates the number of affected\nentities based on the current export or staging process.\n\nYou must retrieve and manage the schema version. This is done with the\n`getSchemaVersion` and `validateSchemaVersion` methods. The schema version is\nused to perform component related validation before importing data. It's added\nto the\n[LAR file](/docs/7-2/reference/-/knowledge_base/r/liferay-archive-lar-file) for\neach application being processed. During import, the environment's schema\nversion is compared to the LAR file's schema version. Validating the schema\nversion avoids broken data when importing. See the\n[`PortletDataHandler.getSchemaVersion()`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/PortletDataHandler.html#getSchemaVersion--)\nmethod's Javadoc for more information.\n\nNext you'll learn about the `StagedModelDataHandler` interface.\n\n## Understanding the `StagedModelDataHandler` Interface\n\nA Staged Model Data Handler supplies information about a staged model (entity)\nto the Export/Import framework, defines a display name for the UI, deletes\nentities, etc. It's also responsible for exporting referenced content. For\nexample, if a Bookmarks entry resides in a Bookmarks folder, the\n`BookmarksEntry` staged model data handler invokes the export of the\n`BookmarksFolder`.\n\nTo create a staged model data handler for your staged model, you must implement the\n[`StagedModelDataHandler`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/StagedModelDataHandler.html)\ninterface. This is typically done by extending the\n[`BaseStagedModelDataHandler`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/BaseStagedModelDataHandler.html)\nclass. Visit the API reference documentation for this interface/class for useful\ninformation on the methods provided.\n\nAdditional implementation details for the `StagedModelDataHandler` interface is\nprovided below:\n\nThe `@Component` annotation section above the implementation class's\ndeclaration registers the class as a staged model data handler in the OSGi\nservice registry. There are two annotation attributes you should set:\n\n`immediate`: activates the component immediately once its provided module has\nstarted.\n\n`service`: points to the `StagedModelDataHandler.class` interface.\n\nThe `getClassNames` method provides the class names of the models the data\nhandler tracks. As a best practice, you should have one staged model data\nhandler per staged model. It's possible to use multiple class types, but this\nis not recommended.\n\nThe `getDisplayName` method retrieves the staged model's display name. This is\nused in the Export/Import UI.\n\nThe `doExportStagedModel` method retrieves your app entity's data element\nfrom the\n[`PortletDataContext`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/PortletDataContext.html)\nand then adds the class model characterized by that data element to the\n`PortletDataContext`. The `PortletDataContext` data populates the LAR file with\nyour application's data during the export process.\n\n| **Note:** A staged model data handler should ensure everything required\n| for its operation is also exported. For example, in the Bookmarks\n| application, an entry requires its folder to keep the folder structure\n| intact. Therefore, the folder should be exported first followed by the\n| entry. Note that once an entity has been exported, subsequent calls to the\n| export method don't repeat the export process multiple times, ensuring\n| optimal performance.\n\nThe `doImportStagedModel` method imports the staged model data. An important\nfeature of the import process is that all exported reference elements are\nautomatically imported when needed. The method must therefore only find the new\nassigned ID for the folder before importing the entry.\n\nThe `PortletDataContext` keeps the data up-to-date during the import process.\nThe old ID and new ID mapping can be reached by using the\n`portletDataContext.getNewPrimaryKeysMap()` method. This method also checks the\nimport mode (e.g., *Copy As New* or *Mirror*) and, depending on the process\nconfiguration and existing environment, adds or updates the entry.\n\nThe `doImportMissingReference` method maps the existing staged model to the\nold ID in the reference element. When a reference is exported as missing, the\nData Handler framework calls this method during the import process and updates\nthe new primary key map in the portlet data context.\n\nWhen importing a LAR (i.e., publishing to the live Site), the import process\nexpects all of an entity's references to be available and validates their\nexistence.\n\nFor example, if you republish an updated bookmarks folder to the live Site\nand did not include some of its existing entries in the publication, these\nentries are considered missing references.\n\nSince you have references from two separate Sites with differing IDs, the\nsystem can't match them during publication. Suppose you export a bookmark\nentry as a missing reference with a primary key (ID) of `1`. When importing\nthat information, the LAR only provides the ID but not the entry itself.\nTherefore, during the import process, the Data Handler framework searches\nfor the entry to replace by its UUID, but the entry to replace has a\ndifferent ID (primary key) of `2`. You must provide a way to handle these\nmissing references.\n\nTo do this, you must add a method that maps the missing reference's primary\nkey from the export to the existing primary key during import. Since the\nreference's UUID is consistent across systems, it's used to complete the\nmapping of differing primary keys. Note that a reference can only be missing\non the live Site if it has already been published previously. Therefore,\nwhen publishing a bookmarks folder for the first time, the system doesn't\ncheck for missing references.\n\nContinue in the section to learn how to develop data handlers for your app.\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/02-export-import/03-developing-data-handlers/02-creating-portlet-data-handlers.markdown",
    "content": "---\nheader-id: creating-portlet-data-handlers\n---\n\n# Creating Portlet Data Handlers\n\n[TOC levels=1-4]\n\nIn this tutorial, you'll create the `BookmarksPortletModelDataHandler` class\nused for the Bookmarks application. The Bookmarks application's portlet data\nhandler tracks system events dealing with Bookmarks entities. It also configures\nthe Export/Import and Staging UI options for the Bookmarks application.\n\n1.  Create a new package in your existing Service Builder project for your data\n    handler classes. For instance, the Bookmarks application's data handler\n    classes reside in the `bookmarks-service` module's\n    `com.liferay.bookmarks.internal.exportimport.data.handler` package.\n\n2.  Create your `-PortletDataHandler` class for your application in the new\n    `-exportimport.data.handler` package and have it implement the\n    [PortletDataHandler](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/PortletDataHandler.html)\n    interface by extending the\n    [BasePortletDataHandler](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/BasePortletDataHandler.html)\n    class. For example,\n\n    ```java\n    public class BookmarksPortletDataHandler extends BasePortletDataHandler {\n    ```\n\n3.  Create an `@Component` annotation section above the class declaration:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"javax.portlet.name=\" + BookmarksPortletKeys.BOOKMARKS\n        },\n        service = PortletDataHandler.class\n    )\n    ```\n\n4.  Set what the portlet data handler controls and the portlet's Export/Import\n    and Staging UIs by adding an `activate` method:\n\n    ```java\n    @Activate\n    protected void activate() {\n        setDataPortletPreferences(\"rootFolderId\");\n        setDeletionSystemEventStagedModelTypes(\n            new StagedModelType(BookmarksEntry.class),\n            new StagedModelType(BookmarksFolder.class));\n        setExportControls(\n            new PortletDataHandlerBoolean(\n                NAMESPACE, \"entries\", true, false, null,\n                BookmarksEntry.class.getName()));\n        setStagingControls(getExportControls());\n    }\n    ```\n\n    ![Figure 1: You can select the content types you'd like to export/import in the UI.](../../../../images/export-import-controls.png)\n\n5.  For the Bookmarks portlet data handler to reference its entry and folder\n    staged models successfully, you must set them in your class:\n\n    ```java\n    @Reference(unbind = \"-\")\n    protected void setBookmarksEntryLocalService(\n        BookmarksEntryLocalService bookmarksEntryLocalService) {\n\n        _bookmarksEntryLocalService = bookmarksEntryLocalService;\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setBookmarksFolderLocalService(\n        BookmarksFolderLocalService bookmarksFolderLocalService) {\n\n        _bookmarksFolderLocalService = bookmarksFolderLocalService;\n    }\n\n    private BookmarksEntryLocalService _bookmarksEntryLocalService;\n    private BookmarksFolderLocalService _bookmarksFolderLocalService;\n    ```\n\n    The `set` methods must be annotated with the\n    [@Reference](https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Reference.html)\n    annotation. \n    \n    <!--Uncomment once article is available\n    Visit the\n    Invoking Local Services\n    article for more information on using the `@Reference` annotation in\n    @product@.\n    -->\n\n    **Important:** @product@'s official Bookmarks app does not use local\n    services in its portlet data handler; instead, it uses the\n    [`StagedModelRepository`](@app-ref@/web-experience/latest/javadocs/com/liferay/exportimport/staged/model/repository/StagedModelRepository.html)\n    framework. This is a new framework, but is a viable option when setting up\n    your portlet data handlers. For more information on this, see the\n    [Providing Entity-Specific Local Services for Staging](/docs/7-2/frameworks/-/knowledge_base/f/providing-entity-specific-local-services-for-export-import)\n    tutorial section. Since local services are more widely used in custom apps,\n    this tutorial covers those instead.\n\n6.  You must create a namespace for your entities so the Export/Import framework\n    can identify your application's entities from other entities in @product@.\n    The Bookmarks application's namespace declaration looks like this:\n\n    ```java\n    public static final String NAMESPACE = \"bookmarks\";\n    ```\n\n    You'll see how this namespace is used later.\n\n7.  Your portlet data handler should retrieve the data related to its staged\n    model entities so it can properly export/import it. Add this functionality\n    by inserting the following methods:\n\n    ```java\n    @Override\n    protected String doExportData(\n            final PortletDataContext portletDataContext, String portletId,\n            PortletPreferences portletPreferences)\n        throws Exception {\n\n        Element rootElement = addExportDataRootElement(portletDataContext);\n\n        if (!portletDataContext.getBooleanParameter(NAMESPACE, \"entries\")) {\n            return getExportDataRootElementString(rootElement);\n        }\n\n        portletDataContext.addPortletPermissions(\n            BookmarksConstants.RESOURCE_NAME);\n\n        rootElement.addAttribute(\n            \"group-id\", String.valueOf(portletDataContext.getScopeGroupId()));\n\n        ExportActionableDynamicQuery folderActionableDynamicQuery =\n            _bookmarksFolderLocalService.\n                getExportActionableDynamicQuery(portletDataContext);\n\n        folderActionableDynamicQuery.performActions();\n\n        ActionableDynamicQuery entryActionableDynamicQuery =\n            _bookmarksEntryLocalService.\n                getExportActionableDynamicQuery(portletDataContext);\n\n        entryActionableDynamicQuery.performActions();\n\n        return getExportDataRootElementString(rootElement);\n    }\n\n    @Override\n    protected PortletPreferences doImportData(\n            PortletDataContext portletDataContext, String portletId,\n            PortletPreferences portletPreferences, String data)\n        throws Exception {\n\n        if (!portletDataContext.getBooleanParameter(NAMESPACE, \"entries\")) {\n            return null;\n        }\n\n        portletDataContext.importPortletPermissions(\n            BookmarksConstants.RESOURCE_NAME);\n\n        Element foldersElement = portletDataContext.getImportDataGroupElement(\n            BookmarksFolder.class);\n\n        List<Element> folderElements = foldersElement.elements();\n\n        for (Element folderElement : folderElements) {\n            StagedModelDataHandlerUtil.importStagedModel(\n                portletDataContext, folderElement);\n        }\n\n        Element entriesElement = portletDataContext.getImportDataGroupElement(\n                BookmarksEntry.class);\n\n        List<Element> entryElements = entriesElement.elements();\n\n        for (Element entryElement : entryElements) {\n            StagedModelDataHandlerUtil.importStagedModel(\n                portletDataContext, entryElement);\n        }\n\n        return null;\n    }\n    ```\n\n8.  Add a method that counts the number of affected entities based on the\n    current export or staging process:\n\n    ```java\n    @Override\n    protected void doPrepareManifestSummary(\n            PortletDataContext portletDataContext,\n            PortletPreferences portletPreferences)\n        throws Exception {\n\n        if (ExportImportDateUtil.isRangeFromLastPublishDate(\n                portletDataContext)) {\n\n            _staging.populateLastPublishDateCounts(\n                portletDataContext,\n                new StagedModelType[] {\n                    new StagedModelType(BookmarksEntry.class.getName()),\n                    new StagedModelType(BookmarksFolder.class.getName())\n                });\n\n            \treturn;\n          \t}\n\n        ActionableDynamicQuery entryExportActionableDynamicQuery =\n            _bookmarksEntryLocalService.\n                getExportActionableDynamicQuery(portletDataContext);\n\n        entryExportActionableDynamicQuery.performCount();\n\n        ActionableDynamicQuery folderExportActionableDynamicQuery =\n            _bookmarksFolderLocalService.\n                getExportActionableDynamicQuery(portletDataContext);\n\n        folderExportActionableDynamicQuery.performCount();\n    }\n    ```\n\n    This number is displayed in the Export and Staging UI. Note that since the\n    Staging framework traverses the entity graph during export, the built-in\n    components provide an approximate value in some cases.\n\n    ![Figure 2: The number of modified Bookmarks entities are displayed in the Export UI.](../../../../images/manifest-summary-count.png)\n\n9.  Set the XML schema version for the XML files included in your exported LAR\n    file:\n\n    ```java\n    public static final String SCHEMA_VERSION = \"1.0.0\";\n\n    @Override\n    public String getSchemaVersion() {\n        return SCHEMA_VERSION;\n    }\n\n    @Override\n    public boolean validateSchemaVersion(String schemaVersion) {\n        return _portletDataHandlerHelper.validateSchemaVersion(\n            schemaVersion, getSchemaVersion());\n    }\n    ```\n\nAwesome! You've set up your portlet data handler and your application can now\nsupport the Export/Import framework and display a UI for it. Be sure to also\nimplement staged model data handlers for your staged models. See the\n[Creating Staged Model Data Handlers](/docs/7-2/frameworks/-/knowledge_base/f/creating-staged-model-data-handlers)\nfor more information.\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/02-export-import/03-developing-data-handlers/03-creating-staged-model-data-handlers.markdown",
    "content": "---\nheader-id: creating-staged-model-data-handlers\n---\n\n# Creating Staged Model Data Handlers\n\n[TOC levels=1-4]\n\nIn this tutorial, you'll create the `BookmarksStagedModelDataHandler` class used\nfor the Bookmarks application. The Bookmarks application has two staged models:\nentries and folders. Creating data handlers for these two entities is similar,\nso you'll examine how this is done for Bookmark entries. This tutorial assumes\nyou've already created\n[staged models](/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models).\n\n1.  Create a new package in your existing Service Builder project for your data\n    handler classes. For instance, the Bookmarks application's data handler\n    classes reside in the `bookmarks-service` module's\n    `com.liferay.bookmarks.internal.exportimport.data.handler` package.\n\n2.  Create a `-StagedModelDataHandler` class in the `-exportimport.data.handler`\n    package. The staged model data handler class should extend the\n    [`BaseStagedModelDataHandler`](@platform-ref@/7.1-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/BaseStagedModelDataHandler.html)\n    class and the entity type should be specified as its parameter. You can see\n    how this was done for the `BookmarksEntryStagedModelDataHandler` class\n    below:\n\n    ```java\n    public class BookmarksEntryStagedModelDataHandler\n        extends BaseStagedModelDataHandler<BookmarksEntry> {\n    ```\n\n3.  Create an `@Component` annotation section above the class declaration.\n\n    ```java\n    @Component(immediate = true, service = StagedModelDataHandler.class)\n    ```\n\n4.  Create a getter and setter method for the local service of the staged model\n    for which you want to provide a data handler:\n\n    ```java\n    @Override\n    protected BookmarksEntryLocalService getBookmarksEntryLocalService() {\n        return _bookmarksEntryLocalService;\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setBookmarksEntryLocalService(\n        BookmarksEntryLocalService bookmarksEntryLocalService) {\n\n        _bookmarksEntryLocalService = bookmarksEntryLocalService;\n    }\n\n    private BookmarksEntryLocalService _bookmarksEntryLocalService;\n    ```\n\n    These methods are used to link this data handler with the staged model for\n    bookmark entries.\n\n    **Important:** @product@'s official Bookmarks app does not use local\n    services in its staged model data handlers; instead, it uses the\n    [`StagedModelRepository`](@app-ref@/web-experience/latest/javadocs/com/liferay/exportimport/staged/model/repository/StagedModelRepository.html)\n    framework. This is a new framework, but is a viable option when setting up\n    your staged model data handlers. For more information on this, see the\n    [Providing Entity-Specific Local Services for Staging](/docs/7-2/frameworks/-/knowledge_base/f/providing-entity-specific-local-services-for-export-import)\n    tutorial section. Since local services are more widely used in custom apps,\n    this tutorial covers those instead.\n\n5.  Provide the class names of the models the data handler tracks. You can do\n    this by overriding the\n    [`StagedModelDataHandler`](@platform-ref@/7.1-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/StagedModelDataHandler.html)'s\n    `getClassnames()` method:\n\n    ```java\n    public static final String[] CLASS_NAMES = {BookmarksEntry.class.getName()};\n\n    @Override\n    public String[] getClassNames() {\n        return CLASS_NAMES;\n    }\n    ```\n\n6.  Add a method that retrieves the staged model's display name:\n\n    ```java\n    @Override\n    public String getDisplayName(BookmarksEntry entry) {\n        return entry.getName();\n    }\n    ```\n\n    The display name is presented with the progress bar during the export/import\n    process.\n\n    ![Figure 1: Your staged model data handler provides the display name in the Export/Import UI.](../../../../images/staged-model-display-name.png)\n\n7.  Add methods that import and export your staged model and its references.\n\n    ```java\n    @Override\n    protected void doExportStagedModel(\n            PortletDataContext portletDataContext, BookmarksEntry entry)\n        throws Exception {\n\n        if (entry.getFolderId() !=\n                BookmarksFolderConstants.DEFAULT_PARENT_FOLDER_ID) {\n\n            StagedModelDataHandlerUtil.exportReferenceStagedModel(\n                portletDataContext, entry, entry.getFolder(),\n                PortletDataContext.REFERENCE_TYPE_PARENT);\n        }\n\n        Element entryElement = portletDataContext.getExportDataElement(entry);\n\n        portletDataContext.addClassedModel(\n            entryElement, ExportImportPathUtil.getModelPath(entry), entry);\n    }\n\n    @Override\n    protected void doImportStagedModel(\n            PortletDataContext portletDataContext, BookmarksEntry entry)\n        throws Exception {\n\n        Map<Long, Long> folderIds =\n            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(\n                BookmarksFolder.class);\n\n        long folderId = MapUtil.getLong(\n            folderIds, entry.getFolderId(), entry.getFolderId());\n\n        ServiceContext serviceContext =\n            portletDataContext.createServiceContext(entry);\n\n        BookmarksEntry importedEntry = null;\n\n        if (portletDataContext.isDataStrategyMirror()) {\n\n            BookmarksEntry existingEntry =\n                _bookmarksEntryLocalService. fetchBookmarksEntryByUuidAndGroupId(\n                    entry.getUuid(), portletDataContext.getScopeGroupId());\n\n            if (existingEntry == null) {\n\n                serviceContext.setUuid(entry.getUuid());\n                importedEntry = _bookmarksEntryLocalService.addEntry(\t\t\t\t\t\n                  userId, portletDataContext.getScopeGroupId(), folderId, entry.getName(), entry.getUrl(), entry.getDescription(), serviceContext);\n            }\n            else {\n                importedEntry = _bookmarksEntryLocalService.updateEntry(\n                    userId, existingEntry.getEntryId(), portletDataContext.getScopeGroupId(), folderId, entry.getName(), entry.getUrl(), entry.getDescription(),\tserviceContext);\n            }\n        }\n        else {\n            importedEntry = _bookmarksEntryLocalService.addEntry(userId, portletDataContext.getScopeGroupId(), folderId,entry.getName(), entry.getUrl(), entry.getDescription(),\tserviceContext);\n        }\n\n        portletDataContext.importClassedModel(entry, importedEntry);\n    }\n    ```\n\n8.  Add the `doImportMissingReference` method to your class:\n\n    ```java\n    @Override\n    protected void doImportMissingReference(\n            PortletDataContext portletDataContext, String uuid, long groupId,\n            long entryId)\n        throws Exception {\n\n        BookmarksEntry existingEntry = fetchMissingReference(uuid, groupId);\n\n        if (existingEntry == null) {\n            return;\n        }\n\n        Map<Long, Long> entryIds =\n            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(\n                BookmarksEntry.class);\n\n        entryIds.put(entryId, existingEntry.getEntryId());\n    }\n    ```\n\nFantastic! You've created a data handler for your staged model. The\nExport/Import framework can now track your entity's behavior and data. Be sure\nto also implement a portlet data handler to manage portlet specific data. See\nthe\n[Creating Portlet Data Handlers](/docs/7-2/frameworks/-/knowledge_base/f/creating-portlet-data-handlers)\narticle to do this for the Bookmarks app.\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/02-export-import/04-providing-entity-specific-local-services-for-export-import/01-intro.markdown",
    "content": "---\nheader-id: providing-entity-specific-local-services-for-export-import\n---\n\n# Providing Entity-Specific Local Services for Export/Import\n\n[TOC levels=1-4]\n\nData handlers must often call your app's local services to perform\nExport/Import-related tasks for its entities. When the Export/Import framework\noperates on entities (i.e., staged models), it often cannot manage important\ninformation from the entity's local services alone. The *Staged Model\nRepository* framework removes this barrier by linking an app's staged model to a\nlocal service. This gives you access to entity-specific methods tailored\nspecifically for the staged model data you're handling.\n\nWhat kind of *entity-specific* methods are we talking about here? Your data\nhandlers only expose a specific set of actions, like export and import methods.\nThe Staged Model Repository framework provides CRUD operations for a specific\nstaged model that local services don't expose.\n\nThe staged model repository does not avoid using your app's local services. It\nonly provides an additional layer that provides Export/Import-specific\nfunctionality. Here's how this works: \n\n- `*StagedModelDataHandler`: de-serializes the provided\n  [LAR file's](/docs/7-2/reference/-/knowledge_base/r/liferay-archive-lar-file)\n  XML into a model.\n- `*StagedModelRepository`: updates the model based on the environment and\n  business logic, providing entity-specific CRUD operations for Staging purposes\n  (e.g., UUID manipulation).\n- Local services are called from the `*StagedModelRepository` and handle the\n  remainder of the process.\n\nPretty cool, right? The first thing you must do is implement the\n`StagedModelRepository` interface. You'll explore this next.\n\n## Understanding the `StagedModelRepository` Interface\n\nProviding specialized local services for your app's staging functionality lets\nyou abstract the additional staging-specific information away from your data\nhandlers. Before you can begin using the Staged Model Repository framework in\nyour app, you must implement it.\n\nThe first important task is adding an `@Component` annotation section above the\nimplementation class's declaration. This registers the class as a staged model\nrepository in the OSGi service registry. There are a few annotation attributes\nyou should set:\n\n- `immediate`: activates the component immediately once its provided module has\n  started.\n- `property`: sets various properties for the component service. You must\n  associate the model class you wish to handle with this service so it's\n  recognized by the data handlers leveraging it. This property must set the\n  fully qualified model class name like this: `property =\n  \"model.class.name=FULLY_QUALIFIED_MODEL_CLASS\"`.\n- `service`: points to the `StagedModelRepository.class` interface.\n\nNext, you must implement the `StagedModelRepository` interface. Implementations\nvary greatly based on the app you're implementing it for. There are two common\ncases you'll experience when implementing its methods:\n\n- Methods that require additional Export/Import information injected before\n  calling the local service.\n- Methods that can call the local service directly.\n\nThe\n[`BookmarksEntryStagedModelRepository.addStagedModel(...)`](https://github.com/liferay/liferay-portal/blob/7.2.0-a1/modules/apps/bookmarks/bookmarks-service/src/main/java/com/liferay/bookmarks/internal/exportimport/staged/model/repository/BookmarksEntryStagedModelRepository.java#L51-L71)\nmethod is an example where only calling the local service would not satisfy the\nstaged model data handler's needs (i.e., its UUID requirement). With the staged\nmodel repository layer, however, you can add export/import specific requirements\non top of the present local services to serve your data handlers' needs.\n\nThe method does this: \n\n- Sets the user ID and service context based on the\n  [`PortletDataContext`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/PortletDataContext.html)\n  (used to populate the LAR file with your application's data during the\n  export process).\n- Sets the UUID, which is required to differentiate staged content between\n  Sites.\n- Calls the entity's local service.\n\nNot every method implementation requires additional export/import information.\nFor example, deleting Bookmarks Entries and deleting Bookmarks Entry staged\nmodels are functionally the same, so your staged model repository's method would\ncall the local service directly (e.g.,\n[`BookmarksEntryStagedModelRepository.deleteStagedModel(...)`](https://github.com/liferay/liferay-portal/blob/7.2.0-a1/modules/apps/bookmarks/bookmarks-service/src/main/java/com/liferay/bookmarks/internal/exportimport/staged/model/repository/BookmarksEntryStagedModelRepository.java#L73-L78)).\n\nNext you'll learn about using a Staged Model Repository.\n\n## Using a Staged Model Repository\n\nYou can leverage a staged model repository by\n\n1.  Creating a getter and setter method to make a `StagedModelRepository` object\n    available to your entity.\n\n2.  Calling the `StagedModelRepository` object to leverage its specialized\n    export/import logic.\n\nThe getter and setter methods instantiate a `StagedModelRepository` object that\nthe staged model data handler can use to access your entity's CRUD operations.\nThe setter method should have an `@Reference` annotation listed above its method\nsignature. This injects the component service of the `*StagedModelRepository`\ninto the staged model repository object. The component service was created when\nyou set the `@Component` annotation in the implementation class.\n\nOnce you have access to the `StagedModelRepository` object, call it to use its\nspecialized export/import logic. Now that you have access to CRUD operations via\nthe `StagedModelRepository` object, you can skip the headache of providing\na slew of parameters and additional functionality in the local service to do\nsimple things like add a Bookmarks entry. The staged model repository abstracts\nthese requirements away from the data handler.\n\nContinue in the section to learn how to develop staged model repositories for\nyour app.\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/02-export-import/04-providing-entity-specific-local-services-for-export-import/02-implementing-the-staged-model-repository-framework.markdown",
    "content": "---\nheader-id: implementing-the-staged-model-repository-framework\n---\n\n# Implementing the Staged Model Repository Framework\n\n[TOC levels=1-4]\n\nIn this article, you'll step through a quick example that demonstrates\nimplementing the `StagedModelRepository` interface to use for a staged model.\nThis example references Liferay's Bookmarks app and Bookmarks Entry entities.\n\n1.  In your app's `-service` bundle, create a package that holds your Staged\n    Model Repository classes (e.g.,\n    `com.liferay.bookmarks.exportimport.staged.model.repository`). If you do not\n    have a `-service` bundle, visit the\n    [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n    articles for info on generating an app's services. You must have them to\n    leverage most Export/Import and Staging features.\n\n2.  Create your `-StagedModelRepository` class in the new package and implement\n    the `StagedModelRepository` interface in the class' declaration. For\n    example,\n\n    ```java\n    public class BookmarksEntryStagedModelRepository\n        implements StagedModelRepository<BookmarksEntry> {\n    ```\n\n    Be sure also to include the staged model type parameter for this repository\n    (e.g., `BookmarksEntry`).\n\n3.  Add an `@Component` annotation for your staged model repository class that\n    looks like this:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = \"model.class.name=com.liferay.bookmarks.model.BookmarksEntry\",\n        service = StagedModelRepository.class\n    )\n    ```\n\n4.  Implement the `StagedModelRepository` interface's methods in your staged\n    model repository. You can reference the\n    [Javadoc](@app-ref@/web-experience/latest/javadocs/com/liferay/exportimport/staged/model/repository/StagedModelRepository.html)\n    for this interface to learn what each method is intended for.\n\n    As an example, you'll set a couple method implementations to get a taste for\n    how it works.\n\n5.  Implement the `addStagedModel(...)` method. The Bookmarks entry example\n    looks like this:\n\n    ```java\n    @Override\n    public BookmarksEntry addStagedModel(\n            PortletDataContext portletDataContext,\n            BookmarksEntry bookmarksEntry)\n        throws PortalException {\n\n        long userId = portletDataContext.getUserId(\n            bookmarksEntry.getUserUuid());\n\n        ServiceContext serviceContext = portletDataContext.createServiceContext(\n            bookmarksEntry);\n\n        if (portletDataContext.isDataStrategyMirror()) {\n            serviceContext.setUuid(bookmarksEntry.getUuid());\n        }\n\n        return _bookmarksEntryLocalService.addEntry(\n            userId, bookmarksEntry.getGroupId(), bookmarksEntry.getFolderId(),\n            bookmarksEntry.getName(), bookmarksEntry.getUrl(),\n            bookmarksEntry.getDescription(), serviceContext);\n    }\n    ```\n\n    This provides the UUID for the local service.\n\n6.  Not every method implementation requires additional staging information.\n    Implementing the `deleteStagedModels` method calls the local service\n    directly.\n\n    ```java\n    @Override\n    public void deleteStagedModels(PortletDataContext portletDataContext)\n        throws PortalException {\n\n        _bookmarksEntryLocalService.deleteEntries(\n            portletDataContext.getScopeGroupId(),\n            BookmarksFolderConstants.DEFAULT_PARENT_FOLDER_ID);\n    }\n    ```\n\n7.  Finish implementing the `StagedModelRepository` so it's usable in your data\n    handlers.\n\nAwesome! You've implemented the Staged Model Repository framework for your app!\nIf you're interested in leveraging this framework after the implementation\nprocess, see the\n[Using the Staged Model Repository Framework](/docs/7-2/frameworks/-/knowledge_base/f/using-the-staged-model-repository-framework)\narticle.\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/02-export-import/04-providing-entity-specific-local-services-for-export-import/03-using-the-staged-model-repository-framework.markdown",
    "content": "---\nheader-id: using-the-staged-model-repository-framework\n---\n\n# Using the Staged Model Repository Framework\n\n[TOC levels=1-4]\n\nLeveraging the Staged Model Repository framework in your app is easy once you've\n[created staged model repository implementation classes](/docs/7-2/frameworks/-/knowledge_base/f/implementing-the-staged-model-repository-framework).\n\nYou'll step through a quick example to demonstrate leveraging the\n`StagedModelRepository` interface in a staged model data handler. The code\nsnippets originate from Liferay's Bookmarks app and Bookmarks Entries.\n\n1.  Create a getter and setter method to make a `StagedModelRepository` object\n    available for the `BookmarksEntry` entity:\n\n    ```java\n    @Override\n    protected StagedModelRepository<BookmarksEntry> getStagedModelRepository() {\n        return _stagedModelRepository;\n    }\n\n    @Reference(\n        target = \"(model.class.name=com.liferay.bookmarks.model.BookmarksEntry)\",\n        unbind = \"-\"\n    )\n    protected void setStagedModelRepository(\n        StagedModelRepository<BookmarksEntry> stagedModelRepository) {\n\n        _stagedModelRepository = stagedModelRepository;\n    }\n\n    private StagedModelRepository<BookmarksEntry> _stagedModelRepository;\n    ```\n\n2.  Call your `_stagedModelRepository` object to leverage its specialized\n    export/import logic. For example,\n\n    ```java\n    newEntry = _stagedModelRepository.updateStagedModel(portletDataContext, importedEntry);\n    ```\n\n    Without the staged model repository logic, you would've called your local\n    service like this:\n\n    ```java\n    serviceContext.setUuid(entry.getUuid());\n\n    newEntry = _bookmarksEntryLocalService.addEntry(\n        userId, portletDataContext.getScopeGroupId(), folderId, entry.getName(), entry.getUrl(), entry.getDescription(), serviceContext);\n    ```\n\n    The large number of parameters and UUID setter the local service method\n    requires aren't needed when leveraging the staged model repository.\n\nGreat! You've successfully leveraged your staged model repository from a data\nhandler!\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/02-export-import/05-using-the-export-import-lifecycle-listener-framework.markdown",
    "content": "---\nheader-id: using-the-export-import-lifecycle-listener-framework\n---\n\n# Using the Export/Import Lifecycle Listener Framework\n\n[TOC levels=1-4]\n\nIn this tutorial, you'll learn how to use the `ExportImportLifecycleListener`\nframework to listen for processes/events during the staging and export/import\nlifecycles.\n\nTo begin creating your lifecycle listener, you must create a module. Follow the\nsteps below:\n\n1.  [Create an OSGi module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project). \n\n2.  Create a unique package name in the module's `src` directory and create a\n    new Java class in that package. To follow naming conventions, begin the\n    class name with the entity or action name you're processing, followed by\n    *ExportImportLifecycleListener* (e.g.,\n    `LoggerExportImportLifecycleListener`).\n\n3.  You must extend one of the two Base classes provided with the\n    Export/Import Lifecycle Listener framework:\n    [BaseExportImportLifecycleListener](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lifecycle/BaseExportImportLifecycleListener.html)\n    (event listener) or\n    [BaseProcessExportImportLifecycleListener](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lifecycle/BaseProcessExportImportLifecycleListener.html)\n    (process listener). To choose, you'll need to consider what parts of a\n    lifecycle you want to listen for (event or process).\n\n4.  Directly above the class's declaration, insert the following annotation:\n\n    ```java\n    @Component(immediate = true, service = ExportImportLifecycleListener.class)\n    ```\n\n    This annotation declares the implementation class of the component and\n    specifies that the portal should start the module immediately. \n\n5.  Specify the methods you want to implement in your class. As an example,\n    you'll step through the\n    [LoggerExportImportLifecycleListener](@app-ref@/web-experience/latest/javadocs/com/liferay/exportimport/lifecycle/LoggerExportImportLifecycleListener.html).\n    This listener extends the `BaseExportImportLifecycleListener`, so you \n    immediately know that it deals with lifecycle events.\n\n6.  Add the `getStagedModelLogFragment(...)` method:\n\n    ```java\n    protected String getStagedModelLogFragment(StagedModel stagedModel) {\n        StringBundler sb = new StringBundler(8);\n\n        sb.append(StringPool.OPEN_CURLY_BRACE);\n        sb.append(\"class: \");\n        sb.append(ExportImportClassedModelUtil.getClassName(stagedModel));\n\n        if (stagedModel instanceof StagedGroupedModel) {\n            StagedGroupedModel stagedGroupedModel =\n                (StagedGroupedModel)stagedModel;\n\n            sb.append(\", groupId: \");\n            sb.append(stagedGroupedModel.getGroupId());\n        }\n\n        sb.append(\", uuid: \");\n        sb.append(stagedModel.getUuid());\n        sb.append(StringPool.CLOSE_CURLY_BRACE);\n\n        return sb.toString();\n    }\n    ```\n\n    This retrieves the staged model's log fragment, which is the lifecycle\n    listener's logging information on events.\n\n7.  Add the `isParallel()` method:\n\n    ```java\n    @Override\n    public boolean isParallel() {\n        return false;\n    }\n    ```\n\n    This determines whether your listener should run in parallel with the\n    import/export process, or if the calling method should stop, execute the\n    listener, and return to where the event was fired after the listener has\n    finished.\n\n8.  Add the `onExportImportLifecycleEvent(...)` method:\n\n    ```java\n    @Override\n    public void onExportImportLifecycleEvent(\n            ExportImportLifecycleEvent exportImportLifecycleEvent)\n        throws Exception {\n\n        if (!_log.isDebugEnabled()) {\n            return;\n        }\n\n        super.onExportImportLifecycleEvent(exportImportLifecycleEvent);\n    }\n    ```\n\n    This consumes the lifecycle event and passes it through the base class's\n    method (as long as Debug mode is not enabled).\n\n9.  Each remaining method is called to print logging information for the user.\n    For example, when a layout export fails, logging information directly\n    related to that event is printed:\n\n    ```java\n    @Override\n    protected void onLayoutExportFailed(\n            PortletDataContext portletDataContext, Throwable throwable)\n        throws Exception {\n\n        if (!_log.isDebugEnabled()) {\n            return;\n        }\n\n        _log.debug(\n            \"Layout export failed for group \" + portletDataContext.getGroupId(),\n            throwable);\n    }\n    ```\n\n    In summary, the `LoggerExportImportLifecycleListener` uses the lifecycle\n    listener framework to print messages to the log when an export/import event\n    occurs. You can view the other logging methods implemented for this class\n    [here](https://github.com/liferay/liferay-portal/blob/7.2.0-m2/modules/apps/export-import/export-import-service/src/main/java/com/liferay/exportimport/lifecycle/LoggerExportImportLifecycleListener.java).\n\n10. Once you've successfully created your export/import lifecycle listener module,\n    generate the module's JAR file and copy it to @product@'s `osgi/modules`\n    folder.\n\n    Once your module is installed and activated in your instance's service\n    registry, your lifecycle listener is ready for use in your Portal instance.\n\nTerrific! You learned about the Export/Import Lifecycle Listener framework, and\nyou've learned how to create your own listener for events/processes that occur\nduring export/import of your portal's content.\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/02-export-import/06-initiating-new-export-import-processes.markdown",
    "content": "---\nheader-id: initiating-new-export-import-processes\n---\n\n# Initiating New Export/Import Processes\n\n[TOC levels=1-4]\n\nIn this tutorial, you'll learn about the `ExportImportConfiguration` framework\nand how you can take advantage of provided services and factories to create\nthese controller objects. Once they're created, you can easily implement\nwhatever import/export functionality you need.\n\nYour first step is to create an `ExportImportConfiguration` object and use it to\ninitiate your custom export/import or staging process.\n\n1.  Use the\n    [`ExportImportConfigurationSettingsMapFactory`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/configuration/ExportImportConfigurationSettingsMapFactory.html)\n    class to create a layout export settings map:\n\n    ```java\n    Map<String, Serializable> exportLayoutSettingsMap =\n        ExportImportConfigurationSettingsMapFactory.\n            buildExportLayoutSettingsMap(...);\n    ```\n\n2.  Create the `ExportImportConfiguration` object by using an *add* method in\n    the entity's local service. The map created previously is used as a\n    parameter to create the `ExportImportConfiguration` object.\n\n    ```java\n    ExportImportConfiguration exportImportConfiguration =\n        exportImportConfigurationLocalService.\n            addDraftExportImportConfiguration(\n                user.getUserId(),\n                ExportImportConfigurationConstants.TYPE_EXPORT_LAYOUT,\n                exportLayoutSettingsMap);\n    ```\n\n    The\n    [ExportImportConfigurationLocalService](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/service/ExportImportConfigurationLocalService.html)\n    provides several useful methods to create and modify your custom\n    `ExportImportConfiguration`.\n\n3.  Call the appropriate service using your newly created\n    `ExportImportConfiguration` object to initiate an export/import or staging\n    process. For example,\n\n    ```java\n    files[0] = exportImportLocalService.exportLayoutsAsFile(\n        exportImportConfiguration);\n    ```\n\n    Notice that your `ExportImportConfiguration` object is the only needed\n    parameter in the method. Your configuration object holds all the required\n    parameters and settings necessary to export your layouts from @product@.\n\nIt's that easy! To start your own export/import or staging process, you must\ncreate an `ExportImportConfiguration` object using a combination of the three\nprovided `ExportImportConfiguration` factories (outlined\n[here](/docs/7-2/frameworks/-/knowledge_base/f/export-import#exportimport-processes)).\nOnce you have your configuration object, provide it as a parameter in one of the\nmany service methods available to you by the Export/Import or Staging interfaces\nto begin your desired process.\n"
  },
  {
    "path": "en/developer/frameworks/articles/content-publication-management/03-staging/01-intro.markdown",
    "content": "---\nheader-id: staging\n---\n\n# Staging\n\n[TOC levels=1-4]\n\n[Staging](docs/7-2/user/-/knowledge_base/u/staging-content-for-publication) lets\nusers change a Site without affecting the live Site and then publish all the\nchanges in one fell swoop. If you include staging support in your application,\nyour users can stage its content until it's ready.\n\nFor example, if your application uses the Staging framework and provides\ninformation intended only during a specific holiday, users can save your\napplication's assets specific for that holiday. They reside in the Staging\nenvironment until they're ready for publishing. \n\nStaging and Export/Import share the same base framework. When publishing your\nstaged content to the live Site, you're essentially importing content from the\nstaged Site and exporting it to the live Site. This means that implementing\nStaging in your app is *almost* the same as implementing the Export/Import\nframework. You can visit the\n[Export/Import](/docs/7-2/frameworks/-/knowledge_base/f/export-import)\nframework's articles for the base APIs that both it and the Staging frameworks\nshare.\n\nIf your app supports Export/Import, its entities\n([staged models](/docs/7-2/frameworks/-/knowledge_base/f/developing-staged-models))\nare automatically tracked by Staging with the use of\n[data handlers](/docs/7-2/frameworks/-/knowledge_base/f/developing-data-handlers).\nThere are some Staging-specific configurations you can add that are not shared\nby Export/Import. Some Staging-specific actions you can complete include\n\n- Control Staging UI settings\n- Filter Staging-specific processes\n- Check for Staging-specific states\n\nYou'll learn about these next.\n\n## Controlling Staging's UI Settings\n\nYou can control most of Staging's UI from your portlet data handler. This can be\ndone several ways; first, you can configure predefined setter methods in the\nportlet data handler's `activate()` method:\n\n- `setStagingControls`: adds fine grained controls over staging behavior that is\n  rendered in the Staging UI. For example, this enables your app's checkboxes in\n  the Content section of the Publication screen. This is usually set like this:\n  `setStagingControls(getExportControls());`. The staging UI typically provides\n  the same content as the export UI (i.e., the Content section for selecting\n  what to publish/export), so it leverages its UI. You can set the Staging UI\n  differently by configuring the `setStagingControls` method differently. See\n  the\n  [`AssetTagsPortletDataHandler`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/asset/asset-tags-service/src/main/java/com/liferay/asset/tags/internal/exportimport/data/handler/AssetTagsPortletDataHandler.java#L82-L84)\n  class for an example of not copying the Export UI for the Staging UI.\n- `setDataAlwaysStaged`: defines whether you can enable/disable your app's\n  content staging (i.e., selectable from the Publication screen). For example,\n  setting this method to `true` automatically stages your app's content.\n  Users can no longer choose whether its content should be staged.\n\nOther setter methods are available that control both Export/Import and Staging\nsettings. You can reference them by visiting the\n[Understanding the `PortletDataHandler` Interface](/docs/7-2/frameworks/-/knowledge_base/f/developing-data-handlers#understanding-the-portletdatahandler-interface)\nsection.\n\nYou can also control whether your app is enabled on the Staged Content screen by\nadding this method to your portlet data handler:\n\n```java\n@Override\npublic boolean isConfigurationEnabled() {\n    return false;\n}\n```\n\nWhen this is set to `false`, your app is disabled on the Staged Content screen.\nThis is set to `true` by default.\n\n![Figure 1: There are many apps available to select from the Staged Content screen.](../../../images/staged-content-screen.png)\n\nThe majority of Staging-specific configurations are completed in a portlet data\nhandler. The staged model data handler does come into play when you want to\nfilter for certain staging processes/states. You'll learn about this next.\n\n## Filtering Staging-Specific Processes and States\n\nYou can filter for certain staging-specific processes/states and complete\nactions based on the returned status. You can do this by leveraging the\nfollowing classes from a staged model data handler:\n\n- [`ExportImportThreadLocal`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/exportimport/kernel/lar/ExportImportThreadLocal.html)\n- [`StagingGroupHelper`](@app-ref@/staging/latest/javadocs/com/liferay/staging/StagingGroupHelper.html)\n\nThe `ExportImportThreadLocal` class provides boolean methods that return whether\na specific process is in progress. Use this to check for events affecting the\nentire site. For example, you can check if the following processes are in\nprogress:\n\n- Local Staging\n- Remote Staging\n- Layout Validation\n- Portlet Staging\n- etc.\n\nThe `StagingGroupHelper` interface provides utility methods that return the\nstaging state in your app. This is intended to check for events only affecting\nyour app. For example, you can check if your app is in these states:\n\n- Resides in Local Staging group\n- Resides in Remote Live group\n- Is a staged portlet\n- etc.\n\nA real example filtering for a staging process and state can be found in the\n[`AssetListEntryStagedModelDataHandler`](https://github.com/liferay/liferay-portal/blob/7.2.0-b2/modules/apps/asset/asset-list-service/src/main/java/com/liferay/asset/list/internal/exportimport/data/handler/AssetListEntryStagedModelDataHandler.java#L215-L222)\nclass:\n\n```java\nif ((assetRendererFactory != null) &&\n    ExportImportThreadLocal.isStagingInProcess() &&\n    !_stagingGroupHelper.isStagedPortlet(\n        assetEntry.getGroupId(),\n        assetRendererFactory.getPortletId())) {\n\n    continue;\n}\n```\n\nThe staged model data handler uses the\n`ExportImportThreadLocal.isStagingInProcess()` method to verify that a staging\nprocess is running. It also checks whether the app is staged by executing\n`!_stagingGroupHelper.isStagedPortlet(...)`.\n\nExcellent! You can now filter for staging-specific processes and states.\n"
  },
  {
    "path": "en/developer/frameworks/articles/dependency-injection/01-dependency-injection-intro.markdown",
    "content": "---\nheader-id: dependency-injection\n---\n\n# Dependency Injection\n\n[TOC levels=1-4]\n\nWhen you're using a object based on its interface, you don't have to concern\nyourself with the implementation because it's abstracted from you. At runtime,\nthe implementation used depends on your environment and your app's\nconfiguration. @product@ offers several standard ways to register\nimplementations and inject them into your applications. \n\n**Contexts and Dependency Injection (CDI):** Is the Java EE standard dependency \ninjection mechanism. @product@'s CDI bean container makes an application's\nconcrete classes available as beans. Bean classes can user other beans by way of\ninjecting them into their fields that have the `@Inject` annotation. \n\n**OSGi Declarative Services:** @product@'s OSGi runtime framework allows \ncomponents to register as service provides and other components can call on the\nregistry to bind the services to their fields that have the `@Reference`\nannotation. @product@'s services and services you create using [Service\nBuilder](/docs/7-2/appdev/-/knowledge_base/a/service-builder) are available as\nOSGi Declarative Services. \n\n**Spring DI:** The Spring framework includes inversion of control (IoC) and \ndependency injection. It's available to applications that configure the Spring\nframework.  \n\nAs an added bonus, @product@ provides [OSGi CDI\nintegration](/docs/7-2/frameworks/-/knowledge_base/f/osgi-cdi-integration). It\nlets you publish CDI beans as OSGi services and consume OSGi services in your\nCDI beans. Which dependency injection mechanism will you use? Read on to learn\nmore about them. \n"
  },
  {
    "path": "en/developer/frameworks/articles/dependency-injection/02-cdi-dependency-injection.markdown",
    "content": "---\nheader-id: cdi-dependency-injection\n---\n\n# CDI Dependency Injection\n\n[TOC levels=1-4]\n\nPortlet 3.0 (see [JSR 362](https://jcp.org/en/jsr/detail?id=362)) supports\nContexts and Dependency Injection (CDI) so you can create and use\ninjectable classes (CDI beans) in your portlet. It also provides injectable\nportlet artifacts called\n[Portlet Predefined Beans](/docs/7-2/reference/-/knowledge_base/r/cdi-portlet-predefined-beans).\nThey give a portlet's CDI beans access to the portlet configuration,\npreferences, requests, responses, and more. Here's how to create and use CDI\nbeans and Portlet Predefined Beans:\n\n1.  Create a portlet WAR project, if you haven't created one\n    already.\n\n    -   Any project that has a class that implements the\n        [`javax.portlet.Portlet`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/Portlet.html)\n        interface, either directly or indirectly.\n    <!-- -   [Bean Portlet](/docs/7-2/appdev/-/knowledge_base/a/bean-portlet) TODO uncomment when Bean Portlet is available. jhinkey -->\n\n    | **Note:** If you're developing a portlet JAR, such as a\n    | [Liferay MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet),\n    | use CDI via\n    | [OSGi CDI Integration](/docs/7-2/frameworks/-/knowledge_base/f/osgi-cdi-integration).\n\n    | **Note:**\n    | @product@ exports the packages provided by the Portlet API and CDI API.\n    | Liferay project templates typically include them as transitive\n    | dependencies. If you must explicitly depend on the portlet API and CDI\n    | artifacts, add them as `compileOnly` (Gradle) or `provided` (Maven)\n    | dependencies.\n\n2.  If your portlet WAR project isn't a Bean Portlet, add this\n    `src/main/webapp/WEB-INF/beans.xml` file to it. This file tells CDI to\n    scan the project for CDI annotations.\n\n    ```xml\n    <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n    <beans xmlns=\"http://xmlns.jcp.org/xml/ns/javaee\" bean-discovery-mode=\"all\" version=\"1.2\"\n    \t   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    \t   xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd\">\n    \t<!-- This file is necessary in order to inform CDI that scanning should occur for CDI annotations. -->\n    </beans>\n    ```\n\n3.  Add the\n    [`@ApplicationScoped`](https://docs.oracle.com/javaee/7/api/javax/enterprise/context/ApplicationScoped.html)\n    annotation to your portlet class.\n\n    ```java\n    import javax.enterprise.context.ApplicationScoped;\n\n    @ApplicationScoped\n    public class MyPortlet ... {\n        ...\n    }\n    ```\n\n4.  Make sure all concrete classes you want to make injectable have the default\n    constructor. These classes are now CDI beans.\n\n5.  Add a scope to each CDI bean.\n\n    | Bean Scope              | Description      |\n    | ----------------------- | ---------------- |\n    | [`@ApplicationScoped`](https://docs.oracle.com/javaee/7/api/javax/enterprise/context/ApplicationScoped.html) | Shares the bean's state across all users' interactions with the portlet. |\n    | [`@Dependent`](https://docs.oracle.com/javaee/7/api/javax/enterprise/context/Dependent.html) | (default scope) Designates the bean to be for the client bean and share the client bean's lifecycle. |\n    | [`@PortletRequestScoped`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/PortletRequestScoped.html) | Associates the bean with the portlet request. |\n    | [`@PortletSessionScoped`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/PortletSessionScoped.html) | Places the bean in the portlet session. |\n    | [`@RenderStateScoped`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/RenderStateScoped.html) | Stores the bean as part of the portlet's render state. **Important:** The bean must implement the `PortletSerializable` interface. |\n\n6.  Use the [JSR 330](https://jcp.org/en/jsr/detail?id=330)\n    [`@Inject`](https://docs.oracle.com/javaee/7/api/javax/inject/Inject.html)\n    annotation in a CDI bean to inject another CDI bean into it. For example,\n    this code informs @product@'s CDI bean container to inject a `GuestBook` CDI\n    bean into this `guestbook` field.\n\n    ```java\n    @Inject\n    private GuestBook guestbook;\n    ```\n\n7.  Inject any [Portlet Predefined Beans](/docs/7-2/reference/-/knowledge_base/r/cdi-portlet-predefined-beans)\n    (portlet request scoped or dependent scoped beans) into your\n    `@PortletRequestScoped` CDI beans.\n\n    ```java\n    @PortletRequestScoped\n    public class RequestProcessor ... {\n\n        @Inject\n        private PortletRequest portletRequest;\n        ...\n    }\n    ```\n\n8.  Inject any [dependent scoped Portlet Predefined Beans](/docs/7-2/reference/-/knowledge_base/r/cdi-portlet-predefined-beans)\n    into your `ApplicationScoped` or `@Dependent` scoped CDI beans. For example,\n\n    ```java\n    @ApplicationScoped\n    public class MyPortlet ... {\n\n        @Inject\n        private PortletConfig portletConfig;\n        ...\n    }\n    ```\n\n9.  Use bean EL names to reference any\n    [portlet redefined *named beans*](/docs/7-2/reference/-/knowledge_base/r/cdi-portlet-predefined-beans)\n    in your JSP or JSF pages.\n\n10. [Deploy](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project)\n    your project.\n\nCongratulations! You have created and used CDI beans and Portlet Predefined\nBeans in your portlet.\n\n## Related Topics\n\n[CDI Portlet Predefined Beans](/docs/7-2/reference/-/knowledge_base/r/cdi-portlet-predefined-beans)\n\n<!--[Bean Portlet](/docs/7-2/appdev/-/knowledge_base/a/bean-portlet) TODO uncomment when Bean Portlet is available. jhinkey -->\n\n[Portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets)\n"
  },
  {
    "path": "en/developer/frameworks/articles/dependency-injection/declarative-services.markdown",
    "content": "---\nheader-id: declarative-services\n---\n\n# Declarative Services\n\n[TOC levels=1-4]\n\n@product@'s OSGi framework registers objects as *services*. Each service\noffers functionality and can leverage functionality other services provide. The\nOSGi Services model supports a collaborative environment for objects.\n\nDeclarative Services (DS) provides a service component model on top of OSGi\nServices. DS service components are marked with the\n[`@Component`](https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html)\nannotation and implement or extend a service class. Service components can refer\nto and use each other's services. The Service Component Runtime (SCR) registers\ncomponent services and handles binding them to other components that reference\nthem. \n\nHere's how the \"magic\" happens:\n\n1.  **Service registration:** On installing a module that contains a\n    service component, the SCR creates a component configuration that associates\n    the component with its specified service type and stores it in a service\n    registry. \n\n2.  **Service reference handling:** On installing a module whose service\n    component references another service type, the SCR searches the registry for\n    a component configuration that matches the service type and on finding a\n    match binds an instance of that service to the referring component.\n\nIt's publish, find, and bind at its best!\n\nHow do you use DS to register and bind services? Does it involve creating XML\nfiles? No, it's much easier than that. You use two annotations: `@Component` and\n`@Reference`. \n\n-   [`@Component`](https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html):\n    Add this annotation to a class definition to make the class a component--a\n    service provider. \n\n-   [`@Reference`](https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Reference.html):\n    Add this annotation to a field to inject it with a service that matches the\n    field's type. \n\nThe\n[`@Component`](https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html)\nannotation makes the class an OSGi component. Setting the annotation's `service`\nattribute to a particular service type allows other components to reference the\nservice component by that service type.\n\nFor example, the following class is a service component of type `SomeApi.class`.\n\n```java\n@Component(\n    service = SomeApi.class\n)\npublic class Service1 implements SomeApi {\n   ...\n}\n```\n\nOn deploying this class's module, the SCR creates a component configuration that\nassociates the class with the service type `SomeApi`.\n\nSpecifying a service reference is easy too. Applying the\n[`@Reference`](https://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Reference.html)\nannotation to a field marks it to be injected with a service matching the\nfield's type.\n\n```java\n@Reference\nSomeApi _someApi;\n```\n\nOn deploying this class's module, the SCR finds a component configuration of the\nclass type `SomeApi` and binds the service to this referencing component class's\nfield `SomeApi _someApi`.\n\n| **Note:** The `@Reference` annotation can only be used in a class that is\n| annotated with `@Component` (i.e, a Declarative Services component ) or a bean\n| class that uses OSGi CDI integration. \n\n| **Note:** At build time in modules created from\n| [Liferay project templates](/docs/7-1/reference/-/knowledge_base/r/project-templates),\n| bnd creates a *component description* file for each module's components\n| automatically. The file specifies the component's services, dependencies, and\n| activation characteristics. On module deployment, the OSGi framework reads the\n| component description to create the component and manage its dependency on \n| other components.\n\nThe SCR stands ready to pair service components with each other. For each\nreferencing component, the SCR binds an instance of the targeted service to it.\n\nAs an improvement over dependency injection with Spring, OSGi Declarative\nServices supports dynamic dependency injection. You can create and publish\nservice components for other classes to use. You can update the components and\neven publish alternative component implementations for a service. This kind of\ndynamism is a powerful part of @product@. \n"
  },
  {
    "path": "en/developer/frameworks/articles/dependency-injection/osgi-cdi-integration/01-osgi-cdi-integration-intro.markdown",
    "content": "---\nheader-id: osgi-cdi-integration\n---\n\n# OSGi CDI Integration\n\n[TOC levels=1-4]\n\n@product@'s runtime environment consists of services (OSGi services). The OSGi\nservice registry and Service Component Runtime (SCR) facilitate providing and\nconsuming services.\n[Contexts and Dependency Injection (CDI)](http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html)\nis a Java SE and EE standard for lifecycle events, stateful objects, and\ndependency injection. \n[OSGi CDI Integration](https://osgi.org/specification/osgi.enterprise/7.0.0/service.cdi.html)\nbrings features and capabilities of CDI to OSGi and makes OSGi services available to\nCDI beans. Here you'll learn how to\n\n-   [Publish CDI beans as OSGi services](/docs/7-2/frameworks/-/knowledge_base/f/publishing-cdi-beans-as-osgi-services):\n    Register CDI beans as services you can use to customize @product@\n    components. \n\n-   [Use OSGi services in beans](/docs/7-2/frameworks/-/knowledge_base/f/using-osgi-services-in-a-bean):\n    Leverage any OSGi service published on @product@ in any bean. \n\nThe following use cases provide more detail. \n\n## Use Case: Registering a CDI bean as an OSGi service\n\n@product@ extension points are implemented as OSGi services. If there's a piece\nof functionality you must customize, you don't have to learn OSGi to do it: you\ncan write your extension/override as a CDI bean instead and use OSGi CDI\nintegration to publish your bean as an OSGi service. \n\nBy implementing the service in your CDI bean class and adding the integration's\n`@org.osgi.service.cdi.annotations.Service` annotation to it, your bean\nregisters as providing that OSGi service. In this way, service consumers can use\nyour service implementation (i.e., your CDI bean). \n\nFor example, the Service Registry in figure 1 shows two implementations of an\nOSGi service called `S1`: \n\n![Figure 1: OSGi Service Component Runtime (SCR) finds `MyBean` as the best (highest ranked) `S1` service provider and binds it to consumer component `C1`.](../../../images/injecting-bean-osgi-service.png)\n\n- `MyBean` is a CDI bean whose service rank is `1000`. \n\n- `MySvcImpl` has a service rank of `0`. \n\nThe Service Component Runtime (`SCR`) finds the matching, highest ranked `S1`\nservice provider and binds it to consumer `C1`. The fact that `MyBean` is a CDI\nbean is transparent to the SCR. \n\n\nOnce a CDI bean is registered as a service, components can use it as they would\nany other OSGi service. \n\n## Use Case: Using an OSGi service in a bean\n\n@product@ contains many development frameworks for all of its constructs, such\nas Users, Sites, Documents, Comments, and the APIs for these assets are all\nimplemented as OSGi services. When developers write new applications using\nLiferay's development frameworks, new assets become available and integrated\nwith the rest of the system. OSGi CDI integration enables your beans to access\nthese OSGi services. \n\n![Figure 2: Here how Liferay's `UserLocalService` is injected into a bean.](../../../images/using-a-service-in-a-bean.png) \n\nIn figure 2, for example, CDI bean `SomeBean` uses the OSGi CDI integration\nannotation `@org.osgi.service.cdi.annotations.Reference` (along with CDI\nannotation `@Inject`) to inject the OSGi service `UserLocalService`. The Service\nComponent Runtime (`SCR`) finds the `UserLocalService` in the Service Registry\nand binds it to `SomeBean`'s field `userSvc`. \n\n\n\nThese are the most common use cases, but you might have more.  Now you can get\nstarted using OSGi CDI integration to\n[publish CDI beans as OSGi services](/docs/7-2/frameworks/-/knowledge_base/f/publishing-cdi-beans-as-osgi-services)! \n"
  },
  {
    "path": "en/developer/frameworks/articles/dependency-injection/osgi-cdi-integration/02-publishing-cdi-beans-as-osgi-services.markdown",
    "content": "---\nheader-id: publishing-cdi-beans-as-osgi-services\n---\n\n# Publishing CDI Beans as OSGi Services\n\n[TOC levels=1-4]\n\nYou can publish CDI beans as OSGi services, making them accessible via the\nLiferay's OSGi service registry. Here's how: \n\n1.  Add a project dependency on the OSGi CDI Integration artifact. For example, \n    here's the dependency to use in a Maven `pom.xml` file:\n\n    ```xml\n    <dependency>\n        <groupId>org.osgi</groupId>\n        <artifactId>org.osgi.service.cdi</artifactId>\n        <version>1.0.0</version>\n    </dependency>\n    ```\n\n2.  Make your CDI bean implement the service interface you're providing. For \n    example, `ShopImpl` provides the `Shop` service by implementing that\n    interface.\n\n    ```java\n    package my.package;\n\n    public class ShopImpl implements Shop {\n        ...\n    }\n    ```\n\n3.  Annotate your CDI bean class with \n    `@org.osgi.service.cdi.annotations.Service`. \n\n    ```java\n    package my.package;\n\n    import org.osgi.service.cdi.annotations.Service;\n\n    @Service \n    public class ShopImpl implements Shop {\n        ...\n    }\n    ```\n\n4.  Deploy the API that defines the service interface, if you haven't deployed \n    it already. \n\n5.  Build and deploy your service project bundle. \n\nOnce your bundle installs and activates, your bean's service implementation is\navailable. You can use Gogo Shell commands can verify that the service\nregistered. \n\nFor example, here are steps for verifying that a bundle\n`com.liferay.portal.samples.cdi.jar.portlet` registers a service called\n`org.apache.portals.samples.Users`. \n\n1.  Navigate to *Control Panel* &rarr; *Configuration* &rarr; *Gogo Shell*. \n\n2.  Use the `lb` Gogo command and `grep` (pass in the bundle's symbolic name) to\n    find your bundle (and its ID). \n\n    Example command:\n\n        g!: lb | grep com.liferay.portal.samples.cdi.jar.portlet\n\n    Results:\n\n        924|Active     |   10|com.liferay.portal.samples.cdi.jar.portlet (0.0.1.201901252134)|0.0.1.201901252134\n\n    The first column contains the bundle ID. \n\n3.  Use the `b` Gogo command with your bundle ID to list your bundle's details \n    and verify the bundle includes your service as one of its registered\n    services. \n\n    Example command:\n\n        g!: b 924\n\n    Results:\n\n        com.liferay.portal.samples.cdi.jar.portlet_0.0.1.201901252134 [924]\n        Id=924, Status=ACTIVE      Data Root=C:\\git\\bundles\\osgi\\state\\org.eclipse.osgi\\924\\data\n        \"Registered Services\"\n          ...\n          {org.apache.portals.samples.Users}={osgi.command.scope=cdiportlet, service.id=4232, service.bundleid=924, service.scope=singleton, osgi.command.function=[getUsersCount], component.name=com.liferay.portal.samples.cdi.jar.portlet, component.id=1}\n          ...\n\nCongratulations on publishing your CDI bean as an OSGi service! \n"
  },
  {
    "path": "en/developer/frameworks/articles/dependency-injection/osgi-cdi-integration/03-using-osgi-services-in-a-bean.markdown",
    "content": "---\nheader-id: using-osgi-services-in-a-bean\n---\n\n# Using OSGi Services in a Bean\n\n[TOC levels=1-4]\n\nAny bean can use the `@org.osgi.service.cdi.annotations.Reference` annotation to\ninject OSGi services. It's the easiest way for a bean to access an OSGi service.\nHere's how:\n\n1.  Add a project dependency on the OSGi CDI Integration artifact. For example,\n    here's the dependency to use in a Maven `pom.xml` file:\n\n    ```xml\n    <dependency>\n        <groupId>org.osgi</groupId>\n        <artifactId>org.osgi.service.cdi</artifactId>\n        <version>1.0.0</version>\n    </dependency>\n    ```\n\n2.  Obtain and inject the OSGi service by using the\n    `@org.osgi.service.cdi.annotations.Reference` and `@javax.inject.Inject`\n    annotations respectively. Here's an example of injecting a service of type\n    `ProductStore`. \n\n    ```java\n    import javax.inject.Inject;\n\n    import org.osgi.service.cdi.annotations.Reference;\n\n    import package.path.ProductStore;\n\n    public class MyBean {\n\n        @Inject\n        @Reference\n        ProductStore productStore;\n\n        ...\n    }\n    ```\n\n3.  Deploy your bean project to @product@. \n\nCongratulations on injecting an OSGi service into your bean! Now your bean uses\nthe OSGi service you injected.\n"
  },
  {
    "path": "en/developer/frameworks/articles/dependency-injection/service-trackers-for-osgi-services.markdown",
    "content": "---\nheader-id: service-trackers-for-osgi-services\n---\n\n# Service Trackers for OSGi Services\n\n[TOC levels=1-4]\n\nIn an OSGi runtime ecosystem, you must consider how your apps can rely\non OSGi services in other modules for functionality. It's possible for service\nimplementations to be swapped out or removed entirely, and your app must not\njust survive but thrive in this environment. \n\nIf you want to [call OSGi services from an OSGi Declarative Services\n`@Component`](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services)\nclasses, it's easy: you just use a [Declarative Services\n(DS)](https://osgi.org/specification/osgi.cmpn/7.0.0/service.component.html)\nannotation,\n[`@Reference`](ttps://osgi.org/javadoc/r6/residential/org/osgi/service/component/annotations/Reference.html),\nto inject the service. The component activates when the referenced service is\navailable. \n\n| **Note:** The `@Reference` annotation can only be used in a class that is\n| annotated with\n| [`@Component`](https://docs.osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/Component.html).\n| That is, only a Declarative Services component can use `@Reference` to bind to\n| an OSGi service.\n\nIf you want to call an OSGi service from a bean, use [OSGi CDI\nintegration](/docs/7-2/frameworks/-/knowledge_base/f/using-osgi-services-in-a-bean). \n\nDS `@Reference` with `@Component`s and OSGi CDI integration with beans manage\nmuch of the complexity of service dynamism for you transparently. If you can use\neither of them, you should. Otherwise, read [implement a Service\nTracker](/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker) to\nlook up services in the service registry. \n"
  },
  {
    "path": "en/developer/frameworks/articles/dependency-injection/using-a-service-tracker.markdown",
    "content": "---\nheader-id: using-a-service-tracker\n---\n\n# Using a Service Tracker\n\n[TOC levels=1-4]\n\nYour non-OSGi and non-bean classes can access any service registered in the OSGi\nruntime using a Service Tracker. It lets you access any OSGi services including\nyour own  [Service Builder\nservices](/docs/7-2/appdev/-/knowledge_base/a/service-builder) and the services\npublished by Liferay's modules (like the popular `UserLocalService`). \n\nYou can create a service tracker in two ways: \n\n1.  Create a service tracker where you need it. \n\n2.  Create a class that extends `org.osgi.util.tracker.ServiceTracker`. \n\n3.  Create a service tracker that tracks service events using a callback \n    handler. \n\nBoth ways depend on `org.osgi.core`, whose packages @product@ exports by\ndefault. Configure it as `compileOnly` (Gradle) or `provided`  (Maven). See the\n[Third Party Packages Portal\nExports](/docs/7-1/reference/-/knowledge_base/r/third-party-packages-portal-exports)\nfor more information. \n\n| **Note:**  The static utility classes (e.g., `UserLocalServiceUtil`) that were\n| useful in Liferay Portal 6.2 (and earlier) exist for compatibility but should\n| not be called, if possible.  Static utility classes cannot account for the \n| OSGi runtime's dynamic environment. If you use a static class, you might\n| attempt calling a stopped service or one that hasn't been deployed or started.\n| This could cause unrecoverable runtime errors. Service Trackers, however, help\n| you make OSGi-friendly service calls. \n\n## Creating a New Service Tracker Where You Need It\n\nTo create it directly, do this: \n\n```java\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\nBundle bundle = FrameworkUtil.getBundle(this.getClass());\nBundleContext bundleContext = bundle.getBundleContext();\nServiceTracker<SomeService, SomeService> serviceTracker =\n    new ServiceTracker(bundleContext, SomeService.class, null);\nserviceTracker.open();\nSomeService someService = serviceTracker.waitForService(500);\n```\n\n## Create a Class That Extends ServiceTracker \n\nA better way is to create a class that extends\n`org.osgi.util.tracker.ServiceTracker`, because this simplifies your code. \n\n1.  Create a class like this one that extends `ServiceTracker`: \n\n    ```java\n    public class SomeServiceTracker\n        extends ServiceTracker<SomeService, SomeService> {\n\n        public SomeServiceTracker(Object host) {\n            super(\n                FrameworkUtil.getBundle(host.getClass()).getBundleContext(),\n                SomeService.class, null);\n        }\n    }\n    ```\n\n2.  Construct a new instance of your service tracker where you need it. The \n    `Object host` parameter obtains your own bundle context and must be an\n    object from your own bundle in order to give accurate results. \n\n    ```java\n    ServiceTracker<SomeService, SomeService> someServiceTracker =\n        new SomeServiceTracker(this);\n    ```\n\n3.  When you want to use the service tracker, open it, typically as early as\n    you can. \n\n    ```java\n    someServiceTracker.open();\n    ```\n\n4.  Before attempting to use a service, use the Service Tracker to interrogate\n    the service's state. For example, check whether the service is `null`:\n\n    ```java\n    SomeService someService = someServiceTracker.getService();\n\n    if (someService == null) {\n        _log.warn(\"The required service 'SomeService' is not available.\");\n    }\n    else {\n        someService.doSomethingCool();\n    }\n    ```\n\nNote, service trackers have several other utility functions for\nintrospecting tracked services.\n\n5.  Later when your application is being destroyed or undeployed, close the\n    service tracker. \n\n    ```java\n    someServiceTracker.close();\n    ```\n\nIf you need to track multiple services or their events, implement a service tracker that uses callback handlers. \n\n## Creating a Service Tracker that Tracks Service Events Using a Callback Handler\n\nIf there's a strong possibility the service might not be available or if you\nneed to track multiple services, the Service Tracker API provides a callback\nmechanism that operates on service *events*. To use this, override\n`ServiceTracker`'s `addingService` and `removedService` methods. Their\n`ServiceReference` parameter references an active service object. \n\nHere's an example `ServiceTracker` implementation from the [OSGi Alliance's OSGi\nCore Release 7\nspecification](https://osgi.org/specification/osgi.core/7.0.0/util.tracker.html#d0e51991):\n\n```java\nnew ServiceTracker<HttpService, MyServlet>(context, HttpService.class, null) {\n\n    public MyServlet addingService(ServiceReference<HttpService> reference) {\n        HttpService httpService = context.getService(reference);\n        MyServlet myServlet = new MyServlet(httpService);\n        return myServlet;\n    }\n\n    public void removedService(\n        ServiceReference<HttpService> reference, MyServlet myServlet) {\n        myServlet.close();\n        context.ungetService(reference);\n    }\n}\n```\n\nWhen the `HttpService` is added to the OSGi registry, this `ServiceTracker`\ncreates a new wrapper class, `MyServlet`, which uses the newly added service.\nWhen the service is removed from the registry, the `removedService` method\ncleans up related resources. \n\nAs an alternative to directly overloading `ServiceTracker` methods, create a\n`org.osgi.util.tracker.ServiceTrackerCustomizer`: \n\n```java\nclass MyServiceTrackerCustomizer \n    implements ServiceTrackerCustomizer<SomeService, MyWrapper> {\n    \n    private final BundleContext bundleContext;\n    \n    MyServiceTrackerCustomizer(BundleContext bundleContext) {\n        this.bundleContext = bundleContext;\n    }\n    \n    @Override\n    public MyWrapper addedService(\n        ServiceReference<SomeService> serviceReference) {\n        \n        // Determine if the service is one that's interesting to you.\n        // The return type of this method is the `tracked` type. Its type \n        // is what is returned from `getService*` methods; useful for wrapping \n        // the service with your own type (e.g., MyWrapper).\n        if (isInteresting(serviceReference)) {\n            MyWrapper myWrapper = new MyWrapper(\n                serviceReference, bundleContext.getService());\n            \n            // trigger the logic that requires the available service(s)\n            triggerServiceAddedLogic(myWrapper);\n            \n            return myWrapper;\n        }\n        \n        // If the return is null, the tracker is effectively ignoring any further\n        // events for the service reference\n        return null;\n    }\n\n    @Override\n    public void modifiedService(\n        ServiceReference<SomeService> serviceReference, MyWrapper myWrapper) {\n        // handle the modified service\n    }\n\n    @Override\n    public void removedService(\n        ServiceReference<SomeService> serviceReference, MyWrapper myWrapper) {\n\n        // finally, trigger logic when the service is going away\n        triggerServiceRemovedLogic(myWrapper);\n\t}\n\n}\n```\n\nRegister the `ServiceTrackerCustomizer` by passing it as the `ServiceTracker`\nconstructor's third parameter.\n\n```java\nServiceTrackerCustomizer<SomeService, MyWrapper> serviceTrackerCustomizer =\n    new MyServiceTrackerCustomizer();\n\nServiceTracker<SomeService, MyWrapper> serviceTracker = \n    new ServiceTracker<>(\n    \tbundleContext, SomeService.class, serviceTrackerCustomizer);\n```\n\nUsing service trackers requires producing some boilerplate code, but now you can\nlook up services in the service registry, even if your plugins can't take\nadvantage of the Declarative Services component model. \n"
  },
  {
    "path": "en/developer/frameworks/articles/friendly-urls/01-friendly-urls-intro.markdown",
    "content": "---\nheader-id: friendly-urls\n---\n\n# Friendly URLs\n\n[TOC levels=1-4]\n\nThis is a story of two URLs who couldn't be more different. One was full of\nhimself and always wanted to show everyone (users and SEO services alike) just\nhow smart he was by openly displaying all the parameters he carried. He was\nhappiest when he could tell people he met were intimidated and confused by him.\n\n    http://localhost:8080/group/guest/~/control_panel/manage?p_p_id=com_liferay_blogs_web_portlet_BlogsAdminPortlet&p_p_lifecycle=0&p_p_state=maximized&p_p_mode=view&_com_liferay_blogs_web_portlet_BlogsAdminPortlet_mvcRenderCommandName=%2Fblogs%2Fedit_entry&_com_liferay_blogs_web_portlet_BlogsAdminPortlet_redirect=http%3A%2F%2Flocalhost%3A8080%2Fgroup%2Fguest%2F~%2Fcontrol_panel%2Fmanage%3Fp_p_id%3Dcom_liferay_blogs_web_portlet_BlogsAdminPortlet%26p_p_lifecycle%3D0%26p_p_state%3Dmaximized%26p_p_mode%3Dview%26_com_liferay_blogs_web_portlet_BlogsAdminPortlet_mvcRenderCommandName%3D%252Fblogs%252Fview%26_com_liferay_blogs_web_portlet_BlogsAdminPortlet_orderBycol%3Dtitle%26_com_liferay_blogs_web_portlet_BlogsAdminPortlet_orderByType%3Dasc%26_com_liferay_blogs_web_portlet_BlogsAdminPortlet_entriesNavigation%3D%26_com_liferay_blogs_web_portlet_BlogsAdminPortlet_cur%3D1%26_com_liferay_blogs_web_portlet_BlogsAdminPortlet_delta%3D20&_com_liferay_blogs_web_portlet_BlogsAdminPortlet_entryId=30836\n\nThe other was just, well, friendly. She was less concerned about looking smart\nand more concerned about those she interacted with, so she shared only the\nimportant things about her. She didn't need to look fancy and complicated. She\naspired to be simple and kind to all the users and SEO services she\nencountered.\n\n    http://localhost:8080/web/guest/home/-/blogs/lunar-scavenger-hunt\n\nIf you want your application to be friendly to your users and to SEO services,\nmake your URLs friendlier. It only takes a couple steps, after all. \n"
  },
  {
    "path": "en/developer/frameworks/articles/friendly-urls/02-creating-friendly-urls.markdown",
    "content": "---\nheader-id: creating-friendly-urls\n---\n\n# Friendly URLs\n\n[TOC levels=1-4]\n\nFollow these steps to create friendly URLs:\n\n1.  Create friendly URL routes. Create a `routes.xml` file in your application's \n    web module. Liferay's pattern puts it in a \n    `src/main/resources/META-INF/friendly-url-routes/` folder. \n\n2.  Add friendly URL routes, using as many `<route>` tags as you need friendly \n    URLs, like this:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <!DOCTYPE routes PUBLIC \"-//Liferay//DTD Friendly URL Routes 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-friendly-url-routes_7_2_0.dtd\">\n\n    <routes>\n        <route>\n            <pattern></pattern>\n            <implicit-parameter name=\"mvcRenderCommandName\">/blogs/view</implicit-parameter>\n            <implicit-parameter name=\"p_p_lifecycle\">0</implicit-parameter>\n            <implicit-parameter name=\"p_p_state\">normal</implicit-parameter>\n        </route>\n        <route>\n            <pattern>/maximized</pattern>\n            <implicit-parameter name=\"mvcRenderCommandName\">/blogs/view</implicit-parameter>\n            <implicit-parameter name=\"p_p_lifecycle\">0</implicit-parameter>\n            <implicit-parameter name=\"p_p_state\">maximized</implicit-parameter>\n        </route>\n        <route>\n            <pattern>/{entryId:\\d+}</pattern>\n            <implicit-parameter name=\"categoryId\"></implicit-parameter>\n            <implicit-parameter name=\"mvcRenderCommandName\">/blogs/view_entry</implicit-parameter>\n            <implicit-parameter name=\"p_p_lifecycle\">0</implicit-parameter>\n            <implicit-parameter name=\"p_p_state\">normal</implicit-parameter>\n            <implicit-parameter name=\"tag\"></implicit-parameter>\n        </route>\n        ...\n    </routes>\n    ```\n\n    Use `<pattern>` tags to define placeholder values for the parameters that \n    normally appear in the generated URL. This is just a mask. The beastly URL  \n    still lurks beneath it. \n\n    The `pattern` value `/{entryId:\\d+}` matches a `/` followed by an `entryId` \n    variable that matches the \n    [Java regular expression](https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html) \n    `\\d+`---one or more numeric digits. For example, a URL `/entryId`, where the \n    `entryId` value is `123` results in a URL value `/123`, which matches the \n    pattern. \n\n    | **Warning:** Make sure your `pattern` values don't end in a slash `/`. A\n    | trailing slash character prevents the request from identifying the correct\n    | route.\n\n    **Important:** If your portlet is instanceable, you must use a variant of \n    the `instanceId` in the `pattern` value. If the starting value is \n    `render-it`, for example, use one of these patterns:\n\n    ```xml\n    <pattern>/{userIdAndInstanceId}/render-it</pattern>\n    ```\n    \n    or\n\n    ```xml\n    <pattern>/{instanceId}/render-it</pattern>\n    ```\n    \n    or\n\n    ```xml\n    <pattern>/{p_p_id}/render-it</pattern>\n    ```\n    \n    Use `<implicit-parameter>` tags to define parameters that are always the \n    same for the URL. For example, for a render URL, you can be certain that the \n    `p_p_lifecycle` parameter is always `0`. You don't have to define these \n    types of implicit parameters, but it's a best practice because if you don't, \n    they still appear in your URL. \n\n    The implicit parameters with the name `mvcRenderCommandName` are very \n    important. If you're using an `MVCPortlet` with `MVCRenderCommand` classes, \n    that parameter comes from the `mvc.command.name` property in the \n    `@Component` of your `MVCRenderCommand` implementation. This determines the \n    page that's rendered (for example, `view.jsp`). \n\n    <!--Add link back for 'using an `MVCPortlet` with `MVCRenderCommand` classes' \n    once mvc-render-command article is available-->\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS, \"mvc.command.name=/\",\n            \"mvc.command.name=/blogs/view\"\n        },\n        service = MVCRenderCommand.class\n    )\n    ```\n\n    The [DTD file](@platform-ref@/7.2-latest/definitions/liferay-friendly-url-routes_7_2_0.dtd.html)\n    completely defines the `routes.xml` file. \n\n3.  Provide an implementation of the [`FriendlyURLMapper` service](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/FriendlyURLMapper.html). \n    Create a component that specifies a `FriendlyURLMapper` service, with two \n    properties:\n\n    -  A `com.liferay.portlet.friendly-url-routes` property sets the path to \n       your `routes.xml` file.\n\n    -  A `javax.portlet.name` property, which you probably have already, \n       specifies your portlet's name.\n\n    ```java\n    @Component(\n        property = {\n            \"com.liferay.portlet.friendly-url-routes=META-INF/friendly-url-routes/routes.xml\",\n            \"javax.portlet.name=\" + BlogsPortletKeys.BLOGS\n        },\n        service = FriendlyURLMapper.class\n    )\n    ```\n\n4.  Implement the `FriendlyURLMapper` service. For your convenience, the \n    [`DefaultFriendlyURLMapper` class](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/DefaultFriendlyURLMapper.html) \n    provides a default implementation. If you extend `DefaultFriendlyURLMapper` \n    you must only override one method, `getMapping()`. Return a String that \n    defines the first part of your Friendly URLs. It's smart to name it after \n    your application. Here's what it looks like for Liferay's Blogs application:\n\n    ```java\n    public class BlogsFriendlyURLMapper extends DefaultFriendlyURLMapper {\n\n        @Override\n        public String getMapping() {\n            return _MAPPING;\n        }\n\n        private static final String _MAPPING = \"blogs\";\n\n    }\n    ```\n\nAll friendly URLs in Blogs begin with the String set here (`blogs`). Let's look \nat one of these Friendly URLs in action. Add a blog entry and then click on the \nentry's title. Look at the URL:\n\n```html\nhttp://localhost:8080/web/guest/home/-/blogs/lunar-scavenger-hunt\n```\n\nAs specified in the friendly URL mapper class, `blogs` is the first part of the\nfriendly URL that comes after the Liferay part of the URL. The next part is\ndetermined by a specific URL route in `routes.xml`:\n\n```xml\n<route>\n    <pattern>/{urlTitle}</pattern>\n    <implicit-parameter name=\"categoryId\"></implicit-parameter>\n    <implicit-parameter name=\"mvcRenderCommandName\">/blogs/view_entry</implicit-parameter>\n    <implicit-parameter name=\"p_p_lifecycle\">0</implicit-parameter>\n    <implicit-parameter name=\"p_p_state\">normal</implicit-parameter>\n    <implicit-parameter name=\"tag\"></implicit-parameter>\n</route>\n```\n\nThe `urlTitle` is generated from the blog post's title. Since it's already\na parameter in the URL (see below), it's available for use in the friendly URL.\n\n```markup\n<portlet:renderURL var=\"viewEntryURL\">\n    <portlet:param name=\"mvcRenderCommandName\" value=\"/blogs/view_entry\" />\n    <portlet:param name=\"urlTitle\" value=\"<%= entry.getUrlTitle() %>\" />\n</portlet:renderURL>\n```\n\nWhen the render URL is invoked, the String defined in the friendly URL mapper\nteams up with the `pattern` tag in your friendly URL routes file, and you get\na very friendly URL indeed, instead of some nasty, conceited, unfriendly URL\nthat's despised by users and SEO services alike.\n\nGreat! Now you know how to make your URLS friendly. \n\n## Related Topics\n\n- [Dependency Injection](/docs/7-2/frameworks/-/knowledge_base/f/dependency-injection)\n- [Localization](/docs/7-2/frameworks/-/knowledge_base/f/localization)\n- [Asset Framework](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/01-front-end-dev-intro.markdown",
    "content": "---\nheader-id: front-end-development\n---\n\n# Front-End Development\n\n[TOC levels=1-4]\n\nYou have complete front-end development freedom. You can use @product@'s \nfront-end frameworks, along with the front-end technologies you love the most:\n\n-   EcmaScript ES2015+\n-   React, Angular, Vue, etc.\n-   [Metal.js](https://metaljs.com/) (developed by Liferay)\n-   [AlloyUI](https://alloyui.com/) (developed by Liferay)\n-   jQuery (included)\n-   Lodash (included, [but disabled by default](https://github.com/liferay/liferay-portal/blob/master/readme/BREAKING_CHANGES.markdown#lodash-is-no-longer-included-by-default))\n\nTo load modules, you must know when they are needed, where they are at build\ntime, whether they should be bundled together or loaded independently, and\nyou must assemble them at runtime. Liferay's Loaders (YUI/AUI, AMD, and npm in\nAMD format) handle loading for you. All you must do is provide a small bit of\ninformation about your module. \n\nThe Liferay JS Bundle Toolkit(the \n[JS Portlet Extender](https://web.liferay.com/marketplace/-/mp/application/115542926), \n[Liferay Bundle Generator](https://www.npmjs.com/package/generator-liferay-bundle), \nand \n[`liferay-npm-bundler`](/docs/7-2/reference/-/knowledge_base/r/liferay-npm-bundler)\n) has the tools you need to create and develop JavaScript portlets with pure \nJavaScript tooling. You can use the \n[`liferay-npm-bundler`](/docs/7-2/reference/-/knowledge_base/r/liferay-npm-bundler)\nto bundle npm packages in your applications. It even has several presets for \ncommon module types (AMD, React, Angular JS,  etc.) to save you time. It creates \nan OSGi bundle for you, extracts all npm dependencies, and transpiles your code \nfor the Liferay AMD Loader. \n\nWhile developing JavaScript applications, you may need to access \n@product@-specific information or web services. The `Liferay` global JavaScript \nObject \n[exposes this information for you](/docs/7-2/reference/-/knowledge_base/r/liferay-javascript-apis), \nto use in your JavaScript applications. \n\n## Lexicon and Clay\n\nLiferay uses its own design language, called \n[Lexicon](https://liferay.design/lexicon), to provide a common framework \nfor building consistent UIs and user experiences across the Liferay product \necosystem. The web implementation of Lexicon (CSS, JS, and HTML) is called \n[Clay](https://clayui.com/). \nIt is automatically available to application developers through a set of CSS \nclasses or our \n[tag library](/docs/7-2/reference/-/knowledge_base/r/using-the-clay-taglib-in-your-portlets). \n\n## Templates\n\nFor templating, you can use Java EE's JSP, FreeMarker, or whatever else you \nlike. \n\n## Themes\n\nThemes use the standard components (CSS, JavaScript, and HTML) along with \nFreeMarker templates. Although the default themes are nice, you may wish to \ncreate your own look and feel for your site. The \n[Liferay JS Theme Toolkit](https://github.com/liferay/liferay-themes-sdk/tree/master/packages) \nhas all the tools you need to create and develop themes, but you can use the \ntools you prefer.\n\nFrom the \n[Theme Builder Gradle Plugin](/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin), \nto the \n[Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme), \nto \n[Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli)'s \n[Theme Template](/docs/7-2/reference/-/knowledge_base/r/theme-template), you \ncan choose the development tools you like best, so you can focus on creating \na well designed theme. \n\n## Front-End Extensions\n\n@product@'s modularity has many benefits for the front-end developer, in the \nform of development customizations and extension points. These extensions assure \nthe stability, conformity, and future evolution of your applications. \n\nBelow are some of the available front-end extensions:\n\n- [Theme Contributors](/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site)\n- [Context Contributors](/docs/7-2/frameworks/-/knowledge_base/f/injecting-additional-context-variables-and-functionality-into-your-theme-templates)\n- [Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/dynamic-includes)\n\nSee \n[Theme Components](/docs/7-2/customization/-/knowledge_base/c/theme-components) \nand \n[Understanding the Page Structure](/docs/7-2/customization/-/knowledge_base/c/understanding-the-page-structure) \nfor more information. \n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/00-themes-intro.markdown",
    "content": "---\nheader-id: themes-introduction\n---\n\n# Themes\n\n[TOC levels=1-4]\n\nThemes customize the default look and feel of your site. You can inject your own\nflavor and personality and represent the visual identity of your brand or\ncompany. \n\nYou'll learn these things:\n\n- [Developing Themes](/docs/7-2/frameworks/-/knowledge_base/f/developing-themes): \n  Learn how to use @product@'s tools and features to develop your theme.\n- [Extending Themes](/docs/7-2/frameworks/-/knowledge_base/f/extending-themes): \n  Learn how to use @product@'s theme extension mechanisms and features to add to \n  your theme.\n\nThemes use the standard components (CSS, JS, and HTML) along with FreeMarker\ntemplates for rendering. There are several \n[default FreeMarker templates](/docs/7-2/customization/-/knowledge_base/c/theme-components#theme-templates)\nthat each handle a key piece of functionality for the page. There are also \n[theme template utilities](/docs/7-2/customization/-/knowledge_base/c/theme-components#theme-template-utilities)\nthat let you use portlets, taglibs, theme objects, and more in your theme\ntemplates. \n[CSS extensions and patterns](/docs/7-2/customization/-/knowledge_base/c/theme-components#css-frameworks-and-extensions)\ncome out-of-the-box, and support SASS, and multiple JavaScript frameworks.\nSeveral mechanisms are available for customizing, developing, and extending\nthemes. \n\n## Theme Workflow\n\nThemes are built on top of one of the following base themes: \n\n- **Unstyled:** provides basic markup, functions, and images \n- **Styled:** inherits from the Unstyled base theme and adds some styling on top\n\nThemes can be built with your choice of tooling. Liferay also offers its own set\nof tools to get your themes up and running quickly. \n\nThe following Liferay tools help you build themes:\n\n- [Theme Builder Gradle Plugin](/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin)\n- [Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme)\n- [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli)'s \n  [Theme Template](/docs/7-2/reference/-/knowledge_base/r/theme-template). \n  <!--Uncomment once article is available\n  - Dev Studio\n  -->\n\nDepending on the tool you choose \n(\n  [Theme Generator](/docs/7-2/reference/-/knowledge_base/r/theme-generator), \n  [Gradle](/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin), \n  [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/theme-template), \n  [Maven](/docs/7-2/reference/-/knowledge_base/r/theme-template), \n  or \n  [Dev Studio](/docs/7-2/reference/-/knowledge_base/r/theme-template)\n), \nthe theme anatomy can be different. The overall development process is the \nsame: \n\n1.  Mirror the structure of the files you want to modify. Most of the time,\n    you'll modify these files:\n\n    - `portal_normal.ftl`: main theme markup\n    - `_custom.scss`: custom CSS styling\n    - `main.js`: the theme's JavaScript\n\n2.  Build and deploy the theme.\n\n3.  Apply the theme \n    [through the Look and Feel menu](/docs/7-2/user/-/knowledge_base/u/page-set-look-and-feel) \n    by selecting your \n    [theme's thumbnail](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-thumbnail-preview-for-your-theme). \n\nThe finished theme is bundled as a WAR (Web application ARchive) file. \n\n| **Note:** While developing your theme, you should enable\n| [Developer Mode](/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes).\n| This disables the JavaScript minifier and caching for CSS and FreeMarker\n| template files, which makes debugging easier.\n\nIf you've built your theme with the Liferay Theme Generator, you can use some\nhelpful Gulp tasks to streamline the process: \n\n- **build:** builds your theme's files based on the specified base theme. \n  See the \n  [gulp build tutorial](/docs/7-2/frameworks/-/knowledge_base/f/building-your-themes-files) \n  for more information.\n- **extend:** sets the base theme or themelet to extend. See the \n  [gulp extend tutorial](/docs/7-2/frameworks/-/knowledge_base/f/changing-your-base-theme) \n  for more information.\n- **init:** specifies the app server to deploy your theme to (automatically run\n  during the initial creation of the theme). See the \n  [gulp init tutorial](/docs/7-2/frameworks/-/knowledge_base/f/updating-your-themes-app-server)\n  for more information. \n- **kickstart:** copies files from an existing theme into your theme to help \n  kickstart it. See the \n  [gulp kickstart tutorial](/docs/7-2/frameworks/-/knowledge_base/f/copying-an-existing-themes-files) \n  for more information.\n- **status:** lists the base theme/themelets that your theme extends. See the \n  [gulp status tutorial](/docs/7-2/frameworks/-/knowledge_base/f/listing-your-themes-extensions) \n  for more information.\n- **watch:** watches for changes to your theme's files and automatically deploys \n  them to the server when a change is made. See the \n  [gulp watch tutorial](/docs/7-2/frameworks/-/knowledge_base/f/automatically-deploying-theme-changes) \n  for more information.\n\nSee \n[Theme Components](/docs/7-2/customization/-/knowledge_base/c/theme-components) \nand \n[Understanding the Page Structure](/docs/7-2/customization/-/knowledge_base/c/understanding-the-page-structure) \nto get a top-level overview of how themes work. \n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/03-developing-themes/01-developing-themes-intro.markdown",
    "content": "---\nheader-id: developing-themes\n---\n\n# Developing Themes\n\n[TOC levels=1-4]\n\nTheme projects created using the \n[Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/theme-generator) \nhave access to several \n[gulp](https://www.npmjs.com/package/gulp) \ntasks you can execute to manage and develop your theme. This section covers the \navailable actions that these tasks provide, as well as other information you may \nfind useful while developing your theme. \n\nThis section covers these topics:\n\n- Using liferay theme tasks (build, deploy, extend, init, kickstart, status, and watch)\n- Using Developer Mode\n- Creating thumbnail previews for your theme\n- Creating color schemes for your theme\n- Making configurable theme settings\n\nWhile developing your theme, you may notice that your theme's CSS and JS files \nare minified. This optimizes performance, but it can make debugging difficult \nduring development. Developer Mode, disabled by default, optimizes development \ninstead. For instance, it loads CSS and JS files individually for easier \ndebugging. Also, you don't have to reboot the server as often in Developer Mode. \nHere is a list of Developer Mode's key behavior changes and the \n[Portal Property](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html)\noverride settings that trigger them (if applicable):\n\n- CSS files are loaded individually rather than being combined and loaded as a\n  single CSS file (`theme.css.fast.load=false`).\n- Layout template caching is disabled (`layout.template.cache.enabled=false`).\n- The server does not launch a browser when starting (`browser.launcher.url=`).\n- FreeMarker Templates for themes and web content are not cached, so changes\n  are applied immediately (via the system setting in your @product@ instance).\n- Minification of CSS and JavaScript resources is disabled\n  (`minifier.enabled=false`).\n\nIndividual file loading of your styling and behaviors, combined with disabled\ncaching for layout and FreeMarker templates, lets you see your changes more \nquickly. These developer settings are defined in the \n[`portal-developer.properties` file](https://github.com/liferay/liferay-portal/blob/7.2.x/portal-impl/src/portal-developer.properties). \nSee \n[Using Developer Mode with Themes](/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes) \nto learn how to enable Developer Mode in your app server. You can also use the \n[Gulp Watch task](/docs/7-2/frameworks/-/knowledge_base/f/automatically-deploying-theme-changes) \nto test theme changes on a proxy port before deploying your theme to your \nserver. \n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/03-developing-themes/02-using-developer-mode-with-themes.markdown",
    "content": "---\nheader-id: using-developer-mode-with-themes\n---\n\n# Using Developer Mode with Themes\n\n[TOC levels=1-4]\n\nThis article shows how to enable Developer Mode in your app server manually and \nthrough @ide@, as well as how to configure other settings that may benefit you \nduring development. Each topic is explained in the relevant section below. \n\n## Enabling Developer Mode Manually\n\nFollow these steps to enabled Developer Mode in your app server manually:\n\n1.  Create a `portal-ext.properties` file in your server's root folder if it \n    doesn't exist.\n    \n2.  Add the line below to it:\n\n    ```properties\n    include-and-override=portal-developer.properties\n    ```\n    \n    Alternatively, add the properties from the \n    [portal-developer.properties](https://github.com/liferay/liferay-portal/blob/7.2.x/portal-impl/src/portal-developer.properties) \n    file to your `portal-ext.properties` file that you want to use.\n    \n3.  Start your app server to apply the changes.\n\nRead the next section to learn how to enable Developer Mode in @ide@. \n\n## Setting Developer Mode in @ide@\n\nFollow these steps to enable Developer Mode for your app server in @ide@:\n \n1.  Double-click on your server in the *Servers* window and open the \n    *Liferay Launch* section.\n \n2.  Select *Custom Launch Settings* and check the *Use developer mode* option. \n\n3.  Save the changes and start your server. \n\n![Figure 1: The *Use developer mode* option lets you enable Developer Mode for your server in @ide@.](../../../../images/developer-mode-ide.png)\n\n| **Warning:** Only change the Server settings from the runtime environment's \n| Liferay Launch section.\n\n## Configuring FreeMarker System Settings\n\nBy default, FreeMarker theme templates and web content templates are cached. You \ncan change this behavior through System Settings with the steps below:\n\n1.  Open the Control Panel and go to *Configuration* &rarr; *System Settings*.\n\n2.  Select *Template Engines* under the *PLATFORM* heading.\n\n3.  By default, the *Resource modification check* (the time in milliseconds that\n    the template is cached) is set to `60000`. Set this value to `0` to disable\n    caching.\n\nYour FreeMarker templates are ready for development. Next you can learn how to \nimprove JavaScript file loading for development. \n\n## JavaScript Fast Loading\n\nBy default, JavaScript fast loading is enabled in Developer Mode \n(`javascript.fast.load=true`). This loads the packed version of files listed in \nthe \n[Portal Properties](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#JavaScript) \n`javascript.barebone.files` or `javascript.everything.files`. You can, however, \ndisable JavaScript fast loading for easier debugging for development. Just set \n`javascript.fast.load` to `false` in your `portal.properties`, or you can \ndisable fast loading by setting the URL parameter `js_fast_load` to `0`.\n\n| **Note:** JavaScript fast loading is retrieved from one of three places: the \n| request (determined by the current URL: \n| `http://localhost:8080/web/guest/home?js_fast_load=1`(on) or \n| `...?js_fast_load=0`(off)), the Session, or the Portal Property \n| (`javascript.fast.load=true`). Preference is given in the order of request, \n| session, and then Portal Properties. This lets you change `js_fast_load`'s \n| value from the default in `portal.properties` without having to manually \n| re-enter `js_fast_load` into the URL upon every new page load.\n\nGreat! You've set up your server for Developer Mode. Now, when you modify your \ntheme's file directly in your bundle, you can see your changes applied \nimmediately on redeploying your theme!\n\n## Related Topics\n\n- [Generating Layout Templates with the Theme Generator](/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/03-developing-themes/03-building-themes.markdown",
    "content": "---\nheader-id: building-your-themes-files\n---\n\n# Building Your Theme's Files\n\n[TOC levels=1-4]\n\nFollow these steps to build your theme's files with the Build task. Note that \nthis task only works for themes that use the \n[liferay JS Theme Toolkit](https://github.com/liferay/liferay-themes-sdk/tree/master/packages), \nsuch as those created with the \n[Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme).\n\n| **Note:** Gulp is included as a local dependency in generated themes, so you \n| are not required to install it. It can be accessed by running \n| `node_modules\\.bin\\gulp` followed by the Gulp task from a generated theme's \n| root folder.\n\n1.  Navigate to your theme's root folder and run `gulp build`.\n\n2.  The `gulp build` task generates the base theme files (in the `build` folder), \n    compiles Sass into CSS, and compresses all theme files into a `.war` file \n    (in the `dist` folder), that you can deploy to your server. Copy any of \n    these files and folders to your theme's `src` folder to modify them. \n\n3.  [Deploy](/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes) \n    the `war` file to your app server to make it available.\n    \n![Figure 1: Run the `gulp build` task to build your theme's files.](../../../../images/theme-dev-building-themes-gulp-build.png)\n\n## Related Topics\n\n- [Automatically Deploying Theme Changes](/docs/7-2/frameworks/-/knowledge_base/f/automatically-deploying-theme-changes)\n- [Copying an Existing Theme's Files](/docs/7-2/frameworks/-/knowledge_base/f/copying-an-existing-themes-files)\n- [Deploying and Applying Themes](/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/03-developing-themes/04-deploying-and-applying-themes.markdown",
    "content": "---\nheader-id: deploying-and-applying-themes\n---\n\n# Deploying and Applying Themes\n\n[TOC levels=1-4]\n\nFollow these steps to deploy your theme with the Deploy task. Note that this \ntask only works for themes that use the \n[liferay JS Theme Toolkit](https://github.com/liferay/liferay-themes-sdk/tree/master/packages), \nsuch as those created with the \n[Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme).\n\n| **Note:** Gulp is included as a local dependency in generated themes, so you \n| are not required to install it. It can be accessed by running \n| `node_modules\\.bin\\gulp` followed by the Gulp task from a generated theme's \n| root folder.\n\nFollow these steps to deploy your theme:\n\n1.  Navigate to your theme's root folder and run `gulp deploy`.\n\n    | **Note:** If you're running the \n    | [Felix Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell), \n    | you can also deploy your theme using the `gulp deploy:gogo` command.\n\n2.  Your server's log displays that the OSGi bundle is started.\n\n    ![Figure 1: Your server's log notifies you when the theme's bundle has started.](../../../../images/theme-dev-deploying-themes-server-log.png)\n\n3.  Apply your theme through the *Build* &rarr; *Pages* menu in the Control \n    Menu. Select the *Configure* option for your site pages, and click the \n    *Change Current Theme* button to apply your theme. \n\nWonderful! Your theme is deployed to your server and applied to your site. \n\n## Related Topics\n\n- [Automatically Deploying Theme Changes](/docs/7-2/frameworks/-/knowledge_base/f/automatically-deploying-theme-changes)\n- [Copying an Existing Theme's Files](/docs/7-2/frameworks/-/knowledge_base/f/copying-an-existing-themes-files)\n- [Creating Themelets with the Theme Generator](/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator)\n\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/03-developing-themes/07-configuring-your-themes-app-server.markdown",
    "content": "---\nheader-id: updating-your-themes-app-server\n---\n\n# Updating Your Theme's App Server\n\n[TOC levels=1-4]\n\nFollow these steps to update the configuration for your theme's app server with \nthe Init task. Note that this task only works for themes that use the \n[liferay JS Theme Toolkit](https://github.com/liferay/liferay-themes-sdk/tree/master/packages), \nsuch as those created with the \n[Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme).\n\n| **Note:** Gulp is included as a local dependency in generated themes, so you \n| are not required to install it. It can be accessed by running \n| `node_modules\\.bin\\gulp` followed by the Gulp task from a generated theme's \n| root folder.\n\n1.  Navigate to your theme's root folder and run `gulp init`.\n\n    ![Figure 1: Run the `gulp init` task to update your app server configuration.](../../../../images/theme-dev-server-configuration-gulp-init.png)\n\n2.  Enter the updated path to your app server and site.\n\n3.  Your theme's `liferay-theme.json` file contains the updated server \n    configuration information:\n\n    ```json\n    {\n      \"LiferayTheme\": {\n        \"deploymentStrategy\": \"LocalAppServer\",\n        \"appServerPath\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0\\\\liferay-ce-portal-7.2.0\\\\tomcat-9.0.10\",\n        \"deployPath\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0\\\\liferay-ce-portal-7.2.0\\\\deploy\",\n        \"url\": \"http://localhost:8080\",\n        \"appServerPathPlugin\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0\\\\liferay-ce-portal-7.2.0\\\\tomcat-9.0.10\\\\webapps\\\\my-new72theme-theme\",\n        \"deployed\": true,\n        \"pluginName\": \"my-new72theme-theme\"\n      }\n    }\n    ```\n\nAwesome! Now you can \n[deploy your theme](/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes) \nto the proper server. \n\n## Related Topics\n\n- [Automatically Deploying Theme Changes](/docs/7-2/frameworks/-/knowledge_base/f/automatically-deploying-theme-changes)\n- [Changing Your Base Theme](/docs/7-2/frameworks/-/knowledge_base/f/changing-your-base-theme)\n- [Listing Your Theme's Extensions](/docs/7-2/frameworks/-/knowledge_base/f/listing-your-themes-extensions)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/03-developing-themes/09-automatically-deploying-theme-changes.markdown",
    "content": "---\nheader-id: automatically-deploying-theme-changes\n---\n\n# Automatically Deploying Theme Changes\n\n[TOC levels=1-4]\n\nFollow these steps to automatically preview your theme's changes with the Watch \ntask. Note that this task only works for themes that use the \n[liferay JS Theme Toolkit](https://github.com/liferay/liferay-themes-sdk/tree/master/packages), \nsuch as those created with the \n[Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme). \n\n| **Note:** Gulp is included as a local dependency in generated themes, so you \n| are not required to install it. It can be accessed by running \n| `node_modules\\.bin\\gulp` followed by the Gulp task from a generated theme's \n| root folder.\n\n1.  Navigate to your theme's root folder and run `gulp watch`. This sets up a \n    proxy for your app server and opens it in a new window in the browser. \n\n    | **Note:** Live changes are only viewable on port `9080` \n    | (`http://localhost:9080`). Live changes **are not viewable** on your app \n    | server (e.g. `http://localhost:8080`).\n\n    ![Figure 1: Run the `gulp watch` task to automatically deploy any changes to your theme.](../../../../images/theme-dev-watching-themes-gulp-watch-startup.png)\n\n2.  Make a change to your theme and save the file. The updated files are built, \n    compiled, and copied directly to the proxy port (e.g. `9080`). CSS changes \n    are deployed live, so no page reload is needed.\n    \n3.  Once you're happy with the changes, \n    [re-deploy](/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes) \n    your theme to apply the changes to your site on your app server.\n\n    ![Figure 2: The watch task notifies you that the changes are deployed.](../../../../images/theme-dev-watching-themes-gulp-watch-auto-deploy.png)\n\n## Related Topics\n\n- [Configuring Your Theme's App Server](/docs/7-2/frameworks/-/knowledge_base/f/updating-your-themes-app-server)\n- [Copying an Existing Theme's Files](/docs/7-2/frameworks/-/knowledge_base/f/copying-an-existing-themes-files)\n- [Deploying and Applying Themes](/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/03-developing-themes/10-creating-a-thumbnail-preview-for-your-theme.markdown",
    "content": "---\nheader-id: creating-a-thumbnail-preview-for-your-theme\n---\n\n# Creating a Thumbnail Preview for Your Theme\n\n[TOC levels=1-4]\n\nWhen you apply a theme to your site pages, you have to choose from the list of\navailable themes in the site selector. The only identification for each theme is\nthe theme's name, along with a small thumbnail preview image that gives a brief\nimpression of the theme. This is even more important when developing color\nschemes for a theme, since names are not displayed for color schemes.\n\nThis article shows how to create a thumbnail preview for your theme so users can\nidentify it.\n\n![Figure 1: Your theme thumbnail is displayed with the rest of the available themes.](../../../../images/theme-dev-theme-thumbnail-default.png)\n\nYour first step in creating a thumbnail preview for your theme is taking a\nscreenshot of your theme. Once you have a screenshot that you like, follow the\nsteps below to create a thumbnail preview for your theme:\n\n1.  Resize the screenshot to 270 pixels high by 480 pixels wide. Your thumbnail\n    *must be* these exact dimensions to display properly.\n\n2.  Save the image as a `.png` file named `thumbnail.png` and place it in the\n    theme's `src/images` folder (create this folder if it doesn't already\n    exist).\n\n    | **Note:** The\n    | [Theme Builder Gradle plugin](/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin)\n    | doesn't recognize a `thumbnail.png` file. If you're using this plugin to\n    | build your theme instead, you must create a `screenshot.png` file in your\n    | theme's `images` folder that is 1080 pixels high by 864 pixels wide. The\n    | thumbnail is automatically generated from the screenshot for you when the\n    | theme is built.\n\n3.  Redeploy the theme. The  file is displayed as the theme's\n    thumbnail.\n\n![Figure 2: Your theme thumbnail is displayed with the rest of the available themes.](../../../../images/theme-dev-theme-thumbnail-custom.png)\n\n## Related Topics\n\n- [Installing the Theme Generator and Creating a Theme](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme)\n- [Creating Themelets with the Theme Generator](/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator)\n- [Creating Color Schemes for Your Theme](/docs/7-2/frameworks/-/knowledge_base/f/creating-color-schemes-for-your-theme)"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/03-developing-themes/11-creating-color-schemes-for-your-theme.markdown",
    "content": "---\nheader-id: creating-color-schemes-for-your-theme\n---\n\n# Creating Color Schemes for Your Theme\n\n[TOC levels=1-4]\n\nColor schemes give your theme additional color palettes. With just a small \namount of changes to your theme's CSS, you can subtly change the look of your \ntheme, while maintaining the same design and feel to it. \n\n![Figure 1: Color schemes give administrators some choices for your theme's look.](../../../../images/theme-dev-color-schemes.png)\n\nFollow these steps to create color schemes for your theme:\n\n1.  Open the theme's `WEB-INF/liferay-look-and-feel.xml` file and follow the \n    pattern below to add the default color scheme. If your default styles are in \n    `_custom.scss`, use the `default` `<css-class>` as shown in the example \n    below. See the \n    [liferay-look-and-feel DTD](@platform-ref@/7.2-latest/definitions/liferay-look-and-feel_7_2_0.dtd.html#color-scheme)\n    for an explanation of each of the elements used below:\n\n    ```xml\n    <theme id=\"my-theme-id\" name=\"My Theme Name\">\n       <color-scheme id=\"01\" name=\"My Default Color Scheme Name\">\n           <default-cs>true</default-cs>\n           <css-class>default</css-class>\n           \n           <color-scheme-images-path>\n               ${images-path}/my_color_schemes_folder_name/${css-class}\n           </color-scheme-images-path>\n       </color-scheme>\n       ...\n    </theme>\n     ```\n\n    | **Note:** Color schemes are sorted alphabetically by `name` rather than \n    | `id`. For example, a color scheme named `Clouds` and `id` `02` would be \n    | selected by default over a color scheme named `Day` with `id` `01`. The \n    | `<default-cs>` element overrides the alphabetical sorting and sets the \n    | color scheme that is selected by default when the theme is chosen.  \n \n2.  Add the remaining color schemes below the default color scheme, using the \n    pattern below. Note that the IDs, names, and CSS classes must be unique for \n    each color scheme.\n \n    ```xml\n    <color-scheme id=\"id-number\" name=\"Color Scheme Name\">\n       <css-class>color-scheme-css-class</css-class>\n    </color-scheme>\n    ```\n    An example `liferay-look-and-feel.xml` configuration is shown below:\n\n    ```xml\n    <look-and-feel>\n    \t<compatibility>\n    \t\t<version>7.2.0+</version>\n    \t</compatibility>\n    \t<theme id=\"my-great-theme\" name=\"My Great Theme\">\n    \t\t<template-extension>ftl</template-extension>\n    \t\t<color-scheme id=\"01\" name=\"Default\">\n    \t\t\t\t<default-cs>true</default-cs>\n    \t\t\t\t<css-class>default</css-class>\n    \t\t\t\t<color-scheme-images-path>\n    \t\t\t\t\t\t${images-path}/color_schemes/${css-class}\n    \t\t\t\t</color-scheme-images-path>\n    \t\t</color-scheme>\n    \t\t<color-scheme id=\"02\" name=\"Dark\">\n    \t\t\t<css-class>dark</css-class>\n    \t\t</color-scheme>\n    \t\t<color-scheme id=\"03\" name=\"Light\">\n    \t\t\t<css-class>light</css-class>\n    \t\t</color-scheme>\n    \t\t<portlet-decorator ...>\n    \t\t\t...\n    \t</theme>\n    </look-and-feel>\n    ```\n\n3.  Create a folder for your color schemes (`color_schemes` for example) in the \n    theme's `css` folder, and add a `.scss` file to it for each color scheme \n    your theme supports, excluding the default color scheme since those styles \n    are included in `_custom.scss`. \n \n4.  The color scheme class is added to the theme's `<body>` element when the \n    color scheme is applied, so add the class to the color scheme's styles to \n    target the proper color scheme. The example below specifies styles for a \n    color scheme with the class `day`: \n\n    ```css\n    body.day { background-color: #DDF; }\n    .day a { color: #66A; }\n    ```\n\n5.  Import the color scheme `.scss` files into the theme's `_custom.scss` file. \n    The example below imports `_day.scss` and `_night.scss` files:\n\n    ```css\n    @import \"color_schemes/day\";\n    @import \"color_schemes/night\";\n    ```\n\n6.  Create a folder for each color scheme in your theme's `images` folder, and \n    add \n    [a thumbnail preview](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-thumbnail-preview-for-your-theme) \n    for them. The folder name *must match* the color scheme's CSS class name.\n\nThere you have it. Now you can go color scheme crazy with your themes!\n\n## Related Topics\n\n- [Generating Layout Templates](/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator)\n- [Creating a Thumbnail Preview for Your Theme](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-thumbnail-preview-for-your-theme)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/03-developing-themes/12-making-configurable-theme-settings.markdown",
    "content": "---\nheader-id: making-configurable-theme-settings\n---\n\n# Making Configurable Theme Settings\n\n[TOC levels=1-4]\n\nIf you have an aspect of a theme that you want an Administrator to configure \nwithout having to manually update and redeploy the theme, you can create a \n*theme setting* for it. Theme settings are very versatile and can be customized \nto meet your needs. \n\n![Figure 1: Here are examples of configurable settings for the site Admin.](../../../../images/theme-dev-configurable-theme-settings.png)\n\nFollow the steps below to create theme settings:\n\n1.  Open your theme's `WEB-INF/liferay-look-and-feel.xml` file, and follow the \n    pattern below to nest a `<setting/>` element inside the parent `<settings>` \n    element for each setting you want to add:\n\n    ```xml\n    <look-and-feel>\n    \t<compatibility>\n    \t\t<version>7.2.0+</version>\n    \t</compatibility>\n    \t<theme id=\"your-theme-name\" name=\"Your Theme Name\">\n    \t\t<template-extension>ftl</template-extension>\n        <settings>\n          <setting configurable=\"true\" key=\"theme-setting-key\"\n          options=\"true,false\" type=\"select\" value=\"true\" />\n          <setting configurable=\"true\" key=\"theme-setting-key\"\n          type=\"text\" value=\"My placeholder text\" />\n        </settings>\n        <portlet-decorator>\n          portlet decorators...\n    \t\t</portlet-decorator>\n    \t</theme>\n    </look-and-feel>\n    ```\n\n    The example below adds a text input setting for a custom hex code:\n    \n    ```xml\n    <settings>\n      <setting configurable=\"true\" key=\"my-hex-code\" type=\"text\" value=\"blue\" />\n    </settings>\n    ```\n\n    See the `liferay-look-and-feel.xml`'s \n    [DTD docs](@platform-ref@/7.2-latest/definitions/liferay-look-and-feel_7_2_0.dtd.html#settings) \n    for an explanation of the setting's configuration options. \n\n    | **Note:** You can modify theme settings with JavaScript to provide a more \n    | custom experience. The example below modifies the theme setting, changing \n    | its `type` to `color`, to provide a color picker for the user:  \n    |\n    | ```xml\n    | <setting configurable=\"true\" key=\"user-color\"\n    | type=\"text\" value=\"#993300\"  \n    | >\n    | <![CDATA[  \n    |      AUI().ready('node',function(A) {\n    |           A.one(\"#[@NAMESPACE@]user-color\").setAttribute(\"type\", \"color\");    \n    |           A.one(\"#[@NAMESPACE@]user-color\").setAttribute(\"style\", \"height: 35px; width: 200px\");    \n    |       });\n    | ]]>\n    | </setting>\n    | ```\n\n2.  Create a file called `init_custom.ftl` in your theme's `templates` folder if \n    it doesn't already exist, and follow the patterns in the table below to \n    define your theme setting variables in it:\n\n    | Return Type | Description | Pattern |\n    | --- | --- | --- |\n    | Boolean | a select box with the options `true` and `false` or a checkbox with values `yes` and `no` | `<#assign my_variable_name = getterUtil.getBoolean(themeDisplay.getThemeSetting(\"theme-setting-key\"))/>` |\n    | String | a text input or text area input | `<#assign my_variable_name = getterUtil.getString(themeDisplay.getThemeSetting(\"theme-setting-key\"))/>` |\n \n    The example below adds a custom hex code setting:\n\n        <#assign my_hex_code = \n        getterUtil.getString(themeDisplay.getThemeSetting(\"my-hex-code\"))/>\n\n3.  Add your theme setting variables to the theme template. The example below \n    prints `my_hex-code`'s value as the value of the header's `style` attribute:\n\n    `portal_normal.ftl`:\n\n    ```html\n    <header style=\"background-color:${my_hex_code}\">\n    ```\n\n4.  [Deploy the theme](/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes) \n    to apply the changes. To set the theme setting for a Public or Private page \n    set, click the *Gear icon* next to the page set you want to configure and \n    update the setting under the *Look and Feel* tab. Alternatively, you can set \n    the theme setting for an individual page by opening the *Actions menu* next \n    to the page and selecting *Configure* and choosing the \n    *Define a Specific look and feel for this page* option. \n\nGreat! You've created configurable settings for your theme. \n\n## Related Topics\n\n- [Creating Themelets with the Theme Generator](/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator)\n- [Listing Your Theme's Extensions](/docs/7-2/frameworks/-/knowledge_base/f/listing-your-themes-extensions)\n- [Importing Resources with a Theme](/docs/7-2/frameworks/-/knowledge_base/f/importing-resources-with-a-theme)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/03-developing-themes/13-using-font-awesome-glyph-icons-in-your-theme.markdown",
    "content": "---\nheader-id: using-font-awesome-and-glyph-icons-in-your-theme\n---\n\n# Using Font Awesome and Glyph Icons in Your Theme\n\n[TOC levels=1-4]\n\nBy default, [Font Awesome v3.2.1](https://fontawesome.com/v3.2.1/) \nand [Bootstrap 3 Glyphicons](https://getbootstrap.com/docs/3.3/components/) \nare enabled globally in @product@ via a system setting. This means that you can \nuse them in your themes to create social media links, for example. A Site \nAdministrator can disable this to improve performance, if they choose. \n\n## Disabling Enabling Global Font Awesome and Glyphicons in Portal\n\nSince Liferay DXP Fix Pack 2 and Liferay Portal 7.2 CE GA2, Font Awesome is \navailable globally as a system setting, which is enabled by default. You can \ndisable this setting to improve performance. To update the setting, follow these \nsteps:\n\n1.  Open the Control Menu and navigate to *Control Panel* &rarr; \n    *Configuration* &rarr; *System Settings* and select *Third Party* under the \n    *PLATFORM* heading. \n    \n2.  Select *Font Awesome* under the *System Scope* and check/uncheck the \n    *Enable Font Awesome* checkbox to enable/disable Font Awesome icons and \n    Glyphicons across the site.\n    \n3.  Click *Save* to save the configuration.\n\n## Including Font Awesome and Glyphicons in Your Theme\n\nAs a safeguard, you should include Font Awesome and Glyphicons with your theme \nif you want to use them. This ensures that your icons won't break if the global \nsystem setting is disabled. If you created the theme with the Liferay Theme \nGenerator and answered yes (y) to the Font Awesome prompt, the Font Awesome \ndependency is added which includes Font Awesome and Glyphicons for you. If you \ndidn't include Font Awesome and Glyphicons when you initially created the theme, \nfollow these steps to include them in your theme now:\n\n1.  Use the Font Awesome v3.2.1 or Bootstrap 3 Glyphicons in your theme's \n    template. The example below uses Font Awesome icons:\n\n    ```html    \n    <div id=\"social-media-links\">\n      <ul class=\"nav flex-row mx-auto\">\n          <li class=\"mx-2\">\n              <div id=\"facebook\">\n                <a class=\"icon-facebook icon-3x text-white\"\n                href=\"http://www.facebook.com/pages/Liferay/45119213107\" \n                target=\"_blank\"><span class=\"hide\">Facebook</span>\n                </a>\n              </div>\n          </li>\n          <li class=\"mx-2\">\n              <div id=\"twitter\">\n                <a class=\"icon-twitter icon-3x text-white\" \n                href=\"http://www.twitter.com/liferay\" \n                target=\"_blank\"><span class=\"hide\">Twitter</span>\n                </a>\n              </div>\n          </li>\n          <li class=\"mx-2\">\n              <div id=\"linked-in\">\n                <a class=\"icon-linkedin icon-3x text-white\"\n                href=\"http://www.linkedin.com/company/83609\" \n                target=\"_blank\"><span class=\"hide\">LinkedIn</span>\n                </a>\n              </div>\n          </li>\n          <li class=\"mx-2\">\n              <div id=\"youtube\">\n                <a class=\"icon-youtube icon-3x text-white\" \n                href=\"http://www.youtube.com/user/liferayinc\" \n                target=\"_blank\"><span class=\"hide\">YouTube</span>\n                </a>\n              </div>\n          </li>\n          <li class=\"mx-2\">\n              <div id=\"google-plus\">\n                <a class=\"icon-google-plus icon-3x text-white\"\n                href=\"https://plus.google.com/+liferay/posts\" \n                target=\"_blank\"><span class=\"hide\">Google</span>\n                </a>\n              </div>\n          </li>\n      </ul>\n    </div>\n    ```\n\n2.  Open the theme's `package.json` and include the `liferay-font-awesome` \n    dev dependency:\n\n    ```json\n    \"liferay-font-awesome\": \"3.4.0\"\n    ```\n\n3.  Run `gulp deploy` from the theme's root folder to build and deploy the \n    theme's files. This adds a `/css/font/` folder to the theme's `build` folder \n    that contains the Font Awesome and Glyphicon fonts. \n    \n## Related Topics\n\n- [Installing the Theme Generator and Creating a Theme](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme)\n- [Upgrading Themes](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-theme-to-7-2)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/04-extending-themes/01-extending-themes-intro.markdown",
    "content": "---\nheader-id: extending-themes\n---\n\n# Extending Themes\n\n[TOC levels=1-4]\n\n@product@ has additional features that you can use to extend your theme and \nmodify your site. This section covers these topics:\n\n- Installing Themelets\n- Injecting additional context variables into your theme templates\n- Packaging independent UI resources for your site\n- Copying an existing theme's files\n- Listing your theme's extensions\n- Overwriting and extending liferay theme tasks\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/04-extending-themes/02-installing-themelets-in-your-theme.markdown",
    "content": "---\nheader-id: installing-a-themelet-in-your-theme\n---\n\n# Installing a Themelet in Your Theme\n\n[TOC levels=1-4]\n\nAfter you've created your \n[themelet](/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator), \nfollow the steps below to install it into your theme.\n\n| **Note:** Gulp is included as a local dependency in generated themes, so you \n| are not required to install it. It can be accessed by running \n| `node_modules\\.bin\\gulp` followed by the Gulp task from a generated theme's \n| root folder.\n\n1.  Navigate to your theme's root folder and run `gulp extend`.\n\n2.  Choose *Themelet* as the theme asset to extend.\n\n3.  Select *Search globally installed npm modules*, *Search npm registry*, or \n    *Specify a package URL* to locate the themelet.\n\n    ![Figure 1: You can extend your theme using globally installed npm modules or published npm modules.](../../../../images/install-themelet.png)\n\n    | **Note:** You can retrieve the URL for a package by running \n    | `npm show package-name dist.tarball`. \n\n4.  Highlight your themelet, press spacebar to activate it, and press *Enter* to\n    install it. \n   \nGreat, now you know how to install a themelet in your theme! The next time you \n[deploy](/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes) \nyour theme, the themelet will be bundled along with it. \n\n## Related Topics\n\n-[Generating Themelets with the Theme Generator](/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator)\n-[Injecting Additional Context Variables and Functionality into Your Theme Templates](/docs/7-2/frameworks/-/knowledge_base/f/injecting-additional-context-variables-and-functionality-into-your-theme-templates)\n-[Packaging Independent UI Resources for Your Site](/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/04-extending-themes/03-injecting-additional-context-variables-into-your-templates.markdown",
    "content": "---\nheader-id: injecting-additional-context-variables-and-functionality-into-your-theme-templates\n---\n\n# Injecting Additional Context Variables and Functionality into Your Theme Templates\n\n[TOC levels=1-4]\n\nJSPs are native to Java EE and therefore have access to all the contextual \nobjects inherit to the platform, like the request and session. Through these \nobjects, developers can obtain @product@-specific context information by \naccessing container objects like `themeDisplay` or `serviceContext`. This, \nhowever, is not the case for FreeMarker templates. To access this information in \nFreeMarker templates, you must inject it yourself into the template's context. \n@product@ gives you a head start by injecting several common objects into the \ntemplate's context and exposing them as \n[FreeMarker macros](/docs/7-2/reference/-/knowledge_base/r/product-freemarker-macros). \nTo inject other objects into the FreeMarker template's context, you must create \na *Context Contributor*. \n\nYou can create a Context Contributor to use non-JSP templating languages for \nthemes, widget templates, and any other templates used in @product@. For example, suppose \nyou want your theme to change color based on the user's organization. You could \ncreate a Context Contributor to inject the user's organization into your theme's \ncontext, and then determine the theme's color based on that information. \n\nFollow the steps below to create a context contributor:\n\n1.  Create an OSGi module using your favorite third party tool, or use \n    [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli).\n\n2.  Create a component class that implements the `TemplateContextContributor` \n    service, and set the `type` property to the type of context you're injecting \n    into. Set it to `TYPE_THEME` to inject context-specific variables for your \n    theme, or set it to `TYPE_GLOBAL` to inject it into every context execution \n    in @product@, like themes, widget templates, DDM templates, etc, as defined in \n    [TemplateContextContributor](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/template/TemplateContextContributor.html). \n    To follow naming conventions, begin the class name with the entity you want \n    to inject context-specific variables for, followed by \n    *TemplateContextContributor* (e.g., `ProductMenuTemplateContextContributor`):\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\"type=\" + TemplateContextContributor.TYPE_THEME},\n        service = TemplateContextContributor.class\n    )\n    ```\n\n3.  Implement the \n    [TemplateContextContributor](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/template/TemplateContextContributor.html) \n    interface in your `*TemplateContextContributor` class, and overwrite the \n    `prepare(Map<String,Object>, HttpServletRequest)` method to inject new or \n    modified variables into the `contextObjects` map. This is your template's \n    context that was described earlier. \n\nThe `ProductMenuTemplateContextContributor`'s class is shown as an example \nbelow. It overwrites the `prepare(...)` method to inject a modified \n`bodyCssClass` variable and a new `liferay_product_menu_state` variable into the \ntheme context for the Product Menu. Specifically, the `cssClass` variable \nprovides styling for the Product Menu and the `productMenuState` variable \ndetermines whether the visible Product Menu should be open or closed:\n\n```java\n@Override\npublic void prepare(\n    Map<String, Object> contextObjects, HttpServletRequest request) {\n\n    if (!isShowProductMenu(request)) {\n        return;\n    }\n\n    String cssClass = GetterUtil.getString(\n        contextObjects.get(\"bodyCssClass\"));\n    String productMenuState = SessionClicks.get(\n        request,\n        ProductNavigationProductMenuWebKeys.\n            PRODUCT_NAVIGATION_PRODUCT_MENU_STATE,\n        \"closed\");\n\n    contextObjects.put(\n        \"bodyCssClass\", cssClass + StringPool.SPACE + productMenuState);\n\n    contextObjects.put(\"liferay_product_menu_state\", productMenuState);\n}\n```\n\nThe `ProductMenuTemplateContextContributor` provides an easy way to inject \nvariables into @product@'s theme directly related to the Product Menu. You can \ndo the same with your custom context contributor. With the power to inject \nadditional variables into any context in @product@, you're free to fully harness \nthe power of your chosen templating language. \n\n## Related Topics\n\n- [Developing Themes](/docs/7-2/frameworks/-/knowledge_base/f/developing-themes)\n- [Theme Contributors](/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/04-extending-themes/04-packaging-independent-ui-resources-for-your-site.markdown",
    "content": "---\nheader-id: packaging-independent-ui-resources-for-your-site\n---\n\n# Packaging Independent UI Resources for Your Site\n\n[TOC levels=1-4]\n\nIf you want to package UI resources independent of a specific theme and include \nthem on every page, a *Theme Contributor* is your best option. If, instead, you \nwant to include separate UI resources on a page that are attached to a theme, \nuse \n[themelets](/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator).\n\nA Theme Contributor is a module that contains CSS and JS resources to apply to \nthe page. The Control Menu, Product Menu, and Simulation Panel are packaged as \nTheme Contributors. \n\n![Figure 1: The Control Menu, Product Menu, and Simulation Panel are packaged as Theme Contributor modules.](../../../../images/theme-contributor-menus-diagram.png)\n\nIf you want to edit or style these standard UI components, you must create a \nTheme Contributor and add your modifications on top. You can also add new UI \ncomponents to @product@ by creating a Theme Contributor. This article shows how \nto create a Theme Contributor module. \n\nFollow these steps to create a Theme Contributor:\n\n1.  Create a generic OSGi module using your favorite third party tool, or use \n    [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli). You can \n    also use the \n    [Blade Template](/docs/7-2/reference/-/knowledge_base/r/theme-contributor-template) \n    to create your module, in which case you can skip step 2. \n\n2.  Add the `Liferay-Theme-Contributor-Type` header to your module's `bnd.bnd` \n    file to identify your module as a Theme Contributor, and add the \n    `Web-ContextPath` header to set the context from which the Theme \n    Contributor's resources are hosted. See the \n    [Control Menu module's](https://search.maven.org/search?q=a:com.liferay.product.navigation.control.menu.theme.contributor) \n    `bnd.bnd` below as an example:\n\n    ```properties\n    Bundle-Name: Liferay Product Navigation Product Menu Theme Contributor\n    Bundle-SymbolicName: com.liferay.product.navigation.product.menu.theme.contributor\n    Bundle-Version: 3.0.4\n    Liferay-Theme-Contributor-Type: product-navigation-product-menu\n    Web-ContextPath: /product-navigation-product-menu-theme-contributor\n    ```\n\n    The Theme Contributor type helps @product@ better identify your module. If \n    you're creating a Theme Contributor to override an existing Theme \n    Contributor, you should try to use the same type to maximize compatibility \n    with future developments. \n\n3.  Add the `Liferay-Theme-Contributor-Weight` to your `bnd.bnd` file to set a \n    priority for your Theme Contributor. To override another Theme Contributor's \n    styles, such as those for the Control Menu, set a higher weight. The higher \n    the value, the higher the priority. If your Theme Contributor has a weight \n    of 100, it will be loaded after one with a weight of 99, allowing your CSS \n    to override theirs:\n\n    ```properties\n    Liferay-Theme-Contributor-Weight: [value]\n    ```\n\n4.  Create a `src/main/resources/META-INF/resources` folder in your module and \n    place your resources (CSS and JS) in that folder.\n\n5.  Build and deploy your module to see your modifications applied to @product@ \n    pages and themes.\n\nThat's all you need to do to create a Theme Contributor for your site. Remember, \nwith great power comes great responsibility, so use Theme Contributors wisely. \nThe UI contributions affect every page and aren't affected by theme deployments. \n\n## Related Topics\n\n- [Developing Themes](/docs/7-2/frameworks/-/knowledge_base/f/developing-themes)\n- [Generating Themelets](/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator)\n- [Installing a Themelet](/docs/7-2/frameworks/-/knowledge_base/f/installing-a-themelet-in-your-theme)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/04-extending-themes/05-changing-the-base-theme.markdown",
    "content": "---\nheader-id: changing-your-base-theme\n---\n\n# Changing Your Base Theme\n\n[TOC levels=1-4]\n\nFollow these steps to change your theme's base theme with the Extend task. Note \nthat this task only works for themes that use the \n[liferay JS Theme Toolkit](https://github.com/liferay/liferay-themes-sdk/tree/master/packages), \nsuch as those created with the \n[Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme). \n\n| **Note:** Gulp is included as a local dependency in generated themes, so you \n| are not required to install it. It can be accessed by running \n| `node_modules\\.bin\\gulp` followed by the Gulp task from a generated theme's \n| root folder.\n\n1.  Navigate to your theme's root folder and run `gulp extend`. \n\n    ![Figure 1: Run the `gulp extend` task to change your base theme.](../../../../images/theme-ext-changing-base-themes-gulp-extend-base-theme.png)\n    \n2.  Enter 1 to select a new base theme to extend. \n\n3.  By default, themes created with the \n    [Liferay Theme Generator](https://github.com/liferay/generator-liferay-theme) \n    are based off of the \n    [styled theme](https://www.npmjs.com/package/liferay-theme-styled). You can \n    extend the styled or unstyled base theme, a globally installed theme, a \n    theme published on the npm registry, or you can specify a package URL. \n    Enter the number for the option you wish to select. \n\n    | **Note:** You can retrieve the URL for a package by running \n    | `npm show package-name dist.tarball`. \n\n    ![Figure 2: You can extend the styled or unstyled base theme, a globally installed theme, a theme published to the npm registry, or you can specify a package URL.](../../../../images/theme-ext-changing-base-themes-gulp-extend-base-theme-choice.png)\n\n    | **Note:** The Classic theme is an implementation of an existing base theme \n    | and is therefore not meant to be extended. Extending Liferay's Classic \n    | theme is strongly discouraged.\n\nYour theme's `package.json` contains the updated base theme configuration:\n\n```json\n\"liferayTheme\": {\n  \"baseTheme\": \"styled\",\n  \"screenshot\": \"\",\n  \"templateLanguage\": \"ftl\",\n  \"version\": \"7.2\"\n},\n```\n\nGreat! You've updated your base theme. When you \n[build your theme's files](/docs/7-2/frameworks/-/knowledge_base/f/building-your-themes-files) \nor \n[deploy it](/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes), \nyour theme will inherit the updated base theme's files. \n\n## Related Topics\n\n- [Configuring Your Theme's App Server](/docs/7-2/frameworks/-/knowledge_base/f/updating-your-themes-app-server)\n- [Deploying and Applying Themes](/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes)\n- [Listing Your Theme's Extensions](/docs/7-2/frameworks/-/knowledge_base/f/listing-your-themes-extensions)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/04-extending-themes/06-copying-an-existing-themes-files.markdown",
    "content": "---\nheader-id: copying-an-existing-themes-files\n---\n\n# Copying an Existing Theme's Files\n\n[TOC levels=1-4]\n\nFollow these steps to copy an existing theme's files with the Kickstart task. \nUnlike extending a base theme, which is a dynamic inheritance that applies your \n`src` files on top of the base theme on every build, the Kickstart task is a one \ntime inheritance. \n\n| **Warning:** The gulp kickstart task copies an existing theme's files into \n| your own, which can potentially overwrite files with the same name. \n| Proceed with caution. \n\nNote that this task only works for themes that use the \n[liferay JS Theme Toolkit](https://github.com/liferay/liferay-themes-sdk/tree/master/packages), \nsuch as those created with the \n[Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme). \n\n| **Note:** Gulp is included as a local dependency in generated themes, so you \n| are not required to install it. It can be accessed by running \n| `node_modules\\.bin\\gulp` followed by the Gulp task from a generated theme's \n| root folder.\n\n1.  Navigate to your theme's root folder and run `gulp kickstart`.\n\n    ![Figure 1: Run the `gulp kickstart` task to copy a theme's files into your own theme.](../../../../images/theme-ext-kickstarting-themes-gulp-kickstart.png)\n\n2.  Select where to search for the theme to copy. You can copy files from \n    globally installed themes or themes published on the npm registry.\n    \n    | **Note:** **You can't kickstart the Classic Theme.**\n    \n    | **Note:** To globally install a theme, run the `npm link` command from the \n    | theme's root folder.\n\n    ![Figure 2: You can copy files from  globally installed themes.](../../../../images/theme-ext-kickstarting-themes-global-theme.png)\n\n3.  The theme's files are copied into your own theme, jump starting development. \n    Add your changes on top of these files.\n    \nCongrats! Now you have a head start to developing your theme. \n \n## Related Topics\n\n- [Building Your Theme's files](/docs/7-2/frameworks/-/knowledge_base/f/building-your-themes-files)\n- [Generating Themelets with the Theme Generator](/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator)\n- [Deploying and Applying Themes](/docs/7-2/frameworks/-/knowledge_base/f/deploying-and-applying-themes)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/04-extending-themes/07-listing-your-themes-extensions.markdown",
    "content": "---\nheader-id: listing-your-themes-extensions\n---\n\n# Listing Your Theme's Extensions\n\n[TOC levels=1-4]\n\nDo you need to know what base theme/themelet(s) your theme extends? Follow these \nsteps to list your theme's extensions with the Status task. Note that this task \nonly works for themes that use the \n[liferay JS Theme Toolkit](https://github.com/liferay/liferay-themes-sdk/tree/master/packages), \nsuch as those created with the \n[Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme).\n\n| **Note:** Gulp is included as a local dependency in generated themes, so you \n| are not required to install it. It can be accessed by running \n| `node_modules\\.bin\\gulp` followed by the Gulp task from a generated theme's \n| root folder.\n\n1.  Navigate to your theme's root folder.\n\n2.  Run `gulp status` to print your theme's current extensions to the command \n    line.\n\nYour theme's current extensions are also found under the `baseTheme` and \n`themeletDependencies` headings in your theme's `package.json`.\n\n![Figure 1: Run the `gulp status` task to list your theme's current extensions.](../../../../images/theme-ext-listing-theme-extensions.png)\n\n## Related Topics\n\n- [Changing Your Base Theme](/docs/7-2/frameworks/-/knowledge_base/f/changing-your-base-theme)\n- [Configuring Your Theme's App Server](/docs/7-2/frameworks/-/knowledge_base/f/updating-your-themes-app-server)\n- [Generating Themelets with the Theme Generator](/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/04-extending-themes/08-overwriting-and-extending-liferay-theme-tasks.markdown",
    "content": "---\nheader-id: overwriting-and-extending-liferay-theme-tasks\n---\n\n# Overwriting and Extending Liferay Theme Tasks\n\n[TOC levels=1-4]\n\nThemes created with the Liferay Theme Generator have access to several default \ngulp theme tasks that provide the standard features required to develop and \nbuild your theme (build, deploy, watch, etc.). You may, however, want to run \nadditional processes on your theme's files prior to deploying the theme to the \nserver---such as minifying your JavaScript files. The Liferay Theme Generator's \nAPIs expose a `hookFn` property that lets you hook into the default gulp theme \ntasks to inject your own logic. \n\nFollow these steps to hook into the default Liferay theme tasks: \n\n1.  Identify the gulp task or sub task that you want to hook into or overwrite. \n    The tasks and their sub tasks are listed in their `[task-name].js` file in \n    the \n    [`tasks` folder](https://github.com/liferay/liferay-js-themes-toolkit/tree/master/packages/liferay-theme-tasks/tasks) \n    of the \n    [`liferay-theme-tasks`](https://github.com/liferay/liferay-js-themes-toolkit/tree/master/packages/liferay-theme-tasks) \n    package. For example, the gulp `build` task and sub tasks are defined in the \n    [`build.js` file](https://github.com/liferay/liferay-js-themes-toolkit/blob/master/packages/liferay-theme-tasks/tasks/build.js#L73-L92): \n\n    ```javascript\n    gulp.task('build', function(cb) {\n    \trunSequence(\n    \t\t'build:clean',\n    \t\t'build:base',\n    \t\t'build:src',\n    \t\t'build:web-inf',\n    \t\t'build:liferay-look-and-feel',\n    \t\t'build:hook',\n    \t\t'build:themelets',\n    \t\t'build:rename-css-dir',\n    \t\t'build:compile-css',\n    \t\t'build:fix-url-functions',\n    \t\t'build:move-compiled-css',\n    \t\t'build:remove-old-css-dir',\n    \t\t'build:fix-at-directives',\n    \t\t'build:r2',\n    \t\t'build:war',\n    \t\tcb\n    \t);\n    });\n    ```\n\n2.  Open your theme's `gulpfile.js` file and locate the \n    `liferayThemeTasks.registerTasks()` method. This method registers the \n    default gulp theme tasks. Add the `hookFn` property to the `registerTasks()` \n    method's configuration object, making sure to pass in the `gulp` instance:\n\n    ```javascript\n    liferayThemeTasks.registerTasks({\n      gulp: gulp,\n      hookFn: function(gulp) {\n        \n      }\n    });\n    ```\n\n3.  Inside the `hookFn()` function, use the `gulp.hook()` method to specify the \n    theme task or sub task that you want to hook into. You can inject your code \n    before or after a task by prefixing it with the `before:` or `after:` \n    keywords. Alternatively, you can use the `gulp.task()` method to overwrite \n    a gulp task. Both methods have two parameters: the task or sub task you want \n    to hook into and a callback function that invokes `done` or returns a stream \n    with the logic that you want to inject. A few example configuration patterns \n    are shown below:\n\n    ```javascript\n    liferayThemeTasks.registerTasks({\n      gulp: gulp,\n      hookFn: function(gulp) {\n        gulp.hook('before:build:src', function(done) {\n          // Fires before build:src task\n        });\n\n        gulp.hook('after:build', function(done) {\n          // Fires after build task\n        });\n\n        gulp.task('build:base', function(done) {\n          // Overwrites build:base task\n        });\n      }\n    });\n    ```\n\nThe example below fires before the `build:war` sub-task and reads the JavaScript \nfiles in the theme's `build` folder, minifies them with the `gulp-uglify` \nmodule, places them back in the `./build/js` folder, invokes `done`, and finally \nlogs that the JavaScript was minified. To follow along, replace your theme's \n`gulpfile.js` with the contents shown below, install the \n[gulp-uglify](https://www.npmjs.com/package/gulp-uglify) \nmodule and the \n[fancy-log](https://www.npmjs.com/package/fancy-log) \nmodule, and run `gulp deploy`:\n\n```javascript\n'use strict';\n\nvar gulp = require('gulp');\nvar log = require('fancy-log');\nvar uglify = require('gulp-uglify');\nvar liferayThemeTasks = require('liferay-theme-tasks');\n\nliferayThemeTasks.registerTasks({\n   gulp: gulp,\n   hookFn: function(gulp) {\n     gulp.hook('before:build:war', function(done) {\n      // Fires before build `war` task\n      gulp.src('./build/js/*.js')\n      .pipe(uglify())\n      .pipe(gulp.dest('./build/js'))\n      .on('end', done);\n      log('Your JS is now minified...');\n      });\n   }\n});\n```\n\nYou should see something similar to the output shown below:\n\n```bash\n[15:58:07] Finished 'build:r2' after 198 ms\n[15:58:07] Starting 'build:war'...\n[15:58:07] Your JS is now minified...\n[15:58:07] Starting 'plugin:version'...\n[15:58:07] Finished 'plugin:version' after 2.52 ms\n```\n\n| **Note:** The `hook` callback function must invoke the `done` argument or \n| return a stream.\n\nNow you know how to hook into and overwrite the default Liferay theme tasks! \n\n## Related Topics\n\n- [Installing the Theme Generator and Creating a Theme](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme)\n- [Generating Themelets](/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator)\n- [Using Developer Mode with Themes](/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/05-clay-and-themes/01-clay-themes-intro.markdown",
    "content": "---\nheader-id: clay-css-and-themes\n---\n\n# Clay CSS and Themes\n\n[TOC levels=1-4]\n\n[Lexicon](https://liferay.design/lexicon/) is a design language that provides a \ncommon framework for building consistent UIs. [Clay](https://clayui.com/docs/css-framework/scss.html), \nthe web implementation of Lexicon, is an extension of Bootstrap's open source \nCSS Framework. Bootstrap is by far the most popular CSS framework on the web. \nBuilt with Sass, Clay CSS fills the front-end gaps between Bootstrap and the \nspecific needs of @product@. \n\nBootstrap features have been extended to cover more use cases. Here are some of \nthe new components added by Clay CSS: \n\n- Aspect Ratio\n- Cards\n- Dropdown Wide and Dropdown Full\n- Figures\n- Nameplates\n- Sidebar / Sidenav\n- Stickers\n- SVG Icons\n- Timelines\n- Toggles\n\nSeveral reusable CSS patterns have also been added to help accomplish time \nconsuming tasks such as these:\n\n- truncating text\n- content filling the remaining container width\n- truncating text inside table cells\n- table cells filling remaining container width and table cells only being as \n  wide as their content\n- open and close icons inside collapsible panels\n- nested vertical navigations\n- slide out panels\n- notification icons/messages\n- vertical alignment of content\n\n[Clay CSS](https://clayui.com/) is bundled with two sub-themes: [Clay Base](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-styled/src/main/resources/META-INF/resources/_styled/css/clay) \nand [Atlas](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-styled/src/main/resources/META-INF/resources/_styled/css/clay/atlas). \nClay Base is @product@'s Bootstrap API extension. It adds all the features and \ncomponents you need and inherits Bootstrap's styles. As a result, Clay Base is \nfully compatible with [third party themes](/docs/7-2/frameworks/-/knowledge_base/f/integrating-third-party-themes-with-clay) \nthat leverage Bootstrap's Sass variable API. \n\nAtlas is @product@'s custom Bootstrap theme that is used in the Classic Theme. \nIts purpose is to overwrite and manipulate Bootstrap and Clay Base to create its \nclassic look and feel. Atlas is equivalent to installing a Bootstrap third party \ntheme.\n\n| **Note:** It is not recommended to integrate third party themes with Atlas, as\n| it adds variables and styles that are outside the scope of Bootstrap's API.\n\nThis section covers these topics:\n\n- Customizing the Atlas and Clay base themes\n- Integrating third party themes with Clay\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/05-clay-and-themes/02-customizing-atlas-and-clay-themes.markdown",
    "content": "---\nheader-id: customizing-atlas-and-clay-base-themes\n---\n\n# Customizing Atlas and Clay Base Themes in @product@\n\n[TOC levels=1-4]\n\nWhether you're customizing the Atlas or Clay base theme, the process is, for the \nmost part, the same. Follow these steps. If you're customizing the Clay base \ntheme, skip to step 3. \n\n1.  By default, Clay base is imported into the theme. If you're overwriting \n    Atlas, add a file named `clay.scss` to your theme's `/src/css/` folder and \n    import `clay/atlas` instead:\n\n    ```scss\n    @import \"clay/atlas\";\n    ```\n\n2.  By default, Clay base variables are imported into the theme. If you're \n    overwriting Atlas, add an `_imports.scss` file to your theme's `/src/css/` \n    folder and import Atlas variables instead:\n\n    ```scss\n    @import \"bourbon\";\n\n    @import \"mixins\";\n\n    @import \"compat/mixins\";\n\n    @import \"clay/atlas-variables\";\n    ```\n\n    | **Note:** Bourbon mixins are deprecated as of @product-ver@ and will be \n    | removed in the next major release. We recommend you use Clay mixins \n    | instead. To use Clay mixins, follow the instructions in \n    | [Using Clay Mixins in Your Theme](/docs/7-2/frameworks/-/knowledge_base/f/using-clay-mixins-in-your-theme)\n\n3.  Add a file named `_clay_variables.scss`. Place your Atlas, Bootstrap, and \n    Clay Base variable modifications in this file.\n\nGreat! Now you know how to customize the Atlas and Clay base themes. \n\n## Related Topics\n\n- [Integrating Third Party Themes with Clay](/docs/7-2/frameworks/-/knowledge_base/f/integrating-third-party-themes-with-clay)\n- [Using Clay Mixins in Your Theme](/docs/7-2/frameworks/-/knowledge_base/f/using-clay-mixins-in-your-theme)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/05-clay-and-themes/03-integrating-third-party-themes-with-clay.markdown",
    "content": "---\nheader-id: integrating-third-party-themes-with-clay\n---\n\n# Integrating Third Party Themes with Clay\n\n[TOC levels=1-4]\n\n[Clay Base](https://github.com/liferay/liferay-portal/tree/7.1.x/modules/apps/frontend-theme/frontend-theme-styled/src/main/resources/META-INF/resources/_styled/css/clay) \nprovides all the features and components your theme needs and inherits \nBootstrap's styles. As a result, Clay Base is fully compatible with third party \nthemes that leverage Bootstrap's Sass variable API. \n\nThe \n[Styled Theme](https://github.com/liferay/liferay-portal/tree/7.1.x/modules/apps/frontend-theme/frontend-theme-styled) \nuses Clay Base to provide its styles and components. Therefore, as a best \npractice, you should use the Styled base theme to integrate third party themes. \n\n| **Note:** You can purchase third party themes from the\n| [Liferay Marketplace](https://web.liferay.com/marketplace). Third party themes\n| must be built with Sass to be compatible. **Make sure** Sass files are included\n| before making any theme purchase.\n\nFollow these steps to integrate a third party theme with Clay Base:\n\n1.  Create a new theme with the Styled Theme as its base. This is the default \n    base theme for newly created themes, so no further action is required. This \n    provides the Clay Base files you need. \n\n2.  In the theme's `/src/css/` folder, add a file named `_clay_variables.scss`. \n    Place your Atlas, Bootstrap, and Clay Base variable modifications in this \n    file.\n\n3.  Create a folder inside `/src/css/` to house your third party theme \n    (e.g. `/src/css/my-third-party-theme/`)\n\n4.  Copy the CSS contents of the theme to the folder you just created.\n\n5.  In `_clay_variables.scss`, import the file containing the theme variables. \n    For example, `@import \"my-third-party-theme/variables.scss\";`\n \n    | **Note:** You may omit the leading underscore when importing Sass files.\n\n6.  In `_custom.scss`, import the file containing the CSS. For example, \n    `@import \"my-third-party-theme/main.scss\";`\n\nNow you know how to integrate third party themes with Clay Base! \n\n## Related Topics\n\n- [Customizing Atlas and Clay Base Themes](/docs/7-2/frameworks/-/knowledge_base/f/customizing-atlas-and-clay-base-themes)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/05-clay-and-themes/04-using-clay-icons-in-your-theme.markdown",
    "content": "---\nheader-id: using-clay-icons-in-a-theme\n---\n\n# Using Clay Icons in a Theme\n\n[TOC levels=1-4]\n\nTo use Clay icons in your themes, you must use the [clay taglib macro](/docs/7-2/reference/-/knowledge_base/r/freemarker-taglib-macros). \nIf you want to use Clay icons in your portlets, follow the steps in the \n[Clay taglib icons](/docs/7-2/reference/-/knowledge_base/r/clay-icons) article. \nTo use Clay icons in your theme, follow these steps:\n\n1.  Open the FreeMarker theme template you want to use the Clay icon in.\n\n2.  Use the `@clay[\"icon\"]` macro and specify the icon with the `symbol` \n    attribute, as shown in the pattern below:\n\n    ```markup\n    <@clay[\"icon\"] symbol=\"icon-name\" />\n    ```\n    \n    The full list of icons can be found on [ClayUI's site](https://clayui.com/docs/components/icon.html) \n    (CSS/Markup tab). Here is an example configuration for a Facebook social \n    media icon:\n\n    ```markup\n        <a class=\"text-white\"\n        href=\"http://www.facebook.com/pages/Liferay/45119213107\" \n        target=\"_blank\">\n          <span class=\"hide\">Facebook</span>\n          <@clay[\"icon\"] symbol=\"social-facebook\" />\n        </a>\n    ```\n    \nGreat! Now you know how to use Clay icons in your theme. \n\n## Related Topics\n\n- [FreeMarker Taglib Macros](/docs/7-2/reference/-/knowledge_base/r/freemarker-taglib-macros)\n- [Clay Icons](/docs/7-2/reference/-/knowledge_base/r/clay-icons)\n- [Customizing Atlas and Clay Base Themes](/docs/7-2/frameworks/-/knowledge_base/f/customizing-atlas-and-clay-base-themes)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/05-clay-and-themes/04-using-clay-mixins-in-your-theme.markdown",
    "content": "---\nheader-id: using-clay-mixins-in-your-theme\n---\n\n# Using Clay Mixins in Your Theme\n\n[TOC levels=1-4]\n\nBourbon mixins are deprecated as of @product-ver@ and will be removed in the \nnext major release. We recommend you use Clay mixins instead. Follow these steps \nto use Clay mixins in your theme:\n\n1.  Add the `clay-css` dependency to the theme's `package.json`:\n\n    ```json\n    \"dependencies\":{\n      \"clay-css\": \"^2.18.0\",\n    }\n    ```\n\n2.  Delete `_imports.scss` if you modified it.\n\n3.  Import the library into the theme's `main.scss` file:\n\n    ```scss\n    @import 'node_modules/clay-css/src/scss/atlas-variables'\n    ```\n\n    or import the base-variables if you want to use Clay Base instead:\n\n    ```scss\n    @import 'node_modules/clay-css/src/scss/base-variables'\n    ```\n    \nGreat! Now you know how to use Clay mixins in your theme! \n\n## Related Topics\n\n- [Customizing Atlas and Clay Base Theme](/docs/7-2/frameworks/-/knowledge_base/f/customizing-atlas-and-clay-base-themes)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/06-portlets-and-themes/01-theming-portlets-intro.markdown",
    "content": "---\nheader-id: theming-portlets\n---\n\n# Theming Portlets\n\n[TOC levels=1-4]\n\nAlthough you can individually style a portlet via the theme's CSS or the \nportlet's [Look and Feel Configuration](/docs/7-2/user/-/knowledge_base/u/look-and-feel-configuration) \nmenu, you may want to modify the default look and feel for all portlets in your \nsite. A portlet's template--its container, CSS classes, and overall HTML \nMarkup--is defined via the theme's `portlet.ftl` file. To provide a custom style \nfor all portlets, use the CSS classes in this file for the various container \nelements along with the portlet decorators to achieve the desired look and feel. \nBe cautious: changes to `portlet.ftl` affect all the portlets in your site when \nthe theme is applied. \n\nTo help you with your bearings as you modify your portlet's template, below is \na quick look at the \n[`portlet.ftl`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-theme/frontend-theme-classic/src/templates/portlet.ftl) \nfile that's included in @product-ver@'s Classic theme. \n\n```markup\n<#assign\n    portlet_display = portletDisplay\n    portlet_back_url = htmlUtil.escapeHREF(portlet_display.getURLBack())\n    portlet_content_css_class = \"portlet-content\"\n    portlet_display_name = htmlUtil.escape(portlet_display.getPortletDisplayName())\n    portlet_display_root_portlet_id = htmlUtil.escapeAttribute(portlet_display.getRootPortletId())\n    portlet_id = htmlUtil.escapeAttribute(portlet_display.getId())\n    portlet_title = htmlUtil.escape(portlet_display.getTitle())\n/>\n```\n\nThese variables are described in the table below:\n\n**Portlet FTL Variables**\n\n| Variable | Description |\n| --- | --- |\n| `portletDisplay` | Fetched from the `themeDisplay` object, contains information about the portlet |\n| `portlet_back_url` | URL to return to the previous page when the portlet `WindowState` is maximized |\n| `portlet_display_name` | The \"friendly\" name of the portlet as displayed in the GUI |\n| `portlet_display_root_portlet_id` | The root portlet ID of the portlet |\n| `portlet_id` | The ID of the portlet (not the same as the portlet namespace) |\n| `portlet_title` | The portlet name set in the portlet Java class (usually from a `Keys.java` class) |\n\nNext, a condition checks if the portlet header should be displayed. If the \nportlet has a portlet toolbar (Configuration, Permissions, Look and Feel), the \ncondition is true and the header is displayed:\n\n```markup\n<#if portlet_display.isPortletDecorate() && !portlet_display.isStateMax() \n&& portlet_display.getPortletConfigurationIconMenu()?? \n&& portlet_display.getPortletToolbar()??>\n```\n\nYou can use a similar pattern if you want to dynamically show portions of the \nportlet's UI. \n\nNext, the portlet title menus are defined. These are used in portlets that let \nyou add resources (Web Content Display, Media Gallery, Documents and Media):\n\n```markup\nportlet_title_menus = portlet_toolbar.getPortletTitleMenus(portlet_display_root_portlet_id, renderRequest, renderResponse)\n```\n\nThe configuration below contains the information for the configuration menu \n(Configuration, Permissions, Look and Feel):\n\n```markup\nportlet_configuration_icons = portlet_configuration_icon_menu.getPortletConfigurationIcons(portlet_display_root_portlet_id, renderRequest, renderResponse)\n```\n\nThe rest of the file contains the HTML markup for the portlet topper and the \nportlet content. This section barely scratches the surface of the `portlet.ftl` \nfile. You must examine the `portlet.ftl` file yourself and determine what \nelements and classes need updated for your theme and site. \n\nNow that you are more familiar with your theme's `portlet.ftl` file, you can \nlearn the role Portlet Decorators play in the overall look and feel of your \nportlets. \n\n## Portlet Decorators\n\nPortlet Decorators modify the style of the application wrapper. Themes come \nbundled with three default portlet decorators, defined in \n`liferay-look-and-feel.xml`:\n\n-   Decorate: this is the default Application Decorator when using the Classic \n    theme. It wraps the application in a white box with a border, and displays\n    the title at the top. \n\n    ![Figure 1: The Classic theme's Decorate Application Decorator wraps the portlet in a white box.](../../../../images/application-decorator-decorate.png)\n\n-   Borderless: this decorator shows the title at the top, but does not display\n    a wrapping box. \n\n    ![Figure 2: The Classic theme's Borderless Application Decorator displays the application's custom title.](../../../../images/application-decorator-borderless.png)\n\n-   Barebone: this decorator displays the bare application content, showing \n    neither the wrapping box nor the custom application title. \n\n    ![Figure 3: The Classic theme's Barebone Application Decorator displays only the application's content.](../../../../images/application-decorator-barebone.png)\n\n| **Note:** Upgrading to @product@ assigns the *borderless* decorator\n| automatically to those portlets that had the *Show Borders* option set to false\n| in previous versions of Liferay.\n\nThis section covers these topics:\n\n- Embedding Portlets in Themes\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/06-portlets-and-themes/02-embedding-portlets-in-themes/01-embedding-portlets-in-themes-intro.markdown",
    "content": "---\nheader-id: embedding-portlets-in-themes\n---\n\n# Embedding Portlets in Themes\n\n[TOC levels=1-4]\n\nYou may occasionally want to embed a portlet in a theme, making the portlet \nvisible on all pages where the theme is used. Since there are numerous drawbacks \nto hard-coding a specific portlet into place, the *Portlet Providers* framework \noffers an alternative that displays the appropriate portlet based on a given \nentity type and action. \n\nThe first thing you should do is open the template file for which you want to \ndeclare an embedded portlet. For example, the `portal_normal.ftl` template file \nis a popular place to declare embedded portlets. To avoid problems, it's usually \nbest to embed portlets with an entity type and action, but you may encounter \ncircumstances where you'll want to hard code it by portlet name. Both methods \nare covered in this section. These topics are covered:\n\n- Embedding a portlet by entity type and action\n- Embedding a portlet by instance name and ID\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/06-portlets-and-themes/02-embedding-portlets-in-themes/02-embedding-portlets-in-themes-by-entity-type.markdown",
    "content": "---\nheader-id: embedding-portlets-in-themes-by-entity-type-and-action\n---\n\n# Embedding Portlets in Themes by Entity Type and Action\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to declare an entity type and action in a \ncustom theme, and you'll create a module that finds the correct portlet to use \nbased on those given parameters. Follow these steps:\n\n1.  Insert a declaration where you want the portlet embedded. This declaration \n    expects two parameters: the type of action and the class name of the entity \n    type the portlet should handle. An example configuration is shown below:\n\n    ```markup\n    <@liferay_portlet[\"runtime\"]\n        portletProviderAction=portletProviderAction.VIEW\n        portletProviderClassName=\"com.liferay.portal.kernel.servlet.taglib.ui.LanguageEntry\"\n    />\n    ```\n    This example declares that the theme is requesting to view language entries. \n    @product@ determines which deployed portlet to use in this case by providing \n    the portlet with the highest service ranking. \n\n    | **Note:** In some cases, a default portlet is already provided to fulfill\n    | certain requests. You can override the default portlet with your custom\n    | portlet by specifying a higher service rank. To do this, set the following\n    | property in your class' `@Component` declaration:\n    | \n    |     property= {\"service.ranking:Integer=20\"}\n    | \n    | Make sure you set the service ranking higher than the default portlet being\n    | used.\n\n2.  The Portal is not yet configured to handle this request. You must create a \n    module that can find the portlet that fits the theme's request. \n    [Create a module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project). \n\n3.  Create a unique package name in the module's `src` directory, and create a \n    new Java class in that package. To follow naming conventions, name the class \n    based on the entity type and action type, followed by *PortletProvider* \n    (e.g., `SiteNavigationLanguageEntryViewPortletProvider`). The class should \n    extend the \n    [`BasePortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BasePortletProvider.html) \n    class and implement the appropriate portlet provider interface based on the \n    action you chose in your theme (e.g., \n    [`ViewPortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/ViewPortletProvider.html), \n    [`BrowsePortletProvider`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/BrowsePortletProvider.html), \n    etc.). \n\n4.  Directly above the class's declaration, insert the following annotation:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\"model.class.name=CLASS_NAME\"},\n        service = INTERFACE.class\n    )\n    ```\n\n    The `property` element must match the entity type you specified in your \n    theme declaration (e.g., \n    `com.liferay.portal.kernel.servlet.taglib.ui.LanguageEntry`). Also, your \n    `service` element should match the interface you're implementing (e.g., \n    `ViewPortletProvider.class`).\n\n5.  Specify the methods you want to implement. Make sure to retrieve the portlet \n    ID and page ID that should be provided when this service is called by your \n    theme. \n\n    A common use case is to implement the `getPortletId()` and \n    `getPlid(ThemeDisplay)` methods. Below is an example configuration:\n\n    ```java    \n    @Override\n    public String getPortletName() {\n      return SiteNavigationLanguagePortletKeys.SITE_NAVIGATION_LANGUAGE;\n    }\n\n    @Override\n    public PortletURL getPortletURL(\n        HttpServletRequest httpServletRequest, Group group)\n      throws PortalException {\n\n      return PortletURLFactoryUtil.create(\n        httpServletRequest, getPortletName(), PortletRequest.RENDER_PHASE);\n    }\n\n    /**\n     * @deprecated As of Judson (7.1.x)\n     */\n    @Deprecated\n    @Override\n    protected long getPlid(ThemeDisplay themeDisplay) throws PortalException {\n      return themeDisplay.getPlid();\n    }\n    ```\n\n    This returns the portlet ID and the PLID, which is the ID that uniquely \n    identifies a page used by your theme. By retrieving these, your theme will \n    know which portlet to use, and which page to use it on. \n\n6.  Generate the module's JAR file and copy it to your app server's \n    `osgi/modules/` folder. Once the module is installed and activated in your \n    Portal's service registry, your embedded portlet is available for use \n    wherever your theme is used. \n\nAwesome! You successfully requested a portlet based on the entity and action \ntypes required, and created and deployed a module that retrieves the portlet and \nembeds it in your theme. \n\n## Related Topics\n\n- [Embedding Portlets in Themes](/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes)\n- [Portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets)\n- [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/06-portlets-and-themes/02-embedding-portlets-in-themes/03-embedding-portlets-in-a-theme-by-name.markdown",
    "content": "---\nheader-id: embedding-a-portlet-by-portlet-name\n---\n\n# Embedding a Portlet by Portlet Name\n\n[TOC levels=1-4]\n\nIf you'd like to embed a specific portlet in the theme, you can hard code it by \nproviding its instance ID and name. Follow these steps:\n\n1.  Open the FreeMarker theme template that you want to embed the portlet in. \n    (`portal_normal.ftl` is a good choice.\n\n2.  Add the `liferay_portlet[\"runtime\"]` macro to the template, as shown below. \n    The portlet name **must** be the same as `javax.portlet.name`'s value.\n\n    ```markup\n    <@liferay_portlet[\"runtime\"]\n        instanceId=\"INSTANCE_ID\"\n        portletName=\"PORTLET_NAME\"\n    />\n    ```\n\n| **Note:** If your portlet is instanceable, an instance ID must be provided;\n| otherwise, you can remove this line. To set your portlet to non-instanceable,\n| set the property `com.liferay.portlet.instanceable` in the component annotation\n| of your portlet to `false`.\n \nHere's an example of an embedded portlet declaration that uses the portlet name \nto embed a web content portlet:\n\n```markup\n<@liferay_portlet[\"runtime\"]\n    portletName=\"com_liferay_journal_content_web_portlet_JournalContentPortlet\"\n/>\n```\n\nGreat! Now you know how to embed a portlet in your theme's by their name and ID. \n\n## Related Topics\n\n- [Embedding Portlets in Themes by Entity Type and Action](/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes-by-entity-type-and-action)\n- [Portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets)\n- [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/06-portlets-and-themes/02-embedding-portlets-in-themes/04-setting-portlet-preferences-for-embedded-portlets.markdown",
    "content": "---\nheader-id: setting-default-preferences-for-an-embedded-portlet\n---\n\n# Setting Default Preferences for an Embedded Portlet\n\n[TOC levels=1-4]\n\nFollow these steps to set default portlet preferences for an embedded portlet:\n\n1.  Retrieve portlet preferences using the `freeMarkerPortletPreferences` \n    object. The example below retrieves the `barebone`:\n\n    ```markup\n    <#assign preferences = freeMarkerPortletPreferences.getPreferences(\n      \"portletSetupPortletDecoratorId\", \"barebone\"\n    ) />\n     ```\n \n2.  Set the `defaultPreferences` attribute of the embedded portlet to the \n    `freeMarkerPortletPreferences` object you just configured:\n\n    ```markup\n    <@liferay_portlet[\"runtime\"]\n        defaultPreferences=\"${preferences}\"\n        portletName=\"com_liferay_login_web_portlet_LoginPortlet\"\n    />\n    ```\n\nBelow are some additional attributes you can define for embedded portlets:\n\n| Preference | Description |\n| --- | --- |\n| **defaultPreferences** | A string of Portlet Preferences for the application. This includes look and feel configurations. |\n| **instanceId** | The instance ID for the app, if the application is instanceable. |\n| **persistSettings** | Whether to have an application use its default settings, which will persist across layouts. The default value is *true*. |\n| **settingsScope** | Specifies which settings to use for the application. The default value is `portletInstance`, but it can be set to `group` or `company`. |\n\nNow you know how to set default preferences for embedded portlets! \n\n## Related Topics\n\n- [Embedding Portlets by Name](/docs/7-2/frameworks/-/knowledge_base/f/embedding-a-portlet-by-portlet-name)\n- [Portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets)\n- [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/07-managing-theme-resources/01-resources-importer-intro.markdown",
    "content": "---\nheader-id: importing-resources-with-a-theme\n---\n\n# Importing Resources with a Theme\n\n[TOC levels=1-4]\n\nTo truly appreciate a theme, you must view it with content. Showcasing a theme \nin the proper context is key to communicating the true intentions of its design.\nWho better to do this than the theme's designer? Designers can provide a sample\ncontext that optimizes the design of their themes. The Resources Importer does\nthis for you. \n\n| **Important:** The Resources Importer is deprecated as of @product-ver@ 7.1.\n| \n| \n\nThe Resources Importer module lets theme developers import files and web content\nwith a theme. Administrators can use the site or site template created by the\nResources Importer to showcase the theme. In fact, all standalone themes that\nare uploaded to Liferay Marketplace **must use** the Resources Importer. This\nensures a uniform experience for Marketplace users: a user can download a theme\nfrom Marketplace, install it, go to Sites or Site Templates in the Control Panel\nand immediately see their new theme in action. \n\n## Organizing Your Resources\n\nAdd your resources to the theme's `/src/WEB-INF/src/resources-importer` folder \nas outlined below:\n\n- `[theme-name]/src/WEB-INF/src/resources-importer/`\n    - `sitemap.json` - defines the pages, layout templates, and portlets\n    - `assets.json` - (optional) specifies details on the assets\n    - `document_library/`\n        - `documents/` - contains documents and media files (assets) \n    - `journal/`\n        - `articles/` - contains web content (HTML) and folders grouping web\n          content articles (XML) by template. Each folder name must match the\n          file name of the corresponding template. For example, create folder\n          `Template 1/` to hold an article based on template file \n          `Template 1.ftl`.\n        - `structures/` - contains structures (JSON) and folders of child\n          structures. Each folder name must match the file name of the\n          corresponding parent structure. For example, create folder\n          `Structure 1/` to hold a child of structure file `Structure 1.json`.\n        - `templates/` - groups templates (VM or FTL) into folders by structure.\n          Each folder name must match the file name of the corresponding\n          structure. For example, create folder `Structure 1/` to hold a\n          template for structure file `Structure 1.json`. \n\nUsing the Resources Importer involves the following steps:\n\n- [Preparing and Organizing  Resources](/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer)\n- [Creating a Sitemap for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-sitemap-for-the-resources-importer)\n- [Defining Assets for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/defining-assets-for-the-resources-importer) (optional)\n- [Specifying Where to Import Your Theme's Resources](/docs/7-2/frameworks/-/knowledge_base/f/specifying-where-to-import-your-themes-resources) \n\nThis section explains how to use the Resources Importer to import resources with \nyour theme. \n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/07-managing-theme-resources/02-creating-a-sitemap/01-creating-a-sitemap-intro.markdown",
    "content": "---\nheader-id: creating-a-sitemap-for-the-resources-importer\n---\n\n# Creating a Sitemap for the Resources Importer\n\n[TOC levels=1-4]\n\nYou have two options for specifying resources to be imported with your theme: a \nsitemap or an [archive LAR file](/docs/7-2/frameworks/-/knowledge_base/f/archiving-your-sites-resources). \nUsing a `sitemap.json` file is the most flexible approach, so we recommend it; \nunlike LAR files, a `sitemap.json` can be created in one version of @product@ \nand used in another. LAR files are version-specific, and can only be imported in \nthe same version in which they were created. \n\n| **Important:** The Resources Importer is deprecated as of @product-ver@ 7.1.\n| \n|\n\nThe `sitemap.json` specifies the site pages, layout templates, web content, \nassets, and portlet configurations provided with the theme. This file describes \nthe contents and hierarchy of the site to import as a site or site template. If \nyou're developing themes for Liferay Marketplace, you must use the \n`sitemap.json` to specify resources to be imported with your theme. Even if \nyou're not familiar with JSON, the `sitemap.json` file is easy to understand. An \nexample `sitemap.json` file is shown below:\n\n```json\n{\n    \"layoutTemplateId\": \"2_columns_ii\",\n    \"privatePages\": [\n        {\n            \"friendlyURL\": \"/private-page\",\n            \"name\": \"Private Page\",\n            \"title\": \"Private Page\"\n        }\n    ],\n    \"publicPages\": [\n        {\n            \"columns\": [\n                [\n                    {\n                        \"portletId\": \"com_liferay_login_web_portlet_LoginPortlet\"\n                    },\n                    {\n                        \"portletId\": \n                        \"com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet\"\n                    },\n                    {\n                        \"portletId\": \n                        \"com_liferay_journal_content_web_portlet_JournalContentPortlet\",\n                        \"portletPreferences\": {\n                            \"articleId\": \"Without Border.html\",\n                            \"groupId\": \"${groupId}\",\n                            \"portletSetupPortletDecoratorId\": \"borderless\"\n                        }\n                    },\n                    {\n                        \"portletId\": \"com_liferay_journal_content_web_portlet_JournalContentPortlet\",\n                        \"portletPreferences\": {\n                            \"articleId\": \"Custom Title.html\",\n                            \"groupId\": \"${groupId}\",\n                            \"portletSetupPortletDecoratorId\": \"decorate\",\n                            \"portletSetupTitle_en_US\": \"Web Content Display with Custom Title\",\n                            \"portletSetupUseCustomTitle\": \"true\"\n                        }\n                    }\n                ],\n                [\n                    {\n                        \"portletId\": \"com_liferay_hello_world_web_portlet_HelloWorldPortlet\"\n                    },\n                    {\n                        \"portletId\": \n                        \"com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet_INSTANCE_${groupId}\",\n                        \"portletPreferences\": {\n                            \"displayStyle\": \"[custom]\",\n                            \"headerType\": \"root-layout\",\n                            \"includedLayouts\": \"all\",\n                            \"nestedChildren\": \"1\",\n                            \"rootLayoutLevel\": \"3\",\n                            \"rootLayoutType\": \"relative\"\n                        }\n                    },\n                        \"Web Content with Image.html\",\n                    {\n                        \"portletId\": \"com_liferay_nested_portlets_web_portlet_NestedPortletsPortlet\",\n                        \"portletPreferences\": {\n                            \"columns\": [\n                                [\n                                    {\n                                        \"portletId\": \n                                        \"com_liferay_journal_content_web_portlet_JournalContentPortlet\",\n                                        \"portletPreferences\": {\n                                            \"articleId\": \"Child Web Content 1.xml\",\n                                            \"groupId\": \"${groupId}\",\n                                            \"portletSetupPortletDecoratorId\": \"decorate\",\n                                            \"portletSetupTitle_en_US\": \n                                            \"Web Content Display with Child Structure 1\",\n                                            \"portletSetupUseCustomTitle\": \"true\"\n                                        }\n                                    }\n                                ],\n                                [\n                                    {\n                                        \"portletId\": \n                                        \"com_liferay_journal_content_web_portlet_JournalContentPortlet\",\n                                        \"portletPreferences\": {\n                                            \"articleId\": \"Child Web Content 2.xml\",\n                                            \"groupId\": \"${groupId}\",\n                                            \"portletSetupPortletDecoratorId\": \"decorate\",\n                                            \"portletSetupTitle_en_US\": \n                                            \"Web Content Display with Child Structure 2\",\n                                            \"portletSetupUseCustomTitle\": \"true\"\n                                        }\n                                    }\n                                ]\n                            ],\n                            \"layoutTemplateId\": \"2_columns_i\"\n                        }\n                    }\n                ]\n            ],\n            \"friendlyURL\": \"/home\",\n            \"nameMap\": {\n                \"en_US\": \"Welcome\",\n                \"fr_FR\": \"Bienvenue\"\n            },\n            \"title\": \"Welcome\"\n        },\n        {\n            \"columns\": [\n                [\n                    {\n                        \"portletId\": \"com_liferay_login_web_portlet_LoginPortlet\"\n                    }\n                ],\n                [\n                    {\n                        \"portletId\": \"com_liferay_hello_world_web_portlet_HelloWorldPortlet\"\n                    }\n                ]\n            ],\n            \"friendlyURL\": \"/layout-prototypes-parent-page\", \n            \"layouts\": [\n                {\n                    \"friendlyURL\": \"/layout-prototypes-page-1\",\n                    \"layoutPrototypeLinkEnabled\": \"true\",\n                    \"layoutPrototypeUuid\": \"371647ba-3649-4039-bfe6-ae32cf404737\",\n                    \"name\": \"Layout Prototypes Page 1\",\n                    \"title\": \"Layout Prototypes Page 1\"\n                },\n                {\n                    \"friendlyURL\": \"/layout-prototypes-page-2\",\n                    \"layoutPrototypeUuid\": \"c98067d0-fc10-9556-7364-238d39693bc4\",\n                    \"name\": \"Layout Prototypes Page 2\",\n                    \"title\": \"Layout Prototypes Page 2\"\n                }\n            ],\n            \"name\": \"Layout Prototypes\",\n            \"title\": \"Layout Prototypes\"\n        },\n        {\n            \"columns\": [\n                [\n                    {\n                        \"portletId\": \"com_liferay_login_web_portlet_LoginPortlet\"\n                    }\n                ],\n                [\n                    {\n                        \"portletId\": \"com_liferay_hello_world_web_portlet_HelloWorldPortlet\"\n                    }\n                ]\n            ],\n            \"friendlyURL\": \"/parent-page\",\n            \"layouts\": [\n                {\n                    \"friendlyURL\": \"/child-page-1\",\n                    \"name\": \"Child Page 1\",\n                    \"title\": \"Child Page 1\"\n                },\n                {\n                    \"friendlyURL\": \"/child-page-2\",\n                    \"name\": \"Child Page 2\",\n                    \"title\": \"Child Page 2\"\n                }\n            ],\n            \"name\": \"Parent Page\",\n            \"title\": \"Parent Page\"\n        },\n        {\n            \"friendlyURL\": \"/url-page\",\n            \"name\": \"URL Page\",\n            \"title\": \"URL Page\",\n            \"type\": \"url\"\n        },\n        {\n            \"friendlyURL\": \"/link-page\",\n            \"name\": \"Link to another Page\",\n            \"title\": \"Link to another Page\",\n            \"type\": \"link_to_layout\",\n            \"typeSettings\": \"linkToLayoutId=1\"\n        },\n        {\n            \"friendlyURL\": \"/hidden-page\",\n            \"name\": \"Hidden Page\",\n            \"title\": \"Hidden Page\",\n            \"hidden\": \"true\"\n        }\n    ]\n}\n```\n\nIf you don't understand the sitemap at this point, don't worry. This section \ncovers how to create a sitemap for your theme, from \n[defining pages](/docs/7-2/frameworks/-/knowledge_base/f/defining-layout-templates-and-pages-in-a-sitemap) \nto \n[defining portlets](/docs/7-2/frameworks/-/knowledge_base/f/defining-portlets-in-a-sitemap). \n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/07-managing-theme-resources/02-creating-a-sitemap/02-defining-layout-templates-and-pages-in-a-sitemap.markdown",
    "content": "---\nheader-id: defining-layout-templates-and-pages-in-a-sitemap\n---\n\n# Defining Layout Templates and Pages in a Sitemap\n\n[TOC levels=1-4]\n\nA sitemap defines the layouts---pages---and layout templates that your site or \nsite template uses. Follow these steps to define pages and layout templates in \nyour theme's `sitemap.json`:\n\n1.  Define a default layout template ID so the target site or site template can \n    reference the layout template to use for its pages. When defined outside the \n    scope of a page, the `layoutTemplateId` sets the default layout template for \n    the theme's pages:\n\n    ```json\n    {\n      \"layoutTemplateId\": \"2_columns_ii\",\n      \"publicPages\": [\n        {\n          \"friendlyURL\": \"/my-page\",\n          \"name\": \"My Page\",\n          \"title\": \"My Page\"\n        }\n      ]  \n    }\n    ```\n\n    You can override this by defining a layout template inside a page \n    configuration. In the example below, the Hidden Page and the Welcome page \n    both use the default `2_columns_ii` layout template, while the Custom Layout \n    Page overrides the default layout template and uses the `2_columns_i` layout \n    template instead:\n\n    ```json\n    {\n      \"layoutTemplateId\":\"2_columns_ii\",\n      \"publicPages\": [\n          {\n            \"friendlyURL\": \"/welcome-page\",\n            \"name\": \"Welcome\",\n            \"title\": \"Welcome\"\n          },\n          {\n            \"friendlyURL\": \"/custom-layout-page\",\n            \"name\": \"Custom Layout Page\",\n            \"title\": \"Custom Layout Page\",\n            \"layoutTemplateId\": \"2_columns_i\"\n          },\n          {\n            \"friendlyURL\": \"/hidden-page\",\n            \"name\": \"Hidden Page\",\n            \"title\": \"Hidden Page\",\n            \"hidden\": \"true\"\n          }\n      ]\n    }\n    ```\n    \n    | **Note:** Pages are imported into a site template by default. Site templates\n    | only support the importing of either public page sets or private page sets, not\n    | both.\n    | \n    | If you want to import both public and private page sets, as shown in the example\n    | `sitemap.json` below, you must\n    | [import your resources into a site](/docs/7-2/frameworks/-/knowledge_base/f/specifying-where-to-import-your-themes-resources).\n  \n2.  Follow the pattern below to specify the public and (optionally) private \n    pages for your theme. You can specify a name for a page, title, friendly \n    URL, whether it is hidden, and much more. The example below defines a \n    default layout template and both public and private page sets to import into \n    a site. See [Sitemap Page Configuration Options](/docs/7-2/reference/-/knowledge_base/r/sitemap-page-configuration-options) \n    for a full list of the available options.\n\n    ```json\n            {\n              \"layoutTemplateId\": \"2_columns_ii\",\n              \"privatePages\": [\n                  {\n                    \"friendlyURL\": \"/private-page\",\n                \t\t\"name\": \"Private Page\",\n                \t\t\"title\": \"Private Page\"\n                  }\n              ],\n              \"publicPages\": [\n                  {\n                    \"friendlyURL\": \"/welcome-page\",\n                    \"nameMap\": {\n                        \"en_US\": \"Welcome\",\n                        \"fr_FR\": \"Bienvenue\"\n                    },\n                    \"title\": \"Welcome\"\n                  },\n                  {\n                    \"friendlyURL\": \"/custom-layout-page\",\n                    \"name\": \"Custom Layout Page\",\n                    \"title\": \"Custom Layout Page\",\n                    \"layoutTemplateId\": \"2_columns_i\"\n                  },\n                  {\n                    \"friendlyURL\": \"/hidden-page\",\n                    \"name\": \"Hidden Page\",\n                    \"title\": \"Hidden Page\",\n                    \"hidden\": \"true\"\n                  }\n              ]\n            }\n    ```\n\n    You can create child pages by configuring the `layouts` element for a page \n    configuration:\n\n    ```json\n    {      \n    \"friendlyURL\": \"/parent-page\",\n    \"layouts\": [\n        {\n            \"friendlyURL\": \"/child-page-1\",\n            \"name\": \"Child Page 1\",\n            \"title\": \"Child Page 1\"\n        },\n        {\n            \"friendlyURL\": \"/child-page-2\",\n            \"name\": \"Child Page 2\",\n            \"title\": \"Child Page 2\"\n        }\n    ],\n    \"name\": \"Parent Page\",\n    \"title\": \"Parent Page\"\n    }\n    ```\n\nGreat! Now you know how to configure pages for the Resources Importer. \n\n## Related Topics\n\n- [Preparing and Organizing Web Content for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer)\n- [Defining Portlets in a Sitemap](/docs/7-2/frameworks/-/knowledge_base/f/defining-portlets-in-a-sitemap)\n- [Specifying Where to Import Your Theme's Resources](/docs/7-2/frameworks/-/knowledge_base/f/specifying-where-to-import-your-themes-resources)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/07-managing-theme-resources/02-creating-a-sitemap/04-defining-portlets-in-a-sitemap.markdown",
    "content": "---\nheader-id: defining-portlets-in-a-sitemap\n---\n\n# Defining Portlets in a Sitemap\n\n[TOC levels=1-4]\n\nYou can embed portlets in a sitemap for the pages you define. You can embed them \nwith the default settings or provide portlet preferences for a more custom look \nand feel. This tutorial covers both these options. \n\nFollow these steps:\n\n1.  Note the portlet's ID. This is the `javax.portlet.name` attribute of the \n    portlet spec. For convenience, The IDs for portlets available out-of-the-box \n    are listed in the \n    [Fully Qualified Portlet IDs](/docs/7-1/reference/-/knowledge_base/r/fully-qualified-portlet-ids) \n    reference guide. For custom portlets, this property is listed in the portlet \n    class as the `javax.portlet.name=` service property.\n    \n2.  List the portlet ID in the column of the layout you want to display the \n    portlet on. An example configuration is shown below:\n\n    ```json\n    {\n        \"layoutTemplateId\": \"2_columns_ii\",\n        \"publicPages\": [\n            {\n                \"columns\": [\n                    [\n                        {\n                            \"portletId\": \"com_liferay_login_web_portlet_LoginPortlet\"\n                        },\n                        {\n                            \"portletId\": \n                            \"com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet\"\n                        }\n                    ],\n                    [\n                        {\n                            \"portletId\": \"com_liferay_hello_world_web_portlet_HelloWorldPortlet\"\n                        }\n                    ]\n                ],\n                \"friendlyURL\": \"/home\",\n                \"nameMap\": {\n                    \"en_US\": \"Welcome\",\n                    \"fr_FR\": \"Bienvenue\"\n                },\n                \"title\": \"Welcome\"\n            }\n        ]\n    }\n    ```\n\n    This approach embeds the portlet with its default settings. To customize the \n    portlet, you must configure the portlet's preferences, as described in the \n    next step. \n    \n3.  Optionally specify portlet preferences for a portlet with the \n    `portletPreferences` key. Below is an example for the Web Content Display \n    portlet:\n\n    ```json\n    {\n        \"portletId\": \"com_liferay_journal_content_web_portlet_JournalContentPortlet\",\n        \"portletPreferences\": {\n            \"articleId\": \"Custom Title.xml\",\n            \"groupId\": \"${groupId}\",\n            \"portletSetupPortletDecoratorId\": \"decorate\",\n            \"portletSetupTitle_en_US\": \"Web Content Display with Custom Title\",\n            \"portletSetupUseCustomTitle\": \"true\"\n        }\n    }\n    ```\n\n    **portletSetupPortletDecoratorId:** Specifies the [portlet decorator]() \n    to use for the portlet (`borderless` || `barebone` || `decorate`). See \n    [Setting Default Preference for An Embedded Portlet](/docs/7-2/frameworks/-/knowledge_base/f/setting-default-preferences-for-an-embedded-portlet) for more information. \n\n| **Tip:** You can specify an\n| [application display template](/docs/7-1/user/-/knowledge_base/u/styling-apps-and-assets)\n| (ADT) for a portlet in the `sitemap.json` file by setting the `displayStyle` and\n| `displayStyleGroupId` portlet preferences, as shown in the example below:\n| \n|     \"portletId\": \"com_liferay_asset_publisher_web_portlet_AssetPublisherPortlet\",\n|         \"portletPreferences\": {\n|             \"displayStyleGroupId\": \"10197\",\n|             \"displayStyle\": \"ddmTemplate_6fe4851b-53bc-4ca7-868a-c836982836f4\"\n|     }\n\nPortlet preferences are unique to each portlet, so first you must determine\nwhich preferences you want to configure. There are two ways to determine the\nproper key/value pair for a portlet preference. The first is to set the\nportlet preference manually, and then check the values in the\n`portletPreferences.preferences` column of the database as a hint for what to\nconfigure in your `sitemap.json`. For example, you can configure the Asset\nPublisher to display assets that match the specified asset tags, using the\nfollowing configuration:\n\n    \"queryName0\": \"assetTags\",\n    \"queryValues0\": \"MyAssetTagName\"\n\nAnother approach is to search each app in your bundle for the keyword \n`preferences--`. This returns some of the app's JSPs that have the portlet \npreferences defined for the portlet.\n\n| **Note:** Portlet preferences that require an existing configuration, such as a\n| tag or category, may require you to create the configuration on the Global site\n| first, so that the Resources Importer finds a match when deployed with the\n| theme.\n\n## Related Topics\n\n- [Preparing and Organizing Web Content for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer)\n- [Defining Layout Templates and Pages in a Sitemap](/docs/7-2/frameworks/-/knowledge_base/f/defining-layout-templates-and-pages-in-a-sitemap)\n- [Specifying Where to Import Your Theme's Resources](/docs/7-2/frameworks/-/knowledge_base/f/specifying-where-to-import-your-themes-resources)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/07-managing-theme-resources/02-creating-a-sitemap/05-retrieving-portlet-ids-with-gogo-shell.markdown",
    "content": "---\nheader-id: retrieving-portlet-ids-with-the-gogo-shell\n---\n\n# Retrieving Portlet IDs with the Gogo Shell\n\n[TOC levels=1-4]\n\nTo [include a portlet in a sitemap](/docs/7-2/frameworks/-/knowledge_base/f/defining-portlets-in-a-sitemap),\nyou must have its ID. For convenience, IDs for out-of-the-box portlets appear in\nthe [Fully Qualified Portlet IDs](/docs/7-2/reference/-/knowledge_base/r/fully-qualified-portlet-ids)\nreference guide. If you've installed purchased or developed portlets, you can\nretrieve their IDs using the Gogo Shell, as this tutorial instructs. \n\nFollow these steps to use the Gogo Shell to retrieve a portlet ID:\n\n1.  Open the Control Panel and go to Configuration &rarr; Gogo Shell.\n\n2.  Run the command `lb [app prefix]`, and locate the app's web bundle. For \n    example, run `lb blogs` to find the blogs web bundle.\n\n        100|Active     |   10|Liferay Blogs Web (3.0.7)\n\n3.  Run the command `scr:list [bundle ID]`, and locate the app's component ID. \n    The blogs portlet entry is shown below. The last number preceding the \n    bundle's state is the component ID:\n\n        [ 100] com.liferay.blogs.web.internal.portlet.BlogsPortlet enabled \n        [ 196] [active] \n\n4.  Run the command `scr:info [component ID]` to list the portlet's information. \n    For example, to list the info for the blogs portlet component, run \n    `scr:info 196`. Note that the bundle and/or component ID may be different \n    for your instance.\n\n5.  Search for `javax.portlet.name` in the results. `javax.portlet.name`'s value \n    is the portlet ID required for the sitemap. The blogs portlet's ID is shown \n    below:\n    \n        javax.portlet.name = com_liferay_blogs_web_portlet_BlogsPortlet\n\n![Figure 1: Portlet IDs can be found via the Gogo Shell.](../../../../../images/resources-importer-gogo-shell.png)\n\n## Related Topics\n\n- [Defining Layout Templates and Pages in a Sitemap](/docs/7-2/frameworks/-/knowledge_base/f/defining-layout-templates-and-pages-in-a-sitemap)\n- [Defining Portlets in a Sitemap](/docs/7-2/frameworks/-/knowledge_base/f/defining-portlets-in-a-sitemap)\n- [Preparing and Organizing Web Content for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/07-managing-theme-resources/03-preparing-and-organizing-web-content/01-preparing-and-organizing-web-content-for-the-importer-intro.markdown",
    "content": "---\nheader-id: preparing-and-organizing-web-content-for-the-resources-importer\n---\n\n# Preparing and Organizing Web Content for the Resources Importer\n\n[TOC levels=1-4]\n\nYou must create the resources to import with your theme. You can create \nresources from scratch and/or bring in resources that you've already created. \nYou can leverage your HTML (basic web content), JSON (structures), or VM or FTL \n(templates) files with the Resource Importer. All web content articles require \na structure and optionally a template. Note that some articles may share the \nsame structure and perhaps even the same template---this is the case for all \nbasic web content articles. Follow these steps to prepare your web content \narticles:\n\n1.  Select *Edit* from the article's options menu, click the *Options* icon \n    at the top right of the page and select *View Source*. Copy the article's \n    raw XML into an XML file locally. Create a folder for the article under \n    `resources-importer/journal/articles/` and rename it as desired. The web \n    content article's XML fills in the data required by the structure. An \n    example web content article's XML is shown below:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n\n    <root available-locales=\"en_US\" default-locale=\"en_US\">\n      <dynamic-element name=\"content\" type=\"text_area\" index-type=\"keyword\" index=\"0\">\n        <dynamic-content language-id=\"en_US\">\n    \t    <![CDATA[\n    \t\t    <center>\n    \t\t    <p><img alt=\"\" src=\"[$FILE=space-program-history.jpg$]\" /></p>\n    \t\t    </center>\n\n    \t\t    <p>In the mid-20th century, after two of the \n    \t\t    most violent wars in history, mankind turned \n    \t\t    its gaze upwards to the stars. Instead of \n    \t\t    continuing to strive against one another, \n    \t\t    man choose instead to strive against the \n    \t\t    limits that we had bound ourselves to. And \n    \t\t    so the Great Space Race began.</p>\n\n    \t\t    <p>At first the race was to reach space--get \n    \t\t    outside the earth's atmosphere, and when \n    \t\t    that had been reached, we shot for the moon. \n    \t\t    After sending men to the moon, robots to \n    \t\t    Mars, and probes beyond the reaches of our \n    \t\t    solar system, it seemed that there was \n    \t\t    nowhere left to go.</p>\n\n    \t\t    <p>The Space Program aims to change that. \n    \t\t    Beyond national boundaries, beyond what \n    \t\t    anyone can imagine that we can do. The sky \n    \t\t    is not the limit.</p>\n    \t    ]]>\n        </dynamic-content>\n      </dynamic-element>\n    </root>\n    ```\n\n2.  Download the web content article's structure. Open the structure and click \n    the *Source* tab to view the structure's file. Copy and paste its contents \n    into a new JSON file in the `resources-importer/journal/structures/` folder. \n    The structure JSON sets a wireframe, or blueprint, for an article's data. If \n    you're saving a basic web content article, you can copy the structure below \n    (replace `en_US` with your language):\n    \n    ```json\n    {\n        \"availableLanguageIds\": [\n            \"en_US\"\n        ],\n        \"defaultLanguageId\": \"en_US\",\n        \"fields\": [\n            {\n                \"label\": {\n                    \"en_US\": \"Content\"\n                },\n                \"predefinedValue\": {\n                    \"en_US\": \"\"\n                },\n                \"style\": {\n                    \"en_US\": \"\"\n                },\n                \"tip\": {\n                    \"en_US\": \"\"\n                },\n                \"dataType\": \"html\",\n                \"fieldNamespace\": \"ddm\",\n                \"indexType\": \"text\",\n                \"localizable\": true,\n                \"name\": \"content\",\n                \"readOnly\": false,\n                \"repeatable\": false,\n                \"required\": false,\n                \"showLabel\": true,\n                \"type\": \"ddm-text-html\"\n            }\n        ]\n    }\n    ```\n\n3.  Download the structure's matching template if it has one. Open the Actions \n    menu for the structure and select *Manage Templates* to view the templates \n    that use it. Create a folder for the template under \n    `resources-importer/journal/templates/` and copy and paste its contents into \n    a new FTL file. The template defines how the data should be displayed. If \n    you're saving a basic web content article, you can copy the FreeMarker \n    template below:\n\n    ```markup\n    ${content.getData()}\n    ```\n\nRepeat the steps above for each web content article you have. Note that some web \ncontent articles may share the same structure and template; In these cases, only \none copy of the structure and template is required for all web content articles \nthat use them. Once your web content articles are saved, you can place them in \ntheir proper folder structure. \n\n## Related Topics\n\n- [Creating a Sitemap for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-sitemap-for-the-resources-importer)\n- [Defining Assets for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/defining-assets-for-the-resources-importer)\n- [Specifying Where to Import Your Theme's Resources](/docs/7-2/frameworks/-/knowledge_base/f/specifying-where-to-import-your-themes-resources)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/07-managing-theme-resources/04-defining-assets/01-defining-assets-intro.markdown",
    "content": "---\nheader-id: defining-assets-for-the-resources-importer\n---\n\n# Defining Assets for the Resources Importer\n\n[TOC levels=1-4]\n\nThe `sitemap.json` file defines the pages of the site or site template to\nimport---along with the layout templates, portlets, and portlet preferences of\nthese pages. You may also want to provide details about the assets you include\nwith the theme. An `assets.json` file lets you provide this information. \n\n1.  Create the `assets.json` in your theme's \n    `[theme-name]/src/WEB-INF/src/resources-importer` folder. \n\n2.  Follow the pattern below to configure your `assets.json` file. Tags can be \n    applied to any asset. Abstract summaries and small images can be applied to \n    web content articles. For example, the following `assets.json` file \n    specifies two tags for the `company_logo.png` image, one tag for the \n    `Custom Title.xml` web content article, and an abstract summary and small \n    image for the `Child Web Content 1.json` article structure:\n\n    ```json\n    {\n        \"assets\": [\n            {\n                \"name\": \"company_logo.png\",\n                \"tags\": [\n                    \"logo\",\n                    \"company\"\n                ]\n            },\n            {\n                \"name\": \"Custom Title.xml\",\n                \"tags\": [\n                    \"web content\"\n                ]\n            },\n            {\n                \"abstractSummary\": \"This is an abstract summary.\",\n                \"name\": \"Child Web Content 1.json\",\n                \"smallImage\": \"company_logo.png\"\n            }\n        ]\n    }\n    ```\n\nNow you know how to configure assets for your web content! \n\n## Related Topics\n\n- [Preparing and Organizing Web Content for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer)\n- [Creating a Sitemap for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-sitemap-for-the-resources-importer)\n- [Specifying Where to Import Your Theme's Resources](/docs/7-2/frameworks/-/knowledge_base/f/specifying-where-to-import-your-themes-resources) \n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/07-managing-theme-resources/05-where-to-import-resources/01-specifying-where-to-import-resources-intro.markdown",
    "content": "---\nheader-id: specifying-where-to-import-your-themes-resources\n---\n\n# Specifying Where to Import Your Theme's Resources\n\n[TOC levels=1-4]\n\nBy default, resources are imported into a new site template named after the \ntheme, but you can also import resources into a new site or existing sites or \nsite templates. These options are covered below. Follow these steps:\n\n1.  Before specifying where to import your resources, you must enable Developer \n    Mode in your theme. To do this, add the following property to your theme's \n    `liferay-plugin-package.properties` file. This is enabled by default for \n    themes generated with the [Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/theme-generator). \n    Without this property enabled, you must manually delete the sites or site \n    templates built by the Resources Importer each time you want to apply \n    changes from your theme's `src/WEB-INF/src/resources-importer` folder:\n\n    ```properties\n    resources-importer-developer-mode-enabled=true\n    ```\n\n    | **Warning:** the `resources-importer-developer-mode-enabled=true` setting can be\n    | dangerous since it involves *deleting* (and re-creating) the affected site or\n    | site template. It's only intended to be used during development. **Never use it\n    | in production.**\n\n2.  Specify where to import your resources. By default, resources are imported \n    into a new site template named after the theme. If that's what you want, you \n    can skip this step. If instead you want your resources to be imported into \n    an existing site template, you must specify a value for the \n    `resources-importer-target-value` property in your theme's \n    `liferay-plugin-package.properties` file:\n\n    ```properties\n    #resources-importer-target-class-name\n\n    resources-importer-target-value=[site-template-name]\n    ```\n\n    Alternatively, you can import resources into an existing site. **You must** \n    import your resources into a site if you define both public and private page \n    sets in your `sitemap.json`. To import resources into an existing site, \n    uncomment the `resources-importer-target-class-name` property and set it to \n    `com.liferay.portal.kernel.model.Group`:\n\n    ```properties\n    resources-importer-target-class-name=com.liferay.portal.kernel.model.Group\n\n    resources-importer-target-value=[site-name] \n    ```\n\n    Double check the name that you're specifying. If you specify the wrong value, \n    you could end up deleting (and re-creating) the wrong site or site template! \n\n    | **Warning:** It's safer to import theme resources into a site template than into\n    | an actual site. The\n    | `resources-importer-target-class-name=com.liferay.portal.kernel.model.Group`\n    | setting can be handy for development and testing but should be used cautiously.\n    | Don't use this setting in a theme deployed to a production Liferay instance or\n    | a theme submitted to Liferay Marketplace. To prepare a theme for deployment to\n    | a production Liferay instance, use the default setting so that the resources are\n    | imported into a site template. You can do this explicitly by setting\n    | `resources-importer-target-class-name=com.liferay.portal.kernel.model.LayoutSetPrototype`\n    | or implicitly by commenting out or removing the\n    | `resources-importer-target-class-name` property.\n\n3.  Deploy the theme. To view your theme and its resources, log in as an \n    administrator, and check the Sites or Site Templates section of the Control \n    Panel to make sure your resources were deployed correctly. From the Control \n    Panel you can easily view your theme and its resources:\n\n    - If you imported into a site template, open its actions menu and select \n      *View Pages* to see it.\n    - If you imported directly into a site, open its actions menu and select \n      *Go to Public Pages* to see it.\n\nGreat! Now you know how to specify where to import your theme's resources. \n\n## Related Topics\n\n- [Preparing and Organizing Web Content for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer)\n- [Creating a Sitemap for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-sitemap-for-the-resources-importer)\n- [Defining Assets for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/defining-assets-for-the-resources-importer)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/07-managing-theme-resources/06-creating-a-theme-lar/01-creating-an-archive-of-your-sites-themes-and-resources-intro.markdown",
    "content": "---\nheader-id: archiving-your-sites-resources\n---\n\n# Archiving Site Resources\n\n[TOC levels=1-4]\n\nAlthough a `sitemap.json` is the recommended approach for including resources \nwith a theme, you can also export your site's data in a LAR (Liferay Archive) \nfile. A LAR file is version-specific; it won't work on any version of \n@product@ other than the one from which it was exported. This approach does, \nhowever, require less configuration, since it does not require a sitemap or \nother files. So, if you're using the exported resources in the same version of \n@product@ and it's not for a theme on Liferay Marketplace, you may prefer a LAR \nfile. \n\nFollow these steps to archive your site's resources:\n\n1.  Export the contents of a site using the site scope. \n\n2.  Place the `archive.lar` file in your theme's \n    `/src/WEB-INF/src/resources-importer` folder. \n    \nGreat! Now you know how to archive your site's resources. \n\n## Related Topics\n\n- [Preparing and Organizing Web Content for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/preparing-and-organizing-web-content-for-the-resources-importer)\n- [Creating a Sitemap for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-sitemap-for-the-resources-importer)\n- [Defining Assets for the Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/defining-assets-for-the-resources-importer)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/02-themes/10-troubleshooting-themes/01-troubleshooting-themes-intro.markdown",
    "content": "---\nheader-id: troubleshooting-themes\n---\n\n# Troubleshooting Themes\n\n[TOC levels=1-4]\n\nThese frequently asked questions and answers help you troubleshoot and correct\nproblems in theme development.\n\nClick a question to view the answer.\n\n- [How can I include OSGi headers in my theme?](#osgi-headers-in-themes)\n- [Why aren't my changes showing up after I redeploy my theme?](#developer-mode)\n- [Why is my theme not loading? It returns the default theme instead.](#default-theme-returned)\n- [How can I prevent specific CSS rules from transforming for RTL Languages?](#rtl-no-flip)\n\n<div class=\"ldn-faq-question\" id=\"osgi-headers-in-themes\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">How can I include OSGi headers in my theme?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>Specify the headers you want to use in your theme's <code>liferay-plugin-package.properties</code> file. Any headers placed in this file are included automatically in your MANIFEST and the OSGi bundle.</p>\n    <p>For example, you can add OSGi dependencies in your theme by importing the exported package with the <code>Import-Package</code> header:</p>\n    <pre><code>Import-Package:com.liferay.docs.portlet</code></pre>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\" id=\"developer-mode\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">Why aren't my changes showing up after I redeploy my theme?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>By default CSS, JS, and theme template files are cached in the browser. During development, you can enable <a href=\"/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes\">Devloper Mode</a> to prevent your theme's files from caching. </p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\" id=\"default-theme-returned\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">Why is my theme not loading? It returns the default theme instead.&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>If you receive the warning \"No theme found for specified theme id...\", you may be referencing an outdated theme ID in your Site. Verify that the theme ID in your theme's <code>liferay-look-and-feel.xml</code> matches the theme ID in the warning message: \"mytheme_WAR_mytheme\". If the theme IDs match, there may be pages using the outdated theme instead of the Site theme. You can verify this by checking the pages manually or searching the database for layouts with values for <code>themeId -</code>. </p>\n  </div>\n</div>\n\n<br/>\n<div class=\"ldn-faq-question\" id=\"rtl-no-flip\">\n  <span class=\"ldn-faq-toggle-button\" data-show=\"false\" style=\"font-weight: normal;\">How can I prevent specific CSS rules from transforming for RTL Languages?&nbsp;<span class=\"icon-caret-right\" style=\"pointer-events:none;\"></span></span>\n  <div class=\"hide\">\n    <p>You can prevent specific CSS rules from transforming (flipping) with the <code>/* @noflip */</code> decoration. Place the decoration to the left of the CSS rule to apply it. For example, this rule gives a left margin of <code>20em</code> to the <code>body</code> no matter if the selected language is LTR or RTL:</p>\n      <pre><code>\n      /* @noflip */ body {\n       margin-left: 20em;\n      }\n      </pre></code>\n    <p>You can also use the <code>.rtl</code> CSS selector for rules that exclusively apply to RTL languages.</p>\n  </div>\n</div>\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/03-layout-templates/00-layout-templates-intro.markdown",
    "content": "---\nheader-id: layout-templates-intro\n---\n\n# Layout Templates\n\n[TOC levels=1-4]\n\nLayout templates dictate where content and apps can be placed on a page. \nThere are several default layout templates for organizing content on your\npages:\n\n![Figure 1: There are many default layout templates to choose from.](../../../images/page-select-layout.png)\n\nIf the default layouts don't work for your site, you can create your own layout\ntemplate by following the articles listed below: \n\n- [Create the layout template](/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator)\n- [Create the layout template thumbnail preview](/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-layout-template-thumbnail-previews)\n- [Bundle the layout template with a theme](/docs/7-2/frameworks/-/knowledge_base/f/including-layout-templates-with-a-theme)\n\nLayout Templates specify a number of rows and columns for the page. The rows \nand columns dictate where apps (and fragments) can be placed on the page. Layout \ntemplates are written in \n[FreeMarker](https://freemarker.apache.org/). \nAn example row's HTML markup is shown below:\n\n```html\n<div class=\"portlet-layout row\">\n        <div class=\"col-md-4 col-sm-6 portlet-column portlet-column-first\" \n        id=\"column-1\">\n                ${processor.processColumn(\"column-1\", \n                \"portlet-column-content portlet-column-content-first\")}\n        </div>\n        <div class=\"col-md-8 col-sm-6 portlet-column portlet-column-last\" \n        id=\"column-2\">\n                ${processor.processColumn(\"column-2\", \n                \"portlet-column-content portlet-column-content-last\")}\n        </div>\n</div>\n```\n\nColumns use the \n[Bootstrap grid system](https://getbootstrap.com/docs/4.0/layout/grid/). \nEvery row consists of twelve sections, so columns can range in size from `1` to \n`12`. Sizes are indicated with the number that follows the `col-[breakpoint]` \nclass prefix (e.g. `col-md-6`). These specify two things: the percentage-based \nwidth of the element and the media query breakpoint (`xs`, `sm`, `md`, or `lg`), \nwhich specifies when the element expands to 100% width. For example, `col-md-6` \nindicates `6/12` width, or `50%`. These classes can also be mixed to achieve \nmore advanced layouts, as shown above. In the example, medium sized viewports \ndisplay `column-1` at 33.33% width and `column-2` at 66.66% width, while both \n`column-1` and `column-2` are 50% width on small sized view ports. \n\nThe processor (`${processor.processColumn()}`) processes each column's content, \ntaking two arguments: the column's `id`, and the classes \n`portlet-column-content` and `portlet-column-content-[case]` (if applicable), \nwhere `[case]` refers to the `first`, `last`, or `only` column in the row. \n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/03-layout-templates/01-creating-custom-layout-template-thumbnail-previews.markdown",
    "content": "---\nheader-id: creating-custom-layout-template-thumbnail-previews\n---\n\n# Creating Custom Layout Template Thumbnail Previews\n\n[TOC levels=1-4]\n\nTo showcase your layout template properly, you must provide a thumbnail preview \nfor it. Without this, no one will know the design of the layout. Follow these \nsteps to provide a thumbnail preview for your layout template:\n\n1.  Navigate to the layout template's `docroot/` folder. If you bundled the \n    layout template with a theme created with the Liferay Theme Generator, the \n    thumbnail is located in your theme's `src/layouttpl/custom/my-layoutttpl` \n    folder.\n\n2.  Create a custom thumbnail PNG inside the folder specified in step 1 with the \n    same name as the layout template that is 120 px x 120 px . Delete the \n    temporary thumbnail PNG file if it exist.\n    \n    ![Figure 1: A thumbnail preview displays the layout's design to the user.](../../../images/porygon_50_50_width_limited.png)\n\n3.  Deploy your layout template to your app server to use it. If your layout \n    template is [bundled with a theme](/docs/7-2/frameworks/-/knowledge_base/f/including-layout-templates-with-a-theme), \n    it deploys when the theme is deployed. Now you know how to create a custom \n    thumbnail preview for your @product@ layout templates! \n\n## Related topics\n\n- [Layout Templates with the Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator)\n- [Bundling Layout Templates with a Theme](/docs/7-2/frameworks/-/knowledge_base/f/including-layout-templates-with-a-theme)\n- [Themes](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/03-layout-templates/02-including-layout-templates-with-a-theme.markdown",
    "content": "---\nheader-id: including-layout-templates-with-a-theme\n---\n\n# Including Layout Templates with a Theme\n\n[TOC levels=1-4]\n\nAlthough you can deploy a layout template by itself, you can also bundle it with \na theme. If you generated a layout template with the [Layouts sub-generator](/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator) \nfrom inside a generated theme project, the layout template is bundled with the \ntheme automatically. If, however, you generated a layout template and want to \nbundle it with a theme afterwards, follow these steps to include the layout \ntemplate with a theme:\n\n1.  Copy the layout template's `liferay-layout-templates.xml` file to the \n    theme's `src/WEB-INF/` folder.\n\n2.  Create a `layouttpl/custom/my-layouttpl/` folder inside the theme's `src/` \n    folder.\n\n3.  Copy the layout template's FreeMarker (.ftl) file, and [thumbnail preview](/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-layout-template-thumbnail-previews) \n    (.png) if it exist, over to the `layouttpl/custom/my-layouttpl/` folder.\n\n4.  Copy the theme's `liferay-theme.json` file into the \n    `src/layouttpl/custom/my-layouttpl/` folder and rename it \n    `liferay-plugin.json`.\n\n5.  Open `liferay-plugin.json`, rename the `LiferayTheme` entry `LiferayPlugin`, \n    and replace the `pluginName` entry's value with the name of the layout \n    template. Below is an example configuration:\n\n```json    \n{\n  \"LiferayPlugin\": {\n    \"deploymentStrategy\": \"LocalAppServer\",\n    \"appServerPath\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0\\\\tomcat-9.0.10\",\n    \"deployPath\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0\\\\tomcat-9.0.10\\\\deploy\",\n    \"url\": \"http://localhost:8080\",\n    \"appServerPathPlugin\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0\\\\tomcat-9.0.10\\\\webapps\\\\my-layouttpl\",\n    \"deployed\": false,\n    \"pluginName\": \"my-layouttpl\"\n  }\n}\n```\n\nNow you know how to include layout templates with your @product@ themes! \n\n## Related topics\n\n- [Layout Templates with the Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator)\n- [Creating Custom Layout Template Thumbnail Previews](/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-layout-template-thumbnail-previews)\n- [Themes](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/05-creating-js-widgets-with-js-tooling/01-creating-widgets-with-javascript-tooling-intro.markdown",
    "content": "---\nheader-id: creating-and-bundling-javascript-widgets-with-javascript-tooling\n---\n\n# Creating and Bundling JavaScript Widgets with JavaScript Tooling\n\n[TOC levels=1-4]\n\nPortlets are a Java standard, so you must have a knowledge and understanding of \nhow Java works to write one. This can be quite the hurdle for front-end \ndevelopers who want to use JavaScript frameworks in their widgets. Thanks to \nthe Liferay JS Generator and \n[liferay-npm-bundler](/docs/7-2/reference/-/knowledge_base/r/liferay-npm-bundler), \ndevelopers can easily create and develop JavaScript widgets in @product@ using \npure JavaScript tooling. The Liferay JS Generator generates JavaScript widgets \nfor @product@. It is just one of Liferay JS Toolkit's \n[tools](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages). \n\n| **Note:** To use the Liferay JS Generator, you must have the Liferay JS \n| Portlet Extender activated in your @product@ instance. It's activated by \n| default. You can confirm this by opening the Control Menu, navigating to the \n| *App Manager*, and searching for `com.liferay.frontend.js.portlet.extender`. \n\n![Figure 1: The JS Portlet Extender lets you use pure JavaScript tooling to write widgets.](../../../images/extender-lifecycle.png)\n\n| **Note:** JavaScript Server-Side-Rendering is not supported out-of-the-box. To\n| use JS frameworks for site rendering, you **must** set up your server-side\n| (or search-crawler) rendering generation to support them.\n\nOnce you've \n[installed the Liferay JS Generator and generated a widget](/docs/7-2/reference/-/knowledge_base/r/installing-the-js-generator-and-generating-a-bundle), \nyou can configure instance settings, system settings, and even provide \nlocalization for your widget. This section explains how to configure these \noptions for your generated JS widget. \n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/05-creating-js-widgets-with-js-tooling/02-configuring-system-and-instance-settings-for-your-widget.markdown",
    "content": "---\nheader-id: configuring-system-settings-and-instance-settings-for-your-js-widget\n---\n\n# Configuring System Settings and Instance Settings for Your JavaScript Widget\n\n[TOC levels=1-4]\n\nAs of v1.1.0 of the JS Portlet Extender, you can define configuration options \nfor your widget. These options are passed to the widget's JavaScript entry point \nas the `configuration` parameter. See the [main entry point's reference](/docs/7-2/reference/-/knowledge_base/r/understanding-the-js-portlet-extender-configuration#main-entry-point) \nfor more information on the entry point. Follow these steps to set system and/or \nportlet instance settings for your widget:\n\n1.  Add a `/features` folder in your project's root folder if it doesn't already \n    exist. \n\n    | **Note:** This location can be overridden with the \n    | `create-jar.features.configuration` option in your project's `.npmbundlerrc` \n    | file. See [OSGi bundle configuration options](/docs/7-2/reference/-/knowledge_base/r/understanding-the-npmbundlerrcs-structure#osgi-bundle-creation-options) \n    | for all the available options for the bundle.\n\n2.  Create a `configuration.json` file in the `/features` folder and follow the \n    pattern below. See the [Configuration JSON](/docs/7-2/reference/-/knowledge_base/r/configuration-json-available-options) \n    reference for an explanation of each of the available options:\n \n    ```json\n    {\n      \"system\": {\n        \"category\": \"{category identifier}\",\n        \"name\": \"{name of configuration}\",\n        \"fields\": {\n          \"{field id 1}\": {\n            \"type\": \"{field type}\",\n            \"name\": \"{field name}\",\n            \"description\": \"{field description}\",\n            \"default\": \"{default value}\",\n            \"options\": {\n              \"{option id 1}\": \"{option name 1}\",\n              \"{option id 2}\": \"{option name 2}\",\n\n              \"{option id n}\": \"{option name n}\"\n            }\n          },\n          \"{field id 2}\": {},\n\n          \"{field id n}\": {}\n        }\n      },\n      \"portletInstance\": {\n        \"name\": \"{name of configuration}\",\n        \"fields\": {\n          \"{field id 1}\": {\n            \"type\": \"{field type}\",\n            \"name\": \"{field name}\",\n            \"description\": \"{field description}\",\n            \"default\": \"{default value}\",\n            \"options\": {\n              \"{option id 1}\": \"{option name 1}\",\n              \"{option id 2}\": \"{option name 2}\",\n\n              \"{option id n}\": \"{option name n}\"\n            }\n          },\n          \"{field id 2}\": {},\n\n          \"{field id n}\": {}\n        }\n      }\n    }\n    ```\n\n3.  Access a system setting's value or a portlet instance setting's value with \n    the syntax `configuration.system` or `configuration.portletInstance` \n    respectively. For instance, to retrieve the `{field id 1}` system settings \n    value, you would use `configuration.system.{field id 1}`. Note that all \n    fields are passed as strings no matter what type they declare in their \n    descriptor.\n\nAwesome! Now you know how to configure system settings and portlet instance \nsettings for your widget. \n\n## Related Topics\n\n- [Localizing Your Widget](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-widget)\n- [Using Translation Features in Your JavaScript Widget](/docs/7-2/frameworks/-/knowledge_base/f/using-translation-features-in-your-widget)\n- [Setting Portlet Properties for Your JavaScript Widget](/docs/7-2/frameworks/-/knowledge_base/f/configuring-portlet-properties-for-your-widget)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/05-creating-js-widgets-with-js-tooling/04-localizing-your-widget.markdown",
    "content": "---\nheader-id: localizing-your-widget\n---\n\n# Localizing Your Widget\n\n[TOC levels=1-4]\n\nFollow the steps below to learn how to localize your widget:\n\n1.  If you chose not to use localization when you generated the bundle, \n    follow this step to enable it in your bundle. Create\n    a `/features/localization` folder in your project and add\n    a `Language.properties` file to it. Add a `create-jar.features.localization`\n    key to your `.npmbuildrc` file that points to the `Language.properties`\n    file. An example configuration is shown below:\n \n    ```json\n    {\n    \t\"create-jar\": {\n    \t\t\"output-dir\": \"dist\",\n    \t\t\"features\": {\n    \t\t\t\"js-extender\": true,\n    \t\t\t\"web-context\": \"/my-test-js-widget\",\n    \t\t\t\"localization\": \"features/localization/Language\",\n    \t\t\t\"settings\": \"features/settings.json\"\n    \t\t}\n    \t},\n    \t...\n    }\n    ```\n\n    | **Note:** The default file path is shown above. You can update this value, \n    | if you want to place your `Language.properties` file in a different \n    | location.\n\n2.  Configure the `Language.properties` file and provide the localized property \n    files (e.g. `Language_[locale].properties`) with the \n    [language keys](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application) \n    for each \n    [available translation](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Languages%20and%20Time%20Zones). \n    The *JavaScript based widget* configuration is shown below:\n\n    ```properties\n    javax.portlet.title.my_js_portlet_project=My JS Widget Project\n    portlet-namespace=Portlet Namespace\n    context-path=Context Path\n    portlet-element-id=Portlet Element Id\n    configuration=Configuration\n    fruit=Favourite fruit\n    fruit-help=Choose the fruit you like the most\n    an-orange=An orange\n    a-pear=A pear\n    an-apple=An apple\n    ```\n\n3.  Retrieve a language key's localized value in JavaScript with the \n    `Liferay.Language.get('key')` method.\n\nGreat! Now you know how to localize your widget! \n\n## Related Topics\n\n- [Configuring System Settings and Instance Settings for Your JavaScript Widget](/docs/7-2/frameworks/-/knowledge_base/f/configuring-system-settings-and-instance-settings-for-your-js-widget)\n- [Using Translation Features in Your JavaScript Widget](/docs/7-2/frameworks/-/knowledge_base/f/using-translation-features-in-your-widget)\n- [Setting Portlet Properties for Your JavaScript Widget](/docs/7-2/frameworks/-/knowledge_base/f/configuring-portlet-properties-for-your-widget)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/05-creating-js-widgets-with-js-tooling/05-using-translation-features.markdown",
    "content": "---\nheader-id: using-translation-features-in-your-widget\n---\n\n# Using Translation Features in Your Widget\n\n[TOC levels=1-4]\n\nBy default, the Liferay JS Generator creates an empty configuration for \ntranslation. The translate script instructs the user how to add new supported \nlocales or configure the credentials when it's run. The translate target reads \nthe supported locales you have defined in the `supportedLocales` key of your \n`.npmbuildrc` file and checks your `*language.properties` files to make sure \nthey match. \n\n| **Note:** To use the translation features, you must have a Microsoft \n| Translator key. Provide your credentials through either the \n| `translatorTextKey` variable in your `.npmbuildrc` file, or provide them in \n| the `TRANSLATOR_TEXT_KEY` environment variable. \n\nFollow these steps to add a new supported locale and automatically create \na language properties file for it with translations:\n\n1.  Add the locale to the `supportedLocales` array in your `.npmbuildrc` file. \n\n2.  Run the translate target with the command below:\n\n    ```bash\n    npm run translate\n    ```\n\n3.  The translate target automatically creates a language properties file for \n    each new **supported** locale with translations for your language keys. It \n    also warns about locales that are not supported, but have a \n    `*language.properties` file.\n \nGreat! Now you know how to use the Liferay JS Generator's translation features \nin your app. \n\n## Related Topics\n\n- [Configuring System Settings and Instance Settings for Your JavaScript Widget](/docs/7-2/frameworks/-/knowledge_base/f/configuring-system-settings-and-instance-settings-for-your-js-widget)\n- [Localizing Your Widget](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-widget)\n- [Setting Portlet Properties for Your JavaScript Widget](/docs/7-2/frameworks/-/knowledge_base/f/configuring-portlet-properties-for-your-widget)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/05-creating-js-widgets-with-js-tooling/06-setting-portlet-properties-for-your-widget.markdown",
    "content": "---\nheader-id: configuring-portlet-properties-for-your-widget\n---\n\n# Configuring Portlet Properties for Your Widget\n\n[TOC levels=1-4]\n\nFollow these steps to configure your widget's properties:\n\n1.  Open your generated JavaScript widget's `package.json` file.\n\n2.  Set the properties under the `portlet` entry. Note that these are the same \n    properties you would define in the Java `@Component` annotation of a \n    portlet, as defined in the \n    [liferay-portlet-app_7_2_0.dtd](@platform-ref@/7.2-latest/definitions/liferay-portlet-app_7_2_0.dtd.html). \n    An example configuration is shown below:\n\n    ```json\n    \"portlet\": {\n    \t\"com.liferay.portlet.display-category\": \"category.sample\",\n    \t\"com.liferay.portlet.header-portlet-css\": \"/css/styles.css\",\n    \t\"com.liferay.portlet.instanceable\": true,\n    \t\"javax.portlet.name\": \"my_js_portlet_project\",\n    \t\"javax.portlet.security-role-ref\": \"power-user,user\",\n    \t\"javax.portlet.resource-bundle\": \"content.Language\"\n    },\n    ```\n \n3.  Deploy your bundle to apply the changes.\n\nGreat! Now you know how to configure your JavaScript widget's properties. \n\n## Related Topics\n\n- [Configuring System Settings and Instance Settings for Your JavaScript Widget](/docs/7-2/frameworks/-/knowledge_base/f/configuring-system-settings-and-instance-settings-for-your-js-widget)\n- [Localizing Your Widget](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-widget)\n- [Using Translation Features in Your JavaScript Widget](/docs/7-2/frameworks/-/knowledge_base/f/using-translation-features-in-your-widget)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/07-javascript-module-loaders/01-javascript-module-loaders-intro.markdown",
    "content": "---\nheader-id: javascript-module-loaders\n---\n\n# JavaScript Module Loaders\n\n[TOC levels=1-4]\n\nA JavaScript module encapsulates code into a useful unit that exports its \ncapability/value. This makes it easier to see the broader scope, easier to find \nwhat you're looking for, and keeps related code close together. A normal web \npage usually loads JavaScript files via HTML `script` tags. That's fine for \nsmall websites, but when developing large scale web applications, a more robust \norganization and loader is needed. A module loader lets an application load \ndependencies easily by specifying a string that identifies the JavaScript \nmodule's name. \n\nThis section shows how to load JavaScript modules in @product@. \n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/07-javascript-module-loaders/02-loading-amd-modules/01-loading-amd-modules-in-liferay-intro.markdown",
    "content": "---\nheader-id: loading-amd-modules-in-liferay\n---\n\n# Loading AMD Modules in Liferay\n\n[TOC levels=1-4]\n\nModularized JavaScript code is a specification for the JavaScript language \ncalled Asynchronous Module Definition, or AMD. The \n[Liferay AMD Module Loader](https://github.com/liferay/liferay-amd-loader#amd-module-loader) \nis the native loader that you can use to load your AMD modules. The steps below \ncover how to use the Liferay AMD Module Loader. \n\n| **Note:** While you can manually configure the AMD Loader, we recommend that you\n| use the\n| [liferay-npm-bundler](/docs/7-2/frameworks/-/knowledge_base/f/using-npm-in-your-portlets)\n| instead.\n\nFollow these steps to prepare your module for the Liferay AMD Module Loader:\n\n1.  Wrap your AMD module code with the `Liferay.Loader.define()` method, such as \n    the one shown below:\n\n    ```javascript\n    Liferay.Loader.define('my-dialog', ['my-node', 'my-plugin-base'], \n    function(myNode, myPluginBase) {\n       return {\n           log: function(text) {\n               console.log('module my-dialog: ' + text);\n           }\n       };\n    });\n    ```\n\n2.  You can modify the configuration to load the module when another module is \n    triggered or when a condition is met. The configuration below specifies that \n    this module should be loaded if another module requests the `my-test` module:\n\n    ```javascript\n    Liferay.Loader.define('my-dialog', ['my-node', 'my-plugin-base'], \n    function(myNode, myPluginBase) {\n       return {\n           log: function(text) {\n               console.log('module my-dialog: ' + text);\n           }\n       };\n    }, {\n       condition: {\n           trigger: 'my-test',\n           test: function() {\n               var el = document.createElement('input');\n\n               return ('placeholder' in el);\n           }\n       },\n       path: 'my-dialog.js'\n    });\n    ```\n\n    The Liferay AMD Loader uses the definition, along with the listed \n    dependencies, as well as any other configurations specified, to create a \n    `config.json` file. This configuration object tells the loader which modules \n    are available, where they are located, and what dependencies they require. \n    Below is an example of a generated `config.json` file:\n\n    ```json\n    {\n        \"frontend-js-web@1.0.0/html/js/parser\": {\n            \"dependencies\": []\n        },\n        \"frontend-js-web@1.0.0/html/js/list-display\": {\n            \"dependencies\": [\"exports\"]\n        },\n        \"frontend-js-web@1.0.0/html/js/autocomplete\": {\n            \"dependencies\": [\"exports\", \"./parser\", \"./list-display\"]\n        }\n    }\n    ```\n\n3.  Load your module in your scripts. Pass the module name to the \n    `Liferay.Loader.require` method. The example below loads a module called \n    `my-dialog`:\n\n    ```javascript\n    Liferay.Loader.require('my-dialog', function(myDialog) {\n        // your code here\n    }, function(error) {\n        console.error(error);\n    });\n    ```\n\n    | **Note:** By default, the AMD Loader times out in seven seconds. You can \n    | configure this value through System Settings. Open the Control Panel and \n    | navigate to *Configuration* &rarr; *System Settings* &rarr; *PLATFORM* &rarr; \n    | *Infrastructure*, and select *JavaScript Loader*. Set the *Module Definition \n    | Timeout* configuration to the time you want and click *Save*.\n\n## Related Topics\n\n- [Loading Modules with AUI Script](/docs/7-2/frameworks/-/knowledge_base/f/loading-modules-with-aui-script)\n- [Using npm in Your Portlets](/docs/7-2/frameworks/-/knowledge_base/f/using-npm-in-your-portlets)\n- [Loading Modules with AUI Script](/docs/7-2/frameworks/-/knowledge_base/f/loading-modules-with-aui-script)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/07-javascript-module-loaders/03-using-external-libraries/01-using-external-libraries-intro.markdown",
    "content": "---\nheader-id: using-external-javascript-libraries\n---\n\n# Using External JavaScript Libraries\n\n[TOC levels=1-4]\n\nYou can use external JavaScript libraries in your portlets (i.e., anything but \nMetal.js or jQuery, which are included by default). If you're the owner or \nhosting the external library, there are a few more requirements to load them \nwith the JavaScript Loaders. \n\nFollow these steps:\n\n1.  If you're the owner of the library, you should make sure that it supports \n    [UMD](https://github.com/umdjs/umd) (Universal Module Definition). You can \n    configure your code to support UMD with the template shown below:\n\n    ```javascript\n    // Assuming your \"module\" will be exported as \"mylibrary\"\n    (function (root, factory) {\n        if (typeof Liferay.Loader.define === 'function' && Liferay.Loader.define.amd) {\n            // AMD. Register as a \"named\" module.\n            Liferay.Loader.define('mylibrary', [], factory);\n        } else if (typeof module === 'object' && module.exports) {\n            // Node. Does not work with strict CommonJS, but\n            // only CommonJS-like environments that support module.exports,\n            // like Node.\n            module.exports = factory();\n        } else {\n            // Browser globals (root is window)\n            root.mylibrary = factory();\n      }\n    }(this, function () {\n\n        // Your library code goes here\n        return {};\n    }));\n    ```\n\n2.  If you're hosting the library (and not loading it from a CDN), you must hide \n    the Liferay AMD Loader to use your Library. Open the Control Panel, navigate \n    to *Configuration* &rarr; *System Settings*. \n\n3.  Click *JavaScript Loader* under *Platform* &rarr; *Infrastructure*. \n\n4.  Uncheck the `expose global` option. \n\n| **Note:**  Once this option is unchecked, you can no longer use the \n| `Liferay.Loader.define` or `Liferay.Loader.require` functions in your app. Also, \n| if you're using third party libraries that are AMD compatible, they could stop \n| working after unchecking this option because they usually use global functions \n| like `require()` or `define()`. \n\nGreat! Now you know how to adapt external libraries for Liferay's JavaScript \nLoaders. \n\n## Related Topics\n\n- [Liferay AMD Module Loader](/docs/7-2/frameworks/-/knowledge_base/f/loading-amd-modules-in-liferay)\n- [Using ES2015+ Modules in Your Portlet](/docs/7-2/frameworks/-/knowledge_base/f/using-esplus-modules-in-your-portlet)\n- [Loading Modules with AUI Script](/docs/7-2/frameworks/-/knowledge_base/f/loading-modules-with-aui-script)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/07-javascript-module-loaders/04-loading-modules-with-aui-script-tag/01-loading-modules-with-aui-script-intro.markdown",
    "content": "---\nheader-id: loading-modules-with-aui-script\n---\n\n# Loading Modules with AUI Script\n\n[TOC levels=1-4]\n\nThe `aui:script` tag is a JSP tag that loads JavaScript on the page, while \nensuring that certain resources are loaded before executing. \n\n| **Note:** AUI is deprecated and no longer in active development in \n| @product-ver@, but all the tags will remain fully functional in @product-ver@. \n| Eventually, these tags will be replaced with [Clay](https://clayui.com/) \n| tag counterparts.\n\nThe `aui:script` tag supports the following options:\n\n| Option | Description |\n| --- | --- |\n| `require` | Requires an AMD module to load with the [Liferay AMD Module Loader](https://github.com/liferay/liferay-amd-loader#amd-module-loader). |\n| `use` | Uses an AlloyUI/YUI module that is loaded via the YUI loader. |\n| `position` | The position the script tag is put on the page. Possible options are `inline` or `auto`. |\n| `sandbox` | Whether to wrap the script tag in an anonymous function. If set to `true`, in addition to the wrapping, `$` and `_` are defined for jQuery and underscore. |\n\nThis section covers how to load ES2015, Metal.js, and AUI modules with the AUI \nscript tag. \n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/07-javascript-module-loaders/04-loading-modules-with-aui-script-tag/02-loading-aui-modules-with-aui.markdown",
    "content": "---\nheader-id: loading-aui-modules-with-aui-script\n---\n\n# Loading AlloyUI Modules with AUI Script\n\n[TOC levels=1-4]\n\nFollow these steps to load modules with `<aui:script>`:\n\n1.  Add the following declaration to your portlet's JSP:\n\n    ```markup\n    <%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n    ```\n\n2.  Add the `<aui:script>` tag and use the `use` attribute to load AlloyUI/YUI \n    modules:\n\n    ```javascript\n    <aui:script use=\"aui-base\">\n        A.one('#someNodeId').on(\n            'click',\n            function(event) {\n                alert('Thank you for clicking.')\n            }\n        );\n    </aui:script>\n    ```\n\nThis loads the `aui-base` AlloyUI component and makes it available to the code \ninside the `aui:script`.\n\nIn the browser, the `aui:script` translates to the full JavaScript shown below:\n\n```javascript\n<script type=\"text/javascript\">\n    AUI().use(\"aui-base\",\n        function(A){\n            A.one('#someNodeId').on(\n                'click',\n                function(event) {\n                    alert('Thank you for clicking.')\n                }\n            );\n        }\n    );\n</script>\n```\n\nWonderful! Now you know how to load AUI/YUI modules in AUI scripts. \n\n## Related Topics\n\n- [Using External JavaScript Libraries](/docs/7-2/frameworks/-/knowledge_base/f/using-external-javascript-libraries)\n- [Loading AMD Modules](/docs/7-2/frameworks/-/knowledge_base/f/loading-amd-modules-in-liferay)\n- [Loading ES2015 and Metal.js Modules with AUI Script](/docs/7-2/frameworks/-/knowledge_base/f/loading-es2015-and-metal-modules-with-aui-script)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/07-javascript-module-loaders/04-loading-modules-with-aui-script-tag/03-loading-es2015-and-metal-modules-with-aui-script-tag.markdown",
    "content": "---\nheader-id: loading-es2015-and-metal-modules-with-aui-script\n---\n\n# Loading ES2015 and Metal.js Modules with AUI Script\n\n[TOC levels=1-4]\n\nFollow these steps to load your ES2015 and Metal.js modules with `<aui:script>`:\n\n1.  Add the following declaration to your portlet's JSP:\n\n    ```markup\n    <%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n    ```\n\n2.  Add the `<aui:script>` tag and use the `require` attribute to load ES2015 \n    and Metal.js modules:\n\n    ```javascript\n    <aui:script require=\"metal-clipboard/src/Clipboard\">\n        new metalClipboardSrcClipboard.default();\n    </aui:script>\n    ```\n\nalternatively, you can specify a variable for your module by adding \n`as variableName` after the module name, as shown in the example below:\n\n```javascript\n<aui:script require=\"metal-clipboard/src/Clipboard as myModule\">\n    new myModule.default();\n</aui:script>\n```\n\nThis resolves the dependencies of the registered module and loads them in order \nuntil all of them are satisfied and the requested module can be safely executed. \n\nIn the browser, the `aui:script` translates to the full JavaScript shown below:\n\n```javascript\n<script type=\"text/javascript\">\n    Liferay.Loader.require(\"metal-clipboard/src/Clipboard\", \n    function(metalClipboardSrcClipboard) {\n        (function() {\n            new metalClipboardSrcClipboard.default();\n        })()\n    }, function(error) {\n        console.error(error)\n    });\n</script>\n```\n\nGreat! Now you know how to load ES2015 and Metal.js modules in AUI scripts. \n\n## Related Topics\n\n- [Using External JavaScript Libraries](/docs/7-2/frameworks/-/knowledge_base/f/using-external-javascript-libraries)\n- [Loading AMD Modules](/docs/7-2/frameworks/-/knowledge_base/f/loading-amd-modules-in-liferay)\n- [Loading AUI, ES2015, and Metal.js Modules Together with AUI Script](/docs/7-2/frameworks/-/knowledge_base/f/loading-aui-es2015-and-metal-modules-with-aui-script)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/07-javascript-module-loaders/04-loading-modules-with-aui-script-tag/04-loading-aui-es2015-metal-modules-with-aui.markdown",
    "content": "---\nheader-id: loading-aui-es2015-and-metal-modules-with-aui-script\n---\n\n# Loading AUI, ES2015, and Metal.js Modules Together with AUI Script\n\n[TOC levels=1-4]\n\nYou may want to load an AUI module along with an ES2015 module or Metal.js \nmodule in an `aui:script`. The `aui:script` tag doesn't support both the \n`require` and `use` attributes in the same configuration. Not to worry though. \nYou can use the `aui:script`'s `require` attribute to load the ES2015 and \nMetal.js modules, while loading the AUI module(s) with the `AUI().use()` \nfunction within the script. \n\nFollow these steps to load your ES2015, Metal.js, and AUI modules together with \n`<aui:script>`:\n\n1.  Add the following declaration to your portlet's JSP:\n\n    ```markup\n    <%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n    ```\n\n2.  Add the `<aui:script>` tag and use the `require` attribute to load ES2015 \n    and Metal.js modules, while using the `AUI().use()` function to load AUI \n    modules, as shown in the example below:\n\n    ```javascript\n    <aui:script require=\"path-to/metal/module\">\n     AUI().use(\n        'liferay-aui-module', \n        function(A) {\n            let var = pathToMetalModule.default;\n        }\n    );\n    </aui:script>\n    ```\n\nGreat! Now you know how to load all your modules with `aui:script`. \n\n## Related Topics\n\n- [Using External JavaScript Libraries](/docs/7-2/frameworks/-/knowledge_base/f/using-external-javascript-libraries)\n- [Loading AMD Modules](/docs/7-2/frameworks/-/knowledge_base/f/loading-amd-modules-in-liferay)\n- [Loading ES2015 and Metal.js Modules with AUI Script](/docs/7-2/frameworks/-/knowledge_base/f/loading-es2015-and-metal-modules-with-aui-script)\n"
  },
  {
    "path": "en/developer/frameworks/articles/front-end-development/07-javascript-module-loaders/04-loading-modules-with-aui-script-tag/05-loading-bundled-npm-modules-in-your-portlets.markdown",
    "content": "---\nheader-id: loading-bundled-npm-modules-in-your-portlets\n---\n\n# Loading Bundled npm Modules in Your Portlets\n\n[TOC levels=1-4]\n\nOnce you've \n[exposed your modules](/docs/7-2/frameworks/-/knowledge_base/f/using-javascript-in-your-portlets), \nyou can use them in your portlet via the `aui:script` tag's `require` attribute. \nYou can load the npm module in your portlet using the \n`npmResolvedPackageName` variable, which is available by default since \n@product-ver@. You can then create an alias to reference it in your portlet. \n\nFollow these steps:\n\n1.  Provide a `Web-ContextPath` in your bundle's `bnd.bnd` file:\n\n    ```properties\n    Web-ContextPath: /my-module-web\n    ```\n\n2.  Make sure the `<liferay-frontend:defineObjects />` tag is included in your \n    portlet's `init.jsp`. This makes the `npmResolvedPackageName` variable \n    available, setting it to your project module's resolved name. For instance, \n    if your module is called `my-module` and is at version `2.3.0`, the implicit \n    variable `npmResolvedPackageName` is set to `my-module@2.3.0`. This lets you \n    prefix any JS module `require` or soy component rendering with this \n    variable.\n\n3.  Use the `npmResolvedPackageName` variable along with the relative path to \n    your JavaScript module file to create an alias in the `<aui:script>`'s \n    `require` attribute. An example configuration is shown below:\n\n    ```markup\n    <aui:script \n      require='<%= npmResolvedPackageName + \n      \"/js/my-module.es as myModule\" %>'>\n    </aui:script>\n    ```\n\n4.  Use the alias inside the `aui:script` to refer to your module:\n\n    ```markup\n    <aui:script \n      require='<%= npmResolvedPackageName + \n      \"/js/my-module.es as myModule\" %>'>\n      \n        myModule.default();\n    </aui:script>\n    ```\n\nNow you know how to use an npm module's package! \n\n## Related Topics\n\n- [Obtaining an OSGi bundle's Dependency npm Package Descriptors](/docs/7-2/frameworks/-/knowledge_base/f/obtaining-dependency-npm-package-descriptors)\n- [liferay-npm-bundler](/docs/7-2/reference/-/knowledge_base/r/liferay-npm-bundler)\n- [How liferay-npm-bundler Publishes npm Packages](/docs/7-2/reference/-/knowledge_base/r/how-the-liferay-npm-bundler-publishes-npm-packages)\n"
  },
  {
    "path": "en/developer/frameworks/articles/info-framework/01-the-info-framework-intro.markdown",
    "content": "---\nheader-id: the-info-framework\n---\n\n# The Info Framework\n\n[TOC levels=1-4]\n\n@product-ver@ introduces the Info Framework to provide a greater degree of \nextensibility for the most common needs of retrieving, processing and \ndisplaying any type of information. A key aspect of the Info Framework is that \nit makes no assumptions about the source of the data or how it is represented \nin memory (like which Java class the information is from). It can work with \ninformation stored in the database, created through some process in memory or \nretrieved from an external source. \n\nIn @product-ver@, the Info Framework still has limited functionality, but it \nsets the foundation for obtaining and displaying information from external \nsystems or custom data models in Liferay. It also provides flexibility in\ncustomizing how any piece of information is displayed.\n\nThe Info Framework is lightweight. By design, it does not require all the \ninformation to implement any specific interface. This means that you can use it \nwith any existing Java class, even if you don't have access to modify it. It \ncomprises a collection of loosely coupled micro-frameworks, so that developers\ncan choose which features to use and ignore the others. This lowers the learning\ncurve and minimizes work for developers.\n\n| **Note:** Liferay veterans may notice similarities between the [Asset Framework](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework) and Info \n| Frameworks. The Info Framework can be considered a generalization of the \n| Asset Framework and its design has considered lots of lessons learned from \n| the Asset Framework. In particular, the Info Framework provides many similar \n| characteristics (such as rendering of information) but with fewer requirements \n| (such as having an `AssetEntry` in the database). We have also made sure the \n| two frameworks play well together so that when the Asset Framework is used, \n| for rendering an asset, the renderer is also available through \n| the Info framework. The Info framework is not meant to be a full replacement \n| for the Asset Framework, but if the Asset Framework seems too complex for \n| your needs, the Info Framework might be a better fit for you.\n\n## Using the Info Framework\n\nThe Info Framework helps generalize information handling. Custom applications\ncan use it to make them more generic and extensible. \n\nSome of the out-of-the-box Liferay features use it to achieve that same goal. In\nparticular, @product@ 7.2 uses it in two ways:\n\n*  The Asset Publisher can display Assets from Information Lists defined by \n   the Info Framework.\n\n*  Display Pages, which previously only worked for an `AssetEntry`, can now \n   leverage the `InfoFramework` to create display pages for any type of \n   information that can be represented by a Java class. Developers can add\n   support for display pages for various entities like Orders, Categories, and\n   Events that are not Assets.\n\nThere are currently two tools provided by the Info Framework: Information Item \nRenderers and Information List Providers. You can create an Information List \nProvider to obtain a list of information items from a source, or create an \nInformation Item Renderer to provide a custom renderer for any type of \ninformation. These two features can be used together or separately.\n\n### List Providers\n\nInformation List Providers obtain a list of information items from a source. To\ndo this, a developer must implement the `InfoListProvider` interface and\nprovide the necessary logic for retrieving the information from its source. By\nproviding an implementation of the `InfoListProvider` interface, developers can\nprovide programmatic retrieval of information of any type, as long as it can be\nrepresented through a Java class. \n\n`InfoListProvider` has four methods to implement:\n\n`getLabel()` provides the label that is displayed for this provider in the UI of\napplications like the Asset Publisher.\n\n`getInfoList()` provides the information list. This method has two variants:\na plain list or a list with pagination and sorting.\n\n`getInfoListCount()` provides total number of items. This is needed for the\npaginated variant of `getInfoList`.\n\nFor an example of how to create Information List providers, see \n[Creating Information List Providers](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-information-list-provider).\n\n### Item Renderers\n\nDevelopers can create custom renderers for any type of information. To do this, \na developer must provide an implementation of the `InfoItemRenderer` interface \nto provide programmatic rendering of information. It can be any kind of \ninformation as long as it can be represented through a Java class. You can \ncreate multiple renderers for a single type of information.\n\nInternally, Liferay's Display Pages use this from the Content component. When \nit is added to a display page template, this component renders whatever \npiece of information is shown through that template (whether it is Content in \nthe strict sense or some other entity type). It is rendered by the first \n`InfoItemRenderer` class registered that entity. Information Item Renderers will \nbe leveraged further in future Liferay versions.\n\nTo create an Information Item Renderer you must create a class that implements \n`InfoItemRenderer` and registers it as a component. Inside that class, you \nneed a `render()` method that contains your logic. To learn about Information\nItem Renderers, see \n[Creating Information Item Renderers](/docs/7-2/frameworks/-/knowledge_base/f/custom-rendering-of-information-with-infoitemrenderer).\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/info-framework/02-creating-information-list-provider.markdown",
    "content": "---\nheader-id: creating-an-information-list-provider\n---\n\n# Creating an Information List Provider\n\n[TOC levels=1-4]\n\nTo demonstrate Information List Providers, follow the instructions below to \nimplement an `InfoListProvider` for the most viewed asset entries. In this case \nthe list shows a list of `AssetEntry` instances. Since they already have their\nown renderer, they can appear in the Asset Publisher with no additional\nchanges. If you create a provider for a custom class, you must also render it.\n\n1.  Create a [module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project)\n    named asset-entry-info-list-provider.\n\n2.  Create a package inside the module named `com.liferay.docs.info.provider`.\n\n3.  Inside the package, create a class named `AssetEntryInfoListProvider` that\n    implements `InfoListProvider` and registers it as a component:\n\n    ```java\n    @Component(service = InfoListProvider.class)\n      public class AssetEntryInfoListProvider implements InfoListProvider<AssetEntry> {\n    \n    }\n    ```\n \n4.  Next, add the necessary `@Reference` that you need for the logic of\n    retrieving assets to the bottom of the class.\n\n    ```java\n    @Reference\n    AssetEntryLocalService _assetEntryLocalService;\n    ```\n\n5.  Then implement `getInfoList` which returns just the list.\n \n    ```java\n    @Override\n    public List<AssetEntry> getInfoList(\n     InfoListProviderContext infoListProviderContext) {\n\n     return _assetEntryLocalService.getTopViewedEntries(\n        new String[0], false, 0, 20);\n    }\n    ```\n\n    Descending order and a maximum of 20 items to return is hardcoded. \n \n6.  Now implement the second method, which provides greater control over how \n    items are returned to the provider.\n\n     ```java\n     @Override\n     public List<AssetEntry> getInfoList(\n       InfoListProviderContext infoListProviderContext, Pagination pagination,\n       Sort sort) {\n\n       return _assetEntryLocalService.getTopViewedEntries(\n          new String[0], !sort.isReverse(), pagination.getStart(),\n          pagination.getEnd());\n     }\n     ```\n\n7.  Provide a method to get a full count of info list items. \n\n     ```java\n     @Override\n     public int getInfoListCount(\n     InfoListProviderContext infoListProviderContext) {\n\n         Company company = infoListProviderContext.getCompany();\n\n         return _assetEntryLocalService.getCompanyEntriesCount(\n         company.getCompanyId());\n     }\n     ```\n\n8.  Finally, add a method that provides a display label for the list.\n\n    ```java\n    @Override\n    public String getLabel(Locale locale) {\n     return \"Most Viewed Content\";\n     }\n     ```\n\nThe completed class should look like this:\n\n```java\n@Component(service = InfoListProvider.class)\n  public class AssetEntryInfoListProvider implements InfoListProvider<AssetEntry> {\n\n           @Override\n           public List<AssetEntry> getInfoList(\n             InfoListProviderContext infoListProviderContext) {\n\n             return _assetEntryLocalService.getTopViewedEntries(\n                new String[0], false, 0, 20);\n           }\n\n         @Override\n         public List<AssetEntry> getInfoList(\n           InfoListProviderContext infoListProviderContext, Pagination pagination,\n           Sort sort) {\n\n           return _assetEntryLocalService.getTopViewedEntries(\n              new String[0], !sort.isReverse(), pagination.getStart(),\n              pagination.getEnd());\n         }\n\n         @Override\n         public int getInfoListCount(\n         InfoListProviderContext infoListProviderContext) {\n\n             Company company = infoListProviderContext.getCompany();\n\n             return _assetEntryLocalService.getCompanyEntriesCount(\n             company.getCompanyId());\n         }\n\n           @Override\n           public String getLabel(Locale locale) {\n             return \"Most Viewed Content\";\n             }\n\n@Reference\nAssetEntryLocalService _assetEntryLocalService;\n}\n``` \n\nThis class is now ready to go! If you deploy it, it shows the \"Most \nViewed Content\" in any Asset Publisher.\n\n## Next steps\n\nThis example is pretty simplistic and probably not useful in real world cases. \nTo begin with, you may want to scope the search to the current site. You can \nalso add more advanced filter criteria or provide a configuration for the \nprovider using Liferay's configuration framework. \n\nAs mentioned, it is also possible to implement providers for custom types. The \nfollowing code shows a partial example of a provider for a custom `MyOrder` \nclass:\n\n```java\n@Component(service = InfoListProvider.class)\npublic class MyOrderProvider implements InfoListProvider<MyOrder> {\n\n    @Override\n    public List<MyOrder> getInfoList(\n        InfoListProviderContext infoListProviderContext, Pagination pagination,\n        Sort sort) {\n\n        return _myOrderLocalService.getOrders(\n            [...], !sort.isReverse(), pagination.getStart(),\n            pagination.getEnd());\n        }\n\n    [..]\n\n    @Reference\n    MyOrderLocalService _myOrderLocalService;\n```\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/info-framework/03-creating-information-item-renderer.markdown",
    "content": "---\nheader-id: custom-rendering-of-information-with-infoitemrenderer\n---\n\n# Custom rendering of information with `InfoItemRenderer`\n\n[TOC levels=1-4]\n\nTo demonstrate the `InfoItemRenderer`, implement a class that can render \ninformation provided through a custom class called `MyOrder`. \n\n1.  Create a [module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project) named `my-order`.\n\n2.  In `my-order`, create a package named `com.liferay.docs.info.myorder`\n\n3.  In the package, create a class that implements `InfoItemRenderer` and \n    register it as a component.\n\n    ```java\n    @Component(service = InfoItemRenderer.class)\n    public class MyOrderRenderer implements InfoItemRenderer<MyOrder> {\n        @Override\n        public void render(\n           MyOrder myOrder, HttpServletRequest httpServletRequest,\n           HttpServletResponse httpServletResponse) {\n\n        }\n    }\n    ```\n\n4.  Next you must add the logic for the `render()` method. \n\n    ```java\n    @Override\n    public void render(\n       MyOrder myOrder, HttpServletRequest httpServletRequest,\n       HttpServletResponse httpServletResponse) {\n\n       StringBundler sb = new StringBundler(3);\n\n       sb.append(\"<ul>\");\n       sb.append(\"<li>By: \" + myOrder.getBy());\n       sb.append(\"<li>When: \" + myOrder.getWhen());\n       sb.append(\"<li>Items: \" + myOrder.getItems());\n       sb.append(\"</ul>\");\n\n       try {\n          PrintWriter printWriter = httpServletResponse.getWriter();\n\n          printWriter.write(sb.toString());\n       }\n       catch (IOException ioe) {\n          throw new RuntimeException(ioe);\n       }\n    }\n    ```\n\nFor this example you rendered everything through a `StringBundler`. In more \ncomplex cases, you would use JSPs or another templating technology.\n\nThe renderer is ready for use! In @product-ver@, Info Item Renderers are not\nwidely used, but the usages and application will grow in future releases.\n"
  },
  {
    "path": "en/developer/frameworks/articles/info-framework/04-custom-application-providers.markdown",
    "content": "---\nheader-id: using-providers-with-custom-applications\n---\n\n# Using Providers with Custom Applications\n\n[TOC levels=1-4]\n\nImagine a widget that can display lists of orders. You can use the Info\nFramework so that it shows any list of orders provided through\n`InfoListProvider`. \n\nFirst you must obtain a list of all available providers for the desired type,\nand then you would obtain a specific provider through that list.\n\n1.  The list of all available providers for `MyOrder`, can be obtained done by \n    using the `InfoListProviderTracker`:\n\n    ```java\n    @Reference\n    InfoListProviderTracker _infoListProviderTracker;\n    ```\n\n    Once a tracker is available, obtaining the list is as simple as invoking\n    `getInfoListProviders()`:\n\n    ```java\n    _infoListProviderTracker.getInfoListProviders(MyOrder.class);\n    ```\n\n    When the user selects an item from this list, you can store the class's name.\n\n2.  When a specific provider is desired it can be obtained through its class name as follows:\n\n    ```java\n    _infoListProviderTracker.getInfoListProvider(infoListProviderClassName);\n    ```\n\n## Leveraging renderers from a custom application\n\nUsing renderers from a custom application is almost identical to using \nproviders. Here is the equivalent code to what you've seen previously:\n\n```java\n_infoItemRendererTracker.getInfoItemRenderers(MyOrder.class.getName());\n\nString infoItemRendererClassName = MyOrderRenderer.class.getName();\n_infoItemRendererTracker.getInfoItemRenderer(infoItemRendererClassName);\n```\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/liferay-forms/01-intro.markdown",
    "content": "---\nheader-id: liferay-forms\n---\n\n# Liferay Forms\n\n[TOC levels=1-4]\n\nThe Liferay Forms application is a full-featured form building tool for\ncollecting data. There's lots of built-in functionality, and for the pieces\nyou're missing, there's lots of extensibility.\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/liferay-forms/02-form-serialization/01-intro.markdown",
    "content": "---\nheader-id: form-serialization-with-the-ddm-io-api\n---\n\n# Form Serialization with the DDM IO API\n\n[TOC levels=1-4]\n\nWhen a form creator saves a form in the Liferay Forms application, the Form\nobject is _serialized_ (converted) into JSON for storage in the @product@\ndatabase. That's the default behavior; if you need the form in a different\nformat, you must write your own serialization and deserialization code. Why\nwould you want to do that? Maybe you think JSON storage is not secure, or you\nhave another tool that can consume the form if it's in YAML. Whatever your\nreasons, the form can be stored in any format as long as you write\na `DDMFormSerializer` and its corresponding `DDMFormDeserializer` with the\nproper logic.\n\nFirst consider what form data looks like by default, in JSON. A simple form, _My\nForm_, with one text field, _Full Name_, is first created as a `DDMForm` Java\nobject, then _serialized_ into JSON for storage in the @product@ database when\nsaved.\n\n```json\n{\n    \"availableLanguageIds\":[\"en_US\"],\n    \"successPage\":{\"body\":{},\n    \"title\":{},\n    \"enabled\":false},\n    \"defaultLanguageId\":\"en_US\",\n    \"fields\":[{\n        \"autocomplete\":false,\n        \"ddmDataProviderInstanceId\":\"[]\",\n        \"dataType\":\"string\",\n        \"predefinedValue\":{\"en_US\":\"\"},\n        \"tooltip\":{\"en_US\":\"\"},\n        \"readOnly\":false,\n        \"label\":{\"en_US\":\"Full Name\"},\n        \"type\":\"text\",\n        \"required\":false,\n        \"showLabel\":true,\n        \"displayStyle\":\"singleline\",\n        \"fieldNamespace\":\"\",\n        \"indexType\":\"keyword\",\n        \"visibilityExpression\":\"\",\n        \"ddmDataProviderInstanceOutput\":\"[]\",\n        \"repeatable\":false,\n        \"name\":\"FullName\",\n        \"options\":[{\"label\":{\"en_US\":\"Option\"},\"value\":\"Option\"}],\n        \"localizable\":true,\n        \"tip\":{\"en_US\":\"\"},\n        \"placeholder\":{\"en_US\":\"\"},\n        \"dataSourceType\":\"\",\n        \"validation\":{\"expression\":\"\",\"errorMessage\":\"\"}\n    }]\n}\n```\n\nFrom its initial state as a `DDMForm` Java object, the form is _serialized_ into\nJSON format, and upon retrieval from the database, it's _deserialized_: \nthe JSON object representing the form is translated back into a `DDMForm` Java\nobject, with all its requisite fields. For example, the JSON for the above\nexample holds each form field in the `fields` attribute. To translate\nthis back into the necessary `DDMForm` object, first parse the data contained in\nthe JSON object into an actual form field using your deserialization logic.\nHere's the logic from `DDMFormJsonDeserializer` that parses the JSON `\"fields\"`\nelement into a list of `DDMFormFields`:\n\n```java\nprotected List<DDMFormField> getDDMFormFields(JSONArray jsonArray)\n    throws PortalException {\n\n    List<DDMFormField> ddmFormFields = new ArrayList<>();\n\n    for (int i = 0; i < jsonArray.length(); i++) {\n        DDMFormField ddmFormField = getDDMFormField(\n            jsonArray.getJSONObject(i));\n\n        ddmFormFields.add(ddmFormField);\n    }\n\n    return ddmFormFields;\n}\n```\n\nNow calling `DDMForm.setDDMFormFields(ddmFormFields)` in the deserializer\ncompletes the translation process from the JSON array back to a `DDMFormField`\nobject that the `DDMForm` needs.\n\nIf you'd like to store forms in a different format, provide custom\n_serialization_ and _deserialization_ functionality.\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/liferay-forms/02-form-serialization/02-serilaizing-forms.markdown",
    "content": "---\nheader-id: serializing-forms\n---\n\n# Serializing Forms\n\n[TOC levels=1-4]\n\nThe DDM IO API serializes and deserializes forms using a request/response\nstructure. The example here creates a serializer for saving form data in\n[YAML](https://yaml.org) format. The same principles shown here apply to\nwriting a deserializer. \n\nTo serialize form data into YAML:\n\n1.  Create a class that implements `DDMFormSerializer`:\n\n    ```java\n    @Component(immediate = true, property = \"ddm.form.serializer.type=yaml\") public\n    class DDMFormYamlSerializer implements DDMFormSerializer { .....  }\n    ```\n\n    The property `ddm.form.serializer.type=yaml` marks the Component so that\n    `DDMFormSerializerTracker` can find the YAML serializer.\n\n2.  Add the serializing logic to the overridden `serialize` method. It takes a\n    `DDMFormSerializerSerializeRequest` and returns a\n    `DDMFormSerializerSerializeResponse` with the serialized string in it.\n\n    ```java\n    @Override public DDMFormSerializerSerializeResponse serialize(\n    DDMFormSerializerSerializeRequest ddmFormSerializerSerializeRequest) {\n\n            DDMForm ddmForm = ddmFormSerializerSerializeRequest.getDDMForm(); \n\n            ...YOUR CODE FOR BUILDING A YAML OBJECT GOES HERE ...  \n\n            DDMFormSerializerSerializeResponse.Builder builder = \n                DDMFormSerializerSerializeResponse.Builder.newBuilder(yamlObject.toString());\n\n            return builder.build(); }\n    ```\n\nThis is what you need to create your serializer. Of course, `YOUR CODE FOR\nBUILDING A YAML OBJECT GOES HERE` requires some explanation. While you can\ndo whatever you want here, there are several things you really ought to\ndo:\n\n**Add the available Language IDs:** Since you have the `DDMForm` object from the\n    request, call `ddmForm.getAvailableLocales()`.\n\n**Add the default Language ID:** Get this from the `DDMForm` object by calling\n    `ddmForm.getDefaultLocale()`.\n\n**Add the Form Fields:** Get these from the `DDMForm` object by calling\n    `ddmForm.getDDMFormFields()`.\n\n**Add any Form Rules:** Get them form the `DDMForm` object with\n    `ddmForm.getDDMFormRules()`.\n\n**Add Success Page Settings:** Get these from the `DDMForm` with\n    `ddmForm.getDDMFormSuccessPageSettings()`.\n\nAll these are done in the default form serializer, `DDMFormJSONSerializer`.\n\nIf you have the @product@ source code, you can find the default serializer in\n\n    modules/apps/dynamic-data-mapping/dynamic-data-mapping-io/src/main/java/com/liferay/dynamic/data/mapping/io/internal/DDMFormJSONSerializer.java\n\nYou didn't create serialization code for no reason. You'll want to call it from\nsomewhere.\n\n## Calling the Serializer \n\nTo get properly serialized form content, follow these steps:\n\n1.  Get the serializer from the `DDMFormSerializerTracker`, passing in the value\n    of the `ddm.form.serializer.type` property.\n\n2.  Construct a `DDMFormSerializerSerializeRequest` object using its nested\n    static `Builder` class.\n\n3.  Call the `serialize` method you wrote in the last section to create the\n    `DDMFormSerializerSerializeResponse`, passing the\n    `DDMFormSerializerSerializeRequest`\n    object, via a call to the `Builder`'s `build` method.\n\n4.  Get the serialized form content from the\n    `DDMFormSerializerSerializeResponse` by calling its `getContent` method.\n\nHere's a code example:\n\n```java\nDDMFormSerializer ddmFormSerializer =\nddmFormSerializerTracker.getDDMFormSerializer(\"yaml\");\n\nDDMFormSerializerSerializeRequest.Builder builder =\nDDMFormSerializerSerializeRequest.Builder.newBuilder(ddmForm);\n\nDDMFormSerializerSerializeResponse ddmFormSerializerSerializeResponse =\nddmFormSerializer.serialize(builder.build());\n\nddmFormSerializerSerializeResponse.getContent();\n```\n\nYou can create a serializer for any format that can be saved in the database as\na String. Once you create the serializer, make it the default by changing the\nstorage format in the Form's Settings menu.\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/localization/01-localization-intro.markdown",
    "content": "---\nheader-id: localization\n---\n\n# Localization\n\n[TOC levels=1-4]\n\nIf you're writing a Liferay application, you're probably a genius who is also\nreally cool, which means your application will be used throughout the entire\nworld. At least, if its messages can be translated into their language, it will.\nThankfully, Liferay facilitates creating and using message translations and\nadapting to cultural conventions for user names and initials. \n\nYou can leverage Liferay's localization framework or use standard resource\nbundles to localize your app. The localization framework uses properties files\n(the same as any resource bundle) but leverages a default properties file called\n`Language.properties` to propagate messages (language keys) to properties files\nfor all your locales. For example, when you add a new message to the\n`Language.properties` file and run Language Builder, it propagates the message\nto your locale files. All you must do is translate the message in each locale\nfile, manually or automatically using Language Builder. \n\nLanguage Builder integrates the Microsoft Text Translator API to translate each\nlocale file's messages from your default locale to the respective locale. A\nmachine's translation is no substitute for a human's, of course, but the\nautomatic translation gives you a base to work from. \n\nIt's common to use the same messages in multiple apps. @product@ provides these\nmessage sharing features:\n\n-   @product@'s messages (and their translations) are available for all your \n    apps to use. JSP tags such as `<liferay-ui:message ... />` let you use all\n    @product@ messages. \n\n-   Language modules are easy to use in all your apps. They're great for \n    centralizing messages in all your locales. \n\nLastly, @product@ provides settings for adapting your app to cultures:\n\n-   Naming conventions for users\n\n-   Initial conventions for user avatars \n\n-   Text direction settings (left-to-right or right-to-left)\n\nLocalization is important to all site users. Liferay helps you get it right!\nStart with localizing your application using Liferay's localization framework. \n"
  },
  {
    "path": "en/developer/frameworks/articles/localization/02-localizing-your-application.markdown",
    "content": "---\nheader-id: localizing-your-application\n---\n\n# Localizing Your Application\n\n[TOC levels=1-4]\n\nLiferay's localization framework helps you create and use localized messages\nin minutes. You create your messages in a default properties file called\n`Language.properties` and localize them in properties files that use the\nconvention `Language_xx.properties`, where `xx` is the locale code. After\ndeploying your app, the messages are available to your templates. Liferay's JSP\ntags, such as `<liferay-ui:message .../>` display them in the user's current\nlocale automatically, without requiring you to access\n`ResourceBundle` or `Locale` objects explicitly. Here are the steps: \n\n1.  Create a default language properties file called `Language.properties` in \n    your project's resource bundle folder. \n\n    | Project Type              | Resource Bundle Folder |\n    | ------------------------- | ---------------------- |\n    | Bean Portlet              | `src/main/resources/content/` |\n    | JSF Portlet               | `src/main/resources/` |\n    | Liferay MVC Portlet       | `src/main/resources/content/` |\n    | PortletMVC4Spring Portlet | `src/main/java/resources/content/` |\n    | Angular Widget            | `features/localization/` |\n    | React Widget              | `features/localization/` |\n    | Vue.js Widget             | `features/localization/` |\n\n2.  Specify your language properties (language keys) using key/value pairs. For \n    example, create a friendly greeting. \n\n    ```properties\n    howdy-partner=Howdy, Partner!\n    ```\n\n3.  Configure your app's resource bundle and the locales you're supporting. The \n    locales your @product@ instance supports are specified in its [portal\n    properties](/docs/7-2/deploy/-/knowledge_base/d/portal-properties) file\n    (here are @product@'s [default\n    locales]((@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Languages%20and%20Time%20Zones)).\n    For example, these configurations support translations for English and\n    Spanish locales: \n\n    **`@PortletConfiguration` class annotation:** Can be used in Portlet 3.0\n    portlets such as Bean Portlets.\n\n    ```java\n    @PortletConfiguration (\n        ...\n        resourceBundle=\"content.Language\",\n        supportedLocales = {\"en\", \"es\"}\n    )\n    ```\n\n    **Portlet descriptor `portlet.xml`:** Can be used in any portlet WAR. \n\n    ```xml \n    <portlet>\n    ...\n    <supported-locale>en</supported-locale>\n    <supported-locale>es</supported-locale>\n    <resource-bundle>content.Language</resource-bundle>\n    ...\n    </portlet>\n    ```\n\n    **`@Component` class annotation:** Can be used in a portlet module such as a\n    Liferay MVC Portlet. \n\n    ```java\n    @Component (\n        ...\n        property = {\n            ...\n            \"javax.portlet.supported-locale=en\",\n            \"javax.portlet.supported-locale=es\",\n            \"javax.portlet.resource-bundle=content.Language\"\n        }\n    )\n    ```\n\n4.  Create language properties for a locale. For demonstration purposes, create \n    one manually. [Automatically generating\n    translations](/docs/7-2/frameworks/-/knowledge_base/f/automatically-generating-translations)\n    is discussed later.\n\n    For example, create a Spanish translation by copying `Language.properties`\n    to `Language_es.properties` and translating the property values to Spanish. \n\n    ```properties \n    howdy-partner=Hola, Compañero!\n    ```\n\n5.  In your front-end template code, use the language property. For example, a \n    JSP could use the `howdy-partner` property via the\n    [`<liferay-ui:message />`](@platform-ref@/7.2-latest/taglibs/util-taglib/liferay-ui/message.html) tag. \n\n    ```markup\n    <%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\" %> \n    ...\n    <liferay-ui:message key=\"howdy-partner\" />\n    ...\n    ```\n\n    | **Tip:** The\n    | [`liferay-ui`](@platform-ref@/7.2-latest/taglibs/util-taglib/liferay-ui/tld-summary.html)\n    | tag library has several tags (e.g., `alert`, `error`, and `message`) that\n    | accept language keys. \n\n6.  Deploy your application and view it in different locales. For example, you \n    could view the app locally in Spanish by specifying the `es` locale code in\n    the URL (e.g., `http://localhost:8080/es/...`). \n\nCongratulations on a great start to localizing your application!\n\nNext, you can explore [generating translations automatically](/docs/7-2/frameworks/-/knowledge_base/f/automatically-generating-translations) \nor [create a language module](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-language-module) \nfor using language keys across applications. \n\n## Related Topics \n\n[Automatically Generating Translations](/docs/7-2/frameworks/-/knowledge_base/f/automatically-generating-translations)\n\n[Using Language Modules](/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module)\n\n[Using @product@'s Language Settings](/docs/7-2/frameworks/-/knowledge_base/f//docs/7-1/tutorials/-/knowledge_base/t/using-liferays-language-settings)\n"
  },
  {
    "path": "en/developer/frameworks/articles/localization/03-localization-settings.markdown",
    "content": "---\nheader-id: using-liferays-localization-settings\n---\n\n# Using Liferay's Localization Settings\n\n[TOC levels=1-4]\n\nYou can customize a given locale's default language settings by overriding the \nproperties that control those settings. For instructions on this, see \n[Overriding Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys). \nHere, you'll learn which properties correspond to common language settings. \n\nSo what all can be customized? This is an excellent question! Consider these \nexamples: \n\n-   In the add and edit user forms, you can configure the name fields that are \n    displayed and the field values available in select fields. For example, you \n    can leave out the middle name field or alter the prefix selections. \n\n-   You can also control the directionality of content and messages (left to \n    right or right to left). \n\nLanguage properties exist in `Language_xx.properties` files, where `xx` \nrepresents the locale. For example, `Language_es.properties` contains the \nproperties for Spanish, `Language_en.properties` contains the properties for \nEnglish, and so on. \n\nThe default (core) language properties are in `Language.properties`. \n**Do not edit this file.** You can, however, open it to view the default \nlanguage settings. There are two ways to do so: \n\n1.  From Liferay Portal's source code. Navigate to \n\n        liferay-portal/portal-impl/src/content/Language.properties\n\n2.  From a bundle's `portal-impl.jar`.\n\n        [Liferay Home]/tomcat-[version]/webapps/ROOT/WEB-INF/lib/portal-impl.jar\n\n    Open the `content` folder in the JAR to find the language files. \n\nThe first section in the core `Language.properties` file is labeled *Language \nSettings* and contains language properties that begin with `lang`: \n\n```properties\n##\n## Language Settings\n##\n\nlang.dir=ltr\nlang.line.begin=left\nlang.line.end=right\nlang.user.default.portrait=initials\nlang.user.initials.field.names=first-name,last-name\nlang.user.name.field.names=prefix,first-name,middle-name,last-name,suffix\nlang.user.name.prefix.values=Dr,Mr,Ms,Mrs\nlang.user.name.required.field.names=last-name\nlang.user.name.suffix.values=II,III,IV,Jr,Phd,Sr\n```\n\nTo use the language settings mentioned here, you must have a module. See the \narticles on \n[overriding language keys](/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys)\nto set up a module with the following characteristics: \n\n-   Contains an implementation of `ResourceBundle` that is registered in the \n    OSGi runtime. \n\n-   Contains a `Language_xx.properties` file for the locale whose properties you \n    want to override. \n\n## Localizing User Names\n\nNaming conventions can differ between locales. For example, users in some \nlocales have more than one last name. You can therefore change the user name \nproperties to fit the given locale. The properties for changing user name \nsettings begin with `lang.user.name`. \n\nUser name fields are configurable in the following ways: \n\n-   Remove certain name fields and make others appear more than once. Some \n    locales need more than one last name, for example. \n\n    ```properties\n    lang.user.name.field.names=prefix,first-name,middle-name,last-name,suffix\n    ```\n\n-   Change the prefix and suffix values for a locale.\n\n    ```properties\n    lang.user.name.prefix.values=Dr,Mr,Ms,Mrs\n    lang.user.name.suffix.values=II,III,IV,Jr,Phd,Sr\n    ```\n\n-   Specify which fields are required.\n\n    ```properties\n    lang.user.name.required.field.names=last-name\n    ```\n\nA user's first name is mandatory. Because of this, take these two points into \nconsideration when configuring a locale's user name settings: \n\n-   The `first-name` field can't be removed from the field names list.\n\n    ```properties\n    lang.user.name.field.names=prefix,first-name,middle-name,last-name,suffix\n    ```\n\n-   Because a first name is required, it's always implicitly included in the\n    `required field names` property: \n\n    ```properties\n    lang.user.name.required.field.names=last-name\n    ```\n\n    Therefore, any fields you enter here are *in addition to* the first name \n    field. Last name is required by default, but you can disable it by deleting \n    its value from the property: \n\n    ```properties\n    lang.user.name.required.field.names=\n    ```\n\n    In that case, only a first name would be required.\n\nFor most of the \n[locales enabled by default](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Languages%20and%20Time%20Zones), \nthe user name properties are tailored to each locale. The default locales are \nspecified by the `locales.enabled` property: \n\n```properties\nlocales.enabled=ar_SA,ca_ES,zh_CN,nl_NL,en_US,fi_FI,fr_FR,de_DE,hu_HU,ja_JP,pt_BR,es_ES,sv_SE\n```\n\nFor example, here are the English (`Language_en.properties`) properties for \nsetting user name fields: \n\n```properties\nlang.user.name.field.names=prefix,first-name,middle-name,last-name,suffix\nlang.user.name.prefix.values=Dr,Mr,Ms,Mrs\nlang.user.name.required.field.names=last-name\nlang.user.name.suffix.values=II,III,IV,Jr,Phd,Sr\n```\n\n![Figure 1: The user name settings impact the appearance of user information and forms.](../../images/english-user-name-fields.png)\n\nCompare those to the Spanish (`Language_es.properties`) settings:\n\n```properties\nlang.user.name.field.names=prefix,first-name,last-name\nlang.user.name.prefix.values=Sr,Sra,Sta,Dr,Dra\nlang.user.name.required.field.names=last-name\n```\n\n![Figure 2: The Spanish user name settings omit the suffix and middle name fields.](../../images/spanish-user-name-fields.png)\n\nThe biggest difference between the English and Spanish form fields is that the \nmiddle name and suffix fields are omitted in the Spanish configuration. Other \ndifferences include the specific prefix values. ¡Muy excelente! \n\n## Identifying User Initials\n\nThe default avatar displays a user's initials. Some cultures use initials\ndifferently, so there's a way to configure them in the appropriate \n`Language_xx.properties` file.\n\n```properties\nlang.user.default.portrait=initials\nlang.user.initials.field.names=first-name,last-name\n```\n\nThe `lang.user.default.portrait` property sets the type of portrait to use for\nusers. This can be set to `initials` or `image`. If set to `image`, the default\nimages defined by the `image.default.user.female.portrait` or\n`image.default.user.male.portrait` properties residing in the\n`portal.properties` file are used. Therefore, the\n`lang.user.initials.field.names` property is ignored.\n\n![Figure 3: The user's initials are displayed for their avatar by default.](../../images/initials-avatar.png)\n\nIf you're leveraging the user's initials for the default avatar, you can use the \n`lang.user.initials.field.names` property to organize how the initials are \ndisplayed. Valid values for this property include `first-name`, `middle-name`, \nand `last-name`, in any order. \n\n## Right to Left or Left to Right?\n\nThe first three properties in the core `Language.properties` file's Language \nSettings section change the display direction of a language's characters. Most \nlanguages read from left to right, but some languages read from right to left \n(e.g., Arabic, Hebrew, and Persian). \n\nHere are the relevant language properties for a right-to-left language: \n\n```properties\nlang.dir=rtl\nlang.line.begin=right\nlang.line.end=left\n```\n\n| **Note:** You can prevent specific CSS rules from transforming (flipping) with \n| the `/* @noflip */` decoration. Place the decoration to the left of the CSS \n| rule to apply it. For example, this rule gives a left margin of `20em` to the \n| `body` no matter if the selected language is LTR or RTL:\n|\n| ```css\n| /* @noflip */ body {\n|  margin-left: 20em;\n| }\n| ```\n|\n| You can also use the `.rtl` CSS selector for rules that exclusively apply to \n| RTL languages.\n\n## Related Topics\n\n[Overriding Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys)\n\n[Localizing Your Application](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application)\n\n[Creating a Language Module](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-language-module)\n\n[Using a Language Module](/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module)\n"
  },
  {
    "path": "en/developer/frameworks/articles/localization/04-creating-language-module.markdown",
    "content": "---\nheader-id: creating-a-language-module\n---\n\n# Creating a Language Module\n\n[TOC levels=1-4]\n\nYou might have an application with multiple modules that provide the view layer. \nThese modules are often called web modules. For example, this application \ncontains three such modules: \n\n    my-application/\n    my-application-web/\n    my-admin-application-web/\n    my-application-content-web/\n    my-application-api/\n    my-application-service/\n\nEach of these modules can have language keys and translations to maintain, \nlikely resulting in duplicate keys. You don't want to end up with different\nvalues for the same key, and you don't want to maintain language keys in \nmultiple places. In this case, you should create a single module---a language \nmodule---for housing all your app's language keys. \n\nFollow these steps to create a language module: \n\n1.  In the root project folder (the one that holds your service, API, and web \n    modules), \n    [create a new module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project) \n    to hold your app's language keys. \n\n2.  In the language module, create a `src/main/resources/content` folder. In \n    this folder, create a `Language.properties` file for your app's default \n    language keys. For example, such a file might look like this: \n\n    ```properties\n    my-app-title=My Application\n    add-entity=Add Entity\n    ```\n\n3.  Create any translations you want in additional language properties files, \n    appending the locale's ID to the file name. For example, a file \n    `Language_es.properties` holds Spanish (`es`) translations and could contain \n    something like this: \n\n    ```properties\n    my-app-title=Mi Aplicación\n    add-entity=Añadir Entity\n    ```\n\n    Here's the folder structure of an example language module called \n    `my-application-lang`. This module contains the app's default language keys \n    (`Language.properties`) and a Spanish translation \n    (`Language_es.properties`): \n\n        my-application-lang/\n            bnd.bnd\n            src/\n                main/\n                    resources/\n                        content/\n                            Language.properties\n                            Language_es.properties\n                            ...\n\nOn building the language module, @product@'s \n`ResourceBundleLoaderAnalyzerPlugin` detects the module's `Language.properties` \nfile and adds a resource bundle \n[capability](http://blog.osgi.org/2015/12/using-requirements-and-capabilities.html) \nto the module. A capability is a contract a module declares to the OSGi \nframework. Capabilities let you associate services with modules that provide \nthem. In this case, @product@ registers a \n[ResourceBundleLoader](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/ResourceBundleLoader.html) \nservice for the resource bundle capability. \n\n## Related Topics\n\n[Overriding Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys)\n\n[Localizing Your Application](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application)\n\n[Using Liferay's Localization Settings](/docs/7-2/frameworks/-/knowledge_base/f/using-liferays-localization-settings)\n\n[Using a Language Module](/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module)\n"
  },
  {
    "path": "en/developer/frameworks/articles/localization/05-using-language-module.markdown",
    "content": "---\nheader-id: using-a-language-module\n---\n\n# Using a Language Module\n\n[TOC levels=1-4]\n\nA module or traditional Liferay plugin can use a resource bundle from another\nmodule and optionally include its own resource bundle. OSGi manifest headers\n`Require-Capability` and `Provide-Capability` make this possible, and it's\nespecially easy in modules generated from Liferay project templates. \nInstructions for using a language module are divided into these environments: \n\n-  [Using a Language Module from a Module](#using-a-language-module-from-a-module)\n-  [Using a Language Module from a Traditional Plugin](#using-a-language-module-from-a-traditional-plugin)\n\nIf you're using bnd with Maven or Gradle, you need only specify Liferay's\n`-liferay-aggregate-resource-bundle:` bnd instruction---at build time, Liferay's\nbnd plugin converts the instruction to `Require-Capability` and\n`Provide-Capability` parameters automatically. Both approaches are demonstrated \nhere. \n\n## Using a Language Module from a Module\n\nModules generated from Liferay project templates have a Liferay bnd build time\ninstruction called `-liferay-aggregate-resource-bundles`. It lets you use other\nresource bundles (including their language keys) along with your own. \n\nHere's how to use this bnd instruction: \n\n1.  Open your module's `bnd.bnd` file.\n\n2.  Add the `-liferay-aggregate-resource-bundles:` bnd instruction and assign it\n    the bundle symbolic names of modules whose resource bundles you wish to \n    aggregate with the current module's resource bundle: \n\n    ```properties\n    -liferay-aggregate-resource-bundles: \\\n        [bundle.symbolic.name1],\\\n        [bundle.symbolic.name2]\n    ```\n\nFor example, a module that uses resource bundles from modules\n`com.liferay.docs.l10n.myapp1.lang` and `com.liferay.docs.l10n.myapp2.lang`\nwould set this in its `bnd.bnd` file:\n\n```properties\n-liferay-aggregate-resource-bundles: \\\n    com.liferay.docs.l10n.myapp1.lang,\\\n    com.liferay.docs.l10n.myapp2.lang\n```\n\nThe current module's resource bundle is prioritized over those of the listed \nmodules. \n\n| **Note:** The Shared Language Key \n| [sample project](/docs/7-2/reference/-/knowledge_base/r/shared-language-keys)\n| is a working example that demonstrates aggregating resource bundles. You can\n| deploy it in Gradle, Maven, and Liferay Workspace build environments. \n\nAt build time, Liferay's bnd plugin converts the bnd instruction to \n`Require-Capability` and `Provide-Capability` parameters automatically. In \ntraditional Liferay plugins, you must specify the parameters manually. \n\n| **Note:** You can always specify the `Require-Capability` and `Provide-\n| Capability` OSGi manifest headers manually, as the next section demonstrates. \n\n## Using a Language Module from a Traditional Plugin\n\nTo use a language module from a traditional Liferay plugin you must specify the\nlanguage module via the `Require-Capability` and `Provide-Capability` OSGi \nmanifest headers in the plugin's `liferay-plugin-package.properties` file. \n\nFollow these steps to configure your traditional plugin to use a language \nmodule: \n\n1.  Open the plugin's `liferay-plugin-package.properties` file and add a \n    `Require-Capability` header that filters on the language module's resource\n    bundle capability. For example, if the language module's symbolic name is\n    `myapp.lang`, specify the requirement like this: \n\n    ```properties\n    Require-Capability: liferay.resource.bundle;filter:=\"(bundle.symbolic.name=myapp.lang)\"\n    ```\n\n2.  In the same `liferay-plugin-package.properties` file, add a \n    `Provide-Capability` header that adds the language module's resource bundle\n    as this plugin's (the `myapp.web` plugin) own resource bundle:\n\n    ```properties\n    Provide-Capability:\\\n    liferay.resource.bundle;resource.bundle.base.name=\"content.Language\",\\\n    liferay.resource.bundle;resource.bundle.aggregate:String=\"(bundle.symbolic.name=myapp.lang)\";bundle.symbolic.name=myapp.web;resource.bundle.base.name=\"content.Language\";service.ranking:Long=\"4\";\\\n    servlet.context.name=myapp-web\n    ```\n\nIn this case, the `myapp.web` plugin solely uses the language module's resource\nbundle---the resource bundle aggregate only includes language module \n`myapp.lang`. \n\nAggregating resource bundles comes into play when you want to use a language\nmodule's resource bundle in addition to your plugin's resource bundle. These\ninstructions show you how to do this, while prioritizing your current plugin's\nresource bundle over the language module resource bundle. In this way, the\nlanguage module's language keys compliment your plugin's language keys. \n\nFor example, a portlet whose bundle symbolic name is `myapp.web` uses keys from\nthe language module `myapp.lang` in addition to its own. The portlet's\n`Provide-Capability` and `Web-ContextPath` OSGi headers accomplish this.\n\n```properties\nProvide-Capability:\\\nliferay.resource.bundle;resource.bundle.base.name=\"content.Language\",\\\nliferay.resource.bundle;resource.bundle.aggregate:String=\"(bundle.symbolic.name=myapp.web),(bundle.symbolic.name=myapp.lang)\";bundle.symbolic.name=myapp.web;resource.bundle.base.name=\"content.Language\";service.ranking:Long=\"4\";\\\nservlet.context.name=myapp-web\n```\n\nThe example `Provide-Capability` header has two parts: \n\n1.  `liferay.resource.bundle;resource.bundle.base.name=\"content.Language\"` \n    declares that the module provides a resource bundle whose base name is\n    `content.language`. \n\n2.  The `liferay.resource.bundle;resource.bundle.aggregate:String=...` directive\n    specifies the list of bundles whose resource bundles are aggregated, the\n    target bundle, the target bundle's resource bundle name, and this service's\n    ranking:\n\n    -   `\"(bundle.symbolic.name=myapp.web),(bundle.symbolic.name=myapp.lang)\"`:\n        The service aggregates resource bundles from bundles\n        `bundle.symbolic.name=myapp.web` (the current\n        module) and `bundle.symbolic.name=myapp.lang`.\n        Aggregate as many bundles as desired. Listed bundles are prioritized in\n        descending order. \n    -   `bundle.symbolic.name=myapp.web;resource.bundle.base.name=\"content.Language\"`:\n        Override the `myapp.web` bundle's resource bundle named\n        `content.Language`.\n    -   `service.ranking:Long=\"4\"`: The resource bundle's service ranking is \n        `4`. The OSGi framework applies this service if it outranks all other\n        resource bundle services that target `myapp.web`'s\n        `content.Language` resource bundle. \n    -   `servlet.context.name=myapp-web`: The target resource bundle is in \n        servlet context `myapp-web`. \n\nNow the language keys from the aggregated resource bundles compliment your \nplugin's language keys. \n\n## Related Topics\n\n[Localizing Your Application](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application)\n\n[Overriding Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys)\n"
  },
  {
    "path": "en/developer/frameworks/articles/localization/06-automatically-generating-translations.markdown",
    "content": "---\nheader-id: automatically-generating-translations\n---\n\n# Automatically Generating Translations\n\n[TOC levels=1-4]\n\nIf your app uses a \n[language module](/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module) or\n[`Language.properties`\nfile](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application) for\nits user interface messages, you're in the right place. Language Builder\nprovides these localization capabilities:\n\n-  Generating language files for each supported locale with a single\n   command. It also propagates new default language file keys to all\n   language files, while keeping their translated values intact. \n\n-  Generating translations automatically using Microsoft's Translator Text API. \n   This gives you a jump start on creating translations. \n\n| **Note:** Language Builder is available as a plugin for projects that use \n| Gradle or Maven. \n\nStart with Configuring the Language Builder plugin. \n\n## Configuring the Language Builder Plugin\n\nConfigure the Language Builder plugin for\n[Gradle](/docs/7-2/reference/-/knowledge_base/r/lang-builder-gradle-plugin)\nor [Maven](/docs/7-2/reference/-/knowledge_base/r/lang-builder-plugin). \n\n**Gradle:**\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath 'com.liferay:com.liferay.gradle.plugins.lang.builder:latest.release'\n    }\n\n    repositories {\n        maven {\n            url \"https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.lang.builder\"\n\nrepositories {\n    maven {\n        url \"https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/\"\n    }\n}\n```\n\n**Maven:**\n\n```xml \n<project>\n    ...\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.liferay</groupId>\n                <artifactId>com.liferay.lang.builder</artifactId>\n                <version>1.0.30</version>\n                <configuration>\n                    <langDirName>.</langDirName>\n                    <translateClientId>${microsoft.translator.client.id}</translateClientId>\n                    <translateClientSecret>${microsoft.translator.client.secret}</translateClientSecret>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n    ...\n</project>\n``` \n\nNow you can invoke Language Builder in your project. \n\n## Running Language Builder\n\nWhen you run Language Builder, it generates properties files for all your\nlocales and propagates all new language properties from `Language.properties` to\nyour locale language files (newly generated and existing). Additionally if you\nconfigured the Microsoft Translator Text API (discussed next), Language Builder\ntranslates your locale language properties. \n\nHere's the command:\n\n**Gradle:**\n\n```bash\ngradlew buildLang\n```\n\n**Maven:**\n\n```bash\nmvn lang-builder:build\n```\n\n| **Tip:** Run Language Builder to update your locale files each time you change\n|your `Language.properties` file. \n\nNote, until you configure translation credentials (discussed  next), Language\nBuilder prints this message:\n\n```\nTranslation is disabled because credentials are not specified\n```\n\nIf you want to configure your app to generate automatic translations using the\nMicrosoft Translator Text API, keep reading. \n\n## Translating Language Keys Automatically\n\nIf you've configured the Language Builder plugin (above) in your project, you're\nwell on your way to translating language keys automatically. Now you have to\nconfigure \n[Microsoft's Translator Text API](https://azure.microsoft.com/en-us/services/cognitive-services/translator-text-api/)\nso you can translate language keys automatically.\n\n| **Important:** Lang Builder does not translate language keys containing HTML \n| (e.g., `<em>`, `<b>`, `<code>`, etc.). Default language keys that contain HTML\n| are only *copied* to your locale language files.\n\n| **Note:** These translations are best used as a starting point. A machine\n| translation can't match the accuracy of a real person who is fluent in the\n| language. Then again, if you only speak English and you need a Hungarian\n| translation, this is better and faster than your attempts at a manual\n| translation.\n\nHere's how to set up the translator and generate translations. \n\n1.  Generate a translation subscription key for the Microsoft Translator Text\n    API. Follow the instructions\n    [here](https://www.microsoft.com/en-us/translator/business/).\n\n2.  Add your client credentials to the Language Builder plugin configuration. \n    For security reasons, pass the credentials to a property that's stored in\n    your local build environment (e.g., see the [Gradle environment\n    guide](https://docs.gradle.org/current/userguide/build_environment.html)). \n\n    **Gradle:**\n\n    Make sure the `buildLang` task knows to use your subscription key for\n    translation by setting the `translateSubscriptionKey` property:\n\n    ```groovy\n    buildLang {\n       translateSubscriptionKey = langTranslateSubscriptionKey\n    }\n    ```\n\n    Here's the entire `build.gradle` example code, \n\n    ```groovy\n    buildscript {\n        dependencies {\n            classpath 'com.liferay:com.liferay.gradle.plugins.lang.builder:latest.release'\n        }\n\n        repositories {\n            maven {\n                url \"https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/\"\n            }\n        }\n    }\n\n    apply plugin: \"com.liferay.lang.builder\"\n\n    buildLang {\n       translateSubscriptionKey = langTranslateSubscriptionKey\n    }\n\n    repositories {\n        maven {\n            url \"https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/\"\n        }\n    }\n    ```\n\n    **Maven:**\n\n    Set the following Language Builder plugin `<translateClientId />` and\n    `<translateClientSecret />` configuration elements using Maven build environment properties: \n\n    ```xml\n    <configuration>\n        <langDirName>.</langDirName>\n        <translateClientId>${microsoft.translator.client.id}</translateClientId>\n        <translateClientSecret>${microsoft.translator.client.secret}</translateClientSecret>\n    </configuration>\n    ...\n    ```\n\n    Here's the entire `pom.xml` example code, \n\n    ```xml\n    <project>\n        ...\n        <build>\n            <plugins>\n                <plugin>\n                    <groupId>com.liferay</groupId>\n                    <artifactId>com.liferay.lang.builder</artifactId>\n                    <version>1.0.30</version>\n                    <configuration>\n                        <langDirName>.</langDirName>\n                        <translateClientId>${microsoft.translator.client.id}</translateClientId>\n                        <translateClientSecret>${microsoft.translator.client.secret}</translateClientSecret>\n                    </configuration>\n                </plugin>\n            </plugins>\n        </build>\n        ...\n    </project>\n    ```\n\n3.  Run Language Builder. \n\n    **Gradle:**\n\n    ```bash\n    gradlew buildLang\n    ```\n\n    **Maven:**\n\n    ```bash\n    mvn lang-builder:build\n    ```\n\nGreat! You can now generate language files and provide automatic translations of\nyour language keys.\n\n## Related Topics \n\n[Localizing Your Application](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application)\n\n[Using Language Modules](/docs/7-2/frameworks/-/knowledge_base/f/using-a-language-module)\n\n[Gradle Language Builder Plugin](/docs/7-2/reference/-/knowledge_base/r/lang-builder-gradle-plugin)\n\n[Maven Language Builder Plugin](/docs/7-2/reference/-/knowledge_base/r/lang-builder-plugin)\n\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)\n\n[Tooling](/docs/7-2/reference/-/knowledge_base/r/tooling)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/01-portlets-intro.markdown",
    "content": "---\nheader-id: portlets\n---\n\n# Portlets\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/w/dxp/liferay-development/building-applications/developing-a-java-web-application/reference/portlets\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n@product@ started off as a portal server, designed to serve Java-based web\napplications called *portlets* (see [JSR 168](https://jcp.org/en/jsr/detail?id=168),\n[JSR-286](https://jcp.org/en/jsr/detail?id=286), and\n[JSR-362](https://jcp.org/en/jsr/detail?id=362)).\nPortlets process requests and generate\nresponses like any other web application. One key difference, however, between\nportlets and other web apps is that portlets run in a portion of the web page.\nWhen you're writing a portlet application, you need only worry about that\napplication: the rest of the page---the navigation, the top banner, and any\nother global components of the interface---is handled by other components.\nPortlets run only in a portal server. They use the portal's existing support for\nuser management, authentication, permissions, page management, and more. This\nfrees you to focus on developing the portlet's core functionality. In many ways,\nwriting your application as a portlet is easier than writing a standalone\napplication.\n\nMany portlets can be placed on a single page by users (if they have permission)\nor portal administrators. For example, a page in a community site could have\na calendar portlet for community events, an announcements portlet for important\nannouncements, and a bookmarks portlet for links of interest to the community.\nYou can drag and drop to reposition and resize portlets on a page without\naltering any portlet code. Alternatively, a single portlet can take up an entire\npage if it's the only app you need on that page. For example, message boards\nor Wikis with complex user interfaces are best suited on their own pages. In\nshort, portlets alleviate many of the traditional pain points associated with\ndeveloping Java-based web apps.\n\n![Figure 1: You can place multiple portlets on a single page.](../../images/portlet-applications.png)\n\nPortlets handle requests in multiple phases. This makes portlets much more\nflexible than servlets. Each portlet phase executes different operations:\n\n-   **Render:** Generates the portlet's content based on its current state. When\n    this phase runs on one portlet, it also runs on all other portlets on the\n    page. The Render phase runs when any portlets on the page complete the\n    Action or Event phases.\n-   **Action:** In response to a user action, the Action phase performs\n    operations that change the portlet's state. The Action phase can also\n    trigger events that are processed by the Event phase. Following the Action\n    phase and optional Event phase, the Render phase then regenerates the\n    portlet's contents.\n-   **Event:** Processes events triggered in the Action phase. Events are used\n    for inter-portlet communication (IPC). Once the portlet processes all\n    events, the portal calls the Render phase on all portlets on the page.\n-   **Resource-serving:** Serves a resource independently from the rest of the\n    lifecycle. This lets a portlet serve dynamic content without running the\n    Render phase on all portlets on a page. The Resource-serving phase handles\n    AJAX requests.\n-   **Header:** Lets you specify resource dependencies, such as CSS, prior to\n    the Render phase.\n\nCompared to servlets, portlets also have some other key differences. Since\nportlets only render a portion of a page, tags like `<html>`, `<head>`, and\n`<body>` aren't allowed. And because you don't know the portlet's page ahead of\ntime, you can't create portlet URLs directly. Instead, the portlet API gives you\nmethods to create portlet URLs programmatically. Also, because portlets don't\nhave direct access to the `javax.servlet.ServletRequest`, they can't read query\nparameters directly from a URL. Portlets instead access a\n`javax.portlet.PortletRequest` object. The portlet specification provides a\nmechanism for a portlet to read only its own URL parameters or those declared as\npublic render parameters. @product@ does, however, provide utility methods that\ncan access the `ServletRequest` and query parameters. Portlets also have a\n*portlet filter* available for each phase in the portlet lifecycle. Portlet\nfilters are similar to servlet filters in that they allow request and response\nmodification on the fly.\n\nPortlets also differ from servlets by having distinct modes and window states.\nModes distinguish the portlet's current function:\n\n-   **View mode:** The portlet's standard mode. Use this mode to access the\n    portlet's main functionality.\n-   **Edit mode:** The portlet's configuration mode. Use this mode to configure\n    a custom view or behavior. For example, the Edit mode of a weather portlet\n    might let you choose a location to retrieve weather data from.\n-   **Help mode:** A mode that displays the portlet's help information.\n\nMost modern applications use View Mode only.\n\nPortlet window states control the amount of space a portlet takes on a page.\nWindow states mimic window behavior in a traditional desktop environment:\n\n-   **Normal:** The portlet can be on a page that contains other portlets. This\n    is the default window state.\n-   **Maximized:** The portlet takes up an entire page.\n-   **Minimized:** Only the portlet's title bar shows.\n\nAll of the\n[ways to develop web front-ends](/docs/7-2/appdev/-/knowledge_base/a/web-front-ends)\non @product@ involve portlets. The JavaScript-based widgets use Liferay's JS\nPortlet Extender behind the scenes and the Java-based web front-ends are\nexplicitly portlets. All of the web front-end types vary in their support of\nPortlet 3.0,\n[dependency injection (DI)](/docs/7-2/frameworks/-/knowledge_base/f/dependency-injection),\nModel View Controller (MVC), and modularity, giving you plenty of good options\nfor developing portlets.\n\n## Related Topics\n\n<!--[Bean Portlet](/docs/7-2/appdev/-/knowledge_base/a/bean-portlet) TODO uncomment when Bean Portlet is available. jhinkey -->\n\n[Spring Portlet MVC: PortletMVC4Spring](/docs/7-2/appdev/-/knowledge_base/a/portletmvc4spring)\n\n[Liferay MVC Portlet](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet)\n\n[JSF Portlet](/docs/7-2/appdev/-/knowledge_base/a/jsf-portlet)\n\n[Portlet 3.0 API Opt In](/docs/7-2/reference/-/knowledge_base/r/portlet-3-0-api-opt-in)"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/01-using-javascript-in-your-portlets-intro.markdown",
    "content": "---\nheader-id: using-javascript-in-your-portlets\n---\n\n# Using JavaScript in Your Portlets\n\n[TOC levels=1-4]\n\nWould you like to use the latest ECMAScript features in your JavaScript files \nand portlets? Do you wish you could use npm and npm packages in your portlets? \n\nTo use the ES2015+ syntax in a JavaScript file, add the extension `.es` to its \nname. For example, you rename file `filename.js` to `filename.es.js`. The \nextension indicates it uses ES2015+ syntax and must therefore be transpiled by \n[Babel](https://babeljs.io/) before deployment. \n\nES2015+ advanced features, such as [generators](https://babeljs.io/docs/learn-es2015/#generators), \nare available to you if you import the `polyfillBabel` class from the \n`polyfill-babel` module:\n\n```javascript\nimport polyfillBabel from 'polyfill-babel'\n```\n\nThe [Babel Polyfill](http://babeljs.io/docs/usage/polyfill/) emulates a complete \nES6 environment. Use it at your own discretion, as it loads a large amount of \ncode. You can inspect [https://github.com/zloirock/core-js#core-js](https://github.com/zloirock/core-js#core-js) \nto see what's polyfilled. \n\nOnce you've completed writing your module, you can expose it by creating a \n`package.json` file that specifies your bundle's name and version. Make sure to \ncreate this in your module's root folder. Below is an example `package.json` \nfile for a `js-logger` module:\n\n```json\n{\n    \"name\": \"js-logger\",\n    \"version\": \"1.0.0\"\n}\n```\n\nThe Module Config Generator creates the module based on this information. In \nthis section, you'll learn how to prepare your JavaScript files to leverage \nECMAScript and npm features in your portlets. \n\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/02-esplus-modules/01-using-esplus-modules-in-your-portlets-intro.markdown",
    "content": "---\nheader-id: using-esplus-modules-in-your-portlet\n---\n\n# Using ES2015 Modules in your Portlet\n\n[TOC levels=1-4]\n\nOnce you've [exposed your modules](/docs/7-2/frameworks/-/knowledge_base/f/using-javascript-in-your-portlets) \nvia your `package.json` file, you can use them in your portlets. The \n`aui:script` tag's `require` attribute makes it easy. \n\nFollow the steps below to use your exposed modules in your portlets:\n\n1.  Declare the `aui` taglib in your view JSP:\n\n    ```markup\n    <%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\" %>\n    ```\n\n    | **Note:** if you created the portlet using Blade, the `aui` taglib is \n    | already provided for you in the `init.jsp`. \n\n2.  Add an `aui:script` tag to the JSP and set the `require` attribute to the \n    relative path for your module. \n\n    The `require` attribute lets you include your exposed modules in your JSP. \n    The AMD Loader fetches the specified module and its dependencies. An example \n    faux Console Logger Portlet's `view.jsp` shown below includes the module \n    `logger.es`:\n\n    ```javascript\n    <aui:script require=\"js-logger/logger.es\">\n        var Logger = jsLoggerLoggerEs.default;\n\n        var loggerOne = new Logger('*** -> ');\n        loggerOne.log('Hello');\n\n        var loggerDefault = new Logger();\n        loggerDefault.log('World');\n    </aui:script>\n    ```\n\n    References to the module within the script tag are named after the `require` \n    value, in camel-case and with all invalid characters removed. The \n    `logger.es` module's reference `jsLoggerLoggerEs` is derived from \n    the module's relative path value `js-logger/logger.es`. The value \n    is stripped of its dash and slash characters and converted to camel case. \n\nThanks to the `aui:script` tag and its `require` attribute, using your modules \nin your portlet is a piece of cake! \n\n## Related Topics\n\n- [Customizing JSPs](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps)\n- [Web Services](/docs/7-2/frameworks/-/knowledge_base/f/web-services)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/03-using-npm-in-your-portlets/01-npm-intro.markdown",
    "content": "---\nheader-id: using-npm-in-your-portlets\n---\n\n# Using npm in Your Portlets\n\n[TOC levels=1-4]\n\nnpm is a powerful tool, and almost a necessity for Front-End development. You \ncan use npm as your JavaScript package manager tool---including npm and npm \npackages---while developing portlets in your normal, everyday workflow. \n \nDeployed portlets leverage [Liferay AMD Loader](/docs/7-2/frameworks/-/knowledge_base/f/loading-amd-modules-in-liferay) \nto share JavaScript modules and take advantage of semantic versioning when \nresolving modules among portlets on the same page. The liferay-npm-bundler helps \nprepare your npm modules for the Liferay AMD Loader. \n\nThe bundler copies the project and `node_modules`' JS files to the output and \nwraps them inside a `Liferay.Loader.define()` call so that the Liferay AMD \nLoader knows how to handle them. It also namespaces the module names in \n`require()` calls and inside the `Liferay.Loader.define()` call with the \nproject's name prefix to achieve [dependency isolation](/docs/7-2/reference/-/knowledge_base/r/how-the-liferay-npm-bundler-publishes-npm-packages#isolated-package-dependencies). \nThe bundler injects the dependencies in the `package.json` pertaining to \nthe module to make them available at runtime. \n\nThis section covers how to set up npm-based portlet projects. \n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/03-using-npm-in-your-portlets/02-formatting-for-amd/01-formatting-your-npm-modules-for-amd-intro.markdown",
    "content": "---\nheader-id: formatting-your-npm-modules-for-amd\n---\n\n# Formatting Your npm Modules for AMD\n\n[TOC levels=1-4]\n\nFor @product@ to recognize your npm modules, they must be formatted for the \nLiferay AMD Loader. Luckily, the liferay-npm-bundler handles this for you, you \njust have to provide the proper configuration and add it to your build script. \nThis article shows how to use the liferay-npm-bundler to set up npm-based \nportlet projects. \n\nFollow these steps to configure your project to use the liferay-npm-bundler:\n\n1.  Install NodeJS >= [v6.11.0](http://nodejs.org/dist/v6.11.0/) if you don't \n    have it installed.\n\n2.  Navigate to your portlet's project folder and initialize a `package.json` \n    file if it's not present yet.\n\n    If you don't have a portlet already, create an empty MVC portlet project. \n    For convenience, you can use \n    [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/installing-blade-cli) \n    to create an empty portlet with the [mvc portlet blade template](/docs/7-2/reference/-/knowledge_base/r/using-the-mvc-portlet-template). \n\n    If you don't have a `package.json` file, you can run `npm init -y` to create \n    an empty one based on the project directory's name. \n\n3.  Run this command to install the liferay-npm-bundler:\n\n    ```bash\n    npm install --save-dev liferay-npm-bundler\n    ```\n\n    | **Note:** Use npm from within your portlet project's root folder (where the\n    | `package.json` file lives), as you normally do on a typical web project.\n\n4.  Add the `liferay-npm-bundler` to your `package.json`'s build script to pack \n    the needed npm packages and transform them to AMD:\n\n    ```json\n    \"scripts\": {\n          \"build\": \"liferay-npm-bundler\"\n    }\n    ```\n    \n5.  Configure your project for the bundler, using the `.npmbundlerrc` file \n    (create this file in your project's root folder if it doesn't exist). See \n    the [liferay-npm-bundler's `.npmbundlerrc` structure reference](/docs/7-2/reference/-/knowledge_base/r/understanding-the-npmbundlerrcs-structure) \n    for more information on the available options. Specify the \n    [loaders and rules](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-npm-bundlers-loaders) \n    to use for your project's source files. The example below processes the \n    JavaScript files in the project's `/src/` and `/assets/` folders with Babel \n    via the [`babel-loader`](https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-babel-loader):\n\n    ```json\n    {\n      \"sources\": [\"src\", \"assets\"],\n      \"rules\": [\n        {\n          \"test\": \"\\\\.js$\",\n          \"exclude\": \"node_modules\",\n          \"use\": [\n            {\n              \"loader\": \"babel-loader\",\n              \"options\": {\n                \"presets\": [\"env\"]\n              }\n            }\n          ]\n        }\n      ]\n    }\n    ```\n\n6.  Run `npm install` to install the required dependencies.\n\n7.  Run the build script to bundle your dependencies with the \n    liferay-npm-bundler:\n\n    ```bash\n    npm run-script build\n    ```\n\n| **Note:** By default, the AMD Loader times out in seven seconds. You can \n| configure this value through System Settings. Open the Control Panel and \n| navigate to *Configuration* &rarr; *System Settings* &rarr; *PLATFORM* &rarr; \n| *Infrastructure*, and select *JavaScript Loader*. Set the \n| *Module Definition Timeout* configuration to the time you want and click \n| *Save*.\n\nGreat! Now you know how to use the liferay-npm-bundler to bundle your npm-based \nportlets for the Liferay AMD Loader. \n\n## Related Topics\n\n- [Preparing Your JavaScript Files for ES2015+](/docs/7-2/frameworks/-/knowledge_base/f/using-javascript-in-your-portlets)\n- [Understanding liferay-npm-bundler's Loaders and Rules](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-npm-bundlers-loaders) \n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/03-using-npm-in-your-portlets/03-migrating-bundler-projects/01-migrating-bundler-projects-intro.markdown",
    "content": "---\nheader-id: migrating-a-liferay-npm-bundler-project-from-1-x-to-2-x\n---\n\n# Migrating a liferay-npm-bundler Project from 1.x to 2.x\n\n[TOC levels=1-4]\n\nYou should use the latest 2.x version of the liferay-npm-bundler. It \n[offers more stability and includes more features out-of-the-box](/docs/7-2/reference/-/knowledge_base/r/what-changed-between-liferay-npm-bundler-1-x-and-2-x). \nIf you already created a project using the 1.x version, don't worry. Follow \nthese steps to migrate your project to 2.x:\n\n1.  Update the `liferay-npm-bundler` dependency in your `package.json` to \n    version 2.x:\n\n    ```json\n    {\n      \"devDependencies\": {\n        ...\n        \"liferay-npm-bundler\": \"^2.0.0\",\n        ...\n      },\n      ...\n    }\n    ```\n\n2.  Remove all `liferay-npm-bundler-preset-*` dependencies from your \n    `package.json` because liferay-npm-bundler 2.x includes these by default.\n\n3.  Remove any bundler presets you configured in your `.npmbundlerrc` file. \n    liferay-npm-bundler 2.x includes one smart preset that handles all \n    frameworks automatically.\n\nThese are the standard requirements that all projects have in common. The \nremaining steps depend on your project's framework. Follow the instructions in \nthe corresponding section to finish migrating your project. \n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/03-using-npm-in-your-portlets/03-migrating-bundler-projects/02-migrating-plain-js-billboard-jquery-metal-js-react-vue-bundler.markdown",
    "content": "---\nheader-id: migrating-plain-js-billboard-jquery-metal-js-react-vue-project\n---\n\n# Migrating a Plain JavaScript, Billboard JS, JQuery, Metal JS, React, or Vue JS Project to Use Bundler 2.x\n\n[TOC levels=1-4]\n\nAfter following the steps covered in the intro to this section, follow these \nremaining steps to migrate the framework projects shown below to 2.x:\n\n- plain JS project\n- Billboard.js project\n- JQuery project\n- Metal.js project\n- React project\n- Vue.js project\n\nWhile Babel is required to transpile your source files, you must remove any \nBabel preset used for transformations from your project that bundler 1.x \nimposed. liferay-npm-bundler 2.x handles these transformations by default:\n\n1.  Remove the *liferay-project* preset from your project's `.babelrc` file. All \n    that should remain is the `es2015` preset shown below:\n\n    ```json\n    {\n      \"presets\": [\"es2015\"]\n    }\n    ```\n\n    If your project uses React, make sure the `react` preset remains as well:\n\n    ```json    \n    {\n      \"presets\": [\"es2015\", \"react\"]\n    }\n    ```\n\n2.  Remove the `babel-preset-liferay-project` dependency from your \n    `package.json`.\n\nAwesome! Your project is migrated to use the new version of the \nliferay-npm-bundler. \n\n## Related Topics\n\n- [Formatting Your npm Modules for AMD](/docs/7-2/frameworks/-/knowledge_base/f/formatting-your-npm-modules-for-amd)\n- [Using the NPMResolver API in Your Portlets](/docs/7-2/frameworks/-/knowledge_base/f/using-the-npmresolver-api-in-your-portlets)\n- [What Changed between liferay-npm-bundler 1.x and 2.x](/docs/7-2/reference/-/knowledge_base/r/what-changed-between-liferay-npm-bundler-1-x-and-2-x)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/03-using-npm-in-your-portlets/03-migrating-bundler-projects/03-migrating-angular-bundler.markdown",
    "content": "---\nheader-id: migrating-an-angular-project-to-use-bundler-2-x\n---\n\n# Migrating an Angular Project to Use Bundler 2.x\n\n[TOC levels=1-4]\n\nAfter following the steps covered in the intro to this section, follow these \nremaining steps to migrate your Angular project to 2.x. While liferay-npm-bundler 1.x relied on \nBabel to perform some transformation steps, these transformations are now \nautomatically applied in version 2.x. Therefore, you should remove Babel from \nyour project:\n\n1.  Open your `tsconfig.json` file and replace the `\"module\": \"amd\"` compiler \n    option with the configuration shown below to produce CommonJS modules:\n\n    ```json\n    {\n      \"compilerOptions\": {\n        ...\n        \"module\": \"commonjs\",\n        ...\n      }\n    }\n    ```\n\n2.  Delete the `.babelrc` file to remove the Babel configuration.\n\n3.  Remove Babel from your `package.json` build process so it matches the \n    configuration below:\n\n    ```json    \n    {\n      \"scripts\": {\n        \"build\": \"tsc && liferay-npm-bundler\"\n      },\n      ...\n    }\n    ```\n\n4.  Remove the following Babel dependencies from your `package.json` \n    *devDependencies*:\n\n    ```json\n    \"babel-cli\": \"6.26.0\",\n    \"babel-preset-liferay-amd\": \"1.2.2\"\n    ```\n\nGreat! Your project is migrated to use the new version of the \nliferay-npm-bundler. \n\n## Related Topics\n        \n- [Formatting Your npm Modules for AMD](/docs/7-2/frameworks/-/knowledge_base/f/formatting-your-npm-modules-for-amd)\n- [Using the NPMResolver API in Your Portlets](/docs/7-2/frameworks/-/knowledge_base/f/using-the-npmresolver-api-in-your-portlets)\n- [What Changed between liferay-npm-bundler 1.x and 2.x](/docs/7-2/reference/-/knowledge_base/r/what-changed-between-liferay-npm-bundler-1-x-and-2-x)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/03-using-npm-in-your-portlets/03-migrating-bundler-projects/04-migrating-to-the-new-mode.markdown",
    "content": "---\nheader-id: migrating-your-project-to-use-the-new-mode\n---\n\n# Migrating Your Project to Use liferay-npm-bundler's New Mode\n\n[TOC levels=1-4]\n\nIn the previous version of the liferay-npm-bundler, before the bundler ran, the \nbuild did some preprocessing, then the bundler modified the output from the \npreprocessed files, as shown in the example build script below:\n\n```json\n{\n  \"scripts\":{\n    \"build\": \"babel --source-maps -d build src && liferay-npm-bundler\"\n  }\n}\n```\n\nIn the new mode, the liferay-npm-bundler runs the whole process, like webpack,\nand is configured via a set of rules. The build script is condensed, as shown\nbelow:\n\n```json\n{\n  \"scripts\":{\n    \"build\": \"liferay-npm-bundler\"\n  }\n}\n```\n\nFollow these steps to migrate your project to use the new configuration mode:\n\n1.  Open the project's `package.json` file and update the `build` script to \n    use only the liferay-npm-bundler:\n\n    ```json\n    {\n      \"scripts\":{\n        \"build\": \"liferay-npm-bundler\"\n      }\n    }\n    ```\n\n2.  Define the rules for the bundler to use (e.g. running babel to transpile \n    files) in the project's `.npmbundlerrc` file. The example configuration \n    below defines rules for using the `babel-loader` to transpile JavaScript \n    files. See the [Default Loaders reference](/docs/7-2/reference/-/knowledge_base/r/default-liferay-npm-bundler-loaders) \n    for the full list of default loaders. Follow the steps in \n    [Creating Custom Loaders for the Bundler](/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-loaders-for-the-liferay-npm-bundler) \n    to create a custom loader. The liferay-npm-bundler processes the `*.js` \n    files in  `/src/` with babel and writes the results in the default `/build/` \n    folder:\n\n    ```json\n    {\n      \"sources\": [\"src\"],\n      \"rules\": [\n        {\n          \"test\": \"\\\\.js$\",\n          \"exclude\": \"node_modules\",\n          \"use\": [\n            {\n              \"loader\": \"babel-loader\",\n              \"options\": {\n                \"presets\": [\"env\"]\n              }\n            }\n          ]\n        }\n      ]\n    }\n    ```\n\n    |**Note:** The new mode of the liferay-npm-bundler acts very much \n    | like webpack, but because webpack creates a single JS bundle file and \n    | liferay-npm-bundler targets AMD loader, they are not compatible.\n\n## Related Topics\n\n- [Default liferay-npm-bundler Loaders](/docs/7-2/reference/-/knowledge_base/r/default-liferay-npm-bundler-loaders)\n- [Understanding liferay-npm-bundler's Loaders](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-npm-bundlers-loaders)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/03-using-npm-in-your-portlets/04-custom-loaders/01-creating-custom-loaders-for-the-bundler-intro.markdown",
    "content": "---\nheader-id: creating-custom-loaders-for-the-liferay-npm-bundler\n---\n\n# Creating Custom Loaders for the liferay-npm-bundler\n\n[TOC levels=1-4]\n\nSince webpack creates JavaScript bundles and the liferay-npm-bundler targets AMD\nloader, webpack's loaders aren't compatible with the liferay-npm-bundler. So, if\nyou want to use a loader that isn't \n[available by default](/docs/7-2/reference/-/knowledge_base/r/default-liferay-npm-bundler-loaders),\nyou must create a custom loader. \n\nA loader, in terms of the liferay-npm-bundler, is defined as an npm package that \nhas a main module which exports a default function with this signature:\n\n```javascript\nfunction(context, options){\n}\n```\n\nThe arguments are defined as follows:\n\n`context`: an object containing these fields: \n\n> `content`: a string with the contents of the processed file (the main input \n> of the loader)\n\n> `filepath`: the project-relative path to the file to process with the loader\n\n> `extraArtifacts`: an object with project-relative paths as keys and strings \n> as values of properties that may be used to output extra files along with \n> the one being processed (for example, you can use it to generate source \n> maps).\n\n> `log`: a logger that writes execution information to the bundler's report \n> file (see the [`PluginLogger` class](https://github.com/liferay/liferay-js-toolkit/blob/master/packages/liferay-npm-build-tools-common/src/plugin-logger.js) \n> for information on its structure and API).\n\n`options`: an object taken from the `options` field of the loader's \nconfiguration (See [Understanding liferay-npm-bundler's loaders and rules](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-npm-bundlers-loaders) \nfor more information).\n\n| **Note:** the function may return nothing or modified content. If something is \n| returned, it is copied on top of the `context.content` field and used to feed \n| the next loader or write the output file. This is the equivalent to \n| `context.content = 'something'`. If your loader does not return a file, but \n| instead it only filters files to prevent them from being generated, you \n| must explicitly set `context.content = 'undefined'`. \n\nFollow these steps to write a new loader. These steps use the Babel loader as an \nexample:\n\n1.  If your loader requires configuration, like Babel, you may define a rule \n    configuration like the one shown below so you can specify options for the \n    loader:\n\n    ```json\n    {\n      \"rules\": [\n        {\n          \"test\": \"\\\\.js$\",\n          \"exclude\": \"node_modules\",\n          \"use\": [\n            {\n              \"loader\": \"babel-loader\",\n              \"options\": {\n                \"presets\": [\"env\", \"react\"]\n              }\n            }\n          ]\n        }\n      ]\n    }\n    ``` \n\n2.  Create an `index.js` file and write a function that takes the input content, \n    passes it through the loader, and writes the result and the source map file \n    to the output folder. The loader function below takes the passed content \n    (JS files), run it through babel, and writes the result and source map to \n    the default `/build/` output folder:\n\n    ```javascript\n    export default function(context, options) {\n      // Get input parameters\n      const { content, filePath, log, sourceMap } = context;\n\n      // Run babel on content\n      const result = babel.transform(content, options);\n\n      // Create an extra .map file with source map next to source .js file\n      context.extraArtifacts[`${filePath}.map`] = JSON.stringify(result.map);\n\n      // Tell the user what we have done\n      log.info(\"babel-loader\", \"Transpiled file\");\n\n      // Return the modified content\n      return result.code;\n    }\n    ```\n\n3.  Place the `index.js` file in an npm package and publish it.\n\n4.  Include the npm package you just created as a `devDependency` in the project's \n    `package.json`:\n\n    ```json\n    \"devDependencies\": {\n      \"liferay-npm-bundler\": \"2.12.0\",\n      \"liferay-npm-build-support\": \"2.12.0\",\n      \"liferay-npm-bundler-loader-babel-loader\": \"2.12.0\",\n      ...\n    }\n    ```\n\n5.  Configure the loader's name in the `rules` section of the project's \n    `.npmbundlerrc` file:\n    \n    ```json\n    {\n      \"sources\": [\"src\"],\n      ...\n      \"rules\": [\n        {\n          \"test\": \"\\\\.js$\",\n          \"exclude\": \"node_modules\",\n          \"use\": [\n            {\n              \"loader\": \"babel-loader\",\n              \"options\": {\n                \"presets\": [\"env\", \"react\"]\n              }\n            }\n          ]\n        }\n      ],\n      ...\n    }\n    ```\n\n## Related Topics\n\n- [Default liferay-npm-bundler Loaders](/docs/7-2/reference/-/knowledge_base/r/default-liferay-npm-bundler-loaders)\n- [Understanding liferay-npm-bundler's Loaders](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-npm-bundlers-loaders)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/03-using-npm-in-your-portlets/08-npmresolver-api/01-using-npmresolver-api-intro.markdown",
    "content": "---\nheader-id: using-the-npmresolver-api-in-your-portlets\n---\n\n# Using the NPMResolver API in Your Portlets\n\n[TOC levels=1-4]\n\nIf you're developing an npm-based portlet, your OSGi bundle's `package.json` is \na treasure-trove of information. It contains everything that's stored in the \nnpm registry about your bundle: default entry point, dependencies, modules, \npackage names, versions, and more. The \n[`NPMResolver` APIs](@app-ref@/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/NPMResolver.html) \nexpose this information so you can access it in your portlet. If it's defined \nin the OSGi bundle's `package.json`, you can retrieve the information in your \nportlet with the `NPMResolver` API. For instance, you can use this API to \n[reference an npm package's static resources](/docs/7-2/frameworks/-/knowledge_base/f/obtaining-dependency-npm-package-descriptors) \n(such as CSS files) and even to \n[make your code more maintainable](/docs/7-2/frameworks/-/knowledge_base/f/referencing-an-npm-modules-package). \n\nTo enable the `NPMResolver` in your portlet, use the `@Reference` annotation to \ninject the `NPMResolver` OSGi component into your portlet's Component class, as \nshown below:\n\n```java\nimport com.liferay.frontend.js.loader.modules.extender.npm.NPMResolver;\n\npublic class MyPortlet extends MVCPortlet {\n  \n  @Reference\n  private NPMResolver `_npmResolver`;\n  \n}\n```\n\n| **Note:** Because the `NPMResolver` reference is tied directly to the OSGi\n| bundle's `package.json` file, it can only be used to retrieve npm module and\n| package information from that file. You can't use the `NPMResolver` to retrieve\n| npm package information for other OSGi bundles.\n \nNow that the `NPMResolver` is added to your portlet, read the topics in this \nsection to learn how to retrieve your OSGi bundle's npm package and module \ninformation. \n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/03-using-npm-in-your-portlets/08-npmresolver-api/02-obtaining-osgi-bundle-npm-package-descriptors.markdown",
    "content": "---\nheader-id: referencing-an-npm-modules-package\n---\n\n# Referencing an npm Module's Package to Improve Code Maintenance\n\n[TOC levels=1-4]\n\nOnce you've \n[exposed your modules](/docs/7-2/frameworks/-/knowledge_base/f/using-javascript-in-your-portlets), \nyou can use them in your portlet via the `aui:script` tag's `require` attribute. \nBy default, @product@ automatically composes an npm module's JavaScript variable \nbased on its name. For example, the module `my-package@1.0.0` translates to the \nvariable `myPackage100` for the `<aui:script>` tag's `require` attribute. This \nmeans that each time a new version of the OSGi bundle's npm package is released, \nyou must update your code's variable to reflect the new version. You can use the \n[`JSPackage` interface](@app-ref@/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/JSPackage.html) \nto obtain the module's package name and create an alias to reference it, so the \nvariable name always reflects the latest version number! \n\nFollow these steps:\n\n1.  Retrieve a reference to the OSGi bundle's npm package using the \n    [`getJSPackage()` method](@app-ref@/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/NPMResolver.html#getJSPackage): \n\n    ```java\n    JSPackage jsPackage = _npmResolver.getJSPackage();\n    ```\n\n2.  Grab the npm package's resolved ID (the current package version, \n    in the format `<package name>@<version>`, defined in the OSGi module's \n    `package.json`) using the \n    [`getResolvedId()` method](@app-ref@/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/JSPackage.html#getResolvedId) \n    and alias it with the `as myVariableName` pattern. The example below \n    retrieves the npm module's resolved ID, sets it to the `bootstrapRequire` \n    variable, and assigns the entire value to the attribute `bootstrapRequire`. \n    This ensures that the package version is always up to date:\n\n    ```java\n    renderRequest.setAttribute(\n      \"bootstrapRequire\",\n      jsPackage.getResolvedId() + \" as bootstrapRequire\");\n    ```\n\n3.  Include the reference to the [`NPMResolver`](@app-ref@/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/NPMResolver.html):\n\n    ```java\n    @Reference\n    private NPMResolver _npmResolver;\n    ```\n\n4.  Resolve the `JSPackage` and `NPMResolver` imports:\n\n    ```java\n    import com.liferay.frontend.js.loader.modules.extender.npm.JSPackage;\n    import com.liferay.frontend.js.loader.modules.extender.npm.NPMResolver;\n    ```\n\n5.  In the portlet's JSP, retrieve the aliased attribute (`bootstrapRequire` in \n    the example):\n\n    ```java\n    <%\n    String bootstrapRequire =\n    \t(String)renderRequest.getAttribute(\"bootstrapRequire\");\n    %>\n    ```\n\n6.  Finally, use the attribute as the `<aui:script>` require attribute's value:\n\n    ```javascript\n    <aui:script require=\"<%= bootstrapRequire %>\">\n    \tbootstrapRequire.default();\n    </aui:script>\n    ```\n\n    Below is the full example `*Portlet` class:\n\n    ```java\n    public class MyPortlet extends MVCPortlet {\n    \t\n    \t@Override\n    \tpublic void doView(\n    \t\t\tRenderRequest renderRequest, RenderResponse renderResponse)\n    \t\tthrows IOException, PortletException {\n\n    \t\tJSPackage jsPackage = _npmResolver.getJSPackage();\n\n    \t\trenderRequest.setAttribute(\n    \t\t\t\"bootstrapRequire\",\n    \t\t\tjsPackage.getResolvedId() + \" as bootstrapRequire\");\n\n    \t\tsuper.doView(renderRequest, renderResponse);\n    \t}\n    \t\n    \t@Reference\n    \tprivate NPMResolver _npmResolver;\n    \t\n    }\n    ```\n\n    And here is the corresponding example `view.jsp`:\n\n    ```markup\n    <%\n    String bootstrapRequire =\n      (String)renderRequest.getAttribute(\"bootstrapRequire\");\n    %>\n\n    <aui:script require=\"<%= bootstrapRequire %>\">\n      bootstrapRequire.default();\n    </aui:script>\n    ```\n\nNow you know how to reference an npm module's package! \n\n## Related Topics\n\n- [Obtaining an OSGi bundle's Dependency npm Package Descriptors](/docs/7-2/frameworks/-/knowledge_base/f/obtaining-dependency-npm-package-descriptors)\n- [liferay-npm-bundler](/docs/7-2/reference/-/knowledge_base/r/liferay-npm-bundler)\n- [How @product@ Publishes npm Packages](/docs/7-2/reference/-/knowledge_base/r/how-the-liferay-npm-bundler-publishes-npm-packages)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/03-using-npm-in-your-portlets/08-npmresolver-api/03-obtaining-dependency-npm-package-descriptors.markdown",
    "content": "---\nheader-id: obtaining-dependency-npm-package-descriptors\n---\n\n# Obtaining an OSGi bundle's Dependency npm Package Descriptors\n\n[TOC levels=1-4]\n\nWhile writing your npm portlet, you may need to reference a dependency package \nor its modules. For instance, you can retrieve an npm dependency package \nmodule's CSS file and use it in your portlet. The \n[`NPMResolver` OSGi component](@app-ref@/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/NPMResolver.html) \nprovides two methods for retrieving an OSGi bundle's dependency npm package \ndescriptors:\n[`getDependencyJSPackage()`](@app-ref@/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/NPMResolver.html#getDependencyJSPackage) \nto retrieve dependency npm packages and \n[`resolveModuleName()`](@app-ref@/foundation/latest/javadocs/com/liferay/frontend/js/loader/modules/extender/npm/NPMResolver.html#resolveModuleName) \nto retrieve dependency npm modules. This article references the `package.json` \nbelow to help demonstrate these methods:\n\n```json\n{\n\t\"dependencies\": {\n\t\t\"react\": \"15.6.2\",\n\t\t\"react-dom\": \"15.6.2\"\n\t},\n\t.\n\t.\n\t.\n}\n```\n\nTo obtain an OSGi bundle's npm dependency package, pass the package's name in as \nthe `getDependencyJSPackage()` method's argument. The example below resolves the \n`react` dependency package:\n\n```java\nString reactResolvedId = npmResolver.getDependencyJSPackage(\"react\");\n```\n\n`reactResolvedId`'s resulting value is `react@15.6.2`.\n\nYou can use the `resolveModuleName()` method To obtain a module in an npm \ndependency package. To do this, pass the module's relative path in as the \n`resolveModuleName()` method's argument. The example below resolves a module \nnamed `react-with-addons` for the `react` dependency package:\n\n```java\nString resolvedModule = \nnpmResolver.resolveModuleName(\"react/dist/react-with-addons\");\n```\n\nThe `resolvedModule` variable evaluates to `react@15.6.2/dist/react-with-addons`. \nYou can also use this to reference static resources inside npm packages (like \nCSS or image files), as shown in the example below:\n\n```java\nString cssPath = npmResolver.resolveModuleName(\n      \"react/lib/css/main.css\"); \n```\n\nNow you know how to obtain an OSGi bundle's dependency npm packages descriptors! \n\n## Related Topics\n\n- [Referencing an npm Module's Package](/docs/7-2/frameworks/-/knowledge_base/f/referencing-an-npm-modules-package)\n- [The Structure of OSGi Bundles Containing npm Packages](/docs/7-2/reference/-/knowledge_base/r/the-structure-of-osgi-bundles-containing-npm-packages)\n- [How @product@ Publishes npm Packages](/docs/7-2/reference/-/knowledge_base/r/how-the-liferay-npm-bundler-publishes-npm-packages)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/09-spa/01-spa-intro.markdown",
    "content": "---\nheader-id: automatic-single-page-applications\n---\n\n# Automatic Single Page Applications\n\n[TOC levels=1-4]\n\nA good user experience is the measure of a well-designed site. A user's time is \nhighly valuable. The last thing you want is for someone to grow frustrated with \nyour site because of constant page reloads. A Single Page Application (SPA) \navoids this issue. Single Page Applications drastically cut down on load times \nby loading only a single HTML page that's dynamically updated as the user \ninteracts and navigates through the site. This provides a more seamless app \nexperience by eliminating page reloads. **SPA is enabled by default in your apps \nand sites and requires no changes to your workflow or code!** If Spa is \ndisabled, ensure that the `com.liferay.frontend.js.spa.web-[version]` module is \ndeployed and active. \n\n## The Benefits of SPAs\n\nLet's say you're surfing the web and you find a really rad site that happens to \nbe SPA enabled. All right! Page load times are blazin' fast. You're deep into \nthe site, scrolling along, when you find this great post that just speaks to \nyou. You copy the URL from the address bar and email it to all of your friends \nwith the subject: 'Your Life Will Change Forever.' They must experience this \nawe-inspiring work! \n\nYou get a response back almost immediately. \"This is a rad site, but what post \nare you talking about?\" it reads. \n\n\"What!? Do my eyes deceive me?\" you exclaim. You were in so much of a hurry to \nshare this life-changing content that you neglected to notice that the URL never \nupdated when you clicked the post. You click the back button, hoping to get \nback to the post, but it takes you to the site you were on before you ever \nvisited this one. The page history didn't update as you navigated through the \napp; Only the main app URL was saved. \n\nWhat a bummer! \"Why? Why have you failed me, site?\" you cry. \n\nIf only there was a way to have a Single Page Application, but also be able to \nlink to the content you want. Well, don't despair my friend. You can have your \ncake and eat it too, thanks to SennaJS. \n\n## What is SennaJS?\n\nSennaJS is @product@'s SPA engine. SennaJS handles the client-side data, and \nAJAX loads the page's content dynamically. While there are other JavaScript \nframeworks out there that may provide some of the same features, Senna's only \nfocus is SPA, ensuring that your site provides the best user experience \npossible. \n\nSennaJS provides the following key enhancements to SPA:\n\n**SEO & Bookmarkability**: Sharing or bookmarking a link displays the same \ncontent you are viewing. Search engines are able to index this content. \n\n**Hybrid rendering**: Ajax + server-side rendering lets you disable `pushState` \nat any time, allowing progressive enhancement. You can use your preferred method \nto render the server side (e.g. HTML fragments or template views). \n\n**State retention**: Scrolling, reloading, or navigating through the history of \nthe page takes you back to where you were. SennaJS exposes lifecycle events that \nrepresent state changes in the application. See [Available SPA Lifecycle Events](available-spa-lifecycle-events) \nfor more information. \n\n**UI feedback**: The UI indicates to the user when some content is requested. \n\n**Pending navigations**: UI rendering is blocked until data is loaded, and the \ncontent is displayed all at once. \n\n**Timeout detection**: If the request takes too long to load or the user tries \nto navigate to a different link while another request is pending, the request \ntimes out. \n\n**History navigation**: The browser history is manipulated via the \n[History API](https://developer.mozilla.org/en-US/docs/Web/API/History), \nso you can use the back and forward history buttons to navigate through the \nhistory of the page. \n\n**Cacheable screens**: Once a surface is loaded, the content is cached in memory \nand is retrieved later without any additional request, speeding up your \napplication. \n\n**Page resources management**: Scripts and stylesheets are evaluated from \ndynamically loaded resources. Additional content can be appended to the DOM \nusing `XMLHttpRequest`. For security reasons, some browsers won't evaluate \n`<script>` tags from content fragments. Therefore, SennaJS extracts scripts from \nthe content and parses them to ensure that they meet the browser loading \nrequirements. \n\nYou can see examples and read more about SennaJS at its [website](http://sennajs.com/). \n\nThis section covers these topics:\n\n- Configuring SPA System Settings\n- Disabling SPA\n- Specifying how resources are loaded during SPA navigation\n- Detaching Global Listeners\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/09-spa/02-configuring-spa-system-settings.markdown",
    "content": "---\nheader-id: configuring-spa-system-settings\n---\n\n# Configuring SPA System Settings\n\n[TOC levels=1-4]\n\nDepending on what behaviors you need to customize, you can configure SPA options \nin one of two places. SPA caching and SPA timeout settings are configured in \nSystem Settings. If you wish to disable SPA for a certain link, page, or portlet \nin your site, see [Disabling SPA](/docs/7-2/frameworks/-/knowledge_base/f/disabling-spa). \n\nFollow these steps to configure system settings for SPA:\n\n1.  In the Control Panel, navigate to *Configuration* &rarr; *System Settings*.\n\n2.  Select *Infrastructure* under the *PLATFORM* heading.\n\n3.  Click *Frontend SPA Infrastructure*.\n\nThe following configuration options are available:\n\n**Cache Expiration Time**:  The time, in minutes, in which the SPA cache is \ncleared. A negative value means the cache should be disabled. \n\n**Navigation Exception Selectors**: Defines a CSS selector that SPA should \nignore. \n\n**Request Timeout Time**: The time, in milliseconds, in which a SPA request \ntimes out. A zero value means the request should never timeout. \n\n**User Notification Timeout**: The time, in milliseconds, in which a \nnotification is shown to the user stating that the request is taking longer than \nexpected. A zero value means no notification should be shown. \n\nGreat! Now you know how to configure system settings for SPA. \n\n## Related Topics\n\n- [Disabling SPA](/docs/7-2/frameworks/-/knowledge_base/f/disabling-spa)\n- [Specifying How Resources Are Loaded During SPA Navigation](/docs/7-2/frameworks/-/knowledge_base/f/specifying-how-resources-are-loaded-during-navigation)\n- [Detaching Global Listeners](/docs/7-2/frameworks/-/knowledge_base/f/detaching-global-listeners)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/09-spa/03-disabling-spa.markdown",
    "content": "---\nheader-id: disabling-spa\n---\n\n# Disabling SPA\n\n[TOC levels=1-4]\n\nCertain elements of your page may require a regular navigation to work properly.\nFor example, you may have downloadable content that you want to share with the\nuser. In these cases, SPA must be disabled for those specific elements. You can\ndisable SPA at these scopes:\n\n- disable SPA across an entire @product@ instance\n- disable SPA in a portlet\n- Disable SPA in individual elements\n\nFollow the steps in the corresponding section to disable SPA for that scope.\n\n## Disabling SPA across an Instance\n\nTo disable SPA across an entire @product@ instance, add the following line to\nyour `portal-ext.properties`:\n\n```properties\njavascript.single.page.application.enabled = false\n```\n\n## Disabling SPA for a Portlet\n\nTo disable SPA for a portlet, you must blacklist it. To blacklist a portlet from\nSPA, follow these steps:\n\n1.  Open your portlet class.\n\n2.  Set the `com.liferay.portlet.single-page-application` property to false:\n\n    ```java\n    com.liferay.portlet.single-page-application=false\n    ```\n\n    If you prefer, you can set this property to false in your `liferay-portlet.xml`\n    instead by adding the following property to the `<portlet>` section:\n\n    ```xml\n    <single-page-application>false</single-page-application>\n    ```\n\n3.  Alternatively, you can override the\n    [`isSinglePageApplication` method](@platform-ref@/7.2-latest/javadocs/portal-impl/com/liferay/portal/model/impl/PortletImpl.html#isSinglePageApplication--)\n    of the portlet to return `false`.\n\n## Disabling SPA for an Individual Element\n\nTo disable SPA for a form or link follow these steps:\n\n1.  Add the `data-senna-off` attribute to the element.\n\n2.  Set the value to `true`. See the example below:\n\n    ```html\n    <a data-senna-off=\"true\" href=\"/pages/page2.html\">Page 2</a>\n    ```\n\nNice! Now you know how to disable SPA in your app.\n\n## Related Topics\n\n- [Configuring SPA System Settings](/docs/7-2/frameworks/-/knowledge_base/f/configuring-spa-system-settings)\n- [Specifying How Resources Are Loaded During SPA Navigation](/docs/7-2/frameworks/-/knowledge_base/f/specifying-how-resources-are-loaded-during-navigation)\n- [Detaching Global Listeners](/docs/7-2/frameworks/-/knowledge_base/f/detaching-global-listeners)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/09-spa/04-specifying-how-resources-are-loaded-during-spa-navigation.markdown",
    "content": "---\nheader-id: specifying-how-resources-are-loaded-during-navigation\n---\n\n# Specifying How Resources Are Loaded During Navigation\n\n[TOC levels=1-4]\n\nBy default, @product@ unloads CSS resources from the `<head>` element on \nnavigation. JavaScript resources in the `<head>`, however, are not removed on \nnavigation. This functionality can be customized by setting the resource's \n`data-senna-track` attribute. Follow these steps to customize your resources:\n\n1.  Select the resource you want to modify the default behavior for.\n\n2.  Add the `data-senna-track` attribute to the resource.\n\n3.  Set the `data-senna-track` attribute to `permanent` to prevent a resource \n    from unloading on navigation.\n    \n    Alternatively, set the `data-senna-track` attribute to `temporary` to unload \n    the resource on navigation. \n    \n    | **Note:** the `data-senna-track` attribute can be added to resources loaded\n    | outside of the `<head>` element as well to specify navigation behavior.\n    \nThe example below ensures that the JS resource isn't unloaded during navigation:\n\n```html\n<script src=\"myscript.js\" data-senna-track=\"permanent\" />\n```\n\nGreat! Now you know how to specify how resources are loaded during SPA \nnavigation. \n\n## Related Topics\n\n- [Configuring SPA System Settings](/docs/7-2/frameworks/-/knowledge_base/f/configuring-spa-system-settings)\n- [Disabling SPA](/docs/7-2/frameworks/-/knowledge_base/f/disabling-spa)\n- [Detaching Global Listeners](/docs/7-2/frameworks/-/knowledge_base/f/detaching-global-listeners)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/07-using-javascript-in-your-portlets/09-spa/05-detaching-global-listeners.markdown",
    "content": "---\nheader-id: detaching-global-listeners\n---\n\n# Detaching Global Listeners\n\n[TOC levels=1-4]\n\nSPA provides several improvements that highly benefit your site and users, but \nwith great power comes great re-SPA-nsibility. I apologize for that last \nsentence, but the fact remains that there is potentially some additional \nmaintenance as a consequence of SPA. In a traditional navigation scenario, every \npage refresh resets everything, so you don't have to worry about what's left \nbehind. In a SPA scenario, however, global listeners such as `Liferay.on`, \n`Liferay.after`, or body delegates can become problematic. Every time you \nexecute these global listeners, you add yet another listener to the globally \npersisted `Liferay` object. The result is multiple invocations of those \nlisteners. This can obviously cause problems if not handled. \n\nFollow these steps to prevent potential problems:\n\n1.  Listen to the navigation event in order to detach your listeners. For \n    example, you would use the following code to listen to a global `category` \n    event and `destroyPortlet` event:\n\n    ```javascript\n    Liferay.on('category', function(event){...});\n    Liferay.on('destroyPortlet', function(event){...});\n    ```\n    \n2.  Detach the event listeners when the portlet is destroyed, as shown in the \n    example below:\n\n    ```javascript\n    var onCategory = function(event) {...};\n\n    var clearPortletHandlers = function(event) {\n        if (event.portletId === '<%= portletDisplay.getRootPortletId() %>') {\n            Liferay.detach('onCategoryHandler', onCategory);\n            Liferay.detach('destroyPortlet', clearPortletHandlers);\n        }\n    };\n\n\n    Liferay.on('category', onCategory);\n    Liferay.on('destroyPortlet', clearPortletHandlers);\n    ```\n\nGreat! Now you know how to properly maintain your global listeners for SPA. \n\n## Related Topics\n\n- [Available SPA Lifecycle Events](/docs/7-2/reference/-/knowledge_base/r/available-spa-lifecycle-events)\n- [Disabling SPA](/docs/7-2/frameworks/-/knowledge_base/f/disabling-spa)\n- [Configuring SPA System Settings](/docs/7-2/frameworks/-/knowledge_base/f/configuring-spa-system-settings)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/01-applying-clay-styles-intro.markdown",
    "content": "---\nheader-id: applying-clay-styles-to-your-app\n---\n\n# Applying Clay Styles to your App\n\n[TOC levels=1-4]\n\nIt's important to have a consistent user experience across your apps. \nPortal's built-in apps achieve this through Liferay's \n[Lexicon Experience Language](https://liferay.design/lexicon/) and its web \nimplementation, [Clay](https://clayui.com/docs/getting-started/clay.html). \n\nClay provides a consistent, user-friendly UI and is included in all themes that \nare based on the `_styled` base theme, making all the components documented on \nthe [Clay site](https://clayui.com/docs/components/alerts.html) accessible.\n\nThis means you can use Clay markup and components in your apps. This section \nexplains how to apply Clay's design patterns to achieve the same look and feel \nas Portal's built-in apps. \n\nThis section covers these topics:\n\n- Applying Clay to navigation\n- Implementing the Management Toolbar\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/02-clay-navigation-patterns/01-applying-clay-patterns-to-your-navigation-bar-intro.markdown",
    "content": "---\nheader-id: applying-clay-patterns-to-navigation\n---\n\n# Applying Clay Patterns to Navigation\n\n[TOC levels=1-4]\n\nThis article covers how to leverage Clay patterns in your app's navigation to \nmake it more user-friendly. Updating your app's navigation bar to use Clay is \neasy, thanks to the `<clay:navigation-bar />` tag. Follow these steps to update \nyour app:\n\n1.  Add the required imports to your app's `init.jsp`:\n\n    ```markup\n    // Import the clay tld file to be able to use the new tag\n    <%@ taglib uri=\"http://liferay.com/tld/clay\" prefix=\"clay\" %>\n\n    // Import the NavigationItem utility class to create the items model\n    <%@ page import=\"com.liferay.frontend.taglib.clay.servlet.taglib.util.JSPNavigationItemList\" %>\n    ```\n\n2.  Add the `frontend-taglib-clay` and `frontend.taglib.soy` module dependencies \n    to your app's `build.gradle` file:\n\n    ```groovy\n    compileOnly group: \"com.liferay\", name: \"com.liferay.frontend.taglib.soy\", \n    version: \"1.0.10\"\n\n    compileOnly group: \"com.liferay\", name: \"com.liferay.frontend.taglib.clay\", \n    version: \"1.0.0\"\n    ```\n\n3.  Inside your JSP view, add a java scriplet to retrieve the navigation \n    variable and portlet URL. An example configuration is shown below:\n\n    ```java\n    <%\n    final String navigation = ParamUtil.getString(request, \"navigation\", \n    \"entries\");\n\n    PortletURL portletURL = renderResponse.createRenderURL();\n\n    portletURL.setParameter(\"mvcRenderCommandName\", \"/blogs/view\");\n    portletURL.setParameter(\"navigation\", navigation);\n    %>\n    ```\n\n4.  Add the `<clay:navigation-bar />` tag to your app, and use the `items` \n    attribute to specify the navigation items. The navigation bar should be dark \n    if your app is intended for Admin use. To do this, set the `inverted` \n    attribute to `true`. If your app is intended for an instance on a live site, \n    keep the navigation bar light by setting the `inverted` attribute to \n    `false`. An example configuration for an admin app is shown below:\n\n    ```markup\n    <clay:navigation-bar\n    \tinverted=\"<%= true %>\"\n    \tnavigationItems=\"<%=\n    \t\tnew JSPNavigationItemList(pageContext) {\n    \t\t\t{\n    \t\t\t\tadd(\n    \t\t\t\tnavigationItem -> {\n    \t\t\t\t\tnavigationItem.setActive(navigation.equals(\"entries\"));\n    \t\t\t\t\tnavigationItem.setHref(renderResponse.createRenderURL());\n    \t\t\t\t\tnavigationItem.setLabel(LanguageUtil.get(request, \"entries\"));\n    \t\t\t\t});\n\n    \t\t\t\tadd(\n    \t\t\t\tnavigationItem -> {\n    \t\t\t\t\tnavigationItem.setActive(navigation.equals(\"images\"));\n    \t\t\t\t\tnavigationItem.setHref(renderResponse.createRenderURL(), \n              \"navigation\", \"images\");\n    \t\t\t\t\tnavigationItem.setLabel(LanguageUtil.get(request, \"images\"));\n    \t\t\t\t});\n    \t\t\t}\n    \t\t}\n    \t%>\"\n    />\n    ```\n\n5.  Add a conditional block to display the proper JSP for the selected \n    navigation item. An example configuration for the Blogs Admin portlet is \n    shown below:\n\n    ```markup\n    <c:choose>\n    \t<c:when test='<%= navigation.equals(\"entries\") %>'>\n    \t\t<liferay-util:include page=\"/blogs_admin/view_entries.jsp\" \n        servletContext=\"<%= application %>\" />\n    \t</c:when>\n    \t<c:otherwise>\n    \t\t<liferay-util:include page=\"/blogs_admin/view_images.jsp\" \n        servletContext=\"<%= application %>\" />\n    \t</c:otherwise>\n    </c:choose>\n    ```\n\nLive site navigation bar:\n\n![Figure 1: The navigation bar should be light for apps on the live site.](../../../../images/clay-patterns-navbar.png)\n\nAdmin app navigation bar:\n\n![Figure 2: The navigation bar should be dark (inverted) in admin apps.](../../../../images/clay-patterns-navbar-inverted.png)\n\nSweet! Now you know how to style a navigation bar with Clay. \n\n## Related topics\n\n[Implementing the Management Toolbar](/docs/7-2/frameworks/-/knowledge_base/f/implementing-the-management-toolbar)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/03-clay-management-toolbar/01-implementing-the-management-toolbar-intro.markdown",
    "content": "---\nheader-id: implementing-the-management-toolbar\n---\n\n# Implementing the Management Toolbar\n\n[TOC levels=1-4]\n\nThe Management Toolbar is a combination of search, filters, sorting options, and \ndisplay options that let you manage data. For admin apps, we recommend that you \nadd a management toolbar to manage your search container results. The \n[Clay Management Toolbar](/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar) \ntaglib reference covers how to use the Clay taglibs to create the Management \nToolbar. This section covers how to create the features below for the Management \nToolbar:\n\n- Implementing View Types\n- Sorting and Filtering Items\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/03-clay-management-toolbar/02-configuring-view-types/01-configuring-mgmt-bar-view-types-intro.markdown",
    "content": "---\nheader-id: implementing-the-view-types\n---\n\n# Implementing the View Types\n\n[TOC levels=1-4]\n\nThe Management Toolbar has three predefined view types for your app's search \ncontainer results. Each style offers a slightly different look and feel. To \nprovide these view types in your app, you must make some updates to your search \nresult columns. Start by defining the view types you want to provide.\n\n- **Cards:** displays search result columns on a horizontal or vertical card \n\n- **List:** displays a detailed description along with summarized details for \nthe search result columns  \n\n- **Table:** the default view, which list the search result columns from left to \nright \n\nFollow these steps to define the view types for your management toolbar:\n\n1.  Import the `ViewTypeItemList` utility class to create the action items \n    model:\n\n    ```markup\n    <%@ page import=\"com.liferay.frontend.taglib.clay.servlet.taglib.util.JSPViewTypeItemList\" %>\n    ```\n\n2.  Add the `frontend.taglib.clay` and `frontend.taglib.soy` module dependencies \n    to your app's `build.gradle` file:\n\n    ```groovy\n    compileOnly group: \"com.liferay\", name: \"com.liferay.frontend.taglib.soy\", \n    version: \"1.0.10\"\n\n    compileOnly group: \"com.liferay\", name: \"com.liferay.frontend.taglib.clay\", \n    version: \"1.0.0\"\n    ```\n\n3.  In your app's main view, retrieve the `displayStyle` for reference. Each \n    view type corresponds to a display style. this is used to determine the \n    proper content configuration to display for the selected view type:\n\n    ```java\n    <%\n    String displayStyle = ParamUtil.getString(request, \"displayStyle\");\n    %>\n    ```\n\n4.  Add the management toolbar to your app's main view and configure the display \n    buttons as shown below. Note that while this example implements all three \n    view types, only one view type is required. The default or active view type \n    is set by adding `viewTypeItem.setActive(true)` to the view type:\n\n    ```markup\n    <clay:management-toolbar\n        disabled=<%= assetTagsDisplayContext.isDisabledTagsManagementBar() %>\n        namespace=\"<%= renderResponse.getNamespace() %>\"\n        searchContainerId=\"assetTags\"\n        selectable=\"<%= true %>\"\n        viewTypeItems=\"<%=\n            new JSPViewTypeItemList(pageContext, baseURL, selectedType) {\n                {\n                \taddCardViewTypeItem(\n                \t\tviewTypeItem -> {\n                \t\t\tviewTypeItem.setActive(true);\n                \t\t\tviewTypeItem.setLabel(\"Card\");\n                \t\t});\n\n                \taddListViewTypeItem(\n                \t\tviewTypeItem -> {\n                \t\t\tviewTypeItem.setLabel(\"List\");\n                \t\t});\n\n                \taddTableViewTypeItem(\n                \t\tviewTypeItem -> {\n                \t\t\tviewTypeItem.setLabel(\"Table\");\n                \t\t});\n                }\n            }\n        %>\"\n    />\n    ```\n        \n    `viewTypeItems`: The available view types\n\n5.  Create a conditional block to check for the view types. If you only have \n    one view type, you can skip this step.\n\n    ```markup\n    <c:choose>\n        <%-- view type configuration goes here --%>\n    </c:choose>\n    ```\n\nNow that the view types are defined, you can follow the instructions in the rest \nof this section to configure them. \n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/03-clay-management-toolbar/02-configuring-view-types/02-implementing-the-card-view.markdown",
    "content": "---\nheader-id: implementing-the-card-view\n---\n\n# Implementing the Card View\n\n[TOC levels=1-4]\n\nThe card view displays the entry's information in a vertical or horizontal card \nwith an image, user profile, or an icon representing the content's type, along \nwith details about the content, such as its name, workflow status, and a \ncondensed description.\n\nSee the \n[Liferay Frontend Cards](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-cards) \nreference for examples and use cases of each card. \n\n![Figure 1: The Management Toolbar's card view gives a quick summary of the content's description and status.](../../../../../images/clay-taglib-management-toolbar-view-type-card.png)\n\nFollow the steps below to create your card view:\n\n1.  Inside the `<c:choose>` conditional block, add a condition for the icon \n    display style (Card view type):\n    \n    ```markup\n    <c:when test='<%= Objects.equals(displayStyle, \"icon\") %>'>\n        <%-- card view type configuration goes here --%>\n    </c:when>\n    ```\n\n2.  Add the proper java scriplet to make the card view responsive to different \n    devices:\n\n    Use the pattern below for vertical cards:\n\n    ```java\n    <%\n    row.setCssClass(\"col-md-2 col-sm-4 col-xs-6\");\n    %>\n    ```\n    \n    For horizontal cards use the following pattern:\n\n    ```java  \n    <%\n    row.setCssClass(\"col-md-3 col-sm-4 col-xs-12\");\n    %>\n    ```\n\n3.  Add the search container column text containing your card. The card should \n    include the actions for the entry(if applicable), as well as an image, icon \n    or user profile, and the entry's title. An example configuration is shown \n    below:\n\n    ```markup\n    <liferay-frontend:icon-vertical-card\n      actionJsp='<%= dlPortletInstanceSettingsHelper.isShowActions() ? \n      \"/image_gallery_display/image_action.jsp\" : StringPool.BLANK %>'\n      actionJspServletContext=\"<%= application %>\"\n      cssClass=\"entry-display-style\"\n      icon=\"documents-and-media\"\n      resultRow=\"<%= row %>\"\n      title=\"<%= dlPortletInstanceSettingsHelper.isShowActions() ? \n      fileEntry.getTitle() : StringPool.BLANK %>\"\n    />\n    ```\n\nGreat! Now you know how to implement the Card view! \n\n## Related Topics\n\n- [Configuring the Clay Management Toolbar Taglib](/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar)\n- [Filtering and Sorting Items with the Management Toolbar](/docs/7-2/frameworks/-/knowledge_base/f/filtering-and-sorting-items-with-the-management-toolbar)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/03-clay-management-toolbar/02-configuring-view-types/03-implementing-the-list-view.markdown",
    "content": "---\nheader-id: implementing-the-list-view\n---\n\n# Implementing the List View\n\n[TOC levels=1-4]\n\nThe list view displays the entry's complete description, along with a small icon \nfor the content type, and its name. \n\n![Figure 1: The Management Toolbar's list view gives the content's full description.](../../../../../images/clay-taglib-management-toolbar-view-type-list.png)\n\nFollow these steps to configure the List view:\n\n1.  Inside the `<c:choose>` conditional block, add a condition for the \n    descriptive display style (list view type):\n\n    ```markup\n    <c:when test='<%= Objects.equals(displayStyle, \"descriptive\") %>'>\n        <%-- list view type configuration goes here --%>\n    </c:when>\n    ```\n\n2.  Follow the example below to add the three columns to the conditional block:\n\nColumn | Content Options | Example\n------------- | ------------- | -------------\n1 | icon | &lt;liferay-ui:search-container-column-icon/&gt;\n&nbsp; | image | &lt;liferay-ui:search-container-column-image/&gt;\n&nbsp;  | user portrait | &lt;liferay-ui:search-container-column-text&gt;<br/>&nbsp;&nbsp;&lt;liferay-ui:user-portrait/&gt;<br/>&lt;/liferay-ui:search-container-column-text&gt;\n2 | description | <liferay-ui:search-container-column-text <br/>&nbsp;&nbsp;colspan=\"<%=2%>\" <br/>><br/>&nbsp;&nbsp;&lt;h5&gt;<%= userGroup.getName() %>&lt;/h5&gt; <br/>&nbsp;&nbsp;&lt;h6 class=\"text-default\"&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;span&gt;<%= userGroup.getDescription() %>&lt;/span&gt; <br/>&nbsp;&nbsp;&lt;/h6&gt; <br/>&nbsp;&nbsp;&lt;h6 class=\"text-default\"&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;span&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<liferay-ui:message arguments=\"<%= usersCount%>\" key=\"x-users\"/> <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/span&gt; <br/>&nbsp;&nbsp;&lt;/h6&gt; <br/></liferay-ui:search-container-column-text> \n3 | actions | <liferay-ui:search-container-column-jsp<br/> &nbsp;&nbsp;path=\"/edit_team_assignments_user_groups_action.jsp\"<br/>/>\n\nGreat! Now you know how to implement the List view! \n\n## Related Topics\n\n- [Configuring the Clay Management Toolbar Taglib](/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar)\n- [Filtering and Sorting Items with the Management Toolbar](/docs/7-2/frameworks/-/knowledge_base/f/filtering-and-sorting-items-with-the-management-toolbar)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/03-clay-management-toolbar/02-configuring-view-types/04-implementing-the-table-view.markdown",
    "content": "---\nheader-id: implementing-the-table-view\n---\n\n# Implementing the Table View\n\n[TOC levels=1-4]\n\nThe table view list the search container columns from left to right. \n\n![Figure 1: The Management Toolbar's table view list the content's information in individual columns.](../../../../../images/clay-taglib-management-toolbar-view-type-table.png)\n\nFollow these steps to configure the Table view:\n\n1.  Inside the `<c:choose>` conditional block, add a condition for the list \n    display style (table view type):\n\n    ```markup\n    <c:when test='<%= Objects.equals(displayStyle, \"list\") %>'>\n      <%-- table view type configuration goes here --%>\n    </c:when>\n    ```\n\n2.  Follow the example below to add the required columns to the conditional \n    block:\n\nColumn | Content Options | Example\n------------- | ------------- | -------------\n1 | name | <liferay-ui:search-container-column-text <br/>&nbsp;&nbsp;cssClass=\"content-column name-column title-column\" <br/>&nbsp;&nbsp;name=\"name\" <br/>&nbsp;&nbsp;truncate=\"<%= true %>\" <br/>&nbsp;&nbsp;value=\"<%= rule.getName(locale) %>\" <br/>/>\n2 | description | <liferay-ui:search-container-column-text <br/>&nbsp;&nbsp;cssClass=\"content-column description-column\" <br/>&nbsp;&nbsp;name=\"description\" <br/>&nbsp;&nbsp;truncate=\"<%= true %>\" <br/>&nbsp;&nbsp;value=\"<%= rule.getDescription(locale) %>\" <br/>/>\n3 | create date | <liferay-ui:search-container-column-date <br/>&nbsp;&nbsp;cssClass=\"create-date-column text-column\" <br/>&nbsp;&nbsp;name=\"create-date\" <br/>&nbsp;&nbsp;property=\"createDate\" <br/>/>\n4 | actions | <liferay-ui:search-container-column-jsp <br/>&nbsp;&nbsp; cssClass=\"entry-action-column\" <br/>&nbsp;&nbsp;path=\"/rule_actions.jsp\" <br/>/>\n\nGreat! Now you know how to implement the Table view! \n\n## Related Topics\n\n- [Configuring the Clay Management Toolbar Taglib](/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar)\n- [Filtering and Sorting Items with the Management Toolbar](/docs/7-2/frameworks/-/knowledge_base/f/filtering-and-sorting-items-with-the-management-toolbar)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/03-clay-management-toolbar/02-configuring-view-types/05-updating-the-search-iterator.markdown",
    "content": "---\nheader-id: updating-the-search-iterator\n---\n\n# Updating the Search Iterator\n\n[TOC levels=1-4]\n\nOnce the view type's display styles are defined, you must update the search \niterator to show the selected view type. Follow these steps:\n\n1.  If your management toolbar only has one view type, you can set the \n    `displayStyle` attribute to the style you defined, otherwise follow the \n    pattern below:\n\n    ```markup\n    <liferay-ui:search-iterator\n        displayStyle=\"<%= displayStyle %>\"\n        markupView=\"lexicon\"\n        searchContainer=\"<%= searchContainer %>\"\n    />\n    ```\n\n    The `displayStyle`'s value is set to the [current view type](/docs/7-2/frameworks/-/knowledge_base/f/implementing-the-view-types).\n\n2.  Save the changes. \n\nGreat! You've updated your search iterator to use your display styles. \n\n## Related Topics\n\n- [Configuring the Clay Management Toolbar Taglib](/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar)\n- [Filtering and Sorting Items with the Management Toolbar](/docs/7-2/frameworks/-/knowledge_base/f/filtering-and-sorting-items-with-the-management-toolbar)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/03-clay-management-toolbar/03-filtering-and-sorting/01-filtering-and-sorting-items-with-the-management-toolbar-intro.markdown",
    "content": "---\nheader-id: filtering-and-sorting-items-with-the-management-toolbar\n---\n\n# Filtering and Sorting Items with the Management Toolbar\n\n[TOC levels=1-4]\n\nThe Management Toolbar lets you filter and sort your search container results. \nWhile you can configure the toolbar's filters in the JSP, this can quickly crowd \nthe JSP. We recommend instead that you move this functionality to a separate \njava class, which we refer to as a Display Context throughout this tutorial.  \n\nThere are two main types of filters: navigation and order. Both of these are \ncontained within the same dropdown menu. Follow the steps below to create your \nfilters.\n\n1.  Depending on your needs, there are two classes that you can extend for your \n    management toolbar Display Context. These base classes provide the required \n    methods to create your navigation and order filters: \n    \n    - [`BaseManagementToolbarDisplayContext`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-taglib/frontend-taglib-clay/src/main/java/com/liferay/frontend/taglib/clay/servlet/taglib/display/context/BaseManagementToolbarDisplayContext.java): \n      for apps without a search container\n    - [`SearchContainerManagementToolbarDisplayContext`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-taglib/frontend-taglib-clay/src/main/java/com/liferay/frontend/taglib/clay/servlet/taglib/display/context/SearchContainerManagementToolbarDisplayContext.java): \n      for apps with a search container (extends \n      `BaseManagementToolbarDisplayContext` and provides additional logic for \n      search containers)\n\n    An example configuration for each is shown below:\n    \n    `BaseManagementToolbarDisplayContext` example:\n\n    ```java\n    public class MyManagementToolbarDisplayContext\n      extends BaseManagementToolbarDisplayContext {\n\n      public MyManagementToolbarDisplayContext(\n        LiferayPortletRequest liferayPortletRequest,\n        LiferayPortletResponse liferayPortletResponse,\n        HttpServletRequest request) {\n\n        super(liferayPortletRequest, liferayPortletResponse, request);\n      }\n      ...\n    }\n    ```\n\n    `SearchContainerManagementToolbarDisplayContext` example:\n\n    ```java\n    public class MyManagementToolbarDisplayContext\n      extends SearchContainerManagementToolbarDisplayContext {\n\n      public MyManagementToolbarDisplayContext(\n        LiferayPortletRequest liferayPortletRequest,\n        LiferayPortletResponse liferayPortletResponse,\n        HttpServletRequest request, SearchContainer searchContainer) {\n\n        super(\n          liferayPortletRequest, liferayPortletResponse, request,\n          searchContainer);\n      }\n    }\n    ```\n\n2.  Override the `getNavigationKeys()` method to return the navigation filter \n    dropdown item(s). If your app doesn't require any navigation filters, you \n    can just provide the *all* filter to display everything. An example \n    configuration is shown below:\n\n    ```java\n    @Override\n    protected String[] getNavigationKeys() {\n      return new String[] {\"all\", \"pending\", \"done\"};\n    }\n    ```\n\n3.  override the `getOrderByKeys()` method to return the columns to order. An \n    example configuration is shown below:\n\n    ```java\n    @Override\n    protected String[] getOrderByKeys() {\n      return new String[] {\"name\", \"items\", \"status\"};\n    }\n    ```\n\n4.  Open the JSP view that contains the Clay Management Toolbar and set its \n    `displayContext` attribute to the Display Context you created. An example \n    configuration is shown below:\n\n    ```markup\n    <clay:management-toolbar\n    \tdisplayContext=\"<%= myManagementToolbarDisplayContext %>\"\n    />\n    ```\n\nNow you know how to configure the Management Toolbar's filters via a Display \nContext. \n\n## Related Topics\n\n- [Configuring Filtering and Sorting Management Toolbar Attributes](/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar#filtering-and-sorting-search-results)\n- [Implementing the View Types](/docs/7-2/frameworks/-/knowledge_base/f/implementing-the-view-types)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/04-app-title-and-back-url/01-configuring-your-apps-title-and-back-link-intro.markdown",
    "content": "---\nheader-id: configuring-your-applications-title-and-back-link\n---\n\n# Configuring Your Application's Title and Back Link\n\n[TOC levels=1-4]\n\nFor administration applications, the title should be moved to the inner views of \nthe app and the associated back link should be moved to the portlet titles. If \nyou open the Blogs Admin application in the Control Panel and add a new blog \nentry, you'll see this behavior in action:\n\n![Figure 1: Adding a new blog entry displays the portlet title at the top, along with a back link.](../../../../images/new-blog-entry-title.png)\n\nThe Blogs Admin application is used as an example throughout this article. \nFollow these steps to configure your app's title and back URL:\n\n1.  Use `ParamUtil` to retrieve the redirect for the URL:\n\n    ```java\n    String redirect = ParamUtil.getString(request, \"redirect\");\n    ```\n\n2.  Display the back icon and set the back URL to the `redirect`:\n\n    ```java\n    portletDisplay.setShowBackIcon(true);\n    portletDisplay.setURLBack(redirect);\n    ```\n\n3.  Finally, set the title using the `renderResponse.setTitle()` method. The \n    example below provides a title for two scenarios: \n\n    - If an existing blog entry is being updated, the blog's title is displayed.\n    - Otherwise it defaults to *New Blog Entry* since a new blog entry is being \n      created. \n\n    ```java\n    renderResponse.setTitle((entry != null) ? entry.getTitle() : \n    LanguageUtil.get(request, \"new-blog-entry\"));\n    %>\n    ```\n\n4.  Update any back links in the view to use the `redirect`. The Blog Admin \n    app's `edit_entry.jsp` form's cancel button is shown below as an example:\n\n    ```java\n    <aui:button cssClass=\"btn-lg\" href=\"<%= redirect %>\" name=\"cancelButton\" \n    type=\"cancel\" />\n    ```\n\nGreat! Now you know how to configure your app's title and back URL. \n\n## Related Topics\n\n- [Applying Clay Patterns to Your Navigation Bar](/docs/7-2/frameworks/-/knowledge_base/f/applying-clay-patterns-to-navigation)\n- [Setting Empty Results Messages](/docs/7-2/frameworks/-/knowledge_base/f/setting-empty-results-messages)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/05-add-button-pattern/01-using-the-add-button-pattern-intro.markdown",
    "content": "---\nheader-id: applying-the-add-button-pattern\n---\n\n# Applying the Add Button Pattern\n\n[TOC levels=1-4]\n\nClay's add button pattern is for actions that add entities (for example \na new blog entry button). It gives you a clean, minimal UI. You can use it in \nany of your app's screens. Follow these steps to add a plus button to your app:\n\n1.  Add the `liferay-frontend` taglib declaration to your portlet's `init.jsp`:\n\n    ```markup\n    <%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n    ```\n\n2.  Add an [`add-menu` tag](@app-ref@/frontend-taglib/latest/taglibdocs/liferay-frontend/add-menu.html) \n    to your portlet's view:\n\n    ```markup\n    <liferay-frontend:add-menu>\n    </liferay-frontend:add-menu>\n    ```\n\n3.  Nest a [`<liferay-frontend:add-menu-item>`](@app-ref@/frontend-taglib/latest/taglibdocs/liferay-frontend/add-menu-item.html) \n    tag for every menu item you have. Here's an example of the add button \n    pattern with a single item:\n\n    ```markup\n    <liferay-frontend:add-menu>\n        <liferay-frontend:add-menu-item \n          title='<%= LanguageUtil.get(request,\"titleName\") %>' \n          url=\"<%= nameURL.toString() %>\" \n        />\n    </liferay-frontend:add-menu>\n    ```\n\n    If there's only one item, the plus icon acts as a button that triggers the \n    item. If there's multiple items, clicking the plus icon displays a menu \n    containing them. \n\n    ![Figure 1: The add button pattern consists of an `add-menu` tag and at least one `add-menu-item` tag.](../../../../images/add-button-diagram.png)\n\nThe `com.liferay.mobile.device.rules.web` module's add menu is shown below:\n\n```markup\n<liferay-frontend:add-menu\n  inline=\"<%= true %>\"\n>\n  <liferay-frontend:add-menu-item\n    title='<%= LanguageUtil.get(resourceBundle, \"add-device-family\") %>'\n    url=\"<%= addRuleGroupURL %>\"\n  />\n</liferay-frontend:add-menu>\n```\n\nThere you have it! Now you know how to use the add button pattern. \n\n## Related Topics\n\n- [Setting Empty Results Messages](/docs/7-2/frameworks/-/knowledge_base/f/setting-empty-results-messages)\n- [Implementing the Management Toolbar](/docs/7-2/frameworks/-/knowledge_base/f/implementing-the-management-toolbar)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/06-configuring-admin-apps/01-configuring-your-admin-apps-actions-menus-intro.markdown",
    "content": "---\nheader-id: configuring-your-admin-apps-actions-menu\n---\n\n# Configuring Your Admin App's Actions Menu\n\n[TOC levels=1-4]\n\nRather then have a series of buttons or menus with actions in the different \nviews of the app, you can move all of these actions to the upper right menu of \nthe administrative portlet, leaving the primary action (often an \n[\"Add\" operation](/docs/7-2/frameworks/-/knowledge_base/f/applying-the-add-button-pattern)) \nvisible in the add menu. For example, the web content application has the \nactions menu shown below:\n\n![Figure 1: The upper right ellipsis menu contains most of the actions for the app.](../../../../images/actions-menu.png)\n\nFollow these steps to configure the actions menu in your admin app:\n\n1.  Create a `*ConfigurationIcon` Component class for the action that extends \n    the [`BasePortletConfigurationIcon` class](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/configuration/icon/BasePortletConfigurationIcon.html) \n    and implements the `PortletConfigurationIcon` service:\n    \n    ```java\n    @Component(\n    \timmediate = true,\n    \tservice = PortletConfigurationIcon.class\n    )\n    public class MyConfigurationIcon extends BasePortletConfigurationIcon {\n      ...\n    }\n    ```\n\n2.  Override the `getMessage()` method to specify the action's label:\n\n    ```java\n    @Override\n    public String getMessage(PortletRequest portletRequest) {\n      ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(\n        WebKeys.THEME_DISPLAY);\n\n      ResourceBundle resourceBundle = ResourceBundleUtil.getBundle(\n        themeDisplay.getLocale(), ExportAllConfigurationIcon.class);\n\n      return LanguageUtil.get(resourceBundle, \"export-all-settings\");\n    }\n    ```\n\n3.  Override the `get()` method to specify whether the action is invoked with \n    the `GET` or `POST` method:\n    \n    ```java\n    @Override\n    public String getMethod() {\n    \treturn \"GET\";\n    }\n    ```\n\n4.  Override the `getURL()` method to specify the URL (or `onClick` \n    JavaScript method) to invoke when the action is clicked:\n\n    ```java\n    @Override\n    public String getURL(\n      PortletRequest portletRequest, PortletResponse portletResponse) {\n\n      LiferayPortletURL liferayPortletURL =\n        (LiferayPortletURL)_portal.getControlPanelPortletURL(\n          portletRequest, ConfigurationAdminPortletKeys.SYSTEM_SETTINGS,\n          PortletRequest.RESOURCE_PHASE);\n\n      liferayPortletURL.setResourceID(\"export\");\n\n      return liferayPortletURL.toString();\n    }\n    ```\n\n5.  Override the `getWeight()` method to specify the order that the action \n    should appear in the list:\n    \n    ```java\n    @Override\n    public double getWeight() {\n      return 1;\n    }\n    ```\n\n6.  Override the `isShow()` method to specify the context in which the action \n    should be displayed:\n\n    ```java\n    @Override\n    public boolean isShow(PortletRequest portletRequest) {\n    \treturn true;\n    }\n    ```\n\n7.  Define the view where you want to display the configuration options. By \n    default, if the portlet uses `mvcPath`, the global actions (such as \n    configuration, export/import, maximized, etc.) are displayed for the JSP \n    indicated in the initialization parameter of the portlet \n    `javax.portlet.init-param.view-template=/view.jsp`. The value indicates the \n    JSP where the global actions should be displayed. However, if the portlet \n    uses MVC Command, the views for the global actions must be indicated with \n    the initialization parameter \n    `javax.portlet.init-param.mvc-command-names-default-views=/wiki_admin/view` \n    and the value must contain the `mvcRenderCommandName` where the global \n    actions should be displayed. \n\n8.  If the portlet can be added to a page and you want to always include the \n    configuration options, add this initialization parameter to the portlet's \n    properties:\n\n    ```java\n    javax.portlet.init-param.always-display-default-configuration-icons=true\n    ```\n\n    In this example, the action appears in the System Settings portlet. To make \n    it appear in a secondary screen, you can use the `path` property as shown \n    below. The value of the `path` property depends on the MVC framework used to \n    develop the app. For the MVCPortlet framework, provide the path \n    (often a JSP) from the `mvcPath` parameter. For MVCPortlet with MVC \n    Commands, the path should contain the `mvcRenderCommandName` where the \n    actions should be displayed (such as `/document_library/edit_folder` for \n    example):\n\n    ```java\n    @Component(\n    \timmediate = true,\n    \tproperty = {\n    \t\t\"javax.portlet.name=\" + ConfigurationAdminPortletKeys.SYSTEM_SETTINGS,\n    \t\t\"path=/view_factory_instances\"\n    \t},\n    \tservice = PortletConfigurationIcon.class\n    )\n    public class ExportFactoryInstancesIcon extends BasePortletConfigurationIcon {\n    \n    \t@Override\n    \tpublic String getMessage(PortletRequest portletRequest) {\n    \t\tThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(\n    \t\t\tWebKeys.THEME_DISPLAY);\n    \n    \t\tResourceBundle resourceBundle = ResourceBundleUtil.getBundle(\n    \t\t\tthemeDisplay.getLocale(), ExportFactoryInstancesIcon.class);\n    \n    \t\treturn LanguageUtil.get(resourceBundle, \"export-entries\");\n    \t}\n    \n    \t@Override\n    \tpublic String getMethod() {\n    \t\treturn \"GET\";\n    \t}\n    \n    \t@Override\n    \tpublic String getURL(\n    \t\tPortletRequest portletRequest, PortletResponse portletResponse) {\n    \n    \t\tLiferayPortletURL liferayPortletURL =\n    \t\t\t(LiferayPortletURL)_portal.getControlPanelPortletURL(\n    \t\t\t\tportletRequest, ConfigurationAdminPortletKeys.SYSTEM_SETTINGS,\n    \t\t\t\tPortletRequest.RESOURCE_PHASE);\n    \n    \t\tConfigurationModel factoryConfigurationModel =\n    \t\t\t(ConfigurationModel)portletRequest.getAttribute(\n    \t\t\t\tConfigurationAdminWebKeys.FACTORY_CONFIGURATION_MODEL);\n    \n    \t\tliferayPortletURL.setParameter(\n    \t\t\t\"factoryPid\", factoryConfigurationModel.getFactoryPid());\n    \n    \t\tliferayPortletURL.setResourceID(\"export\");\n    \n    \t\treturn liferayPortletURL.toString();\n    \t}\n    \n    \t@Override\n    \tpublic double getWeight() {\n    \t\treturn 1;\n    \t}\n    \n    \t@Override\n    \tpublic boolean isShow(PortletRequest portletRequest) {\n    \t\tConfigurationModelIterator configurationModelIterator =\n    \t\t\t(ConfigurationModelIterator)portletRequest.getAttribute(\n    \t\t\t\tConfigurationAdminWebKeys.CONFIGURATION_MODEL_ITERATOR);\n    \n    \t\tif (configurationModelIterator.getTotal() > 0) {\n    \t\t\treturn true;\n    \t\t}\n    \n    \t\treturn false;\n    \t}\n    \n    \t@Reference\n    \tprivate Portal _portal;\n    \n    }\n    ```\n\nThis covers some of the available methods. See the [Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/configuration/icon/BasePortletConfigurationIcon.html) \nfor a complete list of the available methods. \n\nGreat! Now you know how to configure your admin app's actions. \n\n## Related Topics\n\n- [Applying Clay Patterns to Your Navigation Bar](/docs/7-2/frameworks/-/knowledge_base/f/applying-clay-patterns-to-navigation)\n- [Configuring Your Application's Title and Back Link](/docs/7-2/frameworks/-/knowledge_base/f/configuring-your-applications-title-and-back-link)\n"
  },
  {
    "path": "en/developer/frameworks/articles/portlets/08-applying-clay-styles-to-your-app/07-empty-results-message/01-setting-empty-results-messages-intro.markdown",
    "content": "---\nheader-id: setting-empty-results-messages\n---\n\n# Setting Empty Results Messages\n\n[TOC levels=1-4]\n\nIf you've toured the UI, you've probably noticed messages or possibly even \nanimations in the search containers when no results are found. \n\n![Figure 1: This is a still frame from the Web Content portlet's empty results animation.](../../../../images/no-web-content-found.png)\n\nYou can configure your apps to use empty results messages and animations too, \nthanks to the `liferay-frontend:empty-results-message` tag. \n\nFollow these steps:\n\n1.  Add the `liferay-frontend` taglib declaration into your portlet's \n    `init.jsp`:\n\n    ```markup\n    <%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n    ```\n\n2.  Add an [`empty-result-message` tag](@app-ref@/frontend-taglib/latest/taglibdocs/liferay-frontend/empty-result-message.html) \n    to your portlet's view:\n\n    ```markup\n    <liferay-frontend:empty-result-message />\n    ```\n\n3.  Configure the tag's attributes to define your search container's empty \n    results message, with or without an animation or image. The attributes are \n    described in the table below:\n    \n    | Attribute | Description |\n    | --- | --- |\n    | `actionDropdownItems` | Specifies the action or actions to display for the empty results in either a dropdown menu, a link, or a button, depending on the number of available actions. |\n    | `animationType` | The CSS class for the animation. Four values are available by default with these CSS classes: `EmptyResultMessageKeys.AnimationType.EMPTY` (`taglib-empty-state`), `EmptyResultMessageKeys.AnimationType.SEARCH` (`taglib-search-state`), `EmptyResultMessageKeys.AnimationType.SUCCESS` (`taglib-success-state`), and `EmptyResultMessageKeys.AnimationType.NONE`. You can also specify a custom CSS class if you prefer. |\n    | `componentId` | Specifies the ID for the `actionDropdownItems` component (dropdown menu, link, or button)|\n    | `description` | The descriptive text to display beneath the main message. |\n    | `elementType` | The type of element to replace the `x` parameter in the main message's language key `no-x-yet`. |\n\n    An example configuration is shown below:\n\n    ```markup\n    <liferay-frontend:empty-result-message\n        actionDropdownItems=\"<%= (availableSegmentsEntries.size() > 0) ? \n        editAssetListDisplayContext.getAssetListEntryVariationActionDropdownItems() : null %>\"\n        animationType=\"<%= EmptyResultMessageKeys.AnimationType.NONE %>\"\n        componentId='<%= renderResponse.getNamespace() + \"emptyResultMessageComponent\" %>'\n        description='<%= LanguageUtil.get(request, \"no-personalized-variations-were-found\") %>'\n        elementType='<%= LanguageUtil.get(request, \"personalized-variations\") %>'\n    />\n    ```\n\n    | **Note:** You can replace the available default animations with your own \n    | by overriding the `taglib-empty-state`, `taglib-search-state`, and \n    | `taglib-success-state` CSS classes provided by \n    | [_empty_result_message.scss](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-css/frontend-css-web/src/main/resources/META-INF/resources/taglib/_empty_result_message.scss), \n    | or by replacing the existing animations in the `@theme_image_path@/states/` \n    | folder. Alternatively, you can also provide a new CSS class that defines \n    | the animation and use it for the `animationType` attribute's value. \n\nempty_state.gif:\n\n![Figure 2: If you can use the add button to add entities to the app, use the empty state animation.](../../../../images/empty_state.gif)\n\nsearch_state.gif:\n\n![Figure 3: If you can use the add button to add entities to the app, use the search state animation.](../../../../images/search_state.gif)\n\nsuccess_state.gif:\n\n![Figure 4: If you can use the add button to add entities to the app, use the success state animation.](../../../../images/success_state.gif)\n\n| **Note:** Empty results messages can also contain static images if you prefer. \n| Just use a valid image type instead. All animations must be of type `GIF` \n| though. \n\nGreat! Now you know how to configure your app to display an empty results \nmessage. \n\n## Related Topics\n\n- [Using the Liferay Front-End Taglib](/docs/7-2/reference/-/knowledge_base/r/using-liferay-front-end-taglibs-in-your-portlet)\n- [Applying the Add Button Pattern](/docs/7-2/frameworks/-/knowledge_base/f/applying-the-add-button-pattern)\n"
  },
  {
    "path": "en/developer/frameworks/articles/search/01-search-framework-intro.markdown",
    "content": "---\nheader-id: search\n---\n\n# Search\n\n[TOC levels=1-4]\n\n@product@ contains a search engine based on [Elasticsearch](https://www.elastic.co/products/elasticsearch). You can \nextend it, implement search for your own applications, and it's highly\nconfigurable. \n\n## Basic Search Concepts \n\n**Indexing**: During indexing, a document is sent to the search engine. This\ndocument contains fields of various types (string, etc.). The search engine\nprocesses each field and determines whether to store the field or analyze it.\nIndex time analysis can be configured for each field (see Mapping Definitions).\n\nFor fields requiring analysis, the search engine first tokenizes the value to\nobtain individual words or tokens. Then it passes each token through a series of\nanalyzers, which perform different functions. Some remove common words or stop\nwords (e.g., \"the\", \"and\", \"or\") while others perform operations like\nlowercasing all characters.\n\n**Searching**: Searching involves sending a search query and obtaining results\n(a.k.a. hits) from the search engine. The search query might contain both\nqueries and filters (more on this later). Queries and filters specify a\nfield to search within and the value to match against. The search engine\niterates through each field within the nested queries and filters and may\nperform special analysis prior to executing the query (search time analysis).\nSearch time analysis can be configured for each field (see Mapping Definitions).\n\n## Mapping Definitions \n\n_Mappings_ control how a search engine processes a given field. For instance,\nif a field name ends in \"es_ES\", it should be processed as Spanish, removing any\ncommon Spanish words like \"si\".\n\nIn Elasticsearch and Solr, the two supported search engines for @product@,\nmappings are defined in `liferay-type-mappings.json` and `schema.xml`,\nrespectively.\n\nThe Elasticsearch mapping JSON file is in the @product@ \n[source code](https://github.com/liferay/liferay-portal),\nin the `portal-search-elasticsearch6`  module:\n\n    portal-search-elasticsearch6-impl/src/main/resources/META-INF/mappings/liferay-type-mappings.json\n\nThe Solr `schema.xml` is in the `portal-search-solr7` module's source code:\n\n    portal-search-solr7-impl/src/main/resources/META-INF/resources/schema.xml\n\nAccess the Solr 7 module's source code from the `liferay-portal` repository \n[here](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-search-solr7/portal-search-solr7-impl/src/main/resources/META-INF/resources/schema.xml).\n\nYou can customize these mappings to fit your needs. For example, you might want\nto use a special analyzer for a custom inventory number field.\n\n## Liferay Search Infrastructure \n\nSearch engines already provide native APIs, but Liferay wraps the engine with\na search infrastructure that does several things:\n\n1. Index documents with the fields Liferay needs (`entryClassName`,\n   `entryClassPK`, `assetTagNames`, `assetCategories`, `companyId`, `groupId`,\n   staging status, etc.).\n\n2. Apply the right filters to search queries (e.g., for scoping\n   results).\n\n3.  Apply permission checking on the results.\n\n4.  Summarize returned results.\n\nThat's just a taste of Liferay's Search Infrastructure. Continue reading to\nlearn more.\n"
  },
  {
    "path": "en/developer/frameworks/articles/search/02-aggregations/01-aggregations-intro.markdown",
    "content": "---\nheader-id: aggregations\n---\n\n# Aggregations\n\n[TOC levels=1-4]\n\nAggregations take the results of a query and group the data into logical sets.\nAggregations can be composed to provide complex data summaries.\n\n@product-ver@ has a new API that exposes \n[Elasticsearch's native Aggregation functionality](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations.html). \n\nCurrently, these aggregation types are supported:\n\n- [Bucketing aggregations](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-bucket.html) \n    create buckets of documents based on some criterion.  They support\n    sub-aggregations.\n    - Supported bucket aggregations include children aggregations, date\n        histogram aggregations, date range aggregations, diversified sampler\n        aggregations, filter aggregations, filters aggregations, geo distance\n        aggregations, geo hash grid aggregations, global aggregations, histogram\n        aggregations, missing aggregations, nested aggregations, range\n        aggregations, reverse nested aggregations, sample aggregations,\n        significant terms aggregations, significant text aggregations, and terms\n        aggregations.\n- [Metrics aggregations](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-metrics.html) \n    compute metrics over a set of documents.\n    - Supported metrics aggregations include average aggregations, cardinality\n        aggregations, extended stats aggregations, geo bounds aggregations, geo\n        centroid aggregations, max aggregations, min aggregations, percentile\n        ranks aggregations, percentiles aggregations, scripted metric\n        aggregations, stats aggregations, sum aggregations, top hits\n        aggregations, value count aggregations, and weighted average\n        aggregations. \n\n- [Pipeline aggregations](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-pipeline.html) \n    aggregate the output of other aggregations and their associated metrics.\n    - Supported pipeline aggregations include average bucket pipeline\n        aggregations, bucket metrics pipeline aggregations, bucket script\n        pipeline aggregations, bucket selector pipeline aggregations, bucket\n        sort pipeline aggregations, cumulative sum pipeline aggregations,\n        derivative pipeline aggregations, extended stats bucket pipeline\n        aggregations, max bucket pipeline aggregations, min bucket pipeline\n        aggregations, moving function pipeline aggregations, percentiles bucket\n        pipeline aggregations, pipeline aggregations, serial diff pipeline\n        aggregations, stats bucket pipeline aggregations, and sum bucket\n        pipeline aggregations. \n\nAll the supported aggregations are found in the `portal-search-api` module's\n`com.liferay.portal.search.aggregation` package.\n\nIn addition to these aggregations, other aggregation-like features are present\nin the @product@ search API:\n\n**Group By** collects search results (documents) based on a particular field.\nFor example, if you want to group the search results based on the asset\ntype (e.g., web content article, document, blog post, etc.), you can create\na search query that contains\na [com.liferay.portal.kernel.search.GroupBy](https://github.com/liferay/liferay-portal/blob/7.2.x/portal-kernel/src/com/liferay/portal/kernel/search/GroupBy.java) \naggregation with the field `entryClassName`.\n\nYou can specify these attributes for returned groups: \n\n- The maximum number of results in each group\n- Special sorting for the grouped results\n\n**Facets** act like bucket aggregations, holding results that share a certain\ncharacteristic.\n\n## Using Aggregations\n\nThe generalized approach for using aggregations in your own search code is like\nthis:\n\n1.  Instantiate and construct the aggregation object\n2.  Add the aggregation information to the search request\n3.  Process the search response\n\nThese steps are covered in more detail (with examples) \n[here](/docs/7-2/frameworks/-/knowledge_base/f/creating-aggregations-in-low-level-search-calls).\n\n## External References\n\n* <https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations.html>\n* <https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations.html#_structuring_aggregations>\n\n## Search Engine Connector Support\n\n* Elasticsearch 6: Yes\n* Solr 7: No\n\n## New/Related APIs\n\n| API (FQCN) | Provided by Artifact | Notes |\n| -----------|:--------------------:|:--------:|\n| `com.liferay.portal.search.aggregation.*` | com.liferay.portal.search.api | The whole [\"aggregation\" package](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/aggregation) is new as of @product-ver@\n"
  },
  {
    "path": "en/developer/frameworks/articles/search/02-aggregations/02-base-search-aggregations-code.markdown",
    "content": "---\nheader-id: creating-aggregations-in-low-level-search-calls\n---\n\n# Creating Aggregations\n\n[TOC levels=1-4]\n\nEach aggregation has a different purpose and should be processed differently\nonce returned from the search engine, but you add the aggregation\ninformation to the search request in a similar way between all aggregations.\n\n## Instantiate and Construct the Aggregation\n\n1.  Use the `com.liferay.portal.search.aggregation.Aggregations` to instantiate\n    the aggregation you'll construct. For example,\n\n    ```java\n    PercentilesAggregation percentilesAggregation =\n        aggregations.percentiles(\"percentiles\", Field.PRIORITY);\n    ```\n\n    To discover what fields each aggregation must have (e.g., `Sting name,\n    String field` in the case of the above `PercentilesAggregation`), see the\n    [`Aggregations` interface](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/aggregation/Aggregations.java).\n\n2.  Build out the aggregation to get the desired response. This looks\n    different for each aggregation type, but Elasticsearch's documentation on the aggregation\n    type explains it well (such as \n    [Percentiles Aggregations](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-metrics-percentile-aggregation.html))\n    combined with the setters in Liferay's corresponding interface.\n\n    For example, use the `setPercentilesMethod(PercentilesMethod.HDR)` method to\n    use the \n    [High Dynamic Range Histogram](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-metrics-percentile-aggregation.html#_hdr_histogram)\n    for calculating the percentiles.\n\n    ```java\n    percentilesAggregation.setPercentilesMethod(PercentilesMethod.HDR);\n    ```\n\nOnce the aggregation itself is in good shape, feed it to the search query.\n\n## Build the Search Query\n\n1.  Get an instance of `com.liferay.portal.search.searcher.SearchRequestBuilder`\n    from the `SearchRequestBuilderFactory` service:\n\n    ```java\n    SearchRequestBuilder searchRequestBuilder = \n        searchRequestBuilderFactory.builder();\n    ```\n\n2.  Get a `com.liferay.portal.search.searcher.SearchRequest` instance from the\n    builder, then add the aggregation to it and run its `build` method:\n\n    ```java\n    SearchRequest searchRequest =\n        searchRequestBuilder.addAggregation(percentilesAggregation).build();\n    ```\n\n## Execute the Search Query\n\n1.  Perform a search using the `Searcher` service and the `SearchRequest` to get\n    a `com.liferay.portal.search.searcher.SearchResponse`:\n\n    ```java\n    SearchResponse searcher.search(searchRequest);\n    ```\n\n2.  To satisfy the dependencies of the example code here, get a reference to\n    `com.liferay.portal.search.searcher.SearchRequestBuilderFactory` and\n    `com.liferay.portal.search.searcher.Searcher`:\n\n    ```java\n    @Reference\n    protected Searcher searcher;\n\n    @Reference\n    SearchRequestBuilderFactory searchRequestBuilderFactory;\n    ```\n\n## Process the response\n\nWhat you'll do with the `SearchResponse` returned by the `searcher.search` call\ndepends on the type of aggregation and your specific use case. A separate\narticle will be written to demonstrate how to process the response.\n"
  },
  {
    "path": "en/developer/frameworks/articles/search/02-aggregations/03-statistical-aggregations.markdown",
    "content": "---\nheader-id: statistical-aggregations\n---\n\n# Statistical Aggregations\n\n[TOC levels=1-4]\n\nSupport for \n[GroupBy](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/portal-kernel/src/com/liferay/portal/kernel/search/GroupBy.java) \nand \n[Stats](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/portal-kernel/src/com/liferay/portal/kernel/search/Stats.java) \naggregations were introduced in 7.0.\n\nCardinality Aggregations extend @product@'s metrics aggregation capabilities,\nproviding an approximate (i.e., statistical) count of distinct values returned\nby a search query. For example, you could compute a count of distinct values of\nthe _tag_ field. Refer to the \n[Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-metrics-cardinality-aggregation.html) \nfor more details.\n\nWhile this functionality was available in the past directly in the portal kernel\ncode, it's been extracted and re-implemented in \n[`StatsRequest`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/stats/StatsRequest.java) \nand \n[`StatsResponse`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/stats/StatsResponse.java), \nboth introduced in the `com.liferay.portal.search.api` module to avoid modifying\n`portal-kernel`. `StatsRequest` provides the same statistical features that the\nlegacy `com.liferay.portal.kernel.search.Stats` does, and adds the new\ncardinality option.\n\n### `StatsRequest`\n\nThe `StatsRequest` Provides a map of field names and the metric aggregations\nthat are to be computed for each field.\n\n1.  Get a reference to `com.liferay.portal.search.searcher.SearchRequestBuilderFactory`:\n\n    ```java\n    @Reference\n    SearchRequestBuilderFactory searchRequestBuilderFactory;\n    ```\n\n2.  Get an instance of `com.liferay.portal.search.searcher.SearchRequestBuilder`:\n\n    ```java\n    SearchRequestBuilder searchRequestBuilder = searchRequestBuilderFactory.builder();\n    ```\n\n3.  Get a `com.liferay.portal.search.searcher.SearchRequest` instance from the builder:\n\n    ```java\n    SearchRequest searchRequest = searchRequestBuilder.build();\n    ```\n\n4.  Get a reference to `com.liferay.portal.search.stats.StatsRequestBuilderFactory`:\n\n    ```java\n    @Reference\n    StatsRequestBuilderFactory statsRequestBuilderFactory;\n    ```\n\n5.  Get a `com.liferay.portal.search.stats.StatsRequestBuilder` instance and\n    build `com.liferay.portal.search.stats.StatsRequest` with the desired\n    metrics:\n\n    ```java\n    StatsRequestBuilder statsRequestBuilder = \n        statsRequestBuilderFactory.getStatsRequestBuilder();\n    StatsRequest statsRequest = statsRequestBuilder\n        .cardinality(true)\n        .count(true)\n        .field(field)\n        .max(true)\n        .mean(true)\n        .min(true)\n        .missing(true)\n        .sum(true)\n        .sumOfSquares(true)\n        .build();\n    ```\n\n6.  Set `StatsRequest` on the `SearchRequest`:\n\n    ```java\n    searchRequest.statsRequests(statsRequest);\n    ```\n\n7.  Get a reference to `com.liferay.portal.search.searcher.Searcher`:\n\n    ```java\n    @Reference\n    protected Searcher searcher;\n    ```\n\n8.  Perform a search using `Searcher` and `SearchRequest` to get\n    `com.liferay.portal.search.searcher.SearchResponse`:\n\n    ```java\n    SearchResponse searcher.search(searchRequest);\n    ```\n\n[**Click here to see an example from Liferay's codebase**](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-test-util/src/main/java/com/liferay/portal/search/test/util/stats/BaseStatisticsTestCase.java#L128 )\n\n### `StatsResponse`\n\nThe stats response contains the metrics aggregations computed by the search\nengine for a given field.\n\n1.  Get the map containing the metrics aggregations computed by the search engine:\n\n    ```java\n    Map<String, StatsResponse> map = searchResponse.getStatsResponseMap();\n    ```\n\n2.  Get the `StatsResponse` for a given field:\n\n    ```java\n    StatsResponse statsResponse = map.get(field);\n    ```\n\n3.  Get the desired metric, for example _cardinality_:\n\n    ```java\n    statsResponse.getCardinality();\n    ```\n\n[**Click here to see an example from Liferay's codebase**](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-test-util/src/main/java/com/liferay/portal/search/test/util/stats/BaseStatisticsTestCase.java#L128)\n\n### Using the Legacy `Stats` Object\n\nThe legacy `com.liferay.portal.kernel.search.Stats` object continues to be fully\nsupported:\n\n1.  Create a `Stats` instance with the desired metrics:\n\n    ```java\n    Stats stats = new Stats() {\n        {\n            setCount(true);\n            setField(field);\n            setMax(true);\n            setMean(true);\n            setMin(true);\n            setSum(true);\n            setSumOfSquares(true);\n        }\n    };\n    ```\n\n2.  Set `Stats` on the `SearchContext`:\n\n    ```java\n    searchRequestBuilder.withSearchContext(searchContext -> searchContext.addStats(stats));\n    ```\n\n[**Click here to see an example from Liferay's codebase**](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-test-util/src/main/java/com/liferay/portal/search/test/util/stats/BaseStatisticsTestCase.java#L42)\n\n## External References\n\n* https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-metrics.html\n* https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-metrics-cardinality-aggregation.html\n* https://lucene.apache.org/solr/guide/7_5/the-stats-component.html\n\n## Search Engine Connector Support\n\n* Elasticsearch 6: Yes\n* Solr 7: Yes\n\n## New/Related APIs\n\nThese are the relevant APIs for building Statistics Aggregations:\n \nAPI (FQCN) | Provided by Artifact |\n---------: | :------------------: |\n`com.liferay.portal.search.searcher.SearchRequestBuilder#statsRequests(StatsRequest... statsRequests)` | `com.liferay.portal.search.api`\n`com.liferay.portal.search.searcher.SearchResponse#getStatsResponseMap()` | `com.liferay.portal.search.api`\n**`com.liferay.portal.search.stats.StatsRequest`** |\t`com.liferay.portal.search.api`\n`com.liferay.portal.search.stats.StatsRequestBuilder` |\t`com.liferay.portal.search.api`\n`com.liferay.portal.search.stats.StatsRequestBuilderFactory` |\t`com.liferay.portal.search.api`\n**`com.liferay.portal.search.stats.StatsResponse`** |\t`com.liferay.portal.search.api`\n`com.liferay.portal.kernel.search.Stats` | `portal-kernel`\n\n## Deprecated APIs\n\n* SearchSearchRequest#getStats()\n* SearchSearchRequest#setStats(Map<String, Stats> stats)\n"
  },
  {
    "path": "en/developer/frameworks/articles/search/02-indexing-framework/01-intro.markdown",
    "content": "---\nheader-id: model-entity-indexing-framework\n---\n\n# Model Entity Indexing Framework\n\n[TOC levels=1-4]\n\nUnless you're searching for model entities using database queries (not\nrecommended in most cases), each asset in @product@ must be indexed in the\nsearch engine. The indexing code is specific to each asset, as the asset's\ndevelopers know what fields to index and what filters to apply to the search\nquery. This paradigm applies to Liferay's own developers and anyone developing\nmodel entities for use with @product@.\n\nIn past versions of @product@, when your asset required indexing, you would\nimplement a new Indexer by extending\n`com.liferay.portal.kernel.search.BaseIndexer<T>`. @product@ version 7.1\nintroduced a new pattern that relies on \n[composition instead of inheritance](https://stackoverflow.com/questions/2399544/difference-between-inheritance-and-composition).\nThat said, if you want to use the old approach, feel free to extend\n`BaseIndexer`. It's still supported. \n\n## Search and Indexing Overview\n\nStarting with the 7.0 version of @product@, the Search API has become less tied\nto Lucene. Elasticsearch support was added (in addition to Solr), and most of\nthe newer searching and indexing APIs aim to leverage/map Elasticsearch APIs.\nThis means that in many cases (for example the `Query` types) there is\na one-to-one mapping between the Liferay and Elasticsearch APIs.\n\nIn addition to the Elasticsearch-centered APIs, Liferay's Search Infrastructure\nincludes additional APIs serving these purposes: \n\n-   Ensure all indexed documents include some required fields (e.g., \n    `entryClassName`, `entryClassPK`, `assetTagNames`, `assetCategories`,\n    `companyId`, `groupId`, staging status). \n-   Ensure the scope of returned search results is appropriate by applying \n    the right filters to search requests. \n-   Provide permission checking and hit summaries for display in the built-in\n    [search application](/docs/7-2/user/-/knowledge_base/u/search).\n\n## Mapping the Composite Search and Indexing Framework to `Indexer`/`BaseIndexer` Code\n\nIf you're used to the old way of indexing custom entities (extending\n`BaseIndexer`, the abstract implementation of `Indexer`), the table below\nprovides a quick overview about how the methods of the `Indexer` interface were\ndecomposed into several new classes and methods.\n\n|  Indexer/BaseIndexer method | Composite Indexer Equivalent | Example |\n| :-------------------------- | :-------------------------- | :--------------- |\n|  Class Constructor | `SearchRegistrar` | [`BlogsEntrySearchRegistrar`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/BlogsEntrySearchRegistrar.java) |\n|  `setDefaultSelectedFieldNames` | `SearchRegistrar.activate` | [`BlogsEntrySearchRegistrar`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/BlogsEntrySearchRegistrar.java) |\n|  `setDefaultSelectedLocalizedFieldNames` | `SearchRegistrar.activate` | [`BlogsEntrySearchRegistrar`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/BlogsEntrySearchRegistrar.java) |\n|  `setPermissionAware`  | `ModelResourcePermissionRegistrar` | [`DLFileEntryModelResourcePermissionRegistrar`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/document-library/document-library-service/src/main/java/com/liferay/document/library/internal/security/permission/resource/DLFileEntryModelResourcePermissionRegistrar.java) |\n|   `setFilterSearch` | `ModelResourcePermissionRegistrar` | [`DLFileEntryModelResourcePermissionRegistrar`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/document-library/document-library-service/src/main/java/com/liferay/document/library/internal/security/permission/resource/DLFileEntryModelResourcePermissionRegistrar.java) |\n|  `getDocument`/`doGetDocument` | `ModelDocumentContributor` | [`BlogsEntryModelDocumentContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/spi/model/index/contributor/BlogsEntryModelDocumentContributor.java) |\n|  `reindex`/`doReindex` | `ModelIndexerWriterContributor` | [`BlogsEntryModelIndexerWriterContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/spi/model/index/contributor/BlogsEntryModelIndexerWriterContributor.java) |\n|  `addRelatedEntryFields` | `RelatedEntryIndexer` | [`DLFileEntryRelatedEntryIndexer`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/document-library/document-library-service/src/main/java/com/liferay/document/library/internal/search/DLFileEntryRelatedEntryIndexer.java) |\n|  `postProcessContextBooleanFilter`/`PostProcessContextQuery` | `ModelPreFilterContributor` | [`BlogsEntryModelPreFilterContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/spi/model/query/contributor/BlogsEntryModelPreFilterContributor.java) |\n|   `postProcessSearchQuery` | `KeywordQueryContributor` | [`BlogsEntryKeywordQueryContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/spi/model/query/contributor/BlogsEntryKeywordQueryContributor.java) |\n|  `getFullQuery` | `SearchContextContributor` | [`DLFileEntryModelSearchContextContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/document-library/document-library-service/src/main/java/com/liferay/document/library/internal/search/DLFileEntryModelSearchContextContributor.java) |\n|  `isVisible`/`isVisibleRelatedEntry` | `ModelVisibilityContributor` | [`BlogsEntryModelVisibilityContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/spi/model/result/contributor/BlogsEntryModelVisibilityContributor.java) |\n|  `getSummary`/`createSummary`/`doGetSummary` | `ModelSummaryContributor` | [`BlogsEntryModelSummaryContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/internal/search/spi/model/result/contributor/BlogsEntryModelSummaryContributor.java) |\n|  `Indexer.search`/`searchCount` | No change | [`BlogEntriesDisplayContext`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/blogs/blogs-web/src/main/java/com/liferay/blogs/web/internal/display/context/BlogEntriesDisplayContext.java) |\t\n|  `Indexer.delete`/`doDelete` | No change | [`MBMessageLocalServiceImpl.deleteMessage`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/message-boards/message-boards-service/src/main/java/com/liferay/message/boards/service/impl/MBMessageLocalServiceImpl.java#L703) |\n\nIn addition, you can index `ExpandoBridge` attributes. This was previously\naccomplished in `BaseIndexer`'s `getBaseModelDocument`. Now you implement an\n`ExpandoBridgeRetriever`. See \n[`DLFileEntryExpandoBridgeRetriever`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/document-library/document-library-service/src/main/java/com/liferay/document/library/internal/search/DLFileEntryExpandoBridgeRetriever.java)\nfor an example implementation.\n\n## Permissions-Aware Searching and Indexing\n\nIn previous versions of @product@, search was only _permissions\naware_ (indexed with the entity's permissions and searched with those\npermissions intact) if the application developer specified this line in the\n`Indexer` class's constructor:\n\n    setPermissionAware(true);\n\nNow, search is permissions aware by default _if the new permissions approach_,\nas described in\n[these tutorials](/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions), \nis implemented for an application.\n\n## Annotating Service Methods to Trigger Indexing\n\nHaving objects translated into database entities _and_ search engine documents\nmeans that there's a possibility for a state mismatch between the database and\nsearch engine. For example, when a Blogs Entry is added, updated, or removed from\nthe database, corresponding changes must be made in the search engine. To do\nthis, intervention must be made in the service layer. For Service Builder\nentities, this occurs in the `LocalServiceImpl` classes. An annotation\nsimplifies this: `@Indexable`. It takes a `type` property that can have two\nvalues: `REINDEX` or `DELETE`. Commonly, a `deleteEntity` method in the service\nlayer is annotated like this:\n\n```java\n@Indexable(type = IndexableType.DELETE)\n@Override\n@SystemEvent(type = SystemEventConstants.TYPE_DELETE)\npublic BlogsEntry deleteEntry(BlogsEntry entry) throws PortalException {\n    ...\n}\n```\n\nThe `@Indexable` annotation is executed by Liferay's AOP infrastructure, so if\nyou have a method with that annotation, you must call it using a service\ninstance variable injected by your dependency injector, and not using the `this`\nkeyword. Whether using OSGi's Declarative Services (DS) or Spring for dependency\ninjection, there's a protected variable declared in the superclass\n(`*LocalServiceBaseImpl`) that can be used in the `*LocalServiceImpl`, like\nthis.\n\n```java\nblogsEntryLocalService.deleteEntry(entry);\n```\n\nSince you're using the injected service variable, that means you must not call\n\n```java\nthis.deleteEntry(...) \n```\n\nin your `*LocalServiceImpl` methods. The annotation won't be executed and you'll\nbe left with a state mismatch between the search engine document and the\ndatabase column.\n\n## Search and Localization: a Cheat Sheet\n\n[Localization](/docs/7-2/frameworks/-/knowledge_base/f/localization) is\nimportant. Search and localization can play nicely together, if you take some\nprecautions:\n\n- For each field that should be localized (e.g., `content`), index a separate\n  field for each of the site's languages (e.g., `content_en_US`,\n  `content_ja_JP`, `content_es_ES`, ...).\n- Search the localized fields. Whatever you index, that's what you should be\n  querying for.\n- Don't index content in plain (unlocalized) fields if you expect the content to\n  be present in multiple locales.\n- Don't index both the plain and the localized field.\n\nThe indexing and searching articles included in this section demonstrate how to\nhandle localized fields in the search code properly. \n"
  },
  {
    "path": "en/developer/frameworks/articles/search/02-indexing-framework/02-indexing.markdown",
    "content": "---\nheader-id: indexing-model-entities\n---\n\n# Indexing Model Entities\n\n[TOC levels=1-4]\n\nModel entities are searchable when their data fields are indexed by the search\nengine. Search and indexing code relies on Search APIs and SPIs.\n\nThe extension points (i.e., the interfaces to implement) in this section are\nprovided by the `com.liferay.portal.search.spi` bundle. Calls are also made to\nthe `com.liferay.portal.search.api` bundle's methods.\n\nHere are the Gradle dependencies for @product@ 7.2.0 GA1:\n\n```groovy\ndependencies {\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.spi\", version: \"3.2.1\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.api\", version: \"3.7.0\"\n}\n```\n\n| **APIs and SPIs:** SPIs are a special type of API. Generally, code inside a SPI\n| module (e.g., `portal-search-spi`) is used to customize existing behavior, while\n| API modules contain behavior you want to use. Put simply, implement interfaces\n| from an SPI, and consume the code from the API.\n| \n| SPI example:\n| `ModelDocumentContributor` lives in an SPI module because you're supposed\n| to implement it directly, defining your own indexing behavior.\n| \n| API example: `SearchRequest` lives in an API module because its behavior\n| is leveraged inside your code to build a search request.\n\n## Contributing Model Entity Fields to the Index\n\nWrite a `ModelDocumentContributor` class to control how model entity fields are\nindexed in search engine documents.\n\n**Extension Point (SPI):** [`com.liferay.portal.search.spi.model.index.contributor.ModelDocumentContributor<T>`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/index/contributor/ModelDocumentContributor.java)\n\nDeclare the Component's `indexer.class.name` and its service type as a\n`ModelDocumentContributor` class:\n\n```java\n@Component(\n\timmediate = true,\n\tproperty = \"indexer.class.name=com.liferay.foo.model.FooEntry\",\n\tservice = ModelDocumentContributor.class\n)\npublic class FooEntryModelDocumentContributor\n\timplements ModelDocumentContributor<FooEntry> {\n```\n\nImplement the `contribute` method, calling the\n`com.liferay.portal.kernel.Document.add()` method appropriate for the data type\nthe index should use (e.g., `addText` for fields that should be searched using a\n[full text search strategy](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/text.html)).\n\n```java\n@Override\npublic void contribute(Document document, FooEntry fooEntry) {\n\n    document.addDate(Field.DISPLAY_DATE, fooEntry.getDisplayDate());\n    document.addDate(Field.MODIFIED_DATE, fooEntry.getModifiedDate());\n    document.addText(Field.SUBTITLE, fooEntry.getSubtitle());\n```\n\nFor fields that should be\n[localized](/docs/7-2/frameworks/-/knowledge_base/f/localization), index a field for\neach locale in the Site. Many times you'll want to localize the title and\ncontent fields, for example:\n\n```java\nfor (Locale locale :\n        LanguageUtil.getAvailableLocales(fooEntry.getGroupId())) {\n\n    String languageId = LocaleUtil.toLanguageId(locale);\n\n    document.addText(\n        LocalizationUtil.getLocalizedName(Field.CONTENT, languageId),\n        content);\n    document.addText(\n        LocalizationUtil.getLocalizedName(Field.TITLE, languageId),\n        fooEntry.getTitle());\n}\n```\n\nThe above strategy is a good one: loop through the available locales in the\nsite, then use `com.liferay.portal.kernel.util.LocalizationUtil` to add the\nlocalized field name to the document.\n\nThe `contribute` method is called each time the `add` and `update` methods in\nthe entity's service layer are called.\n\n## Configure Re-Indexing and Batch Indexing Behavior\n\n`ModelIndexerWriterContributor` classes configure the re-indexing and batch\nre-indexing behavior for the model entity. This class's `customize` method is\ncalled when a re-index is triggered from the Search administrative application\nfound in Control Panel &rarr; Configuration &rarr; Search, or when a CRUD\noperation is made on the entity, _if_ the `modelIndexed` method is implemented\nin the contributor.\n\n**Extension Point (SPI):** [`com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/index/contributor/ModelIndexerWriterContributor.java)\n\nThe bulk of the work is in the `customize` method. This code uses the\nactionable dynamic query helper method to retrieve all existing Foo entities in\nthe virtual instance (identified by the Company ID). If you're using Service\nBuilder, this query method was generated for you when you built the services.\nEach Foo Entry document is then retrieved and added to a collection.\n\n1.  First set up the component and class declarations:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.foo.model.FooEntry\",\n        service = ModelIndexerWriterContributor.class\n    )\n    public class FooEntryModelIndexerWriterContributor\n        implements ModelIndexerWriterContributor<FooEntry> {\n    ```\n\n2.  Write the `customize` method:\n\n    ```java\n    @Override\n    public void customize(\n        BatchIndexingActionable batchIndexingActionable,\n        ModelIndexerWriterDocumentHelper modelIndexerWriterDocumentHelper) {\n\n        batchIndexingActionable.setAddCriteriaMethod(\n            dynamicQuery -> {\n                Property displayDateProperty = PropertyFactoryUtil.forName(\n                    \"displayDate\");\n\n                dynamicQuery.add(displayDateProperty.lt(new Date()));\n\n                Property statusProperty = PropertyFactoryUtil.forName(\"status\");\n\n                Integer[] statuses = {\n                    WorkflowConstants.STATUS_APPROVED,\n                    WorkflowConstants.STATUS_IN_TRASH\n                };\n\n                dynamicQuery.add(statusProperty.in(statuses));\n            });\n        batchIndexingActionable.setPerformActionMethod(\n            (FooEntry fooEntry) -> {\n                Document document =\n                    modelIndexerWriterDocumentHelper.getDocument(fooEntry);\n\n                batchIndexingActionable.addDocuments(document);\n            });\n    }\n    ```\n\n3.  Override `getBatchIndexingActionable`:\n\n    ```java\n    @Override\n    public BatchIndexingActionable getBatchIndexingActionable() {\n        return _dynamicQueryBatchIndexingActionableFactory.\n            getBatchIndexingActionable(\n                _fooEntryLocalService.getIndexableActionableDynamicQuery());\n    }\n    ```\n\n4.  Override `getcompanyId`, getting the ID from your entity:\n\n    ```java\n\t@Override\n\tpublic long getCompanyId(FooEntry fooEntry) {\n\t\treturn fooEntry.getCompanyId();\n\t}\n    ```\n\n5.  Override `getIndexerWriterMode`:\n\n    ```java\n\t@Override\n\tpublic IndexerWriterMode getIndexerWriterMode(FooEntry fooEntry) {\n\t\tint status = fooEntry.getStatus();\n\n\t\tif ((status == WorkflowConstants.STATUS_APPROVED) ||\n\t\t\t(status == WorkflowConstants.STATUS_IN_TRASH) ||\n\t\t\t(status == WorkflowConstants.STATUS_DRAFT)) {\n\n\t\t\treturn IndexerWriterMode.UPDATE;\n\t\t}\n\n\t\treturn IndexerWriterMode.DELETE;\n\t}\n    ```\n\n    `com.liferay.portal.search.spi.model.index.contributor.helper.IndexerWriterMode`\n    defines the following index-writing options:\n\n    - `IndexerWriterMode.DELETE`: instructs the search framework to delete the\n        given document in the search index without re-creating it\n    - `IndexerWriterMode.PARTIAL_UPDATE`, `IndexerWriterMode.UPDATE`: instructs the\n        search framework to update the given document in the search index.\n    - `IndexerWriterMode.SKIP`: instructs the search framework to not write\n        anything to the search index.\n\n    The default is `IndexerWriterMode.UPDATE`.\n\n6.  Add the services referenced:\n\n    ```java\n\t@Reference\n\tprivate FooEntryLocalService _fooEntryLocalService;\n\n\t@Reference\n\tprivate DynamicQueryBatchIndexingActionableFactory\n\t\t_dynamicQueryBatchIndexingActionableFactory;\n    ```\n\n## Contribute Fields to Every Document\n\n`DocumentContributor` classes (without any `indexer.class.name`  component\nproperty or type parameter) contribute certain fields to every index document,\nregardless of what base entity is being indexed. For example, the\n[`GroupedModelDocumentContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search/src/main/java/com/liferay/portal/search/internal/contributor/document/GroupedModelDocumentContributor.java)\ncontains logic to contribute `GROUP_ID` and `SCOPE_GROUP_ID` fields for all\ndocuments with a backing entity that's also a `GroupedModel`.\n"
  },
  {
    "path": "en/developer/frameworks/articles/search/02-indexing-framework/03-searching.markdown",
    "content": "---\nheader-id: searching-the-index-for-model-entities\n---\n\n# Searching the Index for Model Entities\n\n[TOC levels=1-4]\n\nThe heart of searching for your model entity documents is querying for what you\nindexed. To do this, contribute search terms to the @product@ search query.\n\nThe extension points (i.e., the interfaces to implement) on this page are\nprovided by the `com.liferay.portal.search.spi` bundle.\n\nHere's the Gradle dependency for @product@ 7.2.0 GA1:\n\n```groovy\ndependencies {\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.spi\", version: \"3.2.1\"\n}\n```\n\n## Adding your Model Entity's Terms to the Query\n\n`KeywordQueryContributor` classes contribute clauses to the ongoing search\nquery, to control the way model entities are searched. If you're storing\nlocalized fields in the index (a good idea, as covered in the example code for\nyour `ModelDocumentContributor`), query the localized fields at search time. \n\n**Extension Point (SPI):** [com.liferay.portal.search.spi.model.query.contributor.KeywordQueryContributor](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/query/contributor/KeywordQueryContributor.java)\n\n```java\n@Component(\n\timmediate = true,\n\tproperty = \"indexer.class.name=com.liferay.foo.model.FooEntry\",\n\tservice = KeywordQueryContributor.class\n)\npublic class FooEntryKeywordQueryContributor\n\timplements KeywordQueryContributor {\n\n\t@Override\n\tpublic void contribute(\n\t\tString keywords, BooleanQuery booleanQuery,\n\t\tKeywordQueryContributorHelper keywordQueryContributorHelper) {\n\n\t\tSearchContext searchContext =\n\t\t\tkeywordQueryContributorHelper.getSearchContext();\n\n\t\tqueryHelper.addSearchLocalizedTerm(\n\t\t\tbooleanQuery, searchContext, Field.CONTENT, false);\n\t\tqueryHelper.addSearchTerm(\n\t\t\tbooleanQuery, searchContext, Field.SUBTITLE, false);\n\t\tqueryHelper.addSearchLocalizedTerm(\n\t\t\tbooleanQuery, searchContext, Field.TITLE, false);\n\t}\n\n\t@Reference\n\tprotected QueryHelper queryHelper;\n\n}\n```\n\nThe entity in this example has a title, subtitle, and content field. The\nsubtitle field wasn't stored as a localized field, so it's not searched that\nway, either.\n\n## Contributing Query Clauses to Every Search\n\nIt's a less common need, but to contribute query clauses to every search,\nregardless of what base entity is being searched, implement a\n`KeywordQueryContributor` class registered without an `indexer.class.name`\ncomponent property. For example, see\n[`AlwaysPresentFieldsKeywordQueryContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search/src/main/java/com/liferay/portal/search/internal/contributor/query/AlwaysPresentFieldsKeywordQueryContributor.java).\n\nIt includes a String array that includes the fields that are always searched:\n\n```java\nprivate static final String[] _ALWAYS_PRESENT_FIELDS = {\n    Field.COMMENTS, Field.CONTENT, Field.DESCRIPTION, Field.PROPERTIES,\n    Field.TITLE, Field.URL, Field.USER_NAME\n};\n```\n\n## Pre-Filtering\n\n`*PreFilterContributor` classes control how search results are filtered before\nthey're returned from the search engine. For example, adding the workflow status\nto the query ensures that an entity in the trash isn't returned in the search\nresults. They're constructed each time a query for the model entity is made. \n\n**Extension Pointi (SPI):** [`ModelPreFilterContributor`s](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/query/contributor/ModelPreFilterContributor.java)\n\nTo contribute filter clauses specific to a model entity, use\na `ModelPreFilterContributor`. This one adds a filter for workflow status:\n\n```java\n@Component(\n\timmediate = true,\n\tproperty = \"indexer.class.name=com.liferay.foo.model.FooEntry\",\n\tservice = ModelPreFilterContributor.class\n)\npublic class FooEntryModelPreFilterContributor\n\timplements ModelPreFilterContributor {\n\n\t@Override\n\tpublic void contribute(\n\t\tBooleanFilter booleanFilter, ModelSearchSettings modelSearchSettings,\n\t\tSearchContext searchContext) {\n\n\t\taddWorkflowStatusFilter(\n\t\t\tbooleanFilter, modelSearchSettings, searchContext);\n\t}\n\n\tprotected void addWorkflowStatusFilter(\n\t\tBooleanFilter booleanFilter, ModelSearchSettings modelSearchSettings,\n\t\tSearchContext searchContext) {\n\n\t\tworkflowStatusModelPreFilterContributor.contribute(\n\t\t\tbooleanFilter, modelSearchSettings, searchContext);\n\t}\n\n\t@Reference(target = \"(model.pre.filter.contributor.id=WorkflowStatus)\")\n\tprotected ModelPreFilterContributor workflowStatusModelPreFilterContributor;\n\n}\n```\n\n**Extension Point (SPI):** [`com.liferay.portal.search.spi.model.query.contributor.QueryPreFilterContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/query/contributor/QueryPreFilterContributor.java)\n\nTo contribute Filter clauses to every search, regardless of what base entity is\nbeing searched, implement a `QueryPreFilterContributor`.\n`QueryPreFilterContributor` is constructed only once under the root filter\nduring a search. For example, see \n[`AssetCategoryTitlesKeywordQueryContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search/src/main/java/com/liferay/portal/search/internal/contributor/query/AssetCategoryTitlesKeywordQueryContributor.java).\n\nWhat's the difference between `QueryPreFilterContributor` and\n`ModelPreFilterContributor`? `QueryPreFilterContributor` is constructed only\nonce under the root filter during a search, while `ModelPreFilterContributor`\nis constructed once per model entity and added under each specific entity's\nsub-filter.\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/search/02-indexing-framework/04-results.markdown",
    "content": "---\nheader-id: returning-results\n---\n\n# Returning Results\n\n[TOC levels=1-4]\n\nWhen a model entity's indexed search document is obtained during a search\nrequest, it's converted into a summary of the model entity. You can exert\ncontrol over your model entity's summary.\n\n## Creating a Results Summary\n\n`ModelSummaryContributor` classes get the `Summary` object created for each\nsearch document, so you can manipulate it by adding specific fields or setting\nthe length of the displayed content.\n\n**Extension Pointi (SPI):** [`com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/result/contributor/ModelSummaryContributor.java)\n\n```java\n@Component(\n\timmediate = true,\n\tproperty = \"indexer.class.name=com.liferay.foo.model.FooEntry\",\n\tservice = ModelSummaryContributor.class\n)\npublic class FooEntryModelSummaryContributor\n\timplements ModelSummaryContributor {\n\n\t@Override\n\tpublic Summary getSummary(\n\t\tDocument document, Locale locale, String snippet) {\n\n\t\tString languageId = LocaleUtil.toLanguageId(locale);\n\n\t\treturn _createSummary(\n\t\t\tdocument,\n\t\t\tLocalizationUtil.getLocalizedName(Field.CONTENT, languageId),\n\t\t\tLocalizationUtil.getLocalizedName(Field.TITLE, languageId));\n\t}\n\n\tprivate Summary _createSummary(\n\t\tDocument document, String contentField, String titleField) {\n\n\t\tString prefix = Field.SNIPPET + StringPool.UNDERLINE;\n\n\t\tSummary summary = new Summary(\n\t\t\tdocument.get(prefix + titleField, titleField),\n\t\t\tdocument.get(prefix + contentField, contentField));\n\n\t\tsummary.setMaxContentLength(200);\n\n\t\treturn summary;\n\t}\n\n}\n```\n\n## Controlling the Visibility of Model Entities\n\n[`ModelVisibilityContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/result/contributor/ModelVisibilityContributor.java) \nclasses control the visibility of model entities that can be attached to other\nasset types (for example, File Entries can be attached to Wiki Pages), in the\nsearch context. \n\n**Extension Point (SPI):** [`com.liferay.portal.search.spi.model.result.contributor.ModelVisibilityContributor`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/result/contributor/ModelVisibilityContributor.java)\n\n```java\n@Component(\n\timmediate = true,\n\tproperty = \"indexer.class.name=com.liferay.foo.model.FooEntry\",\n\tservice = ModelVisibilityContributor.class\n)\npublic class FooEntryModelVisibilityContributor\n\timplements ModelVisibilityContributor {\n\n\t@Override\n\tpublic boolean isVisible(long classPK, int status) {\n\t\ttry {\n\t\t\tFooEntry entry = fooEntryLocalService.getEntry(classPK);\n\n\t\t\treturn isVisible(entry.getStatus(), status);\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\tif (_log.isWarnEnabled()) {\n\t\t\t\t_log.warn(\"Unable to check visibility for foo entry \", pe);\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprotected boolean isVisible(int entryStatus, int queryStatus) {\n\t\tif (((queryStatus != WorkflowConstants.STATUS_ANY) &&\n\t\t\t (entryStatus == queryStatus)) ||\n\t\t\t(entryStatus != WorkflowConstants.STATUS_IN_TRASH)) {\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Reference\n\tprotected FooEntryLocalService fooEntryLocalService;\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tFooEntryModelVisibilityContributor.class);\n\n}\n```\n\nOnce you index model entities, add their terms to the @product@ search query,\nand get the summary just right, your model entity is ready to be searched.\n"
  },
  {
    "path": "en/developer/frameworks/articles/search/02-indexing-framework/05-registrar.markdown",
    "content": "---\nheader-id: search-service-registration\n---\n\n# Search Service Registration\n\n[TOC levels=1-4]\n\nThe search framework must know about your entity and how to handle it during a\nsearch request. To register model entities with Liferay's search framework,\n`SearchRegistrar`s use the \n[search framework's registry](https://github.com/liferay/liferay-portal/tree/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/registrar)\nto define certain things about your model entity's\n[`ModelSearchDefinition`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-search/portal-search-spi/src/main/java/com/liferay/portal/search/spi/model/registrar/ModelSearchDefinition.java):\nthe default fields used to retrieve documents from the search engine,\nand the optional search services registered for your entity (for example,\nthe `ModelIndexWriterContributor` for you entity). Registration occurs as soon\nas the Component is activated (during portal startup or deployment of the\nbundle). \n\nA Registrar is required so the container knows about your implementation.\n\n1.  First, declare the class a component and create the class declaration:\n\n    ```java\n    @Component(immediate = true, service = {})\n    public class FooEntrySearchRegistrar {\n    ```\n\n2.  Next write the `activate` method, annotated with the OSGi annotation\n    `@Activate`. On activation of this component, call the\n    `ModelSearchRegistrarHelper.register` method and use the call to build out\n    a `ModelSearchDefinition`:\n\n    ```java\n\t@Activate\n\tprotected void activate(BundleContext bundleContext) {\n\t\t_serviceRegistration = modelSearchRegistrarHelper.register(\n\t\t\tFooEntry.class, bundleContext,\n\t\t\tmodelSearchDefinition -> {\n\t\t\t\tmodelSearchDefinition.setDefaultSelectedFieldNames(\n                    Field.ASSET_TAG_NAMES, Field.COMPANY_ID, \n                    Field.ENTRY_CLASS_NAME, Field.ENTRY_CLASS_PK,\n\t\t\t\t\tField.GROUP_ID, Field.MODIFIED_DATE, Field.SCOPE_GROUP_ID,\n\t\t\t\t\tField.UID);\n\t\t\t\tmodelSearchDefinition.setDefaultSelectedLocalizedFieldNames(\n\t\t\t\t\tField.CONTENT, Field.TITLE);\n\t\t\t\tmodelSearchDefinition.setModelIndexWriteContributor(\n\t\t\t\t\tmodelIndexWriterContributor);\n\t\t\t\tmodelSearchDefinition.setModelSummaryContributor(\n\t\t\t\t\tmodelSummaryContributor);\n\t\t\t\tmodelSearchDefinition.setModelVisibilityContributor(\n\t\t\t\t\tmodelVisibilityContributor);\n\t\t\t});\n\t}\n    ```\n\n    On activation of the Component, a chain of search and indexing classes is\n    registered for the Foo entity. In addition, set the default selected field names\n    used to retrieve results documents from the search engine. Then set the\n    contributors used to build a model search definition.\n\n    In addition to the `ModelSearchDefinition` setter methods used in the above\n    code, there's another to be aware of:\n\n    To select all locales all the time when searching for your model entity,\n    pass `true` to `setSelectAllLocales`:\n\n    ```java\n    modelSearchDefinition.setSelectAllLocales(true);\n    ```\n\n    Technically, there's another setter in `ModelSearchDefinition` that takes a\n    boolean, `setSearchResultPermissionFilterSuppressed`. However, this is\n    intended for internal consumption.\n\n3.  Write a corresponding `deactivate` method:\n\n    ```java\n\t@Deactivate\n\tprotected void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n    ```\n\n4.  Get references to the services needed in the class. For the search services\n    you're providing, specify them by entering the FQCN of your model entity in\n    the reference target's `indexer.class.name` property:\n\n    ```java\n\t@Reference(\n\t\ttarget = \"(indexer.class.name=com.liferay.foo.model.FooEntry)\"\n\t)\n\tprotected ModelIndexerWriterContributor<FooEntry>\n\t\tmodelIndexWriterContributor;\n\n\t@Reference\n\tprotected ModelSearchRegistrarHelper modelSearchRegistrarHelper;\n\n\t@Reference(\n\t\ttarget = \"(indexer.class.name=com.liferay.foo.model.FooEntry)\"\n\t)\n\tprotected ModelSummaryContributor modelSummaryContributor;\n\n\t@Reference(\n\t\ttarget = \"(indexer.class.name=com.liferay.foo.model.FooEntry)\"\n\t)\n\tprotected ModelVisibilityContributor modelVisibilityContributor;\n\n\tprivate ServiceRegistration<?> _serviceRegistration;\n    ```\n\nIt's quite possible you'll want to write this class after first getting all the\nsearch and indexing logic into place. How can you register a\n`ModelIndexerWriterContributor` if you haven't written one yet?\n"
  },
  {
    "path": "en/developer/frameworks/articles/search/03-queries-and-filters/01-queries-and-filters-intro.markdown",
    "content": "---\nheader-id: search-queries-and-filters\n---\n\n# Search Queries and Filters\n\n[TOC levels=1-4]\n\nTo get sensible results from the search engine, you must provide a sensible\nquery. \n\n## Queries and Filters in Liferay's Search API\n\nElasticsearch and Solr do not make API level distinctions between queries and\nfilters. In the past, Liferay's API explicitly provided two sets of APIs, one\nfor queries and one for filters. Both APIs lived in `portal-kernel` (the 7.1\nsource code for filters is \n[here](https://github.com/liferay/liferay-portal/tree/7.1.x/portal-kernel/src/com/liferay/portal/kernel/search/filter)).\n\nIn @product-ver@, there's a new way of querying and filtering via Liferay's\nSearch API, and the APIs for it live in the `portal-search-api` module. Instead\nof calling specific filter APIs, you'll now construct a query and add it to the\nsearch request, specifying it as a filter using the\n`SearchRequestBuilder.postFilterQuery(Query)` method. See the\n[7.2 Query APIs](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/query).\n\n| **Note**: Support for the legacy\n| `com.liferay.portal.kernel.search.Query.getPreBooleanFilter()` is only present\n| in the new search request builder and assembler implementation to allow for\n| backwards compatibility with the `Indexer` framework's handling of queries.\n| The older approach encourages some practices that are not ideal:\n| \n| - Wrapping a `BooleanQuery` with another `BooleanQuery`. \n| \n| - Some queries shouldn't have filters according to Elasticsearch's API.\n\nDespite the more unified filtering and querying code, you should understand the\nfunctional difference between filtering and querying:\n\n*Filters* ask a yes or no question for every document. A filter might ask _is\nthe status field equal to staging or live?_\n\n*Queries* ask the same yes or no question AND how well a document matches the\nspecified criteria. This is the concept of \n[relevance scoring](https://www.elastic.co/guide/en/elasticsearch/guide/current/scoring-theory.html). \nA query might ask _Does the document's content field field contain the words\n\"Liferay\", \"Content\", or \"Management\", and how relevant is the content of the\ndocument to the searched keywords?_\n\n| **Hint:** Filtering is faster than querying, since the documents matching a\n| filter can be cached. Queries not only match documents but also calculate\n| scores. We recommend using filtering and querying together: filters to reduce\n| the number of matched documents, queries for the final examination.\n\n## Supported Query Types\n\nLiferay's Search API supports the following types of queries:\n\n### Full Text Queries\n\n[Full text\nqueries](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/full-text-queries.html)\nare high-level queries usually used for querying full text fields like the\n`content` field of a Blogs Entry. How terms are matched depends on the query\ntype.\n\n*Supported Full Text Queries*\n\n```java\nCommonTermsQuery\nMatchPhraseQuery\nMatchPhrasePrefixQuery\nMatchQuery\nMultiMatchQuery\nSimpleStringQuery\nStringQuery\n```\n\nHere are some common full text queries:\n\n* Match Query: A full text query, scored by relevance.\n* Multi Match Query: Execute a `MatchQuery` over several fields.\n* String Query: Use Lucene query syntax.\n\n### Term Queries\n\n[Term\nqueries](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/term-level-queries.html)\nlook for exact matching on keyword fields and indexed terms.\n\n```java\nExistsQuery\nFuzzyQuery\nIdsQuery\nPrefixQuery\nRangeQuery    \nRegexpQuery\nTermQuery\nTermsQuery\nTermRangeQuery\nTermsSetQuery\nWildcardQuery\n```\n\nHere are some common term queries:\n\n* Wildcard Query: Wildcard (`*` and `?`) matching on keyword fields and indexed terms\n* Fuzzy Query: Scrambles characters in input before matching\n\n### Compound Queries\n\n[Compound\nqueries](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/compound-queries.html)\nare often used to wrap other queries. They're commonly used to switch from\nquery to filter context.\n\n```java\nBooleanQuery\nBoostingQuery\nConstantScoreQuery\nDisMaxQuery\nFunctionScoreQuery\n```\n\nHere are some common compound queries:\n\n* Boolean Query: Allows a combo of several query types. Individual queries are\n  as clauses with `SHOULD` | `MUST` | `MUST_NOT` | `FILTER`\n* Constant Score Query: Wraps another query, switches it to filter mode, and gives all\n  returned documents a constant relevance score. <!--How does this give a\n    filter context to another query? Doesn't matter whether postFilterQuery is\n    called?-->\n<!-- * DisMaxQuery-->\n\n### Joining Queries\n\nThe concept of a join doesn't work well in a distributed index. [Joining\nqueries](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/joining-queries.html)\nallow similar behavior in the search index, such as using the [`nested`\ndatatype](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/nested.html)\nto index an array of objects that can be queried independently, using the\n`NestedQuery`.\n\n```java\nNestedQuery\n```\n\nNested Query: Query nested objects as if they each had a separate document in\nthe index.\n\n### Geo Queries\n\nIn Elasticsearch, you can index latitude/longitude pairs and geographic shapes.\n[Geo\nqueries](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/geo-queries.html)\nlet you query for these points and shapes.\n\n```java\nGeoBoundingBoxQuery\nGeoDistanceQuery\nGeoDistanceRangeQuery\nGeoPolygonQuery\nGeoShapeQuery\n```\n\nA common Geo Query is the `GeoDistanceQuery`, used to find documents within a\ncertain distance of a geographic point (latitude/longitude). \n\n### Specialized Queries\n\nThese\n[queries](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/specialized-queries.html)\ndon't fit into another group:\n\n```java\nMoreLikeThisQuery\nPercolateQuery\nScriptQuery\n```\n* **More Like This:** Use a document to query for similar documents.\n* **Percolate:** Match individual documents against indexed queries (for alerting to\n  new documents of interest, or automatically categorizing documents).\n* **Script:** Filter based on a script. \n\n### Other Queries\n\n`MatchAllQuery` Matches all documents in the index.\n\nThe proper search query is entirely context- and search engine-specific, so you\nshould read the Query documentation straight from\n[Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl.html) \nor \n[Solr](https://lucene.apache.org/solr/guide/7_1/query-syntax-and-parsing.html)\nto determine which queries are available and what they do.\n\nAll the recommended and supported queries and filters are found in the\n`portal-search-api` module's `com.liferay.portal.search.query` and\n`com.liferay.portal.search.filter` packages.\n\nLegacy queries and filters, which are still supported but moving towards\ndeprecation, are found in the `com.liferay.portal.kernel.search.*` packages\nprovided by `portal-kernel`.\n\n## Using Queries\n\nHere's the generalized approach for querying and filtering search documents in\nyour own search code:\n\n1.  Instantiate and construct the query object.\n2.  Add the query to the search request---the method you use determines whether\n    the context is filtering or querying (or both in the same request).\n3.  Execute the search request.\n4.  Process the search response.\n\nThese steps are covered in more detail (with examples) \n[in the next article](/docs/7-2/frameworks/-/knowledge_base/f/building-search-queries-and-filters).\n\n## Search Queries in Liferay's Code\n\nThe APIs for creating queries are best exemplified in Liferay's own test cases.\nFor example, [BaseTermsQueryTestCase](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-search/portal-search-test-util/src/main/java/com/liferay/portal/search/test/util/query/BaseTermsQueryTestCase.java) constructs a search request containing\na `TermsQuery` using the `Queries` API:\n\n```java\nTermsQuery termsQuery = queries.terms(Field.USER_NAME);\n```\n\nThis code executes the search request with the terms query constructed above\nin a query context.\n\nOther query test cases are also available to reference in the `portal-search`\nmodule's [source code](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/portal-search/portal-search-test-util/src/main/java/com/liferay/portal/search/test/util/query).\n\n## External References\n\n* <https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl.html>\n* <https://lucene.apache.org/solr/guide/7_1/query-syntax-and-parsing.html>\n\n## Search Engine Connector Support\n\n* Elasticsearch 6: Yes\n* Solr 7: No* (Only the \"legacy\" query types from\n  `com.liferay.portal.kernel.search.*` are supported as of @product@ Beta 2.)\n\n## New/Related APIs\n\nPackage | Provided by Artifact | Notes |\n-----------|:--------------------:|:--------:|\n`com.liferay.portal.search.query.*` | com.liferay.portal.search.api | Most of the provided [query types](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/query) are new as of 7.2\n`com.liferay.portal.search.filter.*` | com.liferay.portal.search.api | Some of the provided [filter types](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/filter) are new as of 7.2\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/search/03-queries-and-filters/02-building-search-queries-and-filters.markdown",
    "content": "---\nheader-id: building-search-queries-and-filters\n---\n\n# Building Search Queries and Filters\n\n[TOC levels=1-4]\n\nEach filter and query has a [different\npurpose](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl.html),\nbut the way you'll add the information to the search request is similar between\nall queries and filters.\n\n## Queries\n\nA mostly-complete code snippet for building Queries is provided for your\ncopying and pasting convenience [below](#example).\n\n### Declare Gradle Dependencies\n\nAdd the following to your `build.gradle` file:\n\n```gradle\ndependencies {\n\tcompileOnly group: \"biz.aQute.bnd\", name: \"biz.aQute.bndlib\", version: \"3.5.0\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"release.portal.api\", version: \"7.2.0\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\", version: \"1.3.0\"\n}\n```\n\nWith this you can import all the Search APIs.\n\n### Reference the Search Services\n\nTo satisfy the dependencies of the example code presented here, get references\nto\n\n- `com.liferay.portal.search.searcher.SearchRequestBuilderFactory`\n- `com.liferay.portal.search.searcher.Searcher`\n- `com.liferay.portal.search.query.Queries`\n\n```java\n@Reference\nprotected Queries queries;\n\n@Reference\nprotected Searcher searcher;\n\n@Reference\nprotected SearchRequestBuilderFactory searchRequestBuilderFactory;\n```\n\n### Build the Search Query\n\n1.  Use the `com.liferay.portal.search.query.Queries` interface to instantiate\n    the queries you'll construct. For example,\n\n    ```java\n    TermsQuery termsQuery = queries.terms(\"fieldName\");\n    MatchQuery matchQuery = queries.match(\"fieldName\", \"value\");\n    BooleanQuery booleanQuery = queries.booleanQuery();\n    ```\n\n    To discover what parameters each query must have (e.g., `String field` in the\n    case of the above `com.liferay.portal.search.query.TermsQuery`), see the\n    [`Queries`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-search/portal-search-api/src/main/java/com/liferay/portal/search/query/Queries.java)\n    interface.\n\n2.  Build out the queries to get the desired response. This looks different for\n    each query type, as explained in [Elasticsearch's Query\n    documentation](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl.html).\n\n    ```java\n    termsQuery.addValues(\"value1\", \"value2\");\n    ```\n\n3.  You might want to wrap queries. For example, use the queries constructed\n    above as MUST clauses in a `BooleanQuery` wrapper:\n\n    ```java\n    booleanQuery.addMustQueryClauses(termsQuery, matchQuery);\n    ```\n\nOnce the query itself is in good shape, feed it to the search request.\n\n### Build the Search Request\n\n1.  Use `com.liferay.portal.search.searcher.SearchRequestBuilderFactory`\n    to get an instance of\n    `com.liferay.portal.search.searcher.SearchRequestBuilder`:\n\n    ```java\n    SearchRequestBuilder searchRequestBuilder =\n        searchRequestBuilderFactory.builder();\n    ```\n\n    If not setting search keywords into the `SearchContext` (covered below), make\n    sure the request builder enables empty search. \n\n    ```java\n    searchRequestBuilder.emptySearchEnabled(true);\n    ```\n\n    Set the `long companyId` and, optionally, `String keywords` into the\n    `com.liferay.portal.kernel.search.SearchContext`:\n\n    ```java\n    searchRequestBuilder.withSearchContext(\n        searchContext -> {\n            searchContext.setCompanyId(companyId);\n            searchContext.setKeywords(keywords);\n        });\n    ```\n\n    Setting the Company ID into the `SearchContext` is required to ensure the\n    correct index is searched.\n\n    Setting \"keywords\" on the `SearchContext` is necessary if you want to\n    search via user input. For example, if providing a Search bar in an\n    application's view layer, pass its input into the search context. Liferay's\n    search framework adds the user input keywords and any other data in the\n    `SearchContext` object to its own queries, searching the appropriate fields of\n    each indexed entity, as defined by its\n    [`KeywordQueryContributor`](/docs/7-2/frameworks/-/knowledge_base/f/searching-the-index-for-model-entities#adding-your-model-entitys-terms-to-the-query)\n    or by the `postProcessSearchQuery` method of its `Indexer`.\n\n2.  To execute the query, get a\n    `com.liferay.portal.search.searcher.SearchRequest` instance from the\n    builder by adding the query to it and running its `build` method:\n\n    ```java\n    SearchRequest searchRequest = \n        searchRequestBuilder.query(booleanQuery).build();\n    ```\n\n3.  To use a constructed query in a filter context, call the `postFilterQuery`\n    method while building the request:\n\n    ```java\n    SearchRequest searchRequest = \n        searchRequestBuilder.postFilterQuery(termsQuery).build();\n    ```\n\n\n4.  When constructing a search request, you'll often find it necessary to chain\n    the builder methods together:\n\n    ```java\n    SearchRequest searchRequest = \n        searchRequestBuilder.postFilterQuery(myQuery1).query(myQuery2).build();\n    ```\n\n    Chaining allows you to add filters and queries (and anything else from the\n    builder API) to the same request in one fell swoop.\n\n### Execute the Search Request\n\nPerform a search using the `com.liferay.portal.search.searcher.Searcher`\nservice and the `SearchRequest` to get a\n`com.liferay.portal.search.searcher.SearchResponse`:\n\n```java\nSearchResponse searchResponse = searcher.search(searchRequest);\n```\n\n### Process the Search Response\n\nWhat you'll do with the `SearchResponse` returned by the `searcher.search`\ncall is dependent on the type of query and your specific use case. Much of the\ntime you'll want to loop through the `com.liferay.portal.search.hits.SearchHit`\nand `com.liferay.portal.search.document.Document` objects, so that's what's\nshown here, with a simple message printed in the log for each one.\n\n1.  Get the `SearchHits` from the response:\n\n    ```java\n    SearchHits searchHits = searchResponse.getSearchHits();\n    ```\n\n2.  Get a List of the `SearchHit` objects:\n\n    ```java\n    List<SearchHit> searchHitsList = searchHits.getSearchHits();\n    ```\n\n3.  Loop through the `SearchHit` objects in the List, get the `Document`\n    associated with each one, printing its score and UID to the\n    console:\n\n    ```java\n    searchHitsList.forEach(\n        searchHit -> {\n            float hitScore = searchHit.getScore();\n\n            Document doc = searchHit.getDocument();\n\n            String uid = doc.getString(Field.UID);\n\n            System.out.println(\n                StringBundler.concat(\n                    \"Document \", uid, \" had a score of \", hitScore));\n        });\n    ```\n\n### Search Insights: Request and Response Strings\n\nWhen building a search application, it can be useful to inspect the request\nstring (translated into the search engine's dialect), and subsequently see the\nresponse string returned by the search server.\n\nRetrieve these from the `SearchResponse` as\n\n```java\nsearchResponse.getRequestString();\nsearchResponse.getResponseString();\n``` \n\nThe format depends on your search engine: with Elasticsearch, both are JSON.\n\n| **Note:** The JSON returned as a request string is pruned from several\n| Elasticsearch query defaults for clarity. To see the full request JSON that\n| Elasticsearch processed, adjust the [Elasticsearch server's\n| logging](https://www.elastic.co/guide/en/elasticsearch/reference/6.x/logging.html).\n\nInspecting the request string produced by the code example included\n[here](#example) reveals two main `\"bool\":\"must\"` query clauses in the JSON\nbeing sent to the search engine: \n\n1.  The `BooleanQuery` explicitly declared in the code example.\n\n2.  A (very long) query determined by the logic embedded in the\n    [`SearcherImpl#doSearch`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-search/portal-search/src/main/java/com/liferay/portal/search/internal/searcher/SearcherImpl.java#L137)\n    method.\n\nHow you construct the `SearchRequest` determines how the `Searcher` API\nprocesses it, which in turn influences the request String sent to\nElasticsearch. For example, sending `keywords` into the `SearchContext` object\npassed to the `SearchRequest` ensures that queries for certain fields are\nexecuted on all searchable documents.\n\n### Queries Example\n\nThe code below performs a `MatchQuery` on the `title_en_US` field for the value\nprovided via the `keywords` String. In addition, a `TermsQuery` on the\n`folderId` field is executed to match a value of `0` (root `JournalFolder`s are\nidentified by `JournalFolderConstants.DEFAULT_PARENT_FOLDER_ID`, which\nevaluates to `0`). Both queries are wrapped in a `BooleanQuery` must clause.\n\nBecause this example passes `keywords` to the `SearchContext`,\n`emptySearchEnabled(true)` is not called on the `SearchRequestBuilder`. The\n`keywords` variable is not explicitly declared because this should come from\nuser input. Therefore the example `search` method receives `keywords` as a\nparameter, along with the `companyId`:\n\n```java\npackage com.liferay.docs.search;\n\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.util.LocaleUtil;\nimport com.liferay.portal.search.document.Document;\nimport com.liferay.portal.search.hits.SearchHit;\nimport com.liferay.portal.search.hits.SearchHits;\nimport com.liferay.portal.search.query.BooleanQuery;\nimport com.liferay.portal.search.query.MatchQuery;\nimport com.liferay.portal.search.query.Queries;\nimport com.liferay.portal.search.query.TermsQuery;\nimport com.liferay.portal.search.searcher.SearchRequest;\nimport com.liferay.portal.search.searcher.SearchRequestBuilder;\nimport com.liferay.portal.search.searcher.SearchRequestBuilderFactory;\nimport com.liferay.portal.search.searcher.SearchResponse;\nimport com.liferay.portal.search.searcher.Searcher;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\n@Component(\n\tservice = MySearchComponent.class\n)\npublic class MySearchComponent {\n\n\tpublic List<String> search(long companyId, String keywords)\n\t\tthrows PortalException {\n\n\t\tMatchQuery titleQuery = queries.match(\n\t\t\tField.getLocalizedName(LocaleUtil.US, Field.TITLE), keywords);\n\n\t\tTermsQuery rootFolderQuery = queries.terms(Field.FOLDER_ID);\n\n\t\trootFolderQuery.addValues(String.valueOf(\n\t\t\t\tJournalFolderConstants.DEFAULT_PARENT_FOLDER_ID));\n\n\t\tBooleanQuery booleanQuery = queries.booleanQuery();\n\n\t\tbooleanQuery.addMustQueryClauses(rootFolderQuery, titleQuery);\n\n\t\tSearchRequestBuilder searchRequestBuilder =\n\t\t\tsearchRequestBuilderFactory.builder();\n\n\t\t// Uncomment this line below if you aren't setting \"keywords\"\n\t\t// on the SearchContext\n\t\t//\t\tsearchRequestBuilder.emptySearchEnabled(true);\n\n\t\tsearchRequestBuilder.withSearchContext(\n\t\t\tsearchContext -> {\n\t\t\t\tsearchContext.setCompanyId(companyId);\n\t\t\t\tsearchContext.setKeywords(keywords);\n\t\t\t});\n\n\t\tSearchRequest searchRequest = searchRequestBuilder.query(\n\t\t\tbooleanQuery\n\t\t).build();\n\n\t\tSearchResponse searchResponse = searcher.search(searchRequest);\n\n\t\tSearchHits searchHits = searchResponse.getSearchHits();\n\n\t\tList<SearchHit> searchHitsList = searchHits.getSearchHits();\n\n\t\tList<String> resultsList = new ArrayList<>(searchHitsList.size());\n\n\t\tsearchHitsList.forEach(\n\t\t\tsearchHit -> {\n\t\t\t\tfloat hitScore = searchHit.getScore();\n\n\t\t\t\tDocument doc = searchHit.getDocument();\n\n\t\t\t\tString uid = doc.getString(Field.UID);\n\n\t\t\t\tSystem.out.println(\n\t\t\t\t\tStringBundler.concat(\n\t\t\t\t\t\t\"Document \", uid, \" had a score of \", hitScore));\n\n\t\t\t\tresultsList.add(uid);\n\t\t\t});\n\n\t\tSystem.out.println(StringPool.EIGHT_STARS);\n\n\t\t/*\n\t\t *  // Uncomment to inspect the Request and Response Strings\n         * System.out.println( \"Request String:\\n\" + searchResponse.getRequestString() +\n\t\t * \"\\n\" + StringPool.EIGHT_STARS);\n\t\t * System.out.println( \"Response String:\\n\" +\n\t\t * searchResponse.getResponseString() + \"\\n\" + StringPool.EIGHT_STARS);\n\t\t */\n\n\t\treturn resultsList;\n\t}\n\n\t@Reference\n\tprotected Queries queries;\n\n\t@Reference\n\tprotected Searcher searcher;\n\n\t@Reference\n\tprotected SearchRequestBuilderFactory searchRequestBuilderFactory;\n\n}\n```\n## Filters\n\nFilters as a distinct API-level object in @product@ are going away. It's best to\nmirror the APIs of the search engine, and neither supported search engine makes\nan API level distinction between queries and filters. In recognition of this,\nthere's a new way to perform post-filtering, which is filtering the returned\nsearch documents at the end of the search request (after calculating any\naggregations). Add the filter to the query using the `postFilterQuery` method in\nthe request builder:\n\n```java \nSearchRequestBuilder.postFilterQuery(Query);\n```\n\nAs you can see, this takes a `Query` object, not a `Filter`. Therefore,\nconstruct the `Query` as in the previous section, and specify it as\na post-filter while building the request. All of the legacy `Filter` objects\nfrom `portal-kernel` can now be constructed as queries, per the above\nquery-building documentation.\n\n### Legacy Filters in Legacy Search Calls\n\nConstructing the filters found in `portal-kernel`'s\n`com.liferay.portal.kernel.search.filter` package is demonstrated by this `new`\nterm filter:\n\n```java\nTermFilter termFilter = new TermFilter(\"fieldName\", \"filterValue\");\n```\n\nFilters are added in legacy search calls by going through the `Indexer`\nframework's `postProcessContextBooleanFilter` method, which is invoked while the\nsearch framework is constructing the main search query. See the \n[`UserIndexer`'s `addContextQueryParams` method](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/users-admin/users-admin-impl/src/main/java/com/liferay/users/admin/internal/search/UserIndexer.java), \nwhich is called in the overridden `postProcessContextBooleanFilter` to add the\nfilter logic.\n\n## Discovering Indexed Fields\n\nTo find the fields to use in your Queries, navigate to *Control Panel* &rarr;\n*Configuration* &rarr; *Search* in a running portal. From there, open the Field Mappings tab and\nbrowse the mappings for the `liferay-[companyId]` index. Scroll to the\n[`properties`](https://www.elastic.co/guide/en/elasticsearch/reference/current/properties.html)\nsection of the mapping.\n\nA summary of the text fields that are localized can be found\n[here](/docs/7-2/user/-/knowledge_base/u/searching-for-localized-content).\n"
  },
  {
    "path": "en/developer/frameworks/articles/segmentation-and-personalization/01-intro.markdown",
    "content": "---\nheader-id: segmentation-personalization\n---\n\n# Segmentation and Personalization\n\n[TOC levels=1-4]\n\nSegments are groups of users that are defined by a specific criteria. You can \nuse the metadata from the user or organization profile, context information \nderived from the user's behavior, or some combination of the two to define that \ncriteria. Alternatively, segments can be a static set of manually selected \nmembers. \n\nIn @product-ver@, the creation of user segments and experience personalization\nare now part of the product's core functionality. Up to @product@ 7.1,\nthis functionality was provided through the Audience Targeting application.\nIn addition to the administration features of Segmentation and Personalization,\ndevelopers can integrate and extend it.\n\n## Managing segments\n\nThe API to manage segments is provided by the `com.liferay.segments.api module`.\nThe `SegmentsEntryService` provides the methods to perform permission aware\noperations on segments. You can use the provided tools to assign members to\nsegments and to extend segment criteria.\n\nThe `segmentsEntry` criteria field determines the conditions that a user must\nmeet to be assigned to the segment. A condition represents a combination of\nproperties, operations, target values, and conjunctions. For example, a\ncondition identifying Liferay Engineers for a Segment might look like this:\n\n```\norganization name EQUALS Liferay AND Job Title EQUALS Engineer\n```\n\nIn the Segments UI, the segments criteria is built using the\n[Segments Editor](/docs/7-2/user/-/knowledge_base/u/the-segment-editor). The\navailable properties are grouped by topic (e.g. User, Organization, Session).\nTechnically, they are called a `SegmentsCriteriaContributor`, because they\n*contribute* conditions to the segments criteria. \n\nYou can see a number of common Segment management operations with example \ncode in\n[Segment Management](/docs/7-2/frameworks/-/knowledge_base/f/segment-management).\n\n## Extending Segment Criteria\n\nThe default segment capabilities are robust enough to cover most use cases, \nand many types of third party integration can be performed without developing a \ncode extension. Some cases, like retrieving an external segment or list can be \nbe handled by using the REST API.\n\n<!--TODO: provide link to REST API when available. -->\n\nOn the other hand, if you want to segment users based on a field provided from\nan external source (for example, the number of followers a user has on\nInstagram), you can contribute an indexable \n[custom field](/docs/7-2/user/-/knowledge_base/u/custom-fields) to the User entity and\nquery the value using the Expando API. Your new field is automatically available\nfor its use as a profile-based criteria.\n\n<!--TODO: provide link to Expando API when available. -->\n\nYou must only develop a code extension if you must: \n \n- Add a custom session property. This is done through the\n  `RequestContextContributor`.\n\n- Extend the criteria query with new queries, based either on existing model \n  entities or in custom model entities. This is done through the\n  `SegmentsCriteriaContributor`.\n\n### `RequestContextContributor`\n\nUser and Organization properties are model-based properties. That means that \nthe available criteria for users and organizations are based on the attributes \nfor users and organizations defined by the entities `model`. Criteria for \nmodel-based entities can be extended by creating a Custom Field for the \ncorresponding model. Session properties are context-based properties and can't \nbe extended through custom fields. To allow for user segmentation based on new \ncontext-based properties, like custom HTTP headers or attributes, you must\ndevelop an extension and deploy it in your @product@ instance.\n\nThe default fields available for context-based segmentation can be found in the\nContext interface. Liferay generates a context instance with real-time\ninformation for every request. These are mostly obtained from the\n`HttpServletRequest`. The `RequestContextContributor` interface provides an\nextension point for adding a new context-based property to the Session panel in\nthe Segments criteria editor and populating the segmentation context with the\nright value for that field.\n\nThe following service properties define a `RequestContextContributor`:\n\n`request.context.contributor.key`: the unique key of the contributed field.\n\n`request.context.contributor.type`: the  contributor field type (boolean, date,\ndouble, integer, or string[default]).\n\nThe `contribute` method of the `RequestContextContributor` adds the custom field\nkey-value pair to the context. \n\n![Figure 1: Learn more about a `RequestContextContributor` by viewing how it's used.](../../images/request-context-contributor.png)\n\nTo create a `RequestContextContributor` through the step by step process, see\n[Creating a Request Context Contributor](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-request-context-contributor).\n\n### `SegmentsCriteriaContributor`\n\nThe `SegmentsCriteriaContributor` interface provides a mechanism \nto extend the segment criteria query. Each `SegmentsCriteriaContributor` \ncontributes a sub-query (or criterion) and the conjunction (AND, OR) to build \nthe complete criteria query that defines the segment. They also provide a list \nof Field elements to be shown in the Segment Editor UI, as depicted in the \nfigure:\n\n![Figure 2: Learn more about a `SegmentsCriteriaContributor` by viewing how it's used.](../../images/segment-field-contributor.png)\n\nThe following service properties describe a `SegmentsCriteriaContributor`:\n\n`segments.criteria.contributor.key`: the unique key that identifies the \ncontributor.\n\n`segments.criteria.contributor.model.class.name`: the entity type the \ncontributor targets.\n\n`segments.criteria.contributor.priority`: the order in which the fields and\nqueries are contributed.\n\nThe `UserOrganizationSegmentsCriteriaContributor` is a good \nexample of how a `SegmentsCriteriaContributor` works. It contributes new \norganization-related fields (*Organization Properties*) to the segments \ncriteria editor, executes a query on the Organization based model, and finally\ncontributes a subquery to the global user query (AND/OR the user belongs to the\norganizations found in the Organization model query). In summary, you can\nfilter users based on aspects of a different but related entity, such as the\norganization.\n\nTo create a `SegmentsCriteriaContributor` through the step by step process, see\n[Creating Segment Criteria Contributors](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-segment-criteria-contributor).\n"
  },
  {
    "path": "en/developer/frameworks/articles/segmentation-and-personalization/02-segment-management.markdown",
    "content": "---\nheader-id: segment-management\n---\n\n# Segment Management\n\n[TOC levels=1-4]\n\nThere are a broad array of uses for Segments and ways that you can integrate \nthem with your application. You'll learn more about how to manage segments next.\n\n## Defining a Segment\n\nThis snippet defines a segment by retrieving `@Reference` objects from\n`SegmentsCriteriaContributor` and instantiating a new `Criteria` object. It then\nadds user criteria using the `segmentsEntryService`:\n\n- the user's `jobTitle` is `Developer` **AND**\n- the user belongs to an Organization with a name that contains `America`\n\n```java\nprivate void addSegmentWithCriteria() {\n    Criteria criteria = new Criteria();\n\n    _userSegmentsCriteriaContributor.contribute(\n        criteria, \"(jobTitle eq 'Developer')\", Criteria.Conjunction.AND);\n    _organizationCriteriaContributor.contribute(\n        criteria, \"contains(name,'America')\", Criteria.Conjunction.OR);\n\n    segmentsEntryService.addSegmentsEntry(\n        \"segment-key\", nameMap, descriptionMap, true, CriteriaSerializer.serialize(criteria),\n\t\t\t\t    User.class.getName(), serviceContext);\n}\n\n@Reference(target = \"(segments.criteria.contributor.key=organization)\")\nprivate SegmentsCriteriaContributor _organizationSegmentsCriteriaContributor;\n\n@Reference(target = \"(segments.criteria.contributor.key=user)\")\nprivate SegmentsCriteriaContributor _userSegmentsCriteriaContributor;\n```\n\n### Manual Segment Member Assignments\n\nTo define manual user-segment member assignments with the\n`SegmentsEntryRelService`, use a snippet like this:\n\n```java\nsegmentsEntryRelService.addSegmentsEntryRel(\n    segmentsEntryId, _portal.getClassNameId(User.class), userId, serviceContext)\n```\n\nThis assigns a user identified by a `userId` to a segment identified by a\n`segmentsEntryId`:\n\n## Retrieving Segments\n\nSegments and Segment Members can be retrieved programmatically. The code snippet\nbelow retrieves an ordered range of active segments for the `User`, within a\nsite identified by a `groupId`.\n\n```java\nList<SegmentsEntry> segmentsEntries = segmentsEntryService.getSegmentsEntries(groupId, true, User.class.getName(), 0, 10, orderByComparator);\n```\n\n## Retrieving segment members\n\nThe local API to query computed segment-member associations is available in the\n`com.liferay.segments.api module`. The `SegmentsEntryProvider` service provides\nmethods to obtain the entities associated to a segment, and the segments\nassociated to an entity.\n\nThis snippet retrieves a range of primary keys of the entities associated to a\nsegment identified by a `segmentsEntryId`:\n\n```java\nlong[] segmentsEntryClassPKs = segmentsEntryProvider.getSegmentsEntryClassPKs(segmentsEntryId, 0, 10);\n```\n\nTo obtain the total count of entities associated to a segment, use the \n`getSegmentsEntryClassPKsCount` method, as shown in the following snippet:\n\n```java\nint segmentsEntryClassPksCount =\n    segmentsEntryProvider.getSegmentsEntryClassPKsCount(segmentsEntryId);\n```\n\nThe method `getSegmentsEntryIds` obtains the reverse association --- the\nsegments associated to a specific entity. For example, this snippet returns the\nsegments associated to a user identified by a `userId`:\n\n```java\nint segmentsEntryClassPksCount =\n    segmentsEntryProvider.getSegmentsEntryIds(User.class.getName(), userId);\n```     \n\nGreat! You now know how to manage segments!\n"
  },
  {
    "path": "en/developer/frameworks/articles/segmentation-and-personalization/03-request-context-contributor.markdown",
    "content": "---\nheader-id: creating-a-request-context-contributor\n---\n\n# Creating a Request Context Contributor\n\n[TOC levels=1-4]\n\nTo better understand the Request Context Contributor, you'll explore how to\ncreate one. First, you'll create the `SampleRequestContentContributor` class\nfile, which contains the `contribute` method that contributes a new field to\nthe context with a custom attribute. You can view the\n[full project on Github](https://github.com/liferay/liferay-portal/tree/master/modules/apps/segments/segments-context-extension-sample).\n\n1.  [Create a new module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project).\n\n2.  Inside the module, create a package named \n    `com.liferay.segments.context.extension.sample.internal.context.contributor`\n\n3.  Create a Java class named `SampleRequestContentContributor`.\n\n4.  Inside the file, insert the `@Component` declaration:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"request.context.contributor.key=\" + SampleRequestContextContributor.KEY,\n            \"request.context.contributor.type=boolean\"\n        },\n        service = RequestContextContributor.class\n    )\n    ```\n\n4.  Add the class declaration:\n\n    ```java\n    public class SampleRequestContextContributor\n        implements RequestContextContributor {\n\n    }\n    ```\n\n5.  Create the attribute that you're adding. In this case, it's just a static\n    string.\n\n    ```java\n    public static final String KEY = \"sample\";\n    ```\n\n6.  Create the `contribute` method:\n\n    ```java\n    @Override\n    public void contribute(\n        Context context, HttpServletRequest httpServletRequest) {\n\n    \t\tcontext.put(KEY,\n            GetterUtil.getBoolean(httpServletRequest.getAttribute(\"sample.attribute\")));\n    \t}\n    ```\n\nTo customize your field label or add a set of selectable options, you can add \nan optional `SegmentsFieldCustomizer` service associated to your contributed \nfield by its key. Create one now.\n\n1.  Inside the module, create a package named \n    `com.liferay.context.extension.sample.internal.field.customizer`\n\n2.  Create a Java class named `SampleSegmentsFieldCustomizer`.\n\n3.  Inside the file, insert the `@Component` declaration:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"segments.field.customizer.entity.name=Context\",\n            \"segments.field.customizer.key=\" + SampleSegmentsFieldCustomizer.KEY,\n            \"segments.field.customizer.priority:Integer=50\"\n        },\n        service = SegmentsFieldCustomizer.class\n    )\n    ```\n\n4.  Create the class declaration:\n\n    ```java\n    public class SampleSegmentsFieldCustomizer implements SegmentsFieldCustomizer {\n\n    }\n    ```\n\n5.  Create the `KEY` value:\n\n    ```java\n    public static final String KEY = \"sample\";\n    ```\n\n6.  Create the methods to provide a list of fields to be displayed when\n    configuring the criteria.\n\n    ```java\n    @Override\n    public List<String> getFieldNames() {\n        return _fieldNames;\n    }\n\n    @Override\n    public String getKey() {\n        return KEY;\n    }\n\n    @Override\n    public String getLabel(String fieldName, Locale locale) {\n    \t\tResourceBundle resourceBundle = ResourceBundleUtil.getBundle(\n            \"content.Language\", locale, getClass());\n\n    \t\treturn LanguageUtil.get(resourceBundle, \"sample-field-label\");\n    }\n\n    private static final List<String> _fieldNames = ListUtil.fromArray(\n    \t\tnew String[] {\"sample\"});\n    ```\n\nOnce you deploy your extensions, the session section of the segment criteria\neditor includes your new context-based field.\n\n![Figure 1: The sample field appears.](../../images/context-based-field.png)\n\nGreat! You've created a Request Context Contributor!\n"
  },
  {
    "path": "en/developer/frameworks/articles/segmentation-and-personalization/04-segment-criteria-contributors.markdown",
    "content": "---\nheader-id: creating-a-segment-criteria-contributor\n---\n\n# Creating a Segment Criteria Contributor\n\n[TOC levels=1-4]\n\nTo demonstrate the Segment Criteria Contributor, you'll create a contributor\nthat segments users based on the title of Knowledge Base articles they have\nauthored.\n\nThe first step is to make your related entity searchable through OData queries.\nFor this purpose, you must have classes:\n\n- `EntityModel`: represents your associated entity (in this case, the\n  `KBArticle`) with its fields of interest.\n\n- `ODataRetriever`: obtains the `KBArticles` that match a given OData query.\n\nYou can view the\n[full project on Github](https://github.com/epgarcia/liferay-portal/tree/LPS-86249.criteria.extension.sample.2/modules/apps/segments/segments-criteria-extension-sample).\n\nFollow the instructions below to get started.\n\n1.  [Create a module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project).\n\n2.  Create the following packages within the module: \n\n    - `com.liferay.segments.criteria.extension.sample.internal.odata.retreiver`\n    - `com.liferay.segments.criteria.extension.sample.internal.odata.entity`\n    - `com.liferay.segments.criteria.extension.sample.internal.criteria.contributor`\n\nExcellent! You have your module ready. Next, you'll create the entity model.\n\n## Creating the Entity Model\n\nFirst, create the Entity Model for `KBArticle`.\n\n1.  Inside the `...internal.odata.entity` package, create the \n    `KBArticleEntityModel` class which implements `EntityModel`:\n\n    ```java\n    public class KBArticleEntityModel implements EntityModel {\n\n    }\n    ```\n\n2.  Create the key for the entity name:\n\n    ```java\n    public static final String NAME = \"KBArticle\";\n    ```\n\n3.  Create the variable for the entity field map:\n\n    ```java\n    private final Map<String, EntityField> _entityFieldsMap;\n    ```\n\n4.  Create the methods to retrieve the `KBArticleEntity`, entity map, and entity\n    name key:\n\n    ```java\n    public KBArticleEntityModel() {\n        _entityFieldsMap = Stream.of(\n            new StringEntityField(\"title\", locale -> \"titleKeyword\")\n            ).collect(\n                Collectors.toMap(EntityField::getName, Function.identity())\n            );\n    }\n\n    @Override\n    public Map<String, EntityField> getEntityFieldsMap() {\n        return _entityFieldsMap;\n    }\n\n    @Override\n    public String getName() {\n        return NAME;\n    }\n    ```\n\nNext, you'll create the OData Retriever.\n\n## Creating the `ODataRetriever`\n\nNext, create the `ODataRetriever` which gets the data using the relevant filter. \n\n1.  Inside the `...internal.odata.retreiver` package, create \n    `KBArticleODataRetriever.java` which implements `ODataRetriever`:\n\n    ```java\n    public class KBArticleODataRetriever implements ODataRetriever<KBArticle> {\n    \t\n    }\n    ```\n\n2.  Add the `@Component` declaration above the class declaration:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = \"model.class.name=com.liferay.knowledge.base.model.KBArticle\",\n        service = ODataRetriever.class\n    )\n    ```\n\n3.  Create the `@Reference` objects that you need for the Filter Parser,\n    Knowledge Base Article Service, and OData Search Adapter:\n\n    ```java\n    @Reference\n    private FilterParserProvider _filterParserProvider;\n\n     @Reference\n    private KBArticleLocalService _kbArticleLocalService;\n\n    @Reference\n    private ODataSearchAdapter _oDataSearchAdapter;\n    ```\n\n4.  Create and instantiate the `_entityModel` object for the `KBArticle` model:\n\n    ```java\n    private static final EntityModel _entityModel = new KBArticleEntityModel();\n    ```\n\n5.  Create the public methods to retrieve the results and the results count from\n    the OData filter:\n\n    ```java\n    @Override\n    public List<KBArticle> getResults(\n            long companyId, String filterString, Locale locale, int start,  int end)\n        throws PortalException {\n\n        Hits hits = _oDataSearchAdapter.search(\n            companyId, filterString, KBArticle.class.getName(), _entityModel,\n            \t_getFilterParser(), locale, start, end);\n\n        return _getKBArticles(hits);\n    }\n\n    @Override\n    public int getResultsCount(\n            long companyId, String filterString, Locale locale)\n        throws PortalException {\n\n        return _oDataSearchAdapter.searchCount(\n            companyId, filterString, KBArticle.class.getName(), _entityModel,\n                _getFilterParser(), locale);\n    }\n    ```\n\n6.  Create the private methods for instantiating the `FilterParser` and\n    retrieving the Knowledge Base article(s) that meet the criteria:\n\n    ```java\n    private FilterParser _getFilterParser() {\n        return _filterParserProvider.provide(_entityModel);\n    }\n\n    private KBArticle _getKBArticle(Document document) throws PortalException {\n        long resourcePrimKey = GetterUtil.getLong(\n            document.get(Field.ENTRY_CLASS_PK));\n\n        return _kbArticleLocalService.getLatestKBArticle(resourcePrimKey, 0);\n    }\n\n    private List<KBArticle> _getKBArticles(Hits hits) throws PortalException {\n        Document[] documents = hits.getDocs();\n\n        List<KBArticle> kbArticles = new ArrayList<>(documents.length);\n\n        for (Document document : documents) {\n            kbArticles.add(_getKBArticle(document));\n        }\n\n        return kbArticles;\n    }\n    ```\n\nYou're all set to create the Segments Criteria Contributor!\n\n## Creating the `SegmentsCriteriaContributor`\n\nNow create the `SegmentsCriteriaContributor` class that consumes the previous \nclasses to retrieve the articles that match the query generated by the criteria \neditor, and contributes a query to filter users based on the articles they \nauthored.\n\n1.  In the `...internal.criteria.contributor` package, create a\n    `UserKBArticleSegmentCritieriaContributor` class that implements \n    `SegmentsCriteriaContributor`.\n\n    ```java\n    public class UserKBArticleSegmentsCriteriaContributor\n        implements SegmentsCriteriaContributor {\n\n    }\n    ```\n\n2.  Create the `@Component` declaration to set properties and declare the \n    service class.\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"segments.criteria.contributor.key=\" + UserKBArticleSegmentsCriteriaContributor.KEY,\n            \"segments.criteria.contributor.model.class.name=com.liferay.portal.kernel.model.User\",\n            \"segments.criteria.contributor.priority:Integer=70\"\n        },\n        service = SegmentsCriteriaContributor.class\n    )\n    ```\n\n3.  Create the variables to enable logging, retrieve the entity model, and \n    entity key.\n\n    ```java\n    private static final Log _log = LogFactoryUtil.getLog(\n        UserKBArticleSegmentsCriteriaContributor.class);\n\n        private static final EntityModel _entityModel = new KBArticleEntityModel();\n\n        public static final String KEY = \"user-kb-article\";\n    ```\n\n4.  Create the reference variables for the OData retriever and Portal instance.\n\n    ```java\n    @Reference(\n        target = \"(model.class.name=com.liferay.knowledge.base.model.KBArticle)\"\n    )\n\n    private ODataRetriever<KBArticle> _oDataRetriever;\n\n    @Reference\n    private Portal _portal;\n    ```\n\n5.  Create the methods to define the implementation of\n    `SegmentsCriteriaContributor`.\n\n    ```java\n    @Override\n    public void contribute(\n        Criteria criteria, String filterString,\n            Criteria.Conjunction conjunction) {\n\n        criteria.addCriterion(getKey(), getType(), filterString, conjunction);\n\n        long companyId = CompanyThreadLocal.getCompanyId();\n        String newFilterString = null;\n\n        try {\n            StringBundler sb = new StringBundler();\n\n            List<KBArticle> kbArticles = _oDataRetriever.getResults(\n                companyId, filterString, LocaleUtil.getDefault(),\n                QueryUtil.ALL_POS, QueryUtil.ALL_POS);\n\n            for (int i = 0; i < kbArticles.size(); i++) {\n                KBArticle kbArticle = kbArticles.get(i);\n\n                sb.append(\"(userId eq '\");\n                sb.append(kbArticle.getUserId());\n                sb.append(\"')\");\n\n                if (i < (kbArticles.size() - 1)) {\n                    sb.append(\" or \");\n                }\n            }\n\n            newFilterString = sb.toString();\n        }\n        catch (PortalException pe) {\n            _log.error(\n                com.liferay.petra.string.StringBundler.concat(\n                    \"Unable to evaluate criteria \", criteria, \" with filter \",\n                    filterString, \" and conjunction \", conjunction.getValue()),\n                pe);\n        }\n\n        if (Validator.isNull(newFilterString)) {\n            newFilterString = \"(userId eq '0')\";\n        }\n\n        criteria.addFilter(getType(), newFilterString, conjunction);\n    }\n\n    @Override\n    public EntityModel getEntityModel() {\n        return _entityModel;\n    }\n\n    @Override\n    public String getEntityName() {\n        return KBArticleEntityModel.NAME;\n    }\n\n    @Override\n    public List<Field> getFields(PortletRequest portletRequest) {\n        return Collections.singletonList(\n            new Field(\n                \"title\",\n                LanguageUtil.get(_portal.getLocale(portletRequest), \"title\"),\n                \"string\"));\n    }\n\n    @Override\n    public String getKey() {\n        return KEY;\n    }\n\n    @Override\n    public Criteria.Type getType() {\n        return Criteria.Type.MODEL;\n    }\n    ```\n\n6.  [Deploy your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project).\n\nAfter deploying your extension, the segment criteria editor includes a new \nsection containing Knowledge Base properties. Notice that the section's UI, \nthe properties, and their associated input fields and operations have been \nautomatically generated based on the information provided by the extension \nservices. For instance, the Knowledge Base article title supports *equals*, *not \nequals*, *contains*, and *not contains* operations because it was defined as a \n`StringEntityField`.\n\n![Figure 1: The sample field appears.](../../images/segment-new-category.png)\n\nAwesome! You've created a Segment Criteria Contributor!\n"
  },
  {
    "path": "en/developer/frameworks/articles/service-context/01-understanding-service-context-intro.markdown",
    "content": "---\nheader-id: understanding-servicecontext\n---\n\n# ServiceContext\n\n[TOC levels=1-4]\n\nThe `ServiceContext` class holds contextual information for a service. It\naggregates information necessary for features used throughout Liferay's\nportlets, such as permissions, tagging, categorization, and more. This article \ncovers the following `ServiceContext` class topics:\n\n- [Service Context Fields](#service-context-fields)\n- [Creating and Populating a Service Context in Java](#creating-and-populating-a-service-context)\n- [Creating and Populating a Service Context in JavaScript](#creating-and-populating-a-service-context-in-javascript)\n- [Accessing Service Context Data](#accessing-service-context-data)\n\nThe `ServiceContext` fields are first.\n\n## Service Context Fields\n\nThe `ServiceContext` class has many fields. The\n[`ServiceContext` class Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/service/ServiceContext.html)\ndescribes them. \n\nHere's a categorical listing of some commonly used Service Context fields: \n\n- Actions:\n    - `_command`\n    - `_workflowAction`\n- Attributes:\n    - `_attributes`\n    - `_expandoBridgeAttributes`\n- Classification: \n    - `_assetCategoryIds`\n    - `_assetTagNames`\n- Exception\n    - `_failOnPortalException`\n- IDs and Scope:\n    - `_companyId`\n    - `_portletPreferencesIds`\n    - `_plid`\n    - `_scopeGroupId`\n    - `_userId`\n    - `_uuid`\n- Language:\n    - `_languageId`\n- Miscellaneous:\n    - `_headers`\n    - `_signedIn`\n- Permissions:\n    - `_addGroupPermissions`\n    - `_addGuestPermissions`\n    - `_deriveDefaultPermissions`\n    - `_modelPermissions`\n- Request\n    - `_request`\n- Resources:\n    - `_assetEntryVisible`\n    - `_assetLinkEntryIds`\n    - `_assetPriority`\n    - `_createDate`\n    - `_formDate`\n    - `_indexingEnabled`\n    - `_modifiedDate`\n    - `_timeZone`\n- URLs, paths and addresses:\n    - `_currentURL`\n    - `_layoutFullURL`\n    - `_layoutURL`\n    - `_pathMain`\n    - `_pathFriendlyURLPrivateGroup`\n    - `_pathFriendlyURLPrivateUser`\n    - `_pathFriendlyURLPublic`\n    - `_portalURL`\n    - `_remoteAddr`\n    - `_remoteHost`\n    - `_userDisplayURL`\n\nAre you wondering how the `ServiceContext` fields get populated? Good! You'll\nlearn about that next. \n\n## Creating and Populating a Service Context\n\nAlthough all the `ServiceContext` class fields are optional, services that store\ndata with scope must at least specify the scope group ID. Here's an example of\ncreating a `ServiceContext` instance and passing it as a parameter to a Liferay\nservice API: \n\n```java\nServiceContext serviceContext = new ServiceContext();\nserviceContext.setScopeGroupId(myGroupId);\n\n...\n\n_blogsEntryService.addEntry(..., serviceContext);\n```\n\nIf you invoke the service from a servlet, a Struts action, or any other\nfront-end class with access to the `PortletRequest`, use one of the\n`ServiceContextFactory.getInstance(...)` methods. These methods create a\n`ServiceContext` object from the request and automatically populate its fields\nwith all the values specified in the request. The above example looks different\nif you invoke the service from a servlet: \n\n```java\nServiceContext serviceContext =\n    ServiceContextFactory.getInstance(BlogsEntry.class.getName(), portletRequest);\n\n...\n\n_blogsEntryService.addEntry(..., serviceContext);\n```\n\nYou can see an example of populating a `ServiceContext` with information from a\nrequest object in the code of the `ServiceContextFactory.getInstance(...)`\nmethods. The methods demonstrate how to set parameters like *scope group ID*,\n*company ID*, *language ID*, and more. They also demonstrate how to access and\npopulate more complex context parameters like *tags*, *categories*, *asset\nlinks*, *headers*, and the *attributes* parameter. By calling\n`ServiceContextFactory.getInstance(String className, PortletRequest\nportletRequest)`, you can assure that your Expando bridge attributes are set on\nthe `ServiceContext`. Expandos are the back-end implementation of custom fields\nfor entities in Liferay.\n\n## Creating and Populating a Service Context in JavaScript\n\nLiferay's API can be invoked in languages other than Java. Some methods require\nor allow a `ServiceContext` parameter. If you're invoking such a method via\nLiferay's JSON web services, you might want to create and populate\na `ServiceContext` object in JavaScript. Creating a `ServiceContext` object in\nJavaScript is no different from creating any other object in JavaScript.\n\nBefore examining a JSON web service invocation that uses a `ServiceContext`\nobject, it helps to see a simple JSON web service example in JavaScript:\n\n```javascript\nLiferay.Service(\n    '/user/get-user-by-email-address`,\n    {\n        companyId: 20101,\n        emailAddress: 'test@example.com`\n    },\n    function(obj) {\n        console.log(obj);\n    }\n);\n```\n\nIf you run this code, the *test@example.com* user (JSON object) is logged to the\nJavaScript console.\n\nThe `Liferay.Service(...)` function takes three arguments:\n\n1. A string representing the service being invoked\n\n2. A parameters object\n\n3. A callback function\n\nThe callback function takes the result of the service invocation as an argument.\n\nThe Liferay JSON web services page (its URL is\n[localhost:8080/api/jsonws](localhost:8080/api/jsonws) if you're running Liferay\nlocally on port 8080) generates example code for invoking web services. To see\nthe generated code for a particular service, click on the name of the service,\nenter the required parameters, and click *Invoke*. The JSON result of your\nservice invocation appears. There are multiple ways to invoke Liferay's JSON web\nservices: click on *JavaScript Example* to see how to invoke the web service via\nJavaScript, click on *curl Example* to see how to invoke the web service via\ncurl, or click on *URL example* to see how to invoke the web service via a URL.\n\n![Figure 1: When you invoke a service from Liferay's JSON web services page, you can view the result of your service invocation as well as example code for invoking the service via JavaScript, curl, or URL.](../../images/jsonws-simple-example.png) \n\nNext, you'll learn how to access information from a `ServiceContext` object. \n\n## Accessing Service Context Data\n\nIn this section, you'll find code snippets from\n`BlogsEntryLocalServiceImpl.addEntry(..., ServiceContext)`. This code\ndemonstrates how to access information from a `ServiceContext` and provides an\nexample of how the context information can be used. \n\nAs mentioned above, services for entities with scope must get a scope group ID\nfrom the `ServiceContext` object. This is true for the Blogs entry service\nbecause the scope group ID provides the scope of the Blogs entry (the entity\nbeing persisted). For the Blogs entry, the scope group ID is used in the\nfollowing way: \n\n- It's used as the `groupId` for the `BlogsEntry` entity.\n- It's used to generate a unique URL for the blog entry.\n- It's used to set the scope for comments on the blog entry. \n\nHere are the corresponding code snippets:\n\n```java\nlong groupId = serviceContext.getScopeGroupId();\n\n...\n\nentry.setGroupId(groupId);\n\n...\n\nentry.setUrlTitle(getUniqueUrlTitle(entryId, groupId, title));\n\n...\n\n// Message boards\n\nif (PropsValues.BLOGS_ENTRY_COMMENTS_ENABLED) {\n    mbMessageLocalService.addDiscussionMessage(\n        userId, entry.getUserName(), groupId,\n        BlogsEntry.class.getName(), entryId,\n        WorkflowConstants.ACTION_PUBLISH);\n}\n```\n\nCan `ServiceContext` be used to access the UUID of the blog entry? Absolutely!\nCan you use `ServiceContext` to set the time the blog entry was added? You sure\ncan. See here: \n\n```java\nentry.setUuid(serviceContext.getUuid());\n...\nentry.setCreateDate(serviceContext.getCreateDate(now));\n```\n\nCan `ServiceContext` be used in setting permissions on resources? You bet! When\nadding a blog entry, you can add new permissions or apply existing permissions\nfor the entry, like this: \n\n```java\n// Resources\n\nif (serviceContext.isAddGroupPermissions() ||\n    serviceContext.isAddGuestPermissions()) {\n\n    addEntryResources(\n        entry, serviceContext.isAddGroupPermissions(),\n        serviceContext.isAddGuestPermissions());\n}\nelse {\n    addEntryResources(\n        entry, serviceContext.getGroupPermissions(),\n        serviceContext.getGuestPermissions());\n}\n```\n\n`ServiceContext` helps apply categories, tag names, and the link entry IDs to\nasset entries too. Asset links are the back-end term for related assets in\nLiferay.\n\n```java\n// Asset\n\nupdateAsset(\n    userId, entry, serviceContext.getAssetCategoryIds(),\n    serviceContext.getAssetTagNames(),\n    serviceContext.getAssetLinkEntryIds());\n```\n\nDoes `ServiceContext` also play a role in starting a workflow instance for the\nblogs entry? Must you ask? \n\n```java\n// Workflow\n\nif ((trackbacks != null) && (trackbacks.length > 0)) {\n    serviceContext.setAttribute(\"trackbacks\", trackbacks);\n}\nelse {\n    serviceContext.setAttribute(\"trackbacks\", null);\n}\n\n_workflowHandlerRegistry.startWorkflowInstance(\n    user.getCompanyId(), groupId, userId, BlogsEntry.class.getName(),\n    entry.getEntryId(), entry, serviceContext);\n```\n\nThe snippet above also demonstrates the `trackbacks` attribute, a standard\nattribute for the blogs entry service. There may be cases where you need to pass\nin custom attributes to your blogs entry service. Use Expando attributes to\ncarry custom attributes along in your `ServiceContext`. Expando attributes are\nset on the added blogs entry like this: \n\n```java\nentry.setExpandoBridgeAttributes(serviceContext);\n```\n\nYou can see that the `ServiceContext` can be used to transfer lots of useful\ninformation for your services. Understanding how `ServiceContext` is used in\nLiferay helps you determine when and how to use `ServiceContext` in your own\nLiferay application development.\n\n## Related Topics\n\n[Business Logic with Service Builder](/docs/7-2/appdev/-/knowledge_base/a/business-logic-with-service-builder)\n\n[Invoking Local Services](/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services)\n\n[Web Services](/docs/7-2/frameworks/-/knowledge_base/f/web-services)\n"
  },
  {
    "path": "en/developer/frameworks/articles/testing/01-injecting-service-components-into-tests-intro.markdown",
    "content": "---\nheader-id: injecting-service-components-into-tests\n---\n\n# Injecting Service Components into Integration Tests\n\n[TOC levels=1-4]\n\nTest driven development plays a key role in quality assurance. Liferay's tooling \nand integration with standard test frameworks support test driven development \nand help you reach quality milestones. You can use @product@'s `@Inject` \nannotation to inject service components into an integration test, like you use \nthe [`@Reference` annotation to inject service components](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services) \ninto an OSGi component. \n\n| **Note:**\n| [Arquillian](http://arquillian.org/) \n| plus \n| [JUnit](https://junit.org) \n| annotations is one way to develop integration tests. Liferay lets you use \n| whatever testing framework you want. \n\nFollow these steps to inject a service component into a test class:\n\n1.  In your test class, add a rule field of \n    [type `com.liferay.portal.test.rule.LiferayIntegrationTestRule`](@platform-ref@/7.2-latest/javadocs/portal-test-integration/com/liferay/portal/test/rule/LiferayIntegrationTestRule.html).\n    For example, \n\n    ```java\n    @ClassRule\n    @Rule\n    public static final AggregateTestRule aggregateTestRule = \n        new LiferayIntegrationTestRule();\n    ```\n\n2.  Add a field to hold a service component. Making the field static improves \n    efficiency because the container injects static fields once before test \n    runs and nulls them after all tests run. Non-static fields are injected \n    before each test run but stay in memory till all tests finish. \n\n3.  Annotate the field with an `@Inject` annotation. By default, the container \n    injects the field with a service component object matching the field's type. \n\n    `@Inject` uses reflection to inject a field with a service component object \n    matching the field's interface. \n    [Test rule `LiferayIntegrationTestRule`](@platform-ref@/7.2-latest/javadocs/portal-test-integration/com/liferay/portal/test/rule/LiferayIntegrationTestRule.html) \n    provides the annotation. \n\n4.  Optionally add a `filter` string or `type` parameter to further specify the \n    service component object to inject. They can be used separately or together. \n\n    To fill a field with a particular implementation or sub-class object, set \n    the `type` with it. \n    \n    ```java\n    @Inject(type = SubClass.class)\n    ```\n    \n    Replace `SubClass` with the name of the service interface to inject. \n\nAt runtime, the `@Inject` annotation blocks the test until a matching service \ncomponent is available. The block has a timeout and messages are logged \nregarding the test's unavailable dependencies. \n\n| **Important**: If you're publishing the service component you are injecting, the \n| test might never run. If you must publish the service component from the test \n| class, use \n| [Service Trackers](/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker) \n| to access service components. \n\nHere's an example test class that injects a `DDLServiceUpgrade` object into an \n`UpgradeStepRegistrator` interface field:\n\n```java\npublic class Test {\n\n    @ClassRule\n    @Rule\n    public static final AggregateTestRule aggregateTestRule = \n        new LiferayIntegrationTestRule();\n\n    @Test\n    public void testSomething() {\n        // your test code here\n    }\n\n    @Inject(\n        filter = \"(&(objectClass=com.liferay.dynamic.data.lists.internal.upgrade.DDLServiceUpgrade))\"\n    )\n    private static UpgradeStepRegistrator _upgradeStepRegistrator;\n\n}\n```\n\nGreat! Now you can inject service components into your tests. \n\n## Related Topics\n\n- [Service Trackers](/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker)\n"
  },
  {
    "path": "en/developer/frameworks/articles/upgrade-processes/01-upgrade-processes-intro.markdown",
    "content": "---\nheader-id: upgrade-processes\n---\n\n# Upgrade Processes\n\n[TOC levels=1-4]\n\nThe development process doesn't end when you first release your application. \nThrough your own planning, feature requests, and bug reports, developers improve \ntheir applications on a regular basis. Sometimes, those changes result in \nchanges to the data structure and underlying database. When users upgrade, they \nneed a process that transitions them to improved versions of your application. \nFor this, you must create an upgrade process.  \n\nHere's what's involved in creating an upgrade process for your app:\n\n- Specifying the schema version\n- Declaring dependencies\n- Writing upgrade steps\n- Writing the registrator\n- Waiting for upgrade completion\n\nLiferay has an Upgrade framework you can use to make this easier to do. It's a \nfeature-rich framework that makes upgrades safe: the system records the current \nstate of the schema so that if the upgrade fails, the process can revert the \nmodule back to its previous version. [Meaningful schema versioning](/docs/7-2/reference/-/knowledge_base/r/meaningful-schema-versioning) \nis important to clearly communicate the updates to your users. \n\n@product@'s Upgrade framework executes your module's upgrades automatically when \nthe new version starts for the first time. You implement concrete data schema \nchanges in upgrade step classes and then register them with the upgrade \nframework using an *upgrade step* registrator. An upgrade step is a class that \nadapts module data to the module's target database schema. It can execute SQL \ncommands and DDL files to upgrade the data. The Upgrade framework lets you \nencapsulate upgrade logic in multiple upgrade step classes per schema version. \n\nThe Upgrade framework executes the upgrade steps to update the current module \ndata to the latest schema. The registrator's `register` method informs the \nUpgrade framework about each new schema and associated upgrade steps to adapt \ndata to it. Each schema upgrade is represented by a *registration*. A \nregistration is an abstraction for all the changes you need to apply to the \ndatabase from one schema version to the next one. \n\nUpgrade registrations are defined by the following values:\n\n- **Module's bundle symbolic name**\n- **Schema version to upgrade from** (as a `String`)\n- **Schema version to upgrade to** (as a `String`)\n- **List of upgrade steps**\n\nA registration's upgrade step list can consist of as many upgrade steps as \nneeded. How you name and organize upgrade steps is up to you. Liferay's upgrade \nclasses are organized using a package structure similar to this one:\n\n- *some.package.structure*\n    - `upgrade`\n        - `v1_1_0`\n            - `UpgradeFoo.java`   &larr; Upgrade Step\n        - `v2_0_0`\n            - `UpgradeFoo.java`   &larr; Upgrade Step\n            - `UpgradeBar.java`   &larr; Upgrade Step\n        - `MyCustomModuleUpgrade.java`   &larr; Registrator\n\nThe example upgrade structure shown above is for a module that has two database \nschema versions: `1.1.0` and `2.0.0`. They're represented by packages `v1_1_0` \nand `v2_0_0`. Each version package contains upgrade step classes that update the \ndatabase. The example upgrade steps focus on fictitious data elements `Foo` and \n`Bar`. The registrator class (`MyCustomModuleUpgrade`, in this example) is \nresponsible for registering the applicable upgrade steps for each schema \nversion. \n\nHere are some organizational tips:\n\n-   Put all upgrade classes in a sub-package called `upgrade`.\n\n-   Group together similar database updates (ones that operate on a data element\n    or related data elements) in the same upgrade step class.\n\n-   Create upgrade steps in sub-packages named after each data schema version. \n\nThe diagram below illustrates the relationship between the registrator and the \nupgrade steps. \n\n![Figure 1: In a registrator class, the developer specifies a registration for each schema version upgrade. The upgrade steps handle the database updates.](../../images/data-upgrade-module-upgrade-architecture.png)\n\nThis section covers these topics:\n\n- [Creating an upgrade process for your app](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-upgrade-process-for-your-app)\n- [Creating upgrade processes for former service builder plugins](/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes-for-former-service-builder-plugins)\n- [Upgrading data schemas in development](/docs/7-2/frameworks/-/knowledge_base/f/upgrading-data-schemas-in-development)\n"
  },
  {
    "path": "en/developer/frameworks/articles/upgrade-processes/02-creating-an-upgrade-process-for-your-application.markdown",
    "content": "---\nheader-id: creating-an-upgrade-process-for-your-app\n---\n\n# Creating Upgrade Processes for Modules\n\n[TOC levels=1-4]\n\nFollow these steps to create an upgrade process for your module: \n\n1.  Open your module's `bnd.bnd` file, and specify a \n    `Liferay-Require-SchemaVersion` header with the new schema version value. \n    Here's an example schema version header for a module whose new schema is \n    version `1.1`:\n\n    ```properties\n    Liferay-Require-SchemaVersion: 1.1\n    ```\n\n    | **Important**: If no `Liferay-Require-SchemaVersion` header is specified, \n    | @product@ considers the `Bundle-Version` header value to be the database \n    | schema version. \n \n2.  [Add a dependency](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies) \n    on the [`com.liferay.portal.upgrade` module](https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.portal.upgrade/), along with any other \n    modules your upgrade process requires, in your your module's dependency \n    management file (e.g., Maven POM, Gradle build file, or Ivy `ivy.xml` file). \n    An example configuration for a `build.gradle` file is shown below:\n\n    ```groovy\n    compile group: \"com.liferay\", name: \"com.liferay.portal.upgrade.api\", version: \"2.0.3\"\n    ```\n\n3.  Create an `UpgradeProcess` class that extends the \n    [`UpgradeProcess` base class](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/upgrade/UpgradeProcess.html) ( \n    which implements the [`UpgradeStep` interface](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/upgrade/UpgradeStep.html)):\n\n    ```java\n    public class MyUpgradeSchemaClass extends UpgradeProcess {\n      ...\n    }\n    ```\n\n4.  Override the `UpgradeProcess` class's `doUpgrade()` method with instructions \n    for modifying the database. Use the `runSQL` and `runSQLTemplate*` methods \n    (inherited from the \n    [`BaseDBProcess` class](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/dao/db/BaseDBProcess.html)) \n    to execute your SQL commands and SQL DDL, respectively. If you want to \n    create, modify, or drop tables or indexes by executing DDL sentences from an \n    SQL file, make sure to use ANSI SQL only. Doing this assures the commands \n    work on different databases. If you need to use non-ANSI SQL, it's best to \n    write it in the [`UpgradeProcess` class's](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/upgrade/UpgradeProcess.html) \n    `runSQL` or `alter` methods, along with tokens that allow porting the \n    sentences to different databases, as shown in journal-service module's \n    [`UpgradeSchema` upgrade step class](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/journal/journal-service/src/main/java/com/liferay/journal/internal/upgrade/v0_0_4/UpgradeSchema.java) \n    below which uses the `runSQLTemplateString` method to execute ANSI SQL DDL \n    from an SQL file and the `alter` method and [`UpgradeProcess`'s](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/upgrade/UpgradeProcess.html) \n    `UpgradeProcess.AlterColumnName` and `UpgradeProcess.AlterColumnType` inner \n    classes as token classes to modify column names and column types:\n    \n    ```java\n    public class UpgradeSchema extends UpgradeProcess {\n      \n      @Override\n      protected void doUpgrade() throws Exception {\n          String template = StringUtil.read(\n              UpgradeSchema.class.getResourceAsStream(\"dependencies/update.sql\"));\n\n          runSQLTemplateString(template, false, false);\n\n          upgrade(UpgradeMVCCVersion.class);\n\n          alter(\n              JournalArticleTable.class,\n              new AlterColumnName(\n                  \"structureId\", \"DDMStructureKey VARCHAR(75) null\"),\n              new AlterColumnName(\n                  \"templateId\", \"DDMTemplateKey VARCHAR(75) null\"),\n              new AlterColumnType(\"description\", \"TEXT null\"));\n\n          alter(\n              JournalFeedTable.class,\n              new AlterColumnName(\"structureId\", \"DDMStructureKey TEXT null\"),\n              new AlterColumnName(\"templateId\", \"DDMTemplateKey TEXT null\"),\n              new AlterColumnName(\n                  \"rendererTemplateId\", \"DDMRendererTemplateKey TEXT null\"),\n              new AlterColumnType(\"targetPortletId\", \"VARCHAR(200) null\"));\n      }\n    }\n    ```\n\n    Here's a simpler example upgrade step from the \n    `com.liferay.calendar.service` module. It uses the `alter` method to modify \n    a column type in the calendar booking table:\n\n    ```java\n    public class UpgradeCalendarBooking extends UpgradeProcess {\n\n            @Override\n            protected void doUpgrade() throws Exception {\n                    alter(\n                            CalendarBookingTable.class,\n                            new AlterColumnType(\"description\", \"TEXT null\"));\n            }\n\n    }\n    ``` \n\n5.  If your application was modularized from a former traditional Liferay plugin \n    application (application WAR) and it uses Service Builder, \n    [create and register a Bundle Activator](/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes-for-former-service-builder-plugins) \n    to register it in @product@'s `Release_` table. \n\n6.  Create an `UpgradeStepRegistrator` OSGi Component class of service type \n    `UpgradeStepRegistrator.class` that implements the \n    [`UpgradeStepRegistrator` interface](@app-ref@/foundation/latest/javadocs/com/liferay/portal/upgrade/registry/UpgradeStepRegistrator.html):\n\n    ```java\n    package com.liferay.mycustommodule.upgrade;\n\n    import com.liferay.portal.upgrade.registry.UpgradeStepRegistrator;\n\n    import org.osgi.service.component.annotations.Component;\n\n    @Component(immediate = true, service = UpgradeStepRegistrator.class)\n    public class MyCustomModuleUpgrade implements UpgradeStepRegistrator {\n\n\n    }\n    ```\n\n7.  Override the [`register` method](@app-ref@/foundation/latest/javadocs/com/liferay/portal/upgrade/registry/UpgradeStepRegistrator.html#register-com.liferay.portal.upgrade.registry.UpgradeStepRegistrator.Registry-) \n    to implement the module's upgrade registrations---abstractions for the \n    upgrade steps required to update the database from one schema version to the \n    next. For example, the upgrade step registrator class \n    `MyCustomModuleUpgrade` (below) registers three upgrade steps incrementally \n    for each schema version (past and present, `0.0.0` to `2.0.0`, `1.0.0` to \n    `1.1.0`, and `1.1.0` to `2.0.0`). \n\n    The first registration is applied if the module hasn't been installed \n    previously. It contains only one empty upgrade step: [new `DummyUpgradeStep`](https://github.com/liferay/liferay-portal/blob/7.2.x/portal-kernel/src/com/liferay/portal/kernel/upgrade/DummyUpgradeStep.java)(). \n    This registration records the module's latest schema version (i.e., `2.0.0`) \n    in @product@'s `Release_` table. Note that if the same class name is used in \n    multiple packages, you must provide the fully qualified class name for the \n    class, as shown in the second registration (`1.0.0` to `1.1.0`) below for \n    the `UpgradeFoo` class:\n\n    ```java\n    package com.liferay.mycustommodule.upgrade;\n\n    import com.liferay.portal.upgrade.registry.UpgradeStepRegistrator;\n\n    import org.osgi.service.component.annotations.Component;\n\n    @Component(immediate = true, service = UpgradeStepRegistrator.class)\n    public class MyCustomModuleUpgrade implements UpgradeStepRegistrator {\n\n        @Override\n        public void register(Registry registry) {\n            registry.register(\n                \"com.liferay.mycustommodule\", \"0.0.0\", \"2.0.0\",\n                new DummyUpgradeStep());\n\n            registry.register(\n                \"com.liferay.mycustommodule\", \"1.0.0\", \"1.1.0\",\n                new com.liferay.mycustommodule.upgrade.v1_1_0.UpgradeFoo());\n\n            registry.register(\n                \"com.liferay.mycustommodule\", \"1.1.0\", \"2.0.0\",\n                new com.liferay.mycustommodule.upgrade.v2_0_0.UpgradeFoo(),\n                new UpgradeBar());\n        }\n\n    }\n    ```\n\n    | **Important**: Modules that use Service Builder *should not* define a\n    | registration for their initial database schema version, as Service Builder\n    | already records their schema versions to @product@'s `Release_` table. Modules\n    | that don't use Service Builder, however, *should* define a registration for\n    | their initial schema. \n\n8.  If your upgrade step uses an OSGi service, **your upgrade must wait for \n    that service's availability**. Use the `@Reference` annotation to declare \n    any classes that the registrator class depends on. For example, the \n    [`WikiServiceUpgrade` registrator class](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/wiki/wiki-service/src/main/java/com/liferay/wiki/internal/upgrade/WikiServiceUpgrade.java) \n    below references the `SettingsFactory` class for the \n    [`UpgradePortletSettings` upgrade step](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/wiki/wiki-service/src/main/java/com/liferay/wiki/internal/upgrade/v1_0_0/UpgradePortletSettings.java). \n    The `setSettingsFactory` method's `@Reference` annotation declares that the \n    registrator class depends on and must wait for the `SettingsFactory` service \n    to be available in the run time environment:\n    \n    ```java\n    @Component(immediate = true, service = UpgradeStepRegistrator.class)\n    public class WikiServiceUpgrade implements UpgradeStepRegistrator {\n    \n    \t@Override\n    \tpublic void register(Registry registry) {\n    \t\tregistry.register(\"0.0.1\", \"0.0.2\", new UpgradeSchema());\n    \n    \t\tregistry.register(\"0.0.2\", \"0.0.3\", new UpgradeKernelPackage());\n    \n    \t\tregistry.register(\n    \t\t\t\"0.0.3\", \"1.0.0\", new UpgradeCompanyId(),\n    \t\t\tnew UpgradeLastPublishDate(), new UpgradePortletPreferences(),\n    \t\t\tnew UpgradePortletSettings(_settingsFactory), new UpgradeWikiPage(),\n    \t\t\tnew UpgradeWikiPageResource());\n    \n    \t\tregistry.register(\"1.0.0\", \"1.1.0\", new UpgradeWikiNode());\n    \n    \t\tregistry.register(\n    \t\t\t\"1.1.0\", \"1.1.1\",\n    \t\t\tnew UpgradeDiscussionSubscriptionClassName(\n    \t\t\t\t_subscriptionLocalService, WikiPage.class.getName(),\n    \t\t\t\tUpgradeDiscussionSubscriptionClassName.DeletionMode.ADD_NEW));\n    \n    \t\tregistry.register(\n    \t\t\t\"1.1.1\", \"2.0.0\",\n    \t\t\tnew BaseUpgradeSQLServerDatetime(\n    \t\t\t\tnew Class<?>[] {WikiNodeTable.class, WikiPageTable.class}));\n    \t}\n    \n    \t@Reference\n    \tprivate SettingsFactory _settingsFactory;\n    \n    \t@Reference\n    \tprivate SubscriptionLocalService _subscriptionLocalService;\n    \n    }\n    ```\n\n9.  Upgrade the database to the latest database schema version before making its \n    services available. To do this, configure the Bnd header \n    `Liferay-Require-SchemaVersion` to the latest schema version for \n    [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder) \n    services. For all other services, specify an `@Reference` annotation that \n    targets the containing module and its latest schema version.\n\n    Here are the target's required attributes:\n    - `release.bundle.symbolic.name`: module's bundle symbolic name\n    - `release.schema.version`: module's current schema version\n\n    For example, the `com.liferay.comment.page.comments.web` module's \n    [`PageCommentsPortlet` class](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/comment/comment-page-comments-web/src/main/java/com/liferay/comment/page/comments/web/internal/portlet/PageCommentsPortlet.java) \n    upgrades to schema version `2.0.0` by defining the reference below:\n\n    ```java\n    @Reference(\n        target = \"(&(release.bundle.symbolic.name=com.liferay.comment.page.comments.web)(release.schema.version=2.0.0))\"\n    )\n    private Release _release;\n    ```\n\n    Dependencies between OSGi services can reduce the number of service classes \n    in which upgrade reference annotations are needed. For example, there's no \n    need to add an upgrade reference in a dependent service, if the dependency \n    already refers to the upgrade. \n\n    | **Note**: Data verifications using the class `VerifyProcess` are deprecated.\n    | Verifications should be tied to schema versions. Upgrade processes are associated\n    | with schema versions but `VerifyProcess` instances are not.\n \nGreat! Now you know how to create data upgrades for all your modules. \n\n## Related Topics\n\n- [Upgrade Processes for Former Service Builder Plugins](/docs/7-2/frameworks/-/knowledge_base/f/upgrade-processes-for-former-service-builder-plugins)\n- [Upgrading Code to @product-ver@](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver)\n- [Configurable Applications](/docs/7-2/frameworks/-/knowledge_base/f/configurable-applications)\n"
  },
  {
    "path": "en/developer/frameworks/articles/upgrade-processes/03-upgrade-processes-for-former-service-builder-plugins.markdown",
    "content": "---\nheader-id: upgrade-processes-for-former-service-builder-plugins\n---\n\n# Upgrade Processes for Former Service Builder Plugins\n\n[TOC levels=1-4]\n\nIf you modularized a traditional Liferay plugin application that implements \nService Builder services, your new modular application must register itself in \nthe @product@'s `Release_` table. This is required regardless of whether release \nrecords already exist for previous versions of the app. A Bundle Activator is \nthe recommended way to add a release record for the first modular version of \nyour converted application. \n\n**Important**: The steps covered in this article only apply to modular \napplications that use Service Builder and were modularized from traditional \nLiferay plugin applications. They do not apply to you if your application \ndoesn't use Service Builder or has never been a traditional Liferay plugin \napplication (a WAR application). \n\nBundle Activator class code is dense but straightforward. Referring to an \nexample Bundle Activator can be helpful. Here's the Liferay Knowledge Base \napplication's Bundle Activator:\n\n```java\npublic class KnowledgeBaseServiceBundleActivator implements BundleActivator {\n\n\t@Override\n\tpublic void start(BundleContext bundleContext) throws Exception {\n\t\tFilter filter = bundleContext.createFilter(\n\t\t\tStringBundler.concat(\n\t\t\t\t\"(&(objectClass=\", ModuleServiceLifecycle.class.getName(), \")\",\n\t\t\t\tModuleServiceLifecycle.DATABASE_INITIALIZED, \")\"));\n\n\t\t_serviceTracker = new ServiceTracker<Object, Object>(\n\t\t\tbundleContext, filter, null) {\n\n\t\t\t@Override\n\t\t\tpublic Object addingService(\n\t\t\t\tServiceReference<Object> serviceReference) {\n\n\t\t\t\ttry {\n\t\t\t\t\tBaseUpgradeServiceModuleRelease\n\t\t\t\t\t\tupgradeServiceModuleRelease =\n\t\t\t\t\t\t\tnew BaseUpgradeServiceModuleRelease() {\n\n\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\tprotected String getNamespace() {\n\t\t\t\t\t\t\t\t\treturn \"KB\";\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\tprotected String getNewBundleSymbolicName() {\n\t\t\t\t\t\t\t\t\treturn \"com.liferay.knowledge.base.service\";\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\tprotected String getOldBundleSymbolicName() {\n\t\t\t\t\t\t\t\t\treturn \"knowledge-base-portlet\";\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\tupgradeServiceModuleRelease.upgrade();\n\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tcatch (UpgradeException ue) {\n\t\t\t\t\tthrow new RuntimeException(ue);\n\t\t\t\t}\n\t\t\t}\n\n\t\t};\n\n\t\t_serviceTracker.open();\n\t}\n\n\t@Override\n\tpublic void stop(BundleContext bundleContext) {\n\t\t_serviceTracker.close();\n\t}\n\n\tprivate ServiceTracker<Object, Object> _serviceTracker;\n\n}\n```\n\nFollow these steps to create a Bundle Activator:\n\n1.  Create a class that implements the `org.osgi.framework.BundleActivator` \n    interface:\n\n    ```java\n    public class KnowledgeBaseServiceBundleActivator implements BundleActivator {\n      \n    }\n    ```\n\n2.  Add a service tracker field:\n\n    ```java\n    `private ServiceTracker<Object, Object> _serviceTracker;`\n    ```\n\n3.  Override BundleActivator's `stop` method to close the service tracker:\n\n    ```java\n    @Override\n    public void stop(BundleContext bundleContext) throws Exception {\n        _serviceTracker.close();\n    }\n    ```\n\n4.  Override BundleActivator's `start` method to instantiate a service \n    tracker that creates a filter to listen for the app's database \n    initialization event and initializes the service tracker to use that \n    filter. You'll add the service tracker initialization code in the next \n    steps. At the end of the `start` method, open the service tracker. \n\n    ```java\n    @Override\n    public void start(BundleContext bundleContext) throws Exception {\n        Filter filter = bundleContext.createFilter(\n            StringBundler.concat(\n                \"(&(objectClass=\", ModuleServiceLifecycle.class.getName(), \")\",\n                ModuleServiceLifecycle.DATABASE_INITIALIZED, \")\"));\n\n        _serviceTracker = new ServiceTracker<Object, Object>(\n            bundleContext, filter, null) {\n            // See the next step for this code ...\n        };\n\n        _serviceTracker.open();\n    }\n    ```\n\n5.  In the service tracker initialization block `{ // See the next step for this \n    code ... }` from the previous step, add an `addingService` method that \n    instantiates a `BaseUpgradeServiceModuleRelease` for describing your app. \n    The example `BaseUpgradeServiceModuleRelease` instance below describes \n    Liferay's Knowledge Base app:\n\n    ```java\n    @Override\n    public Object addingService(\n        ServiceReference<Object> serviceReference) {\n\n        try {\n            BaseUpgradeServiceModuleRelease\n                    upgradeServiceModuleRelease =\n                new BaseUpgradeServiceModuleRelease() {\n\n                    @Override\n                    protected String getNamespace() {\n                        return \"KB\";\n                    }\n\n                    @Override\n                    protected String getNewBundleSymbolicName() {\n                        return \"com.liferay.knowledge.base.service\";\n                    }\n\n                    @Override\n                    protected String getOldBundleSymbolicName() {\n                        return \"knowledge-base-portlet\";\n                    }\n\n                };\n\n            upgradeServiceModuleRelease.upgrade();\n\n            return null;\n        }\n        catch (UpgradeException ue) {\n            throw new RuntimeException(ue);\n        }\n    }\n    ```\n\n    The `BaseUpgradeServiceModuleRelease` implements the following methods:\n\n    -   `getNamespace`: Returns the namespace value as specified in the former \n        plugin's `service.xml` file. This value is also in the `buildNamespace` \n        field in the plugin's `ServiceComponent` table record. \n    -   `getOldBundleSymbolicName`: Returns the former plugin's name.\n    -   `getNewBundleSymbolicName`: Returns the module's symbolic name. In the \n        module's `bnd.bnd` file, it's the `Bundle-SymbolicName` value. \n    -   `upgrade`: Invokes the app's upgrade processes. \n\n6.  In the module's `bnd.bnd` file, reference the Bundle Activator class you \n    created. Here's the example's Bundle Activator reference:\n\n    ```properties\n    Bundle-Activator: com.liferay.knowledge.base.internal.activator.KnowledgeBaseServiceBundleActivator\n    ```\n\nThe Bundle Activator uses one of the following values to initialize the \n`schemaVersion` field in the application's `Release_` table record:\n\n-   Current `buildNumber`: if there is an existing `Release_` table record for \n    the previous plugin. \n-   `0.0.1`: if there is no existing `Release_` table record. \n\nWonderful! You've set your service module's data upgrade process. \n\n## Related Topics\n\n- [Creating Upgrade Processes for Modules](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-upgrade-process-for-your-app)\n- [Upgrading Code to @product-ver@](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver)\n"
  },
  {
    "path": "en/developer/frameworks/articles/upgrade-processes/04-upgrading-data-schemas-in-development.markdown",
    "content": "---\nheader-id: upgrading-data-schemas-in-development\n---\n\n# Upgrading Data Schemas in Development\n\n[TOC levels=1-4]\n\nAs you develop modules, you might need to iterate through several database \nschema changes. Before you release new module versions with your finalized \nschema changes, you must create a formal \n[data upgrade process](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-upgrade-process-for-your-app). \nUntil then, you can use the Build Auto Upgrade feature to test schema changes on \nthe fly. \n\nFollow these steps to use the Build Auto Upgrade feature to test schema changes \nin development:\n\n1.  Create a `portal-ext.properties` file in your app server's \n    `[Liferay_Home]/` folder if it doesn't already exist.\n    \n2.  [Enable Developer Mode in your app server](/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes) \n    by adding the following line to the properties file:\n    \n    ```properties\n    include-and-override=portal-developer.properties;\n    ```\n    \n    The Build Auto Upgrade feature is a global property \n    `schema.module.build.auto.upgrade` in the file \n    `[Liferay_Home]/portal-developer.properties`, so enabling Developer Mode \n    automatically enables this property as well. \n\n    Alternatively, if you prefer not to enable all the \n    [other properties included in Developer Mode](https://github.com/liferay/liferay-portal/blob/7.2.x/portal-impl/src/portal-developer.properties), \n    you can just add the `schema.module.build.auto.upgrade` property to your \n    `portal-ext.properties` file and set it to `true`:\n    \n    ```properties\n    schema.module.build.auto.upgrade = true;\n    ```\n\nSetting the global property `schema.module.build.auto.upgrade` to `true` applies \nmodule schema changes for redeployed modules whose service build numbers have \nincremented. The `build.number` property in the module's `service.properties` \nfile indicates the service build number. Build Auto Upgrade executes schema \nchanges without massaging existing data. It leaves data empty for created \ncolumns, drops data from deleted and renamed columns, and orphans data from \ndeleted and renamed tables. \n\nAlthough Build Auto Upgrade updates databases quickly and automatically, it \ndoesn't guarantee a proper data upgrade--you implement that via \n[data upgrade processes](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-upgrade-process-for-your-app). \nBuild Auto Upgrade is for development purposes only. \n\n| **WARNING**: DO NOT USE the Build Auto Upgrade feature in production. @product@\n| DOES NOT support Build Auto Upgrade in production. Build Auto Upgrade is for\n| development purposes only. Enabling it in production can result in data loss and\n| improper data upgrade. In production environments, leave the property\n| `schema.module.build.auto.upgrade` in `portal-developer.properties` set to\n| `false`.\n\nBy default, `schema.module.build.auto.upgrade` is set to `false`. On any \nmodule's first deployment, the module's tables are generated regardless of the \n`schema.module.build.auto.upgrade` value. \n\nThe table below summarizes Build Auto Upgrade's handling of schema changes:\n\nSchema Change | Result | \n:------------ | :----------- | \nAdd column    | Create a new empty column. |\nRename column | Drop the existing column and delete all its data. Create a new empty column. |\nDelete column | Drop the existing column and delete all its data. |\nCreate or rename a table in @product@'s built-in data source. | Orphan the existing table and all its data. Create the new table. |\n\nGreat! Now you know how to use the Build Auto Upgrade developer feature. \n\n## Related Topics\n\n[Creating Data Upgrade Process for Modules](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-upgrade-process-for-your-app)\n"
  },
  {
    "path": "en/developer/frameworks/articles/user-associated-data/01-managing-uad-intro.markdown",
    "content": "---\nheader-id: managing-user-associated-data-stored-by-custom-applications\n---\n\n# Managing User-Associated Data Stored by Custom Applications\n\n[TOC levels=1-4]\n\nAdministrators can [delete or\nanonymize](/docs/7-2/user/-/knowledge_base/u/managing-user-data) User Associated\nData (UAD) using management tools that aid compliance efforts with the\nEU's General Data Protection Regulation (GDPR). Out of the box, the UAD\nmanagement tool supports @product@ applications (Blogs, Documents and Media,\netc.), and you can also anonymize data stored by your custom applications.\n\nThis task is made easier for \n[Service Builder applications](/docs/7-2/appdev/-/knowledge_base/a/service-builder).\nAt the core of the anonymization effort, you must identify the model entity's\nfields to anonymize. With Service Builder, attach anonymization attributes to\nelements in the `-service` module's `service.xml` file. For the entire DTD for\nService Builder, see\n[here](https://docs.liferay.com/portal/7.2-ga1/definitions/).\nThese two are the most important attributes for the UAD framework:\n\n- The `uad-anonymize-field-name=fieldName` attribute indicates a field whose\n  value is replaced by that of the anonymous user in the UAD deletion process.\n\n- The `uad-nonanonymizable=true` attribute indicates data that cannot be\n  anonymized automatically and must be reviewed by an administrator.\n\nOnce your application uses the UAD framework to manage User data, there are more\nfeatures in @product-ver@ that make searching and deleting User Associated Data\neven easier.\n"
  },
  {
    "path": "en/developer/frameworks/articles/user-associated-data/02-implementing-uad.markdown",
    "content": "---\nheader-id: adding-the-uad-framework-to-a-service-builder-application\n---\n\n# Adding the UAD Framework to a Service Builder Application\n\n[TOC levels=1-4]\n\nYou'll touch two modules of a Service Builder application to implement the UAD\nfeatures: the `-service` module and a brand new module that Service Builder\ngenerates for you, the `-uad` module.\n\n## Update the Service Module\n\nBefore you specify your model entity's fields to manage with the UAD framework,\nmake sure you have the right dependencies.\n\n### Include Dependencies\n\nTo compile the code that Service Builder generates, you need dependencies on\n`com.liferay.petra.String` and `com.liferay.portal kernel`. Make sure your\nservice module's `build.gradle` includes both:\n\n```groovy\ndependencies {\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"4.4.0\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\", version: \"3.0.0\"\n    ...\n}\n```\n\n### Choose the Fields to Anonymize\n\nIn the `service.xml` file, choose the fields to be handled by the framework by\nadding a `uad-anonymize-field-name=\"[fieldName]\"` or `uad-nonanonymizable=true`. \n\nFor example, to replace the `userName` field of your custom entity with the\n`fullName` of the anonymous user,\n\n```xml\n<column name=\"userName\" type=\"String\" uad-anonymize-field-name=\"fullName\" />\n```\n\nThis declaration specifies that the `content` field cannot be auto-anonymized by\nthe framework but should be reviewed manually.\n\n```xml\n<column name=\"content\" type=\"String\" uad-nonanonymizable=\"true\" />\n```\n\nRun Service Builder. A new module is generated alongside your other modules,\ncalled `my-app-uad`. It requires a little massaging.\n\n## Update the UAD Module\n\nFirst, include your dependencies, and then provide your application's name to\nthe user interface. \n\n### Include Dependencies\n\nThe new module is generated without a build script, so you must provide one. It\nshould include dependencies on `osgi.service.component.annotations`,\n`com.liferay.portal.kernel`, `com.liferay.petra.string`, the\n`com.liferay.user.associated.data.api`, and your own application's `-api`\nmodule:\n\n```groovy\ndependencies {\n  compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"4.4.0\"\n  compileOnly group: \"com.liferay\", name: \"com.liferay.user.associated.data.api\", version: \"4.1.1\"\n  compileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\", version: \"3.0.0\"\n  compileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\", version: \"1.3.0\"\n  compileOnly project(\":modules:custom:custom-api\")\n  }\n```\n\n### Provide Your App's Name to the UI\n\nThe simplest way to provide your app's name to the anonymization UI is to\ninclude a language key in your `Language.properties` file:\n\n```properties\napplication.name.[Bundle-SymbolicName]=Custom App\n```\n\nThe bracketed text is the `Bundle-SymbolicName` from your `-uad` module's\n`bnd.bnd` file. \n\nWhile this approach is recommended, it has one downside: multiple language keys\nare used to label a single application. @product@ applications use the\n`com.liferay.lang.merger` plugin to avoid this. Here's the relevant part of the\nBlogs application's `blogs-uad/build.gradle`:\n\n```groovy\napply plugin: \"com.liferay.lang.merger\"\n\ndependencies {\n\n...\n\n}\n\nmergeLang {\n    setting(\"../blogs-web/src/main/resources/content\") {\n        transformKey \"javax.portlet.title.com_liferay_blogs_web_portlet_BlogsPortlet\", \"application.name.com.liferay.blogs.uad\"\n    }\n\n    sourceDirs = [\"../blogs-web/src/main/resources/content\"]\n}\n```\n"
  },
  {
    "path": "en/developer/frameworks/articles/user-associated-data/03-gdpr-deletion-features.markdown",
    "content": "---\nheader-id: enhancing-the-data-erasure-ui\n---\n\n# Enhancing the Data Erasure UI\n\n[TOC levels=1-4]\n\nAs of @product-ver@, there are new features that enhance an administrator's\nexperience finding data in the Personal Data Erasure UI:\n\n**Filtering**\n: User content can now be viewed and acted upon based on\n   whether it is part of the User's personal Site, some other Site, or the\n   overall company.\n\n**Search**\n: A search bar now allows for filtering content based on a search term.\n\n**Hierarchy Display**\n: If there are multiple types of content that are related in a\nhierarchy, you can define that relationship using a `UADHierarchyDeclaration`,\nand the user interface shows that hierarchy (e.g. files and folders).\n\n## Filtering and Searching in the Data Erasure UI\n\nTo support filtering and searching in your custom entities, implement three\nmethods in the `UADDisplay` class (found in your `-uad` module):\n\n- `isSiteScoped`\n- `search`\n- `searchCount`\n\nThe `isSiteScoped` method returns a boolean, determining if the entity can be\nassociated with a particular Site. This is used to determine which filter they\nare associated with (\"instance\", \"personal-site\", or \"regular-sites\").\n\n![Figure 1: Items in the Personal Data Erasure screen can be filtered by scope.](../../images/uad-scope-filter.png) \n\nThe `search` method takes the following parameters:\n\n`userId`: The `userId` of the selected User.\n\n`groupIds`: An array of `groupId`s used to filter which data is shown\nby which groups it is associated with. In the case that no `groupId`s are\ngiven (it can be null), the search method should return data that is not\nscoped to any given group.\n\n`keywords`: The contents of the search bar. The search method should filter by\nwhatever fields are relevant for the given entity.\n\n`orderByField`: The name of the field used to sort the results. This is\none of the names returned by `getSortingFieldNames`.\n\n`orderByType`: Sort the results in ascending order or descending\norder (`asc` or `desc`), for pagination.\n\n`start`: The starting index of the result set. For pagination.\n\n`end`: The ending index of the result set. For pagination.\n\nThe `searchCount` method takes the following parameters, which are treated\nidentically to the ones in `search`:\n\n- `userId`\n\n- `groupIds`\n\n- `keywords`\n\nRead\n[here](/docs/7-2/frameworks/-/knowledge_base/f/filtering-and-searching-uad-marked-entities)\nfor instructions on how to implement search and filtering for your entity. \n\n## Hierarchy Display\n\nHierarchical UAD display optionally shows entities with a natural parent-child\nrelationship (for example, Document Library Folders and Document Library File\nEntries). Viewing these entities in a hierarchy helps administrators make sense\nof the data they're reviewing for possible erasure.\n\n![Figure 2: Hierarchical representation of nested entities is useful for administrators reviewing User data for possible deletion.](../../images/uad-hierarchy.png)\n\nTo implement a hierarchy display, you must do two things:\n\n1.  Implement a\n    [`UADHierarchyDeclaration`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/user-associated-data/user-associated-data-api/src/main/java/com/liferay/user/associated/data/display/UADHierarchyDeclaration.java)\n    class.\n\n2.  Add a method to the `*UADDisplay` class for each type involved in the\n    hierarchy.\n\nOnce implemented, a hierarchy view is displayed for any of the types returned in\nthe `UADHierarchyDeclaration`. For container entities, a count of all child\nentities is calculated and displayed using the hierarchy-related methods in\n`UADDisplay`.\n\n### UAD Hierarchy Declaration\n\nThe `UADHierarchyDeclaration` defines the types in the hierarchical\nrelationship. There are two classifications for a type in a hierarchy: a\n*container*, and a *non-container*. These are defined by\n`getContainerUADDisplays` and `getNoncontainerUADDisplays`.\n\nEach returns an array of one or more `UADDisplay` classes. Containers can be\nparents or children in the hierarchy. An example is a folder in a file system,\nwhich can contain both files and other folders. The non-container entities can\nonly be children in the hierarchy. An example is a file in a file\nsystem.\n\nThe `UADHierarchyDeclaration` provides some other methods for display purposes.\n\n- A label for the hierarchy is provided through the `getEntitiesTypeLabel`\n  method.\n\n- If any additional information is to be displayed in the table for any of the\n  entity types in addition to the count, those column names should be returned\n  by `getExtraColumnNames`. This is optional.\n\nSee [`DLUADHierarchyDeclaration`](https://github.com/liferay/liferay-portal/blob/master/modules/apps/document-library/document-library-uad/src/main/java/com/liferay/document/library/uad/display/DLUADHierarchyDeclaration.java) for an example.\n\n### Add methods to UADDisplay\n\nEach type involved in the hierarchy should implement some additional methods in\n`UADDisplay`.\n\nThese methods must be implemented by containers and non-containers alike, in the\n`*UADDisplay` class:\n\n`getName` returns a display name for the given entity.\n\n`getParentContainerClass` returns the class of the type that contains this type.\nIt can return itself (for example, a folder can contain a folder).\n\n`getParentContainerId` returns the primary key of the container that contains\nthe entity passed to this method.\n\n`isUserOwned` returns whether or not the given entity is owned by the user. \n\nAdditionally, implement `getTopLevelContainer` in the `*UADDisplay` class for all types\nclassified as _containers_. It's used to derive the count of how many user-owned\nentities are contained inside a given container's tree. It answers the question\n\"which type `T` ancestor of `childObject` is an immediate child of the container\nidentified by `parentContainerClass` and `parentContainerId`?\" The method may\nreturn `null` if `childObject` is not a child of the parent container. This\nmethod is the most complicated to implement and requires some consideration for\neach case. Refer to the test case for examples of the requirements used for\n`DLFolder`:\n[DLFolderUADDisplayTest#testGetTopLevelContainer](https://github.com/liferay/liferay-portal/blob/c8f78609353d6a83a0b755b0bbf93764959821ee/modules/apps/document-library/document-library-uad-test/src/testIntegration/java/com/liferay/document/library/uad/display/test/DLFolderUADDisplayTest.java#L67)\n\nSee the actual implementation for `DLFolder` in\n[DLFolderUADDisplay#getTopLevelContainer](https://github.com/liferay/liferay-portal/blob/c8f78609353d6a83a0b755b0bbf93764959821ee/modules/apps/document-library/document-library-uad/src/main/java/com/liferay/document/library/uad/display/DLFolderUADDisplay.java#L105).\n\nThe method returns either `null` or the container object of type T that is the\ntop level container of the `childObject` (which could be any type of object that\nis a part of the hierarchy). This container does not necessarily have to be\nowned by the user, but is understood to contain data related to the user. This\ninformation is used to count how much user data is inside the container\ndesignated by `parentContainerClass` and `parentContainerId`.\n\n```java\n@Override\npublic DLFolder getTopLevelContainer(\n    Class<?> parentContainerClass, Serializable parentContainerId,\n    Object childObject) {\n\n    try {\n        DLFolder childFolder = null;\n\n        if (childObject instanceof DLFileEntry) {\n            DLFileEntry dlFileEntry = (DLFileEntry)childObject;\n\n            childFolder = dlFileEntry.getFolder();\n        }\n        else {\n            childFolder = (DLFolder)childObject;\n        }\n\n        long parentFolderId = (long)parentContainerId;\n\n        if ((childFolder.getFolderId() == parentFolderId) ||\n            ((parentFolderId !=\n                DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) &&\n             !StringUtil.contains(\n                 childFolder.getTreePath(), String.valueOf(parentFolderId),\n                 \"/\"))) {\n\n            return null;\n        }\n\n        if (childFolder.getParentFolderId() == parentFolderId) {\n            return childFolder;\n        }\n\n        List<Long> ancestorFolderIds = childFolder.getAncestorFolderIds();\n\n        if (parentFolderId == DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {\n            return get(ancestorFolderIds.get(ancestorFolderIds.size() - 1));\n        }\n\n        if (ancestorFolderIds.contains(parentFolderId)) {\n            return get(\n                ancestorFolderIds.get(\n                    ancestorFolderIds.indexOf(parentFolderId) - 1));\n        }\n    }\n    catch (PortalException pe) {\n        _log.error(pe, pe);\n    }\n\n    return null;\n}\n```\n\nThe exact implementation details vary for each entity type.\n"
  },
  {
    "path": "en/developer/frameworks/articles/user-associated-data/04-filtering-and-searching.markdown",
    "content": "---\nheader-id: filtering-and-searching-uad-marked-entities\n---\n\n# Filtering and Searching UAD-Marked Entities\n\n[TOC levels=1-4]\n\nIn the data erasure UI, it's important that administrators can find what they're\nlooking for. The native @product@ entities support filtering and search, and\nwhen you follow the steps here, your entities will, too.\n\nTo add filtering and searching for your custom entities, implement three\nmethods in the `UADDisplay` class (in your application's `-uad` module):\n\n## Filtering\n\nThe `isSiteScoped` method returns a boolean denoting if the\nentities can be associated with a particular Site: `false` if not, and\n`true` if the entities are scoped to a Site. This determines which\nfilter they are associated with (\"instance\", \"personal-site\", or\n\"regular-sites\").\n\n```java\n@Override\npublic boolean isSiteScoped() {\n\n    return false;\n}\n```\n\n## Search\n\nImplement the `search` and `searchCount` methods to enable search in the UAD\ninterface:\n\n1.  The `search` method must return a `List` of entities associated with the\n    `userId`. For example, you could search the database for records\n    associated with the `userId`:\n\n    ```java\n    @Override\n    public List<T> search(\n        long userId, long[] groupIds, String keywords, String orderByField,\n        String orderByType, int start, int end) {\n\n        FooService<T> fooService = getFooService();\n\n        return dummyService.getEntities(userId);\n    }\n    ```\n\n    But if you've gone through the trouble of indexing your model entity's\n    fields in a search engine, it's more likely you'll want to do the initial\n    search, querying for documents matching the `userId`, at the search engine\n    level. After the search, retrieve the matching entities from the database.\n\n    ```java\n    @Override\n    public List<T> search(\n        long userId, long[] groupIds, String keywords, String orderByField,\n        String orderByType, int start, int end) {\n\n        SearchContext searchContext = new SearchContext();\n\n        searchContext.setStart(start);\n        searchContext.setEnd(end);\n        searchContext.setGroupIds(groupIds);\n        searchContext.setKeywords(keywords);\n\n        BooleanQuery booleanQuery = BooleanQueryFactoryUtil.create(\n            searchContext);\n\n        booleanQuery.addExactTerm(\"userId\", userId);\n\n        BooleanClause booleanClause = BooleanClauseFactoryUtil.create(\n            booleanQuery, BooleanClauseOccur.MUST.getName());\n\n        searchContext.setBooleanClauses(new BooleanClause[] {booleanClause});\n\n        Indexer indexer = IndexerRegistryUtil.getIndexer(FooEntry.class);\n\n        Hits hits = indexer.search(searchContext);\n\n        List<FooEntry> fooEntries = new ArrayList<FooEntry>();\n\n        for (int i = 0; i < hits.getDocs().length; i++) {\n                Document doc = hits.doc(i);\n\n                long entryId = GetterUtil\n                .getLong(doc.get(Field.ENTRY_CLASS_PK));\n\n                Entry entry = null;\n\n                try {\n                        entry = _fooEntryLocalService.getFooEntry(fooEntryId);\n                } catch (PortalException pe) {\n                        _log.error(pe.getLocalizedMessage());\n                } catch (SystemException se) {\n                        _log.error(se.getLocalizedMessage());\n                }\n\n                fooEntries.add(fooEntry);\n        }\n\n        return fooEntries;\n    }\n    ```\n\n    It largely boils down to instantiating and populating the search context,\n    which gets passed to the `indexer.search` call to retrieve the `Hits`.\n    Subsequently, populate the `List` by iterating through the `Hits`, using\n    each one's `ENTRY_CLASS_PK` field as the primary key of the entity in the\n    call to the entity's getter. The `BooleanClause` construction and inclusion\n    in the search context ensures that all the results returned correspond to\n    the `userId` that's passed to this method.\n\n2.  The `searchCount` method  returns a long of the result `List`'s `size`\n    method. You could just invoke the class's `search` method, then call the\n    `List` object's `size` method.\n\n    ```java\n    @Override\n    public long searchCount(long userId, long[] groupIds, String keywords) {\n        List<T> results = search(\n            userId, groupIds, keywords, null, null, QueryUtil.ALL_POS,\n            QueryUtil.ALL_POS);\n\n        return results.size();\n    }\n    ```\n\n    But, again, if the model entity is being indexed in a search engine, you can\n    use it to get a count without ever hitting the database. Using the `Hits`\n    object returned from a search (see the code from step 1, but don't include\n    `start` and `end` parameters in the `SearchContext`), call\n    `hits.getLegnth()` and you get the count, as an `int`.\n\nNow administrators responsible for complying with GDPR or other data erasure\nconcerns can search and filter your entity from the @product@ UAD interface.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/01-web-experience-management-intro.markdown",
    "content": "---\nheader-id: web-experience-management\n---\n\n# Web Experience Management\n\n[TOC levels=1-4]\n\nWeb Experience Management encompasses Liferay's features and tools for building\nSites and creating content. Many of these, like Web Content Management and Page\nTemplates, are graphical tools used by administrators and marketers. Others,\nlike Page Fragments, let web developers flex their muscles in content\ncreation. These articles cover where web development intersects with user\nexperience and how to use Liferay's Web Experience frameworks to integrate\ncustom applications into @product@.\n\nSpecifically, you'll learn about\n\n- [Developing Fragments](/docs/7-2/frameworks/-/knowledge_base/f/page-fragments)\n- [Supporting Custom Content Types](/docs/7-2/frameworks/-/knowledge_base/f/supporting-custom-content-types-in-content-and-display-pages)\n- [Screen Navigation](/docs/7-2/frameworks/-/knowledge_base/f/screen-navigation-framework)\n\nFor more information on applying these frameworks for users, see the\n[Web Experience Management](/docs/7-2/user/-/knowledge_base/u/web-experience-management)\nuser articles.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/02-page-fragments/01-page-fragments-intro.markdown",
    "content": "---\nheader-id: page-fragments\n---\n\n# Page Fragments\n\n[TOC levels=1-4]\n\nYou can use Page Fragments to take your design vision and accurately realize it\non a web page. You start with a \"blank slate.\" You then have three tools\nat your disposal to accomplish your vision:\n\n**HTML**: The markup of the fragment. Fragments use standard HTML with \nspecial tags to add dynamic behavior. \n\n**CSS**: Styles and positions the fragment's markup. \n\n**JavaScript**: Provides dynamic behavior to the fragment.\n\nThe HTML, CSS, and JavaScript are all completely standard, but can be \nenhanced with Liferay-specific features. You can specify text, images, and \nlinks as editable and provide for \"rich\" text with formatting.\n\nYou can also access the FreeMarker templates engine from your HTML using the\n[alternative (square bracket) syntax](https://freemarker.apache.org/docs/dgui_misc_alternativesyntax.html).\nLearn more about available FreeMarker objects in \n[Front-end Reference](/docs/7-2/reference/-/knowledge_base/r/front-end-reference).\n\nLiferay portlets can also be embedded in Fragments as widgets, making pages with\nFragments more dynamic than regular web content.\n\nNow you'll step through some Page Fragment basics.\n\n## Developing Page Fragments\n\nThere are two types of Page Fragments: *Sections* and *Components*. A Section\ndefines columns, padding, and spacing on the page. A Component contains content\nthat is added to a Section.\n\nFragments are created inside of Collections. Collections provide an easy way to \nmanage and share groups of related Fragments. Users navigate Collections when \nselecting Fragments to add to a page. To see examples, the admin page shows all\nthe out-of-the-box Fragments (and their code).\n\nYou can create and manage Fragments and Collections without using any external\ntools, but you can also use your preferred web development tools. For an\nexplanation of Fragment creation using Liferay's built in tools, see \n[Creating a Fragment](/docs/7-2/frameworks/-/knowledge_base/f/creating-fragments). \n\n## Making a Fragment Configurable\n\n| **Note:** Defining configurations for Page Fragments is available in Liferay\n| DXP 7.2 SP1+ and Liferay Portal GA2+.\n\nPage Fragments are also configurable: defining configuration options for your\nfragment eliminates having to maintain multiple other fragments similar in\nstyle. For example, if you want a dark background banner and a light background\nbanner, you can create one banner with a configuration option for background\ntype.\n\nThe following field types are supported for Fragment configurations:\n\n- `checkbox`\n- `colorPalette`\n- `itemSelector`\n- `select`\n- `text`\n\nThis is available for all Fragment types (e.g., Fragment Renderer, etc.).\n\nFor more information, see\n[Making a Fragment Configurable](/docs/7-2/frameworks/-/knowledge_base/f/making-a-fragment-configurable).\n\n### Fragments CLI\n\nTo streamline fragment development, @product-ver@ provides command line tools\nfor generating, importing, and exporting fragments and fragment collections. For\nmore information about the CLI, see the \n[official Liferay Fragments CLI project](https://github.com/liferay/generator-liferay-fragments/blob/master/README.md)\nreference. Using this CLI is also covered in \n[Developing a Fragment using Desktop Tools](/docs/7-2/frameworks/-/knowledge_base/f/page-fragments-desktop-tools).\n\n## Contributed Collections\n\nMost of the time, Page Fragments are created and imported through the Page\nFragments interface or created directly using the built-in tools. Any user with\nthe right permissions can update or edit Page Fragments created like this. You\nmay have certain situations, however, where you want 100% static fragments that\ncannot be modified. In this case you can create a Contributed Fragment\nCollection.\n\nContributed Fragment Collections are deployable modules containing Page\nFragments. Those fragments can be used just like regular fragments, but are not \ncontained in the database, and cannot be modified except by updating the module \nthey came from. Use the \n[Creating Contributed Collections guide](/docs/7-2/frameworks/-/knowledge_base/f/creating-contributed-fragment-collection)\nto learn to create your own Contributed Collections.\n\n## Fragment Specific Tags\n\nIn addition to standard HTML, CSS, and JavaScript you can use Liferay-specific\ntags to make editable sections or embed widgets in your Fragment.\n\nEditable elements can be modified before publication. This means that web\ndevelopers can create simple, reusable fragments that have identical formatting,\nbut contain elements that are adaptable to the specific context.\n\nYou can make text, images, and links in a fragment editable by using\nan `<lfr-editable>` tag. The `<lfr-editable>` tag requires a unique `id`, a \ntype, and some content of the specified type inside.\n\nThe following four `type` options are available in an `lfr-editable` \ntag:\n\n`text`: Creates a space for editable plain text. \n\n`image`: Must contain a valid `<img>` tag which can then be replaced with an\nimage before publishing---including those from Documents and Media.\n \n`rich-text`: Provides rich text formatting, such as bold, italics, underline,\nlinks, and predefined styles.\n\n`link`: Must contain a valid anchor tag for which the style, target URL, and \nlink text can be edited before publishing.\n\nThe text or images you provide here are the default values for the fields. \nYou may want to display them in the final version of the page, or you may want\nfiller text that should be replaced before the page is published.\n\nAll of these work together to help you create dynamic, reusable elements for \nbuilding a site. For example, if you need a small text box with an image and \nlink to provide a  product description, you can create a fragment containing\neditable filler text, space for an editable image, the appropriate formatting,\nand an editable link. That fragment can be added to multiple pages, and\nmarketers can define the image, text, and link for each product they need to\ndescribe. \n\nYou can make a Fragment even more dynamic by including a widget. Currently,\nportlets are the only embeddable types of widgets, but other options are planned.\n\nYou can find a complete list and usage examples of these in the \n[Page Fragments Reference](/docs/7-2/reference/-/knowledge_base/r/fragment-specific-tags).\n\n## Recommendations and Best Practices\n\nIn general all your code should be semantic and highly reusable. A main concern\nis making sure that everything is namespaced properly so it won't interfere with\nother elements on the page outside of the Fragment.\n\n## CSS\n\nWhile you can write any CSS in a fragment, it's recommended to prefix it with\na class specific to the fragment to avoid impacting other fragments. To\nfacilitate this, when creating a new fragment, the HTML includes a `div` with an\nautomatically generated class name and the CSS shows a sample selector using\nthat class. Use it as the basis for all selectors you add.\n\n## JavaScript\n\nAvoid adding a lot of JavaScript code, since it isn't easily reusable. Instead,\nreference external JS libraries.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/02-page-fragments/02-developing-a-fragment.markdown",
    "content": "---\nheader-id: creating-fragments\n---\n\n# Developing Fragments\n\n[TOC levels=1-4]\n\nThis tutorial assumes you understand Fragments and Collections. If you don't,\nread the\n[Creating Page Fragments](/docs/7-2/user/-/knowledge_base/u/creating-page-fragments)\narticle first. Once, you're ready, start by creating a Collection:\n\n1.  From the menu for your selected site, click *Site Builder* &rarr; *Page\n    Fragments*.\n \n2.  Create a new Collection named *Developing Fragments*.\n\nFirst, you'll create a *Section*.\n\n## Creating a Section\n\nThe list of Collections appears on the left in the Page Fragments page.\n\n1.  Ensure that you are in the *Developing Fragments* collection.\n\n2.  Click the *New* button ![New](../../../images/icon-add.png) and select\n    *Section*.\n\n3.  Name your Section *Basic Section*\n\nYou're now on the Fragment editing page. There are four panes on this screen.\nYou enter HTML in the top left pane, CSS in the top right, JavaScript in the\nbottom left, and preview the results in the bottom right. The Fragment Editor\neven comes with autocomplete functionality!\n\n![Figure 1: The Fragment editor provides autocomplete for Liferay Fragment specific tags.](../../../images/fragment-editor-autocomplete.png)\n\nYou can look at the three editing panes as if each were writing to a separate \nfile. Everything in the HTML pane goes to `index.html`, the CSS pane goes to \n`index.css`, and the JavaScript pane goes to `index.js`. The preview pane\nrenders everything as it looks on the page. \n\n| **Warning:** Including images inline in base64 in your Page Fragments can \n| increase publishing, import, and export times for pages using those Fragments. \n| Use \n| [references to resources](/docs/7-2/frameworks/-/knowledge_base/f/including-default-resources-in-fragments) \n| in your Page Fragments instead.\n\nA Section defines a work space. Now create a section with an editable rich text\narea where content can be entered:\n\n1.  Add the following code inside the HTML pane:\n\n    ```html\n    <div class=\"banner py-6 py-md-8 text-white\" data-lfr-background-image-id=\"banner\">\n        <div class=\"container my-lg-6\">\n            <div class=\"row\">\n                <div class=\"col-12 col-md-8 col-xl-6\">\n                    <h1>\n                        <lfr-editable id=\"01-title\" type=\"rich-text\">\n                            Banner Title Example\n                        </lfr-editable>\n                    </h1>\n\n                    <div class=\"mb-4 lead\">\n                        <p>\n                            <lfr-editable id=\"02-subtitle\" type=\"rich-text\">\n                                This is a simple banner component that you can use must provide extra information.\n                            </lfr-editable>\n                        </p>\n                    </div>\n\n                    <lfr-editable id=\"03-link\" type=\"link\">\n                        <a href=\"#\" class=\"btn btn-primary\">Go Somewhere</a>\n                    </lfr-editable>\n                </div>\n            </div>\n        </div>\n    </div>\n    ```\n\n2.  Replace the code in the CSS pane with the following:\n\n    ```css\n    .banner {\n        background-color:#415fa9;\n        background-position: center;\n        background-size: cover;\n    }\n    ```\n\n3.  Click *Publish* to save your work and make it available to add to a content\n    page.\n\n| **Note:** When you start typing the name of a tag, the HTML editor provides \n| auto-completion for `lfr` tags like editable elements and embeddable widgets.\n\nAs you work, you can observe your changes in the preview pane.\n\n![Figure 2: The Fragment editor with HTML and CSS code and a live preview.](../../../images/fragment-editor-basic.png)\n\n## Creating a Component\n\nComponents are simple, reusable elements for building parts of a page. Next\ncreate a button with a link as a Component:\n\n1.  Go back to the *Site Builder* &rarr; *Page Fragments* page and select the\n    *Developing Fragments* Collection.\n\n2.  Click the *New* button ![New](../../../images/icon-add.png) and select\n    *Component*.\n\n3.  Name it *Basic Component*.\n\n4.  Back in the editor, add the following code inside the HTML pane:\n\n    ```html\n    <div class=\"basic-link-button\">\n        <lfr-editable id=\"btn00\" type=\"link\">\n            <a href=\"#\" class=\"btn btn-primary\">Read More</a>\n        </lfr-editable>\n    </div>\n    ```\n\n5.  Click *Publish* to save your work and make it available to add to a content \n    page.\n\nThis fragment did not require any CSS. For the button link, no target is \nprovided by default, so the link must be configured when it is added to the\npage.\n\nFrom here, the Fragment can be added to a Page. To see this process in action, \nsee the\n[Building Content Pages](/docs/7-2/user/-/knowledge_base/u/building-content-pages)\narticle.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/02-page-fragments/03-making-a-fragment-configurable.markdown",
    "content": "---\nheader-id: making-a-fragment-configurable\n---\n\n# Making a Fragment Configurable\n\n[TOC levels=1-4]\n\n| **Note:** Defining configurations for Page Fragments is available in Liferay\n| DXP 7.2 SP1+ and Liferay Portal GA2+.\n\nDefining configuration options for a Fragment gives it more flexibility,\nreducing the number of Fragments you must maintain. To make a Fragment\nconfigurable,\n\n1.  Navigate to the *Site Builder* &rarr; *Page Fragments* page.\n\n2.  Click the *Actions* button (![Actions](../../../images/icon-actions.png))\n    &rarr; *Edit* for the Fragment (Section or Component) you want to make\n    configurable.\n\n3.  Select the *Configuration* tab at the top of the page.\n\n    ![Figure 1: Switch from the Code tab to the Configuration tab to create your configuration logic.](../../../images/fragment-config-tab.png)\n\n4.  In the editor, add your JSON code. This code is added to your fragment's\n    `index.json` file. For example, the code below provides the `select` option\n    to choose *dark* or *light* for a Fragment's heading:\n\n    ```json\n    {\n        \"fieldSets\": [\n            {\n                \"label\": \"heading\",\n                \"fields\": [\n                    {\n                        \"name\": \"headingAppliedStyle\",\n                        \"label\": \"applied-style\",\n                        \"description\": \"this-is-the-style-that-will-be-applied\",\n                        \"type\": \"select\",\n                        \"dataType\": \"string\",\n                        \"typeOptions\": {\n                            \"validValues\": [\n                                {\n                                    \"value\": \"dark\"\n                                },\n                                {\n                                    \"value\": \"light\"\n                                }\n                            ]\n                        },\n                        \"defaultValue\": \"light\"\n                    }\n                ]\n            }\n        ]\n    }\n    ```\n\n    | **Note:** The `label` property is optional. If it's left out, your\n    | configuration option has no title.\n\n    | **Note:** If your configuration is invalid, you can't save the\n    | code. Be sure to always have a valid JSON configuration before previewing\n    | or saving it.\n\n    The configuration values selected by the user are made available to the\n    Fragment developer through the FreeMarker context. A configuration value can\n    be referenced using the notation `${configuration.[fieldName]}`. For the\n    example snippet above, `${configuration.headingAppliedStyle}` returns\n    `dark` or `light` depending on the configuration value selected by the user.\n\n5.  You can refer to your Fragment's configuration values in its HTML file\n    (e.g., `index.html`). Navigate back to the *Code* tab at the top of the\n    page and add your HTML. For example,\n\n    ```html\n    [#if configuration.headingAppliedStyle == 'dark']\n    ...\n    [#else]\n    ...\n    [/#if]\n    ```\n\n    Configuration values inserted into the FreeMarker context honor the\n    defined `datatype` value specified in the JSON file. Therefore, for this\n    example, `configuration.headingAppliedStyle?is_string` is `true`.\n\n6.  Click *Publish* to save your work and make it available to add to a Content \n    Page.\n\n    ![Figure 2: You can click your Fragment to view its configuration options.](../../../images/fragment-lang-keys.png)\n\n| **Note:** You can also make a Fragment configurable by leveraging the\n| Fragments Toolkit. You can create/modify the Fragment's configuration JSON\n| file and then reimport the Fragment to your Liferay instance. For more\n| information, see\n| [Page Fragment Desktop Tools](/docs/7-2/frameworks/-/knowledge_base/f/page-fragments-desktop-tools).\n\nAlthough this example highlights accessing configuration values in HTML via the\nFreeMarker context, you can also access these values via JavaScript. JavaScript\nconfiguration objects are named the same as their FreeMarker counterparts. \n\nFor example, a configuration object could be built like this:\n\n```js\nvar configuration = {\n    field1: value1,\n    field2: value2\n}\n```\n\nAnother example of setting the configuration object and using it is shown below:\n\n```js\nconst configurationValue = configuration.field1\n\nconsole.log(configurationValue);\n```\n\nFor more examples of Fragment configuration, see \n[Fragment Configuration Types](/docs/7-2/reference/-/knowledge_base/r/fragment-configuration-types).\n\nAwesome! You now have a configurable Fragment!\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/02-page-fragments/04-managing-fragments-and-collections.markdown",
    "content": "---\nheader-id: managing-fragments-and-collections\n---\n\n# Managing Fragments and Collections\n\n[TOC levels=1-4]\n\nAfter you create Collections and Fragments, you have a handful of options for \nmanaging them. You'll explore several management options next.\n\n## Collections Management Menu\n\nTo access the collections management menu,\n\n1.  Select the Collection you want to manage from the *Collections* list.\n\n2.  Click on the ![Actions](../../../images/icon-actions.png) menu next to the \n    collection name.\n \n3.  Select whether you want to *Edit*, *Export*, *Import*, or *Delete* the\n    collection.\n\n    **Edit**: change the name or description for the collection.\n\n    **Export**: download a `.zip` file containing the full collection.\n\n    **Import**: select a `.zip` file to upload with additional Fragments.\n\n    **Delete**: remove the current collection and all of its contents.\n\nNext, you'll learn about the Fragment Management Menu.\n\n## Fragment Management Menu\n\nTo access the fragment management menu,\n\n1.  Select the Collection containing the Fragment you want to manage from the \n    *Collections* list.\n \n2.  Click on the ![Actions](../../../images/icon-actions.png) menu next to the \n    Fragment name.\n\n3.  Select whether you want to *Edit*, *Rename*, *Move*, *Make a Copy*, *Change \n    Thumbnail* *Export*, or *Delete*.\n\nDid you know you can enable automatic propagation for Fragments? You'll do this\nnext.\n\n## Propagating Fragment Changes Automatically\n\n| **Note:** Propagating Fragment changes is available in Liferay DXP 7.2 SP1+\n| and Liferay Portal GA2+.\n\nBy default, when a Fragment developer makes a change to an existing fragment,\nthe change is not automatically propagated to the pages that were using it. This\ngives marketers and page authors more control over the pages they own, avoiding\nunexpected changes. For example, if three pages were using the same Fragment, an\nupdate to the Fragment could introduce unintended changes to some of the pages\nusing it. While this is a safeguard for the production environment, developers\nmust\n[manually propagate Fragment changes](/docs/7-2/user/-/knowledge_base/u/propagation-of-changes)\nduring testing, which can be tedious. To give developers more freedom, you can\nenable automatic propagation for Fragment changes:\n\n1.  Navigate to the Control Panel &rarr; *Configuration* &rarr;\n    *System Settings* &rarr; *Page Fragments*.\n\n2.  Enable the checkbox *Propagate Fragment Changes Automatically*.\n\n3.  Click *Save*.\n\n![Figure 1: Once Fragment propagation is enabled, developers can automatically propagate Fragment changes to all pages using them.](../../../images/fragment-propagation.png)\n\nGreat! You've enabled Fragment propagation system wide! Now when a developer\npublishes a Fragment, the changes apply immediately to all Content Pages,\nContent Page Templates, and Display Page Templates using it, overwriting\nexisting Fragment code. Automatic propagation works only for HTML, CSS, and JS\nFragment code, not the editable values.\n\n| **Note:** It's recommended to only leverage this functionality during testing,\n| as automatic propagation on the production environment can cause unintended\n| consequences.\n\nWhen using the Fragment Editor, you're now notified that automatic Fragment\npropagation is enabled.\n\n![Figure 2: You're notified when automatic propagation is enabled.](../../../images/fragment-propagation-info.png)\n\nNow that you've seen how to use Liferay's built-in tools to manage Fragments,\nyou can see how to do it using your own tools of choice and the Fragments\nToolkit.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/02-page-fragments/05-page-fragments-desktop-tools.markdown",
    "content": "---\nheader-id: page-fragments-desktop-tools\n---\n\n# Developing A Fragment Using Desktop Tools\n\n[TOC levels=1-4]\n\nYou can develop a fragment using any preferred desktop tools. Since the Fragment\nis HTML, CSS, and JavaScript, you could use a text editor or a specialized tool\nwith its own built in previews. \n\n## Collection Format\n\nTo import a Collection into @product@, it must be archived in a `.zip` with the\ncontents in the following format:\n\n- `collection.json`: a text file which describes your collection with the \n     format `{\"name\":\"<collection-name>\",\"description\":\"<collection-description>\"}`.\n\n- `language.properties`: the language keys defined for the collection.\n\n    - `[fragment-name]/`: a folder containing all of the files for a single \n     Page Fragment.\n\n        - `fragment.json`: a text file that describes a Page Fragment with the \n          format\n\n          ```json\n          {\n              \"cssPath\": \"index.css\",\n              \"configurationPath\": \"index.json\",\n              \"htmlPath\": \"index.html\",\n              \"jsPath\": \"index.js\",\n              \"name\": \"<fragment-name>\",\n              \"type\": \"<fragment-type>\"\n           }\n           ```\n\n          Update the `*Path` properties in your `fragment.json` file if you\n          change the names of your `index.*` files.\n\n        - `index.css`: the CSS source for the fragment.\n\n        - `index.html`: the HTML source for the fragment.\n\n        - `index.json`: a JSON file that defines the fragment's configuration.\n\n        - `index.js`: the JavaScript source for the fragment.\n\n        - `thumbnail.png`: the thumbnail that will display when the Fragment is\n          displayed in a list.\n\n    - `[resources]/`: a folder containing any additional images or other\n      external files needed for the fragment.\n\nA collection can contain any number of fragments, so you can have multiple\nsubfolders in the collection. This format is the same as what's exported from\nwithin Liferay. If you import a `.zip` file that is not organized like this, \nany fragments that are found will be imported into special collection called \n*Imported* which is created for orphaned fragments.\n\n## Fragment CLI\n\nYou can manage Fragment creation and deployment manually, or you can use \nLiferay's Fragment CLI:\n\n1.  Follow the project instructions to\n    [set up the Fragments Toolkit](https://github.com/liferay/generator-liferay-fragments/blob/master/README.md).\n\n2.  Run `yo liferay-fragments`.\n\n3.  Follow the prompts to create a fragment.\n\nNow you will have the basic structure created, but there's still more that the \nFragments Toolkit can help you with.\n\n| **Note:** You can see all of the available tasks inside the `scripts` section\n| in the Fragment CLI `package.json`.\n\n### Creating Collections\n\nBefore you can create any Page Fragments, you must create a Collection. You can\nlearn more about Collections in the\n[Creating Page Fragments](/docs/7-2/user/-/knowledge_base/u/creating-content-pages#creating-page-fragments)\nsection. Creating a Collection will create the base folder structure and some\ninformation about your Collection. To do this,\n\n1.  From inside of your project, run `npm run add-collection`.\n\n2.  Follow the prompts to name your Collection.\n\nYou can now create Page Fragments inside of this Collection.\n\n### Creating Fragments\n\nA Page Fragment is made up of three primary files, `index.html`, `index.css`, \nand `index.js`. However, the files must be properly arranged in the folder \nstructure and have the appropriate metadata to be imported onto your server. \nThe Fragments Toolkit will create the files in the correct hierarchy with all \nof the necessary information.\n\n1.  From inside of the Collection you created, run `npm run add-fragment`.\n\n2.  Follow the prompts to add the necessary information about your Page \n    Fragment.\n    \nNow the files are all created and you can edit them using your editor of \nchoice.\n\n## Importing and Exporting Fragments\n\nThe Fragments Toolkit can connect to your currently running @product@ to import\nand export fragments. You can even have fragments that you create with the \ntoolkit imported into @product@ automatically.\n\n- To get collections and fragments from a running server, run `npm run export`.\n\n- To send the collections and fragments from your current project to a running\n  server, run `npm run import`. If your Fragment's configuration JSON (if\n  available) is invalid, the import fails and provides an error message.\n\n- To have collections and fragments automatically imported into @product@ as\n  they are created or modified, run `npm run import:watch`.\n\n- To preview how a fragment will look when it's imported, run `npm run preview`.\n  This renders a fragment on a specified Liferay server without importing it.\n  When changes are made to the fragment while it's previewed, changes are auto\n  reloaded to rapidly display updates. Note, this is available for Liferay DXP\n  7.2 SP1+ and Liferay Portal 7.2 GA2+. You must install the\n  [OAuth 2](https://web.liferay.com/marketplace/-/mp/application/109571986)\n  plugin in your portal instance for this command to work properly.\n\n- To create a `.zip` file that can be manually imported into @product@, run\n  `npm run compress`.\n\nWith these tools at your disposal, you can more efficiently manage creating \nand editing Page Fragments with whatever tools and environments work best for \nyou. \n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/02-page-fragments/06-creating-contributed-fragment-collection.markdown",
    "content": "---\nheader-id: creating-contributed-fragment-collection\n---\n\n# Creating a Contributed Fragment Collection\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n\t<span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/developer-guide/developing-page-fragments/creating-a-contributed-fragment-set.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nTo create a Contributed Fragment Collection, a developer must,\n\n1.  Create a module which will contain the necessary logic and the fragments.\n\n2.  Extend the class `BaseFragmentCollectionContributor` with all the logic for\n    reading the contributed fragments.\n\n3.  Add fragments as resources in the module.\n\nOnce you deploy the module, any fragments contained in it will be available for \nuse. \n\nTo better understand Contributed Fragment Collections, create one called\n`DemoFragmentCollectionContributor`.\n\n## Create a Module\n\nFirst you must create the module in your development environment. Follow the\ninstructions in\n[Creating a Project](/docs/7-2/reference/-/knowledge_base/r/creating-a-project).\n\n## Create the Java Class\n\nNext, you must create the Java package and class to handle the logic for the \ncontributed collection:\n\n1.  Create a package in your module named\n    `com.liferay.fragment.collection.contributor.demo`\n\n2.  Inside of that package, create a Java class named\n    `DemoFragmentCollectionContributor` that extends\n    `BaseFragmentCollectionContributor`.\n\n3.  Above the class declaration, add the `@Component` annotation to set the\n    service class:\n\n    ```java\n    @Component(service = FragmentCollectionContributor.class)\n    ```\n\n4.  Create the variable for the servlet context:\n\n    ```java\n    private ServletContext _servletContext;\n    ```\n\n5.  Define the `getFragmentCollectionKey()` and `getServletContext()` methods:\n\n    ```java\n    @Override\n    public String getFragmentCollectionKey() {\n        return \"DEMO\";\n    }\n\n    @Override\n    public ServletContext getServletContext() {\n        return _servletContext;\n    }\n    ```\n\n6.  Below that use the `@Reference` annotation to define your module's symbolic\n    name:\n\n    ```java\n    @Reference(\n        target = \"(osgi.web.symbolicname=com.liferay.fragment.collection.contributor.demo)\"\n    )\n    ```\n    | **Note:** `osgi.web.symbolicname` must match `Bundle-SymbolicName` from `bnd.bnd`\n\n7.  Organize your imports and save.\n\n## Create the Resources\n\nNext you need to include the fragments that you want to contribute in your \nmodule:\n\n1.  In your module's `resources/` folder, create the folder structure\n    `/com/liferay/fragment/collection/contributor/demo/dependencies`.\n\n    | **Note:** The class package name and resources package name must match \n    | (e.g. `[my.class.package.structure].dependencies`).\n\n2.  Copy the Fragments you want to distribute into the folder. You can learn \n    how to create a Fragment in the\n    [Creating Fragments section](/docs/7-2/frameworks/-/knowledge_base/f/creating-fragments).\n\n3.  Create a file named `collection.json` in the same folder with this format:\n\n    ```json\n    {\n        \"fragments\": [\n            \"[fragment-1]\",\n            \"[fragment-2]\",\n            \"[fragment-3]\",\n                ...\n        ],\n        \"name\": \"[collection-name]\"\n    }\n    ```\n\n    If a fragment is not listed in `collection.json`, it will not be available\n    in the Contributed Collection, even if the files are included in the module.\n\nNext, you'll configure the module's metadata so the fragments are imported.\n\n## Configuring the Metadata\n\nFollow these steps:\n\n1.  Open your bundle's `bnd.bnd` file and add the `Web-ContextPath` header to \n    point to your bundle's folder so the fragment resources are loaded properly:\n\n    ```properties\n    Web-ContextPath: /my-fragment-collection-contributor\n    ```\n\n2.  Add the `-dsannotations-options` instruction and set it to use the `inherit` \n    option. This specifies to use DS annotations found in the class hierarchy of \n    the component class:\n\n    ```properties\n    -dsannotations-options: inherit\n    ```\n\nNext, you'll dive into providing thumbnail images and language\nkeys/translations.\n\n## Providing Thumbnail Images\n\nYou can also provide thumbnail images for reference for your fragments:\n\n1.  Under `resources/META-INF/resources` create a folder named `thumbnails`.\n\n2.  Copy thumbnail images into the folder with the format `[fragment-name].png`\n    for each fragment.\n\n| **Note:** All fragments added through a Contributed Fragment Collection\n| will be available globally to all Sites.\n\n## Providing Language Keys\n\nProviding language keys in your Fragment gives you the option for translating\nthe text you display. Here's how to do it:\n\n1.  You must define your language keys in the Fragment's collection folder.\n    Create the `[COLLECTION]/src/main/resources/content/Language.properties`\n    file.\n\n2.  Add your language keys. For example,\n\n    ```properties\n    applied-style=Applied Style\n    this-is-the-style-that-will-be-applied=This is the style that will be applied.\n    dark=Dark\n    light=Light\n    ```\n\nYou can learn more about providing translations in the\n[Localizing Your Application](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application)\narticle.\n\n## Deploy the Contributed Fragment Collection\n\nNow that you have created the necessary pieces of the module, you can build it\nand [deploy it](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project) to\n@product@. After it's deployed, the Fragments will be available for use. This\ncan also be done by using the\n[Fragments Toolkit](/docs/7-2/frameworks/-/knowledge_base/f/page-fragments-desktop-tools#importing-and-exporting-fragments).\nContributed Fragments cannot be edited with Liferay, and can only be updated by\nediting the fragments in your module and the building and redeploying them.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/02-page-fragments/07-including-default-resources-in-fragments.markdown",
    "content": "---\nheader-id: including-default-resources-in-fragments\n---\n\n# Including Default Resources in Fragments\n\n[TOC levels=1-4]\n\nWhen creating Page Fragments, you can upload resources (e.g., images, documents,\netc.) to your Fragment Collection to make them always available from the\nCollection, rather than relying on resources uploaded in other areas of your\nSite (e.g., Documents and Media). For more information on how to include\nresources in your Fragment Collection from the Page Fragments interface, see \n[Creating Page Fragments](/docs/7-2/user/-/knowledge_base/u/creating-page-fragments).\n\nOnce you've uploaded your resource to a Fragment Collection, you can specify it\nin your Fragment:\n\n1.  Navigate to the Fragment editing page by clicking your Fragment's *Actions*\n    ![Actions](../../../images/icon-actions.png) &rarr; *Edit* button.\n\n2.  Specify the image by using this syntax: `[resources:IMAGE_NAME]`. For\n    example, you could include an image `building.png` within an HTML image tag\n    like this:\n\n    ```html\n    <img src=\"[resources:building.png]\">\n    ```\n\n    You can view a full example snippet below:\n\n    ```html\n    <div class=\"fragment_38314\">\n        <lfr-editable id=\"img\" type=\"image\">\n            <img src=\"[resources:building.png]\">\n        </lfr-editable>\n    </div>\n    ```\n\n3.  Add any additional HTML, CSS, or JavaScript to your Fragment and then click\n    *Publish*.\n\n    | **Note:** You can also reference your Fragment Collection's resources\n    | in your CSS code too. It follows the same syntax as its HTML.\n\n![Figure 1: Any Fragment from the Fragment Collection has access to the uploaded resources.](../../../images/fragment-resources.png)\n\nGreat! You've successfully referenced a default resource from your Fragment\nCollection!\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/03-supporting-custom-content-types-in-pages/01-intro.markdown",
    "content": "---\nheader-id: supporting-custom-content-types-in-content-and-display-pages\n---\n\n# Supporting Custom Content Types in Content and Display Pages\n\n[TOC levels=1-4]\n\nContent Pages and Display Page Templates can display several types of content\nout-of-the-box:\n\n- Web Content Article\n- Document\n- Blogs Entry\n\nYou can publish these content types in highly customizable ways using \n[Page Fragments](/docs/7-2/frameworks/-/knowledge_base/f/page-fragments). \nYou can use these page types to map fields of certain content (e.g., Web\nContent) to fields defined in a Page Fragment. Then you can publish the content\non a page using the Page Fragment as a template. To see an example of how\nDisplay Page Templates work, see the\n[Display Page Template Example](/docs/7-2/user/-/knowledge_base/u/display-page-template-example). For more info on creating Content Pages, see the\n[Building Content Pages](/docs/7-2/user/-/knowledge_base/u/building-content-pages)\narticle.\n\nIf you want to extend the Content Page or Display Page Template framework to\nsupport other content types, you must use the\n[Info framework](/docs/7-2/frameworks/-/knowledge_base/f/the-info-framework).\n\nYou must complete the following steps:\n\n1.  Provide basic information about your custom content type.\n\n2.  Provide your content type's fields so they're configurable in the Page\n    Editor.\n\n3.  Provide friendly URLs for your page type.\n\n4.  Handle the information that the user is requesting.\n\nAs an example, you'll step through how to provide this information to the\nContent Page and Display Page Template frameworks.\n\n| **Note:** This section assumes you're customizing Display Page Templates'\n| available content types. The same process outlined in these articles also\n| applies to Content Pages, although it's not explicitly stated.\n\nContinue on to begin!\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/03-supporting-custom-content-types-in-pages/02-mapping-a-content-type-to-a-display-page-template.markdown",
    "content": "---\nheader-id: mapping-a-content-type-to-a-page\n---\n\n# Mapping a Content Type to a Page\n\n[TOC levels=1-4]\n\nYou must allow the mapping of your custom content type to the page type. To do\nthis, implement the\n[`InfoDisplayContributor`](@app-ref@/info/2.0.0/javadocs/com/liferay/info/display/contributor/InfoDisplayContributor.html)\ninterface. Follow the steps below to complete this for the custom User content\ntype.\n\n1.  Inside your custom model project, create a new Java package and add a class\n    named `UserInfoDisplayContributor`.\n\n2.  Implement the `InfoDisplayContributor` interface and pass the `User` model\n    as the type parameter. Then add the `@Component` annotation:\n\n    ```java\n    @Component(immediate = true, service = InfoDisplayContributor.class)\n    public class UserInfoDisplayContributor\n        implements InfoDisplayContributor<User> {\n    }\n    ```\n\n    The `@Component` annotation registers the class as an info display\n    contributor in the OSGi service registry. Set the `service` property to the\n    interface you're implementing.\n\n3.  Implement the methods. For the example User content type, three methods are\n    crucial to mapping its model to the Display Page Template framework:\n\n    ```java\n    @Override\n    public String getClassName() {\n        return User.class.getName();\n    }\n\n    @Override\n    public String getInfoURLSeparator() {\n        return \"/user/\";\n    }\n\n    @Override\n    public String getLabel(Locale locale) {\n        return \"Users\";\n    }\n    ```\n\n    - The class name is used to link the Display Page Template to the User\n      model.\n    - The URL separator is used to generate friendly URLs for the Display Page\n      Template.\n    - The label is the display name for the new content type.\n\n![Figure 1: After creating the `*InfoDisplayContributor` class, you can create Display Page Templates and map them to your custom model.](../../../images/custom-model-selectable.png)\n\nGreat! You've mapped your custom content type to the Display Page Template\nframework. Next, you'll provide your content type's fields.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/03-supporting-custom-content-types-in-pages/03-specifying-the-fields-of-a-custom-content-type.markdown",
    "content": "---\nheader-id: specifying-the-fields-of-a-custom-content-type\n---\n\n# Specifying the Fields of a Custom Content Type\n\n[TOC levels=1-4]\n\nNow that your custom content type is selectable for a Display Page Template, you\nmust specify the fields you want the user to map to the fragment's editable\nfields in the Display Page Template. To do this, implement the\n[`InfoDisplayContributorField`](@app-ref@/info/2.0.0/javadocs/com/liferay/info/display/contributor/field/InfoDisplayContributorField.html)\ninterface.\n\nFollow the steps below to create a user name field for the User content type:\n\n1.  Inside your custom model project, add a class named\n    `UserNameInfoDisplayContributorField`.\n\n2.  Implement the `InfoDisplayContributorField` interface and pass the `User`\n    model as the type parameter. Then add the `@Component` annotation:\n\n    ```java\n    @Component(\n        property = \"model.class.name=com.liferay.portal.kernel.model.User\",\n        service = InfoDisplayContributorField.class\n    )\n    public class UserNameInfoDisplayContributorField\n        implements InfoDisplayContributorField<User> {\n    }\n    ```\n\n    The `@Component` annotation declares the class as an info display\n    contributor field in the OSGi service registry. You also set the property\n    `model.class.name`, which associates the content type you wish to configure\n    with this service.\n\n3.  Implement the methods.\n\n    ```java\n    @Override\n    public String getKey() {\n        return \"userName\";\n    }\n\n    @Override\n    public String getLabel(Locale locale) {\n        return \"User Name\";\n    }\n\n    @Override\n    public InfoDisplayContributorFieldType getType() {\n        return InfoDisplayContributorFieldType.TEXT;\n    }\n\n    @Override\n    public String getValue(User user, Locale locale) {\n        return user.getFullName();\n    }\n    ```\n\n    The above methods\n\n    - set the content type field key to `username`.\n    - set the field label to `User Name`.\n    - set the field type to text.\n    - set the field value to the user's full name.\n\n4.  Now you must override the `getInfoDisplayFields` method in your\n    `*DisplayContributor` class, so the mappable fields are displayed. Open the\n    `UserInfoDisplayContributor` class and add the following method:\n\n    ```java\n    @Override\n    public Set<InfoDisplayField> getInfoDisplayFields(\n            long classTypeId, Locale locale)\n        throws PortalException {\n\n        Set<InfoDisplayField> infoDisplayFields = new LinkedHashSet<>();\n\n        List<InfoDisplayContributorField> infoDisplayContributorFields =\n            _infoDisplayContributorFieldTracker.getInfoDisplayContributorFields(\n            getClassName());\n\n        for (InfoDisplayContributorField infoDisplayContributorField :\n        infoDisplayContributorFields) {\n\n            InfoDisplayContributorFieldType infoDisplayContributorFieldType =\n                infoDisplayContributorField.getType();\n\n            infoDisplayFields.add(\n                new InfoDisplayField(\n                infoDisplayContributorField.getKey(),\n                infoDisplayContributorField.getLabel(locale),\n                infoDisplayContributorFieldType.getValue()));\n        }\n\n        return infoDisplayFields;\n    }\n\n    @Reference\n    private InfoDisplayContributorFieldTracker _infoDisplayContributorFieldTracker;\n    ```\n\n    This method references your new `*InfoDisplayContributorField` class to\n    specify your content type's fields.\n\n![Figure 1: After creating the `*InfoDisplayContributorField` class, your custom content type has a new field to map.](../../../images/content-type-custom-fields.png)\n\nAwesome! You've mapped the content type's fields to the editable fields of the\nprovided fragments. Next, you'll provide the friendly URLs for the Display Page\nTemplate.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/03-supporting-custom-content-types-in-pages/04-providing-friendly-urls-for-a-custom-content-type.markdown",
    "content": "---\nheader-id: providing-friendly-urls-for-a-custom-content-type\n---\n\n# Providing Friendly URLs for a Custom Content Type\n\n[TOC levels=1-4]\n\nTo provide a friendly URL for your custom content type, you must implement a\nfriendly URL resolver to retrieve the desired information. For the User content\ntype example, you'll want to retrieve profiles by a user's screen name.\n\nTo do this, follow the steps below.\n\n1.  Open the `*InfoDisplayContributor` class you created previously (covered\n    [here](/docs/7-2/frameworks/-/knowledge_base/f/mapping-a-content-type-to-a-page)).\n    Implement the `getInfoDisplayObjectProvider` method. For the User example,\n    it looks like this:\n\n    ```java\n    @Override\n    public InfoDisplayObjectProvider<User> getInfoDisplayObjectProvider(\n            long groupId, String urlTitle)\n        throws PortalException {\n\n        Group group = _groupLocalService.getGroup(groupId);\n\n        User user = _userLocalService.getUserByScreenName(\n            group.getCompanyId(), urlTitle);\n\n        return new InfoDisplayObjectProvider<User>() {\n\n            @Override\n            public long getClassNameId() {\n                return _classNameLocalService.getClassNameId(getClassName());\n            }\n\n            @Override\n            public long getClassPK() {\n                return user.getUserId();\n            }\n\n            @Override\n            public long getClassTypeId() {\n                return 0;\n            }\n\n            @Override\n            public String getDescription(Locale locale) {\n                return StringPool.BLANK;\n            }\n\n            @Override\n            public User getDisplayObject() {\n                return user;\n            }\n\n            @Override\n            public long getGroupId() {\n                return groupId;\n            }\n\n            @Override\n            public String getKeywords(Locale locale) {\n                return StringPool.BLANK;\n            }\n\n            @Override\n            public String getTitle(Locale locale) {\n                return user.getFullName();\n            }\n\n            @Override\n            public String getURLTitle(Locale locale) {\n                return user.getScreenName();\n            }\n\n        };\n    }\n    ```\n\n    This method returns a new `InfoDisplayObjectProvider` for the User type.\n    This is the specific model instance used to retrieve the mapped values and\n    check for the display page. This is required by the friendly URL resolver.\n    Now you'll implement the friendly URL resolver for the User content type.\n\n2.  Inside your custom model project, add a class named\n    `UserDisplayPageFriendlyURLResolver`.\n\n3.  Implement the\n    [`FriendlyURLResolver`](@platform-ref@//7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/FriendlyURLResolver.html)\n    interface. Then add the `@Component` annotation:\n\n    ```java\n    @Component(service = FriendlyURLResolver.class)\n    public class UserDisplayPageFriendlyURLResolver implements FriendlyURLResolver {\n\n    }\n    ```\n\n    The `@Component` annotation declares the class as a friendly URL resolver in\n    the OSGi service registry.\n\n4.  Implement the methods. The User type's implementation looks like this:\n\n    ```java\n    @Override\n    public String getActualURL(\n        long companyId, long groupId, boolean privateLayout,\n          String mainPath, String friendlyURL, Map<String, String[]> params,\n        Map<String, Object> requestContext)\n    throws PortalException {\n\n        HttpServletRequest request = (HttpServletRequest)requestContext.get(\n            \"request\");\n\n        InfoDisplayContributor infoDisplayContributor =\n            _getInfoDisplayContributor();\n\n        List<String> paths = StringUtil.split(friendlyURL, CharPool.SLASH);\n\n        InfoDisplayObjectProvider infoDisplayObjectProvider =\n            infoDisplayContributor.getInfoDisplayObjectProvider(\n                groupId, paths.get(1));\n\n        request.setAttribute(\n            AssetDisplayPageWebKeys.INFO_DISPLAY_OBJECT_PROVIDER,\n                infoDisplayObjectProvider);\n\n        request.setAttribute(\n            InfoDisplayWebKeys.INFO_DISPLAY_CONTRIBUTOR,\n                infoDisplayContributor);\n\n        Layout layout = _getInfoDisplayObjectProviderLayout(\n            infoDisplayObjectProvider);\n\n        return _portal.getLayoutActualURL(layout, mainPath);\n    }\n\n    @Override\n    public LayoutFriendlyURLComposite getLayoutFriendlyURLComposite(\n        long companyId, long groupId, boolean privateLayout,\n        String friendlyURL, Map<String, String[]> params,\n        Map<String, Object> requestContext)\n    throws PortalException {\n\n        Layout layout = _getInfoDisplayObjectProviderLayout(\n            _getInfoDisplayObjectProvider(groupId, friendlyURL));\n\n        return new LayoutFriendlyURLComposite(layout, friendlyURL);\n    }\n\n    @Override\n    public String getURLSeparator() {\n        return \"/user/\";\n    }\n\n    private InfoDisplayContributor _getInfoDisplayContributor()\n        throws PortalException {\n\n        InfoDisplayContributor infoDisplayContributor =\n            _infoDisplayContributorTracker.\n                getInfoDisplayContributorByURLSeparator(getURLSeparator());\n\n        if (infoDisplayContributor == null) {\n            throw new PortalException(\n                \"Info display contributor is not available for \" +\n                getURLSeparator());\n        }\n\n        return infoDisplayContributor;\n    }\n\n    private InfoDisplayObjectProvider _getInfoDisplayObjectProvider(\n            long groupId, String friendlyURL)\n        throws PortalException {\n\n        List<String> paths = StringUtil.split(friendlyURL, CharPool.SLASH);\n\n        InfoDisplayContributor infoDisplayContributor =\n            _infoDisplayContributorTracker.getInfoDisplayContributor(\n                User.class.getName());\n\n        return infoDisplayContributor.getInfoDisplayObjectProvider(\n            groupId, paths.get(1));\n    }\n\n    private Layout _getInfoDisplayObjectProviderLayout(\n        InfoDisplayObjectProvider infoDisplayObjectProvider) {\n\n        LayoutPageTemplateEntry layoutPageTemplateEntry =\n            _layoutPageTemplateEntryService.fetchDefaultLayoutPageTemplateEntry(\n                infoDisplayObjectProvider.getGroupId(),\n                infoDisplayObjectProvider.getClassNameId(),\n                infoDisplayObjectProvider.getClassTypeId());\n\n        if (layoutPageTemplateEntry != null) {\n            return _layoutLocalService.fetchLayout(\n                layoutPageTemplateEntry.getPlid());\n        }\n\n        return null;\n    }\n\n    @Reference\n    private InfoDisplayContributorTracker _infoDisplayContributorTracker;\n\n    @Reference\n    private LayoutLocalService _layoutLocalService;\n\n    @Reference\n    private LayoutPageTemplateEntryService _layoutPageTemplateEntryService;\n\n    @Reference\n    private Portal _portal;\n    ```\n\n    Notice you're finding the `InfoDisplayObjectProvider` corresponding to the\n    current user. This serves as the representation/descriptor of the mapped\n    object.\n\n    | **Note:** This `FriendlyURLResolver` implementation uses the default\n    | display page template for the User model.\n\n    When this implementation is deployed, you'll receive an empty page when\n    calling the URL `[host]/web/guest/user/[screenName]`. You must return the\n    values from the users that are mapped to the display page. You'll do this\n    next.\n\n5.  Implement the `getInfoDisplayFieldsValue` method in the previously created\n    `*InfoDisplayContributor` class.\n\n    ```java\n    @Override\n    public Map<String, Object> getInfoDisplayFieldsValues(\n            User user, Locale locale)\n        throws PortalException {\n\n        Map<String, Object> infoDisplayFieldsValues = new HashMap<>();\n\n        List<InfoDisplayContributorField> infoDisplayContributorFields =\n            _infoDisplayContributorFieldTracker.getInfoDisplayContributorFields(\n                getClassName());\n\n        for (InfoDisplayContributorField infoDisplayContributorField :\n            infoDisplayContributorFields) {\n\n            Object fieldValue = infoDisplayContributorField.getValue(\n                user, locale);\n\n            infoDisplayFieldsValues.putIfAbsent(\n                infoDisplayContributorField.getKey(), fieldValue);\n        }\n\n        return infoDisplayFieldsValues;\n    }\n    ```\n\nGreat! Now you have a friendly URL that maps to your display page template's\ncustom content type.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/03-supporting-custom-content-types-in-pages/05-integrating-display-pages-into-content-creation.markdown",
    "content": "---\nheader-id: integrating-display-pages-into-content-creation\n---\n\n# Integrating Display Pages into Content Creation\n\n[TOC levels=1-4]\n\nAfter you add support for Display Pages in your custom entities, you can\nintegrate display page configuration into your entity's creation form.\n\n## Display Page Taglib Example\n\nTo provide the Display Page selector for the User type after you \n[created fields for it](/docs/7-2/frameworks/-/knowledge_base/f/specifying-the-fields-of-a-custom-content-type),\n\n1.  Open your JSP used for displaying the editing interface (e.g.,\n    `.../META-INF/resources/.../edit_entry.jsp`).\n\n2.  Add this code in the appropriate place in the layout to add the Display Page\n    selector:\n\n    ```markup\n    <liferay-asset:select-asset-display-page\n        classNameId=\"<%= PortalUtil.getClassNameId(User.class) %>\"\n      \tclassPK=\"<%= userId %>\"\n      \tgroupId=\"<%= scopeGroupId %>\"\n    />\n    ```\n\nNow, a selector is available to define a default Display Page when editing or\ncreating a User.\n\n![Figure 1: You need to add the Display Page selection to your content type's create/edit page to define the Display Page for each instance of that asset.](../../../images/display-pages-select-default-display-page.png)\n\nAwesome! Your custom content type is now available for Content Pages and/or\nDisplay Page Templates.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/04-screen-navigation/01-screen-navigation-framework-intro.markdown",
    "content": "---\nheader-id: screen-navigation-framework\n---\n\n# Screen Navigation Framework\n\n[TOC levels=1-4]\n\nThe Screen Navigation Framework is for customizing and extending application\nUIs. You can use it to make Liferay's applications your own and to make your\napplications customizable by others. \n\nThe framework uses a specific structure for screens and supports one or two \nlevels of navigation. Each item in the top level navigation is a \n`ScreenNavigationCategory`. Each item in the second level is a \n`ScreenNavigationEntry`. Categories are usually represented by tabs, while\nentries use a second level of navigation. You need not have any Entries in your\napplication, but you must have at least one Category.\n\n![Figure 1: The User Management application has three Screen Navigation Categories: General, Contact, and Preference; and each of those have a number of Screen Navigation Entries](../../../images/screen-nav-sample-screen-1.png)\n\nThe Screen structure normally renders Navigation Categories as horizontal tabs\nat the top of the page and Navigation Entries as a vertical list of items along\nthe left side of the page. The screen box containing the content uses the rest\nof the screen. You can customize this default layout for your needs.\n\n![Figure 2: Many application only use Screen Navigation Categories for their functionality, and don't have Screen Navigation Entries. For Blogs, Entries and Images are Categories with no Entries.](../../../images/screen-nav-one-level.png)\n\n## Using the Framework for Your Application \n\nThe Screen Navigation Framework comprises two parts: Java classes for your\nscreens and a tag library for your front-end. To use Screen Navigation for your\napplication, first you'll create the necessary Java classes and then add the \nfront-end support through JSPs.\n\nYour `ScreenNavigationCategory` class must be a a component that implements the \n`ScreenNavigationCategory` interface with these methods:\n\n**`getCategoryKey()`**: returns the category's primary key.\n\n**`getLabel(Locale locale)`**: returns the label of the key.\n\n**`getScreenNavigationKey()`**: returns the navigation key that the \ncategory belongs in, as defined in your application.\n\nYour `ScreenNavigationEntry` class, similarly must be a component which \nimplements `ScreenNavigationEntry` with the following methods:\n\n**`getCategoryKey()`**: returns the category's primary key.\n\n**`getEntryKey()`**: returns the entry's primary key.\n\n**`getLabel()`**: returns the entries label.\n\n**`getScreenNavigationKey()`**: returns the navigation key for the category \nof the current entry.\n\n**`isVisible(User user, T screenModelBean)`**: boolean for whether or not \nthe entry should be visible for the current user.\n\n**`render(HttpServletRequest request, HttpServletResponse response)`**: \nrenders the entry. The `render` method is also where any logic for creating\nthe configuration goes.\n\n## Adding Custom Screens to Liferay Applications \n\nYou can extend certain Liferay Applications with custom screens. Custom screens \ncan add configuration for features you've developed and added to a Liferay \napplication, integrating them seamlessly with the original application.\n\nThe parameters it needs are `key`, `modelBean`, and `portletURL`.\n\n* **Key**: a unique name for the navigation in this application.\n\n* **modelBean**: the model that is being rendered\n\n* **portletURL**: the portlet URL used to build the titles for each link.\n\nIn addition to these parameters, you must build the page that users will use\nfor configuration, and connect it to your business logic through the render \ncommand. \n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/04-screen-navigation/02-screen-navigation-custom-apps.markdown",
    "content": "---\nheader-id: using-screen-navigation-for-your-application\n---\n\n# Using Screen Navigation for Your Application\n\n[TOC levels=1-4]\n\nTo use the Screen Navigation framework with your application you must create a\nScreen Navigation Category and Entry, and then create the JSP to provide the\nlayout for the Screen Navigation Entry.\n\n## Adding Screens to Your Application's Back-end \n\nTo add screens to your application, first you must add at least one Navigation \nCategory for the top level navigation. Then you can add additional Navigation \nEntries for each page that you need. To demonstrate this, follow the \ninstructions for integrating Screen Navigation for a sample application.\n\nFirst, create the Navigation Category:\n\n1.  In your existing application, create a component named\n    `SampleScreenNavigationCategory` that implements the\n    `ScreenNavigationCategory` interface. \n\n2.  In the `@Component` declaration, set your priority property which determines\n    what order items appear in in the side navigation:\n\n    ```java\n    property = \"screen.navigation.category.order:Integer=10\",\n    ```\n\n3.  Add the following methods to the class body:\n\n    ```java\n    @Override\n    public String getCategoryKey() {\n    \treturn SampleScreenNavigationConstants.\n    \t\tCATEGORY_KEY_SAMPLE_CONFIGURATION;\n    }\n\n    @Override\n    public String getLabel(Locale locale) {\n    \treturn LanguageUtil.get(locale, \"general\");\n    }\n\n    @Override\n    public String getScreenNavigationKey() {\n    \treturn SampleScreenNavigationConstants.\n    \t\tSAMPLE_KEY_METHOD;\n    }\n    ```\n \n    When you're finished, your `ScreenNavigationCategory` class looks \n    like this:\n \n    ```java\n    @Component(\n    property = \"screen.navigation.category.order:Integer=10\",\n    service = ScreenNavigationCategory.class\n    )\n    public class SampleScreenNavigationCategory\n      implements ScreenNavigationCategory {\n\n      @Override\n      public String getCategoryKey() {\n        return SampleScreenNavigationConstants.\n            CATEGORY_KEY_SAMPLE_CONFIGURATION;\n      }\n\n      @Override\n      public String getLabel(Locale locale) {\n        return LanguageUtil.get(locale, \"general\");\n      }\n\n      @Override\n      public String getScreenNavigationKey() {\n        return SampleScreenNavigationConstants.\n            SAMPLE_KEY_METHOD;\n      }\n    }\n    ```\n \nNext, add a Navigation Entry: \n\n1.  Create a component named `SampleScreenNavigationEntry` which implements \n    `ScreenNavigationEntry`.\n\n2.  Create the the `@Reference` variables you need for the render logic:\n\n    ```java\n    @Reference\n    private ConfigurationProvider _configurationProvider;\n\n    @Reference\n    private JSPRenderer _jspRenderer;\n\n    @Reference\n    private Portal _portal;\n\n    @Reference(\n        target = \"(osgi.web.symbolicname=com.liferay.commerce.payment.method.sample)\"\n    )\n    private ServletContext _servletContext;\n    ```\n\n3.  Implement the following methods in your component:\n\n    ```java\n    @Override\n    public String getCategoryKey() {\n        return SampleScreenNavigationConstants.\n        \tCATEGORY_KEY_SAMPLE_CONFIGURATION;\n    }\n\n    @Override\n    public String getEntryKey() {\n        return SampleScreenNavigationConstants.\n            ENTRY_KEY_SAMPLE_CONFIGURATION;\n    }\n\n    @Override\n    public String getLabel(Locale locale) {\n        return LanguageUtil.get(\n        \tlocale,\n        \tSampleScreenNavigationConstants.\n        \t\tCATEGORY_KEY_SAMPLE_CONFIGURATION);\n    }\n\n    @Override\n    public String getScreenNavigationKey() {\n        return SpaceShipScreenNavigationConstants.\n        \tSAMPLE_KEY_METHOD;\n    }\n\n    @Override\n    public boolean isVisible(\n        User user, SamplePermissions spaceShipPermissions) {\n\n        if (samplePermissions.criteriaMethod()) \n        {\n\n        \treturn true;\n        }\n\n        return false;\n    }\n\n    @Override\n    public void render(HttpServletRequest request, HttpServletResponse response)\n    throws IOException {\n                \n        _jspRenderer.renderJSP(request, response, \"/my-category/view-category.jsp\");\n    }\n    ```\n    \n    Here is what the `SampleScreenNavigationEntry` class looks like:\n\n    ```java\n    @Component(\n    property = \"screen.navigation.entry.order:Integer=20\",\n    service = ScreenNavigationEntry.class\n    )\n    public class\n        SampleScreenNavigationEntry\n        implements ScreenNavigationEntry<SampleApplication> {\n        \n        public static final String\n        \tENTRY_KEY_SAMPLE_CONFIGURATION =\n        \t\t\"sample-configuration\";\n\n        @Override\n        public String getCategoryKey() {\n        \treturn SpaceShipScreenNavigationConstants.\n        \t\tCATEGORY_KEY_SAMPLE_CONFIGURATION;\n        }\n\n        @Override\n        public String getEntryKey() {\n        \treturn ENTRY_KEY_SAMPLE_CONFIGURATION;\n        }\n\n        @Override\n        public String getLabel(Locale locale) {\n        \treturn LanguageUtil.get(\n        \t\tlocale,\n        \t\tSpaceShipScreenNavigationConstants.\n        \t    \tCATEGORY_KEY_SAMPLE_CONFIGURATION);\n        }\n\n        @Override\n        public String getScreenNavigationKey() {\n        \treturn SpaceShipScreenNavigationConstants.\n        \t\tSAMPLE_KEY_METHOD;\n        }\n\n        @Override\n        public boolean isVisible(\n        \tUser user, SamplePermissions spaceShipPermissions) {\n\n        \tif (samplePermissions.criteriaMethod()) \n            {\n\n        \t\treturn true;\n        \t}\n\n           \t    return false;\n        \t}\n\n        @Override\n        public void render(HttpServletRequest request, HttpServletResponse response)\n        throws IOException {\n                \n            _jspRenderer.renderJSP(request, response, \"/my-category/view-category.jsp\");\n        }\n                    \t\n        @Reference\n        private JSPRenderer _jspRenderer;\n\n        @Reference(\n        \ttarget = \"(osgi.web.symbolicname=com.liferay.commerce.payment.method.sample);\n        \t\n        }\n    ```\n\nYou can implement your render method any way that you want as long as it\nprovides a way to render HTML. Liferay developers typically use JSPs, shown\nbelow. \n\n## Adding Screens to Your Application's Front-end \n\nThe `render` method that you created in your last step references \n`/my-category/view-category.jsp`. Create the JSP now:\n\n1.  In `/src/resources/META-INF/resources` create the `my-category` \n    folder.\n\n2.  Inside of that folder, create `view-category.jsp`.\n\n3.  Inside the JSP add the `liferay-frontend:screen-navigation` taglib with the \n    required parameters:\n\n    ```markup\n    <liferay-frontend:screen-navigation key=\"<%= AssetCategoriesConstants.CATEGORY_KEY_GENERAL %>\"\n    \tmodelBean=\"<%= category %>\"\n    \tportletURL=\"<%= portletURL %>\"\n    />\n    ```\n\nAfter that tag, add the rest of the content of the JSP file to handle user\ninteractions and communication with the back-end for configuration.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/04-screen-navigation/03-customizing-liferay-apps.markdown",
    "content": "---\nheader-id: extending-categories-administration\n---\n\n# Extending Categories Administration\n\n[TOC levels=1-4]\n\nThe Categories Administration application supports adding Custom Screens to\nprovide additional options for editing a category. To demonstrate adding a new\nScreen Navigation Entry and Category, you'll add one to Categories\nAdministration.\n\n1.  Create a new Java class in the `asset-categories-admin-web` module named \n    `CategoryCustomScreenNavigationEntry` that implements \n    `ScreenNavigationCategory` and `ScreenNavigationEntry`.\n\n2.  Add the following Component annotation above the class declaration:\n\n    ```java\n    @Component(\n        property = {\n        \t\"screen.navigation.category.order:Integer=1\",\n    \t    \"screen.navigation.entry.order:Integer=1\"\n    \t    },\n        \t service = {ScreenNavigationCategory.class, ScreenNavigationEntry.class}\n        )\n    ```\n\n    The `screen.navigation.category.order` and `screen.navigation.entry.order`\n    determine where in the navigation the items appear. Higher is first in the\n    navigation.\n \n    In the `service` declaration, declare it as defining\n    a `ScreenNavigationCategory`, `ScreenNavigationEntry`, or both.\n\n3.  For the class body, insert this code:\n\n    ```java\n    @Override\n    public String getCategoryKey() {\n        return \"custom-screen\";\n    }\n\n    @Override\n    public String getEntryKey() {\n        return \"custom-screen\";\n    }\n\n    @Override\n    public String getLabel(Locale locale) {\n        return LanguageUtil.get(locale, \"custom-screen\");\n    }\n\n    @Override\n    public String getScreenNavigationKey() {\n        return AssetCategoriesConstants.CATEGORY_KEY_GENERAL;\n    }\n\n    @Override\n    public void render(HttpServletRequest request, HttpServletResponse response)\n        throws IOException {\n\n             _jspRenderer.renderJSP(request, response, \"/category/custom-screen.jsp\");\n    }\n\n    @Reference\n    private JSPRenderer _jspRenderer;\n    ```\n\n4.  Create a `custom-screen.jsp` in the \n    `/resources/META-INF/resources/category/` folder.\n \n5.  At the top of your JSP class, insert the following scriptlet to use the \n    Screen Navigation UI:\n\n    ```markup\n    <%\n    String redirect = ParamUtil.getString(request, \"redirect\", assetCategoriesDisplayContext.getEditCategoryRedirect());\n\n    long categoryId = ParamUtil.getLong(request, \"categoryId\");\n\n    AssetCategory category = AssetCategoryLocalServiceUtil.fetchCategory(categoryId);\n\n    long parentCategoryId = BeanParamUtil.getLong(category, request, \"parentCategoryId\");\n\n    long vocabularyId = ParamUtil.getLong(request, \"vocabularyId\");\n\n    portletDisplay.setShowBackIcon(true);\n    portletDisplay.setURLBack(redirect);\n\n    renderResponse.setTitle(((category == null) ? LanguageUtil.get(request, \"add-new-category\") : category.getTitle(locale)));\n    %>\n    ```\n\n6.  Below that, insert the following tag:\n\n    ```markup\n    <liferay-frontend:screen-navigation key=\n    \"<%= AssetCategoriesConstants.CATEGORY_KEY_GENERAL %>\"\n    modelBean=\"<%= category %>\"\n    portletURL=\"<%= portletURL %>\"\n    />\n    ```\n\n7. For the rest of the JSP, create your custom screen.\n\nNow you can use that pattern to create additional screens for whatever you need.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/05-developing-a-fragment-renderer/01-intro.markdown",
    "content": "---\nheader-id: developing-a-fragment-renderer\n---\n\n# Developing a Fragment Renderer\n\n[TOC levels=1-4]\n\nWhen creating Fragments through @product@'s provided UI, you're given three\nfront-end languages to leverage: CSS, HTML, and JavaScript. Although you can\nharness a lot of power with these languages alone, they do not provide an easy\nway to retrieve and process information from the database or third party\nsystems. A common solution for this issue is creating a full-fledged portlet to\ncomplete common back-end necessities, but this is sometimes overkill for what you\nneed.\n\nFor a lightweight alternative, you can develop a *Fragment Renderer* to use\nLiferay's provided Java APIs for back-end tasks related to your Fragment. To do\nthis, you must\n[implement the `FragmentRenderer` interface](#implementing-the-fragmentrenderer-interface).\n\nOptionally, you can\n\n- [Leverage the `FragmentRendererContext`](#leveraging-the-fragmentrenderercontext).\n\n- [Use JSPs for your Fragment's display](#rendering-jsps).\n\n- [Choose when to display the component](#choosing-when-to-display-a-component).\n\n- [Translate the Collection language key](#translating-the-collection-language-key).\n\nYou'll explore each step next.\n\n## Implementing the FragmentRenderer Interface\n\nThe\n[`FragmentRenderer`](@app-ref@/fragment/latest/javadocs/com/liferay/fragment/renderer/FragmentRenderer.html)\ninterface requires the implementation of two methods:\n\n`getCollectionKey`: returns the unique key for the component's Collection.\nDefine this key in several components to group them under a collapsible panel in\nthe Page Editor.\n\n`getLabel`: provides the Fragment name.\n\nThe remaining methods are optional, but can be useful in many scenarios:\n\n<!-- Add when Configurable fragments are released for SP1. -Cody\n`getConfiguration`: returns the Fragment's configuration JSON code.\n-->\n\n`getImagePreviewURL`: returns the URL for previewing the Fragment's image.\n\n`getKey`: returns the Fragment's key.\n\n`getType`: returns the Fragment's type. Type values include\n`FragmentConstants.TYPE_COMPONENT` and `FragmentConstants.TYPE_SECTION`.\n\n`isSelectable`: defines whether page authors can select the Fragment\nRenderer. You'll learn more about this in the\n[Choosing When to Display a Component](#choosing-when-to-display-a-component)\nsection.\n\n`render` (highly recommended): defines how to render the Fragment Renderer\n(e.g., JSP or FreeMarker). You can leverage the `FragmentRendererContext` in\nthis method to facilitate the rendering process.\n\nNext, you'll learn about leveraging the `FragmentRendererContext`.\n\n## Leveraging the FragmentRendererContext\n\nThe `render` method receives a read-only instance of the interface\n[`FragmentRendererContext`](@app-ref@/fragment/latest/javadocs/com/liferay/fragment/renderer/FragmentRendererContext.html).\nThis provides information about the context in which the Fragment is being\nrendered. The fields of information that are accessible through it include\n\n**Fragment Entry Link**: The specific instance of the Fragment being rendered.\nThis information can be used to identify the specific site or page to which the\nFragment was added, when it was added, the user who added it, etc.\n\n**Locale**: The current locale to be used for any multi-locale text.\n\n**Mode**: There are three available modes:\n\n- **VIEW**: The component is being rendered within a page being viewed (not\n  edited).\n- **ASSET_DISPLAY_PAGE**: The component is being edited on a Display Page.\n- **EDIT**: The component is being edited on a Content Page.\n\nThere are other fields which should only be necessary for advanced use cases:\n\n**Preview Class PK**: If the Fragment supports displaying content, this field\nsupports previewing an *In progress* version of the content before\nit's ready to publish. In this case, the `render` method returns the content's\nprimary key. \n\n**Preview Type**: Represents the preview type you want to show. The accepted\nvalues include\n\n- **TYPE_LATEST_APPROVED**: The latest approved version of the content.\n- **TYPE_LATEST**: The latest version of the content.\n\n**Field Values**: Fragments can have editable elements through `<lfr-editable>`\ntags; this also applies to those created with `FragmentRenderer`. The\n`getFieldValuesOptional()` method retrieves the field values the user may have\nintroduced in them. This only applies in the context of a Display Page with the\nvalues of the mapped structure.\n\n**Segment Experience IDs**: A list of identifiers for experiences that have been\nconfigured for the current page.\n\n## Rendering JSPs\n\nUsually you'll want to avoid writing HTML in your Java code. Fortunately, you\ncan use the `render` method to use any templating mechanism of your choice. JSP\nintegration is provided out-of-the box. \n\nFor example, rendering a JSP for your Fragment Renderer would look like this:\n\n```java\n@Override\npublic void render(\n    FragmentRendererContext fragmentRendererContext,\n    HttpServletRequest httpServletRequest,\n    HttpServletResponse httpServletResponse) throws IOException {\n\n    httpServletRequest.setAttribute(\n        \"fragmentRendererContext\", fragmentRendererContext);\n\n    _jspRenderer.renderJSP(\n        httpServletRequest, httpServletResponse, \"/my-component.jsp\");\n}\n\n@Reference\nprivate JSPRenderer _jspRenderer;\n\n@Reference(\n    target = \"(osgi.web.symbolicname=com.liferay.fragment.renderer.docs)\",\n    unbind = \"-\"\n)\nprivate ServletContext _servletContext;\n```\n\nThis sets the\n[`FragmentRendererContext`](#leveraging-the-fragmentrenderercontext) in the\nHTTP servlet request, which is then used to render the included JSP file (e.g.,\n`my-component.jsp`).\n\nTo leverage JSPs, you must specify the servlet context for the JSP files. Since\nyour Fragment Renderer is an OSGi module, your `bnd.bnd` file must define a web\ncontext path:\n\n```markup\nBundle-SymbolicName: com.liferay.fragment.renderer.docs\nWeb-ContextPath: /my-fragment-renderer\n```\n\nThen you must reference the Servlet context using the symbolic name of your\nmodule, as was shown above:\n\n```java\n@Reference(\n    target = \"(osgi.web.symbolicname=com.liferay.fragment.renderer.docs)\",\n    unbind = \"-\"\n)\nprivate ServletContext _servletContext;\n```\n\n| **Note:** To use the JSP Renderer, your module must set the\n| `com.liferay.frontend.taglib` dependency in its build file.\n\nNext, you'll learn about controlling when your Fragment Renderer is displayed.\n\n## Choosing When to Display a Component\n\nSometimes offering Fragment components only makes sense in specific cases. You\ncan implement the `isSelectable(...)` method to specify under which conditions\nthe Fragment Renderer is available to page authors.\n\nFor example, if you wanted to make your Fragment Renderer only available in\nDisplay Pages, you could implement the `isSelectable` method like this:\n\n```java\n@Override\npublic boolean isSelectable(HttpServletRequest httpServletRequest) {\n    Layout layout = (Layout)httpServletRequest.getAttribute(WebKeys.LAYOUT);\n\n    if (Objects.equals(\n        layout.getType(), LayoutConstants.TYPE_ASSET_DISPLAY)) {\n\n        return true;\n    }\n\n    return false;\n}\n```\n\nThis determines the Fragment Renderer's page type and returns `true` when the\npage type is a Display Page or `false` if it's not.\n\n## Translating the Collection Language Key\n\nWhen setting your Fragment Renderer's collection name via the `getCollectionKey`\nmethod, you should specify it as a language key and then define it in a resource\nbundle.\n\nFor example, a `getCollectionKey` method could look like this:\n\n```java\n@Override\npublic String getCollectionKey() {\n    return \"sample-components\";\n}\n```\n\nTo specify `sample-components` in a resource bundle, create the\n`src/main/resources/content/Language.properties` file within the Fragment\nRenderer module and define it using the language key\n`fragment.collection.label.{collection-key}`. For example,\n\n```properties\nfragment.collection.label.sample-components=Sample Components\n```\n\nTo learn more about resource bundles, see the\n[Localization](/docs/7-2/frameworks/-/knowledge_base/f/localization) section.\n\nNext, you'll step through creating a Fragment Renderer.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-experience-management/05-developing-a-fragment-renderer/02-creating-a-fragment-renderer.markdown",
    "content": "---\nheader-id: creating-a-fragment-renderer\n---\n\n# Creating a Fragment Renderer\n\n[TOC levels=1-4]\n\nCreating a Fragment Renderer lets you call Liferay's provided Java APIs for\nback-end tasks related to your Fragment. In this article, you'll create a sample\nFragment Renderer that displays values stored in the current @product@\ninstance's database.\n\n1.  [Create a default module project](/docs/7-2/reference/-/knowledge_base/r/creating-a-project)\n    in your development environment.\n\n2.  Create a unique package name in the module's `src` directory and create\n    a new Java class in that package. To follow naming conventions, give your\n    class a unique name followed by `FragmentRenderer` (e.g.,\n    `ShowContextFragmentRenderer`).\n\n3.  Configure your new class to implement the\n    [`FragmentRenderer`](@app-ref@/fragment/latest/javadocs/com/liferay/fragment/renderer/FragmentRenderer.html)\n    interface:\n\n    ```java\n    public class ShowContextFragmentRenderer implements FragmentRenderer {\n    }\n    ```\n\n4.  Insert the following `@Component` annotation above the class declaration:\n\n    ```java\n    @Component(service = FragmentRenderer.class)\n    ```\n\n    This sets the OSGi service type to `FragmentRenderer`.\n\n5.  Implement the two required `FragmentRenderer` methods:\n\n    ```java\n    @Override\n    public String getCollectionKey() {\n        return \"sample-components\";\n    }\n    \n    @Override\n    public String getLabel(Locale locale) {\n        return \"Show Context Component\";\n    }\n    ```\n\n    The `getCollectionKey()` method returns a language key, which you'll define\n    later. The name displayed for this Fragment is defined as *Show Context\n    Component*.\n\n    ![Figure 1: The new Fragment Renderer appears in its defined component collection.](../../../images/show-context-fragment-renderer.png)\n\n6.  Implement the `render` method:\n\n    ```java\n    @Override\n    public void render(\n        FragmentRendererContext fragmentRendererContext,\n        HttpServletRequest httpServletRequest,\n        HttpServletResponse httpServletResponse) throws IOException {\n\n    \t\tPrintWriter printWriter = httpServletResponse.getWriter();\n\n    \t\tprintWriter.write(\"<h3>Context</h3>\");\n    \t\tprintWriter.write(\"<ul>\");\n\n    \t\tFragmentEntryLink fragmentEntryLink =\n            fragmentRendererContext.getFragmentEntryLink();\n\n    \t\tprintWriter.write(\"<li>Added by: \" + fragmentEntryLink.getUserName());\n    \t\tprintWriter.write(\"<li>Added in: \" + fragmentEntryLink.getCreateDate());\n\n    \t\tprintWriter.write(\"<li>Locale: \" + fragmentRendererContext.getLocale());\n    \t\tprintWriter.write(\"<li>Mode: \" + fragmentRendererContext.getMode());\n    \t\tprintWriter.write(\"<li>PreviewClassPK: \" + fragmentRendererContext.getPreviewClassPK());\n    \t\tprintWriter.write(\"<li>PreviewType: \" + fragmentRendererContext.getPreviewType());\n    \t\tprintWriter.write(\"<li>Segment experiences: \" + StringUtil.merge(fragmentRendererContext.getSegmentsExperienceIds(), \", \"));\n    \t\tprintWriter.write(\"</ul>\");\n    }\n    ```\n\n    This method leverages the\n    [`FragmentRendererContext`](/docs/7-2/frameworks/-/knowledge_base/f/developing-a-fragment-renderer#leveraging-the-fragmentrenderercontext),\n    which provides the Fragment's context information stored in the database.\n    This information is displayed in the Fragment Renderer when it's placed on\n    a page.\n\n    ![Figure 2: When adding the new Fragment Renderer to a page, the context information is displayed.](../../../images/show-context-fragment-renderer-page.png)\n\n7.  Define the language key `sample-components` that you used in the\n    `getCollectionKey()` method. To do this, create the\n    `src/main/resources/content/Language.properties` file and add the following\n    language key:\n\n    ```properties\n    fragment.collection.label.sample-components=Sample Components\n    ```\n\n8.  Provide the appropriate dependencies to compile your Fragment Renderer\n    project. For example, the following dependencies are defined for the Show\n    Context Component Fragment Renderer sample (Gradle build) deployed to\n    Liferay Portal 7.2 GA1:\n\n    ```groovy\n    dependencies {\n        compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\", version: \"4.13.0\"\n        compileOnly group: \"com.liferay\", name: \"com.liferay.fragment.api\", version: \"2.7.2\"\n        compileOnly group: \"com.liferay\", name: \"com.liferay.fragment.service\", version: \"2.0.10\"\n        compileOnly group: \"com.liferay\", name: \"com.liferay.frontend.taglib\", version: \"4.0.15\"\n        compileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\", version: \"3.0.0\"\n        compileOnly group: \"javax.portlet\", name: \"portlet-api\", version: \"3.0.0\"\n        compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\", version: \"3.0.1\"\n        compileOnly group: \"jstl\", name: \"jstl\", version: \"1.2\"\n        compileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\", version: \"1.3.0\"\n    }\n    ```\n\n    To stay in sync with the appropriate versions of your project's\n    dependencies, consider using the\n    [Target Platform](/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform)\n    framework.\n\nThat's it! You can compile the sample *Show Context Component* Fragment Renderer\nand [deploy it](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project)!\nIt'll be available to add for a Fragment-enabled page under the *Sample\nComponents* collection.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/01-intro.markdown",
    "content": "---\nheader-id: web-services\n---\n\n# Web Services\n\n[TOC levels=1-4]\n\nIt's important for apps on different machines to communicate. To enable this, an\napp can expose APIs so remote components (other apps or devices) can access the\napp's features. For example, one service could have a client app presenting\ninformation to users, a server app processing data in B2B setting, and an IoT\ndevice requesting data to do its work. Exposing web APIs lets external\napplications or devices communicate with yours. \n\nBecause @product@ contains so many apps and features, it's prudent for Liferay\nto let developers access those apps and features from external apps and devices\nby exposing their APIs. Additionally, Liferay's development platform makes it\neasy to extend them and create new ones. \n\nThere are three different approaches for clients to connect to @product@'s web \nAPIs: \n\n**Headless REST APIs:** You can consume RESTful web services independent of \n@product@'s front end (hence *headless*). These APIs conform to the \n[OpenAPI](https://swagger.io/docs/specification/about/) \nspecification. This is the modern, preferred way to work with web services in \n@product@. \n\n**GraphQL:** All the power of doing multiple queries in a unique request\nfollowing [GraphQL specification](https://graphql.github.io/graphql-spec/June2018/).\n\n**Plain Web/REST Services:** This is the old way to build and consume web \nservices in @product@, but is still supported. \n\nYou can also create your own Headless REST and GraphQL APIs through the **REST builder**.\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/01-intro.markdown",
    "content": "---\nheader-id: headless-rest-apis\n---\n\n# Headless REST APIs\n\n[TOC levels=1-4]\n\n@product@'s headless REST APIs follow the \n[OpenAPI](https://swagger.io/docs/specification/about/) \nspecification and let your apps consume RESTful web services. What's more, you \ncan consume these APIs without being tied to @product@'s UI (hence the term \n*headless*). This gives you a great deal of freedom when designing and \ndeveloping your apps. \n\nThe articles in this section show you how to navigate and consume @product@'s \nheadless REST APIs. But first, you'll learn the design approach for these APIs. \n\n## OpenAPI\n\n[OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md)\n(originally called Swagger) is a Linux Foundation project specification that\ndefines machine-readable files that describe REST APIs and how to consume them. \n\nOpenAPI has become a widely adopted standard for defining REST APIs and\nis supported by major players in the API ecosystem such as Google, Amazon, and\nMicrosoft. As a spec, it is language-agnostic, and many libraries implement it\nor provide code generation to help validate, consume, or produce APIs.\n\n@product@ leverages existing knowledge of OpenAPI to define, create and consume\nREST APIs.\n\n## API Vocabulary\n\nWhen defining an API, the developer must decide how to expose the representation\nof its resources. This determines its ease of use and how it can evolve. \nTraditionally, there are two approaches: \n\n**Contract Last:** The code is written first and features are exposed as web or \nREST services. This approach is typically easier for developers, as they must \nonly implement and expose the business logic. Service Builder is an example of \nthis. \n\n<!--Add link back for 'Service Builder' once service-builder-web-services article is available-->\n\n**Contract First:** The structure for client-server messages is written before \nthe code that implements the services. Such messages are defined independent of \nthe code. This avoids tight coupling and is less likely to break clients as APIs \nevolve. \n\n@product@'s headless web APIs use a mixture of both approaches. An OpenAPI \nprofile uses a contract first approach by defining the paths and schemas before \nwriting any code. It then generates an API automatically based on that profile, \nusing the contract-last characteristic of code generation (like Service \nBuilder). This allows fast development for developers. \n\nThis mixed approach delivers the best of both worlds, allowing a step of \nconscious API design and then simplifying the developer experience by exposing \nonly the business logic to implement. \n\nWhen writing the OpenAPI profile, the main focus should be on defining how\nclient-server messages represent the APIs' resources. In other words, the APIs'\nschemas are defined first and the attributes, resources, and operations are\nnamed to clearly define what they represent and how they should be used. \n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/02-discover-api.markdown",
    "content": "---\nheader-id: get-started-rest-api\n---\n\n# Get Started: Find the API\n\n[TOC levels=1-4]\n\nTo begin consuming web services, you must first know where they are (e.g., a \nservice catalog), what operations you can invoke, and how to invoke them. \nBecause @product@'s headless REST APIs leverage \n[OpenAPI](https://en.wikipedia.org/wiki/OpenAPI_Specification) \n(originally known as Swagger), you don't need a service catalog. You only need \nto know the OpenAPI profile from which to discover the rest of the API. \n\n@product@'s headless APIs are available in SwaggerHub at \n[`https://app.swaggerhub.com/organizations/liferayinc`](https://app.swaggerhub.com/organizations/liferayinc). \nEach API has its own URL in SwaggerHub. For example, you can access the delivery \nAPI definition at \n[`https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0`](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0). \n\nEach OpenAPI profile is also deployed dynamically in your portal instance under \nthis schema: \n\n    http://[host]:[port]/o/[insert-headless-api]/[version]/openapi.yaml\n\nFor example, if you're running @product@ locally on port `8080`, the home URL \nfor discovering the headless delivery API is: \n\n    http://localhost:8080/o/headless-delivery/v1.0/openapi.yaml\n\nYou must be logged in to access this URL, or use basic authentication and a \nbrowser or other tool like \n[Postman](https://www.getpostman.com), \n[Advanced REST Client](https://install.advancedrestclient.com/install), \nor even the `curl` command in your system console. \n\nFor simplicity, the examples in this documentation use the `curl` command and \nsend requests to a @product@ instance running locally on port `8080`. \n\nRun this `curl` command to access the home URL: \n\n    curl http://localhost:8080/o/headless-delivery/v1.0/openapi.yaml -u test@example.com:test\n\nYou should get a response like this: \n\n```yaml\nopenapi: 3.0.1\ninfo:\n  title: Headless Delivery\n  version: v1.0\npaths:\n  /v1.0/blog-posting-images/{blogPostingImageId}:\n    get:\n      tags:\n      - BlogPostingImage\n      operationId: getBlogPostingImage\n      parameters:\n      - name: blogPostingImageId\n        in: path\n        required: true\n        schema:\n          type: integer\n          format: int64\n      responses:\n        default:\n          description: default response\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/BlogPostingImage'\n(...)\n```\n\nThis response follows the OpenAPI version 3.0 syntax to specify the endpoints \n(URLs) of the API and schemas returned. You can also open the OpenAPI profile in \nan OpenAPI editor like the \n[Swagger Editor](https://editor.swagger.io). \nYou can use this editor to inspect the documentation and parameters and make \nrequests to the API. \n\nThere are also many other tools that support OpenAPI, such as client generators, \nvalidators, parsers, and more. See \n[OpenAPI.Tools](https://openapi.tools/) \nfor a comprehensive list. Leveraging OpenAPI provides standards support, \nextensive \n[documentation](https://swagger.io/docs/), \nand industry-wide conventions. \n\n## Related Topics\n\n[Get Started: Invoke a Service](/docs/7-2/frameworks/-/knowledge_base/f/get-started-invoke-a-service)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/03-invoke-service.markdown",
    "content": "---\nheader-id: how-to-invoke-rest-api-service\n---\n\n# How To Invoke a Service\n\n[TOC levels=1-4]\n\nOnce you know which API you want to call via the \n[OpenAPI profile](/docs/7-2/frameworks/-/knowledge_base/f/get-started-discover-the-api), \nyou can send a request to that resource's URL. For example, suppose you want to \nretrieve all the blog entries from a Site. If you consult the OpenAPI profile \nfor @product@'s delivery API, you can find this endpoint: \n\n```yaml\n\"/sites/{siteId}/blog-postings\":\n        get:\n            operationId: getSiteBlogPostingsPage\n            parameters:\n                - in: path\n                  name: siteId\n                  required: true\n                  schema:\n                      format: int64\n                      type: integer\n                - in: query\n                  name: filter\n                  schema:\n                      type: string\n                - in: query\n                  name: page\n                  schema:\n                      type: integer\n                - in: query\n                  name: pageSize\n                  schema:\n                      type: integer\n                - in: query\n                  name: search\n                  schema:\n                      type: string\n                - in: query\n                  name: sort\n                  schema:\n                      type: string\n            responses:\n                200:\n                    content:\n                        application/json:\n                            schema:\n                                items:\n                                    $ref: \"#/components/schemas/BlogPosting\"\n                                type: array\n                    description: \"\"\n            tags: [\"BlogPosting\"]\n```\n\nThe only required parameter is `siteId`, the ID of the blog postings' Site. \nInternally, the `siteId` is a `groupId` that you can retrieve from the database, \na URL, or @product@'s UI via the Site Administration menu. The following GET \nrequest gets the site's blog postings by providing the site ID (`20124`) in the \nURL: \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/blog-postings/\" -u 'test@example.com:test'\n```\n\nIf you send such a request to a site that contains some blog entries, the \nresponse should look like this: \n\n```json\n{\n  \"items\": [\n    {\n      \"alternativeHeadline\": \"The power of OpenAPI & Liferay\",\n      \"articleBody\": \"<p>We are happy to announce...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T07:04:47Z\",\n      \"dateModified\": \"2019-04-22T07:04:51Z\",\n      \"datePublished\": \"2019-04-22T07:02:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"new-headless-apis\",\n      \"headline\": \"New Headless APIs\",\n      \"id\": 59301,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 1\n}\n```\n\nThis response is a JSON object with information about the collection of blogs. \nThe response's attributes contain information about the resource (blogs, in this \ncase). Also note that the results are paginated. The `*page*` attributes refer \nto pages of results. Here's a description of some common attributes: \n\n`id`: Each item has an ID. You can use the ID to retrieve more information about \nthat item. For example, there are two `id` attributes in the above response: one \nfor the blog posting (`59301`) and one for the blog post's creator (`20130`). \n\n`lastPage`: The page number of the final page of results. The above response \nonly contains a single page, so its last page is `1`. \n\n`page`: The page number of the current page. The page in the above response is \n`1`. \n\n`pageSize`: The possible number of this resource's items to be included in a \nsingle page. In the above response this is `20`. \n\n`totalCount`: The total number of this resource's existing items (independent of \npagination). The above response lists the total number of blog postings (`1`) in \na Site. \n\nTo get information on a specific blog posting, send a GET request to the \n`blogPostingId` resource's URL with the blog posting's ID \n(`/blog-postings/{blogPostingId}`). For example, the URL for such a request to \nthe blog posting in the above response is `/blog-postings/59301`. Here's an \nexample response: \n\n```json\n{\n  \"alternativeHeadline\": \"The power of OpenAPI & Liferay\",\n  \"articleBody\": \"<p>We are happy to announce...</p>\",\n  \"creator\": {\n    \"familyName\": \"Test\",\n    \"givenName\": \"Test\",\n    \"id\": 20130,\n    \"name\": \"Test Test\",\n    \"profileURL\": \"/web/test\"\n  },\n  \"dateCreated\": \"2019-04-22T07:04:47Z\",\n  \"dateModified\": \"2019-04-22T07:04:51Z\",\n  \"datePublished\": \"2019-04-22T07:02:00Z\",\n  \"encodingFormat\": \"text/html\",\n  \"friendlyUrlPath\": \"new-headless-apis\",\n  \"headline\": \"New Headless APIs\",\n  \"id\": 59301,\n  \"numberOfComments\": 0,\n  \"siteId\": 20124\n}\n```\n\nAlthough this response is JSON, the API's consumer can select other formats to \nuse (like XML). For more information, see \n[API Formats and Content Negotiation](/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation). \n\n## Related Topics\n\n[Get Started: Discover the API](/docs/7-2/frameworks/-/knowledge_base/f/get-started-discover-the-api)\n\n[API Formats and Content Negotiation](/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/04-authenticated-requests.markdown",
    "content": "---\nheader-id: making-authenticated-rest-api-requests\n---\n\n# Making Authenticated Requests\n\n[TOC levels=1-4]\n\nTo make an authenticated request, you must authenticate as a specific User. \n\nThere are three authentication mechanisms available when invoking web APIs: \n\n**Basic Authentication:** Sends the user credentials as an encoded user name \nand password pair. This is the simplest authentication protocol (available since \nHTTP/1.0). \n\n**OAuth 2.0:** In @product-ver@, you can use OAuth 2.0 for authentication. See \nthe \n[OAuth 2.0 documentation](/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0) \nfor more information. \n\n**Cookie/Session authentication:** From inside the portal you can make direct\nrequests to the APIs by sending the session token.\n\nFirst, you'll learn how send requests with basic authentication. \n\n## Basic Authentication\n\nBasic authentication requires that you send an HTTP `Authorization` header \ncontaining the encoded user name and password. You must first get that encoded \nvalue. To do so, you can use `openssl` or a `Base64` encoder. Either way, you \nmust encode the `user:password` string. Here's an example of the `openssl` \ncommand for encoding the `user:password` string for a user `test@example.com` \nwith the password `Liferay`: \n\n```bash\nopenssl base64 <<< test@example.com:Liferay\n```\n\nThis returns the encoded value: \n\n    dGVzdEBleGFtcGxlLmNvbTpMaWZlcmF5Cg==\n\nIf you don't have `openssl` installed, try the `base64` command: \n\n```bash\nbase64 <<< test@example.com:Liferay\n```\n\n| **Warning:** Encoding a string as shown here does not encrypt the resulting \n| string. An encoded string can easily be decoded by executing \n| `base64 <<< the-encoded-string`, which returns the original string. \n| \n| Anyone listening to your request could therefore decode the `Authorization` \n| header and reveal your user name and password. To prevent this, ensure that \n| all communication is made through HTTPS, which encrypts the entire message \n| (including headers). \n\nUse the encoded value for the HTTP Authorization header when sending the \nrequest: \n\n```bash\ncurl -H \"Authorization: Basic dGVzdEBleGFtcGxlLmNvbTpMaWZlcmF5Cg==\" http://localhost:8080/o/headless-delivery/v1.0/sites/{siteId}/blog-postings/\n```\n\nThe response contains data instead of the 403 error that an unauthenticated \nrequest receives. For more information on the response's structure, see \n[Working with Collections of Data](/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data). \n\n```json\n{\n  \"items\": [\n    {\n      \"alternativeHeadline\": \"The power of OpenAPI & Liferay\",\n      \"articleBody\": \"<p>We are happy to announce...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T07:04:47Z\",\n      \"dateModified\": \"2019-04-22T07:04:51Z\",\n      \"datePublished\": \"2019-04-22T07:02:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"new-headless-apis\",\n      \"headline\": \"New Headless APIs\",\n      \"id\": 59301,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    },\n    {\n      \"alternativeHeadline\": \"How to work with OAuth\",\n      \"articleBody\": \"<p>To configure OAuth...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T09:35:09Z\",\n      \"dateModified\": \"2019-04-22T09:35:09Z\",\n      \"datePublished\": \"2019-04-22T09:34:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"authenticated-requests\",\n      \"headline\": \"Authenticated requests\",\n      \"id\": 59309,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 2\n}\n```\n\n## OAuth 2.0 Authorization\n\n@product-ver@ supports authorization via OAuth 2.0, which is a token-based \nauthorization mechanism. For more details, see \n[@product@'s OAuth 2.0 documentation](/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0). \nThe following sections show you how to use OAuth 2.0 to authenticate web API \nrequests. \n\n### Obtaining the OAuth 2.0 Token\n\nBefore using OAuth 2.0 to invoke a web API, you must register your application \n(your web API's consumer) as an authorized OAuth client. To do this, follow the \ninstructions in the \n[Creating an Application](/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0#creating-an-application) \nsection of the OAuth 2.0 documentation. When creating the application, fill in\nthe form as follows: \n\n**Application Name:** Your application's name. \n\n**Client Profile:** Headless Server. \n\n**Allowed Authorization Types:** Check *Client Credentials*. \n\nAfter clicking *Save* to finish creating the application, write down the Client \nID and Client Secret values that appear at the top of the form. \n\nNext, you must get an OAuth 2.0 access token. To do this, see the tutorial \n[Authorizing Account Access with OAuth 2](/docs/7-2/deploy/-/knowledge_base/d/authorizing-account-access-with-oauth2). \n\n### Invoking the Service with an OAuth 2.0 Token\n\nOnce you have a valid OAuth 2.0 token, include it in the request's \n`Authorization` header, specifying that the authentication type is a \n[bearer token](https://tools.ietf.org/html/rfc6750). \nFor example: \n\n```bash\ncurl -H \"Authorization: Bearer d5571ff781dc555415c478872f0755c773fa159\" http://localhost:8080/o/headless-delivery/v1.0/sites/{siteId}/blog-postings/\n```\n\nThe response contains the resources that the authenticated user has \npermission to access, just like the response from Basic authentication.\n\n## Using Cookie Authentication or Making Requests from the UI\n\nYou can call the REST APIs using the existing session from outside @product@\nby passing the session identifier (the cookie reference) and the Liferay Auth\nToken (a Cross-Site Request Forgery---CSRF---token).\n\nTo do a request from outside @product@ you must provide the `Cookie` identifier\nin the header. In CURL, pass the `-H` parameter: \n\n     -H 'Cookie: JSESSIONID=27D7C95648D7CDBE3347601FC4543F5D'\n\nYou must also provide the CSRF token by passing it in the `p_auth` query\nparameter, or by adding the URL to the whitelist of CSRF allowed URLs or disabling CSRF\nchecks altogether with the `auth.verifier.auth.verifier.PortalSessionAuthVerifier.check.csrf.token` property (application level).\n\nHere's a sample CURL request with the cookie and CSRF token:\n\n```bash\ncurl -H 'Cookie: JSESSIONID=27D7C95648D7CDBE3347601FC4543F5D' http://localhost:8080/o/headless-delivery/v1.0/sites/{siteId}/blog-postings/?p_auth=O4dCU1Mj\n```\n \nTo do a cookie request from inside @product@, from JavaScript code or a Java\nmethod, the session identifier is not needed and you must only provide\nthe CRSF token or add the API to the whitelist of CSRF allowed URLs.\n \n## Making Unauthenticated Requests\n\nUnauthenticated requests are disabled by default in @product@'s headless REST\nAPIs. You can, however, enable them manually by defining an exception in the\nService Access Policy to allow unauthenticated requests.\n\n1. Go to Control Panel &rarr; Configuration &rarr; Service Access Policy.\n\n2. Add a new Service Access Policy.\n\n3. Enable both *Enabled* and *Default* options.\n\n4. Use `com.liferay.headless.delivery.internal.resource.v1_0.OpenAPIResourceImpl`\n   for the Service Class and `getOpenAPI` for the Method Name (or the method/class\n   you want to expose).\n\n5.  Test the APIs by making a request to an OpenAPI profile URL: \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/openapi.yaml\"\n```\n\nYou should get the OpenAPI profile for the API you sent the request to. \n\n## Cross-Origin Resource Sharing (CORS)\n\nModern web browsers block access to content from domains other than the one\ncurrently being visited. For example, browsers block fetch/ajax requests from\na local JavaScript application (being executed in localhost:4000) that tries to\naccess a Tomcat server (running in localhost:8080). \n\nCross Origin Resource Sharing allows the configuration of safe resource sharing\nbetween sites. A web application using APIs can only request endpoints that have\nthe same origin/domain unless some special CORS headers are set that explicitly\nallow querying from different domains. \n\nFor development purposes, it's common to enable CORS headers to allow\nscripts to call APIs served by a different server. \n\n![Figure 1: Configure Cross-Origin Resource Sharing in Liferay](../../../images/cors.png)\n\nFollow these instructions to configure [Cross-Origin Resource Sharing (CORS)](/docs/7-2/deploy/-/knowledge_base/d/configuring-cors) \nin @product@.\n\n## Related Topics\n\n[Get Started: Invoke a Service](/docs/7-2/frameworks/-/knowledge_base/f/get-started-invoke-a-service)\n\n[Working with Collections of Data](/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/05-collections/01-intro.markdown",
    "content": "---\nheader-id: rest-working-with-collections-of-data\n---\n\n# Working with Collections of Data\n\n[TOC levels=1-4]\n\nCollection resources are common in @product@ web APIs. If you followed along \nwith the previous examples that sent requests to the portal's `blog-postings` \nresource URL, you've already seen collections in action: the `BlogPosting`\nresource is a collection. \n\nHere, you'll learn more detailed information about working with collection \nresources. But first you should learn about how collections are returned in \npages. \n\n## Pagination\n\nA small collection can be transmitted in a single response without difficulty. \nTransmitting a large collection all at once, however, can consume too much \nbandwidth, time, and memory. It can also overwhelm the user with too much data. \nIt's therefore best to get and display the elements of a large collection in \ndiscrete chunks, or pages. \n\n@product@'s headless REST APIs return paginated collections by default. The \nfollowing attributes in the responses also contain the information needed to \nnavigate between those pages: \n\n`totalCount`: The total number of this resource's items. \n\n`pageSize`: The number of this resource's items to be included in this\nresponse. \n\n`page`: The current page's number. \n\n`lastPage`: The last page's number. \n\n`items`: The collection elements present in this page. Each element also \ncontains the data of the object it represents, so there's no need for additional \nrequests for individual elements. \n\n`id`: Each item's identifier. You can use this, if necessary, to get more \ninformation on a specific item. \n\nFor examples of working with collection pages, see \n[Pagination](/docs/7-2/frameworks/-/knowledge_base/f/pagination). \n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/05-collections/02-getting-collections.markdown",
    "content": "---\nheader-id: getting-collections\n---\n\n# Getting Collections\n\n[TOC levels=1-4]\n\nRequests for collection resources are the same as those for non-collection \nresources. For example, an \n[authenticated request](/docs/7-2/frameworks/-/knowledge_base/f/making-authenticated-requests) \nto the `UserAccount` endpoint returns a collection containing the portal's \nusers. When sending this request, use the credentials of an administrative user \nwho has permission to view other portal users: \n\n```bash\ncurl \"http://localhost:8080/o/headless-admin-user/v1.0/user-accounts\"  -u 'test@example.com:test'\n```\n\nThe response (below) has two main parts: \n\n-   The list of collection elements, inside the `items` attribute. This example \n    contains data on two users: an administrator (Test), and a user named Javier \n    Gamarra. \n\n-   A set of metadata about the collection. This is the rest of the data in the \n    response. This lets clients know how to use the collection. \n\nThis response is in JSON, which is the default response format for web APIs in \n@product@. For information on specifying other response formats, see \n[API Formats and Content Negotiation](/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation). \n\n```json\n{\n  \"items\": [\n    {\n      \"alternateName\": \"test\",\n      \"birthDate\": \"1970-01-01T00:00:00Z\",\n      \"contactInformation\": {},\n      \"dashboardURL\": \"/user/test\",\n      \"dateCreated\": \"2019-04-17T20:37:19Z\",\n      \"dateModified\": \"2019-04-22T09:56:35Z\",\n      \"emailAddress\": \"test@example.com\",\n      \"familyName\": \"Test\",\n      \"givenName\": \"Test\",\n      \"id\": 20130,\n      \"name\": \"Test Test\",\n      \"profileURL\": \"/web/test\",\n      ...\n    },\n    {\n      \"alternateName\": \"nhpatt\",\n      \"birthDate\": \"1970-01-01T00:00:00Z\",\n      \"contactInformation\": {},\n      \"dateCreated\": \"2019-04-22T10:38:36Z\",\n      \"dateModified\": \"2019-04-22T10:38:37Z\",\n      \"emailAddress\": \"nhpatt@gmail.com\",\n      \"familyName\": \"Gamarra\",\n      \"givenName\": \"Javier\",\n      \"id\": 59347,\n      \"name\": \"Javier Gamarra\",\n      ...\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 2\n}\n```\n\n## Related Topics\n\n[Pagination](/docs/7-2/frameworks/-/knowledge_base/f/pagination)\n\n[Making Authenticated Requests](/docs/7-2/frameworks/-/knowledge_base/f/making-authenticated-requests)\n\n[API Formats and Content Negotiation](/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/05-collections/03-pagination.markdown",
    "content": "---\nheader-id: pagination\n---\n\n# Pagination\n\n[TOC levels=1-4]\n\nCollection resources are returned in pages of information. \n[Working with Collections of Data](/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data) \nexplains this in more detail. Here, you'll learn how to work with collection \npages. \n\nFor example, suppose that there are 123 users your portal and you want to get \ninformation on them. To do this, send an \n[authenticated request](/docs/7-2/frameworks/-/knowledge_base/f/making-authenticated-requests) \nto the UserAccount URL: \n\n```bash\ncurl \"http://localhost:8080/o/headless-admin-user/v1.0/user-accounts\"  -u 'test@example.com:test'\n```\n\nThe response contains the first 30 users and IDs for navigating the rest of the \ncollection. Note that most of the contents of the `items` attribute, which \ncontains the users, are omitted here so you can focus on the metadata for \nnavigating the collection: \n\n```json\n{\n  \"items\": [\n    {\n      \"id\": 20130,\n      ...\n    },\n    {\n      \"id\": 59347,\n      ...\n    }\n  ],\n  \"lastPage\": 5,\n  \"page\": 1,\n  \"pageSize\": 30,\n  \"totalCount\": 123\n}\n```\n\nThe attributes `page` and `pageSize` allow client applications to navigate \nthrough the results. For example, such a client could send a request for a \nspecific page. This example gets the second page (`?page=2`) of documents that \nexist on the site with the ID `20124`: \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/documents?page=2\"  -u 'test@example.com:test'\n```\n\nSimilarly, you can customize the number of elements per page via the optional \nparameter `pageSize` (e.g., `?pageSize=20`). \n\n## Related Topics\n\n[Working with Collections of Data](/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data)\n\n[Making Authenticated Requests](/docs/7-2/frameworks/-/knowledge_base/f/making-authenticated-requests)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/05-collections/04-collection-to-elements.markdown",
    "content": "---\nheader-id: navigating-from-a-collection-to-its-elements\n---\n\n# Navigating from a Collection to its Elements\n\n[TOC levels=1-4]\n\nWhen you \n[get a collection](/docs/7-2/frameworks/-/knowledge_base/f/getting-collections), \nyou can use the response to get an element of that collection. Follow these \nsteps to do so: \n\n1.  Get a collection. This example gets a list of users by sending \n    [an authenticated request](/docs/7-2/frameworks/-/knowledge_base/f/making-authenticated-requests) \n    to the `user-accounts` collection: \n\n    ```bash\n    curl \"http://localhost:8080/o/headless-admin-user/v1.0/user-accounts\"  -u 'test@example.com:test'\n    ```\n\n    Recall from \n    [Getting Collections](/docs/7-2/frameworks/-/knowledge_base/f/getting-collections) \n    that the response's `items` attribute contains the collection elements. In\n    this case, the collection contains two users: Test Test and Javier Gamarra: \n\n    ```json\n    {\n        \"totalItems\": 2,\n        \"numberOfItems\": 2,\n        \"view\": {\n            {\n              \"items\": [\n                {\n                  \"alternateName\": \"test\",\n                  \"birthDate\": \"1970-01-01T00:00:00Z\",\n                  \"contactInformation\": {},\n                  \"dashboardURL\": \"/user/test\",\n                  \"dateCreated\": \"2019-04-17T20:37:19Z\",\n                  \"dateModified\": \"2019-04-22T09:56:35Z\",\n                  \"emailAddress\": \"test@example.com\",\n                  \"familyName\": \"Test\",\n                  \"givenName\": \"Test\",\n                  \"id\": 20130,\n                  \"name\": \"Test Test\",\n                  \"profileURL\": \"/web/test\",\n                  \"roleBriefs\": [\n                    {\n                      \"id\": 20108,\n                      \"name\": \"Administrator\"\n                    },\n                    {\n                      \"id\": 20111,\n                      \"name\": \"Power User\"\n                    },\n                    {\n                      \"id\": 20112,\n                      \"name\": \"User\"\n                    }\n                  ],\n                  \"siteBriefs\": [\n                    {\n                      \"id\": 20128,\n                      \"name\": \"Global\"\n                    },\n                    {\n                      \"id\": 20124,\n                      \"name\": \"Guest\"\n                    }\n                  ]\n                },\n                {\n                  \"alternateName\": \"nhpatt\",\n                  \"birthDate\": \"1970-01-01T00:00:00Z\",\n                  \"contactInformation\": {},\n                  \"dateCreated\": \"2019-04-22T10:38:36Z\",\n                  \"dateModified\": \"2019-04-22T10:38:37Z\",\n                  \"emailAddress\": \"nhpatt@gmail.com\",\n                  \"familyName\": \"Gamarra\",\n                  \"givenName\": \"Javier\",\n                  \"id\": 59347,\n                  \"name\": \"Javier Gamarra\",\n                  \"roleBriefs\": [\n                    {\n                      \"id\": 20112,\n                      \"name\": \"User\"\n                    }\n                  ],\n                  \"siteBriefs\": [\n                    {\n                      \"id\": 20128,\n                      \"name\": \"Global\"\n                    },\n                    {\n                      \"id\": 20124,\n                      \"name\": \"Guest\"\n                    }\n                  ]\n                }\n              ],\n              \"lastPage\": 1,\n              \"page\": 1,\n              \"pageSize\": 20,\n              \"totalCount\": 2\n            }\n        ```\n\n2.  In the response, locate the ID of the element you want and look in the \n    OpenAPI profile for the appropriate GET item endpoint. For example, the \n    `user-accounts` GET item endpoint is `/user-accounts/{userAccountId}`. \n\n3.  Send a GET request to that endpoint. For example, this request gets \n    information for the user with the ID `59347` (Javier Gamarra): \n\n    ```bash\n    curl \"http://localhost:8080/o/headless-admin-user/v1.0/user-accounts/59347\"  -u 'test@example.com:test'\n    ```\n\n## Related Topics\n\n[Getting Collections](/docs/7-2/frameworks/-/knowledge_base/f/getting-collections)\n\n[Pagination](/docs/7-2/frameworks/-/knowledge_base/f/pagination)\n\n[Making Authenticated Requests](/docs/7-2/frameworks/-/knowledge_base/f/making-authenticated-requests)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/06-api-formats.markdown",
    "content": "---\nheader-id: rest-api-formats-and-content-negotiation\n---\n\n# API Formats and Content Negotiation\n\n[TOC levels=1-4]\n\nThe responses in the preceding examples use a standard JSON format, which is the \ndefault response format for @product@'s headless REST APIs. You can also use \nother formats like XML. Formats typically differ in the resource metadata's \nstructure or semantics. There's no best format; use the one that best fits your \nuse case. \n\nYou use *content negotiation* to specify different formats for use. Content \nnegotiation is how the client and server establish the format they use to \nexchange messages. The client tells the server its preferred format via the HTTP \nheaders `Accept` and `Content-Type`. Each format has a string identifier (its \nMIME type) that you can use in the HTTP headers to specify the format. The \nfollowing table lists the MIME type for each supported format. \n\n| API Format | &nbsp;MIME Type | \n| --------- | ----------------------- | \n| application/json | [application/json](https://www.iana.org/assignments/media-types/application/json) |\n| application/xml | [application/xml](https://www.iana.org/assignments/media-types/application/xml) |\n\nWhen you send a request without specifying the API format, the server responds \nwith the default JSON. For example, here's such a request for a list of folders \nfrom the Site with the ID `20124`: \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/document-folders\" -u 'test@example.com:test'\n```\n\n```json\n{\n  \"items\": [\n    {\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T10:21:20Z\",\n      \"dateModified\": \"2019-04-22T10:21:20Z\",\n      \"id\": 59319,\n      \"name\": \"REST APIs Documentation\",\n      \"numberOfDocumentFolders\": 0,\n      \"numberOfDocuments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 1\n}\n```\n\nIf you request the headers, the `Content-Type` response attribute lists the \ncontent type's format (JSON, in this case): \n\n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/document-folders\" -u 'test@example.com:test' --head\n```\n\n    HTTP/1.1 200 \n    X-Content-Type-Options: nosniff\n    X-Frame-Options: SAMEORIGIN\n    X-XSS-Protection: 1\n    Set-Cookie: JSESSIONID=9F61AEB8721DD9149BD577ECBC31AE3F; Path=/; HttpOnly\n    Expires: Thu, 01 Jan 1970 00:00:00 GMT\n    Cache-Control: private, no-cache, no-store, must-revalidate\n    Pragma: no-cache\n    Set-Cookie: COOKIE_SUPPORT=true; Max-Age=31536000; Expires=Tue, 21-Apr-2020 10:23:57 GMT; Path=/; HttpOnly\n    Set-Cookie: GUEST_LANGUAGE_ID=en_US; Max-Age=31536000; Expires=Tue, 21-Apr-2020 10:23:57 GMT; Path=/; HttpOnly\n    Date: Mon, 22 Apr 2019 10:23:57 GMT\n    Content-Type: application/json\n    Transfer-Encoding: chunked\n\nTo get the response in XML instead, specify `application/xml` in the request's \n`Accept` header. Note that the XML response includes the same information as \nJSON, but is structured differently: \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/documents/59203\"  -H 'Accept: application/xml'  -u 'test@example.com:test'\n```\n\n```xml\n    <Page>\n        <items>\n            <items>\n                <creator>\n                    <familyName>Test</familyName>\n                    <givenName>Test</givenName>\n                    <id>20130</id>\n                    <name>Test Test</name>\n                    <profileURL>/web/test</profileURL>\n                    </creator>\n                <dateCreated>2019-04-22T10:21:20Z</dateCreated>\n                <dateModified>2019-04-22T10:21:20Z</dateModified>\n                <id>59319</id>\n                <name>REST APIs Documentation</name>\n                <numberOfDocumentFolders>0</numberOfDocumentFolders>\n                <numberOfDocuments>0</numberOfDocuments>\n                <siteId>20124</siteId>\n            </items>\n        </items>\n        <lastPage>1</lastPage>\n        <page>1</page>\n        <pageSize>20</pageSize>\n        <totalCount>1</totalCount>\n    </Page>\n```\n\nRequesting the headers, you can see that the response is in XML \n(`application/xml`): \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/documents/59203\"  -H 'Accept: application/xml'  -u 'test@example.com:test' --head\n```\n\n    HTTP/1.1 200 \n    X-Content-Type-Options: nosniff\n    X-Frame-Options: SAMEORIGIN\n    X-XSS-Protection: 1\n    Expires: Thu, 01 Jan 1970 00:00:00 GMT\n    Cache-Control: private, no-cache, no-store, must-revalidate\n    Pragma: no-cache\n    Date: Mon, 22 Apr 2019 10:26:21 GMT\n    Content-Type: application/xml\n    Transfer-Encoding: chunked\n\n## Language Negotiation\n\nThe same mechanism used for requesting another response format (content \nnegotiation) is used for requesting content in another language. \n\nAPIs that are available in different languages return the options in a block \ncalled `availableLanguages`. For example, this block in the following response \nlists U.S. English (`en-US`) and Spain/Castilian Spanish (`es-ES`): \n\n```json\n{\n  \"availableLanguages\": [\n    \"en-US\",\n    \"es-ES\"\n  ],\n  \"contentFields\": [\n    {\n      \"dataType\": \"html\",\n      \"name\": \"content\",\n      \"repeatable\": false,\n      \"value\": {\n        \"data\": \"<p>The main reason is because Headless APIs have been designed with real use cases in mind...</p>\"\n      }\n    }\n  ],\n  \"contentStructureId\": 36801,\n  \"creator\": {\n    \"familyName\": \"Test\",\n    \"givenName\": \"Test\",\n    \"id\": 20130,\n    \"name\": \"Test Test\",\n    \"profileURL\": \"/web/test\"\n  },\n  \"dateCreated\": \"2019-04-22T10:29:40Z\",\n  \"dateModified\": \"2019-04-22T10:30:31Z\",\n  \"datePublished\": \"2019-04-22T10:28:00Z\",\n  \"friendlyUrlPath\": \"why-headless-apis-are-better-than-json-ws-services-\",\n  \"id\": 59325,\n  \"key\": \"59323\",\n  \"numberOfComments\": 0,\n  \"renderedContents\": [\n    {\n      \"renderedContentURL\": \"http://localhost:8080/o/headless-delivery/v1.0/structured-contents/59325/rendered-content/36804\",\n      \"templateName\": \"Basic Web Content\"\n    }\n  ],\n  \"siteId\": 20124,\n  \"title\": \"Why Headless APIs are better than JSON-WS services?\",\n  \"uuid\": \"e1c4c152-e47c-313f-2d16-2ee4eba5cd26\"\n}\n```\n\nTo request the content in another language, specify your desired locale in the \nrequest's `Accept-Language` header: \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/structured-contents/59325\"  -H 'Accept-Language: es-ES'  -u 'test@example.com:test'\n```\n\n```json\n    {\n      \"availableLanguages\": [\n        \"en-US\",\n        \"es-ES\"\n      ],\n      \"contentFields\": [\n        {\n          \"dataType\": \"html\",\n          \"name\": \"content\",\n          \"repeatable\": false,\n          \"value\": {\n            \"data\": \"<p>La principal razón es porque las APIs Headless se han diseñado pensando en casos de uso reales...</p>\"\n          }\n        }\n      ],\n      \"contentStructureId\": 36801,\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T10:29:40Z\",\n      \"dateModified\": \"2019-04-22T10:30:31Z\",\n      \"datePublished\": \"2019-04-22T10:28:00Z\",\n      \"friendlyUrlPath\": \"%C2%BFpor-qu%C3%A9-las-apis-headless-son-mejores-que-json-ws-\",\n      \"id\": 59325,\n      \"key\": \"59323\",\n      \"numberOfComments\": 0,\n      \"renderedContents\": [\n        {\n          \"renderedContentURL\": \"http://localhost:8080/o/headless-delivery/v1.0/structured-contents/59325/rendered-content/36804\",\n          \"templateName\": \"Contenido web básico\"\n        }\n      ],\n      \"siteId\": 20124,\n      \"title\": \"¿Por qué las APIs Headless son mejores que JSON-WS?\",\n      \"uuid\": \"e1c4c152-e47c-313f-2d16-2ee4eba5cd26\"\n    }\n```\n\n### Creating Content with Different Languages\n\nBy default, when sending a POST/PUT request, the `Accept-Language` header is\nused as the content's language. However, there is one exception. Some entities\nrequire the first POST to be in the Site's default language. In such \ncases, a POST request for a different language results in an error. \n\nAfter creating a new resource, PUT requests in a different language adds that \ntranslation. PATCH requests return an error (you are expected to update, \nnot create, in a PATCH request). \n\n## Related Topics\n\n[Get Started: Discover the API](/docs/7-2/frameworks/-/knowledge_base/f/get-started-discover-the-api)\n\n[Get Started: Invoke a Service](/docs/7-2/frameworks/-/knowledge_base/f/get-started-invoke-a-service)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/07-openapi-profiles.markdown",
    "content": "---\nheader-id: openapi-profiles\n---\n\n# OpenAPI Profiles\n\n[TOC levels=1-4]\n\nAll the APIs exposed by @product@ are available under the \n[liferayinc SwaggerHub organization](https://app.swaggerhub.com/organizations/liferayinc). \n\n@product@'s headless APIs are categorized in two different use cases:\n\n-   Delivering content (delivery APIs)\n-   Managing and administering content (admin APIs)\n\nThe available APIs demonstrate this categorization. \n\n## Headless Delivery\n\nThe following table lists the APIs that \n[Headless Delivery](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0) \ncontains. Note that the second column shows which internal model in @product@ \nthat the API maps to. \n\n| API | &nbsp;Internal Model | \n| --------- | ----------------------- | \n| `BlogPosting` | `BlogsEntry` |\n| `BlogPostingImage` | `DLFileEntry` (associated with a `BlogsEntry`) |\n| `Comment` | `DiscussionComment` |\n| `ContentDocument` | `DLFileEntry` (associated with a `JournalArticle`) |\n| `ContentSet` | `AssetListEntry` |\n| `ContentStructure` | `DDMStructure` |\n| `Document` | `DLFileEntry` |\n| `DocumentFolder` | `Folder` |\n| `KnowledgeBaseArticle` | `KBArticle` |\n| `KnowledgeBaseAttachment` | `FileEntry` (associated with a `KBArticle`) |\n| `KnowledgeBaseFolder` | `KBFolder` |\n| `MessageBoardAttachment` | `FileEntry` (associated with a `MBMessage`) |\n| `MessageBoardMessage` | `MBMessage` |\n| `MessageBoardSection` | `MBCategory` |\n| `MessageBoardThread` | `MBThread` |\n| `Rating` | `RatingsEntry` |\n| `StructuredContent` | `JournalArticle` |\n| `StructuredContentFolder` | `JournalFolder` |\n\n## Headless Administration\n\nThere are several headless admin APIs, each containing its own set of APIs. The \nfollowing tables list these, as well as any internal models in @product@ that\neach API maps to. \n\n[Headless Admin User](https://app.swaggerhub.com/apis/liferayinc/headless-admin-user/1.0) \ncontains the following APIs for retrieving and managing information about users \nand organizations. \n\n| API | &nbsp;Internal Model | \n| --------- | ----------------------- | \n| `EmailAddress` | N/A |\n| `Organization` | N/A |\n| `Phone` | N/A |\n| `PostalAddress` | `Address` |\n| `Role` | N/A |\n| `Segment` | `SegmentEntry` |\n| `SegmentUser` | N/A |\n| `SiteBrief` | N/A |\n| `UserAccount` | `User` |\n| `WebUrl` | `WebSite` |\n\n[Headless Admin Taxonomy](https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/1.0) \ncontains the following APIs for managing asset categories, asset vocabularies, \nand asset tags. \n\n| API | &nbsp;Internal Model | \n| --------- | ----------------------- | \n| `Keyword` | `AssetTag` |\n| `TaxonomyCategory` | `AssetCategory` |\n| `TaxonomyVocabulary` | `AssetVocabulary` |\n\n[Headless Admin Workflow](https://app.swaggerhub.com/apis/liferayinc/headless-admin-workflow/1.0) \ncontains APIs for transitioning workflows. \n\n## Related Topics\n\n[API Formats and Content Negotiation](/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/08-filter-and-sort.markdown",
    "content": "---\nheader-id: rest-filter-sort-and-search\n---\n\n# Filter, Sort, and Search\n\n[TOC levels=1-4]\n\nYou can use @product@'s headless REST APIs to search for content you're \ninterested in. You can also sort and filter content. Here, you'll learn how. \n\n## Filter\n\nIt's often useful to filter large collections for the exact data that you need. \nNot all collections, however, allow filtering. The ones that support it contain \nthe optional parameter `filter` in their OpenAPI profile. To filter a collection \nbased on the value of one or more fields, use the `filter` parameter following a \nsubset of the [oData standard](https://docs.oasis-open.org/odata/odata/v4.01/csprd06/part1-protocol/odata-v4.01-csprd06-part1-protocol.html#sec_BuiltinFilterOperations). \n\nFiltering mainly applies to fields indexed as keywords in @product@'s search. To \nfind content by terms contained in fields indexed as text, you should instead \nuse \n[search](#search). \n\n### Comparison Operators\n\n| Operator     | Description           | Example                               |\n|------------- |---------------------- | ------------------------------------- |\n| `eq`         | Equal                 | `addressLocality eq 'Redmond'`        |\n|              | Equal null            | `addressLocality eq null`             |\n| `ne`         | Not equal             | `addressLocality ne 'London'`         |\n|              | Not null              | `addressLocality ne null`             |\n| `gt`         | Greater than          | `price gt 20`                         |\n| `ge`         | Greater than or equal | `price ge 10`                         |\n| `lt`         | Less than             | `dateCreated lt 2018-02-13T12:33:12Z` |\n| `le`         | Less than or equal    | `dateCreated le 2012-05-29T09:13:28Z` |\n| `startswith` | Starts with           | `startswith(addressLocality, 'Lond')`   |\n\n### Logical Operators\n\n| Operator     | Description  | Example                          |\n| ------------ | ------------ | -------------------------------- |\n| `and`        | Logical and  | `price le 200 and price gt 3.5`  |\n| `or`         | Logical or   | `price le 3.5 or price gt 200`   |\n| `not`        | Logical not  | `not (price le 3.5)`             |\n\nNote that the `not` operator needs a space character after it. \n\n### Grouping Operators\n\n| Operator  | Description         | Example                                         |\n|---------- | ------------------- | ----------------------------------------------- |\n| `( )`     | Precedence grouping | `(price eq 5) or (addressLocality eq 'London')` |\n\n### String Functions\n\n| Function    | Description | Example                   |\n| ----------- | ----------- | ------------------------- |\n| `contains`  | Contains    | `contains(title,'edmon')` |\n\n### Lambda Operators\n\nLambda operators evaluate a boolean expression on a collection. They must be\nprepended with a navigation path that identifies a collection. \n\n| Lambda Operator | Description | Example                                    |\n| --------------- | ----------- | ------------------------------------------ |\n| `any`           | Any         | `keywords/any(k:contains(k,'substring1'))` |\n\nThe `any` operator applies a boolean expression to each collection element and\nevaluates to `true` if the expression is true for any element. \n\n### Operator combinations and OData syntax\n\nSyntax examples and other operator combinations are covered in the \n[OData standard reference](https://docs.oasis-open.org/odata/odata/v4.01/csprd06/part1-protocol/odata-v4.01-csprd06-part1-protocol.html#sec_BuiltinFilterOperations).\n\n### Escaping in Queries\n\nYou can escape a single quote in a value by adding another single quote. For \nexample, to filter for a blog posting whose headline is `New Headless APIs`, \nappend this filter string to the request URL: \n\n    ?filter=headline eq 'New Headless APIs'\n\nHere's an example of the full request: \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/blog-postings/?filter=headline%20eq%20%27New%20Headless%20APIs%27\"  -u 'test@example.com:test'\n```\n\n```json\n{\n  \"items\": [\n    {\n      \"alternativeHeadline\": \"The power of OpenAPI & Liferay\",\n      \"articleBody\": \"<p>We are happy to announce...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T07:04:47Z\",\n      \"dateModified\": \"2019-04-22T07:04:51Z\",\n      \"datePublished\": \"2019-04-22T07:02:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"new-headless-apis\",\n      \"headline\": \"New Headless APIs\",\n      \"id\": 59301,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 1\n}\n```\n\n### Filtering in Structured Content Fields (ContentField)\n\nTo filter for a `ContentField` value (dynamic values created by the end user), \nyou must use the endpoints that are scoped to an individual `ContentStructure`. \nTo do so, find the ID of the `ContentStructure` and use it in place of \n`{contentStructureId}` in this URL: \n\n    \"/content-structures/{contentStructureId}/structured-contents\"\n\n## Search\n\nIt's often useful to search large collections with keywords. Use search when you \nwant results from any field, rather than specific ones. To perform a search, use \nthe optional parameter `search` followed by the search terms. For example, this \nrequest searches for all the `BlogEntry` fields containing OAuth: \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/blog-postings/?search=OAuth\"  -u 'test@example.com:test'\n```\n\n```json\n{\n  \"items\": [\n    {\n      \"alternativeHeadline\": \"How to work with OAuth\",\n      \"articleBody\": \"<p>To configure OAuth...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T09:35:09Z\",\n      \"dateModified\": \"2019-04-22T09:35:09Z\",\n      \"datePublished\": \"2019-04-22T09:34:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"authenticated-requests\",\n      \"headline\": \"Authenticated requests\",\n      \"id\": 59309,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 1\n}\n```\n\n## Sorting\n\nSorting collection results is another common task. Note, however, that not all\ncollections allow sorting. The ones that support it contain the optional\nparameter `{lb}?sort{rb}` in their OpenAPI profile.\n\nTo get sorted collection results, append `?sort=<param-name>` to the request\nURL. For example, appending `?sort=title` to the request URL sorts the results\nby title. \n\nThe default sort order is ascending (0-1, A-Z). To perform a descending sort,\nappend `:desc` to the parameter name. For example, to perform a descending sort\nby title, append `?sort=title:desc` to the request URL. \n\nTo sort by more than one parameter, separate the parameter names by commas and\nput them in order of priority. For example, to sort first by title and then by\ncreation date, append `?sort=title,dateCreated` to the request URL. \n\nTo specify a descending sort for only one parameter, you must explicitly specify\nascending sort order (`:asc`) for the other parameters. For example: \n\n    ?sort=headline:desc,dateCreated:asc\n\n## Flatten\n\nSome collections (as defined in their OpenAPI profile) allow the query parameter \n`flatten`, which returns all resources and disregards folders or other \nhierarchical classifications. This parameter's default value is `false`, so a \ndocument query to the root folder returns only the documents in that folder. \nWith `flatten` set to `true`, the same query also returns documents in any \nsubfolders, regardless of how deeply those folders are nested. In other words, \nsetting `flatten` set to `true` and querying for documents in a Site's root \nfolder returns all the documents in the Site. \n\n## Related Topics\n\n[Making Authenticated Requests](/docs/7-2/frameworks/-/knowledge_base/f/filter-sort-and-search)\n\n[API Formats and Content Negotiation](/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation)\n\n[Working with Collections of Data](/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/09-restrict-properties.markdown",
    "content": "---\nheader-id: openapi-restrict-properties\n---\n\n# Restrict Properties\n\n[TOC levels=1-4]\n\nRetrieving large entities or collections increases the response's size and uses \nmore bandwidth. You can alleviate this by telling the server via the request \nwhich fields it should include in the response. This is known as \n*sparse fieldsets*. To make a request with sparse fieldsets, include the \n`fields` parameter in the URL with the name of each field's attribute. \n\nFor example, this request doesn't use sparse fieldsets and therefore returns all \nthe fields of a blog posting: \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/blog-postings/59301\"  -u 'test@example.com:test'\n```\n\n```json\n{\n  \"alternativeHeadline\": \"The power of OpenAPI & Liferay\",\n  \"articleBody\": \"<p>We are happy to announce...</p>\",\n  \"creator\": {\n    \"familyName\": \"Test\",\n    \"givenName\": \"Test\",\n    \"id\": 20130,\n    \"name\": \"Test Test\",\n    \"profileURL\": \"/web/test\"\n  },\n  \"dateCreated\": \"2019-04-22T07:04:47Z\",\n  \"dateModified\": \"2019-04-22T07:04:51Z\",\n  \"datePublished\": \"2019-04-22T07:02:00Z\",\n  \"encodingFormat\": \"text/html\",\n  \"friendlyUrlPath\": \"new-headless-apis\",\n  \"headline\": \"New Headless APIs\",\n  \"id\": 59301,\n  \"numberOfComments\": 0,\n  \"siteId\": 20124\n}\n```\n\nTo get only the headline, creation date, and creator, append the `fields` \nparameter to the URL with the fields `headline`, `dateCreated`, and `creator`: \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/blog-postings/59301?fields=headline,dateCreated,creator\"  -u 'test@example.com:test'\n```\n\n```json\n{\n  \"creator\": {\n    \"familyName\": \"Test\",\n    \"givenName\": \"Test\",\n    \"id\": 20130,\n    \"name\": \"Test Test\",\n    \"profileURL\": \"/web/test\"\n  },\n  \"dateCreated\": \"2019-04-22T07:04:47Z\",\n  \"headline\": \"New Headless APIs\"\n}\n```\n\nIn the response, the `creator` attribute is a nested JSON object. To return only \nthe creator's name, specify that nested field via dot notation (`creator.name`): \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/blog-postings/59301?fields=headline,dateCreated,creator.name\"  -u 'test@example.com:test'\n```\n\n```json\n{\n  \"creator\": {\n    \"name\": \"Test Test\"\n  },\n  \"dateCreated\": \"2019-04-22T07:04:47Z\",\n  \"headline\": \"New Headless APIs\"\n}\n```\n\nThe `fields` parameter also works with collection resources to return the \nspecified attributes for every collection item. For example, this request gets \nthe headlines for all the blog postings in the Site with the ID `20124`: \n\n```bash\ncurl \"http://localhost:8080/o/headless-delivery/v1.0/sites/20124/blog-postings/?fields=headline\"  -u 'test@example.com:test'\n```\n\n```json\n{\n  \"items\": [\n    {\n      \"headline\": \"New Headless APIs\"\n    },\n    {\n      \"headline\": \"Authenticated requests\"\n    }\n  ],\n  \"lastPage\": 1,\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 2\n}\n```\n\n## Related Topics\n\n[Making Authenticated Requests](/docs/7-2/frameworks/-/knowledge_base/f/filter-sort-and-search)\n\n[API Formats and Content Negotiation](/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation)\n\n[Working with Collections of Data](/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/10-multipart.markdown",
    "content": "---\nheader-id: rest-multipart-requests\n---\n\n# Multipart Requests\n\n[TOC levels=1-4]\n\nSeveral operations accept a binary file via a multipart request. For example, \nthe definition for posting a file to a `DocumentFolder` specifies a multipart \nrequest: \n\n```yaml\npost:\n    operationId: postDocumentFolderDocument\n    parameters:\n        - in: path\n          name: documentFolderId\n          required: true\n          schema:\n              format: int64\n              type: integer\n    requestBody:\n        content:\n            multipart/form-data:\n                schema:\n                    properties:\n                        document:\n                            $ref: \"#/components/schemas/Document\"\n                        file:\n                            format: binary\n                            type: string\n                    type: object\n    responses:\n        200:\n            content:\n                application/json:\n                    schema:\n                        $ref: \"#/components/schemas/Document\"\n                application/xml:\n                    schema:\n                        $ref: \"#/components/schemas/Document\"\n            description: \"\"\n    tags: [\"Document\"]\n```\n\nThis operation returns a `Document` (in JSON or XML). To create this `Document`, \nyou must supply the operation's multipart request with 2 components: \n\n-   A binary file (bytes) via the `file` property\n-   A JSON string with the binary file's metadata, via the `document` property\n\nTo send this request, the `Content-Type` must be `multipart/form-data`, and you \nmust also specify a boundary name (the boundary name can be arbitrary). \n\nHere's an example request (without the file's bytes) that creates a document in \nthe folder with the ID `38549`: \n\n    curl -X \"POST\" \"http://localhost:8080/o/headless-delivery/v1.0/document-folders/38549/documents\" \\\n         -H 'Accept: application/json' \\\n         -H 'Content-Type: multipart/form-data; boundary=PART' \\\n         -u 'test@example.com:test' \\\n         -F \"file=\" \\\n         -F \"document={\\\"title\\\": \\\"podcast\\\"}\"\n\nAnd here's the response: \n\n```json\n{\n  \"contentUrl\": \"/documents/20123/38549/podcast.mp3/e978e316-620c-df9f-e0bd-7cc0447cca49?version=1.0&t=1556100111417\",\n  \"creator\": {\n    \"familyName\": \"Test\",\n    \"givenName\": \"Test\",\n    \"id\": 20129,\n    \"name\": \"Test Test\",\n    \"profileURL\": \"/web/test\"\n  },\n  \"dateCreated\": \"2019-04-24T10:01:51Z\",\n  \"dateModified\": \"2019-04-24T10:01:51Z\",\n  \"documentFolderId\": 38549,\n  \"encodingFormat\": \"audio/mpeg\",\n  \"fileExtension\": \"mp3\",\n  \"id\": 38553,\n  \"numberOfComments\": 0,\n  \"sizeInBytes\": 28482097,\n  \"title\": \"podcast\"\n}\n```\n\n## Related Topics\n\n[Making Authenticated Requests](/docs/7-2/frameworks/-/knowledge_base/f/filter-sort-and-search)\n\n[API Formats and Content Negotiation](/docs/7-2/frameworks/-/knowledge_base/f/api-formats-and-content-negotiation)\n\n[Working with Collections of Data](/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/11-how-to-get-site-id.markdown",
    "content": "---\nheader-id: how-to-get-site-id\n---\n\n# How to get siteId\n\n[TOC levels=1-4]\n\nSeveral APIs (generally all collection APIs) need the `siteId` parameter to to\nexecute requests. The `siteId` is the internal identifier of the Site where\nthat content was created.\n\n## Using siteId or siteKey\n\nIn all the APIs available from 7.2 GA2+, the `siteKey` is also accepted as\na valid parameter. The `siteKey` is the external name of the Site (for example\n`Guest`). \n\nIn the REST APIs, you can use `siteKey` in all the places that expect a `siteId`;\nin GraphQL APIs, there are two different parameters: `siteId` and `siteKey`. \n\nUsing the `siteKey` is recommended over `siteId` in all situations because it's\nmore recognizable, doesn't expose an internal parameter, and doesn't change in\nimport/export processes. \n\n## Obtain siteId\n\nThere are several ways to retrieve the `siteId`:\n\n* Use the Site API to query it by name, friendly URL or in the list of a User's\n  Sites.\n* From @product@'s UI in the Site Administration menu (not recommended).\n* From the `Group` table in the database (not recommended). \n* From the `ThemeDisplay` object in JavaScript or Java: \n \n        themeDisplay.getSiteGroupId()\n\n![Figure 1: GraphQL BlogPostings definition](../../../images/rest-site-id.png)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/12-filtrable-properties.markdown",
    "content": "---\nheader-id: rest-filterable-properties\n---\n\n# Filterable properties\n\n[TOC levels=1-4]\n\nSome APIs return data that can be filtered and sorted by several properties.\nThis is a non-comprehensive list of the properties that can be used to filter or\nsort.\n\n## Headless Delivery API\n\n### [BlogPosting](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/BlogPosting)\n\n| Key                 | Type                  | Example                                   |\n|-------------------- |---------------------- | ----------------------------------------- |\n| taxonomyCategoryIds | list<integer>         | `taxonomyCategoryIds/any(t:t eq 1)`       |\n| keywords            | list<string>          | `keywords/any(k:contains(k,'substring1'))`|\n| customFields        | complex               | `customFields/Name eq 'Article1'`         |\n| dateCreated         | date                  | `dateCreated lt 2018-02-13T12:33:12Z`     |\n| dateModified        | date                  | `dateModified lt 2018-02-13T12:33:12Z`    |\n| creatorId           | integer               | `creatorId eq 1`                          |\n| headline            | string                | `contains(headline,'substring1')`         |\n\n### [BlogPostingImage](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/BlogPostingImage)\n\n| Key                 | Type                  | Example                                |\n|-------------------- |---------------------- | -------------------------------------- |\n| encodingFormat      | id                    | `encodingFormat eq 1`                  |\n| sizeInBytes         | integer               | `sizeInBytes eq 1`                     |\n| fileExtension       | string                | `contains(fileExtension,'substring1')` |\n| title               | string                | `contains(title,'substring1')`         |\n\n### [Comment](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/Comment)\n\n| Key          | Type                  | Example                               |\n|------------- |---------------------- | ------------------------------------- |\n| dateCreated  | date                  | `dateCreated lt 2018-02-13T12:33:12Z` |\n| dateModified | date                  | `dateModified lt 2018-02-13T12:33:12Z`|\n| creatorId    | integer               | `creatorId eq 1`                      |\n\n### [ContentStructure](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/ContentStructure)\n\n| Key                 | Type                  | Example                                   |\n|-------------------- |---------------------- | ----------------------------------------- |\n| dateCreated         | date                  | `dateCreated lt 2018-02-13T12:33:12Z`     |\n| dateModified        | date                  | `dateModified lt 2018-02-13T12:33:12Z`    |\n| name                | string                | `contains(name,'substring1')`             |\n\n### [Document](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/Document)\n\n| Key                 | Type                  | Example                                   |\n|-------------------- |---------------------- | ----------------------------------------- |\n| taxonomyCategoryIds | list<integer>         | `taxonomyCategoryIds/any(t:t eq 1)`       |\n| keywords            | list<string>          | `keywords/any(k:contains(k,'substring1'))`|\n| customFields        | complex               | `customFields/Name eq 'Article1'`         |\n| dateCreated         | date                  | `dateCreated lt 2018-02-13T12:33:12Z`     |\n| dateModified        | date                  | `dateModified lt 2018-02-13T12:33:12Z`    |\n| encodingFormat      | id                    | `encodingFormat eq 1`                     |\n| creatorId           | integer               | `creatorId eq 1`                          |\n| sizeInBytes         | integer               | `sizeInBytes eq 1`                        |\n| fileExtension       | string                | `contains(fileExtension,'substring1')`    |\n| title               | string                | `contains(title,'substring1')`            |\n\n### [DocumentFolder](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/DocumentFolder)\n\n| Key                 | Type                  | Example                                   |\n|-------------------- |---------------------- | ----------------------------------------- |\n| customFields        | complex               | `customFields/Name eq 'Article1'`         |\n| dateCreated         | date                  | `dateCreated lt 2018-02-13T12:33:12Z`     |\n| dateModified        | date                  | `dateModified lt 2018-02-13T12:33:12Z`    |\n| creatorId           | integer               | `creatorId eq 1`                          |\n| name                | string                | `contains(name,'substring1')`             |\n\n### [KnowledgeBaseArticle](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/KnowledgeBaseArticle)\n\n| Key                 | Type                  | Example                                   |\n|-------------------- |---------------------- | ----------------------------------------- |\n| taxonomyCategoryIds | list<integer>         | `taxonomyCategoryIds/any(t:t eq 1)`       |\n| keywords            | list<string>          | `keywords/any(k:contains(k,'substring1'))`|\n| customFields        | complex               | `customFields/Name eq 'Article1'`         |\n| dateCreated         | date                  | `dateCreated lt 2018-02-13T12:33:12Z`     |\n| dateModified        | date                  | `dateModified lt 2018-02-13T12:33:12Z`    |\n| title               | string                | `contains(title,'substring1')`            |\n\n### [MessageBoardMessage](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/MessageBoardMessage)\n\n| Key                   | Type                  | Example                                   |\n|---------------------- |---------------------- | ----------------------------------------- |\n| showAsAnswer          | boolean               | `showAsAnswer eq true`                    |\n| showAsQuestion        | boolean               | `showAsQuestion eq true`                  |\n| taxonomyCategoryIds   | list<integer>         | `taxonomyCategoryIds/any(t:t eq 1)`       |\n| keywords              | list<string>          | `keywords/any(k:contains(k,'substring1'))`|\n| customFields          | complex               | `customFields/Name eq 'Article1'`         |\n| dateCreated           | date                  | `dateCreated lt 2018-02-13T12:33:12Z`     |\n| dateModified          | date                  | `dateModified lt 2018-02-13T12:33:12Z`    |\n| creatorId             | integer               | `creatorId eq 1`                          |\n| messageBoardSectionId | integer               | `messageBoardSectionId eq 1`              |\n| headline              | string                | `contains(headline,'substring1')`         |\n\n### [MessageBoardSection](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/MessageBoardSection)\n\n| Key                   | Type                  | Example                                   |\n|---------------------- |---------------------- | ----------------------------------------- |\n| customFields          | complex               | `customFields/Name eq 'Article1'`         |\n| dateCreated           | date                  | `dateCreated lt 2018-02-13T12:33:12Z`     |\n| dateModified          | date                  | `dateModified lt 2018-02-13T12:33:12Z`    |\n| creatorId             | integer               | `creatorId eq 1`                          |\n| title                 | string                | `contains(title,'substring1')`            |\n\n### [StructuredContent](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/StructuredContent)\n\n| Key                   | Type                  | Example                                   |\n|---------------------- |---------------------- | ----------------------------------------- |\n| taxonomyCategoryIds   | list<integer>         | `taxonomyCategoryIds/any(t:t eq 1)`       |\n| keywords              | list<string>          | `keywords/any(k:contains(k,'substring1'))`|\n| contentFields         | complex               | `contentFields/Name eq 'Article1'`        |\n| customFields          | complex               | `customFields/Name eq 'Article1'`         |\n| dateCreated           | date                  | `dateCreated lt 2018-02-13T12:33:12Z`     |\n| dateModified          | date                  | `dateModified lt 2018-02-13T12:33:12Z`    |\n| datePublished         | date                  | `datePublished lt 2018-02-13T12:33:12Z`   |\n| contentStructureId    | integer               | `contentStructureId eq 1`                 |\n| title                 | string                | `contains(title,'substring1')`            |\n\n### [StructuredContentFolder](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/StructuredContentFolder)\n\n| Key                   | Type                  | Example                                   |\n|---------------------- |---------------------- | ----------------------------------------- |\n| customFields          | complex               | `customFields/Name eq 'Article1'`         |\n| dateCreated           | date                  | `dateCreated lt 2018-02-13T12:33:12Z`     |\n| dateModified          | date                  | `dateModified lt 2018-02-13T12:33:12Z`    |\n| creatorId             | integer               | `creatorId eq 1`                          |\n| name                  | string                | `contains(name,'substring1')`             |\n\n### [WikiNode](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/WikiNode)\n\n| Key                   | Type                  | Example                                   |\n|---------------------- |---------------------- | ----------------------------------------- |\n| dateCreated           | date                  | `dateCreated lt 2018-02-13T12:33:12Z`     |\n| dateModified          | date                  | `dateModified lt 2018-02-13T12:33:12Z`    |\n| creatorId             | integer               | `creatorId eq 1`                          |\n| name                  | string                | `contains(name,'substring1')`             |\n\n### [WikiPage](https://app.swaggerhub.com/apis/liferayinc/headless-delivery/v1.0#/WikiPage)\n\n| Key                   | Type                  | Example                                   |\n|---------------------- |---------------------- | ----------------------------------------- |\n| customFields          | complex               | `customFields/Name eq 'Article1'`         |\n| dateCreated           | date                  | `dateCreated lt 2018-02-13T12:33:12Z`     |\n| dateModified          | date                  | `dateModified lt 2018-02-13T12:33:12Z`    |\n| headline              | string                | `contains(headline,'substring1')`         |\n\n## Headless Admin User API\n\n### [Organization](https://app.swaggerhub.com/apis/liferayinc/headless-admin-user/v1.0#/Organization)\n\n| Key                  | Type             | Example                                   |\n|--------------------- |----------------- | ----------------------------------------- |\n| keywords             | list<string>     | `keywords/any(k:contains(k,'substring1'))`|\n| dateModified         | date             | `dateModified lt 2018-02-13T12:33:12Z`    |\n| parentOrganizationId | id               | `parentOrganizationId eq 1`               |\n| name                 | string           | `contains(name,'category')`               |\n\n### [User](https://app.swaggerhub.com/apis/liferayinc/headless-admin-user/v1.0#/User)\n\n| Key             | Type                  | Example                                   |\n|---------------- |---------------------- | ----------------------------------------- |\n| keywords        | list<string>          | `keywords/any(k:contains(k,'substring1'))`|\n| dateModified    | date                  | `dateModified lt 2018-02-13T12:33:12Z`    |\n| id              | id                    | `id eq 1`                                 |\n| organizationIds | id                    | `organizationIds eq 1`                    |\n| roleIds         | id                    | `roleIds eq 1`                            |\n| userGroupIds    | id                    | `userGroupIds eq 1`                       |\n| alternateName   | string                | `contains(alternateName,'substring1')`    |\n| emailAddress    | string                | `contains(emailAddress,'substring1')`     |\n| familyName      | string                | `contains(familyName,'substring1')`       |\n| givenName       | string                | `contains(givenName,'substring1')`        |\n| jobTitle        | string                | `contains(jobTitle,'substring1')`         |\n\n## Headless Admin Taxonomy API\n\n### [Category](https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/v1.0#/Category)\n\n| Key          | Type                  | Example                               |\n|------------- |---------------------- | ------------------------------------- |\n| dateCreated  | date                  | `dateCreated lt 2018-02-13T12:33:12Z` |\n| dateModified | date                  | `dateModified lt 2018-02-13T12:33:12Z`|\n| name         | string                | `contains(name,'category')`           |\n\n### [Keyword](https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/v1.0#/Keyword)\n\n| Key          | Type                  | Example                               |\n|------------- |---------------------- | ------------------------------------- |\n| dateCreated  | date                  | `dateCreated lt 2018-02-13T12:33:12Z` |\n| dateModified | date                  | `dateModified lt 2018-02-13T12:33:12Z`|\n| name         | string                | `contains(name,'category')`           |\n\n### [Vocabulary](https://app.swaggerhub.com/apis/liferayinc/headless-admin-taxonomy/v1.0#/Vocabulary)\n\n| Key          | Type                  | Example                               |\n|------------- |---------------------- | ------------------------------------- |\n| dateCreated  | date                  | `dateCreated lt 2018-02-13T12:33:12Z` |\n| dateModified | date                  | `dateModified lt 2018-02-13T12:33:12Z`|\n| name         | string                | `contains(name,'category')`           |\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/02-headless-apis/13-using-rest-apis.markdown",
    "content": "---\nheader-id: using-rest-apis\n---\n\n# Using REST APIs\n\n[TOC levels=1-4]\n\n@product@'s headless REST APIs can be used with any REST client you prefer. The\nonly usual requirements are setting up the `Authentication` header (either\nOAuth, Cookie, Basic...) and the `Content-Type` header if you are creating\ncontent.\n\nOur recommendation for JavaScript applications is to use `fetch` directly, like\nthis:\n\n```javascript\nfetch(`http://localhost:8080/o/headless-delivery/v1.0/sites/${SITE_ID}/structured-contents/'`, \n    {\n        method: 'GET',\n        headers: {\n            'Authorization': `Basic ${BASIC_AUTH}`\n        }\n    }\n);\n```\n\nOr for a `POST` request:\n\n```javascript\nfetch(`http://localhost:8080/o/headless-delivery/v1.0/sites/${SITE_ID}/structured-contents/`, \n    {\n        method: 'POST', \n        headers: {\n            'Authorization': `Basic ${BASIC_AUTH}`,\n            'Content-Type': 'application/json'\n        }, \n        body: JSON.stringify(\n            {\n                \"title\": \"New appointment\",\n                \"contentStructureId\": STRUCTURE_ID,\n                \"contentFields\": [\n                    {\n                        \"name\": \"User\",\n                        \"value\": {\n                            \"data\": USER,\n                        }\n                    },\n                ]\n            }\n        )\n    }\n)\n```\n\nHere are two examples of JavaScript applications using the Headless REST APIs:\n\n* [Alexa skill using Headless REST APIs with node-fetch](https://github.com/dgomezg/liferay-frontend-samples/tree/master/riuvo-alexa-skill).\n* [Example API from scratch using REST Builder](https://liferay.dev/blogs/-/blogs/creating-headless-apis-part-1).\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/03-jax/01-jaxrs-intro.markdown",
    "content": "---\nheader-id: jax-rs\n---\n\n# JAX-RS\n\n[TOC levels=1-4]\n\nJAX-RS web services work in Liferay modules the same way they work outside of\nLiferay. The only difference is that you must register the class in the OSGi\nframework. Liferay makes this easy by providing a template. \n\n[Create a project](/docs/7-2/reference/-/knowledge_base/r/creating-a-project)\nand choose the *rest* template. \n\nThe class that's generated contains a working JAX-RS web service. You can deploy\nit and use it immediately. \n\nWhile it's beyond the scope of this article to cover \n[JAX-RS Whiteboard](https://blog.osgi.org/2018/03/osgi-r7-highlights-jax-rs-whiteboard.html)\nin its entirety, essentially it's JAX-RS unchanged except for configuration\nproperties in the `@Component` annotation. These properties declare three things: \n\n1.  The endpoint for the service\n\n2.  The service name as it appears in the OAuth 2.0 configuration\n\n3.  (Optional) Properties you may want to set for further configuration.\n\nThe generated class contains this configuration: \n\n```java\n@Component( \n        property = { \n            JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE + \"=/greetings\", \n            JaxrsWhiteboardConstants.JAX_RS_NAME + \"=Greetings.Rest\"\n        }, \n        service = Application.class)\n```\n\nThis configuration registers the service at this endpoint: \n\n    https://[server-name]:[port]/o/greetings\n\nIf you're testing this locally on Tomcat, the URL is\n\n    https://localhost:8080/o/greetings\n\nAs you might guess, you don't have access to the service by just calling the URL\nabove. You must authenticate first, which you'll learn how to do next. \n\n## Authenticating to JAX-RS Web Services\n\nAuthentication during development can be done through Basic Authentication or\nportal sessions, but you don't want to leave that enabled for production. For\nproduction, you want OAuth 2.0. Here's how to configure JAX-RS authentication. \n\n### During Development: Basic Auth\n\nWhen you deploy a JAX-RS application, an \n[Auth Verifier](/docs/7-2/deploy/-/knowledge_base/d/authentication-verifiers)\nfilter is registered for it. You can set its properties in your `@Component`\nannotation by prefixing the properties with `auth.verifier`. For example, to\ndisable guest access to the service, configure it like this: \n\n```java\n@Component( \n        property = { \n            JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE + \"=/greetings\", \n            JaxrsWhiteboardConstants.JAX_RS_NAME + \"=Greetings.Rest\",\n            \"auth.verifier.guest.allowed=false\"\n        }, \n        service = Application.class)\n```\n\nBasic Auth is great during development, but credentials passed on the URL appear\nin server logs, so when you're done developing, you should disable Basic Auth\nand use OAuth2 instead. To disable Basic Auth, create and deploy a configuration\nfile called\n`com.liferay.portal.security.auth.verifier.internal.tracker.AuthVerifierFilterTracker.config` \nthat contains this property: \n\n```properties\ndefault.registration.property=[\"filter.init.auth.verifier.OAuth2RESTAuthVerifier.urls.includes=*\",\"filter.init.auth.verifier.PortalSessionAuthVerifier.urls.includes=*\"]\n```\nThis disables Basic Auth for all JAX-RS applications, but keeps Portal Session\nand OAuth2 enabled. \n\n### Using OAuth 2.0 to Invoke a JAX-RS Web Service\n\nYour JAX-RS web service requires authorization by default. To enable this, you\nmust create an \n[OAuth 2.0 application](/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0#creating-an-application)\nto provide a way to grant access to your service: \n\n1.  Go to the *Control Panel* &rarr; *Configuration* &rarr; *OAuth2\n    Administration* and click the ![add](../../../images/icon-add.png) button to\n    add an application. \n\n2.  Give your application a descriptive name. \n\n3.  Choose the Client Profile appropriate for this service. These are templates\n    that auto-select the appropriate authorization types or \"flows\" from the\n    OAuth 2 standard. For this example choose the *Headless Server* profile,\n    which auto-selects the *Client Credentials* authorization type. \n\n4.  Click *Save*. \n\nThe form now reappears with two additional generated fields: Client ID and\nClient Secret. You'll use these to authenticate to your web service. \n\nTo make your service accessible, \n\n1.  Click the *Scopes* tab. \n\n2.  You'll see an entry for your deployed `Greetings.Rest` service. Expand it by\n    clicking the arrow. \n\n3.  Check the box labeled *read data on your behalf*. \n\n4.  Click *Save*. \n\n![Figure 1: Enable the scope to grant access to the service.](../../../images/jax-rs-oauth2-scope.png)\n\nFor simplicity, the examples below use [Curl](https://curl.haxx.se) to\nauthenticate. You need the two pieces of information generated for your\napplication: the Client ID and the Client Secret. For example, say those fields\ncontain these values: \n\n**Client ID:** `id-12e14a84-e558-35a7-cf9a-c64aafc7f` \n\n**Client Secret:** `secret-93f14320-dc39-d67f-9dec-97717b814f`\n\nFirst, you must request an OAuth token. If you're testing locally, you'd make\na request like this: \n\n```bash\ncurl http://localhost:8080/o/oauth2/token -d 'grant_type=client_credentials&client_id=id-12e14a84-e558-35a7-cf9a-c64aafc7f&client_secret=secret-93f14320-dc39-d67f-9dec-97717b814f'\n```\n\nThe response is JSON: \n\n```json\n{\"access_token\":\"a7f12bef7f2e578cf64bce4085db8f17b6a3c2963f865a65b374e89784bbca5\",\"token_type\":\"Bearer\",\"expires_in\":600,\"scope\":\"GET POST PUT\"}\n```\n\nIt contains a token, generated for this client. It expires in 600 seconds, and\nit grants GET, POST, and PUT for this web service. \n\nWhen you want to call the service, you must supply the token in the HTTP header,\nlike this: \n\n```bash\ncurl --header \"Authorization: Bearer a7f12bef7f2e578cf64bce4085db8f17b6a3c2963f865a65b374e89784bbca5\" http://localhost:8080/o/greetings/morning\n```\n\nWith authorization, your web service can be called and responds to the request: \n\n    Good morning!\n\nOf course, this is only one of the authorization flows for OAuth 2.0. If you're\ncreating a web-based client whose back-end is a JAX-RS web service hosted on\n@product@, you'd want one of the other flows. See the \n[OAuth 2.0 documentation](/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0)\nfor further information. Additionally, OAuth 2.0 assumes the use of HTTPS for\nits security: the above URLs are only for local testing purposes. You certainly\nwould not want to pass OAuth tokens between clients and servers in the clear.\nMake sure that in production your server uses HTTPS. \n\n#### OAuth2 Scopes\n\nWithout any special Liferay OAuth2 annotations or properties, a standard OSGi\nJAX-RS application is inspected by the Liferay OAuth2 runtime, and scopes are\nderived by default based on the HTTP verbs supported by the application.\n\nWhen developers want more control, they can use the property\n`oauth2.scopechecker.type=annotations` and the annotation\n`com.liferay.oauth2.provider.scope.RequiresScope` exported from the `Liferay\nOAuth2 Provider Scope API` bundle to annotate endpoint resource methods or whole\nclasses like this:\n\n```java\n@RequiresScope(\"scopeName\")\n```\n\nOnce deployed, this becomes a scope in the \n[OAuth 2.0 configuration](/docs/7-2/deploy/-/knowledge_base/d/oauth2-scopes). \nYou can disable scope checking (not recommended) by setting the scope checker to\na non-existent type: \n\n```properties\noauth2.scope.checker.type=none\n```\n#### Requiring OAuth2\n\nYou can specify OAuth2 authorization as required for your JAX-RS application by\nusing this property: \n\n```properties\nosgi.jaxrs.extension.select=(osgi.jaxrs.name=Liferay.OAuth2)\n```\n\n### JAX-RS and Service Access Policies\n\nWhen authenticating via Basic Auth, the \n[Service Access Policy](/docs/7-2/deploy/-/knowledge_base/d/service-access-policies)\n`SYSTEM_USER_PASSWORD` is enforced. When authenticating via OAuth 2.0, the\n`AUTHORIZED_OAUTH2_SAP` policy is enforced. Configure them appropriately for\nyour environment, as by default, they allow invoking all remote services. To\ndisable Service Access Policy enforcement for JAX-RS endpoints (not recommended),\nset this property: \n\n```properties\nliferay.access.control.disable=true\n```\n\nWith this configured, guests can call these endpoints without administrators\nhaving to define a default Service Access Policy.\n\n## Public JAX-RS Services\n\nTo create a public endpoint for development purposes, all you must do is set two\nproperties: \n\n```java\n@Component(\n    property={\n        \"auth.verifier.guest.allowed=true\",\n        \"liferay.access.control.disable=true\"\n    },\n    service = Application.class\n)\n```\n\nDon't keep this configuration for production. For public services, it's best to\nleave the security in place and whitelist the particular endpoints you're making\npublic. See [Service Access Policies](/docs/7-2/deploy/-/knowledge_base/d/service-access-policies)\nfor further information. \n\n## Using JAX-RS with CORS\n\nIf you foresee that JavaScript in a browser might access your JAX-RS web service\nfrom a different domain, you might want to use the CORS annotation. You can use\nthe `@CORS` annotation to define \n[CORS policies](/docs/7-2/deploy/-/knowledge_base/d/configuring-cors) on your deployed\nJAX-RS applications. Note that the annotations \n[can be overridden by an administrator](/docs/7-2/deploy/-/knowledge_base/d/configuring-cors). \nIt only takes three steps: \n\n1.  Add the Portal Remote CORS API dependency to your module: \n\n```groovy\ncompileOnly project(\":apps:portal-remote:portal-remote-cors-api\")\n```\n\n2.  Activate the CORS annotation feature in your application properties: \n\n```java\n@Component(\n    property = {\n        \"osgi.jaxrs.application.base=/my-application\",\n        \"osgi.jaxrs.name=My.Application.Name\",\n        \"liferay.cors.annotation=true\"\n    },\n    service = Application.class\n    )\n    public class MyApplication extends Application {\n    ...\n    }\n```\n\n3.  Use the `@CORS` annotation throughout your application globally or by\n    method. \n\nGlobally: \n\n```java\n@Component(\n\tproperty = {\n\t\t\"osgi.jaxrs.application.base=/my-application\",\n\t\t\"osgi.jaxrs.name=My.Application.Name\",\n\t\t\"liferay.cors.annotation=true\"\n\t},\n\tservice = Application.class\n)\n@CORS(allowMethods=\"GET\")\npublic class MyApplication extends Application {\n...\n}\n```\n\nBy method: \n\n```java\n@CORS\n\t@GET\n\t@Path(\"/users\")\n\tpublic List<User> getUserList() throws Exception {\n\t\treturn _users;\n\t}\n```\n\nYou can use the annotation to provide a configuration for any of the CORS\nheaders. Here are some examples: \n\n|      Header      |    Annotation Example    | \n|------------------|--------------------------|\n|Access-Control-Allow-Credentials|`@CORS(allowCredentials = false)`|\n|Access-Control-Allow-Headers|`@CORS(allowHeaders = \"X-PINGOTHER\")`|\n|Access-Control-Allow-Methods|`@CORS(allowMethods = \"OPTIONS,POST\")`|\n|Access-Control-Allow-Origin|`@CORS(allowOrigin = \"http://www.liferay.com\")`|\n\nIf for some reason you want to disable the `@CORS` annotations in your\napplication, you can do it globally by disabling it in your `@Component`\nannotation: \n\n```java\n@Component(\n\tproperty = {\n\t\t\"osgi.jaxrs.application.base=/no-cors-application\",\n\t\t\"osgi.jaxrs.name=NoCors.Application.Name\",\n\t\t\"liferay.cors.annotation=false\"\n\t},\n\tservice = Application.class\n)\n```\n\nGreat! Now you know how to create, deploy, and invoke JAX-RS web services on\n@product@'s platform! \n\n## Related Topics\n\n[REST Builder](/docs/7-2/appdev/-/knowledge_base/a/rest-builder)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/03-jax/02-jax-ws.markdown",
    "content": "---\nheader-id: jax-ws\n---\n\n# JAX-WS\n\n[TOC levels=1-4]\n\nLiferay supports\n[JAX-WS](https://en.wikipedia.org/wiki/Java_API_for_XML_Web_Services)\nvia the [Apache CXF](http://cxf.apache.org/) implementation. Apps can publish\nJAX-WS web services to the CXF endpoints defined in your Liferay instance. CXF\nendpoints are effectively context paths the JAX-WS web services are deployed to\nand accessible from. To publish any kind of JAX-WS web service, one or more CXF\nendpoints must be defined. To access JAX-WS web services, an *extender* must\nalso be configured in your Liferay instance. Extenders specify where the\nservices are deployed and whether they are augmented with handlers, providers,\nand so on.\n\n**SOAP Extenders:** Required to publish JAX-WS web services. Each SOAP extender\ncan deploy the services to one or more CXF endpoints and can use a set of\n[JAX-WS handlers](https://jax-ws.java.net/articles/handlers_introduction.html)\nto augment the services.\n\nSOAP extenders are subsystems that track the services the app developer\nregisters in OSGi (those matching the provided\n[OSGi filters](https://osgi.org/javadoc/r6/core/org/osgi/framework/Filter.html)),\nand deploy them under the specified CXF endpoints. For example, if you create\nthe CXF endpoint `/soap`, you could later create a SOAP extender for `/soap`\nthat publishes SOAP services. Of course, this is only a rough example: you can\nfine tune things to your liking.\n\nCXF endpoints and extenders can be created programmatically or with Liferay's\nControl Panel. This tutorial shows you how to do both, and then shows you how to\npublish JAX-WS web services. The following topics are covered:\n\n- [Configuring Endpoints and Extenders with the Control Panel](/docs/7-2/frameworks/-/knowledge_base/f/jax-ws#configuring-endpoints-and-extenders-with-the-control-panel)\n\n- [Configuring Endpoints and Extenders Programmatically](/docs/7-2/frameworks/-/knowledge_base/f/jax-ws#configuring-endpoints-and-extenders-programmatically)\n\n- [Publishing JAX-WS Web Services](/docs/7-2/frameworks/-/knowledge_base/f/jax-ws#publishing-jax-ws-web-services)\n\n## Configuring Endpoints and Extenders with the Control Panel\n\nLiferay's Control Panel lets administrators configure endpoints and extenders\nfor JAX-WS web services. Note that you must be an administrator in your Liferay\ninstance to access the settings here. First, you'll learn how to create CXF\nendpoints.\n\nTo configure a CXF endpoint with the Control Panel, first go to *Control Panel*\n&rarr; *Configuration* &rarr; *System Settings* &rarr; *Web API*. Then select\n*CXF Endpoints* from the list. If there are any existing CXF endpoints, they're\nshown here. To add a new one, click the *Add* button. The form that appears\nlets you configure a new CXF endpoint by filling out these fields:\n\n**Context Path:** The path the JAX-WS web services are deployed to on the\nLiferay server. For example, if you define the context path `/web-services`, any\nservices deployed there are available at\n`http://your-server:your-port/o/web-services`.\n\n**`AuthVerifier` properties:** Any properties defined here are passed as-is to\nthe `AuthVerifier` filter. See the\n[`AuthVerifier` documentation](/docs/7-2/deploy/-/knowledge_base/d/authentication-verifiers)\nfor more details.\n\n**Required Extensions:** CXF normally loads its default extension classes, but\nin some cases you can override them to replace the default behavior. In most\ncases, you can leave this field blank: overriding extensions isn't common. By\nspecifying custom extensions here via [OSGi filters](https://osgi.org/javadoc/r6/core/org/osgi/framework/Filter.html),\nLiferay waits until those extensions are registered in the OSGi framework\nbefore creating the CXF servlet and passing the extensions to the servlet.\n\n![Figure 1: Fill out this form to create a CXF endpoint.](../../../images/cxf-endpoint-form.png)\n\nFor an app to deploy JAX-WS web services, you must configure a SOAP extender.\nTo configure a SOAP extender with the Control Panel, first go to *Control\nPanel* &rarr; *Configuration* &rarr; *System Settings* &rarr; *Web API*. Then\nselect *SOAP Extenders* from the list. If there are any existing SOAP\nextenders, they're shown here. To add a new one, click on the *Add* button. The\nform that appears lets you configure a new SOAP extender by filling out these\nfields:\n\n**Context paths:** Specify at least one CXF endpoint here. This is where the\nservices affected by this extender are deployed. In the preceding CXF endpoint\nexample, this would be `/web-services`. Note that you can specify more than\none CXF endpoint here.\n\n**jax.ws.handler.filters:** Here you can specify a set of\n[OSGi filters](https://osgi.org/javadoc/r6/core/org/osgi/framework/Filter.html)\nthat select certain services registered in the OSGi framework. The selected\nservices should implement JAX-WS handlers and augment the JAX-WS services\nspecified in the *jax.ws.service.filters* property. These JAX-WS handlers\napply to each service selected in this extender.\n\n**jax.ws.service.filters:** Here you can specify a set of OSGi filters that\nselect the services registered in the OSGi framework that are deployed to the\nCXF endpoints. These OSGi services must be\n[proper JAX-WS services](https://docs.oracle.com/javaee/7/tutorial/jaxws001.htm).\n\n**soap.descriptor.builder:** Leave this option empty to use JAX-WS annotations\nto describe the SOAP service. To use a different way to describe the SOAP\nservice, you can provide an OSGi filter here that selects an implementation of\n`com.liferay.portal.remote.soap.extender.SoapDescriptorBuilder`.\n\n![Figure 2: Fill out this form to create a SOAP extender.](../../../images/soap-extenders-form.png)\n\nNext, you'll learn how to create endpoints and extenders programmatically.\n\n## Configuring Endpoints and Extenders Programmatically\n\nTo configure endpoints or extenders programmatically, you must use Liferay's\nconfigurator extender. The configurator extender provides a way for OSGi modules\nto deploy default configuration values. Modules that use the configurator\nextender must provide a `ConfigurationPath` header that points to the\nconfiguration files' location inside the module. For example, the following\nconfiguration sets the `ConfigurationPath` to\n`src/main/resources/configuration`:\n\n```properties\nBundle-Name: Liferay Export Import Service JAX-WS\nBundle-SymbolicName: com.liferay.exportimport.service.jaxws\nBundle-Version: 1.0.0\nLiferay-Configuration-Path: /configuration\nInclude-Resource: configuration=src/main/resources/configuration\nLiferay-Releng-Module-Group-Description:\nLiferay-Releng-Module-Group-Title: Data Management\n```\n\nNote that Liferay-specific Bnd instructions are prefixed with `Liferay` to\navoid conflicts.\n\nThere are two different configuration types in\n[OSGi's `ConfigurationAdmin`](https://osgi.org/javadoc/r4v42/org/osgi/service/cm/ConfigurationAdmin.html):\nsingle, and factory. Factory configurations can have several configuration\ninstances per factory name. @product@ uses factory configurations. You\nmust provide a factory configuration's default values in a `*.properties` file.\nIn this properties file, use a suffix on the end of the PID (persistent\nidentifier) and then provide your settings. For example, the following code uses\nthe `-staging` suffix on the PID and creates a CXF endpoint at the context path\n`/staging-ws`:\n\n`com.liferay.portal.remote.cxf.common.configuration.CXFEndpointPublisherConfiguration-staging.properties`:\n\n```properties\ncontextPath=/staging-ws\n```\n\nAs another example, the following code uses the suffix `-stagingjaxws` on the\nPID and creates a SOAP extender at the context path `/staging-ws`. This code\nalso includes settings for the configuration fields `jaxWsHandlerFilterStrings`\nand `jaxWsServiceFilterStrings`:\n\n`com.liferay.portal.remote.soap.extender.internal.configuration.SoapExtenderConfiguration-stagingjaxws.properties`:\n\n```properties\ncontextPaths=/staging-ws\njaxWsHandlerFilterStrings=(staging.jax.ws.handler=true)\njaxWsServiceFilterStrings=(staging.jax.ws.service=true)\n```\n\nYou must then use these configuration fields in the configuration class. For\nexample, the `SoapExtenderConfiguration` interface below contains the\nconfiguration fields `contextPaths`, `jaxWsHandlerFilterStrings`, and\n`jaxWsServiceFilterStrings`:\n\n```java\n@ExtendedObjectClassDefinition(\n    category = \"foundation\", factoryInstanceLabelAttribute = \"contextPaths\"\n)\n@Meta.OCD(\n    factory = true,\n    id = \"com.liferay.portal.remote.soap.extender.internal.configuration.SoapExtenderConfiguration\",\n    localization = \"content/Language\", name = \"soap.extender.internal.configuration.name\"\n)\npublic interface SoapExtenderConfiguration {\n\n    @Meta.AD(required = false)\n    public String[] contextPaths();\n\n    @Meta.AD(name = \"jax.ws.handler.filters\", required = false)\n    public String[] jaxWsHandlerFilterStrings();\n\n    @Meta.AD(name = \"jax.ws.service.filters\", required = false)\n    public String[] jaxWsServiceFilterStrings();\n\n    @Meta.AD(name = \"soap.descriptor.builder\", required = false)\n    public String soapDescriptorBuilderFilter();\n\n}\n```\n\nNext, you'll learn how to publish JAX-WS web services.\n\n## Publishing JAX-WS Web Services\n\nTo publish JAX-WS web services via SOAP in a module, annotate the class and its\nmethods with standard JAX-WS annotations, and then register it as a service in\nthe OSGi framework. For example, the following class uses the `@WebService`\nannotation for the class and `@WebMethod` annotations for its methods. You must\nalso set the `jaxws` property to `true` in the OSGi `@Component` annotation:\n\n```java\nimport javax.jws.WebMethod;\nimport javax.jws.WebService;\n\nimport org.osgi.service.component.annotations.Component;\n\n@Component(\n    immediate = true, property = \"jaxws=true\", service = Calculator.class\n)\n@WebService\npublic class Calculator {\n\n    @WebMethod\n    public int divide(int a, int b) {\n        return a / b;\n    }\n\n    @WebMethod\n    public int multiply(int a, int b) {\n        return a * b;\n    }\n\n    @WebMethod\n    public int subtract(int a, int b) {\n        return a - b;\n    }\n\n    @WebMethod\n    public int sum(int a, int b) {\n        return a + b;\n    }\n\n}\n```\n\nYou should also make sure that you include `org.osgi.core` and\n`org.osgi.service.component.annotations` as dependencies to your project."
  },
  {
    "path": "en/developer/frameworks/articles/web-services/04-graphql/01-intro.markdown",
    "content": "---\nheader-id: graphql-apis\n---\n\n# GraphQL APIs\n\n[TOC levels=1-4]\n\n@product@ exposes a full \n[GraphQL API](https://graphql.github.io/graphql-spec/June2018/) implementation, and lets\nyour apps fetch several entities from the same request.\n\nHere you'll learn how to navigate and consume @product@'s GraphQL APIs. Since\nGraphQL APIs are discoverable, you'll start with that.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/04-graphql/02-discover-api.markdown",
    "content": "---\nheader-id: get-started-discover-the-api\n---\n\n# Get Started: Discover the API\n\n[TOC levels=1-4]\n\nTo begin consuming the GraphQL APIs, you must first know where they are, what\noperations you can invoke, and how to invoke them. \n\nBecause @product@'s GraphQL APIs leverage the \n[official specification](https://graphql.github.io/graphql-spec/June2018/), you don't need\na service catalog. You only need to know the URL from which to discover the rest\nof the API. \n\n@product@'s GraphQL APIs are available here: \n\n    http://[host]:[port]/o/graphql\n \nFor example, if you're running @product@ locally on port `8080`, the URL \nfor discovering the GraphQL API is \n\n    http://localhost:8080/o/graphql\n\nTo inspect the GraphQL endpoint, use a GraphQL client, such as\n[Altair](https://chrome.google.com/webstore/detail/altair-graphql-client/flnheeellpciglgpaodhkhmapeljopja?hl=en) \n(a Chrome extension) or [GraphiQL](https://github.com/graphql/graphiql). \n\n![Figure 1: GraphQL APIs can be browsed in Altair.](../../../images/graphql-altair.png)\n\nYou don't have to be authenticated to inspect the live documentation, but you\nmust to be able to make requests. There are several ways of authenticating in\nGraphQL APIs (explained [here](/docs/7-2/frameworks/-/knowledge_base/f/authenticated-requests)) \nbut the simplest way to test APIs locally is to use Basic Authentication,\nsetting an `Authorization` header in Altair (first icon on the left). Remember\nthat Basic Auth is a BASE64 transformation of `user`:`password`. This means it's\ninsecure, and should never be used in production. \n\nMost tools that introspect the GraphQL schema can autocomplete your query or\nfill all the fields in for you. \n\nFor a list of tools such as client generators,\nvalidators, and parsers supporting GraphQL, see [Awesome GraphQL](https://github.com/chentsulin/awesome-graphql). \nLeveraging GraphQL provides standards support, extensive automatic\ndocumentation, and industry-wide conventions. \n\n## Unique endpoint and versioning\n\nIn contrast with the REST APIs, where endpoints are deployed by suite\n(headless-delivery, headless-admin-user...), GraphQL APIs are deployed under\nthe same endpoint (/o/graphql). That way we can easily add relationships between\nentities to leverage GraphQL's powerful request characteristics.\n\n@product@'s GraphQL APIs also expose the latest published version of all\nentities available. If several versions of the same entity are deployed, only\nthe latest one is exposed under the `/o/graphql` endpoint (REST APIs use different\nendpoints for different versions). This strategy follows GraphQL standards to\navoid breaking versions by marking deprecated fields and always adding\nproperties to an entity.\n\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/04-graphql/03-invoke-service.markdown",
    "content": "---\nheader-id: get-started-invoke-a-service\n---\n\n# Get Started: Invoke a Service\n\n[TOC levels=1-4]\n\nOnce you know which API you want to call via the GraphQL-introspected\ndocumentation, you can send a request using a POST body. For example, suppose\nyou want to retrieve all the blog entries from a Site. If you consult the\nGraphQL documentation you can find this endpoint: \n\n![Figure 1: GraphQL exposes a definition for BlogPostings.](../../../images/graphql-blog-postings.png)\n\nIf you add the full query with Altair/GraphiQL, you'll see a result like this:\n\n```\nquery{\n  blogPostings(filter: ______, page: ______, pageSize: ______, search: ______, siteId: ______, siteKey: ______, sort: ______){\n    items\n    page\n    pageSize\n    totalCount\n  }\n}\n```\n\nThe only required parameter is `siteId` or `siteKey` (as of 7.2 FP4), the ID, or the internal\nname (like *guest*) of the blog posting's Site. Internally, the `siteId` is\na `groupId` that you can retrieve from the database, a URL, or @product@'s UI\nvia the Site Administration menu. For more information, see \n[How to get SiteId](/docs/7-2/frameworks/-/knowledge_base/f/how-to-get-site-id).\n\nA regular query would ignore optional parameters and return more elements of the\nlist, identified by the property `items`:\n\n```\nquery {\n  blogPostings(siteKey: \"guest\") {\n    items {\n      alternativeHeadline\n      articleBody\n      creator {\n        name\n      }\n      dateCreated\n      dateModified\n      datePublished\n      description\n      encodingFormat\n      friendlyUrlPath\n      headline\n      id\n      keywords\n      numberOfComments\n      relatedContents {\n        title\n      }\n      siteId\n      taxonomyCategoryIds\n      viewableBy\n    }\n    page\n    pageSize\n    totalCount\n  }\n}\n```\n\nIn GraphQL, you must list explicitly every field you want to return in the\nrequest. Complex objects, like `items`, `creator`, or `relatedContents` can\nnot be returned fully: you must specify at least one field (or none).\n\nTo execute the query, make a `POST` request with an `Authentication` header and\nthe query in a JSON object under the key `query`. Don't forget to escape\nstrings! \n\nThe following request gets the Site's blog postings by providing the site key\n(`guest`):\n\n```bash\ncurl -X \"POST\" \"http://localhost:8080/o/graphql\" \\\n     -H 'Content-Type: text/plain; charset=utf-8' \\\n     -u 'test@liferay.com:test' \\\n     -d $'{\n  \"query\": \"query { blogPostings(siteKey: \\\\\"guest\\\\\") { items { alternativeHeadline articleBody creator { id name } dateCreated dateModified datePublished description encodingFormat friendlyUrlPath headline id keywords numberOfComments relatedContents { title } siteId taxonomyCategoryIds viewableBy } page pageSize totalCount } }\"\n  }'\n```\n\nIf you send this request to a Site that contains some Blog entries, the\nresponse may look like this: \n\n```json\n{\n  \"data\": {\n    \"blogPostings\": {\n      \"items\": [\n        {\n          \"alternativeHeadline\": \"\",\n          \"articleBody\": \"<p>Content</p>\",\n          \"creator\": {\n            \"id\": 20124,\n            \"name\": \"Test Test\"\n          },\n          \"dateCreated\": \"2019-10-29T17:48:03Z\",\n          \"dateModified\": \"2019-10-29T17:48:03Z\",\n          \"datePublished\": \"2019-10-29T17:47:00Z\",\n          \"description\": \"\",\n          \"encodingFormat\": \"text/html\",\n          \"friendlyUrlPath\": \"title\",\n          \"headline\": \"Title\",\n          \"id\": 37644,\n          \"keywords\": [],\n          \"numberOfComments\": 0,\n          \"relatedContents\": [],\n          \"siteId\": 20118,\n          \"taxonomyCategoryIds\": null,\n          \"viewableBy\": null\n        }\n      ],\n      \"page\": 1,\n      \"pageSize\": 20,\n      \"totalCount\": 1\n    }\n  }\n}\n```\n\nThis response is a JSON object with information about the collection of blogs.\nThe attributes contain information about the resource (blogs, in this case).\nAlso, note that the results are paginated. The `*page*` attributes refer to\npages of results. Here's a description of some common attributes: \n\n`id`: Each item has an ID. You can use the ID to retrieve more information about\nthat item. For example, there are two `id` attributes in the above response: one\nfor the blog posting (`37644`) and one for the blog post's creator (`20124`). \n\n`page`: The current page's page number. The page in the above response is `1`. \n\n`pageSize`: The possible number of this resource's items to be included in\na single page. In the above response this is `20`. \n\n`totalCount`: The total number of this resource's existing items (independent of\npagination). The above response lists the total number of blog postings (`1`) in\na Site. \n\nTo get information on a specific blog posting, send a POST request with the\n`blogPostingId` to this query:\n\n```\nquery {\n  blogPosting(blogPostingId: 37644) {\n    headline\n    id\n  }\n}\n``` \n \n## GraphQL Clients\n\nThe examples above show the GraphQL requests as cURL operations but you can use\nany GraphQL clients available. We have made heavy use of\n[Apollo](https://www.apollographql.com/docs/), the official React client and the\n[Vue integration](https://github.com/vuejs/vue-apollo) without any issues.\n \n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/04-graphql/04-authenticated-requests.markdown",
    "content": "---\nheader-id: authenticated-requests\n---\n\n# Making Authenticated Requests\n\n[TOC levels=1-4]\n\nTo make an authenticated request, you must authenticate as a specific User. \n\nThere are three authentication mechanisms available when invoking web APIs: \n\n**Basic Authentication:** Sends the user credentials as an encoded user name and\npassword pair. This is the simplest authentication protocol (available since\nHTTP/1.0), but should be used only for development purposes, as it's insecure. \n\n**OAuth 2.0:** In @product-ver@, you can use OAuth 2.0 for authorization. See\nthe [OAuth 2.0 documentation](/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0) for\nmore information. \n\n**Cookie/Session authentication:** From inside the portal you can do direct\nrequests to the APIs by sending the session token.\n\nFirst, you'll learn how to send requests with basic authentication. \n\n## Basic Authentication\n\nBasic authentication requires that you send an HTTP `Authorization` header\ncontaining the encoded user name and password. You must first get that encoded\nvalue. To do so, you can use `openssl` or a `Base64` encoder. Either way, you\nmust encode the `user:password` string. Here's an example of the `openssl`\ncommand for encoding the `user:password` string for a user `test@liferay.com`\nwith the password `Liferay`: \n\n```bash\nopenssl base64 <<< test@liferay.com:Liferay\n```\n\nThis returns the encoded value: \n\n    dGVzdEBsaWZlcmF5LmNvbTpMaWZlcmF5Cg==\n\nIf you don't have `openssl` installed, try the `base64` command: \n\n```bash\nbase64 <<< test@liferay.com:Liferay\n```\n\n| **Warning:** Encoding a string as shown here does not encrypt the resulting \n| string. The encoded string can easily be decoded by executing \n| `base64 <<< the-encoded-string`, which returns the original string. \n| \n| Anyone listening to your request could therefore decode the `Authorization` \n| header and reveal your user name and password. To prevent this, ensure that \n| all communication is made through HTTPS, which encrypts the entire message \n| (including headers). \n\nUse the encoded value for the HTTP Authorization header when sending the\nrequest: \n\n```bash\ncurl -H \"Authorization: Basic dGVzdEBsaWZlcmF5LmNvbTpMaWZlcmF5Cg==\" http://localhost:8080/o/graphql ...\n```\n\nThe response contains data instead of the 403 error that an unauthenticated\nrequest receives. For more information on the response's structure, see \n[Working with Collections of Data](/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data). \n\n## OAuth 2.0 Authorization\n\n@product-ver@ supports authorization via OAuth 2.0, which is a token-based\nauthorization mechanism. For more details, see \n[@product@'s OAuth 2.0 documentation](/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0). The following\nsections show you how to use OAuth 2.0 to authenticate web API requests. \n\n### Obtaining the OAuth 2.0 Token\n\nBefore using OAuth 2.0 to invoke a web API, you must register your application\n(your web API's consumer) as an authorized OAuth client. To do this, follow the\ninstructions in [Creating an Application](/docs/7-2/deploy/-/knowledge_base/d/oauth-2-0#creating-an-application).\nWhen creating the application, fill in the form as follows: \n\n**Application Name:** Your application's name. \n\n**Client Profile:** Headless Server. \n\n**Allowed Authorization Types:** Check *Client Credentials*. \n\nAfter clicking *Save* to finish creating the application, write down the Client\nID and Client Secret values that appear at the top of the form. \n\nNext, you must get an OAuth 2.0 access token. To do this, see \n[Authorizing Account Access with OAuth 2](/docs/7-2/deploy/-/knowledge_base/d/authorizing-account-access-with-oauth2). \n\n### Invoking the Service with an OAuth 2.0 Token\n\nOnce you have a valid OAuth 2.0 token, include it in the request's\n`Authorization` header, specifying that the authentication type is a \n[bearer token](https://tools.ietf.org/html/rfc6750):\n\n```bash\ncurl -H \"Authorization: Bearer d5571ff781dc555415c478872f0755c773fa159\" http://localhost:8080/o/graphql\n```\n\nThe response contains the resources that the authenticated user has permission\nto access, just like the response from Basic authentication. The request could\nbe prevented depending on the scopes defined. If POST a GraphQL query\nand there is scope disabling all request except `GET`, you see a 403. \n\n## Using Cookie Authentication or doing a request from the portal\n\nYou can call the GraphQL APIs using the existing session from outside the\n@product@ by passing the session identifier (the cookie reference) and the\nLiferay Auth Token (a CSRF---Cross-Site Request Forgery---token).\n\nTo make an unauthenticated request from outside the @product@ you must\nprovide the `Cookie` identifier in the header: \n\n```bash\ncurl -H 'Cookie: JSESSIONID=27D7C95648D7CDBE3347601FC4543F5D'\n```\n\nYou must also provide the CSRF token by passing it as a query parameter\ncalled `p_auth` or by adding the URL to the whitelist of CSRF allowed URLs or disabling CSRF\nchecks altogether with the `auth.verifier.auth.verifier.PortalSessionAuthVerifier.check.csrf.token` property (application level).\n\nHere's a sample cURL request with the cookie and CSRF token:\n\n```bash\ncurl -H 'Cookie: JSESSIONID=27D7C95648D7CDBE3347601FC4543F5D' http://localhost:8080/o/graphql?p_auth=O4dCU1Mj\n```\n \nTo do an unauthenticated request from inside the @product@, from JavaScript code\nor a Java method, you don't need the session identifier. You must only provide\nthe CRSF token or add the API to the whitelist of CSRF allowed URLs.\n\n## Making Unauthenticated Requests\n\nUnauthenticated requests are disabled by default in @product@'s GraphQL APIs. As\nall GraphQL APIs share the same endpoint, you cannot have the same level of\ngranularity with Service Access Policies as in REST APIs. For that reason, we do\nnot recommend disabling the security of the GraphQL APIs.\n\n## Related Topics\n\n[Get Started: Invoke a Service](/docs/7-2/frameworks/-/knowledge_base/f/get-started-invoke-a-service)\n\n[Working with Collections of Data](/docs/7-2/frameworks/-/knowledge_base/f/working-with-collections-of-data)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/04-graphql/05-collections.markdown",
    "content": "---\nheader-id: working-with-collections-of-data\n---\n\n# Working with Collections of Data\n\n[TOC levels=1-4]\n\nCollection resources are common in @product@ web APIs. If you followed along\nwith the previous examples that sent requests to the portal's `blog-postings`\nresource URL, you've already seen collections in action: the `BlogPosting`\nresource is a collection. \n\nHere, you'll learn more detailed information about working with collection\nresources. But first, you should learn about collection pagination. \n\n## Pagination\n\nA small collection can be transmitted in a single response without difficulty.\nTransmitting a large collection all at once, however, can consume too much\nbandwidth, time, and memory. It can also overwhelm the user with too much data. \n\nIt's therefore best to get and display the elements of a large collection in\ndiscrete chunks, or pages. \n\n@product@'s GraphQL APIs return paginated collections by default. The following\nattributes in the responses also contain the information needed to navigate\nbetween those pages: \n\n`totalCount`: The total number of this resource's items. \n\n`pageSize`: The number of this resource's items to be included in this response. \n\n`page`: The current page's number. \n\n`items`: The collection elements present on this page. Each element also\ncontains the data of the object it represents, so there's no need for additional\nrequests for individual elements. \n\n`id`: Each item's identifier. You can use this, if necessary, to get more\ninformation on a specific item. \n\nThe attributes `page` and `pageSize` allow client applications to navigate\nthrough the results. For example, such a client could send a request for\na specific page. \n\nThis example gets the second page (`page:2`) of blog postings that exist on the\ncontent set with the ID `42345`: \n\n```\nquery {\n  contentSetContentSetElements(contentSetId: 42345, page: 2) {\n    items {\n      id\n      title\n      content {\n        ... on BlogPosting {\n          headline\n        }\n      }\n    }\n    page\n    pageSize\n    totalCount\n  }\n}\n```\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/04-graphql/06-mutations.markdown",
    "content": "---\nheader-id: mutations\n---\n\n# Mutations\n\n[TOC levels=1-4]\n\nThe GraphQL spec differentiates between retrieve operations (`query`) and\ncreate/update/delete operations (`mutations`). \n\n![Figure 1: The GraphQL Mutations list for Blog postings shows the possible operations.](../../../images/graphql-mutation.png)\n\nTo perform a mutation, do a `POST` request as you did with `query` operations,\nusing cURL, a REST client, or a GraphQL Client like Apollo. The only\ndifference is that create/update `mutations` require an Input type, a JSON\nobject to create or update the content.\n\nA create `mutation` to insert a new blog posting looks like this:\n\n```\nmutation {\n  createSiteBlogPosting(\n    blogPosting: {\n      headline: \"New GraphQL APIs!\"\n      articleBody: \"WoW! This is cool!\"\n    }\n    siteKey: \"guest\"\n  ) {\n    id\n    headline\n  }\n}\n```\n\nAuto-complete also works as expected filling the `blogPosting` object. Here's\na cURL request to create the same entry:\n\n```bash\ncurl 'http://localhost:8080/o/graphql' -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic dGVzdEBsaWZlcmF5LmNvbTp0ZXN0' --data-binary '{\"query\":\"mutation {createSiteBlogPosting(blogPosting: {headline: \\\"New GraphQL APIs!\\\" articleBody: \\\"WoW! This is cool!\\\"} siteKey: \\\"guest\\\") {id headline}}\",\"variables\":{}}'\n```\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/04-graphql/07-fragments-and-node-pattern.markdown",
    "content": "---\nheader-id: fragments-and-node-pattern\n---\n\n# Fragments and Node Patterns\n\n[TOC levels=1-4]\n\n@product@'s GraphQL APIs also supports [GraphQL\nfragments](https://graphql.org/learn/queries/#fragments), reusable sets of\nfields that are needed in different requests. A special type of fragments are\n[inline fragments](https://graphql.org/learn/queries/#inline-fragments), which\naccess the underlying concrete type when querying generic types or interfaces.\n\nYou'll use inline fragments to query objects that inherit from a common\ninterface, like the kind of objects returned from a `ContentSet`. `ContentSet`s\nallow defining lists of assets that comply with a set of rules, a segment, or are\nmanually selected. `ContentSet`s can return any type of asset. This makes them\na perfect fit for inline fragments.\n\nHere's an example of GraphQL querying `ContentSet`s: \n\n```\nquery {\n  contentSetContentSetElements(contentSetId: 42345) {\n    items {\n      id\n      title\n      content {\n        ... on BlogPosting {\n          headline\n        }\n        ... on StructuredContent {\n          relatedContents {\n            id\n            title\n          }\n        }\n      }\n    }\n    page\n    pageSize\n    totalCount\n  }\n}\n```\n\nThis query returns a set of objects, each of a different type.\n\n## Node pattern\n\n`graphQLNode` is a special query that leverages the power of inline fragments.\nThis query accepts a `dataType` and an ID and returns any kind of entity that\nhas a query of the type `{dataType}` and receives an `id` as a parameter. Inline\nfragments can specify the fields you want to return in this special case:\n\n```\nquery{\n  graphQLNode(dataType: ______, id: ______){\n    id\n    ... on BlogPosting {\n      headline\n    }\n  }\n}\n``` \n\nYou can also use `graphQLNode` as a field in entities that contain `contentType`\nand `id` properties. Those entities have a generated property called `GraphQLNode`\nthat can return any type, queried by using inline fragments. A common use case\nis returning an asset linked as `relatedContents` (asset links).\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/04-graphql/08-language-negociation.markdown",
    "content": "---\nheader-id: language-negotiation\n---\n\n# Language Negotiation\n\n[TOC levels=1-4]\n\nThe same mechanism for requesting content in another language in the\nheadless REST APIs is used in GraphQL. \n\nAPIs available in different languages return the options in a block called\n`availableLanguages`. For example, this block lists U.S. English (`en-US`) and\nSpain/Castilian Spanish (`es-ES`): \n\n```json\n{\n  \"availableLanguages\": [\n    \"en-US\",\n    \"es-ES\"\n  ],\n  \"contentFields\": [\n    {\n      \"dataType\": \"html\",\n      \"name\": \"content\",\n      \"repeatable\": false,\n      \"value\": {\n        \"data\": \"<p>The main reason is because Headless APIs have been designed with real use cases in mind...</p>\"\n      }\n    }\n  ],\n  \"contentStructureId\": 36801,\n  \"creator\": {\n    \"familyName\": \"Test\",\n    \"givenName\": \"Test\",\n    \"id\": 20130,\n    \"name\": \"Test Test\",\n    \"profileURL\": \"/web/test\"\n  },\n  \"dateCreated\": \"2019-04-22T10:29:40Z\",\n  \"dateModified\": \"2019-04-22T10:30:31Z\",\n  \"datePublished\": \"2019-04-22T10:28:00Z\",\n  \"friendlyUrlPath\": \"why-headless-apis-are-better-than-json-ws-services-\",\n  \"id\": 59325,\n  \"key\": \"59323\",\n  \"numberOfComments\": 0,\n  \"renderedContents\": [\n    {\n      \"renderedContentURL\": \"http://localhost:8080/o/headless-delivery/v1.0/structured-contents/59325/rendered-content/36804\",\n      \"templateName\": \"Basic Web Content\"\n    }\n  ],\n  \"siteId\": 20124,\n  \"title\": \"Why Headless APIs are better than JSON-WS services?\",\n  \"uuid\": \"e1c4c152-e47c-313f-2d16-2ee4eba5cd26\"\n}\n```\n\nTo request the content in another language, specify your desired locale in the\nrequest's `Accept-Language` header: \n\n```bash\ncurl \"http://localhost:8080/o/graphql\"  -H 'Accept-Language: es-ES'  -u 'test@liferay.com:test' ...\n```\n\n```json\n    {\n      \"availableLanguages\": [\n        \"en-US\",\n        \"es-ES\"\n      ],\n      \"contentFields\": [\n        {\n          \"dataType\": \"html\",\n          \"name\": \"content\",\n          \"repeatable\": false,\n          \"value\": {\n            \"data\": \"<p>La principal razón es porque las APIs Headless se han diseñado pensando en casos de uso reales...</p>\"\n          }\n        }\n      ],\n      \"contentStructureId\": 36801,\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T10:29:40Z\",\n      \"dateModified\": \"2019-04-22T10:30:31Z\",\n      \"datePublished\": \"2019-04-22T10:28:00Z\",\n      \"friendlyUrlPath\": \"%C2%BFpor-qu%C3%A9-las-apis-headless-son-mejores-que-json-ws-\",\n      \"id\": 59325,\n      \"key\": \"59323\",\n      \"numberOfComments\": 0,\n      \"renderedContents\": [\n        {\n          \"renderedContentURL\": \"http://localhost:8080/o/headless-delivery/v1.0/structured-contents/59325/rendered-content/36804\",\n          \"templateName\": \"Contenido web básico\"\n        }\n      ],\n      \"siteId\": 20124,\n      \"title\": \"¿Por qué las APIs Headless son mejores que JSON-WS?\",\n      \"uuid\": \"e1c4c152-e47c-313f-2d16-2ee4eba5cd26\"\n    }\n```\n\n## Creating Content with Different Languages\n\nBy default, when sending a mutation request, the `Accept-Language` header is\nused as the content's language. There is one exception, however. Some entities\nrequire the first request to be in the Site's default language. In such cases,\nthe first request for a different language results in an error. \n\nAfter creating a new resource, a new request in a different language adds that\ntranslation.\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/04-graphql/09-filter-and-sort.markdown",
    "content": "---\nheader-id: graphql-filter-sort-and-search\n---\n\n# Filter, Sort, and Search\n\n[TOC levels=1-4]\n\nYou can use @product@'s headless GraphQL APIs to search for the content you\nwant. You can also sort and filter content. \n\n## Filter\n\nIt's often useful to filter large collections for the exact data that you need.\nNot all collections, however, allow filtering. The ones that support it contain\nthe optional parameter `filter`. To filter a collection based on the value of\none or more fields, use the `filter` parameter following a subset of the\n[OData](https://www.odata.org) standard. \n\nFiltering mainly applies to fields indexed as keywords in @product@'s search. To\nfind content by terms contained in fields indexed as text, you should instead\nuse [search](#search). \n\n### Comparison Operators\n\n| Operator     | Description           | Example                               |\n|------------- |---------------------- | ------------------------------------- |\n| `eq`         | Equal                 | `addressLocality eq 'Redmond'`        |\n|              | Equal null            | `addressLocality eq null`             |\n| `ne`         | Not equal             | `addressLocality ne 'London'`         |\n|              | Not null              | `addressLocality ne null`             |\n| `gt`         | Greater than          | `price gt 20`                         |\n| `ge`         | Greater than or equal | `price ge 10`                         |\n| `lt`         | Less than             | `dateCreated lt 2018-02-13T12:33:12Z` |\n| `le`         | Less than or equal    | `dateCreated le 2012-05-29T09:13:28Z` |\n| `startswith` | Starts with           | `startswith(addressLocality, 'Lond')`   |\n\n### Logical Operators\n\n| Operator     | Description  | Example                          |\n| ------------ | ------------ | -------------------------------- |\n| `and`        | Logical and  | `price le 200 and price gt 3.5`  |\n| `or`         | Logical or   | `price le 3.5 or price gt 200`   |\n| `not`        | Logical not  | `not (price le 3.5)`             |\n\nNote that the `not` operator requires a trailing space. \n\n### Grouping Operators\n\n| Operator  | Description         | Example                                         |\n|---------- | ------------------- | ----------------------------------------------- |\n| `( )`     | Precedence grouping | `(price eq 5) or (addressLocality eq 'London')` |\n\n### String Functions\n\n| Function    | Description | Example                   |\n| ----------- | ----------- | ------------------------- |\n| `contains`  | Contains    | `contains(title,'edmon')` |\n\n### Lambda Operators\n\nLambda operators evaluate a boolean expression on a collection. They must be\nprepended with a navigation path that identifies a collection. \n\n| Lambda Operator | Description | Example                                    |\n| --------------- | ----------- | ------------------------------------------ |\n| `any`           | Any         | `keywords/any(k:contains(k,'substring1'))` |\n\nThe `any` operator applies a boolean expression to each collection element and\nevaluates to `true` if the expression is true for any element. \n\n### Escaping in Queries\n\nYou can escape a single quote in a value by escaping it with a backslash. For\nexample, to filter for a blog posting whose headline is `New Headless APIs`,\nsend this filter string to the filter parameter.\n\n```\nfilter: \\\\\"headline eq \\'Title\\'\\\\\"\n```\n\nHere's an example of the full request: \n\n```bash\ncurl -X \"POST\" \"http://localhost:8080/o/graphql\" \\\n     -H 'Content-Type: text/plain; charset=utf-8' \\\n     -H 'Cookie: COOKIE_SUPPORT=true; GUEST_LANGUAGE_ID=en_US; JSESSIONID=EFEEC1617529C7C85E8CCCE510B0F6CF' \\\n     -u 'test@liferay.com:test' \\\n     -d $'{\n  \"query\": \"query { blogPostings(siteKey: \\\\\"guest\\\\\", filter: \\\\\"headline eq \\'Title\\'\\\\\") { items {headline} page pageSize totalCount } }\"\n}'\n\n```\n\nAnd here's a possible response:\n\n```json\n{\n  \"items\": [\n    {\n      \"alternativeHeadline\": \"The power of OpenAPI & Liferay\",\n      \"articleBody\": \"<p>We are happy to announce...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T07:04:47Z\",\n      \"dateModified\": \"2019-04-22T07:04:51Z\",\n      \"datePublished\": \"2019-04-22T07:02:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"new-headless-apis\",\n      \"headline\": \"New Headless APIs\",\n      \"id\": 59301,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 1\n}\n```\n\n### Filtering in Structured Content Fields (ContentField)\n\nTo filter for a `ContentField` value (dynamic values created by the end user),\nyou must use the paths that are scoped to an individual `ContentStructure`. \n\nFind the ID of the `ContentStructure` and use it in place of\n`{contentStructureId}` in this query: \n\n```\n\"contentStructureStructuredContents\"\n```\n\n## Search\n\nYou can search large collections with keywords. Use search when you want results\nfrom any field, rather than specific ones. To perform a search, use the optional\nparameter `search` followed by the search terms. For example, this request\nsearches for all the `BlogEntry` fields containing Title: \n\n```bash\ncurl -X \"POST\" \"http://localhost:8080/o/graphql\" \\\n     -H 'Content-Type: text/plain; charset=utf-8' \\\n     -u 'test@liferay.com:test' \\\n     -d $'{\n  \"query\": \"query { blogPostings(siteKey: \\\\\"guest\\\\\", search: \\\\\"Title\\\\\") { items {headline} page pageSize totalCount } }\"\n}'\n```\n\n```json\n{\n  \"items\": [\n    {\n      \"alternativeHeadline\": \"How to work with OAuth\",\n      \"articleBody\": \"<p>To configure OAuth...</p>\",\n      \"creator\": {\n        \"familyName\": \"Test\",\n        \"givenName\": \"Test\",\n        \"id\": 20130,\n        \"name\": \"Test Test\",\n        \"profileURL\": \"/web/test\"\n      },\n      \"dateCreated\": \"2019-04-22T09:35:09Z\",\n      \"dateModified\": \"2019-04-22T09:35:09Z\",\n      \"datePublished\": \"2019-04-22T09:34:00Z\",\n      \"encodingFormat\": \"text/html\",\n      \"friendlyUrlPath\": \"authenticated-requests\",\n      \"headline\": \"Authenticated requests\",\n      \"id\": 59309,\n      \"numberOfComments\": 0,\n      \"siteId\": 20124\n    }\n  ],\n  \"page\": 1,\n  \"pageSize\": 20,\n  \"totalCount\": 1\n}\n```\n\n## Sorting\n\nCollection results can be sorted. Note, however, that not all collections allow\nsorting. The ones that support it contain the optional parameter `{lb}?sort{rb}`\nin their GraphQL definition.\n\nTo get sorted collection results, append `sort:\\\"<param-name>\\\"` to the request\nURL. For example, appending `sort:\\\"title\\\"` to the request URL sorts the\nresults by title. \n\nThe default sort order is ascending (0-1, A-Z). To perform a descending sort,\nappend `:desc` to the parameter name. For example, to perform a descending sort\nby title, append `sort:\\\"title:desc\\\"` to the request URL. \n\nTo sort by more than one parameter, separate the parameter names by commas and\nput them in order of priority. For example, to sort first by title and then by\ncreation date, append `sort:\\\"title,dateCreated\\\"` to the request URL. \n\nTo specify a descending sort for only one parameter, you must explicitly specify\nascending sort order (`:asc`) for the other parameters: \n\n```\nsort:\\\"headline:desc,dateCreated:asc\\\"\n```\n\n## Flatten\n\nThe `flatten` query parameter returns all resources and disregards folders or other\nhierarchical classifications. Collection GraphQL specifications define if\n`flatten` is available. Its default value is `false`, so a document query to the\nroot folder returns only the documents in that folder. \n\nWith `flatten` set to `true`, the same query returns documents in any\nsubfolders, regardless of how deeply those folders are nested. Setting `flatten`\nset to `true` and querying for documents in a Site's root folder returns all the\ndocuments in the Site. \n\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/04-graphql/10-multipart.markdown",
    "content": "---\nheader-id: graphql-multipart-requests\n---\n\n# Multipart Requests\n\n[TOC levels=1-4]\n\nSeveral mutations accept a binary file via a multipart request. For example, the\ndefinition for posting a file to a `DocumentFolder` specifies a multipart\nrequest, `Upload` type in GraphQL:\n\n![Figure 1: Create Document accepts a `multipartBody`.](../../../images/graphql-mutation-upload.png)\n\nThe GraphQL specification doesn't support natively multipart uploads, but an \n[extension](https://github.com/jaydenseric/graphql-multipart-request-spec)\ncontributed by the community covers that use case. \n\nLiferay's implementation includes that extension and allows uploading files.\n\nMultipart support in GraphQL is disabled by default. To enable it, add the\nconfiguration to upload multipart files in the Liferay application's `web.xml`\nfile:\n\n```xml\n<servlet>\n        <servlet-name>Module Framework Servlet</servlet-name>\n        <servlet-class>\n            com.liferay.portal.module.framework.ModuleFrameworkServletAdapter\n        </servlet-class>\n        <load-on-startup>1</load-on-startup>\n        <async-supported>true</async-supported>\n        <multipart-config>\n            <location>/tmp</location>\n            <max-file-size>20848820</max-file-size>\n            <max-request-size>418018841</max-request-size>\n            <file-size-threshold>1048576</file-size-threshold>\n        </multipart-config>\n</servlet>\n```\n\nTo test, use the Altair configuration to upload files. Use the selector to\nupload one or multiple files and define a variable in the query.\n\n![Figure 2: Creating a Document in Altair is easy with the selector.](../../../images/graphql-mutation-upload-altair.png)\n\n```\nmutation($file: [Upload]) {\n  createSiteDocument(multipartBody: $file, siteKey: \"guest\") {\n    id\n    title\n  }\n}\n```\n\nThe variable above is `file` because there's only one. If you wanted to upload\nseveral files, name the variable `$files` and each file should have\na numeric sequence: `files.0`, `files.1`, `files.2`, etc.\n\nAll multipart APIs allow sending a JSON file containing the file's metadata\n(title, description, etc.). That parameter should be the second file uploaded\n(defined using the `file.0`, `file.1` syntax). \n\nFor example, \n\n```\ndocument={\\\"title\\\": \\\"Alternative name\\\"}\"\n```\n\nAnd here's the response: \n\n```json\n{\n  \"data\": {\n    \"createSiteDocument\": {\n      \"id\": 37701,\n      \"title\": \"99-rest-generator.markdown\"\n    }\n  }\n}\n```\n\nThe cURL request is slightly different (Altair fills out the variables):\n\n```bash\ncurl 'http://localhost:8080/o/graphql' -H 'Authorization: Basic dGVzdEBsaWZlcmF5LmNvbTp0ZXN0' \\\n-F operations='{\"query\":\"mutation($files: [Upload]) {createSiteDocument(multipartBody: $files, siteId: 20122) {id}}\",\"variables\": { \"files\": [null] } }' \\\n-F map='{ \"0\": [\"variables.files.0\"]}' \\\n-F 0=@\"99-rest-generator.markdown\"\n```\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/04-graphql/11-using-graphql-apis.markdown",
    "content": "---\nheader-id: using-graphql-apis\n---\n\n# Using GraphQL APIs\n\n[TOC levels=1-4]\n\n@product@'s GraphQL APIs are independent of clients and can be used with any\nGraphQL client you want. The only usual requirements are setting up the\n`Authentication` header using OAuth, Cookie, Basic, etc.\n\nFor JavaScript applications, we recommend using \n[Apollo Client](https://www.apollographql.com/) or\n[graphql-request](https://github.com/prisma-labs/graphql-request), like this:\n\n```javascript\nconst { GraphQLClient } = require('graphql-request');\n\nconst graphQLClient = new GraphQLClient('http://localhost:8080/o/graphql', {\n    headers: {\n        authorization: `Basic ${AUTHORIZATION_TOKEN}`\n    }\n});\n\nconst getDestinationsQuery = ` {\n  destinations: contentSetContentSetElements(contentSetId: ${DESTINATION_CONTENTSET_ID}) {\n    items {\n        id\n        title\n    }\n    page\n    pageSize\n    totalCount\n  }\n}`;\n\n...\n\nconst response = await graphQLClient.request(getDestinationsQuery);\n```\n\nHere are several examples of JavaScript applications using GraphQL APIs:\n\n* [Alexa skill using GraphQL APIs](https://github.com/dgomezg/liferay-frontend-samples/blob/master/lifeair-alexa-skill/) \n* [React application using Apollo Client for React](https://github.com/dgomezg/liferay-frontend-samples/tree/master/lifeair-react-showroom)\n* [Vue application using Apollo Client for Vue](https://github.com/dgomezg/liferay-frontend-samples/tree/master/lifeair-vue-showroom)\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/05-rest-builder/01-intro.markdown",
    "content": "---\nheader-id: rest-builder\n---\n\n# REST Builder\n\n[TOC levels=1-4]\n\nREST Builder is @product@'s tool to build REST and GraphQL APIs. It's based\non OpenAPI, following an\n[API-first approach](/docs/7-2/frameworks/-/knowledge_base/f/headless-rest-apis).\n\nUsing REST Builder takes only three steps:\n\n1. Write the OpenAPI profile.\n\n2. Use REST builder to generate the scaffolding.\n\n3. Fill out the generated classes with your logic.\n\nA good overview of the process is detailed [here](https://help.liferay.com/hc/es/articles/360028748872-Generating-APIs-with-REST-Builder).\n\nWe'll see each step in detail but first, let's talk about why we want to use\nREST Builder.\n\n## Why we should use REST Builder\n\nThere are several reasons to prefer REST Builder over rolling our own [JAX-RS\nservices](https://help.liferay.com/hc/en-us/articles/360031902292-JAX-RS). Some of them are the following:\n\n* Development speed: you avoid writing JAX-RS annotations, converters, adding\n  support for multipart or layers to organize your code. Everything is generated.\n* API scaffolding: pagination, filtering, searching, JSON writers, XML\n  generation, even unit, and integration tests are generated.\n* GraphQL support out of the box: write your REST API and get a GraphQL endpoint\n  for free.\n* Integration with Liferay's authentication pipelines: Basic, OAuth, Cookie,\n  CORS handling. You don't have to search manually for the user or the company;\n  everything is already there.\n* JSON & XML support: APIs return whichever format the consumer prefers.\n* Consistency: all APIs follow the same rules and conventions, enforced by\n  REST builder."
  },
  {
    "path": "en/developer/frameworks/articles/web-services/05-rest-builder/02-how-to-install.markdown",
    "content": "---\nheader-id: how-to-install\n---\n\n# How to install REST Builder\n\n[TOC levels=1-4]\n\nUse the [Gradle plugin](https://portal.liferay.dev/docs/7-2/reference/-/knowledge_base/r/rest-builder-gradle-plugin) \nto install REST builder by adding this gradle configuration to your project: \n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.rest.builder\", version: \"1.0.21\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.portal.tools.rest.builder\"\n```\n\nTo use it, run `gradlew buildREST`. Note that your Gradle wrapper may not be in\nyour app’s project directory, so you may need to use [Blade](/docs/7-2/reference/-/knowledge_base/r/blade-cli) \nto locate it: \n\n```bash\nblade gw buildREST\n```\n\nIf you want to use a specific version of REST builder, you can specify it explicitly:\n\n```groovy\ndependencies {\n    restBuilder group: \"com.liferay\", name: \"com.liferay.portal.tools.rest.builder\", version: \"1.0.30\"\n}\n```\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/05-rest-builder/03-openapi.markdown",
    "content": "---\nheader-id: rest-builder-open-api\n---\n\n# REST Builder & OpenAPI\n\n[TOC levels=1-4]\n\nREST Builder is based on [OpenAPI](https://swagger.io/specification/), and its\nphilosophy is \"OpenAPI first\": first you write the profile and then you use\nit as the base of your implementation.\n\nBut first you must create a project with two empty bundles (a Blade\ntemplate will follow soon). The bundles (-api and -impl) should have the files you\nare already used to: a `build.gradle` and a `bnd.bnd`. The novelty is two\nYAML files, a configuration file (`rest-config.yaml`) and the OpenAPI profile\n(`rest-openapi.yaml`). An example project is\n[here](https://github.com/nhpatt/liferay-devcon-appointment). \n\nLet's see the configuration file in detail. In the root of the -impl project we\nhave to create a YAML file to specify paths and the basic configuration of our\nnew API. A sample implementation would be:\n\n```yaml\napiDir: \"../headless-test-api/src/main/java\"\napiPackagePath: \"com.liferay.headless.test\"\napplication:\n    baseURI: \"/headless-test\"\n    className: \"HeadlessTestApplication\"\n    name: \"Liferay.Headless.Test\"\nauthor: \"Javier Gamarra\"\n```\n\nThis file specifies the path of the -api bundle, the java package that we will\nuse across all the bundles and the information of the JAX-RS application: the\npath of our application, the name of the class and the JAX-RS name of our API.\n\nI've skipped two advanced features, generating a client and automated tests,\nwill see them later.\n\nJust one step left, writing our OpenAPI profile.\n\n## OpenAPI profile\n\nThe OpenAPI profile will be the source of all our APIs, in this file, we will\nadd the paths and entities of our API. First, we'll create\na [YAML](https://en.wikipedia.org/wiki/YAML) file called rest-openapi.yaml.\nWriting YAML files is tricky so we recommend using the [swagger\neditor](http://editor.swagger.io/) to do it, which validates the YAML file\nagainst YAML syntax and the [OpenAPI\nspecification](https://github.com/OAI/OpenAPI-Specification).\n\nA simple OpenAPI profile that retrieves a fictitious entity might look like this:\n\n```yaml\ncomponents:\n  schemas:\n    Entity:\n      description: A very simple entity\n      properties:\n        name:\n          description: The entity name.\n          type: string\n        id:\n          description: The entity ID.\n          type: integer\n      type: object\ninfo:\n  description: \"\"\n  title: \"My API\"\n  version: v1.0\nopenapi: 3.0.1\npaths:\n  \"/entities/{entityId}\":\n    get:\n      parameters:\n        - in: path\n          name: entityId\n          required: true\n          schema:\n            type: integer\n      responses:\n        200:\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Entity\"\n          description: \"\"\n      tags: [\"Entity\"]\n```\n\nAll OpenAPI profiles have three different sections: components, info, and paths.\nThe easiest one is the information block. It contains the OpenAPI version, the\ntitle and the version of your API:\n\n```yaml\ninfo:\n  description: \"\"\n  title: \"My API\"\n  version: v1.0\nopenapi: 3.0.1\n```\n\nIndentations should be spaces. The \n[swagger editor](http://editor.swagger.io/) helps with formatting.\n\nThe components section specifies the schemas/entities to return or accept\non your APIs. In this case, you define a schema called _Entity_\nthat has two string fields: a name and an id. \n\n```yaml\ncomponents:\n  schemas:\n    Entity:\n      description: A very simple entity\n      properties:\n        name:\n          description: The entity name.\n          type: string\n        id:\n          description: The entity ID.\n          type: integer\n      type: object\n```\n\nThe OpenAPI specification defines \n[many types and fields](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaObject)\nyou can use in your schemas. \n\nThe other common type is `$ref`, a \n[reference type](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#referenceObject)\nthat allows you to refer to an existing type like this: \n\n```yaml\n$ref: '#/components/schemas/Entity'\n```\n\nThe last block, called paths, defines the URLs that you'll expose in your APIs,\nwith the type of HTTP verbs, list of parameters, status codes, etc. \n\n```yaml\npaths:\n  \"/entities/{entityId}\":\n    get:\n      parameters:\n        - in: path\n          name: entityId\n          required: true\n          schema:\n            type: integer\n      responses:\n        200:\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Entity\"\n          description: \"\"\n      tags: [\"Entity\"]\n```\n\nThe pattern above, `\"/entities/{entity}\"`, follows a common pattern in REST\nAPIs. This is the endpoint that retrieves one element, `\"/entities\"`. It returns\na list of elements, and a POST request creates one.\n\nFor every path, it is mandatory to add a tag that points to an existing schema to\nindicate where to generate your code. REST Builder creates a method inside the\nclass `[TAG]ResourceImpl.java`. \n\n## Generation\n\nOnce you've written your OpenAPI configuration and profile, it's time to\ngenerate your scaffolding for REST and GraphQL.\n\nIn the -impl or in the root module folder, execute this command:\n\n```bash\ngw buildREST\n```\n\nYou can use `gw bR` if you want to save a few keystrokes.\n\nIf everything's indented properly and the OpenAPI profile validates, REST\nBuilder generates your JAX-RS resources and the GraphQL endpoint. Next, you'll\nsee what has been generated and how to implement our business logic.\n\n## Examples\n\nHere's a complete example that defines all CRUD operations in OpenAPI.\n\n### GET Collection\n\n```yaml\npaths:\n    \"/entities\":\n        get:\n            responses:\n                200:\n                    content:\n                        application/json:\n                            schema:\n                                items:\n                                    $ref: \"#/components/schemas/Entity\"\n                                type: array\n                    description: \"\"\n            tags: [\"Entity\"]\n```\n\n### DELETE\n\n```yaml\npaths:\n    \"/entities/{entityId}\":\n        delete:\n            parameters:\n                - in: path\n                  name: entityId\n                  required: true\n                  schema:\n                      type: integer\n            responses:\n                204:\n                    content:\n                        application/json: {}\n                    description: \"\"\n            tags: [\"Entity\"]\n```\n\n### POST\n\n```yaml\npaths:\n    \"/entities\":\n        post:\n            requestBody:\n                content:\n                    application/json:\n                        schema:\n                            $ref: \"#/components/schemas/Entity\"\n            responses:\n                200:\n                    content:\n                        application/json:\n                            schema:\n                                $ref: \"#/components/schemas/Entity\"\n                    description: \"\"\n            tags: [\"Entity\"]\n```\n\n### PUT\n\n```yaml\npaths:\n    \"/entities/{entityId}\":\n        put:\n            parameters:\n                - in: path\n                  name: entityId\n                  required: true\n                  schema:\n                      type: integer\n            requestBody:\n                content:\n                    application/json:\n                        schema:\n                            $ref: \"#/components/schemas/Entity\"\n            responses:\n                200:\n                    content:\n                        application/json:\n                            schema:\n                                $ref: \"#/components/schemas/Entity\"\n                    description: \"\"\n            tags: [\"Entity\"]\n```\n\n## Summary\n\nThere are more examples showcasing all the supported OpenAPI syntax\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/apps/headless/headless-delivery/headless-delivery-impl/rest-openapi.yaml)\nand\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/apps/headless/headless-admin-taxonomy/headless-admin-taxonomy-impl/rest-openapi.yaml). \nYour next step is to [create your API](/docs/7-2/frameworks/-/knowledge_base/f/rest-builder-develop). \n\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/05-rest-builder/04-develop.markdown",
    "content": "---\nheader-id: rest-builder-develop\n---\n\n# Developing an API with REST Builder\n\n[TOC levels=1-4]\n\nAfter executing `gw buildREST`, you have two modules: _headless-test-api_ and\n_headless-test-impl_.\n\n* Headless Test API contains the interfaces for your resources and the POJOs\n  of your schemas.\n\n* Headless Test Impl contains your implementation and the JAX-RS application.\n\nYour generated `EntityResource` looks like this:\n\n```java\npublic interface EntityResource {\n\n\tpublic Page<Entity> getEntitiesPage() throws Exception;\n\n\tpublic Entity postEntity(Entity entity) throws Exception;\n\n\tpublic void deleteEntity(Integer entityId) throws Exception;\n\n\tpublic Entity getEntity(Integer entityId) throws Exception;\n\n\tpublic Entity putEntity(Integer entityId, Entity entity) throws Exception;\n\n    //Context methods\n\n\tpublic default void setContextAcceptLanguage(AcceptLanguage contextAcceptLanguage) {}\n\n\tpublic void setContextCompany(Company contextCompany);\n\n\tpublic default void setContextHttpServletRequest(HttpServletRequest contextHttpServletRequest) {}\n\n\tpublic default void setContextHttpServletResponse(HttpServletResponse contextHttpServletResponse) {}\n\n\tpublic default void setContextUriInfo(UriInfo contextUriInfo) {}\n\n\tpublic void setContextUser(User contextUser);\n}\n```\n\nThese are generated methods you defined in the OpenAPI profile (the full set as\ndisplayed in the examples).\n\nREST builder also generates two implementation files, a base class, with all\nthe JAX-RS, GraphQL and OpenAPI annotations and an empty implementation,\n`EntityResourceImpl`:\n\n```java\n@Component(\n\tproperties = \"OSGI-INF/liferay/rest/v1_0/entity.properties\",\n\tscope = ServiceScope.PROTOTYPE, service = EntityResource.class\n)\npublic class EntityResourceImpl extends BaseEntityResourceImpl {\n}\n```\n\nThis is where you implement new methods, by overriding the base class\nimplementation and returning your code. For example, here's a prototype\nimplementation storing entities in a Map:\n\n```java\nMap<Integer, Entity> entities = new HashMap<>();\n\n@Override\npublic Entity getEntity(Integer entityId) throws Exception {\n    return entities.get(entityId);\n}\n\n@Override\npublic Page<Entity> getEntitiesPage() throws Exception {\n    return Page.of(entities.values());\n}\n\n@Override\npublic void deleteEntity(Integer entityId) throws Exception {\n    entities.remove(entityId);\n}\n\n@Override\npublic Entity postEntity(Entity entity) throws Exception {\n    entities.put(entity.getId(), entity);\n    return entity;\n}\n\n@Override\npublic Entity putEntity(Integer entityId, Entity entity) throws Exception {\n    entities.put(entity.getId(), entity);\n    return entity;\n}\n```\n\nFor the collection, you return a `Page` object based on a list but there\nare also utility methods that return the pagination information:\n\n ```java\nPage.of(list, pagination, totalCount)\n```\n\nDon't touch the interfaces or the base classes (those are\nregenerated every time you run REST Builder). Like \n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder), you only have \nto maintain the implementation classes, and if you change the API, run REST\nBuilder again and the interfaces are updated. Your business logic could call\nother REST APIs, use Service Builder or another persistence mechanism.\n\n## Development Cycle\n\nWhile implementing your API's business logic, you'll typically improve your API\nby adding parameters or other paths. For that, you'll modify the OpenAPI profile\nand regenerate the API again calling the `buildREST` command.\n\nThe cycle starts anew until you get to the final state and deploy your APIs. \nThey become available at this URL pattern: \n                                                                               \n    http://localhost:8080/o/[application class name]/[OpenAPI version]/\n\nYou can also execute `jaxrs:check` in the OSGi console to see all the JAX-RS\nendpoints.\n\nGraphQL paths and entities are added automatically to the default\nGraphQL endpoint: \n\n    localhost:8080/o/graphql \n\nYou can disable GraphQL generation by adding `generateGraphQL: false` to your\n`rest-config.yaml` (`generateREST` controls the generation of the REST\nendpoints).\n\n## Wrapping Up\n\nSo... that's all! \n\nWhen everything is ready, you might want to consider publishing your Headless API\nto Swaggerhub so others can consume it. You can use the following URL pattern for that: \n\n    http://localhost:8080/o/[application name]/[application version]/openapi.yaml\n\nThe URL for the example above, therefore, would be \n\n    http://localhost:8080/o/headless-test/v1.0/openapi.yaml\n\nThis URL has the content of `rest-openapi.yaml` plus the classes that REST\nBuilder generated for you. \n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/05-rest-builder/05-how-to-add-features-for-managing-collections.markdown",
    "content": "---\nheader-id: rest-builder-managing-collections\n---\n\n# Managing Collections in REST Builder\n\n[TOC levels=1-4]\n\n## Pagination\n\nTo add pagination to your endpoints, add `page` and `pageSize` as query\nparameters to your OpenAPI profile, like this: \n\n```yaml\n- in: query\n  name: page\n  schema:\n      type: integer\n- in: query\n  name: pageSize\n  schema:\n      type: integer\n```\n\nThose two parameters add a `Pagination pagination` parameter in the method\nsignature to restrict the number of entries to return in the `Page.of`\nconstructor.\n\nPagination is highly recommended for entities that can have many elements, to\navoid very large requests.\n\n## Filtering, sorting and searching\n\nAdding support for filtering, sorting, and searching is trickier. The first\nstep is to add the query parameters to the OpenAPI profile, like this:\n\n```yaml\n- in: query\n  name: filter\n  schema:\n      type: string\n- in: query\n  name: search\n  schema:\n      type: string\n- in: query\n  name: sort\n  schema:\n      type: string\n```\n\nThe method signature then receives a Sort object, a Filter object, and\nstring with the search request. Those objects use \n[Liferay's indexing framework](/docs/7-2/frameworks/-/knowledge_base/f/model-entity-indexing-framework).\nThis gives you many benefits, like support for permissions out-of-the-box and\nhaving to write very little code to achieve sort, filter, and search.\n\nSo first, you must make sure your entity is indexed and uses the indexing\nframework.\n\nOnce that's done you have three things to do:\n\n1. Add an `EntityModel` to translate between our indexing framework and your code\n2. Inject your `entityModel` into your resource implementation.\n3. Call Search utilities to avoid boilerplate code.\n\n### Add an EntityModel\n \nThe `EntityModel` is a class that translates the name the property has\nin your API to the name used to index it.\n\n```java\npublic class EntityEntityModel implements EntityModel {\n\tpublic EntityEntityModel() {\n\t\t_entityFieldsMap = EntityModel.toEntityFieldsMap(\n\t\t\tnew StringEntityField(\n\t\t\t\t\"name\", locale -> Field.getSortableFieldName(Field.NAME))\n\t\t);\n\t}\n\n\t@Override\n\tpublic Map<String, EntityField> getEntityFieldsMap() {\n\t\treturn _entityFieldsMap;\n\t}\n\n\tprivate final Map<String, EntityField> _entityFieldsMap;\n}\n```\n\nThe `EntityModel` decouples the way you filter/sort from the way you\nindex the information. You could use one field to sort, backed internally by\nseveral indexed fields or vice-versa.\n\n### Inject Your EntityModel\n\nInjecting your `EntityModel` is really easy, our resource implementation just has\nto implement the `EntityModelResource` interface. \n\nThis entity model is simple and doesn't have any dynamic fields, so you can\ninstantiate it directly and return it in the `getEntityModel` method, like this:\n\n```java\n@Component(\n\tproperties = \"OSGI-INF/liferay/rest/v1_0/entity.properties\",\n\tscope = ServiceScope.PROTOTYPE, service = EntityResource.class\n)\npublic class EntityResourceImpl extends BaseEntityResourceImpl implements\n\tEntityModelResource {\n\n    ...\n\n\t@Override\n\tpublic EntityModel getEntityModel(MultivaluedMap multivaluedMap) {\n\t\treturn _entityEntityModel;\n\t}\n\n\tprivate EntityEntityModel _entityEntityModel = new EntityEntityModel();\n}\n```\n\n### Call search utilities\n\nFinally, you must call `SearchUtil.search()` that links everything together. It\nrequires these parameters:\n\n`booleanQueryUnsafeConsumer`: a boolean query to restrict the information we\nwant to retrieve.\n\n`filter`: pass-through of the filter object.\n\n`indexerClass`: the class of the entity that to filter/search.\n\n`keywords`: pass-through of the search string.\n\n`pagination`: pass-through of the pagination object (to read the row requested).\n\n`queryConfigUnsafeConsumer`: the configuration for the fields that you want to\nreturn, typically the id to do a query later, in the `transformUnsafeFunction`.\n\n`searchContextUnsafeConsumer`: global configuration of the query.\n\n`transformUnsafeFunction`: the function that transforms from `Document` (of the\nindexing framework) to your entity, either searching in the database, your\npersistence, another API, etc. \n\n`sorts`: pass-through of the sorts object.\n\nThe code would be similar to this:\n\n```java\n@Override\npublic Page<Entity> getEntitiesPage(\n    String search, Filter filter, Pagination pagination, Sort[] sorts)\n    throws Exception {\n\n    return SearchUtil.search(\n        booleanQuery -> {},\n        filter, Entity.class, search, pagination,\n        queryConfig -> queryConfig.setSelectedFieldNames(\n            Field.ENTRY_CLASS_PK),\n        searchContext -> searchContext.setCompanyId(contextCompany.getCompanyId()),\n        document -> new Entity(), //FILL with your implementation\n        sorts);\n}\n```\n\n### Using Your filter, search, and sort\n\nLifeay uses OData to express our filter queries, following this\n[syntax](/docs/7-2/frameworks/-/knowledge_base/f/filter-sort-and-search#filter). \n\nAnd that's it! Now you can can filter/search and sort by the fields you \ndefined in your `EntityModel`.\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/05-rest-builder/06-scaffolding.markdown",
    "content": "---\nheader-id: rest-builder-scaffolding\n---\n\n# REST Builder Scaffolding\n\n[TOC levels=1-4]\n\nApart from the JAX-RS annotations, basic structure and GraphQL support, there\nare some other things the REST Builder provides:\n\n- Context Fields\n- Automatic Transactions\n- Test Generation\n- Client Generation\n- Common Utilities\n\nThese are useful and time saving additions to your development cycle. \n\n## Context fields\n\nThe `ResourceImpl` classes are JAX-RS-compliant resources. You can add\n`@Context` injections, mix JAX-RS endpoints, and use full power of the JAX-RS\nstandard.\n\nREST Builder injects several fields (as protected fields in the base class) to\nhelp you implement new APIs, like these:\n\n* `contextAcceptLanguage`, containing the current `Locale`.\n* `contextCompany`, containing the current `Company`.\n* `contextHttpServletRequest`, containing the `HttpServletRequest`.\n* `contextHttpServletResponse`, containing the `HttpServletResponse`.\n* `contextUser`, containing the current logged-in `User`.\n* `contextUriInfo`, containing the `UriInfo` information (paths, endpoints).\n\n## Automatic transactions\n\nOne little-known feature of REST Builder is that it wraps your API calls\nin transactions if the HTTP verb used is POST, PUT, PATCH or DELETE (doesn't\napply for GET operations). \n\nIf you need to do several Service Builder calls in a sequence, you don't have\nto wrap them in a transaction; it happens automatically. The transaction\ncommits if no exception is thrown and rolls back if an exception bubbles up\nto the resource implementation method.\n\n## Test generation\n\nYou can generate integration tests if you specify a `testDir` property: \n\n```yaml\ntestDir: \"../headless-admin-taxonomy-test/src/testIntegration/java\"\n```\n\nREST Builder generates integration tests under the -test module. Those tests\ncheck the API using a REST client. They check the response, doing an\nend-to-end test involving all steps from parsing the request to returning\nJSON.\n\nThe generated tests are ignored by default and, depending on the path, may\nforce you to implement a creation method (to be able to add content to either\nupdate/delete or retrieve it).\n\nThe [headless-delivery-test](https://github.com/liferay/liferay-portal/tree/master/modules/apps/headless/headless-delivery/headless-delivery-test)\nproject contains many examples. \n\n## Client generation\n\nYou can generate a Java client if you specify a `clientDir` property: \n\n```yaml\nclientDir: \"../headless-admin-taxonomy-client/src/main/java\"\n```\n\nThis is a Java, typed, client that interacts with the APIs using static methods.\nThe client project contains all the methods to call your paths and parses the\nrequests and responses.\n\n## Common utilities\n\nREST Builder provides several JAX-RS utilities, from exception mappers for the\nmost common exceptions in the portal, XML and JSON Body Readers/Writers, Site\nvalidation (that works with `siteId` and `siteKey`), and support for Bean\nValidation.\n\nThere are also utility libraries that can be useful when developing new APIs:\n\n* `ContentLanguageUtil`, to deal with the `ContentLanguage` header.\n* `JaxRsLinkUtil`, to create links between APIs.\n* `LocalDateTimeUtil`, to transform between date formats.\n* `LocalizedMapUtil`, to create maps with locales as keys.\n* `SearchUtil`, to use the search framework to return Pages of content.\n* `TransformUtil`, to deal with lambdas and conversions.\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/05-rest-builder/07-support-for-oneOf-anyOf-and-allOf.markdown",
    "content": "---\nheader-id: rest-builder-support-for-oneof-anyof-and-allof\n---\n\n# Support for oneOf, anyOf and allOf\n\n[TOC levels=1-4]\n\nOpenAPI 3.0 added several ways of using inheritance and composition to create\ncomplex schemas. Specifically, it added support for \n[_allOf_, _anyOf,_ and _oneOf_](https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/),\nwith these semantics:\n\n* allOf – the value validates against all the subschemas \n* anyOf – the value validates against any of the subschemas\n* oneOf – the value validates against exactly one of the subschemas\n\nNext, you'll learn each option's syntax and how its code is generated. \n\n## allOf\n\nWith `allOf` you can use the power of composition to create an entity that\ncombines several others. It's the most potent way of reusing code without\nlosing expressiveness and granularity: you can define small entities that can\nbe reused by composing several to create a larger entity.\n\nTo use `allOf` you must follow this syntax:\n\n```yaml\nEntityA:\n    properties:\n        nameA:\n            type: string\nEntityB:\n    properties:\n        nameB:\n            type: string\nEntityC:\n    allOf:\n        - $ref: '#/components/schemas/EntityA'\n        - $ref: '#/components/schemas/EntityB'\n```\n\nThis OpenAPI syntax generates the following Java code inside the `EntityC` class:\n\n```java\n@Schema\n@Valid\npublic EntityA getEntityA() {\n    return entityA;\n}\n\n@Schema\n@Valid\npublic EntityB getEntityB() {\n    return entityB;\n}\n```\n\n## oneOf\n\n`OneOf` is the simplest of the generics properties. It defines a property that\ncan have different types. Since Java doesn't support Union Types, use an Object\nto model the property:\n\n```yaml\nEntityA:\n    properties:\n        nameA:\n            type: string\nEntityB:\n    properties:\n        nameB:\n            anyOf:\n                - $ref: \"#/components/schemas/EntityA\"\n                - type: object\n                  properties:\n                      name:\n                          type: string\n```\n\nThis syntax generates the following Java code:\n\n```java\n@Schema\n@Valid\npublic Object getNameB() {\n    return nameB;\n}\n```\n\n## anyOf\n\nThe final generic keyword, `anyOf` leverages `JsonSubTypes` to extend entities\nwith properties using inheritance. You can define parent relationships (in this\nexample, `EntityC`) with two children containing the properties of the parent and\ntheir own properties. Here's how to define YAML to use inheritance:\n\n```yaml\nEntityC:\n    oneOf:\n        - properties:\n            nameA:\n                type: string\n        - properties:\n            nameB:\n                type: string\n    properties:\n        nameC:\n            type: string\n```\n\nThis generates a parent class and two children:\n\n```java\n@JsonSubTypes(\n    {\n        @JsonSubTypes.Type(name = \"nameA\", value = NameA.class),\n        @JsonSubTypes.Type(name = \"nameB\", value = NameB.class)\n    }\n)\n@JsonTypeInfo(\n    include = JsonTypeInfo.As.PROPERTY, property = \"childType\",\n    use = JsonTypeInfo.Id.NAME\n)\n@Generated(\"\")\n@GraphQLName(\"EntityC\")\n@JsonFilter(\"Liferay.Vulcan\")\n@XmlRootElement(name = \"EntityC\")\npublic class EntityC {\n\n    @Schema\n    public String getNameC() {\n        return nameC;\n    }\n\n    public void setNameC(String nameC) {\n        this.nameC = nameC;\n    }\n```\n\nAnd two children classes look like this: \n\n```java\n@JsonTypeInfo(\n    defaultImpl = NameA.class, include = JsonTypeInfo.As.PROPERTY,\n    property = \"childType\", use = JsonTypeInfo.Id.NAME\n)\n@Generated(\"\")\n@GraphQLName(\"NameA\")\n@JsonFilter(\"Liferay.Vulcan\")\n@XmlRootElement(name = \"NameA\")\npublic class NameA extends EntityC {\n\n    @Schema\n    public String getNameA() {\n        return nameA;\n    }\n\n    public void setNameA(String nameA) {\n        this.nameA = nameA;\n    }\n```\n"
  },
  {
    "path": "en/developer/frameworks/articles/web-services/05-rest-builder/08-good-practices-and-conventions.markdown",
    "content": "---\nheader-id: rest-builder-conventions\n---\n\n# REST Builder Liferay Conventions\n\n[TOC levels=1-4]\n\nLiferay's headless APIs follow several patterns and conventions to provide\nconsistency and uniformity in the APIs. \n\nBelow is a list the most important ones. It's a living list. Improvements are\nbeing made all the time, so check back to stay up to date on the changes. \n\n## YAML & OpenAPI restrictions\n\n* *Tags are required*. We can't assign a class to a DELETE operation (doesn't\n  return anything, doesn't receive an entity) so we need the tag to assign the\n  method to a Java class.\n* Responses must return a status code (default is not supported).\n* Paths must be quoted.\n* Paths only contain the method path (application and version are inherited\n  from the JAX-RS application).\n\n## Conventions\n\n* We use path parameters for information that is required (like the id in\n  a DELETE operation) and query parameters for optional information (filtering,\n  sorting...)\n\n* We don't expose `className`. If you need to return information like the\n  `className`, use `contentType` keyword.\n\n"
  },
  {
    "path": "en/developer/frameworks/articles/workflow/01-workflow-intro.markdown",
    "content": "---\nheader-id: the-workflow-framework\n---\n\n# The Workflow Framework\n\n[TOC levels=1-4]\n\nBlogs Entries, Journal Articles, and Forms Entries are just a few Assets\nsupporting workflow. There's nothing stopping you from likewise enabling\nworkflow for your custom Assets. Discover here how the workflow framework\nworks, and find the steps and code samples for enabling your custom entities to\nuse the workflow capabilities in subsequent articles.\n\nA workflow process is a set of steps that an Asset must proceed through before\nit's marked with the workflow status _Approved_. The steps are defined in an XML file\ncalled a\n[workflow definition](/7-2/reference/-/knowledge_base/r/crafting-xml-workflow-definitions).\nEach Asset is configured to run through a specific workflow definition via the\n[Control Panel](/7-2/user/-/knowledge_base/u/workflow).\n\n![Figure 1: Enable workflow on your custom Asset, and it can be sent through a workflow process just like a native Asset.](../../images/workflow-configuration.png)\n\nThe workflow status is a database field that must be present for an entity to\nsupport workflow. If a database has the status field, but no workflow code has\nbeen written, it's auto-marked _Approved_ by Liferay's Service Builder\ninfrastructure, to assure that everything works smoothly by default.\n\n## Supporting Workflow in the Database\n\nThere are several database fields that must be present for an Asset to support\nworkflow:\n\n`int status` represents the workflow status of each Asset.\n\n`long statusByUserId` is the ID of the user that set the status (for example,\nthe initial User that hit the Submit for Publication button to add a new\nAsset.\n\n`String statusByUserName` is the User Name of the User that set the status of\nthe Asset.\n\n`Date statusDate` is the date/time when the status was set.\n\nFor Service Builder applications, add these as entity columns in the\n`service.xml` file, run Service Builder, and you're good to go.\n\n## Setting the Status Fields\n\nOnce the database table has the proper status fields, set them in your Entity's\n`addEntity` service method. Initially, set the status as a DRAFT.  It's what\nthe workflow framework expects of an entity as it enters the workflow process.\nThe status is an `int`, but you don't have to remember which number corresponds\nto the DRAFT status. Instead, use the\n[`WorkflowConstants`](https://github.com/liferay/liferay-portal/blob/7.2.x/portal-kernel/src/com/liferay/portal/kernel/workflow/WorkflowConstants.java)\nin `portal-kernel`. For a draft, pass in\n\n```java\nWorkflowConstants.STATUS_DRAFT\n```\nIf you're curious, the `int` represented by this constant is `2`. Another\nimportant status, APPROVED, is represented by the `int` value `0` and the constant\n\n```java\nWorkflowConstants.STATUS_APPROVED\n```\n\nThe User fields (`statusByUserID` and `statusByUserName`) are easy, since the\n`userId` of the User making the `addEntity` request is part of the request\nitself, and passed into the `addEntity` method for most assets. Use the ID\ndirectly as the `statusByUserId`, and get the full name associated with the User\nby using the ID to retrieve the `User` object.\n\n```java\nentity.setStatusByUserId(userId);\nentity.setStatusByUserName(user.getFullName());\n```\n\nThe `statusDate` is usually best set as the date the entity was modified, and\nis part of the Service Context in the request:\n\n```java\nentity.setStatusDate(serviceContext.getModifiedDate(null));\n```\n\nOnce the status dates are set, the entity is ready to be sent into the workflow\nframework.\n\n## Sending the Entity to the Workflow Framework\n\nWhen an entity is added to the database, the application must detect whether\nworkflow is enabled. If not, it automatically marks the entity as approved so it\nappears in the UI. Otherwise, it's left in draft status and the workflow\nback-end handles it. Thankfully, this whole process is easily done with a call\nto `WorkflowHandlerRegistryUtil.startWorkflowInstance` in your persistence code.\n\n## Allowing the Workflow Framework to Handle the Entity\n\nOnce the entity is sent to the Workflow Framework, much of the process is\nautomated, and you need not worry about the details. Write one class that gives\nthe framework some information on how to process the entity. It's called a\nworkflow handler (`WorkflowHandler<T>`), and you can create it by extending the\nhandy abstract implementation, `BaseWorkflowHandler<T>`.\n\nThe workflow handler usually goes in the module containing service\nimplementations. It's nice to keep your back-end code separate from your view\nlayer and controller (ala the MVC pattern).\n\nMake your workflow handler a Component class so it can be registered properly\nwith OSGi runtime. It requires one Component property, `model.class.name`, which\nis the fully qualified class name for class you pass as the type parameter in\nthe class declaration.\n\nIn addition to the property, declare the type of service you're providing in the\nComponent: `WorkflowHandler.class`.\n\nWorkflow handlers extending the `BaseWorkflowHandler` must override three methods:\n\n`getClassName` returns the model class's fully qualified class name\n(`com.my.app.package.model.FooEntity`, for example).\n\n`getType` returns the model resource name\n(`model.resource.com.my.app.package.model.FooEntity`, for example).\n\n`updateStatus` does most of the heavy lifting here. It returns a call to a local\nservice method of the same name (for example,\n`FooEntityLocalService.updateStatus`), so the status returned from the workflow\nback-end can be persisted to the entity table in the database. The\n`updateStatus` method needs a user ID, the primary key for the class (for\nexample, `fooEntityId`), the workflow status, the service context, and the\nworkflow context. The status and the workflow context can be obtained from the\nworkflow back-end. The other parameters can be obtained from the workflow\ncontext.\n\n## Supporting Workflow in the Service Layer\n\nThe service layer must update the status of the entity when it returns from the\nWorkflow Framework. Make an `updateStatus` method for this purpose, and make\nsure, at a minimum, to set the status fields again as the Asset comes out of the\nWorkflow Framework, and call the persistence layer's `update` method.\n\nAfter that, provide any additional logic you might want, like checking the\nstatus and updating the Asset's visibility (using the `assetEntryLocalService`)\nbased on the condition (visible if _Approved_, not visible is any other status).\n\n<!--\nOther Workflow Considerations in the Service Layer\n\n**Blogs Entries, accounting for scheduled publication:**\n[BlogsEntryLocalServiceImpl#updateStatus](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/blogs/blogs-service/src/main/java/com/liferay/blogs/service/impl/BlogsEntryLocalServiceImpl.java#L1505)\n\nAsset\n\nSocial\n\nStats (Blogs)\n\nTrash\n\nJournal,\n[JournalArticleLocalServiceImpl#updateStatus](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/journal/journal-service/src/main/java/com/liferay/journal/service/impl/JournalArticleLocalServiceImpl.java#L6530)\n\n\nEmail\n\nSubscriptions\n-->\n\nReturn the entry once you're through here.\n\n## Database Cleanup: Delete the Workflow Instance Links\n\nWhen you send an entity to the workflow framework via the\n`startWorkflowInstance` call, it creates an entry in the `workflowinstancelink`\ndatabase table. In your service layer's deletion logic, you must delete the\nworkflow instance links. This `delete` call ensures there are no orphaned\nentries in the `workflowinstancelinks` table.\n\nTo get the `WorkflowInstanceLocalService` injected into your\n`*LocalServiceBaseImpl` so you can call its methods in the `LocalServiceImpl`,\nadd a `reference entity` to your entity declaration in `service.xml`, specifying\n`WorkflowInstancelink`.\n\n## Updating the User Interface\n\nAfter you finish all the backend work, update your UI. Some common tasks here\ninclude:\n\n- In any public facing portion of the application (accessible to guest Users),\n    don't display the entity if the status is anything except _Approved_. This\n    task requires the creation of an additional `finder` method that accounts\n    for workflow status, and a corresponding `getter` to expose it in the service\n    layer.\n\n- In administrative portions of the application, display the entities, but also\n    display their workflow status. There's a tag library for this.\n\nSee the next article for more concrete steps and code snippets.\n"
  },
  {
    "path": "en/developer/frameworks/articles/workflow/02-workflow-framework.markdown",
    "content": "---\nheader-id: liferays-workflow-framework\n---\n\n# Liferay's Workflow Framework\n\n[TOC levels=1-4]\n\nTo workflow-enable your entities,\n\n1. Create a Workflow Handler\n2. Update the Service Layer\n3. Update the User Interface\n\nTime to get started.\n\n## Creating a Workflow Handler\n\nIf you're in a Service Builder application, the workflow handler goes in your\n`-service` module.\n\n1.  Create a Component class that extends `BaseWorkflowHandler<T>`.\n\n    ```java\n    @Component(immediate = true, service = WorkflowHandler.class)\n    public class FooEntityWorkflowHandler extends BaseWorkflowHandler<FooEntity>\n    ```\n\n2.  Override three methods in the workflow handler.\n\n    ```java\n    @Override\n    public String getClassName() {\n        return FooEntity.class.getName();\n    }\n\n    @Override\n    public String getType(Locale locale) {\n        return ResourceActionsUtil.getModelResource(locale, getClassName());\n    }\n\n    @Override\n    public FooEntity updateStatus(int status, Map<String, Serializable> workflowContext) throws PortalException {\n    ... }\n    ```\n\n    Most of the heavy lifting is in the `updateStatus` method. It returns\n    a call to a local service method of the same name (for example,\n    `FooEntityLocalService.updateStatus`), so the status returned from the workflow\n    back-end can be persisted to the entity table in the database.\n\n    The `updateStatus` method needs a user ID, the primary key for the\n    class (for example, `fooEntityId`), the workflow status, the service\n    context, and the workflow context. The status and the workflow context can\n    be obtained from the workflow back-end. The other parameters can be\n    obtained from the workflow context. Here's an example `updateStatus` method:\n\n    ```java\n    @Override\n    public FooEntity updateStatus(\n            int status, Map<String, Serializable> workflowContext)\n        throws PortalException {\n\n        long userId = GetterUtil.getLong(\n            (String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));\n        long classPK = GetterUtil.getLong(\n            (String)workflowContext.get(\n                WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n        ServiceContext serviceContext = (ServiceContext)workflowContext.get(\n            WorkflowConstants.CONTEXT_SERVICE_CONTEXT);\n\n        return _fooEntityLocalService.updateStatus(\n            userId, classPK, status, serviceContext);\n    }\n    ```\n\nNow your entity can be handled by Liferay's workflow framework. Next, update\nthe service methods to account for workflow status and add a new method to\nupdate the status of an entity in the database.\n\n## Updating the Service Layer\n\nIn most Liferay applications,\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\nis used to create database fields. First, you must update the service layer:\n\n1.  Make sure your entity database table has `status`, `statusByUserId`,\n    `statusByUserName`, and `statusDate` fields.\n\n    ```xml\n    <column name=\"status\" type=\"int\" />\n    <column name=\"statusByUserId\" type=\"long\" />\n    <column name=\"statusByUserName\" type=\"String\" />\n    <column name=\"statusDate\" type=\"Date\" />\n    ```\n\n2.  Wherever you're setting the other database fields in your persistence code,\n    set the workflow status as a draft and set the other fields.\n\n    ```java\n    fooEntity.setStatus(WorkflowConstants.STATUS_DRAFT);\n    fooEntity.setStatusByUserId(userId);\n    fooEntity.setStatusByUserName(user.getFullName());\n    fooEntity.setStatusDate(serviceContext.getModifiedDate(null));\n    ```\n\n    With Service Builder driven Liferay applications, this is in the local service\n    implementation class (`-LocalServiceImpl`).\n\n\n3.  At the end of any method that adds a new entity to your database, call the\n    workflow service to enable sending the entity into the workflow backend:\n\n    ```java\n    WorkflowHandlerRegistryUtil.startWorkflowInstance(\n        fooEntity.getCompanyId(), fooEntity.getGroupId(), fooEntity.getUserId(),\n        FooEntity.class.getName(), fooEntity.getPrimaryKey(), fooEntity,\n        serviceContext);\n    ```\n\n4.  Implement the `updateStatus` method that must be called in the workflow\n    handler. In the end, persist the updated entity to the database.\n\n    ```java\n    fooEntity.setStatus(status);\n    fooEntity.setStatusByUserId(user.getUserId());\n    fooEntity.setStatusByUserName(user.getFullName());\n    fooEntity.setStatusDate(serviceContext.getModifiedDate(now));\n\n    fooEntityPersistence.update(fooEntity);\n    ```\n\n5.  Do anything else that might make sense here, like\n    changing the visibility of the asset depending on its workflow status:\n\n    ```java\n    if (status == WorkflowConstants.STATUS_APPROVED) {\n        assetEntryLocalService.updateVisible(\n            FooEntity.class.getName(), fooEntityId, true);\n    }\n    else {\n        assetEntryLocalService.updateVisible(\n            FooEntity.class.getName(), fooEntityId, false);\n    }\n    ```\n\n    Here's what a full `updateStatus` method might look like:\n\n    ```java\n    @Indexable(type = IndexableType.REINDEX)\n    public FooEntity updateStatus(\n        long userId, long fooEntityId, int status, ServiceContext serviceContext\n    ) throws PortalException, SystemException {\n\n    \t\tUser user = userLocalService.getUser(userId);\n    \t\tFooEntity fooEntity = getFooEntity(fooEntityId);\n\n    \t\tfooEntity.setStatus(status);\n    \t\tfooEntity.setStatusByUserId(userId);\n    \t\tfooEntity.setStatusByUserName(user.getFullName());\n    \t\tfooEntity.setStatusDate(new Date());\n\n    \t\tfooEntityPersistence.update(fooEntity);\n\n    \t\tif (status == WorkflowConstants.STATUS_APPROVED) {\n    \t\t\tassetEntryLocalService.updateVisible(\n              FooEntity.class.getName(), fooEntityId, true);\n    \t\t}\n        else {\n    \t\t\tassetEntryLocalService.updateVisible(\n              FooEntity.class.getName(), fooEntityId, false);\n    \t\t}\n\n    \t\treturn fooEntity;\n\t  }\n    ```\n\n6.  Add a call to `deleteWorkflowInstanceLinks` in the `deleteEntity` method:\n\n    ```java\n    workflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(\n        fooEntity.getCompanyId(), fooEntity.getGroupId(),\n        FooEntity.class.getName(), fooEntity.getFooEntityId());\n    ```\n\n    To get the `WorkflowInstanceLocalService` injected into your\n    `*LocalServiceBaseImpl` so you can call its methods in the\n    `LocalServiceImpl`, add this to your entity declaration in `service.xml`:\n\n    ```xml\n    <reference entity=\"WorkflowInstanceLink\" package-path=\"com.liferay.portal\" />\n    ```\n\nFor an example of a fully implemented `updateStatus` method, see the\n`com.liferay.portlet.blogs.service.impl.BlogsEntryLocalServiceImpl` class in\n`portal-impl`.\n<!-- Check validity for 7.2-->\n\nOnce you've accounted for workflow status in your database and service layer,\nthere's only one thing left to do: update the user interface.\n\n## Workflow Status and the View Layer\n\nThis is dependent on the needs of your application, but often involves the\nfollowing these steps:\n\n**Display only approved entities:**\n\n1.  If you're using Service Builder, define your finder in your application's\n    `service.xml` and let Service Builder generate it for you.\n\n    ```xml\n    <finder name=\"G_S\" return-type=\"Collection\">\n        <finder-column name=\"groupId\"></finder-column>\n        <finder-column name=\"status\"></finder-column>\n    </finder>\n    ```\n\n2.  Make sure you have a getter in your service layer that uses the new finder.\n\n    ```java\n    public List<FooEntity> getFooEntities(long groupId, int status)\n        throws SystemException {\n\n        return fooEntityPersistence.findByG_S(\n            groupId, WorkflowConstants.STATUS_APPROVED);\n    }\n    ```\n\n3.  Finally, update your JSP to use the appropriate getter.\n\n    ```markup\n    <liferay-ui:search-container-results\n        results=\"<%= FooEntityLocalServiceUtil.getFooEntities(\n            scopeGroupId, fooEntityId(), Workflowconstants.STATUS_APPROVED,\n            searchContainer.getStart(), searchContainer.getEnd()) %>\"\n    />\n        ...\n    ```\n\n**Display the workflow status:**\n\nWhen you want to display the workflow status, use the `<aui:worklfow-status>`\ntag.\n\n```markup\n<aui:workflow-status markupView=\"lexicon\" showIcon=\"<%= false %>\" showLabel=\"<%= false %>\" status=\"<%= fooEntity.getStatus() %>\" />\n```\n\nOnce your user interface accounts for workflow, your Liferay application is\nfully workflow enabled.\n"
  },
  {
    "path": "en/developer/frameworks/articles/wysiwyg-editors/01-wysiwyg-editors-intro.markdown",
    "content": "---\nheader-id: wysiwyg-editors\n---\n\n# WYSIWYG Editors\n\n[TOC levels=1-4]\n\nWYSIWYG editors are an important part of content creation. Liferay's platform \nsupports several different editors, including CKEditor, TinyMCE, and our \nflagship, AlloyEditor. This section shows how to customize these WYSIWYG editors \nfor your apps and sites. \n"
  },
  {
    "path": "en/developer/frameworks/articles/wysiwyg-editors/02-wysiwyg-editor-in-portlet/01-adding-a-wysiwyg-editor-to-a-portlet-intro.markdown",
    "content": "---\nheader-id: adding-a-wysiwyg-editor-to-a-portlet\n---\n\n# Adding a WYSIWYG Editor to a Portlet\n\n[TOC levels=1-4]\n\nIt's easy to include WYSIWYG editors in your portlet, thanks to the \n`<liferay-editor:editor />` tag. Follow these steps:\n\n1.  Add the liferay-editor taglib declaration to your portlet's JSP:\n\n    ```markup\n    <%@ taglib uri=\"http://liferay.com/tld/editor\" prefix=\"liferay-editor\" %>\n    ```\n\n2.  Add the editor to your JSP with the `<liferay-editor:editor />` tag. \n    Configure it using the attributes shown in the table below:\n    \n    | Attribute | Type | Description |\n    | --- | --- | --- |\n    | `autoCreate` | `java.lang.String` | Whether to show the HTML edit view of the editor initially |\n    | `contents` | `java.lang.String` | Sets the initial contents of the editor |\n    | `contentsLanguageId` | `java.lang.String` | Sets the language ID for the input editor's text |\n    | `cssClass` | `java.lang.String` | A CSS class for styling the component. |\n    | `data` | `java.util.Map` | Data that can be used as the editorConfig |\n    | `editorName` | `java.lang.String` | The editor you want to use (alloyeditor, ckeditor, tinymce, simple) |\n    | `name` | `java.lang.String` | A name for the input editor. The default value is `editor`. |\n    | `onBlurMethod` | `java.lang.String` | A function to be called when the input editor loses focus. |\n    | `onChangeMethod` | `java.lang.String` | A function to be called on a change in the input editor. |\n    | `onFocusMethod` | `java.lang.String` | A function to be called when the input editor gets focus. |\n    | `onInitMethod` | `java.lang.String` | A function to be called when the input editor initializes. |\n    | `placeholder` | `java.lang.String` | Placeholder text to display in the input editor. |\n    | `showSource` | `java.lang.String` | Whether to enable editing the HTML source code of the content. The default value  is `true`. |\n    \n    See the [taglibdocs](@app-ref@/frontend-editor/latest/taglibdocs/liferay-editor/editor.html) \n    for the complete list of supported attributes. \n\n    Below is an example configuration:\n\n    ```html    \n    <div class=\"alloy-editor-container\">\n        <liferay-editor:editor\n        \t\tcontents=\"Default Content\"\n        \t\tcssClass=\"my-alloy-editor\"\n        \t\teditorName=\"alloyeditor\"\n        \t\tname=\"myAlloyEditor\"\n        \t\tplaceholder=\"description\"\n        \t\tshowSource=\"true\" \n        /> \n    </div>\n    ```\n\n3.  Optionally pass JavaScript functions through the `onBlurMethod`, \n    `onChangeMethod`, `onFocusMethod`, and `onInitMethod` attributes. Here is an \n    example configuration that uses the `onInitMethod` attribute to pass a \n    JavaScript function called `OnDescriptionEditorInit`:\n\n    ```markup\n    <%@ taglib uri=\"http://liferay.com/tld/editor\" prefix=\"liferay-editor\" %>\n\n    <div class=\"alloy-editor-container\">\n        <liferay-editor:editor\n            contents=\"Default Content\"\n            cssClass=\"my-alloy-editor\"\n            editorName=\"alloyeditor\"\n            name=\"myAlloyEditor\"\n            onInitMethod=\"OnDescriptionEditorInit\"\n            placeholder=\"description\"\n            showSource=\"true\" />\n    </div>\n    ```\n\n    ```javascript \n    <aui:script>\n        function <portlet:namespace />OnDescriptionEditorInit() {\n            <c:if test=\"<%= !customAbstract %>\">\n                document.getElementById(\n                  '<portlet:namespace />myAlloyEditor'\n                ).setAttribute('contenteditable', false);\n            </c:if>\n        }\n    </aui:script>\n    ```\n\nAs you can see, it's easy to include WYSIWYG editors in your portlets! \n\n## Related Topics\n\n- [Adding New Behavior to an Editor](/docs/7-2/frameworks/-/knowledge_base/f/adding-new-behavior-to-an-editor)\n- [Modifying an Editor's Configuration](/docs/7-2/frameworks/-/knowledge_base/f/modifying-an-editors-configuration)\n- [Modifying the AlloyEditor](/docs/7-2/frameworks/-/knowledge_base/f/alloyeditor)\n"
  },
  {
    "path": "en/developer/frameworks/articles/wysiwyg-editors/03-modifying-editor-configuration/01-modifying-an-editors-configuration-intro.markdown",
    "content": "---\nheader-id: modifying-an-editors-configuration\n---\n\n# Modifying an Editor's Configuration\n\n[TOC levels=1-4]\n\nYou can use many different kinds of WYSIWYG editors to edit content in \nportlets. Depending on the content you're editing, you may want to modify the \neditor to provide a customized configuration for your needs. In this article, \nyou'll learn how to modify the default configuration for @product@'s supported \nWYSIWYG editors to meet your requirements. \n\nTo modify the editor's configuration, create a module with a component that \nimplements the \n[`EditorConfigContributor`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/editor/configuration/EditorConfigContributor.html) \ninterface. Follow these steps to modify one of @product@'s WYSIWYG editors:\n\n1.  [Create an OSGi module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project).\n\n2.  Open the portlet's `build.gradle` file and update the \n    `com.liferay.portal.kernel` `version` to `4.13.1`. This is the version \n    bundled with the @product@ release.\n\n3.  Create a unique package name in the module's `src` directory, and create a \n    new Java class in that package that extends the \n    [`BaseEditorConfigContributor`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/editor/configuration/BaseEditorConfigContributor.html) \n    class: \n\n4.  Create a component class that implements the `EditorConfigContributor` \n    service:\n\n    ```java\n    @Component(\n        property = {\n\n        },\n\n        service = EditorConfigContributor.class\n    )\n    ```\n\n5.  Add the following imports:\n\n    ```java\n    import com.liferay.portal.kernel.editor.configuration.BaseEditorConfigContributor;\n    import com.liferay.portal.kernel.editor.configuration.EditorConfigContributor;\n    import com.liferay.portal.kernel.json.JSONArray;\n    import com.liferay.portal.kernel.json.JSONFactoryUtil;\n    import com.liferay.portal.kernel.json.JSONObject;\n    import com.liferay.portal.kernel.portlet.RequestBackedPortletURLFactory;\n    import com.liferay.portal.kernel.theme.ThemeDisplay;\n    ```\n\n6.  Specify the editor's name, editor's configuration key, and/or the portlet \n    name(s) where the editor resides. These three properties can be specified \n    independently, or together, in any order. See the \n    [`EditorConfigContributor`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/editor/configuration/EditorConfigContributor.html) interface's Javadoc for more information about \n    the available properties and how to use them. The example configuration \n    below modifies the AlloyEditor's Content Editor, identified by the \n    `contentEditor` configuration key and `alloyeditor` name key. \n    \n    | **Note:** If you're targeting all editors for a portlet, the\n    | `editor.config.key` is not required. For example, if you just want to target\n    | the Web Content portlet's editors, you can provide the configuration below:\n    | \n    | ```java\n    | @Component(\n    | property = {\"editor.name=ckeditor\",\n    | \"javax.portlet.name=com_liferay_journal_web_portlet_JournalPortlet\",\n    | \"service.ranking:Integer=100\"\n    | }\n    | ```\n    \n    Two portlet names are declared (Blogs and Blogs Admin), specifying that the \n    service applies to the content editors in those portlets. Lastly, the \n    configuration overrides the default one by providing a higher \n    [service ranking](/docs/7-2/customization/-/knowledge_base/c/creating-a-custom-osgi-service):\n\n    ```java\n    @Component(\n        property = {\n            \"editor.config.key=contentEditor\", \"editor.name=alloyeditor\",\n            \"javax.portlet.name=com_liferay_blogs_web_portlet_BlogsPortlet\",\n            \"javax.portlet.name=com_liferay_blogs_web_portlet_BlogsAdminPortlet\", \n            \"service.ranking:Integer=100\"\n        },\n\n        service = EditorConfigContributor.class\n    )\n    ```\n\n    | **NOTE:** If you want to create a global configuration that applies to an\n    | editor everywhere it's used, you must create two separate configurations:\n    | one configuration that targets just the editor and a second configuration\n    | that targets the Blogs and Blogs Admin portlets. For example, the two\n    | separate configurations below apply the updates to AlloyEditor everywhere\n    | it's used:\n    | \n    | Configuration one:\n    | ```java\n    | @Component(\n    |     immediate = true,\n    |     property = {\n    |         \"editor.name=alloyeditor\",\n    |         \"service.ranking:Integer=100\"\n    |     },\n    | \n    |     service = EditorConfigContributor.class\n    | )\n    | ```\n    | \n    | Configuration two:\n    | ```java\n    | @Component(\n    |     immediate = true,\n    |     property = {\n    |         \"editor.name=alloyeditor\",\n    |         \"javax.portlet.name=com_liferay_blogs_web_portlet_BlogsPortlet\",\n    |         \"javax.portlet.name=com_liferay_blogs_web_portlet_BlogsAdminPortlet\",\n    |         \"service.ranking:Integer=100\"\n    |     },\n    | \n    |     service = EditorConfigContributor.class\n    | )\n    | ```\n\n7.  Override the `populateConfigJSONObject()` method to provide the custom \n    configuration for the editor. This method updates the original configuration \n    JSON object. It can also Update or delete existing configurations, or any \n    other configuration introduced by another `*EditorConfigContributor`. \n\n    ```java\n    @Override\n    public void populateConfigJSONObject(\n        JSONObject jsonObject, Map<String, Object> inputEditorTaglibAttributes,\n        ThemeDisplay themeDisplay,\n        RequestBackedPortletURLFactory requestBackedPortletURLFactory) {\n\n    }\n    ```\n\n8.  In the `populateConfigJSONObject` method, you must instantiate a \n    [`JSONObject`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/json/JSONObject.html) \n    to hold the current configuration of the editor. For instance, you could \n    use the code snippet below to retrieve the available toolbars for the \n    editor:\n\n    ```java\n    JSONObject toolbars = jsonObject.getJSONObject(\"toolbars\");\n    ```\n\n    | **Note:** This toolbar configuration is only applicable for the AlloyEditor.\n    | If you choose a configuration that is supported by multiple editors, you\n    | could apply it to them all. To do this, you could specify all the editors\n    | (e.g., `\"editor.name=alloyeditor\"`, `\"editor.name=ckeditor\"`,\n    | `ckeditor_bbcode` etc.) in the `@Component` annotation  of your\n    | `EditorConfigContributor` implementation, as you did in step six. Use the\n    | links the bottom of this article to view each editor's configuration\n    | options and requirements.\n\n9.  Now that you've retrieved the toolbar, you can modify it. The example below \n    adds a camera button to the AlloyEditor's Add toolbar. It extracts the *Add* \n    buttons out of the toolbar configuration object as \n    a \n    [`JSONArray`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/json/JSONArray.html), \n    and then adds the button to that `JSONArray`:\n\n    ```java\n    if (toolbars != null) {\n        JSONObject toolbarAdd = toolbars.getJSONObject(\"add\");\n\n        if (toolbarAdd != null) {\n            JSONArray addButtons = toolbarAdd.getJSONArray(\"buttons\");\n\n            addButtons.put(\"camera\");\n        }\n    }\n    ```\n\n    The configuration JSON object is passed to the editor with the modifications \n    you've implemented in the `populateConfigJSONObject` method.\n\n10.  Finally, generate the module's JAR file and copy it to your `deploy`\n     folder. Once the module is installed and activated in the service\n     registry, your new editor configuration is available for use. \n\n@product@ supports several different types of WYSIWYG editors, which include \n(among others):\n\n- [AlloyEditor](https://alloyeditor.com/api/1.5.0/Core.html)\n- [CKEditor](http://docs.ckeditor.com/#!/api/CKEDITOR.config)\n- [TinyMCE](http://www.tinymce.com/wiki.php/Configuration)\n\nMake sure to visit each editor's configuration API to learn what each editor \noffers for configuration settings. \n\n## Related Topics\n\n- [Adding New Behavior to an Editor](/docs/7-2/frameworks/-/knowledge_base/f/adding-new-behavior-to-an-editor)\n- [Modifying the AlloyEditor](/docs/7-2/frameworks/-/knowledge_base/f/alloyeditor)\n- [Adding a WYSIWYG Editor to a Portlet](/docs/7-2/frameworks/-/knowledge_base/f/adding-a-wysiwyg-editor-to-a-portlet)\n"
  },
  {
    "path": "en/developer/frameworks/articles/wysiwyg-editors/04-alloy-editor/01-alloy-editor-intro.markdown",
    "content": "---\nheader-id: alloyeditor\n---\n\n# AlloyEditor\n\n[TOC levels=1-4]\n\nAlloyEditor is a modern WYSIWYG editor built on top of CKEDITOR, designed to \ncreate modern and gorgeous web content. AlloyEditor is the default WYSIWYG \neditor.\n\n![Figure 1: AlloyEditor is the default WYSIWYG editor built on top of CKEditor.](../../../images/alloyeditor-website.png)\n\nThis section shows how to modify the default AlloyEditor configuration to meet \nyour requirements. \n"
  },
  {
    "path": "en/developer/frameworks/articles/wysiwyg-editors/04-alloy-editor/02-adding-buttons-to-the-alloyeditor/01-adding-buttons-to-the-alloy-editor-intro.markdown",
    "content": "---\nheader-id: adding-buttons-to-alloyeditor-toolbars\n---\n\n# Adding Buttons to AlloyEditor's Toolbars\n\n[TOC levels=1-4]\n\nAlloyEditor's toolbars contain several useful functions out-of-the-box. You \nmay, however, want to customize the default configuration to include a button \nyou've created, to add an existing button to a toolbar, or to add an \n[existing CKEditor button that's bundled with @product@'s AlloyEditor](/docs/7-2/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide).\nThe \n[`EditorConfigContributor` interface](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/editor/configuration/EditorConfigContributor.html), \nprovides everything you need to modify an editor's configuration, including \nadding buttons to AlloyEditor's toolbars. \n[CKEditor Configuration settings](https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_config.html) \nthat modify the editor's behavior (excluding UI modifications) can also be \npassed down through this configuration object. \n\nThe `com.liferay.docs.my.button` module is used as an example throughout this \nsection. If you want to use it as a starting point for your own configuration \nor follow along with the articles, you can download the module's zip file from \nthe [Github repo](https://github.com/liferay/liferay-docs/tree/7.1.x/develop/tutorials/code/osgi/modules/com.liferay.docs.my.button). \n"
  },
  {
    "path": "en/developer/frameworks/articles/wysiwyg-editors/04-alloy-editor/02-adding-buttons-to-the-alloyeditor/02-preparing-and-configuring-your-module.markdown",
    "content": "---\nheader-id: creating-the-osgi-module-and-configuring-the-editorconfigcontributor\n---\n\n# Creating the OSGi Module and Configuring the EditorConfigContributor Class\n\n[TOC levels=1-4]\n\nTo add a button to the AlloyEditor's toolbars, you must first create an OSGi \ncomponent class of service type `EditorConfigContributor.class`. Follow these \nsteps to create and configure the OSGi module:\n\n1.  [Create an OSGi module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project), \n    using \n    [Blade's portlet template](/docs/7-2/reference/-/knowledge_base/r/using-the-mvc-portlet-template):\n\n    ```bash\n    blade create -t portlet -p com.liferay.docs.my.button -c \n    MyEditorConfigContributor my-new-button\n    ```\n\n2.  Open the portlet's `build.gradle` file and update the \n    `com.liferay.portal.kernel` `version` to `4.13.1`. This is the version \n    bundled with the @product@ release.\n\n3.  Open the portlet class you created in step one (`MyEditorConfigContributor`) \n    and add the following imports:\n\n    ```java\n    import com.liferay.portal.kernel.editor.configuration.BaseEditorConfigContributor;\n    import com.liferay.portal.kernel.editor.configuration.EditorConfigContributor;\n    import com.liferay.portal.kernel.json.JSONArray;\n    import com.liferay.portal.kernel.json.JSONFactoryUtil;\n    import com.liferay.portal.kernel.json.JSONObject;\n    import com.liferay.portal.kernel.portlet.RequestBackedPortletURLFactory;\n    import com.liferay.portal.kernel.theme.ThemeDisplay;\n    ```\n\n4.  Replace the `@Component` and properties with the properties below:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n          \"editor.name=alloyeditor\",\n          \"service.ranking:Integer=100\"\n        },\n        service = EditorConfigContributor.class  \n    )\n    ```\n\n    This targets AlloyEditor for the configuration and overrides the default \n    service by providing a higher \n    [service ranking](/docs/7-2/customization/-/knowledge_base/c/creating-a-custom-osgi-service). \n    If you want to target a more specific configuration, you can find the \n    available properties in the \n    [`EditorConfigContributor` interface's Javadoc](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/editor/configuration/EditorConfigContributor.html).\n\n5.  Extend `BaseEditorConfigContributor` instead of `GenericPortlet`.\n\n6.  Replace the `doView()` method and contents with the \n    `populateConfigJSONObject()` method shown below:\n\n    ```java\n    @Override\n    public void populateConfigJSONObject(\n    JSONObject jsonObject, Map<String, Object> inputEditorTaglibAttributes,\n    ThemeDisplay themeDisplay,\n    RequestBackedPortletURLFactory requestBackedPortletURLFactory) {\n\n    }\n    ```\n\n7.  Inside the `populateConfigJSONObject()` method, retrieve the AlloyEditor's \n    toolbars: \n\n    ```java\n    JSONObject toolbarsJSONObject = jsonObject.getJSONObject(\"toolbars\");\n\n    if (toolbarsJSONObject == null) {\n            toolbarsJSONObject = JSONFactoryUtil.createJSONObject();\n    }\n    ```\n\n8.  If you're adding a button for one of the \n    [CKEditor plugins bundled with the AlloyEditor](/docs/7-2/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide), \n    add the code below to retrieve the extra plugins and add the plugin to the \n    AlloyEditor's configuration. The example below adds the `clipboard` CKEditor \n    plugin:\n\n    ```java\n    String extraPlugins = jsonObject.getString(\"extraPlugins\");\n\n    if (Validator.isNotNull(extraPlugins)) {\n      extraPlugins = extraPlugins + \",ae_uibridge,ae_autolink,\n      ae_buttonbridge,ae_menubridge,ae_panelmenubuttonbridge,ae_placeholder,\n      ae_richcombobridge,clipboard\";\n    }\n    else {\n      extraPlugins = \"ae_uibridge,ae_autolink,ae_buttonbridge,ae_menubridge,\n      ae_panelmenubuttonbridge,ae_placeholder,ae_richcombobridge,clipboard\";\n    }\n\n    jsonObject.put(\"extraPlugins\", extraPlugins);\n    ```\n\n    AlloyEditor also comes with several plugins to bridge the gap between the \n    CKEditor's UI and the AlloyEditor's UI. These are prefixed with the `ae_` \n    you see above. We recommend that you include them all to ensure \n    compatibility. \n\nThe `*EditorConfigContributor` class is prepared. Now you must choose which \ntoolbar you want to add the button(s) to: the \n[Add Toolbar](/docs/7-2/frameworks/-/knowledge_base/f/adding-a-button-to-the-add-toolbar) \nor one of the \n[Styles Toolbars](/docs/7-2/frameworks/-/knowledge_base/f/adding-a-button-to-a-styles-toolbar). \n\n## Related Topics\n\n- [Adding New Behavior to an Editor](/docs/7-2/frameworks/-/knowledge_base/f/adding-new-behavior-to-an-editor)\n- [CKEditor Plugin Reference Guide](/docs/7-2/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide)\n"
  },
  {
    "path": "en/developer/frameworks/articles/wysiwyg-editors/04-alloy-editor/02-adding-buttons-to-the-alloyeditor/03-adding-buttons-to-the-alloyeditor-add-toolbar.markdown",
    "content": "---\nheader-id: adding-a-button-to-the-add-toolbar\n---\n\n# Adding a Button to the Add Toolbar\n\n[TOC levels=1-4]\n\nThe Add Toolbar appears in the AlloyEditor when your cursor is in the editor and \nyou click the Add button: \n\n![Figure 1: The Add toolbar lets you add content to the editor.](../../../../images/alloyeditor-add-toolbar.png)\n\nFollow these steps to add a button to the AlloyEditor's Add Toolbar:\n \n1.  Inside the `populateConfigJSONObject()` method, retrieve the Add Toolbar:\n\n    ```json\n    JSONObject addToolbar = toolbarsJSONObject.getJSONObject(\"add\");\n    ```\n\n2.  Retrieve the existing Add Toolbar buttons:\n\n    ```json    \n    JSONArray addToolbarButtons = addToolbar.getJSONArray(\"buttons\");\n    ```\n\n3.  Add the button to the existing buttons. Note that the button's name is case \n    sensitive. The example below adds the `camera` button to the Add Toolbar:\n\n    ```json    \n    addToolbarButtons.put(\"camera\");\n    ```\n\n    The camera button is just one of the buttons available by default with\n    AlloyEditor, but they are not all enabled. Here's the full list of available\n    buttons you can add to the Add Toolbar: \n    \n    - camera\n    - embed\n    - hline\n    - image\n    - table\n\n    See [here](https://alloyeditor.com/docs/features/camera.html) for an \n    explanation of each button's features.\n\n4.  Update the AlloyEditor's configuration with the changes you made:\n\n    ```java\n    addToolbar.put(\"buttons\", addToolbarButtons);\n\n    toolbarsJSONObject.put(\"add\", addToolbar);\n\n    jsonObject.put(\"toolbars\", toolbarsJSONObject);\n    ```\n\n5.  [Deploy your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project) \n    and create new content that uses the AlloyEditor---like a blog entry or web \n    content article---to see your new configuration in action! \n\nThe `com.liferay.docs.my.button` module's updated Add Toolbar is shown in the \nfigure below:\n\n![Figure 2: The Updated Add toolbar lets you add pictures from a camera directly to the editor.](../../../../images/alloyeditor-updated-add-toolbar.png)\n\n## Related Topics\n\n- [Adding New Behavior to an Editor](/docs/7-2/frameworks/-/knowledge_base/f/adding-new-behavior-to-an-editor)\n- [Adding a Button to a Styles Toolbar](/docs/7-2/frameworks/-/knowledge_base/f/adding-a-button-to-a-styles-toolbar)\n"
  },
  {
    "path": "en/developer/frameworks/articles/wysiwyg-editors/04-alloy-editor/02-adding-buttons-to-the-alloyeditor/04-adding-buttons-to-the-alloyeditor-styles-toolbar.markdown",
    "content": "---\nheader-id: adding-a-button-to-a-styles-toolbar\n---\n\n# Adding a Button to a Styles Toolbar\n\n[TOC levels=1-4]\n\nA Styles Toolbar appears when content is selected or highlighted in AlloyEditor. \nThere are five Styles toolbars to choose from:\n\n`embedurl`: Appears when embedded content is selected. \n\n![Figure 1: The embed URL Styles toolbar lets you format embedded content in the editor.](../../../../images/alloyeditor-embedurl-toolbar.png)\n\n`image`: Appears when an image is selected.\n\n![Figure 2: The image Styles toolbar lets you format images in the editor.](../../../../images/alloyeditor-image-toolbar.png)\n\n`link`: Appears when a hyperlink is selected.\n\n![Figure 3: The link Styles toolbar lets you format hyperlinks in the editor.](../../../../images/alloyeditor-link-toolbar.png)\n\n`table`: Appears when a table is selected.\n\n![Figure 4: The table Styles toolbar lets you format tables in the editor.](../../../../images/alloyeditor-table-toolbar.png)\n\n`text`: Appears when text is highlighted.\n\n![Figure 5: The text Styles toolbar lets you format highlighted text in the editor.](../../../../images/alloyeditor-text-toolbar.png)\n\nFollow these steps to add a button to one of the Styles toolbars:\n\n1.  Inside the `populateConfigJSONObject()` method, retrieve the Styles toolbar:\n\n    ```java\n    JSONObject stylesToolbar = toolbarsJSONObject.getJSONObject(\"styles\");\n\n    if (stylesToolbar == null) {\n            stylesToolbar = JSONFactoryUtil.createJSONObject();\n    }\n    ```\n\n2.  Retrieve the available selection toolbars:\n\n    ```java\n    JSONArray selectionsJSONArray = stylesToolbar.getJSONArray(\n    \"selections\");\n    ```\n\n3.  Iterate through the selection toolbars, select the one you want to add \n    the button(s) to (`embedurl`, `image`, `link`, `table`, or `text`), retrieve \n    the existing buttons, and add your button. The example below adds the \n    `clipboard` plugin's `Copy`, `Cut`, and `Paste` buttons to the `text` \n    selection toolbar. Note that buttons are case sensitive and may be aliased \n    or not match the name of the plugin. Search the plugin's \n    [`plugin.js` file](/docs/7-1/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide) \n    for `editor.ui.addButton` to find the button's name:\n\n    ```java\n    for (int i = 0; i < selectionsJSONArray.length(); i++) {\n            JSONObject selection = selectionsJSONArray.getJSONObject(i);\n\n            if (Objects.equals(selection.get(\"name\"), \"text\")) {\n                    JSONArray buttons = selection.getJSONArray(\"buttons\");\n\n                    buttons.put(\"Copy\");\n                    buttons.put(\"Cut\");\n                    buttons.put(\"Paste\");\n            }\n    }\n    ```\n\n    The example above adds one of the \n    [CKEditor plugins bundled with @product@'s AlloyEditor](/docs/7-2/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide). \n    There are also several buttons available by default with the AlloyEditor,\n    but they are not all enabled. The full list of existing buttons you can add\n    to the Styles toolbars is shown in the table below, ordered by Toolbar:\n\n    | text | table | image | link |\n    | ---- | ----- | ----- | ---- |\n    | bold | tableHeading | imageCenter | linkEdit |\n    | code | tableRow | imageLeft | |\n    | h1 | tableColumn | imageRight | |\n    | h2 | tableCell | | |\n    | indentBlock | tableRemove | | |\n    | italic | | | |\n    | link | | | |\n    | ol | | | |\n    | outdentBlock | | | |\n    | paragraphLeft | | | |\n    | paragraphRight | | | |\n    | paragraphCenter | | | |\n    | paragraphJustify | | | |\n    | quote | | | |\n    | removeFormat | | | |\n    | strike | | | |\n    | styles | | | |\n    | subscript | | | |\n    | superscript | | | |\n    | twitter | | | |\n    | ul | | | |\n    | underline | | | |\n \n    See [here](https://alloyeditor.com/docs/features/camera.html) for an \n    explanation of each button's features. \n\n4.  Update the AlloyEditor's configuration with the changes you made:\n\n    ```java\n    stylesToolbar.put(\"selections\", selectionsJSONArray);\n\n    toolbarsJSONObject.put(\"styles\", stylesToolbar);\n\n    jsonObject.put(\"toolbars\", toolbarsJSONObject);\n    ```\n\n5.  [Deploy your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project) \n    and create a new piece of content that uses the AlloyEditor---such as a blog \n    entry or web content article---to see your new configuration in action! \n\nThe `com.liferay.docs.my.button` module's updated text styles toolbar is shown \nin the figure below:\n\n![Figure 6: The Updated text styles toolbar lets you copy, cut, and paste text in the editor.](../../../../images/alloyeditor-updated-styles-toolbar.png)\n\n## Related Topics\n\n- [Adding a Button to the Add Toolbar](/docs/7-2/frameworks/-/knowledge_base/f/adding-a-button-to-the-add-toolbar)\n- [CKEditor Plugin Reference Guide](/docs/7-2/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide)\n"
  },
  {
    "path": "en/developer/frameworks/articles/wysiwyg-editors/04-alloy-editor/04-embedding-content/01-embedding-content-in-the-editor-intro.markdown",
    "content": "---\nheader-id: embedding-content-in-the-alloy-editor\n---\n\n# Embedding Content in the AlloyEditor\n\n[TOC levels=1-4]\n\nWhether it's a video from a popular streaming service, or an entertaining \npodcast, embedded content is commonplace on the web. Sharing content from a \nthird party is sometimes required to properly cover a topic. The `EmbedProvider` \nmechanism lets you embed third party content in the AlloyEditor, while writing \nblog posts, web content articles, etc. By default, the `EmbedProvider` mechanism \nis only configured for embedding video content \n(Facebook, Twitch, Vimeo, and YouTube) into the AlloyEditor. This tutorial shows \nhow to include additional video providers, and even add support for additional \ncontent types. \n\nAn `EmbedProvider` requires four pieces of information:\n\n- An ID: The content's ID\n- A Template: The required embed code for the provider\n- A URL Schemes: URL patterns that are supported for the provider template\n- A Type (optional): The provider category\n\nWhen you add a supported URL to the editor, the `EmbedProvider` transforms the \nURL into the embed code. \n\nFollow these steps to create an `*EmbedProvider`:\n\n1.  [Create a module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project) \n    for the Embed Provider.\n\n2.  Add the following dependencies to the `build.gradle` file:\n\n    ```groovy\n    compileOnly group: \"com.liferay\", name:\n    \"com.liferay.frontend.editor.api\", version: \"1.0.1\"\n\n    compileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\",\n    version: \"2.0.0\"\n    ```\n\n3.  Create a component class that implements the `EditorEmbedProvider` service:\n\n    ```java\n    @Component(\n      immediate = true,\n      service = EditorEmbedProvider.class\n    )\n    ```\n\n4.  Optionally set the `type` property to the content's type. If creating a \n    provider for a content type other than video, you can create a new type \n    constant and \n    add a new button for the content type. If you do create your own button, we \n    recommend that you use the existing \n    [embed video button's JSX files](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-editor/frontend-editor-api/src/main/java/com/liferay/frontend/editor/embed) \n    as an example to write your own files. By default, the provider is \n    categorized as `UNKNOWN`. The example configuration below specifies the \n    `VIDEO` type, using a constant provided by the \n    [`EditorEmbedProviderTypeConstants` class](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-api/src/main/java/com/liferay/frontend/editor/embed/EditorEmbedProviderTypeConstants.java):\n\n    ```java\n    @Component(\n      immediate = true,\n      property = \"type=\" + EditorEmbedProviderTypeConstants.VIDEO,\n      service = EditorEmbedProvider.class\n    )\n    ```\n\n5.  Implement the \n    [`EditorEmbedProvider` interface](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-api/src/main/java/com/liferay/frontend/editor/embed/EditorEmbedProvider.java). \n    An example configuration is shown below:\n\n    ```java\n    public class MyEditorEmbedProvider implements EditorEmbedProvider {\n\n    }\n    ```\n\n6.  Add the required imports:\n\n    ```java\n    import com.liferay.frontend.editor.api.embed.EditorEmbedProvider;\n    import com.liferay.frontend.editor.api.embed.EditorEmbedProviderTypeConstants;\n    import com.liferay.petra.string.StringBundler;\n    ```\n\n    Note the `*TypeConstants` import is only needed if you're adding a Video \n    type provider. \n\n7.  Override the `*EmbedProvider`'s `getId()` method to return the ID for the \n    provider. An example configuration is shown below:\n\n    ```java\n    @Override\n    public String getId() {\n    \treturn \"providerName\";\n    }\n    ```\n\n8.  Override the `*EmbedProvider`'s `getTpl()` method to provide the embed \n    template code (usually an iframe for the provider). The example below \n    defines the template for a streaming video service. Note that `{embedId}` is \n    a placeholder for the unique identifier for the embedded content:\n\n    ```java\n    @Override\n    public String getTpl() {\n    \treturn StringBundler.concat(\n    \t\t\"<iframe allow=\\\"autoplay; encrypted-media\\\" allowfullscreen \",\n    \t\t\"height=\\\"315\\\" frameborder=\\\"0\\\" \",\n    \t\t\"src=\\\"https://www.liferaylunarresortstreaming.com/embed/{embedId}?rel=0\\\" \",\n    \t\t\"width=\\\"560\\\"></iframe>\");\n    }\n    ```\n\n9.  Override the `*EmbedProvider`'s `getURLSchemes()` method to return an array \n    of supported URL schemes that have an embedded representation for the \n    provider. URL schemes are defined using a JavaScript regular expression that \n    indicates whether a URL matches the provider. Every URL scheme should \n    contain a single matching group. Matches replace the `{embedId}` placeholder \n    defined in the previous step:\n\n    ```java\n    @Override\n    public String[] getURLSchemes() {\n    \treturn new String[] {\n    \t\t\"https?:\\\\/\\\\/(?:www\\\\.)?liferaylunarresortstreaming.com\\\\/watch\\\\?v=(\\\\S*)$\"\n    \t};\n    }\n    ```\n\n10.  Deploy your module and open an app that uses the AlloyEditor, such as \n     Blogs, and create a new entry. Click the *add button* and select the video \n     button---or your new content type button---and paste the content's URL. \n     Click the *checkmark* to confirm that the URL scheme is supported. The \n     content is embedded into the editor. \n\nNow you know how to embed content in the AlloyEditor. Create a new content \nentry, such as a blog post, and click the embed video button---or the one you \ncreated---and paste the content's URL. \n\n## Related Topics\n\n- [Adding Buttons to AlloyEditor's Toolbars](/docs/7-2/frameworks/-/knowledge_base/f/adding-buttons-to-alloyeditor-toolbars)\n- [Adding New Behavior to an Editor](/docs/7-2/frameworks/-/knowledge_base/f/adding-new-behavior-to-an-editor)\n"
  },
  {
    "path": "en/developer/frameworks/articles/wysiwyg-editors/05-adding-new-editor-behaviors/01-adding-new-behavior-to-an-editor-intro.markdown",
    "content": "---\nheader-id: adding-new-behavior-to-an-editor\n---\n\n# Adding New Behavior to an Editor\n\n[TOC levels=1-4]\n\nYou can select from several different WYSIWYG editors for your users, and each \nis configurable and has its strengths and weaknesses. Configuration alone, \nhowever, doesn't always expose the features you want. In these cases, you can \nprogrammatically access the editor instance to create the editor experience you \nwant, using the `liferay-util:dynamic-include` JavaScript extension point. It \ninjects JavaScript code right after the editor instantiation to configure/change \nthe editor. \n\n| **Note:** By default, the CKEditor strips empty `<i>` tags, such as those used\n| for Font Awesome icons, from published content, when switching between the Code\n| View and the Source View of the editor. You can disable this behavior by using\n| the `ckeditor#additionalResources` or `alloyeditor#additionalResources`\n| [extension points](/docs/7-2/customization/-/knowledge_base/c/wysiwyg-editor-dynamic-includes)\n| to\n| add the code shown below to the editor:\n| \n|     CKEDITOR.dtd.$removeEmpty.i = 0\n\nThe `liferay-util:dynamic-include` extension point is in configurable editors' \nJSP files: it's the gateway for injecting JavaScript into your editor instance. \nIn this article, you'll learn how to use this JavaScript extension point. Follow \nthese steps to inject JavaScript into the WYSIWYG editor to modify its behavior:\n\n1.  Create a JS file containing your editor functionality in a folder that makes \n    sense to reference, since you must register the file in your module. The \n    extension point injects the JavaScript code right after editor \n    initialization. \n\n    Liferay injects JavaScript code for some applications: \n\n    - [creole_dialog_definition.js](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/extension/creole_dialog_definition.js) for the wiki\n    - [creole_dialog_show.js](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/extension/creole_dialog_show.js) also for the wiki\n    - [dialog_definition.js](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/extension/dialog_definition.js) for various applications\n\n    These JS files redefine the fields that show in dialogs, depending on what \n    the selected language (HTML, BBCode, Creole) supports. For example, Creole \n    doesn't support background color in table cells, so the table cells are \n    removed from the options displayed to the user when running in Creole \n    mode. \n\n2.  [Create a module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project) \n    that can register your new JS file and inject it into your editor instance. \n\n3.  Create a unique package name in the module's `src` directory, and create a \n    new Java class in that package. To follow naming conventions, your class \n    name should begin with the editor you're modifying, followed by custom \n    attributes, and ending with *DynamicInclude* (e.g., \n    `CKEditorCreoleOnEditorCreateDynamicInclude.java`). Your Java class should \n    implement the [`DynamicInclude`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/servlet/taglib/DynamicInclude.html) \n    interface. \n\n4.  Directly above the class's declaration, insert the following annotation:\n\n    ```java\n    @Component(immediate = true, service = DynamicInclude.class)\n    ```\n\n    This declares the component's implementation class and starts the module \n    once deployed to Portal. \n\n5.  If you have not yet overridden the abstract methods from `DynamicInclude`, do\n    that now. There are two implemented methods to edit: `include(...)` and \n    `register(...)`.\n\n6.  In the `include(...)` method, retrieve the bundle containing your custom JS \n    file. Retrieve the JS file as a URL and inject its contents into the editor. \n    Here's the code that does this for the `creole_dialog_definition.js` \n    file:\n\n    ```java\n    Bundle bundle = _bundleContext.getBundle();\n\n    URL entryURL = bundle.getEntry(\n        \"/META-INF/resources/html/editors/ckeditor/extension\" +\n            \"/creole_dialog_definition.js\");\n\n    StreamUtil.transfer(entryURL.openStream(), response.getOutputStream());\n    ```\n\n    In the `include(...)` method, you can also retrieve editor configurations\n    and choose the JS file to inject based on the configuration selected by the\n    user. For example, this would be applicable for the use case that was\n    suggested previously dealing with Creole's deficiency with displaying\n    background colors in table cells. Liferay implemented this in the\n    `include(...)` method in the\n    [`CKEditorCreoleOnEditorCreateDynamicInclude`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/java/com/liferay/frontend/editor/ckeditor/web/internal/servlet/taglib/CKEditorCreoleOnEditorCreateDynamicInclude.java)\n    class.\n\n7.  Make sure you've instantiated your bundle's context so you can successfully \n    retrieve your bundle. As a best practice, do this by creating an activation \n    method and then setting the `BundleContext` as a private field. Here's an \n    example: \n\n    ```java\n    @Activate\n    protected void activate(BundleContext bundleContext) {\n        _bundleContext = bundleContext;\n    }\n\n    private BundleContext _bundleContext;\n    ```\n\n    This method uses the `@Activate` annotation, which specifies that it \n    should be invoked once the service component has satisfied its requirements. \n    For this default example, the `_bundleContext` was used in the \n    `include(...)` method. \n\n8.  Now register the editor you're customizing. For example, if you were \n    injecting JS code into the CKEditor's JSP file, the code would look like \n    this:\n\n    ```java\n    dynamicIncludeRegistry.register(\n        \"com.liferay.frontend.editor.ckeditor.web#ckeditor#onEditorCreate\");\n    ```\n\n    This registers the CKEditor into the Dynamic Include registry and specifies \n    that JS code will be injected into the editor once it's created. \n\n    Just as you can configure individual JSP pages to use a specific \n    implementation of the available WYSIWYG editors, you can use those same \n    implementation options for the registration process. Visit the \n    [Editors](@platform-ref@/7.1-latest/propertiesdoc/portal.properties.html#Editors) \n    section of `portal.properties` for more details. For example, to configure \n    the Creole implementation of the CKEditor, you could use the following \n    key:\n\n        \"com.liferay.frontend.editor.ckeditor.web#ckeditor_creole#onEditorCreate\"\n\nThat's it! The JS code that you created is now injected into the editor instance \nyou've specified. You're now able to use JavaScript to add new behavior to your \n@product@ supported WYSIWYG editor! \n\n## Related Topics\n\n- [Adding New Behavior to an Editor](/docs/7-2/frameworks/-/knowledge_base/f/adding-new-behavior-to-an-editor)\n- [Embedding Portlets in Themes](/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes)\n- [Portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets)\n"
  },
  {
    "path": "en/developer/frameworks/build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"Development Framework articles\" basedir=\".\">\n\t<property name=\"project.dir\" value=\"../../../../liferay-docs\" />\n\n\t<import file=\"../build.xml\" />\n\n\t<property name=\"doc.dir\" value=\"frameworks\"/>\n\t<property name=\"doc.type\" value=\"article\"/>\n\n</project>\n"
  },
  {
    "path": "en/developer/reference/articles/01-intro/01-reference-intro.markdown",
    "content": "---\nheader-id: developer-reference\n---\n\n# Developer Reference\n\n[TOC levels=1-4]\n\nThis developer reference contains lists of options for various APIs. The actual\nAPI reference is stored on [docs.liferay.com](https://docs.liferay.com). Here,\nyou'll find higher level descriptions of those APIs, lists of tag libraries,\ndescriptions of Gradle and Maven plugins, and much more. \n\nOne highlight here is a complete description of Liferay's development tooling.\nThis includes not only our Blade CLI (a tool that bridges the gap between Gradle\nand Maven, bringing archetype-like functionality to Liferay Gradle projects),\nbut also our plugins for IntelliJ and Eclipse, not to mention our Maven or\nGradle-based Liferay Workspace. It also includes a complete description of our\nJS Generator, which helps front-end developers create pure JavaScript widgets. \n\n"
  },
  {
    "path": "en/developer/reference/articles/02-breaking-changes.markdown",
    "content": "---\nheader-id: breaking-changes\n---\n\n# Breaking Changes\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/liferay-internals/reference/7-2-breaking-changes.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThis document presents a chronological list of changes that break existing\nfunctionality, APIs, or contracts with third party Liferay developers or users.\nWe try our best to minimize these disruptions, but sometimes they are\nunavoidable.\n\nHere are some of the types of changes documented in this file:\n\n- Functionality that is removed or replaced\n- API incompatibilities: Changes to public Java or JavaScript APIs\n- Changes to context variables available to templates\n- Changes in CSS classes available to Liferay themes and portlets\n- Configuration changes: Changes in configuration files, like\n  `portal.properties`, `system.properties`, etc.\n- Execution requirements: Java version, J2EE Version, browser versions, etc.\n- Deprecations or end of support: For example, warning that a certain\n  feature or API will be dropped in an upcoming version.\n- Recommendations: For example, recommending using a newly introduced API that\n  replaces an old API, in spite of the old API being kept in Liferay Portal for\n  backwards compatibility.\n\n## Breaking Changes List\n\n### Removed Support for JSP Templates in Themes\n- **Date:** 2018-Nov-14\n- **JIRA Ticket:** [LPS-87064](https://issues.liferay.com/browse/LPS-87064)\n\n#### What changed?\n\nThemes can no longer leverage JSP templates. Also, related logic has been removed from the public APIs `com.liferay.portal.kernel.util.ThemeHelper` and `com.liferay.taglib.util.ThemeUtil`.\n\n#### Who is affected?\n\nThis affects anyone who has themes using JSP templates or is using the removed methods.\n\n#### How should I update my code?\n\nIf you have a theme using JSP templates, consider migrating it to FreeMarker.\n\n#### Why was this change made?\n\nJSP is not a real template engine and is rarely used. FreeMarker is the recommended template engine moving forward.\n\nThe removal of JSP templates allows for an increased focus on existing and new template engines.\n\n---------------------------------------\n\n### Lodash Is No Longer Included by Default\n- **Date:** 2018-Nov-27\n- **JIRA Ticket:** [LPS-87677](https://issues.liferay.com/browse/LPS-87677)\n\n#### What changed?\n\nPreviously, Lodash was included in every page by default and made available through the global `window._` and scoped `AUI._` variables. Lodash is no longer included by default and those variables are now undefined.\n\n#### Who is affected?\n\nThis affects any developer who used the `AUI._` or `window._` variables in their custom scripts.\n\n#### How should I update my code?\n\nYou should provide your own Lodash version for your custom developments to use following any of the possible strategies to add third party libraries.\n\nAs a temporary measure, you can bring back the old behavior by setting the *Enable Lodash* property in Liferay Portal's *Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr; *Third Party* &rarr; *Lodash* to `true`.\n\n#### Why was this change made?\n\nThis change was made to avoid bundling and serving additional library code on every page that was mostly unused and redundant.\n\n---------------------------------------\n\n### Moved Two Staging Properties to OSGi Configuration\n- **Date:** 2018-Dec-12\n- **JIRA Ticket:** [LPS-88018](https://issues.liferay.com/browse/LPS-88018)\n\n#### What changed?\n\nTwo Staging properties have been moved from `portal.properties` to an OSGi configuration named `ExportImportServiceConfiguration.java` in the `export-import-service` module.\n\n#### Who is affected?\n\nThis affects anyone using the following portal properties:\n\n- `staging.delete.temp.lar.on.failure`\n- `staging.delete.temp.lar.on.success`\n\n#### How should I update my code?\n\nInstead of overriding the `portal.properties` file, you can manage the properties from Portal's configuration administrator. This can be accessed by navigating to Liferay Portal's *Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr; *Infrastructure* &rarr; *Export/Import* and editing the settings there.\n\nIf you would like to include the new configuration in your application, follow the instructions for [making applications configurable](https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-1/making-applications-configurable).\n\n#### Why was this change made?\n\nThis change was made as part of the modularization efforts to ease portal configuration changes.\n\n---------------------------------------\n\n### Remove Link Application URLs to Page Functionality\n- **Date:** 2018-Dec-14\n- **JIRA Ticket:** [LPS-85948](https://issues.liferay.com/browse/LPS-85948)\n\n#### What changed?\n\nThe *Link Portlet URLs to Page* option in the Look and Feel portlet was marked as deprecated in Liferay Portal 7.1, allowing the user to show and hide the option through a configuration property. In Liferay Portal 7.2, this has been removed and can no longer be configured.\n\n#### Who is affected?\n\nThis affects administrators who used the option in the UI and developers who leveraged the option in the portlet.\n\n#### How should I update my code?\n\nYou should update any portlets leveraging this feature, since any preconfigured reference to the property is ignored in the portal.\n\n#### Why was this change made?\n\nA limited number of portlets use this property; there are better ways to achieve the same results.\n\n---------------------------------------\n\n### Moved TermsOfUseContentProvider out of kernel.util\n- **Date:** 2019-Jan-07\n- **JIRA Ticket:** [LPS-88869](https://issues.liferay.com/browse/LPS-88869)\n\n#### What changed?\n\nThe `TermsOfUseContentProvider` interface's package changed:\n\n`com.liferay.portal.kernel.util` &rarr; `com.liferay.portal.kernel.term.of.use`\n\nThe `TermsOfUseContentProviderRegistryUtil` class' name and package changed:\n\n`TermsOfUseContentProviderRegistryUtil` &rarr; `TermsOfUseContentProviderUtil`\n\nand `com.liferay.portal.kernel.util` &rarr; `com.liferay.portal.internal.terms.of.use`\n\nThe logic of getting `TermsOfUseContentProvider` was also changed. Instead of always returning the first service registered, which is random and depends on the order of registered services, the `TermsOfUseContentProvider` service is tracked and updated with `com.liferay.portal.kernel.util.ServiceProxyFactory`. As a result, the `TermsOfUseContentProvider` now respects service ranking.\n\n#### Who is affected?\n\nThis affects anyone who used `com.liferay.portal.kernel.util.TermsOfUseContentProviderRegistryUtil` to lookup the `com.liferay.portal.kernel.util.TermsOfUseContentProvider` service.\n\n#### How should I update my code?\n\nIf `com.liferay.portal.kernel.util.TermsOfUseContentProvider` is used, update the import package name. If there is any usage in `portal-web`, update `com.liferay.portal.kernel.util.TermsOfUseContentProviderRegistryUtil` to `com.liferay.portal.kernel.term.of.use.TermsOfUseContentProviderUtil`. Remove usages of `com.liferay.portal.kernel.util.TermsOfUseContentProviderRegistryUtil` in modules and use the `@Reference` annotation to fetch the `com.liferay.portal.kernel.term.of.use.TermsOfUseContentProvider` service instead.\n\n#### Why was this change made?\n\nThis is one of several steps to clean up kernel provider interfaces to reduce the chance of package version lock down.\n\n---------------------------------------\n\n### Removed HibernateConfigurationConverter and Converter\n- **Date:** 2019-Jan-07\n- **JIRA Ticket:** [LPS-88870](https://issues.liferay.com/browse/LPS-88870)\n\n#### What changed?\n\nThe interface `com.liferay.portal.kernel.util.Converter` and its implementation `com.liferay.portal.spring.hibernate.HibernateConfigurationConverter` were removed.\n\n#### Who is affected?\n\nThis removes the support of generating customized `portlet-hbm.xml` files implemented by `HibernateConfigurationConverter`. Refer to [LPS-5363](https://issues.liferay.com/browse/LPS-5363) for more information.\n\n#### How should I update my code?\n\nYou should remove usages of `HibernateConfigurationConverter`. Make sure the generated `portlet-hbm.xml` is accurate.\n\n#### Why was this change made?\n\nThis is one of several steps to clean up kernel provider interfaces to reduce the chance of package version lock down.\n\n---------------------------------------\n\n### Switched to Use JDK Function and Supplier\n- **Date:** 2019-Jan-08\n- **JIRA Ticket:** [LPS-88911](https://issues.liferay.com/browse/LPS-88911)\n\n#### What changed?\n\nThe `Function` and `Supplier` interfaces in package `com.liferay.portal.kernel.util` were removed. Their usages were replaced with `java.util.function.Function` and `java.util.function.Supplier`.\n\n#### Who is affected?\n\nThis affects anyone who implemented the `Function` and `Supplier` interfaces in package `com.liferay.portal.kernel.util`.\n\n#### How should I update my code?\n\nYou should replace usages of `com.liferay.portal.kernel.util.Function` and `com.liferay.portal.kernel.util.Supplier` with `java.util.function.Function` and `java.util.function.Supplier`, respectively.\n\n#### Why was this change made?\n\nThis is one of several steps to clean up kernel provider interfaces to reduce the chance of package version lock down.\n\n---------------------------------------\n\n### Deprecated com.liferay.portal.service.InvokableService Interface\n- **Date:** 2019-Jan-08\n- **JIRA Ticket:** [LPS-88912](https://issues.liferay.com/browse/LPS-88912)\n\n#### What changed?\n\nThe `InvokableService` and `InvokableLocalService` interfaces in package `com.liferay.portal.kernel.service` were removed.\n\n#### Who is affected?\n\nThis affects anyone who used `InvokableService` and `InvokableLocalService` in package `com.liferay.portal.kernel.service`.\n\n#### How should I update my code?\n\nYou should remove usages of `InvokableService` and `InvokableLocalService`. Make sure to use the latest version of Service Builder to generate implementations for services in case there is any compile errors after removal.\n\n#### Why was this change made?\n\nThis is one of several steps to clean up kernel provider interfaces to reduce the chance of package version lock down.\n\n---------------------------------------\n\n### Dropped Support of ServiceLoaderCondition\n- **Date:** 2019-Jan-08\n- **JIRA Ticket:** [LPS-88913](https://issues.liferay.com/browse/LPS-88913)\n\n#### What changed?\n\nThe interface `ServiceLoaderCondition` and its implementation `DefaultServiceLoaderCondition` in package `com.liferay.portal.kernel.util` were removed.\n\n#### Who is affected?\n\nThis affects anyone using `ServiceLoaderCondition` and `DefaultServiceLoaderCondition`.\n\n#### How should I update my code?\n\nYou should remove usages of `ServiceLoaderCondition`. Update usages of `load` methods in `com.liferay.portal.kernel.util.ServiceLoader` according to the updated method signatures.\n\n#### Why was this change made?\n\nThis is one of several steps to clean up kernel provider interfaces to reduce the chance of package version lock down.\n\n---------------------------------------\n\n### Switched to Use JDK Predicate\n- **Date:** 2019-Jan-14\n- **JIRA Ticket:** [LPS-89139](https://issues.liferay.com/browse/LPS-89139)\n\n#### What changed?\n\nThe interface `com.liferay.portal.kernel.util.PredicateFilter` was removed and replaced with `java.util.function.Predicate`. As a result, the following implementations were removed:\n\n- `com.liferay.portal.kernel.util.AggregatePredicateFilter`\n- `com.liferay.portal.kernel.util.PrefixPredicateFilter`\n- `com.liferay.portal.kernel.portlet.JavaScriptPortletResourcePredicateFilter`\n- `com.liferay.dynamic.data.mapping.form.values.query.internal.model.DDMFormFieldValuePredicateFilter`\n\nThe `com.liferay.portal.kernel.util.ArrayUtil_IW` class was regenerated.\n\n#### Who is affected?\n\nThis affects anyone who used `PredicateFilter`, `AggregatePredicateFilter`, `PrefixPredicateFilter`, `JavaScriptPortletResourcePredicateFilter`, and `DDMFormFieldValuePredicateFilter`.\n\n#### How should I update my code?\n\nYou should replace usages of `com.liferay.portal.kernel.util.PredicateFilter` with `java.util.function.Predicate`. Additionally, remove usages of `AggregatePredicateFilter`, `PrefixPredicateFilter`, `JavaScriptPortletResourcePredicateFilter`, and `DDMFormFieldValuePredicateFilter`.\n\n#### Why was this change made?\n\nThis is one of several steps to clean up kernel provider interfaces to reduce the chance of package version lock down.\n\n---------------------------------------\n\n### Removed Unsafe Functional Interfaces in Package com.liferay.portal.kernel.util\n- **Date:** 2019-Jan-15\n- **JIRA Ticket:** [LPS-89223](https://issues.liferay.com/browse/LPS-89223)\n\n#### What changed?\n\nThe `com.liferay.portal.osgi.util.test.OSGiServiceUtil` class was removed. Also, the following interfaces were removed from the `com.liferay.portal.kernel.util` package:\n\n- `UnsafeConsumer`\n- `UnsafeFunction`\n- `UnsafeRunnable`\n\n#### Who is affected?\n\nThis affects anyone using the class/interfaces mentioned above.\n\n#### How should I update my code?\n\nThe `com.liferay.portal.osgi.util.test.OSGiServiceUtil` class has been deprecated since Liferay Portal 7.1. If usages for this class still exist, replace it with its direct replacement: `com.liferay.osgi.util.service.OSGiServiceUtil`. Replace usages of `UnsafeConsumer`, `UnsafeFunction` and `UnsafeRunnable` with their corresponding interfaces in package `com.liferay.petra.function`.\n\n#### Why was this change made?\n\nThis is one of several steps to clean up kernel provider interfaces to reduce the chance of package version lock down.\n\n---------------------------------------\n\n### Deprecated NTLM in Portal Distribution\n- **Date:** 2019-Jan-21\n- **JIRA Ticket:** [LPS-88300](https://issues.liferay.com/browse/LPS-88300)\n\n#### What changed?\n\nNTLM modules have been moved from the `portal-security-sso` project to a new project named `portal-security-sso-ntlm`. This new project is deprecated and available to download from Liferay Marketplace.\n\n#### Who is affected?\n\nThis affects anyone using NTLM as an authentication system.\n\n#### How should I update my code?\n\nIf you want to continue using NTLM as an authentication system, you must download the corresponding modules from Liferay Marketplace. Alternatively, you can migrate to Kerberos (recommended), which requires no changes and is compatible with Liferay Portal 7.0+.\n\n#### Why was this change made?\n\nThis change was made to avoid using an old proprietary solution (NTLM). Kerberos is now recommended, which is a standard protocol and a more secure method of authentication compared to NTLM.\n\n---------------------------------------\n\n### Deprecated OpenID in Portal Distribution\n- **Date:** 2019-Jan-21\n- **JIRA Ticket:** [LPS-88906](https://issues.liferay.com/browse/LPS-88906)\n\n#### What changed?\n\nOpenID modules have been moved to a new project named `portal-security-sso-openid`. This new project is deprecated and available to download from Liferay Marketplace.\n\n#### Who is affected?\n\nThis affects anyone using OpenID as an authentication system.\n\n#### How should I update my code?\n\nIf you want to continue using OpenID as an authentication system, you must download the corresponding module from Liferay Marketplace. Alternatively, you should migrate to OpenID Connect, available on Liferay Portal Distribution.\n\n#### Why was this change made?\n\nThis change was made to avoid using a deprecated solution (OpenID). OpenID Connect is now recommended, which is a more secure method of authentication since it runs on top of OAuth.\n\n---------------------------------------\n\n### Deprecated Google SSO in Portal Distribution\n- **Date:** 2019-Jan-21\n- **JIRA Ticket:** [LPS-88905](https://issues.liferay.com/browse/LPS-88905)\n\n#### What changed?\n\nGoogle SSO modules have been moved from the `portal-security-sso` project to a new project named `portal-security-sso-google`. This new project is deprecated and available to download from Liferay Marketplace.\n\n#### Who is affected?\n\nThis affects anyone using Google SSO as an authentication system.\n\n#### How should I update my code?\n\nIf you want to continue using Google SSO as an authentication system, you must download the corresponding module from Liferay Marketplace. Alternatively, you can use OpenID Connect.\n\n#### Why was this change made?\n\nThis change was made to avoid using an old solution for authentication (Google SSO). OpenID Connect is the recommended specification to use Google implementation for authentication.\n\n---------------------------------------\n\n### Updated AlloyEditor v2.0 Includes New Major Version of React\n- **Date:** 2019-Feb-04\n- **JIRA Ticket:** [LPS-90079](https://issues.liferay.com/browse/LPS-90079)\n\n#### What changed?\n\nAlloyEditor was upgraded to version 2.0.0, which includes a major upgrade from React v15 to v16.\n\nThe `React.createClass` was [deprecated in React v15.5.0](https://reactjs.org/blog/2017/04/07/react-v15.5.0.html) (April 2017) and [removed in React v16.0.0](https://reactjs.org/blog/2017/09/26/react-v16.0.html) (September 2017). All the buttons bundled with AlloyEditor have been updated to use the ES6 class syntax instead of `React.createClass`.\n\n#### Who is affected?\n\nThis affects anyone who built their own buttons using `React.createClass`. The `createClass` function is no longer available, and attempts to access it at runtime will trigger an error.\n\n#### How should I update my code?\n\nYou should update your code in one of two ways:\n\n- Port custom buttons from the `React.createClass` API to use the ES6 `class` API, as described in [the React documentation](https://reactjs.org/docs/react-component.html). For example, see the changes made in moving to an [ES6 class-based button](https://github.com/liferay/alloy-editor/blob/b082c312179ae6626cb2ddcc04ad3ebc5b355e1b/src/components/buttons/button-ol.jsx) from [the previous `createClass`-based implementation](https://github.com/liferay/alloy-editor/blob/2826ab9ceabe17c6ba0d38985baf8a787c23db43/src/ui/react/src/components/buttons/button-ol.jsx).\n\n- Provide a compatibility adapter. The [create-react-class package](https://www.npmjs.com/package/create-react-class) (described [here](https://reactjs.org/docs/react-without-es6.html)) can be injected into the page to restore the `createClass` API.\n\n#### Why was this change made?\n\nThis change was made to use a newer major version of React, which brings performance and compatibility improvements and reduces the bundle size by removing deprecated APIs.\n\n---------------------------------------\n\n### Deprecated dl.tabs.visible property\n- **Date:** 2019-Apr-10\n- **JIRA Ticket:** [LPS-93948](https://issues.liferay.com/browse/LPS-93948)\n\n#### What changed?\n\nThe `dl.tabs.visible` property let users toggle the visibility of a Documents and Media widget's navigation tabs when placed on a widget page. This configuration option has been removed, so the navigation tab will never appear on widget pages.\n\n#### Who is affected?\n\nThis affects anyone who set the `dl.tabs.visible` property to `true`.\n\n#### How should I update my code?\n\nNo code changes are necessary.\n\n#### Why was this change made?\n\nDocuments & Media has been reviewed from a UX perspective, and removing the navigation tabs in widget pages was part of a UI clean up process.\n\n---------------------------------------\n\n### Move the User Menu out of the Product Menu\n- **Date:** 2019-Apr-19\n- **JIRA Ticket:** [LPS-87868](https://issues.liferay.com/browse/LPS-87868)\n\n#### What changed?\n\nThe User Menu was removed from the Product Menu, and the user menu entries were moved to the new Personal Menu, a dropdown menu triggered by the user avatar.\n\n#### Who is affected?\n\nThis affects anyone who has customized the User Menu section of the Product Menu.\n\n#### How should I update my code?\n\nIf you would like to keep your custom user menu entries and have them available in the Personal Menu, you need to implement the `PersonalMenuEntry` interface. All panel apps registered with the `PanelCategoryKeys.USER`, `PanelCategoryKeys.USER_MY_ACCOUNT`, and `PanelCategoryKeys.USER_SIGN_OUT` panel category keys should be converted to `PersonalMenuEntry`.\n\n#### Why was this change made?\n\nProduct navigation has been reviewed from a UX perspective, and removing the User Menu from the Product Menu and splitting the menu to its own provides a better user experience.\n\n---------------------------------------\n\n### Removed Hong Kong and Macau from the List of Countries\n- **Date:** 2019-Apr-26\n- **JIRA Ticket:** [LPS-82203](https://issues.liferay.com/browse/LPS-82203)\n\n#### What changed?\n\nHong Kong and Macau have been removed from the list of countries and listed as regions of China as Xianggang (region code: CN-91) and Aomen (region code: CN-92), respectively.\n\n#### Who is affected?\n\nThis affects anyone who used Hong Kong or Macau in their addresses.\n\n#### How should I update my code?\n\nNo code changes are necessary. However, if you have hardcoded the `countryId` of Hong Kong and Macau in your code, they should be updated to China's `countryId`. References to Hong Kong and Macau should be done with their corresponding `regionId`.\n\n#### Why was this change made?\n\nAfter the handover of Hong Kong in 1997 and of Macau in 1999, Hong Kong and Macau are now the special administrative regions of China.\n\n---------------------------------------\n\n### JGroups Was Upgraded From 3.6.16 to 4.1.1\n- **Date:** 2019-Aug-15\n- **JIRA Ticket:** [LPS-97897](https://issues.liferay.com/browse/LPS-97897)\n\n#### What changed?\n\nJGroups was upgraded from version 3.6.16 to 4.1.1.\n\n#### Who is affected?\n\nThis affects anyone using Cluster Link.\n\n#### How should I update my code?\n\nThe `cluster.link.channel.properties.*` property in `portal.properties` no longer accepts a connection string as a value; it now requires a file path to a configuration XML file. Some of the protocol properties from 3.6.16 are removed and no longer parsed by 4.1.1; you should update the protocol properties accordingly.\n\n#### Why was this change made?\n\nThis upgrade was made to fix a security issue.\n\n---------------------------------------\n\n### Liferay `AssetEntries_AssetCategories` Is No Longer Used\n- **Date:** 2019-Sep-11\n- **JIRA Tickets:** [LPS-99973](https://issues.liferay.com/browse/LPS-99973),\n[LPS-76488](https://issues.liferay.com/browse/LPS-76488)\n\n#### What changed?\n\nPreviously, Liferay used a mapping table and a corresponding interface for the relationship between `AssetEntry` and `AssetCategory` in `AssetEntryLocalService` and `AssetCategoryLocalService`. This mapping table and the corresponding interface have been replaced by the table `AssetEntryAssetCategoryRel` and the service `AssetEntryAssetCategoryRelLocalService`.\n\n#### Who is affected?\n\nThis affects any content or code that relies on calling the old interfaces for the `AssetEntries_AssetCategories` relationship, through the `AssetEntryLocalService` and `AssetCategoryLocalService`.\n\n#### How should I update my code?\n\nUse the new methods in `AssetEntryAssetCategoryRelLocalService` to retrieve the same data as before. The method signatures haven't changed; they have just been relocated to a different service.\n\n**Example**\n\nOld way:\n\n```java\nList<AssetEntry> entries =\nAssetEntryLocalServiceUtil.getAssetCategoryAssetEntries(categoryId);\n\nfor (AssetEntry entry: entries) {\n  ...\n}\n```\n\nNew way:\n\n```java\nlong[] assetEntryPKs =\n_assetEntryAssetCategoryRelLocalService.getAssetEntryPrimaryKeys(assetCategoryId);\n\nfor (long assetEntryPK: assetEntryPKs) {\n  AssetEntry = _assetEntryLocalService.getEntry(assetEntryPK);\n  ...\n}\n\n...\n\n@Reference\nprivate AssetEntryAssetCategoryRelLocalService _assetEntryAssetCategoryRelLocalService;\n\n@Reference\nprivate AssetEntryLocalService _assetEntryLocalService;\n```\n\n#### Why was this change made?\n\nThis change was made due to changes resulting from [LPS-76488](https://issues.liferay.com/browse/LPS-76488), which let developers control the order of a list of assets for a given category.\n\n---------------------------------------\n\n### Auto Tagging Must Be Reconfigured Manually\n- **Date: 2019-Oct-2**\n- **JIRA Ticket:** [LPS-97123](https://issues.liferay.com/browse/LPS-97123)\n\n#### What changed?\n\nAuto Tagging configurations were renamed and reorganized. There's no longer an automatic upgrade process, so you must reconfigure Auto Tagging manually.\n\n#### Who is affected?\n\nThis affects DXP 7.2 installations that are upgraded to SP1 and have Auto Tagging configured and enabled.\n\n#### How should I update my code?\n\nYou must reconfigure Auto Tagging through System Settings (please see the [official documentation](https://help.liferay.com/hc/en-us/articles/360029041551-Configuring-Asset-Auto-Tagging) for details). Any code referencing the old configuration interfaces must be updated to use the new ones.\n\n#### Why was this change made?\n\nThis change unifies the previously split configuration interfaces, improving the user experience.\n\n---------------------------------------\n\n### Blogs Image Properties Were Moved to System Settings\n- **Date: 2019-Oct-2**\n- **JIRA Ticket:** [LPS-95298](https://issues.liferay.com/browse/LPS-95298)\n\n#### What changed?\n\nBlogs image configuration was moved from `portal.properties` to System Settings. There's no automatic upgrade process, so custom Blogs image properties must be reconfigured manually.\n\n#### Who is affected?\n\nThis affects DXP 7.2 installations that are upgraded to SP1 and have custom values for the `blogs.image.max.size` and `blogs.image.extensions` properties.\n\n#### How should I update my code?\n\nIf you would like to keep your custom Blogs image property values, you must reconfigure them through the System Settings under *Configuration* &rarr; *Blogs* &rarr; *File Uploads*. Any code referencing the old properties must be updated to use the new configuration interfaces.\n\n#### Why was this change made?\n\nThis change was made so Blogs image properties can be configured without a restart.\n\n---------------------------------------\n\n### Removed Cache Bootstrap Feature\n- **Date:** 2020-Jan-8\n- **JIRA Ticket:** [LPS-96563](https://issues.liferay.com/browse/LPS-96563)\n\n#### What changed?\n\nThe cache bootstrap feature has been removed. These properties can no longer be used to enable/configure cache bootstrap:\n\n`ehcache.bootstrap.cache.loader.enabled`,\n`ehcache.bootstrap.cache.loader.properties.default`,\n`ehcache.bootstrap.cache.loader.properties.${specific.cache.name}`.\n\n#### Who is affected?\n\nThis affects anyone using the properties listed above.\n\n#### How should I update my code?\n\nThere's no direct replacement for the removed feature. If you have code that depends on it, you must implement it yourself.\n\n#### Why was this change made?\n\nThis change was made to avoid security issues.\n\n---------------------------------------"
  },
  {
    "path": "en/developer/reference/articles/02-cdi-portlet-predefined-beans.markdown",
    "content": "---\nheader-id: cdi-portlet-predefined-beans\n---\n\n# CDI Portlet Predefined Beans\n\n[TOC levels=1-4]\n\n@product@ provides injectable portlet artifacts for\n[CDI](/docs/7-2/frameworks/-/knowledge_base/f/cdi-dependency-injection) called\nPortlet Predefined Beans, as specified by [JSR 362](https://jcp.org/en/jsr/detail?id=362). \nThere are two types of predefined\nbeans:\n\n-   Portlet Request Scoped Beans\n    ([`@PortletRequestScoped`](https://docs.liferay.com/portlet | - | api/3.0/javadocs/javax/portlet/annotations/PortletRequestScoped.html)) \n\n-   Dependent Scoped Beans ([`@Dependent`\n    scoped](https://docs.oracle.com/javaee/7/api/javax/enterprise/context/Dependent.html))\n\nThe table below describes these attributes for each bean: \n\n**Artifact:** The bean's type. \n\n**Bean EL Name:** Expression Language (EL) name for accessing the bean in a JSP \nor JSF page. \n\n**Qualifier:** Annotation applied to the bean for defining and selecting a bean\nimplementation. \n\n**Valid during (phase):** The \n[portlet phases](/docs/7-2/frameworks/-/knowledge_base/f/portlets)\nin which the bean is valid. \n\n## Portlet Request Scoped Beans\n\nThese beans have the `@PortletRequestScoped` annotation. Here are their artifact\ntypes, bean EL names, and annotation qualifiers, along with their valid portlet\nphases. \n\nTable 1: Portlet Request Scoped Beans[^1]\n\n| Artifact | Bean EL Name | Qualifier | Valid during | \n| -------- | ------------ | --------- | ------------ |\n| `PortletConfig` | `portletConfig` | - | all | \n| `PortletRequest` | `portletRequest` | - | all | \n| `PortletResponse` | `portletResponse` | - | all | \n| `ActionRequest` | `actionRequest` | - | action |\n| `ActionResponse` | `actionResponse` | - | action | \n| `HeaderRequest` | `headerRequest` | - | header | \n| `HeaderResponse` | `headerResponse` | - | header | \n| `RenderRequest` | `renderRequest` | - | render | \n| `RenderResponse` | `renderResponse` | - | render | \n| `EventRequest` | `eventRequest` | - | event | \n| `EventResponse` | `eventResponse` | - | event | \n| `ResourceRequest` | `resourceRequest` | - | resource | \n| `ResourceResponse` | `resourceResponse` | - | resource | \n| `StateAwareResponse` | `stateAwareResponse` | - | action, event |\n| `MimeResponse` | `mimeResponse` | - | header, render, resource | \n| `ClientDataRequest` | `clientDataRequest` | - | action, resource | \n| `RenderParameters` | `renderParams` | - | all |\n| `MutableRenderParameters` | `mutableRenderParams` | - | action, event | \n| `ActionParameters` | `actionParams` | - | action | \n| `ResourceParameters` | `resourceParams` | - | resource | \n| `PortletContext` | `portletContext` | - | all |\n| `PortletMode` | `portletMode` | - | all |\n| `WindowState` | `windowState` | - | all |\n| `PortletPreferences` | `portletPreferences` | - | all |\n| `Cookies(List<Cookie>)` | `cookies` | - | all |\n| `PortletSession` | `portletSession` | - | all | \n| `Locales(List<Locale>)` | `locales` | - | all |\n\n## Dependent Scoped Beans \n\nThese beans use the `@Dependent` scope. They're of type `java.lang.String`,\nwhich is `final`. This disqualifies them from being proxied. To prevent using\ndependent scoped beans in a scope broader than their original scope, you should\nonly inject them into `@PortletRequestScoped` beans. \n\nTable 2: Dependent Scoped Beans[^2]\n\n| Artifact | Bean EL Name | Qualifier | Valid during |\n| -------- | ------------ | --------- | ------------ |\n| `Namespace` (String) | `namespace` | `@Namespace` | all |\n| `ContextPath` (String) | `contextPath` | `@ContextPath` | all |\n| `WindowID` (String) | `windowId` | `@WindowId` | all |\n| `Portlet name` (String) | `portletName` | `@PortletName` | all |\n\n[^1]: Martin Scott Nicklous, Java&trade; Portlet Specification 3.0, page 122. \n \n[^2]: Martin Scott Nicklous, Java&trade; Portlet Specification 3.0, page 123. \n\n## Related Topics\n\n[CDI Dependency Injection](/docs/7-2/frameworks/-/knowledge_base/f/cdi-dependency-injection)\n"
  },
  {
    "path": "en/developer/reference/articles/02-classes-moved/01-classes-moved-from-portal-service-jar-intro.markdown",
    "content": "---\nheader-id: classes-moved-from-portal-service-jar\n---\n\n# Classes Moved From portal-service.jar\n\n[TOC levels=1-4]\n\nTo leverage the benefits of modularization, many classes from <em>portal-service.jar</em>\nhave been moved into application and framework API modules. The table below\nprovides details about these classes and the modules they've moved to. Package\nchanges and each module's group, artifact ID, and version are listed, to\nfacilitate \n<a href=\"/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies\">configuring dependencies</a>. \n\n<style>\ntable, th, td {\n    border: 1px solid black;\n    border-collapse: collapse;\n}\nth, td {\n    padding: 5px;\n    text-align: left;\n}\ncaption {\n    text-align: left;\n}\n</style>\n<table style=\"width:100%\">\n\n  <caption>\n    <b>Classes Moved from portal-service.jar to Modules</b>\n\t<p>\n      This information was generated based on comparing classes in\n      <em>liferay-portal-src-6.2-ce-ga6</em> to classes in <em>liferay-ce-portal-src-7.2.0-ga1</em>.\n    </p>\n  </caption>\n  <tr>\n    <th>Class</th>\n    <th>Package</th>\n    <th>Group ID, Artifact ID,<br>\n        and Version</th>\n  </tr>\n\n  <tr>\n    <td>ActionHandler</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup.action<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.action\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>ActionHandlerManager</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.action\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>ActionHandlerManagerUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.action\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>ActionTypeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>AlternateKeywordQueryHitsProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search<br>\n\t  <em>New:</em> com.liferay.portal.search.internal.hits\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.search<br>\n        6.0.14\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleContentException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleContentSizeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleCreateDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleDisplayDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleDisplayDateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleExpirationDateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleIDComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleIdException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleModifiedDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleReviewDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleReviewDateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleSmallImageNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleSmallImageSizeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleTitleComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleTitleException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleVersionComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleVersionException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>AuditMessageProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.audit<br>\n\t  <em>New:</em> com.liferay.portal.security.audit\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.audit.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>AutoDeleteFileInputStream</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.io<br>\n\t  <em>New:</em> com.liferay.petra.io\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.io<br>\n        3.0.2\n    </td>\n  </tr>\n  <tr>\n    <td>AverageStatistics</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.monitoring.statistics<br>\n\t  <em>New:</em> com.liferay.portal.monitoring.internal.statistics\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.monitoring<br>\n        7.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.portal.background.task.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service.persistence<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.portal.background.task.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service.persistence<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.portal.background.task.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BannedUserException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>BaseCmisRepository</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>BaseCmisSearchQueryBuilder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>BaseDDLExporter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.internal.exporter\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.service<br>\n        3.0.12\n    </td>\n  </tr>\n  <tr>\n    <td>BaseDDMDisplay</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>BaseFieldRenderer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>BaseScriptingExecutor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.scripting<br>\n\t  <em>New:</em> com.liferay.portal.scripting\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.scripting.api<br>\n        3.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>BaseStatistics</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.monitoring.statistics<br>\n\t  <em>New:</em> com.liferay.portal.monitoring.internal.statistics\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.monitoring<br>\n        7.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>BaseStorageAdapter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>BlockingPortalCache</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache<br>\n\t  <em>New:</em> com.liferay.portal.cache\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.api<br>\n        2.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntry</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service.persistence<br>\n\t  <em>New:</em> com.liferay.blogs.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service.persistence<br>\n\t  <em>New:</em> com.liferay.blogs.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntrySoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service.persistence<br>\n\t  <em>New:</em> com.liferay.blogs.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUser</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service.persistence<br>\n\t  <em>New:</em> com.liferay.blogs.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service.persistence<br>\n\t  <em>New:</em> com.liferay.blogs.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service.persistence<br>\n\t  <em>New:</em> com.liferay.blogs.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntry</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service.persistence<br>\n\t  <em>New:</em> com.liferay.bookmarks.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service.persistence<br>\n\t  <em>New:</em> com.liferay.bookmarks.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntrySoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service.persistence<br>\n\t  <em>New:</em> com.liferay.bookmarks.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service.persistence<br>\n\t  <em>New:</em> com.liferay.bookmarks.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service.persistence<br>\n\t  <em>New:</em> com.liferay.bookmarks.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service.persistence<br>\n\t  <em>New:</em> com.liferay.bookmarks.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>ByteArrayReportResultContainer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>CMISBetweenExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISConjunction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISContainsExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISContainsNotExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISContainsValueExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISCriterion</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISDisjunction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISFullTextConjunction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISInFolderExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISInTreeExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISJunction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISNotExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISParameterValueUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISRepositoryHandler</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISRepositoryUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.impl<br>\n        4.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>CMISSearchQueryBuilder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISSimpleExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISSimpleExpressionOperator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CharPool</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.util<br>\n\t  <em>New:</em> com.liferay.petra.string\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.string<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>CharsetDecoderUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.nio.charset<br>\n\t  <em>New:</em> com.liferay.petra.nio\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.nio<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>CharsetEncoderUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.nio.charset<br>\n\t  <em>New:</em> com.liferay.petra.nio\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.nio<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ClassLoaderPool</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.util<br>\n\t  <em>New:</em> com.liferay.petra.lang\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.lang<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ClassPathUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.process<br>\n\t  <em>New:</em> com.liferay.petra.process\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.process<br>\n        3.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>ClassResolverUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.util<br>\n\t  <em>New:</em> com.liferay.petra.lang\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.lang<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>CollatedSpellCheckHitsProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search<br>\n\t  <em>New:</em> com.liferay.portal.search.internal.hits\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.search<br>\n        6.0.14\n    </td>\n  </tr>\n  <tr>\n    <td>CompoundSessionIdServletRequest</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.servlet.filters.compoundsessionid<br>\n\t  <em>New:</em> com.liferay.portal.compound.session.id.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.compound.session.id<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>Condition</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage.query<br>\n\t  <em>New:</em> com.liferay.adaptive.media.image.media.query\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.adaptive.media.image.api<br>\n        3.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>ConsumerOutputProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.process<br>\n\t  <em>New:</em> com.liferay.petra.process\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.process<br>\n        3.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>ContactConverterKeys</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>ContentException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ContentNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ContextClassloaderReportDesignRetriever</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>CountStatistics</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.monitoring.statistics<br>\n\t  <em>New:</em> com.liferay.portal.monitoring.internal.statistics\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.monitoring<br>\n        7.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>DDL</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLExporter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exporter\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLExporterFactory</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exporter\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecord</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSet</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersion</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersionModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersionPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersionSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersionUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersionVersionComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.util.comparator<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersionWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDM</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContent</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMDisplay</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMDisplayRegistry</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMIndexer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMIndexerUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.asset.list.internal.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.asset.list.service<br>\n        1.0.11\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLink</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructurePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateHelper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplatePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMXML</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DLContent</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.content.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentDataBlobModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.content.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.content.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.content.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.content.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.content.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.content.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.content.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.content.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentVersionComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.util.comparator<br>\n\t  <em>New:</em> com.liferay.document.library.content.service.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.service<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.content.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRank</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.sync.constants\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEvent</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.sync.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.sync.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.sync.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.sync.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.sync.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.sync.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.sync.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.sync.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.sync.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>Database</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.util<br>\n\t  <em>New:</em> com.liferay.portal.tools.db.upgrade.client\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.tools.db.upgrade.client<br>\n        3.0.0\n    </td>\n  </tr>\n  <tr>\n    <td>DefaultAttributesTransformer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.impl<br>\n        2.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DefaultMessageBus</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DefaultSingleDestinationMessageSender</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.sender<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.sender\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DefaultSingleDestinationSynchronousMessageSender</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.sender<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.sender\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DefaultSynchronousMessageSender</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.sender<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.sender\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DeleteFileFinalizeAction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>DestinationStatisticsManager</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.jmx<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.jmx\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DestinationStatisticsManagerMBean</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.jmx<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.jmx\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DirectSynchronousMessageSender</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.sender<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.sender\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>Dummy</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.exportimport.test.util.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.exportimport.test.util<br>\n        2.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>DummyContext</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.dummy\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>DummyDirContext</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.dummy\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>DummyFinalizeAction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicateArticleIdException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicateFeedIdException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicateLDAPServerNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicateNodeNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicatePageException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicateRuleGroupInstanceException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicateVoteException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>EntryDisplayDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.util.comparator<br>\n\t  <em>New:</em> com.liferay.blogs.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EntryModifiedDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.util.comparator<br>\n\t  <em>New:</em> com.liferay.bookmarks.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EntryNameComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.util.comparator<br>\n\t  <em>New:</em> com.liferay.bookmarks.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EntryPriorityComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.util.comparator<br>\n\t  <em>New:</em> com.liferay.bookmarks.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EntrySmallImageNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs<br>\n\t  <em>New:</em> com.liferay.blogs.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EntryURLComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.util.comparator<br>\n\t  <em>New:</em> com.liferay.bookmarks.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EntryVisitsComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.util.comparator<br>\n\t  <em>New:</em> com.liferay.bookmarks.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EqualityWeakReference</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>Fact</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>FeedContentFieldException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FeedIdException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FeedNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FeedTargetLayoutFriendlyUrlException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FeedTargetPortletIdException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FieldConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FieldRenderer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FieldRendererFactory</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>Fields</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FileRankCreateDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.util.comparator<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.service<br>\n        2.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>FinalizeAction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>FinalizeManager</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>FlagsEntryService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.flags.service<br>\n\t  <em>New:</em> com.liferay.flags.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.flags.api<br>\n        4.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>FlagsEntryServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.flags.service<br>\n\t  <em>New:</em> com.liferay.flags.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.flags.api<br>\n        4.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>FlagsEntryServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.flags.service<br>\n\t  <em>New:</em> com.liferay.flags.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.flags.api<br>\n        4.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>FlagsRequest</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.flags.messaging<br>\n\t  <em>New:</em> com.liferay.flags.internal.messaging\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.flags.service<br>\n        4.0.2\n    </td>\n  </tr>\n  <tr>\n    <td>GroupConverterKeys</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>ImportFilesException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticle</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleDisplay</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticlePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResource</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourcePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContent</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journalcontent.util<br>\n\t  <em>New:</em> com.liferay.journal.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearch</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalConverter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util<br>\n\t  <em>New:</em> com.liferay.journal.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeed</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalSearchConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalStructureConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>LDAPFilterException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.validator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>LDAPGroup</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.exportimport\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>LDAPServerNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>LDAPToPortalConverter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.exportimport\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>LDAPUser</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.exportimport\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>LDAPUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>LockLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.lock.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.lock.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.lock.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.portal.lock.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service.persistence<br>\n\t  <em>New:</em> com.liferay.portal.lock.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.portal.lock.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service.persistence<br>\n\t  <em>New:</em> com.liferay.portal.lock.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.portal.lock.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockedThreadException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>LoggingOutputProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.process<br>\n\t  <em>New:</em> com.liferay.petra.process\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.process<br>\n        3.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBan</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategory</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.constants\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryDisplay</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.web.internal.display\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.web<br>\n        3.0.17\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategorySoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussion</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingList</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessage</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.constants\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageDisplay</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessagePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUser</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThread</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.constants\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlag</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBTreeWalker</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBeanRegistry</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.jmx<br>\n\t  <em>New:</em> com.liferay.portal.jmx\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.jmx.api<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>MDRAction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRPermission</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.permission<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.web.internal.security.permission.resource\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.web<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRule</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroup</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstancePermission</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.permission<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.web.internal.security.permission.resource\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.web<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstancePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupPermission</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.permission<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.web.internal.security.permission.resource\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.web<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRulePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MailingListEmailAddressException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MailingListInServerNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MailingListInUserNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MailingListOutEmailAddressException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MailingListOutServerNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MailingListOutUserNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MemoryReportDesignRetriever</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>MessageBodyException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MessageBusManager</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.jmx<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.jmx\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>MessageBusManagerMBean</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.jmx<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.jmx\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>MessageCreateDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.util.comparator<br>\n\t  <em>New:</em> com.liferay.message.boards.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MessageSubjectException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MessageThreadComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.util.comparator<br>\n\t  <em>New:</em> com.liferay.message.boards.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>Modifications</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.exportimport\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchArticleException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchArticleImageException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchArticleResourceException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchBanException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchChoiceException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchContentException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchContentSearchException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchDiscussionException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchFeedException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchFileRankException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchMailingListException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchNodeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchPageException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchPageResourceException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchQuestionException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchRecordException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchRecordSetException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchRecordVersionException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchRuleException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchRuleGroupException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchRuleGroupInstanceException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchStatsUserException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs<br>\n\t  <em>New:</em> com.liferay.blogs.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchStorageLinkException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchStructureLinkException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchTemplateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchThreadException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchThreadFlagException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchVoteException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>NodeNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>OutputProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.process<br>\n\t  <em>New:</em> com.liferay.petra.process\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.process<br>\n        3.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>PageContentException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>PageCreateDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.util.comparator<br>\n\t  <em>New:</em> com.liferay.wiki.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>PageTitleComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.util.comparator<br>\n\t  <em>New:</em> com.liferay.wiki.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>PageTitleException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>PageVersionComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.util.comparator<br>\n\t  <em>New:</em> com.liferay.wiki.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>PageVersionException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoice</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoicePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service.persistence<br>\n\t  <em>New:</em> com.liferay.polls.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service.persistence<br>\n\t  <em>New:</em> com.liferay.polls.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestion</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service.persistence<br>\n\t  <em>New:</em> com.liferay.polls.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service.persistence<br>\n\t  <em>New:</em> com.liferay.polls.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVote</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVotePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service.persistence<br>\n\t  <em>New:</em> com.liferay.polls.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service.persistence<br>\n\t  <em>New:</em> com.liferay.polls.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PoolAction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterChannel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal.cluster.link\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterChannelFactory</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal.cluster.link\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterChannelSelector</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal.cluster.link\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterEvent</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterEventCoalesceComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterEventType</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterLink</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal.cluster.link\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalExecutorFactory</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.executor<br>\n\t  <em>New:</em> com.liferay.portal.executor.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.executor<br>\n        4.0.2\n    </td>\n  </tr>\n  <tr>\n    <td>PortalToLDAPConverter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.exportimport\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>PortletDisplayTemplate</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.portletdisplaytemplate.util<br>\n\t  <em>New:</em> com.liferay.portlet.display.template\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portlet.display.template.api<br>\n        2.0.2\n    </td>\n  </tr>\n  <tr>\n    <td>PortletDisplayTemplateConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.portletdisplaytemplate.util<br>\n\t  <em>New:</em> com.liferay.portlet.display.template.constants\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portlet.display.template.api<br>\n        2.0.2\n    </td>\n  </tr>\n  <tr>\n    <td>PortletDisplayTemplateUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.portletdisplaytemplate.util<br>\n\t  <em>New:</em> com.liferay.roles.admin.web.internal.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.roles.admin.web<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortletDisplayTemplateUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.portletdisplaytemplate.util<br>\n\t  <em>New:</em> com.liferay.roles.admin.web.internal.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.roles.admin.web<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortletDisplayTemplateUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.portletdisplaytemplate.util<br>\n\t  <em>New:</em> com.liferay.roles.admin.web.internal.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.roles.admin.web<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>ProcessUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.process<br>\n\t  <em>New:</em> com.liferay.petra.process\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.process<br>\n        3.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>QueryIndexingHitsProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search<br>\n\t  <em>New:</em> com.liferay.portal.search.internal.hits\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.search<br>\n        6.0.14\n    </td>\n  </tr>\n  <tr>\n    <td>QuerySuggestionHitsProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search<br>\n\t  <em>New:</em> com.liferay.portal.search.internal.hits\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.search<br>\n        6.0.14\n    </td>\n  </tr>\n  <tr>\n    <td>QueryType</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>QuestionChoiceException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>QuestionDescriptionException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>QuestionExpirationDateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>QuestionExpiredException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>QuestionTitleException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>RecordSetDDMStructureIdException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>RecordSetDuplicateRecordSetKeyException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>RecordSetNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>RegistryAwareMBeanServer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.jmx<br>\n\t  <em>New:</em> com.liferay.portal.jmx.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.jmx<br>\n        6.0.2\n    </td>\n  </tr>\n  <tr>\n    <td>ReportCompilerRequestMessageListener</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting.messaging<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine.messaging\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportDataSourceType</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportDesignRetriever</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportEngine</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportExportException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportFormat</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportFormatExporter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportFormatExporterRegistry</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportGenerationException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportRequest</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportRequestContext</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportRequestMessageListener</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting.messaging<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine.messaging\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportResultContainer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>RequestStatistics</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.monitoring.statistics<br>\n\t  <em>New:</em> com.liferay.portal.monitoring.internal.statistics\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.monitoring<br>\n        7.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>RequiredMessageException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>RequiredNodeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>RequiredTemplateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>RequiredTemplateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>RuleGroupInstancePriorityComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.util<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RuleGroupProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.rule\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RuleGroupProcessorUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.rule\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RuleHandler</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup.rule<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.rule\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RulesEngine</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RulesEngineException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RulesEngineUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RulesLanguage</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RulesResourceRetriever</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>ServletContextReportDesignRetriever</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting.servlet<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine.servlet\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>SoftReferencePool</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>SortFactoryImpl</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search<br>\n\t  <em>New:</em> com.liferay.portal.search.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.search<br>\n        6.0.14\n    </td>\n  </tr>\n  <tr>\n    <td>SplitThreadException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>Statistics</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.monitoring.statistics<br>\n\t  <em>New:</em> com.liferay.portal.monitoring.internal.statistics\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.monitoring<br>\n        7.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>StatsUserLastPostDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.util.comparator<br>\n\t  <em>New:</em> com.liferay.blogs.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>StorageAdapter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StorageEngine</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StorageException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StorageFieldNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StringQueryImpl</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search<br>\n\t  <em>New:</em> com.liferay.portal.search.internal.query\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.search<br>\n        6.0.14\n    </td>\n  </tr>\n  <tr>\n    <td>StructureDuplicateStructureKeyException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StructureFieldException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StructureIdComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util.comparator<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StructureModifiedDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util.comparator<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StructureStructureKeyComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util.comparator<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>SummaryStatistics</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.monitoring.statistics<br>\n\t  <em>New:</em> com.liferay.portal.monitoring.internal.statistics\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.monitoring<br>\n        7.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>SynchronousMessageListener</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.sender<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.sender\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateDuplicateTemplateKeyException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateIdComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util.comparator<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateModifiedDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util.comparator<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateScriptException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateSmallImageNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateSmallImageNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateSmallImageSizeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateSmallImageSizeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ThreadLastPostDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.util.comparator<br>\n\t  <em>New:</em> com.liferay.message.boards.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>UniformPortalCacheClusterChannelSelector</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal.cluster.link\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>UnknownRuleHandlerException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup.rule<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.rule\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>UserConverterKeys</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>WikiFormatException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNode</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPage</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageDisplay</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPagePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResource</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourcePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n\n</table>\n"
  },
  {
    "path": "en/developer/reference/articles/02-export-import-and-staging/01-intro.markdown",
    "content": "---\nheader-id: export-import-and-staging\n---\n\n# Export/Import and Staging\n\n[TOC levels=1-4]\n\nExport/Import and Staging are frameworks that help you manage your content\npublication. This section provides reference documentation complementing the\n[Content Publication Management](/docs/7-2/frameworks/-/knowledge_base/f/content-publication-management)\nsection.\n"
  },
  {
    "path": "en/developer/reference/articles/02-export-import-and-staging/decision-to-implement-staging.markdown",
    "content": "---\nheader-id: decision-to-implement-staging\n---\n\n# Decision to Implement Staging\n\n[TOC levels=1-4]\n\nStaging is an advanced publication tool that lets you create or modify your site\nbefore releasing it to the public. Most of @product@'s included applications\n(e.g., Web Content, Bookmarks, etc.) support Staging. Implementing Staging in\nyour own application can be beneficial, but how do you know if it's the right\nmove?\n\nNot every application needs to support Staging and Export/Import. The most\nimportant question to consider during the decision process is\n\n*What part of your application are you primarily focused on using Staging for?*\n\nWhen Staging is enabled, all pages and applications are staged automatically.\n@product@'s architecture separates the application and its configuration from\nthe actual content, meaning that content can exist without any application to\ndisplay it and vice versa. Although Staging supports all applications and their\nconfigurations by default, not all applications' content is supported by\nStaging.\n\nImplementing Staging for your application means you're defining the logic for\nhow the Staging framework should process, serialize, and de-serialize your app's\ncontent, and how to insert it into a database.\n\nTherefore, if you want to track your application's content, you should implement\nStaging in your application. Here are a few other scenarios where you should\nimplement Staging in your application:\n\n- You're using remote staging. When publishing to a remote live site, your\n  content must be transferred to a different @product@ installation. Therefore,\n  Staging must be able to recognize the content to facilitate the transfer.\n- You want a space where you can freely edit and test your content before\n  publishing it to a live audience.\n- Your content is being referenced from another content type that supports\n  Staging.\n- You want to process your portlet's preferences during publication (i.e., you\n  might want to publish some content with it or complete extra steps).\n- You want to process the content during publication (e.g., writing validation\n  for your content during the import process).\n\nIf none of these options are beneficial for you, implementing Staging in your\napplication is unnecessary.\n\nWhen content supports Staging and Staging is enabled, it is created in a Staging\ngroup and is only published to a live site when that site is published. When\ncontent is **not** supported by Staging, it is never added to a Staging group\nand is not reviewable during the Staging publication process; it's added and\nremoved from the live site only.\n\nFrom a technical standpoint, publishing an entity or content follows the process\nbelow:\n\n1.  The entity's possible references are discovered and processed.\n2.  The entity's fields are processed.\n3.  The entity is serialized into a LAR file.\n4.  The LAR is transferred to the live site (local or remote live).\n5.  After de-serialization, the entity's fields are processed.\n6.  The entity is added to the database.\n\nAwesome! You should now have a good idea about whether you should implement\nStaging for your application.\n"
  },
  {
    "path": "en/developer/reference/articles/02-export-import-and-staging/liferay-archive-file.markdown",
    "content": "---\nheader-id: liferay-archive-lar-file\n---\n\n# Liferay Archive (LAR) File\n\n[TOC levels=1-4]\n\nAn easier way to export/import your application's data is to use a Liferay\nARchive (LAR) file. Liferay provides the LAR feature to address the need to\nexport/import data in a database agnostic manner. So what exactly is a LAR file?\n\nA LAR file is a compressed file (ZIP archive) @product@ uses to export/import\ndata. LAR files can be created for single portlets, pages, or sets of pages.\nPortlets that are LAR-capable provide an interface to let you control how their\ndata is imported/exported. There are several @product@ use cases that require\nthe use of LAR files:\n\n- Backing up and restoring portlet-specific data without requiring a full\n  database backup.\n- Cloning sites.\n- Specifying a template to be used for users' public or private pages.\n- Using Local Live or Remote Live staging.\n\nThe data handler framework is available so developers don't have to\ncreate/modify a LAR file manually. **It is strongly recommended never to modify\na LAR file.** You should always use Liferay's provided data handler APIs to\nconstruct it.\n\nKnowing how a LAR file is constructed, however, is beneficial to understand the\noverall purpose of your application's data handlers. Next, you'll explore a LAR\nfile's anatomy.\n\n## LAR File Anatomy\n\nWhat is a LAR file? You know the general concept for *why* it's used, but you\nmay want to know what lives inside to make your export/import processes work.\nWith a fundamental understanding for how a LAR file is constructed, you can\nbetter understand what your data handlers generate behind the scenes.\n\nBelow is the structure of a simple LAR file. It illustrates the exportation of a\nsingle Bookmarks entry and the portlet's configuration:\n\n- `Bookmarks_Admin-201701091904.portlet.lar`\n    - `group`\n        - `20143`\n            - `com.liferay.bookmarks.model.BookmarksEntry`\n                - `35005.xml`\n            - `portlet`\n                - `com_liferay_bookmarks_web_portlet_BookmarksAdminPortlet`\n                    - `20137`\n                        - `portlet.xml`\n                    - `20143`\n                        - `portlet-data.xml`\n    - `manifest.xml`\n\nYou'll dissect the anatomy structure next.\n\n## LAR Manifest\n\nYou can tell from the LAR's generated name what information is contained in\nthe LAR: the Bookmarks Admin app's data. The `manifest.xml` file sits at the\nroot of the LAR file. It provides essential information about the export\nprocess. The `manifest.xml` for the sample Bookmarks LAR is pretty bare since\nit's not exporting much content, but this file can become large when exporting\npages of content. There are four main parts (tags) to a `manifest.xml` file.\n\n- `header`: contains information about the LAR file, current process,\n  and site you're exporting (if necessary). For example, it can include\n  locales, build information, export date, company ID, group ID, layouts,\n  themes, etc.\n- `missing-references`: lists entities that must be validated during import. For\n  example, suppose you're exporting a web content article that references an\n  image (e.g., an embedded image residing in the document library). If the image\n  was not selected for export, the image must already exist in the site where\n  the article is imported. Therefore, the image would be flagged as a missing\n  reference in the LAR file. If the missing reference does not exist in the site\n  when the LAR is imported, the import process fails. If your import fails, the\n  Import UI shows you the missing references that weren't validated. \n- `portlets`: defines the portlets (i.e., portlet data) exported in the LAR.\n  Each portlet definition has basic information on the exported portlet and\n  points to the generated `portlet.xml` for more specialized portlet\n  information.\n- `manifest-summary`: contains information on what has been exported. The\n  Staging and Export frameworks export or publish some entities even\n  though they weren't marked for it, because the process respects data\n  integrity. This section holds information for all the entities that have\n  been processed. The entities defining a non-zero `addition-count` attribute\n  are displayed in the Export/Import UI.\n\nThe `manifest.xml` file also defines layout information if you've exported pages\nin your LAR. For example, your manifest could have `LayoutSet`, `Layout`, and\n`LayoutFriendlyURL` tags specifying staged models and their various references\nin an exported page.\n\nNow that you've learned about the LAR's `manifest.xml` and how it's used to\nstore high-level data about your export process, you can dive deeper into the\nLAR file's folders.\n\n## LAR Folders\n\nThe `group` folder has two main parts:\n\n- Entities\n- Portlets\n\nIf you look at the anatomy of the sample Bookmarks LAR, you'll notice that\n`group/[groupId]` folder holds a folder named after the entity you're exporting\n(e.g., `com.liferay.bookmarks.model.BookmarksEntry`) and a `portlet` folder\nholding a folder named after the portlet from which you're exporting (e.g.,\n`com_liferay_bookmarks_web_portlet_BookmarksAdminPortlet`). For each\nentity/portlet you export, there are subsequent folders holding data about them.\nEntities and portlets can also be stored in a `company` folder. Although the\nmajority of entities belong to a group, some exist outside of a group scope\n(e.g., users).\n\nIf you open the\n`/group/20143/com.liferay.bookmarks.model.BookmarksEntry/35005.xml` file, you'll\nfind serialized data about the entity, which is similar to what is stored\nin the database.\n\nThe `portlet` folder holds all the portlets you exported. Each portlet has its\nown folder that holds various XML files with data describing the exported\ncontent. There are three main XML files that can be generated for a single\nportlet:\n\n- `portlet.xml`: provides essential information about the portlet, similar to a\n  manifest file. For example, this can include the portlet ID, high-level entity\n  information stored in the portlet (e.g., web content articles in a web content\n  portlet), permissioning, etc.\n- `portlet-data.xml`: describes specific entity data stored in the portlet. For\n  example, for the web content portlet, articles stored in the portlet are\n  defined in `staged-model` tags and are linked to their serialized entity XML\n  files.\n- `portlet-preferences.xml`: defines the settings of the portlet. For example,\n   this can include portlet preferences like the portlet owner, default user,\n   article IDs, etc.\n\nNote that when you import a LAR, it only includes the portlet data. You have to\ndeploy the portlet to be able to use it. \n\nYou now know how exported entities, portlets, and pages are defined in a LAR\nfile. For a summarized outline of what you've learned about LAR file\nconstruction, see the diagram below.\n\n![Figure 1: Entities, Portlets, and Pages are defined in a LAR in different places.](../../images/lar-diagram.png)\n\nExcellent! You now have a fundamental understanding for how a LAR file is\ngenerated and how it's structured.\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/01-front-end-intro.markdown",
    "content": "---\nheader-id: front-end-reference\n---\n\n# Front-End Reference\n\n[TOC levels=1-4]\n\nThis section contains resources that you might find useful for Front-End \ndevelopment. \n\nThe topics below are covered in this section:\n\n- [@product@ FreeMarker Macros](/docs/7-2/reference/-/knowledge_base/r/product-freemarker-macros)\n- [FreeMarker Taglib Macros](/docs/7-2/reference/-/knowledge_base/r/freemarker-taglib-macros)\n- [Front-end Taglibs](/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs)\n- [Liferay npm Bundler](/docs/7-2/reference/-/knowledge_base/r/liferay-npm-bundler)\n- [Liferay JS APIs](/docs/7-2/reference/-/knowledge_base/r/liferay-javascript-apis)\n- [Setting up Your npm Environment](/docs/7-2/reference/-/knowledge_base/r/setting-up-your-npm-environment)\n- [Sitemap.json Page Configuration Options](/docs/7-2/reference/-/knowledge_base/r/sitemap-page-configuration-options)\n- [CKEditor Plugin Reference Guide](/docs/7-2/reference/-/knowledge_base/r/ckeditor-plugin-reference-guide)\n- [Fully Qualified Portlet IDs List](/docs/7-2/reference/-/knowledge_base/r/fully-qualified-portlet-ids)\n<!--- AlloyEditor Button Reference Guide -->\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/02-freemarker-macros/01-liferay-freemarker-macros-intro.markdown",
    "content": "---\nheader-id: product-freemarker-macros\n---\n\n# @product@ FreeMarker Macros\n\n[TOC levels=1-4]\n\n@product@ defines several \n[macros](https://freemarker.apache.org/docs/ref_directive_macro.html) in \n[`FTL_Liferay.ftl` template](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-template/portal-template-freemarker/src/main/resources/FTL_liferay.ftl) \nthat you can use in your theme templates to include theme resources, standard \nportlets, and more. @product@ also exposes its taglibs as FreeMarker \nmacros. See each \n[taglib's documentation](/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs) \nfor more information on using the taglib in your FreeMarker templates. This \nreference guide lists the available FreeMarker macros that @product@ offers. \n\n| Macro | Parameters | Description | Example |\n| --- | --- | --- | --- |\n| breadcrumbs | default_preferences | Adds the Breadcrumbs portlet with optional preferences | `<@liferay.breadcrumbs />` |\n| control_menu | N/A | Adds the Control Menu portlet | `<@liferay.control_menu />` |\n| css | file_name | Adds an external stylesheet with the specified file name location | `<@liferay.css file_name=\"${css_folder}/mycss.css\"/>` |\n| date | format | Prints the date in the current locale with the given format | `<@liferay.date format=\"/yyyy/MM/dd/HH/\" />` |\n| js | file_name | Adds an external JavaScript file with the specified file name source | `<@liferay.js file_name=\"${javascript_folder}/myJs.js\"/>` |\n| language | key | Prints the specified language key in the current locale | `<@liferay.language key=\"last-modified\" />` |\n| language_format | arguments<br/>key | Formats the given language key with the specified arguments. For example, passing `go-to-x` as the key and `Mars` as the arguments prints *Go to Mars*. | `<@liferay.language_format arguments=\"${site_name}\" key=\"go-to-x\" />` |\n| languages | default_preferences | Adds the Languages portlet with optional preferences | `<@liferay.languages />` |\n| navigation_menu | default_preferences<br/>instance_id | Adds the Navigation Menu portlet with optional preferences and instance ID. | `<@liferay.navigation_menu />` |\n| search | default_preferences | Adds the Search portlet with optional preferences | `<@liferay.search />` |\n| search_bar | default_preferences | Adds the Search Bar portlet with optional preferences | `<@liferay.search_bar />` |\n| user_personal_bar | N/A | Adds the User Personal Bar portlet | `<@liferay.user_personal_bar />` |\n\nA few reference examples are shown below.\n\n## Reference Examples\n\nThe example below includes a language key with the `language` macro directive \nalong with its language `key` parameter:\n\n```markup\n<@liferay.language key=\"powered-by\" />\n```\n\nThis example includes the Search portlet with its \n[Portlet Decorator](/docs/7-2/frameworks/-/knowledge_base/f/theming-portlets#portlet-decorators) \nportlet preference set to barebone:\n\n```markup\n<@liferay.search default_preferences=\n  freeMarkerPortletPreferences.getPreferences(\n    \"portletSetupPortletDecoratorId\", \"barebone\"\n  ) \n/>\n```\n\nYou can also pass multiple portlet preferences in an object, as shown in the \nexample below for the Navigation Menu portlet:\n\n```markup\n<#assign secondaryNavigationPreferencesMap = \n  {\n    \"displayStyle\": \"ddmTemplate_NAVBAR-BLANK-JUSTIFIED-FTL\", \n    \"portletSetupPortletDecoratorId\": \"barebone\", \n    \"rootLayoutType\": \"relative\", \n    \"siteNavigationMenuId\": \"0\", \n    \"siteNavigationMenuType\": \"1\"\n  } \n/>\n\n<@liferay.navigation_menu\n  default_preferences=\n  freeMarkerPortletPreferences.getPreferences(secondaryNavigationPreferencesMap)\n  instance_id=\"main_navigation_menu\"\n/>\n```\n\n| **Note:** Portlet preferences are unique to each portlet, so first you \n| must determine which preferences you want to configure. There are two ways \n| to determine the proper key/value pair for a portlet preference. The first \n| is to set the portlet preference manually, and then check the values in \n| the `portletPreferences.preferences` column of the database as a hint for \n| what to configure. \n|\n|  Another approach is to search each app in your bundle for the keyword \n|  `preferences--`. This returns app JSPs that have the portlet preferences \n|  defined for the portlet. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/01-front-end-taglibs-intro.markdown",
    "content": "---\nheader-id: front-end-taglibs\n---\n\n# Front-End Taglibs\n\n[TOC levels=1-4]\n\nYou have access to a powerful set of taglibs for creating commonly used UI\ncomponents in your apps, themes, and web content. The following taglibs are \ncovered in this section:\n\n- AUI: create common UI components such as forms, buttons, and more.\n\n- Chart: visualize data. Create bar charts, line charts, scatter charts, spline \n  charts, and much more. \n\n- Clay: create \n  [Clay components](https://clayui.com/docs/components/alerts.html), \n  such as alerts, buttons, drop-down menus, form elements, and more for your \n  apps. \n\n- Frontend: create UI components commonly used throughout Portal's apps, such \n  as add menus, cards, management bars, and more.\n\n- Liferay UI: create common UI components such as icons, tabs, and more.\n  \n- Liferay Util: load additional resources, define parameters, buffer content, \n  and more.\n\n| **Note:** Each taglib is available as a FreeMarker macro, except for the Chart \n| taglib. The Chart taglib is **not** available as a FreeMarker macro. The \n| articles in this section provide the proper syntax to use for each macro. See \n| the \n| [FreeMarker Taglib Mappings reference](/docs/7-2/reference/-/knowledge_base/r/product-freemarker-macros) \n| for a complete list of the available FreeMarker taglib macros.\n\nIn this section, you'll learn how to use taglibs to build awesome user \ninterfaces for your apps! \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/02-theme-objects/01-liferay-theme-objects-intro.markdown",
    "content": "---\nheader-id: liferay-theme-objects-available-in-jsps\n---\n\n# Liferay Theme Objects Available in JSPs\n\n[TOC levels=1-4]\n\nWhen you include the `<liferay-theme:defineObjects>` tag in your JSP, you gain \naccess to several Liferay theme objects via variables. These objects are \ndescribed in the table below:\n\n| Object | Description |\n| --- | --- |\n| `account` | The user's Account object. This object maps to the Account table in the Liferay database. |\n| `colorScheme` | An object representing the current color scheme in the theme that is being rendered by the portal |\n| `company` | The current Company object. This represents the portal instance on which the user is currently navigating. |\n| `contact` | The user's Contact object. This object maps to the Contact table in the Liferay database. |\n| `layout` | The page to which the user has currently navigated |\n| `layoutTypePortlet` | This object can be used to programmatically add or remove portlets from a page. |\n| `locale` | The current user’s locale, as defined by Java |\n| `permissionChecker` | An object that can determine---given a particular resource---whether the current user has a particular permission for that resource |\n| `plid` | A portal layout ID. This is a unique identifier for any  page that exists in the portal, across all portal instances. |\n| `portletDisplay` | An object that gives the programmer access to many attributes of the current portlet, including the portlet name, the portlet mode, the ID of the column on the layout in which it resides, and more |\n| `realUser` | When an administrator is impersonating a user, this variable tracks the administrator’s User object. |\n| `scopeGroupId` | By default, contains the groupId for the community or organization in which this portlet resides. If the scopeable attribute is set to true, this may contain a unique scope identifier for custom scopes, such as the page scope, if the portlet has been configured to use a custom scope. |\n| `theme` | An object representing the current theme that is being rendered by the portal |\n| `themeDisplay` | A runtime object that contains many useful items, such as the logged-in user, the layout, logo information, paths, and much more |\n| `timeZone` | The current user’s time zone, as defined by Java |\n| `user` | The User object representing the current user |\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/03-portlet-objects/01-liferay-portlet-objects-intro.markdown",
    "content": "---\nheader-id: liferay-portlet-objects-available-in-jsps\n---\n\n# Liferay Portlet Objects Available in JSPs\n\n[TOC levels=1-4]\n\nYou may have noticed the `<liferay-portlet:defineObjects>` tag in your JSPs. \nSimilar to the [theme:defineObjects](/docs/7-2/reference/-/knowledge_base/r/liferay-theme-objects-available-in-jsps) \ntag, when you include this tag in your JSP, you gain access to several variables \nthat, in this case, return useful information about your portlet. Note that the \nJSR-286 specification defines four lifecycle methods for a portlet: \nprocessAction, processEvent, render, and serveResource. Some of the variables \ndefined by the `<portlet:defineObjects/>` tag are only available to a JSP if the \nJSP was included during the appropriate phase of the portlet lifecycle. These \nobjects are described in the table below:\n\n| Object | Description |\n| ------ | ----------- |\n| `ActionRequest actionRequest` | Represents the request sent to the portlet to handle an action. `actionRequest` is only available to a JSP if the JSP was included during the action-processing phase. |\n| `ActionResponse actionResponse` | Represents the portlet response to an action request. `actionResponse` is only available to a JSP if the JSP was included in the action-processing phase. |\n| `EventRequest eventRequest` | Represents the request sent to the portlet to handle an event. `eventRequest` is only available to a JSP if the JSP was included during the event-processing phase. |\n| `EventResponse eventResponse` | Represents the portlet response to an event request. `eventResponse` is only available to a JSP if the JSP was included in the event-processing phase. |\n| `HeaderRequest headerRequest` | Represents the request sent to the portlet to handle its HTML header or HEAD section. `headerRequest` is only available to a JSP if the JSP was included during the header-processing phase. |\n| `HeaderResponse headerResponse` | Represents the portlet response to a header request. `headerResponse` is only available to a JSP if the JSP was included in the header-processing phase. |\n| [`LiferayPortletRequest liferayPortletRequest`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/LiferayPortletRequest.html) | Provides access to the `HttpServletRequest`, the `Portlet`, and the portlet name and lifecycle value. `liferayPortletRequest` is available in all portlet phases. |\n| [`LiferayPortletResponse liferayPortletResponse`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/LiferayPortletResponse.html) | Includes the properties returned to the portal and provides a means to add or change properties. `liferayPortletResponse` is available in all portlet phases. |\n| `RenderRequest renderRequest` | Represents the request sent to the portlet to render the portlet. `renderRequest` is only available to a JSP if the JSP was included during the render request phase. |\n| `RenderResponse renderResponse` | Represents an object that assists the portlet in sending a response to the portal. `renderResponse` is only available to a JSP if the JSP was included during the render request phase. |\n| `ResourceRequest resourceRequest` | Represents the request sent to the portlet for rendering resources. `resourceRequest` is only available to a JSP if the JSP was included during the resource-serving phase. |\n| `ResourceResponse resourceResponse` | Represents an object that assists the portlet in rendering a resource. `resourceResponse` is only available to a JSP if the JSP was included in the resource-serving phase. |\n| `PortletConfig portletConfig` | Represents the portlet's configuration including, the portlet's name, initialization parameters, resource bundle, and application context. `portletConfig` is always available to a portlet JSP, regardless of the request-processing phase in which it was included. |\n| `PortletPreferences portletPreferences` | Provides access to a portlet's preferences. `portletPreferences` is always available to a portlet JSP, regardless of the request-processing phase in which it was included. |\n| `Map<String, String[]> portletPreferencesValues` | Provides a Map equivalent to the `portletPreferences.getMap()` call or an empty Map if no portlet preferences exist. |\n| `PortletSession portletSession` | Provides a way to identify a user across more than one request and to store transient information about a user. A `portletSession` is created for each user client. `portletSession` is always available to a portlet JSP, regardless of the request-processing phase in which it was included. `portletSession` is `null` if no session exists. |\n| `Map<String, Object> portletSessionScope` | Provides a Map equivalent to the `PortletSession.getAtrributeMap()` call or an empty Map if no session attributes exist. |\n\nFor more details, visit the\n[Portlet 3.0 API Javadoc](https://docs.liferay.com/portlet-api/3.0/javadocs/). \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/04-liferay-ui-taglibs/01-using-liferay-ui-taglibs-intro.markdown",
    "content": "---\nheader-id: using-the-liferay-ui-taglib\n---\n\n# Using the Liferay UI Taglib\n\n[TOC levels=1-4]\n\nThe Liferay UI tag library provides tags that implement commonly used UI \ncomponents. These tags make your markup consistent, responsive, and accessible. \n\nYou can find a list of the available Liferay UI taglibs in the \n[Liferay UI taglibdocs](@platform-ref@/7.2-latest/taglibs/util-taglib/liferay-ui/tld-summary.html). \nEach taglib has a list of attributes that can be passed to the tag. Some of \nthese are required and some are optional. See the taglibdocs to view the \nrequirements for each tag. You'll find the full markup generated by the tags in \ntheir JSPs in their \n[Liferay Github Repo](https://github.com/liferay/liferay-portal/tree/7.2.x/portal-web/docroot/html/taglib/ui) \nfolders.\n\nTo use the Liferay-UI taglib library in your apps, you must add the following \ndeclaration to your JSP:\n\n```markup\n<%@ taglib prefix=\"liferay-ui\" uri=\"http://liferay.com/tld/ui\" %>\n```\n    \nThe Liferay-UI taglib is also available via a macro for your FreeMarker theme \nand web content templates. Follow this syntax:\n\n```markup\n<@liferay_ui[\"tag-name\"] attribute=\"string value\" attribute=10 />\n```\n\nThis section covers how to create UI components with the Liferay UI taglibs. \nEach article contains code examples along with a screenshot of the resulting UI. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/04-liferay-ui-taglibs/02-liferay-ui-icons.markdown",
    "content": "---\nheader-id: liferay-ui-icons\n---\n\n# Liferay UI Icons\n\n[TOC levels=1-4]\n\nThe Liferay UI taglibs provide several icons you can include in your apps. To \nadd an icon to your app, use the `liferay-ui:icon` tag and specify the icon with \neither the `icon`, `iconCssClass`, or `image` attribute. An example of each use \ncase is shown below. \n\nThe `image` attribute specifies \n[Liferay UI icons](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-unstyled/src/main/resources/META-INF/resources/_unstyled/images) \nto use (as defined in the Unstyled theme's `images/common` folder). Here's an \nexample configuration for a JSP:\n\n```markup\n<div class=\"col-md-3\">\n\t<liferay-ui:icon image=\"subscribe\" />\n\n\t<span class=\"ml-2\">Subscribe</span>\n</div>\n```\n\n![Figure 1: Use the image attribute to use a theme icon.](../../../../images/liferay-ui-taglib-icon-subscribe.png)\n\nThe Liferay UI taglib also exposes language flag icons. To use a language flag \nicon, provide the `../language/` relative path before the icon's name. Below is \nan example snippet from the Web Content Search portlet that displays the \ncurrent language's flag along with a localized message:\n\n```markup\n<liferay-ui:icon\n    image='<%= \"../language/\" + languageId %>'\n    message='<%= LanguageUtil.format(\n        request,\n        \"this-result-comes-from-the-x-version-of-this-content\",\n        snippetLocale.getDisplayLanguage(locale),\n        false\n    ) %>'\n/>\n```\n\nYou can achieve the same result in FreeMarker with the following code that uses \nthe available \n[`init.ftl` variables](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-theme/frontend-theme-unstyled/src/main/resources/META-INF/resources/_unstyled/templates/init.ftl) \nand \n[@product@ macros](/docs/7-2/reference/-/knowledge_base/r/product-freemarker-macros):\n\n```markup\n<#assign flag_message>\n    <@liferay.language_format \n        arguments=language \n        key=\"this-result-comes-from-the-x-version-of-this-content\" \n    />\n</#assign>\n\n<@liferay_ui[\"icon\"]\n    image=\"../language/${language_id}\"\n    message=flag_message\n/>\n```\n\nThe full list of available icons is shown in the figures below:\n\n![Figure 2: The Liferay UI taglib offers multiple icons for use in your app.](../../../../images/liferay-ui-taglib-icons.png)\n\n![Figure 3: Liferay UI icons can be configured based on language.](../../../../images/liferay-ui-taglib-icon-flags.png)\n\nThe `icon` attribute specifies \n[Font Awesome icons](https://fontawesome.com/v3.2.1/icons/) \nto use:\n\n```markup\n<liferay-ui:icon icon=\"angle-down\" />\n```\n\n![Figure 4: You can use the icon attribute to include Font Awesome icons in your app.](../../../../images/liferay-ui-taglib-icon-angle-down.png)\n\nThe `iconCssClass` attribute specifies a \n[glyphicon](http://marcoceppi.github.io/bootstrap-glyphicons/) \nto use:\n\n```markup\n<liferay-ui:icon\n    iconCssClass=\"icon-remove-sign\"\n    label=\"<%= true %>\"\n    message=\"unsubscribe\"\n    url=\"<%= unsubscribeURL %>\"\n/>\n```\n\n![Figure 5: You can use Font Awesome icons in your app.](../../../../images/liferay-ui-taglib-icon-css-class.png)\n\nThe examples above use some of the icon's available attributes. See the \n[Icon taglibdocs](@platform-ref@/7.2-latest/taglibs/util-taglib/liferay-ui/icon.html) \nfor the full list. \n\n## Related Topics\n\n- [Clay Icons](/docs/7-2/reference/-/knowledge_base/r/clay-icons)\n- [Liferay UI Icon Lists](/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-lists)\n- [Liferay UI Icon Menus](/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-menus)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/04-liferay-ui-taglibs/03-liferay-ui-icon-lists.markdown",
    "content": "---\nheader-id: liferay-ui-icon-lists\n---\n\n# Liferay UI Icon Lists\n\n[TOC levels=1-4]\n\nAn icon list displays icons in a horizontal list, instead of in a pop-up \nnavigation menu like an \n[icon menu](/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-menus). You \ncan see an example of an icon list menu in a message board thread. The thread's \nactions are visible at all times for administrators:\n\n![Figure 1: Icon lists display an app's actions at all times.](../../../../images/liferay-ui-taglib-icon-list.png)\n\nCreate the list menu with the `liferay-ui:icon-list` tag and nest \n[icons](/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icons) \nfor each list item, as shown below:\n\n```markup\n<div class=\"thread-actions\">\n    <liferay-ui:icon-list>\n\n        <liferay-ui:icon\n        iconCssClass=\"icon-lock\"\n        message=\"permissions\"\n        method=\"get\"\n        url=\"<%= permissionsURL %>\"\n        useDialog=\"<%= true %>\"\n        />\n\n        <liferay-rss:rss\n        delta=\"<%= rssDelta %>\"\n        displayStyle=\"<%= rssDisplayStyle %>\"\n        feedType=\"<%= rssFeedType %>\"\n        url=\"<%= MBRSSUtil.getRSSURL(plid, 0, message.getThreadId(), 0, themeDisplay) %>\"\n        />\n\n        <liferay-ui:icon\n        iconCssClass=\"icon-remove-sign\"\n        message=\"unsubscribe\"\n        url=\"<%= unsubscribeURL %>\"\n        />\n\n        <liferay-ui:icon\n        iconCssClass=\"icon-lock\"\n        message=\"lock\"\n        url=\"<%= lockThreadURL %>\"\n        />\n\n        <liferay-ui:icon\n        iconCssClass=\"icon-move\"\n        message=\"move\"\n        url=\"<%= editThreadURL %>\"\n        />\n\n        <liferay-ui:icon-delete\n        showIcon=\"<%= true %>\"\n        trash=\"<%= trashHelper.isTrashEnabled(themeDisplay.getScopeGroupId()) %>\"\n        url=\"<%= deleteURL %>\"\n        />\n    </liferay-ui:icon-list>\n</div>\n```\n\nSee the \n[Icon List taglibdocs](@platform-ref@/7.2-latest/taglibs/util-taglib/liferay-ui/icon-list.html) \nfor the full list of available attributes.\n\n## Related Topics\n\n- [Clay Icons](/docs/7-2/reference/-/knowledge_base/r/clay-icons)\n- [Liferay UI Icon Menus](/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-menus)\n- [Liferay UI Icons](/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icons)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/04-liferay-ui-taglibs/04-liferay-ui-icon-menus.markdown",
    "content": "---\nheader-id: liferay-ui-icon-menus\n---\n\n# Liferay UI Icon Menus\n\n[TOC levels=1-4]\n\nYou can add a pop-up navigation menu to your app with the `liferay-ui:icon-menu` \ntag. Icon menus display menu options when needed, storing them away in \na collapsed menu when they're not. This keeps the UI clean and uncluttered. Just \nas with an icon list, you nest \n[icons](/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icons) for each \nnavigation item. You can see an example of a icon menu in a site's actions menu \nin the My Sites portlet:\n\n![Figure 1: Setting up an icon menu is a piece of cake.](../../../../images/liferay-ui-taglib-icon-menu.png)\n\nExample JSP configuration:\n\n```markup\n<liferay-ui:icon-menu\n    direction=\"left-side\"\n    icon=\"<%= StringPool.BLANK %>\"\n    markupView=\"lexicon\"\n    message=\"<%= StringPool.BLANK %>\"\n    showWhenSingleIcon=\"<%= true %>\"\n>\n\n\t\t\t\t<liferay-ui:icon\n\t\t\t\t\tmessage=\"go-to-public-pages\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\turl=\"<%= group.getDisplayURL(themeDisplay, false) %>\"\n\t\t\t\t/>\n\n\t\t\t\t<liferay-ui:icon\n\t\t\t\t\tmessage=\"leave\"\n\t\t\t\t\turl=\"<%= leaveURL %>\"\n\t\t\t\t/>\n\n</liferay-ui:icon-menu>\n```\n\nNote that the `url` attribute is required for icons to render properly. See the \n[Icon Menu taglibdocs](@platform-ref@/7.2-latest/taglibs/util-taglib/liferay-ui/icon-menu.html) \nfor the full list of attributes. \n\n## Related Topics\n\n- [Clay Icons](/docs/7-2/reference/-/knowledge_base/r/clay-icons)\n- [Liferay UI Icon Lists](/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-lists)\n- [Liferay UI Icons](/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icons)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/04-liferay-ui-taglibs/05-liferay-ui-tabs.markdown",
    "content": "---\nheader-id: liferay-ui-tabs\n---\n\n# Liferay UI Tabs\n\n[TOC levels=1-4]\n\nTabs create dividers that organize content into individual sections. Content can \nbe embedded or included from another JSP. \n\nTo add tabs to your app, use the `<liferay-ui:tabs>` tag and specify each tab's \nname as a comma-separated list for the `names` attribute. For example, three \ntabs named `tab1`, `tab2`, and `tab3`, look like this in the JSP:\n\n```markup\n<liferay-ui:tabs names=\"tab1,tab2,tab3\">\n\n</liferay-ui:tabs>\n```\n\nEach tab requires a corresponding section to display content. Nest \n`liferay-ui:section` tags for each of the tabs. Within each section, you can add \nHTML content or add content indirectly by including content from another JSP \n(via the `<%@ includefile=\"filepath\"%>` directive). The example snippet below is \nfrom the Calendar portlet's `configuration.jsp`:\n\n```markup\n<liferay-ui:tabs\n    names='<%= \"user-settings,display-settings,rss\" %>'\n    param=\"tabs2\"\n    refresh=\"<%= false %>\"\n    type=\"tabs nav-tabs-default\"\n>\n    <liferay-ui:section>\n        <%@ include file=\"/configuration/user_settings.jspf\" %>\n    </liferay-ui:section>\n\n    <liferay-ui:section>\n        <%@ include file=\"/configuration/display_settings.jspf\" %>\n    </liferay-ui:section>\n\n    <liferay-ui:section>\n        <%@ include file=\"/configuration/rss.jspf\" %>\n    </liferay-ui:section>\n</liferay-ui:tabs>\n```\n\n![Figure 1: Tabs are a useful way to organize configuration options into individual sections within the same UI.](../../../../images/liferay-ui-taglib-tabs.png)\n\nThe example above uses some of the tab's available attributes. See the \n[Tabs taglibdocs](@platform-ref@/7.2-latest/taglibs/util-taglib/liferay-ui/tabs.html) \nfor the full list of attributes. \n\n## Related Topics\n\n- [Clay Navigation Bars](/docs/7-2/reference/-/knowledge_base/r/clay-navigation-bars)\n- [Clay Dropdown Menus and Action Menus](/docs/7-2/reference/-/knowledge_base/r/clay-dropdown-menus-and-action-menus)\n- [Liferay UI Icon Help](/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-help)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/04-liferay-ui-taglibs/06-liferay-ui-icon-help.markdown",
    "content": "---\nheader-id: liferay-ui-icon-help\n---\n\n# Liferay UI Icon Help\n\n[TOC levels=1-4]\n\nThe icon help tag lets you communicate additional information to your users in \nan unobtrusive way. It renders as an iconic question mark that provides more \ninformation through a pop-up tooltip on mouse over. You can see an example of \nthis in the Control Panel:\n\n![Figure 1: Here's an example of the icon help tag.](../../../../images/liferay-ui-taglib-tooltip.png)\n\n| **Note:** If you have installed a custom theme you may also need to add the\n| following imports to your `view.jsp` to make `liferay-ui:icon-help` tag work:\n| \n|     <%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\"%>\n|     <liferay-theme:defineObjects />\n\nAdd the `<liferay-ui:icon-help/>` tag next to the UI that needs tooltip \ninformation. Define the informational text with the required `message` \nattribute. Below is an example snippet for one of the Server Administration's \nclean up actions:\n\n```markup\n<h5>\n    <liferay-ui:message key=\"clean-up-permissions\" />\n    <liferay-ui:icon-help message=\"clean-up-permissions-help\" />\n</h5>\n```\n\n![Figure 2: help icons are used throughout the Control Panel.](../../../../images/liferay-ui-taglib-tooltip-02.png)\n\nNote that the message is supplied via a \n[language key](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-application). \nWhile you can use a string for the tooltip's message for testing purposes, a \nlanguage key is considered best practice and should be used in production. \n\n## Related Topics\n\n- [Clay Badges](/docs/7-2/reference/-/knowledge_base/r/clay-badges)\n- [Clay Stickers](/docs/7-2/reference/-/knowledge_base/r/clay-stickers)\n- [Liferay UI Icon Menus](/docs/7-2/reference/-/knowledge_base/r/liferay-ui-icon-menus)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/05-liferay-frontend-taglibs/01-using-liferay-frontend-taglibs-intro.markdown",
    "content": "---\nheader-id: using-liferay-front-end-taglibs-in-your-portlet\n---\n\n# Using Liferay Front-end Taglibs in Your Portlet\n\n[TOC levels=1-4]\n\nThe Liferay Front-end tag library provides a set of tags for creating common\nfront-end UI components in your app. \n\nTo use the Front-end taglib in you apps, add the following declaration to your \nJSP:\n\n```markup\n<%@ taglib prefix=\"liferay-frontend\" uri=\"http://liferay.com/tld/frontend\" %>\n```\n\nThe Liferay Front-end taglib is also available via a macro for your FreeMarker \ntheme templates and web content templates. Follow this syntax:\n\n```markup\n<@liferay_frontend[\"tag-name\"] attribute=\"string value\" attribute=10 />\n```\n\nThe following Front-end UI components are covered in this section:\n\n- [Add Menu](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-add-menu)\n- [Cards](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-cards)\n- [Info Bar](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-info-bar)\n- [Management Bar](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-management-bar)\n\nEach article contains a set of examples along with a screenshot of the resulting \nUI. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/05-liferay-frontend-taglibs/02-add-menu/01-liferay-frontend-add-menu-intro.markdown",
    "content": "---\nheader-id: liferay-front-end-add-menu\n---\n\n# Liferay Front-end Add Menu\n\n[TOC levels=1-4]\n\nThe add menu tag creates an add menu button for one or multiple items. It's \nused for actions that add entities (e.g. a new blog entry), and is part of the \nManagement Bar. Use the `<liferay-frontend:add-menu>` tag to create the add \nmenu and nest a `<liferay-frontend:add-menu-item>` tag for each item. \n\n| **Note:** This pattern is deprecated as of @product-ver@. We recommend that you\n| use the Clay Management Toolbar's\n| [creation menu pattern](/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar#creation-menu)\n| instead.\n\nWhen the menu has one item, the button triggers the item's action as shown in \nthe example below for the Blogs Admin App:\n\n```markup\n<liferay-frontend:management-bar>\n  <liferay-frontend:management-bar-buttons>\n      ...\n      <liferay-frontend:add-menu\n        inline=\"<%= true %>\"\n      >\n        <liferay-frontend:add-menu-item\n          title='<%= LanguageUtil.get(request, \"add-blog-entry\") %>'\n          url=\"<%= addEntryURL %>\"\n        />\n      </liferay-frontend:add-menu>\n\n  </liferay-frontend:management-bar-buttons>\n</liferay-frontend:management-bar>\n```\n\n![Figure 1: The add button pattern consists of an `add-menu` tag and at least one `add-menu-item` tag.](../../../../../images/liferay-frontend-taglib-add-menu-one-item.png)\n\nWhen the menu has multiple items, they display in a pop-up menu. For example, \nthe Message Boards Admin application has the configuration below:\n\n```markup\n<liferay-frontend:add-menu>\n    ...\n    <liferay-frontend:add-menu-item title='<%= LanguageUtil.get(request,\n    \"thread\") %>' url=\"<%= addMessageURL.toString() %>\" />\n    ...\n    <liferay-frontend:add-menu-item title='<%= LanguageUtil.get(request,\n    (categoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) ?\n    \"category[message-board]\" : \"subcategory[message-board]\") %>'\n    url=\"<%= addCategoryURL.toString() %>\" />\n    ...\n</liferay-frontend:add-menu>\n```\n\n![Figure 2: The add button pattern consists of an `add-menu` tag and at least one `add-menu-item` tag.](../../../../../images/liferay-frontend-taglib-add-menu-items.png)\n\nThe examples above use some of the available attributes. See the \n[add menu](@app-ref@/foundation/latest/taglibdocs/liferay-frontend/add-menu.html) \nand \n[add menu item](@app-ref@/foundation/latest/taglibdocs/liferay-frontend/add-menu-item.html) \ntaglibdocs for the full list of available attributes for the tags. \n\n## Related Topics\n\n- [Liferay Frontend Cards](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-cards)\n- [Liferay Frontend Info Bar](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-info-bar)\n- [Liferay Frontend Management Bar](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-management-bar)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/05-liferay-frontend-taglibs/03-cards/01-liferay-frontend-cards-intro.markdown",
    "content": "---\nheader-id: liferay-front-end-cards\n---\n\n# Liferay Front-end Cards\n\n[TOC levels=1-4]\n\nIf you have data you want to compare that's heavy on image usage, cards are the\ncomponent for the job. Cards visually represent data in a minimal and compact\nformat. Use them for images, document libraries, user profiles, and more. There\nare four main types of Cards:\n\n- Horizontal Cards\n- Icon Cards\n- Vertical Cards\n- User Cards\n\nExamples of each card are shown below.\n\n## Horizontal Card\n\nHorizontal cards are used primarily to display documents, such as files and \nfolders. An example configuration is shown below:\n\n```markup\n<liferay-frontend:horizontal-card\n\ttext=\"Documents\"\n  url=\"https://portal.liferay.dev/docs/7-1/tutorials/-/knowledge_base/t/clay-icons\"\n>\n\t<liferay-frontend:horizontal-card-col>\n\t\t\t\t<liferay-frontend:horizontal-card-icon\n\t\t\t\t\ticon=\"folder\"\n\t\t\t\t/>\n\t</liferay-frontend:horizontal-card-col>\n</liferay-frontend:horizontal-card>\n```\n\n![Figure 1: Horizontal cards are perfect to display files and documents.](../../../../../images/liferay-frontend-taglib-cards-horizontal.png)\n\nThe `<liferay-frontend:horizontal-card-icon>` tag uses \n[Clay Icons](/docs/7-2/reference/-/knowledge_base/r/clay-icons) for its `icon` \nattribute. \n\n## Icon Vertical Card\n\nIcon vertical cards, as the name suggests, are cards that display information in\na vertical format that emphasizes an icon. These cards show content that doesn't\nhave an associated image. Instead, an icon representing the type of content is\ndisplayed. The example snippet below displays information for a web content\narticle:\n\n```markup\n<liferay-frontend:icon-vertical-card\n  cssClass=\"article-preview-content\"\n  icon=\"web-content\"\n  title=\"<%= title %>\"\n>\n  <liferay-frontend:vertical-card-sticker-bottom>\n    <liferay-ui:user-portrait\n      cssClass=\"sticker sticker-bottom\"\n      userId=\"<%= assetRenderer.getUserId() %>\"\n    />\n  </liferay-frontend:vertical-card-sticker-bottom>\n\n  <liferay-frontend:vertical-card-footer>\n    <aui:workflow-status \n      markupView=\"lexicon\" \n      showIcon=\"<%= false %>\" \n      showLabel=\"<%= false %>\" \n      status=\"<%= article.getStatus() %>\" \n    />\n  </liferay-frontend:vertical-card-footer>\n</liferay-frontend:icon-vertical-card>\n```\n\n![Figure 2: Vertical icon cards are perfect to display an entity selection, such as a web content article.](../../../../../images/liferay-frontend-taglib-cards-icon-vertical.png)\n\n## Vertical Card\n\nVertical cards display information in a vertical card format, as opposed to\na horizontal format. If the content has an associated image (like a blog header\nimage) you can use a vertical card to display the image. If there is no\nassociated image, you can use an icon vertical card to represent the content's\ntype instead (e.g. a PDF file). The example below displays a vertical card for\na web content article when an image preview is available:\n\n```markup\n<liferay-frontend:vertical-card\n  cssClass=\"article-preview-content\"\n  imageUrl=\"<%= articleImageURL %>\"\n  title=\"<%= title %>\"\n>\n  <liferay-frontend:vertical-card-sticker-bottom>\n    <liferay-ui:user-portrait\n      cssClass=\"sticker sticker-bottom\"\n      userId=\"<%= assetRenderer.getUserId() %>\"\n    />\n  </liferay-frontend:vertical-card-sticker-bottom>\n\n  <liferay-frontend:vertical-card-footer>\n    <aui:workflow-status \n      markupView=\"lexicon\" \n      showIcon=\"<%= false %>\" \n      showLabel=\"<%= false %>\" \n      status=\"<%= article.getStatus() %>\" \n    />\n  </liferay-frontend:vertical-card-footer>\n</liferay-frontend:vertical-card>\n```\n\n![Figure 3: Vertical cards are perfect to display files and documents.](../../../../../images/liferay-frontend-taglib-cards-vertical.png)\n\n## HTML Vertical Card\n\nThe HTML Vertical card lets you display custom HTML in the header of the \nvertical card. The example below embeds a video:\n\n```markup\n<liferay-util:buffer var = \"customThumbnailHtml\">\n\t<div class=\"embed-responsive embed-responsive-16by9\">\n\t  <iframe class=\"embed-responsive-item\" \n    src=\"https://www.youtube.com/embed/8Bg9jPJpGOM?rel=0\" \n    allowfullscreen></iframe>\n\t</div>\n</liferay-util:buffer>\n\n<div class=\"container\">\n  <div class=\"row\">\n    <div class=\"col-md-4\">\n      <liferay-frontend:html-vertical-card\n      \thtml=\"<%= customThumbnailHtml %>\"\n      \ttitle=\"My Video\"\n      >\n      </liferay-frontend:html-vertical-card>\n    </div>\n  </div>\n</div>\n```\n\n![Figure 4: Html vertical cards let you display custom HTML in the card's header.](../../../../../images/liferay-frontend-taglib-cards-html-vertical.png)\n\n## User Vertical Card\n\nThe User Vertical card displays user profile selections in the icon view of the\nManagement Bar. Below is an example snippet from the User Admin portlet:\n\n```markup\n<liferay-frontend:user-vertical-card\n  actionJsp=\"/membership_request_action.jsp\"\n  actionJspServletContext=\"<%= application %>\"\n  resultRow=\"<%= row %>\"\n  subtitle=\"<%= membershipRequestUser.getEmailAddress() %>\"\n  title=\"<%= HtmlUtil.escape(membershipRequestUser.getFullName()) %>\"\n  userId=\"<%= membershipRequest.getUserId() %>\"\n>\n  <liferay-frontend:vertical-card-header>\n    <liferay-ui:message \n      arguments=\"<%= LanguageUtil.getTimeDescription(\n      request, \n      System.currentTimeMillis() - membershipRequest.getCreateDate().getTime(), \n      true) %>\" \n      key=\"x-ago\" \n      translateArguments=\"<%= false %>\" \n    />\n  </liferay-frontend:vertical-card-header>\n</liferay-frontend:user-vertical-card>\n```\n\n![Figure 5: User vertical cards are perfect to display files and documents.](../../../../../images/liferay-frontend-taglib-cards-user-vertical.png)\n\n## Related Topics\n\n- [Liferay Front-end Add Menu](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-add-menu)\n- [Liferay Front-end Info Bar](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-info-bar)\n- [Liferay Front-end Management Bar](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-management-bar)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/05-liferay-frontend-taglibs/04-info-bar/01-liferay-frontend-info-bar-intro.markdown",
    "content": "---\nheader-id: liferay-front-end-info-bar\n---\n\n# Liferay Front-end Info Bar\n\n[TOC levels=1-4]\n\nAn info bar provides a button that toggles the visibility of additional sidebar \ninformation. This is perfect for providing more detailed metadata for a search \nresult, such as the file size, type, URL, etc. \n\n![Figure 1: The info bar tags create a sidebar panel toggler that reveals additional info.](../../../../../images/liferay-frontend-taglib-info-bar-article.png)\n\nThe configuration has two key parts: the info bar---and buttons---and the\nsidebar panel. \n\nInfo bar:\n\n```markup\n<liferay-frontend:info-bar>\n  <liferay-frontend:info-bar-buttons>\n    <liferay-frontend:info-bar-sidenav-toggler-button\n      icon=\"info-circle\"\n      label=\"my info\"\n    />\n  </liferay-frontend:info-bar-buttons>\n</liferay-frontend:info-bar>\n```\n\nThe `<liferay-frontend:info-bar-sidenav-toggler-button>` tag uses \n[Clay Icons](/docs/7-2/reference/-/knowledge_base/r/clay-icons) \nfor the `icon` attribute. \n\nSidebar panel:\n\n```markup\n<div class=\"closed container-fluid-1280 sidenav-container sidenav-right\" id=\"<portlet:namespace />infoPanelId\">\n    <liferay-frontend:sidebar-panel>\n      <div>\n      <h2>sidebar content</h2>\n      <p>Here is some content</p>\n      </div>\n    </liferay-frontend:sidebar-panel>\n</div>\n```\n\nNote that the sidebar panel's wrapper `<div>` has the classes `closed` and \n`sidenav-right`. The info button toggles the classes `open` and `closed`, \nshowing and hiding the sidebar panel. The `sidenav-right` class specifies that \nthe panel should open on the right.\n\n![Figure 2: The info bar tags create a sidebar panel toggler that reveals additional info.](../../../../../images/liferay-frontend-taglib-info-bar.png)\n\nThe examples above use some of the available attributes. See the \n[info bar](@app-ref@/foundation/latest/taglibdocs/liferay-frontend/info-bar.html), \n[info bar buttons](@app-ref@/foundation/latest/taglibdocs/liferay-frontend/info-bar-buttons.html), \n[info bar sidenav toggler button](@app-ref@/foundation/latest/taglibdocs/liferay-frontend/info-bar-sidenav-toggler-button.html), \nand \n[sidebar panel](@app-ref@/foundation/latest/taglibdocs/liferay-frontend/sidebar-panel.html) \ntaglibdocs for the full list of available attributes for the tags. \n\n## Related Topics\n\n- [Liferay Front-end Add Menu](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-add-menu)\n- [Liferay Front-end Cards](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-cards)\n- [Liferay Front-end Management Bar](/docs/7-2/reference/-/knowledge_base/r/liferay-front-end-management-bar)\n\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/05-liferay-frontend-taglibs/05-liferay-frontend-management-bar/01-liferay-frontend-management-bar-intro.markdown",
    "content": "---\nheader-id: liferay-front-end-management-bar\n---\n\n# Liferay Front-end Management Bar\n\n[TOC levels=1-4]\n\nThe Management Bar gives administrators control over search container results. \nIt lets you filter, sort, and choose a display style for search results, so you \ncan quickly identify the document, web content, asset entry, or whatever you're \nlooking for in your app. The Management Bar is fully customizable, so you can \nimplement all the controls, or just the ones your app requires. \n\n![Figure 1: The Management Bar lets the user customize how the app displays content.](../../../../../images/liferay-frontend-taglib-management-bar-message-boards.png)\n\n| **Note:** The Liferay Front-end Management Bar is deprecated as of @product-ver@.\n| We recommend that you use the\n| [Clay Management Toolbar](/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar)\n| instead.\n\nThe Management Bar has a few key sections. Each section is grouped and \nconfigured using different taglibs:\n\nThe \n[`<liferay-frontend:management-bar-buttons>` tag](@app-ref@/foundation/latest/taglibdocs/liferay-frontend/management-bar-buttons.html) \nwraps the Management Bar's button elements:\n\n![Figure 2: The `management-bar-buttons` tag contains the Management Bar's main buttons.](../../../../../images/liferay-frontend-taglib-management-bar-buttons.png)\n\nThe \n[`<liferay-frontend:management-bar-sidenav-toggler-button>` tag](@app-ref@/foundation/latest/taglibdocs/liferay-frontend/management-bar-sidenav-toggler-button.html) \nimplements slide-out navigation for the info button.\n\nThe \n[`<liferay-frontend:management-bar-display-buttons>` tag](@app-ref@/foundation/latest/taglibdocs/liferay-frontend/management-bar-display-buttons.html) \nrenders the app's display style options.\n\n![Figure 3: The `management-bar-display-buttons` tag contains the content's display options.](../../../../../images/liferay-frontend-taglib-management-bar-display-buttons.png)\n\nThe \n[`<liferay-frontend:management-bar-filters>` tag](@app-ref@/foundation/latest/taglibdocs/liferay-frontend/management-bar-filters.html) \nwraps the app's filtering options. This filter should be included in all control \npanel applications. Filtering options can include sort criteria, sort ordering, \nand more. \n\n![Figure 4: The `management-bar-filters` tag contains the content filtering options.](../../../../../images/liferay-frontend-taglib-management-bar-filters.png)\n\nFinally, the \n[`<liferay-frontend:management-bar-action-buttons>` tag](@app-ref@/foundation/latest/taglibdocs/liferay-frontend/management-bar-action-buttons.html) \nwraps the actions that you can execute over selected items. You can select \nmultiple items between pages. The management bar keeps track of the number of \nselected items for you. \n\n![Figure 5: The management bar keeps track of the items selected and displays the actions to execute on them.](../../../../../images/liferay-frontend-taglib-management-bar-action-buttons.png)\n\nFor example, here's the Management Bar configuration in the Trash app:\n\n```markup\n<liferay-frontend:management-bar\n   includeCheckBox=\"<%= true %>\"\n   searchContainerId=\"trash\"\n>\n   <liferay-frontend:management-bar-buttons>\n       <liferay-frontend:management-bar-sidenav-toggler-button />\n\n       <liferay-portlet:actionURL name=\"changeDisplayStyle\"\n       varImpl=\"changeDisplayStyleURL\">\n           <portlet:param name=\"redirect\" value=\"<%= currentURL %>\" />\n       </liferay-portlet:actionURL>\n\n       <liferay-frontend:management-bar-display-buttons\n           displayViews='<%= new String[] {\"descriptive\", \"icon\",\n           \"list\"} %>'\n           portletURL=\"<%= changeDisplayStyleURL %>\"\n           selectedDisplayStyle=\"<%= trashDisplayContext.getDisplayStyle()\n           %>\"\n       />\n   </liferay-frontend:management-bar-buttons>\n\n   <liferay-frontend:management-bar-filters>\n       <liferay-frontend:management-bar-navigation\n           navigationKeys='<%= new String[] {\"all\"} %>'\n           portletURL=\"<%= trashDisplayContext.getPortletURL() %>\"\n       />\n\n       <liferay-frontend:management-bar-sort\n           orderByCol=\"<%= trashDisplayContext.getOrderByCol() %>\"\n           orderByType=\"<%= trashDisplayContext.getOrderByType() %>\"\n           orderColumns='<%= new String[] {\"removed-date\"} %>'\n           portletURL=\"<%= trashDisplayContext.getPortletURL() %>\"\n       />\n   </liferay-frontend:management-bar-filters>\n\n   <liferay-frontend:management-bar-action-buttons>\n       <liferay-frontend:management-bar-sidenav-toggler-button />\n\n       <liferay-frontend:management-bar-button href=\"javascript:;\"\n       icon=\"trash\" id=\"deleteSelectedEntries\" label=\"delete\" />\n   </liferay-frontend:management-bar-action-buttons>\n</liferay-frontend:management-bar>\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/05-liferay-frontend-taglibs/05-liferay-frontend-management-bar/02-including-actions-in-the-management-bar.markdown",
    "content": "---\nheader-id: including-actions-in-the-management-bar\n---\n\n# Including Actions in the Management Bar\n\n[TOC levels=1-4]\n\nWhile an actions menu is typically included with each search container result, \nyou can also include these actions in the management bar. This keeps everything \norganized within the same UI. This update adds a checkbox next to each search \ncontainer result, as well as adds one in the management bar itself to select all \nresults. The actions are displayed when a checkbox is checked---individual or \nselect all---and hidden from view otherwise. \n\n![Figure 1: You can select individual results or all results at once.](../../../../../images/liferay-frontend-taglib-management-bar-include-checkbox.png)\n\nFollow these steps to include actions in your management bar:\n\n1.  Update the `<liferay-frontend:management-bar>` tag to include the checkbox \n    and provide the search container's ID:\n\n    ```markup\n    <liferay-frontend:management-bar\n    \tincludeCheckBox=\"<%= true %>\"\n    \tsearchContainerId=\"mySearchContainerId\"\n    >\n    ```\n\n2.  After the closing `</liferay-frontend:management-bar-filters>` tag, add the \n    `<liferay-frontend:management-bar-action-buttons>` tags:\n\n    ```markup\n    <liferay-frontend:management-bar-action-buttons>\n\n    </liferay-frontend:management-bar-action-buttons>\n    ```\n\n3.  Use the available management bar button taglibs \n    (e.g. `management-bar-button`) to build the action buttons for your app's \n    management bar. A code snippet from the Site admin portlet is shown below: \n\n    ```markup\n    <liferay-frontend:management-bar-action-buttons>\n    \t<liferay-frontend:management-bar-sidenav-toggler-button\n    \t\ticon=\"info-circle\"\n    \t\tlabel=\"info\"\n    \t/>\n\n    \t<liferay-frontend:management-bar-button\n    \t\thref=\"javascript:deleteEntries();\"\n    \t\ticon=\"trash\"\n    \t\tid=\"deleteSites\"\n    \t\tlabel=\"delete\"\n    \t/>\n    </liferay-frontend:management-bar-action-buttons>\n    ```\n\n![Figure 2: You can have as many actions as your app requires.](../../../../../images/liferay-frontend-taglib-management-bar-actions.png)\n\n## Related Topics\n\n- [Disabling All or Portions of the Management Bar](/docs/7-2/reference/-/knowledge_base/r/disabling-all-or-portions-of-the-management-bar)\n- [Clay Management Toolbar](/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/05-liferay-frontend-taglibs/05-liferay-frontend-management-bar/03-disabling-the-management-bar.markdown",
    "content": "---\nheader-id: disabling-all-or-portions-of-the-management-bar\n---\n\n# Disabling All or Portions of the Management Bar\n\n[TOC levels=1-4]\n\nWhen there are no search results to display, you should disable all the \nManagement Bar's buttons, except the sidenav toggler button. \n\nYou can disable the Management Bar by adding the `disabled` attribute to the \n`liferay-frontend:management-bar` tag:\n\n```markup\n<liferay-frontend:management-bar\n        disabled=\"<%= total == 0 %>\"\n        includeCheckBox=\"<%= true %>\"\n        searchContainerId=\"<%= searchContainerId %>\"\n>\n```\n\nYou can also disable individual components by adding the `disabled` attribute to \nthe corresponding tag. The example below disables the display buttons when the \nsearch container displays 0 results, since changing the display style has no \neffect when there aren't any results to view:\n\n```markup\n<liferay-frontend:management-bar-display-buttons\n        disabled=\"<%= total == 0 %>\"\n        displayViews='<%= new String[] {\"descriptive\", \"icon\", \"list\"} %>'\n        portletURL=\"<%= changeDisplayStyleURL %>\"\n        selectedDisplayStyle=\"<%= displayStyle %>\"\n/>\n```\n\n![Figure 1: You can disable all or portions of the Management Bar.](../../../../../images/liferay-frontend-taglib-management-bar-disabled.png)\n\n## Related Topics\n\n- [Including Actions in the Management Bar](/docs/7-2/reference/-/knowledge_base/r/including-actions-in-the-management-bar)\n- [Clay Management Toolbar](/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/06-liferay-util-taglibs/01-using-liferay-util-taglibs-intro.markdown",
    "content": "---\nheader-id: using-the-liferay-util-taglib\n---\n\n# Using the Liferay Util Taglib\n\n[TOC levels=1-4]\n\nThe Liferay Util taglib is used to pull other resources into a portlet or theme. \nYou can use it to specify which resources to insert at the bottom or top of the \npage's HTML. \n\nTo use the Liferay-Util taglib, add the following declaration to your JSP:\n\n```markup\n<%@ taglib prefix=\"liferay-util\" uri=\"http://liferay.com/tld/util\" %>\n```\n\nThe Liferay-Util taglib is also available via a macro for your FreeMarker theme \ntemplates and web content templates. Follow this syntax:\n\n```markup\n<@liferay_util[\"tag-name\"] attribute=\"string value\" attribute=10 />\n```\n\nThis section covers the available Liferay Util tags you can use in your app to \ninject content into portlets and themes. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/06-liferay-util-taglibs/02-liferay-util-body-bottom.markdown",
    "content": "---\nheader-id: using-liferay-util-body-bottom\n---\n\n# Using Liferay Util Body Bottom\n\n[TOC levels=1-4]\n\nThe body bottom tag is not a self-closing tag. It lets you add additional HTML \nor scripts to the bottom of the `body` tag. content placed between the opening \nand closing of this tag is passed to the \n[body_bottom.jsp](https://github.com/liferay/liferay-portal/blob/7.2.x/portal-web/docroot/html/common/themes/body_bottom.jsp#L26-L31) \nand outputs in this JSP. \n\nThis tag also has an optional `outputKey` attribute. If several portlets \non the page include the same resource with this tag, you can specify the same \n`outputKey` value for each tag so the resource is only loaded once. \n\nThe example configuration below uses the `<liferay-util:body-bottom>` tag to \ninclude JavaScript provided by the portlet's bundle:\n\n```markup\n<liferay-util:body-bottom outputKey=\"bodybottom\" >\n\t<script \n  src=\"/o/my-liferay-util-portlet/js/my_custom_javascript_body_bottom.js\" \n  type=\"text/javascript\"></script>\n</liferay-util:body-bottom>\n```\n\nNow you know how to use the `<liferay-util:body-bottom>` tag to include \nadditional resources in the bottom of the page's body. \n\n## Related Topics\n\n- [Using the Liferay Util HTML Body Top Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-body-top)\n- [Using the Liferay Util HTML Top Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-html-top)\n- [Using the Liferay UI Taglib](/docs/7-2/reference/-/knowledge_base/r/using-the-liferay-ui-taglib)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/06-liferay-util-taglibs/03-liferay-util-body-top.markdown",
    "content": "---\nheader-id: using-liferay-util-body-top\n---\n\n# Using Liferay Util Body Top\n\n[TOC levels=1-4]\n\nThe body top tag is not a self-closing tag. The content placed between the \nopening and closing of this tag is moved to the top of the `body` tag. When \nsomething is passed using this taglib, the \n[body_top.jsp](https://github.com/liferay/liferay-portal/blob/7.2.x/portal-web/docroot/html/common/themes/body_top.jsp#L25-L31) \nis passed markup and outputs in this JSP. \n\nThis tag also has an optional `outputKey` attribute. If several portlets \non the page include the same resource with this tag, you can specify the same \n`outputKey` value for each tag so the resource is only loaded once. \n\nThe example configuration below uses the `<liferay-util:body-top>` tag to \ninclude JavaScript provided by the portlet's bundle:\n\n```markup\n<liferay-util:body-top outputKey=\"bodytop\" >\n\t<script \n  src=\"/o/my-liferay-util-portlet/js/my_custom_javascript_body_top.js\" \n  type=\"text/javascript\"></script>\n</liferay-util:body-top>\n```\n\nNow you know how to use the `<liferay-util:body-top>` tag to include additional \nresources in the top of the page's body. \n\n## Related Topics\n\n- [Using the Liferay Util HTML Body Bottom Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-body-bottom)\n- [Using the Liferay Util HTML Bottom Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-html-bottom)\n- [Using the Clay Taglib](/docs/7-2/reference/-/knowledge_base/r/using-the-clay-taglib-in-your-portlets)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/06-liferay-util-taglibs/04-liferay-util-buffer.markdown",
    "content": "---\nheader-id: using-liferay-util-buffer\n---\n\n# Using Liferay Util Buffer\n\n[TOC levels=1-4]\n\nThe buffer tag is not a self-closing tag. The content placed between the opening \nand closing of this tag is saved to a buffer and its output is assigned to the \nJava variable declared with the tag's `var` attribute. The output is returned as \na String, letting you post-process it. For example, you can use this to \n[override a JSP's existing contents](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-osgi-fragments#provide-the-overridden-jsp). \n\nThe example below saves the link's generated markup to a buffer and then uses \nthe returned string as the argument for a `liferay-ui:message` key:\n\n```markup\n<liferay-util:buffer\n\t\tvar=\"linkContent\"\n>\n\t\t<aui:a \n\t\t\thref=\"https://www.liferay.com/\" \n\t\t\ttarget=\"_blank\">Liferay\n\t\t</aui:a>\n</liferay-util:buffer>\n\n<liferay-ui:message \n\t\targuments=\"<%= linkContent %>\" \n\t\tkey=\"see-x-for-more-information\" \n\t\ttranslateArguments=\"<%= false %>\" \n/>\n```\n\nNow you know how to use the `<liferay-util:buffer>` tag to save content to a \nbuffer. \n\n![Figure 1: You can use the Liferay Util Buffer tag to save pieces of markup to reuse in your JSP.](../../../../images/liferay-util-buffer.png)\n    \n## Related Topics\n\n- [JSP Overrides Using OSGi Fragments](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-osgi-fragments#provide-the-overridden-jsp)\n- [Using the Liferay Util Param Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-param)\n- [Using the Liferay Front-End Taglibs](/docs/7-2/reference/-/knowledge_base/r/using-liferay-front-end-taglibs-in-your-portlet)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/06-liferay-util-taglibs/05-liferay-util-dynamic-include.markdown",
    "content": "---\nheader-id: using-liferay-util-dynamic-include\n---\n\n# Using Liferay Util Dynamic Include\n\n[TOC levels=1-4]\n\nThe dynamic include tag lets you specify a point or points in a JSP or theme \nwhere a developer can inject additional HTML, resources, or functionality, using \nthe `DynamicIncludeRegistry`. You can read more about the OSGi Service Registry \n[here](http://docs.spring.io/osgi/docs/current/reference/html/service-registry.html). \nThe `key` attribute identifies the extension point. See \n[Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/dynamic-includes) \nfor example configurations that use dynamic include extension points to inject \nadditional functionality. \n\nThe example configuration below uses the `<liferay-util:dynamic-include>` tag to \ninclude an extension point before the primary code and an extension point after \nthe primary code:\n\n```markup\n<%@ taglib uri=\"http://liferay.com/tld/util\" prefix=\"liferay-util\" %>\n\n<liferay-util:dynamic-include key=\"/path/to/jsp#pre\" />\n\n<div>\n        <p>And here we have our content</p>\n</div>\n\n<liferay-util:dynamic-include key=\"/path/to/jsp#post\" />\n```\n\nNow you know how to use the `<liferay-util:dynamic-include>` tag to add \nextension points to your app. \n    \n## Related Topics\n\n- [Dynamic Includes](/docs/7-2/customization/-/knowledge_base/c/dynamic-includes)\n- [Using the Liferay Util Body Top Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-body-top)\n- [Using the Chart Taglib](/docs/7-2/reference/-/knowledge_base/r/using-the-chart-taglib-in-your-portlets)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/06-liferay-util-taglibs/06-liferay-util-get-url.markdown",
    "content": "---\nheader-id: using-liferay-util-get-url\n---\n\n# Using Liferay Util Get URL\n\n[TOC levels=1-4]\n\nThe get URL tag scrapes the URL provided by the `url` attribute. If a value is \nprovided for the `var` attribute, the content from the screen scrape is scoped \nto that variable. Otherwise, the scraped content is displayed where the taglib \nis used. \n\nA basic configuration for the `<liferay-util:get-url>` tag is shown below:\n\n```markup\n<liferay-util:get-url url=\"https://www.liferay.com/\" />\n```\n\nHere is an example that uses the `var` attribute:\n\n```markup\n<liferay-util:get-url url=\"https://www.liferay.com/\" var=\"Liferay\" />\n\n<div>\n\t\t\t\t<h2>We borrowed <a href=\"https://www.liferay.com/\">Liferay</a>. Here it is.</h2>\n\n\t\t\t\t<div class=\"Liferay\">\n\t\t\t\t\t\t\t\t<%= Liferay %>\n\t\t\t\t</div>\n</div>\n```\n\n![Figure 1: You can use the Liferay Util Get URL tag to scrape URLs.](../../../../images/liferay-util-get-url-ldn.png)\n\nNow you know how to use the `<liferay-util:get-url>` tag to scrape URLs. \n\n## Related Topics\n\n- [Using the Liferay Util Param Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-param)\n- [Using the Liferay Util Include Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-include)\n- [Using the AUI Taglib](/docs/7-2/reference/-/knowledge_base/r/using-aui-taglibs)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/06-liferay-util-taglibs/07-liferay-util-html-bottom.markdown",
    "content": "---\nheader-id: using-liferay-util-html-bottom\n---\n\n# Using Liferay Util HTML Bottom\n\n[TOC levels=1-4]\n\nThe HTML bottom tag is not a self-closing tag. Content placed between the \nopening and closing of this tag is moved to the bottom of the `<html>` tag. When \nsomething is passed using this taglib, the \n[bottom.jsp](https://github.com/liferay/liferay-portal/blob/master/portal-web/docroot/html/common/themes/bottom.jsp#L53-L59) \nis passed markup and outputs in this JSP. \n\nThis tag also has an optional `outputKey` attribute. If several portlets \non the page include the same resource with this tag, you can specify the same \n`outputKey` value for each tag so the resource is only loaded once. \n\nThe example configuration below uses the `<liferay-util:html-bottom>` tag to \ninclude JavaScript (a common use case) provided by the portlet's bundle:\n\n```markup\n<liferay-util:html-bottom outputKey=\"htmlbottom\">\n\n    <script src=\"/o/my-liferay-util-portlet/js/my_custom_javascript.js\" \n    type=\"text/javascript\"></script>\n\n</liferay-util:html-bottom>\n```\n\nNow you know how to use the `<liferay-util:html-bottom>` tag to include \nadditional resources in the bottom of the page's HTML tag. \n\n## Related Topics\n\n- [Using the Liferay Util HTML Body Bottom Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-body-bottom)\n- [Using the Liferay Util HTML Top Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-html-top)\n- [Using the Liferay UI Taglib](/docs/7-2/reference/-/knowledge_base/r/using-the-liferay-ui-taglib)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/06-liferay-util-taglibs/08-liferay-util-html-top.markdown",
    "content": "---\nheader-id: using-liferay-util-html-top\n---\n\n# Using Liferay Util HTML Top\n\n[TOC levels=1-4]\n\nThe HTML top tag is not a self-closing tag. The content placed between the \nopening and closing of this tag is moved to the `<head>` tag. When something is \npassed using this taglib, the \n[top_head.jsp](https://github.com/liferay/liferay-portal/blob/master/portal-web/docroot/html/common/themes/top_head.jsp#L147-L153) \nis passed markup and outputs in this JSP. \n\nThis tag also has an optional `outputKey` attribute. If several portlets \non the page include the same resource with this tag, you can specify the same \n`outputKey` value for each tag so the resource is only loaded once. \n\nThe example configuration below uses the `<liferay-util:html-top>` tag to \ninclude additional CSS styles provided by the portlet's bundle:\n\n```markup\n<liferay-util:html-top outputKey=\"htmltop\">\n\t\t\t\t<link data-senna-track=\"permanent\" \n        href=\"/o/my-liferay-util-portlet/css/my-custom-styles.css\" \n        rel=\"stylesheet\" type=\"text/css\" />\n</liferay-util:html-top>\n```\n\nNow you know how to use the `<liferay-util:html-top>` tag to include additional \nresources in the top of the page's HTML tag. \n\n## Related Topics\n\n- [Using the Liferay Util HTML Bottom Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-html-bottom)\n- [Using the Liferay Util Body Top Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-body-top)\n- [Using the Clay Taglib](/docs/7-2/reference/-/knowledge_base/r/using-the-clay-taglib-in-your-portlets)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/06-liferay-util-taglibs/09-liferay-util-include.markdown",
    "content": "---\nheader-id: using-liferay-util-include\n---\n\n# Using Liferay Util Include\n\n[TOC levels=1-4]\n\nThe include tag lets you include other JSP files in your portlet's JSP, theme, \nor web content. This can increase readability as well as provide separation of \nconcerns for JSP files. \n\nThe `page` attribute is required and specifies the path to the JSP or JSPF to \ninclude. The `servletContext` refers to the request context that the included \nJSP should use. Passing `<%= application %>` to this attribute lets the included \nJSP use the same `request` object as other objects that might be set in the \nprior JSP. \n\nBelow is an example configuration for the `<liferay-util:include>` tag:\n\n```markup\n<liferay-util:include \n  page=\"/relative/path/to/file.jsp\" \n  servletContext=\"<%= application %>\"\n/>\n```\n\nNow you know how to use the `<liferay-util:include>` tag to include other JSPs \nin your portlets, themes, and web content. \n\n## Related Topics\n\n- [Using the Liferay Util Param Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-param)\n- [Using the Liferay Util Dynamic Include Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-dynamic-include)\n- [Using the Liferay Front-End Taglibs](/docs/7-2/reference/-/knowledge_base/r/using-liferay-front-end-taglibs-in-your-portlet)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/06-liferay-util-taglibs/10-liferay-util-param.markdown",
    "content": "---\nheader-id: using-liferay-util-param\n---\n\n# Using Liferay Util Param\n\n[TOC levels=1-4]\n\nThe param tag lets you set a parameter for an \n[included JSP page](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-include). \nThis configuration requires two JSPs. JSP A, the main view of the app, includes \nJSP B and sets its parameter value. This lets you dynamically set content when \nyou include the JSP. \n\nFor example, say you have your main functionality in `my-app.jsp`, and you have \nadditional functionality provided by `more-content.jsp`. You could have the \nexample configuration shown below:\n\n`more-content.jsp`:\n\n```markup\n<%@ page import=\"com.liferay.portal.kernel.util.ParamUtil\" %>\n\n<%\nString answer = ParamUtil.getString(request, \"answer\");\n%>\n\n<div>\n  <p>The answer to life, the universe and everything is <%= answer %>.</p>\n</div>\n```\n\nThen in `my-app.jsp`, you can include `more-content.jsp` and set the value of \nthe `answer` parameter:\n\n```markup\n<liferay-util:include page=\"/path/to/more-content.jsp\" servletContext=\"<%= application %>\">\n  <liferay-util:param name=\"answer\" value=\"42\" />\n</liferay-util:include>\n```\n\nThis results in the following output in `my-app.jsp`:\n\n```html\nThe answer to life, the universe and everything is 42.\n```\n\nNow you know how to use the `<liferay-util:param>` tag to set parameters for \nincluded JSPs. You can use this approach to include common reusable pieces of \ncode in your apps. \n\n## Related Topics\n\n- [Using the Liferay Util Include Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-include)\n- [Using the Liferay Util Body Top Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-body-top)\n- [Using the Chart Taglib](/docs/7-2/reference/-/knowledge_base/r/using-the-chart-taglib-in-your-portlets)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/06-liferay-util-taglibs/11-liferay-util-whitespace-remover.markdown",
    "content": "---\nheader-id: using-liferay-util-whitespace-remover\n---\n\n# Using Liferay Util Whitespace Remover\n\n[TOC levels=1-4]\n\nThe whitespace remover tag removes line breaks and tabs from code blocks \nincluded between the opening and closing of the tag. Below is an example \nconfiguration for the `<liferay-util:whitespace-remover>` tag:\n\nwith remover:\n\n```markup\n<liferay-util:whitespace-remover>\n\t<p>Here is some text with        tabs.</p>\n</liferay-util:whitespace-remover>\n```\n\nresult:\n\n```html\nHere is some text withtabs.\n```\nNow you know how to use the `<liferay-util:whitespace-remover>` tag to ensure \nthat your code formatting is consistent. \n\n## Related Topics\n\n- [Using the Liferay Util Param Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-param)\n- [Using the Liferay Util Buffer Tag](/docs/7-2/reference/-/knowledge_base/r/using-liferay-util-buffer)\n- [Using the AUI Taglib](/docs/7-2/reference/-/knowledge_base/r/using-aui-taglibs)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/01-using-clay-taglibs-intro.markdown",
    "content": "---\nheader-id: using-the-clay-taglib-in-your-portlets\n---\n\n# Using the Clay Taglib in Your portlets\n\n[TOC levels=1-4]\n\nThe Liferay Clay tag library provides a set of tags for creating \n[Clay](https://clayui.com/) \nUI components in your app. \n\n| **Note:** AUI taglibs are deprecated as of @product-ver@. We recommend that you\n| use Clay taglibs to avoid future compatibility issues.\n\nTo use the Clay taglib in your apps, add the following declaration to your JSP:\n\n```markup\n<%@ taglib prefix=\"clay\" uri=\"http://liferay.com/tld/clay\" %>\n```\nThe Liferay Clay taglib is also available via a macro for your FreeMarker theme \ntemplates and web content templates. Follow this syntax:\n\n```markup\n<@clay[\"tag-name\"] attribute=\"string value\" attribute=10 />\n```\n\nClay taglibs provide the following UI components for your apps:\n\n- [Alerts](/docs/7-2/reference/-/knowledge_base/r/clay-alerts)\n- [Badges](/docs/7-2/reference/-/knowledge_base/r/clay-badges)\n- [Buttons](/docs/7-2/reference/-/knowledge_base/r/clay-buttons)\n- [Cards](/docs/7-2/reference/-/knowledge_base/r/clay-cards)\n- [Dropdown Menus and Action Menus](/docs/7-2/reference/-/knowledge_base/r/clay-dropdown-menus-and-action-menus)\n- [Form Elements](/docs/7-2/reference/-/knowledge_base/r/clay-form-elements)\n- [Icons](/docs/7-2/reference/-/knowledge_base/r/clay-icons)\n- [Labels and links](/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links)\n- [Management Toolbar](/docs/7-2/reference/-/knowledge_base/r/clay-management-toolbar)\n- [Navigation Bars](/docs/7-2/reference/-/knowledge_base/r/clay-navigation-bars)\n- [Progress Bars](/docs/7-2/reference/-/knowledge_base/r/clay-progress-bars)\n- [Stickers](/docs/7-2/reference/-/knowledge_base/r/clay-stickers)\n\nThis section covers how to create these components with the Clay taglibs. Each \narticle contains a set of clay component examples along with a screenshot of the \nresulting UI. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/02-clay-alerts.markdown",
    "content": "---\nheader-id: clay-alerts\n---\n\n# Clay Alerts\n\n[TOC levels=1-4]\n\nClay alerts come in two types: embedded and stripe. Both types, along with \nseveral examples of each, are shown below. \n\n## Embedded Alerts\n\nEmbedded alerts are usually used inside forms. The element that contains it\ndetermines an embedded alert's width. The close action is not required for\nembedded alerts. The following embedded alerts can be created with Clay\ntaglibs:\n\nDanger alert (embedded):\n\n```markup\n<clay:alert\n\tmessage=\"This is an error message.\"\n\tstyle=\"danger\"\n\ttitle=\"Error\"\n/>\n```\n\n![Figure 1: The danger alert notifies the user of an error or issue.](../../../../images/clay-taglib-alert-danger.png)\n\nSuccess alert (embedded):\n\n```markup\n<clay:alert\n\tmessage=\"This is a success message.\"\n\tstyle=\"success\"\n\ttitle=\"Success\"\n/>\n```\n\n![Figure 2: The success alert notifies the user when an action is successful.](../../../../images/clay-taglib-alert-success.png)\n\nInfo alert (embedded):\n\n```markup\n<clay:alert\n\tmessage=\"This is an info message.\"\n\ttitle=\"Info\"\n/>\n```\n\n![Figure 3: The info alert displays general information to the user.](../../../../images/clay-taglib-alert-info.png)\n\nWarning alert (embedded):\n\n```markup\n<clay:alert\n\tmessage=\"This is a warning message.\"\n\tstyle=\"warning\"\n\ttitle=\"Warning\"\n/>\n```\n\n![Figure 4: The warning alert displays a warning message to the user.](../../../../images/clay-taglib-alert-warning.png)\n\n## Stripe Alerts\n\nStripe alerts are placed below the last navigation element (either the header or \nthe navigation bar), and they usually appear on *Save* action, communicating \nthe status of the action once received from the server. Unlike embedded alerts, \nstripe alerts require the close action. A stripe alert is always the full width \nof the container and pushes all the content below it. The following stripe \nalerts can be created with Clay taglibs:\n\nDanger alert (stripe):\n\n```markup\n<clay:stripe\n\tmessage=\"This is an error message.\"\n\tstyle=\"danger\"\n\ttitle=\"Error\"\n/>\n```\n\n![Figure 5: The danger striped alert notifies the user that an action has failed.](../../../../images/clay-taglib-alert-danger-stripe.png)\n\nSuccess alert (stripe):\n\n```markup\n<clay:stripe\n\tmessage=\"This is a success message.\"\n\tstyle=\"success\"\n\ttitle=\"Success\"\n/>\n```\n\n![Figure 6: The success striped alert notifies the user that an action has completed successfully.](../../../../images/clay-taglib-alert-success-stripe.png)\n\nInfo alert (stripe):\n\n```markup\n<clay:stripe\n\tmessage=\"This is an info message.\"\n\ttitle=\"Info\"\n/>\n```\n\n![Figure 7: The info striped alert displays general information about an action to the user.](../../../../images/clay-taglib-alert-info-stripe.png)\n\nWarning alert (stripe):\n\n```markup\n<clay:stripe\n\tmessage=\"This is a warning message.\"\n\tstyle=\"warning\"\n\ttitle=\"Warning\"\n/>\n```\n \n![Figure 8: The warning striped alert warns the user about an action.](../../../../images/clay-taglib-alert-warning-stripe.png)\n\nNow you know how to alert users!\n\n## Related Topics\n\n- [Clay Buttons](/docs/7-2/reference/-/knowledge_base/r/clay-buttons)\n- [Clay Form Elements](/docs/7-2/reference/-/knowledge_base/r/clay-form-elements)\n- [Clay Labels and Links](/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/03-clay-badges.markdown",
    "content": "---\nheader-id: clay-badges\n---\n\n# Clay Badges\n\n[TOC levels=1-4]\n\nBadges help highlight important information such as notifications or new and \nunread messages. Badges have circular borders and are only used to specify a \nnumber. This covers the different types of Clay badges you can add to your app. \n\n## Badge Types\n\nThe following badge styles are available:\n\nPrimary badge:\n\n```markup\n<div class=\"col-md-1\">\n    <clay:badge label=\"8\" />\n\n    <div>Primary</div>\n</div>\n```\n\n![Figure 1: A primary badge is bright blue, commanding attention like the primary button of a form.](../../../../images/clay-taglib-badge-primary.png)\n\nSecondary badge:\n\n```markup\n<div class=\"col-md-1\">\n    <clay:badge label=\"87\" style=\"secondary\" />\n\n    <div>Secondary</div>\n</div>\n```\n\n![Figure 2: A secondary badge is light-grey and draws less focus than a primary button.](../../../../images/clay-taglib-badge-secondary.png)\n\nInfo badge:\n\n```markup\n<div class=\"col-md-1\">\n    <clay:badge label=\"91\" style=\"info\" />\n\n    <div>Info</div>\n</div>\n```\n\n![Figure 3: A info badge is dark blue and meant for numbers related to general information.](../../../../images/clay-taglib-badge-info.png)\n\nError badge:\n\n```markup\n<div class=\"col-md-1\">\n    <clay:badge label=\"130\" style=\"danger\" />\n\n    <div>Error</div>\n</div>\n```\n\n![Figure 4: An error badge displays numbers related to an error.](../../../../images/clay-taglib-badge-error.png)\n\nSuccess badge:\n\n```markup\n<div class=\"col-md-1\">\n    <clay:badge label=\"1111\" style=\"success\" />\n\n    <div>Success</div>\n</div>\n```\n\n![Figure 5: A success badge displays numbers related to a successful action.](../../../../images/clay-taglib-badge-success.png)\n\nWarning badge:\n\n```markup\n<div class=\"col-md-1\">\n    <clay:badge label=\"21\" style=\"warning\" />\n\n    <div>Warning</div>\n</div>\n```\n\n![Figure 6: A warning badge displays numbers related to warnings that should be addressed.](../../../../images/clay-taglib-badge-warning.png)\n\nNow you know how to use badges to keep track of values in your app.\n\n## Related Topics\n\n- [Clay Labels and Links](/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links)\n- [Clay Cards](/docs/7-2/reference/-/knowledge_base/r/clay-cards)\n- [Clay Stickers](/docs/7-2/reference/-/knowledge_base/r/clay-stickers)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/04-clay-buttons.markdown",
    "content": "---\nheader-id: clay-buttons\n---\n\n# Clay Buttons\n\n[TOC levels=1-4]\n\nButtons come in several types and variations. This tutorial covers the \ndifferent styles and variations of buttons you can create with the Clay \ntaglibs. \n\n## Types\n\n**Primary button:** Used for the most important actions. Two primary buttons \nshould not be together or near each other. \n\nPrimary button with label:\n\n```markup\n<clay:button label=\"Primary\" />\n```\n\n![Figure 1: A primary button is bright blue, grabbing the user's attention.](../../../../images/clay-taglib-button-primary.png)\n\n**Secondary button:** Used for secondary actions. There can be multiple \nsecondary buttons together or near each other. \n\n```markup\n<div class=\"col\">\n    <clay:button label=\"Secondary\" style=\"secondary\" />\n</div>\n<div class=\"col\">\n    <clay:button ariaLabel=\"Wiki\" icon=\"wiki\" style=\"secondary\" />\n</div>\n```\n\n![Figure 2: A secondary button draws less attention than a primary button and is meant for secondary actions.](../../../../images/clay-taglib-button-secondary.png)\n\n**Borderless button:** Used in cases such as toolbars where the secondary button \nwould be too heavy for the design. This keeps the design clean.\n\n```markup\n<div class=\"col\">\n    <clay:button label=\"Borderless\" style=\"borderless\" />\n</div>\n<div class=\"col\">\n    <clay:button ariaLabel=\"Page Template\" icon=\"page-template\" style=\"borderless\" />\n</div>\n```\n\n![Figure 3: Borderless buttons remove the dark outline from the button.](../../../../images/clay-taglib-button-borderless.png)\n\n**Link button:** Used for Cancel actions.\n\n```markup\n<div class=\"col\">\n    <clay:button label=\"Link\" style=\"link\" />\n</div>\n<div class=\"col\">\n    <clay:button ariaLabel=\"Add Role\" icon=\"add-role\" style=\"link\" />\n</div>\n```\n\n![Figure 4: You can also turn buttons into links.](../../../../images/clay-taglib-button-link.png)\n\nYou can use labels or icons for your buttons. Below is an example of a Primary \nbutton with an icon:\n\n```markup\n<clay:button ariaLabel=\"Workflow\" icon=\"workflow\" />\n```\n\n![Figure 5: Buttons can also display icons.](../../../../images/clay-taglib-button-primary-icon.png)\n\nYou can disable a button by adding the `disabled` attribute:\n\n```markup\n<div class=\"col\">\n    <clay:button disabled=\"<%= true %>\" label=\"Primary\" />\n</div>\n<div class=\"col\">\n    <clay:button ariaLabel=\"Workflow\" disabled=\"<%= true %>\" icon=\"workflow\" />\n</div>\n```\n\n![Figure 6: Buttons can be disabled if you don't want the user to interact with them.](../../../../images/clay-taglib-button-primary-disabled.png)\n\n## Variations\n\nButton with icon and text:\n\n```markup\n<clay:button icon=\"share\" label=\"Share\" />\n```\n\n![Figure 7: Buttons can display both icons and text.](../../../../images/clay-taglib-button-icon-text.png)\n\nButton with monospaced text:\n\n```markup\n<clay:button icon=\"indent-less\" monospaced=\"<%= true %>\" style=\"secondary\" />\n```\n\n![Figure 8: Buttons can display monospaced text.](../../../../images/clay-taglib-button-monospaced.png)\n\nBlock level button: \n\n```markup\n<clay:button block=\"<%= true %>\" label=\"Button\" />\n ```\n \n![Figure 9: Block level buttons span the entire width of the container.](../../../../images/clay-taglib-button-block-level.png)\n\nPlus button:\n\n```markup\n<clay:button icon=\"plus\" monospaced=\"<%= true %>\" style=\"secondary\" />\n```\n\n![Figure 10: A plus button is used for add actions in an app.](../../../../images/clay-taglib-button-plus.png)\n\nAction button:\n\n```markup\n<clay:button icon=\"ellipsis-v\" monospaced=\"<%= true %>\" style=\"borderless\" />\n ```\n \n![Figure 11: An action button is used to display actions menus.](../../../../images/clay-taglib-button-action.png)\n\n## Related Topics\n\n- [Clay Alerts](/docs/7-2/reference/-/knowledge_base/r/clay-alerts)\n- [Clay Buttons](/docs/7-2/reference/-/knowledge_base/r/clay-buttons)\n- [Clay Labels and Links](/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/05-clay-cards.markdown",
    "content": "---\nheader-id: clay-cards\n---\n\n# Clay Cards\n\n[TOC levels=1-4]\n\nCards visually represent data. Use them for images, document libraries, user\nprofiles and more. There are four main types of Cards:\n\n- Image Cards\n- File Cards\n- User Cards\n- Horizontal Cards\n\nEach of these types is covered below. \n\n## Image Cards\n\nImage Cards are used for image/document galleries. \n\nImage Card:\n\n```markup\n<clay:image-card\n\tactionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n\thref=\"#1\"\n\timageAlt=\"thumbnail\"\n\timageSrc=\"https://images.unsplash.com/photo-1506976773555-b3da30a63b57\"\n\tsubtitle=\"Author Action\"\n\ttitle=\"Madrid\"\n/>\n```\n\n![Figure 1: Image Cards display images and documents.](../../../../images/clay-taglib-image-card.png)\n\nImage Card with icon:\n\n```markup\n<clay:image-card\n\tactionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n\ticon=\"camera\"\n\tsubtitle=\"Author Action\"\n\ttitle=\"<%= SVG_FILE_TITLE %>\"\n/>\n```\n\n![Figure 2: Image Cards can also display icons instead of images.](../../../../images/clay-taglib-image-card-icon.png)\n\nImage Card empty:\n\n```markup\n<clay:image-card \n  actionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n\tsubtitle=\"Author Action\"\n\ttitle=\"<%= SVG_FILE_TITLE %>\"\n/>\n```\n\n![Figure 3: Cards can also display nothing.](../../../../images/clay-taglib-image-card-empty.png)\n\nCards can also contain file types. Specify the file type with the `filetype` \nattribute:\n\n```markup\n<clay:image-card\n\tactionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n\tfileType=\"JPG\"\n\tfileTypeStyle=\"danger\"\n\thref=\"#1\"\n\timageAlt=\"thumbnail\"\n\timageSrc=\"https://images.unsplash.com/photo-1499310226026-b9d598980b90\"\n\tsubtitle=\"Author Action\"\n\ttitle=\"California\"\n/>\n```\n\n![Figure 4: Cards can also contain file types.](../../../../images/clay-taglib-image-card-file-type.png)\n\nInclude the `labels` attribute to add a label to a Card:\n\n```markup\n<clay:image-card\n\tactionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n\tfileType=\"JPG\"\n\tfileTypeStyle=\"danger\"\n\thref=\"#1\"\n\timageAlt=\"thumbnail\"\n\timageSrc=\"https://images.unsplash.com/photo-1503703294279-c83bdf7b4bf4\"\n\tlabels=\"<%= cardsDisplayContext.getLabels() %>\"\n\tsubtitle=\"Author Action\"\n\ttitle=\"Beetle\"\n/>\n```\n\n![Figure 5: You can include labels in Cards.](../../../../images/clay-taglib-image-card-icon-label.png)\n\nInclude the `selectable` attribute to make cards selectable (include a checkbox):\n\n```markup\n<clay:image-card\n\tactionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n\tfileType=\"JPG\"\n\tfileTypeStyle=\"danger\"\n\thref=\"#1\"\n\timageAlt=\"thumbnail\"\n\timageSrc=\"https://images.unsplash.com/photo-1506020647804-b04ee956dc04\"\n\tlabels=\"<%= cardsDisplayContext.getLabels() %>\"\n\tselectable=\"<%= true %>\"\n\tselected=\"<%= true %>\"\n\tsubtitle=\"Author Action\"\n\ttitle=\"Beetle\"\n/>\n```\n\n![Figure 6: Cards can be selectable.](../../../../images/clay-taglib-image-card-icon-selectable.png)\n\n## File Cards\n\nFile Cards display an icon of the file's type. They represent file types other\nthan image files (i.e. PDF, MP3, DOC, etc.).\n\n```markup\n<clay:file-card\n\tactionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n\tfileType=\"MP3\"\n\tfileTypeStyle=\"warning\"\n\tlabels=\"<%= cardsDisplayContext.getLabels() %>\"\n\tlabelStylesMap=\"<%= cardsDisplayContext.getLabelStylesMap() %>\"\n\tselectable=\"<%= true %>\"\n\tselected=\"<%= true %>\"\n\tsubtitle=\"Jimi Hendrix\"\n\ttitle=\"<%= MP3_FILE_TITLE %>\"\n/>\n```\n\n![Figure 7: File Cards display file type icons.](../../../../images/clay-taglib-file-card.png)\n\nYou can optionally use the `labelStylesMap` attribute to pass a `HashMap` of \nmultiple labels, as shown above.\n\nThe example below specifies a list `icon` instead of the default file icon: \n\n```markup\n<clay:file-card\n\tactionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n\tfileType=\"DOC\"\n\tfileTypeStyle=\"info\"\n\ticon=\"list\"\n\tlabels=\"<%= cardsDisplayContext.getLabels() %>\"\n\tselectable=\"<%= true %>\"\n\tselected=\"<%= true %>\"\n\tsubtitle=\"Paco de Lucia\"\n\ttitle=\"<%= DOC_FILE_TITLE %>\"\n/>\n```\n\n| **Note:** The full list of available Liferay icons can be found on the\n| [Clay CSS website](https://clayui.com/docs/components/icon.html).\n\n## User Cards\n\nUser Cards display user profile images or the initials of the user's name or \nname+surname.\n\nUser Card with initials:\n\n```markup\n<clay:user-card\n\tactionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n\tinitials=\"HS\"\n\tname=\"User Name\"\n\tsubtitle=\"Latest Action\"\n\tuserColor=\"danger\"\n/>\n```\n\n![Figure 8: User Cards can display a user's initials.](../../../../images/clay-taglib-user-card-initial.png)\n\nUser Card with profile image:\n\n```markup\n<clay:user-card\n\tactionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n\tdisabled=\"<%= true %>\"\n\timageAlt=\"thumbnail\"\n\timageSrc=\"https://images.unsplash.com/photo-1502290822284-9538ef1f1291\"\n\tname=\"User name\"\n\tselectable=\"<%= true %>\"\n\tselected=\"<%= true %>\"\n\tsubtitle=\"Latest Action\"\n/>\n```\n\n![Figure 9: A User Card can also display a profile image.](../../../../images/clay-taglib-user-card-profile-image.png)\n\n## Horizontal Cards\n\nHorizontal Cards represent folders and can have the same amount of information\nas other Cards. The key difference is that horizontal Cards let you remove the\nimage portion of the Card, since only the folder icon is required.\n\n```markup\n<clay:horizontal-card\n\tactionItems=\"<%= cardsDisplayContext.getDefaultActionItems() %>\"\n\tselectable=\"<%= true %>\"\n\tselected=\"<%= true %>\"\n\ttitle=\"ReallySuperInsanelyJustIncrediblyLongAndTotallyNotPossibleWordButWeAreReallyTryingToCoverAllOurBasesHereJustInCaseSomeoneIsNutsAsPerUsual\"\n/>\n```\n\n![Figure 10: Horizontal Cards are good for displaying folders.](../../../../images/clay-taglib-horizontal-card.png)\n\nNow you know how to use Cards in your UI to display information in your apps.\n\n## Related Topics\n\n- [Clay Badges](/docs/7-2/reference/-/knowledge_base/r/clay-badges)\n- [Clay Labels and Links](/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links)\n- [Clay Stickers](/docs/7-2/reference/-/knowledge_base/r/clay-stickers)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/06-clay-dropdown-and-action-menus.markdown",
    "content": "---\nheader-id: clay-dropdown-menus-and-action-menus\n---\n\n# Clay Dropdown Menus and Action Menus\n\n[TOC levels=1-4]\n\nYou can add dropdown menus to your app via the `clay:dropdown-menu` and \n`clay:actions-menu` taglibs. The Clay taglibs provide several menu variations \nfor you to choose. Both taglibs with several examples are shown below.\n\n## Dropdown Menus\n\nBasic dropdown menu:\n\n```markup\n<clay:dropdown-menu\n\titems=\"<%= dropdownsDisplayContext.getDefaultDropdownItems() %>\"\n\tlabel=\"Default\"\n/>\n```\n \n![Figure 1: Clay taglibs provide everything you need to add dropdown menus to your app.](../../../../images/clay-taglib-dropdown-basic.png)\n \nThe dropdown menu's items are defined in its Java class--`dropdownDisplayContext` \nin this case. Menu items are `NavigationItem` objects. You can disable menu \nitems with the `setDisabled(true)` method and make a menu item active with the \n`setActive(true)` method. The `href` attribute is set with the `setHref()` \nmethod, and labels are defined with the `setLabel()` method. Here's an example\nimplementation of the `dropdownDisplayContext` class:\n\n```java\nif (_defaultDropdownItems != null) {\n  return _defaultDropdownItems;\n}\n\n_defaultDropdownItems = new ArrayList<>();\n\nfor (int i = 0; i < 4; i++) {\n  NavigationItem navigationItem = new NavigationItem();\n\n  if (i == 1) {\n    navigationItem.setDisabled(true);\n  }\n  else if (i == 2) {\n    navigationItem.setActive(true);\n  }\n\n  navigationItem.setHref(\"#\" + i);\n  navigationItem.setLabel(\"Option \" + i);\n\n  _defaultDropdownItems.add(navigationItem);\n}\n\nreturn _defaultDropdownItems;\n}\n```\n\nYou can organize menu items into groups by setting the `NavigationItem`'s type\nto `TYPE_GROUP` and nesting the items in separate `ArrayList`s. You can add\na horizontal separator to separate the groups visually with the \n`setSeparator(true)` method. Below is a code snippet from the \n`dropdownsDisplayContext` class:\n\n```java\ngroup1NavigationItem.setSeparator(true);\ngroup1NavigationItem.setType(NavigationItem.TYPE_GROUP);\n```\n\nCorresponding taglib:\n\n```markup\n<clay:dropdown-menu\n\titems=\"<%= dropdownsDisplayContext.getGroupDropdownItems() %>\"\n\tlabel=\"Dividers\"\n/>\n```\n\n![Figure 2: You can organize dropdown menu items into groups.](../../../../images/clay-taglib-dropdown-group.png)\n\nYou can also add inputs to dropdown menus. To add an input to a dropdown menu, \nset the input's type with the `setType()` method \n(e.g. `NavigationItem.TYPE_CHECKBOX`), its name with the `setInputName()` \nmethod, and its value with the `setInputValue()` method. Here's an example\nimplementation: \n\n```java\nnavigationItem.setInputName(\"checkbox\" + i);\nnavigationItem.setInputValue(\"checkboxValue\" + i);\nnavigationItem.setLabel(\"Group 1 - Option \" + i);\nnavigationItem.setType(NavigationItem.TYPE_CHECKBOX);\n```\n\nCorresponding taglib:\n\n```markup\n<clay:dropdown-menu\n\tbuttonLabel=\"Done\"\n\titems=\"<%= dropdownsDisplayContext.getInputDropdownItems() %>\"\n\tlabel=\"Inputs\"\n\tsearchable=\"<%= true %>\"\n/>\n```\n\n![Figure 3: Inputs can be included in dropdown menus.](../../../../images/clay-taglib-dropdown-input.png)\n\nMenu items can also contain icons. To add an icon to a menu item, use the \n`setIcon()` method. Below is an example:\n\n```java\nnavigationItem.setIcon(\"check-circle-full\")\n```\n\nCorresponding taglib:\n\n```markup\n<clay:dropdown-menu\n\titems=\"<%= dropdownsDisplayContext.getIconDropdownItems() %>\"\n\titemsIconAlignment=\"left\"\n\tlabel=\"Icons\"\n/>\n```\n\n![Figure 4: Icons can be included in dropdown menus.](../../../../images/clay-taglib-dropdown-icons.png)\n\n## Actions Menus\n\nBasic actions menu:\n\n```markup\n<clay:dropdown-actions\n\titems=\"<%= dropdownsDisplayContext.getDefaultDropdownItems() %>\"\n/>\n```\n\n![Figure 5: You can also create Actions menus with Clay taglibs.](../../../../images/clay-taglib-dropdown-actions.png)\n\nAn actions menu can also display help text to the user: \n\n```markup\n<clay:dropdown-actions\n\tbuttonLabel=\"More\"\n\tbuttonStyle=\"secondary\"\n\tcaption=\"Showing 4 of 32 Options\"\n\thelpText=\"You can customize this menu or see all you have by pressing \\\"more\\\".\"\n\titems=\"<%= dropdownsDisplayContext.getDefaultDropdownItems() %>\"\n/>\n```\n\n![Figure 6: You can provide help text in Actions menus.](../../../../images/clay-taglib-dropdown-actions-help.png)\n\nClay taglibs make it easy to add dropdown menus and action menus to your apps.\n\n## Related Topics\n\n- [Clay Form Elements](/docs/7-2/reference/-/knowledge_base/r/clay-form-elements)\n- [Clay Navigation Bars](/docs/7-2/reference/-/knowledge_base/r/clay-navigation-bars)\n- [Clay Progress Bars](/docs/7-2/reference/-/knowledge_base/r/clay-progress-bars)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/07-clay-form-elements.markdown",
    "content": "---\nheader-id: clay-form-elements\n---\n\n# Clay Form Elements\n\n[TOC levels=1-4]\n\nThe Liferay Clay tag library provides several tags for creating form elements. \nAn example of each tag is shown below. \n\n## Checkbox\n\nCheckboxes give the user a true or false input.\n\n```markup\t\t\n<clay:checkbox \n\t\tchecked=\"<%= true %>\" \n\t\thideLabel=\"<%= true %>\" \n\t\tlabel=\"My Input\" \n\t\tname=\"name\" \n/>\n```\n\nAttributes:\n\n**checked:** Whether the checkbox is checked\n\n**disabled:** Whether the checkbox is enabled\n\n**hideLabel:** Whether to display the checkbox label\n\n**indeterminate:** Checkbox variable for multiple selection\n\n**label:** The checkbox's label\n\n**name:** The checkbox's name\n\n![Figure 1: Clay taglibs provide checkboxes.](../../../../images/clay-taglib-form-checkbox.png)\n\n## Radio\n\nA radio button lets the user select one choice from a set of options in a form.\n\n```markup\n<clay:radio \n\t\tchecked=\"<%= true %>\" \n\t\thideLabel=\"<%= true %>\" \n\t\tlabel=\"My Input\" \n\t\tname=\"name\" \n/>\n```\n\nAttributes:\n\n**checked:** Whether the radio button is checked\n\n**hideLabel:** Whether to display the radio button label\n\n**disabled:** Whether the radio button is enabled\n\n**label:** The radio button's label\n\n**name:** The radio button's name\n\n![Figure 2: Clay taglibs provide radio buttons.](../../../../images/clay-taglib-form-radio-button.png)\n\n## Selector\n\nA selector gives the user a select box with a set of options to choose from. \n\nThe Java scriplet below creates eight dummy options for the selector: \n\n```java\n<%\nList<Map<String, Object>> options = new ArrayList<>();\n\nfor (int i = 0; i < 8; i++) {\n\tMap<String, Object> option = new HashMap<>();\n\n\toption.put(\"label\", \"Sample \" + i);\n\toption.put(\"value\", i);\n\n\toptions.add(option);\n}\n%>\n```\n\n```markup\n<clay:select \n\t\tlabel=\"Regular Select Element\" \n\t\tname=\"name\" \n\t\toptions=\"<%= options %>\" \n/>\n```\n\n![Figure 3: Clay taglibs provide select boxes.](../../../../images/clay-taglib-form-selector.png)\n\nIf you want let users select multiple options at once, set the `multiple` \nattribute to `true`:\n\n```markup\n<clay:select \n\t\tlabel=\"Multiple Select Element\" \n\t\tmultiple=\"<%= true %>\" \n\t\tname=\"name\" \n\t\toptions=\"<%= options %>\" \n/>\n```\n\n![Figure 4: You can let users select multiple options from the select menu.](../../../../images/clay-taglib-form-selector-multiple.png)\n\nAttributes:\n\n**disabled:** Whether the selector is enabled\n**label:** The selector's label\n**multiple:** Whether multiple options can be selected\n**name:** The selector's name\n\nNow you know how to use Clay taglibs to add common form elements to your app!\n\n## Related Topics\n\n- [Clay Buttons](/docs/7-2/reference/-/knowledge_base/r/clay-buttons)\n- [Clay Icons](/docs/7-2/reference/-/knowledge_base/r/clay-icons)\n- [Clay Labels and Links](/docs/7-2/reference/-/knowledge_base/r/clay-labels-and-links)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/08-clay-icons.markdown",
    "content": "---\nheader-id: clay-icons\n---\n\n# Clay Icons\n\n[TOC levels=1-4]\n\nThe Liferay Clay taglibs provide several icons that you can use in your apps. \nUse the `clay:icon` tag and specify the icon with the `symbol` attribute:\n\n```markup\n<clay:icon symbol=\"folder\" />\n```\n\n![Figure 1: You can include icons in your app with the Clay taglib.](../../../../images/clay-taglib-icon-folder.png)\n\nThe full list of icons is shown below:\n\n![Figure 2: The Clay taglib gives you access to several @product@ icons.](../../../../images/clay-taglib-icon-library.png)\n\nThe Liferay Clay taglibs also provide a set of language flag icons that you can \nuse in your app. The full list of language flags is shown below:\n\n![Figure 3: You can include language flags in your apps.](../../../../images/clay-taglib-icon-language-flags.png)\n\n## Related Topics\n\n- [Clay Badges](/docs/7-2/reference/-/knowledge_base/r/clay-badges)\n- [Clay Stickers](/docs/7-2/reference/-/knowledge_base/r/clay-stickers)\n- [Using Clay Icons in a Theme](/docs/7-2/frameworks/-/knowledge_base/f/using-clay-icons-in-a-theme)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/09-clay-links-and-labels.markdown",
    "content": "---\nheader-id: clay-labels-and-links\n---\n\n# Clay Labels and Links\n\n[TOC levels=1-4]\n\nLiferay Clay taglibs provide tags for creating labels and links in your app. \nBoth of these elements are covered below. \n\n## Labels\n\nThe Liferay Clay taglibs provide a few different labels for your app. Use the \n`clay:label` tag to add a label to your app. You can create color-coded labels, \nremovable labels, and labels that contain links. The sections below demonstrate \nall of these options. \n\n### Color-coded Labels\n\nThe Liferay Clay labels come in four different colors: dark-blue for info, \nlight-gray for status, orange for pending, red for rejected, and green for \napproved.\n\nInfo labels are dark-blue, and since they stand out a bit more than status \nlabels, they are best for conveying general information. To use an info label, \nset the `style` attribute to `info`:\n\n```markup\n<clay:label label=\"Label text\" style=\"info\" />\n```\n\n![Figure 1: Info labels convey general information.](../../../../images/clay-taglib-label-info.png)\n\nStatus labels are light-gray, and due to their neutral color, they are best for \nconveying basic information. Status labels are the default label and therefore \nrequire no `style` attribute: \n\n```markup\n<clay:label label=\"Status\" />\n```\n\n![Figure 2: Status labels are the least flashy and best for displaying basic information.](../../../../images/clay-taglib-label-status.png)\n\nWarning labels are orange, and due to their color, they are best for conveying\na warning message. To use a warning label, set the `style` attribute to\n`warning`:\n\n```markup\n<clay:label label=\"Pending\" style=\"warning\" />\n```\n\n![Figure 3: Warning labels notify the user of issues, but nothing app breaking.](../../../../images/clay-taglib-label-warning.png)\n\nDanger labels are red and indicate that something is wrong or has failed. To use \na danger label, set the `style` attribute to `danger`:\n\n```markup\n<clay:label label=\"Rejected\" style=\"danger\" />\n```\n\n![Figure 4: Danger labels convey a sense of urgency that must be addressed.](../../../../images/clay-taglib-label-danger.png)\n\nSuccess labels are green and indicate that something has completed successfully. \nTo use a success label, set the `style` attribute to `success`:\n\n```markup\n<clay:label label=\"Approved\" style=\"success\" />\n```\n\n![Figure 5: Success labels indicate a successful action.](../../../../images/clay-taglib-label-success.png)\n\nLabels can also be bigger. Set the `size` attribute to `lg` to display large\nlabels:\n\n```markup    \n<clay:label label=\"Approved\" size=\"lg\" style=\"success\" />\n```\n\n### Removable Labels\n\nIf you want to let a user close a label (e.g. a temporary notification), you can \nmake the label removable by setting the `closeable` attribute to `true`.\n\n```markup\n<clay:label closeable=\"<%= true %>\" label=\"Normal Label\" />\n```\n\n![Figure 6: Labels can be removable.](../../../../images/clay-taglib-label-removable.png)\n\n### Labels with Links\n\nYou can make a label a link by adding the `href` attribute to it just as you \nwould an anchor tag:\n\n```markup\n<clay:label href=\"#\" label=\"Label Text\" />\n```\n\n![Figure 7: Labels can also be links.](../../../../images/clay-taglib-label-link.png)\n\n## Links\n\nYou can add traditional hyperlinks to your app with the `<clay:link>` tag:\n\n```markup\n<clay:link href=\"#\" label=\"link text\" />\n```\n\n![Figure 8: Clay taglibs also provide link elements.](../../../../images/clay-taglib-link.png)\n\nNow you know how to add links and labels to your apps!\n\n## Related Topics\n\n- [Clay Badges](/docs/7-2/reference/-/knowledge_base/r/clay-badges)\n- [Clay Cards](/docs/7-2/reference/-/knowledge_base/r/clay-cards)\n- [Clay Form Elements](/docs/7-2/reference/-/knowledge_base/r/clay-form-elements)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/10-clay-management-toolbar.markdown",
    "content": "---\nheader-id: clay-management-toolbar\n---\n\n# Clay Management Toolbar\n\n[TOC levels=1-4]\n\nThe Management Toolbar gives administrators control over search container \nresults in their apps. It lets you filter, sort, and choose a view type for \nsearch results, so you can quickly identify the document, web content, asset \nentry, or whatever you're looking for. The Management Toolbar is fully \ncustomizable, so you can implement all the controls or just the ones your app \nrequires. \n\n![Figure 1: The Management ToolBar lets the user customize how the app displays content.](../../../../images/clay-taglib-management-toolbar.png)\n\nTo create a management toolbar, use the `clay:management-toolbar` taglib. The \ntoolbar contains a few key sections. Each section is grouped and configured\nusing different attributes. These attributes are described in more detail below. \n\n## Using a Display Context to Configure the Management Toolbar\n\nIf you're using a Display Context---a separate class to configure your display \noptions for your management toolbar---to define all or some of the configuration \noptions for the toolbar, you can specify the Display Context with the \n`displayContext` attribute. An example is shown below:\n\n```markup\n<clay:management-toolbar \n    displayContext=\"<%= viewUADEntitiesManagementToolbarDisplayContext %>\" \n/>\n```\n\nYou can see an example use case of a Display Context in \n[Filtering and Sorting Items with the Management Toolbar](/docs/7-2/frameworks/-/knowledge_base/f/filtering-and-sorting-items-with-the-management-toolbar). \nA Display Context is not required for a management toolbar's configuration. You \ncan provide as much or as little of the configuration options for your \nmanagement toolbar through the Display Context as you like.  \n\n## Checkbox and Actions\n\nThe `actionItems`, `searchContainerId`, `selectable`, and `totalItems` \nattributes let you include a checkbox in the toolbar to select all search \ncontainer results and run bulk actions on them. Actions and total items display \nwhen an individual result is checked, or when the master checkbox is checked in \nthe toolbar. \n\n`actionItems`: The list of dropdown items to display when a result is checked or \nthe master checkbox in the Management Toolbar is checked. You can select \nmultiple results between pages. The Management Toolbar keeps track of the number \nof selected results for you.\n\n`searchContainerId`: The ID of the search container connected to the Management \nToolbar \n\n`selectable`: Whether to include a checkbox in the Management Toolbar\n\n`totalItems`: The total number of items across pagination. This number displays \nwhen one or multiple items are selected. \n\nAn example configuration is shown below:\n\n```java\nactionItems=\"<%=\n    new JSPDropdownItemList(pageContext) {\n        {\n          add(\n            dropdownItem -> {\n              dropdownItem.setHref(\"#edit\");\n              dropdownItem.setLabel(\"Edit\");\n            });\n  \n          add(\n            dropdownItem -> {\n              dropdownItem.setHref(\"#download\");\n              dropdownItem.setIcon(\"download\");\n              dropdownItem.setLabel(\"Download\");\n              dropdownItem.setQuickAction(true);\n            });\n  \n          add(\n            dropdownItem -> {\n              dropdownItem.setHref(\"#delete\");\n              dropdownItem.setLabel(\"Delete\");\n              dropdownItem.setIcon(\"trash\");\n              dropdownItem.setQuickAction(true);\n            });\n        }\n    }\n%>\"\n```\n\nAction items are listed in the Actions menu, along with the number of items \nselected across pagination. \n\n![Figure 2: Actions are also listed in the Management Toolbar's dropdown menu when an item, multiple items, or the master checkbox is checked.](../../../../images/clay-taglib-management-toolbar-actions.png)\n\nIf an action has an icon specified, such as the Delete and Download actions in \nthe example above, the icon is displayed next to the action menu as well. \n\n![Figure 3: The Management Toolbar keeps track of the results selected and displays the actions to execute on them.](../../../../images/clay-taglib-management-toolbar-selectable.png)\n\n## Filtering and Sorting Search Results\n\nThe `filterItems`, `sortingOrder`, and `sortingURL` attributes let you filter \nand sort search container results. Filtering and sorting are grouped together in \none convenient dropdown menu. \n\n`filterItems`: Sets the search container's filtering options. This filter should \nbe included in all control panel applications. Filtering options can include \nsort criteria, sort ordering, and more.\n\n`filterLabelItems`: Sets the search container's filter labels to display. This \nlets the user know which filters are currently applied.\n\n`sortingOrder`: The current sorting order: ascending or descending.\n\n`sortingURL`: The URL to change the sorting order\n\nThe example below adds two filter options and two sorting options:\n\n```java\nfilterItems=\"<%=\n    new DropdownItemList(_request) {\n        {\n            addGroup(\n                dropdownGroupItem -> {\n                    dropdownGroupItem.setDropdownItemList(\n                        new DropdownItemList(_request) {\n                            {\n                                add(\n                                    dropdownItem -> {\n                                        dropdownItem.setHref(\"#1\");\n                                        dropdownItem.setLabel(\"Filter 1\");\n                                    });\n\n                                add(\n                                    dropdownItem -> {\n                                        dropdownItem.setHref(\"#2\");\n                                        dropdownItem.setLabel(\"Filter 2\");\n                                    });\n                            }\n                        }\n                    );\n                    dropdownGroupItem.setLabel(\"Filter By\");\n                });\n                \n            addGroup(\n                dropdownGroupItem -> {\n                    dropdownGroupItem.setDropdownItemList(\n                        new DropdownItemList(_request) {\n                            {\n                                add(\n                                    dropdownItem -> {\n                                        dropdownItem.setHref(\"#3\");\n                                        dropdownItem.setLabel(\"Order 1\");\n                                    });\n\n                                add(\n                                    dropdownItem -> {\n                                        dropdownItem.setHref(\"#4\");\n                                        dropdownItem.setLabel(\"Order 2\");\n                                    });\n                            }\n                        }\n                    );\n                    dropdownGroupItem.setLabel(\"Order By\");\n                });\n        }\n    }\n%>\"\n```\n\n```java\nfilterLabelItems=\"<%=\n  new LabelItemList() {\n    {\n      add(\n        labelItem -> {\n          labelItem.setLabel(\"Filter 1\");\n        });\n\n      add(\n        labelItem -> {\n          labelItem.setLabel(\"Filter 2\");\n        });\n    }\n  };\n%>\"\n```\n\n![Figure 4: You can also sort and filter search container results.](../../../../images/clay-taglib-management-toolbar-filter-and-sort.png)\n\n![Figure 5: You can also sort and filter search container results.](../../../../images/clay-taglib-management-toolbar-filter-label-items.jpg)\n\n## Search Form\n\nThe `clearResultsURL`, `searchActionURL`, `searchFormName`, `searchInputName`, \nand `searchValue` attributes let you configure the search form. The main portion \nof the Management Toolbar is reserved for the search form.\n\n`clearResultsURL`: The URL to reset the search\n\n`searchActionURL`: The action URL to send the search form\n\n`searchFormName`: The search form's name\n\n`searchInputName`: The search input's name\n\n`searchValue`: The search input's value\n\nAn example configuration is shown below:\n\n```markup\n<clay:management-toolbar\n    clearResultsURL=\"<%= searchURL %>\"\n    disabled=\"<%= isDisabled %>\"\n    namespace=\"<%= renderResponse.getNamespace() %>\"\n    searchActionURL=\"<%= searchURL %>\"\n    searchFormName=\"fm\"\n    searchInputName=\"<%= DisplayTerms.KEYWORDS %>\"\n    searchValue=\"<%= ParamUtil.getString(request, searchInputName) %>\"\n    selectable=\"<%= false %>\"\n    totalItems=\"<%= totalItems %>\"\n/>\n```\n\n![Figure 6: The search form comprises most of the Management Toolbar, letting users search through the search container results.](../../../../images/clay-taglib-management-toolbar-search-form.png)\n\n## Info Panel\n\nThe `infoPanelId` and `showInfoButton` attributes let you add a retractable \nsidebar panel that displays additional information related to a search container \nresult.\n\n`infoPanelId`: The ID of the info panel to toggle\n\n`showInfoButton`: Whether to show the info button\n\nIn the example configuration below, the `showInfoButton` attribute is provided \nin the Display Context---specified with the `displayContext` attribute---and the \n`infoPanelId` is explicitly set in the JSP:\n\n```markup\n<clay:management-toolbar\n    displayContext=\"<%= journalDisplayContext %>\"\n    infoPanelId=\"infoPanelId\"\n    namespace=\"<%= renderResponse.getNamespace() %>\"\n    searchContainerId=\"<=% searchContainerId %>\"\n/>\n```\n\n![Figure 7: The info panel keeps your UI clutter-free.](../../../../images/clay-taglib-management-toolbar-info-panel.png)\n\n## View Types\n\nThe `viewTypes` attribute specifies the display options for the search container \nresults. There are three display options to choose from:\n\n**Cards:** Displays search result columns on a horizontal or vertical card. \n\n![Figure 8: The Management Toolbar's icon display view gives a quick summary of the content's description and status.](../../../../images/clay-taglib-management-toolbar-view-type-card.png)\n\n**List:** Displays a detailed description along with summarized details for \nthe search result columns. \n\n![Figure 9: The Management Toolbar's List view type gives the content's full description.](../../../../images/clay-taglib-management-toolbar-view-type-list.png)\n\n**Table:** The default view. Lists the search result columns from left to \nright. \n\n![Figure 10: The Management Toolbar's Table view type list the content's information in individual columns.](../../../../images/clay-taglib-management-toolbar-view-type-table.png)\n\nAn example configuration is shown below:\n\n```java\nviewTypes=\"<%=\n    new JSPViewTypeItemList(pageContext, baseURL, selectedType) {\n        {\n        \taddCardViewTypeItem(\n        \t\tviewTypeItem -> {\n        \t\t\tviewTypeItem.setActive(true);\n        \t\t\tviewTypeItem.setLabel(\"Card\");\n        \t\t});\n\n        \taddListViewTypeItem(\n        \t\tviewTypeItem -> {\n        \t\t\tviewTypeItem.setLabel(\"List\");\n        \t\t});\n\n        \taddTableViewTypeItem(\n        \t\tviewTypeItem -> {\n        \t\t\tviewTypeItem.setLabel(\"Table\");\n        \t\t});\n        }\n    }\n%>\"\n```\n\nWhile the example above shows how to configure the view types in the JSP, you \nmust also \n[specify when to use each view type](/docs/7-2/frameworks/-/knowledge_base/f/implementing-the-view-types).\n\n![Figure 11: The Management Toolbar offers three view type options.](../../../../images/clay-taglib-management-toolbar-view-types.png)\n\n## Creation Menu\n\nThe `creationMenu` attribute creates an add menu button for one or multiple \nitems. It's used for creating new entities (e.g. a new blog entry).\n\nUse the `addPrimaryDropdownItem()` method to add the top level items to the \ndropdown menu, or use the `addFavoriteDropdownItem()` method to add secondary \nitems to the dropdown menu. \n\nThe example configuration below adds two primary creation menu items and two \nsecondary creation menu items:\n\n```java\ncreationMenu=\"<%= \n    new JSPCreationMenu(pageContext) {\n  \t\t\t{\n  \t\t\t\taddPrimaryDropdownItem(\n  \t\t\t\t\tdropdownItem -> {\n  \t\t\t\t\t\tdropdownItem.setHref(\"#1\");\n  \t\t\t\t\t\tdropdownItem.setLabel(\"Sample 1\");\n  \t\t\t\t\t});\n  \n  \t\t\t\taddPrimaryDropdownItem(\n  \t\t\t\t\tdropdownItem -> {\n  \t\t\t\t\t\tdropdownItem.setHref(\"#2\");\n  \t\t\t\t\t\tdropdownItem.setLabel(\"Sample 2\");\n  \t\t\t\t\t});\n  \n  \t\t\t\taddFavoriteDropdownItem(\n  \t\t\t\t\tdropdownItem -> {\n  \t\t\t\t\t\tdropdownItem.setHref(\"#3\");\n  \t\t\t\t\t\tdropdownItem.setLabel(\"Favorite 1\");\n  \t\t\t\t\t});\n  \n  \t\t\t\taddFavoriteDropdownItem(\n  \t\t\t\t\tdropdownItem -> {\n  \t\t\t\t\t\tdropdownItem.setHref(\"#4\");\n  \t\t\t\t\t\tdropdownItem.setLabel(\"Other item\");\n  \t\t\t\t\t});\n  \t\t\t}\n  \t};\n%>\"\n```\n\n![Figure 12: The Management Toolbar lets you optionally add a Creation Menu for creating new entities.](../../../../images/clay-taglib-management-toolbar-creation-menu.png)\n\n## Related Topics\n\n- [Clay Dropdown Menus and Action Menus](/docs/7-2/reference/-/knowledge_base/r/clay-dropdown-menus-and-action-menus)\n- [Clay Icons](/docs/7-2/reference/-/knowledge_base/r/clay-icons)\n- [Clay Navigation Bars](/docs/7-2/reference/-/knowledge_base/r/clay-navigation-bars)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/11-clay-navigation-bars.markdown",
    "content": "---\nheader-id: clay-navigation-bars\n---\n\n# Clay Navigation Bars\n\n[TOC levels=1-4]\n\nSimilar to dropdown menus, navigation bars display a list of navigation items. \nThe key difference is navigation bars are displayed in a horizontal bar with all \nnavigation items visible at all times. The navigation bar also indicates the \nactive navigation item with an underline. Navigation bars come in two styles: \nwhite background with dark-grey text (default) and dark-grey background with \nwhite text (inverted).\n\nDefault navigation bar:\n\n```markup\n<clay:navigation-bar \n    navigationItems=\"<%= navigationBarsDisplayContext.getNavigationItems() %>\" \n/>\n```\n\n![Figure 1: You can include navigation bars in your apps.](../../../../images/clay-taglib-nav-bars.png)\n\nInverted navigation bar (set `inverted` attribute to `true`):\n\n```markup\n<clay:navigation-bar \n    inverted=\"<%= true %>\" \n    navigationItems=\"<%= navigationBarsDisplayContext.getNavigationItems() %>\" \n/>\n```\n\n![Figure 2: Navigation bars can be inverted if you prefer.](../../../../images/clay-taglib-nav-bars-inverted.png)\n\n## Related Topics\n\n- [Clay Dropdown Menus and Action Menus](/docs/7-2/reference/-/knowledge_base/r/clay-dropdown-menus-and-action-menus)\n- [Clay Form Elements](/docs/7-2/reference/-/knowledge_base/r/clay-form-elements)\n- [Clay Progress Bars](/docs/7-2/reference/-/knowledge_base/r/clay-progress-bars)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/12-clay-progress-bars.markdown",
    "content": "---\nheader-id: clay-progress-bars\n---\n\n# Clay Progress Bars\n\n[TOC levels=1-4]\n\nYou can add progress bars to your app with the `clay:progressbar` tag. These \nindicate the completion percentage of a task and come in three status styles: \n`default` (blue), `warning` (red), and `complete` (green with checkmark). You \ncan provide a minimum value (`minValue`) and a maximum value (`maxValue`). \n\nDefault progress bar:\n\n```markup\n<clay:progressbar \n    maxValue=\"<%= 100 %>\" \n    minValue=\"<%= 0 %>\" \n    value=\"<%= 30 %>\" \n/>\n```\n\n![Figure 1: You can include progress bars in your apps.](../../../../images/clay-taglib-progress-bar.png)\n\nWarning progress bar:\n\n```markup\n<clay:progressbar \n    maxValue=\"<%= 100 %>\" \n    minValue=\"<%= 0 %>\" \n    status=\"warning\" \n    value=\"<%= 70 %>\" \n/>\n```\n\n![Figure 2: warning progress bars indicate that the progress has not completed due to an error.](../../../../images/clay-taglib-progress-bar-warning.png)\n\nComplete progress bar:\n\n```markup\n<clay:progressbar \n    status=\"complete\" \n/>\n```\n\n![Figure 3: The complete progress bar indicates the progress is complete.](../../../../images/clay-taglib-progress-bar-complete.png)\n\nClay taglibs make it easy to track progress in your apps.\n\n## Related Topics\n\n- [Clay Dropdown Menus and Action Menus](/docs/7-2/reference/-/knowledge_base/r/clay-dropdown-menus-and-action-menus)\n- [Clay Icons](/docs/7-2/reference/-/knowledge_base/r/clay-icons)\n- [Clay Navigation Bars](/docs/7-2/reference/-/knowledge_base/r/clay-navigation-bars)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/07-clay-taglibs/13-clay-stickers.markdown",
    "content": "---\nheader-id: clay-stickers\n---\n\n# Clay Stickers\n\n[TOC levels=1-4]\n\nWhereas badges display numbers and labels display short information, stickers\nare small visual indicators of the content (usually the content type). They\ncan include a small label or a Liferay icon, and they come in two shapes:\ncircle and square. \n\nSquare sticker with label:\n\n```markup\n<clay:sticker label=\"JPG\" />\n```\n\n![Figure 1: You can include stickers in your apps.](../../../../images/clay-taglib-sticker-square-label.png)\n\nSquare sticker with icon:\n\n```markup\n<clay:sticker icon=\"picture\" />\n```\n\n![Figure 2: Stickers can include icons.](../../../../images/clay-taglib-sticker-square-icon.png)\n\nCircle sticker:\n\n```markup\n<clay:sticker label=\"JPG\" shape=\"circle\" />\n```\n\n![Figure 3: You can also have circle stickers.](../../../../images/clay-taglib-sticker-round.png)\n\nStickers can be positioned in any corner of a div. Indicate their position with \nthe `position` attribute: `top-left`, `bottom-left`, `top-right`, or \n`bottom-right`:\n\n```markup\n<div class=\"aspect-ratio\">\n\n\t<img class=\"aspect-ratio-item-fluid\" src=\"/images/thumbnail_hot_air_ballon.jpg\" />\n\n\t<clay:sticker label=\"PDF\" position=\"top-left\" style=\"danger\" />\n</div>\n```\n\n![Figure 4: You can specify the position of the sticker within a container.](../../../../images/clay-taglib-sticker-position.png)\n\nNow you know how to use Clay stickers in your app!\n\n## Related Topics\n\n- [Clay Badges](/docs/7-2/reference/-/knowledge_base/r/clay-badges)\n- [Clay Cards](/docs/7-2/reference/-/knowledge_base/r/clay-cards)\n- [Clay Icons](/docs/7-2/reference/-/knowledge_base/r/clay-icons)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/01-using-chart-taglibs-intro.markdown",
    "content": "---\nheader-id: using-the-chart-taglib-in-your-portlets\n---\n\n# Using the Chart Taglib in Your Portlets\n\n[TOC levels=1-4]\n\nLines, splines, bars, pies and more, the Chart tag Library provides everything\nyou need to model data. Each taglib gives you access to the corresponding\n[Clay component](https://github.com/liferay/clay/tree/2.x-stable/packages/clay-charts/src).\nThese components contain the default configuration for the UI.\n\nTo use the Chart taglib in your apps, add the following declaration to your JSP:\n\n```markup\n<%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n```\n\nThis section covers the types of charts you can create with the Chart taglibs.\nEach article contains a set of chart examples along with sample Java data and a\nfigure displaying the rendered results.\n\n![Figure 1: You can create many different types of charts with the chart taglibs.](../../../../images/chart-taglib-sample-portlet.png)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/02-bar-charts.markdown",
    "content": "---\nheader-id: bar-charts\n---\n\n# Bar Charts\n\n[TOC levels=1-4]\n\nBar charts contain multiple sets of data. A bar chart models the data in bars.\nEach data series (created with the `addColumns()` method) is defined with a new\ninstance of the\n[`MultiValueColumn` object](https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/MultiValueColumn.html),\nwhich takes an ID and a set of values. Follow these steps to configure your\nportlet to use bar charts.\n\n1.  Import the chart taglib along with the `BarChartConfig` and\n    `MultiValueColumn` classes into your bundle's `init.jsp` file:\n\n    ```markup\n    <%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.point.bar.BarChartConfig\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.MultiValueColumn\" %>\n    ```\n\n2.  Add the following Java scriptlet to the top of your `view.jsp`:\n\n    ```java\n    <%\n    BarChartConfig _barChartConfig = new BarChartConfig();\n\n    _barChartConfig.addColumns(\n    \tnew MultiValueColumn(\"data1\", 100, 20, 30),\n    \tnew MultiValueColumn(\"data2\", 20, 70, 100)\n    );\n    %>\n    ```\n\n3.  Add the `<chart>` taglib to the `view.jsp`, passing the `_barChartConfig` as\n    the `config` attribute's value:\n\n    ```markup\n    <chart:bar\n      config=\"<%= _barChartConfig %>\"\n    />\n    ```\n\n![Figure 1: A bar chart models the data in bars.](../../../../images/chart-taglib-bar.png)\n\nAwesome! Now you know how to create bar charts for your apps.\n\n## Related Topics\n\n- [Line Charts](/docs/7-2/reference/-/knowledge_base/r/line-charts)\n- [Donut Charts](/docs/7-2/reference/-/knowledge_base/r/donut-charts)\n- [Combination Charts](/docs/7-2/reference/-/knowledge_base/r/combination-charts)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/03-line-charts.markdown",
    "content": "---\nheader-id: line-charts\n---\n\n# Line Charts\n\n[TOC levels=1-4]\n\nLine charts contain multiple sets of data. A Line chart displays the data\nlinearly. Each data series (created with the `addColumns()` method) is defined\nwith a new instance of the\n[`MultiValueColumn` object](https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/MultiValueColumn.html),\nwhich takes an ID and a set of values. Follow these steps to configure your\nportlet to use line charts.\n\n1.  Import the chart taglib along with the `LineChartConfig` and\n    `MultiValueColumn` classes into your bundle's `init.jsp` file:\n\n    ```markup\n    <%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.point.line.LineChartConfig\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.MultiValueColumn\" %>\n    ```\n\n2.  Add the following Java scriptlet to the top of your `view.jsp`:\n\n    ```java\n    <%\n    LineChartConfig _lineChartConfig = new LineChartConfig();\n\n    _lineChartConfig.addColumns(\n      new MultiValueColumn(\"data1\", 100, 20, 30),\n      new MultiValueColumn(\"data2\", 20, 70, 100)\n    );\n    %>\n    ```\n\n3.  Add the `<chart>` taglib to the `view.jsp`, passing the `_lineChartConfig`\n    as the `config` attribute's value:\n\n    ```markup\n    <chart:line\n      config=\"<%= _lineChartConfig %>\"\n    />\n    ```\n\n![Figure 1: A Line chart displays the data linearly.](../../../../images/chart-taglib-line.png)\n\nAwesome! Now you know how to create line charts for your apps.\n\n## Related Topics\n\n- [Spline Charts](/docs/7-2/reference/-/knowledge_base/r/spline-charts)\n- [Step Charts](/docs/7-2/reference/-/knowledge_base/r/step-charts)\n- [Predictive Charts](/docs/7-2/reference/-/knowledge_base/r/predictive-charts)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/04-scatter-charts.markdown",
    "content": "---\nheader-id: scatter-charts\n---\n\n# Scatter Charts\n\n[TOC levels=1-4]\n\nScatter charts contain multiple sets of data. A scatter chart models the data as\nindividual points. Each data series (created with the `addColumns()` method) is\ndefined with a new instance of the\n[`MultiValueColumn` object](https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/MultiValueColumn.html),\nwhich takes an ID and a set of values. Follow these steps to configure your\nportlet to use scatter charts.\n\n1.  Import the chart taglib along with the `ScatterChartConfig` and\n    `MultiValueColumn` classes into your bundle's `init.jsp` file:\n\n    ```markup\n    <%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.point.scatter.ScatterChartConfig\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.MultiValueColumn\" %>\n    ```\n\n2.  Add the following Java scriptlet to the top of your `view.jsp`:\n\n    ```java\n    <%\n    ScatterChartConfig _scatterChartConfig = new ScatterChartConfig();\n\n    _scatterChartConfig.addColumns(\n      new MultiValueColumn(\"data1\", 100, 20, 30),\n      new MultiValueColumn(\"data2\", 20, 70, 100));\n    %>\n    ```\n\n3.  Add the `<chart>` taglib to the `view.jsp`, passing the `_scatterChartConfig`\n    as the `config` attribute's value:\n\n    ```markup\n    <chart:scatter\n      config=\"<%= _scatterChartConfig %>\"\n    />\n    ```\n\n![Figure 1: A scatter chart models the data as individual points.](../../../../images/chart-taglib-scatter.png)\n\nAwesome! Now you know how to create scatter charts for your apps.\n\n## Related Topics\n\n- [Line Charts](/docs/7-2/reference/-/knowledge_base/r/line-charts)\n- [Step Charts](/docs/7-2/reference/-/knowledge_base/r/step-charts)\n- [Predictive Charts](/docs/7-2/reference/-/knowledge_base/r/predictive-charts)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/05-spline-charts.markdown",
    "content": "---\nheader-id: spline-charts\n---\n\n# Spline Charts\n\n[TOC levels=1-4]\n\nSpline charts contain multiple sets of data. A spline chart connects points of\ndata with a smooth curve. Each data series\n(created with the `addColumns()` method) is defined with a new instance of the\n[`MultiValueColumn` object](https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/MultiValueColumn.html),\nwhich takes an ID and a set of values. Follow these steps to configure your\nportlet to use spline charts.\n\n1.  Import the chart taglib along with the `SplineChartConfig` and\n    `MultiValueColumn` classes into your bundle's `init.jsp` file:\n\n    ```markup\n    <%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.point.spline.SplineChartConfig\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.MultiValueColumn\" %>\n    ```\n\n2.  Add the following Java scriptlet to the top of your `view.jsp`:\n\n    ```java\n    <%\n    SplineChartConfig _splineChartConfig = new SplineChartConfig();\n\n    _splineChartConfig.addColumns(\n      new MultiValueColumn(\"data1\", 100, 20, 30),\n      new MultiValueColumn(\"data2\", 20, 70, 100)\n    );\n    %>\n    ```\n\n3.  Add the `<chart>` taglib to the `view.jsp`, passing the `_splineChartConfig`\n    as the `config` attribute's value:\n\n    ```markup\n    <chart:spline\n      config=\"<%= _splineChartConfig %>\"\n    />\n    ```\n\n![Figure 1: A spline chart connects points of data with a smooth curve.](../../../../images/chart-taglib-spline.png)\n\nYou can also use an area spline chart if you prefer. An area spline chart\nhighlights the area under the spline curve.\n\n```markup\n<chart:area-spline\n  config=\"<%= _splineChartConfig %>\"\n/>\n```\n\n![Figure 2: An area spline chart highlights the area under the spline curve.](../../../../images/chart-taglib-area-spline.png)\n\nAwesome! Now you know how to create spline charts for your apps.\n\n## Related Topics\n\n- [Line Charts](/docs/7-2/reference/-/knowledge_base/r/line-charts)\n- [Step Charts](/docs/7-2/reference/-/knowledge_base/r/step-charts)\n- [Scatter Charts](/docs/7-2/reference/-/knowledge_base/r/scatter-charts)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/06-step-charts.markdown",
    "content": "---\nheader-id: step-charts\n---\n\n# Step Charts\n\n[TOC levels=1-4]\n\nStep charts contain multiple sets of data. A step chart steps between the points\nof data, resembling steps. Each data series\n(created with the `addColumns()` method) is defined with a new instance of the\n[`MultiValueColumn` object](https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/MultiValueColumn.html),\nwhich takes an ID and a set of values. Follow these steps to configure your\nportlet to use step charts.\n\n1.  Import the chart taglib along with the `StepChartConfig` and\n    `MultiValueColumn` classes into your bundle's `init.jsp` file:\n\n    ```markup\n    <%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.point.step.StepChartConfig\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.MultiValueColumn\" %>\n    ```\n\n2.  Add the following Java scriptlet to the top of your `view.jsp`:\n\n    ```java\n    <%\n    StepChartConfig _stepChartConfig = new StepChartConfig();\n\n    _stepChartConfig.addColumns(\n      new MultiValueColumn(\"data1\", 100, 20, 30),\n      new MultiValueColumn(\"data2\", 20, 70, 100)\n    );\n    %>\n    ```\n\n3.  Add the `<chart>` taglib to the `view.jsp`, passing the `_stepChartConfig`\n    as the `config` attribute's value:\n\n    ```markup\n    <chart:step\n      config=\"<%= _stepChartConfig %>\"\n    />\n    ```\n\n![Figure 1: A step chart steps between the points of data, resembling steps.](../../../../images/chart-taglib-step.png)\n\nYou can also use an area step chart if you prefer. An area step chart highlights\nthe area covered by a step graph.\n\n```markup\n<chart:area-step\n  config=\"<%= _stepChartConfig %>\"\n/>\n```\n\n![Figure 2: An area step chart highlights the area covered by a step graph.](../../../../images/chart-taglib-area-step.png)\n\nAwesome! Now you know how to create step charts for your apps.\n\n## Related Topics\n\n- [Line Charts](/docs/7-2/reference/-/knowledge_base/r/line-charts)\n- [Spline Charts](/docs/7-2/reference/-/knowledge_base/r/spline-charts)\n- [Scatter Charts](/docs/7-2/reference/-/knowledge_base/r/scatter-charts)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/07-combination-charts.markdown",
    "content": "---\nheader-id: combination-charts\n---\n\n# Combination Charts\n\n[TOC levels=1-4]\n\nCombination charts have minor differences from other charts. In a combination\nchart, you must define the representation type of each data set: `AREA`,\n`AREA_SPLINE`, `AREA_STEP`, `BAR`, `BUBBLE`, `DONUT`, `GAUGE`, `LINE`, `PIE`,\n`SCATTER`, `SPLINE`, or `STEP`. Each data set in a combination chart is an\ninstance of the [`TypedMultiValueColumn` object](https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/TypedMultiValueColumn.html). Each object receives an ID, the\nrepresentation type, and values for the data. Follow these steps to configure\nyour portlet to use combination charts.\n\n1.  Import the chart taglib along with the `CombinationChartConfig`,\n    `MultiValueColumn`, and `MultiValueColumn.Type` classes into your bundle's\n    `init.jsp` file:\n\n    ```markup\n    <%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.combination.CombinationChartConfig\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.MultiValueColumn\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.TypedMultiValueColumn.Type\" %>\n    ```\n\n2.  Add the following Java scriptlet to the top of your `view.jsp`:\n\n    ```java\n    <%\n    CombinationChartConfig _combinationChartConfig =\n    new CombinationChartConfig();\n\n    _combinationChartConfig.addColumns(\n      new TypedMultiValueColumn(\n        \"data1\", Type.BAR, 30, 20, 50, 40, 60, 50),\n      new TypedMultiValueColumn(\n        \"data2\", Type.BAR, 200, 130, 90, 240, 130, 220),\n      new TypedMultiValueColumn(\n        \"data3\", Type.SPLINE, 300, 200, 160, 400, 250, 250),\n      new TypedMultiValueColumn(\n        \"data4\", Type.LINE, 200, 130, 90, 240, 130, 220),\n      new TypedMultiValueColumn(\n        \"data5\", Type.BAR, 130, 120, 150, 140, 160, 150),\n      new TypedMultiValueColumn(\n        \"data6\", Type.AREA, 90, 70, 20, 50, 60, 120)\n      );\n\n    _combinationChartConfig.addGroup(\"data1\", \"data2\");\n    %>\n    ```\n\n3.  Add the `<chart>` taglib to the `view.jsp`, passing the\n    `_combinationChartConfig` as the `config` attribute's value:\n\n    ```markup\n    <chart:combination\n      config=\"<%= _combinationChartConfig %>\"\n    />\n    ```\n\n![Figure 1: A combination chart displays a variety of data set types.](../../../../images/chart-taglib-combination.png)\n\nAwesome! Now you know how to create combination charts for your apps.\n\n## Related Topics\n\n- [Bar Charts](/docs/7-2/reference/-/knowledge_base/r/bar-charts)\n- [Line Charts](/docs/7-2/reference/-/knowledge_base/r/line-charts)\n- [Geomap Charts](/docs/7-2/reference/-/knowledge_base/r/geomap-charts)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/08-donut-charts.markdown",
    "content": "---\nheader-id: donut-charts\n---\n\n# Donut Charts\n\n[TOC levels=1-4]\n\nDonut charts are percentage-based. A donut chart is similar to a pie chart, but\nit has a hole in the center. Each data set must be defined as a new instance of\nthe\n[`SingleValueColumn` object](https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/SingleValueColumn.html).\nFollow these steps to configure your portlet to use donut charts.\n\n1.  Import the chart taglib along with the `DonutChartConfig` and\n    `SingleValueColumn` classes into your bundle's `init.jsp` file:\n\n    ```markup\n    <%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.percentage.donut.DonutChartConfig\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.SingleValueColumn\" %>\n    ```\n\n2.  Add the following Java scriptlet to the top of your `view.jsp`:\n\n    ```java\n    <%\n    DonutChartConfig _donutChartConfig = new DonutChartConfig();\n\n    _donutChartConfig.addColumns(\n      new SingleValueColumn(\"data1\", 30),\n      new SingleValueColumn(\"data2\", 70)\n    );\n    %>\n    ```\n\n3.  Add the `<chart>` taglib to the `view.jsp`, passing the `_donutChartConfig`\n    as the `config` attribute's value:\n\n    ```markup\n    <chart:donut\n      config=\"<%= _donutChartConfig %>\"\n    />\n    ```\n\n![Figure 1: A donut chart is similar to a pie chart, but it has a hole in the center.](../../../../images/chart-taglib-donut.png)\n\nAwesome! Now you know how to create donut charts for your apps.\n\n## Related Topics\n\n- [Pie Charts](/docs/7-2/reference/-/knowledge_base/r/pie-charts)\n- [Gauge Charts](/docs/7-2/reference/-/knowledge_base/r/gauge-charts)\n- [Bar Charts](/docs/7-2/reference/-/knowledge_base/r/bar-charts)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/09-gauge-charts.markdown",
    "content": "---\nheader-id: gauge-charts\n---\n\n# Gauge Charts\n\n[TOC levels=1-4]\n\nGauge charts are percentage-based. A gauge chart shows where percentage-based\ndata falls over a given range. Each data set must be defined as a new instance\nof the\n[`SingleValueColumn` object](https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/SingleValueColumn.html).\nFollow these steps to configure your portlet to use gauge charts.\n\n1.  Import the chart taglib along with the `GaugeChartConfig` and\n    `SingleValueColumn` classes into your bundle's `init.jsp` file:\n\n    ```markup\n    <%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.gauge.GaugeChartConfig\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.SingleValueColumn\" %>\n    ```\n\n2.  Add the following Java scriptlet to the top of your `view.jsp`:\n\n    ```java\n    <%\n    GaugeChartConfig _gaugeChartConfig = new GaugeChartConfig();\n\n    _gaugeChartConfig.addColumn(\n      new SingleValueColumn(\"data1\", 85.4)\n    );\n    %>\n    ```\n\n3.  Add the `<chart>` taglib to the `view.jsp`, passing the `_gaugeChartConfig`\n    as the `config` attribute's value:\n\n    ```markup\n    <chart:gauge\n      config=\"<%= _gaugeChartConfig %>\"\n    />\n    ```\n\n![Figure 1: A gauge chart shows where percentage-based data falls over a given range.](../../../../images/chart-taglib-gauge.png)\n\nAwesome! Now you know how to create gauge charts for your apps.\n\n## Related Topics\n\n- [Pie Charts](/docs/7-2/reference/-/knowledge_base/r/pie-charts)\n- [Donut Charts](/docs/7-2/reference/-/knowledge_base/r/donut-charts)\n- [Bar Charts](/docs/7-2/reference/-/knowledge_base/r/bar-charts)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/10-pie-charts.markdown",
    "content": "---\nheader-id: pie-charts\n---\n\n# Pie Charts\n\n[TOC levels=1-4]\n\nPie charts are percentage-based. A pie chart models percentage-based data as\nindividual slices of pie. Each data set must be defined as a new instance of the\n[`SingleValueColumn` object](https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/SingleValueColumn.html).\nFollow these steps to configure your portlet to use pie charts.\n\n1.  Import the chart taglib along with the `PieChartConfig` and\n    `SingleValueColumn` classes into your bundle's `init.jsp` file:\n\n    ```markup\n    <%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.percentage.pie.PieChartConfig\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.SingleValueColumn\" %>\n    ```\n\n2.  Add the following Java scriptlet to the top of your `view.jsp`:\n\n    ```java\n    <%\n    PieChartConfig _pieChartConfig = new PieChartConfig();\n\n    _pieChartConfig.addColumn(\n      new SingleValueColumn(\"data1\", 85.4)\n    );\n    %>\n    ```\n\n3.  Add the `<chart>` taglib to the `view.jsp`, passing the `_pieChartConfig`\n    as the `config` attribute's value:\n\n    ```markup\n    <chart:pie\n      config=\"<%= _pieChartConfig %>\"\n    />\n    ```\n\n![Figure 1: A pie chart models percentage-based data as individual slices of pie.](../../../../images/chart-taglib-pie.png)\n\nAwesome! Now you know how to create pie charts for your apps.\n\n## Related Topics\n\n- [Donut Charts](/docs/7-2/reference/-/knowledge_base/r/donut-charts)\n- [Gauge Charts](/docs/7-2/reference/-/knowledge_base/r/gauge-charts)\n- [Spline Charts](/docs/7-2/reference/-/knowledge_base/r/spine-charts)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/11-geomap-charts.markdown",
    "content": "---\nheader-id: geomap-charts\n---\n\n# Geomap Charts\n\n[TOC levels=1-4]\n\nA Geomap Chart lets you visualize data based on geography, given a specified\ncolor range---a lighter color representing a lower rank and a darker a higher\nrank usually. The default configuration comes from the Clay charts\n[geomap component](https://github.com/liferay/clay/blob/2.x-stable/packages/clay-charts/src/Geomap.js#L90-L104):\nwhich ranges from light-blue (#b1d4ff) to dark-blue (#0065e4) and ranks the\ngeography based on the location's `pop_est` value (specified in the geomap's\nJSON file).\n\n![Figure 1: A Geomap chart displays a heatmap representing the data.](../../../../images/chart-taglib-geomap-default.png)\n\nFollow these steps to configure your portlet to use geomap charts.\n\n1.  Import the chart taglib along with the `GeomapConfig`, `GeomapColor`, and\n    `GeomapColorRange` classes into your bundle's `init.jsp` file:\n\n    ```markup\n    <%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.geomap.GeomapConfig\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.geomap.GeomapColor\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.geomap.GeomapColorRange\" %>\n    ```\n\n2.  Add the following Java scriptlet to the top of your `view.jsp`. The\n    colors---a color for minimum and a color for maximum---are completely\n    configurable, as shown in the second example configuration below:\n    `_geomapConfig2`. Create a new `GeomapColorRange` and set the minimum and\n    maximum color values with the `setMax()` and `setMin()` methods. Specify the\n    highlight color---the color displayed when you mouse over an area---with the\n    `setSelected()` method. use the `geomapColor.setValue()` method to specify\n    the JSON property to determine the geomap's ranking. Specify the JSON\n    filepath with the `setDataHREF()` method. The example below displays a\n    geomap based on the length of each location's name:\n\n    ```java\n    <%\n    GeomapConfig _geomapConfig1 = new GeomapConfig();\n    GeomapConfig _geomapConfig2 = new GeomapConfig();\n\n    GeomapColor geomapColor = new GeomapColor();\n    GeomapColorRange geomapColorRange = new GeomapColorRange();\n\n    geomapColorRange.setMax(\"#b2150a\");\n    geomapColorRange.setMin(\"#ee3e32\");\n\n    geomapColor.setGeomapColorRange(geomapColorRange);\n\n    geomapColor.setSelected(\"#a9615c\");\n\n    geomapColor.setValue(\"name_len\");\n\n    _geomapConfig2.setColor(geomapColor);\n\n    StringBuilder sb = new StringBuilder();\n\n    sb.append(_portletRequest.getScheme());\n    sb.append(StringPool.COLON);\n    sb.append(StringPool.SLASH);\n    sb.append(StringPool.SLASH);\n    sb.append(_portletRequest.getServerName());\n    sb.append(StringPool.COLON);\n    sb.append(_portletRequest.getServerPort());\n    sb.append(_portletRequest.getContextPath());\n    sb.append(StringPool.SLASH);\n    sb.append(\"geomap.geo.json\");\n\n    _geomapConfig1.setDataHREF(sb.toString());\n    _geomapConfig2.setDataHREF(sb.toString());\n    %>\n    ```\n\n3.  Add the `<chart>` taglib to the `view.jsp` along with any styling\n    information for the geomap, such as the size and margins as shown below:\n\n    ```html\n    <style type=\"text/css\">\n    \t.geomap {\n    \t\tmargin: 10px 0 10px 0;\n    \t}\n    \t.geomap svg {\n    \t\twidth: 100%;\n    \t\theight: 500px !important;\n    \t}\n    </style>\n\n    <chart:geomap\n    \tconfig=\"<%= _geomapConfig1 %>\"\n    \tid=\"geomap-default-colors\"\n    />\n\n    <chart:geomap\n    \tconfig=\"<%= _geomapConfig2 %>\"\n    \tid=\"geomap-custom-colors\"\n    />\n    ```\n\n![Figure 2: Geomap charts can be customized to fit the look and feel you desire.](../../../../images/chart-taglib-geomap-custom.png)\n\nAwesome! Now you know how to create geomap charts for your apps.\n\n## Related Topics\n\n- [Bar Charts](/docs/7-2/reference/-/knowledge_base/r/bar-charts)\n- [Pie Charts](/docs/7-2/reference/-/knowledge_base/r/pie-charts)\n- [Combination Charts](/docs/7-2/reference/-/knowledge_base/r/combination-charts)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/12-predictive-charts.markdown",
    "content": "---\nheader-id: predictive-charts\n---\n\n# Predictive Charts\n\n[TOC levels=1-4]\n\nPredictive charts let you visualize current data along with predicted/forecasted\ndata within a given value range.\n\n![Figure 1: Predicted/forecasted data is surrounded by a highlighted area of possible values.](../../../../images/chart-taglib-predictive-value-range.png)\n\nFollow these steps to use predictive charts.\n\n1.  Import the chart taglib along with the `PredictiveChartConfig` and\n    `MixedDataColumn` classes into your bundle's `init.jsp` file:\n\n    ```markup\n    <%@ taglib prefix=\"chart\" uri=\"http://liferay.com/tld/chart\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.predictive.PredictiveChartConfig\" %>\n    <%@ page import=\"com.liferay.frontend.taglib.chart.model.MixedDataColumn\" %>\n    ```\n\n2.  Add the following Java scriptlet to the top of your `view.jsp`. Add a\n    [`MixedDataColumn` object](https://docs.liferay.com/portal/7.2-latest/apps/frontend-taglib-2.0.2/javadocs/com/liferay/frontend/taglib/chart/model/MixedDataColumn.html)\n    ---a column that supports both single number values and arrays of three\n    numbers---for each data series. Single number values define existing data.\n    Arrays of numbers are used as the prediction/forecast data and contain three\n    numbers: a minimum value, an estimated value, and a maximum value. The\n    estimated value is rendered solid and surrounded by a highlighted area with\n    borders specified by the minimum and maximum values. This lets you visualize\n    your estimated values, while also giving you an idea of the possible value\n    ranges. Use the `addDataColumn()` method to add each data series:\n\n    ```java\n    <%\n    private PredictiveChartConfig _predictiveChartConfig = new\n    PredictiveChartConfig();\n\n    MixedDataColumn mixedDataColumn1 = new MixedDataColumn(\n      \"data1\", 130, 340, 200, 500, 80, 240, 40,\n      new Number[] {370, 400, 450}, new Number[] {210, 240, 270},\n      new Number[] {150, 180, 210}, new Number[] {60, 90, 120},\n      new Number[] {310, 340, 370}\n    );\n\n    _predictiveChartConfig.addDataColumn(mixedDataColumn1);\n\n    MixedDataColumn mixedDataColumn2 = new MixedDataColumn(\n      \"data2\", 210, 160, 50, 125, 230, 110, 90,\n      Arrays.asList(170, 200, 230), Arrays.asList(10, 40, 70),\n      Arrays.asList(350, 380, 410), Arrays.asList(260, 290, 320),\n      Arrays.asList(30, 70, 150)\n    );\n\n    _predictiveChartConfig.addDataColumn(mixedDataColumn2);\n\n    _predictiveChartConfig.setAxisXTickFormat(\"%b\");\n\n    _predictiveChartConfig.setPredictionDate(\"2018-07-01\");\n\n    List<String> timeseries = new ArrayList<>();\n\n    timeseries.add(\"2018-01-01\");\n    timeseries.add(\"2018-02-01\");\n    timeseries.add(\"2018-03-01\");\n    timeseries.add(\"2018-04-01\");\n    timeseries.add(\"2018-05-01\");\n    timeseries.add(\"2018-06-01\");\n    timeseries.add(\"2018-07-01\");\n    timeseries.add(\"2018-08-01\");\n    timeseries.add(\"2018-09-01\");\n    timeseries.add(\"2018-10-01\");\n    timeseries.add(\"2018-11-01\");\n    timeseries.add(\"2018-12-01\");\n\n    _predictiveChartConfig.setTimeseries(timeseries);\n    %>\n    ```\n    Predictive charts have these properties:\n\n    **axisXTickFormat:** An optional string which specfies the time formatting\n    on the X axis. For more information on which formats can be specified please\n    refer to\n    [d3's time format README](https://github.com/d3/d3-time-format/blob/master/README.md#locale_format).\n    This value is set using the `setAxisXTickFormat()` method.\n\n    **Prediction Date:** A date as a string that represents the point in the\n    timeline from when the forecast/prediction is shown. This value is parsed as\n    a Date object in JavaScript and set using the `setPredictionDate()` method.\n\n    **Time Series:** A timeline for the data which is displayed on the X axis of\n    the chart. This value is set as an array of dates (`2018-01-01` for example).\n\n3.  Add the `<chart>` taglib to the `view.jsp`, passing the\n    `_predictiveChartConfig` as the `config` attribute's value:\n\n    ```markup\n    <chart:predictive\n      config=\"<%= _predictiveChartConfig %>\"\n    />\n    ```\n\nThe area contained within the light-blue rectangle is the point from which the\npredicted/forecasted values are shown:\n\n![Figure 2: A predictive chart lets you visualize estimated future data alongside existing data.](../../../../images/chart-taglib-predictive.png)\n\nAwesome! Now you know how to create predictive charts for your apps.\n\n## Related Topics\n\n- [Line Charts](/docs/7-2/reference/-/knowledge_base/r/Line-charts)\n- [Combination Charts](/docs/7-2/reference/-/knowledge_base/r/combination-charts)\n- [Geomap Charts](/docs/7-2/reference/-/knowledge_base/r/geomap-charts)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/08-chart-taglibs/13-refreshing-charts-to-reflect-real-time-data.markdown",
    "content": "---\nheader-id: refreshing-charts-to-reflect-real-time-data\n---\n\n# Refreshing Charts to Reflect Real Time Data\n\n[TOC levels=1-4]\n\nThe polling interval property is an optional property for all charts. It\nspecifies the time in milliseconds for the chart's data to refresh. You can\nuse this for charts that receive any kind of real time data, such as a JSON file\nthat changes periodically. This ensures that the chart is up to date, reflecting\nthe most recent data. Follow these steps to configure your chart to use real\ntime data.\n\n1.  Add a new java scriptlet and create a new instance of the chart's object,\n\t\tand put the data into the `data` attribute. Finally, set the chart's polling\n\t\tinterval with the `setPollingInterval()` method. An example `view.jsp`\n\t\tconfiguration is shown below:\n\n\t\t```java\n\t\t<%\n\t\tLineChartConfig _pollingIntervalLineChartConfig = new LineChartConfig();\n\n\t\t_pollingIntervalLineChartConfig.put(\"data\", \"/foo.json\");\n\n\t\t_pollingIntervalLineChartConfig.setPollingInterval(2000);\n\t\t%>\n\t\t```\n\n2.  Set the chart taglib's `config` attribute to the updated configuration\n\t\tobject that you created in the last step, as shown in the example below:\n\n\t\t```markup\n\t\t<chart:line\n\t\t\tcomponentId=\"polling-interval-line-chart\"\n\t\t\tconfig=\"<%= _pollingIntervalLineChartConfig %>\"\n\t\t/>\n\t\t```\n\n![Figure 1: The polling interval property lets you refresh charts at a given interval to reflect real time data.](../../../../images/chart-polling-interval.gif)\n\nNow you know how to reflect real time data in your charts!\n\n## Related Topics\n\n- [Bar Charts](/docs/7-2/reference/-/knowledge_base/r/bar-charts)\n- [Scatter Charts](/docs/7-2/reference/-/knowledge_base/r/scatter-charts)\n- [Donut Charts](/docs/7-2/reference/-/knowledge_base/r/donut-charts)"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/09-aui-taglibs/01-using-aui-taglibs-intro.markdown",
    "content": "---\nheader-id: using-aui-taglibs\n---\n\n# Using AUI Taglibs\n\n[TOC levels=1-4]\n\nThe AUI tag library provides tags that implement commonly used UI components. \nThese tags make your markup consistent, responsive, and accessible. \n\nYou can find a list of the available `<aui>` taglibs in the \n[AUI taglibdocs](https://docs.liferay.com/portal/7.2-latest/taglibs/util-taglib/aui/tld-summary.html). \nEach taglib has a list of attributes that can be passed to the tag. Some of \nthese are required, and some are optional. See the taglibdocs to view the \nrequirements for each tag. You'll find the full markup generated by the tags in \ntheir JSPs in their \n[Liferay Github Repo](https://github.com/liferay/liferay-portal/tree/7.2.x/portal-web/docroot/html/taglib/aui) \nfolders.\n\nTo use the AUI taglib library in your apps, you must add the following \ndeclaration to your JSP:\n\n```markup\n<%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n```\n\nThe AUI taglib is also available via a macro for your FreeMarker theme templates \nand web content templates. Follow this syntax:\n\n```markup\n<@liferay_aui[\"tag-name\"] attribute=\"string value\" attribute=10 />\n```\n\nThis section covers how to create UI components with the AUI taglibs. Each \narticle contains code examples along with a screenshot of the resulting UI. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/03-front-end-taglibs/09-aui-taglibs/02-building-forms-with-aui-tags.markdown",
    "content": "---\nheader-id: building-forms-with-aui-tags\n---\n\n# Building Forms with AUI Tags\n\n[TOC levels=1-4]\n\nThe \n[AUI tag library](https://docs.liferay.com/portal/7.2-latest/taglibs/util-taglib/aui/tld-summary.html) \nprovides all the components you need to build forms for your applications. AUI \ntags provide many benefits to standard form elements, such as custom \nnamespacing, localization, and even validation. They provide multiple attributes \nthat let you create the experience you want for your users. \n\nFollow these steps to build a form using AUI tags:\n\n1.  Add the `aui` taglib declaration to your portlet's `view.jsp` if you haven't \n    already:\n\n    ```markup\n    <%@ taglib prefix=\"aui\" uri=\"http://liferay.com/tld/aui\" %>\n    ```\n\n2.  Build your form using the tags shown below. Each tag links to the \n    corresponding taglibdoc that list the available attributes:\n\n    - [`<aui:input>`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/input.html)\n    - [`<aui:button>`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/button.html)\n    - [`<aui:button-row>`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/button-row.html)\n    - [`<aui:container>`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/container.html)\n    - [`<aui:col>`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/col.html)\n    - [`<aui:row>`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/row.html)\n    - [`<aui:field-wrapper>`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/field-wrapper.html)\n    - [`<aui:fieldset>`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/fieldset.html)\n    - [`<aui:fieldset-group>`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/fieldset-group.html)\n    - [`<aui:form>`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/form.html)\n    - [`<aui:select>`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/select.html)\n    - [`<aui:option>`](https://docs.liferay.com/ce/portal/7.2-latest/taglibs/util-taglib/aui/option.html)\n\n    An example form is shown below:\n\n    ```markup\n    <aui:form name=\"fm\">\n    \t<aui:fieldset-group markupView=\"lexicon\">\n    \t\t<aui:fieldset label=\"Personal Information\">\n    \t\t\t<aui:row>\n    \t\t\t\t<aui:col width=\"50\">\n    \t\t\t\t\t<aui:input label=\"First Name\" name=\"firstName\" type=\"text\" />\n    \t\t\t\t</aui:col>\n    \t\t\t\t<aui:col width=\"50\">\n    \t\t\t\t\t<aui:input label=\"Last Name\" name=\"lastName\" type=\"text\" />\n    \t\t\t\t</aui:col>\n    \t\t\t</aui:row>\n    \t\t\t<aui:row>\n    \t\t\t\t<aui:col width=\"50\">\n    \t\t\t\t\t<aui:input label=\"Username\" name=\"username\" type=\"text\" />\n    \t\t\t\t</aui:col>\n    \t\t\t\t<aui:col width=\"50\">\n    \t\t\t\t\t<aui:input label=\"Email\" name=\"email\" type=\"email\" />\n    \t\t\t\t</aui:col>\n    \t\t\t</aui:row>\n    \t\t</aui:fieldset>\n    \t</aui:fieldset-group>\n    \t<aui:fieldset-group markupView=\"lexicon\">\n    \t\t<aui:fieldset label=\"Miscellaneous\">\n    \t\t\t<aui:input label=\"Hobbies\" name=\"hobbies\" type=\"textarea\" />\n    \t\t\t<aui:input label=\"Receive email updates\" name=\"emailUpdates\" type=\"checkbox\" />\n    \t\t</aui:fieldset>\n    \t</aui:fieldset-group>\n    \t<aui:button-row>\n    \t\t<aui:button name=\"submitButton\" type=\"submit\" value=\"Submit\" />\n    \t</aui:button-row>\n    </aui:form>\n    ```\n\n    ![Figure 1: The AUI tags provide everything you need to build forms for your applications.](../../../../images/aui-taglib-basic-form.png)\n\n3.  Optionally add validation to your form fields. Nest a `<aui:validator>` tag \n    inside each form field that you want to validate. Specify the validation \n    rule with the `<aui:validator>` tag's `name` attribute (The available \n    validation rules are shown in the table below). You can override a field's \n    default validation error message with the `errorMessage` attribute. An \n    example configuration is shown below:\n\n    ```markup\n    <aui:form name=\"myForm\">\n        <aui:input name=\"password\" id=\"password\" label=\"Password\" \n        required=\"true\" />\n        <aui:input name=\"confirmPassword\" id=\"password\" \n        label=\"Confirm Password\" required=\"true\">\n            <aui:validator name=\"equalTo\" \n            errorMessage=\"The passwords much match. Please try again.\" >\n            '#<portlet:namespace>password'\n            </aui:validator>\n        </aui:input>\n    </aui:form>\n    ```\n\n    ![Figure 2: The AUI tags also provide validation for form fields.](../../../../images/aui-taglib-form-validation.png)\n\n    The full list of available validation rules is shown in the table below:\n\n    | Rule | Description | Default Error Message |\n    | --- | --- | --- |\n    | `acceptFiles` | Specifies that the field can only contain the file types given. Each file extension must be separated by a comma. For example </br> `<aui:validator name=\"acceptFiles\">'jpg,png,tif,gif'</aui:validator>` | 'Please enter a file with a valid extension ([supported extensions]).' |\n    | `alpha` | Permits alphabetic characters | 'Please enter only alpha characters.' |\n    | `alphanum` | Permits alphanumeric characters | 'Please enter only alphanumeric characters.' |\n    | `date` | Permits dates | 'Please enter a valid date.' |\n    | `digits` | Permits digits | 'Please enter only digits.' |\n    | `email` | Permits an email address | 'Please enter a valid email address.' |\n    | `equalTo` | Permits contents equal to another field with the specified field ID. For example, </br> `<aui:validator name=\"equalTo\">'#<portlet:namespace/>password'</aui:validator>` | 'Please enter the same value again.' |\n    | `max` | Permits an integer value less than the specified value. For example, a max value of 20 is specified with </br> `<aui:validator name=\"max\">20</aui:validator>` | 'Please enter a value less than or equal to [max value].' |\n    | `maxLength` | Permits a maximum field length of the specified size (follows the same syntax as `max`) | 'Please enter no more than [max] characters.' |\n    | `min` | Permits an integer value greater than the specified minimum value (follows the same syntax as `max`) | 'Please enter a value greater than or equal to [min value].' |\n    | `minLength` | Permits a field length longer than the specified size (follows the same syntax as `max`). | 'Please enter at least [min] characters.' |\n    | `number` | Permits numerical values | 'Please enter a valid number.' |\n    | `range` | Permits a number between the specified range. For example, a range between 1.23 and 10 is specified here </br> `<aui:validator name=\"range\">[1.23,10]</aui:validator>` | 'Please enter a value between [0] and [1].' |\n    | `rangeLength` | Permits a field length between the specified range (follows the same syntax as `range`)  | 'Please enter a value between [0] and [1] characters long.' |\n    | `required` | Prevents a blank field  | 'This field is required.' |\n    | `url` | Permits a URL value | 'Please enter a valid URL.' |\n\nNow you know how to build user-friendly forms for your applications. \n\n## Related Topics\n\n- [Using the Chart Taglib in Your Portlets](/docs/7-2/reference/-/knowledge_base/r/using-the-chart-taglib-in-your-portlets)\n- [Using Liferay Front-end Taglibs in Your Portlet](/docs/7-2/reference/-/knowledge_base/r/using-liferay-front-end-taglibs-in-your-portlet)\n- [Using the Clay Taglib in Your portlets](/docs/7-2/reference/-/knowledge_base/r/using-the-clay-taglib-in-your-portlets)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/04-liferay-npm-bundler/01-liferay-npm-bundler-intro.markdown",
    "content": "---\nheader-id: liferay-npm-bundler\n---\n\n# liferay-npm-bundler\n\n[TOC levels=1-4]\n\nThe liferay-npm-bundler is a bundler (like \n[Webpack](https://webpack.github.io/) \nor \n[Browserify](http://browserify.org/)\n) that targets @product@ as a platform and assumes you're using your npm \npackages from widgets (as opposed to typical web applications). \n\nThe workflow for running npm packages inside widgets is slightly different from\nstandard bundlers. Instead of bundling the JavaScript in a single file, you must\n*link* all packages together in the browser when the full web page is assembled.\nThis lets widgets share common versions of modules instead of each one loading\nits own copy. The liferay-npm-bundler handles this for you. \n\n| **Note:** You can also find information for the liferay-npm-bundler in the \n| project's \n| [Wiki](https://github.com/liferay/liferay-npm-build-tools/wiki).\n\n## How the Liferay npm Bundler Works Internally\n\nThe liferay-npm-bundler takes a widget project and outputs its files \n(including npm packages) to a build folder, so the standard widget build \n(Gradle) can produce an OSGi bundle. You can learn more about the build folder's \nstructure in \n[The Structure of OSGi Bundles Containing NPM Packages](/docs/7-2/reference/-/knowledge_base/r/the-structure-of-osgi-bundles-containing-npm-packages) \nreference. \n\nThe liferay-npm-bundler uses the process below to create the OSGi bundle:\n\n1.  Copy the project's `package.json` file to the output directory.\n\n2.  Traverse the project's dependency tree to determine its dependencies.\n\n3.  For the project,\n\n    a. Run the source files, specified in the `.npmbundlerrc` configuration, \n       through the rules.\n\n    b. Pre-process the project's package with any configured plugins.\n\n    c. Run \n       [Babel](https://babeljs.io/) with configured plugins for each `.js` file\n       inside the project.\n\n    d. Post-process the project package with any configured plugins.\n\n4.  For each npm package dependency:\n\n    a. Copy the npm package to the output folder and prefix the bundle's name \n    to it. Note that the bundler stores packages in a plain \n    *bundle-name$package*@*version* format, rather than the standard \n    node_modules tree format. To determine what is copied, the bundler invokes a \n    plugin to filter the package file list. \n\n    b. Run rules on the package files.\n\n    c. Pre-process the npm package with any configured plugins.\n\n    d. Run \n       [Babel](https://babeljs.io/) with configured plugins for each `.js` file \n       inside the npm package.\n\n    e. Post-process the npm package with any configured plugins.\n\nThe only difference between the pre-process and post-process steps are when they \nare run (before or after Babel is run, respectively). During this workflow, \nliferay-npm-bundler calls all the configured plugins so they can perform \ntransformations on the npm packages (for instance, modifying their `package.json` \nfiles, or deleting or moving files). \n\n| **Note:** that the pre, post, and Babel phases were designed for the old mode \n| of operation (See the [Migrating Your Project to Use the New Mode](/docs/7-2/frameworks/-/knowledge_base/f/migrating-your-project-to-use-the-new-mode) \n| for more information) and they will gradually be replaced with rules for the \n| new mode.\n\nIn this reference section, you'll learn more about the liferay-npm-bundler's \nconfiguration, default presets, format, and more. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/04-liferay-npm-bundler/03-npmbundlerrc-structure.markdown",
    "content": "---\nheader-id: understanding-the-npmbundlerrcs-structure\n---\n\n# Understanding the `.npmbundlerrc`'s Structure\n\n[TOC levels=1-4]\n\nThe liferay-npm-bundler is configured via a `.npmbundlerrc` file placed in the \nwidget project's root folder. You can create a complete configuration manually \nor extend a configuration preset (via Babel). \n\nThis article explains the `.npmbundlerrc` file's structure. See the \n[default preset reference](/docs/7-2/reference/-/knowledge_base/r/how-the-default-preset-configures-the-liferay-npm-bundler) \nto learn how the default preset configures the liferay-npm-bundler. See \n[Creating JavaScript Widgets with JavaScript Tooling](/docs/7-2/frameworks/-/knowledge_base/f/creating-and-bundling-javascript-widgets-with-javascript-tooling) \nto learn how to use the liferay-npm-bundler along with the Liferay JS Generator \nto create JavaScript widgets. \n\n## The Structure\n\nThe `.npmbundlerrc` file has four possible phase definitions: *copy-process*, \n*pre-process*, *post-process*, and *babel*. These phase definitions are \nexplained in more detail below:\n\n**Copy-Process:** Defined with the `copy-plugins` property (only available for \ndependency packages). Specifies which files should be copied or excluded from \neach given package.\n\n**Pre-Process:** Defined with the `plugins` property. Specifies plugins to run \nbefore the Babel phase is run.\n\n**Babel:** Defined with the `.babelrc` definition. Specifies the `.babelrc` file \nto use when running Babel through the package's `.js` files. \n\n| **Note:** During this phase, Babel transforms package files (for example, to\n| convert them to AMD format, if necessary), but doesn't transpile them. In\n| theory, you could also transpile them by configuring the proper plugins. We\n| recommend transpiling before running the bundler, to avoid mixing both unrelated\n| processes.\n\n**Post-Process:** Defined with the `post-plugins` property. An alternative to \nusing the *pre-process* phase, this specifies plugins to run after the Babel \nphase has completed.\n\nHere's an example of a `.npmbundlerrc` configuration:\n\n```json\n{\n    \"exclude\": {\n        \"*\": [\n            \"test/**/*\"\n        ],\n        \"some-package-name\": [\n            \"test/**/*\",\n            \"bin/**/*\"\n        ],\n        \"another-package-name@1.0.10\": [\n            \"test/**/*\",\n            \"bin/**/*\",\n            \"lib/extras-1.0.10.js\"\n        ]\n    },\n    \"include-dependencies\": [\n        \"isobject\", \"isarray\"\n    ],\n    \"output\": \"build\",\n    \"verbose\": false,\n    \"dump-report\": true,\n    \"config\": {\n        \"imports\": {\n            \"npm-angular5-provider\": {\n                \"@angular/common\": \"^5.0.0\",\n            \"@angular/core\": \"^5.0.0\"\n            }\n        }\n    },\n    \"/\": {\n    \"plugins\": [\"resolve-linked-dependencies\"],\n    \".babelrc\": {\n      \"presets\": [\"liferay-standard\"]\n    },\n    \"post-plugins\": [\n            \"namespace-packages\",\n            \"inject-imports-dependencies\"\n        ]\n    },\n    \"*\": {\n      \"copy-plugins\": [\"exclude-imports\"],\n      \"plugins\": [\"replace-browser-modules\"],\n      \".babelrc\": {\n        \"presets\": [\"liferay-standard\"]\n      },\n      \"post-plugins\": [\n        \"namespace-packages\",\n        \"inject-imports-dependencies\",\n        \"inject-peer-dependencies\"\n      ]\n    },\n    \"packages\": {\n        \"a-package-name\": [\n        \"copy-plugins\": [\"exclude-imports\"],\n        \"plugins\": [\"replace-browser-modules\"],\n        \".babelrc\": {\n          \"presets\": [\"liferay-standard\"]\n        },\n        \"post-plugins\": [\n          \"namespace-packages\",\n          \"inject-imports-dependencies\",\n          \"inject-peer-dependencies\"\n        ]\n        ],\n        \"other-package-name@1.0.10\": [\n          \"copy-plugins\": [\"exclude-imports\"],\n          \"plugins\": [\"replace-browser-modules\"],\n          \".babelrc\": {\n            \"presets\": [\"liferay-standard\"]\n          },\n          \"post-plugins\": [\n            \"namespace-packages\",\n            \"inject-imports-dependencies\",\n            \"inject-peer-dependencies\"\n          ]\n        ]\n    }\n}\n```\n\n| **Note:** Not all definition formats (`*`, `some-package-name`, and \n| `some-package-name@version`) shown above are required. In most cases, the \n| wildcard definition (`*`) is enough. The non-wildcard formats \n| (`some-package-name` and `some-package-name@version`) are rare exceptions for \n| packages that require a more specific configuration than the wildcard \n| definition provides.\n\n### Standard Configuration Options\n\nBelow are the standard configuration options for the `.npmbundlerrc` file:\n\n`config`: Defines the global configuration that is made available to all \nliferay-npm-bundler and Babel plugins. Please refer to each plugin's \ndocumentation to find the available options for each specific plugin. \n\n```json\n{\n  \"config\": {\n    \"imports\": {\n      \"vuejs-provider\": {\n        \"vue\": \"^2.0.0\"\n      }\n    }\n  }\n}\n```\n\n`dump-report:` Sets whether to generate a debugging report. If `true`, a \n`liferay-npm-bundler-report.html` file is generated in the project directory \nthat describes all actions and decisions taken when processing project and npm \nmodules. Note that you can also pass this as the build flag \n`$ liferay-npm-bundler --dump-report` or `$ liferay-npm-bundler -r`. The default \nvalue is `false`.\n\n`no-tracking:` whether to send usage analytics to our servers. Note that you can \nalso pass this as a build flag with the CLI argument \n`$ liferay-npm-bundler --no-tracking`, or by \ncreating a marker file called `.liferay-npm-bundler-no-tracking` in the \nproject's root folder or any of its ancestors, or by setting the environment \nvariable `LIFERAY_NPM_BUNDLER_NO_TRACKING=''`. The default value is `false`.\n\n`output:` by default the bundler writes packages to the standard Gradle \nresources folder: `build/resources/main/META-INF/resources`. Set this value to \noverride the default output folder. Note that the dependency npm packages are \nplaced in a `node_modules` folder inside the build folder. Note if `create-jar` \nis set, the default output folder is `build`.\n\n`preset:` specifies the `liferay-npm-bundler` preset to use as a base \nconfiguration. Note that if a `.npmbundlerrc` file is not provided, the default \n`liferay-npm-bundler-preset-standard` preset is used. All settings provided by \nthe preset are inherited, but they can be overridden.\n\n`verbose:` Sets whether to output detailed information about what the tool is \ndoing to the console. The default value is `false`.\n\n### Package Processing Options\n\n`\"/\"`: plugins' configuration for the project's package.\n\n`\"\\\"`: plugins' configuration for dependency packages.\n\n*(asterisk)*: Defines the default plugin configuration for all npm packages. It \ncontains four values identified by a corresponding key. Keys `copy-plugins`, \n`plugins` and `post-plugins` identify arrays of `liferay-npm-bundler` plugins to \napply in the copy, pre and post process steps. Key `.babelrc` identifies an \nobject specifying the configuration to use in the Babel step and has the same \nstructure of a standard `.babelrc` file.\n\n`exclude:` defines glob expressions of files to exclude from bundling from all\nor specific packages. Each list is an array identified by one of the following \nkeys: `*` (any package), `{package name}` (any version of the package), or \n`{package name}@{version}` (a specific version of a package). Below is an \nexample configuration:\n\n```json\n{\n  \"exclude\": {\n    \"*\": [\"__tests__/**/*\"],\n    \"is-object\": [\"test/**/*\"],\n    \"is-array@1.0.1\": [\"test/**/*\", \"Makefile\"]\n  }\n}\n```\n\n`ignore:` skips processing the specified JavaScript files with Babel for the \nproject. An example configuration is shown below:\n\n```json\n{\n  \"ignore\": [\"lib/legacy/**/*.js\"]\n}\n```\n\n`include-dependencies:` defines packages to include in bundling, even if they \nare not listed under the `dependencies` section of `package.json`. These \npackages must be available in the `node_modules` folder (i.e. installed \nmanually, without saving them to `package.json`, or listed in the\n`devDependencies` section).\n\n`packages:` defines plugin configuration for npm packages, per package.\n\n`max-parallel-files:` Defines the maximum number of files to process in parallel \nto avoid EMFILE errors (especially on Windows). The default value is `128`. \n\n`process-serially:` **Note**: removed since v 2.7.0. Replaced with \n`max-parallel-files`. \n\n`rules:` defines rules to apply to the projects source files with the loader. \nRules must have a `use` array property that defines the loader to use, which \ncan be specified using a package name or an object with `loader` and \n`options` properties if applicable, and one or more of the properties below: \n\n- `test`: defines a regular expression to filter files in the `sources` folders \n  to determine whether to apply rules to them. The project-relative path of each \n  eligible file is compared against the regular expression and files that match \n  are processed by the loaders.\n- `exclude`: refines the `test` expression by specifying files to exclude.\n- `include`: refines the `test` expression by specifying files to include.\n\nHere's an example configuration: \n\n```json\n{\n  \"rules\": [\n    {\n      \"test\": \"\\\\.js$\",\n      \"exclude\": \"node_modules\",\n      \"use\": [\n        {\n          \"loader\": \"babel-loader\",\n          \"options\": {\n            \"presets\": [\"env\", \"react\"]\n          }\n        }\n      ]\n    },\n    {\n      \"test\": \"\\\\.css$\",\n      \"use\": [\"style-loader\"]\n    },\n    {\n      \"test\": \"\\\\.json$\",\n      \"use\": [\"json-loader\"]\n    }\n  ]\n}\n```\n\n`sources:` rules apply to files in these project folders. Folders can be nested\n(e.g. `/src/main/resources/`) and must be written using POSIX path separators \n(i.e. use `/` instead of `\\` on Win32 systems). Note that rules are \nautomatically applied to package dependency files of the project. \n\nAn example configuration is shown below:\n\n```json\n{\n  \"sources\": [\"src\", \"assets\"]\n}\n```\n\n### OSGi Bundle Creation Options\n\nSince version 2.2.0, the liferay-npm-bundler can create widget OSGi bundles for \nyou. See \n[Creating and Bundling JavaScript Widgets with JavaScript Tooling](/docs/7-2/frameworks/-/knowledge_base/f/creating-and-bundling-javascript-widgets-with-javascript-tooling) \nfor complete instructions. The configuration options for OSGi bundle \ncreation are shown below:\n\n- **create-jar**: Creates an OSGi bundle when set to a truthy value. When set to \n`true`, all sub-options take default values. When an object is passed, each \nsub-option can be configured individually. Note that you can also pass this as a \nbuild flag: `$ liferay-npm-bundler --create-` or `$ liferay-npm-bundler -j`. The \ndefault value is `false`.\n\n```json\n{\n  \"create-jar\": true\n}\n```\n\n- **create-jar.auto-deploy-portlet**: **Note** that this option is deprecated. \nUse the `create-jar.features.js-extender` option instead. \n\n- **create-jar.features.configuration**: specifies the file describing the \nsystem (OSGi) and widget instance (widget preferences, as defined in the \nPortlet spec) configuration to use. (see \n[Configuring System Settings and Instance Settings for Your JavaScript Widgets](/docs/7-2/frameworks/-/knowledge_base/f/configuring-system-settings-and-instance-settings-for-your-js-widget) \nfor more information on the required settings configuration). The \ndefault value is `features/configuration.json` if that file exists, otherwise \nthe default is `undefined`.\n\n```json\n{\n  \"create-jar\": {\n    \"features\": {\n      \"configuration\": \"features/configuration.json\"\n    }\n  }\n}\n```\n\n- **create-jar.output-dir:** specifies where to place the final JAR\n\n```json\n{\n  \"create-jar\": {\n    \"output-dir\": \"dist\"\n  }\n}\n```\n\n- **create-jar.features.js-extender:** controls whether to process the OSGi \nbundle with the JS Portlet Extender. You can also specify the minimum required \nversion of the Extender to use for the bundle. This can be useful if you want to \nuse advanced features in your bundle, but you want it to be deployable in older \nversions of the Extender. Pass the string `\"any\"` to let the bundle deploy in \nany version of the Extender. If `true`, the liferay-npm-bundler automatically \ndetermines the minimum version of the Extender required for the features used in \nthe bundle. the default value is `true`. An example configuration is shown \nbelow:\n\n```json\n{\n  \"create-jar\": {\n    \"features\": {\n      \"js-extender\": \"1.1.0\"\n    }\n  }\n}\n```\n\n- **create-jar.features.web-context:** specifies the context path to use for \npublishing bundle's static resources. The default value is \n`/{project name}-{project version}`.\n\n```json\n{\n  \"create-jar\": {\n    \"features\": {\n      \"web-context\": \"/my-project\"\n    }\n  }\n}\n```\n\n- **create-jar.features.localization:** specifies the L10N file to use for the \nbundle (see \n[Providing Localization in Your JavaScript Widgets](/docs/7-2/frameworks/-/knowledge_base/f/localizing-your-widget) \nfor more information on using localization in your widget. The default value is \n`features/localization/Language` if a properties file with that base name \nexists, otherwise the default is `undefined`.\n\n```json\n{\n  \"create-jar\": {\n    \"features\": {\n      \"localization\": \"features/localization/Language\"\n    }\n  }\n}\n```\n\n- **create-jar.features.settings:** **Note** that this option is deprecated. Use \nthe `create-jar.features.configuration` option instead. \n\n| **Note:** Plugins' configuration specifies the options for configuring plugins \n| in all the possible phases, as well as the `.babelrc` file to use when running \n| Babel (see [Babel's documentation](https://babeljs.io/docs/usage/babelrc/) for \n| more information on that file format). \n\n| **Note:** Prior to version 1.4.0 of the liferay-npm-bundler, package \n| configurations were placed next to the tools options \n| (`*`, `output`, `exclude`, etc.) To prevent package name collisions, package \n| configurations are now namespaced and placed under the `packages` section. To \n| maintain backwards compatibility, the liferay-npm-bundler falls back to the \n| root section outside `packages` for package configuration, if no package \n| configurations (`package-name@version`, `package-name`, or `*`) are found in \n| the `packages` section.\n\nNow you know the structure of the `.npmbundlerrc` file!\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/04-liferay-npm-bundler/04-how-the-default-preset-configures-the-liferay-npm-bundler.markdown",
    "content": "---\nheader-id: how-the-default-preset-configures-the-liferay-npm-bundler\n---\n\n# How the Default Preset Configures the liferay-npm-bundler\n\n[TOC levels=1-4]\n\nThe liferay-npm-bundler comes with a default configuration preset: \n[`liferay-npm-bundler-preset-standard`](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-preset-standard)\n---Note you may omit the `liferay-npm-bundler` prefix from the npm package name \nin your `.npmbundlerrc` file. This preset configures several plugins for the \nbuild process and is automatically used (even if the `.npmbundlerrc` is \nmissing), unless you override it with one of your own. Running the \nliferay-npm-bundler with this preset applies the \n[config file](https://github.com/liferay/liferay-npm-build-tools/blob/master/packages/liferay-npm-bundler-preset-standard/config.json) \nfrom `liferay-npm-bundler-preset-standard`:\n\n```json\n{\n  \"/\": {\n    \"plugins\": [\"resolve-linked-dependencies\"],\n    \".babelrc\": {\n      \"presets\": [\"liferay-standard\"]\n    },\n    \"post-plugins\": [\"namespace-packages\", \"inject-imports-dependencies\"]\n  },\n  \"*\": {\n    \"copy-plugins\": [\"exclude-imports\"],\n    \"plugins\": [\"replace-browser-modules\"],\n    \".babelrc\": {\n      \"presets\": [\"liferay-standard\"]\n    },\n    \"post-plugins\": [\n      \"namespace-packages\",\n      \"inject-imports-dependencies\",\n      \"inject-peer-dependencies\"\n    ]\n  }\n}\n```\n\nThe configuration above states that for all npm packages (`*`) the pre-process \nphase (`plugins`) must run the `replace-browser-modules` plugin. Setting this to \n`post-plugins` would run it during the post phase instead. \n\n| **Note:** You can override configuration preset values by adding your own \n| configuration to your project's `.npmbundlerrc` file. For instance, using the \n| configuration preset example above, you can define your own `.babelrc` value \n| in `.npmbundlerrc` file to override the defined \"liferay-standard\" babelrc \n| preset.\n\nThe \n[`liferay-standard` preset](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-preset-liferay-standard) \napplies the following plugins to packages:\n\n- [exclude-imports](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-exclude-imports):\n  Exclude packages declared in the `imports` section from the build.\n\n- [inject-imports-dependencies](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-inject-imports-dependencies):\n  Inject dependencies declared in the `imports` section in the dependencies' \n  `package.json` files.\n\n- [inject-peer-dependencies](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-inject-peer-dependencies):\n  Inject declared peer dependencies (as they are resolved in the project's \n  `node_modules` folder) in the dependencies' `package.json` files.\n\n- [namespace-packages](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-namespace-packages):\n  Namespace package names based on the root project's package name to isolate \n  packages per project and avoid collisions. This prepends \n  `<project-package-name>$` to each package name appearance in `package.json` \n  files.\n\n- [replace-browser-modules](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-replace-browser-modules):\n  Replaces the server side files for modules listed under \n  `browser`/`unpkg`/`jsdelivr` section of `package.json` with their browser \n  counterparts. \n\n- [resolve-linked-dependencies](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-resolve-linked-dependencies):\n  Replace linked dependencies versions appearing in `package.json` files \n  (those obtained from local file system or GitHub, for example) by their real \n  version number, as resolved in the project's `node_modules` directory.\n\nIn addition, the bundler runs Babel with the \n[babel-preset-liferay-standard](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-preset-liferay-standard) \npreset, that invokes the following plugins:\n\n- [babel-plugin-normalize-requires](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-plugin-normalize-requires):\n  Normalize AMD `require()` calls.\n\n- [babel-plugin-transform-node-env-inline](https://github.com/babel/minify/tree/master/packages/babel-plugin-transform-node-env-inline):\n  Inline the `NODE_ENV` environment variable, and if it's part of a binary \n  expression (eg. `process.env.NODE_ENV === \"development\"`), then statically \n  evaluate and replace it.\n\n- [babel-plugin-minify-dead-code-elimination](https://www.npmjs.com/package/babel-plugin-minify-dead-code-elimination): \n  Inline bindings when possible. Tries to evaluate expressions and prunes \n  unreachable as a result.\n\n- [babel-plugin-wrap-modules-amd](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-plugin-wrap-modules-amd):\n  Wrap modules inside an AMD `define()` module.\n\n- [babel-plugin-name-amd-modules](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-plugin-name-amd-modules):\n  Name AMD modules based on package name, version, and module path.\n\n- [babel-plugin-namespace-modules](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-plugin-namespace-modules):\n  Namespace modules based on the root project's package name, prepending \n  `<project-package-name>$`. Wrap modules inside an AMD `define()` module for \n  each module name appearance (in `define()` or `require()` calls) so that the \n  packages are localized per project and don't clash.\n\n- [babel-plugin-namespace-amd-define](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/babel-plugin-namespace-amd-define):\n  Add a prefix to AMD `define()` calls (by default `Liferay.Loader.`).\n\nNow you know the available configuration presets for `.npmbundlerrc` and how \nthey work. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/04-liferay-npm-bundler/05-the-structure-of-osgi-bundles-npm.markdown",
    "content": "---\nheader-id: the-structure-of-osgi-bundles-containing-npm-packages\n---\n\n# The Structure of OSGi Bundles Containing npm Packages\n\n[TOC levels=1-4]\n\nTo deploy JavaScript modules, you must create an OSGi bundle with the npm \ndependencies extracted from the project's `node_modules` folder and modify them \nto work with the \n[Liferay AMD Loader](https://github.com/liferay/liferay-amd-loader). \nThe liferay-npm-bundler automates this process for you, creating a bundle \nsimilar to the one below:\n\n- `my-bundle/`\n    - `META-INF/`\n        - `resources/`\n            - `package.json`\n                - name: my-bundle-package\n                - version: 1.0.0\n                - main: lib/index\n                - dependencies:\n                    - my-bundle-package$isarray: 2.0.0\n                    - my-bundle-package$isobject: 2.1.0\n                - ...\n            - `lib/`\n                - `index.js`\n                - ...\n            - ...\n            - `node_modules/`\n                - `my-bundle-package$isobject@2.1.0/`\n                    - `package.json`\n                        - name: my-bundle-package$isobject\n                        - version: 2.1.0\n                        - main: lib/index\n                        - dependencies:\n                            - my-bundle-package$isarray: 1.0.0\n                        - ...\n                    - ...\n                - `my-bundle-package$isarray@1.0.0/`\n                    - `package.json`\n                        - name: my-bundle-package$isarray\n                        - version: 1.0.0\n                        - ...\n                    - ...\n                - `my-bundle-package$isarray@2.0.0/`\n                    - `package.json`\n                        - name: my-bundle-package$isarray\n                        - version: 2.0.0\n                        - ...\n                    - ...\n\nThe packages inside `node_modules` are the same format as the npm tool and can \nbe copied (after a little processing for things like converting to AMD, for \nexample) from a standard `node_modules` folder. The `node_modules` folder can \nhold any number of npm packages (even different versions of the same package), \nor no npm packages at all.\n\nNow that you know the structure for OSGi bundles containing npm packages, you \ncan learn how the liferay-npm-bundler handles inline JavaScript packages. \n\n## Inline JavaScript packages\n\nThe resulting OSGi bundle that the liferay-npm-bundler creates lets you deploy \none inline JavaScript package (named `my-bundle-package` in the example) with \nseveral npm packages that are placed inside the `node_modules` folder, one \npackage per folder.  \n\nThe inline package is nested in the OSGi standard `META-INF/resources` folder \nand is defined by a standard npm `package.json` file.\n\nThe inline package is optional, but only one inline package is allowed per OSGi \nbundle. The inline package usually provides the JavaScript code for a widget, \nwhen the OSGi bundle contains one. Note that the architecture does not \ndifferentiate between inline and npm packages once they are published. The \ninline package is only used for organizational purposes. \n\nNow you know how the liferay-npm-bundler creates OSGi bundles for npm packages!\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/04-liferay-npm-bundler/06-how-portal-publishes-npm-packages.markdown",
    "content": "---\nheader-id: how-the-liferay-npm-bundler-publishes-npm-packages\n---\n\n# How the Liferay npm Bundler Publishes npm Packages\n\n[TOC levels=1-4]\n\nWhen you deploy an OSGi bundle with the specified structure, as explained in \n[The Structure of OSGi Bundles Containing NPM Packages](/docs/7-2/reference/-/knowledge_base/r/the-structure-of-osgi-bundles-containing-npm-packages) \nreference, its modules are made available for consumption through canonical \nURLs. To better illustrate resolved modules, the example structure below is the \nstandard structure that the liferay-npm-bundler 1.x generates, and therefore \ndoesn't have the namespaced packages that the 2.x version generates. Please \nrefer to the last sections of this article to know how liferay-npm-bundler 2.0 \noverrides this de-duplication mechanism to implement isolated dependencies and \nimports.\n\n- `my-bundle/`\n    - `META-INF/`\n        - `resources/`\n            - `package.json`\n                - name: my-bundle-package\n                - version: 1.0.0\n                - main: lib/index\n                - dependencies:\n                    - isarray: 2.0.0\n                    - isobject: 2.1.0\n                - ...\n            - `lib/`\n                - `index.js`\n                - ...\n            - ...\n            - `node_modules/`\n                - `isobject@2.1.0/`\n                    - `package.json`\n                        - name: isobject\n                        - version: 2.1.0\n                        - main: lib/index\n                        - dependencies:\n                            - isarray: 1.0.0\n                        - ...\n                    - ...\n                - `isarray@1.0.0/`\n                    - `package.json`\n                        - name: isarray\n                        - version: 1.0.0\n                        - ...\n                    - ...\n                - `isarray@2.0.0/`\n                    - `package.json`\n                        - name: isarray\n                        - version: 2.0.0\n                        - ...\n                    - ...\n\nIf you deploy the example OSGi bundle shown above, the following URLs are made \navailable (one for each module):\n\n- [http://localhost/o/js/module/598/my-bundle-package@1.0.0/lib/index.js](http://localhost/o/js/module/598/my-bundle-package@1.0.0/lib/index.js)\n\n- [http://localhost/o/js/module/598/isobject@2.1.0/index.js](http://localhost/o/js/module/598/isobject@2.1.0/index.js)\n\n- [http://localhost/o/js/module/598/isarray@1.0.0/index.js](http://localhost/o/js/module/598/isarray@1.0.0/index.js)\n\n- [http://localhost/o/js/module/598/isarray@2.0.0/index.js](http://localhost/o/js/module/598/isarray@2.0.0/index.js)\n\n| **NOTE:** The OSGi bundle ID (598) may vary.\n\nYou can learn about package de-duplication next.\n\n## Package De-duplication\n\nSince two or more OSGi modules may export multiple copies of the same package \nand version, Liferay Portal must de-duplicate such collisions. To accomplish \nde-duplication, a new concept called *resolved module* was created.\n\nA resolved module is the reference package exported to Liferay Portal's \nfront-end, when multiple copies of the same package and version exist. It's \nrandomly referenced from one of the several bundles exporting the same copies of \nthe package.\n\nUsing the example from the previous section, for each group of canonical URLs \nreferring to the same module inside different OSGi bundles, there's another \ncanonical URL for the resolved module. The example structure has the resolved \nmodule URLs shown below:\n\n- [http://localhost/o/js/resolved-module/my-bundle-package@1.0.0/lib/index.js](http://localhost/o/js/resolved-module/my-bundle-package@1.0.0/lib/index.js)\n\n- [http://localhost/o/js/resolved-module/my-bundle-package$isobject@2.1.0/index.js](http://localhost/o/js/resolved-module/my-bundle-package$isobject@2.1.0/index.js)\n\n- [http://localhost/o/js/resolved-module/my-bundle-package$isarray@1.0.0/index.js](http://localhost/o/js/resolved-module/my-bundle-package$isarray@1.0.0/index.js)\n\n- [http://localhost/o/js/resolved-module/my-bundle-package$isarray@2.0.0/index.js](http://localhost/o/js/resolved-module/my-bundle-package$isarray@2.0.0/index.js)\n\n| **NOTE:** The OSGi bundle ID (598 in the example) is removed and module is \n| replaced by `resolved-module`.\n\nNext you can learn how the bundler (since version 2.0.0) isolates package \ndependencies. See \n[What Changed Between liferay-npm-bundler 1.x and 2.x](/docs/7-2/reference/-/knowledge_base/r/what-changed-between-liferay-npm-bundler-1-x-and-2-x) \nfor more information on why this change was made. \n\n## Isolated Package Dependencies\n\nA typical OSGi bundle structure generated with liferay-npm-bundler 2.x is shown \nbelow:\n\n- `my-bundle/`\n    - `META-INF/`\n        - `resources/`\n            - `package.json`\n                - name: my-bundle-package\n                - version: 1.0.0\n                - main: lib/index\n                - dependencies:\n                    - my-bundle-package$isarray: 2.0.0\n                    - my-bundle-package$isobject: 2.1.0\n                - ...\n            - `lib/`\n                - `index.js`\n                - ...\n            - ...\n            - `node_modules/`\n                - `my-bundle-package$isobject@2.1.0/`\n                    - `package.json`\n                        - name: my-bundle-package$isobject\n                        - version: 2.1.0\n                        - main: lib/index\n                        - dependencies:\n                            - my-bundle-package$isarray: 1.0.0\n                        - ...\n                    - ...\n                - `my-bundle-package$isarray@1.0.0/`\n                    - `package.json`\n                        - name: my-bundle-package$isarray\n                        - version: 1.0.0\n                        - ...\n                    - ...\n                - `my-bundle-package$isarray@2.0.0/`\n                    - `package.json`\n                        - name: my-bundle-package$isarray\n                        - version: 2.0.0\n                        - ...\n                    - ...\n\nNote that each package dependency is namespaced with the bundle's name \n(`my-bundle-package$` in the example structure). This lets each project load its \nown dependencies and avoid potential collisions with projects that export the \nsame package. For example, consider the two widget projects below:\n\n- `my-widget`\n    - package.json\n        - dependencies:\n            - a-library 1.0.0\n            - a-helper 1.0.0\n    - node_modules\n        - a-library\n            - version: 1.0.0\n            - dependencies:\n                - a-helper ^1.0.0\n        - a-helper\n            - version: 1.0.0\n\n- `another-widget`\n    - package.json\n        - dependencies:\n            - a-library 1.0.0\n            - a-helper 1.2.0\n    - node_modules\n        - a-library\n            - version: 1.0.0\n            - dependencies:\n                - a-helper ^1.0.0\n        - a-helper\n            - version: 1.2.0\n\nIn this example, `a-library` depends on `a-helper` at version 1.0.0 or higher \n(note the caret ^ expression in the dependencies). The bundler implements \nisolated dependencies by prefixing the name of the bundle to the modules, so \nthat `my-widget` gets its `a-helper` at 1.0.0, while `another-widget` gets its \n`a-helper` at 1.2.0.\n \nThe dependencies isolation not only avoids collisions between bundles, but also \nmakes peer dependencies behave deterministically as each widget gets what it \nhad in its `node_modules` folder when it was developed. \n\nNow that you understand how namespacing modules isolates bundle dependencies, \navoiding collisions, you can learn about de-duplication next. \n\n## De-duplication through Importing\n\nIsolated dependencies are very useful, but there are times when sharing the same \npackage between modules would be more beneficial. To do this, the \nliferay-npm-bundler lets you import packages from an external OSGi bundle, \ninstead of using your own. This lets you put shared dependencies in one project \nand reference them from the rest. \n\nImagine that you have three widgets that compose the homepage of your site: \n`my-toolbar`, `my-menu`, and `my-content`. These widgets depend on the fake, \nbut awesome, Wonderful UI Components (WUI) framework. This quite limited \nframework is composed of only three packages:\n\n1.  `component-core`\n2.  `button`\n3.  `textfield`\n\nSince the bundler namespaces each dependency package with the widget's name by \ndefault, you would end up with three namespaced copies of the WUI package on the \npage. This is not what you want. Since they share the same package, instead you \ncan create a fourth bundle that contains the WUI package, and import the WUI \npackage in the three widgets. This results in the structure below:\n\n- `my-toolbar/`\n    - `.npmbundlerrc`\n        - config:\n            - imports:\n                - wui-provider:\n                    - component-core: ^1.0.0\n                    - button: ^1.0.0\n                    - textfield: ^1.0.0\n- `my-menu/`\n    - `.npmbundlerrc`\n        - config:\n            - imports:\n                - wui-provider:\n                    - component-core: ^1.0.0\n                    - button: ^1.0.0\n                    - textfield: ^1.0.0\n- `my-content/`\n    - `.npmbundlerrc`\n        - config:\n            - imports:\n                - wui-provider:\n                    - component-core: ^1.0.0\n                    - button: ^1.0.0\n                    - textfield: ^1.0.0\n- `wui-provider/`\n    - `.package.json`\n        - name: wui-provider\n        - dependencies: \n            - component-core: 1.0.0\n            - button: 1.0.0\n            - textfield: 1.0.0\n\nThe bundler switches the namespace of certain packages, thus pointing them to an \nexternal bundle. Say that you have the following code in `my-toolbar` widget:\n\n```javascript\nvar Button = require('button');\n```\n\nBy default, the bundler 2.x transforms this into the following when not imported \nfrom another bundle:\n\n```javascript\nvar Button = require('my-toolbar$button');\n```\n\nBut, because `button` is imported from `wui-provider`, it is instead changed to \nthe value below:\n\n```javascript\nvar Button = require('wui-provider$button');\n```\n\nAlso, a dependency on `wui-provider$button` at version `^1.0.0` is included in \n`my-toolbar`'s `package.json` file so that the loader finds the correct version. \nThat's all you need. Once `wui-provider$button` is required at runtime, it jumps \nto `wui-provider`'s context and loads the subdependencies from there on, even if \ncode is executed from `my-toolbar`. This works because, as you can imagine, \n`wui-provider`'s modules are namespaced too, and once you load a module from it, \nit keeps requiring `wui-provider$` prefixed modules all the way down. \n\nNext, you will learn possible strategies for importing.\n\n## Strategies When Importing Packages\n\nDe-duplication by importing is a powerful tool, but you must design a versioning \nstrategy suitable for you so that you don't run into errors. \n\nFirst of all, you must decide if you want to declare imported dependencies only \nin the `.npmbundlerrc` file or in the `package.json` too. Listing an imported \ndependency in `.npmbundlerrc` is enough, even if it isn't present in your \n`node_modules` folder because during runtime the loader will find it. Listing an \nimported dependency in `.npmbundlerrc` is enough, even if it isn't present in \nyour `node_modules` folder, because during runtime the loader finds it. If you \nhave previous experience with dynamic linking support in standard operating \nsystems, think of it as a DLL or shared object. \n\nYou may need to install your dependencies in `node_modules` too if you use them \nfor tests, or if they contain types needed to compile (like in Typescript), etc. \nIf that is the case, then you can place them in the `dependencies` or \n`devDependencies` section of your `package.json`. If you list them under the \nlatter, they are automatically excluded from the output bundle by the \nliferay-npm-bundler. Otherwise, you need to exclude them in the `.npmbundlerrc` \nfile so they don't redundantly appear in the output.\n\nIf you list dependencies both in `package.json` and `.npmbundlerrc`, decide how \nto keep versions in sync. The best advice is to use the same version constraints \nin both files, but you may decide not to do so if it is necessary. For example, \nimagine that you import one of your dependencies from another bundle during \nruntime to run tests. Say you are using version constraint ^1.5.1. It would be \ndesirable that if you have tested your code with a version >=1.5.1 and <2.0.0 \n(that's what ^1.5.1 means), you get a compatible version during runtime. Thus, \nyou would declare the dependency with ^1.5.1 in `.npmbundlerrc` too. \n\nHowever, there are times when you may want to be more lenient, and you may need \nto get a lower version (1.4.0 for example) during runtime, even if you are \ndeveloping against ^1.5.1. In that case, you can declare ^1.5.1 in your \n`package.json` and ^1.0.0 in `.npmbundlerrc`. \n\nIn the end, it's up to you to decide how you want to handle your dependencies:\n\n1. `package.json` (While developing)\n\n2. `.npmbundlerrc` (During runtime)\n\nwe recommend that you choose a versioning strategy and stick to it, to ensure \ndependencies are satisfied at runtime. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/04-liferay-npm-bundler/07-how-the-bundler-formats-js-modules.markdown",
    "content": "---\nheader-id: understanding-how-liferay-npm-bundler-formats-javascript-modules-for-amd\n---\n\n# Understanding How liferay-npm-bundler Formats JavaScript Modules for AMD\n\n[TOC levels=1-4]\n\nLiferay AMD Loader is based on the \n[AMD specification](https://github.com/amdjs/amdjs-api/wiki/AMD). \nAll modules inside an npm OSGi bundle must be in AMD format. This is done for \n[CommonJS](http://www.commonjs.org/) modules by wrapping the module code inside \na `define` call. The liferay-npm-bundler helps automate this process by wrapping \nthe module for you. This article references the OSGi structure below as an \nexample. You can learn more about this structure in \n[The Structure of OSGi Bundles Containing NPM Packages](/docs/7-2/reference/-/knowledge_base/r/the-structure-of-osgi-bundles-containing-npm-packages) \nreference.\n\n- `my-bundle/`\n    - `META-INF/`\n        - `resources/`\n            - `package.json`\n                - name: my-bundle-package\n                - version: 1.0.0\n                - main: lib/index\n                - dependencies:\n                    - my-bundle-package$isarray: 2.0.0\n                    - my-bundle-package$isobject: 2.1.0\n                - ...\n            - `lib/`\n                - `index.js`\n                - ...\n            - ...\n            - `node_modules/`\n                - `my-bundle-package$isobject@2.1.0/`\n                    - `package.json`\n                        - name: my-bundle-package$isobject\n                        - version: 2.1.0\n                        - main: lib/index\n                        - dependencies:\n                            - my-bundle-package$isarray: 1.0.0\n                        - ...\n                    - ...\n                - `my-bundle-package$isarray@1.0.0/`\n                    - `package.json`\n                        - name: my-bundle-package$isarray\n                        - version: 1.0.0\n                        - ...\n                    - ...\n                - `my-bundle-package$isarray@2.0.0/`\n                    - `package.json`\n                        - name: my-bundle-package$isarray\n                        - version: 2.0.0\n                        - ...\n                    - ...\n\nFor example, the `my-bundle-package$isobject@2.1.0` package's `index.js` file \ncontains the following code:\n\n```javascript\n'use strict';\n\nvar isArray = require('my-bundle-package$isarray');\n\nmodule.exports = function isObject(val) {\n    return val != null && typeof val === 'object' && isArray(val) === false;\n};\n```\n\nThe updated module code configured for AMD format is shown below:\n\n```javascript\ndefine(\n    'my-bundle-package$isobject@2.1.0/index', \n    ['module', 'require', 'my-bundle-package$isarray'], \n    function (module, require) {\n        'use strict';\n\n        var define = undefined;\n\n        var isArray = require('my-bundle-package$isarray');\n\n        module.exports = function isObject(val) {\n            return val != null && typeof val === 'object' \n            && isArray(val) === false;\n        };\n    }\n);\n```\n\n| **Note:** The module's name must be based on its package, version, and file \n| path (for example `my-bundle-package$isobject@2.1.0/index`), otherwise Liferay \n| AMD Loader can't find it. \n\nNote the module's dependencies: \n`['module', 'require', 'my-bundle-package$isarray']`.\n\n`module` and `require` must be used to get a reference to the `module.exports` \nobject and the local `require` function, as defined in the AMD specification. \n\nThe subsequent dependencies state the modules on which this module depends. Note \nthat `my-bundle-package$isarray` in the example is not a package but rather an \nalias of the `my-bundle-package$isarray` package's main module (thus, it is \nequivalent to `my-bundle-package$isarray/index`). \n\nAlso note that there is enough information in the `package.json` files to know \nthat `my-bundle-package$isarray` refers to `my-bundle-package$isarray/index`, \nbut also that it must be resolved to version `1.0.0` of such package, i.e., that \n`my-bundle-package$isarray/index` in this case refers to \n`my-bundle-package$isarray@1.0.0/index`. \n\nYou may also have noted the `var define = undefined;` addition to the top of the\nfile. This is introduced by `liferay-npm-bundler` to make the module think that\nit is inside a CommonJS environment (instead of an AMD one). This is because\nsome npm packages are written in UMD format and, because we are wrapping it\ninside our AMD `define()` call, we don't want them to execute their own\n`define()` but prefer them to take the CommonJS path, where the exports are done\nthrough the `module.exports` global.\n\nNow you have a better understanding of how liferay-npm-bundler formats \nJavaScript modules for AMD! \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/04-liferay-npm-bundler/08-how-liferay-amd-loader-configuration-is-exported.markdown",
    "content": "---\nheader-id: understanding-how-liferay-amd-loader-configuration-is-exported\n---\n\n# Understanding How Liferay AMD Loader Configuration is Exported\n\n[TOC levels=1-4]\n\n| **NOTE:** This article is for users who know how Liferay AMD Loader works \n| under the hood. See \n| [Liferay AMD Module Loader](/docs/7-2/frameworks/-/knowledge_base/f/loading-amd-modules-in-liferay) \n| for more information.\n\nWith [de-duplication](/docs/7-2/reference/-/knowledge_base/r/how-the-liferay-npm-bundler-publishes-npm-packages#package-de-duplication) \nin place, JavaScript modules are made available to Liferay AMD Loader through \nthe configuration returned by the `/o/js_loader_modules` URL.\n\nThe OSGi bundle shown below is used for reference in this article:\n\n- `my-bundle/`\n    - `META-INF/`\n        - `resources/`\n            - `package.json`\n                - name: my-bundle-package\n                - version: 1.0.0\n                - main: lib/index\n                - dependencies:\n                    - isarray: 2.0.0\n                    - isobject: 2.1.0\n                - ...\n            - `lib/`\n                - `index.js`\n                - ...\n            - ...\n            - `node_modules/`\n                - `isobject@2.1.0/`\n                    - `package.json`\n                        - name: isobject\n                        - version: 2.1.0\n                        - main: lib/index\n                        - dependencies:\n                            - isarray: 1.0.0\n                        - ...\n                    - ...\n                - `isarray@1.0.0/`\n                    - `package.json`\n                        - name: isarray\n                        - version: 1.0.0\n                        - ...\n                    - ...\n                - `isarray@2.0.0/`\n                    - `package.json`\n                        - name: isarray\n                        - version: 2.0.0\n                        - ...\n                    - ...\n\nFor example, for the specified structure (shown above), as explained in \n[The Structure of OSGi Bundles Containing npm Packages](/docs/7-2/reference/-/knowledge_base/r/the-structure-of-osgi-bundles-containing-npm-packages) \nreference, the following configuration is published for Liferay AMD loader to \nconsume:\n\n```javascript\nLiferay.PATHS = {\n  ...\n  'my-bundle-package@1.0.0/lib/index': '/o/js/resolved-module/my-bundle-package@1.0.0/lib/index',\n  'isobject@2.1.0/index': '/o/js/resolved-module/isobject@2.1.0/index',\n  'isarray@1.0.0/index': '/o/js/resolved-module/isarray@1.0.0/index',\n  'isarray@2.0.0/index': '/o/js/resolved-module/isarray@2.0.0/index',\n  ...\n}\nLiferay.MODULES = {\n  ...\n  \"my-bundle-package@1.0.0/lib/index.es\": {\n    \"dependencies\": [\"exports\", \"isarray\", \"isobject\"],\n    \"map\": {\n      \"isarray\": \"isarray@2.0.0\", \n      \"isobject\": \"isobject@2.1.0\"\n    }\n  },\n  \"isobject@2.1.0/index\": {\n    \"dependencies\": [\"module\", \"require\", \"isarray\"],\n    \"map\": {\n      \"isarray\": \"isarray@1.0.0\"\n    }\n  },\n  \"isarray@1.0.0/index\": {\n    \"dependencies\": [\"module\", \"require\"],\n    \"map\": {}\n  },\n  \"isarray@2.0.0/index\": {\n    \"dependencies\": [\"module\", \"require\"],\n    \"map\": {}\n  },\n  ...\n}\nLiferay.MAPS = {\n  ...\n  'my-bundle-package@1.0.0': { value: 'my-bundle-package@1.0.0/lib/index', exactMatch: true}\n  'isobject@2.1.0': { value: 'isobject@2.1.0/index', exactMatch: true},\n  'isarray@2.0.0': { value: 'isarray@2.0.0/index', exactMatch: true},\n  'isarray@1.0.0': { value: 'isarray@1.0.0/index', exactMatch: true},\n  ...\n}\n```\n\n\nNote:\n\n- The `Liferay.PATHS` property describes paths to the JavaScript module files.\n\n- The `Liferay.MODULES` property describes the dependency names and versions of \n  each module.\n\n- The `Liferay.MAPS` property describes the aliases of the package's main \n  modules.\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/04-liferay-npm-bundler/09-changes-between-bundler-1.x-and-2.x.markdown",
    "content": "---\nheader-id: what-changed-between-liferay-npm-bundler-1-x-and-2-x\n---\n\n# What Changed Between Liferay npm Bundler 1.x and 2.x\n\n[TOC levels=1-4]\n\nThis reference doc outlines the key changes between liferay-npm-bundler version \n1.x and 2.x.\n\n## Automatically Formatting Modules for AMD\n\nIn version series 1.x of the bundler it was the developer's responsibility to \nwrap project modules in an AMD `define()` call. However, since 2.x the bundler \ndoes it for you, so the only requisite is that the project's code is \ntranspiled/written for CommonJS modules model (the standard model for module \nhandling in Node.js, that uses `require()` calls to load modules).\n\n## Isolating Project Dependencies\n\nPackage names are prefixed with the bundle name since version 2.0.0 of the \nbundler, but were left intact in previous versions. This strategy is used to \nisolate packages from different bundles. You can still deploy bundler 1.x \npackages (without prefix), and they will still work as they did for previous \nversions of the bundler.\n\n## Improved Peer Dependency Support\n\nIn bundler 1.x, there was only one shared peer dependency package available \nbetween widgets. With isolated dependencies per widget, it's easy to honor \npeer dependencies perfectly. Peer dependencies can be resolved exactly as stated \nin projects because their names are prefixed with the project's name. This is \npossible because of the new  [liferay-npm-bundler-plugin-inject-peer-dependencies](https://github.com/liferay/liferay-npm-build-tools/tree/master/packages/liferay-npm-bundler-plugin-inject-peer-dependencies) \nplugin. It scans all JS modules for `require` calls. If the bundler finds a \nrequired package in the `main.js` file, but it is not declared in the \n`package.json`, it resolves it to the proper version that is found in the \n`node_modules` folder. The plugin then injects a new dependency in the output \n`package.json` for the required package.\n\nNote that injected dependency version constraints are the specific version \nnumber required, without caret or any other semantic version operator. This is \nto honor the exact peer dependency found in the project. Injecting more relaxed \nsemantic version expressions could lead to unstable results.\n\n## Manually De-duplicating Through Importing\n\nNamespacing means that each widget gets its own dependencies. Only using the \nbundler this way obtains the same functionality as standard bundlers like \nwebpack or Browserify, so you wouldn't need a specific tool like \nliferay-npm-bundler. Since @product@ is a widget based architecture, sharing \ndependencies among different widgets would be very beneficial.\n\nIn bundler 1.x that deduplication was made automatically, but there was no \ncontrol over it. However, with version 2.x, you may now import packages from an\nexternal OSGi bundle, instead of using your own. This lets you put shared \ndependencies in one project, and reference them from the rest. Though This new \nway of de-duplication is not automatic, it leads to full control \n(during build time) of how each package is resolved.\n\nNow that you understand what changed between version 1.x and 2.x of the \nliferay-npm-bundler, you can follow the steps in the \n[Migrating a liferay-npm-bundler Project from 1.x to 2.x](/docs/7-2/frameworks/-/knowledge_base/f/migrating-a-liferay-npm-bundler-project-from-1-x-to-2-x) \nto migrate your 1.x projects to 2.x. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/04-liferay-npm-bundler/10-understanding-loader-rules.markdown",
    "content": "---\nheader-id: understanding-liferay-npm-bundlers-loaders\n---\n\n# Understanding liferay-npm-bundler's Loaders\n\n[TOC levels=1-4]\n\nliferay-npm-bundler's mechanism is inspired by webpack. Like webpack, the \nliferay-npm-bundler processes files using a set of rules that include loaders \nthat transform a project's source files before producing the final output. \n\n| **Note:** While webpack creates a single JS bundle file, liferay-npm-bundler \n| targets an AMD loader, so webpack and liferay-npm-bundler loaders are not \n| compatible.\n\nLoaders are npm packages that export a function in their main module that \nreceives source files and returns modified files, and optionally new files, \nbased on the loader's configuration. For example, the [babel-loader](https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-babel-loader) \nreceives ES6+ JavaScript files, runs Babel on them, and returns transpiled ES5 \nfiles along with a generated source map. You can use this pattern to \n[create custom loaders](/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-loaders-for-the-liferay-npm-bundler). \nA few example loader functions are shown below:\n\n- Pass JavaScript files through Babel or TSC\n- Convert CSS files into JS modules that dynamically inject the CSS into the \n  HTML page\n- Process CSS files with SASS\n- Create tools that generate code based on [IDL](https://en.wikipedia.org/wiki/Interface_description_language) \n  files\n\nLoaders are configured via the project's `.npmbundlerrc` file. A loader's \nconfiguration is specified using two key options: `sources` \n(the folders that contain the sources files to process) and `rules` \n(the loaders, options---if applicable---and regular expressions that determine \nwhich files to process). See [Understanding the `.npmbundlerrc`'s Structure](/docs/7-2/reference/-/knowledge_base/r/understanding-the-npmbundlerrcs-structure#package-processing-options) \nfor more information on the configuration requirements and options. \n\nLoaders can be chained. Files are processed by the loaders in the order \nthey are listed in the `use` property. The files are passed to the first loader, \nprocessed, sent to the next loader, and so on, until the files are \nprocessed by the rules. You can run complex processes, such as converting \na SASS file into CSS with the sass-loader, and then convert it into a JavaScript \nmodule with the style-loader. Once the rules are applied, the \nliferay-npm-bundler continues with the pre, post, and babel phases of the \nbundler plugins. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/04-liferay-npm-bundler/11-default-bundler-loaders.markdown",
    "content": "---\nheader-id: default-liferay-npm-bundler-loaders\n---\n\n# Default liferay-npm-bundler Loaders\n\n[TOC levels=1-4]\n\nSeveral [loaders](/docs/7-2/frameworks/-/knowledge_base/f/understanding-liferay-npm-bundlers-loaders) \nare available for the liferay-npm-bundler by default:\n\n[`babel-loader`](https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-babel-loader): \nprocesses source files with [Babel](https://babeljs.io/). This avoids an extra \nbuild step before the bundler.\n\n[`copy-loader`](https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-copy-loader): \ncopies source files (static assets) to the output folder. \n\n[`css-loader`](https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-css-loader): \nconverts a CSS file into a JavaScript module that's inserted into the DOM once \nit's loaded.\n\n[`json-loader`](https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-json-loader): \ngenerates JavaScript modules that export the contents of a JSON file as an \nobject, so you can include JSON files with the `require()` call.\n\n[`sass-loader`](https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-sass-loader): \nruns `node-sass` or `sass` on source files. This lets you generate static CSS \nfiles. It can be chained before `style-loader`. \n\n[`style-loader`](https://github.com/liferay/liferay-js-toolkit/tree/master/packages/liferay-npm-bundler-loader-style-loader): \nconverts a CSS file into a JavaScript module that inserts the CSS contents into\nthe DOM once it's loaded. This lets you include CSS files with a `require()`\ncall.\n\nSee the [liferay-js-toolkit loaders showcase](https://github.com/izaera/liferay-js-toolkit-showcase/tree/loaders) \nfor an example use case of the liferay-npm-bundler's loaders. If the default \nloaders don't meet your requirements, you can follow the instructions in \n[Creating Custom Loaders for the Bundler](/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-loaders-for-the-liferay-npm-bundler) \nto create your own loaders. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/05-liferay-js-apis/01-liferay-js-apis-intro.markdown",
    "content": "---\nheader-id: liferay-javascript-apis\n---\n\n# Liferay JavaScript APIs\n\n[TOC levels=1-4]\n\nThe `Liferay` JavaScript object exposes methods, objects, and properties that \nyou can use to access @product@-specific information. This section contains \na comprehensive list of some of the most useful utilities you can find inside \nthe `Liferay` object. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/05-liferay-js-apis/02-theme-display.markdown",
    "content": "---\nheader-id: liferay-themedisplay\n---\n\n# Accessing ThemeDisplay Information\n\n[TOC levels=1-4]\n\nThe `Liferay` global JavaScript Object exposes useful methods, objects, and \nproperties, each containing a wealth of information, one of which is \n`ThemeDisplay`. If you have experience with Java development in @product@, you \nmay be familiar with ThemeDisplay. The JavaScript object exposes the same \ninformation as the ThemeDisplay Java Class. It gives you access to valuable \ninformation that you can use in your applications, such as the Portal instance, \nthe current user, the user's language, whether the user is signed in or being \nimpersonated, the file path to the theme's resources, and much more. \n\nThe `Liferay` global object is automatically available in @product@ at runtime. \nTo access the `ThemeDisplay` object, use the following dot notation in your app:\n\n```javascript\nLiferay.ThemeDisplay.method-name\n```\n\nThis reference describes some of the most commonly used `ThemeDisplay` methods \nfor retrieving IDs, file paths, and login information. An exhaustive list of all \nof the available methods is displayed in the table below:\n\n| Method | Type | Description |\n| --- | --- | --- |\n| getLayoutId | number | |\n| getLayoutRelativeURL | string | Returns the relative URL for the page |\n| getLayoutURL | string | |\n| getParentLayoutId | number | |\n| isControlPanel | boolean | |\n| isPrivateLayout | boolean | |\n| isVirtualLayout | boolean | |\n| getBCP47LanguageId | number | |\n| getCDNBaseURL | string | Returns the content delivery network (CDN) base URL, or the current portal URL if the CDN base URL is null |\n| getCDNDynamicResourcesHost | string | Returns the content delivery network (CDN) dynamic resources host, or the current portal URL if the CDN dynamic resources host is null |\n| getCDNHost | string |  |\n| getCompanyGroupId | number | |\n| getCompanyId | number | Returns the portal instance ID |\n| getDefaultLanguageId | number | |\n| getDoAsUserIdEncoded | string | |\n| getLanguageId | number | Returns the user's language ID |\n| getParentGroupId | number | |\n| getPathContext | string | |\n| getPathImage | string | Returns the relative path of the portlet's image directory |\n| getPathJavaScript | string | Returns the relative path of the directory containing the portlet's JavaScript source files |\n| getPathMain | string | Returns the path of the portal instance's main directory |\n| getPathThemeImages | string | Returns the path of the current theme's image directory |\n| getPathThemeRoot | string | Returns the relative path of the current theme's root directory |\n| getPlid | string | Returns the primary key of the page |\n| getPortalURL | string | Returns the portal instance's base URL |\n| getScopeGroupId | number | Returns the ID of the scoped or sub-scoped active group (e.g. site) |\n| getScopeGroupIdOrLiveGroupId | number | |\n| getSessionId | number | Returns the session ID, or a blank string if the session ID is not available to the application |\n| getSiteGroupId | number | |\n| getURLControlPanel | string | |\n| getURLHome | string | |\n| getUserId | number | Returns the ID of the user for which the current request is being handled |\n| getUserName | string | Returns the user's name |\n| isAddSessionIdToURL | boolean | |\n| isFreeformLayout | boolean | |\n| isImpersonated | boolean | Returns `true` if the current user is being impersonated. Authorized administrative users can [impersonate](/docs/7-2/user/-/knowledge_base/u/adding-editing-and-deleting-users#editing-users) act as another user to test that user's account |\n| isSignedIn | boolean | Returns `true` if the user is logged in to the portal |\n| isStateExclusive | boolean | |\n| isStateMaximized | boolean | |\n| isStatePopUp | boolean | |\n\nThe example configuration below alerts users with a standard message if they are \na guest or a personal greeting if they are signed in. This is a basic example, \nand perhaps a bit invasive, but it illustrates how you can create unique \nexperiences for each user with the `ThemeDisplay` APIs:\n\n```javascript\nif(Liferay.ThemeDisplay.isSignedIn()){\n    alert('Hello ' + Liferay.ThemeDisplay.getUserName() + '. Welcome Back.')\n}\nelse {\n    alert('Hello Guest.')\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/05-liferay-js-apis/03-working-with-urls-in-js.markdown",
    "content": "---\nheader-id: working-with-urls-in-javascript\n---\n\n# Working with URLs in JavaScript\n\n[TOC levels=1-4]\n\nThe `Liferay` global JavaScript Object exposes methods, objects, and properties \nthat access the portal context. Four of these are helpful when working with \nURLS: `authToken`, `currentURL`, `currentURLEncoded`, and `PortletURL`. If you \nhave experience with Java development in @product@, you may have worked with \nsome of these before. The `Liferay` global object is automatically available at \nruntime, so no additional dependencies are required. \n\n| **Note:** Since Liferay DXP SP1 and Liferay Portal CE 7.2 GA2, the \n| `Liferay.PortletURL` utilities are deprecated and have been replaced with \n| `Liferay.Util.PortletURL` utilities. We recommend that you use the updated \n| versions to ensure future compatibility. The examples below use the updated \n| utilities.\n\nThis covers how to use the `Liferay` global JavaScript object to manipulate \nURLs. A list of the available methods and properties appears in the tables shown \nbelow. Example configurations are shown below the tables. \n\n## Portlet URL Methods\n\n`Liferay.Util.PortletURL` Methods:\n\n| Method | Parameters | Returns |\n| --- | --- | --- |\n| `createPortletURL` | `basePortletURL`, `parameters` | A portlet URL as a [URL](https://url.spec.whatwg.org/#api) object |\n| `createActionURL` | `basePortletURL`, `parameters` | A portlet URL as a [URL](https://url.spec.whatwg.org/#api) object |\n| `createRenderURL` | `basePortletURL`, `parameters` | A portlet URL as a [URL](https://url.spec.whatwg.org/#api) object |\n| `createResourceURL` | `basePortletURL`, `parameters` | A portlet URL as a [URL](https://url.spec.whatwg.org/#api) object |\n\n## Liferay Util PortletURL\n\n`Liferay.Util.PortletURL` provides APIs for creating portlet URLs \n(`actionURL`, `renderURL`, and `resourceURL`) with JavaScript in your JSPs. \nBelow is an example configuration for a JSP:\n\n```markup\nvar basePortletURL = 'https://localhost:8080/group/control_panel/manage?p_p_id=com_liferay_roles_admin_web_portlet_RolesAdminPortlet';\n\nvar actionURL = Liferay.Util.PortletURL.createActionURL(\n  basePortletURL,\n  {\n    'javax.portlet.action': 'addUser',\n    foo: 'bar'\n  }  \n);\n\nconsole.log(actionURL.toString());\n// https://localhost:8080/group/control_panel/manage?p_p_id=com_liferay_roles_admin_web_portlet_RolesAdminPortlet&javax.portlet.action=addUser&com_liferay_roles_admin_web_portlet_RolesAdminPortlet_foo=bar&p_p_lifecycle=1\n```\nThe same API is available as a module for use in your JavaScript files. The ES6 \nexample below uses the `createActionURL` module:\n\n```javascript\nimport {createActionURL} from 'frontend-js-web';\n\nvar basePortletURL = 'https://localhost:8080/group/control_panel/manage?p_p_id=com_liferay_roles_admin_web_portlet_RolesAdminPortlet';\n\nvar actionURL = createActionURL(\n  basePortletURL,\n  {\n    'p_p_id': Liferay.PortletKeys.DOCUMENT_LIBRARY,\n    foo: 'bar'\n  }  \n);\n```\n\nSee the [Portlet URL Methods](#portlet-url-methods) section for more information \nabout the method used in the example above. \n\n## Liferay AuthToken\n\nThe `Liferay.authToken` property holds the current authentication token value as \na String. The `authToken` is used to validate permissions when you make calls to \nservices. To use the `authToken` in a URL, pass `Liferay.authToken` as the URL's \n`p_auth` parameter, as shown in the example below:\n\n```javascript\nimport {createActionURL} from 'frontend-js-web';\n\nvar basePortletURL = 'https://localhost:8080/group/control_panel/manage?p_p_id=com_liferay_roles_admin_web_portlet_RolesAdminPortlet';\n\nvar actionURL = createActionURL(\n  basePortletURL,\n  {\n    'p_auth': Liferay.authToken\n  }  \n);\n```\n\n## Liferay CurrentURL\n\nThe `Liferay.currentURL` property holds the path of the current URL from the \nserver root.\n\nFor example, if checked from `my.domain.com/es/web/guest/home`, the value is \n`/es/web/guest/home`, as shown below:\n\n```javascript\n// Inside my.domain.com/es/web/guest/home\nconsole.log(Liferay.currentURL); // \"/es/web/guest/home\"\n```\n\n## Liferay CurrentURLEncoded\n\nThe `Liferay.currentURLEncoded` property holds the path of the current URL, \nencoded in ASCII for safe transmission over the Internet, from the server root. \n\nFor example, if checked from `my.domain.com/es/web/guest/home`, the value is \n`%2Fes%2Fweb%2Fguest%2Fhome`, as shown below:\n\n```javascript\n// Inside my.domain.com/es/web/guest/home\nconsole.log(Liferay.currentURLEncoded); // \"%2Fes%2Fweb%2Fguest%2Fhome\"\n```\n\nNow you know how to manipulate URLs using methods within the `Liferay` global \nJavaScript object. \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/05-liferay-js-apis/04-javascript-utilities.markdown",
    "content": "---\nheader-id: javascript-utilities\n---\n\n# @product@ JavaScript Utilities\n\n[TOC levels=1-4]\n\nThis reference explains some of the utility methods and objects inside the \n`Liferay` global JavaScript object. \n\n## Retrieve Browser Information\n\nThe `Liferay.Browser` object contains \nmethods that expose the current user agent characteristics without the need of \naccessing and parsing the global `window.navigator` object. \n\nThe available methods for the `Liferay.Browser` object are listed in the table \nbelow:\n\n| Method | Return Type | Description |\n| --- | --- | --- |\n| acceptsGzip | boolean | Returns whether the browser accepts gzip file compression |\n| getMajorVersion | number | Returns the major version of the browser |\n| getRevision | number | Returns the revision version of the browser |\n| getVersion | number | Returns the major.minor version of the browser |\n| isAir | boolean | Returns whether the browser is Adobe AIR |\n| isChrome | boolean | Returns whether the browser is Chrome |\n| isFirefox | boolean | Returns whether the browser is Firefox |\n| isGecko | boolean | Returns whether the browser is Gecko |\n| isIe | boolean | Returns whether the browser is Internet Explorer |\n| isIphone | boolean | Returns whether the browser is on an Iphone |\n| isLinux | boolean | Returns whether the browser is being viewed on Linux |\n| isMac | boolean | Returns whether the browser is being viewed on Mac |\n| isMobile | boolean | Returns whether the browser is being viewed on a mobile device |\n| isMozilla | boolean | Returns whether the browser is Mozilla |\n| isOpera | boolean | Returns whether the browser is Opera |\n| isRtf | boolean | Returns whether the browser supports RTF |\n| isSafari | boolean | Returns whether the browser is Safari |\n| isSun | boolean | Returns whether the browser is being viewed on Sun OS |\n| isWebKit | boolean | Returns whether the browser is WebKit |\n| isWindows | boolean | Returns whether the browser is being viewed on Windows |\n\nBelow is an example configuration:\n\n```javascript  \nLiferay.Browser.isChrome(); //returns true in Chrome\n```\n\n## Format XML\n\nThe `Liferay.Util.formatXML` utility takes XML content, as a String, and returns \nit formatted. \n\nParameters:\n- `content`: The XML string to format\n- `options`: An optional configuration object `{}` that contains additional \n  parameters for formatting the XML \n\nThe default configuration contains these options:\n\n```javascript  \nconst DEFAULT_OPTIONS = {\n  newLine: NEW_LINE, //'\\r\\n'\n  tagIndent: TAG_INDENT //'\\t'\n};\n```\n\nBelow is an example configuration for a JSP that overwrites the default options:\n\n```javascript  \nvar options = {newLine: '\\n', tagIndent: ' '};\n\nvar input = `<?xml xlmns:a=\"http://www.w3.org/TR/html4/\" version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE note>\n<a:note>                         <a:to>Foo</a:to>\n<a:from>Bar</a:from><a:heading>FooBar</a:heading>\n<a:body>FooBarBaz!</a:body>\n                  </a:note>\n`;\n\nvar formattedXMLString = Liferay.Util.formatXML(input, options);\n\nconsole.log(formattedXMLString);\n/*results:\n<?xml xlmns:a=\"http://www.w3.org/TR/html4/\" version=\"1.0\" encoding=\"UTF-8\"?>\\n'\n<!DOCTYPE note>\\n'\n<a:note>\\n'\n<a:to>Foo</a:to>\\n'\n<a:from>Bar</a:from>\\n'\n<a:heading>FooBar</a:heading>\\n'\n<a:body>FooBarBaz!</a:body>\\n'\n</a:note>';\n*/\n```\n\n## Format Storage Size\n\nThe `Liferay.Util.formatStorage` utility takes a storage size number (in bytes) \nand returns it in the proper format (KB, MB, or GB) as a String. \n\nParameters:\n- `size`: The numerical value of the storage size in bytes\n- `options`: An optional configuration object `{}` that contains additional \n  parameters for formatting the storage size \n\nThe default configuration contains these options:\n\n```javascript  \nconst DEFAULT_OPTIONS = {\n  addSpaceBeforeSuffix: false,\n  decimalSeparator: '.',\n  denominator: 1024.0,\n  suffixGB: 'GB',\n  suffixKB: 'KB',\n  suffixMB: 'MB'\n};\n```\n\nBelow is an example configuration that overwrites some of the default options:\n\n```javascript  \nvar formattedSize = Liferay.Util.formatStorage(1048576, {\n  addSpaceBeforeSuffix: true,\n  decimalSeparator: ',',\n  suffixMB: 'megabytes'\n});\n\nconsole.log(formattedSize); //1,0 megabytes\n```\n\n## Store and Retrieve Session Form data\n\n`Liferay.Util.setSessionValue()`: Sets a key, value pair for the Store utility's \nfetch value. \n\nParameters:\n- `key`: The `formData` key (String)\n- `value`: The `formData` key's corresponding value (Object|String)\n\n`Liferay.Util.getSessionValue()`: Retrieves the Store utility's fetch value for \nthe given `key`.\n\nParameters:\n- `key`: The key (String) to fetch the value for.\n\nBelow is an example configuration for a JSP:\n\n```markup\nLiferay.Util.Session.set('state', 'open');\n\nLiferay.Util.Session.get('state').then(function(value) {\n  console.log(value); //open\n});\n```\n\nHere is an example configuration that uses ES6:\n\n```javascript  \nimport {getSessionValue, setSessionValue} from 'frontend-js-web';\n\nsetSessionValue('state', 'open');\n\ngetSessionValue('state').then(value =>{\n  console.log(value); //open\n});\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/05-liferay-js-apis/05-invoking-liferay-services.markdown",
    "content": "---\nheader-id: invoking-liferay-services\n---\n\n# Invoking Liferay Services\n\n[TOC levels=1-4]\n\n@product@ provides many web services out-of-the-box. To see a comprehensive list \nof the available web services, navigate to `http://localhost:8080/api/jsonws` \n(assuming your localhost is running on port 8080). \n\n<!--Uncomment once registering-json-web-services#mapping-and-naming-conventions article is available\nIf you've deployed your own \nService Builder-generated JSON web services, \nfollow these guidelines\nfor invoking them. These services are useful for creating single page \napplications and can even be used to create custom front-ends in @product@. \n-->\n\nThis reference covers how to invoke these web services using JavaScript. \n\n## Invoking Web Services via JavaScript\n\n@product-ver@ contains a global JavaScript object called `Liferay` that has many \nuseful utilities. One method is `Liferay.Service`, which invokes JSON web \nservices. \n\nThe `Liferay.Service` method takes four possible arguments:\n\n**service {string|object}:** Specify the service name or an object with the \nkeys as the service to call, and the value as the service configuration object. \n(Required) \n\n**data {object|node|string}:** Specify the data to send to the service. If the \nobject passed is the ID of a form or a form element, the form fields will be \nserialized and used as the data. \n\n**successCallback {function}:** A function to execute when the server returns a \nresponse. It receives a JSON object as its first parameter. \n\n**exceptionCallback {function}:** A function to execute when the response from \nthe server contains a service exception. It receives an exception message as \nits first parameter. \n\nOne of the benefits of using the `Liferay.Service` method versus using \na standard AJAX request is that it handles the authentication for you. \n\nBelow is an example configuration of the `Liferay.Service` method:\n\n```javascript\nLiferay.Service(\n        '/user/get-user-by-email-address',\n        {\n                companyId: Liferay.ThemeDisplay.getCompanyId(),\n                emailAddress: 'test@example.com'\n        },\n        function(obj) {\n                console.log(obj);\n        }\n);\n```\n\nThe example above retrieves information about a user by passing its `companyId` \nand `emailAddress`. The response data resembles the following JSON object:\n\n```json\n{\n        \"agreedToTermsOfUse\": true,\n        \"comments\": \"\",\n        \"companyId\": \"20116\",\n        \"contactId\": \"20157\",\n        \"createDate\": 1471990639779,\n        \"defaultUser\": false,\n        \"emailAddress\": \"test@example.com\",\n        \"emailAddressVerified\": true,\n        \"facebookId\": \"0\",\n        \"failedLoginAttempts\": 0,\n        \"firstName\": \"Test\",\n        \"googleUserId\": \"\",\n        \"graceLoginCount\": 0,\n        \"greeting\": \"Welcome Test Test!\",\n        \"jobTitle\": \"\",\n        \"languageId\": \"en_US\",\n        \"lastFailedLoginDate\": null,\n        \"lastLoginDate\": 1471996720765,\n        \"lastLoginIP\": \"127.0.0.1\",\n        \"lastName\": \"Test\",\n        \"ldapServerId\": \"-1\",\n        \"lockout\": false,\n        \"lockoutDate\": null,\n        \"loginDate\": 1472077523149,\n        \"loginIP\": \"127.0.0.1\",\n        \"middleName\": \"\",\n        \"modifiedDate\": 1472077523149,\n        \"mvccVersion\": \"7\",\n        \"openId\": \"\",\n        \"portraitId\": \"0\",\n        \"reminderQueryAnswer\": \"test\",\n        \"reminderQueryQuestion\": \"what-is-your-father's-middle-name\",\n        \"screenName\": \"test\",\n        \"status\": 0,\n        \"timeZoneId\": \"UTC\",\n        \"userId\": \"20156\",\n        \"uuid\": \"c641a7c9-5acb-aa68-b3ea-5575e1845d2f\"\n}\n```\n\nNow that you know how to send an individual request, you're ready to run batch \nrequests. \n\n## Batching Requests\n\nAnother way to invoke the `Liferay.Service` method is by passing an object with \nthe keys of the service to call and the value of the service configuration \nobject. \n\nBelow is an example configuration for a batch request:\n\n```javascript\nLiferay.Service(\n        {\n                '/user/get-user-by-email-address': {\n                        companyId: Liferay.ThemeDisplay.getCompanyId(),\n                        emailAddress: 'test@example.com'\n                }\n        },\n        function(obj) {\n                console.log(obj);\n        }\n);\n```\n\nYou can invoke multiple services with the same request by passing in an array of \nservice objects. Here's an example:\n\n```javascript\nLiferay.Service(\n        [\n                {\n                        '/user/get-user-by-email-address': {\n                                companyId: Liferay.ThemeDisplay.getCompanyId(),\n                                emailAddress: 'test@example.com'\n                        }\n                },\n                {\n                        '/role/get-user-roles': {\n                                userId: Liferay.ThemeDisplay.getUserId()\n                        }\n                }\n        ],\n        function(obj) {\n                // obj is now an array of response objects\n                // obj[0] == /user/get-user-by-email-address data\n                // obj[1] == /role/get-user-roles data\n\n                console.log(obj);\n        }\n);\n```\n\nNext you can learn how to nest your requests. \n\n## Nesting Requests\n\nNested service calls bind information from related objects together in a JSON \nobject. You can call other services in the same HTTP request and conveniently \nnest returned objects. \n\nYou can use variables to reference objects returned from service calls. Variable \nnames must start with a dollar sign (`$`). \n\nThe example in this section retrieves user data with `/user/get-user-by-id` and \nuses the `contactId` returned from that service to then invoke \n`/contact/get-contact` in the same request. \n\n| **Note:** You must flag parameters that take values from existing variables. To \n| flag a parameter, insert the `@` prefix before the parameter name. \n\nBelow is an example configuration that demonstrates these concepts:\n\n```javascript\nLiferay.Service(\n        {\n                \"$user = /user/get-user-by-id\": {\n                        \"userId\": Liferay.ThemeDisplay.getUserId(),\n                        \"$contact = /contact/get-contact\": {\n                                \"@contactId\": \"$user.contactId\"\n                        }\n                }\n        },\n        function(obj) {\n                console.log(obj);\n        }\n);\n```\n\nHere is what the response data would look like for the request above:\n\n```json\n{\n        \"agreedToTermsOfUse\": true,\n        \"comments\": \"\",\n        \"companyId\": \"20116\",\n        \"contactId\": \"20157\",\n        \"createDate\": 1471990639779,\n        \"defaultUser\": false,\n        \"emailAddress\": \"test@example.com\",\n        \"emailAddressVerified\": true,\n        \"facebookId\": \"0\",\n        \"failedLoginAttempts\": 0,\n        \"firstName\": \"Test\",\n        \"googleUserId\": \"\",\n        \"graceLoginCount\": 0,\n        \"greeting\": \"Welcome Test Test!\",\n        \"jobTitle\": \"\",\n        \"languageId\": \"en_US\",\n        \"lastFailedLoginDate\": null,\n        \"lastLoginDate\": 1472231639378,\n        \"lastLoginIP\": \"127.0.0.1\",\n        [...]\n        \"screenName\": \"test\",\n        \"status\": 0,\n        \"timeZoneId\": \"UTC\",\n        \"userId\": \"20156\",\n        \"uuid\": \"c641a7c9-5acb-aa68-b3ea-5575e1845d2f\",\n        \"contact\": {\n                \"accountId\": \"20118\",\n                \"birthday\": 0,\n                [...]\n                \"createDate\": 1471990639779,\n                \"emailAddress\": \"test@example.com\",\n                \"employeeNumber\": \"\",\n                \"employeeStatusId\": \"\",\n                \"facebookSn\": \"\",\n                \"firstName\": \"Test\",\n                \"lastName\": \"Test\",\n                \"male\": true,\n                \"middleName\": \"\",\n                \"modifiedDate\": 1471990639779,\n                [...]\n                \"userName\": \"\"\n        }\n}\n```\n \nNow that you know how to process requests, you can learn how to filter the \nresults. \n\n## Filtering Results\n\nIf you don't want all the properties returned by a service, you can define a \nwhitelist of properties. This returns only the specific properties you request \nin the object. \n\nBelow is an example of whitelisting properties:\n\n```javascript\nLiferay.Service(\n        {\n                '$user[emailAddress,firstName] = /user/get-user-by-id': {\n                        userId: Liferay.ThemeDisplay.getUserId()\n                }\n        },\n        function(obj) {\n                console.log(obj);\n        }\n);\n```\n\nTo specify whitelist properties, place the properties in square brackets \n(e.g., `[whiteList]`) immediately following the name of your variable. The \nexample above requests only the `emailAddress` and `firstName` of the user. \n\nBelow is the filtered response:\n\n```json\n{\n        \"firstName\": \"Test\",\n        \"emailAddress\": \"test@example.com\"\n}\n```\n\nNext you can learn how to populate the inner parameters of the request. \n \n## Inner Parameters\n\nWhen you pass in an object parameter, you'll often need to populate its inner \nparameters (i.e., fields). \n\nConsider a default parameter `serviceContext` of type `ServiceContext`. To make \nan appropriate call to JSON web services you might need to set `serviceContext` \nfields such as `scopeGroupId`, as shown below:\n\n```javascript\nLiferay.Service(\n        '/example/some-web-service',\n        {\n                serviceContext: {\n                        scopeGroupId: 123\n                }\n        },\n        function(obj) {\n                console.log(obj);\n        }\n);\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/05-liferay-js-apis/06-handling-ajax-requests.markdown",
    "content": "---\nheader-id: handling-ajax-requests-with-fetch\n---\n\n# Handling AJAX Requests with `Liferay.Util.fetch`\n\n[TOC levels=1-4]\n\nWhen you make Ajax requests (referred to as Service Resource actions/requests in \n@product@), they must protect against [CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery)\nand include the proper credentials. Since Liferay DXP 7.2 SP1 and Liferay \nCE Portal 7.2 GA2, @product@ provides a `Liferay.Util.fetch` utility \nbased on the standard [`fetch`](https://fetch.spec.whatwg.org/) API that you \ncan use to make AJAX requests. It includes these key features:\n\n- A thin wrapper on ES6 [`fetch`](https://fetch.spec.whatwg.org/) that shares \n  the same API\n- Sets `credentials:include` to each request\n- Sets `x-csrf-token` header to each request\n- Requires no dependencies\n\nBelow is an example configuration in ES6:\n\n```JavaScript\nimport {fetch} from 'frontend-js-web';\n\nfetch(url, {\n  body: new FormData(form),\n  method: 'POST'\n})\n  .then(response => response.json())\n  .then(response => processData(response))\n  .then(response => failureCallback(error));\n```\n\nExample use case in JSPs:\n\n```JavaScript\nLiferay.Util.fetch(url, {\n  body: new FormData(form),\n  method: 'POST'\n}).then(function(response) {\n  return response.json();\n}).then(function(response) {\n  message.innerHTML = response.message;\n}).catch(function() {\n  failureCallback();\n});\n```\n\n| **NOTE:** global access through `Liferay.Util` is only meant for use in JSP \n| code. In ES6, you must use the `fetch` module, as shown in the JavaScript \n| example above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/05-liferay-js-apis/07-working-with-addresses.markdown",
    "content": "---\nheader-id: working-with-addresses\n---\n\n# Working with Addresses\n\n[TOC levels=1-4]\n\nThe `Liferay` global JavaScript Object exposes methods, objects, and properties \nthat access the portal context. The `Liferay.Address` utility contains methods \nfor retrieving information about the addresses country and region. The `Liferay` \nglobal object is automatically available at runtime, so no additional \ndependencies are required. \n\nThe available methods are listed below, along with an example configuration. \n\n`Liferay.Address.getCountries(callback)`: returns an Array of the available \ncountries.\n\nParameters:\n- `callback`: A callback function to post-process the Array of countries\n\nThe example below prints the list of available regions for the selected country \n(the United States in this case) in a table:\n\n```javascript\nLiferay.Address.getCountries(function(e){console.table(e)}, 19);\n```\n\n`Liferay.Address.getRegions(callback, selectKey)`: returns an Array of the \navailable regions, by country, for the specified region ID.\n\nParameters:\n- `callback`: A callback function to post-process the Array of regions\n- `selectKey`: The selected region ID\n\nThe example below prints the list of available countries in a table to the \nconsole:\n\n```javascript\nLiferay.Address.getCountries(function(e){console.table(e)});\n```\n\nThis example uses an AUI `DynamicSelect` module to create a pair of select \nfields in a JSP. The first field retrieves the countries with the \n`Liferay.Address.getCountries()` method, and the second select field is \ndynamically populated with the selected country's available regions with the \n`Liferay.Address.getRegions()` method:\n\n```markup\n<aui:script use=\"liferay-dynamic-select\">\n  new Liferay.DynamicSelect(\n    [\n      {\n        select: '<portlet:namespace />countryId',\n        selectData: Liferay.Address.getCountries,\n        selectDesc: 'nameCurrentValue',\n        selectId: 'countryId',\n        selectSort: '<%= true %>',\n        selectVal: '<%= countryId %>'\n      },\n      {\n        select: '<portlet:namespace />regionId',\n        selectData: Liferay.Address.getRegions,\n        selectDesc: 'name',\n        selectDisableOnEmpty: true,\n        selectId: 'regionId',\n        selectVal: '<%= regionId %>'\n      }\n    ]\n  );\n</aui:script>\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/06-freemarker-taglib-macros/01-freemarker-taglib-macros-intro.markdown",
    "content": "---\nheader-id: freemarker-taglib-macros\n---\n\n# FreeMarker Taglib Macros\n\n[TOC levels=1-4]\n\n@product@'s taglibs are mapped to FreeMarker macros, so you can use them in your \nFreeMarker templates. See the \n[Taglib reference](/docs/7-2/reference/-/knowledge_base/r/front-end-taglibs) \nfor more information on using each taglib in your theme templates. The taglib \nmacros are defined in `taglib-mappings.properties` files. For convenience, these \nmacros are listed in the table below:\n\n<table class=\"table table-striped table-bordered\">\n<thead>\n<tr>\n<th>Macro</th>\n<th>Taglib</th>\n<th>TLD</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>`liferay_aui`</td>\n<td>liferay-aui</td>\n<td>liferay-aui.tld</td>\n</tr>\n<tr>\n<td>`liferay_portlet`</td>\n<td>liferay-portlet</td>\n<td>liferay-portlet-ext.tld</td>\n</tr>\n<tr>\n<td>`liferay_security`</td>\n<td>liferay-security</td>\n<td>liferay-security.tld</td>\n</tr>\n<tr>\n<td>`liferay_theme`</td>\n<td>liferay-theme</td>\n<td>liferay-theme.tld</td>\n</tr>\n<tr>\n<td>`liferay_ui`</td>\n<td>liferay-ui</td>\n<td>liferay-ui.tld</td>\n</tr>\n<tr>\n<td>`liferay_util`</td>\n<td>liferay-util</td>\n<td>liferay-util.tld</td>\n</tr>\n<tr>\n<td>`portlet`</td>\n<td>portlet</td>\n<td>liferay-portlet.tld</td>\n</tr>\n<tr>\n<td>`liferay_frontend`</td>\n<td>liferay-frontend</td>\n<td>liferay-frontend.tld</td>\n</tr>\n<tr>\n<td>`clay`</td>\n<td>clay</td>\n<td>liferay-clay.tld</td>\n</tr>\n<tr>\n<td>`liferay_map`</td>\n<td>liferay-map</td>\n<td>liferay-map.tld</td>\n</tr>\n<tr>\n<td>`liferay_rss`</td>\n<td>liferay-rss</td>\n<td>liferay-rss.tld</td>\n</tr>\n<tr>\n<td>`liferay_flags`</td>\n<td>liferay-flags</td>\n<td>liferay-flags.tld</td>\n</tr>\n<tr>\n<td>`liferay_expando`</td>\n<td>liferay-expando</td>\n<td>liferay-expando.tld</td>\n</tr>\n<tr>\n<td>`liferay_journal`</td>\n<td>liferay-journal</td>\n<td>liferay-journal.tld</td>\n</tr>\n<tr>\n<td>`liferay_social_bookmarks`</td>\n<td>liferay-social-bookmarks</td>\n<td>liferay-social-bookmarks.tld</td>\n</tr>\n<tr>\n<td>`liferay_site`</td>\n<td>liferay-site</td>\n<td>liferay-site.tld</td>\n</tr>\n<tr>\n<td>`liferay_comment`</td>\n<td>liferay-comment</td>\n<td>liferay-comment.tld</td>\n</tr>\n<tr>\n<td>`liferay_social_activities`</td>\n<td>liferay-social-activities</td>\n<td>liferay-social-activities.tld</td>\n</tr>\n<tr>\n<td>`liferay_asset`</td>\n<td>liferay-asset</td>\n<td>liferay-asset.tld</td>\n</tr>\n<tr>\n<td>`liferay_trash`</td>\n<td>liferay-trash</td>\n<td>liferay-trash.tld</td>\n</tr>\n<tr>\n<td>`liferay_item_selector`</td>\n<td>liferay-item-selector</td>\n<td>liferay-item-selector.tld</td>\n</tr>\n<tr>\n<td>`liferay_layout`</td>\n<td>liferay-layout</td>\n<td>liferay-layout.tld</td>\n</tr>\n<tr>\n<td>`liferay_editor`</td>\n<td>liferay-editor</td>\n<td>liferay-editor.tld</td>\n</tr>\n<tr>\n<td>`liferay-fragment`</td>\n<td>liferay-fragment</td>\n<td>liferay-fragment.tld</td>\n</tr>\n<tr>\n<td>`liferay_reading_time`</td>\n<td>liferay-reading-time</td>\n<td>liferay-reading-time.tld</td>\n</tr>\n<tr>\n<td>`liferay_sharing`</td>\n<td>liferay-sharing</td>\n<td>liferay-sharing.tld</td>\n</tr>\n<tr>\n<td>`liferay_site_navigation`</td>\n<td>liferay-site-navigation</td>\n<td>liferay-site-navigation.tld</td>\n</tr>\n<tr>\n<td>`adaptive_media_image`</td>\n<td>liferay-adaptive-media</td>\n<td>liferay-adaptive-media.tld</td>\n</tr>\n<tr>\n<td>`liferay_product_navigation`</td>\n<td>liferay-product-navigation</td>\n<td>liferay-product-navigation.tld</td>\n</tr>\n</tbody>\n</table>\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/07-npm-environment/01-setting-up-your-npm-environment-intro.markdown",
    "content": "---\nheader-id: setting-up-your-npm-environment\n---\n\n# Setting up Your npm Environment\n\n[TOC levels=1-4]\n\nIf you're using npm for development in @product@, you should set up your npm \nenvironment to avoid potential permissions issues. Follow these steps to \nconfigure your npm environment:\n\n1.  Create an `.npmrc` file in your user's home directory. This helps bypass npm \n    permission-related issues. \n\n2.  In the `.npmrc` file, specify a `prefix` property based on your user's home \n    directory, like the one shown below. This value specifies where to install \n    global npm packages:\n\n    ```bash\n    prefix=/Users/[username]/.npm-packages\n    ```\n\n3.  Set the `NPM_PACKAGES` system environment variable to the `prefix` value you \n    just specified:\n\n    ```bash\n    NPM_PACKAGES=/Users/[username]/.npm-packages (same as prefix value)\n    ```\n\n4.  Since npm installs Yeoman and gulp executables to `${NPM_PACKAGES}/bin` on \n    UNIX and to `%NPM_PACKAGES%` on Windows, make sure to add the appropriate \n    directory to your system path. For example, on UNIX you'd set this:\n\n    ```bash\n    PATH=${PATH}:${NPM_PACKAGES}/bin\n    ```\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/08-sitemap-page-configuration/01-sitemap-pages-configuraiton-options-intro.markdown",
    "content": "---\nheader-id: sitemap-page-configuration-options\n---\n\n# Sitemap Page Configuration Options\n\n[TOC levels=1-4]\n\nIf you're importing resources with your themes, you must define the pages for \nthe site in the theme's `sitemap.json`. Below is the full list of available \nconfiguration options for pages in the theme's `sitemap.json`:\n\n**colorSchemeId:** Specifies a different color scheme (by ID) than the default\ncolor scheme to use for the page.\n\n**columns:** Specifies the column contents for the page.\n\n**friendlyURL:** Sets the page's friendly URL.\n\n**hidden:** Sets whether the page is hidden.\n\n**layoutCss:** Sets custom CSS for the page to load after the theme.\n\n**layoutPrototypeLinkEnabled:** Sets whether the page inherits changes made to \nthe page template (if the page has one).\n\n**layoutPrototypeName:** Specifies the page template (by name) to use for the \npage. If this is defined, the page template's UUID is retrieved using the\nname, and `layoutPrototypeUuid` is not required. \n\n**layoutPrototypeUuid:** Specifies the page template (by UUID) to use for the \npage. If `layoutPrototypeName` is defined, this is not required. \n\n**layouts:** Specifies child pages for a page set.\n\n**name:** The page's name.\n\n**nameMap:** Passes a name object with multiple name key/value pairs. You can \nuse this to pass translations for a page's title, as shown in the example above. \n\n**privatePages:** Specifies private pages.\n\n**publicPages:** Specifies public pages.\n\n**themeId:** Specifies a different theme (by ID) than the default theme bundled\nwith the `sitemap.json` to use for the page.\n\n**title:** The page's title.\n\n**type:** Sets the page type. The default value is `portlet` (empty page). \nPossible values are `copy` (copy of a page of this site), `embedded`, \n`full_page_application`, `link_to_layout`, `node` (page set), `panel`, \n`portlet`, and `url` (link to URL).\n\n**typeSettings:** Specifies settings (using key/value pairs) for the page \n`type`.\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/09-ckeditor-plugins/01-ckeditor-plugin-reference-guide-intro.markdown",
    "content": "---\nheader-id: ckeditor-plugin-reference-guide\n---\n\n# CKEditor Plugin Reference Guide\n\n[TOC levels=1-4]\n\nThis reference guide provides a list of the default CKEditor plugins bundled \nwith @product@'s AlloyEditor. You can \n[use these existing CKEditor plugins in your custom AlloyEditor configurations](/docs/7-2/frameworks/-/knowledge_base/f/adding-buttons-to-alloyeditor-toolbars). \nEach plugin below links to its `plugin.js` file for reference, specifying the \nplugin's name and buttons if applicable:\n\n- [about](https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/about/plugin.js)\n- [allyhelp](https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/a11yhelp/plugin.js)\n- [allyhelpbtn](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/a11yhelpbtn/plugin.js)\n- [ajaxsave](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/ajaxsave/plugin.js)\n- [autocomplete](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/autocomplete/plugin.js)\n- [basicstyles](https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/basicstyles/plugin.js)\n- [bbcode](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/bbcode/plugin.js)\n- [bidi](https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/bidi/plugin.js)\n- [blockquote](https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/blockquote/plugin.js)\n- [clipboard](https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/clipboard/plugin.js)\n- [colorbutton](https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/colorbutton/plugin.js)\n- [colordialog](https://github.com/ckeditor/ckeditor-dev/tree/master/plugins/colordialog/plugin.js)\n- [contextmenu](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/contextmenu/plugin.js)\n- [creole](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/creole/plugin.js)\n- [dialogadvtab](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/dialogadvtab/plugin.js)\n- [div](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/div/plugin.js)\n- [elementspath](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/elementspath/plugin.js)\n- [enterkey](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/enterkey/plugin.js)\n- [entities](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/entities/plugin.js)\n- [filebrowse](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/filebrowser/plugin.js)\n- [find](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/find/plugin.js)\n- [flash](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/flash/plugin.js)\n- [floatingspace](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/floatingspace/plugin.js)\n- [font](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/font/plugin.js)\n- [format](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/format/plugin.js)\n- [forms](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/forms/plugin.js)\n- [horizontalrule](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/horizontalrule/plugin.js)\n- [htmlwriter](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/htmlwriter/plugin.js)\n- [image](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/image/plugin.js)\n- [iframe](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/iframe/plugin.js)\n- [indent](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/indent/plugin.js)\n- [itemselector](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/itemselector/plugin.js)\n- [justify](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/justify/plugin.js)\n- [link](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/link/plugin.js)\n- [list](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/list/plugin.js)\n- [liststyle](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/liststyle/plugin.js)\n- [lfrpopup](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/lfrpopup/plugin.js)\n- [magicline](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/magicline/plugin.js)\n- [media](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/media/plugin.js)\n- [newpage](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/newpage/plugin.js)\n- [pagebreak](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/pagebreak/plugin.js)\n- [pastefromword](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/pastefromword/plugin.js)\n- [pastetext](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/pastetext/plugin.js)\n- [preview](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/preview/plugin.js)\n- [removeformat](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/removeformat/plugin.js)\n- [resize](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/resize/plugin.js)\n- [restore](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/restore/plugin.js)\n- [selectall](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/selectall/plugin.js)\n- [showblocks](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/showblocks/plugin.js)\n- [showborders](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/showborders/plugin.js)\n- [smiley](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/smiley/plugin.js)\n- [sourcearea](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/sourcearea/plugin.js)\n- [specialchar](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/specialchar/plugin.js)\n- [stylescombo](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/stylescombo/plugin.js)\n- [tab](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/tab/plugin.js)\n- [table](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/table/plugin.js)\n- [tabletools](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/tabletools/plugin.js)\n- [templates](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/templates/plugin.js)\n- [toolbar](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/toolbar/plugin.js)\n- [undo](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/undo/plugin.js)\n- [wikilink](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-editor/frontend-editor-ckeditor-web/src/main/resources/META-INF/resources/_diffs/plugins/wikilink/plugin.js)\n- [wysiwygarea](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/wysiwygarea/plugin.js)\n\n| **Note:** The following CKEditor plugins are not available for inline mode in \n| AlloyEditor at this time, but you can still use them in the classic CKEditor:\n| \n| - [maximize](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/maximize/plugin.js)\n| - [print](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/print/plugin.js)\n| - [save](https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/save/plugin.js)\n| \n| To use the Classic CKEditor instead of AlloyEditor, there are a few\n| properties to set, depending on the portlet. Add the\n| [properties](https://github.com/liferay/liferay-portal/blob/7.2.x/portal-impl/src/portal.properties#L5432-L5441)\n| that you need to your `portal-ext.properties` file:\n| \n|     editor.wysiwyg.default=ckeditor\n|     editor.wysiwyg.portal-impl.portlet.ddm.text_html.ftl=ckeditor\n|     editor.wysiwyg.portal-web.docroot.html.portlet.announcements.edit_entry.jsp=ckeditor\n|     editor.wysiwyg.portal-web.docroot.html.portlet.blogs.edit_entry.jsp=ckeditor\n|     editor.wysiwyg.portal-web.docroot.html.portlet.mail.edit.jsp=ckeditor\n|     editor.wysiwyg.portal-web.docroot.html.portlet.mail.edit_message.jsp=ckeditor\n|     editor.wysiwyg.portal-web.docroot.html.portlet.message_boards.edit_message.html.jsp=ckeditor\n|     editor.wysiwyg.portal-web.docroot.html.taglib.ui.discussion.jsp=ckeditor\n|     editor.wysiwyg.portal-web.docroot.html.taglib.ui.email_notification_settings.jsp=ckeditor\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/11-portlet-ids/01-fully-qualified-portlet-ids-intro.markdown",
    "content": "---\nheader-id: fully-qualified-portlet-ids\n---\n\n# Fully Qualified Portlet IDs\n\n[TOC levels=1-4]\n\nBelow is a listing of the portlet IDs for the default portlets in @product@. You \ncan use these IDs to embed portlets in your theme's \n[sitemap](/docs/7-2/frameworks/-/knowledge_base/f/defining-portlets-in-a-sitemap). \n\n**Collaboration**\n\n| Portlet | ID |\n| --- | --- |\n| Blogs | `com_liferay_blogs_web_portlet_BlogsPortlet` |\n| Blogs Aggregator | `com_liferay_blogs_web_portlet_BlogsAgreggatorPortlet` |\n| Calendar | `com_liferay_calendar_web_portlet_CalendarPortlet` |\n| Dynamic Data Lists Display | `com_liferay_dynamic_data_lists_web_portlet_DDLDisplayPortlet` |\n| Form | `com_liferay_dynamic_data_mapping_form_web_portlet_DDMFormPortlet` |\n| Invite Members | `com_liferay_invitation_invite_members_web_portlet_InviteMembersPortlet` |\n| Message Boards | `com_liferay_message_boards_web_portlet_MBPortlet` |\n| Recent Bloggers | `com_liferay_blogs_recent_bloggers_web_portlet_RecentBloggersPortlet` |\n\n**Community**\n\n| Portlet | ID |\n| --- | --- |\n| My Sites | `com_liferay_site_my_sites_web_portlet_MySitesPortlet` |\n| Page Comments | `com_liferay_comment_page_comments_web_portlet_PageCommentsPortlet` |\n| Page Flags | `com_liferay_flags_web_portlet_PageFlagsPortlet` |\n| Page Ratings | `com_liferay_ratings_page_ratings_web_portlet_PageRatingsPortlet` |\n\n**Content Management**\n\n| Portlet | ID |\n| --- | --- |\n| Asset Publisher | `com_liferay_asset_publisher_web_portlet_AssetPublisherPortlet` |\n| Breadcrumb | `com_liferay_site_navigation_breadcrumb_web_portlet_SiteNavigationBreadcrumbPortlet` |\n| Categories Navigation | `com_liferay_asset_categories_navigation_web_portlet_AssetCategoriesNavigationPortlet` |\n| Documents and Media | `com_liferay_document_library_web_portlet_DLPortlet` |\n| Highest Rated Assets | `com_liferay_asset_publisher_web_portlet_HighestRatedAssetsPortlet` |\n| Knowledge Base Article | `com_liferay_knowledge_base_web_portlet_ArticlePortlet` |\n| Knowledge Base Display | `com_liferay_knowledge_base_web_portlet_DisplayPortlet` |\n| Knowledge Base Search | `com_liferay_knowledge_base_web_portlet_SearchPortlet` |\n| Knowledge Base Section | `com_liferay_knowledge_base_web_portlet_SectionPortlet` |\n| Media Gallery | `com_liferay_document_library_web_portlet_IGDisplayPortlet` |\n| Most Viewed Assets | `com_liferay_asset_publisher_web_portlet_MostViewedAssetsPortlet` |\n| Navigation Menu | `com_liferay_site_navigation_menu_web_portlet_SiteNavigationMenuPortlet` |\n| Nested Applications | `com_liferay_nested_portlets_web_portlet_NestedPortletsPortlet` |\n| Polls Display Portlet | `com_liferay_polls_web_portlet_PollsDisplayPortlet` |\n| Related Assets | `com_liferay_asset_publisher_web_portlet_RelatedAssetsPortlet` |\n| Site Map | `com_liferay_site_navigation_site_map_web_portlet_SiteNavigationSiteMapPortlet` |\n| Sites Directory | `com_liferay_site_navigation_directory_web_portlet_SitesDirectoryPortlet` |\n| Tag Cloud | `com_liferay_asset_tags_navigation_web_portlet_AssetTagsCloudPortlet` |\n| Tags Navigation | `com_liferay_asset_tags_navigation_web_portlet_AssetTagsNavigationPortlet` |\n| Web Content Display | `com_liferay_journal_content_web_portlet_JournalContentPortlet` |\n\n**News**\n\n| Portlet | ID |\n| --- | --- |\n| Alerts | `com_liferay_announcements_web_portlet_AlertsPortlet` |\n| Announcements | `com_liferay_announcements_web_portlet_AnnouncementsPortlet` |\n| Recent Content Portlet | `com_liferay_asset_publisher_web_portlet_RecentContentPortlet` |\n\n**Sample**\n\n| Portlet | ID |\n| --- | --- |\n| Hello World | `com_liferay_hello_world_web_portlet_HelloWorldPortlet` |\n| IFrame | `com_liferay_iframe_web_portlet_IFramePortlet` |\n\n**Search**\n\n| Portlet | ID |\n| --- | --- |\n| Category Facet | `com_liferay_portal_search_web_category_facet_portlet_CategoryFacetPortlet` |\n| Custom Facet | `com_liferay_portal_search_web_custom_facet_portlet_CustomFacetPortlet` |\n| Folder Facet | `com_liferay_portal_search_web_folder_facet_portlet_FolderFacetPortlet` |\n| Modified Facet | `com_liferay_portal_search_web_modified_facet_portlet_ModifiedFacetPortlet` |\n| Search Bar | `com_liferay_portal_search_web_search_bar_portlet_SearchBarPortlet` |\n| Search Insights | `com_liferay_portal_search_web_search_insights_portlet_SearchInsightsPortlet` |\n| Search Options | `com_liferay_portal_search_web_search_options_portlet_SearchOptionsPortlet` |\n| Search Results | `com_liferay_portal_search_web_search_results_portlet_SearchResultsPortlet` |\n| Site Facet | `com_liferay_portal_search_web_site_facet_portlet_SiteFacetPortlet` |\n| Suggestions | `com_liferay_portal_search_web_suggestions_portlet_SuggestionsPortlet` |\n| Tag Facet | `com_liferay_portal_search_web_tag_facet_portlet_TagFacetPortlet` |\n| Type Facet | `com_liferay_portal_search_web_type_facet_portlet_TypeFacetPortlet` |\n| User Facet | `com_liferay_portal_search_web_user_facet_portlet_UserFacetPortlet` |\n\n**Social**\n\n| Portlet | ID |\n| --- | --- |\n| Activities | `com_liferay_social_activities_web_portlet_SocialActivitiesPortlet` |\n| Contacts Center | `com_liferay_contacts_web_portlet_ContactsCenterPortlet` |\n| Members | `com_liferay_social_networking_web_members_portlet_MembersPortlet` |\n| My Contacts | `com_liferay_contacts_web_portlet_MyContactsPortlet` |\n| Profile | `com_liferay_contacts_web_portlet_ProfilePortlet` |\n\n**Tools**\n\n| Portlet | ID |\n| --- | --- |\n| Language Selector | `com_liferay_site_navigation_language_web_portlet_SiteNavigationLanguagePortlet` |\n| Search | `com_liferay_portal_search_web_portlet_SearchPortlet` |\n| Sign In | `com_liferay_login_web_portlet_LoginPortlet` |\n\n**Wiki**\n\n| Portlet | ID |\n| --- | --- |\n| Page Menu | `com_liferay_wiki_navigation_web_portlet_WikiNavigationPageMenuPortlet` |\n| Tree Menu | `com_liferay_wiki_navigation_web_portlet_WikiNavigationTreeMenuPortlet` |\n| Wiki | `com_liferay_wiki_web_portlet_WikiPortlet` |\n| Wiki Display | `com_liferay_wiki_web_portlet_WikiDisplayPortlet` |\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/12-spa-lifecycle-events/01-spa-lifecycle-events-intro.markdown",
    "content": "---\nheader-id: available-spa-lifecycle-events\n---\n\n# Available SPA Lifecycle Events\n\n[TOC levels=1-4]\n\nDuring development, you may need to know when navigation has started or stopped \nin your SPA. SennaJS makes this easy by exposing lifecycle events that \nrepresent state changes in the application. The available lifecycle events are \nlisted in the table below:\n\n| Event | Description | Ex payload |\n| --- | --- | --- |\n| `beforeNavigate` | Fires before navigation starts. This event passes a JSON object with the path to the content you are navigating to and whether to update the history. | `{ path: '/pages/page1.html', replaceHistory: false }` |\n| `startNavigate` | Fires when navigation begins | `{ form: '<form name=\"form\"></form>', path: '/pages/page1.html',\nreplaceHistory: false }` |\n| `endNavigate` | Fired after the content has been retrieved and inserted onto\nthe page | `{ form: '<form name=\"form\"></form>', path: '/pages/page1.html' }` |\n\nThese events can be leveraged easily by listening for them on the Liferay global \nobject. For example, the JavaScript below alerts the user to \"Get ready to \nnavigate to\" the URL that has been clicked, just before SPA navigation begins:\n\n```javascript\nLiferay.on('beforeNavigate', function(event) {\n    alert(\"Get ready to navigate to \" + event.path);\n});\n```\n\nThe alert takes advantage of the payload for the `beforeNavigate` event, \nretrieving the URL from the `path` attribute of the JSON payload object. \n\n![Figure 1: You can leverage SPA lifecycle events in your apps.](../../../images/private-messaging-before-navigate.png)\n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/13-theme-anatomy/01-theme-anatomy-reference-guide-intro.markdown",
    "content": "---\nheader-id: theme-reference-guide\n---\n\n# Theme Anatomy Reference Guide\n\n[TOC levels=1-4]\n\nA theme is made up of several files. Although most of the files are named after\ntheir matching components, their functions may be unclear. This reference guide\nexplains each file's usage to make clear which files to modify.\n\nThemes built with the [Liferay JS Theme Toolkit](https://github.com/liferay/liferay-js-themes-toolkit/tree/master/packages) \nhave the anatomy shown below: \n\n- `theme-name/`\n    - `src/`\n        - `css/`\n            - [`_clay_custom.scss`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#-clay-customscss)\n            - [`_clay_variables.scss`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#-clay-variablesscss)\n            - [`_custom.scss`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#-customscss)\n            - [`_liferay_variables_custom.scss`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#-liferay-variables-customscss)\n        - `images/`\n            -   (custom images)\n        - `js/`\n            - [`main.js`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#mainjs)\n        - `templates/`\n            - [`init_custom.ftl`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#init-customftl)\n            - [`navigation.ftl`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#navigationftl)\n            - [`portal_normal.ftl`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#portal-normalftl)\n            - [`portal_pop_up.ftl`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#portal-pop-upftl)\n            - [`portlet.ftl`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#portletftl)\n        - `WEB-INF/`\n            - [`liferay-look-and-feel.xml`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#liferay-look-and-feelxml)\n            - [`liferay-plugin-package.properties`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#liferay-plugin-packageproperties)\n            - `src/`\n                - `resources-importer/`\n                    - (Many directories)\n    - [`liferay-theme.json`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#liferay-themejson)\n    - [`package.json`](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide#packagejson)\n \nRegarding CSS files, you should only modify `_clay_custom.scss`,\n`_clay_variables.scss`, `_custom.scss`, and `_liferay_variables_custom.scss`.\n\nYou can of course overwrite any CSS file you want, but if you modify any other\nfiles, you're removing styling that @product-ver@ needs to work properly.\n\n## Theme Files\n\n### _clay_custom.scss\n\nUsed for Clay custom styles, i.e. styles for a third party Bootstrap theme. \nAnything written in this file is compiled in the same scope as Bootstrap/Lexicon, \nso you can use their variables, mixins, etc. You can also implement any of the \nvariables you define in `_clay_variables.scss`.\n\n### _clay_variables.scss\n\nUsed to store custom Sass variables. This file gets injected into the \nBootstrap/Lexicon build, so you can overwrite variables and change how those \nlibraries are compiled.\n\n### _custom.scss\n\nUsed for custom CSS styles. You should place all of your custom CSS\nmodifications in this file.\n\n### _liferay_variables_custom.scss\n\nUsed for overwriting variables defined in `_liferay_variables.scss` without \nwiping out the whole file.\n\n### init_custom.ftl\n\nUsed for custom FreeMarker variables i.e. \n[theme setting](/docs/7-2/frameworks/-/knowledge_base/f/making-configurable-theme-settings) \nvariables.\n\n### navigation.ftl\n\nThe theme template for the theme's navigation.\n\n### portal_normal.ftl\n\nSimilar to a static site's `index.html`, this file acts as a hub for all theme\ntemplates.\n\n### portal_pop_up.ftl\n\nThe theme template for pop up dialogs for the theme's portlets.\n\n### portlet.ftl\n\nThe theme template for the theme's portlets. If your theme uses [Application \nDecorators](/docs/7-2/frameworks/-/knowledge_base/f/theming-portlets#portlet-decorators), \nyou can modify this file to create application decorator-specific theme \nsettings.\n\n### liferay-theme.json\n\nContains the configuration settings for your app server, in Node.js tool-based \nthemes. You can change this file manually at any time to update your server \nsettings. The file can also be updated via the \n[`gulp init` task](/docs/7-2/frameworks/-/knowledge_base/f/updating-your-themes-app-server). \n\n### package.json\n\nContains theme setting information such as the theme template language, version,\nand base theme, for Node.js tool developed themes. You can update this file\nmanually. The \n[`gulp extend` task](/docs/7-2/frameworks/-/knowledge_base/f/changing-your-base-theme) \ncan also be used to change the base theme. \n\n### main.js\n\nUsed for custom JavaScript.\n\n### liferay-look-and-feel.xml\n\nContains basic information for the theme. If your theme has \n[theme settings](/docs/7-2/frameworks/-/knowledge_base/f/making-configurable-theme-settings),\nthey are defined in this file. For a full explanation of this file, please see\nthe [Definitions docs](@platform-ref@/7.2-latest/definitions/liferay-look-and-feel_7_2_0.dtd.html). \n\n### liferay-plugin-package.properties\n\nContains general properties for the theme. \n[Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/importing-resources-with-a-theme) \nconfiguration settings are also placed in this file. For a full explanation of \nthe properties available for this file please see the \n[7.2 Properties documentation](@platform-ref@/7.2-latest/propertiesdoc/liferay-plugin-package_7_2_0.properties.html). \n"
  },
  {
    "path": "en/developer/reference/articles/02-front-end/14-freemarker-variables/01-freemarker-variables-intro.markdown",
    "content": "---\nheader-id: freemarker-variable-reference-guide\n---\n\n# Freemarker Variable Reference Guide\n\n[TOC levels=1-4]\n\nBy default, FreeMarker templates have access to several variables defined in \n[`init.ftl`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-theme/frontend-theme-unstyled/src/main/resources/META-INF/resources/_unstyled/templates/init.ftl) \nthat you can use in your [themes](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction) \nto access several theme objects, settings, and resources. Several of these \nvariables are listed below for reference:\n\n**Common Variables**\n\n| Variable | Description |\n| --- | --- |\n| `theme_display` | Returns the `themeDisplay` Java Object and all its methods |\n| `portlet_display` | Returns the `portletDisplay` Java Object and all its methods |\n| `layoutSet` | Returns the page set |\n| `theme_timestamp` | Prints the date in the current locale with the given format |\n| `theme_settings` | Retrieves theme settings. See [configurable theme settings](/docs/7-2/frameworks/-/knowledge_base/f/making-configurable-theme-settings) for more information. |\n| `root_css_class` | Returns the root CSS class which indicates the direction of the page (`ltr` (left-to-right) by default)  |\n| `css_class` | Returns a string of the current classes applied to the body of the page |\n| `page_group` | Retrieves the page group |\n| `css_folder` | Returns the path to the theme's `css` folder |\n| `images_folder` | Returns the path to the theme's `images` folder |\n| `javascript_folder` | Returns the path to the theme's `javascript` folder |\n| `templates_folder` | Returns the path to the theme's `templates` folder |\n| `full_css_path` | Returns the full path, which includes the servlet context, to the theme's `css` |\n| `full_templates_path` | returns the full path, which includes the servlet context, to the theme's `templates` |\n| `css_main_file` | Returns the path to `main.css` |\n| `js_main_file` | Returns the path to `main.js` |\n| `company_id` | Returns the company ID |\n| `company_name` | Returns the company name |\n| `company_logo` | Returns the company logo's URL |\n| `company_logo_height` | Returns the company logo's height |\n| `company_logo_width` | Returns the company logo's width |\n| `company_url` | Returns the URL of the home page for the company |\n| `time_zone` | Returns the time zone for the current user |\n| `is_login_redirect_required` | Returns whether a login redirect is required for the user |\n| `is_signed_in` | Returns whether the user is signed in |\n| `group_id` | Returns the group ID for the current user |\n| `time_zone` | Returns the time zone for the current user |\n| `is_default_user` | Returns if the user has a default role |\n| `is_female` | Returns if the current user is Female |\n| `is_male` | Returns if the current user is Male |\n| `is_setup_complete` | Returns whether the user has configured their profile |\n| `language` | Returns the native language for the current user |\n| `language_id` | Returns the ID of the current locale |\n| `user_birthday` | Returns the current user's birthday |\n| `user_comments` | Returns comments from the user's profile |\n| `user_email_address` | Returns the user's email address |\n| `user_first_name` | Returns the user's first name |\n| `user_greeting` | Returns the user's greeting |\n| `user_id` | Returns the ID of the current user |\n| `user_last_login_ip` | Returns the IP address that the user last logged in from |\n| `user_last_name` | Returns the last name of the current user |\n| `user_login_ip` | Returns the current user's current IP address |\n| `user_middle_name` | Returns the user's middle name |\n| `user_name` | Returns the current user's username |\n| `w3c_language_id` | Returns the W3C language code of the current language |\n\n**URLs**\n\n| Variable | Description |\n| --- | --- |\n| `show_control_panel` | Returns whether the current user has permission to view the Control Panel |\n| `control_panel_text` | Returns the \"control-panel\" language key in the current user's locale, if they have permission to view the Control Panel |\n| `control_panel_url` | Returns the URL to the Control Panel, if the current user has permission to view the Control Panel |\n| `show_home` | Returns whether the current user is on a page |\n| `home_text` | Returns the \"home\" language key in the current user's locale |\n| `home_url` | Returns the URL to the home page |\n| `show_my_account` | Returns whether the current user's account icon is visible |\n| `my_account_text` | Returns the \"my-account\" language key in the current user's locale, if the user's account icon is visible |\n| `my_account_url` | Returns the URL to the user's Account Settings page if the user's account icon is visible |\n| `show_sign_in` | Returns whether the sign in link is visible |\n| `sign_in_text` | Returns the \"sign-in\" language key in the current user's locale, if they are signed out |\n| `sign_in_url` | Returns the sign in URL, if the current user is signed out |\n| `show_sign_out` | Returns whether the sign out link is visible |\n| `sign_out_text` | Returns the \"sign-out\" language key in the current user's locale, if they are signed in |\n| `sign_out_url` | Returns the sign out URL, if the current user is signed in |\n\n**Page**\n\n| Variable | Description |\n| --- | --- |\n| `the_title` | Returns the current page's title |\n| `selectable` | Returns whether the current page is selectable |\n| `is_maximized` | Returns whether the page is maximized |\n| `page` | Returns the current page (layout) |\n| `is_first_child` | Returns whether the current page is the first child page in the navigation |\n| `is_first_parent` | Returns whether the current page is the first parent page in the navigation |\n| `is_portlet_page` | Returns whether the current page is a widget page (portlet) |\n| `site_name` | Returns the site's name |\n| `is_guest_group` | Returns whether the current page group is for guests |\n| `site_type` | Returns the type of the current site: site, company  site, organization site, or user site |\n| `site_default_url` | Returns the default URL for the site |\n| `layout_friendly_url` | Returns the friendly URL of the current page |\n| `portlet_id` | Returns the portlet ID for the specified portlet |\n\n**Logo**\n\n| Variable | Description |\n| --- | --- |\n| `logo_css_class` | Returns a string of the current classes applied to the logo. |\n| `use_company_logo` | Returns whether the logo is displayed |\n| `site_logo_height` | Returns the logo's height |\n| `site_logo_width` | Returns the logo's width |\n| `show_site_name_supported` | Returns whether the logo is configured to show the site name. The value is `true` if `show_site_name_default` is true. |\n| `show_site_name_default` | Returns whether the Show Site Name Default theme setting is enabled |\n| `show_site_name` | Returns whether the `showSiteName` property for the current pageset is enabled |\n| `logo_description` | Returns the Site's name or nothing if `show_site_name` is enabled. It is used for alternate text for the logo by default. |\n\n**Navigation**\n\n| Variable | Description |\n| --- | --- |\n| `has_navigation` | Returns whether navigation exists (i.e. at least one page exists) |\n| `nav_items` | Returns the current pages as list |\n| `nav_css_class` | Returns a string of the current classes applied to the page's navigation |\n\n**My Sites**\n\n| Variable | Description |\n| --- | --- |\n| `show_my_sites` | Returns whether the current user has a My Sites page |\n| `show_my_places` | Returns whether the current user has a My Sites page |\n| `my_sites_text` | Returns the \"my-sites\" language key in the current user's locale |\n| `my_places_text` | Returns whether the current user has a My Sites page |\n\n**Includes**\n\n| Variable | Description |\n| --- | --- |\n| `dir_include` | Returns \"/html\"  |\n| `body_bottom_include` | Returns \"${dir_include}/common/themes/body_bottom.jsp\" |\n| `body_top_include` | Returns \"${dir_include}/common/themes/body_top.jsp\" |\n| `bottom_include` | Returns \"${dir_include}/common/themes/bottom.jsp\" |\n| `top_head_include` | Returns \"${dir_include}/common/themes/top_head.jsp\" |\n| `top_messages_include` | Returns \"${dir_include}/common/themes/top_messages.jsp\" |\n\n**Date**\n\n| Variable | Description |\n| --- | --- |\n| `date` | Gives access to the `dateUtil` Java Object and all its methods |\n| `current_time` | Returns the current time |\n| `the_year` | Returns the current year |\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/00-intro.markdown",
    "content": "---\nheader-id: gradle-plugins\n---\n\n# Gradle Plugins\n\n[TOC levels=1-4]\n\nLiferay provides plugins that you can apply to your Gradle project. This\nreference documentation describes how to apply and use Liferay's Gradle plugins.\n\n**Important:** If you're using\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)\nto create Liferay apps, most of the Liferay Gradle plugins covered in this\nsection are already applied by default. The\n[com.liferay.gradle.plugins.workspace](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-workspace)\nand\n[com.liferay.gradle.plugins](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins)\ndependencies provide them, both of which are preset in workspace by default.\n\nDo not apply a Liferay Gradle plugin to an app that already has access to it.\n\nEach article in this section describes how to apply the plugin, what Gradle\ntasks the plugin provides, the plugin's configuration properties, and the\nplugin's dependencies. \n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/app-javadoc-builder-gradle-plugin.markdown",
    "content": "---\nheader-id: app-javadoc-builder-gradle-plugin\n---\n\n# App Javadoc Builder Gradle Plugin\n\n[TOC levels=1-4]\n\nThe App Javadoc Builder Gradle plugin lets you generate API documentation as a\nsingle, combined HTML document for an application that spans different\nsubprojects, each one representing a different component of the same\napplication.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in the build script of the root project:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.app.javadoc.builder\", version: \"1.2.2\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.app.javadoc.builder\"\n```\n\nThe App Javadoc Builder plugin automatically applies the [`base`](https://docs.gradle.org/current/userguide/standard_plugins.html#N135C1)\nand `reporting-base` plugins.\n\n## Project Extension\n\nThe App Javadoc Builder plugin exposes the following properties through the\nextension named `appJavadocBuilder`:\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`copyTags` | `boolean` | `true` | Whether to copy the custom block tags configuration from the subprojects. It sets the Javadoc [`-tag`](http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#tag) argument for the [`appJavadoc`](#appjavadoc) task.\n`doclintDisabled` | `boolean` | `true` on JDK8+, `false` otherwise. | Whether to ignore Javadoc errors. It sets the Javadoc [`-Xdoclint`](docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#BEJEFABE) and [`-quiet`](http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDGFHAA) arguments for the [`appJavadoc`](#appjavadoc) task.\n`groupNameClosure` | `Closure<String>` | The subproject's description, or the subproject's name if the description is empty. | The closure invoked in order to get the group heading for a subproject. The given closure is passed a [`Project`](https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html) as its parameter. If `groupPackages` is `false`, this property is not used.\n`groupPackages` | `boolean` | `true` | Whether to separate packages on the overview page based on the subprojects they belong to. It sets the [`-group`](docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html#CHDIGGII) argument for the [`appJavadoc`](#appjavadoc) task.\n`subprojects` | `Set<Project>` | `project.subprojects` | The subprojects to include in the API documentation of the app.\n\nThe same extension exposes the following methods:\n\nMethod | Description\n------ | -----------\n`AppJavadocBuilderExtension onlyIf(Closure<Boolean> onlyIfClosure)` | Includes a subproject in the API documentation if the given closure returns `true`. The closure is evaluated at the end of the subproject configuration phase and is passed a single parameter: the subproject. If the closure returns `false`, the subproject is not included in the API documentation.\n`AppJavadocBuilderExtension onlyIf(Spec<Project> onlyIfSpec)` | Includes a subproject in the API documentation if the given spec is satisfied. The spec is evaluated at the end of the subproject configuration phase. If the spec is not satisfied, the subproject is not included in the API documentation.\n`AppJavadocBuilderExtension subprojects(Iterable<Project> subprojects)` | Include additional projects in the API documentation of the app.\n`AppJavadocBuilderExtension subprojects(Project... subprojects)` | Include additional projects in the API documentation of the app.\n\n## Tasks\n\nThe plugin adds two tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n<a name=\"appjavadoc\"></a>`appJavadoc` | The `javadoc` tasks of the subprojects. | [`Javadoc`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html) | Generates Javadoc API documentation for the app.\n`jarAppJavadoc` | `appJavadoc` | [`Jar`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html) | Assembles a JAR archive containing the Javadoc files for this app.\n\nThe `appJavadoc` task is automatically configured with sensible defaults:\n\nProperty Name | Default Value\n------------- | -------------\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html#org.gradle.api.tasks.javadoc.Javadoc:classpath) | The `javadoc.classpath` of all the subprojects.\n[`destinationDir`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html#org.gradle.api.tasks.javadoc.Javadoc:destinationDir) | `${project.buildDir}/docs/javadoc`\n[`options.encoding`](https://docs.gradle.org/current/javadoc/org/gradle/external/javadoc/MinimalJavadocOptions.html#getEncoding\\(\\)) | `\"UTF-8\"`\n[`source`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html#org.gradle.api.tasks.javadoc.Javadoc:source) | The `javadoc.source` of all the subprojects.\n[`title`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html#org.gradle.api.tasks.javadoc.Javadoc:title) | `project.reporting.apiDocTitle`\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/baseline-gradle-plugin.markdown",
    "content": "---\nheader-id: baseline-gradle-plugin\n---\n\n# Baseline Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Baseline Gradle plugin lets you verify that the OSGi [semantic versioning](http://semver.org/)\nrules are obeyed by your OSGi bundle.\n\nWhen you run the [`baseline`](#baseline) task, the plugin *baselines* the new\nbundle against the latest released non-snapshot bundle (i.e., the *baseline*).\nThat is, it compares the public exported API of the new bundle with\nthe baseline. If there are any changes, it uses the OSGi semantic versioning\nrules to calculate the minimum new version. If the new bundle has a lower\nversion, errors are thrown.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.baseline\", version: \"2.1.0\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.baseline\"\n```\n\nThe Baseline plugin automatically applies the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nand [`reporting-base`](https://docs.gradle.org/current/userguide/standard_plugins.html#sec:base_plugins) plugins.\n\nSince the plugin needs to download the baseline, you have to configure a\n[repository](https://docs.gradle.org/current/userguide/artifact_dependencies_tutorial.html#sec:repositories_tutorial)\nthat hosts it; for example, the central Maven 2 repository:\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n```\n\n## Project Extension\n\nThe Baseline plugin exposes the following properties through the\n`baselineConfiguration` extension:\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`allowMavenLocal` | `boolean` | `false` | Whether to let the baseline come from the local Maven cache (by default: `${user.home}/.m2`). If the local Maven cache is not [configured](https://docs.gradle.org/current/userguide/dependency_management.html#sub:maven_local) as a project repository, this property has no effect.\n`lowestBaselineVersion` | `String` | `\"1.0.0\"` | The greatest project version to ignore for the baseline check. If the [project version](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:version) is less than or equal to the value of this property, the [`baseline`](#baseline) task is skipped.\n<a name=\"lowestmajorversion\"></a>`lowestMajorVersion` | `Integer` | Content of the file `${project.projectDir}/.lfrbuild-lowest-major-version`, where the default file name can be changed by setting the project property `baseline.lowest.major.version.file`. | The lowest major version of the released artifact to use in the baseline check.\n`lowestMajorVersionRequired` | `boolean` | `false` | Whether to fail the build if the [`lowestMajorVersion`](#lowestmajorversion) is not specified.\n\nIf the `lowestMajorVersion` is not specified, the plugin runs the check using\nthe most recent released non-snapshot bundle as baseline, which matches the\n[version range](http://ant.apache.org/ivy/history/latest-milestone/settings/version-matchers.html)\n`(,${project.version})`. Otherwise, if the `lowestMajorVersion` is equal to a\nvalue `L` and the project has version `M.x.y` (with `L` less or equal than `M`),\nmultiple checks are performed in order, using the following version ranges as\nbaseline:\n\n1. `[L.0.0, (L + 1).0.0)`\n2. `[(L + 1).0.0, (L + 2).0.0)`\n3. ...\n4. `[(M - 2).0.0, (M - 1).0.0)`\n5. `[(M - 1).0.0, M.0.0)`\n6. `[M.0.0, M.x.y)`\n\nThe first failing check fails the whole build.\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n<a name=\"baseline\"></a>`baseline` | [`jar`]((https://docs.gradle.org/current/userguide/java_plugin.html#sec:jar)) | [`BaselineTask`](#baselinetask) | Compares the public API of this project with the public API of the previous released version, if found.\n\nThe `baseline` task is automatically configured with sensible defaults:\n\nProperty Name | Default Value\n------------- | -------------\n[`baselineConfiguration`](#baselineconfiguration) | [`configurations.baseline`](#baseline-dependency)\n[`bndFile`](#bndfile) | `${project.projectDir}/bnd.bnd`\n[`newJarFile`](#newjarfile) | [`project.tasks.jar.archivePath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archivePath)\n[`sourceDir`](#sourcedir) | The first `resources` directory of the `main` source set (by default: `src/main/resources`).\n\n### BaselineTask\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"baselineconfiguration\"></a>`baselineConfiguration` | `Configuration` | `null` | The configuration that contains exactly one dependency to the baseline bundle.\n<a name=\"bndfile\"></a>`bndFile` | `File` | `null` | The BND file of the project. If provided, the task will automatically update the [`Bundle-Version`](http://bnd.bndtools.org/heads/bundle_version.html) header.\n`forceCalculatedVersion` | `boolean` | `false` | Whether to fail the baseline check if the `Bundle-Version` has been excessively increased.\n<a name=\"ignoreexcessiveversionincreases\"></a>`ignoreExcessiveVersionIncreases` | `boolean` | `false` | Whether to ignore excessive package version increase warnings.\n<a name=\"ignorefailures\"></a>`ignoreFailures` | `boolean` | `false` | Whether the build should not break when semantic versioning errors are found.\n`logFile` | `File` | `null` | The file to which the results of the baseline check are written. *(Read-only)*\n`logFileName` | `String` | `\"baseline/${task.name}.log\"` | The name of the file to which the results of the baseline check are written. If the `reporting-base` plugin is applied, the file name is relative to [`reporting.baseDir`](https://docs.gradle.org/current/dsl/org.gradle.api.reporting.ReportingExtension.html#org.gradle.api.reporting.ReportingExtension:baseDir); otherwise, it's relative to the project directory.\n<a name=\"newjarfile\"></a>`newJarFile` | `File` | `null` | The file of the new OSGi bundle.\n`reportDiff` | `boolean` | `true` if the project property `baseline.jar.report.level` has either value `\"diff\"` or `\"persist\"`; `false` otherwise | Whether to show a granular, differential report of all changes that occurred in the exported packages of the OSGi bundle.\n`reportOnlyDirtyPackages` | `boolean` | Value of the project property `baseline.jar.report.only.dirty.packages` if specified; `true` otherwise. | Whether to show only packages with API changes in the report.\n<a name=\"sourcedir\"></a>`sourceDir` | `File` | `null` | The directory to which the [`packageinfo`](http://bnd.bndtools.org/chapters/170-versioning.html#versioning-packages) files are generated or updated.\n\nThe properties of type `File` support any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties to defer evaluation until task execution.\n\n### Helper Tasks\n\nIf the [`lowestMajorVersion`](#lowestmajorversion) property is specified with a\nvalue `L`, the plugin creates a series of helper tasks of type [`BaselineTask`](#baselinetask)\nat the end of the [project evaluation](https://docs.gradle.org/current/userguide/build_lifecycle.html#N11BAE),\none for each major version between `L` and the major version `M` of the project:\n\n1. Task `baseline${L + 1}`, which depends on `baseline${L + 2}` and uses the\nversion range `[(L + 1).0.0, (L + 2).0.0)` as baseline.\n2. Task `baseline${L + 2}`, which depends on `baseline${L + 3}` and uses the\nversion range `[(L + 2).0.0, (L + 3).0.0)` as baseline.\n3. ...\n4. Task `baseline${M - 2}`, which depends on `baseline${M - 1}` and uses the\nversion range `[(M - 2).0.0, (M - 1).0.0)` as baseline.\n5. Task `baseline${M - 1}`, which depends on `baseline${M}` and uses the\nversion range `[(M - 1).0.0, M.0.0)` as baseline.\n5. Task `baseline${M}`, which uses the version range `[M.0.0, M.x.y)` as\nbaseline.\n\nThe `baseline` task is also configured to use the version range\n`[L.0.0, (L + 1).0.0)` as baseline, and to depend on the task\n`baseline${L + 1}`. This means that running the `baseline` task runs the\nbaseline check against multiple versions, starting from the most recent `M` and\ngoing back to `L`.\n\nMoreover, all tasks except `baseline${M}` have the property\n[`ignoreExcessiveVersionIncreases`](#ignoreexcessiveversionincreases) set to\n`true`.\n\n## Additional Configuration\n\nThere are additional configurations that can help you baseline your OSGi bundle.\n\n### Baseline Dependency\n\nThe plugin creates a configuration called `baseline` with a default dependency\nto a released non-snapshot version of the bundle:\n\n- version range `[L.0.0, (L + 1).0.0)` if the [`lowestMajorVersion`](#lowestmajorversion)\nproperty is specified with a value `L`.\n- version range `(,${project.version})` otherwise.\n\nIt is possible to override this setting and use a different version of the\nbundle as baseline.\n\n### System Properties\n\nIt is possible to set the default values of the [`ignoreFailures`](#ignorefailures)\nproperty for a `BaselineTask` task via system properties:\n\n    -D${task.name}.ignoreFailures=true\n\nFor example, run the following Bash command to execute the baseline check\nwithout breaking the build, in case of errors:\n\n```bash\n./gradlew baseline -Dbaseline.ignoreFailures=true\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/change-log-builder-gradle-plugin.markdown",
    "content": "---\nheader-id: change-log-builder-gradle-plugin\n---\n\n# Change Log Builder Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Change Log Builder Gradle plugin lets you generate and maintain a\nchange log file based on the Git commits in your project. A change log file\ngenerated by this plugin looks like this\n\n```ini\n#\n# Bundle Version 1.0.1\n#\n9c77ff4c95cb1a325db3bdd089be105206e8b63c^..b421f00ac84b065685b131833fecc594fc01c760=LPS-123 LPS-1321\n\n#\n# Bundle Version 1.0.2\n#\nb421f00ac84b065685b131833fecc594fc01c760^..bc15d8d84e12b9544f78e4e3743c510dbaec2d89=LPS-456\n```\n\nEvery time the [`buildChangeLog`](#buildchangelog) task is executed, a new line\nis added to the change log, which lists all Git [commit prefixes](#ticketidprefixes)\n(usually issue ticket IDs) that occurred in a certain range. The end of the\nrange is always the tip of the current branch. The start range can vary,\ndepending on the case:\n\n- If `buildChangeLog` has never been executed for the project, the change log\ndoes not exist. Therefore, the most recent commit from two years ago is used for\nthe range start.\n- If a change log already exists for your project, the start range begins at the\nrange end of the last line in the change log.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.change.log.builder\", version: \"1.1.3\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.change.log.builder\"\n```\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n<a name=\"buildchangelog\"></a>`buildChangeLog` | \\- | [`BuildChangeLogTask`](#buildchangelogtask) | Builds the change log file for this project.\n\nThe `buildChangeLog` task is automatically configured with sensible defaults,\ndepending on whether the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin is applied:\n\nProperty Name | Default Value\n------------- | -------------\n[`changeLogHeader`](#changelogheader) | `\"Bundle Version ${project.version}\"`\n[`changeLogFile`](#changelogfile) | <p>**If the `java` plugin is applied:** The `META-INF/liferay-releng.changelog` file in the first `resources` directory of the `main` source set (by default, `src/main/resources/META-INF/liferay-releng.changelog`).</p><p>**Otherwise:** `\"${project.projectDir}/liferay-releng.changelog\"`</p>\n[`dirs`](#dirs) | `[project.projectDir]`\n\n### BuildChangeLogTask\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"changelogfile\"></a>`changeLogFile` | `File` | `null` | The change log file to build.\n<a name=\"changelogheader\"></a>`changeLogHeader` | `String` | `null` | The header for the new line in the change log.\n<a name=\"dirs\"></a>`dirs` | `FileCollection` | `[]` | The directories to consider when listing the commits in the range specified.\n`gitDir` | `File` | `project.rootDir` | The base directory to start searching for the `.git` directory. The search proceeds in all the ancestors of the directory specified.\n`rangeEnd` | `String` | `null` | The hash of the last commit to consider. If not set, it corresponds to the range end of the last line in the change log, or the most recent commit from at least two years ago if the change log file does not exist yet.\n`rangeStart` | `String` | `null` | The hash of the first commit to consider. If not set, it corresponds to the hash of the tip of the current branch.\n<a name=\"ticketidprefixes\"></a>`ticketIdPrefixes` | `Set<String>` | `[\"CLDSVCS\", \"LPS\", \"SOS\", \"SYNC\"]` | The valid prefix of the Git commit messages to add to the change log. For example, if a commit message is `\"LPS-123 Bugfix\"`, `\"LPS-123\"` will be added to the change log.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties to defer evaluation until task execution.\n\n#### Task Methods\n\nMethod | Description\n------ | -----------\n`BuildChangeLogTask dirs(Iterable<?> dirs)` | Adds directories to consider when listing the commits in the range specified.\n`BuildChangeLogTask dirs(Object... dirs)` | Adds directories to consider when listing the commits in the range specified.\n`BuildChangeLogTask ticketIdPrefixes(Iterable<String> ticketIdPrefixes)` | Adds valid prefixes of the Git commit messages to add to the change log.\n`BuildChangeLogTask ticketIdPrefixes(String... ticketIdPrefixes)` | Adds valid prefixes of the Git commit messages to add to the change log.\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/css-builder-gradle-plugin.markdown",
    "content": "---\nheader-id: css-builder-gradle-plugin\n---\n\n# CSS Builder Gradle Plugin\n\n[TOC levels=1-4]\n\nThe CSS Builder Gradle plugin lets you run the [Liferay CSS Builder](https://github.com/liferay/liferay-portal/tree/master/modules/util/css-builder)\ntool to compile [Sass](http://sass-lang.com/) files in your project.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.css.builder\", version: \"3.0.0\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.css.builder\"\n```\n\nSince the plugin automatically resolves the Liferay CSS Builder library as a\ndependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t}\n}\n```\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`buildCSS` | \\- | [`BuildCSSTask`](#buildcsstask) | Compiles the Sass files in this project.\n\nThe plugin also adds the following dependencies to tasks defined by the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin:\n\nName | Depends On\n---- | ----------\n`processResources` | `buildCSS`\n\nThe `buildCSS` task is automatically configured with sensible defaults,\ndepending on whether the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nor the [`war`](https://docs.gradle.org/current/userguide/war_plugin.html)\nplugins are applied:\n\nProperty Name | Default Value\n------------- | -------------\n[`baseDir`](#basedir) | <p>**If the `java` plugin is applied:** The first `resources` directory of the `main` source set (by default: `src/main/resources`).</p><p>**If the `war` plugin is applied:** `project.webAppDir`.</p><p>**Otherwise:** `null`</p>\n\n### BuildCSSTask\n\nTasks of type `BuildCSSTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, such as [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.css.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize),\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args) | CSS Builder command line arguments\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.cssBuilder`](#liferay-css-builder-dependency)\n[`defaultCharacterEncoding`](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/JavaExec.html#setDefaultCharacterEncoding\\(java.lang.String\\)) | `\"UTF-8\"`\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.liferay.css.builder.CSSBuilder\"`\n[`systemProperties`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:systemProperties) | `[\"sass.compiler.jni.clean.temp.dir\", true]`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`appendCssImportTimestamps` | `boolean` | `true` | Whether to append the current timestamp to the URLs in the `@import` CSS at-rules. It sets the `sass.append.css.import.timestamps` argument.\n<a name=\"basedir\"></a>`baseDir` | `File` | `null` | The base directory that contains the SCSS files to compile. It sets the `sass.docroot.dir` argument.\n`cssFiles` | `FileCollection` | \\- | The SCSS files to compile. *(Read-only)*\n`dirNames` | `List<String>` | `[\"/\"]` | The name of the directories, relative to [`baseDir`](#basedir), which contain the SCSS files to compile. All sub-directories are searched for SCSS files as well. It sets the `sass.dir` argument.\n`generateSourceMap` | `boolean` | `false` | Whether to generate [source maps](https://developers.google.com/web/tools/chrome-devtools/debug/readability/source-maps) for easier debugging. It sets the `sass.generate.source.map` argument.\n<a name=\"importdir\"></a>`importDir` | `File` | `null` | The `META-INF/resources` directory of the [Liferay Frontend Common CSS](https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-css/frontend-css-common) artifact. This is required in order to make [Bourbon](http://bourbon.io) and other CSS libraries available to the compilation.\n`importFile` | `File` | [`configurations.portalCommonCSS.singleFile`](#liferay-frontend-common-css-dependency) | The Liferay Frontend Common CSS JAR file. If [`importDir`](#importdir) is set, this property has no effect.\n`importPath` | `File` | \\- | The value of the `importDir` property if set; otherwise `importFile`. It sets the `sass.portal.common.path` argument. *(Read-only)*\n`outputDirName` | `String` | `\".sass-cache/\"` | The name of the sub-directories where the SCSS files are compiled to. For each directory that contains SCSS files, a sub-directory with this name is created. It sets the `sass.output.dir` argument.\n`outputDirs` | `FileCollection` | \\- | The directories where the SCSS files are compiled to. Usually, these directories are ignored by the Version Control System. *(Read-only)*\n`precision` | `int` | `5` | The numeric precision of numbers in Sass. It sets the `sass.precision` argument.\n`rtlExcludedPathRegexps` | `List<String>` | `[]` | The SCSS file patterns to exclude when converting for right-to-left (RTL) support. It sets the `sass.rtl.excluded.path.regexps` argument.\n`sassCompilerClassName` | `String` | `null` | The type of Sass compiler to use. Supported values are `\"jni\"` and `\"ruby\"`. If not set, defaults to `\"jni\"`. It sets the `sass.compiler.class.name` argument.\n\n| **Note:** Liferay's CSS Builder is supported for Oracle's JDK and uses a native\n| compiler for increased speed. If you're using an IBM JDK, you may experience\n| issues when building your Sass files (e.g., when building a theme). It's\n| recommended to switch to using the Oracle JDK, but if you prefer using the IBM\n| JDK, you must use the fallback Ruby compiler. You can do this two ways:\n| \n| - If you're working in a\n|   [Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)\n|   or using the\n|   [Liferay Gradle Plugins](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins)\n|   plugin, set `sass.compiler.class.name=ruby` in your `gradle.properties` file.\n| - Otherwise, set `buildCSS.sassCompilerClassName='ruby'` in the project's\n|   `build.gradle` file.\n| \n| The `sass.compiler.class.name=ruby` Gradle property only works for modules, so\n| if you're using the Ruby compiler in a WAR project (e.g., theme), you must use\n| the second option.\n| \n| Be aware that the Ruby-based compiler doesn't perform as well as the native\n| compiler, so expect longer compile times.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the `int`\nand `String` properties, to defer evaluation until task execution.\n\n#### Task Methods\n\nMethod | Description\n------ | -----------\n`BuildCSSTask dirNames(Iterable<Object> dirNames)` | Adds sub-directory names, relative to [`baseDir`](#basedir), which contain the SCSS files to compile.\n`BuildCSSTask dirNames(Object... dirNames)` | Adds sub-directory names, relative to [`baseDir`](#basedir), which contain the SCSS files to compile.\n`BuildCSSTask rtlExcludedPathRegexps(Iterable<Object> rtlExcludedPathRegexps)` | Adds SCSS file patterns to exclude when converting for right-to-left (RTL) support.\n`BuildCSSTask rtlExcludedPathRegexps(Object... rtlExcludedPathRegexps)` | Adds SCSS file patterns to exclude when converting for right-to-left (RTL) support.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the CSS Builder.\n\n### Liferay CSS Builder Dependency\n\nBy default, the plugin creates a configuration called `cssBuilder` and adds a\ndependency to the latest released version of the Liferay CSS Builder. It is\npossible to override this setting and use a specific version of the tool by\nmanually adding a dependency to the `cssBuilder` configuration:\n\n```groovy\ndependencies {\n    cssBuilder group: \"com.liferay\", name: \"com.liferay.css.builder\", version: \"3.0.0\"\n}\n```\n\n### Liferay Frontend Common CSS Dependency\n\nBy default, the plugin creates a configuration called `portalCommonCSS` and adds\na dependency to the latest released version of the Liferay Frontend Common CSS\nartifact. It is possible to override this setting and use a specific version of\nthe artifact by manually adding a dependency to the `portalCommonCSS`\nconfiguration:\n\n```groovy\ndependencies {\n    portalCommonCSS group: \"com.liferay\", name: \"com.liferay.frontend.css.common\", version: \"2.0.1\"\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/db-support-gradle-plugin.markdown",
    "content": "---\nheader-id: db-support-gradle-plugin\n---\n\n# DB Support Gradle Plugin\n\n[TOC levels=1-4]\n\nThe DB Support Gradle plugin lets you run the [Liferay DB Support](https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-db-support)\ntool to execute certain actions on a local Liferay database. So far, the\nfollowing actions are available:\n\n- Cleans the Liferay database from the Service Builder tables and rows of a\nmodule.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n\tdependencies {\n\t\tclasspath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.db.support\", version: \"1.0.5\"\n\t}\n\n\trepositories {\n\t\tmaven {\n\t\t\turl \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t\t}\n\t}\n}\n\napply plugin: \"com.liferay.portal.tools.db.support\"\n```\n\nSince the plugin automatically resolves the Liferay DB Support library as\na dependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n\tmaven {\n\t\turl \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t}\n}\n```\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`cleanServiceBuilder` | \\- | [`CleanServiceBuilderTask`](#cleanservicebuildertask) | Cleans the Liferay database from the Service Builder tables and rows of a module.\n\nThe `cleanServiceBuilder` task is automatically configured with sensible\ndefaults, depending on whether the [`base`](https://docs.gradle.org/current/userguide/standard_plugins.html#N135C1)\nplugin is applied:\n\nProperty Name | Default Value\n------------- | -------------\n[`servletContextName`](#servletcontextname) | <p>**If the `base` plugin is applied:**  The bundle symbolic name of the project inferred via the [`OsgiHelper`](https://github.com/gradle/gradle/blob/master/subprojects/osgi/src/main/java/org/gradle/api/internal/plugins/osgi/OsgiHelper.java) class.</p><p>**Otherwise:** `null`</p>\n[`serviceXmlFile`](#servicexmlfile) | `\"${project.projectDir}/service.xml\"`\n\n### CleanServiceBuilderTask\n\nTasks of type `BuildDeploymentHelperTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, such as [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.lang.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize),\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args) | The DB Support command line arguments.\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.dbSupport`](#jdbc-drivers-dependency) + [`project.configurations.dbSupportTool`](#liferay-db-support-dependency)\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.liferay.portal.tools.db.support.DBSupport\"`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`password` | `String` | `null` | The user password for connecting to the Liferay database. It sets the `--password` argument. If [`propertiesFile`](#propertiesfile) is set, this property has no effect.\n<a name=\"propertiesfile\"></a>`propertiesFile` | `File` | `null` | The `portal-ext.properties` file that contains the JDBC settings for connecting to the Liferay database. It sets the `--properties-file` argument.\n<a name=\"servletcontextname\"></a>`servletContextName` | `String` | `null` | The servlet context name (usually the value of the `Bundle-Symbolic-Name` manifest header) of the module. It sets the `--servlet-context-name` argument.\n<a name=\"servicexmlfile\"></a>`serviceXmlFile` | `File` | `null` | The `service.xml` file of the module. It sets the `--service-xml-file` argument.\n`url` | `String` | `null` | The JDBC URL for connecting to the Liferay database. It sets the `--url` argument. If [`propertiesFile`](#propertiesfile) is set, this property has no effect.\n`userName` | `String` | `null` | The user name for connecting to the Liferay database. It sets the `--user-name` argument. If [`propertiesFile`](#propertiesfile) is set, this property has no effect.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the `int`\nand `String` properties to defer evaluation until task execution.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the Deployment Helper.\n\n### JDBC Drivers Dependency\n\nThe plugin creates a configuration called `dbSupport`, which can be used to\nprovide the suitable JDBC driver for your Liferay database:\n\n```groovy\ndependencies {\n\tdbSupport group: \"mysql\", name: \"mysql-connector-java\", version: \"5.1.23\"\n\tdbSupport group: \"org.mariadb.jdbc\", name: \"mariadb-java-client\", version: \"1.1.9\"\n\tdbSupport group: \"org.postgresql\", name: \"postgresql\", version: \"9.4-1201-jdbc41\"\n}\n```\n\n### Liferay DB Support Dependency\n\nBy default, the plugin creates a configuration called `dbSupportTool` and adds a\ndependency to the latest released version of the Liferay DB Support. It is\npossible to override this setting and use a specific version of the tool by\nmanually adding a dependency to the `dbSupportTool` configuration:\n\n```groovy\ndependencies {\n\tdbSupportTool group: \"com.liferay\", name: \"com.liferay.portal.tools.db.support\", version: \"1.0.8\"\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/dependency-checker-gradle-plugin.markdown",
    "content": "---\nheader-id: dependency-checker-gradle-plugin\n---\n\n# Dependency Checker Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Dependency Checker Gradle plugin lets you warn users if a specific\nconfiguration dependency is not the latest one available from the Maven central\nrepository. The plugin eventually fails the build if the dependency age (the\ndifference between the timestamp of the current version and the latest version)\nis above a predetermined threshold.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.dependency.checker\", version: \"1.0.3\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.dependency.checker\"\n```\n\n## Project Extension\n\nThe Dependency Checker Gradle plugin exposes the following properties through\nthe extension named `dependencyChecker`:\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"ignorefailures\"></a>`ignoreFailures` | `boolean` | `true` | Whether to print an error message instead of failing the build when the dependency check fails, either for a network error or because the dependency is out-of-date.\n\nThe same extension exposes the following methods:\n\nMethod | Description\n------ | -----------\n`void maxAge(Map<?, ?> args)` | Declares the max age allowed for a dependency. The `args` map must contain the following entries: <ul><li>`configuration`: the configuration name</li><li>`group`: the dependency group</li><li>`name`: the dependency name</li><li>`maxAge`: an instance of [`groovy.time.Duration`](http://docs.groovy-lang.org/latest/html/api/groovy/time/Duration.html) that represents the maximum age allowed for the dependency</li><li>`throwError`: a `boolean` value representing whether to throw an error if the dependency is out-of-date</li></ul>\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the Deployment Helper.\n\n### Project Properties\n\nIt is possible to set the default values of the [`ignoreFailures`](#ignorefailures)\nproperty via the project property `dependencyCheckerIgnoreFailures`:\n\n    -PdependencyCheckerIgnoreFailures=false\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/deployment-helper-gradle-plugin.markdown",
    "content": "---\nheader-id: deployment-helper-gradle-plugin\n---\n\n# Deployment Helper Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Deployment Helper Gradle plugin lets you run the [Liferay Deployment Helper](https://github.com/liferay/liferay-portal/tree/master/modules/util/deployment-helper)\ntool to create a cluster deployable WAR from your OSGi artifacts.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.deployment.helper\", version: \"1.0.5\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.deployment.helper\"\n```\n\nSince the plugin automatically resolves the Liferay Deployment Helper library as\na dependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`buildDeploymentHelper` | \\- | [`BuildDeploymentHelperTask`](#builddeploymenthelpertask) | Builds a WAR which contains one or more files that are copied once the WAR is deployed.\n\n### BuildDeploymentHelperTask\n\nTasks of type `BuildDeploymentHelperTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, such as [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.lang.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize),\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args) | The Deployment Helper command line arguments.\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.deploymentHelper`](#liferay-deployment-helper-dependency)\n[`deploymentFiles`](#deploymentfiles) | The output files of the [`jar`](https://docs.gradle.org/current/userguide/java_plugin.html#sec:jar) tasks of this project and all its sub-projects.\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.liferay.deployment.helper.DeploymentHelper\"`\n[`outputFile`](#outputfile) | `\"${project.buildDir}/${project.name}.war\"`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"deploymentfiles\"></a>`deploymentFiles` | `FileCollection` | `[]` | The files or directories to include in the WAR and copy once the WAR is deployed. If a directory is added to this collection, all the JAR files contained in the directory are included in the WAR.\n`deploymentPath` | `File` | `null` | The directory to which the included files are copied.\n<a name=\"outputfile\"></a>`outputFile` | `File` | `null` | The WAR file to build.\n\nThe properties of type `File` support any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\n\n#### Task Methods\n\nMethod | Description\n------ | -----------\n`BuildDeploymentHelperTask deploymentFiles(Iterable<?> deploymentFiles)` | Adds files or directories to include in the WAR and copy once the WAR is deployed. The values are evaluated as per [`project.files`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:files\\(java.lang.Object[]\\)).\n`BuildDeploymentHelperTask deploymentFiles(Object... deploymentFiles)` | Adds files or directories to include in the WAR and copy once the WAR is deployed. The values are evaluated as per [`project.files`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:files\\(java.lang.Object[]\\)).\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the Deployment Helper.\n\n### Liferay Deployment Helper Dependency\n\nBy default, the plugin creates a configuration called `deploymentHelper` and\nadds a dependency to the latest released version of the Liferay Deployment\nHelper. It is possible to override this setting and use a specific version of\nthe tool by manually adding a dependency to the `deploymentHelper`\nconfiguration:\n\n```groovy\ndependencies {\n    deploymentHelper group: \"com.liferay\", name: \"com.liferay.deployment.helper\", version: \"1.0.4\"\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/go-gradle-plugin.markdown",
    "content": "---\nheader-id: go-gradle-plugin\n---\n\n# Go Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Go Gradle plugin lets you run [Go](https://golang.org/) as part of your\nbuild.\n\nThe plugin has been successfully tested with Gradle 3.5.1 up to 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.go\", version: \"1.0.0\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.go\"\n```\n\n## Project Extension\n\nThe Go Gradle plugin exposes the following properties through the extension\nnamed `go`:\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"godir\"></a>`goDir` | `File` | `\"${project.buildDir}/go\"` | The directory where the Go distribution is unpacked.\n`goUrl` | `String` | `\"https://dl.google.com/go/go${go.goVersion}.${platform}-${bitMode}.${extension}` | The URL of the Go distribution to download.\n`goVersion` | `String` | `\"1.11.4\"` | The Go distribution's version to use.\n<a name=\"workingdir\"></a>`workingDir` | `File` | `\"${project.projectDir}\"` | The directory that contains the project's Go source code.\n\n## Tasks\n\nThe plugin adds a series of tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n<a name=\"downloadgo\"></a>`downloadGo` | \\- | [`DownloadGoTask`](#downloadgotask) | Downloads and unpacks the local Go distribution for the project.\n[`goBuild${programName}`](#gocommandprogramname-task) | `downloadGo` | [`ExecuteGoTask`](#executegotask) | Compiles packages and dependencies for the Go program.\n[`goClean${programName}`](#gocommandprogramname-task) | `downloadGo` | [`ExecuteGoTask`](#executegotask) | Removes object files for the Go program.\n[`goRun${programName}`](#gocommandprogramname-task) | `downloadGo` | [`ExecuteGoTask`](#executegotask) | Compiles and runs the Go program.\n[`goTest${programName}`](#gocommandprogramname-task) | `downloadGo` | [`ExecuteGoTask`](#executegotask) | Tests packages for the Go program.\n\n### DownloadGoTask\n\nThe purpose of this task is to download and unpack a Go distribution.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`goDir` | `File` | `null` | The directory where the Go distribution is unpacked.\n`goUrl` | `String` | `null` | The URL of the Go distribution to download.\n\nThe `File` type support any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties, to defer evaluation until task execution.\n\n### ExecuteGoTask\n\nThis is the base task to run Go in a Gradle build. All tasks of type\n`ExecuteGoTask` automatically depend on [`downloadGo`](#downloadgo).\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`args` | `List<Object>` | `[]` | The arguments for the Go invocation.\n`command` | `String` | `\"go\"` | The file name of the executable to invoke.\n`environment` | `Map<Object, Object>` | `[]` | The environment variables for the Go invocation.\n`inheritProxy` | `boolean` | `true` | Whether to set the `http_proxy`, `https_proxy`, and `no_proxy` environment variables in the Go invocation based on the values of the system properties `https.proxyHost`, `https.proxyPort`, `https.proxyUser`, `https.proxyPassword`, `https.nonProxyHosts`, `https.proxyHost`, `https.proxyPort`, `https.proxyUser`, `https.proxyPassword`, and `https.nonProxyHosts`. If these environment variables are already set, their values will not be overwritten.\n`goDir` | `File` | `go.goDir`](#godir) | The directory that contains the executable to invoke.\n`useGradleExec` | `boolean` | <p>**If running in a [Gradle Daemon](https://docs.gradle.org/current/userguide/gradle_daemon.html):** `true`</p><p>**Otherwise:** `false`</p> | Whether to invoke Go using [`project.exec`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:exec\\(org.gradle.api.Action\\)), which can solve hanging problems with the Gradle Daemon.\n<a name=\"workingdirproperty\"></a>`workingDir` | `File` | `go.workingDir`](#workingdir) | The working directory to use in the Go invocation.\n\nThe type `File` properties support any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties to defer evaluation until task execution.\n\n#### Task Methods\n\nMethod | Description\n------ | -----------\n`ExecuteGoTask args(Iterable<?> args)` | Adds arguments for the Go invocation.\n`ExecuteGoTask args(Object... args)` | Adds arguments for the Go invocation.\n`ExecuteGoTask environment(Map<?, ?> environment)` | Adds environment variables for the Go invocation.\n`ExecuteGoTask environment(Object key, Object value)` | Adds an environment variable for the Go invocation.\n\n### go${command}${programName} Task\n\nFor each Go program in [`workingDir`](#workingdirproperty), four tasks of type\n[`ExecuteGoTask`](#executegotask) are added. Each of these tasks are\nautomatically configured with sensible defaults:\n\nProperty Name | Default Value\n------------- | -------------\n`args` | `[\"${command}\", \"${programFile.absolutePath}\"]`\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/gulp-gradle-plugin.markdown",
    "content": "---\nheader-id: gulp-gradle-plugin\n---\n\n# Gulp Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Gulp Gradle plugin lets you run [Gulp](http://gulpjs.com/) tasks as part of\nyour build.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.gulp\", version: \"2.0.59\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.gulp\"\n```\n\nThe Gulp plugin automatically applies the [`com.liferay.node`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-node)\nplugin.\n\n## Tasks\n\nThe plugin adds one [task rule](https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:task_rules)\nto your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`gulp<Task>` | `downloadNode`, `npmInstall` | [`ExecuteGulpTask`](#executegulptask) | Executes a named Gulp task.\n\n### ExecuteGulpTask\n\nTasks of type `ExecuteGulpTask` extend [`ExecuteNodeScriptTask`](/docs/7-2/reference/-/knowledge_base/r/node-gradle-plugin#executenodescripttask),\nso all its properties and methods, such as `args` and `inheritProxy`, are\navailable. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n`scriptFile` | `\"node_modules/gulp/bin/gulp.js\"`\n\nGulp must be already installed in the `node_modules` directory of the project;\notherwise, it will not be downloaded by the task. In order to ensure Gulp is\ninstalled, you can add the Gulp dependency to the project's `package.json` file.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`gulpCommand` | `String` | `null` | The Gulp task to execute.\n\nIt is possible to use Closures and Callables as values for the `String`\nproperties to defer evaluation until task execution.\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/jasper-jspc-gradle-plugin.markdown",
    "content": "---\nheader-id: jasper-jspc-gradle-plugin\n---\n\n# Jasper JSPC Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Jasper JSPC Gradle plugin lets you run the [Liferay Jasper JSPC](https://github.com/liferay/liferay-portal/tree/master/modules/util/jasper-jspc)\ntool to compile the JavaServer Pages (JSP) files in your project. This\ncan be useful to\n\n- check for errors in the JSP files.\n- pre-compile the JSP files for better performance.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.jasper.jspc\", version: \"2.0.5\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.jasper.jspc\"\n```\n\nThe Jasper JSPC plugin automatically applies the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin.\n\nSince the plugin automatically resolves the Liferay Jasper JSPC library as a\ndependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## Tasks\n\nThe plugin adds two tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`compileJSP` | `generateJSPJava` | [`JavaCompile`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html) | Compiles JSP files to check for errors.\n`generateJSPJava` | [`jar`](https://docs.gradle.org/current/userguide/java_plugin.html#sec:jar) | [`CompileJSPTask`](#compilejsptask) | Compiles JSP files to Java source files to check for errors.\n\nThe `generateJSPJava` task is automatically configured with sensible defaults,\ndepending on whether the [`war`](https://docs.gradle.org/current/userguide/war_plugin.html)\nplugin is applied:\n\nProperty Name | Default Value\n------------- | -------------\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.jspCTool`](#liferay-jasper-jspc-dependency)\n[`destinationDir`](#destinationdir) | `\"${project.buildDir}/jspc\"`\n[`jspCClasspath`](#jspcclasspath) | [`project.configurations.jspC`](#jsp-compilation-classpath)\n[`webAppDir`](#webappdir) | <p>**If the `war` plugin is applied:** `project.webAppDir`.</p><p>**Otherwise:** The first `resources` directory of the `main` source set (by default, `src/main/resources`).</p>\n\nThe `compileJSP` task is also configured with the following defaults:\n\nProperty Name | Default Value\n------------- | -------------\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html#org.gradle.api.tasks.compile.JavaCompile:classpath) | `project.configurations.jspCTool + project.configurations.jspC`\n[`destinationDir`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html#org.gradle.api.tasks.compile.JavaCompile:destinationDir) | `compileJSP.temporaryDir`\n[`source`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html#org.gradle.api.tasks.compile.JavaCompile:source) | `generateJSPJava.outputs`\n\n### CompileJSPTask\n\nTasks of type `CompileJSPTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, such as [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.css.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize),\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.liferay.jasper.jspc.JspC\"`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"destinationdir\"></a>`destinationDir` | `File` | `null` | The directory where the the JSP files are compiled to. Package directories are automatically generated based on the directories containing the uncompiled JSP files. It sets the `-d` argument.\n<a name=\"jspcclasspath\"></a>`jspCClasspath` | `FileCollection` | `null` | The classpath to use for the JSP files compilation.\n<a name=\"webappdir\"></a>`webAppDir` | `File` | `null` | The directory containing the web application. All JSP files in the directory and its subdirectories are compiled. It sets the `-webapp` argument.\n\nThe properties of type `File` support any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\n\n## Additional Configuration\n\nThere are additional configurations that can help you use Jasper JSPC.\n\n### JSP Compilation Classpath\n\nThe plugin creates a configuration called `jspC` and adds several dependencies\nat the end of the configuration phase of the project:\n\n- the JAR file of the project generated by the [`jar`](https://docs.gradle.org/current/userguide/java_plugin.html#sec:jar) task.\n- the output files of the `main` source set.\n- the `compileClasspath` file collection of the `main` source set.\n\nIf necessary, it is possible to add more dependencies to the `jspC`\nconfiguration.\n\n### Liferay Jasper JSPC Dependency\n\nBy default, the plugin creates a configuration called `jspCTool` and adds a\ndependency to the latest released version of the Liferay Jasper JSPC. It is\npossible to override this setting and use a specific version of the tool by\nmanually adding a dependency to the `jspCTool` configuration:\n\n```groovy\ndependencies {\n    jspCTool group: \"com.liferay\", name: \"com.liferay.jasper.jspc\", version: \"1.0.11\"\n    jspCTool group: \"org.apache.ant\", name: \"ant\", version: \"1.9.4\"\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/javadoc-formatter-gradle-plugin.markdown",
    "content": "---\nheader-id: javadoc-formatter-gradle-plugin\n---\n\n# Javadoc Formatter Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Javadoc Formatter Gradle plugin lets you format project Javadoc comments\nusing the [Liferay Javadoc Formatter tool](https://github.com/liferay/liferay-portal/tree/master/modules/util/javadoc-formatter).\nThe tool lets you generate:\n\n- Default [`@author`](http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html#@author)\n  tags to all classes.\n- Comment stubs to classes, fields, and methods.\n- Missing [`@Override`](https://docs.oracle.com/javase/8/docs/api/java/lang/Override.html)\n  annotations.\n- An XML representation of the Javadoc comments, which can be used by tools in\n  order to index the Javadocs of the project.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.javadoc.formatter\", version: \"1.0.27\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.javadoc.formatter\"\n```\n\nSince the plugin automatically resolves the Liferay Javadoc Formatter library as\na dependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`formatJavadoc` | \\- | [`FormatJavadocTask`](#formatjavadoctask) | Runs the Liferay Javadoc Formatter to format files.\n\n### FormatJavadocTask\n\nTasks of type `FormatJavadocTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, like [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.lang.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize),\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args) | Javadoc Formatter command line arguments\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.javadocFormatter`](#liferay-javadoc-formatter-dependency)\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.liferay.javadoc.formatter.JavadocFormatter\"`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`author` | `String` | `\"Brian Wing Shun Chan\"` | The value of the `@author` tag to add at class level if missing. It sets the `javadoc.author` argument.\n`generateXML` | `boolean` | `false` | Whether to generate a XML representation of the Javadoc comments. The XML files are generated in the `src/main/resources` directory only if the Java files are contained in `src/main/java`. It sets the `javadoc.generate.xml` argument.\n`initializeMissingJavadocs` | `boolean` | `false` | Whether to add comment stubs at the class, field, and method levels. If `false`, only the class-level `@author` is added. It sets the `javadoc.init` argument.\n`limits` | `List<String>` | `[]` | The Java file name patterns, relative to [`workingDir`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:workingDir), to include when formatting Javadoc comments. The patterns must be specified without the `.java` file type suffix. If empty, all Java files are formatted. It sets the `javadoc.limit` argument.\n`lowestSupportedJavaVersion` | `double` | `1.7` | If a method is annotated with the [`@SinceJava`](https://github.com/liferay/liferay-portal/blob/master/modules/util/javadoc-formatter/src/main/java/com/liferay/javadoc/formatter/SinceJava.java) annotation and its `value` argument is greater than the value specified for the `lowestSupportedJavaVersion` property, then the `@Override` annotation is not automatically added, even if it is missing. It sets the `javadoc.lowest.supported.java.version` argument. See [LPS-37353](https://issues.liferay.com/browse/LPS-37353).\n`outputFilePrefix` | `String` | `\"javadocs\"` | The file name prefix of the XML representation of the Javadoc comments. If `generateXML` is `false`, this property is not used. It sets the `javadoc.output.file.prefix` argument.\n`updateJavadocs` | `boolean` | `false` | Whether to fix existing comment blocks by adding missing tags. It sets the `javadoc.update` argument.\n\nIt is possible to use Closures and Callables as values for the `String`\nproperties, to defer evaluation until task execution.\n\n#### Task Methods\n\nMethod | Description\n------ | -----------\n`FormatJavadocTask dirNames(Iterable<Object> limits)` | Adds Java file name patterns, relative to `workingDir`, to include when formatting Javadoc comments.\n`FormatJavadocTask dirNames(Object... limits)` | Adds Java file name patterns, relative to `workingDir`, to include when formatting Javadoc comments.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the Javadoc Formatter.\n\n### Liferay Javadoc Formatter Dependency\n\nBy default, the plugin creates a configuration called `javadocFormatter` and adds\na dependency to the latest released version of the Liferay Javadoc Formatter. It\nis possible to override this setting and use a specific version of the tool by\nmanually adding a dependency to the `javadocFormatter` configuration:\n\n```groovy\ndependencies {\n    javadocFormatter group: \"com.liferay\", name: \"com.liferay.javadoc.formatter\", version: \"1.0.32\"\n}\n```\n\nIf the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin is applied, the `javadocFormatter` configuration automatically extends\nfrom the [`compile`](https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_plugin_and_dependency_management)\nconfiguration.\n\n### System Properties\n\nIt is possible to set the default values of the `generateXML`,\n`initializeMissingJavadocs`, `limits`, and `updateJavadocs` properties for a\n`FormatJavadocTask` task via system properties:\n\n- `-D${task.name}.generate.xml=true`\n- `-D${task.name}.init=SomeClassName1,SomeClassName2,com.liferay.portal.**`\n- `-D${task.name}.limit=**/com/example/`\n- `-D${task.name}.update=true`\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/js-module-config-generator-gradle-plugin.markdown",
    "content": "---\nheader-id: js-module-config-generator-gradle-plugin\n---\n\n# JS Module Config Generator Gradle Plugin\n\n[TOC levels=1-4]\n\nThe JS Module Config Generator Gradle plugin lets you run the\n[Liferay AMD Module Config Generator](https://github.com/liferay/liferay-module-config-generator)\nto generate the configuration file needed to load AMD files via combo loader in\nLiferay.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.js.module.config.generator\", version: \"2.1.57\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.js.module.config.generator\"\n```\n\nThe JS Module Config Generator plugin automatically applies the\n[`com.liferay.node`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-node)\nplugin.\n\n## Project Extension\n\nThe JS Module Config Generator plugin exposes the following properties through\nthe extension named `jsModuleConfigGenerator`:\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"version\"></a>`version` | `String` | `\"1.2.1\"` | The version of the Liferay AMD Module Config Generator to use.\n\n## Tasks\n\nThe plugin adds two tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`configJSModules` | `downloadLiferayModuleConfigGenerator`, `processResources` | [`ConfigJSModulesTask`](#configjsmodulestask) | Generates the configuration file needed to load AMD files via combo loader in Liferay.\n`downloadLiferayModuleConfigGenerator` | `downloadNode` | `DownloadNodeModuleTask` | Downloads the Liferay AMD Module Config Generator in the project's `node_modules` directory.\n\nBy default, the `downloadLiferayModuleConfigGenerator` task downloads the\nversion of `liferay-module-config-generator` declared in the\n[`jsModuleConfigGenerator.version`](#version) property. If the project's\n`package.json` file, however, already lists the\n`liferay-module-config-generator` package in its `dependencies` or\n`devDependencies`, the\n`downloadLiferayModuleConfigGenerator` task is disabled.\n\nThe `configJSModules` task is automatically configured with sensible defaults,\ndepending on whether the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin is applied:\n\nProperty Name | Default Value\n------------- | -------------\n[`moduleConfigFile`](#moduleconfigfile) | `\"${project.projectDir}/package.json\"`\n[`outputFile`](#outputfile) | `\"${sourceSets.main.output.resourcesDir}/META-INF/config.json\"`\n[`sourceDir`](#sourcedir) | `\"${sourceSets.main.output.resourcesDir}/META-INF/resources\"`\n\nThe plugin also adds the following dependencies to tasks defined by the `java`\nplugin:\n\nName | Depends On\n---- | ----------\n`classes` | `configJSModules`\n\nIf the [`com.liferay.js.transpiler`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-js-transpiler)\nplugin is applied, the `configJSModules` task is configured to always run after\nthe `transpileJS` task.\n\n### ConfigJSModulesTask\n\nTasks of type `ConfigJSModulesTask` extend `ExecuteNodeScriptTask`, so all its\nproperties and methods, such as `args`, `inheritProxy`, and `workingDir`, are\navailable. The `ConfigJSModulesTask` instances also implement the\n[`PatternFilterable`](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/util/PatternFilterable.html)\ninterface, which lets you specify include and exclude patterns for the files in\n[`sourceDir`](#sourcedir) to process.\n\nThey also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`includes`](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/util/PatternFilterable.html#getIncludes\\(\\)) | `[\"**/*.es.js*\", \"**/*.soy.js*\"]`\n`scriptFile` | `\"${downloadLiferayModuleConfigGenerator.moduleDir}/bin/index.js\"`\n\nThe purpose of this task is to run the Liferay AMD Module Config Generator from\nthe included files in [`sourceDir`](#sourcedir). The generator processes these\nfiles and creates a configuration file in the location specified by the\n[`outputFile`](#outputfile) property.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`configVariable` | `String` | `null` | The configuration variable to which the modules should be added. It sets the `--config` argument.\n`customDefine` | `String` | `\"Liferay.Loader\"` | The namespace of the `define(...)` call to use in the JS files. It sets the `--namespace` argument.\n`ignorePath` | `boolean` | `false` | Whether not to create module `path` and `fullPath` properties. It sets the `--ignorePath` argument.\n`keepFileExtension` | `boolean` | `false` | Whether to keep the file extension when generating the module name. It sets the `--keepExtension` argument.\n`lowerCase` | `boolean` | `false` | Whether to convert file name to lower case before using it as the module name. It sets the `--lowerCase` argument.\n<a name=\"moduleconfigfile\"></a>`moduleConfigFile` | `File` | `null` | The JSON file which contains configuration data for the modules. It sets the `--moduleConfig` argument.\n`moduleExtension` | `String` | `null` | The extension for the module file (e.g., `.js`). If specified, use the provided string as an extension instead to get it automatically from the file name. It sets the `--extension` argument.\n`moduleFormat` | `String` | `null` | The regular expression and value to apply to the file name when generating the module name. It sets the `--format` argument.\n<a name=\"outputfile\"></a>`outputFile` | `File` | `null` | The file where the generated configuration is stored. It sets the `--output` argument.\n<a name=\"sourcedir\"></a>`sourceDir` | `File` | `null` | The directory that contains the files to process.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the `int`\nand `String` properties to defer evaluation until task execution.\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/js-transpiler-gradle-plugin.markdown",
    "content": "---\nheader-id: js-transpiler-gradle-plugin\n---\n\n# JS Transpiler Gradle Plugin\n\n[TOC levels=1-4]\n\nThe JS Transpiler Gradle plugin lets you run [`metal-cli`](https://github.com/metal/metal-cli)\nto build [Metal.js](http://metaljs.com/) code, compile Soy files, and transpile\nES6 to ES5.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.js.transpiler\", version: \"2.4.36\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.js.transpiler\"\n```\n\nThere are two JS Transpiler Gradle plugins you can apply to your project:\n\n- [*JS Transpiler Plugin*](#js-transpiler-plugin): builds Metal.js code,\ncompiles Soy files, and transpiles ES6 to ES5:\n\n    ```groovy\n    apply plugin: \"com.liferay.js.transpiler\"\n    ```\n\n- [*JS Transpiler Base Plugin*](#js-transpiler-base-plugin): provides a way to\nuse Gradle dependencies (such as an [external module](https://docs.gradle.org/current/userguide/dependency_management.html#sub:module_dependencies)\nor [project dependencies](https://docs.gradle.org/current/userguide/dependency_management.html#sub:project_dependencies))\nin Node.js scripts:\n\n    ```groovy\n    apply plugin: \"com.liferay.js.transpiler.base\"\n    ```\n\n## JS Transpiler Plugin\n\nThe JS Transpiler plugin automatically applies the [*JS Transpiler Base Plugin*](#js-transpiler-base-plugin).\n\nThe plugin adds two tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`downloadMetalCli` | `downloadNode` | `DownloadNodeModuleTask` | Downloads `metal-cli` in the project's `node_modules` directory.\n`transpileJS` | `downloadMetalCli`, `expandJSCompileDependencies`, `npmInstall`, `processResources` | [`TranspileJSTask`](#transpilejstask) | Builds Metal.js code.\n\nBy default, the `downloadMetalCli` task downloads the version 1.3.1 of\n`metal-cli`. If the project's `package.json` file, however, already lists the\n`metal-cli` package in its `dependencies` or `devDependencies`, the\n`downloadMetalCli` task is disabled.\n\nThe `transpileJS` task is automatically configured with sensible defaults,\ndepending on whether the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin is applied:\n\nProperty Name | Default Value\n------------- | -------------\n[`sourceDir`](#sourcedir) | The directory `META-INF/resources` in the first `resources` directory of the `main` source set (by default, `src/main/resources/META-INF/resources`).\n`workingDir` | `\"${sourceSets.main.output.resourcesDir}/META-INF/resources\"`\n\nThe plugin also adds the following dependencies to tasks defined by the `java`\nplugin:\n\nName | Depends On\n---- | ----------\n`classes` | `transpileJS`\n\nThe plugin adds a new configuration to the project called `soyCompile`. If one\nor more dependencies are added to this configuration, they will be expanded into\ntemporary directories and passed to the `transpileJS` task as additional\n[`soyDependencies`](#soydependencies) values.\n\n## JS Transpiler Base Plugin\n\nThe JS Transpiler Base plugin automatically applies the [`com.liferay.node`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-node)\nplugin.\n\nThe plugin adds a new configuration to the project called `jsCompile`. If one or\nmore dependencies are added to this configuration, they will be expanded into\nsub-directories of the `node_modules` directory, with names equal to the names\nof the dependencies.\n\nThe plugin also adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`expandJSCompileDependencies` | \\- | [`DefaultTask`](https://docs.gradle.org/current/javadoc/org/gradle/api/DefaultTask.html) | Expands the additional configured JavaScript dependencies. The task itself does not do any work, but depends on a series of [Copy](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html) tasks called `expandJSCompileDependency${file}`, which expand each dependency declared in the `jsCompile` configuration into the `node_modules` directory.\n\nAll the tasks of type `ExecuteNpmTask` whose name starts with `\"npmRun\"` are\nconfigured to depend on `expandJSCompileDependencies`. This means that, before\nrunning any [script](https://docs.npmjs.com/misc/scripts) declared in the\n`package.json` file of the project, all the `jsCompile` dependencies will be\nexpanded into the `node_modules` directory.\n\n## Tasks\n\n### TranspileJSTask\n\nTasks of type `TranspileJSTask` extend `ExecuteNodeScriptTask`, so all its\nproperties and methods, such as `args`, `inheritProxy`, and `workingDir`, are\navailable. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n`scriptFile` | `\"${downloadMetalCli.moduleDir}/index.js\"`\n`soySrcIncludes` | `[\"**/*.soy\"]`\n`srcIncludes` | `[\"**/*.es.js*\", \"**/*.soy.js*\"]`\n\nThe purpose of this task is to run the `build` command of `metal-cli` to\nbuild Metal.js code from [`sourceDir`](#sourcedir) into the `workingDir`\ndirectory.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`bundleFileName` | `String` | `null` | The name of the final bundle file for formats (e.g., *globals*) that create one. It sets the `--bundleFileName` argument.\n`globalName` | `String` | `null` | The name of the global variable that holds exported modules. It sets the `--globalName` argument. This is only used by the *globals* format build.\n`moduleName` | `String` | `null` | The name of the project that is being compiled. All built modules are stored in a folder with this name. It sets the `--moduleName` argument. This is only used by the *amd* format build.\n`modules` | `String` | `\"amd\"` | The format(s) that the source files are built to. It sets the `--format` argument.\n`skipWhenEmpty` | `boolean` | `true` | Whether to disable the task and remove its dependencies if the [`sourceFiles`](#sourcefiles) property does not return any file at the end of the project evaluation.\n<a name=\"sourcedir\"></a>`sourceDir` | `File` | `null` | The directory that contains the files to build.\n<a name=\"sourcefiles\"></a>`sourceFiles` | `FileCollection` | `[]` | The Soy and JS files to compile. *(Read-only)*\n`sourceMaps` | `SourceMaps` | `enabled` | Whether to generate source map files. Available values include `disabled`, `enabled`, and `enabled_inline`.\n<a name=\"soydependencies\"></a>`soyDependencies` | `Set<String>` | `[\"${npmInstall.workingDir}/node_modules/clay*/src/**/*.soy\", \"${npmInstall.workingDir}/node_modules/metal*/src/**/*.soy\"]` | The path GLOBs of Soy files that the main source files depend on, but that should not be compiled. It sets the `--soyDeps` argument.\n`soySkipMetalGeneration` | `boolean` | `false` | Whether to just compile Soy files, without adding Metal.js generated code, like the `component` class. It sets the `--soySkipMetalGeneration` argument.\n`soySrcIncludes` | `Set<String>` | `[]` | The path GLOBs of the Soy files to compile. It sets the `--soySrc` argument.\n`srcIncludes` | `Set<String>` | `[]` | The path GLOBs of the JS files to compile. It sets the `--src` argument.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the `int`\nand `String` properties to defer evaluation until task execution.\n\n#### Task Methods\n\nMethod | Description\n------ | -----------\n`TranspileJSTask soyDependency(Iterable<?> soyDependencies)` | Adds path GLOBs of Soy files that the main source files depend on, but that should not be compiled.\n`TranspileJSTask soyDependency(Object... soyDependencies)` | Adds path GLOBs of Soy files that the main source files depend on, but that should not be compiled.\n`TranspileJSTask soySrcInclude(Iterable<?> soySrcIncludes)` | Adds path GLOBs of Soy files to compile.\n`TranspileJSTask soySrcInclude(Object... soySrcIncludes)` | Adds path GLOBs of Soy files to compile.\n`TranspileJSTask srcInclude(Iterable<?> srcIncludes)` | Adds path GLOBs of JS files to compile.\n`TranspileJSTask srcInclude(Object... srcIncludes)` | Adds path GLOBs of JS files to compile.\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/jsdoc-gradle-plugin.markdown",
    "content": "---\nheader-id: jsdoc-gradle-plugin\n---\n\n# JSDoc Gradle Plugin\n\n[TOC levels=1-4]\n\nThe JSDoc Gradle plugin lets you run the [JSDoc](http://usejsdoc.org/) tool in\norder to generate documentation for your project's JavaScript files.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.jsdoc\", version: \"2.0.33\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n```\n\nThere are two JSDoc Gradle plugins you can apply to your project:\n\n- Apply the [JSDoc Plugin](#jsdoc-plugin) to generate JavaScript documentation\nfor your project:\n\n    ```groovy\n    apply plugin: \"com.liferay.jsdoc\"\n    ```\n\n- Apply the [App JSDoc Plugin](#appjsdoc-plugin) in a parent project to\ngenerate the JavaScript documentation as a single, combined HTML document for an\napplication that spans different subprojects, each one representing a different\ncomponent of the same application:\n\n    ```groovy\n    apply plugin: \"com.liferay.app.jsdoc\"\n    ```\n\nBoth plugins automatically apply the [`com.liferay.node`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-node)\nplugin.\n\n## JSDoc Plugin\n\nThe plugin adds two tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`downloadJSDoc` | `downloadNode` | `DownloadNodeModuleTask` | Downloads JSDoc in the project's `node_modules` directory.\n`jsdoc` | `downloadJSDoc` | [`JSDocTask`](#jsdoctask) | Generates API documentation for the project's JavaScript code.\n\nBy default, the `downloadJSDoc` task downloads version `3.5.5` of the `jsdoc`\npackage. If the project's `package.json` file, however, already lists the\n`jsdoc` package in its `dependencies` or `devDependencies`, the `downloadJSDoc`\ntask is disabled.\n\nThe `jsdoc` task is automatically configured with sensible defaults,\ndepending on whether the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin is applied:\n\nProperty Name | Default Value\n------------- | -------------\n[`destinationDir`](#destinationdir) | <p>**If the `java` plugin is applied:** `\"${project.docsDir}/jsdoc\"`</p><p>**Otherwise:** `\"${project.buildDir}/jsdoc\"`</p>\n[`sourceDirs`](#sourcedirs) | The directory `META-INF/resources` in the first `resources` directory of the `main` source set (by default, `src/main/resources/META-INF/resources`).\n\n## AppJSDoc Plugin\n\nTo use the App JSDoc plugin, it is required to apply the `com.liferay.app.jsdoc`\nplugin in a parent project (that is, a project that is a\ncommon ancestor of all the subprojects representing the various components of\nthe app). It is also required to apply the [`com.liferay.jsdoc`](#jsdoc-plugin)\nplugin to all the subprojects that contain JavaScript files.\n\nThe App JSDoc plugin adds three tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`appJSDoc` | `downloadJSDoc` | [`JSDocTask`](#jsdoctask) | Generates API documentation for the app's JavaScript code.\n`downloadJSDoc` | `downloadNode` | `DownloadNodeModuleTask` | Downloads JSDoc in the app's `node_modules` directory.\n`jarAppJSDoc` | `appJSDoc` | [`Jar`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html) | Assembles a JAR archive containing the JavaScript documentation files for this app.\n\nBy default, the `downloadJSDoc` task downloads version `3.5.5` of the `jsdoc`\npackage. If the project's `package.json` file, however, already lists the\n`jsdoc` package in its `dependencies` or `devDependencies`, the `downloadJSDoc`\ntask is disabled.\n\nThe `appJSDoc` task is automatically configured with sensible defaults:\n\nProperty Name | Default Value\n------------- | -------------\n[`destinationDir`](#destinationdir) | `${project.buildDir}/docs/jsdoc`\n[`sourceDirs`](#sourcedirs) | The sum of all the `jsdoc.sourceDirs` values of the subprojects.\n\n## Project Extension\n\nThe App JSDoc plugin exposes the following properties through the extension\nnamed `appJSDocConfiguration`:\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`subprojects` | `Set<Project>` | `project.subprojects` | The subprojects to include in the JavaScript documentation of the app.\n\nThe same extension exposes the following methods:\n\nMethod | Description\n------ | -----------\n`AppJSDocConfigurationExtension subprojects(Iterable<Project> subprojects)` | Include additional projects in the JavaScript documentation of the app.\n`AppJSDocConfigurationExtension subprojects(Project... subprojects)` | Include additional projects in the JavaScript documentation of the app.\n\n## Tasks\n\n### JSDocTask\n\nTasks of type `JSDocTask` extend `ExecuteNodeScriptTask`, so all its\nproperties and methods, such as `args`, `inheritProxy`, and `workingDir`, are\navailable.\n\nThey also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n`scriptFile` | `\"${downloadJSDoc.moduleDir}/jsdoc.js\"`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`configuration` | [`TextResource`](https://docs.gradle.org/current/dsl/org.gradle.api.resources.TextResource.html) | `null` | The JSDoc configuration file. It sets the `--configure` argument.\n<a name=\"destinationdir\"></a>`destinationDir` | `File` | `null` | The directory where the JavaScript API documentation files are saved. It sets the `--destination` argument.\n`packageJsonFile` | `File` | `\"${project.projectDir}/package.json\"` | The path to the project's package file. It sets the `--package` argument.\n<a name=\"sourcedirs\"></a>`sourceDirs` | `FileCollection` | `[]` | The directories that contains the files to process.\n`readmeFile` | `File` | `null` | The path to the project's README file. It sets the `--readme` argument.\n`tutorialsDir` | `File` | `null` | The directory in which JSDoc should search for tutorials. It sets the `--tutorials` argument.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file(java.css.Object)).\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/lang-builder-gradle-plugin.markdown",
    "content": "---\nheader-id: lang-builder-gradle-plugin\n---\n\n# Lang Builder Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Lang Builder Gradle plugin lets you run the [Liferay Lang Builder](https://github.com/liferay/liferay-portal/tree/master/modules/util/lang-builder)\ntool to sort and translate the language keys in your project.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.lang.builder\", version: \"3.0.12\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.lang.builder\"\n```\n\nSince the plugin automatically resolves the Liferay Lang Builder library as a\ndependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t}\n}\n```\n\nSee [this page](/docs/7-2/frameworks/-/knowledge_base/f/automatically-generating-translations)\non the *Liferay Developer Network* for more information about usage of the Lang\nBuilder Gradle plugin.\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`buildLang` | \\- | [`BuildLangTask`](#buildlangtask) | Runs Liferay Lang Builder to translate language property files.\n\nThe `buildLang` task is automatically configured with sensible defaults,\ndepending on whether the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin is applied:\n\nProperty Name | Default Value\n------------- | -------------\n[`langDir`](#langdir) | <p>**If the `java` plugin is applied:** The directory `content` in the first `resources` directory of the `main` source set (by default: `src/main/resources/content`).</p><p>**Otherwise:** `null`</p>\n\n### BuildLangTask\n\nTasks of type `BuildLangTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, such as [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.lang.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize),\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args) | Lang Builder command line arguments\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.langBuilder`](#liferay-lang-builder-dependency)\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.liferay.lang.builder.LangBuilder\"`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`excludedLanguageIds` | `Set<String>` | `[\"da\", \"de\", \"fi\", \"ja\", \"nl\", \"pt_PT\", \"sv\"]` | The language IDs to exclude in the automatic translation. It sets the `lang.excluded.language.ids` argument.\n<a name=\"langdir\"></a>`langDir` | `File` | `null` | The directory where the language properties files are saved. It sets the `lang.dir` argument.\n`langFileName` | `String` | `\"Language\"` | The file name prefix of the language properties files (e.g., `Language_it.properties`). It sets the `lang.file` argument.\n`plugin` | `boolean` | `true` | Whether to check for duplicate language keys between the project and the portal. If `portalLanguagePropertiesFile` is not set, this property has no effect. It sets the `lang.plugin` argument.\n`portalLanguagePropertiesFile` | `File` | `null` | The `Language.properties` file of the portal. It sets the `lang.portal.language.properties.file` argument.\n`translate` | `boolean` | `true` | Whether to translate the language keys and generate a language properties file for each locale that's supported by Liferay. It sets the `lang.translate` argument.\n`translateSubscriptionKey` | `String` | `null` | The subscription key for Microsoft Translation integration. Subscription to the Translator Text Translation API on Microsoft Cognitive Services is required. Basic subscriptions, up to 2 million characters a month, are free. See [here](http://docs.microsofttranslator.com/text-translate.html) for more information. It sets the `lang.translate.subscription.key` argument.\n\nThe properties of type `File` support any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.lang.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties, to defer evaluation until task execution.\n\n#### Task Methods\n\nMethod | Description\n------ | -----------\n`BuildLangTask excludedLanguageIds(Iterable<Object> excludedLanguageIds)` | Adds language IDs to exclude in the automatic translation.\n`BuildLangTask excludedLanguageIds(Object... excludedLanguageIds)` | Adds language IDs to exclude in the automatic translation.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the Lang Builder.\n\n### Liferay Lang Builder Dependency\n\nBy default, the plugin creates a configuration called `langBuilder` and adds a\ndependency to the latest released version of the Liferay Lang Builder. It is\npossible to override this setting and use a specific version of the tool by\nmanually adding a dependency to the `langBuilder` configuration:\n\n```groovy\ndependencies {\n    langBuilder group: \"com.liferay\", name: \"com.liferay.lang.builder\", version: \"1.0.31\"\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/maven-plugin-builder-gradle-plugin.markdown",
    "content": "---\nheader-id: maven-plugin-builder-gradle-plugin\n---\n\n# Maven Plugin Builder Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Maven Plugin Builder Gradle Plugin lets you generate the\n[Maven plugin descriptor](https://maven.apache.org/ref/current/maven-plugin-api/plugin.html)\nfor any [Mojos](https://maven.apache.org/general.html#What_is_a_Mojo) found in\nyour project.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.maven.plugin.builder\", version: \"1.2.4\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.maven.plugin.builder\"\n```\n\n## Tasks\n\nThe plugin adds two tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`buildPluginDescriptor` |[`compileJava`](https://docs.gradle.org/current/userguide/java_plugin.html#sec:compile), [`WriteMavenSettings`](#writemavensettings) | [`BuildPluginDescriptorTask`](#buildplugindescriptortask) | Generates the Maven plugin descriptor for the project.\n<a name=\"writemavensettings\"></a>`WriteMavenSettings` | \\- | [`WriteMavenSettingsTask`](#writemavensettingstask) | Writes a temporary Maven settings file to be used during subsequent Maven invocations.\n\nThe Maven Plugin Builder Plugin automatically applies the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin.\n\nThe plugin also adds the following dependencies to tasks defined by the [`maven`](https://docs.gradle.org/current/userguide/maven_plugin.html)\nplugin:\n\nName | Depends On\n---- | ----------\n`install`, `uploadArchives`, and all the other tasks of type [`Upload`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Upload.html) | `buildPluginDescriptor`\n\nThe `buildPluginDescriptor` task is automatically configured with sensible\ndefaults:\n\nProperty Name | Default Value\n------------- | -------------\n[`classesDir`](#classesdir) | `sourceSets.main.output.classesDir`\n[`mavenEmbedderClasspath`](#mavenembedderclasspath) | [`configurations.mavenEmbedder`](#maven-embedder-dependency)\n[`mavenSettingsFile`](#mavensettingsfile) | [`writeMavenSettings.outputFile`](#outputfile)\n[`outputDir`](#outputdir) | The directory `META-INF/maven` in the first `resources` directory of the `main` source set (by default: `src/main/resources/META-INF/maven`).\n[`pomArtifactId`](#pomartifactid) | The bundle symbolic name of the project inferred via the [`OsgiHelper`](https://github.com/gradle/gradle/blob/master/subprojects/osgi/src/main/java/org/gradle/api/internal/plugins/osgi/OsgiHelper.java) class.\n[`pomGroupId`](#pomgroupid) | [`project.group`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:group)\n[`pomVersion`](#pomversion) | [`project.version`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:version) (if it ends with `\"-SNAPSHOT\"`, the suffix will be removed)\n[`sourceDir`](#sourcedir) | The first `java` directory of the `main` source set (by default: `src/main/java`).\n\nThe plugin ensures that the `processResources` task always runs before\n`buildPluginDescriptor` to let `processResources` copy the newly generated files\nin the `buildPluginDescriptor.outputDir` directory.\n\nThe `writeMavenSettings` task is also automatically configured with sensible\ndefaults:\n\nProperty Name | Default Value\n------------- | -------------\n[`localRepositoryDir`](#localrepositorydir) | `maven.repo.local` system property\n[`nonProxyHosts`](#nonproxyhosts) | `http.nonProxyHosts` system property\n[`outputFile`](#outputfile) | `\"${project.buildDir}/settings.xml\"`\n[`proxyHost`](#proxyhost) | `http.ProxyHost` or `https.proxyHost` system property (depending on the protocol of [`repositoryUrl`](#repositoryurl))\n[`proxyPassword`](#proxypassword) | `http.ProxyPassword` or `https.proxyPassword` system property (depending on the protocol of [`repositoryUrl`](#repositoryurl))\n[`proxyPort`](#proxyport) | `http.ProxyPort` or `https.proxyPort` system property (depending on the protocol of [`repositoryUrl`](#repositoryurl))\n[`proxyUser`](#proxyuser) | `http.ProxyUser` or `https.proxyUser` system property (depending on the protocol of [`repositoryUrl`](#repositoryurl))\n[`repositoryUrl`](#repositoryurl) | `repository.url` system property\n\nIf running on JDK8+, the plugin also disables the [*doclint*](http://docs.oracle.com/javase/8/docs/technotes/tools/unix/javadoc.html#BEJEFABE)\nfeature in all tasks of type [`Javadoc`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html).\n\n### BuildPluginDescriptorTask\n\nTasks of type `BuildPluginDescriptorTask` work by generating a temporary\n`pom.xml` file based on the project, and then invoking the [Maven Embedder](http://maven.apache.org/ref/3.3.9/maven-embedder/)\nto build the Maven plugin descriptor.\n\nIt is possible to declare information for the plugin descriptor generation using\neither [Java 5 Annotations](https://maven.apache.org/plugin-tools/maven-plugin-tools-annotations/)\nor [Javadoc Tags](https://maven.apache.org/plugin-tools/maven-plugin-tools-java/).\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"classesdir\"></a>`classesDir` | `File` | `null` | The directory that contains the compiled classes. It sets the value of the [`build.outputDirectory`](http://maven.apache.org/ref/3.3.9/maven-model/maven.html#class_build) element in the generated `pom.xml` file.\n`configurationScopeMappings` | `Map<String, String>` | `[\"compile\": \"compile\", \"provided\", \"provided\"]` | The mapping between the configuration names in the Gradle project and the [dependency scopes](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope) in the `pom.xml` file. It is used to add [`dependencies.dependency`](http://maven.apache.org/ref/3.3.3/maven-model/maven.html#class_dependency) elements in the generated `pom.xml` file.\n`forcedExclusions` | `Set<String>` | `[]` | The *group:name:version* notation of the dependencies to always exclude from the ones added in the `pom.xml` file. It adds [`dependencies.dependency.exclusions.exclusion`](http://maven.apache.org/ref/3.3.3/maven-model/maven.html#class_exclusion) elements to the generated `pom.xml` file.\n`goalPrefix` | `String` | `null` | The goal prefix for the Maven plugin specified in the descriptor. It sets the value of the [`build.plugins.plugin.configuration.goalPrefix`](https://maven.apache.org/plugin-tools/maven-plugin-plugin/examples/generate-descriptor.html) element in the generated `pom.xml` file.\n`mavenDebug` | `boolean` | `false` | Whether to invoke the Maven Embedder in debug mode.\n<a name=\"mavenembedderclasspath\"></a>`mavenEmbedderClasspath` | `FileCollection` | `null` | The classpath used to invoke the Maven Embedder.\n`mavenEmbedderMainClassName` | `String` | `\"org.apache.maven.cli.MavenCli\"` | The Maven Embedder's main class name.\n`mavenPluginPluginVersion` | `String` | `\"3.4\"` | The version of the [Maven Plugin Plugin](https://maven.apache.org/plugin-tools/maven-plugin-plugin/) to use to generate the plugin descriptor for the project.\n<a name=\"mavensettingsfile\"></a>`mavenSettingsFile` | `File` | `null` | The custom `settings.xml` file to use. It sets the `--settings` argument on the Maven Embedder invocation.\n<a name=\"outputdir\"></a>`outputDir` | `File` | `null` | The directory where the Maven plugin descriptor files are saved.\n<a name=\"pomartifactid\"></a>`pomArtifactId` | `String` | `null` | The identifier for the artifact that is unique within the group. It sets the value of the [`project.artifactId`](http://maven.apache.org/ref/3.3.3/maven-model/maven.html#class_project) element in the generated `pom.xml` file.\n<a name=\"pomgroupid\"></a>`pomGroupId` | `String` | `null` | The universally unique identifier for the project. It sets the value of the [`project.groupId`](http://maven.apache.org/ref/3.3.3/maven-model/maven.html#class_project) element in the generated `pom.xml` file.\n`pomRepositories` | `Map<String, Object>` | `[\"liferay-public\": \"http://repository.liferay.com/nexus/content/groups/public\"]` | The name and URL of the remote repositories. It adds [`repositories.repository`](http://maven.apache.org/ref/3.3.3/maven-model/maven.html#class_repository) elements in the generated `pom.xml` file.\n<a name=\"pomversion\"></a>`pomVersion` | `String` | `null` | The version of the artifact produced by this project. It sets the value of the [`project.version`](http://maven.apache.org/ref/3.3.3/maven-model/maven.html#class_project) element in the generated `pom.xml` file.\n<a name=\"sourcedir\"></a>`sourceDir` | `String` | `null` | The directory that contains the source files.  It sets the value of the [`build.sourceDirectory`](http://maven.apache.org/ref/3.3.9/maven-model/maven.html#class_build) element in the generated `pom.xml` file.\n`useSetterComments` | `boolean` | `true` | Whether to allow [Mojo Javadoc Tags](https://maven.apache.org/plugin-tools/maven-plugin-tools-java/) in the setter methods of the Mojo.\n\nThe properties of type `File` support any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.lang.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties, to defer evaluation until task execution.\n\n### Task Methods\n\nMethod | Description\n------ | -----------\n`void configurationScopeMapping(String configurationName, String scope)` | Adds a mapping between a configuration name in the Gradle project and the dependency scope in the `pom.xml` file.\n`BuildPluginDescriptorTask forcedExclusions(Iterable<String> forcedExclusions)` | Adds *group:name:version* notations of dependencies to always exclude from the ones added in the `pom.xml` file.\n`BuildPluginDescriptorTask forcedExclusions(String... forcedExclusions)` | Adds *group:name:version* notations of dependencies to always exclude from the ones added in the `pom.xml` file.\n`BuildPluginDescriptorTask pomRepositories(Map<String, ?> pomRepositories` | Adds names and URLs of remote repositories in the `pom.xml` file.\n`BuildPluginDescriptorTask pomRepository(String id, Object url)` | Adds the name and URL of a remote repository in the `pom.xml` file.\n\n### WriteMavenSettingsTask\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"localrepositorydir\"></a>`localRepositoryDir` | `String` | `null` | The directory of the system's local repository. It sets the value of the [`localRepository`](https://maven.apache.org/settings.html#Simple_Values) element in the generated `settings.xml` file.\n<a name=\"nonproxyhosts\"></a>`nonProxyHosts` | `String` | `null` | The patterns of the host that should be accessed without going through the proxy. It sets the value of the [`proxies.proxy.nonProxyHosts`](https://maven.apache.org/settings.html#Proxies) element in the generated `settings.xml` file.\n<a name=\"outputfile\"></a>`outputFile` | `File` | `null` | The generated `settings.xml` file.\n<a name=\"proxyhost\"></a>`proxyHost` | `String` | `null` | The host name or address of the proxy server. It sets the value of the [`proxies.proxy.host`](https://maven.apache.org/settings.html#Proxies) element in the generated `settings.xml` file.\n<a name=\"proxypassword\"></a>`proxyPassword` | `String` | `null` | The password to use to access a protected proxy server. It sets the value of the [`proxies.proxy.password`](https://maven.apache.org/settings.html#Proxies) element in the generated `settings.xml` file.\n<a name=\"proxyport\"></a>`proxyPort` | `String` | `null` | The port number of the proxy server. It sets the value of the [`proxies.proxy.port`](https://maven.apache.org/settings.html#Proxies) element in the generated `settings.xml` file.\n<a name=\"proxyuser\"></a>`proxyUser` | `String` | `null` | The user name to use to access a protected proxy server. It sets the value of the [`proxies.proxy.username`](https://maven.apache.org/settings.html#Proxies) element in the generated `settings.xml` file.\n<a name=\"repositoryurl\"></a>`repositoryUrl` | `String` | `null` | The URL of the repository [mirror](https://maven.apache.org/guides/mini/guide-mirror-settings.html#Using_A_Single_Repository). It sets the value of the [`mirrors.mirror.url`](https://maven.apache.org/settings.html#Mirrors) element in the generated `settings.xml` file.\n\nThe properties of type `File` support any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.lang.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties, to defer evaluation until task execution.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the Maven Plugin\nBuilder.\n\n### Maven Embedder Dependency\n\nBy default, the plugin creates a configuration called `mavenEmbedder` and adds a\ndependency to the 3.3.9 version of the Maven Embedder. It is possible to\noverride this setting and use a specific version of the tool by manually adding\na dependency to the `mavenEmbedder` configuration:\n\n```groovy\ndependencies {\n    mavenEmbedder group: \"org.apache.maven\", name: \"maven-embedder\", version: \"3.3.9\"\n    mavenEmbedder group: \"org.apache.maven.wagon\", name: \"wagon-http\", version: \"2.10\"\n    mavenEmbedder group: \"org.eclipse.aether\", name: \"aether-connector-basic\", version: \"1.0.2.v20150114\"\n    mavenEmbedder group: \"org.eclipse.aether\", name: \"aether-transport-wagon\", version: \"1.0.2.v20150114\"\n    mavenEmbedder group: \"org.slf4j\", name: \"slf4j-simple\", version: \"1.7.5\"\n}\n```\n\n### System Properties\n\nIt is possible to set the default value of the `mavenDebug` property for a\n`BuildPluginDescriptorTask` task via system property:\n\n- `-D${task.name}.maven.debug=true`\n\nFor example, run the following Bash command to invoke the Maven Embedder in\ndebug mode to attach a remote debugger:\n\n```bash\n./gradlew buildPluginDescriptor -DbuildPluginDescriptor.maven.debug=true\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/node-gradle-plugin.markdown",
    "content": "---\nheader-id: node-gradle-plugin\n---\n\n# Node Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Node Gradle plugin lets you run [Node.js](https://nodejs.org/) and\n[NPM](https://www.npmjs.com/) as part of your build.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.node\", version: \"4.6.18\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.node\"\n```\n\n## Project Extension\n\nThe Node Gradle plugin exposes the following properties through the extension\nnamed `node`:\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"download\"></a>`download` | `boolean` | `true` | Whether to download and use a local and isolated Node.js distribution instead of the one installed in the system.\n`global` | `boolean` | `false` | Whether to use a single Node.js installation for the whole multi-project build. This reduces the time required to unpack the Node.js distribution and the time required to download NPM packages thanks to a shared packages cache. If `download` is `false`, this property has no effect.\n<a name=\"nodedir\"></a>`nodeDir` | `File` | <p>**If `global` is `true`:** `\"${rootProject.buildDir}/node\"`</p><p>**Otherwise:** `\"${project.buildDir}/node\"`</p> | The directory where the Node.js distribution is unpacked. If `download` is `false`, this property has no effect.\n`nodeUrl` | `String` | `\"http://nodejs.org/dist/v${node.nodeVersion}/node-v${node.nodeVersion}-${platform}-x${bitMode}.${extension}\"` | The URL of the Node.js distribution to download. If `download` is `false`, this property has no effect.\n`nodeVersion` | `String` | `\"5.5.0\"` | The version of the Node.js distribution to use. If `download` is `false`, this property has no effect.\n`npmArgs` | `List<String>` | `[]` | The arguments added automatically to every task of type [`ExecuteNpmTask`](#executenpmtask).\n`npmUrl` | `String` | `\"https://registry.npmjs.org/npm/-/npm-${node.npmVersion}.tgz\"` | The URL of the NPM version to download. If `download` is `false`, this property has no effect.\n`npmVersion` | `String` | `null` | The version of NPM to use. If `null`, the version of NPM embedded inside the Node.js distribution is used. If `download` is `false`, this property has no effect.\n\nIt is possible to override the default value of the `download` property by\nsetting the `nodeDownload` project property. For example, this can be done via\ncommand line argument:\n\n```bash\n./gradlew -PnodeDownload=false npmInstall\n```\n\nThe same extension exposes the following methods:\n\nMethod | Description\n------ | -----------\n`NodeExtension npmArgs(Iterable<?> npmArgs)` | Adds arguments to automatically add to every task of type [`ExecuteNpmTask`](#executenpmtask).\n`NodeExtension npmArgs(Object... npmArgs)` | Adds arguments to automatically add to every task of type [`ExecuteNpmTask`](#executenpmtask).\n\nThe properties of type `File` support any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for `String`,\nto defer evaluation until execution.\n\nPlease note that setting the `global` property of the `node` extension via the\ncommand line is not supported. It can only be set via Gradle script, which can\nbe done by adding the following code to the `build.gradle` file in the root of\na project (e.g., Liferay Workspace):\n\n```groovy\nallprojects {\n    plugins.withId(\"com.liferay.node\") {\n        node.global = true\n    }\n}\n```\n\n## Tasks\n\nThe plugin adds a series of tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`cleanNPM` | \\- | [`Delete`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Delete.html) | Deletes the `node_modules` directory, the `npm-shrinkwrap.json` file and the `package-lock.json` files from the project, if present.\n<a name=\"downloadnode\"></a>`downloadNode` | \\- | [`DownloadNodeTask`](#downloadnodetask) | Downloads and unpacks the local Node.js distribution for the project. If `node.download` is `false`, this task is disabled.\n`npmInstall` | `downloadNode` | [`NpmInstallTask`](#npminstalltask) | Runs `npm install` to install the dependencies declared in the project's `package.json` file, if present. By default, the task is [configured](#npminstallretries) to run `npm install` two more times if it fails.\n[`npmRun${script}`](#npmrunscript-task) | `npmInstall` | [`ExecuteNpmTask`](#executenpmtask) | Runs the `${script}` NPM script.\n`npmPackageLock` | `cleanNPM`, `npmInstall` | [`DefaultTask`](https://docs.gradle.org/current/javadoc/org/gradle/api/DefaultTask.html) | Deletes the NPM files and runs `npm install` to install the dependencies declared in the project's `package.json` file, if present.\n`npmShrinkwrap` | `cleanNPM`, `npmInstall` | [`NpmShrinkwrapTask`](#npmshrinkwraptask) | Locks down the versions of a package's dependencies in order to control which dependency versions are used.\n\n### DownloadNodeTask\n\nThe purpose of this task is to download and unpack a Node.js distribution.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`nodeDir` | `File` | `null` | The directory where the Node.js distribution is unpacked.\n`nodeExeUrl` | `String` | `null` | The URL of `node.exe` to download when on Windows.\n`nodeUrl` | `String` | `null` | The URL of the Node.js distribution to download.\n`npmUrl` | `String` | `null` | The URL of the NPM version to download.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties, to defer evaluation until task execution.\n\n### ExecuteNodeTask\n\nThis is the base task to run Node.js in a Gradle build. All tasks of type\n`ExecuteNodeTask` automatically depend on [`downloadNode`](#downloadnode).\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`args` | `List<Object>` | `[]` | The arguments for the Node.js invocation.\n`command` | `String` | `\"node\"` | The file name of the executable to invoke.\n`environment` | `Map<Object, Object>` | `[]` | The environment variables for the Node.js invocation.\n`inheritProxy` | `boolean` | `true` | Whether to set the `http_proxy`, `https_proxy`, and `no_proxy` environment variables in the Node.js invocation based on the values of the system properties `https.proxyHost`, `https.proxyPort`, `https.proxyUser`, `https.proxyPassword`, `https.nonProxyHosts`, `https.proxyHost`, `https.proxyPort`, `https.proxyUser`, `https.proxyPassword`, and `https.nonProxyHosts`. If these environment variables are already set, their values will not be overwritten.\n`nodeDir` | `File` | <p>**If [`node.download`](#download) is `true`:** [`node.nodeDir`](#nodedir)</p><p>**Otherwise:** `null`</p> | The directory that contains the executable to invoke. If `null`, the executable must be available in the system `PATH`.\n<a name=\"npminstallretries\"></a>`npmInstallRetries` | `int` | `0` | The number of times the `node_modules` is deleted, the NPM cached data is verified (`npm cache verify`), and `npm install` is retried in case the Node.js invocation defined by this task fails. This can help solving corrupted `node_modules` directories by re-downloading the project's dependencies.\n<a name=\"workingdir\"></a>`workingDir` | `File` | `project.projectDir` | The working directory to use in the Node.js invocation.\n\nThe properties of type `File` support any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties to defer evaluation until task execution.\n\n#### Task Methods\n\nMethod | Description\n------ | -----------\n`ExecuteNodeTask args(Iterable<?> args)` | Adds arguments for the Node.js invocation.\n`ExecuteNodeTask args(Object... args)` | Adds arguments for the Node.js invocation.\n`ExecuteNodeTask environment(Map<?, ?> environment)` | Adds environment variables for the Node.js invocation.\n`ExecuteNodeTask environment(Object key, Object value)` | Adds an environment variable for the Node.js invocation.\n\n### ExecuteNodeScriptTask\n\nThe purpose of this task is to execute a Node.js script. Tasks of type\n`ExecuteNodeScriptTask` extend [`ExecuteNodeTask`](#executenodetask).\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`scriptFile` | `File` | `null` | The Node.js script to execute.\n\nThe properties of type `File` support any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\n\n### ExecuteNpmTask\n\nThe purpose of this task is to execute an NPM command. Tasks of type\n`ExecuteNpmTask` extend [`ExecuteNodeScriptTask`](#executenodescripttask) with\nthe following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n`command` | <p>**If `nodeDir` is `null`:** `\"npm\"`</p><p>**Otherwise:** `\"node\"`</p>\n`scriptFile` | <p>**If `nodeDir` is `null`:** `null`</p><p>**Otherwise:** `\"${nodeDir}/lib/node_modules/npm/bin/npm-cli.js\"` or `\"${nodeDir}/node_modules/npm/bin/npm-cli.js\"` on Windows.</p>\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`cacheConcurrent` | `boolean` | <p>**If `node.npmVersion` is greater than or equal to `5.0.0`, or `node.nodeVersion` is greater than or equal to `8.0.0`:** `true`</p><p>**Otherwise:** `false`</p> | Whether to run this task concurrently, in case the version of NPM in use supports multiple concurrent accesses to the same cache directory.\n`cacheDir` | `File` | <p>**If `nodeDir` is `null`, or `node.npmVersion` is greater than or equal to `5.0.0`, or `node.nodeVersion` is greater than or equal to `8.0.0`:** `null`</p><p>**Otherwise:** `\"${nodeDir}/.cache\"`</p> | The location of NPM's cache directory. It sets the [`--cache`](https://docs.npmjs.com/misc/config#cache) argument. Leave the property `null` to keep the default value.\n`logLevel` | `String` | Value to mirror the log level set in the task's [`logger`](https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#org.gradle.api.Task:logger) object. | The NPM log level. It sets the [--loglevel](https://docs.npmjs.com/misc/config#loglevel) argument.\n`production` | `boolean` | `false` | Whether to run in production mode during the NPM invocation. It sets the [`--production`](https://docs.npmjs.com/misc/config#production) argument.\n`progress` | `boolean` | `true` | Whether to show a progress bar during the NPM invocation. It sets the [`--progress`](https://docs.npmjs.com/misc/config#progress) argument.\n<a name=\"registry\"></a>`registry` | `String` | `null` | The base URL of the NPM package registry. It sets the [`--registry`](https://docs.npmjs.com/misc/config#registry) argument. Leave the property `null` or empty to keep the default value.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties, to defer evaluation until task execution.\n\n### DownloadNodeModuleTask\n\nThe purpose of this task is to download a Node.js package. The packages are\ndownloaded in the `${workingDir}/node_modules` directory, which is equal, by\ndefault, to the `node_modules` directory of the project. Tasks of type\n`DownloadNodeModuleTask` extend [`ExecuteNpmTask`](#executenpmtask) in order to\nexecute the command [`npm install ${moduleName}@${moduleVersion}`](https://docs.npmjs.com/cli/install).\n\n`DownloadNodeModuleTask` instances are automatically disabled if the project's\n`package.json` file already lists a module with the same name in its\n`dependencies` or `devDependencies` object.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`moduleName` | `String` | `null` | The name of the Node.js package to download.\n`moduleVersion` | `String` | `null` | The version of the Node.js package to download.\n\nIt is possible to use Closures and Callables as values for the `String`\nproperties, to defer evaluation until task execution.\n\n### NpmInstallTask\n\nPurpose of these tasks is to install the dependencies declared in a\n`package.json` file. Tasks of type `NpmInstallTask` extend\n[`ExecuteNpmTask`](#executenpmtask) in order to run the command [`npm install`](https://docs.npmjs.com/cli/install).\n\n`NpmInstallTask` instances are automatically disabled if the `package.json` file\ndoes not declare any dependency in the `dependency` or `devDependencies` object.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`nodeModulesCacheDir` | `File` | `null` | <p>The directory where `node_modules` directories are cached. By setting this property, it is possible to cache the `node_modules` directory of a project and avoid unnecessary invocations of `npm install`, useful especially in Continuous Integration environments.</p><p>The `node_modules` directory is cached based on the content of the project's `package-lock.json` (or `npm-shrinkwrap.json`, or `package.json` if absent). Therefore, if `NpmInstallTask` tasks in multiple projects are configured with the same `nodeModulesCacheDir`, and their `package-lock.json`, `npm-shrinkwrap.json` or `package.json` declare the same dependencies, their `node_modules` caches will be shared.</p><p>This feature is not available if the [`com.liferay.cache`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-cache) plugin is applied.</p>\n`nodeModulesCacheNativeSync` | `boolean` | `true` | Whether to use `rsync` (on Linux/macOS) or `robocopy` (on Windows) to cache and restore the `node_modules` directory. If `nodeModulesCacheDir` is not set, this property has no effect.\n`nodeModulesDigestFile` | `File` | `null` | <p>If this property is set, the content of the project's `package-lock.json` (or `npm-shrinkwrap.json`, or `package.json` if absent) is checked with the digest from the `node_modules` directory. If the digests match, do nothing. If the digests don't match, the `node_modules` directory is deleted before running `npm install`.</p><p>This feature is not available if the `com.liferay.cache` plugin is applied or if the property `nodeModulesCacheDir` is set.</p>\n`removeShrinkwrappedUrls` | `boolean` | `true` if the [registry](#registry) property has a value, `false` otherwise. | Whether to temporarily remove all the hard-coded URLs in the `from` and `resolved` fields of the `npm-shinkwrap.json` file before invoking `npm install`. This way, it is possible to force NPM to download all dependencies from a custom registry declared in the [`registry`](#registry) property.\n`useNpmCI` | `boolean` | `false` | Whether to run `npm ci` instead of `npm install`. If the `package-lock.json` file does not exist, this property has no effect.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\n\n### NpmShrinkwrapTask\n\nThe purpose of this task is to lock down the versions of a package's\ndependencies so that you can control exactly which dependency versions are used\nwhen your package is installed. Tasks of type `NpmShrinkwrapTask` extend\n[`ExecuteNpmTask`](#executenpmtask) to execute the command\n[`npm shrinkwrap`](https://docs.npmjs.com/cli/shrinkwrap).\n\nThe generated `npm-shrinkwrap.json` file is automatically sorted and formatted,\nso it's easier to see the changes with the previous version.\n\n`NpmShrinkwrapTask` instances are automatically disabled if the `package.json`\nfile does not exist.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`excludedDependencies` | `List<String>` | `[]` | The package names to exclude from the generated `npm-shrinkwrap.json` file.\n`includeDevDependencies` | `boolean` | `true` | Whether to include the package's `devDependencies`. It sets the [`--dev`](https://docs.npmjs.com/cli/shrinkwrap#other-notes) argument.\n\nIt is possible to use Closures and Callables as values for the `String`\nproperties to defer evaluation until task execution.\n\n#### Task Methods\n\nMethod | Description\n------ | -----------\n`NpmShrinkwrapTask excludeDependencies(Iterable<?> excludedDependencies)` | Adds package names to exclude from the generated `npm-shrinkwrap.json` file.\n`NpmShrinkwrapTask excludeDependencies(Object... excludedDependencies)` | Adds package names to exclude from the generated `npm-shrinkwrap.json` file.\n\n### PublishNodeModuleTask\n\nThe purpose of this task is to publish a package to the\n[NPM registry](https://www.npmjs.com/). Tasks of type `PublishNodeModuleTask`\nextend [`ExecuteNpmTask`](#executenpmtask) in order to execute the command\n[`npm publish`](https://docs.npmjs.com/cli/publish).\n\nThese tasks generate a new temporary `package.json` file in the directory\nassigned to the [`workingDir`](#workingdir) property; then the `npm publish`\ncommand is executed. If the `package.json` file in that location does not exist,\nthe one in the root of the project directory (if found) is copied; otherwise, a\nnew file is created.\n\nThe `package.json` is then processed by adding the values provided by the task\nproperties, if not already present in the file itself. It is still possible to\noverride one or more fields of the `package.json` file with the values provided\nby the task properties by adding one or more keys (e.g., `\"version\"`) to the\n`overriddenPackageJsonKeys` property.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`moduleAuthor` | `String` | `null` | The value of the [`author`](https://docs.npmjs.com/files/package.json#people-fields-author-contributors) field in the generated `package.json` file.\n`moduleBugsUrl` | `String` | `null` | The value of the [`bugs.url`](https://docs.npmjs.com/files/package.json#bugs) field in the generated `package.json` file.\n`moduleDescription` | `String` | `project.description` | The value of the [`description`](https://docs.npmjs.com/files/package.json#description-1) field in the generated `package.json` file.\n`moduleKeywords` | `List<String>` | `[]` | The value of the [`keywords`](https://docs.npmjs.com/files/package.json#keywords) field in the generated `package.json` file.\n`moduleLicense` | `String` | `null` | The value of the [`license`](https://docs.npmjs.com/files/package.json#license) field in the generated `package.json` file.\n`moduleMain` | `String` | `null` | The value of the [`main`](https://docs.npmjs.com/files/package.json#main) field in the generated `package.json` file.\n`moduleName` | `String` | Name based on [`osgiHelper.bundleSymbolicName`](https://github.com/gradle/gradle/blob/master/subprojects/osgi/src/main/java/org/gradle/api/internal/plugins/osgi/OsgiHelper.java): for example, if `osgiHelper.bundleSymbolicName` is `\"com.liferay.gradle.plugins.node\"`, the default value for the `moduleName` property is `\"liferay-gradle-plugins-node\"`. | The value of the [`name`](https://docs.npmjs.com/files/package.json#name) field in the generated `package.json` file.\n`moduleRepository` | `String` | `null` | The value of the [`repository`](https://docs.npmjs.com/files/package.json#repository) field in the generated `package.json` file.\n`moduleVersion` | `String` | `project.version` | The value of the [`version`](https://docs.npmjs.com/files/package.json#version) field in the generated `package.json` file.\n`npmEmailAddress` | `String` | `null` | The email address of the npmjs.com user that publishes the package.\n`npmPassword` | `String` | `null` | The password of the npmjs.com user that publishes the package.\n`npmUserName` | `String` | `null` | The name of the npmjs.com user that publishes the package.\n`overriddenPackageJsonKeys` | `Set<String>` | `[]` | The field values to override in the generated `package.json` file.\n \n#### Task Methods\n \n Method | Description\n ------ | -----------\n `PublishNodeModuleTask overriddenPackageJsonKeys(Iterable<String> overriddenPackageJsonKeys)` | Adds field values to override in the generated `package.json` file.\n `PublishNodeModuleTask overriddenPackageJsonKeys(String... overriddenPackageJsonKeys)` | Adds field values to override in the generated `package.json` file.\n\n### npmRun${script} Task\n\nFor each [script](https://docs.npmjs.com/misc/scripts) declared in the\n`package.json` file of the project, one task `npmRun${script}` of type\n[`ExecuteNpmTask`](#executenpmtask) is added. Each of these tasks is\nautomatically configured with sensible defaults:\n\nProperty Name | Default Value\n------------- | -------------\n`args` | `[\"run-script\", \"${script}\"]`\n\nIf the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin is applied and the `package.json` file declares a script named `\"build\"`,\nthe script is executed before the `classes` task but after the\n[`processResources`](https://docs.gradle.org/4.0/userguide/java_plugin.html#sec:java_resources)\ntask.\n\nIf the [`lifecycle-base`](https://docs.gradle.org/current/javadoc/org/gradle/language/base/plugins/LifecycleBasePlugin.html)\nplugin is applied and the `package.json` file declares a script named `test`,\nthe script is executed when running the `check` task.\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/rest-builder-gradle-plugin.markdown",
    "content": "---\nheader-id: rest-builder-gradle-plugin\n---\n\n# REST Builder Gradle Plugin\n\n[TOC levels=1-4]\n\nThe REST Builder Gradle plugin lets you generate a REST layer defined in the\nREST Builder `rest-config.yaml` and `rest-openapi.yaml` files.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.rest.builder\", version: \"1.0.21\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.portal.tools.rest.builder\"\n```\n\nThe REST Builder plugin automatically applies the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin.\n\nSince the plugin automatically resolves the [Liferay REST Builder](https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-rest-builder)\nlibrary as a dependency, you have to configure a repository that hosts the\nlibrary and its transitive dependencies. The Liferay CDN repository hosts them\nall:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`buildREST` | \\- | [`BuildRESTTask`](#buildresttask) | Runs the Liferay REST Builder.\n\n### BuildRESTTask\n\nTasks of type `BuildRESTTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, such as [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.lang.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize)\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args) | REST Builder command line arguments\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.restBuilder`](#liferay-rest-builder-dependency)\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.liferay.portal.tools.rest.builder.RESTBuilder\"`\n[`systemProperties`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:systemProperties) | `[]`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`copyrightFile` | `File` | `null` | The file that contains the copyright header.\n`restConfigDir` | `File` |`${project.projectDir}` | The directory that contains the `rest-config.yaml` and `rest-openapi.yaml` files.\n\nIn the typical scenario, the `rest-config.yaml` and `rest-openapi.yaml` files\nare contained in the project directory of `my-rest-app-impl`. In the\n`build.gradle` of the same module, apply the `com.liferay.rest.builder` plugin.\n\nThe properties of type `File` supports any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.lang.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties, to defer evaluation until task execution.\n\n## Additional Configuration\n\nThere are additional configurations added to use REST Builder.\n\n### Liferay REST Builder Dependency\n\nBy default, the plugin creates a configuration called `restBuilder` and adds\na dependency to the latest released version of Liferay REST Builder.\n\n```groovy\ndependencies {\n    restBuilder group: \"com.liferay\", name: \"com.liferay.portal.tools.rest.builder\", version: \"1.0.22\"\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/service-builder-gradle-plugin.markdown",
    "content": "---\nheader-id: service-builder-gradle-plugin\n---\n\n# Service Builder Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Service Builder Gradle plugin lets you generate a service layer defined in a\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n`service.xml` file.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.service.builder\", version: \"2.2.46\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.portal.tools.service.builder\"\n```\n\nThe Service Builder plugin automatically applies the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin.\n\nSince the plugin automatically resolves the [Liferay Service Builder](https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-service-builder)\nlibrary as a dependency, you have to configure a repository that hosts the\nlibrary and its transitive dependencies. The Liferay CDN repository hosts them\nall:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`buildService` | \\- | [`BuildServiceTask`](#buildservicetask) | Runs the Liferay Service Builder.\n\nThe `buildService` task is automatically configured with sensible defaults,\ndepending on whether the [`war`](https://docs.gradle.org/current/userguide/war_plugin.html)\nplugin is applied, or whether the [`osgiModule`](#osgimodule) property is `true`:\n\nProperty Name | Default Value\n------------- | -------------\n[`apiDir`](#apidir) | <p>**If the `war` plugin is applied:** `${project.webAppDir}/WEB-INF/service`</p><p>**Otherwise:** `null`</p>\n[`hbmFile`](#hbmfile) | <p>**If `osgiModule` is `true`:** `${buildService.resourcesDir}/META-INF/module-hbm.xml`</p><p>**Otherwise:** `${buildService.resourcesDir}/META-INF/portlet-hbm.xml`</p>\n[`implDir`](#impldir) | The first `java` directory of the `main` source set (by default: `src/main/java`).\n[`inputFile`](#inputfile) | <p>**If the `war` plugin is applied:** `${project.webAppDir}/WEB-INF/service.xml`</p><p>**Otherwise:** `${project.projectDir}/service.xml`</p>\n[`modelHintsFile`](#modelhintsfile) | The file `META-INF/portlet-model-hints.xml` in the first `resources` directory of the `main` source set (by default: `src/main/resources/META-INF/portlet-model-hints.xml`).\n[`pluginName`](#pluginname) | <p>**If `osgiModule` is `true`:** `\"\"`</p><p>**Otherwise:** `project.name`</p>\n[`propsUtil`](#pluginname) | <p>**If `osgiModule` is `true`:** `\"${bundleSymbolicName}.util.ServiceProps\"`<br />The `bundleSymbolicName` of the project is inferred via the [`OsgiHelper`](https://github.com/gradle/gradle/blob/master/subprojects/osgi/src/main/java/org/gradle/api/internal/plugins/osgi/OsgiHelper.java) class.</p><p>**Otherwise:** `\"com.liferay.util.service.ServiceProps\"`</p>\n[`resourcesDir`](#resourcesdir) | The first `resources` directory of the `main` source set (by default: `src/main/resources`).\n[`springFile`](#springfile) | <p>**If `osgiModule` is `true`:** the file `META-INF/spring/module-spring.xml` in the first `resources` directory of the `main` source set (by default: `src/main/resources/META-INF/spring/module-spring.xml`)</p><p>**Otherwise:** the file `META-INF/portlet-spring.xml` in the first `resources` directory of the `main` source set (by default: `src/main/resources/META-INF/portlet-spring.xml`)</p>\n[`sqlDir`](#sqldir) | <p>**If the `war` plugin is applied:** `${project.webAppDir}/WEB-INF/sql`</p><p>**Otherwise:** The directory `META-INF/sql` in the first `resources` directory of the `main` source set (by default: `src/main/resources/META-INF/sql`).</p>\n\n<!--Add back link below for `typlical scenario` once \ndefining-an-object-relational-map-with-service-builder \narticle is available\n-->\n\nIn the typical scenario of a data-driven Liferay OSGi application split in \n`myapp-app`, `myapp-service` and `myapp-web` modules, the `service.xml` file is \nusually contained in the root directory of `myapp-service`. In the \n`build.gradle` of the same module, it is enough to apply the \n`com.liferay.service.builder` plugin [as described](#usage), and then add the \nfollowing snippet to enable the use of Liferay Service Builder:\n\n```groovy\nbuildService {\n    apiDir = \"../myapp-api/src/main/java\"\n    testDir = \"../myapp-test/src/testIntegration/java\"\n}\n```\n\nWhile `apiDir` is required, the `testDir` property assignment can be left out,\nin which case Arquillian-based integration test classes are generated.\n\n### BuildServiceTask\n\nTasks of type `BuildWSDDTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, such as [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.lang.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize)\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args) | Service Builder command line arguments\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.serviceBuilder`](#liferay-service-builder-dependency)\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.liferay.portal.tools.service.builder.ServiceBuilder\"`\n[`systemProperties`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:systemProperties) | `[\"file.encoding\": \"UTF-8\"]`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"apidir\"></a>`apiDir` | `File` | `null` | A directory where the service API Java source files are generated. It sets the `service.api.dir` argument.\n`autoImportDefaultReferences` | `boolean` | `true` | Whether to automatically add default references, like `com.liferay.portal.ClassName`, `com.liferay.portal.Resource` and `com.liferay.portal.User`, to the services. It sets the `service.auto.import.default.references` argument.\n`autoNamespaceTables` | `boolean` | `true` | Whether to prefix table names by the namespace specified in the `service.xml` file. It sets the `service.auto.namespace.tables` argument.\n`beanLocatorUtil` | `String` | `\"com.liferay.util.bean.PortletBeanLocatorUtil\"` | The fully qualified class name of a bean locator class to use in the generated service classes. It sets the `service.bean.locator.util` argument.\n`buildNumber` | `long` | `1` | A specific value to assign the `build.number` property in the `service.properties` file. It sets the `service.build.number` argument.\n`buildNumberIncrement` | `boolean` | `true` | Whether to automatically increment the `build.number` property in the `service.properties` file by one at every service generation. It sets the `service.build.number.increment` argument.\n`databaseNameMaxLength` | `int` | `30` | The upper bound for database table and column name lengths to ensure it works on all databases. It sets the `service.database.name.max.length` argument.\n<a name=\"hbmfile\"></a>`hbmFile` | `File` | `null` | A Hibernate Mapping file to generate. It sets the `service.hbm.file` argument.\n<a name=\"impldir\"></a>`implDir` | `File` | `null` | A directory where the service Java source files are generated. It sets the `service.impl.dir` argument.\n<a name=\"inputfile\"></a>`inputFile` | `File` | `null` | The project's `service.xml` file. It sets the `service.input.file` argument.\n`modelHintsConfigs` | `Set` | `[\"classpath*:META-INF/portal-model-hints.xml\", \"META-INF/portal-model-hints.xml\", \"classpath*:META-INF/ext-model-hints.xml\", \"classpath*:META-INF/portlet-model-hints.xml\"]` | Paths to the model hints files for Liferay Service Builder to use in generating the service layer. It sets the `service.model.hints.configs` argument.<!-- Add back link for 'modal hints' once customizing-model-entities-with-model-hints article is available -->\n<a name=\"modelhintsfile\"></a>`modelHintsFile` | `File` | `null` | A model hints file for the project. It sets the `service.model.hints.file` argument.\n<a name=\"osgimodule\"></a>`osgiModule` | `boolean` | `false` | Whether to generate the service layer for OSGi modules. It sets the `service.osgi.module` argument.\n<a name=\"pluginname\"></a>`pluginName` | `String` | `null` | If specified, a plugin can enable additional generation features, such as `Clp` class generation, for non-OSGi modules. It sets the `service.plugin.name` argument.\n<a name=\"propsutil\"></a>`propsUtil` | `String` | `null` | The fully qualified class name of the service properties util class to generate. It sets the `service.props.util` argument.\n`readOnlyPrefixes` | `Set` | `[\"fetch\", \"get\", \"has\", \"is\", \"load\", \"reindex\", \"search\"]` | Prefixes of methods to consider read-only. It sets the `service.read.only.prefixes` argument.\n`resourceActionsConfigs` | `Set` | `[\"META-INF/resource-actions/default.xml\", \"resource-actions/default.xml\"]` | Paths to the [resource actions](/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions) files for Liferay Service Builder to use in generating the service layer. It sets the `service.resource.actions.configs` argument.\n<a name=\"resourcesdir\"></a>`resourcesDir` | `File` | `null` | A directory where the service non-Java files are generated. It sets the `service.resources.dir` argument.\n<a name=\"springfile\"></a>`springFile` | `File` | `null` | A service Spring file to generate. It sets the `service.spring.file` argument.\n`springNamespaces` | `Set` | `[\"beans\"]` | Namespaces of Spring XML Schemas to add to the service Spring file. It sets the `service.spring.namespaces` argument.\n<a name=\"sqldir\"></a>`sqlDir` | `File` | `null` | A directory where the SQL files are generated. It sets the `service.sql.dir` argument.\n`sqlFileName` | `String` | `\"tables.sql\"` | A name (relative to `sqlDir`) for the file in which the SQL table creation instructions are generated. It sets the `service.sql.file` argument.\n`sqlIndexesFileName` | `String` | `\"indexes.sql\"` | A name (relative to `sqlDir`) for the file in which the SQL index creation instructions are generated. It sets the `service.sql.indexes.file` argument.\n`sqlSequencesFileName` | `String` | `\"sequences.sql\"` | A name (relative to `sqlDir`) for the file in which the SQL sequence creation instructions are generated. It sets the `service.sql.sequences.file` argument.\n`targetEntityName` | `String` | `null` | If specified, it's the name of the entity for which Liferay Service Builder should generate the service. It sets the `service.target.entity.name` argument.\n`testDir` | `File` | `null` | If specified, it's a directory where integration test Java source files are generated. It sets the `service.test.dir` argument.\n`uadDir` | `File` | `null` | A directory where the UAD (user-associated data) Java source files are generated. It sets the `service.uad.dir` argument.\n`uadTestIntegrationDir` | `File` | `null` | A directory where integration test UAD (user-associated data) Java source files are generated. It sets the `service.uad.test.integration.dir` argument.\n\nThe properties of type `File` supports any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.lang.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties, to defer evaluation until task execution.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use Service Builder.\n\n### Liferay Service Builder Dependency\n\nBy default, the plugin creates a configuration called `serviceBuilder` and adds\na dependency to the latest released version of Liferay Service Builder. It is\npossible to override this setting and use a specific version of the tool by\nmanually adding a dependency to the `serviceBuilder` configuration:\n\n```groovy\ndependencies {\n    serviceBuilder group: \"com.liferay\", name: \"com.liferay.portal.tools.service.builder\", version: \"1.0.292\"\n}\n```\n\nIf you're applying the\n[`com.liferay.gradle.plugins`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins)\nor\n[`com.liferay.gradle.plugins.workspace`](https://github.com/liferay/liferay-portal/blob/master/modules/sdk/gradle-plugins-workspace)\nplugins to your project, the Service Builder dependency is already added to the\n`serviceBuilder` configuration. Therefore, if you try to apply a customized\nversion of Service Builder, it's not recognized; you must override the\nconfiguration already applied.\n\nTo do this, you must customize the classpath of the `buildService` task. If\nyou're supplying the customized Service Builder plugin through a module named\n`custom-sb-api`, you could modify the `buildService` task like this:\n\n```groovy\nbuildService {\n    apiDir = \"../custom-sb-api/src/main/java\"\n    classpath = configurations.serviceBuilder.filter { file -> !file.name.contains(\"com.liferay.portal.tools.service.builder\") }\n}\n```\n\nIf you do this in conjunction with the `serviceBuilder` dependency\nconfiguration, the custom Service Builder version is used.\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/source-formatter-gradle-plugin.markdown",
    "content": "---\nheader-id: source-formatter-gradle-plugin\n---\n\n# Source Formatter Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Source Formatter Gradle plugin lets you format project files using the\n[Liferay Source Formatter](https://github.com/liferay/liferay-portal/tree/master/modules/util/source-formatter)\ntool.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.source.formatter\", version: \"2.3.413\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.source.formatter\"\n```\n\nSince the plugin automatically resolves the Liferay Source Formatter library as\na dependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## Tasks\n\nThe plugin adds two tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`checkSourceFormatting` | \\- | [`FormatSourceTask`](#formatsourcetask) | Runs the Liferay Source Formatter to check for source formatting errors.\n`formatSource` | \\- | [`FormatSourceTask`](#formatsourcetask) | Runs the Liferay Source Formatter to format the project files.\n\nIf desired, it is possible to check for source formatting errors while executing\nthe [`check`](https://docs.gradle.org/current/userguide/java_plugin.html#N15056)\ntask by adding the following dependency:\n\n```groovy\ncheck {\n    dependsOn checkSourceFormatting\n}\n```\n\nThe same can be achieved by adding the following snippet to the `build.gradle`\nfile in the root directory of a [*Liferay Workspace*](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace):\n\n```groovy\nsubprojects {\n    afterEvaluate {\n        if (plugins.hasPlugin(\"base\") && plugins.hasPlugin(\"com.liferay.source.formatter\")) {\n            check.dependsOn checkSourceFormatting\n        }\n    }\n}\n```\n\nThe tasks `checkSourceFormatting` and `formatSource` are automatically skipped\nif another task with the same name is being executed in a parent project.\n\n### FormatSourceTask\n\nTasks of type `FormatSourceTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, like [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.lang.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize)\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args) | Source Formatter command line arguments\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.sourceFormatter`](#liferay-source-formatter-dependency)\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.liferay.source.formatter.SourceFormatter\"`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`autoFix` | `boolean` | `false` | Whether to automatically fix source formatting errors. It sets the `source.auto.fix` argument.\n`baseDir` | `File` |  | The Source Formatter base directory. It sets the `source.base.dir` argument. *(Read-only)*\n`baseDirName` | `String` | `\"./\"` | The name of the Source Formatter base directory, relative to the project directory.\n`fileExtensions` | `List<String>` | `[]` | The file extensions to format. If empty, all file extensions will be formatted. It sets the `source.file.extensions` argument.\n`files` | `List<File>` | | The list of files to format. It sets the `source.files` argument. *(Read-only)*\n`fileNames` | `List<String>` | `null` | The file names to format, relative to the project directory. If `null`, all files contained in `baseDir` will be formatted.\n`formatCurrentBranch` | `boolean` | `false` | Whether to format only the files contained in `baseDir` that are added or modified in the current Git branch. It sets the `format.current.branch` argument.\n`formatLatestAuthor` | `boolean` | `false` | Whether to format only the files contained in `baseDir` that are added or modified in the latest Git commits of the same author. It sets the `format.latest.author` argument.\n`formatLocalChanges` | `boolean` | `false` | Whether to format only the unstaged files contained in `baseDir`. It sets the `format.local.changes` argument.\n`gitWorkingBranchName` | `String` | `\"master\"` | The Git working branch name. It sets the `git.working.branch.name` argument.\n`includeSubrepositories` | `boolean` | `false` | Whether to format files that are in read-only subrepositories. It sets the `include.subrepositories` argument.\n`maxLineLength` | `int` | `80` | The maximum number of characters allowed in Java files. It sets the `max.line.length` argument.\n`printErrors` | `boolean` | `true` | Whether to print formatting errors on the Standard Output stream. It sets the `source.print.errors` argument.\n`processorThreadCount` | `int` | `5` | The number of threads used by Source Formatter. It sets the `processor.thread.count` argument.\n`showDebugInformation` | `boolean` | `false` | Whether to show debug information, if present. It sets the `show.debug.information` argument.\n`showDocumentation` | `boolean` | `false` | Whether to show the documentation for the source formatting issues, if present. It sets the `show.documentation` argument.\n`showStatusUpdates` | `boolean` | `false` | Whether to show status updates during source formatting, if present. It sets the `show.status.updates` argument.\n`throwException` | `boolean` | `false` | Whether to fail the build if formatting errors are found. It sets the `source.throw.exception` argument.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the Source Formatter.\n\n### Liferay Source Formatter Dependency\n\nBy default, the plugin creates a configuration called `sourceFormatter` and adds\na dependency to the latest released version of Liferay Source Formatter. It is\npossible to override this setting and use a specific version of the tool by\nmanually adding a dependency to the `sourceFormatter` configuration:\n\n```groovy\ndependencies {\n    sourceFormatter group: \"com.liferay\", name: \"com.liferay.source.formatter\", version: \"1.0.885\"\n}\n```\n\n### System Properties\n\nIt is possible to set the default values of the `fileExtensions`, `fileNames`,\n`formatCurrentBranch`, `formatLatestAuthor`, and `formatLocalChanges` properties\nfor a `FormatSourceTask` task via system properties:\n\n- `-D${task.name}.file.extensions=java,xml`\n- `-D${task.name}.file.names=README.markdown,src/main/resources/hello.txt`\n- `-D${task.name}.format.current.branch=true`\n- `-D${task.name}.format.latest.author=true`\n- `-D${task.name}.format.local.changes=true`\n\nFor example, run the following Bash command to format only the unstaged files in\nthe project:\n\n```bash\n./gradlew formatSource -DformatSource.format.local.changes=true\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/soy-gradle-plugin.markdown",
    "content": "---\nheader-id: soy-gradle-plugin\n---\n\n# Soy Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Soy Gradle plugin lets you compile [Closure Templates](https://developers.google.com/closure/templates/)\ninto JavaScript functions. It also lets you use a custom localization mechanism\nin the generated `.soy.js` files by replacing [`goog.getMsg`](https://developers.google.com/closure/templates/docs/translation#closurecompiler)\ndefinitions with a different function call (e.g., `Liferay.Language.get`).\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.soy\", version: \"3.1.8\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n```\n\nThere are two Soy Gradle plugins you can apply to your project:\n\n- Apply the [*Soy Plugin*](#soy-plugin) to compile Closure Templates into\nJavaScript functions:\n\n    ```groovy\n    apply plugin: \"com.liferay.soy\"\n    ```\n\n- Apply the [*Soy Translation Plugin*](#soy-translation-plugin) to use a custom\nlocalization mechanism in the generated `.soy.js` files:\n\n    ```groovy\n    apply plugin: \"com.liferay.soy.translation\"\n    ```\n\nSince the Soy Gradle plugin automatically resolves the Soy library as a\ndependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## Soy Plugin\n\nThe Soy plugin adds two tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`buildSoy` | \\- | [`BuildSoyTask`](#buildsoytask) | Compiles Closure Templates into JavaScript functions.\n`wrapSoyAlloyTemplate` | - `configJSModules` if [`com.liferay.js.module.config.generator`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-js-module-config-generator) is applied <br> - `processResources` if `java` is applied <br> - `transpileJS` if [`com.liferay.js.transpiler`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-js-transpiler) is applied | [`WrapSoyAlloyTemplateTask`](#wrapsoyalloytemplatetask) | Wraps the JavaScript functions compiled from Closure Templates into AlloyUI modules.\n\nThe plugin also adds the following dependencies to tasks defined by the `java`\nplugin:\n\nName | Depends On\n---- | ----------\n`classes` | `wrapSoyAlloyTemplate`\n\nThe `buildSoy` task is automatically configured with sensible defaults,\ndepending on whether the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin is applied:\n\nProperty Name | Default Value\n------------- | -------------\n[`includes`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:includes) | `[\"**/*.soy\"]`\n[`source`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:source) | <p>**If the `java` plugin is applied:** The first `resources` directory of the `main` source set (by default, `src/main/resources`).</p><p>**Otherwise:** `[]`</p>\n\nThe `wrapSoyAlloyTemplate` task is **disabled by default**, and it is\nautomatically configured with sensible defaults, depending on whether the `java`\nplugin is applied:\n\nProperty Name | Default Value\n------------- | -------------\n[`enabled`](https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#org.gradle.api.Task:enabled) | `false`\n[`includes`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:includes) | `[\"**/*.soy.js\"]`\n[`source`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:source) | <p>**If the `java` plugin is applied:** `project.sourceSets.main.output.resourcesDir`</p><p>**Otherwise:** `[]`</p>\n\n### Additional Configuration\n\nThere are additional configurations that can help you use the Soy library.\n\n#### Soy Dependency\n\nBy default, the plugin creates a configuration called `soy` and adds a\ndependency to the `2015-04-10` version of the Soy library. It is possible to\noverride this setting and use a specific version of the tool by manually adding\na dependency to the `soy` configuration:\n\n```groovy\ndependencies {\n    soy group: \"com.google.template\", name: \"soy\", version: \"2015-04-10\"\n}\n```\n\n## Soy Translation Plugin\n\nThe Soy Translation plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`replaceSoyTranslation` | - `configJSModules` if [`com.liferay.js.module.config.generator`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-js-module-config-generator) is applied <br> - `processResources` if `java` is applied <br> - `transpileJS` if [`com.liferay.js.transpiler`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-js-transpiler) is applied | [`ReplaceSoyTranslationTask`](#replacesoytranslationtask) | Replaces `goog.getMsg` definitions with `Liferay.Language.get` calls.\n\nThe plugin also adds the following dependencies to tasks defined by the `java`\nplugin:\n\nName | Depends On\n---- | ----------\n`classes` | `replaceSoyTranslation`\n\nThe `replaceSoyTranslation` task is automatically configured with sensible\ndefaults, depending on whether the `java` plugin is applied:\n\nProperty Name | Default Value\n------------- | -------------\n[`includes`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:includes) | `[\"**/*.soy.js\"]`\n[`replacementClosure`](#replacementclosure) | Replaces `goog.getMsg` definitions with `Liferay.Language.get` calls.\n[`source`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:source) | <p>**If the `java` plugin is applied:** `project.sourceSets.main.output.resourcesDir`</p><p>**Otherwise:** `[]`</p>\n\n## Tasks\n\n### BuildSoyTask\n\nTasks of type `BuildSoyTask` extend [`SourceTask`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html),\nso all its properties and methods, such as [`include`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:include\\(java.lang.Iterable\\))\nand [`exclude`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:exclude\\(java.lang.Iterable\\)),\nare available.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`classpath` | [`FileCollection`](https://docs.gradle.org/current/javadoc/org/gradle/api/file/FileCollection.html) | [`project.configurations.soy`](#soy-dependency) | The classpath for executing the [Liferay Portal Tools Soy Builder](https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-soy-builder).\n\n### WrapSoyAlloyTemplateTask\n\nTasks of type `WrapSoyAlloyTemplateTask` extend [`SourceTask`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html),\nso all its properties and methods, such as [`include`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:include\\(java.lang.Iterable\\))\nand [`exclude`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:exclude\\(java.lang.Iterable\\)),\nare available.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`moduleName` | `String` | `null` | The name of the AlloyUI module.\n`namespace` | `String` | `null` | The namespace of the Closure Templates of the project.\n\nIt is possible to use Closures and Callables as values for the `String`\nproperties to defer evaluation until task execution.\n\n### ReplaceSoyTranslationTask\n\nThe `ReplaceSoyTranslationTask` task type finds all the `goog.getMsg`\ndefinitions in the project's files and replaces them with a custom function\ncall.\n\n```javascript\nvar MSG_EXTERNAL_123 = goog.getMsg('welcome-to-{$releaseInfo}', { 'releaseInfo': opt_data.releaseInfo });\n```\n\nA `goog.getMsg` definition looks like the example above, and it has the\nfollowing components:\n\n- *variable name*: `MSG_EXTERNAL_123`\n- *language key*: `welcome-to-{$releaseInfo}`\n- *arguments object*: `{ 'releaseInfo': opt_data.releaseInfo }`\n\nTasks of type `ReplaceSoyTranslationTask` extend [`SourceTask`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html),\nso all its properties and methods, such as [`include`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:include\\(java.lang.Iterable\\))\nand [`exclude`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:exclude\\(java.lang.Iterable\\)),\nare available.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"replacementclosure\"></a>`replacementClosure` | `Closure<String>` | `null` | The Closure invoked in order to get the replacement for `goog.getMsg` definitions. The given Closure is passed the *variable name*, *language key*, and *arguments object* as its parameters.\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/target-platform-gradle-plugin.markdown",
    "content": "---\nheader-id: target-platform-gradle-plugin\n---\n\n# Target Platform Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Target Platform Gradle plugin helps with building multiple projects\nagainst a declared API target platform. Java dependencies can be managed with\nMaven BOMs and OSGi modules can be resolved against an OSGi distribution.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n\tdependencies {\n\t\tclasspath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.target.platform\", version: \"2.0.0\"\n\t}\n\n\trepositories {\n\t\tmaven {\n\t\t\turl \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t\t}\n\t}\n}\n```\n\nThere are two Target Platform Gradle plugins you can apply to your project. If\nyou have a multi-module Gradle project, you only need to apply these plugins to\nthe root project.\n\n- The [*Target Platform Plugin*](#target-platform-plugin) helps to configure\nyour projects to build against an established set of platform artifacts,\nincluding Java and OSGi dependencies.\n\n\t```groovy\n\tapply plugin: \"com.liferay.target.platform\"\n\t```\n\n- The [*Target Platform IDE Plugin*](#target-platform-ide-plugin) is a superset\nof the Target Platform Plugin (it applies the above plugin) and also adds IDE\nintegration for searching and debugging source code in the target platform\nartifacts.\n\n\t```groovy\n\tapply plugin: \"com.liferay.target.platform.ide\"\n\t```\n\nSince the plugin automatically resolves target platform configurations as\ndependencies, you must configure a repository that hosts these artifacts. The\nLiferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n\tmaven {\n\t\turl \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t}\n}\n```\n\n## Target Platform Plugin\n\nThe plugin applies the [Spring Dependency Management Plugin](https://github.com/spring-gradle-plugins/dependency-management-plugin)\nand then adds several specific configurations to configure the BOMs that are\nimported to manage Java dependencies and the various artifacts used in resolving\nOSGi dependencies. Also, a new `resolve` task is added to resolve all OSGi\nrequirements against a declared distribution artifact.\n\nThe plugin adds a series of configurations to your project:\n\nName | Description\n---- | -----------\n`targetPlatformBoms` | Configures all the BOMs to import as managed dependencies.\n`targetPlatformBundles` | Configures all the bundles in addition to the distro to resolve against.\n`targetPlatformDistro` | Configures the distro JAR file to use as base for resolving against.\n`targetPlatformRequirements` | Configures the list of JAR files to use as run requirements for resolving.\n\nThe plugin adds a task `resolve` of type [`ResolveTask`](#resolvetask) to your\nproject that performs an OSGi resolve operation using the\n`targetPlatformRequirements` configuration as the basis of the requirements. The\n`targetPlatformBundles` configuration is used as a repository for the resolver\nto resolve requirements. Lastly, the `targetPlatformDistro` configuration is\nused to provide the *distro* artifact for the resolve process. The *distro* is\nthe artifact that provides all the OSGi capabilities of the target platform. All\nof these parameters are used to create a `bndrun` file that can be used as input\ninto the Bndrun resolve operation.\n\n## Target Platform IDE Plugin\n\nThe plugin applies the [Target Platform](#target-platform-plugin) and the\n[`eclipse`](https://docs.gradle.org/current/userguide/eclipse_plugin.html)\nplugins to your project, and also adds a special `targetPlatformIDE`\nconfiguration, which is used to configure both the `eclipse` model and `idea`\nplugin model in Gradle to add all target platform artifacts to the classpath so\nthey are visible to both Eclipse and IntelliJ's Java Model Search (for looking\nup sources to classes).\n\n## Project Extension\n\nThe Target Platform plugin exposes the following properties through the\nextension named `targetPlatform`:\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`ignoreResolveFailures` | `boolean` | `true` | Whether to ignore resolve failures found when executing tasks of type [`ResolveTask`](#resolvetask).\n`subprojects` | `Set<Project>` | `project.subprojects` | The subprojects to configure with target platform support, including dependency management and the `resolve` task.\n\nThe same extension exposes the following methods:\n\nMethod | Description\n------ | -----------\n`TargetPlatformExtension applyToConfiguration(Iterable<?> configurationNames)` | Adds additional configurations to configure the BOMs that are imported to manage Java dependencies and the various artifacts used in resolving OSGi dependencies.\n`TargetPlatformExtension applyToConfiguration(Object... configurationNames)` | Adds additional configurations to configure the BOMs that are imported to manage Java dependencies and the various artifacts used in resolving OSGi dependencies.\n`TargetPlatformExtension onlyIf(Closure<Boolean> onlyIfClosure)` | Includes a subproject in the target platform configuration if the given closure returns `true`. The closure is evaluated at the end of the subproject configuration phase and is passed a single parameter: the subproject. If the closure returns `false`, the subproject is not included in the target platform configuration\n`TargetPlatformExtension onlyIf(Spec<Project> onlyIfSpec)` | Includes a subproject in the target platform configuration if the given spec is satisfied. The spec is evaluated at the end of the subproject configuration phase. If the spec is not satisfied, the subproject is not included in the target platform configuration.\n`TargetPlatformExtension resolveOnlyIf(Closure<Boolean> resolveOnlyIfClosure)` | Includes a subproject in the resolving process (including both the requirements and bundles configuration) if the given closure returns `true`. The closure is evaluated at the end of the subproject configuration phase and is passed a single parameter: the subproject. If the closure returns `false`, the subproject is the resolution process.\n`TargetPlatformExtension resolveOnlyIf(Spec<Project> resolveOnlyIfSpec)` | Includes a subproject in the resolving platform configuration if the given spec is satisfied. The spec is evaluated at the end of the subproject configuration phase. If the spec is not satisfied, the subproject is not included in the target platform configuration.\n`TargetPlatformExtension subprojects(Iterable<Project> subprojects)` | Includes additional projects to be configured with Target Platform support.\n`TargetPlatformExtension subprojects(Project... subprojects)` | Includes additional projects to be configured with Target Platform support.\n\n## Tasks\n\n### ResolveTask\n\nThe purpose of this task is to resolve an OSGi module (or all OSGi modules of\nsubprojects) against the available `targetPlatformBundles` and\n`targetPlatformDistro` configurations. By default, the `targetPlatformBundles`\nare all the artifacts created by all the subprojects. The `targetPlatformDistro`\nmust be set explicitly to a valid distribution artifact. When the task is\nperformed, a `bndrun` file is generated using the specified\n`targetPlatformDistro` as the `-distro` instruction; the `-runrequirements` are\na set of `osgi.identity` requirements for the `targetPlatformRequirements`\nconfiguration. If the resolve operation is able to find a valid set of\n`-runbundles` that match the `-runrequirements`, then the task passes\nsuccessfully (the resolution is valid). If a set of run bundles can't be found,\nthe resolution has failed and the failed requirements are listed as output of\nthe task.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`bndrunFile` | `File` | `null` | If this property is specified, it is used as the `bndrun` file to input into the resolver.\n`bundlesFileCollection` | `FileCollection` | All JAR files of subprojects with `jar` task | The input to `bndrun` resolve operation.\n`distroFileCollection` | `FileCollection` | `null` | The *distro* parameter for the generated `bndrun` file.\n`ignoreFailures` | `boolean` | `false` | Whether the `resolve` task should ignore failing the build for resolution errors.\n`offline` | `boolean` | `null` | Whether to run the bndrun resolve operation in offline mode.\n`requirementsFileCollection` | `FileCollection` | <p>**For the root project:** All the output JAR files of the subprojects.</p><p>**For subprojects:** The output JAR file of the subproject.</p> | For each resolve operation, the requirements must be specified in the `bndrun` file; each of the JARs in this collection generate an `osgi.identify` requirement in the `bndrun` file.\n\n## Additional Configuration\n\nThere are additional configurations that you can use to configure the target\nplatform.\n\n### Target Platform BOMs Dependency\n\nThe plugin creates a configuration called `targetPlatformBoms` with no defaults.\nYou can use this dependency to set which BOMs to import to configure your target\nplatform.\n\n```groovy\ndependencies {\n\ttargetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom\", version: \"7.2.0\"\n\ttargetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom.compile.only\", version: \"7.2.0\"\n}\n```\n\n### Target Platform Bundles Dependency\n\nThe plugin creates a configuration called `targetPlatformBundles`. It is\nconfigured with default dependencies to all resolvable bundles in a\nmulti-project build (e.g., all projects in multi-project build that have a `jar`\ntask). This can be used to specify additional bundles that should be added to\nthe set of bundles given to `resolve` task to resolve against when checking for\nOSGi requirements.\n\n```groovy\ndependencies {\n\ttargetPlatformBundles group: \"com.google.guava\", name: \"guava\", version: \"23.0\"\n}\n```\n\n### Target Platform Distro Dependency\n\nThe plugin creates a configuration called `targetPlatformDistro`. It is has no\ndefault so you must specify which artifact you want to use as the distribution\nto resolve against.\n\n```groovy\ndependencies {\n\ttargetPlatformDistro group: \"com.liferay.portal\", name: \"release.portal.distro\", version: \"7.2.0\"\n}\n```\n\nIf you have created your own custom distro JAR that is available locally, you\ncan use the `files` method to add it to the configuration.\n\n```groovy\ndependencies {\n\ttargetPlatformDistro files(\"custom-distro.jar\")\n}\n```\n\n### Target Platform Requirements Dependency\n\nThe plugin creates a configuration called `targetPlatformRequirements`. It is\nconfigured with default dependencies to all resolvable bundles in a\nmulti-project build (e.g., all projects in multi-project build that have a `jar`\ntask). This is can be used to specify additional bundles that should be added to\nthe set of bundles given to the `resolve` task to set as `osgi.identity`\nrequirements.\n\n```groovy\ndependencies {\n\ttargetPlatformRequirements group: \"com.liferay\", name: \"com.liferay.other.bundle\", version: \"1.0\"\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/theme-builder-gradle-plugin.markdown",
    "content": "---\nheader-id: theme-builder-gradle-plugin\n---\n\n# Theme Builder Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Theme Builder Gradle plugin lets you run the [Liferay Theme Builder](https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-theme-builder)\ntool to build the Liferay theme files in your project.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.theme.builder\", version: \"2.0.7\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.portal.tools.theme.builder\"\n```\n\nThe Theme Builder plugin automatically applies the [`war`](https://docs.gradle.org/current/userguide/war_plugin.html)\nplugin. It also applies the [`com.liferay.css.builder`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-css-builder)\nplugin to compile the [Sass](http://sass-lang.com/) files in the theme.\n\nSince the plugin automatically resolves the Liferay Theme Builder library as a\ndependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`buildTheme` | \\- | [`BuildThemeTask`](#buildthemetask) | Builds the theme files.\n\nThe plugin also adds the following dependencies to tasks defined by the\n`com.liferay.css.builder` and `war` plugins:\n\nName | Depends On\n---- | ----------\n[`buildCSS`](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-css-builder#tasks) | `buildTheme`\n[`war`](https://docs.gradle.org/current/userguide/war_plugin.html#sec:war_default_settings) | `buildTheme`\n\nThe `buildCSS` dependency compiles the Sass files contained in the directory\nspecified by the [`buildTheme.outputDir`](#outputdir) property. Moreover, the\n`war` task is configured as follows\n\n- exclude the directory specified in the [`buildTheme.diffsDir`](#diffsdir)\nproperty from the WAR file.\n- include the files contained in the [`buildTheme.outputDir`](#outputdir)\ndirectory into the WAR file.\n- include only the compiled CSS files, not SCSS files, into the WAR file.\n\nThe `buildTheme` task is automatically configured with sensible defaults:\n\nProperty Name | Default Value\n------------- | -------------\n[`diffsDir`](#diffsdir) | `project.webAppDir`\n[`outputDir`](#outputdir) | `\"${project.buildDir}/buildTheme\"`\n[`parentFile`](#parentfile) | The first JAR file in the [`parentThemes`](#parent-theme-dependencies) configuration that contains a `META-INF/resources/${buildTheme.parentName}` directory, or the first WAR file in the `parentThemes` configuration whose name starts with `${parentName}-theme-`.\n[`parentName`](#parentname) | `\"_styled\"`\n[`templateExtension`](#templateextension) | `\"ftl\"`\n[`themeName`](#themename) | `project.name`\n[`unstyledFile`](#unstyledfile) | The first JAR file in the [`parentThemes`](#parent-theme-dependencies) configuration that contains a `META-INF/resources/_unstyled` directory.\n\n### BuildThemeTask\n\nTasks of type `BuildThemeTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, such as [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.css.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize),\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args) | Theme Builder command line arguments\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.themeBuilder`](#liferay-theme-builder-dependency)\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.liferay.portal.tools.theme.builder.ThemeBuilder\"`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"diffsdir\"></a>`diffsDir` | `File` | `null` | The directory that contains the files to copy over the parent theme. It sets the `--diffs-dir` argument.\n<a name=\"outputdir\"></a>`outputDir` | `File` | `null` | The directory where to build the theme. It sets the `--output-dir` argument.\n<a name=\"parentdir\"></a>`parentDir` | `File` | `null` | The directory of the parent theme. It sets the `--parent-path` argument.\n<a name=\"parentfile\"></a>`parentFile` | `File` | `null` | The JAR file of the parent theme. If `parentDir` is specified, this property has no effect. It sets the `--parent-path` argument.\n<a name=\"parentname\"></a>`parentName` | `String` | `null` | The name of the parent theme. It sets the `--parent-name` argument.\n<a name=\"templateextension\"></a>`templateExtension` | `String` | `null` | The extension of the template files, usually `\"ftl\"` or `\"vm\"`. It sets the `--template-extension` argument.\n<a name=\"themename\"></a>`themeName` | `String` | `null` | The name of the new theme. It sets the `--name` argument.\n<a name=\"unstyleddir\"></a>`unstyledDir` | `File` | `null` | The directory of [Liferay Frontend Theme Unstyled](https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-unstyled). It sets the `--unstyled-dir` argument.\n<a name=\"unstyledfile\"></a>`unstyledFile` | `File` | `null` | The JAR file of [Liferay Frontend Theme Unstyled](https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-unstyled). If `unstyledDir` is specified, this property has no effect. It sets the `--unstyled-dir` argument.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.css.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties to defer evaluation until task execution.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the CSS Builder.\n\n### Liferay Theme Builder Dependency\n\nBy default, the plugin creates a configuration called `themeBuilder` and adds a\ndependency to the latest released version of the Liferay Theme Builder. It is\npossible to override this setting and use a specific version of the tool by\nmanually adding a dependency to the `themeBuilder` configuration:\n\n```groovy\ndependencies {\n    themeBuilder group: \"com.liferay\", name: \"com.liferay.portal.tools.theme.builder\", version: \"1.1.7\"\n}\n```\n\n### Parent Theme Dependencies\n\nBy default, the plugin creates a configuration called `parentThemes` and adds\ndependencies to the latest released versions of the\n[Liferay Frontend Theme Styled](https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-styled),\n[Liferay Frontend Theme Unstyled](https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-unstyled),\nand [Liferay Frontend Theme Classic](https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-classic)\nartifacts. It is possible to override this setting and use a specific version of\nthe artifacts by manually adding dependencies to the `parentThemes`\nconfiguration. For example,\n\n```groovy\ndependencies {\n    parentThemes group: \"com.liferay\", name: \"com.liferay.frontend.theme.styled\", version: \"VERSION\"\n    parentThemes group: \"com.liferay\", name: \"com.liferay.frontend.theme.unstyled\", version: \"VERSION\"\n    parentThemes group: \"com.liferay.plugins\", name: \"classic-theme\", version: \"VERSION\"\n}\n```\n\nSpecifying dependency versions is not required when leveraging workspace's\n[Target Platform](/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform)\nfunctionality. All dependencies with the group ID `com.liferay` or\n`com.liferay.portal` are automatically set when targeting a platform. For\nexternal theme dependencies (e.g., `classic-theme` with the group ID\n`com.liferay.plugins`), you can find the version used by your specific @product@\ninstance by leveraging the\n[Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell).\nIn a Gogo shell prompt, execute the following command:\n\n    lb -s theme\n\nThis lists the deployed theme bundles and their versions. Extract the versions\nfor the theme dependencies you want to leverage and add them to your\nconfiguration.\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/tld-formatter-gradle-plugin.markdown",
    "content": "---\nheader-id: tld-formatter-gradle-plugin\n---\n\n# TLD Formatter Gradle Plugin\n\n[TOC levels=1-4]\n\nThe TLD Formatter Gradle plugin lets you format a project's TLD files using the\n[Liferay TLD Formatter](https://github.com/liferay/liferay-portal/tree/master/modules/util/tld-formatter)\ntool.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.tld.formatter\", version: \"1.0.9\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.tld.formatter\"\n```\n\nSince the plugin automatically resolves the Liferay TLD Formatter library as a\ndependency, you have to configure a repository that hosts the library\nand its transitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`formatTLD` | \\- | [`FormatTLDTask`](#formattldtask) | Runs the Liferay TLD Formatter to format files.\n\n### FormatTLDTask\n\nTasks of type `FormatTLDTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, such as [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.lang.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize),\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args) | TLD Formatter command line arguments\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.tldFormatter`](#liferay-tld-formatter-dependency)\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.liferay.tld.formatter.TLDFormatter\"`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`plugin` | `boolean` | `true` | Whether to format all the TLD files contained in the [`workingDir`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:workingDir) directory. If `false`, all `liferay-portlet-ext.tld` files are ignored. It sets the `tld.plugin` argument.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the TLD Formatter.\n\n### Liferay TLD Formatter Dependency\n\nBy default, the plugin creates a configuration called `tldFormatter` and adds\na dependency to the latest released version of Liferay TLD Formatter. It is\npossible to override this setting and use a specific version of the tool by\nmanually adding a dependency to the `tldFormatter` configuration:\n\n```groovy\ndependencies {\n    tldFormatter group: \"com.liferay\", name: \"com.liferay.tld.formatter\", version: \"1.0.5\"\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/tlddoc-builder-gradle-plugin.markdown",
    "content": "---\nheader-id: tlddoc-builder-gradle-plugin\n---\n\n# TLDDoc Builder Gradle Plugin\n\n[TOC levels=1-4]\n\nThe TLDDoc Builder Gradle plugin lets you run the\n[Tag Library Documentation Generator](http://web.archive.org/web/20070624180825/https://taglibrarydoc.dev.java.net/)\ntool in order to generate documentation for the JSP Tag Library Descriptor (TLD)\nfiles in your project.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.tlddoc.builder\", version: \"1.3.3\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n```\n\nThere are two TLDDoc Builder Gradle plugins you can apply to your project:\n\n- Apply the [*TLDDoc Builder Plugin*](#tlddoc-builder-plugin) to generate tag\nlibrary documentation for your project:\n\n    ```groovy\n    apply plugin: \"com.liferay.tlddoc.builder\"\n    ```\n\n- Apply the [*App TLDDoc Builder Plugin*](#app-tlddoc-builder-plugin) in a\nparent project to generate the tag library documentation as a single, combined\nHTML document for an application that spans different subprojects, each one\nrepresenting a different component of the same application:\n\n    ```groovy\n    apply plugin: \"com.liferay.app.tlddoc.builder\"\n    ```\n\nSince the plugin automatically resolves the Tag Library Documentation Generator\nlibrary as a dependency, you must configure a repository that hosts the\nlibrary and its transitive dependencies. The Liferay CDN repository hosts them\nall:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## TLDDoc Builder Plugin\n\nThe plugin adds three tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n<a name=\"copytlddocresources\"></a>`copyTLDDocResources` | \\- | [`Copy`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html) | Copies the tag library documentation resources from `src/main/tlddoc` to the [destination directory](#destinationdir) of the `tlddoc` task.\n`tlddoc` | `copyTLDDocResources`, `validateTLD` | [`TLDDocTask`](#tlddoctask) | Generates the tag library documentation.\n<a name=\"validatetld\"></a>`validateTLD` | \\- | [`ValidateSchemaTask`](#validateschematask) | Validates the TLD files in the project.\n\nThe `tlddoc` task is automatically configured with sensible defaults,\ndepending on whether the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin is applied:\n\nProperty Name | Default Value with the `java` plugin\n------------- | ------------------------------------\n[`destinationDir`](#destinationdir) | `${project.docsDir}/tlddoc`\n[`includes`](#includes) | `[\"**/*.tld\"]`\n[`source`](#source) | `project.sourceSets.main.resources.srcDirs`\n\nThe `validateTLD` task is also automatically configured with sensible defaults,\ndepending on whether the `java` plugin is applied:\n\nProperty Name | Default Value\n------------- | -------------\n`includes` | <p>**If the `java` plugin is applied:** `[\"**/*.tld\"]`</p><p>**Otherwise:** `[]`</p>\n`source` | <p>**If the `java` plugin is applied:** `project.sourceSets.main.resources.srcDirs`</p><p>**Otherwise:** `null`</p>\n\nBy default, the `tlddoc` task generates the documentation for all the TLD files\nthat are found in the resources directories of the `main` source set. The\ndocumentation files are saved in `build/docs/tlddoc` and include the files\ncopied from `src/main/tlddoc`.\n\nThe `copyTLDDocResources` task lets you add references to images and other\nresources directly in the TLD files. For example, if the project includes an\nimage called `breadcrumb.png` in the `src/main/tlddoc/images` directory, you can\nreference it in a TLD file contained in the `src/main/resources` directory:\n\n    <description>Hello World <![CDATA[<img src=\"../images/breadcrumb.png\"]]></description>\n\n## App TLDDoc Builder Plugin\n\nIn order to use the App TLDDoc Builder plugin, it is required to apply the\n`com.liferay.app.tlddoc.builder` plugin in a parent project (that is, a project\nthat is a common ancestor of all the subprojects representing the various\ncomponents of the app). It is also required to apply the\n[`com.liferay.tlddoc.builder`](#tlddoc-builder-plugin) plugin to all the\nsubprojects that contain TLD files.\n\nThe App TLDDoc Builder plugin automatically applies the [`base`](https://docs.gradle.org/current/userguide/standard_plugins.html#N135C1)\nplugin. It also adds three tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`appTLDDoc` | `copyAppTLDDocResources`, the [`validateTLD`](#validatetld) tasks of the subprojects | [`TLDDocTask`](#tlddoctask) | Generates tag library documentation for the app.\n`copyAppTLDDocResources` | \\- | [`Copy`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html) | Copies the tag library documentation resources defined as [inputs](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskInputs.html#getFiles\\(\\)) for the [`copyTDLDocResources`](#copytlddocresources) tasks of the subprojects, aggregating them into the [destination directory](#destinationdir) of the `appTLDDoc` task.\n`jarAppTLDDoc` | `appTLDDoc` | [`Jar`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html) | Assembles a JAR archive containing the tag library documentation files for this app.\n\nThe `appTLDDoc` task is automatically configured with sensible defaults:\n\nProperty Name | Default Value\n------------- | -------------\n[`destinationDir`](#destinationdir) | `${project.buildDir}/docs/tlddoc`\n[`source`](#source) | The sum of all the `tlddoc.source` values of the subprojects\n\n## Project Extension\n\nThe App TLDDoc Builder plugin exposes the following properties through the\nextension named `appTLDDocBuilder`:\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`subprojects` | `Set<Project>` | `project.subprojects` | The subprojects to include in the tag library documentation of the app.\n\nThe same extension exposes the following methods:\n\nMethod | Description\n------ | -----------\n`AppTLDDocBuilderExtension subprojects(Iterable<Project> subprojects)` | Include additional projects in the tag library documentation of the app.\n`AppTLDDocBuilderExtension subprojects(Project... subprojects)` | Include additional projects in the tag library documentation of the app.\n\n## Tasks\n\n### TLDDocTask\n\nTasks of type `TLDDocTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, such as [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.tlddoc.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize),\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args) | Tag Library Documentation Generator command line arguments\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.tlddoc`](#tag-library-documentation-generator-dependency)\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.sun.tlddoc.TLDDoc\"`\n[`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize) | `\"256m\"`\n\nThe `TLDDocTask` class is also very similar to [`SourceTask`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html),\nwhich means it provides a `source` property and lets you specify include and\nexclude patterns.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"destinationdir\"></a>`destinationDir` | `File` | `null` | The directory where the tag library documentation files are saved.\n`excludes` | `Set<String>` | `[]` | The TLD file patterns to exclude.\n<a name=\"includes\"></a>`includes` | `Set<String>` | `[]` | The TLD file patterns to include.\n<a name=\"source\"></a>`source` | [`FileTree`](https://docs.gradle.org/current/javadoc/org/gradle/api/file/FileTree.html) | `[]` | The TLD files to generate documentation for, after the include and exclude patterns have been applied.\n`xsltDir` | `File` | `null` | The directory that contains the custom XSLT stylesheets used by the Tag Library Documentation Generator to produce the final documentation files. It sets the `-xslt` argument.\n\nThe properties of type `File` support any type that can be resolved by\n[`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.tlddoc.Object\\)).\n\n#### Task Methods\n\nThe methods available for `TLDDocTask` are exactly the same as the one defined\nin the [`SourceTask`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html)\nclass.\n\n### ValidateSchemaTask\n\nTasks of type `ValidateSchemaTask` extend [`SourceTask`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html),\nso all its properties and methods, such as [`include`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:include\\(java.lang.Iterable\\))\nand [`exclude`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:exclude\\(java.lang.Iterable\\)),\nare available.\n\nTasks of this type invoke the [`schemavalidate`](http://ant.apache.org/manual/Tasks/schemavalidate.html)\nAnt task in order to validate XML files described by an XML schema.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`dtdDisabled` | `boolean` | `false` | Whether to disable DTD support.\n`fullChecking` | `boolean` | `true` | Whether to enable full schema checking.\n`lenient` | `boolean` | `false` | Whether to only check if the XML document is well-formed.\n<a name=\"xmlparserclassname\"></a>`xmlParserClassName` | `String` | `null` | The class name of the XML parser to use.\n<a name=\"xmlparserclasspath\"></a>`xmlParserClasspath` | `FileCollection` | `null` | The classpath with the XML parser.\n\nIt is possible to use Closures and Callables as values for the `String`\nproperties to defer evaluation until task execution.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the TLDDoc Builder.\n\n### Tag Library Documentation Generator Dependency\n\nBy default, the plugin creates a configuration called `tlddoc` and adds a\ndependency to the 1.3 version of the Tag Library Documentation Generator. It is\npossible to override this setting and use a specific version of the tool by\nmanually adding a dependency to the `tlddoc` configuration:\n\n```groovy\ndependencies {\n    tlddoc group: \"taglibrarydoc\", name: \"tlddoc\", version: \"1.3\"\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/whip-gradle-plugin.markdown",
    "content": "---\nheader-id: whip-gradle-plugin\n---\n\n# Whip Gradle Plugin\n\n[TOC levels=1-4]\n\nThe Whip Gradle plugin lets you use the [Liferay Whip](https://github.com/liferay/liferay-portal/tree/master/modules/test/whip)\nlibrary to ensure that unit tests fully cover your project's code. See\n[here](https://github.com/liferay/liferay-portal/tree/master/modules/sdk/gradle-plugins-whip/src/gradleTest/smoke)\nfor a usage sample.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.whip\", version: \"1.0.7\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.whip\"\n```\n\nSince the plugin automatically resolves the Liferay Whip library as a\ndependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\nBy default, Whip is automatically applied to all tasks of type [`Test`](https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/testing/Test.html).\nIf a task has Whip applied and Whip is [enabled](#enabled), then Whip is\nconfigured as a Java Agent.\n\n## Project Extension\n\nThe Whip Gradle plugin exposes the following properties through the extension\nnamed `whip`:\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n<a name=\"version\"></a>`version` | `String` | `latest.release` | The version of the Liferay Whip library to use.\n\nThe same extension exposes the following methods:\n\nMethod | Description\n------ | -----------\n`void applyTo(Task task)` | Applies Whip to a task. The task instance must implement the [`JavaForkOptions`](https://docs.gradle.org/current/javadoc/org/gradle/process/JavaForkOptions.html) interface.\n\n## Task Extension\n\nIf Whip is applied, the following task properties are available through the\nextension named `whip`:\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`dataFile` | `File` | `test-coverage/whip.dat` |\n<a name=\"enabled\"></a>`enabled` | `boolean` | `true` | Whether to configure Whip as a Java Agent.\n`excludes` | `List<String>` | `[]` | The class name patterns to exclude when checking for unit test code coverage. For example, a value could be `['.*Test', '.*Test\\\\$.*', '.*\\\\$Proxy.*', 'com/liferay/whip/.*']`.\n`includes` | `List<String>` | `[]` | The class name patterns to include when checking for unit test code coverage.\n`instrumentDump` | `boolean` | `false` |\n<a name=\"whipjarfile\"></a>`whipJarFile` | `File` | The first file in the `whip` configuration whose name starts with `com.liferay.whip-`. | The Whip JAR file.\n\nThe same extension exposes the following methods:\n\nMethod | Description\n------ | -----------\n`WhipTaskExtension excludes(Iterable<Object> excludes)` | Adds class name patterns to exclude when checking for unit test coverage.\n`WhipTaskExtension excludes(Object... excludes)` | Adds class name patterns to exclude when checking for unit test coverage.\n`WhipTaskExtension includes(Iterable<Object> includes)` | Adds class name patterns to include when checking for unit test coverage.\n`WhipTaskExtension includes(Object... includes)` | Adds class name patterns to include when checking for unit test coverage.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use Whip.\n\n### Liferay Whip Dependency\n\nBy default, the Whip Gradle plugin creates a configuration called `whip` and\nadds a dependency to the version of Liferay Whip configured in the [`whip.version`](#version)\nextension property. It is possible to override this setting and use a specific\nversion of the library by manually adding a dependency to the `whip`\nconfiguration:\n\n```groovy\ndependencies {\n    whip group: \"com.liferay\", name: \"com.liferay.whip\", version: \"1.0.1\"\n}\n```\n\nIn order to leverage the sensible default of the [`whip.whipJarFile`](#whipjarfile)\ntask property, the name of the dependency must be `com.liferay.whip`. Otherwise,\nit will be necessary to set the value of the `whip.whipJarFile` property\nmanually.\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/wsdd-builder-gradle-plugin.markdown",
    "content": "---\nheader-id: wsdd-builder-gradle-plugin\n---\n\n# WSDD Builder Gradle Plugin\n\n[TOC levels=1-4]\n\nThe WSDD Builder Gradle plugin lets you run the [Liferay WSDD Builder](https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-wsdd-builder)\ntool to generate the [Apache Axis](http://axis.apache.org/axis/) Web Service\nDeployment Descriptor (WSDD) files from a [Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n`service.xml` file.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.wsdd.builder\", version: \"1.0.13\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.portal.tools.wsdd.builder\"\n```\n\nThe WSDD Builder plugin automatically applies the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin.\n\nSince the plugin automatically resolves the Liferay WSDD Builder library as a\ndependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t}\n}\n```\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`buildWSDD` | [`compileJava`](https://docs.gradle.org/current/userguide/java_plugin.html#sec:compile) | [`BuildWSDDTask`](#buildwsddtask) | Runs the Liferay WSDD Builder.\n\nBy default, the `buildWSDD` task uses the `${project.projectDir}/service.xml`\nfile as input. Then, it generates `${project.projectDir}/server-config.wsdd` and\nthe `*_deploy.wsdd` and `*_undeploy.wsdd` files in the first [`resources`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceSet.html#org.gradle.api.tasks.SourceSet:resources)\ndirectory of the `main` [source set](https://docs.gradle.org/current/userguide/java_plugin.html#N1503E)\n(by default: `src/main/resources`).\n\nIf the [`war`](https://docs.gradle.org/current/userguide/war_plugin.html)\nplugin is applied, the task uses `${project.webAppDir}/WEB-INF/service.xml` as\ninput to generate `${project.webAppDir}/WEB-INF/server-config.wsdd`. The\n`*_deploy.wsdd` and `*_undeploy.wsdd` files are still generated in the first\n`resources` directory of the `main` source set.\n\nLiferay WSDD Build Service requires an additional classpath (configured with the\n`buildWSDD.builderClasspath` property), to correctly generate the WSDD files.\nThe `buildWSDD` task uses the following default value, which creates an implicit\ndependency to the `compileJava` task:\n\n```groovy\ntasks.compileJava.outputs.files + sourceSets.main.compileClasspath + sourceSets.main.runtimeClasspath\n```\n\n### BuildWSDDTask\n\nTasks of type `BuildWSDDTask` extend [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html),\nso all its properties and methods, such as [`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args\\(java.lang.Iterable\\))\nand [`maxHeapSize`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:maxHeapSize),\nare available. They also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`args`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:args) | WSDD Builder command line arguments\n[`classpath`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:classpath) | [`project.configurations.wsddBuilder`](#liferay-wsdd-builder-dependency)\n[`main`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main) | `\"com.liferay.portal.tools.wsdd.builder.WSDDBuilder\"`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`builderClasspath` | `String` | `null` | A classpath that the Liferay WSDD Builder uses to generate WSDD files. It sets the `wsdd.class.path` argument.\n`inputFile` | `File` | `null` | A `service.xml` from which to generate the WSDD files. It sets the `wsdd.input.file` argument.\n`outputDir` | `File` | `null` | A directory where the `*_deploy.wsdd` and `*_undeploy.wsdd` files are generated. It sets the `wsdd.output.path` argument.\n`serverConfigFile` | `File` | `${project.projectDir}/server-config.wsdd` | A `server-config.wsdd` file to generate. It sets the `wsdd.server.config.file` argument.\n`serviceNamespace` | `String` | `\"Plugin\"` | A namespace for the WSDD Service. It sets the `wsdd.service.namespace` argument.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.lang.Object\\)).\nMoreover, it is possible to use Closures and Callables as values for the\n`String` properties, to defer evaluation until task execution.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the WSDD Builder.\n\n### Liferay WSDD Builder Dependency\n\nBy default, the plugin creates a configuration called `wsddBuilder` and adds a\ndependency to the latest released version of the Liferay WSDD Builder. It is\npossible to override this setting and use a specific version of the tool by\nmanually adding a dependency to the `wsddBuilder` configuration:\n\n```groovy\ndependencies {\n    wsddBuilder group: \"com.liferay\", name: \"com.liferay.portal.tools.wsdd.builder\", version: \"1.0.10\"\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/wsdl-builder-gradle-plugin.markdown",
    "content": "---\nheader-id: wsdl-builder-gradle-plugin\n---\n\n# WSDL Builder Gradle Plugin\n\n[TOC levels=1-4]\n\nThe WSDL Builder Gradle plugin lets you generate [Apache Axis](http://axis.apache.org/axis/)\nclient stubs from Web Service Description (WSDL) files.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.wsdl.builder\", version: \"2.0.3\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.wsdl.builder\"\n```\n\nThe WSDL Builder plugin automatically applies the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin.\n\nSince the plugin automatically resolves the Apache Axis library as a dependency,\nyou have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## Tasks\n\nThe plugin adds one main task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`buildWSDL` | \\- | [`BuildWSDLTask`](#buildwsdltask) | Generates WSDL client stubs.\n\nBy default, the `buildWSDL` task looks for WSDL files in the\n`${project.projectDir}/wsdl` directory. If the [`war`](https://docs.gradle.org/current/userguide/war_plugin.html)\nplugin is applied, it looks in the `${project.webAppDir}/WEB-INF/wsdl`\ndirectory.\n\nFor each WSDL file that can be found, the task generates client stubs via direct\ninvocation of the [*WSDL2Java*](http://axis.apache.org/axis/java/user-guide.html#Client-side_bindings)\ntool, saving them in the first [`java`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceSet.html#org.gradle.api.tasks.SourceSet:java)\ndirectory of the `main` [source set](https://docs.gradle.org/current/userguide/java_plugin.html#N1503E)\n(by default: `src/main/java`).\n\nIf configured to do so, `buildWSDL` can instead save the client stub Java files\nin a temporary directory, compile them, and package them in JAR files. The JAR\nfiles are named after the WSDL file and saved in `${project.projectDir}/lib`, by\ndefault, or in `${project.webAppDir}/WEB-INF/lib`, if the `war` plugin is\napplied.\n\n### BuildWSDLTask\n\nTasks of type `FormatWSDLTask` extend [`SourceTask`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html),\nso all its properties and methods, such as [`include`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:include\\(java.lang.Iterable\\))\nand [`exclude`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:exclude\\(java.lang.Iterable\\)),\nare available.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`buildLibs` | `boolean` | `true` | Whether to package the client stub classes of each WSDL file in JAR files, saved to the directory the `destinationDir` property references. If `false`, the task generates the client stub Java files to the `destinationDir` directory.\n`destinationDir` | `File` | `null` | A directory where the client stub Java files (if `buildLibs` is `false`) or the client stub JAR files (if `buildLibs` is `true`) are saved.\n`generateOptions.mapping` | `Map` | `[:]` | Namespace-to-package mappings (sets the `--NStoPkg` argument in the *WSDL2Java* invocation). It is possible to use a `Closure` or a `Callable`, to defer evaluation until task execution..\n`generateOptions.noWrapped` | `boolean` | `false` | Whether to turn off support for \"wrapped\" document/literal (sets the `--noWrapped` argument in the *WSDL2Java* invocation).\n`generateOptions.serverSide` | `boolean` | `false` | Whether to emit server-side bindings for the web service (sets the `--server-side` argument in the *WSDL2Java* invocation).\n`generateOptions.verbose` | `boolean` | `false` | Whether to print informational messages (sets the `--verbose` argument in the *WSDL2Java* invocation).\n`includeSource` | `boolean` | `true` | Whether to package the client stub Java files in the JAR file's  `OSGI-OPT/src` directory. If `buildLibs` is `false`, this property has no effect.\n`includeWSDLs` | `boolean` | `true` | Whether to configure the [`processResources`](https://docs.gradle.org/current/userguide/java_plugin.html#sec:resources) task to include the WSDL files in the project JAR's `wsdl` directory.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.lang.Object\\)).\n\n#### Task Methods\n\nMethod Signature | Description\n---------------- | -----------\n`generateOptions.mapping(Object namespace, Object packageName)` | Adds a namespace-to-package mapping.\n`generateOptions.mappings(Map mappings)` | Adds multiple namespace-to-package mappings.\n\n#### Helper Tasks\n\nAt the end of the [project evaluation](https://docs.gradle.org/current/userguide/build_lifecycle.html#N11BAE),\na series of helper tasks are created for each WSDL file returned by the\n[`source`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:source)\nproperty of the `BuildWSDLTask` tasks. The names of the helper tasks start with\nthe WSDL file name, without any extension.\n\n- `${WSDL file title}Generate` of type [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html): invokes [*WSDL2Java*](https://axis.apache.org/axis/java/reference.html#WSDL2Java_Reference) to generate the client stubs for the WSDL file.\n\nIf `buildWSDLTask.buildLibs` is `true`, the following helper tasks are also\ncreated:\n\n- `${WSDL file title}Compile` of type [`JavaCompile`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html): compiles the client stub Java files for the WSDL file.\n- `${WSDL file title}Jar` of type [`Jar`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html): packages in a JAR file called `${WSDL file title}-ws.jar`, the client stub for the WSDL file.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use WSDL Builder.\n\n### Apache Axis Dependency\n\nBy default, the plugin creates a configuration called `wsdlBuilder` and adds the\nfollowing dependencies:\n\n- `axis:axis-wsdl4j:1.5.1`\n- `com.liferay:org.apache.axis:1.4.LIFERAY-PATCHED-1`\n- `commons-discovery:commons-discovery:0.2`\n- `commons-logging:commons-logging:1.0.4`\n- `javax.activation:activation:1.1`\n- `javax.mail:mail:1.4`\n- `org.apache.axis:axis-jaxrpc:1.4`\n- `org.apache.axis:axis-saaj:1.4`\n\nIt is possible to override this setting and use a specific version of Apache\nAxis, by manually populating the `wsdlBuilder` configuration with the desired\ndependencies.\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/xml-formatter-gradle-plugin.markdown",
    "content": "---\nheader-id: xml-formatter-gradle-plugin\n---\n\n# XML Formatter Gradle Plugin\n\n[TOC levels=1-4]\n\nThe XML Formatter Gradle plugin lets you format a project's XML files using the\n[Liferay XML Formatter](https://github.com/liferay/liferay-portal/tree/master/modules/util/xml-formatter)\ntool.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.xml.formatter\", version: \"1.0.11\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.xml.formatter\"\n```\n\nSince the plugin automatically resolves the Liferay XML Formatter library as a\ndependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## Tasks\n\nThe plugin adds one task to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`formatXML` | \\- | [`FormatXMLTask`](#formatxmltask) | Runs the Liferay XML Formatter to format the project files.\n\nIf the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin is applied, the task formats XML files contained in the [`resources`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceSet.html#org.gradle.api.tasks.SourceSet:resources)\ndirectories of the `main` [source set](https://docs.gradle.org/current/userguide/java_plugin.html#N1503E)\n(by default: `src/main/resources/**/*.xml`).\n\n### FormatXMLTask\n\nTasks of type `FormatXMLTask` extend [`SourceTask`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html),\nso all its properties and methods, such as [`include`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:include\\(java.lang.Iterable\\))\nand [`exclude`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceTask.html#org.gradle.api.tasks.SourceTask:exclude\\(java.lang.Iterable\\)),\nare available.\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`classpath` | [`FileCollection`](https://docs.gradle.org/current/javadoc/org/gradle/api/file/FileCollection.html) | [`project.configurations.xmlFormatter`](#liferay-xml-formatter-dependency) | The classpath for executing the main class.\n`mainClassName` | `String` | `\"com.liferay.xml.formatter.XMLFormatter\"` | The fully qualified name of the XML Formatter Main class.\n`stripComments` | `boolean` | `false` | Whether to remove all the comments from the XML files. It sets the `xml.formatter.strip.comments` argument.\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the XML Formatter.\n\n### Liferay XML Formatter Dependency\n\nBy default, the plugin creates a configuration called `xmlFormatter` and adds\na dependency to the latest released version of the Liferay XML Formatter. It is\npossible to override this setting and use a specific version of the tool by\nmanually adding a dependency to the `xmlFormatter` configuration:\n\n```groovy\ndependencies {\n    xmlFormatter group: \"com.liferay\", name: \"com.liferay.xml.formatter\", version: \"1.0.5\"\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-gradle-plugins/xsd-builder-gradle-plugin.markdown",
    "content": "---\nheader-id: xsd-builder-gradle-plugin\n---\n\n# XSD Builder Gradle Plugin\n\n[TOC levels=1-4]\n\nThe XSD Builder Gradle plugin lets you generate [Apache XMLBeans](https://xmlbeans.apache.org/)\nbindings from XML Schema (XSD) files.\n\nThe plugin has been successfully tested with Gradle 4.10.2.\n\n## Usage\n\nTo use the plugin, include it in your build script:\n\n```groovy\nbuildscript {\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.xsd.builder\", version: \"1.0.7\"\n    }\n\n    repositories {\n        maven {\n            url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n        }\n    }\n}\n\napply plugin: \"com.liferay.xsd.builder\"\n```\n\nThe XSD Builder plugin automatically applies the [`java`](https://docs.gradle.org/current/userguide/java_plugin.html)\nplugin.\n\nSince the plugin automatically resolves the Liferay Service Builder library as a\ndependency, you have to configure a repository that hosts the library and its\ntransitive dependencies. The Liferay CDN repository hosts them all:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\n## Tasks\n\nThe plugin adds three tasks to your project:\n\nName | Depends On | Type | Description\n---- | ---------- | ---- | -----------\n`buildXSD` | `buildXSDCompile` | [`BuildXSDTask`](#buildxsdtask) | Generates XMLBeans bindings and compiles them in a JAR file.\n`buildXSDGenerate` | `cleanBuildXSDGenerate` | [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html) | Invokes the [XMLBeans Schema Compiler](https://xmlbeans.apache.org/docs/2.6.0/guide/tools.html#scomp) to generate Java types from XML Schema.\n`buildXSDCompile` | `buildXSDGenerate`, `cleanBuildXSDCompile` | [`JavaCompile`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html) | Compiles the generated Java types.\n\nBy default, the `buildXSD` task looks for XSD files in the\n`${project.projectDir}/xsd` directory, and saves the generated JAR file as\n`${project.projectDir}/lib/${project.archivesBaseName}-xbean.jar`.\n\nIf the [`war`](https://docs.gradle.org/current/userguide/war_plugin.html)\nplugin is applied, the task looks for XSD files in the\n`${project.webAppDir}/WEB-INF/xsd` directory, and saves the generated JAR file\nas `${project.webAppDir}/WEB-INF/lib/${project.archivesBaseName}-xbean.jar`.\n\n### BuildXSDTask\n\nTasks of type `BuildXSDTask` extend [`Zip`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html).\nThey also have the following properties set by default:\n\nProperty Name | Default Value\n------------- | -------------\n[`appendix`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html#org.gradle.api.tasks.bundling.Zip:appendix) | `\"xbean\"`\n[`extension`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html#org.gradle.api.tasks.bundling.Zip:extension) | `\"jar\"`\n[`version`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html#org.gradle.api.tasks.bundling.Zip:version) | `null`\n\nFor each task of type `BuildXSDTask`, the following helper tasks are created:\n\n- `${buildXSDTask.name}Compile`\n- `${buildXSDTask.name}Generate`\n\n#### Task Properties\n\nProperty Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`inputDir` | `File` | `null` | A directory containing XSD files from which to generate [Apache XMLBeans](https://xmlbeans.apache.org/) bindings.\n\nThe properties of type `File` support any type that can be resolved by [`project.file`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:file\\(java.lang.Object\\)).\n\n## Additional Configuration\n\nThere are additional configurations that can help you use the XSD Builder.\n\n### Apache XMLBeans Dependency\n\nBy default, the XSD Builder Gradle plugin creates a configuration called\n`xsdBuilder` and adds a dependency to the 2.5.0 version of Apache XMLBeans. It\nis possible to override this setting and use a specific version of the library\nby manually adding a dependency to the `xsdBuilder` configuration:\n\n```groovy\ndependencies {\n    xsdBuilder group: \"org.apache.xmlbeans\", name: \"xmlbeans\", version: \"2.6.0\"\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-item-selector-criterion-and-return-types.markdown",
    "content": "---\nheader-id: item-selector-criterion-and-return-types\n---\n\n# Item Selector Criterion and Return Types\n\n[TOC levels=1-4]\n\n@product@ contains Item Selector criterion \n([`ItemSelectorCriterion`](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/ItemSelectorCriterion.html)) \nand return type \n([`ItemSelectorReturnType`](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/ItemSelectorReturnType.html)) \nclasses that developers can use in Item Selectors. The following sections in \nthis document list the available classes: \n\n-   [Criterion Classes](#item-selector-criterion-classes)\n-   [Return Type Classes](#item-selector-return-type-classes)\n\nIf there isn't a criterion or return type for your needs, you can create your \nown by following the instructions in \n[Creating Custom Criterion and Return Types](/docs/7-2/frameworks/-/knowledge_base/f/creating-custom-criterion-and-return-types). \nFor more information on Item Selectors in general, including definitions of \ncriterion and return types, see the \n[Item Selector introduction](/docs/7-2/frameworks/-/knowledge_base/f/item-selector). \n\n## Item Selector Criterion Classes\n\n**Assets:**\n\n[AssetDisplayPageSelectorCriterion](@app-ref@/asset/latest/javadocs/com/liferay/asset/display/page/item/selector/criterion/AssetDisplayPageSelectorCriterion.html): \nAsset display page. \n\n**Blogs:**\n\n[BlogsItemSelectorCriterion](@app-ref@/blogs/latest/javadocs/com/liferay/blogs/item/selector/criterion/BlogsItemSelectorCriterion.html): \nBlogs item. \n\n**Page Fragments:**\n\n[FragmentItemSelectorCriterion](@app-ref@/fragment/latest/javadocs/com/liferay/fragment/item/selector/criterion/FragmentItemSelectorCriterion.html): \n[Page fragment](/docs/7-2/frameworks/-/knowledge_base/f/page-fragments). \n\n**Item Selector:**\n\n[AudioItemSelectorCriterion](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/criteria/audio/criterion/AudioItemSelectorCriterion.html): \nAudio file. \n\n[FileItemSelectorCriterion](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/criteria/file/criterion/FileItemSelectorCriterion.html): \nDocument Library file. \n\n[ImageItemSelectorCriterion](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/criteria/image/criterion/ImageItemSelectorCriterion.html): \nImage file. \n\n[UploadItemSelectorCriterion](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/criteria/upload/criterion/UploadItemSelectorCriterion.html):\nUploadable file. \n\n[URLItemSelectorCriterion](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/criteria/url/criterion/URLItemSelectorCriterion.html):\nURL. \n\n[VideoItemSelectorCriterion](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/criteria/video/criterion/VideoItemSelectorCriterion.html):\nVideo file. \n\n**Journal (Web Content):**\n\n[JournalItemSelectorCriterion](@app-ref@/journal/latest/javadocs/com/liferay/journal/item/selector/criterion/JournalItemSelectorCriterion.html): \nWeb content article. \n\n**Knowledge Base:**\n\n[KBAttachmentItemSelectorCriterion](@app-ref@/knowledge-base/latest/javadocs/com/liferay/knowledge/base/item/selector/criterion/KBAttachmentItemSelectorCriterion.html): \nKnowledge base attachment. \n\n**Layout:**\n\n[LayoutItemSelectorCriterion](@app-ref@/layout/latest/javadocs/com/liferay/layout/item/selector/criterion/LayoutItemSelectorCriterion.html): \nPage layout. \n\n**Organizations:**\n\n[OrganizationItemSelectorCriterion](@app-ref@/organizations/latest/javadocs/com/liferay/organizations/item/selector/OrganizationItemSelectorCriterion.html): \nOrganization. \n\n**Roles:**\n\n[RoleItemSelectorCriterion](@app-ref@/roles/latest/javadocs/com/liferay/roles/item/selector/RoleItemSelectorCriterion.html): \nRole. \n\n**Site Navigation:**\n\n[SiteNavigationMenuItemItemSelectorCriterion](@app-ref@/site-navigation/latest/javadocs/com/liferay/site/navigation/item/selector/criterion/SiteNavigationMenuItemItemSelectorCriterion.html): \nSite navigation menu item.\n\n[SiteNavigationMenuItemSelectorCriterion](@app-ref@/site-navigation/latest/javadocs/com/liferay/site/navigation/item/selector/criterion/SiteNavigationMenuItemSelectorCriterion.html): \nSite navigation menu. \n\n**Sites:**\n\n[SiteItemSelectorCriterion](@app-ref@/site/latest/javadocs/com/liferay/site/item/selector/criterion/SiteItemSelectorCriterion.html): \nSite.\n\n**User Groups Admin:**\n\n[UserGroupItemSelectorCriterion](@app-ref@/user-groups-admin/latest/javadocs/com/liferay/user/groups/admin/item/selector/UserGroupItemSelectorCriterion.html): \nUser group.\n\n**Users Admin:**\n\n[UserItemSelectorCriterion](@app-ref@/users-admin/latest/javadocs/com/liferay/users/admin/item/selector/UserItemSelectorCriterion.html): \nUser. \n\n**Wiki:** \n\n[WikiAttachmentItemSelectorCriterion](@app-ref@/wiki/latest/javadocs/com/liferay/wiki/item/selector/criterion/WikiAttachmentItemSelectorCriterion.html): \nWiki attachment.\n\n[WikiPageItemSelectorCriterion](@app-ref@/wiki/latest/javadocs/com/liferay/wiki/item/selector/criterion/WikiPageItemSelectorCriterion.html): \nWiki page.\n\n## Item Selector Return Type Classes\n\n**Adaptive Media:**\n\n[AMImageFileEntryItemSelectorReturnType](@app-ref@/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/item/selector/AMImageFileEntryItemSelectorReturnType.html): \nAdaptive Media image file. \n\n[AMImageURLItemSelectorReturnType](@app-ref@/adaptive-media/latest/javadocs/com/liferay/adaptive/media/image/item/selector/AMImageURLItemSelectorReturnType.html): \nAdaptive Media image URL.\n\n**Item Selector:**\n\n[Base64ItemSelectorReturnType](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/criteria/Base64ItemSelectorReturnType.html): \nThe entity's Base64 encoding as a `String`. \n\n[DownloadURLItemSelectorReturnType](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/criteria/DownloadURLItemSelectorReturnType.html): \nThe entity's download URL as a `String`. \n\n[FileEntryItemSelectorReturnType](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/criteria/FileEntryItemSelectorReturnType.html): \nFile entry information as a JSON object. \n\n[URLItemSelectorReturnType](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/criteria/URLItemSelectorReturnType.html): \nThe entity's URL as a `String`. \n\n[UUIDItemSelectorReturnType](@app-ref@/item-selector/latest/javadocs/com/liferay/item/selector/criteria/UUIDItemSelectorReturnType.html): \nThe entity's universally unique identifier (UUID) as a `String`. \n\n**Site:**\n\n[SiteItemSelectorReturnType](@app-ref@/site/latest/javadocs/com/liferay/site/item/selector/criteria/SiteItemSelectorReturnType.html): \nThe Site's information as a JSON object. \n\n**Wiki:**\n\n[WikiPageTitleItemSelectorReturnType](@app-ref@/wiki/latest/javadocs/com/liferay/wiki/item/selector/WikiPageTitleItemSelectorReturnType.html): \nThe wiki page's title. \n\n[WikiPageURLItemSelectorReturnType](@app-ref@/wiki/latest/javadocs/com/liferay/wiki/item/selector/WikiPageURLItemSelectorReturnType.html): \nThe wiki page's URL. \n"
  },
  {
    "path": "en/developer/reference/articles/02-java-apis.markdown",
    "content": "---\nheader-id: java-apis\n---\n\n# Java APIs\n\n[TOC levels=1-4]\n\nHere you'll find Javadoc for @product-ver@ and @product@ apps. Note that each \nlink to the Javadoc listed here opens in a new window. \n\nFor help finding module attributes and configuring dependencies, see \n[Configuring Dependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies). \n\n## @product-ver@ Java APIs\n\nThis table contains links to the Javadoc for @product-ver@ API modules. The root \nlocation for these modules' Javadoc is \n[here](@platform-ref@/7.2-latest/javadocs/). \n\n<style>\ntable, th, td {\n    border: 1px solid black;\n    border-collapse: collapse;\n}\nth, td {\n    padding: 5px;\n    text-align: left;\n}\n</style>\n<table style=\"width:100%\">\n\n  <tr>\n    <th rowspan=\"5\">Core</th>\n    <td>\n\t<a href=\"@platform-ref@/7.2-latest/javadocs/portal-kernel/index.html\" target=\"_blank\">\n\tcom.liferay.portal.kernel (portal-kernel):\n\t</a>&nbsp;for developing applications on @product@\n    </td>\n  </tr>\n\n  <tr>\n    <td>\n       <a href=\"@platform-ref@/7.2-latest/javadocs/util-bridges/index.html\" target=\"_blank\">\n\tcom.liferay.util.bridges (util-bridges):\n\t</a>&nbsp;for using various non-proprietary computing languages, frameworks, and utilities\n       on @product@\n    </td>\n  </tr>\n\n  <tr>\n    <td>\n       <a href=\"@platform-ref@/7.2-latest/javadocs/util-java/index.html\" target=\"_blank\">\n\tcom.liferay.util.java (util-java):\n\t</a>&nbsp;for using various Java-related frameworks and utilities on @product@\n    </td>\n  </tr>\n\n  <tr>\n    <td>\n       <a href=\"@platform-ref@/7.2-latest/javadocs/util-slf4j/index.html\" target=\"_blank\">\n\tcom.liferay.util.slf4j (util-slf4j):\n\t</a>&nbsp;for using the Simple Logging Facade for Java (SLF4J)\n    </td>\n  </tr>\n\n  <tr>\n    <td>\n       <a href=\"@platform-ref@/7.2-latest/javadocs/portal-impl/index.html\" target=\"_blank\">\n\tcom.liferay.portal.impl (portal-impl):\n\t</a>&nbsp;refer to this only if you are an advanced Liferay developer that needs a deeper\n       understanding of @product-ver@'s implementation in order to contribute to it\n    </td>\n  </tr>\n\n</table>\n\n## @product@ App Java APIs\n\nThe tables in this section link to the API modules for apps in these categories: \n\n-   [Collaboration](#collaboration)\n-   [Forms and Workflow](#forms-and-workflow)\n-   [Foundation](#foundation)\n-   [Web Experience](#web-experience)\n\nNote that the root location for these modules' Javadoc is \n[@app-ref@](@app-ref@). \n\n### Collaboration\n\n<style>\ntable, th, td {\n    border: 1px solid black;\n    border-collapse: collapse;\n}\nth, td {\n    padding: 5px;\n    text-align: left;\n}\n</style>\n\n<table style=\"width:100%\">\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/announcements/\" target=\"_blank\">\n      Announcements\n      </a>\n    </th>\n    <td>\n       com.liferay.announcements.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"3\">\n      <a href=\"@app-ref@/blogs/\" target=\"_blank\">\n      Blogs\n      </a>\n    </th>\n    <td>\n       com.liferay.blogs.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n    com.liferay.blogs.item.selector.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n    com.liferay.blogs.recent.bloggers.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/comment/\" target=\"_blank\">\n      Comment\n      </a>\n    </th>\n    <td>\n       com.liferay.comment.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"7\">\n      <a href=\"@app-ref@/document-library/\" target=\"_blank\">\n      Document Library\n      </a>\n    </th>\n    <td>\n       com.liferay.document.library.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.document.library.content.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.document.library.file.rank.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.document.library.repository.authorization.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.document.library.repository.cmis.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.document.library.repository.external.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.document.library.sync.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/flags/\" target=\"_blank\">\n      Flags\n      </a>\n    </th>\n    <td>\n       com.liferay.flags.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/invitation/\" target=\"_blank\">\n      Invitation\n      </a>\n    </th>\n    <td>\n       com.liferay.invitation.invite.members.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"2\">\n      <a href=\"@app-ref@/item-selector/\" target=\"_blank\">\n      Item Selector\n      </a>\n    </th>\n    <td>\n       com.liferay.item.selector.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.item.selector.criteria.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/mentions/\" target=\"_blank\">\n      Mentions\n      </a>\n    </th>\n    <td>\n       com.liferay.mentions.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/message-boards/\" target=\"_blank\">\n      Message Boards\n      </a>\n    </th>\n    <td>\n       com.liferay.message.boards.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/ratings/\" target=\"_blank\">\n      Ratings\n      </a>\n    </th>\n    <td>\n        com.liferay.ratings.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/reading-time/\" target=\"_blank\">\n      Reading Time\n      </a>\n    </th>\n    <td>\n        com.liferay.reading.time.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"4\">\n      <a href=\"@app-ref@/social/\" target=\"_blank\">\n      Social\n      </a>\n    </th>\n    <td>\n        com.liferay.social.activities.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.social.activity.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.social.bookmarks.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.social.user.statistics.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/subscription/\" target=\"_blank\">\n      Subscription\n      </a>\n    </th>\n    <td>\n        com.liferay.subscription.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/upload/\" target=\"_blank\">\n      Upload\n      </a>\n    </th>\n    <td>\n         com.liferay.upload.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/wiki/\" target=\"_blank\">\n      Wiki\n      </a>\n    </th>\n    <td>\n         com.liferay.wiki.api\n      </td>\n  </tr>\n\n</table>\n\n### Forms and Workflow\n\n<style>\ntable, th, td {\n    border: 1px solid black;\n    border-collapse: collapse;\n}\nth, td {\n    padding: 5px;\n    text-align: left;\n}\n</style>\n\n<table style=\"width:100%\">\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/calendar/\" target=\"_blank\">\n      Calendar\n      </a>\n    </th>\n    <td>\n         com.liferay.calendar.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/dynamic-data-lists/\" target=\"_blank\">\n      Dynamic Data Lists\n      </a>\n    </th>\n    <td>\n         com.liferay.dynamic.data.lists.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/dynamic-data-mapping/\" target=\"_blank\">\n      Dynamic Data Mapping\n      </a>\n    </th>\n    <td>\n         com.liferay.dynamic.data.mapping.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/polls/\" target=\"_blank\">\n      Polls\n      </a>\n    </th>\n    <td>\n         com.liferay.polls.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-reports-engine/\" target=\"_blank\">\n      Portal Reports Engine\n      </a>\n    </th>\n    <td>\n         com.liferay.portal.reports.engine.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-rules-engine/\" target=\"_blank\">\n      Portal Rules Engine\n      </a>\n    </th>\n    <td>\n          com.liferay.portal.rules.engine.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"4\">\n      <a href=\"@app-ref@/portal-workflow/\" target=\"_blank\">\n      Portal Workflow\n      </a>\n    </th>\n    <td>\n          com.liferay.portal.workflow.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.workflow.kaleo.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.workflow.kaleo.definition.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.workflow.kaleo.runtime.api\n      </td>\n  </tr>\n\n</table>\n\n### Foundation\n\n<style>\ntable, th, td {\n    border: 1px solid black;\n    border-collapse: collapse;\n}\nth, td {\n    padding: 5px;\n    text-align: left;\n}\n</style>\n\n<table style=\"width:100%\">\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/captcha/\" target=\"_blank\">\n      Captcha\n      </a>\n    </th>\n    <td>\n          com.liferay.captcha.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/configuration-admin/\" target=\"_blank\">\n      Configuration Admin\n      </a>\n    </th>\n    <td>\n          com.liferay.configuration.admin.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/contacts/\" target=\"_blank\">\n      Contacts\n      </a>\n    </th>\n    <td>\n          com.liferay.contacts.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/friendly-url/\" target=\"_blank\">\n      Friendly URL\n      </a>\n    </th>\n    <td>\n          com.liferay.friendly.url.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/frontend-editor/\" target=\"_blank\">\n      Frontend Editor\n      </a>\n    </th>\n    <td>\n          com.liferay.frontend.editor.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/frontend-image-editor/\" target=\"_blank\">\n      Frontend Image Editor\n      </a>\n    </th>\n    <td>\n           com.liferay.frontend.image.editor.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/frontend-js/\" target=\"_blank\">\n      Frontend JS\n      </a>\n    </th>\n    <td>\n            com.liferay.frontend.js.loader.modules.extender.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/map/\" target=\"_blank\">\n      Map\n      </a>\n    </th>\n    <td>\n             com.liferay.map.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/mobile-device-rules/\" target=\"_blank\">\n      Mobile Device Rules\n      </a>\n    </th>\n    <td>\n             com.liferay.mobile.device.rules.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"2\">\n      <a href=\"@app-ref@/organizations/\" target=\"_blank\">\n      Organizations\n      </a>\n    </th>\n    <td>\n             com.liferay.organizations.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.organizations.item.selector.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/password-policies-admin/\" target=\"_blank\">\n      Password Policies Admin\n      </a>\n    </th>\n    <td>\n             com.liferay.password.policies.admin.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-background-task/\" target=\"_blank\">\n      Portal Background Task\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.background.task.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-cache/\" target=\"_blank\">\n      Portal Cache\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.cache.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-configuration/\" target=\"_blank\">\n      Portal Configuration\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.configuration.upgrade.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-instances/\" target=\"_blank\">\n      Portal Instances\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.instances.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-lock/\" target=\"_blank\">\n      Portal Lock\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.lock.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-remote/\" target=\"_blank\">\n      Portal Remote\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.remote.soap.extender.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-scripting/\" target=\"_blank\">\n      Portal Scripting\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.scripting.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"3\">\n      <a href=\"@app-ref@/portal-search/\" target=\"_blank\">\n      Portal Search\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.search.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.search.engine.adapter.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.search.web.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"3\">\n      <a href=\"@app-ref@/portal-security-audit/\" target=\"_blank\">\n      Portal Security Audit\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.security.audit.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.security.audit.event.generators.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.security.audit.storage.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"7\">\n      <a href=\"@app-ref@/portal-security-sso/\" target=\"_blank\">\n      Portal Security SSO\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.security.sso.cas.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.security.sso.facebook.connect.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.security.sso.ntlm.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.security.sso.openid.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.security.sso.openid.connect.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.security.sso.opensso.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.security.sso.token.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"5\">\n      <a href=\"@app-ref@/portal-security/\" target=\"_blank\">\n      Portal Security\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.security.exportimport.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.security.ldap.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.security.permission.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.security.service.access.policy.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.security.service.access.quota.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-security-sso-google/\" target=\"_blank\">\n      Portal Security SSO Google\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.security.sso.google.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-settings/\" target=\"_blank\">\n      Portal Settings\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.settings.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-template/\" target=\"_blank\">\n      Portal Template\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.template.soy.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portal-url-builder/\" target=\"_blank\">\n      Portal URL Builder\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.url.builder.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"7\">\n      <a href=\"@app-ref@/portal/\" target=\"_blank\">\n      Portal\n      </a>\n    </th>\n    <td>\n             com.liferay.portal.custom.jsp.bag.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.dao.orm.custom.sql.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.instance.lifecycle.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.jmx.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.output.stream.container.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.spring.extender.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.portal.upgrade.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"2\">\n      <a href=\"@app-ref@/roles/\" target=\"_blank\">\n      Roles\n      </a>\n    </th>\n    <td>\n             com.liferay.roles.admin.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.roles.item.selector.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/text-localizer/\" target=\"_blank\">\n      Text Localizer\n      </a>\n    </th>\n    <td>\n             com.liferay.text.localizer.address.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/user-associated-data/\" target=\"_blank\">\n      User-associated Data\n      </a>\n    </th>\n    <td>\n             com.liferay.user.associated.data.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"2\">\n      <a href=\"@app-ref@/user-groups-admin/\" target=\"_blank\">\n      User Groups Admin\n      </a>\n    </th>\n    <td>\n             com.liferay.user.groups.admin.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.user.groups.admin.item.selector.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"2\">\n      <a href=\"@app-ref@/users-admin/\" target=\"_blank\">\n      Users Admin\n      </a>\n    </th>\n    <td>\n             com.liferay.users.admin.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.users.admin.item.selector.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/xstream/\" target=\"_blank\">\n      XStream\n      </a>\n    </th>\n    <td>\n             com.liferay.xstream.configurator.api\n      </td>\n  </tr>\n\n</table>\n\n### Web Experience\n\n<style>\ntable, th, td {\n    border: 1px solid black;\n    border-collapse: collapse;\n}\nth, td {\n    padding: 5px;\n    text-align: left;\n}\n</style>\n\n<table style=\"width:100%\">\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/application-list/\" target=\"_blank\">\n      Application List\n      </a>\n    </th>\n    <td>\n             com.liferay.application.list.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"11\">\n      <a href=\"@app-ref@/asset/\" target=\"_blank\">\n      Asset\n      </a>\n    </th>\n    <td>\n             com.liferay.asset.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.asset.categories.navigation.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.asset.category.property.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.asset.display.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.asset.display.page.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.asset.display.page.item.selector.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.asset.entry.rel.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.asset.publisher.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.asset.tag.stats.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.asset.tags.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.asset.tags.navigation.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"2\">\n      <a href=\"@app-ref@/export-import/\" target=\"_blank\">\n      Export Import\n      </a>\n    </th>\n    <td>\n             com.liferay.exportimport.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.exportimport.changeset.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"2\">\n      <a href=\"@app-ref@/fragment/\" target=\"_blank\">\n      Fragment\n      </a>\n    </th>\n    <td>\n             com.liferay.fragment.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.fragment.item.selector.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/html-preview/\" target=\"_blank\">\n      HTML Preview\n      </a>\n    </th>\n    <td>\n             com.liferay.html.preview.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"3\">\n      <a href=\"@app-ref@/journal/\" target=\"_blank\">\n      Journal\n      </a>\n    </th>\n    <td>\n             com.liferay.journal.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.journal.content.asset.addon.entry.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.journal.item.selector.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"6\">\n      <a href=\"@app-ref@/layout/\" target=\"_blank\">\n      Layout\n      </a>\n    </th>\n    <td>\n             com.liferay.layout.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.layout.admin.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.layout.item.selector.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.layout.page.template.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.layout.prototype.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.layout.set.prototype.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/portlet-display-template/\" target=\"_blank\">\n      Portlet Display Template\n      </a>\n    </th>\n    <td>\n             com.liferay.portlet.display.template.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"3\">\n      <a href=\"@app-ref@/product-navigation/\" target=\"_blank\">\n      Product Navigation\n      </a>\n    </th>\n    <td>\n             com.liferay.product.navigation.control.menu.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.product.navigation.product.menu.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.product.navigation.simulation.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/rss/\" target=\"_blank\">\n      RSS\n      </a>\n    </th>\n    <td>\n             com.liferay.rss.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"4\">\n      <a href=\"@app-ref@/site-navigation/\" target=\"_blank\">\n      Site Navigation\n      </a>\n    </th>\n    <td>\n             com.liferay.site.navigation.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.site.navigation.admin.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.site.navigation.item.selector.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.site.navigation.language.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"2\">\n      <a href=\"@app-ref@/site/\" target=\"_blank\">\n      Site\n      </a>\n    </th>\n    <td>\n             com.liferay.site.api\n      </td>\n  </tr>\n  <tr>\n    <td>\n       com.liferay.site.item.selector.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/staging/\" target=\"_blank\">\n      Staging\n      </a>\n    </th>\n    <td>\n             com.liferay.staging.api\n      </td>\n  </tr>\n\n  <tr>\n    <th rowspan=\"1\">\n      <a href=\"@app-ref@/trash/\" target=\"_blank\">\n      Trash\n      </a>\n    </th>\n    <td>\n             com.liferay.trash.api\n      </td>\n  </tr>\n\n</table>\n\n## JavaScript and CSS\n\n[**Lexicon**](https://liferay.design/lexicon/): A system for building \napplications in and outside of @product@, designed to be fluid and extensible, \nas well as provide a consistent and documented API. \n\n[**Clay**](https://clayui.com/): The web implementation of Lexicon. \n\n[**Bootstrap**](http://getbootstrap.com/): The base CSS library onto which \nLexicon is built. @product@ uses Bootstrap natively and all of its CSS classes \nand JavaScript features are available within portlets, templates, and themes. \n\n[**AlloyUI**](http://alloyui.com): AlloyUI and all of its JavaScript APIs are \navailable within portlets, templates, and themes. \n\n## Descriptor Definitions\n\n[**DTDs**](@platform-ref@/7.2-latest/definitions/): Describes the XML files used \nin configuring @product@ apps, @product@ plugins, and @product-ver@. \n"
  },
  {
    "path": "en/developer/reference/articles/02-liferay-faces/01-intro.markdown",
    "content": "---\nheader-id: liferay-faces\n---\n\n# Liferay Faces\n\n[TOC levels=1-4]\n\nLiferay Faces is an umbrella project that provides support for the\nJavaServer&#8482; Faces (JSF) standard within @product@. It encompasses the\nfollowing projects:\n\n- [Liferay Faces Bridge](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-bridge)\n  enables you to deploy JSF web apps as portlets without writing\n  portlet-specific Java code. It also contains innovative features that make it\n  possible to leverage the power of JSF 2.x inside a portlet application.\n  Liferay Faces Bridge implements the JSR 329 Portlet Bridge Standard. \n- [Liferay Faces Alloy](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-alloy)\n  enables you to use AlloyUI components in a way that is consistent with JSF\n  development. \n- [Liferay Faces Portal](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-portal)\n  enables you to leverage Liferay-specific utilities and UI components in JSF\n  portlets. \n\nIn this section of reference documentation, you'll learn more about each of\nthese projects. You'll also learn about the Liferay Faces version scheme.\n"
  },
  {
    "path": "en/developer/reference/articles/02-liferay-faces/02-liferay-faces-version-scheme.markdown",
    "content": "---\nheader-id: liferay-faces-version-scheme\n---\n\n# Liferay Faces Version Scheme\n\n[TOC levels=1-4]\n\nIn this article, you'll learn which Liferay Faces artifacts should be used with\nyour portlet and explore the Liferay Faces versioning scheme by discovering what\neach component of a version means. Once you have the versioning scheme mastered,\nyou can view several example configurations.\n\n## Using The Liferay Faces Archetype Portlet\n\nThe [Liferay Faces Archetype portlet](http://liferayfaces.org) can be used to\ndetermine the Liferay Faces artifacts and versions that you must include in\nyour portlet. Select your preferred Liferay Portal version, JSF version,\ncomponent suite (optional), and build tool, and the portlet will provide you\nwith both a command to generate your portlet from a Maven archetype and a list\nof dependencies that can be copied into your build files. In the next section,\nyou'll be provided with compatibility information about each version of the\nLiferay Faces artifacts.\n\n## Liferay Faces Alloy\n\nProvides a suite of JSF components that utilize [AlloyUI](http://alloyui.com/).\n\n|Branch|Example Artifact|AlloyUI|JSF API|Additional Info|\n|------|----------------|-------|-------|---------------|\n|[master \\(4.x\\)](https://github.com/liferay/liferay-faces-alloy/tree/master)|com.liferay.faces.alloy-4.1.0.jar|3.1.x|2.2+|*AlloyUI 3.1.x is the version that comes bundled with Liferay Portal 7.3.*|\n|[3.x](https://github.com/liferay/liferay-faces-alloy/tree/3.x)|com.liferay.faces.alloy-3.1.0.jar|3.0.x|2.2+|*AlloyUI 3.0.x is the version that comes bundled with Liferay Portal 7.0/7.1/7.2.*|\n|[2.x](https://github.com/liferay/liferay-faces-alloy/tree/2.x)|com.liferay.faces.alloy-2.0.1.jar|2.0.x|2.1+|*AlloyUI 2.0.x is the version that comes bundled with Liferay Portal 6.2.*|\n|[1.x](https://github.com/liferay/liferay-faces-alloy/tree/1.x)|com.liferay.faces.alloy-1.0.1.jar|2.0.x|1.2|*AlloyUI 2.0.x is the version that comes bundled with Liferay Portal 6.2.*|\n\n## Liferay Faces Bridge\n\nProvides the ability to deploy JSF web applications as portlets within\n[Apache Pluto](https://portals.apache.org/pluto/), the reference implementation\nfor JSR 286 (Portlet 2.0) and JSR 362 (Portlet 3.0).\n\n|Branch|Example Artifacts|Portlet API|JSF API|JCP Specification|Additional Info|\n|------|-----------------|:-----------:|:-------:|:-----------------:|---------------|\n|API: [5.x](https://github.com/liferay/liferay-faces-bridge-api/tree/5.x)<br/>IMPL: [5.x](https://github.com/liferay/liferay-faces-bridge-impl/tree/5.x)|com.liferay.faces.bridge.api-5.0.0.jar<br/>com.liferay.faces.bridge.impl-5.0.0.jar|3.0|2.2|[JSR 378](https://www.jcp.org/en/jsr/detail?id=378)|*Under \"Final Review\" by the JCP and scheduled for release in 2020.*|\n|API: [4.x](https://github.com/liferay/liferay-faces-bridge-api/tree/4.x)<br/>IMPL: [4.x](https://github.com/liferay/liferay-faces-bridge-impl/tree/4.x)|com.liferay.faces.bridge.api-4.1.0.jar<br/>com.liferay.faces.bridge.impl-4.0.0.jar|2.0|2.2|[JSR 329](https://www.jcp.org/en/jsr/detail?id=329)|*Includes non-standard bridge extensions for JSF 2.2.*|\n|API: [3.x](https://github.com/liferay/liferay-faces-bridge-api/tree/3.x)<br/>IMPL: [3.x](https://github.com/liferay/liferay-faces-bridge-impl/tree/3.x)|com.liferay.faces.bridge.api-3.1.0.jar<br/>com.liferay.faces.bridge.impl-3.0.0.jar|2.0|2.1|[JSR 329](https://www.jcp.org/en/jsr/detail?id=329)|*Includes non-standard bridge extensions for JSF 2.1.*|\n|API: [2.x](https://github.com/liferay/liferay-faces-bridge-api/tree/2.x)<br/>IMPL: [2.x](https://github.com/liferay/liferay-faces-bridge-impl/tree/2.x)|com.liferay.faces.bridge.api-2.1.0.jar<br/>com.liferay.faces.bridge.impl-2.0.0.jar|2.0|1.2|[JSR 329](https://www.jcp.org/en/jsr/detail?id=329) (MR1)|*Includes support for Maintenance Release 1 (MR1).*|\n|1.x|N/A|1.0|1.2|[JSR 301](https://www.jcp.org/en/jsr/detail?id=301)|*N/A (Not Applicable) since Liferay Faces Bridge has never implemented JSR 301.*|\n\n## Liferay Faces Bridge Ext\n\nExtension to Liferay Faces Bridge that provides compatibility with\n[Liferay Portal](https://liferay.dev/-/portal)\nand also takes advantage of Liferay-specific features such as friendly URLs.\n\n|Branch           |Example Artifact                  |&nbsp;&nbsp;Liferay Portal API&nbsp;&nbsp;|&nbsp;&nbsp;Bridge API&nbsp;&nbsp;|&nbsp;&nbsp;Portlet API&nbsp;&nbsp;|JSF API|\n|-----------------|------------------------------------|:--------------:|:----------:|:-----------:|:-------:|\n|[8.x](https://github.com/liferay/liferay-faces-bridge-ext/tree/master)|com.liferay.faces.bridge.ext-8.0.0.jar|7.3.0+|5.x|3.0|2.3|\n|[7.x](https://github.com/liferay/liferay-faces-bridge-ext/tree/7.x)|com.liferay.faces.bridge.ext-7.0.0.jar|7.3.0+|5.x|3.0|2.2|\n|[6.x](https://github.com/liferay/liferay-faces-bridge-ext/tree/6.x)|com.liferay.faces.bridge.ext-6.0.0.jar|7.3.0+|4.x|2.0|2.2|\n|[5.x](https://github.com/liferay/liferay-faces-bridge-ext/tree/5.x)|com.liferay.faces.bridge.ext-5.0.4.jar|7.0.x/7.1.x/7.2.x|4.x|2.0|2.2|\n|[4.x](https://github.com/liferay/liferay-faces-bridge-ext/tree/4.x)|UNUSED|N/A|N/A|N/A|N/A|\n|[3.x](https://github.com/liferay/liferay-faces-bridge-ext/tree/3.x)|com.liferay.faces.bridge.ext-3.0.1.jar|6.2.x|4.x|2.0|2.2|\n|[2.x](https://github.com/liferay/liferay-faces-bridge-ext/tree/2.x)|com.liferay.faces.bridge.ext-2.0.1.jar|6.2.x|3.x|2.0|2.1|\n|[1.x](https://github.com/liferay/liferay-faces-bridge-ext/tree/1.x)|com.liferay.faces.bridge.ext-1.0.1.jar|6.2.x|2.x|2.0|1.2|\n\n## Liferay Faces Portal\n\nProvides a suite of JSF components that are based on the JSP tags provided by\n[Liferay Portal](https://liferay.dev/-/portal).\n\n|Branch|Example Artifact|Liferay Portal API&nbsp;&nbsp;|&nbsp;&nbsp;Portlet API|&nbsp;&nbsp;JSF API|\n|------|----------------|:------------------:|:-------:|:-------:|\n|[6.x](https://github.com/liferay/liferay-faces-portal/tree/master)|com.liferay.faces.portal-6.0.0.jar|7.2+|3.0|2.3|\n|[5.x](https://github.com/liferay/liferay-faces-portal/tree/5.x)|com.liferay.faces.portal-5.0.0.jar|7.2+|3.0|2.2|\n|[4.x](https://github.com/liferay/liferay-faces-portal/tree/4.x)|com.liferay.faces.portal-4.0.0.jar|7.2/7.3|2.0|2.2|\n|[3.x](https://github.com/liferay/liferay-faces-portal/tree/3.x)|com.liferay.faces.portal-3.0.1.jar|7.0/7.1/7.2|2.0|2.2|\n|[2.x](https://github.com/liferay/liferay-faces-portal/tree/2.x)|com.liferay.faces.portal-2.0.1.jar|6.2|2.0|2.1/2.2|\n|[1.x](https://github.com/liferay/liferay-faces-portal/tree/1.x)|com.liferay.faces.portal-1.0.1.jar|6.2|2.0|1.2|\n\n## Liferay Faces Util\n\nLibrary that contains general purpose JSF utilities to support many of the\nsub-projects that comprise Liferay Faces.\n\n|Branch|Example Artifact|&nbsp;&nbsp;JSF API|\n|------|----------------|:-------:|\n|[4.x](https://github.com/liferay/liferay-faces-util/tree/4.x)|com.liferay.faces.util-3.1.0.jar|2.3|\n|[3.x](https://github.com/liferay/liferay-faces-util/tree/3.x)|com.liferay.faces.util-3.1.0.jar|2.2|\n|[2.x](https://github.com/liferay/liferay-faces-util/tree/2.x)|com.liferay.faces.util-2.1.0.jar|2.1|\n|[1.x](https://github.com/liferay/liferay-faces-util/tree/1.x)|com.liferay.faces.util-1.1.0.jar|1.2|\n\nNow that you know all about the Liferay Faces versioning scheme, you may be\ncurious as to how these components interact with each other. Refer to the\nfollowing figure to view the Liferay Faces dependency diagram.\n\n![Figure 1: The Liferay Faces dependency diagram helps visualize how components interact and depend on each other.](../../images/liferay-faces-dependency-diagram.png)\n\n\nNext, you can view some example configurations to see the new versioning scheme\nin action.\n"
  },
  {
    "path": "en/developer/reference/articles/02-liferay-faces/03-understanding-liferay-faces-bridge.markdown",
    "content": "---\nheader-id: understanding-liferay-faces-bridge\n---\n\n# Understanding Liferay Faces Bridge\n\n[TOC levels=1-4]\n\nThe Liferay Faces Bridge enables you to deploy JSF web apps as portlets without\nwriting portlet-specific code. It also contains innovative features that make it\npossible to leverage the power of JSF 2.x inside a portlet application.\n\nLiferay Faces Bridge is distributed in a `.jar` file. You can add Liferay Faces\nBridge as a dependency to your portlet projects, in order to deploy your JSF web\napplications as portlets within JSR 286 (Portlet 2.0) compliant portlet\ncontainers, like Liferay Portal 5.2, 6.0, 6.1, 6.2, and 7.0. \n\nThe Liferay Faces Bridge project home page can be found\n[here](https://community.liferay.com/-/faces). \n\nTo fully understand Liferay Faces Bridge, you must first understand the portlet\nbridge standard. Because the Portlet 1.0 and JSF 1.0 specs were being created at\nessentially the same time, the Expert Group (EG) for the JSF specification\nconstructed the JSF framework to be compliant with portlets. For example, the\n[ExternalContext.getRequest()](https://javaee.github.io/javaee-spec/javadocs/javax/faces/context/ExternalContext.html#getRequest--)\nmethod returns an `Object` instead of an\n[javax.servlet.http.HttpServletRequest](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/http/HttpServletRequest.html).\nWhen this method is used in a portal, the `Object` can be cast to a\n[javax.portlet.PortletRequest](http://portals.apache.org/pluto/portlet-2.0-apidocs/javax/portlet/PortletRequest.html).\nDespite the EG's consciousness of portlet compatibility within the design of\nJSF, the gap between the portlet and JSF lifecycles had to be bridged. \n\nPortlet bridge standards and implementations evolved over time. \n\nStarting in 2004, several different JSF portlet bridge implementations were\ndeveloped in order to provide JSF developers with the ability to deploy their\nJSF web apps as portlets. In 2006, the JCP formed the Portlet Bridge 1.0\n([JSR 301](http://www.jcp.org/en/jsr/detail?id=301)) EG in order to define a\nstandard bridge API, as well as detailed requirements for bridge\nimplementations. JSR 301 was released in 2010, targeting Portlet 1.0 and JSF\n1.2.\n\nWhen the Portlet 2.0 ([JSR 286](http://www.jcp.org/en/jsr/detail?id=286))\nstandard was released in 2008, it became necessary for the JCP to form the\nPortlet Bridge 2.0 ([JSR 329](http://www.jcp.org/en/jsr/detail?id=329)) EG. JSR\n329 was also released in 2010, targeting Portlet 2.0 and JSF 1.2. \n\nAfter the [JSR 314](http://www.jcp.org/en/jsr/detail?id=314) EG released JSF 2.0\nin 2009 and JSF 2.1 in 2010, it became evident that a Portlet Bridge 3.0\nstandard would be beneficial. In 2015 the JCP formed\n[JSR 378](http://www.jcp.org/en/jsr/detail?id=378)) which is defining a\nbridge for Portlet 3.0 and JSF 2.2. There are also variants of *Liferay Faces\nBridge* that support Portlet 2.0 and JSF 1.2/2.1/2.2.\n\nLiferay Faces Bridge is the Reference Implementation (RI) of the Portlet Bridge\nStandard. It also contains innovative features that make it possible to leverage\nthe power of JSF 2.x inside a portlet application. \n\nNow that you're familiar with some of the history of the Portlet Bridge\nstandards, you'll learn about the responsibilities required of the portlet\nbridge. \n\nA JSF portlet bridge aligns the correct phases of the JSF lifecycle with each\nphase of the portlet lifecycle. For instance, if a browser sends an HTTP GET\nrequest to a portal page with a JSF portlet in it, the `RENDER_PHASE` is\nperformed in the portlet's lifecycle. The JSF portlet bridge then initiates the\n`RESTORE_VIEW` and `RENDER_RESPONSE` phases in the JSF lifecycle. Likewise, when\nan HTTP POST is executed on a portlet and the portlet enters the `ACTION_PHASE`,\nthen the full JSF lifecycle is initiated by the bridge. \n\n![Figure 1: The different phases of the JSF Lifecycle are executed depending on which phase of the Portlet lifecycle is being executed.](../../images/lifecycle-bridge.png)\n\n<!-- Neil stated the following about the JSF Lifecycle image above:\n\n\"In the following image, we talk about JSR 286 (Portlet 2.0), but once we're\ndone with Portlet 3.0 in Liferay 7.1 (very soon) and JSR 378 (Portlet 3.0 Bridge\nfor JSF 2.2, not until the end of Q2 2019), we will need to change that to JSR\n362 and also add the HEADER_PHASE.\"\n\nWe'll need to update the image once this is available. -Cody -->\n\nBesides ensuring that the two lifecycles connect correctly, the JSF portlet\nbridge also acts as a mediator between the portal URL generator and JSF\nnavigation rules. JSF portlet bridges ensure that URLs created by the portal\ncomply with JSF navigation rules, so that a JSF portlet is able to switch to\ndifferent views. \n\nThe JSR 329/378 standards defines several configuration options prefixed with\nthe `javax.portlet.faces` namespace. Liferay Faces Bridge defines additional\nimplementation-specific options prefixed with the `com.liferay.faces.bridge`\nnamespace. \n\nLiferay Faces Bridge is an essential part of the JSF development process for\n@product@. Visit the\n[JSF Portlets with Liferay Faces](/docs/7-1/tutorials/-/knowledge_base/t/jsf-portlets-with-liferay-faces)\nsection of tutorials for more information on JSF development for @product@.\n\n## Related Topics\n\n[Understanding Liferay Faces Alloy](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-alloy)\n\n[Understanding Liferay Faces Portal](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-portal)\n\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n"
  },
  {
    "path": "en/developer/reference/articles/02-liferay-faces/04-understanding-liferay-faces-alloy.markdown",
    "content": "---\nheader-id: understanding-liferay-faces-alloy\n---\n\n# Understanding Liferay Faces Alloy\n\n[TOC levels=1-4]\n\nLiferay Faces Alloy is distributed in a `.jar` file. You can add Liferay Faces\nAlloy as a dependency to your portlet projects, to use AlloyUI in a way that is\nconsistent with JSF development. \n\n| **Note:** AlloyUI is deprecated in @product@ 7.2.\n\nDuring the creation of a JSF portlet in Liferay IDE/Developer Studio, you have\nthe option of choosing the portlet's JSF Component Suite. The options include\n*JSF standard*,\n[*ICEfaces*](http://www.icesoft.org/java/projects/ICEfaces/overview.jsf),\n[*PrimeFaces*](http://primefaces.org/),\n[*RichFaces*](http://richfaces.jboss.org/), and *Liferay Faces Alloy*.\n\nIf you selected the Liferay Faces Alloy JSF Component Suite during your\nportlet's setup, the `.jar` file is included in your portlet project. \n\nThe Liferay Faces Alloy project provides a set of UI components that utilize\nAlloyUI. For example, a brief list of some of the supported `aui:` tags are\nlisted below: \n\n- Input: `alloy:inputText`, `alloy:inputDate`, `alloy:inputFile`\n- Panel: `alloy:accordion`, `alloy:column`, `alloy:fieldset`, `alloy:row`\n- Select: `alloy:selectOneMenu`, `alloy:selectOneRadio`, `alloy:selectStarRating`\n\nIf you want to utilize Liferay's AlloyUI technology based on YUI3, you must\ninclude the Liferay Faces Alloy `.jar` file in your JSF portlet project. If you\nselected *Liferay Faces Alloy* during your JSF portlet's setup, you have Liferay\nFaces Alloy preconfigured in your project, so you're automatically able to use\nthe `alloy:` tags. \n\nAs you can see, it's extremely easy to configure your JSF application to use\nLiferay's AlloyUI tags. \n\n## Related Topics\n\n[Developing a JSF Portlet Application](/docs/7-2/appdev/-/knowledge_base/a/developing-a-jsf-portlet-application)\n\n[Understanding Liferay Faces Bridge](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-bridge)\n\n[Understanding Liferay Faces Portal](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-portal)\n"
  },
  {
    "path": "en/developer/reference/articles/02-liferay-faces/05-understanding-liferay-faces-portal.markdown",
    "content": "---\nheader-id: understanding-liferay-faces-portal\n---\n\n# Understanding Liferay Faces Portal\n\n[TOC levels=1-4]\n\n*Liferay Faces Portal* is distributed in a `.jar` file. You can add Liferay\nFaces Portal as a dependency for your portlet projects to use its\nLiferay-specific utilities and UI components. When Liferay Faces Portal is\nincluded in a JSF portlet project, the `com.liferay.faces.portal.[version].jar`\nfile resides in the portlet's library. \n\n![Figure 1: The required `.jar` files are downloaded for your JSF portlet based on the JSF UI Component Suite you configured.](../../images/jsf-jars-package-explorer.png)\n\nSome of the features included in Liferay Faces Portal are: \n\n- Utilities: Provides the `LiferayPortletHelperUtil` which contains a variety\n  Portlet-API and Liferay-specific convenience methods.\n\n- JSF Components: Provides a set of JSF equivalents for popular @product@ JSP\n  tags (not exhaustive):\n    - `liferay-ui:captcha` &rarr; `portal:captcha`\n    - `liferay-ui:input-editor` &rarr; `portal:inputRichText`\n    - `liferay-ui:search` &rarr; `portal:inputSearch`\n    - `liferay-ui:header` &rarr; `portal:header`\n    - `aui:nav` &rarr; `portal:nav`\n    - `aui:nav-item` &rarr; `portal:navItem`\n    - `aui:nav-bar` &rarr; `portal:navBar`\n    - `liferay-security:permissionsURL` &rarr; `portal:permissionsURL`\n    - `liferay-portlet:runtime` &rarr; `portal:runtime`\n\n    For more information, visit\n    [https://liferayfaces.org/web/guest/portal-showcase](https://liferayfaces.org/web/guest/portal-showcase).\n\n- Expression Language: Adds a set of EL keywords such as `liferay` for getting\n  Liferay-specific info, and `i18n` for integration with out-of-the-box Liferay\n  internationalized messages. \n\nGreat! You now have an understanding of what Liferay Faces Portal is, and what\nit accomplishes in your JSF application. \n\n## Related Topics\n\n[Developing a JSF Portlet Application](/docs/7-2/appdev/-/knowledge_base/a/developing-a-jsf-portlet-application)\n\n[Understanding Liferay Faces Bridge](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-bridge)\n\n[Understanding Liferay Faces Alloy](/docs/7-2/reference/-/knowledge_base/r/understanding-liferay-faces-alloy)\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/00-intro.markdown",
    "content": "---\nheader-id: maven-plugins\n---\n\n# Maven Plugins\n\n[TOC levels=1-4]\n\nLiferay provides plugins that you can apply to your Maven project. This\nreference documentation describes\n\n- Configuring the plugin in your `pom.xml` file.\n- The plugin's available goals you can leverage.\n- The plugin's configuration properties.\n\nIf you're looking for additional instructions on using Maven with your modules,\nsee the [Maven](/docs/7-2/reference/-/knowledge_base/r/maven) articles.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/bundle-support-plugin.markdown",
    "content": "---\nheader-id: bundle-support-plugin\n---\n\n# Bundle Support Plugin\n\n[TOC levels=1-4]\n\nThe Bundle Support plugin lets you use\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)\nas a Maven project.\n\n## Usage\n\nTo use the plugin, include it in your project's root `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.bundle.support</artifactId>\n            <version>3.2.5</version>\n            <executions>\n                <execution>\n                    <id>clean</id>\n                    <goals>\n                        <goal>clean</goal>\n                    </goals>\n                    <phase>clean</phase>\n                    <configuration>\n                    </configuration>\n                </execution>\n                <execution>\n                    <id>deploy</id>\n                    <goals>\n                        <goal>deploy</goal>\n                    </goals>\n                    <phase>pre-integration-test</phase>\n                    <configuration>\n                    </configuration>\n                </execution>\n            </executions>\n        </plugin>\n        ...\n    </plugins>\n</build>\n```\n\n## Goals\n\nThe plugin adds five Maven goals to your project:\n\nName | Description\n---- | -----------\n[bundle-support:clean](#clean-goals-available-parameters) |  Deletes a file from the `deploy` directory of a Liferay bundle.\n[bundle-support:create-token](#create-token-goals-available-parameters) | Creates a token used to validate your user credentials when downloading a DXP bundle.\n[bundle-support:deploy](#deploy-goals-available-parameters) | Deploys the Maven project to the specified @product@ bundle.\n[bundle-support:dist](#dist-goals-available-parameters) | Creates a distributable @product@ bundle archive file (e.g., ZIP).\n[bundle-support:init](#init-goals-available-parameters) | Downloads and installs the specified @product@ version.\n\n## clean Goal's Available Parameters\n\nYou can set the following parameters in the `clean` execution's\n`<configuration>` section of the POM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`liferayHome` | `String` | `bundles` |  The directory where your @product@ instance resides. This can be specified from the command line as `-DliferayHome=`.\n`fileName` | `String` | `${project.artifactId}.${project.packaging}` | The name of the file to delete from your bundle.\n\n## create-token Goal's Available Parameters\n\nYou can change the default parameter values of the `create-token` goal by\ncreating an `<execution>` section containing `<configuration>` tags. For\nexample,\n\n```xml\n<execution>\n    <id>create-token</id>\n    <goals>\n        <goal>create-token</goal>\n    </goals>\n    <configuration>\n    </configuration>\n</execution>\n```\n\nYou can set the following parameters in the `create-token` execution's\n`<configuration>` section of the POM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`emailAddress` | `String` | `null` | The email address to use when downloading a DXP bundle. This email address must match the one registered for your DXP subscription.\n`force` | `boolean` | `false` | Whether to override the existing token with a newly generated one.\n`password` | `String` | `null` | The password to use when downloading a DXP bundle. This password must match the one registered for your DXP subscription.\n`passwordFile` | `File` | `null` | The file to hold your password used when downloading a DXP bundle.\n`tokenFile` | `File` | `${user.home}/.liferay/token` | The file to hold the Liferay bundle authentication token.\n`tokenUrl` | `URL` | `https://releases-cdn.liferay.com/portal/7.1.0-b3/liferay-ce-portal-tomcat-7.1-b3-20180611140920623.zip` | The URL pointing to the bundle Zip to download.\n\nAfter executing the `create-token` goal, you're prompted for your email address\nand password, both of which are used to generate your token. It's recommended to\nconfigure your email and password from the command line rather than specifying\nthem in your POM file.\n\n## deploy Goal's Available Parameters\n\nYou can set the following parameters in the `deploy` execution's\n`<configuration>` section of the POM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`liferayHome` | `String` | `bundles` | The directory where your @product@ instance resides. This can be specified from the command line as `-DliferayHome=`.\n`deployFile` | `File` | `${project.build.directory}/${project.build.finalName}.${project.packaging}` | The packaged file (e.g., JAR) to deploy to the Liferay bundle.\n`outputFileName` | `String` | `${project.artifactId}.${project.packaging}` | The name of the output file.\n\n## dist Goal's Available Parameters\n\nYou can change the default parameter values of the `dist` goal by creating an\n`<execution>` section containing `<configuration>` tags. For example,\n\n```xml\n<execution>\n    <id>dist</id>\n    <goals>\n        <goal>dist</goal>\n    </goals>\n    <configuration>\n    </configuration>\n</execution>\n```\n\nYou can set the following parameters in the `dist` execution's `<configuration>`\nsection of the POM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`liferayHome` | `String` | `bundles` | The directory where your @product@ instance resides. This can be specified from the command line as `-DliferayHome=`.\n`archiveFileName` | `String` | `null` | The name for the generated archive file.\n`cacheDir`  | `File` | `${user.home}/.liferay/bundles` | The directory where the downloaded bundle Zip files are stored.\n`configs`  | `String` | `configs` | The directory that contains the configuration files.\n`deployFile` | `File` |` ${project.build.directory}/${project.build.finalName}.${project.packaging}` | The packaged file (e.g., JAR) to deploy to the Liferay bundle.\n`environment`  | `String` | `${liferay.workspace.environment}` | The environment of your Liferay home deployment. (e.g., `common`, `dev`, `local`, `prod`, and `uat`).\n`format`  | `String` | `zip` | The format type to use when packaging the Liferay bundle as an archive.\n`includeFolder` | `boolean` | `true` | Whether to add a parent folder to the archive.\n`outputFileName` | `String` | `${project.artifactId}.${project.packaging}` | The path to the archive file.\n`password` | `String` | `null` | The password if your Liferay bundle's URL requires authentication.\n`stripComponents` | `int` | `1` | The number of directories to strip when expanding your bundle.\n`token` | `boolean` | `false` | Whether to use a token to download a @product@ bundle. This should be set to `true` when downloading a DXP bundle.\n`tokenFile` | `File` | `${user.home}/.liferay/token` | The file to hold the Liferay bundle authentication token.\n`url` | `URL` | `${liferay.workspace.bundle.url}` | The URL of the Liferay bundle to expand.\n`userName` | `String` | `null` | The user name if your Liferay bundle's URL requires authentication.\n\n## init Goal's Available Parameters\n\nYou can change the default parameter values of the `init` goal by creating an\n`<execution>` section containing `<configuration>` tags. For example,\n\n```xml\n<execution>\n    <id>init</id>\n    <goals>\n        <goal>init</goal>\n    </goals>\n    <configuration>\n    </configuration>\n</execution>\n```\n\nYou can set the following parameters in the `init` execution's `<configuration>`\nsection of the POM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`liferayHome` | `String` | `bundles` | The directory where your @product@ instance resides. This can be specified from the command line as `-DliferayHome=`.\n`cacheDir` | `File` | `${user.home}/.liferay/bundles` | The directory where the downloaded bundle Zip files are stored.\n`configs` | `String` | `configs` | The directory that contains the configuration files.\n`environment` | `String` | `${liferay.workspace.environment}` | The environment with the settings appropriate for current development (e.g., `common`, `dev`, `local`, `prod`, and `uat`).\n`password` | `String` | `null` | The password if your Liferay bundle's URL requires authentication.\n`stripComponents` | `int` | `1` | The number of directories to strip when expanding your bundle.\n`token` | `boolean` | `false` | Whether to use a token to download a @product@ bundle. This should be set to `true` when downloading a DXP bundle.\n`tokenFile` | `File` | `${user.home}/.liferay/token` | The file to hold the Liferay bundle authentication token.\n`url` | `URL` | `${liferay.workspace.bundle.url}` | The URL of the Liferay bundle to expand.\n`userName` | `String` | `null` | The user name if your Liferay bundle's URL requires authentication.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/css-builder-plugin.markdown",
    "content": "---\nheader-id: css-builder-plugin\n---\n\n# CSS Builder Plugin\n\n[TOC levels=1-4]\n\nThe CSS Builder plugin lets you compile [Sass](http://sass-lang.com/) files in\nyour project.\n\n## Usage\n\nTo use the plugin, include it in your project's root `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.css.builder</artifactId>\n            <version>3.0.0</version>\n            <executions>\n                <execution>\n                    <id>default-build</id>\n                    <phase>compile</phase>\n                    <goals>\n                        <goal>build</goal>\n                    </goals>\n                </execution>\n            </executions>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n```\n\nYou can view an example POM containing the CSS Builder configuration\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/util/css-builder/samples/pom.xml).\n\n## Goals\n\nThe plugin adds one Maven goal to your project:\n\nName | Description\n---- | -----------\n`css-builder:build` | Compiles the Sass files in the project.\n\n## Available Parameters\n\nYou can set the following parameters in the `<configuration>` section of the\nPOM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`appendCssImportTimestamps` | `boolean` | `true` | Whether to append the current timestamp to the URLs in the `@import` CSS at-rules.\n<a name=\"basedir\"></a>`baseDir` | `File` | `\"src/META-INF/resources\"` | The base directory that contains the SCSS files to compile.\n`dirNames` | `List<String>` | `[\"/\"]` | The name of the directories, relative to [`baseDir`](#basedir), which contain the SCSS files to compile.\n`generateSourceMap` | `boolean` | `false` | Whether to generate [source maps](https://developers.google.com/web/tools/chrome-devtools/debug/readability/source-maps) for easier debugging.\n`importDir` | `File` | `null` | The `META-INF/resources` directory of the [Liferay Frontend Common CSS](https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-css/frontend-css-common) artifact. This is required in order to make [Bourbon](http://bourbon.io) and other CSS libraries available to the compilation.\n`outputDirName` | `String` | `\".sass-cache/\"` | The name of the sub-directories where the SCSS files are compiled to. For each directory that contains SCSS files, a sub-directory with this name is created.\n`precision` | `int` | `9` | The numeric precision of numbers in Sass.\n`rtlExcludedPathRegexps` | `List<String>` |  | The SCSS file patterns to exclude when converting for right-to-left (RTL) support.\n`sassCompilerClassName` | `String` | `\"jni\"` | The type of Sass compiler to use. Supported values are `\"jni\"` and `\"ruby\"`. The Ruby Sass compiler requires `com.liferay.sass.compiler.ruby.jar`, `com.liferay.ruby.gems.jar`, and `jruby-complete.jar` to be added to the classpath.\n \nYou can also manage the `com.liferay.frontend.css.common` default theme\ndependency provided by the CSS Builder in your `pom.xml`. This can be modified\nby adding it as a project dependency:\n\n```xml\n<project>\n    ...\n    <dependencies>\n        <dependency>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.frontend.css.common</artifactId>\n            <version>3.0.1</version>\n            <scope>provided</scope>\n        </dependency>\n        ...\n    </dependencies>\n</project>\n```\n\nThere are additional Liferay theme-related dependencies you can manage this way\nthat are provided by the Theme Builder. See\n[this section](/docs/7-2/reference/-/knowledge_base/r/theme-builder-plugin) for\nmore information.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/db-support-plugin.markdown",
    "content": "---\nheader-id: db-support-plugin\n---\n\n# DB Support Plugin\n\n[TOC levels=1-4]\n\nThe DB Support plugin lets you run the Liferay DB Support tool to execute\ncertain actions on a local @product@ database. The following actions are\navailable:\n\n- Cleans the Liferay database from the Service Builder tables and rows of a\n  module.\n\n## Usage\n\nTo use the plugin, include it in your project's `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.db.support</artifactId>\n            <version>1.0.6</version>\n            <configuration>\n            </configuration>\n            <dependencies>\n                <dependency>\n                    <groupId>org.hsqldb</groupId>\n                    <artifactId>hsqldb</artifactId>\n                    <version>2.4.0</version>\n                </dependency>\n            </dependencies>\n        </plugin>\n    ...\n    </plugins>\n</build>\n```\n\nAlso notice the configured plugin dependency. You must configure the JDBC driver\nused by your @product@ bundle so the DB Support plugin can properly manage your\ndatabase. Replace the HSQLDB driver listed above with your custom database's\nJDBC driver.\n\n## Goals\n\nThe plugin adds one Maven goal to your project:\n\nName | Description\n---- | -----------\n`db-support:clean-service-builder` |  Cleans the @product@ database from the Service Builder tables and rows of a module.\n\n## Available Parameters\n\nYou can set the following parameters in the `<configuration>` section of the\nPOM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`password` | `String` | `jdbc.default.password` | The user password for connecting to the @product@ database.\n`propertiesFile` | `File` | `null` | The `portal-ext.properties` file which contains the JDBC settings for connecting to the @product@ database.\n`serviceXmlFile` | `File` | `null` | The `service.xml` file of the module.\n`servletContextName` | `String` | `null` | The servlet context name (usually the value of the `Bundle-Symbolic-Name` manifest header) of the module.\n`url` | `String` | `jdbc.default.url` | The JDBC URL for connecting to the @product@ database.\n`userName` | `String` | `jdbc.default.username` | The user name for connecting to the @product@ database.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/deployment-helper-plugin.markdown",
    "content": "---\nheader-id: deployment-helper-plugin\n---\n\n# Deployment Helper Plugin\n\n[TOC levels=1-4]\n\nThe Deployment Helper plugin lets you create a cluster deployable WAR from your\nOSGi artifacts.\n\n## Usage\n\nTo use the plugin, include it in your project's root `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.deployment.helper</artifactId>\n            <version>1.0.4</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n```\n\nYou can view an example POM containing the Deployment Helper configuration\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/util/deployment-helper/samples/pom.xml).\n\n## Goals\n\nThe plugin adds one Maven goal to your project:\n\nName | Description\n---- | -----------\n`deployment-helper:build` | Builds a WAR which contains one or more files that are copied once the WAR is deployed.\n\n## Available Parameters\n\nYou can set the following parameters in the `<configuration>` section of the\nPOM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`deploymentFileNames` | `String` | `null` | The files or directories to include in the WAR and copy once the WAR is deployed. If a directory is added to this collection, all the JAR files contained in the directory are included in the WAR.\n`deploymentPath` | `String` | `null` | The directory to which the included files are copied.\n`outputFileName` | `String` | `null` | The WAR file to build.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/javadoc-formatter-plugin.markdown",
    "content": "---\nheader-id: javadoc-formatter-plugin\n---\n\n# Javadoc Formatter Plugin\n\n[TOC levels=1-4]\n\nThe Javadoc Formatter plugin lets you format project Javadoc comments. The tool\nlets you generate:\n\n- Default [`@author`](http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html#@author)\n  tags to all classes.\n- Comment stubs to classes, fields, and methods.\n- Missing [`@Override`](https://docs.oracle.com/javase/8/docs/api/java/lang/Override.html)\n  annotations.\n- An XML representation of the Javadoc comments, which can be used by tools in\n  order to index the Javadocs of the project.\n\n## Usage\n\nTo use the plugin, include it in your project's root `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.javadoc.formatter</artifactId>\n            <version>1.0.32</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n```\n\nYou can view an example POM containing the Javadoc Formatter configuration\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/util/javadoc-formatter/samples/pom.xml).\n\n## Goals\n\nThe plugin adds one Maven goal to your project:\n\nName | Description\n---- | -----------\n`javadoc-formatter:format` | Runs the Liferay Javadoc Formatter to format files.\n\n## Available Parameters\n\nYou can set the following parameters in the `<configuration>` section of the\nPOM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`author` | `String` | `\"Brian Wing Shun Chan\"` | The value of the `@author` tag to add at class level if missing.\n`generateXml` | `boolean` | `false` | Whether to generate a XML representation of the Javadoc comments. The XML files are generated in the `src/main/resources` directory only if the Java files are contained in `src/main/java`.\n`initializeMissingJavadocs` | `boolean` | `false` | Whether to add comment stubs at the class, field, and method levels. If `false`, only the class-level `@author` is added.\n`inputDirName` | `String` | `\"./\"` | The root directory to begin searching for Java files to format.\n`limits` | `String[]` | `[]` | The Java file name patterns, relative to the working directory, to include when formatting Javadoc comments. The patterns must be specified without the `.java` file type suffix. If empty, all Java files are formatted.\n`outputFilePrefix` | `String` | `\"javadocs\"` | The file name prefix of the XML representation of the Javadoc comments. If `generateXML` is `false`, this property is not used.\n`updateJavadocs` | `boolean` | `false` | Whether to fix existing comment blocks by adding missing tags.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/lang-builder-plugin.markdown",
    "content": "---\nheader-id: lang-builder-plugin\n---\n\n# Lang Builder Plugin\n\n[TOC levels=1-4]\n\nThe Lang Builder plugin lets you sort and translate the language keys in your\nproject.\n\n## Usage\n\nTo use the plugin, include it in your project's root `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.lang.builder</artifactId>\n            <version>1.0.31</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n```\n\nYou can view an example POM containing the Lang Builder configuration\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/util/lang-builder/samples/pom.xml).\n\n## Goals\n\nThe plugin adds one Maven goal to your project:\n\nName | Description\n---- | -----------\n`lang-builder:build` | Runs Liferay Lang Builder to translate language property files.\n\n## Available Parameters\n\nYou can set the following parameters in the `<configuration>` section of the\nPOM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`excludedLanguageIds` | `String[]` | `{\"da\", \"de\", \"fi\", \"ja\", \"nl\", \"pt_PT\", \"sv\"}` | The language IDs to exclude in the automatic translation.\n`langDirName` | `String` | `\"src/content\"` | The directory where the language properties files are saved.\n`langFileName` | `String` | `\"Language\"` | The file name prefix of the language properties files (e.g., `Language_it.properties`).\n`plugin` | `boolean` | `true` | Whether to check for duplicate language keys between the project and the portal.\n`portalLanguagePropertiesFileName` | `String` | `null` | The `Language.properties` file of the portal.\n`translate` | `boolean` | `true` | Whether to translate the language keys and generate a language properties file for each locale that's supported by @product@.\n`translateSubscriptionKey` | `String` | `null` | The subscription key for Microsoft Translation integration. Subscription to the Translator Text Translation API on Microsoft Cognitive Services is required. Basic subscriptions, up to 2 million characters a month, are free.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/rest-builder-plugin.markdown",
    "content": "---\nheader-id: rest-builder-plugin\n---\n\n# REST Builder Plugin\n\n[TOC levels=1-4]\n\nThe REST Builder plugin lets you generate a REST layer defined in the REST\nBuilder `rest-config.yaml` and `rest-openapi.yaml` files.\n\n## Usage\n\nTo use the plugin, include it in your project's root `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.rest.builder</artifactId>\n            <version>1.0.22</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n```\n\nYou can view an example POM containing the REST Builder configuration\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/util/portal-tools-rest-builder/samples/pom.xml).\n\n## Goals\n\nThe plugin adds one Maven goal to your project:\n\nName | Description\n---- | -----------\n`rest-builder:build` | Runs the Liferay REST Builder.\n\n## Available Parameters\n\nYou can set the following parameters in the `<configuration>` section of the\nPOM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`copyrightFile` | `File` | `null` | The file that contains the copyright header.\n`restConfigDir` | `File` | `${project.projectDir}` | The directory that contains the `rest-config.yaml` and `rest-openapi.yaml` files.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/service-builder-plugin.markdown",
    "content": "---\nheader-id: service-builder-plugin\n---\n\n# Service Builder Plugin\n\n[TOC levels=1-4]\n\nThe Service Builder plugin lets you generate a service layer defined in a\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n`service.xml` file. Visit the\n[Using Service Builder in a Maven Project](/docs/7-2/reference/-/knowledge_base/r/using-service-builder-in-a-maven-project)\ntutorial to learn more about applying Service Builder to your Maven project.\n\n## Usage\n\nTo use the plugin, include it in your project's root `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.service.builder</artifactId>\n            <version>1.0.292</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n```\n\nYou can view an example POM containing the Service Builder configuration\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/util/portal-tools-service-builder/samples/pom.xml).\n\n## Goals\n\nThe plugin adds one Maven goal to your project:\n\nName | Description\n---- | -----------\n`service-builder:build` |  Runs the Liferay Service Builder.\n\n## Available Parameters\n\nYou can set the following parameters in the `<configuration>` section of the\nPOM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`apiDirName` | `String` | `\"../portal-kernel/src\"` | A directory where the service API Java source files are generated.\n`autoImportDefaultReferences` | `boolean` | `true` | Whether to automatically add default references, like `com.liferay.portal.ClassName`, `com.liferay.portal.Resource` and `com.liferay.portal.User`, to the services. \n`autoNamespaceTables` | `boolean` | `null` | Whether to prefix table names by the namespace specified in the `service.xml` file.\n`beanLocatorUtil` | `String` | `\"com.liferay.portal.kernel.bean.PortalBeanLocatorUtil\"` | The fully qualified class name of a bean locator class to use in the generated service classes.\n`buildNumber` | `long` | `1` | A specific value to assign the `build.number` property in the `service.properties` file.\n`buildNumberIncrement` | `boolean` | `true` | Whether to automatically increment the `build.number` property in the `service.properties` file by one at every service generation.\n`databaseNameMaxLength` | `int` | `30` | The upper bound for database table and column name lengths to ensure it works on all databases.\n`hbmFileName` | `String` | `\"src/META-INF/portal-hbm.xml\"` | A Hibernate Mapping file to generate.\n`implDirName` | `String` | `\"src\"` | A directory where the service Java source files are generated.\n`inputFileName` | `String` | `\"service.xml\"` | The project's `service.xml` file.\n`modelHintsConfigs` | `String` | `\"classpath*:META-INF/portal-model-hints.xml, META-INF/portal-model-hints.xml, classpath*:META-INF/ext-model-hints.xml, classpath*:META-INF/portlet-model-hints.xml\"` | Paths to the model hints files for Liferay Service Builder to use in generating the service layer. <!-- Add back link for 'model hints' once article is available -->\n`modelHintsFileName` | `String` | `\"src/META-INF/portal-model-hints.xml\"` | A model hints file for the project.\n`osgiModule` | `boolean` | `null` | Whether to generate the service layer for OSGi modules.\n`pluginName` | `String` | `null` | If specified, a plugin can enable additional generation features, such as `Clp` class generation, for non-OSGi modules.\n`propsUtil` | `String` | `\"com.liferay.portal.util.PropsUtil\"` | The fully qualified class name of the service properties util class to generate.\n`readOnlyPrefixes` | `String` | `\"fetch, get, has, is, load, reindex, search\"` | Prefixes of methods to consider read-only.\n`resourceActionsConfigs` | `String` | `\"META-INF/resource-actions/default.xml, resource-actions/default.xml\"` | Paths to the [resource actions](/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions) files for Liferay Service Builder to use in generating the service layer.\n`resourcesDirName` | `String` | `\"src\"` | A directory where the service non-Java files are generated.\n`springFileName` | `String` | `\"src/META-INF/portal-spring.xml\"` | A service Spring file to generate.\n`springNamespaces` | `String` | `\"beans\"` | Namespaces of Spring XML Schemas to add to the service Spring file.\n`sqlDirName` | `String` | `\"../sql\"` | A directory where the SQL files are generated.\n`sqlFileName` | `String` | `\"portal-tables.sql\"` | A name (relative to `sqlDir`) for the file in which the SQL table creation instructions are generated.\n`sqlIndexesFileName` | `String` | `\"indexes.sql\"` | A name (relative to `sqlDir`) for the file in which the SQL index creation instructions are generated.\n`sqlSequencesFileName` | `String` | `\"sequences.sql\"` | A name (relative to `sqlDir`) for the file in which the SQL sequence creation instructions are generated.\n`targetEntityName` | `String` | `null` | If specified, it's the name of the entity for which Liferay Service Builder should generate the service.\n`testDirName` | `String` | `\"test/integration\"` | If specified, it's a directory where integration test Java source files are generated.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/source-formatter-plugin.markdown",
    "content": "---\nheader-id: source-formatter-plugin\n---\n\n# Source Formatter Plugin\n\n[TOC levels=1-4]\n\nThe Source Formatter plugin formats project files according to Liferay's source\nformatting standards. For more documentation on Source Formatter specific\nfunctionality, visit the tool's\n[documentation](https://github.com/liferay/liferay-portal/tree/master/modules/util/source-formatter/documentation)\nfolder.\n\n## Usage\n\nTo use the plugin, include it in your project's root `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.source.formatter</artifactId>\n            <version>1.0.885</version>\n            <executions>\n                <execution>\n                    <phase>process-sources</phase>\n                    <goals>\n                        <goal>format</goal>\n                    </goals>\n                </execution>\n            </executions>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n```\n\nYou can view an example POM containing the Source Formatter configuration\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/util/source-formatter/samples/pom.xml).\n\n## Goals\n\nThe plugin adds one Maven goal to your project:\n\nName | Description\n---- | -----------\n`source-formatter:format` |  Runs the Liferay Source Formatter to format source formatting errors.\n\n## Available Parameters\n\nYou can set the following parameters in the `<configuration>` section of the\nPOM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`autoFix` | `boolean` | `true` | Whether to automatically fix source formatting errors.\n`baseDir` | `String` | `\"./\"` | The Source Formatter base directory. *(Read-only)*\n`fileNames` | `String[]` | `null` | The file names to format, relative to the project directory. If `null`, all files contained in `baseDir` will be formatted.\n`formatCurrentBranch` | `boolean` | `false` | Whether to format only the files contained in `baseDir` that are added or modified in the current Git branch.\n`formatLatestAuthor` | `boolean` | `false` | Whether to format only the files contained in `baseDir` that are added or modified in the latest Git commits of the same author.\n`formatLocalChanges` | `boolean` | `false` | Whether to format only the unstaged files contained in `baseDir`.\n`gitWorkingBranchName` | `String` | `\"master\"` | The Git working branch name.\n`includeSubrepositories` | `boolean` | `false` | Whether to format files that are in read-only subrepositories.\n`maxLineLength` | `int` | `80` | The maximum number of characters allowed in Java files.\n`printErrors` | `boolean` | `true` | Whether to print formatting errors on the Standard Output stream.\n`processorThreadCount` | `int` | `5` | The number of threads used by Source Formatter.\n`showDocumentation` | `boolean` | `false` | Whether to show the documentation for the source formatting issues, if present.\n`throwException` | `boolean` | `false` | Whether to fail the build if formatting errors are found.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/theme-builder-plugin.markdown",
    "content": "---\nheader-id: theme-builder-plugin\n---\n\n# Theme Builder Plugin\n\n[TOC levels=1-4]\n\nThe Theme Builder plugin lets you build Liferay theme files in your project.\nVisit the\n[Building a Theme with Maven](/docs/7-2/reference/-/knowledge_base/r/building-a-theme-with-maven)\ntutorial to learn more about applying Theme Builder to your Maven project.\n\n## Usage\n\nTo use the plugin, include it in your project's root `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.theme.builder</artifactId>\n            <version>1.1.7</version>\n            <executions>\n                <execution>\n                    <phase>generate-resources</phase>\n                    <goals>\n                        <goal>build</goal>\n                    </goals>\n                    <configuration>\n                    </configuration>\n                </execution>\n            </executions>\n        </plugin>\n        ...\n    </plugins>\n</build>\n```\n\nYou can view an example POM containing the Theme Builder configuration\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/util/portal-tools-theme-builder/samples/pom.xml).\n\n## Goals\n\nThe plugin adds one Maven goal to your project:\n\nName | Description\n---- | -----------\n`theme-builder:build` |  Builds the theme files.\n\n## Available Parameters\n\nYou can set the following parameters in the `<configuration>` section of the\nPOM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`diffsDir` | `File` | `${maven.war.src}` | The directory that contains the files to copy over the parent theme.\n`name` | `String` | `${project.artifactId}` | The name of the new theme.\n`outputDir` | `File` | `${project.build.directory}/${project.build.finalName}` | The directory where to build the theme.\n`parentDir` | `File` | `null` | The directory of the parent theme.\n`parentName` | `String` | `null` | The name of the parent theme.\n`templateExtension` | `String` | `\"ftl\"` |  The extension of the template files, usually `\"ftl\"` or `\"vm\"`.\n`unstyledDir` | `File` | `null` | The directory of [Liferay Frontend Theme Unstyled](https://github.com/liferay/liferay-portal/tree/master/modules/apps/frontend-theme/frontend-theme-unstyled).\n\nYou can also manage the `com.liferay.frontend.theme.styled` and\n`com.liferay.frontend.theme.unstyled` default theme dependencies provided by the\nTheme Builder in your `pom.xml`. They can be modified by adding them as project\ndependencies:\n\n```xml\n<project>\n    ...\n    <dependencies>\n        ...\n        <dependency>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.frontend.theme.styled</artifactId>\n            <version>3.0.4</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.frontend.theme.unstyled</artifactId>\n            <version>3.0.4</version>\n            <scope>provided</scope>\n        </dependency>\n    </dependencies>\n</project>\n```\n\nThere is an additional Liferay theme-related dependency you can manage this\nway that's provided by the CSS Builder. See\n[this section](/docs/7-2/reference/-/knowledge_base/r/css-builder-plugin) for\nmore information.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/tld-formatter-plugin.markdown",
    "content": "---\nheader-id: tld-formatter-plugin\n---\n\n# TLD Formatter Plugin\n\n[TOC levels=1-4]\n\nThe TLD Formatter plugin lets you format a project's TLD files.\n\n## Usage\n\nTo use the plugin, include it in your project's root `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.tld.formatter</artifactId>\n            <version>1.0.5</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n```\n\nYou can view an example POM containing the TLD Formatter configuration\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/util/tld-formatter/samples/pom.xml).\n\n## Goals\n\nThe plugin adds one Maven goal to your project:\n\nName | Description\n---- | -----------\n`tld-formatter:format` | Runs the Liferay TLD Formatter to format files.\n\n## Available Parameters\n\nYou can set the following parameters in the `<configuration>` section of the\nPOM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`baseDirName` | `String` | `\"./\"` | The base directory to begin searching for TLD files to format.\n`plugin` | `boolean` | `true` | Whether to format all the TLD files contained in the working directory. If `false`, all `liferay-portlet-ext.tld` files are ignored.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/wsdd-builder-plugin.markdown",
    "content": "---\nheader-id: wsdd-builder-plugin\n---\n\n# WSDD Builder Plugin\n\n[TOC levels=1-4]\n\nThe WSDD Builder plugin lets you generate the\n[Apache Axis](http://axis.apache.org/axis/) Web Service Deployment Descriptor\n(WSDD) files from a\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\n`service.xml` file.\n\n## Usage\n\nTo use the plugin, include it in your project's root `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.portal.tools.wsdd.builder</artifactId>\n            <version>1.0.10</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n```\n\nYou can view an example POM containing the WSDD Builder configuration\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/util/portal-tools-wsdd-builder/samples/pom.xml).\n\n## Goals\n\nThe plugin adds one Maven goal to your project:\n\nName | Description\n---- | -----------\n`wsdd-builder:build` | Runs the Liferay WSDD Builder to generate the WSDD files.\n\n## Available Parameters\n\nYou can set the following parameters in the `<configuration>` section of the\nPOM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`classPath` | `String` | `null` | The classpath that the Liferay WSDD Builder uses to generate WSDD files.\n`inputFileName` | `String` | `\"service.xml\"` | The file from which to generate the WSDD files.\n`outputDirName` | `String` | `\"src\"` | The directory where the `*_deploy.wsdd` and `*_undeploy.wsdd` files are generated.\n`serverConfigFileName` | `String` | `\"server-config.wsdd\"` | The file to generate.\n`serviceNamespace` | `String` | `\"Plugin\"` | The namespace for the WSDD Service.\n"
  },
  {
    "path": "en/developer/reference/articles/02-maven-plugins/xml-formatter-plugin.markdown",
    "content": "---\nheader-id: xml-formatter-plugin\n---\n\n# XML Formatter Plugin\n\n[TOC levels=1-4]\n\nThe XML Formatter plugin lets you format a project's XML files.\n\n## Usage\n\nTo use the plugin, include it in your project's root `pom.xml` file:\n\n```xml\n<build>\n    <plugins>\n    ...\n        <plugin>\n            <groupId>com.liferay</groupId>\n            <artifactId>com.liferay.xml.formatter</artifactId>\n            <version>1.0.5</version>\n            <configuration>\n            </configuration>\n        </plugin>\n    ...\n    </plugins>\n</build>\n```\n\nYou can view an example POM containing the XML Formatter configuration\n[here](https://github.com/liferay/liferay-portal/blob/master/modules/util/xml-formatter/samples/pom.xml).\n\n## Goals\n\nThe plugin adds one Maven goal to your project:\n\nName | Description\n---- | -----------\n`xml-formatter:format` | Runs the Liferay XML Formatter to format the project files.\n\n## Available Parameters\n\nYou can set the following parameters in the `<configuration>` section of the\nPOM:\n\nParameter Name | Type | Default Value | Description\n------------- | ---- | ------------- | -----------\n`fileName` | `String` | `null` | The XML file to format. This plugin only lets you format one XML file at a time.\n`stripComments` | `boolean` | `false` | Whether to remove all the comments from the XML file.\n"
  },
  {
    "path": "en/developer/reference/articles/02-meaningful-schema-versioning.markdown",
    "content": "---\nheader-id: meaningful-schema-versioning\n---\n\n# Meaningful Schema Versioning\n\n[TOC levels=1-4]\n\nLiferay's data schema version convention communicates a schema's compatibility\nwith older versions of the software. It tells you whether a schema's changes\nmaintain or break compatibility with existing software. For example, if a new\ndata schema removes a field your software expects, the schema breaks\ncompatibility. But if a new schema's changes are non-breaking (e.g., adds a new\nfield), the schema is compatible and can be used with existing software.\n\nSince @product@ 7.1, Liferay uses a meaningful schema version convention\n(similar to\n[Semantic Versioning](http://semver.org))\nto define new\n[upgrade steps](/docs/7-2/frameworks/-/knowledge_base/f/creating-an-upgrade-process-for-your-app) \nand support rollback of schema micro versions. The schema version defines the\nstatus of the database schema and its data belonging to that module or Core in a\ncertain moment. The concept of schema versioning is different from bundle\nversioning. The biggest concern in versioning a data schema is\n**backward-compatibility** between the new schema and the code that operates on\nthe data. \n\nHere's Liferay's schema version convention:\n\n**MAJOR.MINOR.MICRO**\n\nEach part means something: \n\n**MAJOR:** Contains breaking schema/data changes that are incompatible with the \nprevious version of the code. \n\n**MINOR:** Contains schema/data changes compatible with the previous version of \nthe code. The changes are required for the new version of the code to work (the\napplication will fail without applying the schema/data changes)\n\n**MICRO:** Contains schema/data changes that are compatible with the previous \nversion of the code. The changes are optional. \n\nIf you're not sure what kind of schema version change represents your upgrade\nstep, ask yourself these questions:\n\n1.  Will the previous code version (previous FP, SP, or GA) work with these \n    schema/data changes? \n\n    -   If not, it is a major change. \n    -   If yes, continue. \n\n2.  Are the schema/data changes required for the application to work? \n    (Obviously, all changes are intended to improve the application but in some\n    cases the application is fully functional without them)\n\n    -   If yes, it is a minor change.\n    -   If not, it is a micro change. \n\nNext are some concrete examples of micro, minor, and major changes. \n\n## Micro change examples\n\nHere are common micro changes:\n\n-   Increasing `VARCHAR` field sizes. \n-   Modifying DB indexes. \n-   Modifying data values to adapt to current logic. These include backwards \n    compatible data changes only. These changes commonly occur when data updates\n    are missed for new functionalities. \n-   Converting a field from a String to a CLOB, as long as the field has few \n    records and isn't used in `DISTINCT` or `GROUP BY` SQL clauses. \n\n## Minor change examples\n\nHere are common minor changes:\n\n-   Adding a new DB field.\n-   Adding a new DB table. \n\n**Important:** The changes above are major if they require modifying current \nexisting data or extract information to populate the new field or table. In such\ncases, the data can become incorrect if you rolled back to the previous code\nversion and then, after some time, installed the new code again. \n\n## Major change examples\n\nHere are common major changes:\n\n-   Making data modifications that are not backward compatible. \n-   Removing a DB field\n-   Removing a DB table. \n-   Altering a column name. \n-   Decreasing the size of a `VARCHAR` field. \n-   Converting a field from a String to a CLOB, where the field is has many \n    records or is used in `DISTINCT` or `GROUP BY` SQL clauses. \n-   Adding a new DB field or table that requires modifying current existing data\n    or extracts information to populate the new field or table. \n\nNow you can ascribe meaningful versions to your module's data schemas. \n\n\n"
  },
  {
    "path": "en/developer/reference/articles/02-portlet-3-opt-in.markdown",
    "content": "---\nheader-id: portlet-3-0-api-opt-in\n---\n\n# Portlet 3.0 API Opt In\n\n[TOC levels=1-4]\n\nA portlet must specify version 3.0 to \"opt in\" to the Portlet 3.0 API. The 3.0 Portlet API version can be specified in the following ways.\n\n## Standard Portlet `@PortletApplication` Annotation\n\nStandard portlets need only specify the\n[`@PortletApplication`](https://docs.liferay.com/portlet-api/3.0/javadocs/javax/portlet/annotations/PortletApplication.html)\nannotation.\n\n```java\n@PortletApplication(version=\"3.0\") // 3.0 is the default for this annotation attribute\n@PortletConfiguration(portletName=\"myPortlet\")\npublic class MyPortlet {\n    ...\n}\n```\n\n## Liferay MVC Portlet `@Component` Annotation\n\nDeclarative Services portlets, such as `MVCPortlet`, can specify version 3.0 in\ntheir `@Component` annotation.\n\n```java\n@Component(properties=\"javax.portlet.version=3.0\", service=javax.portlet.Portlet.class)\npublic class MyDeclarativeServicesPortlet {\n    ...\n}\n```\n\n### `portlet.xml` Descriptor\n\nAll portlets can specify version 3.0 in their `portlet.xml` descriptor.\n\n```xml\n<?xml version=\"1.0\"?>\n\n<portlet-app xmlns=\"http://xmlns.jcp.org/xml/ns/portlet\"\n\t\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t\t xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/portlet http://xmlns.jcp.org/xml/ns/portlet/portlet-app_3_0.xsd\"\n\t\t\t version=\"3.0\">\n\t...\n</portlet-app>\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-portlet-descriptor-to-osgi-service-property-map.markdown",
    "content": "---\nheader-id: portlet-descriptor-to-osgi-service-property-map\n---\n\n# Portlet Descriptor to OSGi Service Property Map\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/developing-applications/developing-a-java-web-application/reference/portlet-descriptor-to-osgi-service-property-map.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThis article maps portlet XML descriptor values to OSGi service properties for\npublishing OSGi Portlets. \n\nOSGi service definitions can use properties. OSGi service properties centralize\nand simplify portlet configuration. They are typically represented as key-value\npairs or, more generally, as a Map-like object.\n\nPortlet spec property keys are prefixed by\n\n```java\njavax.portlet.\n```\n\nLiferay property keys are prefixed by\n\n```java\ncom.liferay.portlet.\n```\n\nThe mappings essentially flatten what is found in the XML descriptor. The\nproperty names resemble the original descriptor names. \n\nThis article covers these descriptor mappings:\n\n-   [Portlet descriptor mappings](#portlet-descriptor-mappings)\n\n-   [Liferay descriptor mappings](#liferay-descriptor-mappings)\n\n    -   [From `liferay-display.xml`](#liferay-display)\n\n    -   [From `liferay-portlet.xml`](#liferay-portlet)\n\nThe standard portlet descriptor mappings are first. \n\n## Portlet Descriptor Mappings\n\n**Note:** XPath notation derived from the **Portlet XSD** [4](#four) is used in\nthis document for simplicity.\n\n| portlet.xml XPath | OSGi Portlet Service Property|\n|-------------------|------------------------------|\n|`/portlet-app/container-runtime-option`|not supported|\n|`/portlet-app/custom-portlet-mode`|not supported|\n|`/portlet-app/custom-window-state`|not supported|\n|`/portlet-app/default-namespace`|`javax.portlet.default-namespace=<String>`|\n|`/portlet-app/event-definition`|`javax.portlet.event-definition=<QNameLocalPart>;<QNameURI>[;<PayloadType>][,<AliasQNameLocalPart>;<AliasQNameURI>]` [2](#two)|\n|`/portlet-app/filter`<br/>`/portlet-app/filter/init-param/name`<br/>`/portlet-app/filter-mapping`|[3](#three)<br/>`javax.portlet.init-param.<name>=<value>` [3](#three), [9](#nine)<br/>[3](#three)|\n|`/portlet-app/public-render-parameter`|not supported|\n|`/portlet-app/resource-bundle`|not supported|\n|`/portlet-app/security-constraint`|not supported|\n|`/portlet-app/user-attribute`|not supported|\n|`/portlet-app/version`|`javax.portlet.version=<value>`|\n|`/portlet-app/portlet/async-supported`|`javax.portlet.async-supported=<boolean>`|\n|`/portlet-app/portlet/cache-scope`|not supported|\n|`/portlet-app/portlet/container-runtime-option`|`javax.portlet.container-runtime-option.<name>=<value>` [2](#two)|\n|`/portlet-app/portlet/dependency`|`javax.portlet.dependency=<name>;<scope>;<version>` [2](#two), [6](#six)|\n|`/portlet-app/portlet/description`|`javax.portlet.description=<String>`|\n|`/portlet-app/portlet/display-name`|`javax.portlet.display-name=<String>`|\n|`/portlet-app/portlet/expiration-cache`|`javax.portlet.expiration-cache=<int>`|\n|`/portlet-app/portlet/init-param/name`|`javax.portlet.init-param.<name>=<value>`|\n|`/portlet-app/portlet/listener`|`javax.portlet.listener=<listener-class>;<ordinal>` [2](#two),[8](#eight)|\n|`/portlet-app/portlet/multipart-config/file-size-threshold`|`javax.portlet.multipart.file-size-threshold=<Integer>`|\n|`/portlet-app/portlet/multipart-config/location`|`javax.portlet.multipart.location=<String>`|\n|`/portlet-app/portlet/multipart-config/max-file-size`|`javax.portlet.multipart.max-file-size=<Long>`|\n|`/portlet-app/portlet/multipart-config/max-request-size`|`javax.portlet.multipart.max-request-size=<Long>`|\n|`/portlet-app/portlet/portlet-class`|[1](#one)|\n|`/portlet-app/portlet/portlet-info/keywords`|`javax.portlet.info.keywords=<String>`|\n|`/portlet-app/portlet/portlet-info/short-title`|`javax.portlet.info.short-title=<String>`|\n|`/portlet-app/portlet/portlet-info/title`|`javax.portlet.info.title=<String>`|\n|`/portlet-app/portlet/portlet-name` [10](#ten)|`javax.portlet.name=<String>` [10](#ten)|\n|`/portlet-app/portlet/portlet-preferences`|`javax.portlet.preferences=<String>`<br/>OR<br/>`javax.portlet.preferences=classpath:<path_to_file_in_jar>`|\n|`/portlet-app/portlet/portlet-preferences/preferences-validator`|`javax.portlet.preferences-validator=<String>` [1](#one)|\n|`/portlet-app/portlet/resource-bundle`|`javax.portlet.resource-bundle=<String>`|\n|`/portlet-app/portlet/security-role-ref`|`javax.portlet.security-role-ref=<String>[,<String>]`[2](#two)|\n|`/portlet-app/portlet/supported-locale`|`javax.portlet.supported-locale=<String>` [2](#two)|\n|`/portlet-app/portlet/supported-processing-event`|`javax.portlet.supported-processing-event=<QNameLocalPart>` OR `javax.portlet.supported-processing-event=<QNameLocalPart>;<QNameURI>`  [2](#two)|\n|`/portlet-app/portlet/supported-public-render-parameter`|`javax.portlet.supported-public-render-parameter=<String>`[2](#two)|\n|`/portlet-app/portlet/supported-publishing-event`|`javax.portlet.supported-publishing-event=<QNameLocalPart>` OR `javax.portlet.supported-publishing-event=<QNameLocalPart>;<QNameURI>` [2](#two)|\n|`/portlet-app/portlet/supports/mime-type`|`javax.portlet.mime-type=<mime-type>`|\n|`/portlet-app/portlet/supports/portlet-mode`|`javax.portlet.portlet-mode=<mime-type>;<portlet-mode>[,<portlet-mode>]*`|\n|`/portlet-app/portlet/supports/window-state`|`javax.portlet.window-state=<mime-type>;<window-state>[,<window-state>]*`|\n\n## Liferay Descriptor Mappings\n\n### Liferay Display\n\n|liferay-display.xml XPath | OSGi Portlet Service Property|\n|--------------------------|------------------------------|\n|`/display/category[@name]`|`com.liferay.portlet.display-category=<value>`|\n\n### Liferay Portlet\n\n**Note:** XPath notation derived from **Liferay Portlet** [5](#five) is used in\nthis document for simplicity.\n\n|liferay-portlet.xml XPath | OSGi Liferay Portlet Service Property|\n|--------------------------|--------------------------------------|\n|`/liferay-portlet-app/portlet/action-timeout`|`com.liferay.portlet.action-timeout=<int>`|\n|`/liferay-portlet-app/portlet/action-url-redirect`|`com.liferay.portlet.action-url-redirect=<boolean>`|\n|`/liferay-portlet-app/portlet/active`|`com.liferay.portlet.active=<boolean>`|\n|`/liferay-portlet-app/portlet/add-default-resource`|`com.liferay.portlet.add-default-resource=<boolean>`|\n|`/liferay-portlet-app/portlet/ajaxable`|`com.liferay.portlet.ajaxable=<boolean>`|\n|`/liferay-portlet-app/portlet/asset-renderer-factory`|[3](#three)|\n|`/liferay-portlet-app/portlet/atom-collection-adapter`|[3](#three)|\n|`/liferay-portlet-app/portlet/autopropagated-parameters`|`com.liferay.portlet.autopropagated-parameters=<String>`[2](#two)|\n|`/liferay-portlet-app/portlet/configuration-action-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/configuration-path`|`com.liferay.portlet.configuration-path=<String>`|\n|`/liferay-portlet-app/portlet/control-panel-entry-category`|`com.liferay.portlet.control-panel-entry-category=<String>`|\n|`/liferay-portlet-app/portlet/control-panel-entry-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/control-panel-entry-weight`|`com.liferay.portlet.control-panel-entry-weight=<double>`|\n|`/liferay-portlet-app/portlet/css-class-wrapper`|`com.liferay.portlet.css-class-wrapper=<String>`|\n|`/liferay-portlet-app/portlet/custom-attributes-display`|[3](#three)|\n|`/liferay-portlet-app/portlet/ddm-display`|[3](#three)|\n|`/liferay-portlet-app/portlet/facebook-integration`|`com.liferay.portlet.facebook-integration=<String>`|\n|`/liferay-portlet-app/portlet/footer-portal-css`|`com.liferay.portlet.footer-portal-css=<String>`[2](#two)|\n|`/liferay-portlet-app/portlet/footer-portal-javascript`|`com.liferay.portlet.footer-portal-javascript=<String>`[2](#two)|\n|`/liferay-portlet-app/portlet/footer-portlet-css`|`com.liferay.portlet.footer-portlet-css=<String>`[2](#two)|\n|`/liferay-portlet-app/portlet/footer-portlet-javascript`|`com.liferay.portlet.footer-portlet-javascript=<String>`[2](#two)|\n|`/liferay-portlet-app/portlet/friendly-url-mapper-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/friendly-url-mapping`|`com.liferay.portlet.friendly-url-mapping=<String>`|\n|`/liferay-portlet-app/portlet/friendly-url-routes`|`com.liferay.portlet.friendly-url-routes=<String>`|\n|`/liferay-portlet-app/portlet/header-portal-css`|`com.liferay.portlet.header-portal-css=<String>`[2](#two)|\n|`/liferay-portlet-app/portlet/header-portal-javascript`|`com.liferay.portlet.header-portal-javascript=<String>`[2](#two)|\n|`/liferay-portlet-app/portlet/header-portlet-css`|`com.liferay.portlet.header-portlet-css=<String>`[2](#two)|\n|`/liferay-portlet-app/portlet/header-portlet-javascript`|`com.liferay.portlet.header-portlet-javascript=<String>`[2](#two)|\n|`/liferay-portlet-app/portlet/header-request-attribute-prefix`|`com.liferay.portlet.header-request-attribute-prefix=<String>` [7](#seven)|\n|`/liferay-portlet-app/portlet/header-timeout`|`header-timeout=<int>`|\n|`/liferay-portlet-app/portlet/icon`|`com.liferay.portlet.icon=<String>`|\n|`/liferay-portlet-app/portlet/include`|not supported|\n|`/liferay-portlet-app/portlet/indexer-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/instanceable`|`com.liferay.portlet.instanceable=<boolean>`|\n|`/liferay-portlet-app/portlet/layout-cacheable`|`com.liferay.portlet.layout-cacheable=<boolean>`|\n|`/liferay-portlet-app/portlet/maximize-edit`|`com.liferay.portlet.maximize-edit=<boolean>`|\n|`/liferay-portlet-app/portlet/maximize-help`|`com.liferay.portlet.maximize-help=<boolean>`|\n|`/liferay-portlet-app/portlet/open-search-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/parent-struts-path`|`com.liferay.portlet.parent-struts-path=<String>`|\n|`/liferay-portlet-app/portlet/permission-propagator`|[3](#three)|\n|`/liferay-portlet-app/portlet/poller-processor-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/pop-message-listener-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/pop-up-print`|`com.liferay.portlet.pop-up-print=<boolean>`|\n|`/liferay-portlet-app/portlet/portlet-data-handler-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/portlet-layout-listener-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/portlet-name`|not supported|\n|`/liferay-portlet-app/portlet/portlet-url-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/preferences-company-wide`|`com.liferay.portlet.preferences-company-wide=<boolean>`|\n|`/liferay-portlet-app/portlet/preferences-owned-by-group`|`com.liferay.portlet.preferences-owned-by-group=<boolean>`|\n|`/liferay-portlet-app/portlet/preferences-unique-per-layout`|`com.liferay.portlet.preferences-unique-per-layout=<boolean>`|\n|`/liferay-portlet-app/portlet/private-request-attributes`|`com.liferay.portlet.private-request-attributes=<boolean>`|\n|`/liferay-portlet-app/portlet/private-session-attributes`|`com.liferay.portlet.private-session-attributes=<boolean>`|\n|`/liferay-portlet-app/portlet/remoteable`|`com.liferay.portlet.remoteable=<boolean>`|\n|`/liferay-portlet-app/portlet/render-timeout`|`com.liferay.portlet.render-timeout=<int>`|\n|`/liferay-portlet-app/portlet/render-weight`|`com.liferay.portlet.render-weight=<int>`|\n|`/liferay-portlet-app/portlet/requires-namespaced-parameters`|`com.liferay.portlet.requires-namespaced-parameters=<boolean>`|\n|`/liferay-portlet-app/portlet/restore-current-view`|`com.liferay.portlet.restore-current-view=<boolean>`|\n|`/liferay-portlet-app/portlet/scheduler-entry`|[3](#three)|\n|`/liferay-portlet-app/portlet/scopeable`|`com.liferay.portlet.scopeable=<boolean>`|\n|`/liferay-portlet-app/portlet/show-portlet-access-denied`|`com.liferay.portlet.show-portlet-access-denied=<boolean>`|\n|`/liferay-portlet-app/portlet/show-portlet-inactive`|`com.liferay.portlet.show-portlet-inactive=<boolean>`|\n|`/liferay-portlet-app/portlet/single-page-application`|`com.liferay.portlet.single-page-application=<boolean>`|\n|`/liferay-portlet-app/portlet/social-activity-interpreter-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/social-request-interpreter-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/social-interactions-configuration`|[3](#three)|\n|`/liferay-portlet-app/portlet/staged-model-data-handler-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/struts-path`|`com.liferay.portlet.struts-path=<String>`|\n|`/liferay-portlet-app/portlet/system`|`com.liferay.portlet.system=<boolean>`|\n|`/liferay-portlet-app/portlet/template-handler`|[3](#three)|\n|`/liferay-portlet-app/portlet/trash-handler`|[3](#three)|\n|`/liferay-portlet-app/portlet/url-encoder-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/use-default-template`|`com.liferay.portlet.use-default-template=<boolean>`|\n|`/liferay-portlet-app/portlet/user-notification-definitions`|not supported|\n|`/liferay-portlet-app/portlet/user-notification-handler-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/user-principal-strategy`|`com.liferay.portlet.user-principal-strategy=<String>`|\n|`/liferay-portlet-app/portlet/virtual-path`|`com.liferay.portlet.virtual-path=<String>`|\n|`/liferay-portlet-app/portlet/webdav-storage-class`|[3](#three)|\n|`/liferay-portlet-app/portlet/webdav-storage-token`|not supported|\n|`/liferay-portlet-app/portlet/workflow-handler`|[3](#three)|\n|`/liferay-portlet-app/portlet/xml-rpc-method-class`|[3](#three)|\n\n-   [<a name=\"one\">1</a>] Portlets are registered as concrete objects.\n-   [<a name=\"two\">2</a>] Multiples of these properties may be used. This \n    results in an array of values.\n-   [<a name=\"three\">3</a>] This type is registered as an OSGi service.\n-   [<a name=\"four\">4</a>] \n    https://xmlns.jcp.org/xml/ns/portlet/portlet-app_3_0.xsd\n-   [<a name=\"five\">5</a>] \n    [http://www.liferay.com/dtd/liferay-portlet-app_7_2_0.dtd](@platform-ref@/7.2-latest/definitions/liferay-portlet-app_7_2_0.dtd.html)\n-   [<a name=\"six\">6</a>] Here's an example of using multiple \n    `javax.portlet.dependency` properties.\n\n    *Old:*\n    \n    ```xml\n    <portlet>\n    \t...\n    \t<dependency>\n    \t\t<name>jquery</name>\n    \t\t<scope>com.jquery</scope>\n    \t\t<version>2.1.1</version>\n    \t</dependency>\n    \t<dependency>\n    \t\t<name>jsutil</name>\n    \t\t<scope>com.mycompany</scope>\n    \t\t<version>1.0.0</version>\n    \t</dependency>\n    \t...\n    </portlet>\n    ```\n\n    *New:*\n\n    ```java\n    @Component(\n        immediate = true, property = {\n            \"javax.portlet.name=my_portlet\",\n            \"javax.portlet.display-name=my-portlet\",\n            \"javax.portlet.dependency=jquery;com.jquery;2.1.1\",\n            \"javax.portlet.dependency=jsutil;com.mycompany;1.0.0\"\n        }, service = Portlet.class\n    )\n    public class MyPortlet extends GenericPortlet {\n        ...\n    } \n    ```\n\n\n-   [<a name=\"seven\">7</a>] Here's an example for the \n    `com.liferay.portlet.header-request-attribute-prefix` property.\n\n    *Old:*\n\n    ```xml\n    <portlet>\n    \t...\n    \t<header-request-attribute-prefix>com.mycompany</header-request-attribute-prefix>\n    \t...\n    </portlet>\n    ```\n    \n    *New:* \n\n    ```java\n    @Component(\n        immediate = true, property = {\n            \"javax.portlet.name=my_portlet\",\n            \"javax.portlet.display-name=my-portlet\",\n            \"javax.portlet.dependency=jquery;com.jquery;2.1.1\",\n            \"javax.portlet.dependency=jsutil;com.mycompany;1.0.0\",\n            \"com.liferay.portlet.header-request-attribute-prefix=com.mycompany\"\n        }, service = Portlet.class\n    )\n    public class MyPortlet extends GenericPortlet {\n        ...\n    } \n    ```\n\n-   [<a name=\"eight\">8</a>] Here's an example for the \n    `javax.portlet.listener` property.\n\n    *Old:*\n\n    ```xml\n    <portlet>\n        ...\n        <listener>\n            <listener-class>com.mycompany.MyPortletURLGenerationListener</listener-class>\n            <ordinal>1</ordinal>\n        </listener>\n        ...\n    </portlet>\n    ```\n\n    *New:*\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\"javax.portlet.name=myPortlet\",\n            \"javax.portlet.listener=com.mycompany.MyPortletURLGenerationListener;1\"\n        }, service = Portlet.class\n    )\n    public class MyPortlet extends GenericPortlet {\n        ...\n    } \n    ```\n\n-   [<a name=\"nine\">9</a>] An `javax.portlet.init-param` property can be \n    declared like this: \n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\"javax.portlet.name=myPortlet\", \n            \"javax.portlet.init-param.myInitParam=1234\"},\n        service = PortletFilter.class\n    )\n    public class MyFilter implements RenderFilter {\n        ...\n    }\n    ```\n\n-   [<a name=\"ten\">10</a>] @product@ creates each portlet's ID based on the \n    portlet's name (i.e., the `portlet-name` descriptor in `liferay-portlet.xml`\n    or the `javax.portlet.name` OSGi service property). Dashes, periods, and\n    spaces are allowed in the portlet name, but they and all other JavaScript\n    unsafe characters are stripped from the name value that's used for the\n    portlet ID. Therefore, make your portlet name unique in light of the\n    characters that are removed. Otherwise, if you try to deploy a portlet whose\n    ID is the same as a portlet that's already deployed, your portlet deployment\n    fails and @product@ logs a message like this:\n\n    ```\n    Portlet id [portletId] is already in use\n    ```\n"
  },
  {
    "path": "en/developer/reference/articles/02-portletmvc4spring/01-portletmvc4spring-intro.markdown",
    "content": "---\nheader-id: portletmvc4spring\n---\n\n# PortletMVC4Spring\n\n[TOC levels=1-4]\n\nPortletMVC4Spring integrates Spring, the Spring Web Framework, and the MVC\ndesign pattern with portlet development. As such, it uses configuration files\nfrom each of these areas and provides new annotations that facilitate portlet\napplication development. Here are the PortletMVC4Spring reference topics:\n\n-   [PortletMVC4Spring Annotations](/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-annotations)\n\n-   [PortletMVC4Spring Configuration Files](/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-configuration-files)\n"
  },
  {
    "path": "en/developer/reference/articles/02-portletmvc4spring/02-portletmvc4spring-project-anatomy.markdown",
    "content": "---\nheader-id: portletmvc4spring-project-anatomy\n---\n\n# PortletMVC4Spring Project Anatomy\n\n[TOC levels=1-4]\n\nPortletMVC4Spring portlets are packaged in WARs. Liferay provide Maven archetypes for creating projects configured to use JSP/JSPX and Thymeleaf templates. Their commands are listed below. The PortletMVC4Spring project structure follows the commands. \n\n## Maven Commands for Generating PortletMVC4Spring Projects\n\nHere are Maven commands for \ngenerating PortletMVC4Spring portlet projects that use JSPX and\n[Thymeleaf](https://www.thymeleaf.org)\nView templates: \n\n### SP/JSPX Form Portlet\n\n```bash\nmvn archetype:generate \\\n-DarchetypeGroupId=com.liferay.portletmvc4spring.archetype \\\n-DarchetypeArtifactId=com.liferay.portletmvc4spring.archetype.form.jsp.portlet \\\n-DarchetypeVersion=5.1.0 \\ \n-DgroupId=com.mycompany \\ \n-DartifactId=com.mycompany.my.form.jsp.portlet\n```\n\n### Thymeleaf Form Portlet\n\n```bash\nmvn archetype:generate \\\n-DarchetypeGroupId=com.liferay.portletmvc4spring.archetype \\\n-DarchetypeArtifactId=com.liferay.portletmvc4spring.archetype.form.thymeleaf.portlet \\\n-DarchetypeVersion=5.1.0 \\\n-DgroupId=com.mycompany \\\n-DartifactId=com.mycompany.my.form.thymeleaf.portlet\n```\n\n## Project Structure \n\nThe Maven commands generate a project that includes Model and Controller\nclasses, View templates, a resource bundle, a stylesheet, and more. The\n[Spring contexts and configuration files](/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-configuration-files)\nset PortletMVC4Spring development essentials. Here's the resulting project\nstructure: \n\n-   `[com.mycompany.my.form.jsp.portlet]`/ &rarr; Arbitrary project name\n    -   `src/`\n        -   `main/`\n            -   `java/[my-package-path]/`\n                -   `controller/` &rarr; Sub-package for Controller classes \n                    (optional)\n                -   `dto/` &rarr; Sub-package for Model (data transfer \n                    object) classes (optional)\n                -    `resources/` &rarr; Resources to include in the class \n                    path \n                    -   `content/` &rarr; Resource bundles \n                    -   `log4j.properties` &rarr; Log4J logging \n                        configuration\n                -   `webapp/` \n                    -   `resources/`\n                        -   `css/` &rarr; Style sheets\n                        -   `images/` &rarr; Images \n                    -   `WEB-INF/`\n                        -   `spring-context/` &rarr; Contexts\n                            -   `portlet/` &rarr; Portlet contexts \n                                -   `portlet1-context.xml` &rarr; Portlet \n                                    context\n                            -   `portlet-application-context.xml` &rarr; \n                                Application context\n                        -   `views/` &rarr; View templates \n                        -   `liferay-display.xml` &rarr; Portlet display \n                            configuration \n                        -   `liferay-plugin-package.properties` &rarr; \n                            Packaging descriptor \n                        -   `liferay-portlet.xml` &rarr; Liferay-specific \n                            portlet configuration \n                        -   `portlet.xml` &rarr; Portlet configuration\n                        -   `web.xml` &rarr; Web application configuration\n        -   `test/java/` &rarr; Test source files\n    -   `build.gradle` &rarr; Gradle build file \n    -   `pom.xml` &rarr; Maven POM\n"
  },
  {
    "path": "en/developer/reference/articles/02-portletmvc4spring/03-portletmvc4spring-annotations.markdown",
    "content": "---\nheader-id: portletmvc4spring-annotations\n---\n\n# PortletMVC4Spring Annotations\n\n[TOC levels=1-4]\n\nPortletMVC4Spring provides several annotations for mapping requests to controller classes and controller methods.  \n\n## `@RenderMapping` Annotation Examples\n\nThe following table describes some `@RenderMapping` annotation examples.\n\n| Example         | Description |\n| :-------------- | :---------- |\n| `@RenderMapping`  | Handle primary render requests if no other handler methods match the render request. |\n| `@RenderMapping(params = \"javax.portlet.action=success\")` | Handle the render request if has parameter a parameter setting `javax.portlet.action=success`. |\n| ` @RenderMapping(param = \"foo\")` | Handle the request if it has a parameter named `foo`, regardless of its value. |\n| `@RenderMapping(param = \"!bar\")` | Handle the request as long as it has not parameter named `bar`. |\n|  `@RenderMapping(windowState = \"MAXIMIZED\")` | Handle the request if the window state is `MAXIMIZED`. Note, supported portlet window states are `NORMAL`, `MAXIMIZED`, and `MINIMIZED`. |\n\n## `@ActionMapping` Annotation Examples\n\nThe table below describes some `@ActionMapping` annotation examples.\n\n| Example         | Description |\n| :-------------- | :---------- |\n| `@ActionMapping`  | Handle primary action requests if no other handler methods match the action request. |\n| `@ActionMapping(params = some.param=yourValue\")` | Handle the action request if has parameter a parameter setting `javax.portlet.action=success`. |\n| ` @ActionMapping(param = \"foo\")` | Handle the request if it has a parameter named `foo`, regardless of its value. |\n| `@ActionMapping(param = \"!bar\")` | Handle the request as long as it has not parameter named `bar`. |\n"
  },
  {
    "path": "en/developer/reference/articles/02-portletmvc4spring/99-portletmvc4spring-configuration-files.markdown",
    "content": "---\nheader-id: portletmvc4spring-configuration-files\n---\n\n# PortletMVC4Spring Configuration Files\n\n[TOC levels=1-4]\n\nA PortletMVC4Spring application has these descriptors, Spring contexts, and\nproperties files in its `WEB-INF` folder: \n\n-   `web.xml` &rarr; Web application descriptor\n-   `portlet.xml` &rarr; Portlet application descriptor\n-   `liferay-portlet.xml` &rarr; Liferay-specific portlet descriptor \n-   `liferay-display.xml` &rarr; Liferay-specific display \n    descriptor \n-   `spring-context/portlet-application-context.xml` &rarr; Portlet application \n    context\n-   `spring-context/portlet/[portlet]-context.xml` &rarr; Portlet context\n-   `liferay-plugin-package.properties` &rarr; Packaging descriptor \n\nExamples of each file are provided and portlet-specific content is highlighted. \n\n## web.xml\n\nThe servlet container processes the `web.xml`. This file specifies the servlet\nthat render's the portlet and the portlet application's context, servlet,\nfilters, listeners, and more. Here's an example `web.xml`:\n\n```xml\n<?xml version=\"1.0\"?>\n\n<web-app version=\"3.1\" xmlns=\"http://xmlns.jcp.org/xml/ns/javaee\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd\">\n\t<context-param>\n\t\t<param-name>contextConfigLocation</param-name>\n\t\t<param-value>/WEB-INF/spring-context/portlet-application-context.xml</param-value>\n\t</context-param>\n\t<servlet>\n\t\t<servlet-name>ViewRendererServlet</servlet-name>\n\t\t<servlet-class>com.liferay.portletmvc4spring.ViewRendererServlet</servlet-class>\n\t\t<load-on-startup>1</load-on-startup>\n\t</servlet>\n\t<servlet-mapping>\n\t\t<servlet-name>ViewRendererServlet</servlet-name>\n\t\t<url-pattern>/WEB-INF/servlet/view</url-pattern>\n\t</servlet-mapping>\n\t<filter>\n\t\t<filter-name>delegatingFilterProxy</filter-name>\n\t\t<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>\n\t</filter>\n\t<filter-mapping>\n\t\t<filter-name>delegatingFilterProxy</filter-name>\n\t\t<url-pattern>/WEB-INF/servlet/view</url-pattern>\n\t\t<dispatcher>FORWARD</dispatcher>\n\t\t<dispatcher>INCLUDE</dispatcher>\n\t</filter-mapping>\n\t<listener>\n\t\t<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n\t</listener>\n</web-app>\n```\n\nThe `<context-param/>` element gives the path to the portlet application context\n(discussed later):\n\n```xml\n<context-param>\n    <param-name>contextConfigLocation</param-name>\n    <param-value>/WEB-INF/spring-context/portlet-application-context.xml</param-value>\n</context-param>\n```\n\nThe `<servlet/>` and `<servlet-mapping/>` elements set the servlet and the\ninternal location for its views. \n\n```xml\n<servlet>\n    <servlet-name>ViewRendererServlet</servlet-name>\n    <servlet-class>com.liferay.portletmvc4spring.ViewRendererServlet</servlet-class>\n    <load-on-startup>1</load-on-startup>\n</servlet>\n<servlet-mapping>\n    <servlet-name>ViewRendererServlet</servlet-name>\n    <url-pattern>/WEB-INF/servlet/view</url-pattern>\n</servlet-mapping>\n```\n\nThe\n[`ViewRendererServlet`](https://liferay.github.io/portletmvc4spring/apidocs/com/liferay/portletmvc4spring/ViewRendererServlet.html). \nconverts portlet requests into servlet requests and\nenables the view to be rendered using the Spring Web MVC infrastructure and the\ninfrastructure's renderers for JSP, Thymeleaf, Velocity, and more. \n\nThe filter and filter mappings are set to forward and include servlet views as\nnecessary. \n\n```xml\n<filter>\n    <filter-name>delegatingFilterProxy</filter-name>\n    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>\n</filter>\n<filter-mapping>\n    <filter-name>delegatingFilterProxy</filter-name>\n    <url-pattern>/WEB-INF/servlet/view</url-pattern>\n    <dispatcher>FORWARD</dispatcher>\n    <dispatcher>INCLUDE</dispatcher>\n</filter-mapping>\n```\n\nA listener is configured for processing the application's contexts. \n\n```xml \n<listener>\n    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n</listener>\n```\n\nLiferay's project archetypes generate all this boilerplate code.  \n\n## portlet.xml\n\nThe `portlet.xml` file describes the portlet application to the portlet\ncontainer. Here's an example:\n\n```xml\n<?xml version=\"1.0\"?>\n\n<portlet-app xmlns=\"http://xmlns.jcp.org/xml/ns/portlet\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/portlet http://xmlns.jcp.org/xml/ns/portlet/portlet-app_3_0.xsd\" version=\"3.0\">\n\t<portlet>\n\t\t<portlet-name>portlet1</portlet-name>\n\t\t<display-name>com.mycompany.my.form.jsp.portlet</display-name>\n\t\t<portlet-class>com.liferay.portletmvc4spring.DispatcherPortlet</portlet-class>\n\t\t<init-param>\n\t\t\t<name>contextConfigLocation</name>\n\t\t\t<value>/WEB-INF/spring-context/portlet/portlet1-context.xml</value>\n\t\t</init-param>\n\t\t<expiration-cache>0</expiration-cache>\n\t\t<supports>\n\t\t\t<mime-type>text/html</mime-type>\n\t\t\t<portlet-mode>view</portlet-mode>\n\t\t</supports>\n\t\t<resource-bundle>content.portlet1</resource-bundle>\n\t\t<portlet-info>\n\t\t\t<title>com.mycompany.my.form.jsp.portlet</title>\n\t\t\t<short-title>com.mycompany.my.form.jsp.portlet</short-title>\n\t\t\t<keywords>com.mycompany.my.form.jsp.portlet</keywords>\n\t\t</portlet-info>\n\t\t<security-role-ref>\n\t\t\t<role-name>administrator</role-name>\n\t\t</security-role-ref>\n\t\t<security-role-ref>\n\t\t\t<role-name>guest</role-name>\n\t\t</security-role-ref>\n\t\t<security-role-ref>\n\t\t\t<role-name>power-user</role-name>\n\t\t</security-role-ref>\n\t\t<security-role-ref>\n\t\t\t<role-name>user</role-name>\n\t\t</security-role-ref>\n\t</portlet>\n\t<filter>\n\t\t<filter-name>SpringSecurityPortletFilter</filter-name>\n\t\t<filter-class>com.liferay.portletmvc4spring.security.SpringSecurityPortletFilter</filter-class>\n\t\t<lifecycle>ACTION_PHASE</lifecycle>\n\t\t<lifecycle>RENDER_PHASE</lifecycle>\n\t\t<lifecycle>RESOURCE_PHASE</lifecycle>\n\t</filter>\n\t<filter-mapping>\n\t\t<filter-name>SpringSecurityPortletFilter</filter-name>\n\t\t<portlet-name>portlet1</portlet-name>\n\t</filter-mapping>\n</portlet-app>\n```\n\nThis application has one portlet named `portlet1`.\n\n```xml\n<portlet-name>portlet1</portlet-name>\n<display-name>com.mycompany.my.form.jsp.portlet</display-name>\n<portlet-class>com.liferay.portletmvc4spring.DispatcherPortlet</portlet-class>\n```\n\nThe `<portlet-name/>` is internal and the `<display-name/>` is shown to users.\n`<portlet-class/>` specifies the portlet's Java class. \n\n**Important:** All PortletMVC4Spring portlets must specify\n`<portlet-class>com.liferay.portletmvc4spring.DispatcherPortlet</portlet-class>`. \n\nThe `<supports/>` element must declare the mime type that the portlet templates\nuse. \n\nThe `<resource-bundle/>` sets the path to the portlet's localized Java message\nproperties. For example, the element refers to properties at\n`content/portlet1.properties`: \n\n```xml\n<resource-bundle>content.portlet1</resource-bundle>\n```\n\nThe `<portlet-info/>` element lists the portlet's titles and reserved keyword. \n\nThe `<security-role-ref/>` elements declare default user roles the portlet\naccounts for. \n\nLastly, the `<filter/>` named \n[`SpringSecurityPortletFilter`](https://liferay.github.io/portletmvc4spring/apidocs/index.html)\nprevents Cross-Site Request Forgery (CSRF). \n\n```xml\n<filter>\n    <filter-name>SpringSecurityPortletFilter</filter-name>\n    <filter-class>com.liferay.portletmvc4spring.security.SpringSecurityPortletFilter</filter-class>\n    <lifecycle>ACTION_PHASE</lifecycle>\n    <lifecycle>RENDER_PHASE</lifecycle>\n    <lifecycle>RESOURCE_PHASE</lifecycle>\n</filter>\n<filter-mapping>\n    <filter-name>SpringSecurityPortletFilter</filter-name>\n    <portlet-name>portlet1</portlet-name>\n</filter-mapping>\n```\n\nThe\n[`portlet XSD`](https://docs.liferay.com/portlet-api/3.0/portlet-app_3_0.xsd)\ndefines the `portlet.xml`. The Liferay-specific portlet descriptor is next. \n\n## liferay-portlet.xml\n\nThe `liferay-portlet.xml` file applies Liferay-specific settings that provide\nmore developer features. Here's an example:\n\n```xml \n<?xml version=\"1.0\"?>\n<!DOCTYPE liferay-portlet-app PUBLIC \"-//Liferay//DTD Portlet Application 7.1.0//EN\" \"http://www.liferay.com/dtd/liferay-portlet-app_7_1_0.dtd\">\n\n<liferay-portlet-app>\n\t<portlet>\n\t\t<portlet-name>portlet1</portlet-name>\n\t\t<icon>/resources/images/icon.png</icon>\n\t\t<requires-namespaced-parameters>false</requires-namespaced-parameters>\n\t</portlet>\n\t<role-mapper>\n\t\t<role-name>administrator</role-name>\n\t\t<role-link>Administrator</role-link>\n\t</role-mapper>\n\t<role-mapper>\n\t\t<role-name>guest</role-name>\n\t\t<role-link>Guest</role-link>\n\t</role-mapper>\n\t<role-mapper>\n\t\t<role-name>power-user</role-name>\n\t\t<role-link>Power User</role-link>\n\t</role-mapper>\n\t<role-mapper>\n\t\t<role-name>user</role-name>\n\t\t<role-link>User</role-link>\n\t</role-mapper>\n</liferay-portlet-app>\n```\n\nThis `<portlet/>` element associates an icon with the portlet and indicates\nthat name-spaced parameters aren't required. \n\nThe `<role-mapper/>` elements associate the portlet with default @product@ user\nroles. \n\nThe\n[liferay-portlet-app DTD](@platform-ref@/7.2-latest/definitions/liferay-portlet-app_7_2_0.dtd.html)\ndefines the `liferay-portlet.xml` file. \n\n## liferay-display.xml\n\nThe `liferay-display.xml` applies display characteristics to the portlet. For\nexample, this descriptor associates the portlet with a Widget category in\n@product@'s Add Widget menu. \n\n```xml\n<?xml version=\"1.0\"?>\n<!DOCTYPE display PUBLIC \"-//Liferay//DTD Display 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-display_7_2_0.dtd\">\n\n<display>\n<category name=\"category.sample\">\n    <portlet id=\"portlet1\" />\n</category>\n</display>\n```\n\nSee the\n[liferay-display DTD](@platform-ref@/7.2-latest/definitions/liferay-display_7_2_0.dtd.html)\nfor details. \n\nIt's time to look at the application contexts. \n\n## Portlet Application Context \n\nThis context applies to all of the application's portlets. This is where you\nspecify view resolvers, resource bundles, security beans, proxies, and more.\nHere's an example:\n\n```xml \n<?xml version=\"1.0\"?>\n\n<beans\n\txmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:context=\"http://www.springframework.org/schema/context\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"\n\t\thttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\">\n\t<context:annotation-config />\n\t<bean class=\"org.springframework.web.servlet.view.InternalResourceViewResolver\" id=\"viewResolver\">\n\t\t<property name=\"contentType\" value=\"text/html;charset=UTF-8\" />\n\t\t<property name=\"prefix\" value=\"/WEB-INF/views/\" />\n\t\t<property name=\"suffix\" value=\".jspx\" />\n\t\t<property name=\"viewClass\" value=\"com.liferay.portletmvc4spring.PortletJstlView\" />\n\t</bean>\n\t<bean id=\"messageSource\" class=\"org.springframework.context.support.ResourceBundleMessageSource\">\n\t\t<property name=\"basenames\">\n\t\t\t<list>\n\t\t\t\t<value>content.portlet1</value>\n\t\t\t</list>\n\t\t</property>\n\t\t<property name=\"defaultEncoding\" value=\"UTF-8\" />\n\t</bean>\n\t<bean id=\"springSecurityPortletConfigurer\" class=\"com.liferay.portletmvc4spring.security.SpringSecurityPortletConfigurer\" />\n\t<bean id=\"delegatingFilterProxy\" class=\"org.springframework.web.filter.DelegatingFilterProxy\">\n\t\t<property name=\"targetBeanName\" value=\"springSecurityFilterChain\" />\n\t</bean>\n</beans>\n```\n\nThe view resolver bean above handles JSPX view templates. To resolve Thymeleaf\nview templates, for example, you could specify these beans: \n\n```xml \n<bean class=\"org.thymeleaf.templateresolver.ServletContextTemplateResolver\" id=\"templateResolver\">\n    <property name=\"prefix\" value=\"/WEB-INF/views/\"/>\n    <property name=\"suffix\" value=\".html\"/>\n    <property name=\"templateMode\" value=\"HTML\"/>\n</bean>\n<bean class=\"org.thymeleaf.spring5.SpringTemplateEngine\" id=\"templateEngine\">\n    <property name=\"templateResolver\" ref=\"templateResolver\"></property>\n    <property name=\"enableSpringELCompiler\" value=\"true\" />\n</bean>\n<bean class=\"org.thymeleaf.spring5.view.ThymeleafViewResolver\" id=\"viewResolver\">\n    <property name=\"templateEngine\" ref=\"templateEngine\"/>\n    <property name=\"order\" value=\"1\"/>\n</bean>\n``` \n\nThe context's `springSecurityPortletConfigurer` bean facilitates using Spring\nSecurity: \n\n```xml\n<bean id=\"springSecurityPortletConfigurer\" \n    class=\"com.liferay.portletmvc4spring.security.SpringSecurityPortletConfigurer\" />\n```\n\nYou can also designate contexts for each portlet in the application. \n\n## Portlet Contexts\n\nBeans specific to a portlet, go in the portlet's context. Since annotations are\nthe easiest way to develop PortletMVC4Spring portlets, you should specify MVC\nannotation scanning in the portlet context:\n\n```xml \n<?xml version=\"1.0\"?>\n\n<beans\n    xmlns=\"http://www.springframework.org/schema/beans\"\n    xmlns:context=\"http://www.springframework.org/schema/context\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n    xsi:schemaLocation=\"\n        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\n        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\n        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n    <context:component-scan base-package=\"portlet1**\"/>\n    <mvc:annotation-driven/>\n</beans>\n```\n\nThe portlet context naming convention is `[portlet-name]-context.xml`. To\nassociate your portlet with its own context, edit your application's\n`portlet.xml` file and add an `<init-param/>` element that maps the `<portlet/>`\nelement to the portlet's context: \n\n```xml \n<init-param>\n    <name>contextConfigLocation</name>\n    <value>/WEB-INF/spring-context/portlet/portlet1-context.xml</value>\n</init-param>\n```\n\nWhat's left is to describe your application package. \n\n## liferay-plugin-package.properties \n\nThis file specifies the application's name, version, Java package\nimports/exports, and OSGi metadata. Here's an example package properties file:\n\n```properties \nauthor=N/A\nchange-log=\nlicenses=N/A\nliferay-versions=7.1.0+\nlong-description=\nmodule-group-id=com.mycompany\nmodule-incremental-version=1\nname=com.mycompany.my.form.jsp.portlet\npage-url=\nshort-description=my portlet short description\ntags=myTag\nBundle-Version: 1.0.0\nImport-Package: com.liferay.portal.webserver,com.liferay.portal.kernel.servlet.filters.invoker\n```\n\nIt uses this OSGi metadata header to\n[import required Java packages](/docs/7-2/customization/-/knowledge_base/c/importing-packages):\n\n```properties\nImport-Package: com.liferay.portal.webserver,\\\ncom.liferay.portal.kernel.servlet.filters.invoker\n```\n\nOn deploying the portlet application WAR file, the\n[WAB Generator](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator)\nadds the specified OSGi metadata to the resulting web application bundle\n(WAB) that's deployed to Liferay's runtime framework.\n\nThe\n[liferay-plugin-package reference document](@platform-ref@/7.2-latest/propertiesdoc/liferay-plugin-package_7_2_0.properties.html)\ndescribes the `liferay-plugin-package.properties` file. \n\nCongratulations! You've successfully toured the PortletMVC4Spring configuration\nfiles. \n\n## Related Topics \n\n[PortletMVC4Spring Annotations](/docs/7-2/reference/-/knowledge_base/r/portletmvc4spring-annotations)\n\n[Migrating to PortletMVC4Spring](/docs/7-2/appdev/-/knowledge_base/a/migrating-to-portletmvc4spring)\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/00-intro.markdown",
    "content": "---\nheader-id: project-templates\n---\n\n# Project Templates\n\n[TOC levels=1-4]\n\nLiferay provides project templates that you can use to generate starter projects\nformatted in an opinionated way. These templates can be used by most build tools\n(e.g., Gradle, Maven, Dev Studio) to generate your desired project structure.\n\nIf you're using [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli),\nexecute the following command to display a full list of project templates:\n\n```bash\nblade create -l\n```\n\nIf you're using [Maven](/docs/7-2/reference/-/knowledge_base/r/maven), you can\nview and use the project templates as Maven archetypes. Execute the following\ncommand to list them:\n\n```bash\nmvn archetype:generate -Dfilter=liferay\n```\n\nArchetypes with the `com.liferay.project.templates` prefix are the latest\ntemplates offered by Liferay.\n\nIf you're using\n[Dev Studio](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio),\nnavigate to *File* &rarr; *New* &rarr; *Liferay Module Project* and view the\nproject templates from the *Project Template Name* drop-down menu.\n\nIn this section of reference articles, each project template is outlined with\nthe appropriate generation command and folder structure. Visit the project\ntemplate article you're most interested in to start building your own project!\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/activator-template.markdown",
    "content": "---\nheader-id: activator-template\n---\n\n# Activator Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay activator as a Liferay\nmodule. To create a Liferay activator via the command line using Blade CLI or\nMaven, use one of the commands with the following parameters:\n\n```bash\nblade create -t activator [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.activator \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate\na Maven project using Blade CLI.\n\nThe template for this kind of project is `activator`. Suppose you want to create\nan activator project called `my-activator-project` with a package name of\n`com.liferay.docs.activator` and a class name of `Activator`. You could run the\nfollowing command to accomplish this:\n\n```bash\nblade create -t activator -p com.liferay.docs.activator -c Activator my-activator-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.activator \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-activator-project \\\n    -Dpackage=com.liferay.docs.activator \\\n    -Dversion=1.0 \\\n    -DclassName=Activator \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nNote that in your class, you're implementing the\n`org.osgi.framework.BundleActivator` interface.\n\nAfter running the Blade command above, your project's directory structure looks\nlike this:\n\n- `my-activator-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/activator`\n                    - `Activator.java`\n            - `resources`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is functional and is deployable to a @product@ instance. To\nbuild upon the generated app, modify the project by adding logic and additional\nfiles to the folders outlined above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/api-template.markdown",
    "content": "---\nheader-id: api-template\n---\n\n# API Template\n\n[TOC levels=1-4]\n\nIn this tutorial, you'll learn how to create a Liferay API as a Liferay module.\nTo create a Liferay API via the command line using Blade CLI or Maven, use one\nof the commands with the following parameters:\n\n```bash\nblade create -t api [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.api \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `api`. The `api` template creates a\nsimple `api` module with an empty public interface. For example, suppose you\nwant to create an API project called `my-api-project` with a package name of\n`com.liferay.docs.api` and a class name of `MyApi`. You could run the following\ncommand to accomplish this:\n\n```bash\nblade create -t api -p com.liferay.docs -c MyApi my-api-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.api \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-api-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=MyApi \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure looks\nlike this:\n\n- `my-api-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/api`\n                    - `MyApi.java`\n            - `resources`\n                - `com/liferay/docs/api`\n                    - `packageinfo`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is a working application and is deployable to a @product@\ninstance. To build upon the generated app, modify the project by adding logic\nand additional files to the folders outlined above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/control-menu-entry-template.markdown",
    "content": "---\nheader-id: control-menu-entry-template\n---\n\n# Control Menu Entry Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay Control Menu entry as a\nLiferay module. To create a Liferay Control Menu entry via the command line\nusing Blade CLI or Maven, use one of the commands with the following parameters:\n\n```bash\nblade create -t control-menu-entry [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.control.menu.entry \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `control-menu-entry`. Suppose you want to\ncreate a control menu entry project called `my-control-menu-entry-project` with\na package name of `com.liferay.docs.entry.control.menu` and a class name of\n`SampleProductNavigationControlMenuEntry`. You could run the following command\nto accomplish this:\n\n```bash\nblade create -t control-menu-entry -p com.liferay.docs.entry -c Sample my-control-menu-entry-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.control.menu.entry \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-control-menu-entry-project \\\n    -Dpackage=com.liferay.docs.entry \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure would\nlook like this:\n\n- `my-control-menu-entry-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/entry/control/menu`\n                    - `SampleProductNavigationControlMenuEntry.java`\n            - `resources`\n                - `content`\n                    - `Language.properties`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is functional and is deployable to a @product@ instance. To\nbuild upon the generated app, modify the project by adding logic and additional\nfiles to the folders outlined above. You can visit the\n[control-menu-entry](/docs/7-1/reference/-/knowledge_base/r/control-menu-entry)\nsample project for a more expanded sample of a Control Menu entry. \n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/form-field-template.markdown",
    "content": "---\nheader-id: form-field-template\n---\n\n# Form Field Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay form field as a Liferay\nmodule. To create a Liferay form field via the command line using Blade CLI or\nMaven, use one of the commands with the following parameters:\n\n```bash\nblade create -t form-field [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.form.field \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `form-field`. Suppose you want to\ncreate a form field project called `my-form-field-project` with a package name\nof `com.liferay.docs.form.field` and a class name prefix of `MyFormField`. You\ncould run one of the following commands to accomplish this:\n\n```bash\nblade create -t form-field -p com.liferay.docs -c MyFormField my-form-field-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.form.field \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-form-field-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=MyFormField \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure looks\nlike this:\n\n- `my-form-field-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/form/field`\n                    - `MyFormFieldDDMFormFieldRenderer.java`\n                    - `MyFormFieldDDMFormFieldType.java`\n            - `resources`\n                - `content`\n                    - `Language.properties`\n                - `META-INF`\n                    - `resources`\n                        - `config.js`\n                        - `my-form-field-project.soy`\n                        - `my-form-field-project_field.js`\n                        - `my-form-field-project_field.es.js`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is a working form field and is deployable to a @product@\ninstance. To build upon the generated app, modify the project by adding logic\nand additional files to the folders outlined above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/fragment-template.markdown",
    "content": "---\nheader-id: using-the-fragment-template\n---\n\n# Fragment Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay fragment as a Liferay\nmodule. You can learn more about fragment modules in the\n[Declaring a Fragment Host](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-osgi-fragments#declaring-a-fragment-host)\narticle and in section 3.14 of the\n[OSGi Alliance's core specification document](https://osgi.org/download/r6/osgi.core-6.0.0.pdf).\n\nTo create a Liferay fragment via the command line using Blade CLI or\nMaven, use one of the commands with the following parameters:\n\n```bash\nblade create -t fragment [-h hostBundleName] [-H hostBundleVersion] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.fragment \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `fragment`. Suppose you want to create\na fragment project called `my-fragment-project` with a host bundle symbolic name\nof `com.liferay.login.web` and host bundle version of `1.0.0`. You could run the\nfollowing command to accomplish this:\n\n```bash\nblade create -t fragment -h com.liferay.login.web -H 1.0.0 my-fragment-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.fragment \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-fragment-project \\\n    -Dversion=1.0 \\\n    -Dpackage= \\\n    -DhostBundleSymbolicName=com.liferay.login.web \\\n    -DhostBundleVersion=1.0.0 \\\n    -DliferayVersion=7.2\n```\n\nThe folder structure is created, but there are no files. The only files created\nare the `bnd.bnd` and `build.gradle` files, which specify your host bundle and\nits information, and your build tool's files. After running the Blade command\nabove, your project's directory structure looks like this:\n\n- `my-fragment-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `my/fragment/project`\n            -`resources`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is functional and is deployable to a @product@ instance. To\nbuild upon the generated app, modify the project by adding logic and additional\nfiles to the folders outlined above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/freemarker-portlet-template.markdown",
    "content": "---\nheader-id: freemarker-portlet-template\n---\n\n# FreeMarker Portlet Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay FreeMarker portlet\napplication as a Liferay module. To create a Liferay FreeMarker portlet\napplication via the command line using Blade CLI or Maven, use one of the\ncommands with the following parameters:\n\n```bash\nblade create -t freemarker-portlet [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.freemarker.portlet \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `freemarker-portlet`. Suppose you want\nto create a FreeMarker portlet project called `my-freemarker-portlet-project`\nwith a package name of `com.liferay.docs.freemarkerportlet` and a class name of\n`MyFreemarkerPortlet`. Also, you'd like to create a service of type\n`javax.portlet.Portlet` that extends the\n`com.liferay.util.bridges.freemarker.FreeMarkerPortlet` class. Here, *service*\nmeans an OSGi service, not a Liferay API. Another way to say *service type* is\nto say *component type*. You could run the following command to accomplish this:\n\n```bash\nblade create -t freemarker-portlet -p com.liferay.docs.freemarkerportlet -c MyFreemarkerPortlet my-freemarker-portlet-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.freemarker.portlet \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-freemarker-portlet-project \\\n    -Dpackage=com.liferay.docs.freemarkerportlet \\\n    -Dversion=1.0 \\\n    -DclassName=MyFreemarkerPortlet \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure looks\nlike this:\n\n- `my-freemarker-portlet-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/freemarkerportlet`\n                    - `constants`\n                        - `MyFreemarkerPortletKeys.java`\n                    - `portlet`\n                        - `MyFreemarkerPortlet.java`\n            - `resources`\n                - `content`\n                    - `Language.properties`\n                - `META-INF`\n                    - `resources`\n                        - `css`\n                            - `main.scss`\n                - `templates`\n                    - `init.ftl`\n                    - `view.ftl`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is a working application and is deployable to a @product@\ninstance. To build upon the generated app, modify the project by adding logic\nand additional files to the folders outlined above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/layout-template.markdown",
    "content": "---\nheader-id: layout-template\n---\n\n# Layout Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay layout template as a WAR\nproject. To create a Liferay layout template via the command line using Blade\nCLI or Maven, use one of the commands with the following parameters:\n\n```bash\nblade create -t layout-template projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.layout.template \\\n    -DartifactId=[projectName] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `layout-template`. Suppose you want to\ncreate a layout template project called `my-layout-template-project`. You\ncould run one of the following commands to accomplish this:\n\n```bash\nblade create -t layout-template my-layout-template-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.layout.template \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-layout-template-project \\\n    -Dversion=1.0 \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure looks\nlike this:\n\n- `my-layout-template-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `webapp`\n                - `WEB-INF`\n                    - `liferay-layout-templates.xml`\n                    - `liferay-plugin-package.properties`\n                - `my-layout-template-project.ftl`\n                - `my-layout-template-project.png`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated WAR is a working layout template and is deployable to a @product@\ninstance. To build upon the generated layout template, modify the project by\nadding logic and additional files to the folders outlined above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/modules-ext-template.markdown",
    "content": "---\nheader-id: modules-ext-template\n---\n\n# Modules Ext Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create an Ext module. To create an Ext\nmodule via the command line using Blade CLI or Maven, use one of the commands\nwith the following parameters:\n\n```bash\nblade create -t modules-ext [-p packageName] [-m originalModuleName] [-M originalModuleVersion] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.modules.ext \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DoriginalModuleName=[originalModuleName] \\\n    -DoriginalModuleVersion=[originalModuleVersion] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `modules-ext`. Suppose you want to\ncreate an Ext module called `my-ext-module-project` that overrides the\n`com.liferay.test.web` module (BSN) with version `1.0.0`. If you have\n[Target Platform](/docs/7-1/tutorials/-/knowledge_base/t/managing-the-target-platform-for-liferay-workspace)\nenabled, you're not required to specify the intended module version to override.\nAlso, the override module has a package path of `com.liferay.docs.test`. You\nmust use the exact path of the original module when creating an Ext module. You\ncould run the following command to accomplish this: \n\n```bash\nblade create -t modules-ext -p com.liferay.docs.test -m com.liferay.test.web -M 1.0.0 my-ext-module-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.modules.ext \\\n    -DartifactId=my-ext-module-project \\\n    -Dpackage=com.liferay.docs.test \\\n    -DoriginalModuleName=com.liferay.test.web \\\n    -DoriginalModuleVersion=1.0.0 \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure looks\nlike this:\n\n- `my-ext-module-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/test`\n            - `resources`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nTo build upon the generated project, modify the project by adding logic and\nadditional files to the folders outlined above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/mvc-portlet-template.markdown",
    "content": "---\nheader-id: using-the-mvc-portlet-template\n---\n\n# MVC Portlet Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay MVC portlet application as\na Liferay module. To create a Liferay MVC portlet application via the command\nline using Blade CLI or Maven, use one of the commands with the following\nparameters:\n\n```bash\nblade create -t mvc-portlet [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.mvc.portlet \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `mvc-portlet`. Suppose you want to\ncreate an MVC portlet project called `my-mvc-portlet-project` with a package\nname of `com.liferay.docs.mvcportlet` and a class name of `MyMvcPortlet`. Also,\nyou'd like to create a service of type `javax.portlet.Portlet` that extends the\n`com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet` class. Here,\n*service* means an OSGi service, not a Liferay API. Another way to say *service\ntype* is to say *component type*. You could run the following command to\naccomplish this:\n\n```bash\nblade create -t mvc-portlet -p com.liferay.docs.mvcportlet -c MyMvcPortlet my-mvc-portlet-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.mvc.portlet \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-mvc-portlet-project \\\n    -Dpackage=com.liferay.docs.mvcportlet \\\n    -Dversion=1.0 \\\n    -DclassName=MyMvcPortlet \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure looks\nlike this:\n\n- `my-mvc-portlet-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/mvcportlet`\n                    - `constants`\n                        - `MyMvcPortletKeys.java`\n                    -  `portlet`\n                        - `MyMvcPortlet.java`\n            - `resources`\n                - `content`\n                    - `Language.properties`\n                - `META-INF`\n                    - `resources`\n                        - `init.jsp`\n                        - `view.jsp`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is a working application and is deployable to a @product@\ninstance. To build upon the generated app, modify the project by adding logic\nand additional files to the folders outlined above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/panel-app-template.markdown",
    "content": "---\nheader-id: panel-app-template\n---\n\n# Panel App Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay panel app and category as\na Liferay module. To create a Liferay panel app and category via the command\nline using Blade CLI or Maven, use one of the commands with the following\nparameters:\n\n```bash\nblade create -t panel-app [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.panel.app \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `panel-app`. Suppose you want to create\na panel app project called `my-panel-app-project` with a package name prefix of\n`com.liferay.docs` and a class name prefix of `Sample`. You could run the\nfollowing command to accomplish this:\n\n```bash\nblade create -t panel-app -p com.liferay.docs -c Sample my-panel-app-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.panel.app \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-panel-app-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure would\nlook like this\n\n- `my-panel-app-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/`\n                    - `application/list`\n                        - `SamplePanelApp.java`\n                        - `SamplePanelCategory.java`\n                    - `constants`\n                        - `SamplePanelCategoryKeys.java`\n                        - `SamplePortletKeys.java`\n                    - `portlet`\n                        - `SamplePortlet.java`\n            - `resources`\n                - `content`\n                    - `Language.properties`\n                - `META-INF`\n                    - `resources`\n                        - `init.jsp`\n                        - `view.jsp`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is functional and is deployable to a @product@ instance.\nThe generated module, by default, creates a panel category with a panel app in\n@product@'s Product Menu. To build upon the generated app, modify the project by\nadding logic and additional files to the folders outlined above. \n\n<!--Uncomment once article is available\nYou can visit the \nCustomizing the Product Menu\ntutorial for instructions on customizing a panel app project.\n-->\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/portlet-configuration-icon-template.markdown",
    "content": "---\nheader-id: portlet-configuration-icon\n---\n\n# Portlet Configuration Icon\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay portlet configuration\nicon as a Liferay module. To create a portlet configuration icon via the command\nline using Blade CLI or Maven, use one of the commands with the following\nparameters:\n\n```bash\nblade create -t portlet-configuration-icon [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.portlet.configuration.icon \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `portlet-configuration-icon`. Suppose\nyou want to create a portlet configuration icon project called\n`my-portlet-config-icon` with a package name of\n`com.liferay.docs.portlet.configuration.icon` and a class name of\n`SamplePortletConfigurationIcon`. You could run the following command to\naccomplish this:\n\n```bash\nblade create -t portlet-configuration-icon -p com.liferay.docs -c Sample my-portlet-config-icon\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.portlet.configuration.icon \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-portlet-config-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure would\nlook like this\n\n- `my-portlet-config-icon`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/portlet/configuration/icon`\n                    - `SamplePortletConfigurationIcon.java`\n            - `resources`\n                - `content`\n                    - `Language.properties`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is functional and is deployable to a @product@ instance. The\ngenerated module, by default, creates a sample link in the Hello World portlet's\nOptions menu. To build upon the generated app, modify the project by adding\nlogic and additional files to the folders outlined above. You can visit the\n[portlet-configuration-icon](https://github.com/liferay/liferay-blade-samples/tree/master/gradle/extensions/portlet-configuration-icon)\nsample project for a more expanded sample of a portlet configuration icon.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/portlet-provider-template.markdown",
    "content": "---\nheader-id: portlet-provider-template\n---\n\n# Portlet Provider Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay portlet provider as a\nLiferay module. To create a Liferay portlet provider via the command line using\nBlade CLI or Maven, use one of the commands with the following parameters:\n\n```bash\nblade create -t portlet-provider [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.portlet.provider \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `portlet-provider`. Suppose you want to\ncreate a portlet provider project called `my-portlet-provider-project` with a\npackage name of `com.liferay.docs.portlet` and a class name prefix of `Sample`.\nYou could run the following command to accomplish this:\n\n```bash\nblade create -t portlet-provider -p com.liferay.docs -c Sample my-portlet-provider-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.portlet.provider \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-portlet-provider-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure would\nlook like this\n\n- `my-portlet-provider-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs`\n                    - `constants`\n                        - `SamplePortletKeys.java`\n                        - `SampleWebKeys.java`\n                    - `portlet`\n                        - `SampleAddPortletProvider.java`\n                        - `SamplePortlet.java`\n            - `resources`\n                - `META-INF`\n                    - `resources`\n                        - `init.jsp`\n                        - `view.jsp`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is functional and is deployable to a @product@ instance. To\nbuild upon the generated app, modify the project by adding logic and additional\nfiles to the folders outlined above. You can visit the\n[Providing Portlets to Manage Requests](/docs/7-2/frameworks/-/knowledge_base/f/embedding-portlets-in-themes)\ntutorial for instructions on customizing a portlet provider project.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/portlet-toolbar-contributor-template.markdown",
    "content": "---\nheader-id: portlet-toolbar-contributor-template\n---\n\n# Portlet Toolbar Contributor Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay portlet toolbar\ncontributor as a Liferay module. To create a portlet toolbar contributor entry\nvia the command line using Blade CLI or Maven, use one of the commands with the\nfollowing parameters:\n\n```bash\nblade create -t portlet-toolbar-contributor [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.portlet.toolbar.contributor \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `portlet-toolbar-contributor`. Suppose\nyou want to create a portlet toolbar contributor project called\n`my-portlet-toolbar-contributor` with a package name of\n`com.liferay.docs.portlet.toolbar.contributor` and a class name of\n`SamplePortletToolbarContributor`. You could run the following command to\naccomplish this:\n\n```bash\nblade create -t portlet-toolbar-contributor -p com.liferay.docs -c Sample my-portlet-toolbar-contributor\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.portlet.toolbar.contributor \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-portlet-toolbar-contributor \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure would\nlook like this\n\n- `my-portlet-toolbar-contributor`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/portlet/toolbar/contributor`\n                    - `SamplePortletToolbarContributor.java`\n            - `resources`\n                - `content`\n                    - `Language.properties`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is functional and is deployable to a @product@ instance. To\nbuild upon the generated app, modify the project by adding logic and additional\nfiles to the folders outlined above. This generated project, by default, creates\na new button on the Hello World portlet's toolbar. You can visit the\n[portlet-toolbar-contributor](https://github.com/liferay/liferay-blade-samples/tree/master/gradle/extensions/portlet-toolbar-contributor)\nsample project for a more expanded sample of a portlet toolbar contributor.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/rest-template.markdown",
    "content": "---\nheader-id: rest-template\n---\n\n# REST Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay RESTful web service\npackaged in a Liferay module. To create a Liferay RESTful web service via the\ncommand line using Blade CLI or Maven, use one of the commands with the\nfollowing parameters:\n\n```bash\nblade create -t rest [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.rest \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `rest`. Suppose you want to create a\nRESTful web service project called `my-rest-project` with a package name of\n`com.liferay.docs.application` and a class name prefix of `Rest`. You could run\none of the following commands to accomplish this:\n\n```bash\nblade create -t rest -p com.liferay.docs -c Rest my-rest-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.rest \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-rest-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Rest \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure looks\nlike this:\n\n- `my-rest-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/application`\n                    - `RestApplication.java`\n            - `resources`\n                - `configuration`\n                    - `com.liferay.portal.remote.cxf.common.configuration.CXFEndpointPublisherConfiguration-cxf.properties`\n                    - `com.liferay.portal.remote.rest.extender.configuration.RestExtenderConfiguration-rest.properties`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is a working RESTful web service and is deployable to a\n@product@ instance. To build upon the generated app, modify the project by\nadding logic and additional files to the folders outlined above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/service-builder-template.markdown",
    "content": "---\nheader-id: using-the-service-builder-template\n---\n\n# Service Builder Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay portlet application that\nuses Service Builder as Liferay modules. To create a Liferay Service Builder\nproject via the command line using Blade CLI or Maven, use one of the commands\nwith the following parameters:\n\n```bash\nblade create -t service-builder [-p packageName] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service.builder \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DapiPath=[apiPath] \\\n    -DdependencyInjector=[dependencyInjector] \\\n    -DliferayVersion=7.2\n```\n\nBy default, the Service Builder project uses OSGi Declarative Services (`ds`)\nfor its dependency injector. If you prefer using Spring, you can set the\nparameter `--dependency-injector spring` with Blade CLI or\n`-DdependencyInjector=spring` with Maven. See the\n[Dependency Injection](/docs/7-2/frameworks/-/knowledge_base/f/dependency-injection)\nsection for more information on these options.\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `service-builder`. Suppose you want to\ncreate a Service Builder project called `tasks` with a package name of\n`com.liferay.docs.tasks` using OSGi Declarative Services. You could run the\nfollowing command to accomplish this:\n\n```bash\nblade create -t service-builder -p com.liferay.docs.tasks tasks\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service.builder \\\n    -DgroupId=com.liferay \\\n    -DartifactId=tasks \\\n    -Dpackage=com.liferay.docs.tasks \\\n    -Dversion=1.0 \\\n    -DapiPath=com.liferay.api.path \\\n    -DdependencyInjector=ds \\\n    -DliferayVersion=7.2\n```\n\nThis task creates the `tasks-api` and `tasks-service` folders. In many cases, a\nService Builder project also requires a `-web` folder to hold, for example,\nportlet classes. This should be created manually. After running the Blade\ncommand above, your project's directory structure looks like this:\n\n- `tasks`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `tasks-api`\n        - `bnd.bnd`\n        - `build.gradle`\n    - `tasks-service`\n        - `bnd.bnd`\n        - `build.gradle`\n        - `service.xml`\n    - `build.gradle`\n    - `gradlew`\n    - `settings.gradle`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nTo generate your service and API classes for the `*-api` and `*-service`\nmodules, replace the `service.xml` file in the `*-service` module. Depending on\nyour build tool, you can build your services by executing\n\n```bash\nblade gw buildService\n```\n\nor\n\n```bash\nmvn service-builder:build\n```\n\nfrom the `tasks` root directory. Note that `blade gw` only works if the Gradle\nwrapper can be detected. To ensure the availability of the Gradle wrapper, be\nsure to work in a Liferay Workspace.\n\nThe `mvn service-builder:build` command only works if you're using the\n`com.liferay.portal.tools.service.builder` plugin version 1.0.145+. Maven\nprojects using an earlier version of the Service Builder plugin should update\ntheir POM accordingly.\n\nThe generated module is functional and is deployable to a @product@ instance. To\nbuild upon the generated app, modify the project by adding logic and additional\nfiles to the folders outlined above.\n\nFor more information on Service Builder, see the\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\nsection.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/service-template.markdown",
    "content": "---\nheader-id: using-the-service-template\n---\n\n# Service Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay service as a Liferay\nmodule. To create a Liferay service via the command line using Blade CLI or\nMaven, use one of the commands with the following parameters:\n\n```bash\nblade create -t service [-p packageName] [-c className] [-s serviceName] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className]\n    -DserviceName=[serviceName] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `service`. Suppose you want to create a\nservice project called `my-service-project` with a package name of\n`com.liferay.docs.service` and a class name of `Service`. Also, you'd like to\ncreate a service of type `com.liferay.portal.kernel.events.LifecycleAction` that\nalso implements that same service. You could run the following command to\naccomplish this:\n\n```bash\nblade create -t service -p com.liferay.docs.service -c Service -s com.liferay.portal.kernel.events.LifecycleAction my-service-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-service-project \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Service \\\n    -DclassName=com.liferay.portal.kernel.events.LifecycleAction \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure would\nlook like this\n\n- `my-service-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/service`\n                    - `Service.java`\n            - `resources`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is functional and is deployable to a @product@ instance. To\nbuild upon the generated app, modify the project by adding logic and additional\nfiles to the folders outlined above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/service-wrapper-template.markdown",
    "content": "---\nheader-id: using-the-service-wrapper-template\n---\n\n# Service Wrapper Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay service wrapper as a\nLiferay module. To create a Liferay service wrapper via the command line using\nBlade CLI or Maven, use one of the commands with the following parameters:\n\n```bash\nblade create -t service-wrapper [-p packageName] [-c className] [-s serviceWrapperClass] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service.wrapper \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DserviceWrapperClass=[serviceWrapperClass] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `service-wrapper`. Suppose you want to\ncreate a service wrapper project called `service-override` with a package name of\n`com.liferay.docs.serviceoverride` and a class name of\n`UserLocalServiceOverride`. Also, you'd like to create a service of type\n`com.liferay.portal.kernel.service.ServiceWrapper` that extends the\n`com.liferay.portal.service.UserLocalServiceWrapper` class. You could run the\nfollowing command to accomplish this:\n\n```bash\nblade create -t service-wrapper -p com.liferay.docs.serviceoverride -c UserLocalServiceOverride -s com.liferay.portal.kernel.service.UserLocalServiceWrapper service-override\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.service.wrapper \\\n    -DgroupId=com.liferay \\\n    -DartifactId=service-override \\\n    -Dpackage=com.liferay.docs.serviceoverride \\\n    -Dversion=1.0 \\\n    -DclassName=UserLocalServiceOverride \\\n    -DserviceWrapperClass=com.liferay.portal.kernel.service.UserLocalServiceWrapper \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nHere, *service* means an OSGi service, not a Liferay API. Another way to say\n*service type* is to say *component type*.\n\nAfter running the Blade command above, your project's directory structure looks\nlike this:\n\n- `service-override`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/serviceoverride`\n                    - `UserLocalServiceOverride.java`\n            - `resources`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is a working application and is deployable to a @product@\ninstance. To build upon the generated app, modify the project by adding logic\nand additional files to the folders outlined above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/simulation-panel-entry-template.markdown",
    "content": "---\nheader-id: simulation-panel-entry-template\n---\n\n# Simulation Panel Entry Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay simulation panel entry as\na Liferay module. To create a simulation panel entry via the command line using\nBlade CLI or Maven, use one of the commands with the following parameters:\n\n```bash\nblade create -t simulation-panel-entry [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.simulation.panel.entry \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `simulation-panel-entry`. Suppose you\nwant to create a simulation panel entry project called\n`my-simulation-panel-entry` with a package name of\n`com.liferay.docs.application.list` and a class name of\n`SampleSimulationPanelApp`. You could run the following command to accomplish\nthis:\n\n```bash\nblade create -t simulation-panel-entry -p com.liferay.docs -c Sample my-simulation-panel-entry\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.simulation.panel.entry \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-simulation-panel-entry \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure would\nlook like this\n\n- `my-simulation-panel-entry`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/application/list`\n                    - `SampleSimulationPanelApp.java`\n            - `resources`\n                - `content`\n                    - `Language.properties`\n                - `META-INF`\n                    - `resources`\n                        - `simulation_panel.jsp`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is functional and is deployable to a @product@ instance. To\nbuild upon the generated app, modify the project by adding logic and additional\nfiles to the folders outlined above. You can visit the\n[simulation-panel-app](https://github.com/liferay/liferay-blade-samples/tree/master/gradle/apps/simulation-panel-app)\nsample project for a more expanded sample of a control menu entry. \n\n<!--Uncomment once article is available\nLikewise, see\nthe\nExtending the Simulation Menu\ntutorial for instructions on customizing a simulation panel entry project.\n-->\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/social-bookmark.markdown",
    "content": "---\nheader-id: social-bookmark-template\n---\n\n# Social Bookmark Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay social bookmark as a\nLiferay module. To create a social bookmark as a module via the command line\nusing Blade CLI or Maven, use one of the commands with the following parameters:\n\n```bash\nblade create -t social-bookmark [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.social.bookmark \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `social-bookmark`. Suppose you want to\ncreate a social bookmark project called `my-social-bookmark-project` with a\npackage name of `com.liferay.docs.socialbookmark` and a class name of\n`TestSocialBookmark`. You could run the following command to accomplish this:\n\n```bash\nblade create -t social-bookmark -p com.liferay.docs.socialbookmark -c Test my-social-bookmark-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.social.bookmark \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-social-bookmark-project \\\n    -Dpackage=com.liferay.docs.socialbookmark \\\n    -Dversion=1.0 \\\n    -DclassName=Test \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure looks\nlike this:\n\n- `my-social-bookmark-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/socialbookmark`\n                    - `social/bookmark`\n                        - `TestSocialBookmark`\n            - `resources`\n                - `content`\n                    - `Language.properties`\n                - `META-INF`\n                    - `resources`\n                        - `icons.svg`\n                        - `init.jsp`\n                        - `page.jsp`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is a working application and is deployable to a @product@\ninstance. An unmodified module generated as described above creates a social\nbookmark named *Test* that searches the current URL using Google Search.\n\n![Figure 1: Click the magnifying glass icon to search the current URL using Google Search.](../../images/social-bookmark-project-template.png)\n\nTo build upon the generated app, modify the project by adding logic and\nadditional files to the folders outlined above. For more information on\ndeveloping social bookmarks, see the\n[Social API](/docs/7-2/frameworks/-/knowledge_base/f/social-api) section of\ntutorials. For information on configuring social bookmarks for the Blogs widget,\nsee the [Displaying Blogs](/docs/7-2/user/-/knowledge_base/u/displaying-blogs)\narticle.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/spring-mvc-portlet-template.markdown",
    "content": "---\nheader-id: spring-mvc-portlet-template\n---\n\n# Spring MVC Portlet Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay Spring MVC portlet\napplication as a WAR. To create a Liferay Spring MVC portlet via the command\nline using Blade CLI or Maven, use one of the commands with the following\nparameters:\n\n```bash\nblade create -t spring-mvc-portlet [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.spring.mvc.portlet \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `spring-mvc-portlet`. Suppose you want\nto create a Spring MVC portlet project called `my-spring-mvc-portlet-project`\nwith a package name of `com.liferay.docs.springmvcportlet` and a class name of\n`MySpringMvcPortlet`. Also, you'd like to create a Spring-annotated portlet\nclass named `MySpringMvcPortletViewController`.\n\n```bash\nblade create -t spring-mvc-portlet -p com.liferay.docs.springmvcportlet -c MySpringMvcPortlet my-spring-mvc-portlet-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.spring.mvc.portlet \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-spring-mvc-portlet-project \\\n    -Dpackage=com.liferay.docs.springmvcportlet \\\n    -Dversion=1.0 \\\n    -DclassName=MySpringMvcPortlet \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure looks\nlike this:\n\n- `my-spring-mvc-portlet-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/springmvcportlet/portlet`\n                    - `MySpringMvcPortletViewController`\n            - `resources`\n                - `content`\n                    - `Language.properties`\n            - `webapp`\n                - `css`\n                    - `main.scss`\n                - `WEB-INF`\n                    - `jsp`\n                        - `init.jsp`\n                        - `view.jsp`\n                    - `spring-context`\n                        - `portlet`\n                            - `my-spring-mvc-portlet-project.xml`\n                        - `portlet-application-context.xml`\n                    - `tld`\n                        - `liferay-portlet.tld`\n                        - `liferay-portlet-ext.tld`\n                        - `liferay-security.tld`\n                        - `liferay-theme.tld`\n                        - `liferay-ui.tld`\n                        - `liferay-util.tld`\n                    - `liferay-display.xml`\n                    - `liferay-plugin-package.properties`\n                    - `liferay-portlet.xml`\n                    - `portlet.xml`\n                    - `web.xml`\n                - `icon.png`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated WAR is a working application and is deployable to a @product@\ninstance. To build upon the generated app, modify the project by adding logic\nand additional files to the folders outlined above. You can visit the\n[springmvc-portlet](/docs/7-1/reference/-/knowledge_base/r/spring-mvc-portlet)\nsample project for a more expanded sample of a Spring MVC portlet.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/template-context-contributor-template.markdown",
    "content": "---\nheader-id: template-context-contributor-template\n---\n\n# Template Context Contributor Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay template context\ncontributor as a Liferay module. To create a template context contributor via\nthe command line using Blade CLI or Maven, use one of the commands with the\nfollowing parameters:\n\n```bash\nblade create -t template-context-contributor [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.template.context.contributor \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `template-context-contributor`. Suppose\nyou want to create a template context contributor project called\n`my-template-context-contributor` with a package name of\n`com.liferay.docs.theme.contributor` and a class name of\n`SampleTemplateContextContributor`. You could run the following command to\naccomplish this:\n\n```bash\nblade create -t template-context-contributor -p com.liferay.docs -c Sample my-template-context-contributor\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.template.context.contributor \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-template-context-contributor \\\n    -Dpackage=com.liferay.docs \\\n    -Dversion=1.0 \\\n    -DclassName=Sample \\\n    -Dauthor=Joe Bloggs \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's directory structure would\nlook like this\n\n- `my-template-context-contributor`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/context/contributor`\n                    - `SampleTemplateContextContributor.java`\n            - `resources`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is functional and is deployable to a @product@ instance. To\nbuild upon the generated app, modify the project by adding logic and additional\nfiles to the folders outlined above. You can visit the\n[template-context-contributor](https://github.com/liferay/liferay-blade-samples/tree/master/gradle/themes/template-context-contributor)\nsample project for a more expanded sample of a template context contributor.\nLikewise, see the\n[Context Contributors](/docs/7-2/frameworks/-/knowledge_base/f/injecting-additional-context-variables-and-functionality-into-your-theme-templates)\ntutorial for instructions on customizing a template context contributor project.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/theme-contributor-template.markdown",
    "content": "---\nheader-id: theme-contributor-template\n---\n\n# Theme Contributor Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay theme contributor as a\nLiferay module. To create a theme contributor via the command line using Blade\nCLI or Maven, use one of the commands with the following parameters:\n\n```bash\nblade create -t theme-contributor [--contributorType contributorType] [-p packageName] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.theme.contributor \\\n    -DartifactId=[projectName] \\\n    -Dpackage=[packageName] \\\n    -DcontributorType=[contributorType] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `theme-contributor`. Suppose you want\nto create a theme contributor project called `my-theme-contributor` with a\npackage name of `com.liferay.docs.theme.contributor` and a contributor type of\n`my-contributor`. You could run the following command to accomplish this:\n\n```bash\nblade create -t theme-contributor --contributor-type my-contributor -p com.liferay.docs.theme.contributor my-theme-contributor\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.theme.contributor \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-theme-contributor \\\n    -Dpackage=com.liferay.docs.theme.contributor \\\n    -Dversion=1.0 \\\n    -DcontributorType=my-contributor \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's folder structure would\nlook like this: \n\n- `my-theme-contributor`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/theme/contributor`\n            - `resources`\n                - `META-INF`\n                    - `resources`\n                    \t- `css`\n                    \t\t- `my-contributor`\n                    \t\t\t- `_body.scss`\n                    \t\t\t- `_control_menu.scss`\n                    \t\t\t- `_product_menu.scss`\n                    \t\t\t- `_simulation_panel.scss`\n                    \t\t- `my-contributor.scss`\n                    \t- `js`\n                    \t\t- `my-contributor.js`\n    - `bnd.bnd`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated module is functional and is deployable to a @product@ instance. To\nbuild upon the generated app, modify the project by adding logic and additional\nfiles to the folders outlined above. You can visit the\n[Blade Theme Contributor](/docs/7-2/reference/-/knowledge_base/r/theme-contributor)\nsample project for a more expanded sample of a theme contributor. Likewise, see\nthe\n[Theme Contributors](/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site)\ntutorial for instructions on customizing a theme contributor project.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/theme-template.markdown",
    "content": "---\nheader-id: theme-template\n---\n\n# Theme Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay theme as a WAR project. To\ncreate a Liferay theme via the command line using Blade CLI or Maven, use one of\nthe commands with the following parameters:\n\n```bash\nblade create -t theme projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.theme \\\n    -DartifactId=[projectName] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `theme`. Suppose you want to create a\ntheme project called `my-theme-project` as a WAR file. You could run the\nfollowing command to accomplish this:\n\n```bash\nblade create -t theme my-theme-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.theme \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-theme-project \\\n    -Dversion=1.0 \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's folder structure looks\nlike this: \n\n- `my-theme-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `resources`\n                - `resources-importer`\n                    - `sitemap.json`\n            - `webapp`\n                - `css`\n                    - `_custom.scss`\n                - `WEB-INF`\n                    - `liferay-plugin-package.properties`\n                    - `web.xml`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated theme is functional and is deployable to a @product@ instance. To\nbuild upon the generated project, modify the project by adding logic and\nadditional files to the folders outlined above. You can visit the\n[simple-theme](/docs/7-1/reference/-/knowledge_base/r/theme) project for a more\nexpanded sample of a theme. Likewise, see the\n[Themes](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction) section\nfor more information on creating themes.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/war-core-ext.markdown",
    "content": "---\nheader-id: war-core-ext\n---\n\n# WAR Core Ext\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay WAR core Ext project. To\ncreate a WAR core Ext project via the command line using Blade CLI or Maven, use\none of the commands with the following parameters:\n\n```bash\nblade create -t war-core-ext projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.war.core.ext \\\n    -DartifactId=[projectName] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `war-core-ext`. Suppose you want to\ncreate a WAR core Ext project called `my-war-core-ext-project`. You could run\nthe following command to accomplish this:\n\n```bash\nblade create -t war-core-ext my-war-core-ext-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.war.core-ext \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-war-core-ext-project \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's folder structure looks\nlike this: \n\n- `my-war-core-ext-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `extImpl`\n            - `java`\n            - `resources`\n                - `META-INF`\n                    - `ext-model-hints.xml`\n                    - `ext-spring.xml`\n                    - `portal-log4j-ext.xml`\n        - `extKernel`\n            - `java`\n            - `resources`\n                - `META-INF`\n        - `extUtilBridges`\n            - `java`\n            - `resources`\n                - `META-INF`\n        - `extUtilJava`\n            - `java`\n            - `resources`\n                - `META-INF`\n        - `extUtilTaglib`\n            - `java`\n            - `resources`\n                - `META-INF`\n        - `main`\n            - `webapp`\n                - `WEB-INF`\n                    - `ext-web`\n                        - `docroot`\n                            - `WEB-INF`\n                                - `liferay-portlet-ext.xml`\n                                - `portlet-ext.xml`\n                                - `struts-config-ext.xml`\n                                - `tiles-defs-ext.xml`\n                                - `web.xml`\n                    - `liferay-plugin-package.properties`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\n| **Note:** If you generate a WAR Ext project using Gradle outside of Liferay\n| Workspace, you must set the `app.server.parent.dir` property in the project's\n| `gradle.properties`. The app server location is required for this project to\n| compile.\n\nThe generated WAR Ext project is functional and is deployable to a @product@ instance.\nTo build upon the generated project, modify the project by adding logic and\nadditional files to the folders outlined above. Deploying WAR Ext projects is\nonly supported for limited use cases; it is recommended to leverage provided\nextension points offered in @product@. You can visit the\n[Customization with Ext](/docs/7-2/customization/-/knowledge_base/c/customization-with-ext)\nsection for info on how to do this.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/war-hook-template.markdown",
    "content": "---\nheader-id: war-hook-template\n---\n\n# WAR Hook Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay WAR hook project. To\ncreate a Liferay WAR hook via the command line using Blade CLI or Maven, use one\nof the commands with the following parameters:\n\n```bash\nblade create -t war-hook [-p packageName] [-c className] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.war.hook \\\n    -DartifactId=[projectName]\n    -Dpackage=[packageName] \\\n    -DclassName=[className] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `war-hook`. Suppose you want to create\na WAR hook project called `my-war-hook-project` with a package name of\n`com.liferay.docs` and a class name of `MyWarHook`. You could run the following\ncommand to accomplish this:\n\n```bash\nblade create -t war-hook -p com.liferay.docs -c MyWarHook my-war-hook-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.war.hook \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-war-hook-project \\\n    -Dpackage=com.liferay.docs \\\n    -DclassName=MyWarHook \\\n    -Dversion=1.0 \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's folder structure looks\nlike this: \n\n- `my-war-hook-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs`\n                    - `MyWarHookLoginPostAction`\n                    - `MyWarHookStartupAction`\n            - `resources`\n                - `portal.properties`\n            - `webapp`\n                - `WEB-INF`\n                    - `liferay-hook.xml`\n                    - `liferay-plugin-package.properties`\n                    - `web.xml`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated WAR hook is functional and is deployable to a @product@ instance.\nTo build upon the generated project, modify the project by adding logic and\nadditional files to the folders outlined above. Deploying WAR hooks is\nsupported for @product-ver@, however, it is recommended to optimize your WAR\nhooks to fragments or other applicable module projects. You can visit the\n[Liferay Customization](/docs/7-2/customization/-/knowledge_base/c/liferay-customization) section for\ninfo on how to do this for many project types. See the\n[Customizing Liferay Portal](/docs/6-2/tutorials/-/knowledge_base/t/customizing-liferay-portal)\nsection for more information on WAR hooks.\n"
  },
  {
    "path": "en/developer/reference/articles/02-project-templates/war-mvc-portlet-template.markdown",
    "content": "---\nheader-id: war-mvc-portlet-template\n---\n\n# WAR MVC Portlet Template\n\n[TOC levels=1-4]\n\nIn this article, you'll learn how to create a Liferay MVC portlet project as a\nWAR file. To create a Liferay MVC portlet project as a WAR via the command line\nusing Blade CLI or Maven, use one of the commands with the following parameters:\n\n```bash\nblade create -t war-mvc-portlet [-p packageName] projectName\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.war.mvc.portlet \\\n    -DartifactId=[projectName]\n    -Dpackage=[packageName] \\\n    -DliferayVersion=7.2\n```\n\nYou can also insert the `-b maven` parameter in the Blade command to generate a\nMaven project using Blade CLI.\n\nThe template for this kind of project is `war-mvc-portlet`. Suppose you want to\ncreate a WAR MVC portlet project called `my-war-mvc-portlet-project` with a\npackage name of `com.liferay.docs.war.mvc` and a class name of\n`MyWarMvcPortlet`. You could run the following command to accomplish this:\n\n```bash\nblade create -t war-mvc-portlet -p com.liferay.docs.war.mvc my-war-mvc-portlet-project\n```\n\nor\n\n```bash\nmvn archetype:generate \\\n    -DarchetypeGroupId=com.liferay \\\n    -DarchetypeArtifactId=com.liferay.project.templates.war.mvc.portlet \\\n    -DgroupId=com.liferay \\\n    -DartifactId=my-war-mvc-portlet-project \\\n    -Dpackage=com.liferay.docs.war.mvc \\\n    -Dversion=1.0 \\\n    -DliferayVersion=7.2\n```\n\nAfter running the Blade command above, your project's folder structure looks\nlike this: \n\n- `my-war-mvc-portlet-project`\n    - `gradle`\n        - `wrapper`\n            - `gradle-wrapper.jar`\n            - `gradle-wrapper.properties`\n    - `src`\n        - `main`\n            - `java`\n                - `com/liferay/docs/war/mvc`\n            - `resources`\n                - `content`\n                    - `Language.properties`\n            - `webapp`\n                - `css`\n                    - `main.scss`\n                - `WEB-INF`\n                    - `tld`\n                        - `liferay-portlet.tld`\n                        - `liferay-portlet-ext.tld`\n                        - `liferay-security.tld`\n                        - `liferay-theme.tld`\n                        - `liferay-ui.tld`\n                        - `liferay-util.tld`\n                    - `liferay-display.xml`\n                    - `liferay-plugin-package.properties`\n                    - `liferay-portlet.xml`\n                    - `portlet.xml`\n                    - `web.xml`\n                - `init.jsp`\n                - `view.jsp`\n    - `build.gradle`\n    - `gradlew`\n\nThe Maven-generated project includes a `pom.xml` file and does not include the\nGradle-specific files, but otherwise, appears exactly the same.\n\nThe generated WAR MVC portlet is functional and is deployable to a @product@ instance.\nTo build upon the generated project, modify the project by adding logic and\nadditional files to the folders outlined above. Deploying WAR MVC portlets is\nsupported for @product-ver@, however, it is recommended to optimize your WAR\nportlet to a module project, if possible. You can visit the\n[From Liferay Portal 6 to 7](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver)\nsection for info on how to do this.\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/01-intro.markdown",
    "content": "---\nheader-id: sample-projects\n---\n\n# Sample Projects\n\n[TOC levels=1-4]\n\n| **Note:** This section of articles does not provide documentation for *all*\n| sample projects residing in the `liferay-blade-samples` repo. The documentation\n| for these samples is in progress and will grow over time.\n\nLiferay provides sample projects that target different integration points in\n@product@. These projects reside in the\n[liferay-blade-samples](https://github.com/liferay/liferay-blade-samples)\nGithub repository and can be easily copy/pasted to your local environment. You\ncan also generated them using\n[Blade CLI](/docs/7-2/reference/-/knowledge_base/r/generating-project-samples-with-blade-cli).\n\nThe sample projects are grouped into three different parent folders based on the\nbuild tools used to generate them:\n\n- `gradle`\n- `liferay-workspace`\n- `maven`\n\nThe provided sample projects are organized by their development toolchains to\ncater to a variety of developers. Each folder offers the same set of sample\nLiferay projects. Their only difference is that the build files are specific to\ntheir toolchain. For example, the `gradle` folder contains projects using\nstandard OSS Gradle plugins that can be added to any Gradle composite build. The\nsame concept also applies to the `liferay-workspace` and `maven` projects.\n\n| **Note:** The Liferay Workspace folder stores WAR-type samples in a separate\n| folder named\n| [wars](https://github.com/liferay/liferay-blade-samples/tree/7.1/liferay-workspace/wars).\n| The Gradle and Maven tool folders mix WAR samples with the other sample types\n| (apps, extensions, etc.).\n\nThe `gradle` folder also uses the Liferay Gradle plugin (e.g.,\n`com.liferay.plugin`) which encompasses additional functionality for various\ntypes of Liferay projects. The Liferay Gradle plugin is recommended for Gradle\nusers developing for @product@. \n\nSome samples also come configured with logging to help you fully understand\nwhat the sample is accomplishing behind the scenes. For example, OSGi module\nlogging is implemented for several samples (e.g.,\n[action-command-portlet](https://github.com/liferay/liferay-blade-samples/tree/7.1/gradle/apps/action-command-portlet),\n[document-action](/docs/7-2/reference/-/knowledge_base/r/document-action),\n[service-builder/jdbc](/docs/7-2/reference/-/knowledge_base/r/service-builder-application-using-external-database-via-jdbc),\netc.), which lets OSGi modules supply their own logging configuration defaults\nwithout external configuration. See the\n[Adjusting Module Logging](/docs/7-2/appdev/-/knowledge_base/a/adjusting-module-logging)\narticle for more information.\n\nFor a list of sample template projects available, visit the\n[Liferay extension points](https://github.com/liferay/liferay-blade-samples#liferay-extension-points-and-template-projects)\nsub-section in the Liferay Blade Samples repository. This list is not\ncomprehensive. A subset of missing extension point samples can be found in the\n[Liferay extension points without template projects](https://github.com/liferay/liferay-blade-samples#liferay-extension-points-without-template-projects)\nsub-section. Visit the repo's\n[Contribution Guidelines](https://github.com/liferay/liferay-blade-samples#contribution-guidelines)\nsection for details on contributing to this repository.\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/02-apps/01-intro.markdown",
    "content": "---\nheader-id: apps\n---\n\n# Apps\n\n[TOC levels=1-4]\n\nThis section focuses on Liferay sample applications. You can view these sample\napps by visiting the `apps` folder corresponding to your preferred build tool:\n\n- [Gradle sample apps](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps)\n- [Liferay Workspace sample apps](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps)\n- [Maven sample apps](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/apps)\n\nVisit a particular sample page to learn more!\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/02-apps/greedy-policy-option-portlet.markdown",
    "content": "---\nheader-id: greedy-policy-option-portlet\n---\n\n# Greedy Policy Option Application\n\n[TOC levels=1-4]\n\nThe Greedy Policy Option sample provides two portlets that can be added to a\n@product@ page: Greedy Portlet and Reluctant Portlet.\n\n![Figure 1: The Greedy Policy Option app provides two portlets that only print text. You'll dive deeper later to discover their interesting capabilities involving services.](../../../images/greedy-policy-app.png)\n\nThese two portlets do not provide anything useful out-of-the-box. They are,\nhowever, very effective at demonstrating the ability to reference services using\ngreedy and reluctant policy options. You'll learn how to do this later.\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample provides two modules referencing services using greedy and reluctant\npolicy options.\n\n- `service-reference`: Provides an OSGi service interface called\n  `SomeService`, a default implementation of it, and portlets that refer to\n  service instances. One portlet refers to new higher ranked instances of the\n  service automatically. The other portlet is reluctant to use new higher\n  ranked instances and continues to use its bound service. The reluctant\n  portlet can, however, be configured dynamically to use other service\n  instances.\n\n-  `higher-ranked-service`: Has a higher ranked `SomeService` implementation.\n\nHere are each module's file structures:\n\n- `service-reference/`\n    - `bnd.bnd`\n    - `configs/`\n        - `com.liferay.blade.reluctant.vs.greedy.portlet.portlet.ReluctantPortlet.config` &rarr; `ReluctantPortlet` configuration file\n    - `src/main/java/com/liferay/blade/reluctant/vs/greedy/portlet/`\n        - `api/`\n            - `SomeService.java` &rarr; Service interface\n        - `constants/`\n            - `ReluctantPortletVsGreedyPortletKeys.java` &rarr; Portlet constants\n        - `portlet/`\n            - `DefaultSomeService.java` &rarr; Zero ranked service implementation\n            - `GreedyPortlet.java` &rarr; Refers to `SomeService` using a greedy service policy option\n            - `ReluctantPortletPortlet.java` &rarr; Refers to `SomeService` using a reluctant service policy option by default.\n\n- `higher-ranked-service/`\n    - `bnd.bnd`\n    - `src/main/java/com/liferay/blade/reluctant/vs/greedy/svc/HigherRankedService.java` &rarr; Service implementation with service ranking value of `100`\n\n## How does this sample leverage the API(s) and/or code component?\n\nHere are the things you can learn using the sample modules:\n\n1.  [Binding a component's service reference to the highest ranked service instance that's available initially.](#binding-a-newly-deployed-components-service-reference-to-the-highest-ranking-service-instance-thats-available-initially)\n\n2.  [Deploying a module with a higher ranked service instance for binding to greedy references immediately.](#deploying-a-module-with-a-higher-ranked-service-instance-for-binding-to-greedy-references-immediately)\n\n3.  [Configuring a component to reference a different service instance dynamically.](#configuring-a-component-to-reference-a-different-service-instance-dynamically)\n\nLet's walk through the demonstration.\n\n### Binding a newly deployed component's service reference to the highest ranking service instance that's available initially\n\nOn deploying a component that references a service, it binds to the highest\nranking service instance that matches its target filter (if specified).\n\nThe portlet classes refer to instances of interface `SomeService`. The\n`doSomething` method returns a `String`.\n\n```java\npublic interface SomeService {\n\n    public String doSomething();\n\n}\n```\n\nClass `DefaultSomeService` implements `SomeService`. Its `doSomething` method\nreturns the `String` \"I am Default!\".\n\n```java\n@Component\npublic class DefaultSomeService implements SomeService {\n\n    @Override\n    public String doSomething() {\n        return \"I am Default!\";\n    }\n\n}\n```\n\nWhen module's portlets refer to `DefaultSomeService`, they display the `String`\n\"I am Default!\".\n\nThe `ReluctantPortlet` class's `SomeService` reference's policy option is the\ndefault: static and reluctant. This policy option keeps the reference bound to\nits current service instance unless that instance stops or the reference is\nreconfigured to refer to a different service instance.\n\n```java\n@Component(\n   immediate = true,\n   property = {\n       \"com.liferay.portlet.display-category=category.sample\",\n       \"com.liferay.portlet.instanceable=true\",\n       \"javax.portlet.display-name=Reluctant Portlet\",\n       \"javax.portlet.init-param.template-path=/\",\n       \"javax.portlet.init-param.view-template=/view.jsp\",\n       \"javax.portlet.name=\" + ReluctantVsGreedyPortletKeys.Reluctant,\n       \"javax.portlet.resource-bundle=content.Language\",\n       \"javax.portlet.security-role-ref=power-user,user\"\n    },\n    service = Portlet.class\n  )\npublic class ReluctantPortlet extends MVCPortlet {\n\n   @Override\n   public void doView(\n           RenderRequest renderRequest, RenderResponse renderResponse)\n       throws IOException, PortletException {\n\n       renderRequest.setAttribute(\"doSomething\", _someService.doSomething());\n\n       super.doView(renderRequest, renderResponse);\n   }\n\n    @Reference\n    private SomeService _someService;\n\n}\n```\n\nThe `ReluctantPortlet`'s method `doView` sets render request attribute\n`doSomething` to the value returned from the `SomeService` instance's\n`doSomething` method (e.g., `DefaultService` returns \"I am default!\").\n\nThe `GreedyPortlet` class is similar to `ReluctantPortlet`, except its\n`SomeService` reference's policy option is static and greedy (i.e.,\n`ReferencePolicyOption.GREEDY`).\n\n```java\npublic class GreedyPortlet extends MVCPortlet {\n\n  @Override\n\tpublic void doView(\n  \t\tRenderRequest renderRequest, RenderResponse renderResponse)\n  \tthrows IOException, PortletException {\n\n  \trenderRequest.setAttribute(\"doSomething\", _someService.doSomething());\n\n  \tsuper.doView(renderRequest, renderResponse);\n\t}\n\n\t@Reference (policyOption = ReferencePolicyOption.GREEDY)\n\tprivate SomeService _someService;\n\n}\n```\n\nThe greedy policy option lets the component switch to using a higher ranked\n`SomeService` instance if one becomes active in the system. The section\n[*Deploying a module with a higher ranked service instance for binding to greedy references immediately*](#deploying-a-module-with-a-higher-ranked-service-instance-for-binding-to-greedy-references-immediately)\ndemonstrates this portlet switching to a higher ranked service.\n\nIt's time to see this module's portlets and service in action.\n\n1.  Stop module `higher-ranked-service` if it's active.\n2.  Deploy the `service-reference` module.\n3.  Add the *Reluctant Portlet* from the *Add* &rarr; *Widgets* &rarr;\n    *Sample* category to a site page.\n\n    The portlet displays the message \"SomeService says I am Default!\"--whose\n    latter part comes from the render request attribute set by the\n    `DefaultService` instance.\n\n    ![Figure 2: *Reluctant Portlet* displays the message \"SomeService says I am Default!\"](../../../images/reluctant-portlet-using-default.png)\n\n4.  Add the *Greedy Portlet* from the *Add* &rarr; *Widgets* &rarr;\n    *Sample* category to a site page.\n\n    The portlet displays the message \"SomeService says I am better, use me!\".\n    Both portlets are referencing a `DefaultService` instance.\n\n    ![Figure 3: *Greedy Portlet* displays the message \"SomeService says I am better, use me!\"](../../../images/greedy-portlet-using-default.png)\n\nSince `DefaultService` is the only active `SomeService` instance in the system,\nthe portlets refer to it for their `SomeService` fields.\n\nThe `DefaultService` and portlets *Reluctant Portlet* and *Greedy Portlet* are\nactive. Let's activate a higher ranked `SomeService` instance and see how the\nportlets react to it.\n\n### Deploying a module with a higher ranked service instance for binding to greedy references immediately\n\nModule `higher-ranked-service` provides a `SomeService` implementation called\n`HigherRankedService`. `HigherRankedService`'s service ranking is `100`--that's\n`100` more than `DefaultService`'s ranking `0`. Its `doSomething` method returns\nthe `String` \"I am better, use me!\".\n\n1.  Deploy the `higher-ranked-service` module.\n2.  Refresh your page that has the portlets *Reluctant Portlet* and *Greedy Portlet*.\n\n*Reluctant Portlet* continues displaying message \"SomeService says I am better,\nuse me!\". It's \"reluctant\" to unbind from the `DefaultService` instance and\nbind to the newly activated `HigherRankedService` service.\n\n*Greedy Portlet* displays a new message \"SomeService says I am better, use me!\".\nThe part of the message \"I am better, use me!\" comes from the\n`HigherRankedService` instance to which it refers.\n\n![Figure 4: The *Greedy Portlet* is using a `HigherRankedService` instance](../../../images/greedy-portlet-using-higher-ranked-service.png)\n\nNext, learn how to bind the *Reluctant Portlet* to a `HigherRankedService`\ninstance.\n\n### Configuring a component to reference a different service instance dynamically\n\nThe *Reluctant Portlet* is currently bound to a `DefaultService` instance. It's\n\"reluctant\" to unbind from it and bind to a different service. OSGi\nConfiguration Administration lets you reconfigure service references to filter\non and bind to different service instances.\n\nThe `service-reference` module's configuration files and\n`com.liferay.blade.reluctant.vs.greedy.portlet.portlet.ReluctantPortlet.config`\nand `com.liferay.blade.reluctant.vs.greedy.portlet.portlet.ReluctantPortlet.cfg`\nconfigure the `ReluctantPortlet` component to use a `HigherRankedService`\ninstance.\n\n```\n_someService.target=(component.name=com.liferay.blade.reluctant.vs.greedy.service.HigherRankedService)\n```\n\nThe service configuration filters on a service whose `component.name` is\n`com.liferay.blade.reluctant.vs.greedy.service.HigherRankedService`.\n\nNote: For deploying to @product-ver@, use file with suffix `.config`. For\nearlier versions (i.e., Liferay DXP 7.0 Fix Packs earlier than Fix Pack 8 and\nLiferay CE Portal 7.0 GA3 or earlier), use the file with suffix `.cfg`.\n\nHere are the steps to reconfigure `ReluctantPortlet` to use\n`HigherRankedService`:\n\n1.  Copy the configuration file to `[Liferay-Home]/osgi/configs`.\n2.  Refresh your browser.\n\n*Reluctant Portlet* displays a new message \"SomeService says I am better, use\nme!\".\n\n![Figure 5: *Reluctant Portlet* is using the `HigherRankedService` instance instead of a `DefaultService` instance.](../../../images/reluctant-portlet-using-higher-ranked-service.png)\n\n*Reluctant Portlet* is using `HigherRankedService` instance instead of a\n`DefaultService` instance. You've configured *Reluctant Portlet* to use a\n`HigherRankedService` instance!\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/greedy-policy-option-portlet)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps/greedy-policy-option-portlet)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/apps/greedy-policy-option-portlet)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/02-apps/kotlin-portlet.markdown",
    "content": "---\nheader-id: kotlin-portlet\n---\n\n# Kotlin Portlet\n\n[TOC levels=1-4]\n\nThe Kotlin Portlet sample provides an input form that accepts a name. Once\nsubmitting a name, the portlet renders a greeting message.\n\n![Figure 1: After saving the inputted name, it's displayed as a greeting on the portlet page.](../../../images/kotlin-portlet.png)\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample highlights the use of the [Kotlin](https://kotlinlang.org/)\nprogramming language in conjunction with Liferay's MVC framework.\nSpecifically, this sample leverages the\n[MVCActionCommand](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCActionCommand.html)\ninterface.\n\n## How does this sample leverage the API(s) and/or code component?\n\nThis sample uses the MVC Action Command's `processAction(...)` method to process \nthe inputted text (i.e., name). The text is set as an attribute in the \n`KotlinGreeterActionCommandKt.kt` class using an `ActionRequest` and then is \nretrieved in the JSP using a `RenderRequest`. \n\n<!-- Add back link for 'MVC Action Command' once mvc-action-command article is available-->\n\n## Where Is This Sample?\n\nThis sample is built with the following build tools:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/kotlin-portlet)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps/kotlin-portlet)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/02-apps/service-builder-samples/01-intro.markdown",
    "content": "---\nheader-id: service-builder-samples\n---\n\n# Service Builder Samples\n\n[TOC levels=1-4]\n\nThis section focuses on Liferay Service Builder sample projects built with\nvarious build tools. You can view these samples by visiting the\n`apps/service-builder` folder corresponding to your preferred build tool:\n\n- [Gradle Service Builder sample apps](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/service-builder)\n- [Liferay Service Builder Workspace sample apps](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps/service-builder)\n- [Maven Service Builder sample apps](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/apps/service-builder)\n\nThe following Service Builder samples are documented:\n\n- [Service Builder application demonstrating Actionable Dynamic Query](/docs/7-2/reference/-/knowledge_base/r/service-builder-application-demonstrating-actionable-dynamic-query)\n- [Service Builder application with JDBC connection](/docs/7-2/reference/-/knowledge_base/r/service-builder-application-using-external-database-via-jdbc)\n- [Service Builder application with JNDI connection](/docs/7-2/reference/-/knowledge_base/r/service-builder-application-using-external-database-via-jndi)\n\nVisit a particular sample page to learn more!\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/02-apps/service-builder-samples/service-builder-adq.markdown",
    "content": "---\nheader-id: service-builder-application-demonstrating-actionable-dynamic-query\n---\n\n# Service Builder Application Demonstrating Actionable Dynamic Query\n\n[TOC levels=1-4]\n\nThis sample is similar to the\n[`basic` Service Builder sample](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/service-builder/basic),\nwhich lets you perform CRUD (create, read, update, delete) operations on service\nbuilder entities. The distinctive feature of the Service Builder Actionable\nDynamic Query (ADQ) sample is that it also lets you perform a mass update on all\nexisting service builder entities.\n\n![Figure 1: This sample provides options to add entities and perform a mass update.](../../../../images/adq-sample.png)\n\n<!-- TODO: Update image above with actual content when sample works correctly.\n-->\n\nTo see the ADQ Service Builder sample in action, complete the following steps:\n\n1.  Add the sample to a page by navigating to *Add*\n    (![Add](../../../../images/icon-add.png)) &rarr; *Widgets* &rarr; *Sample*\n    and dragging it to the page.\n\n2.  Select the app's *Add* button and add an entity. Do this several times to\n    create multiple entities.\n\n3.  Click the *Mass Update* button and click *Save* to invoke the update.\n\n    <!-- Add image back when sample works:\n    [Figure 2: Clicking the *Save* button executes the mass update.](../../../../images/adq-sample-mass-update.png)\n    -->\n\n    After invoking the update, each entity's `field3` value (whose value is less\n    than 100) is incremented.\n\nYou've leveraged the actionable dynamic query API in your sample!\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample demonstrates @product@'s actionable dynamic query API. Specifically,\nit demonstrates how to create an ADQ, add criteria to an ADQ, specify an action\nfor the ADQ, and execute the ADQ.\n\n## How does this sample leverage the API(s) and/or code component?\n\nAn action request is sent to the `JSPPortlet` with a `cmd` request parameter.\nWhen the `JSPPortlet`'s `processAction` method processes the request, the value\nof the `cmd` parameter is parsed and then the portlet's `massUpdate` method is\ninvoked. The `massUpdate` method, in turn, invokes the `massUpdate` method\ndefined in the `adq-service` module's `BarLocalServiceImpl`. This is where the\nsample leverages the actionable dynamic query API:\n\n```java\npublic void massUpdate() {\n    ActionableDynamicQuery adq = getActionableDynamicQuery();\n\n    adq.setAddCriteriaMethod(\n        new ActionableDynamicQuery.AddCriteriaMethod() {\n\n            @Override\n            public void addCriteria(DynamicQuery dynamicQuery) {\n                dynamicQuery.add(RestrictionsFactoryUtil.lt(\"field3\", 100));\n            }\n\n        });\n\n    adq.setPerformActionMethod(\n        new ActionableDynamicQuery.PerformActionMethod<Bar>() {\n\n            @Override\n            public void performAction(Bar bar) {\n                int field3 = bar.getField3();\n\n                field3++;\n                bar.setField3(field3);\n\n                updateBar(bar);\n            }\n\n        });\n\n    try {\n        adq.performActions();\n    }\n    catch (Exception e) {\n        e.printStackTrace();\n    }\n}\n```\n\nFor more information on the actionable dynamic query API, visit its dedicated\n[tutorial](/docs/7-0/tutorials/-/knowledge_base/t/dynamic-query#actionable-dynamic-queries).\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/02-apps/service-builder-samples/service-builder-jdbc.markdown",
    "content": "---\nheader-id: service-builder-application-using-external-database-via-jdbc\n---\n\n# Service Builder Application Using External Database via JDBC\n\n[TOC levels=1-4]\n\nThis sample demonstrates how to connect a Liferay Service Builder application to\nan external database via a JDBC connection. Here, an external database means any\ndatabase other than @product@'s database. For this sample to work correctly, you\nmust prepare such an external database and configure @product@ to use it. Follow\nthe steps below to make the required preparations before deploying the\napplication.\n\n1.  Create the external database to which your Service Builder application will\n    connect. For example, create a MariaDB database called `external`. Add a\n    table to this database called `country` with a `BIGINT` column called `Id`\n    and a `VARCHAR(255)` column called `Name`. Add at least one record to this\n    table. Here are the MariaDB commands to accomplish this:\n\n    ```bash\n    create database external character set utf8;\n\n    use external;\n\n    create table country(id bigint not null primary key, name varchar(255));\n\n    insert into country(id, name) values(1, 'Australia');\n    ```\n\n    Make sure that your database commands were successful: Running `select *\n    from country;` should return the record you added.\n\n2.  Create a `portal-ext.properties` file in your @product@ instance's\n    `[LIFERAY_HOME]` folder (this folder should be marked by the presence of a\n    `.liferay-home` file). In your `portal-ext.properties` file, define the\n    details of your JDBC data source connection:\n\n    ```properties\n    jdbc.ext.driverClassName=org.mariadb.jdbc.Driver\n    jdbc.ext.password=userpassword\n    jdbc.ext.url=jdbc:mariadb://localhost/external?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n    jdbc.ext.username=yourusername\n    ```\n\n    Note that @product@'s primary data source is specified by the `jdbc.default`\n    prefix. These details are often specified in a\n    `portal-setup-wizard.properties` file. Here, we've chosen to use the\n    `jdbc.ext` prefix for our alternate data source.\n\n3.  Create a `com.liferay.blade.samples.jdbcservicebuilder.service-log4j-ext.xml`\n    in your Liferay instance's `[LIFERAY_HOME]/osgi/log4` folder. Create this\n    folder if it doesn't yet exist. Add this content to the XML file that you\n    created:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">\n\n    <log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\">\n        <category name=\"com.liferay.blade.samples.jdbcservicebuilder.service.impl\">\n            <priority value=\"INFO\" />\n        </category>\n    </log4j:configuration>\n    ```\n\n    This XML file defines the log level for the classes in the\n    `com.liferay.blade.samples.jdbcservicebuilder.service.impl` package. The\n    `com.liferay.blade.samples.jdbcservicebuilder.service.impl.CountryLocalServiceImpl`\n    is the class that will produce log messages when the sample portlet is\n    viewed.\n\nNow your sample is ready for deployment! Make sure to build and deploy each of\nthe three modules that comprise the sample application:\n\n- `jdbc-api`\n- `jdbc-service`\n- `jdbc-web`\n\nAfter these modules have been deployed, add the `-web` portlet to a @product@\npage.\n\n![Figure 1: This sample prints out the values previously inputted into the database.](../../../../images/jdbc-sb-sample.png)\n\nA sample table is printed in the portlet's view, representing the info inputted\ninto the database.\n\n## What API(s) and/or code components does this sample highlight?\n\nThe sample configures the data source using Spring Beans and demonstrates two\nways to access data from an external database defined by a JDBC connection:\n\n- extract data directly from the raw data source by explicitly specifying a SQL\n  query.\n- read data using the helper methods that Service Builder generates in your\n  application's persistence layer.\n\n## How does this sample leverage the API(s) and/or code component?\n\nOnce you've added the `-web` portlet to a page, the\n`CountryLocalService.useJDBC` method is invoked. This method accesses the\ndatabase defined by the JDBC connection you specified and logs information about\nthe rows in the `country` table to @product@'s log.\n\n### Configuring the Data Source\n\nThe `-service` module's `src/main/resources/META-INF/spring/ext-spring.xml` file\nconfigures the external data source connection and applies the alias\n`extDataSource` to the data source. The `service.xml` file `entity` element\nspecifies the data source via the attribute assignment\n`data-source=\"extDataSource\"`. The `ext-spring.xml` and `service.xml` files\ndemonstrate the configuration steps explained in\n[Connecting the Data Source Using Spring Beans](/docs/7-2/appdev/-/knowledge_base/a/connecting-the-data-source-using-spring-beans).\n\n### Accessing Data\n\nThe first way of accessing data from the external database is to extract it\ndirectly from the raw data source by explicitly specifying a SQL query. This\ntechnique is demonstrated by the `CountryLocalServiceImpl.useJDBC` method. That\nmethod obtains the Spring-defined data source that's injected into the\n`countryPersistence` bean, opens a new connection, and reads data from the data\nsource. This is the technique used by the sample application to write the data\nto @product@'s log.\n\nThe second way of accessing data from the external database is to read data\nusing the helper methods that Service Builder generates in your application's\npersistence layer. This technique is demonstrated by the `UseJDBC.getCountries`\nmethod which first obtains an instance of the `CountryLocalService` OSGi service\nand then invokes `countryLocalService.getCountries`. The\n`countryLocalService.getCountries` and `countryLocalService.getCountriesCount`\nmethods are two examples of the persistence layer helper methods that Service\nBuilder generates. This is the technique used by the sample application to\nactually display the data. The portlet's `view.jsp` uses the\n`<search-container>` JSP tag to display a list of results. The results are\nobtained by the `UseJDBC.getCountries` method mentioned above.\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/02-apps/service-builder-samples/service-builder-jndi.markdown",
    "content": "---\nheader-id: service-builder-application-using-external-database-via-jndi\n---\n\n# Service Builder Application Using External Database via JNDI\n\n[TOC levels=1-4]\n\nThe `apps/service-builder/jndi` sample demonstrates how to connect a Liferay Service Builder application to\nan external database via a JNDI connection configured on the application server. Here, an external database means any\ndatabase other than @product@'s database. For this sample to work correctly, you\nmust prepare such an external database and configure your application server to use it.\n\n| **Important:** Connecting to an external data source using JNDI is broken in\n| Portal CE 7.2 GA1 and GA2, and in DXP 7.2 releases prior to FP5/SP2. See\n| [LPS-107733](https://issues.liferay.com/browse/LPS-107733) for details.\n\nFollow the steps below to make the required preparations before deploying the\napplication.\n\n1.  Create an external database based on sample application's `service.xml`.\n\n    `service.xml`:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n    <service-builder package-path=\"com.liferay.blade.samples.jndiservicebuilder\">\n    \t<namespace>REGION</namespace>\n    \t<!--<entity data-source=\"sampleDataSource\" local-service=\"true\" name=\"Foo\" remote-service=\"false\" session-factory=\"sampleSessionFactory\" table=\"bar\" tx-manager=\"sampleTransactionManager uuid=\"true\"\">-->\n    \t<entity\n    \t\tdata-source=\"extDataSource\"\n    \t\tlocal-service=\"true\"\n    \t\tname=\"Region\"\n    \t\tremote-service=\"false\"\n    \t\ttable=\"region\"\n    \t\tuuid=\"false\"\n    \t>\n    \t\t<column db-name=\"id\" name=\"regionId\" primary=\"true\" type=\"long\" />\n    \t\t<column db-name=\"name\" name=\"regionName\" type=\"String\" />\n    \t</entity>\n    </service-builder>\n    ```\n\n    The entity's data source name `extDataSource` is arbitrary but must be specified in the data source configuration in the application server (next step).\n\n    Here are MariaDB commands to create the database:\n\n    ```bash\n    create database external character set utf8;\n\n    use external;\n\n    create table region(id bigint not null primary key, name varchar(255));\n\n    insert into region(id, name) values(1, 'Tasmania');\n    ```\n\n    The database name is arbitrary; the data source configuration in your application server (next step), however, must specify this same database. The database table called `region` represents the service entity. The table has a `BIGINT` column called `Id` and a `VARCHAR(255)` column called `Name`.\n\n    Add at least one record to this table. Running `select * from region;` should return the record you added.\n\n1.  In your application server configuration, define a JNDI connection to your database and map it to the `data-source` name (i.e., `extDataSource`) that the sample `service.xml` entities specify.\n\n    For example, if Tomcat is your application server, open your `[LIFERAY_HOME]/tomcat-version/conf/server.xml` file and add a `Resource` element like this one inside of the `<GlobalNamingResources>` element:\n\n    ```xml\n    <Resource\n        name=\"jdbc/externalDataSource\"\n        auth=\"Container\"\n        type=\"javax.sql.DataSource\"\n        factory=\"org.apache.tomcat.jdbc.pool.DataSourceFactory\"\n        driverClassName=\"org.mariadb.jdbc.Driver\"\n        url=\"jdbc:mariadb://localhost/external?useUnicode=true&amp;characterEncoding=UTF-8&amp;useFastDateParsing=false\"\n        username=\"[place user name here]\"\n        password=\"[place password here]\"\n        maxActive=\"20\"\n        maxIdle=\"5\"\n        maxWait=\"10000\"\n    />\n    ```\n\n    Replace the user name and password values and see the [Database Templates](/docs/7-2/deploy/-/knowledge_base/d/database-templates) for the URL parameters to use for your database.\n\n1.  If you are using Tomcat, open your `[LIFERAY_HOME]/tomcat-version/conf/context.xml` file and add this resource link element inside of the `<Context>` element:\n\n    ```xml\n    <ResourceLink name=\"jdbc/externalDataSource\" global=\"jdbc/externalDataSource\" type=\"javax.sql.DataSource\"/>\n    ```\n\n    Now your data source is defined at Tomcat's scope.\n\n1.  Create a `com.liferay.blade.samples.jndiservicebuilder.service-log4j-ext.xml`\n    in your @product@ instance's `[LIFERAY_HOME]/osgi/log4` folder. Create this\n    folder if it doesn't yet exist. Add this content to the XML file that you\n    created:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">\n\n    <log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\">\n        <category name=\"com.liferay.blade.samples.jndiservicebuilder.service.impl\">\n            <priority value=\"INFO\" />\n        </category>\n    </log4j:configuration>\n    ```\n\n    This XML file defines the log level for the classes in the\n    `com.liferay.blade.samples.jndiservicebuilder.service.impl` package. The\n    `com.liferay.blade.samples.jndiservicebuilder.service.impl.RegionLocalServiceImpl`\n    is the class that will produce log messages when the sample portlet is\n    viewed.\n\nNow your sample is ready for deployment! Make sure to build and deploy each of\nthe three modules that comprise the sample application:\n\n- `jndi-api`\n- `jndi-service`\n- `jndi-web`\n\nAfter these modules have been deployed, add the `jndi-web` portlet to a\n@product@ page.\n\n![Figure 1: This sample prints out the values previously inputted into the database.](../../../../images/jndi-sb-sample.png)\n\nA sample table is printed in the portlet's view, representing the info inputted\ninto the database.\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample demonstrates two ways to access data from an external database\ndefined by a JNDI connection:\n\n- extract data directly from the raw data source by explicitly specifying a SQL\n  query.\n- read data using the helper methods that Service Builder generates in your\n  application's persistence layer.\n\n## How does this sample leverage the API(s) and/or code component?\n\nOnce you've added the `jndi-web` portlet to a page, the\n`RegionLocalServiceUtil.useJNDI` method is invoked. This method accesses the\ndatabase defined by the JNDI connection you specified and logs information about\nthe rows in the `region` table to @product@'s log.\n\nThe first way of accessing data from the external database is to extract data directly from\nthe raw data source by explicitly specifying a SQL query. This technique is\ndemonstrated by the `RegionLocalServiceImpl.useJNDI` method. That method obtains\nthe Spring-defined data source that's injected into the `regionPersistence`\nbean, opens a new connection, and reads data from the data source. This is the\ntechnique used by the sample application to write the data to @product@'s log.\n\nThe second way of accessing data from the external database is to read data\nusing the helper methods that Service Builder generates in your application's\npersistence layer. This technique is demonstrated by the `UseJNDI.getRegions`\nmethod which first obtains an instance of the `RegionLocalService` OSGi service\nand then invokes `regionLocalService.getRegions`. The\n`regionLocalService.getRegions` and `regionLocalService.getRegionsCount` methods\nare two examples of the persistence layer helper methods that Service Builder\ngenerates. This is the technique used by the sample application to actually\ndisplay the data. The portlet's `view.jsp` uses the `<search-container>` JSP tag\nto display a list of results. The results are obtained by the\n`UseJNDI.getRegions` method mentioned above.\n\n## Additional Information\n\n* [Connecting to an External Data Source](/docs/7-2/appdev/-/knowledge_base/a/connecting-service-builder-to-an-external-database)"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/02-apps/shared-language-keys.markdown",
    "content": "---\nheader-id: shared-language-keys\n---\n\n# Shared Language Keys\n\n[TOC levels=1-4]\n\nThe Shared Language Keys sample provides a JSP portlet that displays language\nkeys.\n\n![Figure 1: The sample JSP portlet displays three language keys.](../../../images/language-web-portlet.png)\n\nThe language keys displayed in the portlet come from two different modules.\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample is broken into two modules:\n\n- `language`\n- `language-web`\n\nThe `language-web` module provides a JSP portlet with unique language keys that\nit displays. The `language` module provides a resource module which only holds\nlanguage keys. Its sole purpose is to share language keys with the JSP portlet\nprovided in `language-web`. This sample conveys Liferay's recommended approach\nto sharing language keys through OSGi services.\n\n## How does this sample leverage the API(s) and/or code component?\n\nYou must deploy both `language-web` and `language` modules to simulate this\nsample's targeted demonstration.\n\nFirst, note the language keys provided by each module:\n\n- `language-web`\n    - `blade_language_web_LanguageWebPortlet.caption=Hello from BLADE Language Web!`\n    - `blade_language_web_override_LanguageWebPortlet.caption=I have overridden the key from BLADE Language Module!`\n\n- `language`\n    - `blade_language_LanguageWebPortlet.caption=Hello from the BLADE Language Module!`\n    - `blade_language_web_override_LanguageWebPortlet.caption=Hello from the BLADE Language Module but you won't see me!`\n\nWhen you place the sample BLADE Language Web portlet on a @product@ page, you're\npresented with three language keys:\n\n![Figure 2: The Language Web portlet displays three phrases, two of which are shared from a different module.](../../../images/shared-language-keys.png)\n\nThe first message is provided by the `language-web` module. The second message\nis from the `language` module. The third message is provided by both modules; as\nyou can see, the `language-web`'s message is used, overriding the `language`\nmodule's identically named language key.\n\nThis sample shows what takes precedence when displaying language keys. The order\nfor this example goes\n\n1.  `language-web` module language keys\n2.  `language` module language keys\n3.  @product@ language keys\n\nSo how does sharing language keys work?\n\nBy default, the `ResourceBundleLoaderAnalyzerPlugin` expands modules with\n`/content/Language.properties` files to add provided capabilities:\n\n- `bundle.symbolic.name`\n- `resource.bundle.base.name`\n\nThen the deployed `LanguageExtender` scans modules with those capabilities to\nautomatically register an associated `ResourceBundleLoader`.\n\nYou can leverage this functionality to use keys from common language modules by\nrepublishing an aggregate `ResourceBundleLoader`. This can be done two ways:\n\n1. Via Components\n\n    You can get a reference to the registered service in your components as\n    detailed in the\n\t\t[Overriding a Module's Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys)\n\t\ttutorial. The main disadvantage of this approach is that it forces you to\n\t\tprovide a specific implementation of the `ResourceBundleLoader`, making it\n\t\tharder to modularize in the future.\n\n2. Via Provide Capability\n\n    The same `LanguageExtender` that registers the services supports an extended\n    syntax that lets you register an aggregate of a collection of bundles:\n\n    ```\n    -liferay-aggregate-resource-bundles: \\\n        blade.language\n    ```\n\n    This approach has the advantage of easier extensibility. When language keys\n    change, only the common language modules must be built and redeployed for\n    the modules referencing them to recognize their updates.\n\nFor more information on sharing language keys, visit the\n[Internationalization](/docs/7-2/frameworks/-/knowledge_base/f/localization)\ntutorials.\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/shared-language-keys)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps/shared-language-keys)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/apps/shared-language-keys)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/02-apps/simulation-panel-app.markdown",
    "content": "---\nheader-id: simulation-panel-app\n---\n\n# Simulation Panel App\n\n[TOC levels=1-4]\n\nThe Simulation Panel App provides new functionality in @product@'s Simulation\nMenu. When deploying this sample with no customizations, the *Simulation Sample*\nfeature is provided in the Simulation Menu with four options.\n\n<!-- Fix image below when Simulation Panel App works properly (BLADE-267). -Cody\n\n[Figure 1: A simulation panel app adds new functionality to the Simulation Menu.](../../../images/simulation-panel-app.png)\n-->\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample leverages the\n[PanelApp](@app-ref@/web-experience/latest/javadocs/com/liferay/application/list/PanelApp.html)\nAPI.\n\n## How does this sample leverage the API(s) and/or code component?\n\nThis sample leverages the `PanelApp` interface as an OSGi service via the\n`@Component` annotation:\n\n```java\n@Component(\n    immediate = true,\n    property = {\n        \"panel.app.order:Integer=500\",\n        \"panel.category.key=\" + SimulationPanelCategory.SIMULATION\n    },\n    service = PanelApp.class\n)\n```\n\nThere are also two properties provided via the `@Component` annotation:\n\n- `panel.app.order`: the order in which the panel app is displayed among other\n   panel apps in the chosen category. Entries are ordered from top to bottom.\n   For example, an entry with order `1` will be listed above an entry with order\n   `2`. If the order is not specified, it's chosen at random based on which\n   service was registered first in the OSGi container.\n- `panel.category.key`: the host panel category for your panel app, which\n   should be the Simulation Menu category.\n\nThe simulation panel app extends the\n[BaseJSPPanelApp](https://docs.liferay.com/ce/apps/web-experience/latest/javadocs/com/liferay/application/list/BaseJSPPanelApp.html),\nwhich provides a skeletal implementation of the\n[PanelApp](https://docs.liferay.com/ce/apps/web-experience/latest/javadocs/com/liferay/application/list/PanelApp.html)\ninterface with JSP support. JSPs, however, are not the only way to provide\nfrontend functionality to your panel categories/apps. You can create your own\nclass implementing `PanelApp` to use other technologies, such as FreeMarker.\n\n<!--Uncomment once articles are available\nTo learn more about Liferay Portal's product navigation using panel categories\nand panel apps, see the\nCustomizing the Product Menu\ntutorial. For more information on extending the Simulation Menu, see the\nExtending the Simulation Menu\ntutorial.\n-->\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/simulation-panel-app)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps/simulation-panel-app)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/apps/simulation-panel-app)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/02-apps/workflow-samples/01-intro.markdown",
    "content": "---\nheader-id: workflow-samples\n---\n\n# Workflow Samples\n\n[TOC levels=1-4]\n\nThis section focuses on Liferay's Workflow Framework sample projects built with\nvarious build tools. You can view these samples by visiting the\n`apps/workflow` folder corresponding to your preferred build tool:\n\n- [Gradle Workflow sample apps](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/workflow)\n- [Liferay Workspace Workflow sample apps](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/apps/workflow)\n- [Maven Workflow sample apps](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/apps/workflow)\n\nThe following Workflow samples are documented:\n\n- [Workflow application](/docs/7-2/reference/-/knowledge_base/r/workflow-application)\n- [Workflow application with Asset Integration](/docs/7-2/reference/-/knowledge_base/r/workflow-application-with-asset-integration)\n\nVisit a particular sample page to learn more!\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/02-apps/workflow-samples/02-workflow-asset.markdown",
    "content": "---\nheader-id: workflow-application-with-asset-integration\n---\n\n# Workflow Asset Application\n\n[TOC levels=1-4]\n\nThis sample demonstrates workflow enabling a model entity that is an asset.\n\nTo see the Workflow sample in action, complete the following steps:\n\n1.  Add the sample widget to a page by navigating to *Add*\n    (![Add](../../../../images/icon-add.png)) &rarr; *Widgets* &rarr; *Sample*\n    &rarr; *Workflow Asset* and dragging it to the page.\n\n2.  Go to *Control Panel* &rarr; *Workflow* &rarr; *Process Builder* &rarr;\n    *Configuration* and assign a workflow to the Qux entity.\n\n3.  Select the app's *Add* button and add an entity. Do this several times to\n    create multiple entities.\n\n4.  Go to *User* &rarr; *My Workflow Tasks* &rarr; *Assigned to My Roles* and\n    assigned the task to me and Approve the Task.\n\nNow you've taken the entity and successfully run it through a workflow.\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample demonstrates @product@'s Workflow Handler API. Specifically, it\ndemonstrates how to create a `WorkflowHandler` for your custom entity that is\n[asset enabled](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework).\n\n## How does this sample leverage the API(s) and/or code component?\n\nThe basic implementation of `WorkflowHandler` is done via extension of the\n`BaseWorkflowHandler` class. This is where the sample leverages the basic\nmethods required for the entity's `WorkflowHandler`.\n\n```java\n@Override\npublic String getClassName() {\n  return Qux.class.getName();\n}\n\n@Override\npublic String getTitle(long classPK, Locale locale) {\n  return String.valueOf(classPK);\n}\n\n@Override\npublic String getType(Locale locale) {\n  return ResourceActionsUtil.getModelResource(locale, getClassName());\n}\n\n@Override\npublic Qux updateStatus(\n    int status, Map<String, Serializable> workflowContext)\n  throws PortalException {\n\n  long userId = GetterUtil.getLong(\n    (String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));\n\n  long classPK = GetterUtil.getLong(\n    (String)workflowContext.get(\n      WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n  return _quxLocalService.updateStatus(userId, classPK, status);\n}\n```\n\nFor more information on the workflow framework, visit its dedicated\n[documentation](/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework).\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/02-apps/workflow-samples/03-workflow-basic.markdown",
    "content": "---\nheader-id: workflow-application\n---\n\n# Workflow Application\n\n[TOC levels=1-4]\n\nThe\n[`basic`](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/apps/workflow/basic)\nsample demonstrates workflow enabling an entity that is not an asset.\n\nTo see the Workflow sample in action, complete the following steps:\n\n1.  Add the sample widget to a page by navigating to *Add*\n    (![Add](../../../../images/icon-add.png)) &rarr; *Widgets* &rarr; *Sample*\n    &rarr; *Workflow Basic* and dragging it to the page.\n\n2.  Go to *Control Panel* &rarr; *Workflow* &rarr; *Process Builder* &rarr;\n    *Configuration* and assign a workflow to the `Baz` entity.\n\n3.  Select the app's *Add* button and add an entity. Do this several times to\n    create multiple entities.\n\n4.  Go to *User* &rarr; *My Workflow Tasks* &rarr; *Assigned to My Roles* and\n    assigned the task to me and Approve the Task.\n\nNow you've taken the entity and successfully run it through a workflow.\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample demonstrates @product@'s Workflow Handler API.  Specifically, it\ndemonstrates how to create a `WorkflowHandler` for your custom entity.\n\n## How does this sample leverage the API(s) and/or code component?\n\nThe basic implementation of `WorkflowHandler` is done via extension of the\n`BaseWorkflowHandler` class. This is where the sample leverages the basic\nmethods required for the entity's `WorkflowHandler`.\n\n```java\n@Override\npublic String getClassName() {\n  return Baz.class.getName();\n}\n\n@Override\npublic String getTitle(long classPK, Locale locale) {\n  return String.valueOf(classPK);\n}\n\n@Override\npublic String getType(Locale locale) {\n  return ResourceActionsUtil.getModelResource(locale, getClassName());\n}\n\n@Override\npublic Baz updateStatus(\n    int status, Map<String, Serializable> workflowContext)\n  throws PortalException {\n\n  long userId = GetterUtil.getLong(\n    (String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));\n\n  long classPK = GetterUtil.getLong(\n    (String)workflowContext.get(\n      WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n  return _bazLocalService.updateStatus(userId, classPK, status);\n}\n```\n\nFor more information on the workflow framework, visit its dedicated\n[documentation](/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework).\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/03-extensions/01-intro.markdown",
    "content": "---\nheader-id: extensions\n---\n\n# Extensions\n\n[TOC levels=1-4]\n\nThis section focuses on Liferay sample extensions. You can view these sample\nextensions by visiting the `extensions` folder corresponding to your preferred\nbuild tool:\n\n- [Gradle sample extensions](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions)\n- [Liferay Workspace sample extensions](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions)\n- [Maven sample extensions](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions)\n\nVisit a particular sample page to learn more!\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/03-extensions/control-menu-entry.markdown",
    "content": "---\nheader-id: control-menu-entry\n---\n\n# Control Menu Entry\n\n[TOC levels=1-4]\n\nThe Control Menu Entry sample provides a customizable button that is added to\nLiferay Portal's default Control Menu. When deploying this sample with no\ncustomizations, an additional button is added to the User (right side) portion\nof the Control Menu.\n\n![Figure 1: The User area of the Control Menu is provided an additional link button when the Control Menu Entry sample is deployed to @product@.](../../../images/controlmenuentry.png)\n\nThe button navigates the user to Liferay's website: https://www.liferay.com.\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample leverages the\n[ProductNavigationControlMenuEntry](@app-ref@/web-experience/latest/javadocs/com/liferay/product/navigation/control/menu/ProductNavigationControlMenuEntry.html)\nAPI.\n\n## How does this sample leverage the API(s) and/or code component?\n\nThis sample first leverages the `ProductNavigationControlMenuEntry` interface as\nan OSGi service via the `@Component` annotation:\n\n```java\n@Component(\n    immediate = true,\n    property = {\n        \"product.navigation.control.menu.category.key=\" + ProductNavigationControlMenuCategoryKeys.USER,\n        \"product.navigation.control.menu.entry.order:Integer=1\"\n    },\n    service = ProductNavigationControlMenuEntry.class\n)\n```\n\nThere are also two properties provided via the `@Component` annotation:\n\n- `product.navigation.control.menu.category.key`: the category in which your\n   entry should reside. The default Control Menu provides three categories:\n   *SITES* (left portion), *TOOLS* (middle portion), and *USER* (right portion).\n- `product.navigation.control.menu.entry.order:Integer`: the order in which your\n   entry will be displayed in the category. Entries are ordered from left to\n   right. For example, an entry with order `1` will be listed to the left of an\n   entry with order `2`. If the order is not specified, it's chosen at random\n   based on which service was registered first in the OSGi container.\n\nThis sample also implements the `ProductNavigationControlMenuEntry` interface.\nThe following methods are implemented:\n\n- `getIcon(HttpServletRequest)`\n- `getLabel(Locale)`\n- `getURL(HttpServletRequest)`\n- `isShow(HttpServletRequest)`\n\nRefer to this sample's `BladeProductNavigationControlMenuEntry` class for\nJavadocs describing these methods. \n\n<!--Uncomment once article is available\nFor more information on how to customize\nLiferay Portal's Control Menu, visit the\nCustomizing the Control Menu\ntutorial.\n-->\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/control-menu-entry)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/control-menu-entry)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/control-menu-entry)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/03-extensions/document-action.markdown",
    "content": "---\nheader-id: document-action\n---\n\n# Document Action\n\n[TOC levels=1-4]\n\nThe Document Action sample shows how to add a context menu option to an entry in\nthe Documents and Media portlet. When deploying this sample with no\ncustomizations, an additional menu option is available in the Documents and\nMedia Admin portlet and the Documents and Media portlet. This sample creates a\n*Blade Basic Info* option that displays basic information about the entry (e.g.,\nfile name, type, version, etc.). For example, the Admin portlet provides the new\noption as illustrated in the images below:\n\n![Figure 1: The new *Blade Basic Info* option is available from the entry's Options menu.](../../../images/documents-and-media-admin-portlet.png)\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample leverages the\n[PortletConfigurationIcon](@product-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/configuration/icon/PortletConfigurationIcon.html)\nAPI.\n\n## How does this sample leverage the API(s) and/or code component?\n\nThere are four Java classes used in this sample:\n\n- `BladeActionConfigurationIcon`: Adds the new context menu option to the\n   Document Detail screen options (![Options](../../../images/icon-options.png))\n   (top right corner) of the Documents and Media Admin portlet. See the\n   [Configuring Your Admin App's Actions Menu](/docs/7-0/tutorials/-/knowledge_base/t/configuring-your-admin-apps-actions-menu)\n   tutorial for more details.\n- `BladeActionDisplayContext`: Adds the Display Context for the document action.\n   More about Display Contexts are described later.\n- `BladeActionDisplayContextFactory`: Adds the Display Context factory for the\n   document action.\n- `BladeDocumentActionPortlet`: Provides the portlet class, which extends the\n   [GenericPortlet](https://portals.apache.org/pluto/portlet-2.0-apidocs/javax/portlet/GenericPortlet.html).\n   This class generates what is shown when the context menu option is selected.\n\n<!-- Update 7.0 link above when 7.2 version is available (LRDOCS-4313). -Cody -->\n\nA Display Context is a Java class that controls access to a portlet screen's UI\nelements. For example, the Document Library would use Display Contexts to\nprovide its screens all their UI elements. It would use one Display Context for\nits document edit screen, another for its document view screen, etc. A portlet\nideally uses a different Display Context for each of its screens.\n\nA screen's JSP calls on the Display Context (DC) to get elements to render and\nto decide whether to render certain types of elements. Some of the DC methods\nreturn a collection of UI elements (e.g., a menu object of menu items), while\nother DC methods return booleans that determine whether to show particular\nelement types. The DC decides which objects to display, while the JSP organizes\nthe rendered objects and implements the screen's look and feel. You don't have\nto decide which elements to display in your JSP; simply call the DC methods to\npopulate UI components with objects to render.\n\nTo customize or extend a portlet screen that uses a DC, you can extend the DC\nand override the methods that control access to the elements that interest you.\nFor example, you can turn off displaying certain types of elements (e.g.,\nactions) by overriding the DC method that makes that decision. You can add new\ncustom elements (e.g., new actions) or remove existing elements (e.g., a delete\naction) from a collection of elements a DC method returns. The beauty of\ncustomizing via a DC is that you don't have to modify the JSP. You only modify\nthe particular methods that are related to the UI customization goals. And JSP\nupdates won't break the DC customizations. Replacing a JSP, on the other hand,\ncan lead to missing an important JSP modification that a new Liferay version\nintroduces.\n\nAs you create custom portlets, you may want to implement DCs. You can benefit\nfrom the separation of concerns that DCs provide and customers can extend your\nportlet DCs to specify which UI elements to display. And they don't need to\nworry about missing out on the updates you make to the JSPs.\n\n## Where Is This Sample?\n   \nThere are three different versions of this sample, each built with a different\nbuild tool:\n   \n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/document-action)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/document-action)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/document-action)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/03-extensions/gogo-shell-command.markdown",
    "content": "---\nheader-id: gogo-shell-command\n---\n\n# Gogo Shell Command\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/liferay-internals/fundamentals/using-the-gogo-shell.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe Gogo Shell Command sample demonstrates adding a custom command to\n@product@'s Gogo shell environment. All @product@ installations have a Gogo\nshell environment, which lets system administrators interact with @product@'s\nmodule framework on a local server machine.\n\nThis example adds a new custom Gogo shell command called `usercount` under the\n`blade` scope. It prints out the number of registered users on your @product@\ninstallation.\n\nTo test this sample, follow the instructions below:\n\n1.  Start a @product@ installation.\n2.  Navigate to the Control Panel &rarr; *Configuration* &rarr; *Gogo Shell*.\n3.  Execute `help` to view all the available commands. The sample Gogo shell\n    command is listed.\n\n    ![Figure 1: The sample Gogo shell command is listed with all the available commands.](../../../images/gogo-shell-1.png)\n\n4.  Execute `usercount` to execute the new custom command. The number of users\n    on your running Liferay Portal installation is printed.\n\n    ![Figure 2: The outcome of executing the `usercount` command.](../../../images/gogo-shell-2.png)\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample demonstrates creating a new Gogo shell command by leveraging\n`osgi.command.*` properties in a Java class.\n\n## How does this sample leverage the API(s) and/or code component?\n\nTo add this new Gogo shell command, you must implement the logic in a Java\nclass with the following two properties:\n\n- `osgi.command.function`: the command's name, which must match the method name\n   in the registered service implementation.\n- `osgi.command.scope`: the general scope or namespace for the command.\n\nThese properties are set in your class's `@Component` annotation like this:\n\n```java\n@Component(\n    property = {\"osgi.command.function=usercount\", \"osgi.command.scope=blade\"},\n    service = Object.class\n)\n```\n\nThe logic for the `usercount` command is specified in the method with the same\nname:\n\n```java\npublic void usercount() {\n    System.out.println(\n        \"# of users: \" + getUserLocalService().getUsersCount());\n}\n```\n\nThis method uses *Declarative Services* to get a reference for the\n`UserLocalService` to invoke the `getUsersCount` method. This lets you find the\nnumber of users currently in the system.\n\nFor more information on using the Gogo shell, see the\n[Using the Felix Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\ntutorial.\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/gogo)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/gogo)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/gogo)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/03-extensions/index-settings-contributor.markdown",
    "content": "---\nheader-id: index-settings-contributor\n---\n\n# Index Settings Contributor\n\n[TOC levels=1-4]\n\nThe Index Settings Contributor sample demonstrates how to add a custom type\nmapping to @product@. You can demo this sample by completing the following\nsteps:\n\n1.  Navigate to the *Control Panel* &rarr; *Configuration* &rarr; *Search* menu.\n\n2.  Click *Execute* for the *Reindex all search indexes* action.\n\n    All properties defined in your `.json` file are added to @product@'s search\n    engine. This sample adds the following index properties:\n\n    - `sampleDate`\n    - `sampleDouble`\n    - `sampleLong`\n    - `sampleText`\n\n    You'll verify this next.\n\n3.  Find your @product@'s instance ID. This can be found in the *Control Panel*\n    &rarr; *Configuration* &rarr; *Virtual Instances* menu.\n\n4.  Navigate to the following URL:\n\n        http://localhost:9200/liferay-[INSTANCE_ID]/_mapping/LiferayDocumentType?pretty\n\n    Be sure to insert your instance ID into the URL.\n\n5.  Verify the added properties are listed.\n\n    ![Figure 1: This sample added four new index properties.](../../../images/index-settings-contributor.png)\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample leverages the\n[IndexSettingsContributor](@app-ref@/foundation/latest/javadocs/com/liferay/portal/search/elasticsearch/settings/IndexSettingsContributor.html)\nAPI.\n\n## How does this sample leverage the API(s) and/or code component?\n\nLiferay's search engine provides an API to define custom mappings. To use it,\nfollow these fundamental steps:\n\n1.  Define the new mapping. In this sample, the mapping is defined in the\n    `META-INF/mappings/resources/index-type-mappings.json` file. Notice that the\n    default document for @product@ is called `LiferayDocumentType`. The\n    mapping's features can be found in\n    [Elasticsearch's docs](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/mapping.html).\n\n2.  Inject the mapping into Elasticsearch. The `IndexSettingsContributor` class'\n    components are invoked during the reindexing stage and receive a\n    `TypeMappingsHelper` as a hook to add new mappings.\n\nThis sample has two classes:\n\n- `ResourceUtil`: reads the `.json` file.\n\n- `IndexSettingsContributor`: allows the addition of type mappings on\n  @product@'s search engine.\n\nThe `IndexSettingsContributor`'s `contribute` method adds the type mappings:\n\n```java\n@Override\npublic void contribute(\n    String indexName, TypeMappingsHelper typeMappingsHelper) {\n    try {\n        String mappings = ResourceUtil.readResouceAsString(\n            \"META-INF/resources/mappings/index-type-mappings.json\");\n\n        typeMappingsHelper.addTypeMappings(indexName, mappings);\n    }\n    catch (Exception e) {\n        e.printStackTrace();\n    }\n}\n```\n\nFor the `ResourceUtil.readResouceAsString` parameter, you should pass the path\nfor the `.json` file that contains the properties to be added.\n\nAlso, it is important to highlight the `IndexSettingsContributor`'s `@Component`\nannotation that registers a new service to the OSGi container:\n\n```java\n@Component(\n    immediate = true,\n    service = com.liferay.portal.search.elasticsearch6.settings.IndexSettingsContributor.class\n)\n\n> If using Elasticsearch 7, the value of the `service` property is instead `com.liferay.portal.search.elasticsearch7.settings.IndexSettingsContributor.class`.\n```\n\nThis sample demonstrates the essentials needed to contribute your own index\nsettings.\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/blob/7.2/gradle/extensions/index-settings-contributor)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/blob/7.2/liferay-workspace/extensions/index-settings-contributor)\n- [Maven](https://github.com/liferay/liferay-blade-samples/blob/7.2/maven/extensions/index-settings-contributor)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/03-extensions/indexer-post-processor.markdown",
    "content": "---\nheader-id: indexer-post-processor\n---\n\n# Indexer Post Processor\n\n[TOC levels=1-4]\n\nThe Indexer Post Processor sample demonstrates using the `IndexerPostProcessor`\ninterface, which is provided to customize search queries and documents before\nthey're sent to the search engine, and/or customize result summaries when\nthey're returned to end users. This basic demonstration prints a message in the\nlog when one of the `*IndexerPostProcessor` methods is called. \n\nTo see this sample's messages in @product@'s log, you must add a logging\ncategory to the portal. Navigate to *Control Panel* &rarr; *Configuration*\n&rarr; *Server Administration* and click on *Log Levels* &rarr; *Add Category*.\nThen fill out the form:\n\n- *Logger Name*: `com.liferay.blade.samples.indexerpostprocessor`\n- *Log Level*: `INFO`\n\nOnce you save the new logging category, you can witness the sample indexer post\nprocessor in action. For example, you can test the sample's\n`BlogsIndexerPostProcessor` implementation by creating a blog entry. When you\npublish the blog, the following message is logged in the console:\n\n```bash\n18:27:30,737 INFO  [http-nio-8080-exec-8][BlogsIndexerPostProcessor:76] postProcessDocument\n```\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample leverages the\n[IndexerPostProcessor](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/search/IndexerPostProcessor.html)\nAPI.\n\n## How does this sample leverage the API(s) and/or code component?\n\nThis sample contains four implementations of the `IndexerPostProcessor`\ninterface:\n\n- `BlogsIndexerPostProcessor`\n- `MultipleEntityIndexerPostProcessor`\n- `MultipleIndexerPostProcessor`\n- `UserEntityIndexerPostProcessor`\n\nAll these classes leverage the interface as an OSGi service via the `@Component`\nannotation. For example, the `@Component` annotation of the\n`UserEntityIndexerPostProcessor` looks like this:\n\n```java\n@Component(\n    immediate = true,\n    property = {\n        \"indexer.class.name=com.liferay.portal.kernel.model.User\",\n        \"indexer.class.name=com.liferay.portal.kernel.model.UserGroup\"\n    },\n    service = IndexerPostProcessor.class\n)\n```\n\nThere's one property type provided via the `@Component` annotation:\n\n- `indexer.class.name`: the fully qualified class name of the indexed entity or\nan `Indexer` class itself.\n\nThis sample's implementations of the `IndexerPostProcessor` interface override\nthe following methods:\n\n- `postProcessContextBooleanFilter`\n- `postProcessContextQuery`\n- `postProcessDocument`\n- `postProcessFullQuery`\n- `postProcessSearchQuery(BooleanQuery, BooleanFilter)`\n- `postProcessSearchQuery(BooleanQuery, SearchContext)`\n- `postProcessSummary`\n\nFor more information on Liferay's Search API, refer to the\n[Introduction to Liferay Search](/docs/7-2/frameworks/-/knowledge_base/f/search)\narticle.\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/indexer-post-processor)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/indexer-post-processor)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/indexer-post-processor)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/03-extensions/model-listener.markdown",
    "content": "---\nheader-id: model-listener\n---\n\n# Model Listener\n\n[TOC levels=1-4]\n\nThe Model Listener sample demonstrates adding a custom model listener to a\nLiferay Portal out-of-the-box entity. When deploying this sample with no\ncustomizations, a custom model listener is added to the portal's layouts,\nlistening for `onBeforeCreate` events. This means that any page creation will\ntrigger this listener, which will execute before the new page is created.\n\nFor example, if a new page is added with the name *My Test Page*, the following\nmessage is printed to the console:\n\n![Figure 1: The sample model listener's message in the console.](../../../images/model-listener-1.png)\n\nYou can also verify that the model listener sample was executed by navigating\nto the new page's *Options* &rarr; *Configure Page* &rarr; *SEO* option. The\nHTML Title field looks like this:\n\n![Figure 2: The page's HTML title updated by the model listener sample.](../../../images/model-listener-2.png)\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample leverages the\n[ModelListener](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/ModelListener.html)\nAPI.\n\n## How does this sample leverage the API(s) and/or code component?\n\nModel Listeners are used to listen for persistence events on models and take\nactions as a result of those events. Actions can be executed on an entity's\ndatabase table before or after a `create`, `remove`, `update`, `addAssociation`,\nor `removeAssociation` event. It's possible to have more than one model listener\non a single model too; the execution order is not guaranteed.\n\nThere are two steps to create a new model listener:\n\n- Implement a Model Listener class\n- Register the new service in Liferay's OSGi runtime\n\nThis sample adds the model listener logic in a new Java class named\n`CustomLayoutListener` that extends\n[BaseModelListener](@platform-ref@/7.1-latest/javadocs/portal-kernel/com/liferay/portal/kernel/model/BaseModelListener.html).\n\n```java\npublic class CustomLayoutListener extends BaseModelListener<Layout> {\n\n    @Override\n    public void onBeforeCreate(Layout model) throws ModelListenerException {\n        System.out.println(\n            \"About to create layout: \" + model.getNameCurrentValue());\n\n        model.setTitle(\"Title generated by model listener!\");\n    }\n\n}\n```\n\nImportant things to note in this code snippet are\n\n- The entity to be targeted by this model listener is specified as the\n  parameterized type (e.g., `Layout`).\n- The overridden methods dictate the type of event(s) that are listened for\n  (e.g., `onBeforeCreate`); they also trigger the logic execution.\n\nThe final step is registering the service in Liferay's OSGi runtime, which is\naccomplished by the following annotation (if using Declarative Services):\n\n```java\n@Component(immediate = true, service = ModelListener.class)\n```\n\nFor more information on model listeners, see the\n[Creating Model Listeners](/docs/7-2/customization/-/knowledge_base/c/model-listeners)\ntutorial.\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/model-listener)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/model-listener)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/model-listener)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/03-extensions/screen-name-validator.markdown",
    "content": "---\nheader-id: screen-name-validator\n---\n\n# Screen Name Validator\n\n[TOC levels=1-4]\n\nThe Screen Name Validator sample provides a way to validate a user's inputted\nscreen name. During validation, the screen name is tested client-side and\nserver-side.\n\nThis sample checks if a user's screen name contains reserved words that are\nconfigured in the *Control Panel* &rarr; *Configuration* &rarr; *System\nSettings* &rarr; *Foundation* &rarr; *ScreenName Validator* menu. The default\nvalues for the screen name validator's reserved words are *admin* and *user*.\n\n![Figure 1: Enter reserved words for the screen name validator.](../../../images/screenname-validator-config.png)\n\nYou can test this sample by following the following steps:\n\n1.  Deploy the Screen Name Validator to your portal installation.\n2.  Navigate to the *Control Panel* &rarr; *Users* &rarr; *Users and\n    Organizations* menu.\n3.  Create a new user by selecting the *Add User*\n    (![Add User](../../../images/icon-add.png)) button.\n4.  Adding a screen name that contains the word *admin* or *user*.\n\n![Figure 2: The error message displays when inputting a reserved word for the screen name.](../../../images/screenname-validator-test.png)\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample leverages the\n[ScreenNameValidator](@product-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/security/auth/ScreenNameValidator.html)\nAPI.\n\n## How does this sample leverage the API(s) and/or code component?\n\nTo customize this sample, modify its\n`com.liferay.blade.samples.screenname.validator.internal.CustomScreenNameValidator`\nclass.\n\nYou can also customize this sample's configuration by adding more properties in\nits\n`com.liferay.blade.samples.screenname.validator.CustomScreenNameConfiguration`\nclass.\n\nFor more information on customizing the Validation sample to fit your needs, see\nthe Javadoc provided in this sample's Java classes.\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/screen-name-validator)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/screen-name-validator)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/screen-name-validator)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/03-extensions/servlet.markdown",
    "content": "---\nheader-id: servlet\n---\n\n# Servlet\n\n[TOC levels=1-4]\n\nThe Servlet sample provides an OSGi Whiteboard Servlet in @product@. When\ndeploying this sample and configuring the servlet, a *Hello World* message is\ndisplayed when accessing the servlet page URL. Log info is also outputted to\nyour console.\n\n![Figure 1: The servlet displays *Hello World* from the configured servlet page URL.](../../../images/servlet-sample.png)\n\n![Figure 2: The servlet also logs info in the console.](../../../images/servlet-sample-log.png)\n\nTo configure the servlet in @product@, complete the following steps:\n\n1.  Navigate to the *Control Panel* &rarr; *Configuration* &rarr; *Server\n    Administration* &rarr; *Log Levels*.\n\n2.  Select *Add Category*.\n\n3.  Insert *com.liferay.blade.samples.servlet.BladeServlet* for the Logger Name\n    and *INFO* for the Log Level.\n\n4.  Navigate to the http://localhost:8080/o/blade/servlet URL.\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample leverages the\n[HttpServlet](https://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServlet.html)\nAPI.\n\n## How does this sample leverage the API(s) and/or code component?\n\nTo customize this sample, modify its\n`com.liferay.blade.samples.servlet.BladeServlet` class. This class extends the\n`HttpServlet` class. Creating your own servlet for @product@ is useful when you\nneed to implement servlet actions. For example, if you wanted to implement the\nCMIS server by yourself with [Apache Chemistry](https://chemistry.apache.org/),\nyou would need to implement your own servlet, managing requests at a low level.\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/extensions/servlet)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/extensions/servlet)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/extensions/servlet)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/04-overrides/01-intro.markdown",
    "content": "---\nheader-id: overrides\n---\n\n# Overrides\n\n[TOC levels=1-4]\n\nThis section focuses on Liferay sample overrides. You can view these sample\noverrides by visiting the `overrides` folder corresponding to your preferred\nbuild tool:\n\n- [Gradle sample overrides](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/overrides)\n- [Liferay Workspace sample overrides](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/overrides)\n- [Maven sample overrides](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/overrides)\n\nVisit a particular sample page to learn more!\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/04-overrides/module-jsp-override.markdown",
    "content": "---\nheader-id: module-jsp-override\n---\n\n# Module JSP Override\n\n[TOC levels=1-4]\n\nThe Module JSP Override sample conveys how to override an application's JSP by\nleveraging OSGi fragment modules. This is not the recommended practice for\noverriding JSPs in @product-ver@. See the\n[Customizing JSPs](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps)\narticle for better options.\n\n\nThis example overrides the default `login.jsp` file in the\n`com.liferay.login.web` bundle by adding the red text *changed* to the Sign In\nform.\n\n![Figure 1: The customized Sign In form with the new *changed* text.](../../../images/hook-jsp.png)\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample demonstrates how to create a fragment host module and configure it\nto override an existing module's JSP.\n\n## How does this sample leverage the API(s) and/or code component?\n\nYou can create your own JSP override by\n\n- Declaring the fragment host.\n- Providing the JSP that will override the original one.\n\nTo properly declare the fragment host in the `bnd.bnd` file, you must specify\nthe host module's (where the original JSP is located) Bundle Symbolic Name and\nthe host module's exact version to which the fragment belongs. In this example,\nthis is configured like this:\n\n```\nFragment-Host: com.liferay.login.web;bundle-version=\"1.0.0\"\n```\n\nThen you must provide the new JSP intended to override the original one. Be sure\nto mimic the host module's folder structure when overriding its JAR. For this\nexample, since the original JSP is in the folder\n`/META-INF/resources/login.jsp`, the new JSP file resides in the folder\n`src/main/resources/META-INF/resources/login.jsp`.\n\nIf needed, you can also target the original JSP following one of the two\npossible naming conventions: `original` or `portal`. This pattern looks like\n\n```markup\n<liferay-util:include\n    page=\"/login.original.jsp\"\n    servletContext=\"<%= application %>\"\n/>\n```\n\nor\n\n```markup\n<liferay-util:include\n    page=\"/login.portal.jsp\"\n    servletContext=\"<%= application %>\"\n/>\n```\n\nThis approach can be used to override any application JSP (i.e., JSPs residing\nin a module). You can also add new JSPs to an existing module with this\ntechnique. For more information on other ways to customize JSPs, see the\n[Customizing JSPs](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps)\narticles.\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/overrides/module-jsp-override)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/overrides/module-jsp-override)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/overrides/module-jsp-override)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/04-overrides/resource-bundle-override.markdown",
    "content": "---\nheader-id: resource-bundle-override\n---\n\n# Resource Bundle Override\n\n[TOC levels=1-4]\n\nThis example overrides the default\n`javax.portlet.title.com_liferay_login_web_portlet_LoginPortlet` language key\nfor @product@'s default Login portlet. After deploying this sample to @product@,\nthe Login portlet's *Sign In* title is modified to display *Login Portlet\nOverride*.\n\n![Figure 1: The customized Login portlet displays the new language key.](../../../images/hook-resourcebundle.png)\n\nFor reference, the Login portlet's language keys are stored in the\n[liferay-portal](https://github.com/liferay/liferay-portal) Github repo's\n`modules/apps/login/login-web/src/main/resources/content` folder.\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample leverages the\n[`Provide-Capability`](https://bnd.bndtools.org/chapters/220-contracts.html)\nOSGi manifest header.\n\n## How does this sample leverage the API(s) and/or code component?\n\nThis sample conveys the recommended approach to override a portlet's\nlanguage keys file for any module that is deployed to @product@'s OSGi runtime\n(not applicable to @product@'s core language keys).\n\nThe steps to override a portlet's language keys are\n\n- Provide the new language keys that will override the original ones.\n- Prioritize the new module's resource bundle.\n\nThis sample's `src/main/resources/content` folder holds the language properties\nfile to override. Since this example's goal is to override only the English\nkeys, the `Language_en.properties` is added. You can add more language\nproperties files for additional language key locales you want to override (e.g.,\n`Language_en.properties` for Spanish).\n\nOnce your language keys are in place, you must use OSGi manifest headers to\nspecify your custom language keys are for the target module. To compliment the\ntarget module's resource bundle, you must aggregate your resource bundle with\nthe target module's resource bundle. This is done by ranking your module first\nto prioritize its resource bundle over the target module resource bundle. See\nthis sample's `bnd.bnd` as an example for setting the `Provide-Capability` OSGi\nheader:\n\n```\nProvide-Capability:\\\n    liferay.resource.bundle;\\\n        resource.bundle.base.name=\"content.Language\",\\\n    liferay.resource.bundle;\\\n        bundle.symbolic.name=com.liferay.login.web;\\\n        resource.bundle.aggregate:String=\"(bundle.symbolic.name=com.liferay.blade.login.web.resource.bundle.override),(bundle.symbolic.name=com.liferay.login.web)\";\\\n        resource.bundle.base.name=\"content.Language\";\\\n        service.ranking:Long=\"2\";\\\n        servlet.context.name=login-web\n```\n\nFor more information on the `Provide-Capability` header and its parts, see the\n[Prioritze Your Module's Resource Bundle](/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys#prioritize-your-modules-resource-bundle)\nsection.\n\nThis approach can be used to override any portlet's language keys (i.e.,\n`language.properties` files that are inside a module deployed to @product@'s\nOSGi runtime). If you need to override @product@'s core language keys, see the\n[Overriding Global Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-global-language-keys)\narticle.\n\nFor more information on using a resource bundle to override a module's\nlanguage keys, see the\n[Overriding a Module's Language Keys](/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys)\ntutorial.\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/overrides/login-web-resource-bundle-override)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/overrides/login-web-resource-bundle-override)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/overrides/login-web-resource-bundle-override)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/05-themes/01-intro.markdown",
    "content": "---\nheader-id: themes\n---\n\n# Themes\n\n[TOC levels=1-4]\n\nThis section focuses on Liferay sample themes. You can view these sample themes\nby visiting the `themes` folder corresponding to your preferred build tool:\n\n- [Gradle sample themes](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/themes)\n- [Liferay Workspace sample themes](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/themes)\n- [Maven sample themes](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/themes)\n\nVisit a particular sample page to learn more!\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/05-themes/simple-theme.markdown",
    "content": "---\nheader-id: theme\n---\n\n# Simple Theme\n\n[TOC levels=1-4]\n\nThe Simple Theme sample provides the base files for a theme, using the\n[Theme Builder Gradle plugin](/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin).\nWhen deploying this sample with no customizations, a theme based off of the \n`_styled` base theme is created.\n\n![Figure 1: A theme based off of the Styled base theme is created when the Theme Blade sample is deployed to Liferay Portal.](../../../images/theme.png)\n\nFor more information on themes, visit the \n[Introduction to Themes](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction)\ntutorial.\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample demonstrates a way to create a simple theme in @product@.\n\n## How does this sample leverage the API(s) and/or code component?\n\nTo modify this sample, add the `images`, `js`, or `templates` folder, along with\nyour modified files, to the `src/main/webapp` folder. The sample already\nprovides the `src/main/resources/resources-importer`, `src/main/webapp/WEB-INF`,\nand `src/main/webapp/css` folders for you. Add your style modifications to the\nprovided `css/_custom.scss` file. For a complete explanation of a theme's files,\nsee the\n[Theme Reference Guide](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide).\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/themes/simple-theme)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/wars/simple-theme)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/themes/simple-theme)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/05-themes/template-context-contributor.markdown",
    "content": "---\nheader-id: template-context-contributor\n---\n\n# Template Context Contributor\n\n[TOC levels=1-4]\n\nThe Template Context Contributor sample injects a new variable into @product@'s\ntheme context. When deploying this sample with no customizations, you can use\nthe `${sample_text}` variable from any theme.\n\n## What API(s) and/or code components does this sample highlight?\n\nMany developers prefer using templating frameworks like FreeMarker and Velocity,\nbut don't have access to the common objects offered to those working with JSPs.\nContext contributors allow non-JSP developers an easy way to inject variables\ninto their Liferay templates.\n\nThis sample leverages the\n[TemplateContextContributor](@product-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/template/TemplateContextContributor.html)\nAPI.\n\n## How does this sample leverage the API(s) and/or code component?\n\nYou can easily modify this sample by customizing its\n`BladeTemplateContextContributor.java` Java class. For example, the default\ncontext contributor sample provides the `${sample_text}` variable by injecting\nit into Liferay's `contextObjects`, which is a map provided by default to offer\ncommon variables to non-JSP template developers. You can easily inject your own\nvariables into the `contextObjects` map usable by any theme deployed to\n@product@.\n\nAre you working with templates that aren't themes (e.g., ADTs, DDM templates,\netc.)? You can change the context in which your variables are injected by\nmodifying the `property` attribute in the `@Component` annotation. If you want\nyour variable available for all templates, change it to\n\n```java\nproperty = {\"type=\" + TemplateContextContributor.TYPE_GLOBAL}\n```\n\nFor more information on customizing the Template Context Contributor sample to\nfit your needs, see the Javadoc listed in this sample's\n`com.liferay.blade.samples.theme.contributorBladeTemplateContextContributor`\nclass. For more information on context contributors and how to create them in\n@product@, visit the\n[Context Contributors](/docs/7-2/frameworks/-/knowledge_base/f/injecting-additional-context-variables-and-functionality-into-your-theme-templates)\ntutorial.\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/themes/template-context-contributor)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/themes/template-context-contributor)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/themes/template-context-contributor)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/05-themes/theme-contributor.markdown",
    "content": "---\nheader-id: theme-contributor\n---\n\n# Theme Contributor\n\n[TOC levels=1-4]\n\nThe Theme Contributor sample contributes updates to the UI of the theme\nbody, Control Menu, Product Menu, and Simulation Panel. When deploying this\nsample with no customizations, the colors of the theme and aforementioned menus\nare updated.\n\n![Figure 1: Your @product@ pages and menu fonts now have a yellow tint.](../../../images/theme-contributor-yellow.png)\n\nAlso, there's a simple JavaScript update that is provided, which logs a message\nto the browser's console window that states *Hello Blade Theme Contributor!*.\n\n![Figure 2: The message is printed to your browser's console window using JavaScript.](../../../images/theme-contributor-console-output.png)\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample demonstrates a way to contribute updates to a @product@ theme. Theme\nContributors let you package UI resources (e.g., CSS and JS) independent of a\ntheme to include on a @product@ page.\n\n## How does this sample leverage the API(s) and/or code component?\n\nTo modify this sample, replace the corresponding JS or SCSS file with the\nJavaScript or styles that you want, or add your own JS or SCSS files. For\nexample, this sample provides an update to the Control Menu's `background-color`\nin its\n`src/main/resources/META-INF/resources/css/blade.theme.contributor/_control_menu.scss`\nfile:\n\n```css\nbody {\n        .control-menu {\n                background-color: darkkhaki;\n        }\n}\n```\n\nAll of the SCSS files used in this sample are imported into the main\n`blade.theme.contributor.scss` file:\n\n```css\n@import \"bourbon\";\n@import \"mixins\";\n\n@import \"blade.theme.contributor/body\";\n@import \"blade.theme.contributor/control_menu\";\n@import \"blade.theme.contributor/product_menu\";\n@import \"blade.theme.contributor/simulation_panel\";\n```\n\nIf you add your own `SCSS` files, you must add them to the list of imports in \nthe `blade.theme.contributor.scss` file.\n\nLikewise, the sample `blade.theme.contributor.js` logs a message to your\nbrowser's console window using the following JS logic:\n\n```js\nconsole.log('Hello Blade Theme Contributor!');\n```\n\nFor more information on Theme Contributors, visit the\n[Theme Contributors](/docs/7-2/frameworks/-/knowledge_base/f/packaging-independent-ui-resources-for-your-site)\ntutorial.\n\n## Where Is This Sample?\n\nThere are three different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/themes/theme-contributor)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/themes/theme-contributor)\n- [Maven](https://github.com/liferay/liferay-blade-samples/tree/7.2/maven/themes/theme-contributor)\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/06-ext/01-intro.markdown",
    "content": "---\nheader-id: ext\n---\n\n# Ext\n\n[TOC levels=1-4]\n\nThis section focuses on Liferay Ext modules. You can view these sample\napps by visiting the `ext` folder corresponding to your preferred build tool:\n\n- [Gradle sample apps](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/ext)\n- [Liferay Workspace sample apps](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/ext)\n\n<!-- No Maven Ext samples are available at this time. -Cody\n- [Maven sample apps](https://github.com/liferay/liferay-blade-samples/tree/7.1/maven/ext)\n-->\n\nVisit the sample page to learn more!\n"
  },
  {
    "path": "en/developer/reference/articles/02-sample-projects/06-ext/login-web-ext.markdown",
    "content": "---\nheader-id: login-web-ext\n---\n\n# Login Web Ext\n\n[TOC levels=1-4]\n\nThe Login Ext Module sample demonstrates how to customize a default Liferay\nmodule's source code. This example replaces the default `login.jsp` file in the\n`com.liferay.login.web` bundle by adding the text *Hello from\ncom.liferay.login.web.ext module! 2 + 2 = 4* to the Sign In form.\n\n![Figure 1: The Login Ext module customizes the original Login module.](../../../images/login-ext.png)\n\nIt also prints the following text to the console when you select *Forgot\nPassword* from the Sign In form:\n\n    In com.liferay.login.web.internal.portlet.action.ForgotPasswordMVCRenderCommand render\n\nBefore deploying the sample, you must stop the original bundle you intend to\noverride. This is because the Ext sample's generated JAR includes the original\nbundle source plus your modified source files. Follow the instructions below to\ndo this:\n\n1.  Connect to your portal instance using\n    [Gogo Shell](/docs/7-1/reference/-/knowledge_base/r/using-the-felix-gogo-shell).\n\n2.  Search for the bundle ID of the original bundle to override. To find the\n    `com.liferay.login.web` bundle, execute this command:\n\n    ```bash\n    lb -s | grep com.liferay.login.web\n    ```\n\n    This returns output similar to this:\n\n    ```bash\n    1580|Active   |   10|com.liferay.login.web (4.0.5)\n    ```\n\n    Make note of the ID (e.g., `1580`).\n\n3.  Stop the bundle:\n\n    ```bash\n    stop 1580\n    ```\n\nOnce the original bundle is stopped, deploy the Ext module. Note that you cannot\nleverage Blade or Gradle's `deploy` command to do this. The `deploy` command\ndeploys the module to the `osgi\\marketplace\\override` folder by default, which\ndoes not configure Ext modules properly for usage. You should build and copy the\nExt module's JAR to the `deploy` folder manually, or leverage Liferay Dev\nStudio's [deployment](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project#liferay-dev-studio)\nfeature.\n\n## What API(s) and/or code components does this sample highlight?\n\nThis sample demonstrates how to create an Ext module and configure it to replace\na default module bundle.\n\n## How does this sample leverage the API(s) and/or code component?\n\nYou can create your own Ext module project by\n\n- Declaring the original module name and version.\n- Providing the source code that will replace the original.\n\nTo declare the original module in the `build.gradle` file properly (only\nsupports Gradle), you must specify the original module's Bundle Symbolic Name\nand the original module's exact version. In this example, this is configured\nlike this:\n\n```groovy\noriginalModule group: \"com.liferay\", name: \"com.liferay.login.web\", version: \"4.0.5\"\n```\n\nIf you're leveraging\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace),\nyou should put your Ext module project in the `/ext` folder (default); you can\nspecify a different Ext folder name in workspace's `gradle.properties` by adding\n\n```properties\nliferay.workspace.ext.dir=EXT_DIR\n```\n\nIf you are developing an Ext module project in standalone mode (not associated\nwith Liferay Workspace), you must declare the Ext Gradle plugin in your\n`build.gradle`:\n\n```groovy\napply plugin: 'com.liferay.osgi.ext.plugin'\n```\n\nThen you must provide your own code intended to replace the original one. **Be\nsure to mimic the original module's folder structure when overriding its JAR.**\n\nThe following file types can be overlaid with an Ext module:\n\n- CSS\n- Java\n- JavaScript\n- Language files (`Language.properties`)\n- Scss\n- Soy\n- etc.\n\nThe\n[Ext Gradle Plugin](https://github.com/liferay/liferay-portal/blob/master/modules/sdk/gradle-plugins/src/main/java/com/liferay/gradle/plugins/LiferayOSGiExtPlugin.java)\nhelps compile your code into the JAR. For example, `.scss` files are compiled\ninto `.css` files, which are included in your module's JAR file artifact. This\nis done by the `buildCSS` task.\n\n## Where Is This Sample?\n\nThere are two different versions of this sample, each built with a different\nbuild tool:\n\n- [Gradle](https://github.com/liferay/liferay-blade-samples/tree/7.2/gradle/ext/login-web-ext)\n- [Liferay Workspace](https://github.com/liferay/liferay-blade-samples/tree/7.2/liferay-workspace/ext/login-web-ext)\n"
  },
  {
    "path": "en/developer/reference/articles/02-segmentation-and-personalization/01-intro.markdown",
    "content": "---\nheader-id: segmentation-and-personalization-reference\n---\n\n# Segmentation and Personalization Reference\n\n[TOC levels=1-4]\n\nBrowse this section's reference articles for additional information on the\nSegmentation and Personalization framework.\n"
  },
  {
    "path": "en/developer/reference/articles/02-segmentation-and-personalization/02-defining-segment-criteria.markdown",
    "content": "---\nheader-id: defining-segmentation-criteria\n---\n\n# Defining Segmentation Criteria\n\n[TOC levels=1-4]\n\nThere are three categories for defining Segment criteria:\n\n- User Properties\n- Organization Properties\n- Session Properties\n\nThere are several types of information that can be collected by the User \nSegment interface. Some data is entered in text boxes, while others use \nselectors to select specific criteria or tools like a date picker. In addition, \nsome fields use an operator, which, depending on the specific context lets you \nselect the relationship between the user or agent data and the criteria:\n\n- *equals*\n\n- *not equals*\n\n- *greater than*\n\n- *greater than or equals*\n\n- *less than*\n\n- *less than or equals*\n\n- *contains*\n\n- *does not contain*\n\nDepending on the nature of the criteria, the operator selection may contain \ndifferent combinations. For example, the *Date* selection described below \ncontains options for all the above option except *contains* and *does not \ncontain*, whereas the *Email Address* selection has *equals*, *not equals*, \n*contains* and *does not contain*.\n\nIn between each criteria and each category, you can define an \"and\" or \"or\" \nconjunction. For \"and\" all criteria must be true in order for the criteria to be satisfied. With \"or\" it will be true if any of the defined criteria are true. You can also mix operators to create complex cases.\n\n## User Properties\n\nThe following are the criteria available for defining user properties:\n\n**Date Modified:** Provides a date picker and an relationship selector to select\nthe date that user information was last changed\n\n**Email Address:** Provides a text box to enter the email provided in the user's  \nprofile.\n\n**First Name:** Enter the first name provided in the user's profile.\n\n**Group:** Select a site that the user is a member of.\n\n**Job Title:** Enter the job title provided in the user's profile.\n\n**Last Name:** Enter the last name provided in the user's profile.\n\n**Role:** Select a role that the user is a member of.\n\n**Screen Name:** Enter the users' screen name.\n\n**Team:** Select a team that the user is a member of.\n\n**User Group:** Select a user group that the user is a member of.\n\n**User:** Select a specific user from a list.\n\n**Name:** The full name of the user.\n\n## Organization Properties\n\n**Date Modified:** Enter the date that the organization information was last modified.\n\n**Name:** Enter the name of the organization.\n\n**Hierarchy Path:** Enter the name of an ancestor organization.\n\n**Organization:** Select a specific organization.\n\n**Parent Organization:** Select a specific parent organization.\n\n**Type:** Select the type of organization, if organization types have been defined.\n\n## Session Properties\n\n**Browser:** Enter a property from the browser.\n\n**Cookies:** Enter the name of a browser cookie.\n\n**Device Brand:** Enter the brand name of the device being used.\n\n**Device Model:** Enter the model name of the device being used.\n\n**Device Screen Resolution Height:** Enter the screen resolution \nheight value.\n\n**Device Screen Resolution Width:** Enter the screen resolution width value.\n\n**Language:** Select the current Language.\n\n**Last Sign In Date:** Select the date of the user's last sign in.\n\n**Local Date:** Select the current date where the user is located.\n\n**Referrer URL:** Enter the URL that the user last visited.\n\n**Signed In:** Select whether the user is signed in.\n\n**URL:** Enter the current URL.\n\n**User Agent:** Enter a User Agent property.\n"
  },
  {
    "path": "en/developer/reference/articles/02-third-party-packages-portal-exports.markdown",
    "content": "---\nheader-id: third-party-packages-portal-exports\n---\n\n# Third Party Packages Portal Exports\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\"> This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/liferay-internals/reference/exported-third-party-packages.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe `com.liferay.portal.bootstrap` module exports many third party Java packages \nthat can cause problems if used improperly. If your WAR's Gradle file, for \nexample, uses the `compile` scope for a [dependency](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies) \nthat Liferay's OSGi runtime already provides, the dependency JAR is included in \nthe WAR's `WEB-INF/lib` and deployed in the resulting WAB, and two versions of \ndependency classes wind up on the classpath. This can cause weird errors that \nare hard to debug. \n\nTo find a list of the packages exported by `com.liferay.portal.bootstrap`, go to \nthe source file `modules/core/portal-bootstrap/system.packages.extra.bnd`. If \nyou don't have access to the source code, the same list (in a less user-friendly \nformat) is in the `META-INF/system.packages.extra.mf` file in \n`[LIFERAY_HOME]/osgi/core/com.liferay.portal.bootstrap.jar`. These packages are \ninstalled and available in Liferay's OSGi runtime. If your module or WAR uses \none of them, specify the corresponding dependency as being \"provided\" (provided \nby @product@). Here's how to specify a provided dependency:\n\nMaven: `<scope>provided</scope>`\n \nGradle: `providedCompile`\n\nNow you can safely leverage third party packages @product@ provides! \n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/00-intro.markdown",
    "content": "---\nheader-id: tooling\n---\n\n# Tooling\n\n[TOC levels=1-4]\n\nYou can write code for @product@ using any standard toolset. Liferay is\ntool-agnostic, which frees you to work with whatever you're already productive\nusing.\n\nLiferay has also created its own tools that streamline @product@ development.\nThese tools integrate with popular build environments (e.g., Gradle, Maven, and \nNodeJS). They include\n\n- [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli): a command\n  line interface used to build and manage Liferay Workspaces and @product@\n  projects. This CLI is intended for Gradle or Maven development.\n- [Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace):\n  a generated Gradle/Maven environment built to hold and manage @product@\n  projects.\n- [Liferay Dev Studio](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio):\n  an Eclipse-based IDE supporting development for @product@.\n- [Liferay IntelliJ Plugin](/docs/7-2/reference/-/knowledge_base/r/intellij):\n  a plugin providing support for @product@ development with IntelliJ IDEA.\n- [Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/theme-generator):\n  a generator that creates themes, layouts templates, and themelets for \n  @product@ development.\n- [Liferay JS Generator](/docs/7-2/reference/-/knowledge_base/r/js-generator):\n  a generator that creates JavaScript portlets with JavaScript tooling.\n\nLiferay also provides a plethora of\n[Gradle](/docs/7-2/reference/-/knowledge_base/r/gradle-plugins) and\n[Maven plugins](/docs/7-2/reference/-/knowledge_base/r/maven-plugins) you can\napply to your projects. Many of these are already built into tools such as\nLiferay Workspace.\n\nWant samples or predefined project templates? Liferay has you covered with 30+\n[project templates](/docs/7-2/reference/-/knowledge_base/r/project-templates)\nand many more\n[project samples](/docs/7-2/reference/-/knowledge_base/r/sample-projects).\n\nIf you're a newbie looking for the best development tool for @product@, or even\na seasoned veteran looking for a tool you may like more than your current setup,\nthis section answers your tooling questions. \n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/01-creating-a-project.markdown",
    "content": "---\nheader-id: creating-a-project\n---\n\n# Creating a Project\n\n[TOC levels=1-4]\n\nLiferay provides many project templates you can use to generate starter projects\nformatted in an opinionated way. Visit the [Project\nTemplates](/docs/7-2/reference/-/knowledge_base/r/project-templates)\nreference section for more information on the available project templates. Each\nproject template has different configurable options, so be sure to research\na project template before generating it.\n\nYou can use your desired tool to generate a project. The following tools are\npreconfigured for Liferay project generation:\n\n- [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli)\n- [Liferay Dev Studio](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio)\n- [Liferay IntelliJ Plugin](/docs/7-2/reference/-/knowledge_base/r/intellij)\n- [Maven](/docs/7-2/reference/-/knowledge_base/r/maven)\n\nIt's recommended to create Liferay projects within a\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace).\nMost tools, however, support creating projects in a standalone environment\n(except for IntelliJ). Visit the appropriate section to learn how to create a\nproject with the highlighted tool.\n\n## Blade CLI\n\n1.  Print the available project templates by executing this:\n\n    ```bash\n    blade create -l\n    ```\n\n    Note the project template you want to generate; you'll use it in the next\n    step.\n\n2.  Run the following command to create a Gradle project with Blade CLI:\n\n    ```bash\n    blade create -t [projectTemplate] [option1] [option2] ... [optionN] [projectName]\n    ```\n\n| **Note:** If you want to generate a project for a previous version (e.g.,\n| Liferay Portal 7.0), you can specify this using the `-v` flag. For example, to\n| create a project for Liferay Portal 7.0, you would include `-v 7.0` in your\n| create command sequence.\n\nThe available configuration options are documented for each\n[project template](/docs/7-2/reference/-/knowledge_base/r/project-templates).\nRun `blade create --help` for the entire list of available options.\n\n## Liferay Dev Studio\n\n1.  Navigate to *File* &rarr; *New* &rarr; *Liferay Module Project*.\n\n2.  Specify the project name, location, build type, @product@ version, and\n    template type.\n\n    ![Figure 1: The New Liferay Module Project wizard offers project templates for JAR and WAR-based projects.](../../images/liferay-project-wizard.png)\n\n3.  Click *Next* and you're given additional configuration options based on the\n    project template you selected. For example, if you selected a template that\n    requires a component class, you must configure it in the wizard.\n\n    ![Figure 2: Specify your component class's details in the Portlet Component Class Wizard.](../../images/component-class-wizard.png)\n\n    You can specify your component class's name, package name, and its\n    properties. The properties you assign are the ones found in the `@Component`\n    annotation's `property = {...}` assignment.\n\n    | **Note:** You can also create a new component class for a pre-existing\n    | module project. Navigate to *File* &rarr; *New* &rarr; *Liferay Component\n    | Class*. This is a similar wizard to the previous component class wizard,\n    | except you can select a component class template. \n\n4.  Click *Finish* to create your project.\n\n## Liferay IntelliJ Plugin\n\n1.  Navigate to *File* &rarr; *New* &rarr; *Liferay Module*.\n\n    ![Figure 3: Selecting *Liferay Module* opens the New Liferay Modules wizard.](../../images/intellij-new-liferay-module.png)\n\n2.  Select the project you want to create.\n\n    ![Figure 4: Choose the project template to create your module.](../../images/intellij-modules.png)\n\n3.  Configure your project's SDK (i.e., JDK), package name, class name, and\n    service name, if necessary. Then click *Next*.\n\n4.  Give your project a name. Then click *Finish*.\n\n## Maven\n\n1.  Execute the following Maven command:\n\n    ```bash\n    mvn archetype:generate -Dfilter=liferay\n    ```\n\n2.  Select the archetype you want to leverage and proceed through the\n    configuration prompts.\n\n| **Note:** Maven projects can also be generated using Blade CLI. Follow\n| [Blade CLI's](#blade-cli) project creation instructions and insert the\n| `-b maven` parameter in the Blade command.\n\nArchetypes prefixed with `com.liferay.project.templates.[TYPE]` or\n`com.liferay.faces.archetype:[TYPE]` are compatible with @product-ver@. All\nother Liferay archetypes are legacy archetypes targeted for previous versions of\n@product@.\n\nSee Maven's\n[Archetype Generation](http://maven.apache.org/archetype/maven-archetype-plugin/generate-mojo.html)\ndocumentation for further details on how to modify the Maven archetype\ngeneration process.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/02-deploying-a-project.markdown",
    "content": "---\nheader-id: deploying-a-project\n---\n\n# Deploying a Project\n\n[TOC levels=1-4]\n\nDeploying a project to @product@ can be completed using your tool of choice. The\nfollowing tools are preconfigured (or can be easily configured) for Liferay\nproject generation:\n\n- [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli)\n- [Gradle](https://gradle.org/)\n- [Liferay Dev Studio](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio)\n- [Liferay IntelliJ Plugin](/docs/7-2/reference/-/knowledge_base/r/intellij)\n- [Maven](/docs/7-2/reference/-/knowledge_base/r/maven)\n\nThe deployment process is the same across all tools; the deployment\ncommand/action builds and deploys your project based on the build tool's\ndeployment configuration. For example, leveraging Blade CLI in a default Gradle\nLiferay Workspace uses the underlying Gradle deployment configuration. The build\ntool's deployment configuration is found by reading the Liferay Home folder. The\nLiferay Home folder is preconfigured in most cases; if it's not, ways to\nconfigure it are included below. All tools support JAR and WAR-style project\ndeployment.\n\nIt's recommended to deploy Liferay projects within a\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace).\nMost tools, however, support deploying projects in a standalone environment\n(except for IntelliJ). Visit the appropriate section to learn how to deploy a\nproject with the highlighted tool.\n\n## Blade CLI\n\nThis is the recommended way to deploy Gradle and Maven projects in a Liferay\nWorkspace via command line. Blade CLI is leveraged by Dev Studio and IntelliJ\ntoo.\n\nRun this command to deploy your project:\n\n```bash\nblade deploy\n```\n\nIf you prefer not to use your underlying build tool's (Gradle or Maven) module\ndeployment configuration, and instead, you want to deploy straight to\n@product@'s OSGi container, run this command:\n\n```bash\nblade deploy -l\n```\n\n## Gradle\n\nDeploying with pure Gradle is not recommended unless you prefer to develop\noutside of a Liferay Workspace. [Blade CLI](#blade-cli) is a better tool for\ndeploying Liferay Gradle projects in most cases.\n\n1.  Apply the Liferay Gradle plugin in your project's `build.gradle` file:\n\n    ```groovy\n    apply plugin: \"com.liferay.plugin\"\n    ```\n\n2.  Extend the Liferay extension object to set your Liferay Home and `deploy`\n    folder:\n\n    ```groovy\n    liferay {\n        liferayHome = \"../../../../liferay-ce-portal-7.1.1-ga2\"\n        deployDir = file(\"${liferayHome}/deploy\")\n    }\n    ```\n\n3.  Run this command to deploy your project:\n\n    ```bash\n    ./gradlew deploy\n    ```\n\n## Liferay Dev Studio\n\nThese steps assume you've\n[installed a Liferay server in Dev Studio](/docs/7-2/reference/-/knowledge_base/r/installing-a-liferay-server-in-dev-studio). \n\n1.  Right-click the server from the Servers window and select *Add and\n    Remove...*.\n\n2.  Add the project(s) you'd like to deploy from the Available window to the\n    Configured window. Then click *Finish*.\n\n    ![Figure 1: Using the this deployment method is convenient when deploying multiple projects.](../../images/add-and-remove-ide.png)\n\n3.  Verify your project builds, deploys, and starts successfully by viewing the\n    results in the Console window.\n\nDev Studio's deployment mechanism executes the `watch` task behind the scenes.\nFor more information on what to expect from the `watch` task, see\n[this article](/docs/7-2/reference/-/knowledge_base/r/blade-cli).\n\n## Liferay IntelliJ Plugin\n\nThese steps assume you've\n[installed a Liferay server in IntelliJ](/docs/7-2/reference/-/knowledge_base/r/installing-a-server-in-intellij).\nA configured Liferay Workspace is required to create and deploy Liferay projects\nin IntelliJ.\n\n1.  Right-click your project from within the Liferay Workspace folder structure\n    and select *Liferay* &rarr; *Deploy*.\n\n    This automatically loads a build progress window viewable at the bottom of\n    your IntelliJ instance.\n\n    ![Figure 2: Verify that your project built successfully.](../../images/intellij-project-build.png)\n\n2.  Verify that your project builds successfully from the build progress window.\n    Then navigate back to your server's window and confirm it starts in your\n    configured @product@ instance.\n\n## Maven\n\nIf you're developing your project in a Liferay Workspace, skip to step 3.\n\n1.  Add the following plugin configuration to your Liferay Maven project's\n    parent `pom.xml` file.\n\n    ```xml\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.liferay</groupId>\n                <artifactId>com.liferay.portal.tools.bundle.support</artifactId>\n                <version>3.4.1</version>\n                <executions>\n                    <execution>\n                        <id>deploy</id>\n                        <goals>\n                            <goal>deploy</goal>\n                        </goals>\n                        <phase>pre-integration-test</phase>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n    ```\n\n    This POM configuration applies Liferay's\n    [Bundle Support plugin](/docs/7-2/reference/-/knowledge_base/r/bundle-support-plugin).\n    This plugin is applied in Liferay Workspace by default. The Bundle Support\n    configuration defines the\n    [executions](https://maven.apache.org/guides/mini/guide-configuring-plugins.html#Using_the_executions_Tag)\n    tag, which configures the Bundle Support plugin to run during the\n    `pre-integration-test` phase of your Maven project's build lifecycle. The\n    [`deploy`](http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#A_Build_Phase_is_Made_Up_of_Plugin_Goals)\n    goal is defined for that lifecycle phase. \n\n2.  Define your Liferay home folder in your POM. You can do this by adding the\n    following logic within the `plugin` tags, but outside of the `execution`\n    tags:\n\n    ```xml\n    <configuration>\n        <liferayHome>C:/liferay/liferay-ce-portal-7.1-ga1</liferayHome>\n    </configuration>\n    ```\n\n3.  Run this command to deploy your project:\n\n    ```bash\n    mvn verify\n    ```\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/01-intro.markdown",
    "content": "---\nheader-id: blade-cli\n---\n\n# Blade CLI\n\n[TOC levels=1-4]\n\n[Blade CLI](https://github.com/liferay/liferay-blade-cli/) is a command line\ntool that makes it easy for Liferay developers to create, manage, and deploy\nLiferay projects (Gradle or Maven). Blade CLI can\n\n- Create Liferay projects usable in any IDE or development environment\n- Create/manage @product@ instances\n- Deploy Liferay projects\n- And more\n\nThe table below describes all Blade CLI commands for the latest Blade CLI\nrelease. \n\nCommand | Description\n------- | -------------\n`convert` | Converts a Plugins SDK plugin project to a Gradle Workspace project. See the [Running the Migration Command](/docs/7-1/reference/-/knowledge_base/r/migrating-traditional-plugins-to-workspace-web-applications#running-the-migration-command) command for details.\n`create` | Creates a new Liferay project from available templates. See the [Creating a Project section for Blade CLI](/docs/7-2/reference/-/knowledge_base/r/creating-a-project#blade-cli) for more information.\n`deploy` | Builds and deploys projects to @product@. See the [Deploying a Project section for Blade CLI](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project#blade-cli) for more information.\n`extension install` | Installs an extension into Blade CLI.\n`extension uninstall` | Uninstalls an extension from Blade CLI.\n`gw` | Executes a Gradle command using the Gradle Wrapper, if detected (e.g., `blade gw tasks`).\n`help` | Provides information for Blade CLI's commands.\n`init` | Initializes a new Liferay Workspace. See the [Creating a Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/creating-a-liferay-workspace#blade-cli) article for more information.\n`samples` | Generates a sample project. See the [Generating Project Samples with Blade CLI](/docs/7-2/reference/-/knowledge_base/r/generating-project-samples-with-blade-cli) article for more information.\n`server init` | Initializes the Liferay server configured in Liferay Workspace's `gradle.properties` file. Set the `liferay.workspace.bundle.url` property to configure the server to initialize.\n`server start` | Starts the Liferay server in the background. You can add the `-d` flag to start the server in debug mode. Debug mode can be customized by adding the `-p` tag to set the custom remote debugging port (defaults are `8000` for Tomcat and `8787` for Wildfly) and/or the boolean `-s` tag to set whether you want to suspend the started server until the debugger is connected. See the [Managing Your Liferay Server with Blade CLI](/docs/7-2/reference/-/knowledge_base/r/managing-your-liferay-server-with-blade-cli) article for more information.\n`server stop` | Stops the Liferay server.\n`server run` | Starts the Liferay server in the foreground. See the `server start` property for more information.\n`sh` | Connects to @product@, executes succeeding Gogo command, and returns output. For example, `blade sh lb` lists @product@'s bundles using the Gogo shell. See the [Managing Your Liferay Server with Blade CLI](/docs/7-2/reference/-/knowledge_base/r/managing-your-liferay-server-with-blade-cli) article for more information.\n`update` | Updates Blade CLI to the latest version. See the [Updating Blade CLI](/docs/7-2/reference/-/knowledge_base/r/updating-blade-cli) article for details.\n`upgradeProps` | Analyzes your old `portal-ext.properties` and your newly installed 7.x server to show you properties moved to OSGi configuration files or removed from the product. \n`watch` | Watches for changes to a deployed project and automatically redeploys it when changes are detected. This command does not rebuild your project and copy it to Portal every time a change is detected, but rather, installs it into the runtime as a reference. This means that the Portal does not make a cached copy of the project. This allows the Portal to see changes that are made to your project's files immediately. When you cancel the `watch` task, your module is uninstalled automatically. The `blade deploy -w` command works similarly to `blade watch`, except it manually recompiles and deploys your project every time a change is detected. This causes slower update times, but does preserve your deployed project in Portal when it's shut down.\n`version` | Displays version information about Blade CLI.\n\n<!-- TODO: Find more about new `upgradeProps` property. -Cody-->\n\nFor information on command options, run the command with the `--help` flag\n(e.g., `blade samples --help`).\n\nContinue on to learn about leveraging Blade CLI to create and test @product@\ninstances and projects.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/02-installing-blade-cli.markdown",
    "content": "---\nheader-id: installing-blade-cli\n---\n\n# Installing Blade CLI\n\n[TOC levels=1-4]\n\nYou can install Blade CLI using the Liferay Project SDK installer. This installs\nJPM and Blade CLI into your user home folder and optionally initializes a\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)\nfolder. Do not use the Liferay Project SDK installer to update Blade CLI;\ninstead, follow the instructions for\n[updating Blade CLI](/docs/7-2/reference/-/knowledge_base/r/updating-blade-cli).\n\nIf you must configure proxy settings for Blade CLI, follow the\n[Installing Blade CLI with Proxy Requirements](/docs/7-2/reference/-/knowledge_base/r/installing-blade-cli-with-proxy-requirements) instructions instead.\n\nFollow the steps below to download and install Blade CLI:\n\n1.  Download the latest\n    [Liferay Project SDK installer](https://sourceforge.net/projects/lportal/files/Liferay%20IDE/)\n    that corresponds with your operating system (e.g., Windows, MacOS, or\n    Linux). The Project SDK installer is listed under *Liferay IDE*, so the\n    folder versions are based on IDE releases. You can select an installer\n    without @ide@ if you don't intend to use it. The Project SDK installer is\n    available for versions 3.2.0+. Do **not** select the large\n    green download button; this downloads Liferay Portal instead.\n\n2.  Run the installer. \n\n3.  Select the Java Runtime to use with Blade CLI. Then click *OK*.\n\n4.  Click *Next* to step through the installer's introduction.\n\n5.  If you'd like to initialize a Liferay Workspace, you can set its location. \n\n    ![Figure 1: Determine where your Liferay Workspace should reside, if you want one.](../../../images/blade-installer-workspace-init.png)\n\n    Select *Don't initialize Liferay Workspace directory* if you only want to\n    install Blade CLI. Then click *Next*.\n\n6.  If you initialized a Liferay Workspace folder, an additional option appears\n    for selecting the Liferay product type to use with your workspace. Choose\n    the product type and click *Next*.\n\n    ![Figure 2: Select the product version you'll use with your Liferay Workspace.](../../../images/installer-workspace-type.png)\n\n7.  Click *Next* to begin installing Blade CLI/Liferay Workspace on your\n    computer.\n\nThat's it! Blade CLI is installed on your machine! If you specified a location\nto initialize a Liferay Workspace folder, that is also available.\n\nIf Blade CLI doesn't work properly on your machine, visit the\n[Common Errors with Blade CLI](/docs/7-2/reference/-/knowledge_base/r/common-errors-with-blade-cli)\narticle for solutions to common problems.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/03-installing-blade-cli-with-proxy-requirements.markdown",
    "content": "---\nheader-id: installing-blade-cli-with-proxy-requirements\n---\n\n# Installing Blade CLI with Proxy Requirements\n\n[TOC levels=1-4]\n\nIf you have proxy server requirements and want to use Blade CLI, you must\nconfigure your http(s) proxy for it using JPM: \n\n1.  Install JPM and Blade CLI using the Liferay Project SDK installer. Read the\n    [Installing Blade CLI](/docs/7-2/reference/-/knowledge_base/r/installing-blade-cli)\n    tutorial for more details.\n\n2.  Execute the following command to configure your proxy requirements for Blade\n    CLI:\n\n    ```bash\n    jpm command --jvmargs \"-Dhttp(s).proxyHost=[your proxy host] -Dhttp(s).proxyPort=[your proxy port]\" jpm\n    ```\n\nExcellent! You've configured Blade CLI with your proxy settings using JPM.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/04-managing-your-liferay-server-with-blade-cli.markdown",
    "content": "---\nheader-id: managing-your-liferay-server-with-blade-cli\n---\n\n# Managing Your Liferay Server with Blade CLI\n\n[TOC levels=1-4]\n\nYou can  manage a Liferay server using Blade CLI. Managing a server with Blade\nCLI should be done in a \n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace).\n\nBlade CLI has commands for installing, starting, stopping, inspecting, and\nmodifying a Liferay server:\n\n1.  Make sure you've created a Liferay Workspace. See the\n    [Creating a Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/creating-a-liferay-workspace#blade-cli)\n    article for more information.\n\n2.  Initialize a Liferay server by running\n\n    ```bash\n    blade server init\n    ```\n\n    This downloads the @product@ bundle set in your workspace's\n    `gradle.propeties` file. See the\n    [Adding a Liferay Bundle to Workspace](/docs/7-2/reference/-/knowledge_base/r/adding-a-liferay-bundle-to-liferay-workspace)\n    article for more information.\n\n    You can initialize a server based on a\n    [defined environment](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace#testing-projects)\n    by running the following command:\n\n    ```bash\n    blade server init --environment [ENVIRONMENT]\n    ```\n\n    For example, you could pass in the `uat` variable to generate a bundle\n    with the configs set in the `configs/uat` workspace folder.\n\n3.  Start your Liferay server (Tomcat or Wildfly/JBoss) by running\n\n    ```bash\n    blade server start\n    ```\n\n    This starts the server in the background. You can tail the logs by adding\n    the `-t` flag. If you prefer starting the server in the foreground, run\n    `blade server run`. Additionally, if you prefer starting the server in debug\n    mode, add the `-d` flag. See the\n    [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli) article for\n    additional flags you can set when starting your Liferay server.\n\n4.  Examine your server's OSGi container by using Blade CLI's `sh` command,\n    which provides access to your server using the Felix Gogo shell. For\n    example, to check if you successfully deployed your application from the\n    previous section, you could run:\n\n    ```bash\n    blade sh lb\n    ```\n\n    Your output lists a long list of modules that are active/installed in your\n    server's OSGi container.\n\n    ![Figure 1: Blade CLI accesses the Gogo shell script to run the `lb` command.](../../../images/blade-sh.png)\n\n    You can run any Gogo command using `blade sh`. This command requires\n    [Developer Mode](/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes)\n    to be enabled. Developer Mode is enabled in workspace by default. See the\n    [Using the Felix Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\n    section for more information on this tool.\n\n5.  Once you're finished modifying your Liferay bundle, you can package it as a\n    sharable file by running this command:\n\n    ```bash\n    blade gw distBundle[Zip|Tar]\n    ```\n\n    This lets you create a ZIP or TAR file to share with others. This option is\n    only available with Gradle at this time. The above command leverages Blade\n    CLI's `gw` option, which executes the project's Gradle wrapper.\n\n    | **Note:** You can avoid deploying a module inside your workspace's\n    | `modules/` folder when `distBundle[Zip|Tar]` is called by adding the\n    | following snippet to your workspace's `build.gradle` file:\n    | \n    | ```groovy\n    | distBundle {\n    |     exclude \"com.liferay.jsp.spy*.jar\"\n    | }\n    | ```\n    | \n    | You can replace the JAR name above with the module JAR you want to exclude.\n    | This is useful for those who want to have a module in their workspace that\n    | is used for development or debug purposes only, and it should not be deployed\n    | to production. This works for Gradle builds only at this time.\n\n    <!-- TODO: Add way for producing a distributable workspace using Blade, when\n    available. It can only be done currently with ./gradlew distBundle[Zip|Tar].\n    -->\n\n6.  Turn off your Liferay server:\n\n    ```bash\n    blade server stop\n    ```\n\nTo reference all of Blade CLI's available options, see the\n[Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli) article.\n\nAwesome! You learned how to interact with @product@ using Blade CLI.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/05-generating-project-samples-with-blade-cli.markdown",
    "content": "---\nheader-id: generating-project-samples-with-blade-cli\n---\n\n# Generating Project Samples with Blade CLI\n\n[TOC levels=1-4]\n\nLiferay provides many useful \n[sample projects](https://github.com/liferay/liferay-blade-samples) for those\ninterested in learning best practices for @product@ projects. You can learn more\nabout these samples by visiting \n[Sample Projects](/docs/7-2/reference/-/knowledge_base/r/sample-projects).\n\nRather than cloning the repository, you can generate these samples using Blade\nCLI for convenience.\n\n1.  List the available sample projects:\n\n    ```bash\n    blade samples\n    ```\n\n    Note the sample project you want to generate; you'll use it in the next\n    step.\n\n2.  Run the following command to generate a sample project:\n\n    ```bash\n    blade samples <NAME>\n    ```\n\n    For example, to generate the\n    [portlet-ds](https://github.com/liferay/liferay-blade-samples/tree/master/gradle/apps/ds-portlet)\n    sample, execute\n\n    ```bash\n    blade samples ds-portlet\n    ```\n\n    The sample is generated in the current folder.\n\n| **Note:** Interested in generating legacy versions of Blade samples? Pass in the\n| `-v` param followed by the @product@ version to target. For example,\n| \n| ```bash\n| blade samples -v 7.0 ds-portlet\n| ```\n\nAwesome! You've successfully generated a Liferay sample project using Blade CLI!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/06-updating-blade-cli.markdown",
    "content": "---\nheader-id: updating-blade-cli\n---\n\n# Updating Blade CLI\n\n[TOC levels=1-4]\n\nBlade CLI is updated frequently, so you should update your Blade CLI\nenvironment for new features. You can check the released versions of Blade CLI\non Nexus by inspecting the\n[`com.liferay.blade.cli`](https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/blade/com.liferay.blade.cli/)\nartifact. You can check your current installed version by running `blade\nversion`.\n\nTo update your Blade CLI installation to the latest stable version, run\n\n```bash\nblade update\n```\n\nAlthough Blade CLI is frequently released, if you want bleeding edge features\nnot yet available, you can install the latest snapshot version:\n\n```bash\nblade update -s\n```\n\nThis pulls the latest snapshot version of Blade CLI and installs it to your\nlocal machine. Running `blade version` after installing a snapshot displays\noutput similar to this:\n\n```bash\nblade version 3.3.1.SNAPSHOT201811301746\n```\n\nBe careful; snapshot versions are unstable and should only be used for\nexperimental purposes.\n\nAwesome! You've successfully learned how to update Blade CLI.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/07-converting-plugins-sdk-projects-with-blade-cli.markdown",
    "content": "---\nheader-id: converting-plugins-sdk-projects-with-blade-cli\n---\n\n# Converting Plugins SDK Projects with Blade CLI\n\n[TOC levels=1-4]\n\nBlade CLI can automatically migrate a Plugins SDK project to a Liferay\nWorkspace. During the process, the Ant-based Plugins SDK project is copied to\nthe applicable workspace folder based on its project type (e.g., `wars`) and is\nconverted to a Gradle-based Liferay Workspace project. This drastically speeds\nup the migration process when upgrading to a Liferay Workspace from a legacy\nPlugins SDK.\n\n| **Note:** There is no Maven command for the migration process yet, so you\n| must complete it manually for Maven-based workspaces.\n\nTo copy your Plugins SDK project and convert it to Gradle, use the Blade\n`convert` command:\n\n1.  Navigate to the root folder of your workspace in a command line tool.\n\n2.  Execute the following command:\n\n    ```bash\n    blade convert -s [PLUGINS_SDK_PATH] [PLUGINS_SDK_PROJECT_NAME]\n    ```\n\n    You must provide the path of the Plugins SDK your project resides in and the\n    project name you want to convert. If you prefer converting all the Plugins\n    SDK projects at once, replace the project name variable with `-a` (i.e.,\n    specifying all plugins).\n\n    | **Note:** If the `convert` task doesn't work as described above, you may\n    | need to update your Blade CLI version. See the\n    | [Updating Blade CLI](/docs/7-2/reference/-/knowledge_base/r/updating-blade-cli)\n    | article for more information.\n\n    This Gradle conversion process also works for themes; they're converted to\n    automatically leverage NodeJS. If you're converting a Java-based theme, add\n    the `-t` option to your command too. This will incorporate the\n    [Theme Builder](/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin)\n    Gradle plugin for the theme instead. For more information on upgrading\n    6.2 themes, see the\n    [Upgrade a 6.2 Theme to 7.2](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-6-2-themes-to-7-2).\n\n| **Note:** When converting a Service Builder project, the `convert` task\n| automatically extracts the project's service interfaces and implementations\n| into OSGi modules (i.e., *-impl and *-api) and places them in the workspace's\n| `modules` folder. Your portlet and controller logic remain a WAR and\n| reside in the `wars` folder.\n\nYour project is successfully converted to a Gradle-based workspace project!\nGreat job!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/08-extending-blade-cli/01-intro.markdown",
    "content": "---\nheader-id: extending-blade-cli\n---\n\n# Extending Blade CLI\n\n[TOC levels=1-4]\n\nYou can extend Blade in three different ways: \n\n- [Creating Custom Commands for Blade CLI](/docs/7-2/reference/-/knowledge_base/r/creating-custom-commands-for-blade-cli)\n- [Creating Custom Project Templates for Blade CLI](/docs/7-2/reference/-/knowledge_base/r/creating-custom-project-templates-for-blade-cli)\n- [Installing New Extensions for Blade CLI](/docs/7-2/reference/-/knowledge_base/r/installing-new-extensions-for-blade-cli)\n\nThere are a few use cases to consider when extending Blade CLI. For example, if\nyou only want to add a new command that applies globally to all types of\nworkspaces, you can create and install a new custom command as explained in the\nlinks above.\n\nAlternatively, you may want a set of custom commands that only apply to a\nspecific workspace environment. Normally, Liferay developers who use Blade CLI\nrun a series of Blade commands that make sense in the *default* Liferay\nWorkspace. What if the workspace, however, should support a containerized\nenvironment (e.g., Docker) or some other specialized environment? The commands\nused in the development workflow must complete the workflow differently.\n\nTo customize Blade CLI's development workflow, you must create a Blade\n*profile*. Blade profiles *override* existing Blade commands or add *new*\ncommands in a preserved environment that can be applied to any Liferay\nWorkspace. For example, `blade init` for a profile `myprofile` would override\nthe default `init` command to do something before/after the normal `init`\ncommand. For more information, see \n[Creating a Blade Profile](/docs/7-2/reference/-/knowledge_base/r/creating-a-blade-profile).\n\n| **Note:** Blade CLI leverages the profile system internally for Maven support.\n| The Maven specific code is stored in an extension JAR and embedded inside the\n| default Blade JAR.\n\nContinue on to learn more!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/08-extending-blade-cli/02-creating-custom-commands-for-blade-cli.markdown",
    "content": "---\nheader-id: creating-custom-commands-for-blade-cli\n---\n\n# Creating Custom Commands for Blade CLI\n\n[TOC levels=1-4]\n\nTo create a custom command for Blade CLI, follow these steps:\n\n| **Note:** This article creates a Gradle-based command project. These steps can\n| be completed for a Maven-based project too.\n\n1.  [Create a generic OSGi module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project).\n\n2.  You'll leverage [JCommander](http://jcommander.org/) and the Blade CLI API\n    to create your custom command. Add these dependencies in your build file.\n    For example, a `build.gradle` file's `dependencies` block looks like this:\n\n    ```groovy\n    dependencies {\n        compileOnly group: \"com.beust\", name: \"jcommander\", version: \"1.72\"\n        compileOnly group: \"com.liferay.blade\", name: \"com.liferay.blade.cli\", version: \"latest.release\"\n    }\n    ```\n\n3.  Build a Command class by extending the\n    [`BaseCommand`](https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BaseCommand.java)\n    class:\n\n    ```java\n    import com.liferay.blade.cli.command.BaseCommand;\n\n    public class Hello extends BaseCommand<HelloArgs> {\n\n        @Override\n        public void execute() throws Exception {\n            HelloArgs helloArgs = getArgs();\n\n            getBladeCLI().out(\"Hello \" + helloArgs.getName());\n        }\n\n        @Override\n        public Class<HelloArgs> getArgsClass() {\n            return HelloArgs.class;\n        }\n\n    }\n    ```\n\n    This registers your new command with Blade. You must define the `execute()`\n    command for all classes extending `BaseCommand`. The `BaseCommand` class\n    expects an arguments class as its parameter. You'll create this next.\n\n4.  Create a class that holds your command's arguments:\n\n    ```java\n    import com.beust.jcommander.Parameter;\n    import com.beust.jcommander.Parameters;\n\n    import com.liferay.blade.cli.command.BaseArgs;\n\n    @Parameters(commandDescription = \"Executes a hello command\", commandNames = \"hello\")\n    public class HelloArgs extends BaseArgs {\n\n        public String getName() {\n            return _name;\n        }\n\n        @Parameter(description = \"The name to say hello to\", names = \"--name\", required = true)\n        private String _name;\n\n    }\n    ```\n\n    This class extends the\n    [`BaseArgs`](https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BaseArgs.java)\n    class. Notice that the class declaration has the `@Parameters` JCommander\n    annotation. This sets your command's description and name. The `@Parameter`\n    annotation applied to the private string `_name` sets how the command's\n    parameter is called and whether it's required.\n\n5.  Since Blade looks for custom commands using the\n    `com.liferay.blade.cli.command.BaseCommand` service interface, you must use\n    a standard JRE service loader mechanism to finish registering your new\n    command with Blade CLI.\n\n    Create a file named `com.liferay.blade.cli.command.BaseCommand` in the\n    `src/main/resources/META-INF/services/` folder. This class should list all\n    of your custom commands' fully qualified class names:\n\n    ```\n    com.liferay.extensions.sample.command.Hello\n    ```\n\n    | **Note:** Java's Service Loader Interface (SPI) is used to load the\n    | fully qualified classes in the `META-INF/services` folder. You can learn\n    | more about SPIs\n    | [here](https://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html).\n\n6.  Generate the extension's JAR file (e.g., `gradlew build`).\n\nAwesome! You've created a custom command! You can deploy multiple custom\ncommands in a single JAR, so you can continue adding custom command projects to\nthis module, if desired. See the\n[Installing New Extensions](/docs/7-2/reference/-/knowledge_base/r/installing-new-extensions-for-blade-cli)\narticle to install the command (JAR) to Blade CLI.\n\nYou can examine a working custom command project\n[here](https://github.com/liferay/liferay-blade-cli/blob/master/extensions/sample-command).\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/08-extending-blade-cli/03-creating-custom-project-templates-for-blade-cli.markdown",
    "content": "---\nheader-id: creating-custom-project-templates-for-blade-cli\n---\n\n# Creating Custom Project Templates for Blade CLI\n\n[TOC levels=1-4]\n\nBlade comes with 32+ project templates, but many times you may feel that\nthose are too simple or don't fit the need for your development team. You can\ncreate new custom project templates that fit your team's workflow and have Blade\nuse them instead.\n\n| **Important:** An extension JAR can only contain one template. If you have\n| multiple templates, each must be deployed in a separate JAR.\n\nImplementing a custom project template should mimic that of a Maven archetype.\nThe best way to illustrate this is by visualizing a\n[sample template](https://github.com/liferay/liferay-blade-cli/tree/master/extensions/sample-template)'s\nfolder structure:\n\n- `src/`\n    - `main/resources/`\n        - `META-INF`\n            - `maven`\n                - `archetype-metadata.xml`\n            - `archetype-post-generate.groovy` (optional; only invoked by Maven projects)\n        - `archetype-resources`\n            - Folder structure to be generated\n- `bnd.bnd`\n- `[build.gradle|pom.xml]`\n\nYou can read more about Maven archetypes and their features and capabilities\n[here](https://maven.apache.org/guides/introduction/introduction-to-archetypes.html).\n\nTo create a custom project template that can be generated using Blade CLI,\nfollow these steps:\n\n1.  Create a generic Maven archetype following the folder structure outlined\n    above. Follow Maven's documentation to configure the archetype project\n    appropriately.\n\n2.  Open the template's `bnd.bnd` file and ensure it sets the following\n    configurations:\n\n    ```\n    Bundle-Description: TEMPLATE_DESCRIPTION\n    Bundle-Name: TEMPLATE_NAME\n    Bundle-SymbolicName: SYMBOLIC_NAME\n    Bundle-Version: TEMPLATE_VERSION\n    Liferay-Versions: LIFERAY_VERSION_RANGE\n    -removeheaders:\\\n        Import-Package,\\\n        Private-Package,\\\n        Require-Capability\n    ```\n\n    For example, a template's `bnd.bnd` could look like this:\n\n    ```\n    Bundle-Description: Creates a Sample as a module project.\n    Bundle-Name: Liferay Project Templates Sample\n    Bundle-SymbolicName: com.liferay.project.templates.sample\n    Bundle-Version: 1.0.0\n    Liferay-Versions: [7,8)\n    -removeheaders:\\\n        Import-Package,\\\n        Private-Package,\\\n        Require-Capability\n    ```\n\n    The `Bundle-SymbolicName` of your template JAR must have the pattern\n    `*.project.templates.<name>.*`. The `-removeheaders` definition is a\n    packaging requirement for all project templates. For more information on\n    Bnd versioning, visit\n    [Bnd's official docs](https://bnd.bndtools.org/chapters/170-versioning.html).\n\n3.  Generate the extension's JAR file (e.g., `gradlew build`).\n\nIt's that easy! You've created a custom project template. See the\n[Installing New Extensions](/docs/7-2/reference/-/knowledge_base/r/installing-new-extensions-for-blade-cli)\narticle to install the project template (JAR) to Blade CLI.\n\nYou can examine a working custom project template\n[here](https://github.com/liferay/liferay-blade-cli/blob/master/extensions/sample-template).\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/08-extending-blade-cli/04-installing-new-extensions-for-blade-cli.markdown",
    "content": "---\nheader-id: installing-new-extensions-for-blade-cli\n---\n\n# Installing New Extensions for Blade CLI\n\n[TOC levels=1-4]\n\nAfter you've created a new extension for Blade CLI, you must install it so it's\navailable for use. You can learn how to create\n[custom commands](/docs/7-2/reference/-/knowledge_base/r/creating-custom-commands-for-blade-cli)\nand\n[custom project templates](/docs/7-2/reference/-/knowledge_base/r/creating-custom-project-templates-for-blade-cli)\nin their respective articles.\n\nWhen Blade CLI starts, it looks in the user's `${user.home}/.blade/extensions`\nfolder for JAR files. All JAR files are searched to see if they contain\nvalid Blade extensions. You'll learn how to install new extensions next.\n\n## Installing a New Extension\n\nTo install an extension, you must move the extension JAR to the user's\n`${user.home}/.blade/extensions` folder. You can do this automatically from\nBlade CLI by running\n\n```bash\nblade extension install /path/to/my_extension.JAR\n```\n\nYou can verify that the extension is available by running the following\ncommands, depending on extension type:\n\n**Custom Command:**\n\n```bash\nblade help\n```\n\n**Custom Project Template:**\n\n```bash\nblade create -l\n```\n\nGreat! You've installed a new extension!\n\n## Uninstalling an Extension\n\nYou can uninstall a Blade extension by running this:\n\n```bash\nblade extension uninstall EXTENSION_NAME.jar\n```\n\nThis removes the extension JAR from the user's `${user.home}/.blade/extensions`\nfolder.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/08-extending-blade-cli/05-creating-a-blade-profile.markdown",
    "content": "---\nheader-id: creating-a-blade-profile\n---\n\n# Creating a Blade Profile\n\n[TOC levels=1-4]\n\nThere are two steps to follow when adding a new Blade profile:\n\n- [Creating a new profile](#creating-a-new-profile)\n- [Setting the profile in Liferay Workspace](#setting-a-profile)\n\nYou'll learn how to create a profile first.\n\n## Creating a New Profile\n\nTo create a new Blade profile, follow these steps:\n\n1.  [Create a generic OSGi module](/docs/7-2/reference/-/knowledge_base/r/creating-a-project).\n\n2.  To create a new command, create the command and arguments classes extending\n    [`BaseCommand`](https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BaseCommand.java)\n    and\n    [`BaseArgs`](https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BaseArgs.java),\n    respectively, as described in the\n    [Creating Custom Commands](/docs/7-2/reference/-/knowledge_base/r/creating-custom-commands-for-blade-cli)\n    article. These classes should reside in the profile module's\n    `src/main/java/PACKAGE_NAME` folder. These classes register your command and\n    arguments to Blade CLI.\n\n3.  To override a default command, follow the same steps outlined\n    [here](/docs/7-2/reference/-/knowledge_base/r/creating-custom-commands-for-blade-cli):\n\n    - Create a command class\n    - Create an arguments class\n    - Define your commands' fully qualified class names for the service loader\n\n    Instead of extending the\n    [`BaseCommand`](https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BaseCommand.java)\n    and\n    [`BaseArgs`](https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BaseArgs.java),\n    classes, however, extend the command/arguments classes defined for the\n    command you intend to override. Make sure to also set the `@Parameters`\n    annotation's `commandNames` argument to the command to override.\n\n    For example, if you intend to override the default `deploy` command, your\n    arguments class declaration would look like this:\n\n    ```java\n    @Parameters(commandDescription = \"Overridden Deploy Command\", commandNames = \"deploy\")\n    public class OverriddenArgs extends DeployArgs {\n\n    }\n    ```\n\n    The corresponding command class override would look like this:\n\n    ```java\n    public class OverriddenCommand extends BaseCommand<OverriddenArgs> {\n\n        @Override\n        public void execute() throws Exception {\n\t\t        OverriddenArgs args = getArgs();\n\n            getBladeCLI().out(\"OverriddenCommand says \" + args.isWatch());\n        }\n\n        @Override\n        public Class<OverriddenArgs> getArgsClass() {\n            return OverriddenArgs.class;\n        }\n    }\n    ```\n\n    You can search for the default command/arguments classes\n    [here](https://github.com/liferay/liferay-blade-cli/tree/master/cli/src/main/java/com/liferay/blade/cli/command).\n\n4.  To associate a command to your new profile, set the\n    [`BladeProfile`](https://github.com/liferay/liferay-blade-cli/blob/master/cli/src/main/java/com/liferay/blade/cli/command/BladeProfile.java)\n    annotation in your command class:\n\n    ```java\n    @BladeProfile(\"myprofile\")\n    public class NewCommand extends BaseCommand<NewArgs> {\n\n    }\n    ```\n\n    The annotation's parameter should specify the profile you want to associate\n    the command with (e.g., `myprofile`).\n\nExcellent! You've created a new Blade profile and learned how to add new\ncommands or override default commands by leveraging the profile.\n\n| **Note:** Command classes spanning multiple JARs can use/share the same\n| profile name. For example, if you want to extend the internal (provided)\n| `maven` profile extension with new commands, you can do it externally the same\n| way you'd create a new profile.\n\nYou can reference the\n[sample profile project](https://github.com/liferay/liferay-blade-cli/tree/master/extensions/sample-profile)\nto examine a new command and overridden command's setup in a custom profile.\n\nNext, you'll learn how to set your new profile for use in a Liferay Workspace.\n\n## Setting a Profile\n\nTo set your new Blade profile in a Liferay Workspace, open the\n`${workspaceDir}/.blade.properties` file and set the `profile.name` property to\nyour profile name:\n\n```properties\nprofile.name=myprofile\n```\n\nThis specifies which Blade profile is active and uses its defined commands. The\ndefault setting is `gradle`. You can also set this property to `maven`\nout-of-the-box, which is applied for Maven-based workspaces. You can only set\none profile for a workspace.\n\nYou can specify the Blade profile for a workspace when initializing it too. This\nis done by passing the profile name as an argument when creating the workspace:\n\n```bash\nblade init -P [profile-name] [workspace-name]\n```\n\nFor example, if you execute the following command:\n\n```bash\nblade init -P myprofile my-new-custom-workspace\n```\n\nYour `my-new-custom-workspace` has the following properties set in its\n`${workspaceDir}/.blade.properties` file:\n\n```properties\nliferay.version.default=7.2\nprofile.name=myprofile\n```\n\n| **Note:** The `-P` profile parameter can be used for any command to specify\n| the profile to use for that command. This is helpful if you want to run a\n| command not associated with the workspace's current profile.\n\nAwesome! You've set your new Blade profile!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/blade-cli/100-common-errors-with-blade-cli-installation.markdown",
    "content": "---\nheader-id: common-errors-with-blade-cli\n---\n\n# Common Errors with Blade CLI\n\n[TOC levels=1-4]\n\nIf Blade CLI isn't working as expected, you may find answers here.\n\nThe following issues are documented:\n\n- [The blade command is not available in my CLI](#the-blade-command-is-not-available-in-my-cli)\n- [I can't update my Blade CLI version](#i-cant-update-my-blade-cli-version)\n\nVisit the appropriate section to learn more.\n\n## The blade command is not available in my CLI\n\nThe Liferay Project SDK installer attempts to add JPM to your path. For Windows,\nit uses the Windows registry. For Mac/Linux, it updates `.bashrc` or `.zshrc`. \n\nAt a minimum, Mac/Linux users must open a new shell after the installer finishes\nfor the new features to be available. If, however, you're using a different\nshell (e.g., Korn, csh) or you've customized your CLI via `.profile` or\nsome other configuration file, you must add JPM to your path manually. \n\nTo add JPM's required `bin` folder, execute the appropriate command based on\nyour operating system.\n\nmacOS:\n\n```bash\necho 'export PATH=\"$PATH:$HOME/Library/PackageManager/bin\"' >> ~/.bash_profile\n```\n\nLinux:\n\n```bash\necho 'export PATH=\"$PATH:$HOME/jpm/bin\"' >> ~/.bash_profile\n```\n\nOnce you open a new shell, the `blade` command should be available.\n\n## I can't update my Blade CLI version\n\nIf you run `blade version` after updating, but don't see the expected version\ninstalled, you may have two separate Blade CLI installations on your machine.\nThis is typically caused if you installed an earlier version of Blade CLI and\nthen used the Liferay Project SDK installer (at any time prior) to update the\nolder Blade CLI instance. This is not recommended. Doing this installs Blade CLI\nin the global and user home folder of your machine. The latest Blade CLI update\nprocess installs to your user home folder, so you must delete the legacy Blade\nfiles in your global folder, if present. To do this, navigate to your\n`GLOBAL_FOLDER/JPM4J` folder and delete\n\n- `/bin/blade`\n- `/commands/blade`\n\nThe newest Blade CLI installation in your user home folder is now recognized and\navailable.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/dev-studio/01-intro.markdown",
    "content": "---\nheader-id: liferay-dev-studio\n---\n\n# Liferay Dev Studio\n\n[TOC levels=1-4]\n\nLiferay Dev Studio is an extension for the\n[Eclipse](https://www.eclipse.org/ide/) platform for developing @product@\nplugins. It works with build tools such as Gradle and Maven and configuration\ntools like BndTools.\n\nDev Studio makes Liferay development easier by providing an intuitive GUI. It\ncontains\n\n- Liferay-specific wizards for creating/developing @product@ projects.\n- Liferay server management and project testing capabilities.\n- Editors for Service Builder files, workflow definitions, POM files, and more.\n- Snippets for tag libraries\n- etc.\n\nHere you'll learn how to\n\n- [Install Dev Studio](/docs/7-2/reference/-/knowledge_base/r/installing-liferay-dev-studio)\n- [Set Proxy Requirements](/docs/7-2/reference/-/knowledge_base/r/setting-proxy-requirements-for-dev-studio)\n- [Install a Liferay server](/docs/7-2/reference/-/knowledge_base/r/installing-a-liferay-server-in-dev-studio)\n- [Import a Liferay project](/docs/7-2/reference/-/knowledge_base/r/importing-projects-in-dev-studio)\n- [Use the Gogo Shell](/docs/7-2/reference/-/knowledge_base/r/using-the-gogo-shell-in-dev-studio)\n- [Search @product@ source](/docs/7-2/reference/-/knowledge_base/r/searching-product-source-in-dev-studio)\n- [Debug @product@ source](/docs/7-2/reference/-/knowledge_base/r/debugging-product-source-in-dev-studio)\n- [Update Dev Studio](/docs/7-2/reference/-/knowledge_base/r/updating-liferay-dev-studio)\n\nYou can also find\n[Gradle](/docs/7-2/reference/-/knowledge_base/r/gradle-in-dev-studio) and\n[Maven](/docs/7-2/reference/-/knowledge_base/r/maven-in-dev-studio) reference\narticles that highlight popular use cases for those respective build tools in\nDev Studio.\n\nFor help\n[creating](/docs/7-2/reference/-/knowledge_base/r/creating-a-project#liferay-dev-studio)\nand\n[deploying](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project#liferay-dev-studio)\nprojects with Dev Studio, or\n[creating a Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/creating-a-liferay-workspace#dev-studio),\nvisit their respective articles.\n\nLet Dev Studio aid in your conquest for @product@ development. Continue on to\nlearn how!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/dev-studio/02-installing-dev-studio.markdown",
    "content": "---\nheader-id: installing-liferay-dev-studio\n---\n\n# Installing Liferay Dev Studio\n\n[TOC levels=1-4]\n\nLiferay Dev Studio is a plugin for Eclipse that brings many Liferay-specific\nfeatures to the table. You can install it into your existing Eclipse\nenvironment, or Liferay provides a bundled version included in its Project SDK.\nHere you'll learn the different methods available for installing Dev Studio:\n\n- [install the Dev Studio bundle from scratch](#install-the-dev-studio-bundle)\n- [install Dev Studio into an existing Eclipse instance using an update URL](#install-dev-studio-into-eclipse)\n- [install Dev Studio into an existing Eclipse instance using a ZIP file](#install-dev-studio-into-eclipse-from-a-zip-file)\n\n**Important:** If you're installing Dev Studio into an existing Eclipse\nenvironment, you must be on Eclipse Photon or newer. For instructions on\nupgrading to Photon, see Eclipse's\n[upgrade documentation](https://wiki.eclipse.org/FAQ_How_do_I_upgrade_Eclipse_IDE%3F#Upgrading_existing_Eclipse_IDE_and_Installed_Features_to_newer_release).\nWith this particular upgrade, you should also deactivate the current available\nupdate sites in the *Window* &rarr; *Preferences* &rarr; *Install/Update* &rarr;\n*Available Software Sites* menu to ensure a successful upgrade (e.g., Oxygen).\n\n## Install the Dev Studio Bundle\n\n1.  Download and install [Java](http://java.oracle.com). Liferay runs on Java,\n    so you'll need it to run everything else. Because you'll be developing apps\n    for @product@ in Dev Studio, the Java Development Kit (JDK) is required. It\n    is an enhanced version of the Java Environment used for developing new Java\n    technology. You can download the Java SE JDK from the Java\n    [Downloads](http://www.oracle.com/technetwork/java/javase/downloads/index.html)\n    page. \n\n2.  Download Liferay's latest\n    [Project SDK with Dev Studio](https://sourceforge.net/projects/lportal/files/Liferay%20IDE/)\n    executable that correlates to your operating system. The Project SDK\n    includes @ide@,\n    [Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace),\n    and [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli).\n\n3.  Run the Project SDK executable and step through the installer to install\n    everything to your machine. For help with setting up proxy settings (if\n    necessary), see the\n    [Dev Studio Proxy Settings](/docs/7-2/reference/-/knowledge_base/r/setting-proxy-requirements-for-dev-studio)\n    and\n    [Liferay Workspace Proxy Settings](/docs/7-2/reference/-/knowledge_base/r/setting-proxy-requirements-for-liferay-workspace)\n    articles for more information.\n\nCongratulations! You've installed Liferay Dev Studio! It's now available in the\nProject SDK folder's `liferay-developer-studio`. To run Dev Studio, execute the\n`DeveloperStudio` executable. A Liferay Workspace has also been initialized in\nthat same folder.\n\n## Install Dev Studio into Eclipse\n\nIf you already have an Eclipse environment that you're using for other\nthings, it's easy to add Dev Studio to your existing Eclipse installation. \n\n1.  In your browser, go to the\n    [Liferay Dev Studio](https://community.liferay.com/en_GB/project/-/asset_publisher/TyF2HQPLV1b5/content/ide-installation-instructions)\n    page. Copy the URL for the latest stable update site. \n\n2.  In Eclipse, select *Help* &rarr; *Install New Software*. \n\n3.  In the *Work with* field, copy in the URL.\n\n4.  You'll see the Liferay Dev Studio components in the list below. Check them\n    off and click *Next*. \n\n5.  Accept the terms of the agreements and click *Next*, and Dev Studio is\n    installed. Like other Eclipse plugins you'll have to restart Eclipse to\n    enable it. \n\n## Install Dev Studio into Eclipse from a ZIP File\n\nTo install Liferay Dev Studio into Eclipse from a Zip file, follow these steps: \n\n1.  Go to the\n    [Dev Studio](https://community.liferay.com/en_GB/project/-/asset_publisher/TyF2HQPLV1b5/content/ide-installation-instructions)\n    downloads page. Under *Other Downloads*, select the *Liferay IDE [version]\n    Archived Update-site* option to download it.\n\n2.  In Eclipse, go to *Help* &rarr; *Install New Software...*. \n\n3.  In the *Add* dialog, click the *Archive* button and browse to the location\n    of the downloaded Dev Studio Zip file.\n\n4.  You'll see the Dev Studio components in the list below. Check them off and\n    click *Next*. \n\n5.  Accept the terms of the agreements and click *Next*, and Liferay Dev Studio\n    is installed. Like other Eclipse plugins you'll have to restart Eclipse to\n    enable it.\n\nAwesome! You've installed Liferay Dev Studio. Now you can begin Liferay\ndevelopment using a popular and supported IDE.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/dev-studio/03-setting-proxy-requirements-for-dev-studio.markdown",
    "content": "---\nheader-id: setting-proxy-requirements-for-dev-studio\n---\n\n# Setting Proxy Requirements for Dev Studio\n\n[TOC levels=1-4]\n\nIf you have proxy server requirements and want to configure your http(s) proxy\t\t\nto work with Liferay Dev Studio, follow the instructions below.\n\n1.  Navigate to Eclipse's *Window* &rarr; *Preferences* &rarr; *General*\n    &rarr; *Network Connections* menu.\n\n2.  Set the *Active Provider* drop-down selector to *Manual*.\n\n3.  Under *Proxy entries*, configure both proxy HTTP and HTTPS by clicking the\n    field and selecting the *Edit* button.\n\n    ![Figure 1: You can configure your proxy settings in Dev Studio's Network Connections menu.](../../../images/ide-network-connections.png)\n\n4.  For each schema (HTTP and HTTPS), enter your proxy server's host, port, and\n    authentication settings (if necessary). Do not leave whitespace at the end\n    of your proxy host or port settings.\n\n5.  Once you've configured your proxy entry, click *Apply and Close*.\n\nIf you're working with a Liferay Workspace in Dev Studio, you'll need to configure\nyour proxy settings for that environment too. See the\n[Setting Proxy Requirements for Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/setting-proxy-requirements-for-liferay-workspace)\narticle for more details.\n\nAwesome! You've successfully configured Dev Studio's proxy settings!\n\n## Additional Proxy Settings\n\nSome Eclipse plugins do not properly check the `core.net` proxy infrastructure\nwhen setting proxy settings via *Window* &rarr; *Preferences* &rarr; *General*\n&rarr; *Network Connections*. Therefore, you may need to configure additional\nproxy settings.\n\nTo do so, open the `eclipse.ini` file associated with your Eclipse installation\nand add the following entries:\n\n```\n-vmargs\n-Dhttp.proxyHost=www.somehost.com\n-Dhttp.proxyPort=1080\n-Dhttp.proxyUser=userId\n-Dhttp.proxyPassword=somePassword\n-Dhttps.proxyHost=www.somehost.com\n-Dhttps.proxyPort=1080\n-Dhttps.proxyUser=userId\n-Dhttps.proxyPassword=somePassword\n```\n\nAfter saving the file, restart Eclipse. Now your additional proxy settings are\napplied!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/dev-studio/04-installing-a-server-in-dev-studio.markdown",
    "content": "---\nheader-id: installing-a-liferay-server-in-dev-studio\n---\n\n# Installing a Liferay Server in Dev Studio\n\n[TOC levels=1-4]\n\nDev Studio offers a single GUI for managing a Liferay server and its deployed\nprojects. A server is installed and managed from the Servers view (lower left\ncorner in Eclipse).\n\nFor reference, here's how the Dev Studio server buttons work with your @product@\ninstance:\n\n- *Start* (![Start Server](../../../images/icon-start-server.png)): Starts the\n  server.\n- *Stop* (![Stop Server](../../../images/icon-stop-server.png)): Stops the\n  server.\n- *Debug* (![Debug Server](../../../images/icon-debug-server.png)): Starts the\n  server in debug mode. For more information on debugging in Dev Studio, see the\n  [Debugging @product@ source in Dev Studio](/docs/7-2/reference/-/knowledge_base/r/debugging-product-source-in-dev-studio)\n  article.\n\nFollow these steps to install your server. Note you must have already downloaded\nand de-compressed the server bundle: \n\n1.  In the Servers view, click the *No Servers are available* link. If you\n    already have a server installed, you can install a new server by\n    right-clicking in the Servers view and selecting *New* &rarr; *Server*.\n    This brings up a wizard that walks you through the process of defining a new\n    server.\n\n2.  Select the type of server you would like to create from the list of\n    available options. For a standard server, open the *Liferay, Inc.* folder\n    and select the *Liferay 7.x* option. You can change the server name to\n    something more unique too; this is the name displayed in the Servers view.\n    Then click *Next*.\n\n    ![Figure 1: Choose the type of server you want to create.](../../../images/define-new-server.png)\n\n    **Note:** If you've already configured previous Liferay servers, you'll be\n    provided the *Server runtime environment* field, which lets you choose\n    previously configured runtime environments. If you want to re-add an\n    existing server, select one from the dropdown menu. You can also add a new\n    server by selecting *Add*, or you can edit existing servers by selecting\n    *Configure runtime environments*. Once you've configured the server runtime\n    environment, select *Finish*. If you selected an existing server, your\n    server installation is finished; you can skip steps 3-5.\n\n3.  Enter a name for your server. This is the name for the @product@ runtime\n    configuration used by Dev Studio. This is not the display name used in the\n    Servers tab.\n\n4.  Browse to the installation folder of the @product@ bundle. For example,\n    `C:\\liferay-ce-portal-7.2.0-m2\\tomcat-9.0.10`.\n\n    ![Figure 2: Specify the installation folder of the bundle.](../../../images/specify-bundle-directory.png)\n\n5.  Select a runtime JRE and click *Finish*. Your new server appears under the\n    Servers view.\n\n    ![Figure 3: Your new server appears under the *Servers* view.](../../../images/new-server-added.png)\n\nCongratulations! Your server is now available in Liferay Dev Studio!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/dev-studio/05-importing-projects-in-dev-studio.markdown",
    "content": "---\nheader-id: importing-projects-in-dev-studio\n---\n\n# Importing Projects in Dev Studio\n\n[TOC levels=1-4]\n\nThere are two types of Liferay projects you can import into Dev Studio:\n\n- Liferay Module Project (this also includes WAR-style projects)\n- Liferay Workspace Project\n\nYou cannot import Liferay projects individually that reside in a\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace).\nYou can either import a non-workspace Liferay project (or group of projects if\nthe parent folder is specified) or an entire workspace project with all its\nLiferay projects.\n\nTo import a pre-existing Liferay project into Dev Studio, follow the steps\noutlined below:\n\n1.  Right-click in the Project Explorer and select *Import* &rarr; *Liferay\n    Module Project*. If you're interested in importing an entire Liferay\n    Workspace, select the *Liferay Workspace Project* instead.\n\n    ![Figure 1: You can import a single project or folder of projects.](../../../images/import-liferay-project.png)\n\n    Once you've selected your project(s), the project build type is displayed.\n\n2.  Click *Finish*.\n\nNow your Liferay project is available from the Package Explorer.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/dev-studio/06-using-the-gogo-shell-in-dev-studio.markdown",
    "content": "---\nheader-id: using-the-gogo-shell-in-dev-studio\n---\n\n# Using the Gogo Shell in Dev Studio\n\n[TOC levels=1-4]\n\nIf you're using Dev Studio to develop and deploy your projects, you may be\ninterested in managing them after they're deployed with Dev Studio too. You can\ndo this with the Dev Studio's\n[Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\nfeature.\n\n1.  Right-click your started portal instance in the Servers view.\n\n2.  Select *Open Gogo Shell*. \n\n    ![Figure 1: Select *Open Gogo Shell* to open a terminal window in Dev Studio using Gogo shell.](../../../images/open-gogo-shell.png)\n\n    A Gogo shell terminal appears, allowing you to enter Gogo commands to\n    inspect your Liferay instance and the projects deployed to it.\n\n3.  A common use case for the Gogo Shell is verifying successful project\n    deployment. Enter the `lb` command to view a list of deployed bundles. If\n    the project status is active, then it deployed successfully.\n\n    ![Figure 2: You can check to see if your project deployed successfully to Liferay using the Gogo shell.](../../../images/gogo-deploy-successful.png)\n\n**Important:** Dev Studio's Gogo shell usage requires\n[Developer Mode](/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes)\nto be enabled. Developer Mode is enabled in\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)\nby default.\n\nExcellent! You've learned how to manage your deployed projects with Dev Studio's\nGogo Shell integration.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/dev-studio/07-searching-liferay-portal-source-with-dev-studio.markdown",
    "content": "---\nheader-id: searching-product-source-in-dev-studio\n---\n\n# Searching @product@ Source in Dev Studio\n\n[TOC levels=1-4]\n\nIn Liferay Dev Studio, you can search through @product@'s source code to aid in\nthe development of your project. Liferay provides great resources to help with\ndevelopment (e.g., official documentation,\n[docs.liferay.com](https://docs.liferay.com/),\n[sample projects](/docs/7-2/reference/-/knowledge_base/r/sample-projects),\netc.), but sometimes searching through Liferay's codebase (i.e., platform and\nofficial apps) for patterns is just as useful. For example, if you're creating\nan application that extends a class provided in Liferay's `portal-kernel` JAR,\nyou can inspect that class and research how it's used in other areas of\n@product@'s codebase.\n\nTo do this, you must be developing in a\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace).\nLiferay Workspace is able to provide this functionality by targeting a specific\n@product@ version, which indexes the configured @product@ source code to provide\nadvanced search. See the\n[Managing the Target Platform in Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform)\ntutorial for more information on how this works.\n\nWorkspace does not perform portal source indexing by default. You must enable\nthis functionality by adding the following property to your workspace's\n`gradle.properties` file:\n\n```properties\ntarget.platform.index.sources=true\n```\n\n| **Note:** Portal source indexing is disabled in Gradle workspace version\n| 2.0.3+ (Target Platform plugin version 2.0.0+).\n\nIn this tutorial, you'll explore three use cases where advanced search would be\nuseful.\n\n- [Search class hierarchy](#search-class-hierarchy)\n- [Search declarations](#search-method-declarations)\n- [Search references](#search-annotation-references)\n\nThese examples are just a small subset of what you can search in Liferay Dev\nStudio. See Eclipse's documentation on\n[Java Search](http://help.eclipse.org/oxygen/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Fconcepts%2Fconcept-java-search.htm&resultof=%22%6a%61%76%61%22%20)\nfor a comprehensive guide.\n\n## Search Class Hierarchy\n\nInspecting classes that extend a similar superclass can help you find useful\npatterns and examples for how you can develop your own app. For example, suppose\nyour app extends the\n[MVCPortlet](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCPortlet.html)\nclass. You can search classes that extend that same class in Dev Studio. Complete\nthe steps below for a simple example:\n\n1.  Right-click the `MVCPortlet` declaration.\n\n2.  Select *Open Type Hierarchy*.\n\nThis opens a window that lets you inspect all classes residing in the target\nplatform that extend `MVCPortlet`.\n\n![Figure 1: Browse the Type Hierarchy window and open the provided classes for examples on how to extend a class.](../../../images/open-type-hierarchy.png)\n\nGreat! Now you can search for all extensions and implementations of a\nclass/interface to aid in your quest for developing the perfect app.\n\n## Search Method Declarations\n\nSometimes you want a search to be more granular, exploring the declarations of a\nspecific method provided by a class/interface. Dev Studio's advanced search has\nno limits; Liferay Workspace's target platform indexing provides method\nexploration too!\n\nSuppose in the\n[MVCPortlet](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCPortlet.html)\nclass you're extending, you want to search for declarations of its `doView`\nmethod you're overriding. Here's how to do it:\n\n1.  Right-click the `doView` method declaration in your custom app's class.\n\n2.  Select *Declarations* &rarr; *Workspace*.\n\n![Figure 2: All declarations of the method are returned in the Search window.](../../../images/inspect-declared-method.png)\n\nThe rendered Search window displays the other occurrences in the target platform\nwhere that method was overridden.\n\n## Search Annotation References\n\nAnnotations used in @product@'s source code can sometimes be cryptic. You can\nfind out how they can be used in your own application by searching for where\nthese types of annotations exist in Liferay's target platform.\n\nFor example, you may find some documentation on using the `@Reference`\nannotation in an OSGi module and implement it in your custom app. It could be\nuseful to reference real world examples in @product@'s apps to check how it was\nused elsewhere. You can complete this search like this:\n\n1.  Right-click the `@Reference` annotation in a class.\n\n2.  Select *References* &rarr; *Workspace*.\n\n![Figure 3: All matching annotations are displayed in the Search window.](../../../images/inspect-references-ide.png)\n\nThe rendered Search window displays the other occurrences in the target platform\nwhere that annotation was used.\n\nExcellent! You now have the tools to search the configured target platform\nspecified in your Liferay Workspace!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/dev-studio/08-debugging-liferay-portal-source-with-dev-studio.markdown",
    "content": "---\nheader-id: debugging-product-source-in-dev-studio\n---\n\n# Debugging @product@ Source in Dev Studio\n\n[TOC levels=1-4]\n\nYou can debug @product@ source code in Dev Studio to help resolve errors.\nDebugging @product@ code follows most of the same techniques associated with\ndebugging in Eclipse. If you need help with general debugging, you can visit\nEclipse's documentation. Here's some helpful Eclipse links to visit:\n\n- [Debugger](http://help.eclipse.org/oxygen/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Fconcepts%2Fcdebugger.htm&cp=1_2_9)\n- [Local Debugging](http://help.eclipse.org/oxygen/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Fconcepts%2Fclocdbug.htm&cp=1_2_11)\n- [Remote Debugging](http://help.eclipse.org/oxygen/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Fconcepts%2Fcremdbug.htm&cp=1_2_12)\n\nThere are a couple Liferay-specific configurations to know before debugging\n@product@ code:\n\n- [Configure your target platform.](#configure-your-target-platform)\n- [Configure a Liferay server and start it in debug mode.](#configure-a-liferay-server-and-start-it-in-debug-mode)\n\nLet's explore these Liferay-specific debugging configurations.\n\n## Configure Your Target Platform\n\nTo configure your target platform, you must be developing in a\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace).\nLiferay Workspace is able to provide debugging capabilities by targeting a\nspecific @product@ version, which indexes the configured @product@ source code.\nLiferay Workspace does not perform portal source indexing by default. You must\nenable this functionality by adding the following property to your workspace's\n`gradle.properties` file:\n\n```properties\ntarget.platform.index.sources=true\n```\n\n| **Note:** Portal source indexing is disabled in Gradle workspace version\n| 2.0.3+ (Target Platform plugin version 2.0.0+).\n\nWithout specifying a target platform, @product@'s source code\ncannot be accessed by Dev Studio. See the\n[Managing the Target Platform in Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform)\ntutorial for more information on how this works.\n\n**Important:** The target platform should match the Liferay server version you\nconfigure in the next section.\n\nOnce the target platform is configured in your workspace, Eclipse has access to\nall of @product@'s source code. Next, you'll configure a Liferay server and\nlearn how to start it in Debug mode.\n\n## Configure a Liferay Server and Start It in Debug Mode\n\nConfiguring your target platform gives Eclipse @product@'s source code to\nreference. Now you must configure a Liferay server matching the target platform\nversion so you can deploy the custom code you wish to debug.\n\n1.  Set up your @product@ server to run in Dev Studio. See the\n    [Installing a Server in  Dev Studio](/docs/7-2/reference/-/knowledge_base/r/installing-a-liferay-server-in-dev-studio) \n    for more details.\n\n2.  Start the server in debug mode. To do this, click the debug button in the \n    Servers pane of Dev Studio.\n\n    ![Figure 1: The red box in this screenshot highlights the debug button. Click this button to start the server in debug mode.](../../../images/ide-debug.png)\n\nAwesome! You're now equipped to begin debugging in Liferay Dev Studio!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/dev-studio/09-updating-dev-studio.markdown",
    "content": "---\nheader-id: updating-liferay-dev-studio\n---\n\n# Updating Liferay Dev Studio\n\n[TOC levels=1-4]\n\nIf you're already using Liferay Dev Studio but must update your environment,\nfollow the steps below:\n\n1.  In Dev Studio, go to *Help* &rarr; *Install New Software...*. \n\n2.  In the *Work with* field, copy in the URL\n    http://releases.liferay.com/tools/ide/latest/stable/.\n\n3.  You'll see the Dev Studio components in the list below. Check them off and\n    click *Next*.\n\n    ![Figure 1: Make sure to check all the Dev Studio components you wish to install.](../../../images/ide-updatesite-install.png)\n\n4.  Accept the terms of the agreements. Click *Next*, and Dev Studio is updated.\n    You must restart Dev Studio for the updates to take effect.\n\nYou're now on the latest version of Liferay Dev Studio!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/dev-studio/100-gradle-in-dev-studio.markdown",
    "content": "---\nheader-id: gradle-in-dev-studio\n---\n\n# Gradle in Dev Studio\n\n[TOC levels=1-4]\n\n[Gradle](http://gradle.org/) is a popular open source build automation system.\nYou can take full advantage of Gradle in Liferay Dev Studio through\n[Buildship](https://projects.eclipse.org/releases/photon), a collection\nof Eclipse plugins that provide support for building software using Gradle\nwith Liferay Dev Studio. Buildship is bundled with Liferay Dev Studio versions\n3.0 and higher.\n\n![Figure 1: Navigate to *Help* &rarr; *Installation Details* to view plugins included in Dev Studio.](../../../images/buildship-in-liferayide.png)\n\nThis reference article highlights some useful tips for leveraging Gradle in Dev\nStudio.\n\n- [Creating Pure Gradle Projects](#creating-pure-gradle-projects)\n- [Importing Pure Gradle Projects](#importing-pure-gradle-projects)\n- [Gradle Tasks and Executions](#gradle-tasks-and-executions)\n\nNote that\n[creating Liferay Gradle projects](/docs/7-2/reference/-/knowledge_base/r/creating-a-project#liferay-dev-studio)\nand\n[deploying Gradle projects](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project#liferay-dev-studio)\nwith Dev Studio are covered in their respective articles.\n\nThe first thing you'll learn about in this tutorial is creating pure Gradle\nprojects in Dev Studio.\n\n## Creating Pure Gradle Projects\n\nMost of Dev Studio's wizards rely on your usage of Liferay Workspace. This is\nfor good reason; it's the recommended developer environment for Liferay\nprojects. You can, however, create pure Gradle projects and manually configure\nthem to be deployable to @product@.\n\nYou can create a pure Gradle project by using the Gradle Project wizard.\n\n1.  Navigate to *File* &rarr; *New* &rarr; *Project...*.\n\n2.  Select *Gradle* &rarr; *Gradle Project*. Then click *Next* &rarr; *Next*.\n\n3.  Enter a valid project name. You can also specify your project location and\n    working sets.\n\n4.  Optionally, you can navigate to the next page and specify your Gradle\n    distribution and other advanced options. Once you're finished, select\n    *Finish*.\n\n![Figure 2: You can specify your Gradle distribution and advanced options such as home directories, JVM options, and program arguments.](../../../images/new-gradle-project.png)\n\nExcellent! You've created a pure Gradle project using Dev Studio.\n\n## Importing Pure Gradle Projects\n\nYou can also import existing pure Gradle projects in Dev Studio.\n\n1.  Go to *File* &rarr; *Import...*.\n\n2.  Select *Gradle* &rarr; *Existing Gradle Project* &rarr; *Next* &rarr;\n    *Next*.\n\n    ![Figure 3: You can specify what Gradle project to import from the *Import Gradle Project* wizard.](../../../images/import-gradle-project.png)\n\n3.  Click the *Browse...* button to choose a Gradle project.\n\n4.  Optionally, you can navigate to the next page and specify your Gradle\n    distribution and other advanced options. Once you're finished, click *Next*\n    again to review the import configuration. Select *Finish* once you've\n    confirmed your Gradle project import.\n\nNext you'll learn about Gradle tasks and executions, and learn how to run and\ndisplay them in Dev Studio.\n\n## Gradle Tasks and Executions\n\nDev Studio provides two views to enhance your development experience using\nGradle: Gradle Tasks and Gradle Executions. You can open these views by\nfollowing the instructions below.\n\n1. Go to *Window* &rarr; *Show View* &rarr; *Other...*.\n\n2. Navigate to the *Gradle* folder and open *Gradle Tasks* and *Gradle\n   Executions*.\n\nGradle tasks and executions views open automatically once you create or import a\nGradle project.\n\nThe Gradle Tasks view lets you display the Gradle tasks available for you to use\nin your Gradle project. Users can execute a task listed under the Gradle project\nby double-clicking it.\n\n![Figure 4: Navigate into your preferred Gradle project to view its available Gradle tasks.](../../../images/gradle-tasks.png)\n\nOnce you've executed a Gradle task, you can open the Gradle Executions view to\ninspect its output.\n\n![Figure 5: The Gradle Executions view helps you visualize the Gradle build process.](../../../images/gradle-executions.png)\n\nKeep in mind that if you change the Gradle build scripts inside your Gradle\nprojects (e.g., `build.gradle` or `settings.gradle`), you must refresh the\nproject so Dev Studio can account for the change and display it properly in\nyour views. To refresh a Gradle project, right-click on the project and select\n*Gradle* &rarr; *Refresh Gradle Project*.\n\n![Figure 6: Make sure to always refresh your Gradle project in Dev Studio after build script edits.](../../../images/refresh-gradle-project.png)\n\nIf you prefer Eclipse refresh your Gradle projects automatically, navigate to \n*Window* &rarr; *Preferences* &rarr; *Gradle* and enable the *Automatic Project\nSynchronization* checkbox. If you'd like to enable Gradle's automatic\nsynchronization for just one Gradle project, you can right-click a Gradle\nproject and select *Properties* &rarr; *Gradle* and enable auto sync that way.\n\nExcellent! You're now equipped with the knowledge to add, import, and build your\nGradle projects in Liferay Dev Studio!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/dev-studio/101-maven-in-dev-studio.markdown",
    "content": "---\nheader-id: maven-in-dev-studio\n---\n\n# Maven in Dev Studio\n\n[TOC levels=1-4]\n\nYou can take full advantage of [Maven](https://maven.apache.org/) in Liferay Dev\nStudio with its built-in Maven support. In this article, you'll learn about the\nfollowing topics:\n\n- [Installing Maven Plugins for Liferay Dev Studio](#installing-maven-plugins-for-dev-studio)\n- [Importing Maven Projects](#importing-maven-projects)\n- [Using the POM Graphic Editor](#using-the-pom-graphic-editor)\n\nNote that\n[creating](/docs/7-2/reference/-/knowledge_base/r/creating-a-project#liferay-dev-studio)\nand\n[deploying](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project#liferay-dev-studio)\nMaven projects with Dev Studio are covered in their respective articles.\n\nFirst you'll install the necessary Maven plugins for Dev Studio.\n\n## Installing Maven Plugins for Dev Studio\n\nTo support Maven projects in Dev Studio properly, you first need a mechanism to\nrecognize Maven projects as Dev Studio projects. For Dev Studio to recognize the\nMaven project and for it to be able to leverage Java EE tooling features (e.g.,\nthe Servers view) with the project, the project must be a flexible web project.\nDev Studio relies on the following Eclipse plugins to provide this capability:\n\n- `m2e` (Maven integration for Eclipse)\n- `m2e-wtp` (Maven integration for WTP)\n\nAll you have to do is install them so you can begin developing Maven projects\nfor @product@.\n\nWhen first installing Dev Studio, the installation startup screen asks if you\nwant to install the Maven plugins automatically. Don't worry if you missed this\nduring setup. You'll learn how to install the required Maven plugins for Dev\nStudio manually below.\n\n1.  Navigate to *Help* &rarr; *Install New Software*. In the *Work with* field,\n    insert the following value:\n\n        http://releases.liferay.com/tools/ide/latest/stable/\n\n2.  Check the *Liferay IDE Maven Support* option. This bundles all the required\n    Maven plugins you need to begin developing Maven projects for @product@.\n\n    ![Figure 1: You can install all the necessary Maven plugins for Dev Studio by installing the *Liferay IDE Maven Support* option.](../../../images/maven-install-ide-plugins.png)\n\n    If the *Liferay IDE Maven Support* option does not appear, then it's already\n    installed. To verify that it's installed, uncheck the *Hide items that are\n    already installed* checkbox and look for *Liferay IDE Maven Support* in the\n    list of installed plugins. Also, if you want to view everything that is\n    bundled with the *Liferay IDE Maven Support* option, uncheck the *Group\n    items by category* checkbox.\n\n3.  Click *Next*, review the install details, accept the term and license\n    agreements, and select *Finish*.\n\n| **Note:** Both Maven and Eclipse have their own standard build project\n| lifecycles that are independent from each other. For both to work together and\n| run seamlessly within Dev Studio, a lifecycle mapping is required to link both\n| lifecycles into one combined lifecycle. Normally, this would have to be done\n| manually by the user. Fortunately, the m2e-liferay plugin combines the\n| lifecycle metadata mapping and Eclipse build lifecycles, to provide a seamless\n| user experience. The lifecycle mappings for your project can be viewed by\n| right-clicking your project and selecting *Properties* &rarr; *Maven* &rarr;\n| *Lifecycle Mapping*.\n\nAwesome! Your Dev Studio is ready to develop Maven projects for @product@!\n\nYou'll learn about importing Maven projects in Dev Studio next.\n\n## Importing Maven Projects\n\nTo import a pre-existing, non-Liferay Maven project into Dev Studio, follow the\nsteps outlined below:\n\n1.  Navigate to *File* &rarr; *Import* &rarr; *Maven* &rarr; *Existing Maven\n    Projects* and click *Next*.\n\n    ![Figure 2: Dev Studio offers the Maven folder in the Import wizard.](../../../images/import-maven-project.png)\n\n2.  Click *Browse...* and select the root folder for your Maven project. Once\n    you've selected it, the `pom.xml` for that project should be visible in the\n    Projects menu.\n\n    ![Figure 3: Use the Import Maven Projects wizard to import your pre-existing project.](../../../images/select-maven-import.png)\n\n3.  Click *Finish*.\n\nNow your Maven project is available from the Package Explorer. To import a\nLiferay project built with Maven, see the\n[Importing Projects in Dev Studio](/docs/7-2/reference/-/knowledge_base/r/importing-projects-in-dev-studio)\nNext you'll learn about Dev Studio's POM graphical editor.\n\n### Using the POM Graphic Editor\n\nYou're provided a graphical POM editor when opening your Maven project's\n`pom.xml` in Dev Studio. This gives you several different ways to leverage\nthe power of Maven in your project:\n\n- **Overview:** provides a graphical interface where you can add to and edit the\n  `pom.xml` file.\n\n- **Dependencies:** provides a graphical interface for adding and editing\n  dependencies in your project, as well as modifying the `dependencyManagement`\n  section of the `pom.xml` file.\n\n- **Effective POM:** provides a read-only version of your project POM merged\n  with its parent POM(s), `settings.xml`, and the settings in Eclipse for Maven.\n\n- **Dependency Hierarchy:** provides a hierarchical view of project dependencies\n  and an interactive listing of resolved dependencies.\n\n- **pom.xml:** provides an editor for your POM's source XML.\n\nThe figure below shows the `pom.xml` file editor and its modes.\n\n![Figure 4: Liferay Dev Studio provides five interactive modes to help you edit and organize your POM..](../../../images/pom-editor-features.png)\n\nBy taking advantage of these interactive modes, Dev Studio makes modifying and\norganizing your POM and its dependencies a snap! \n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/intellij/01-intro.markdown",
    "content": "---\nheader-id: intellij\n---\n\n# IntelliJ\n\n[TOC levels=1-4]\n\nThe\n[Liferay IntelliJ plugin](https://plugins.jetbrains.com/plugin/10739-liferay-intellij-plugin)\nprovides support for @product@ development in\n[IntelliJ IDEA](https://www.jetbrains.com/idea/). Liferay's IntelliJ plugin\nprovides the following built-in features:\n\n- Creating a Liferay Workspace (Gradle and Maven based)\n- Creating Liferay projects (Gradle and Maven based)\n- @product@ Tomcat/Wildfly server support for project deployment and debugging\n- Support for adding line markers for each entity in the service editor\n- Syntax checking, highlighting, and code completion (e.g., Bnd and XML files)\n\nIn these articles, you'll learn how to install the Liferay IntelliJ plugin and\nleverage its features to improve Liferay development with IntelliJ IDEA.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/intellij/02-installing-liferay-intellij-plugin.markdown",
    "content": "---\nheader-id: installing-the-liferay-intellij-plugin\n---\n\n# Installing the Liferay IntelliJ Plugin\n\n[TOC levels=1-4]\n\nThere are two ways to install the Liferay IntelliJ plugin:\n\n- [via IntelliJ Marketplace](#installing-via-intellij-marketplace)\n- [via Zip file](#installing-via-zip-file)\n\nFollow the steps pertaining to your preferred installation process.\n\n## Installing Via IntelliJ Marketplace\n\n1.  In IntelliJ, navigate to *File* &rarr; *Settings* &rarr; *Plugins*.\n\n2.  In the Marketplace tab, search for *Liferay* in the provided search bar.\n\n3.  Click *Install* next to the Liferay IntelliJ Plugin.\n\n    ![Figure 1: IntelliJ Marketplace offers a streamlined way to install plugins.](../../../images/intellij-marketplace-installation.png)\n\n4.  After the plugin has downloaded, select *Restart IDE*.\n\nOnce IntelliJ restarts, the Liferay IntelliJ plugin is installed and ready for\nuse.\n\n## Installing Via Zip File\n\n1.  Navigate to the\n    [JetBrains' Liferay IntelliJ plugin](https://plugins.jetbrains.com/plugin/10739-liferay-intellij-plugin)\n    page and download it to your local machine.\n\n2.  In IntelliJ, navigate to *File* &rarr; *Settings* &rarr; *Plugins*.\n\n3.  Click the gear icon from the top menu and select *Install Plugin from\n    Disk...*.\n\n4.  Select the Liferay IntelliJ plugin and click *OK*.\n\n5.  Navigate to the Installed tab in the top menu and select *Restart IDE*.\n\nOnce IntelliJ restarts, the Liferay IntelliJ plugin is installed and ready for\nuse.\n\nGreat job! You're now ready to develop for @product@ in IntelliJ!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/intellij/03-installing-a-server-in-intellij-idea.markdown",
    "content": "---\nheader-id: installing-a-server-in-intellij\n---\n\n# Installing a Server in IntelliJ\n\n[TOC levels=1-4]\n\nInstalling a Liferay server in IntelliJ is easy. In just a few steps, you'll\nhave your server up and running.\n\n| **Note:** Tomcat and Wildfly are the only supported Liferay app server bundles\n| available to install in IntelliJ.\n\nFollow these steps to install your server:\n\n1.  Right-click your Liferay Workspace and select *Liferay* &rarr; *InitBundle*.\n\n    This downloads the @product@ bundle specified in your workspace's\n    `gradle.properties` file. You can change the default bundle by updating the\n    `liferay.workspace.bundle.url` property. For example, this is required to\n    update the default bundle version and/or type (e.g., Wildfly). The\n    downloaded bundle is stored in the workspace's `bundles` folder.\n\n2.  Navigate to the top right Configurations dropdown menu and select *Edit\n    Configurations*. From here, you can configure your server's run and debug\n    configurations.\n\n    ![Figure 1: You have several options to choose from the server dropdown menu.](../../../images/intellij-server-dropdown.png)\n\n3.  Click the *Add New Configuration* button\n    (![Add Config](../../../images/icon-intellij-add-config.png)) and select\n    *Liferay Server*.\n\n4.  Give your server a better name and modify any other configurations, if\n    necessary. Then select *OK* .\n\n    ![Figure 2: Set your Liferay server's configurations in the Run/Debug Configurations menu.](../../../images/intellij-run-debug-wizard.png)\n\nYour server is now available in IntelliJ! Make sure to select it in the\nConfigurations dropdown before executing the configuration buttons (below).\n\nFor reference, here's how the IntelliJ configuration buttons work with your\n@product@ instance:\n\n- *Start* (![Start Server](../../../images/icon-intellij-start-server.png)):\n  Starts the server.\n- *Stop* (![Stop Server](../../../images/icon-intellij-stop-server.png)): Stops\n  the server.\n- *Debug* (![Debug Server](../../../images/icon-intellij-debug-server.png)):\n  Starts the server in debug mode. For more information on debugging in\n  IntelliJ, see the\n  [IntelliJ Debugging](https://www.jetbrains.com/help/idea/debugging-code.html)\n  article.\n\nNow you're ready to use your server in IntelliJ!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/intellij/04-updating-liferay-intellij-plugin.markdown",
    "content": "---\nheader-id: updating-liferay-intellij-plugin\n---\n\n# Updating Liferay IntelliJ Plugin\n\n[TOC levels=1-4]\n\nIf you're already using the Liferay IntelliJ plugin but need to update your\nenvironment, follow the steps below:\n\n1.  In IntelliJ, navigate to *File* &rarr; *Settings* &rarr; *Plugins*.\n\n2.  Click the *Updates* tab. If the Liferay IntelliJ plugin is outdated, it will\n    be listed as an available update.\n\n    ![Figure 1: Check for updates periodically to ensure you're leveraging the latest features.](../../../images/update-intellij-plugin.png)\n\n3.  Click the *Update* button (if available) to update the Liferay IntelliJ\n    plugin.\n\n4.  Once it's downloaded, click the *Restart IDE* button.\n\n    ![Figure 2: The Available Updates prompt also prints the plugin version to which you're updating.](../../../images/intellij-update-restart.png)\n\nOnce IntelliJ restarts, the Liferay IntelliJ plugin is updated and ready for\nuse.\n\n\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-js-generator/01-js-generator-intro.markdown",
    "content": "---\nheader-id: js-generator\n---\n\n# Liferay JS Generator\n\n[TOC levels=1-4]\n\nThe Liferay JS Generator generates JavaScript widgets using pure JavaScript \ntooling. You don't have to have a deep understanding of Java to write a widget \nfor @product@. See the [Liferay JS Generator developer documentation](/docs/7-2/frameworks/-/knowledge_base/f/creating-and-bundling-javascript-widgets-with-javascript-tooling) \nfor more information on configuring generated JavaScript widgets. This section \ncovers these reference topics for the Liferay JS Generator:\n\n- How to install the Liferay JS Generator and use it to create a JS widget\n- An explanation of JS Portlet Extender's method signature\n- A reference list of the available configuration options for system settings \n  and instance settings\n\n| **Note:** The Liferay Bundle Generator is deprecated as of v2.7.1 of the \n| [Liferay JS Toolkit](https://github.com/liferay/liferay-js-toolkit). It has \n| been renamed the Liferay JS Generator. If you're still running the Liferay \n| Bundle Generator, we recommend that you install the Liferay JS Generator \n| instead at your earliest convenience, as the Liferay Bundle Generator will be \n| removed in future versions.\n\nThe available commands for bundles created with the Liferay JS Generator are \nlisted below:\n\n| Command | Description |\n| --- | --- |\n| `npm run build` | Places the output of liferay-npm-bundler in the designated output folder. The standard output is a JAR file that can be deployed manually to @product@. |\n| `npm run deploy` | Deploys the application to the configured server. |\n| `npm run start` | Tests the application in a local webpack installation instead of a @product@ server. This speeds up development because you can see live changes without the need to deploy. Note, however, that because it's outside a Liferay instance, you don't have access to Liferay's APIs. |\n| `npm run translate` | Runs the translation features for your bundle. Note that this feature requires Microsoft Translator credentials. See [Using Translation Features in Your Widget](/docs/7-2/frameworks/-/knowledge_base/f/using-translation-features-in-your-widget) for more information. |\n\n| **Note:** By default, the webpack server uses port 8080, which conflicts with\n| the port used by Tomcat. You can point the \n| webpack server to a different port by setting the `port` key in `.npmbuildrc`:\n| \n| ```json\n| \"webpack\": {\n|   \"port\": 2070\n| }\n| ```\n\nRead this section to learn how to install the Liferay JS Generator and \nunderstand its configuration. \n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-js-generator/02-installing-the-js-generator-and-creating-js-portlets.markdown",
    "content": "---\nheader-id: installing-the-js-generator-and-generating-a-bundle\n---\n\n# Installing the JS Generator and Generating a Bundle\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n    <span class=\"wysiwyg-color-blue120\">This document has been replaced by an article on <a href=\"https://learn.liferay.com/dxp/latest/en/building-applications/tooling/other-tools/liferay-cli-tool.html\">Liferay Learn</a> and is no longer maintained here. The Liferay CLI tool is used for creating JavaScript application projects for Liferay versions 7.1+.</span>\n</aside>\n\nHere you'll learn how to install the \n[Liferay JS Generator](https://www.npmjs.com/package/generator-liferay-bundle) \nand use it to create JavaScript widgets. See the \n[Angular Application](/docs/7-2/appdev/-/knowledge_base/a/developing-an-angular-application), \n[React Application](/docs/7-2/appdev/-/knowledge_base/a/developing-a-react-application), \nand \n[Vue Application](/docs/7-2/appdev/-/knowledge_base/a/developing-a-vue-application) \narticles to learn how to use your existing Angular, React, and Vue apps in \n@product@. \n\n| **Note:** To use the Liferay JS Generator, you must have the Liferay JS \n| Portlet Extender activated in your @product@ instance. It's activated by \n| default. You can confirm this by opening the Control Menu, navigating to the \n| *App Manager*, and searching for `com.liferay.frontend.js.portlet.extender`.\n\nFollow these steps to create your JavaScript widget:\n\n1.  Install \n    [Node.js](http://nodejs.org/). \n    Note that Node Package Manager (npm) is installed with this as well. You'll \n    use npm to install the remaining dependencies and generator, and \n    [configure your npm environment](/docs/7-2/reference/-/knowledge_base/r/setting-up-your-npm-environment).\n\n2.  Install [Yeoman](http://yeoman.io/) for the generator:\n\n    ```bash\n    npm install -g yeoman\n    ```\n\n3.  Install the Liferay JS Generator:\n\n    ```bash\n    npm install -g generator-liferay-js\n    ```\n\n4.  Run the generator with the command below, select the JavaScript widget you \n    want to create, and answer the prompts that follow.\n\n    ```bash\n    yo liferay-js\n    ```\n\n    ![Figure 1: The liferay-js generator prompts you for widget options.](../../../images/liferay-js-generator-prompts.png)\n\n5.  If you specified your app server information when your widget was \n    generated, you can deploy your widget by running the command below. You can \n    verify this by checking the value of the `liferayDir` entry in the widget's \n    `.npmbuildrc`.\n\n    ```bash\n    npm run deploy\n    ```\n\nGreat! Now you know how to install and run the Liferay JS Generator. \n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-js-generator/04-understanding-the-js-portlet-extender-configuration.markdown",
    "content": "---\nheader-id: understanding-the-js-portlet-extender-configuration\n---\n\n# Understanding the JS Portlet Extender Configuration\n\n[TOC levels=1-4]\n\nBundles generated with the Liferay JS Generator require specific method \nsignatures, MANIFEST headers, and configuration within their `package.json` \nfile to use the JS Portlet Extender. This configuration is provided by default. \n\n## Manifest Header\n\nThe OSGi bundle contains the MANIFEST header shown below, which \nspecifies a dependency on the JS Portlet Extender:\n\n```properties\nRequire-Capability: osgi.extender;filter:=\"(osgi.extender=liferay.npm.portlet)\"\n```\n\n## Main Entry Point\n\nThe main module of your JavaScript widget must export a JavaScript function \nwith the signature below. Bundles created with the Liferay JS Generator have \nthis out-of-the-box:\n\n```javascript\nfunction({portletNamespace, contextPath, portletElementId, configuration}) {\n  ...\n}\n```\n\nThe entry point function receives one object parameter with four fields:\n\n- `portletNamespace`: the unique namespace of the widget as defined in the\n  Portlet specification. \n- `contextPath`: the URL path that can be used to retrieve bundle resources from\n  the browser (it doesn't contain the protocol, host, or port, just the absolute\n  path). \n- `portletElementId`: the DOM identifier of the widget's `<div>` node that can\n  be used to render HTML. \n- `configuration` (optional): since JS Portlet Extender version 1.1.0, this\n  field contains the system (OSGi) and portlet instance (preferences as\n  described in the Portlet spec) configuration for the widget. It has two\n  subfields: \n\n  - `system:` contains the system level configuration (defined in Control \n    Panel &rarr; System Settings)\n \n  - `portletInstance:` contains the per-widget configuration (defined in the \n    Configuration menu option of the widget)\n\nNote that all values are received as strings, no matter what their type is in \nOSGi configuration store. \n\nThe JavaScript-based widget's main `index.js` file configuration is shown \nbelow for reference. Note that system settings and localization are enabled in \nthe example below:\n\n```javascript\nexport default function main({portletNamespace, contextPath, portletElementId, configuration}) {\n    \n    const node = document.getElementById(portletElementId);\n\n    node.innerHTML =`\n        <div>\n            <span class=\"tag\">${Liferay.Language.get('porlet-namespace')}:</span>\n            <span class=\"value\">${portletNamespace}</span>\n        </div>\n        <div>\n            <span class=\"tag\">${Liferay.Language.get('context-path')}:</span>\n            <span class=\"value\">${contextPath}</span>\n        </div>\n        <div>\n            <span class=\"tag\">${Liferay.Language.get('portlet-element-id')}:</span>\n            <span class=\"value\">${portletElementId}</span>\n        </div>\n        \n        <div>\n            <span class=\"tag\">${Liferay.Language.get('configuration')}:</span>\n            <span class=\"value\">\n                ${JSON.stringify(configuration, null, 2)}\n            </span>\n        </div>\n        \n    `;\n    \n}\n```\n\nThe JavaScript file containing the main entry point function is specified in the \n`main` entry of the `package.json` file. Below is the `main` entry for the \n*JavaScript based widget*:\n\n```json    \n\"main\": \"index.js\"\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-js-generator/05-configuration-json-options.markdown",
    "content": "---\nheader-id: configuration-json-available-options\n---\n\n# Configuration JSON Available Options\n\n[TOC levels=1-4]\n\nIf you've \n[created an OSGi bundle with the Liferay JS Generator](/docs/7-2/reference/-/knowledge_base/r/installing-the-js-generator-and-generating-a-bundle) \nand want to provide system settings or instance settings for your widget, you \nmust provide a `configuration.json` file. This reference guide lists the \navailable configuration options for `configuration.json` along with example \ncode. \n\n## JSON Format\n\nThe `configuration.json` must follow the basic pattern shown below:\n\n```json\n{\n  \"system\": {\n    \"category\": \"{category identifier}\",\n    \"name\": \"{name of configuration}\",\n    \"fields\": {\n      \"{field id 1}\": {\n        \"type\": \"{field type}\",\n        \"name\": \"{field name}\",\n        \"description\": \"{field description}\",\n        \"default\": \"{default value}\",\n        \"options\": {\n          \"{option id 1}\": \"{option name 1}\",\n          \"{option id 2}\": \"{option name 2}\",\n\n          \"{option id n}\": \"{option name n}\"\n        }\n      },\n      \"{field id 2}\": {},\n\n      \"{field id n}\": {}\n    }\n  },\n  \"portletInstance\": {\n    \"name\": \"{name of configuration}\",\n    \"fields\": {\n      \"{field id 1}\": {\n        \"type\": \"{field type}\",\n        \"name\": \"{field name}\",\n        \"description\": \"{field description}\",\n        \"default\": \"{default value}\",\n        \"options\": {\n          \"{option id 1}\": \"{option name 1}\",\n          \"{option id 2}\": \"{option name 2}\",\n\n          \"{option id n}\": \"{option name n}\"\n        }\n      },\n      \"{field id 2}\": {},\n\n      \"{field id n}\": {}\n    }\n  }\n}\n```\n\nThe available options are described in the table below:\n\n| Option | Value |\n| --- | --- |\n| `{category identifier}` | Describes the identifier of the configuration category where the settings must be placed. It's equivalent to the category field of the `@ExtendedObjectClassDefinition` annotation explained [here](/docs/7-2/frameworks/-/knowledge_base/f/categorizing-the-configuration). The category field of `configuration.json` is optional and, when not set, the project's name specified in `package.json` is used. You need JS Portlet Extender 1.1.0+ for this feature to work. Otherwise, the system configuration will show up under *Platform* &rarr; *Third Party* in System Settings. |\n| `{name of configuration}` | the configuration's name as a string or a localization key. If no value is given, the bundler falls back to the project's name, then description given in `package.json`. |\n| `{field id}` | the field's name as a string or a localization key |\n| `{field type}` | specifies the field's type, which can be one of the following types: &nbsp;- `number`: an integer number&nbsp;- `float`: a floating point number&nbsp;- `string`: a string&nbsp;- `boolean`: true or false&nbsp;- `password`: a password (string) |\n| `{field name}` | the field's name as a string or a localization key |\n| `{field description}` | an optional string or a localization key that describes the field's purpose and appears as hint text near it |\n| `{default value}` | an optional default value for the field |\n| `options` | an optional section that defines a fixed set of values for the field |\n| `{option id}` | a string that defines the option's ID |\n| `{option name}` | the option's name as a string or a localization key |\n\nAn example configuration is shown below:\n\n```json\n{\n  \"system\": {\n    \"category\": \"third-party\",\n    \"name\": \"My project\",\n    \"fields\": {\n      \"a-number\": {\n        \"type\": \"number\",\n        \"name\": \"A number\",\n        \"description\": \"An integer number\",\n        \"default\": \"42\"\n      },\n      \"a-string\": {\n        \"type\": \"string\",\n        \"name\": \"A string\",\n        \"description\": \"An arbitrary length string\",\n        \"default\": \"this is a string\"\n      },\n      \"a-password\": {\n        \"type\": \"password\",\n        \"name\": \"A password\",\n        \"description\": \"A secret string\",\n        \"default\": \"s3.cr3t\"\n      },\n      \"a-boolean\": {\n        \"type\": \"boolean\",\n        \"name\": \"A boolean\",\n        \"description\": \"A true|false value\",\n        \"default\": true\n      },\n      \"an-option\": {\n        \"type\": \"string\",\n        \"name\": \"An option\",\n        \"description\": \"A restricted values option\",\n        \"required\": true,\n        \"default\": \"A\",\n        \"options\": {\n          \"A\": \"Option a\",\n          \"B\": \"Option b\"\n        }\n      }\n    }\n  },\n  \"portletInstance\": {\n    \"name\": \"Widget configuration\",\n    \"fields\": {\n      \"a-float\": {\n        \"type\": \"float\",\n        \"name\": \"A float\",\n        \"description\": \"A floating point number\",\n        \"default\": \"1.1\"\n      }\n    }\n  }\n}\n```\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-js-generator/06-adapting-apps-for-liferay.markdown",
    "content": "---\nheader-id: adapting-existing-apps-to-run-on-product\n---\n\n# Adapting Existing Apps to Run on @product@\n\n[TOC levels=1-4]\n\nThere are two ways to get your existing front-end applications running on\n@product@: \n\n1.  [Migrate your project](/docs/7-2/appdev/-/knowledge_base/a/web-front-ends) \n    to a Liferay JS Toolkit project. \n\n2.  Since v2.15.0 of the Liferay JS Toolkit, create projects normally, as you \n    would with [create-react-app](https://facebook.github.io/create-react-app/), \n    [Angular CLI](https://cli.angular.io/) \n    (any project containing `@angular/cli` as a dependency or devDependency), \n    and [Vue CLI](https://cli.vuejs.org/) \n    (any project containing `@vue/cli-service` as a dependency or devDependency), \n    and adapt them to run on @product@. \n\nOnly adapt your project if you intend it to be platform-agnostic. If\nyou want to integrate with @product@ fully and have access to all the \n[features and benefits](/docs/7-2/frameworks/-/knowledge_base/f/creating-and-bundling-javascript-widgets-with-javascript-tooling) \nthat it provides, [migrate your project](/docs/7-2/appdev/-/knowledge_base/a/web-front-ends) \nto a true Liferay JS Toolkit project instead.\n\nThe reason for this is some of @product@'s features may not be available because\nthe native frameworks expect certain things. For example, Angular assumes that\nit controls a whole Single Page Application as opposed to the small portion of\nthe page that it controls in a portlet-based platform such as @product@. Since\nwebpack bundles all JavaScript in a single file to consume per app, if there are\nfive widgets on a page, you have five copies of the framework in the JavaScript\ninterpreter. To prevent this, \n[migrate your project](/docs/7-2/appdev/-/knowledge_base/a/web-front-ends) to \na true Liferay JS Toolkit project instead. \n\nTo adapt your project, it must have the structure shown below:\n\n- **Angular CLI projects** must use `app-root` as the application's Dom \n  selector. \n\n- **creact-react-app projects** must use `ReactDom.render()` call in your entry \n  point with a `document.getElementById()` parameter.\n  \n- **Vue CLI projects** must use `#app` as the application's DOM selector.\n\nWhen your project meets the requirements, you can follow these steps to use the \nLiferay JS Generator to adapt it:\n\n1.  Open the command line and navigate to your project's folder.\n\n2.  Run the Liferay JS Generator's `adapt` subtarget:\n\n    ```bash\n    yo liferay-js:adapt\n    ```\n\n    ![Figure 1: You can run the adapt subtarget of the Liferay JS Generator to adapt your existing apps for Liferay.](../../../images/liferay-js-generator-adapt-run.png)\n\n3.  Answer the prompts. An example configuration appears below:\n\n    ```bash\n    ? Under which category should your widget be listed? category.sample\n    ? Do you have a local installation of Liferay for development? Yes\n    ? Where is your local installation of Liferay placed? /home/user/liferay\n    ```\n\n    Your project is adapted to use the Liferay JS Toolkit and run on @product@! \n\n    ![Figure 2: You can run the adapt subtarget of the Liferay JS Generator to adapt your existing apps for Liferay.](../../../images/liferay-js-generator-adapt-complete.png)\n\n4.  The adapt process automatically adds a few npm scripts to the project's \n    `package.json` so you can build and deploy your project to @product@. Note \n    that you can swap `npm` for `yarn` below if you prefer to use yarn instead.\n\n    Run the command below to add a deployable JAR to the `build.liferay` folder \n    in your project. For example, if you want to build the JAR and copy it to \n    another app server, you can run this command:\n\n    ```bash\n    npm run build:liferay\n    ```\n\n    Run the command below to deploy the adapted app to your @product@ instance:\n\n    ```bash\n    npm run deploy:liferay\n    ```\n\nGreat! Now you know how to use the Liferay JS Generator to adapt your existing \napps to run on @product@. See the \n[React Guestbook App](https://github.com/liferay/liferay-docs/tree/master/en/developer/reference/code/adapted-react-app/) \nfor a working example of an adapted app. \n\n![Figure 3: Your adapted app runs in Liferay in no time.](../../../images/liferay-js-generator-adapt-deployed.png)\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/01-intro.markdown",
    "content": "---\nheader-id: liferay-workspace\n---\n\n# Liferay Workspace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/what-is-liferay-workspace.html\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nA *Liferay Workspace* is a generated environment that is built to hold and\nmanage your Liferay projects. This workspace is intended to aid in the\nmanagement of Liferay projects by providing various build scripts and configured\nproperties. You can download the\n[Liferay Project SDK installer](https://sourceforge.net/projects/lportal/files/Liferay%20IDE/)\nand run it to install\n[Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli) (default\nCLI for workspace), initialize a new Liferay Workspace, and download @ide@.\n\nLiferay Workspace is the official way to create/manage @product-ver@ projects\nusing Gradle. Do you prefer Maven over Gradle? You can also generate a\nMaven-based workspace.\n\nYou'll cover the following topics in this section:\n\n- [Installing Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/installing-liferay-workspace)\n- [Creating a Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/creating-a-liferay-workspace)\n- [Importing a Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/importing-a-liferay-workspace-into-an-ide)\n- [Setting Proxy Requirements](/docs/7-2/reference/-/knowledge_base/r/setting-proxy-requirements-for-liferay-workspace)\n- [Adding a Bundle](/docs/7-2/reference/-/knowledge_base/r/adding-a-liferay-bundle-to-liferay-workspace)\n- [Setting Environment Configurations](/docs/7-2/reference/-/knowledge_base/r/setting-environment-configurations-for-liferay-workspace)\n- [Building Node.js Themes](/docs/7-2/reference/-/knowledge_base/r/building-node-js-themes-in-liferay-workspace)\n- [Building Gradle/Maven Themes](/docs/7-2/reference/-/knowledge_base/r/building-gradle-maven-themes-in-liferay-workspace)\n- [Managing the Target Platform](/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform)\n- [Validating Modules Against the Target Platform](/docs/7-2/reference/-/knowledge_base/r/validating-modules-against-the-target-platform)\n- [Leveraging Docker](/docs/7-2/reference/-/knowledge_base/r/leveraging-docker)\n- [Updating Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/updating-liferay-workspace)\n- [Updating Default Plugins Provided by Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/updating-default-plugins-provided-by-liferay-workspace)\n\nLiferay Workspaces can be used in many different development environments, which\nmakes it flexible and applicable to many different developers. For example, a\nLiferay Workspace easily integrates with Eclipse and IntelliJ, providing a\nseamless development experience. See how to\n[install](/docs/7-2/reference/-/knowledge_base/r/installing-liferay-workspace)\nand\n[create](/docs/7-2/reference/-/knowledge_base/r/creating-a-liferay-workspace)\na Liferay Workspace for more information.\n\nYou'll learn about workspace's anatomy and development lifecycle next.\n\n## Workspace Anatomy\n\nA Liferay Workspace offers a development environment that can be configured to\nfit your development needs. Properties are available to help manage default and\noptional folders. This provides you the power to customize your workspace's\nfolder structure any way you'd like. The top-level files/folder of a Liferay\n(Gradle) Workspace are outlined below:\n\n- `bundles` (generated): the default folder for @product@ bundles.\n- `configs`: holds the configuration files for different environments. These\n  files serve as your global configuration files for all @product@ servers and\n  projects residing in your workspace. To learn more about using the `configs`\n  folder, see the [Testing Projects](#testing-projects) section.\n- `ext` (generated): holds the Ext OSGi modules and Ext plugins.\n- `gradle`: holds the Gradle Wrapper used by your workspace.\n- `modules`: holds your custom modules. This can also hold front-end portlets\n  created with the\n  [Liferay JS Toolkit](/docs/7-2/reference/-/knowledge_base/r/js-generator).\n- `themes`: holds Node.js-style themes that use the Liferay JS Theme Toolkit, \n  which are built using the Liferay Theme Generator.\n- `wars`: holds traditional WAR-style web application projects and theme\n  projects (i.e., generated by the\n  [`theme`](/docs/7-2/reference/-/knowledge_base/r/theme-template) project\n  template).\n- `build.gradle`: the common Gradle build file.\n- `gradle.properties`: specifies the workspace's project locations and @product@\n  server configuration globally. \n- `gradle-local.properties`: sets user-specific properties for your workspace.\n  This lets multiple users use a single workspace, letting them configure\n  specific properties for the workspace on their own machine.\n- `gradlew`: executes the Gradle command wrapper.\n- `settings.gradle`: applies plugins to the workspace and configures its\n  dependencies.\n\nIf you're using a workspace generated for Maven projects, your folder hierarchy\nis the same, except the Gradle build files are swapped out for a `pom.xml` file.\n\nVisit your workspace's `gradle.properties` file for a list of properties (with\ndescriptions) you can define to adapt your workspace. For a Maven-based\nworkspace, see the\n[Bundle Support Plugin](/docs/7-2/reference/-/knowledge_base/r/bundle-support-plugin)\narticle for info on adapting your Maven workspace.\n\nIf you'd like to keep the global Gradle properties the same, but want to change\nthem for yourself only (perhaps for local testing), you can override the\n`gradle.properties` file with your own `gradle-local.properties` file.\n\nNext, you'll learn about workspace's development lifecycle.\n\n## Development Lifecycle\n\nLiferay Workspaces offer a full development lifecycle for your projects to make\nyour Liferay development easier than ever. The development lifecycle includes\n\n- [Creating projects](#creating-projects)\n- [Building projects](#building-projects)\n- [Deploying projects](#deploying-projects)\n- [Testing projects](#testing-projects)\n- [Releasing projects](#releasing-projects)\n\n- [Test](#development-lifecycle)\n\nYou'll learn about each lifecycle option next.\n\n### Creating Projects\n\nWorkspace provides a slew of\n[project templates](/docs/7-2/reference/-/knowledge_base/r/project-templates)\nthat you can use to create many different types of Liferay projects. Workspace\nalso provides development support for front-end portlets generated with the \n[Liferay JS Toolkit](/docs/7-2/reference/-/knowledge_base/r/js-generator).\nThey're stored in the `modules` folder by default.\n\nYou can also configure where to generate certain projects (modules, themes,\nWARs, etc.). These settings are documented in the `gradle.properties` file. See\nthe\n[Creating a Project](/docs/7-2/reference/-/knowledge_base/r/creating-a-project)\narticle for more information.\n\nLiferay Workspace manages theme projects in two separate folders based on how\nthey're created:\n\n- [Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/theme-generator)\n  (Node.js-based themes that use the Liferay JS Theme Toolkit)\n- [Project template/archetype](/docs/7-2/reference/-/knowledge_base/r/theme-template)\n  (Gradle/Maven-based themes)\n\nLiferay Workspace offers an environment where developers can use the Liferay\nTheme Generator to create themes and their work can be seamlessly integrated\ninto their overall DevOps strategy. You can leverage the Liferay Theme Generator\nto\n[create Node.js-based themes inside workspace](/docs/7-2/reference/-/knowledge_base/r/building-node-js-themes-in-liferay-workspace)\nor you can leverage it externally and copy themes into Workspace.\n\nWorkspace also offers a\n[traditional Java-based theme approach](/docs/7-2/reference/-/knowledge_base/r/building-gradle-maven-themes-in-liferay-workspace)\n(leveraging Gradle/Maven) for those that can't use the Liferay JS Theme\nToolkit's tools in their CI environment.\n\n### Building Projects\n\nLiferay Workspace abstracts many build requirements away so you can focus on\ndeveloping projects instead of worrying about how to build them. This is done by\nincorporating a slew of plugins under the hood to allow for easily\naccessible tooling. See the\n[Gradle Plugins](/docs/7-2/reference/-/knowledge_base/r/gradle-plugins)\nand\n[Maven Plugins](/docs/7-2/reference/-/knowledge_base/r/maven-plugins)\nsections for information on some of the plugins provided by workspace.\n\nGradle-based workspaces also include a Gradle wrapper in its ROOT folder (e.g.,\n`gradlew`), which you can leverage to execute Gradle commands. This means that\nyou can run familiar Gradle build commands (e.g., `build`, `clean`, `compile`,\netc.) from a Liferay Workspace without having Gradle installed on your machine.\nFor Maven-based workspaces, Maven build commands are supported (e.g., `package`,\n`verify`, `deploy`, etc.).\n\nLiferay Workspace lets you build your projects out-of-the-box without the hassle\nof manual build configurations.\n\n### Deploying Projects\n\nLiferay Workspace provides easy-to-use deployment mechanisms that let you deploy\nyour project to a Liferay server without any custom configuration. To learn more\nabout deploying projects from a workspace, visit the\n[Deploying a Project](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project)\narticle.\n\n### Testing Projects\n\nLiferay provides many configuration settings for @product-ver@. Configuring\nseveral different @product@ installations to simulate/test certain behaviors can\nbecome cumbersome and time consuming. With Liferay Workspace, you can easily\norganize environment settings and generate an environment installation with\nthose settings.\n\nLiferay Workspace provides the `configs` folder, which lets you configure\ndifferent environments in the same workspace. For example, you could configure\nseparate @product@ environment settings for development, testing, and production\nin a single Liferay Workspace. So how does it work?\n\nThe `configs` folder offers six subfolders:\n\n`common`: holds a common configuration that you want applied to all\nenvironments.\n\n`dev`: holds the development configuration.\n\n`docker`: holds the configuration for a Docker container.\n\n`local`: holds the configuration intended for testing locally.\n\n`prod`: holds the configuration for a production site.\n\n`uat`: holds the configuration for a UAT site.\n\nYou're not limited to just these environments. You can create any subfolder in\nthe `configs` folder (e.g., `aws`, `test`, etc.) to simulate any environment.\nEach environment folder can supply its own database, `portal-ext.properties`,\nElasticsearch, etc. The files in each folder overlay your @product@\ninstallation, which you generate from within workspace.\n\n![Figure 1: The `configs/common` and `configs/[environment]` overlay you @product@ bundle when it's generated.](../../../images/workspace-configs.png)\n\nWhen workspace generates a @product@ bundle, these things happen:\n\n1.  Configuration files found in the `configs/common` folder are applied to the\n    @product@ bundle.\n\n2.  The configured workspace environment (`dev`, `local`, etc.) is applied on\n    top of any existing configurations from the `common` folder.\n\nSee the\n[Setting Environment Configurations for Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/setting-environment-configurations-for-liferay-workspace)\narticle for more information.\n\n### Releasing Projects\n\nLiferay Workspace does not provide a built-in release mechanism, but there are\neasy ways to use external release tools with workspace. The most popular choice\nis uploading your projects to a Maven Nexus repository. You could also use other\nrelease tools like [Artifactory](https://www.jfrog.com/artifactory/).\n\nUploading projects to a remote repository is useful if you need to share them\nwith other non-workspace projects. Also, if you're ready for your projects to be\nin the spotlight, uploading them to a public remote repository gives other\ndevelopers the chance to use them.\n\nFor more instructions on how to set up a Maven Nexus repository for your\nworkspace's projects, see the\n[Creating a Maven Repository](/docs/7-2/reference/-/knowledge_base/r/creating-a-maven-repository)\nand\n[Deploying Liferay Maven Artifacts to a Repository](/docs/7-2/reference/-/knowledge_base/r/deploying-liferay-maven-artifacts-to-a-repository)\narticles.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/02-installing-liferay-workspace.markdown",
    "content": "---\nheader-id: installing-liferay-workspace\n---\n\n# Installing Liferay Workspace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/creating-a-liferay-workspace.html\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nYou can install Liferay Workspace using the Liferay Project SDK installer. This\ninstalls JPM and\n[Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli) into your user\nhome folder and optionally initializes a Liferay Workspace folder. This is the\nsame installer covered in the\n[Installing Blade CLI](/docs/7-2/reference/-/knowledge_base/r/installing-blade-cli)\narticle.\n\nFollow the steps below to download and install Liferay Workspace:\n\n1.  Download the latest\n    [Liferay Project SDK installer](https://sourceforge.net/projects/lportal/files/Liferay%20IDE/)\n    that corresponds with your operating system (e.g., Windows, MacOS, or\n    Linux). The Project SDK installer is listed under *Liferay IDE*, so the\n    folder versions are based on IDE releases. You can select an installer that\n    does not include @ide@, if you don't intend to use it. The Project SDK\n    installer is available for versions 3.2.0+. Do **not** select the large\n    green download button; this downloads Liferay Portal instead.\n\n2.  Run the installer. Click *Next* to step through the installer's\n    introduction.\n\n3.  Set the folder where your Liferay Workspace should be initialized.\n\n    ![Figure 1: Determine where your Liferay Workspace should reside.](../../../images/blade-installer-workspace-init.png)\n\n    Then click *Next*.\n\n4.  Choose the Liferay product type you intend to use with the workspace. Then\n    click *Next*.\n\n    ![Figure 2: Select the product version you'll use with your Liferay Workspace.](../../../images/installer-workspace-type.png)\n\n    | **Note:** You'll be prompted for your liferay.com username and password\n    | before downloading the Liferay DXP bundle. Your credentials are not saved\n    | locally; they're saved as a token in the `~/.liferay` folder. The token is\n    | used by your workspace if you ever decide to redownload a DXP bundle.\n    | Furthermore, the bundle that is downloaded in your workspace is also\n    | copied to your `~/.liferay/bundles` folder, so if you decide to initialize\n    | another @product@ instance of the same version, the bundle is not\n    | re-downloaded. See the\n    | [Adding a Liferay Bundle to Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/adding-a-liferay-bundle-to-liferay-workspace)\n    | for more information on this topic.\n\n5.  Click *Next* to begin installing Liferay Workspace on your machine.\n\nThat's it! Liferay Workspace is now installed on your machine!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/03-creating-a-liferay-workspace.markdown",
    "content": "---\nheader-id: creating-a-liferay-workspace\n---\n\n# Creating a Liferay Workspace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/creating-a-liferay-workspace.html\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nYou can create a Liferay Workspace using the following tools:\n\n- [Blade CLI](#blade-cli)\n- [Dev Studio](#dev-studio)\n- [IntelliJ](#intellij)\n- [Maven](#maven)\n\nVisit the appropriate section to learn how to create a workspace with the\nhighlighted tool.\n\n## Blade CLI\n\n1.  Navigate to the folder where you want your workspace generated.\n\n2.  Run the following command to build a Gradle-based workspace:\n\n    ```bash\n    blade init -v 7.2 [WORKSPACE_NAME]\n    ```\n\n    | **Note**: The version you set when first initializing your workspace is\n    | stored in the workspace's `.blade.properties` file with the\n    | `liferay.version.default` property. This version is applied when creating\n    | projects based on the corresponding project template versions.\n    |\n    | If you wish to develop projects for a different @product@ version, you can\n    | pass a different version in the Blade init command. For example,\n    |\n    | ```bash\n    | blade init -v 7.0 [WORKSPACE_NAME]\n    | ```\n\nYou can also create a Maven-based workspace with Blade CLI. See the\n[Maven](#maven) section for more information.\n\n## Dev Studio\n\n1.  Select *File* &rarr; *New* &rarr; *Liferay Workspace Project*.\n\n    ![Figure 1: By selecting *Liferay Workspace Project*, you begin the process of creating a new workspace for your Liferay projects.](../../../images/selecting-liferay-workspace.png)\n\n    A New Liferay Workspace dialog appears, presenting several configuration\n    options.\n\n2.  Give your workspace project a name.\n\n3.  Choose the location where you'd like your workspace to reside. Checking the\n    *Use default location* checkbox places your Liferay Workspace in the Eclipse\n    workspace you're working in.\n\n4.  Select the build tool you want your workspace to be built with (i.e., Gradle\n    or Maven).\n\n5.  Choose the Liferay Portal version you plan to develop for (i.e., 7.2, 7.1,\n    or 7.0).\n\n6.  Select the specific target platform version corresponding to the GA release\n    you're developing for (e.g., 7.2.0 &rarr; 7.2 GA1). For more information on\n    target platform benefits, see the\n    [Managing the Target Platform](/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform)\n    articles.\n\n7.  Check the *Download Liferay bundle* checkbox if you'd like to auto-generate\n    a Liferay instance in your workspace. You'll be prompted to name the server\n    and provide the server's download URL, if selected.\n\n    | **Note:** You can configure a pre-existing Liferay bundle in your\n    | workspace by creating a folder for the bundle in your workspace and\n    | configuring it in the workspace's `gradle.properties` file by setting the\n    | `liferay.workspace.home.dir` property.\n\n8.  Check the *Add project to working set* checkbox if you want your workspace\n    to be a part of a larger working set you've already created in Dev Studio.\n    For more information on working sets, visit\n    [Eclipse Help](https://help.eclipse.org/mars/index.jsp?topic=%2Forg.eclipse.platform.doc.user%2Fconcepts%2Fcworkset.htm).\n\n9.  Click *Finish* to create your Liferay Workspace.\n\n    ![Figure 2: Dev Studio provides an easy-to-follow menu to create your Liferay Workspace.](../../../images/new-workspace-menu.png)\n\nA dialog appears prompting you to open the Liferay Workspace perspective. Click\n*Yes*, and your perspective will switch to Liferay Workspace.\n\n## IntelliJ\n\n1.  Open the New Project wizard by selecting *File* &rarr; *New* &rarr;\n    *Project*. If you're starting IntelliJ for the first time, you can do this\n    by selecting *Create New Project* in the opening window.\n\n2.  Select *Liferay* from the left menu.\n\n3.  Choose the build type for your workspace (i.e., Gradle or Maven). Then click\n    *Next*.\n\n    ![Figure 3: Choose *Liferay Gradle Workspace* or *Liferay Maven Workspace*, depending on the build you prefer.](../../../images/intellij-workspace-build.png)\n\n4.  Specify your workspace's name, location, intended @product@ version,\n    [target platform](/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform),\n    and SDK (i.e., Java JDK). Then click *Finish*.\n\n    ![Figure 4: Specify your workspace's configurations.](../../../images/intellij-workspace-project.png)\n\n5.  A window opens for additional build configurations for the build type you\n    selected (i.e., Gradle or Maven). Verify the settings and click *OK*.\n\n## Maven\n\n1.  Execute the following Maven command:\n\n    ```bash\n    mvn archetype:generate -Dfilter=liferay\n    ```\n\n2.  Select the `com.liferay.project.templates.workspace` archetype to generate.\n\n3.  Step through the remaining prompts to generate the workspace project.\n\n A Maven-based Liferay Workspace can also be generated using Blade CLI. Follow\n [Blade CLI's](#blade-cli) workspace creation instructions and insert the `-b\n maven` parameter in the Blade command.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/04-importing-a-liferay-workspace-into-an-ide.markdown",
    "content": "---\nheader-id: importing-a-liferay-workspace-into-an-ide\n---\n\n# Importing a Liferay Workspace into an IDE\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/creating-code-with-liferay-workspace.html\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay supports two IDEs with preconfigured Liferay Workspace wizards and\nfunctionalities\n\n- [Dev Studio](#dev-studio)\n- [IntelliJ](#intellij)\n\nThese aren't the only IDEs you can leverage to use Liferay Workspace, but they\nare the ones with out-of-the-box support for it.\n\nVisit the appropriate section to learn how to import a workspace with the\nhighlighted tool.\n\n## Dev Studio\n\n1.  Navigate to *File* &rarr; *Import* &rarr; *Liferay* &rarr; *Liferay\n    Workspace Project*.\n\n2.  Click *Next* and browse for your workspace project.\n\n    ![Figure 1: You can import an existing Liferay Workspace into your current Dev Studio session.](../../../images/liferay-workspace-import.png)\n\n3.  Once you've selected you workspace, click *Finish*.\n\n## IntelliJ\n\n1.  Select *File* &rarr; *New* &rarr; *Project from Existing Sources...*.\n\n2.  Select the workspace you want to import. Then click *OK*.\n\n    ![Figure 2: Specify your workspace's configurations.](../../../images/intellij-import-workspace.png)\n\n3.  Click the *Import project from external model* radio button and select the\n    build tool your workspace uses (e.g., Gradle or Maven).\n\n4.  Configure the project import (if necessary) and then click *Finish*. See the\n    [Import a Project](https://www.jetbrains.com/help/idea/creating-and-managing-projects.html#importing-project)\n    section of IntelliJ's official documentation for more information.\n\n5.  Step through the remaining import prompts and then open your imported\n    workspace as you desire (i.e., in the current window or a new window).\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/05-setting-proxy-requirements-for-liferay-workspace.markdown",
    "content": "---\nheader-id: setting-proxy-requirements-for-liferay-workspace\n---\n\n# Setting Proxy Requirements for Liferay Workspace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/creating-a-liferay-workspace.html\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nIf you're working behind a corporate firewall that requires using a proxy server\nto access external repositories, you need to add some extra configuration to\nmake Liferay Workspace work within your environment. You'll learn how to set\nproxy requirements for both Gradle and Maven environments.\n\n## Gradle\n\n1.  Open your `~/.gradle/gradle.properties` file. Create this file if it does\n    not exist.\n\n2.  Add the following properties to the file:\n\n    ```properties\n    systemProp.http.proxyHost=www.somehost.com\n    systemProp.http.proxyPort=1080\n    systemProp.https.proxyHost=www.somehost.com\n    systemProp.https.proxyPort=1080\n    ```\n\n    Make sure to replace the proxy host and port values with your own.\n\n3.  If the proxy server requires authentication, also add the following\n    properties:\n\n    ```properties\n    systemProp.http.proxyUser=userId\n    systemProp.http.proxyPassword=yourPassword\n    systemProp.https.proxyUser=userId\n    systemProp.https.proxyPassword=yourPassword\n    ```\n\nExcellent! Your proxy settings are set in your Liferay Workspace's Gradle\nenvironment.\n\n## Maven\n\n1.  Open your `~/.m2/settings.xml` file. Create this file if it does not exist.\n\n2.  Add the following XML snippet to the file:\n\n    ```xml\n    <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n        <settings xmlns=\"http://maven.apache.org/SETTINGS/1.0.0\"\n            xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n            xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd\">\n            <proxies>\n                <proxy>\n                    <id>httpProxy</id>\n                    <active>true</active>\n                    <protocol>http</protocol>\n                    <host>www.somehost.com</host>\n                    <port>1080</port>\n                </proxy>\n                <proxy>\n                    <id>httpsProxy</id>\n                    <active>true</active>\n                    <protocol>https</protocol>\n                    <host>www.somehost.com</host>\n                    <port>1080</port>\n                </proxy>\n            </proxies>\n        </settings>\n    ```\n\n    Make sure to replace the proxy host and port values with your own.\n\n3.  If the proxy server requires authentication, also add the `username` and\n    `password` proxy properties. For example, the HTTP proxy authentication\n    configuration would look like this:\n\n    ```xml\n    <proxy>\n      <id>httpProxy</id>\n      <active>true</active>\n      <protocol>http</protocol>\n      <host>www.somehost.com</host>\n      <port>1080</port>\n      <username>userID</username>\n      <password>somePassword</password>\n    </proxy>\n    ```\n\nExcellent! Your Maven proxy settings are now set.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/06-adding-a-liferay-bundle-to-workspace.markdown",
    "content": "---\nheader-id: adding-a-liferay-bundle-to-liferay-workspace\n---\n\n# Adding a Liferay Bundle to Liferay Workspace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/creating-code-with-liferay-workspace.html\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay Workspaces can generate and hold a Liferay Server. This lets you\nbuild/test your workspace's plugins against a running Liferay instance. Follow\nthe instructions below to get started.\n\n1.  Open your workspace's root `gradle.properties` file.\n\n2.  Set the `liferay.workspace.bundle.url` property to the bundle's download URL\n    you want to generate and install. For example,\n\n    ```properties\n    liferay.workspace.bundle.url=https://releases-cdn.liferay.com/portal/7.2.0-ga1/liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761.7z\n    ```\n\n    For DXP subscribers, it would look like this:\n\n    ```properties\n    liferay.workspace.bundle.url=https://api.liferay.com/downloads/portal/7.2.10/liferay-dxp-tomcat-7.2.10-ga1-20190531140450482.7z\n    ```\n\n    | **Note:** The DXP download URL must be set to the bundle hosted on\n    | *api.liferay.com*. It can be tricky to find the fully qualified bundle\n    | name/number for the DXP bundle you want. You cannot access Liferay's API\n    | site directly to find it, so you must start to download DXP manually from\n    | Liferay's Customer Portal, take note of the file name, and append it to\n    | `https://api.liferay.com/downloads/portal/`.\n\n    DXP subscribers must also set the `liferay.workspace.bundle.token.download`\n    property to `true` to allow your workspace to access Liferay's API site.\n\n3.  Navigate to your workspace's root folder and run\n\n    ```bash\n    blade server init\n    ```\n\n4.  Verify your bundle was downloaded. The bundle is generated in the `bundles`\n    folder by default. You can change this by setting the `gradle.properties`\n    file's `liferay.workspace.home.dir` property to a different folder. \n\nYou can also produce a distributable Liferay bundle (Zip or Tar) from within a\nworkspace. To do this, navigate to your workspace's root folder and run the\nfollowing command:\n\n```bash\n./gradlew distBundle[Zip|Tar]\n```\n\nYour distribution file is available from the workspace's `/build` folder.\n\n| **Note:** You can define different environments for your Liferay bundle for\n| easy testing. You can learn more about this in the\n| [Testing Projects](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace#testing-projects)\n| section.\n\nYou're all set to develop projects for a nested @product@ bundle.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/07-setting-environment-configurations-for-liferay-workspace.markdown",
    "content": "---\nheader-id: setting-environment-configurations-for-liferay-workspace\n---\n\n# Setting Environment Configurations for Liferay Workspace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#updating-liferay-workspace-and-bundled-plugins\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay Workspace offers the `configs` folder, which provides a way to\norganize multiple environment settings and generate a Liferay bundle for each\nenvironment configuration.\n\nTo simulate using the `configs` folder, you'll explore a typical scenario.\nSuppose you want a local @product@ installation for testing and a UAT\ninstallation for simulating a production site. Assume you want the following\nconfiguration for the two environments:\n\n**Local Environment**\n\n- Use MySQL database pointing to localhost\n- Skip setup wizard\n\n**UAT Environment**\n\n- Use MySQL database pointing to a live server\n- Skip setup wizard\n\nTo configure these two environments in your workspace, follow the steps below:\n\n1.  Open the `configs/common` folder and add the\n    `portal-setup-wizard.properties` file with the `setup.wizard.enabled=false`\n    property.\n\n2.  Open the `configs/local` folder and configure the MySQL database settings\n    for localhost in a `portal-ext.properties` file.\n\n3.  Open the `configs/uat` folder and configure the MySQL database settings for\n    the live server in a `portal-ext.properties` file.\n\n4.  Now that your two environments are configured, generate one of them:\n\n    ```bash\n    blade server init --environment uat\n    ```\n\n5.  To generate a distributable @product@ installation of the environment to the\n    workspace's `/build` folder, run\n\n    ```bash\n    ./gradlew distBundle[Zip|Tar] -Pliferay.workspace.environment=uat\n    ```\n\n    | **Note:** You may prefer to set your workspace environment in the\n    | `gradle.properties` file instead of passing it via Gradle command. If so,\n    | it's recommended to set the workspace environment variable inside the\n    | `[USER_HOME]/.gradle/gradle.properties` file.\n    | \n    | ```properties\n    | liferay.workspace.environment=local\n    | ```\n    | \n    | The variable is set to `local` by default.\n\n    You've successfully configured two environments and generated one of them.\n\nAwesome! You can now test various @product@ bundle environments using Liferay\nWorkspace.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/08-building-nodejs-themes-in-liferay-workspace.markdown",
    "content": "---\nheader-id: building-node-js-themes-in-liferay-workspace\n---\n\n# Building Node.js Themes in Liferay Workspace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/creating-code-with-liferay-workspace.html\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay Workspace reserves the `themes` folder only for themes that are created \nwith the Themes Generator. There are no Blade CLI-provided commands or Maven \narchetypes to generate a theme for this folder. You must leverage the \n[Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/theme-generator) \nfrom within the `themes` folder to create them; you can also copy a generated\ntheme into the folder.\n\nYou'll demo this theme management capability next. Be sure the Liferay Theme\nGenerator's required tooling is installed.\n\n1.  Navigate to your workspace's `themes` folder and run the following command:\n\n    ```bash\n    yo liferay-theme\n    ```\n\n    Follow the prompts to create your theme.\n\n2.  Navigate into your new theme and run\n\n    ```bash\n    ./gradlew build\n    ```\n\n    Liferay Workspace builds the front-end theme using Gradle. Under the hood,\n    Liferay's \n    [Node Gradle Plugin](/docs/7-2/reference/-/knowledge_base/r/node-gradle-plugin)\n    is applied and used to build your theme.\n\n3.  Workspace is smart enough to differentiate between theme types. For\n    instance, you can't copy a theme built with the Theme Generator into the\n    `wars` folder and expect it to build. You can test if your project is\n    recognized by workspace by running this command from workspace's root\n    folder:\n\n    ```bash\n    ./gradlew projects\n    ```\n\n    Your CLI should display your new theme under the `themes` project.\n\n    ```bash\n    Root project 'liferay-workspace'\n    +--- Project ':themes'\n    |    \\--- Project ':themes:my-generated-theme'\n    ```\n\n    If you moved a WAR-style theme (Gradle/Maven-based) into the `themes`\n    folder, it is not recognized by the Gradle `projects` command.\n\n| **Note:** Workspace identifies whether a theme was generated by the Theme\n| Generator by checking whether it has a `package.json` file. Any theme without\n| this file is not compatible in the `themes` folder.\n\nExcellent! You learned how generated themes are recognized in workspace and \nwhere they should reside. For more information on building Gradle/Maven-based\nthemes in workspace, see its dedicated\n[article](/docs/7-2/reference/-/knowledge_base/r/building-gradle-maven-themes-in-liferay-workspace).\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/09-building-gradle-maven-themes-in-liferay-workspace.markdown",
    "content": "---\nheader-id: building-gradle-maven-themes-in-liferay-workspace\n---\n\n# Building Gradle/Maven Themes in Liferay Workspace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/creating-code-with-liferay-workspace.html\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay Workspace provides the `wars` folder for any WAR-style project. Themes\ncreated with\n[Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli) or Maven\nusing the\n[`theme`](/docs/7-2/reference/-/knowledge_base/r/theme-template) project\ntemplate or archetype are automatically generated here when creating the project\nwithin Workspace.\n\nFollow the steps below to build a Gradle/Maven theme in workspace's `wars`\nfolder:\n\n1.  Follow the\n    [Creating a Project](/docs/7-2/reference/-/knowledge_base/r/creating-a-project)\n    article to generate a project based on a project template or archetype. Make\n    sure to select the `theme` template.\n\n    Themes built using Liferay's `theme` project template are always WARs and\n    should always reside in Workspace's `wars` folder. They should never be\n    moved to the `themes` folder; that folder is reserved for\n    [themes generated by the Theme Generator](/docs/7-2/reference/-/knowledge_base/r/building-node-js-themes-in-liferay-workspace).\n\n2.  Navigate into your new theme and run\n\n    ```bash\n    ./gradlew build\n    ```\n\n    Liferay Workspace builds the theme using Gradle. Under the hood, Liferay's \n    [Theme Builder Gradle Plugin](/docs/7-2/reference/-/knowledge_base/r/theme-builder-gradle-plugin)\n    is applied and used to build your theme. It works similarly in a Maven\n    workspace. See the \n    [Building Themes in a Maven Project](/docs/7-1/frameworks/-/knowledge_base/frameworks/building-themes-in-a-maven-project)\n    article for more information.\n\nAwesome! You know how WAR-style themes are built in workspace and where they\nshould reside.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/10-managing-the-target-platform/01-intro.markdown",
    "content": "---\nheader-id: managing-the-target-platform\n---\n\n# Managing the Target Platform\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#managing-the-target-platform\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n| **Note:** The Target Platform articles currently assume you're using Gradle\n| as a build tool. If your projects are built with Maven, you can still\n| leverage the Target Platform features, but it is not built into Liferay\n| Workspace *yet* ([LPS-90524](https://issues.liferay.com/browse/LPS-90524)).\n| See the\n| [Targeting a Platform with Maven](/docs/7-2/reference/-/knowledge_base/r/targeting-a-platform-with-maven)\n| article to set the Target Platform for Maven-based projects.\n\nLiferay Workspace helps you target a specific release of @product@, so\ndependencies get resolved properly. This makes upgrades easy: specify your\ntarget platform, and Workspace points to the new version. All your dependencies\nare updated to the latest ones provided in the targeted release.\n\n| **Note:** There are times when configuring dependencies based on a version\n| range is better than tracking exact versions. See the\n| [Semantic Versioning](/docs/7-2/customization/-/knowledge_base/c/semantic-versioning)\n| tutorial for more details.\n\nNext, you'll discover how all of this is possible.\n\n## Dependency Management with BOMs\n\nYou can target a version by importing a predefined bill of materials (BOM). This\nonly requires that you specify a property in your workspace's\n`gradle.properties` file (see\n[this article](/docs/7-2/reference/-/knowledge_base/r/setting-the-target-platform)\nfor details).\n\n| **Note:** The Target Platform feature is only supported for Gradle projects at\n| this time.\n\nEach @product@ version has a predefined BOM that you can specify for your\nworkspace to reference. Each BOM defines the artifacts and their versions used\nin the specific release. BOMs list all dependencies in a management fashion, so\nit doesn't **add** dependencies to your project; it only **provides** your build\ntool (e.g., Gradle or Maven) the versions needed for the project's defined\nartifacts. This means you don't need to specify your dependency versions; the\nBOM automatically defines the appropriate artifact versions based on the BOM.\n\nYou can override a BOM's defined artifact version by specifying a different\nversion in your project's `build.gradle`. Artifact versions defined in your\nproject's build files override those specified in the predefined BOM. Note that\noverriding the BOM can be dangerous; make sure the new version is compatible in\nthe targeted platform.\n\nFor more information on BOMs, see the\n[Importing Dependencies](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism#Importing_Dependencies)\nsection in Maven's official documentation. To view a BOM file and its mapping of\nartifacts and versions, visit [repository.liferay.com](https://repository.liferay.com)\nand search for the BOM artifacts (e.g.,\n[release.portal.bom](https://repository.liferay.com/nexus/index.html#nexus-search;quick~release.portal.bom)\nand\n[release.dxp.bom](https://repository.liferay.com/nexus/index.html#nexus-search;quick~release.dxp.bom)).\n\nPretty cool, right? Next, you'll learn how to leverage platform targeting in Dev\nStudio.\n\n## Leveraging Target Platform in Dev Studio\n\n[Liferay Dev Studio 3.2+](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio) \nhelps you streamline targeting a specific version even more. Dev Studio can\nindex the configured @product@ source code to\n\n- provide advanced Java search (Open Type and Reference Searching)\n  ([article](/docs/7-2/reference/-/knowledge_base/r/searching-product-source-in-dev-studio))\n- debug @product@ sources\n  ([article](/docs/7-2/reference/-/knowledge_base/r/debugging-product-source-in-dev-studio))\n\nTo enable this functionality, set the following property in your workspace's\n`gradle.properties` file:\n\n```properties\ntarget.platform.index.sources=true\n```\n\n| **Note:** Portal source indexing is disabled in Gradle workspace version\n| 2.0.3+ (Target Platform plugin version 2.0.0+). See the\n| [Updating Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/updating-liferay-workspace)\n| article for instructions on how to update your workspace.\n\nThese options in Dev Studio are only available when developing in a Liferay\nWorkspace, or if you have the\n[Target Platform Gradle plugin](/docs/7-2/reference/-/knowledge_base/r/target-platform-gradle-plugin)\napplied to your multi-module Gradle project with specific configurations. See\nthe\n[Targeting a Platform Outside of Workspace](/docs/7-2/reference/-/knowledge_base/r/targeting-a-platform-outside-of-workspace)\narticle for more info on applying the Target Platform Gradle plugin.\n\nContinue on to learn how to set the target platform.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/10-managing-the-target-platform/02-setting-the-target-platform.markdown",
    "content": "---\nheader-id: setting-the-target-platform\n---\n\n# Setting the Target Platform\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#managing-the-target-platform\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nSetting the target platform version to develop for takes two steps: \n\n1.  Open the workspace's `gradle.properties` file and set the\n    `liferay.workspace.target.platform.version` property to the version you want\n    to target. For example,\n\n    ```properties\n    liferay.workspace.target.platform.version=7.2.0\n    ```\n\n    | **Note:** You must explicitly uncomment the property in your workspace's\n    | `gradle.properties` file to set it. Target Platform is not enabled by\n    | default.\n\n    If you're using Liferay DXP, you can set the property like this:\n\n    ```properties\n    liferay.workspace.target.platform.version=7.2.10\n    ```\n\n    The versions following a GA1 release of DXP follow fix pack versions (e.g.,\n    `7.2.10.fp1`, `7.2.10.fp2`, etc.).\n\n2.  Once the target platform is configured, check to make sure no dependencies\n    in your Gradle build files specify a version. The versions are now imported\n    from the configured target platform's BOM. For example, a simple MVC\n    portlet's `build.gradle` may look something like this:\n\n    ```groovy\n    dependencies {\n        compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n        compileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\"\n        compileOnly group: \"javax.portlet\", name: \"portlet-api\"\n        compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n        compileOnly group: \"jstl\", name: \"jstl\"\n        compileOnly group: \"org.osgi\", name: \"osgi.cmpn\"\n    }\n    ```\n\n| **Note**: The `liferay.workspace.target.platform.version` property also sets\n| the distro JAR, which can be used to validate your projects during the build\n| process. See the\n| [Validating Modules Against the Target Platform](/docs/7-2/reference/-/knowledge_base/r/validating-modules-against-the-target-platform)\n| articles for more info.\n\n| **Note:** The target platform functionality is available in Liferay Workspace\n| version 1.9.0+. If you have an older version, you must update it to leverage\n| platform targeting. See the\n| [Updating Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/updating-liferay-workspace)\n| article to do this.\n\nYou've configured your target platform in workspace. You're all set!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/10-managing-the-target-platform/03-targeting-a-platform-outside-of-workspace.markdown",
    "content": "---\nheader-id: targeting-a-platform-outside-of-workspace\n---\n\n# Targeting a Platform Outside of Workspace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#managing-the-target-platform\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nIf you prefer to not use Liferay Workspace, but still want to target a platform,\nyou must apply the \n[Target Platform Gradle plugin](/docs/7-2/reference/-/knowledge_base/r/target-platform-gradle-plugin)\nto the root `build.gradle` file of your custom multi-module Gradle build.\n\nTo do this, follow the steps below.\n\n1.  Open your project's `build.gradle` file and add this:\n\n    ```groovy\n    buildscript {\n        dependencies {\n            classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.target.platform\", version: \"2.0.0\"\n        }\n        repositories {\n            maven {\n                url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n            }\n        }\n    }\n    ```\n\n    This sets the dependency on the Target Platform Gradle plugin and configures\n    the repository that provides the necessary artifacts for your project build.\n\n2.  Apply Liferay's Target Platform Gradle plugin to the `build.xml` file:\n\n    ```groovy\n    apply plugin: \"com.liferay.target.platform\"\n    ```\n\n3.  Set the Target Platform plugin's dependencies:\n\n    ```groovy\n    dependencies {\n        targetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom\", version: \"7.2.0\"\n        targetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom.compile.only\", version: \"7.2.0\"\n        targetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom.third.party\", version: \"7.2.0\"\n    }\n    ```\n\n    These dependencies are described below:\n\n    `com.liferay.ce.portal.bom`: provides all the artifacts included in\n    @product@.\n\n    `com.liferay.ce.portal.compile.only`: provides artifacts that are not\n    included in @product@, but are necessary to reference during the build\n    (e.g., `org.osgi.core`).\n\n    `release.portal.bom.third.party`: provides all third party artifacts that\n    make up the Liferay Portal bundle.\n\n    Liferay DXP users must replace the artifact names and versions:\n\n    - `release.portal.bom` &rarr; `release.dxp.bom`\n    - `release.portal.bom.compile.only` &rarr; `release.dxp.bom.compile.only`\n    - `release.portal.bom.third.party` &rarr; `release.dxp.bom.third.party`\n    - `7.2.0` &rarr; `7.2.10`\n\n4.  If you're interested in\n[advanced search](/docs/7-2/reference/-/knowledge_base/r/searching-product-source-in-dev-studio)\nand/or\n[debugging](/docs/7-2/reference/-/knowledge_base/r/debugging-product-source-in-dev-studio)\n@product@'s source using\n[Liferay Dev Studio](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio),\nyou must also apply the following configuration:\n\n    ```groovy\n    targetPlatformIDE {\n        includeGroups \"com.liferay\", \"com.liferay.portal\"\n    }\n    ```\n\n    This indexes the target platform's source code and makes it available to\n    Dev Studio.\n\nNow you can define your target platform!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/10-managing-the-target-platform/04-targetinging-a-platform-with-maven.markdown",
    "content": "---\nheader-id: targeting-a-platform-with-maven\n---\n\n# Targeting a Platform with Maven\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#managing-the-target-platform\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAlthough a Maven-based Liferay Workspace does not offer a configurable property\nto set the target platform, you can still leverage the Target Platform framework\nby adding a few dependencies to your project.\n\n1.  Open your workspace's root `pom.xml` file and add the following\n    dependencies:\n\n    ```xml\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>com.liferay.portal</groupId>\n                <artifactId>release.portal.bom</artifactId>\n                <version>7.2.0</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.liferay.portal</groupId>\n                <artifactId>release.portal.bom.compile.only</artifactId>\n                <version>7.2.0</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.liferay.portal</groupId>\n                <artifactId>release.portal.bom.third.party</artifactId>\n                <version>7.2.0</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    ```\n\n    These dependencies are described below:\n\n    `com.liferay.ce.portal.bom`: provides all the artifacts included in\n    @product@.\n\n    `com.liferay.ce.portal.compile.only`: provides artifacts that are not\n    included in @product@, but are necessary to reference during the build\n    (e.g., `org.osgi.core`).\n\n    `release.portal.bom.third.party`: provides all third party artifacts that\n    make up the Liferay Portal bundle.\n\n    Liferay DXP users must replace the artifact names and versions:\n\n    - `release.portal.bom` &rarr; `release.dxp.bom`\n    - `release.portal.bom.compile.only` &rarr; `release.dxp.bom.compile.only`\n    - `release.portal.bom.third.party`\n    - `7.2.0` &rarr; `7.2.10`\n\n2.  Go through the remaining POMs in your workspace and remove `<version>` tags\n    for all Liferay-specific artifacts. These versions are now being provided\n    by the Target Platform framework.\n\nGreat! You can now target a platform in your Maven-based workspace.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/11-validating-modules-against-the-target-platform/01-intro.markdown",
    "content": "---\nheader-id: validating-modules-against-the-target-platform\n---\n\n# Validating Modules Against the Target Platform\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#managing-the-target-platform\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n| **Important:** Validating modules with the `resolve` task is deprecated. It\n| only functions as it's documented here in versions prior to Liferay Workspace\n| (Gradle only) version 2.0.3. It is being redesigned for workspace versions\n| 2.0.3+ and is still in development at this time.\n\nAfter you write a module in Liferay Workspace, you can validate it before\ndeployment to make sure of several things: \n\n- Will my app deploy successfully? \n- Will there be some sort of missing requirement? \n- If there's an issue, how do I diagnose it? \n\nThese are all common worries that can be frustrating.\n\nInstead of deploying your app and checking for errors in the log, you can\nvalidate your app before deployment. This is done by calling Liferay Workspace's\n`resolve` task, which validates your modules against a targeted platform.\n\nYou'll cover the following topics in this section:\n\n- [Resolving your modules](#resolving-your-modules).\n- [Modifying the target platform's capabilities](#modifying-the-target-platforms-capabilities).\n- [Including the resolver in your Gradle build](#including-the-resolver-in-your-gradle-build).\n\nContinue on to learn how this works.\n\n## Resolving Your Modules\n\nYou can resolve your modules before deployment. This can be done by calling the\n`resolve` Gradle task provided by Liferay Workspace.\n\n```bash\n./gradlew resolve\n```\n\nThis task gathers all the capabilities provided by\n \n- the specified version of @product@ (i.e.,\n  [targeted platform](/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform))\n- the current workspace's modules\n\nSome capabilities/information gathered by the `resolve` task that are validated\ninclude\n\n- declared required capabilities\n- module versions\n- package imports/use constraints\n- service references\n\nIt also computes a list of run requirements for your project. Then it compares\nthe current project's requirements against the gathered capabilities. If your\nproject requires something not available in the gathered list of capabilities,\nthe task fails.\n\nThe task can only validate OSGi modules. It does not work with WAR-style\nprojects, themes, or npm portlets.\n\n| **Note:** The `resolve` task can be executed from a specific project folder or\n| from the workspace's root folder. Running the task from the root folder\n| validates all the modules in your workspace.\n\nThe `resolve` task can automatically gather the available capabilities from your\nworkspace, but you must specify this for your targeted @product@ version. To do\nthis, open your workspace's `gradle.properties` file and set the\n`liferay.workspace.target.platform.version` property to the version you want to\ntarget. For example,\n\n```properties\nliferay.workspace.target.platform.version=7.2.0\n```\n\nIf you're using Liferay DXP, you can set the property like this:\n\n```properties\nliferay.workspace.target.platform.version=7.2.10\n```\n\nThe versions following a GA1 release of DXP follow fix pack versions (e.g.,\n`7.2.10.fp1`, `7.2.10.fp2`, etc.).\n\nSetting the target platform property provides a static *distro* JAR for the\nspecified version of @product@, which contains all the metadata (i.e.,\ncapabilities, packages, versions, etc.) running in that version. The distro JAR\nis a complete snapshot of everything provided in the OSGi runtime; this serves\nas the target platform's list of capabilities that your modules are validated\nagainst.\n\nYou can now validate your module projects before deploying them! If the resolver\nthrows errors, see the article on\n[how to resolve common output errors reported by the `resolve` task](/docs/7-2/reference/-/knowledge_base/r/how-to-resolve-common-output-errors-reported-by-the-resolve-task).\nSometimes, you must modify the `resolve` task's default behavior to successfully\nvalidate your app. See the next section for more information.\n\n## Modifying the Target Platform's Capabilities\n\nIn a perfect world, everything the `resolve` task gathers and checks against\nwould work during your development process. Unfortunately, there are exceptions\nthat may force you to modify the default functionality of the `resolve` task.\n\nThere are two scenarios you may run into during development that require a\nmodification for your project to pass the resolver check.\n\n- You're depending on a third party library that is not available in the\n  targeted @product@ instance or the current workspace.\n- You're depending on a customized distribution of @product@.\n\nYou'll explore these use cases next.\n\n### Depending on Third Party Libraries Not Included in @product@\n\nThe `resolve` task, by default, gathers all of @product@'s capabilities and the\ncapabilities of your workspace's modules. What if, however, your module depends\non a third party project that is not included in either space (e.g.,\n[Google Guava](https://opensource.google.com/projects/guava))?. The `resolve`\ntask fails by default if your project depends on this project type. You\nprobably plan to have this project deployed and available at runtime, so it's\nnot a concern, but the resolver doesn't know that; you must customize the\nresolver to bypass this.\n\nThere are three ways you can do this:\n\n- [Embed the third party library in your module](/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module)\n- [Add the third party library's capabilities to the current static set of resolver capabilities](/docs/7-2/reference/-/knowledge_base/r/adding-a-third-party-librarys-capabilities-to-the-resolvers-capabilities)\n- [Skip the resolving process for your module](/docs/7-2/reference/-/knowledge_base/r/skipping-the-resolving-process-for-a-module)\n\n| **Note:** You should only embed a third party library in your module if it's\n| the only module that depends on it. You should not bypass the resolver failure\n| this way if more than one project in the OSGi container depends on that\n| library.\n\nFor help resolving third party dependency errors, see the\n[Resolving Third Party Library Package Dependencies](/docs/7-1/frameworks/-/knowledge_base/frameworks/adding-third-party-libraries-to-a-module)\ntutorial.\n\n### Depending on a Customized Distribution of @product@\n\nThere are times when manually specifying your project's list of dependent JARs\ndoes not suffice. If your app requires a customized @product@ instance to\nrun, you must regenerate the target platform's default list of capabilities with\nan updated list. Two examples of a customized @product@ instance are described\nbelow:\n\n**Example 1: Leveraging an External Feature**\n\nThere are many external features/frameworks available that are not included in\nthe downloadable bundle by default. After deploying a feature/framework, it's\navailable for your module projects to leverage. When validating your app,\nhowever, the `resolve` task does not have access to external capabilities not\nincluded by default. For example, Audience Targeting is an example of this type\nof external framework. If you're creating a Liferay Audience Targeting rule that\ndepends on the Audience Targeting framework, you can't easily provide a slew of\nJARs for your module. In this case, you should install the platform your code\ndepends on and regenerate an updated list of capabilities that your @product@\ninstance provides.\n\n**Example 2: Leveraging a Customized Core Feature**\n\nYou can extend @product@'s core features to provide a customized experience for\nyour intended audience. Once deployed, you can assume these customizations are\npresent and build other things on top of them. The new capabilities resulting\nfrom your customizations are not available, however, in the target platform's\ndefault list of capabilities. Therefore, when your application relies on\nnon-default capabilities, it fails during the `resolve` task. To get around\nthis, you must regenerate a new list of capabilities that your customized\n@product@ instance provides.\n\nTo regenerate the target platform's capabilities (distro JAR) based on the\ncurrent workspace's @product@ instance, follow the\n[Depending on a Customized Distribution of @product@](/docs/7-2/reference/-/knowledge_base/r/depending-on-a-customized-distribution-of-product)\narticle.\n\n## Including the Resolver in Your Gradle Build\n\nBy default, Liferay Workspace provides the `resolve` task as an independent\nexecutable. It's provided by the\n[Target Platform](/docs/7-2/reference/-/knowledge_base/r/target-platform-gradle-plugin)\nGradle plugin and is not integrated in any other Gradle processes. This gives\nyou control over your Gradle build without imposing strategies you may not want\nincluded in your default build process.\n\nWith that said, the `resolve` task can be useful to include in your build\nprocess if you want to check for errors in your module projects before\ndeployment. Instead of resolving your projects separately from your standard\nbuild, you can build and resolve them all in one shot.\n\nIn Liferay Workspace, the recommended path for doing this is adding it to the\ndefault `check` Gradle task. The `check` task is provided by default in a\nworkspace by the\n[Java](https://docs.gradle.org/current/userguide/java_plugin.html#_lifecycle_tasks)\nplugin. Adding the `resolve` task to the `check` lifecycle task also promotes\nthe `resolve` task to run for CI and other test tools that typically run the\n`check` task for verification. Of course, Gradle's `build` task also depends on\nthe `check` task, so you can run `gradlew build` and run the resolver too.\n\nYou can learn how to include the resolver in your Gradle build by visiting\n[this article](/docs/7-2/reference/-/knowledge_base/r/including-the-resolver-in-your-gradle-build).\n\nContinue on for various step-by-step instructions for configuring/manipulating\nthe resolver task.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/11-validating-modules-against-the-target-platform/02-adding-a-third-party-librarys-capabilities-to-the-resolvers-capabilities.markdown",
    "content": "---\nheader-id: adding-a-third-party-librarys-capabilities-to-the-resolvers-capabilities\n---\n\n# Adding a Third Party Library's Capabilities to the Resolver's Capabilities\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#managing-the-target-platform\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nYou can add your third party dependencies to the target platform's default list\nof capabilities by listing them as provided modules.\n\n1.  Open your workspace's root `build.gradle` file.\n\n2.  Add a code snippet similar to this:\n\n    ```groovy\n    dependencies {\n        providedModules group: \"GROUP_ID\", name: \"NAME\", version: \"VERSION\"\n    }\n    ```\n\n    For example, if you wanted to add\n    [Google Guava](https://opensource.google.com/projects/guava) as a provided\n    module, it would look like this:\n\n    ```groovy\n    dependencies {\n        providedModules group: \"com.google.guava\", name: \"guava\", version: \"23.0\"\n    }\n    ```\n\nThis both provides the third party dependency to the resolver, and it downloads\nand includes it in your @product@ bundle's `osgi/modules` folder when you\ninitialize it (e.g., `blade server init`).\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/11-validating-modules-against-the-target-platform/03-skipping-the-resolving-process-for-a-module.markdown",
    "content": "---\nheader-id: skipping-the-resolving-process-for-a-module\n---\n\n# Skipping the Resolving Process for a Module\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#managing-the-target-platform\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nIt may be easiest to skip validating a particular module during the resolve\nprocess.\n\n1.  Open your workspace's root `build.gradle` file.\n\n2.  Insert the following Gradle code at the bottom of the file:\n\n    ```groovy\n    targetPlatform {\n        resolveOnlyIf { project ->\n            project.name != 'PROJECT_NAME'\n        }\n    }\n    ```\n\n    Be sure to replace the `PROJECT_NAME` filler with your module's name (e.g.,\n    `test-api`).\n\n3.  (Optional) If you prefer to disable the Target Platform plugin altogether,\n    you can add a slightly different directive to your `build.gradle` file:\n\n    ```groovy\n    targetPlatform {\n        onlyIf { project ->\n            project.name != 'PROJECT_NAME'\n        }\n    }\n    ```\n\n    This both skips the `resolve` task execution and disables BOM dependency\n    management. \n\nNow the `resolve` task skips your module project.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/11-validating-modules-against-the-target-platform/04-depending-on-a-customized-distribution-of-liferay.markdown",
    "content": "---\nheader-id: depending-on-a-customized-distribution-of-product\n---\n\n# Depending on a Customized Distribution of @product@\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#managing-the-target-platform\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nTo regenerate the target platform's capabilities (distro JAR) based on the\ncurrent workspace's @product@ instance, follow the steps below:\n\n1.  Start the @product@ instance stored in your workspace. Make sure the\n    platform you want to depend on is installed.\n\n2.  Download the\n    [BND Remote Agent JAR file](https://search.maven.org/#search%7Cga%7C1%7Cbiz.aqute.remote.agent)\n    and copy it into the `osgi/modules` folder.\n\n3.  From the root folder of your workspace, run the following command:\n\n    ```bash\n    bnd remote distro -o custom_distro.jar release.portal.distro 7.2.0\n    ```\n\n    Liferay DXP users must replace the `release.portal.distro` artifact name\n    with `release.dxp.distro` and use the `7.2.10` version syntax.\n\n    This connects to the newly deployed BND agent running in @product@ and\n    generates a new distro JAR named `custom_distro.jar`. All other capabilities\n    inherit their functionality based on your @product@ instance, so verify the\n    workspace bundle is the version you plan to release in production.\n\n4.  Navigate to your workspace's root `build.gradle` file and add the following\n    dependency:\n\n    ```groovy\n    dependencies {\n        targetPlatformDistro files('custom_distro.jar')\n    }\n    ```\n\nNow your workspace is pointing to a custom distro JAR file instead of the\ndefault one provided. Run the `resolve` task to validate your modules against\nthe new set of capabilities.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/11-validating-modules-against-the-target-platform/05-including-the-resolver-in-your-gradle-build.markdown",
    "content": "---\nheader-id: including-the-resolver-in-your-gradle-build\n---\n\n# Including the Resolver in Your Gradle Build\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#managing-the-target-platform\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nTo call the `resolve` task during Gradle's `check` task automatically, follow\nthe instructions below:\n\n1.  Open your workspace's root `build.gradle` file.\n\n2.  Add the following directive:\n\n    ```groovy\n    check.dependsOn resolve\n    ```\n\n    The `resolve` task is now called during the `check` task.\n\n    You can also configure this for specific projects in a workspace if you\n    don't want all modules to be included in the global `check`.\n\n3.  (Optional) If the `resolve` task runs during every Gradle build, you may\n    want to prevent the build from failing if there are errors reported by the\n    resolver. To do this, open your workspace's root `build.gradle` file and add\n    the following code:\n\n    ```groovy\n    targetPlatform {\n        ignoreResolveFailures = true\n    }\n    ```\n\n    This reports the failures without failing the build. Note, this can only be\n    configured in the workspace's root `build.gradle` file.\n\nAwesome! You can now run the `resolve` task in your current Gradle lifecycle.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/11-validating-modules-against-the-target-platform/06-how-to-resolve-common-output-errors-reported-by-the-resolve-task.markdown",
    "content": "---\nheader-id: how-to-resolve-common-output-errors-reported-by-the-resolve-task\n---\n\n# How to Resolve Common Output Errors Reported by the Resolve Task\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#managing-the-target-platform\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay Workspace provides the `resolve` Gradle task to validate modules. This\nis very useful for finding issues and reporting them as output before\ndeployment. For general help with OSGi related issues, visit the\n[Troubleshooting FAQ](/docs/7-2/appdev/-/knowledge_base/a/troubleshooting-application-development-issues)\nsection.\n\nFor help interpreting the `resolve` task's output, see the list below for common\noutput errors, what they mean, and how to fix them.\n\n## Missing Import Error\n\nWhen your module refers to an unavailable import, the container throws this\nerror. For example, suppose you have a module `test-service` that depends on\nthe `com.google.common.base` package. If the container can't find that package,\nit throws this error:\n\n```bash\nResolution exception in project 'modules:test-service': Unresolved requirements in root project 'modules:test-service':\n    Mandatory:\n        [osgi.wiring.package ] com.google.common.base; version=[23.0.0,24.0.0)\n        [osgi.identity       ] test.service\n```\n\nThis kind of error can also occur when separate modules require different\nversions of another module. If you have *module A* requiring *module Test\nversion 1* and *module B* requiring *module Test version 4*, without running\nthe resolver, both modules A and B would compile successfully. When they\nwere deployed, however, one would fail in the OSGi runtime because both\ndependencies cannot be satisfied. These types of scenarios are difficult to\ndiagnose, but with the `resolve` task, can be found with ease.\n\nTo fix missing import errors, you may need to adjust the\n[export](/docs/7-2/customization/-/knowledge_base/c/exporting-packages) and/or\n[import](/docs/7-2/customization/-/knowledge_base/c/importing-packages)\nconfiguration of your modules. Also, see the\n[Resolving Third Party Library Package Dependencies](/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module)\ntutorial for more information on resolving import errors. Sometimes, this kind\nof error can be solved by editing the `resolve` task's list of capabilities. See\nthe\n[Resolving Third Party Library Package Dependencies](/docs/7-2/customization/-/knowledge_base/c/adding-third-party-libraries-to-a-module)\nsection to learn how to do this.\n\n## Missing Service Reference\n\nIf your module references a non-existent service, an error is thrown.\nThis is helpful because service reference issues are hard to diagnose during\ndeployment without using the\n[Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell).\n\nFor example, if your module `test-portlet` references a service (e.g.,\n`test.api.TestApi`) it does not have access to, the following error is thrown:\n\n```bash\nResolution exception in project 'modules:test-portlet': Unresolved requirements in project 'modules:test-portlet':\n    Mandatory:\n        [osgi.identity ] test.portlet\n        [osgi.service  ] objectClass=test.api.TestApi\n```\n\nTo fix this, you must make the service available to your module. If you're\nexpecting the service to be provided by your target platform, check to make sure\nit's being provided. If it's a service provided by a custom module, check that\nservice provider module and ensure it's correctly providing that service to\nyour module. To check the target platform for available services, follow the\nsteps below:\n\n1.  Start your target platform instance.\n\n2.  Open the Gogo shell.\n\n3.  List all services containing a keyword by running `services | grep\n    \"SERVICE_NAME\"`. It's easiest to do this rather than listing all services\n    since there are usually too many to sift through.\n\n4.  You can also list services provided by a component. Run `lb -s` to list all\n    provided bundles by their bundle symbolic name (BSN). Find the BSN for the\n    desired component and then run `scr:info <BSN>`.\n\nIf you're unable to track down your missing service, it may be provided by a\ncustomized @product@ core feature or an external @product@ feature. If this is\nthe case, it isn't included in the target platform's default capabilities. You\ncan make the custom service capability available to reference by\n[generating a new custom distro JAR](/docs/7-2/reference/-/knowledge_base/r/depending-on-a-customized-distribution-of-product).\n\n## Missing Fragment Host\n\nReferring to a non-existent fragment host throws an error. For example, if your\n`test.login` fragment is configured to modify a fragment host named\n`com.liferay.login.web` that cannot be referenced, the following error is\nthrown:\n\n```bash\nResolution exception in project 'modules:test.login': Unresolved requirements in project 'modules:test-login':\n    Mandatory:\n        [osgi.identity    ] test.login\n        [osgi.wiring.host ] com.liferay.login.web; version=1.0.10\n```\n\nConfiguring a fragment host in your module is typically done with the\n`Fragment-Host` header in the `bnd.bnd` file:\n\n```properties\nFragment-Host: com.liferay.login.web;bundle-version=\"[1.0.0,1.0.1)\"\n```\n\nTo fix this, inspect your target platform to ensure it includes the JAR you're\nattempting to add a fragment for. Your fragment host header may be referencing\nan incorrect bundle symbolic name (BSN) or version. The easiest way to check\nthis is by using the\n[Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell).\nFollow the steps below to find the bundle symbolic name:\n\n1.  Start your target platform instance.\n\n2.  Open the Gogo shell.\n\n3.  List all installed bundles by BSN with the command `lb -s`. You can search\n    through the output to find the BSN. If you already know the BSN and want to\n    check the version, run `lb -s | grep \"<BSN>\"`.\n\nOnce you know the correct BSN/version to reference, update your `Fragment-Host`\nheader to resolve the error.\n\nFor more information on fragments, see the\n[JSP Overrides Using OSGi Fragments](/docs/7-2/customization/-/knowledge_base/c/jsp-overrides-using-osgi-fragments)\ntutorial.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/11-validating-modules-against-the-target-platform/99-validating-modules-outside-of-workspace.markdown",
    "content": "---\nheader-id: validating-modules-outside-of-workspace\n---\n\n# Validating Modules Outside of Workspace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#managing-the-target-platform\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nIf you prefer to not use Liferay Workspace, but still want to validate modules\nagainst a target platform, you must apply the\n[Target Platform Gradle plugin](/docs/7-2/reference/-/knowledge_base/r/target-platform-gradle-plugin)\nto the root `build.gradle` file of your multi-module Gradle build. Follow the\n[Targeting a Platform Outside of Workspace](/docs/7-2/reference/-/knowledge_base/r/targeting-a-platform-outside-of-workspace)\nsection to do this.\n\nOnce you have the Target Platform plugin and its BOM dependencies configured,\nyou must configure the `targetPlatformDistro` dependency. Follow the\ninstructions below to do this.\n\n1.  Open your project's root `build.gradle` file.\n\n2.  Add the `targetPlatformDistro` dependency to the list of dependencies. It\n    should look like this:\n\n    ```groovy\n    dependencies {\n        targetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom\", version: \"7.2.0\"\n        targetPlatformBoms group: \"com.liferay.portal\", name: \"release.portal.bom.compile.only\", version: \"7.2.0\"\n        targetPlatformDistro group: \"com.liferay.portal\", name \"release.portal.distro\", version: \"7.2.0\"\n    }\n    ```\n\n    Liferay DXP users must replace the artifact names and versions:\n    \n    - `release.portal.bom` &rarr; `release.dxp.bom`\n    - `release.portal.bom.compile.only` &rarr; `release.dxp.bom.compile.only`\n    - `release.portal.distro` &rarr; `release.dxp.distro`\n    - `7.2.0` &rarr; `7.2.10`\n\nNow you can validate your non-workspace modules against a target platform!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/12-leveraging-docker/01-intro.markdown",
    "content": "---\nheader-id: leveraging-docker\n---\n\n# Leveraging Docker\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-a-liferay-docker-container.html\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nDocker has become increasingly popular in today's development lifecycle, by\nproviding an automated way to package software and its dependencies into\na standardized unit that can be shared cross-platform. Read Docker's extensive\n[documentation](https://docs.docker.com/) to learn more.\n\nLiferay provides Docker images for\n\n- [Liferay Portal](https://hub.docker.com/r/liferay/portal)\n- [Liferay DXP](https://hub.docker.com/r/liferay/dxp)\n- [Liferay Commerce](https://hub.docker.com/r/liferay/commerce)\n- [Liferay Portal Snapshots](https://hub.docker.com/r/liferay/portal-snapshot)\n\nYou can pull Liferay's Docker images from those resources and manage them\nyourself. Liferay Workspace, however, provides an easy way to integrate Docker\ndevelopment into your existing development workflow with preconfigured Gradle\ntasks.\n\nThe following Docker commands (Gradle-based) are available in Liferay Workspace:\n\nCommand | Description\n------- | -------------\n`buildDockerImage` | Builds the Docker image with all modules/configurations deployed.\n`createDockerContainer` | Creates a Docker container from the @product@ image and mounts the workspace's `/build/docker` folder to the container's `/etc/liferay` folder.\n`createDockerfile` | Creates a `Dockerfile` to build the Docker image.\n`dockerDeploy` | Deploys the project to the container's `deploy` folder by copying the project archive file to workspace's `build/docker/deploy` folder. This command can also be executed from workspace's root folder to deploy all projects and copy all Docker configurations (i.e., from the `configs/common` and `configs/docker` folders) to the container.\n`logsDockerContainer` | Prints the portal runtime's logs. You can exit log tracking mode while maintaining a running container (e.g., [Ctrl&#124;Command] + C).\n`pullDockerImage` | Pulls the Docker image.\n`removeDockerContainer` | Removes the container from Docker's system.\n`startDockerContainer` | Starts the Docker container.\n`stopDockerContainer` | Stops the Docker container.\n\n| **Note:** Leveraging Docker in Liferay Workspace is only available for Gradle\n| projects at this time.\n\nIn this section, you'll learn how to\n\n- [Create a Docker container based on a provided @product@ image](/docs/7-2/reference/-/knowledge_base/r/creating-a-product-docker-container).\n- [Configure the container](/docs/7-2/reference/-/knowledge_base/r/configuring-a-docker-container).\n- [Build a custom image](/docs/7-2/reference/-/knowledge_base/r/building-a-custom-docker-image).\n\nContinue on to learn more.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/12-leveraging-docker/02-creating-a-liferay-docker-container.markdown",
    "content": "---\nheader-id: creating-a-product-docker-container\n---\n\n# Creating a @product@ Docker Container\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-a-liferay-docker-container.html\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nTo create a @product@ Docker container in Liferay Workspace, complete the steps\nbelow.\n\n1.  Choose the Docker image you need. This is configured in your workspace's\n    `gradle.properties` file by customizing this property: \n\n    ```properties\n    liferay.workspace.docker.image.liferay\n    ```\n\n    To find the possible property values you can set, see the official @product@\n    Docker Hub's Tags section (e.g.,\n    [Liferay Portal Docker Tags](https://hub.docker.com/r/liferay/portal/tags)).\n    For example, if you want to base your container on the Liferay Portal 7.2\n    GA1 image, you would set this property:\n\n    ```properties\n    liferay.workspace.docker.image.liferay=liferay/portal:7.2.0-ga1\n    ```\n\n2.  Run the following command from your workspace's root folder:\n\n    ```bash\n    ./gradlew createDockerContainer\n    ```\n\nThis command creates a new container named `[projectName]-liferayapp`. A new\n`build/docker` folder is generated in your workspace. This folder is mounted\ninto the container's file system. This means files in workspace's `build/docker`\nfolder are also available in the container's `/etc/liferay` folder.\n\nAny projects in your workspace are automatically compiled and copied to the\n`build/docker/deploy` folder when the container is created; this means that when\nthe container is started, all your projects are deployed to the container. All\nconfigurations are also applied to the container.\n\n| **Note:** During your container's startup, you may run into the following\n| error:\n| \n| ```bash\n| /etc/liferay/entrypoint.sh: line 3:    11 Killed\n| ${LIFERAY_HOME}/tomcat/bin/catalina.sh run\n| ```\n| \n| This usually means you have not allocated enough memory to your Docker engine\n| to successfully run your container. See Docker's\n| [documentation](https://docs.docker.com) to learn how to increase resources\n| available to Docker.\n\nOnce your container is created, you can\n[configure it](/docs/7-2/reference/-/knowledge_base/r/configuring-a-docker-container).\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/12-leveraging-docker/03-configuring-a-docker-container.markdown",
    "content": "---\nheader-id: configuring-a-docker-container\n---\n\n# Configuring a Docker Container\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-a-liferay-docker-container.html\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nBefore starting your container, you may want to add additional portal\nconfigurations. This could include things like\n\n- Property overrides (e.g., `portal-ext.properties`)\n- Marketplace app overrides\n- App server configurations\n- License files\n\nYou can do this by applying files (and their accompanying folder structures, if\nnecessary) to your workspace's `configs/docker` folder. This folder is treated\nas your Liferay Home for Docker development; you add additional files that\noverlay your workspace's `configs/common` folder and your @product@ container's\ndefault configuration.\n\nAs an example, you'll enable the\n[Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell)\nfor your container.\n\n1.  Add a `portal-ext.properties` file to your workspace's `configs/docker`\n    folder.\n\n2.  Add the following property to the `portal-ext.properties` file:\n\n    ```properties\n    module.framework.properties.osgi.console=0.0.0.0:11311\n    ```\n\n    This lets you access your container using Gogo shell via telnet session.\n\n3.  Start the container.\n\n    Once the container is started, the configurations stored in `configs/common`\n    and `configs/docker` are transferred to the `build/docker/files` folder,\n    which applies all configurations to the container's file system. For more\n    information on workspace's `configs` folder, see\n    [this section](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace#testing-projects).\n\n    | **Note:** You can call the `deployDocker` Gradle task from your\n    | workspace's root folder to initiate the Docker configuration transfer to\n    | the `build/docker/files` folder manually. It's executed automatically when\n    | creating or starting the container.\n\nYou can now apply configurations to your @product@ Docker container.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/12-leveraging-docker/04-building-a-custom-docker-image.markdown",
    "content": "---\nheader-id: building-a-custom-docker-image\n---\n\n# Building a Custom Docker Image\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-a-liferay-docker-container.html\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nYou can preserve your container's configuration by building it as an image.\n\n1.  Build your custom @product@ image by running\n\n    ```bash\n    ./gradlew buildDockerImage\n    ```\n\n    A `Dockerfile` is generated for your container when building your image. The\n    `Dockerfile` is generated in your workspace's `build/docker` folder. For\n    more information on how to configure the `Dockerfile`, see Docker's\n    [Dockerfile reference documentation](https://docs.docker.com/engine/reference/builder/).\n\n    You can generate a `Dockerfile` manually at any time by running\n\n    ```bash\n    ./gradlew createDockerfile\n    ```\n\n2.  Run `docker image ls` to verify the image's availability.\n\nYou can now build @product@ Docker images in Liferay Workspace!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/13-updating-liferay-workspace.markdown",
    "content": "---\nheader-id: updating-liferay-workspace\n---\n\n# Updating Liferay Workspace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#updating-liferay-workspace-and-bundled-plugins\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay Workspace is continuously being updated with new features. If you\ncreated your workspace a while ago, you may be missing out on some of the latest\nfeatures that could improve your @product@ development experience. Updating your\nLiferay Workspace is easy; you'll learn how to do it for Gradle and Maven-based\nworkspaces next.\n\n## Gradle\n\n1.  Find the latest Liferay Workspace version. To do this, view the Workspace\n    Gradle plugin's\n    [released versions](https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.gradle.plugins.workspace/)\n    on Liferay's repository. Copy the version to which you want to upgrade.\n\n2.  Open your Liferay Workspace's `settings.gradle` file. This file resides in\n    your Workspace's root folder.\n\n3.  In the `dependencies` block, you'll find code similar to below:\n\n    ```groovy\n    dependencies {\n        classpath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.workspace\", version: \"[WORKSPACE_VERSION]\"\n    }\n    ```\n\n    Update the `com.liferay.gradle.plugins.workspace` dependency's `version` to\n    the version number you copied in step 1.\n\n4.  Execute any Gradle command to initiate the update process for your Workspace\n    (e.g., `blade gw tasks`).\n\n| **Note:** The Gradle wrapper provided in a Gradle-based Liferay Workspace must\n| be updated if you're migrating from a workspace before version `1.10.14` to\n| the latest available version. To update your Gradle wrapper, run\n| \n| ```bash\n| ./gradlew wrapper --gradle-version=4.10.2\n| ```\n\nAwesome! You've upgraded your Gradle-based Liferay Workspace!\n\n## Maven\n\n1.  Find the latest Liferay Workspace version. To do this, view the Bundle\n    Support plugin's\n    [released versions](https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.portal.tools.bundle.support/)\n    on Liferay's repository. Copy the version to which you want to upgrade.\n\n2.  Open your Liferay Workspace's root `pom.xml` file.\n\n3.  Within the `plugin` tags, you'll find code similar to below:\n\n    ```xml\n    <plugin>\n        <groupId>com.liferay</groupId>\n        <artifactId>com.liferay.portal.tools.bundle.support</artifactId>\n        <version>3.4.2</version>\n        ...\n    </plugin>\n    ```\n\n    Update the `com.liferay.portal.tools.bundle.support` artifact's `version` to\n    the version number you copied in step 1.\n\n4.  Execute any Maven command to initiate the update process for your Workspace\n    (e.g., `mvn verify`).\n\nAwesome! You've upgraded your Maven-based Liferay Workspace!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/liferay-workspace/14-updating-default-plugins-provided-by-liferay-workspace.markdown",
    "content": "---\nheader-id: updating-default-plugins-provided-by-liferay-workspace\n---\n\n# Updating Default Plugins Provided by Liferay Workspace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"\nhttps://learn.liferay.com/dxp/latest/en/building-applications/tooling/liferay-workspace/configuring-liferay-workspace.html#updating-liferay-workspace-and-bundled-plugins\n\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay Workspace comes with a slew of plugins like these: \n\n- [CSS Builder](https://github.com/liferay/liferay-portal/tree/master/modules/util/css-builder)\n- [Javadoc Formatter](https://github.com/liferay/liferay-portal/tree/master/modules/util/javadoc-formatter)\n- [Lang Builder](https://github.com/liferay/liferay-portal/tree/master/modules/util/lang-builder)\n- [Service Builder](https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-service-builder)\n- [Source Formatter](https://github.com/liferay/liferay-portal/tree/master/modules/util/source-formatter)\n- [Theme Builder](https://github.com/liferay/liferay-portal/tree/master/modules/util/portal-tools-theme-builder)\n- etc.\n\nBundled plugins are updated with each release of workspace. Suppose you need\na new feature in the \n[Source Formatter plugin](https://repository.liferay.com/nexus/content/repositories/liferay-public-releases/com/liferay/com.liferay.source.formatter/),\nbut the latest workspace version has not yet been updated to include it. You can\nupgrade it yourself!\n\nTo upgrade one of workspace's bundled plugins, follow these steps:\n\n1.  Find the bundle symbolic name (BSN) for the plugin you want to update. You\n    can find this value in the\n    [`portal-tools.properties`](https://github.com/liferay/liferay-portal/blob/master/modules/sdk/gradle-plugins/src/main/resources/com/liferay/gradle/plugins/dependencies/portal-tools.properties)\n    file. For example, the Source Formatter's BSN is\n    `com.liferay.source.formatter`.\n\n2.  Open your workspace's `build.gradle` file and copy the plugin's BSN followed\n    by `.version` and set the desired plugin version you want to use. For\n    example,\n\n    ```groovy\n    com.liferay.source.formatter.version=1.0.819\n    ```\n\n    If you're most interested in the latest and greatest plugins, you can set\n    the above property to `latest.release` to always use the latest available\n    version. This could, however, cause your workspace to become unstable.\n\nThat's it! You're no longer tied to particular plugin versions provided by your\nworkspace.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/maven/01-intro.markdown",
    "content": "---\nheader-id: maven\n---\n\n# Maven\n\n[TOC levels=1-4]\n\n[Maven](https://maven.apache.org/) is a viable option for managing Liferay\nprojects if you don't want to use Liferay's default Gradle management system.\nLiferay provides several\n[Maven plugins](/docs/7-2/reference/-/knowledge_base/r/maven-plugins) for\ngenerating and managing your project. Liferay also provides easy to obtain Maven\nartifacts that are required for Liferay Maven module development. Here, you'll\nlearn how to\n\n- Install Liferay Maven artifacts\n- Create/Manage a Maven Repository\n- Apply Maven plugins\n\nBecause @product@ is tool-agnostic, Maven is fully supported for @product@\ndevelopment. Read on for details about these topics.\n\n## Installing Liferay Maven Artifacts\n\nTo create Liferay projects using Maven, you'll need its dependencies. This isn't\na problem---Liferay provides them as Maven artifacts. You can retrieve them from\na remote repository.\n\nThere are two remote repositories that contain Liferay artifacts: Central\nRepository and Liferay Repository. The Central Repository is the default\nrepository used to download artifacts if you don't have a remote repository\nconfigured. Using the Central Repository to install Liferay Maven artifacts only\nrequires that you \n[specify your module's dependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies) \nin its `pom.xml` file. \n\nWhen packaging your module, the automatic Maven artifact installation process\nonly downloads the artifacts necessary for that module from the Central\nRepository. \n\nThe Central Repository *usually* offers the latest Liferay Maven artifacts, but\nthe Liferay Repository *guarantees* access to the latest artifacts released by\nLiferay. Other than a slight delay in artifact releases, the two repositories\nare identical. When the Liferay repository is configured in your `settings.xml`\nfile, archetypes are generated based on that repository's contents. The Liferay\nMaven repository offers a good alternative for those who want the most\nup-to-date Maven artifacts produced by Liferay. \n\n| **Note:** If you've configured the Liferay Nexus repository to access Liferay\n| Maven artifacts and you've already been syncing from the Central Repository,\n| you might have to clear out parts of your local repository to force Maven to\n| re-download the newer artifacts. Also, don't leave the Liferay repository\n| enabled when publishing artifacts to Maven Central. You must comment out the\n| Liferay Repository credentials when publishing your artifacts.\n\nNext, you'll learn about managing your Maven artifacts.\n\n## Managing Maven Artifacts in a Repository\n\nYou can share Liferay artifacts and modules with teammates or manage your\nrepositories using a GUI by using\n[Sonatype Nexus](http://www.sonatype.org/nexus/). It's a Maven repository\nmanagement server for creating and managing release servers, snapshot servers,\nand proxy servers. There are several other Maven repository management servers\nyou can use (for example, [Artifactory](https://www.jfrog.com/artifactory/)),\nbut this section focuses on Nexus.\n\nYou'll learn how to\n\n- [Create a repository](/docs/7-2/reference/-/knowledge_base/r/creating-a-maven-repository)\n- [Configure a repository](/docs/7-2/reference/-/knowledge_base/r/configuring-local-maven-settings-to-access-repositories)\n- [Deploy artifacts to a repository](/docs/7-2/reference/-/knowledge_base/r/deploying-liferay-maven-artifacts-to-a-repository)\n\nBefore using repository servers, you must specify them in your Maven environment\nsettings. Your repository settings let Maven find the repository and retrieve\nand install artifacts. You can configure your local Maven settings in the\n`[USER_HOME]/.m2/settings.xml` file.\n\n| **Note**: You must only configure a repository server if you're sharing\n| artifacts (e.g., Liferay artifacts and/or your modules) with others. If you're\n| installing Liferay artifacts from the Central/Liferay Repository\n| and aren't interested in sharing artifacts, you don't need a repository server\n| specified in your Maven settings. You can find out more about installing\n| artifacts from the Central Repository or Liferay's own Nexus repository in the\n| [Installing Remote Liferay Maven Artifacts](/docs/7-2/reference/-/knowledge_base/r/installing-remote-liferay-maven-artifacts)\n| article.\n\nTo deploy to a remote repository, your Liferay project should be packaged using\nMaven. Maven provides a packaging command that creates an artifact (JAR) that\ncan be easily deployed to your remote repository.\n\nOnce you've created a deployable artifact, you can configure your module project\nto communicate with your remote repository and use Maven's `deploy` command to\nsend it on its way. Once your module project resides on the remote repository,\nother developers can configure your remote repository in their projects and set\ndependencies in their project POMs to reference it.\n\n## Applying Maven Plugins\n\nThere are several important Maven plugins that provide important functionality\nto Liferay Maven projects. The available Liferay Maven plugins are available in\nthe [Maven Plugins](/docs/7-2/reference/-/knowledge_base/r/maven-plugins)\nsection.\n\nThe following tasks are covered in this section:\n\n- [Building an OSGi module JAR](/docs/7-2/reference/-/knowledge_base/r/building-an-osgi-module-jar-with-maven)\n- [Building themes](/docs/7-2/reference/-/knowledge_base/r/building-a-theme-with-maven)\n- [Compiling Sass files](/docs/7-2/reference/-/knowledge_base/r/compiling-sass-files-in-a-maven-project)\n- [Using Service Builder](/docs/7-2/reference/-/knowledge_base/r/using-service-builder-in-a-maven-project)\n\nRead on to learn more!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/maven/02-installing-remote-liferay-maven-artifacts.markdown",
    "content": "---\nheader-id: installing-remote-liferay-maven-artifacts\n---\n\n# Installing Liferay Maven Artifacts\n\n[TOC levels=1-4]\n\nIf you haven't configured your project to retrieve artifacts from a custom Maven\nrepository, your project will leverage the artifacts from the Central\nRepository. You can view these artifacts from the\n[Maven Central Repository](https://search.maven.org/) site. Use the Latest\nVersion column as a guide to see what's available for the version of @product@\nyou're developing for. \n\nIf you'd like to access Liferay's latest released Maven artifacts, configure\nMaven to use [Liferay's Nexus repository](https://repository-cdn.liferay.com)\ninstead. To do this, open your project's parent `pom.xml` and add this:\n\n```xml\n<repositories>\n    <repository>\n        <id>liferay-public-releases</id>\n        <name>Liferay Public Releases</name>\n        <url>https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-releases</url>\n    </repository>\n</repositories>\n\n<pluginRepositories>\n    <pluginRepository>\n        <id>liferay-public-releases</id>\n        <url>https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-releases/</url>\n    </pluginRepository>\n</pluginRepositories>\n```\n\nThe above configuration retrieves artifacts from Liferay's release repository.\n\nIf you're most interested in retrieving Liferay's latest snapshot artifacts,\nfollow the instructions below to configure Liferay's Nexus repository to access\nthem.\n\n1.  Open your project's parent `pom.xml` and add this:\n\n    ```xml\n    <repositories>\n        <repository>\n            <id>liferay-public-snapshots</id>\n            <name>Liferay Public Snapshots</name>\n            <url>https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-snapshots</url>\n        </repository>\n    </repositories>\n\n    <pluginRepositories>\n        <pluginRepository>\n            <id>liferay-public-snapshots</id>\n            <url>https://repository-cdn.liferay.com/nexus/content/repositories/liferay-public-snapshots/</url>\n        </pluginRepository>\n    </pluginRepositories>\n    ```\n\n2.  Enable your project to access snapshot artifacts by adding this code to your\n    parent project's `pom.xml`:\n\n    ```xml\n    <snapshots>\n        <enabled>true</enabled>\n    </snapshots>\n    ```\n\nYou're now equipped to access Liferay's Maven artifacts via the\n\n- Central Repository\n- Liferay Repository (releases)\n- Liferay repository (snapshots)\n\nGreat job!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/maven/04-creating-a-maven-repository.markdown",
    "content": "---\nheader-id: creating-a-maven-repository\n---\n\n# Creating a Maven Repository\n\n[TOC levels=1-4]\n\nTo create a Maven repository using Nexus, download\n[Nexus](https://help.sonatype.com/display/NXRM2/Download) and follow the\ninstructions on Nexus'\n[Installation page](https://help.sonatype.com/display/NXRM2/Installing+and+Running)\nto install and start it. \n\nTo create your own repository using Nexus, follow these steps: \n\n1.  Open your web browser; navigate to your Nexus repository server (e.g.,\n    [http://localhost:8081/nexus](http://localhost:8081/nexus)) and log in. The\n    default user name is `admin` with password `admin123`. \n\n2.  Click on *Repositories* and navigate to *Add...* &rarr; *Hosted Repository*. \n\n    ![Figure 1: Adding a repository to hold your Liferay artifacts is easy with Nexus.](../../../images/maven-nexus-create-repo.png)\n\n    To learn more about each type of Nexus repository, read Sonatype's\n    [Managing Repositories](http://books.sonatype.com/nexus-book/reference/confignx-sect-manage-repo.html)\n    guide.\n\n3.  Enter repository properties appropriate for the type of artifacts it will\n    hold. If you're installing release version artifacts into the repository,\n    specify *Release* as the repository policy. Below are example repository\n    property values: \n\n    - **Repository ID:** *liferay-releases*\n    - **Repository Name:** *Liferay Release Repository*\n    - **Provider:** *Maven2*\n    - **Repository Policy:** *Release*\n\n    To create a snapshot repository, choose *Snapshot* for the Repository Policy\n    and update the ID and name accordingly.\n\n4.  Click *Save*.\n\nVoila! You've created a repository for your Liferay releases (i.e.,\n`liferay-releases`) and/or Liferay snapshots (i.e., `liferay-snapshots`). To\nlearn how to deploy your Liferay Maven artifacts to a Nexus repository, see the\n[Deploying Liferay Maven Artifacts to a Repository](/docs/7-2/reference/-/knowledge_base/r/deploying-liferay-maven-artifacts-to-a-repository)\ntutorial.\n\nSee the\n[Configuring Local Maven Settings to Access Repositories](/docs/7-2/reference/-/knowledge_base/r/configuring-local-maven-settings-to-access-repositories)\nto configure your new repository servers in your Maven settings to install\nartifacts to them. \n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/maven/05-configuring-local-maven-settings-to-access-repositories.markdown",
    "content": "---\nheader-id: configuring-local-maven-settings-to-access-repositories\n---\n\n# Configuring Local Maven Settings to Access Repositories\n\n[TOC levels=1-4]\n\nTo configure your Maven environment to access your repository servers, do the\nfollowing:\n\n1.  Navigate to your `[USER_HOME]/.m2/settings.xml` file. Create it if it\n    doesn't yet exist. \n\n2.  Configure your repository server settings. Here are contents from a\n    `settings.xml` file that has `liferay-releases` and `liferay-snapshots`\n    repository servers configured: \n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <settings>\n        <servers>\n            <server>\n                <id>liferay-releases</id>\n                <username>admin</username>\n                <password>admin123</password>\n            </server>\n            <server>\n                <id>liferay-snapshots</id>\n                <username>admin</username>\n                <password>admin123</password>\n            </server>\n        </servers>\n    </settings>\n    ```\n\n    The user name `admin` and password `admin123` are the credentials of the\n    default Nexus administrator account. If you changed these credentials for\n    your Nexus server, make sure to update `settings.xml` with these changes. \n\nNow that your repositories are configured, they're ready to receive all the\nLiferay Maven artifacts you'll download and the Liferay module artifacts you'll\ncreate!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/maven/06-deploying-liferay-maven-artifacts-to-a-repository.markdown",
    "content": "---\nheader-id: deploying-liferay-maven-artifacts-to-a-repository\n---\n\n# Deploying Liferay Maven Artifacts to a Repository\n\n[TOC levels=1-4]\n\nDeploying artifacts to a remote repository is important if you intend to share\nyour Maven projects with others. First, you must have a remote repository that\ncan hold deployed Maven artifacts. If you do not currently have a remote\nrepository,\n[create one](/docs/7-2/reference/-/knowledge_base/r/creating-a-maven-repository).\nAlso make sure your `[USER_HOME]/.m2/settings.xml` file specifies your remote\nrepository's ID, user name, and password (configuration instructions\n[here](/docs/7-2/reference/-/knowledge_base/r/configuring-local-maven-settings-to-access-repositories)).\n\nTo follow this article, you'll need a Liferay module built with Maven. For\ndemonstration purposes, this tutorial uses the `portlet.ds` sample module\nproject. To follow along with this module, download the\n[portlet.ds](https://portal.liferay.dev/documents/113763090/114000186/portlet.ds.zip)\nZip. \n\nNow it's time to deploy a Maven artifact to your Nexus repository.\n\n1.  Create a folder anywhere on your machine to serve as the parent folder\n    for your Liferay modules. Unzip the `portlet.ds` module project into that\n    folder.\n\n2.  Create a `pom.xml` file inside this folder. Copy the following logic into\n    the parent POM:\n\n    ```xml\n    <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n    <project\n        xmlns=\"http://maven.apache.org/POM/4.0.0\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n    >\n\n        <modelVersion>4.0.0</modelVersion>\n        <groupId>liferay.sample</groupId>\n        <artifactId>liferay.sample.maven</artifactId>\n        <version>1.0.0</version>\n        <name>Liferay Maven Module Projects</name>\n        <packaging>pom</packaging>\n\n        <distributionManagement>\n            <repository>\n                <id>liferay-releases</id>\n                <url>http://localhost:8081/nexus/content/repositories/liferay-releases</url>\n            </repository>\n        </distributionManagement>\n\n        <modules>\n            <module>portlet.ds</module>\n        </modules>\n    </project>\n    ```\n\n    The tags `<modelVersion>` through `<packaging>` are POM tags that are\n    used frequently in parent POMs. Visit Maven's\n    [POM Reference](https://maven.apache.org/pom.html) documentation for more\n    information.\n\n    The `<distributionManagement>` tag specifies the deployment repository for\n    all module projects residing in the parent folder. This repository should\n    also be specified in your `[USER_HOME]/.m2/settings.xml`. Both the parent\n    POM and `settings.xml` file's repository declarations are required to deploy\n    your modules to that remote repository.\n\n    Finally, you must list the modules residing in the parent folder that you\n    want deployed using the `<modules>` tag. The `portlet.ds` module is\n    specified within that tag.\n\n3.  Open the `portlet.ds` module's `pom.xml` file. If you did not download the\n    `portlet.ds` module project Zip, you can reference its POM below. \n\n    ```xml\n    <project\n        xmlns=\"http://maven.apache.org/POM/4.0.0\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n    >\n\n        <modelVersion>4.0.0</modelVersion>\n        <artifactId>portlet.ds</artifactId>\n        <version>1.0.0</version>\n        <packaging>jar</packaging>\n\n        <parent>\n            <groupId>liferay.sample</groupId>\n            <artifactId>liferay.sample.maven</artifactId>\n            <version>1.0.0</version>\n            <relativePath>../pom.xml</relativePath>\n        </parent>\n\n        <dependencies>\n            <dependency>\n                <groupId>javax.portlet</groupId>\n                <artifactId>portlet-api</artifactId>\n                <version>2.0</version>\n                <scope>provided</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.osgi</groupId>\n                <artifactId>org.osgi.service.component.annotations</artifactId>\n                <version>1.3.0</version>\n                <scope>provided</scope>\n            </dependency>\n        </dependencies>\n    </project>\n    ```\n\n    The `portlet.ds` module's POM specifies its own attributes first, followed\n    by the parent POM's attributes. Declaring the `<parent>` tag like above\n    links the `portlet.ds` module to its parent POM, which is necessary to\n    deploy to the remote repository. Then the module's dependencies are listed.\n    These dependencies are downloaded from the Central Repository and\n    installed to your local `.m2` repository when you package the `portlet.ds`\n    module.\n\n4.  Now that you've configured your parent POM and module POM, package your\n    Maven project. Navigate to your module project (e.g., `project.ds`) using\n    the command line and run the Maven package command:\n\n    ```bash\n    mvn package\n    ```\n\n    This downloads and installs all your module's dependencies and packages the\n    project into a JAR file. Navigate to your module project's generated build\n    folder (e.g., `/target`). You'll notice there is a newly generated JAR file.\n    This is the artifact you'll deploy to your Nexus repository.\n\n5.  Run Maven's deploy command to deploy your module project's artifact to your\n    configured remote repository.\n\n    ```bash\n    mvn deploy\n    ```\n\n    Your console shows output from the artifact being deployed into your\n    repository server.\n\nTo verify that your artifact is deployed, navigate to the *Repositories* page of\nyour Nexus server and select your repository. A window appears below showing\nthe Liferay artifact now deployed to your repository.\n\n![Figure 1: Your repository server now provides access to your Liferay Maven artifacts.](../../../images/maven-verify-deployment.png)\n\nAwesome! You can now share your Liferay module projects with anyone by deploying\nthem as artifacts to your remote repository!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/maven/07-building-an-osgi-module-jar-with-maven.markdown",
    "content": "---\nheader-id: building-an-osgi-module-jar-with-maven\n---\n\n# Building an OSGi Module JAR with Maven\n\n[TOC levels=1-4]\n\nIf you have an existing Liferay module built with Maven that you created from\nscratch, or you're upgrading your Maven project from a previous version of\n@product@, your project probably can't generate an executable OSGi JAR. Don't\nfret! You can do this by making a few minor configurations in your module's\nPOMs.\n\n| **Note:** If you used Liferay's Maven archetypes to generate your module\n| project, the project already has the Maven plugins required to generate an\n| OSGi JAR.\n\nContinue on to see how this is done.\n\n1.  In your project's `pom.xml` file, add the\n    [BND Maven Plugin](http://njbartlett.name/2015/03/27/announcing-bnd-maven-plugin.html)\n    declaration:\n\n    ```xml\n    <plugin>\n        <groupId>biz.aQute.bnd</groupId>\n        <artifactId>bnd-maven-plugin</artifactId>\n        <version>3.3.0</version>\n        <executions>\n            <execution>\n                <goals>\n                    <goal>bnd-process</goal>\n                </goals>\n            </execution>\n        </executions>\n        <dependencies>\n            <dependency>\n                <groupId>biz.aQute.bnd</groupId>\n                <artifactId>biz.aQute.bndlib</artifactId>\n                <version>3.2.0</version>\n            </dependency>\n            <dependency>\n                <groupId>com.liferay</groupId>\n                <artifactId>com.liferay.ant.bnd</artifactId>\n                <version>2.0.41</version>\n            </dependency>\n        </dependencies>\n    </plugin>\n    ```\n\n    The BND Maven plugin prepares all your Maven module's resources (e.g.,\n    `MANIFEST.MF`) and inserts them into the generated\n    `[Maven Project]/target/classes` folder. This plugin prepares your module to\n    be packaged as an OSGi JAR deployable to @product@.\n\n    | **Note:** Although WABs can be generated using the `bnd-maven-plugin`,\n    | this is not supported by Liferay. WABs should be created as a standard WAR\n    | project and deployed to the\n    | [Liferay WAB Generator](/docs/7-2/customization/-/knowledge_base/c/deploying-wars-wab-generator),\n    | which generates a WAB for you.\n\n2.  In your project's `pom.xml` file, add the\n    [Maven JAR Plugin](http://maven.apache.org/plugins/maven-jar-plugin/)\n    declaration:\n\n    ```xml\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-jar-plugin</artifactId>\n                <version>2.6</version>\n                <configuration>\n                    <archive>\n                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>\n                    </archive>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n    ```\n\n    The Maven JAR plugin builds your Maven project as a JAR file, including the\n    resources generated by the BND Maven plugin. The above configuration also\n    sets the default project `MANIFEST.MF` file path for your project, which is\n    essential when packaging your module using the BND Maven plugin. By default,\n    the Maven JAR Plugin ignores the `target/classes/META-INF/MANIFEST.MF`\n    generated by the BND Maven plugin, so you must explicitly set it as the\n    manifest file so it's included properly in the generated JAR file.\n\n3.  Add a [`bnd.bnd` file](http://bnd.bndtools.org/) to your Liferay Maven\n    project, residing in the same folder as your project's `pom.xml` file.\n\n4.  Build your Maven OSGi JAR by running\n\n    ```bash\n    mvn package\n    ```\n\n    Your Maven JAR is generated in your project's `/target` folder. You can\n    deploy it manually into @product@'s `/deploy` folder, or you can configure\n    your project to deploy automatically to @product@ by following the\n    [Deploying a Project](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project#maven)\n    article.\n\nFantastic! You've configured your Liferay Maven project to package itself into a\ndeployable OSGi module.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/maven/08-building-themes-in-a-maven-project.markdown",
    "content": "---\nheader-id: building-a-theme-with-maven\n---\n\n# Building a Theme with Maven\n\n[TOC levels=1-4]\n\nLiferay's Theme Builder is used to build @product@ theme files in your project.\nYou can incorporate the Theme Builder into your Maven project to generate\nWAR-style\n[themes](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction)\ndeployable to @product@.\n\nThe easiest way to create a Liferay theme with Maven is to create a new Maven\nproject using Liferay's provided\n[Theme archetype](/docs/7-2/reference/-/knowledge_base/r/theme-template);\nTheme Builder is configured in the new project by default. In some cases,\nhowever, this may not be convenient. For instance, if you have a legacy theme\nproject and don't want to start over, generating a new project is not ideal. \n\nFor cases like this, you should manually configure your Maven project to\nleverage Theme Builder. You'll learn how to do this next.\n\n1.  Configure Liferay's\n    [Theme Builder](/docs/7-2/reference/-/knowledge_base/r/theme-builder-plugin)\n    plugin in your project's `pom.xml` file:\n\n    ```xml\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.liferay</groupId>\n                <artifactId>com.liferay.portal.tools.theme.builder</artifactId>\n                <version>1.1.7</version>\n                <executions>\n                    <execution>\n                        <phase>generate-resources</phase>\n                        <goals>\n                            <goal>build</goal>\n                        </goals>\n                        <configuration>\n                            <diffsDir>${maven.war.src}</diffsDir>\n                            <name>${project.artifactId}</name>\n                            <outputDir>${project.build.directory}/${project.build.finalName}</outputDir>\n                            <parentDir>${project.build.directory}/deps/com.liferay.frontend.theme.styled.jar</parentDir>\n                            <parentName>_styled</parentName>\n                            <templateExtension>ftl</templateExtension>\n                            <unstyledDir>${project.build.directory}/deps/com.liferay.frontend.theme.unstyled.jar</unstyledDir>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n    ```\n\n    The above configuration applies the Theme Builder plugin and then defines\n    the Theme Builder's execution and configuration.\n\n    - The\n      [executions](https://maven.apache.org/guides/mini/guide-configuring-plugins.html#Using_the_executions_Tag)\n      tag configures the Theme Builder to run during the `generate-resources`\n      phase of your Maven project's build lifecycle. The `build`\n      [goal](http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#A_Build_Phase_is_Made_Up_of_Plugin_Goals)\n      is defined for that lifecycle phase.\n    - The\n      [configuration](https://maven.apache.org/pom.html#Plugins) defines tag\n      several important properties. For more info on these properties, see the\n      [Theme Builder Plugin](/docs/7-2/reference/-/knowledge_base/r/theme-builder-plugin)\n      article.\n\n2.  Apply the CSS Builder plugin, which is required to use Theme Builder:\n\n    ```xml\n    <plugin>\n        <groupId>com.liferay</groupId>\n        <artifactId>com.liferay.css.builder</artifactId>\n        <version>2.1.3</version>\n        <executions>\n            <execution>\n                <id>default-build</id>\n                <phase>compile</phase>\n                <goals>\n                    <goal>build</goal>\n                </goals>\n            </execution>\n        </executions>\n        <configuration>\n            <docrootDirName>target/${project.build.finalName}</docrootDirName>\n            <outputDirName>/</outputDirName>\n            <portalCommonPath>target/deps/com.liferay.frontend.css.common.jar</portalCommonPath>\n        </configuration>\n    </plugin>\n    ```\n\n    You can learn more about the CSS Builder's Maven configuration by visiting\n    the\n    [Compiling Sass Files in a Maven Project](/docs/7-2/reference/-/knowledge_base/r/compiling-sass-files-in-a-maven-project)\n    tutorial.\n\n3.  You can configure your project to exclude Sass files from being packaged in\n    your theme. This is optional, but is a nice convenience to keep any\n    unnecessary source code out of your WAR. Since the Theme Builder creates\n    a WAR-style theme, you should apply the\n    [maven-war-plugin](https://maven.apache.org/plugins/maven-war-plugin/) so it\n    instructs the WAR file packaging process to exclude Sass files:\n\n    ```xml\n    <plugin>\n        <artifactId>maven-war-plugin</artifactId>\n        <version>3.0.0</version>\n        <configuration>\n            <packagingExcludes>**/*.scss</packagingExcludes>\n        </configuration>\n    </plugin>\n    ```\n\n4.  Insert the `<packaging>` tag in your project's POM so your project is\n    correctly packaged as a WAR file. This tag can be placed with your project's\n    `groupId`, `artifactId`, and `version` specifications like this:\n\n    ```xml\n    <groupId>com.liferay</groupId>\n    <artifactId>com.liferay.project.templates.theme</artifactId>\n    <version>1.0.0</version>\n    <packaging>war</packaging>\n    ```\n\n5.  Building themes requires certain dependencies. You can \n    [configure these dependenices](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies) \n    in your project's `pom.xml` as directories or JAR files. If you choose to \n    use JARs, you must apply the \n    [maven-dependency-plugin](http://maven.apache.org/plugins/maven-dependency-plugin/) \n    and have it copy JAR dependencies into your project from Maven Central: \n\n    ```xml\n    <plugin>\n        <artifactId>maven-dependency-plugin</artifactId>\n        <executions>\n            <execution>\n                <phase>generate-sources</phase>\n                <goals>\n                    <goal>copy</goal>\n                </goals>\n                <configuration>\n                    <artifactItems>\n                        <artifactItem>\n                            <groupId>com.liferay</groupId>\n                            <artifactId>com.liferay.frontend.css.common</artifactId>\n                            <version>${com.liferay.frontend.css.common.version}</version>\n                        </artifactItem>\n                        <artifactItem>\n                            <groupId>com.liferay</groupId>\n                            <artifactId>com.liferay.frontend.theme.styled</artifactId>\n                            <version>${com.liferay.frontend.theme.styled.version}</version>\n                        </artifactItem>\n                        <artifactItem>\n                            <groupId>com.liferay</groupId>\n                            <artifactId>com.liferay.frontend.theme.unstyled</artifactId>\n                            <version>${com.liferay.frontend.theme.unstyled.version}</version>\n                        </artifactItem>\n                    </artifactItems>\n                    <outputDirectory>${project.build.directory}/deps</outputDirectory>\n                    <stripVersion>true</stripVersion>\n                </configuration>\n            </execution>\n        </executions>\n    </plugin>\n    ```\n\n    This configuration copies the `com.liferay.frontend.css.common`,\n    `com.liferay.frontend.theme.styled`, and\n    `com.liferay.frontend.theme.unstyled` dependencies into your Maven project.\n    Notice that you've set the `stripVersion` tag to `true` and you're setting\n    the artifact versions within each `artifactItem` tag. You'll set these\n    versions and a few other properties for your Maven project next.\n\n6.  Configure the properties for your project in its `pom.xml` file:\n\n    ```xml\n    <properties>\n        <com.liferay.css.builder.version>2.1.3</com.liferay.css.builder.version>\n        <com.liferay.frontend.css.common.version>2.0.4</com.liferay.frontend.css.common.version>\n        <com.liferay.frontend.theme.styled.version>2.0.28</com.liferay.frontend.theme.styled.version>\n        <com.liferay.frontend.theme.unstyled.version>2.2.5</com.liferay.frontend.theme.unstyled.version>\n        <com.liferay.portal.tools.theme.builder.version>1.1.7</com.liferay.portal.tools.theme.builder.version>\n    </properties>\n    ```\n\n    The properties above set the versions for the CSS and Theme Builder plugins\n    and their dependencies.\n\nYou've successfully configured your Maven project to build a Liferay theme with\nTheme Builder!\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/maven/09-compiling-sass-files-in-a-maven-project.markdown",
    "content": "---\nheader-id: compiling-sass-files-in-a-maven-project\n---\n\n# Compiling Sass Files in a Maven Project\n\n[TOC levels=1-4]\n\nIf your Liferay Maven project uses Sass files to style its UI, you must\nconfigure the project to convert its Sass files into CSS files so they are\nrecognizable for Maven's build lifecycle. It would be a real pain to convert\nyour Sass files into CSS files manually before building your Maven project!\n\nLiferay provides the\n[CSS Builder](/docs/7-2/reference/-/knowledge_base/r/css-builder-plugin)\nplugin, which converts Sass files into CSS files so the Maven build can parse\nyour style sheets.\n\nHere's how to apply Liferay's CSS builder to your Maven project.\n\n1.  Open your project's `pom.xml` file and apply Liferay's CSS Builder:\n\n    ```xml\n    <plugin>\n        <groupId>com.liferay</groupId>\n        <artifactId>com.liferay.css.builder</artifactId>\n        <version>2.1.0</version>\n        <executions>\n            <execution>\n                <id>default-build</id>\n                <phase>compile</phase>\n                <goals>\n                    <goal>build</goal>\n                </goals>\n            </execution>\n        </executions>\n        <configuration>\n            <docrootDirName>${com.liferay.portal.tools.theme.builder.outputDir}</docrootDirName>\n            <outputDirName>/</outputDirName>\n            <portalCommonPath>target/deps/com.liferay.frontend.css.common.jar</portalCommonPath>\n        </configuration>\n    </plugin>\n    ```\n\n    The above configuration applies the CSS Builder and then defines the CSS\n    Builder's execution and configuration.\n\n    - The\n      [executions](https://maven.apache.org/guides/mini/guide-configuring-plugins.html#Using_the_executions_Tag)\n      tag configures the CSS Builder to run during the `compile` phase of your\n      Maven project's build lifecycle. The `build`\n      [goal](http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#A_Build_Phase_is_Made_Up_of_Plugin_Goals)\n      is defined for that lifecycle phase.\n    - The\n      [configuration](https://maven.apache.org/pom.html#Plugins) tag defines\n      two important properties. For more info on these properties, see the\n      [CSS Builder Plugin](/docs/7-2/reference/-/knowledge_base/r/css-builder-plugin)\n      article.\n\n2.  If you're using [Bourbon](http://bourbon.io/) in your Sass files, you'll\n    need to \n    [add an additional plugin dependency](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies) \n    to your project's POM. If you're not using Bourbon, skip this step. Add the \n    following plugin dependency: \n\n    ```xml\n    <plugin>\n        <artifactId>maven-dependency-plugin</artifactId>\n        <executions>\n            <execution>\n                <phase>generate-sources</phase>\n                <goals>\n                    <goal>copy</goal>\n                </goals>\n                <configuration>\n                    <artifactItems>\n                        <artifactItem>\n                            <groupId>com.liferay</groupId>\n                            <artifactId>com.liferay.frontend.css.common</artifactId>\n                            <version>2.0.4</version>\n                        </artifactItem>\n                    </artifactItems>\n                    <outputDirectory>${project.build.directory}/deps</outputDirectory>\n                    <stripVersion>true</stripVersion>\n                </configuration>\n            </execution>\n        </executions>\n    </plugin>\n    ```\n\n    The [maven-dependency-plugin](http://maven.apache.org/plugins/maven-dependency-plugin/)\n    copies the `com.liferay.frontend.css.common` dependency from Maven Central\n    to your project's build folder so the CSS Builder can leverage it.\n\n3.  Use this command to compile your Maven project's Sass files:\n\n    ```bash\n    mvn compile\n    ```\n\n| **Note:** Liferay's CSS Builder is supported for Oracle's JDK and uses a\n| native compiler for increased speed. If you're using an IBM JDK, you may\n| experience issues when building your Sass files (e.g., when building a theme).\n| It's recommended to switch to using the Oracle JDK, but if you prefer using\n| the IBM JDK, you must use the fallback Ruby compiler. To do this, add the\n| following tag to your CSS Builder configuration in your POM:\n| \n| ```xml\n| <sassCompilerClassName>ruby</sasscompilerClassName>\n| ```\n| \n| Be aware that the Ruby-based compiler doesn't perform as well as the native\n| compiler, so expect longer compile times.\n\nAwesome! You can now compile Sass files in your Liferay Maven project.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/maven/10-using-service-builder-in-a-maven-project.markdown",
    "content": "---\nheader-id: using-service-builder-in-a-maven-project\n---\n\n# Using Service Builder in a Maven Project\n\n[TOC levels=1-4]\n\nThe easiest way to add\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder)\nto your Maven project is to create a new Maven project using Liferay's provided\nService Builder archetype; it's configured in the new project by default. You\ncan learn how to generate a Service Builder Maven project by visiting the\n[Service Builder Template](/docs/7-2/reference/-/knowledge_base/r/using-the-service-builder-template)\narticle.\n\nIn some cases, you should not use this template due to a number of reasons:\n\n- You're updating a legacy Maven project to follow OSGi modular architecture.\n- You have a pre-existing modular Maven project and don't want to start over.\n\nFor these cases, you can configure Service Builder in your project manually.\nFollow the instructions below to configure Service Builder in your Maven\nproject!\n\n1.  Apply the Service Builder plugin in your Maven project's `pom.xml` file:\n\n    ```xml\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.liferay</groupId>\n                <artifactId>com.liferay.portal.tools.service.builder</artifactId>\n                <version>1.0.276</version>\n                <configuration>\n                    <apiDirName>../basic-api/src/main/java</apiDirName>\n                    <autoImportDefaultReferences>true</autoImportDefaultReferences>\n                    <autoNamespaceTables>true</autoNamespaceTables>\n                    <buildNumberIncrement>true</buildNumberIncrement>\n                    <hbmFileName>src/main/resources/META-INF/module-hbm.xml</hbmFileName>\n                    <implDirName>src/main/java</implDirName>\n                    <inputFileName>service.xml</inputFileName>\n                    <modelHintsFileName>src/main/resources/META-INF/portlet-model-hints.xml</modelHintsFileName>\n                    <mergeModelHintsConfigs>src/main/resources/META-INF/portlet-model-hints.xml</mergeModelHintsConfigs>\n                    <osgiModule>true</osgiModule>\n                    <propsUtil>com.liferay.blade.samples.servicebuilder.service.util.PropsUtil</propsUtil>\n                    <resourcesDirName>src/main/resources</resourcesDirName>\n                    <springFileName>src/main/resources/META-INF/spring/module-spring.xml</springFileName>\n                    <springNamespaces>beans,osgi</springNamespaces>\n                    <sqlDirName>src/main/resources/META-INF/sql</sqlDirName>\n                    <sqlFileName>tables.sql</sqlFileName>\n                    <testDirName>src/main/test</testDirName>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n    ```\n\n    The `configuration` tag used above is an example of what a Service Builder\n    project's configuration could look like. All the configuration attributes\n    above should be modified to fit your project.\n\n    The above code configures Service Builder for a sample `basic-service`\n    module. When running Service Builder with this configuration, the project's\n    API classes are generated in the `basic-api` module's `src/main/java`\n    folder. You can also specify your hibernate module mappings, implementation\n    directory name, model hints file, Spring configurations, SQL configurations,\n    etc. You can reference all the configurable Service Builder properties in\n    the\n    [Service Builder Plugin](/docs/7-2/reference/-/knowledge_base/r/service-builder-plugin)\n    reference article. \n    \n    <!--Uncomment once article is available\n    Also, visit the\n    Defining an Object-Relational Map with Service Builder\n    tutorial for more information on defining a `service.xml` file to configure\n    Service Builder.\n    -->\n\n2.  Apply the WSDD Builder plugin declaration directly after the Service Builder\n    plugin declaration:\n\n    ```xml\n    <plugin>\n        <groupId>com.liferay</groupId>\n        <artifactId>com.liferay.portal.tools.wsdd.builder</artifactId>\n        <version>1.0.10</version>\n        <configuration>\n            <inputFileName>service.xml</inputFileName>\n            <outputDirName>src/main/java</outputDirName>\n            <serverConfigFileName>src/main/resources/server-config.wsdd</serverConfigFileName>\n        </configuration>\n    </plugin>\n    ```\n\n    The WSDD Builder is required to generate your project's remote services.\n    \n    <!--Uncomment once article is available\n    Visit the\n    Creating Remote Services\n    tutorial for more information on WSDD (Web Service Deployment Descriptor).\n    -->\n    \n    See the\n    [WSDD Builder Plugin](/docs/7-2/reference/-/knowledge_base/r/wsdd-builder-plugin)\n    article for more information on the configuration properties.\n\nTerrific! You've successfully configured your Maven project to use Service\nBuilder by applying the Service Builder and WSDD Builder plugins in your\nproject's POM. \n\n<!--Uncomment once article is available\nTo run Service Builder, see the\nRunning Service Builder and Understanding the Generated Code\narticle for instructions.\n-->\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/maven/99-upgrading-your-maven-build-environment.markdown",
    "content": "---\nheader-id: upgrading-your-maven-build-environment\n---\n\n# Upgrading Your Maven Build Environment\n\n[TOC levels=1-4]\n\n| **Note:** This upgrade article only applies to projects residing in a pre\n| Liferay Portal 7.0 Maven environment that are not upgrading to Liferay\n| Workspace. If you're interested in upgrading to a Maven-based Liferay\n| Workspace (recommended), see the\n| [Upgrading Code to @product-ver@](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver)\n| tutorials for more information.\n\nIf you're an avid Maven user and have been using it for Liferay Portal 6.2\nproject development or older, you must upgrade your Maven build to be compatible\nwith @product-ver@ development. There are two main parts of the Maven\nenvironment upgrade process that you must address:\n\n- [Upgrading to new @product-ver@ Maven plugins](#upgrading-to-new-product-ver-maven-plugins)\n- [Updating Liferay Maven artifact dependencies](#updating-liferay-maven-artifact-dependencies)\n\nFor more information on using Maven with @product-ver@, see the\n[Maven](/docs/7-2/reference/-/knowledge_base/r/maven) section.\n\nLiferay also offers a Maven development environment tailored specifically for\n@product-ver@ development. Learn more about this in the\n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)\nsection.\n\nYou'll start off by upgrading your Maven environment's Liferay Maven plugins.\n\n## Upgrading to New @product-ver@ Maven Plugins\n\nThe biggest change for your project's build plugins is the removal of the\n`liferay-maven-plugin`. Liferay now provides several individual Maven plugins\nthat accomplish specific tasks. For example, you can configure Maven plugins for\nLiferay's CSS Builder, Service Builder, Theme Builder, etc. With smaller plugins\navailable to accomplish specific tasks in your project, you no longer have to\nrely on one large plugin that provides many things you may not want.\n\nFor example, suppose your Liferay Portal 6.2 project was using the\n`liferay-maven-plugin` for Liferay CSS Builder only. First, you should remove\nthe legacy `liferay-maven-plugin` plugin dependency from your project's\n`pom.xml` file:\n\n```xml\n<plugin>\n    <groupId>com.liferay.maven.plugins</groupId>\n    <artifactId>liferay-maven-plugin</artifactId>\n    <version>${liferay.version}</version>\n    <configuration>\n        <autoDeployDir>${liferay.auto.deploy.dir}</autoDeployDir>\n        <liferayVersion>${liferay.version}</liferayVersion>\n        <pluginType>portlet</pluginType>\n    </configuration>\n</plugin>\n```\n\nThen, add the CSS Builder plugin dependency to your project's `pom.xml` file:\n\n```xml\n<plugin>\n    <groupId>com.liferay</groupId>\n    <artifactId>com.liferay.css.builder</artifactId>\n    <version>2.1.3</version>\n    <executions>\n        <execution>\n            <id>default-build</id>\n            <phase>generate-sources</phase>\n            <goals>\n                <goal>build</goal>\n            </goals>\n        </execution>\n    </executions>\n        <configuration>\n            <docrootDirName>src/main/webapp</docrootDirName>\n        </configuration>\n</plugin>\n```\n\nSome common Liferay Maven plugins are listed below, with their corresponding\nartifact IDs and articles explaining how to configure them:\n\n**Common Liferay Maven Plugins**\n\nName | Artifact ID | Tutorial |\n:----| :---------- | :------- |\nBundle Support | [com.liferay.portal.tools.bundle.support](https://search.maven.org/search?q=com.liferay.portal.tools.bundle.support) | [Bundle Support Plugin](/docs/7-2/reference/-/knowledge_base/r/bundle-support-plugin) |\nCSS Builder | [com.liferay.css.builder](https://search.maven.org/search?q=com.liferay.css.builder) | [CSS Builder Plugin](/docs/7-2/reference/-/knowledge_base/r/css-builder-plugin) |\nDB Support | [com.liferay.portal.tools.db.support](https://search.maven.org/search?q=com.liferay.portal.tools.db.support) | [DB Support Plugin](/docs/7-2/reference/-/knowledge_base/r/db-support-plugin)\nDeployment Helper | [com.liferay.deployment.helper](https://search.maven.org/search?q=com.liferay.deployment.helper) | [Deployment Helper Plugin](/docs/7-2/reference/-/knowledge_base/r/deployment-helper-plugin) |\nJavadoc Formatter | [com.liferay.javadoc.formatter](https://search.maven.org/search?q=com.liferay.javadoc.formatter) | [Javadoc Formatter Plugin](/docs/7-2/reference/-/knowledge_base/r/javadoc-formatter-plugin) |\nLang Builder | [com.liferay.lang.builder](https://search.maven.org/search?q=com.liferay.lang.builder) | [Lang Builder Plugin](/docs/7-2/reference/-/knowledge_base/r/lang-builder-plugin) |\nREST Builder | [com.liferay.portal.tools.rest.builder](https://search.maven.org/search?q=com.liferay.portal.tools.rest.builder) | [REST Builder Plugin](/docs/7-2/reference/-/knowledge_base/r/rest-builder-plugin) |\nService Builder | [com.liferay.portal.tools.service.builder](https://search.maven.org/search?q=com.liferay.portal.tools.service.builder) | [Service Builder Plugin](/docs/7-2/reference/-/knowledge_base/r/service-builder-plugin) |\nSource Formatter | [com.liferay.source.formatter](https://search.maven.org/search?q=com.liferay.source.formatter) | [Source Formatter Plugin](/docs/7-2/reference/-/knowledge_base/r/source-formatter-plugin) |\nTheme Builder | [com.liferay.portal.tools.theme.builder](https://search.maven.org/search?q=com.liferay.portal.tools.theme.builder) | [Theme Builder Plugin](/docs/7-2/reference/-/knowledge_base/r/theme-builder-plugin) |\nTLD Formatter | [com.liferay.tld.formatter](https://search.maven.org/search?q=com.liferay.tld.formatter) |[TLD Formatter Plugin](/docs/7-2/reference/-/knowledge_base/r/tld-formatter-plugin) |\nWSDD Builder | [com.liferay.portal.tools.wsdd.builder](https://search.maven.org/search?q=com.liferay.portal.tools.wsdd.builder) | [WSDD Builder Plugin](/docs/7-2/reference/-/knowledge_base/r/wsdd-builder-plugin) |\nXML Formatter | [com.liferay.xml.formatter](https://search.maven.org/search?q=com.liferay.xml.formatter) | [XML Formatter Plugin](/docs/7-2/reference/-/knowledge_base/r/xml-formatter-plugin) |\n\n| **Note:** When upgrading to a Liferay Workspace built with Maven, many of\n| these plugins are applied to the workspace by default.\n\nIn Liferay Portal 6.2, you were also required to specify all your app server\nconfiguration settings. For example, your parent POM probably contained settings\nsimilar to these:\n\n```xml\n<properties>\n    <liferay.app.server.deploy.dir>\n        E:\\liferay-portal-6.2.0-ce-ga1\\tomcat-7.0.42\\webapps\n    </liferay.app.server.deploy.dir>\n\n    <liferay.app.server.lib.global.dir>\n        E:\\liferay-portal-6.2.0-ce-ga1\\tomcat-7.0.42\\lib\\ext\n    </liferay.app.server.lib.global.dir>\n\n    <liferay.app.server.portal.dir>\n        E:\\liferay-portal-6.2.0-ce-ga1\\tomcat-7.0.42\\webapps\\root\n    </liferay.app.server.portal.dir> \n\n    <liferay.auto.deploy.dir>\n        E:\\liferay-portal-6.2.0-ce-ga1\\deploy\n    </liferay.auto.deploy.dir>\n\n    <liferay.version>\n        6.2.0\n    </liferay.version>\n\n    <liferay.maven.plugin.version>\n        6.2.0\n    </liferay.maven.plugin.version\n \n</properties>\n```\n\nThis is no longer required in @product-ver@ because Liferay's Maven tools no\nlonger rely on your @product@ installation's specific versions. You should\nremove them from your POM file.\n\nAwesome! You've learned about the new Maven plugins available to you for\n@product-ver@ development. Next, you'll learn about updating your Liferay Maven\nartifacts.\n\n## Updating Liferay Maven Artifact Dependencies\n\nMany Liferay Portal 6.2 artifact dependencies you were using have changed in\n@product-ver@. See the table below for popular Liferay Maven artifacts that have\nchanged:\n\nLiferay Portal 6.2 Artifact ID | @product-ver@ Artifact ID |\n:----------------------------- | :------------------------ |\n`portal-service` | `com.liferay.portal.kernel` |\n`util-bridges` | `com.liferay.util.bridges` |\n`util-java` | `com.liferay.util.java` |\n`util-slf4j` | `com.liferay.util.slf4j` |\n`util-taglib` | `com.liferay.util.taglib` |\n\nFor more information on resolving dependencies in @product-ver@, see the\n[Resolving a Plugin's Dependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies)\narticle.\n\nOf course, you must also update the artifacts you're referencing for your\nprojects. If you're using the Central Repository to install Liferay Maven\nartifacts, you won't need to do anything more than update the artifacts in\nyour POMs. If, however, you're working behind a proxy or don't have Internet\naccess, you must update your company-shared or local repository with the latest\n@product-ver@ Maven artifacts.\n\nWith these updates, you can easily upgrade your Liferay Maven build so you can\nbegin developing projects for @product-ver@.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/theme-generator/01-theme-generator-intro.markdown",
    "content": "---\nheader-id: theme-generator\n---\n\n# Theme Generator\n\n[TOC levels=1-4]\n\nThe Liferay Theme Generator generates themes for @product@. It is just one of\nLiferay JS Theme Toolkit's\n[tools](https://github.com/liferay/liferay-themes-sdk/tree/master/packages).\n\nA couple versions of the Liferay Theme Generator are available. The version you\nmust install depends on the version of @product@ you're developing on. The\nrequired versions are listed in the table below:\n\n| Liferay Version | Liferay Theme Generator Version | Command                                         |\n|:----------------|:--------------------------------|:------------------------------------------------|\n| 6.2             | 7.x.x                           | `npm install -g generator-liferay-theme@^7.x.x` |\n| 7.0             | 7.x.x or 8.x.x                  | Same as above or below                          |\n| 7.1             | 8.x.x                           | `npm install -g generator-liferay-theme@^8.x.x` |\n| 7.2             | 9.x.x                           | `npm install -g generator-liferay-theme@^9.x.x` |\n\nSee [Version Compatibility Matrix](https://learn.liferay.com/w/dxp/building-applications/tooling/reference/node-version-information#version-compatibility-matrix) for more information.\n\n## Sub-generators\n\nThe Liferay Theme Generator includes the sub-generators listed in the table\nbelow:\n\n| Sub-generator | Command to run              | Description                                                     |\n|:--------------|:----------------------------|:----------------------------------------------------------------|\n| Layouts       | `yo liferay-theme:layout`   | Generate layout templates with an interactive VIM.              |\n| Themelets     | `yo liferay-theme:themelet` | Create small, reusable, pieces of CSS and HTML for your themes. |\n\n### Layouts Sub-generator\n\nThe Layouts sub-generator provides the controls to create a layout template\nthat meets your needs. You can add and remove rows and columns on-the-fly as you\nrequire. See\n[Generating Layout Templates](/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator)\nfor more information.\n\n### Themelets Sub-generator\n\nThemelets are small, extendable, and reusable pieces of code. They only require\nthe CSS and/or JavaScript files that you want to add to your theme. This modular\napproach reduces the need for duplicated code across themes and makes it easy\nfor developers to share code snippets with other developers. Themelets are\napplicable for changes both small and large, from modifying the appearance of an\nindividual piece of UI to adding new features. If there is something you have to\nmanually code for every theme you create, it's a good candidate for a themelet.\nSee\n[Generating Themelets](/docs/7-2/reference/-/knowledge_base/r/creating-themelets-with-the-themes-generator)\nfor more information.\n\nWhile you can create themes using the tools you prefer, the Liferay Theme\nGenerator is designed with theme development for @product@ in mind. Its toolset\nand features help streamline theme development.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/theme-generator/02-installing-the-theme-generator-and-creating-themes.markdown",
    "content": "---\nheader-id: installing-the-theme-generator-and-creating-a-theme\n---\n\n# Installing the Theme Generator and Creating a Theme\n\n[TOC levels=1-4]\n\nThe steps below show how to install the Liferay Theme Generator and generate a\ntheme.\n\n![Figure 1: The tools are in your hands to create any theme you can imagine.](../../../images/theme-generator-theme-example.png)\n\nYour first step in generating a theme is installing\n[NodeJS](http://nodejs.org/)\n(along with Node Package Manager(npm))\nif it's not already installed. We recommend installing [v10.15.1](https://nodejs.org/download/release/v10.15.1/),\nwhich is the version Liferay Portal 7.2 supports (See\nthe [compatibility matrix](https://learn.liferay.com/w/dxp/building-applications/tooling/reference/node-version-information#version-compatibility-matrix)). Once NodeJS is installed and\nyou've [set up your npm environment](/docs/7-2/reference/-/knowledge_base/r/setting-up-your-npm-environment),\nyou can follow these steps to install the Liferay Theme Generator and generate a\ntheme:\n\n1.  Use npm to install the\n    [Yeoman](http://yeoman.io/)\n    dependency:\n\n    ```bash\n    npm install -g yo\n    ```\n\n    | **Note:** Gulp is included as a local dependency in generated themes, so you\n    | are not required to install it. It can be accessed by running\n    | `node_modules\\.bin\\gulp` followed by the Gulp task from a generated theme's\n    | root folder.\n\n2.  Install the Liferay Theme Generator with the command below:\n\n    ```bash\n    npm install -g generator-liferay-theme\n    ```\n\n    If you're on Windows, follow the instructions in step 3 to install Sass,\n    otherwise you can skip to step 4.\n\n3.  The generator uses node-sass. If you're on Windows, you must also install\n    [node-gyp and Python](https://github.com/nodejs/node-gyp#installation).\n\n4.  Run the generator and follow the prompts to create your theme:\n\n    ```bash\n    yo liferay-theme\n    ```\n\n    ![Figure 2: You can generate a theme by answering just a few configuration questions.](../../../images/theme-generator-theme-prompt.png)\n\n    | **Note:** Since Liferay DXP Fix Pack 2 and Liferay Portal 7.2 CE GA2, Font\n    | Awesome is available globally as a system setting, which is enabled by\n    | default. If you're using Font Awesome icons in your theme, answer yes (y)\n    | to the Font Awesome question to include Font Awesome imports in your\n    | theme. This ensures that your icons won't break if a Site Administrator\n    | disables the global setting.\n\n5.  Navigate to your theme folder and run `gulp deploy` to deploy your new theme\n    to the server.\n\nNow you have a powerful theme development tool at your disposal. The sky is the\nlimit!"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/theme-generator/03-generating-layout-templates.markdown",
    "content": "---\nheader-id: creating-layout-templates-with-the-themes-generator\n---\n\n# Generating Layout Templates with the Theme Generator\n\n[TOC levels=1-4]\n\nThis article shows how to use the Liferay Theme Generator's Layouts \nsub-generator to create a layout template. \n\n![Figure 1: The *1-2-1 Columns* page layout creates a nice flow for your content.](../../../images/layout-template-1-2-1-columns.png)\n\nYour first step in creating a layout template with the Liferay Theme Generator's \nLayouts sub-generator is installing the \n[Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme) \nif it's not already installed. Once the generator is installed, you can follow \nthese steps to create a layout template:\n\n1.  Open the Command Line and navigate to the folder where you want to create\n    your layout template.\n\n    | **Note:** Run the Layouts sub-generator from the theme's root folder to\n    | bundle it with the theme. This adds the layout template to the theme's\n    | `src/layouttpl/custom` folder. This **only works** for generated themes.\n\n2.  Run The Layouts sub-generator with the command below, and use the options \n    listed below to create your layout: \n\n    ```bash\n    yo liferay-theme:layout\n    ```\n\n    ![Figure 2: You must specify the width for each column in the row.](../../../images/layout-column-widths.png)\n\n    ![Figure 3: The Layouts sub-generator automates the layout creation process.](../../../images/layout-prompt.png)\n\n    - **Add a row:** Adds a row below the last row.\n    \n    - **Insert row:** Displays a vi to insert your row. Use your arrow keys to \n    choose where to insert your row, highlighted in blue, then press Enter to \n    insert the row.\n    \n    ![Figure 4: Rows can be inserted using the layout vi.](../../../images/insert-row.png)\n    \n    - **Remove row:** Displays a vi to remove your row. Use your arrow keys to \n    select the row you want to remove, highlighted in red, then press Enter to \n    remove the row.\n    \n    ![Figure 5: Rows are removed using the layout vi.](../../../images/remove-row.png)\n    \n    - **Finish Layout:** Complete the layout template.\n    \n    ![Figure 6: Select the *Finish layout* option to complete your design.](../../../images/finish-layout.png)\n\n3.  Run `gulp deploy` to deploy your layout template to the server you \n    specified, or deploy your theme if the layout is [bundled with it](/docs/7-2/frameworks/-/knowledge_base/f/including-layout-templates-with-a-theme).\n\n    | **Note:** Gulp is included as a local dependency of the generator, so you \n    | are not required to install it. It can be accessed by running \n    | `node_modules\\.bin\\gulp` followed by the Gulp task from a generated theme's \n    | root folder.\n\nAwesome! You just created a layout template with the Theme Generator's Layouts \nsub-generator. Your layout template project should have a file structure similar \nto the one below:\n\n- `my-liferay-layout-layouttpl/`\n    - `docroot/`\n        - `WEB-INF/`\n            - `liferay-layout-templates.xml`\n            - `liferay-plugin-package.properties`\n        - `my_liferay_layout.png`\n        - `my_liferay_layout.tpl`\n    - `node_modules/`\n        - (lots of packages)\n    - `gulpfile.js`\n    - `liferay-plugin.json`\n    - `package-lock.json`\n    - `package.json`\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/theme-generator/05-generating-themelets.markdown",
    "content": "---\nheader-id: creating-themelets-with-the-themes-generator\n---\n\n# Generating Themelets with the Theme Generator\n\n[TOC levels=1-4]\n\nThis steps below show how to use the Liferay Theme Generator's Themelets \nsub-generator to create a themelet. \n\n![Figure 1: Themelets can be used to modify one aspect of the UI, that you can then reuse in your other themes.](../../../images/product-menu-animation-themelet.png)\n\nYour first step in creating a themelet is installing the \n[Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/installing-the-theme-generator-and-creating-a-theme) \nif it's not already installed. Once the generator is installed, you can follow \nthese steps to create a themelet:\n\n1.  Open the Command Line and navigate to the folder you want to create your\n    themelet in.\n\n2.  Run `yo liferay-theme:themelet` and follow the prompts to generate the\n    themelet.\n\n    ![Figure 2: The Themelet sub-generator automates the themelet creation process, making it quick and easy.](../../../images/themelet-prompt.png)\n\n    The generated themelet contains a `package.json` file with configuration \n    information and a `src/css` folder that contains a `_custom.scss` file. Just \n    like a theme, add your CSS changes to the `src/css` folder, and add your \n    JavaScript changes to the `src/js` folder.\n\n3.  To use your themelet, you must install it globally first. This makes the \n    themelet visible to the generator. To install your themelet globally, \n    navigate into its root folder and run `npm link`. Note, you may need to run \n    the command using `sudo npm link`. This creates a globally-installed \n    symbolic link for the themelet in your npm packages folder. Now your \n    themelet is available to install in your themes. \n\nAwesome! You just created a themelet with the Theme Generator's Themelets \nsub-generator. \n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/upgrade-planner/01-liferay-upgrade-planner-intro.markdown",
    "content": "---\nheader-id: liferay-upgrade-planner\n---\n\n# Liferay Upgrade Planner\n\n[TOC levels=1-4]\n\nThe Liferay Upgrade Planner provides an automated way to adapt your\ninstallation's data and legacy plugins to your desired @product@ upgrade\nversion. We recommend leveraging this tool for any of the following upgrades:\n\n- Liferay Portal 6.2 &rarr; @product@ 7.0, 7.1, or 7.2\n- @product@ 7.0 &rarr; @product@ 7.1 or 7.2\n- @product@ 7.1 &rarr; @product@ 7.2\n\nFor step-by-step instructions for following the two upgrade paths, see the\nfollowing documentation:\n\n- [Data Upgrade](/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver)\n- [Code Upgrade](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver)\n\nThe Upgrade Planner is provided in\n[Liferay Dev Studio](/docs/7-2/reference/-/knowledge_base/r/liferay-dev-studio) \n(versions 3.6+). Here's what the Upgrade Planner does: \n\n<!-- Standalone app is planned, but not available yet. -->\n\n- Updates your development environment.\n- Identifies code affected by the API changes.\n- Describes each API change related to the code.\n- Suggests how to adapt the code.\n- Provides options, in some cases, to adapt code automatically.\n- Transfers database and server data to your new environment.\n\nEven if you prefer tools other than Dev Studio (which is based on Eclipse), you\nshould upgrade your data and legacy plugins using the Upgrade Planner first--you\ncan use your favorite tools afterward.\n\nTo start the Upgrade Planner in Dev Studio, do this:\n\n1.  Navigate to *Project* &rarr; *New Liferay Upgrade Plan...*.\n\n2.  In the New Liferay Upgrade Plan wizard, assign your plan a name and choose\n    an upgrade plan outline. The data and code upgrade processes are separate,\n    so you must step through each process independently.\n\n3.  Choose your current Liferay version and the new version you're upgrading to.\n\n4.  If you chose to complete a code upgrade, you must also select the folder\n    where your legacy plugins reside (e.g., Plugins SDK for Liferay 6.2\n    projects).\n\n5.  Click *Finish*.\n\n![Figure 1: Configure your upgrade plan before beginning the upgrade process.](../../../images/upgrade-plan-wizard.png)\n\nSwitch to the new Liferay Upgrade Planner perspective (prompted automatically).\nYou're now offered several windows in the UI:\n\n- *Project Explorer:* displays your legacy plugin environment and new\n  development environment. It also displays your\n  [upgrade problems](/docs/7-2/tutorials/-/knowledge_base/t/fixing-upgrade-problems)\n  that are detected during the *Fix Upgrade Problems* step.\n- *Liferay Upgrade Plan:* outlines the upgrade plan's steps and step summaries.\n- *Liferay Upgrade Plan Info:* shows official documentation that describes the\n  upgrade step.\n\nTo progress through your upgrade plan, click the steps outlined in the Liferay\nUpgrade Plan window. Each step can have several options:\n\n- *Click to preview:* previews what an automated step will perform.\n- *Click to perform:* executes an automated process provided with the step. This\n  is only offered for steps where the Upgrade Planner can assist.\n- *Click when complete:* marks the step as complete. This is only offered when\n  the Upgrade Planner cannot provide automated assistance and, instead, only\n  offers documentation to assist in completing the step manually.\n- *Restart:* marks a completed step as unfinished. The step is performed again\n  if automation is involved.\n- *Skip:* skips the step and jumps to the next step in the outline.\n\n![Figure 2: You can preview the Upgrade Planner's automated updates before you perform them.](../../../images/preview-upgrade-planner-changes.png)\n\nGreat! You now have a good understanding of the Liferay Upgrade Planner's UI and\nhow to get started. Visit the\n[Data Upgrade](/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver) and\n[Code Upgrade](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-code-to-product-ver)\nsections for more information on those upgrade processes.\n"
  },
  {
    "path": "en/developer/reference/articles/02-tooling/upgrade-planner/02-using-upgrade-planner-with-proxy-requirements.markdown",
    "content": "---\nheader-id: using-the-upgrade-planner-with-proxy-requirements\n---\n\n# Using the Upgrade Planner with Proxy Requirements\n\n[TOC levels=1-4]\n\nIf you have proxy server requirements and want to configure your http(s) proxy\t\t\nto work with the Liferay Upgrade Planner, follow the instructions below.\n\n1.  In Dev Studio's `DeveloperStudio.ini`/`eclipse.ini` file, add the following\n    parameters:\n\n    ```ini\n    -Djdk.http.auth.proxying.disabledSchemes=\n    -Djdk.http.auth.tunneling.disabledSchemes=\n    ```\n\n2.  Launch Dev Studio.\n\n3.  Go to *Window* &rarr; *Preferences* &rarr; *General* &rarr; *Network\n    Connections*.\n\n4.  Set the *Active Provider* drop-down selector to *Manual*.\n\n5.  Under *Proxy entries*, configure both proxy HTTP and HTTPS by clicking the\n    field and selecting the *Edit* button.\n\n    ![Figure 1: You can configure your proxy settings in Dev Studio's Network Connections menu.](../../../images/upgrade-planner-proxy.png)\n\n6.  For each schema (HTTP and HTTPS), enter your proxy server's host, port, and\n    authentication settings (if necessary). Do not leave whitespace at the end\n    of your proxy host or port settings.\n\n7.  Once you've configured your proxy entry, click *Apply and Close*.\n\nAwesome! You've successfully configured the Upgrade Planner's proxy settings!\n"
  },
  {
    "path": "en/developer/reference/articles/02-web-experience-management/01-intro.markdown",
    "content": "---\nheader-id: web-experience-management-reference\n---\n\n# Web Experience Management Reference\n\n[TOC levels=1-4]\n\nBrowse this section's reference articles for additional information on the\nWeb Experience Management framework.\n"
  },
  {
    "path": "en/developer/reference/articles/02-web-experience-management/02-page-fragments/01-fragment-specific-tags-intro.markdown",
    "content": "---\nheader-id: fragment-specific-tags\n---\n\n# Fragment Specific Tags\n\n[TOC levels=1-4]\n\nThere are Liferay-specific tags for creating editable, text, image, and link \nfields, and for embedding widgets.\n\n## Making Text Editable\n\nYou can make text of a fragment editable by enclosing it in an \n`<lfr-editable>` tag like this:\n\n```html\n<lfr-editable id=\"unique-id\" type=\"text\">\n   This is editable text!\n</lfr-editable>\n```\n\nIf you need formatting options like text or color styles, use `rich-text`:\n\n```html\n<lfr-editable id=\"unique-id\" type=\"rich-text\">\n   This is editable text that I can make bold or italic! \n</lfr-editable>\n```\n\nThe `lfr-editable` tag doesn't render without a unique `id`. \n\n| **Note:** If you want to make text inside an HTML element editable, you must \n| use the `rich-text` type. The `text` type strips HTML formatting out of the \n| text before rendering.\n\n## Making Images Editable\n\nImages use the same `<lfr-editable>` tag as text, but with the `image` type, \nlike this:\n \n```html\n<lfr-editable id=\"unique-id\" type=\"image\">\n   <img src=\"...\">\n</lfr-editable>\n```\n\nAfter you add the `lfr-editable` tag with the type `image` to a Fragment, when \nyou add that Fragment to a page, you can then click on the editable image to \nselect an image or configure content mapping for the image.\n\n![Figure 1: You have several options for defining an image on a Content Page.](../../../images/fragment-image-editor.png)\n\nMost images can be handled like this, but to add an editable background image\nyou must add an additional property to set the background image ID,\n`data-lfr-background-image-id`. The background image ID is set in the main `div`\nfor the Fragment and is the same as your editable image ID.\n\n```html\n<div data-lfr-background-image-id=\"unique-id\">\n   <lfr-editable id=\"unique-id\" type=\"image\">\n      <img src=\"...\">\n   </lfr-editable>\n</div>\n```\n\nContent mapping connects editable fields in your Fragment with fields from an \nAsset type like Web Content or Blogs. For example, you can map an image field \nto display a preview image for a Web Content Article. For more information on\nmapping fields, see \n[Editable Elements](/docs/7-2/user/-/knowledge_base/u/content-page-elements#editable-elements).\n\n## Creating Editable Links\n\nThere is also a specific syntax for creating editable link elements:\n\n```html\n<lfr-editable id=\"unique-id\" type=\"link\">\n    <a href=\"default-target-url-goes-here\">Link text goes here</a>\n</lfr-editable>\n```\n\nMarketers can edit the link text, target URL, and basic link styling---primary\nbutton, secondary button, link.\n\n![Figure 2: You have several options for defining a link's appearance and behavior.](../../../images/fragment-link-editor.png)\n\nFor more information on editable links, see \n[Editable Links](/docs/7-2/user/-/knowledge_base/u/content-page-elements#editable-links).\n\n## Including Widgets Within A Fragment \n\nTo include a widget, you must know its registered name. For example, the Site \nNavigation Menu portlet is registered as `nav`. Each registered portlet has an\n`lfr-widget-[name]` tag that's used to embed it. For example: the Navigation\nMenu tag is `<lfr-widget-nav />`. You could embed it in a block like this:\n\n```html\n<div class=\"nav-widget\">\n    <lfr-widget-nav>\n    </lfr-widget-nav>\n</div>\n```\n\nThese are the widgets that can be embedded and their accompanying tags:\n\n| Widget Name    | Tag |\n| -------- | --- |\t\n|DDL Display\t|`<lfr-widget-dynamic-data-list>`  |\n|Form           |`<lfr-widget-form>`               |\n|Asset Publisher|`<lfr-widget-asset-list>`     |\n|Breadcrumb\t    |`<lfr-widget-breadcrumb>` |\n|Categories Navigation |`<lfr-widget-categories-nav>` |\n|Flash\t|`<lfr-widget-flash>`|\n|Media Gallery\t|`<lfr-widget-media-gallery>`|\n|Navigation Menu\t|`<lfr-widget-nav>`|\n|Polls Display\t|`<lfr-widget-polls>`|\n|Related Assets\t|`<lfr-widget-related-assets>`|\n|Site Map\t|`<lfr-widget-site-map>`|\n|Tag Cloud\t|`<lfr-widget-tag-cloud>`|\n|Tags Navigation\t|`<lfr-widget-tags-nav>`|\n|Web Content Display\t|`<lfr-widget-web-content>`\n|RSS Publisher (Deprecated)\t|`<lfr-widget-rss>`|\n|Iframe\t|`<lfr-widget-iframe>`|\n\n## Enabling Embedding for Your Widget\n\nIf you have a custom widget that you want to embed in a fragment, you can \nconfigure that widget to be embeddable. To embed your widget, it must be an OSGi\nComponent. Inside the `@Component` annotation for the portlet class you want to\nembed, add this property:\n\n```properties\ncom.liferay.fragment.entry.processor.portlet.alias=app-name\n```\n\nWhen you deploy your widget, it's available to add. The name you specify in the\nproperty must be appended to the `lfr-widget` tag like this:\n\n```markup\n<lfr-widget-app-name>\n</lfr-widget-app-name>\n```\n\n| **Note:** According to the W3C HTML standards, custom elements cannot be self \n| closing. Therefore, even though you cannot add anything between the opening and\n| closing `<lfr-widget...>` tags, you cannot use the self closing notation for \n| the tag.\n"
  },
  {
    "path": "en/developer/reference/articles/02-web-experience-management/02-page-fragments/02-fragment-configuration-types.markdown",
    "content": "---\nheader-id: fragment-configuration-types\n---\n\n# Fragment Configuration Types\n\n[TOC levels=1-4]\n\nThere are four configurable Fragment types available to implement:\n\n- `checkbox`\n- `colorPalette`\n- `select`\n- `text`\n\nFor more information on how to make a Fragment configurable, see \n[Making a Fragment Configurable](/docs/7-2/frameworks/-/knowledge_base/f/making-a-fragment-configurable).\n\nIn this article, you'll explore JSON examples of each Fragment type.\n\n## Checkbox Configuration\n\nThe following JSON configuration creates a checkbox you can implement for cases\nwhere a boolean value selection is necessary:\n\n```json\n{\n    \"fieldSets\": [\n        {\n            \"fields\": [\n                {\n                    \"name\": \"hideBody\",\n                    \"label\": \"Hide Body\",\n                    \"description\": \"hide-body\",\n                    \"type\": \"checkbox\",\n                    \"defaultValue\": false\n                }\n                ...\n            ]\n        }\n    ]\n}\n```\n\n![Figure 1: The checkbox configuration is useful when a boolean selection is necessary.](../../../images/fragment-config-checkbox.png)\n\n## Color Palette Configuration\n\nThe following JSON configuration creates a color selector you can implement for\ncases where you must select a color:\n\n```json\n{\n    \"fieldSets\": [\n        {\n            \"fields\": [\n                {\n                    \"name\": \"textColor\",\n                    \"label\": \"Text color\",\n                    \"type\": \"colorPalette\",\n                    \"dataType\": \"object\",\n                    \"defaultValue\": {\n                        \"cssClass\": \"white\",\n                        \"rgbValue\": \"rgb(255,255,255)\"\n                    }\n                }\n                ...\n            ]\n        }\n    ]\n}\n```\n\nThe `colorPalette` type stores an object with two values: `cssClass` and\n`rgbValue`.\n\nFor example, if you implement the snippet above, you can use it in the\nFreeMarker context like this:\n\n```html\n<h3 class=\"text-${configuration.textColor.cssClass}\">Example</h3>\n```\n\nIf you were to choose the color white, the `h3` tag heading would have the class\n`text-white'`.\n\n![Figure 2: The `colorPalette` configuration is useful when a color selection is necessary.](../../../images/fragment-config-colorpalette.png)\n\n## Select Configuration\n\nThe following JSON configuration creates a selector you can implement for cases\nwhere you must select a predefined option:\n\n```json\n{\n    \"fieldSets\": [\n        {\n            \"fields\": [\n                {\n                    \"name\": \"numberOfFeatures\",\n                    \"label\": \"Number Of Features\",\n                    \"description\": \"number-of-features\",\n                    \"type\": \"select\",\n                    \"dataType\": \"int\",\n                    \"typeOptions\": {\n                        \"validValues\": [\n                            {\"value\": \"1\"},\n                            {\"value\": \"2\"},\n                            {\"value\": \"3\"}\n                        ]\n                    },\n                    \"defaultValue\": \"3\"\n                }\n                ...\n            ]\n        }\n    ]\n}\n```\n\nConfiguration values inserted into the FreeMarker context honor the defined\n`datatype` value specified in the JSON file.\n\n![Figure 3: The `select` configuration is useful when an option choice is necessary.](../../../images/fragment-config-select.png)\n\n## Text Configuration\n\nThe following JSON configuration creates an input text field you can implement\nfor cases where you must manually input a text option:\n\n```json\n{\n    \"fieldSets\": [\n        {\n            \"fields\": [\n                {\n                    \"name\": \"buttonText\",\n                    \"label\": \"Button Text\",\n                    \"description\": \"button-text\",\n                    \"type\": \"text\",\n                    \"typeOptions\": {\n                        \"placeholder\": \"Placeholder\"\n                    },\n                    \"dataType\": \"string\",\n                    \"defaultValue\": \"Go Somewhere\"\n                }\n                ...\n            ]\n        }\n    ]\n}\n```\n\nConfiguration values inserted into the FreeMarker context honor the defined\n`datatype` value specified in the JSON file.\n\n![Figure 4: The `text` configuration is useful when an input text option is necessary.](../../../images/fragment-config-text.png)\n"
  },
  {
    "path": "en/developer/reference/articles/02-web-experience-management/02-page-fragments/03-escaping-fragment-configuration-text-values.markdown",
    "content": "---\nheader-id: escaping-fragment-configuration-text-values\n---\n\n# Escaping Fragment Configuration Text Values\n\n[TOC levels=1-4]\n\nWhen you define text configuration and other options for a Fragment, Fragment\ndevelopers can declare any text value they want. With this freedom comes risk;\nmalicious code could be inserted into the text field, wreaking havoc for other\nusers of the Fragment.\n\nIn this article, you'll learn how to escape Fragment text values so Fragment\nauthors are protected from XSS attacks.\n\n### Escaping Values in HTML/FreeMarker\n\nYou must take special care when adding a text value in your Fragment's HTML. For\nexample, if a user includes malicious code within `<script>` tags, it runs when\nthe page is rendered. \n\nTo solve this problem, a utility is available in the FreeMarker context via the\n`htmlUtil` class.\n\nFor generic cases, an `escape` method is available:\n\n```html\n<div class=\"fragment_38816\">\n    ${htmlUtil.escape(configuration.text)}\"\n</div>\n```\n\nThis escapes your Fragment configuration's `text` field, preventing malicious\ncode from affecting Fragment authors.\n\nFor more information on escaping methods, see the\n[HtmlUtil](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/util/HtmlUtil.html)\nclass.\n\n### Escaping Values in JavaScript\n\nWhen including JavaScript in your Fragment, you must be aware of potential\nattack vectors, like setting an attribute or appending HTML children. To prevent\nthese attacks, you should use the `Liferay.Util.escapeHTML` function. You can\ncall it from your Fragment's JavaScript like this:\n\n```js\nfunction (fragmentElement, configuration) {\n    const escapedValue = Liferay.Util.escapeHTML(configuration.text)\n}\n```\n\nThis escapes your Fragment configuration's `text` field, preventing malicious\ncode in your Fragment's JavaScript code.\n"
  },
  {
    "path": "en/developer/reference/articles/02-web-experience-management/03-asset-display-page-taglib.markdown",
    "content": "---\nheader-id: asset-display-page-taglib\n---\n\n# Asset Display Page Taglib\n\n[TOC levels=1-4]\n\nOnce you have created an Asset Display Page associated with your Asset type, \nyou can add the option to select an Asset Display Page in the form where you\ncreate the Asset. The `<liferay-asset:select-asset-display-page />` taglib\nrenders a form field for selecting an Asset Display Page for\nyour asset.\n\nThere are three options when selecting a display page:\n\n- The default display page for the asset type if one has been configured.\n\n- Any other selectable display page.\n\n- None\n\nIf you select no default display page, a display page is not defined. \n\n## Display Page Attributes\n\nWhen you use the display page selector taglib, you can define the following \nattributes:\n\n`classNameId (long)` (required): a class name ID of the asset type to \nselect an asset display page for.\n\n`classPK (long)`: a primary key of the asset entry to select an asset \ndisplay page for.\n\n`classTypeId (long)`: a class type ID of the asset type to select an \nasset display page for.\n\n`eventName (String)`: event name which fires when a user selects a display\npage using the item selector.\n\n`groupId (long)` (required): the entity's group ID to select an asset \ndisplay page for.\n\n`showPortletLayouts (boolean)`: allow selection of pages that have Asset\nPublisher configured as a default Asset Publisher for the page.\n\nThe attribute `showPortletLayout` provides backwards compatibility for display \npages created for Journal Articles in older versions. When `showPortletLayouts`\nis set to true, you can select any public or private pages with an Asset\nPublisher widget on it configured as the *Default Asset Publisher for the\npage*.\n\nWhen submitting a form with the taglib, it populates the request with the \nfollowing parameters:\n\n`displayPageType (int)`: 1 = Default, 2 = Specific, 3 = None.\n\n`assetDisplayPageId (long)`: ID of selected Asset Display Page.\n\n`layoutUuid`: Layout UUID in case of a portlet page with default Asset \nPublisher.\n"
  },
  {
    "path": "en/developer/reference/articles/02-workflow/01-creating-workflow-definitions-intro.markdown",
    "content": "---\nheader-id: crafting-xml-workflow-definitions\n---\n\n# Crafting XML Workflow Definitions\n\n[TOC levels=1-4]\n\nYou don't need a fancy visual designer to build workflows. To be clear, Kaleo\nDesigner may make you a faster workflow designer through its graphical\ninterface. If you plan to build lots of workflow processes, a Digital Enterprise\nsubscription gets you access to Kaleo Designer. But with a little copy and paste\nfrom existing workflows and a little handcrafted XML, you can build any workflow\nand attain workflow wizard-hood in the process. Follow this set of tutorials to\nlearn what elements you can put into your definitions.\n\n## Existing Workflow Definitions\n\nOnly one workflow definition is installed by default: Single Approver. Several\nmore, however, are embedded in the source code of your @product@ installation.\nIf you're comfortable extracting the XML files from a JAR file embedded in an\nLPKG file, you're welcome to follow the steps below to obtain the workflow\ndefinitions. \n\nTo extract the definitions for yourself,\n\n1.  Navigate to\n\n        [Liferay Home]/osgi/marketplace\n\n2.  Using an archive manager, open the LPKG\n\n        Liferay CE Forms and Workflow.lpkg\n\n3.  Open the JAR file named\n\n        com.liferay.portal.workflow.kaleo.runtime.impl-[version].jar\n\n4.  In the JAR file, navigate to\n\n        META-INF/definitions/\n\n5.  Extract the four XML workflow definition files. These definitions provide\n    good reference material for many of the workflow features and elements\n    described in these articles. In fact, most of the XML snippets you see here\n    are lifted directly from these definitions.\n\n## Schema\n\nThe XML structure of a workflow definition is defined in an XSD file:\n\n    liferay-worklow-definition-7_2_0.xsd\n\nDeclare the schema at the top of the workflow definition file:\n\n```xml\n<?xml version=\"1.0\"?>\n<workflow-definition\n    xmlns=\"urn:liferay.com:liferay-workflow_7.2.0\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"urn:liferay.com:liferay-workflow_7.2.0\n        http://www.liferay.com/dtd/liferay-workflow-definition_7_2_0.xsd\">\n```\n\nTo read 464 lines of beautifully formatted XML that defines how to write more\nXML (it's practically poetic), check out the XSD\n[here](https://www.liferay.com/dtd/liferay-workflow-definition_7_2_0.xsd).\nOtherwise, move on to entering the definition's metadata.\n\n## Metadata\n\nGive the definition a name, description, and version:\n\n```xml\n<name>Category Specific Approval</name>\n<description>A single approver can approve a workflow content.</description>\n<version>1</version>\n```\n\nAll these tags are optional. If present the first time a definition is saved,\nthe `<name>` tag serves as a unique identifier for the definition. If not\nspecified (or added sometime after the first save), a random unique name is\ngenerated and used to identify the workflow.\n\nOnce the schema and metadata are in place, it's time to turn up the funky beats\nand get into the flow (the workflow). Learn about workflow nodes in the next\narticle.\n"
  },
  {
    "path": "en/developer/reference/articles/02-workflow/02-workflow-nodes.markdown",
    "content": "---\nheader-id: workflow-definition-nodes\n---\n\n# Workflow Definition Nodes\n\n[TOC levels=1-4]\n\nAfter your definition's schema and metadata are in place, begin defining the\nprocess. *Node* elements, with their sub-elements, are fundamental building\nblocks making up workflow definitions.\n\n## State Nodes\n\nState nodes don't require user input. The workflow does whatever is specified\nin the state node's `actions` tag (a notification and/or a custom script), and\nthen moves to the provided transition. Workflows start and end with a state.\nThe initial state node often only contains a transition:\n\n```xml\n<state>\n    <name>created</name>\n    <initial>true</initial>\n    <transitions>\n        <transition>\n            <name>Determine Branch</name>\n            <target>determine-branch</target>\n            <default>true</default>\n        </transition>\n    </transitions>\n</state>\n```\n\nIf a notification or script is required in your state node, use an `actions`\ntag. Here's an `action` element containing a Groovy script. This is found in\nmany terminal state nodes and marks the asset as approved in the workflow.\n\n```xml\n<actions>\n    <action>\n        <name>Approve</name>\n        <description>Approve</description>\n        <script>\n            <![CDATA[\n            com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil.\n                updateStatus(com.liferay.portal.kernel.workflow.WorkflowConstants.     \n                    getLabelStatus(\"approved\"), workflowContext);]]>\n        </script>\n        <script-language>groovy</script-language>\n        <execution-type>onEntry</execution-type>\n    </action>\n</actions>\n```\n\n## Conditions\n\nConditions let you inspect the asset (or its execution context) and do\nsomething, like send it to a particular transition.\n\nHere's the `determine-branch` condition from the Category Specific Approval\nworkflow definition:\n\n```xml\n<condition>\n    <name>determine-branch</name>\n    <script>\n        <![CDATA[\n            import com.liferay.asset.kernel.model.AssetCategory;\n            import com.liferay.asset.kernel.model.AssetEntry;\n            import com.liferay.asset.kernel.model.AssetRenderer;\n            import com.liferay.asset.kernel.model.AssetRendererFactory;\n            import com.liferay.asset.kernel.service.AssetEntryLocalServiceUtil;\n            import com.liferay.portal.kernel.util.GetterUtil;\n            import com.liferay.portal.kernel.workflow.WorkflowConstants;\n            import com.liferay.portal.kernel.workflow.WorkflowHandler;\n            import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;\n\n            import java.util.List;\n\n            String className = (String)workflowContext.get(WorkflowConstants.CONTEXT_ENTRY_CLASS_NAME);\n\n            WorkflowHandler workflowHandler = WorkflowHandlerRegistryUtil.getWorkflowHandler(className);\n\n            AssetRendererFactory assetRendererFactory = workflowHandler.getAssetRendererFactory();\n\n            long classPK = GetterUtil.getLong((String)workflowContext.get(WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n            AssetRenderer assetRenderer = workflowHandler.getAssetRenderer(classPK);\n\n            AssetEntry assetEntry = assetRendererFactory.getAssetEntry(assetRendererFactory.getClassName(), assetRenderer.getClassPK());\n\n            List<AssetCategory> assetCategories = assetEntry.getCategories();\n\n            returnValue = \"Content Review\";\n\n            for (AssetCategory assetCategory : assetCategories) {\n                String categoryName = assetCategory.getName();\n\n                if (categoryName.equals(\"legal\")) {\n                    returnValue = \"Legal Review\";\n\n                    return;\n                }\n            }\n        ]]>\n    </script>\n    <script-language>groovy</script-language>\n    <transitions>\n        <transition>\n            <name>Legal Review</name>\n            <target>legal-review</target>\n            <default>false</default>\n        </transition>\n        <transition>\n            <name>Content Review</name>\n            <target>content-review</target>\n            <default>false</default>\n        </transition>\n    </transitions>\n</condition>\n```\n\nThis example checks the asset category to choose the processing path, whether to\ntransition to the *Legal Review* task or the *Content Review* task.\n\nThe `returnValue` variable points from the condition to a transition, and its\nvalue must match a valid transition name. This script looks up the asset in\nquestion, retrieves its asset category, and sets an initial `returnValue`. Then\nit checks to see if the asset has been marked with the *legal* category. If not\nit goes through *Content Review* (to the content-review task in the workflow),\nand if it does it goes through *Legal Review* (to the legal-review task in the\nworkflow).\n\n## Forks and Joins\n\nForks split the workflow process, and joins bring the process back to a unified\nbranch. Processing must always be brought back using a Join (or a Join XOR),\nand the number of forks and joins in a workflow definition must be equal.\n\n```xml\n<fork>\n    <name>fork-1</name>\n    <transitions>\n        <transition>\n            <name>transition-1</name>\n            <target>task-1</target>\n            <default>true</default>\n        </transition>\n        <transition>\n            <name>transition-2</name>\n            <target>task-2</target>\n            <default>false</default>\n        </transition>\n    </transitions>\n</fork>\n<join>\n    <name>join-1</name>\n    <transitions>\n        <transition>\n            <name>transition-4</name>\n            <target>EndNode</target>\n            <default>true</default>\n        </transition>\n    </transitions>\n</join>\n```\n\nThe workflow doesn't move past the join until the asset transitions to it\nfrom both of the forks. To fork the workflow process, but then allow the\nprocessing to continue when only one fork is completed, use a Join XOR.\n\nA Join XOR differs from a join in one important way: it removes the constraint\nthat both forks must be completed before processing can continue. The asset\nmust complete just one of the forks before processing continues.\n\n```xml\n<join-xor>\n    <name>join-xor</name>\n    <transitions>\n        <transition>\n            <name>transition3</name>\n            <target>EndNode</target>\n            <default>true</default>\n        </transition>\n    </transitions>\n</join-xor>\n```\n\n## Task Nodes\n\n[Task nodes](/docs/7-2/reference/-/knowledge_base/r/workflow-task-nodes)\nare at the core of the workflow definition. They're the part where a user\ninteracts with the asset in some way. Tasks can also have sub-elements,\nincluding notifications, assignments, and task timers.\n\nHere's the `content-review` task from the Category Specific Approval\nworkflow, with some of the `role` assignment tags cut out for brevity:\n\n```xml\n<task>\n    <name>content-review</name>\n    <actions>\n        <notification>\n            <name>Review Notification</name>\n            <template>You have a new submission waiting for your review in the workflow.</template>\n            <template-language>text</template-language>\n            <notification-type>email</notification-type>\n            <notification-type>user-notification</notification-type>\n            <execution-type>onAssignment</execution-type>\n        </notification>\n    </actions>\n    <assignments>\n        <roles>\n            <role>\n                <role-type>organization</role-type>\n                <name>Organization Administrator</name>\n            </role>\n            ...\n        </roles>\n    </assignments>\n    <task-timers>\n        <task-timer>\n            <name></name>\n            <delay>\n                <duration>1</duration>\n                <scale>hour</scale>\n            </delay>\n            <blocking>false</blocking>\n            <timer-actions>\n                <timer-notification>\n                    <name></name>\n                    <template></template>\n                    <template-language>text</template-language>\n                    <notification-type>user-notification</notification-type>\n                </timer-notification>\n            </timer-actions>\n        </task-timer>\n    </task-timers>\n    <transitions>\n        <transition>\n            <name>approve</name>\n            <target>approved</target>\n            <default>true</default>\n        </transition>\n        <transition>\n            <name>reject</name>\n            <target>update</target>\n            <default>false</default>\n        </transition>\n    </transitions>\n</task>\n```\n\nLearn more about workflow tasks in the next article.\n"
  },
  {
    "path": "en/developer/reference/articles/02-workflow/03-tasks.markdown",
    "content": "---\nheader-id: workflow-task-nodes\n---\n\n# Workflow Task Nodes\n\n[TOC levels=1-4]\n\nTask nodes are fundamental parts of a workflow definition. When you define your\norganization's business processes and design corresponding workflows, you likely\nfirst envision the tasks. As the name implies, tasks are the part of the\nworkflow where *work* is done. A user enters the picture and must interact with\nthe submitted asset. Users often take the role of reviewer, deciding if an asset\nfrom the workflow is acceptable for publication or needs more work.\n\nUnlike other workflow nodes, task nodes have Assignments, because a user is\nexpected to *do something* (often approve or reject the submitted asset) when a\nworkflow process enters the task node.\n\nCommonly, task nodes contain task timers, assignments, actions (which can\ninclude notifications and scripts), and transitions. Notifications and actions\naren't limited to task nodes, but task nodes and their assignments deserve their\nown article (this one).\n\nCheck out the Review task in the Single Approver definition, noting that several\n`<role>` tags are excluded from this snippet for brevity:\n\n```xml\n<task>\n\t<name>review</name>\n\t<actions>\n\t\t<notification>\n\t\t\t<name>Review Notification</name>\n\t\t\t<template>${userName} sent you a ${entryType} for review in the workflow.</template>\n\t\t\t<template-language>freemarker</template-language>\n\t\t\t<notification-type>email</notification-type>\n\t\t\t<notification-type>user-notification</notification-type>\n\t\t\t<execution-type>onAssignment</execution-type>\n\t\t</notification>\n\t\t<notification>\n\t\t\t<name>Review Completion Notification</name>\n\t\t\t<template><![CDATA[Your submission was reviewed<#if taskComments?has_content> and the reviewer applied the following ${taskComments}</#if>.]]></template>\n\t\t\t<template-language>freemarker</template-language>\n\t\t\t<notification-type>email</notification-type>\n\t\t\t<recipients>\n\t\t\t\t<user />\n\t\t\t</recipients>\n\t\t\t<execution-type>onExit</execution-type>\n\t\t</notification>\n\t</actions>\n\t<assignments>\n\t\t<roles>\n\t\t\t<role>\n\t\t\t\t<role-type>organization</role-type>\n\t\t\t\t<name>Organization Administrator</name>\n\t\t\t</role>\n              ...\n\t\t</roles>\n\t</assignments>\n\t<transitions>\n\t\t<transition>\n\t\t\t<name>approve</name>\n\t\t\t<target>approved</target>\n\t\t</transition>\n\t\t<transition>\n\t\t\t<name>reject</name>\n\t\t\t<target>update</target>\n\t\t\t<default>false</default>\n\t\t</transition>\n\t</transitions>\n</task>\n```\n\nThere are two `actions` in the review task, both `<notification>`s. Each\nnotification may contain a name, template, notification-type, execution-type,\nand recipients. Besides notifications, You can also use the `<action>` tag.\nThese have a name and a\n[script](/docs/7-2/user/-/knowledge_base/u/leveraging-the-script-engine-in-workflow)\nand are more often used in state nodes than tasks.\n\n## Assignments\n\nWorkflow tasks are completed by a user. Assignments make sure the right users\ncan access the tasks. You can choose how you want to configure your assignments.\n\nYou can choose to add assignments to specific roles, to multiple roles of a role\ntype (organization, site, or regular role types), to the asset creator, to\nresource actions, or to specific users. Additionally, you can write a script to\ndefine the assignment. For an example, see the\n`single-approver-definition-scripted-assignment.xml`.\n\n```xml\n<assignments>\n    <roles>\n        <role>\n            <role-type>organization</role-type>\n            <name>Organization Administrator</name>\n        </role>\n    </roles>\n</assignments>\n```\n\nThe above assignment specifies that an Organization Administrator must complete\nthe task.\n\n```xml\n<assignments>\n    <user>\n        <user-id>20156</user-id>\n    </user>\n</assignments>\n```\n\nThe above assignment specifies that only the user with the user ID of 20156 may\ncomplete the task. Alternatively, specify the `<screen-name>` or\n`<email-address>` of the user.\n\n```xml\n<assignments>\n    <scripted-assignment>\n        <script>\n            <![CDATA[\n                    import com.liferay.portal.kernel.model.Group;\n                    import com.liferay.portal.kernel.model.Role;\n                    import com.liferay.portal.kernel.service.GroupLocalServiceUtil;\n                    import com.liferay.portal.kernel.service.RoleLocalServiceUtil;\n                    import com.liferay.portal.kernel.util.GetterUtil;\n                    import com.liferay.portal.kernel.workflow.WorkflowConstants;\n\n                    long companyId = GetterUtil.getLong((String)workflowContext.get(WorkflowConstants.CONTEXT_COMPANY_ID));\n\n                    long groupId = GetterUtil.getLong((String)workflowContext.get(WorkflowConstants.CONTEXT_GROUP_ID));\n\n                    Group group = GroupLocalServiceUtil.getGroup(groupId);\n\n                    roles = new ArrayList<Role>();\n\n                    Role adminRole = RoleLocalServiceUtil.getRole(companyId, \"Administrator\");\n\n                    roles.add(adminRole);\n\n                    if (group.isOrganization()) {\n                        Role role = RoleLocalServiceUtil.getRole(companyId, \"Organization Content Reviewer\");\n\n                        roles.add(role);\n                    }\n                    else {\n                        Role role = RoleLocalServiceUtil.getRole(companyId, \"Site Content Reviewer\");\n\n                        roles.add(role);\n                    }\n\n                    user = null;\n                ]]>\n            </script>\n        <script-language>groovy</script-language>\n    </scripted-assignment>\n</assignments>\n```\n\nThe above assignment assigns the task to the *Administrator* role, then checks\nwhether the *group* of the asset is an Organization. If it is, the *Organization\nContent Reviewer* role is assigned to it. If it's not, the task is assigned to\nthe *Site Content Reviewer* role.\n\nNote the `roles = new ArrayList<Role>();` line above. In a scripted assignment,\nthe `roles` variable is where you specify any roles the task is assigned to. For\nexample, when `roles.add(adminRole);` is called, the Administrator role is added\nto the assignment.\n\nAssigning tasks to Roles, Organizations, or Asset Creators is a straightforward\nconcept, but what does it mean to assign a workflow task to a Resource Action?\nImagine an *UPDATE* resource action. If your workflow definition specifies the\nUPDATE action in an assignment, then anyone who has permission to update the\ntype of asset being processed in the workflow is assigned to the task. You can\nconfigure multiple assignments for a task.\n\n## Resource Action Assignments\n\n*Resource actions* are operations performed by users on an application or\nentity. For example, a user might have permission to update Message Boards\nMessages. This is called an UPDATE resource action, because the user can update\nthe resource. If you're uncertain about what resource actions are, refer to the\ndeveloper tutorial on the\n[permission system](/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions)\nfor a more detailed explanation.\n\nTo find all the resource actions that have been created, you need access to the\nRoles Admin application in the Control Panel (in other words, you need\npermission for the VIEW action on the roles resource).\n\n- Navigate to Control Panel &rarr; Users &rarr; Roles.\n- Add a new Regular Role. See the\n  [article on managing roles](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions)\n  for more information.\n- Once the role is added, navigate to the Define Permissions interface for the\n  role.\n- Find the resource whose action should define your workflow assignment.\n\nHere's what the assignment's XML looks like:\n\n```xml\n<assignments>\n    <resource-actions>\n        <resource-action>UPDATE</resource-action>\n    </resource-actions>\n</assignments>\n```\n\nNow when the workflow proceeds to the task with the resource action assignment,\nusers with `UPDATE` permission on the resource (for example, Message Boards\nMessages) are notified of the task and can assign it to themselves (if the\nnotification is set to Task Assignees). Specifically, users see the tasks in\ntheir *My Workflow Tasks* application under the tab *Assigned to My Roles*.\n\nUse all upper case letters for resource action names. Here are some common\nresource actions:\n\n    UPDATE\n    ADD\n    DELETE\n    VIEW\n    PERMISSIONS\n    SUBSCRIBE\n    ADD_DISCUSSION\n\nDetermine the probable resource action name from the permissions screen for a\nresource. For example, in Message Boards, one of the permissions displayed on\nthat screen is *Add Discussion*. Convert that to all uppercase and replace the\nspace with an underscore, and you have the action name.\n\n## Task Timers\n\nTask timers trigger an action after a specified time period passes. Timers are\nuseful for ensuring a task does not go unattended for a long time. Available\ntimer actions include sending an additional notification, reassigning the asset,\nor creating a timer action.\n\n```xml\n<task-timers>\n    <task-timer>\n        <name></name>\n        <delay>\n            <duration>1</duration>\n            <scale>hour</scale>\n        </delay>\n        <blocking>false</blocking>\n        <recurrence>\n            <duration>10</duration>\n            <scale>minute</scale>\n        </recurrence>\n        <timer-actions>\n            <timer-notification>\n                <name></name>\n                <template></template>\n                <template-language>text</template-language>\n                <notification-type>user-notification</notification-type>\n            </timer-notification>\n        </timer-actions>\n    </task-timer>\n</task-timers>\n```\n\nThe above task timer creates a notification. Specify a time period in the\n`<delay>` tag, and specify what action to take when the time expires in the\n`<timer-actions>` block. The `<blocking>` element specifies whether the timer\nactions may recur. If blocking is set to `false`, timer actions may recur. In\na `recurrence` element, specify the recurrence interval using a `duration` and\na `scale`, as demonstrated above. The above recurrence element specifies that\nthe timer actions run again every ten minutes after the initial occurrence.\nSetting blocking to true prevents timer actions from recurring.\n\n```xml\n<timer-actions>\n    <reassignments>\n       <assignments>\n         <roles>\n          <role>\n              <role-type></role-type>\n              <name></name>\n          </role>\n          ...\n         </roles>\n       </assignments>\n    </reassignments>\n</timer-actions>\n```\n\nThe above snippet demonstrates how to set up a reassignment action.\n\nLike `<action>` elements, `<timer-action>` elements can contain scripts.\n\n```xml\n<timer-actions>\n    <timer-action>\n        <name>doSomething</name>\n        <description>Do something cool when time runs out.</description>\n        <script>\n           ...\n        </script>\n        <script-language>groovy</script-language>\n    </timer-action>\n</timer-actions>\n```\n\nThe above example isn't functional but it demonstrates setting up a `<script>`\nin your task timer.\n[Read the _Scripting in Workflow_ article](/docs/7-2/user/-/knowledge_base/u/leveraging-the-script-engine-in-workflow)\nfor more information.\n\n| **Note:** A `timer-action` can contain all the same tags as an `action`, with\n|one exception: `execution-type`. Timer actions are always triggered once the\n|time is up, so specifying and execution type of `onEntry`, for example, isn't\n|meaningful inside a timer.\n\nTasks are at the core of the workflow definition. Once you understand how to\ncreate tasks and the other\n[workflow nodes](/docs/7-2/reference/-/knowledge_base/r/workflow-definition-nodes) and add\ntransitions between the nodes, you're on the cusp of workflow wizard-hood.\n"
  },
  {
    "path": "en/developer/reference/articles/02-workflow/04-notifications.markdown",
    "content": "---\nheader-id: workflow-notifications\n---\n\n# Workflow Notifications\n\n[TOC levels=1-4]\n\nWhile an asset is in a workflow, relevant Users should be notified about certain\nevents, like when a review task is completed. Any workflow node with an\n`<actions>` element can have notifications.\n\n```xml\n<actions>\n    <action>\n        <notification>\n            <name>Creator Modification Notification</name>\n            <template>Your submission was rejected by ${userName}, please modify and resubmit.</template>\n            <template-language>freemarker</template-language>\n            <notification-type>email</notification-type>\n            <notification-type>user-notification</notification-type>\n            <execution-type>onAssignment</execution-type>\n        </notification>\n    </actions>\n</actions>\n```\n\nThe above Creator Modification Notification sends a notification message in two\nways: via email and via user notification (this goes to the Notifications widget\nin the User's Site). The message is defined in a FreeMarker template and sent\nonce a task assignment is created. But who receives the notification? If no\nrecipients are explicitly specified via a `recipients` tag, the asset's creator\nreceives the notification.\n\n## Notification Options\n\nThere are several elements that can be specified in a `<notification>`:\n\n**Name**\n: Set the name of the notification in the `<name>` element. This information is\nused to display the notification in the _My Workflow Tasks_ widget of a User's\npersonal Site.\n\n**Description**\n: The `<description>` element contains the subject of the notification if the\nnotification type is `email`. The syntax is determined by the template language\nyou're using.\n\n**Template**\n: The `<template>` element contains the message of the notification. The syntax\nis determined by the template language you're using.\n\n**Template Language**\n: Choose from `freemarker`, `velocity`, or plain `text` in the\n`<template-language>` tag.\n\n**Notification Type**\n: Choose whether to send an `email`, `user-notification` (via the Notification\nwidget), `im` (instant message), or `private-message` in the\n`<notification-type>` tag.\n\n```xml\n<notification-type>email</notification-type>\n```\n\n**Execution Type**\n: Choose to link the sending of the notification to entry into the node\n(`onEntry`), when a task is assigned (`onAssignment`), or when the workflow\nprocessing is leaving a node (`onExit`). If you specify a notification to be\nsent on assignment, the assignee is notified automatically.\n\n**Recipients**\n: Decide who should receive the notification in the `<recipients>` tag:\n\n```xml\n<recipients>\n    [SEE BELOW FOR THE AVAILABLE RECIPIENT TAGS]\n</recipients>\n```\n\nAvailable recipient tags are\n\n- `<user>`: notify the User that sent the asset through the workflow.\n  Specify the tag as `<user />`. To notify a specific user, enter the\n  `userId`:\n\n```xml\n<recipients>\n    <user />\n</recipients>\n<recipients>\n    <user>\n        <user-id>20139</user-id>\n    </user>\n</recipients>\n```\n\n- `<roles>`: notify specific Roles, either by ID or by their type and name.\n\n```xml\n<recipients>\n    <roles>\n        <role>\n            <role-id>33621</role-id>\n        </role>\n    </roles>\n</recipients>\n<recipients>\n    <roles>\n        <role>\n            <role-type>regular</role-type>\n            <name>Power User</name>\n            <auto-create>false</auto-create>\n        </role>\n    </roles>\n</recipients>\n```\n\n- `<assignees />`: notify the task assignees.\n\n- `<scripted-recipient>`: use a script to identify notification recipients.\n\n```xml\n<recipients>\n    <scripted-recipient>\n        <script>\n            <![CDATA[Script logic goes here]]>\n        </script>\n        <script-language>groovy</script-language>\n    </scripted-recipient>\n</recipients>\n```\n\nIf the notification type is `email`, you can specify the `recipientType`\nattribute of the `<recipients>` tag as _To_, _CC_, or _BCC_.\n\n```xml\n<recipients receptionType=\"cc\">\n    <roles>\n        <role>\n            <role-type>regular</role-type>\n            <name>Manager</name>\n        </role>\n    </roles>\n</recipients>\n```\n\nBy default, `recipientType` is `to`.\n\nAs always, read the\n[schema for all the details](https://www.liferay.com/dtd/liferay-workflow-definition_7_1_0.xsd).\n\n"
  },
  {
    "path": "en/developer/reference/articles-dxp/02-classes-moved/01-classes-moved-from-portal-service-jar-intro.markdown",
    "content": "# Classes Moved From portal-service.jar\n\n\nTo leverage the benefits of modularization, many classes from <em>portal-service.jar</em>\nhave been moved into application and framework API modules. The table below\nprovides details about these classes and the modules they've moved to. Package\nchanges and each module's group, artifact ID, and version are listed, to\nfacilitate \n<a href=\"/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies\">configuring dependencies</a>. \n\n<style>\ntable, th, td {\n    border: 1px solid black;\n    border-collapse: collapse;\n}\nth, td {\n    padding: 5px;\n    text-align: left;\n}\ncaption {\n    text-align: left;\n}\n</style>\n<table style=\"width:100%\">\n\n  <caption>\n    <b>Classes Moved from portal-service.jar to Modules</b>\n\t<p>\n      This information was generated based on comparing classes in\n      <em>liferay-portal-src-6.2-ee-sp20</em> to classes in <em>liferay-dxp-src-7.2.10-ga1</em>.\n    </p>\n  </caption>\n  <tr>\n    <th>Class</th>\n    <th>Package</th>\n    <th>Group ID, Artifact ID,<br>\n        and Version</th>\n  </tr>\n\n  <tr>\n    <td>ActionHandler</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup.action<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.action\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>ActionHandlerManager</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.action\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>ActionHandlerManagerUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.action\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>ActionTypeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>AlternateKeywordQueryHitsProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search<br>\n\t  <em>New:</em> com.liferay.portal.search.internal.hits\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.search<br>\n        6.0.14\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleContentException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleContentSizeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleCreateDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleDisplayDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleDisplayDateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleExpirationDateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleIDComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleIdException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleModifiedDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleResourcePKComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleReviewDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleReviewDateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleSmallImageNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleSmallImageSizeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleTitleComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleTitleException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleVersionComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util.comparator<br>\n\t  <em>New:</em> com.liferay.journal.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ArticleVersionException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>AuditMessageProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.audit<br>\n\t  <em>New:</em> com.liferay.portal.security.audit\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.audit.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>AutoDeleteFileInputStream</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.io<br>\n\t  <em>New:</em> com.liferay.petra.io\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.io<br>\n        3.0.2\n    </td>\n  </tr>\n  <tr>\n    <td>AverageStatistics</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.monitoring.statistics<br>\n\t  <em>New:</em> com.liferay.portal.monitoring.internal.statistics\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.monitoring<br>\n        7.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.portal.background.task.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service.persistence<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.portal.background.task.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service.persistence<br>\n\t  <em>New:</em> com.liferay.portal.background.task.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BackgroundTaskWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.portal.background.task.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.background.task.api<br>\n        4.1.3\n    </td>\n  </tr>\n  <tr>\n    <td>BannedUserException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>BaseCmisRepository</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>BaseCmisSearchQueryBuilder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>BaseDDLExporter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.internal.exporter\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.service<br>\n        3.0.12\n    </td>\n  </tr>\n  <tr>\n    <td>BaseDDMDisplay</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>BaseFieldRenderer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>BaseScriptingExecutor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.scripting<br>\n\t  <em>New:</em> com.liferay.portal.scripting\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.scripting.api<br>\n        3.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>BaseStatistics</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.monitoring.statistics<br>\n\t  <em>New:</em> com.liferay.portal.monitoring.internal.statistics\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.monitoring<br>\n        7.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>BaseStorageAdapter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>BlockingPortalCache</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache<br>\n\t  <em>New:</em> com.liferay.portal.cache\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.api<br>\n        2.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntry</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service.persistence<br>\n\t  <em>New:</em> com.liferay.blogs.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service.persistence<br>\n\t  <em>New:</em> com.liferay.blogs.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntrySoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service.persistence<br>\n\t  <em>New:</em> com.liferay.blogs.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsEntryWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUser</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service.persistence<br>\n\t  <em>New:</em> com.liferay.blogs.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service<br>\n\t  <em>New:</em> com.liferay.blogs.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service.persistence<br>\n\t  <em>New:</em> com.liferay.blogs.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.service.persistence<br>\n\t  <em>New:</em> com.liferay.blogs.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BlogsStatsUserWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.model<br>\n\t  <em>New:</em> com.liferay.blogs.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntry</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service.persistence<br>\n\t  <em>New:</em> com.liferay.bookmarks.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service.persistence<br>\n\t  <em>New:</em> com.liferay.bookmarks.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntrySoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service.persistence<br>\n\t  <em>New:</em> com.liferay.bookmarks.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksEntryWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service.persistence<br>\n\t  <em>New:</em> com.liferay.bookmarks.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service.persistence<br>\n\t  <em>New:</em> com.liferay.bookmarks.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service<br>\n\t  <em>New:</em> com.liferay.bookmarks.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.service.persistence<br>\n\t  <em>New:</em> com.liferay.bookmarks.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>BookmarksFolderWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.model<br>\n\t  <em>New:</em> com.liferay.bookmarks.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>ByteArrayReportResultContainer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>CMISBetweenExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISConjunction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISContainsExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISContainsNotExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISContainsValueExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISCriterion</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISDisjunction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISFullTextConjunction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISInFolderExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISInTreeExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISJunction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISNotExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISParameterValueUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISRepositoryHandler</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISRepositoryUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.impl<br>\n        4.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>CMISSearchQueryBuilder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISSimpleExpression</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CMISSimpleExpressionOperator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.repository.cmis.search<br>\n\t  <em>New:</em> com.liferay.document.library.repository.cmis.search\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.repository.cmis.api<br>\n        3.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>CharPool</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.util<br>\n\t  <em>New:</em> com.liferay.petra.string\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.string<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>CharsetDecoderUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.nio.charset<br>\n\t  <em>New:</em> com.liferay.petra.nio\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.nio<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>CharsetEncoderUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.nio.charset<br>\n\t  <em>New:</em> com.liferay.petra.nio\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.nio<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ClassLoaderPool</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.util<br>\n\t  <em>New:</em> com.liferay.petra.lang\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.lang<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ClassPathUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.process<br>\n\t  <em>New:</em> com.liferay.petra.process\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.process<br>\n        3.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>ClassResolverUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.util<br>\n\t  <em>New:</em> com.liferay.petra.lang\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.lang<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>CollatedSpellCheckHitsProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search<br>\n\t  <em>New:</em> com.liferay.portal.search.internal.hits\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.search<br>\n        6.0.14\n    </td>\n  </tr>\n  <tr>\n    <td>CompoundSessionIdServletRequest</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.servlet.filters.compoundsessionid<br>\n\t  <em>New:</em> com.liferay.portal.compound.session.id.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.compound.session.id<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>Condition</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage.query<br>\n\t  <em>New:</em> com.liferay.adaptive.media.image.media.query\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.adaptive.media.image.api<br>\n        3.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>ConsumerOutputProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.process<br>\n\t  <em>New:</em> com.liferay.petra.process\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.process<br>\n        3.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>ContactConverterKeys</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>ContentException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ContentNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ContextClassloaderReportDesignRetriever</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>CountStatistics</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.monitoring.statistics<br>\n\t  <em>New:</em> com.liferay.portal.monitoring.internal.statistics\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.monitoring<br>\n        7.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>DDL</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLExporter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exporter\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLExporterFactory</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exporter\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecord</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSet</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSetWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersion</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersionModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersionPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersionSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersionUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersionVersionComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.util.comparator<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordVersionWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDLRecordWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DDM</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContent</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMContentWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMDisplay</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMDisplayRegistry</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMIndexer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMIndexerUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.asset.list.internal.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.asset.list.service<br>\n        1.0.11\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLink</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStorageLinkWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLinkWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructurePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMStructureWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateHelper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplatePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.service.persistence<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMTemplateWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.model<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DDMXML</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DLContent</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.content.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentDataBlobModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.content.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.content.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.content.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.content.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.content.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.content.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.content.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.content.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentVersionComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.util.comparator<br>\n\t  <em>New:</em> com.liferay.document.library.content.service.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.service<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLContentWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.content.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.content.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRank</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLFileRankWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.sync.constants\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEvent</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.sync.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.sync.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.sync.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service<br>\n\t  <em>New:</em> com.liferay.document.library.sync.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.sync.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.sync.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.sync.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.service.persistence<br>\n\t  <em>New:</em> com.liferay.document.library.sync.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>DLSyncEventWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.model<br>\n\t  <em>New:</em> com.liferay.document.library.sync.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.sync.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>Database</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.util<br>\n\t  <em>New:</em> com.liferay.portal.tools.db.upgrade.client\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.tools.db.upgrade.client<br>\n        3.0.0\n    </td>\n  </tr>\n  <tr>\n    <td>DefaultAttributesTransformer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.impl<br>\n        2.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DefaultMessageBus</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DefaultSingleDestinationMessageSender</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.sender<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.sender\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DefaultSingleDestinationSynchronousMessageSender</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.sender<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.sender\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DefaultSynchronousMessageSender</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.sender<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.sender\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DeleteFileFinalizeAction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>DestinationStatisticsManager</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.jmx<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.jmx\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DestinationStatisticsManagerMBean</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.jmx<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.jmx\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>DirectSynchronousMessageSender</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.sender<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.sender\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>Dummy</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.exportimport.test.util.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.exportimport.test.util<br>\n        2.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>DummyContext</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.dummy\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>DummyDirContext</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.dummy\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>DummyFinalizeAction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicateArticleIdException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicateFeedIdException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicateLDAPServerNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicateNodeNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicatePageException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicateRuleGroupInstanceException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>DuplicateVoteException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>EntryDisplayDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.util.comparator<br>\n\t  <em>New:</em> com.liferay.blogs.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EntryModifiedDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.util.comparator<br>\n\t  <em>New:</em> com.liferay.bookmarks.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EntryNameComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.util.comparator<br>\n\t  <em>New:</em> com.liferay.bookmarks.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EntryPriorityComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.util.comparator<br>\n\t  <em>New:</em> com.liferay.bookmarks.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EntrySmallImageNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs<br>\n\t  <em>New:</em> com.liferay.blogs.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EntryURLComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.util.comparator<br>\n\t  <em>New:</em> com.liferay.bookmarks.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EntryVisitsComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.bookmarks.util.comparator<br>\n\t  <em>New:</em> com.liferay.bookmarks.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.bookmarks.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>EqualityWeakReference</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>Fact</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>FeedContentFieldException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FeedIdException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FeedNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FeedTargetLayoutFriendlyUrlException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FeedTargetPortletIdException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FieldConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FieldRenderer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FieldRendererFactory</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>Fields</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>FileRankCreateDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary.util.comparator<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.service<br>\n        2.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>FinalizeAction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>FinalizeManager</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>FlagsEntryService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.flags.service<br>\n\t  <em>New:</em> com.liferay.flags.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.flags.api<br>\n        4.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>FlagsEntryServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.flags.service<br>\n\t  <em>New:</em> com.liferay.flags.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.flags.api<br>\n        4.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>FlagsEntryServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.flags.service<br>\n\t  <em>New:</em> com.liferay.flags.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.flags.api<br>\n        4.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>FlagsRequest</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.flags.messaging<br>\n\t  <em>New:</em> com.liferay.flags.internal.messaging\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.flags.service<br>\n        4.0.2\n    </td>\n  </tr>\n  <tr>\n    <td>GroupConverterKeys</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>ImportFilesException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticle</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleDisplay</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticlePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResource</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourcePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleResourceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalArticleWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContent</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journalcontent.util<br>\n\t  <em>New:</em> com.liferay.journal.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearch</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalContentSearchWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalConverter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.util<br>\n\t  <em>New:</em> com.liferay.journal.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeed</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFeedWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service<br>\n\t  <em>New:</em> com.liferay.journal.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.service.persistence<br>\n\t  <em>New:</em> com.liferay.journal.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalFolderWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalSearchConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>JournalStructureConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal.model<br>\n\t  <em>New:</em> com.liferay.journal.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>LDAPFilterException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.validator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>LDAPGroup</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.exportimport\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>LDAPServerNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>LDAPToPortalConverter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.exportimport\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>LDAPUser</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.exportimport\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>LDAPUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>LockLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.lock.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.lock.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service<br>\n\t  <em>New:</em> com.liferay.portal.lock.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.portal.lock.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service.persistence<br>\n\t  <em>New:</em> com.liferay.portal.lock.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.portal.lock.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.service.persistence<br>\n\t  <em>New:</em> com.liferay.portal.lock.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.model<br>\n\t  <em>New:</em> com.liferay.portal.lock.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.lock.api<br>\n        4.1.1\n    </td>\n  </tr>\n  <tr>\n    <td>LockedThreadException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>LoggingOutputProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.process<br>\n\t  <em>New:</em> com.liferay.petra.process\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.process<br>\n        3.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBan</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBBanWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategory</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.constants\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryDisplay</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.web.internal.display\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.web<br>\n        3.0.17\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategorySoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBCategoryWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussion</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBDiscussionWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingList</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMailingListWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessage</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.constants\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageDisplay</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessagePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBMessageWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUser</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBStatsUserWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThread</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.constants\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlag</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadFlagWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service<br>\n\t  <em>New:</em> com.liferay.message.boards.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.service.persistence<br>\n\t  <em>New:</em> com.liferay.message.boards.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBThreadWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBTreeWalker</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.model<br>\n\t  <em>New:</em> com.liferay.message.boards.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MBeanRegistry</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.jmx<br>\n\t  <em>New:</em> com.liferay.portal.jmx\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.jmx.api<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>MDRAction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRActionWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRPermission</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.permission<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.web.internal.security.permission.resource\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.web<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRule</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroup</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstancePermission</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.permission<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.web.internal.security.permission.resource\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.web<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstancePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupInstanceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupPermission</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.permission<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.web.internal.security.permission.resource\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.web<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleGroupWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRulePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.service.persistence<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MDRRuleWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.model<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>MailingListEmailAddressException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MailingListInServerNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MailingListInUserNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MailingListOutEmailAddressException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MailingListOutServerNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MailingListOutUserNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MemoryReportDesignRetriever</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>MessageBodyException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MessageBusManager</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.jmx<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.jmx\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>MessageBusManagerMBean</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.jmx<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.jmx\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>MessageCreateDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.util.comparator<br>\n\t  <em>New:</em> com.liferay.message.boards.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MessageSubjectException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>MessageThreadComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.util.comparator<br>\n\t  <em>New:</em> com.liferay.message.boards.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>Modifications</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.exportimport\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchArticleException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchArticleImageException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchArticleResourceException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchBanException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchChoiceException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchContentException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchContentSearchException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchDiscussionException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchFeedException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.journal.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.journal.api<br>\n        4.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchFileRankException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.documentlibrary<br>\n\t  <em>New:</em> com.liferay.document.library.file.rank.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.document.library.file.rank.api<br>\n        2.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchMailingListException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchNodeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchPageException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchPageResourceException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchQuestionException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchRecordException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchRecordSetException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchRecordVersionException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchRuleException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchRuleGroupException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchRuleGroupInstanceException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchStatsUserException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs<br>\n\t  <em>New:</em> com.liferay.blogs.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchStorageLinkException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchStructureLinkException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchTemplateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchThreadException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchThreadFlagException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>NoSuchVoteException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>NodeNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>OutputProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.process<br>\n\t  <em>New:</em> com.liferay.petra.process\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.process<br>\n        3.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>PageContentException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>PageCreateDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.util.comparator<br>\n\t  <em>New:</em> com.liferay.wiki.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>PageTitleComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.util.comparator<br>\n\t  <em>New:</em> com.liferay.wiki.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>PageTitleException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>PageVersionComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.util.comparator<br>\n\t  <em>New:</em> com.liferay.wiki.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>PageVersionException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoice</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoicePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service.persistence<br>\n\t  <em>New:</em> com.liferay.polls.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service.persistence<br>\n\t  <em>New:</em> com.liferay.polls.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsChoiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestion</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionPersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service.persistence<br>\n\t  <em>New:</em> com.liferay.polls.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service.persistence<br>\n\t  <em>New:</em> com.liferay.polls.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsQuestionWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVote</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVotePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service.persistence<br>\n\t  <em>New:</em> com.liferay.polls.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service<br>\n\t  <em>New:</em> com.liferay.polls.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.service.persistence<br>\n\t  <em>New:</em> com.liferay.polls.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PollsVoteWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls.model<br>\n\t  <em>New:</em> com.liferay.polls.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>PoolAction</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterChannel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal.cluster.link\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterChannelFactory</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal.cluster.link\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterChannelSelector</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal.cluster.link\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterEvent</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterEventCoalesceComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterEventType</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalCacheClusterLink</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal.cluster.link\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortalExecutorFactory</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.executor<br>\n\t  <em>New:</em> com.liferay.portal.executor.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.executor<br>\n        4.0.2\n    </td>\n  </tr>\n  <tr>\n    <td>PortalToLDAPConverter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap.exportimport\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>PortletDisplayTemplate</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.portletdisplaytemplate.util<br>\n\t  <em>New:</em> com.liferay.portlet.display.template\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portlet.display.template.api<br>\n        2.0.2\n    </td>\n  </tr>\n  <tr>\n    <td>PortletDisplayTemplateConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.portletdisplaytemplate.util<br>\n\t  <em>New:</em> com.liferay.portlet.display.template.constants\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portlet.display.template.api<br>\n        2.0.2\n    </td>\n  </tr>\n  <tr>\n    <td>PortletDisplayTemplateUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.portletdisplaytemplate.util<br>\n\t  <em>New:</em> com.liferay.roles.admin.web.internal.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.roles.admin.web<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortletDisplayTemplateUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.portletdisplaytemplate.util<br>\n\t  <em>New:</em> com.liferay.roles.admin.web.internal.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.roles.admin.web<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>PortletDisplayTemplateUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.portletdisplaytemplate.util<br>\n\t  <em>New:</em> com.liferay.roles.admin.web.internal.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.roles.admin.web<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>ProcessUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.process<br>\n\t  <em>New:</em> com.liferay.petra.process\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.process<br>\n        3.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>QueryIndexingHitsProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search<br>\n\t  <em>New:</em> com.liferay.portal.search.internal.hits\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.search<br>\n        6.0.14\n    </td>\n  </tr>\n  <tr>\n    <td>QuerySuggestionHitsProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search<br>\n\t  <em>New:</em> com.liferay.portal.search.internal.hits\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.search<br>\n        6.0.14\n    </td>\n  </tr>\n  <tr>\n    <td>QueryType</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>QuestionChoiceException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>QuestionDescriptionException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>QuestionExpirationDateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>QuestionExpiredException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>QuestionTitleException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.polls<br>\n\t  <em>New:</em> com.liferay.polls.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.polls.api<br>\n        6.0.3\n    </td>\n  </tr>\n  <tr>\n    <td>RecordSetDDMStructureIdException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>RecordSetDuplicateRecordSetKeyException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>RecordSetNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatalists<br>\n\t  <em>New:</em> com.liferay.dynamic.data.lists.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.lists.api<br>\n        4.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>RegistryAwareMBeanServer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.jmx<br>\n\t  <em>New:</em> com.liferay.portal.jmx.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.jmx<br>\n        6.0.2\n    </td>\n  </tr>\n  <tr>\n    <td>ReportCompilerRequestMessageListener</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting.messaging<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine.messaging\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportDataSourceType</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportDesignRetriever</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportEngine</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportExportException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportFormat</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportFormatExporter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportFormatExporterRegistry</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportGenerationException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportRequest</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportRequestContext</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportRequestMessageListener</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting.messaging<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine.messaging\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>ReportResultContainer</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>RequestStatistics</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.monitoring.statistics<br>\n\t  <em>New:</em> com.liferay.portal.monitoring.internal.statistics\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.monitoring<br>\n        7.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>RequiredMessageException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>RequiredNodeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>RequiredTemplateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>RequiredTemplateException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>RuleGroupInstancePriorityComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.mobiledevicerules.util<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RuleGroupProcessor</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.rule\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RuleGroupProcessorUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.rule\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RuleHandler</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup.rule<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.rule\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RulesEngine</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RulesEngineException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RulesEngineUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RulesLanguage</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>RulesResourceRetriever</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.rules<br>\n\t  <em>New:</em> com.liferay.portal.rules.engine\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.rules.engine.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>SearchUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search.util<br>\n\t  <em>New:</em> com.liferay.portal.vulcan.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.vulcan.api<br>\n        3.2.2\n    </td>\n  </tr>\n  <tr>\n    <td>SearchUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search.util<br>\n\t  <em>New:</em> com.liferay.portal.vulcan.util\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.vulcan.api<br>\n        3.2.2\n    </td>\n  </tr>\n  <tr>\n    <td>ServletContextReportDesignRetriever</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.bi.reporting.servlet<br>\n\t  <em>New:</em> com.liferay.portal.reports.engine.servlet\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.reports.engine.api<br>\n        5.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>SoftReferencePool</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.memory<br>\n\t  <em>New:</em> com.liferay.petra.memory\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.petra.memory<br>\n        3.0.1\n    </td>\n  </tr>\n  <tr>\n    <td>SortFactoryImpl</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search<br>\n\t  <em>New:</em> com.liferay.portal.search.internal\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.search<br>\n        6.0.14\n    </td>\n  </tr>\n  <tr>\n    <td>SplitThreadException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards<br>\n\t  <em>New:</em> com.liferay.message.boards.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>Statistics</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.monitoring.statistics<br>\n\t  <em>New:</em> com.liferay.portal.monitoring.internal.statistics\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.monitoring<br>\n        7.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>StatsUserLastPostDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.blogs.util.comparator<br>\n\t  <em>New:</em> com.liferay.blogs.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.blogs.api<br>\n        5.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>StorageAdapter</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StorageEngine</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.storage<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.storage\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StorageException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StorageFieldNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StringQueryImpl</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.search<br>\n\t  <em>New:</em> com.liferay.portal.search.internal.query\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.search<br>\n        6.0.14\n    </td>\n  </tr>\n  <tr>\n    <td>StructureDuplicateStructureKeyException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StructureFieldException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StructureIdComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util.comparator<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StructureModifiedDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util.comparator<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>StructureStructureKeyComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util.comparator<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>SummaryStatistics</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.monitoring.statistics<br>\n\t  <em>New:</em> com.liferay.portal.monitoring.internal.statistics\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.monitoring<br>\n        7.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>SynchronousMessageListener</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.messaging.sender<br>\n\t  <em>New:</em> com.liferay.portal.messaging.internal.sender\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.messaging<br>\n        6.0.5\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateDuplicateTemplateKeyException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateIdComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util.comparator<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateModifiedDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping.util.comparator<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateScriptException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateSmallImageNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateSmallImageNameException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateSmallImageSizeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.dynamicdatamapping<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>TemplateSmallImageSizeException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.journal<br>\n\t  <em>New:</em> com.liferay.dynamic.data.mapping.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.dynamic.data.mapping.api<br>\n        5.2.1\n    </td>\n  </tr>\n  <tr>\n    <td>ThreadLastPostDateComparator</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.messageboards.util.comparator<br>\n\t  <em>New:</em> com.liferay.message.boards.util.comparator\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.message.boards.api<br>\n        5.1.4\n    </td>\n  </tr>\n  <tr>\n    <td>UniformPortalCacheClusterChannelSelector</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.cache.cluster<br>\n\t  <em>New:</em> com.liferay.portal.cache.multiple.internal.cluster.link\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.cache.multiple<br>\n        3.0.6\n    </td>\n  </tr>\n  <tr>\n    <td>UnknownRuleHandlerException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.kernel.mobile.device.rulegroup.rule<br>\n\t  <em>New:</em> com.liferay.mobile.device.rules.rule\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.mobile.device.rules.api<br>\n        4.0.4\n    </td>\n  </tr>\n  <tr>\n    <td>UserConverterKeys</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portal.security.ldap<br>\n\t  <em>New:</em> com.liferay.portal.security.ldap\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.portal.security.ldap.api<br>\n        2.0.8\n    </td>\n  </tr>\n  <tr>\n    <td>WikiFormatException</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki<br>\n\t  <em>New:</em> com.liferay.wiki.exception\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNode</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiNodeWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPage</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageConstants</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageDisplay</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageFinder</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPagePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResource</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceLocalService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceLocalServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceLocalServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceModel</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourcePersistence</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageResourceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageService</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageServiceUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageServiceWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service<br>\n\t  <em>New:</em> com.liferay.wiki.service\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageSoap</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageUtil</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.service.persistence<br>\n\t  <em>New:</em> com.liferay.wiki.service.persistence\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n  <tr>\n    <td>WikiPageWrapper</td>\n    <td>\n\t  <em>Old:</em> com.liferay.portlet.wiki.model<br>\n\t  <em>New:</em> com.liferay.wiki.model\n\t</td>\n    <td>com.liferay<br>\n        com.liferay.wiki.api<br>\n        4.0.7\n    </td>\n  </tr>\n\n</table>\n"
  },
  {
    "path": "en/developer/reference/articles-dxp/02-tooling/dev-studio/02-installing-dev-studio.markdown",
    "content": "---\nheader-id: installing-liferay-dev-studio\n---\n\n# Installing Liferay Dev Studio\n\n[TOC levels=1-4]\n\nLiferay Dev Studio is a plugin for Eclipse that brings many Liferay-specific\nfeatures to the table. You can install it into your existing Eclipse\nenvironment, or Liferay provides a bundled version included in its Project SDK.\nHere you'll learn the different methods available for installing Dev Studio:\n\n- [install the Dev Studio bundle from scratch](#install-the-dev-studio-bundle)\n- [install Dev Studio into an existing Eclipse instance using an update URL](#install-dev-studio-into-eclipse)\n- [install Dev Studio into an existing Eclipse instance using a ZIP file](#install-dev-studio-into-eclipse-from-a-zip-file)\n\n**Important:** If you're installing Dev Studio into an existing Eclipse\nenvironment, you must be on Eclipse Photon or newer. For instructions on\nupgrading to Photon, see Eclipse's\n[upgrade documentation](https://wiki.eclipse.org/FAQ_How_do_I_upgrade_Eclipse_IDE%3F#Upgrading_existing_Eclipse_IDE_and_Installed_Features_to_newer_release).\nWith this particular upgrade, you should also deactivate the current available\nupdate sites in the *Window* &rarr; *Preferences* &rarr; *Install/Update* &rarr;\n*Available Software Sites* menu to ensure a successful upgrade (e.g., Oxygen).\n\n## Install the Dev Studio Bundle\n\n1.  Download and install [Java](http://java.oracle.com). Liferay runs on Java,\n    so you'll need it to run everything else. Because you'll be developing apps\n    for @product@ in Dev Studio, the Java Development Kit (JDK) is required. It\n    is an enhanced version of the Java Environment used for developing new Java\n    technology. You can download the Java SE JDK from the Java\n    [Downloads](http://www.oracle.com/technetwork/java/javase/downloads/index.html)\n    page. \n\n2.  Download Liferay's latest\n    [Project SDK with Dev Studio](https://sourceforge.net/projects/lportal/files/Liferay%20IDE/).\n    Go to the [Downloads](https://customer.liferay.com/group/customer/downloads)\n    page in Liferay's Help Center. Select *Developer Tools* in the Product\n    drop-down and *Developer Studio* for the file type. Then select the\n    executable that correlates to your operating system.\n\n    The Project SDK\n    includes @ide@,\n    [Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace),\n    and [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli).\n\n3.  Run the Project SDK executable and step through the installer to install\n    everything to your machine. For help with setting up proxy settings (if\n    necessary), see the\n    [Dev Studio Proxy Settings](/docs/7-2/reference/-/knowledge_base/r/setting-proxy-requirements-for-dev-studio)\n    and\n    [Liferay Workspace Proxy Settings](/docs/7-2/reference/-/knowledge_base/r/setting-proxy-requirements-for-liferay-workspace)\n    articles for more information.\n\nCongratulations! You've installed Liferay Dev Studio! It's now available in the\nProject SDK folder's `liferay-developer-studio`. To run Dev Studio, execute the\n`DeveloperStudio` executable. A Liferay Workspace has also been initialized in\nthat same folder.\n\n## Install Dev Studio into Eclipse\n\nIf you already have an Eclipse environment that you're using for other\nthings, it's easy to add Dev Studio to your existing Eclipse installation. \n\n1.  In Eclipse, select *Help* &rarr; *Install New Software*. \n\n2.  In the *Work with* field, copy in the following URL:\n    https://releases.liferay.com/tools/ide/latest/stable/\n\n3.  You'll see the Liferay Dev Studio components in the list below. Check them\n    off and click *Next*. \n\n4.  Accept the terms of the agreements and click *Next*, and Dev Studio is\n    installed. Like other Eclipse plugins you'll have to restart Eclipse to\n    enable it. \n\n## Install Dev Studio into Eclipse from a ZIP File\n\nTo install Liferay Dev Studio into Eclipse from a Zip file, follow these steps: \n\n1.  Go to the\n    [Dev Studio](https://community.liferay.com/en_GB/project/-/asset_publisher/TyF2HQPLV1b5/content/ide-installation-instructions)\n    downloads page. Under *Other Downloads*, select the *Liferay IDE [version]\n    Archived Update-site* option to download it.\n\n2.  In Eclipse, go to *Help* &rarr; *Install New Software...*. \n\n3.  In the *Add* dialog, click the *Archive* button and browse to the location\n    of the downloaded Dev Studio Zip file.\n\n4.  You'll see the Dev Studio components in the list below. Check them off and\n    click *Next*. \n\n5.  Accept the terms of the agreements and click *Next*, and Liferay Dev Studio\n    is installed. Like other Eclipse plugins you'll have to restart Eclipse to\n    enable it.\n\nAwesome! You've installed Liferay Dev Studio. Now you can begin Liferay\ndevelopment using a popular and supported IDE.\n"
  },
  {
    "path": "en/developer/reference/build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"Reference Materials\" basedir=\".\">\n\t<property name=\"project.dir\" value=\"../../../../liferay-docs\" />\n\n\t<import file=\"../build.xml\" />\n\n\t<property name=\"doc.dir\" value=\"reference\"/>\n\t<property name=\"doc.type\" value=\"reference\"/>\n\n</project>\n"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# production\n/build\n\n# misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nbuild.liferay"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/.npmbuildrc",
    "content": "{\n\t\"liferayDir\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761\\\\liferay-ce-portal-7.2.0-ga1\"\n}"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/.npmbundlerrc",
    "content": "{\n\t\"preset\": \"liferay-npm-bundler-preset-create-react-app\"\n}"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/README.md",
    "content": "This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).\n\n## Available Scripts\n\nIn the project directory, you can run:\n\n### `npm start`\n\nRuns the app in the development mode.<br>\nOpen [http://localhost:3000](http://localhost:3000) to view it in the browser.\n\nThe page will reload if you make edits.<br>\nYou will also see any lint errors in the console.\n\n### `npm test`\n\nLaunches the test runner in the interactive watch mode.<br>\nSee the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.\n\n### `npm run build`\n\nBuilds the app for production to the `build` folder.<br>\nIt correctly bundles React in production mode and optimizes the build for the best performance.\n\nThe build is minified and the filenames include the hashes.<br>\nYour app is ready to be deployed!\n\nSee the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.\n\n### `npm run eject`\n\n**Note: this is a one-way operation. Once you `eject`, you can’t go back!**\n\nIf you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.\n\nInstead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.\n\nYou don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.\n\n## Learn More\n\nYou can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).\n\nTo learn React, check out the [React documentation](https://reactjs.org/).\n\n### Code Splitting\n\nThis section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting\n\n### Analyzing the Bundle Size\n\nThis section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size\n\n### Making a Progressive Web App\n\nThis section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app\n\n### Advanced Configuration\n\nThis section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration\n\n### Deployment\n\nThis section has moved here: https://facebook.github.io/create-react-app/docs/deployment\n\n### `npm run build` fails to minify\n\nThis section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify\n"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/features/localization/Language.properties",
    "content": "javax.portlet.title.myreactguestbookapp=my-react-guestbook-app\n"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/package.json",
    "content": "{\n  \"name\": \"my-react-guestbook-app\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"dependencies\": {\n    \"react\": \"^16.8.1\",\n    \"react-dom\": \"^16.8.1\",\n    \"react-router-dom\": \"^4.3.1\",\n    \"react-scripts\": \"2.1.5\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test\",\n    \"eject\": \"react-scripts eject\",\n    \"build:liferay\": \"lnbs-build\",\n    \"deploy:liferay\": \"npm run build:liferay && lnbs-deploy\"\n  },\n  \"eslintConfig\": {\n    \"extends\": \"react-app\"\n  },\n  \"browserslist\": [\n    \">0.2%\",\n    \"not dead\",\n    \"not ie <= 11\",\n    \"not op_mini all\"\n  ],\n  \"devDependencies\": {\n    \"liferay-npm-build-support\": \"^2.13.2\",\n    \"liferay-npm-bundler\": \"^2.13.2\",\n    \"liferay-npm-bundler-preset-create-react-app\": \"^2.13.2\"\n  },\n  \"portlet\": {\n    \"com.liferay.portlet.display-category\": \"category.sample\",\n    \"com.liferay.portlet.instanceable\": true,\n    \"javax.portlet.name\": \"myreactguestbookapp\",\n    \"javax.portlet.security-role-ref\": \"power-user,user\",\n    \"javax.portlet.resource-bundle\": \"content.Language\"\n  }\n}\n"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <link rel=\"shortcut icon\" href=\"%PUBLIC_URL%/favicon.ico\" />\n    <!-- Bootstrap CSS -->\n    <link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css\" integrity=\"sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO\" crossorigin=\"anonymous\">\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"\n    />\n    <meta name=\"theme-color\" content=\"#000000\" />\n    <!--\n      manifest.json provides metadata used when your web app is installed on a\n      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/\n    -->\n    <link rel=\"manifest\" href=\"%PUBLIC_URL%/manifest.json\" />\n    <!--\n      Notice the use of %PUBLIC_URL% in the tags above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n    <title>React App</title>\n  </head>\n  <body>\n    <noscript>You need to enable JavaScript to run this app.</noscript>\n    <div id=\"root\"></div>\n    <!--\n      This HTML file is a template.\n      If you open it directly in the browser, you will see an empty page.\n\n      You can add webfonts, meta tags, or analytics to this file.\n      The build step will place the bundled scripts into the <body> tag.\n\n      To begin the development, run `npm start` or `yarn start`.\n      To create a production bundle, use `npm run build` or `yarn build`.\n    -->\n  </body>\n</html>\n"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/public/manifest.json",
    "content": "{\n  \"short_name\": \"React App\",\n  \"name\": \"Create React App Sample\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\",\n      \"sizes\": \"64x64 32x32 24x24 16x16\",\n      \"type\": \"image/x-icon\"\n    }\n  ],\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"theme_color\": \"#000000\",\n  \"background_color\": \"#ffffff\"\n}\n"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/src/App.css",
    "content": ".App {\n  text-align: center;\n}\n\n.App-logo {\n  animation: App-logo-spin infinite 20s linear;\n  height: 40vmin;\n  pointer-events: none;\n}\n\n.App-header {\n  background-color: #282c34;\n  min-height: 100vh;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-size: calc(10px + 2vmin);\n  color: white;\n}\n\n.App-link {\n  color: #61dafb;\n}\n\n@keyframes App-logo-spin {\n  from {\n    transform: rotate(0deg);\n  }\n  to {\n    transform: rotate(360deg);\n  }\n}\n"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/src/App.js",
    "content": "import React from 'react';\nimport { Route, Switch, Redirect } from 'react-router-dom';\nimport EntryForm from './add-entry';\nimport Guestbook from './view-guestbook';\n\nclass App extends React.Component {\n  constructor(props) {\n    super(props);\n    this.addEntry = this.addEntry.bind(this);\n    this.resetRedirect = this.resetRedirect.bind(this);\n    this.state = {\n      entries: [\n        { id: 1, name: 'Joe Bloggs', message: 'Had an awesome Time!' },\n        { id: 2, name: 'Jane Bloggs', message: 'Great event!' },\n        { id: 3, name: 'Bill Bloggs', message: 'Had a good time.' },\n        { id: 4, name: 'Bob Nosester', message: 'Great atmosphere!' },\n        { id: 5, name: 'Martha Nosester', message: 'Lovely aromas.' }\n      ],\n      redirect: false\n    };\n  }\n\n  addEntry(name, message){\n    \n    const newEntry = {id: this.state.entries.length + 1, name: name, message: message};\n    \n    if(name !== '' && message !== ''){\n      this.setState({\n        entries: this.state.entries.concat(newEntry),\n        redirect: true\n      });\n    }\n  }\n  \n  resetRedirect(){\n    this.setState({\n      redirect: false\n    });\n  }\n  \n  render(){\n    return (\n      <Switch>\n        <Route exact path=\"/\" render={() => (<Redirect to=\"/view-guestbook\"/>)}/>\n        <Route path=\"/view-guestbook\" render={() => < Guestbook entries={this.state.entries} resetRedirect={this.resetRedirect}/>}/>\n        <Route path=\"/add-entry\" render={() => this.state.redirect ? (< Redirect to=\"/view-guestbook\"/>) : (< EntryForm addMethod={this.addEntry}/>)}/>\n      </Switch>\n    )\n  }\n}\n\nexport default App;\n\n/*\nimport React, { Component } from 'react';\nimport { Route, Switch, Redirect } from 'react-router-dom';\nimport EntryForm from './add-entry';\nimport Guestbook from './view-guestbook';\n\nclass App extends Component {\n  \n  state = {\n    entries: [\n      { id: 1, name: 'Joe Bloggs', message: 'Had an awesome Time!' },\n      { id: 2, name: 'Jane Bloggs', message: 'Great event!' },\n      { id: 3, name: 'Bill Bloggs', message: 'Had a good time.' },\n      { id: 4, name: 'Bob Nosester', message: 'Great atmosphere!' },\n      { id: 5, name: 'Martha Nosester', message: 'Lovely aromas.' }\n    ],\n    redirect: false\n  };\n  \n  addEntry = (name, message) => {\n    const {entries} = this.state;\n    \n    const newEntry = {id: this.state.entries.length + 1, name: name, message: message};\n    \n    if(name !== '' && message !== ''){\n      this.setState({\n        entries: entries.concat(newEntry),\n        redirect: true\n      });\n    }\n  }\n  \n  resetRedirect = () => {\n    this.setState({\n      redirect: false\n    });\n  }\n  \n  render() {\n    return (\n      <Switch>\n        <Route exact path=\"/\" render={() => (<Redirect to=\"/view-guestbook\"/>)}/>\n        <Route path=\"/view-guestbook\" render={() => < Guestbook entries={this.state.entries} resetRedirect={this.resetRedirect}/>}/>\n        <Route path=\"/add-entry\" render={() => this.state.redirect ? (< Redirect to=\"/view-guestbook\"/>) : (< EntryForm addMethod={this.addEntry}/>)}/>\n      </Switch>\n    )\n  }\n}\n\nexport default App;\n*/"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/src/App.test.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\n\nit('renders without crashing', () => {\n  const div = document.createElement('div');\n  ReactDOM.render(<App />, div);\n  ReactDOM.unmountComponentAtNode(div);\n});\n"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/src/add-entry.js",
    "content": "import React from 'react';\nimport { Link } from 'react-router-dom';\n\nclass EntryForm extends React.Component {\n  constructor(props) {\n    super(props);\n    this.state = {\n      name: '',\n      message: ''\n    };\n  }\n\n  handleChange(event) {\n    this.setState({[event.target.name]: event.target.value});\n  }\n  \n  handleClick(event) {\n    if(this.state.name !== '' && this.state.message !== ''){\n      this.props.addMethod(this.state.name, this.state.message);\n    }\n    else {\n      alert('you must fill in all fields');\n    }\n  }\n  \n  render() {\n\n      return (\n        <form>\n          <div className=\"form-group\">\n            <label htmlFor=\"addEntryNameInput\">Name</label>\n            <input className=\"form-control\" id=\"addEntryNameInput\" value={this.state.name} onChange={this.handleChange.bind(this)} name=\"name\" aria-label=\"name\" />\n          </div>\n          <div className=\"form-group mb-4\">\n            <label htmlFor=\"addEntryMessageInput\">Message</label>\n            <input className=\"form-control\" id=\"addEntryMessageInput\" value={this.state.message} onChange={this.handleChange.bind(this)} name=\"message\" aria-label=\"message\" />\n          </div>\n          <button type=\"button\" className=\"btn btn-primary mr-2\" onClick={this.handleClick.bind(this)}>Save</button>\n          <Link to=\"/view-guestbook\" className=\"btn btn-outline-secondary\">Cancel</Link>\n        </form>\n      );\n  }\n}\n\nexport default EntryForm;"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/src/index.css",
    "content": "body {\n  margin: 0;\n  padding: 0;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n    \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n    sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n  font-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\",\n    monospace;\n}\n"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/src/index.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\nimport { HashRouter as Router } from 'react-router-dom';\n\nReactDOM.render((\n  <div className=\"mx-auto\">\n    <div className=\"ml-3 mr-3 mt-3\">\n      <Router>\n        <App/>\n      </Router>\n    </div>\n  </div>\n), document.getElementById('root'));\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: http://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/src/serviceWorker.js",
    "content": "// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read http://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n  window.location.hostname === 'localhost' ||\n    // [::1] is the IPv6 localhost address.\n    window.location.hostname === '[::1]' ||\n    // 127.0.0.1/8 is considered localhost for IPv4.\n    window.location.hostname.match(\n      /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n    )\n);\n\nexport function register(config) {\n  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n    // The URL constructor is available in all browsers that support SW.\n    const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n    if (publicUrl.origin !== window.location.origin) {\n      // Our service worker won't work if PUBLIC_URL is on a different origin\n      // from what our page is served on. This might happen if a CDN is used to\n      // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n      return;\n    }\n\n    window.addEventListener('load', () => {\n      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n      if (isLocalhost) {\n        // This is running on localhost. Let's check if a service worker still exists or not.\n        checkValidServiceWorker(swUrl, config);\n\n        // Add some additional logging to localhost, pointing developers to the\n        // service worker/PWA documentation.\n        navigator.serviceWorker.ready.then(() => {\n          console.log(\n            'This web app is being served cache-first by a service ' +\n              'worker. To learn more, visit http://bit.ly/CRA-PWA'\n          );\n        });\n      } else {\n        // Is not localhost. Just register service worker\n        registerValidSW(swUrl, config);\n      }\n    });\n  }\n}\n\nfunction registerValidSW(swUrl, config) {\n  navigator.serviceWorker\n    .register(swUrl)\n    .then(registration => {\n      registration.onupdatefound = () => {\n        const installingWorker = registration.installing;\n        if (installingWorker == null) {\n          return;\n        }\n        installingWorker.onstatechange = () => {\n          if (installingWorker.state === 'installed') {\n            if (navigator.serviceWorker.controller) {\n              // At this point, the updated precached content has been fetched,\n              // but the previous service worker will still serve the older\n              // content until all client tabs are closed.\n              console.log(\n                'New content is available and will be used when all ' +\n                  'tabs for this page are closed. See http://bit.ly/CRA-PWA.'\n              );\n\n              // Execute callback\n              if (config && config.onUpdate) {\n                config.onUpdate(registration);\n              }\n            } else {\n              // At this point, everything has been precached.\n              // It's the perfect time to display a\n              // \"Content is cached for offline use.\" message.\n              console.log('Content is cached for offline use.');\n\n              // Execute callback\n              if (config && config.onSuccess) {\n                config.onSuccess(registration);\n              }\n            }\n          }\n        };\n      };\n    })\n    .catch(error => {\n      console.error('Error during service worker registration:', error);\n    });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n  // Check if the service worker can be found. If it can't reload the page.\n  fetch(swUrl)\n    .then(response => {\n      // Ensure service worker exists, and that we really are getting a JS file.\n      const contentType = response.headers.get('content-type');\n      if (\n        response.status === 404 ||\n        (contentType != null && contentType.indexOf('javascript') === -1)\n      ) {\n        // No service worker found. Probably a different app. Reload the page.\n        navigator.serviceWorker.ready.then(registration => {\n          registration.unregister().then(() => {\n            window.location.reload();\n          });\n        });\n      } else {\n        // Service worker found. Proceed as normal.\n        registerValidSW(swUrl, config);\n      }\n    })\n    .catch(() => {\n      console.log(\n        'No internet connection found. App is running in offline mode.'\n      );\n    });\n}\n\nexport function unregister() {\n  if ('serviceWorker' in navigator) {\n    navigator.serviceWorker.ready.then(registration => {\n      registration.unregister();\n    });\n  }\n}\n"
  },
  {
    "path": "en/developer/reference/code/adapted-react-app/my-react-guestbook-app/src/view-guestbook.js",
    "content": "import React from 'react';\nimport { Link } from 'react-router-dom';\n\nclass Guestbook extends React.Component {\n\n  render() {\n    \n    return (\n      <div className=\"guestbook\">\n        <h1>Guestbook</h1>\n        <table className=\"table table-striped table-bordered mb-4\">\n          <thead className=\"thead-light\">\n            <tr>\n              <th>Name</th>\n              <th>Message</th>\n            </tr>\n          </thead>\n          <CreateTableData entryData={this.props.entries}/>\n        </table>\n        <div>\n          <Link to=\"/add-entry\" onClick={this.props.resetRedirect} className=\"btn btn-outline-secondary\">Add Entry</Link>\n        </div>\n      </div>\n    );\n  }\n}\nconst CreateTableData = props => {\n\n    const entries = props.entryData.map((row, index) => {\n          \n      return (\n        <tr key={index}>\n          <td>{row.name}</td>\n          <td>{row.message}</td>\n        </tr>\n      )\n    });\n    \n    return <tbody>{entries}</tbody>;\n\n}\n\n\nexport default Guestbook;"
  },
  {
    "path": "en/developer/tutorials/articles/01-tutorials-intro/01-tutorials-intro.markdown",
    "content": "---\nheader-id: developer-tutorials\n---\n\n# Developer Tutorials\n\n[TOC levels=1-4]\n\nLiferay's developer tutorials help you learn from scratch. They are the\n\"opinionated\" way to work with code on Liferay's platform. Here, you'll learn\nthese things: \n\n- Writing applications on various frameworks, such as Bean Portlet,\n  PortletMVC4Spring, and Liferay MVC Portlet (coming soon)\n\n- Writing front-end applications using Angular, React, and Vue (coming soon) \n\n- Upgrading your code from older versions of @product@\n\n- Creating back-end and headless services (coming soon)\n\n\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/01-intro.markdown",
    "content": "---\nheader-id: developing-a-web-application\n---\n\n# Developing a Web Application\n\n[TOC levels=1-4]\n\nThis tutorial shows you how to build a Liferay application from beginning to\nend. Starting by designing the back-end using Liferay's object-relational mapper\nService Builder, you'll move from there to designing the widget that calls all\nof those services. In the process, you'll learn about many of Liferay's\ndevelopment frameworks, including \n\n- Permissions\n- Search\n- Assets\n- REST Builder web services\n- Workflow\n- Staging\n\nWhen you're finished, the application looks like this: \n\n![Figure 1: It looks humble, but there's a lot of functionality packed in this application.](../../images/finished-guestbook-intro.png)\n\nThis tutorial assumes nothing, so it starts at the beginning: setting up a \nLiferay development environment. Though you can use anything from a text editor \nand the command line to your Java IDE of choice, Liferay @ide@ optimizes\ndevelopment on Liferay's platform. It integrates Liferay's Blade tools for\nmodular development. \n\nOnce you have an environment ready, you'll jump right in and start creating your\nobject-relational map. After you've created your back-end, you'll move to the\nfront-end. \n\nFrom there you'll see everything from UI standards to providing remote \nservices. Once everything is completed and wrapped up with a bow, you can \ndistribute the application on Marketplace. \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/setting-up-a-development-environment\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/02-setting-up-liferay-development/01-setting-up-liferay-development-intro.markdown",
    "content": "---\nheader-id: setting-up-a-development-environment\n---\n\n# Setting Up a Development Environment\n\n[TOC levels=1-4]\n\nLiferay's development tools help you get started fast. All you need as\na prerequisite is a Java Development Kit version 8 ([JDK](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)\nor [OpenJDK](https://jdk.java.net/java-se-ri/8) is fine).\nOnce that's installed, there are only three steps. \n\n- Download a Liferay @ide@ bundle. \n\n- Unzip the downloaded package to a location on your system. \n\n- Start @ide@. \n\nYou'll follow these steps and then generate a Liferay Workspace for developing\nyour first @product@ application. \n\n## Installing a Liferay @ide@ Bundle\n\nFollow these steps: \n\n1.  Download and install \n    [Liferay @ide@](/docs/7-2/reference/-/knowledge_base/r/installing-liferay-dev-studio)\n    Installing it is easy: run the installer.\n\n2. To run Liferay @ide@, run the `DeveloperStudio` executable. On Windows, the\n   installer doesn't create a menu entry, so you should add a shortcut to it to\n   your desktop or task bar.\n\nThe first time you start Liferay @ide@, it prompts you to select an Eclipse\nworkspace.  If you specify an empty folder, Liferay @ide@ creates a new\nworkspace in that folder. Follow these steps to create a new workspace:\n\n1.  When prompted, indicate your workspace's path. Name your new workspace \n    `guestbook-workspace` and click *OK*. Windows has path length limitations,\n    so on that OS, you may want to create your workspace the root folder\n    (`C:\\`). \n\n2.  When Liferay @ide@ first launches, it presents a welcome page. Click the\n    *Workbench* icon to continue. \n\nNice job! Your development environment is installed and your Eclipse workspace\nis set up. \n\n## Creating a Liferay Workspace\n\nNow you'll create another kind of workspace: a \n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace). \nLiferay Workspace helps manage Liferay projects by providing various build\nscripts, properties, and configuration for you. Liferay Workspace uses \n[Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli) and \n[Gradle](https://gradle.org/) to manage dependencies and organize your build \nenvironment. Note that to avoid configuration issues, you can only create one \nLiferay Workspace for each Eclipse Workspace. \n\nFollow these steps to create a Liferay Workspace in Liferay @ide@:\n\n1.  Select *File* &rarr; *New* &rarr; *Liferay Workspace Project*. Note: you may \n    have to select *File* &rarr; *New* &rarr; *Other*, then choose *Liferay \n    Workspace Project* in the *Liferay* category. \n\n    ![Figure 1: By selecting *Liferay Workspace*, you begin the process of creating a new workspace for your @product@ projects.](../../../images/dev-studio-create-workspace.png)\n\n    A *New Liferay Workspace* dialog appears, which presents several \n    configuration options. \n\n2.  Give your workspace the name `com-liferay-docs-guestbook`. \n\n3.  Next, choose your workspace's location. Leave the default setting checked. \n    This places your Liferay Workspace inside your Eclipse workspace. \n \n4.  For *Liferay Version*, 7.2 should already be selected. \n\n5.  Leave the rest of the defaults. \n\n6.  Check the *Download Liferay bundle* checkbox to download and \n\tunzip a @product@ instance in your workspace automatically.  When prompted,\n\tname the server `liferay-tomcat-bundle`. \n\n6.  Click *Finish* to create your Liferay Workspace. This may take a while \n    because Liferay @ide@ downloads the @product@ bundle in the background. \n\nCongratulations! Your development environment is ready! Next, you'll get started \ndeveloping your first @product@ application. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/03-generating-the-backend/00-intro.markdown",
    "content": "---\nheader-id: generating-the-back-end\n---\n\n# Generating the Back-end\n\n[TOC levels=1-4]\n\nYou can start writing an application in either the front-end or the back-end. If\nyou start with the front-end, you design the screens and forms first, using mock\ndata. If you start with the back-end, you create your data store up front, and\nthen you can create your front-end later. This is what you'll do with the\nGuestbook application. \n\nA *persistence* layer and a *service* layer make up the bottom layers of your\nback-end. You'll persist guestbooks and their entries to a database. Your\nservice layer calls your persistence layer, providing a buffer in case you wish\nto swap out your persistence technology later. \n\n![Figure 1: Service Builder generates the shaded layers of your application.](../../../images/application-layers.png)\n\n*Service Builder* is Liferay's code generation tool for defining object models\nand mapping those models to SQL databases. By defining your model in a single\nXML file, you can generate your object model (the M in MVC), your service layer,\nand your persistence layer all in one shot. At the same time, you can  support\nevery database @product@ supports. \n\nReady to begin? \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/what-is-service-builder-0\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/03-generating-the-backend/01-what-is-service-builder.markdown",
    "content": "---\nheader-id: what-is-service-builder-0\n---\n\n# What is Service Builder?\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Generating the Back-End</p><p>Step 1 of 3</p>\n</div>\n\nNow you'll use Service Builder to generate your application's Model, Service,\nand Persistence layers. Then you can add your application's necessary business\nlogic. \n\n## Guestbook Application Design\n\nThe Guestbook application handles multiple Guestbooks and their entries. To make\nthis work, you'll create two tables in the database: one for guestbooks and one\nfor guestbook entries. \n\n![Figure 1: When you're done, the Guestbook supports multiple guestbooks and makes use of many Liferay features.](../../../images/guestbook-final.png)\n\n## Service Layer\n\nThis application is data-driven. It uses services for storing and retrieving \ndata. The application asks for data, and the service fetches it from the\npersistence layer. The application can then display this data to the user, who\nreads or modifies it. If the data is modified, the application passes it back\nto the service, which calls the persistence layer to store it. The application\ndoesn't need to know anything about how the service does what it does. \n\nTo get started, you'll create a Service Builder project and populate its \n`service.xml` file with all the necessary entities to generate this code: \n\n1.  In Liferay @ide@, click *File* &rarr; *New* &rarr; *Liferay Module Project*.\n\n2.  Name the project `guestbook`.\n\n3.  Select `service-builder` for the Project Template Name.\n \n4.  Click *Next*.\n\n5.  Enter `com.liferay.docs.guestbook` for the *Package Name*.\n\n6.  Click *Finish*.\n\nThis creates two modules: an API module (`guestbook-api`) and a service module \n(`guestbook-service`). Next, you'll learn how to use them. \n\n![Figure 2: Your current project structure.](../../../images/guestbook-service-project.png)\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/03-generating-the-backend/02-generating-layers.markdown",
    "content": "---\nheader-id: generating-model-service-and-persistence-layers\n---\n\n# Generating Model, Service, and Persistence Layers\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Generating the Back-End</p><p>Step 2 of 3</p>\n</div>\n\nTo model the guestbooks and entries, you'll create guestbook and entry model \nclasses. But you won't do this directly in Java. Instead, you'll define them in \nService Builder, which generates your object model and maps it to all the SQL \ndatabases @product@ supports. \n\nThis application's design allows for multiple guestbooks, each containing \ndifferent sets of entries. All users with permission to access the application \ncan add entries, but only administrative users can add guestbooks. \n\nIt's time to get started. You'll create the `Guestbook` entity first: \n\n1.  In your `guestbook-service` project, open `service.xml`. Make sure the\n    *Source* tab is selected. \n\n2.  When Liferay @ide@ generated your project, it filled this file with dummy \n    entities, which you'll replace. Remove everything in the file below the\n    `DOCTYPE`. Replace the file's opening contents with the following code: \n\n    ```xml\n    <service-builder dependency-injector=\"ds\" package-path=\"com.liferay.docs.guestbook\" mvcc-enabled=\"true\">\n        <author>liferay</author>\n        <namespace>GB</namespace>\n        <entity name=\"Guestbook\" local-service=\"true\" uuid=\"true\" remote-service=\"true\">\n    ```\n\n    This defines the author, namespace, and the entity name. The namespace keeps \n    the database field names from conflicting. The last tag is the opening tag \n    for the `Guestbook` entity definition. In this tag, you enable local and\n    remote services for the entity, define its name, and specify that it should\n    have a \n    [universally unique identifier (UUID)](https://en.wikipedia.org/wiki/Universally_unique_identifier). \n\n3.  The Guestbook requires only two fields: a primary key to identify it\n    uniquely in the database, and a name. Add these fields: \n\n    ```xml\n    <!-- Guestbook fields -->\n\n    <column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n    <column name=\"name\" type=\"String\" />\n    ```\n\n    This defines `guestbookId` as the entity's primary key of the type `long`\n    and the name as a `String`. \n\n4.  Next, define the group instance. The `groupId` defines the ID of the Site in\n    @product@ where the entity instance exists. The `companyId` is the primary\n    key of a \n    [portal instance](/docs/7-2/user/-/knowledge_base/u/setting-up).\n\n    ```xml\n    <!-- Group instance -->\n\n    <column name=\"groupId\" type=\"long\" />\n    <column name=\"companyId\" type=\"long\" />\n    ```\n\n5.  Next, add audit fields. These fields help you track owners of entity\n    instances, along with those instances' create and modify dates: \n\n    ```xml\n    <!-- Audit fields -->\n\n    <column name=\"userId\" type=\"long\" />\n    <column name=\"userName\" type=\"String\" />\n    <column name=\"createDate\" type=\"Date\" />\n    <column name=\"modifiedDate\" type=\"Date\" />\n    ```\n\n6.  After this, add fields that support Liferay's workflow system. These provide\n    fields in the database to track your entity's status as it passes through\n    the workflow. \n\n    ```xml\n    <!-- Status fields -->\n\n    <column name=\"status\" type=\"int\" />\n    <column name=\"statusByUserId\" type=\"long\" />\n    <column name=\"statusByUserName\" type=\"String\" />\n    <column name=\"statusDate\" type=\"Date\" />\n    ```\n\n7.  Before the closing `</entity>` tag, add this finder definition: \n\n    ```xml\n        <finder name=\"GroupId\" return-type=\"Collection\">\n            <finder-column name=\"groupId\" />\n        </finder>\n\n    </entity>\n    ```\n\nA [finder](/docs/7-2/appdev/-/knowledge_base/a/defining-service-entity-finder-methods) \ngenerates a `get` method for retrieving Guestbook entities. The fields used by\nthe finder define the scope of the data retrieved. This finder gets all\nGuestbooks by their `groupId`. This is how administrators put Guestbooks on\nmultiple Sites, and each `Guestbook` has its own data scoped to its Site. \n\nThe `Guestbook` entity is finished for now. Next, you'll create the\n`GuestbookEntry` entity: \n\n1.  Add the opening entity tag:\n\n    ```xml\n    <entity name=\"GuestbookEntry\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n    ```\n\n    As with the `Guestbook` entity, you enable local and remote services, define\n    the entity's name, and specify that it should have a UUID. \n\n2.  Add the fields that define the `GuestbookEntry`'s data: \n\n    ```xml\n    <!-- Guestbook Entry fields -->\n\n    <column name=\"entryId\" primary=\"true\" type=\"long\" />\n    <column name=\"name\" type=\"String\" />\n    <column name=\"email\" type=\"String\" />\n    <column name=\"message\" type=\"String\" />\n    <column name=\"guestbookId\" type=\"long\" />\n    ```\n\n    The `name`, `email`, and `message` fields comprise a `GuestbookEntry`. These\n    fields define the name of the person creating the entry, an email address,\n    and the Guestbook message, respectively. The `guestbookId` is assigned\n    automatically by code you'll write, and is a foreign key to the `Guestbook`\n    where this entry belongs. \n\n3.  Add fields to track the portal instance and group: \n\n    ```xml\n    <!-- Group instance -->\n\n    <column name=\"groupId\" type=\"long\" />\n    <column name=\"companyId\" type=\"long\" />\n    ```\n\n4. Add audit fields: \n\n    ```xml\n    <!-- Audit fields -->\n\n    <column name=\"userId\" type=\"long\" />\n    <column name=\"userName\" type=\"String\" />\n    <column name=\"createDate\" type=\"Date\" />\n    <column name=\"modifiedDate\" type=\"Date\" />\n    ```\n\n5.  Add status fields to track workflow: \n\n    ```xml\n    <!-- Status fields -->\n   \n    <column name=\"status\" type=\"int\" />\n    <column name=\"statusByUserId\" type=\"long\" />\n    <column name=\"statusByUserName\" type=\"String\" />\n    <column name=\"statusDate\" type=\"Date\" />\n    ```\n\n6.  When querying for `GuestbookEntry`s, you can order them by one or more\n    columns. Since visitors sign `Guestbook`s in order by time, order your\n    `GuestbookEntry` instances by the date they were created: \n\n    ```xml\n    <order>\n        <order-column name=\"createDate\" order-by=\"desc\" />\n    </order>\n    ```\n\n7.  Add a finder that retrieves `GuestbooEntry`s by a combination of `groupId`\n    and `guestbookId`. This supports @product@'s multi-tenancy by only returning\n    those entries that belong both to the current Site and the current\n    Guestbook. After defining your finder add the closing entity tag:\n\n    ```xml\n        <finder name=\"G_G\" return-type=\"Collection\">\n            <finder-column name=\"groupId\" />\n            <finder-column name=\"guestbookId\" />\n        </finder>\n    </entity>\n    ```\n\n8.  Define exception types outside the `<entity>` tags, just before the closing\n    `</service-builder>` tag: \n\n    ```xml\n        <exceptions>\n            <exception>GuestbookEntryEmail</exception>\n            <exception>GuestbookEntryMessage</exception>\n            <exception>GuestbookEntryName</exception>\n            <exception>GuestbookName</exception>\n        </exceptions>\n\n    </service-builder>\n    ```\n\n    These generate exception classes you'll use later in try/catch statements. \n\n9.  Save your `service.xml` file.\n\nNow you're ready to run Service Builder to generate your model, service, and\npersistence layers!\n\n1.  In the Gradle Tasks pane on the right side of @ide@, open\n    `com-liferay-docs-guestbook` &rarr; `modules` &rarr; `guestbook` &rarr;\n    `guestbook-service` &rarr; `build`. \n\n2.  Run `buildService` by right-clicking it and selecting *Run Gradle Tasks*.\n    Make sure you're connected to the Internet, as Gradle downloads dependencies\n    the first time you run it. \n\n3.  In the Project Explorer, right-click the `guestbook-service` module and \n    select *Refresh*. Repeat this step for the `guestbook-api` module. This \n    ensures that the new classes and interfaces generated by Service Builder \n    show up in @ide@.\n\n4.  In the Project Explorer, right-click the `guestbook-service` module and \n    select *Gradle* &rarr; *Refresh Gradle Project*. Repeat this step for the \n    `guestbook-api` module. This ensures that your modules' Gradle dependencies \n    are up to date. \n\nService Builder is based on a design philosophy called loose coupling. It\ngenerates three layers of your application: the model, the service, and the\npersistence layers. Loose coupling means you can swap out the persistence layer\nwith little to no change in the model and service layers. The model is in the \n`-api` module, and the service and persistence layers are in the `-service` \nmodule. \n\n![Figure 1: The Model, Service, and Persistence Layer comprise a loose coupling design.](../../../images/model-service-persistence.png)\n\nEach layer is implemented using Java Interfaces and implementations of those\ninterfaces. Rather than have one `Guestbook` class that represents your\nmodel, Service Builder generates a system of classes that includes a `Guestbook`\ninterface, a `GuestbookBaseImpl` abstract class that Service Builder manages,\nand a `GuestbookImpl` class that you can customize. With this design, you can\ncustomize your model and let Service Builder generate the tedious-to-write\ncode. That's why Service Builder is a code generator for code generator haters. \n\nNext, you'll create the service implementations. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/03-generating-the-backend/03-implementing-service-methods.markdown",
    "content": "---\nheader-id: implementing-service-methods\n---\n\n# Implementing Service Methods\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Generating the Back-End</p><p>Step 3 of 3</p>\n</div>\n\nWhen you use Service Builder, you implement the services in the service module.\nBecause your application's projects are \n[components](/docs/7-2/appdev/-/knowledge_base/a/invoking-local-services), \nyou can reference your service layer from your web module. \n\nYou'll implement services for guestbooks and entries in the `guestbook-service` \nmodule's `GuestbookLocalServiceImpl` and `GuestbookEntryLocalServiceImpl`,\nrespectively. \n\nFollow these steps to implement services for guestbooks in \n`GuestbookLocalServiceImpl`: \n\n1.  In the `com.liferay.docs.guestbook.service.impl` package, open \n    `GuestbookLocalServiceImpl`. Then add this `addGuestbook` method: \n\n    ```java\n\tpublic Guestbook addGuestbook(long userId, String name,\n\t\t\tServiceContext serviceContext) throws PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name);\n\n\t\tlong guestbookId = counterLocalService.increment();\n\n\t\tGuestbook guestbook = guestbookPersistence.create(guestbookId);\n\n\t\tguestbook.setUuid(serviceContext.getUuid());\n\t\tguestbook.setUserId(userId);\n\t\tguestbook.setGroupId(groupId);\n\t\tguestbook.setCompanyId(user.getCompanyId());\n\t\tguestbook.setUserName(user.getFullName());\n\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tguestbook.setName(name);\n\t\tguestbook.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookPersistence.update(guestbook);\n\n\t\treturn guestbook;\n\t}\n    ```\n\n    This method adds a guestbook to the database. It retrieves metadata from the \n    environment (such as the current user's ID, the group ID, etc.), along with \n    data passed from the user. It validates this data and uses it to construct a \n    `Guestbook` object. The method then persists this object to the database and \n    returns the object. You implement the business logic here because Service\n    Builder already generated the model and all the code that maps that model to\n    the database. \n\n2.  Add the methods for getting `Guestbook` objects: \n\n    ```java\n\tpublic List<Guestbook> getGuestbooks(long groupId) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end,\n\t\t\tOrderByComparator<Guestbook> obc) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end, obc);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end);\n\t}\n\n\tpublic int getGuestbooksCount(long groupId) {\n\n\t\treturn guestbookPersistence.countByGroupId(groupId);\n\t}\n    ```\n\n    These call the finders you generated with Service Builder. The first method \n    retrieves a list of guestbooks from the Site specified by `groupId`. The \n    next two methods get paginated lists, optionally in a particular order. The \n    final method gives you the total number of guestbooks for a given Site. \n\n3.  Finally, add the guestbook validator method:\n\n    ```java\n\tprotected void validate(String name) throws PortalException {\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookNameException();\n\t\t}\n\t}\n    ```\n\t\n    This method uses @product@'s `Validator` to make sure the user entered text \n    for the guestbook name. \n\n4.  Press [CTRL]+[SHIFT]+O to organize imports and select the following classes\n    when prompted: \n\n    - `java.util.List`\n    - `java.util.Date`\n    - `com.liferay.portal.kernel.util.Validator`\n\nNow you're ready to implement services for entries in `GuestbookEntryLocalServiceImpl`. \n\n1.  In the `com.liferay.docs.guestbook.service.impl` package, open \n    `GuestbookEntryLocalServiceImpl`. Add this `addEntry` method: \n\n    ```java\n\tpublic GuestbookEntry addGuestbookEntry(long userId, long guestbookId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tlong entryId = counterLocalService.increment();\n\n\t\tGuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\n\t\tentry.setUuid(serviceContext.getUuid());\n\t\tentry.setUserId(userId);\n\t\tentry.setGroupId(groupId);\n\t\tentry.setCompanyId(user.getCompanyId());\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setCreateDate(serviceContext.getCreateDate(now));\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\t\tentry.setGuestbookId(guestbookId);\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Calls to other Liferay frameworks go here\n\n\t\treturn entry;\n\t}\n    ```\n\t\n    Like the `addGuestbook` method, `addGuestbookEntry` takes data from the\n    current context along with data the user entered, validates it, and creates\n    a model object. That object is then persisted to the database and returned. \n\t\n2.  Add this `updateGuestbookEntry` method:\n\n    ```java\n\tpublic GuestbookEntry updateGuestbookEntry(long userId, long guestbookId,\n\t\t\tlong entryId, String name, String email, String message,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException {\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tentry.setUserId(userId);\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Integrate with Liferay frameworks here.\n\n\t\treturn entry;\n\t}\n    ```\n\n    This method first retrieves the entry and updates its data to reflect what\n    the user submitted, including its date modified. \n\n2.  Add two `deleteGuestbookEntry` methods: \n\n    ```java\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry entry)\n\t\tthrows PortalException {\n\n\t\tguestbookEntryPersistence.remove(entry);\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId) throws PortalException {\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\treturn deleteGuestbookEntry(entry);\n\t}\n    ```\n\t\n    These methods delete entries using the `entryId` or the object. If you\n    supply the `entryId`, the object is retrieved and passed to the method that\n    deletes the object. \t\n\n3.  Add the methods for getting `GuestbookEntry` objects: \n\n    ```java\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end) throws SystemException {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end, OrderByComparator<GuestbookEntry> obc) {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend, obc);\n\t}\n\n\tpublic GuestbookEntry getGuestbookEntry(long entryId) throws PortalException {\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.countByG_G(groupId, guestbookId);\n\t}\n    ```\n\t\n    These methods, like the getters in `GuestbookLocalServiceImpl`, call the \n    finders you generated with Service Builder. These `getGuestbookEntries*`\n    methods, however, retrieve entries from a specified Guestbook and Site. The\n    first method gets a list of entries. The next method gets a paginated list.\n    The third method sorts the paginated list, and the last method gets the\n    total number of entries as an integer. \n\n4.  Add the `validate` method: \n\n    ```java\n\tprotected void validate(String name, String email, String entry)\n\t\tthrows PortalException {\n\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookEntryNameException();\n\t\t}\n\n\t\tif (!Validator.isEmailAddress(email)) {\n\t\t\tthrow new GuestbookEntryEmailException();\n\t\t}\n\n\t\tif (Validator.isNull(entry)) {\n\t\t\tthrow new GuestbookEntryMessageException();\n\t\t}\n\t}\n    ```\n\n    This method makes sure the user entered relevant data when creating an \n    entry. \n\n5.  Press [CTRL]+[SHIFT]+O to organize imports and select the following classes\n    when prompted: \n\n    - `java.util.List`\n    - `java.util.Date`\n    - `com.liferay.portal.kernel.util.Validator`\n\nNice work! These local service methods implement the services that are \nreferenced in the portlet class. \n\n## Updating Generated Classes\n\nNow that you've implemented the service methods, you must make them available to \nthe rest of your application. To do this, run `buildService` again: \n\n1.  In *Gradle Tasks* &rarr; *guestbook-service* &rarr; *build*, right-click \n    `buildService` and select *Run Gradle Tasks*. In the utility classes, Service \n    Builder populates calls to your newly created service methods. \n\n2.  In the Project Explorer, right-click the `guestbook-service` module and \n    select *Refresh*. Repeat this step for the `guestbook-api` module. This \n    ensures that the changes made by Service Builder show up in Liferay @ide@.\n\n3.  In the Project Explorer, right-click the `guestbook-service` module and \n    select *Gradle* &rarr; *Refresh Gradle Project*. Repeat this step for the \n    `guestbook-api` module. This ensures that your modules' Gradle dependencies \n    are up to date. \n\n| **Tip:** If something goes awry when working with Service Builder, repeat these\n| steps to run Service Builder again and refresh your API and service modules.\n\nExcellent! Your new back-end has been generated. Now it's time to create a web\napplication that uses it. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/04-building-the-web-front-end/01-intro.markdown",
    "content": "---\nheader-id: building-the-web-front-end\n---\n\n# Building the Web Front-End\n\n[TOC levels=1-4]\n\nYou now have a back-end: you created a `service.xml` file to define your\napplication's data model, and generated the back-end code (the model, service,\nand persistence layers) with Service Builder. You also added service methods\nusing the appropriate extension points: your entities' `*LocalServiceImpl`\nclasses. Now you can add a front-end to match new back-end, creating a fully\nfunctional application. \n\nYou'll create two portlets: the Guestbook portlet for users to use to add\nentries and in a later step, a Guestbook Admin portlet for administrators to add\nGuestbooks. \n\nStarting with this step, [source code is provided](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/guestbook/04-web-front-end)\nin case you get stuck.\n\nReady to begin? \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/creating-the-web-project\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/04-building-the-web-front-end/02-creating-the-web-project.markdown",
    "content": "---\nheader-id: creating-the-web-project\n---\n\n# Creating the Web Project\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Building the Web Front-End</p><p>Step 1 of 11</p>\n</div>\n\nYour first step is to create another Liferay Module Project. Modules are the\ncore building blocks of @product@ applications. Every application is made from\none or more modules. Each module encapsulates a functional piece of an\napplication. Multiple modules form a complete application. \n\nModules can be web modules or [OSGi](https://www.osgi.org/) modules. Since\nyou'll be creating a Liferay MVC Portlet, you'll create an OSGI module. The OSGi\ncontainer in @product@ can run any OSGi module. Each module is packaged as a JAR\nfile that contains a manifest file. The manifest is needed for the container to\nrecognize the module. Technically, a module that contains only a manifest is\nstill valid. Of course, such a module wouldn't be very interesting. \n\nYou already created Service Builder modules. Now you'll create your MVC Portlet\nmodule. For the purpose of this tutorial, you'll create your modules inside\nyour Liferay Workspace. \n\n1.  In Liferay @ide@, select *File* &rarr; *New* &rarr; *Liferay Module\n    Project*. \n\n2.  Complete the first screen of the wizard with the following information: \n\n    ![Figure 1: Complete the New Module Project wizard.](../../../images/new-module-project.png)\n\n    - Enter `guestbook-web` for the Project name. \n    - Use the *Gradle* Build type.\n    - The Liferay version is *7.2*.\n    - Select `mvc-portlet` for the Project Template. \n\n    Click *Next*. \n\n5.  On the second screen of the wizard, enter `Guestbook` for the component \n    class name, and `com.liferay.docs.guestbook.portlet` for the package name. \n    Click *Finish*.\n\nNote that it may take a while for @ide@ to create your project, because Gradle \ndownloads your project's dependencies for you during project creation. Once this \nis done, you have a module project named `guestbook-web`. The `mvc-portlet` \ntemplate configured the project with the proper dependencies and generated all \nthe files you need to get started: \n\n- The portlet class (in the package you specified)\n- JSP files (in `/src/main/resources`)\n- Language properties (also in `/src/main/resources`)\n\nYour new module project is a *portlet* application. You'll learn what that is in\na moment, but first there's some housekeeping to do. \n\nIn larger projects, it is important to have all of your files and modules well \norganized. Since the `guestbook-web` module really goes with your Service\nBuilder modules, it should be in the `guestbook` folder.\n\n1.  In the *Project Explorer*, right-click on `guestbook-web` and select\n    *Move*.\n\n2.  In the window that appears, click *Browse*, choose the `guestbook` \n    folder and then click *OK*.\n \nYour `guestbook-web` folder now appears in the structure with the other modules.\n\n![Figure 2: After you move it, all of your modules are in the same folder..](../../../images/guestbook-refactor.png)\n\n| **Note:** Sometimes Eclipse refuses to move your project. If that happens,\n| close Eclipse, use your operating system's file manager to move the\n| `guestbook-web` folder into the `guestbook` folder, and then restart Eclipse. \n\nYou're now ready to begin writing your front-end, but first some explanation is\nin order. \n\n## What is a Portlet?\n\nWeb applications can be simple or complex: they might display an article or\ncalculate your taxes. These applications run on a *platform* that provides\napplication developers the building blocks they need to make applications.\n\n![Figure 3: Many Liferay applications can run at the same time on the same page.](../../../images/portlet-applications.png)\n\n@product@ provides a platform that contains common features needed by today's\napplications, including user management, security, user interfaces, services, \nand more. Portlets are one of those basic building blocks. Often a web \napplication takes up the entire page. Portlets can do this or share the page\nwith many applications at the same time. @product@'s framework takes this into\naccount at every step. \n\n## What is a Component?\n\nLiferay MVC Portlets are *Components*. If a module (sometimes also called\na *bundle*) encapsulates pieces of your application, a component is the object\nthat contains the core functionality. A Component is managed by a component\nframework or container. Components are deployed inside modules, and they're\ncreated, started, stopped, and destroyed as needed by the container. What\na perfect model for a web application! It can be made available only when\nneeded, and when it's not, the container can make sure it doesn't use resources\nneeded by other components. \n\nIn this case, you created a Declarative Services (DS) component. With\nDeclarative Services, you declare that an object is a component, and you define \ndata about the component so the container knows how to manage it. A default \nconfiguration was created for you; you'll examine it later. \n\n## Deploying the Application\n\nEven though all you've done is generate it, the `guestbook-web` project is ready\nto be built and deployed.\n\n1.  Make sure that your server is running, and if it isn't, select it in \n    @ide@'s Servers pane and click the start button (![Start Server](../../../images/icon-start-server.png)).\n\n2.  After it starts, drag and drop the `guestbook-web` project from the Project\n    Explorer to the server.\n \n    ![Figure 4: Drag and drop the module.](../../../images/deploy-module.gif)\n\n3.  Open a browser and navigate to @product@\n    ([http://localhost:8080](http://localhost:8080) by default).\n\n    If this is your first time starting @product@, you'll go through a short \n    wizard to set up your server. In this wizard, make sure you use the default \n    database (Hypersonic). Although this database isn't intended for production \n    use, it works fine for development and testing. \n\n4.  Click the menu button at the top left and select *Site Builder* &rarr;\n    *Pages*. \n\n5.  Click the ![add](../../../images/icon-add.png) button at the top right to\n    add a Public Page. \n\n6.  Choose *Widget Page* and name it *Guestbook*. \n\n7.  Choose the *One Column* layout and click *Save*. \n\n8.  Click *Go to Site* on the left, and then navigate to your new Guestbook\n    page. \n \n9.  Click *Add* \n    (![Add Widget](../../../images/icon-add-app.png)) in the upper right hand \n    corner.\n\n5.  Select *Widgets*. In the Applications list, your application appears in the\n    Sample category. Its name is `Guestbook`. \n\n![Figure 5: This is your new page with the Guestbook application that you created.](../../../images/default-guestbook-application.png)\n\nNow you're ready to jump in and start developing your Guestbook portlet. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/04-building-the-web-front-end/03-defining-component-metadata-properties.markdown",
    "content": "---\nheader-id: defining-the-component-metadata-properties\n---\n\n# Defining the Component Metadata Properties\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Building the Web Front-End</p><p>Step 2 of 11</p>\n</div>\n\nWhen users add applications to a page, they pick them from a list of *display\ncategories*. \n\n![Figure 1: Users choose applications from a list of display categories.](../../../images/display-categories.png)\n\nA portlet's display category is defined in its component class as a metadata \nproperty. Since the Guestbook portlet lets users communicate with each other, \nyou'll add it to the Social category. Only one Guestbook portlet should be added \nto a page, so you'll also define it as a *non-instanceable* portlet. Such a \nportlet can appear only once on a page or Site, depending on its scope. \n\n1.  Open the `GuestbookPortlet` class and update the component class metadata \n    properties to match this configuration: \n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n          \"com.liferay.portlet.display-category=category.social\",\n          \"com.liferay.portlet.instanceable=false\",\n          \"com.liferay.portlet.scopeable=true\",\n          \"javax.portlet.display-name=Guestbook\",\n          \"javax.portlet.expiration-cache=0\",\n          \"javax.portlet.init-param.template-path=/\",\n          \"javax.portlet.init-param.view-template=/guestbook/view.jsp\",\n          \"javax.portlet.resource-bundle=content.Language\",\n          \"javax.portlet.security-role-ref=power-user,user\",\n          \"javax.portlet.supports.mime-type=text/html\"\n        },\n        service = Portlet.class\n    )\n    ```\n\nThe `com.liferay.portlet.display-category=category.social` property sets the \nGuestbook portlet's display category to *Social*. The \n`com.liferay.portlet.instanceable=false` property specifies that the Guestbook \nportlet is non-instanceable, so only one instance of the portlet can be added \nto a page. In the property `javax.portlet.init-param.view-template`, you also \nupdate the location of the main `view.jsp` to a folder in\n`src/main/resources/META-INF/resources` called `/guestbook`. You'll wind up\ncreating two folders there for the two different portlets you'll create:\n`guestbook` and `guestbook-admin`. For now, just create the `guestbook` folder: \n\n1.  Open `src/main/resources`, then open `META-INF`. Right-click on the\n    `resources` folder and select *New* &rarr; *Folder*. \n\n2.  Name the folder *guestbook* and hit *Enter* (or click OK). \n\n3.  Drag `view.jsp` and drop it onto the `guestbook` folder to move it there. \n\n4.  Open `view.jsp` and modify the path to `init.jsp` to include it from the\n    parent folder: \n\n    ```markup\n    <%@ include file=\"../init.jsp\" %>\n    ```\n\nSince you edited the portlet's metadata, you must remove and re-add the portlet \nto the page before continuing: \n\n1.  Go to `localhost:8080` in your web browser.\n\n2.  Sign in to your administrative account.\n\n3.  The Guestbook portlet now shows an error on the page. Click its portlet menu \n    (at the top-right of the portlet), then select *Remove* and click *OK* to \n    confirm.\n\n4.  Open the *Add* menu and select *Widgets*.\n\n5.  Open the *Social* category and drag and drop the *Guestbook* widget\n    onto the page.\n\nGreat! Now the Guestbook portlet appears in an appropriate category. Though you \nwere able to add it to the page before, the user experience is better. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/04-building-the-web-front-end/04-portlet-keys.markdown",
    "content": "---\nheader-id: creating-portlet-keys\n---\n\n# Creating Portlet Keys\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Building the Web Front-End</p><p>Step 3 of 11</p>\n</div>\n\n`PortletKeys` manage important things like the portlet name or other repeatable,\ncommonly used variables in one place. This way, if you must change the portlet's\nname, you can do it in one place and then reference it in every class that\nneeds it. Keys are created in a `PortletKeys` class and then referenced in\na component property. \n\nFollow these steps to create your application's `PortletKeys`:\n\n1.  Open the `com.liferay.docs.guestbook.constants` package. \n\n2.  Open `GuestbookPortletKeys` and make sure there's a public, static, final\n    String called `GUESTBOOK` with a value of \n    `com_liferay_docs_guestbook_portlet_GuestbookPortlet`: \n \n    ```java\n    public static final String GUESTBOOK =\n             \"com_liferay_docs_guestbook_portlet_GuestbookPortlet\";\n    ```\n\n3. Save the file.\n\n4.  In your `guestbook-web` module, open the `GuestbookPortlet` class and \n    update the component class metadata properties by adding one new property: \n\n    ```java\n    \"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK,\n    ```\n\n    Note that you need the trailing comma if you've added the property to the\n    middle of the list. If you've added it to the end of the last, leave it off\n    (but add a trailing comma to the prior property!). \n\n5.  Save `GuestbookPortlet`. \n\nNice job! \n\nNext, you'll integrate your application with the back-end you generated with \nService Builder. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/04-building-the-web-front-end/05-integrating-the-back-end.markdown",
    "content": "---\nheader-id: integrating-the-back-end\n---\n\n# Integrating the Back-end\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Building the Web Front-End</p><p>Step 4 of 11</p>\n</div>\n\nTo use your Service Builder-generated back-end in your front-end, you must\ntell your front-end project that the back-end exists and where to find it. \n\nFor the web module to access the generated services, you must make it aware of\nthe API and service modules: \n\n1.  Open `guestbook-web`'s `build.gradle` file and add these dependencies:\n\n    ```groovy\n    compileOnly project(\":modules:guestbook:guestbook-api\")\n    compileOnly project(\":modules:guestbook:guestbook-service\")\n    ```\n\n2.  Save the file. Right-click on the `guestbook-web` project and select\n    *Gradle* &rarr; *Refresh Gradle Project*. \n\n3.  Now you must add *references* to the Service Builder services you need. To \n    do this, add them as class variables with `@Reference` annotations on their \n    setter methods. Open `GuestbookPortlet` and add these references to the \n    bottom of the file: \n\n    ```java\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\t\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n    ```\n\n    Note that it's Liferay's code style to add class variables this way. The\n    `@Reference` annotation causes Liferay's OSGi container to\n    inject references to your generated services so you can use them. \n\n4.  Press Ctrl-Shift-O to add the import you need: \n\n    - `org.osgi.service.component.annotations.Reference`\n\nNow you're ready to begin building your front-end. \n\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/04-building-the-web-front-end/06-creating-a-button.markdown",
    "content": "---\nheader-id: creating-an-add-entry-button\n---\n\n# Creating an Add Entry Button\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Building the Web Front-End</p><p>Step 5 of 11</p>\n</div>\n\nA guestbook application is pretty simple, right? People come to your site and \npost their names and brief messages. Others can read these entries and post\ntheir own. \n\nWhen you created your project, it generated a file named `view.jsp`, which\nyou've already moved to `src/main/resources/META-INF/resources/guestbook`\nfolder. This file contains the default view for users when the portlet is added\nto the page. Right now it contains sample content: \n\n```markup\n<%@ include file=\"../init.jsp\" %>\n\n<p>\n    <b><liferay-ui:message key=\"guestbook-web.caption\"/></b>\n</p>\n```\n\nFirst, `view.jsp` imports `init.jsp`. By Liferay convention, we declare imports\nand tag library declarations in an `init.jsp` file. The other JSP files in\nthe application import `init.jsp`. This reserves JSP dependency management to\na single file. \n\nBesides importing `init.jsp`, `view.jsp` displays a message defined by a\nlanguage key. This key and its value are declared in your project's\n`src/main/resources/content/Language.properties` file. \n\nIt's time to start developing the Guestbook application. First, users need a way \nto add a Guestbook entry. In `view.jsp`, follow these steps to add this button: \n\n1.  Remove everything under the include for `init.jsp`. \n\n2.  Below the include, add the following \n    [AlloyUI](http://alloyui.com/) tags to display an Add Entry button inside of \n    a button row: \n\n    ```markup\n    <aui:button-row>\n        <aui:button value=\"Add Entry\"></aui:button>\n    </aui:button-row>\n    ```\n\nYou can use `aui` tags in `view.jsp` since `init.jsp` declares the AlloyUI tag \nlibrary by default (as well as other important imports and tags): \n\n```markup\n<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\" %>\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\" %>\n\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\" %>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\" %>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\" %>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\" %>\n\n<portlet:defineObjects />\n\n<liferay-theme:defineObjects />\n```\n\nYour application now displays a button instead of a message, but the button \ndoesn't do anything. Next, you'll create a URL for your button. \n\n![Figure 1: Your new button is awesome, but it doesn't work yet.](../../../images/guestbook-new-button.png)\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/04-building-the-web-front-end/07-generating-portlet-urls.markdown",
    "content": "---\nheader-id: generating-portlet-urls\n---\n\n# Generating Portlet URLs\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Building the Web Front-End</p><p>Step 6 of 11</p>\n</div>\n\nSince users can place multiple portlets on a single page, you have no idea what\nother portlets may share a page with yours. This means that you can't define\nURLs for various functions in your application like you otherwise would.\n\nFor example, consider a Calendar application that a user puts on the same page \nas a Blog application. To implement the functionality for deleting calendar \nevents and blog entries in the respective application, both application \ndevelopers append the `del` parameter to the URL and give it a primary key \nvalue so the application can look up and delete the calendar event or blog \nentry. Since both applications read this parameter, their delete functionality \nclashes. \n\nSystem-generated URLs prevent this. If the system generates a unique URL for\neach piece of functionality, multiple applications can coexist in perfect\nharmony. \n\nIn `view.jsp`, follow these steps to create system-generated URLs in your\nportlet: \n\n1.  Add these tags below `<%@ include file=\"../init.jsp\" %>`, but above the \n    `<aui:button-row>` tag: \n\n    ```java\n    <portlet:renderURL var=\"addEntryURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\"></portlet:param>\n    </portlet:renderURL>\n    ```\n\n2.  Add this attribute to the `<aui:button>` tag, before `value=\"Add Entry\"`:\n\n    ```java\n    onClick=\"<%= addEntryURL.toString() %>\"\n    ```\n\n    Your `view.jsp` page should now look like this: \n\n    ```markup\n    <%@ include file=\"/init.jsp\" %>\n\n    <portlet:renderURL var=\"addEntryURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\"></portlet:param>\n    </portlet:renderURL>\n\n    <aui:button-row>\n        <aui:button onClick=\"<%= addEntryURL.toString() %>\" value=\"Add Entry\"></aui:button>\n    </aui:button-row>\n    ```\n\nThe `<portlet:renderURL>` tag's `var` attribute creates the `addEntryURL` \nvariable to hold the system-generated URL. The `<portlet:param>` tag defines a \nURL parameter to append to the URL. In this example, a URL parameter named \n`mvcPath` with a value of `/edit_entry.jsp` is appended to the URL. \n\nNote that your `GuestbookPortlet` class (located in your `guestbook-web` \nmodule's `com.liferay.docs.guestbook.portlet` package) extends Liferay's \n`MVCPortlet` class. In a \n[Liferay MVC portlet](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet), \nthe `mvcPath` URL parameter indicates a page within your portlet. To navigate to\nanother page in your portlet, use a portal URL with the parameter `mvcPath` to\nlink to the specific page. \n\nIn the example above, you created a `renderURL` that points to your \napplication's `edit_entry.jsp` page, which you haven't yet created. Note that\nusing an AlloyUI button to follow the generated URL isn't required. You can use \nany HTML construct that contains a link. Users can click your button to access \nyour application's `edit_entry.jsp` page. This currently produces an error since \nno `edit_entry.jsp` exists yet. Creating `edit_entry.jsp` is your next step. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/04-building-the-web-front-end/08-linking-to-another-page.markdown",
    "content": "---\nheader-id: linking-to-another-page\n---\n\n# Linking to Another Page\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Building the Web Front-End</p><p>Step 7 of 11</p>\n</div>\n\nIn the same folder your `view.jsp` is in, create the `edit_entry.jsp` file:\n\n1.  Right-click your project's `src/main/resources/META-INF/resources/guestbook`\n    folder and choose *New* &rarr; *File*.\n\n2.  Name the file `edit_entry.jsp` and click *Finish*.\n\n3.  Add this line to the top of the file:\n\n    ```markup\n    <%@ include file=\"../init.jsp\" %>\n    ```\n\n    Remember, it's a best practice to add all JSP imports and tag library\n    declarations to a single file that's imported by your application's other \n    JSP files. For `edit_entry.jsp`, you need these imports to access the \n    portlet tags that create URLs and the Alloy tags that create the form. \n\n4.  Next, you need a scriptlet that helps determine the function the user\n    accessed. You named this JSP `edit_entry.jsp` because it's used both for\n    adding and editing. Add this scriptlet to add logic for determining which\n    function the user wants: \n\n    ```markup\n    <% \n\n    long entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\n    GuestbookEntry entry = null;\n    if (entryId > 0) {\n      entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n    }\n\n    long guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n    %>\n    ```\n\n    If an `entryId` greater than `0` is found in the request, editing\n    a `GuestbookEntry` is assumed. Otherwise, the user is adding. \n\n5.  You'll create two URLs: one in the next step to submit the form and one in\n    this step to go back to `view.jsp`. To create the URL to go back to\n    `view.jsp`, add the following tag below the first line you added: \n\n    ```markup\n    <portlet:renderURL var=\"viewURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\"></portlet:param>\n    </portlet:renderURL>\n    ```\n\nNext, you must create a new URL for submitting the form. This is a different\nkind of URL, for it triggers a portlet action.\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/04-building-the-web-front-end/09-forms-and-action-urls.markdown",
    "content": "---\nheader-id: forms-and-action-urls\n---\n\n# Forms and Action URLs\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Building the Web Front-End/p><p>Step 8 of 11</p>\n</div>\n\nRecall that portlets run in a portion of a page, and a page can contain multiple \nportlets. Because of this, portlets have *phases* of operation. Here, you'll \nlearn about the most important two. The first phase is the one you've already \nused: the *render* phase. All this means is that the portlet draws itself, using \nthe JSPs you write for it. \n\nThe other phase is called the *action* phase. This phase runs once, when a user\ntriggers a portlet action. The portlet performs whatever action the user\ntriggered, such as performing a search or adding a record to a database. Then\nthe portlet goes back to the render phase and re-renders itself according to its\nnew state.\n\n## Action URLs\n\nTo save a guestbook entry, you must trigger a portlet action. For this, you'll\ncreate an action URL.\n\nAdd the following tag in `edit_entry.jsp` after the closing \n`</portlet:renderURL>` tag: \n\n```markup\n<portlet:actionURL name=\"addEntry\" var=\"addEntryURL\" />\n```\n\nYou now have the two required URLs for your form. \n\n## Forms\n\nThe form for creating guestbook entries has three fields: one for the name of the\nperson submitting the entry, one for the person's email address, and one for the\nentry itself. \n\nAdd the following tags to the end of your `edit_entry.jsp` file: \n\n```markup\n<aui:form action=\"<%= addEntryURL %>\" name=\"<portlet:namespace />fm\">\n\n<aui:model-context bean=\"<%= entry %>\" model=\"<%= GuestbookEntry.class %>\" />\n\n\t<aui:fieldset>\n\n\t\t<aui:input name=\"name\" />\n\t\t<aui:input name=\"email\" />\n\t\t<aui:input name=\"message\" />\n\t\t<aui:input name=\"entryId\" type=\"hidden\" />\n\t\t<aui:input name=\"guestbookId\" type=\"hidden\" value='<%= entry == null ? guestbookId : entry.getGuestbookId() %>'/>\n\n\t</aui:fieldset>\n\n\t<aui:button-row>\n\n\t\t<aui:button type=\"submit\"></aui:button>\n\t\t<aui:button type=\"cancel\" onClick=\"<%= viewURL.toString() %>\"></aui:button>\n\n\t</aui:button-row>\n</aui:form>\n```\n\nLiferay uses its Alloy UI tag library to create forms. \n\nSave `edit_entry.jsp` and redeploy your application. If you refresh the page and\nclick the *Add Entry* button, your form appears. If you click the *Cancel*\nbutton, you go back to `view.jsp`, but don't try the *Save* button yet. You\nhaven't yet created the action that saves a guestbook entry, so clicking *Save*\nproduces an error.\n\n![Figure 1: This is the Guestbook application's form for adding entries.](../../../images/first-guestbook-portlet-edit-entry.png)\n\nImplementing portlet actions (what happens when the user clicks *Save* or\n*Delete*) is your next task. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/04-building-the-web-front-end/10-implementing-portlet-actions.markdown",
    "content": "---\nheader-id: implementing-portlet-actions\n---\n\n# Implementing Portlet Actions\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Building the Web Front-End</p><p>Step 9 of 11</p>\n</div>\n\nWhen users submit the form, your application stores the form data for display in \nthe guestbook. This is where you call the back-end you generated to store the\ndata for later retrieval in the database. \n\nTo make your portlet do anything other than re-render itself, you must implement \nportlet actions. An action defines some processing, usually based on user input, \nthat the portlet must perform before it renders itself. In the case of the \nguestbook portlet, the action you'll implement next saves a guestbook entry that \na user typed into the form. Saved guestbook entries can be retrieved and \ndisplayed later. \n\nSince you're using Liferay's MVC Portlet framework, you have an easy way to \nimplement actions. Portlet actions are implemented in the portlet class, which \nis the controller. In the form you just created, you made an action URL, and you\ncalled it `addEntry`. To create a portlet action, you create a method in the\nportlet class with the same name. `MVCPortlet` calls that method when a user\ntriggers its matching URL. \n\n## Creating an Add Entry Action\n\n1.  Open `GuestbookPortlet`. \n\n2.  Create a method with the following signature:\n\n    ```java\n    public void addEntry(ActionRequest request, ActionResponse response) {\n\n    }\n    ```\n\n3.  Press [CTRL]+[SHIFT]+O to organize imports and import the required\n    `javax.portlet.ActionRequest` and `javax.portlet.ActionResponse` classes.\n\nYou've now created a portlet action. It doesn't do anything, but at least you\nwon't get an error now if you submit your form. Next, you should make the\naction save the form data. \n\nThe following method implements adding a guestbook entry using the back-end you\ngenerated with Service Builder: \n\n```java\npublic void addEntry(ActionRequest request, ActionResponse response)\n            throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            GuestbookEntry.class.getName(), request);\n\n        String userName = ParamUtil.getString(request, \"name\");\n        String email = ParamUtil.getString(request, \"email\");\n        String message = ParamUtil.getString(request, \"message\");\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n        long entryId = ParamUtil.getLong(request, \"entryId\");\n\n    if (entryId > 0) {\n\n        try {\n\n            _guestbookEntryLocalService.updateGuestbookEntry(\n                serviceContext.getUserId(), guestbookId, entryId, userName,\n                email, message, serviceContext);\n\n            response.setRenderParameter(\n                \"guestbookId\", Long.toString(guestbookId));\n\n        }\n        catch (Exception e) {\n            System.out.println(e);\n\n            PortalUtil.copyRequestParameters(request, response);\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook/edit_entry.jsp\");\n        }\n\n    }\n    else {\n\n        try {\n            _guestbookEntryLocalService.addGuestbookEntry(\n                serviceContext.getUserId(), guestbookId, userName, email,\n                message, serviceContext);\n\n            response.setRenderParameter(\n                \"guestbookId\", Long.toString(guestbookId));\n\n        }\n        catch (Exception e) {\n            System.out.println(e);\n\n            PortalUtil.copyRequestParameters(request, response);\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook/edit_entry.jsp\");\n        }\n    }\n}\n```\n\n1.  Replace your existing `addEntry` method with the above method.\n\n2.  Press [CTRL]+[SHIFT]+O to organize imports.\n\nThis `addEntry` method gets the name, message, and email fields that the\nuser submits in the JSP and passes them to the service to be stored as entry\ndata. It also gets a \n[`ServiceContext`](/docs/7-2/frameworks/-/knowledge_base/f/understanding-servicecontext) \nso information about the request's current context can be retrieved, such as the\nID of the current user. The `if-else` logic checks whether there's an existing\n`entryId`. If there is, the `update` service method is called, and if not, the\n`add` service method is called. In both cases, it sets a render parameter with\nthe Guestbook ID so the application can display the guestbook's entries after\nthis one has been added. This is all done in `try...catch` statements. Note the\nsetting of the `mvcPath` render parameter to direct processing to the proper JSP\nbased on what happens. \n\n## Creating a Delete Entry Action\n\nNext, create an action that deletes an entry: \n\n```java\npublic void deleteEntry(ActionRequest request, ActionResponse response) throws PortalException {\n        long entryId = ParamUtil.getLong(request, \"entryId\");\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            GuestbookEntry.class.getName(), request);\n\n        try {\n\n            response.setRenderParameter(\n                \"guestbookId\", Long.toString(guestbookId));\n\n            _guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n        }\n\n        catch (Exception e) {\n            Logger.getLogger(GuestbookPortlet.class.getName()).log(\n                Level.SEVERE, null, e);\n        }\n}\n```\n\nThis action accepts an `entryId` from the request and calls the service to\ndelete it. \n\nBut there's something missing, isn't there? These methods expect a `guestbookId`\nto be in the request, so the `GuestbookEntry` can be connected to its\n`Guestbook`. You'll create that next, as well as a mechanism for viewing\nguestbook entries. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/04-building-the-web-front-end/11-displaying-guestbook-entries.markdown",
    "content": "---\nheader-id: viewing-guestbook-entries\n---\n\n# Displaying Guestbook Entries\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Building the Web Front-End</p><p>Step 10 of 11</p>\n</div>\n\nTo display guestbook entries, you must do the reverse of what you did to store\nthem: retrieve them the database, loop through them, and present them on the\npage. To do this, you must override the default MVC Portlet `render` method so\nyou can tell your portlet how to render itself.\n\n## Rendering the Portlet\n\n1.  Add the following `render` method to `GuestbookPortlet`:\n\n    ```java\n    @Override\n    public void render(RenderRequest renderRequest, RenderResponse renderResponse)\n        throws IOException, PortletException {\n\n        try {\n            ServiceContext serviceContext = ServiceContextFactory.getInstance(\n                Guestbook.class.getName(), renderRequest);\n\n            long groupId = serviceContext.getScopeGroupId();\n\n            long guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n            List<Guestbook> guestbooks = _guestbookLocalService.getGuestbooks(\n                groupId);\n\n            if (guestbooks.isEmpty()) {\n                Guestbook guestbook = _guestbookLocalService.addGuestbook(\n                    serviceContext.getUserId(), \"Main\", serviceContext);\n\n                guestbookId = guestbook.getGuestbookId();\n            }\n\n            if (guestbookId == 0) {\n                guestbookId = guestbooks.get(0).getGuestbookId();\n            }\n\n            renderRequest.setAttribute(\"guestbookId\", guestbookId);\n        }\n        catch (Exception e) {\n            throw new PortletException(e);\n        }\n\n        super.render(renderRequest, renderResponse);\n    }\n    ```\n\n    This `render` method checks for guestbooks in the current Site. If there aren't\n    any, it creates one. The `guestbookId` that it has (either the first one or one\n    that has been selected in functionality you haven't written yet) is set in the\n    request object so that it becomes the current guestbook.\n\n2.  Press [CTRL]+[SHIFT]+O to organize imports and then save the file.\n\n| Note: When you are prompted to choose imports, here are some guidelines:\n|\n| * Always use `org.osgi...` packages instead of `aQute.bnd...`\n|\n| * Generally use `java.util...` or `javax.portlet...` packages.\n|\n| * You never use `java.awt...` in this project.\n|\n| * Only use `com.liferay...` when it is for a Liferay specific implementation or\n|   your custom implementation of a concept.\n|\n| For example:\n|\n| * If you are given the choice between `javax.portlet.Portlet` and\n|     `com.liferay.portlet.Portlet` choose `javax.portlet.Portlet`.\n|\n| * If you are given the choice between `org.osgi.component` and\n|   `aQute.bnd.annotation.component` choose `org.osgi.component`\n|\n| If at some point you think you chose an incorrect import, but you're not sure\n| what it might be, you can erase all of the imports from the file and press\n| [CTRL]+[SHIFT]+O again and see if you can identify where you went wrong.\n\nNow that you have your controller preparing your data for display, your next\nstep is to implement the view so users can see guestbook entries.\n\n## Displaying Guestbook Entries\n\nLiferay's development framework makes it easy to loop through data and display\nit nicely to the end user. You'll use a Liferay UI construct called *Search\nContainer* to make this happen.\n\n1.  Replace the contents of `view.jsp` with this code:\n\n    ```markup\n    <%@include file=\"../init.jsp\"%>\n\n    <%\n    long guestbookId = Long.valueOf((Long) renderRequest\n            .getAttribute(\"guestbookId\"));\n    %>\n\n    <aui:button-row cssClass=\"guestbook-buttons\">\n\n        <portlet:renderURL var=\"addEntryURL\">\n            <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n            <portlet:param name=\"guestbookId\"\n                value=\"<%=String.valueOf(guestbookId)%>\" />\n        </portlet:renderURL>\n\n        <aui:button onClick=\"<%=addEntryURL.toString()%>\" value=\"Add Entry\"></aui:button>\n\n    </aui:button-row>\n\n    <liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntriesCount()%>\">\n    <liferay-ui:search-container-results\n        results=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(scopeGroupId.longValue(),\n                        guestbookId, searchContainer.getStart(),\n                        searchContainer.getEnd())%>\" />\n\n    <liferay-ui:search-container-row\n        className=\"com.liferay.docs.guestbook.model.GuestbookEntry\" modelVar=\"entry\">\n\n        <liferay-ui:search-container-column-text property=\"message\" />\n\n        <liferay-ui:search-container-column-text property=\"name\" />\n\n        <liferay-ui:search-container-column-jsp\n            align=\"right\"\n            path=\"/guestbook/entry_actions.jsp\" />\n\n    </liferay-ui:search-container-row>\n\n    <liferay-ui:search-iterator />\n\n    </liferay-ui:search-container>\n    ```\n\n2.  You've used a lot of new objects in this JSP, so you must declare them in\n    `init.jsp`. Replace the contents of `init.jsp` with this:\n\n    ```markup\n    <%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\"%>\n    <%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\"%>\n    <%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\"%>\n    <%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\"%>\n    <%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\"%>\n    <%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\"%>\n    <%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n    <%@ taglib uri=\"http://liferay.com/tld/security\" prefix=\"liferay-security\" %>\n\n    <%@ page import=\"java.util.List\" %>\n    <%@ page import=\"com.liferay.portal.kernel.util.ParamUtil\" %>\n    <%@ page import=\"com.liferay.portal.kernel.util.HtmlUtil\" %>\n    <%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n    <%@ page import=\"com.liferay.petra.string.StringPool\" %>\n    <%@ page import=\"com.liferay.portal.kernel.model.PersistedModel\" %>\n    <%@ page import=\"com.liferay.portal.kernel.dao.search.SearchEntry\" %>\n    <%@ page import=\"com.liferay.portal.kernel.dao.search.ResultRow\" %>\n    <%@ page import=\"com.liferay.docs.guestbook.model.Guestbook\" %>\n    <%@ page import=\"com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil\" %>\n    <%@ page import=\"com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil\" %>\n    <%@ page import=\"com.liferay.docs.guestbook.model.GuestbookEntry\" %>\n\n    <liferay-theme:defineObjects />\n\n    <portlet:defineObjects />\n    ```\n\nMany of these objects, such as `HtmlUtil`, `ParamUtil`, and `StringPool`, are\nLiferay helper utilities that enable you with a single line of code do things\nlike extract parameters, escape data, or provide `String`s that otherwise have\nto be escaped.\n\nSave your work.\n\n## Creating an Actions JSP\n\nActions can be performed on your entities once they're stored. Users who enter\nGuestbook entries may wish to edit them or delete them. Now you'll provide that\nfunctionality.\n\n1.  Right-click on the `src/main/resources/META-INF/resources/guestbook` folder\n    and select *New* &rarr; *File*.\n\n2.  Name the file `entry_actions.jsp`.\n\n3.  Paste the following code into the file:\n\n    ```markup\n    <%@include file=\"../init.jsp\"%>\n\n    <%\n    String mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n    ResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);\n\n    GuestbookEntry entry = (GuestbookEntry)row.getObject();\n    %>\n\n    <liferay-ui:icon-menu>\n\n            <portlet:renderURL var=\"editURL\">\n                <portlet:param name=\"entryId\"\n                    value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n                <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n            </portlet:renderURL>\n\n            <liferay-ui:icon image=\"edit\" message=\"Edit\"\n                url=\"<%=editURL.toString() %>\" />\n\n            <portlet:actionURL name=\"deleteEntry\" var=\"deleteURL\">\n                <portlet:param name=\"entryId\"\n                    value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n                <portlet:param name=\"guestbookId\"\n                    value=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n            </portlet:actionURL>\n\n            <liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n    </liferay-ui:icon-menu>\n    ```\n\nYou may have noticed this JSP was included in the Search Container rows in your\n`view.jsp`. As the Search Container loops through Guestbook entries, this JSP\ngenerates an Actions button for each of them containing two functions: a call to\nyour `addEntry` method (which both adds and edits) and a call to your\n`deleteEntry` method. Both calls supply the current `guestbookId` and `entryId`\nparameters so the Action method has everything it needs to call the service\nmethod that does the work.\n\nAwesome! You've now completed the first iteration of your Guestbook application.\n\n![Figure 1: You have a form to enter information.](../../../images/guestbook-prototype-form.png)\n\n![Figure 2: Submitted entries are displayed here.](../../../images/guestbook-prototype-container.png)\n\n\nNext you'll review what's been done so far, and you'll deploy and test your\napplication.\n\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/04-building-the-web-front-end/12-fitting-it-all-together.markdown",
    "content": "---\nheader-id: fitting-it-all-together\n---\n\n# Fitting it All Together\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Building the Web Front-End</p><p>Step 11 of 11</p>\n</div>\n\nYou've created a complete data-driven application from the back-end to the \ndisplay. It's a great time to review how everything connects together. \n\n## The Back-End\n\nThe first thing you did was generate back-end services for your application\nusing Liferay's object-relational mapper, Service Builder. \n\n![Figure 1: Service Builder makes generating your database entities and your Java objects a snap.](../../../images/service-builder-guestbook.png)\n\nUsing a single `service.xml` file, you generated your object model, SQL to\ncreate your tables, a persistence layer to perform all CRUD operations on your\ndata, and a service layer for your business logic. Then you added business logic\nto your service layer to expose the persistence layer's functionality according\nto your business rules. \n\nWith all of that ready, it was time to build a front-end client. \n\n## The Front-End\n\nOnce you completed your back-end, you decided to develop a web front-end\nusing Liferay's MVC Portlet framework. You generated a Liferay MVC Portlet\nproject and used its Model View Controller development paradigm to implement\na user interface for your back-end functionality. \n\n![Figure 2: The controller directs page flow in an MVC Portlet application.](../../../images/guestbook-mvc-diagram-1.png)\n\nFirst, you created a default view in `view.jsp`. You created a button there that\nlinks to `edit_entry.jsp`, which is used for both adding and editing Guestbook\nentries (though you haven't implemented editing yet). Here, you created a form\nto capture information from Users adding Guestbook entries. The form's Action\nURL directs processing to your controller's portlet action of the same name.\nThis action extracts the data from the form (in the `ActionRequest` object) and\npasses it to the business logic you created in your service layer. \n\nAfter the business logic runs, your controller passes processing back to\n`view.jsp`, where the new Guestbook entry is displayed. \n\nNow that you've built the application and you can see a clear picture of how it \nall works, it's time to test it. \n\n## Deploying and Testing the Application\n\n1.  Drag and drop the `guestbook-api` module onto the server.\n\n2.  Drag and drop the `guestbook-service` module onto the server.\n\n3.  Look for the STARTED messages from the console. \n\n4.  Go to your @product@ instance at `localhost:8080` in your browser to test \n    your updated application. \n\n5.  Like you did before, use your administrative account to remove the Guestbook\n    from the page and add it again. \n\n6.  Click *Add Entry*.\n\n7.  Enter a *Name*, *Message*, and *Email Address*.\n\n8.  Click *Submit*.\n\n9.  Verify that your entry appears.\n\n![Figure 3: Your first guestbook and entry appears. Nice job!](../../../images/guestbook-entry-test.png)\n\n## What's Next?\n\nYou've created a working web application and deployed it on @product@. If you've \ncreated web applications before, though, you know that it's missing some \nimportant features: security, front-end validation, and an interface for \nadministrators to create multiple guestbooks per portlet. In the next section, \nyou'll begin adding these features. \n\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/05-writing-admin-portlet/01-writing-the-guestbook-admin-application-intro.markdown",
    "content": "---\nheader-id: writing-an-administrative-portlet\n---\n\n# Writing an Administrative Portlet\n\n[TOC levels=1-4]\n\nThe Guestbook application lets users add and view guestbook entries. The\napplication's back-end, however, is much more powerful. It can support many\nguestbooks and their associated entries. But at this point, there's no UI to\nsupport these added features. When you create this UI, you must also make sure\nthat only administrators can add guestbooks. \n\nTo accomplish this, you'll create a Guestbook Admin portlet and place it in \n@product@'s administrative interface---specifically, within the Content menu. \nThis way, the Guestbook Admin portlet is accessible only to Site Administrators, \nwhile users use the Guestbook portlet to create entries. \n\nIn short, this is a simple application with a simple interface: \n\n![Figure 1: The Guestbook Admin portlet lets administrators manage Guestbooks.](../../../images/admin-app-start.png)\n\nIf you get stuck, [source code](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/guestbook/05-admin-portlet)\nfor this step is provided. \n\nAre you ready to begin? \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/creating-the-classes\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/05-writing-admin-portlet/02-creating-the-classes.markdown",
    "content": "---\nheader-id: creating-the-classes\n---\n\n# Creating the Classes\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Writing the Guestbook Admin App</p><p>Step 1 of 6</p>\n</div>\n\nBecause the Guestbook and Guestbook Admin applications should be bundled\ntogether, you'll create the new application inside the `guestbook-web` project.\nIf you disagree with this design decision, you can create a separate project for\nGuestbook Admin; the project template you'd use is *panel-app*. For now,\nhowever, it's better to go through the process manually to learn how it all\nworks: \n\n1.  Right-click the `com.liferay.docs.guestbook.portlet` package in the\n    `guestbook-web` project and select *New* &rarr; *Class*. \n\n2.  Name the class `GuestbookAdminPortlet`. \n\n3.  Click *Browse* next to the Superclass and search for `MVCPortlet`. Click it\n    and select *OK*. \n\n4.  Click *Finish*. \n\nYou now have your Guestbook Admin application's portlet class. For an \nadministrative application, however, you need at least one more component. \n\n## Panels and Categories\n\nAs described in the \n[product menu tutorial](/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu), \nthere are three sections of the product menu as illustrated below. \n\n![Figure 1: The product menu is split into three sections: the Control Panel, the User menu, and the Sites menu.](../../../images/product-menu-parts.png)\n\nEach section is called a *panel category*. A panel category can hold various \nmenu items called *panel apps*. In the illustration above, the Sites menu is \nopen to reveal its panel apps and categories (yes, you can nest them). \n\nThe most natural place for the Guestbook Admin portlet is in the *Content\n& Data* panel category with @product@'s other content-based apps. This\nintegrates it nicely in the spot where Site administrators expect it to be. This\nalso means you don't have to create a new category for it: you can just create\nthe panel entry, which is what you'll do next. If you'd like to learn more about\npanel categories and apps after this, see the \n[product menu tutorial](/docs/7-2/customization/-/knowledge_base/c/customizing-the-product-menu)\nand the \n[control menu tutorial](/docs/7-2/customization/-/knowledge_base/c/customizing-the-control-menu).\n\nFollow these steps to create the panel entry for the Guestbook Admin portlet: \n\n1.  Add the dependency you need to extend @product@'s panel categories and apps. \n    To do this, open `guestbook-web`'s `build.gradle` file and add these \n    dependencies: \n\n    ```groovy\n    compileOnly group: \"com.liferay\", name: \"com.liferay.application.list.api\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\"\n    ```\n\n2.  After saving the file, right-click `guestbook-web` and select *Gradle*\n    &rarr; *Refresh Gradle Project*. \n\n3.  Right-click `src/main/java` in the `guestbook-web` project and select *New* \n    &rarr; *Package*. Name the package \n    `com.liferay.docs.guestbook.application.list` and click *Finish*. \n\n4.  Right-click your new package and select *New* &rarr; *Class*. Name the class \n    `GuestbookAdminPanelApp`.\n \n5.  Click *Browse* next to Superclass, search for \n    `BasePanelApp`, select it, and click *OK*. Then click *Finish*. \n\nGreat! You've created the classes you need, and you're ready to begin working on\nthem. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/05-writing-admin-portlet/03-updating-metadata.markdown",
    "content": "---\nheader-id: adding-metadata\n---\n\n# Adding Metadata\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Writing the Guestbook Admin App</p><p>Step 2 of 6</p>\n</div>\n\nNow that you've generated the classes, you must turn them into OSGi components.\nRemember that because components are container-managed objects, you must provide\nmetadata that tells @product@'s OSGi container how to manage their life cycles. \n\nFollow these steps:\n\n1.  Add the following portlet key to the `GuestbookPortletKeys` class (it's in\n    the `com.liferay.docs.guestbook.constants` package):\n\n    ```java\n    public static final String GUESTBOOK_ADMIN =\n      \"com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet\";\n    ```\n\n    Save the file. \n\n2.  Open the `GuestbookAdminPortlet` class and add the `@Component` annotation \n    immediately above the class declaration:\n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n                \"com.liferay.portlet.display-category=category.hidden\",\n                \"com.liferay.portlet.scopeable=true\",\n                \"javax.portlet.display-name=Guestbooks\",\n                \"javax.portlet.expiration-cache=0\",\n                \"javax.portlet.init-param.portlet-title-based-navigation=true\",\n                \"javax.portlet.init-param.template-path=/\",\n                \"javax.portlet.init-param.view-template=/guestbook_admin/view.jsp\",\n                \"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN,\n                \"javax.portlet.resource-bundle=content.Language\",\n                \"javax.portlet.security-role-ref=administrator\",\n                \"javax.portlet.supports.mime-type=text/html\",\n                \"com.liferay.portlet.add-default-resource=true\"\n        },\n        service = Portlet.class\n    )\n    ```\n\n3.  Hit [CTRL]+[SHIFT]+O to add the `javax.portlet.Portlet` and other imports. \n\nThere are only a few new things here. Note the value of the\n`javax.portlet.display-name` property: `Guestbooks`. This is the name that\nappears in the Site menu. Also note the value of the `javax.portlet.name`\nproperty: `+ GuestbookPortletKeys.GUESTBOOK_ADMIN`. This specifies the portlet's\ntitle via the `GUESTBOOK_ADMIN` portlet key that you just created. \n\nPay special attention to the following metadata property:\n\n```properties\ncom.liferay.portlet.display-category=category.hidden\n```\n\nThis is the same property you used before with the Guestbook portlet. You placed\nthat portlet in the Social category. The value `category.hidden` specifies a \nspecial category that doesn't appear anywhere. The Guestbook Admin portlet goes\nhere because you don't want users adding it to a page. This prevents them from\ndoing that. \n\nNext, you can configure the Panel app class. Follow these steps:\n\n1.  Open the `GuestbookAdminPanelApp` class and add the `@Component` annotation \n    immediately above the class declaration: \n\n    ```java\n    @Component(\n        immediate = true,\n        property = {\n            \"panel.app.order:Integer=300\",\n            \"panel.category.key=\" + PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT\n        },\n        service = PanelApp.class\n    )\n    ```\n\n    The `panel.category.key` metadata property determines where to place the\n    Guestbook Admin portlet in the Product Menu. Remember that the Product Menu \n    is divided into three main sections: the Control Panel, the User Menu, and \n    the Site Administration area. The value of the `panel.category.key` property \n    is `PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT`, which means Guestbook \n    Admin is in *Site Builder* &rarr; *Content & Data*. The key is provided by \n    [the `PanelCategoryKeys` class](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/application-list/application-list-api/src/main/java/com/liferay/application/list/constants/PanelCategoryKeys.java). \n    The `panel.app.order` value determines the rank for the Guestbook Admin \n    portlet in the list. \n\n2.  Finally, update the class to use the proper name and portlet keys:\n\n    ```java\n    public class GuestbookAdminPanelApp extends BasePanelApp {\n\n        @Override\n        public String getPortletId() {\n            return GuestbookPortletKeys.GUESTBOOK_ADMIN;\n        }\n\n        @Override\n        @Reference(\n            target = \"(javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN + \")\",\n            unbind = \"-\"\n        )\n        public void setPortlet(Portlet portlet) {\n            super.setPortlet(portlet);\n        }\n\n    }\n    ```\n\n3.  Hit [CTRL]+[SHIFT]+O to organize imports. This time, import\n    `com.liferay.portal.kernel.model.Portlet` instead of \n    `javax.portlet.Portlet`. \n\nNow that the configuration is out of the way, you're free to implement the \napp's functionality: adding, editing, and deleting Guestbooks. That's the next \nstep. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/05-writing-admin-portlet/04-updating-your-service-layer.markdown",
    "content": "---\nheader-id: updating-your-service-layer\n---\n\n# Updating Your Service Layer\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Writing the Guestbook Admin App</p><p>Step 3 of 6</p>\n</div>\n\nEarlier, you wrote an `addGuestbook` service method in\n`GuestbookLocalServiceImpl` and only used it to add a default guestbook. To\nhave full functionality over guestbooks, you must also add methods for updating\nand deleting guestbooks, as well as for returning the number of guestbooks in\na Site. \n\n## Adding Guestbook Service Methods\n\nRemember that when working with Service Builder, you define your service in the\n`*Impl` classes. After you add, remove, or change the signature of a method in\nan `*Impl` class, you must run Service Builder. Service Builder updates the\naffected interfaces and any other generated code. \n\nFollow these steps to add the required guestbook service methods: \n\n1.  Go to the `guestbook-service` project and open \n    `GuestbookLocalServiceImpl.java` in the \n    `com.liferay.docs.guestbook.service.impl` package. Add the following method \n    for updating a guestbook: \n\n    ```java\n    public Guestbook updateGuestbook(long userId, long guestbookId,\n        String name, ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Date now = new Date();\n\n            validate(name);\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            User user = userLocalService.getUser(userId);\n\n            guestbook.setUserId(userId);\n            guestbook.setUserName(user.getFullName());\n            guestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n            guestbook.setName(name);\n            guestbook.setExpandoBridgeAttributes(serviceContext);\n\n            guestbookPersistence.update(guestbook);\n\n            return guestbook;\n    }\n    ```\n\n    The `updateGuestbook` method retrieves the `Guestbook` by its ID, replaces \n    its data with what the user entered, and then calls the persistence layer to \n    save it back to the database. \n\n2.  Next, add the following method for deleting a guestbook: \n\n    ```java\n\tpublic Guestbook deleteGuestbook(long guestbookId,\n                    ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            List<GuestbookEntry> entries = _guestbookEntryLocalService.getGuestbookEntries(\n                            serviceContext.getScopeGroupId(), guestbookId);\n\n            for (GuestbookEntry entry : entries) {\n                    _guestbookEntryLocalService.deleteGuestbookEntry(entry.getEntryId());\n            }\n\n            guestbook = deleteGuestbook(guestbook);\n\n            return guestbook;\n    }\n    ```\n\n    It's important to consider what should happen if you delete a guestbook that\n    has existing entries. If you only deleted the guestbook, the guestbook's \n    orphaned entries would still exist in the database. Your `deleteGuestbook`\n    service method makes a service call to delete a guestbook's entries before\n    deleting that guestbook. This way, guestbook entries are never orphaned. \n\n3.  Add a reference to the `GuestbookEntry` local service, so it can be injected\n    and used by the `deleteGuestbook` method: \n\n    ```java\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n    ```\n\n    By convention, Liferay adds these to the bottom of the class. \n\n4.  Use [CTRL]+[SHIFT]+O to update your imports, choosing\n    `com.liferay.portal.kernel.exception.SystemException`, and then save\n    `GuestbookLocalServiceImpl.java`.\n\n5.  In the Gradle Tasks pane on the right side in Liferay @ide@, run Service \n    Builder by opening the `guestbook-service` module and double-clicking \n    `buildService`. \n\n| **Note:** If you prefer, you can use [Blade CLI](/docs/7-2/reference/-/knowledge_base/r/blade-cli) \n| to run your Gradle tasks. If you have Blade CLI installed, go to the\n| `guestbook-service` folder on your CLI and enter the command `blade gw\n| buildService`. This runs Service Builder to build your services outside of\n| Eclipse. \n\nNow that you've finished updating the service layer, it's time to work on the\nGuestbook Admin portlet itself. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/05-writing-admin-portlet/05-defining-portlet-actions.markdown",
    "content": "---\nheader-id: defining-portlet-actions\n---\n\n# Defining Portlet Actions\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Writing the Guestbook Admin App</p><p>Step 4 of 6</p>\n</div>\n\nThe Guestbook Admin portlet now needs action methods for adding, updating, and\ndeleting guestbooks. As with the Guestbook portlet, action methods call the\ncorresponding service methods. Note that since your services all run in the same\ncontainer, any application can call the Guestbook services. This is an advantage\nof @product@'s OSGi-based architecture: different applications or modules can\ncall services published by other modules. If a service is published, it can be\nused via `@Reference`. You'll take advantage of this here in the Guestbook Admin\nportlet to consume one of the same services consumed by the Guestbook portlet\n(the `addGuestbook` service). \n\n## Adding Three Portlet Actions\n\nThe Guestbook Admin portlet must let administrators add, update, and delete \n`Guestbook` objects. You'll create portlet actions to meet these requirements. \nOpen `GuestbookAdminPortlet.java` and follow these steps: \n\n1.  Add the following action method and instance variables needed for adding a\n    new guestbook:\n\n    ```java\n    public void addGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n\n        try {\n            _guestbookLocalService.addGuestbook(\n                serviceContext.getUserId(), name, serviceContext);\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n    \n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n    ```\n \n    Since `addGuestbook` is a portlet action method, it takes `ActionRequest` \n    and `ActionResponse` parameters. To make the service call to add a new \n    guestbook, the guestbook's name must be retrieved from the request. The \n    `serviceContext` must also be retrieved from the request and passed as an\n    argument in the service call. If an exception is thrown, you should display \n    the Add Guestbook form and not the default view. That's why you add this \n    line in the `catch` block: \n\n    ```java\n    response.setRenderParameter(\"mvcPath\",\n            \"/guestbook_admin/edit_guestbook.jsp\");\n    ```\n\n    Later, you'll use this for field validation and to show error messages to \n    the user. Note that `/guestbook_admin/edit_guestbook.jsp` doesn't \n    exist yet; you'll create it in the next step. \n\n2.  Add the following action method for updating an existing guestbook:\n\n    ```java\n    public void updateGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.updateGuestbook(\n                serviceContext.getUserId(), guestbookId, name, serviceContext);\n\n        } catch (PortalException pe) {\n        \n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n    ```\n \n    This method retrieves the guestbook name, ID, and the `serviceContext` from\n    the request. The `updateGuestbook` service call uses the guestbook's ID to \n    identify the guestbook to update. If there's a problem with the service \n    call, the Guestbook Admin portlet displays the Edit Guestbook form again so \n    that the user can edit the form and resubmit:\n\n    ```java\n    response.setRenderParameter(\"mvcPath\",\n            \"/guestbook_admin/edit_guestbook.jsp\");\n    ```\n\n    Note that the Edit Guestbook form uses the same JSP as the Add Guestbook\n    form to avoid duplication of code. \n\n3.  Add the following action method for deleting a guestbook:\n\n    ```java\n    public void deleteGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.deleteGuestbook(guestbookId, serviceContext);\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n        }\n    }\n    ```\n\n    This method uses the service layer to delete the guestbook by its ID. Since \n    the `deleteGuestbook` action is invoked from the Guestbook Admin portlet's \n    default view, there's no need to set the `mvcPath` render parameter to point \n    to a particular JSP if there was a problem with the `deleteGuestbook` \n    service call. \n\n4.  Hit [CTRL]+[SHIFT]+O to organize imports. Import the logging classes from\n    `java.util`. Save the file. \n\nYou now have your service methods and portlet action methods in place. Before you \nimplement the Guestbook Admin portlet's user interface, you should update the\nGuestbook portlet so it can show users all the Guestbooks your administrators\nadd. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/05-writing-admin-portlet/06-adding-tabs-to-the-guestbook-portlet.markdown",
    "content": "---\nheader-id: adding-tabs-to-the-guestbook-portlet\n---\n\n# Adding Tabs to the Guestbook Portlet\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Writing the Guestbook Admin App</p><p>Step 5 of 6</p>\n</div>\n\nBefore you finish the Guestbook Admin portlet, you must prepare the Guestbook\nportlet's UI to display multiple Guestbooks. As administrators add Guestbooks\nusing the Guestbook Admin portlet, users must be able to choose which Guestbook\nthey want to sign. They'll do this using a series of tabs across the top: \n\n![Figure 1: Users can click a tab to choose which Guestbook to sign.](../../../images/guestbook-tabs.png)\n\nThis is surprisingly easy to do using Liferay's Alloy UI tag library: \n\n1.  Open `view.jsp` from the `src/main/resources/META-INF/resources/guestbook`\n    folder. \n\n2.  After the first Java snippet (the one that gets the `guestbookId` out of the\n    request), add this code: \n\n    ```markup\n\n    <aui:nav cssClass=\"nav-tabs\">\n\n        <%\n            List<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n                for (int i = 0; i < guestbooks.size(); i++) {\n\n                    Guestbook curGuestbook = (Guestbook) guestbooks.get(i);\n                    String cssClass = StringPool.BLANK;\n\n                    if (curGuestbook.getGuestbookId() == guestbookId) {\n                        cssClass = \"active\";\n                    }\n\n        %>\n\n        <portlet:renderURL var=\"viewPageURL\">\n            <portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\" />\n            <portlet:param name=\"guestbookId\"\n                value=\"<%=String.valueOf(curGuestbook.getGuestbookId())%>\" />\n        </portlet:renderURL>\n\n            \n        <aui:nav-item cssClass=\"<%=cssClass%>\" href=\"<%=viewPageURL%>\"\n            label=\"<%=HtmlUtil.escape(curGuestbook.getName())%>\" />\n\n        <%  \n                    }\n                \n        %>\n\n    </aui:nav>\n    ```\n\n3.  Save the file. \n\nThis code declares the AUI navigation tabs. Then a code scriptlet gets all the\nGuestbooks in this scope and loops through each one. As it examines them, it\nchecks to see if the one it's examining is the current Guestbook. If so, a CSS\nstyle called `active` is applied. \n\nAfter this, a new URL called `viewPageURL` is created that points to `view.jsp`\nwith a `guestbookId` parameter containing the current Guestbook in the loop.\nFinally, an `<aui:nav-item>` tag declares the markup for the tab, using the CSS\nclass, the URL containing the parameters to navigate to the new Guestbook, and\nthe name to label it. \n\nThe loop continues until all the retrieved Guestbooks have tabs. \n\nAwesome! You've updated the Guestbook portlet so it can display all the\nGuestbooks administrators add. Now it's time to provide a UI for your Guestbook\nAdmin portlet so they can do just that. \n\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/05-writing-admin-portlet/07-creating-a-user-interface.markdown",
    "content": "---\nheader-id: creating-a-user-interface\n---\n\n# Creating a User Interface\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Writing the Guestbook Admin App</p><p>Step 6 of 6</p>\n</div>\n\nIt's time to create the Guestbook Admin portlet's user interface. The portlet's \ndefault view has a button for adding new guestbooks. It must also display the \nguestbooks that already exist. \n\nEach guestbook's name appears next to an Actions button. The Actions button\nreveals options for editing the guestbook, configuring its permissions, or\ndeleting it. \n\n## Step 1: Creating the Default View\n\nThe Guestbook Admin portlet's user interface is made up of three JSPs: the\ndefault view, the Actions button, and the form for adding or editing a\nguestbook. \n\nCreate the default view first: \n\n1.  In `src/main/resources/META-INF/resources`, create a folder called\n    `guestbook_admin`, where you'll create your JSPs. \n \n2.  Create a file in this folder called `view.jsp` and fill it with this code: \n\n    ```markup\n    <%@include file=\"../init.jsp\"%>\n\n    <liferay-ui:search-container\n        total=\"<%= GuestbookLocalServiceUtil.getGuestbooksCount(scopeGroupId) %>\">\n        <liferay-ui:search-container-results\n            results=\"<%= GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId, \n                searchContainer.getStart(), searchContainer.getEnd()) %>\" />\n\n        <liferay-ui:search-container-row\n            className=\"com.liferay.docs.guestbook.model.Guestbook\" modelVar=\"guestbook\">\n\n            <liferay-ui:search-container-column-text property=\"name\" />\n                    \n            <liferay-ui:search-container-column-jsp\n                align=\"right\" \n                path=\"/guestbook_admin/guestbook_actions.jsp\" />\n            \n        </liferay-ui:search-container-row>\n\n        <liferay-ui:search-iterator />\n    </liferay-ui:search-container>\n\n    <aui:button-row cssClass=\"guestbook-admin-buttons\">\n        <portlet:renderURL var=\"addGuestbookURL\">\n            <portlet:param name=\"mvcPath\"\n                value=\"/guestbook_admin/edit_guestbook.jsp\" />\n            <portlet:param name=\"redirect\" value=\"<%= \"currentURL\" %>\" />\n        </portlet:renderURL>\n            \n        <aui:button onClick=\"<%= addGuestbookURL.toString() %>\"\n            value=\"Add Guestbook\" />\n    </aui:button-row>\n    ```\n \nFirst is the `init.jsp` include to gain access to the imports. \n\nNext is a button row with a single button for adding new guestbooks:\n`<aui:button-row cssClass=\"guestbook-admin-buttons\">`. The `cssClass` \nattribute specifies a custom CSS class for additional styling. The \n`<portlet:renderURL>` tag constructs a URL that points to the \n`edit_guestbook.jsp`. You haven't created this JSP yet, but you'll use it \nfor adding a new guestbook and editing an existing one. \n\nFinally, a Liferay search container displays the list of guestbooks. Three\nsub-tags define the search container: \n\n- `<liferay-ui:search-container-results>`\n- `<liferay-ui:search-container-row>` \n- `<liferay-ui:search-iterator>` \n\nThe `<liferay-ui:search-container-results>` tag's `results` attribute uses a \nservice call to retrieve the guestbooks in the scope. The `total` attribute \nuses another service call to get a count of guestbooks. \n\nThe `<liferay-ui:search-container-row>` tag defines what rows contain. In \nthis case, the `className` attribute defines \n`com.liferay.docs.guestbook.model.Guestbook`. The `modelVar` attribute \ndefines `guestbook` as the variable for the currently iterated guestbook. In \nthe search container row, two columns are defined. The \n`<liferay-ui:search-container-column-text property=\"name\" />` tag specifies \nthe first column. This tag displays text. Its `property=\"name\"` attribute \nspecifies that the text to be displayed is the current guestbook object's \n`name` attribute. The tag `<liferay-ui:search-container-column-jsp` \n`path=\"/guestbook_admin/guestbook_actions.jsp\" align=\"right\" />` \nspecifies the second (and last) column. This tag includes another JSP file \nwithin a search container column. Its `path` attribute specifies the path to \nthe JSP file that should be displayed: `guestbook_actions.jsp`. \n\nFinally, the `<liferay-ui:search-iterator />` tag iterates through and \ndisplays the list of guestbooks. Using Liferay's search container makes the \nGuestbook Admin portlet look like a native @product@ portlet. It also \nprovides built-in pagination so that your portlet can automatically display \nlarge numbers of guestbooks on one Site. \n\n## Step 2: Creating the Actions Button\n\nNow create the `guestbook_actions.jsp` file that displays the\nlist of possible actions for each guestbook. \n\nCreate a new file called `guestbook_actions.jsp` in your project's \n`/guestbook_admin` folder. Paste in this code: \n\n```markup\n<%@include file=\"../init.jsp\"%>\n\n<%\n    String mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n    ResultRow row = (ResultRow) request\n                    .getAttribute(\"SEARCH_CONTAINER_RESULT_ROW\");\n\n    Guestbook guestbook = (Guestbook) row.getObject();\n%>\n\n<liferay-ui:icon-menu>\n\n    <portlet:renderURL var=\"editURL\">\n        <portlet:param name=\"guestbookId\"\n            value=\"<%=String.valueOf(guestbook.getGuestbookId()) %>\" />\n        <portlet:param name=\"mvcPath\"\n            value=\"/guestbook_admin/edit_guestbook.jsp\" />\n    </portlet:renderURL>\n\n    <liferay-ui:icon image=\"edit\" message=\"Edit\"\n            url=\"<%=editURL.toString() %>\" />\n\n    <portlet:actionURL name=\"deleteGuestbook\" var=\"deleteURL\">\n            <portlet:param name=\"guestbookId\"\n                value=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\" />\n    </portlet:actionURL>\n\n    <liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n</liferay-ui:icon-menu>\n```\n\nThis JSP comprises the pop-up actions menu that shows the actions users can\nperform on a guestbook: editing it or deleting it. First, `init.jsp` is included\nbecause it contains all the JSP imports. Because `guestbook_actions.jsp` is\nincluded for every Search Container row, it retrieves the guestbook in the\ncurrent iteration. The scriptlet grabs that guestbook so its ID can be supplied\nto the menu tags. \n\nThe `<liferay-ui:icon-menu>` tag dominates `guestbook_actions.jsp`. It's a \ncontainer for menu items, of which there are currently only two (you'll add \nmore later). The Edit menu item displays the Edit icon and the message \n*Edit*: \n\n```markup\n<liferay-ui:icon image=\"edit\" message=\"Edit\"\n        url=\"<%=editURL.toString() %>\" />\n```\n\nThe `editURL` variable comes from the `<portlet:renderURL var=\"editURL\">` \ntag with two parameters: `guestbookId` and `mvcPath`. The `guestbookId` \nparameter specifies the guestbook to edit (it's the one from the selected \nsearch container result row), and the `mvcPath` parameter specifies the Edit \nGuestbook form's path. \n\nThe Delete menu item displays a delete icon and the default message\n*Delete*:\n\n```markup\n<liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n```\n\nUnlike the `editURL`, which is a render URL that links to the \n`edit_guestbook.jsp`, the `deleteURL` is an action URL that invokes the \nportlet's `deleteGuestbook` action. The tag \n`<portlet:actionURL name=\"deleteGuestbook\" var=\"deleteURL\">` creates this \naction URL, which only takes one parameter: the `guestbookId` of the \nguestbook to be deleted. \n \n## Step 3: Creating the Edit Guestbook JSP\n\nNow there's just one more JSP file left to create: the `edit_guestbook.jsp` \nthat contains the form for adding a new guestbook and editing an existing \none. \n\nCreate a new file called `edit_guestbook.jsp` in your project's\n`/guestbook_admin` directory. Then add the following code to it:\n\n```markup\n<%@include file = \"../init.jsp\" %>\n\n<%\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n        \n        Guestbook guestbook = null;\n\n        if (guestbookId > 0) {\n                guestbook = GuestbookLocalServiceUtil.getGuestbook(guestbookId);\n        }\n%>\n\n<portlet:renderURL var=\"viewURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook_admin/view.jsp\" />\n</portlet:renderURL>\n\n<portlet:actionURL name='<%= guestbook == null ? \"addGuestbook\" : \"updateGuestbook\" %>' var=\"editGuestbookURL\" />\n\n<aui:form action=\"<%= editGuestbookURL %>\" name=\"fm\">\n\n        <aui:model-context bean=\"<%= guestbook %>\" model=\"<%= Guestbook.class %>\" />\n\n        <aui:input type=\"hidden\" name=\"guestbookId\"\n            value='<%= guestbook == null ? \"\" : guestbook.getGuestbookId() %>' />\n\n        <aui:fieldset>\n             <aui:input name=\"name\" />\n        </aui:fieldset>\n\n        <aui:button-row>\n             <aui:button type=\"submit\" />\n             <aui:button onClick=\"<%= viewURL %>\" type=\"cancel\"  />\n        </aui:button-row>\n</aui:form>\n```\n\nAfter the `init.jsp` import, you declare a `null` guestbook variable. If\nthere's a `guestbookId` parameter in the request, you use the `guestbookId` to\nretrieve the corresponding guestbook via a service call for edit.  Otherwise,\nyou know that you're adding a new guestbook.\n\nNext is a view URL that points to the Guestbook Admin portlet's default \nview. This URL is invoked if the user clicks *Cancel* on the Add Guestbook\nor Edit Guestbook form. After that, you create an action URL that invokes\neither the Guestbook Admin portlet's `addGuestbook` method or its \n`updateGuestbook` method, depending on whether the `guestbook` variable is \nnull. \n\nIf a guestbook is being edited, its name should appear in the form's name field.\nYou use the following tag to define a model of the guestbook that can be used in\nthe AlloyUI form: \n\n```markup\n<aui:model-context bean=\"<%= guestbook %>\" model=\"<%= Guestbook.class %>\" />\n```\n\nThe form is created with the following tag:\n\n```markup\n<aui:form action=\"<%= editGuestbookURL %>\" name=\"<portlet:namespace />fm\">\n```\n\nThe form is submitted via the `editGuestbookURL`, which calls the Guestbook\nAdmin portlet's `addGuestbook` or `updateGuestbook` action method, as discussed\nabove.\n\nThe `guestbookId` must appear on the form so that it can be submitted.\nThe user, however, doesn't need to see it. Thus, you specify \n`type=\"hidden\"`:\n\n```markup\n<aui:input type=\"hidden\" name=\"guestbookId\"\n        value='<%= guestbook == null ? \"\" : guestbook.getGuestbookId() %>' />\n```\n\nThe name, of course, should be editable by the user so it's not hidden.\n\nThe last item on the form is a button row with two buttons. The *Submit*\nbutton submits the form, invoking the `editGuestbookURL` which, in turn,\ninvokes either the `addGuestbook` or `updateGuestbook` method. The *Cancel*\nbutton invokes the `viewURL` which displays the default view.\n\nExcellent! You've now finished creating the UI for the Guestbook Admin portlet. \nIt should now match the figure below: \n\n![Figure 1: The Guestbook Admin portlet can add or edit guestbooks, configure their permissions, or delete them.](../../../images/admin-app-start.png)\n\nSave all your files and wait for redeploy. Test out the Guestbook Admin portlet!\nTry adding, editing, and deleting guestbooks. \n\n| **Note:** If you get \"Guestbook is unavailable\" errors, remove the modules\n| from the server, redeploy them, and test again. \n\nNow all the Guestbook application's primary functions work. There are still many\nmissing features, however. For example, if there's ever an error, users never\nsee it: all the code written so far just prints messages in the logs. Next,\nyou'll learn how to display those errors to the user. \n\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/06-displaying-message-errors/01-intro.markdown",
    "content": "---\nheader-id: displaying-messages-and-errors\n---\n\n# Displaying Messages and Errors\n\n[TOC levels=1-4]\n\nWhen users interact with your application, they perform tasks it defines, like \nsaving or editing things. The Guestbook application is no different. Your \napplication should provide feedback on these operations so users can know \nthey completed. Up to now, you've been placing this information in logs \nthat only administrators can access. Wouldn't it be better to show users these \nmessages? \n\n![Figure 1: You can use Liferay's APIs to display helpful messages.](../../../images/guestbook-status-message.png)\n\nThat's exactly what you'll do next, in three steps: \n\n1.  Create language keys for your messages.\n2.  Add the error messages to your action methods.\n3.  Report those error messages in your JSPs.\n\nIf you get stuck, [source code](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook)\nfor this step is provided. \n\nReady to get started? \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/creating-language-keys\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/06-displaying-message-errors/02-creating-language-keys.markdown",
    "content": "---\nheader-id: creating-language-keys\n---\n\n# Creating Language Keys\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Displaying Messages and Errors</p><p>Step 1 of 3</p>\n</div>\n\nModern applications should place messages and form field labels in a language\nkeys files that hold multiple language translations. Here, you'll learn how to\nprovide a *default* set of English language keys for your application. For more\ninformation on language keys and providing automatically translated language\nkeys, \n[see Generating Translations](/docs/7-2/frameworks/-/knowledge_base/f/automatically-generating-translations).\n\nLanguage keys are stored in the `Language.properties` file included in your \n`guestbook-web` module. `Language.properties` is the default, but you can create \nmore translations by appending the ISO-639 language code to the file name\n(e.g., `Language_es.properties` for Spanish or `Language_de.properties` for\nGerman). For now, stick to the default language keys. \n\nFollow these steps to create your language keys:\n\n1.  Open `/src/main/resources/content/Language.properties` in your \n    `guestbook-web` module. Remove the default keys in this file. \n\n2.  Paste in the following keys: \n\n    ```properties\n    entry-added=Entry added successfully.\n    entry-deleted=Entry deleted successfully.\n    guestbook-added=Guestbook added successfully.\n    guestbook-updated=Guestbook updated successfully.\n    guestbook-deleted=Guestbook deleted successfully.\n    ```\n\n3.  Save the file. \n\nYour messages are now in place, and your application can use them. Next, you'll \nadd them to your action methods. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/06-displaying-message-errors/03-adding-error-messages.markdown",
    "content": "---\nheader-id: adding-failure-and-success-messages\n---\n\n# Adding Failure and Success Messages\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Displaying Messages and Errors</p><p>Step 2 of 3</p>\n</div>\n\nTo display feedback to users properly, you must edit your portlet classes to use\n@product@'s `SessionMessages` and `SessionErrors` classes. These classes collect\nmessages that the view layer shows to the user through a tag. \n\nYou'll add these messages to code that runs when the user triggers a system \nfunction that can succeed or fail, such as creating, editing, or deleting a\nGuestbook or Guestbook entry. This happens in action methods. You must update\nthese methods to handle failure and success states in `GuestbookPortlet.java`\nand `GuestbookAdminPortlet.java`. Start by updating `addEntry` and `deleteEntry`\nin `GuestbookPortlet.java`: \n\n1.  Find the `addEntry` method in `GuestbookPortlet.java`. In the first\n    `try...catch` block's `try` section, and add the success message just \n    before the closing `}`: \n\n    ```java\n    SessionMessages.add(request, \"entryAdded\");\n    ```\n\n    This uses Liferay's `SessionMessages` API to add a success message whenever\n    a Guestbook is successfully added. It looks up the message you placed in\n    the `Language.properties` file and inserts the message for the key\n    `entry-added` (it automatically converts the key from camel case). \n\n2.  Below that, in the `catch` block, find the following code:\n\n    ```java\n    System.out.println(e);\n    ```\n\n3.  Beneath it, paste this line:\n\n    ```java\n    SessionErrors.add(request, e.getClass().getName());\n    ```\n\n    Now you not only log the message to the console, you also use the \n    `SessionErrors` object to show the message to the user. \n \nNext, do the same for the `deleteEntry` method: \n\n1.  After the logic to delete the entry, add a success message:\n\n    ```java\n    SessionMessages.add(request, \"entryDeleted\");\n    ```\n\n2.  Find the `Logger...` block of code in the `deleteEntry` method and after it,\n    paste this line: \n\n    ```java\n    SessionErrors.add(request, e.getClass().getName());\n    ```\n\n3.  Hit [CTRL]+[SHIFT]+O to import\n    `com.liferay.portal.kernel.servlet.SessionErrors` and\n    `com.liferay.portal.kernel.servlet.SessionMessages`. Save the file. \n\nWell done! You've added the messages to `GuestbookPortlet`. Now you must update \n`GuestbookAdminPortlet.java`:\n\n1.  Open `GuestbookAdminPortlet.java` and look for the same cues.\n\n2.  Add the appropriate success messages to the `try` section of the\n    `try...catch` in `addGuestbook`, `updateGuestbook`, and `deleteGuestbook`, \n    respectively: \n\n     ```java\n     SessionMessages.add(request, \"guestbookAdded\");\n\n     SessionMessages.add(request, \"guestbookUpdated\");\n\n     SessionMessages.add(request, \"guestbookDeleted\");\n     ```\n\n3.  In the `catch` section of those same methods, find `Logger.getlogger...` and \n    paste the `SessionErrors` block beneath it: \n\n    ```java\n    SessionErrors.add(request, pe.getClass().getName());\n    ```\n\n4.  Hit [CTRL]+[SHIFT]+O to import `SessionErrors` and `SessionMessages`. Save \n    the file. \n\nGreat! The controller now makes relevant and detailed feedback available. Now \nall you need to do is publish this feedback in the view layer. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/06-displaying-message-errors/04-adding-messages-jsp.markdown",
    "content": "---\nheader-id: adding-messages-to-jsps\n---\n\n# Adding Messages to JSPs\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Displaying Messages and Errors</p><p>Step 3 of 3</p>\n</div>\n\nAny messages the user should see are now stored in either `SessionMessages` or\n`SessionErrors`. Next, you'll make these messages appear in your JSPs. \n\n1.  In the `guestbook-web` module, open `guestbook/view.jsp`. Add the \n    following block of success messages to the top of the file, just below the \n    `init.jsp` include statement:\n\n    ```markup\n    <liferay-ui:success key=\"entryAdded\" message=\"entry-added\" />\n    <liferay-ui:success key=\"entryDeleted\" message=\"entry-deleted\" />\n    ```\n\n    This tag accesses what's stored in `SessionMessages`. It has two attributes. \n    The first is the `SessionMessages` key that you provided in the \n    `GuestbookPortlet.java` class's add and delete methods. The second looks up \n    the specified key in the `Language.properties` file. You could have \n    specified a hard-coded message here, but it's far better to provide a \n    localized key. \n\n2.  Now open `guestbook_admin/view.jsp`. Add the following block of \n    success messages in the same spot below the include: \n\n    ```markup\n    <liferay-ui:success key=\"guestbookAdded\" message=\"guestbook-added\" />\n    <liferay-ui:success key=\"guestbookUpdated\" message=\"guestbook-updated\" />\n    <liferay-ui:success key=\"guestbookDeleted\" message=\"guestbook-deleted\" />\n    ```\n\n![Figure 1: Now the message displays the value you specified in `Language.properties`.](../../../images/message-complete.png)\n\nCongratulations! You've added useful feedback for operations in your \napplication. \n\nYour application is shaping up, but it is missing another important feature:\npermissions. Next, you'll add permission checking for your guestbooks and\nentries. \n\nLook for the next part soon! \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/07-permissions/01-permissions-intro.markdown",
    "content": "---\nheader-id: using-resources-and-permissions\n---\n\n# Using Resources and Permissions\n\n[TOC levels=1-4]\n\nYour application is a great foundation to build on. What comes next? What if\nusers want a Guestbook that's limited to certain trusted people? What if you\ndon't want just any old user to go around editing or deleting people's Guestbook\nentries? To do that, you have to implement permissions. \n\nThankfully, with @product@ you don't have to write an entire permissions system \nfrom scratch: the framework provides a robust and well-tested permissions system \nthat you can implement quickly. You'll follow Liferay's well-defined process for \nimplementing permissions, called *DRAC*: \n\n- **Define** all resources and permissions\n- **Register** all defined resources in the permissions system\n- **Associate** permissions with resources\n- **Check** for permission before returning resources\n\nIf you get stuck, [source code](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook) \nfor this step is provided. \n\nReady to start? \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/defining-permissions\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/07-permissions/02-defining-permissions.markdown",
    "content": "---\nheader-id: defining-permissions\n---\n\n# Defining Permissions\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Implementing Permissions</p><p>Step 1 of 5</p>\n</div>\n\n@product@'s permissions framework is configured declaratively, like Service \nBuilder. You define all your permissions in an XML file that by convention is \ncalled `default.xml` (but you could really call it whatever you want). Then you \nimplement permissions checks in the following places in your code: \n\n- In the view layer, when showing links or buttons to protected functionality\n- In the actions, before performing a protected action\n- Later, in your service, before calling the remote service\n\nYou should first define the permissions you want. To get started, think of your \napplication's use cases and how access to that functionality should be \ncontrolled: \n\n- The Add Guestbook button should be available only to administrators. \n\n- The Guestbook tabs should be filtered by permissions so administrators can \n  control who can see them. \n\n- To prevent anonymous users from spamming the guestbook, the Add Entry button \n  should be available only to Site members. \n\n- Users should be able to set permissions on their own entries. \n\nNow you're ready to create the permissions configuration. Objects in your \napplication (such as `Guestbook` and `GuestbookEntry`) are defined as\n*resources*, and *resource actions* manage how users can interact with those\nresources. There are therefore two kinds of permissions: portlet permissions and\nresource (or model) permissions. Portlet permissions protect access to global\nfunctions, such as *Add Entry*. If users don't have permission to access that\nglobal function, they're missing a portlet permission. Resource permissions\nprotect access to objects, such as `Guestbook` and `GuestbookEntry`. A user may\nhave permission to view one `GuestbookEntry`, view and edit another\n`GuestbookEntry`, and may not be able to access another `GuestbookEntry` at all.\nThis is due to a resource permission. \n\n![Figure 1: Portlet permissions and resource permissions cover different parts of the application.](../../../images/permission-types.png)\n\n## Defining Model Permissions\n\nFirst, create the permissions file in the `guestbook-service` project: \n\n1.  In the `/src/main/resources/META-INF` folder, create a subfolder called `resource-actions`.\n\n2.  Create a new file in this folder called `default.xml`.\n\n3.  Click the *Source* tab. Add the following `DOCTYPE` declaration to the top\n    of the file:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action  \n    Mapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n    ```\n\n4.  Place the following wrapper tags into your `default.xml` file, below the \n    `DOCTYPE` declaration: \n\n    ```xml\n    <resource-action-mapping>\n\n    </resource-action-mapping>\n    ```\n\n    You'll define your resource and model permissions inside these tags. \n\n5.  Next, place the permissions for your `com.liferay.docs.guestbook` \n    package between the `<resource-action-mapping>` tags: \n\n    ```xml\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n        </portlet-ref>\n        <root>true</root>\n        <permissions>\n            <supports>\n                <action-key>ADD_GUESTBOOK</action-key>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>ADD_ENTRY</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>ADD_GUESTBOOK</action-key>\n                <action-key>ADD_ENTRY</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n    ```\n\n    This defines the baseline configuration for the `Guestbook` and\n    `GuestbookEntry` entities. The supported actions are `ADD_GUESTBOOK` and\n    `ADD_ENTRY`. Site members can `ADD_ENTRY` by default, and guests can't\n    perform either action (but they can view). \n\n6.  Below that, but above the closing `</resource-action-mapping>`, place the\n    `Guestbook` model permissions: \n\n    ```xml\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook.model.Guestbook</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n        </portlet-ref>\n        <permissions>\n            <supports>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>DELETE</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>UPDATE</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>UPDATE</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n    ```\n \n    This defines the `Guestbook` specific actions, including adding, deleting, \n    updating, and viewing. By default, Site members and guests can view \n    guestbooks, but guests can't update them. \n\n7.  Below the `Guestbook` model permissions, but still above the closing \n    `</resource-action-mapping>`, place the `GuestbookEntry` model permissions: \n\n    ```xml\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook.model.GuestbookEntry</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n        </portlet-ref>\n        <permissions>\n            <supports>\n                <action-key>DELETE</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>UPDATE</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>UPDATE</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n    ```\n\n    This defines `GuestbookEntry` specific actions. By default, a Site member\n    can add or view a Guestbook entry, and a guest can only view a Guestbook\n    entry. \n\n8.  Save the file. \n\nNext, you must tell the framework where your permissions are defined. You'll\ndefine resource and model permissions in the module where your model is defined: \n\n1.  In `guestbook-service`'s `src/main/resources` folder, create a file called\n    `portlet.properties`. \n \n2.  In this file, place the following property: \n\n    ```properties\n    resource.actions.configs=META-INF/resource-actions/default.xml\n    ```\n\nThis property defines the name and location of your permissions definition file. \n\n## Defining Portlet Permissions\n\nYou now have permissions defined at the model level, but you must also define\nportlet permissions. These are managed in the `guestbook-web` module, which\ncontains the portlet class. Follow these steps to add the portlet permissions in\nthe `guestbook-web` module: \n\n1.  Create a subfolder called `resource-actions` in the\n    `src/main/resources/META-INF` folder. \n\n2.  Create a new file in this folder called `default.xml`.\n\n3.  Add the following `DOCTYPE` declaration to the top of the file:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action  \n    Mapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n    ```\n\n4.  Below the `DOCTYPE` declaration, add the following `resource-action-mapping` \n    tags: \n\n    ```xml\n    <resource-action-mapping>\n\n    </resource-action-mapping>\n    ```\n\n    You'll define your portlet permissions inside these tags. \n\n5.  Insert this block of code inside the `resource-action-mapping` tags:\n\n    ```xml\n    <portlet-resource>\n        <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n        <permissions>\n            <supports>\n                <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n                <action-key>CONFIGURATION</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n                <action-key>CONFIGURATION</action-key>\n            </guest-unsupported>\n        </permissions>\n    </portlet-resource>\n    ```\n\n    This defines the default permissions for the Guestbook Admin portlet. It \n    supports the actions `ACCESS_IN_CONTROL_PANEL`, `CONFIGURATION`, and `VIEW`. \n    While anyone can view the app, guests and Site members can't configure it or \n    access it in the Control Panel. Since it's a Control Panel portlet, this\n    effectively means that only administrators can access it. \n\n6.  Below the Guestbook Admin permissions, insert this block of code: \n\n    ```xml\n    <portlet-resource>\n        <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n        <permissions>\n            <supports>\n                <action-key>ADD_TO_PAGE</action-key>\n                <action-key>CONFIGURATION</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported />\n        </permissions>\n    </portlet-resource>\n    ```\n\n    This defines permissions for the Guestbook portlet. It supports the actions \n    `ADD_TO_PAGE`, `CONFIGURATION`, and `VIEW`. Site members and guests get the \n    `VIEW` permission by default. \n\n7.  Save the file. \n\n8.  In `guestbook-web`'s `src/main/resources` folder, create a file called \n    `portlet.properties`. \n\n9.  In this file, place the following property: \n\n    ```properties\n    resource.actions.configs=META-INF/resource-actions/default.xml\n    ```\n\n10. Save the file.\n\nGreat job! You've now successfully designed and implemented a permissions scheme \nfor your application. Next, you'll create the Java code to support permissions \nin the service layer. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/07-permissions/03-registering-permissions-database.markdown",
    "content": "---\nheader-id: registering-your-permissions-in-the-database\n---\n\n# Registering Your Permissions in the Database\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Implementing Permissions</p><p>Step 2 of 5</p>\n</div>\n\nThe last step introduced the concept of *resources*. Resources are data stored \nwith your entities that define how they can be accessed. For example, when the \nconfiguration in your `default.xml` files is applied to your application's \nentities in the database, resources are created. These resources are then used \nin conjunction with @product@'s permissions system to determine who can do what \nto the entities. \n\nTo use these resources, @product@ must know about them. To do that you\n*register* the resources with the system, both in the database and with the\nrunning permissions system in the OSGi container. \n\n@product@ provides a complete API---integrated with Service Builder---for\nmanaging resources. This API is injected into your implementation classes\nautomatically. To manage the resources, you need only call the API in the\nservice's add and delete methods. Follow these steps to do this in your\napplication. \n\n## Registering Guestbook Resources\n\n1.  In your `guestbook-service` module, open `GuestbookLocalServiceImpl.java` \n    from the `com.liferay.docs.guestbook.service.impl` package. \n\n2.  Just before the `addGuestbook` method's `return` statement, add this code: \n\n    ```java\n    resourceLocalService.addResources(user.getCompanyId(), groupId, userId,\n        Guestbook.class.getName(), guestbookId, false, true, true);\n    ```\n\n    Note that the `resourceLocalService` object is already there, ready for you \n    to use. This is one of several utilities that are injected automatically\n    by Service Builder into the base class your implementation class extends.\n    You'll see the rest in the future.\n\n    This code adds a resource to @product@'s database to correspond with your \n    entity (note that the `guestbookId` is included in the call). The three \n    booleans at the end are settings. The first is whether to add portlet \n    action permissions. This should only be `true` if the permission is for a \n    portlet resource. Since this permission is for a model resource (an entity), \n    it's `false`. The other two are settings for adding group and guest \n    permissions. If you set these to `true`, you'll add the default permissions \n    you defined in the permissions configuration file (`default.xml`) in the \n    previous step. Since you definitely want to do this, these booleans are set \n    to `true`. \n\n3.  Next, go to the `updateGuestbook` method. Add a similar bit of code in \n    between `guestbookPersistence.update(guestbook);` and the `return` \n    statement: \n\n    ```java\n    resourceLocalService.updateResources(serviceContext.getCompanyId(),\n        serviceContext.getScopeGroupId(), \n        Guestbook.class.getName(), guestbookId,\n        serviceContext.getModelPermissions());\n    ```\n\n4.  Now you'll do the same for `deleteGuestbook`. Add this code in between \n    `guestbook = deleteGuestbook(guestbook);` and the `return` statement:\n\n    ```java\n    resourceLocalService.deleteResource(serviceContext.getCompanyId(),\n        Guestbook.class.getName(), ResourceConstants.SCOPE_INDIVIDUAL,\n        guestbookId);\n    ```\n\n5.  Hit [CTRL]+[SHIFT]+O to organize the imports and save the file. \n\n## Registering Guestbook Entry Resources\n\n1.  Now you'll add resources for the `GuestbookEntry` entity. Open \n    `GuestbookEntryLocalServiceImpl.java` from the same package. For\n    `addGuestbookEntry`, add a line of code that adds resources for this entity,\n    just before the return statement: \n\n    ```java\n    resourceLocalService.addResources(user.getCompanyId(), groupId, userId,\n        GuestbookEntry.class.getName(), entryId, false, true, true);\n    ```\n\n2.  Find `updateEntry` and add its resource action, also just before \n    the `return` statement: \n\n    ```java\n    resourceLocalService.updateResources(\n          user.getCompanyId(), serviceContext.getScopeGroupId(), \n          GuestbookEntry.class.getName(), entryId, \n          serviceContext.getModelPermissions());\n    ```\n\n3.  For `deleteGuestbookEntry`, add this code just before the `return` statement\n    and just after the call to `guestbookEntryPersistence`: \n\n    ```java\n    resourceLocalService.deleteResource(\n       entry.getCompanyId(), GuestbookEntry.class.getName(),\n       ResourceConstants.SCOPE_INDIVIDUAL, entry.getEntryId());\n    ```\n\n4.  Hit Ctrl-Shift-O to add imports and save the file. \n\nThat's all it takes to add permissions resources to the database. Future\nentities added to the database are fully permissions-enabled. Note, however,\nthat entities you've already added to your Guestbook application in the portal\ndon't have resources and thus can't be protected by permissions. You'll fix this\nlater. Now you must register permissions with the permissions system, so it\nknows how to check for them. \n\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/07-permissions/04-registering-permissions-container.markdown",
    "content": "---\nheader-id: registering-permissions-with-container\n---\n\n# Registering Permissions with the Container\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Implementing Permissions</p><p>Step 3 of 5</p>\n</div>\n\nA running service checks permissions, but since the Guestbook portlet,\nGuestbooks, and Guestbook Entries are new to the system, it must be taught about\nthem. You do this by creating permissions registrar classes. These follow what\nyou did in `default.xml`: you need one for your portlet permissions and one for\neach of your entities. First, you must do a little reorganization. \n\n1.  In your API module, create a `GuestbookConstants` class in a new package\n    called `com.liferay.docs.guestbook.constants`: \n\n    ```java\n    package com.liferay.docs.guestbook.constants;\n\n    public class GuestbookConstants {\n        \n        public static final String RESOURCE_NAME = \"com.liferay.docs.guestbook\";\n\n    }\n    ```\n\n    The `RESOURCE_NAME` string must match exactly your resource name from\n    `default.xml`. You'll see why in a moment. \n\n2.  You have a `GuestbookPortletKeys` class in your web module. These keys must\n    now be accessible to all modules, so drag this class from the web module and\n    drop it into the new `com.liferay.docs.guestbook.constants` package in your\n    API module. \n\nNow you're ready to create your permissions registrar classes. \n\n1.  Open the `build.gradle` file in your `guestbook-service` module. \n\n2.  Add the following three dependencies and save the file: \n\n    ```groovy\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.function\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.model.adapter\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.reflect\"\n    ```\n\n3.  In your service module, create a package that by convention ends in\n    `internal.security.permission.resource`. \n\n4.  Create a class in this package called\n    `GuestbookModelResourcePermissionRegistrar` with the contents below. \n\n    ```java\n    package com.liferay.docs.guestbook.internal.security.permission.resource;\n\n    import java.util.Dictionary;\n\n    import org.osgi.framework.BundleContext;\n    import org.osgi.framework.ServiceRegistration;\n    import org.osgi.service.component.annotations.Activate;\n    import org.osgi.service.component.annotations.Component;\n    import org.osgi.service.component.annotations.Deactivate;\n    import org.osgi.service.component.annotations.Reference;\n\n    import com.liferay.docs.guestbook.constants.GuestbookConstants;\n    import com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\n    import com.liferay.docs.guestbook.model.Guestbook;\n    import com.liferay.docs.guestbook.service.GuestbookLocalService;\n    import com.liferay.exportimport.kernel.staging.permission.StagingPermission;\n    import com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n    import com.liferay.portal.kernel.security.permission.resource.ModelResourcePermissionFactory;\n    import com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\n    import com.liferay.portal.kernel.security.permission.resource.StagedModelPermissionLogic;\n    import com.liferay.portal.kernel.security.permission.resource.WorkflowedModelPermissionLogic;\n    import com.liferay.portal.kernel.service.GroupLocalService;\n    import com.liferay.portal.kernel.util.HashMapDictionary;\n    import com.liferay.portal.kernel.workflow.permission.WorkflowPermission;\n\n    @Component (immediate=true)\n    public class GuestbookModelResourcePermissionRegistrar {\n\n     @Activate\n        public void activate(BundleContext bundleContext) {\n            Dictionary<String, Object> properties = new HashMapDictionary<>();\n\n            properties.put(\"model.class.name\", Guestbook.class.getName());\n\n            _serviceRegistration = bundleContext.registerService(\n                ModelResourcePermission.class,\n                ModelResourcePermissionFactory.create(\n                    Guestbook.class, Guestbook::getGuestbookId,\n                    _guestbookLocalService::getGuestbook, _portletResourcePermission,\n                    (modelResourcePermission, consumer) -> {\n                        consumer.accept(\n                            new StagedModelPermissionLogic<>(\n                                _stagingPermission, GuestbookPortletKeys.GUESTBOOK,\n                                Guestbook::getGuestbookId));\n                        consumer.accept(\n                            new WorkflowedModelPermissionLogic<>(\n                                    _workflowPermission, modelResourcePermission,\n                                    _groupLocalService, Guestbook::getGuestbookId));\n                    }),\n                properties);\n        }\n\n        @Deactivate\n        public void deactivate() {\n            _serviceRegistration.unregister();\n        }\n\n        @Reference\n        private GuestbookLocalService _guestbookLocalService;\n\n        @Reference(target = \"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\")\n        private PortletResourcePermission _portletResourcePermission;\n\n        private ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n        @Reference\n        private StagingPermission _stagingPermission;\n\n        @Reference\n        private WorkflowPermission _workflowPermission;\n        \n        @Reference\n        private GroupLocalService _groupLocalService;\n    }\n    ```\n\nThis class registers a chain of permission logic classes for checking\npermissions for Guestbook entities. Since this functionality is the same for all\nentities, all that's necessary is to specify yours in addition to the standard\nLiferay ones for staging and workflow. Introspection is done on your entity by\nthe factory to create the necessary permissions service. You implemented the\nconstants class so you can specify the resource model name you defined in\n`default.xml`. The `model.class.name` is set so that any module needing this\nservice can find this model resource permission by its type. \n\nNow create the registrar for the `GuestbookEntry` entity: \n\n1.  Create a class in the same package called\n    `GuestbookEntryModelResourcePermissionRegistrar`. \n\n2.  The only difference between this class and the one above is that it operates\n    on `GuestbookEntry` entities instead of `Guestbook` entities (the imports\n    have been left off in the snippet below): \n\n    ```java\n    package com.liferay.docs.guestbook.internal.security.permission.resource;\n\n    @Component(immediate = true)\n    public class GuestbookEntryModelResourcePermissionRegistrar {\n\n     @Activate\n        public void activate(BundleContext bundleContext) {\n            Dictionary<String, Object> properties = new HashMapDictionary<>();\n\n            properties.put(\"model.class.name\", GuestbookEntry.class.getName());\n\n            _serviceRegistration = bundleContext.registerService(\n                ModelResourcePermission.class,\n                ModelResourcePermissionFactory.create(\n                    GuestbookEntry.class, GuestbookEntry::getEntryId,\n                    _guestbookEntryLocalService::getGuestbookEntry, _portletResourcePermission,\n                    (modelResourcePermission, consumer) -> {\n                        consumer.accept(\n                            new StagedModelPermissionLogic<>(\n                                _stagingPermission, GuestbookPortletKeys.GUESTBOOK,\n                                GuestbookEntry::getEntryId));\n                        consumer.accept(\n                            new WorkflowedModelPermissionLogic<>(\n                                    _workflowPermission, modelResourcePermission,\n                                    _groupLocalService, GuestbookEntry::getEntryId));\n                    }),\n                properties);\n        }\n\n        @Deactivate\n        public void deactivate() {\n            _serviceRegistration.unregister();\n        }\n\n        @Reference\n        private GuestbookEntryLocalService _guestbookEntryLocalService;\n\n        @Reference(target = \"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\")\n        private PortletResourcePermission _portletResourcePermission;\n\n        private ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n        @Reference\n        private StagingPermission _stagingPermission;\n\n        @Reference\n        private WorkflowPermission _workflowPermission;\n        \n        @Reference\n        private GroupLocalService _groupLocalService;\n    }\n    ```\n\n3.  Hit Ctrl-Shift-O to add the imports, and then save the file. \n\nFinally, create the registrar for the portlet permissions: \n\n1.  Create a class in the same package called\n    `GuestbookPortletResourcePermissionRegistrar`. \n\n2.  This class is simpler because you don't have to tell it how to retrieve\n    primary keys from any entity: \n\n    ```java\n    package com.liferay.docs.guestbook.internal.security.permission.resource;\n\n    @Component (immediate = true)\n    public class GuestbookPortletResourcePermissionRegistrar {\n        \n            @Activate\n        public void activate(BundleContext bundleContext) {\n            Dictionary<String, Object> properties = new HashMapDictionary<>();\n\n            properties.put(\"resource.name\", GuestbookConstants.RESOURCE_NAME);\n\n            _serviceRegistration = bundleContext.registerService(\n                PortletResourcePermission.class,\n                PortletResourcePermissionFactory.create(\n                    GuestbookConstants.RESOURCE_NAME,\n                    new StagedPortletPermissionLogic(\n                        _stagingPermission, GuestbookPortletKeys.GUESTBOOK)),\n                properties);\n        }\n\n        @Deactivate\n        public void deactivate() {\n            _serviceRegistration.unregister();\n        }\n\n        private ServiceRegistration<PortletResourcePermission> _serviceRegistration;\n\n        @Reference\n        private StagingPermission _stagingPermission;\n\n    }\n    ```\n\n3.  Type Ctrl-Shift-O to add the imports. Save the file. \n\nYou've now completed step two: the R in DRAC: registering permissions. Next,\nyou'll enable users to associate permissions with resources. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/07-permissions/05-assigning-permissions-to-resources.markdown",
    "content": "---\nheader-id: assigning-permissions-to-resources\n---\n\n# Assigning Permissions to Resources\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Implementing Permissions</p><p>Step 4 of 5</p>\n</div>\n\nYou've now defined your permissions and registered them in both the container\nand the database so permissions can be checked. Now you'll create a UI for users\nto assign permissions along with helper classes to make it easy to check\npermissions in the final step.\n\nHere's how it works. You have a permission, such as `ADD_ENTRY`, and a resource,\nsuch as a `Guestbook`. For a user to add an entry to a guestbook, you must check\nif that user has the `ADD_ENTRY` permission for that guestbook. Helper classes\nmake it easier to check permissions.\n\n## Creating a Guestbook Portlet Permission Helper\n\n1.  Right-click the `guestbook-web` module and select *New* &rarr;\n    *Package*. To follow Liferay's practice, name the package\n    `com.liferay.docs.guestbook.web.security.permission.resource`. This\n    is where you'll place your helper classes.\n\n2.  Right-click the new package and select *New* &rarr; *Class*. Name the class\n    `GuestbookPermission`.\n\n3.  Replace this class's contents with the following code:\n\n    ```java\n    package com.liferay.docs.guestbook.web.security.permission.resource;\n\n    import org.osgi.service.component.annotations.Component;\n    import org.osgi.service.component.annotations.Reference;\n\n    import com.liferay.docs.guestbook.constants.GuestbookConstants;\n    import com.liferay.portal.kernel.security.permission.PermissionChecker;\n    import com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\n\n    @Component(immediate=true)\n    public class GuestbookPermission {\n\n        public static boolean contains(PermissionChecker permissionChecker, long groupId, String actionId) {\n\n            return _portletResourcePermission.contains(permissionChecker, groupId, actionId);\n\n        }\n\n        @Reference(\n                target=\"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\",\n                unbind=\"-\"\n                )\n        protected void setPortletResourcePermission(PortletResourcePermission portletResourcePermission) {\n\n            _portletResourcePermission = portletResourcePermission;\n        }\n\n        private static PortletResourcePermission _portletResourcePermission;\n\n    }\n    ```\n\nThis class is a component defining one static method (so you don't have to\ninstantiate the class) that encapsulates the model you're checking permissions\nfor. Liferay's `PermissionChecker` class does most of the work: give it the\nproper resource and action, such as `ADD_ENTRY`, and it returns whether the\npermission exists or not.\n\nThere's only one method: a `check` method that throws an exception if the user\ndoesn't have permission.\n\n## Creating Model Permission Helpers\n\nNext, you'll create helpers for your two entities:\n\n1.  Create a class in the same package called `GuestbookModelPermission.java`.\n\n2.  Replace this class's contents with the following code:\n\n    ```java\n    package com.liferay.docs.guestbook.web.security.permission.resource;\n\n    import org.osgi.service.component.annotations.Component;\n    import org.osgi.service.component.annotations.Reference;\n\n    import com.liferay.docs.guestbook.model.Guestbook;\n    import com.liferay.portal.kernel.exception.PortalException;\n    import com.liferay.portal.kernel.security.permission.PermissionChecker;\n    import com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n\n    @Component(immediate = true)\n    public class GuestbookModelPermission {\n\n        public static boolean contains(\n                PermissionChecker permissionChecker, Guestbook guestbook, String actionId) throws PortalException {\n\n            return _guestbookModelResourcePermission.contains(permissionChecker, guestbook, actionId);\n        }\n\n        public static boolean contains(\n                PermissionChecker permissionChecker, long guestbookId, String actionId) throws PortalException {\n\n            return _guestbookModelResourcePermission.contains(permissionChecker, guestbookId, actionId);\n        }\n\n        @Reference(\n                target = \"(model.class.name=com.liferay.docs.guestbook.model.Guestbook)\",\n                unbind = \"-\")\n        protected void setEntryModelPermission(ModelResourcePermission<Guestbook> modelResourcePermission) {\n\n            _guestbookModelResourcePermission = modelResourcePermission;\n        }\n\n        private static ModelResourcePermission<Guestbook>_guestbookModelResourcePermission;\n\n    }\n    ```\n\nAs you can see, this class is similar to `GuestbookPermission`. The difference\nis that `GuestbookModelPermission` is for the model/resource permission, so you\nsupply the entity or its primary key (`guestbookId`).\n\nYour final class is almost identical to `GuestbookModelPermission`, but it's for\nthe `GuestbookEntry` entity. Follow these steps to create it:\n\n1.  Create a class in the same package called `GuestbookEntryPermission.java`.\n\n2.  Replace this class's contents with the following code:\n\n    ```java\n    package com.liferay.docs.guestbook.web.security.permission.resource;\n\n    import org.osgi.service.component.annotations.Component;\n    import org.osgi.service.component.annotations.Reference;\n\n    import com.liferay.docs.guestbook.model.GuestbookEntry;\n    import com.liferay.portal.kernel.exception.PortalException;\n    import com.liferay.portal.kernel.security.permission.PermissionChecker;\n    import com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n\n    @Component(immediate = true)\n    public class GuestbookEntryPermission {\n\n        public static boolean contains(\n                PermissionChecker permissionChecker, GuestbookEntry entry, String actionId) throws PortalException {\n\n            return _guestbookEntryModelResourcePermission.contains(permissionChecker, entry, actionId);\n        }\n\n        public static boolean contains(\n                PermissionChecker permissionChecker, long entryId, String actionId) throws PortalException {\n\n            return _guestbookEntryModelResourcePermission.contains(permissionChecker, entryId, actionId);\n        }\n\n        @Reference(\n                target = \"(model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\",\n                unbind = \"-\")\n        protected void setEntryModelPermission(ModelResourcePermission<GuestbookEntry> modelResourcePermission) {\n\n            _guestbookEntryModelResourcePermission = modelResourcePermission;\n        }\n\n        private static ModelResourcePermission<GuestbookEntry>_guestbookEntryModelResourcePermission;\n\n    }\n    ```\n\nThis class is almost identical to `GuestbookModelPermission`. The only\ndifference is that `GuestbookEntryPermission` is for the `GuestbookEntry`\nentity.\n\nNow you can expose the permissions UI to your users so they can assign\npermissions:\n\n1.  Go to the `init.jsp` in your `guestbook-web` project. Add the following\n    imports to the file:\n\n    ```markup\n    <%@ page import=\"com.liferay.docs.guestbook.web.security.permission.resource.GuestbookModelPermission\" %>\n    <%@ page import=\"com.liferay.docs.guestbook.web.security.permission.resource.GuestbookPermission\" %>\n    <%@ page import=\"com.liferay.docs.guestbook.web.security.permission.resource.GuestbookEntryPermission\" %>\n    <%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n    <%@ page import=\"com.liferay.portal.kernel.security.permission.ActionKeys\" %>\n    ```\n\n    The first three are the permissions helper classes you just created.\n\n2.  Save the file.\n\n3.  Open `guestbook_actions.jsp` from the `guestbook_admin` folder. Add this\n    code just after the `<liferay-ui:icon-delete>` tag:\n\n    ```markup\n    <c:if\n    test=\"<%=GuestbookModelPermission.contains(permissionChecker, guestbook.getGuestbookId(), ActionKeys.PERMISSIONS) %>\">\n\n        <liferay-security:permissionsURL\n            modelResource=\"<%= Guestbook.class.getName() %>\"\n            modelResourceDescription=\"<%= guestbook.getName() %>\"\n            resourcePrimKey=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\"\n            var=\"permissionsURL\" />\n\n        <liferay-ui:icon image=\"permissions\" url=\"<%= permissionsURL %>\" />\n\n    </c:if>\n    ```\n\n4.  Save the file.\n\nYou just added an action button that displays Liferay's permissions UI for\nGuestbooks. On top of that, you used the permissions helper you just created to\ntest whether users can even see the action button. It only appears if users have\nthe *permissions* permission.\n\nYou'll implement this for Guestbook entries in the next step.\n\nCongratulations! You've now created helper classes for your permissions, and\nyou've enabled users to associate permissions with their resources. The only\nthing left is to implement permission checks in the application's view layer.\nYou'll do this next."
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/07-permissions/06-checking-permissions-jsps.markdown",
    "content": "---\nheader-id: checking-for-permission-in-jsps\n---\n\n# Checking for Permission in JSPs\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Implementing Permissions</p><p>Step 5 of 5</p>\n</div>\n\nYou've already seen how user interface components can be wrapped in permission\nchecks pretty easily. In this step, you'll implement the rest. \n\n## Checking Permissions in the UI\n\nRecall that you want to restrict access to three areas in your application: \n\n- The guestbook tabs across the top of your application\n- The Add Guestbook button\n- The Add Entry button\n\nFirst, you'll check permissions for the guestbook tabs: \n\n1.  Open `/guestbook/view.jsp` and find the scriptlet that gets the \n    `guestbookId` from the request. Below this are the `<aui-nav>` tags that\n    generate the tabs. Remove those tags and all the code between them. In its\n    place, add the following code, which is the same thing with the addition of\n    permission checks: \n\n    ```markup\n    <aui:nav cssClass=\"nav-tabs\">\n\n        <%\n            List<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n                for (int i = 0; i < guestbooks.size(); i++) {\n\n                    Guestbook curGuestbook = guestbooks.get(i);\n                    String cssClass = StringPool.BLANK;\n\n                    if (curGuestbook.getGuestbookId() == guestbookId) {\n                        cssClass = \"active\";\n                    }\n\n                    if (GuestbookModelPermission.contains(\n                        permissionChecker, curGuestbook.getGuestbookId(), \"VIEW\")) {\n                                            \n        %>\n\n        <portlet:renderURL var=\"viewPageURL\">\n            <portlet:param name=\"mvcPath\" value=\"/guestbookwebportlet/view.jsp\" />\n            <portlet:param name=\"guestbookId\"\n                value=\"<%=String.valueOf(curGuestbook.getGuestbookId())%>\" />\n        </portlet:renderURL>\n\n            \n        <aui:nav-item cssClass=\"<%=cssClass%>\" href=\"<%=viewPageURL%>\"\n            label=\"<%=HtmlUtil.escape(curGuestbook.getName())%>\" />\n\n        <%  \n                    }\n                \n                }\n        %>\n\n    </aui:nav>\n    ```\n\n    This code gets a list of guestbooks from the database, iterates through \n    them, checks the permission for each against the current user's Roles, and \n    adds the guestbooks the user can access to a list of tabs. \n\n    You've now implemented your first permission check. As you can see, it's \n    relatively straightforward thanks to the static methods in your helper \n    classes. The code above shows the tab only if the current user has the \n    `VIEW` permission for the guestbook. \n\n    Next, you'll add permission checks to the Add Entry button. \n\n2.  Scroll down to the line that reads \n    `<aui:button-row cssClass=\"guestbook-buttons\">`. Just below this line, add \n    the following line of code to check for the `ADD_ENTRY` permission: \n\n    ```markup\n    <c:if test='<%= GuestbookPermission.contains(permissionChecker, scopeGroupId, \"ADD_ENTRY\") %>'>\n    ```\n\n3.  After this is the code that creates the `addEntryURL` and the Add Entry \n    button. After the `aui:button` tag and above the `</aui:button-row>` tag, \n    add the closing tag for the `<c:if>` statement: \n\n    ```markup\n    </c:if>\n    ```\n\n    You've now implemented your permission check for the Add Entry button by \n    using JSTL tags. \n\n4.  Save the file. \n\nNext, you'll add permission checking to `entry_actions.jsp` to determine what\noptions appear for logged in users who can see the actions menu in the portlet.\nJust like before, you'll wrap each `renderURL` in an `if` statement that checks\nthe permissions against available actions. To do this, follow these steps: \n\n1.  Open `src/main/resources/META-INF/resources/guestbook/entry_actions.jsp`. \n\n2.  Remove all the code from this file and replace it with what's below: \n\n    ```markup\n    <%@include file=\"../init.jsp\"%>\n\n        <%\n        String mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n        ResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);\n\n        GuestbookEntry entry = (GuestbookEntry)row.getObject(); \n        %>\n\n        <liferay-ui:icon-menu>\n\n            <c:if\n                test=\"<%= GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.UPDATE) %>\">\n                <portlet:renderURL var=\"editURL\">\n                    <portlet:param name=\"entryId\"\n                        value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n                    <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n                </portlet:renderURL>\n\n                <liferay-ui:icon image=\"edit\" message=\"Edit\"\n                    url=\"<%=editURL.toString() %>\" />\n            </c:if>\n\n            <c:if\n            test=\"<%=GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.PERMISSIONS) %>\">\n\n                <liferay-security:permissionsURL\n                    modelResource=\"<%= GuestbookEntry.class.getName() %>\"\n                    modelResourceDescription=\"<%= entry.getMessage() %>\"\n                    resourcePrimKey=\"<%= String.valueOf(entry.getEntryId()) %>\"\n                    var=\"permissionsURL\" />\n            \n                <liferay-ui:icon image=\"permissions\" url=\"<%= permissionsURL %>\" />\n\n            </c:if>\n\n            <c:if\n                test=\"<%=GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.DELETE) %>\">\n\n                <portlet:actionURL name=\"deleteEntry\" var=\"deleteURL\">\n                    <portlet:param name=\"entryId\"\n                        value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n                    <portlet:param name=\"guestbookId\"\n                        value=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n                </portlet:actionURL>\n\n                <liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n            </c:if>\n\n        </liferay-ui:icon-menu>\n        ```\n\n3.  Save the file. \n\nThis code updates each button with a permissions check. If the current user\ncan't perform the given action, the action doesn't appear. \n\nExcellent! You've now implemented all the permissions checks for the Guestbook \nportlet. \n\n## Testing the Application\n\nBefore testing the application, you must reset your database, because guestbook\nentries you created without resources won't work with permissions. \n\n1.  If your server is running, shut it down. \n\n2.  Find your Liferay Workspace on your file system (it should be inside your\n    Eclipse workspace). Inside the `bundles/data` folder is a `hypersonic`\n    folder. \n\n3.  Remove everything from the `hypersonic` folder.\n\n4.  Restart your server. \n\nAdd new guestbooks and entries to test your application with different users.\nAdministrative users see all the buttons, regular users see the Add Entry\nbutton, and guests see no buttons at all (but can navigate). \n\nNow see if you can do the same for the Guestbook Admin portlet. Don't worry if\nyou can't: at the end of this Learning Path is a link to the completed project\nfor you to examine. \n\nGreat! The next step is to integrate search and indexing into your application.\nThis is a prerequisite for the much more powerful stuff to come. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/01-intro.markdown",
    "content": "---\nheader-id: search-and-indexing\n---\n\n# Search and Indexing\n\n[TOC levels=1-4]\n\nThe Guestbook and Guestbook Admin portlets are up and running. The Guestbook\nportlet lets users add, edit, delete, and configure permissions for Guestbook\nEntries. The Guestbook Admin portlet lets Site administrators create, edit,\ndelete, and configure permissions for Guestbooks. In the case of a very popular\nevent (maybe a *Lunar Luau* dinner at the Lunar Resort), there could be many\nGuestbook Entries in the portlet, and users might want to search for Entries\nthat mentioned the delicious low-gravity ham that was served (melts in your\nmouth). Searching for the word *ham* should display these Guestbook entries. In\nshort, Guestbook entries must be searchable via a search bar in the Guestbook\nportlet. \n\n| **Note:** In previous versions of @product@, search was only _permissions\n| aware_ (indexed with the entity's permissions and searched with those\n| permissions intact) if the application developer specified this line in the\n| `Indexer` class's constructor:\n| \n|     setPermissionAware(true);\n| \n| Now, search is permissions aware by default _if the new permissions approach_,\n| as described in the previous step of this tutorial and in\n| [these articles](/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions),\n| is implemented for an application.\n\nTo enable search, you must index Guestbooks and Guestbook entries. Although you\nprobably won't have enough Guestbooks in a Site to warrant searching the\nGuestbook Admin portlet, indexing Guestbooks has other benefits. In a later\nstep, you'll asset-enable Guestbooks and Guestbook entries so the Asset\nPublisher can display them. Enabling search is a prerequisite for this, because\nthe Asset Publisher uses the index to find assets. \n\nIf you get stuck, [source code](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook)\nfor this step is provided. \n\nBut assets are for later. Right now it's time to index those Guestbooks. Ready? \n\n![Figure 1: Add a search bar so users can search for Guestbook Entries. If a message or name matches the search query, the Entry is displayed in the search results.](../../../images/guestbook-portlet-search.png)\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/enabling-search-and-indexing-for-guestbooks\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/02-enabling-search-and-indexing-for-guestbooks/01-intro.markdown",
    "content": "---\nheader-id: enabling-search-and-indexing-for-guestbooks\n---\n\n# Enabling Search and Indexing for Guestbooks\n\n[TOC levels=1-4]\n\nIn this section, you'll create the classes that control these aspects of the\nsearch functionality:\n\n- Registration:\n\n    - `GuestbookSearchRegistrar` registers the search services to the search\n    framework for the Guestbook entity.\n\n- Indexing:\n\n    - `GuestbookModelDocumentContributor` controls which Guestbook fields are\n      indexed in the search engine.\n\n    - `GuestbookModelIndexerWriterContributor` configures the re-indexing and\n      batch re-indexing behavior for Guestbooks.\n\n- Querying:\n\n    - `GuestbookKeywordQueryContributor` contributes clauses to the ongoing\n      search query.\n\n    - `GuestbookModelPreFilterContributor` controls how search results are filtered\n      before they're returned from the search engine.\n\n- Generating Result Summaries:\n\n    - `GuestbookModelSummaryContributor` constructs the result summary for\n      Guestbooks, including specifying which fields to use.\n\nAfter creating the search classes, you'll modify the service layer to update the\nsearch index when a guestbook is persisted. Specifically,\n`GuestbookLocalServiceImpl`'s `addGuestbook`, `updateGuestbook`, and\n`deleteGuestbook` methods are updated to invoke the guestbook indexer.\n\nIn prior versions of @product@, search and indexing was accomplished with one\n`*Indexer` class that extended `BaseIndexer`. In @product-ver@ is a new pattern\nthat relies on \n[composition instead of inheritance](https://stackoverflow.com/questions/2399544/difference-between-inheritance-and-composition).\nIf you want to use the old approach, feel free to extend `BaseIndexer`. It's\nstill supported. \n\nSince there's no reason to search for guestbooks in the UI, only back-end work\nis necessary. \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/understanding-search-and-indexing\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/02-enabling-search-and-indexing-for-guestbooks/02-understanding-search-and-indexing.markdown",
    "content": "---\nheader-id: understanding-search-and-indexing\n---\n\n# Understanding Search and Indexing\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Search and Indexing for Guestbooks</p><p>Step 1 of 6</p>\n</div>\n\nBy default, @product@ uses Elasticsearch, a search engine backed by the popular\nLucene search library, to implement its search and indexing functionality. You\ncould search the database, but that requires resource-hogging table merges.\nInstead, a search engine like Elasticsearch converts searchable entities into\n*documents*. In Elasticsearch, documents are searchable database entities\nconverted into JSON objects. After you implement indexing for guestbook entries,\n@product@ creates a document for each entry. The indexing code specifies which\nguestbook entry fields to add to each guestbook entry document, and it adds all\nthe guestbook entry documents to an index. A search returns a *hits* object\ncontaining pointers to documents matching the search query. Searching for\nentities with a search engine via an index is faster than searching for entities\nin the database. Elasticsearch provides some additional features like relevancy\nscoring and fuzzy search queries. \n\nAlong with the search engine, @product@ has its own search infrastructure.\n@product@ adds the following features to the existing Elasticsearch API: \n\n-   Indexed documents include the fields needed by @product@ (e.g., \n    `entryClassName`, `entryClassPK`, `assetTagNames`, `assetCategories`, \n    `companyId`, `groupId`, staging status). \n-   It ensures the scope of returned search results is appropriate by applying \n    the right filters to search requests. \n-   It provides permission checking and hit summaries to display in the search \n    portlet. \n\nTo understand how the search and indexing code presented here makes your custom\nmodels seamlessly searchable, you must know how to influence each portion of the\nsearch and indexing cycle:\n\n**Indexing**: Model entities store data fields in the database. For example,\nGuestbooks store the _name_ field. During the cycle's Indexing step, you prepare\nthe model entity to be searchable by defining the model's fields that are sent\nto the search engine, later used during a search.\n\n**To influence the way model entity fields are indexed in search engine\ndocuments,**\n\n`ModelDocumentContributor` classes specify which database fields are indexed in\nthe model entity's search engine documents. This class's `contribute` method is\ncalled each time the `add` and `update` methods in the entity's service layer\nare called.\n\n`ModelIndexerWriterContributor` classes configure the re-indexing and batch\nre-indexing behavior for the model entity. This class's method is called when\na re-index is triggered from the Search administrative application found in\nControl Panel &rarr; Configuration &rarr; Search.\n\n**Searching:** Most searches start with a user entering keywords into a search\nbar. The entered keywords are processed by the back-end search infrastructure,\ntransformed into a *query* the search engine can understand, and used to match\nagainst each search document's fields.\n\n**To exert control over the way your model entity documents are searched,**\n\n`KeywordQueryContributor` classes contribute clauses to the ongoing search\nquery. This is called at search time, and ensures that all the fields you\nindexed are also the ones searched. For example, if you index fields with the\nSite locale appended to them (`title_en_us`, for example), you want the search\nquery to include the same locale when the document is searched. If the search\nquery contain another locale (like `title_es_ES`) or searches the plain field\n(`title`), inaccurate results are returned.\n\n`ModelPreFilterContributor`s control how search results are filtered before\nthey're returned from the search engine. For example, adding the workflow status\nto the query ensures that an entity in the trash isn't returned in the search\nresults. For the Guestbook application, a `ModelPrefilterContributor` isn't\nnecessary until you get to the section on workflow-enabling Guestbooks.\n\n**Returning Results:** When a model entity's indexed search document is obtained\nduring a search request, it's converted into a summary of the model entity.\n\n**To influence the result summaries for your model entity documents,**\n\n`ModelSummaryContributor` classes get the `Summary` object created for each\nsearch document, so you can manipulate it by adding specific fields or setting\nthe length of the displayed content.\n\n`ModelVisibilityContributor` classes control the visibility of model entities\nthat can be attached to other asset types (for example, File Entries can be\nattached to Wiki Pages), in the search context. Since Guestbooks and Guestbook\nentries won't be attached to other assets, a model visibility contributor isn't\nnecessary.\n\nOne important step must occur to make sure the above classes are discovered by\nthe search framework. \n\n**Registration**\n\nTo register the model entity with Liferay's search framework,\n\n`SearchRegistrar`s use the search framework's registry to define certain things\nabout your model entity's `ModelSearchDefinition`: which fields are used by\ndefault to retrieve documents from the search engine, and which optional search\nservices are registered for your entity. Registration occurs as soon as the\nComponent is activated (during portal startup).\n\nLet's index some Guestbooks, shall we? \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/02-enabling-search-and-indexing-for-guestbooks/03-registering-search-services.markdown",
    "content": "---\nheader-id: registering-guestbooks-with-the-search-framework\n---\n\n# Registering Guestbooks with the Search Framework\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Search and Indexing for Guestbooks</p><p>Step 2 of 6</p>\n</div>\n\nFirst, update your `build.gradle` with the necessary imports.\n\n1.  Open the `build.gradle` file in your `guestbook-service` project.\n\n2.  Add the Search Service Provider Interface and API dependencies to the\n\t`build.gradle` file:\n\n    ```groovy\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.spi\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.api\"\n    ```\n\n3.  Save the file and run `Refresh Gradle Project`.\n\nOnce the dependency is configured, register the Search services that build the\nentity's `ModelSearchDefinition`.\n\nA `*SearchRegistrar` specifies the classes that the entity uses to contribute to\nbuilding a `ModelSearchDefinition`. Activation of the `SearchRegistrar`\ncomponent results in a cascade of activity in the search framework, culminating\nwith the building of a `DefaultIndexer`. The `DefaultIndexer` is registered\nunder the class name defined in the registrar, and then used for\nindexing/searching objects of that class.\n\nCreate the `GuestbookSearchRegistrar`:\n\n1.  Create a new package in the `guestbook-service` module project's\n    `src/main/java` folder called `com.liferay.docs.guestbook.search`. In this\n    package, create a new class called `GuestbookSearchRegistrar` and populate\n    it with two methods, `activate` and `deactivate`.\n\n    ```java\n    @Component(immediate = true)\n    public class GuestbookSearchRegistrar {\n\n        @Activate\n        protected void activate(BundleContext bundleContext) {\n\n            _serviceRegistration = modelSearchRegistrarHelper.register(\n                Guestbook.class, bundleContext, modelSearchDefinition -> {\n                    modelSearchDefinition.setDefaultSelectedFieldNames(\n                        Field.ASSET_TAG_NAMES, Field.COMPANY_ID, Field.CONTENT,\n                        Field.ENTRY_CLASS_NAME, Field.ENTRY_CLASS_PK,\n                        Field.GROUP_ID, Field.MODIFIED_DATE, Field.SCOPE_GROUP_ID,\n                        Field.TITLE, Field.UID);\n\n                    modelSearchDefinition.setModelIndexWriteContributor(\n                        modelIndexWriterContributor);\n                    modelSearchDefinition.setModelSummaryContributor(\n                        modelSummaryContributor);\n                });\n        }\n\n        @Deactivate\n        protected void deactivate() {\n\n            _serviceRegistration.unregister();\n        }\n    ```\n\n    The annotations `@Activate` and `@Deactivate` ensure each method is invoked\n    as soon as the Component is started (activated) or when it's about to be\n    stopped (deactivated). On activation of the Component, a chain of search and\n    indexing classes is registered for the Guestbook entity. Set the default\n    selected field names used to retrieve results documents from the search\n    engine. Then set the contributors used to build a model search definition.\n\n2.  Specify the service references for the class:\n\n    ```java\n        @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.Guestbook)\")\n        protected ModelIndexerWriterContributor<Guestbook> modelIndexWriterContributor;\n\n        @Reference\n        protected ModelSearchRegistrarHelper modelSearchRegistrarHelper;\n\n        @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.Guestbook)\")\n        protected ModelSummaryContributor modelSummaryContributor;\n\n        private ServiceRegistration<?> _serviceRegistration;\n\n    }\n    ```\n\n    Target the `Guestbook` model while looking up a reference to the contributor\n    classes. Later, when you create these contributor classes, you'll specify\n    the model name again to complete the circle.\n\n3.  Add the imports by Organizing Imports (Ctrl-Shift-O). Choose the\n    `com.liferay.portal.kernel.search.Field` class. \n\n4. Export the `com.liferay.docs.guestbook.search` package in the \n   `guestbook-service` module's `bnd.bnd` file. The export section should look\n   like this: \n\n    ```\n    Export-Package: com.liferay.docs.guestbook.search\n    ```\n\nThe Guestbook search and indexing class registration is completed. Next, you'll\nwrite the search and indexing logic.\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/02-enabling-search-and-indexing-for-guestbooks/04-indexing-guestbooks.markdown",
    "content": "---\nheader-id: indexing-guestbooks\n---\n\n# Indexing Guestbooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Search and Indexing for Guestbooks</p><p>Step 3 of 6</p>\n</div>\n\nTo control how Guestbook objects are translated into search engine documents,\ncreate two classes in the new search package:\n\n1.  Implement a `ModelDocumentContributor` that \"contributes\" fields to a search\n    document indexed by the search engine. The main searchable field for\n    guestbooks is the guestbook name. \n\n2.  `ModelIndexerWriterContributor` configures the batch indexing behavior for\n    Guestbooks. This code is executed when Guestbooks are re-indexed from the\n    Search administration section of the Control Panel.\n\n## Implementing `ModelDocumentContributor`\n\nCreate `GuestbookModelDocumentContributor` and populate it with this:\n\n```java\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelDocumentContributor.class\n)\npublic class GuestbookModelDocumentContributor\n    implements ModelDocumentContributor<Guestbook> {\n\n    @Override\n    public void contribute(Document document, Guestbook guestbook) {\n        try {\n            document.addDate(Field.MODIFIED_DATE, guestbook.getModifiedDate());\n\n            Locale defaultLocale = PortalUtil.getSiteDefaultLocale(\n    guestbook.getGroupId());\n\n            String localizedTitle = LocalizationUtil.getLocalizedName(\n    Field.TITLE, defaultLocale.toString());\n\n            document.addText(localizedTitle, guestbook.getName());\n        } catch (PortalException pe) {\n            if (_log.isWarnEnabled()) {\n                _log.warn(\n    \"Unable to index guestbook \" + guestbook.getGuestbookId(), pe);\n            }\n        }\n    }\n\n    private static final Log _log = LogFactoryUtil.getLog(\n    GuestbookModelDocumentContributor.class);\n\n}\n```\n\nBecause @product@ supports localization, you should too. The above code gets \nthe default locale from the Site by passing the `Guestbook`'s group ID to \nthe `getSiteDefaultLocale` method, then using it to get the localized name\nof the Guestbook's title field. The retrieved Site locale is appended to the\nfield (e.g., `title_en_US`), so the field gets passed to the search engine\nand goes through the right analysis and\n[tokenization](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/analysis-tokenizers.html). \n\nUse Ctrl-Shift-O to add these imports, and then save the file: \n\n- `com.liferay.portal.kernel.search.Field` \n- `com.liferay.portal.kernel.search.Document` \n\n## Implementing `ModelIndexerWriterContributor`\n\nCreate `GuestbookModelIndexerWriterContributor` and populate it with this:\n\n```java\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelIndexerWriterContributor.class\n)\npublic class GuestbookModelIndexerWriterContributor\n    implements ModelIndexerWriterContributor<Guestbook> {\n\n    @Override\n    public void customize(\n        BatchIndexingActionable batchIndexingActionable,\n        ModelIndexerWriterDocumentHelper modelIndexerWriterDocumentHelper) {\n\n        batchIndexingActionable.setPerformActionMethod((Guestbook guestbook) -> {\n            Document document = modelIndexerWriterDocumentHelper.getDocument(\n    guestbook);\n\n            batchIndexingActionable.addDocuments(document);\n        });\n    }\n\n    @Override\n    public BatchIndexingActionable getBatchIndexingActionable() {\n        return dynamicQueryBatchIndexingActionableFactory.getBatchIndexingActionable(\n    guestbookLocalService.getIndexableActionableDynamicQuery());\n    }\n\n    @Override\n    public long getCompanyId(Guestbook guestbook) {\n        return guestbook.getCompanyId();\n    }\n\n    @Override\n    public void modelIndexed(Guestbook guestbook) {\n        guestbookEntryBatchReindexer.reindex(\n    guestbook.getGuestbookId(), guestbook.getCompanyId());\n    }\n\n    @Reference\n    protected DynamicQueryBatchIndexingActionableFactory\n    dynamicQueryBatchIndexingActionableFactory;\n\n    @Reference\n    protected GuestbookEntryBatchReindexer guestbookEntryBatchReindexer;\n\n    @Reference\n    protected GuestbookLocalService guestbookLocalService;\n\n}\n```\n\nFirst look at the `customize` method. Configure the batch indexing behavior for\nthe entity's documents by calling `BatchIndexingActionable` methods. This code\nuses the Guestbook's actionable dynamic query helper method to retrieve all\nGuestbooks in the virtual instance (identified by the Company ID). Service\nBuilder generated this query method for you when you built the services. Each\nGuestbook's document is then retrieved and added to a collection.\n\nWhen you write the indexing classes for Guestbook entries, you'll add the\nGuestbook title to the `GuestbookEntry` document. Thus, you must provide a way to update\nthe indexed `GuestbookEntry` documents if a Guestbook title is changed.  The\n`modelIndexed` method calls a `reindex` method from an interface that will be\ncreated later for `GuestbookEntry`s. For now, ignore the error in the\n`modelIndexed` method. \n\nUse Ctrl-Shift-O to add this import, and save the file: \n\n- `com.liferay.portal.kernel.search.Document`\n\nOnce the re-indexing behavior is in place, you can move on to controlling how\nGuestbook documents are queried from the search engine.\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/02-enabling-search-and-indexing-for-guestbooks/05-querying-guestbook.markdown",
    "content": "---\nheader-id: querying-for-guestbook-documents\n---\n\n# Querying for Guestbook Documents\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Search and Indexing for Guestbooks</p><p>Step 4 of 6</p>\n</div>\n\nThe code is in place for for indexing Guestbooks to the search engine. Next,\nyou'll code the behavior necessary for querying the indexed documents.\n\nImplement two interfaces:\n\n1.  `KeywordQueryContributor` contributes clauses to the ongoing search query.\n\n2.  `ModelPreFilterContributor` controls how search results are filtered before\n    they're returned from the search engine.\n\n## Implementing `KeywordQueryContributor`\n\nCreate `GuestbookKeywordQueryContributor`: \n\n```java\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = KeywordQueryContributor.class\n)\npublic class GuestbookKeywordQueryContributor\n    implements KeywordQueryContributor {\n\n    @Override\n    public void contribute(\n        String keywords, BooleanQuery booleanQuery,\n        KeywordQueryContributorHelper keywordQueryContributorHelper) {\n\n        SearchContext searchContext =\n    keywordQueryContributorHelper.getSearchContext();\n\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.TITLE, false);\n    }\n\n    @Reference\n    protected QueryHelper queryHelper;\n\n}\n```\n\nThis class adds Guestbook fields to the search query constructed by the Search\napplication in @product@. Later, when you asset-enable Guestbooks, this code\nallows indexed Guestbooks to be searched from the Search application when\na keyword is entered into the search bar. Use the query helper to add search\nterms to the query that allow Guestbooks to be found. Here it's important to\nnote that adding the localized search term is important. Since the localized\nGuestbook title was indexed, you must retrieve the localized value from the\nsearch engine.\n\nDon't forget to add imports with Ctrl-Shift-O. Choose\n`com.liferay.portal.kernel.search.BooleanQuery` and\n`com.liferay.portal.kernel.search.Field`. \n\nOnce the query code is in place, define how returned Guestbook documents are\nsummarized. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/02-enabling-search-and-indexing-for-guestbooks/06-summarizing-search-documents.markdown",
    "content": "---\nheader-id: generating-results-summaries\n---\n\n# Generating Results Summaries\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Search and Indexing for Guestbooks</p><p>Step 5 of 6</p>\n</div>\n\nThe Search application and the Asset Publisher application must display results\nretrieved from the search engine. Control the summarized content by implementing\na `ModelSummaryContributor`.\n\nA summary is a condensed, text-based version of the entity's document that can\nbe displayed generically. You create it by combining key parts of the entity's\ndata so users can browse through search resmlts to find the entity they want.\n\nCreate a `GuestbookModelSummaryContributor`:\n\n```java\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelSummaryContributor.class\n)\npublic class GuestbookModelSummaryContributor\n    implements ModelSummaryContributor {\n\n    @Override\n    public Summary getSummary(\n        Document document, Locale locale, String snippet) {\n\n        Summary summary = createSummary(document);\n\n        summary.setMaxContentLength(200);\n\n        return summary;\n    }\n\n    private Summary createSummary(Document document) {\n        String prefix = Field.SNIPPET + StringPool.UNDERLINE;\n\n        String title = document.get(prefix + Field.TITLE, Field.TITLE);\n\n        return new Summary(title, StringPool.BLANK);\n    }\n\n}\n```\n\nFirst override `getSummary` and set the maximum summary length on the summary\nreturned. The value `200` is a Liferay standard. Control the summary creation in\na utility method called `createSummary`. Create a `prefix` variable using two\nconstants, `Field.SNIPPET` and `Stringpool.UNDERLINE`. The `snippet_title` field\nis returned from the `document.get` call, and added to the summary. Using the\nsnippet field provides two benefits:\n\n1.  Snippet text can be highlighted so matching keywords are emphasized.\n\n2.  Snippet text can be shortened automatically by the Search application so a\n    sensible portion of the field's text is displayed in the search results. \n\nGuestbook titles are likely short, so only the highlighting behavior is useful\nfor the title field of Guestbooks. For longer fields (like some `content`\nfields), the clipping behavior is more useful. Additional highlighting behavior\ncan be configured via the `index.search.highlight.*` properties in\n[portal.properties](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Lucene%20Search).\n\nCreate summaries by combining key parts of the entity's data so users can browse\nthrough search results to find the entity they want.\n\nDon't forget to Ctrl-Shift-O and import these classes: \n\n- `com.liferay.portal.kernel.search.Field` \n- `com.liferay.petra.string.StringPool`\n- `com.liferay.portal.kernel.search.Summary`\n- `com.liferay.portal.kernel.search.Document`\n\nSave your file. \n\nOnce all the search and indexing logic is in place, update the service layer so\n`add`, `update`, and `delete` service calls trigger the new logic.\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/02-enabling-search-and-indexing-for-guestbooks/07-handling-indexing-in-the-guestbook-service-layer.markdown",
    "content": "---\nheader-id: handling-indexing-in-the-guestbook-service-layer\n---\n\n# Handling Indexing in the Guestbook Service Layer\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Search and Indexing for Guestbooks</p><p>Step 6 of 6</p>\n</div>\n\nWhenever a Guestbook database entity is added, updated, or deleted, the search\nindex must be updated accordingly. The @product@ annotation `@Indexable`\ncombines with the `IndexableType` to mark your service methods so documents can\nbe updated or deleted. Next, you'll annotate `addGuestbook`, `updateGuestbook`,\nand `deleteGuestbook` service methods. \n\n1.  Open `GuestbookLocalServiceImpl` in the `guestbook-service` module's \n    `com.liferay.docs.guestbook.service.impl` package and add the following \n    annotation above the method signature for the `addGuestbook` and \n    `updateGuestbook` methods:\n\n    ```java\n    @Indexable(type = IndexableType.REINDEX)\n    public Guestbook addGuestbook(...)\n\n    @Indexable(type = IndexableType.REINDEX)\n    public Guestbook updateGuestbook(...)\n    ```\n\n    The `@Indexable` annotation indicates that an index update is required\n    following the method execution. The indexing classes control the type of\n    index: setting the `@Indexable` annotation type to `IndexableType.REINDEX`\n    updates the document in the index that corresponds to the updated Guestbook. \n\n2.  Add the following annotation above the method signature for the \n    `deleteGuestbook` method: \n\n    ```java\n    @Indexable(type = IndexableType.DELETE)\n    public Guestbook deleteGuestbook(...)\n    ```\n\n    When a Guestbook is deleted from the database, its document shouldn't\n    remain in the search index. This ensures that it is deleted.\n\n3.  Use Ctrl-Shift-O to add the necessary imports:\n\n    ```java\n    import com.liferay.portal.kernel.search.Indexable;\n    import com.liferay.portal.kernel.search.IndexableType;\n    ```\n\n    Save the file. \n\n4.  In the Gradle Tasks pane on the right-hand side of Liferay @ide@, \n    double-click `buildService` in `guestbook-service` &rarr; `build`. This \n    re-runs Service Builder to incorporate your changes to \n    `GuestbookLocalServiceImpl`. \n\nNext, you'll enable search and indexing for Guestbook Entries. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/03-enabling-search-and-indexing-for-guestbook-entries/01-intro.markdown",
    "content": "---\nheader-id: enabling-search-and-indexing-for-entries\n---\n\n# Enabling Search and Indexing for Entries\n\n[TOC levels=1-4]\n\nNow you'll create the classes that control these aspects of the search\nfunctionality:\n\n- Registration:\n\n    - `GuestbookEntrySearchRegistrar` registers the search service for the\n    `GuestbookEntry` entity.\n\n- Indexing:\n\n    - `GuestbookEntryModelDocumentContributor` controls which `GuestbookEntry` fields are\n      indexed in the search engine.\n\n    - `GuestbookEntryModelIndexerWriterContributor` configures the re-indexing and\n      batch re-indexing behavior for `GuestbookEntry`s.\n\n    - `GuestbookEntryBatchReindexer`, an interface, and its `GuestbookEntryBatchReindexerImpl`, \n\t  for re-indexing `GuestbookEntries `when their Guestbook is updated.\n\n- Querying:\n\n    - `GuestbookEntryKeywordQueryContributor` contributes clauses to the ongoing\n      search query.\n\n    - `GuestbookEntryModelPreFilterContributor` controls how search results are filtered\n      before they're returned from the search engine.\n\n- Generating Result Summaries:\n\n    - `GuestbookEntryModelSummaryContributor` constructs the result summary for\n      `GuestbookEntry`s, including specifying which fields to use.\n\nAfter creating the search classes, modify the service layer to update the search\nindex when a `GuestbookEntry `is persisted:\n\n- Update `GuestbookEntryLocalServiceImpl`'s `addEntry`, `updateEntry`, and\n  `deleteEntry` methods to update the index so it matches the database.\n\n| **Note:** In prior versions of @product@, search and indexing was accomplished\n| with one `*Indexer` class that extended `BaseIndexer`. This tutorial\n| demonstrates a new pattern that relies on \n| [composition instead of inheritance](https://stackoverflow.com/questions/2399544/difference-between-inheritance-and-composition).\n| If you desire to use the old approach, feel free to extend `BaseIndexer`. It's\n| still supported.\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/registering-entries-with-the-search-framework\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/03-enabling-search-and-indexing-for-guestbook-entries/02-registering-search-services.markdown",
    "content": "---\nheader-id: registering-entries-with-the-search-framework\n---\n\n# Registering Entries with the Search Framework\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Search and Indexing for Entries</p><p>Step 1 of 5</p>\n</div>\n\nThe search registrar for Entries is very similar to the one created for\nGuestbooks. You'll even put it in the same package\n(`com.liferay.docs.guestbook.search`). \n\nCreate the `GuestbookEntrySearchRegistrar`:\n\n1.  In `com.liferay.docs.guestbook.search`, create a new class called\n    `GuestbookEntrySearchRegistrar` and populate it with two methods, `activate` and\n    `deactivate`.\n\n    ```java\n    @Component(immediate = true)\n    public class GuestbookEntrySearchRegistrar {\n\n        @Activate\n        protected void activate(BundleContext bundleContext) {\n\n            _serviceRegistration = modelSearchRegistrarHelper.register(\n                GuestbookEntry.class, bundleContext, modelSearchDefinition -> {\n                    modelSearchDefinition.setDefaultSelectedFieldNames(\n                        Field.COMPANY_ID, Field.ENTRY_CLASS_NAME,\n                        Field.ENTRY_CLASS_PK, Field.UID, \n                        Field.SCOPE_GROUP_ID, Field.GROUP_ID);\n\n                    modelSearchDefinition.setDefaultSelectedLocalizedFieldNames(\n                        Field.TITLE, Field.CONTENT);\n\n                    modelSearchDefinition.setModelIndexWriteContributor(\n                        modelIndexWriterContributor);\n                    modelSearchDefinition.setModelSummaryContributor(\n                        modelSummaryContributor);\n                    modelSearchDefinition.setSelectAllLocales(true);\n\n                });\n        }\n\n        @Deactivate\n        protected void deactivate() {\n            _serviceRegistration.unregister();\n        }\n    ```\n\n\nAs you did with Guestbooks, set the default selected field names used to\nretrieve results documents from the search engine. For entries, call\n`setDefaultSelectedLocalizedFieldNames` for the title and content fields. This\nensures that the localized version of the field is searched and returned. The\nonly other difference with Entries is the call to\n`setSelectAllLocales(true)`. It takes the fields set in\n`setDefaultSelectedLocalizedFieldNames` and sets those fields for each\navailable locale in the `stored_fields` parameter of the search request. If not\nset to `true`, only a single locale is searched.\n\n2.  Specify the service references for the class:\n\n    ```java\n        @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n        protected ModelIndexerWriterContributor<GuestbookEntry> modelIndexWriterContributor;\n\n        @Reference\n        protected ModelSearchRegistrarHelper modelSearchRegistrarHelper;\n\n        @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n        protected ModelSummaryContributor modelSummaryContributor;\n\n        private ServiceRegistration<?> _serviceRegistration;\n\n    }\n    ```\n\n    Target the `GuestbookEntry` model while looking up a reference to the\n    contributor classes. Later, when you create these contributor classes,\n    you'll specify the model name again to complete the circle.\n\n3.  Use Ctrl-Shift-O to add imports: \n\n    - `com.liferay.portal.kernel.search.Field`\n\nThe entry search and indexing class registration is completed. Next, you'll\nwrite the search and indexing logic.\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/03-enabling-search-and-indexing-for-guestbook-entries/03-indexing-entries.markdown",
    "content": "---\nheader-id: indexing-entries\n---\n\n# Indexing Entries\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Search and Indexing for Entries</p><p>Step 2 of 5</p>\n</div>\n\nTo control how `GuestbookEntry `objects are translated into search engine\ndocuments, create these classes in the search package:\n\n1.  `ModelDocumentContributor`: The main searchable fields for entries are\n    _Name_ and _Message_. The Guestbook name associated with the entry is\n    indexed, too.\n\n2.  `ModelIndexerWriterContributor` configures the batch indexing behavior for\n    entries. This code is executed when Entries are re-indexed from the\n    Search administration section of the Control Panel.\n\n3.  A new interface, `GuestbookEntryBatchReindexer`, with its implementation,\n    `GuestbookEntryBatchReindexerImpl`. These classes contain code to ensure\n    that entries are re-indexed when their Guestbook is updated.\n\n## Implementing `ModelDocumentContributor`\n\nCreate `GuestbookEntryModelDocumentContributor` and populate it with this:\n\n```java\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelDocumentContributor.class\n)\npublic class GuestbookEntryModelDocumentContributor\n    implements ModelDocumentContributor<GuestbookEntry> {\n\n    @Override\n    public void contribute(Document document, GuestbookEntry entry) {\n        try {\n            Locale defaultLocale = PortalUtil.getSiteDefaultLocale(\n    entry.getGroupId());\n\n            document.addDate(Field.MODIFIED_DATE, entry.getModifiedDate());\n            document.addText(\"entryEmail\", entry.getEmail());\n\n            String localizedTitle = LocalizationUtil.getLocalizedName(\n    Field.TITLE, defaultLocale.toString());\n            String localizedContent = LocalizationUtil.getLocalizedName(\n    Field.CONTENT, defaultLocale.toString());\n\n            document.addText(localizedTitle, entry.getName());\n            document.addText(localizedContent, entry.getMessage());\n\n            long guestbookId = entry.getGuestbookId();\n\n            Guestbook guestbook = _guestbookLocalService.getGuestbook(\n    guestbookId);\n\n            String guestbookName = guestbook.getName();\n\n            String localizedGbName = LocalizationUtil.getLocalizedName(\n    Field.NAME, defaultLocale.toString());\n\n            document.addText(localizedGbName, guestbookName);\n        } catch (PortalException pe) {\n            if (_log.isWarnEnabled()) {\n                _log.warn(\"Unable to index entry \" + entry.getEntryId(), pe);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    private static final Log _log = LogFactoryUtil.getLog(\n    GuestbookEntryModelDocumentContributor.class);\n\n    @Reference\n    private GuestbookLocalService _guestbookLocalService;\n\n}\n```\n\nAs with Guestbooks, add the localized values for fields that might be\ntranslated. The Site locale is appended to the field (e.g., `title_en_US`), so\nthe field gets passed to the search engine and goes through the right analysis\nand\n[tokenization](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/analysis-tokenizers.html). \n\nUse Ctrl-Shift-O to add the following imports and save the file: \n\n- `com.liferay.portal.kernel.search.Document`\n- `com.liferay.portal.kernel.search.Field`\n\n## Implementing `ModelIndexerWriterContributor`\n\nCreate `GuestbookEntryModelIndexerWriterContributor` and populate it with this:\n\n```java\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelIndexerWriterContributor.class\n)\npublic class GuestbookEntryModelIndexerWriterContributor\n    implements ModelIndexerWriterContributor<GuestbookEntry> {\n\n    @Override\n    public void customize(\n        BatchIndexingActionable batchIndexingActionable,\n        ModelIndexerWriterDocumentHelper modelIndexerWriterDocumentHelper) {\n\n        batchIndexingActionable.setPerformActionMethod((GuestbookEntry entry) -> {\n            Document document = modelIndexerWriterDocumentHelper.getDocument(\n    entry);\n\n            batchIndexingActionable.addDocuments(document);\n            \n        });\n    }\n\n    @Override\n    public BatchIndexingActionable getBatchIndexingActionable() {\n        return dynamicQueryBatchIndexingActionableFactory.getBatchIndexingActionable(\n    guestbookEntryLocalService.getIndexableActionableDynamicQuery());\n    }\n\n    @Override\n    public long getCompanyId(GuestbookEntry entry) {\n        return entry.getCompanyId();\n    }\n\n    @Reference\n    protected DynamicQueryBatchIndexingActionableFactory\n    dynamicQueryBatchIndexingActionableFactory;\n\n    @Reference\n    protected GuestbookEntryLocalService guestbookEntryLocalService;\n\n}\n```\n\nThe interesting work is done in the `customize` method, where all entries are\nretrieved and added to a collection. \n\nUse Ctrl-Shift-O to add an import for\n`com.liferay.portal.kernel.search.Document` and save the file. \n\n## Implementing `GuestbookEntryBatchReindexer`\n\nCreate a new interface, `GuestbookEntryBatchReindexer`, with one method called\n`reindex`:\n\n```java\npackage com.liferay.docs.guestbook.search;\n\npublic interface GuestbookEntryBatchReindexer {\n\n    public void reindex(long guestbookId, long companyId);\n\n}\n```\n\nThen create the implementation class, `GuestbookEntryBatchReindexerImpl`:\n\n```java\n@Component(immediate = true, service = GuestbookEntryBatchReindexer.class)\npublic class GuestbookEntryBatchReindexerImpl implements GuestbookEntryBatchReindexer {\n\n    @Override\n    public void reindex(long guestbookId, long companyId) {\n        BatchIndexingActionable batchIndexingActionable =\n    indexerWriter.getBatchIndexingActionable();\n\n        batchIndexingActionable.setAddCriteriaMethod(dynamicQuery -> {\n            Property guestbookIdPropery = PropertyFactoryUtil.forName(\n    \"guestbookId\");\n\n            dynamicQuery.add(guestbookIdPropery.eq(guestbookId));\n        });\n\n        batchIndexingActionable.setCompanyId(companyId);\n\n        batchIndexingActionable.setPerformActionMethod((GuestbookEntry entry) -> {\n            Document document = indexerDocumentBuilder.getDocument(entry);\n\n            batchIndexingActionable.addDocuments(document);\n        });\n\n        batchIndexingActionable.performActions();\n\n    }\n\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n    protected IndexerDocumentBuilder indexerDocumentBuilder;\n\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n    protected IndexerWriter<GuestbookEntry> indexerWriter;\n\n}\n```\n\nThe `reindex` method of the interface is called when a Guestbook is updated.\nThe entry documents are re-indexed to include the update Guestbook title.\n\nUse Ctrl-Shift-O to add the following imports, and save the file: \n\n- `com.liferay.portal.kernel.search.Document`\n- `com.liferay.portal.kernel.dao.orm.Property`\n\nYou should notice that errors in the project go away at this point. \n\nOnce the re-indexing behavior is in place, you can move on to the code for\ncontrolling how `GuestbookEntry` documents are queried from the search engine.\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/03-enabling-search-and-indexing-for-guestbook-entries/04-querying-entries.markdown",
    "content": "---\nheader-id: querying-for-guestbook-entry-documents\n---\n\n# Querying for Guestbook Entry Documents\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Search and Indexing for Entries</p><p>Step 3 of 5</p>\n</div>\n\nThe code is in place for indexing Guestbook entries to the search engine. Next\ncode the behavior necessary for querying the indexed documents.\n\nImplement two classes:\n\n1.  `GuestbookEntryKeywordQueryContributor` contributes clauses to the ongoing search\n    query.\n\n2.  `GuestbookEntryModelPreFilterContributor` controls how search results are filtered\n    before they're returned from the search engine.\n\n## Implementing `KeywordQueryContributor`\n\nCreate `GuestbookEntryKeywordQueryContributor` and populate it with this:\n\n```java\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = KeywordQueryContributor.class\n)\npublic class GuestbookEntryKeywordQueryContributor implements KeywordQueryContributor {\n\n    @Override\n    public void contribute(\n        String keywords, BooleanQuery booleanQuery,\n        KeywordQueryContributorHelper keywordQueryContributorHelper) {\n\n        SearchContext searchContext =\n    keywordQueryContributorHelper.getSearchContext();\n\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.TITLE, false);\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.CONTENT, false);\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, \"entryEmail\", false);\n    }\n\n    @Reference\n    protected QueryHelper queryHelper;\n\n}\n```\n\nAdding the localized search terms is important. For all localized\n`GuestbookEntry` fields in the index, retrieve the localized value from the\nsearch engine.\n\nUse Ctrl-Shift-O to add these imports, and then save the file: \n\n- `com.liferay.portal.kernel.search.BooleanQuery`\n- `com.liferay.portal.kernel.search.Field`\n\nNow that the query code is in place, you can define how returned\n`GuestbookEntry` documents are summarized. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/03-enabling-search-and-indexing-for-guestbook-entries/05-summarizing-search-documents.markdown",
    "content": "---\nheader-id: generating-results-summaries-0\n---\n\n# Generating Results Summaries\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Search and Indexing for Entries</p><p>Step 3 of 5</p>\n</div>\n\nThe Search application and the Asset Publisher application display results\nretrieved from the search engine. You can control the display by implementing\na `ModelSummaryContributor`.\n\nCreate a `GuestbookEntryModelSummaryContributor`:\n\n```java\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelSummaryContributor.class\n)\npublic class GuestbookEntryModelSummaryContributor implements ModelSummaryContributor {\n\n    @Override\n    public Summary getSummary(\n        Document document, Locale locale, String snippet) {\n\n        Summary summary = createSummary(document);\n\n        summary.setMaxContentLength(128);\n\n        return summary;\n    }\n\n    private Summary createSummary(Document document) {\n        String prefix = Field.SNIPPET + StringPool.UNDERLINE;\n\n        String title = document.get(prefix + Field.TITLE, Field.CONTENT);\n        String content = document.get(prefix + Field.CONTENT, Field.CONTENT);\n\n        return new Summary(title, content);\n    }\n\n}\n```\n\nFirst override `getSummary`, and set the maximum summary length on the summary\nreturned. The value `200` is a Liferay standard. Control the summary creation\nin a utility method called `createSummary`. Guestbooks only included the title\nin the summary, but Entries use the title and the content (the Entry message\nfield) to populate the summary.\n\nCreate summaries by combining key parts of the entity's data.\n\nUse Ctrl-Shift-O to add these imports, and then save the file: \n\n- `com.liferay.portal.kernel.search.Field`\n- `com.liferay.petra.string.StringPool`\n- `com.liferay.portal.kernel.search.Summary`\n- `com.liferay.portal.kernel.search.Document`\n\nNow that the search and indexing logic is in place, you can update the service\nlayer so `add`, `update`, and `delete` service calls trigger the new logic.\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/03-enabling-search-and-indexing-for-guestbook-entries/06-handling-indexing-in-the-entry-service-layer.markdown",
    "content": "---\nheader-id: handling-indexing-in-the-entry-service-layer\n---\n\n# Handling Indexing in the Entry Service Layer\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Search and Indexing for Entries</p><p>Step 5 of 5</p>\n</div>\n\nWhenever a Guestbook entry is added, updated, or deleted, the corresponding\ndocument should also be updated or deleted. A minor update to each of the\n`addEntry`, `updateEntry`, and `deleteEntry` service methods for Entries is all\nit takes. \n\nFollow these steps to update the methods: \n\n1.  Open `GuestbookEntryLocalServiceImpl` in the `guestbook-service` module's\n    `com.liferay.docs.guestbook.service.impl` package, and add the annotation\n    `@Indexable(type = IndexableType.REINDEX)` above the signature for the\n    `addGuestbookEntry` and `updateGuestbookEntry` methods:\n\n    ```java\n    @Indexable(type = IndexableType.REINDEX)\n    public GuestbookEntry addGuestbookEntry(...)\n\n    @Indexable(type = IndexableType.REINDEX)\n    public GuestbookEntry updateGuestbookEntry(...)\n    ```\n\n    The `@Indexable` annotation indicates that an index update is required\n    following method execution. The indexing classes control exactly how the\n    indexing happens. Setting the `@Indexable` annotation's type to\n    `IndexableType.REINDEX` updates the indexed document that corresponds to the\n    updated `GuestbookEntry`. \n\n2.  Add the `@Indexable(type = IndexableType.DELETE)` annotation above the \n    signature for the `deleteEntry` method. The indexable type \n    `IndexableType.DELETE` ensures that the `GuestbookEntry` is deleted from the\n    index: \n\n    ```java\n    @Indexable(type = IndexableType.DELETE)\n    public GuestbookEntry deleteGuestbookEntry(...)\n    ```\n\n3.  Use Ctrl-Shift-O to add the required imports:\n\n    ```java\n    import com.liferay.portal.kernel.search.Indexable;\n    import com.liferay.portal.kernel.search.IndexableType;\n    ```\n\n    Save the file. \n\n4.  In the Gradle Tasks pane on the right-hand side of Liferay @ide@, \n    double-click `buildService` in `guestbook-service` &rarr; `build`. This \n    re-runs Service Builder to incorporate your changes to \n    `GuestbookEntryLocalServiceImpl`. \n\nGuestbooks and their entries now have search and indexing support in the\nback-end. Next, you'll enable search in the Guestbook portlet's front-end. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/04-updating-your-user-interface-for-search/01-intro.markdown",
    "content": "---\nheader-id: updating-your-user-interface-for-search\n---\n\n# Updating Your User Interface For Search\n\n[TOC levels=1-4]\n\nUpdating the Guestbook portlet's user interface for search takes two steps:\n\n1.  Update the Guestbook portlet's default view JSP to display a search bar for \n    submitting queries.\n\n2.  Create a new JSP for the Guestbook portlet to display search results.\n\nYou'll start by updating the Guestbook portlet's view JSP.\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/adding-a-search-bar-to-the-guestbook-portlet\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/04-updating-your-user-interface-for-search/02-adding-a-search-bar-to-the-guestbook-portlet.markdown",
    "content": "---\nheader-id: adding-a-search-bar-to-the-guestbook-portlet\n---\n\n# Adding a Search Bar to the Guestbook Portlet\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating Your UI for Search</p><p>Step 1 of 2</p>\n</div>\n\nCreate the search bar UI for the Guestbook portlet:\n\n1.  In `guestbook-web`, open the file \n    `src/main/resources/META-INF/resources/guestbook/view.jsp`. Add a \n    render URL near the top of the file, just after the scriptlet that gets the \n    `guestbookId` from the request:\n\n    ```markup\n    <portlet:renderURL var=\"searchURL\">\n        <portlet:param name=\"mvcPath\" \n        value=\"/guestbook/view_search.jsp\" />\n    </portlet:renderURL>\n    ```\n\n    The render URL points to `/guestbook/view_search.jsp` (created in\n    the next step). You construct the URL first to specify what\n    happens when the user submits a search query. \n\n2.  Right after the render URL, create an AUI form that adds an input field for\n    search keywords and a *Submit* button that executes the form action, which\n    is mapped to the `searchURL`.\n\n    ```markup\n    <aui:form action=\"${searchURL}\" name=\"fm\">\n\n        <div class=\"row\">\n            <div class=\"col-md-8\">\n                <aui:input inlineLabel=\"left\" label=\"\" name=\"keywords\" placeholder=\"search-entries\" size=\"256\" />\n            </div>\n\n            <div class=\"col-md-4\">\n                <aui:button type=\"submit\" value=\"search\" />\n            </div>\n        </div>\n\n     </aui:form>\n     ```\n\nThe body of the search form consists of a `<div>` with one row containing\ntwo fields: an input field, named `keywords` and a _Submit_ button. Its\n`name=\"keywords\"` attribute specifies the name of the URL parameter that\ncontains the search query. The `<aui:button>` tag defines the search button.\nThe `type=\"submit\"` attribute specifies that when the button is clicked (or\nthe *Enter* key is pressed), the AUI form is submitted. The `value=\"search\"`\nattribute specifies the name that appears on the button. \n\nThat's all there is to the search form! When the form is submitted, the\n`mvcPath` parameter pointing to the `view_search.jsp` is included in the URL\nalong with the `keywords` parameter containing the search query. Next you'll\ncreate the `view_search.jsp` file to display the search results. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/08-search-and-indexing/04-updating-your-user-interface-for-search/03-creating-a-search-results-jsp-for-the-guestbook-portlet.markdown",
    "content": "---\nheader-id: creating-a-search-results-jsp-for-the-guestbook-portlet\n---\n\n# Creating a Search Results JSP for the Guestbook Portlet\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating Your UI for Search</p><p>Step 2 of 2</p>\n</div>\n\nThere are several design goals to implement in the search results JSP:\n\n- Use a search container to display guestbook entries matching a search query. \n- Make the Actions button available for each guestbook entry in the results,\n  like it is in the main view's search container.\n- Include the search bar so that users can edit and resubmit their queries \n  without having to click the back link to go to the portlet's default view.\n\n![Figure 1: The search results should appear in a search container, and the Actions button should appear for each entry. The search bar should also be displayed.](../../../../images/guestbook-portlet-search-results.png)\n\nFollow these steps to create the search results JSP:\n\n1.  Create a new file called `view_search.jsp` in your `guestbook-web` module's \n    `src/main/resources/META-INF/resources/guestbook` folder. In this file,\n    include the `init.jsp`: \n\n    ```markup\n    <%@include file=\"../init.jsp\"%>\n    ```\n\n2.  Extract the `keywords` and `guestbookId` parameters from the request. The \n    `keywords` parameter contains the search query, and the `guestbookId` \n    parameter contains the ID of the guestbook being searched: \n\n    ```markup\n    <%\n      String keywords = ParamUtil.getString(request, \"keywords\");\n      long guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n    %>\n    ```\n\n3.  Define the `searchURL` and `viewURL` as `renderURL`s. Both use the `mvcPath` \n    parameter that's available to Liferay MVC Portlets:\n\n    ```markup\n    <portlet:renderURL var=\"searchURL\">\n            <portlet:param name=\"mvcPath\" \n            value=\"/guestbook/view_search.jsp\" />\n    </portlet:renderURL>\n\n    <portlet:renderURL var=\"viewURL\">\n        <portlet:param \n            name=\"mvcPath\" \n            value=\"/guestbook/view.jsp\" \n        />\n    </portlet:renderURL>\n    ```\n\n    The `searchURL` points to the current JSP: `view_search.jsp`. The `viewURL` \n    points back to the Guestbook portlet's main view. These URLs are used in\n    the AUI form that you'll create next. \n\n4.  Add this AUI form:\n\n    ```markup\n    <aui:form action=\"${searchURL}\" name=\"fm\">\n\n        <liferay-ui:header backURL=\"${viewURL}\" title=\"back\" />\n\n        <div class=\"row\">\n            <div class=\"col-md-8\">\n                <aui:input inlineLabel=\"left\" label=\"\" name=\"keywords\" placeholder=\"search-entries\" size=\"256\" />\n            </div>\n\n            <div class=\"col-md-4\">\n                <aui:button type=\"submit\" value=\"search\" />\n            </div>\n        </div>\n    </aui:form>\n    ```\n\n    This form is identical to the one that you added to the Guestbook portlet's\n    `view.jsp`, except that this one contains a `<liferay-ui:header>` tag that\n    displays the Back icon next to the word *Back*. The `backURL` attribute in\n    the header uses the `viewURL` defined above. Submitting the form invokes the\n    `searchURL` with the user's search query added to the URL in the `keywords` \n    parameter. \n\n5.  Start a scriptlet to get a search context and set some attributes in it: \n\n    ```markup\n    <%\n        SearchContext searchContext = SearchContextFactory.getInstance(request);\n\n        searchContext.setKeywords(keywords);\n        searchContext.setAttribute(\"paginationType\", \"more\");\n        searchContext.setStart(0);\n        searchContext.setEnd(10);\n    ```\n\n    To execute a search, you need a `SearchContext` object. \n    `SearchContextFactory` creates a `SearchContext` from the request\n    object. Add the user's search query to the `SearchContext` by passing the \n    `keywords` URL parameter to the `setKeywords` method. Then specify details \n    about pagination and how the search results should be displayed. \n\n6.  Still in the scriptlet, obtain an `Indexer` to run a search. Retrieve the \n    entry indexer from the map in @product@'s indexer registry by passing in the\n    indexer's class or class name:\n\n    ```java\n    Indexer<GuestbookEntry> indexer = IndexerRegistryUtil.getIndexer(GuestbookEntry.class);\n    ```\n\n7.  In the same scriptlet, use the indexer and the search context to run a \n    search: \n\n    ```java\n    Hits hits = indexer.search(searchContext);\n\n    List<GuestbookEntry> entries = new ArrayList<GuestbookEntry>();\n\n    for (int i = 0; i < hits.getDocs().length; i++) {\n            Document doc = hits.doc(i);\n\n            long entryId = GetterUtil\n            .getLong(doc.get(Field.ENTRY_CLASS_PK));\n\n            GuestbookEntry entry = null;\n\n            try {\n                    entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n            } catch (PortalException pe) {\n                    _log.error(pe.getLocalizedMessage());\n            } catch (SystemException se) {\n                    _log.error(se.getLocalizedMessage());\n            }\n\n            entries.add(entry);\n    }\n    ```\n\n    The search results return as `Hits` objects containing pointers to documents \n    that correspond to guestbook entries. You then loop through the hit \n    documents, retrieving the corresponding guestbook entries and adding them to\n    a list. \n\n8.  Finish the scriptlet by retrieving a list of all the guestbooks that exist \n    in the current site. Create a map between the guestbook IDs and the \n    guestbook names. \n\n    ```markup\n        List<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n        Map<String, String> guestbookMap = new HashMap<String, String>();\n\n        for (Guestbook guestbook : guestbooks) {\n                guestbookMap.put(Long.toString(guestbook.getGuestbookId()), guestbook.getName());\n        }\n    %>\n    ```\n\n    Making this single service call and creating a map is more efficient than \n    making separate service calls for each guestbook. \n\n9.  Display the search results in a search container:\n\n    ```markup\n    <liferay-ui:search-container delta=\"10\" \n        emptyResultsMessage=\"no-entries-were-found\" \n        total=\"<%= entries.size() %>\">\n            <liferay-ui:search-container-results\n                    results=\"<%= entries %>\"\n    />\n    ```\n\n    This specifies three attributes for the `<liferay-ui:search-container>` tag: \n\n    - `delta=\"10\"`: specifies that at most, 10 entries can appear per page. \n    - `emptyResultsMessage`: specifies the message indicating there are no \n      results. \n    - `total`: specifies the number of search results. \n\n    The `results` attribute of the tag `<liferay-ui:search-container-results>` \n    specifies the search results. This is easy since you stored the entries \n    resulting from the search in the `entries` list. \n\n10. Use the `<liferay-ui:search-container-row>` tag to set the name of the class \n    whose properties are displayed in each row:\n\n    ```markup\n    <liferay-ui:search-container-row\n            className=\"com.liferay.docs.guestbook.model.GuestbookEntry\"\n            keyProperty=\"entryId\" modelVar=\"entry\" escapedModel=\"<%=true%>\">\n    ```\n\n    This uses the `className` attribute for the class name and specifies the\n    entity's primary key attribute in the `keyProperty` attribute. The \n    `modelVar` property specifies the name of the `Entry` variable that's\n    available to each search container row. To ensure that each field of the \n    `entry` variable is escaped (sanitized), the `escapedModel` is `true`. This \n    prevents potential hacks that could occur if users submitted malicious code \n    into the Add Guestbook form, for example. \n\n11. Inside the `<liferay-ui:search-container-row>` tag, specify the four columns \n    to display: the guestbook entry's guestbook name, message, entry name, and \n    the actions JSP. The guestbook name is retrieved from the map created in the \n    scriptlet: \n\n    ```markup\n        <liferay-ui:search-container-column-text name=\"guestbook\"\n            value=\"<%=guestbookMap.get(Long.toString(entry.getGuestbookId()))%>\" />\n\n        <liferay-ui:search-container-column-text property=\"message\" />\n\n        <liferay-ui:search-container-column-text property=\"name\" />\n                \n        <liferay-ui:search-container-column-jsp\n            path=\"/guestbook/entry_actions.jsp\"\n            align=\"right\" />\n    </liferay-ui:search-container-row>\n    ```\n\n12. Use the `<liferay-ui:search-iterator>` tag to iterate through the search \n    results and handle pagination. Close the search container tag:\n\n    ```markup\n            <liferay-ui:search-iterator />\n    </liferay-ui:search-container>\n    ```\n\n13. At the bottom of `view_search.jsp`, declare a `Log` object. You used this \n    log in the `catch` clauses of the `try` clause that calls the \n    `GuestbookEntryLocalServiceUtil.getGuestbookEntry` method to retrieve the\n    guestbook entries. If this service call throws an exception, it's best to\n    log the error so a server administrator can determine what went wrong.\n    @product@'s convention is to declare custom logs for individual classes or\n    JSPs at the bottom of the file: \n\n    ```markup\n    <%!\n        private static Log _log = LogFactoryUtil.getLog(\"html.guestbook.view_search_jsp\");\n    %>\n    ```\n\n14. Finally, your `view_search.jsp` requires some extra imports. Add the \n    following imports to `init.jsp`:\n\n    ```markup\n    <%@ page import=\"com.liferay.portal.kernel.dao.search.SearchContainer\" %>\n    <%@ page import=\"com.liferay.portal.kernel.exception.PortalException\" %>\n    <%@ page import=\"com.liferay.portal.kernel.exception.SystemException\" %>\n    <%@ page import=\"com.liferay.portal.kernel.language.LanguageUtil\" %>\n    <%@ page import=\"com.liferay.portal.kernel.log.Log\" %>\n    <%@ page import=\"com.liferay.portal.kernel.log.LogFactoryUtil\" %>\n    <%@ page import=\"com.liferay.portal.kernel.search.Indexer\" %>\n    <%@ page import=\"com.liferay.portal.kernel.search.IndexerRegistryUtil\" %>\n    <%@ page import=\"com.liferay.portal.kernel.search.SearchContext\" %>\n    <%@ page import=\"com.liferay.portal.kernel.search.SearchContextFactory\" %>\n    <%@ page import=\"com.liferay.portal.kernel.search.Hits\" %>\n    <%@ page import=\"com.liferay.portal.kernel.search.Document\" %>\n    <%@ page import=\"com.liferay.portal.kernel.search.Field\" %>\n    <%@ page import=\"com.liferay.portal.kernel.util.GetterUtil\" %>\n    <%@ page import=\"com.liferay.portal.kernel.util.Validator\" %>\n    <%@ page import=\"com.liferay.portal.kernel.util.PortalUtil\" %>\n\n    <%@ page import=\"java.util.ArrayList\" %>\n    <%@ page import=\"java.util.Map\" %>\n    <%@ page import=\"java.util.HashMap\" %>\n\n    <%@ page import=\"javax.portlet.PortletURL\" %>\n    ```\n\nGood work! The Guestbook portlet now supports search! Now your users can find\nthose Guestbook Entries they were looking for. \n\n![Figure 2: The Guestbook Application now supports searching for indexed Guestbook Entries.](../../../../images/guestbook-portlet-search-conclusion.png)\n\nAs before, remove the `hypersonic` folder from the `data` folder of your Liferay\nbundle, and remove the modules from your server in @ide@. Start your server and\nredeploy all three modules, add some Guestbook entries, and try searching for\nthem. \n\nOnce indexing is in place, the asset framework can be added to the Guestbook\napplication. It provides functionality that's shared across different types of\ncontent like blog posts, message board posts, wiki articles, and more. This is\nthe heart of integration with @product@'s development platform. \n\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/09-assets/01-intro.markdown",
    "content": "---\nheader-id: assets-integrating-with-liferays-framework\n---\n\n# Assets: Integrating with Liferay's Framework\n\n[TOC levels=1-4]\n\nThe asset framework transforms entities into a common format that can be\npublished anywhere in your Site. Web content articles, blog posts, wiki\narticles, and documents are some asset-enabled entities that come\nout-of-the-box. By asset-enabling your own applications, you can take advantage\nof @product@'s functionality for publishing your application's data across your\nSite in the form of asset publisher entries, notifications, social activities,\nand more. \n\nThe asset framework includes these features:\n\n- Tags and categories\n- Comments and ratings\n- Related assets (a.k.a. Asset links)\n- Faceted search\n- Integration with the Asset Publisher portlet\n- Integration with the Search portlet\n- Integration with the Tags Navigation, Tag Cloud, and Categories \n  Navigation portlets\n\nNow you'll asset-enable the guestbook and guestbook entry entities. You'll\nimplement tags, categories, and related assets for guestbooks and guestbook\nentries. You'll implement comments and ratings in guestbook entries. You'll also\nlearn how asset-enabled guestbooks and guestbook entries integrate with\ncore portlets like the Asset Publisher, Tags Navigation, Tag Cloud, and\nCategories Navigation portlets. \n\nAs always, [source code](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook) is provided in case you get stuck.\n\nReady to start? \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/enabling-assets-at-the-service-layer\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/09-assets/02-enabling-assets-at-the-service-layer/01-enabling-assets-at-the-service-layer-intro.markdown",
    "content": "---\nheader-id: enabling-assets-at-the-service-layer\n---\n\n# Enabling Assets at the Service Layer\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p>Enabling Assets at the Service Layer</p><p>Step 1 of 3</p>\n</div>\n\nEach row in the `AssetEntry` table represents an asset. It has an `entryId`\nprimary key along with `classNameId` and `classPK` foreign keys. The\n`classNameId` specifies the asset's type. For example, an asset with\na `classNameId` of `JournalArticle` means that the asset represents a web\ncontent article (`JournalArticle` is the back-end name for a web content\narticle). An asset's `classPK` is the primary key of the entity represented by\nthe asset. \n\nFollow these steps to make asset services available to your entities' service\nlayers: \n\n1.  In the `guestbook-service` module's `service.xml` file, add the following \n    references directly above the closing `</entity>` tags for `Guestbook` and \n    `GuestbookEntry`: \n\n    ```xml\n    <reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetEntry\" />\n    <reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetLink\" />\n    ```\n\n    As mentioned above, you must use the `AssetEntry` service so your\n    application can add asset entries corresponding to guestbooks and guestbook\n    entries. You also use the `AssetLink` service to support related assets.\n    *Asset links* are @product@'s back-end term for related assets. \n\n2.  You must add finders---two for `Guestbook`s and two for\n    `GuestbookEntry`s---so your assets show in Asset Publisher, because it\n    searches for entities by `status` (i.e., is it Workflow-approved?) and by\n    `groupId` (i.e., is it in this Site?). Add these below the existing finders\n    for the `Guestbook` and `GuestbookEntry` entities: \n\n    ```xml\n    <finder name=\"Status\" return-type=\"Collection\">\n        <finder-column name=\"status\" />\n    </finder>\n\n    <finder name=\"G_S\" return-type=\"Collection\">\n        <finder-column name=\"groupId\" />\n        <finder-column name=\"status\" />\n    </finder>\n    ```\n\n3.  Run the `buildService` Gradle task. This task injects the objects referenced above\n    into your services for use. \n\n4.  Right-click `build.gradle` and select *Gradle* &rarr; *Refresh Gradle\n    Project*. \n\nGreat! Next, you'll handle assets in your service layer. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/09-assets/02-enabling-assets-at-the-service-layer/02-handling-assets-at-the-guestbook-service-layer.markdown",
    "content": "---\nheader-id: handling-assets-for-the-guestbook-service\n---\n\n# Handling Assets for the Guestbook Service\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Assets at the Service Layer</p><p>Step 2 of 3</p>\n</div>\n\nBefore you can update the Service Layer to add the Asset Renderers, you must\nupdate your `build.gradle` to provide the `portlet-api` and `javax.servlet-api`\nlibraries that the asset link service needs to function. \n\n1.  Open the `build.gradle` file in your `guestbook-service` module.\n\n2.  Add the following two lines in the `dependencies` section:\n\n    ```groovy\n    compileOnly group: \"javax.portlet\", name: \"portlet-api\"\n    compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n    ```\n\n3.  Save your `build.gradle` file, which refreshes your project. \n\nNow you'll update the guestbook service layer to use assets. You must update the\n`add`, `update`, and `delete` methods of your project's `GuestbookLocalServiceImpl`:\n\n1.  Open your project's `GuestbookLocalServiceImpl` class and find the \n    `addGuestbook` method. Add the call to add the asset entries below the call \n    that adds resources: \n\n    ```java\n    AssetEntry assetEntry = assetEntryLocalService.updateEntry(userId,\n                      groupId, guestbook.getCreateDate(),\n                      guestbook.getModifiedDate(), Guestbook.class.getName(),\n                      guestbookId, guestbook.getUuid(), 0,\n                      serviceContext.getAssetCategoryIds(),\n                      serviceContext.getAssetTagNames(), true, true, null, null, null, null,\n                      ContentTypes.TEXT_HTML, guestbook.getName(), null, null, null,\n                      null, 0, 0, null);\n\n    assetLinkLocalService.updateLinks(userId, assetEntry.getEntryId(),\n                      serviceContext.getAssetLinkEntryIds(),\n                      AssetLinkConstants.TYPE_RELATED);\n    ```\n\n    Calling `assetEntryLocalService.updateEntry` adds a new row (corresponding\n    to the guestbook that's being added) to the `AssetEntry` table in\n    @product@'s database. `AssetEntryLocalServiceImpl`'s `updateEntry` method\n    both adds and updates asset entries because it checks to see whether the\n    asset entry already exists in the database and then takes the appropriate \n    action. If you check the \n    [Javadoc](@platform-ref@/7.2-latest/javadocs/portal-impl/com/liferay/portlet/asset/service/impl/AssetEntryLocalServiceImpl.html) \n    for `AssetEntryLocalServiceUtil.updateEntry`, you'll see that this method is\n    overloaded. Now, why did you use a version of this method with such a long\n    method signature? Because there's only one version of `updateEntry` that\n    takes a `title` parameter (to set the asset entry's title). Since you want\n    to set the asset title to `guestbook.getName()`, that's the version you use. \n\n    Later, you'll update the Guestbook Admin portlet's form for adding \n    guestbooks to allow the selection of related assets, which are stored in the \n    database's `AssetLink` table. The `assetLinkLocalService.updateLinks` call \n    adds the appropriate entries to the table so related assets work for your \n    guestbook entities. The `updateEntry` method adds and updates asset entries \n    the same way `updateLink` adds and updates asset links. \n\n2.  Next, add the asset calls to `GuestbookLocalServiceImpl`'s `updateGuestbook`\n    method, directly after the resource call:\n\n    ```java\n    AssetEntry assetEntry = assetEntryLocalService.updateEntry(guestbook.getUserId(),\n                      guestbook.getGroupId(), guestbook.getCreateDate(),\n                      guestbook.getModifiedDate(), Guestbook.class.getName(),\n                      guestbookId, guestbook.getUuid(), 0,\n                      serviceContext.getAssetCategoryIds(),\n                      serviceContext.getAssetTagNames(), true, true, guestbook.getCreateDate(), \n                      null, null, null, ContentTypes.TEXT_HTML, guestbook.getName(), null, null, \n                      null, null, 0, 0, serviceContext.getAssetPriority());\n\n    assetLinkLocalService.updateLinks(serviceContext.getUserId(),\n                      assetEntry.getEntryId(), serviceContext.getAssetLinkEntryIds(),\n                      AssetLinkConstants.TYPE_RELATED);\n    ```\n\n    Here, `assetEntryLocalService.updateEntry` updates an existing asset entry \n    and `assetLinkLocalService.updateLinks` adds or updates that entry's asset \n    links (related assets). \n\n3.  Next, add the asset calls to the `deleteGuestbook` method, directly after \n    the resource calls:\n\n    ```java\n    AssetEntry assetEntry = assetEntryLocalService.fetchEntry(\n                      Guestbook.class.getName(), guestbookId);\n\n    assetLinkLocalService.deleteLinks(assetEntry.getEntryId());\n\n    assetEntryLocalService.deleteEntry(assetEntry);\n    ```\n\n    Here, you use the guestbook's class name and ID to retrieve the \n    corresponding asset entry. Then you delete that asset entry's asset links \n    and the asset entry itself. \n \n4.  Finally, organize your imports, save the file, and run Service Builder to\n    apply the changes. \n\nNext, you'll do the same thing for guestbook entries. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/09-assets/02-enabling-assets-at-the-service-layer/03-handling-assets-at-the-entry-service-layer.markdown",
    "content": "---\nheader-id: handling-assets-for-the-guestbookentry-service\n---\n\n# Handling Assets for the GuestbookEntry Service\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Enabling Assets at the Service Layer</p><p>Step 3 of 3</p>\n</div>\n\nNow you must update the guestbook entry entity's service methods. In these \nmethods, the calls you'll make to `assetEntryLocalService` and \n`assetLinkLocalService` are identical to the ones you made in the guestbook\nentity's service methods, except you're specifying assets for `GuestbookEntry`\nentities. \n\n1.  Open `GuestbookEntryLocalServiceImpl` and add the asset calls to the\n    `addGuestbookEntry` method after the resource calls: \n\n    ```java\n    AssetEntry assetEntry = assetEntryLocalService.updateEntry(userId,\n                      groupId, entry.getCreateDate(), entry.getModifiedDate(),\n                      GuestbookEntry.class.getName(), entryId, entry.getUuid(), 0,\n                      serviceContext.getAssetCategoryIds(),\n                      serviceContext.getAssetTagNames(), true, true, null, null, null, null,\n                      ContentTypes.TEXT_HTML, entry.getMessage(), null, null, null,\n                      null, 0, 0, null);\n\n    assetLinkLocalService.updateLinks(userId, assetEntry.getEntryId(),\n                      serviceContext.getAssetLinkEntryIds(),\n                      AssetLinkConstants.TYPE_RELATED);\n    ```\n\n2.  Next, add the asset calls to the `updateGuestbookEntry` method after the resource \n    calls: \n\n    ```java\n    AssetEntry assetEntry = assetEntryLocalService.updateEntry(userId,\n                  serviceContext.getScopeGroupId(),\n                  entry.getCreateDate(), entry.getModifiedDate(),\n                  GuestbookEntry.class.getName(), entryId, entry.getUuid(),\n                  0, serviceContext.getAssetCategoryIds(),\n                  serviceContext.getAssetTagNames(), true, true,\n                  entry.getCreateDate(), null, null, null,\n                  ContentTypes.TEXT_HTML, entry.getMessage(), null,\n                  null, null, null, 0, 0,\n                  serviceContext.getAssetPriority());\n\n    assetLinkLocalService.updateLinks(userId, assetEntry.getEntryId(),\n                  serviceContext.getAssetLinkEntryIds(),\n                  AssetLinkConstants.TYPE_RELATED);\n    ```\n\n3.  Add the asset calls to the `deleteGuestbookEntry` method after the resource calls: \n\n    ```java\n    AssetEntry assetEntry = assetEntryLocalService.fetchEntry(\n                          GuestbookEntry.class.getName(), entry.getEntryId());\n\n    assetLinkLocalService.deleteLinks(assetEntry.getEntryId());\n\n    assetEntryLocalService.deleteEntry(assetEntry);\n    ```\n\n4.  Organize your imports, save the file, and run Service Builder. \n\n5.  Finally, add these language keys to the\n    `guestbook-web/src/main/resources/content/Language.properties` file:\n\n    ```properties\n    model.resource.com.liferay.docs.guestbook.model.Guestbook=Guestbook\n    model.resource.com.liferay.docs.guestbook.model.GuestbookEntry=Guestbook Entry\n    ```\n\nExcellent! You've asset-enabled your guestbook and guestbook entry entities at\nthe service layer. Your next step is to implement asset renderers for these\nentities so they can be fully integrated into the asset framework. Every asset\nneeds an asset renderer class so the Asset Publisher portlet can display it.\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/09-assets/03-implementing-asset-renderers/01-implementing-asset-renderers-intro.markdown",
    "content": "---\nheader-id: implementing-asset-renderers\n---\n\n# Implementing Asset Renderers\n\n[TOC levels=1-4]\n\nAssets are display versions of entities, so they contain fields like `title`,\n`description`, and `summary`. @product@ uses these fields to display assets. \nAsset Renderers translate an entity into an asset via these fields. You must\ntherefore create and register Asset Renderer classes for your guestbook and\nguestbook entry entities. Without these classes, @product@ can't display your\nentities in Asset Publisher, Notifications, Activities, or anywhere else that\ndisplays assets. \n\nYour next task is to create these Asset Renderers. Ready to begin? \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/implementing-a-guestbook-asset-renderer\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/09-assets/03-implementing-asset-renderers/02-implementing-a-guestbook-asset-renderer.markdown",
    "content": "---\nheader-id: implementing-a-guestbook-asset-renderer\n---\n\n# Implementing a Guestbook Asset Renderer\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Implementing Asset Renderers</p><p>Step 1 of 2</p>\n</div>\n\n@product@'s asset renderers follow the factory pattern, so you must create a \n`GuestbookAssetRendererFactory` that instantiates the `GuestbookAssetRenderer`'s\nprivate guestbook object. Here, you'll create both classes. \n\nYou'll create the Asset Renderer class first. \n\n## Creating the AssetRenderer Class\n\nFollow these steps to create the `GuestbookAssetRenderer` class: \n\n1.  Create a new package called `com.liferay.docs.guestbook.web.internal.asset`\n    in the `guestbook-web` module's `src/main/java` folder. In this package,\n    create a `GuestbookAssetRenderer` class that extends @product@'s\n    `BaseJSPAssetRenderer` class. Extending this class gives you a head-start on\n    implementing the `AssetRenderer` interface: \n\n    ```java\n    public class GuestbookAssetRenderer extends BaseJSPAssetRenderer<Guestbook> {\n\n    }\n    ```\n \n2.  Add the constructor, the guestbook class variable, and the permissions \n    model resource. Most of the methods in this class are getters that return \n    fields from the private `_guestbook` object. Methods requiring a \n    permission check use `_guestbookModelResourcePermission`: \n\n    ```java\n    public GuestbookAssetRenderer(Guestbook guestbook, ModelResourcePermission<Guestbook> modelResourcePermission) {\n\n                _guestbook = guestbook;\n                _guestbookModelResourcePermission = modelResourcePermission;\n    }\n\n    // Add the other methods here\n    \n    private Guestbook _guestbook;\n    private final ModelResourcePermission<Guestbook> _guestbookModelResourcePermission;   \n    private Logger logger = Logger.getLogger(this.getClass().getName());\n    ```\n \n3.  The `BaseJSPAssetRenderer` abstract class that you're extending contains \n    dummy implementations of the `hasEditPermission` and `hasViewPermission`\n    methods that you must override with actual permission checks using the\n    permissions resources that you created earlier. Add these methods below the\n    comment labeled `Add the other methods here`:\n\n    ```java\n    @Override\n    public boolean hasEditPermission(PermissionChecker permissionChecker) \n    {\n        try {\n            return _guestbookModelResourcePermission.contains(\n                permissionChecker, _guestbook, ActionKeys.UPDATE);\n        }\n        catch (Exception e) {\n        }\n\n        return false;\n    }\n\n    @Override\n    public boolean hasViewPermission(PermissionChecker permissionChecker) \n    {\n        try {\n            return _guestbookModelResourcePermission.contains(\n                permissionChecker, _guestbook, ActionKeys.VIEW);\n        }\n        catch (Exception e) {\n        }\n\n        return true;\n    }\n    ```\n\n4.  Add the following getter methods to retrieve information about the \n    guestbook asset: \n\n    ```java\n    @Override\n    public Guestbook getAssetObject() {\n      return _guestbook;\n    }\n\n    @Override\n    public long getGroupId() {\n      return _guestbook.getGroupId();\n    }\n\n    @Override\n    public long getUserId() {\n\n      return _guestbook.getUserId();\n    }\n\n    @Override\n    public String getUserName() {\n      return _guestbook.getUserName();\n    }\n\n    @Override\n    public String getUuid() {\n      return _guestbook.getUuid();\n    }\n\n    @Override\n    public String getClassName() {\n      return Guestbook.class.getName();\n    }\n\n    @Override\n    public long getClassPK() {\n      return _guestbook.getGuestbookId();\n    }\n\n    @Override\n    public String getSummary(PortletRequest portletRequest, PortletResponse \n      portletResponse) {\n        return \"Name: \" + _guestbook.getName();\n    }\n\n    @Override\n    public String getTitle(Locale locale) {\n      return _guestbook.getName();\n    }\n\n    @Override\n    public boolean include(HttpServletRequest request, HttpServletResponse \n      response, String template) throws Exception {\n        request.setAttribute(\"GUESTBOOK\", _guestbook);\n        request.setAttribute(\"HtmlUtil\", HtmlUtil.getHtml());\n        request.setAttribute(\"StringUtil\", new StringUtil());\n        return super.include(request, response, template);\n    }\n    ```\n\n    The final method makes several utilities and the `Guestbook`\n    entity available in the `HttpServletRequest` object. \n\n5.  Override the `getJspPath` method. It returns a string representing the\n    path to the JSP that renders the guestbook asset. When the Asset Publisher\n    displays an asset's full content, it invokes the asset renderer class's\n    `getJspPath` method and passes a `template` string parameter that equals\n    `\"full_content\"`. This returns `/asset/guestbook/full_content.jsp` when the\n    `full_content` template string is passed as a parameter. You'll create this\n    JSP later when updating your application's user interface: \n\n    ```java\n    @Override\n    public String getJspPath(HttpServletRequest request, String template) {\n\n        if (template.equals(TEMPLATE_FULL_CONTENT)) {\n          request.setAttribute(\"gb_guestbook\", _guestbook);\n\n          return \"/asset/guestbook/\" + template + \".jsp\";\n        } else {\n          return null;\n        }\n    }\n    ```\n\n6.  Override the `getURLEdit` method. This method returns a URL for editing the\n    asset: \n\n    @Override\n    public PortletURL getURLEdit(LiferayPortletRequest liferayPortletRequest,\n      LiferayPortletResponse liferayPortletResponse) throws Exception {\n\n        PortletURL portletURL = liferayPortletResponse.createLiferayPortletURL(\n            getControlPanelPlid(liferayPortletRequest), GuestbookPortletKeys.GUESTBOOK,\n            PortletRequest.RENDER_PHASE);\n        portletURL.setParameter(\"mvcPath\", \"/guestbook/edit_guestbook.jsp\");\n        portletURL.setParameter(\"guestbookId\", String.valueOf(_guestbook.getGuestbookId()));\n        portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n\n        return portletURL;\n    }\n\n7.  Override the `getURLViewInContext` method. This method returns a URL to \n    view the asset in its native application: \n\n    ```java\n    @Override\n    public String getURLViewInContext(LiferayPortletRequest liferayPortletRequest,\n      LiferayPortletResponse liferayPortletResponse, String noSuchEntryRedirect) throws Exception {\n        try {\n          long plid = PortalUtil.getPlidFromPortletId(_guestbook.getGroupId(),\n              GuestbookPortletKeys.GUESTBOOK);\n\n          PortletURL portletURL;\n          if (plid == LayoutConstants.DEFAULT_PLID) {\n            portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(liferayPortletRequest),\n                GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n          } else {\n            portletURL = PortletURLFactoryUtil.create(liferayPortletRequest,\n                GuestbookPortletKeys.GUESTBOOK, plid, PortletRequest.RENDER_PHASE);\n          }\n\n          portletURL.setParameter(\"mvcPath\", \"/guestbook/view.jsp\");\n          portletURL.setParameter(\"guestbookId\", String.valueOf(_guestbook.getGuestbookId()));\n\n          String currentUrl = PortalUtil.getCurrentURL(liferayPortletRequest);\n\n          portletURL.setParameter(\"redirect\", currentUrl);\n\n          return portletURL.toString();\n\n        } catch (PortalException e) {\n\n            logger.log(Level.SEVERE, e.getMessage());\n\n        } catch (SystemException e) {\n\n            logger.log(Level.SEVERE, e.getMessage());\n\n        }\n\n        return noSuchEntryRedirect;\n    }\n    ```\n\n8.  Override the `getURLView` method. This method returns a URL to view the \n    asset from within the Asset Publisher: \n\n    ```java\n    @Override\n    public String getURLView(LiferayPortletResponse liferayPortletResponse, \n    WindowState windowState) throws Exception {\n\n        return super.getURLView(liferayPortletResponse, windowState);\n    }\n    ```\n\n10.  Organize imports (Ctrl-Shift-O) and save the file. Choose these imports: \n\n    - `java.util.logging.Logger` \n    - `com.liferay.portal.kernel.exception.SystemException` \n    - `java.util.logging.Level` \n    - `com.liferay.petra.string.StringUtil`\n \nNext you can create the `AssetRendererFactory` class. \n\n## Creating the GuestbookAssetRendererFactory Class\n\nFollow these steps to create the `GuestbookAssetRendererFactory`:\n\n1.  In the `com.liferay.docs.guestbook.web.internal.asset` package, create\n    a class called `GuestbookAssetRendererFactory` that extends @product@'s\n    `BaseAssetRendererFactory` class, and overwrite the generated constructor\n    and class variables with this: \n\n    ```java\n    @Component(immediate = true, \n      property = {\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK}, \n      service = AssetRendererFactory.class\n      )\n    public class GuestbookAssetRendererFactory extends \n      BaseAssetRendererFactory<Guestbook> {\n\n      public GuestbookAssetRendererFactory() {\n        setClassName(CLASS_NAME);\n        setLinkable(_LINKABLE);\n        setPortletId(GuestbookPortletKeys.GUESTBOOK); setSearchable(true);\n        setSelectable(true); \n      }         \n     \n      // Add the other methods here\n\n      private ServletContext _servletContext;\n      private GuestbookLocalService _guestbookLocalService;\n      private static final boolean _LINKABLE = true;\n      public static final String CLASS_NAME = Guestbook.class.getName();\n      public static final String TYPE = \"guestbook\";\n      private Logger logger = Logger.getLogger(this.getClass().getName());\n      private ModelResourcePermission<Guestbook> _guestbookModelResourcePermission;\n    }\n    ```\n\n    This code contains the class declaration, the constructor, and the class\n    variables. It sets the class name it creates an `AssetRenderer` for,\n    a portlet ID, and a `true` boolean (`_LINKABLE`). The boolean denotes\n    implemented methods that provide URLs in the generated `AssetRenderer`. \n\n    Insert the methods below where you see the comment `Add the other methods\n    here`. \n\n2.  Implement the `getAssetRenderer` method, which constructs new \n    `GuestbookAssetRenderer` instances for particular guestbooks. It uses the \n    `classPK` (primary key) parameter to retrieve the guestbook from the \n    database. It then calls the `GuestbookAssetRenderer`'s constructor, passing \n    the retrieved guestbook and permissions resource model as arguments: \n\n    ```java\n    @Override\n    public AssetRenderer<Guestbook> getAssetRenderer(long classPK, int type) \n    throws PortalException {\n      \n      Guestbook guestbook = _guestbookLocalService.getGuestbook(classPK);\n\n      GuestbookAssetRenderer guestbookAssetRenderer = \n      new GuestbookAssetRenderer(guestbook, _guestbookModelResourcePermission);\n\n      guestbookAssetRenderer.setAssetRendererType(type);\n      guestbookAssetRenderer.setServletContext(_servletContext);\n\n      return guestbookAssetRenderer;\n    }\n    ```\n\n2.  You're extending `BaseAssetRendererFactory`, an abstract class that\n    implements the `AssetRendererFactory` interface. To ensure that your custom \n    asset is associated with the correct entity, each asset renderer factory \n    must implement the `getClassName` and `getType` methods (among others): \n\n    ```java\n    @Override\n    public String getClassName() {\n        return CLASS_NAME;\n    }\n\n    @Override\n    public String getType() {\n        return TYPE;\n    }\n    ```\n\n3.  Implement the `hasPermission` method via the `GuestbookPermission` class:\n\n    ```java\n    @Override\n    public boolean hasPermission(PermissionChecker permissionChecker, \n    long classPK, String actionId) throws Exception {\n\n      Guestbook guestbook = _guestbookLocalService.getGuestbook(classPK);\n      long groupId = guestbook.getGroupId();\n      return GuestbookPermission.contains(permissionChecker, groupId, \n      actionId);\n    }\n    ```\n\n4.  Add the remaining code to create the portlet URL for the asset and specify \n    whether it's linkable:\n\n    ```java\n    @Override\n    public PortletURL getURLAdd(LiferayPortletRequest liferayPortletRequest,\n      LiferayPortletResponse liferayPortletResponse, long classTypeId) {\n        PortletURL portletURL = null;\n\n        try {\n          ThemeDisplay themeDisplay = (ThemeDisplay) \n          liferayPortletRequest.getAttribute(WebKeys.THEME_DISPLAY);\n\n          portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(themeDisplay),\n              GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n          portletURL.setParameter(\"mvcPath\", \"/guestbook/edit_guestbook.jsp\");\n          portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n\n          } catch (PortalException e) {\n          \n                logger.log(Level.SEVERE, e.getMessage()); \n                \n          }\n\n        return portletURL;\n    }\n\n    @Override\n    public boolean isLinkable() {\n        return _LINKABLE;\n    }\n\n    @Override\n    public String getIconCssClass() {\n      return \"bookmarks\";\n    }\n\n    @Reference(target = \"(osgi.web.symbolicname=com.liferay.docs.guestbook.portlet)\",\n        unbind = \"-\")\n    public void setServletContext(ServletContext servletContext) {\n        _servletContext = servletContext;\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setGuestbookLocalService(GuestbookLocalService guestbookLocalService) {\n        _guestbookLocalService = guestbookLocalService; \n    }\n    ```\n\n5.  Organize imports (Ctrl-Shift-O) and save the file. Select these imports:\n\n    - `java.util.logging.Logger`\n    - `java.util.logging.Level`\n\nGreat! The guestbook asset renderer is complete. Next, you'll create the entry \nasset renderer.\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/09-assets/03-implementing-asset-renderers/03-implementing-an-entry-asset-renderer.markdown",
    "content": "---\nheader-id: implementing-a-guestbook-entry-asset-renderer\n---\n\n# Implementing a Guestbook Entry Asset Renderer\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Implementing Asset Renderers</p><p>Step 2 of 2</p>\n</div>\n\nThe classes you'll create here are nearly identical to the \n`GuestbookAssetRenderer` and `GuestbookAssetRendererFactory` classes you created\nfor guestbooks in the previous step. This step provides the code needed for\nguestbook entries. Please review the previous sections to learn how this code\nworks. \n\n## Creating the GuestbookEntryAssetRenderer Class\n\nIn the `com.liferay.docs.guestbook.web.internal.asset` package, create\na `GuestbookEntryAssetRenderer` class that extends @product@'s\n`BaseJSPAssetRenderer` class. Replace the contents of your\n`GuestbookEntryAssetRenderer` class with the following code: \n\n```java\npackage com.liferay.docs.guestbook.web.internal.asset;\n\nimport com.liferay.asset.kernel.model.BaseJSPAssetRenderer;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.LayoutConstants;\nimport com.liferay.portal.kernel.portlet.LiferayPortletRequest;\nimport com.liferay.portal.kernel.portlet.LiferayPortletResponse;\nimport com.liferay.portal.kernel.portlet.PortletURLFactoryUtil;\nimport com.liferay.portal.kernel.security.permission.ActionKeys;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.util.HtmlUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.petra.string.StringUtil;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport java.util.Locale;\nimport javax.portlet.PortletRequest;\nimport javax.portlet.PortletResponse;\nimport javax.portlet.PortletURL;\nimport javax.portlet.WindowState;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class GuestbookEntryAssetRenderer extends BaseJSPAssetRenderer<GuestbookEntry> {\n\n    public GuestbookEntryAssetRenderer(GuestbookEntry entry, ModelResourcePermission<GuestbookEntry> modelResourcePermission) {\n\n        _entry = entry;\n        _guestbookEntryModelResourcePermission = modelResourcePermission;\n    }\n\n    @Override\n    public boolean hasViewPermission(PermissionChecker permissionChecker) \n    {\n        try {\n            return _guestbookEntryModelResourcePermission.contains(\n                    permissionChecker, _entry, ActionKeys.VIEW);\n        }\n        catch (Exception e) {\n        }\n\n        return true;\n    }\n\n    @Override\n    public GuestbookEntry getAssetObject() {\n        return _entry;\n    }\n\n    @Override\n    public long getGroupId() {\n        return _entry.getGroupId();\n    }\n\n    @Override\n    public long getUserId() {\n\n        return _entry.getUserId();\n    }\n\n    @Override\n    public String getUserName() {\n        return _entry.getUserName();\n    }\n\n    @Override\n    public String getUuid() {\n        return _entry.getUuid();\n    }\n\n    @Override\n    public String getClassName() {\n        return GuestbookEntry.class.getName();\n    }\n\n    @Override\n    public long getClassPK() {\n        return _entry.getEntryId();\n    }\n\n    @Override\n    public String getSummary(PortletRequest portletRequest, \n            PortletResponse portletResponse) {\n        return \"Name: \" + _entry.getName() + \". Message: \" + _entry.getMessage();\n    }\n\n    @Override\n    public String getTitle(Locale locale) {\n        return _entry.getMessage();\n    }\n\n    @Override\n    public boolean include(HttpServletRequest request, \n            HttpServletResponse response, String template) throws Exception {\n        request.setAttribute(\"ENTRY\", _entry);\n        request.setAttribute(\"HtmlUtil\", HtmlUtil.getHtml());\n        request.setAttribute(\"StringUtil\", new StringUtil());\n        return super.include(request, response, template);\n    }\n\n    @Override\n    public String getJspPath(HttpServletRequest request, String template) {\n\n        if (template.equals(TEMPLATE_FULL_CONTENT)) {\n            request.setAttribute(\"gb_entry\", _entry);\n\n            return \"/asset/entry/\" + template + \".jsp\";\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public PortletURL getURLEdit(LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse) throws Exception {\n        PortletURL portletURL = liferayPortletResponse.createLiferayPortletURL(\n                getControlPanelPlid(liferayPortletRequest), GuestbookPortletKeys.GUESTBOOK,\n                PortletRequest.RENDER_PHASE);\n        portletURL.setParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n        portletURL.setParameter(\"entryId\", String.valueOf(_entry.getEntryId()));\n        portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n\n        return portletURL;\n    }\n\n    @Override\n    public String getURLViewInContext(LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse, String noSuchEntryRedirect) \n                    throws Exception {\n        try {\n            long plid = PortalUtil.getPlidFromPortletId(_entry.getGroupId(),\n                    GuestbookPortletKeys.GUESTBOOK);\n\n            PortletURL portletURL;\n            if (plid == LayoutConstants.DEFAULT_PLID) {\n                portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(liferayPortletRequest),\n                        GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n            } else {\n                portletURL = PortletURLFactoryUtil.create(liferayPortletRequest,\n                        GuestbookPortletKeys.GUESTBOOK, plid, PortletRequest.RENDER_PHASE);\n            }\n\n            portletURL.setParameter(\"mvcPath\", \"/guestbook/view_entry.jsp\");\n            portletURL.setParameter(\"entryId\", String.valueOf(_entry.getEntryId()));\n\n            String currentUrl = PortalUtil.getCurrentURL(liferayPortletRequest);\n\n            portletURL.setParameter(\"redirect\", currentUrl);\n\n            return portletURL.toString();\n\n        } catch (PortalException e) {\n\n        } catch (SystemException e) {\n        }\n\n        return noSuchEntryRedirect;\n    }\n\n    @Override\n    public String getURLView(LiferayPortletResponse liferayPortletResponse, \n            WindowState windowState) throws Exception {\n\n        return super.getURLView(liferayPortletResponse, windowState);\n    }\n\n    @Override\n    public boolean isPrintable() {\n        return true;\n    }\n    private final ModelResourcePermission<GuestbookEntry> _guestbookEntryModelResourcePermission;\n    private GuestbookEntry _entry;\n}\n```\n\nThis class is similar to the `GuestbookAssetRenderer` class. For the\n`GuestbookEntryAssetRenderer.getSummary` method, you return a summary that\ndisplays the entry name (the name of the user who created the entry) and the\nentry message. \n\n`GuestbookAssetRenderer.getSummary` returns a summary that displays the \nguestbook name. `GuestbookEntryAssetRenderer.getTitle` returns the entry\nmessage. `GuestbookAssetRenderer.getTitle` returns the guestbook name. The other\nmethods of `GuestbookEntryAssetRenderer` are nearly identical to those of\n`GuestbookAssetRenderer`. \n\n## Creating the GuestbookEntryAssetRendererFactory Class\n\nNext, you must create the guestbook entry asset renderer's factory class. In the \n`com.liferay.docs.guestbook.web.internal.asset` package, create a class called \n`GuestbookEntryAssetRendererFactory` that extends @product@'s\n`BaseAssetRendererFactory` class. Replace its content with the following code: \n\n```java\npackage com.liferay.docs.guestbook.web.internal.asset;\n\nimport com.liferay.asset.kernel.model.AssetRenderer;\nimport com.liferay.asset.kernel.model.AssetRendererFactory;\nimport com.liferay.asset.kernel.model.BaseAssetRendererFactory;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookEntryPermission;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.LiferayPortletRequest;\nimport com.liferay.portal.kernel.portlet.LiferayPortletResponse;\nimport com.liferay.portal.kernel.portlet.LiferayPortletURL;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.theme.ThemeDisplay;\nimport com.liferay.portal.kernel.util.WebKeys;\n\nimport javax.portlet.PortletRequest;\nimport javax.portlet.PortletURL;\nimport javax.portlet.WindowState;\nimport javax.portlet.WindowStateException;\nimport javax.servlet.ServletContext;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\n@Component(\n        immediate = true,\n        property = {\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK},\n        service = AssetRendererFactory.class\n        )\npublic class GuestbookEntryAssetRendererFactory extends BaseAssetRendererFactory<GuestbookEntry> {\n\n    public GuestbookEntryAssetRendererFactory() {\n        setClassName(CLASS_NAME);\n        setLinkable(_LINKABLE);\n        setPortletId(GuestbookPortletKeys.GUESTBOOK);\n        setSearchable(true);\n        setSelectable(true);\n\n    }\n\n    @Override\n    public AssetRenderer<GuestbookEntry> getAssetRenderer(long classPK, int type)\n            throws PortalException {\n\n        GuestbookEntry entry = _guestbookEntryLocalService.getGuestbookEntry(classPK);\n\n        GuestbookEntryAssetRenderer guestbookEntryAssetRenderer = new GuestbookEntryAssetRenderer(entry, _guestbookEntryModelResourcePermission);\n\n        guestbookEntryAssetRenderer.setAssetRendererType(type);\n        guestbookEntryAssetRenderer.setServletContext(_servletContext);\n\n        return guestbookEntryAssetRenderer;\n    }\n\n    @Override\n    public String getClassName() {\n        return CLASS_NAME;\n    }\n\n    @Override\n    public String getType() {\n        return TYPE;\n    }\n\n    @Override\n    public boolean hasPermission(PermissionChecker permissionChecker,\n            long classPK, String actionId) throws Exception {\n\n        GuestbookEntry entry = _guestbookEntryLocalService.getGuestbookEntry(classPK);\n        return GuestbookEntryPermission.contains(permissionChecker, entry, actionId);\n    }\n\n    @Override\n    public PortletURL getURLAdd(LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse, long classTypeId) {\n\n        PortletURL portletURL = null;\n\n        try {\n            ThemeDisplay themeDisplay = (ThemeDisplay) liferayPortletRequest.getAttribute(WebKeys.THEME_DISPLAY);\n\n            portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(themeDisplay),\n                    GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n            portletURL.setParameter(\"mvcRenderCommandName\", \"/guestbook/edit_entry\");\n            portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n        } catch (PortalException e) {\n        }\n\n        return portletURL;\n    }\n\n    @Override\n    public PortletURL getURLView(LiferayPortletResponse liferayPortletResponse, WindowState windowState) {\n\n        LiferayPortletURL liferayPortletURL\n        = liferayPortletResponse.createLiferayPortletURL(\n                GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n\n        try {\n            liferayPortletURL.setWindowState(windowState);\n        } catch (WindowStateException wse) {\n\n        }\n        return liferayPortletURL;\n    }\n\n    @Override\n    public boolean isLinkable() {\n        return _LINKABLE;\n    }\n\n    @Override\n    public String getIconCssClass() {\n        return \"pencil\";\n    }\n\n    @Reference(target = \"(osgi.web.symbolicname=com.liferay.docs.guestbook.portlet)\",\n            unbind = \"-\")\n    public void setServletContext (ServletContext servletContext) {\n        _servletContext = servletContext;\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setGuestbookEntryLocalService(GuestbookEntryLocalService guestbookEntryLocalService) {\n        _guestbookEntryLocalService = guestbookEntryLocalService;\n    }\n\n\n    private GuestbookEntryLocalService _guestbookEntryLocalService;\n    private ServletContext _servletContext;\n    private static final boolean _LINKABLE = true;\n    public static final String CLASS_NAME = GuestbookEntry.class.getName();\n    public static final String TYPE = \"entry\";\n\n    private ModelResourcePermission<GuestbookEntry>\n    _guestbookEntryModelResourcePermission;\n\n\n}\n```\n\nNow your guestbook project's entities are fully asset-enabled. To test the\nfunctionality, add the Asset Publisher portlet to a page. Then add and edit\nguestbooks and guestbook entries. Then check the Asset Publisher portlet. The\nAsset Publisher dynamically displays assets of any kind from the current Site. \n\n![Figure 1: After you've implemented and registered your asset renderers for your custom entities, the Asset Publisher can display your entities.](../../../../images/custom-entities-asset-publisher.png)\n\nConfirm that the Asset Publisher displays the guestbooks and guestbook entries\nthat you added. \n\nGreat! Next, you'll update your portlets' user interfaces to use several asset\nframework features: comments, ratings, tags, categories, and related assets. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/09-assets/04-adding-asset-features-to-your-user-interface/01-adding-asset-features-to-your-user-interface-intro.markdown",
    "content": "---\nheader-id: adding-asset-features-to-your-user-interface\n---\n\n# Adding Asset Features to Your User Interface\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Adding Asset Features to Your UI</p><p>Step 1 of 5</p>\n</div>\n\nNow that your guestbook and guestbook entry entities are asset-enabled, you can\nadd asset functionality to your application. You'll start by implementing\ncomments, ratings, tags, categories, and related assets for guestbooks. Then\nyou'll do the same for guestbook entries. All the back-end support for these\nfeatures is already implemented. Your only task is to update your applications'\nuser interfaces to use these features. \n\nNow you'll create several new JSPs that need new imports. Add the following\nimports to the `guestbook-web` module project's `init.jsp` file: \n\n```markup\n<%@ taglib uri=\"http://liferay.com/tld/asset\" prefix=\"liferay-asset\" %>\n<%@ taglib uri=\"http://liferay.com/tld/comment\" prefix=\"liferay-comment\" %>\n\n<%@ page import=\"com.liferay.asset.kernel.service.AssetEntryLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.asset.kernel.service.AssetTagLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.asset.kernel.model.AssetEntry\" %>\n<%@ page import=\"com.liferay.asset.kernel.model.AssetTag\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.ListUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.comment.Discussion\" %>\n<%@ page import=\"com.liferay.portal.kernel.comment.CommentManagerUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.service.ServiceContextFunction\" %>\n```\n\nAdd these imports now so you don't run into errors as you work through the steps. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/09-assets/04-adding-asset-features-to-your-user-interface/02-creating-jsps-for-displaying-custom-assets-in-the-asset-publisher.markdown",
    "content": "---\nheader-id: creating-jsps-for-displaying-custom-assets-in-the-asset-publisher\n---\n\n# Creating JSPs for Displaying Custom Assets in the Asset Publisher\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Adding Asset Features to Your UI</p><p>Step 2 of 5</p>\n</div>\n\nBefore proceeding, you must tie up a loose end from the previous step. Remember \nthat you implemented `getJspPath` methods in your `GuestbookAssetRenderer` and\n`GuestbookEntryAssetRenderer` classes to JSPs that don't exist yet. These\nmethods return paths to JSPs the Asset Publisher uses to display the assets'\nfull content. The `getJspPath` method of `GuestbookAssetRenderer` returns\n`\"/asset/guestbook/full_content.jsp\"`, and the `getJspPath` method of\n`EntryAssetRenderer` returns `\"/asset/entry/full_content.jsp\"`. It's time to\ncreate these JSPs. \n\nFollow these steps: \n\n1.  In the `guestbook-web` module project, create a new folder called `asset` \n    under the `resources/META-INF/resources` folder. Add two folders to this new \n    folder: `entry` and `guestbook`. \n\n2.  Create a new file called `full_content.jsp` in the `/asset/guestbook` \n    folder. This JSP displays a guestbook asset's full content. Add the \n    following code to this file: \n\n    ```markup\n    <%@include file=\"../../init.jsp\"%>\n\n    <%\n    Guestbook guestbook = (Guestbook)request.getAttribute(\"gb_guestbook\");\n\n    guestbook = guestbook.toEscapedModel();\n    %>\n\n    <dl>\n            <dt>Name</dt>\n            <dd><%= guestbook.getName() %></dd>\n    </dl>\n    ```\n\n    This JSP grabs the guestbook object from the request and displays the\n    guestbook's name. In `GuestbookAssetRenderer`, the `getJspPath` method\n    added the `gb_guestbook` request attribute: \n\n    ```java\n    request.setAttribute(\"gb_guestbook\", _guestbook);\n    ```\n\n    The guestbook's `toEscapedModel` method belongs to the `GuestbookModelImpl`\n    class, which was generated by Service Builder. This method returns a *safe*\n    guestbook object (a guestbook in which each field is HTML-escaped). Calling \n    `guestbook = guestbook.toEscapedModel()` before displaying the guestbook \n    name ensures that your JSP won't display malicious code that's masquerading \n    as a guestbook name. \n\n3.  Next, in the `/asset/entry` folder, create a `full_content.jsp` for \n    displaying a guestbook entry asset's full content. Add the following code to \n    this file:\n\n    ```markup\n    <%@include file=\"../../init.jsp\"%>\n\n    <%\n    GuestbookEntry entry = (GuestbookEntry)request.getAttribute(\"gb_entry\");\n\n    entry = entry.toEscapedModel();\n    %>\n\n    <dl>\n            <dt>Guestbook</dt>\n            <dd><%= GuestbookLocalServiceUtil.getGuestbook(entry.getGuestbookId()).getName() %></dd>\n            <dt>Name</dt>\n            <dd><%= entry.getName() %></dd>\n            <dt>Message</dt>\n            <dd><%= entry.getMessage() %></dd>\n    </dl>\n    ```\n\nThis JSP shows a combination of fields from the Guestbook and the selected\nGuestbook Entry. \n\nAfter deploying your changes, test your new JSPs by clicking a guestbook's or\nguestbook entry's title in the Asset Publisher. The Asset Publisher renders\n`full_content.jsp`: \n\n![Figure 1: When you click the title for a guestbook or guestbook entry in the Asset Publisher, your `full_content.jsp` should be displayed.](../../../../images/asset-publisher-full-content.png)\n\nBy default, when displaying an asset's full view, the Asset Publisher displays \nadditional links for social media so you can publicize your asset. The *Back*\nicon and the *View in Context* link return you to the Asset Publisher's default\nview. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/09-assets/04-adding-asset-features-to-your-user-interface/03-enabling-tags-categories-and-assets-for-guestbooks.markdown",
    "content": "---\nheader-id: enabling-tags-categories-and-related-assets-for-guestbooks\n---\n\n# Enabling Tags, Categories, and Related Assets for Guestbooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Adding Asset Features to Your UI</p><p>Step 3 of 5</p>\n</div>\n\nSince you already asset-enabled guestbooks at the service layer, guestbook\nentities can now support tags and categories. All that's left is to enable them\nin the UI. In this step, you'll update the Guestbook Admin portlet's\n`edit_guestbook.jsp` so administrators can add, edit, or remove tags and\ncategories when adding or updating a guestbook. \n\n## Enabling Asset Features\n\nFollow these steps: \n\n1.  In the `guestbook-web` module's `/guestbook_admin/edit_guestbook.jsp`, \n    add the tags `<liferay-asset:asset-categories-error />` and \n    `<liferay-asset:asset-tags-error/>` to the `aui:form` below the closing \n    `</aui:button-row>` tag: \n\n    ```markup\n    <liferay-asset:asset-categories-error />\n    <liferay-asset:asset-tags-error />\n    ```\n\n    These tags display error messages if an error occurs with the tags or \n    categories submitted in the form. \n\n2.  Below the error tags, add a `<liferay-ui:panel>` tag surrounded by\n    a `<c:if>` statement: \n\n    ```markup\n    <c:if test=\"<%= guestbook != null %>\">\n\n        <liferay-ui:panel defaultState=\"closed\" extended=\"<%= false %>\"\n          id=\"guestbookCategorizationPanel\" persistState=\"<%= true %>\"\n          title=\"categorization\">\n\n        </liferay-ui:panel>\n\n    </c:if>\n    ```\n\n    The `<liferay-ui:panel>` tag generates a collapsible section. The tags\n    you'll add in the next step don't work if `guestbook` is `null`, so you only\n    display the panel if the current Guestbook is being edited. \n\n3.  Add input fields for tags and categories inside the panel section you just \n    created. Specify the `assetCategories` and `assetTags` types for the \n    `<aui:input />` tags. These input tags represent asset categories and asset\n    tags. You can group related input fields together with an `<aui:fieldset>`\n    tag. The tags generate the appropriate selectors for tags and categories and\n    displays those that have already been added to the guestbook: \n\n    ```markup\n\t<aui:fieldset>\n        <liferay-asset:asset-categories-selector className=\"<%= Guestbook.class.getName() %>\" classPK=\"<%= guestbook.getGuestbookId() %>\" />\n\t\t<liferay-asset:asset-tags-selector className=\"<%= Guestbook.class.getName() %>\" classPK=\"<%= guestbook.getGuestbookId() %>\" />\n\t</aui:fieldset>\n    ```\n\n4.  Add a second `<liferay-ui:panel>` tag under the existing one. In this new \n    tag, add an `<aui:fieldset>` tag containing a `<liferay-ui:asset-links>` \n    tag. To display the correct asset links (the selected guestbook's related \n    assets), set the `className` and `classPK` attributes: \n\n    ```markup\n    <liferay-ui:panel defaultState=\"closed\" extended=\"<%= false %>\"\n      id=\"guestbookAssetLinksPanel\" persistState=\"<%= true %>\"\n      title=\"related-assets\">\n      <aui:fieldset>\n        <liferay-asset:input-asset-links\n          className=\"<%= Guestbook.class.getName() %>\"\n          classPK=\"<%= guestbookId %>\" />\n      </aui:fieldset>\n    </liferay-ui:panel>\n    ```\n\nTest the updated `edit_guestbook.jsp` page by navigating to the Guestbook Admin \nportlet in the Control Panel and clicking *Add Guestbook*. After adding the\nGuestbook, edit it. You'll see a field for adding tags and a selector for\nselecting related assets.\n\n![Figure 1: Once you've updated your Guestbook Admin portlet's `edit_guestbook.jsp` page, you'll see forms for adding tags and selecting related assets.](../../../../images/guestbook-tags-related-assets.png)\n\nDon't do anything with these fields yet, because you're not done implementing \nassets. Next, you'll enable tags and categories for guestbook entries. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/09-assets/04-adding-asset-features-to-your-user-interface/04-enabling-tags-categories-and-assets-for-entries.markdown",
    "content": "---\nheader-id: enabling-tags-categories-and-related-assets-for-guestbook-entries\n---\n\n# Enabling Tags, Categories, and Related Assets for Guestbook Entries\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Adding Asset Features to Your UI</p><p>Step 4 of 5</p>\n</div>\n\nEnabling tags, categories, and related assets for guestbook entries is similar \nto enabling them for guestbooks. Please refer back to the previous step for\na detailed explanation. \n\nOpen your `guestbook-web` module's `guestbook/edit_entry.jsp` file. \nYou'll add two pieces of code: a header for navigation and a panel for tags and\ncategories similar to the one you added to the `edit_guestbook.jsp` file. \n\n1.  Add the header after the `addEntry` action URL tag: \n\n    ```markup\n    <liferay-ui:header\n        backURL=\"<%= viewURL.toString() %>\"\n        title=\"<%= entry == null ? \"Add Entry\" : entry.getName() %>\"\n    />\n    ```\n\n2. Add the asset tags/categories/links in a collapsible panel after the closing\n   `</aui:fieldset>`: \n\n   ```markup\n<liferay-asset:asset-categories-error />\n<liferay-asset:asset-tags-error />\n\n<liferay-ui:panel defaultState=\"closed\" \n                  extended=\"<%= false %>\" id=\"entryCategorizationPanel\" \n                  persistState=\"<%= true %>\" title=\"categorization\">\n\n    <aui:fieldset>\n       <liferay-asset:asset-categories-selector className=\"<%= GuestbookEntry.class.getName() %>\" classPK=\"<%= entryId %>\" />\n       <liferay-asset:asset-tags-selector className=\"<%= GuestbookEntry.class.getName() %>\" classPK=\"<%= entryId %>\" />\n    </aui:fieldset>\n\n</liferay-ui:panel>\n\n<liferay-ui:panel defaultState=\"closed\" \n                  extended=\"<%= false %>\" \n                  id=\"entryAssetLinksPanel\" \n                  persistState=\"<%= true %>\" \n                  title=\"related-assets\">\n\n    <aui:fieldset collapsed=\"<%= true %>\" collapsible=\"<%= true %>\" label=\"related-assets\">\n            \n        <liferay-asset:input-asset-links\n            className=\"<%= GuestbookEntry.class.getName() %>\"\n            classPK=\"<%= entryId %>\"\n        />\n            \n    </aui:fieldset>\n</liferay-ui:panel>\n```\n\nTest your JSP by using the Guestbook portlet to add and update Guestbook \nentries. Add and remove tags, categories, and related assets. \n\n| **Note:** Setting your custom asset as the *Main Asset* of a page is\n| required to display related assets in the Related Assets portlet. This is done\n| when creating\n| [Friendly URLs](/docs/7-2/tutorials/-/knowledge_base/t/making-urls-friendlier)\n| in a later step.\n\nWell done! Next, you'll enable comments and ratings for guestbook entries. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/09-assets/04-adding-asset-features-to-your-user-interface/05-enabling-comments-and-ratings-for-entries.markdown",
    "content": "---\nheader-id: enabling-comments-and-ratings-for-guestbook-entries\n---\n\n# Enabling Comments and Ratings for Guestbook Entries\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Adding Asset Features to Your UI</p><p>Step 5 of 5</p>\n</div>\n\nThe asset framework lets users comment on and rate assets. As with tags,\ncategories, and related assets, you must update the user interface to expose\nthese features. Good application design requires that you have a View page where\nusers can rate and comment on assets. Follow these steps to enable comments and\nratings on guestbook entries: \n\n1.  Create a new file called `view_entry.jsp` in your `guestbook-web` module \n    project's `src/main/resources/META-INF/resources/guestbook` folder. \n\n2.  Add a Java scriptlet to the file you just created. In this scriptlet, use an \n    `entryId` request attribute to get a `GuestbookEntry` object. For security\n    reasons, convert this object to an escaped model as discussed in the earlier\n    step \n    [Creating JSPs for Displaying Custom Assets in the Asset Publisher](/docs/7.2/tutorials/-/knowledge_base/t/creating-jsps-for-displaying-custom-assets-in-the-asset-publisher):\n\n    ```markup\n    <%@ include file=\"../init.jsp\"%>\n\n    <%\n      long entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\n      long guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n      GuestbookEntry entry = null;\n\n      entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n\n      entryId = entry.getEntryId();\n\n      entry = entry.toEscapedModel();\n\n      AssetEntry assetEntry = \n      AssetEntryLocalServiceUtil.getEntry(GuestbookEntry.class.getName(), \n      entry.getEntryId());\n    ```\n\n3.  Next, update the breadcrumb entry with the current entry's name: \n\n    ```markup\n    String currentURL = PortalUtil.getCurrentURL(request);\n    PortalUtil.addPortletBreadcrumbEntry(request, entry.getMessage(),\n    currentURL);\n    ```\n\n4.  End the scriptlet by adding the names of the current entry's existing \n    asset tags as keywords to the portal page. These tag names appear in a \n    `<meta content=\"[tag names here]\" lang=\"en-US\" name=\"keywords\" />` element \n    in your portal page's `<head>` section. These keywords can help search \n    engines find and index your page: \n\n    ```markup\n        PortalUtil.setPageSubtitle(entry.getMessage(), request);\n        PortalUtil.setPageDescription(entry.getMessage(), request);\n\n        List<AssetTag> assetTags = \n        AssetTagLocalServiceUtil.getTags(GuestbookEntry.class.getName(), \n        entry.getEntryId());\n        PortalUtil.setPageKeywords(ListUtil.toString(assetTags, \"name\"), \n        request);\n    %>\n    ```\n\n5.  After the scriptlet, specify the URLs for the page and back link: \n\n    ```markup\n    <liferay-portlet:renderURL varImpl=\"viewEntryURL\">\n      <portlet:param name=\"mvcPath\"\n        value=\"/guestbook/view_entry.jsp\" />\n      <portlet:param name=\"entryId\" value=\"<%=String.valueOf(entryId)%>\" />\n    </liferay-portlet:renderURL>\n\n    <liferay-portlet:renderURL varImpl=\"viewURL\">\n      <portlet:param name=\"mvcPath\"\n        value=\"/guestbook/view.jsp\" />\n    </liferay-portlet:renderURL>\n\n    <liferay-ui:header backURL=\"<%=viewURL.toString()%>\"\n      title=\"<%=entry.getName()%>\" \n    />\n    ```\n\n6.  Next, define the page's main content. Display the guestbook's name and the \n    entry's name and message with the `<dl>`, `<dt>`, and `<dd>` tags: \n\n    ```markup\n    <dl>\n      <dt>Guestbook</dt>\n      <dd><%=GuestbookLocalServiceUtil.getGuestbook(entry.getGuestbookId()).getName()%></dd>\n      <dt>Name</dt>\n      <dd><%=entry.getName()%></dd>\n      <dt>Message</dt>\n      <dd><%=entry.getMessage()%></dd>\n    </dl>\n    ```\n\n    This is the same way you defined the page's main content in \n    `/asset/full_content.jsp`. \n\n7.  Next, use a `<liferay-ui:panel-container>` tag to create a panel container. \n    Inside this tag, use a `<liferay-ui:panel>` tag to create a panel to display\n    the comments and ratings components: \n\n    ```markup\n    <liferay-ui:panel-container extended=\"<%=false%>\"\n      id=\"guestbookCollaborationPanelContainer\" persistState=\"<%=true%>\">\n      <liferay-ui:panel collapsible=\"<%=true%>\" extended=\"<%=true%>\"\n        id=\"guestbookCollaborationPanel\" persistState=\"<%=true%>\"\n        title=\"Collaboration\">\n    ```\n\n8.  Add the ratings component with the `<liferay-ui:ratings>` tag:\n\n    ```markup\n    <liferay-ui:ratings className=\"<%=GuestbookEntry.class.getName()%>\"\n      classPK=\"<%=entry.getEntryId()%>\" type=\"stars\" />\n\n    <br />\n    ```\n\n9.  Next, add a scriptlet to retrieve the comments discussion object:\n\n    ```markup\n    <% \n        Discussion discussion = \n        CommentManagerUtil.getDiscussion(user.getUserId(), \n        scopeGroupId, GuestbookEntry.class.getName(), \n        entry.getEntryId(), new ServiceContextFunction(request));\n    %>\n    ```\n\n10.  Below that add the tag for tracking the number of comments:\n\n    ```markup\n    <c:if test=\"<%= discussion != null %>\">\n      <h2>\n        <strong><liferay-ui:message arguments=\"<%= discussion.getDiscussionCommentsCount() %>\" key='<%= (discussion.getDiscussionCommentsCount() == 1) ? \"x-comment\" : \"x-comments\" %>' /></strong>\n    ```\n\n11. Create the `liferay-comment:discussion` tag, which creates the comments\n    form, *Reply* button, and retrieves the discussion content. It also\n    handles the form action of posting the comment without requiring\n    you to create a portlet action URL.\n\n    ```markup\n      <liferay-comment:discussion\n        className=\"<%= GuestbookEntry.class.getName() %>\"\n        classPK=\"<%= entry.getEntryId() %>\"\n        discussion=\"<%= discussion %>\"\n        formName=\"fm2\"\n        ratingsEnabled=\"true\"\n        redirect=\"<%= currentURL %>\"\n        userId=\"<%= entry.getUserId() %>\"\n        />\n    </c:if>\n\n  </liferay-ui:panel>\n</liferay-ui:panel-container>\n    ```\n\n12. To restrict comments and ratings access to logged-in users, wrap the whole \n    panel container in a `<c:if>` tag that tests the expression \n    `themeDisplay.isSignedIn()`:\n\n    ```markup\n    <c:if test=\"<%= themeDisplay.isSignedIn() %>\">\n        ... your panel container ...\n    </c:if>\n    ```\n\n    Make sure you add the closing `</c:if>` tag after the closing \n    `</liferay-ui:panel-container>` tag.\n\n| **Note:** Discussions (comments) are implemented as message board messages. In\n| the `MBMessage` table, there's a `classPK` column. This `classPK` represents\n| the guestbook entry's `entryId`, linking the comment to the guestbook. Ratings\n| are stored in the `RatingsEntry` table. Similarly, the `RatingsEntry` table\n| contains a `classPK` column that links the guestbook entry to the rating. Using\n| a `classPK` foreign key in one table to represent the primary key of another\n| table is a common pattern throughout @product@.\n\nNext, you'll update the guestbook actions to use the new view. \n\n## Updating the Entry Actions JSP\n\nYour `view_entry.jsp` page is currently orphaned. Fix this by adding the *View*\noption to the Actions Menu. Open the `/guestbook/entry_actions.jsp`\nand find the following line:\n\n```markup\n<liferay-ui:icon-menu>\n```\n\nAdd the following lines below it:\n\n```markup\n<portlet:renderURL var=\"viewEntryURL\">\n  <portlet:param name=\"entryId\"\n    value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n  <portlet:param name=\"mvcPath\"\n    value=\"/guestbook/view_entry.jsp\" />\n</portlet:renderURL>\n\n<liferay-ui:icon message=\"View\" url=\"<%= viewEntryURL.toString() %>\" />\n```\n\nHere, you create a URL that points to `view_entry.jsp`. Test this link by\nselecting the *View* option in a guestbook entry's Actions Menu. Then test your\ncomments and ratings. \n\nExcellent! You've asset-enabled the guestbook and guestbook entry entities and \nenabled tags, categories, and related assets for both entities. You've also \nenabled comments and ratings for guestbook entry entities! Great job! \n\n![Figure 1: Now you can see comments, rating, and the full range of asset features.](../../../../images/asset-publisher-full-content-finished.png)\n\nYour next task is to add Workflow, so you can approve or deny guestbook entries,\nthereby preventing people from spamming your guestbook. \n\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/10-workflow/01-intro.markdown",
    "content": "---\nheader-id: using-workflow\n---\n\n# Using Workflow\n\n[TOC levels=1-4]\n\nThe Guestbook application accepts submissions from any logged in user, so \nthere's no telling what people could post. Illegal data, objectionable content, \nthe entire contents of Don Quixote: all of these and more are possibilities. \nYou can make sure user posts don't run afoul of the law or policy by enabling \n*workflow* in your application. \n\nWorkflow is a review process that ensures a submitted entity isn't published\nbefore it's reviewed. To prevent posting objectionable content, an initially\nsubmitted Guestbook entry should be marked as a *draft* and sent through the\nworkflow framework. It comes back to the application code ready to have any\nrelevant fields updated in the database based on its status. The view layer must\nfilter entities by status to display only reviewed entities.\n\n| **Note:** The exact review process is defined separately from the code that\n| enables workflow. An XML file provides the definition of a workflow in\n| @product@. If you're a Liferay Digital Enterprise subscriber, you have access to\n| the Workflow Designer, which offers a convenient drag-and-drop user\n| interface for designing workflow definition files. You can read more about this\n| in Liferay DXP's\n| [documentation](https://help.liferay.com/hc/en-us/articles/360028821892-Workflow-Designer).\n| @product@ comes with a workflow definition called the *Single Approver*\n| definition, but you can write your own workflow definitions according to your\n| organization's requirements.\n| \n| A few additional definitions are included in @product@'s source code, which you\n| can use to see how workflow definitions are defined. To discover how to access\n| these files, see\n| [here](/docs/7-2/user/-/knowledge_base/u/enabling-workflow).\n\nThis tutorial instructs the reader in workflow-enabling the Guestbook App's\n`Guestbook` and `GuestbookEntry` entities to ensure that only approved content\nis published after review.\n\n![Figure 1: Enable workflow in your assets, just like @product@'s own assets.](../../../images/workflow-config.png)\n\nThere are five steps to enabling workflow:\n\n1.  Update the service layer to set each entity's status fields.\n\n2.  Send the entity to @product@'s workflow framework. \n\n3.  Add *getter* methods that account for an entity's workflow status.\n\n4.  Handle the entity as it returns from the workflow framework.\n\n5.  Update the user interface to account for workflow status.\n\nThe first three steps happen in the service layer, so that's a good place to\nstart.\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/supporting-workflow-at-the-service-layer\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/10-workflow/02-supporting-workflow-status/01-enabling-workflow-service-layer-intro.markdown",
    "content": "---\nheader-id: supporting-workflow-at-the-service-layer\n---\n\n# Supporting Workflow at the Service Layer\n\n[TOC levels=1-4]\n\nWhen you \n[asset enabled the Guestbook Application](/docs/7-2/tutorials/-/knowledge_base/t/assets-integrating-with-liferays-framework),\nyou used four database columns in the Guestbook entities that keep track of\nworkflow status (they were added in the beginning; celebrate!). The necessary\nfields are `status`, `statusByUserName`, `statusByUserId`, and `statusDate`. The\ncolumns are defined in the `guestbook-service` module's `service.xml` file.\n\n```xml\n<column name=\"status\" type=\"int\" />\n<column name=\"statusByUserId\" type=\"long\" />\n<column name=\"statusByUserName\" type=\"String\" />\n<column name=\"statusDate\" type=\"Date\" />\n```\n\nThe `status` field tells you the current status of the entity (it defaults to\n`0`, which evaluates to *approved*). The other status fields store the date of\nthe last change (`statusDate`) along with the ID and name of the user\n(`statusByUserId` and `statusByUserName`) who made the update.\n\nAlthough the status columns are in the Guestbook application's entity tables,\nyou must update the local service implementation's `add` methods to set them,\nand while you're there, send the entity to the workflow framework. You'll also\nwrite a method to update the status fields when the entity returns from the\nworkflow framework, along with getters that take workflow status as a parameter.\nThat sounds like a lot of work, but thanks to Service Builder, you must change\nonly three files: `service.xml`, `GuestbookLocalServiceImpl`, and\n`GuestbookEntryLocalServiceImpl`.\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/setting-the-guestbook-status\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/10-workflow/02-supporting-workflow-status/02-setting-guestbook-status.markdown",
    "content": "---\nheader-id: setting-the-guestbook-status\n---\n\n# Setting the Guestbook Status\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step\">\n    <p>Supporting Workflow at the Service Layer<br>Step 1 of 3</p>\n</div>\n\nBefore now, you set the status of all added guestbooks to approved in the\nservice layer. Now you'll set it to draft and pass it to the workflow framework. \n\n1.  From `guestbook-service`, open `GuestbookLocalServiceImpl` and add the\n    status fields below the existing setter methods in the `addGuestbook`\n    method:\n\n    ```java\n    guestbook.setStatus(WorkflowConstants.STATUS_DRAFT);\n    guestbook.setStatusByUserId(userId);\n    guestbook.setStatusByUserName(user.getFullName());\n    guestbook.setStatusDate(serviceContext.getModifiedDate(null));\n    ```\n\n    This manually populates the status fields and sets the workflow status as\n    a draft in the `GB_GuestbookEntry` database table. At this point they're\n    identical to the similarly named non-status counterparts (like `setUserId`\n    and `setStatusByUserId`), but they'll be updated independently in the\n    `updateStatus` method you write later.\n\n2.  Still in the `addGuestbook` method, place the following code right before\n    the `return` statement:\n\n    ```java\n    WorkflowHandlerRegistryUtil.startWorkflowInstance(guestbook.getCompanyId(), \n                guestbook.getGroupId(), guestbook.getUserId(), Guestbook.class.getName(), \n                guestbook.getPrimaryKey(), guestbook, serviceContext);\n    ```\n\n    The call to `startWorkflowInstance` detects whether workflow is installed\n    and enabled. If it isn't, the added entity is automatically marked as\n    approved. The `startWorkflowInstance` call also calls your\n    `GuestbookWorkflowHandler` class, which you'll create later. \n\n3.  Organize imports (*[CTRL]+[SHIFT]+O*), and save your work.\n\nThe `startWorkflowInstance` method is where your entity enters the workflow\nframework, but you're not finished yet. Just like you wouldn't drop your child\noff at college and then change your number and move to a new address, you're\nnot going to abandon your `Guestbook` entity (yet). \n\n## Creating the updateStatus Method\n\nExert control over how the status fields are updated in the database.\n\n1.  Create an `updateStatus` method in `GuestbookLocalServiceImpl`, immediately\n    following the `deleteGuestbook` method. Here's the first half of it:\n\n    ```java\n    public Guestbook updateStatus(long userId, long guestbookId, int status,\n\t\t\tServiceContext serviceContext) throws PortalException,\n\t\t\tSystemException {\n\n\t\tUser user = userLocalService.getUser(userId);\n\t\tGuestbook guestbook = getGuestbook(guestbookId);\n\n\t\tguestbook.setStatus(status);\n\t\tguestbook.setStatusByUserId(userId);\n\t\tguestbook.setStatusByUserName(user.getFullName());\n\t\tguestbook.setStatusDate(new Date());\n\n\t\tguestbookPersistence.update(guestbook);\n    ```\n\n    If this method is called, it's because your entity is returning from the\n    workflow framework, and it's time to update the status values in the database.\n    Set the status fields, then persist the updated entity to the database. \n \n2.  Before saving, finish the method:\n\n    ```java\n\t\tif (status == WorkflowConstants.STATUS_APPROVED) {\n\n\t\t\tassetEntryLocalService.updateVisible(Guestbook.class.getName(),\n\t\t\t\t\tguestbookId, true);\n\n\t\t} else {\n\n\t\t\tassetEntryLocalService.updateVisible(Guestbook.class.getName(),\n\t\t\t\t\tguestbookId, false);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n    ```\n\n    This `if` statement determines the visibility of the asset based on its workflow\n    status. If it's approved, the `assetEntryLocalService.updateVisible` method sets\n    the guestbook in question to `true` so it can be displayed in the Asset\n    Publisher and in the search results. Otherwise (`else`) it sets the visibility\n    to `false` to ensure that unapproved guestbooks aren't displayed to users in the\n    Asset Publisher or the Search portlet.\n\n3.  There's one more update to make in the `deleteGuestbook` method. When\n    deleting, you must clean up the workflow system's database tables to avoid\n    leaving orphaned entries when the backing entity is deleted. Before making\n    the method call, open `service.xml` and add the following tag below the\n    existing `<reference>` tags in the `Guestbook` entity:\n\n    ```xml\n    <reference entity=\"WorkflowInstanceLink\" package-path=\"com.liferay.portal\" />\n    ```\n\n4.  Back in `GuestbookLocalServiceImpl`, find the `deleteGuestbook` method and\n    put this method call right before the `return` statement:\n\n    ```java\n    workflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(\n        guestbook.getCompanyId(), guestbook.getGroupId(),\n        Guestbook.class.getName(), guestbook.getGuestbookId());\n    ```\n\n5.  Organize imports (*[CTRL]+[SHIFT]+O*) and save your work. Then run the\n    `buildService` Gradle task. It injects the\n    `WorkflowInstanceLinkLocalService` service into a protected variable in\n    `GuesbookLocalServiceBaseImpl`. Since `GuestbookLocalServiceImpl` extends\n    the base class, you can use it directly.\n\n6.  Run *Refresh Gradle Project*. \n\nNow the guestbook entity's service layer populates the status fields in the\ndatabase, sends the entity into the workflow framework, and cleans up when it's\ndeleted. You'll do the same thing for guestbook entries next.\n\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/10-workflow/02-supporting-workflow-status/03-setting-entry-status.markdown",
    "content": "---\nheader-id: setting-the-entry-workflow-status\n---\n\n# Setting the Entry Workflow Status\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step\">\n    <p>Supporting Workflow at the Service Layer<br>Step 2 of 3</p>\n</div>\n\nNow you'll set the status fields, introduce entries to the workflow framework,\nand add the `updateStatus` method to `GuestbookEntryLocalServiceImpl`. It works\nthe same as it did for guestbooks.\n\n1.  Add the following lines in the `addGuestbookEntry` method, immediately after\n    the existing setter methods (e.g., `entry.setMessage(message)`):\n\n    ```java\n    entry.setStatus(WorkflowConstants.STATUS_DRAFT);\n    entry.setStatusByUserId(userId);\n    entry.setStatusByUserName(user.getFullName());\n    entry.setStatusDate(serviceContext.getModifiedDate(null));\n    ```\n\n2.  Still in the `addGuestbookEntry` method, place the following code right\n    before the `return` statement:\n\n    ```java\n    WorkflowHandlerRegistryUtil.startWorkflowInstance(entry.getCompanyId(), \n\t\t\t\tentry.getGroupId(), entry.getUserId(), GuestbookEntry.class.getName(), \n\t\t\t\tentry.getPrimaryKey(), entry, serviceContext);\n    ```\n\n    The `startWorkflowInstance` call eventually directs the workflow processing to\n    your `GuestbookEntryWorkflowHandler` class, which you'll create later. That\n    class is responsible for making sure the entity is updated in the database (via\n    an `updateStatus` method), but it's best practice to make persistence calls in\n    the service layer. \n\n3.  Add a corresponding `updateStatus` method here in\n    `GuestbookEntryLocalServiceImpl`. Add this method to the bottom of the\n    class:\n\n     ```java\n     public GuestbookEntry updateStatus(long userId, long guestbookId, long entryId, int status,\n\t\t\tServiceContext serviceContext) throws PortalException,\n\t\t\tSystemException {\n\n\t\tUser user = userLocalService.getUser(userId);\n\t\tGuestbookEntry entry = getGuestbookEntry(entryId);\n\n\t\tentry.setStatus(status);\n\t\tentry.setStatusByUserId(userId);\n\t\tentry.setStatusByUserName(user.getFullName());\n\t\tentry.setStatusDate(new Date());\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\tif (status == WorkflowConstants.STATUS_APPROVED) {\n\n\t\t\tassetEntryLocalService.updateVisible(GuestbookEntry.class.getName(),\n\t\t\t\t\tentryId, true);\n\n\t\t} else {\n\n\t\t\tassetEntryLocalService.updateVisible(GuestbookEntry.class.getName(),\n\t\t\t\t\tentryId, false);\n\t\t}\n\n\t\treturn entry;\n\t}\n    ```\n\n4.  As with Guestbooks, you must add a call  to `deleteWorkflowInstanceLinks` in\n    the entry's delete method to avoid leaving orphaned database entries in the\n    `workflowinstancelinks` table. First add the following `<reference>` tag to\n    `service.xml`, this time in the `entry` entity section, below the existing\n    reference tags:\n\n    ```xml\n    <reference entity=\"WorkflowInstanceLink\" package-path=\"com.liferay.portal\" />\n    ```\n\n5.  Add the following method call to the `deleteGuestbookEntry` method in\n    `GuestbookEntryLocalServiceImpl`, right before the `return` statement:\n\n    ```java\n    workflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(\n        entry.getCompanyId(), entry.getGroupId(),\n        GuestbookEntry.class.getName(), entry.getEntryId());\n    ```\n\n6.  Organize imports (*[CTRL]+[SHIFT]+O*), save your work, run Service\n    Builder, and refresh the Gradle project.\n\nNow both entities support the status of the entity and can handle it as it\nenters the workflow framework and as it returns from the workflow framework.\nThere's one more update to make in the local service implementation classes:\nadding getter methods that take the status as a parameter. Later you'll use\nthese methods in the view layer so you can display only approved guestbooks and\nentries. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/10-workflow/02-supporting-workflow-status/04-finding-entities-by-status.markdown",
    "content": "---\nheader-id: retrieving-guestbooks-and-entries-by-status\n---\n\n# Retrieving Guestbooks and Entries by Status\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step\">\n    <p>Supporting Workflow at the Service Layer<br>Step 3 of 3</p>\n</div>\n\nThe service implementation for both entities now supports adding the status\nfields to the database tables. There's one more update to make in the service\nlayer, but to understand why, you must think about the view layer. When the\nGuestbook portlet displays entries, you must make sure it doesn't show entries\nthat haven't been approved. Currently, the entry's view layer shows all\nguestbooks:\n\n\t\tList<Guestbook> guestbooks = GuestbookLocalServiceUtil\n\t\t\t\t\t.getGuestbooks(scopeGroupId);\n\nThere's a problem: the getter only takes the `scopeGroupId` as a parameter, so\nthere's no way to get guestbooks by their status.\n\nLikewise, unapproved entries must not be displayed, but the view layer currently\ngets all entries:\n\n        <liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntriesCount()%>\">\n        <liferay-ui:search-container-results\n            results=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(scopeGroupId.longValue(),\n                            guestbookId, searchContainer.getStart(),\n                            searchContainer.getEnd())%>\" />\n\nThe solution is to implement for guestbooks and entries a getter that takes the\n`status` field as a parameter. Thankfully, Service Builder makes it easy.\n\nOpen the `guestbook-service` module's `service.xml` file. \n\n1. For the `GuestbookEntry` entity, remove the following finder:\n    \n        <finder name=\"G_S\" return-type=\"Collection\">\n          <finder-column name=\"groupId\" />\n          <finder-column name=\"status\" />\n        </finder>\n\n2. Add this finder in its place:\n\n   ```xml\n   <finder name=\"G_G_S\" return-type=\"Collection\">\n      <finder-column name=\"groupId\" />\n      <finder-column name=\"guestbookId\" />\n      <finder-column name=\"status\" />\n   </finder>\n   ```\n\nRun service builder (double-click `guestbook-service/build/buildService` in the\nGradle Tasks pane). Service Builder generates finder methods in the\npersistence layer that take the specified fields (for example, `status`) as\nparameters.\n\n## Calling the Persistence Layer\n\nDon't call the persistence layer directly in the application code. Instead\nexpose the new persistence methods in the service layer. \n\n1.  Open `GuestbookLocalServiceImpl`, add this getter, and save the file:\n\n    ```java\n\tpublic List<Guestbook> getGuestbooks(long groupId, int status)\n\t\tthrows SystemException {\n\t\t\n\t\treturn guestbookPersistence.findByG_S(\n\t\t\tgroupId, WorkflowConstants.STATUS_APPROVED);\n\t}\n    ```\n\n    This getter gets only approved guestbooks. That's why you hard code the\n    workflow constant `STATUS_APPROVED` into the status parameter when calling\n    the persistence method. \n\n2.  Now open `GuestbookEntryLocalServiceImpl`, add these two getters, and save\n    the file:\n\n    ```java\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId, int status, int start, int end)\n\t\tthrows SystemException {\n\n\t\treturn guestbookEntryPersistence.findByG_G_S(\n\t\t\tgroupId, guestbookId, WorkflowConstants.STATUS_APPROVED);\n\t}\n\n\tpublic int getGuestbookEntriesCount(\n\t\tlong groupId, long guestbookId, int status)\n\t\tthrows SystemException {\n\n\t\treturn guestbookEntryPersistence.countByG_G_S(\n\t\t\tgroupId, guestbookId, WorkflowConstants.STATUS_APPROVED);\n\t}\n    ```\n\n    You'll replace the existing methods with these `getGuestbookEntries` and\n    `getGuestbookEntriesCount` methods in the view layer, ensuring that only\n    approved entries are displayed. \n\n3.  Save the file, run Service Builder, and refresh the Gradle project. \n\nThe work here relates to the UI updates you'll make later. Next, you must\nimplement workflow handlers so that you can call the `updateStatus` service\nmethod when the entity returns from the workflow framework.\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/10-workflow/03-workflow-handlers/01-implementing-workflow-handlers-intro.markdown",
    "content": "---\nheader-id: handling-workflow\n---\n\n# Handling Workflow\n\n[TOC levels=1-4]\n\nThe guestbook project's service layer is now updated to handle workflow. It now\nproperly sets the status fields for guestbooks and guestbook entries, gets\nentities by their statuses, and sends entities to @product@'s workflow framework\nwhenever the `addGuestbook` or `addGuestbookEntry` methods are called. Recall\nthat you still have an uncalled service method, `updateStatus`, for both\nentities. Now you'll implement workflow handlers, classes that interact with\n@product@'s workflow framework and your service layer (by calling `updateStatus`\non the appropriate entity).\n\nThere's a handy abstract class you can extend to make the job easier, called\n`BaseWorkflowHandler`. You'll do this next for both entities of the guestbook\nproject, starting with guestbooks.\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/creating-a-workflow-handler-for-guestbooks\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/10-workflow/03-workflow-handlers/02-guestbook-workflow-handlers.markdown",
    "content": "---\nheader-id: creating-a-workflow-handler-for-guestbooks\n---\n\n# Creating a Workflow Handler for Guestbooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step\">\n    <p>Handling Workflow<br>Step 1 of 2</p>\n</div>\n\nEach workflow enabled entity needs a `WorkflowHandler`. \n\n1.  Create a new package in the `guestboook-service` module called\n    `com.liferay.docs.guestbook.workflow`, then create the\n    `GuestbookWorkflowHandler` class in it. Extend `BaseWorkflowHandler` and pass\n    in `Guestbook` as the type parameter:\n\n    ```java\n    public class GuestbookWorkflowHandler extends BaseWorkflowHandler<Guestbook> {\n    ```\n\n2.  Make it a Component class:\n\n    ```java\n    @Component(immediate = true, service = WorkflowHandler.class)\n    ```\n\n3.  There are three abstract methods to implement: `getClassName`, `getType`,\n    and `updateStatus`. First add `getClassName`: \n\n    ```java\n    @Override\n    public String getClassName() {\n        return Guestbook.class.getName();\n    }\n    ```\n\n    `getClassName` returns the guestbook entity's fully qualified class name\n    (`com.liferay.docs.guestbook.model.Guestbook`).\n\n4.  Next, add `getType`: \n\n    ```java\n    @Override\n    public String getType(Locale locale) {\n        return _resourceActions.getModelResource(locale, getClassName());\n    }\n    ```\n\n    `getType` returns the model resource name\n    (`model.resource.com.liferay.docs.guestbook.model.Guestbook`). \n\n5.  Finally, add the meat of the workflow handler, which is in the\n    `updateStatus` method: \n\n    ```java\n    @Override\n    public Guestbook updateStatus(\n            int status, Map<String, Serializable> workflowContext)\n        throws PortalException {\n\n        long userId = GetterUtil.getLong(\n            (String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));\n        long resourcePrimKey = GetterUtil.getLong(\n            (String)workflowContext.get(\n                WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n        ServiceContext serviceContext = (ServiceContext)workflowContext.get(\n            \"serviceContext\");\n\n        return _guestbookLocalService.updateStatus(\n            userId, resourcePrimKey, status, serviceContext);\n    }\n    ```\n\n    When you crafted the service layer's `updateStatus` method (see the last section\n    for more details), you specified parameters that must be passed to the method.\n    Here you're making sure that those parameters are available to pass to the\n    service call. Get the `userId` and `resourcePrimKey` from `GetterUtil`. Its\n    `getLong` method takes a `String`, which you can get from the `workflowContext`\n    `Map` using `WorkflowConstants` for the context user ID and the context\n    entry class PK.\n\n6.  Make sure you inject the `ResourceActions` service into a private variable\n    at the end of the class, using the `@Reference` annotation:\n\n    ```java\n    @Reference(unbind = \"-\")\n    protected void setResourceActions(ResourceActions resourceActions) {\n\n        _resourceActions = resourceActions;\n    }\n\n    private ResourceActions _resourceActions;\n    ```\n\n7.  Inject a `GuestbookLocalService` into a private variable using the\n    `@Reference` annotation.\n\n    ```java\n        @Reference(unbind = \"-\")\n        protected void setGuestbookLocalService(\n            GuestbookLocalService guestbookLocalService) {\n\n            _guestbookLocalService = guestbookLocalService;\n        }\n\n        private GuestbookLocalService _guestbookLocalService;\n\n    }\n    ```\n\n8.  Organize imports (*[CTRL]+[SHIFT]+O*) and save your work. \n\nNow the Guestbook application updates the database with the necessary status\ninformation, interacting with Liferay's workflow classes to make sure each\nentity is properly handled by @product@. At this point you can enable workflow\nfor the Guestbook inside @product@ and see how it works. Navigate to *Control\nPanel &rarr; Workflow* &rarr; *Process Builder* &rarr; *Configuration*. The\nGuestbook entity appears among @product@'s native entities. Enable the Single\nApprover Workflow for Guestbooks; then go to the Guestbook Admin portlet and add\na new Guestbook. A notification appears next to your user name in the product\nmenu. You receive a notification from the workflow that a task is ready for\nreview. Click it, and you're taken to the My Workflow Tasks portlet, where you\ncan complete the review task.\n\n![Figure 1: Click the workflow notification in the Notifications portlet to review the guestbook submitted to the workflow.](../../../../images/workflow-notification.png)\n\nTo complete the review, click the actions button\n(![Actions](../../../../images/icon-actions.png)) from My Workflow Tasks and\nselect *Assign to Me*. Click the actions button again and select *Approve*.\n\n![Figure 2: Click the workflow notification in the Notifications portlet to review the guestbook submitted to the workflow.](../../../../images/workflow-assign-to-me.png)\n\nRight now the workflow process for guestbooks is functional, but the UI isn't\nadapted for it. You'll write the workflow handler for guestbook entries next,\nand then update the UI to account for each entity's workflow status.\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/10-workflow/03-workflow-handlers/03-entry-workflow-handlers.markdown",
    "content": "---\nheader-id: creating-a-workflow-handler-for-guestbook-entries\n---\n\n# Creating a Workflow Handler for Guestbook Entries\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step\">\n    <p>Handling Workflow<br>Step 2 of 2</p>\n</div>\n\nThe Guestbook entry's workflow handler is almost identical to the guestbook's.\n\n1.  Create a new class in the `com.liferay.docs.guestbook.workflow` package of\n    the `guestbook-service` module. Name it `GuestbookEntryWorkflowHandler` and\n    extend `BaseWorkflowHandler`. Paste this in as the class body:\n\n    ```java\n    @Component(immediate = true, service = WorkflowHandler.class)\n    public class GuestbookEntryWorkflowHandler extends BaseWorkflowHandler<GuestbookEntry> {\n\n        @Override\n        public String getClassName() {\n\n            return GuestbookEntry.class.getName();\n\n        }\n\n        @Override\n        public String getType(Locale locale) {\n\n            return _resourceActions.getModelResource(locale, getClassName());\n\n        }\n\n        @Override\n        public GuestbookEntry updateStatus(\n            int status, Map<String, Serializable> workflowContext)\n            throws PortalException {\n\n            long userId = GetterUtil.getLong(\n                (String) workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));\n            long resourcePrimKey = GetterUtil.getLong(\n                (String) workflowContext.get(\n                    WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n            ServiceContext serviceContext =\n                (ServiceContext) workflowContext.get(\"serviceContext\");\n\n            long guestbookId =\n                _guestbookEntryLocalService.getGuestbookEntry(resourcePrimKey).getGuestbookId();\n            \n            return _guestbookEntryLocalService.updateStatus(\n                userId, guestbookId, resourcePrimKey, status, serviceContext);\n        }\n\n        @Reference(unbind = \"-\")\n        protected void setGuestbookEntryLocalService(GuestbookEntryLocalService guestbookEntryLocalService) {\n\n            _guestbookEntryLocalService = guestbookEntryLocalService;\n        }\n\n        @Reference(unbind = \"-\")\n        protected void setResourceActions(ResourceActions resourceActions) {\n\n            _resourceActions = resourceActions;\n        }\n\n        private GuestbookEntryLocalService _guestbookEntryLocalService;\n        private ResourceActions _resourceActions;\n    }\n    ```\n\n    There is nothing unique about this code as compared with the guestbook's\n    workflow handler, except that we need the `gustbookId` for the entry. That's\n    easily obtained by getting the `GuestbookEntry` object with\n    `guestbookEntryLocalService`, then getting its `guestbookId`. See the last\n    article for the rest of the handler's implementation details.\n\n2.  Organize imports with *CTRL+SHIFT+O* and save the file.\n\nThe back-end of the guestbook project is fully workflow enabled. All that's left\nis to update the Guestbook Application's UI to handle workflow status. \n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/10-workflow/04-displaying-approved-items/01-displaying-approved-workflow-items-intro.markdown",
    "content": "---\nheader-id: displaying-approved-workflow-items\n---\n\n# Displaying Approved Workflow Items\n\n[TOC levels=1-4]\n\nThere's not much left to do. Both entities in the guestbook project's back-end\nare workflow enabled, so it's time to update the UI. The Guestbook Admin portlet\nand the Guestbook portlet each requires its own display strategy.\n\nThe Guestbook Admin application is accessed by administrators, so it can display\nall guestbooks that have been submitted, even if they're not marked as approved.\nHowever, adding a *Status* field to the search container makes sense. That way\nadmins can see which guestbooks are already approved, which are drafts, which\nare pending, etc.\n\nThe Guestbook application is meant to be viewed by site members and even guests\n(unauthenticated users of your site). Here it's smart to display only approved\nguestbooks and approved entries.\n\nStart by updating the Guestbook Admin UI.\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/displaying-guestbook-status\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/10-workflow/04-displaying-approved-items/02-displaying-status-guestbook-admin.markdown",
    "content": "---\nheader-id: displaying-guestbook-status\n---\n\n# Displaying Guestbook Status\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step\">\n    <p>Displaying Approved Workflow Items<br>Step 1 of 2</p>\n</div>\n\nThe Guestbook Admin application's main view currently has a search container\nwith two columns: the guestbook name and the guestbook actions button. \n\n![Figure 1: The Guestbook Admin's main view currently shows the name of the guestbook and its actions button.](../../../../images/lp-workflow-admin-nostatus.png)\n\nNow you'll add a third column between the two existing ones: *Status*.\n\n1.  Open\n    `guestbook-web/src/main/reosurces/META-INF/resources/guestbook_admin/view.jsp`.\n\n2.  Find the existing `search-container-column` definitions:\n\n        <liferay-ui:search-container-column-text property=\"name\" />\n\n        <liferay-ui:search-container-column-jsp align=\"right\"\n            path=\"/guestbook_admin/guestbook_actions.jsp\" />\n\n3.  Put the following new column between the existing columns: \n\n    ```markup\n    <liferay-ui:search-container-column-status property=\"status\" />\n    ```\n\nSave the file and wait for the `web` module to redeploy. With the addition of\none line in the JSP, the Guestbook Admin application now displays the\nguestbook's workflow status.\n\n![Figure 2: The Guestbook Admin's main view, displaying the status of each guestbook.](../../../../images/lp-workflow-admin-status.png)\n\nNow you can move on to the Guestbook application's view layer.\n"
  },
  {
    "path": "en/developer/tutorials/articles/02-developing-a-web-application/10-workflow/04-displaying-approved-items/03-displaying-approved-entries.markdown",
    "content": "---\nheader-id: displaying-approved-entries\n---\n\n# Displaying Approved Entries\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step\">\n    <p>Displaying Approved Workflow Items<br>Step 2 of 2</p>\n</div>\n\nThe Guestbook application needs to be updated so that only guestbooks and\nentries with a status of *approved* appear in the UI.\n\nChange the getters used to retrieve both entities in the view layer.\n\n1.  You need a new import, so first open\n    `guestbook-web/src/main/resources/META-INF/resources/init.jsp` and add this\n    line: \n\n    ```markup\n    <%@ page import=\"com.liferay.portal.kernel.workflow.WorkflowConstants\"%>\n    ```\n\n2.  Now open\n    `guestbook-web/src/main/resources/META-INF/resources/guestbook/view.jsp`.\n    Find the scriptlet that retrieves guestbooks:\n\n        <%\n            List<Guestbook> guestbooks = GuestbookLocalServiceUtil\n                        .getGuestbooks(scopeGroupId);\n                for (int i = 0; i < guestbooks.size(); i++) {\n                    Guestbook curGuestbook = (Guestbook) guestbooks.get(i);\n                    String cssClass = StringPool.BLANK;\n                    if (curGuestbook.getGuestbookId() == guestbookId) {\n                        cssClass = \"active\";\n                    }\n                    if (GuestbookPermission.contains(\n                        permissionChecker, curGuestbook.getGuestbookId(), \"VIEW\")) {\n                                            \n        %>\n\n    Change it so it calls the getter you added that takes workflow status into\n    account. All you need to do is change this method call \n\n        List<Guestbook> guestbooks = GuestbookLocalServiceUtil\n                    .getGuestbooks(scopeGroupId);\n\n    to\n\n    ```java\n    List<Guestbook> guestbooks = GuestbookLocalServiceUtil\n                .getGuestbooks(scopeGroupId, WorkflowConstants.STATUS_APPROVED);\n    ```\n\n    Save the file, and now only approved guestbooks are displayed in the\n    Guestbook application. \n \n3.  Next, update the entry's UI in the same `view.jsp`.\n    Find the tags that set the search container's total and its results:\n\n        <liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.\n                        getGuestbookEntriesCount()%>\">\n        <liferay-ui:search-container-results results=\n                        \"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries\n                        (scopeGroupId.longValue(),\n                        guestbookId, searchContainer.getStart(),\n                        searchContainer.getEnd())%>\" />\n\n    Replace the getters to use the ones that take workflow status as a parameter,\n    and pass `WorkflowConstants.STATUS_APPROVED` as the status. Here's what it looks\n    like when you're finished:\n\n    ```markup\n    <liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.\n                    getGuestbookEntriesCount(scopeGroupId.longValue(), \n                    guestbookId, WorkflowConstants.STATUS_APPROVED)%>\">\n    <liferay-ui:search-container-results results=\n                    \"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(\n                    scopeGroupId.longValue(), guestbookId, \n                    WorkflowConstants.STATUS_APPROVED, \n                    searchContainer.getStart(), searchContainer.getEnd())%>\" />\n    ```\n\nNow only approved entries are displayed, and the search container's counter only\ncounts the approved entries. If you update the `getGuestbookEntries` call but\nnot the `getGuestbookEntriesCount` call, the count that's displayed includes\napproved entries and entries with any other workflow status, and it won't match\nthe total that's displayed at the bottom of the search container.\n\n![Figure 1: If you don't update the counter method to account for workflow status, it displays an incorrect count in the search container.](../../../../images/lp-workflow-entries-count.png)\n\nNow Guestbooks and Guestbook Entries are now fully workflow enabled, to the\ngreat relief of the Lunar Resort's site administrators. You've saved them a lot\nof headaches dealing with inappropriate content, primarily submitted by visitors\nfrom Mars. Those Martians really need some lessons in netiquette.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/01-intro.markdown",
    "content": "---\nheader-id: upgrading-code-to-product-ver\n---\n\n# Upgrading Code to @product-ver@\n\n[TOC levels=1-4]\n\nUpgrading to @product-ver@ involves migrating your installation and code (your\ncustom apps) to the new version. You'll learn how to upgrade your code in this\nsection.\n\nThese tutorials assume you're using the\n[Liferay Upgrade Planner](/docs/7-2/reference/-/knowledge_base/r/liferay-upgrade-planner).\nTo follow along with this section, install the planner and step through the\nupgrade instructions. You can also use the planner to\n[upgrade your data](/docs/7-2/deploy/-/knowledge_base/d/upgrading-to-product-ver);\nthis is a separate process that must be done independently from the code upgrade\nprocess.\n\nFor convenience, this tutorial section also references documentation and\noutlined steps to aid those opting to upgrade their code manually.\n\nHere are the code upgrade steps:\n\n1.  {.root}[Upgrade Your Development Environment](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment){.title}\n\n    Legacy project environments should be upgraded to the latest version of\n    Liferay Workspace to ensure you leverage all available features.{.summary}\n\n    1.  [Set Up Liferay Workspace](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment#setting-up-liferay-workspace){.title}\n\n        A Liferay Workspace is a generated environment that is built to hold\n        and manage your Liferay projects. Create/import a workspace to get\n        started.{.summary}\n\n        1.  [Create New Liferay Workspace](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment#creating-new-liferay-workspace){.title commandId=create_new_liferay_workspace}\n\n            If you don't have an existing 7.x Liferay Workspace, you must create\n            one. Skip to the next step if you have an existing workspace.{.summary}\n\n        2.  [Import Existing Liferay Workspace](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment#importing-existing-liferay-workspace){.title commandId=import_existing_liferay_workspace}\n\n            Import an existing Liferay Workspace. If you don't have one, revisit\n            the previous step.{.summary}\n\n    2.  [Configure Liferay Workspace Settings](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment#configuring-liferay-workspace-settings){.title}\n\n        Set the @product@ version in workspace's configuration you intend to\n        upgrade to.{.summary}\n\n        1.  [Configure Workspace Product Key](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment#configure-workspace-product-key){.title commandId=configure_workspace_product_key}\n\n            Configure your workspace by setting a product key.{.summay}\n\n        2.  [Initialize Server Bundle](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment#initializing-server-bundle){commandId=initialize_server_bundle}\n\n            Download the @product@ bundle you're upgrading to.\n\n        3.  [Migrate .cfg Files to .config Files](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-your-development-environment#migrate-cfg-files-to-config-files){.title commandId=upgrade-cfg-to-config}\n\n            Convert `.cfg` files to `.config` files.{.summay}\n\n2.  [Migrate Plugins SDK Projects](/docs/7-2/tutorials/-/knowledge_base/t/migrating-plugins-sdk-projects-to-liferay-workspace){.title}\n\n    Copy your Plugins SDK projects into workspace and convert them to\n    Gradle/Maven projects.{.summary}\n\n    1.  [Import Existing Plugins SDK Projects](/docs/7-2/tutorials/-/knowledge_base/t/migrating-plugins-sdk-projects-to-liferay-workspace#importing-existing-plugins-sdk-projects){.title commandId=import_existing_plugins_sdk}\n\n        Import your existing Plugins SDK projects.{.summary}\n\n    2.  [Migrate Existing Plugins to Workspace](/docs/7-2/tutorials/-/knowledge_base/t/migrating-plugins-sdk-projects-to-liferay-workspace#migrating-existing-plugins-to-workspace){.title commandId=migrate_existing_plugins_to_workspace}\n\n        Migrate your existing plugins to workspace. This involves moving the\n        plugin to workspace and converting it to the workspace's build\n        environment.{.summary}\n\n3.  [Upgrade Build Dependencies](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-build-dependencies){.title}\n\n    Optimize your workspace's build environment for the most efficient code\n    upgrade experience.{.summary}\n\n    1.  [Update Repository URL](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-build-dependencies#updating-the-repository-url){.title commandId=update_repository_url}\n\n        Update your repository URL to Liferay's frequently updated CDN\n        repository.{.summary}\n\n    2. [Update Workspace Plugin Version](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-build-dependencies#updating-the-workspace-plugin-version){.title commandId=update_workspace_plguin_version}\n\n        Update your Workspace plugin version to leverage the latest features of\n        Liferay Workspace.{.summary}\n\n    3.  [Remove Dependency Versions](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-build-dependencies#removing-your-projects-build-dependency-versions){.title commandId=remove_dependency_version}\n\n        Remove the project's dependency versions since it's leveraging target\n        platform.{.summary}\n\n4.  [Fix Upgrade Problems](/docs/7-2/tutorials/-/knowledge_base/t/fixing-upgrade-problems){.title}\n\n    Fix common upgrade problems dealing with your project's dependencies and\n    breaking changes.{.summary}\n\n    1.  [Auto-Correct Upgrade Problems](/docs/7-2/tutorials/-/knowledge_base/t/fixing-upgrade-problems#auto-correcting-upgrade-problems){.title commandId=auto_correct_find_upgrade_problems}\n\n        Auto-correct straightforward upgrade problems.{.summary}\n\n    2.  [Find Upgrade Problems](/docs/7-2/tutorials/-/knowledge_base/t/fixing-upgrade-problems#finding-upgrade-problems){.title commandId=find_upgrade_problems}\n\n        Find upgrade problems. These are problems that cannot be auto-corrected;\n        you can update them manually according to the breaking changes\n        documentation.{.summary}\n\n    3.  [Resolve Upgrade Problems](/docs/7-2/tutorials/-/knowledge_base/t/fixing-upgrade-problems#resolving-upgrade-problems){.title}\n\n        Mark upgrade problems as resolved after addressing them.{.summary}\n\n    4.  [Remove Problem Markers](/docs/7-2/tutorials/-/knowledge_base/t/fixing-upgrade-problems#removing-problem-markers){.title commandId=remove_upgrade_problems_markers}\n\n        After fixing your upgrade problems, remove the problem markers.{.summary}\n\n    5.  [Resolving a Project's Dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n\n    6.  [Resolving Breaking Changes](/docs/7-2/tutorials/-/knowledge_base/t/resolving-breaking-changes)\n\n5.  [Upgrade Service Builder Services](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-service-builder-services){.title}\n\n    Upgrade your Liferay Service Builder services.{.summary}\n\n    1.  [Remove Legacy Files](/docs/7-2/tutorials/-/knowledge_base/t/removing-legacy-files){.title commandId=remove_legacy_files}\n\n        Remove legacy files that are no longer leveraged by Service Builder.{.summary}\n\n    2.  [Migrate from Spring DI to OSGi Declarative Services](/docs/7-2/tutorials/-/knowledge_base/t/converting-a-service-builder-module-from-spring-di-to-osgi-ds){.title}\n\n        Leverage OSGi Declarative Services in your Service Builder project.{.summary}\n\n    3.  [Rebuild Services](/docs/7-2/tutorials/-/knowledge_base/t/rebuilding-services){.title commandId=rebuild_services}\n\n        Rebuild your project's services to persist your updates.{.summary}\n\n6.  [Upgrade Customization Plugins](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-customization-plugins){.title}\n\n    Upgrade your customization plugins so they're deployable to @product-ver@.{.summary}\n\n    1.  [Upgrade Customization Modules](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-customization-modules){.title}\n\n    2.  [Upgrade Core JSP Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-core-jsp-hooks){.title}\n\n    3.  [Upgrade Portlet JSP Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portlet-jsp-hooks){.title}\n\n    4.  [Upgrade Service Wrapper Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-service-wrapper-hooks){.title}\n\n    5.  [Upgrade Core Language Key Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-core-language-key-hooks){.title}\n\n    6.  [Upgrade Portlet Language Key Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portlet-language-key-hooks){.title}\n\n    7.  [Upgrade Model Listener Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-model-listener-hooks){.title}\n\n    8.  [Upgrade Event Action Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-event-action-hooks){.title}\n\n    9.  [Upgrade Servlet Filter Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-servlet-filter-hooks){.title}\n\n    10. [Upgrade Portal Properties Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portal-property-hooks){.title}\n\n    11. [Upgrade Struts Action Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-struts-action-hooks){.title}\n\n7.  [Upgrade Themes](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-theme-to-7-2){.title}\n\n    Upgrade your themes so they're deployable to @product-ver@.{.summary}\n\n    1.  [Upgrade 6.2 Themes to 7.2](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-6-2-themes-to-7-2){.title}\n\n    2.  [Upgrade 7.0 Themes to 7.2](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-7-0-themes-to-7-2){.title}\n\n    3.  [Upgrade 7.1 Themes to 7.2](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-7-1-themes-to-7-2){.title}\n\n8.  [Upgrade Layout Templates](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-layout-template-to-7-2){.title}\n\n    Upgrade your layout templates so they're deployable to @product-ver@.{.summary}\n\n9.  [Upgrade Frameworks & Features](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-frameworks-and-features){.title}\n\n    1.  [Upgrade JNDI Data Source Usage](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-jndi-data-source-usage){.title}\n\n        Use @product@'s class loader to access the app server's JNDI API.{.summary}\n\n    2.  [Upgrade Service Builder Service Invocation](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-service-builder-service-invocation){.title}\n\n        For Service Builder logic remaining in a WAR, you must implement a\n        service tracker to call services. For logic divided into OSGi modules,\n        you can leverage Declarative Services.{.summary}\n\n    3.  [Upgrade Service Builder](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-service-builder){.title}\n\n        Adapt your app to account for Service Builder-specific changes.{.summary}\n\n    4.  [Migrate Off of Velocity Templates](/docs/7-2/tutorials/-/knowledge_base/t/migrating-off-of-velocity-templates){.title}\n\n        Velocity template usage is deprecated for @product-ver@. You should\n        convert your template to FreeMarker.{.summary}\n\n10. [Upgrade Portlets](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portlets){.title}\n\n    Upgrade your portlets so they're deployable to @product-ver@.{.summary}\n\n    1.  [Upgrade Generic Portlets](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-genericportlet){.title}\n\n    2.  [Upgrade Liferay MVC Portlets](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-liferay-mvc-portlet){.title}\n\n    3.  [Upgrade JSF Portlets](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-liferay-jsf-portlet){.title}\n\n    4.  [Upgrade Servlet-based Portlets](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-servlet-based-portlet){.title}\n\n    5.  [Upgrading Spring Portlet MVC Portlets](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-spring-portlet-mvc-portlet){.title}\n\n    6.  [Upgrade Struts 1 Portlets](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-struts-1-portlet){.title}\n\n11. [Upgrade Web Plugins](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-web-plugins){.title}\n\n    Upgrade web plugins previously stored in the `webs` folder of your legacy\n    Plugins SDK.{.summary}\n\n12. [Upgrade Ext Plugins](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-ext-plugins){.title}\n\n    Attempt to leverage an extension point instead of upgrading your Ext plugin.\n    If an Ext plugin is necessary, you must review all changes between the\n    previous Liferay Portal instance you were using and @product-ver@, and then\n    manually modify your Ext plugin to merge your changes with @product@'s.{.summary}\n\nOnce you've finished the code upgrade steps, your custom apps will be compatible\nwith @product-ver@!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/02-upgrading-your-development-environment.markdown",
    "content": "---\nheader-id: upgrading-your-development-environment\n---\n\n# Upgrading Your Development Environment\n\n[TOC levels=1-4]\n\nA [Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace)\nis a generated environment that is built to hold and manage your Liferay\nprojects. It is intended to aid in the management of Liferay projects by\nproviding various build scripts and configured properties.\n\nLiferay Workspace is the recommended environment for your code migration;\ntherefore, it will be the assumed development environment in this section.\n\nContinue on to set up a workspace.\n\n## Setting Up Liferay Workspace\n\nYou must set up your workspace development environment before you begin\nupgrading your custom apps. If you don't have an existing workspace, follow the\nstep for creating one. If you have an existing workspace, follow the step on\nimporting it into the Upgrade Planner.\n\n### Creating New Liferay Workspace\n\nInitiating this step in the Upgrade Planner loads the Liferay Workspace\nProject wizard.\n\n1.  Give your new workspace a name.\n\n2.  Choose the build type (Gradle or Maven) you prefer for your workspace\n    environment and future Liferay projects.\n\n3.  Click Finish.\n\nYou now have a new Liferay Workspace available in the Upgrade Planner!\n\nFor more information on creating a Liferay Workspace outside the planner, see\nthe\n[Creating a Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/creating-a-liferay-workspace)\nsection.\n\n### Importing Existing Liferay Workspace\n\nIf you already have an existing 7.x Liferay Workspace, you should import it\ninto the planner. Once you initiate this step, you're given a File\nExplorer/Manager to select your existing workspace. After selecting it, the\nworkspace is imported into the Project Explorer.\n\nFor more information importing a workspace into your IDE, see\n[this article](/docs/7-2/reference/-/knowledge_base/r/importing-projects-in-dev-studio).\n\n## Configuring Liferay Workspace Settings\n\nYou must configure your workspace with the @product@ version you intend to\nupgrade to.\n\n### Configure Workspace Product Key\n\nConfigure your workspace by setting a product key. This automatically sets the Target Platform version, Docker image name, bundle URL, and other default settings for the @product@ release.\n\n### Initializing Server Bundle\n\nOnce your workspace is configured for the @product@ version you're upgrading to,\nyou can initialize the server bundle. This involves downloading the bundle and\nextracting it into its folder (e.g., `bundles`). If you have an existing\nworkspace already equipped with an older Liferay bundle, this deletes the old\nbundle and initializes the new one.\n\nIf you're upgrading your code manually and working in Dev Studio, you can do\nthis by right-clicking the workspace project and selecting *Liferay* &rarr;\n*Initialize Server Bundle*. See the\n[Installing a Server in IntelliJ](/docs/7-2/reference/-/knowledge_base/r/installing-a-server-in-intellij)\narticle if you use IntelliJ instead. Visit the\n[Managing Your Liferay Server with Blade CLI](/docs/7-2/reference/-/knowledge_base/r/managing-your-liferay-server-with-blade-cli)\narticle for information on how to do this via the command line.\n\n### Migrate .cfg Files to .config Files\n\n`.config` files are preferred over `.cfg` files because they allow specifying a property value's type, and allow multi-valued properties."
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/03-migrating-plugins-sdk-projects-to-liferay-workspace.markdown",
    "content": "---\nheader-id: migrating-plugins-sdk-projects-to-liferay-workspace\n---\n\n# Migrating Plugins SDK Projects to Liferay Workspace\n\n[TOC levels=1-4]\n\nThe Plugins SDK was deprecated for @product@ 7.0 and removed for @product@ 7.1.\nTherefore, to upgrade your custom apps to @product-ver@, you must migrate them\nto a new environment. \n[Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace) is\nthe recommended environment for your code migration and will be the assumed\nchoice in this section.\n\nThere are two steps you must follow to migrate your custom code to workspace:\n\n1. Import the Plugins SDK project into the Upgrade Planner.\n\n2. Convert the Plugins SDK project to a supported workspace build type.\n\nYou'll step through importing a Plugins SDK project first.\n\n## Importing Existing Plugins SDK Projects\n\nInitiating this step in the Upgrade Planner imports your Plugins SDK\nprojects into the Upgrade Planner. These projects originate from the\nPlugins SDK you set when the Upgrade Planner process was started.\n\nIf you're manually upgrading your code, you can skip this step.\n\nYou're now ready to migrate your Plugins SDK projects to your new workspace!\n\n## Migrating Existing Plugins to Workspace\n\nLiferay Workspace can be generated as a Gradle or Maven environment, but it does\nnot support the Plugins SDK's Ant build. Because of this, you must convert your\nprojects to one of the supported build tools:\n\n- Gradle\n- Maven\n\nWhen initiating this step for a Gradle-based workspace, your Ant-based Plugins\nSDK project is copied to the applicable workspace folder based on its project\ntype (e.g., `wars`) and is converted to a Gradle project. There is also a Blade\nCLI command that completes this via the command line. Visit the\n[Converting Plugins SDK Projects with Blade CLI](/docs/7-2/reference/-/knowledge_base/r/converting-plugins-sdk-projects-with-blade-cli)\narticle for more information.\n\nIf you're migrating your Ant project to a Maven workspace, you must manually\ncopy the project to the applicable folder based on the project type (e.g.,\n`wars`). The majority of Plugins SDK projects belong in the workspace's `wars`\nfolder. You can consult the\n[Workspace Anatomy](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace#workspace-anatomy)\nsection for a full overview of a workspace's folder structure and choose where\nyour custom app should reside. Once you've made the decision, copy your custom\napp to the applicable workspace folder.\n\nThen you must convert your project from Ant to Maven. You'll have to\ncomplete this conversion manually.\n\nOnce you're finished, you should have your project(s) residing in the applicable\nworkspace folders as Gradle/Maven projects.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/04-upgrading-build-dependencies.markdown",
    "content": "---\nheader-id: upgrading-build-dependencies\n---\n\n# Upgrading Build Dependencies\n\n[TOC levels=1-4]\n\nNow that your projects are readily available in a workspace, you must ensure\nyour project build dependencies are upgraded. Your workspace streamlines the\nbuild dependency upgrade process by only requiring three modifications:\n\n- [Update the repository URL](#updating-the-repository-url) (Gradle only)\n- [Update the workspace plugin version](#updating-the-workspace-plugin-version)\n- [Remove your project's build dependency versions](#removing-your-projects-build-dependency-versions)\n  (Gradle only)\n\nIf you're upgrading a recently created workspace, only a subset of these tasks\nmay be required.\n\nYou'll start by updating the repository URL.\n\n## Updating the Repository URL\n\nInitiating this step in the Upgrade Planner updates the repository URL used\nto download artifacts for your workspace.\n\nIf you're using a Gradle-based workspace, the repository URL is updated to point\nto the latest Liferay CDN repository. This is set in your workspace's\n`settings.gradle` file within the `buildscript` block like this:\n\n```groovy\nrepositories {\n    maven {\n        url \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n    }\n}\n```\n\nOnce the repository URL is set to the proper CDN repository, your build\ndependencies will be downloaded from Liferay's own managed repo.\n\nFor Maven-based workspaces, Maven Central is the default repository, so no\naction is required.\n\n## Updating the Workspace Plugin Version\n\nFor the best upgrade experience, you should ensure you're leveraging the latest\nLiferay Workspace version so all the latest features are available to you.\nInitiate this step to upgrade the appropriate plugin.\n\nSee the\n[Updating Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/updating-liferay-workspace)\narticle to do this for Gradle-based workspaces manually. For Maven-based\nworkspaces, make sure you set the latest\n[Bundle Support plugin](/docs/7-2/reference/-/knowledge_base/r/bundle-support-plugin)\nversion in your root `pom.xml` file.\n\n## Removing Your Project's Build Dependency Versions\n\n| **Note:** This step only applies to Gradle-based workspaces since the target\n| platform feature is only available for Gradle projects at this time.\n\nSince your workspace is leveraging the target platform feature, there is no need\nto set your plugin's dependency versions in its `build.gradle` file. This is\nbecause the target platform version you set already defines the artifact\nversions your project uses. Therefore, if dependency versions are present in\nany of your projects' `build.gradle` files, you must remove them.\n\nInitiate this step to remove your dependency versions from your project's\n`build.gradle` file\n\nAs an example of what a `build.gradle`'s `dependencies` block should look like,\nsee the below snippet:\n\n```groovy\ndependencies {\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n    compileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\"\n    compileOnly group: \"javax.portlet\", name: \"portlet-api\"\n    compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n    compileOnly group: \"jstl\", name: \"jstl\"\n    compileOnly group: \"org.osgi\", name: \"osgi.cmpn\"\n}\n```\n\nIf you have not set the target platform feature in your workspace, see the\n[Managing the Target Platform](/docs/7-2/reference/-/knowledge_base/r/managing-the-target-platform)\narticle for more information.\n\nGreat! You've successfully upgraded your build dependencies! You likely have\ncompile errors in your project; this is because your dependencies may have\nchanged. You'll learn how to update that and more next.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/05-fixing-upgrade-problems/01-intro.markdown",
    "content": "---\nheader-id: fixing-upgrade-problems\n---\n\n# Fixing Upgrade Problems\n\n[TOC levels=1-4]\n\nNow that your development environment build configuration is settled, you can\nstart upgrading your project(s). The two most common upgrade problems are\n\n- [Broken project dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n- [Breaking changes](/docs/7-2/tutorials/-/knowledge_base/t/resolving-breaking-changes)\n\nVisit these upgrade problem tutorials for tips on how to fix them.\n\nThis tutorial is heavily focused on the Liferay Upgrade Planner. If you're\nupgrading your code manually, continue to the listed tutorials above to fix your\ncode upgrade problems.\n\nYou'll begin auto-correcting upgrade problems first.\n\n## Auto-Correcting Upgrade Problems\n\nInitiate this step to auto-correct straightforward updates like\n\n- package imports\n- JSP tag names\n- Liferay descriptor versions\n- XML descriptor content\n- etc.\n\nIf you choose to preview the auto-correct upgrade problems first, you can view\nthem in the Project Explorer under the *Liferay Upgrade Problems* dropdown. If\nyou click one of the upgrade problems listed with the preview, you're offered\ndocumentation in the *Liferay Upgrade Plan Info* window on the proposed change.\n\nOnce you've performed this step, the result list is removed.\n\n## Finding Upgrade Problems\n\nInitiating this step finds the upgrade problems that were not eligible for\nauto-correction. The problems are listed under the *Liferay Upgrade Problems*\ndropdown. If you click one of the upgrade problems listed with the preview,\nyou're offered documentation in the *Liferay Upgrade Plan Info* window on the\nproposed change.\n\nThese upgrade problems are available in the\n[breaking changes](/docs/7-2/tutorials/-/knowledge_base/t/resolving-breaking-changes)\nfor the version upgrade you're performing.\n\nThe next step is resolving the reported upgrade problems.\n\n## Resolving Upgrade Problems\n\nNow that the upgrade problems have been located, you must resolve them. As you\nselect each upgrade problem, the documentation for how to adapt your code is\ndisplayed in the *Liferay Upgrade Plan Info* window.\n\nFor each upgrade problem node, you're also given the version the upgrade problem\napplies to (e.g., when upgrading to @product@ 7.2 from Liferay Portal 6.2, you\ncould have upgrade problems from the 7.0, 7.1, or 7.2 upgrade). As you step\nthrough the reported problems, mark them as resolved/skipped using the context\nmenu. You can right-click on the problem in the Project Explorer and choose from\nfour options:\n\n- Mark done\n- Mark undone\n- Ignore\n- Ignore all problems of this type\n\nLeave this step marked as *Incomplete* until you have resolved all upgrade\nproblems accordingly.\n\n## Removing Problem Markers\n\nAfter resolving all the reported upgrade problems, you must remove all\npreviously found markers because, in most cases, the line number and other\naccompanying marker information are out of date and must be removed before\ncontinuing. Initiate this step to remove all the problem markers.\n\nGreat! You've fixed all the upgrade problems that could be automatically\ndetected by the Code Upgrade Tool. Next, you'll take a deeper look at resolving\nproject dependency errors.\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/05-fixing-upgrade-problems/02-resolving-a-projects-dependencies.markdown",
    "content": "---\nheader-id: resolving-a-projects-dependencies\n---\n\n# Resolving a Project's Dependencies\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Fixing Upgrade Problems</p><p>Step 1 of 2</p>\n</div>\n\nYou may have compile errors due to missing Liferay classes or unresolved symbols\nbecause they've been moved, renamed, or removed. As a part of modularization in\n@product@, many of these classes reside in new modules. \n\nYou must resolve all of these Liferay classes for your project. Some of the\nclass changes are quick and easy to fix. Changes involving the new modules\nrequire more effort to resolve, but doing so is still straightforward. \n\nLiferay class changes and required adaptations can be grouped into three\ncategories:\n\n- [Class moved to a package in the classpath](#class-moved-to-a-package-in-the-classpath)\n\n- [Class moved to a module *not* in the classpath](#class-moved-to-a-module-not-in-the-classpath)\n\n- [Class replaced or removed](#class-replaced-or-removed)\n\nContinue on to learn how to resolve each change.\n\n## Class Moved to a Package in the Classpath\n\nThis change is common and easy to fix. Consider resolving these classes first.\n\nSince the module is already on your classpath, you need only update the class\nimport. You can do this by using the Liferay Upgrade Planner or by organizing\nimports in Dev Studio/IntelliJ. The Upgrade Planner reports each moved class for\nyou to address one by one. Organizing imports in Dev Studio/IntelliJ\nautomatically resolves multiple classes at once.\n\nIt's typically faster to resolve moved classes using the mentioned IDEs. You can\nfollow similar instructions for both IDEs:\n\n1.  Comment out or remove any imports marked as errors.\n\n2.  Execute the *Organize Imports* keyboard sequence *Ctrl-Shift-o* (Dev Studio)\n    or *Ctrl-Alt-o* (IntelliJ).\n\nThe IDEs automatically generate the new import statements. If there is more than\none available import package for a class, a wizard appears that lets you select\nthe correct import. \n\nGreat! You've updated your class imports!\n\n## Class Moved to a Module Not in the Classpath\n\nYou must resolve the new module as a dependency for your project. This requires\nidentifying the module and specifying your project's dependency on it. \n\nBefore @product@ 7.0, all the platform APIs were in `portal-service.jar`. Many \nof these APIs are now in independent modules. Modularization has resulted in \nmany benefits, as described in the article \n[The Benefits of Modularity](/docs/7-2/customization/-/knowledge_base/c/the-benefits-of-modularity). \nOne such advantage is that these API modules can evolve separately from the\nplatform kernel. They also simplify future upgrades. For example, instead of\nhaving to check all of Liferay's APIs, each module's \n[Semantic Versioning](http://semver.org) \nindicates whether the module contains any backwards-incompatible changes. You\nneed only adapt your code to such modules (if any). \n\nAs part of the modularization, `portal-service.jar` has been renamed \nappropriately to `portal-kernel.jar`, as it continues to hold the portal \nkernel's APIs. \n\n![Figure 1: Liferay refactored the portal-service JAR for @product-ver@. Application APIs now exist in their own modules, and the portal-service JAR is now *portal-kernel*.](../../../images/from-liferay-6-portal-apis-before-after.png)\n\nEach app module consists of a set of classes that are highly cohesive and have\na specific purpose, such as providing the app's API, implementation, or UI. The \napp modules are therefore much easier to understand. Next, you'll track down the \nmodules that now hold the classes referenced by your plugin. \n\nThe reference article \n[Classes Moved from `portal-service.jar`](/docs/7-2/reference/-/knowledge_base/r/classes-moved-from-portal-service-jar) \ncontains a table that maps each class moved from `portal-service.jar` to its new \nmodule in @product@ 7.1. The table includes each class's new package and\nsymbolic name (artifact ID). You'll use this information to configure your\nplugin's dependencies on these modules. \n\nFor more information on finding and resolving your project dependencies, see\n[Configuring Dependencies](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies). \n\n## Class Replaced or Removed\n\nIn cases where the class has been replaced by another class or removed from the\nproduct, some investigation is required. The easiest way to resolve this type of\nissue is to use the Upgrade Planner. It finds removed classes your project\nis referencing and explains what happened to the class, how to handle the\nchange, and why the change was made. These are listed as breaking changes (among\nother types of changes). Move on to the next section to learn about Liferay's\nbreaking changes.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/05-fixing-upgrade-problems/03-resolving-breaking-changes.markdown",
    "content": "---\nheader-id: resolving-breaking-changes\n---\n\n# Resolving Breaking Changes\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Fixing Upgrade Problems</p><p>Step 2 of 2</p>\n</div>\n\nLiferay goes to great lengths to maintain backwards compatibility. Sometimes,\nbreaking changes are necessary to improve @product@. There may be cases where\nbreaking changes affect your code upgrade process and must be resolved. A\nbreaking change can include\n\n- Functionality that is removed or replaced\n- API incompatibilities: Changes to public Java or JavaScript APIs\n- Changes to context variables available to templates\n- Changes in CSS classes available to Liferay themes and portlets\n- Configuration changes: Changes in configuration files, like\n  `portal.properties`, `system.properties`, etc.\n- Execution requirements: Java version, J2EE Version, browser versions, etc.\n- Deprecations or end of support: For example, warning that a certain\n  feature or API will be dropped in an upcoming version.\n- Recommendations: For example, recommending using a newly introduced API that\n  replaces an old API, in spite of the old API being kept in Liferay Portal for\n  backwards compatibility.\n\nLiferay provides a list of breaking changes for every major release to ensure\nyou can easily adapt your code during the upgrade process.\n\n- [@product@ 7.0 Breaking Changes](/docs/7-0/reference/-/knowledge_base/r/breaking-changes)\n- [@product@ 7.1 Breaking Changes](/docs/7-1/reference/-/knowledge_base/r/breaking-changes)\n- [@product-ver@ Breaking Changes](/docs/7-2/reference/-/knowledge_base/r/breaking-changes)\n\nThe easiest way to resolve breaking changes is by using the\n[Liferay Upgrade Planner](/docs/7-2/reference/-/knowledge_base/r/liferay-upgrade-planner).\nIt automatically finds all documented breaking changes and can automatically\nresolve some of them on its own.\n\nIf you're resolving breaking changes manually, make sure to investigate each\nbreaking change document if you're upgrading code across multiple versions. For\nexample, if you're upgrading from Liferay Portal 6.2 to @product-ver@, you must\nresolve all the breaking changes listed in the three documents listed above.\n\nNow that you've resolved your breaking changes, you'll learn how to upgrade\nservice builder services next.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/06-upgrading-service-builder-services/01-intro.markdown",
    "content": "---\nheader-id: upgrading-service-builder-services\n---\n\n# Upgrading Service Builder Services\n\n[TOC levels=1-4]\n\nTo properly upgrade app's leveraging service builder, you must complete the\nfollowing steps:\n\n- Remove Legacy Files\n- Migrate from Spring DI to OSGi Declarative Services\n- Rebuild Services\n\nYou'll start by removing legacy files.\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/removing-legacy-files\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/06-upgrading-service-builder-services/02-removing-legacy-files.markdown",
    "content": "---\nheader-id: removing-legacy-files\n---\n\n# Removing Legacy Files\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Service Builder Services</p><p>Step 1 of 3</p>\n</div>\n\nThe first step in upgrading your Service Builder services is to delete legacy\nfiles. These legacy files include\n\n- `portlet-spring.xml`\n- `shard-data-source-spring.xml`\n- `/src/main/resources/META-INF/` (folder)\n\nWhen initiating this step, these files/folders are automatically removed from\nyour Service Builder project.\n\nIf you're manually upgrading your code, delete the listed files/folders above.\n\nNext, you'll convert your Service Builder Module from Spring DI to OSGi DS.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/06-upgrading-service-builder-services/03-converting-a-service-builder-module-from-spring-di-to-osgi-ds.markdown",
    "content": "---\nheader-id: converting-a-service-builder-module-from-spring-di-to-osgi-ds\n---\n\n# Converting a Service Builder Module from Spring DI to OSGi DS\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Service Builder Services</p><p>Step 2 of 3</p>\n</div>\n\nPrior to @product-ver@, Service Builder modules could only use Spring for\ndependency injection (DI). Now\n[OSGi Declarative Services](/docs/7-2/frameworks/-/knowledge_base/f/declarative-services)\n(DS) is the default DI mechanism for new Service Builder modules. Although OSGi\nDS is the default DI mechanism, Spring is still supported. Therefore, this is an\noptional migration step.\n\nTo learn more about the decision to convert your Service Builder modules' DI\nmechanism and how to complete the conversion process, see the\n[Migrating a Service Builder Module from Spring DI to OSGi DS](/docs/7-2/appdev/-/knowledge_base/a/migrating-a-service-builder-module-from-spring-di-to-osgi-ds)\narticle.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/06-upgrading-service-builder-services/04-rebuilding-services.markdown",
    "content": "---\nheader-id: rebuilding-services\n---\n\n# Rebuilding Services\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Service Builder Services</p><p>Step 3 of 3</p>\n</div>\n\nTo properly upgrade Service Builder projects, you must rebuild all service\nclasses so your changes are persisted across your project. Initiate this step\nto rebuild your services and finalize your Service Builder upgrade.\n\n<!--Uncomment once article is available\nTo run Service Builder outside the Liferay Upgrade Planner, see the\nRunning Service Builder article. This covers running Service Builder with Blade \nCLI, Gradle, Maven, and Dev Studio.\n-->\n\nGreat! Your Service Builder services are upgraded!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/07-upgrading-customization-plugins/01-intro.markdown",
    "content": "---\nheader-id: upgrading-customization-plugins\n---\n\n# Upgrading Customization Plugins\n\n[TOC levels=1-4]\n\n@product@ has more extension points than ever, and connecting existing hook\nplugins to them takes very few steps. In most cases, after you upgrade your hook\nusing the Liferay Upgrade Planner, it's ready to run on @product@. The following\ntutorials show you how to upgrade each type of hook plugin.\n\n- [Override/Extension Modules](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-customization-modules)\n- [Core JSP Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-core-jsp-hooks)\n- [Portlet JSP Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portlet-jsp-hooks)\n- [Service Wrapper Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-service-wrapper-hooks)\n- [Core Language Key Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-core-language-key-hooks)\n- [Portlet Language Key Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portlet-language-key-hooks)\n- [Model Listener Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-model-listener-hooks)\n- [Event Actions Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-event-action-hooks)\n- [Servlet Filter Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-servlet-filter-hooks)\n- [Portal Properties Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-portal-property-hooks)\n- [Struts Action Hooks](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-struts-action-hooks)\n\nContinue on to get started!\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/upgrading-customization-modules\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/07-upgrading-customization-plugins/02-upgrading-customization-modules.markdown",
    "content": "---\nheader-id: upgrading-customization-modules\n---\n\n# Upgrading Customization Modules\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 1 of 11</p>\n</div>\n\nCustomization modules include any module extension or override used to\ncustomize another module. For examples of these types of modules, visit the\n[`extensions`](https://github.com/liferay/liferay-blade-samples/tree/master/liferay-workspace/extensions)\nand\n[`overrides`](https://github.com/liferay/liferay-blade-samples/tree/master/liferay-workspace/overrides)\nsample projects.\n\nGetting a customization module running on @product-ver@ takes two steps:\n\n1.  Adapt your code to @product-ver@'s API using the Liferay Upgrade Planner. When\n    you ran the planner's *Fix Upgrade Problems* step, many of the existing\n    issues were autocorrected or flagged. For any remaining errors, consult the\n    [Resolving a Project's Dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n    article.\n\n2.  Deploy your module.\n\n| **Note:** A fragment was a common customization module in past versions of\n| @product@. Fragments are no longer recommended; you should upgrade a fragment\n| to a dynamic include or portlet filter. For more information on recommended\n| ways of customizing JSPs in @product-ver@, see the\n| [Customizing JSPs](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps)\n| section.\n\nGreat! Your customization module is upgraded for @product-ver@!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/07-upgrading-customization-plugins/03-upgrading-core-jsp-hooks.markdown",
    "content": "---\nheader-id: upgrading-core-jsp-hooks\n---\n\n# Upgrading Core JSP Hooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 2 of 11</p>\n</div>\n\nGetting a core JSP hook running on @product-ver@ takes two steps:\n\n1.  Adapt your code to @product-ver@'s API using the Liferay Upgrade Planner. When\n    you ran the planner's *Fix Upgrade Problems* step, many of the existing\n    issues were autocorrected/flagged. For any remaining errors, consult the\n    [Resolving a Project's Dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n    article.\n\n2.  Deploy your hook plugin.\n\n@product@'s Plugin Compatibility Layer converts the plugin WAR to a Web\nApplication Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nAlthough you can upgrade your core JSP hook to @product-ver@, there are better\nways to override a core JSP. The two recommended approaches are\n\n- Dynamic includes\n- Portlet filters\n\nFor more information on recommended ways of customizing JSPs in @product-ver@,\nsee the\n[Customizing JSPs](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps)\nsection.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/07-upgrading-customization-plugins/04-upgrading-portlet-jsp-hooks.markdown",
    "content": "---\nheader-id: upgrading-portlet-jsp-hooks\n---\n\n# Upgrading Portlet JSP Hooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 3 of 11</p>\n</div>\n\nGetting a portlet JSP hook running on @product-ver@ takes two steps:\n\n1.  Adapt your code to @product-ver@'s API using the Liferay Upgrade Planner. When\n    you ran the planner's *Fix Upgrade Problems* step, many of the existing\n    issues were autocorrected/flagged. For any remaining errors, consult the\n    [Resolving a Project's Dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n    article.\n\n2.  Deploy your hook plugin.\n\n@product@'s Plugin Compatibility Layer converts the plugin WAR to a Web\nApplication Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nAlthough you can upgrade your portlet JSP hook to @product-ver@, there are\nbetter ways to override a portlet JSP. The two recommended approaches are\n\n- Dynamic includes\n- Portlet filters\n\nFor more information on recommended ways of customizing JSPs in @product-ver@,\nsee the\n[Customizing JSPs](/docs/7-2/customization/-/knowledge_base/c/customizing-jsps)\nsection.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/07-upgrading-customization-plugins/05-upgrading-service-wrapper-hooks.markdown",
    "content": "---\nheader-id: upgrading-service-wrapper-hooks\n---\n\n# Upgrading Service Wrapper Hooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 4 of 11</p> \n</div>\n\nUpgrading traditional\n[service wrapper hook plugins](/docs/6-2/tutorials/-/knowledge_base/t/overriding-a-portal-service-using-a-hook)\nto @product-ver@ is quick and easy.\n\n1.  Adapt your code to @product-ver@'s API using the Liferay Upgrade Planner. When\n    you ran the planner's *Fix Upgrade Problems* step, many of the existing\n    issues were autocorrected/flagged. For any remaining errors, consult the\n    [Resolving a Project's Dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n    article.\n\n2.  Deploy the plugin.\n\n@product@'s Plugin Compatibility Layer converts the plugin WAR to a Web\nApplication Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nYour service wrapper hook is now available in @product@.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/07-upgrading-customization-plugins/06-upgrading-core-language-key-hooks.markdown",
    "content": "---\nheader-id: upgrading-core-language-key-hooks\n---\n\n# Upgrading Core Language Key Hooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 5 of 11</p>\n</div>\n\nHere are the steps for upgrading a core language key hook to @product-ver@. \n\n1.  Create a new module based on the Blade sample `resource-bundle` project\n    ([Gradle](https://github.com/liferay/liferay-blade-samples/tree/master/gradle/extensions/resource-bundle)\n    or [Maven](https://github.com/liferay/liferay-blade-samples/tree/master/maven/extensions/resource-bundle)). \n\n    Here are the main parts of the module folder structure:\n\n    - `src/main/java/[resource bundle path]` &rarr; Custom resource bundle class\n      goes here \n    -  `src/main/resources/content`\n        - `Language.properties`\n        - `Language_xx.properties`\n        - ...\n\n2.  Copy all your plugin's language properties files into the module\n    folder `src/main/resources/content/`.\n\n3.  [Create a resource bundle loader](/docs/7-2/customization/-/knowledge_base/c/overriding-global-language-keys#create-a-resource-bundle-service-component). \n\n4.  [Deploy your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project). \n\nYour core language key customizations are deployed to @product-ver@.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/07-upgrading-customization-plugins/07-upgrading-portlet-language-key-hooks.markdown",
    "content": "---\nheader-id: upgrading-portlet-language-key-hooks\n---\n\n# Upgrading Portlet Language Key Hooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 6 of 11</p>\n</div>\n\nYou can upgrade your portlet language key hooks to @product-ver@ by following\nthese steps:\n\n1.  Create a new module based on the Blade sample `resource-bundle` project\n    ([Gradle](https://github.com/liferay/liferay-blade-samples/tree/master/gradle/extensions/resource-bundle)\n    or [Maven](https://github.com/liferay/liferay-blade-samples/tree/master/maven/extensions/resource-bundle)). \n\n    Here are the module folder structure's main files:\n\n    - `src/main/java/[resource bundle path]` &rarr;\n      [`ResourceBundleLoader` extension](@platform-ref@/7.2-latest/javadocs/portal-kernel/)\n      goes here\n    - `src/main/resources/content`\n        - `Language.properties`\n        - `Language_xx.properties`\n        - ...\n\n2.  Copy your language properties files into module folder\n    `src/main/resources/content/`.\n\n3.  In your `bnd.bnd` file,\n    [specify OSGi manifest headers](/docs/7-2/customization/-/knowledge_base/c/overriding-a-modules-language-keys)\n    that target the portlet module's resource bundle, but prioritize yours. \n\n4.  [Deploy your module](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project). \n\nYour portlet language key customizations are deployed in your new module on\n@product-ver@. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/07-upgrading-customization-plugins/08-upgrading-model-listener-hooks.markdown",
    "content": "---\nheader-id: upgrading-model-listener-hooks\n---\n\n# Upgrading Model Listener Hooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 7 of 11</p>\n</div>\n\nDevelopers have been creating model listeners for several @product@\nversions. Upgrading model listener Hooks from previous portal versions has never\nbeen easier.\n\n1.  Adapt your code to @product-ver@'s API using the Liferay Upgrade Planner. When\n    you ran the planner's *Fix Upgrade Problems* step, many of the existing\n    issues were autocorrected/flagged. For any remaining errors, consult the\n    [Resolving a Project's Dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n    article.\n\n2.  Deploy your hook plugin.\n\n@product@'s Plugin Compatibility Layer converts the plugin WAR to a Web\nApplication Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nYour model listener hook is now available in @product@.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/07-upgrading-customization-plugins/09-upgrading-event-actions-hooks.markdown",
    "content": "---\nheader-id: upgrading-event-action-hooks\n---\n\n# Upgrading Event Action Hooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 8 of 11</p>\n</div>\n\nEvent action hooks can be upgraded by completing these steps:\n\n1.  Adapt your code to @product-ver@'s API using the Liferay Upgrade Planner. When\n    you ran the planner's *Fix Upgrade Problems* step, many of the existing\n    issues were autocorrected/flagged. For any remaining errors, consult the\n    [Resolving a Project's Dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n    article.\n\n2.  Deploy your hook plugin.\n\n@product@'s Plugin Compatibility Layer converts the plugin WAR to a Web\nApplication Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nYour event action hook is now available in @product@.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/07-upgrading-customization-plugins/10-upgrading-servlet-filter-hooks.markdown",
    "content": "---\nheader-id: upgrading-servlet-filter-hooks\n---\n\n# Upgrading Servlet Filter Hooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 9 of 11</p>\n</div>\n\nIf you have servlet filter hooks ready to be upgraded, this tutorial's for you.\nThe process is simple:\n\n1.  Adapt your code to @product-ver@'s API using the Liferay Upgrade Planner. When\n    you ran the planner's *Fix Upgrade Problems* step, many of the existing\n    issues were autocorrected/flagged. For any remaining errors, consult the\n    [Resolving a Project's Dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n    article.\n\n2.  Deploy your hook plugin.\n\n@product@'s Plugin Compatibility Layer converts the plugin WAR to a Web\nApplication Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nYour Servlet Filter is running on @product-ver@!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/07-upgrading-customization-plugins/11-upgrading-portal-property-hooks.markdown",
    "content": "---\nheader-id: upgrading-portal-property-hooks\n---\n\n# Upgrading Portal Property Hooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 10 of 11</p>\n</div>\n\nAll portal properties in previous @product@ versions that are also used in\n@product-ver@ can be overridden using portal property hooks. To upgrade portal\nproperty hooks, do this:\n\n1.  Adapt your code to @product-ver@'s API using the Liferay Upgrade Planner. When\n    you ran the planner's *Fix Upgrade Problems* step, many of the existing\n    issues were autocorrected/flagged. For any remaining errors, consult the\n    [Resolving a Project's Dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n    article.\n\n2.  Deploy your hook plugin.\n\n@product@'s Plugin Compatibility Layer converts the plugin WAR to a Web\nApplication Bundle (WAB) and installs it to Liferay's OSGi Runtime.\n\nYour custom property values are live!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/07-upgrading-customization-plugins/12-upgrading-struts-action-hooks.markdown",
    "content": "---\nheader-id: upgrading-struts-action-hooks\n---\n\n# Upgrading Struts Action Hooks\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Customization Plugins</p><p>Step 11 of 11</p>\n</div>\n\nIn Liferay Portal 6.1 and 6.2, developers could customize the Portal and Portlet\nStruts Actions using a Hook and `StrutsAction` wrapper. For example, the\n`liferay-hook.xml` file for a hook that overrode the login portlet's login\naction had this entry:\n\n```xml\n<struts-action>\n    <struts-action-path>/login/login</struts-action-path>\n    <struts-action-impl>\n        com.liferay.sample.hook.action.ExampleStrutsPortletAction\n    </struts-action-impl>\n</struts-action>\n```\n\nThe `liferay-hook.xml` contains the Struts mapping and the new class that \noverrides the default login action. \n\nThe wrapper could extend either `BaseStrutsAction` or `BaseStrutsPortletAction`,\ndepending on whether the Struts Action was a portal or portlet action,\nrespectively.\n\nSince @product@ 7.0, this mechanism no longer applies for most portlets because\nthey no longer use Struts Actions, but instead use Liferay `MVCCommand`s.\n\nThis tutorial demonstrates how to convert your existing `StrutsAction` wrappers\nto `MVCCommand`s. \n\n## Converting Your Old Wrapper to `MVCCommand`s\n\nConverting `StrutsAction` wrappers to `MVCCommand`s is easier than you may\nthink.\n\nAs a review, legacy `StrutsAction` wrappers implemented all methods, such as\n`processAction`, `render`, and `serveResource`, even if only one method was\nbeing customized. Each of these methods can now be customized independently\nusing different classes, making the logic simpler and easier to maintain.\nDepending on the method you customized in your `StrutsAction` wrapper, you need\nto use the matching \n[`MVCCommand` interface](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCCommand.html)\nshown below:\n\n-   `processAction` &rarr; [`MVCActionCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCActionCommand.html)\n-   `render` &rarr; [`MVCRenderCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCRenderCommand.html)\n-   `serveResource` &rarr; [`MVCResourceCommand`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html)\n\nLook at the\n[`ExampleStrutsPortletAction` class](/docs/6-2/tutorials/-/knowledge_base/t/overriding-and-adding-struts-actions)\nfor a `StrutsAction` wrapper example. Depending on the actions overridden, the\nuser must use different `MVCCommand`s. In this example, the action and render\nwere overridden, so to migrate to the new pattern, you would create two classes:\nan `MVCActionCommand` and `MVCRenderCommand`.\n\nNext you'll determine the mapping the `MVCCommand` uses.\n\n## Mapping Your `MVCCommand` URLs\n\nFor most cases, the `MVCCommand` mapping is the same mapping defined in the \nlegacy Struts Action.\n\nUsing the beginning login example once again, the `struts-action-path` mapping,\n`/login/login`, remains the same for the `MVCCommand` mapping in @product-ver@,\nbut some of the mappings may have changed. It's best to check @product@'s source\ncode to determine the correct mapping.\n\nMap to your `MVCCommand` URLs using portlet URL tags:\n\n- `MVCRenderCommand` URLs go in `mvcRenderCommandName` parameters. For\n  example,\n\n    ```xml\n    <portlet:renderURL var=\"editEntryURL\">\n        <portlet:param name=\"mvcRenderCommandName\" value=\"/hello/edit_entry\" />\n        <portlet:param name=\"entryId\" value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n    </portlet:renderURL>\n    ```\n\n- `MVCActionCommand` URLs go in `actionURL` tag `name` attributes or in a\n  parameter `ActionRequest.ACTION_NAME`. For example,\n\n    ```xml\n    <portlet:actionURL name=\"/blogs/edit_entry\" var=\"editEntryURL\" />\n    ```\n \n- `MVCResourceCommand` URLs go in `resourceURL` tag `id` attributes. For\n  example,\n\n    ```xml\n    <portlet:resourceURL id=\"/login/captcha\" var=\"captchaURL\" />\n    ```\n\nOnce you have this information, you can override the `MVCCommand` by following\nthe instructions found in these `MVCCommand` articles:\n\n-   [Adding Logic to MVCCommands](/docs/7-2/customization/-/knowledge_base/c/adding-logic-to-mvc-commands)\n-   [Overriding MVCRenderCommands](/docs/7-2/customization/-/knowledge_base/c/overriding-mvcrendercommand)\n-   [Overriding MVCActionCommands](/docs/7-2/customization/-/knowledge_base/c/overriding-mvcactioncommand)\n-   [Overriding MVCResourceCommands](/docs/7-2/customization/-/knowledge_base/c/overriding-mvcresourcecommand)\n\nNow you know how to convert your `StrutsActionWrapper`s to `MVCCommand`s!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/01-upgrading-a-theme-to-7-2-intro.markdown",
    "content": "---\nheader-id: upgrading-a-theme-to-7-2\n---\n\n# Upgrading a Theme to 7.2\n\n[TOC levels=1-4]\n\nIn these tutorials, you'll learn how to upgrade your themes from earlier \nversions of @product@ to @product-ver@. As you go through this process, you'll \nlearn how to upgrade your theme's metadata, styling, templates, UI, and more \nusing all the best practices and standards. By the end of the tutorial, you'll \nhave a theme that runs on @product-ver@. \n\nTo upgrade your theme, select the tutorial below that corresponds to the current \nversion of your theme:\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/upgrading-6-2-themes-to-7-2\">Let's Go 6.2!<span class=\"icon-circle-arrow-right\"></span></a><br>\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/upgrading-7-0-themes-to-7-2\">Let's Go 7.0!<span class=\"icon-circle-arrow-right\"></span></a><br>\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/upgrading-7-1-themes-to-7-2\">Let's Go 7.1!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/01-upgrading-6.2-themes-to-7.1-intro.markdown",
    "content": "---\nheader-id: upgrading-6-2-themes-to-7-2\n---\n\n# Upgrading Your Theme from Liferay Portal 6.2 to 7.2\n\n[TOC levels=1-4]\n\nIn this tutorial, you'll upgrade the Lunar Resort theme developed in the \nLiferay Portal 6.2 [Developing a Liferay Theme](/docs/6-2/tutorials/-/knowledge_base/t/developing-a-liferay-theme) \nLearning Path to @product-ver@ using the [Liferay JS Theme Toolkit](https://github.com/liferay/liferay-js-themes-toolkit/tree/master/packages). \nThe Lunar Resort theme is similar to many Liferay Portal 6.2 themes, as it \nextends the [`_styled` theme](https://github.com/liferay/liferay-portal/tree/6.2.x/portal-web/docroot/html/themes/_styled), \nadds configurable settings, and incorporates a responsive design that leverages \nFont Awesome icons and Bootstrap. The theme [ZIP file](https://github.com/liferay/liferay-docs/blob/7.0.x/develop/tutorials/code/upgrading-themes/lunar-resort-theme-migration-6.2.zip) \ncontains its original source code. \n\n![Figure 1: The Lunar Resort example theme upgraded in this tutorial uses a clean, minimal design.](../../../../images/finished-7-2-theme.png) \n\nAs you upgrade this theme, you'll learn how to update metadata, theme templates, \nUI, and more using all the best practices and standards. Completing this \ntutorial prepares you for upgrading your own theme. \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/setting-up-the-development-environment-for-6-2-themes\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/02-setting-up-the-development-environment/01-setting-up-the-development-environment-intro.markdown",
    "content": "---\nheader-id: setting-up-the-development-environment-for-6-2-themes\n---\n\n# Setting up the Development Environment\n\n[TOC levels=1-4]\n\nIn this section, you'll set up your development environment to use the \n[Liferay JS Theme Toolkit](https://github.com/liferay/liferay-js-themes-toolkit/tree/master/packages). \nSetting up your development environment involves these steps:\n\n* Install NodeJS with npm. \n\n* Install Yeoman. \n\n* Install the Liferay Theme Generator.\n\n* Import the 6.2 theme to use the Liferay JS Theme Toolkit\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/installing-the-liferay-theme-generator-to-import-a-6-2-theme\">Let's Go<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/02-setting-up-the-development-environment/02-installing-the-theme-generator.markdown",
    "content": "---\nheader-id: installing-the-liferay-theme-generator-to-import-a-6-2-theme\n---\n\n# Installing the Liferay Theme Generator to Import a 6.2 Theme\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Setting up the Development Environment</p><p>Step 1 of 2</p>\n</div>\n\nFollow these steps: \n\n1.  Install [NodeJS](http://nodejs.org/) (along with Node Package Manager(npm)) \n    if it's not already installed. We recommend installing the Long Term Support \n    (LTS) version. Once NodeJS is installed, [set up your npm environment](/docs/7-2/reference/-/knowledge_base/r/setting-up-your-npm-environment).\n\n2.  Use npm to install the [Yeoman](http://yeoman.io/) dependency:\n\n    ```bash\n    npm install -g yo\n    ```\n\n3.  Install the Liferay Theme Generator v8.x.x with the command below:\n\n    ```bash\n    npm install -g generator-liferay-theme@8.x.x\n    ```\n\n    | **Note:** Liferay Theme Generator v8.x.x supports importing 6.2 themes to use \n    | the Liferay JS Theme Toolkit. Later on, you will install the latest version of \n    | the Liferay Theme Generator to complete the upgrade process.\n\n    If you're on Windows, follow the instructions in step 4 to install Sass, \n    otherwise you can skip that step.\n\n4.  The generator uses node-sass. If you're on Windows, you must also install \n    [node-gyp and Python](https://github.com/nodejs/node-gyp#installation).\n\nNice job! Your npm environment is set up and the Liferay Theme Generator and \ndependencies are installed. Next, you can import the @product@ 6.2 theme to use \nthe Liferay JS Theme Toolkit. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/02-setting-up-the-development-environment/03-importing-the-theme-into-the-toolkit.markdown",
    "content": "---\nheader-id: importing-the-theme-into-the-liferay-js-theme-toolkit\n---\n\n# Importing the Theme into the Liferay JS Theme Toolkit\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Setting up the Development Environment</p><p>Step 2 of 2</p>\n</div>\n\nNow you'll import your theme to use the Liferay JS Theme Toolkit. The Liferay JS \nTheme Toolkit provides several useful Gulp tasks for automating theme \ndevelopment and maintenance, including upgrading parts of the theme. \n\nFollow these steps to import your theme to use the Liferay JS Theme Toolkit:\n\n1.  Run the command below to import your 6.2 theme to use the Liferay JS Theme \n    Toolkit:\n\n    ```bash\n    yo liferay-theme:import\n    ```\n\n2.  Provide the path (relative or absolute) to the 6.2 theme project's root \n    folder and press the *Enter* key to import the theme.\n\n3.  Answer the prompts to configure the location to your @product-ver@ app \n    server.\n\nCongratulations! Your development environment is set up to use the Liferay JS \nTheme Toolkit. Next, you'll start upgrading the theme. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/03-running-the-upgrade-task/01-running-the-theme-upgrade-task-for-6.2-themes-intro.markdown",
    "content": "---\nheader-id: running-the-upgrade-task-for-6-2-themes\n---\n\n# Running the Upgrade Task for 6.2 Themes\n\n[TOC levels=1-4]\n\nYou can upgrade a Liferay Portal 6.2 theme to @product-ver@, regardless of the\ndevelopment environment you use. This tutorial uses the Liferay JS Theme \nToolkit's Gulp `upgrade` task to automate much of the steps. Because the theme \nwas built on @product@ 6.2, the Gulp `upgrade` task must be run three times to \nbring it up to @product-ver@. \n\nThe Liferay Theme Generator is available in a few different versions. To update \nthe @product@ 6.2 theme to @product@ 7.0, you must install v8.x.x of the \n`liferay-theme-tasks` dependency. After the theme is updated to 7.1, you must \nthen install v9.x.x of the `liferay-theme-tasks` dependency to complete the \nupgrade process. \n\nHere's what the Upgrade Task does:\n\n- Updates the theme's Liferay version\n- Updates the theme's Bootstrap version\n- Updates the theme's Lexicon version\n- Updates CSS file names\n- Updates theme dependencies\n- Suggests specific code updates\n\nFollow these steps to take the theme through the upgrade process:\n\n1.  Navigate to the theme's root directory and run the command below to update \n    the theme's `liferay-theme-tasks` dependency to version `8.x.x`:\n    \n    ```bash\n    npm install --save-dev liferay-theme-tasks@8.x.x\n    ```\n\n2.  Run the command below to initially upgrade it from 6.2 to 7.0.\n    \n    | **Note**: The Upgrade task overwrites the theme's files. We recommend that \n    | you backup your files before proceeding with the upgrade process.\n\n    ```bash\n    gulp upgrade\n    ```\n\n    Here's what the 6.2 to 7.0 upgrade task does:\n \n    - Updates the theme's Liferay version\n    - Renames CSS files\n    - Suggests specific code updates\n\n    The task continues upgrading CSS files, prompting you to update CSS file \n    names. For @product-ver@, Sass files should use the `.scss` extension, and \n    file names for Sass partials should start with an underscore (e.g., \n    `_custom.scss`). The `upgrade` task prompts you for each CSS file to rename. \n\n    The upgrade task automatically upgrades CSS code that it can identify. For \n    everything else, it suggests upgrades. \n\n3.  Run the `gulp upgrade` command again to upgrade the 7.0 theme to 7.1.\n\n    Here's what it does:\n\n    - Creates core code for generating theme base files\n    - Collects removed Bootstrap and Lexicon variables\n    - Updates Bootstrap version references\n    - Updates Lexicon version references\n    - Updates Liferay version references\n\n4.  You must update the theme's `liferay-theme-tasks` dependency to version \n    `9.x.x` to complete the upgrade process. Install the latest version of the \n    Liferay Theme Generator as well while you're at it, so future uses of the \n    tool will be compatible with the @product-ver@ theme. Both commands are \n    shown below. Run them separately:\n    \n    ```bash\n    npm install --save-dev liferay-theme-tasks@9.x.x\n\n    npm install -g generator-liferay-theme@9.x.x\n    ```\n\n5.  With the `9.x.x` versions of the `liferay-theme-tasks` and Liferay Theme \n    Generator installed, run the `gulp upgrade` command for the final time to \n    upgrade the 7.1 theme to 7.2:\n\n    Here's what it does:\n\n    - Updates Liferay version references\n    - Updates theme dependencies\n\nThe Gulp `upgrade` task lists any deprecated or removed variables. For other \nareas of the code it suspects might need updates, it logs suggestions. The task \nalso reports changes that may affect theme templates. \n\nThe Gulp `upgrade` task jump-starts the upgrade process, but it doesn't complete \nit. Manual updates are required. The remaining portion of this tutorial covers \nthese manual steps. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/04-updating-6.2-css-code/01-updating-6.2-css-code-intro.markdown",
    "content": "---\nheader-id: updating-6-2-css-code\n---\n\n# Updating 6.2 CSS Code\n\n[TOC levels=1-4]\n\n@product-ver@'s UI improvements require these CSS-related changes:\n\n- Updating rules and imports\n- Modifying responsiveness tokens\n\nThe theme upgrade process involves conforming to these changes. \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/updating-6-2-css-rules-and-imports\">Let's Go<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/04-updating-6.2-css-code/02-updating-bootstrap-rules-and-font-awesome-imports.markdown",
    "content": "---\nheader-id: updating-6-2-css-rules-and-imports\n---\n\n# Updating 6.2 CSS Rules and Imports\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating 6.2 CSS Code<p><p>Step 1 of 2</p>\n</div>\n\n@product-ver@ uses Bootstrap 4.3's CSS rule syntax. Font Awesome icons have been \nremoved from base themes, so you should remove those stale imports if you have \nthem. The Gulp `upgrade` task reports automatic CSS updates and suggests manual \nupdates. For example, here is part of the task log for the Lunar Resort theme \nupgrade from 6.2 to 7.0. For each update performed and suggested, the task \nreports a file name and line number range:\n\n```bash\n----------------------------------------------------------------\n Bootstrap Upgrade (2 to 3)\n----------------------------------------------------------------\n\nBecause Liferay Portal 7.0 uses Bootstrap 3, the default box model has been \nchanged to box-sizing: border-box. So if you were using width or height, and \npadding together on an element, you may need to make changes, or those elements \nmay have unexpected sizes.\n\nFile: src/css/_aui_variables.scss\n    Line 5: \"$white\" has been removed\n    Line 31: \"$white\" has been removed\nFile: src/css/_custom.scss\n    Line 201: Padding no longer affects width or height, you may need to change \n    your rule (lines 201-227)\n    Line 207: Padding no longer affects width or height, you may need to change \n    your rule (lines 207-226)\n    Line 212: You would change height from \"62px\" to \"82px\"\n    Line 305: Padding no longer affects width or height, you may need to change \n    your rule (lines 305-314)\n    Line 308: You would change height from \"39px\" to \"46px\"\n    Line 409: Padding no longer affects width or height, you may need to change \n    your rule (lines 409-418)\n```\n\nFollow these steps to update your theme's Bootstrap rules and Font Awesome \nimports:\n\n1.  Since Bootstrap 3 adopted the `box-sizing: border-box` property for all \n    elements and pseudo-elements (e.g., `:before` and `:after`), padding no \n    longer affects dimensions. \n    [Bootstrap's documentation](https://getbootstrap.com/docs/3.3/css/#less-mixins-box-sizing) \n    describes the box sizing changes. Update the width and height for all CSS \n    rules that use padding. For example, examine the `height` value change in \n    this CSS rule for the Lunar Resort theme's `_custom.scss` file:\n\n    Original:\n\n    ```css\n    #reserveBtn {\n    \tbackground-color: #00C4FB;\n    \tborder-radius: 10px;\n    \tcolor: #FFF;\n    \tfont-size: 1.5em;\n    \theight: 62px;\n    \tmargin: 30px;\n    \tpadding: 10px 0;\n    \t...\n    }\n    ```\n\n    Updated:\n\n    ```css\n    #reserveBtn {\n    \tbackground-color: #00C4FB;\n    \tborder-radius: 10px;\n    \tcolor: #FFF;\n    \tfont-size: 1.5em;\n    \theight: 82px;\n    \tmargin: 30px;\n    \tpadding: 10px 0;\n    \t...\n    }\n    ```\n\n    | **Note:** For individual elements, you can overwrite the \n    | `box-sizing:border-box` rule with `box-sizing:content-box`. \n\n2.  The following variables are removed in Bootstrap 4. Remove these variables \n    where they are used in the theme:\n\n    ```scss\n    $line-height-computed\n    $padding-base-horizontal\n    $padding-base-vertical\n    $padding-large-horizontal\n    $padding-large-vertical\n    $padding-small-horizontal\n    $padding-small-vertical\n    $padding-xs-horizontal\n    $padding-xs-vertical\n    $gray-base\n    $gray-darker\n    $gray-dark\n    $gray\n    $gray-light\n    $gray-lighter\n    $brand-primary\n    $brand-success\n    $brand-info\n    $brand-warning\n    $brand-danger\n    $state-success-text\n    $state-success-bg\n    $state-success-border\n    $state-info-text\n    $state-info-bg\n    $state-info-border\n    $state-warning-text\n    $state-warning-bg\n    $state-warning-border\n    $state-danger-text\n    $state-danger-bg\n    $state-danger-border\n    ```\n\n    See the \n    [Migrating from 2.x to 3.0 guide](http://getbootstrap.com/migration/#migrating-from-2x-to-30) \n    for CSS rules that changed in Bootstrap 3. Likewise, you can refer to the \n    [Migrating to v4 guide](https://getbootstrap.com/docs/4.3/migration/) \n    for updating CSS rules to Bootstrap 4. \n\n3.  Font Awesome icons were removed from the theme and Font are now included as \n    a package dependency if you answer yes (y) to include Font Awesome during \n    the upgrade task. If you included the old imports in `_custom.scss`, they \n    must be removed:\n\n    ```scss\n    @import \"aui/alloy-font-awesome/scss/mixins-alloy\";\n    @import \"aui/alloy-font-awesome/scss/variables\";\n    ```\n\nGreat! The rules and imports are updated. You can update the responsiveness \nnext. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/04-updating-6.2-css-code/03-updating-6.2-responsiveness.markdown",
    "content": "---\nheader-id: updating-the-6-2-responsiveness\n---\n\n# Updating the Responsiveness\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating 6.2 CSS Code</p><p>Step 2 of 2</p>\n</div>\n\nBootstrap 4 explicit media queries replaced the Bootstrap 2 `respond-to` mixins\nfor CSS responsiveness. Follow these steps to update the theme's responsiveness:\n\n1.  Open `_custom.scss`.\n\n2.  Replace all `respond-to` mixins with corresponding media queries shown\n    below. Note that some of the dimensions have slightly changed:\n\n    **Media Query Replacements**\n\n    | Liferay Portal 6.2 Mixin                            |  &nbsp;@product-ver@ Media Query                                     |\n    -------------------------------------- |:---------------------------------------------------------- |\n    `@include respond-to(phone)` (max-width: 767px)          | `@include media-breakpoint-down(sm)` (max-width: 767px)              |\n    `@include respond-to(tablet)` (min-width: 768px, max-width: 979px)          | `@include media-breakpoint-only(md)` (min-width: 768px, max-width: 991px)                |\n    `@include respond-to(phone, tablet)` (max-width: 979px) | `@include media-breakpoint-down(md)` (max-width: 991px)      |\n    `@include respond-to(desktop, tablet)` (min-width: 768px) | `@include media-breakpoint-up(md)` (min-width: 768px)                                     |\n    `@include respond-to(desktop)` (min-width: 980px)        | `@include media-breakpoint-up(lg)` (min-width: 992px)          |\n\n    The Lunar Resort theme's original and updated syntax is shown below:\n\n    Original:\n\n    ```scss\n    @include respond-to(phone, tablet) {\n        html #wrapper #banner #navigation {\n        ...\n        }\n    }\n    ```\n\n    Updated:\n\n    ```scss\n    @include media-breakpoint-down(md) {\n        html #wrapper #banner #navigation {\n        ...\n        }\n    }\n    ```\n\nThe CSS code is updated! Next you'll update the theme's templates. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/05-updating-theme-templates/01-updating-6.2-theme-templates-intro.markdown",
    "content": "---\nheader-id: updating-6-2-theme-templates\n---\n\n# Updating 6.2 Theme Templates\n\n[TOC levels=1-4]\n\n@product@ 6.2 theme templates and @product-ver@ theme templates are essentially \nthe same. Here are the main changes:\n\n-   Velocity templates were deprecated in Liferay Portal CE 7.0 and are now \n    removed in favor of FreeMarker templates in @product@. Below are the key \n    reasons for this move: \n\n    -   FreeMarker is developed and maintained regularly, while Velocity is no \n        longer actively being developed.\n\n    -   FreeMarker is faster and supports more sophisticated macros.\n\n    -   FreeMarker supports using taglibs directly rather than requiring a \n        method to represent them. You can pass body content to them, parameters, \n        etc.\n\n-   The Dockbar has been replaced and reorganized into a set of three distinct \n    menus:\n\n    -  *The Product Menu*: Manage Site and page navigation, content, settings \n       and pages for the current Site, and navigate to user account settings, \n       etc.\n\n    -  *The Control Menu*: Configure and add content to the page and view the \n        page in a simulation window. \n\n    -  *The User Personal Bar*: Display notifications and the user's avatar and \n        name. \n\n    ![Figure 1: The Dockbar was removed and must be replaced with the new Control Menu.](../../../../../images/upgrading-themes-dockbar.png) \n\nStart by converting your Velocity theme templates to FreeMarker. You can refer\nto Apache's \n[FreeMarker documentation](https://freemarker.apache.org/docs/ref.html) \nfor help. Common @product@ FreeMarker variables and macros can be found in \n[`FTL_liferay.ftl`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-template/portal-template-freemarker/src/main/resources/FTL_liferay.ftl)\n\nThe [Gulp `upgrade` task](running-the-upgrade-task-for-6.2-themes) reports the \nrequired theme template changes in the log. For example, here are the 6.2 to 7.0 \nupgrade log and 7.0 to 7.1 upgrade logs for the Lunar Resort theme:\n\n```bash\n----------------------------------------------------------------\n Liferay Upgrade (6.2 to 7)\n----------------------------------------------------------------\n\nFile: portal_normal.ftl\n    Warning: <@liferay.dockbar /> is deprecated, replace with \n    <@liferay.control_menu /> for new admin controls.\n    Warning: not all admin controls will be visible without \n    <@liferay.control_menu />\n    Warning: ${theme} variable is no longer available in Freemarker \n    templates, see https://goo.gl/9fXzYt for more information.\n[18:57:23] Finished 'upgrade:log-changes' after 5.61 ms\n[18:57:23] Finished 'upgrade' after 19 s\n\n----------------------------------------------------------------\n Liferay Upgrade (7.0 to 7.1)\n----------------------------------------------------------------\n\nRenamed aui.scss to clay.scss\n[19:16:54] Finished 'upgrade:log-changes' after 2.53 ms\n[19:16:54] Finished 'upgrade' after 16 min\n```\n\nThe log warns about removed and deprecated code and suggests replacements when \napplicable. \n\nIn this section you'll learn how to update various theme templates to \n@product-ver@. \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/updating-6-2-portal-normal-theme-template\">Let's Go<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/05-updating-theme-templates/02-updating-6.2-portal-normal-theme-template.markdown",
    "content": "---\nheader-id: updating-6-2-portal-normal-theme-template\n---\n\n# Updating 6.2 Portal Normal Theme Template\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating 6.2 Theme Templates</p><p>Step 1 of 3</p>\n</div>\n\nFollow these steps to update `portal_normal.ftl`:\n\n1.  Open `portal_normal.ftl` and replace the following 6.2 directives with the \n    updated syntax. This change is described in the [7.0 Breaking Changes](/docs/7-0/reference/-/knowledge_base/r/breaking-changes#taglibs-are-no-longer-accessible-via-the-theme-variable-in-freemarker) \n    reference document:\n\n      6.2                                |  &nbsp;Updated                                                                                                                     |\n    ------------------------------------ |:------------------------------------------------------------------------------------------------------------------------------ |\n    `${theme.include(top_head_include)}`                   | `<@liferay_util[\"include\"] page=top_head_include />`                                                         |\n    `${theme.include(body_top_include)}`                   | `<@liferay_util[\"include\"] page=body_top_include />`                                                         |\n    `${theme.include(content_include)}`                    | `<@liferay_util[\"include\"] page=content_include />`                                                          |\n    `${theme.wrapPortlet(\"portlet.ftl\", content_include)}` | `<@liferay_theme[\"wrap-portlet\"] page=\"portlet.ftl\"> <@liferay_util[\"include\"] page=content_include /> </@>` |\n    `${theme.include(body_bottom_include)}`                | `<@liferay_util[\"include\"] page=body_bottom_include />`                                                      |\n    `${theme.include(bottom_include)}`                     | `<@liferay_util[\"include\"] page=bottom_include />`                                                           |\n    `${theme_settings[\"my-theme-setting\"]}`                | `${themeDisplay.getThemeSetting(\"my-theme-setting\")}`                                                                      |\n    `${theme.runtime(\"56\", \"articleId=\" + my_article_id)}` | `<@liferay_portlet[\"runtime\"] portletName=`<br/>`\"com_liferay_journal_content_web_portlet_JournalContentPortlet\"` <br/>`queryString=\"articleId=\" + my_article_id />`|\n\n2.  Remove the breadcrumbs and page title code:\n\n    ```html\n    <nav id=\"breadcrumbs\">\t\t\n        <@liferay.breadcrumbs />\t\t\n    </nav>\n    ...\n    <h2 class=\"page-title\">\n        <span>${the_title}</span>\n    </h2>\n    ```\n\n3.  Remove `dockbar-split` from the `body` element's `class` value so it matches \n    the markup below:\n\n    ```html    \n    <body class=\"${css_class}\">\n    ```\n\n4.  Find the \n    `<a href=\"#main-content\" id=\"skip-to-content\"><@liferay.language key=\"skip-to-content\" /></a>` \n    element and replace it with the updated Liferay UI quick access macro shown \n    below:\n\n    ```markup    \n    <@liferay_ui[\"quick-access\"] contentId=\"#main-content\" />\n    ```\n\n5.  Replace the `<@liferay.dockbar />` macro with the updated Control menu \n    macro:\n\n    ```markup\n    <@liferay.control_menu />\n    ```\n\n6.  Replace the ` || is_signed_in` condition for the `navigation.ftl` theme template include with `&& is_setup_complete`:\n\n    ```markup    \n    <#if has_navigation && is_setup_complete>\n    \t<#include \"${full_templates_path}/navigation.ftl\" />\n    </#if>\n    ```\n\n7.  Replace the `content` `<div>` with an HTML 5 `section` element. The `section` \n    element is more accurate and provides better accessibility for screen \n    readers:\n\n    ```html\n    <section id=\"content\">\n    ```\n\n8.  Add the `<h1 class=\"hide-accessible\">${the_title}</h1>` header element just \n    inside the `content` `<section>` to support accessibility, and remove the\n    breadcrumbs `<nav>` element from inside it.\n\n`portal_normal.ftl` is updated! Next you can update the navigation template. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/05-updating-theme-templates/03-updating-6.2-navigation-theme-template.markdown",
    "content": "---\nheader-id: updating-6-2-navigation-theme-template\n---\n\n# Updating 6.2 Navigation Theme Template\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating 6.2 Theme Templates</p><p>Step 2 of 3</p>\n</div>\n\nFollow these steps to update `navigation.ftl`:\n\n1.  Below the `<nav class=\"${nav_css_class}\" id=\"navigation\" role=\"navigation\">` \n    element, add the heading below to improve accessibility for screen readers:\n\n    ```html\n    <h1 class=\"hide-accessible\">\n        <@liferay.language key=\"navigation\" />\n    </h1>\n    ```\n\n2.  Remove the `nav_item_attr_selected` variable declaration at the top, and add \n    the layout declaration shown below instead, to access the layout. Don't \n    forget to remove all uses of `nav_item_attr_selected` throughout the rest of \n    the template:\n\n    ```markup\n    <#assign nav_item_layout = nav_item.getLayout() />\n    ```\n\n3.  Replace the `${nav_item.icon()}`variable in the \n    `<a aria-labelledby=\"layout_${nav_item.getLayoutId()}\"...</a>` anchor with \n    the element below: \n\n    ```markup\n    <@liferay_theme[\"layout-icon\"] layout=nav_item_layout />\n    ```\n\n4.  Remove the `nav_child_attr_selected` variable from the bottom of the \n    template, including all uses throughout the rest of the template.\n\nThe navigation template is updated. You can update `portlet.ftl` next. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/05-updating-theme-templates/04-updating-6.2-init-custom-theme-template.markdown",
    "content": "---\nheader-id: updating-6-2-init-custom-theme-template\n---\n\n# Updating 6.2 Init Custom Theme Template\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating 6.2 Theme Templates</p><p>Step 3 of 3</p>\n</div>\n\nThe Lunar Resort theme has a couple theme settings defined in `init_custom.ftl`. \nThe syntax has changed slightly in @product-ver@. Follow these steps to update \nthe theme setting syntax:\n\n1.  Replace the `getterUtil.getBoolean(theme_settings` method with \n    `getterUtil.getBoolean(themeDisplay.getThemeSetting`:\n      \n    Original:\n\n    ```markup\n    <#assign show_breadcrumbs = \n    getterUtil.getBoolean(theme_settings[\"show-breadcrumbs\"])/>\n\n    <#assign show_page_title = \n    getterUtil.getBoolean(theme_settings[\"show-page-title\"])/>\n    ```\n\n    Updated:\n\n    ```markup\n    <#assign show_breadcrumbs = \n    getterUtil.getBoolean(themeDisplay.getThemeSetting(\"show-breadcrumbs\"))/>\n\n    <#assign show_page_title = \n    getterUtil.getBoolean(themeDisplay.getThemeSetting(\"show-page-title\"))/>\n    ```\n\n2.  Although the Lunar Resort theme doesn't have any String variables, you would \n    replace the `getterUtil.getString(theme_settings` method with \n    `themeDisplay.getThemeSetting`:\n\n    Original:\n\n    ```markup\n    <#assign string_setting = \n    getterUtil.getString(theme_settings[\"my-string-key\"])/>\n    ```\n\n    Updated:\n\n    ```markup\n    <#assign string_setting = \n    themeDisplay.getThemeSetting(\"my-string-key\")/>\n    ```\n\nAwesome! The theme templates are updated. You can always compare theme templates \nwith the updated ones found in the \n[`_unstyled` theme](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-unstyled/src/main/resources/META-INF/resources/_unstyled/templates),\nif you're unsure if  something has changed. Refer to the suggested changes that\nthe Gulp `upgrade`  task reports for the theme. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/06-updating-the-resources-importer-configuration-and-content/01-updating-the-importer-intro.markdown",
    "content": "---\nheader-id: updating-the-resources-importer\n---\n\n# Updating the Resources Importer\n\n[TOC levels=1-4]\n\nThe Resources Importer is now an OSGi module bundled with @product@, so you \ndon't have to download the Resources Importer separately. The following \ncomponents have been updated and are the focus of this section:\n\n- Plugin properties\n- Web content article files and folder structure\n- Sitemap\n\n| **Note:** Due to the page and article import order, articles that link to pages\n| in the Site's layout cause a null pointer exception\n| [issue](https://issues.liferay.com/browse/LPS-64859).\n| These links have been removed from the Lunar Resort theme's web content\n| articles to avoid this issue.\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/updating-6-2-liferay-plugin-package-properties\">Let's Go<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/06-updating-the-resources-importer-configuration-and-content/02-updating-package-properties.markdown",
    "content": "---\nheader-id: updating-6-2-liferay-plugin-package-properties\n---\n\n# Updating 6.2 Liferay Plugin Package Properties\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating 6.2 Resources Importer</p><p>Step 1 of 3</p>\n</div>\n\nSince the Lunar Resort theme was developed in the Plugins SDK, it requires \nthe updates covered in this section. Themes developed outside of the Plugins SDK \ndo not require these changes. \n\nFollow these steps to update the Lunar Resort Theme's \n`liferay-plugin-package.properties` file:\n\n1.  Open the `src\\WEB-INF\\liferay-plugin-package.properties` file and remove the \n    `required-deployment-contexts` property. This is no longer needed since the \n    Resources Importer is bundled with @product@.\n\n2.  The group model class's fully-qualified class name has changed. Replace the \n    `resources-importer-target-class-name` property's value with the updated one \n    below:\n\n```properties\ncom.liferay.portal.kernel.model.Group\n```\n\nNow that the `liferay-plugin-package.properties` is updated, you can update the \ntheme's web content. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/06-updating-the-resources-importer-configuration-and-content/03-updating-web-content.markdown",
    "content": "---\nheader-id: updating-6-2-web-content\n---\n\n# Updating 6.2 Web Content\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating 6.2 Resources Importer</p><p>Step 2 of 3</p>\n</div>\n\nAll web content articles must be written in XML and have a structure for article \ncreation and a template for rendering. \n\n| **Note:** The example Lunar Resort theme's updated XML articles are in the ZIP\n| file's `/resources-importer/journal/articles/Basic Web Content/` folder for\n| reference.\n\nFollow these steps to update the theme's web content:\n\n1.  Create a subfolder called `BASIC_WEB_CONTENT` in the \n    `/resources-importer/journal/articles/` folder, and move all the basic HTML \n    articles (articles that did not require a structure or template previously) \n    into it. \n\n2.  Create a subfolder in the `/resources-importer/journal/templates/` folder \n    with the same name as the folder you just created (`BASIC_WEB_CONTENT`). The \n    articles and template folder names **must match** for the web content to \n    import properly.\n\n3.  XML article structures are now written in JSON. Open the \n    `/resources-importer/journal/structures/` folder and create a new file \n    inside called `BASIC_WEB_CONTENT.json`. The structure name **must match** \n    the folder names created in the previous steps. To ensure the syntax is \n    correct for web content articles that used a structure and template before, \n    we recommend that you recreate the structure and template in @product@. \n\n4.  Add the JSON structure below to the `BASIC_WEB_CONTENT.json` file. This \n    provides the required metadata to render standard web content articles \n    (i.e. the language, fields, etc.):\n\n    ```json\n    {\n        \"availableLanguageIds\": [\n            \"en_US\"\n        ],\n        \"defaultLanguageId\": \"en_US\",\n        \"fields\": [\n            {\n                \"label\": {\n                    \"en_US\": \"Content\"\n                },\n                \"predefinedValue\": {\n                    \"en_US\": \"\"\n                },\n                \"style\": {\n                    \"en_US\": \"\"\n                },\n                \"tip\": {\n                    \"en_US\": \"\"\n                },\n                \"dataType\": \"html\",\n                \"fieldNamespace\": \"ddm\",\n                \"indexType\": \"keyword\",\n                \"localizable\": true,\n                \"name\": \"content\",\n                \"readOnly\": false,\n                \"repeatable\": false,\n                \"required\": false,\n                \"showLabel\": true,\n                \"type\": \"ddm-text-html\"\n            }\n        ]\n    }\n    ```\n\n5.  Create a new FreeMarker template file for basic web content inside the \n    `/resources-importer/journal/templates/BASIC_WEB_CONTENT` folder called \n    `BASIC_WEB_CONTENT.ftl`, and add the method below to retrieve the article's \n    data:\n\n    ```markup\n    ${content.getData()}\n    ```\n\n6.  Convert the basic web content articles from HTML to XML to conform to the \n    new format. Replace the `.html` file extensions with `.xml`. wrap each basic \n    web content article's content with the XML shown below:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n\n    <root available-locales=\"en_US\" default-locale=\"en_US\">\n            <dynamic-element name=\"content\" type=\"text_area\"\n            index-type=\"keyword\" index=\"0\">\n                    <dynamic-content language-id=\"en_US\">\n                            <![CDATA[\n                            ORIGINAL HTML CONTENT GOES HERE\n                            ]]>\n                    </dynamic-content>\n            </dynamic-element>\n    </root>\n    ```\n\n7.  @product-ver@'s updated Bootstrap requires that you replace all \n    `span[number]` classes with the updated `col-[device-size]-[number]` syntax.\n    `[device-size]` can be `xs`, `sm`, `md`, or `lg`. See Bootstrap's \n    [documentation](https://getbootstrap.com/docs/4.0/layout/grid/) for more \n    information. The original and updated classes for the Lunar Resort's \n    `2 column description.xml` article are shown below:\n\n    Original:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n\n    <root available-locales=\"en_US\" default-locale=\"en_US\">\n        <dynamic-element name=\"content\" type=\"text_area\"\n        index-type=\"keyword\" index=\"0\">\n            <dynamic-content language-id=\"en_US\">\n                <![CDATA[\n                    <div class=\"container-fluid\">\n                        <div class=\"span4\" id=\"columnLeft\">\n                            Out of This World\n                        </div>\n                        <div class=\"span8\" id=\"columnRight\">\n                            Come to the Lunar Resort...\n                        </div>\n                    </div>\n                ]]>\n            </dynamic-content>\n        </dynamic-element>\n    </root>\n    ```\n\n    Updated:\n\n    ```xml\n    <?xml version=\"1.0\"?>\n\n    <root available-locales=\"en_US\" default-locale=\"en_US\">\n        <dynamic-element name=\"content\" type=\"text_area\"\n        index-type=\"keyword\" index=\"0\">\n            <dynamic-content language-id=\"en_US\">\n                <![CDATA[\n                    <div class=\"container-fluid\">\n                        <div class=\"col-md-4\" id=\"columnLeft\">\n                            Out of This World\n                        </div>\n                        <div class=\"col-md-8\" id=\"columnRight\">\n                            Come to the Lunar Resort...\n                        </div>\n                    </div>\n                ]]>\n            </dynamic-content>\n        </dynamic-element>\n    </root>\n    ```\n\nThe web content is updated! Next, you must update the theme's sitemap file. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/06-updating-the-resources-importer-configuration-and-content/04-updating-the-sitemap.markdown",
    "content": "---\nheader-id: updating-the-6-2-sitemap\n---\n\n# Updating the 6.2 Sitemap\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating 6.2 Resources Importer</p><p>Step 3 of 3</p>\n</div>\n\nIn @product@ 6.2, portlet IDs were incremental numbers. In @product-ver@, \nthey're explicit class names. Update the `sitemap.json` file with the new \nportlet IDs. Follow these steps to update the sitemap:\n\n1.  Replace the portlet IDs with the updated class names. The \n    [Portlet ID Quick Reference Guide](/docs/7-2/reference/-/knowledge_base/r/fully-qualified-portlet-ids) \n    list the default portlet IDs. Check `liferay-portlet.xml` for the portlet ID \n    number in 6.2 and replace it with the updated ID in the quick reference \n    Guide.\n    \n    | **Note:** you can also retrieve a portlet's ID from the UI. Open the \n    | portlet's *Options* menu, select *Look and Feel Configuration*. \n    | \n    | ![Figure 1: You can find the portlet ID in the *Look and Feel Configuration* menu.](../../../../../images/upgrading-themes-look-and-feel-menu.png)\n    | \n    | Select the *Advanced Styling* tab. The `Portlet ID` value appears in the \n    | blue box. \n    | \n    | ![Figure 2: The portlet ID appears within the blue box in the *Advanced Styling* tab.](../../../../../images/upgrading-themes-portlet-id.png)\n\n    The original and updated versions of the Lunar Resort theme's `sitemap.json` \n    are shown below:\n\n    Original:\n\n    ```json\n    {\n    \t\"name\": \"Collaboration\",\n    \t\"title\": \"Collaboration\",\n    \t\"friendlyURL\": \"/collaboration\",\n    \t\"layoutTemplateId\": \"2_columns\",\n    \t\"columns\": [\n    \t\t[\n    \t\t\t{\n    \t\t\t\"portletId\": \"36\"\n    \t\t\t}\n    \t\t],\n    \t\t[\n    \t\t\t{\n    \t\t\t\"portletId\": \"115\"\n    \t\t\t}\n    \t\t]\n    \t\n    \t]\n    }\n    ```\n\n    Updated:\n\n    ```json\n    {\n      \"name\": \"Collaboration\",\n      \"title\": \"Collaboration\",\n      \"friendlyURL\": \"/collaboration\",\n      \"layoutTemplateId\": \"2_columns\",\n      \"columns\": [\n        [\n          {\n          \"portletId\": \"com_liferay_wiki_web_portlet_WikiPortlet\"\n          }\n        ],\n        [\n          {\n          \"portletId\": \"com_liferay_blogs_web_portlet_BlogsAgreggatorPortlet\"\n          }\n        ]\n      \n      ]\n    },\n    ```\n\n2.  Update references to the web content articles in the `sitemap.json` to use \n    the XML file extensions.\n    \nGreat! The Resources Importer updates are complete. Next you'll apply Clay \nmarkup patterns to the theme's custom UI. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/02-upgrading-6.2-themes-7.2/07-applying-clay-design-patterns/01-applying-clay-design-patterns-intro.markdown",
    "content": "---\nheader-id: applying-clay-design-patterns-to-6-2-themes\n---\n\n# Applying Clay Design Patterns\n\n[TOC levels=1-4]\n\n@product-ver@ uses [Clay](https://clayui.com/), a web implementation of \nLiferay's [Lexicon Experience Language](https://lexicondesign.io/). The Lexicon \nExperience Language provides styling guidelines and best practices for \napplication UIs. Clay's CSS, HTML, and JavaScript components enable developers \nto build fully-realized UIs quickly and effectively. @product@'s \n[compatibility layer](/docs/7-2/tutorials/-/knowledge_base/t/using-the-bootstrap-3-lexicon-css-compatibility-layer) \nlet's you use [Lexicon CSS](https://lexiconcss.wedeploy.io/) markup alongside \n[Clay CSS](https://clayui.com/). \n\n| **Note:** The compatibility layer is meant as a short-term solution to ensure \n| that your Bootstrap 3 and Lexicon CSS components aren't broken while you \n| update your theme to use [Bootstrap 4](https://getbootstrap.com/docs/4.3/migration/) \n| and [Clay CSS](https://clayui.com/docs/css-framework/scss.html). It will be \n| disabled in a future release. Migrate your theme to use Bootstrap 4 and Clay \n| CSS as soon as you're able to. \n\nThis section demonstrates how to apply Clay to the Lunar Resort's form. \n\nFollow these steps:\n\n1.  Replace the `control-group` classes with `form-group` classes:\n\n2.  Remove the `control-label` classes from the `label` elements:\n\n3.  Remove `<div class=\"controls\">` elements.\n\n4.  Add the `form-control` class to each `input` element.\n\n5.  Add the `btn-primary` class to your submit buttons to emphasize them.\n\nThe Lunar Resort's original form and updated form are shown below:\n\nOriginal form markup:\n\n```html\n<form class=\"form-horizontal\">\n        <fieldset>\n          <legend>Reservation Form</legend>\n          <div class=\"control-group\">\n              <label class=\"control-label\" for=\"inputName\">Name</label>\n              <div class=\"controls\">\n                      <input type=\"text\" id=\"inputName\"\n                      placeholder=\"Enter your Name here\" required=\"required\">\n                  </div>\n          </div>\n          <div class=\"control-group\">\n              <label class=\"control-label\" for=\"inputEmail\">Email</label>\n              <div class=\"controls\">\n                  <input type=\"email\" id=\"inputEmail\"\n                  placeholder=\"Enter your E-Mail here\" required=\"required\">\n              </div>\n          </div>\n          <div class=\"control-group\">\n              <div class=\"controls\">\n                  <button type=\"submit\" class=\"btn\">Submit</button>\n              </div>\n          </div>\n        </fieldset>\n</form>\n```\n\nUpdated form markup:\n\n```html\n<form role=\"form-horizontal\">\n        <fieldset>\n          <legend>Reservation Form</legend>\n          <div class=\"form-group\">\n              <label for=\"inputName\">Name</label>\n              <input type=\"text\" id=\"inputName\" class=\"form-control\"\n              placeholder=\"Enter your Name here\" required=\"required\">\n          </div>\n          <div class=\"form-group\">\n              <label for=\"inputEmail\">Email</label>\n              <input type=\"email\" id=\"inputEmail\" class=\"form-control\"\n              placeholder=\"Enter your E-Mail here\" required=\"required\">\n          </div>\n          <div class=\"form-group\">\n                  <button type=\"submit\" class=\"btn btn-primary\">Submit\n                  </button>\n          </div>\n        </fieldset>\n</form>\n```\n\nThe Lunar Resort theme is updated for @product-ver@! \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/03-upgrading-7.0-themes-7.2/01-upgrading-7.0-themes-intro.markdown",
    "content": "---\nheader-id: upgrading-7-0-themes-to-7-2\n---\n\n# Upgrading Your Theme from Liferay Portal 7.0 to 7.2\n\n[TOC levels=1-4]\n\nIn this tutorial, you'll learn how to use the [Liferay JS Theme Toolkit](https://github.com/liferay/liferay-js-themes-toolkit/tree/master/packages) \nto upgrade a @product@ 7.0 theme to @product-ver@. As you upgrade this theme, \nyou'll learn how to update metadata, theme templates, UI (including support for \nBootstrap 4 and Lexicon 2.0.), and more using all the best practices and \nstandards. Completing this tutorial prepares you for upgrading your own theme. \n\nTheme upgrades involve these steps:\n\n-  Updating project metadata\n-  Updating CSS\n-  Updating theme templates\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/running-the-upgrade-task-for-7-0-themes\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/03-upgrading-7.0-themes-7.2/02-running-the-upgrade-task/01-running-the-theme-upgrade-task-intro.markdown",
    "content": "---\nheader-id: running-the-upgrade-task-for-7-0-themes\n---\n\n# Running the Upgrade Task for 7.0 Themes\n\n[TOC levels=1-4]\n\nYou can upgrade a @product@ 7.0 theme to @product-ver@, regardless of the \ndevelopment environment you use. This tutorial uses the Liferay JS Theme \nToolkit's Gulp `upgrade` task to automate much of the steps. The Gulp `upgrade` \ntask must be run twice to bring a @product@ 7.0 theme up to @product-ver@. \n\nThe Liferay Theme Generator is available in a few different versions. To update \nthe @product@ 7.0 theme to @product@ 7.1, you must install v8.x.x of the \n`liferay-theme-tasks` dependency. After the theme is updated to 7.1, you must \nthen install v9.x.x of the `liferay-theme-tasks` dependency to complete the \nupgrade process. \n\nHere's what the Upgrade Task does:\n\n- Updates the theme's Liferay version\n- Updates the theme's Bootstrap version\n- Updates the theme's Lexicon version\n- Suggests specific code updates\n\nFollow these steps to take the theme through the upgrade process:\n\n1.  Navigate to the theme's root directory and run the command below to update \n    the theme's `liferay-theme-tasks` dependency to version `8.x.x`:\n    \n    ```bash\n    npm install --save-dev liferay-theme-tasks@8.x.x\n    ```\n\n2.  Run the `gulp upgrade` command to upgrade the @product@ 7.0 theme to 7.1.\n\n    | **Note**: The Upgrade task overwrites the theme's files. We recommend that \n    | you backup your files before proceeding with the upgrade process.\n\n    Here's what it does:\n\n    - Creates core code for generating theme base files\n    - Collects removed Bootstrap and Lexicon variables\n    - Updates Bootstrap version references\n    - Updates Lexicon version references\n    - Updates Liferay version references\n\n3.  You must update the theme's `liferay-theme-tasks` dependency to version \n    `9.x.x` to complete the upgrade process. Install the latest version of the \n    Liferay Theme Generator as well while you're at it, so future uses of the \n    tool will be compatible with the @product-ver@ theme. Both commands are \n    shown below. Run them separately:\n    \n    ```bash\n    npm install --save-dev liferay-theme-tasks@9.x.x\n\n    npm install -g generator-liferay-theme@9.x.x\n    ```\n\n4.  With the `9.x.x` versions of the `liferay-theme-tasks` and Liferay Theme \n    Generator installed, run the `gulp upgrade` command for the final time to \n    upgrade the 7.1 theme to 7.2:\n\n    Here's what it does:\n\n    - Updates Liferay version references\n    - Updates theme dependencies\n\n    | **Note:** Since Liferay DXP Fix Pack 2 and Liferay Portal 7.2 CE GA2, Font \n    | Awesome is available globally as a system setting, which is enabled by \n    | default. If you're using Font Awesome icons in your theme, answer yes (y) \n    | to the Font Awesome question during the Upgrade task to include Font \n    | Awesome imports in your theme. This ensures that your icons won't break if \n    | a Site Administrator disables the global setting. \n\n5.  Run `gulp init` from your theme's root directory to update the path of your \n    @product@ server to point to your 7.2 @product@ server. \n\nThe Gulp `upgrade` task lists any deprecated or removed variables. For other \nareas of the code it suspects might need updates, it logs suggestions. The task \nalso reports changes that may affect theme templates. This jump-starts the \nupgrade process, but it doesn't complete it. Manual updates are required. The \nremaining portion of this tutorial covers these manual steps. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/03-upgrading-7.0-themes-7.2/03-updating-7.0-css-code/01-updating-7.0-css-code-intro.markdown",
    "content": "---\nheader-id: updating-7-0-css-code\n---\n\n# Updating 7.0 CSS Code\n\n[TOC levels=1-4]\n\n@product-ver@'s UI improvements requires these CSS-related changes:\n\n- Renaming CSS files\n- Class variable changes\n- Updating core imports\n\nThe theme upgrade process involves conforming to these changes. Now you'll \nupdate the theme's CSS files to reflect these changes. Start with updating CSS \nfile names. \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/updating-7-0-css-file-names-for-clay\">Let's Go<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/03-upgrading-7.0-themes-7.2/03-updating-7.0-css-code/02-renaming-7-0-css-files.markdown",
    "content": "---\nheader-id: updating-7-0-css-file-names-for-clay\n---\n\n# Updating 7.0 CSS File Names for Clay\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating 7.0 CSS Code</p><p>Step 1 of 3</p>\n</div>\n\nSome of the CSS filenames have changed to reflect the introduction of Clay \n(previously Lexicon CSS). The file name changes for the Unstyled theme are \nlisted below. Refer to the \n[Theme Reference Guide](/docs/7-2/reference/-/knowledge_base/r/theme-reference-guide) \nfor a complete list of expected theme CSS files. \n\nOrignal AUI file names:\n\n- `css/`\n\t- `_aui_custom.scss`\n\t- `_aui_variables.scss`\n\t- `aui.scss`\n\nUpdated Clay file names:\n\n- `css/`\n\t- `_clay_custom.scss`\n\t- `_clay_variables.scss`\n\t- `clay.scss`\n\n\nNext, you can update the theme's CSS variables. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/03-upgrading-7.0-themes-7.2/03-updating-7.0-css-code/03-updating-7.0-variables.markdown",
    "content": "---\nheader-id: updating-7-0-class-variables\n---\n\n# Updating 7.0 Class Variables\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating 7.0 CSS Code</p><p>Step 2 of 3</p>\n</div>\n\n@product-ver@ uses Bootstrap 4's CSS rule syntax. The new syntax lets developers \nleverage Bootstrap 4 features and improvements. The [Migrating to v4 guide](https://getbootstrap.com/docs/4.0/migration/) \nprovides complete instructions for updating CSS rules to Bootstrap 4. \n\nFollow these steps to upgrade the theme's CSS variables:\n\n1.  Consult the upgrade log produced by the `gulp upgrade` task. It suggests the \n    manual Lexicon updates required for the theme.\n\n2.  Make the required changes in the log. The log lists removed and/or \n    deprecated variables and suggests possible changes. For each update \n    performed or suggested, the task reports a file name. For example, here is \n    part of the task log for the 7.0 Westeros Bank theme:\n\n```bash\n----------------------------------------------------------------\n Lexicon Upgrade (1.0 to 2.0)\n----------------------------------------------------------------\n\nFile: _variables_custom.scss\n    $brand-default was deprecated in Lexicon CSS 1.x.x and has been removed \n\t\tin the new Clay 2.x.x version\n```\n\n| **Note:** If the `gulp upgrade` task detects any variables in the theme that \n| are removed in Clay from the previous LexiconCSS version, it adds a \n| `_variables_deprecated.scss` file to the theme containing the removed \n| variables, to make sure the theme compiles and to decouple it from future \n| upgrades.\n\nAfter updating your theme's CSS variables and mixins, you should update the \nimports next. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/03-upgrading-7.0-themes-7.2/03-updating-7.0-css-code/04-updating-7.0-imports.markdown",
    "content": "---\nheader-id: updating-7-0-imports\n---\n\n# Updating 7.0 Imports\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Updating 7.0 CSS Code</p><p>Step 3 of 3</p>\n</div>\n\nFont Awesome imports and core imports have changed. Follow these steps to update \nthe theme:\n\n1.  Originally in Liferay Portal CE 7.0 and Liferay DXP, Font Awesome icons were \n    imported in `_aui_variables.scss` (now renamed `_clay_variables.scss`). Font \n    Awesome icons are now included as a package dependency if you answer yes (y) \n    to include Font Awesome during the upgrade task. If a 7.0 theme was made \n    prior to this move and `_aui_variables.scss` was modified, the Font Awesome \n    imports shown below must be removed from `_clay_variables.scss`:\n\n    ```scss\n    // Icon paths\n    $FontAwesomePath: \"aui/lexicon/fonts/alloy-font-awesome/font\";\n    $font-awesome-path: \"aui/lexicon/fonts/alloy-font-awesome/font\";\n    $icon-font-path: \"aui/lexicon/fonts/\";\n    ```\n\n2.  Update the old AUI lexicon paths to use the new Clay paths instead, as shown \n\t\tin the table below:\n\n    |Pattern|Replacement|\n    |---|---|\n    |`@import \"/aui/lexicon/bootstrap/mixins/\";`|removed|\n    |`@import \"/aui/lexicon/lexicon-base/mixins/\";`|removed|\n    |`@import \"/aui/lexicon/atlas-theme/mixins/\";`|removed|\n    |`@import \"aui/lexicon/atlas-variables\";`|`@import \"clay/atlas-variables\";`|\n    |`@import \"aui/lexicon/atlas\";`|`@import \"clay/atlas\";`|\n\nGreat! Your imports are updated, and your CSS upgrade is complete. Next you can \nupgrade the theme templates. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/03-upgrading-7.0-themes-7.2/04-updating-7.0-theme-templates/01-updating-7.0-theme-templates-intro.markdown",
    "content": "---\nheader-id: updating-7-0-theme-templates-to-7-2\n---\n\n# Updating 7.0 Theme Templates to 7.2\n\n[TOC levels=1-4]\n\n@product@ 7.0 theme templates and @product-ver@ theme templates are essentially \nthe same. Here are the main changes:\n\n-   Velocity templates were deprecated in Liferay Portal CE 7.0 and are now \n    removed in favor of FreeMarker templates in @product-ver@. \n\nKey reasons for using FreeMarker templates and removing Velocity templates\nare these: \n\n-   FreeMarker is developed and maintained regularly, while Velocity is no longer\n    actively being developed.\n \n-   FreeMarker is faster and supports more sophisticated macros.\n\n-   FreeMarker supports using taglibs directly rather than requiring a method \n    to represent them. You can pass body content to them, parameters, etc.\n\nIf you haven't converted your Velocity theme templates to FreeMarker, **you must \nconvert your Velocity theme templates to FreeMarker now**. \n\nThe `gulp upgrade` command reports the required theme template changes in the \nlog. For example, here is the `gulp upgrade` log for the Westeros Bank theme:\n\n```bash\n----------------------------------------------------------------\n Liferay Upgrade (7.0 to 7.1)\n----------------------------------------------------------------\n\nRenamed aui.scss to clay.scss\nFile: footer.ftl\n    Warning: .container-fluid-1280 has been deprecated. Please use \n    .container-fluid.container-fluid-max-xl instead.\nFile: portal_normal.ftl\n    Warning: .navbar-header has been removed. This container should be \n    removed in most cases. Please, use your own container if necessary.\n```\n\nThe log warns about removed and deprecated code and suggests replacements when\napplicable. For reference, the main changes between @product@ 7.0 themes and \n@product-ver@ themes appear below:\n\n- List items inside a container with the `list-inline` class \n  [now require](https://getbootstrap.com/docs/4.3/migration/#typography) \n  the `list-inline-item` class. \n\n- The `container-fluid-1280` class has been deprecated. Please use \n  `container-fluid container-fluid-max-xl` instead.\n\n- Responsive navbar behaviors \n  [are now applied](https://getbootstrap.com/docs/4.3/migration/#navbar)\n  to the `navbar` class via the required `navbar-expand-{breakpoint}` class. \n\n- The `navbar-toggle` class is now `navbar-toggler` and \n  [has different inner markup](https://getbootstrap.com/docs/4.3/migration/#navbar). \n\n- The `navbar-header` class has been removed. This container should be removed \n  in most cases. Please, use your own container if necessary.\n\nIn this section you'll learn how to update various theme templates to \n@product-ver@. \n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/updating-7-0-theme-templates\">Let's Go<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/03-upgrading-7.0-themes-7.2/04-updating-7.0-theme-templates/02-updating-7.0-theme-templates-to-7.2.markdown",
    "content": "---\nheader-id: updating-7-0-theme-templates\n---\n\n# Updating 7.0 Theme Templates\n\n[TOC levels=1-4]\n\nFollow these steps to update the theme's templates. Note these changes are only \nrequired if the templates are modified in the theme:\n\n1.  Open `portal_normal.ftl` and remove the breadcrumbs:\n\n    ```html\n    <nav id=\"breadcrumbs\">\t\t\n        <@liferay.breadcrumbs />\t\t\n    </nav>\n    ```\n\n2.  Still inside `portal_normal.ftl`, remove `id=\"main-surface\"` from the `body` \n    tag so it looks like the one below. This is not needed for SPA to work \n    properly:\n\n    ```html\n    <body class=\"${css_class}\">\n    ```\n\n3.  Open `navigation.ftl` and remove the `nav_item_attr_selected` variable \n    declaration at the top. Don't forget to remove all uses of the \n    `nav_item_attr_selected` throughout the rest of the template.\n\n4.  Also inside `navigation.ftl`, remove the `nav_child_attr_selected` variable \n    from the bottom of the template, including all uses throughout the rest of \n    the template.\n    \n5.  Open `portlet.ftl` and find the code snippet below:\n\n    ```html\n    <a \n      class=\"icon-monospaced portlet-icon-back text-default\" \n      href=\"${portlet_back_url}\" \n      title=\"<@liferay.language key=\"return-to-full-page\" />\"\n    >\n    ```\n    \n    Add the `list-unstyled` class to it:\n\n    ```html\n    <a \n      class=\"icon-monospaced list-unstyled portlet-icon-back text-default\" \n      href=\"${portlet_back_url}\" \n      title=\"<@liferay.language key=\"return-to-full-page\" />\"\n    >\n    ```\n\n6.  Still inside `portlet.ftl`, find the \n    `<div class=\"autofit-float autofit-row\">` element and add the \n    `portlet-header` class to it:\n\n    ```html    \n    <div class=\"autofit-float autofit-row portlet-header\">\n    ```\n\nThe theme templates are updated! If you modified any other FreeMarker theme \ntemplates, you can compare them with templates in the \n[`_unstyled` theme](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-unstyled/src/main/resources/META-INF/resources/_unstyled/templates). Next you can learn how \nto use @product@'s compatibility layer to help ease the transition to Bootstrap \n4 and Clay CSS. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/03-upgrading-7.0-themes-7.2/05-bootstrap-lexicon-compatibility-layer.markdown",
    "content": "---\nheader-id: using-the-bootstrap-3-lexicon-css-compatibility-layer\n---\n\n# Using the Bootstrap 3 Lexicon CSS Compatibility Layer\n\n[TOC levels=1-4]\n\nBy default, @product-ver@ includes Bootstrap 4 out-of-the-box. Bootstrap 4 has \nbeen completely rewritten and therefore includes some \n[notable changes](https://getbootstrap.com/docs/4.3/migration/) \nand \n[compatibility updates](https://getbootstrap.com/docs/4.3/getting-started/introduction/) \nthat may be cause for concern if your theme uses Bootstrap 3 or Lexicon CSS. Not \nto worry though. To ensure that your upgrade runs smoothly, @product@ includes a \ncompatibility layer so you can use Bootstrap 3 markup and Lexicon CSS markup \nalongside the new Bootstrap 4 and Clay CSS. If your theme extends the \n[Styled base theme](https://github.com/liferay/liferay-portal/tree/7.2.x/modules/apps/frontend-theme/frontend-theme-styled), \nthis compatibility layer is included by default. \n\n| **Note:** The compatibility layer is meant as a short-term solution to ensure \n| that your Bootstrap 3 and Lexicon CSS components aren't broken while you \n| update your theme to use [Bootstrap 4](https://getbootstrap.com/docs/4.3/migration/) \n| and [Clay CSS](https://clayui.com/docs/css-framework/scss.html). It will be \n| disabled in a future release. Migrate your theme to use Bootstrap 4 and Clay \n| CSS as soon as you're able to. \n \nFollow these guidelines to update your markup:\n\n1.  Inspect your themes UI with the compatibility layer enabled \n    (it's enabled by default), and note any issues.\n\n2.  Individually disable the component(s) in the compatibility layer that you \n    don't need. These are listed in the \n    [`css/compat/_variables.scss`](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/frontend-theme/frontend-theme-styled/src/main/resources/META-INF/resources/_styled/css/compat/_variables.scss) \n    file. For convenience, these components are listed below:\n\n    ```scss\n    // Compatibility layer components config\n\n    $compat-alerts: true !default;\n    $compat-basic_search: true !default;\n    $compat-breadcrumbs: true !default;\n    $compat-button_groups: true !default;\n    $compat-buttons: true !default;\n    $compat-cards: true !default;\n    $compat-component_animations: true !default;\n    $compat-dropdowns: true !default;\n    $compat-figures: true !default;\n    $compat-form_validation: true !default;\n    $compat-forms: true !default;\n    $compat-grid: true !default;\n    $compat-icons: true !default;\n    $compat-labels: true !default;\n    $compat-liferay: true !default;\n    $compat-list_groups: true !default;\n    $compat-management_bar: true !default;\n    $compat-modals: true !default;\n    $compat-nav_tabs: true !default;\n    $compat-navbar: true !default;\n    $compat-navs: true !default;\n    $compat-pager: true !default;\n    $compat-pagination: true !default;\n    $compat-panels: true !default;\n    $compat-progress_bars: true !default;\n    $compat-responsive_utilities: true !default;\n    $compat-sidebar: true !default;\n    $compat-simple_flexbox_grid: true !default;\n    $compat-stickers: true !default;\n    $compat-tables: true !default;\n    $compat-toggle_card: true !default;\n    $compat-toggle_switch: true !default;\n    $compat-toolbar: true !default;\n    $compat-user_icons: true !default;\n    $compat-utilities: true !default;\n    ```\n\n    To disable a component, add the component you want to remove compatibility \n    for to `/src/css/_clay_custom.scss` (create this file if it doesn't exist) \n    and set its value to `false`. The example below removes compatibility for \n    alerts and cards:\n\n    ```scss    \n    $compat-alerts: false !default;\n    $compat-cards: false !default;\n    ```\n\n    | **Note:** Some @product@ components haven't been migrated to Bootstrap 4.\n    | Disabling certain components might cause portions of the UI to break.\n    | Therefore, after upgrading your markup, we recommend that you re-enable any\n    | components you disable. Proceed with caution.\n\n3.  Update your markup to Bootstrap 4 and Clay CSS until you're satisfied with \n    the result.\n\n4.  Re-enable any components you disabled in the compatibility layer by \n    removing any components you set to false in `/src/css/_clay_custom.scss`. \n    This prevents @product@'s UI from breaking.\n\nNow you know how to use the Bootstrap 3 and Lexicon CSS compatibility layer to \nprovide a smooth transition during your theme upgrade. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/08-upgrading-themes/04-upgrading-7.1-themes-7.2/01-upgrading-7-1-themes-7-2-intro.markdown",
    "content": "---\nheader-id: upgrading-7-1-themes-to-7-2\n---\n\n# Upgrading 7.1 Themes to 7.2\n\n[TOC levels=1-4]\n\nYou can upgrade a Liferay Portal 7.1 theme to @product-ver@, regardless of the\ndevelopment environment you use. This tutorial uses the Liferay JS Theme \nToolkit's Gulp `upgrade` task to automate much of the steps. This requires \nv9.x.x of the Liferay Theme Generator and liferay theme tasks. \n\nHere's what the Upgrade Task does:\n\n- Updates Liferay version references\n- Updates theme dependencies\n\nFollow these steps to upgrade the theme:\n\n1.  Install the Liferay Theme Generator v9.x.x with the command below:\n\n    ```bash\n    npm install -g generator-liferay-theme@9.x.x\n    ```\n\n2.  You must update the theme's `liferay-theme-tasks` dependency to version \n    `9.x.x` as well to run the upgrade process:\n    \n    ```bash\n    npm install --save-dev liferay-theme-tasks@9.x.x\n    ```\n\n3.  With the `9.x.x` versions of the `liferay-theme-tasks` and Liferay Theme \n    Generator installed, run the `gulp upgrade` command to upgrade the 7.1 theme \n    to 7.2.\n\n    | **Note**: The Upgrade task overwrites the theme's files. We recommend that \n    | you backup your files before proceeding with the upgrade process.\n\n4.  In 7.1, Font Awesome & Glyphicons were included in the [compatibility layer](/docs/7-1/tutorials/-/knowledge_base/t/using-the-bootstrap-3-lexicon-css-compatibility-layer). \n    Since Liferay DXP Fix Pack 2 and Liferay Portal 7.2 CE GA2, Font Awesome is \n    available globally as a system setting, which is enabled by default. If \n    you're using Font Awesome icons in your theme, answer yes (y) to the Font \n    Awesome question during the Upgrade task to include the Font Awesome \n    dependency  in your theme. This ensures that your icons won't break if a \n    Site Administrator disables the global setting. \n\n5.  Run `gulp init` from the theme's root directory to update the path of the \n    app server to point to the new 7.2 app server. \n\nThere you have it! The theme is ready to run on @product-ver@. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/09-upgrading-layout-templates/01-upgrading-layout-templates-to-7.2-intro.markdown",
    "content": "---\nheader-id: upgrading-a-layout-template-to-7-2\n---\n\n# Upgrading a Layout Template to 7.2\n\n[TOC levels=1-4]\n\nIn these tutorials, you'll learn how to upgrade your layout templates from \nearlier versions of @product@ to @product-ver@. By the end of the tutorial, \nyou'll have a layout template that runs on @product-ver@. \n\nSelect the tutorial below that corresponds to the current version of your layout \ntemplate:\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/upgrading-6-2-layout-templates-to-7-2\">Let's Go 6.2!<span class=\"icon-circle-arrow-right\"></span></a><br>\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/upgrading-7-0-and-7-1-layout-templates-to-7-2\">Let's Go 7.0 and 7.1!<span class=\"icon-circle-arrow-right\"></span></a><br>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/09-upgrading-layout-templates/02-upgrading-6.2-layouts-7.2/01-upgrading-6-2-layout-templates-intro.markdown",
    "content": "---\nheader-id: upgrading-6-2-layout-templates-to-7-2\n---\n\n# Upgrading 6.2 Layout Templates to 7.2\n\n[TOC levels=1-4]\n\nUpgrading your @product@ 6.2 layout template to @product-ver@ requires a few \nupdates:\n\n1.  Open your layout template's `liferay-plugin-package.properties` file and \n    update the `liferay-versions` property to `7.2.0+`:\n\n    ```properties\n    liferay-versions=7.2.0+\n    ```\n\n2.  Velocity layout templates are supported, but deprecated as of @product@ 7.1. \n    We recommend that you convert your Velocity layout templates to FreeMarker \n    now. Wrap the \n    `processor.processColumn(\"column-1\", \"portlet-column-content portlet-column-content-first\")` \n    methods with braces (`{...}`) and change the template's file extension to \n    `.ftl`.\n\n3.  Update the Bootstrap `span[number]` classes to use the newer \n    `col-[size]-[number]` classes. See [Layout Templates](/docs/7-2/frameworks/-/knowledge_base/f/layout-templates-intro) \n    for more information on the updated syntax.\n\n4.  Save the changes.\n\n    Below is an example configuration:\n\n    Original:\n\n    ```html\n    <div class=\"span4 span6 portlet-column portlet-column-first\" \n    id=\"column-1\">\n            $processor.processColumn(\"column-1\", \n            \"portlet-column-content portlet-column-content-first\")\n    </div>\n    <div class=\"span8 span6 portlet-column portlet-column-last\" \n    id=\"column-2\">\n            $processor.processColumn(\"column-2\", \n            \"portlet-column-content portlet-column-content-last\")\n    </div>\n    </div>\n    ```\n\n    Updated:\n\n    ```html\n    <div class=\"col-md-4 col-sm-6 portlet-column portlet-column-first\" \n    id=\"column-1\">\n            ${processor.processColumn(\"column-1\", \n            \"portlet-column-content portlet-column-content-first\")}\n    </div>\n    <div class=\"col-md-8 col-sm-6 portlet-column portlet-column-last\" \n    id=\"column-2\">\n            ${processor.processColumn(\"column-2\", \n            \"portlet-column-content portlet-column-content-last\")}\n    </div>\n    </div>\n    ```\n\nAwesome! Your layout template is upgraded. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/09-upgrading-layout-templates/03-upgrading-7.0-7.1-layouts-7.2/01-upgrading-7-0-layout-templates-intro.markdown",
    "content": "---\nheader-id: upgrading-7-0-and-7-1-layout-templates-to-7-2\n---\n\n# Upgrading 7.0 and 7.1 Layout Templates to 7.2\n\n[TOC levels=1-4]\n\nIf you're upgrading your @product@ 7.0 or @product@ 7.1 layout template to \n@product-ver@, follow these steps:\n\n1.  Open your layout template's `liferay-plugin-package.properties` file and \n    update the `liferay-versions` property to `7.2.0+`:\n\n    ```properties\n    liferay-versions=7.2.0+\n    ```\n\n2.  Velocity layout templates are supported, but deprecated as of @product@ 7.1. \n    We recommend that you convert your Velocity layout templates to FreeMarker \n    now. Wrap the \n    `processor.processColumn(\"column-1\", \"portlet-column-content portlet-column-content-first\")` \n    methods with braces (`{...}`) and change the template's file extension to \n    `.ftl`.\n\n3.  Save the changes.\n\n    Below is an example configuration:\n\n    Original (`my_layout_template.tpl`):\n\n    ```html\n    <div class=\"col-md-4 col-sm-6 portlet-column portlet-column-first\" \n    id=\"column-1\">\n            $processor.processColumn(\"column-1\", \n            \"portlet-column-content portlet-column-content-first\")\n    </div>\n    <div class=\"col-md-8 col-sm-6 portlet-column portlet-column-last\" \n    id=\"column-2\">\n            $processor.processColumn(\"column-2\", \n            \"portlet-column-content portlet-column-content-last\")\n    </div>\n    </div>\n    ```\n\n    Updated (`my_layout_template.ftl`):\n\n    ```html\n    <div class=\"col-md-4 col-sm-6 portlet-column portlet-column-first\" \n    id=\"column-1\">\n            ${processor.processColumn(\"column-1\", \n            \"portlet-column-content portlet-column-content-first\")}\n    </div>\n    <div class=\"col-md-8 col-sm-6 portlet-column portlet-column-last\" \n    id=\"column-2\">\n            ${processor.processColumn(\"column-2\", \n            \"portlet-column-content portlet-column-content-last\")}\n    </div>\n    </div>\n    ```\n\nAwesome! Your layout template is upgraded. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/10-upgrading-frameworks-and-features/01-intro.markdown",
    "content": "---\nheader-id: upgrading-frameworks-and-features\n---\n\n# Upgrading Frameworks and Features\n\n[TOC levels=1-4]\n\nYour upgrade process not only relies on portlet technology, themes, and\ncustomization plugins, but also the frameworks your project leverages. The\nfollowing frameworks and their upgrade processes are discussed in this section:\n\n- JNDI data source usage\n- Service Builder service invocation\n- Service Builder\n- Velocity templates\n\nContinue on to learn more about upgrading these frameworks.\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/upgrading-jndi-data-source-usage\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/10-upgrading-frameworks-and-features/02-upgrading-jndi-data-source-usage.markdown",
    "content": "---\nheader-id: upgrading-jndi-data-source-usage\n---\n\n# Upgrading JNDI Data Source Usage\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Frameworks and Features</p><p>Step 1 of 4</p>\n</div>\n\nIn @product@'s OSGi environment, you must use the portal's class loader to load\nthe application server's JNDI classes. An OSGi bundle's attempt to connect to a\nJNDI data source without using @product@'s class loader results in a\n`java.lang.ClassNotFoundException`.\n\nFor more information on how to do this, see the\n[Connecting to JNDI Data Sources](/docs/7-2/appdev/-/knowledge_base/a/connecting-to-data-sources-using-jndi)\narticle.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/10-upgrading-frameworks-and-features/03-upgrading-service-builder-service-invocation.markdown",
    "content": "---\nheader-id: upgrading-service-builder-service-invocation\n---\n\n# Upgrading Service Builder Service Invocation\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Frameworks and Features</p><p>Step 2 of 4</p>\n</div>\n\nWhen upgrading a portlet leveraging\n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder), you must\nfirst decide if you're building your Service Builder logic as a WAR or\nmodularizing it.\n\n| **Note:** Service Builder portlets automatically migrated to Liferay Workspace\n| using the Upgrade Planner or Blade CLI's `convert` command automatically\n| have its Service Builder logic converted to API and implementation modules.\n| This is a best practice for @product-ver@.\n\nIf you prefer keeping your Service Builder logic as a WAR, you must implement a\nservice tracker to call services. See the\n[Service Trackers](/docs/7-2/frameworks/-/knowledge_base/f/using-a-service-tracker)\narticle for more information.\n\n<!--Uncomment once article is available\nIf you're optimizing your Service Builder logic to invoke Liferay services from\na module, see the Invoking Local Services article for more information.\n-->\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/10-upgrading-frameworks-and-features/04-upgrading-service-builder.markdown",
    "content": "---\nheader-id: upgrading-service-builder\n---\n\n# Upgrading Service Builder\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Frameworks and Features</p><p>Step 3 of 4</p>\n</div>\n\n@product-ver@ continues to use \n[Service Builder](/docs/7-2/appdev/-/knowledge_base/a/service-builder), so\nyou can focus on your application's business logic instead of its persistence\ndetails. It still generates model classes, local and remote services, and\npersistence. \n\nUpgrading most Service Builder portlets involves these steps: \n\n1.  [Adapt the code to @product-ver@'s API](#step-1-adapt-the-code-to-product-vers-api)\n2.  [Resolve dependencies](#step-2-resolve-dependencies)\n3.  [Build the services](#step-3-build-the-services)\n\nStart by adapting the code. \n\n## Step 1: Adapt the Code to @product-ver@'s API\n\nAdapt the portlet to @product-ver@'s API using the Upgrade Planner. When\nrunning the planner's *Fix Upgrade Problems* step, many of the existing issues\nare autocorrected. For remaining issues, the planner identifies code affected by\nthe new API and ways to adapt it.\n\nFor example, consider an example portlet with the following compilation error:\n\n```bash\n/html/guestbook/view.jsp(58,1) PWC6131: Attribute total invalid for tag search-container-results according to TLD\n```\n\nThe `view.jsp` file specifies a tag library attribute `total` that doesn't exist\nin @product-ver@'s `liferay-ui` tag library. Notice the second attribute\n`total`. \n\n```markup\n<liferay-ui:search-container-results\n    results=\"<%=EntryLocalServiceUtil.getEntries(scopeGroupId,\n                    guestbookId, searchContainer.getStart(),\n                    searchContainer.getEnd())%>\"\n    total=\"<%=EntryLocalServiceUtil.getEntriesCount(scopeGroupId,\n                    guestbookId)%>\" />\n```\n\nRemove the `total` attribute assignment to make the tag like this:\n\n```markup\n<liferay-ui:search-container-results\n    results=\"<%=EntryLocalServiceUtil.getEntries(scopeGroupId,\n                    guestbookId, searchContainer.getStart(),\n                    searchContainer.getEnd())%>\" />\n```\n\nResolve these error types and others until your code is adapted to the new API. \n\n## Step 2: Resolve Dependencies\n\nTo adapt your app's dependencies, refer to the\n[Resolving a Project's Dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\ntutorial. Once your dependencies are upgraded, rebuild your services!\n\n## Step 3: Build the Services\n\n<!--Uncomment once article is available\nTo rebuild your portlet's services, see the Running Service Builder article.\n-->\n\nAn example change where upgrading legacy Service Builder code can produce\ndiffering results is explained below.\n\nA Liferay Portal 6.2 portlet's `service.xml` file specifies exception class\nnames in `exception` elements like this:\n\n```xml\n<service-builder package-path=\"com.liferay.docs.guestbook\">\n    ...\n    <exceptions>\n        <exception>GuestbookName</exception>\n        <exception>EntryName</exception>\n        <exception>EntryMessage</exception>\n        <exception>EntryEmail</exception>\n    </exceptions>\n</service-builder>\n```\n\nIn Liferay Portal 6.2, Service Builder generates exception classes to the path\nattribute `package-path` specifies. In @product-ver@, Service Builder generates\nthem to `[package-path]/exception`. \n\nOld path:\n\n```\n[package-path]\n```\n\nNew path:\n\n```\n[package-path]/exception \n```\n\nFor example, the example portlet's package path is\n`com.liferay.docs.guestbook`. Its exception class for `exception` element\n`GuestbookName` is generated to\n`docroot/WEB-INF/service/com/liferay/docs/guestbook/exception`. Classes that use\nthe exception must import\n`com.liferay.docs.guestbook.exception.GuestbookNameException`. If this upgrade\nis required in your Service Builder project, you must update the references to\nyour portlet's exception classes. \n\nOnce your Service Builder portlet is upgraded,\n[deploy it](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project).\n\n| **Note:** Service Builder portlets automatically migrated to Liferay Workspace\n| using the Upgrade Planner or Blade CLI's `convert` command automatically\n| has its Service Builder logic converted to API and implementation modules.\n| This is a best practice for @product-ver@.\n\nThe portlet is now available on @product@. Congratulations on upgrading a\nportlet that uses Service Builder!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/10-upgrading-frameworks-and-features/05-migrating-off-of-velocity-templates.markdown",
    "content": "---\nheader-id: migrating-off-of-velocity-templates\n---\n\n# Migrating Off of Velocity Templates\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Frameworks and Features</p><p>Step 4 of 4</p>\n</div>\n\nVelocity templates were deprecated in Liferay Portal 7.0 and are now removed in\nfavor of FreeMarker templates in @product-ver@. Below are the key reasons for\nthis move:\n\n- FreeMarker is developed and maintained regularly, while Velocity is no longer\n  actively being developed.\n\n- FreeMarker is faster and supports more sophisticated macros.\n\n- FreeMarker supports using taglibs directly rather than requiring a method to\n  represent them. You can pass body content to them, parameters, etc.\n\nAlthough Velocity templates still work in @product-ver@, we highly recommend\nmigrating to FreeMarker templates. For more information on this topic, see the\n[Upgrading Layout Templates](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-layout-template-to-7-2)\nsection.\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/11-upgrading-portlets/01-intro.markdown",
    "content": "---\nheader-id: upgrading-portlets\n---\n\n# Upgrading Portlets\n\n[TOC levels=1-4]\n\nAll portlet types developed for Liferay Portal 6.x, 7.0, and 7.1 can be upgraded\nand deployed to @product-ver@.\n\nUpgrading most portlets involves these steps:\n\n1.  Adapt the code to @product-ver@'s API\n2.  Resolve dependencies\n\nLiferay's Upgrade Planner helps you adapt your code to @product-ver@'s API.\nThis makes resolving a portlet's dependencies straightforward. In most cases,\nafter you finish the above steps, you can deploy your portlet to @product@.\n\nThe portlet upgrade tutorials show you how to upgrade the following common\nportlets: \n\n-   [GenericPortlet](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-genericportlet)\n-   [Liferay MVC Portlet](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-liferay-mvc-portlet)\n-   [Liferay JSF Portlet](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-liferay-jsf-portlet)\n-   [Servlet-based portlet](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-servlet-based-portlet)\n-   [Spring Portlet MVC](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-spring-portlet-mvc-portlet)\n-   [Struts Portlet](/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-struts-1-portlet)\n\nLet's get your portlet running on @product-ver@!\n\n<a class=\"go-link btn btn-primary\" href=\"/docs/7-2/tutorials/-/knowledge_base/t/upgrading-a-genericportlet\">Let's Go!<span class=\"icon-circle-arrow-right\"></span></a>\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/11-upgrading-portlets/02-upgrading-a-genericportlet.markdown",
    "content": "---\nheader-id: upgrading-a-genericportlet\n---\n\n# Upgrading a GenericPortlet\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Portlets</p><p>Step 1 of 6</p>\n</div>\n\nIt's common to create portlets that extend `javax.portlet.GenericPortlet`. After\nall, `GenericPortlet` provides a default `javax.portlet.Portlet` interface\nimplementation. Upgrading a `GenericPortlet` is straightforward and takes only\ntwo steps: \n\n1.  Adapt the portlet to @product-ver@'s API using the Liferay Upgrade Planner.\n    When running the planner's *Fix Upgrade Problems* step, many of the existing\n    issues are autocorrected. For remaining issues, the planner identifies code\n    affected by the new API and ways to adapt it.\n\n2.  [Resolve its dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n\n3.  [Deploy it](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project)\n\nWhen the portlet WAR file is deployed, @product@'s Plugin Compatibility Layer\nconverts the WAR to a Web Application Bundle (WAB) and installs the portlet as a\nWAB to @product@'s OSGi runtime.\n\nOn deploying an upgraded portlet, the server prints messages that indicate the\nfollowing portlet status:\n\n-   WAR processing\n-   WAB startup\n-   Availability to users\n\nDeploying a portlet produces messages like these:\n\n```bash\n2018-03-21 17:44:59.179 INFO  [com.liferay.portal.kernel.deploy.auto.AutoDeployScanner][AutoDeployDir:262] Processing sample-dao-portlet-7.1.0.1.war\n...\n2018-03-21 17:45:09.959 INFO  [Refresh Thread: Equinox Container: 0012cbb0-7e2c-0018-146e-95a4d71cdf95][PortletHotDeployListener:298] 1 portlet for sample-dao-portlet is available for use \n...\n2018-03-21 17:45:10.151 INFO  [Refresh Thread: Equinox Container: 0012cbb0-7e2c-0018-146e-95a4d71cdf95][BundleStartStopLogger:35] STARTED sample-dao-portlet_7.1.0.1 [655]\n```\n\nThe portlet is now available on @product@.\n\nYou've learned how to upgrade and deploy a portlet that extends\n`GenericPortlet`. You adapt the code, resolve dependencies, and deploy the\nportlet as you always have. It's just that easy!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/11-upgrading-portlets/03-upgrading-a-liferay-mvc-portlet.markdown",
    "content": "---\nheader-id: upgrading-a-liferay-mvc-portlet\n---\n\n# Upgrading a Liferay MVC Portlet\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Portlets</p><p>Step 2 of 6</p>\n</div>\n\nLiferay's MVC Portlet framework is used extensively in @product@'s portlets and\nis a popular choice for portlet developers. The\n[`MVCPortlet`](@platform-ref@/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCPortlet.html)\nclass is a lightweight extension of `javax.portlet.GenericPortlet`. Its `init`\nmethod saves you from writing a lot of boilerplate code. MVC portlets can be\nupgraded to @product-ver@ without a hitch. \n\nUpgrading a Liferay MVC Portlet involves these steps:\n\n1.  Adapt the portlet to @product-ver@'s API using the Liferay Upgrade Planner.\n    When running the planner's *Fix Upgrade Problems* step, many of the existing\n    issues are autocorrected. For remaining issues, the planner identifies code\n    affected by the new API and ways to adapt it.\n\n2.  [Resolve its dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n\n3.  [Deploy it](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project)\n\nAfter deploying the upgraded portlet, the server prints messages that indicate\nthe following portlet status:\n\n-   WAR processing\n-   WAB startup\n-   Availability to users\n\nYou've upgraded and deployed your Liferay MVC Portlet on your @product-ver@\ninstance. Have fun showing off your upgraded portlet!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/11-upgrading-portlets/04-upgrading-a-liferay-jsf-portlet.markdown",
    "content": "---\nheader-id: upgrading-a-liferay-jsf-portlet\n---\n\n# Upgrading a Liferay JSF Portlet\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Portlets</p><p>Step 3 of 6</p>\n</div>\n\nLiferay JSF portlets are easy to upgrade and require few changes. They interface\nwith the [Liferay Faces](/docs/7-2/reference/-/knowledge_base/r/liferay-faces)\nproject, which encapsulates @product@'s Java API and JavaScript code. Because\nof this, upgrading JSF portlets to @product-ver@ requires only updating\ndependencies.\n\nThere are two ways to find a JSF portlet's dependencies for @product-ver@:\n\n- The [http://liferayfaces.org/](http://liferayfaces.org/) home page lets you\n  look up the dependencies (Gradle or Maven) by @product@ version, JSF version,\n  and component suites. \n- The\n  [Liferay Faces Version Scheme](/docs/7-2/reference/-/knowledge_base/r/liferay-faces-version-scheme)\n  article's tables list artifacts by @product@ version, JSF version, portlet\n  version, and AlloyUI and Metal component suite version. \n\nIn this article, you'll upgrade a @product@ JSF portlet's (JSF 2.2) dependencies\nto @product-ver@.\n\n1.  Open your Liferay JSF portlet's build file (e.g., `pom.xml`, `build.gradle`)\n    to where the dependencies are configured.\n\n2.  Navigate to the [http://liferayfaces.org/](http://liferayfaces.org/) site\n    and generate a dependency list by choosing the environment to which you want\n    to upgrade your portlet.\n\n    ![Figure 1: The Liferay Faces site gives you options to generate dependencies for many environments.](../../../images/jsf-dependency-generation.png)\n\n3.  Compare the generated dependencies with your portlet's dependencies and make\n    any necessary updates. For example, in the sample dependencies listed below,\n    the Mojarra dependency and two Liferay Faces dependencies require updating:\n\n    ```xml\n    <dependency>\n        <groupId>org.glassfish</groupId>\n        <artifactId>javax.faces</artifactId>\n        <version>2.2.13</version>\n        <scope>runtime</scope>\n    </dependency>\n    <dependency>\n        <groupId>com.liferay.faces</groupId>\n        <artifactId>com.liferay.faces.bridge.ext</artifactId>\n        <version>3.0.0</version>\n    </dependency>\n    <dependency>\n        <groupId>com.liferay.faces</groupId>\n        <artifactId>com.liferay.faces.bridge.impl</artifactId>\n        <version>4.0.0</version>\n    </dependency>\n    ```\n\n    Using the [http://liferayfaces.org/](http://liferayfaces.org/) dependency\n    list as a guide, these dependencies would be updated to\n\n    ```xml\n    <dependency>\n        <groupId>org.glassfish</groupId>\n        <artifactId>javax.faces</artifactId>\n        <version>2.2.19</version>\n        <scope>runtime</scope>\n    </dependency>\n    <dependency>\n        <groupId>com.liferay.faces</groupId>\n        <artifactId>com.liferay.faces.bridge.ext</artifactId>\n        <version>5.0.4</version>\n    </dependency>\n    <dependency>\n        <groupId>com.liferay.faces</groupId>\n        <artifactId>com.liferay.faces.bridge.impl</artifactId>\n        <version>4.1.3</version>\n    </dependency>\n    ```\n\nOnce your Liferay JSF portlet's dependencies are updated, it's deployable to\n@product-ver@! Follow the\n[Deploying a Project](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project)\narticle for deployment help.\n\nWhen the portlet WAR is deployed, @product@'s Plugin Compatibility Layer\nconverts the WAR to a Web Application Bundle (WAB) and installs the portlet as a\nWAB to @product@'s OSGi runtime. The server prints messages that indicate the\nfollowing portlet status:\n\n- WAR processing\n- WAB startup\n- Availability to users\n\nDeploying a Liferay JSF portlet produces messages like these:\n\n```bash\n13:41:43,690 INFO ... [com.liferay.portal.kernel.deploy.auto.AutoDeployScanner][AutoDeployDir:252] Processing com.liferay.faces.demo.jsf.applicant.portlet-1.0.war\n...\n13:42:03,522 INFO  [fileinstall-C:/liferay-ce-portal-7.2-ga1/osgi/war][BundleStartStopLogger:35] STARTED com.liferay.faces.demo.jsf.applicant.portlet-1.0_4.1.0 [503]\n...\n13:42:05,169 INFO  [fileinstall-C:/liferay-ce-portal-7.2-ga1/osgi/war][PortletHotDeployListener:293] 1 portlet for com.liferay.faces.demo.jsf.applicant.portlet-1.0 is available for use\n```\n\nAfter the portlet deployment is complete, it's available on @product@.\n\nYou've learned how to upgrade and deploy a Liferay JSF portlet. You resolved\ndependencies and deployed the portlet as you always have. It's just that easy!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/11-upgrading-portlets/05-upgrading-a-servlet-based-portlet.markdown",
    "content": "---\nheader-id: upgrading-a-servlet-based-portlet\n---\n\n# Upgrading a Servlet-based Portlet\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Portlets</p><p>Step 4 of 6</p>\n</div>\n\nThis tutorial shows you how to upgrade servlet-based portlets. It refers to\ncode from before and after upgrading a sample servlet-based portlet called\n*Sample JSON* (project `sample-json-portlet`). The portlet shows a *Click me*\nlink. When users click the link, the Liferay logo appears. \n\nFollow these steps to upgrade a servlet-based portlet: \n\n1.  Adapt the portlet to @product-ver@'s API using the Liferay Upgrade Planner.\n    When running the planner's *Fix Upgrade Problems* step, many of the existing\n    issues are autocorrected. For remaining issues, the planner identifies code\n    affected by the new API and ways to adapt it.\n\n2.  [Resolve its dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n\n3.  [Deploy it](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project)\n\nFor an example upgrade scenario, consider this:\n\nSome servlet-based portlets relied on Liferay Portal to provide several\ndependency JAR files. Here's the `portal-dependency-jars` property from a sample\nportlet's `liferay-plugin-package.properties` file: \n\n```properties\nportal-dependency-jars=\\\n    dom4j.jar,\\\n    jabsorb.jar,\\\n    json-java.jar\n```\n\nThis property is deprecated in @product-ver@ because importing and exporting\nJava packages has replaced wholesale use of JARs. This means modules and WABs\ncan import packages without concerning themselves with JARs. @product@ exports\nmany third party packages for plugins to use. Best practices for using packages\nthat @product@ exports are found\n[here](/docs/7-2/customization/-/knowledge_base/c/configuring-dependencies).\n\nOnce you've deployed your portlet, the server prints messages that indicate the\nfollowing portlet status:\n\n-   WAR processing\n-   WAB startup\n-   Availability to users\n\nThe portlet is installed to Liferay's OSGi runtime and is available to users.\n\nCongratulations! You've upgraded and deployed your servlet-based portlet to\n@product-ver@. \n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/11-upgrading-portlets/06-upgrading-a-spring-portlet-mvc-portlet.markdown",
    "content": "---\nheader-id: upgrading-a-spring-portlet-mvc-portlet\n---\n\n# Upgrading a Spring Portlet MVC Portlet\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step\">\n    <p>Upgrading Portlets<br>Step 5 of 6</p>\n</div>\n\nUpgraded portlets that use Spring Portlet MVC should be migrated to use\nPortletMVC4Spring. The main reason is that PortletMVC4Spring is maintained for\ncompatibility with the latest versions of the Spring Framework.\n\n| **Note:** The PortletMVC4Spring project began as Spring Portlet MVC and was\n| part of the [Spring Framework](https://spring.io/projects/spring-framework).\n| When the project was pruned from version 5.0.x of the Spring Framework under\n| [SPR-14129](https://github.com/spring-projects/spring-framework/issues/18701),\n| it became necessary to fork and rename the project. This made it possible to\n| improve and maintain the project for compatibility with the latest versions of\n| the Spring Framework and the Portlet API.\n\n| [Liferay](http://www.liferay.com/) adopted Spring Portlet MVC in March of 2019\n| and the project was renamed to PortletMVC4Spring.\n\nFor more information on PortletMVC4Spring, see its dedicated\n[section of articles](/docs/7-2/appdev/-/knowledge_base/a/portletmvc4spring).\nFor specific information on migrating a portlet using Spring Portlet MVC to\nPortletMVC4Spring, see the\n[Migrating to PortletMVC4Spring](/docs/7-2/appdev/-/knowledge_base/a/migrating-to-portletmvc4spring)\narticle.\n\nOnce you've migrated your portlet to leverage the PortletMVC4Spring framework,\nyou must also adapt your Liferay-specific APIs and dependencies. To do this,\ncomplete the following steps:\n\n1.  Adapt the portlet to @product-ver@'s API using the Liferay Upgrade Planner.\n    When running the planner's *Fix Upgrade Problems* step, many of the existing\n    issues are autocorrected. For remaining issues, the planner identifies code\n    affected by the new API and ways to adapt it.\n\n2.  [Resolve its dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n\n3.  [Deploy it](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project)\n\nAfter deploying the upgraded portlet, the server prints messages that indicate\nthe following portlet status:\n\n-   WAR processing\n-   WAB startup\n-   Availability to users\n\nYou've migrated your Spring Portlet MVC portlet to the updated PortletMVC4Spring\nframework, updated any additional APIs and dependencies, and deployed it to your\n@product-ver@ instance. Your portlet's upgrade process is complete!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/11-upgrading-portlets/07-upgrading-a-struts-1-portlet.markdown",
    "content": "---\nheader-id: upgrading-a-struts-1-portlet\n---\n\n# Upgrading a Struts 1 Portlet\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Portlets</p><p>Step 6 of 6</p>\n</div>\n\nStruts is a stable, widely adopted framework that implements the Model View\nController (MVC) design pattern. If you have a Struts portlet for previous\nversions of Liferay Portal, you can upgrade it to @product-ver@.\n\nUpgrading Struts portlets to @product-ver@ is easier than you might think.\n@product@ lets you continue working with Struts portlets as Java EE web\napplications.\n\nThis tutorial demonstrates how to upgrade a portlet that uses the Struts 1\nFramework.\n\nHere's a sample Struts portlet's folder structure with file/folder descriptions:\n\n-   `sample-struts-portlet`\n    -   `docroot/`\n        -   `html/portlet/sample_struts_portlet/` &rarr; JSPs\n        -   `WEB-INF/`\n            -   `lib/` &rarr; Required third-party libraries unavailable in the @product@ system\n            -   `src/`\n                -   `com/liferay/samplestruts/model/` &rarr; Model classes\n                -   `com/liferay/samplestruts/servlet/` &rarr; Test servlet and servlet context listener\n                -   `com/liferay/samplestruts/struts/`\n                    -   `action/` &rarr; `Action` classes that return View pages to the client\n                    -   `form/` &rarr; `ActionForm` classes for model interaction\n                    -   `render/` &rarr; `Action` classes that present additional pages and handle input\n                    -   `SampleException.java` &rarr; Exception class\n                -   `content/test/` &rarr; Resource bundles\n                -   `META-INF/` &rarr; Javadoc\n            -   `tld/` &rarr; Tag library definitions\n            -   `liferay-display.xml` &rarr; Sets the application category\n            -   `liferay-plugin-package.properties` &rarr; Sets metadata and portal dependencies\n            -   `liferay-portlet.xml` &rarr; Maps descriptive role names to roles \n            -   `liferay-releng.properties` &rarr; (internal) Release properties\n            -   `portlet.xml` &rarr; Defines the portlet and its initialization parameters and security roles\n            -   `struts-config.xml` &rarr; Struts configuration\n            -   `tiles-defs.xml` &rarr; Struts Tile definitions\n            -   `validation.xml` &rarr; Defines form inputs for validation\n            -   `validation-rules.xml` &rarr; Struts validation rules\n            -   `web.xml` &rarr; Web application descriptor\n    -   `build.xml` &rarr; Apache Ant build file\n\nUpgrading a Struts 1 portlet involves these steps:\n\n1.  Adapt the portlet to @product-ver@'s API using the Liferay Upgrade Planner.\n    When running the planner's *Fix Upgrade Problems* step, many of the existing\n    issues are autocorrected. For remaining issues, the planner identifies code\n    affected by the new API and ways to adapt it.\n\n2.  [Resolve its dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n\nYou've resolved the Sample Struts portlet's dependencies. It's ready to deploy. \n\n| **Important**: Setting Portal property `jsp.page.context.force.get.attribute` \n| (described in the\n| [JSP section](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#JSP)) \n| to `true` (default) forces calls to\n| `com.liferay.taglib.servlet.PageContextWrapper#findAttribute(String)` to use\n| `getAttribute(String)`. Although this improves performance by avoiding\n| unnecessary fall-backs, it can cause attribute lookup problems in Struts\n| portlets. To use Struts portlets in your sites, makes sure to set the Portal\n| property `jsp.page.context.force.get.attribute` to `false` in a file\n| `[Liferay-Home]/portal-ext.properties`. \n| \n| ```properties\n| jsp.page.context.force.get.attribute=false\n| ```\n\nOn [deploying](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project) a\nStruts portlet Web Application aRchive (WAR), @product@'s Web Application Bundle\n(WAB) Generator creates an OSGi module (bundle) for the portlet and installs it\nto Liferay's OSGi framework. The server prints messages indicating the following\nportlet status:\n\n-   WAR processing\n-   WAB startup\n-   Availability to users\n\nThe Struts portlet is now available on your @product@ instance. The Struts\nportlet behaves just as it did on previous versions on your @product-ver@ site.\n\nCongratulations on upgrading your Struts portlet to @product-ver@!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/12-upgrading-web-plugins.markdown",
    "content": "---\nheader-id: upgrading-web-plugins\n---\n\n# Upgrading Web Plugins\n\n[TOC levels=1-4]\n\n<div class=\"learn-path-step row\">\n    <p id=\"stepTitle\">Upgrading Web Plugins</p><p>Step 1 of 1</p>\n</div>\n\nWeb plugins are regular\n[Java EE web modules](https://docs.oracle.com/cd/E19226-01/820-7627/bnadx/index.html)\ndesigned to work with @product@. These plugins were stored in the `webs` folder\nof the legacy Plugins SDK.\n\nUpgrading a Liferay web plugin involves these steps:\n\n1.  Adapt the plugin to @product-ver@'s API using the Liferay Upgrade Planner.\n    When running the planner's *Fix Upgrade Problems* step, many of the existing\n    issues are autocorrected. For remaining issues, the planner identifies code\n    affected by the new API and ways to adapt it.\n\n2.  [Resolve its dependencies](/docs/7-2/tutorials/-/knowledge_base/t/resolving-a-projects-dependencies)\n\n3.  [Deploy it](/docs/7-2/reference/-/knowledge_base/r/deploying-a-project)\n\nAfter deploying the upgraded portlet, the server prints messages that indicate\nthe following portlet status:\n\n-   WAR processing\n-   WAB startup\n-   Availability to users\n\nYou've upgraded and deployed your Liferay web plugin on your @product-ver@\ninstance. Great job!\n"
  },
  {
    "path": "en/developer/tutorials/articles/03-upgrading-code-to-liferay-7.2/13-upgrading-ext-plugins.markdown",
    "content": "---\nheader-id: upgrading-ext-plugins\n---\n\n# Upgrading Ext Plugins\n\n[TOC levels=1-4]\n\nExt plugins let you use internal APIs and even let you overwrite @product@ core\nfiles. This puts your deployment at risk of being incompatible with security,\nperformance, or feature updates released by Liferay. When upgrading to a new\nversion of @product@, you must review all changes and manually modify your Ext\nprojects to merge your changes with @product@'s.\n\nDuring your upgrade to @product-ver@, it's highly recommended to leverage an\nextension point to customize @product@ instead of using you existing Ext plugin,\nif possible. @product-ver@ provides many extension points that let you customize\nalmost every detail of @product@. If there's a way to customize what you want\nwith an extension point, do it that way instead. See\n[Finding Extension Points](/docs/7-2/customization/-/knowledge_base/c/finding-extension-points)\nfor more details.\n\nFor more information on Ext projects, how to decide if you need one, and how to\nmanage them, see the\n[Customization with Ext](/docs/7-2/customization/-/knowledge_base/c/customization-with-ext)\nsection.\n"
  },
  {
    "path": "en/developer/tutorials/articles/04-creating-a-theme/01-creating-a-theme-intro.markdown",
    "content": "---\nheader-id: creating-a-theme\n---\n\n# Creating a Theme\n\n[TOC levels=1-4]\n\nThis tutorial takes you step-by-step through the process of creating a theme. \nYou'll create a responsive theme for Liferay's Lunar Resort that demonstrates \nbest practices and uses @product@'s theme tools, extensions, and mechanisms. \nSeveral example files are referenced throughout this tutorial. You can download \nthe [`lunar-resort-theme.zip`](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code) \nif you want to follow along locally. The Lunar Resort theme's files are also \nincluded in the [`lunar-resort-theme`](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme) \nfolder of the Liferay Docs repo, if you would rather view them there.\n\nThis tutorial covers these topics:\n\n- Generating the theme and configuring it to extend the Atlas base theme \n- Customizing the Header and logo\n- Customizing the Header navigation\n- Customizing the Footer and embedding footer navigation\n- Creating a color scheme variant\n\nBy the end of this tutorial, you'll be able to create the theme below:\n\n![Figure 1: The finished Lunar Resort Theme uses @product@'s tools to produce a user-friendly UI that is maintainable.](../../images/theme-tutorial-finished-theme.png)\n"
  },
  {
    "path": "en/developer/tutorials/articles/04-creating-a-theme/02-setting-up-the-theme.markdown",
    "content": "---\nheader-id: setting-up-the-theme\n---\n\n# Setting up the Theme\n\n[TOC levels=1-4]\n\nIn this section, you'll use the Liferay JS Theme Toolkit's Liferay Theme \nGenerator to generate the theme's files. You'll complete these tasks:\n\n- Install the Liferay Theme Generator and its dependencies\n- Generate a theme\n- Configure the theme to extend the [Atlas base theme](/docs/7-2/frameworks/-/knowledge_base/f/customizing-atlas-and-clay-base-themes). \n\nAtlas provides the look of the Classic theme. It builds on the default Clay Base \ntheme and provides additional styles.\n\nFollow these steps to generate and configure the theme:\n\n1.  Install the Theme Generator. Since you're developing a theme for \n    @product-ver@, install v9.x.x if it's not installed already. Run the command \n    below:\n\n    ```bash    \n    npm install -g generator-liferay-theme@9.x.x\n    ```\n\n2.  Install the Yeoman and gulp dependencies:\n\n    ```bash\n    npm install -g yo gulp\n    ```\n    \n3.  Generate the starting theme with the Theme Generator. Enter \n    *Lunar Resort Theme* for the name and *lunar-resort* for the ID, and answer \n    no for the Font Awesome prompt. This theme uses Clay icons instead:\n\n    ```bash\n    yo liferay-theme\n    ```\n\n    ![Figure 1: Answer no for the Font Awesome Prompt](../../images/theme-tutorial-yeoman-prompt.png)\n\n4.  To develop the theme you must copy the default files from the theme's build \n    and modify them. The `/src/css/` folder and `_custom.scss` file are included \n    by default. Run the command below from the theme's root folder to \n    build the files:\n    \n    ```bash\n    gulp build\n    ```\n    \n5.  Create a new `/src/templates/` folder and copy `portal_normal.ftl` from the \n    `build/templates/` folder into it.\n\n6.  Configure the theme to extend the Atlas theme. Add a `clay.scss` file to the \n    theme's `/src/css/` folder and add the import shown below:\n    \n    ```sass\n    @import \"clay/atlas\";\n    ```\n    \n7.  Create an `_imports.scss` file in the `/src/css/` folder and add the imports \n    shown below to it. This includes the default imports and replaces the \n    `clay/base-variables` with the Atlas base variables:\n\n    ```sass\n    @import \"bourbon\";\n\n    @import \"mixins\";\n\n    @import \"compat/mixins\";\n\n    @import \"clay/atlas-variables\";\n    ```\n\nYou've generated the theme, prepared it for development, and configured it to \nextend the Atlas theme. Continue to the next section to build the Lunar Resort's \nHeader and customize the logo. \n"
  },
  {
    "path": "en/developer/tutorials/articles/04-creating-a-theme/03-updating-the-header-and-logo.markdown",
    "content": "---\nheader-id: customizing-the-lunar-resorts-header-and-logo\n---\n\n# Customizing the Lunar Resort's Header and Logo\n\n[TOC levels=1-4]\n\nThe Header contains the navigation and logo for the site. In this section you'll \ncustomize the look and feel of the Header and add a custom logo. \n\nFollow these steps:\n\n1.  Open `portal_normal.ftl` and replace the `<header>...</header>` element and \n    contents with the updated code snippet below. This updates the structure \n    slightly, making the banner expand the full width of the Header, and adds a \n    new `header_css_class` variable to the `class` attribute. This variable is \n    defined in a later step.\n    \n    ```markup\n    <header class=\"${header_css_class}\">\n    \t<div class=\"container-fluid\" id=\"banner\" role=\"banner\">\n    \t\t<a class=\"${logo_css_class}\" href=\"${site_default_url}\" title=\"<@liferay.language_format arguments=\"${site_name}\" key=\"go-to-x\" />\">\n    \t\t\t<img alt=\"${logo_description}\" height=\"${site_logo_height}\" src=\"${site_logo}\" width=\"${site_logo_width}\" />\n    \t\t\t<#if show_site_name>\n    \t\t\t\t${site_name}\n    \t\t\t</#if>\n    \t\t</a>\n\n    \t\t<#if has_navigation>\n    \t\t\t<#include \"${full_templates_path}/navigation.ftl\" />\n    \t\t</#if>\n    \t</div>\n    </header>\n    ```\n\n2.  Replace the `<div class=\"container-fluid\" id=\"wrapper\">` element with the \n    updated code below to remove some margins and padding:\n    \n    ```markup\n    <div class=\"container-fluid mt-0 pt-0 px-0\" id=\"wrapper\">\n    ```\n    \n    And move the wrapper down, and place it directly above the \n    `<section id=\"content\">` element:\n    \n    ```markup\n    <div class=\"container-fluid mt-0 pt-0 px-0\" id=\"wrapper\">\n      <section id=\"content\">\n      ...\n      </section>\n      <footer...>\n      ...\n      </footer>\n    </div>\n    ```\n\n3.  The logo's height is retrieved with the `${site_logo_height}` variable. The \n    height of the logo is a bit too large for the Lunar Resort theme, so you \n    must adjust it. Remove the `width` attribute from the logo's image so it \n    defaults to `auto`:\n    \n    ```markup\n    <img alt=\"${logo_description}\" height=\"${site_logo_height}\" src=\"${site_logo}\" />\n    ```\n    \n4.  Create `init_custom.ftl` in your theme's `/src/templates/` folder and assign \n    the logo's `site_logo_height` variable to the value below:\n    \n    ```\n    <#assign site_logo_height = 56 />\n    ```\n\n5.  Assign the new `header_css_class` variable you added in step one to the \n    value below:\n\n    ```markup\n    <\n    #assign header_css_class = \n    \"navbar navbar-expand-md navbar-dark flex-column flex-md-row bd-navbar\" \n    />\n    ```\n\n    This applies Bootstrap and Clay utility classes to provide the overall look \n    and feel of the Header. Assigning the classes to a variable keeps \n    `portal_normal` clean and makes the code easy to maintain. If you want to \n    update the classes, you just have to modify the variable \n    (e.g. `header_css_class = header_css_class + \" my-new-class\"`).\n\n6.  Add the code snippet below to update the `logo_css_class` variable to use \n    Bootstrap's `navbar-brand` class:\n\n    ```markup\n    <#assign logo_css_class = logo_css_class + \" navbar-brand\" />\n    ```\n\n7.  Before you upload the theme to see what it looks like so far, you must \n    [create a theme thumbnail](/docs/7-2/frameworks/-/knowledge_base/f/creating-a-thumbnail-preview-for-your-theme) \n    so you can identify it. To save time, copy the `thumbnail.png` asset from \n    the [`lunar-resort-build/assets/images/`](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets/images) \n    folder to a new `/src/images/` folder. Note that its dimensions are 480px by \n    270px. These dimensions are required to display the theme thumbnail properly.\n\n8.  The theme isn't complete yet, but you'll deploy what you have so you can \n    replace the default logo with the Lunar Resort logo. Enable [Developer Mode](/docs/7-2/frameworks/-/knowledge_base/f/using-developer-mode-with-themes) \n    before deploying your theme, so the theme's files are not cached for future \n    deployments. Start the server, if it's not already started, and deploy the \n    theme with the command below: \n\n    ```bash\n    gulp deploy\n    ```\n\n9.  Before you configure the pages, you must import the Lunar Resort's pages. \n    Open the Control Menu and navigate to *Publishing* &rarr; *Import*. Click \n    the Plus button to create a new import process. Click *Select File* and \n    import the `lunar_resort_pages.lar` from the [`lunar-resort-build/assets/`](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets) \n    folder. Keep the default settings and click *Import*.\n\n10.  Open the Control Menu and navigate to *Site Builder* &rarr; *Pages*. Click \n     the Gear icon next to *Public Pages* to open the configuration menu. Under \n     the *Look and Feel* tab, scroll down and click the *Change Current Theme* \n     button and select the Lunar Resort Theme. Scroll to the Logo heading, click \n     the *Change* button, upload the `lunar-resort-logo.png` asset from the \n     [`lunar-resort-build/assets/images/`](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets/images) \n     folder, and click the *Save* button to apply the theme and logo.\n    \nGreat! You've customized the Lunar Resort's Header and applied a custom logo. \nNext, you'll configure and customize the theme's navigation. \n"
  },
  {
    "path": "en/developer/tutorials/articles/04-creating-a-theme/04-customizing-the-navigation.markdown",
    "content": "---\nheader-id: customizing-the-navigation\n---\n\n# Customizing the Navigation\n\n[TOC levels=1-4]\n\nNavigation items (pages) are defined and configured in @product@. The Navigation \ntemplate iterates through the existing navigation items (pages) and assigns the \ntemplate's markup for each of them. Page updates therefore require no updates to \nthe theme directly and can be made by a Site Administrator, thus reducing the \nmaintenance costs. \n\nTo customize the navigation, you can either use the default navigation provided \nin `navigation.ftl` and customize the markup template, or you can embed the \nnavigation portlet in the theme and customize its preferences. Both approaches \nuse the same overall markup. This section takes the former approach and \ncustomizes the default configuration in `navigation.ftl`. in the next section, \nyou'll embed the navigation portlet in the Footer and configure its preferences \nto only display the top level (parent) navigation items. \n\nFollow these steps to configure the Header's navigation:\n\n1.  Copy the default `navigation.ftl` file from the `/src/build/templates/` \n    folder into the theme's `/src/templates/` folder. The `build` folder was \n    generated when you built the theme and again when you initially deployed the \n    theme in the last section.\n\n2.  By default, the User Personal Bar is hidden from the theme. You can either \n    enable this via System Settings outside the scope of the theme, or you can \n    include it in your theme. In this case, you'll include it in the theme. Open \n    the `navigation.ftl` template you just copied and add this User Personal Bar \n    markup to the top:\n\n    ```markup\n    <div class=\"mx-1 mx-sm-3 order-md-1 lunar-user\">\n    \t<@liferay.user_personal_bar />\n    </div>\n    ```\n    along with some utility classes to position and order the User Personal Bar, \n    this also adds a custom `lunar-user` class, which you'll use later for \n    styling. \n\n3.  Modify the default template to use Bootstrap's `navbar` format. Wrap the \n    `<nav>...</nav>` element with the `<div>` shown below:\n    \n    ```html\n    <div class=\"collapse navbar-collapse\" id=\"lunarNav\">\n      <nav ... >\n      </nav>\n    </div>\n    ```\n\n4.  Open the `portal_normal.ftl` template and find this conditional wrapper:\n\n    ```markup\n    <#if has_navigation>\n      <#include \"${full_templates_path}/navigation.ftl\" />\n    </#if>\n    ```\n\n    Update the conditional to include the menu toggler for the mobile \n    navigation. This targets the `#lunarNav` wrapper that you added in the \n    previous step:\n\n    ```markup\n    <#if has_navigation>\n      <button \n        aria-controls=\"navigation\" \n        aria-expanded=\"false\" \n        class=\"btn-monospaced ml-auto navbar-toggler\" \n        data-target=\"#lunarNav\" \n        data-toggle=\"collapse\" \n        type=\"button\">\n        <span class=\"navbar-toggler-icon\"></span>\n      </button>\n      <#include \"${full_templates_path}/navigation.ftl\" />\n    </#if>\n    ```\n\n5.  Open `navigation.ftl` and add the `navbar-nav` and `mr-auto` classes to \n    the `<ul>` element at the top: \n\n    ```html\n    <ul aria-label=\"<@liferay.language key=\"site-pages\" />\" class=\"navbar-nav mr-auto\" role=\"menubar\">\n    ```\n\n6.  Open `navigation.ftl` and replace the first `<#assign... />` declaration \n    with the one below. This adds the `nav-item` class to the \n    `nav_item_css_class` variable declaration in `navigation.ftl` and declares a \n    new `nav_item_caret` variable:\n    \n    ```markup\n    <#assign\n      nav_item_attr_has_popup = \"\"\n      nav_item_css_class = \"nav-item\"\n      nav_item_layout = nav_item.getLayout()\n      nav_item_caret = \"\"\n    />\n    ```\n\n7.  Replace the `nav_item.isSelected` conditional block with the one shown \n    below. This adds the `selected` class to the existing `nav_item_css_class` \n    classes:\n    \n    ```markup\n    <#if nav_item.isSelected()>\n      <#assign\n        nav_item_attr_has_popup = \"aria-haspopup='true'\"\n        nav_item_css_class = \"${nav_item_css_class} selected\"\n      />\n    </#if>\n    ```\n    \n8.  The Lunar Resort contains nested pages (child navigation items). By default, \n    child navigation items are displayed at the block level. Instead, the \n    Administrator wants to display these items in a dropdown list that is only \n    displayed on hover of the parent navigation item. Add this conditional block \n    directly below the `nav_item.isSelected` block you just modified. This adds \n    the `dropdown` class to the parent navigation item and updates the \n    `nav_item_caret` variable to hold Clay caret icon markup to indicate the \n    parent navigation has nested child items:\n    \n    ```markup\n    <#if nav_item.hasChildren()>\n      <#assign\n        nav_item_css_class = \"${nav_item_css_class} dropdown\"\n        nav_item_caret = '<svg class=\"lexicon-icon\">\n        <use xlink:href=\"${images_folder}/lexicon/icons.svg#caret-bottom\" />\n        </svg>'\n      />\n    </#if>\n    ```\n\n9.  Locate the anchor's markup below: \n\n    ```markup\n    <a aria-labelledby=\"layout_${nav_item.getLayoutId()}\" \n    ${nav_item_attr_has_popup} \n    href=\"${nav_item.getURL()}\" \n    ${nav_item.getTarget()} \n    role=\"menuitem\"\n    >\n      <span>\n        <@liferay_theme[\"layout-icon\"] layout=nav_item_layout /> \n        ${nav_item.getName()}\n      </span>\n    </a>\n    ```\n\n    Replace it with the updated markup shown below to include the \n    `${nav_item_caret}` variable:\n    \n    ```markup\n    <a \n      aria-labelledby=\"layout_${nav_item.getLayoutId()}\" \n      class=\"nav-link\" ${nav_item_attr_has_popup} \n      href=\"${nav_item.getURL()}\" \n      ${nav_item.getTarget()} \n      role=\"menuitem\"\n    >\n      <span>\n        <@liferay_theme[\"layout-icon\"] layout=nav_item_layout /> \n        ${nav_item.getName()}\n      </span> \n      ${nav_item_caret}\n    </a>\n    ```\n\n10.  Add the `dropdown-menu` class to the `<ul class=\"child-menu\" role=\"menu\">` \n     element and replace the `nav_child_css_class` variable declarations with \n     the ones below to add the `nav-item` class to them:\n     \n     ```markup\n     <#assign\n       nav_child_css_class = \"nav-item\"\n     />\n\n     <#if nav_item.isSelected()>\n       <#assign\n         nav_child_css_class = \"nav-item selected\"\n       />\n     </#if>\n     ```\n\n11.  Find the `<a>` element with the \n     `aria-labelledby=\"layout_${nav_child.getLayoutId()}\"` attribute and add the \n     `class=\"nav-link\"` attribute to it:\n\n    ```markup\n    <a aria-labelledby=\"layout_${nav_child.getLayoutId()}\" class=\"nav-link\"...></a>\n    ```\n\n12.  Add a call to action for the visitors to the Lunar Resort site so they can \n     book their flight. Add the book now button's code below the closing \n     `</nav>` element. This uses some utility classes for the basic look and \n     feel and ordering, as well as a custom `btn-orange` class that you'll \n     provide styling for later:\n\n    ```html\n    <a aria-controls=\"book-now\" class=\"btn text-white btn-orange order-md-2\">\n    \t<p class=\"book-now-text mb-0\">Book Now</p>\n    </a>\n    ```\n    \n13.  The Lunar Resort's color scheme is comprised of three colors: orange, \n     white, and blue. Since these colors are used throughout the theme, you'll \n     store them in SASS variables in a separate file. Create a new file called \n     `_colors.scss` inside the theme's `/src/css/` folder and add these \n     variables to it. Note that White is already defined as the global variable \n     `$white` by the Atlas theme.\n\n     ```scss\n     $lunar-resort-orange: #dfa356;\n     $lunar-resort-blue: #415fa7;\n     $lunar-resort-link-teal: #00ccFF;\n     ```\n\n14.  Now that the main colors are defined, open `/src/css/_custom.scss` and add \n     the code snippet below. This imports the `_colors.scss` file so you can use \n     the variables you just created. It adds some basic styling for the Header \n     and navigation, including a style to highlight the page that is currently \n     active via the `selected` class. It also displays the child menu items at \n     the block level on smaller devices with the \n     `@include media-breakpoint-down` breakpoint:\n    \n     ```scss\n     @import 'colors';\n\n     body {\n      \n       a.btn-orange {\n         background-color: $lunar-resort-orange;\n         margin-right: 5px;\n         \n         &:hover {\n           border-color: $white;\n         }\n         \n         @include media-breakpoint-down(sm){\n           width: 100%;\n         }\n       }\n\n       header {\n         background-color: $lunar-resort-blue;\n         \n         .lunar-user a {\n             color: $lunar-resort-link-teal;\n         }\n\n         .user-avatar-link .lexicon-icon {\n           color: $lunar-resort-blue;\n         }\n        \n         li.nav-item {\n           & a.nav-link span {\n             font-size: 1.5em;\n           }\n\n           &:hover ul.child-menu {\n             background-color: $lunar-resort-blue;\n             display: block;\n             margin-top: -10px;\n           }\n\n           &.selected {\n             background-color: $white;\n             height: 73px;\n             & a.nav-link {\n               color: $lunar-resort-blue;\n               font-weight: bold;\n               &:hover {\n                 color: $lunar-resort-blue;\n                 font-weight: normal;\n                 padding-left: 9.619px;\n                 padding-right: 9.619px;\n               }\n             }\n           }\n          \n           @include media-breakpoint-down(sm){\n             ul.child-menu {\n               display: block;\n             }\n           }    \n         }\n       }\n     }\n     ```\n\n15.  The Control Menu is displayed on top of everything when the user is signed \n     in, which covers the Header. You must update the `navigation.ftl` template \n     to account for the Control Menu. @product@ provides a unique class that is \n     added to the `body` of the page when each product navigation (which \n     includes the Control Menu) is visible. Use the `has-control-menu` class is \n     added to the body when the Control Menu is visible. Open `_custom.scss` and \n     add this code snippet just above the closing bracket for the `body` to add \n     a top margin to the Header that's equal to the height of the Control Menu:\n\n     ```scss\n     &.has-control-menu {\n       header {\n         margin-top: 56px;  \n       }\n     }\n     ```\n\n16.  The Control Menu's height is slightly smaller on mobile devices, so you \n     must account for that responsiveness in your styling. Update the code \n     snippet you just added to match the one below:\n     \n     ```scss\n     &.has-control-menu {\n       header {\n         margin-top: 56px;\n         @include media-breakpoint-down(sm){\n           margin-top: 48px;\n         }  \n       }\n     }\n     ```\n\nGreat! The Header's navigation is customized. The updated Header and logo should \nlook like the figure below:\n\n![Figure 1: The updated Header and navigation are much more user-friendly now.](../../images/theme-tutorial-updated-navigation.png)\n\nNext, you'll define the Footer and embed a navigation portlet to display \nnavigation. \n"
  },
  {
    "path": "en/developer/tutorials/articles/04-creating-a-theme/05-defining-the-footer-and-navigation.markdown",
    "content": "---\nheader-id: defining-the-lunar-resorts-footer-and-footer-navigation\n---\n\n# Defining the Lunar Resort's Footer and Footer Navigation\n\n[TOC levels=1-4]\n\nYou've configured the Header and its navigation, but at the moment the Footer is \na bit bare bones. In this section, you'll update the Footer to include contact \ninformation for the Lunar Resort and include navigation with an embedded \nnavigation portlet. \n\nFollow these steps:\n\n1.  To keep the Portal Normal template uncluttered, create a separate template \n    to hold the Footer's markup. Create a new file called `footer.ftl` in the \n    theme's `/src/templates/` folder.\n    \n2.  Copy the Footer markup (shown below) from `portal_normal.ftl` into \n    `footer.ftl`:\n    \n    ```markup\n    <footer id=\"footer\" role=\"contentinfo\">\n  \t\t<p class=\"powered-by\">\n  \t\t\t<@liferay.language key=\"powered-by\" /> <a href=\"http://www.liferay.com\" rel=\"external\">Liferay</a>\n  \t\t</p>\n  \t</footer>\n    ```\n    \n    And update the `<p>` element in `footer.ftl` to include the classes shown \n    below:\n    \n    ```markup\n    <footer id=\"footer\" role=\"contentinfo\">\n  \t\t<p class=\"powered-by text-center text-white py-3 mb-0\">\n  \t\t\t<@liferay.language key=\"powered-by\" /> <a href=\"http://www.liferay.com\" rel=\"external\">Liferay</a>\n  \t\t</p>\n  \t</footer>\n    ```     \n\n3.  Add this `@liferay.navigation_menu` macro snippet above the `powered-by` \n    paragraph to embed the navigation portlet. This configuration stores the \n    portlet preferences in a `preferencesMap` variable. The `displayDepth` of \n    `1` specifies that the portlet must only render the top-level parent \n    navigation, and `portletSetupPortletDecoratorId` sets the portlet decorator \n    to `barebone`, which removes the portlet's wrapper and only renders the \n    portlet's content:\n\n    ```markup\n    <nav id=\"navbarFooter\">\n    \t<div class=\"text-center mx-auto\">\n    \t\t<div class=\"nav text-uppercase\" role=\"menubar\">\n    \t\t\t<#assign preferencesMap = {\"displayDepth\": \"1\", \"portletSetupPortletDecoratorId\": \"barebone\"} />\n\n    \t\t\t<@liferay.navigation_menu\n    \t\t\t\tdefault_preferences=freeMarkerPortletPreferences.getPreferences(preferencesMap)\n    \t\t\t\tinstance_id=\"footer_navigation_menu\"\n    \t\t\t/>\n    \t\t</div>\n    \t</div>\n    </nav>\n    ```\n\n4.  The visitors need some social media links so they can keep tabs on the \n    latest and greatest news from the Lunar Resort. Replace the snippet you just \n    added with the one below. This uses [Clay icons](https://clayui.com/docs/components/icons.html) \n    and adds a wrapper to prepare for the next step.\n    \n    ```markup\n    <div id=\"navbarContactWrapper\" class=\"row mx-0\">\n      <nav id=\"navbarFooter\" class=\"col-12 col-md-6 pt-5\">\n        <div id=\"socialMediaWrapper\" class=\"col-12 col-md-4 text-center mx-auto mb-4\">\n          <h2 class=\"nav-heading\">\n              Follow Us\n          </h2>\n            <div id=\"socialMediaLinks\">\n              <ul class=\"nav flex-row mx-auto\">\n                  <li class=\"mx-2\">\n                      <div id=\"facebook\"><a class=\"text-white\"\n                      href=\"http://www.facebook.com/pages/Liferay/45119213107\" \n                      target=\"_blank\"><span class=\"hide\">Facebook</span>\n                      <@clay[\"icon\"] symbol=\"social-facebook\" />\n                      </a></div>\n                  </li>\n                  <li class=\"mx-2\">\n                      <div id=\"twitter\"><a class=\"text-white\" \n                      href=\"http://www.twitter.com/liferay\" \n                      target=\"_blank\"><span class=\"hide\">Twitter</span>\n                      <@clay[\"icon\"] symbol=\"twitter\" />\n                      </a></div>\n                  </li>\n                  <li class=\"mx-2\">\n                      <div id=\"linked-in\"><a class=\"text-white\"\n                      href=\"http://www.linkedin.com/company/83609\" \n                      target=\"_blank\"><span class=\"hide\">LinkedIn</span>\n                      <@clay[\"icon\"] symbol=\"social-linkedin\" />\n                      </a></div>\n                  </li>\n                  <li class=\"mx-2\">\n                      <div id=\"youtube\"><a class=\"text-white\"\n                      href=\"http://www.youtube.com/user/liferayinc\" \n                      target=\"_blank\"><span class=\"hide\">YouTube</span>\n                      <@clay[\"icon\"] symbol=\"video\" />\n                      </a></div>\n                  </li>\n              </ul>\n            </div>\n        </div>\n        <div class=\"text-center mx-auto\">\n          <div class=\"nav text-uppercase\" role=\"menubar\">\n            <#assign preferencesMap = {\"displayDepth\": \"1\", \"portletSetupPortletDecoratorId\": \"barebone\"} />\n    \n            <@liferay.navigation_menu\n              default_preferences=freeMarkerPortletPreferences.getPreferences(preferencesMap)\n              instance_id=\"footer_navigation_menu\"\n            />\n          </div>\n        </div>\n      </nav>\n    </div>\n    ```\n\n5.  Add this snippet below the closing `</nav>` tag to add the Lunar Resort's \n    contact information. Also, copy the `lunar-resort-logo-vertical.png` asset \n    from the [`lunar-resort-build/assets/images/`](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets/images) \n    folder to the `/src/images/` folder so you can use it in the Footer:\n\n    ```markup\n    <div class=\"contact-info-container text-center pt-5 pb-2 col-12 col-md-4 mx-auto mb-4\">\n      <img alt=\"lunar-resort-logo\" height=\"90\" class=\"mb-2\" src=\"${images_folder}/lunar-resort-logo-vertical.png\" />\n      <div id=\"contactTextWrapper\" class=\"row mx-0\">\n        <p class=\"col-12 col-md-6\">\n          123 Mare Nectaris Lane<br>\n          Mare Nectaris, Moon Colony 10010<br>\n        </p>\n        <p class=\"col-12 col-md-6\">\n          Tel: 4-919-843-6666<br>\n          Fax: 4-919-843-6667<br>\n          <a href=\"mailto:info@lunarresort.com\">info@thelunarresort.com</a>\n        </p>\n      </div>\n    </div>\n    ```\n  \n6.  The Administrator doesn't want to display the Footer on every page, so she \n    would like the option to hide it. To do that, create a [theme setting](/docs/7-2/frameworks/-/knowledge_base/f/making-configurable-theme-settings) \n    to optionally show the Footer. Open your theme's \n    `/src/WEB-INF/liferay-look-and-feel.xml` file and add this snippet just \n    below the `<template-extension>ftl</template-extension>` entry. This renders \n    a togglable *Show Footer* option in the *Look and Feel* section for the \n    theme's configuration.\n\n    ```xml\n    <settings>\n      <setting configurable=\"true\" key=\"show-footer\" type=\"checkbox\" value=\"true\" />\n    </settings>\n    ```\n\n7.  Now you must define a FreeMarker variable to store the value of the \n    `show-footer` theme setting so you can check for it in `portal_normal.ftl`. \n    Open `init_custom.ftl` and add the variable declaration below to set the \n    `show_footer` variable to the value (true or false) of the `show-footer` \n    theme setting:\n    \n    ```markup\n    <#assign\n      show_footer = getterUtil.getBoolean(themeDisplay.getThemeSetting(\"show-footer\"))\n    />\n    ```\n\n8.  Open `portal_normal.ftl` and replace the Footer markup with the code snippet \n    below to include the Footer template when the `show-footer` theme setting is \n    `true`:\n    \n    ```markup\n    <#if show_footer>\n  \t\t<#include \"${full_templates_path}/footer.ftl\" />\n  \t</#if>\n    ```\n    \n9.  Open `_custom.scss` and add this snippet above the `&.has-control-menu` \n    styling to style the Footer:\n\n    ```scss\n    #footer {\n      background-color: $lunar-resort-blue;\n      color: $white;\n      ul {\n        margin-left: auto;\n        margin-right: auto;\n\n        &.navbar-nav {\n          width: 410px;\n          .nav-item.hover:after {\n            width: auto;\n          }\n          \n          a {\n            color: $white;\n            @include media-breakpoint-down(sm) {\n              padding-left: 6px;\n              padding-right: 6px;\n            }\n          }\n        }\n      }\n      \n      #socialMediaWrapper ul {\n        width: 192px;\n        \n        li a {\n          font-size: 2rem;\n        }\n      }\n      \n      p.powered-by a, .contact-info-container a {\n          color: $lunar-resort-link-teal;\n      }\n\n    }\n    ```\n\n10.  The majority of the Lunar Resort's content is provided with [Fragments](/docs/7-2/frameworks/-/knowledge_base/f/page-fragments). \n     Since Fragments are out of the scope of this tutorial, you'll upload the \n     completed fragments. Open the Control Menu and navigate to *Site Builder* \n     &rarr; *Page Fragments*, and select the *Import* option from the New \n     dropdown menu. Import the `collections-lunar-resort.zip` asset from the \n     [`lunar-resort-build/assets/`](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets) folder.\n    \n11.  Re-deploy the updated theme with the command below:\n\n     ```bash\n     gulp deploy\n     ```\n\nThe updated Footer and navigation should look like the figure below:\n    \n![Figure 1: The updated Footer provides everything visitors need to follow and contact the Lunar Resort.](../../images/theme-tutorial-updated-footer.png)\n\nIn the next section you'll learn how to create a color scheme for the Lunar \nResort. \n"
  },
  {
    "path": "en/developer/tutorials/articles/04-creating-a-theme/06-adding-a-color-scheme.markdown",
    "content": "---\nheader-id: adding-a-color-scheme-variant-for-the-lunar-resort-theme\n---\n\n# Adding a Color Scheme Variant for the Lunar Resort Theme\n\n[TOC levels=1-4]\n\nIn this section, you'll create a color scheme variant for the Lunar Resort Theme \nto apply during the Lunar Eclipse, when special discounts are available. You'll \ncreate a color scheme that reflects the reds and yellows present during a lunar \neclipse. Since the majority of the Lunar Resort Site's content is created with \npage fragments, you must account for the color scheme styles in the page \nfragments as well. Follow these steps:\n\n1.  Open the theme's `WEB-INF/liferay-look-and-feel.xml` file and add these \n    color-scheme entries above the `<portlet-decorator>..</portlet-decorator>` \n    ones:\n\n    ```xml\n    <theme id=\"my-theme-id\" name=\"My Theme Name\">\n      <template-extension>ftl</template-extension>\n      <color-scheme id=\"01\" name=\"Default\">\n      \t\t<default-cs>true</default-cs>\n      \t\t<css-class>default</css-class>\n      \t\t<color-scheme-images-path>\n      \t\t\t\t${images-path}/color_schemes/${css-class}\n      \t\t</color-scheme-images-path>\n      </color-scheme>\n      <color-scheme id=\"02\" name=\"Eclipse\">\n      \t<css-class>eclipse</css-class>\n      </color-scheme>\n      ...\n    </theme>\n    ```\n\n    | **Note:** Color schemes are sorted alphabetically by `name` rather than \n    | `id`. For example, a color scheme named `Clouds` and `id` `02` would be \n    | selected by default over a color scheme named `Day` with `id` `01`. The \n    | `<default-cs>` element overrides the alphabetical sorting and sets the \n    | color scheme that is selected by default when the theme is chosen.  \n\n2.  Open `/src/css/_colors.scss` and update the colors to include the two new \n    ones (eclipse yellow and eclipse red) for the color scheme:\n\n    ```sass    \n    $lunar-resort-orange: #dfa356;\n    $lunar-resort-blue: #415fa7;\n    $lunar-resort-link-teal: #00ccFF;\n    $lunar-resort-eclipse-yellow: #dfd456;\n    $lunar-resort-eclipse-red: #a75441;\n    ```\n\n3.  Create a `/src/css/color_schemes/` folder for the color scheme, and add a \n    `eclipse.scss` file to it for the Eclipse color scheme. The default color \n    scheme's styles are included in `_custom.scss`, so you don't need to create \n    anything for them. \n\n4.  The color scheme's class is added to the `<body>` element when the theme's \n    color scheme is applied, so you must prefix the body styles with the \n    `eclipse` class to target the proper color scheme. Open \n    `/src/css/color_schemes/eclipse.scss` and add this import and styles to it \n    to use the new colors you defined:\n\n    ```sass\n    @import '../colors';\n    \n    body.eclipse {\n      \n      a.btn-orange {\n        background-color: $lunar-resort-eclipse-yellow;\n      }\n    \n      header {\n        background-color: $lunar-resort-eclipse-red;\n    \n        .user-avatar-link .lexicon-icon {\n          color: $lunar-resort-eclipse-red;\n        }\n        \n        li.nav-item {\n          \n          ul.child-menu {\n            background-color: $lunar-resort-eclipse-red;\n          }\n            \n          &:hover ul.child-menu {\n            background-color: $lunar-resort-eclipse-red;\n          }\n          \n          &.selected {\n            & a.nav-link {\n              color: $lunar-resort-eclipse-red;\n              &:hover {\n                color: $lunar-resort-eclipse-red;\n              }\n            }\n          }\n        }\n      }\n    \n      #footer {\n        background-color: $lunar-resort-eclipse-red;\n      }\n    \n    }\n    ```\n\n5.  Import the eclipse color scheme's CSS file into `_custom.scss` so it's \n    loaded with the rest of the custom styles:\n\n    ```sass\n    @import \"color_schemes/eclipse\";\n    ```\n\n6.  You must create thumbnails for each color scheme, just like you did the \n    theme. To save time, copy the \n    [`lunar-resort-build/assets/images/color_schemes/`](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets/images/color_schemes) \n    folder to the theme's \n    `/src/images/` folder. Note that the color scheme folder names match the \n    color scheme CSS class names defined in `liferay-look-and-feel.xml`.\n\n7.  Now that the color scheme is created, you must update the page fragments to \n    use the eclipse color scheme class so they have the same look as the color \n    scheme when it's applied to the page. The fragments don't have access to the \n    SASS color variables, so you must use the hexadecimal color codes. To save \n    time, import the updated page fragments from the \n    `lunar-resort-build/assets/` folder. Open the Control Menu and navigate to \n    *Site Builder* &rarr; *Page Fragments*, click the Actions menu next to \n    COLLECTIONS, and select the *Import* option. Import the \n    `collections-lunar-resort-color-scheme.zip` asset from the \n    [`lunar-resort-build/assets/`](https://github.com/liferay/liferay-docs/tree/master/en/developer/tutorials/code/lunar-resort-theme/lunar-resort-build/assets) \n    folder. Note that each fragment style that requires a color change is \n    duplicated and prefixed with `body.eclipse`. A couple example configurations \n    are shown below:\n    \n    ```css\n    .fragment_35201 h3.text-center {\n      background-color: #dfa356;\n      color: #FFF;\n    }\n\n    body.eclipse .fragment_35201 h3.text-center {\n      background-color: #dfd456;\n    }\n    ```\n\n    ```css\n    .fragment_35201 a.btn {\n      background-color: #415fa7;\n    }\n\n    body.eclipse .fragment_35201 a.btn {\n      background-color: #a75441;\n    }\n    ```\n\n8.  Deploy the theme. Open the Control Menu, navigate to *Site Builder* &rarr; \n    *Pages*, and click the Gear icon next to *Public Pages*. Select the Eclipse \n    color scheme under the *LOOK AND FEEL* tab and save to apply the changes. \n    \n    ![Figure 1: Color schemes are a good way to subtly change the look and feel of your site.](../../images/theme-tutorial-color-schemes.png)\n    \n    The theme should look like the figure below with the Eclipse color scheme \n    applied:\n\n    ![Figure 2: The finished color scheme gives the Lunar Resort site a fiery glow.](../../images/theme-tutorial-eclipse-color-scheme.png)\n\nGreat! You've seen how you can quickly change the look and feel of the Lunar \nResort with just a simple color scheme. Now you know how to develop a theme to \ncustomize the overall look and feel of your site! \n\nSee the [Themes section](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction) \nfor information on developing themes. \n"
  },
  {
    "path": "en/developer/tutorials/build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"tutorials Conversion\" basedir=\".\">\n\t<property name=\"project.dir\" value=\"../../../../liferay-docs\" />\n\n\t<import file=\"../build.xml\" />\n\n\t<property name=\"doc.dir\" value=\"tutorials\"/>\n\t<property name=\"doc.type\" value=\"tutorial\"/>\n\n</project>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/.blade.properties",
    "content": "#Wed Aug 14 16:06:16 EDT 2019\nliferay.version.default=7.2\nprofile.name=gradle\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/.gitignore",
    "content": "**/*.iml\n**/.ivy\n**/.classpath\n**/.project\n**/.sass-cache\n**/.settings\n**/bin\n**/build\n**/build_gradle\n**/node_modules\n**/test-coverage\n**/tmp\n**/.web_bundle_build\n.gradle\n.idea\n/bundles\n/gradle-*.properties\n/plugins-sdk/**/classes\n/plugins-sdk/**/ivy.xml.MD5\n/plugins-sdk/**/liferay-hook.xml.processed\n/plugins-sdk/build.*.properties\n/plugins-sdk/dependencies/**/*.jar\n/plugins-sdk/dist\n/plugins-sdk/lib\ntest-results"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/configs/common/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/configs/dev/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/configs/docker/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/configs/local/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/configs/prod/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "operationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/configs/prod/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/configs/uat/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "logExceptionsOnly=B\"false\"\noperationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/configs/uat/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.2-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/gradle.properties",
    "content": "\n    # Set the directory where the downloaded bundle Zip files are stored. The\n    # default value is the \".liferay/bundles\" folder inside the user home\n    # directory.\n    #\n    #liferay.workspace.bundle.cache.dir=~/.liferay/bundles\n\n    #\n    # Set this to true if the \"liferay.workspace.bundle.url\" property is set to\n    # a DXP bundle Zip. This property allows the token residing in the\n    # \"~/.liferay\" folder to be used to validate your user credentials when\n    # downloading the bundle. The default value is false.\n    #\n    #liferay.workspace.bundle.token.download=false\n\n    #\n    # Set the email address to use when downloading a DXP bundle. This is used\n    # to create the authentication token. The email address must match the one\n    # registered for your DXP subscription.\n    #\n    # If you wish to create a new token without providing your email address and\n    # password in this file, you can create a token manually by navigating to\n    # your Liferay profile's Account Setting page and generating a token in the\n    # Authentication Tokens menu. Your token must reside in the \"~/.liferay\"\n    # folder.\n    #\n    #liferay.workspace.bundle.token.email.address=\n\n    #\n    # Set this to true to override the existing token with a newly generated\n    # token created by the \"createToken\" task. The default value is false.\n    #\n    #liferay.workspace.bundle.token.force=false\n\n    #\n    # Set the password to use when downloading a DXP bundle. This is used to\n    # create the authentication token. The password must match the one\n    # registered for your DXP subscription. See the\n    # \"liferay.workspace.bundle.token.email.address\" property for more details.\n    #\n    #liferay.workspace.bundle.token.password=\n\n    #\n    # Set the file to hold the Liferay bundle authentication token password.\n    # The default file value is \"~/.liferay/token\".\n    #\n    #liferay.workspace.bundle.token.password.file=\n\n    #\n    # Set the URL pointing to the bundle Zip to download. If the URL points to a\n    # DXP bundle (e.g., https://api.liferay.com/...), set the\n    # \"liferay.workspace.bundle.token.download\" property to true. The default\n    # value is the URL for Liferay Portal CE 7.0 GA7, Liferay Portal CE 7.1 GA4,\n    # or Liferay Portal CE 7.2 GA1, depending on the portal version the\n    # workspace is targeting.\n    #\nliferay.workspace.bundle.url = https://releases-cdn.liferay.com/portal/7.2.0-ga1/liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761.tar.gz\n\n    #\n    # Set the \"app.server.tomcat.version\" to match what is contained inside the\n    # Liferay bundle. Both the TestIntegrationPlugin and and LiferayExtPlugin\n    # rely on this version to match the bundled Tomcat version. If your\n    # configured bundle url points to a bundle with a different Tomcat version,\n    # set the property below to match that Tomcat version.\n    #\napp.server.tomcat.version = 9.0.17\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository in the\n    # root project. The default value is true.\n    #\n    #liferay.workspace.default.repository.enabled=true\n\n    #\n    # Set the Liferay Portal Docker image to create your container from. The\n    # default value points to Liferay Portal CE 7.2 GA1.\n    #\n    #liferay.workspace.docker.image.liferay=liferay/portal:7.2.0-ga1\n\n    #\n    # Set the environment with the settings appropriate for current development.\n    # The \"configs\" folder is used to hold different environments in the same\n    # workspace. You can organize environment settings and generate an\n    # environment installation with those settings. There are five\n    # environments: common, dev, docker, local, prod, and uat. The default value\n    # is \"local\".\n    #\n    #liferay.workspace.environment=local\n\n    #\n    # Set the folder that contains the Liferay bundle downloaded from the\n    # \"liferay.workspace.bundle.url\" property. The default value is \"bundles\".\n    #\n    #liferay.workspace.home.dir=bundles\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository for\n    # module/OSGi projects. The default value is true.\n    #\n    #liferay.workspace.modules.default.repository.enabled=true\n\n    #\n    # Set the folder that contains all Ext OSGi modules and Ext plugins. The\n    # default value is \"ext\".\n    #\n    #liferay.workspace.ext.dir=ext\n\n    #\n    # Set the folder that contains all module/OSGi projects. The default value\n    # is \"modules\".\n    #\n    #liferay.workspace.modules.dir=modules\n\n    #\n    # Set this to true to compile the JSP files in OSGi modules and have them\n    # added to the distributable Zip/Tar. The default value is false.\n    #\n    #liferay.workspace.modules.jsp.precompile.enabled=false\n\n    #\n    # Set the folder that contains the Plugins SDK environment. The default\n    # value is \"plugins-sdk\".\n    #\n    #liferay.workspace.plugins.sdk.dir=plugins-sdk\n\n    #\n    # Set the Liferay Portal or DXP version to develop and test against. By\n    # setting this property, it enables the target platform features such as\n    # dependency management and OSGi resolve tasks. Use the version that\n    # matches the Liferay Portal or DXP bundle version in this workspace.\n    #\n    # For a list of all available target platform versions, see\n    # https://bit.ly/2IkAwwW for Liferay Portal and https://bit.ly/2GIyfZF for\n    # Liferay DXP.\n    #\n    #liferay.workspace.target.platform.version=7.2.0\n\n    #\n    # Set this to true if you have enabled the Target Platform plugin (i.e. you\n    # have set the above property) and you want to apply the TargetPlatformIDE\n    # plugin to the root workspace project. This will cause all of the BOM\n    # artifacts jars and their Java sources to be indexed by your IDE. Setting\n    # this property to true can slow down your IDE's project synchronization.\n    #\n    # target.platform.index.sources=false\n\n    #\n    # Set the folder that contains Node.js-style theme projects. The default\n    # value is \"themes\".\n    #\n    #liferay.workspace.themes.dir=themes\n\n    #\n    # Set this to true to build the theme projects using the Liferay Portal\n    # Tools Theme Builder. The default value is false.\n    #\n    #liferay.workspace.themes.java.build=false\n\n    #\n    # Set the folder that contains classic WAR-style projects. The default value\n    # is \"wars\".\n    #\n    #liferay.workspace.wars.dir=wars\n\n\n    #\n    # Set the subscription key for Microsoft Translation integration.\n    # Subscription to the Translator Text Translation API on Microsoft Cognitive\n    # Services is required. Basic subscriptions, up to 2 million characters a\n    # month, are free. See\n    # http://docs.microsofttranslator.com/text-translate.html for more\n    # information.\n    #\nmicrosoft.translator.subscription.key = \nliferay.workspace.target.platform.version = 7.2.0\ntarget.platform.index.sources = false\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/.gitignore",
    "content": ".gradle/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/bnd.bnd",
    "content": "Bundle-Name: guestbook-api\nBundle-SymbolicName: com.liferay.docs.guestbook.api\nBundle-Version: 1.0.0\nExport-Package:\\\n\tcom.liferay.docs.guestbook.exception,\\\n\tcom.liferay.docs.guestbook.model,\\\n\tcom.liferay.docs.guestbook.service,\\\n\tcom.liferay.docs.guestbook.service.persistence\n-check: EXPORTS\n-includeresource: META-INF/service.xml=../guestbook-service/service.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryEmailException extends PortalException {\n\n\tpublic EntryEmailException() {\n\t}\n\n\tpublic EntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryMessageException extends PortalException {\n\n\tpublic EntryMessageException() {\n\t}\n\n\tpublic EntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryNameException extends PortalException {\n\n\tpublic EntryNameException() {\n\t}\n\n\tpublic EntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryEmailException extends PortalException {\n\n\tpublic GuestbookEntryEmailException() {\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryMessageException extends PortalException {\n\n\tpublic GuestbookEntryMessageException() {\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryNameException extends PortalException {\n\n\tpublic GuestbookEntryNameException() {\n\t}\n\n\tpublic GuestbookEntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookNameException extends PortalException {\n\n\tpublic GuestbookNameException() {\n\t}\n\n\tpublic GuestbookNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookEntryException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookEntryException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookEntryException() {\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookException() {\n\t}\n\n\tpublic NoSuchGuestbookException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/Guestbook.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookModel\n * @generated\n */\n@ImplementationClassName(\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\")\n@ProviderType\npublic interface Guestbook extends GuestbookModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<Guestbook, Long> GUESTBOOK_ID_ACCESSOR =\n\t\tnew Accessor<Guestbook, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(Guestbook guestbook) {\n\t\t\t\treturn guestbook.getGuestbookId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Guestbook> getTypeClass() {\n\t\t\t\treturn Guestbook.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntry.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookEntryModel\n * @generated\n */\n@ImplementationClassName(\n\t\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\"\n)\n@ProviderType\npublic interface GuestbookEntry extends GuestbookEntryModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<GuestbookEntry, Long> ENTRY_ID_ACCESSOR =\n\t\tnew Accessor<GuestbookEntry, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(GuestbookEntry guestbookEntry) {\n\t\t\t\treturn guestbookEntry.getEntryId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<GuestbookEntry> getTypeClass() {\n\t\t\t\treturn GuestbookEntry.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryModel\n\textends BaseModel<GuestbookEntry>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook entry model instance should use the {@link GuestbookEntry} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\tpublic long getEntryId();\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\tpublic void setEntryId(long entryId);\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getEmail();\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\tpublic void setEmail(String email);\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getMessage();\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\tpublic void setMessage(String message);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntrySoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookEntryServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntrySoap implements Serializable {\n\n\tpublic static GuestbookEntrySoap toSoapModel(GuestbookEntry model) {\n\t\tGuestbookEntrySoap soapModel = new GuestbookEntrySoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setEntryId(model.getEntryId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setEmail(model.getEmail());\n\t\tsoapModel.setMessage(model.getMessage());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(GuestbookEntry[] models) {\n\t\tGuestbookEntrySoap[] soapModels = new GuestbookEntrySoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[][] toSoapModels(\n\t\tGuestbookEntry[][] models) {\n\n\t\tGuestbookEntrySoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels =\n\t\t\t\tnew GuestbookEntrySoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookEntrySoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(\n\t\tList<GuestbookEntry> models) {\n\n\t\tList<GuestbookEntrySoap> soapModels = new ArrayList<GuestbookEntrySoap>(\n\t\t\tmodels.size());\n\n\t\tfor (GuestbookEntry model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookEntrySoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookEntrySoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetEntryId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic String getEmail() {\n\t\treturn _email;\n\t}\n\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn _message;\n\t}\n\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link GuestbookEntry}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryWrapper\n\textends BaseModelWrapper<GuestbookEntry>\n\timplements GuestbookEntry, ModelWrapper<GuestbookEntry> {\n\n\tpublic GuestbookEntryWrapper(GuestbookEntry guestbookEntry) {\n\t\tsuper(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"entryId\", getEntryId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"email\", getEmail());\n\t\tattributes.put(\"message\", getMessage());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong entryId = (Long)attributes.get(\"entryId\");\n\n\t\tif (entryId != null) {\n\t\t\tsetEntryId(entryId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tString email = (String)attributes.get(\"email\");\n\n\t\tif (email != null) {\n\t\t\tsetEmail(email);\n\t\t}\n\n\t\tString message = (String)attributes.get(\"message\");\n\n\t\tif (message != null) {\n\t\t\tsetMessage(message);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@Override\n\tpublic String getEmail() {\n\t\treturn model.getEmail();\n\t}\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn model.getEntryId();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@Override\n\tpublic String getMessage() {\n\t\treturn model.getMessage();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEmail(String email) {\n\t\tmodel.setEmail(email);\n\t}\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\tmodel.setEntryId(entryId);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\t@Override\n\tpublic void setMessage(String message) {\n\t\tmodel.setMessage(message);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookEntryWrapper wrap(GuestbookEntry guestbookEntry) {\n\t\treturn new GuestbookEntryWrapper(guestbookEntry);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic interface GuestbookModel\n\textends BaseModel<Guestbook>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook model instance should use the {@link Guestbook} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookSoap implements Serializable {\n\n\tpublic static GuestbookSoap toSoapModel(Guestbook model) {\n\t\tGuestbookSoap soapModel = new GuestbookSoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(Guestbook[] models) {\n\t\tGuestbookSoap[] soapModels = new GuestbookSoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[][] toSoapModels(Guestbook[][] models) {\n\t\tGuestbookSoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels = new GuestbookSoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookSoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(List<Guestbook> models) {\n\t\tList<GuestbookSoap> soapModels = new ArrayList<GuestbookSoap>(\n\t\t\tmodels.size());\n\n\t\tfor (Guestbook model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookSoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookSoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetGuestbookId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link Guestbook}.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic class GuestbookWrapper\n\textends BaseModelWrapper<Guestbook>\n\timplements Guestbook, ModelWrapper<Guestbook> {\n\n\tpublic GuestbookWrapper(Guestbook guestbook) {\n\t\tsuper(guestbook);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookWrapper wrap(Guestbook guestbook) {\n\t\treturn new GuestbookWrapper(guestbook);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for GuestbookEntry. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryLocalServiceUtil} to access the guestbook entry local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\tpublic GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId);\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException;\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId);\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows SystemException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> obc);\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\tpublic GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\taddGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().addGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\taddGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\t\tString message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tcreateGuestbookEntry(long entryId) {\n\n\t\treturn getService().createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(\n\t\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntry(long entryId) {\n\n\t\treturn getService().fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tint start, int end) {\n\n\t\treturn getService().getGuestbookEntries(start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId) {\n\n\t\treturn getService().getGuestbookEntries(groupId, guestbookId);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tString uuid, long companyId, int start, int end,\n\t\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int getGuestbookEntriesCount() {\n\t\treturn getService().getGuestbookEntriesCount();\n\t}\n\n\tpublic static int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn getService().getGuestbookEntriesCount(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tupdateGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().updateGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tupdateGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\t\tString email, String message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\tpublic static GuestbookEntryLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryLocalService.class);\n\n\t\tServiceTracker<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryLocalService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryLocalService}.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceWrapper\n\timplements GuestbookEntryLocalService,\n\t\t\t   ServiceWrapper<GuestbookEntryLocalService> {\n\n\tpublic GuestbookEntryLocalServiceWrapper(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry createGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookEntryLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry fetchGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookEntryLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(int start, int end) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(long groupId, long guestbookId) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry getGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntryLocalService getWrappedService() {\n\t\treturn _guestbookEntryLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for GuestbookEntry. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryServiceUtil} to access the guestbook entry remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookEntryService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookEntryService.class);\n\n\t\tServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryService, GuestbookEntryService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookEntryService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryService}.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceWrapper\n\timplements GuestbookEntryService, ServiceWrapper<GuestbookEntryService> {\n\n\tpublic GuestbookEntryServiceWrapper(\n\t\tGuestbookEntryService guestbookEntryService) {\n\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookEntryService getWrappedService() {\n\t\treturn _guestbookEntryService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookEntryService guestbookEntryService) {\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\tprivate GuestbookEntryService _guestbookEntryService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for Guestbook. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookLocalServiceUtil} to access the guestbook local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(Guestbook guestbook);\n\n\tpublic Guestbook addGuestbook(\n\t\t\tlong userId, String name, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId);\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook);\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbook(long guestbookId);\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException;\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(\n\t\tlong groupId, int start, int end, OrderByComparator<Guestbook> obc);\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(Guestbook guestbook);\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().addGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbook(userId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn getService().getGuestbooks(start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn getService().getGuestbooks(groupId);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int getGuestbooksCount() {\n\t\treturn getService().getGuestbooksCount();\n\t}\n\n\tpublic static int getGuestbooksCount(long groupId) {\n\t\treturn getService().getGuestbooksCount(groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().updateGuestbook(guestbook);\n\t}\n\n\tpublic static GuestbookLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookLocalService.class);\n\n\t\tServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookLocalService, GuestbookLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookLocalService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookLocalService}.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceWrapper\n\timplements GuestbookLocalService, ServiceWrapper<GuestbookLocalService> {\n\n\tpublic GuestbookLocalServiceWrapper(\n\t\tGuestbookLocalService guestbookLocalService) {\n\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.addGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.addGuestbook(\n\t\t\tuserId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn _guestbookLocalService.getGuestbooksCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbooksCount(long groupId) {\n\t\treturn _guestbookLocalService.getGuestbooksCount(groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.updateGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic GuestbookLocalService getWrappedService() {\n\t\treturn _guestbookLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookLocalService guestbookLocalService) {\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for Guestbook. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookServiceUtil} to access the guestbook remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookService, GuestbookService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookService.class);\n\n\t\tServiceTracker<GuestbookService, GuestbookService> serviceTracker =\n\t\t\tnew ServiceTracker<GuestbookService, GuestbookService>(\n\t\t\t\tbundle.getBundleContext(), GuestbookService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookService}.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceWrapper\n\timplements GuestbookService, ServiceWrapper<GuestbookService> {\n\n\tpublic GuestbookServiceWrapper(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookService getWrappedService() {\n\t\treturn _guestbookService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\tprivate GuestbookService _guestbookService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryPersistence\n\textends BasePersistence<GuestbookEntry> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryUtil} to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic void removeByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic void cacheResult(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic void cacheResult(java.util.List<GuestbookEntry> guestbookEntries);\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic GuestbookEntry create(long entryId);\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId);\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll();\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook entry service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookEntryPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().clearCache(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, GuestbookEntry> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static GuestbookEntry update(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().update(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static GuestbookEntry update(\n\t\tGuestbookEntry guestbookEntry, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbookEntry, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tentryId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic static GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tentryId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator,\n\t\t\tretrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_PrevAndNext(\n\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic static void removeByG_G(long groupId, long guestbookId) {\n\t\tgetPersistence().removeByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByG_G(long groupId, long guestbookId) {\n\t\treturn getPersistence().countByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic static void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().cacheResult(guestbookEntry);\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic static void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tgetPersistence().cacheResult(guestbookEntries);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static GuestbookEntry create(long entryId) {\n\t\treturn getPersistence().create(entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry remove(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().remove(entryId);\n\t}\n\n\tpublic static GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().updateImpl(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn getPersistence().fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookEntryPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence> _serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryPersistence.class);\n\n\t\tServiceTracker<GuestbookEntryPersistence, GuestbookEntryPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryPersistence.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookPersistence extends BasePersistence<Guestbook> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookUtil} to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(long groupId);\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic void removeByGroupId(long groupId);\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByGroupId(long groupId);\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic void cacheResult(Guestbook guestbook);\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic void cacheResult(java.util.List<Guestbook> guestbooks);\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic Guestbook create(long guestbookId);\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException;\n\n\tpublic Guestbook updateImpl(Guestbook guestbook);\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId);\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll();\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(Guestbook guestbook) {\n\t\tgetPersistence().clearCache(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, Guestbook> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static Guestbook update(Guestbook guestbook) {\n\t\treturn getPersistence().update(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static Guestbook update(\n\t\tGuestbook guestbook, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbook, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tguestbookId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic static Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tguestbookId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(long groupId) {\n\t\treturn getPersistence().findByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn getPersistence().findByGroupId(groupId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_First(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_First(\n\t\t\tgroupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_PrevAndNext(\n\t\t\tguestbookId, groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic static void removeByGroupId(long groupId) {\n\t\tgetPersistence().removeByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByGroupId(long groupId) {\n\t\treturn getPersistence().countByGroupId(groupId);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic static void cacheResult(Guestbook guestbook) {\n\t\tgetPersistence().cacheResult(guestbook);\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic static void cacheResult(List<Guestbook> guestbooks) {\n\t\tgetPersistence().cacheResult(guestbooks);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static Guestbook create(long guestbookId) {\n\t\treturn getPersistence().create(guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook remove(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().remove(guestbookId);\n\t}\n\n\tpublic static Guestbook updateImpl(Guestbook guestbook) {\n\t\treturn getPersistence().updateImpl(guestbook);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn getPersistence().fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic static List<Guestbook> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookPersistence.class);\n\n\t\tServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker<GuestbookPersistence, GuestbookPersistence>(\n\t\t\t\t\tbundle.getBundleContext(), GuestbookPersistence.class,\n\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/bnd.bnd",
    "content": "Bundle-Name: guestbook-service\nBundle-SymbolicName: com.liferay.docs.guestbook.service\nBundle-Version: 1.0.0\nLiferay-Require-SchemaVersion: 1.0.0\nLiferay-Service: true\n-dsannotations-options: inherit\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.lang\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.portal.aop.api\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\tcompileOnly project(\":modules:guestbook:guestbook-api\")\n}\n\nbuildService {\n\tapiDir = \"../guestbook-api/src/main/java\"\n}\n\ngroup = \"com.liferay.docs.guestbook\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/service.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"ds\" package-path=\"com.liferay.docs.guestbook\">\n\t<author>Liferay</author>\n\t<namespace>GB</namespace>\n\t\n\t<entity name=\"Guestbook\" local-service=\"true\" uuid=\"true\" remote-service=\"true\">\n\t\t\n\t\t<!-- Guestbook fields -->\n\n\t\t<column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\t\t\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<finder name=\"GroupId\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"groupId\" />\n\t\t</finder>\n\n\t</entity>\n\t\n    <entity name=\"GuestbookEntry\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n\n\t\t<!-- Guestbook Entry fields -->\n\n\t\t<column name=\"entryId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t<column name=\"email\" type=\"String\" />\n\t\t<column name=\"message\" type=\"String\" />\n\t\t<column name=\"guestbookId\" type=\"long\" />\n\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\t   \n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<order>\n\t\t\t<order-column name=\"createDate\" order-by=\"desc\" />\n\t\t</order>\n\n        <finder name=\"G_G\" return-type=\"Collection\">\n            <finder-column name=\"groupId\" />\n            <finder-column name=\"guestbookId\" />\n        </finder>\n    </entity>\n\n    <exceptions>\n        <exception>GuestbookEntryEmail</exception>\n        <exception>GuestbookEntryMessage</exception>\n        <exception>GuestbookEntryName</exception>\n        <exception>GuestbookName</exception>\n    </exceptions>\n</service-builder>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookBaseImpl\n\textends GuestbookModelImpl implements Guestbook {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookLocalServiceUtil.addGuestbook(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookLocalServiceUtil.updateGuestbook(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing Guestbook in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookCacheModel\n\timplements CacheModel<Guestbook>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookCacheModel guestbookCacheModel = (GuestbookCacheModel)obj;\n\n\t\tif (guestbookId == guestbookCacheModel.guestbookId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, guestbookId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(27);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic Guestbook toEntityModel() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookImpl.setGuestbookId(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tguestbookImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setName(name);\n\t\t}\n\n\t\tguestbookImpl.setGroupId(groupId);\n\t\tguestbookImpl.setCompanyId(companyId);\n\t\tguestbookImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookImpl.setStatus(status);\n\t\tguestbookImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long guestbookId;\n\tpublic String name;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryBaseImpl\n\textends GuestbookEntryModelImpl implements GuestbookEntry {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookEntryLocalServiceUtil.addGuestbookEntry(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookEntryLocalServiceUtil.updateGuestbookEntry(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing GuestbookEntry in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryCacheModel\n\timplements CacheModel<GuestbookEntry>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntryCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\t(GuestbookEntryCacheModel)obj;\n\n\t\tif (entryId == guestbookEntryCacheModel.entryId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, entryId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(33);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", entryId=\");\n\t\tsb.append(entryId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", email=\");\n\t\tsb.append(email);\n\t\tsb.append(\", message=\");\n\t\tsb.append(message);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEntityModel() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookEntryImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookEntryImpl.setEntryId(entryId);\n\n\t\tif (name == null) {\n\t\t\tguestbookEntryImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setName(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tguestbookEntryImpl.setEmail(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setEmail(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tguestbookEntryImpl.setMessage(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setMessage(message);\n\t\t}\n\n\t\tguestbookEntryImpl.setGuestbookId(guestbookId);\n\t\tguestbookEntryImpl.setGroupId(groupId);\n\t\tguestbookEntryImpl.setCompanyId(companyId);\n\t\tguestbookEntryImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookEntryImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookEntryImpl.setStatus(status);\n\t\tguestbookEntryImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookEntryImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tentryId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\t\temail = objectInput.readUTF();\n\t\tmessage = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(entryId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(message);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long entryId;\n\tpublic String name;\n\tpublic String email;\n\tpublic String message;\n\tpublic long guestbookId;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.GuestbookEntry<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryImpl extends GuestbookEntryBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook entry model instance should use the {@link com.liferay.docs.guestbook.model.GuestbookEntry} interface instead.\n\t */\n\tpublic GuestbookEntryImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.GuestbookEntryModel;\nimport com.liferay.docs.guestbook.model.GuestbookEntrySoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.DateUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookEntryModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookEntryModelImpl\n\textends BaseModelImpl<GuestbookEntry> implements GuestbookEntryModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_GuestbookEntry\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"entryId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"email\", Types.VARCHAR},\n\t\t{\"message\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"groupId\", Types.BIGINT}, {\"companyId\", Types.BIGINT},\n\t\t{\"userId\", Types.BIGINT}, {\"userName\", Types.VARCHAR},\n\t\t{\"createDate\", Types.TIMESTAMP}, {\"modifiedDate\", Types.TIMESTAMP},\n\t\t{\"status\", Types.INTEGER}, {\"statusByUserId\", Types.BIGINT},\n\t\t{\"statusByUserName\", Types.VARCHAR}, {\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"entryId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"email\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"message\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_GuestbookEntry (uuid_ VARCHAR(75) null,entryId LONG not null primary key,name VARCHAR(75) null,email VARCHAR(75) null,message VARCHAR(75) null,guestbookId LONG,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_GuestbookEntry\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbookEntry.createDate DESC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_GuestbookEntry.createDate DESC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 4L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 8L;\n\n\tpublic static final long CREATEDATE_COLUMN_BITMASK = 16L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static GuestbookEntry toModel(GuestbookEntrySoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbookEntry model = new GuestbookEntryImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setEntryId(soapModel.getEntryId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setEmail(soapModel.getEmail());\n\t\tmodel.setMessage(soapModel.getMessage());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<GuestbookEntry> toModels(\n\t\tGuestbookEntrySoap[] soapModels) {\n\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> models = new ArrayList<GuestbookEntry>(\n\t\t\tsoapModels.length);\n\n\t\tfor (GuestbookEntrySoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookEntryModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetEntryId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName,\n\t\t\t\tattributeGetterFunction.apply((GuestbookEntry)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<GuestbookEntry, Object>>\n\t\t\tattributeSetterBiConsumers = getAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<GuestbookEntry, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(GuestbookEntry)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<GuestbookEntry, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, GuestbookEntry>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbookEntry.class.getClassLoader(), GuestbookEntry.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<GuestbookEntry> constructor =\n\t\t\t\t(Constructor<GuestbookEntry>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<GuestbookEntry, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<GuestbookEntry, Object>>();\n\t\tMap<String, BiConsumer<GuestbookEntry, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<GuestbookEntry, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", GuestbookEntry::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUuid);\n\t\tattributeGetterFunctions.put(\"entryId\", GuestbookEntry::getEntryId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"entryId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setEntryId);\n\t\tattributeGetterFunctions.put(\"name\", GuestbookEntry::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setName);\n\t\tattributeGetterFunctions.put(\"email\", GuestbookEntry::getEmail);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"email\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setEmail);\n\t\tattributeGetterFunctions.put(\"message\", GuestbookEntry::getMessage);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"message\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setMessage);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"guestbookId\", GuestbookEntry::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"groupId\", GuestbookEntry::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", GuestbookEntry::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", GuestbookEntry::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", GuestbookEntry::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"createDate\", GuestbookEntry::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", GuestbookEntry::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", GuestbookEntry::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\",\n\t\t\t(BiConsumer<GuestbookEntry, Integer>)GuestbookEntry::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", GuestbookEntry::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)\n\t\t\t\tGuestbookEntry::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", GuestbookEntry::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)\n\t\t\t\tGuestbookEntry::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusDate\", GuestbookEntry::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getEmail() {\n\t\tif (_email == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _email;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getMessage() {\n\t\tif (_message == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _message;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_columnBitmask |= GUESTBOOKID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGuestbookId) {\n\t\t\t_setOriginalGuestbookId = true;\n\n\t\t\t_originalGuestbookId = _guestbookId;\n\t\t}\n\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getOriginalGuestbookId() {\n\t\treturn _originalGuestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_columnBitmask = -1L;\n\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), GuestbookEntry.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tguestbookEntryImpl.setUuid(getUuid());\n\t\tguestbookEntryImpl.setEntryId(getEntryId());\n\t\tguestbookEntryImpl.setName(getName());\n\t\tguestbookEntryImpl.setEmail(getEmail());\n\t\tguestbookEntryImpl.setMessage(getMessage());\n\t\tguestbookEntryImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookEntryImpl.setGroupId(getGroupId());\n\t\tguestbookEntryImpl.setCompanyId(getCompanyId());\n\t\tguestbookEntryImpl.setUserId(getUserId());\n\t\tguestbookEntryImpl.setUserName(getUserName());\n\t\tguestbookEntryImpl.setCreateDate(getCreateDate());\n\t\tguestbookEntryImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookEntryImpl.setStatus(getStatus());\n\t\tguestbookEntryImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookEntryImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookEntryImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(GuestbookEntry guestbookEntry) {\n\t\tint value = 0;\n\n\t\tvalue = DateUtil.compareTo(\n\t\t\tgetCreateDate(), guestbookEntry.getCreateDate());\n\n\t\tvalue = value * -1;\n\n\t\tif (value != 0) {\n\t\t\treturn value;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntry)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)obj;\n\n\t\tlong primaryKey = guestbookEntry.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl = this;\n\n\t\tguestbookEntryModelImpl._originalUuid = guestbookEntryModelImpl._uuid;\n\n\t\tguestbookEntryModelImpl._originalGuestbookId =\n\t\t\tguestbookEntryModelImpl._guestbookId;\n\n\t\tguestbookEntryModelImpl._setOriginalGuestbookId = false;\n\n\t\tguestbookEntryModelImpl._originalGroupId =\n\t\t\tguestbookEntryModelImpl._groupId;\n\n\t\tguestbookEntryModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookEntryModelImpl._originalCompanyId =\n\t\t\tguestbookEntryModelImpl._companyId;\n\n\t\tguestbookEntryModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookEntryModelImpl._setModifiedDate = false;\n\n\t\tguestbookEntryModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<GuestbookEntry> toCacheModel() {\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\tnew GuestbookEntryCacheModel();\n\n\t\tguestbookEntryCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookEntryCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.entryId = getEntryId();\n\n\t\tguestbookEntryCacheModel.name = getName();\n\n\t\tString name = guestbookEntryCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.name = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.email = getEmail();\n\n\t\tString email = guestbookEntryCacheModel.email;\n\n\t\tif ((email != null) && (email.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.email = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.message = getMessage();\n\n\t\tString message = guestbookEntryCacheModel.message;\n\n\t\tif ((message != null) && (message.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.message = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookEntryCacheModel.groupId = getGroupId();\n\n\t\tguestbookEntryCacheModel.companyId = getCompanyId();\n\n\t\tguestbookEntryCacheModel.userId = getUserId();\n\n\t\tguestbookEntryCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookEntryCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookEntryCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookEntryCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookEntryCacheModel.status = getStatus();\n\n\t\tguestbookEntryCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookEntryCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookEntryCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookEntryCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookEntryCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, GuestbookEntry>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _originalGuestbookId;\n\tprivate boolean _setOriginalGuestbookId;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate GuestbookEntry _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.Guestbook<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookImpl extends GuestbookBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook model instance should use the {@link com.liferay.docs.guestbook.model.Guestbook} interface instead.\n\t */\n\tpublic GuestbookImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookModel;\nimport com.liferay.docs.guestbook.model.GuestbookSoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookModelImpl\n\textends BaseModelImpl<Guestbook> implements GuestbookModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_Guestbook\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"groupId\", Types.BIGINT},\n\t\t{\"companyId\", Types.BIGINT}, {\"userId\", Types.BIGINT},\n\t\t{\"userName\", Types.VARCHAR}, {\"createDate\", Types.TIMESTAMP},\n\t\t{\"modifiedDate\", Types.TIMESTAMP}, {\"status\", Types.INTEGER},\n\t\t{\"statusByUserId\", Types.BIGINT}, {\"statusByUserName\", Types.VARCHAR},\n\t\t{\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_Guestbook (uuid_ VARCHAR(75) null,guestbookId LONG not null primary key,name VARCHAR(75) null,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_Guestbook\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbook.guestbookId ASC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_Guestbook.guestbookId ASC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 4L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 8L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static Guestbook toModel(GuestbookSoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbook model = new GuestbookImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<Guestbook> toModels(GuestbookSoap[] soapModels) {\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> models = new ArrayList<Guestbook>(soapModels.length);\n\n\t\tfor (GuestbookSoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetGuestbookId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName, attributeGetterFunction.apply((Guestbook)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<Guestbook, Object>> attributeSetterBiConsumers =\n\t\t\tgetAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<Guestbook, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(Guestbook)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<Guestbook, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<Guestbook, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, Guestbook>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbook.class.getClassLoader(), Guestbook.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<Guestbook> constructor =\n\t\t\t\t(Constructor<Guestbook>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<Guestbook, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<Guestbook, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<Guestbook, Object>>();\n\t\tMap<String, BiConsumer<Guestbook, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<Guestbook, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", Guestbook::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\", (BiConsumer<Guestbook, String>)Guestbook::setUuid);\n\t\tattributeGetterFunctions.put(\"guestbookId\", Guestbook::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"name\", Guestbook::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\", (BiConsumer<Guestbook, String>)Guestbook::setName);\n\t\tattributeGetterFunctions.put(\"groupId\", Guestbook::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\", (BiConsumer<Guestbook, Long>)Guestbook::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", Guestbook::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\", (BiConsumer<Guestbook, Long>)Guestbook::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", Guestbook::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\", (BiConsumer<Guestbook, Long>)Guestbook::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", Guestbook::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\", (BiConsumer<Guestbook, String>)Guestbook::setUserName);\n\t\tattributeGetterFunctions.put(\"createDate\", Guestbook::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", Guestbook::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", Guestbook::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\", (BiConsumer<Guestbook, Integer>)Guestbook::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", Guestbook::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", Guestbook::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<Guestbook, String>)Guestbook::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\"statusDate\", Guestbook::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), Guestbook.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic Guestbook toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tguestbookImpl.setUuid(getUuid());\n\t\tguestbookImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookImpl.setName(getName());\n\t\tguestbookImpl.setGroupId(getGroupId());\n\t\tguestbookImpl.setCompanyId(getCompanyId());\n\t\tguestbookImpl.setUserId(getUserId());\n\t\tguestbookImpl.setUserName(getUserName());\n\t\tguestbookImpl.setCreateDate(getCreateDate());\n\t\tguestbookImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookImpl.setStatus(getStatus());\n\t\tguestbookImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(Guestbook guestbook) {\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() < primaryKey) {\n\t\t\treturn -1;\n\t\t}\n\t\telse if (getPrimaryKey() > primaryKey) {\n\t\t\treturn 1;\n\t\t}\n\t\telse {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof Guestbook)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbook guestbook = (Guestbook)obj;\n\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookModelImpl guestbookModelImpl = this;\n\n\t\tguestbookModelImpl._originalUuid = guestbookModelImpl._uuid;\n\n\t\tguestbookModelImpl._originalGroupId = guestbookModelImpl._groupId;\n\n\t\tguestbookModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookModelImpl._originalCompanyId = guestbookModelImpl._companyId;\n\n\t\tguestbookModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookModelImpl._setModifiedDate = false;\n\n\t\tguestbookModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<Guestbook> toCacheModel() {\n\t\tGuestbookCacheModel guestbookCacheModel = new GuestbookCacheModel();\n\n\t\tguestbookCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookCacheModel.name = getName();\n\n\t\tString name = guestbookCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookCacheModel.name = null;\n\t\t}\n\n\t\tguestbookCacheModel.groupId = getGroupId();\n\n\t\tguestbookCacheModel.companyId = getCompanyId();\n\n\t\tguestbookCacheModel.userId = getUserId();\n\n\t\tguestbookCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookCacheModel.status = getStatus();\n\n\t\tguestbookCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, Guestbook>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate Guestbook _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookEntryLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\tguestbookEntry.setNew(true);\n\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.create(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbookEntry.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId) {\n\n\t\treturn guestbookEntryPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookEntryLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbookEntry.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<GuestbookEntry>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(GuestbookEntry guestbookEntry)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbookEntry);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryLocalService.deleteGuestbookEntry(\n\t\t\t(GuestbookEntry)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end) {\n\t\treturn guestbookEntryPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn guestbookEntryPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryLocalService = (GuestbookEntryLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\tprotected GuestbookEntryLocalService guestbookEntryLocalService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl\n * @generated\n */\npublic abstract class GuestbookEntryServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookEntryService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryService = (GuestbookEntryService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookEntryLocalService\n\t\tguestbookEntryLocalService;\n\n\tprotected GuestbookEntryService guestbookEntryService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook addGuestbook(Guestbook guestbook) {\n\t\tguestbook.setNew(true);\n\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.create(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.remove(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.remove(guestbook);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbook.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic Guestbook fetchGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\t\treturn guestbookPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.findByPrimaryKey(guestbookId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\n\t\t\t\"guestbookId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbook.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<Guestbook>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(Guestbook guestbook)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbook);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookLocalService.deleteGuestbook((Guestbook)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn guestbookPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooks(int start, int end) {\n\t\treturn guestbookPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn guestbookPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook updateGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookLocalService = (GuestbookLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\tprotected GuestbookLocalService guestbookLocalService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl\n * @generated\n */\npublic abstract class GuestbookServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookService = (GuestbookService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookLocalService\n\t\tguestbookLocalService;\n\n\tprotected GuestbookService guestbookService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntry</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.Guestbook</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.docs.guestbook.exception.GuestbookEntryEmailException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryMessageException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryNameException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\n\n/**\n * The implementation of the guestbook entry local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\", service = AopService.class)\npublic class GuestbookEntryLocalServiceImpl extends GuestbookEntryLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n\t * via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code\n\t * >.\n\t */\n\n\tpublic GuestbookEntry addGuestbookEntry(long userId, long guestbookId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tlong entryId = counterLocalService.increment();\n\n\t\tGuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\n\t\tentry.setUuid(serviceContext.getUuid());\n\t\tentry.setUserId(userId);\n\t\tentry.setGroupId(groupId);\n\t\tentry.setCompanyId(user.getCompanyId());\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setCreateDate(serviceContext.getCreateDate(now));\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\t\tentry.setGuestbookId(guestbookId);\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Calls to other Liferay frameworks go here\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry updateGuestbookEntry(long userId, long guestbookId,\n\t\t\tlong entryId, String name, String email, String message,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException {\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tentry.setUserId(userId);\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Integrate with Liferay frameworks here.\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry entry)\n\t\tthrows PortalException {\n\n\t\tguestbookEntryPersistence.remove(entry);\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId) throws PortalException {\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\treturn deleteGuestbookEntry(entry);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end) throws SystemException {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end, OrderByComparator<GuestbookEntry> obc) {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend, obc);\n\t}\n\n\tpublic GuestbookEntry getGuestbookEntry(long entryId) throws PortalException {\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.countByG_G(groupId, guestbookId);\n\t}\n\n\tprotected void validate(String name, String email, String entry)\n\t\tthrows PortalException {\n\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookEntryNameException();\n\t\t}\n\n\t\tif (!Validator.isEmailAddress(email)) {\n\t\t\tthrow new GuestbookEntryEmailException();\n\t\t}\n\n\t\tif (Validator.isNull(entry)) {\n\t\t\tthrow new GuestbookEntryMessageException();\n\t\t}\n\t}\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook entry remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookEntryService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=GuestbookEntry\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookEntryServiceImpl extends GuestbookEntryServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> to access the guestbook entry remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.docs.guestbook.exception.GuestbookNameException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.base.GuestbookLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\n\n/**\n * The implementation of the guestbook local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.Guestbook\", service = AopService.class)\npublic class GuestbookLocalServiceImpl extends GuestbookLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code> via\n\t * injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\tpublic Guestbook addGuestbook(long userId, String name,\n\t\t\tServiceContext serviceContext) throws PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name);\n\n\t\tlong guestbookId = counterLocalService.increment();\n\n\t\tGuestbook guestbook = guestbookPersistence.create(guestbookId);\n\n\t\tguestbook.setUuid(serviceContext.getUuid());\n\t\tguestbook.setUserId(userId);\n\t\tguestbook.setGroupId(groupId);\n\t\tguestbook.setCompanyId(user.getCompanyId());\n\t\tguestbook.setUserName(user.getFullName());\n\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tguestbook.setName(name);\n\t\tguestbook.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookPersistence.update(guestbook);\n\n\t\treturn guestbook;\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end,\n\t\t\tOrderByComparator<Guestbook> obc) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end, obc);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end);\n\t}\n\n\tpublic int getGuestbooksCount(long groupId) {\n\n\t\treturn guestbookPersistence.countByGroupId(groupId);\n\t}\n\n\tprotected void validate(String name) throws PortalException {\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookNameException();\n\t\t}\n\t}\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=Guestbook\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookServiceImpl extends GuestbookServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> to access the guestbook remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookEntryPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookEntryPersistence.class)\n@ProviderType\npublic class GuestbookEntryPersistenceImpl\n\textends BasePersistenceImpl<GuestbookEntry>\n\timplements GuestbookEntryPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookEntryUtil</code> to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookEntryImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_First(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_Last(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbookEntry.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof GuestbookEntry) {\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbookEntry.getUuid()) ||\n\t\t\t\t(groupId != guestbookEntry.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<GuestbookEntry> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbookEntry guestbookEntry = list.get(0);\n\n\t\t\t\t\tresult = guestbookEntry;\n\n\t\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (GuestbookEntry)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\t@Override\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbookEntry.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\ttrue);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\tfalse);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_C_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tlong companyId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbookEntry.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByG_G;\n\tprivate FinderPath _finderPathWithoutPaginationFindByG_G;\n\tprivate FinderPath _finderPathCountByG_G;\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(long groupId, long guestbookId) {\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn findByG_G(groupId, guestbookId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {groupId, guestbookId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tgroupId, guestbookId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif ((groupId != guestbookEntry.getGroupId()) ||\n\t\t\t\t\t\t(guestbookId != guestbookEntry.getGuestbookId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByG_G(groupId, guestbookId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByG_G_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tlong guestbookId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(guestbookId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\t@Override\n\tpublic void removeByG_G(long groupId, long guestbookId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByG_G(\n\t\t\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByG_G(long groupId, long guestbookId) {\n\t\tFinderPath finderPath = _finderPathCountByG_G;\n\n\t\tObject[] finderArgs = new Object[] {groupId, guestbookId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_G_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_G_G_GUESTBOOKID_2 =\n\t\t\"guestbookEntry.guestbookId = ?\";\n\n\tpublic GuestbookEntryPersistenceImpl() {\n\t\tsetModelClass(GuestbookEntry.class);\n\n\t\tsetModelImplClass(GuestbookEntryImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\t@Override\n\tpublic void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {\n\t\t\t\tguestbookEntry.getUuid(), guestbookEntry.getGroupId()\n\t\t\t},\n\t\t\tguestbookEntry);\n\n\t\tguestbookEntry.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\t@Override\n\tpublic void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbook entries.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookEntryImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook entry.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(GuestbookEntry guestbookEntry) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookEntryModelImpl)guestbookEntry, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<GuestbookEntry> guestbookEntries) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache(\n\t\t\t\t(GuestbookEntryModelImpl)guestbookEntry, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookEntryModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic GuestbookEntry create(long entryId) {\n\t\tGuestbookEntry guestbookEntry = new GuestbookEntryImpl();\n\n\t\tguestbookEntry.setNew(true);\n\t\tguestbookEntry.setPrimaryKey(entryId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbookEntry.setUuid(uuid);\n\n\t\tguestbookEntry.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn remove((Serializable)entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\tGuestbookEntryImpl.class, primaryKey);\n\n\t\t\tif (guestbookEntry == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbookEntry);\n\t\t}\n\t\tcatch (NoSuchGuestbookEntryException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected GuestbookEntry removeImpl(GuestbookEntry guestbookEntry) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbookEntry)) {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\t\tGuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbookEntry != null) {\n\t\t\t\tsession.delete(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbookEntry != null) {\n\t\t\tclearCache(guestbookEntry);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t@Override\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\tboolean isNew = guestbookEntry.isNew();\n\n\t\tif (!(guestbookEntry instanceof GuestbookEntryModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbookEntry.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(\n\t\t\t\t\tguestbookEntry);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbookEntry proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom GuestbookEntry implementation \" +\n\t\t\t\t\tguestbookEntry.getClass());\n\t\t}\n\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl =\n\t\t\t(GuestbookEntryModelImpl)guestbookEntry;\n\n\t\tif (Validator.isNull(guestbookEntry.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbookEntry.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbookEntry.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookEntryModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setModifiedDate(\n\t\t\t\t\tserviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbookEntry.isNew()) {\n\t\t\t\tsession.save(guestbookEntry);\n\n\t\t\t\tguestbookEntry.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.merge(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByG_G.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry, false);\n\n\t\tclearUniqueFindersCache(guestbookEntryModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookEntryModelImpl);\n\n\t\tguestbookEntry.resetOriginalValues();\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn findByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn fetchByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOKENTRY;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (GuestbookEntry guestbookEntry : findAll()) {\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOKENTRY);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"entryId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOKENTRY;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookEntryModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook entry persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookEntryModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookEntryModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.COMPANYID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GUESTBOOKID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookEntryImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.GuestbookEntry\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbookEntry.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No GuestbookEntry exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No GuestbookEntry exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookEntryPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.impl.GuestbookImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookPersistence.class)\n@ProviderType\npublic class GuestbookPersistenceImpl\n\textends BasePersistenceImpl<Guestbook> implements GuestbookPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookUtil</code> to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_First(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_Last(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbook.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbook == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof Guestbook) {\n\t\t\tGuestbook guestbook = (Guestbook)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbook.getUuid()) ||\n\t\t\t\t(groupId != guestbook.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<Guestbook> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbook guestbook = list.get(0);\n\n\t\t\t\t\tresult = guestbook;\n\n\t\t\t\t\tcacheResult(guestbook);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (Guestbook)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbook);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbook.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_C_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbook.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByGroupId;\n\tprivate FinderPath _finderPathWithoutPaginationFindByGroupId;\n\tprivate FinderPath _finderPathCountByGroupId;\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId) {\n\t\treturn findByGroupId(\n\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId, int start, int end) {\n\t\treturn findByGroupId(groupId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByGroupId(groupId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif ((groupId != guestbook.getGroupId())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_First(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByGroupId(groupId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_Last(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByGroupId(groupId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByGroupId(\n\t\t\tgroupId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByGroupId_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\t@Override\n\tpublic void removeByGroupId(long groupId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByGroupId(\n\t\t\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByGroupId(long groupId) {\n\t\tFinderPath finderPath = _finderPathCountByGroupId;\n\n\t\tObject[] finderArgs = new Object[] {groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_GROUPID_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tpublic GuestbookPersistenceImpl() {\n\t\tsetModelClass(Guestbook.class);\n\n\t\tsetModelImplClass(GuestbookImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\t@Override\n\tpublic void cacheResult(Guestbook guestbook) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {guestbook.getUuid(), guestbook.getGroupId()},\n\t\t\tguestbook);\n\n\t\tguestbook.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\t@Override\n\tpublic void cacheResult(List<Guestbook> guestbooks) {\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\t\tguestbook.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbook);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbooks.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(Guestbook guestbook) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<Guestbook> guestbooks) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\tguestbook.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic Guestbook create(long guestbookId) {\n\t\tGuestbook guestbook = new GuestbookImpl();\n\n\t\tguestbook.setNew(true);\n\t\tguestbook.setPrimaryKey(guestbookId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbook.setUuid(uuid);\n\n\t\tguestbook.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException {\n\t\treturn remove((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook guestbook = (Guestbook)session.get(\n\t\t\t\tGuestbookImpl.class, primaryKey);\n\n\t\t\tif (guestbook == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbook);\n\t\t}\n\t\tcatch (NoSuchGuestbookException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected Guestbook removeImpl(Guestbook guestbook) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbook)) {\n\t\t\t\tguestbook = (Guestbook)session.get(\n\t\t\t\t\tGuestbookImpl.class, guestbook.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbook != null) {\n\t\t\t\tsession.delete(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbook != null) {\n\t\t\tclearCache(guestbook);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t@Override\n\tpublic Guestbook updateImpl(Guestbook guestbook) {\n\t\tboolean isNew = guestbook.isNew();\n\n\t\tif (!(guestbook instanceof GuestbookModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbook.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(guestbook);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbook proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom Guestbook implementation \" +\n\t\t\t\t\tguestbook.getClass());\n\t\t}\n\n\t\tGuestbookModelImpl guestbookModelImpl = (GuestbookModelImpl)guestbook;\n\n\t\tif (Validator.isNull(guestbook.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbook.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbook.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbook.isNew()) {\n\t\t\t\tsession.save(guestbook);\n\n\t\t\t\tguestbook.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook = (Guestbook)session.merge(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getUuid(),\n\t\t\t\t\tguestbookModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByGroupId.\n\t\t\t\t\t getColumnBitmask()) != 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook, false);\n\n\t\tclearUniqueFindersCache(guestbookModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookModelImpl);\n\n\t\tguestbook.resetOriginalValues();\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbook == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\treturn findByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn fetchByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOK);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOK;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (Guestbook guestbook : findAll()) {\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOK);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"guestbookId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOK;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.COMPANYID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {Long.class.getName()},\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByGroupId\",\n\t\t\tnew String[] {Long.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.Guestbook\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK =\n\t\t\"SELECT guestbook FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK_WHERE =\n\t\t\"SELECT guestbook FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK_WHERE =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbook.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No Guestbook exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No Guestbook exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/constants/GBPersistenceConstants.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl.constants;\n\nimport com.liferay.petra.string.StringBundler;\n\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.Constants;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * @author Liferay\n * @generated\n */\n@Component(immediate = true, service = {})\npublic class GBPersistenceConstants {\n\n\tpublic static final String BUNDLE_SYMBOLIC_NAME =\n\t\t\"com.liferay.docs.guestbook.service\";\n\n\tpublic static final String ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER =\n\t\t\"(origin.bundle.symbolic.name=\" + BUNDLE_SYMBOLIC_NAME + \")\";\n\n\t@Activate\n\tprotected void activate(BundleContext bundleContext) {\n\t\tBundle bundle = bundleContext.getBundle();\n\n\t\tif (!BUNDLE_SYMBOLIC_NAME.equals(bundle.getSymbolicName())) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\tStringBundler.concat(\n\t\t\t\t\t\"Incorrect \", Constants.BUNDLE_SYMBOLICNAME, \" for bundle \",\n\t\t\t\t\tbundle.getSymbolicName()));\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/module-hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">\n\n<hibernate-mapping auto-import=\"false\" default-lazy=\"false\">\n\t<import class=\"com.liferay.docs.guestbook.model.Guestbook\" />\n\t<import class=\"com.liferay.docs.guestbook.model.GuestbookEntry\" />\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\" table=\"GB_Guestbook\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\" table=\"GB_GuestbookEntry\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"entryId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"email\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"message\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n</hibernate-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/portlet-model-hints.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<model-hints>\n\t<model name=\"com.liferay.docs.guestbook.model.Guestbook\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n\t<model name=\"com.liferay.docs.guestbook.model.GuestbookEntry\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"entryId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"email\" type=\"String\" />\n\t\t<field name=\"message\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n</model-hints>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/indexes.sql",
    "content": "create index IX_9294AD47 on GB_Guestbook (groupId);\ncreate index IX_9314A9F7 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_EDD4239 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], groupId);\n\ncreate index IX_E84D72FD on GB_GuestbookEntry (groupId, guestbookId);\ncreate index IX_CC265FEF on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_4A541631 on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], groupId);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/sequences.sql",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/tables.sql",
    "content": "create table GB_Guestbook (\n\tuuid_ VARCHAR(75) null,\n\tguestbookId LONG not null primary key,\n\tname VARCHAR(75) null,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);\n\ncreate table GB_GuestbookEntry (\n\tuuid_ VARCHAR(75) null,\n\tentryId LONG not null primary key,\n\tname VARCHAR(75) null,\n\temail VARCHAR(75) null,\n\tmessage VARCHAR(75) null,\n\tguestbookId LONG,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/service.properties",
    "content": "##\n## Properties Override\n##\n\n    #\n    # Specify where to get the overridden properties. Updates should not be made\n    # on this file but on the overridden version of this file.\n    #\n    include-and-override=service-ext.properties\n\n##\n## Build\n##\n\n    build.namespace=GB\n    build.number=4\n    build.date=1565898054827"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/.gitignore",
    "content": ".gradle/\n.sass-cache/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/bnd.bnd",
    "content": "Bundle-Name: guestbook-web\nBundle-SymbolicName: com.liferay.docs.guestbook.portlet\nBundle-Version: 1.0.0\nExport-Package: com.liferay.docs.guestbook.portlet.constants"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\"\n\tcompileOnly group: \"javax.portlet\", name: \"portlet-api\"\n\tcompileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n\tcompileOnly group: \"jstl\", name: \"jstl\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\n    compileOnly project(\":modules:guestbook:guestbook-api\")\n    compileOnly project(\":modules:guestbook:guestbook-service\")\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/constants/GuestbookPortletKeys.java",
    "content": "package com.liferay.docs.guestbook.constants;\n\n/**\n * @author sezovr\n */\npublic class GuestbookPortletKeys {\n\n\tpublic static final String GUESTBOOK =\n\t\t\"com_liferay_docs_guestbook_portlet_GuestbookPortlet\";\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\nimport javax.portlet.PortletException;\nimport javax.portlet.RenderRequest;\nimport javax.portlet.RenderResponse;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.servlet.SessionErrors;\nimport com.liferay.portal.kernel.servlet.SessionMessages;\nimport com.liferay.portal.kernel.util.ParamUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\n/**\n * @author sezovr\n */\n@Component(immediate = true, property = { \"com.liferay.portlet.display-category=category.social\",\n\t\t\"com.liferay.portlet.header-portlet-css=/css/main.css\", \n\t\t\"com.liferay.portlet.instanceable=false\",\n\t\t\"com.liferay.portlet.scopeable=true\", \n\t\t\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK,\n\t\t\"javax.portlet.display-name=Guestbook\", \n\t\t\"javax.portlet.expiration-cache=0\",\n\t\t\"javax.portlet.init-param.template-path=/\", \n\t\t\"javax.portlet.init-param.view-template=/guestbook/view.jsp\",\n\t\t\"javax.portlet.resource-bundle=content.Language\", \n\t\t\"javax.portlet.security-role-ref=power-user,user\",\n\t\t\"javax.portlet.supports.mime-type=text/html\" }, \n\tservice = Portlet.class)\npublic class GuestbookPortlet extends MVCPortlet {\n\n\tpublic void addEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\n\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(GuestbookEntry.class.getName(), request);\n\n\t\tString userName = ParamUtil.getString(request, \"name\");\n\t\tString email = ParamUtil.getString(request, \"email\");\n\t\tString message = ParamUtil.getString(request, \"message\");\n\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\n\t\tif (entryId > 0) {\n\n\t\t\ttry {\n\n\t\t\t\t_guestbookEntryLocalService.updateGuestbookEntry(serviceContext.getUserId(), guestbookId, entryId, userName,\n\t\t\t\t\t\temail, message, serviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(e);\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\n\t\t} else {\n\n\t\t\ttry {\n\t\t\t\t_guestbookEntryLocalService.addGuestbookEntry(serviceContext.getUserId(), guestbookId, userName, email, message,\n\t\t\t\t\t\tserviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(e);\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void deleteEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\t\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\t\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\tGuestbookEntry.class.getName(), request);\n\n\t\t\ttry {\n\n\t\t\t\tresponse.setRenderParameter(\n\t\t\t\t\t\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t\t_guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\t\t\t}\n\n\t\t\tcatch (Exception e) {\n\t\t\t\tLogger.getLogger(GuestbookPortlet.class.getName()).log(\n\t\t\t\t\tLevel.SEVERE, null, e);\n\t\t\t}\n\t}\n\n\t@Override\n\tpublic void render(RenderRequest renderRequest, RenderResponse renderResponse)\n\t\t\tthrows IOException, PortletException {\n\n\t\t\ttry {\n\t\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\t\tGuestbook.class.getName(), renderRequest);\n\n\t\t\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\t\t\tlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n\t\t\t\tList<Guestbook> guestbooks = _guestbookLocalService.getGuestbooks(\n\t\t\t\t\tgroupId);\n\n\t\t\t\tif (guestbooks.isEmpty()) {\n\t\t\t\t\tGuestbook guestbook = _guestbookLocalService.addGuestbook(\n\t\t\t\t\t\tserviceContext.getUserId(), \"Main\", serviceContext);\n\n\t\t\t\t\tguestbookId = guestbook.getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\tif (guestbookId == 0) {\n\t\t\t\t\tguestbookId = guestbooks.get(0).getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\trenderRequest.setAttribute(\"guestbookId\", guestbookId);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new PortletException(e);\n\t\t\t}\n\n\t\t\tsuper.render(renderRequest, renderResponse);\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/css/main.scss",
    "content": ".guestbook-web {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/edit_entry.jsp",
    "content": "<%@include file=\"../init.jsp\" %>\n\n<% \n\nlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\nGuestbookEntry entry = null;\nif (entryId > 0) {\n  entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n}\n\nlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n%>\n\n<portlet:renderURL var=\"viewURL\">\n\t<portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\"></portlet:param>\n</portlet:renderURL>\n\n<portlet:actionURL name=\"addEntry\" var=\"addEntryURL\"></portlet:actionURL>\n\n<aui:form action=\"<%= addEntryURL %>\" name=\"<portlet:namespace />fm\">\n\n<aui:model-context bean=\"<%= entry %>\" model=\"<%= GuestbookEntry.class %>\" />\n\n\t<aui:fieldset>\n\n\t\t<aui:input name=\"name\" />\n\t\t<aui:input name=\"email\" />\n\t\t<aui:input name=\"message\" />\n\t\t<aui:input name=\"entryId\" type=\"hidden\" />\n\t\t<aui:input name=\"guestbookId\" type=\"hidden\" value='<%= entry == null ? guestbookId : entry.getGuestbookId() %>'/>\n\n\t</aui:fieldset>\n\n\t<aui:button-row>\n\n\t\t<aui:button type=\"submit\"></aui:button>\n\t\t<aui:button type=\"cancel\" onClick=\"<%= viewURL.toString() %>\"></aui:button>\n\n\t</aui:button-row>\n</aui:form>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/entry_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n        <%\n        String mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n        ResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);\n\n        GuestbookEntry entry = (GuestbookEntry)row.getObject(); \n        %>\n\n        <liferay-ui:icon-menu>\n\n                <portlet:renderURL var=\"editURL\">\n                    <portlet:param name=\"entryId\"\n                        value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n                    <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n                </portlet:renderURL>\n\n                <liferay-ui:icon image=\"edit\" message=\"Edit\"\n                    url=\"<%=editURL.toString() %>\" />\n\n                <portlet:actionURL name=\"deleteEntry\" var=\"deleteURL\">\n                    <portlet:param name=\"entryId\"\n                        value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n                    <portlet:param name=\"guestbookId\"\n                        value=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n                </portlet:actionURL>\n\n                <liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n        </liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\nlong guestbookId = Long.valueOf((Long) renderRequest\n\t\t.getAttribute(\"guestbookId\"));\n%>\n\n<aui:button-row cssClass=\"guestbook-buttons\">\n\n\t<portlet:renderURL var=\"addEntryURL\">\n\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(guestbookId)%>\" />\n\t</portlet:renderURL>\n\n\t<aui:button onClick=\"<%=addEntryURL.toString()%>\" value=\"Add Entry\"></aui:button>\n\n</aui:button-row>\n\n<liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntriesCount()%>\">\n<liferay-ui:search-container-results\n\tresults=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(scopeGroupId.longValue(),\n\t\t\t\t\tguestbookId, searchContainer.getStart(),\n\t\t\t\t\tsearchContainer.getEnd())%>\" />\n\n<liferay-ui:search-container-row\n\tclassName=\"com.liferay.docs.guestbook.model.GuestbookEntry\" modelVar=\"entry\">\n\n\t<liferay-ui:search-container-column-text property=\"message\" />\n\n\t<liferay-ui:search-container-column-text property=\"name\" />\n\n\t<liferay-ui:search-container-column-jsp\n\t\talign=\"right\" \n\t\tpath=\"/guestbook/entry_actions.jsp\" />\n\n</liferay-ui:search-container-row>\n\n<liferay-ui:search-iterator />\n\n</liferay-ui:search-container>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/init.jsp",
    "content": "<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\"%>\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\"%>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n<%@ taglib uri=\"http://liferay.com/tld/security\" prefix=\"liferay-security\" %>\n\n<%@ page import=\"java.util.List\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.ParamUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.HtmlUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.petra.string.StringPool\" %>\n<%@ page import=\"com.liferay.portal.kernel.model.PersistedModel\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.SearchEntry\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.ResultRow\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.Guestbook\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.GuestbookEntry\" %> \n\n<liferay-theme:defineObjects />\n\n<portlet:defineObjects />"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/content/Language.properties",
    "content": "javax.portlet.title.com_liferay_docs_guestbook_portlet_GuestbookPortlet=Guestbook\nguestbook.caption=Hello from Guestbook!"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/04-web-front-end/com-liferay-docs-guestbook/settings.gradle",
    "content": "buildscript {\n\tdependencies {\n\t\tclasspath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.workspace\", version: \"2.0.4\"\n\t\tclasspath group: \"net.saliman\", name: \"gradle-properties-plugin\", version: \"1.4.6\"\n\t}\n\n\trepositories {\n\t\tmaven {\n\t\t\turl \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t\t}\n\t}\n}\n\napply plugin: \"net.saliman.properties\"\n\napply plugin: \"com.liferay.workspace\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/.blade.properties",
    "content": "#Wed Aug 14 16:06:16 EDT 2019\nliferay.version.default=7.2\nprofile.name=gradle\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/.gitignore",
    "content": "**/*.iml\n**/.ivy\n**/.classpath\n**/.project\n**/.sass-cache\n**/.settings\n**/bin\n**/build\n**/build_gradle\n**/node_modules\n**/test-coverage\n**/tmp\n**/.web_bundle_build\n.gradle\n.idea\n/bundles\n/gradle-*.properties\n/plugins-sdk/**/classes\n/plugins-sdk/**/ivy.xml.MD5\n/plugins-sdk/**/liferay-hook.xml.processed\n/plugins-sdk/build.*.properties\n/plugins-sdk/dependencies/**/*.jar\n/plugins-sdk/dist\n/plugins-sdk/lib\ntest-results"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/configs/common/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/configs/dev/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/configs/docker/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/configs/local/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/configs/prod/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "operationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/configs/prod/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/configs/uat/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "logExceptionsOnly=B\"false\"\noperationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/configs/uat/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.2-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/gradle.properties",
    "content": "\n    # Set the directory where the downloaded bundle Zip files are stored. The\n    # default value is the \".liferay/bundles\" folder inside the user home\n    # directory.\n    #\n    #liferay.workspace.bundle.cache.dir=~/.liferay/bundles\n\n    #\n    # Set this to true if the \"liferay.workspace.bundle.url\" property is set to\n    # a DXP bundle Zip. This property allows the token residing in the\n    # \"~/.liferay\" folder to be used to validate your user credentials when\n    # downloading the bundle. The default value is false.\n    #\n    #liferay.workspace.bundle.token.download=false\n\n    #\n    # Set the email address to use when downloading a DXP bundle. This is used\n    # to create the authentication token. The email address must match the one\n    # registered for your DXP subscription.\n    #\n    # If you wish to create a new token without providing your email address and\n    # password in this file, you can create a token manually by navigating to\n    # your Liferay profile's Account Setting page and generating a token in the\n    # Authentication Tokens menu. Your token must reside in the \"~/.liferay\"\n    # folder.\n    #\n    #liferay.workspace.bundle.token.email.address=\n\n    #\n    # Set this to true to override the existing token with a newly generated\n    # token created by the \"createToken\" task. The default value is false.\n    #\n    #liferay.workspace.bundle.token.force=false\n\n    #\n    # Set the password to use when downloading a DXP bundle. This is used to\n    # create the authentication token. The password must match the one\n    # registered for your DXP subscription. See the\n    # \"liferay.workspace.bundle.token.email.address\" property for more details.\n    #\n    #liferay.workspace.bundle.token.password=\n\n    #\n    # Set the file to hold the Liferay bundle authentication token password.\n    # The default file value is \"~/.liferay/token\".\n    #\n    #liferay.workspace.bundle.token.password.file=\n\n    #\n    # Set the URL pointing to the bundle Zip to download. If the URL points to a\n    # DXP bundle (e.g., https://api.liferay.com/...), set the\n    # \"liferay.workspace.bundle.token.download\" property to true. The default\n    # value is the URL for Liferay Portal CE 7.0 GA7, Liferay Portal CE 7.1 GA4,\n    # or Liferay Portal CE 7.2 GA1, depending on the portal version the\n    # workspace is targeting.\n    #\nliferay.workspace.bundle.url = https://releases-cdn.liferay.com/portal/7.2.0-ga1/liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761.tar.gz\n\n    #\n    # Set the \"app.server.tomcat.version\" to match what is contained inside the\n    # Liferay bundle. Both the TestIntegrationPlugin and and LiferayExtPlugin\n    # rely on this version to match the bundled Tomcat version. If your\n    # configured bundle url points to a bundle with a different Tomcat version,\n    # set the property below to match that Tomcat version.\n    #\napp.server.tomcat.version = 9.0.17\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository in the\n    # root project. The default value is true.\n    #\n    #liferay.workspace.default.repository.enabled=true\n\n    #\n    # Set the Liferay Portal Docker image to create your container from. The\n    # default value points to Liferay Portal CE 7.2 GA1.\n    #\n    #liferay.workspace.docker.image.liferay=liferay/portal:7.2.0-ga1\n\n    #\n    # Set the environment with the settings appropriate for current development.\n    # The \"configs\" folder is used to hold different environments in the same\n    # workspace. You can organize environment settings and generate an\n    # environment installation with those settings. There are five\n    # environments: common, dev, docker, local, prod, and uat. The default value\n    # is \"local\".\n    #\n    #liferay.workspace.environment=local\n\n    #\n    # Set the folder that contains the Liferay bundle downloaded from the\n    # \"liferay.workspace.bundle.url\" property. The default value is \"bundles\".\n    #\n    #liferay.workspace.home.dir=bundles\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository for\n    # module/OSGi projects. The default value is true.\n    #\n    #liferay.workspace.modules.default.repository.enabled=true\n\n    #\n    # Set the folder that contains all Ext OSGi modules and Ext plugins. The\n    # default value is \"ext\".\n    #\n    #liferay.workspace.ext.dir=ext\n\n    #\n    # Set the folder that contains all module/OSGi projects. The default value\n    # is \"modules\".\n    #\n    #liferay.workspace.modules.dir=modules\n\n    #\n    # Set this to true to compile the JSP files in OSGi modules and have them\n    # added to the distributable Zip/Tar. The default value is false.\n    #\n    #liferay.workspace.modules.jsp.precompile.enabled=false\n\n    #\n    # Set the folder that contains the Plugins SDK environment. The default\n    # value is \"plugins-sdk\".\n    #\n    #liferay.workspace.plugins.sdk.dir=plugins-sdk\n\n    #\n    # Set the Liferay Portal or DXP version to develop and test against. By\n    # setting this property, it enables the target platform features such as\n    # dependency management and OSGi resolve tasks. Use the version that\n    # matches the Liferay Portal or DXP bundle version in this workspace.\n    #\n    # For a list of all available target platform versions, see\n    # https://bit.ly/2IkAwwW for Liferay Portal and https://bit.ly/2GIyfZF for\n    # Liferay DXP.\n    #\n    #liferay.workspace.target.platform.version=7.2.0\n\n    #\n    # Set this to true if you have enabled the Target Platform plugin (i.e. you\n    # have set the above property) and you want to apply the TargetPlatformIDE\n    # plugin to the root workspace project. This will cause all of the BOM\n    # artifacts jars and their Java sources to be indexed by your IDE. Setting\n    # this property to true can slow down your IDE's project synchronization.\n    #\n    # target.platform.index.sources=false\n\n    #\n    # Set the folder that contains Node.js-style theme projects. The default\n    # value is \"themes\".\n    #\n    #liferay.workspace.themes.dir=themes\n\n    #\n    # Set this to true to build the theme projects using the Liferay Portal\n    # Tools Theme Builder. The default value is false.\n    #\n    #liferay.workspace.themes.java.build=false\n\n    #\n    # Set the folder that contains classic WAR-style projects. The default value\n    # is \"wars\".\n    #\n    #liferay.workspace.wars.dir=wars\n\n\n    #\n    # Set the subscription key for Microsoft Translation integration.\n    # Subscription to the Translator Text Translation API on Microsoft Cognitive\n    # Services is required. Basic subscriptions, up to 2 million characters a\n    # month, are free. See\n    # http://docs.microsofttranslator.com/text-translate.html for more\n    # information.\n    #\nmicrosoft.translator.subscription.key = \nliferay.workspace.target.platform.version = 7.2.0\ntarget.platform.index.sources = false\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/.gitignore",
    "content": ".gradle/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/bnd.bnd",
    "content": "Bundle-Name: guestbook-api\nBundle-SymbolicName: com.liferay.docs.guestbook.api\nBundle-Version: 1.0.0\nExport-Package:\\\n\tcom.liferay.docs.guestbook.exception,\\\n\tcom.liferay.docs.guestbook.model,\\\n\tcom.liferay.docs.guestbook.service,\\\n\tcom.liferay.docs.guestbook.service.persistence\n-check: EXPORTS\n-includeresource: META-INF/service.xml=../guestbook-service/service.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryEmailException extends PortalException {\n\n\tpublic EntryEmailException() {\n\t}\n\n\tpublic EntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryMessageException extends PortalException {\n\n\tpublic EntryMessageException() {\n\t}\n\n\tpublic EntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryNameException extends PortalException {\n\n\tpublic EntryNameException() {\n\t}\n\n\tpublic EntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryEmailException extends PortalException {\n\n\tpublic GuestbookEntryEmailException() {\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryMessageException extends PortalException {\n\n\tpublic GuestbookEntryMessageException() {\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryNameException extends PortalException {\n\n\tpublic GuestbookEntryNameException() {\n\t}\n\n\tpublic GuestbookEntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookNameException extends PortalException {\n\n\tpublic GuestbookNameException() {\n\t}\n\n\tpublic GuestbookNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookEntryException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookEntryException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookEntryException() {\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookException() {\n\t}\n\n\tpublic NoSuchGuestbookException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/Guestbook.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookModel\n * @generated\n */\n@ImplementationClassName(\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\")\n@ProviderType\npublic interface Guestbook extends GuestbookModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<Guestbook, Long> GUESTBOOK_ID_ACCESSOR =\n\t\tnew Accessor<Guestbook, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(Guestbook guestbook) {\n\t\t\t\treturn guestbook.getGuestbookId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Guestbook> getTypeClass() {\n\t\t\t\treturn Guestbook.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntry.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookEntryModel\n * @generated\n */\n@ImplementationClassName(\n\t\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\"\n)\n@ProviderType\npublic interface GuestbookEntry extends GuestbookEntryModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<GuestbookEntry, Long> ENTRY_ID_ACCESSOR =\n\t\tnew Accessor<GuestbookEntry, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(GuestbookEntry guestbookEntry) {\n\t\t\t\treturn guestbookEntry.getEntryId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<GuestbookEntry> getTypeClass() {\n\t\t\t\treturn GuestbookEntry.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryModel\n\textends BaseModel<GuestbookEntry>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook entry model instance should use the {@link GuestbookEntry} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\tpublic long getEntryId();\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\tpublic void setEntryId(long entryId);\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getEmail();\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\tpublic void setEmail(String email);\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getMessage();\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\tpublic void setMessage(String message);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntrySoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookEntryServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntrySoap implements Serializable {\n\n\tpublic static GuestbookEntrySoap toSoapModel(GuestbookEntry model) {\n\t\tGuestbookEntrySoap soapModel = new GuestbookEntrySoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setEntryId(model.getEntryId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setEmail(model.getEmail());\n\t\tsoapModel.setMessage(model.getMessage());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(GuestbookEntry[] models) {\n\t\tGuestbookEntrySoap[] soapModels = new GuestbookEntrySoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[][] toSoapModels(\n\t\tGuestbookEntry[][] models) {\n\n\t\tGuestbookEntrySoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels =\n\t\t\t\tnew GuestbookEntrySoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookEntrySoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(\n\t\tList<GuestbookEntry> models) {\n\n\t\tList<GuestbookEntrySoap> soapModels = new ArrayList<GuestbookEntrySoap>(\n\t\t\tmodels.size());\n\n\t\tfor (GuestbookEntry model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookEntrySoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookEntrySoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetEntryId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic String getEmail() {\n\t\treturn _email;\n\t}\n\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn _message;\n\t}\n\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link GuestbookEntry}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryWrapper\n\textends BaseModelWrapper<GuestbookEntry>\n\timplements GuestbookEntry, ModelWrapper<GuestbookEntry> {\n\n\tpublic GuestbookEntryWrapper(GuestbookEntry guestbookEntry) {\n\t\tsuper(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"entryId\", getEntryId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"email\", getEmail());\n\t\tattributes.put(\"message\", getMessage());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong entryId = (Long)attributes.get(\"entryId\");\n\n\t\tif (entryId != null) {\n\t\t\tsetEntryId(entryId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tString email = (String)attributes.get(\"email\");\n\n\t\tif (email != null) {\n\t\t\tsetEmail(email);\n\t\t}\n\n\t\tString message = (String)attributes.get(\"message\");\n\n\t\tif (message != null) {\n\t\t\tsetMessage(message);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@Override\n\tpublic String getEmail() {\n\t\treturn model.getEmail();\n\t}\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn model.getEntryId();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@Override\n\tpublic String getMessage() {\n\t\treturn model.getMessage();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEmail(String email) {\n\t\tmodel.setEmail(email);\n\t}\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\tmodel.setEntryId(entryId);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\t@Override\n\tpublic void setMessage(String message) {\n\t\tmodel.setMessage(message);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookEntryWrapper wrap(GuestbookEntry guestbookEntry) {\n\t\treturn new GuestbookEntryWrapper(guestbookEntry);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic interface GuestbookModel\n\textends BaseModel<Guestbook>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook model instance should use the {@link Guestbook} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookSoap implements Serializable {\n\n\tpublic static GuestbookSoap toSoapModel(Guestbook model) {\n\t\tGuestbookSoap soapModel = new GuestbookSoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(Guestbook[] models) {\n\t\tGuestbookSoap[] soapModels = new GuestbookSoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[][] toSoapModels(Guestbook[][] models) {\n\t\tGuestbookSoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels = new GuestbookSoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookSoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(List<Guestbook> models) {\n\t\tList<GuestbookSoap> soapModels = new ArrayList<GuestbookSoap>(\n\t\t\tmodels.size());\n\n\t\tfor (Guestbook model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookSoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookSoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetGuestbookId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link Guestbook}.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic class GuestbookWrapper\n\textends BaseModelWrapper<Guestbook>\n\timplements Guestbook, ModelWrapper<Guestbook> {\n\n\tpublic GuestbookWrapper(Guestbook guestbook) {\n\t\tsuper(guestbook);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookWrapper wrap(Guestbook guestbook) {\n\t\treturn new GuestbookWrapper(guestbook);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for GuestbookEntry. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryLocalServiceUtil} to access the guestbook entry local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\tpublic GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId);\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException;\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId);\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows SystemException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> obc);\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\tpublic GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\taddGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().addGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\taddGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\t\tString message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tcreateGuestbookEntry(long entryId) {\n\n\t\treturn getService().createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(\n\t\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntry(long entryId) {\n\n\t\treturn getService().fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tint start, int end) {\n\n\t\treturn getService().getGuestbookEntries(start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId) {\n\n\t\treturn getService().getGuestbookEntries(groupId, guestbookId);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tString uuid, long companyId, int start, int end,\n\t\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int getGuestbookEntriesCount() {\n\t\treturn getService().getGuestbookEntriesCount();\n\t}\n\n\tpublic static int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn getService().getGuestbookEntriesCount(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tupdateGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().updateGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tupdateGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\t\tString email, String message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\tpublic static GuestbookEntryLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryLocalService.class);\n\n\t\tServiceTracker<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryLocalService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryLocalService}.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceWrapper\n\timplements GuestbookEntryLocalService,\n\t\t\t   ServiceWrapper<GuestbookEntryLocalService> {\n\n\tpublic GuestbookEntryLocalServiceWrapper(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry createGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookEntryLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry fetchGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookEntryLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(int start, int end) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(long groupId, long guestbookId) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry getGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntryLocalService getWrappedService() {\n\t\treturn _guestbookEntryLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for GuestbookEntry. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryServiceUtil} to access the guestbook entry remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookEntryService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookEntryService.class);\n\n\t\tServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryService, GuestbookEntryService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookEntryService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryService}.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceWrapper\n\timplements GuestbookEntryService, ServiceWrapper<GuestbookEntryService> {\n\n\tpublic GuestbookEntryServiceWrapper(\n\t\tGuestbookEntryService guestbookEntryService) {\n\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookEntryService getWrappedService() {\n\t\treturn _guestbookEntryService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookEntryService guestbookEntryService) {\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\tprivate GuestbookEntryService _guestbookEntryService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for Guestbook. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookLocalServiceUtil} to access the guestbook local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(Guestbook guestbook);\n\n\tpublic Guestbook addGuestbook(\n\t\t\tlong userId, String name, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId);\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook);\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException;\n\n\tpublic Guestbook deleteGuestbook(\n\t\t\tlong guestbookId, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbook(long guestbookId);\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException;\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(\n\t\tlong groupId, int start, int end, OrderByComparator<Guestbook> obc);\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(Guestbook guestbook);\n\n\tpublic Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().addGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbook(userId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbook(guestbookId);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().deleteGuestbook(guestbookId, serviceContext);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn getService().getGuestbooks(start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn getService().getGuestbooks(groupId);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int getGuestbooksCount() {\n\t\treturn getService().getGuestbooksCount();\n\t}\n\n\tpublic static int getGuestbooksCount(long groupId) {\n\t\treturn getService().getGuestbooksCount(groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().updateGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbook(\n\t\t\tuserId, guestbookId, name, serviceContext);\n\t}\n\n\tpublic static GuestbookLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookLocalService.class);\n\n\t\tServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookLocalService, GuestbookLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookLocalService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookLocalService}.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceWrapper\n\timplements GuestbookLocalService, ServiceWrapper<GuestbookLocalService> {\n\n\tpublic GuestbookLocalServiceWrapper(\n\t\tGuestbookLocalService guestbookLocalService) {\n\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.addGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.addGuestbook(\n\t\t\tuserId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbookId);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(\n\t\t\tguestbookId, serviceContext);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn _guestbookLocalService.getGuestbooksCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbooksCount(long groupId) {\n\t\treturn _guestbookLocalService.getGuestbooksCount(groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.updateGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.updateGuestbook(\n\t\t\tuserId, guestbookId, name, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookLocalService getWrappedService() {\n\t\treturn _guestbookLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookLocalService guestbookLocalService) {\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for Guestbook. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookServiceUtil} to access the guestbook remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookService, GuestbookService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookService.class);\n\n\t\tServiceTracker<GuestbookService, GuestbookService> serviceTracker =\n\t\t\tnew ServiceTracker<GuestbookService, GuestbookService>(\n\t\t\t\tbundle.getBundleContext(), GuestbookService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookService}.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceWrapper\n\timplements GuestbookService, ServiceWrapper<GuestbookService> {\n\n\tpublic GuestbookServiceWrapper(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookService getWrappedService() {\n\t\treturn _guestbookService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\tprivate GuestbookService _guestbookService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryPersistence\n\textends BasePersistence<GuestbookEntry> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryUtil} to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic void removeByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic void cacheResult(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic void cacheResult(java.util.List<GuestbookEntry> guestbookEntries);\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic GuestbookEntry create(long entryId);\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId);\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll();\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook entry service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookEntryPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().clearCache(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, GuestbookEntry> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static GuestbookEntry update(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().update(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static GuestbookEntry update(\n\t\tGuestbookEntry guestbookEntry, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbookEntry, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tentryId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic static GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tentryId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator,\n\t\t\tretrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_PrevAndNext(\n\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic static void removeByG_G(long groupId, long guestbookId) {\n\t\tgetPersistence().removeByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByG_G(long groupId, long guestbookId) {\n\t\treturn getPersistence().countByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic static void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().cacheResult(guestbookEntry);\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic static void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tgetPersistence().cacheResult(guestbookEntries);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static GuestbookEntry create(long entryId) {\n\t\treturn getPersistence().create(entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry remove(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().remove(entryId);\n\t}\n\n\tpublic static GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().updateImpl(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn getPersistence().fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookEntryPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence> _serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryPersistence.class);\n\n\t\tServiceTracker<GuestbookEntryPersistence, GuestbookEntryPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryPersistence.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookPersistence extends BasePersistence<Guestbook> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookUtil} to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(long groupId);\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic void removeByGroupId(long groupId);\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByGroupId(long groupId);\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic void cacheResult(Guestbook guestbook);\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic void cacheResult(java.util.List<Guestbook> guestbooks);\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic Guestbook create(long guestbookId);\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException;\n\n\tpublic Guestbook updateImpl(Guestbook guestbook);\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId);\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll();\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(Guestbook guestbook) {\n\t\tgetPersistence().clearCache(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, Guestbook> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static Guestbook update(Guestbook guestbook) {\n\t\treturn getPersistence().update(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static Guestbook update(\n\t\tGuestbook guestbook, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbook, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tguestbookId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic static Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tguestbookId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(long groupId) {\n\t\treturn getPersistence().findByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn getPersistence().findByGroupId(groupId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_First(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_First(\n\t\t\tgroupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_PrevAndNext(\n\t\t\tguestbookId, groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic static void removeByGroupId(long groupId) {\n\t\tgetPersistence().removeByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByGroupId(long groupId) {\n\t\treturn getPersistence().countByGroupId(groupId);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic static void cacheResult(Guestbook guestbook) {\n\t\tgetPersistence().cacheResult(guestbook);\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic static void cacheResult(List<Guestbook> guestbooks) {\n\t\tgetPersistence().cacheResult(guestbooks);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static Guestbook create(long guestbookId) {\n\t\treturn getPersistence().create(guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook remove(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().remove(guestbookId);\n\t}\n\n\tpublic static Guestbook updateImpl(Guestbook guestbook) {\n\t\treturn getPersistence().updateImpl(guestbook);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn getPersistence().fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic static List<Guestbook> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookPersistence.class);\n\n\t\tServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker<GuestbookPersistence, GuestbookPersistence>(\n\t\t\t\t\tbundle.getBundleContext(), GuestbookPersistence.class,\n\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/bnd.bnd",
    "content": "Bundle-Name: guestbook-service\nBundle-SymbolicName: com.liferay.docs.guestbook.service\nBundle-Version: 1.0.0\nLiferay-Require-SchemaVersion: 1.0.0\nLiferay-Service: true\n-dsannotations-options: inherit\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.lang\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.portal.aop.api\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\tcompileOnly project(\":modules:guestbook:guestbook-api\")\n}\n\nbuildService {\n\tapiDir = \"../guestbook-api/src/main/java\"\n}\n\ngroup = \"com.liferay.docs.guestbook\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/service.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"ds\" package-path=\"com.liferay.docs.guestbook\">\n\t<author>Liferay</author>\n\t<namespace>GB</namespace>\n\t\n\t<entity name=\"Guestbook\" local-service=\"true\" uuid=\"true\" remote-service=\"true\">\n\t\t\n\t\t<!-- Guestbook fields -->\n\n\t\t<column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\t\t\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<finder name=\"GroupId\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"groupId\" />\n\t\t</finder>\n\n\t</entity>\n\t\n    <entity name=\"GuestbookEntry\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n\n\t\t<!-- Guestbook Entry fields -->\n\n\t\t<column name=\"entryId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t<column name=\"email\" type=\"String\" />\n\t\t<column name=\"message\" type=\"String\" />\n\t\t<column name=\"guestbookId\" type=\"long\" />\n\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\t   \n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<order>\n\t\t\t<order-column name=\"createDate\" order-by=\"desc\" />\n\t\t</order>\n\n        <finder name=\"G_G\" return-type=\"Collection\">\n            <finder-column name=\"groupId\" />\n            <finder-column name=\"guestbookId\" />\n        </finder>\n    </entity>\n\n    <exceptions>\n        <exception>GuestbookEntryEmail</exception>\n        <exception>GuestbookEntryMessage</exception>\n        <exception>GuestbookEntryName</exception>\n        <exception>GuestbookName</exception>\n    </exceptions>\n</service-builder>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookBaseImpl\n\textends GuestbookModelImpl implements Guestbook {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookLocalServiceUtil.addGuestbook(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookLocalServiceUtil.updateGuestbook(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing Guestbook in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookCacheModel\n\timplements CacheModel<Guestbook>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookCacheModel guestbookCacheModel = (GuestbookCacheModel)obj;\n\n\t\tif (guestbookId == guestbookCacheModel.guestbookId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, guestbookId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(27);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic Guestbook toEntityModel() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookImpl.setGuestbookId(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tguestbookImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setName(name);\n\t\t}\n\n\t\tguestbookImpl.setGroupId(groupId);\n\t\tguestbookImpl.setCompanyId(companyId);\n\t\tguestbookImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookImpl.setStatus(status);\n\t\tguestbookImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long guestbookId;\n\tpublic String name;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryBaseImpl\n\textends GuestbookEntryModelImpl implements GuestbookEntry {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookEntryLocalServiceUtil.addGuestbookEntry(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookEntryLocalServiceUtil.updateGuestbookEntry(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing GuestbookEntry in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryCacheModel\n\timplements CacheModel<GuestbookEntry>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntryCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\t(GuestbookEntryCacheModel)obj;\n\n\t\tif (entryId == guestbookEntryCacheModel.entryId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, entryId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(33);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", entryId=\");\n\t\tsb.append(entryId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", email=\");\n\t\tsb.append(email);\n\t\tsb.append(\", message=\");\n\t\tsb.append(message);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEntityModel() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookEntryImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookEntryImpl.setEntryId(entryId);\n\n\t\tif (name == null) {\n\t\t\tguestbookEntryImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setName(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tguestbookEntryImpl.setEmail(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setEmail(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tguestbookEntryImpl.setMessage(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setMessage(message);\n\t\t}\n\n\t\tguestbookEntryImpl.setGuestbookId(guestbookId);\n\t\tguestbookEntryImpl.setGroupId(groupId);\n\t\tguestbookEntryImpl.setCompanyId(companyId);\n\t\tguestbookEntryImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookEntryImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookEntryImpl.setStatus(status);\n\t\tguestbookEntryImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookEntryImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tentryId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\t\temail = objectInput.readUTF();\n\t\tmessage = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(entryId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(message);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long entryId;\n\tpublic String name;\n\tpublic String email;\n\tpublic String message;\n\tpublic long guestbookId;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.GuestbookEntry<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryImpl extends GuestbookEntryBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook entry model instance should use the {@link com.liferay.docs.guestbook.model.GuestbookEntry} interface instead.\n\t */\n\tpublic GuestbookEntryImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.GuestbookEntryModel;\nimport com.liferay.docs.guestbook.model.GuestbookEntrySoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.DateUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookEntryModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookEntryModelImpl\n\textends BaseModelImpl<GuestbookEntry> implements GuestbookEntryModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_GuestbookEntry\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"entryId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"email\", Types.VARCHAR},\n\t\t{\"message\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"groupId\", Types.BIGINT}, {\"companyId\", Types.BIGINT},\n\t\t{\"userId\", Types.BIGINT}, {\"userName\", Types.VARCHAR},\n\t\t{\"createDate\", Types.TIMESTAMP}, {\"modifiedDate\", Types.TIMESTAMP},\n\t\t{\"status\", Types.INTEGER}, {\"statusByUserId\", Types.BIGINT},\n\t\t{\"statusByUserName\", Types.VARCHAR}, {\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"entryId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"email\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"message\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_GuestbookEntry (uuid_ VARCHAR(75) null,entryId LONG not null primary key,name VARCHAR(75) null,email VARCHAR(75) null,message VARCHAR(75) null,guestbookId LONG,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_GuestbookEntry\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbookEntry.createDate DESC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_GuestbookEntry.createDate DESC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 4L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 8L;\n\n\tpublic static final long CREATEDATE_COLUMN_BITMASK = 16L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static GuestbookEntry toModel(GuestbookEntrySoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbookEntry model = new GuestbookEntryImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setEntryId(soapModel.getEntryId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setEmail(soapModel.getEmail());\n\t\tmodel.setMessage(soapModel.getMessage());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<GuestbookEntry> toModels(\n\t\tGuestbookEntrySoap[] soapModels) {\n\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> models = new ArrayList<GuestbookEntry>(\n\t\t\tsoapModels.length);\n\n\t\tfor (GuestbookEntrySoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookEntryModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetEntryId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName,\n\t\t\t\tattributeGetterFunction.apply((GuestbookEntry)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<GuestbookEntry, Object>>\n\t\t\tattributeSetterBiConsumers = getAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<GuestbookEntry, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(GuestbookEntry)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<GuestbookEntry, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, GuestbookEntry>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbookEntry.class.getClassLoader(), GuestbookEntry.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<GuestbookEntry> constructor =\n\t\t\t\t(Constructor<GuestbookEntry>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<GuestbookEntry, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<GuestbookEntry, Object>>();\n\t\tMap<String, BiConsumer<GuestbookEntry, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<GuestbookEntry, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", GuestbookEntry::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUuid);\n\t\tattributeGetterFunctions.put(\"entryId\", GuestbookEntry::getEntryId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"entryId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setEntryId);\n\t\tattributeGetterFunctions.put(\"name\", GuestbookEntry::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setName);\n\t\tattributeGetterFunctions.put(\"email\", GuestbookEntry::getEmail);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"email\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setEmail);\n\t\tattributeGetterFunctions.put(\"message\", GuestbookEntry::getMessage);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"message\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setMessage);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"guestbookId\", GuestbookEntry::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"groupId\", GuestbookEntry::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", GuestbookEntry::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", GuestbookEntry::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", GuestbookEntry::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"createDate\", GuestbookEntry::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", GuestbookEntry::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", GuestbookEntry::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\",\n\t\t\t(BiConsumer<GuestbookEntry, Integer>)GuestbookEntry::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", GuestbookEntry::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)\n\t\t\t\tGuestbookEntry::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", GuestbookEntry::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)\n\t\t\t\tGuestbookEntry::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusDate\", GuestbookEntry::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getEmail() {\n\t\tif (_email == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _email;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getMessage() {\n\t\tif (_message == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _message;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_columnBitmask |= GUESTBOOKID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGuestbookId) {\n\t\t\t_setOriginalGuestbookId = true;\n\n\t\t\t_originalGuestbookId = _guestbookId;\n\t\t}\n\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getOriginalGuestbookId() {\n\t\treturn _originalGuestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_columnBitmask = -1L;\n\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), GuestbookEntry.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tguestbookEntryImpl.setUuid(getUuid());\n\t\tguestbookEntryImpl.setEntryId(getEntryId());\n\t\tguestbookEntryImpl.setName(getName());\n\t\tguestbookEntryImpl.setEmail(getEmail());\n\t\tguestbookEntryImpl.setMessage(getMessage());\n\t\tguestbookEntryImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookEntryImpl.setGroupId(getGroupId());\n\t\tguestbookEntryImpl.setCompanyId(getCompanyId());\n\t\tguestbookEntryImpl.setUserId(getUserId());\n\t\tguestbookEntryImpl.setUserName(getUserName());\n\t\tguestbookEntryImpl.setCreateDate(getCreateDate());\n\t\tguestbookEntryImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookEntryImpl.setStatus(getStatus());\n\t\tguestbookEntryImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookEntryImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookEntryImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(GuestbookEntry guestbookEntry) {\n\t\tint value = 0;\n\n\t\tvalue = DateUtil.compareTo(\n\t\t\tgetCreateDate(), guestbookEntry.getCreateDate());\n\n\t\tvalue = value * -1;\n\n\t\tif (value != 0) {\n\t\t\treturn value;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntry)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)obj;\n\n\t\tlong primaryKey = guestbookEntry.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl = this;\n\n\t\tguestbookEntryModelImpl._originalUuid = guestbookEntryModelImpl._uuid;\n\n\t\tguestbookEntryModelImpl._originalGuestbookId =\n\t\t\tguestbookEntryModelImpl._guestbookId;\n\n\t\tguestbookEntryModelImpl._setOriginalGuestbookId = false;\n\n\t\tguestbookEntryModelImpl._originalGroupId =\n\t\t\tguestbookEntryModelImpl._groupId;\n\n\t\tguestbookEntryModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookEntryModelImpl._originalCompanyId =\n\t\t\tguestbookEntryModelImpl._companyId;\n\n\t\tguestbookEntryModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookEntryModelImpl._setModifiedDate = false;\n\n\t\tguestbookEntryModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<GuestbookEntry> toCacheModel() {\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\tnew GuestbookEntryCacheModel();\n\n\t\tguestbookEntryCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookEntryCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.entryId = getEntryId();\n\n\t\tguestbookEntryCacheModel.name = getName();\n\n\t\tString name = guestbookEntryCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.name = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.email = getEmail();\n\n\t\tString email = guestbookEntryCacheModel.email;\n\n\t\tif ((email != null) && (email.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.email = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.message = getMessage();\n\n\t\tString message = guestbookEntryCacheModel.message;\n\n\t\tif ((message != null) && (message.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.message = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookEntryCacheModel.groupId = getGroupId();\n\n\t\tguestbookEntryCacheModel.companyId = getCompanyId();\n\n\t\tguestbookEntryCacheModel.userId = getUserId();\n\n\t\tguestbookEntryCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookEntryCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookEntryCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookEntryCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookEntryCacheModel.status = getStatus();\n\n\t\tguestbookEntryCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookEntryCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookEntryCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookEntryCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookEntryCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, GuestbookEntry>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _originalGuestbookId;\n\tprivate boolean _setOriginalGuestbookId;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate GuestbookEntry _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.Guestbook<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookImpl extends GuestbookBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook model instance should use the {@link com.liferay.docs.guestbook.model.Guestbook} interface instead.\n\t */\n\tpublic GuestbookImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookModel;\nimport com.liferay.docs.guestbook.model.GuestbookSoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookModelImpl\n\textends BaseModelImpl<Guestbook> implements GuestbookModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_Guestbook\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"groupId\", Types.BIGINT},\n\t\t{\"companyId\", Types.BIGINT}, {\"userId\", Types.BIGINT},\n\t\t{\"userName\", Types.VARCHAR}, {\"createDate\", Types.TIMESTAMP},\n\t\t{\"modifiedDate\", Types.TIMESTAMP}, {\"status\", Types.INTEGER},\n\t\t{\"statusByUserId\", Types.BIGINT}, {\"statusByUserName\", Types.VARCHAR},\n\t\t{\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_Guestbook (uuid_ VARCHAR(75) null,guestbookId LONG not null primary key,name VARCHAR(75) null,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_Guestbook\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbook.guestbookId ASC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_Guestbook.guestbookId ASC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 4L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 8L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static Guestbook toModel(GuestbookSoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbook model = new GuestbookImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<Guestbook> toModels(GuestbookSoap[] soapModels) {\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> models = new ArrayList<Guestbook>(soapModels.length);\n\n\t\tfor (GuestbookSoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetGuestbookId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName, attributeGetterFunction.apply((Guestbook)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<Guestbook, Object>> attributeSetterBiConsumers =\n\t\t\tgetAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<Guestbook, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(Guestbook)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<Guestbook, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<Guestbook, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, Guestbook>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbook.class.getClassLoader(), Guestbook.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<Guestbook> constructor =\n\t\t\t\t(Constructor<Guestbook>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<Guestbook, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<Guestbook, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<Guestbook, Object>>();\n\t\tMap<String, BiConsumer<Guestbook, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<Guestbook, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", Guestbook::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\", (BiConsumer<Guestbook, String>)Guestbook::setUuid);\n\t\tattributeGetterFunctions.put(\"guestbookId\", Guestbook::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"name\", Guestbook::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\", (BiConsumer<Guestbook, String>)Guestbook::setName);\n\t\tattributeGetterFunctions.put(\"groupId\", Guestbook::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\", (BiConsumer<Guestbook, Long>)Guestbook::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", Guestbook::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\", (BiConsumer<Guestbook, Long>)Guestbook::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", Guestbook::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\", (BiConsumer<Guestbook, Long>)Guestbook::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", Guestbook::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\", (BiConsumer<Guestbook, String>)Guestbook::setUserName);\n\t\tattributeGetterFunctions.put(\"createDate\", Guestbook::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", Guestbook::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", Guestbook::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\", (BiConsumer<Guestbook, Integer>)Guestbook::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", Guestbook::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", Guestbook::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<Guestbook, String>)Guestbook::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\"statusDate\", Guestbook::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), Guestbook.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic Guestbook toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tguestbookImpl.setUuid(getUuid());\n\t\tguestbookImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookImpl.setName(getName());\n\t\tguestbookImpl.setGroupId(getGroupId());\n\t\tguestbookImpl.setCompanyId(getCompanyId());\n\t\tguestbookImpl.setUserId(getUserId());\n\t\tguestbookImpl.setUserName(getUserName());\n\t\tguestbookImpl.setCreateDate(getCreateDate());\n\t\tguestbookImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookImpl.setStatus(getStatus());\n\t\tguestbookImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(Guestbook guestbook) {\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() < primaryKey) {\n\t\t\treturn -1;\n\t\t}\n\t\telse if (getPrimaryKey() > primaryKey) {\n\t\t\treturn 1;\n\t\t}\n\t\telse {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof Guestbook)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbook guestbook = (Guestbook)obj;\n\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookModelImpl guestbookModelImpl = this;\n\n\t\tguestbookModelImpl._originalUuid = guestbookModelImpl._uuid;\n\n\t\tguestbookModelImpl._originalGroupId = guestbookModelImpl._groupId;\n\n\t\tguestbookModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookModelImpl._originalCompanyId = guestbookModelImpl._companyId;\n\n\t\tguestbookModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookModelImpl._setModifiedDate = false;\n\n\t\tguestbookModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<Guestbook> toCacheModel() {\n\t\tGuestbookCacheModel guestbookCacheModel = new GuestbookCacheModel();\n\n\t\tguestbookCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookCacheModel.name = getName();\n\n\t\tString name = guestbookCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookCacheModel.name = null;\n\t\t}\n\n\t\tguestbookCacheModel.groupId = getGroupId();\n\n\t\tguestbookCacheModel.companyId = getCompanyId();\n\n\t\tguestbookCacheModel.userId = getUserId();\n\n\t\tguestbookCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookCacheModel.status = getStatus();\n\n\t\tguestbookCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, Guestbook>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate Guestbook _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookEntryLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\tguestbookEntry.setNew(true);\n\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.create(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbookEntry.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId) {\n\n\t\treturn guestbookEntryPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookEntryLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbookEntry.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<GuestbookEntry>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(GuestbookEntry guestbookEntry)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbookEntry);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryLocalService.deleteGuestbookEntry(\n\t\t\t(GuestbookEntry)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end) {\n\t\treturn guestbookEntryPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn guestbookEntryPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryLocalService = (GuestbookEntryLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\tprotected GuestbookEntryLocalService guestbookEntryLocalService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl\n * @generated\n */\npublic abstract class GuestbookEntryServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookEntryService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryService = (GuestbookEntryService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookEntryLocalService\n\t\tguestbookEntryLocalService;\n\n\tprotected GuestbookEntryService guestbookEntryService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook addGuestbook(Guestbook guestbook) {\n\t\tguestbook.setNew(true);\n\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.create(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.remove(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.remove(guestbook);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbook.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic Guestbook fetchGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\t\treturn guestbookPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.findByPrimaryKey(guestbookId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\n\t\t\t\"guestbookId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbook.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<Guestbook>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(Guestbook guestbook)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbook);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookLocalService.deleteGuestbook((Guestbook)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn guestbookPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooks(int start, int end) {\n\t\treturn guestbookPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn guestbookPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook updateGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookLocalService = (GuestbookLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\tprotected GuestbookLocalService guestbookLocalService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl\n * @generated\n */\npublic abstract class GuestbookServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookService = (GuestbookService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookLocalService\n\t\tguestbookLocalService;\n\n\tprotected GuestbookService guestbookService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntry</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.Guestbook</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.docs.guestbook.exception.GuestbookEntryEmailException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryMessageException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryNameException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\n\n/**\n * The implementation of the guestbook entry local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\", service = AopService.class)\npublic class GuestbookEntryLocalServiceImpl extends GuestbookEntryLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n\t * via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code\n\t * >.\n\t */\n\n\tpublic GuestbookEntry addGuestbookEntry(long userId, long guestbookId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tlong entryId = counterLocalService.increment();\n\n\t\tGuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\n\t\tentry.setUuid(serviceContext.getUuid());\n\t\tentry.setUserId(userId);\n\t\tentry.setGroupId(groupId);\n\t\tentry.setCompanyId(user.getCompanyId());\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setCreateDate(serviceContext.getCreateDate(now));\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\t\tentry.setGuestbookId(guestbookId);\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Calls to other Liferay frameworks go here\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry updateGuestbookEntry(long userId, long guestbookId,\n\t\t\tlong entryId, String name, String email, String message,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException {\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tentry.setUserId(userId);\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Integrate with Liferay frameworks here.\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry entry)\n\t\tthrows PortalException {\n\n\t\tguestbookEntryPersistence.remove(entry);\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId) throws PortalException {\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\treturn deleteGuestbookEntry(entry);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end) throws SystemException {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end, OrderByComparator<GuestbookEntry> obc) {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend, obc);\n\t}\n\n\tpublic GuestbookEntry getGuestbookEntry(long entryId) throws PortalException {\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.countByG_G(groupId, guestbookId);\n\t}\n\n\tprotected void validate(String name, String email, String entry)\n\t\tthrows PortalException {\n\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookEntryNameException();\n\t\t}\n\n\t\tif (!Validator.isEmailAddress(email)) {\n\t\t\tthrow new GuestbookEntryEmailException();\n\t\t}\n\n\t\tif (Validator.isNull(entry)) {\n\t\t\tthrow new GuestbookEntryMessageException();\n\t\t}\n\t}\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook entry remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookEntryService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=GuestbookEntry\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookEntryServiceImpl extends GuestbookEntryServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> to access the guestbook entry remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.exception.GuestbookNameException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.base.GuestbookLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\n\n/**\n * The implementation of the guestbook local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.Guestbook\", service = AopService.class)\npublic class GuestbookLocalServiceImpl extends GuestbookLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code> via\n\t * injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\tpublic Guestbook addGuestbook(long userId, String name,\n\t\t\tServiceContext serviceContext) throws PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name);\n\n\t\tlong guestbookId = counterLocalService.increment();\n\n\t\tGuestbook guestbook = guestbookPersistence.create(guestbookId);\n\n\t\tguestbook.setUuid(serviceContext.getUuid());\n\t\tguestbook.setUserId(userId);\n\t\tguestbook.setGroupId(groupId);\n\t\tguestbook.setCompanyId(user.getCompanyId());\n\t\tguestbook.setUserName(user.getFullName());\n\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tguestbook.setName(name);\n\t\tguestbook.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookPersistence.update(guestbook);\n\n\t\treturn guestbook;\n\t}\n\n\tpublic Guestbook updateGuestbook(long userId, long guestbookId,\n        String name, ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Date now = new Date();\n\n            validate(name);\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            User user = userLocalService.getUser(userId);\n\n            guestbook.setUserId(userId);\n            guestbook.setUserName(user.getFullName());\n            guestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n            guestbook.setName(name);\n            guestbook.setExpandoBridgeAttributes(serviceContext);\n\n            guestbookPersistence.update(guestbook);\n\n            return guestbook;\n    }\n\n\tpublic Guestbook deleteGuestbook(long guestbookId,\n                    ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            List<GuestbookEntry> entries = _guestbookEntryLocalService.getGuestbookEntries(\n                            serviceContext.getScopeGroupId(), guestbookId);\n\n            for (GuestbookEntry entry : entries) {\n                    _guestbookEntryLocalService.deleteGuestbookEntry(entry.getEntryId());\n            }\n\n            guestbook = deleteGuestbook(guestbook);\n\n            return guestbook;\n    }\n\n\tpublic List<Guestbook> getGuestbooks(long groupId) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end,\n\t\t\tOrderByComparator<Guestbook> obc) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end, obc);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end);\n\t}\n\n\tpublic int getGuestbooksCount(long groupId) {\n\n\t\treturn guestbookPersistence.countByGroupId(groupId);\n\t}\n\n\tprotected void validate(String name) throws PortalException {\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookNameException();\n\t\t}\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=Guestbook\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookServiceImpl extends GuestbookServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> to access the guestbook remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookEntryPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookEntryPersistence.class)\n@ProviderType\npublic class GuestbookEntryPersistenceImpl\n\textends BasePersistenceImpl<GuestbookEntry>\n\timplements GuestbookEntryPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookEntryUtil</code> to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookEntryImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_First(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_Last(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbookEntry.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof GuestbookEntry) {\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbookEntry.getUuid()) ||\n\t\t\t\t(groupId != guestbookEntry.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<GuestbookEntry> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbookEntry guestbookEntry = list.get(0);\n\n\t\t\t\t\tresult = guestbookEntry;\n\n\t\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (GuestbookEntry)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\t@Override\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbookEntry.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\ttrue);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\tfalse);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_C_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tlong companyId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbookEntry.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByG_G;\n\tprivate FinderPath _finderPathWithoutPaginationFindByG_G;\n\tprivate FinderPath _finderPathCountByG_G;\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(long groupId, long guestbookId) {\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn findByG_G(groupId, guestbookId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {groupId, guestbookId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tgroupId, guestbookId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif ((groupId != guestbookEntry.getGroupId()) ||\n\t\t\t\t\t\t(guestbookId != guestbookEntry.getGuestbookId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByG_G(groupId, guestbookId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByG_G_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tlong guestbookId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(guestbookId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\t@Override\n\tpublic void removeByG_G(long groupId, long guestbookId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByG_G(\n\t\t\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByG_G(long groupId, long guestbookId) {\n\t\tFinderPath finderPath = _finderPathCountByG_G;\n\n\t\tObject[] finderArgs = new Object[] {groupId, guestbookId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_G_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_G_G_GUESTBOOKID_2 =\n\t\t\"guestbookEntry.guestbookId = ?\";\n\n\tpublic GuestbookEntryPersistenceImpl() {\n\t\tsetModelClass(GuestbookEntry.class);\n\n\t\tsetModelImplClass(GuestbookEntryImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\t@Override\n\tpublic void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {\n\t\t\t\tguestbookEntry.getUuid(), guestbookEntry.getGroupId()\n\t\t\t},\n\t\t\tguestbookEntry);\n\n\t\tguestbookEntry.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\t@Override\n\tpublic void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbook entries.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookEntryImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook entry.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(GuestbookEntry guestbookEntry) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookEntryModelImpl)guestbookEntry, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<GuestbookEntry> guestbookEntries) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache(\n\t\t\t\t(GuestbookEntryModelImpl)guestbookEntry, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookEntryModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic GuestbookEntry create(long entryId) {\n\t\tGuestbookEntry guestbookEntry = new GuestbookEntryImpl();\n\n\t\tguestbookEntry.setNew(true);\n\t\tguestbookEntry.setPrimaryKey(entryId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbookEntry.setUuid(uuid);\n\n\t\tguestbookEntry.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn remove((Serializable)entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\tGuestbookEntryImpl.class, primaryKey);\n\n\t\t\tif (guestbookEntry == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbookEntry);\n\t\t}\n\t\tcatch (NoSuchGuestbookEntryException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected GuestbookEntry removeImpl(GuestbookEntry guestbookEntry) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbookEntry)) {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\t\tGuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbookEntry != null) {\n\t\t\t\tsession.delete(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbookEntry != null) {\n\t\t\tclearCache(guestbookEntry);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t@Override\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\tboolean isNew = guestbookEntry.isNew();\n\n\t\tif (!(guestbookEntry instanceof GuestbookEntryModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbookEntry.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(\n\t\t\t\t\tguestbookEntry);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbookEntry proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom GuestbookEntry implementation \" +\n\t\t\t\t\tguestbookEntry.getClass());\n\t\t}\n\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl =\n\t\t\t(GuestbookEntryModelImpl)guestbookEntry;\n\n\t\tif (Validator.isNull(guestbookEntry.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbookEntry.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbookEntry.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookEntryModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setModifiedDate(\n\t\t\t\t\tserviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbookEntry.isNew()) {\n\t\t\t\tsession.save(guestbookEntry);\n\n\t\t\t\tguestbookEntry.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.merge(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByG_G.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry, false);\n\n\t\tclearUniqueFindersCache(guestbookEntryModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookEntryModelImpl);\n\n\t\tguestbookEntry.resetOriginalValues();\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn findByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn fetchByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOKENTRY;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (GuestbookEntry guestbookEntry : findAll()) {\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOKENTRY);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"entryId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOKENTRY;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookEntryModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook entry persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookEntryModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookEntryModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.COMPANYID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GUESTBOOKID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookEntryImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.GuestbookEntry\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbookEntry.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No GuestbookEntry exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No GuestbookEntry exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookEntryPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.impl.GuestbookImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookPersistence.class)\n@ProviderType\npublic class GuestbookPersistenceImpl\n\textends BasePersistenceImpl<Guestbook> implements GuestbookPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookUtil</code> to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_First(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_Last(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbook.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbook == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof Guestbook) {\n\t\t\tGuestbook guestbook = (Guestbook)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbook.getUuid()) ||\n\t\t\t\t(groupId != guestbook.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<Guestbook> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbook guestbook = list.get(0);\n\n\t\t\t\t\tresult = guestbook;\n\n\t\t\t\t\tcacheResult(guestbook);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (Guestbook)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbook);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbook.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_C_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbook.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByGroupId;\n\tprivate FinderPath _finderPathWithoutPaginationFindByGroupId;\n\tprivate FinderPath _finderPathCountByGroupId;\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId) {\n\t\treturn findByGroupId(\n\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId, int start, int end) {\n\t\treturn findByGroupId(groupId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByGroupId(groupId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif ((groupId != guestbook.getGroupId())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_First(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByGroupId(groupId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_Last(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByGroupId(groupId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByGroupId(\n\t\t\tgroupId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByGroupId_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\t@Override\n\tpublic void removeByGroupId(long groupId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByGroupId(\n\t\t\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByGroupId(long groupId) {\n\t\tFinderPath finderPath = _finderPathCountByGroupId;\n\n\t\tObject[] finderArgs = new Object[] {groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_GROUPID_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tpublic GuestbookPersistenceImpl() {\n\t\tsetModelClass(Guestbook.class);\n\n\t\tsetModelImplClass(GuestbookImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\t@Override\n\tpublic void cacheResult(Guestbook guestbook) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {guestbook.getUuid(), guestbook.getGroupId()},\n\t\t\tguestbook);\n\n\t\tguestbook.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\t@Override\n\tpublic void cacheResult(List<Guestbook> guestbooks) {\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\t\tguestbook.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbook);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbooks.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(Guestbook guestbook) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<Guestbook> guestbooks) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\tguestbook.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic Guestbook create(long guestbookId) {\n\t\tGuestbook guestbook = new GuestbookImpl();\n\n\t\tguestbook.setNew(true);\n\t\tguestbook.setPrimaryKey(guestbookId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbook.setUuid(uuid);\n\n\t\tguestbook.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException {\n\t\treturn remove((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook guestbook = (Guestbook)session.get(\n\t\t\t\tGuestbookImpl.class, primaryKey);\n\n\t\t\tif (guestbook == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbook);\n\t\t}\n\t\tcatch (NoSuchGuestbookException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected Guestbook removeImpl(Guestbook guestbook) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbook)) {\n\t\t\t\tguestbook = (Guestbook)session.get(\n\t\t\t\t\tGuestbookImpl.class, guestbook.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbook != null) {\n\t\t\t\tsession.delete(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbook != null) {\n\t\t\tclearCache(guestbook);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t@Override\n\tpublic Guestbook updateImpl(Guestbook guestbook) {\n\t\tboolean isNew = guestbook.isNew();\n\n\t\tif (!(guestbook instanceof GuestbookModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbook.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(guestbook);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbook proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom Guestbook implementation \" +\n\t\t\t\t\tguestbook.getClass());\n\t\t}\n\n\t\tGuestbookModelImpl guestbookModelImpl = (GuestbookModelImpl)guestbook;\n\n\t\tif (Validator.isNull(guestbook.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbook.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbook.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbook.isNew()) {\n\t\t\t\tsession.save(guestbook);\n\n\t\t\t\tguestbook.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook = (Guestbook)session.merge(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getUuid(),\n\t\t\t\t\tguestbookModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByGroupId.\n\t\t\t\t\t getColumnBitmask()) != 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook, false);\n\n\t\tclearUniqueFindersCache(guestbookModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookModelImpl);\n\n\t\tguestbook.resetOriginalValues();\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbook == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\treturn findByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn fetchByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOK);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOK;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (Guestbook guestbook : findAll()) {\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOK);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"guestbookId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOK;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.COMPANYID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {Long.class.getName()},\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByGroupId\",\n\t\t\tnew String[] {Long.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.Guestbook\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK =\n\t\t\"SELECT guestbook FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK_WHERE =\n\t\t\"SELECT guestbook FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK_WHERE =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbook.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No Guestbook exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No Guestbook exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/constants/GBPersistenceConstants.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl.constants;\n\nimport com.liferay.petra.string.StringBundler;\n\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.Constants;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * @author Liferay\n * @generated\n */\n@Component(immediate = true, service = {})\npublic class GBPersistenceConstants {\n\n\tpublic static final String BUNDLE_SYMBOLIC_NAME =\n\t\t\"com.liferay.docs.guestbook.service\";\n\n\tpublic static final String ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER =\n\t\t\"(origin.bundle.symbolic.name=\" + BUNDLE_SYMBOLIC_NAME + \")\";\n\n\t@Activate\n\tprotected void activate(BundleContext bundleContext) {\n\t\tBundle bundle = bundleContext.getBundle();\n\n\t\tif (!BUNDLE_SYMBOLIC_NAME.equals(bundle.getSymbolicName())) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\tStringBundler.concat(\n\t\t\t\t\t\"Incorrect \", Constants.BUNDLE_SYMBOLICNAME, \" for bundle \",\n\t\t\t\t\tbundle.getSymbolicName()));\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/module-hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">\n\n<hibernate-mapping auto-import=\"false\" default-lazy=\"false\">\n\t<import class=\"com.liferay.docs.guestbook.model.Guestbook\" />\n\t<import class=\"com.liferay.docs.guestbook.model.GuestbookEntry\" />\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\" table=\"GB_Guestbook\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\" table=\"GB_GuestbookEntry\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"entryId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"email\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"message\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n</hibernate-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/portlet-model-hints.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<model-hints>\n\t<model name=\"com.liferay.docs.guestbook.model.Guestbook\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n\t<model name=\"com.liferay.docs.guestbook.model.GuestbookEntry\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"entryId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"email\" type=\"String\" />\n\t\t<field name=\"message\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n</model-hints>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/indexes.sql",
    "content": "create index IX_9294AD47 on GB_Guestbook (groupId);\ncreate index IX_9314A9F7 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_EDD4239 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], groupId);\n\ncreate index IX_E84D72FD on GB_GuestbookEntry (groupId, guestbookId);\ncreate index IX_CC265FEF on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_4A541631 on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], groupId);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/sequences.sql",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/tables.sql",
    "content": "create table GB_Guestbook (\n\tuuid_ VARCHAR(75) null,\n\tguestbookId LONG not null primary key,\n\tname VARCHAR(75) null,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);\n\ncreate table GB_GuestbookEntry (\n\tuuid_ VARCHAR(75) null,\n\tentryId LONG not null primary key,\n\tname VARCHAR(75) null,\n\temail VARCHAR(75) null,\n\tmessage VARCHAR(75) null,\n\tguestbookId LONG,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/service.properties",
    "content": "##\n## Properties Override\n##\n\n    #\n    # Specify where to get the overridden properties. Updates should not be made\n    # on this file but on the overridden version of this file.\n    #\n    include-and-override=service-ext.properties\n\n##\n## Build\n##\n\n    build.namespace=GB\n    build.number=5\n    build.date=1567633317058"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/.gitignore",
    "content": ".gradle/\n.sass-cache/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/bnd.bnd",
    "content": "Bundle-Name: guestbook-web\nBundle-SymbolicName: com.liferay.docs.guestbook.portlet\nBundle-Version: 1.0.0\nExport-Package: com.liferay.docs.guestbook.portlet.constants"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.application.list.api\"\n\tcompileOnly group: \"javax.portlet\", name: \"portlet-api\"\n\tcompileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n\tcompileOnly group: \"jstl\", name: \"jstl\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\n    compileOnly project(\":modules:guestbook:guestbook-api\")\n    compileOnly project(\":modules:guestbook:guestbook-service\")\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/application/list/GuestbookAdminPanelApp.java",
    "content": "package com.liferay.docs.guestbook.application.list;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.application.list.BasePanelApp;\nimport com.liferay.application.list.PanelApp;\nimport com.liferay.application.list.constants.PanelCategoryKeys;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.portal.kernel.model.Portlet;\n\n@Component(\n        immediate = true,\n        property = {\n            \"panel.app.order:Integer=300\",\n            \"panel.category.key=\" + PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT\n        },\n        service = PanelApp.class\n    )\npublic class GuestbookAdminPanelApp extends BasePanelApp {\n\n        @Override\n        public String getPortletId() {\n            return GuestbookPortletKeys.GUESTBOOK_ADMIN;\n        }\n\n        @Override\n        @Reference(\n            target = \"(javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN + \")\",\n            unbind = \"-\"\n        )\n        public void setPortlet(Portlet portlet) {\n            super.setPortlet(portlet);\n        }\n\n    }\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/constants/GuestbookPortletKeys.java",
    "content": "package com.liferay.docs.guestbook.constants;\n\n/**\n * @author sezovr\n */\npublic class GuestbookPortletKeys {\n\n\tpublic static final String GUESTBOOK =\n\t\t\"com_liferay_docs_guestbook_portlet_GuestbookPortlet\";\n\n\tpublic static final String GUESTBOOK_ADMIN =\n\t\t  \"com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet\";\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookAdminPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.util.ParamUtil;\n@Component(\n        immediate = true,\n        property = {\n                \"com.liferay.portlet.display-category=category.hidden\",\n                \"com.liferay.portlet.scopeable=true\",\n                \"javax.portlet.display-name=Guestbooks\",\n                \"javax.portlet.expiration-cache=0\",\n                \"javax.portlet.init-param.portlet-title-based-navigation=true\",\n                \"javax.portlet.init-param.template-path=/\",\n                \"javax.portlet.init-param.view-template=/guestbook_admin/view.jsp\",\n                \"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN,\n                \"javax.portlet.resource-bundle=content.Language\",\n                \"javax.portlet.security-role-ref=administrator\",\n                \"javax.portlet.supports.mime-type=text/html\",\n                \"com.liferay.portlet.add-default-resource=true\"\n        },\n        service = Portlet.class\n    )\npublic class GuestbookAdminPortlet extends MVCPortlet {\n\npublic void addGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n\n        try {\n            _guestbookLocalService.addGuestbook(\n                serviceContext.getUserId(), name, serviceContext);\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n\n    public void updateGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.updateGuestbook(\n                serviceContext.getUserId(), guestbookId, name, serviceContext);\n\n        } catch (PortalException pe) {\n        \n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n    \n    public void deleteGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.deleteGuestbook(guestbookId, serviceContext);\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n        }\n    }\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\nimport javax.portlet.PortletException;\nimport javax.portlet.RenderRequest;\nimport javax.portlet.RenderResponse;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.util.ParamUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\n/**\n * @author sezovr\n */\n@Component(immediate = true, property = { \"com.liferay.portlet.display-category=category.social\",\n\t\t\"com.liferay.portlet.header-portlet-css=/css/main.css\", \n\t\t\"com.liferay.portlet.instanceable=false\",\n\t\t\"com.liferay.portlet.scopeable=true\", \n\t\t\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK,\n\t\t\"javax.portlet.display-name=Guestbook\", \n\t\t\"javax.portlet.expiration-cache=0\",\n\t\t\"javax.portlet.init-param.template-path=/\", \n\t\t\"javax.portlet.init-param.view-template=/guestbook/view.jsp\",\n\t\t\"javax.portlet.resource-bundle=content.Language\", \n\t\t\"javax.portlet.security-role-ref=power-user,user\",\n\t\t\"javax.portlet.supports.mime-type=text/html\" }, \n\tservice = Portlet.class)\npublic class GuestbookPortlet extends MVCPortlet {\n\n\tpublic void addEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\n\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(GuestbookEntry.class.getName(), request);\n\n\t\tString userName = ParamUtil.getString(request, \"name\");\n\t\tString email = ParamUtil.getString(request, \"email\");\n\t\tString message = ParamUtil.getString(request, \"message\");\n\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\n\t\tif (entryId > 0) {\n\n\t\t\ttry {\n\n\t\t\t\t_guestbookEntryLocalService.updateGuestbookEntry(serviceContext.getUserId(), guestbookId, entryId, userName,\n\t\t\t\t\t\temail, message, serviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(e);\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\n\t\t} else {\n\n\t\t\ttry {\n\t\t\t\t_guestbookEntryLocalService.addGuestbookEntry(serviceContext.getUserId(), guestbookId, userName, email, message,\n\t\t\t\t\t\tserviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t} catch (Exception e) {\n\n\t\t\t\tSystem.out.println(e);\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic void deleteEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\t\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\t\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\tGuestbookEntry.class.getName(), request);\n\n\t\t\ttry {\n\n\t\t\t\tresponse.setRenderParameter(\n\t\t\t\t\t\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t\t_guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\t\t\t}\n\n\t\t\tcatch (Exception e) {\n\t\t\t\tLogger.getLogger(GuestbookPortlet.class.getName()).log(\n\t\t\t\t\tLevel.SEVERE, null, e);\n\t\t\t}\n\t}\n\n\t@Override\n\tpublic void render(RenderRequest renderRequest, RenderResponse renderResponse)\n\t\t\tthrows IOException, PortletException {\n\n\t\t\ttry {\n\t\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\t\tGuestbook.class.getName(), renderRequest);\n\n\t\t\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\t\t\tlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n\t\t\t\tList<Guestbook> guestbooks = _guestbookLocalService.getGuestbooks(\n\t\t\t\t\tgroupId);\n\n\t\t\t\tif (guestbooks.isEmpty()) {\n\t\t\t\t\tGuestbook guestbook = _guestbookLocalService.addGuestbook(\n\t\t\t\t\t\tserviceContext.getUserId(), \"Main\", serviceContext);\n\n\t\t\t\t\tguestbookId = guestbook.getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\tif (guestbookId == 0) {\n\t\t\t\t\tguestbookId = guestbooks.get(0).getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\trenderRequest.setAttribute(\"guestbookId\", guestbookId);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new PortletException(e);\n\t\t\t}\n\n\t\t\tsuper.render(renderRequest, renderResponse);\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/css/main.scss",
    "content": ".guestbook-web {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/edit_entry.jsp",
    "content": "<%@include file=\"../init.jsp\" %>\n\n<% \n\nlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\nGuestbookEntry entry = null;\nif (entryId > 0) {\n  entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n}\n\nlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n%>\n\n<portlet:renderURL var=\"viewURL\">\n\t<portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\"></portlet:param>\n</portlet:renderURL>\n\n<portlet:actionURL name=\"addEntry\" var=\"addEntryURL\"></portlet:actionURL>\n\n<aui:form action=\"<%= addEntryURL %>\" name=\"<portlet:namespace />fm\">\n\n<aui:model-context bean=\"<%= entry %>\" model=\"<%= GuestbookEntry.class %>\" />\n\n\t<aui:fieldset>\n\n\t\t<aui:input name=\"name\" />\n\t\t<aui:input name=\"email\" />\n\t\t<aui:input name=\"message\" />\n\t\t<aui:input name=\"entryId\" type=\"hidden\" />\n\t\t<aui:input name=\"guestbookId\" type=\"hidden\" value='<%= entry == null ? guestbookId : entry.getGuestbookId() %>'/>\n\n\t</aui:fieldset>\n\n\t<aui:button-row>\n\n\t\t<aui:button type=\"submit\"></aui:button>\n\t\t<aui:button type=\"cancel\" onClick=\"<%= viewURL.toString() %>\"></aui:button>\n\n\t</aui:button-row>\n</aui:form>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/entry_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n        <%\n        String mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n        ResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);\n\n        GuestbookEntry entry = (GuestbookEntry)row.getObject(); \n        %>\n\n        <liferay-ui:icon-menu>\n\n                <portlet:renderURL var=\"editURL\">\n                    <portlet:param name=\"entryId\"\n                        value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n                    <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n                </portlet:renderURL>\n\n                <liferay-ui:icon image=\"edit\" message=\"Edit\"\n                    url=\"<%=editURL.toString() %>\" />\n\n                <portlet:actionURL name=\"deleteEntry\" var=\"deleteURL\">\n                    <portlet:param name=\"entryId\"\n                        value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n                    <portlet:param name=\"guestbookId\"\n                        value=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n                </portlet:actionURL>\n\n                <liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n        </liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\nlong guestbookId = Long.valueOf((Long) renderRequest\n\t\t.getAttribute(\"guestbookId\"));\n%>\n\n<aui:nav cssClass=\"nav-tabs\">\n\n\t<%\n\t\tList<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n\t\t\tfor (int i = 0; i < guestbooks.size(); i++) {\n\n\t\t\t\tGuestbook curGuestbook = (Guestbook) guestbooks.get(i);\n\t\t\t\tString cssClass = StringPool.BLANK;\n\n\t\t\t\tif (curGuestbook.getGuestbookId() == guestbookId) {\n\t\t\t\t\tcssClass = \"active\";\n\t\t\t\t}\n\n\t%>\n\n\t<portlet:renderURL var=\"viewPageURL\">\n\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\" />\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(curGuestbook.getGuestbookId())%>\" />\n\t</portlet:renderURL>\n\n\t\t\n\t<aui:nav-item cssClass=\"<%=cssClass%>\" href=\"<%=viewPageURL%>\"\n\t\tlabel=\"<%=HtmlUtil.escape(curGuestbook.getName())%>\" />\n\n\t<%  \n\t\t\t\t}\n\t\t\t\n\t%>\n\n</aui:nav>\n\n<aui:button-row cssClass=\"guestbook-buttons\">\n\n\t<portlet:renderURL var=\"addEntryURL\">\n\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(guestbookId)%>\" />\n\t</portlet:renderURL>\n\n\t<aui:button onClick=\"<%=addEntryURL.toString()%>\" value=\"Add Entry\"></aui:button>\n\n</aui:button-row>\n\n<liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntriesCount()%>\">\n<liferay-ui:search-container-results\n\tresults=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(scopeGroupId.longValue(),\n\t\t\t\t\tguestbookId, searchContainer.getStart(),\n\t\t\t\t\tsearchContainer.getEnd())%>\" />\n\n<liferay-ui:search-container-row\n\tclassName=\"com.liferay.docs.guestbook.model.GuestbookEntry\" modelVar=\"entry\">\n\n\t<liferay-ui:search-container-column-text property=\"message\" />\n\n\t<liferay-ui:search-container-column-text property=\"name\" />\n\n\t<liferay-ui:search-container-column-jsp\n\t\talign=\"right\" \n\t\tpath=\"/guestbook/entry_actions.jsp\" />\n</liferay-ui:search-container-row>\n\n<liferay-ui:search-iterator />\n\n</liferay-ui:search-container>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/edit_guestbook.jsp",
    "content": "<%@include file = \"../init.jsp\" %>\n\n<%\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n        \n        Guestbook guestbook = null;\n\n        if (guestbookId > 0) {\n                guestbook = GuestbookLocalServiceUtil.getGuestbook(guestbookId);\n        }\n%>\n\n<portlet:renderURL var=\"viewURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook_admin/view.jsp\" />\n</portlet:renderURL>\n\n<portlet:actionURL name='<%= guestbook == null ? \"addGuestbook\" : \"updateGuestbook\" %>' var=\"editGuestbookURL\" />\n\n<aui:form action=\"<%= editGuestbookURL %>\" name=\"fm\">\n\n        <aui:model-context bean=\"<%= guestbook %>\" model=\"<%= Guestbook.class %>\" />\n\n        <aui:input type=\"hidden\" name=\"guestbookId\"\n            value='<%= guestbook == null ? \"\" : guestbook.getGuestbookId() %>' />\n\n        <aui:fieldset>\n             <aui:input name=\"name\" />\n        </aui:fieldset>\n\n        <aui:button-row>\n             <aui:button type=\"submit\" />\n             <aui:button onClick=\"<%= viewURL %>\" type=\"cancel\"  />\n        </aui:button-row>\n</aui:form>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/guestbook_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\n\tString mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n\tResultRow row = (ResultRow) request\n\t\t\t\t\t.getAttribute(\"SEARCH_CONTAINER_RESULT_ROW\");\n\n\tGuestbook guestbook = (Guestbook) row.getObject();\n%>\n\n<liferay-ui:icon-menu>\n\n\t<portlet:renderURL var=\"editURL\">\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(guestbook.getGuestbookId()) %>\" />\n\t\t<portlet:param name=\"mvcPath\"\n\t\t\tvalue=\"/guestbook_admin/edit_guestbook.jsp\" />\n\t</portlet:renderURL>\n\n\t<liferay-ui:icon image=\"edit\" message=\"Edit\"\n\t\t\turl=\"<%=editURL.toString() %>\" />\n\n\t<portlet:actionURL name=\"deleteGuestbook\" var=\"deleteURL\">\n\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\tvalue=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\" />\n\t</portlet:actionURL>\n\n\t<liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n</liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<liferay-ui:search-container\n\ttotal=\"<%= GuestbookLocalServiceUtil.getGuestbooksCount(scopeGroupId) %>\">\n\t<liferay-ui:search-container-results\n\t\tresults=\"<%= GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId, \n\t\t\tsearchContainer.getStart(), searchContainer.getEnd()) %>\" />\n\n\t<liferay-ui:search-container-row\n\t\tclassName=\"com.liferay.docs.guestbook.model.Guestbook\" modelVar=\"guestbook\">\n\n\t\t<liferay-ui:search-container-column-text property=\"name\" />\n\t\t\t\t\n\t\t<liferay-ui:search-container-column-jsp\n\t\t\talign=\"right\" \n\t\t\tpath=\"/guestbook_admin/guestbook_actions.jsp\" />\n\t\t\n\t</liferay-ui:search-container-row>\n\n\t<liferay-ui:search-iterator />\n</liferay-ui:search-container>\n\n<aui:button-row cssClass=\"guestbook-admin-buttons\">\n\t<portlet:renderURL var=\"addGuestbookURL\">\n\t\t<portlet:param name=\"mvcPath\"\n\t\t\tvalue=\"/guestbook_admin/edit_guestbook.jsp\" />\n\t\t<portlet:param name=\"redirect\" value=\"<%= \"currentURL\" %>\" />\n\t</portlet:renderURL>\n\t\t\n\t<aui:button onClick=\"<%= addGuestbookURL.toString() %>\"\n\t\tvalue=\"Add Guestbook\" />\n</aui:button-row>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/init.jsp",
    "content": "<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\"%>\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\"%>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n<%@ taglib uri=\"http://liferay.com/tld/security\" prefix=\"liferay-security\" %>\n\n<%@ page import=\"java.util.List\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.ParamUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.HtmlUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.petra.string.StringPool\" %>\n<%@ page import=\"com.liferay.portal.kernel.model.PersistedModel\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.SearchEntry\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.ResultRow\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.Guestbook\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.GuestbookEntry\" %> \n\n<liferay-theme:defineObjects />\n\n<portlet:defineObjects />"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/content/Language.properties",
    "content": "javax.portlet.title.com_liferay_docs_guestbook_portlet_GuestbookPortlet=Guestbook\nguestbook.caption=Hello from Guestbook!\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/05-admin-portlet/com-liferay-docs-guestbook/settings.gradle",
    "content": "buildscript {\n\tdependencies {\n\t\tclasspath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.workspace\", version: \"2.0.4\"\n\t\tclasspath group: \"net.saliman\", name: \"gradle-properties-plugin\", version: \"1.4.6\"\n\t}\n\n\trepositories {\n\t\tmaven {\n\t\t\turl \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t\t}\n\t}\n}\n\napply plugin: \"net.saliman.properties\"\n\napply plugin: \"com.liferay.workspace\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/.blade.properties",
    "content": "#Wed Aug 14 16:06:16 EDT 2019\nliferay.version.default=7.2\nprofile.name=gradle\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/.gitignore",
    "content": "**/*.iml\n**/.ivy\n**/.classpath\n**/.project\n**/.sass-cache\n**/.settings\n**/bin\n**/build\n**/build_gradle\n**/node_modules\n**/test-coverage\n**/tmp\n**/.web_bundle_build\n.gradle\n.idea\n/bundles\n/gradle-*.properties\n/plugins-sdk/**/classes\n/plugins-sdk/**/ivy.xml.MD5\n/plugins-sdk/**/liferay-hook.xml.processed\n/plugins-sdk/build.*.properties\n/plugins-sdk/dependencies/**/*.jar\n/plugins-sdk/dist\n/plugins-sdk/lib\ntest-results"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/configs/common/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/configs/dev/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/configs/docker/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/configs/local/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/configs/prod/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "operationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/configs/prod/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/configs/uat/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "logExceptionsOnly=B\"false\"\noperationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/configs/uat/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.2-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/gradle.properties",
    "content": "\n    # Set the directory where the downloaded bundle Zip files are stored. The\n    # default value is the \".liferay/bundles\" folder inside the user home\n    # directory.\n    #\n    #liferay.workspace.bundle.cache.dir=~/.liferay/bundles\n\n    #\n    # Set this to true if the \"liferay.workspace.bundle.url\" property is set to\n    # a DXP bundle Zip. This property allows the token residing in the\n    # \"~/.liferay\" folder to be used to validate your user credentials when\n    # downloading the bundle. The default value is false.\n    #\n    #liferay.workspace.bundle.token.download=false\n\n    #\n    # Set the email address to use when downloading a DXP bundle. This is used\n    # to create the authentication token. The email address must match the one\n    # registered for your DXP subscription.\n    #\n    # If you wish to create a new token without providing your email address and\n    # password in this file, you can create a token manually by navigating to\n    # your Liferay profile's Account Setting page and generating a token in the\n    # Authentication Tokens menu. Your token must reside in the \"~/.liferay\"\n    # folder.\n    #\n    #liferay.workspace.bundle.token.email.address=\n\n    #\n    # Set this to true to override the existing token with a newly generated\n    # token created by the \"createToken\" task. The default value is false.\n    #\n    #liferay.workspace.bundle.token.force=false\n\n    #\n    # Set the password to use when downloading a DXP bundle. This is used to\n    # create the authentication token. The password must match the one\n    # registered for your DXP subscription. See the\n    # \"liferay.workspace.bundle.token.email.address\" property for more details.\n    #\n    #liferay.workspace.bundle.token.password=\n\n    #\n    # Set the file to hold the Liferay bundle authentication token password.\n    # The default file value is \"~/.liferay/token\".\n    #\n    #liferay.workspace.bundle.token.password.file=\n\n    #\n    # Set the URL pointing to the bundle Zip to download. If the URL points to a\n    # DXP bundle (e.g., https://api.liferay.com/...), set the\n    # \"liferay.workspace.bundle.token.download\" property to true. The default\n    # value is the URL for Liferay Portal CE 7.0 GA7, Liferay Portal CE 7.1 GA4,\n    # or Liferay Portal CE 7.2 GA1, depending on the portal version the\n    # workspace is targeting.\n    #\nliferay.workspace.bundle.url = https://releases-cdn.liferay.com/portal/7.2.0-ga1/liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761.tar.gz\n\n    #\n    # Set the \"app.server.tomcat.version\" to match what is contained inside the\n    # Liferay bundle. Both the TestIntegrationPlugin and and LiferayExtPlugin\n    # rely on this version to match the bundled Tomcat version. If your\n    # configured bundle url points to a bundle with a different Tomcat version,\n    # set the property below to match that Tomcat version.\n    #\napp.server.tomcat.version = 9.0.17\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository in the\n    # root project. The default value is true.\n    #\n    #liferay.workspace.default.repository.enabled=true\n\n    #\n    # Set the Liferay Portal Docker image to create your container from. The\n    # default value points to Liferay Portal CE 7.2 GA1.\n    #\n    #liferay.workspace.docker.image.liferay=liferay/portal:7.2.0-ga1\n\n    #\n    # Set the environment with the settings appropriate for current development.\n    # The \"configs\" folder is used to hold different environments in the same\n    # workspace. You can organize environment settings and generate an\n    # environment installation with those settings. There are five\n    # environments: common, dev, docker, local, prod, and uat. The default value\n    # is \"local\".\n    #\n    #liferay.workspace.environment=local\n\n    #\n    # Set the folder that contains the Liferay bundle downloaded from the\n    # \"liferay.workspace.bundle.url\" property. The default value is \"bundles\".\n    #\n    #liferay.workspace.home.dir=bundles\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository for\n    # module/OSGi projects. The default value is true.\n    #\n    #liferay.workspace.modules.default.repository.enabled=true\n\n    #\n    # Set the folder that contains all Ext OSGi modules and Ext plugins. The\n    # default value is \"ext\".\n    #\n    #liferay.workspace.ext.dir=ext\n\n    #\n    # Set the folder that contains all module/OSGi projects. The default value\n    # is \"modules\".\n    #\n    #liferay.workspace.modules.dir=modules\n\n    #\n    # Set this to true to compile the JSP files in OSGi modules and have them\n    # added to the distributable Zip/Tar. The default value is false.\n    #\n    #liferay.workspace.modules.jsp.precompile.enabled=false\n\n    #\n    # Set the folder that contains the Plugins SDK environment. The default\n    # value is \"plugins-sdk\".\n    #\n    #liferay.workspace.plugins.sdk.dir=plugins-sdk\n\n    #\n    # Set the Liferay Portal or DXP version to develop and test against. By\n    # setting this property, it enables the target platform features such as\n    # dependency management and OSGi resolve tasks. Use the version that\n    # matches the Liferay Portal or DXP bundle version in this workspace.\n    #\n    # For a list of all available target platform versions, see\n    # https://bit.ly/2IkAwwW for Liferay Portal and https://bit.ly/2GIyfZF for\n    # Liferay DXP.\n    #\n    #liferay.workspace.target.platform.version=7.2.0\n\n    #\n    # Set this to true if you have enabled the Target Platform plugin (i.e. you\n    # have set the above property) and you want to apply the TargetPlatformIDE\n    # plugin to the root workspace project. This will cause all of the BOM\n    # artifacts jars and their Java sources to be indexed by your IDE. Setting\n    # this property to true can slow down your IDE's project synchronization.\n    #\n    # target.platform.index.sources=false\n\n    #\n    # Set the folder that contains Node.js-style theme projects. The default\n    # value is \"themes\".\n    #\n    #liferay.workspace.themes.dir=themes\n\n    #\n    # Set this to true to build the theme projects using the Liferay Portal\n    # Tools Theme Builder. The default value is false.\n    #\n    #liferay.workspace.themes.java.build=false\n\n    #\n    # Set the folder that contains classic WAR-style projects. The default value\n    # is \"wars\".\n    #\n    #liferay.workspace.wars.dir=wars\n\n\n    #\n    # Set the subscription key for Microsoft Translation integration.\n    # Subscription to the Translator Text Translation API on Microsoft Cognitive\n    # Services is required. Basic subscriptions, up to 2 million characters a\n    # month, are free. See\n    # http://docs.microsofttranslator.com/text-translate.html for more\n    # information.\n    #\nmicrosoft.translator.subscription.key = \nliferay.workspace.target.platform.version = 7.2.0\ntarget.platform.index.sources = false\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/.gitignore",
    "content": ".gradle/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/bnd.bnd",
    "content": "Bundle-Name: guestbook-api\nBundle-SymbolicName: com.liferay.docs.guestbook.api\nBundle-Version: 1.0.0\nExport-Package:\\\n\tcom.liferay.docs.guestbook.exception,\\\n\tcom.liferay.docs.guestbook.model,\\\n\tcom.liferay.docs.guestbook.service,\\\n\tcom.liferay.docs.guestbook.service.persistence\n-check: EXPORTS\n-includeresource: META-INF/service.xml=../guestbook-service/service.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryEmailException extends PortalException {\n\n\tpublic EntryEmailException() {\n\t}\n\n\tpublic EntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryMessageException extends PortalException {\n\n\tpublic EntryMessageException() {\n\t}\n\n\tpublic EntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryNameException extends PortalException {\n\n\tpublic EntryNameException() {\n\t}\n\n\tpublic EntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryEmailException extends PortalException {\n\n\tpublic GuestbookEntryEmailException() {\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryMessageException extends PortalException {\n\n\tpublic GuestbookEntryMessageException() {\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryNameException extends PortalException {\n\n\tpublic GuestbookEntryNameException() {\n\t}\n\n\tpublic GuestbookEntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookNameException extends PortalException {\n\n\tpublic GuestbookNameException() {\n\t}\n\n\tpublic GuestbookNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookEntryException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookEntryException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookEntryException() {\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookException() {\n\t}\n\n\tpublic NoSuchGuestbookException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/Guestbook.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookModel\n * @generated\n */\n@ImplementationClassName(\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\")\n@ProviderType\npublic interface Guestbook extends GuestbookModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<Guestbook, Long> GUESTBOOK_ID_ACCESSOR =\n\t\tnew Accessor<Guestbook, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(Guestbook guestbook) {\n\t\t\t\treturn guestbook.getGuestbookId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Guestbook> getTypeClass() {\n\t\t\t\treturn Guestbook.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntry.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookEntryModel\n * @generated\n */\n@ImplementationClassName(\n\t\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\"\n)\n@ProviderType\npublic interface GuestbookEntry extends GuestbookEntryModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<GuestbookEntry, Long> ENTRY_ID_ACCESSOR =\n\t\tnew Accessor<GuestbookEntry, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(GuestbookEntry guestbookEntry) {\n\t\t\t\treturn guestbookEntry.getEntryId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<GuestbookEntry> getTypeClass() {\n\t\t\t\treturn GuestbookEntry.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryModel\n\textends BaseModel<GuestbookEntry>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook entry model instance should use the {@link GuestbookEntry} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\tpublic long getEntryId();\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\tpublic void setEntryId(long entryId);\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getEmail();\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\tpublic void setEmail(String email);\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getMessage();\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\tpublic void setMessage(String message);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntrySoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookEntryServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntrySoap implements Serializable {\n\n\tpublic static GuestbookEntrySoap toSoapModel(GuestbookEntry model) {\n\t\tGuestbookEntrySoap soapModel = new GuestbookEntrySoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setEntryId(model.getEntryId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setEmail(model.getEmail());\n\t\tsoapModel.setMessage(model.getMessage());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(GuestbookEntry[] models) {\n\t\tGuestbookEntrySoap[] soapModels = new GuestbookEntrySoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[][] toSoapModels(\n\t\tGuestbookEntry[][] models) {\n\n\t\tGuestbookEntrySoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels =\n\t\t\t\tnew GuestbookEntrySoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookEntrySoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(\n\t\tList<GuestbookEntry> models) {\n\n\t\tList<GuestbookEntrySoap> soapModels = new ArrayList<GuestbookEntrySoap>(\n\t\t\tmodels.size());\n\n\t\tfor (GuestbookEntry model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookEntrySoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookEntrySoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetEntryId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic String getEmail() {\n\t\treturn _email;\n\t}\n\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn _message;\n\t}\n\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link GuestbookEntry}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryWrapper\n\textends BaseModelWrapper<GuestbookEntry>\n\timplements GuestbookEntry, ModelWrapper<GuestbookEntry> {\n\n\tpublic GuestbookEntryWrapper(GuestbookEntry guestbookEntry) {\n\t\tsuper(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"entryId\", getEntryId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"email\", getEmail());\n\t\tattributes.put(\"message\", getMessage());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong entryId = (Long)attributes.get(\"entryId\");\n\n\t\tif (entryId != null) {\n\t\t\tsetEntryId(entryId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tString email = (String)attributes.get(\"email\");\n\n\t\tif (email != null) {\n\t\t\tsetEmail(email);\n\t\t}\n\n\t\tString message = (String)attributes.get(\"message\");\n\n\t\tif (message != null) {\n\t\t\tsetMessage(message);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@Override\n\tpublic String getEmail() {\n\t\treturn model.getEmail();\n\t}\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn model.getEntryId();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@Override\n\tpublic String getMessage() {\n\t\treturn model.getMessage();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEmail(String email) {\n\t\tmodel.setEmail(email);\n\t}\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\tmodel.setEntryId(entryId);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\t@Override\n\tpublic void setMessage(String message) {\n\t\tmodel.setMessage(message);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookEntryWrapper wrap(GuestbookEntry guestbookEntry) {\n\t\treturn new GuestbookEntryWrapper(guestbookEntry);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic interface GuestbookModel\n\textends BaseModel<Guestbook>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook model instance should use the {@link Guestbook} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookSoap implements Serializable {\n\n\tpublic static GuestbookSoap toSoapModel(Guestbook model) {\n\t\tGuestbookSoap soapModel = new GuestbookSoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(Guestbook[] models) {\n\t\tGuestbookSoap[] soapModels = new GuestbookSoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[][] toSoapModels(Guestbook[][] models) {\n\t\tGuestbookSoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels = new GuestbookSoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookSoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(List<Guestbook> models) {\n\t\tList<GuestbookSoap> soapModels = new ArrayList<GuestbookSoap>(\n\t\t\tmodels.size());\n\n\t\tfor (Guestbook model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookSoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookSoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetGuestbookId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link Guestbook}.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic class GuestbookWrapper\n\textends BaseModelWrapper<Guestbook>\n\timplements Guestbook, ModelWrapper<Guestbook> {\n\n\tpublic GuestbookWrapper(Guestbook guestbook) {\n\t\tsuper(guestbook);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookWrapper wrap(Guestbook guestbook) {\n\t\treturn new GuestbookWrapper(guestbook);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for GuestbookEntry. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryLocalServiceUtil} to access the guestbook entry local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\tpublic GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId);\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException;\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId);\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows SystemException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> obc);\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\tpublic GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\taddGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().addGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\taddGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\t\tString message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tcreateGuestbookEntry(long entryId) {\n\n\t\treturn getService().createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(\n\t\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntry(long entryId) {\n\n\t\treturn getService().fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tint start, int end) {\n\n\t\treturn getService().getGuestbookEntries(start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId) {\n\n\t\treturn getService().getGuestbookEntries(groupId, guestbookId);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tString uuid, long companyId, int start, int end,\n\t\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int getGuestbookEntriesCount() {\n\t\treturn getService().getGuestbookEntriesCount();\n\t}\n\n\tpublic static int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn getService().getGuestbookEntriesCount(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tupdateGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().updateGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tupdateGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\t\tString email, String message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\tpublic static GuestbookEntryLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryLocalService.class);\n\n\t\tServiceTracker<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryLocalService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryLocalService}.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceWrapper\n\timplements GuestbookEntryLocalService,\n\t\t\t   ServiceWrapper<GuestbookEntryLocalService> {\n\n\tpublic GuestbookEntryLocalServiceWrapper(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry createGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookEntryLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry fetchGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookEntryLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(int start, int end) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(long groupId, long guestbookId) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry getGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntryLocalService getWrappedService() {\n\t\treturn _guestbookEntryLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for GuestbookEntry. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryServiceUtil} to access the guestbook entry remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookEntryService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookEntryService.class);\n\n\t\tServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryService, GuestbookEntryService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookEntryService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryService}.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceWrapper\n\timplements GuestbookEntryService, ServiceWrapper<GuestbookEntryService> {\n\n\tpublic GuestbookEntryServiceWrapper(\n\t\tGuestbookEntryService guestbookEntryService) {\n\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookEntryService getWrappedService() {\n\t\treturn _guestbookEntryService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookEntryService guestbookEntryService) {\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\tprivate GuestbookEntryService _guestbookEntryService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for Guestbook. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookLocalServiceUtil} to access the guestbook local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(Guestbook guestbook);\n\n\tpublic Guestbook addGuestbook(\n\t\t\tlong userId, String name, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId);\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook);\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException;\n\n\tpublic Guestbook deleteGuestbook(\n\t\t\tlong guestbookId, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbook(long guestbookId);\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException;\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(\n\t\tlong groupId, int start, int end, OrderByComparator<Guestbook> obc);\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(Guestbook guestbook);\n\n\tpublic Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().addGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbook(userId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbook(guestbookId);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().deleteGuestbook(guestbookId, serviceContext);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn getService().getGuestbooks(start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn getService().getGuestbooks(groupId);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int getGuestbooksCount() {\n\t\treturn getService().getGuestbooksCount();\n\t}\n\n\tpublic static int getGuestbooksCount(long groupId) {\n\t\treturn getService().getGuestbooksCount(groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().updateGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbook(\n\t\t\tuserId, guestbookId, name, serviceContext);\n\t}\n\n\tpublic static GuestbookLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookLocalService.class);\n\n\t\tServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookLocalService, GuestbookLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookLocalService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookLocalService}.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceWrapper\n\timplements GuestbookLocalService, ServiceWrapper<GuestbookLocalService> {\n\n\tpublic GuestbookLocalServiceWrapper(\n\t\tGuestbookLocalService guestbookLocalService) {\n\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.addGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.addGuestbook(\n\t\t\tuserId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbookId);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(\n\t\t\tguestbookId, serviceContext);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn _guestbookLocalService.getGuestbooksCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbooksCount(long groupId) {\n\t\treturn _guestbookLocalService.getGuestbooksCount(groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.updateGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.updateGuestbook(\n\t\t\tuserId, guestbookId, name, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookLocalService getWrappedService() {\n\t\treturn _guestbookLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookLocalService guestbookLocalService) {\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for Guestbook. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookServiceUtil} to access the guestbook remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookService, GuestbookService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookService.class);\n\n\t\tServiceTracker<GuestbookService, GuestbookService> serviceTracker =\n\t\t\tnew ServiceTracker<GuestbookService, GuestbookService>(\n\t\t\t\tbundle.getBundleContext(), GuestbookService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookService}.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceWrapper\n\timplements GuestbookService, ServiceWrapper<GuestbookService> {\n\n\tpublic GuestbookServiceWrapper(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookService getWrappedService() {\n\t\treturn _guestbookService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\tprivate GuestbookService _guestbookService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryPersistence\n\textends BasePersistence<GuestbookEntry> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryUtil} to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic void removeByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic void cacheResult(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic void cacheResult(java.util.List<GuestbookEntry> guestbookEntries);\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic GuestbookEntry create(long entryId);\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId);\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll();\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook entry service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookEntryPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().clearCache(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, GuestbookEntry> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static GuestbookEntry update(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().update(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static GuestbookEntry update(\n\t\tGuestbookEntry guestbookEntry, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbookEntry, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tentryId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic static GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tentryId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator,\n\t\t\tretrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_PrevAndNext(\n\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic static void removeByG_G(long groupId, long guestbookId) {\n\t\tgetPersistence().removeByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByG_G(long groupId, long guestbookId) {\n\t\treturn getPersistence().countByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic static void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().cacheResult(guestbookEntry);\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic static void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tgetPersistence().cacheResult(guestbookEntries);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static GuestbookEntry create(long entryId) {\n\t\treturn getPersistence().create(entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry remove(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().remove(entryId);\n\t}\n\n\tpublic static GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().updateImpl(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn getPersistence().fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookEntryPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence> _serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryPersistence.class);\n\n\t\tServiceTracker<GuestbookEntryPersistence, GuestbookEntryPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryPersistence.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookPersistence extends BasePersistence<Guestbook> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookUtil} to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(long groupId);\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic void removeByGroupId(long groupId);\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByGroupId(long groupId);\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic void cacheResult(Guestbook guestbook);\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic void cacheResult(java.util.List<Guestbook> guestbooks);\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic Guestbook create(long guestbookId);\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException;\n\n\tpublic Guestbook updateImpl(Guestbook guestbook);\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId);\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll();\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(Guestbook guestbook) {\n\t\tgetPersistence().clearCache(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, Guestbook> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static Guestbook update(Guestbook guestbook) {\n\t\treturn getPersistence().update(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static Guestbook update(\n\t\tGuestbook guestbook, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbook, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tguestbookId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic static Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tguestbookId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(long groupId) {\n\t\treturn getPersistence().findByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn getPersistence().findByGroupId(groupId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_First(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_First(\n\t\t\tgroupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_PrevAndNext(\n\t\t\tguestbookId, groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic static void removeByGroupId(long groupId) {\n\t\tgetPersistence().removeByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByGroupId(long groupId) {\n\t\treturn getPersistence().countByGroupId(groupId);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic static void cacheResult(Guestbook guestbook) {\n\t\tgetPersistence().cacheResult(guestbook);\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic static void cacheResult(List<Guestbook> guestbooks) {\n\t\tgetPersistence().cacheResult(guestbooks);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static Guestbook create(long guestbookId) {\n\t\treturn getPersistence().create(guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook remove(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().remove(guestbookId);\n\t}\n\n\tpublic static Guestbook updateImpl(Guestbook guestbook) {\n\t\treturn getPersistence().updateImpl(guestbook);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn getPersistence().fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic static List<Guestbook> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookPersistence.class);\n\n\t\tServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker<GuestbookPersistence, GuestbookPersistence>(\n\t\t\t\t\tbundle.getBundleContext(), GuestbookPersistence.class,\n\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/bnd.bnd",
    "content": "Bundle-Name: guestbook-service\nBundle-SymbolicName: com.liferay.docs.guestbook.service\nBundle-Version: 1.0.0\nLiferay-Require-SchemaVersion: 1.0.0\nLiferay-Service: true\n-dsannotations-options: inherit\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.lang\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.portal.aop.api\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\tcompileOnly project(\":modules:guestbook:guestbook-api\")\n}\n\nbuildService {\n\tapiDir = \"../guestbook-api/src/main/java\"\n}\n\ngroup = \"com.liferay.docs.guestbook\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/service.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"ds\" package-path=\"com.liferay.docs.guestbook\">\n\t<author>Liferay</author>\n\t<namespace>GB</namespace>\n\t\n\t<entity name=\"Guestbook\" local-service=\"true\" uuid=\"true\" remote-service=\"true\">\n\t\t\n\t\t<!-- Guestbook fields -->\n\n\t\t<column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\t\t\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<finder name=\"GroupId\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"groupId\" />\n\t\t</finder>\n\n\t</entity>\n\t\n    <entity name=\"GuestbookEntry\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n\n\t\t<!-- Guestbook Entry fields -->\n\n\t\t<column name=\"entryId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t<column name=\"email\" type=\"String\" />\n\t\t<column name=\"message\" type=\"String\" />\n\t\t<column name=\"guestbookId\" type=\"long\" />\n\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\t   \n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<order>\n\t\t\t<order-column name=\"createDate\" order-by=\"desc\" />\n\t\t</order>\n\n        <finder name=\"G_G\" return-type=\"Collection\">\n            <finder-column name=\"groupId\" />\n            <finder-column name=\"guestbookId\" />\n        </finder>\n    </entity>\n\n    <exceptions>\n        <exception>GuestbookEntryEmail</exception>\n        <exception>GuestbookEntryMessage</exception>\n        <exception>GuestbookEntryName</exception>\n        <exception>GuestbookName</exception>\n    </exceptions>\n</service-builder>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookBaseImpl\n\textends GuestbookModelImpl implements Guestbook {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookLocalServiceUtil.addGuestbook(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookLocalServiceUtil.updateGuestbook(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing Guestbook in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookCacheModel\n\timplements CacheModel<Guestbook>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookCacheModel guestbookCacheModel = (GuestbookCacheModel)obj;\n\n\t\tif (guestbookId == guestbookCacheModel.guestbookId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, guestbookId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(27);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic Guestbook toEntityModel() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookImpl.setGuestbookId(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tguestbookImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setName(name);\n\t\t}\n\n\t\tguestbookImpl.setGroupId(groupId);\n\t\tguestbookImpl.setCompanyId(companyId);\n\t\tguestbookImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookImpl.setStatus(status);\n\t\tguestbookImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long guestbookId;\n\tpublic String name;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryBaseImpl\n\textends GuestbookEntryModelImpl implements GuestbookEntry {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookEntryLocalServiceUtil.addGuestbookEntry(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookEntryLocalServiceUtil.updateGuestbookEntry(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing GuestbookEntry in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryCacheModel\n\timplements CacheModel<GuestbookEntry>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntryCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\t(GuestbookEntryCacheModel)obj;\n\n\t\tif (entryId == guestbookEntryCacheModel.entryId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, entryId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(33);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", entryId=\");\n\t\tsb.append(entryId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", email=\");\n\t\tsb.append(email);\n\t\tsb.append(\", message=\");\n\t\tsb.append(message);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEntityModel() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookEntryImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookEntryImpl.setEntryId(entryId);\n\n\t\tif (name == null) {\n\t\t\tguestbookEntryImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setName(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tguestbookEntryImpl.setEmail(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setEmail(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tguestbookEntryImpl.setMessage(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setMessage(message);\n\t\t}\n\n\t\tguestbookEntryImpl.setGuestbookId(guestbookId);\n\t\tguestbookEntryImpl.setGroupId(groupId);\n\t\tguestbookEntryImpl.setCompanyId(companyId);\n\t\tguestbookEntryImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookEntryImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookEntryImpl.setStatus(status);\n\t\tguestbookEntryImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookEntryImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tentryId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\t\temail = objectInput.readUTF();\n\t\tmessage = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(entryId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(message);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long entryId;\n\tpublic String name;\n\tpublic String email;\n\tpublic String message;\n\tpublic long guestbookId;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.GuestbookEntry<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryImpl extends GuestbookEntryBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook entry model instance should use the {@link com.liferay.docs.guestbook.model.GuestbookEntry} interface instead.\n\t */\n\tpublic GuestbookEntryImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.GuestbookEntryModel;\nimport com.liferay.docs.guestbook.model.GuestbookEntrySoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.DateUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookEntryModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookEntryModelImpl\n\textends BaseModelImpl<GuestbookEntry> implements GuestbookEntryModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_GuestbookEntry\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"entryId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"email\", Types.VARCHAR},\n\t\t{\"message\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"groupId\", Types.BIGINT}, {\"companyId\", Types.BIGINT},\n\t\t{\"userId\", Types.BIGINT}, {\"userName\", Types.VARCHAR},\n\t\t{\"createDate\", Types.TIMESTAMP}, {\"modifiedDate\", Types.TIMESTAMP},\n\t\t{\"status\", Types.INTEGER}, {\"statusByUserId\", Types.BIGINT},\n\t\t{\"statusByUserName\", Types.VARCHAR}, {\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"entryId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"email\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"message\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_GuestbookEntry (uuid_ VARCHAR(75) null,entryId LONG not null primary key,name VARCHAR(75) null,email VARCHAR(75) null,message VARCHAR(75) null,guestbookId LONG,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_GuestbookEntry\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbookEntry.createDate DESC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_GuestbookEntry.createDate DESC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 4L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 8L;\n\n\tpublic static final long CREATEDATE_COLUMN_BITMASK = 16L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static GuestbookEntry toModel(GuestbookEntrySoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbookEntry model = new GuestbookEntryImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setEntryId(soapModel.getEntryId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setEmail(soapModel.getEmail());\n\t\tmodel.setMessage(soapModel.getMessage());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<GuestbookEntry> toModels(\n\t\tGuestbookEntrySoap[] soapModels) {\n\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> models = new ArrayList<GuestbookEntry>(\n\t\t\tsoapModels.length);\n\n\t\tfor (GuestbookEntrySoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookEntryModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetEntryId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName,\n\t\t\t\tattributeGetterFunction.apply((GuestbookEntry)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<GuestbookEntry, Object>>\n\t\t\tattributeSetterBiConsumers = getAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<GuestbookEntry, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(GuestbookEntry)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<GuestbookEntry, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, GuestbookEntry>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbookEntry.class.getClassLoader(), GuestbookEntry.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<GuestbookEntry> constructor =\n\t\t\t\t(Constructor<GuestbookEntry>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<GuestbookEntry, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<GuestbookEntry, Object>>();\n\t\tMap<String, BiConsumer<GuestbookEntry, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<GuestbookEntry, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", GuestbookEntry::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUuid);\n\t\tattributeGetterFunctions.put(\"entryId\", GuestbookEntry::getEntryId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"entryId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setEntryId);\n\t\tattributeGetterFunctions.put(\"name\", GuestbookEntry::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setName);\n\t\tattributeGetterFunctions.put(\"email\", GuestbookEntry::getEmail);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"email\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setEmail);\n\t\tattributeGetterFunctions.put(\"message\", GuestbookEntry::getMessage);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"message\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setMessage);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"guestbookId\", GuestbookEntry::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"groupId\", GuestbookEntry::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", GuestbookEntry::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", GuestbookEntry::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", GuestbookEntry::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"createDate\", GuestbookEntry::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", GuestbookEntry::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", GuestbookEntry::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\",\n\t\t\t(BiConsumer<GuestbookEntry, Integer>)GuestbookEntry::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", GuestbookEntry::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)\n\t\t\t\tGuestbookEntry::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", GuestbookEntry::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)\n\t\t\t\tGuestbookEntry::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusDate\", GuestbookEntry::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getEmail() {\n\t\tif (_email == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _email;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getMessage() {\n\t\tif (_message == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _message;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_columnBitmask |= GUESTBOOKID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGuestbookId) {\n\t\t\t_setOriginalGuestbookId = true;\n\n\t\t\t_originalGuestbookId = _guestbookId;\n\t\t}\n\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getOriginalGuestbookId() {\n\t\treturn _originalGuestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_columnBitmask = -1L;\n\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), GuestbookEntry.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tguestbookEntryImpl.setUuid(getUuid());\n\t\tguestbookEntryImpl.setEntryId(getEntryId());\n\t\tguestbookEntryImpl.setName(getName());\n\t\tguestbookEntryImpl.setEmail(getEmail());\n\t\tguestbookEntryImpl.setMessage(getMessage());\n\t\tguestbookEntryImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookEntryImpl.setGroupId(getGroupId());\n\t\tguestbookEntryImpl.setCompanyId(getCompanyId());\n\t\tguestbookEntryImpl.setUserId(getUserId());\n\t\tguestbookEntryImpl.setUserName(getUserName());\n\t\tguestbookEntryImpl.setCreateDate(getCreateDate());\n\t\tguestbookEntryImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookEntryImpl.setStatus(getStatus());\n\t\tguestbookEntryImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookEntryImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookEntryImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(GuestbookEntry guestbookEntry) {\n\t\tint value = 0;\n\n\t\tvalue = DateUtil.compareTo(\n\t\t\tgetCreateDate(), guestbookEntry.getCreateDate());\n\n\t\tvalue = value * -1;\n\n\t\tif (value != 0) {\n\t\t\treturn value;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntry)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)obj;\n\n\t\tlong primaryKey = guestbookEntry.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl = this;\n\n\t\tguestbookEntryModelImpl._originalUuid = guestbookEntryModelImpl._uuid;\n\n\t\tguestbookEntryModelImpl._originalGuestbookId =\n\t\t\tguestbookEntryModelImpl._guestbookId;\n\n\t\tguestbookEntryModelImpl._setOriginalGuestbookId = false;\n\n\t\tguestbookEntryModelImpl._originalGroupId =\n\t\t\tguestbookEntryModelImpl._groupId;\n\n\t\tguestbookEntryModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookEntryModelImpl._originalCompanyId =\n\t\t\tguestbookEntryModelImpl._companyId;\n\n\t\tguestbookEntryModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookEntryModelImpl._setModifiedDate = false;\n\n\t\tguestbookEntryModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<GuestbookEntry> toCacheModel() {\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\tnew GuestbookEntryCacheModel();\n\n\t\tguestbookEntryCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookEntryCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.entryId = getEntryId();\n\n\t\tguestbookEntryCacheModel.name = getName();\n\n\t\tString name = guestbookEntryCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.name = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.email = getEmail();\n\n\t\tString email = guestbookEntryCacheModel.email;\n\n\t\tif ((email != null) && (email.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.email = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.message = getMessage();\n\n\t\tString message = guestbookEntryCacheModel.message;\n\n\t\tif ((message != null) && (message.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.message = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookEntryCacheModel.groupId = getGroupId();\n\n\t\tguestbookEntryCacheModel.companyId = getCompanyId();\n\n\t\tguestbookEntryCacheModel.userId = getUserId();\n\n\t\tguestbookEntryCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookEntryCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookEntryCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookEntryCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookEntryCacheModel.status = getStatus();\n\n\t\tguestbookEntryCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookEntryCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookEntryCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookEntryCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookEntryCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, GuestbookEntry>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _originalGuestbookId;\n\tprivate boolean _setOriginalGuestbookId;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate GuestbookEntry _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.Guestbook<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookImpl extends GuestbookBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook model instance should use the {@link com.liferay.docs.guestbook.model.Guestbook} interface instead.\n\t */\n\tpublic GuestbookImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookModel;\nimport com.liferay.docs.guestbook.model.GuestbookSoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookModelImpl\n\textends BaseModelImpl<Guestbook> implements GuestbookModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_Guestbook\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"groupId\", Types.BIGINT},\n\t\t{\"companyId\", Types.BIGINT}, {\"userId\", Types.BIGINT},\n\t\t{\"userName\", Types.VARCHAR}, {\"createDate\", Types.TIMESTAMP},\n\t\t{\"modifiedDate\", Types.TIMESTAMP}, {\"status\", Types.INTEGER},\n\t\t{\"statusByUserId\", Types.BIGINT}, {\"statusByUserName\", Types.VARCHAR},\n\t\t{\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_Guestbook (uuid_ VARCHAR(75) null,guestbookId LONG not null primary key,name VARCHAR(75) null,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_Guestbook\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbook.guestbookId ASC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_Guestbook.guestbookId ASC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 4L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 8L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static Guestbook toModel(GuestbookSoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbook model = new GuestbookImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<Guestbook> toModels(GuestbookSoap[] soapModels) {\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> models = new ArrayList<Guestbook>(soapModels.length);\n\n\t\tfor (GuestbookSoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetGuestbookId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName, attributeGetterFunction.apply((Guestbook)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<Guestbook, Object>> attributeSetterBiConsumers =\n\t\t\tgetAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<Guestbook, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(Guestbook)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<Guestbook, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<Guestbook, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, Guestbook>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbook.class.getClassLoader(), Guestbook.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<Guestbook> constructor =\n\t\t\t\t(Constructor<Guestbook>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<Guestbook, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<Guestbook, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<Guestbook, Object>>();\n\t\tMap<String, BiConsumer<Guestbook, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<Guestbook, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", Guestbook::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\", (BiConsumer<Guestbook, String>)Guestbook::setUuid);\n\t\tattributeGetterFunctions.put(\"guestbookId\", Guestbook::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"name\", Guestbook::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\", (BiConsumer<Guestbook, String>)Guestbook::setName);\n\t\tattributeGetterFunctions.put(\"groupId\", Guestbook::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\", (BiConsumer<Guestbook, Long>)Guestbook::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", Guestbook::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\", (BiConsumer<Guestbook, Long>)Guestbook::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", Guestbook::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\", (BiConsumer<Guestbook, Long>)Guestbook::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", Guestbook::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\", (BiConsumer<Guestbook, String>)Guestbook::setUserName);\n\t\tattributeGetterFunctions.put(\"createDate\", Guestbook::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", Guestbook::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", Guestbook::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\", (BiConsumer<Guestbook, Integer>)Guestbook::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", Guestbook::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", Guestbook::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<Guestbook, String>)Guestbook::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\"statusDate\", Guestbook::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), Guestbook.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic Guestbook toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tguestbookImpl.setUuid(getUuid());\n\t\tguestbookImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookImpl.setName(getName());\n\t\tguestbookImpl.setGroupId(getGroupId());\n\t\tguestbookImpl.setCompanyId(getCompanyId());\n\t\tguestbookImpl.setUserId(getUserId());\n\t\tguestbookImpl.setUserName(getUserName());\n\t\tguestbookImpl.setCreateDate(getCreateDate());\n\t\tguestbookImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookImpl.setStatus(getStatus());\n\t\tguestbookImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(Guestbook guestbook) {\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() < primaryKey) {\n\t\t\treturn -1;\n\t\t}\n\t\telse if (getPrimaryKey() > primaryKey) {\n\t\t\treturn 1;\n\t\t}\n\t\telse {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof Guestbook)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbook guestbook = (Guestbook)obj;\n\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookModelImpl guestbookModelImpl = this;\n\n\t\tguestbookModelImpl._originalUuid = guestbookModelImpl._uuid;\n\n\t\tguestbookModelImpl._originalGroupId = guestbookModelImpl._groupId;\n\n\t\tguestbookModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookModelImpl._originalCompanyId = guestbookModelImpl._companyId;\n\n\t\tguestbookModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookModelImpl._setModifiedDate = false;\n\n\t\tguestbookModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<Guestbook> toCacheModel() {\n\t\tGuestbookCacheModel guestbookCacheModel = new GuestbookCacheModel();\n\n\t\tguestbookCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookCacheModel.name = getName();\n\n\t\tString name = guestbookCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookCacheModel.name = null;\n\t\t}\n\n\t\tguestbookCacheModel.groupId = getGroupId();\n\n\t\tguestbookCacheModel.companyId = getCompanyId();\n\n\t\tguestbookCacheModel.userId = getUserId();\n\n\t\tguestbookCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookCacheModel.status = getStatus();\n\n\t\tguestbookCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, Guestbook>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate Guestbook _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookEntryLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\tguestbookEntry.setNew(true);\n\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.create(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbookEntry.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId) {\n\n\t\treturn guestbookEntryPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookEntryLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbookEntry.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<GuestbookEntry>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(GuestbookEntry guestbookEntry)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbookEntry);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryLocalService.deleteGuestbookEntry(\n\t\t\t(GuestbookEntry)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end) {\n\t\treturn guestbookEntryPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn guestbookEntryPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryLocalService = (GuestbookEntryLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\tprotected GuestbookEntryLocalService guestbookEntryLocalService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl\n * @generated\n */\npublic abstract class GuestbookEntryServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookEntryService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryService = (GuestbookEntryService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookEntryLocalService\n\t\tguestbookEntryLocalService;\n\n\tprotected GuestbookEntryService guestbookEntryService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook addGuestbook(Guestbook guestbook) {\n\t\tguestbook.setNew(true);\n\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.create(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.remove(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.remove(guestbook);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbook.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic Guestbook fetchGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\t\treturn guestbookPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.findByPrimaryKey(guestbookId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\n\t\t\t\"guestbookId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbook.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<Guestbook>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(Guestbook guestbook)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbook);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookLocalService.deleteGuestbook((Guestbook)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn guestbookPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooks(int start, int end) {\n\t\treturn guestbookPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn guestbookPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook updateGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookLocalService = (GuestbookLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\tprotected GuestbookLocalService guestbookLocalService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl\n * @generated\n */\npublic abstract class GuestbookServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookService = (GuestbookService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookLocalService\n\t\tguestbookLocalService;\n\n\tprotected GuestbookService guestbookService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntry</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.Guestbook</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.docs.guestbook.exception.GuestbookEntryEmailException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryMessageException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryNameException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\n\n/**\n * The implementation of the guestbook entry local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\", service = AopService.class)\npublic class GuestbookEntryLocalServiceImpl extends GuestbookEntryLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n\t * via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code\n\t * >.\n\t */\n\n\tpublic GuestbookEntry addGuestbookEntry(long userId, long guestbookId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tlong entryId = counterLocalService.increment();\n\n\t\tGuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\n\t\tentry.setUuid(serviceContext.getUuid());\n\t\tentry.setUserId(userId);\n\t\tentry.setGroupId(groupId);\n\t\tentry.setCompanyId(user.getCompanyId());\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setCreateDate(serviceContext.getCreateDate(now));\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\t\tentry.setGuestbookId(guestbookId);\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Calls to other Liferay frameworks go here\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry updateGuestbookEntry(long userId, long guestbookId,\n\t\t\tlong entryId, String name, String email, String message,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException {\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tentry.setUserId(userId);\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Integrate with Liferay frameworks here.\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry entry)\n\t\tthrows PortalException {\n\n\t\tguestbookEntryPersistence.remove(entry);\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId) throws PortalException {\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\treturn deleteGuestbookEntry(entry);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end) throws SystemException {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end, OrderByComparator<GuestbookEntry> obc) {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend, obc);\n\t}\n\n\tpublic GuestbookEntry getGuestbookEntry(long entryId) throws PortalException {\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.countByG_G(groupId, guestbookId);\n\t}\n\n\tprotected void validate(String name, String email, String entry)\n\t\tthrows PortalException {\n\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookEntryNameException();\n\t\t}\n\n\t\tif (!Validator.isEmailAddress(email)) {\n\t\t\tthrow new GuestbookEntryEmailException();\n\t\t}\n\n\t\tif (Validator.isNull(entry)) {\n\t\t\tthrow new GuestbookEntryMessageException();\n\t\t}\n\t}\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook entry remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookEntryService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=GuestbookEntry\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookEntryServiceImpl extends GuestbookEntryServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> to access the guestbook entry remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.exception.GuestbookNameException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.base.GuestbookLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\n\n/**\n * The implementation of the guestbook local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.Guestbook\", service = AopService.class)\npublic class GuestbookLocalServiceImpl extends GuestbookLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code> via\n\t * injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\tpublic Guestbook addGuestbook(long userId, String name,\n\t\t\tServiceContext serviceContext) throws PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name);\n\n\t\tlong guestbookId = counterLocalService.increment();\n\n\t\tGuestbook guestbook = guestbookPersistence.create(guestbookId);\n\n\t\tguestbook.setUuid(serviceContext.getUuid());\n\t\tguestbook.setUserId(userId);\n\t\tguestbook.setGroupId(groupId);\n\t\tguestbook.setCompanyId(user.getCompanyId());\n\t\tguestbook.setUserName(user.getFullName());\n\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tguestbook.setName(name);\n\t\tguestbook.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookPersistence.update(guestbook);\n\n\t\treturn guestbook;\n\t}\n\n\tpublic Guestbook updateGuestbook(long userId, long guestbookId,\n        String name, ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Date now = new Date();\n\n            validate(name);\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            User user = userLocalService.getUser(userId);\n\n            guestbook.setUserId(userId);\n            guestbook.setUserName(user.getFullName());\n            guestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n            guestbook.setName(name);\n            guestbook.setExpandoBridgeAttributes(serviceContext);\n\n            guestbookPersistence.update(guestbook);\n\n            return guestbook;\n    }\n\n\tpublic Guestbook deleteGuestbook(long guestbookId,\n                    ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            List<GuestbookEntry> entries = _guestbookEntryLocalService.getGuestbookEntries(\n                            serviceContext.getScopeGroupId(), guestbookId);\n\n            for (GuestbookEntry entry : entries) {\n                    _guestbookEntryLocalService.deleteGuestbookEntry(entry.getEntryId());\n            }\n\n            guestbook = deleteGuestbook(guestbook);\n\n            return guestbook;\n    }\n\n\tpublic List<Guestbook> getGuestbooks(long groupId) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end,\n\t\t\tOrderByComparator<Guestbook> obc) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end, obc);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end);\n\t}\n\n\tpublic int getGuestbooksCount(long groupId) {\n\n\t\treturn guestbookPersistence.countByGroupId(groupId);\n\t}\n\n\tprotected void validate(String name) throws PortalException {\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookNameException();\n\t\t}\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=Guestbook\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookServiceImpl extends GuestbookServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> to access the guestbook remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookEntryPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookEntryPersistence.class)\n@ProviderType\npublic class GuestbookEntryPersistenceImpl\n\textends BasePersistenceImpl<GuestbookEntry>\n\timplements GuestbookEntryPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookEntryUtil</code> to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookEntryImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_First(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_Last(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbookEntry.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof GuestbookEntry) {\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbookEntry.getUuid()) ||\n\t\t\t\t(groupId != guestbookEntry.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<GuestbookEntry> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbookEntry guestbookEntry = list.get(0);\n\n\t\t\t\t\tresult = guestbookEntry;\n\n\t\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (GuestbookEntry)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\t@Override\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbookEntry.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\ttrue);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\tfalse);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_C_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tlong companyId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbookEntry.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByG_G;\n\tprivate FinderPath _finderPathWithoutPaginationFindByG_G;\n\tprivate FinderPath _finderPathCountByG_G;\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(long groupId, long guestbookId) {\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn findByG_G(groupId, guestbookId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {groupId, guestbookId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tgroupId, guestbookId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif ((groupId != guestbookEntry.getGroupId()) ||\n\t\t\t\t\t\t(guestbookId != guestbookEntry.getGuestbookId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByG_G(groupId, guestbookId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByG_G_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tlong guestbookId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(guestbookId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\t@Override\n\tpublic void removeByG_G(long groupId, long guestbookId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByG_G(\n\t\t\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByG_G(long groupId, long guestbookId) {\n\t\tFinderPath finderPath = _finderPathCountByG_G;\n\n\t\tObject[] finderArgs = new Object[] {groupId, guestbookId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_G_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_G_G_GUESTBOOKID_2 =\n\t\t\"guestbookEntry.guestbookId = ?\";\n\n\tpublic GuestbookEntryPersistenceImpl() {\n\t\tsetModelClass(GuestbookEntry.class);\n\n\t\tsetModelImplClass(GuestbookEntryImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\t@Override\n\tpublic void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {\n\t\t\t\tguestbookEntry.getUuid(), guestbookEntry.getGroupId()\n\t\t\t},\n\t\t\tguestbookEntry);\n\n\t\tguestbookEntry.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\t@Override\n\tpublic void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbook entries.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookEntryImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook entry.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(GuestbookEntry guestbookEntry) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookEntryModelImpl)guestbookEntry, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<GuestbookEntry> guestbookEntries) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache(\n\t\t\t\t(GuestbookEntryModelImpl)guestbookEntry, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookEntryModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic GuestbookEntry create(long entryId) {\n\t\tGuestbookEntry guestbookEntry = new GuestbookEntryImpl();\n\n\t\tguestbookEntry.setNew(true);\n\t\tguestbookEntry.setPrimaryKey(entryId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbookEntry.setUuid(uuid);\n\n\t\tguestbookEntry.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn remove((Serializable)entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\tGuestbookEntryImpl.class, primaryKey);\n\n\t\t\tif (guestbookEntry == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbookEntry);\n\t\t}\n\t\tcatch (NoSuchGuestbookEntryException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected GuestbookEntry removeImpl(GuestbookEntry guestbookEntry) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbookEntry)) {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\t\tGuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbookEntry != null) {\n\t\t\t\tsession.delete(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbookEntry != null) {\n\t\t\tclearCache(guestbookEntry);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t@Override\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\tboolean isNew = guestbookEntry.isNew();\n\n\t\tif (!(guestbookEntry instanceof GuestbookEntryModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbookEntry.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(\n\t\t\t\t\tguestbookEntry);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbookEntry proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom GuestbookEntry implementation \" +\n\t\t\t\t\tguestbookEntry.getClass());\n\t\t}\n\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl =\n\t\t\t(GuestbookEntryModelImpl)guestbookEntry;\n\n\t\tif (Validator.isNull(guestbookEntry.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbookEntry.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbookEntry.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookEntryModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setModifiedDate(\n\t\t\t\t\tserviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbookEntry.isNew()) {\n\t\t\t\tsession.save(guestbookEntry);\n\n\t\t\t\tguestbookEntry.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.merge(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByG_G.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry, false);\n\n\t\tclearUniqueFindersCache(guestbookEntryModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookEntryModelImpl);\n\n\t\tguestbookEntry.resetOriginalValues();\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn findByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn fetchByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOKENTRY;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (GuestbookEntry guestbookEntry : findAll()) {\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOKENTRY);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"entryId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOKENTRY;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookEntryModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook entry persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookEntryModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookEntryModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.COMPANYID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GUESTBOOKID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookEntryImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.GuestbookEntry\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbookEntry.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No GuestbookEntry exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No GuestbookEntry exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookEntryPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.impl.GuestbookImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookPersistence.class)\n@ProviderType\npublic class GuestbookPersistenceImpl\n\textends BasePersistenceImpl<Guestbook> implements GuestbookPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookUtil</code> to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_First(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_Last(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbook.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbook == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof Guestbook) {\n\t\t\tGuestbook guestbook = (Guestbook)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbook.getUuid()) ||\n\t\t\t\t(groupId != guestbook.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<Guestbook> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbook guestbook = list.get(0);\n\n\t\t\t\t\tresult = guestbook;\n\n\t\t\t\t\tcacheResult(guestbook);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (Guestbook)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbook);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbook.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_C_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbook.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByGroupId;\n\tprivate FinderPath _finderPathWithoutPaginationFindByGroupId;\n\tprivate FinderPath _finderPathCountByGroupId;\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId) {\n\t\treturn findByGroupId(\n\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId, int start, int end) {\n\t\treturn findByGroupId(groupId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByGroupId(groupId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif ((groupId != guestbook.getGroupId())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_First(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByGroupId(groupId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_Last(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByGroupId(groupId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByGroupId(\n\t\t\tgroupId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByGroupId_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\t@Override\n\tpublic void removeByGroupId(long groupId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByGroupId(\n\t\t\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByGroupId(long groupId) {\n\t\tFinderPath finderPath = _finderPathCountByGroupId;\n\n\t\tObject[] finderArgs = new Object[] {groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_GROUPID_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tpublic GuestbookPersistenceImpl() {\n\t\tsetModelClass(Guestbook.class);\n\n\t\tsetModelImplClass(GuestbookImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\t@Override\n\tpublic void cacheResult(Guestbook guestbook) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {guestbook.getUuid(), guestbook.getGroupId()},\n\t\t\tguestbook);\n\n\t\tguestbook.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\t@Override\n\tpublic void cacheResult(List<Guestbook> guestbooks) {\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\t\tguestbook.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbook);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbooks.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(Guestbook guestbook) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<Guestbook> guestbooks) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\tguestbook.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic Guestbook create(long guestbookId) {\n\t\tGuestbook guestbook = new GuestbookImpl();\n\n\t\tguestbook.setNew(true);\n\t\tguestbook.setPrimaryKey(guestbookId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbook.setUuid(uuid);\n\n\t\tguestbook.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException {\n\t\treturn remove((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook guestbook = (Guestbook)session.get(\n\t\t\t\tGuestbookImpl.class, primaryKey);\n\n\t\t\tif (guestbook == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbook);\n\t\t}\n\t\tcatch (NoSuchGuestbookException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected Guestbook removeImpl(Guestbook guestbook) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbook)) {\n\t\t\t\tguestbook = (Guestbook)session.get(\n\t\t\t\t\tGuestbookImpl.class, guestbook.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbook != null) {\n\t\t\t\tsession.delete(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbook != null) {\n\t\t\tclearCache(guestbook);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t@Override\n\tpublic Guestbook updateImpl(Guestbook guestbook) {\n\t\tboolean isNew = guestbook.isNew();\n\n\t\tif (!(guestbook instanceof GuestbookModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbook.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(guestbook);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbook proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom Guestbook implementation \" +\n\t\t\t\t\tguestbook.getClass());\n\t\t}\n\n\t\tGuestbookModelImpl guestbookModelImpl = (GuestbookModelImpl)guestbook;\n\n\t\tif (Validator.isNull(guestbook.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbook.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbook.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbook.isNew()) {\n\t\t\t\tsession.save(guestbook);\n\n\t\t\t\tguestbook.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook = (Guestbook)session.merge(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getUuid(),\n\t\t\t\t\tguestbookModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByGroupId.\n\t\t\t\t\t getColumnBitmask()) != 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook, false);\n\n\t\tclearUniqueFindersCache(guestbookModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookModelImpl);\n\n\t\tguestbook.resetOriginalValues();\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbook == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\treturn findByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn fetchByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOK);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOK;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (Guestbook guestbook : findAll()) {\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOK);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"guestbookId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOK;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.COMPANYID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {Long.class.getName()},\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByGroupId\",\n\t\t\tnew String[] {Long.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.Guestbook\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK =\n\t\t\"SELECT guestbook FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK_WHERE =\n\t\t\"SELECT guestbook FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK_WHERE =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbook.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No Guestbook exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No Guestbook exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/constants/GBPersistenceConstants.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl.constants;\n\nimport com.liferay.petra.string.StringBundler;\n\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.Constants;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * @author Liferay\n * @generated\n */\n@Component(immediate = true, service = {})\npublic class GBPersistenceConstants {\n\n\tpublic static final String BUNDLE_SYMBOLIC_NAME =\n\t\t\"com.liferay.docs.guestbook.service\";\n\n\tpublic static final String ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER =\n\t\t\"(origin.bundle.symbolic.name=\" + BUNDLE_SYMBOLIC_NAME + \")\";\n\n\t@Activate\n\tprotected void activate(BundleContext bundleContext) {\n\t\tBundle bundle = bundleContext.getBundle();\n\n\t\tif (!BUNDLE_SYMBOLIC_NAME.equals(bundle.getSymbolicName())) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\tStringBundler.concat(\n\t\t\t\t\t\"Incorrect \", Constants.BUNDLE_SYMBOLICNAME, \" for bundle \",\n\t\t\t\t\tbundle.getSymbolicName()));\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/module-hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">\n\n<hibernate-mapping auto-import=\"false\" default-lazy=\"false\">\n\t<import class=\"com.liferay.docs.guestbook.model.Guestbook\" />\n\t<import class=\"com.liferay.docs.guestbook.model.GuestbookEntry\" />\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\" table=\"GB_Guestbook\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\" table=\"GB_GuestbookEntry\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"entryId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"email\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"message\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n</hibernate-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/portlet-model-hints.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<model-hints>\n\t<model name=\"com.liferay.docs.guestbook.model.Guestbook\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n\t<model name=\"com.liferay.docs.guestbook.model.GuestbookEntry\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"entryId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"email\" type=\"String\" />\n\t\t<field name=\"message\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n</model-hints>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/indexes.sql",
    "content": "create index IX_9294AD47 on GB_Guestbook (groupId);\ncreate index IX_9314A9F7 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_EDD4239 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], groupId);\n\ncreate index IX_E84D72FD on GB_GuestbookEntry (groupId, guestbookId);\ncreate index IX_CC265FEF on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_4A541631 on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], groupId);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/sequences.sql",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/tables.sql",
    "content": "create table GB_Guestbook (\n\tuuid_ VARCHAR(75) null,\n\tguestbookId LONG not null primary key,\n\tname VARCHAR(75) null,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);\n\ncreate table GB_GuestbookEntry (\n\tuuid_ VARCHAR(75) null,\n\tentryId LONG not null primary key,\n\tname VARCHAR(75) null,\n\temail VARCHAR(75) null,\n\tmessage VARCHAR(75) null,\n\tguestbookId LONG,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/service.properties",
    "content": "##\n## Properties Override\n##\n\n    #\n    # Specify where to get the overridden properties. Updates should not be made\n    # on this file but on the overridden version of this file.\n    #\n    include-and-override=service-ext.properties\n\n##\n## Build\n##\n\n    build.namespace=GB\n    build.number=5\n    build.date=1567633317058"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/.gitignore",
    "content": ".gradle/\n.sass-cache/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/bnd.bnd",
    "content": "Bundle-Name: guestbook-web\nBundle-SymbolicName: com.liferay.docs.guestbook.portlet\nBundle-Version: 1.0.0\nExport-Package: com.liferay.docs.guestbook.portlet.constants"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.application.list.api\"\n\tcompileOnly group: \"javax.portlet\", name: \"portlet-api\"\n\tcompileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n\tcompileOnly group: \"jstl\", name: \"jstl\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\n    compileOnly project(\":modules:guestbook:guestbook-api\")\n    compileOnly project(\":modules:guestbook:guestbook-service\")\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/application/list/GuestbookAdminPanelApp.java",
    "content": "package com.liferay.docs.guestbook.application.list;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.application.list.BasePanelApp;\nimport com.liferay.application.list.PanelApp;\nimport com.liferay.application.list.constants.PanelCategoryKeys;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.portal.kernel.model.Portlet;\n\n@Component(\n        immediate = true,\n        property = {\n            \"panel.app.order:Integer=300\",\n            \"panel.category.key=\" + PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT\n        },\n        service = PanelApp.class\n    )\npublic class GuestbookAdminPanelApp extends BasePanelApp {\n\n        @Override\n        public String getPortletId() {\n            return GuestbookPortletKeys.GUESTBOOK_ADMIN;\n        }\n\n        @Override\n        @Reference(\n            target = \"(javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN + \")\",\n            unbind = \"-\"\n        )\n        public void setPortlet(Portlet portlet) {\n            super.setPortlet(portlet);\n        }\n\n    }\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/constants/GuestbookPortletKeys.java",
    "content": "package com.liferay.docs.guestbook.constants;\n\n/**\n * @author sezovr\n */\npublic class GuestbookPortletKeys {\n\n\tpublic static final String GUESTBOOK =\n\t\t\"com_liferay_docs_guestbook_portlet_GuestbookPortlet\";\n\n\tpublic static final String GUESTBOOK_ADMIN =\n\t\t  \"com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet\";\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookAdminPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.servlet.SessionErrors;\nimport com.liferay.portal.kernel.servlet.SessionMessages;\nimport com.liferay.portal.kernel.util.ParamUtil;\n@Component(\n        immediate = true,\n        property = {\n                \"com.liferay.portlet.display-category=category.hidden\",\n                \"com.liferay.portlet.scopeable=true\",\n                \"javax.portlet.display-name=Guestbooks\",\n                \"javax.portlet.expiration-cache=0\",\n                \"javax.portlet.init-param.portlet-title-based-navigation=true\",\n                \"javax.portlet.init-param.template-path=/\",\n                \"javax.portlet.init-param.view-template=/guestbook_admin/view.jsp\",\n                \"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN,\n                \"javax.portlet.resource-bundle=content.Language\",\n                \"javax.portlet.security-role-ref=administrator\",\n                \"javax.portlet.supports.mime-type=text/html\",\n                \"com.liferay.portlet.add-default-resource=true\"\n        },\n        service = Portlet.class\n    )\npublic class GuestbookAdminPortlet extends MVCPortlet {\n\npublic void addGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n\n        try {\n            _guestbookLocalService.addGuestbook(\n                serviceContext.getUserId(), name, serviceContext);\n            \n            SessionMessages.add(request, \"guestbookAdded\");\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n            \n            SessionErrors.add(request, pe.getClass().getName());\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n\n    public void updateGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.updateGuestbook(\n                serviceContext.getUserId(), guestbookId, name, serviceContext);\n            \n            SessionMessages.add(request,  \"guestbookUpdated\");\n\n        } catch (PortalException pe) {\n        \n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            SessionErrors.add(request, pe.getClass().getName());\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n    \n    public void deleteGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.deleteGuestbook(guestbookId, serviceContext);\n            \n            SessionMessages.add(request,  \"guestbookDeleted\");\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            SessionErrors.add(request, pe.getClass().getName());\n        }\n    }\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\nimport javax.portlet.PortletException;\nimport javax.portlet.RenderRequest;\nimport javax.portlet.RenderResponse;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.servlet.SessionErrors;\nimport com.liferay.portal.kernel.servlet.SessionMessages;\nimport com.liferay.portal.kernel.util.ParamUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\n/**\n * @author sezovr\n */\n@Component(immediate = true, property = { \"com.liferay.portlet.display-category=category.social\",\n\t\t\"com.liferay.portlet.header-portlet-css=/css/main.css\", \n\t\t\"com.liferay.portlet.instanceable=false\",\n\t\t\"com.liferay.portlet.scopeable=true\", \n\t\t\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK,\n\t\t\"javax.portlet.display-name=Guestbook\", \n\t\t\"javax.portlet.expiration-cache=0\",\n\t\t\"javax.portlet.init-param.template-path=/\", \n\t\t\"javax.portlet.init-param.view-template=/guestbook/view.jsp\",\n\t\t\"javax.portlet.resource-bundle=content.Language\", \n\t\t\"javax.portlet.security-role-ref=power-user,user\",\n\t\t\"javax.portlet.supports.mime-type=text/html\" }, \n\tservice = Portlet.class)\npublic class GuestbookPortlet extends MVCPortlet {\n\n\tpublic void addEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\n\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(GuestbookEntry.class.getName(), request);\n\n\t\tString userName = ParamUtil.getString(request, \"name\");\n\t\tString email = ParamUtil.getString(request, \"email\");\n\t\tString message = ParamUtil.getString(request, \"message\");\n\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\n\t\tif (entryId > 0) {\n\n\t\t\ttry {\n\n\t\t\t\t_guestbookEntryLocalService.updateGuestbookEntry(serviceContext.getUserId(), guestbookId, entryId, userName,\n\t\t\t\t\t\temail, message, serviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(e);\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\n\t\t} else {\n\n\t\t\ttry {\n\t\t\t\t_guestbookEntryLocalService.addGuestbookEntry(serviceContext.getUserId(), guestbookId, userName, email, message,\n\t\t\t\t\t\tserviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t\tSessionMessages.add(request, \"entryAdded\");\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tSessionErrors.add(request, e.getClass().getName());\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic void deleteEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\t\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\t\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\tGuestbookEntry.class.getName(), request);\n\n\t\t\ttry {\n\n\t\t\t\tresponse.setRenderParameter(\n\t\t\t\t\t\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t\t_guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\n\t\t\t\tSessionMessages.add(request, \"entryDeleted\");\n\n\t\t\t}\n\n\t\t\tcatch (Exception e) {\n\t\t\t\tLogger.getLogger(GuestbookPortlet.class.getName()).log(\n\t\t\t\t\tLevel.SEVERE, null, e);\n\n\t\t\tSessionErrors.add(request, e.getClass().getName());\n\t\t\t}\n\t}\n\n\t@Override\n\tpublic void render(RenderRequest renderRequest, RenderResponse renderResponse)\n\t\t\tthrows IOException, PortletException {\n\n\t\t\ttry {\n\t\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\t\tGuestbook.class.getName(), renderRequest);\n\n\t\t\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\t\t\tlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n\t\t\t\tList<Guestbook> guestbooks = _guestbookLocalService.getGuestbooks(\n\t\t\t\t\tgroupId);\n\n\t\t\t\tif (guestbooks.isEmpty()) {\n\t\t\t\t\tGuestbook guestbook = _guestbookLocalService.addGuestbook(\n\t\t\t\t\t\tserviceContext.getUserId(), \"Main\", serviceContext);\n\n\t\t\t\t\tguestbookId = guestbook.getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\tif (guestbookId == 0) {\n\t\t\t\t\tguestbookId = guestbooks.get(0).getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\trenderRequest.setAttribute(\"guestbookId\", guestbookId);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new PortletException(e);\n\t\t\t}\n\n\t\t\tsuper.render(renderRequest, renderResponse);\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/css/main.scss",
    "content": ".guestbook-web {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/edit_entry.jsp",
    "content": "<%@include file=\"../init.jsp\" %>\n\n<% \n\nlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\nGuestbookEntry entry = null;\nif (entryId > 0) {\n  entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n}\n\nlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n%>\n\n<portlet:renderURL var=\"viewURL\">\n\t<portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\"></portlet:param>\n</portlet:renderURL>\n\n<portlet:actionURL name=\"addEntry\" var=\"addEntryURL\"></portlet:actionURL>\n\n<aui:form action=\"<%= addEntryURL %>\" name=\"<portlet:namespace />fm\">\n\n<aui:model-context bean=\"<%= entry %>\" model=\"<%= GuestbookEntry.class %>\" />\n\n\t<aui:fieldset>\n\n\t\t<aui:input name=\"name\" />\n\t\t<aui:input name=\"email\" />\n\t\t<aui:input name=\"message\" />\n\t\t<aui:input name=\"entryId\" type=\"hidden\" />\n\t\t<aui:input name=\"guestbookId\" type=\"hidden\" value='<%= entry == null ? guestbookId : entry.getGuestbookId() %>'/>\n\n\t</aui:fieldset>\n\n\t<aui:button-row>\n\n\t\t<aui:button type=\"submit\"></aui:button>\n\t\t<aui:button type=\"cancel\" onClick=\"<%= viewURL.toString() %>\"></aui:button>\n\n\t</aui:button-row>\n</aui:form>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/entry_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n        <%\n        String mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n        ResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);\n\n        GuestbookEntry entry = (GuestbookEntry)row.getObject(); \n        %>\n\n        <liferay-ui:icon-menu>\n\n                <portlet:renderURL var=\"editURL\">\n                    <portlet:param name=\"entryId\"\n                        value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n                    <portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n                </portlet:renderURL>\n\n                <liferay-ui:icon image=\"edit\" message=\"Edit\"\n                    url=\"<%=editURL.toString() %>\" />\n\n                <portlet:actionURL name=\"deleteEntry\" var=\"deleteURL\">\n                    <portlet:param name=\"entryId\"\n                        value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n                    <portlet:param name=\"guestbookId\"\n                        value=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n                </portlet:actionURL>\n\n                <liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n        </liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<liferay-ui:success key=\"entryAdded\" message=\"entry-added\" />\n<liferay-ui:success key=\"entryDeleted\" message=\"entry-deleted\" />\n\n<%\nlong guestbookId = Long.valueOf((Long) renderRequest\n\t\t.getAttribute(\"guestbookId\"));\n%>\n\n<aui:nav cssClass=\"nav-tabs\">\n\n\t<%\n\t\tList<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n\t\t\tfor (int i = 0; i < guestbooks.size(); i++) {\n\n\t\t\t\tGuestbook curGuestbook = (Guestbook) guestbooks.get(i);\n\t\t\t\tString cssClass = StringPool.BLANK;\n\n\t\t\t\tif (curGuestbook.getGuestbookId() == guestbookId) {\n\t\t\t\t\tcssClass = \"active\";\n\t\t\t\t}\n\n\t%>\n\n\t<portlet:renderURL var=\"viewPageURL\">\n\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\" />\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(curGuestbook.getGuestbookId())%>\" />\n\t</portlet:renderURL>\n\n\t\t\n\t<aui:nav-item cssClass=\"<%=cssClass%>\" href=\"<%=viewPageURL%>\"\n\t\tlabel=\"<%=HtmlUtil.escape(curGuestbook.getName())%>\" />\n\n\t<%  \n\t\t\t\t}\n\t\t\t\n\t%>\n\n</aui:nav>\n\n<aui:button-row cssClass=\"guestbook-buttons\">\n\n\t<portlet:renderURL var=\"addEntryURL\">\n\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(guestbookId)%>\" />\n\t</portlet:renderURL>\n\n\t<aui:button onClick=\"<%=addEntryURL.toString()%>\" value=\"Add Entry\"></aui:button>\n\n</aui:button-row>\n\n<liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntriesCount()%>\">\n<liferay-ui:search-container-results\n\tresults=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(scopeGroupId.longValue(),\n\t\t\t\t\tguestbookId, searchContainer.getStart(),\n\t\t\t\t\tsearchContainer.getEnd())%>\" />\n\n<liferay-ui:search-container-row\n\tclassName=\"com.liferay.docs.guestbook.model.GuestbookEntry\" modelVar=\"entry\">\n\n\t<liferay-ui:search-container-column-text property=\"message\" />\n\n\t<liferay-ui:search-container-column-text property=\"name\" />\n\n\t<liferay-ui:search-container-column-jsp\n\t\talign=\"right\" \n\t\tpath=\"/guestbook/entry_actions.jsp\" />\n</liferay-ui:search-container-row>\n\n<liferay-ui:search-iterator />\n\n</liferay-ui:search-container>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/edit_guestbook.jsp",
    "content": "<%@include file = \"../init.jsp\" %>\n\n<%\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n        \n        Guestbook guestbook = null;\n\n        if (guestbookId > 0) {\n                guestbook = GuestbookLocalServiceUtil.getGuestbook(guestbookId);\n        }\n%>\n\n<portlet:renderURL var=\"viewURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook_admin/view.jsp\" />\n</portlet:renderURL>\n\n<portlet:actionURL name='<%= guestbook == null ? \"addGuestbook\" : \"updateGuestbook\" %>' var=\"editGuestbookURL\" />\n\n<aui:form action=\"<%= editGuestbookURL %>\" name=\"fm\">\n\n        <aui:model-context bean=\"<%= guestbook %>\" model=\"<%= Guestbook.class %>\" />\n\n        <aui:input type=\"hidden\" name=\"guestbookId\"\n            value='<%= guestbook == null ? \"\" : guestbook.getGuestbookId() %>' />\n\n        <aui:fieldset>\n             <aui:input name=\"name\" />\n        </aui:fieldset>\n\n        <aui:button-row>\n             <aui:button type=\"submit\" />\n             <aui:button onClick=\"<%= viewURL %>\" type=\"cancel\"  />\n        </aui:button-row>\n</aui:form>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/guestbook_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\n\tString mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n\tResultRow row = (ResultRow) request\n\t\t\t\t\t.getAttribute(\"SEARCH_CONTAINER_RESULT_ROW\");\n\n\tGuestbook guestbook = (Guestbook) row.getObject();\n%>\n\n<liferay-ui:icon-menu>\n\n\t<portlet:renderURL var=\"editURL\">\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(guestbook.getGuestbookId()) %>\" />\n\t\t<portlet:param name=\"mvcPath\"\n\t\t\tvalue=\"/guestbook_admin/edit_guestbook.jsp\" />\n\t</portlet:renderURL>\n\n\t<liferay-ui:icon image=\"edit\" message=\"Edit\"\n\t\t\turl=\"<%=editURL.toString() %>\" />\n\n\t<portlet:actionURL name=\"deleteGuestbook\" var=\"deleteURL\">\n\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\tvalue=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\" />\n\t</portlet:actionURL>\n\n\t<liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n</liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<liferay-ui:success key=\"guestbookAdded\" message=\"guestbook-added\" />\n<liferay-ui:success key=\"guestbookUpdated\" message=\"guestbook-updated\" />\n<liferay-ui:success key=\"guestbookDeleted\" message=\"guestbook-deleted\" />\n\n<liferay-ui:search-container\n\ttotal=\"<%= GuestbookLocalServiceUtil.getGuestbooksCount(scopeGroupId) %>\">\n\t<liferay-ui:search-container-results\n\t\tresults=\"<%= GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId, \n\t\t\tsearchContainer.getStart(), searchContainer.getEnd()) %>\" />\n\n\t<liferay-ui:search-container-row\n\t\tclassName=\"com.liferay.docs.guestbook.model.Guestbook\" modelVar=\"guestbook\">\n\n\t\t<liferay-ui:search-container-column-text property=\"name\" />\n\t\t\t\t\n\t\t<liferay-ui:search-container-column-jsp\n\t\t\talign=\"right\" \n\t\t\tpath=\"/guestbook_admin/guestbook_actions.jsp\" />\n\t\t\n\t</liferay-ui:search-container-row>\n\n\t<liferay-ui:search-iterator />\n</liferay-ui:search-container>\n\n<aui:button-row cssClass=\"guestbook-admin-buttons\">\n\t<portlet:renderURL var=\"addGuestbookURL\">\n\t\t<portlet:param name=\"mvcPath\"\n\t\t\tvalue=\"/guestbook_admin/edit_guestbook.jsp\" />\n\t\t<portlet:param name=\"redirect\" value=\"<%= \"currentURL\" %>\" />\n\t</portlet:renderURL>\n\t\t\n\t<aui:button onClick=\"<%= addGuestbookURL.toString() %>\"\n\t\tvalue=\"Add Guestbook\" />\n</aui:button-row>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/init.jsp",
    "content": "<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\"%>\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\"%>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n<%@ taglib uri=\"http://liferay.com/tld/security\" prefix=\"liferay-security\" %>\n\n<%@ page import=\"java.util.List\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.ParamUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.HtmlUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.petra.string.StringPool\" %>\n<%@ page import=\"com.liferay.portal.kernel.model.PersistedModel\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.SearchEntry\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.ResultRow\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.Guestbook\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.GuestbookEntry\" %> \n\n<liferay-theme:defineObjects />\n\n<portlet:defineObjects />"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/content/Language.properties",
    "content": "entry-added=Entry added successfully.\nentry-deleted=Entry deleted successfully.\nguestbook-added=Guestbook added successfully.\nguestbook-updated=Guestbook updated successfully.\nguestbook-deleted=Guestbook deleted successfully."
  },
  {
    "path": "en/developer/tutorials/code/guestbook/06-messages/com-liferay-docs-guestbook/settings.gradle",
    "content": "buildscript {\n\tdependencies {\n\t\tclasspath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.workspace\", version: \"2.0.4\"\n\t\tclasspath group: \"net.saliman\", name: \"gradle-properties-plugin\", version: \"1.4.6\"\n\t}\n\n\trepositories {\n\t\tmaven {\n\t\t\turl \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t\t}\n\t}\n}\n\napply plugin: \"net.saliman.properties\"\n\napply plugin: \"com.liferay.workspace\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/.blade.properties",
    "content": "#Wed Aug 14 16:06:16 EDT 2019\nliferay.version.default=7.2\nprofile.name=gradle\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/.gitignore",
    "content": "**/*.iml\n**/.ivy\n**/.classpath\n**/.project\n**/.sass-cache\n**/.settings\n**/bin\n**/build\n**/build_gradle\n**/node_modules\n**/test-coverage\n**/tmp\n**/.web_bundle_build\n.gradle\n.idea\n/bundles\n/gradle-*.properties\n/plugins-sdk/**/classes\n/plugins-sdk/**/ivy.xml.MD5\n/plugins-sdk/**/liferay-hook.xml.processed\n/plugins-sdk/build.*.properties\n/plugins-sdk/dependencies/**/*.jar\n/plugins-sdk/dist\n/plugins-sdk/lib\ntest-results"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/configs/common/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/configs/dev/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/configs/docker/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/configs/local/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/configs/prod/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "operationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/configs/prod/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/configs/uat/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "logExceptionsOnly=B\"false\"\noperationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/configs/uat/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.2-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/gradle.properties",
    "content": "\n    # Set the directory where the downloaded bundle Zip files are stored. The\n    # default value is the \".liferay/bundles\" folder inside the user home\n    # directory.\n    #\n    #liferay.workspace.bundle.cache.dir=~/.liferay/bundles\n\n    #\n    # Set this to true if the \"liferay.workspace.bundle.url\" property is set to\n    # a DXP bundle Zip. This property allows the token residing in the\n    # \"~/.liferay\" folder to be used to validate your user credentials when\n    # downloading the bundle. The default value is false.\n    #\n    #liferay.workspace.bundle.token.download=false\n\n    #\n    # Set the email address to use when downloading a DXP bundle. This is used\n    # to create the authentication token. The email address must match the one\n    # registered for your DXP subscription.\n    #\n    # If you wish to create a new token without providing your email address and\n    # password in this file, you can create a token manually by navigating to\n    # your Liferay profile's Account Setting page and generating a token in the\n    # Authentication Tokens menu. Your token must reside in the \"~/.liferay\"\n    # folder.\n    #\n    #liferay.workspace.bundle.token.email.address=\n\n    #\n    # Set this to true to override the existing token with a newly generated\n    # token created by the \"createToken\" task. The default value is false.\n    #\n    #liferay.workspace.bundle.token.force=false\n\n    #\n    # Set the password to use when downloading a DXP bundle. This is used to\n    # create the authentication token. The password must match the one\n    # registered for your DXP subscription. See the\n    # \"liferay.workspace.bundle.token.email.address\" property for more details.\n    #\n    #liferay.workspace.bundle.token.password=\n\n    #\n    # Set the file to hold the Liferay bundle authentication token password.\n    # The default file value is \"~/.liferay/token\".\n    #\n    #liferay.workspace.bundle.token.password.file=\n\n    #\n    # Set the URL pointing to the bundle Zip to download. If the URL points to a\n    # DXP bundle (e.g., https://api.liferay.com/...), set the\n    # \"liferay.workspace.bundle.token.download\" property to true. The default\n    # value is the URL for Liferay Portal CE 7.0 GA7, Liferay Portal CE 7.1 GA4,\n    # or Liferay Portal CE 7.2 GA1, depending on the portal version the\n    # workspace is targeting.\n    #\nliferay.workspace.bundle.url = https://releases-cdn.liferay.com/portal/7.2.0-ga1/liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761.tar.gz\n\n    #\n    # Set the \"app.server.tomcat.version\" to match what is contained inside the\n    # Liferay bundle. Both the TestIntegrationPlugin and and LiferayExtPlugin\n    # rely on this version to match the bundled Tomcat version. If your\n    # configured bundle url points to a bundle with a different Tomcat version,\n    # set the property below to match that Tomcat version.\n    #\napp.server.tomcat.version = 9.0.17\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository in the\n    # root project. The default value is true.\n    #\n    #liferay.workspace.default.repository.enabled=true\n\n    #\n    # Set the Liferay Portal Docker image to create your container from. The\n    # default value points to Liferay Portal CE 7.2 GA1.\n    #\n    #liferay.workspace.docker.image.liferay=liferay/portal:7.2.0-ga1\n\n    #\n    # Set the environment with the settings appropriate for current development.\n    # The \"configs\" folder is used to hold different environments in the same\n    # workspace. You can organize environment settings and generate an\n    # environment installation with those settings. There are five\n    # environments: common, dev, docker, local, prod, and uat. The default value\n    # is \"local\".\n    #\n    #liferay.workspace.environment=local\n\n    #\n    # Set the folder that contains the Liferay bundle downloaded from the\n    # \"liferay.workspace.bundle.url\" property. The default value is \"bundles\".\n    #\n    #liferay.workspace.home.dir=bundles\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository for\n    # module/OSGi projects. The default value is true.\n    #\n    #liferay.workspace.modules.default.repository.enabled=true\n\n    #\n    # Set the folder that contains all Ext OSGi modules and Ext plugins. The\n    # default value is \"ext\".\n    #\n    #liferay.workspace.ext.dir=ext\n\n    #\n    # Set the folder that contains all module/OSGi projects. The default value\n    # is \"modules\".\n    #\n    #liferay.workspace.modules.dir=modules\n\n    #\n    # Set this to true to compile the JSP files in OSGi modules and have them\n    # added to the distributable Zip/Tar. The default value is false.\n    #\n    #liferay.workspace.modules.jsp.precompile.enabled=false\n\n    #\n    # Set the folder that contains the Plugins SDK environment. The default\n    # value is \"plugins-sdk\".\n    #\n    #liferay.workspace.plugins.sdk.dir=plugins-sdk\n\n    #\n    # Set the Liferay Portal or DXP version to develop and test against. By\n    # setting this property, it enables the target platform features such as\n    # dependency management and OSGi resolve tasks. Use the version that\n    # matches the Liferay Portal or DXP bundle version in this workspace.\n    #\n    # For a list of all available target platform versions, see\n    # https://bit.ly/2IkAwwW for Liferay Portal and https://bit.ly/2GIyfZF for\n    # Liferay DXP.\n    #\n    #liferay.workspace.target.platform.version=7.2.0\n\n    #\n    # Set this to true if you have enabled the Target Platform plugin (i.e. you\n    # have set the above property) and you want to apply the TargetPlatformIDE\n    # plugin to the root workspace project. This will cause all of the BOM\n    # artifacts jars and their Java sources to be indexed by your IDE. Setting\n    # this property to true can slow down your IDE's project synchronization.\n    #\n    # target.platform.index.sources=false\n\n    #\n    # Set the folder that contains Node.js-style theme projects. The default\n    # value is \"themes\".\n    #\n    #liferay.workspace.themes.dir=themes\n\n    #\n    # Set this to true to build the theme projects using the Liferay Portal\n    # Tools Theme Builder. The default value is false.\n    #\n    #liferay.workspace.themes.java.build=false\n\n    #\n    # Set the folder that contains classic WAR-style projects. The default value\n    # is \"wars\".\n    #\n    #liferay.workspace.wars.dir=wars\n\n\n    #\n    # Set the subscription key for Microsoft Translation integration.\n    # Subscription to the Translator Text Translation API on Microsoft Cognitive\n    # Services is required. Basic subscriptions, up to 2 million characters a\n    # month, are free. See\n    # http://docs.microsofttranslator.com/text-translate.html for more\n    # information.\n    #\nmicrosoft.translator.subscription.key = \nliferay.workspace.target.platform.version = 7.2.0\ntarget.platform.index.sources = false\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/.gitignore",
    "content": ".gradle/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/bnd.bnd",
    "content": "Bundle-Name: guestbook-api\nBundle-SymbolicName: com.liferay.docs.guestbook.api\nBundle-Version: 1.0.0\nExport-Package:\\\n\tcom.liferay.docs.guestbook.exception,\\\n\tcom.liferay.docs.guestbook.model,\\\n\tcom.liferay.docs.guestbook.service,\\\n\tcom.liferay.docs.guestbook.service.persistence\n-check: EXPORTS\n-includeresource: META-INF/service.xml=../guestbook-service/service.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/constants/GuestbookConstants.java",
    "content": "package com.liferay.docs.guestbook.constants;\n\npublic class GuestbookConstants {\n\t\n\tpublic static final String RESOURCE_NAME = \"com.liferay.docs.guestbook\";\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/constants/GuestbookPortletKeys.java",
    "content": "package com.liferay.docs.guestbook.constants;\n\n/**\n * @author sezovr\n */\npublic class GuestbookPortletKeys {\n\n\tpublic static final String GUESTBOOK =\n\t\t\"com_liferay_docs_guestbook_portlet_GuestbookPortlet\";\n\n\tpublic static final String GUESTBOOK_ADMIN =\n\t\t  \"com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet\";\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryEmailException extends PortalException {\n\n\tpublic EntryEmailException() {\n\t}\n\n\tpublic EntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryMessageException extends PortalException {\n\n\tpublic EntryMessageException() {\n\t}\n\n\tpublic EntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryNameException extends PortalException {\n\n\tpublic EntryNameException() {\n\t}\n\n\tpublic EntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryEmailException extends PortalException {\n\n\tpublic GuestbookEntryEmailException() {\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryMessageException extends PortalException {\n\n\tpublic GuestbookEntryMessageException() {\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryNameException extends PortalException {\n\n\tpublic GuestbookEntryNameException() {\n\t}\n\n\tpublic GuestbookEntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookNameException extends PortalException {\n\n\tpublic GuestbookNameException() {\n\t}\n\n\tpublic GuestbookNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookEntryException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookEntryException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookEntryException() {\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookException() {\n\t}\n\n\tpublic NoSuchGuestbookException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/Guestbook.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookModel\n * @generated\n */\n@ImplementationClassName(\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\")\n@ProviderType\npublic interface Guestbook extends GuestbookModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<Guestbook, Long> GUESTBOOK_ID_ACCESSOR =\n\t\tnew Accessor<Guestbook, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(Guestbook guestbook) {\n\t\t\t\treturn guestbook.getGuestbookId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Guestbook> getTypeClass() {\n\t\t\t\treturn Guestbook.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntry.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookEntryModel\n * @generated\n */\n@ImplementationClassName(\n\t\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\"\n)\n@ProviderType\npublic interface GuestbookEntry extends GuestbookEntryModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<GuestbookEntry, Long> ENTRY_ID_ACCESSOR =\n\t\tnew Accessor<GuestbookEntry, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(GuestbookEntry guestbookEntry) {\n\t\t\t\treturn guestbookEntry.getEntryId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<GuestbookEntry> getTypeClass() {\n\t\t\t\treturn GuestbookEntry.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryModel\n\textends BaseModel<GuestbookEntry>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook entry model instance should use the {@link GuestbookEntry} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\tpublic long getEntryId();\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\tpublic void setEntryId(long entryId);\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getEmail();\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\tpublic void setEmail(String email);\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getMessage();\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\tpublic void setMessage(String message);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntrySoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookEntryServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntrySoap implements Serializable {\n\n\tpublic static GuestbookEntrySoap toSoapModel(GuestbookEntry model) {\n\t\tGuestbookEntrySoap soapModel = new GuestbookEntrySoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setEntryId(model.getEntryId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setEmail(model.getEmail());\n\t\tsoapModel.setMessage(model.getMessage());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(GuestbookEntry[] models) {\n\t\tGuestbookEntrySoap[] soapModels = new GuestbookEntrySoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[][] toSoapModels(\n\t\tGuestbookEntry[][] models) {\n\n\t\tGuestbookEntrySoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels =\n\t\t\t\tnew GuestbookEntrySoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookEntrySoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(\n\t\tList<GuestbookEntry> models) {\n\n\t\tList<GuestbookEntrySoap> soapModels = new ArrayList<GuestbookEntrySoap>(\n\t\t\tmodels.size());\n\n\t\tfor (GuestbookEntry model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookEntrySoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookEntrySoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetEntryId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic String getEmail() {\n\t\treturn _email;\n\t}\n\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn _message;\n\t}\n\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link GuestbookEntry}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryWrapper\n\textends BaseModelWrapper<GuestbookEntry>\n\timplements GuestbookEntry, ModelWrapper<GuestbookEntry> {\n\n\tpublic GuestbookEntryWrapper(GuestbookEntry guestbookEntry) {\n\t\tsuper(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"entryId\", getEntryId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"email\", getEmail());\n\t\tattributes.put(\"message\", getMessage());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong entryId = (Long)attributes.get(\"entryId\");\n\n\t\tif (entryId != null) {\n\t\t\tsetEntryId(entryId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tString email = (String)attributes.get(\"email\");\n\n\t\tif (email != null) {\n\t\t\tsetEmail(email);\n\t\t}\n\n\t\tString message = (String)attributes.get(\"message\");\n\n\t\tif (message != null) {\n\t\t\tsetMessage(message);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@Override\n\tpublic String getEmail() {\n\t\treturn model.getEmail();\n\t}\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn model.getEntryId();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@Override\n\tpublic String getMessage() {\n\t\treturn model.getMessage();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEmail(String email) {\n\t\tmodel.setEmail(email);\n\t}\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\tmodel.setEntryId(entryId);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\t@Override\n\tpublic void setMessage(String message) {\n\t\tmodel.setMessage(message);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookEntryWrapper wrap(GuestbookEntry guestbookEntry) {\n\t\treturn new GuestbookEntryWrapper(guestbookEntry);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic interface GuestbookModel\n\textends BaseModel<Guestbook>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook model instance should use the {@link Guestbook} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookSoap implements Serializable {\n\n\tpublic static GuestbookSoap toSoapModel(Guestbook model) {\n\t\tGuestbookSoap soapModel = new GuestbookSoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(Guestbook[] models) {\n\t\tGuestbookSoap[] soapModels = new GuestbookSoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[][] toSoapModels(Guestbook[][] models) {\n\t\tGuestbookSoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels = new GuestbookSoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookSoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(List<Guestbook> models) {\n\t\tList<GuestbookSoap> soapModels = new ArrayList<GuestbookSoap>(\n\t\t\tmodels.size());\n\n\t\tfor (Guestbook model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookSoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookSoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetGuestbookId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link Guestbook}.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic class GuestbookWrapper\n\textends BaseModelWrapper<Guestbook>\n\timplements Guestbook, ModelWrapper<Guestbook> {\n\n\tpublic GuestbookWrapper(Guestbook guestbook) {\n\t\tsuper(guestbook);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookWrapper wrap(Guestbook guestbook) {\n\t\treturn new GuestbookWrapper(guestbook);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for GuestbookEntry. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryLocalServiceUtil} to access the guestbook entry local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\tpublic GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId);\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException;\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId);\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows SystemException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> obc);\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\tpublic GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\taddGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().addGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\taddGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\t\tString message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tcreateGuestbookEntry(long entryId) {\n\n\t\treturn getService().createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(\n\t\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntry(long entryId) {\n\n\t\treturn getService().fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tint start, int end) {\n\n\t\treturn getService().getGuestbookEntries(start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId) {\n\n\t\treturn getService().getGuestbookEntries(groupId, guestbookId);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tString uuid, long companyId, int start, int end,\n\t\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int getGuestbookEntriesCount() {\n\t\treturn getService().getGuestbookEntriesCount();\n\t}\n\n\tpublic static int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn getService().getGuestbookEntriesCount(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tupdateGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().updateGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tupdateGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\t\tString email, String message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\tpublic static GuestbookEntryLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryLocalService.class);\n\n\t\tServiceTracker<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryLocalService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryLocalService}.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceWrapper\n\timplements GuestbookEntryLocalService,\n\t\t\t   ServiceWrapper<GuestbookEntryLocalService> {\n\n\tpublic GuestbookEntryLocalServiceWrapper(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry createGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookEntryLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry fetchGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookEntryLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(int start, int end) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(long groupId, long guestbookId) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry getGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntryLocalService getWrappedService() {\n\t\treturn _guestbookEntryLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for GuestbookEntry. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryServiceUtil} to access the guestbook entry remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookEntryService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookEntryService.class);\n\n\t\tServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryService, GuestbookEntryService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookEntryService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryService}.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceWrapper\n\timplements GuestbookEntryService, ServiceWrapper<GuestbookEntryService> {\n\n\tpublic GuestbookEntryServiceWrapper(\n\t\tGuestbookEntryService guestbookEntryService) {\n\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookEntryService getWrappedService() {\n\t\treturn _guestbookEntryService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookEntryService guestbookEntryService) {\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\tprivate GuestbookEntryService _guestbookEntryService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for Guestbook. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookLocalServiceUtil} to access the guestbook local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(Guestbook guestbook);\n\n\tpublic Guestbook addGuestbook(\n\t\t\tlong userId, String name, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId);\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook);\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException;\n\n\tpublic Guestbook deleteGuestbook(\n\t\t\tlong guestbookId, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbook(long guestbookId);\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException;\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(\n\t\tlong groupId, int start, int end, OrderByComparator<Guestbook> obc);\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(Guestbook guestbook);\n\n\tpublic Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().addGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbook(userId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbook(guestbookId);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().deleteGuestbook(guestbookId, serviceContext);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn getService().getGuestbooks(start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn getService().getGuestbooks(groupId);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int getGuestbooksCount() {\n\t\treturn getService().getGuestbooksCount();\n\t}\n\n\tpublic static int getGuestbooksCount(long groupId) {\n\t\treturn getService().getGuestbooksCount(groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().updateGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbook(\n\t\t\tuserId, guestbookId, name, serviceContext);\n\t}\n\n\tpublic static GuestbookLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookLocalService.class);\n\n\t\tServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookLocalService, GuestbookLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookLocalService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookLocalService}.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceWrapper\n\timplements GuestbookLocalService, ServiceWrapper<GuestbookLocalService> {\n\n\tpublic GuestbookLocalServiceWrapper(\n\t\tGuestbookLocalService guestbookLocalService) {\n\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.addGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.addGuestbook(\n\t\t\tuserId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbookId);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(\n\t\t\tguestbookId, serviceContext);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn _guestbookLocalService.getGuestbooksCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbooksCount(long groupId) {\n\t\treturn _guestbookLocalService.getGuestbooksCount(groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.updateGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.updateGuestbook(\n\t\t\tuserId, guestbookId, name, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookLocalService getWrappedService() {\n\t\treturn _guestbookLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookLocalService guestbookLocalService) {\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for Guestbook. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookServiceUtil} to access the guestbook remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookService, GuestbookService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookService.class);\n\n\t\tServiceTracker<GuestbookService, GuestbookService> serviceTracker =\n\t\t\tnew ServiceTracker<GuestbookService, GuestbookService>(\n\t\t\t\tbundle.getBundleContext(), GuestbookService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookService}.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceWrapper\n\timplements GuestbookService, ServiceWrapper<GuestbookService> {\n\n\tpublic GuestbookServiceWrapper(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookService getWrappedService() {\n\t\treturn _guestbookService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\tprivate GuestbookService _guestbookService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryPersistence\n\textends BasePersistence<GuestbookEntry> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryUtil} to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic void removeByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic void cacheResult(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic void cacheResult(java.util.List<GuestbookEntry> guestbookEntries);\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic GuestbookEntry create(long entryId);\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId);\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll();\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook entry service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookEntryPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().clearCache(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, GuestbookEntry> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static GuestbookEntry update(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().update(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static GuestbookEntry update(\n\t\tGuestbookEntry guestbookEntry, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbookEntry, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tentryId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic static GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tentryId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator,\n\t\t\tretrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_PrevAndNext(\n\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic static void removeByG_G(long groupId, long guestbookId) {\n\t\tgetPersistence().removeByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByG_G(long groupId, long guestbookId) {\n\t\treturn getPersistence().countByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic static void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().cacheResult(guestbookEntry);\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic static void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tgetPersistence().cacheResult(guestbookEntries);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static GuestbookEntry create(long entryId) {\n\t\treturn getPersistence().create(entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry remove(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().remove(entryId);\n\t}\n\n\tpublic static GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().updateImpl(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn getPersistence().fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookEntryPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence> _serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryPersistence.class);\n\n\t\tServiceTracker<GuestbookEntryPersistence, GuestbookEntryPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryPersistence.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookPersistence extends BasePersistence<Guestbook> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookUtil} to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(long groupId);\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic void removeByGroupId(long groupId);\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByGroupId(long groupId);\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic void cacheResult(Guestbook guestbook);\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic void cacheResult(java.util.List<Guestbook> guestbooks);\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic Guestbook create(long guestbookId);\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException;\n\n\tpublic Guestbook updateImpl(Guestbook guestbook);\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId);\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll();\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(Guestbook guestbook) {\n\t\tgetPersistence().clearCache(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, Guestbook> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static Guestbook update(Guestbook guestbook) {\n\t\treturn getPersistence().update(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static Guestbook update(\n\t\tGuestbook guestbook, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbook, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tguestbookId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic static Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tguestbookId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(long groupId) {\n\t\treturn getPersistence().findByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn getPersistence().findByGroupId(groupId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_First(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_First(\n\t\t\tgroupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_PrevAndNext(\n\t\t\tguestbookId, groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic static void removeByGroupId(long groupId) {\n\t\tgetPersistence().removeByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByGroupId(long groupId) {\n\t\treturn getPersistence().countByGroupId(groupId);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic static void cacheResult(Guestbook guestbook) {\n\t\tgetPersistence().cacheResult(guestbook);\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic static void cacheResult(List<Guestbook> guestbooks) {\n\t\tgetPersistence().cacheResult(guestbooks);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static Guestbook create(long guestbookId) {\n\t\treturn getPersistence().create(guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook remove(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().remove(guestbookId);\n\t}\n\n\tpublic static Guestbook updateImpl(Guestbook guestbook) {\n\t\treturn getPersistence().updateImpl(guestbook);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn getPersistence().fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic static List<Guestbook> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookPersistence.class);\n\n\t\tServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker<GuestbookPersistence, GuestbookPersistence>(\n\t\t\t\t\tbundle.getBundleContext(), GuestbookPersistence.class,\n\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/bnd.bnd",
    "content": "Bundle-Name: guestbook-service\nBundle-SymbolicName: com.liferay.docs.guestbook.service\nBundle-Version: 1.0.0\nLiferay-Require-SchemaVersion: 1.0.0\nLiferay-Service: true\n-dsannotations-options: inherit\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.lang\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.function\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.model.adapter\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.reflect\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.portal.aop.api\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\tcompileOnly project(\":modules:guestbook:guestbook-api\")\n}\n\nbuildService {\n\tapiDir = \"../guestbook-api/src/main/java\"\n}\n\ngroup = \"com.liferay.docs.guestbook\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/service.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"ds\" package-path=\"com.liferay.docs.guestbook\">\n\t<author>Liferay</author>\n\t<namespace>GB</namespace>\n\t\n\t<entity name=\"Guestbook\" local-service=\"true\" uuid=\"true\" remote-service=\"true\">\n\t\t\n\t\t<!-- Guestbook fields -->\n\n\t\t<column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\t\t\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<finder name=\"GroupId\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"groupId\" />\n\t\t</finder>\n\n\t</entity>\n\t\n    <entity name=\"GuestbookEntry\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n\n\t\t<!-- Guestbook Entry fields -->\n\n\t\t<column name=\"entryId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t<column name=\"email\" type=\"String\" />\n\t\t<column name=\"message\" type=\"String\" />\n\t\t<column name=\"guestbookId\" type=\"long\" />\n\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\t   \n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<order>\n\t\t\t<order-column name=\"createDate\" order-by=\"desc\" />\n\t\t</order>\n\n        <finder name=\"G_G\" return-type=\"Collection\">\n            <finder-column name=\"groupId\" />\n            <finder-column name=\"guestbookId\" />\n        </finder>\n    </entity>\n\n    <exceptions>\n        <exception>GuestbookEntryEmail</exception>\n        <exception>GuestbookEntryMessage</exception>\n        <exception>GuestbookEntryName</exception>\n        <exception>GuestbookName</exception>\n    </exceptions>\n</service-builder>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/internal/security/permission/resource/GuestbookEntryModelResourcePermissionRegistrar.java",
    "content": "\n/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.StagedModelPermissionLogic;\nimport com.liferay.portal.kernel.security.permission.resource.WorkflowedModelPermissionLogic;\nimport com.liferay.portal.kernel.service.GroupLocalService;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\nimport com.liferay.portal.kernel.workflow.permission.WorkflowPermission;\n\n@Component(immediate = true)\npublic class GuestbookEntryModelResourcePermissionRegistrar {\n\n @Activate\n\tpublic void activate(BundleContext bundleContext) {\n\t\tDictionary<String, Object> properties = new HashMapDictionary<>();\n\n\t\tproperties.put(\"model.class.name\", GuestbookEntry.class.getName());\n\n\t\t_serviceRegistration = bundleContext.registerService(\n\t\t\tModelResourcePermission.class,\n\t\t\tModelResourcePermissionFactory.create(\n\t\t\t\tGuestbookEntry.class, GuestbookEntry::getEntryId,\n\t\t\t\t_guestbookEntryLocalService::getGuestbookEntry, _portletResourcePermission,\n\t\t\t\t(modelResourcePermission, consumer) -> {\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew StagedModelPermissionLogic<>(\n\t\t\t\t\t\t\t_stagingPermission, GuestbookPortletKeys.GUESTBOOK,\n\t\t\t\t\t\t\tGuestbookEntry::getEntryId));\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew WorkflowedModelPermissionLogic<>(\n\t\t\t\t\t\t\t\t_workflowPermission, modelResourcePermission,\n\t\t\t\t\t\t\t\t_groupLocalService, GuestbookEntry::getEntryId));\n\t\t\t\t}),\n\t\t\tproperties);\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n\t@Reference(target = \"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\")\n\tprivate PortletResourcePermission _portletResourcePermission;\n\n\tprivate ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n\t@Reference\n\tprivate StagingPermission _stagingPermission;\n\n\t@Reference\n\tprivate WorkflowPermission _workflowPermission;\n\t\n\t@Reference\n\tprivate GroupLocalService _groupLocalService;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/internal/security/permission/resource/GuestbookModelResourcePermissionRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.StagedModelPermissionLogic;\nimport com.liferay.portal.kernel.security.permission.resource.WorkflowedModelPermissionLogic;\nimport com.liferay.portal.kernel.service.GroupLocalService;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\nimport com.liferay.portal.kernel.workflow.permission.WorkflowPermission;\n\n@Component (immediate=true)\npublic class GuestbookModelResourcePermissionRegistrar {\n\n @Activate\n\tpublic void activate(BundleContext bundleContext) {\n\t\tDictionary<String, Object> properties = new HashMapDictionary<>();\n\n\t\tproperties.put(\"model.class.name\", Guestbook.class.getName());\n\n\t\t_serviceRegistration = bundleContext.registerService(\n\t\t\tModelResourcePermission.class,\n\t\t\tModelResourcePermissionFactory.create(\n\t\t\t\tGuestbook.class, Guestbook::getGuestbookId,\n\t\t\t\t_guestbookLocalService::getGuestbook, _portletResourcePermission,\n\t\t\t\t(modelResourcePermission, consumer) -> {\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew StagedModelPermissionLogic<>(\n\t\t\t\t\t\t\t_stagingPermission, GuestbookPortletKeys.GUESTBOOK,\n\t\t\t\t\t\t\tGuestbook::getGuestbookId));\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew WorkflowedModelPermissionLogic<>(\n\t\t\t\t\t\t\t\t_workflowPermission, modelResourcePermission,\n\t\t\t\t\t\t\t\t_groupLocalService, Guestbook::getGuestbookId));\n\t\t\t\t}),\n\t\t\tproperties);\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n\t@Reference(target = \"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\")\n\tprivate PortletResourcePermission _portletResourcePermission;\n\n\tprivate ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n\t@Reference\n\tprivate StagingPermission _stagingPermission;\n\n\t@Reference\n\tprivate WorkflowPermission _workflowPermission;\n\t\n\t@Reference\n\tprivate GroupLocalService _groupLocalService;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/internal/security/permission/resource/GuestbookPortletResourcePermissionRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.StagedPortletPermissionLogic;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\n\n@Component (immediate = true)\npublic class GuestbookPortletResourcePermissionRegistrar {\n\t\n\t\t@Activate\n\tpublic void activate(BundleContext bundleContext) {\n\t\tDictionary<String, Object> properties = new HashMapDictionary<>();\n\n\t\tproperties.put(\"resource.name\", GuestbookConstants.RESOURCE_NAME);\n\n\t\t_serviceRegistration = bundleContext.registerService(\n\t\t\tPortletResourcePermission.class,\n\t\t\tPortletResourcePermissionFactory.create(\n\t\t\t\tGuestbookConstants.RESOURCE_NAME,\n\t\t\t\tnew StagedPortletPermissionLogic(\n\t\t\t\t\t_stagingPermission, GuestbookPortletKeys.GUESTBOOK)),\n\t\t\tproperties);\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\tprivate ServiceRegistration<PortletResourcePermission> _serviceRegistration;\n\n\t@Reference\n\tprivate StagingPermission _stagingPermission;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookBaseImpl\n\textends GuestbookModelImpl implements Guestbook {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookLocalServiceUtil.addGuestbook(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookLocalServiceUtil.updateGuestbook(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing Guestbook in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookCacheModel\n\timplements CacheModel<Guestbook>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookCacheModel guestbookCacheModel = (GuestbookCacheModel)obj;\n\n\t\tif (guestbookId == guestbookCacheModel.guestbookId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, guestbookId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(27);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic Guestbook toEntityModel() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookImpl.setGuestbookId(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tguestbookImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setName(name);\n\t\t}\n\n\t\tguestbookImpl.setGroupId(groupId);\n\t\tguestbookImpl.setCompanyId(companyId);\n\t\tguestbookImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookImpl.setStatus(status);\n\t\tguestbookImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long guestbookId;\n\tpublic String name;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryBaseImpl\n\textends GuestbookEntryModelImpl implements GuestbookEntry {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookEntryLocalServiceUtil.addGuestbookEntry(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookEntryLocalServiceUtil.updateGuestbookEntry(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing GuestbookEntry in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryCacheModel\n\timplements CacheModel<GuestbookEntry>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntryCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\t(GuestbookEntryCacheModel)obj;\n\n\t\tif (entryId == guestbookEntryCacheModel.entryId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, entryId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(33);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", entryId=\");\n\t\tsb.append(entryId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", email=\");\n\t\tsb.append(email);\n\t\tsb.append(\", message=\");\n\t\tsb.append(message);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEntityModel() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookEntryImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookEntryImpl.setEntryId(entryId);\n\n\t\tif (name == null) {\n\t\t\tguestbookEntryImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setName(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tguestbookEntryImpl.setEmail(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setEmail(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tguestbookEntryImpl.setMessage(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setMessage(message);\n\t\t}\n\n\t\tguestbookEntryImpl.setGuestbookId(guestbookId);\n\t\tguestbookEntryImpl.setGroupId(groupId);\n\t\tguestbookEntryImpl.setCompanyId(companyId);\n\t\tguestbookEntryImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookEntryImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookEntryImpl.setStatus(status);\n\t\tguestbookEntryImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookEntryImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tentryId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\t\temail = objectInput.readUTF();\n\t\tmessage = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(entryId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(message);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long entryId;\n\tpublic String name;\n\tpublic String email;\n\tpublic String message;\n\tpublic long guestbookId;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.GuestbookEntry<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryImpl extends GuestbookEntryBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook entry model instance should use the {@link com.liferay.docs.guestbook.model.GuestbookEntry} interface instead.\n\t */\n\tpublic GuestbookEntryImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.GuestbookEntryModel;\nimport com.liferay.docs.guestbook.model.GuestbookEntrySoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.DateUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookEntryModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookEntryModelImpl\n\textends BaseModelImpl<GuestbookEntry> implements GuestbookEntryModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_GuestbookEntry\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"entryId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"email\", Types.VARCHAR},\n\t\t{\"message\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"groupId\", Types.BIGINT}, {\"companyId\", Types.BIGINT},\n\t\t{\"userId\", Types.BIGINT}, {\"userName\", Types.VARCHAR},\n\t\t{\"createDate\", Types.TIMESTAMP}, {\"modifiedDate\", Types.TIMESTAMP},\n\t\t{\"status\", Types.INTEGER}, {\"statusByUserId\", Types.BIGINT},\n\t\t{\"statusByUserName\", Types.VARCHAR}, {\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"entryId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"email\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"message\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_GuestbookEntry (uuid_ VARCHAR(75) null,entryId LONG not null primary key,name VARCHAR(75) null,email VARCHAR(75) null,message VARCHAR(75) null,guestbookId LONG,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_GuestbookEntry\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbookEntry.createDate DESC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_GuestbookEntry.createDate DESC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 4L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 8L;\n\n\tpublic static final long CREATEDATE_COLUMN_BITMASK = 16L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static GuestbookEntry toModel(GuestbookEntrySoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbookEntry model = new GuestbookEntryImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setEntryId(soapModel.getEntryId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setEmail(soapModel.getEmail());\n\t\tmodel.setMessage(soapModel.getMessage());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<GuestbookEntry> toModels(\n\t\tGuestbookEntrySoap[] soapModels) {\n\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> models = new ArrayList<GuestbookEntry>(\n\t\t\tsoapModels.length);\n\n\t\tfor (GuestbookEntrySoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookEntryModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetEntryId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName,\n\t\t\t\tattributeGetterFunction.apply((GuestbookEntry)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<GuestbookEntry, Object>>\n\t\t\tattributeSetterBiConsumers = getAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<GuestbookEntry, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(GuestbookEntry)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<GuestbookEntry, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, GuestbookEntry>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbookEntry.class.getClassLoader(), GuestbookEntry.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<GuestbookEntry> constructor =\n\t\t\t\t(Constructor<GuestbookEntry>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<GuestbookEntry, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<GuestbookEntry, Object>>();\n\t\tMap<String, BiConsumer<GuestbookEntry, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<GuestbookEntry, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", GuestbookEntry::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUuid);\n\t\tattributeGetterFunctions.put(\"entryId\", GuestbookEntry::getEntryId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"entryId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setEntryId);\n\t\tattributeGetterFunctions.put(\"name\", GuestbookEntry::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setName);\n\t\tattributeGetterFunctions.put(\"email\", GuestbookEntry::getEmail);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"email\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setEmail);\n\t\tattributeGetterFunctions.put(\"message\", GuestbookEntry::getMessage);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"message\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setMessage);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"guestbookId\", GuestbookEntry::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"groupId\", GuestbookEntry::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", GuestbookEntry::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", GuestbookEntry::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", GuestbookEntry::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"createDate\", GuestbookEntry::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", GuestbookEntry::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", GuestbookEntry::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\",\n\t\t\t(BiConsumer<GuestbookEntry, Integer>)GuestbookEntry::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", GuestbookEntry::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)\n\t\t\t\tGuestbookEntry::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", GuestbookEntry::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)\n\t\t\t\tGuestbookEntry::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusDate\", GuestbookEntry::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getEmail() {\n\t\tif (_email == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _email;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getMessage() {\n\t\tif (_message == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _message;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_columnBitmask |= GUESTBOOKID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGuestbookId) {\n\t\t\t_setOriginalGuestbookId = true;\n\n\t\t\t_originalGuestbookId = _guestbookId;\n\t\t}\n\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getOriginalGuestbookId() {\n\t\treturn _originalGuestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_columnBitmask = -1L;\n\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), GuestbookEntry.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tguestbookEntryImpl.setUuid(getUuid());\n\t\tguestbookEntryImpl.setEntryId(getEntryId());\n\t\tguestbookEntryImpl.setName(getName());\n\t\tguestbookEntryImpl.setEmail(getEmail());\n\t\tguestbookEntryImpl.setMessage(getMessage());\n\t\tguestbookEntryImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookEntryImpl.setGroupId(getGroupId());\n\t\tguestbookEntryImpl.setCompanyId(getCompanyId());\n\t\tguestbookEntryImpl.setUserId(getUserId());\n\t\tguestbookEntryImpl.setUserName(getUserName());\n\t\tguestbookEntryImpl.setCreateDate(getCreateDate());\n\t\tguestbookEntryImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookEntryImpl.setStatus(getStatus());\n\t\tguestbookEntryImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookEntryImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookEntryImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(GuestbookEntry guestbookEntry) {\n\t\tint value = 0;\n\n\t\tvalue = DateUtil.compareTo(\n\t\t\tgetCreateDate(), guestbookEntry.getCreateDate());\n\n\t\tvalue = value * -1;\n\n\t\tif (value != 0) {\n\t\t\treturn value;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntry)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)obj;\n\n\t\tlong primaryKey = guestbookEntry.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl = this;\n\n\t\tguestbookEntryModelImpl._originalUuid = guestbookEntryModelImpl._uuid;\n\n\t\tguestbookEntryModelImpl._originalGuestbookId =\n\t\t\tguestbookEntryModelImpl._guestbookId;\n\n\t\tguestbookEntryModelImpl._setOriginalGuestbookId = false;\n\n\t\tguestbookEntryModelImpl._originalGroupId =\n\t\t\tguestbookEntryModelImpl._groupId;\n\n\t\tguestbookEntryModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookEntryModelImpl._originalCompanyId =\n\t\t\tguestbookEntryModelImpl._companyId;\n\n\t\tguestbookEntryModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookEntryModelImpl._setModifiedDate = false;\n\n\t\tguestbookEntryModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<GuestbookEntry> toCacheModel() {\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\tnew GuestbookEntryCacheModel();\n\n\t\tguestbookEntryCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookEntryCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.entryId = getEntryId();\n\n\t\tguestbookEntryCacheModel.name = getName();\n\n\t\tString name = guestbookEntryCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.name = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.email = getEmail();\n\n\t\tString email = guestbookEntryCacheModel.email;\n\n\t\tif ((email != null) && (email.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.email = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.message = getMessage();\n\n\t\tString message = guestbookEntryCacheModel.message;\n\n\t\tif ((message != null) && (message.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.message = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookEntryCacheModel.groupId = getGroupId();\n\n\t\tguestbookEntryCacheModel.companyId = getCompanyId();\n\n\t\tguestbookEntryCacheModel.userId = getUserId();\n\n\t\tguestbookEntryCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookEntryCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookEntryCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookEntryCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookEntryCacheModel.status = getStatus();\n\n\t\tguestbookEntryCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookEntryCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookEntryCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookEntryCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookEntryCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, GuestbookEntry>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _originalGuestbookId;\n\tprivate boolean _setOriginalGuestbookId;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate GuestbookEntry _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.Guestbook<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookImpl extends GuestbookBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook model instance should use the {@link com.liferay.docs.guestbook.model.Guestbook} interface instead.\n\t */\n\tpublic GuestbookImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookModel;\nimport com.liferay.docs.guestbook.model.GuestbookSoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookModelImpl\n\textends BaseModelImpl<Guestbook> implements GuestbookModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_Guestbook\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"groupId\", Types.BIGINT},\n\t\t{\"companyId\", Types.BIGINT}, {\"userId\", Types.BIGINT},\n\t\t{\"userName\", Types.VARCHAR}, {\"createDate\", Types.TIMESTAMP},\n\t\t{\"modifiedDate\", Types.TIMESTAMP}, {\"status\", Types.INTEGER},\n\t\t{\"statusByUserId\", Types.BIGINT}, {\"statusByUserName\", Types.VARCHAR},\n\t\t{\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_Guestbook (uuid_ VARCHAR(75) null,guestbookId LONG not null primary key,name VARCHAR(75) null,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_Guestbook\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbook.guestbookId ASC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_Guestbook.guestbookId ASC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 4L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 8L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static Guestbook toModel(GuestbookSoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbook model = new GuestbookImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<Guestbook> toModels(GuestbookSoap[] soapModels) {\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> models = new ArrayList<Guestbook>(soapModels.length);\n\n\t\tfor (GuestbookSoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetGuestbookId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName, attributeGetterFunction.apply((Guestbook)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<Guestbook, Object>> attributeSetterBiConsumers =\n\t\t\tgetAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<Guestbook, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(Guestbook)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<Guestbook, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<Guestbook, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, Guestbook>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbook.class.getClassLoader(), Guestbook.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<Guestbook> constructor =\n\t\t\t\t(Constructor<Guestbook>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<Guestbook, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<Guestbook, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<Guestbook, Object>>();\n\t\tMap<String, BiConsumer<Guestbook, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<Guestbook, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", Guestbook::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\", (BiConsumer<Guestbook, String>)Guestbook::setUuid);\n\t\tattributeGetterFunctions.put(\"guestbookId\", Guestbook::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"name\", Guestbook::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\", (BiConsumer<Guestbook, String>)Guestbook::setName);\n\t\tattributeGetterFunctions.put(\"groupId\", Guestbook::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\", (BiConsumer<Guestbook, Long>)Guestbook::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", Guestbook::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\", (BiConsumer<Guestbook, Long>)Guestbook::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", Guestbook::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\", (BiConsumer<Guestbook, Long>)Guestbook::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", Guestbook::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\", (BiConsumer<Guestbook, String>)Guestbook::setUserName);\n\t\tattributeGetterFunctions.put(\"createDate\", Guestbook::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", Guestbook::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", Guestbook::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\", (BiConsumer<Guestbook, Integer>)Guestbook::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", Guestbook::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", Guestbook::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<Guestbook, String>)Guestbook::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\"statusDate\", Guestbook::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), Guestbook.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic Guestbook toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tguestbookImpl.setUuid(getUuid());\n\t\tguestbookImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookImpl.setName(getName());\n\t\tguestbookImpl.setGroupId(getGroupId());\n\t\tguestbookImpl.setCompanyId(getCompanyId());\n\t\tguestbookImpl.setUserId(getUserId());\n\t\tguestbookImpl.setUserName(getUserName());\n\t\tguestbookImpl.setCreateDate(getCreateDate());\n\t\tguestbookImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookImpl.setStatus(getStatus());\n\t\tguestbookImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(Guestbook guestbook) {\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() < primaryKey) {\n\t\t\treturn -1;\n\t\t}\n\t\telse if (getPrimaryKey() > primaryKey) {\n\t\t\treturn 1;\n\t\t}\n\t\telse {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof Guestbook)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbook guestbook = (Guestbook)obj;\n\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookModelImpl guestbookModelImpl = this;\n\n\t\tguestbookModelImpl._originalUuid = guestbookModelImpl._uuid;\n\n\t\tguestbookModelImpl._originalGroupId = guestbookModelImpl._groupId;\n\n\t\tguestbookModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookModelImpl._originalCompanyId = guestbookModelImpl._companyId;\n\n\t\tguestbookModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookModelImpl._setModifiedDate = false;\n\n\t\tguestbookModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<Guestbook> toCacheModel() {\n\t\tGuestbookCacheModel guestbookCacheModel = new GuestbookCacheModel();\n\n\t\tguestbookCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookCacheModel.name = getName();\n\n\t\tString name = guestbookCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookCacheModel.name = null;\n\t\t}\n\n\t\tguestbookCacheModel.groupId = getGroupId();\n\n\t\tguestbookCacheModel.companyId = getCompanyId();\n\n\t\tguestbookCacheModel.userId = getUserId();\n\n\t\tguestbookCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookCacheModel.status = getStatus();\n\n\t\tguestbookCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, Guestbook>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate Guestbook _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookEntryLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\tguestbookEntry.setNew(true);\n\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.create(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbookEntry.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId) {\n\n\t\treturn guestbookEntryPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookEntryLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbookEntry.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<GuestbookEntry>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(GuestbookEntry guestbookEntry)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbookEntry);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryLocalService.deleteGuestbookEntry(\n\t\t\t(GuestbookEntry)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end) {\n\t\treturn guestbookEntryPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn guestbookEntryPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryLocalService = (GuestbookEntryLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\tprotected GuestbookEntryLocalService guestbookEntryLocalService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl\n * @generated\n */\npublic abstract class GuestbookEntryServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookEntryService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryService = (GuestbookEntryService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookEntryLocalService\n\t\tguestbookEntryLocalService;\n\n\tprotected GuestbookEntryService guestbookEntryService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook addGuestbook(Guestbook guestbook) {\n\t\tguestbook.setNew(true);\n\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.create(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.remove(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.remove(guestbook);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbook.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic Guestbook fetchGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\t\treturn guestbookPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.findByPrimaryKey(guestbookId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\n\t\t\t\"guestbookId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbook.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<Guestbook>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(Guestbook guestbook)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbook);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookLocalService.deleteGuestbook((Guestbook)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn guestbookPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooks(int start, int end) {\n\t\treturn guestbookPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn guestbookPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook updateGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookLocalService = (GuestbookLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\tprotected GuestbookLocalService guestbookLocalService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl\n * @generated\n */\npublic abstract class GuestbookServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookService = (GuestbookService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookLocalService\n\t\tguestbookLocalService;\n\n\tprotected GuestbookService guestbookService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntry</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.Guestbook</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.docs.guestbook.exception.GuestbookEntryEmailException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryMessageException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryNameException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.ResourceConstants;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\n\n/**\n * The implementation of the guestbook entry local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\", service = AopService.class)\npublic class GuestbookEntryLocalServiceImpl extends GuestbookEntryLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n\t * via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code\n\t * >.\n\t */\n\n\tpublic GuestbookEntry addGuestbookEntry(long userId, long guestbookId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tlong entryId = counterLocalService.increment();\n\n\t\tGuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\n\t\tentry.setUuid(serviceContext.getUuid());\n\t\tentry.setUserId(userId);\n\t\tentry.setGroupId(groupId);\n\t\tentry.setCompanyId(user.getCompanyId());\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setCreateDate(serviceContext.getCreateDate(now));\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\t\tentry.setGuestbookId(guestbookId);\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Calls to other Liferay frameworks go here\n\n\t\tresourceLocalService.addResources(user.getCompanyId(), groupId, userId,\n\t\t\t\tGuestbookEntry.class.getName(), entryId, false, true, true);\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry updateGuestbookEntry(long userId, long guestbookId,\n\t\t\tlong entryId, String name, String email, String message,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException {\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tentry.setUserId(userId);\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Integrate with Liferay frameworks here.\n\n\t\tresourceLocalService.updateResources(\n\t\t\t\t  user.getCompanyId(), serviceContext.getScopeGroupId(), \n\t\t\t\t  GuestbookEntry.class.getName(), entryId, \n\t\t\t\t  serviceContext.getModelPermissions());\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry entry)\n\t\tthrows PortalException {\n\n\t\tguestbookEntryPersistence.remove(entry);\n\n\t\tresourceLocalService.deleteResource(\n\t\t\t   entry.getCompanyId(), GuestbookEntry.class.getName(),\n\t\t\t   ResourceConstants.SCOPE_INDIVIDUAL, entry.getEntryId());\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId) throws PortalException {\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\treturn deleteGuestbookEntry(entry);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end) throws SystemException {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end, OrderByComparator<GuestbookEntry> obc) {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend, obc);\n\t}\n\n\tpublic GuestbookEntry getGuestbookEntry(long entryId) throws PortalException {\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.countByG_G(groupId, guestbookId);\n\t}\n\n\tprotected void validate(String name, String email, String entry)\n\t\tthrows PortalException {\n\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookEntryNameException();\n\t\t}\n\n\t\tif (!Validator.isEmailAddress(email)) {\n\t\t\tthrow new GuestbookEntryEmailException();\n\t\t}\n\n\t\tif (Validator.isNull(entry)) {\n\t\t\tthrow new GuestbookEntryMessageException();\n\t\t}\n\t}\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook entry remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookEntryService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=GuestbookEntry\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookEntryServiceImpl extends GuestbookEntryServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> to access the guestbook entry remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.exception.GuestbookNameException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.base.GuestbookLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.ResourceConstants;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\n\n/**\n * The implementation of the guestbook local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.Guestbook\", service = AopService.class)\npublic class GuestbookLocalServiceImpl extends GuestbookLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code> via\n\t * injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\tpublic Guestbook addGuestbook(long userId, String name,\n\t\t\tServiceContext serviceContext) throws PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name);\n\n\t\tlong guestbookId = counterLocalService.increment();\n\n\t\tGuestbook guestbook = guestbookPersistence.create(guestbookId);\n\n\t\tguestbook.setUuid(serviceContext.getUuid());\n\t\tguestbook.setUserId(userId);\n\t\tguestbook.setGroupId(groupId);\n\t\tguestbook.setCompanyId(user.getCompanyId());\n\t\tguestbook.setUserName(user.getFullName());\n\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tguestbook.setName(name);\n\t\tguestbook.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookPersistence.update(guestbook);\n\n\t\tresourceLocalService.addResources(user.getCompanyId(), groupId, userId,\n\t\t\t\tGuestbook.class.getName(), guestbookId, false, true, true);\n\t\t\treturn guestbook;\n\t}\n\n\tpublic Guestbook updateGuestbook(long userId, long guestbookId,\n        String name, ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Date now = new Date();\n\n            validate(name);\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            User user = userLocalService.getUser(userId);\n\n            guestbook.setUserId(userId);\n            guestbook.setUserName(user.getFullName());\n            guestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n            guestbook.setName(name);\n            guestbook.setExpandoBridgeAttributes(serviceContext);\n\n            guestbookPersistence.update(guestbook);\n\n            resourceLocalService.updateResources(serviceContext.getCompanyId(),\n                    serviceContext.getScopeGroupId(), \n                    Guestbook.class.getName(), guestbookId,\n                    serviceContext.getModelPermissions());\n\n            return guestbook;\n    }\n\n\tpublic Guestbook deleteGuestbook(long guestbookId,\n                    ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            List<GuestbookEntry> entries = _guestbookEntryLocalService.getGuestbookEntries(\n                            serviceContext.getScopeGroupId(), guestbookId);\n\n            for (GuestbookEntry entry : entries) {\n                    _guestbookEntryLocalService.deleteGuestbookEntry(entry.getEntryId());\n            }\n\n            guestbook = deleteGuestbook(guestbook);\n\n            resourceLocalService.deleteResource(serviceContext.getCompanyId(),\n                    Guestbook.class.getName(), ResourceConstants.SCOPE_INDIVIDUAL,\n                    guestbookId);\n\n            return guestbook;\n    }\n\n\tpublic List<Guestbook> getGuestbooks(long groupId) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end,\n\t\t\tOrderByComparator<Guestbook> obc) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end, obc);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end);\n\t}\n\n\tpublic int getGuestbooksCount(long groupId) {\n\n\t\treturn guestbookPersistence.countByGroupId(groupId);\n\t}\n\n\tprotected void validate(String name) throws PortalException {\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookNameException();\n\t\t}\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=Guestbook\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookServiceImpl extends GuestbookServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> to access the guestbook remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookEntryPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookEntryPersistence.class)\n@ProviderType\npublic class GuestbookEntryPersistenceImpl\n\textends BasePersistenceImpl<GuestbookEntry>\n\timplements GuestbookEntryPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookEntryUtil</code> to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookEntryImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_First(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_Last(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbookEntry.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof GuestbookEntry) {\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbookEntry.getUuid()) ||\n\t\t\t\t(groupId != guestbookEntry.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<GuestbookEntry> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbookEntry guestbookEntry = list.get(0);\n\n\t\t\t\t\tresult = guestbookEntry;\n\n\t\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (GuestbookEntry)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\t@Override\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbookEntry.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\ttrue);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\tfalse);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_C_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tlong companyId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbookEntry.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByG_G;\n\tprivate FinderPath _finderPathWithoutPaginationFindByG_G;\n\tprivate FinderPath _finderPathCountByG_G;\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(long groupId, long guestbookId) {\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn findByG_G(groupId, guestbookId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {groupId, guestbookId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tgroupId, guestbookId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif ((groupId != guestbookEntry.getGroupId()) ||\n\t\t\t\t\t\t(guestbookId != guestbookEntry.getGuestbookId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByG_G(groupId, guestbookId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByG_G_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tlong guestbookId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(guestbookId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\t@Override\n\tpublic void removeByG_G(long groupId, long guestbookId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByG_G(\n\t\t\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByG_G(long groupId, long guestbookId) {\n\t\tFinderPath finderPath = _finderPathCountByG_G;\n\n\t\tObject[] finderArgs = new Object[] {groupId, guestbookId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_G_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_G_G_GUESTBOOKID_2 =\n\t\t\"guestbookEntry.guestbookId = ?\";\n\n\tpublic GuestbookEntryPersistenceImpl() {\n\t\tsetModelClass(GuestbookEntry.class);\n\n\t\tsetModelImplClass(GuestbookEntryImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\t@Override\n\tpublic void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {\n\t\t\t\tguestbookEntry.getUuid(), guestbookEntry.getGroupId()\n\t\t\t},\n\t\t\tguestbookEntry);\n\n\t\tguestbookEntry.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\t@Override\n\tpublic void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbook entries.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookEntryImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook entry.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(GuestbookEntry guestbookEntry) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookEntryModelImpl)guestbookEntry, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<GuestbookEntry> guestbookEntries) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache(\n\t\t\t\t(GuestbookEntryModelImpl)guestbookEntry, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookEntryModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic GuestbookEntry create(long entryId) {\n\t\tGuestbookEntry guestbookEntry = new GuestbookEntryImpl();\n\n\t\tguestbookEntry.setNew(true);\n\t\tguestbookEntry.setPrimaryKey(entryId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbookEntry.setUuid(uuid);\n\n\t\tguestbookEntry.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn remove((Serializable)entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\tGuestbookEntryImpl.class, primaryKey);\n\n\t\t\tif (guestbookEntry == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbookEntry);\n\t\t}\n\t\tcatch (NoSuchGuestbookEntryException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected GuestbookEntry removeImpl(GuestbookEntry guestbookEntry) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbookEntry)) {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\t\tGuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbookEntry != null) {\n\t\t\t\tsession.delete(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbookEntry != null) {\n\t\t\tclearCache(guestbookEntry);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t@Override\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\tboolean isNew = guestbookEntry.isNew();\n\n\t\tif (!(guestbookEntry instanceof GuestbookEntryModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbookEntry.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(\n\t\t\t\t\tguestbookEntry);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbookEntry proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom GuestbookEntry implementation \" +\n\t\t\t\t\tguestbookEntry.getClass());\n\t\t}\n\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl =\n\t\t\t(GuestbookEntryModelImpl)guestbookEntry;\n\n\t\tif (Validator.isNull(guestbookEntry.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbookEntry.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbookEntry.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookEntryModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setModifiedDate(\n\t\t\t\t\tserviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbookEntry.isNew()) {\n\t\t\t\tsession.save(guestbookEntry);\n\n\t\t\t\tguestbookEntry.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.merge(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByG_G.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry, false);\n\n\t\tclearUniqueFindersCache(guestbookEntryModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookEntryModelImpl);\n\n\t\tguestbookEntry.resetOriginalValues();\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn findByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn fetchByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOKENTRY;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (GuestbookEntry guestbookEntry : findAll()) {\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOKENTRY);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"entryId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOKENTRY;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookEntryModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook entry persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookEntryModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookEntryModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.COMPANYID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GUESTBOOKID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookEntryImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.GuestbookEntry\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbookEntry.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No GuestbookEntry exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No GuestbookEntry exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookEntryPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.impl.GuestbookImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookPersistence.class)\n@ProviderType\npublic class GuestbookPersistenceImpl\n\textends BasePersistenceImpl<Guestbook> implements GuestbookPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookUtil</code> to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_First(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_Last(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbook.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbook == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof Guestbook) {\n\t\t\tGuestbook guestbook = (Guestbook)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbook.getUuid()) ||\n\t\t\t\t(groupId != guestbook.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<Guestbook> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbook guestbook = list.get(0);\n\n\t\t\t\t\tresult = guestbook;\n\n\t\t\t\t\tcacheResult(guestbook);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (Guestbook)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbook);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbook.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_C_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbook.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByGroupId;\n\tprivate FinderPath _finderPathWithoutPaginationFindByGroupId;\n\tprivate FinderPath _finderPathCountByGroupId;\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId) {\n\t\treturn findByGroupId(\n\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId, int start, int end) {\n\t\treturn findByGroupId(groupId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByGroupId(groupId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif ((groupId != guestbook.getGroupId())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_First(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByGroupId(groupId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_Last(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByGroupId(groupId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByGroupId(\n\t\t\tgroupId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByGroupId_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\t@Override\n\tpublic void removeByGroupId(long groupId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByGroupId(\n\t\t\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByGroupId(long groupId) {\n\t\tFinderPath finderPath = _finderPathCountByGroupId;\n\n\t\tObject[] finderArgs = new Object[] {groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_GROUPID_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tpublic GuestbookPersistenceImpl() {\n\t\tsetModelClass(Guestbook.class);\n\n\t\tsetModelImplClass(GuestbookImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\t@Override\n\tpublic void cacheResult(Guestbook guestbook) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {guestbook.getUuid(), guestbook.getGroupId()},\n\t\t\tguestbook);\n\n\t\tguestbook.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\t@Override\n\tpublic void cacheResult(List<Guestbook> guestbooks) {\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\t\tguestbook.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbook);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbooks.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(Guestbook guestbook) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<Guestbook> guestbooks) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\tguestbook.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic Guestbook create(long guestbookId) {\n\t\tGuestbook guestbook = new GuestbookImpl();\n\n\t\tguestbook.setNew(true);\n\t\tguestbook.setPrimaryKey(guestbookId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbook.setUuid(uuid);\n\n\t\tguestbook.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException {\n\t\treturn remove((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook guestbook = (Guestbook)session.get(\n\t\t\t\tGuestbookImpl.class, primaryKey);\n\n\t\t\tif (guestbook == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbook);\n\t\t}\n\t\tcatch (NoSuchGuestbookException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected Guestbook removeImpl(Guestbook guestbook) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbook)) {\n\t\t\t\tguestbook = (Guestbook)session.get(\n\t\t\t\t\tGuestbookImpl.class, guestbook.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbook != null) {\n\t\t\t\tsession.delete(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbook != null) {\n\t\t\tclearCache(guestbook);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t@Override\n\tpublic Guestbook updateImpl(Guestbook guestbook) {\n\t\tboolean isNew = guestbook.isNew();\n\n\t\tif (!(guestbook instanceof GuestbookModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbook.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(guestbook);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbook proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom Guestbook implementation \" +\n\t\t\t\t\tguestbook.getClass());\n\t\t}\n\n\t\tGuestbookModelImpl guestbookModelImpl = (GuestbookModelImpl)guestbook;\n\n\t\tif (Validator.isNull(guestbook.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbook.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbook.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbook.isNew()) {\n\t\t\t\tsession.save(guestbook);\n\n\t\t\t\tguestbook.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook = (Guestbook)session.merge(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getUuid(),\n\t\t\t\t\tguestbookModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByGroupId.\n\t\t\t\t\t getColumnBitmask()) != 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook, false);\n\n\t\tclearUniqueFindersCache(guestbookModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookModelImpl);\n\n\t\tguestbook.resetOriginalValues();\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbook == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\treturn findByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn fetchByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOK);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOK;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (Guestbook guestbook : findAll()) {\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOK);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"guestbookId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOK;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.COMPANYID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {Long.class.getName()},\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByGroupId\",\n\t\t\tnew String[] {Long.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.Guestbook\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK =\n\t\t\"SELECT guestbook FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK_WHERE =\n\t\t\"SELECT guestbook FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK_WHERE =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbook.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No Guestbook exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No Guestbook exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/constants/GBPersistenceConstants.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl.constants;\n\nimport com.liferay.petra.string.StringBundler;\n\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.Constants;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * @author Liferay\n * @generated\n */\n@Component(immediate = true, service = {})\npublic class GBPersistenceConstants {\n\n\tpublic static final String BUNDLE_SYMBOLIC_NAME =\n\t\t\"com.liferay.docs.guestbook.service\";\n\n\tpublic static final String ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER =\n\t\t\"(origin.bundle.symbolic.name=\" + BUNDLE_SYMBOLIC_NAME + \")\";\n\n\t@Activate\n\tprotected void activate(BundleContext bundleContext) {\n\t\tBundle bundle = bundleContext.getBundle();\n\n\t\tif (!BUNDLE_SYMBOLIC_NAME.equals(bundle.getSymbolicName())) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\tStringBundler.concat(\n\t\t\t\t\t\"Incorrect \", Constants.BUNDLE_SYMBOLICNAME, \" for bundle \",\n\t\t\t\t\tbundle.getSymbolicName()));\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/module-hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">\n\n<hibernate-mapping auto-import=\"false\" default-lazy=\"false\">\n\t<import class=\"com.liferay.docs.guestbook.model.Guestbook\" />\n\t<import class=\"com.liferay.docs.guestbook.model.GuestbookEntry\" />\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\" table=\"GB_Guestbook\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\" table=\"GB_GuestbookEntry\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"entryId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"email\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"message\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n</hibernate-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/portlet-model-hints.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<model-hints>\n\t<model name=\"com.liferay.docs.guestbook.model.Guestbook\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n\t<model name=\"com.liferay.docs.guestbook.model.GuestbookEntry\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"entryId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"email\" type=\"String\" />\n\t\t<field name=\"message\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n</model-hints>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/resource-actions/default.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action  \nMapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n\n<resource-action-mapping>\n\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n        </portlet-ref>\n        <root>true</root>\n        <permissions>\n            <supports>\n                <action-key>ADD_GUESTBOOK</action-key>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>ADD_ENTRY</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>ADD_GUESTBOOK</action-key>\n                <action-key>ADD_ENTRY</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook.model.Guestbook</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n        </portlet-ref>\n        <permissions>\n            <supports>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>DELETE</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>UPDATE</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>UPDATE</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook.model.GuestbookEntry</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n        </portlet-ref>\n        <permissions>\n            <supports>\n                <action-key>DELETE</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>UPDATE</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>UPDATE</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n\n</resource-action-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/indexes.sql",
    "content": "create index IX_9294AD47 on GB_Guestbook (groupId);\ncreate index IX_9314A9F7 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_EDD4239 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], groupId);\n\ncreate index IX_E84D72FD on GB_GuestbookEntry (groupId, guestbookId);\ncreate index IX_CC265FEF on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_4A541631 on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], groupId);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/sequences.sql",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/tables.sql",
    "content": "create table GB_Guestbook (\n\tuuid_ VARCHAR(75) null,\n\tguestbookId LONG not null primary key,\n\tname VARCHAR(75) null,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);\n\ncreate table GB_GuestbookEntry (\n\tuuid_ VARCHAR(75) null,\n\tentryId LONG not null primary key,\n\tname VARCHAR(75) null,\n\temail VARCHAR(75) null,\n\tmessage VARCHAR(75) null,\n\tguestbookId LONG,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/portlet.properties",
    "content": "resource.actions.configs=META-INF/resource-actions/default.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/service.properties",
    "content": "##\n## Properties Override\n##\n\n    #\n    # Specify where to get the overridden properties. Updates should not be made\n    # on this file but on the overridden version of this file.\n    #\n    include-and-override=service-ext.properties\n\n##\n## Build\n##\n\n    build.namespace=GB\n    build.number=5\n    build.date=1567633317058"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/.gitignore",
    "content": ".gradle/\n.sass-cache/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/bnd.bnd",
    "content": "Bundle-Name: guestbook-web\nBundle-SymbolicName: com.liferay.docs.guestbook.portlet\nBundle-Version: 1.0.0\nExport-Package: com.liferay.docs.guestbook.portlet.constants"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.application.list.api\"\n\tcompileOnly group: \"javax.portlet\", name: \"portlet-api\"\n\tcompileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n\tcompileOnly group: \"jstl\", name: \"jstl\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\n    compileOnly project(\":modules:guestbook:guestbook-api\")\n    compileOnly project(\":modules:guestbook:guestbook-service\")\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/application/list/GuestbookAdminPanelApp.java",
    "content": "package com.liferay.docs.guestbook.application.list;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.application.list.BasePanelApp;\nimport com.liferay.application.list.PanelApp;\nimport com.liferay.application.list.constants.PanelCategoryKeys;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.portal.kernel.model.Portlet;\n\n@Component(\n        immediate = true,\n        property = {\n            \"panel.app.order:Integer=300\",\n            \"panel.category.key=\" + PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT\n        },\n        service = PanelApp.class\n    )\npublic class GuestbookAdminPanelApp extends BasePanelApp {\n\n        @Override\n        public String getPortletId() {\n            return GuestbookPortletKeys.GUESTBOOK_ADMIN;\n        }\n\n        @Override\n        @Reference(\n            target = \"(javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN + \")\",\n            unbind = \"-\"\n        )\n        public void setPortlet(Portlet portlet) {\n            super.setPortlet(portlet);\n        }\n\n    }\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookAdminPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.servlet.SessionErrors;\nimport com.liferay.portal.kernel.servlet.SessionMessages;\nimport com.liferay.portal.kernel.util.ParamUtil;\n@Component(\n        immediate = true,\n        property = {\n                \"com.liferay.portlet.display-category=category.hidden\",\n                \"com.liferay.portlet.scopeable=true\",\n                \"javax.portlet.display-name=Guestbooks\",\n                \"javax.portlet.expiration-cache=0\",\n                \"javax.portlet.init-param.portlet-title-based-navigation=true\",\n                \"javax.portlet.init-param.template-path=/\",\n                \"javax.portlet.init-param.view-template=/guestbook_admin/view.jsp\",\n                \"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN,\n                \"javax.portlet.resource-bundle=content.Language\",\n                \"javax.portlet.security-role-ref=administrator\",\n                \"javax.portlet.supports.mime-type=text/html\",\n                \"com.liferay.portlet.add-default-resource=true\"\n        },\n        service = Portlet.class\n    )\npublic class GuestbookAdminPortlet extends MVCPortlet {\n\npublic void addGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n\n        try {\n            _guestbookLocalService.addGuestbook(\n                serviceContext.getUserId(), name, serviceContext);\n\n            SessionMessages.add(request, \"guestbookAdded\");\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            SessionErrors.add(request, pe.getClass().getName());\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n\n    public void updateGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.updateGuestbook(\n                serviceContext.getUserId(), guestbookId, name, serviceContext);\n\n            SessionMessages.add(request, \"guestbookUpdated\");\n\n        } catch (PortalException pe) {\n        \n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            SessionErrors.add(request, pe.getClass().getName());\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n    \n    public void deleteGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.deleteGuestbook(guestbookId, serviceContext);\n            SessionMessages.add(request, \"guestbookDeleted\");\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n            SessionErrors.add(request, pe.getClass().getName());\n        }\n    }\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\nimport javax.portlet.PortletException;\nimport javax.portlet.RenderRequest;\nimport javax.portlet.RenderResponse;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.servlet.SessionErrors;\nimport com.liferay.portal.kernel.servlet.SessionMessages;\nimport com.liferay.portal.kernel.util.ParamUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\n/**\n * @author sezovr\n */\n@Component(immediate = true, property = { \"com.liferay.portlet.display-category=category.social\",\n\t\t\"com.liferay.portlet.header-portlet-css=/css/main.css\", \n\t\t\"com.liferay.portlet.instanceable=false\",\n\t\t\"com.liferay.portlet.scopeable=true\", \n\t\t\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK,\n\t\t\"javax.portlet.display-name=Guestbook\", \n\t\t\"javax.portlet.expiration-cache=0\",\n\t\t\"javax.portlet.init-param.template-path=/\", \n\t\t\"javax.portlet.init-param.view-template=/guestbook/view.jsp\",\n\t\t\"javax.portlet.resource-bundle=content.Language\", \n\t\t\"javax.portlet.security-role-ref=power-user,user\",\n\t\t\"javax.portlet.supports.mime-type=text/html\" }, \n\tservice = Portlet.class)\npublic class GuestbookPortlet extends MVCPortlet {\n\n\tpublic void addEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\n\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(GuestbookEntry.class.getName(), request);\n\n\t\tString userName = ParamUtil.getString(request, \"name\");\n\t\tString email = ParamUtil.getString(request, \"email\");\n\t\tString message = ParamUtil.getString(request, \"message\");\n\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\n\t\tif (entryId > 0) {\n\n\t\t\ttry {\n\n\t\t\t\t_guestbookEntryLocalService.updateGuestbookEntry(serviceContext.getUserId(), guestbookId, entryId, userName,\n\t\t\t\t\t\temail, message, serviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t\tSessionMessages.add(request, \"entryAdded\");\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(e);\n\t\t\t\tSessionErrors.add(request, e.getClass().getName());\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\n\t\t} else {\n\n\t\t\ttry {\n\t\t\t\t_guestbookEntryLocalService.addGuestbookEntry(serviceContext.getUserId(), guestbookId, userName, email, message,\n\t\t\t\t\t\tserviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t} catch (Exception e) {\n\n\t\t\t\tSystem.out.println(e);\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic void deleteEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\t\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\t\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\tGuestbookEntry.class.getName(), request);\n\n\t\t\ttry {\n\n\t\t\t\tresponse.setRenderParameter(\n\t\t\t\t\t\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t\t_guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\t\t\t}\n\n\t\t\tcatch (Exception e) {\n\t\t\t\tLogger.getLogger(GuestbookPortlet.class.getName()).log(\n\t\t\t\t\tLevel.SEVERE, null, e);\n\n\t\t\t\tSessionErrors.add(request, e.getClass().getName());\n\t\t\t}\n\t}\n\n\t@Override\n\tpublic void render(RenderRequest renderRequest, RenderResponse renderResponse)\n\t\t\tthrows IOException, PortletException {\n\n\t\t\ttry {\n\t\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\t\tGuestbook.class.getName(), renderRequest);\n\n\t\t\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\t\t\tlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n\t\t\t\tList<Guestbook> guestbooks = _guestbookLocalService.getGuestbooks(\n\t\t\t\t\tgroupId);\n\n\t\t\t\tif (guestbooks.isEmpty()) {\n\t\t\t\t\tGuestbook guestbook = _guestbookLocalService.addGuestbook(\n\t\t\t\t\t\tserviceContext.getUserId(), \"Main\", serviceContext);\n\n\t\t\t\t\tguestbookId = guestbook.getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\tif (guestbookId == 0) {\n\t\t\t\t\tguestbookId = guestbooks.get(0).getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\trenderRequest.setAttribute(\"guestbookId\", guestbookId);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new PortletException(e);\n\t\t\t}\n\n\t\t\tsuper.render(renderRequest, renderResponse);\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/security/permission/resource/GuestbookEntryPermission.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.web.internal.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n\n@Component(immediate = true)\npublic class GuestbookEntryPermission {\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, GuestbookEntry entry, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookEntryModelResourcePermission.contains(permissionChecker, entry, actionId);\n\t}\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, long entryId, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookEntryModelResourcePermission.contains(permissionChecker, entryId, actionId);\n\t}\n\t\n\t@Reference(\n\t\t\ttarget = \"(model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\", \n\t\t\tunbind = \"-\")\n\tprotected void setEntryModelPermission(ModelResourcePermission<GuestbookEntry> modelResourcePermission) {\n\t\t\n\t\t_guestbookEntryModelResourcePermission = modelResourcePermission;\n\t}\n\t\n\tprivate static ModelResourcePermission<GuestbookEntry>_guestbookEntryModelResourcePermission;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/security/permission/resource/GuestbookModelPermission.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.web.internal.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n\n@Component(immediate = true)\npublic class GuestbookModelPermission {\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, Guestbook guestbook, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookModelResourcePermission.contains(permissionChecker, guestbook, actionId);\n\t}\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, long guestbookId, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookModelResourcePermission.contains(permissionChecker, guestbookId, actionId);\n\t}\n\t\n\t@Reference(\n\t\t\ttarget = \"(model.class.name=com.liferay.docs.guestbook.model.Guestbook)\", \n\t\t\tunbind = \"-\")\n\tprotected void setEntryModelPermission(ModelResourcePermission<Guestbook> modelResourcePermission) {\n\t\t\n\t\t_guestbookModelResourcePermission = modelResourcePermission;\n\t}\n\t\n\tprivate static ModelResourcePermission<Guestbook>_guestbookModelResourcePermission;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/security/permission/resource/GuestbookPermission.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.web.internal.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\n\n@Component(immediate=true)\npublic class GuestbookPermission {\n\n\tpublic static boolean contains(PermissionChecker permissionChecker, long groupId, String actionId) {\n\t\t\n\t\treturn _portletResourcePermission.contains(permissionChecker, groupId, actionId);\n\t\t\n\t}\n\t\n\t@Reference(\n\t\t\ttarget=\"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\", \n\t\t\tunbind=\"-\"\n\t\t\t)\n\tprotected void setPortletResourcePermission(PortletResourcePermission portletResourcePermission) {\n\t\t\n\t\t_portletResourcePermission = portletResourcePermission;\n\t}\n\t\n\tprivate static PortletResourcePermission _portletResourcePermission;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resource-actions/default.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action  \nMapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n\n<resource-action-mapping>\n\n    <portlet-resource>\n        <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n        <permissions>\n            <supports>\n                <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n                <action-key>CONFIGURATION</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n                <action-key>CONFIGURATION</action-key>\n            </guest-unsupported>\n        </permissions>\n    </portlet-resource>\n\n    <portlet-resource>\n        <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n        <permissions>\n            <supports>\n                <action-key>ADD_TO_PAGE</action-key>\n                <action-key>CONFIGURATION</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported />\n        </permissions>\n    </portlet-resource>\n\n</resource-action-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/css/main.scss",
    "content": ".guestbook-web {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/edit_entry.jsp",
    "content": "<%@include file=\"../init.jsp\" %>\n\n<% \n\nlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\nGuestbookEntry entry = null;\nif (entryId > 0) {\n  entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n}\n\nlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n%>\n\n<portlet:renderURL var=\"viewURL\">\n\t<portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\"></portlet:param>\n</portlet:renderURL>\n\n<portlet:actionURL name=\"addEntry\" var=\"addEntryURL\"></portlet:actionURL>\n\n<aui:form action=\"<%= addEntryURL %>\" name=\"<portlet:namespace />fm\">\n\n<aui:model-context bean=\"<%= entry %>\" model=\"<%= GuestbookEntry.class %>\" />\n\n\t<aui:fieldset>\n\n\t\t<aui:input name=\"name\" />\n\t\t<aui:input name=\"email\" />\n\t\t<aui:input name=\"message\" />\n\t\t<aui:input name=\"entryId\" type=\"hidden\" />\n\t\t<aui:input name=\"guestbookId\" type=\"hidden\" value='<%= entry == null ? guestbookId : entry.getGuestbookId() %>'/>\n\n\t</aui:fieldset>\n\n\t<aui:button-row>\n\n\t\t<aui:button type=\"submit\"></aui:button>\n\t\t<aui:button type=\"cancel\" onClick=\"<%= viewURL.toString() %>\"></aui:button>\n\n\t</aui:button-row>\n</aui:form>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/entry_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\nString mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\nResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);\n\nGuestbookEntry entry = (GuestbookEntry)row.getObject(); \n%>\n\n<liferay-ui:icon-menu>\n\n\t<c:if\n\t\ttest=\"<%= GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.UPDATE) %>\">\n\t\t<portlet:renderURL var=\"editURL\">\n\t\t\t<portlet:param name=\"entryId\"\n\t\t\t\tvalue=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n\t\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n\t\t</portlet:renderURL>\n\n\t\t<liferay-ui:icon image=\"edit\" message=\"Edit\"\n\t\t\turl=\"<%=editURL.toString() %>\" />\n\t</c:if>\n\n\t<c:if\n\ttest=\"<%=GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.PERMISSIONS) %>\">\n\n\t\t<liferay-security:permissionsURL\n\t\t\tmodelResource=\"<%= GuestbookEntry.class.getName() %>\"\n\t\t\tmodelResourceDescription=\"<%= entry.getMessage() %>\"\n\t\t\tresourcePrimKey=\"<%= String.valueOf(entry.getEntryId()) %>\"\n\t\t\tvar=\"permissionsURL\" />\n\t\n\t\t<liferay-ui:icon image=\"permissions\" url=\"<%= permissionsURL %>\" />\n\n\t</c:if>\n\n\t<c:if\n\t\ttest=\"<%=GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.DELETE) %>\">\n\n\t\t<portlet:actionURL name=\"deleteEntry\" var=\"deleteURL\">\n\t\t\t<portlet:param name=\"entryId\"\n\t\t\t\tvalue=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\tvalue=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n\t\t</portlet:actionURL>\n\n\t\t<liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\t</c:if>\n\n</liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<liferay-ui:success key=\"entryAdded\" message=\"entry-added\" />\n<liferay-ui:success key=\"entryDeleted\" message=\"entry-deleted\" />\n\n<%\nlong guestbookId = Long.valueOf((Long) renderRequest\n\t\t.getAttribute(\"guestbookId\"));\n%>\n\n\n<aui:nav cssClass=\"nav-tabs\">\n\n\t<%\n\t\tList<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n\t\t\tfor (int i = 0; i < guestbooks.size(); i++) {\n\n\t\t\t\tGuestbook curGuestbook = guestbooks.get(i);\n\t\t\t\tString cssClass = StringPool.BLANK;\n\n\t\t\t\tif (curGuestbook.getGuestbookId() == guestbookId) {\n\t\t\t\t\tcssClass = \"active\";\n\t\t\t\t}\n\n\t\t\t\tif (GuestbookModelPermission.contains(\n\t\t\t\t\tpermissionChecker, curGuestbook.getGuestbookId(), \"VIEW\")) {\n\t\t\t\t\t\t\t\t\t\t\n\t%>\n\n\t<portlet:renderURL var=\"viewPageURL\">\n\t\t<portlet:param name=\"mvcPath\" value=\"/guestbookwebportlet/view.jsp\" />\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(curGuestbook.getGuestbookId())%>\" />\n\t</portlet:renderURL>\n\n\t\t\n\t<aui:nav-item cssClass=\"<%=cssClass%>\" href=\"<%=viewPageURL%>\"\n\t\tlabel=\"<%=HtmlUtil.escape(curGuestbook.getName())%>\" />\n\n\t<%  \n\t\t\t\t}\n\t\t\t\n\t\t\t}\n\t%>\n\n</aui:nav>\n\n<aui:button-row cssClass=\"guestbook-buttons\">\n\n<c:if test='<%= GuestbookPermission.contains(permissionChecker, scopeGroupId, \"ADD_ENTRY\") %>'>\n\n\t<portlet:renderURL var=\"addEntryURL\">\n\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(guestbookId)%>\" />\n\t</portlet:renderURL>\n\n\t<aui:button onClick=\"<%=addEntryURL.toString()%>\" value=\"Add Entry\"></aui:button>\n\n</c:if>\n\n</aui:button-row>\n\n<liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntriesCount()%>\">\n<liferay-ui:search-container-results\n\tresults=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(scopeGroupId.longValue(),\n\t\t\t\t\tguestbookId, searchContainer.getStart(),\n\t\t\t\t\tsearchContainer.getEnd())%>\" />\n\n<liferay-ui:search-container-row\n\tclassName=\"com.liferay.docs.guestbook.model.GuestbookEntry\" modelVar=\"entry\">\n\n\t<liferay-ui:search-container-column-text property=\"message\" />\n\n\t<liferay-ui:search-container-column-text property=\"name\" />\n\n\t<liferay-ui:search-container-column-jsp\n\t\talign=\"right\" \n\t\tpath=\"/guestbook/entry_actions.jsp\" />\n</liferay-ui:search-container-row>\n\n<liferay-ui:search-iterator />\n\n</liferay-ui:search-container>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/edit_guestbook.jsp",
    "content": "<%@include file = \"../init.jsp\" %>\n\n<%\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n        \n        Guestbook guestbook = null;\n\n        if (guestbookId > 0) {\n                guestbook = GuestbookLocalServiceUtil.getGuestbook(guestbookId);\n        }\n%>\n\n<portlet:renderURL var=\"viewURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook_admin/view.jsp\" />\n</portlet:renderURL>\n\n<portlet:actionURL name='<%= guestbook == null ? \"addGuestbook\" : \"updateGuestbook\" %>' var=\"editGuestbookURL\" />\n\n<aui:form action=\"<%= editGuestbookURL %>\" name=\"fm\">\n\n        <aui:model-context bean=\"<%= guestbook %>\" model=\"<%= Guestbook.class %>\" />\n\n        <aui:input type=\"hidden\" name=\"guestbookId\"\n            value='<%= guestbook == null ? \"\" : guestbook.getGuestbookId() %>' />\n\n        <aui:fieldset>\n             <aui:input name=\"name\" />\n        </aui:fieldset>\n\n        <aui:button-row>\n             <aui:button type=\"submit\" />\n             <aui:button onClick=\"<%= viewURL %>\" type=\"cancel\"  />\n        </aui:button-row>\n</aui:form>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/guestbook_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\n\tString mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n\tResultRow row = (ResultRow) request\n\t\t\t\t\t.getAttribute(\"SEARCH_CONTAINER_RESULT_ROW\");\n\n\tGuestbook guestbook = (Guestbook) row.getObject();\n%>\n\n<liferay-ui:icon-menu>\n\n\t<portlet:renderURL var=\"editURL\">\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(guestbook.getGuestbookId()) %>\" />\n\t\t<portlet:param name=\"mvcPath\"\n\t\t\tvalue=\"/guestbook_admin/edit_guestbook.jsp\" />\n\t</portlet:renderURL>\n\n\t<liferay-ui:icon image=\"edit\" message=\"Edit\"\n\t\t\turl=\"<%=editURL.toString() %>\" />\n\n\t<portlet:actionURL name=\"deleteGuestbook\" var=\"deleteURL\">\n\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\tvalue=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\" />\n\t</portlet:actionURL>\n\n\t<liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n    <c:if\n    test=\"<%=GuestbookModelPermission.contains(permissionChecker, guestbook.getGuestbookId(), ActionKeys.PERMISSIONS) %>\">\n\n        <liferay-security:permissionsURL\n            modelResource=\"<%= Guestbook.class.getName() %>\"\n            modelResourceDescription=\"<%= guestbook.getName() %>\"\n            resourcePrimKey=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\"\n            var=\"permissionsURL\" />\n    \n        <liferay-ui:icon image=\"permissions\" url=\"<%= permissionsURL %>\" />\n\n    </c:if>\n</liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<liferay-ui:success key=\"guestbookAdded\" message=\"guestbook-added\" />\n<liferay-ui:success key=\"guestbookUpdated\" message=\"guestbook-updated\" />\n<liferay-ui:success key=\"guestbookDeleted\" message=\"guestbook-deleted\" />\n\n<liferay-ui:search-container\n\ttotal=\"<%= GuestbookLocalServiceUtil.getGuestbooksCount(scopeGroupId) %>\">\n\t<liferay-ui:search-container-results\n\t\tresults=\"<%= GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId, \n\t\t\tsearchContainer.getStart(), searchContainer.getEnd()) %>\" />\n\n\t<liferay-ui:search-container-row\n\t\tclassName=\"com.liferay.docs.guestbook.model.Guestbook\" modelVar=\"guestbook\">\n\n\t\t<liferay-ui:search-container-column-text property=\"name\" />\n\t\t\t\t\n\t\t<liferay-ui:search-container-column-jsp\n\t\t\talign=\"right\" \n\t\t\tpath=\"/guestbook_admin/guestbook_actions.jsp\" />\n\t\t\n\t</liferay-ui:search-container-row>\n\n\t<liferay-ui:search-iterator />\n</liferay-ui:search-container>\n\n<aui:button-row cssClass=\"guestbook-admin-buttons\">\n\t<portlet:renderURL var=\"addGuestbookURL\">\n\t\t<portlet:param name=\"mvcPath\"\n\t\t\tvalue=\"/guestbook_admin/edit_guestbook.jsp\" />\n\t\t<portlet:param name=\"redirect\" value=\"<%= \"currentURL\" %>\" />\n\t</portlet:renderURL>\n\t\t\n\t<aui:button onClick=\"<%= addGuestbookURL.toString() %>\"\n\t\tvalue=\"Add Guestbook\" />\n</aui:button-row>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/init.jsp",
    "content": "<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\"%>\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\"%>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n<%@ taglib uri=\"http://liferay.com/tld/security\" prefix=\"liferay-security\" %>\n\n<%@ page import=\"java.util.List\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.ParamUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.HtmlUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.petra.string.StringPool\" %>\n<%@ page import=\"com.liferay.portal.kernel.model.PersistedModel\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.SearchEntry\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.ResultRow\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.Guestbook\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.GuestbookEntry\" %> \n<%@ page import=\"com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookModelPermission\" %>\n<%@ page import=\"com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookPermission\" %>\n<%@ page import=\"com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookEntryPermission\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.portal.kernel.security.permission.ActionKeys\" %>\n\n<liferay-theme:defineObjects />\n\n<portlet:defineObjects />"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/content/Language.properties",
    "content": "entry-added=Entry added successfully.\nentry-deleted=Entry deleted successfully.\nguestbook-added=Guestbook added successfully.\nguestbook-updated=Guestbook updated successfully.\nguestbook-deleted=Guestbook deleted successfully."
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/portlet.properties",
    "content": "resource.actions.configs=META-INF/resource-actions/default.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/07-permissions/com-liferay-docs-guestbook/settings.gradle",
    "content": "buildscript {\n\tdependencies {\n\t\tclasspath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.workspace\", version: \"2.0.4\"\n\t\tclasspath group: \"net.saliman\", name: \"gradle-properties-plugin\", version: \"1.4.6\"\n\t}\n\n\trepositories {\n\t\tmaven {\n\t\t\turl \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t\t}\n\t}\n}\n\napply plugin: \"net.saliman.properties\"\n\napply plugin: \"com.liferay.workspace\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/.blade.properties",
    "content": "#Wed Aug 14 16:06:16 EDT 2019\nliferay.version.default=7.2\nprofile.name=gradle\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/.gitignore",
    "content": "**/*.iml\n**/.ivy\n**/.classpath\n**/.project\n**/.sass-cache\n**/.settings\n**/bin\n**/build\n**/build_gradle\n**/node_modules\n**/test-coverage\n**/tmp\n**/.web_bundle_build\n.gradle\n.idea\n/bundles\n/gradle-*.properties\n/plugins-sdk/**/classes\n/plugins-sdk/**/ivy.xml.MD5\n/plugins-sdk/**/liferay-hook.xml.processed\n/plugins-sdk/build.*.properties\n/plugins-sdk/dependencies/**/*.jar\n/plugins-sdk/dist\n/plugins-sdk/lib\ntest-results"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/configs/common/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/configs/dev/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/configs/docker/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/configs/local/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/configs/prod/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "operationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/configs/prod/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/configs/uat/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "logExceptionsOnly=B\"false\"\noperationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/configs/uat/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.2-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/gradle.properties",
    "content": "\n    # Set the directory where the downloaded bundle Zip files are stored. The\n    # default value is the \".liferay/bundles\" folder inside the user home\n    # directory.\n    #\n    #liferay.workspace.bundle.cache.dir=~/.liferay/bundles\n\n    #\n    # Set this to true if the \"liferay.workspace.bundle.url\" property is set to\n    # a DXP bundle Zip. This property allows the token residing in the\n    # \"~/.liferay\" folder to be used to validate your user credentials when\n    # downloading the bundle. The default value is false.\n    #\n    #liferay.workspace.bundle.token.download=false\n\n    #\n    # Set the email address to use when downloading a DXP bundle. This is used\n    # to create the authentication token. The email address must match the one\n    # registered for your DXP subscription.\n    #\n    # If you wish to create a new token without providing your email address and\n    # password in this file, you can create a token manually by navigating to\n    # your Liferay profile's Account Setting page and generating a token in the\n    # Authentication Tokens menu. Your token must reside in the \"~/.liferay\"\n    # folder.\n    #\n    #liferay.workspace.bundle.token.email.address=\n\n    #\n    # Set this to true to override the existing token with a newly generated\n    # token created by the \"createToken\" task. The default value is false.\n    #\n    #liferay.workspace.bundle.token.force=false\n\n    #\n    # Set the password to use when downloading a DXP bundle. This is used to\n    # create the authentication token. The password must match the one\n    # registered for your DXP subscription. See the\n    # \"liferay.workspace.bundle.token.email.address\" property for more details.\n    #\n    #liferay.workspace.bundle.token.password=\n\n    #\n    # Set the file to hold the Liferay bundle authentication token password.\n    # The default file value is \"~/.liferay/token\".\n    #\n    #liferay.workspace.bundle.token.password.file=\n\n    #\n    # Set the URL pointing to the bundle Zip to download. If the URL points to a\n    # DXP bundle (e.g., https://api.liferay.com/...), set the\n    # \"liferay.workspace.bundle.token.download\" property to true. The default\n    # value is the URL for Liferay Portal CE 7.0 GA7, Liferay Portal CE 7.1 GA4,\n    # or Liferay Portal CE 7.2 GA1, depending on the portal version the\n    # workspace is targeting.\n    #\nliferay.workspace.bundle.url = https://releases-cdn.liferay.com/portal/7.2.0-ga1/liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761.tar.gz\n\n    #\n    # Set the \"app.server.tomcat.version\" to match what is contained inside the\n    # Liferay bundle. Both the TestIntegrationPlugin and and LiferayExtPlugin\n    # rely on this version to match the bundled Tomcat version. If your\n    # configured bundle url points to a bundle with a different Tomcat version,\n    # set the property below to match that Tomcat version.\n    #\napp.server.tomcat.version = 9.0.17\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository in the\n    # root project. The default value is true.\n    #\n    #liferay.workspace.default.repository.enabled=true\n\n    #\n    # Set the Liferay Portal Docker image to create your container from. The\n    # default value points to Liferay Portal CE 7.2 GA1.\n    #\n    #liferay.workspace.docker.image.liferay=liferay/portal:7.2.0-ga1\n\n    #\n    # Set the environment with the settings appropriate for current development.\n    # The \"configs\" folder is used to hold different environments in the same\n    # workspace. You can organize environment settings and generate an\n    # environment installation with those settings. There are five\n    # environments: common, dev, docker, local, prod, and uat. The default value\n    # is \"local\".\n    #\n    #liferay.workspace.environment=local\n\n    #\n    # Set the folder that contains the Liferay bundle downloaded from the\n    # \"liferay.workspace.bundle.url\" property. The default value is \"bundles\".\n    #\n    #liferay.workspace.home.dir=bundles\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository for\n    # module/OSGi projects. The default value is true.\n    #\n    #liferay.workspace.modules.default.repository.enabled=true\n\n    #\n    # Set the folder that contains all Ext OSGi modules and Ext plugins. The\n    # default value is \"ext\".\n    #\n    #liferay.workspace.ext.dir=ext\n\n    #\n    # Set the folder that contains all module/OSGi projects. The default value\n    # is \"modules\".\n    #\n    #liferay.workspace.modules.dir=modules\n\n    #\n    # Set this to true to compile the JSP files in OSGi modules and have them\n    # added to the distributable Zip/Tar. The default value is false.\n    #\n    #liferay.workspace.modules.jsp.precompile.enabled=false\n\n    #\n    # Set the folder that contains the Plugins SDK environment. The default\n    # value is \"plugins-sdk\".\n    #\n    #liferay.workspace.plugins.sdk.dir=plugins-sdk\n\n    #\n    # Set the Liferay Portal or DXP version to develop and test against. By\n    # setting this property, it enables the target platform features such as\n    # dependency management and OSGi resolve tasks. Use the version that\n    # matches the Liferay Portal or DXP bundle version in this workspace.\n    #\n    # For a list of all available target platform versions, see\n    # https://bit.ly/2IkAwwW for Liferay Portal and https://bit.ly/2GIyfZF for\n    # Liferay DXP.\n    #\n    #liferay.workspace.target.platform.version=7.2.0\n\n    #\n    # Set this to true if you have enabled the Target Platform plugin (i.e. you\n    # have set the above property) and you want to apply the TargetPlatformIDE\n    # plugin to the root workspace project. This will cause all of the BOM\n    # artifacts jars and their Java sources to be indexed by your IDE. Setting\n    # this property to true can slow down your IDE's project synchronization.\n    #\n    # target.platform.index.sources=false\n\n    #\n    # Set the folder that contains Node.js-style theme projects. The default\n    # value is \"themes\".\n    #\n    #liferay.workspace.themes.dir=themes\n\n    #\n    # Set this to true to build the theme projects using the Liferay Portal\n    # Tools Theme Builder. The default value is false.\n    #\n    #liferay.workspace.themes.java.build=false\n\n    #\n    # Set the folder that contains classic WAR-style projects. The default value\n    # is \"wars\".\n    #\n    #liferay.workspace.wars.dir=wars\n\n\n    #\n    # Set the subscription key for Microsoft Translation integration.\n    # Subscription to the Translator Text Translation API on Microsoft Cognitive\n    # Services is required. Basic subscriptions, up to 2 million characters a\n    # month, are free. See\n    # http://docs.microsofttranslator.com/text-translate.html for more\n    # information.\n    #\nmicrosoft.translator.subscription.key = \nliferay.workspace.target.platform.version = 7.2.0\ntarget.platform.index.sources = false\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/.gitignore",
    "content": ".gradle/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/bnd.bnd",
    "content": "Bundle-Name: guestbook-api\nBundle-SymbolicName: com.liferay.docs.guestbook.api\nBundle-Version: 1.0.0\nExport-Package:\\\n\tcom.liferay.docs.guestbook.exception,\\\n\tcom.liferay.docs.guestbook.model,\\\n\tcom.liferay.docs.guestbook.service,\\\n\tcom.liferay.docs.guestbook.service.persistence\n-check: EXPORTS\n-includeresource: META-INF/service.xml=../guestbook-service/service.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/constants/GuestbookConstants.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.constants;\n\npublic class GuestbookConstants {\n\t\n\tpublic static final String RESOURCE_NAME = \"com.liferay.docs.guestbook\";\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/constants/GuestbookPortletKeys.java",
    "content": "package com.liferay.docs.guestbook.constants;\n\n/**\n * @author sezovr\n */\npublic class GuestbookPortletKeys {\n\n\tpublic static final String GUESTBOOK =\n\t\t\"com_liferay_docs_guestbook_portlet_GuestbookPortlet\";\n\n\tpublic static final String GUESTBOOK_ADMIN =\n\t\t  \"com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet\";\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryEmailException extends PortalException {\n\n\tpublic EntryEmailException() {\n\t}\n\n\tpublic EntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryMessageException extends PortalException {\n\n\tpublic EntryMessageException() {\n\t}\n\n\tpublic EntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryNameException extends PortalException {\n\n\tpublic EntryNameException() {\n\t}\n\n\tpublic EntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryEmailException extends PortalException {\n\n\tpublic GuestbookEntryEmailException() {\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryMessageException extends PortalException {\n\n\tpublic GuestbookEntryMessageException() {\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryNameException extends PortalException {\n\n\tpublic GuestbookEntryNameException() {\n\t}\n\n\tpublic GuestbookEntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookNameException extends PortalException {\n\n\tpublic GuestbookNameException() {\n\t}\n\n\tpublic GuestbookNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookEntryException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookEntryException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookEntryException() {\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookException() {\n\t}\n\n\tpublic NoSuchGuestbookException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/Guestbook.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookModel\n * @generated\n */\n@ImplementationClassName(\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\")\n@ProviderType\npublic interface Guestbook extends GuestbookModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<Guestbook, Long> GUESTBOOK_ID_ACCESSOR =\n\t\tnew Accessor<Guestbook, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(Guestbook guestbook) {\n\t\t\t\treturn guestbook.getGuestbookId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Guestbook> getTypeClass() {\n\t\t\t\treturn Guestbook.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntry.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookEntryModel\n * @generated\n */\n@ImplementationClassName(\n\t\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\"\n)\n@ProviderType\npublic interface GuestbookEntry extends GuestbookEntryModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<GuestbookEntry, Long> ENTRY_ID_ACCESSOR =\n\t\tnew Accessor<GuestbookEntry, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(GuestbookEntry guestbookEntry) {\n\t\t\t\treturn guestbookEntry.getEntryId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<GuestbookEntry> getTypeClass() {\n\t\t\t\treturn GuestbookEntry.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryModel\n\textends BaseModel<GuestbookEntry>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook entry model instance should use the {@link GuestbookEntry} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\tpublic long getEntryId();\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\tpublic void setEntryId(long entryId);\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getEmail();\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\tpublic void setEmail(String email);\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getMessage();\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\tpublic void setMessage(String message);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntrySoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookEntryServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntrySoap implements Serializable {\n\n\tpublic static GuestbookEntrySoap toSoapModel(GuestbookEntry model) {\n\t\tGuestbookEntrySoap soapModel = new GuestbookEntrySoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setEntryId(model.getEntryId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setEmail(model.getEmail());\n\t\tsoapModel.setMessage(model.getMessage());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(GuestbookEntry[] models) {\n\t\tGuestbookEntrySoap[] soapModels = new GuestbookEntrySoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[][] toSoapModels(\n\t\tGuestbookEntry[][] models) {\n\n\t\tGuestbookEntrySoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels =\n\t\t\t\tnew GuestbookEntrySoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookEntrySoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(\n\t\tList<GuestbookEntry> models) {\n\n\t\tList<GuestbookEntrySoap> soapModels = new ArrayList<GuestbookEntrySoap>(\n\t\t\tmodels.size());\n\n\t\tfor (GuestbookEntry model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookEntrySoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookEntrySoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetEntryId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic String getEmail() {\n\t\treturn _email;\n\t}\n\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn _message;\n\t}\n\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link GuestbookEntry}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryWrapper\n\textends BaseModelWrapper<GuestbookEntry>\n\timplements GuestbookEntry, ModelWrapper<GuestbookEntry> {\n\n\tpublic GuestbookEntryWrapper(GuestbookEntry guestbookEntry) {\n\t\tsuper(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"entryId\", getEntryId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"email\", getEmail());\n\t\tattributes.put(\"message\", getMessage());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong entryId = (Long)attributes.get(\"entryId\");\n\n\t\tif (entryId != null) {\n\t\t\tsetEntryId(entryId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tString email = (String)attributes.get(\"email\");\n\n\t\tif (email != null) {\n\t\t\tsetEmail(email);\n\t\t}\n\n\t\tString message = (String)attributes.get(\"message\");\n\n\t\tif (message != null) {\n\t\t\tsetMessage(message);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@Override\n\tpublic String getEmail() {\n\t\treturn model.getEmail();\n\t}\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn model.getEntryId();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@Override\n\tpublic String getMessage() {\n\t\treturn model.getMessage();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEmail(String email) {\n\t\tmodel.setEmail(email);\n\t}\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\tmodel.setEntryId(entryId);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\t@Override\n\tpublic void setMessage(String message) {\n\t\tmodel.setMessage(message);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookEntryWrapper wrap(GuestbookEntry guestbookEntry) {\n\t\treturn new GuestbookEntryWrapper(guestbookEntry);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic interface GuestbookModel\n\textends BaseModel<Guestbook>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook model instance should use the {@link Guestbook} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookSoap implements Serializable {\n\n\tpublic static GuestbookSoap toSoapModel(Guestbook model) {\n\t\tGuestbookSoap soapModel = new GuestbookSoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(Guestbook[] models) {\n\t\tGuestbookSoap[] soapModels = new GuestbookSoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[][] toSoapModels(Guestbook[][] models) {\n\t\tGuestbookSoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels = new GuestbookSoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookSoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(List<Guestbook> models) {\n\t\tList<GuestbookSoap> soapModels = new ArrayList<GuestbookSoap>(\n\t\t\tmodels.size());\n\n\t\tfor (Guestbook model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookSoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookSoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetGuestbookId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link Guestbook}.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic class GuestbookWrapper\n\textends BaseModelWrapper<Guestbook>\n\timplements Guestbook, ModelWrapper<Guestbook> {\n\n\tpublic GuestbookWrapper(Guestbook guestbook) {\n\t\tsuper(guestbook);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookWrapper wrap(Guestbook guestbook) {\n\t\treturn new GuestbookWrapper(guestbook);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for GuestbookEntry. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryLocalServiceUtil} to access the guestbook entry local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId);\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException;\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId);\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows SystemException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> obc);\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\taddGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().addGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\taddGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\t\tString message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tcreateGuestbookEntry(long entryId) {\n\n\t\treturn getService().createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(\n\t\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntry(long entryId) {\n\n\t\treturn getService().fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tint start, int end) {\n\n\t\treturn getService().getGuestbookEntries(start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId) {\n\n\t\treturn getService().getGuestbookEntries(groupId, guestbookId);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tString uuid, long companyId, int start, int end,\n\t\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int getGuestbookEntriesCount() {\n\t\treturn getService().getGuestbookEntriesCount();\n\t}\n\n\tpublic static int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn getService().getGuestbookEntriesCount(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tupdateGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().updateGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tupdateGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\t\tString email, String message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\tpublic static GuestbookEntryLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryLocalService.class);\n\n\t\tServiceTracker<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryLocalService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryLocalService}.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceWrapper\n\timplements GuestbookEntryLocalService,\n\t\t\t   ServiceWrapper<GuestbookEntryLocalService> {\n\n\tpublic GuestbookEntryLocalServiceWrapper(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry createGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookEntryLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry fetchGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookEntryLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(int start, int end) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(long groupId, long guestbookId) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry getGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntryLocalService getWrappedService() {\n\t\treturn _guestbookEntryLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for GuestbookEntry. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryServiceUtil} to access the guestbook entry remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookEntryService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookEntryService.class);\n\n\t\tServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryService, GuestbookEntryService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookEntryService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryService}.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceWrapper\n\timplements GuestbookEntryService, ServiceWrapper<GuestbookEntryService> {\n\n\tpublic GuestbookEntryServiceWrapper(\n\t\tGuestbookEntryService guestbookEntryService) {\n\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookEntryService getWrappedService() {\n\t\treturn _guestbookEntryService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookEntryService guestbookEntryService) {\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\tprivate GuestbookEntryService _guestbookEntryService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for Guestbook. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookLocalServiceUtil} to access the guestbook local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(Guestbook guestbook);\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(\n\t\t\tlong userId, String name, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId);\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook);\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException;\n\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(\n\t\t\tlong guestbookId, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbook(long guestbookId);\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException;\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(\n\t\tlong groupId, int start, int end, OrderByComparator<Guestbook> obc);\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(Guestbook guestbook);\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().addGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbook(userId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbook(guestbookId);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().deleteGuestbook(guestbookId, serviceContext);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn getService().getGuestbooks(start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn getService().getGuestbooks(groupId);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int getGuestbooksCount() {\n\t\treturn getService().getGuestbooksCount();\n\t}\n\n\tpublic static int getGuestbooksCount(long groupId) {\n\t\treturn getService().getGuestbooksCount(groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().updateGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbook(\n\t\t\tuserId, guestbookId, name, serviceContext);\n\t}\n\n\tpublic static GuestbookLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookLocalService.class);\n\n\t\tServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookLocalService, GuestbookLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookLocalService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookLocalService}.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceWrapper\n\timplements GuestbookLocalService, ServiceWrapper<GuestbookLocalService> {\n\n\tpublic GuestbookLocalServiceWrapper(\n\t\tGuestbookLocalService guestbookLocalService) {\n\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.addGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.addGuestbook(\n\t\t\tuserId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbookId);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(\n\t\t\tguestbookId, serviceContext);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn _guestbookLocalService.getGuestbooksCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbooksCount(long groupId) {\n\t\treturn _guestbookLocalService.getGuestbooksCount(groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.updateGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.updateGuestbook(\n\t\t\tuserId, guestbookId, name, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookLocalService getWrappedService() {\n\t\treturn _guestbookLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookLocalService guestbookLocalService) {\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for Guestbook. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookServiceUtil} to access the guestbook remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookService, GuestbookService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookService.class);\n\n\t\tServiceTracker<GuestbookService, GuestbookService> serviceTracker =\n\t\t\tnew ServiceTracker<GuestbookService, GuestbookService>(\n\t\t\t\tbundle.getBundleContext(), GuestbookService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookService}.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceWrapper\n\timplements GuestbookService, ServiceWrapper<GuestbookService> {\n\n\tpublic GuestbookServiceWrapper(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookService getWrappedService() {\n\t\treturn _guestbookService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\tprivate GuestbookService _guestbookService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryPersistence\n\textends BasePersistence<GuestbookEntry> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryUtil} to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId);\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] filterFindByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic void removeByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\tpublic int filterCountByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic void cacheResult(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic void cacheResult(java.util.List<GuestbookEntry> guestbookEntries);\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic GuestbookEntry create(long entryId);\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId);\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll();\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook entry service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookEntryPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().clearCache(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, GuestbookEntry> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static GuestbookEntry update(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().update(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static GuestbookEntry update(\n\t\tGuestbookEntry guestbookEntry, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbookEntry, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tentryId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic static GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tentryId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator,\n\t\t\tretrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_PrevAndNext(\n\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn getPersistence().filterFindByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn getPersistence().filterFindByG_G(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().filterFindByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] filterFindByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().filterFindByG_G_PrevAndNext(\n\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic static void removeByG_G(long groupId, long guestbookId) {\n\t\tgetPersistence().removeByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByG_G(long groupId, long guestbookId) {\n\t\treturn getPersistence().countByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static int filterCountByG_G(long groupId, long guestbookId) {\n\t\treturn getPersistence().filterCountByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic static void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().cacheResult(guestbookEntry);\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic static void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tgetPersistence().cacheResult(guestbookEntries);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static GuestbookEntry create(long entryId) {\n\t\treturn getPersistence().create(entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry remove(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().remove(entryId);\n\t}\n\n\tpublic static GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().updateImpl(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn getPersistence().fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookEntryPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence> _serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryPersistence.class);\n\n\t\tServiceTracker<GuestbookEntryPersistence, GuestbookEntryPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryPersistence.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookPersistence extends BasePersistence<Guestbook> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookUtil} to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(long groupId);\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByGroupId(long groupId);\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] filterFindByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic void removeByGroupId(long groupId);\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByGroupId(long groupId);\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\tpublic int filterCountByGroupId(long groupId);\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic void cacheResult(Guestbook guestbook);\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic void cacheResult(java.util.List<Guestbook> guestbooks);\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic Guestbook create(long guestbookId);\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException;\n\n\tpublic Guestbook updateImpl(Guestbook guestbook);\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId);\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll();\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(Guestbook guestbook) {\n\t\tgetPersistence().clearCache(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, Guestbook> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static Guestbook update(Guestbook guestbook) {\n\t\treturn getPersistence().update(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static Guestbook update(\n\t\tGuestbook guestbook, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbook, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tguestbookId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic static Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tguestbookId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(long groupId) {\n\t\treturn getPersistence().findByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn getPersistence().findByGroupId(groupId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_First(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_First(\n\t\t\tgroupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_PrevAndNext(\n\t\t\tguestbookId, groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByGroupId(long groupId) {\n\t\treturn getPersistence().filterFindByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn getPersistence().filterFindByGroupId(groupId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().filterFindByGroupId(\n\t\t\tgroupId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] filterFindByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().filterFindByGroupId_PrevAndNext(\n\t\t\tguestbookId, groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic static void removeByGroupId(long groupId) {\n\t\tgetPersistence().removeByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByGroupId(long groupId) {\n\t\treturn getPersistence().countByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\tpublic static int filterCountByGroupId(long groupId) {\n\t\treturn getPersistence().filterCountByGroupId(groupId);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic static void cacheResult(Guestbook guestbook) {\n\t\tgetPersistence().cacheResult(guestbook);\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic static void cacheResult(List<Guestbook> guestbooks) {\n\t\tgetPersistence().cacheResult(guestbooks);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static Guestbook create(long guestbookId) {\n\t\treturn getPersistence().create(guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook remove(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().remove(guestbookId);\n\t}\n\n\tpublic static Guestbook updateImpl(Guestbook guestbook) {\n\t\treturn getPersistence().updateImpl(guestbook);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn getPersistence().fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic static List<Guestbook> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookPersistence.class);\n\n\t\tServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker<GuestbookPersistence, GuestbookPersistence>(\n\t\t\t\t\tbundle.getBundleContext(), GuestbookPersistence.class,\n\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/bnd.bnd",
    "content": "Bundle-Name: guestbook-service\nBundle-SymbolicName: com.liferay.docs.guestbook.service\nBundle-Version: 1.0.0\nLiferay-Require-SchemaVersion: 1.0.0\nLiferay-Service: true\nExport-Package: com.liferay.docs.guestbook.search\n-dsannotations-options: inherit"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.lang\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.portal.aop.api\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.function\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.model.adapter\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.reflect\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.spi\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.api\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\tcompileOnly project(\":modules:guestbook:guestbook-api\")\n}\n\nbuildService {\n\tapiDir = \"../guestbook-api/src/main/java\"\n}\n\ngroup = \"com.liferay.docs.guestbook\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/service.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"ds\" package-path=\"com.liferay.docs.guestbook\">\n\t<author>Liferay</author>\n\t<namespace>GB</namespace>\n\t\n\t<entity name=\"Guestbook\" local-service=\"true\" uuid=\"true\" remote-service=\"true\">\n\t\t\n\t\t<!-- Guestbook fields -->\n\n\t\t<column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\t\t\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<finder name=\"GroupId\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"groupId\" />\n\t\t</finder>\n\n\t</entity>\n\t\n    <entity name=\"GuestbookEntry\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n\n\t\t<!-- Guestbook Entry fields -->\n\n\t\t<column name=\"entryId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t<column name=\"email\" type=\"String\" />\n\t\t<column name=\"message\" type=\"String\" />\n\t\t<column name=\"guestbookId\" type=\"long\" />\n\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\t   \n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<order>\n\t\t\t<order-column name=\"createDate\" order-by=\"desc\" />\n\t\t</order>\n\n        <finder name=\"G_G\" return-type=\"Collection\">\n            <finder-column name=\"groupId\" />\n            <finder-column name=\"guestbookId\" />\n        </finder>\n    </entity>\n\n    <exceptions>\n        <exception>GuestbookEntryEmail</exception>\n        <exception>GuestbookEntryMessage</exception>\n        <exception>GuestbookEntryName</exception>\n        <exception>GuestbookName</exception>\n    </exceptions>\n</service-builder>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/internal/security/permission/resource/GuestbookEntryModelResourcePermissionRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.StagedModelPermissionLogic;\nimport com.liferay.portal.kernel.security.permission.resource.WorkflowedModelPermissionLogic;\nimport com.liferay.portal.kernel.service.GroupLocalService;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\nimport com.liferay.portal.kernel.workflow.permission.WorkflowPermission;\n\n@Component(immediate = true)\npublic class GuestbookEntryModelResourcePermissionRegistrar {\n\n @Activate\n\tpublic void activate(BundleContext bundleContext) {\n\t\tDictionary<String, Object> properties = new HashMapDictionary<>();\n\n\t\tproperties.put(\"model.class.name\", GuestbookEntry.class.getName());\n\n\t\t_serviceRegistration = bundleContext.registerService(\n\t\t\tModelResourcePermission.class,\n\t\t\tModelResourcePermissionFactory.create(\n\t\t\t\tGuestbookEntry.class, GuestbookEntry::getEntryId,\n\t\t\t\t_guestbookEntryLocalService::getGuestbookEntry, _portletResourcePermission,\n\t\t\t\t(modelResourcePermission, consumer) -> {\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew StagedModelPermissionLogic<>(\n\t\t\t\t\t\t\t_stagingPermission, GuestbookPortletKeys.GUESTBOOK,\n\t\t\t\t\t\t\tGuestbookEntry::getEntryId));\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew WorkflowedModelPermissionLogic<>(\n\t\t\t\t\t\t\t\t_workflowPermission, modelResourcePermission,\n\t\t\t\t\t\t\t\t_groupLocalService, GuestbookEntry::getEntryId));\n\t\t\t\t}),\n\t\t\tproperties);\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n\t@Reference(target = \"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\")\n\tprivate PortletResourcePermission _portletResourcePermission;\n\n\tprivate ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n\t@Reference\n\tprivate StagingPermission _stagingPermission;\n\n\t@Reference\n\tprivate WorkflowPermission _workflowPermission;\n\t\n\t@Reference\n\tprivate GroupLocalService _groupLocalService;\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/internal/security/permission/resource/GuestbookModelResourcePermissionRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.StagedModelPermissionLogic;\nimport com.liferay.portal.kernel.security.permission.resource.WorkflowedModelPermissionLogic;\nimport com.liferay.portal.kernel.service.GroupLocalService;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\nimport com.liferay.portal.kernel.workflow.permission.WorkflowPermission;\n\n@Component (immediate=true)\npublic class GuestbookModelResourcePermissionRegistrar {\n\n @Activate\n\tpublic void activate(BundleContext bundleContext) {\n\t\tDictionary<String, Object> properties = new HashMapDictionary<>();\n\n\t\tproperties.put(\"model.class.name\", Guestbook.class.getName());\n\n\t\t_serviceRegistration = bundleContext.registerService(\n\t\t\tModelResourcePermission.class,\n\t\t\tModelResourcePermissionFactory.create(\n\t\t\t\tGuestbook.class, Guestbook::getGuestbookId,\n\t\t\t\t_guestbookLocalService::getGuestbook, _portletResourcePermission,\n\t\t\t\t(modelResourcePermission, consumer) -> {\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew StagedModelPermissionLogic<>(\n\t\t\t\t\t\t\t_stagingPermission, GuestbookPortletKeys.GUESTBOOK,\n\t\t\t\t\t\t\tGuestbook::getGuestbookId));\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew WorkflowedModelPermissionLogic<>(\n\t\t\t\t\t\t\t\t_workflowPermission, modelResourcePermission,\n\t\t\t\t\t\t\t\t_groupLocalService, Guestbook::getGuestbookId));\n\t\t\t\t}),\n\t\t\tproperties);\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n\t@Reference(target = \"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\")\n\tprivate PortletResourcePermission _portletResourcePermission;\n\n\tprivate ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n\t@Reference\n\tprivate StagingPermission _stagingPermission;\n\n\t@Reference\n\tprivate WorkflowPermission _workflowPermission;\n\t\n\t@Reference\n\tprivate GroupLocalService _groupLocalService;\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/internal/security/permission/resource/GuestbookPortletResourcePermissionRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.StagedPortletPermissionLogic;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\n\n@Component (immediate = true)\npublic class GuestbookPortletResourcePermissionRegistrar {\n\t\n\t\t@Activate\n\tpublic void activate(BundleContext bundleContext) {\n\t\tDictionary<String, Object> properties = new HashMapDictionary<>();\n\n\t\tproperties.put(\"resource.name\", GuestbookConstants.RESOURCE_NAME);\n\n\t\t_serviceRegistration = bundleContext.registerService(\n\t\t\tPortletResourcePermission.class,\n\t\t\tPortletResourcePermissionFactory.create(\n\t\t\t\tGuestbookConstants.RESOURCE_NAME,\n\t\t\t\tnew StagedPortletPermissionLogic(\n\t\t\t\t\t_stagingPermission, GuestbookPortletKeys.GUESTBOOK)),\n\t\t\tproperties);\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\tprivate ServiceRegistration<PortletResourcePermission> _serviceRegistration;\n\n\t@Reference\n\tprivate StagingPermission _stagingPermission;\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookBaseImpl\n\textends GuestbookModelImpl implements Guestbook {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookLocalServiceUtil.addGuestbook(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookLocalServiceUtil.updateGuestbook(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing Guestbook in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookCacheModel\n\timplements CacheModel<Guestbook>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookCacheModel guestbookCacheModel = (GuestbookCacheModel)obj;\n\n\t\tif (guestbookId == guestbookCacheModel.guestbookId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, guestbookId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(27);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic Guestbook toEntityModel() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookImpl.setGuestbookId(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tguestbookImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setName(name);\n\t\t}\n\n\t\tguestbookImpl.setGroupId(groupId);\n\t\tguestbookImpl.setCompanyId(companyId);\n\t\tguestbookImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookImpl.setStatus(status);\n\t\tguestbookImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long guestbookId;\n\tpublic String name;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryBaseImpl\n\textends GuestbookEntryModelImpl implements GuestbookEntry {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookEntryLocalServiceUtil.addGuestbookEntry(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookEntryLocalServiceUtil.updateGuestbookEntry(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing GuestbookEntry in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryCacheModel\n\timplements CacheModel<GuestbookEntry>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntryCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\t(GuestbookEntryCacheModel)obj;\n\n\t\tif (entryId == guestbookEntryCacheModel.entryId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, entryId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(33);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", entryId=\");\n\t\tsb.append(entryId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", email=\");\n\t\tsb.append(email);\n\t\tsb.append(\", message=\");\n\t\tsb.append(message);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEntityModel() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookEntryImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookEntryImpl.setEntryId(entryId);\n\n\t\tif (name == null) {\n\t\t\tguestbookEntryImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setName(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tguestbookEntryImpl.setEmail(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setEmail(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tguestbookEntryImpl.setMessage(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setMessage(message);\n\t\t}\n\n\t\tguestbookEntryImpl.setGuestbookId(guestbookId);\n\t\tguestbookEntryImpl.setGroupId(groupId);\n\t\tguestbookEntryImpl.setCompanyId(companyId);\n\t\tguestbookEntryImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookEntryImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookEntryImpl.setStatus(status);\n\t\tguestbookEntryImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookEntryImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tentryId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\t\temail = objectInput.readUTF();\n\t\tmessage = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(entryId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(message);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long entryId;\n\tpublic String name;\n\tpublic String email;\n\tpublic String message;\n\tpublic long guestbookId;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.GuestbookEntry<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryImpl extends GuestbookEntryBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook entry model instance should use the {@link com.liferay.docs.guestbook.model.GuestbookEntry} interface instead.\n\t */\n\tpublic GuestbookEntryImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.GuestbookEntryModel;\nimport com.liferay.docs.guestbook.model.GuestbookEntrySoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.DateUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookEntryModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookEntryModelImpl\n\textends BaseModelImpl<GuestbookEntry> implements GuestbookEntryModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_GuestbookEntry\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"entryId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"email\", Types.VARCHAR},\n\t\t{\"message\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"groupId\", Types.BIGINT}, {\"companyId\", Types.BIGINT},\n\t\t{\"userId\", Types.BIGINT}, {\"userName\", Types.VARCHAR},\n\t\t{\"createDate\", Types.TIMESTAMP}, {\"modifiedDate\", Types.TIMESTAMP},\n\t\t{\"status\", Types.INTEGER}, {\"statusByUserId\", Types.BIGINT},\n\t\t{\"statusByUserName\", Types.VARCHAR}, {\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"entryId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"email\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"message\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_GuestbookEntry (uuid_ VARCHAR(75) null,entryId LONG not null primary key,name VARCHAR(75) null,email VARCHAR(75) null,message VARCHAR(75) null,guestbookId LONG,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_GuestbookEntry\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbookEntry.createDate DESC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_GuestbookEntry.createDate DESC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 4L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 8L;\n\n\tpublic static final long CREATEDATE_COLUMN_BITMASK = 16L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static GuestbookEntry toModel(GuestbookEntrySoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbookEntry model = new GuestbookEntryImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setEntryId(soapModel.getEntryId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setEmail(soapModel.getEmail());\n\t\tmodel.setMessage(soapModel.getMessage());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<GuestbookEntry> toModels(\n\t\tGuestbookEntrySoap[] soapModels) {\n\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> models = new ArrayList<GuestbookEntry>(\n\t\t\tsoapModels.length);\n\n\t\tfor (GuestbookEntrySoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookEntryModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetEntryId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName,\n\t\t\t\tattributeGetterFunction.apply((GuestbookEntry)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<GuestbookEntry, Object>>\n\t\t\tattributeSetterBiConsumers = getAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<GuestbookEntry, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(GuestbookEntry)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<GuestbookEntry, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, GuestbookEntry>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbookEntry.class.getClassLoader(), GuestbookEntry.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<GuestbookEntry> constructor =\n\t\t\t\t(Constructor<GuestbookEntry>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<GuestbookEntry, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<GuestbookEntry, Object>>();\n\t\tMap<String, BiConsumer<GuestbookEntry, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<GuestbookEntry, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", GuestbookEntry::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUuid);\n\t\tattributeGetterFunctions.put(\"entryId\", GuestbookEntry::getEntryId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"entryId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setEntryId);\n\t\tattributeGetterFunctions.put(\"name\", GuestbookEntry::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setName);\n\t\tattributeGetterFunctions.put(\"email\", GuestbookEntry::getEmail);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"email\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setEmail);\n\t\tattributeGetterFunctions.put(\"message\", GuestbookEntry::getMessage);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"message\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setMessage);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"guestbookId\", GuestbookEntry::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"groupId\", GuestbookEntry::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", GuestbookEntry::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", GuestbookEntry::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", GuestbookEntry::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"createDate\", GuestbookEntry::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", GuestbookEntry::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", GuestbookEntry::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\",\n\t\t\t(BiConsumer<GuestbookEntry, Integer>)GuestbookEntry::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", GuestbookEntry::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)\n\t\t\t\tGuestbookEntry::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", GuestbookEntry::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)\n\t\t\t\tGuestbookEntry::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusDate\", GuestbookEntry::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getEmail() {\n\t\tif (_email == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _email;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getMessage() {\n\t\tif (_message == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _message;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_columnBitmask |= GUESTBOOKID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGuestbookId) {\n\t\t\t_setOriginalGuestbookId = true;\n\n\t\t\t_originalGuestbookId = _guestbookId;\n\t\t}\n\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getOriginalGuestbookId() {\n\t\treturn _originalGuestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_columnBitmask = -1L;\n\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), GuestbookEntry.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tguestbookEntryImpl.setUuid(getUuid());\n\t\tguestbookEntryImpl.setEntryId(getEntryId());\n\t\tguestbookEntryImpl.setName(getName());\n\t\tguestbookEntryImpl.setEmail(getEmail());\n\t\tguestbookEntryImpl.setMessage(getMessage());\n\t\tguestbookEntryImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookEntryImpl.setGroupId(getGroupId());\n\t\tguestbookEntryImpl.setCompanyId(getCompanyId());\n\t\tguestbookEntryImpl.setUserId(getUserId());\n\t\tguestbookEntryImpl.setUserName(getUserName());\n\t\tguestbookEntryImpl.setCreateDate(getCreateDate());\n\t\tguestbookEntryImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookEntryImpl.setStatus(getStatus());\n\t\tguestbookEntryImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookEntryImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookEntryImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(GuestbookEntry guestbookEntry) {\n\t\tint value = 0;\n\n\t\tvalue = DateUtil.compareTo(\n\t\t\tgetCreateDate(), guestbookEntry.getCreateDate());\n\n\t\tvalue = value * -1;\n\n\t\tif (value != 0) {\n\t\t\treturn value;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntry)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)obj;\n\n\t\tlong primaryKey = guestbookEntry.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl = this;\n\n\t\tguestbookEntryModelImpl._originalUuid = guestbookEntryModelImpl._uuid;\n\n\t\tguestbookEntryModelImpl._originalGuestbookId =\n\t\t\tguestbookEntryModelImpl._guestbookId;\n\n\t\tguestbookEntryModelImpl._setOriginalGuestbookId = false;\n\n\t\tguestbookEntryModelImpl._originalGroupId =\n\t\t\tguestbookEntryModelImpl._groupId;\n\n\t\tguestbookEntryModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookEntryModelImpl._originalCompanyId =\n\t\t\tguestbookEntryModelImpl._companyId;\n\n\t\tguestbookEntryModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookEntryModelImpl._setModifiedDate = false;\n\n\t\tguestbookEntryModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<GuestbookEntry> toCacheModel() {\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\tnew GuestbookEntryCacheModel();\n\n\t\tguestbookEntryCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookEntryCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.entryId = getEntryId();\n\n\t\tguestbookEntryCacheModel.name = getName();\n\n\t\tString name = guestbookEntryCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.name = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.email = getEmail();\n\n\t\tString email = guestbookEntryCacheModel.email;\n\n\t\tif ((email != null) && (email.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.email = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.message = getMessage();\n\n\t\tString message = guestbookEntryCacheModel.message;\n\n\t\tif ((message != null) && (message.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.message = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookEntryCacheModel.groupId = getGroupId();\n\n\t\tguestbookEntryCacheModel.companyId = getCompanyId();\n\n\t\tguestbookEntryCacheModel.userId = getUserId();\n\n\t\tguestbookEntryCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookEntryCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookEntryCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookEntryCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookEntryCacheModel.status = getStatus();\n\n\t\tguestbookEntryCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookEntryCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookEntryCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookEntryCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookEntryCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, GuestbookEntry>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _originalGuestbookId;\n\tprivate boolean _setOriginalGuestbookId;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate GuestbookEntry _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.Guestbook<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookImpl extends GuestbookBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook model instance should use the {@link com.liferay.docs.guestbook.model.Guestbook} interface instead.\n\t */\n\tpublic GuestbookImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookModel;\nimport com.liferay.docs.guestbook.model.GuestbookSoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookModelImpl\n\textends BaseModelImpl<Guestbook> implements GuestbookModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_Guestbook\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"groupId\", Types.BIGINT},\n\t\t{\"companyId\", Types.BIGINT}, {\"userId\", Types.BIGINT},\n\t\t{\"userName\", Types.VARCHAR}, {\"createDate\", Types.TIMESTAMP},\n\t\t{\"modifiedDate\", Types.TIMESTAMP}, {\"status\", Types.INTEGER},\n\t\t{\"statusByUserId\", Types.BIGINT}, {\"statusByUserName\", Types.VARCHAR},\n\t\t{\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_Guestbook (uuid_ VARCHAR(75) null,guestbookId LONG not null primary key,name VARCHAR(75) null,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_Guestbook\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbook.guestbookId ASC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_Guestbook.guestbookId ASC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 4L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 8L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static Guestbook toModel(GuestbookSoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbook model = new GuestbookImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<Guestbook> toModels(GuestbookSoap[] soapModels) {\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> models = new ArrayList<Guestbook>(soapModels.length);\n\n\t\tfor (GuestbookSoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetGuestbookId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName, attributeGetterFunction.apply((Guestbook)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<Guestbook, Object>> attributeSetterBiConsumers =\n\t\t\tgetAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<Guestbook, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(Guestbook)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<Guestbook, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<Guestbook, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, Guestbook>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbook.class.getClassLoader(), Guestbook.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<Guestbook> constructor =\n\t\t\t\t(Constructor<Guestbook>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<Guestbook, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<Guestbook, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<Guestbook, Object>>();\n\t\tMap<String, BiConsumer<Guestbook, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<Guestbook, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", Guestbook::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\", (BiConsumer<Guestbook, String>)Guestbook::setUuid);\n\t\tattributeGetterFunctions.put(\"guestbookId\", Guestbook::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"name\", Guestbook::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\", (BiConsumer<Guestbook, String>)Guestbook::setName);\n\t\tattributeGetterFunctions.put(\"groupId\", Guestbook::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\", (BiConsumer<Guestbook, Long>)Guestbook::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", Guestbook::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\", (BiConsumer<Guestbook, Long>)Guestbook::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", Guestbook::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\", (BiConsumer<Guestbook, Long>)Guestbook::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", Guestbook::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\", (BiConsumer<Guestbook, String>)Guestbook::setUserName);\n\t\tattributeGetterFunctions.put(\"createDate\", Guestbook::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", Guestbook::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", Guestbook::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\", (BiConsumer<Guestbook, Integer>)Guestbook::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", Guestbook::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", Guestbook::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<Guestbook, String>)Guestbook::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\"statusDate\", Guestbook::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), Guestbook.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic Guestbook toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tguestbookImpl.setUuid(getUuid());\n\t\tguestbookImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookImpl.setName(getName());\n\t\tguestbookImpl.setGroupId(getGroupId());\n\t\tguestbookImpl.setCompanyId(getCompanyId());\n\t\tguestbookImpl.setUserId(getUserId());\n\t\tguestbookImpl.setUserName(getUserName());\n\t\tguestbookImpl.setCreateDate(getCreateDate());\n\t\tguestbookImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookImpl.setStatus(getStatus());\n\t\tguestbookImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(Guestbook guestbook) {\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() < primaryKey) {\n\t\t\treturn -1;\n\t\t}\n\t\telse if (getPrimaryKey() > primaryKey) {\n\t\t\treturn 1;\n\t\t}\n\t\telse {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof Guestbook)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbook guestbook = (Guestbook)obj;\n\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookModelImpl guestbookModelImpl = this;\n\n\t\tguestbookModelImpl._originalUuid = guestbookModelImpl._uuid;\n\n\t\tguestbookModelImpl._originalGroupId = guestbookModelImpl._groupId;\n\n\t\tguestbookModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookModelImpl._originalCompanyId = guestbookModelImpl._companyId;\n\n\t\tguestbookModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookModelImpl._setModifiedDate = false;\n\n\t\tguestbookModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<Guestbook> toCacheModel() {\n\t\tGuestbookCacheModel guestbookCacheModel = new GuestbookCacheModel();\n\n\t\tguestbookCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookCacheModel.name = getName();\n\n\t\tString name = guestbookCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookCacheModel.name = null;\n\t\t}\n\n\t\tguestbookCacheModel.groupId = getGroupId();\n\n\t\tguestbookCacheModel.companyId = getCompanyId();\n\n\t\tguestbookCacheModel.userId = getUserId();\n\n\t\tguestbookCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookCacheModel.status = getStatus();\n\n\t\tguestbookCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, Guestbook>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate Guestbook _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryBatchReindexer.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\npublic interface GuestbookEntryBatchReindexer {\n\t\n\tpublic void reindex(long guestbookId, long companyId);\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryBatchReindexerImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.search.batch.BatchIndexingActionable;\nimport com.liferay.portal.search.indexer.IndexerDocumentBuilder;\nimport com.liferay.portal.search.indexer.IndexerWriter;\n\n@Component(immediate = true, service = GuestbookEntryBatchReindexer.class)\npublic class GuestbookEntryBatchReindexerImpl implements GuestbookEntryBatchReindexer {\n\n    @Override\n    public void reindex(long guestbookId, long companyId) {\n        BatchIndexingActionable batchIndexingActionable =\n    indexerWriter.getBatchIndexingActionable();\n\n        batchIndexingActionable.setAddCriteriaMethod(dynamicQuery -> {\n            Property guestbookIdPropery = PropertyFactoryUtil.forName(\n    \"guestbookId\");\n\n            dynamicQuery.add(guestbookIdPropery.eq(guestbookId));\n        });\n\n        batchIndexingActionable.setCompanyId(companyId);\n\n        batchIndexingActionable.setPerformActionMethod((GuestbookEntry entry) -> {\n            Document document = indexerDocumentBuilder.getDocument(entry);\n\n            batchIndexingActionable.addDocuments(document);\n        });\n\n        batchIndexingActionable.performActions();\n        \n    }\n\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n    protected IndexerDocumentBuilder indexerDocumentBuilder;\n\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n    protected IndexerWriter<GuestbookEntry> indexerWriter;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryKeywordQueryContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.portal.kernel.search.BooleanQuery;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.search.SearchContext;\nimport com.liferay.portal.search.query.QueryHelper;\nimport com.liferay.portal.search.spi.model.query.contributor.KeywordQueryContributor;\nimport com.liferay.portal.search.spi.model.query.contributor.helper.KeywordQueryContributorHelper;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = KeywordQueryContributor.class\n)\npublic class GuestbookEntryKeywordQueryContributor implements KeywordQueryContributor {\n\n    @Override\n    public void contribute(\n        String keywords, BooleanQuery booleanQuery,\n        KeywordQueryContributorHelper keywordQueryContributorHelper) {\n\n        SearchContext searchContext =\n    keywordQueryContributorHelper.getSearchContext();\n\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.TITLE, false);\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.CONTENT, false);\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, \"entryEmail\", false);\n    }\n\n    @Reference\n    protected QueryHelper queryHelper;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryModelDocumentContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.util.LocalizationUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelDocumentContributor;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelDocumentContributor.class\n)\npublic class GuestbookEntryModelDocumentContributor\n    implements ModelDocumentContributor<GuestbookEntry> {\n\n    @Override\n    public void contribute(Document document, GuestbookEntry entry) {\n        try {\n            Locale defaultLocale = PortalUtil.getSiteDefaultLocale(\n    entry.getGroupId());\n\n            document.addDate(Field.MODIFIED_DATE, entry.getModifiedDate());\n            document.addText(\"entryEmail\", entry.getEmail());\n\n            String localizedTitle = LocalizationUtil.getLocalizedName(\n    Field.TITLE, defaultLocale.toString());\n            String localizedContent = LocalizationUtil.getLocalizedName(\n    Field.CONTENT, defaultLocale.toString());\n\n            document.addText(localizedTitle, entry.getName());\n            document.addText(localizedContent, entry.getMessage());\n\n            long guestbookId = entry.getGuestbookId();\n\n            Guestbook guestbook = _guestbookLocalService.getGuestbook(\n    guestbookId);\n\n            String guestbookName = guestbook.getName();\n\n            String localizedGbName = LocalizationUtil.getLocalizedName(\n    Field.NAME, defaultLocale.toString());\n\n            document.addText(localizedGbName, guestbookName);\n        } catch (PortalException pe) {\n            if (_log.isWarnEnabled()) {\n                _log.warn(\"Unable to index entry \" + entry.getEntryId(), pe);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    private static final Log _log = LogFactoryUtil.getLog(\n    GuestbookEntryModelDocumentContributor.class);\n\n    @Reference\n    private GuestbookLocalService _guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryModelIndexerWriterContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.search.batch.BatchIndexingActionable;\nimport com.liferay.portal.search.batch.DynamicQueryBatchIndexingActionableFactory;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor;\nimport com.liferay.portal.search.spi.model.index.contributor.helper.ModelIndexerWriterDocumentHelper;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelIndexerWriterContributor.class\n)\npublic class GuestbookEntryModelIndexerWriterContributor\n    implements ModelIndexerWriterContributor<GuestbookEntry> {\n\n    @Override\n    public void customize(\n        BatchIndexingActionable batchIndexingActionable,\n        ModelIndexerWriterDocumentHelper modelIndexerWriterDocumentHelper) {\n\n        batchIndexingActionable.setPerformActionMethod((GuestbookEntry entry) -> {\n            Document document = modelIndexerWriterDocumentHelper.getDocument(\n    entry);\n\n            batchIndexingActionable.addDocuments(document);\n            \n        });\n    }\n\n    @Override\n    public BatchIndexingActionable getBatchIndexingActionable() {\n        return dynamicQueryBatchIndexingActionableFactory.getBatchIndexingActionable(\n    guestbookEntryLocalService.getIndexableActionableDynamicQuery());\n    }\n\n    @Override\n    public long getCompanyId(GuestbookEntry entry) {\n        return entry.getCompanyId();\n    }\n\n    @Reference\n    protected DynamicQueryBatchIndexingActionableFactory\n    dynamicQueryBatchIndexingActionableFactory;\n\n    @Reference\n    protected GuestbookEntryLocalService guestbookEntryLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryModelSummaryContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.petra.string.StringPool;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.search.Summary;\nimport com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelSummaryContributor.class\n)\npublic class GuestbookEntryModelSummaryContributor implements ModelSummaryContributor {\n\n    @Override\n    public Summary getSummary(\n        Document document, Locale locale, String snippet) {\n\n        Summary summary = createSummary(document);\n\n        summary.setMaxContentLength(128);\n\n        return summary;\n    }\n\n    private Summary createSummary(Document document) {\n        String prefix = Field.SNIPPET + StringPool.UNDERLINE;\n\n        String title = document.get(prefix + Field.TITLE, Field.CONTENT);\n        String content = document.get(prefix + Field.CONTENT, Field.CONTENT);\n\n        return new Summary(title, content);\n    }\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntrySearchRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor;\nimport com.liferay.portal.search.spi.model.registrar.ModelSearchRegistrarHelper;\nimport com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor;\n\n@Component(immediate=true)\npublic class GuestbookEntrySearchRegistrar {\n\n\t@Activate\n\tprotected void activate(BundleContext bundleContext) {\n\n\t\t_serviceRegistration = modelSearchRegistrarHelper.register(\n\t\t\tGuestbookEntry.class, bundleContext, modelSearchDefinition -> {\n\t\t\t\tmodelSearchDefinition.setDefaultSelectedFieldNames(\n\t\t\t\t\tField.COMPANY_ID, Field.ENTRY_CLASS_NAME,\n\t\t\t\t\tField.ENTRY_CLASS_PK, Field.UID, \n\t\t\t\t\tField.SCOPE_GROUP_ID, Field.GROUP_ID);\n\n\t\t\t\tmodelSearchDefinition.setDefaultSelectedLocalizedFieldNames(\n\t\t\t\t\tField.TITLE, Field.CONTENT);\n\n\t\t\t\tmodelSearchDefinition.setModelIndexWriteContributor(\n\t\t\t\t\tmodelIndexWriterContributor);\n\t\t\t\tmodelSearchDefinition.setModelSummaryContributor(\n\t\t\t\t\tmodelSummaryContributor);\n\t\t\t\tmodelSearchDefinition.setSelectAllLocales(true);\n\n\t\t\t});\n\t}\n\n\t@Deactivate\n\tprotected void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\t@Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n\tprotected ModelIndexerWriterContributor<GuestbookEntry> modelIndexWriterContributor;\n\n\t@Reference\n\tprotected ModelSearchRegistrarHelper modelSearchRegistrarHelper;\n\n\t@Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n\tprotected ModelSummaryContributor modelSummaryContributor;\n\n\tprivate ServiceRegistration<?> _serviceRegistration;\n\n    }"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookKeywordQueryContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.portal.kernel.search.BooleanQuery;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.search.SearchContext;\nimport com.liferay.portal.search.query.QueryHelper;\nimport com.liferay.portal.search.spi.model.query.contributor.KeywordQueryContributor;\nimport com.liferay.portal.search.spi.model.query.contributor.helper.KeywordQueryContributorHelper;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = KeywordQueryContributor.class\n)\npublic class GuestbookKeywordQueryContributor\n    implements KeywordQueryContributor {\n\n    @Override\n    public void contribute(\n        String keywords, BooleanQuery booleanQuery,\n        KeywordQueryContributorHelper keywordQueryContributorHelper) {\n\n        SearchContext searchContext =\n    keywordQueryContributorHelper.getSearchContext();\n\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.TITLE, false);\n    }\n\n    @Reference\n    protected QueryHelper queryHelper;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookModelDocumentContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.util.LocalizationUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelDocumentContributor;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelDocumentContributor.class\n)\npublic class GuestbookModelDocumentContributor\n    implements ModelDocumentContributor<Guestbook> {\n\n    @Override\n    public void contribute(Document document, Guestbook guestbook) {\n        try {\n            document.addDate(Field.MODIFIED_DATE, guestbook.getModifiedDate());\n\n            Locale defaultLocale = PortalUtil.getSiteDefaultLocale(\n    guestbook.getGroupId());\n\n            String localizedTitle = LocalizationUtil.getLocalizedName(\n    Field.TITLE, defaultLocale.toString());\n\n            document.addText(localizedTitle, guestbook.getName());\n        } catch (PortalException pe) {\n            if (_log.isWarnEnabled()) {\n                _log.warn(\n    \"Unable to index guestbook \" + guestbook.getGuestbookId(), pe);\n            }\n        }\n    }\n\n    private static final Log _log = LogFactoryUtil.getLog(\n    GuestbookModelDocumentContributor.class);\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookModelIndexerWriterContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.search.batch.BatchIndexingActionable;\nimport com.liferay.portal.search.batch.DynamicQueryBatchIndexingActionableFactory;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor;\nimport com.liferay.portal.search.spi.model.index.contributor.helper.ModelIndexerWriterDocumentHelper;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelIndexerWriterContributor.class\n)\npublic class GuestbookModelIndexerWriterContributor\n    implements ModelIndexerWriterContributor<Guestbook> {\n\n    @Override\n    public void customize(\n        BatchIndexingActionable batchIndexingActionable,\n        ModelIndexerWriterDocumentHelper modelIndexerWriterDocumentHelper) {\n\n        batchIndexingActionable.setPerformActionMethod((Guestbook guestbook) -> {\n            Document document = modelIndexerWriterDocumentHelper.getDocument(\n    guestbook);\n\n            batchIndexingActionable.addDocuments(document);\n        });\n    }\n\n    @Override\n    public BatchIndexingActionable getBatchIndexingActionable() {\n        return dynamicQueryBatchIndexingActionableFactory.getBatchIndexingActionable(\n    guestbookLocalService.getIndexableActionableDynamicQuery());\n    }\n\n    @Override\n    public long getCompanyId(Guestbook guestbook) {\n        return guestbook.getCompanyId();\n    }\n\n    @Override\n    public void modelIndexed(Guestbook guestbook) {\n        guestbookEntryBatchReindexer.reindex(\n    guestbook.getGuestbookId(), guestbook.getCompanyId());\n    }\n\n    @Reference\n    protected DynamicQueryBatchIndexingActionableFactory\n    dynamicQueryBatchIndexingActionableFactory;\n\n    @Reference\n    protected GuestbookEntryBatchReindexer guestbookEntryBatchReindexer;\n\n    @Reference\n    protected GuestbookLocalService guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookModelSummaryContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.petra.string.StringPool;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.search.Summary;\nimport com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelSummaryContributor.class\n)\npublic class GuestbookModelSummaryContributor\n    implements ModelSummaryContributor {\n\n    @Override\n    public Summary getSummary(\n        Document document, Locale locale, String snippet) {\n\n        Summary summary = createSummary(document);\n\n        summary.setMaxContentLength(200);\n\n        return summary;\n    }\n\n    private Summary createSummary(Document document) {\n        String prefix = Field.SNIPPET + StringPool.UNDERLINE;\n\n        String title = document.get(prefix + Field.TITLE, Field.TITLE);\n\n        return new Summary(title, StringPool.BLANK);\n    }\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookSearchRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor;\nimport com.liferay.portal.search.spi.model.registrar.ModelSearchRegistrarHelper;\nimport com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor;\n\n@Component(immediate=true)\npublic class GuestbookSearchRegistrar {\n\n        @Activate\n        protected void activate(BundleContext bundleContext) {\n\n            _serviceRegistration = modelSearchRegistrarHelper.register(\n                Guestbook.class, bundleContext, modelSearchDefinition -> {\n                    modelSearchDefinition.setDefaultSelectedFieldNames(\n                        Field.ASSET_TAG_NAMES, Field.COMPANY_ID, Field.CONTENT,\n                        Field.ENTRY_CLASS_NAME, Field.ENTRY_CLASS_PK,\n                        Field.GROUP_ID, Field.MODIFIED_DATE, Field.SCOPE_GROUP_ID,\n                        Field.TITLE, Field.UID);\n\n                    modelSearchDefinition.setModelIndexWriteContributor(\n                        modelIndexWriterContributor);\n                    modelSearchDefinition.setModelSummaryContributor(\n                        modelSummaryContributor);\n                });\n        }\n\n        @Deactivate\n        protected void deactivate() {\n\n            _serviceRegistration.unregister();\n        }\n\n        @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.Guestbook)\")\n        protected ModelIndexerWriterContributor<Guestbook> modelIndexWriterContributor;\n\n        @Reference\n        protected ModelSearchRegistrarHelper modelSearchRegistrarHelper;\n\n        @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.Guestbook)\")\n        protected ModelSummaryContributor modelSummaryContributor;\n\n        private ServiceRegistration<?> _serviceRegistration;\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookEntryLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\tguestbookEntry.setNew(true);\n\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.create(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbookEntry.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId) {\n\n\t\treturn guestbookEntryPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookEntryLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbookEntry.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<GuestbookEntry>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(GuestbookEntry guestbookEntry)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbookEntry);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryLocalService.deleteGuestbookEntry(\n\t\t\t(GuestbookEntry)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end) {\n\t\treturn guestbookEntryPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn guestbookEntryPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryLocalService = (GuestbookEntryLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\tprotected GuestbookEntryLocalService guestbookEntryLocalService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl\n * @generated\n */\npublic abstract class GuestbookEntryServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookEntryService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryService = (GuestbookEntryService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookEntryLocalService\n\t\tguestbookEntryLocalService;\n\n\tprotected GuestbookEntryService guestbookEntryService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook addGuestbook(Guestbook guestbook) {\n\t\tguestbook.setNew(true);\n\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.create(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.remove(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.remove(guestbook);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbook.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic Guestbook fetchGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\t\treturn guestbookPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.findByPrimaryKey(guestbookId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\n\t\t\t\"guestbookId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbook.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<Guestbook>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(Guestbook guestbook)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbook);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookLocalService.deleteGuestbook((Guestbook)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn guestbookPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooks(int start, int end) {\n\t\treturn guestbookPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn guestbookPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook updateGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookLocalService = (GuestbookLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\tprotected GuestbookLocalService guestbookLocalService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl\n * @generated\n */\npublic abstract class GuestbookServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookService = (GuestbookService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookLocalService\n\t\tguestbookLocalService;\n\n\tprotected GuestbookService guestbookService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntry</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.Guestbook</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.docs.guestbook.exception.GuestbookEntryEmailException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryMessageException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryNameException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.ResourceConstants;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\n\n/**\n * The implementation of the guestbook entry local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\", service = AopService.class)\npublic class GuestbookEntryLocalServiceImpl extends GuestbookEntryLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n\t * via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code\n\t * >.\n\t */\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(long userId, long guestbookId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tlong entryId = counterLocalService.increment();\n\n\t\tGuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\n\t\tentry.setUuid(serviceContext.getUuid());\n\t\tentry.setUserId(userId);\n\t\tentry.setGroupId(groupId);\n\t\tentry.setCompanyId(user.getCompanyId());\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setCreateDate(serviceContext.getCreateDate(now));\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\t\tentry.setGuestbookId(guestbookId);\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Calls to other Liferay frameworks go here\n\n\t\tresourceLocalService.addResources(user.getCompanyId(), groupId, userId,\n\t\t\tGuestbookEntry.class.getName(), entryId, false, true, true);\n\n\t\treturn entry;\n\t}\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(long userId, long guestbookId,\n\t\t\tlong entryId, String name, String email, String message,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException {\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tentry.setUserId(userId);\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Integrate with Liferay frameworks here.\n\n        resourceLocalService.updateResources(\n              user.getCompanyId(), serviceContext.getScopeGroupId(), \n              GuestbookEntry.class.getName(), entryId, \n              serviceContext.getModelPermissions());\n\n\t\treturn entry;\n\t}\n\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry entry)\n\t\tthrows PortalException {\n\n\t\tguestbookEntryPersistence.remove(entry);\n\n\t\tresourceLocalService.deleteResource(\n\t\t   entry.getCompanyId(), GuestbookEntry.class.getName(),\n\t\t   ResourceConstants.SCOPE_INDIVIDUAL, entry.getEntryId());\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId) throws PortalException {\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\treturn deleteGuestbookEntry(entry);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end) throws SystemException {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end, OrderByComparator<GuestbookEntry> obc) {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend, obc);\n\t}\n\n\tpublic GuestbookEntry getGuestbookEntry(long entryId) throws PortalException {\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.countByG_G(groupId, guestbookId);\n\t}\n\n\tprotected void validate(String name, String email, String entry)\n\t\tthrows PortalException {\n\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookEntryNameException();\n\t\t}\n\n\t\tif (!Validator.isEmailAddress(email)) {\n\t\t\tthrow new GuestbookEntryEmailException();\n\t\t}\n\n\t\tif (Validator.isNull(entry)) {\n\t\t\tthrow new GuestbookEntryMessageException();\n\t\t}\n\t}\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook entry remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookEntryService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=GuestbookEntry\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookEntryServiceImpl extends GuestbookEntryServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> to access the guestbook entry remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.exception.GuestbookNameException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.base.GuestbookLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.ResourceConstants;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\n\n/**\n * The implementation of the guestbook local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.Guestbook\", service = AopService.class)\npublic class GuestbookLocalServiceImpl extends GuestbookLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code> via\n\t * injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(long userId, String name,\n\t\t\tServiceContext serviceContext) throws PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name);\n\n\t\tlong guestbookId = counterLocalService.increment();\n\n\t\tGuestbook guestbook = guestbookPersistence.create(guestbookId);\n\n\t\tguestbook.setUuid(serviceContext.getUuid());\n\t\tguestbook.setUserId(userId);\n\t\tguestbook.setGroupId(groupId);\n\t\tguestbook.setCompanyId(user.getCompanyId());\n\t\tguestbook.setUserName(user.getFullName());\n\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tguestbook.setName(name);\n\t\tguestbook.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookPersistence.update(guestbook);\n\n\t\tresourceLocalService.addResources(user.getCompanyId(), groupId, userId,\n\t\t\t\tGuestbook.class.getName(), guestbookId, false, true, true);\n\n\t\treturn guestbook;\n\t}\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(long userId, long guestbookId,\n        String name, ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Date now = new Date();\n\n            validate(name);\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            User user = userLocalService.getUser(userId);\n\n            guestbook.setUserId(userId);\n            guestbook.setUserName(user.getFullName());\n            guestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n            guestbook.setName(name);\n            guestbook.setExpandoBridgeAttributes(serviceContext);\n\n            guestbookPersistence.update(guestbook);\n\n            resourceLocalService.updateResources(serviceContext.getCompanyId(),\n                    serviceContext.getScopeGroupId(), \n                    Guestbook.class.getName(), guestbookId,\n                    serviceContext.getModelPermissions());\n\n            return guestbook;\n    }\n\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(long guestbookId,\n                    ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            List<GuestbookEntry> entries = _guestbookEntryLocalService.getGuestbookEntries(\n                            serviceContext.getScopeGroupId(), guestbookId);\n\n            for (GuestbookEntry entry : entries) {\n                    _guestbookEntryLocalService.deleteGuestbookEntry(entry.getEntryId());\n            }\n\n            guestbook = deleteGuestbook(guestbook);\n\n\t\t\tresourceLocalService.deleteResource(serviceContext.getCompanyId(),\n\t\t\t\t\tGuestbook.class.getName(), ResourceConstants.SCOPE_INDIVIDUAL,\n\t\t\t\t\tguestbookId);\n            return guestbook;\n    }\n\n\tpublic List<Guestbook> getGuestbooks(long groupId) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end,\n\t\t\tOrderByComparator<Guestbook> obc) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end, obc);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end);\n\t}\n\n\tpublic int getGuestbooksCount(long groupId) {\n\n\t\treturn guestbookPersistence.countByGroupId(groupId);\n\t}\n\n\tprotected void validate(String name) throws PortalException {\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookNameException();\n\t\t}\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=Guestbook\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookServiceImpl extends GuestbookServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> to access the guestbook remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookEntryPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.SQLQuery;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.security.permission.InlineSQLHelperUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookEntryPersistence.class)\n@ProviderType\npublic class GuestbookEntryPersistenceImpl\n\textends BasePersistenceImpl<GuestbookEntry>\n\timplements GuestbookEntryPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookEntryUtil</code> to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookEntryImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_First(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_Last(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbookEntry.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof GuestbookEntry) {\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbookEntry.getUuid()) ||\n\t\t\t\t(groupId != guestbookEntry.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<GuestbookEntry> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbookEntry guestbookEntry = list.get(0);\n\n\t\t\t\t\tresult = guestbookEntry;\n\n\t\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (GuestbookEntry)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\t@Override\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbookEntry.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\ttrue);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\tfalse);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_C_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tlong companyId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbookEntry.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByG_G;\n\tprivate FinderPath _finderPathWithoutPaginationFindByG_G;\n\tprivate FinderPath _finderPathCountByG_G;\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(long groupId, long guestbookId) {\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn findByG_G(groupId, guestbookId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {groupId, guestbookId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tgroupId, guestbookId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif ((groupId != guestbookEntry.getGroupId()) ||\n\t\t\t\t\t\t(guestbookId != guestbookEntry.getGuestbookId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByG_G(groupId, guestbookId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByG_G_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tlong guestbookId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(guestbookId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn filterFindByG_G(\n\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn filterFindByG_G(groupId, guestbookId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_G(\n\t\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t\t}\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator, true);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_TABLE, orderByComparator, true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookEntryImpl.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookEntryImpl.class);\n\t\t\t}\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(guestbookId);\n\n\t\t\treturn (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\tq, getDialect(), start, end);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] filterFindByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_G_PrevAndNext(\n\t\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t\t}\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = filterGetByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = filterGetByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry filterGetByG_G_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tlong guestbookId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t6 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByFields[i], true));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByFields[i], true));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookEntryImpl.class);\n\t\t}\n\t\telse {\n\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookEntryImpl.class);\n\t\t}\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(guestbookId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\t@Override\n\tpublic void removeByG_G(long groupId, long guestbookId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByG_G(\n\t\t\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByG_G(long groupId, long guestbookId) {\n\t\tFinderPath finderPath = _finderPathCountByG_G;\n\n\t\tObject[] finderArgs = new Object[] {groupId, guestbookId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic int filterCountByG_G(long groupId, long guestbookId) {\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn countByG_G(groupId, guestbookId);\n\t\t}\n\n\t\tStringBundler query = new StringBundler(3);\n\n\t\tquery.append(_FILTER_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tq.addScalar(\n\t\t\t\tCOUNT_COLUMN_NAME, com.liferay.portal.kernel.dao.orm.Type.LONG);\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(guestbookId);\n\n\t\t\tLong count = (Long)q.uniqueResult();\n\n\t\t\treturn count.intValue();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprivate static final String _FINDER_COLUMN_G_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_G_G_GUESTBOOKID_2 =\n\t\t\"guestbookEntry.guestbookId = ?\";\n\n\tpublic GuestbookEntryPersistenceImpl() {\n\t\tsetModelClass(GuestbookEntry.class);\n\n\t\tsetModelImplClass(GuestbookEntryImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\t@Override\n\tpublic void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {\n\t\t\t\tguestbookEntry.getUuid(), guestbookEntry.getGroupId()\n\t\t\t},\n\t\t\tguestbookEntry);\n\n\t\tguestbookEntry.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\t@Override\n\tpublic void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbook entries.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookEntryImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook entry.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(GuestbookEntry guestbookEntry) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookEntryModelImpl)guestbookEntry, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<GuestbookEntry> guestbookEntries) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache(\n\t\t\t\t(GuestbookEntryModelImpl)guestbookEntry, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookEntryModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic GuestbookEntry create(long entryId) {\n\t\tGuestbookEntry guestbookEntry = new GuestbookEntryImpl();\n\n\t\tguestbookEntry.setNew(true);\n\t\tguestbookEntry.setPrimaryKey(entryId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbookEntry.setUuid(uuid);\n\n\t\tguestbookEntry.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn remove((Serializable)entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\tGuestbookEntryImpl.class, primaryKey);\n\n\t\t\tif (guestbookEntry == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbookEntry);\n\t\t}\n\t\tcatch (NoSuchGuestbookEntryException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected GuestbookEntry removeImpl(GuestbookEntry guestbookEntry) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbookEntry)) {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\t\tGuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbookEntry != null) {\n\t\t\t\tsession.delete(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbookEntry != null) {\n\t\t\tclearCache(guestbookEntry);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t@Override\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\tboolean isNew = guestbookEntry.isNew();\n\n\t\tif (!(guestbookEntry instanceof GuestbookEntryModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbookEntry.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(\n\t\t\t\t\tguestbookEntry);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbookEntry proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom GuestbookEntry implementation \" +\n\t\t\t\t\tguestbookEntry.getClass());\n\t\t}\n\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl =\n\t\t\t(GuestbookEntryModelImpl)guestbookEntry;\n\n\t\tif (Validator.isNull(guestbookEntry.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbookEntry.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbookEntry.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookEntryModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setModifiedDate(\n\t\t\t\t\tserviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbookEntry.isNew()) {\n\t\t\t\tsession.save(guestbookEntry);\n\n\t\t\t\tguestbookEntry.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.merge(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByG_G.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry, false);\n\n\t\tclearUniqueFindersCache(guestbookEntryModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookEntryModelImpl);\n\n\t\tguestbookEntry.resetOriginalValues();\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn findByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn fetchByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOKENTRY;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (GuestbookEntry guestbookEntry : findAll()) {\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOKENTRY);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"entryId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOKENTRY;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookEntryModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook entry persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookEntryModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookEntryModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.COMPANYID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GUESTBOOKID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookEntryImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.GuestbookEntry\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _FILTER_ENTITY_TABLE_FILTER_PK_COLUMN =\n\t\t\"guestbookEntry.entryId\";\n\n\tprivate static final String _FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT DISTINCT {guestbookEntry.*} FROM GB_GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String\n\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1 =\n\t\t\t\"SELECT {GB_GuestbookEntry.*} FROM (SELECT DISTINCT guestbookEntry.entryId FROM GB_GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String\n\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2 =\n\t\t\t\") TEMP_TABLE INNER JOIN GB_GuestbookEntry ON TEMP_TABLE.entryId = GB_GuestbookEntry.entryId\";\n\n\tprivate static final String _FILTER_SQL_COUNT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT COUNT(DISTINCT guestbookEntry.entryId) AS COUNT_VALUE FROM GB_GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _FILTER_ENTITY_ALIAS = \"guestbookEntry\";\n\n\tprivate static final String _FILTER_ENTITY_TABLE = \"GB_GuestbookEntry\";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbookEntry.\";\n\n\tprivate static final String _ORDER_BY_ENTITY_TABLE = \"GB_GuestbookEntry.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No GuestbookEntry exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No GuestbookEntry exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookEntryPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.impl.GuestbookImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.SQLQuery;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.security.permission.InlineSQLHelperUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookPersistence.class)\n@ProviderType\npublic class GuestbookPersistenceImpl\n\textends BasePersistenceImpl<Guestbook> implements GuestbookPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookUtil</code> to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_First(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_Last(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbook.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbook == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof Guestbook) {\n\t\t\tGuestbook guestbook = (Guestbook)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbook.getUuid()) ||\n\t\t\t\t(groupId != guestbook.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<Guestbook> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbook guestbook = list.get(0);\n\n\t\t\t\t\tresult = guestbook;\n\n\t\t\t\t\tcacheResult(guestbook);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (Guestbook)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbook);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbook.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_C_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbook.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByGroupId;\n\tprivate FinderPath _finderPathWithoutPaginationFindByGroupId;\n\tprivate FinderPath _finderPathCountByGroupId;\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId) {\n\t\treturn findByGroupId(\n\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId, int start, int end) {\n\t\treturn findByGroupId(groupId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByGroupId(groupId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif ((groupId != guestbook.getGroupId())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_First(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByGroupId(groupId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_Last(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByGroupId(groupId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByGroupId(\n\t\t\tgroupId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByGroupId_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByGroupId(long groupId) {\n\t\treturn filterFindByGroupId(\n\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn filterFindByGroupId(groupId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByGroupId(groupId, start, end, orderByComparator);\n\t\t}\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOK_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator, true);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_TABLE, orderByComparator, true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookImpl.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookImpl.class);\n\t\t\t}\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\treturn (List<Guestbook>)QueryUtil.list(q, getDialect(), start, end);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] filterFindByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByGroupId_PrevAndNext(\n\t\t\t\tguestbookId, groupId, orderByComparator);\n\t\t}\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = filterGetByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = filterGetByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook filterGetByGroupId_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOK_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByFields[i], true));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByFields[i], true));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookImpl.class);\n\t\t}\n\t\telse {\n\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookImpl.class);\n\t\t}\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\t@Override\n\tpublic void removeByGroupId(long groupId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByGroupId(\n\t\t\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByGroupId(long groupId) {\n\t\tFinderPath finderPath = _finderPathCountByGroupId;\n\n\t\tObject[] finderArgs = new Object[] {groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic int filterCountByGroupId(long groupId) {\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn countByGroupId(groupId);\n\t\t}\n\n\t\tStringBundler query = new StringBundler(2);\n\n\t\tquery.append(_FILTER_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tq.addScalar(\n\t\t\t\tCOUNT_COLUMN_NAME, com.liferay.portal.kernel.dao.orm.Type.LONG);\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tLong count = (Long)q.uniqueResult();\n\n\t\t\treturn count.intValue();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprivate static final String _FINDER_COLUMN_GROUPID_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tpublic GuestbookPersistenceImpl() {\n\t\tsetModelClass(Guestbook.class);\n\n\t\tsetModelImplClass(GuestbookImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\t@Override\n\tpublic void cacheResult(Guestbook guestbook) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {guestbook.getUuid(), guestbook.getGroupId()},\n\t\t\tguestbook);\n\n\t\tguestbook.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\t@Override\n\tpublic void cacheResult(List<Guestbook> guestbooks) {\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\t\tguestbook.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbook);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbooks.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(Guestbook guestbook) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<Guestbook> guestbooks) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\tguestbook.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic Guestbook create(long guestbookId) {\n\t\tGuestbook guestbook = new GuestbookImpl();\n\n\t\tguestbook.setNew(true);\n\t\tguestbook.setPrimaryKey(guestbookId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbook.setUuid(uuid);\n\n\t\tguestbook.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException {\n\t\treturn remove((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook guestbook = (Guestbook)session.get(\n\t\t\t\tGuestbookImpl.class, primaryKey);\n\n\t\t\tif (guestbook == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbook);\n\t\t}\n\t\tcatch (NoSuchGuestbookException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected Guestbook removeImpl(Guestbook guestbook) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbook)) {\n\t\t\t\tguestbook = (Guestbook)session.get(\n\t\t\t\t\tGuestbookImpl.class, guestbook.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbook != null) {\n\t\t\t\tsession.delete(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbook != null) {\n\t\t\tclearCache(guestbook);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t@Override\n\tpublic Guestbook updateImpl(Guestbook guestbook) {\n\t\tboolean isNew = guestbook.isNew();\n\n\t\tif (!(guestbook instanceof GuestbookModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbook.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(guestbook);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbook proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom Guestbook implementation \" +\n\t\t\t\t\tguestbook.getClass());\n\t\t}\n\n\t\tGuestbookModelImpl guestbookModelImpl = (GuestbookModelImpl)guestbook;\n\n\t\tif (Validator.isNull(guestbook.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbook.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbook.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbook.isNew()) {\n\t\t\t\tsession.save(guestbook);\n\n\t\t\t\tguestbook.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook = (Guestbook)session.merge(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getUuid(),\n\t\t\t\t\tguestbookModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByGroupId.\n\t\t\t\t\t getColumnBitmask()) != 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook, false);\n\n\t\tclearUniqueFindersCache(guestbookModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookModelImpl);\n\n\t\tguestbook.resetOriginalValues();\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbook == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\treturn findByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn fetchByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOK);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOK;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (Guestbook guestbook : findAll()) {\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOK);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"guestbookId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOK;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.COMPANYID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {Long.class.getName()},\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByGroupId\",\n\t\t\tnew String[] {Long.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.Guestbook\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK =\n\t\t\"SELECT guestbook FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK_WHERE =\n\t\t\"SELECT guestbook FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK_WHERE =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _FILTER_ENTITY_TABLE_FILTER_PK_COLUMN =\n\t\t\"guestbook.guestbookId\";\n\n\tprivate static final String _FILTER_SQL_SELECT_GUESTBOOK_WHERE =\n\t\t\"SELECT DISTINCT {guestbook.*} FROM GB_Guestbook guestbook WHERE \";\n\n\tprivate static final String\n\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1 =\n\t\t\t\"SELECT {GB_Guestbook.*} FROM (SELECT DISTINCT guestbook.guestbookId FROM GB_Guestbook guestbook WHERE \";\n\n\tprivate static final String\n\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2 =\n\t\t\t\") TEMP_TABLE INNER JOIN GB_Guestbook ON TEMP_TABLE.guestbookId = GB_Guestbook.guestbookId\";\n\n\tprivate static final String _FILTER_SQL_COUNT_GUESTBOOK_WHERE =\n\t\t\"SELECT COUNT(DISTINCT guestbook.guestbookId) AS COUNT_VALUE FROM GB_Guestbook guestbook WHERE \";\n\n\tprivate static final String _FILTER_ENTITY_ALIAS = \"guestbook\";\n\n\tprivate static final String _FILTER_ENTITY_TABLE = \"GB_Guestbook\";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbook.\";\n\n\tprivate static final String _ORDER_BY_ENTITY_TABLE = \"GB_Guestbook.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No Guestbook exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No Guestbook exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/constants/GBPersistenceConstants.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl.constants;\n\nimport com.liferay.petra.string.StringBundler;\n\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.Constants;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * @author Liferay\n * @generated\n */\n@Component(immediate = true, service = {})\npublic class GBPersistenceConstants {\n\n\tpublic static final String BUNDLE_SYMBOLIC_NAME =\n\t\t\"com.liferay.docs.guestbook.service\";\n\n\tpublic static final String ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER =\n\t\t\"(origin.bundle.symbolic.name=\" + BUNDLE_SYMBOLIC_NAME + \")\";\n\n\t@Activate\n\tprotected void activate(BundleContext bundleContext) {\n\t\tBundle bundle = bundleContext.getBundle();\n\n\t\tif (!BUNDLE_SYMBOLIC_NAME.equals(bundle.getSymbolicName())) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\tStringBundler.concat(\n\t\t\t\t\t\"Incorrect \", Constants.BUNDLE_SYMBOLICNAME, \" for bundle \",\n\t\t\t\t\tbundle.getSymbolicName()));\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/module-hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">\n\n<hibernate-mapping auto-import=\"false\" default-lazy=\"false\">\n\t<import class=\"com.liferay.docs.guestbook.model.Guestbook\" />\n\t<import class=\"com.liferay.docs.guestbook.model.GuestbookEntry\" />\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\" table=\"GB_Guestbook\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\" table=\"GB_GuestbookEntry\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"entryId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"email\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"message\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n</hibernate-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/portlet-model-hints.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<model-hints>\n\t<model name=\"com.liferay.docs.guestbook.model.Guestbook\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n\t<model name=\"com.liferay.docs.guestbook.model.GuestbookEntry\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"entryId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"email\" type=\"String\" />\n\t\t<field name=\"message\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n</model-hints>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/resource-actions/default.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action  \nMapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n\n<resource-action-mapping>\n\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n        </portlet-ref>\n        <root>true</root>\n        <permissions>\n            <supports>\n                <action-key>ADD_GUESTBOOK</action-key>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>ADD_ENTRY</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>ADD_GUESTBOOK</action-key>\n                <action-key>ADD_ENTRY</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook.model.Guestbook</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n        </portlet-ref>\n        <permissions>\n            <supports>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>DELETE</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>UPDATE</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>UPDATE</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook.model.GuestbookEntry</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n        </portlet-ref>\n        <permissions>\n            <supports>\n                <action-key>DELETE</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>UPDATE</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>UPDATE</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n\n</resource-action-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/indexes.sql",
    "content": "create index IX_9294AD47 on GB_Guestbook (groupId);\ncreate index IX_9314A9F7 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_EDD4239 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], groupId);\n\ncreate index IX_E84D72FD on GB_GuestbookEntry (groupId, guestbookId);\ncreate index IX_CC265FEF on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_4A541631 on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], groupId);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/sequences.sql",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/tables.sql",
    "content": "create table GB_Guestbook (\n\tuuid_ VARCHAR(75) null,\n\tguestbookId LONG not null primary key,\n\tname VARCHAR(75) null,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);\n\ncreate table GB_GuestbookEntry (\n\tuuid_ VARCHAR(75) null,\n\tentryId LONG not null primary key,\n\tname VARCHAR(75) null,\n\temail VARCHAR(75) null,\n\tmessage VARCHAR(75) null,\n\tguestbookId LONG,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/portlet.properties",
    "content": "resource.actions.configs=META-INF/resource-actions/default.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/service.properties",
    "content": "##\n## Properties Override\n##\n\n    #\n    # Specify where to get the overridden properties. Updates should not be made\n    # on this file but on the overridden version of this file.\n    #\n    include-and-override=service-ext.properties\n\n##\n## Build\n##\n\n    build.namespace=GB\n    build.number=7\n    build.date=1569430062885"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/.gitignore",
    "content": ".gradle/\n.sass-cache/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/bnd.bnd",
    "content": "Bundle-Name: guestbook-web\nBundle-SymbolicName: com.liferay.docs.guestbook.portlet\nBundle-Version: 1.0.0\nExport-Package: com.liferay.docs.guestbook.portlet.constants"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.application.list.api\"\n\tcompileOnly group: \"javax.portlet\", name: \"portlet-api\"\n\tcompileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n\tcompileOnly group: \"jstl\", name: \"jstl\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\n    compileOnly project(\":modules:guestbook:guestbook-api\")\n    compileOnly project(\":modules:guestbook:guestbook-service\")\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/application/list/GuestbookAdminPanelApp.java",
    "content": "package com.liferay.docs.guestbook.application.list;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.application.list.BasePanelApp;\nimport com.liferay.application.list.PanelApp;\nimport com.liferay.application.list.constants.PanelCategoryKeys;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.portal.kernel.model.Portlet;\n\n@Component(\n        immediate = true,\n        property = {\n            \"panel.app.order:Integer=300\",\n            \"panel.category.key=\" + PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT\n        },\n        service = PanelApp.class\n    )\npublic class GuestbookAdminPanelApp extends BasePanelApp {\n\n        @Override\n        public String getPortletId() {\n            return GuestbookPortletKeys.GUESTBOOK_ADMIN;\n        }\n\n        @Override\n        @Reference(\n            target = \"(javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN + \")\",\n            unbind = \"-\"\n        )\n        public void setPortlet(Portlet portlet) {\n            super.setPortlet(portlet);\n        }\n\n    }\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookAdminPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.servlet.SessionErrors;\nimport com.liferay.portal.kernel.servlet.SessionMessages;\nimport com.liferay.portal.kernel.util.ParamUtil;\n@Component(\n        immediate = true,\n        property = {\n                \"com.liferay.portlet.display-category=category.hidden\",\n                \"com.liferay.portlet.scopeable=true\",\n                \"javax.portlet.display-name=Guestbooks\",\n                \"javax.portlet.expiration-cache=0\",\n                \"javax.portlet.init-param.portlet-title-based-navigation=true\",\n                \"javax.portlet.init-param.template-path=/\",\n                \"javax.portlet.init-param.view-template=/guestbook_admin/view.jsp\",\n                \"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN,\n                \"javax.portlet.resource-bundle=content.Language\",\n                \"javax.portlet.security-role-ref=administrator\",\n                \"javax.portlet.supports.mime-type=text/html\",\n                \"com.liferay.portlet.add-default-resource=true\"\n        },\n        service = Portlet.class\n    )\npublic class GuestbookAdminPortlet extends MVCPortlet {\n\npublic void addGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n\n        try {\n            _guestbookLocalService.addGuestbook(\n                serviceContext.getUserId(), name, serviceContext);\n            \n            SessionMessages.add(request, \"guestbookAdded\");\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n            \n            SessionErrors.add(request, pe.getClass().getName());\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n\n    public void updateGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.updateGuestbook(\n                serviceContext.getUserId(), guestbookId, name, serviceContext);\n            \n            SessionMessages.add(request,  \"guestbookUpdated\");\n\n        } catch (PortalException pe) {\n        \n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            SessionErrors.add(request, pe.getClass().getName());\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n    \n    public void deleteGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.deleteGuestbook(guestbookId, serviceContext);\n            \n            SessionMessages.add(request,  \"guestbookDeleted\");\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            SessionErrors.add(request, pe.getClass().getName());\n        }\n    }\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\nimport javax.portlet.PortletException;\nimport javax.portlet.RenderRequest;\nimport javax.portlet.RenderResponse;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.servlet.SessionErrors;\nimport com.liferay.portal.kernel.servlet.SessionMessages;\nimport com.liferay.portal.kernel.util.ParamUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\n/**\n * @author sezovr\n */\n@Component(immediate = true, property = { \"com.liferay.portlet.display-category=category.social\",\n\t\t\"com.liferay.portlet.header-portlet-css=/css/main.css\", \n\t\t\"com.liferay.portlet.instanceable=false\",\n\t\t\"com.liferay.portlet.scopeable=true\", \n\t\t\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK,\n\t\t\"javax.portlet.display-name=Guestbook\", \n\t\t\"javax.portlet.expiration-cache=0\",\n\t\t\"javax.portlet.init-param.template-path=/\", \n\t\t\"javax.portlet.init-param.view-template=/guestbook/view.jsp\",\n\t\t\"javax.portlet.resource-bundle=content.Language\", \n\t\t\"javax.portlet.security-role-ref=power-user,user\",\n\t\t\"javax.portlet.supports.mime-type=text/html\" }, \n\tservice = Portlet.class)\npublic class GuestbookPortlet extends MVCPortlet {\n\n\tpublic void addEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\n\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(GuestbookEntry.class.getName(), request);\n\n\t\tString userName = ParamUtil.getString(request, \"name\");\n\t\tString email = ParamUtil.getString(request, \"email\");\n\t\tString message = ParamUtil.getString(request, \"message\");\n\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\n\t\tif (entryId > 0) {\n\n\t\t\ttry {\n\n\t\t\t\t_guestbookEntryLocalService.updateGuestbookEntry(serviceContext.getUserId(), guestbookId, entryId, userName,\n\t\t\t\t\t\temail, message, serviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(e);\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\n\t\t} else {\n\n\t\t\ttry {\n\t\t\t\t_guestbookEntryLocalService.addGuestbookEntry(serviceContext.getUserId(), guestbookId, userName, email, message,\n\t\t\t\t\t\tserviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t\tSessionMessages.add(request, \"entryAdded\");\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tSessionErrors.add(request, e.getClass().getName());\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic void deleteEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\t\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\t\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\tGuestbookEntry.class.getName(), request);\n\n\t\t\ttry {\n\n\t\t\t\tresponse.setRenderParameter(\n\t\t\t\t\t\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t\t_guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\n\t\t\t\tSessionMessages.add(request, \"entryDeleted\");\n\n\t\t\t}\n\n\t\t\tcatch (Exception e) {\n\t\t\t\tLogger.getLogger(GuestbookPortlet.class.getName()).log(\n\t\t\t\t\tLevel.SEVERE, null, e);\n\n\t\t\tSessionErrors.add(request, e.getClass().getName());\n\t\t\t}\n\t}\n\n\t@Override\n\tpublic void render(RenderRequest renderRequest, RenderResponse renderResponse)\n\t\t\tthrows IOException, PortletException {\n\n\t\t\ttry {\n\t\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\t\tGuestbook.class.getName(), renderRequest);\n\n\t\t\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\t\t\tlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n\t\t\t\tList<Guestbook> guestbooks = _guestbookLocalService.getGuestbooks(\n\t\t\t\t\tgroupId);\n\n\t\t\t\tif (guestbooks.isEmpty()) {\n\t\t\t\t\tGuestbook guestbook = _guestbookLocalService.addGuestbook(\n\t\t\t\t\t\tserviceContext.getUserId(), \"Main\", serviceContext);\n\n\t\t\t\t\tguestbookId = guestbook.getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\tif (guestbookId == 0) {\n\t\t\t\t\tguestbookId = guestbooks.get(0).getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\trenderRequest.setAttribute(\"guestbookId\", guestbookId);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new PortletException(e);\n\t\t\t}\n\n\t\t\tsuper.render(renderRequest, renderResponse);\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/security/permission/resource/GuestbookEntryPermission.java",
    "content": "package com.liferay.docs.guestbook.web.internal.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n\n@Component(immediate = true)\npublic class GuestbookEntryPermission {\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, GuestbookEntry entry, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookEntryModelResourcePermission.contains(permissionChecker, entry, actionId);\n\t}\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, long entryId, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookEntryModelResourcePermission.contains(permissionChecker, entryId, actionId);\n\t}\n\t\n\t@Reference(\n\t\t\ttarget = \"(model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\", \n\t\t\tunbind = \"-\")\n\tprotected void setEntryModelPermission(ModelResourcePermission<GuestbookEntry> modelResourcePermission) {\n\t\t\n\t\t_guestbookEntryModelResourcePermission = modelResourcePermission;\n\t}\n\t\n\tprivate static ModelResourcePermission<GuestbookEntry>_guestbookEntryModelResourcePermission;\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/security/permission/resource/GuestbookModelPermission.java",
    "content": "package com.liferay.docs.guestbook.web.internal.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n\n@Component(immediate = true)\npublic class GuestbookModelPermission {\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, Guestbook guestbook, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookModelResourcePermission.contains(permissionChecker, guestbook, actionId);\n\t}\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, long guestbookId, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookModelResourcePermission.contains(permissionChecker, guestbookId, actionId);\n\t}\n\t\n\t@Reference(\n\t\t\ttarget = \"(model.class.name=com.liferay.docs.guestbook.model.Guestbook)\", \n\t\t\tunbind = \"-\")\n\tprotected void setEntryModelPermission(ModelResourcePermission<Guestbook> modelResourcePermission) {\n\t\t\n\t\t_guestbookModelResourcePermission = modelResourcePermission;\n\t}\n\t\n\tprivate static ModelResourcePermission<Guestbook>_guestbookModelResourcePermission;\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/security/permission/resource/GuestbookPermission.java",
    "content": "package com.liferay.docs.guestbook.web.internal.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\n\n@Component(immediate=true)\npublic class GuestbookPermission {\n\n\tpublic static boolean contains(PermissionChecker permissionChecker, long groupId, String actionId) {\n\t\t\n\t\treturn _portletResourcePermission.contains(permissionChecker, groupId, actionId);\n\t\t\n\t}\n\t\n\t@Reference(\n\t\t\ttarget=\"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\", \n\t\t\tunbind=\"-\"\n\t\t\t)\n\tprotected void setPortletResourcePermission(PortletResourcePermission portletResourcePermission) {\n\t\t\n\t\t_portletResourcePermission = portletResourcePermission;\n\t}\n\t\n\tprivate static PortletResourcePermission _portletResourcePermission;\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resource-actions/default.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action  \nMapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n\n<resource-action-mapping>\n\n        <portlet-resource>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n            <permissions>\n                <supports>\n                    <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n                    <action-key>CONFIGURATION</action-key>\n                    <action-key>VIEW</action-key>\n                </supports>\n                <site-member-defaults>\n                    <action-key>VIEW</action-key>\n                </site-member-defaults>\n                <guest-defaults>\n                    <action-key>VIEW</action-key>\n                </guest-defaults>\n                <guest-unsupported>\n                    <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n                    <action-key>CONFIGURATION</action-key>\n                </guest-unsupported>\n            </permissions>\n        </portlet-resource>\n\n    <portlet-resource>\n        <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n        <permissions>\n            <supports>\n                <action-key>ADD_TO_PAGE</action-key>\n                <action-key>CONFIGURATION</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported />\n        </permissions>\n    </portlet-resource>\n\n</resource-action-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/css/main.scss",
    "content": ".guestbook-web {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/edit_entry.jsp",
    "content": "<%@include file=\"../init.jsp\" %>\n\n<% \n\nlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\nGuestbookEntry entry = null;\nif (entryId > 0) {\n  entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n}\n\nlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n%>\n\n<portlet:renderURL var=\"viewURL\">\n\t<portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\"></portlet:param>\n</portlet:renderURL>\n\n<portlet:actionURL name=\"addEntry\" var=\"addEntryURL\"></portlet:actionURL>\n\n<aui:form action=\"<%= addEntryURL %>\" name=\"<portlet:namespace />fm\">\n\n<aui:model-context bean=\"<%= entry %>\" model=\"<%= GuestbookEntry.class %>\" />\n\n\t<aui:fieldset>\n\n\t\t<aui:input name=\"name\" />\n\t\t<aui:input name=\"email\" />\n\t\t<aui:input name=\"message\" />\n\t\t<aui:input name=\"entryId\" type=\"hidden\" />\n\t\t<aui:input name=\"guestbookId\" type=\"hidden\" value='<%= entry == null ? guestbookId : entry.getGuestbookId() %>'/>\n\n\t</aui:fieldset>\n\n\t<aui:button-row>\n\n\t\t<aui:button type=\"submit\"></aui:button>\n\t\t<aui:button type=\"cancel\" onClick=\"<%= viewURL.toString() %>\"></aui:button>\n\n\t</aui:button-row>\n</aui:form>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/entry_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n\t<%\n\tString mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n\tResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);\n\n\tGuestbookEntry entry = (GuestbookEntry)row.getObject(); \n\t%>\n\n\t<liferay-ui:icon-menu>\n\n\t\t<c:if\n\t\t\ttest=\"<%= GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.UPDATE) %>\">\n\t\t\t<portlet:renderURL var=\"editURL\">\n\t\t\t\t<portlet:param name=\"entryId\"\n\t\t\t\t\tvalue=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n\t\t\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n\t\t\t</portlet:renderURL>\n\n\t\t\t<liferay-ui:icon image=\"edit\" message=\"Edit\"\n\t\t\t\turl=\"<%=editURL.toString() %>\" />\n\t\t</c:if>\n\n\t\t<c:if\n\t\ttest=\"<%=GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.PERMISSIONS) %>\">\n\n\t\t\t<liferay-security:permissionsURL\n\t\t\t\tmodelResource=\"<%= GuestbookEntry.class.getName() %>\"\n\t\t\t\tmodelResourceDescription=\"<%= entry.getMessage() %>\"\n\t\t\t\tresourcePrimKey=\"<%= String.valueOf(entry.getEntryId()) %>\"\n\t\t\t\tvar=\"permissionsURL\" />\n\t\t\n\t\t\t<liferay-ui:icon image=\"permissions\" url=\"<%= permissionsURL %>\" />\n\n\t\t</c:if>\n\n\t\t<c:if\n\t\t\ttest=\"<%=GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.DELETE) %>\">\n\n\t\t\t<portlet:actionURL name=\"deleteEntry\" var=\"deleteURL\">\n\t\t\t\t<portlet:param name=\"entryId\"\n\t\t\t\t\tvalue=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n\t\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\t\tvalue=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n\t\t\t</portlet:actionURL>\n\n\t\t\t<liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\t\t</c:if>\n\n\t</liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<liferay-ui:success key=\"entryAdded\" message=\"entry-added\" />\n<liferay-ui:success key=\"entryDeleted\" message=\"entry-deleted\" />\n\n<%\nlong guestbookId = Long.valueOf((Long) renderRequest\n\t\t.getAttribute(\"guestbookId\"));\n%>\n\n<portlet:renderURL var=\"searchURL\">\n\t<portlet:param name=\"mvcPath\" \n\tvalue=\"/guestbook/view_search.jsp\" />\n</portlet:renderURL>\n\n<aui:form action=\"<%=searchURL.toString() %>\" name=\"fm\">\n\n\t<div class=\"row\">\n\t\t<div class=\"col-md-8\">\n\t\t\t<aui:input inlineLabel=\"left\" label=\"\" name=\"keywords\" placeholder=\"search-entries\" size=\"256\" />\n\t\t</div>\n\n\t\t<div class=\"col-md-4\">\n\t\t\t<aui:button type=\"submit\" value=\"search\" />\n\t\t</div>\n\t</div>\n\n</aui:form>\n\n<aui:nav cssClass=\"nav-tabs\">\n\n\t<%\n\t\tList<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n\t\t\tfor (int i = 0; i < guestbooks.size(); i++) {\n\n\t\t\t\tGuestbook curGuestbook = guestbooks.get(i);\n\t\t\t\tString cssClass = StringPool.BLANK;\n\n\t\t\t\tif (curGuestbook.getGuestbookId() == guestbookId) {\n\t\t\t\t\tcssClass = \"active\";\n\t\t\t\t}\n\n\t\t\t\tif (GuestbookModelPermission.contains(\n\t\t\t\t\tpermissionChecker, curGuestbook.getGuestbookId(), \"VIEW\")) {\n\t\t\t\t\t\t\t\t\t\t\n\t%>\n\n\t<portlet:renderURL var=\"viewPageURL\">\n\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\" />\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(curGuestbook.getGuestbookId())%>\" />\n\t</portlet:renderURL>\n\n\t\t\n\t<aui:nav-item cssClass=\"<%=cssClass%>\" href=\"<%=viewPageURL%>\"\n\t\tlabel=\"<%=HtmlUtil.escape(curGuestbook.getName())%>\" />\n\n\t<%  \n\t\t\t\t}\n\t\t\t\n\t\t\t}\n\t%>\n\n</aui:nav>\n\n<aui:button-row cssClass=\"guestbook-buttons\">\n\n    <c:if test='<%= GuestbookPermission.contains(permissionChecker, scopeGroupId, \"ADD_ENTRY\") %>'>\n\t\t<portlet:renderURL var=\"addEntryURL\">\n\t\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\tvalue=\"<%=String.valueOf(guestbookId)%>\" />\n\t\t</portlet:renderURL>\n\n\t\t<aui:button onClick=\"<%=addEntryURL.toString()%>\" value=\"Add Entry\"></aui:button>\n\t\t\n\t</c:if>\n\n</aui:button-row>\n\n<liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntriesCount()%>\">\n<liferay-ui:search-container-results\n\tresults=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(scopeGroupId.longValue(),\n\t\t\t\t\tguestbookId, searchContainer.getStart(),\n\t\t\t\t\tsearchContainer.getEnd())%>\" />\n\n<liferay-ui:search-container-row\n\tclassName=\"com.liferay.docs.guestbook.model.GuestbookEntry\" modelVar=\"entry\">\n\n\t<liferay-ui:search-container-column-text property=\"message\" />\n\n\t<liferay-ui:search-container-column-text property=\"name\" />\n\n\t<liferay-ui:search-container-column-jsp\n\t\talign=\"right\" \n\t\tpath=\"/guestbook/entry_actions.jsp\" />\n</liferay-ui:search-container-row>\n\n<liferay-ui:search-iterator />\n\n</liferay-ui:search-container>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/view_search.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\n  String keywords = ParamUtil.getString(request, \"keywords\");\n  long guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n%>\n<portlet:renderURL var=\"searchURL\">\n\t<portlet:param name=\"mvcPath\" \n\tvalue=\"/guestbook/view_search.jsp\" />\n</portlet:renderURL>\n\n<portlet:renderURL var=\"viewURL\">\n\t<portlet:param \n\t\tname=\"mvcPath\" \n\t\tvalue=\"/guestbook/view.jsp\" \n\t/>\n</portlet:renderURL>\n\n<aui:form action=\"${searchURL}\" name=\"fm\">\n\n\t<liferay-ui:header backURL=\"${viewURL}\" title=\"back\" />\n\n\t<div class=\"row\">\n\t\t<div class=\"col-md-8\">\n\t\t\t<aui:input inlineLabel=\"left\" label=\"\" name=\"keywords\" placeholder=\"search-entries\" size=\"256\" />\n\t\t</div>\n\n\t\t<div class=\"col-md-4\">\n\t\t\t<aui:button type=\"submit\" value=\"search\" />\n\t\t</div>\n\t</div>\n</aui:form>\n\n<%\n\tSearchContext searchContext = SearchContextFactory.getInstance(request);\n\n\tsearchContext.setKeywords(keywords);\n\tsearchContext.setAttribute(\"paginationType\", \"more\");\n\tsearchContext.setStart(0);\n\tsearchContext.setEnd(10);\n\n    Indexer<GuestbookEntry> indexer = IndexerRegistryUtil.getIndexer(GuestbookEntry.class);\n\n    Hits hits = indexer.search(searchContext);\n\n    List<GuestbookEntry> entries = new ArrayList<GuestbookEntry>();\n\n\tfor (int i = 0; i < hits.getDocs().length; i++) {\n\t\t\tDocument doc = hits.doc(i);\n\n\t\t\tlong entryId = GetterUtil\n\t\t\t.getLong(doc.get(Field.ENTRY_CLASS_PK));\n\n\t\t\tGuestbookEntry entry = null;\n\n\t\t\ttry {\n\t\t\t\t\tentry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n\t\t\t} catch (PortalException pe) {\n\t\t\t\t\t_log.error(pe.getLocalizedMessage());\n\t\t\t} catch (SystemException se) {\n\t\t\t\t\t_log.error(se.getLocalizedMessage());\n\t\t\t}\n\n\t\t\tentries.add(entry);\n\t}\n\n\tList<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n\tMap<String, String> guestbookMap = new HashMap<String, String>();\n\n\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tguestbookMap.put(Long.toString(guestbook.getGuestbookId()), guestbook.getName());\n\t}\n%>\n\n<liferay-ui:search-container delta=\"10\" \n\temptyResultsMessage=\"no-entries-were-found\" \n\ttotal=\"<%= entries.size() %>\">\n\t\t<liferay-ui:search-container-results\n\t\t\t\tresults=\"<%= entries %>\"\n/>\n\n\t<liferay-ui:search-container-row\n\t\t\tclassName=\"com.liferay.docs.guestbook.model.GuestbookEntry\"\n\t\t\tkeyProperty=\"entryId\" modelVar=\"entry\" escapedModel=\"<%=true%>\">\n\n        <liferay-ui:search-container-column-text name=\"guestbook\"\n            value=\"<%=guestbookMap.get(Long.toString(entry.getGuestbookId()))%>\" />\n\n        <liferay-ui:search-container-column-text property=\"message\" />\n\n        <liferay-ui:search-container-column-text property=\"name\" />\n                \n        <liferay-ui:search-container-column-jsp\n            path=\"/guestbook/entry_actions.jsp\"\n            align=\"right\" />\n   </liferay-ui:search-container-row>\n   \n   <liferay-ui:search-iterator />\n\n</liferay-ui:search-container>\n\n<%!\n\tprivate static Log _log = LogFactoryUtil.getLog(\"html.guestbook.view_search_jsp\");\n%>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/edit_guestbook.jsp",
    "content": "<%@include file = \"../init.jsp\" %>\n\n<%\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n        \n        Guestbook guestbook = null;\n\n        if (guestbookId > 0) {\n                guestbook = GuestbookLocalServiceUtil.getGuestbook(guestbookId);\n        }\n%>\n\n<portlet:renderURL var=\"viewURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook_admin/view.jsp\" />\n</portlet:renderURL>\n\n<portlet:actionURL name='<%= guestbook == null ? \"addGuestbook\" : \"updateGuestbook\" %>' var=\"editGuestbookURL\" />\n\n<aui:form action=\"<%= editGuestbookURL %>\" name=\"fm\">\n\n        <aui:model-context bean=\"<%= guestbook %>\" model=\"<%= Guestbook.class %>\" />\n\n        <aui:input type=\"hidden\" name=\"guestbookId\"\n            value='<%= guestbook == null ? \"\" : guestbook.getGuestbookId() %>' />\n\n        <aui:fieldset>\n             <aui:input name=\"name\" />\n        </aui:fieldset>\n\n        <aui:button-row>\n             <aui:button type=\"submit\" />\n             <aui:button onClick=\"<%= viewURL %>\" type=\"cancel\"  />\n        </aui:button-row>\n</aui:form>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/guestbook_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\n\tString mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n\tResultRow row = (ResultRow) request\n\t\t\t\t\t.getAttribute(\"SEARCH_CONTAINER_RESULT_ROW\");\n\n\tGuestbook guestbook = (Guestbook) row.getObject();\n%>\n\n<liferay-ui:icon-menu>\n\n\t<portlet:renderURL var=\"editURL\">\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(guestbook.getGuestbookId()) %>\" />\n\t\t<portlet:param name=\"mvcPath\"\n\t\t\tvalue=\"/guestbook_admin/edit_guestbook.jsp\" />\n\t</portlet:renderURL>\n\n\t<liferay-ui:icon image=\"edit\" message=\"Edit\"\n\t\t\turl=\"<%=editURL.toString() %>\" />\n\n\t<portlet:actionURL name=\"deleteGuestbook\" var=\"deleteURL\">\n\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\tvalue=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\" />\n\t</portlet:actionURL>\n\n\t<liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n\t<c:if\n\t\ttest=\"<%=GuestbookModelPermission.contains(permissionChecker, guestbook.getGuestbookId(), ActionKeys.PERMISSIONS) %>\">\n\n\t\t\t<liferay-security:permissionsURL\n\t\t\t\tmodelResource=\"<%= Guestbook.class.getName() %>\"\n\t\t\t\tmodelResourceDescription=\"<%= guestbook.getName() %>\"\n\t\t\t\tresourcePrimKey=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\"\n\t\t\t\tvar=\"permissionsURL\" />\n\t\t\n\t\t\t<liferay-ui:icon image=\"permissions\" url=\"<%= permissionsURL %>\" />\n\n\t</c:if>\n</liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<liferay-ui:success key=\"guestbookAdded\" message=\"guestbook-added\" />\n<liferay-ui:success key=\"guestbookUpdated\" message=\"guestbook-updated\" />\n<liferay-ui:success key=\"guestbookDeleted\" message=\"guestbook-deleted\" />\n\n<liferay-ui:search-container\n\ttotal=\"<%= GuestbookLocalServiceUtil.getGuestbooksCount(scopeGroupId) %>\">\n\t<liferay-ui:search-container-results\n\t\tresults=\"<%= GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId, \n\t\t\tsearchContainer.getStart(), searchContainer.getEnd()) %>\" />\n\n\t<liferay-ui:search-container-row\n\t\tclassName=\"com.liferay.docs.guestbook.model.Guestbook\" modelVar=\"guestbook\">\n\n\t\t<liferay-ui:search-container-column-text property=\"name\" />\n\t\t\t\t\n\t\t<liferay-ui:search-container-column-jsp\n\t\t\talign=\"right\" \n\t\t\tpath=\"/guestbook_admin/guestbook_actions.jsp\" />\n\t\t\n\t</liferay-ui:search-container-row>\n\n\t<liferay-ui:search-iterator />\n</liferay-ui:search-container>\n\n<aui:button-row cssClass=\"guestbook-admin-buttons\">\n\t<portlet:renderURL var=\"addGuestbookURL\">\n\t\t<portlet:param name=\"mvcPath\"\n\t\t\tvalue=\"/guestbook_admin/edit_guestbook.jsp\" />\n\t\t<portlet:param name=\"redirect\" value=\"<%= \"currentURL\" %>\" />\n\t</portlet:renderURL>\n\t\t\n\t<aui:button onClick=\"<%= addGuestbookURL.toString() %>\"\n\t\tvalue=\"Add Guestbook\" />\n</aui:button-row>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/init.jsp",
    "content": "<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\"%>\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\"%>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n<%@ taglib uri=\"http://liferay.com/tld/security\" prefix=\"liferay-security\" %>\n\n<%@ page import=\"java.util.List\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.ParamUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.HtmlUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.petra.string.StringPool\" %>\n<%@ page import=\"com.liferay.portal.kernel.model.PersistedModel\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.SearchEntry\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.ResultRow\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.Guestbook\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.GuestbookEntry\" %> \n<%@ page import=\"com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookModelPermission\" %>\n<%@ page import=\"com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookPermission\" %>\n<%@ page import=\"com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookEntryPermission\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.portal.kernel.security.permission.ActionKeys\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.SearchContainer\" %>\n<%@ page import=\"com.liferay.portal.kernel.exception.PortalException\" %>\n<%@ page import=\"com.liferay.portal.kernel.exception.SystemException\" %>\n<%@ page import=\"com.liferay.portal.kernel.language.LanguageUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.log.Log\" %>\n<%@ page import=\"com.liferay.portal.kernel.log.LogFactoryUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Indexer\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.IndexerRegistryUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.SearchContext\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.SearchContextFactory\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Hits\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Document\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Field\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.GetterUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.Validator\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.PortalUtil\" %>\n\n<%@ page import=\"java.util.ArrayList\" %>\n<%@ page import=\"java.util.Map\" %>\n<%@ page import=\"java.util.HashMap\" %>\n\n<%@ page import=\"javax.portlet.PortletURL\" %>\n\n<liferay-theme:defineObjects />\n\n<portlet:defineObjects />"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/content/Language.properties",
    "content": "entry-added=Entry added successfully.\nentry-deleted=Entry deleted successfully.\nguestbook-added=Guestbook added successfully.\nguestbook-updated=Guestbook updated successfully.\nguestbook-deleted=Guestbook deleted successfully."
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/portlet.properties",
    "content": "resource.actions.configs=META-INF/resource-actions/default.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/08-search/com-liferay-docs-guestbook/settings.gradle",
    "content": "buildscript {\n\tdependencies {\n\t\tclasspath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.workspace\", version: \"2.0.4\"\n\t\tclasspath group: \"net.saliman\", name: \"gradle-properties-plugin\", version: \"1.4.6\"\n\t}\n\n\trepositories {\n\t\tmaven {\n\t\t\turl \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t\t}\n\t}\n}\n\napply plugin: \"net.saliman.properties\"\n\napply plugin: \"com.liferay.workspace\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/.blade.properties",
    "content": "#Wed Aug 14 16:06:16 EDT 2019\nliferay.version.default=7.2\nprofile.name=gradle\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/.gitignore",
    "content": "**/*.iml\n**/.ivy\n**/.classpath\n**/.project\n**/.sass-cache\n**/.settings\n**/bin\n**/build\n**/build_gradle\n**/node_modules\n**/test-coverage\n**/tmp\n**/.web_bundle_build\n.gradle\n.idea\n/bundles\n/gradle-*.properties\n/plugins-sdk/**/classes\n/plugins-sdk/**/ivy.xml.MD5\n/plugins-sdk/**/liferay-hook.xml.processed\n/plugins-sdk/build.*.properties\n/plugins-sdk/dependencies/**/*.jar\n/plugins-sdk/dist\n/plugins-sdk/lib\ntest-results"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/configs/common/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/configs/dev/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/configs/docker/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/configs/local/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/configs/prod/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "operationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/configs/prod/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/configs/uat/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "logExceptionsOnly=B\"false\"\noperationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/configs/uat/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.2-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/gradle.properties",
    "content": "\n    # Set the directory where the downloaded bundle Zip files are stored. The\n    # default value is the \".liferay/bundles\" folder inside the user home\n    # directory.\n    #\n    #liferay.workspace.bundle.cache.dir=~/.liferay/bundles\n\n    #\n    # Set this to true if the \"liferay.workspace.bundle.url\" property is set to\n    # a DXP bundle Zip. This property allows the token residing in the\n    # \"~/.liferay\" folder to be used to validate your user credentials when\n    # downloading the bundle. The default value is false.\n    #\n    #liferay.workspace.bundle.token.download=false\n\n    #\n    # Set the email address to use when downloading a DXP bundle. This is used\n    # to create the authentication token. The email address must match the one\n    # registered for your DXP subscription.\n    #\n    # If you wish to create a new token without providing your email address and\n    # password in this file, you can create a token manually by navigating to\n    # your Liferay profile's Account Setting page and generating a token in the\n    # Authentication Tokens menu. Your token must reside in the \"~/.liferay\"\n    # folder.\n    #\n    #liferay.workspace.bundle.token.email.address=\n\n    #\n    # Set this to true to override the existing token with a newly generated\n    # token created by the \"createToken\" task. The default value is false.\n    #\n    #liferay.workspace.bundle.token.force=false\n\n    #\n    # Set the password to use when downloading a DXP bundle. This is used to\n    # create the authentication token. The password must match the one\n    # registered for your DXP subscription. See the\n    # \"liferay.workspace.bundle.token.email.address\" property for more details.\n    #\n    #liferay.workspace.bundle.token.password=\n\n    #\n    # Set the file to hold the Liferay bundle authentication token password.\n    # The default file value is \"~/.liferay/token\".\n    #\n    #liferay.workspace.bundle.token.password.file=\n\n    #\n    # Set the URL pointing to the bundle Zip to download. If the URL points to a\n    # DXP bundle (e.g., https://api.liferay.com/...), set the\n    # \"liferay.workspace.bundle.token.download\" property to true. The default\n    # value is the URL for Liferay Portal CE 7.0 GA7, Liferay Portal CE 7.1 GA4,\n    # or Liferay Portal CE 7.2 GA1, depending on the portal version the\n    # workspace is targeting.\n    #\nliferay.workspace.bundle.url = https://releases-cdn.liferay.com/portal/7.2.0-ga1/liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761.tar.gz\n\n    #\n    # Set the \"app.server.tomcat.version\" to match what is contained inside the\n    # Liferay bundle. Both the TestIntegrationPlugin and and LiferayExtPlugin\n    # rely on this version to match the bundled Tomcat version. If your\n    # configured bundle url points to a bundle with a different Tomcat version,\n    # set the property below to match that Tomcat version.\n    #\napp.server.tomcat.version = 9.0.17\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository in the\n    # root project. The default value is true.\n    #\n    #liferay.workspace.default.repository.enabled=true\n\n    #\n    # Set the Liferay Portal Docker image to create your container from. The\n    # default value points to Liferay Portal CE 7.2 GA1.\n    #\n    #liferay.workspace.docker.image.liferay=liferay/portal:7.2.0-ga1\n\n    #\n    # Set the environment with the settings appropriate for current development.\n    # The \"configs\" folder is used to hold different environments in the same\n    # workspace. You can organize environment settings and generate an\n    # environment installation with those settings. There are five\n    # environments: common, dev, docker, local, prod, and uat. The default value\n    # is \"local\".\n    #\n    #liferay.workspace.environment=local\n\n    #\n    # Set the folder that contains the Liferay bundle downloaded from the\n    # \"liferay.workspace.bundle.url\" property. The default value is \"bundles\".\n    #\n    #liferay.workspace.home.dir=bundles\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository for\n    # module/OSGi projects. The default value is true.\n    #\n    #liferay.workspace.modules.default.repository.enabled=true\n\n    #\n    # Set the folder that contains all Ext OSGi modules and Ext plugins. The\n    # default value is \"ext\".\n    #\n    #liferay.workspace.ext.dir=ext\n\n    #\n    # Set the folder that contains all module/OSGi projects. The default value\n    # is \"modules\".\n    #\n    #liferay.workspace.modules.dir=modules\n\n    #\n    # Set this to true to compile the JSP files in OSGi modules and have them\n    # added to the distributable Zip/Tar. The default value is false.\n    #\n    #liferay.workspace.modules.jsp.precompile.enabled=false\n\n    #\n    # Set the folder that contains the Plugins SDK environment. The default\n    # value is \"plugins-sdk\".\n    #\n    #liferay.workspace.plugins.sdk.dir=plugins-sdk\n\n    #\n    # Set the Liferay Portal or DXP version to develop and test against. By\n    # setting this property, it enables the target platform features such as\n    # dependency management and OSGi resolve tasks. Use the version that\n    # matches the Liferay Portal or DXP bundle version in this workspace.\n    #\n    # For a list of all available target platform versions, see\n    # https://bit.ly/2IkAwwW for Liferay Portal and https://bit.ly/2GIyfZF for\n    # Liferay DXP.\n    #\n    #liferay.workspace.target.platform.version=7.2.0\n\n    #\n    # Set this to true if you have enabled the Target Platform plugin (i.e. you\n    # have set the above property) and you want to apply the TargetPlatformIDE\n    # plugin to the root workspace project. This will cause all of the BOM\n    # artifacts jars and their Java sources to be indexed by your IDE. Setting\n    # this property to true can slow down your IDE's project synchronization.\n    #\n    target.platform.index.sources=true\n\n    #\n    # Set the folder that contains Node.js-style theme projects. The default\n    # value is \"themes\".\n    #\n    #liferay.workspace.themes.dir=themes\n\n    #\n    # Set this to true to build the theme projects using the Liferay Portal\n    # Tools Theme Builder. The default value is false.\n    #\n    #liferay.workspace.themes.java.build=false\n\n    #\n    # Set the folder that contains classic WAR-style projects. The default value\n    # is \"wars\".\n    #\n    #liferay.workspace.wars.dir=wars\n\n\n    #\n    # Set the subscription key for Microsoft Translation integration.\n    # Subscription to the Translator Text Translation API on Microsoft Cognitive\n    # Services is required. Basic subscriptions, up to 2 million characters a\n    # month, are free. See\n    # http://docs.microsofttranslator.com/text-translate.html for more\n    # information.\n    #\nmicrosoft.translator.subscription.key = \nliferay.workspace.target.platform.version = 7.2.0\ntarget.platform.index.sources = false\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/.gitignore",
    "content": ".gradle/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/bnd.bnd",
    "content": "Bundle-Name: guestbook-api\nBundle-SymbolicName: com.liferay.docs.guestbook.api\nBundle-Version: 1.0.0\nExport-Package:\\\n\tcom.liferay.docs.guestbook.exception,\\\n\tcom.liferay.docs.guestbook.model,\\\n\tcom.liferay.docs.guestbook.service,\\\n\tcom.liferay.docs.guestbook.service.persistence\n-check: EXPORTS\n-includeresource: META-INF/service.xml=../guestbook-service/service.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/constants/GuestbookConstants.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.constants;\n\npublic class GuestbookConstants {\n\t\n\tpublic static final String RESOURCE_NAME = \"com.liferay.docs.guestbook\";\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/constants/GuestbookPortletKeys.java",
    "content": "package com.liferay.docs.guestbook.constants;\n\n/**\n * @author sezovr\n */\npublic class GuestbookPortletKeys {\n\n\tpublic static final String GUESTBOOK =\n\t\t\"com_liferay_docs_guestbook_portlet_GuestbookPortlet\";\n\n\tpublic static final String GUESTBOOK_ADMIN =\n\t\t  \"com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet\";\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryEmailException extends PortalException {\n\n\tpublic EntryEmailException() {\n\t}\n\n\tpublic EntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryMessageException extends PortalException {\n\n\tpublic EntryMessageException() {\n\t}\n\n\tpublic EntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryNameException extends PortalException {\n\n\tpublic EntryNameException() {\n\t}\n\n\tpublic EntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryEmailException extends PortalException {\n\n\tpublic GuestbookEntryEmailException() {\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryMessageException extends PortalException {\n\n\tpublic GuestbookEntryMessageException() {\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryNameException extends PortalException {\n\n\tpublic GuestbookEntryNameException() {\n\t}\n\n\tpublic GuestbookEntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookNameException extends PortalException {\n\n\tpublic GuestbookNameException() {\n\t}\n\n\tpublic GuestbookNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookEntryException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookEntryException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookEntryException() {\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookException() {\n\t}\n\n\tpublic NoSuchGuestbookException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/Guestbook.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookModel\n * @generated\n */\n@ImplementationClassName(\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\")\n@ProviderType\npublic interface Guestbook extends GuestbookModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<Guestbook, Long> GUESTBOOK_ID_ACCESSOR =\n\t\tnew Accessor<Guestbook, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(Guestbook guestbook) {\n\t\t\t\treturn guestbook.getGuestbookId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Guestbook> getTypeClass() {\n\t\t\t\treturn Guestbook.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntry.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookEntryModel\n * @generated\n */\n@ImplementationClassName(\n\t\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\"\n)\n@ProviderType\npublic interface GuestbookEntry extends GuestbookEntryModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<GuestbookEntry, Long> ENTRY_ID_ACCESSOR =\n\t\tnew Accessor<GuestbookEntry, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(GuestbookEntry guestbookEntry) {\n\t\t\t\treturn guestbookEntry.getEntryId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<GuestbookEntry> getTypeClass() {\n\t\t\t\treturn GuestbookEntry.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryModel\n\textends BaseModel<GuestbookEntry>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook entry model instance should use the {@link GuestbookEntry} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\tpublic long getEntryId();\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\tpublic void setEntryId(long entryId);\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getEmail();\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\tpublic void setEmail(String email);\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getMessage();\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\tpublic void setMessage(String message);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntrySoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookEntryServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntrySoap implements Serializable {\n\n\tpublic static GuestbookEntrySoap toSoapModel(GuestbookEntry model) {\n\t\tGuestbookEntrySoap soapModel = new GuestbookEntrySoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setEntryId(model.getEntryId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setEmail(model.getEmail());\n\t\tsoapModel.setMessage(model.getMessage());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(GuestbookEntry[] models) {\n\t\tGuestbookEntrySoap[] soapModels = new GuestbookEntrySoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[][] toSoapModels(\n\t\tGuestbookEntry[][] models) {\n\n\t\tGuestbookEntrySoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels =\n\t\t\t\tnew GuestbookEntrySoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookEntrySoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(\n\t\tList<GuestbookEntry> models) {\n\n\t\tList<GuestbookEntrySoap> soapModels = new ArrayList<GuestbookEntrySoap>(\n\t\t\tmodels.size());\n\n\t\tfor (GuestbookEntry model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookEntrySoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookEntrySoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetEntryId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic String getEmail() {\n\t\treturn _email;\n\t}\n\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn _message;\n\t}\n\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link GuestbookEntry}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryWrapper\n\textends BaseModelWrapper<GuestbookEntry>\n\timplements GuestbookEntry, ModelWrapper<GuestbookEntry> {\n\n\tpublic GuestbookEntryWrapper(GuestbookEntry guestbookEntry) {\n\t\tsuper(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"entryId\", getEntryId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"email\", getEmail());\n\t\tattributes.put(\"message\", getMessage());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong entryId = (Long)attributes.get(\"entryId\");\n\n\t\tif (entryId != null) {\n\t\t\tsetEntryId(entryId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tString email = (String)attributes.get(\"email\");\n\n\t\tif (email != null) {\n\t\t\tsetEmail(email);\n\t\t}\n\n\t\tString message = (String)attributes.get(\"message\");\n\n\t\tif (message != null) {\n\t\t\tsetMessage(message);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@Override\n\tpublic String getEmail() {\n\t\treturn model.getEmail();\n\t}\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn model.getEntryId();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@Override\n\tpublic String getMessage() {\n\t\treturn model.getMessage();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEmail(String email) {\n\t\tmodel.setEmail(email);\n\t}\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\tmodel.setEntryId(entryId);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\t@Override\n\tpublic void setMessage(String message) {\n\t\tmodel.setMessage(message);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookEntryWrapper wrap(GuestbookEntry guestbookEntry) {\n\t\treturn new GuestbookEntryWrapper(guestbookEntry);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic interface GuestbookModel\n\textends BaseModel<Guestbook>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook model instance should use the {@link Guestbook} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookSoap implements Serializable {\n\n\tpublic static GuestbookSoap toSoapModel(Guestbook model) {\n\t\tGuestbookSoap soapModel = new GuestbookSoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(Guestbook[] models) {\n\t\tGuestbookSoap[] soapModels = new GuestbookSoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[][] toSoapModels(Guestbook[][] models) {\n\t\tGuestbookSoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels = new GuestbookSoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookSoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(List<Guestbook> models) {\n\t\tList<GuestbookSoap> soapModels = new ArrayList<GuestbookSoap>(\n\t\t\tmodels.size());\n\n\t\tfor (Guestbook model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookSoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookSoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetGuestbookId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link Guestbook}.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic class GuestbookWrapper\n\textends BaseModelWrapper<Guestbook>\n\timplements Guestbook, ModelWrapper<Guestbook> {\n\n\tpublic GuestbookWrapper(Guestbook guestbook) {\n\t\tsuper(guestbook);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookWrapper wrap(Guestbook guestbook) {\n\t\treturn new GuestbookWrapper(guestbook);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for GuestbookEntry. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryLocalServiceUtil} to access the guestbook entry local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId);\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException;\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId);\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows SystemException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> obc);\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\taddGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().addGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\taddGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\t\tString message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tcreateGuestbookEntry(long entryId) {\n\n\t\treturn getService().createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(\n\t\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntry(long entryId) {\n\n\t\treturn getService().fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tint start, int end) {\n\n\t\treturn getService().getGuestbookEntries(start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId) {\n\n\t\treturn getService().getGuestbookEntries(groupId, guestbookId);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tString uuid, long companyId, int start, int end,\n\t\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int getGuestbookEntriesCount() {\n\t\treturn getService().getGuestbookEntriesCount();\n\t}\n\n\tpublic static int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn getService().getGuestbookEntriesCount(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tupdateGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().updateGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tupdateGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\t\tString email, String message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\tpublic static GuestbookEntryLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryLocalService.class);\n\n\t\tServiceTracker<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryLocalService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryLocalService}.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceWrapper\n\timplements GuestbookEntryLocalService,\n\t\t\t   ServiceWrapper<GuestbookEntryLocalService> {\n\n\tpublic GuestbookEntryLocalServiceWrapper(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry createGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookEntryLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry fetchGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookEntryLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(int start, int end) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(long groupId, long guestbookId) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry getGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntryLocalService getWrappedService() {\n\t\treturn _guestbookEntryLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for GuestbookEntry. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryServiceUtil} to access the guestbook entry remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookEntryService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookEntryService.class);\n\n\t\tServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryService, GuestbookEntryService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookEntryService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryService}.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceWrapper\n\timplements GuestbookEntryService, ServiceWrapper<GuestbookEntryService> {\n\n\tpublic GuestbookEntryServiceWrapper(\n\t\tGuestbookEntryService guestbookEntryService) {\n\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookEntryService getWrappedService() {\n\t\treturn _guestbookEntryService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookEntryService guestbookEntryService) {\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\tprivate GuestbookEntryService _guestbookEntryService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for Guestbook. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookLocalServiceUtil} to access the guestbook local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(Guestbook guestbook);\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(\n\t\t\tlong userId, String name, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId);\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook);\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException;\n\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(\n\t\t\tlong guestbookId, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbook(long guestbookId);\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException;\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(\n\t\tlong groupId, int start, int end, OrderByComparator<Guestbook> obc);\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(Guestbook guestbook);\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().addGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbook(userId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbook(guestbookId);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().deleteGuestbook(guestbookId, serviceContext);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn getService().getGuestbooks(start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn getService().getGuestbooks(groupId);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int getGuestbooksCount() {\n\t\treturn getService().getGuestbooksCount();\n\t}\n\n\tpublic static int getGuestbooksCount(long groupId) {\n\t\treturn getService().getGuestbooksCount(groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().updateGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbook(\n\t\t\tuserId, guestbookId, name, serviceContext);\n\t}\n\n\tpublic static GuestbookLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookLocalService.class);\n\n\t\tServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookLocalService, GuestbookLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookLocalService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookLocalService}.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceWrapper\n\timplements GuestbookLocalService, ServiceWrapper<GuestbookLocalService> {\n\n\tpublic GuestbookLocalServiceWrapper(\n\t\tGuestbookLocalService guestbookLocalService) {\n\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.addGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.addGuestbook(\n\t\t\tuserId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbookId);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(\n\t\t\tguestbookId, serviceContext);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn _guestbookLocalService.getGuestbooksCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbooksCount(long groupId) {\n\t\treturn _guestbookLocalService.getGuestbooksCount(groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.updateGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.updateGuestbook(\n\t\t\tuserId, guestbookId, name, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookLocalService getWrappedService() {\n\t\treturn _guestbookLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookLocalService guestbookLocalService) {\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for Guestbook. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookServiceUtil} to access the guestbook remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookService, GuestbookService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookService.class);\n\n\t\tServiceTracker<GuestbookService, GuestbookService> serviceTracker =\n\t\t\tnew ServiceTracker<GuestbookService, GuestbookService>(\n\t\t\t\tbundle.getBundleContext(), GuestbookService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookService}.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceWrapper\n\timplements GuestbookService, ServiceWrapper<GuestbookService> {\n\n\tpublic GuestbookServiceWrapper(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookService getWrappedService() {\n\t\treturn _guestbookService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\tprivate GuestbookService _guestbookService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryPersistence\n\textends BasePersistence<GuestbookEntry> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryUtil} to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId);\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] filterFindByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic void removeByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\tpublic int filterCountByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Returns all the guestbook entries where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByStatus(int status);\n\n\t/**\n\t * Returns a range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByStatus_First(\n\t\t\tint status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByStatus_First(\n\t\tint status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByStatus_Last(\n\t\t\tint status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByStatus_Last(\n\t\tint status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByStatus_PrevAndNext(\n\t\t\tlong entryId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where status = &#63; from the database.\n\t *\n\t * @param status the status\n\t */\n\tpublic void removeByStatus(int status);\n\n\t/**\n\t * Returns the number of guestbook entries where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByStatus(int status);\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_S(long groupId, int status);\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_S(\n\t\tlong groupId, int status, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_S_First(\n\t\t\tlong groupId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_S_First(\n\t\tlong groupId, int status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_S_Last(\n\t\t\tlong groupId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_S_Last(\n\t\tlong groupId, int status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByG_S_PrevAndNext(\n\t\t\tlong entryId, long groupId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_S(\n\t\tlong groupId, int status);\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] filterFindByG_S_PrevAndNext(\n\t\t\tlong entryId, long groupId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and status = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t */\n\tpublic void removeByG_S(long groupId, int status);\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByG_S(long groupId, int status);\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\tpublic int filterCountByG_S(long groupId, int status);\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic void cacheResult(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic void cacheResult(java.util.List<GuestbookEntry> guestbookEntries);\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic GuestbookEntry create(long entryId);\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId);\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll();\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook entry service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookEntryPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().clearCache(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, GuestbookEntry> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static GuestbookEntry update(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().update(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static GuestbookEntry update(\n\t\tGuestbookEntry guestbookEntry, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbookEntry, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tentryId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic static GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tentryId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator,\n\t\t\tretrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_PrevAndNext(\n\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn getPersistence().filterFindByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn getPersistence().filterFindByG_G(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().filterFindByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] filterFindByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().filterFindByG_G_PrevAndNext(\n\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic static void removeByG_G(long groupId, long guestbookId) {\n\t\tgetPersistence().removeByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByG_G(long groupId, long guestbookId) {\n\t\treturn getPersistence().countByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static int filterCountByG_G(long groupId, long guestbookId) {\n\t\treturn getPersistence().filterCountByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByStatus(int status) {\n\t\treturn getPersistence().findByStatus(status);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end) {\n\n\t\treturn getPersistence().findByStatus(status, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByStatus(\n\t\t\tstatus, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByStatus(\n\t\t\tstatus, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByStatus_First(\n\t\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByStatus_First(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByStatus_First(\n\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByStatus_First(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByStatus_Last(\n\t\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByStatus_Last(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByStatus_Last(\n\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByStatus_Last(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByStatus_PrevAndNext(\n\t\t\tlong entryId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByStatus_PrevAndNext(\n\t\t\tentryId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where status = &#63; from the database.\n\t *\n\t * @param status the status\n\t */\n\tpublic static void removeByStatus(int status) {\n\t\tgetPersistence().removeByStatus(status);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByStatus(int status) {\n\t\treturn getPersistence().countByStatus(status);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_S(long groupId, int status) {\n\t\treturn getPersistence().findByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_S(\n\t\tlong groupId, int status, int start, int end) {\n\n\t\treturn getPersistence().findByG_S(groupId, status, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByG_S(\n\t\t\tgroupId, status, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByG_S(\n\t\t\tgroupId, status, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_S_First(\n\t\t\tlong groupId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_S_First(\n\t\t\tgroupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_S_First(\n\t\tlong groupId, int status,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_S_First(\n\t\t\tgroupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_S_Last(\n\t\t\tlong groupId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_S_Last(\n\t\t\tgroupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_S_Last(\n\t\tlong groupId, int status,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_S_Last(\n\t\t\tgroupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByG_S_PrevAndNext(\n\t\t\tlong entryId, long groupId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_S_PrevAndNext(\n\t\t\tentryId, groupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_S(\n\t\tlong groupId, int status) {\n\n\t\treturn getPersistence().filterFindByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end) {\n\n\t\treturn getPersistence().filterFindByG_S(groupId, status, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().filterFindByG_S(\n\t\t\tgroupId, status, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] filterFindByG_S_PrevAndNext(\n\t\t\tlong entryId, long groupId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().filterFindByG_S_PrevAndNext(\n\t\t\tentryId, groupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and status = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t */\n\tpublic static void removeByG_S(long groupId, int status) {\n\t\tgetPersistence().removeByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByG_S(long groupId, int status) {\n\t\treturn getPersistence().countByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static int filterCountByG_S(long groupId, int status) {\n\t\treturn getPersistence().filterCountByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic static void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().cacheResult(guestbookEntry);\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic static void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tgetPersistence().cacheResult(guestbookEntries);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static GuestbookEntry create(long entryId) {\n\t\treturn getPersistence().create(entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry remove(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().remove(entryId);\n\t}\n\n\tpublic static GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().updateImpl(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn getPersistence().fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookEntryPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence> _serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryPersistence.class);\n\n\t\tServiceTracker<GuestbookEntryPersistence, GuestbookEntryPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryPersistence.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookPersistence extends BasePersistence<Guestbook> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookUtil} to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(long groupId);\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByGroupId(long groupId);\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] filterFindByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic void removeByGroupId(long groupId);\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByGroupId(long groupId);\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\tpublic int filterCountByGroupId(long groupId);\n\n\t/**\n\t * Returns all the guestbooks where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByStatus(int status);\n\n\t/**\n\t * Returns a range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByStatus(\n\t\tint status, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByStatus(\n\t\tint status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByStatus(\n\t\tint status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByStatus_First(\n\t\t\tint status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByStatus_First(\n\t\tint status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByStatus_Last(\n\t\t\tint status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByStatus_Last(\n\t\tint status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByStatus_PrevAndNext(\n\t\t\tlong guestbookId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where status = &#63; from the database.\n\t *\n\t * @param status the status\n\t */\n\tpublic void removeByStatus(int status);\n\n\t/**\n\t * Returns the number of guestbooks where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByStatus(int status);\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByG_S(long groupId, int status);\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByG_S_First(\n\t\t\tlong groupId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByG_S_First(\n\t\tlong groupId, int status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByG_S_Last(\n\t\t\tlong groupId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByG_S_Last(\n\t\tlong groupId, int status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByG_S_PrevAndNext(\n\t\t\tlong guestbookId, long groupId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByG_S(long groupId, int status);\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] filterFindByG_S_PrevAndNext(\n\t\t\tlong guestbookId, long groupId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; and status = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t */\n\tpublic void removeByG_S(long groupId, int status);\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByG_S(long groupId, int status);\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\tpublic int filterCountByG_S(long groupId, int status);\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic void cacheResult(Guestbook guestbook);\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic void cacheResult(java.util.List<Guestbook> guestbooks);\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic Guestbook create(long guestbookId);\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException;\n\n\tpublic Guestbook updateImpl(Guestbook guestbook);\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId);\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll();\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(Guestbook guestbook) {\n\t\tgetPersistence().clearCache(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, Guestbook> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static Guestbook update(Guestbook guestbook) {\n\t\treturn getPersistence().update(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static Guestbook update(\n\t\tGuestbook guestbook, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbook, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tguestbookId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic static Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tguestbookId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(long groupId) {\n\t\treturn getPersistence().findByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn getPersistence().findByGroupId(groupId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_First(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_First(\n\t\t\tgroupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_PrevAndNext(\n\t\t\tguestbookId, groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByGroupId(long groupId) {\n\t\treturn getPersistence().filterFindByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn getPersistence().filterFindByGroupId(groupId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().filterFindByGroupId(\n\t\t\tgroupId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] filterFindByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().filterFindByGroupId_PrevAndNext(\n\t\t\tguestbookId, groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic static void removeByGroupId(long groupId) {\n\t\tgetPersistence().removeByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByGroupId(long groupId) {\n\t\treturn getPersistence().countByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\tpublic static int filterCountByGroupId(long groupId) {\n\t\treturn getPersistence().filterCountByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByStatus(int status) {\n\t\treturn getPersistence().findByStatus(status);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByStatus(int status, int start, int end) {\n\t\treturn getPersistence().findByStatus(status, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByStatus(\n\t\t\tstatus, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByStatus(\n\t\t\tstatus, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByStatus_First(\n\t\t\tint status, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByStatus_First(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByStatus_First(\n\t\tint status, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByStatus_First(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByStatus_Last(\n\t\t\tint status, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByStatus_Last(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByStatus_Last(\n\t\tint status, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByStatus_Last(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByStatus_PrevAndNext(\n\t\t\tlong guestbookId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByStatus_PrevAndNext(\n\t\t\tguestbookId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where status = &#63; from the database.\n\t *\n\t * @param status the status\n\t */\n\tpublic static void removeByStatus(int status) {\n\t\tgetPersistence().removeByStatus(status);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByStatus(int status) {\n\t\treturn getPersistence().countByStatus(status);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByG_S(long groupId, int status) {\n\t\treturn getPersistence().findByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end) {\n\n\t\treturn getPersistence().findByG_S(groupId, status, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByG_S(\n\t\t\tgroupId, status, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByG_S(\n\t\t\tgroupId, status, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByG_S_First(\n\t\t\tlong groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByG_S_First(\n\t\t\tgroupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByG_S_First(\n\t\tlong groupId, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_S_First(\n\t\t\tgroupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByG_S_Last(\n\t\t\tlong groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByG_S_Last(\n\t\t\tgroupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByG_S_Last(\n\t\tlong groupId, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_S_Last(\n\t\t\tgroupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByG_S_PrevAndNext(\n\t\t\tlong guestbookId, long groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByG_S_PrevAndNext(\n\t\t\tguestbookId, groupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByG_S(long groupId, int status) {\n\t\treturn getPersistence().filterFindByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end) {\n\n\t\treturn getPersistence().filterFindByG_S(groupId, status, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().filterFindByG_S(\n\t\t\tgroupId, status, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] filterFindByG_S_PrevAndNext(\n\t\t\tlong guestbookId, long groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().filterFindByG_S_PrevAndNext(\n\t\t\tguestbookId, groupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; and status = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t */\n\tpublic static void removeByG_S(long groupId, int status) {\n\t\tgetPersistence().removeByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByG_S(long groupId, int status) {\n\t\treturn getPersistence().countByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\tpublic static int filterCountByG_S(long groupId, int status) {\n\t\treturn getPersistence().filterCountByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic static void cacheResult(Guestbook guestbook) {\n\t\tgetPersistence().cacheResult(guestbook);\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic static void cacheResult(List<Guestbook> guestbooks) {\n\t\tgetPersistence().cacheResult(guestbooks);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static Guestbook create(long guestbookId) {\n\t\treturn getPersistence().create(guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook remove(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().remove(guestbookId);\n\t}\n\n\tpublic static Guestbook updateImpl(Guestbook guestbook) {\n\t\treturn getPersistence().updateImpl(guestbook);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn getPersistence().fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic static List<Guestbook> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookPersistence.class);\n\n\t\tServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker<GuestbookPersistence, GuestbookPersistence>(\n\t\t\t\t\tbundle.getBundleContext(), GuestbookPersistence.class,\n\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/bnd.bnd",
    "content": "Bundle-Name: guestbook-service\nBundle-SymbolicName: com.liferay.docs.guestbook.service\nBundle-Version: 1.0.0\nLiferay-Require-SchemaVersion: 1.0.0\nLiferay-Service: true\nExport-Package: com.liferay.docs.guestbook.search\n-dsannotations-options: inherit"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.lang\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.portal.aop.api\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.function\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.model.adapter\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.reflect\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.spi\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.api\"\n\tcompileOnly group: \"javax.portlet\", name: \"portlet-api\"\n    compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\tcompileOnly project(\":modules:guestbook:guestbook-api\")\n}\n\nbuildService {\n\tapiDir = \"../guestbook-api/src/main/java\"\n}\n\ngroup = \"com.liferay.docs.guestbook\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/service.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"ds\" package-path=\"com.liferay.docs.guestbook\">\n\t<author>Liferay</author>\n\t<namespace>GB</namespace>\n\t\n\t<entity name=\"Guestbook\" local-service=\"true\" uuid=\"true\" remote-service=\"true\">\n\t\t\n\t\t<!-- Guestbook fields -->\n\n\t\t<column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\t\t\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<finder name=\"GroupId\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"groupId\" />\n\t\t</finder>\n\n\t\t<finder name=\"Status\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"status\" />\n\t\t</finder>\n\n\t\t<finder name=\"G_S\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"groupId\" />\n\t\t\t<finder-column name=\"status\" />\n\t\t</finder>\n\n\t\t<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetEntry\" />\n\t\t<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetLink\" />\n\t</entity>\n\t\n    <entity name=\"GuestbookEntry\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n\n\t\t<!-- Guestbook Entry fields -->\n\n\t\t<column name=\"entryId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t<column name=\"email\" type=\"String\" />\n\t\t<column name=\"message\" type=\"String\" />\n\t\t<column name=\"guestbookId\" type=\"long\" />\n\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\t   \n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<order>\n\t\t\t<order-column name=\"createDate\" order-by=\"desc\" />\n\t\t</order>\n\n        <finder name=\"G_G\" return-type=\"Collection\">\n            <finder-column name=\"groupId\" />\n            <finder-column name=\"guestbookId\" />\n        </finder>\n\n\t\t<finder name=\"Status\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"status\" />\n\t\t</finder>\n\n\t\t<finder name=\"G_S\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"groupId\" />\n\t\t\t<finder-column name=\"status\" />\n\t\t</finder>\n\n\t\t<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetEntry\" />\n\t\t<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetLink\" />\n\n    </entity>\n\n    <exceptions>\n        <exception>GuestbookEntryEmail</exception>\n        <exception>GuestbookEntryMessage</exception>\n        <exception>GuestbookEntryName</exception>\n        <exception>GuestbookName</exception>\n    </exceptions>\n</service-builder>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/internal/security/permission/resource/GuestbookEntryModelResourcePermissionRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.StagedModelPermissionLogic;\nimport com.liferay.portal.kernel.security.permission.resource.WorkflowedModelPermissionLogic;\nimport com.liferay.portal.kernel.service.GroupLocalService;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\nimport com.liferay.portal.kernel.workflow.permission.WorkflowPermission;\n\n@Component(immediate = true)\npublic class GuestbookEntryModelResourcePermissionRegistrar {\n\n @Activate\n\tpublic void activate(BundleContext bundleContext) {\n\t\tDictionary<String, Object> properties = new HashMapDictionary<>();\n\n\t\tproperties.put(\"model.class.name\", GuestbookEntry.class.getName());\n\n\t\t_serviceRegistration = bundleContext.registerService(\n\t\t\tModelResourcePermission.class,\n\t\t\tModelResourcePermissionFactory.create(\n\t\t\t\tGuestbookEntry.class, GuestbookEntry::getEntryId,\n\t\t\t\t_guestbookEntryLocalService::getGuestbookEntry, _portletResourcePermission,\n\t\t\t\t(modelResourcePermission, consumer) -> {\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew StagedModelPermissionLogic<>(\n\t\t\t\t\t\t\t_stagingPermission, GuestbookPortletKeys.GUESTBOOK,\n\t\t\t\t\t\t\tGuestbookEntry::getEntryId));\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew WorkflowedModelPermissionLogic<>(\n\t\t\t\t\t\t\t\t_workflowPermission, modelResourcePermission,\n\t\t\t\t\t\t\t\t_groupLocalService, GuestbookEntry::getEntryId));\n\t\t\t\t}),\n\t\t\tproperties);\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n\t@Reference(target = \"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\")\n\tprivate PortletResourcePermission _portletResourcePermission;\n\n\tprivate ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n\t@Reference\n\tprivate StagingPermission _stagingPermission;\n\n\t@Reference\n\tprivate WorkflowPermission _workflowPermission;\n\t\n\t@Reference\n\tprivate GroupLocalService _groupLocalService;\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/internal/security/permission/resource/GuestbookModelResourcePermissionRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.StagedModelPermissionLogic;\nimport com.liferay.portal.kernel.security.permission.resource.WorkflowedModelPermissionLogic;\nimport com.liferay.portal.kernel.service.GroupLocalService;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\nimport com.liferay.portal.kernel.workflow.permission.WorkflowPermission;\n\n@Component (immediate=true)\npublic class GuestbookModelResourcePermissionRegistrar {\n\n @Activate\n\tpublic void activate(BundleContext bundleContext) {\n\t\tDictionary<String, Object> properties = new HashMapDictionary<>();\n\n\t\tproperties.put(\"model.class.name\", Guestbook.class.getName());\n\n\t\t_serviceRegistration = bundleContext.registerService(\n\t\t\tModelResourcePermission.class,\n\t\t\tModelResourcePermissionFactory.create(\n\t\t\t\tGuestbook.class, Guestbook::getGuestbookId,\n\t\t\t\t_guestbookLocalService::getGuestbook, _portletResourcePermission,\n\t\t\t\t(modelResourcePermission, consumer) -> {\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew StagedModelPermissionLogic<>(\n\t\t\t\t\t\t\t_stagingPermission, GuestbookPortletKeys.GUESTBOOK,\n\t\t\t\t\t\t\tGuestbook::getGuestbookId));\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew WorkflowedModelPermissionLogic<>(\n\t\t\t\t\t\t\t\t_workflowPermission, modelResourcePermission,\n\t\t\t\t\t\t\t\t_groupLocalService, Guestbook::getGuestbookId));\n\t\t\t\t}),\n\t\t\tproperties);\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n\t@Reference(target = \"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\")\n\tprivate PortletResourcePermission _portletResourcePermission;\n\n\tprivate ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n\t@Reference\n\tprivate StagingPermission _stagingPermission;\n\n\t@Reference\n\tprivate WorkflowPermission _workflowPermission;\n\t\n\t@Reference\n\tprivate GroupLocalService _groupLocalService;\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/internal/security/permission/resource/GuestbookPortletResourcePermissionRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.StagedPortletPermissionLogic;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\n\n@Component (immediate = true)\npublic class GuestbookPortletResourcePermissionRegistrar {\n\t\n\t\t@Activate\n\tpublic void activate(BundleContext bundleContext) {\n\t\tDictionary<String, Object> properties = new HashMapDictionary<>();\n\n\t\tproperties.put(\"resource.name\", GuestbookConstants.RESOURCE_NAME);\n\n\t\t_serviceRegistration = bundleContext.registerService(\n\t\t\tPortletResourcePermission.class,\n\t\t\tPortletResourcePermissionFactory.create(\n\t\t\t\tGuestbookConstants.RESOURCE_NAME,\n\t\t\t\tnew StagedPortletPermissionLogic(\n\t\t\t\t\t_stagingPermission, GuestbookPortletKeys.GUESTBOOK)),\n\t\t\tproperties);\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\tprivate ServiceRegistration<PortletResourcePermission> _serviceRegistration;\n\n\t@Reference\n\tprivate StagingPermission _stagingPermission;\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookBaseImpl\n\textends GuestbookModelImpl implements Guestbook {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookLocalServiceUtil.addGuestbook(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookLocalServiceUtil.updateGuestbook(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing Guestbook in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookCacheModel\n\timplements CacheModel<Guestbook>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookCacheModel guestbookCacheModel = (GuestbookCacheModel)obj;\n\n\t\tif (guestbookId == guestbookCacheModel.guestbookId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, guestbookId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(27);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic Guestbook toEntityModel() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookImpl.setGuestbookId(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tguestbookImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setName(name);\n\t\t}\n\n\t\tguestbookImpl.setGroupId(groupId);\n\t\tguestbookImpl.setCompanyId(companyId);\n\t\tguestbookImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookImpl.setStatus(status);\n\t\tguestbookImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long guestbookId;\n\tpublic String name;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryBaseImpl\n\textends GuestbookEntryModelImpl implements GuestbookEntry {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookEntryLocalServiceUtil.addGuestbookEntry(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookEntryLocalServiceUtil.updateGuestbookEntry(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing GuestbookEntry in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryCacheModel\n\timplements CacheModel<GuestbookEntry>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntryCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\t(GuestbookEntryCacheModel)obj;\n\n\t\tif (entryId == guestbookEntryCacheModel.entryId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, entryId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(33);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", entryId=\");\n\t\tsb.append(entryId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", email=\");\n\t\tsb.append(email);\n\t\tsb.append(\", message=\");\n\t\tsb.append(message);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEntityModel() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookEntryImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookEntryImpl.setEntryId(entryId);\n\n\t\tif (name == null) {\n\t\t\tguestbookEntryImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setName(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tguestbookEntryImpl.setEmail(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setEmail(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tguestbookEntryImpl.setMessage(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setMessage(message);\n\t\t}\n\n\t\tguestbookEntryImpl.setGuestbookId(guestbookId);\n\t\tguestbookEntryImpl.setGroupId(groupId);\n\t\tguestbookEntryImpl.setCompanyId(companyId);\n\t\tguestbookEntryImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookEntryImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookEntryImpl.setStatus(status);\n\t\tguestbookEntryImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookEntryImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tentryId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\t\temail = objectInput.readUTF();\n\t\tmessage = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(entryId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(message);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long entryId;\n\tpublic String name;\n\tpublic String email;\n\tpublic String message;\n\tpublic long guestbookId;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.GuestbookEntry<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryImpl extends GuestbookEntryBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook entry model instance should use the {@link com.liferay.docs.guestbook.model.GuestbookEntry} interface instead.\n\t */\n\tpublic GuestbookEntryImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.GuestbookEntryModel;\nimport com.liferay.docs.guestbook.model.GuestbookEntrySoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.DateUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookEntryModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookEntryModelImpl\n\textends BaseModelImpl<GuestbookEntry> implements GuestbookEntryModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_GuestbookEntry\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"entryId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"email\", Types.VARCHAR},\n\t\t{\"message\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"groupId\", Types.BIGINT}, {\"companyId\", Types.BIGINT},\n\t\t{\"userId\", Types.BIGINT}, {\"userName\", Types.VARCHAR},\n\t\t{\"createDate\", Types.TIMESTAMP}, {\"modifiedDate\", Types.TIMESTAMP},\n\t\t{\"status\", Types.INTEGER}, {\"statusByUserId\", Types.BIGINT},\n\t\t{\"statusByUserName\", Types.VARCHAR}, {\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"entryId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"email\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"message\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_GuestbookEntry (uuid_ VARCHAR(75) null,entryId LONG not null primary key,name VARCHAR(75) null,email VARCHAR(75) null,message VARCHAR(75) null,guestbookId LONG,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_GuestbookEntry\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbookEntry.createDate DESC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_GuestbookEntry.createDate DESC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 4L;\n\n\tpublic static final long STATUS_COLUMN_BITMASK = 8L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 16L;\n\n\tpublic static final long CREATEDATE_COLUMN_BITMASK = 32L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static GuestbookEntry toModel(GuestbookEntrySoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbookEntry model = new GuestbookEntryImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setEntryId(soapModel.getEntryId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setEmail(soapModel.getEmail());\n\t\tmodel.setMessage(soapModel.getMessage());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<GuestbookEntry> toModels(\n\t\tGuestbookEntrySoap[] soapModels) {\n\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> models = new ArrayList<GuestbookEntry>(\n\t\t\tsoapModels.length);\n\n\t\tfor (GuestbookEntrySoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookEntryModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetEntryId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName,\n\t\t\t\tattributeGetterFunction.apply((GuestbookEntry)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<GuestbookEntry, Object>>\n\t\t\tattributeSetterBiConsumers = getAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<GuestbookEntry, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(GuestbookEntry)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<GuestbookEntry, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, GuestbookEntry>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbookEntry.class.getClassLoader(), GuestbookEntry.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<GuestbookEntry> constructor =\n\t\t\t\t(Constructor<GuestbookEntry>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<GuestbookEntry, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<GuestbookEntry, Object>>();\n\t\tMap<String, BiConsumer<GuestbookEntry, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<GuestbookEntry, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", GuestbookEntry::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUuid);\n\t\tattributeGetterFunctions.put(\"entryId\", GuestbookEntry::getEntryId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"entryId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setEntryId);\n\t\tattributeGetterFunctions.put(\"name\", GuestbookEntry::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setName);\n\t\tattributeGetterFunctions.put(\"email\", GuestbookEntry::getEmail);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"email\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setEmail);\n\t\tattributeGetterFunctions.put(\"message\", GuestbookEntry::getMessage);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"message\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setMessage);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"guestbookId\", GuestbookEntry::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"groupId\", GuestbookEntry::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", GuestbookEntry::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", GuestbookEntry::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", GuestbookEntry::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"createDate\", GuestbookEntry::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", GuestbookEntry::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", GuestbookEntry::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\",\n\t\t\t(BiConsumer<GuestbookEntry, Integer>)GuestbookEntry::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", GuestbookEntry::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)\n\t\t\t\tGuestbookEntry::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", GuestbookEntry::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)\n\t\t\t\tGuestbookEntry::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusDate\", GuestbookEntry::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getEmail() {\n\t\tif (_email == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _email;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getMessage() {\n\t\tif (_message == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _message;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_columnBitmask |= GUESTBOOKID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGuestbookId) {\n\t\t\t_setOriginalGuestbookId = true;\n\n\t\t\t_originalGuestbookId = _guestbookId;\n\t\t}\n\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getOriginalGuestbookId() {\n\t\treturn _originalGuestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_columnBitmask = -1L;\n\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_columnBitmask |= STATUS_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalStatus) {\n\t\t\t_setOriginalStatus = true;\n\n\t\t\t_originalStatus = _status;\n\t\t}\n\n\t\t_status = status;\n\t}\n\n\tpublic int getOriginalStatus() {\n\t\treturn _originalStatus;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), GuestbookEntry.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tguestbookEntryImpl.setUuid(getUuid());\n\t\tguestbookEntryImpl.setEntryId(getEntryId());\n\t\tguestbookEntryImpl.setName(getName());\n\t\tguestbookEntryImpl.setEmail(getEmail());\n\t\tguestbookEntryImpl.setMessage(getMessage());\n\t\tguestbookEntryImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookEntryImpl.setGroupId(getGroupId());\n\t\tguestbookEntryImpl.setCompanyId(getCompanyId());\n\t\tguestbookEntryImpl.setUserId(getUserId());\n\t\tguestbookEntryImpl.setUserName(getUserName());\n\t\tguestbookEntryImpl.setCreateDate(getCreateDate());\n\t\tguestbookEntryImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookEntryImpl.setStatus(getStatus());\n\t\tguestbookEntryImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookEntryImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookEntryImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(GuestbookEntry guestbookEntry) {\n\t\tint value = 0;\n\n\t\tvalue = DateUtil.compareTo(\n\t\t\tgetCreateDate(), guestbookEntry.getCreateDate());\n\n\t\tvalue = value * -1;\n\n\t\tif (value != 0) {\n\t\t\treturn value;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntry)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)obj;\n\n\t\tlong primaryKey = guestbookEntry.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl = this;\n\n\t\tguestbookEntryModelImpl._originalUuid = guestbookEntryModelImpl._uuid;\n\n\t\tguestbookEntryModelImpl._originalGuestbookId =\n\t\t\tguestbookEntryModelImpl._guestbookId;\n\n\t\tguestbookEntryModelImpl._setOriginalGuestbookId = false;\n\n\t\tguestbookEntryModelImpl._originalGroupId =\n\t\t\tguestbookEntryModelImpl._groupId;\n\n\t\tguestbookEntryModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookEntryModelImpl._originalCompanyId =\n\t\t\tguestbookEntryModelImpl._companyId;\n\n\t\tguestbookEntryModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookEntryModelImpl._setModifiedDate = false;\n\n\t\tguestbookEntryModelImpl._originalStatus =\n\t\t\tguestbookEntryModelImpl._status;\n\n\t\tguestbookEntryModelImpl._setOriginalStatus = false;\n\n\t\tguestbookEntryModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<GuestbookEntry> toCacheModel() {\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\tnew GuestbookEntryCacheModel();\n\n\t\tguestbookEntryCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookEntryCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.entryId = getEntryId();\n\n\t\tguestbookEntryCacheModel.name = getName();\n\n\t\tString name = guestbookEntryCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.name = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.email = getEmail();\n\n\t\tString email = guestbookEntryCacheModel.email;\n\n\t\tif ((email != null) && (email.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.email = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.message = getMessage();\n\n\t\tString message = guestbookEntryCacheModel.message;\n\n\t\tif ((message != null) && (message.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.message = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookEntryCacheModel.groupId = getGroupId();\n\n\t\tguestbookEntryCacheModel.companyId = getCompanyId();\n\n\t\tguestbookEntryCacheModel.userId = getUserId();\n\n\t\tguestbookEntryCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookEntryCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookEntryCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookEntryCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookEntryCacheModel.status = getStatus();\n\n\t\tguestbookEntryCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookEntryCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookEntryCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookEntryCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookEntryCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, GuestbookEntry>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _originalGuestbookId;\n\tprivate boolean _setOriginalGuestbookId;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate int _originalStatus;\n\tprivate boolean _setOriginalStatus;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate GuestbookEntry _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.Guestbook<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookImpl extends GuestbookBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook model instance should use the {@link com.liferay.docs.guestbook.model.Guestbook} interface instead.\n\t */\n\tpublic GuestbookImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookModel;\nimport com.liferay.docs.guestbook.model.GuestbookSoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookModelImpl\n\textends BaseModelImpl<Guestbook> implements GuestbookModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_Guestbook\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"groupId\", Types.BIGINT},\n\t\t{\"companyId\", Types.BIGINT}, {\"userId\", Types.BIGINT},\n\t\t{\"userName\", Types.VARCHAR}, {\"createDate\", Types.TIMESTAMP},\n\t\t{\"modifiedDate\", Types.TIMESTAMP}, {\"status\", Types.INTEGER},\n\t\t{\"statusByUserId\", Types.BIGINT}, {\"statusByUserName\", Types.VARCHAR},\n\t\t{\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_Guestbook (uuid_ VARCHAR(75) null,guestbookId LONG not null primary key,name VARCHAR(75) null,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_Guestbook\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbook.guestbookId ASC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_Guestbook.guestbookId ASC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long STATUS_COLUMN_BITMASK = 4L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 8L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 16L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static Guestbook toModel(GuestbookSoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbook model = new GuestbookImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<Guestbook> toModels(GuestbookSoap[] soapModels) {\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> models = new ArrayList<Guestbook>(soapModels.length);\n\n\t\tfor (GuestbookSoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetGuestbookId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName, attributeGetterFunction.apply((Guestbook)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<Guestbook, Object>> attributeSetterBiConsumers =\n\t\t\tgetAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<Guestbook, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(Guestbook)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<Guestbook, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<Guestbook, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, Guestbook>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbook.class.getClassLoader(), Guestbook.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<Guestbook> constructor =\n\t\t\t\t(Constructor<Guestbook>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<Guestbook, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<Guestbook, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<Guestbook, Object>>();\n\t\tMap<String, BiConsumer<Guestbook, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<Guestbook, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", Guestbook::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\", (BiConsumer<Guestbook, String>)Guestbook::setUuid);\n\t\tattributeGetterFunctions.put(\"guestbookId\", Guestbook::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"name\", Guestbook::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\", (BiConsumer<Guestbook, String>)Guestbook::setName);\n\t\tattributeGetterFunctions.put(\"groupId\", Guestbook::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\", (BiConsumer<Guestbook, Long>)Guestbook::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", Guestbook::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\", (BiConsumer<Guestbook, Long>)Guestbook::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", Guestbook::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\", (BiConsumer<Guestbook, Long>)Guestbook::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", Guestbook::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\", (BiConsumer<Guestbook, String>)Guestbook::setUserName);\n\t\tattributeGetterFunctions.put(\"createDate\", Guestbook::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", Guestbook::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", Guestbook::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\", (BiConsumer<Guestbook, Integer>)Guestbook::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", Guestbook::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", Guestbook::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<Guestbook, String>)Guestbook::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\"statusDate\", Guestbook::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_columnBitmask |= STATUS_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalStatus) {\n\t\t\t_setOriginalStatus = true;\n\n\t\t\t_originalStatus = _status;\n\t\t}\n\n\t\t_status = status;\n\t}\n\n\tpublic int getOriginalStatus() {\n\t\treturn _originalStatus;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), Guestbook.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic Guestbook toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tguestbookImpl.setUuid(getUuid());\n\t\tguestbookImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookImpl.setName(getName());\n\t\tguestbookImpl.setGroupId(getGroupId());\n\t\tguestbookImpl.setCompanyId(getCompanyId());\n\t\tguestbookImpl.setUserId(getUserId());\n\t\tguestbookImpl.setUserName(getUserName());\n\t\tguestbookImpl.setCreateDate(getCreateDate());\n\t\tguestbookImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookImpl.setStatus(getStatus());\n\t\tguestbookImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(Guestbook guestbook) {\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() < primaryKey) {\n\t\t\treturn -1;\n\t\t}\n\t\telse if (getPrimaryKey() > primaryKey) {\n\t\t\treturn 1;\n\t\t}\n\t\telse {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof Guestbook)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbook guestbook = (Guestbook)obj;\n\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookModelImpl guestbookModelImpl = this;\n\n\t\tguestbookModelImpl._originalUuid = guestbookModelImpl._uuid;\n\n\t\tguestbookModelImpl._originalGroupId = guestbookModelImpl._groupId;\n\n\t\tguestbookModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookModelImpl._originalCompanyId = guestbookModelImpl._companyId;\n\n\t\tguestbookModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookModelImpl._setModifiedDate = false;\n\n\t\tguestbookModelImpl._originalStatus = guestbookModelImpl._status;\n\n\t\tguestbookModelImpl._setOriginalStatus = false;\n\n\t\tguestbookModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<Guestbook> toCacheModel() {\n\t\tGuestbookCacheModel guestbookCacheModel = new GuestbookCacheModel();\n\n\t\tguestbookCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookCacheModel.name = getName();\n\n\t\tString name = guestbookCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookCacheModel.name = null;\n\t\t}\n\n\t\tguestbookCacheModel.groupId = getGroupId();\n\n\t\tguestbookCacheModel.companyId = getCompanyId();\n\n\t\tguestbookCacheModel.userId = getUserId();\n\n\t\tguestbookCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookCacheModel.status = getStatus();\n\n\t\tguestbookCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, Guestbook>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate int _originalStatus;\n\tprivate boolean _setOriginalStatus;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate Guestbook _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryBatchReindexer.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\npublic interface GuestbookEntryBatchReindexer {\n\t\n\tpublic void reindex(long guestbookId, long companyId);\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryBatchReindexerImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.search.batch.BatchIndexingActionable;\nimport com.liferay.portal.search.indexer.IndexerDocumentBuilder;\nimport com.liferay.portal.search.indexer.IndexerWriter;\n\n@Component(immediate = true, service = GuestbookEntryBatchReindexer.class)\npublic class GuestbookEntryBatchReindexerImpl implements GuestbookEntryBatchReindexer {\n\n    @Override\n    public void reindex(long guestbookId, long companyId) {\n        BatchIndexingActionable batchIndexingActionable =\n    indexerWriter.getBatchIndexingActionable();\n\n        batchIndexingActionable.setAddCriteriaMethod(dynamicQuery -> {\n            Property guestbookIdPropery = PropertyFactoryUtil.forName(\n    \"guestbookId\");\n\n            dynamicQuery.add(guestbookIdPropery.eq(guestbookId));\n        });\n\n        batchIndexingActionable.setCompanyId(companyId);\n\n        batchIndexingActionable.setPerformActionMethod((GuestbookEntry entry) -> {\n            Document document = indexerDocumentBuilder.getDocument(entry);\n\n            batchIndexingActionable.addDocuments(document);\n        });\n\n        batchIndexingActionable.performActions();\n        \n    }\n\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n    protected IndexerDocumentBuilder indexerDocumentBuilder;\n\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n    protected IndexerWriter<GuestbookEntry> indexerWriter;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryKeywordQueryContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.portal.kernel.search.BooleanQuery;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.search.SearchContext;\nimport com.liferay.portal.search.query.QueryHelper;\nimport com.liferay.portal.search.spi.model.query.contributor.KeywordQueryContributor;\nimport com.liferay.portal.search.spi.model.query.contributor.helper.KeywordQueryContributorHelper;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = KeywordQueryContributor.class\n)\npublic class GuestbookEntryKeywordQueryContributor implements KeywordQueryContributor {\n\n    @Override\n    public void contribute(\n        String keywords, BooleanQuery booleanQuery,\n        KeywordQueryContributorHelper keywordQueryContributorHelper) {\n\n        SearchContext searchContext =\n    keywordQueryContributorHelper.getSearchContext();\n\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.TITLE, false);\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.CONTENT, false);\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, \"entryEmail\", false);\n    }\n\n    @Reference\n    protected QueryHelper queryHelper;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryModelDocumentContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.util.LocalizationUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelDocumentContributor;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelDocumentContributor.class\n)\npublic class GuestbookEntryModelDocumentContributor\n    implements ModelDocumentContributor<GuestbookEntry> {\n\n    @Override\n    public void contribute(Document document, GuestbookEntry entry) {\n        try {\n            Locale defaultLocale = PortalUtil.getSiteDefaultLocale(\n    entry.getGroupId());\n\n            document.addDate(Field.MODIFIED_DATE, entry.getModifiedDate());\n            document.addText(\"entryEmail\", entry.getEmail());\n\n            String localizedTitle = LocalizationUtil.getLocalizedName(\n    Field.TITLE, defaultLocale.toString());\n            String localizedContent = LocalizationUtil.getLocalizedName(\n    Field.CONTENT, defaultLocale.toString());\n\n            document.addText(localizedTitle, entry.getName());\n            document.addText(localizedContent, entry.getMessage());\n\n            long guestbookId = entry.getGuestbookId();\n\n            Guestbook guestbook = _guestbookLocalService.getGuestbook(\n    guestbookId);\n\n            String guestbookName = guestbook.getName();\n\n            String localizedGbName = LocalizationUtil.getLocalizedName(\n    Field.NAME, defaultLocale.toString());\n\n            document.addText(localizedGbName, guestbookName);\n        } catch (PortalException pe) {\n            if (_log.isWarnEnabled()) {\n                _log.warn(\"Unable to index entry \" + entry.getEntryId(), pe);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    private static final Log _log = LogFactoryUtil.getLog(\n    GuestbookEntryModelDocumentContributor.class);\n\n    @Reference\n    private GuestbookLocalService _guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryModelIndexerWriterContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.search.batch.BatchIndexingActionable;\nimport com.liferay.portal.search.batch.DynamicQueryBatchIndexingActionableFactory;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor;\nimport com.liferay.portal.search.spi.model.index.contributor.helper.ModelIndexerWriterDocumentHelper;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelIndexerWriterContributor.class\n)\npublic class GuestbookEntryModelIndexerWriterContributor\n    implements ModelIndexerWriterContributor<GuestbookEntry> {\n\n    @Override\n    public void customize(\n        BatchIndexingActionable batchIndexingActionable,\n        ModelIndexerWriterDocumentHelper modelIndexerWriterDocumentHelper) {\n\n        batchIndexingActionable.setPerformActionMethod((GuestbookEntry entry) -> {\n            Document document = modelIndexerWriterDocumentHelper.getDocument(\n    entry);\n\n            batchIndexingActionable.addDocuments(document);\n            \n        });\n    }\n\n    @Override\n    public BatchIndexingActionable getBatchIndexingActionable() {\n        return dynamicQueryBatchIndexingActionableFactory.getBatchIndexingActionable(\n    guestbookEntryLocalService.getIndexableActionableDynamicQuery());\n    }\n\n    @Override\n    public long getCompanyId(GuestbookEntry entry) {\n        return entry.getCompanyId();\n    }\n\n    @Reference\n    protected DynamicQueryBatchIndexingActionableFactory\n    dynamicQueryBatchIndexingActionableFactory;\n\n    @Reference\n    protected GuestbookEntryLocalService guestbookEntryLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryModelSummaryContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.petra.string.StringPool;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.search.Summary;\nimport com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelSummaryContributor.class\n)\npublic class GuestbookEntryModelSummaryContributor implements ModelSummaryContributor {\n\n    @Override\n    public Summary getSummary(\n        Document document, Locale locale, String snippet) {\n\n        Summary summary = createSummary(document);\n\n        summary.setMaxContentLength(128);\n\n        return summary;\n    }\n\n    private Summary createSummary(Document document) {\n        String prefix = Field.SNIPPET + StringPool.UNDERLINE;\n\n        String title = document.get(prefix + Field.TITLE, Field.CONTENT);\n        String content = document.get(prefix + Field.CONTENT, Field.CONTENT);\n\n        return new Summary(title, content);\n    }\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntrySearchRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor;\nimport com.liferay.portal.search.spi.model.registrar.ModelSearchRegistrarHelper;\nimport com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor;\n\n@Component(immediate=true)\npublic class GuestbookEntrySearchRegistrar {\n\n\t@Activate\n\tprotected void activate(BundleContext bundleContext) {\n\n\t\t_serviceRegistration = modelSearchRegistrarHelper.register(\n\t\t\tGuestbookEntry.class, bundleContext, modelSearchDefinition -> {\n\t\t\t\tmodelSearchDefinition.setDefaultSelectedFieldNames(\n\t\t\t\t\tField.COMPANY_ID, Field.ENTRY_CLASS_NAME,\n\t\t\t\t\tField.ENTRY_CLASS_PK, Field.UID, \n\t\t\t\t\tField.SCOPE_GROUP_ID, Field.GROUP_ID);\n\n\t\t\t\tmodelSearchDefinition.setDefaultSelectedLocalizedFieldNames(\n\t\t\t\t\tField.TITLE, Field.CONTENT);\n\n\t\t\t\tmodelSearchDefinition.setModelIndexWriteContributor(\n\t\t\t\t\tmodelIndexWriterContributor);\n\t\t\t\tmodelSearchDefinition.setModelSummaryContributor(\n\t\t\t\t\tmodelSummaryContributor);\n\t\t\t\tmodelSearchDefinition.setSelectAllLocales(true);\n\n\t\t\t});\n\t}\n\n\t@Deactivate\n\tprotected void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\t@Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n\tprotected ModelIndexerWriterContributor<GuestbookEntry> modelIndexWriterContributor;\n\n\t@Reference\n\tprotected ModelSearchRegistrarHelper modelSearchRegistrarHelper;\n\n\t@Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n\tprotected ModelSummaryContributor modelSummaryContributor;\n\n\tprivate ServiceRegistration<?> _serviceRegistration;\n\n    }"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookKeywordQueryContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.portal.kernel.search.BooleanQuery;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.search.SearchContext;\nimport com.liferay.portal.search.query.QueryHelper;\nimport com.liferay.portal.search.spi.model.query.contributor.KeywordQueryContributor;\nimport com.liferay.portal.search.spi.model.query.contributor.helper.KeywordQueryContributorHelper;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = KeywordQueryContributor.class\n)\npublic class GuestbookKeywordQueryContributor\n    implements KeywordQueryContributor {\n\n    @Override\n    public void contribute(\n        String keywords, BooleanQuery booleanQuery,\n        KeywordQueryContributorHelper keywordQueryContributorHelper) {\n\n        SearchContext searchContext =\n    keywordQueryContributorHelper.getSearchContext();\n\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.TITLE, false);\n    }\n\n    @Reference\n    protected QueryHelper queryHelper;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookModelDocumentContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.util.LocalizationUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelDocumentContributor;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelDocumentContributor.class\n)\npublic class GuestbookModelDocumentContributor\n    implements ModelDocumentContributor<Guestbook> {\n\n    @Override\n    public void contribute(Document document, Guestbook guestbook) {\n        try {\n            document.addDate(Field.MODIFIED_DATE, guestbook.getModifiedDate());\n\n            Locale defaultLocale = PortalUtil.getSiteDefaultLocale(\n    guestbook.getGroupId());\n\n            String localizedTitle = LocalizationUtil.getLocalizedName(\n    Field.TITLE, defaultLocale.toString());\n\n            document.addText(localizedTitle, guestbook.getName());\n        } catch (PortalException pe) {\n            if (_log.isWarnEnabled()) {\n                _log.warn(\n    \"Unable to index guestbook \" + guestbook.getGuestbookId(), pe);\n            }\n        }\n    }\n\n    private static final Log _log = LogFactoryUtil.getLog(\n    GuestbookModelDocumentContributor.class);\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookModelIndexerWriterContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.search.batch.BatchIndexingActionable;\nimport com.liferay.portal.search.batch.DynamicQueryBatchIndexingActionableFactory;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor;\nimport com.liferay.portal.search.spi.model.index.contributor.helper.ModelIndexerWriterDocumentHelper;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelIndexerWriterContributor.class\n)\npublic class GuestbookModelIndexerWriterContributor\n    implements ModelIndexerWriterContributor<Guestbook> {\n\n    @Override\n    public void customize(\n        BatchIndexingActionable batchIndexingActionable,\n        ModelIndexerWriterDocumentHelper modelIndexerWriterDocumentHelper) {\n\n        batchIndexingActionable.setPerformActionMethod((Guestbook guestbook) -> {\n            Document document = modelIndexerWriterDocumentHelper.getDocument(\n    guestbook);\n\n            batchIndexingActionable.addDocuments(document);\n        });\n    }\n\n    @Override\n    public BatchIndexingActionable getBatchIndexingActionable() {\n        return dynamicQueryBatchIndexingActionableFactory.getBatchIndexingActionable(\n    guestbookLocalService.getIndexableActionableDynamicQuery());\n    }\n\n    @Override\n    public long getCompanyId(Guestbook guestbook) {\n        return guestbook.getCompanyId();\n    }\n\n    @Override\n    public void modelIndexed(Guestbook guestbook) {\n        guestbookEntryBatchReindexer.reindex(\n    guestbook.getGuestbookId(), guestbook.getCompanyId());\n    }\n\n    @Reference\n    protected DynamicQueryBatchIndexingActionableFactory\n    dynamicQueryBatchIndexingActionableFactory;\n\n    @Reference\n    protected GuestbookEntryBatchReindexer guestbookEntryBatchReindexer;\n\n    @Reference\n    protected GuestbookLocalService guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookModelSummaryContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.petra.string.StringPool;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.search.Summary;\nimport com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelSummaryContributor.class\n)\npublic class GuestbookModelSummaryContributor\n    implements ModelSummaryContributor {\n\n    @Override\n    public Summary getSummary(\n        Document document, Locale locale, String snippet) {\n\n        Summary summary = createSummary(document);\n\n        summary.setMaxContentLength(200);\n\n        return summary;\n    }\n\n    private Summary createSummary(Document document) {\n        String prefix = Field.SNIPPET + StringPool.UNDERLINE;\n\n        String title = document.get(prefix + Field.TITLE, Field.TITLE);\n\n        return new Summary(title, StringPool.BLANK);\n    }\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookSearchRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor;\nimport com.liferay.portal.search.spi.model.registrar.ModelSearchRegistrarHelper;\nimport com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor;\n\n@Component(immediate=true)\npublic class GuestbookSearchRegistrar {\n\n        @Activate\n        protected void activate(BundleContext bundleContext) {\n\n            _serviceRegistration = modelSearchRegistrarHelper.register(\n                Guestbook.class, bundleContext, modelSearchDefinition -> {\n                    modelSearchDefinition.setDefaultSelectedFieldNames(\n                        Field.ASSET_TAG_NAMES, Field.COMPANY_ID, Field.CONTENT,\n                        Field.ENTRY_CLASS_NAME, Field.ENTRY_CLASS_PK,\n                        Field.GROUP_ID, Field.MODIFIED_DATE, Field.SCOPE_GROUP_ID,\n                        Field.TITLE, Field.UID);\n\n                    modelSearchDefinition.setModelIndexWriteContributor(\n                        modelIndexWriterContributor);\n                    modelSearchDefinition.setModelSummaryContributor(\n                        modelSummaryContributor);\n                });\n        }\n\n        @Deactivate\n        protected void deactivate() {\n\n            _serviceRegistration.unregister();\n        }\n\n        @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.Guestbook)\")\n        protected ModelIndexerWriterContributor<Guestbook> modelIndexWriterContributor;\n\n        @Reference\n        protected ModelSearchRegistrarHelper modelSearchRegistrarHelper;\n\n        @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.Guestbook)\")\n        protected ModelSummaryContributor modelSummaryContributor;\n\n        private ServiceRegistration<?> _serviceRegistration;\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookEntryLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\tguestbookEntry.setNew(true);\n\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.create(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbookEntry.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId) {\n\n\t\treturn guestbookEntryPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookEntryLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbookEntry.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<GuestbookEntry>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(GuestbookEntry guestbookEntry)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbookEntry);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryLocalService.deleteGuestbookEntry(\n\t\t\t(GuestbookEntry)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end) {\n\t\treturn guestbookEntryPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn guestbookEntryPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryLocalService = (GuestbookEntryLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\tprotected GuestbookEntryLocalService guestbookEntryLocalService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetEntryLocalService\n\t\tassetEntryLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetLinkLocalService\n\t\tassetLinkLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl\n * @generated\n */\npublic abstract class GuestbookEntryServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookEntryService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryService = (GuestbookEntryService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookEntryLocalService\n\t\tguestbookEntryLocalService;\n\n\tprotected GuestbookEntryService guestbookEntryService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetEntryLocalService\n\t\tassetEntryLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetEntryService\n\t\tassetEntryService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetLinkLocalService\n\t\tassetLinkLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook addGuestbook(Guestbook guestbook) {\n\t\tguestbook.setNew(true);\n\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.create(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.remove(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.remove(guestbook);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbook.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic Guestbook fetchGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\t\treturn guestbookPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.findByPrimaryKey(guestbookId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\n\t\t\t\"guestbookId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbook.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<Guestbook>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(Guestbook guestbook)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbook);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookLocalService.deleteGuestbook((Guestbook)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn guestbookPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooks(int start, int end) {\n\t\treturn guestbookPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn guestbookPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook updateGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookLocalService = (GuestbookLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\tprotected GuestbookLocalService guestbookLocalService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetEntryLocalService\n\t\tassetEntryLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetLinkLocalService\n\t\tassetLinkLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl\n * @generated\n */\npublic abstract class GuestbookServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookService = (GuestbookService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookLocalService\n\t\tguestbookLocalService;\n\n\tprotected GuestbookService guestbookService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetEntryLocalService\n\t\tassetEntryLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetEntryService\n\t\tassetEntryService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetLinkLocalService\n\t\tassetLinkLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntry</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.Guestbook</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.asset.kernel.model.AssetEntry;\nimport com.liferay.asset.kernel.model.AssetLinkConstants;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryEmailException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryMessageException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryNameException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.ResourceConstants;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.ContentTypes;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\n\n/**\n * The implementation of the guestbook entry local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\", service = AopService.class)\npublic class GuestbookEntryLocalServiceImpl extends GuestbookEntryLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n\t * via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code\n\t * >.\n\t */\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(long userId, long guestbookId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tlong entryId = counterLocalService.increment();\n\n\t\tGuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\n\t\tentry.setUuid(serviceContext.getUuid());\n\t\tentry.setUserId(userId);\n\t\tentry.setGroupId(groupId);\n\t\tentry.setCompanyId(user.getCompanyId());\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setCreateDate(serviceContext.getCreateDate(now));\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\t\tentry.setGuestbookId(guestbookId);\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Calls to other Liferay frameworks go here\n\n\t\tresourceLocalService.addResources(user.getCompanyId(), groupId, userId,\n\t\t\tGuestbookEntry.class.getName(), entryId, false, true, true);\n\n\t\tAssetEntry assetEntry = assetEntryLocalService.updateEntry(userId,\n                      groupId, entry.getCreateDate(), entry.getModifiedDate(),\n                      GuestbookEntry.class.getName(), entryId, entry.getUuid(), 0,\n                      serviceContext.getAssetCategoryIds(),\n                      serviceContext.getAssetTagNames(), true, true, null, null, null, null,\n                      ContentTypes.TEXT_HTML, entry.getMessage(), null, null, null,\n                      null, 0, 0, null);\n\n\t\tassetLinkLocalService.updateLinks(userId, assetEntry.getEntryId(),\n                      serviceContext.getAssetLinkEntryIds(),\n                      AssetLinkConstants.TYPE_RELATED);\n\n\t\treturn entry;\n\t}\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(long userId, long guestbookId,\n\t\t\tlong entryId, String name, String email, String message,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException {\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tentry.setUserId(userId);\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Integrate with Liferay frameworks here.\n\n        resourceLocalService.updateResources(\n              user.getCompanyId(), serviceContext.getScopeGroupId(), \n              GuestbookEntry.class.getName(), entryId, \n              serviceContext.getModelPermissions());\n\n        AssetEntry assetEntry = assetEntryLocalService.updateEntry(userId,\n                  serviceContext.getScopeGroupId(),\n                  entry.getCreateDate(), entry.getModifiedDate(),\n                  GuestbookEntry.class.getName(), entryId, entry.getUuid(),\n                  0, serviceContext.getAssetCategoryIds(),\n                  serviceContext.getAssetTagNames(), true, true,\n                  entry.getCreateDate(), null, null, null,\n                  ContentTypes.TEXT_HTML, entry.getMessage(), null,\n                  null, null, null, 0, 0,\n                  serviceContext.getAssetPriority());\n\n        assetLinkLocalService.updateLinks(userId, assetEntry.getEntryId(),\n                  serviceContext.getAssetLinkEntryIds(),\n                  AssetLinkConstants.TYPE_RELATED);\n\t\treturn entry;\n\t}\n\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry entry)\n\t\tthrows PortalException {\n\n\t\tguestbookEntryPersistence.remove(entry);\n\n\t\tresourceLocalService.deleteResource(\n\t\t   entry.getCompanyId(), GuestbookEntry.class.getName(),\n\t\t   ResourceConstants.SCOPE_INDIVIDUAL, entry.getEntryId());\n\n\t\tAssetEntry assetEntry = assetEntryLocalService.fetchEntry(\n                          GuestbookEntry.class.getName(), entry.getEntryId());\n\n\t\tassetLinkLocalService.deleteLinks(assetEntry.getEntryId());\n\n\t\tassetEntryLocalService.deleteEntry(assetEntry);\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId) throws PortalException {\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\treturn deleteGuestbookEntry(entry);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end) throws SystemException {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end, OrderByComparator<GuestbookEntry> obc) {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend, obc);\n\t}\n\n\tpublic GuestbookEntry getGuestbookEntry(long entryId) throws PortalException {\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.countByG_G(groupId, guestbookId);\n\t}\n\n\tprotected void validate(String name, String email, String entry)\n\t\tthrows PortalException {\n\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookEntryNameException();\n\t\t}\n\n\t\tif (!Validator.isEmailAddress(email)) {\n\t\t\tthrow new GuestbookEntryEmailException();\n\t\t}\n\n\t\tif (Validator.isNull(entry)) {\n\t\t\tthrow new GuestbookEntryMessageException();\n\t\t}\n\t}\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook entry remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookEntryService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=GuestbookEntry\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookEntryServiceImpl extends GuestbookEntryServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> to access the guestbook entry remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.asset.kernel.model.AssetEntry;\nimport com.liferay.asset.kernel.model.AssetLinkConstants;\nimport com.liferay.docs.guestbook.exception.GuestbookNameException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.base.GuestbookLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.ResourceConstants;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.ContentTypes;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\n\n/**\n * The implementation of the guestbook local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.Guestbook\", service = AopService.class)\npublic class GuestbookLocalServiceImpl extends GuestbookLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code> via\n\t * injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(long userId, String name,\n\t\t\tServiceContext serviceContext) throws PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name);\n\n\t\tlong guestbookId = counterLocalService.increment();\n\n\t\tGuestbook guestbook = guestbookPersistence.create(guestbookId);\n\n\t\tguestbook.setUuid(serviceContext.getUuid());\n\t\tguestbook.setUserId(userId);\n\t\tguestbook.setGroupId(groupId);\n\t\tguestbook.setCompanyId(user.getCompanyId());\n\t\tguestbook.setUserName(user.getFullName());\n\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tguestbook.setName(name);\n\t\tguestbook.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookPersistence.update(guestbook);\n\n\t\tresourceLocalService.addResources(user.getCompanyId(), groupId, userId,\n\t\t\t\tGuestbook.class.getName(), guestbookId, false, true, true);\n\n\t\tAssetEntry assetEntry = assetEntryLocalService.updateEntry(userId,\n                      groupId, guestbook.getCreateDate(),\n                      guestbook.getModifiedDate(), Guestbook.class.getName(),\n                      guestbookId, guestbook.getUuid(), 0,\n                      serviceContext.getAssetCategoryIds(),\n                      serviceContext.getAssetTagNames(), true, true, null, null, null, null,\n                      ContentTypes.TEXT_HTML, guestbook.getName(), null, null, null,\n                      null, 0, 0, null);\n\n\t\tassetLinkLocalService.updateLinks(userId, assetEntry.getEntryId(),\n                      serviceContext.getAssetLinkEntryIds(),\n                      AssetLinkConstants.TYPE_RELATED);\n\t\treturn guestbook;\n\t}\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(long userId, long guestbookId,\n        String name, ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Date now = new Date();\n\n            validate(name);\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            User user = userLocalService.getUser(userId);\n\n            guestbook.setUserId(userId);\n            guestbook.setUserName(user.getFullName());\n            guestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n            guestbook.setName(name);\n            guestbook.setExpandoBridgeAttributes(serviceContext);\n\n            guestbookPersistence.update(guestbook);\n\n            resourceLocalService.updateResources(serviceContext.getCompanyId(),\n                    serviceContext.getScopeGroupId(), \n                    Guestbook.class.getName(), guestbookId,\n                    serviceContext.getModelPermissions());\n\n            AssetEntry assetEntry = assetEntryLocalService.updateEntry(guestbook.getUserId(),\n                      guestbook.getGroupId(), guestbook.getCreateDate(),\n                      guestbook.getModifiedDate(), Guestbook.class.getName(),\n                      guestbookId, guestbook.getUuid(), 0,\n                      serviceContext.getAssetCategoryIds(),\n                      serviceContext.getAssetTagNames(), true, true, guestbook.getCreateDate(), \n                      null, null, null, ContentTypes.TEXT_HTML, guestbook.getName(), null, null, \n                      null, null, 0, 0, serviceContext.getAssetPriority());\n\n            assetLinkLocalService.updateLinks(serviceContext.getUserId(),\n                      assetEntry.getEntryId(), serviceContext.getAssetLinkEntryIds(),\n                      AssetLinkConstants.TYPE_RELATED);\n            return guestbook;\n    }\n\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(long guestbookId,\n                    ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            List<GuestbookEntry> entries = _guestbookEntryLocalService.getGuestbookEntries(\n                            serviceContext.getScopeGroupId(), guestbookId);\n\n            for (GuestbookEntry entry : entries) {\n                    _guestbookEntryLocalService.deleteGuestbookEntry(entry.getEntryId());\n            }\n\n            guestbook = deleteGuestbook(guestbook);\n\n\t\t\tresourceLocalService.deleteResource(serviceContext.getCompanyId(),\n\t\t\t\t\tGuestbook.class.getName(), ResourceConstants.SCOPE_INDIVIDUAL,\n\t\t\t\t\tguestbookId);\n\n\t\t\tAssetEntry assetEntry = assetEntryLocalService.fetchEntry(\n                      Guestbook.class.getName(), guestbookId);\n\n\t\t\tassetLinkLocalService.deleteLinks(assetEntry.getEntryId());\n\n\t\t\tassetEntryLocalService.deleteEntry(assetEntry);\n\t\t\t\n            return guestbook;\n    }\n\n\tpublic List<Guestbook> getGuestbooks(long groupId) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end,\n\t\t\tOrderByComparator<Guestbook> obc) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end, obc);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end);\n\t}\n\n\tpublic int getGuestbooksCount(long groupId) {\n\n\t\treturn guestbookPersistence.countByGroupId(groupId);\n\t}\n\n\tprotected void validate(String name) throws PortalException {\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookNameException();\n\t\t}\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=Guestbook\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookServiceImpl extends GuestbookServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> to access the guestbook remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookEntryPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.SQLQuery;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.security.permission.InlineSQLHelperUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookEntryPersistence.class)\n@ProviderType\npublic class GuestbookEntryPersistenceImpl\n\textends BasePersistenceImpl<GuestbookEntry>\n\timplements GuestbookEntryPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookEntryUtil</code> to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookEntryImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_First(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_Last(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbookEntry.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof GuestbookEntry) {\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbookEntry.getUuid()) ||\n\t\t\t\t(groupId != guestbookEntry.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<GuestbookEntry> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbookEntry guestbookEntry = list.get(0);\n\n\t\t\t\t\tresult = guestbookEntry;\n\n\t\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (GuestbookEntry)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\t@Override\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbookEntry.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\ttrue);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\tfalse);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_C_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tlong companyId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbookEntry.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByG_G;\n\tprivate FinderPath _finderPathWithoutPaginationFindByG_G;\n\tprivate FinderPath _finderPathCountByG_G;\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(long groupId, long guestbookId) {\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn findByG_G(groupId, guestbookId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {groupId, guestbookId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tgroupId, guestbookId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif ((groupId != guestbookEntry.getGroupId()) ||\n\t\t\t\t\t\t(guestbookId != guestbookEntry.getGuestbookId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByG_G(groupId, guestbookId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByG_G_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tlong guestbookId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(guestbookId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn filterFindByG_G(\n\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn filterFindByG_G(groupId, guestbookId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_G(\n\t\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t\t}\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator, true);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_TABLE, orderByComparator, true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookEntryImpl.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookEntryImpl.class);\n\t\t\t}\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(guestbookId);\n\n\t\t\treturn (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\tq, getDialect(), start, end);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] filterFindByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_G_PrevAndNext(\n\t\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t\t}\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = filterGetByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = filterGetByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry filterGetByG_G_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tlong guestbookId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t6 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByFields[i], true));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByFields[i], true));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookEntryImpl.class);\n\t\t}\n\t\telse {\n\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookEntryImpl.class);\n\t\t}\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(guestbookId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\t@Override\n\tpublic void removeByG_G(long groupId, long guestbookId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByG_G(\n\t\t\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByG_G(long groupId, long guestbookId) {\n\t\tFinderPath finderPath = _finderPathCountByG_G;\n\n\t\tObject[] finderArgs = new Object[] {groupId, guestbookId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic int filterCountByG_G(long groupId, long guestbookId) {\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn countByG_G(groupId, guestbookId);\n\t\t}\n\n\t\tStringBundler query = new StringBundler(3);\n\n\t\tquery.append(_FILTER_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tq.addScalar(\n\t\t\t\tCOUNT_COLUMN_NAME, com.liferay.portal.kernel.dao.orm.Type.LONG);\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(guestbookId);\n\n\t\t\tLong count = (Long)q.uniqueResult();\n\n\t\t\treturn count.intValue();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprivate static final String _FINDER_COLUMN_G_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_G_G_GUESTBOOKID_2 =\n\t\t\"guestbookEntry.guestbookId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByStatus;\n\tprivate FinderPath _finderPathWithoutPaginationFindByStatus;\n\tprivate FinderPath _finderPathCountByStatus;\n\n\t/**\n\t * Returns all the guestbook entries where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByStatus(int status) {\n\t\treturn findByStatus(status, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByStatus(int status, int start, int end) {\n\t\treturn findByStatus(status, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByStatus(status, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByStatus;\n\t\t\tfinderArgs = new Object[] {status};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByStatus;\n\t\t\tfinderArgs = new Object[] {status, start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif ((status != guestbookEntry.getStatus())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_STATUS_STATUS_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByStatus_First(\n\t\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByStatus_First(\n\t\t\tstatus, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByStatus_First(\n\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByStatus(\n\t\t\tstatus, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByStatus_Last(\n\t\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByStatus_Last(\n\t\t\tstatus, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByStatus_Last(\n\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByStatus(status);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByStatus(\n\t\t\tstatus, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByStatus_PrevAndNext(\n\t\t\tlong entryId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByStatus_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, status, orderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByStatus_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, status, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByStatus_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, int status,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_STATUS_STATUS_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(status);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where status = &#63; from the database.\n\t *\n\t * @param status the status\n\t */\n\t@Override\n\tpublic void removeByStatus(int status) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByStatus(\n\t\t\t\t\tstatus, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByStatus(int status) {\n\t\tFinderPath finderPath = _finderPathCountByStatus;\n\n\t\tObject[] finderArgs = new Object[] {status};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_STATUS_STATUS_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_STATUS_STATUS_2 =\n\t\t\"guestbookEntry.status = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByG_S;\n\tprivate FinderPath _finderPathWithoutPaginationFindByG_S;\n\tprivate FinderPath _finderPathCountByG_S;\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_S(long groupId, int status) {\n\t\treturn findByG_S(\n\t\t\tgroupId, status, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_S(\n\t\tlong groupId, int status, int start, int end) {\n\n\t\treturn findByG_S(groupId, status, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByG_S(groupId, status, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByG_S;\n\t\t\tfinderArgs = new Object[] {groupId, status};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByG_S;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tgroupId, status, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif ((groupId != guestbookEntry.getGroupId()) ||\n\t\t\t\t\t\t(status != guestbookEntry.getStatus())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_S_First(\n\t\t\tlong groupId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_S_First(\n\t\t\tgroupId, status, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_S_First(\n\t\tlong groupId, int status,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByG_S(\n\t\t\tgroupId, status, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_S_Last(\n\t\t\tlong groupId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_S_Last(\n\t\t\tgroupId, status, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_S_Last(\n\t\tlong groupId, int status,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByG_S(groupId, status);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByG_S(\n\t\t\tgroupId, status, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByG_S_PrevAndNext(\n\t\t\tlong entryId, long groupId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByG_S_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, status, orderByComparator,\n\t\t\t\ttrue);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByG_S_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, status, orderByComparator,\n\t\t\t\tfalse);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByG_S_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(status);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_S(long groupId, int status) {\n\t\treturn filterFindByG_S(\n\t\t\tgroupId, status, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end) {\n\n\t\treturn filterFindByG_S(groupId, status, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_S(groupId, status, start, end, orderByComparator);\n\t\t}\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator, true);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_TABLE, orderByComparator, true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookEntryImpl.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookEntryImpl.class);\n\t\t\t}\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(status);\n\n\t\t\treturn (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\tq, getDialect(), start, end);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] filterFindByG_S_PrevAndNext(\n\t\t\tlong entryId, long groupId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_S_PrevAndNext(\n\t\t\t\tentryId, groupId, status, orderByComparator);\n\t\t}\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = filterGetByG_S_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, status, orderByComparator,\n\t\t\t\ttrue);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = filterGetByG_S_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, status, orderByComparator,\n\t\t\t\tfalse);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry filterGetByG_S_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t6 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByFields[i], true));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByFields[i], true));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookEntryImpl.class);\n\t\t}\n\t\telse {\n\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookEntryImpl.class);\n\t\t}\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(status);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and status = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t */\n\t@Override\n\tpublic void removeByG_S(long groupId, int status) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByG_S(\n\t\t\t\t\tgroupId, status, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByG_S(long groupId, int status) {\n\t\tFinderPath finderPath = _finderPathCountByG_S;\n\n\t\tObject[] finderArgs = new Object[] {groupId, status};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic int filterCountByG_S(long groupId, int status) {\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn countByG_S(groupId, status);\n\t\t}\n\n\t\tStringBundler query = new StringBundler(3);\n\n\t\tquery.append(_FILTER_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tq.addScalar(\n\t\t\t\tCOUNT_COLUMN_NAME, com.liferay.portal.kernel.dao.orm.Type.LONG);\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(status);\n\n\t\t\tLong count = (Long)q.uniqueResult();\n\n\t\t\treturn count.intValue();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprivate static final String _FINDER_COLUMN_G_S_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_G_S_STATUS_2 =\n\t\t\"guestbookEntry.status = ?\";\n\n\tpublic GuestbookEntryPersistenceImpl() {\n\t\tsetModelClass(GuestbookEntry.class);\n\n\t\tsetModelImplClass(GuestbookEntryImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\t@Override\n\tpublic void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {\n\t\t\t\tguestbookEntry.getUuid(), guestbookEntry.getGroupId()\n\t\t\t},\n\t\t\tguestbookEntry);\n\n\t\tguestbookEntry.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\t@Override\n\tpublic void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbook entries.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookEntryImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook entry.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(GuestbookEntry guestbookEntry) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookEntryModelImpl)guestbookEntry, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<GuestbookEntry> guestbookEntries) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache(\n\t\t\t\t(GuestbookEntryModelImpl)guestbookEntry, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookEntryModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic GuestbookEntry create(long entryId) {\n\t\tGuestbookEntry guestbookEntry = new GuestbookEntryImpl();\n\n\t\tguestbookEntry.setNew(true);\n\t\tguestbookEntry.setPrimaryKey(entryId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbookEntry.setUuid(uuid);\n\n\t\tguestbookEntry.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn remove((Serializable)entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\tGuestbookEntryImpl.class, primaryKey);\n\n\t\t\tif (guestbookEntry == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbookEntry);\n\t\t}\n\t\tcatch (NoSuchGuestbookEntryException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected GuestbookEntry removeImpl(GuestbookEntry guestbookEntry) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbookEntry)) {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\t\tGuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbookEntry != null) {\n\t\t\t\tsession.delete(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbookEntry != null) {\n\t\t\tclearCache(guestbookEntry);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t@Override\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\tboolean isNew = guestbookEntry.isNew();\n\n\t\tif (!(guestbookEntry instanceof GuestbookEntryModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbookEntry.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(\n\t\t\t\t\tguestbookEntry);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbookEntry proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom GuestbookEntry implementation \" +\n\t\t\t\t\tguestbookEntry.getClass());\n\t\t}\n\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl =\n\t\t\t(GuestbookEntryModelImpl)guestbookEntry;\n\n\t\tif (Validator.isNull(guestbookEntry.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbookEntry.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbookEntry.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookEntryModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setModifiedDate(\n\t\t\t\t\tserviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbookEntry.isNew()) {\n\t\t\t\tsession.save(guestbookEntry);\n\n\t\t\t\tguestbookEntry.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.merge(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\targs = new Object[] {guestbookEntryModelImpl.getStatus()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByStatus, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByStatus, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\tguestbookEntryModelImpl.getStatus()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByG_S, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByG_S, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByG_G.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByStatus.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalStatus()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByStatus, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByStatus, args);\n\n\t\t\t\targs = new Object[] {guestbookEntryModelImpl.getStatus()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByStatus, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByStatus, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByG_S.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalStatus()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_S, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_S, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getStatus()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_S, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_S, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry, false);\n\n\t\tclearUniqueFindersCache(guestbookEntryModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookEntryModelImpl);\n\n\t\tguestbookEntry.resetOriginalValues();\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn findByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn fetchByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOKENTRY;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (GuestbookEntry guestbookEntry : findAll()) {\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOKENTRY);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"entryId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOKENTRY;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookEntryModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook entry persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookEntryModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookEntryModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.COMPANYID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GUESTBOOKID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByStatus = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByStatus\",\n\t\t\tnew String[] {\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByStatus = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByStatus\",\n\t\t\tnew String[] {Integer.class.getName()},\n\t\t\tGuestbookEntryModelImpl.STATUS_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByStatus = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByStatus\",\n\t\t\tnew String[] {Integer.class.getName()});\n\n\t\t_finderPathWithPaginationFindByG_S = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByG_S\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByG_S = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByG_S\",\n\t\t\tnew String[] {Long.class.getName(), Integer.class.getName()},\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.STATUS_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByG_S = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByG_S\",\n\t\t\tnew String[] {Long.class.getName(), Integer.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookEntryImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.GuestbookEntry\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _FILTER_ENTITY_TABLE_FILTER_PK_COLUMN =\n\t\t\"guestbookEntry.entryId\";\n\n\tprivate static final String _FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT DISTINCT {guestbookEntry.*} FROM GB_GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String\n\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1 =\n\t\t\t\"SELECT {GB_GuestbookEntry.*} FROM (SELECT DISTINCT guestbookEntry.entryId FROM GB_GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String\n\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2 =\n\t\t\t\") TEMP_TABLE INNER JOIN GB_GuestbookEntry ON TEMP_TABLE.entryId = GB_GuestbookEntry.entryId\";\n\n\tprivate static final String _FILTER_SQL_COUNT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT COUNT(DISTINCT guestbookEntry.entryId) AS COUNT_VALUE FROM GB_GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _FILTER_ENTITY_ALIAS = \"guestbookEntry\";\n\n\tprivate static final String _FILTER_ENTITY_TABLE = \"GB_GuestbookEntry\";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbookEntry.\";\n\n\tprivate static final String _ORDER_BY_ENTITY_TABLE = \"GB_GuestbookEntry.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No GuestbookEntry exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No GuestbookEntry exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookEntryPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.impl.GuestbookImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.SQLQuery;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.security.permission.InlineSQLHelperUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookPersistence.class)\n@ProviderType\npublic class GuestbookPersistenceImpl\n\textends BasePersistenceImpl<Guestbook> implements GuestbookPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookUtil</code> to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_First(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_Last(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbook.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbook == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof Guestbook) {\n\t\t\tGuestbook guestbook = (Guestbook)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbook.getUuid()) ||\n\t\t\t\t(groupId != guestbook.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<Guestbook> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbook guestbook = list.get(0);\n\n\t\t\t\t\tresult = guestbook;\n\n\t\t\t\t\tcacheResult(guestbook);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (Guestbook)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbook);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbook.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_C_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbook.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByGroupId;\n\tprivate FinderPath _finderPathWithoutPaginationFindByGroupId;\n\tprivate FinderPath _finderPathCountByGroupId;\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId) {\n\t\treturn findByGroupId(\n\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId, int start, int end) {\n\t\treturn findByGroupId(groupId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByGroupId(groupId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif ((groupId != guestbook.getGroupId())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_First(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByGroupId(groupId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_Last(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByGroupId(groupId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByGroupId(\n\t\t\tgroupId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByGroupId_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByGroupId(long groupId) {\n\t\treturn filterFindByGroupId(\n\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn filterFindByGroupId(groupId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByGroupId(groupId, start, end, orderByComparator);\n\t\t}\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOK_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator, true);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_TABLE, orderByComparator, true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookImpl.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookImpl.class);\n\t\t\t}\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\treturn (List<Guestbook>)QueryUtil.list(q, getDialect(), start, end);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] filterFindByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByGroupId_PrevAndNext(\n\t\t\t\tguestbookId, groupId, orderByComparator);\n\t\t}\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = filterGetByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = filterGetByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook filterGetByGroupId_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOK_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByFields[i], true));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByFields[i], true));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookImpl.class);\n\t\t}\n\t\telse {\n\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookImpl.class);\n\t\t}\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\t@Override\n\tpublic void removeByGroupId(long groupId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByGroupId(\n\t\t\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByGroupId(long groupId) {\n\t\tFinderPath finderPath = _finderPathCountByGroupId;\n\n\t\tObject[] finderArgs = new Object[] {groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic int filterCountByGroupId(long groupId) {\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn countByGroupId(groupId);\n\t\t}\n\n\t\tStringBundler query = new StringBundler(2);\n\n\t\tquery.append(_FILTER_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tq.addScalar(\n\t\t\t\tCOUNT_COLUMN_NAME, com.liferay.portal.kernel.dao.orm.Type.LONG);\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tLong count = (Long)q.uniqueResult();\n\n\t\t\treturn count.intValue();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprivate static final String _FINDER_COLUMN_GROUPID_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByStatus;\n\tprivate FinderPath _finderPathWithoutPaginationFindByStatus;\n\tprivate FinderPath _finderPathCountByStatus;\n\n\t/**\n\t * Returns all the guestbooks where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByStatus(int status) {\n\t\treturn findByStatus(status, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByStatus(int status, int start, int end) {\n\t\treturn findByStatus(status, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByStatus(status, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByStatus;\n\t\t\tfinderArgs = new Object[] {status};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByStatus;\n\t\t\tfinderArgs = new Object[] {status, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif ((status != guestbook.getStatus())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_STATUS_STATUS_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByStatus_First(\n\t\t\tint status, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByStatus_First(status, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByStatus_First(\n\t\tint status, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByStatus(status, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByStatus_Last(\n\t\t\tint status, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByStatus_Last(status, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByStatus_Last(\n\t\tint status, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByStatus(status);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByStatus(\n\t\t\tstatus, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByStatus_PrevAndNext(\n\t\t\tlong guestbookId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByStatus_PrevAndNext(\n\t\t\t\tsession, guestbook, status, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByStatus_PrevAndNext(\n\t\t\t\tsession, guestbook, status, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByStatus_PrevAndNext(\n\t\tSession session, Guestbook guestbook, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_STATUS_STATUS_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(status);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where status = &#63; from the database.\n\t *\n\t * @param status the status\n\t */\n\t@Override\n\tpublic void removeByStatus(int status) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByStatus(\n\t\t\t\t\tstatus, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByStatus(int status) {\n\t\tFinderPath finderPath = _finderPathCountByStatus;\n\n\t\tObject[] finderArgs = new Object[] {status};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_STATUS_STATUS_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_STATUS_STATUS_2 =\n\t\t\"guestbook.status = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByG_S;\n\tprivate FinderPath _finderPathWithoutPaginationFindByG_S;\n\tprivate FinderPath _finderPathCountByG_S;\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByG_S(long groupId, int status) {\n\t\treturn findByG_S(\n\t\t\tgroupId, status, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end) {\n\n\t\treturn findByG_S(groupId, status, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByG_S(groupId, status, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByG_S;\n\t\t\tfinderArgs = new Object[] {groupId, status};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByG_S;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tgroupId, status, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif ((groupId != guestbook.getGroupId()) ||\n\t\t\t\t\t\t(status != guestbook.getStatus())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByG_S_First(\n\t\t\tlong groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByG_S_First(\n\t\t\tgroupId, status, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByG_S_First(\n\t\tlong groupId, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByG_S(\n\t\t\tgroupId, status, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByG_S_Last(\n\t\t\tlong groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByG_S_Last(\n\t\t\tgroupId, status, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByG_S_Last(\n\t\tlong groupId, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByG_S(groupId, status);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByG_S(\n\t\t\tgroupId, status, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByG_S_PrevAndNext(\n\t\t\tlong guestbookId, long groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByG_S_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, status, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByG_S_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, status, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByG_S_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(status);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByG_S(long groupId, int status) {\n\t\treturn filterFindByG_S(\n\t\t\tgroupId, status, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end) {\n\n\t\treturn filterFindByG_S(groupId, status, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_S(groupId, status, start, end, orderByComparator);\n\t\t}\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOK_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator, true);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_TABLE, orderByComparator, true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookImpl.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookImpl.class);\n\t\t\t}\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(status);\n\n\t\t\treturn (List<Guestbook>)QueryUtil.list(q, getDialect(), start, end);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] filterFindByG_S_PrevAndNext(\n\t\t\tlong guestbookId, long groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_S_PrevAndNext(\n\t\t\t\tguestbookId, groupId, status, orderByComparator);\n\t\t}\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = filterGetByG_S_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, status, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = filterGetByG_S_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, status, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook filterGetByG_S_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t6 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOK_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByFields[i], true));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByFields[i], true));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookImpl.class);\n\t\t}\n\t\telse {\n\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookImpl.class);\n\t\t}\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(status);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; and status = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t */\n\t@Override\n\tpublic void removeByG_S(long groupId, int status) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByG_S(\n\t\t\t\t\tgroupId, status, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByG_S(long groupId, int status) {\n\t\tFinderPath finderPath = _finderPathCountByG_S;\n\n\t\tObject[] finderArgs = new Object[] {groupId, status};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic int filterCountByG_S(long groupId, int status) {\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn countByG_S(groupId, status);\n\t\t}\n\n\t\tStringBundler query = new StringBundler(3);\n\n\t\tquery.append(_FILTER_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tq.addScalar(\n\t\t\t\tCOUNT_COLUMN_NAME, com.liferay.portal.kernel.dao.orm.Type.LONG);\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(status);\n\n\t\t\tLong count = (Long)q.uniqueResult();\n\n\t\t\treturn count.intValue();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprivate static final String _FINDER_COLUMN_G_S_GROUPID_2 =\n\t\t\"guestbook.groupId = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_G_S_STATUS_2 =\n\t\t\"guestbook.status = ?\";\n\n\tpublic GuestbookPersistenceImpl() {\n\t\tsetModelClass(Guestbook.class);\n\n\t\tsetModelImplClass(GuestbookImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\t@Override\n\tpublic void cacheResult(Guestbook guestbook) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {guestbook.getUuid(), guestbook.getGroupId()},\n\t\t\tguestbook);\n\n\t\tguestbook.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\t@Override\n\tpublic void cacheResult(List<Guestbook> guestbooks) {\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\t\tguestbook.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbook);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbooks.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(Guestbook guestbook) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<Guestbook> guestbooks) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\tguestbook.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic Guestbook create(long guestbookId) {\n\t\tGuestbook guestbook = new GuestbookImpl();\n\n\t\tguestbook.setNew(true);\n\t\tguestbook.setPrimaryKey(guestbookId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbook.setUuid(uuid);\n\n\t\tguestbook.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException {\n\t\treturn remove((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook guestbook = (Guestbook)session.get(\n\t\t\t\tGuestbookImpl.class, primaryKey);\n\n\t\t\tif (guestbook == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbook);\n\t\t}\n\t\tcatch (NoSuchGuestbookException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected Guestbook removeImpl(Guestbook guestbook) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbook)) {\n\t\t\t\tguestbook = (Guestbook)session.get(\n\t\t\t\t\tGuestbookImpl.class, guestbook.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbook != null) {\n\t\t\t\tsession.delete(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbook != null) {\n\t\t\tclearCache(guestbook);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t@Override\n\tpublic Guestbook updateImpl(Guestbook guestbook) {\n\t\tboolean isNew = guestbook.isNew();\n\n\t\tif (!(guestbook instanceof GuestbookModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbook.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(guestbook);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbook proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom Guestbook implementation \" +\n\t\t\t\t\tguestbook.getClass());\n\t\t}\n\n\t\tGuestbookModelImpl guestbookModelImpl = (GuestbookModelImpl)guestbook;\n\n\t\tif (Validator.isNull(guestbook.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbook.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbook.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbook.isNew()) {\n\t\t\t\tsession.save(guestbook);\n\n\t\t\t\tguestbook.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook = (Guestbook)session.merge(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\targs = new Object[] {guestbookModelImpl.getStatus()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByStatus, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByStatus, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookModelImpl.getGroupId(), guestbookModelImpl.getStatus()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByG_S, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByG_S, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getUuid(),\n\t\t\t\t\tguestbookModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByGroupId.\n\t\t\t\t\t getColumnBitmask()) != 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByStatus.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalStatus()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByStatus, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByStatus, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getStatus()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByStatus, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByStatus, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByG_S.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalGroupId(),\n\t\t\t\t\tguestbookModelImpl.getOriginalStatus()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_S, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_S, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getGroupId(),\n\t\t\t\t\tguestbookModelImpl.getStatus()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_S, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_S, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook, false);\n\n\t\tclearUniqueFindersCache(guestbookModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookModelImpl);\n\n\t\tguestbook.resetOriginalValues();\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbook == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\treturn findByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn fetchByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOK);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOK;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (Guestbook guestbook : findAll()) {\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOK);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"guestbookId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOK;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.COMPANYID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {Long.class.getName()},\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByGroupId\",\n\t\t\tnew String[] {Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByStatus = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByStatus\",\n\t\t\tnew String[] {\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByStatus = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByStatus\",\n\t\t\tnew String[] {Integer.class.getName()},\n\t\t\tGuestbookModelImpl.STATUS_COLUMN_BITMASK);\n\n\t\t_finderPathCountByStatus = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByStatus\",\n\t\t\tnew String[] {Integer.class.getName()});\n\n\t\t_finderPathWithPaginationFindByG_S = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByG_S\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByG_S = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByG_S\",\n\t\t\tnew String[] {Long.class.getName(), Integer.class.getName()},\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.STATUS_COLUMN_BITMASK);\n\n\t\t_finderPathCountByG_S = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByG_S\",\n\t\t\tnew String[] {Long.class.getName(), Integer.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.Guestbook\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK =\n\t\t\"SELECT guestbook FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK_WHERE =\n\t\t\"SELECT guestbook FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK_WHERE =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _FILTER_ENTITY_TABLE_FILTER_PK_COLUMN =\n\t\t\"guestbook.guestbookId\";\n\n\tprivate static final String _FILTER_SQL_SELECT_GUESTBOOK_WHERE =\n\t\t\"SELECT DISTINCT {guestbook.*} FROM GB_Guestbook guestbook WHERE \";\n\n\tprivate static final String\n\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1 =\n\t\t\t\"SELECT {GB_Guestbook.*} FROM (SELECT DISTINCT guestbook.guestbookId FROM GB_Guestbook guestbook WHERE \";\n\n\tprivate static final String\n\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2 =\n\t\t\t\") TEMP_TABLE INNER JOIN GB_Guestbook ON TEMP_TABLE.guestbookId = GB_Guestbook.guestbookId\";\n\n\tprivate static final String _FILTER_SQL_COUNT_GUESTBOOK_WHERE =\n\t\t\"SELECT COUNT(DISTINCT guestbook.guestbookId) AS COUNT_VALUE FROM GB_Guestbook guestbook WHERE \";\n\n\tprivate static final String _FILTER_ENTITY_ALIAS = \"guestbook\";\n\n\tprivate static final String _FILTER_ENTITY_TABLE = \"GB_Guestbook\";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbook.\";\n\n\tprivate static final String _ORDER_BY_ENTITY_TABLE = \"GB_Guestbook.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No Guestbook exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No Guestbook exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/constants/GBPersistenceConstants.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl.constants;\n\nimport com.liferay.petra.string.StringBundler;\n\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.Constants;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * @author Liferay\n * @generated\n */\n@Component(immediate = true, service = {})\npublic class GBPersistenceConstants {\n\n\tpublic static final String BUNDLE_SYMBOLIC_NAME =\n\t\t\"com.liferay.docs.guestbook.service\";\n\n\tpublic static final String ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER =\n\t\t\"(origin.bundle.symbolic.name=\" + BUNDLE_SYMBOLIC_NAME + \")\";\n\n\t@Activate\n\tprotected void activate(BundleContext bundleContext) {\n\t\tBundle bundle = bundleContext.getBundle();\n\n\t\tif (!BUNDLE_SYMBOLIC_NAME.equals(bundle.getSymbolicName())) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\tStringBundler.concat(\n\t\t\t\t\t\"Incorrect \", Constants.BUNDLE_SYMBOLICNAME, \" for bundle \",\n\t\t\t\t\tbundle.getSymbolicName()));\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/module-hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">\n\n<hibernate-mapping auto-import=\"false\" default-lazy=\"false\">\n\t<import class=\"com.liferay.docs.guestbook.model.Guestbook\" />\n\t<import class=\"com.liferay.docs.guestbook.model.GuestbookEntry\" />\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\" table=\"GB_Guestbook\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\" table=\"GB_GuestbookEntry\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"entryId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"email\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"message\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n</hibernate-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/portlet-model-hints.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<model-hints>\n\t<model name=\"com.liferay.docs.guestbook.model.Guestbook\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n\t<model name=\"com.liferay.docs.guestbook.model.GuestbookEntry\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"entryId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"email\" type=\"String\" />\n\t\t<field name=\"message\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n</model-hints>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/resource-actions/default.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action  \nMapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n\n<resource-action-mapping>\n\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n        </portlet-ref>\n        <root>true</root>\n        <permissions>\n            <supports>\n                <action-key>ADD_GUESTBOOK</action-key>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>ADD_ENTRY</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>ADD_GUESTBOOK</action-key>\n                <action-key>ADD_ENTRY</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook.model.Guestbook</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n        </portlet-ref>\n        <permissions>\n            <supports>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>DELETE</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>UPDATE</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>UPDATE</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook.model.GuestbookEntry</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n        </portlet-ref>\n        <permissions>\n            <supports>\n                <action-key>DELETE</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>UPDATE</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>UPDATE</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n\n</resource-action-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/indexes.sql",
    "content": "create index IX_A4D0B52D on GB_Guestbook (groupId, status);\ncreate index IX_B946FEA9 on GB_Guestbook (status);\ncreate index IX_9314A9F7 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_EDD4239 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], groupId);\n\ncreate index IX_E84D72FD on GB_GuestbookEntry (groupId, guestbookId);\ncreate index IX_D8346035 on GB_GuestbookEntry (groupId, status);\ncreate index IX_4BED4AA1 on GB_GuestbookEntry (status);\ncreate index IX_CC265FEF on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_4A541631 on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], groupId);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/sequences.sql",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/tables.sql",
    "content": "create table GB_Guestbook (\n\tuuid_ VARCHAR(75) null,\n\tguestbookId LONG not null primary key,\n\tname VARCHAR(75) null,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);\n\ncreate table GB_GuestbookEntry (\n\tuuid_ VARCHAR(75) null,\n\tentryId LONG not null primary key,\n\tname VARCHAR(75) null,\n\temail VARCHAR(75) null,\n\tmessage VARCHAR(75) null,\n\tguestbookId LONG,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/portlet.properties",
    "content": "resource.actions.configs=META-INF/resource-actions/default.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/service.properties",
    "content": "##\n## Properties Override\n##\n\n    #\n    # Specify where to get the overridden properties. Updates should not be made\n    # on this file but on the overridden version of this file.\n    #\n    include-and-override=service-ext.properties\n\n##\n## Build\n##\n\n    build.namespace=GB\n    build.number=10\n    build.date=1569879064867"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/.gitignore",
    "content": ".gradle/\n.sass-cache/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/bnd.bnd",
    "content": "Bundle-Name: guestbook-web\nBundle-SymbolicName: com.liferay.docs.guestbook.portlet\nBundle-Version: 1.0.0\nExport-Package: com.liferay.docs.guestbook.portlet.constants"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.asset.taglib\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.comment.taglib\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.application.list.api\"\n\tcompileOnly group: \"javax.portlet\", name: \"portlet-api\"\n\tcompileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n\tcompileOnly group: \"jstl\", name: \"jstl\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\n    compileOnly project(\":modules:guestbook:guestbook-api\")\n    compileOnly project(\":modules:guestbook:guestbook-service\")\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/application/list/GuestbookAdminPanelApp.java",
    "content": "package com.liferay.docs.guestbook.application.list;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.application.list.BasePanelApp;\nimport com.liferay.application.list.PanelApp;\nimport com.liferay.application.list.constants.PanelCategoryKeys;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.portal.kernel.model.Portlet;\n\n@Component(\n        immediate = true,\n        property = {\n            \"panel.app.order:Integer=300\",\n            \"panel.category.key=\" + PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT\n        },\n        service = PanelApp.class\n    )\npublic class GuestbookAdminPanelApp extends BasePanelApp {\n\n        @Override\n        public String getPortletId() {\n            return GuestbookPortletKeys.GUESTBOOK_ADMIN;\n        }\n\n        @Override\n        @Reference(\n            target = \"(javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN + \")\",\n            unbind = \"-\"\n        )\n        public void setPortlet(Portlet portlet) {\n            super.setPortlet(portlet);\n        }\n\n    }\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookAdminPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.servlet.SessionErrors;\nimport com.liferay.portal.kernel.servlet.SessionMessages;\nimport com.liferay.portal.kernel.util.ParamUtil;\n@Component(\n        immediate = true,\n        property = {\n                \"com.liferay.portlet.display-category=category.hidden\",\n                \"com.liferay.portlet.scopeable=true\",\n                \"javax.portlet.display-name=Guestbooks\",\n                \"javax.portlet.expiration-cache=0\",\n                \"javax.portlet.init-param.portlet-title-based-navigation=true\",\n                \"javax.portlet.init-param.template-path=/\",\n                \"javax.portlet.init-param.view-template=/guestbook_admin/view.jsp\",\n                \"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN,\n                \"javax.portlet.resource-bundle=content.Language\",\n                \"javax.portlet.security-role-ref=administrator\",\n                \"javax.portlet.supports.mime-type=text/html\",\n                \"com.liferay.portlet.add-default-resource=true\"\n        },\n        service = Portlet.class\n    )\npublic class GuestbookAdminPortlet extends MVCPortlet {\n\npublic void addGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n\n        try {\n            _guestbookLocalService.addGuestbook(\n                serviceContext.getUserId(), name, serviceContext);\n            \n            SessionMessages.add(request, \"guestbookAdded\");\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n            \n            SessionErrors.add(request, pe.getClass().getName());\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n\n    public void updateGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.updateGuestbook(\n                serviceContext.getUserId(), guestbookId, name, serviceContext);\n            \n            SessionMessages.add(request,  \"guestbookUpdated\");\n\n        } catch (PortalException pe) {\n        \n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            SessionErrors.add(request, pe.getClass().getName());\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n    \n    public void deleteGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.deleteGuestbook(guestbookId, serviceContext);\n            \n            SessionMessages.add(request,  \"guestbookDeleted\");\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            SessionErrors.add(request, pe.getClass().getName());\n        }\n    }\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\nimport javax.portlet.PortletException;\nimport javax.portlet.RenderRequest;\nimport javax.portlet.RenderResponse;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.servlet.SessionErrors;\nimport com.liferay.portal.kernel.servlet.SessionMessages;\nimport com.liferay.portal.kernel.util.ParamUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\n/**\n * @author sezovr\n */\n@Component(immediate = true, property = { \"com.liferay.portlet.display-category=category.social\",\n\t\t\"com.liferay.portlet.header-portlet-css=/css/main.css\", \n\t\t\"com.liferay.portlet.instanceable=false\",\n\t\t\"com.liferay.portlet.scopeable=true\", \n\t\t\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK,\n\t\t\"javax.portlet.display-name=Guestbook\", \n\t\t\"javax.portlet.expiration-cache=0\",\n\t\t\"javax.portlet.init-param.template-path=/\", \n\t\t\"javax.portlet.init-param.view-template=/guestbook/view.jsp\",\n\t\t\"javax.portlet.resource-bundle=content.Language\", \n\t\t\"javax.portlet.security-role-ref=power-user,user\",\n\t\t\"javax.portlet.supports.mime-type=text/html\" }, \n\tservice = Portlet.class)\npublic class GuestbookPortlet extends MVCPortlet {\n\n\tpublic void addEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\n\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(GuestbookEntry.class.getName(), request);\n\n\t\tString userName = ParamUtil.getString(request, \"name\");\n\t\tString email = ParamUtil.getString(request, \"email\");\n\t\tString message = ParamUtil.getString(request, \"message\");\n\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\n\t\tif (entryId > 0) {\n\n\t\t\ttry {\n\n\t\t\t\t_guestbookEntryLocalService.updateGuestbookEntry(serviceContext.getUserId(), guestbookId, entryId, userName,\n\t\t\t\t\t\temail, message, serviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(e);\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\n\t\t} else {\n\n\t\t\ttry {\n\t\t\t\t_guestbookEntryLocalService.addGuestbookEntry(serviceContext.getUserId(), guestbookId, userName, email, message,\n\t\t\t\t\t\tserviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t\tSessionMessages.add(request, \"entryAdded\");\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tSessionErrors.add(request, e.getClass().getName());\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic void deleteEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\t\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\t\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\tGuestbookEntry.class.getName(), request);\n\n\t\t\ttry {\n\n\t\t\t\tresponse.setRenderParameter(\n\t\t\t\t\t\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t\t_guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\n\t\t\t\tSessionMessages.add(request, \"entryDeleted\");\n\n\t\t\t}\n\n\t\t\tcatch (Exception e) {\n\t\t\t\tLogger.getLogger(GuestbookPortlet.class.getName()).log(\n\t\t\t\t\tLevel.SEVERE, null, e);\n\n\t\t\tSessionErrors.add(request, e.getClass().getName());\n\t\t\t}\n\t}\n\n\t@Override\n\tpublic void render(RenderRequest renderRequest, RenderResponse renderResponse)\n\t\t\tthrows IOException, PortletException {\n\n\t\t\ttry {\n\t\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\t\tGuestbook.class.getName(), renderRequest);\n\n\t\t\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\t\t\tlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n\t\t\t\tList<Guestbook> guestbooks = _guestbookLocalService.getGuestbooks(\n\t\t\t\t\tgroupId);\n\n\t\t\t\tif (guestbooks.isEmpty()) {\n\t\t\t\t\tGuestbook guestbook = _guestbookLocalService.addGuestbook(\n\t\t\t\t\t\tserviceContext.getUserId(), \"Main\", serviceContext);\n\n\t\t\t\t\tguestbookId = guestbook.getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\tif (guestbookId == 0) {\n\t\t\t\t\tguestbookId = guestbooks.get(0).getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\trenderRequest.setAttribute(\"guestbookId\", guestbookId);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new PortletException(e);\n\t\t\t}\n\n\t\t\tsuper.render(renderRequest, renderResponse);\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/asset/GuestbookAssetRenderer.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.web.internal.asset;\n\nimport java.util.Locale;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.PortletRequest;\nimport javax.portlet.PortletResponse;\nimport javax.portlet.PortletURL;\nimport javax.portlet.WindowState;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport com.liferay.asset.kernel.model.BaseJSPAssetRenderer;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.petra.string.StringUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.LayoutConstants;\nimport com.liferay.portal.kernel.portlet.LiferayPortletRequest;\nimport com.liferay.portal.kernel.portlet.LiferayPortletResponse;\nimport com.liferay.portal.kernel.portlet.PortletURLFactoryUtil;\nimport com.liferay.portal.kernel.security.permission.ActionKeys;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.util.HtmlUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\npublic class GuestbookAssetRenderer extends BaseJSPAssetRenderer<Guestbook>{\n\n    public GuestbookAssetRenderer(Guestbook guestbook, ModelResourcePermission<Guestbook> modelResourcePermission) {\n\n                _guestbook = guestbook;\n                _guestbookModelResourcePermission = modelResourcePermission;\n    }\n    \n    @Override\n    public boolean hasEditPermission(PermissionChecker permissionChecker) \n    {\n        try {\n            return _guestbookModelResourcePermission.contains(\n                permissionChecker, _guestbook, ActionKeys.UPDATE);\n        }\n        catch (Exception e) {\n        }\n\n        return false;\n    }\n\n    @Override\n    public boolean hasViewPermission(PermissionChecker permissionChecker) \n    {\n        try {\n            return _guestbookModelResourcePermission.contains(\n                permissionChecker, _guestbook, ActionKeys.VIEW);\n        }\n        catch (Exception e) {\n        }\n\n        return true;\n    }\n\n    @Override\n    public Guestbook getAssetObject() {\n      return _guestbook;\n    }\n\n    @Override\n    public long getGroupId() {\n      return _guestbook.getGroupId();\n    }\n\n    @Override\n    public long getUserId() {\n\n      return _guestbook.getUserId();\n    }\n\n    @Override\n    public String getUserName() {\n      return _guestbook.getUserName();\n    }\n\n    @Override\n    public String getUuid() {\n      return _guestbook.getUuid();\n    }\n\n    @Override\n    public String getClassName() {\n      return Guestbook.class.getName();\n    }\n\n    @Override\n    public long getClassPK() {\n      return _guestbook.getGuestbookId();\n    }\n\n    @Override\n    public String getSummary(PortletRequest portletRequest, PortletResponse \n      portletResponse) {\n        return \"Name: \" + _guestbook.getName();\n    }\n\n    @Override\n    public String getTitle(Locale locale) {\n      return _guestbook.getName();\n    }\n\n    @Override\n    public boolean include(HttpServletRequest request, HttpServletResponse \n      response, String template) throws Exception {\n        request.setAttribute(\"GUESTBOOK\", _guestbook);\n        request.setAttribute(\"HtmlUtil\", HtmlUtil.getHtml());\n        request.setAttribute(\"StringUtil\", new StringUtil());\n        return super.include(request, response, template);\n    }\n\n    @Override\n    public String getJspPath(HttpServletRequest request, String template) {\n\n        if (template.equals(TEMPLATE_FULL_CONTENT)) {\n          request.setAttribute(\"gb_guestbook\", _guestbook);\n\n          return \"/asset/guestbook/\" + template + \".jsp\";\n        } else {\n          return null;\n        }\n    }\n\n    @Override\n    public PortletURL getURLEdit(LiferayPortletRequest liferayPortletRequest,\n      LiferayPortletResponse liferayPortletResponse) throws Exception {\n\n        PortletURL portletURL = liferayPortletResponse.createLiferayPortletURL(\n            getControlPanelPlid(liferayPortletRequest), GuestbookPortletKeys.GUESTBOOK,\n            PortletRequest.RENDER_PHASE);\n        portletURL.setParameter(\"mvcRenderCommandName\", \"/guestbookwebportlet/edit_guestbook\");\n        portletURL.setParameter(\"guestbookId\", String.valueOf(_guestbook.getGuestbookId()));\n        portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n\n        return portletURL;\n    }\n\n    @Override\n    public String getURLViewInContext(LiferayPortletRequest liferayPortletRequest,\n      LiferayPortletResponse liferayPortletResponse, String noSuchEntryRedirect) throws Exception {\n        try {\n          long plid = PortalUtil.getPlidFromPortletId(_guestbook.getGroupId(),\n              GuestbookPortletKeys.GUESTBOOK);\n\n          PortletURL portletURL;\n          if (plid == LayoutConstants.DEFAULT_PLID) {\n            portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(liferayPortletRequest),\n                GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n          } else {\n            portletURL = PortletURLFactoryUtil.create(liferayPortletRequest,\n                GuestbookPortletKeys.GUESTBOOK, plid, PortletRequest.RENDER_PHASE);\n          }\n\n          portletURL.setParameter(\"mvcRenderCommandName\", \"/guestbookwebportlet/view\");\n          portletURL.setParameter(\"guestbookId\", String.valueOf(_guestbook.getGuestbookId()));\n\n          String currentUrl = PortalUtil.getCurrentURL(liferayPortletRequest);\n\n          portletURL.setParameter(\"redirect\", currentUrl);\n\n          return portletURL.toString();\n\n        } catch (PortalException e) {\n\n            logger.log(Level.SEVERE, e.getMessage());\n\n        } catch (SystemException e) {\n\n            logger.log(Level.SEVERE, e.getMessage());\n\n        }\n\n        return noSuchEntryRedirect;\n    }\n\n    @Override\n    public String getURLView(LiferayPortletResponse liferayPortletResponse, \n    WindowState windowState) throws Exception {\n\n        return super.getURLView(liferayPortletResponse, windowState);\n    }\n\n    private Guestbook _guestbook;\n    private final ModelResourcePermission<Guestbook> _guestbookModelResourcePermission;   \n    private Logger logger = Logger.getLogger(this.getClass().getName());\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/asset/GuestbookAssetRendererFactory.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.web.internal.asset;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.PortletRequest;\nimport javax.portlet.PortletURL;\nimport javax.servlet.ServletContext;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.asset.kernel.model.AssetRenderer;\nimport com.liferay.asset.kernel.model.AssetRendererFactory;\nimport com.liferay.asset.kernel.model.BaseAssetRendererFactory;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.LiferayPortletRequest;\nimport com.liferay.portal.kernel.portlet.LiferayPortletResponse;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.theme.ThemeDisplay;\nimport com.liferay.portal.kernel.util.WebKeys;\n\n\n@Component(immediate = true, \n  property = {\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK}, \n  service = AssetRendererFactory.class\n  )\npublic class GuestbookAssetRendererFactory extends \n  BaseAssetRendererFactory<Guestbook> {\n\n  public GuestbookAssetRendererFactory() {\n\tsetClassName(CLASS_NAME);\n\tsetLinkable(_LINKABLE);\n\tsetPortletId(GuestbookPortletKeys.GUESTBOOK); setSearchable(true);\n\tsetSelectable(true); \n  }         \n\t\n    @Override\n    public AssetRenderer<Guestbook> getAssetRenderer(long classPK, int type) \n    throws PortalException {\n      \n      Guestbook guestbook = _guestbookLocalService.getGuestbook(classPK);\n\n      GuestbookAssetRenderer guestbookAssetRenderer = \n      new GuestbookAssetRenderer(guestbook, _guestbookModelResourcePermission);\n\n      guestbookAssetRenderer.setAssetRendererType(type);\n      guestbookAssetRenderer.setServletContext(_servletContext);\n\n      return guestbookAssetRenderer;\n    }\n\n    @Override\n    public String getClassName() {\n    \treturn CLASS_NAME;\n    }\n\n    @Override\n    public String getType() {\n    \treturn TYPE;\n    }\n\n    @Override\n    public PortletURL getURLAdd(LiferayPortletRequest liferayPortletRequest,\n      LiferayPortletResponse liferayPortletResponse, long classTypeId) {\n        PortletURL portletURL = null;\n\n        try {\n          ThemeDisplay themeDisplay = (ThemeDisplay) \n          liferayPortletRequest.getAttribute(WebKeys.THEME_DISPLAY);\n\n          portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(themeDisplay),\n              GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n          portletURL.setParameter(\"mvcRenderCommandName\", \"/guestbookwebportlet/edit_guestbook\");\n          portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n\n          } catch (PortalException e) {\n          \n                logger.log(Level.SEVERE, e.getMessage()); \n                \n          }\n\n        return portletURL;\n    }\n\n    @Override\n    public boolean isLinkable() {\n        return _LINKABLE;\n    }\n\n    @Override\n    public String getIconCssClass() {\n      return \"bookmarks\";\n    }\n\n    @Reference(target = \"(osgi.web.symbolicname=com.liferay.docs.guestbook.portlet)\",\n        unbind = \"-\")\n    public void setServletContext(ServletContext servletContext) {\n        _servletContext = servletContext;\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setGuestbookLocalService(GuestbookLocalService guestbookLocalService) {\n        _guestbookLocalService = guestbookLocalService; \n    }\n\n  private ServletContext _servletContext;\n  private GuestbookLocalService _guestbookLocalService;\n  private static final boolean _LINKABLE = true;\n  public static final String CLASS_NAME = Guestbook.class.getName();\n  public static final String TYPE = \"guestbook\";\n  private Logger logger = Logger.getLogger(this.getClass().getName());\n  private ModelResourcePermission<Guestbook> _guestbookModelResourcePermission;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/asset/GuestbookEntryAssetRenderer.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.web.internal.asset;\n\nimport com.liferay.asset.kernel.model.BaseJSPAssetRenderer;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.LayoutConstants;\nimport com.liferay.portal.kernel.portlet.LiferayPortletRequest;\nimport com.liferay.portal.kernel.portlet.LiferayPortletResponse;\nimport com.liferay.portal.kernel.portlet.PortletURLFactoryUtil;\nimport com.liferay.portal.kernel.security.permission.ActionKeys;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.util.HtmlUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.petra.string.StringUtil;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport java.util.Locale;\nimport javax.portlet.PortletRequest;\nimport javax.portlet.PortletResponse;\nimport javax.portlet.PortletURL;\nimport javax.portlet.WindowState;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class GuestbookEntryAssetRenderer extends BaseJSPAssetRenderer<GuestbookEntry> {\n\n    public GuestbookEntryAssetRenderer(GuestbookEntry entry, ModelResourcePermission<GuestbookEntry> modelResourcePermission) {\n\n        _entry = entry;\n        _guestbookEntryModelResourcePermission = modelResourcePermission;\n    }\n\n    @Override\n    public boolean hasViewPermission(PermissionChecker permissionChecker) \n    {\n        try {\n            return _guestbookEntryModelResourcePermission.contains(\n                    permissionChecker, _entry, ActionKeys.VIEW);\n        }\n        catch (Exception e) {\n        }\n\n        return true;\n    }\n\n    @Override\n    public GuestbookEntry getAssetObject() {\n        return _entry;\n    }\n\n    @Override\n    public long getGroupId() {\n        return _entry.getGroupId();\n    }\n\n    @Override\n    public long getUserId() {\n\n        return _entry.getUserId();\n    }\n\n    @Override\n    public String getUserName() {\n        return _entry.getUserName();\n    }\n\n    @Override\n    public String getUuid() {\n        return _entry.getUuid();\n    }\n\n    @Override\n    public String getClassName() {\n        return GuestbookEntry.class.getName();\n    }\n\n    @Override\n    public long getClassPK() {\n        return _entry.getEntryId();\n    }\n\n    @Override\n    public String getSummary(PortletRequest portletRequest, \n            PortletResponse portletResponse) {\n        return \"Name: \" + _entry.getName() + \". Message: \" + _entry.getMessage();\n    }\n\n    @Override\n    public String getTitle(Locale locale) {\n        return _entry.getMessage();\n    }\n\n    @Override\n    public boolean include(HttpServletRequest request, \n            HttpServletResponse response, String template) throws Exception {\n        request.setAttribute(\"ENTRY\", _entry);\n        request.setAttribute(\"HtmlUtil\", HtmlUtil.getHtml());\n        request.setAttribute(\"StringUtil\", new StringUtil());\n        return super.include(request, response, template);\n    }\n\n    @Override\n    public String getJspPath(HttpServletRequest request, String template) {\n\n        if (template.equals(TEMPLATE_FULL_CONTENT)) {\n            request.setAttribute(\"gb_entry\", _entry);\n\n            return \"/asset/entry/\" + template + \".jsp\";\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public PortletURL getURLEdit(LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse) throws Exception {\n        PortletURL portletURL = liferayPortletResponse.createLiferayPortletURL(\n                getControlPanelPlid(liferayPortletRequest), GuestbookPortletKeys.GUESTBOOK,\n                PortletRequest.RENDER_PHASE);\n        portletURL.setParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n        portletURL.setParameter(\"entryId\", String.valueOf(_entry.getEntryId()));\n        portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n\n        return portletURL;\n    }\n\n    @Override\n    public String getURLViewInContext(LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse, String noSuchEntryRedirect) \n                    throws Exception {\n        try {\n            long plid = PortalUtil.getPlidFromPortletId(_entry.getGroupId(),\n                    GuestbookPortletKeys.GUESTBOOK);\n\n            PortletURL portletURL;\n            if (plid == LayoutConstants.DEFAULT_PLID) {\n                portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(liferayPortletRequest),\n                        GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n            } else {\n                portletURL = PortletURLFactoryUtil.create(liferayPortletRequest,\n                        GuestbookPortletKeys.GUESTBOOK, plid, PortletRequest.RENDER_PHASE);\n            }\n\n            portletURL.setParameter(\"mvcPath\", \"/guestbook/view_entry.jsp\");\n            portletURL.setParameter(\"entryId\", String.valueOf(_entry.getEntryId()));\n\n            String currentUrl = PortalUtil.getCurrentURL(liferayPortletRequest);\n\n            portletURL.setParameter(\"redirect\", currentUrl);\n\n            return portletURL.toString();\n\n        } catch (PortalException e) {\n\n        } catch (SystemException e) {\n        }\n\n        return noSuchEntryRedirect;\n    }\n\n    @Override\n    public String getURLView(LiferayPortletResponse liferayPortletResponse, \n            WindowState windowState) throws Exception {\n\n        return super.getURLView(liferayPortletResponse, windowState);\n    }\n\n    @Override\n    public boolean isPrintable() {\n        return true;\n    }\n    private final ModelResourcePermission<GuestbookEntry> _guestbookEntryModelResourcePermission;\n    private GuestbookEntry _entry;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/asset/GuestbookEntryAssetRendererFactory.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.web.internal.asset;\n\nimport com.liferay.asset.kernel.model.AssetRenderer;\nimport com.liferay.asset.kernel.model.AssetRendererFactory;\nimport com.liferay.asset.kernel.model.BaseAssetRendererFactory;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookEntryPermission;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.LiferayPortletRequest;\nimport com.liferay.portal.kernel.portlet.LiferayPortletResponse;\nimport com.liferay.portal.kernel.portlet.LiferayPortletURL;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.theme.ThemeDisplay;\nimport com.liferay.portal.kernel.util.WebKeys;\n\nimport javax.portlet.PortletRequest;\nimport javax.portlet.PortletURL;\nimport javax.portlet.WindowState;\nimport javax.portlet.WindowStateException;\nimport javax.servlet.ServletContext;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\n@Component(\n        immediate = true,\n        property = {\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK},\n        service = AssetRendererFactory.class\n        )\npublic class GuestbookEntryAssetRendererFactory extends BaseAssetRendererFactory<GuestbookEntry> {\n\n    public GuestbookEntryAssetRendererFactory() {\n        setClassName(CLASS_NAME);\n        setLinkable(_LINKABLE);\n        setPortletId(GuestbookPortletKeys.GUESTBOOK);\n        setSearchable(true);\n        setSelectable(true);\n\n    }\n\n    @Override\n    public AssetRenderer<GuestbookEntry> getAssetRenderer(long classPK, int type)\n            throws PortalException {\n\n        GuestbookEntry entry = _guestbookEntryLocalService.getGuestbookEntry(classPK);\n\n        GuestbookEntryAssetRenderer guestbookEntryAssetRenderer = new GuestbookEntryAssetRenderer(entry, _guestbookEntryModelResourcePermission);\n\n        guestbookEntryAssetRenderer.setAssetRendererType(type);\n        guestbookEntryAssetRenderer.setServletContext(_servletContext);\n\n        return guestbookEntryAssetRenderer;\n    }\n\n    @Override\n    public String getClassName() {\n        return CLASS_NAME;\n    }\n\n    @Override\n    public String getType() {\n        return TYPE;\n    }\n\n    @Override\n    public boolean hasPermission(PermissionChecker permissionChecker,\n            long classPK, String actionId) throws Exception {\n\n        GuestbookEntry entry = _guestbookEntryLocalService.getGuestbookEntry(classPK);\n        return GuestbookEntryPermission.contains(permissionChecker, entry, actionId);\n    }\n\n    @Override\n    public PortletURL getURLAdd(LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse, long classTypeId) {\n\n        PortletURL portletURL = null;\n\n        try {\n            ThemeDisplay themeDisplay = (ThemeDisplay) liferayPortletRequest.getAttribute(WebKeys.THEME_DISPLAY);\n\n            portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(themeDisplay),\n                    GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n            portletURL.setParameter(\"mvcRenderCommandName\", \"/guestbook/edit_entry\");\n            portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n        } catch (PortalException e) {\n        }\n\n        return portletURL;\n    }\n\n    @Override\n    public PortletURL getURLView(LiferayPortletResponse liferayPortletResponse, WindowState windowState) {\n\n        LiferayPortletURL liferayPortletURL\n        = liferayPortletResponse.createLiferayPortletURL(\n                GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n\n        try {\n            liferayPortletURL.setWindowState(windowState);\n        } catch (WindowStateException wse) {\n\n        }\n        return liferayPortletURL;\n    }\n\n    @Override\n    public boolean isLinkable() {\n        return _LINKABLE;\n    }\n\n    @Override\n    public String getIconCssClass() {\n        return \"pencil\";\n    }\n\n    @Reference(target = \"(osgi.web.symbolicname=com.liferay.docs.guestbook.portlet)\",\n            unbind = \"-\")\n    public void setServletContext (ServletContext servletContext) {\n        _servletContext = servletContext;\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setGuestbookEntryLocalService(GuestbookEntryLocalService guestbookEntryLocalService) {\n        _guestbookEntryLocalService = guestbookEntryLocalService;\n    }\n\n\n    private GuestbookEntryLocalService _guestbookEntryLocalService;\n    private ServletContext _servletContext;\n    private static final boolean _LINKABLE = true;\n    public static final String CLASS_NAME = GuestbookEntry.class.getName();\n    public static final String TYPE = \"entry\";\n\n    private ModelResourcePermission<GuestbookEntry>\n    _guestbookEntryModelResourcePermission;\n\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/security/permission/resource/GuestbookEntryPermission.java",
    "content": "package com.liferay.docs.guestbook.web.internal.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n\n@Component(immediate = true)\npublic class GuestbookEntryPermission {\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, GuestbookEntry entry, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookEntryModelResourcePermission.contains(permissionChecker, entry, actionId);\n\t}\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, long entryId, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookEntryModelResourcePermission.contains(permissionChecker, entryId, actionId);\n\t}\n\t\n\t@Reference(\n\t\t\ttarget = \"(model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\", \n\t\t\tunbind = \"-\")\n\tprotected void setEntryModelPermission(ModelResourcePermission<GuestbookEntry> modelResourcePermission) {\n\t\t\n\t\t_guestbookEntryModelResourcePermission = modelResourcePermission;\n\t}\n\t\n\tprivate static ModelResourcePermission<GuestbookEntry>_guestbookEntryModelResourcePermission;\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/security/permission/resource/GuestbookModelPermission.java",
    "content": "package com.liferay.docs.guestbook.web.internal.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n\n@Component(immediate = true)\npublic class GuestbookModelPermission {\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, Guestbook guestbook, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookModelResourcePermission.contains(permissionChecker, guestbook, actionId);\n\t}\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, long guestbookId, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookModelResourcePermission.contains(permissionChecker, guestbookId, actionId);\n\t}\n\t\n\t@Reference(\n\t\t\ttarget = \"(model.class.name=com.liferay.docs.guestbook.model.Guestbook)\", \n\t\t\tunbind = \"-\")\n\tprotected void setEntryModelPermission(ModelResourcePermission<Guestbook> modelResourcePermission) {\n\t\t\n\t\t_guestbookModelResourcePermission = modelResourcePermission;\n\t}\n\t\n\tprivate static ModelResourcePermission<Guestbook>_guestbookModelResourcePermission;\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/security/permission/resource/GuestbookPermission.java",
    "content": "package com.liferay.docs.guestbook.web.internal.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\n\n@Component(immediate=true)\npublic class GuestbookPermission {\n\n\tpublic static boolean contains(PermissionChecker permissionChecker, long groupId, String actionId) {\n\t\t\n\t\treturn _portletResourcePermission.contains(permissionChecker, groupId, actionId);\n\t\t\n\t}\n\t\n\t@Reference(\n\t\t\ttarget=\"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\", \n\t\t\tunbind=\"-\"\n\t\t\t)\n\tprotected void setPortletResourcePermission(PortletResourcePermission portletResourcePermission) {\n\t\t\n\t\t_portletResourcePermission = portletResourcePermission;\n\t}\n\t\n\tprivate static PortletResourcePermission _portletResourcePermission;\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resource-actions/default.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action  \nMapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n\n<resource-action-mapping>\n\n        <portlet-resource>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n            <permissions>\n                <supports>\n                    <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n                    <action-key>CONFIGURATION</action-key>\n                    <action-key>VIEW</action-key>\n                </supports>\n                <site-member-defaults>\n                    <action-key>VIEW</action-key>\n                </site-member-defaults>\n                <guest-defaults>\n                    <action-key>VIEW</action-key>\n                </guest-defaults>\n                <guest-unsupported>\n                    <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n                    <action-key>CONFIGURATION</action-key>\n                </guest-unsupported>\n            </permissions>\n        </portlet-resource>\n\n    <portlet-resource>\n        <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n        <permissions>\n            <supports>\n                <action-key>ADD_TO_PAGE</action-key>\n                <action-key>CONFIGURATION</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported />\n        </permissions>\n    </portlet-resource>\n\n</resource-action-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/asset/entry/full_content.jsp",
    "content": "<%@include file=\"../../init.jsp\"%>\n\n<%\nGuestbookEntry entry = (GuestbookEntry)request.getAttribute(\"gb_entry\");\n\nentry = entry.toEscapedModel();\n%>\n\n<dl>\n\t\t<dt>Guestbook</dt>\n\t\t<dd><%= GuestbookLocalServiceUtil.getGuestbook(entry.getGuestbookId()).getName() %></dd>\n\t\t<dt>Name</dt>\n\t\t<dd><%= entry.getName() %></dd>\n\t\t<dt>Message</dt>\n\t\t<dd><%= entry.getMessage() %></dd>\n</dl>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/asset/guestbook/full_content.jsp",
    "content": "<%@include file=\"../../init.jsp\"%>\n\n<%\nGuestbook guestbook = (Guestbook)request.getAttribute(\"gb_guestbook\");\n\nguestbook = guestbook.toEscapedModel();\n%>\n\n<dl>\n\t\t<dt>Name</dt>\n\t\t<dd><%= guestbook.getName() %></dd>\n</dl>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/css/main.scss",
    "content": ".guestbook-web {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/edit_entry.jsp",
    "content": "<%@include file=\"../init.jsp\" %>\n\n<% \n\nlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\nGuestbookEntry entry = null;\nif (entryId > 0) {\n  entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n}\n\nlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n%>\n\n<portlet:renderURL var=\"viewURL\">\n\t<portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\"></portlet:param>\n</portlet:renderURL>\n\n<portlet:actionURL name=\"addEntry\" var=\"addEntryURL\"></portlet:actionURL>\n<%-- Add header here --%>\n\n<liferay-ui:header\n    backURL=\"<%= viewURL.toString() %>\"\n    title=\"<%= entry == null ? \"Add Entry\" : entry.getName() %>\"\n/>\n\n<aui:form action=\"<%= addEntryURL %>\" name=\"<portlet:namespace />fm\">\n\n<aui:model-context bean=\"<%= entry %>\" model=\"<%= GuestbookEntry.class %>\" />\n\n\t<aui:fieldset>\n\n\t\t<aui:input name=\"name\" />\n\t\t<aui:input name=\"email\" />\n\t\t<aui:input name=\"message\" />\n\t\t<aui:input name=\"entryId\" type=\"hidden\" />\n\t\t<aui:input name=\"guestbookId\" type=\"hidden\" value='<%= entry == null ? guestbookId : entry.getGuestbookId() %>'/>\n\n\t</aui:fieldset>\n\n<%-- Add asset categories and links here --%>\n\n<liferay-asset:asset-categories-error />\n<liferay-asset:asset-tags-error />\n\n<liferay-ui:panel defaultState=\"closed\" \n                  extended=\"<%= false %>\" id=\"entryCategorizationPanel\" \n                  persistState=\"<%= true %>\" title=\"categorization\">\n\n    <aui:fieldset>\n       <liferay-asset:asset-categories-selector className=\"<%= GuestbookEntry.class.getName() %>\" classPK=\"<%= entryId %>\" />\n       <liferay-asset:asset-tags-selector className=\"<%= GuestbookEntry.class.getName() %>\" classPK=\"<%= entryId %>\" />\n    </aui:fieldset>\n\n</liferay-ui:panel>\n\n<liferay-ui:panel defaultState=\"closed\" \n                  extended=\"<%= false %>\" \n                  id=\"entryAssetLinksPanel\" \n                  persistState=\"<%= true %>\" \n                  title=\"related-assets\">\n\n    <aui:fieldset collapsed=\"<%= true %>\" collapsible=\"<%= true %>\" label=\"related-assets\">\n            \n        <liferay-asset:input-asset-links\n            className=\"<%= GuestbookEntry.class.getName() %>\"\n            classPK=\"<%= entryId %>\"\n        />\n            \n    </aui:fieldset>\n</liferay-ui:panel>\n\n\t<aui:button-row>\n\n\t\t<aui:button type=\"submit\"></aui:button>\n\t\t<aui:button type=\"cancel\" onClick=\"<%= viewURL.toString() %>\"></aui:button>\n\n\t</aui:button-row>\n</aui:form>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/entry_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\nString mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\nResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);\n\nGuestbookEntry entry = (GuestbookEntry)row.getObject(); \n%>\n\n<liferay-ui:icon-menu>\n\n<portlet:renderURL var=\"viewEntryURL\">\n  <portlet:param name=\"entryId\"\n    value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n  <portlet:param name=\"mvcPath\"\n    value=\"/guestbook/view_entry.jsp\" />\n</portlet:renderURL>\n\n<liferay-ui:icon message=\"View\" url=\"<%= viewEntryURL.toString() %>\" />\n\t<c:if\n\t\ttest=\"<%= GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.UPDATE) %>\">\n\t\t<portlet:renderURL var=\"editURL\">\n\t\t\t<portlet:param name=\"entryId\"\n\t\t\t\tvalue=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n\t\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n\t\t</portlet:renderURL>\n\n\t\t<liferay-ui:icon image=\"edit\" message=\"Edit\"\n\t\t\turl=\"<%=editURL.toString() %>\" />\n\t</c:if>\n\n\t<c:if\n\ttest=\"<%=GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.PERMISSIONS) %>\">\n\n\t\t<liferay-security:permissionsURL\n\t\t\tmodelResource=\"<%= GuestbookEntry.class.getName() %>\"\n\t\t\tmodelResourceDescription=\"<%= entry.getMessage() %>\"\n\t\t\tresourcePrimKey=\"<%= String.valueOf(entry.getEntryId()) %>\"\n\t\t\tvar=\"permissionsURL\" />\n\t\n\t\t<liferay-ui:icon image=\"permissions\" url=\"<%= permissionsURL %>\" />\n\n\t</c:if>\n\n\t<c:if\n\t\ttest=\"<%=GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.DELETE) %>\">\n\n\t\t<portlet:actionURL name=\"deleteEntry\" var=\"deleteURL\">\n\t\t\t<portlet:param name=\"entryId\"\n\t\t\t\tvalue=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\tvalue=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n\t\t</portlet:actionURL>\n\n\t\t<liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\t</c:if>\n\n</liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<liferay-ui:success key=\"entryAdded\" message=\"entry-added\" />\n<liferay-ui:success key=\"entryDeleted\" message=\"entry-deleted\" />\n\n<%\nlong guestbookId = Long.valueOf((Long) renderRequest\n\t\t.getAttribute(\"guestbookId\"));\n%>\n\n<portlet:renderURL var=\"searchURL\">\n\t<portlet:param name=\"mvcPath\" \n\tvalue=\"/guestbook/view_search.jsp\" />\n</portlet:renderURL>\n\n<aui:form action=\"<%=searchURL.toString() %>\" name=\"fm\">\n\n\t<div class=\"row\">\n\t\t<div class=\"col-md-8\">\n\t\t\t<aui:input inlineLabel=\"left\" label=\"\" name=\"keywords\" placeholder=\"search-entries\" size=\"256\" />\n\t\t</div>\n\n\t\t<div class=\"col-md-4\">\n\t\t\t<aui:button type=\"submit\" value=\"search\" />\n\t\t</div>\n\t</div>\n\n</aui:form>\n\n<aui:nav cssClass=\"nav-tabs\">\n\n\t<%\n\t\tList<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n\t\t\tfor (int i = 0; i < guestbooks.size(); i++) {\n\n\t\t\t\tGuestbook curGuestbook = guestbooks.get(i);\n\t\t\t\tString cssClass = StringPool.BLANK;\n\n\t\t\t\tif (curGuestbook.getGuestbookId() == guestbookId) {\n\t\t\t\t\tcssClass = \"active\";\n\t\t\t\t}\n\n\t\t\t\tif (GuestbookModelPermission.contains(\n\t\t\t\t\tpermissionChecker, curGuestbook.getGuestbookId(), \"VIEW\")) {\n\t\t\t\t\t\t\t\t\t\t\n\t%>\n\n\t<portlet:renderURL var=\"viewPageURL\">\n\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\" />\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(curGuestbook.getGuestbookId())%>\" />\n\t</portlet:renderURL>\n\n\t\t\n\t<aui:nav-item cssClass=\"<%=cssClass%>\" href=\"<%=viewPageURL%>\"\n\t\tlabel=\"<%=HtmlUtil.escape(curGuestbook.getName())%>\" />\n\n\t<%  \n\t\t\t\t}\n\t\t\t\n\t\t\t}\n\t%>\n\n</aui:nav>\n\n<aui:button-row cssClass=\"guestbook-buttons\">\n\n    <c:if test='<%= GuestbookPermission.contains(permissionChecker, scopeGroupId, \"ADD_ENTRY\") %>'>\n\t\t<portlet:renderURL var=\"addEntryURL\">\n\t\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\tvalue=\"<%=String.valueOf(guestbookId)%>\" />\n\t\t</portlet:renderURL>\n\n\t\t<aui:button onClick=\"<%=addEntryURL.toString()%>\" value=\"Add Entry\"></aui:button>\n\t\t\n\t</c:if>\n\n</aui:button-row>\n\n<liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntriesCount()%>\">\n<liferay-ui:search-container-results\n\tresults=\"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(scopeGroupId.longValue(),\n\t\t\t\t\tguestbookId, searchContainer.getStart(),\n\t\t\t\t\tsearchContainer.getEnd())%>\" />\n\n<liferay-ui:search-container-row\n\tclassName=\"com.liferay.docs.guestbook.model.GuestbookEntry\" modelVar=\"entry\">\n\n\t<liferay-ui:search-container-column-text property=\"message\" />\n\n\t<liferay-ui:search-container-column-text property=\"name\" />\n\n\t<liferay-ui:search-container-column-jsp\n\t\talign=\"right\" \n\t\tpath=\"/guestbook/entry_actions.jsp\" />\n</liferay-ui:search-container-row>\n\n<liferay-ui:search-iterator />\n\n</liferay-ui:search-container>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/view_entry.jsp",
    "content": "<%@ include file=\"../init.jsp\"%>\n\n<%\n\tlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\n\tlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n\tGuestbookEntry entry = null;\n\n\tentry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n\n\tentryId = entry.getEntryId();\n\n\tentry = entry.toEscapedModel();\n\n\tAssetEntry assetEntry = \n\tAssetEntryLocalServiceUtil.getEntry(GuestbookEntry.class.getName(), \n\t  entry.getEntryId());\n\n\tString currentURL = PortalUtil.getCurrentURL(request);\n\tPortalUtil.addPortletBreadcrumbEntry(request, entry.getMessage(),\n\tcurrentURL);\n\t\n\tPortalUtil.setPageSubtitle(entry.getMessage(), request);\n\tPortalUtil.setPageDescription(entry.getMessage(), request);\n\n\tList<AssetTag> assetTags = \n\tAssetTagLocalServiceUtil.getTags(GuestbookEntry.class.getName(), \n\tentry.getEntryId());\n\tPortalUtil.setPageKeywords(ListUtil.toString(assetTags, \"name\"), \n\trequest);\n%>\n\n<liferay-portlet:renderURL varImpl=\"viewEntryURL\">\n  <portlet:param name=\"mvcPath\"\n\tvalue=\"/guestbook/view_entry.jsp\" />\n  <portlet:param name=\"entryId\" value=\"<%=String.valueOf(entryId)%>\" />\n</liferay-portlet:renderURL>\n\n<liferay-portlet:renderURL varImpl=\"viewURL\">\n  <portlet:param name=\"mvcPath\"\n\tvalue=\"/guestbook/view.jsp\" />\n</liferay-portlet:renderURL>\n\n<liferay-ui:header backURL=\"<%=viewURL.toString()%>\"\n  title=\"<%=entry.getName()%>\" \n/>\n\n<dl>\n  <dt>Guestbook</dt>\n  <dd><%=GuestbookLocalServiceUtil.getGuestbook(entry.getGuestbookId()).getName()%></dd>\n  <dt>Name</dt>\n  <dd><%=entry.getName()%></dd>\n  <dt>Message</dt>\n  <dd><%=entry.getMessage()%></dd>\n</dl>\n\n<c:if test=\"<% themeDisplay.isSignedIn() %>\">\n\n\t<liferay-ui:panel-container extended=\"<%=false%>\"\n\t  id=\"guestbookCollaborationPanelContainer\" persistState=\"<%=true%>\">\n\t  <liferay-ui:panel collapsible=\"<%=true%>\" extended=\"<%=true%>\"\n\t\tid=\"guestbookCollaborationPanel\" persistState=\"<%=true%>\"\n\t\ttitle=\"Collaboration\">\n\t\t\n\t\t<liferay-ui:ratings \n\t\t\tclassName=\"<%=GuestbookEntry.class.getName() %>\" \n\t\t\tclassPK=\"<%=entry.getEntryId() %>\" \n\t\t\ttype=\"stars\" />\n\n\t\t<br/>\n\n\t\t<% \n\t\t\tDiscussion discussion = \n\t\t\tCommentManagerUtil.getDiscussion(user.getUserId(), \n\t\t\tscopeGroupId, GuestbookEntry.class.getName(), \n\t\t\tentry.getEntryId(), new ServiceContextFunction(request));\n\t\t%>\n\n\t\t<c:if test=\"<%= discussion != null %>\">\n\t\t  <h2>\n\t\t\t<strong><liferay-ui:message arguments=\"<%= discussion.getDiscussionCommentsCount() %>\" key='<%= (discussion.getDiscussionCommentsCount() == 1) ? \"x-comment\" : \"x-comments\" %>' /></strong>\n\t      </h2>\n\n\t\t  <liferay-comment:discussion\n\t\t\tclassName=\"<%= GuestbookEntry.class.getName() %>\"\n\t\t\tclassPK=\"<%= entry.getEntryId() %>\"\n\t\t\tdiscussion=\"<%= discussion %>\"\n\t\t\tformName=\"fm2\"\n\t\t\tratingsEnabled=\"true\"\n\t\t\tredirect=\"<%= currentURL %>\"\n\t\t\tuserId=\"<%= entry.getUserId() %>\"\n\t\t\t/>\n\t\t\t\n\t\t</c:if>\t\n\n\t\t</liferay-ui:panel>\n\t</liferay-ui:panel-container>\n\n</c:if>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/view_search.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\n  String keywords = ParamUtil.getString(request, \"keywords\");\n  long guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n%>\n<portlet:renderURL var=\"searchURL\">\n\t<portlet:param name=\"mvcPath\" \n\tvalue=\"/guestbook/view_search.jsp\" />\n</portlet:renderURL>\n\n<portlet:renderURL var=\"viewURL\">\n\t<portlet:param \n\t\tname=\"mvcPath\" \n\t\tvalue=\"/guestbook/view.jsp\" \n\t/>\n</portlet:renderURL>\n\n<aui:form action=\"${searchURL}\" name=\"fm\">\n\n\t<liferay-ui:header backURL=\"${viewURL}\" title=\"back\" />\n\n\t<div class=\"row\">\n\t\t<div class=\"col-md-8\">\n\t\t\t<aui:input inlineLabel=\"left\" label=\"\" name=\"keywords\" placeholder=\"search-entries\" size=\"256\" />\n\t\t</div>\n\n\t\t<div class=\"col-md-4\">\n\t\t\t<aui:button type=\"submit\" value=\"search\" />\n\t\t</div>\n\t</div>\n</aui:form>\n\n<%\n\tSearchContext searchContext = SearchContextFactory.getInstance(request);\n\n\tsearchContext.setKeywords(keywords);\n\tsearchContext.setAttribute(\"paginationType\", \"more\");\n\tsearchContext.setStart(0);\n\tsearchContext.setEnd(10);\n\n    Indexer<GuestbookEntry> indexer = IndexerRegistryUtil.getIndexer(GuestbookEntry.class);\n\n    Hits hits = indexer.search(searchContext);\n\n    List<GuestbookEntry> entries = new ArrayList<GuestbookEntry>();\n\n\tfor (int i = 0; i < hits.getDocs().length; i++) {\n\t\t\tDocument doc = hits.doc(i);\n\n\t\t\tlong entryId = GetterUtil\n\t\t\t.getLong(doc.get(Field.ENTRY_CLASS_PK));\n\n\t\t\tGuestbookEntry entry = null;\n\n\t\t\ttry {\n\t\t\t\t\tentry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n\t\t\t} catch (PortalException pe) {\n\t\t\t\t\t_log.error(pe.getLocalizedMessage());\n\t\t\t} catch (SystemException se) {\n\t\t\t\t\t_log.error(se.getLocalizedMessage());\n\t\t\t}\n\n\t\t\tentries.add(entry);\n\t}\n\n\tList<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n\tMap<String, String> guestbookMap = new HashMap<String, String>();\n\n\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tguestbookMap.put(Long.toString(guestbook.getGuestbookId()), guestbook.getName());\n\t}\n%>\n\n<liferay-ui:search-container delta=\"10\" \n\temptyResultsMessage=\"no-entries-were-found\" \n\ttotal=\"<%= entries.size() %>\">\n\t\t<liferay-ui:search-container-results\n\t\t\t\tresults=\"<%= entries %>\"\n/>\n\n\t<liferay-ui:search-container-row\n\t\t\tclassName=\"com.liferay.docs.guestbook.model.GuestbookEntry\"\n\t\t\tkeyProperty=\"entryId\" modelVar=\"entry\" escapedModel=\"<%=true%>\">\n\n        <liferay-ui:search-container-column-text name=\"guestbook\"\n            value=\"<%=guestbookMap.get(Long.toString(entry.getGuestbookId()))%>\" />\n\n        <liferay-ui:search-container-column-text property=\"message\" />\n\n        <liferay-ui:search-container-column-text property=\"name\" />\n                \n        <liferay-ui:search-container-column-jsp\n            path=\"/guestbook/entry_actions.jsp\"\n            align=\"right\" />\n   </liferay-ui:search-container-row>\n   \n   <liferay-ui:search-iterator />\n\n</liferay-ui:search-container>\n\n<%!\n\tprivate static Log _log = LogFactoryUtil.getLog(\"html.guestbook.view_search_jsp\");\n%>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/edit_guestbook.jsp",
    "content": "<%@include file = \"../init.jsp\" %>\n\n<%\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n        \n        Guestbook guestbook = null;\n\n        if (guestbookId > 0) {\n                guestbook = GuestbookLocalServiceUtil.getGuestbook(guestbookId);\n        }\n%>\n\n<portlet:renderURL var=\"viewURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook_admin/view.jsp\" />\n</portlet:renderURL>\n\n<portlet:actionURL name='<%= guestbook == null ? \"addGuestbook\" : \"updateGuestbook\" %>' var=\"editGuestbookURL\" />\n\n<aui:form action=\"<%= editGuestbookURL %>\" name=\"fm\">\n\n        <aui:model-context bean=\"<%= guestbook %>\" model=\"<%= Guestbook.class %>\" />\n\n        <aui:input type=\"hidden\" name=\"guestbookId\"\n            value='<%= guestbook == null ? \"\" : guestbook.getGuestbookId() %>' />\n\n        <aui:fieldset>\n             <aui:input name=\"name\" />\n        </aui:fieldset>\n\n        <aui:button-row>\n             <aui:button type=\"submit\" />\n             <aui:button onClick=\"<%= viewURL %>\" type=\"cancel\"  />\n        </aui:button-row>\n\n    <liferay-asset:asset-categories-error />\n    <liferay-asset:asset-tags-error />\n\n\t<c:if test=\"<%= guestbook != null %>\">\n\t\t<liferay-ui:panel defaultState=\"closed\" extended=\"<%= false %>\"\n\t\t  id=\"guestbookCategorizationPanel\" persistState=\"<%= true %>\"\n\t\t  title=\"categorization\">\n\n\t\t\t<aui:fieldset>\n\t\t\t\t<liferay-asset:asset-categories-selector className=\"<%= Guestbook.class.getName() %>\" classPK=\"<%= guestbook.getGuestbookId() %>\" />\n\t\t\t\t<liferay-asset:asset-tags-selector className=\"<%= Guestbook.class.getName() %>\" classPK=\"<%= guestbook.getGuestbookId() %>\" />\n\t\t\t</aui:fieldset>\n\n\t\t</liferay-ui:panel>\n\t</c:if>\n\n    <liferay-ui:panel defaultState=\"closed\" extended=\"<%= false %>\"\n      id=\"guestbookAssetLinksPanel\" persistState=\"<%= true %>\"\n      title=\"related-assets\">\n      <aui:fieldset>\n        <liferay-asset:input-asset-links\n          className=\"<%= Guestbook.class.getName() %>\"\n          classPK=\"<%= guestbookId %>\" />\n      </aui:fieldset>\n    </liferay-ui:panel>\n\n</aui:form>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/guestbook_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\n\tString mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n\tResultRow row = (ResultRow) request\n\t\t\t\t\t.getAttribute(\"SEARCH_CONTAINER_RESULT_ROW\");\n\n\tGuestbook guestbook = (Guestbook) row.getObject();\n%>\n\n<liferay-ui:icon-menu>\n\n\t<portlet:renderURL var=\"editURL\">\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(guestbook.getGuestbookId()) %>\" />\n\t\t<portlet:param name=\"mvcPath\"\n\t\t\tvalue=\"/guestbook_admin/edit_guestbook.jsp\" />\n\t</portlet:renderURL>\n\n\t<liferay-ui:icon image=\"edit\" message=\"Edit\"\n\t\t\turl=\"<%=editURL.toString() %>\" />\n\n\t<portlet:actionURL name=\"deleteGuestbook\" var=\"deleteURL\">\n\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\tvalue=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\" />\n\t</portlet:actionURL>\n\n\t<liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n\t<c:if\n\t\ttest=\"<%=GuestbookModelPermission.contains(permissionChecker, guestbook.getGuestbookId(), ActionKeys.PERMISSIONS) %>\">\n\n\t\t\t<liferay-security:permissionsURL\n\t\t\t\tmodelResource=\"<%= Guestbook.class.getName() %>\"\n\t\t\t\tmodelResourceDescription=\"<%= guestbook.getName() %>\"\n\t\t\t\tresourcePrimKey=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\"\n\t\t\t\tvar=\"permissionsURL\" />\n\t\t\n\t\t\t<liferay-ui:icon image=\"permissions\" url=\"<%= permissionsURL %>\" />\n\n\t</c:if>\n</liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<liferay-ui:success key=\"guestbookAdded\" message=\"guestbook-added\" />\n<liferay-ui:success key=\"guestbookUpdated\" message=\"guestbook-updated\" />\n<liferay-ui:success key=\"guestbookDeleted\" message=\"guestbook-deleted\" />\n\n<liferay-ui:search-container\n\ttotal=\"<%= GuestbookLocalServiceUtil.getGuestbooksCount(scopeGroupId) %>\">\n\t<liferay-ui:search-container-results\n\t\tresults=\"<%= GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId, \n\t\t\tsearchContainer.getStart(), searchContainer.getEnd()) %>\" />\n\n\t<liferay-ui:search-container-row\n\t\tclassName=\"com.liferay.docs.guestbook.model.Guestbook\" modelVar=\"guestbook\">\n\n\t\t<liferay-ui:search-container-column-text property=\"name\" />\n\t\t\t\t\n\t\t<liferay-ui:search-container-column-jsp\n\t\t\talign=\"right\" \n\t\t\tpath=\"/guestbook_admin/guestbook_actions.jsp\" />\n\t\t\n\t</liferay-ui:search-container-row>\n\n\t<liferay-ui:search-iterator />\n</liferay-ui:search-container>\n\n<aui:button-row cssClass=\"guestbook-admin-buttons\">\n\t<portlet:renderURL var=\"addGuestbookURL\">\n\t\t<portlet:param name=\"mvcPath\"\n\t\t\tvalue=\"/guestbook_admin/edit_guestbook.jsp\" />\n\t\t<portlet:param name=\"redirect\" value=\"<%= \"currentURL\" %>\" />\n\t</portlet:renderURL>\n\t\t\n\t<aui:button onClick=\"<%= addGuestbookURL.toString() %>\"\n\t\tvalue=\"Add Guestbook\" />\n</aui:button-row>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/init.jsp",
    "content": "<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\"%>\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\"%>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n<%@ taglib uri=\"http://liferay.com/tld/security\" prefix=\"liferay-security\" %>\n<%@ taglib uri=\"http://liferay.com/tld/asset\" prefix=\"liferay-asset\" %>\n<%@ taglib uri=\"http://liferay.com/tld/comment\" prefix=\"liferay-comment\" %>\n\n<%@ page import=\"java.util.Map\" %> \n<%@ page import=\"java.util.HashMap\" %>\n<%@ page import=\"com.liferay.asset.kernel.service.AssetEntryLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.asset.kernel.service.AssetTagLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.asset.kernel.model.AssetEntry\" %>\n<%@ page import=\"com.liferay.asset.kernel.model.AssetTag\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.ListUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.comment.Discussion\" %>\n<%@ page import=\"com.liferay.portal.kernel.comment.CommentManagerUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.service.ServiceContextFunction\" %>\n<%@ page import=\"java.util.List\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.ParamUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.HtmlUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.petra.string.StringPool\" %>\n<%@ page import=\"com.liferay.portal.kernel.model.PersistedModel\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.SearchEntry\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.ResultRow\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.Guestbook\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.GuestbookEntry\" %> \n<%@ page import=\"com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookModelPermission\" %>\n<%@ page import=\"com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookPermission\" %>\n<%@ page import=\"com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookEntryPermission\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.portal.kernel.security.permission.ActionKeys\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.SearchContainer\" %>\n<%@ page import=\"com.liferay.portal.kernel.exception.PortalException\" %>\n<%@ page import=\"com.liferay.portal.kernel.exception.SystemException\" %>\n<%@ page import=\"com.liferay.portal.kernel.language.LanguageUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.log.Log\" %>\n<%@ page import=\"com.liferay.portal.kernel.log.LogFactoryUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Indexer\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.IndexerRegistryUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.SearchContext\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.SearchContextFactory\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Hits\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Document\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Field\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.GetterUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.Validator\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.PortalUtil\" %>\n\n<%@ page import=\"java.util.ArrayList\" %>\n<%@ page import=\"java.util.Map\" %>\n<%@ page import=\"java.util.HashMap\" %>\n\n<%@ page import=\"javax.portlet.PortletURL\" %>\n\n<liferay-theme:defineObjects />\n\n<portlet:defineObjects />"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/content/Language.properties",
    "content": "entry-added=Entry added successfully.\nentry-deleted=Entry deleted successfully.\nguestbook-added=Guestbook added successfully.\nguestbook-updated=Guestbook updated successfully.\nguestbook-deleted=Guestbook deleted successfully.\nmodel.resource.com.liferay.docs.guestbook.model.Guestbook=Guestbook\nmodel.resource.com.liferay.docs.guestbook.model.GuestbookEntry=Guestbook Entry"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/portlet.properties",
    "content": "resource.actions.configs=META-INF/resource-actions/default.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/09-assets/com-liferay-docs-guestbook/settings.gradle",
    "content": "buildscript {\n\tdependencies {\n\t\tclasspath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.workspace\", version: \"2.0.4\"\n\t\tclasspath group: \"net.saliman\", name: \"gradle-properties-plugin\", version: \"1.4.6\"\n\t}\n\n\trepositories {\n\t\tmaven {\n\t\t\turl \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t\t}\n\t}\n}\n\napply plugin: \"net.saliman.properties\"\n\napply plugin: \"com.liferay.workspace\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/.blade.properties",
    "content": "#Wed Aug 14 16:06:16 EDT 2019\nliferay.version.default=7.2\nprofile.name=gradle\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/.gitignore",
    "content": "**/*.iml\n**/.ivy\n**/.classpath\n**/.project\n**/.sass-cache\n**/.settings\n**/bin\n**/build\n**/build_gradle\n**/node_modules\n**/test-coverage\n**/tmp\n**/.web_bundle_build\n.gradle\n.idea\n/bundles\n/gradle-*.properties\n/plugins-sdk/**/classes\n/plugins-sdk/**/ivy.xml.MD5\n/plugins-sdk/**/liferay-hook.xml.processed\n/plugins-sdk/build.*.properties\n/plugins-sdk/dependencies/**/*.jar\n/plugins-sdk/dist\n/plugins-sdk/lib\ntest-results"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/configs/common/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/configs/dev/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/configs/docker/.touch",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/configs/local/portal-ext.properties",
    "content": "include-and-override=portal-developer.properties\n\n#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/configs/prod/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "operationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/configs/prod/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/configs/uat/osgi/configs/com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration.config",
    "content": "logExceptionsOnly=B\"false\"\noperationMode=\"REMOTE\"\ntransportAddresses=[\"127.0.0.1:9300\"]"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/configs/uat/portal-ext.properties",
    "content": "#\n# MySQL\n#\n#jdbc.default.driverClassName=com.mysql.cj.jdbc.Driver\n#jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false\n#jdbc.default.username=root\n#jdbc.default.password="
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.2-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/gradle.properties",
    "content": "\n    # Set the directory where the downloaded bundle Zip files are stored. The\n    # default value is the \".liferay/bundles\" folder inside the user home\n    # directory.\n    #\n    #liferay.workspace.bundle.cache.dir=~/.liferay/bundles\n\n    #\n    # Set this to true if the \"liferay.workspace.bundle.url\" property is set to\n    # a DXP bundle Zip. This property allows the token residing in the\n    # \"~/.liferay\" folder to be used to validate your user credentials when\n    # downloading the bundle. The default value is false.\n    #\n    #liferay.workspace.bundle.token.download=false\n\n    #\n    # Set the email address to use when downloading a DXP bundle. This is used\n    # to create the authentication token. The email address must match the one\n    # registered for your DXP subscription.\n    #\n    # If you wish to create a new token without providing your email address and\n    # password in this file, you can create a token manually by navigating to\n    # your Liferay profile's Account Setting page and generating a token in the\n    # Authentication Tokens menu. Your token must reside in the \"~/.liferay\"\n    # folder.\n    #\n    #liferay.workspace.bundle.token.email.address=\n\n    #\n    # Set this to true to override the existing token with a newly generated\n    # token created by the \"createToken\" task. The default value is false.\n    #\n    #liferay.workspace.bundle.token.force=false\n\n    #\n    # Set the password to use when downloading a DXP bundle. This is used to\n    # create the authentication token. The password must match the one\n    # registered for your DXP subscription. See the\n    # \"liferay.workspace.bundle.token.email.address\" property for more details.\n    #\n    #liferay.workspace.bundle.token.password=\n\n    #\n    # Set the file to hold the Liferay bundle authentication token password.\n    # The default file value is \"~/.liferay/token\".\n    #\n    #liferay.workspace.bundle.token.password.file=\n\n    #\n    # Set the URL pointing to the bundle Zip to download. If the URL points to a\n    # DXP bundle (e.g., https://api.liferay.com/...), set the\n    # \"liferay.workspace.bundle.token.download\" property to true. The default\n    # value is the URL for Liferay Portal CE 7.0 GA7, Liferay Portal CE 7.1 GA4,\n    # or Liferay Portal CE 7.2 GA1, depending on the portal version the\n    # workspace is targeting.\n    #\nliferay.workspace.bundle.url = https://releases-cdn.liferay.com/portal/7.2.0-ga1/liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761.tar.gz\n\n    #\n    # Set the \"app.server.tomcat.version\" to match what is contained inside the\n    # Liferay bundle. Both the TestIntegrationPlugin and and LiferayExtPlugin\n    # rely on this version to match the bundled Tomcat version. If your\n    # configured bundle url points to a bundle with a different Tomcat version,\n    # set the property below to match that Tomcat version.\n    #\napp.server.tomcat.version = 9.0.17\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository in the\n    # root project. The default value is true.\n    #\n    #liferay.workspace.default.repository.enabled=true\n\n    #\n    # Set the Liferay Portal Docker image to create your container from. The\n    # default value points to Liferay Portal CE 7.2 GA1.\n    #\n    #liferay.workspace.docker.image.liferay=liferay/portal:7.2.0-ga1\n\n    #\n    # Set the environment with the settings appropriate for current development.\n    # The \"configs\" folder is used to hold different environments in the same\n    # workspace. You can organize environment settings and generate an\n    # environment installation with those settings. There are five\n    # environments: common, dev, docker, local, prod, and uat. The default value\n    # is \"local\".\n    #\n    #liferay.workspace.environment=local\n\n    #\n    # Set the folder that contains the Liferay bundle downloaded from the\n    # \"liferay.workspace.bundle.url\" property. The default value is \"bundles\".\n    #\n    #liferay.workspace.home.dir=bundles\n\n    #\n    # Set this to true to configure Liferay CDN as the default repository for\n    # module/OSGi projects. The default value is true.\n    #\n    #liferay.workspace.modules.default.repository.enabled=true\n\n    #\n    # Set the folder that contains all Ext OSGi modules and Ext plugins. The\n    # default value is \"ext\".\n    #\n    #liferay.workspace.ext.dir=ext\n\n    #\n    # Set the folder that contains all module/OSGi projects. The default value\n    # is \"modules\".\n    #\n    #liferay.workspace.modules.dir=modules\n\n    #\n    # Set this to true to compile the JSP files in OSGi modules and have them\n    # added to the distributable Zip/Tar. The default value is false.\n    #\n    #liferay.workspace.modules.jsp.precompile.enabled=false\n\n    #\n    # Set the folder that contains the Plugins SDK environment. The default\n    # value is \"plugins-sdk\".\n    #\n    #liferay.workspace.plugins.sdk.dir=plugins-sdk\n\n    #\n    # Set the Liferay Portal or DXP version to develop and test against. By\n    # setting this property, it enables the target platform features such as\n    # dependency management and OSGi resolve tasks. Use the version that\n    # matches the Liferay Portal or DXP bundle version in this workspace.\n    #\n    # For a list of all available target platform versions, see\n    # https://bit.ly/2IkAwwW for Liferay Portal and https://bit.ly/2GIyfZF for\n    # Liferay DXP.\n    #\n    #liferay.workspace.target.platform.version=7.2.0\n\n    #\n    # Set this to true if you have enabled the Target Platform plugin (i.e. you\n    # have set the above property) and you want to apply the TargetPlatformIDE\n    # plugin to the root workspace project. This will cause all of the BOM\n    # artifacts jars and their Java sources to be indexed by your IDE. Setting\n    # this property to true can slow down your IDE's project synchronization.\n    #\n    target.platform.index.sources=true\n\n    #\n    # Set the folder that contains Node.js-style theme projects. The default\n    # value is \"themes\".\n    #\n    #liferay.workspace.themes.dir=themes\n\n    #\n    # Set this to true to build the theme projects using the Liferay Portal\n    # Tools Theme Builder. The default value is false.\n    #\n    #liferay.workspace.themes.java.build=false\n\n    #\n    # Set the folder that contains classic WAR-style projects. The default value\n    # is \"wars\".\n    #\n    #liferay.workspace.wars.dir=wars\n\n\n    #\n    # Set the subscription key for Microsoft Translation integration.\n    # Subscription to the Translator Text Translation API on Microsoft Cognitive\n    # Services is required. Basic subscriptions, up to 2 million characters a\n    # month, are free. See\n    # http://docs.microsofttranslator.com/text-translate.html for more\n    # information.\n    #\nmicrosoft.translator.subscription.key = \nliferay.workspace.target.platform.version = 7.2.0\ntarget.platform.index.sources = false\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/.gitignore",
    "content": ".gradle/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/build.gradle",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/bnd.bnd",
    "content": "Bundle-Name: guestbook-api\nBundle-SymbolicName: com.liferay.docs.guestbook.api\nBundle-Version: 1.0.0\nExport-Package:\\\n\tcom.liferay.docs.guestbook.exception,\\\n\tcom.liferay.docs.guestbook.model,\\\n\tcom.liferay.docs.guestbook.service,\\\n\tcom.liferay.docs.guestbook.service.persistence\n-check: EXPORTS\n-includeresource: META-INF/service.xml=../guestbook-service/service.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/constants/GuestbookConstants.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.constants;\n\npublic class GuestbookConstants {\n\t\n\tpublic static final String RESOURCE_NAME = \"com.liferay.docs.guestbook\";\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/constants/GuestbookPortletKeys.java",
    "content": "package com.liferay.docs.guestbook.constants;\n\n/**\n * @author sezovr\n */\npublic class GuestbookPortletKeys {\n\n\tpublic static final String GUESTBOOK =\n\t\t\"com_liferay_docs_guestbook_portlet_GuestbookPortlet\";\n\n\tpublic static final String GUESTBOOK_ADMIN =\n\t\t  \"com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet\";\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryEmailException extends PortalException {\n\n\tpublic EntryEmailException() {\n\t}\n\n\tpublic EntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryMessageException extends PortalException {\n\n\tpublic EntryMessageException() {\n\t}\n\n\tpublic EntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/EntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class EntryNameException extends PortalException {\n\n\tpublic EntryNameException() {\n\t}\n\n\tpublic EntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic EntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic EntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryEmailException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryEmailException extends PortalException {\n\n\tpublic GuestbookEntryEmailException() {\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryEmailException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryEmailException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryMessageException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryMessageException extends PortalException {\n\n\tpublic GuestbookEntryMessageException() {\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryMessageException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryMessageException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookEntryNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryNameException extends PortalException {\n\n\tpublic GuestbookEntryNameException() {\n\t}\n\n\tpublic GuestbookEntryNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookEntryNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookEntryNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/GuestbookNameException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.PortalException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookNameException extends PortalException {\n\n\tpublic GuestbookNameException() {\n\t}\n\n\tpublic GuestbookNameException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic GuestbookNameException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic GuestbookNameException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookEntryException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookEntryException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookEntryException() {\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookEntryException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/exception/NoSuchGuestbookException.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.exception;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\nimport com.liferay.portal.kernel.exception.NoSuchModelException;\n\n/**\n * @author Liferay\n */\n@ProviderType\npublic class NoSuchGuestbookException extends NoSuchModelException {\n\n\tpublic NoSuchGuestbookException() {\n\t}\n\n\tpublic NoSuchGuestbookException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic NoSuchGuestbookException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\tpublic NoSuchGuestbookException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/Guestbook.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookModel\n * @generated\n */\n@ImplementationClassName(\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\")\n@ProviderType\npublic interface Guestbook extends GuestbookModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<Guestbook, Long> GUESTBOOK_ID_ACCESSOR =\n\t\tnew Accessor<Guestbook, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(Guestbook guestbook) {\n\t\t\t\treturn guestbook.getGuestbookId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Guestbook> getTypeClass() {\n\t\t\t\treturn Guestbook.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntry.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.annotation.ImplementationClassName;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.util.Accessor;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * @author Liferay\n * @see GuestbookEntryModel\n * @generated\n */\n@ImplementationClassName(\n\t\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\"\n)\n@ProviderType\npublic interface GuestbookEntry extends GuestbookEntryModel, PersistedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this interface directly. Add methods to <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\tpublic static final Accessor<GuestbookEntry, Long> ENTRY_ID_ACCESSOR =\n\t\tnew Accessor<GuestbookEntry, Long>() {\n\n\t\t\t@Override\n\t\t\tpublic Long get(GuestbookEntry guestbookEntry) {\n\t\t\t\treturn guestbookEntry.getEntryId();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<Long> getAttributeClass() {\n\t\t\t\treturn Long.class;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<GuestbookEntry> getTypeClass() {\n\t\t\t\treturn GuestbookEntry.class;\n\t\t\t}\n\n\t\t};\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryModel\n\textends BaseModel<GuestbookEntry>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook entry model instance should use the {@link GuestbookEntry} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\tpublic long getEntryId();\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\tpublic void setEntryId(long entryId);\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getEmail();\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\tpublic void setEmail(String email);\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@AutoEscape\n\tpublic String getMessage();\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\tpublic void setMessage(String message);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntrySoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookEntryServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntrySoap implements Serializable {\n\n\tpublic static GuestbookEntrySoap toSoapModel(GuestbookEntry model) {\n\t\tGuestbookEntrySoap soapModel = new GuestbookEntrySoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setEntryId(model.getEntryId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setEmail(model.getEmail());\n\t\tsoapModel.setMessage(model.getMessage());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(GuestbookEntry[] models) {\n\t\tGuestbookEntrySoap[] soapModels = new GuestbookEntrySoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[][] toSoapModels(\n\t\tGuestbookEntry[][] models) {\n\n\t\tGuestbookEntrySoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels =\n\t\t\t\tnew GuestbookEntrySoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookEntrySoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookEntrySoap[] toSoapModels(\n\t\tList<GuestbookEntry> models) {\n\n\t\tList<GuestbookEntrySoap> soapModels = new ArrayList<GuestbookEntrySoap>(\n\t\t\tmodels.size());\n\n\t\tfor (GuestbookEntry model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookEntrySoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookEntrySoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetEntryId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic String getEmail() {\n\t\treturn _email;\n\t}\n\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn _message;\n\t}\n\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookEntryWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link GuestbookEntry}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryWrapper\n\textends BaseModelWrapper<GuestbookEntry>\n\timplements GuestbookEntry, ModelWrapper<GuestbookEntry> {\n\n\tpublic GuestbookEntryWrapper(GuestbookEntry guestbookEntry) {\n\t\tsuper(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"entryId\", getEntryId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"email\", getEmail());\n\t\tattributes.put(\"message\", getMessage());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong entryId = (Long)attributes.get(\"entryId\");\n\n\t\tif (entryId != null) {\n\t\t\tsetEntryId(entryId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tString email = (String)attributes.get(\"email\");\n\n\t\tif (email != null) {\n\t\t\tsetEmail(email);\n\t\t}\n\n\t\tString message = (String)attributes.get(\"message\");\n\n\t\tif (message != null) {\n\t\t\tsetMessage(message);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook entry.\n\t *\n\t * @return the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook entry.\n\t *\n\t * @return the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the email of this guestbook entry.\n\t *\n\t * @return the email of this guestbook entry\n\t */\n\t@Override\n\tpublic String getEmail() {\n\t\treturn model.getEmail();\n\t}\n\n\t/**\n\t * Returns the entry ID of this guestbook entry.\n\t *\n\t * @return the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn model.getEntryId();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook entry.\n\t *\n\t * @return the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook entry.\n\t *\n\t * @return the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the message of this guestbook entry.\n\t *\n\t * @return the message of this guestbook entry\n\t */\n\t@Override\n\tpublic String getMessage() {\n\t\treturn model.getMessage();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook entry.\n\t *\n\t * @return the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook entry.\n\t *\n\t * @return the name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook entry.\n\t *\n\t * @return the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook entry.\n\t *\n\t * @return the status of this guestbook entry\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook entry.\n\t *\n\t * @return the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook entry.\n\t *\n\t * @return the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook entry.\n\t *\n\t * @return the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook entry.\n\t *\n\t * @return the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook entry.\n\t *\n\t * @return the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook entry.\n\t *\n\t * @return the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook entry.\n\t *\n\t * @return the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook entry.\n\t *\n\t * @return the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is approved.\n\t *\n\t * @return <code>true</code> if this guestbook entry is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is denied.\n\t *\n\t * @return <code>true</code> if this guestbook entry is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook entry is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is expired.\n\t *\n\t * @return <code>true</code> if this guestbook entry is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook entry is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook entry is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is pending.\n\t *\n\t * @return <code>true</code> if this guestbook entry is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook entry is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook entry is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook entry.\n\t *\n\t * @param companyId the company ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook entry.\n\t *\n\t * @param createDate the create date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the email of this guestbook entry.\n\t *\n\t * @param email the email of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEmail(String email) {\n\t\tmodel.setEmail(email);\n\t}\n\n\t/**\n\t * Sets the entry ID of this guestbook entry.\n\t *\n\t * @param entryId the entry ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\tmodel.setEntryId(entryId);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook entry.\n\t *\n\t * @param groupId the group ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook entry.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the message of this guestbook entry.\n\t *\n\t * @param message the message of this guestbook entry\n\t */\n\t@Override\n\tpublic void setMessage(String message) {\n\t\tmodel.setMessage(message);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook entry.\n\t *\n\t * @param modifiedDate the modified date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook entry.\n\t *\n\t * @param name the name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook entry.\n\t *\n\t * @param primaryKey the primary key of this guestbook entry\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook entry.\n\t *\n\t * @param status the status of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook entry.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook entry.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook entry.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook entry.\n\t *\n\t * @param statusDate the status date of this guestbook entry\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook entry.\n\t *\n\t * @param userId the user ID of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook entry.\n\t *\n\t * @param userName the user name of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook entry.\n\t *\n\t * @param userUuid the user uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook entry.\n\t *\n\t * @param uuid the uuid of this guestbook entry\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookEntryWrapper wrap(GuestbookEntry guestbookEntry) {\n\t\treturn new GuestbookEntryWrapper(guestbookEntry);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.portal.kernel.bean.AutoEscape;\nimport com.liferay.portal.kernel.model.BaseModel;\nimport com.liferay.portal.kernel.model.GroupedModel;\nimport com.liferay.portal.kernel.model.ShardedModel;\nimport com.liferay.portal.kernel.model.StagedAuditedModel;\nimport com.liferay.portal.kernel.model.WorkflowedModel;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model interface for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This interface and its corresponding implementation <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in <code>com.liferay.docs.guestbook.model.impl.GuestbookImpl</code>.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic interface GuestbookModel\n\textends BaseModel<Guestbook>, GroupedModel, ShardedModel,\n\t\t\tStagedAuditedModel, WorkflowedModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. All methods that expect a guestbook model instance should use the {@link Guestbook} interface instead.\n\t */\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\tpublic long getPrimaryKey();\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\tpublic void setPrimaryKey(long primaryKey);\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUuid();\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\tpublic long getGuestbookId();\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\tpublic void setGuestbookId(long guestbookId);\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@AutoEscape\n\tpublic String getName();\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\tpublic void setName(String name);\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId();\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId);\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId();\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId);\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId();\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId);\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid();\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid);\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getUserName();\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName);\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate();\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate);\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate();\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate);\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus();\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status);\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId();\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId);\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid();\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid);\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@AutoEscape\n\t@Override\n\tpublic String getStatusByUserName();\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName);\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate();\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate);\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending();\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport java.io.Serializable;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * This class is used by SOAP remote services, specifically {@link com.liferay.docs.guestbook.service.http.GuestbookServiceSoap}.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookSoap implements Serializable {\n\n\tpublic static GuestbookSoap toSoapModel(Guestbook model) {\n\t\tGuestbookSoap soapModel = new GuestbookSoap();\n\n\t\tsoapModel.setUuid(model.getUuid());\n\t\tsoapModel.setGuestbookId(model.getGuestbookId());\n\t\tsoapModel.setName(model.getName());\n\t\tsoapModel.setGroupId(model.getGroupId());\n\t\tsoapModel.setCompanyId(model.getCompanyId());\n\t\tsoapModel.setUserId(model.getUserId());\n\t\tsoapModel.setUserName(model.getUserName());\n\t\tsoapModel.setCreateDate(model.getCreateDate());\n\t\tsoapModel.setModifiedDate(model.getModifiedDate());\n\t\tsoapModel.setStatus(model.getStatus());\n\t\tsoapModel.setStatusByUserId(model.getStatusByUserId());\n\t\tsoapModel.setStatusByUserName(model.getStatusByUserName());\n\t\tsoapModel.setStatusDate(model.getStatusDate());\n\n\t\treturn soapModel;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(Guestbook[] models) {\n\t\tGuestbookSoap[] soapModels = new GuestbookSoap[models.length];\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModel(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[][] toSoapModels(Guestbook[][] models) {\n\t\tGuestbookSoap[][] soapModels = null;\n\n\t\tif (models.length > 0) {\n\t\t\tsoapModels = new GuestbookSoap[models.length][models[0].length];\n\t\t}\n\t\telse {\n\t\t\tsoapModels = new GuestbookSoap[0][0];\n\t\t}\n\n\t\tfor (int i = 0; i < models.length; i++) {\n\t\t\tsoapModels[i] = toSoapModels(models[i]);\n\t\t}\n\n\t\treturn soapModels;\n\t}\n\n\tpublic static GuestbookSoap[] toSoapModels(List<Guestbook> models) {\n\t\tList<GuestbookSoap> soapModels = new ArrayList<GuestbookSoap>(\n\t\t\tmodels.size());\n\n\t\tfor (Guestbook model : models) {\n\t\t\tsoapModels.add(toSoapModel(model));\n\t\t}\n\n\t\treturn soapModels.toArray(new GuestbookSoap[soapModels.size()]);\n\t}\n\n\tpublic GuestbookSoap() {\n\t}\n\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setPrimaryKey(long pk) {\n\t\tsetGuestbookId(pk);\n\t}\n\n\tpublic String getUuid() {\n\t\treturn _uuid;\n\t}\n\n\tpublic void setUuid(String uuid) {\n\t\t_uuid = uuid;\n\t}\n\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic String getName() {\n\t\treturn _name;\n\t}\n\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\tpublic void setGroupId(long groupId) {\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\tpublic void setCompanyId(long companyId) {\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\tpublic String getUserName() {\n\t\treturn _userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\tpublic void setStatus(int status) {\n\t\t_status = status;\n\t}\n\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\tpublic String getStatusByUserName() {\n\t\treturn _statusByUserName;\n\t}\n\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\tprivate String _uuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _companyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate int _status;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/model/GuestbookWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model;\n\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.wrapper.BaseModelWrapper;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * <p>\n * This class is a wrapper for {@link Guestbook}.\n * </p>\n *\n * @author Liferay\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic class GuestbookWrapper\n\textends BaseModelWrapper<Guestbook>\n\timplements Guestbook, ModelWrapper<Guestbook> {\n\n\tpublic GuestbookWrapper(Guestbook guestbook) {\n\t\tsuper(guestbook);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tattributes.put(\"uuid\", getUuid());\n\t\tattributes.put(\"guestbookId\", getGuestbookId());\n\t\tattributes.put(\"name\", getName());\n\t\tattributes.put(\"groupId\", getGroupId());\n\t\tattributes.put(\"companyId\", getCompanyId());\n\t\tattributes.put(\"userId\", getUserId());\n\t\tattributes.put(\"userName\", getUserName());\n\t\tattributes.put(\"createDate\", getCreateDate());\n\t\tattributes.put(\"modifiedDate\", getModifiedDate());\n\t\tattributes.put(\"status\", getStatus());\n\t\tattributes.put(\"statusByUserId\", getStatusByUserId());\n\t\tattributes.put(\"statusByUserName\", getStatusByUserName());\n\t\tattributes.put(\"statusDate\", getStatusDate());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tString uuid = (String)attributes.get(\"uuid\");\n\n\t\tif (uuid != null) {\n\t\t\tsetUuid(uuid);\n\t\t}\n\n\t\tLong guestbookId = (Long)attributes.get(\"guestbookId\");\n\n\t\tif (guestbookId != null) {\n\t\t\tsetGuestbookId(guestbookId);\n\t\t}\n\n\t\tString name = (String)attributes.get(\"name\");\n\n\t\tif (name != null) {\n\t\t\tsetName(name);\n\t\t}\n\n\t\tLong groupId = (Long)attributes.get(\"groupId\");\n\n\t\tif (groupId != null) {\n\t\t\tsetGroupId(groupId);\n\t\t}\n\n\t\tLong companyId = (Long)attributes.get(\"companyId\");\n\n\t\tif (companyId != null) {\n\t\t\tsetCompanyId(companyId);\n\t\t}\n\n\t\tLong userId = (Long)attributes.get(\"userId\");\n\n\t\tif (userId != null) {\n\t\t\tsetUserId(userId);\n\t\t}\n\n\t\tString userName = (String)attributes.get(\"userName\");\n\n\t\tif (userName != null) {\n\t\t\tsetUserName(userName);\n\t\t}\n\n\t\tDate createDate = (Date)attributes.get(\"createDate\");\n\n\t\tif (createDate != null) {\n\t\t\tsetCreateDate(createDate);\n\t\t}\n\n\t\tDate modifiedDate = (Date)attributes.get(\"modifiedDate\");\n\n\t\tif (modifiedDate != null) {\n\t\t\tsetModifiedDate(modifiedDate);\n\t\t}\n\n\t\tInteger status = (Integer)attributes.get(\"status\");\n\n\t\tif (status != null) {\n\t\t\tsetStatus(status);\n\t\t}\n\n\t\tLong statusByUserId = (Long)attributes.get(\"statusByUserId\");\n\n\t\tif (statusByUserId != null) {\n\t\t\tsetStatusByUserId(statusByUserId);\n\t\t}\n\n\t\tString statusByUserName = (String)attributes.get(\"statusByUserName\");\n\n\t\tif (statusByUserName != null) {\n\t\t\tsetStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tDate statusDate = (Date)attributes.get(\"statusDate\");\n\n\t\tif (statusDate != null) {\n\t\t\tsetStatusDate(statusDate);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the company ID of this guestbook.\n\t *\n\t * @return the company ID of this guestbook\n\t */\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn model.getCompanyId();\n\t}\n\n\t/**\n\t * Returns the create date of this guestbook.\n\t *\n\t * @return the create date of this guestbook\n\t */\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn model.getCreateDate();\n\t}\n\n\t/**\n\t * Returns the group ID of this guestbook.\n\t *\n\t * @return the group ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn model.getGroupId();\n\t}\n\n\t/**\n\t * Returns the guestbook ID of this guestbook.\n\t *\n\t * @return the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn model.getGuestbookId();\n\t}\n\n\t/**\n\t * Returns the modified date of this guestbook.\n\t *\n\t * @return the modified date of this guestbook\n\t */\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn model.getModifiedDate();\n\t}\n\n\t/**\n\t * Returns the name of this guestbook.\n\t *\n\t * @return the name of this guestbook\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn model.getName();\n\t}\n\n\t/**\n\t * Returns the primary key of this guestbook.\n\t *\n\t * @return the primary key of this guestbook\n\t */\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn model.getPrimaryKey();\n\t}\n\n\t/**\n\t * Returns the status of this guestbook.\n\t *\n\t * @return the status of this guestbook\n\t */\n\t@Override\n\tpublic int getStatus() {\n\t\treturn model.getStatus();\n\t}\n\n\t/**\n\t * Returns the status by user ID of this guestbook.\n\t *\n\t * @return the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn model.getStatusByUserId();\n\t}\n\n\t/**\n\t * Returns the status by user name of this guestbook.\n\t *\n\t * @return the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\treturn model.getStatusByUserName();\n\t}\n\n\t/**\n\t * Returns the status by user uuid of this guestbook.\n\t *\n\t * @return the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\treturn model.getStatusByUserUuid();\n\t}\n\n\t/**\n\t * Returns the status date of this guestbook.\n\t *\n\t * @return the status date of this guestbook\n\t */\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn model.getStatusDate();\n\t}\n\n\t/**\n\t * Returns the user ID of this guestbook.\n\t *\n\t * @return the user ID of this guestbook\n\t */\n\t@Override\n\tpublic long getUserId() {\n\t\treturn model.getUserId();\n\t}\n\n\t/**\n\t * Returns the user name of this guestbook.\n\t *\n\t * @return the user name of this guestbook\n\t */\n\t@Override\n\tpublic String getUserName() {\n\t\treturn model.getUserName();\n\t}\n\n\t/**\n\t * Returns the user uuid of this guestbook.\n\t *\n\t * @return the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUserUuid() {\n\t\treturn model.getUserUuid();\n\t}\n\n\t/**\n\t * Returns the uuid of this guestbook.\n\t *\n\t * @return the uuid of this guestbook\n\t */\n\t@Override\n\tpublic String getUuid() {\n\t\treturn model.getUuid();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is approved.\n\t *\n\t * @return <code>true</code> if this guestbook is approved; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isApproved() {\n\t\treturn model.isApproved();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is denied.\n\t *\n\t * @return <code>true</code> if this guestbook is denied; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDenied() {\n\t\treturn model.isDenied();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is a draft.\n\t *\n\t * @return <code>true</code> if this guestbook is a draft; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isDraft() {\n\t\treturn model.isDraft();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is expired.\n\t *\n\t * @return <code>true</code> if this guestbook is expired; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isExpired() {\n\t\treturn model.isExpired();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is inactive.\n\t *\n\t * @return <code>true</code> if this guestbook is inactive; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isInactive() {\n\t\treturn model.isInactive();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is incomplete.\n\t *\n\t * @return <code>true</code> if this guestbook is incomplete; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\treturn model.isIncomplete();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is pending.\n\t *\n\t * @return <code>true</code> if this guestbook is pending; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isPending() {\n\t\treturn model.isPending();\n\t}\n\n\t/**\n\t * Returns <code>true</code> if this guestbook is scheduled.\n\t *\n\t * @return <code>true</code> if this guestbook is scheduled; <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isScheduled() {\n\t\treturn model.isScheduled();\n\t}\n\n\t@Override\n\tpublic void persist() {\n\t\tmodel.persist();\n\t}\n\n\t/**\n\t * Sets the company ID of this guestbook.\n\t *\n\t * @param companyId the company ID of this guestbook\n\t */\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\tmodel.setCompanyId(companyId);\n\t}\n\n\t/**\n\t * Sets the create date of this guestbook.\n\t *\n\t * @param createDate the create date of this guestbook\n\t */\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\tmodel.setCreateDate(createDate);\n\t}\n\n\t/**\n\t * Sets the group ID of this guestbook.\n\t *\n\t * @param groupId the group ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\tmodel.setGroupId(groupId);\n\t}\n\n\t/**\n\t * Sets the guestbook ID of this guestbook.\n\t *\n\t * @param guestbookId the guestbook ID of this guestbook\n\t */\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\tmodel.setGuestbookId(guestbookId);\n\t}\n\n\t/**\n\t * Sets the modified date of this guestbook.\n\t *\n\t * @param modifiedDate the modified date of this guestbook\n\t */\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\tmodel.setModifiedDate(modifiedDate);\n\t}\n\n\t/**\n\t * Sets the name of this guestbook.\n\t *\n\t * @param name the name of this guestbook\n\t */\n\t@Override\n\tpublic void setName(String name) {\n\t\tmodel.setName(name);\n\t}\n\n\t/**\n\t * Sets the primary key of this guestbook.\n\t *\n\t * @param primaryKey the primary key of this guestbook\n\t */\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tmodel.setPrimaryKey(primaryKey);\n\t}\n\n\t/**\n\t * Sets the status of this guestbook.\n\t *\n\t * @param status the status of this guestbook\n\t */\n\t@Override\n\tpublic void setStatus(int status) {\n\t\tmodel.setStatus(status);\n\t}\n\n\t/**\n\t * Sets the status by user ID of this guestbook.\n\t *\n\t * @param statusByUserId the status by user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\tmodel.setStatusByUserId(statusByUserId);\n\t}\n\n\t/**\n\t * Sets the status by user name of this guestbook.\n\t *\n\t * @param statusByUserName the status by user name of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\tmodel.setStatusByUserName(statusByUserName);\n\t}\n\n\t/**\n\t * Sets the status by user uuid of this guestbook.\n\t *\n\t * @param statusByUserUuid the status by user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t\tmodel.setStatusByUserUuid(statusByUserUuid);\n\t}\n\n\t/**\n\t * Sets the status date of this guestbook.\n\t *\n\t * @param statusDate the status date of this guestbook\n\t */\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\tmodel.setStatusDate(statusDate);\n\t}\n\n\t/**\n\t * Sets the user ID of this guestbook.\n\t *\n\t * @param userId the user ID of this guestbook\n\t */\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\tmodel.setUserId(userId);\n\t}\n\n\t/**\n\t * Sets the user name of this guestbook.\n\t *\n\t * @param userName the user name of this guestbook\n\t */\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\tmodel.setUserName(userName);\n\t}\n\n\t/**\n\t * Sets the user uuid of this guestbook.\n\t *\n\t * @param userUuid the user uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t\tmodel.setUserUuid(userUuid);\n\t}\n\n\t/**\n\t * Sets the uuid of this guestbook.\n\t *\n\t * @param uuid the uuid of this guestbook\n\t */\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\tmodel.setUuid(uuid);\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn model.getStagedModelType();\n\t}\n\n\t@Override\n\tprotected GuestbookWrapper wrap(Guestbook guestbook) {\n\t\treturn new GuestbookWrapper(guestbook);\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for GuestbookEntry. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryLocalServiceUtil} to access the guestbook entry local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId);\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException;\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId);\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows SystemException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int status, int start, int end)\n\t\tthrows SystemException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> obc);\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbookEntriesCount(\n\t\t\tlong groupId, long guestbookId, int status)\n\t\tthrows SystemException;\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry);\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n\tpublic GuestbookEntry updateStatus(\n\t\t\tlong userId, long guestbookId, long entryId, int status,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\taddGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().addGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\taddGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\t\tString message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tcreateGuestbookEntry(long entryId) {\n\n\t\treturn getService().createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(\n\t\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tdeleteGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntry(long entryId) {\n\n\t\treturn getService().fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tint start, int end) {\n\n\t\treturn getService().getGuestbookEntries(start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId) {\n\n\t\treturn getService().getGuestbookEntries(groupId, guestbookId);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int status, int start, int end)\n\t\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, status, start, end);\n\t}\n\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> getGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn getService().getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List\n\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tString uuid, long companyId, int start, int end,\n\t\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbookEntriesByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int getGuestbookEntriesCount() {\n\t\treturn getService().getGuestbookEntriesCount();\n\t}\n\n\tpublic static int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn getService().getGuestbookEntriesCount(groupId, guestbookId);\n\t}\n\n\tpublic static int getGuestbookEntriesCount(\n\t\t\tlong groupId, long guestbookId, int status)\n\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().getGuestbookEntriesCount(\n\t\t\tgroupId, guestbookId, status);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntry(long entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookEntryByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tupdateGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn getService().updateGuestbookEntry(guestbookEntry);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tupdateGuestbookEntry(\n\t\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\t\tString email, String message,\n\t\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.GuestbookEntry updateStatus(\n\t\t\tlong userId, long guestbookId, long entryId, int status,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateStatus(\n\t\t\tuserId, guestbookId, entryId, status, serviceContext);\n\t}\n\n\tpublic static GuestbookEntryLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryLocalService.class);\n\n\t\tServiceTracker<GuestbookEntryLocalService, GuestbookEntryLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryLocalService, GuestbookEntryLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryLocalService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryLocalService}.\n *\n * @author Liferay\n * @see GuestbookEntryLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryLocalServiceWrapper\n\timplements GuestbookEntryLocalService,\n\t\t\t   ServiceWrapper<GuestbookEntryLocalService> {\n\n\tpublic GuestbookEntryLocalServiceWrapper(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry addGuestbookEntry(\n\t\t\tlong userId, long guestbookId, String name, String email,\n\t\t\tString message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.addGuestbookEntry(\n\t\t\tuserId, guestbookId, name, email, message, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry createGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.createGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(guestbookEntry);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry deleteGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookEntryLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookEntryLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry fetchGuestbookEntry(\n\t\tlong entryId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\tfetchGuestbookEntryByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookEntryLocalService.fetchGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookEntryLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(int start, int end) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(long groupId, long guestbookId) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int start, int end)\n\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\tgetGuestbookEntries(\n\t\t\t\tlong groupId, long guestbookId, int status, int start, int end)\n\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, status, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntries(\n\t\t\tlong groupId, long guestbookId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry> obc) {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntries(\n\t\t\tgroupId, guestbookId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.GuestbookEntry>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookEntryLocalService.\n\t\t\tgetGuestbookEntriesByUuidAndCompanyId(\n\t\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount(\n\t\t\tgroupId, guestbookId);\n\t}\n\n\t@Override\n\tpublic int getGuestbookEntriesCount(\n\t\t\tlong groupId, long guestbookId, int status)\n\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntriesCount(\n\t\t\tgroupId, guestbookId, status);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry getGuestbookEntry(\n\t\t\tlong entryId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntry(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry\n\t\t\tgetGuestbookEntryByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getGuestbookEntryByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookEntryLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookEntryLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\tcom.liferay.docs.guestbook.model.GuestbookEntry guestbookEntry) {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateGuestbookEntry(\n\t\t\tlong userId, long guestbookId, long entryId, String name,\n\t\t\tString email, String message,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.updateGuestbookEntry(\n\t\t\tuserId, guestbookId, entryId, name, email, message, serviceContext);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.GuestbookEntry updateStatus(\n\t\t\tlong userId, long guestbookId, long entryId, int status,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookEntryLocalService.updateStatus(\n\t\t\tuserId, guestbookId, entryId, status, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntryLocalService getWrappedService() {\n\t\treturn _guestbookEntryLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(\n\t\tGuestbookEntryLocalService guestbookEntryLocalService) {\n\n\t\t_guestbookEntryLocalService = guestbookEntryLocalService;\n\t}\n\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for GuestbookEntry. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookEntryService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryServiceUtil} to access the guestbook entry remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for GuestbookEntry. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookEntryService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookEntryService.class);\n\n\t\tServiceTracker<GuestbookEntryService, GuestbookEntryService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryService, GuestbookEntryService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookEntryService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookEntryServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookEntryService}.\n *\n * @author Liferay\n * @see GuestbookEntryService\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceWrapper\n\timplements GuestbookEntryService, ServiceWrapper<GuestbookEntryService> {\n\n\tpublic GuestbookEntryServiceWrapper(\n\t\tGuestbookEntryService guestbookEntryService) {\n\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookEntryService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookEntryService getWrappedService() {\n\t\treturn _guestbookEntryService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookEntryService guestbookEntryService) {\n\t\t_guestbookEntryService = guestbookEntryService;\n\t}\n\n\tprivate GuestbookEntryService _guestbookEntryService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalService;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Propagation;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the local service interface for Guestbook. Methods of this\n * service will not have security checks based on the propagated JAAS\n * credentials because this service can only be accessed from within the same\n * VM.\n *\n * @author Liferay\n * @see GuestbookLocalServiceUtil\n * @generated\n */\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookLocalService\n\textends BaseLocalService, PersistedModelLocalService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookLocalServiceUtil} to access the guestbook local service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(Guestbook guestbook);\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(\n\t\t\tlong userId, String name, ServiceContext serviceContext)\n\t\tthrows PortalException;\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId);\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook);\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException;\n\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(\n\t\t\tlong guestbookId, ServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic DynamicQuery dynamicQuery();\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end);\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery);\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbook(long guestbookId);\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ActionableDynamicQuery getActionableDynamicQuery();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tPortletDataContext portletDataContext);\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException;\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException;\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId, int status)\n\t\tthrows SystemException;\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooks(\n\t\tlong groupId, int start, int end, OrderByComparator<Guestbook> obc);\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator);\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount();\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic int getGuestbooksCount(long groupId);\n\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic IndexableActionableDynamicQuery getIndexableActionableDynamicQuery();\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n\t@Override\n\t@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException;\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(Guestbook guestbook);\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n\tpublic Guestbook updateStatus(\n\t\t\tlong userId, long guestbookId, int status,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the local service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and\n * is an access point for service operations in application layer code running\n * on the local server. Methods of this service will not have security checks\n * based on the propagated JAAS credentials because this service can only be\n * accessed from within the same VM.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().addGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().addGuestbook(userId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deleteGuestbook(guestbookId);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().deleteGuestbook(guestbookId, serviceContext);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tdeletePersistedModel(\n\t\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().deletePersistedModel(persistedModel);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.DynamicQuery\n\t\tdynamicQuery() {\n\n\t\treturn getService().dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn getService().dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\tpublic static <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn getService().dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\tpublic static long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn getService().dynamicQueryCount(dynamicQuery, projection);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn getService().fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn getService().fetchGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn getService().getActionableDynamicQuery();\n\t}\n\n\tpublic static com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn getService().getExportActionableDynamicQuery(portletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getGuestbookByUuidAndGroupId(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn getService().getGuestbooks(start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn getService().getGuestbooks(groupId);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\tgetGuestbooks(long groupId, int status)\n\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().getGuestbooks(groupId, status);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end);\n\t}\n\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn getService().getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\tpublic static java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn getService().getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int getGuestbooksCount() {\n\t\treturn getService().getGuestbooksCount();\n\t}\n\n\tpublic static int getGuestbooksCount(long groupId) {\n\t\treturn getService().getGuestbooksCount(groupId);\n\t}\n\n\tpublic static\n\t\tcom.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn getService().getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static com.liferay.portal.kernel.model.PersistedModel\n\t\t\tgetPersistedModel(java.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn getService().getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn getService().updateGuestbook(guestbook);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateGuestbook(\n\t\t\tuserId, guestbookId, name, serviceContext);\n\t}\n\n\tpublic static com.liferay.docs.guestbook.model.Guestbook updateStatus(\n\t\t\tlong userId, long guestbookId, int status,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn getService().updateStatus(\n\t\t\tuserId, guestbookId, status, serviceContext);\n\t}\n\n\tpublic static GuestbookLocalService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookLocalService.class);\n\n\t\tServiceTracker<GuestbookLocalService, GuestbookLocalService>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookLocalService, GuestbookLocalService>(\n\t\t\t\t\t\tbundle.getBundleContext(), GuestbookLocalService.class,\n\t\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookLocalServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookLocalService}.\n *\n * @author Liferay\n * @see GuestbookLocalService\n * @generated\n */\n@ProviderType\npublic class GuestbookLocalServiceWrapper\n\timplements GuestbookLocalService, ServiceWrapper<GuestbookLocalService> {\n\n\tpublic GuestbookLocalServiceWrapper(\n\t\tGuestbookLocalService guestbookLocalService) {\n\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.addGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook addGuestbook(\n\t\t\tlong userId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.addGuestbook(\n\t\t\tuserId, name, serviceContext);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook createGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.createGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbook);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(guestbookId);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook deleteGuestbook(\n\t\t\tlong guestbookId,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.deleteGuestbook(\n\t\t\tguestbookId, serviceContext);\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel deletePersistedModel(\n\t\t\tcom.liferay.portal.kernel.model.PersistedModel persistedModel)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.deletePersistedModel(persistedModel);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery() {\n\t\treturn _guestbookLocalService.dynamicQuery();\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> java.util.List<T> dynamicQuery(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery, int start,\n\t\tint end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<T> orderByComparator) {\n\n\t\treturn _guestbookLocalService.dynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tcom.liferay.portal.kernel.dao.orm.DynamicQuery dynamicQuery,\n\t\tcom.liferay.portal.kernel.dao.orm.Projection projection) {\n\n\t\treturn _guestbookLocalService.dynamicQueryCount(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook fetchGuestbook(\n\t\tlong guestbookId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\tfetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\n\t\treturn _guestbookLocalService.fetchGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery\n\t\tgetActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getActionableDynamicQuery();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery\n\t\tgetExportActionableDynamicQuery(\n\t\t\tcom.liferay.exportimport.kernel.lar.PortletDataContext\n\t\t\t\tportletDataContext) {\n\n\t\treturn _guestbookLocalService.getExportActionableDynamicQuery(\n\t\t\tportletDataContext);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook getGuestbook(\n\t\t\tlong guestbookId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbook(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook\n\t\t\tgetGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getGuestbookByUuidAndGroupId(\n\t\t\tuuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\tgetGuestbooks(long groupId, int status)\n\t\tthrows com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, status);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(long groupId, int start, int end) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end);\n\t}\n\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooks(\n\t\t\tlong groupId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook> obc) {\n\n\t\treturn _guestbookLocalService.getGuestbooks(groupId, start, end, obc);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(String uuid, long companyId) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic java.util.List<com.liferay.docs.guestbook.model.Guestbook>\n\t\tgetGuestbooksByUuidAndCompanyId(\n\t\t\tString uuid, long companyId, int start, int end,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator\n\t\t\t\t<com.liferay.docs.guestbook.model.Guestbook>\n\t\t\t\t\torderByComparator) {\n\n\t\treturn _guestbookLocalService.getGuestbooksByUuidAndCompanyId(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn _guestbookLocalService.getGuestbooksCount();\n\t}\n\n\t@Override\n\tpublic int getGuestbooksCount(long groupId) {\n\t\treturn _guestbookLocalService.getGuestbooksCount(groupId);\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\treturn _guestbookLocalService.getIndexableActionableDynamicQuery();\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookLocalService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic com.liferay.portal.kernel.model.PersistedModel getPersistedModel(\n\t\t\tjava.io.Serializable primaryKeyObj)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException {\n\n\t\treturn _guestbookLocalService.getPersistedModel(primaryKeyObj);\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\tcom.liferay.docs.guestbook.model.Guestbook guestbook) {\n\n\t\treturn _guestbookLocalService.updateGuestbook(guestbook);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateGuestbook(\n\t\t\tlong userId, long guestbookId, String name,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.updateGuestbook(\n\t\t\tuserId, guestbookId, name, serviceContext);\n\t}\n\n\t@Override\n\tpublic com.liferay.docs.guestbook.model.Guestbook updateStatus(\n\t\t\tlong userId, long guestbookId, int status,\n\t\t\tcom.liferay.portal.kernel.service.ServiceContext serviceContext)\n\t\tthrows com.liferay.portal.kernel.exception.PortalException,\n\t\t\t   com.liferay.portal.kernel.exception.SystemException {\n\n\t\treturn _guestbookLocalService.updateStatus(\n\t\t\tuserId, guestbookId, status, serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookLocalService getWrappedService() {\n\t\treturn _guestbookLocalService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookLocalService guestbookLocalService) {\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookService.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.jsonwebservice.JSONWebService;\nimport com.liferay.portal.kernel.security.access.control.AccessControlled;\nimport com.liferay.portal.kernel.service.BaseService;\nimport com.liferay.portal.kernel.transaction.Isolation;\nimport com.liferay.portal.kernel.transaction.Transactional;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the remote service interface for Guestbook. Methods of this\n * service are expected to have security checks based on the propagated JAAS\n * credentials because this service can be accessed remotely.\n *\n * @author Liferay\n * @see GuestbookServiceUtil\n * @generated\n */\n@AccessControlled\n@JSONWebService\n@ProviderType\n@Transactional(\n\tisolation = Isolation.PORTAL,\n\trollbackFor = {PortalException.class, SystemException.class}\n)\npublic interface GuestbookService extends BaseService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookServiceUtil} to access the guestbook remote service. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to automatically copy the method declarations to this interface.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic String getOSGiServiceIdentifier();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * Provides the remote service utility for Guestbook. This utility wraps\n * <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and is an\n * access point for service operations in application layer code running on a\n * remote server. Methods of this service are expected to have security checks\n * based on the propagated JAAS credentials because this service can be\n * accessed remotely.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Add custom service methods to <code>com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\tpublic static String getOSGiServiceIdentifier() {\n\t\treturn getService().getOSGiServiceIdentifier();\n\t}\n\n\tpublic static GuestbookService getService() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookService, GuestbookService>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookService.class);\n\n\t\tServiceTracker<GuestbookService, GuestbookService> serviceTracker =\n\t\t\tnew ServiceTracker<GuestbookService, GuestbookService>(\n\t\t\t\tbundle.getBundleContext(), GuestbookService.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/GuestbookServiceWrapper.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service;\n\nimport com.liferay.portal.kernel.service.ServiceWrapper;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides a wrapper for {@link GuestbookService}.\n *\n * @author Liferay\n * @see GuestbookService\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceWrapper\n\timplements GuestbookService, ServiceWrapper<GuestbookService> {\n\n\tpublic GuestbookServiceWrapper(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn _guestbookService.getOSGiServiceIdentifier();\n\t}\n\n\t@Override\n\tpublic GuestbookService getWrappedService() {\n\t\treturn _guestbookService;\n\t}\n\n\t@Override\n\tpublic void setWrappedService(GuestbookService guestbookService) {\n\t\t_guestbookService = guestbookService;\n\t}\n\n\tprivate GuestbookService _guestbookService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookEntryPersistence\n\textends BasePersistence<GuestbookEntry> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookEntryUtil} to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId);\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId);\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] filterFindByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic void removeByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\tpublic int filterCountByG_G(long groupId, long guestbookId);\n\n\t/**\n\t * Returns all the guestbook entries where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByStatus(int status);\n\n\t/**\n\t * Returns a range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByStatus_First(\n\t\t\tint status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByStatus_First(\n\t\tint status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByStatus_Last(\n\t\t\tint status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByStatus_Last(\n\t\tint status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByStatus_PrevAndNext(\n\t\t\tlong entryId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where status = &#63; from the database.\n\t *\n\t * @param status the status\n\t */\n\tpublic void removeByStatus(int status);\n\n\t/**\n\t * Returns the number of guestbook entries where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByStatus(int status);\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @return the matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G_S(\n\t\tlong groupId, long guestbookId, int status);\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_S_First(\n\t\t\tlong groupId, long guestbookId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_S_First(\n\t\tlong groupId, long guestbookId, int status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry findByG_G_S_Last(\n\t\t\tlong groupId, long guestbookId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic GuestbookEntry fetchByG_G_S_Last(\n\t\tlong groupId, long guestbookId, int status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] findByG_G_S_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_G_S(\n\t\tlong groupId, long guestbookId, int status);\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic java.util.List<GuestbookEntry> filterFindByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry[] filterFindByG_G_S_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t */\n\tpublic void removeByG_G_S(long groupId, long guestbookId, int status);\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic int countByG_G_S(long groupId, long guestbookId, int status);\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\tpublic int filterCountByG_G_S(long groupId, long guestbookId, int status);\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic void cacheResult(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic void cacheResult(java.util.List<GuestbookEntry> guestbookEntries);\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic GuestbookEntry create(long entryId);\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry);\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException;\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId);\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll();\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic java.util.List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<GuestbookEntry>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookEntryUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook entry service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookEntryPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().clearCache(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, GuestbookEntry> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<GuestbookEntry> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static GuestbookEntry update(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().update(guestbookEntry);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static GuestbookEntry update(\n\t\tGuestbookEntry guestbookEntry, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbookEntry, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tentryId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\tpublic static GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tentryId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn getPersistence().findByG_G(groupId, guestbookId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator,\n\t\t\tretrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_PrevAndNext(\n\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn getPersistence().filterFindByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn getPersistence().filterFindByG_G(\n\t\t\tgroupId, guestbookId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().filterFindByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] filterFindByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().filterFindByG_G_PrevAndNext(\n\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\tpublic static void removeByG_G(long groupId, long guestbookId) {\n\t\tgetPersistence().removeByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByG_G(long groupId, long guestbookId) {\n\t\treturn getPersistence().countByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static int filterCountByG_G(long groupId, long guestbookId) {\n\t\treturn getPersistence().filterCountByG_G(groupId, guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByStatus(int status) {\n\t\treturn getPersistence().findByStatus(status);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end) {\n\n\t\treturn getPersistence().findByStatus(status, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByStatus(\n\t\t\tstatus, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByStatus(\n\t\t\tstatus, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByStatus_First(\n\t\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByStatus_First(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByStatus_First(\n\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByStatus_First(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByStatus_Last(\n\t\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByStatus_Last(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByStatus_Last(\n\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByStatus_Last(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByStatus_PrevAndNext(\n\t\t\tlong entryId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByStatus_PrevAndNext(\n\t\t\tentryId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where status = &#63; from the database.\n\t *\n\t * @param status the status\n\t */\n\tpublic static void removeByStatus(int status) {\n\t\tgetPersistence().removeByStatus(status);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByStatus(int status) {\n\t\treturn getPersistence().countByStatus(status);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @return the matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G_S(\n\t\tlong groupId, long guestbookId, int status) {\n\n\t\treturn getPersistence().findByG_G_S(groupId, guestbookId, status);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end) {\n\n\t\treturn getPersistence().findByG_G_S(\n\t\t\tgroupId, guestbookId, status, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findByG_G_S(\n\t\t\tgroupId, guestbookId, status, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByG_G_S(\n\t\t\tgroupId, guestbookId, status, start, end, orderByComparator,\n\t\t\tretrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_S_First(\n\t\t\tlong groupId, long guestbookId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_S_First(\n\t\t\tgroupId, guestbookId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_S_First(\n\t\tlong groupId, long guestbookId, int status,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_S_First(\n\t\t\tgroupId, guestbookId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry findByG_G_S_Last(\n\t\t\tlong groupId, long guestbookId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_S_Last(\n\t\t\tgroupId, guestbookId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\tpublic static GuestbookEntry fetchByG_G_S_Last(\n\t\tlong groupId, long guestbookId, int status,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_G_S_Last(\n\t\t\tgroupId, guestbookId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] findByG_G_S_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByG_G_S_PrevAndNext(\n\t\t\tentryId, groupId, guestbookId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_G_S(\n\t\tlong groupId, long guestbookId, int status) {\n\n\t\treturn getPersistence().filterFindByG_G_S(groupId, guestbookId, status);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end) {\n\n\t\treturn getPersistence().filterFindByG_G_S(\n\t\t\tgroupId, guestbookId, status, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static List<GuestbookEntry> filterFindByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().filterFindByG_G_S(\n\t\t\tgroupId, guestbookId, status, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry[] filterFindByG_G_S_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().filterFindByG_G_S_PrevAndNext(\n\t\t\tentryId, groupId, guestbookId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t */\n\tpublic static void removeByG_G_S(\n\t\tlong groupId, long guestbookId, int status) {\n\n\t\tgetPersistence().removeByG_G_S(groupId, guestbookId, status);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @return the number of matching guestbook entries\n\t */\n\tpublic static int countByG_G_S(long groupId, long guestbookId, int status) {\n\t\treturn getPersistence().countByG_G_S(groupId, guestbookId, status);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\tpublic static int filterCountByG_G_S(\n\t\tlong groupId, long guestbookId, int status) {\n\n\t\treturn getPersistence().filterCountByG_G_S(\n\t\t\tgroupId, guestbookId, status);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\tpublic static void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tgetPersistence().cacheResult(guestbookEntry);\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\tpublic static void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tgetPersistence().cacheResult(guestbookEntries);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\tpublic static GuestbookEntry create(long entryId) {\n\t\treturn getPersistence().create(entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry remove(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().remove(entryId);\n\t}\n\n\tpublic static GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\treturn getPersistence().updateImpl(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows com.liferay.docs.guestbook.exception.\n\t\t\tNoSuchGuestbookEntryException {\n\n\t\treturn getPersistence().findByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\tpublic static GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn getPersistence().fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\tpublic static List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookEntryPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker\n\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence> _serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(\n\t\t\tGuestbookEntryPersistence.class);\n\n\t\tServiceTracker<GuestbookEntryPersistence, GuestbookEntryPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker\n\t\t\t\t\t<GuestbookEntryPersistence, GuestbookEntryPersistence>(\n\t\t\t\t\t\tbundle.getBundleContext(),\n\t\t\t\t\t\tGuestbookEntryPersistence.class, null);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookPersistence.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.service.persistence.BasePersistence;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The persistence interface for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookUtil\n * @generated\n */\n@ProviderType\npublic interface GuestbookPersistence extends BasePersistence<Guestbook> {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this interface directly. Always use {@link GuestbookUtil} to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this interface.\n\t */\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(String uuid);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic void removeByUuid(String uuid);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid(String uuid);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache);\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUUID_G(String uuid, long groupId);\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic void removeByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByUuid_C(String uuid, long companyId);\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(long groupId);\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByGroupId(long groupId);\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] filterFindByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic void removeByGroupId(long groupId);\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByGroupId(long groupId);\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\tpublic int filterCountByGroupId(long groupId);\n\n\t/**\n\t * Returns all the guestbooks where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByStatus(int status);\n\n\t/**\n\t * Returns a range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByStatus(\n\t\tint status, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByStatus(\n\t\tint status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByStatus(\n\t\tint status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByStatus_First(\n\t\t\tint status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByStatus_First(\n\t\tint status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByStatus_Last(\n\t\t\tint status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByStatus_Last(\n\t\tint status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByStatus_PrevAndNext(\n\t\t\tlong guestbookId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where status = &#63; from the database.\n\t *\n\t * @param status the status\n\t */\n\tpublic void removeByStatus(int status);\n\n\t/**\n\t * Returns the number of guestbooks where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByStatus(int status);\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByG_S(long groupId, int status);\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByG_S_First(\n\t\t\tlong groupId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByG_S_First(\n\t\tlong groupId, int status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic Guestbook findByG_S_Last(\n\t\t\tlong groupId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic Guestbook fetchByG_S_Last(\n\t\tlong groupId, int status,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] findByG_S_PrevAndNext(\n\t\t\tlong guestbookId, long groupId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByG_S(long groupId, int status);\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\tpublic java.util.List<Guestbook> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook[] filterFindByG_S_PrevAndNext(\n\t\t\tlong guestbookId, long groupId, int status,\n\t\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\t\torderByComparator)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; and status = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t */\n\tpublic void removeByG_S(long groupId, int status);\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbooks\n\t */\n\tpublic int countByG_S(long groupId, int status);\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\tpublic int filterCountByG_S(long groupId, int status);\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic void cacheResult(Guestbook guestbook);\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic void cacheResult(java.util.List<Guestbook> guestbooks);\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic Guestbook create(long guestbookId);\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException;\n\n\tpublic Guestbook updateImpl(Guestbook guestbook);\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException;\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId);\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll();\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(int start, int end);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator);\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic java.util.List<Guestbook> findAll(\n\t\tint start, int end,\n\t\tcom.liferay.portal.kernel.util.OrderByComparator<Guestbook>\n\t\t\torderByComparator,\n\t\tboolean retrieveFromCache);\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic void removeAll();\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic int countAll();\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-api/src/main/java/com/liferay/docs/guestbook/service/persistence/GuestbookUtil.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.OrderByComparator;\n\nimport java.io.Serializable;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\n/**\n * The persistence utility for the guestbook service. This utility wraps <code>com.liferay.docs.guestbook.service.persistence.impl.GuestbookPersistenceImpl</code> and provides direct access to the database for CRUD operations. This utility should only be used by the service layer, as it must operate within a transaction. Never access this utility in a JSP, controller, model, or other front-end class.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @see GuestbookPersistence\n * @generated\n */\n@ProviderType\npublic class GuestbookUtil {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify this class directly. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache()\n\t */\n\tpublic static void clearCache() {\n\t\tgetPersistence().clearCache();\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#clearCache(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static void clearCache(Guestbook guestbook) {\n\t\tgetPersistence().clearCache(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#countWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static long countWithDynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn getPersistence().countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#fetchByPrimaryKeys(Set)\n\t */\n\tpublic static Map<Serializable, Guestbook> fetchByPrimaryKeys(\n\t\tSet<Serializable> primaryKeys) {\n\n\t\treturn getPersistence().fetchByPrimaryKeys(primaryKeys);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn getPersistence().findWithDynamicQuery(dynamicQuery, start, end);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#findWithDynamicQuery(DynamicQuery, int, int, OrderByComparator)\n\t */\n\tpublic static List<Guestbook> findWithDynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel)\n\t */\n\tpublic static Guestbook update(Guestbook guestbook) {\n\t\treturn getPersistence().update(guestbook);\n\t}\n\n\t/**\n\t * @see com.liferay.portal.kernel.service.persistence.BasePersistence#update(com.liferay.portal.kernel.model.BaseModel, ServiceContext)\n\t */\n\tpublic static Guestbook update(\n\t\tGuestbook guestbook, ServiceContext serviceContext) {\n\n\t\treturn getPersistence().update(guestbook, serviceContext);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid) {\n\t\treturn getPersistence().findByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn getPersistence().findByUuid(uuid, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid(uuid, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid(\n\t\t\tuuid, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_First(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_Last(uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_PrevAndNext(\n\t\t\tguestbookId, uuid, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\tpublic static void removeByUuid(String uuid) {\n\t\tgetPersistence().removeByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid(String uuid) {\n\t\treturn getPersistence().countByUuid(uuid);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\treturn getPersistence().fetchByUUID_G(uuid, groupId, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\tpublic static Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().removeByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUUID_G(String uuid, long groupId) {\n\t\treturn getPersistence().countByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn getPersistence().findByUuid_C(uuid, companyId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByUuid_C_PrevAndNext(\n\t\t\tguestbookId, uuid, companyId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\tpublic static void removeByUuid_C(String uuid, long companyId) {\n\t\tgetPersistence().removeByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByUuid_C(String uuid, long companyId) {\n\t\treturn getPersistence().countByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(long groupId) {\n\t\treturn getPersistence().findByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn getPersistence().findByGroupId(groupId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByGroupId(\n\t\t\tgroupId, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_First(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_First(\n\t\t\tgroupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByGroupId_Last(groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByGroupId_PrevAndNext(\n\t\t\tguestbookId, groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByGroupId(long groupId) {\n\t\treturn getPersistence().filterFindByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn getPersistence().filterFindByGroupId(groupId, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().filterFindByGroupId(\n\t\t\tgroupId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] filterFindByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().filterFindByGroupId_PrevAndNext(\n\t\t\tguestbookId, groupId, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\tpublic static void removeByGroupId(long groupId) {\n\t\tgetPersistence().removeByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByGroupId(long groupId) {\n\t\treturn getPersistence().countByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\tpublic static int filterCountByGroupId(long groupId) {\n\t\treturn getPersistence().filterCountByGroupId(groupId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByStatus(int status) {\n\t\treturn getPersistence().findByStatus(status);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByStatus(int status, int start, int end) {\n\t\treturn getPersistence().findByStatus(status, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByStatus(\n\t\t\tstatus, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByStatus(\n\t\t\tstatus, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByStatus_First(\n\t\t\tint status, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByStatus_First(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByStatus_First(\n\t\tint status, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByStatus_First(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByStatus_Last(\n\t\t\tint status, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByStatus_Last(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByStatus_Last(\n\t\tint status, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByStatus_Last(status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByStatus_PrevAndNext(\n\t\t\tlong guestbookId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByStatus_PrevAndNext(\n\t\t\tguestbookId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where status = &#63; from the database.\n\t *\n\t * @param status the status\n\t */\n\tpublic static void removeByStatus(int status) {\n\t\tgetPersistence().removeByStatus(status);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByStatus(int status) {\n\t\treturn getPersistence().countByStatus(status);\n\t}\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByG_S(long groupId, int status) {\n\t\treturn getPersistence().findByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end) {\n\n\t\treturn getPersistence().findByG_S(groupId, status, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findByG_S(\n\t\t\tgroupId, status, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\tpublic static List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findByG_S(\n\t\t\tgroupId, status, start, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByG_S_First(\n\t\t\tlong groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByG_S_First(\n\t\t\tgroupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByG_S_First(\n\t\tlong groupId, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_S_First(\n\t\t\tgroupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook findByG_S_Last(\n\t\t\tlong groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByG_S_Last(\n\t\t\tgroupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\tpublic static Guestbook fetchByG_S_Last(\n\t\tlong groupId, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().fetchByG_S_Last(\n\t\t\tgroupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] findByG_S_PrevAndNext(\n\t\t\tlong guestbookId, long groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByG_S_PrevAndNext(\n\t\t\tguestbookId, groupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByG_S(long groupId, int status) {\n\t\treturn getPersistence().filterFindByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end) {\n\n\t\treturn getPersistence().filterFindByG_S(groupId, status, start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\tpublic static List<Guestbook> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().filterFindByG_S(\n\t\t\tgroupId, status, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook[] filterFindByG_S_PrevAndNext(\n\t\t\tlong guestbookId, long groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().filterFindByG_S_PrevAndNext(\n\t\t\tguestbookId, groupId, status, orderByComparator);\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; and status = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t */\n\tpublic static void removeByG_S(long groupId, int status) {\n\t\tgetPersistence().removeByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbooks\n\t */\n\tpublic static int countByG_S(long groupId, int status) {\n\t\treturn getPersistence().countByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\tpublic static int filterCountByG_S(long groupId, int status) {\n\t\treturn getPersistence().filterCountByG_S(groupId, status);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\tpublic static void cacheResult(Guestbook guestbook) {\n\t\tgetPersistence().cacheResult(guestbook);\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\tpublic static void cacheResult(List<Guestbook> guestbooks) {\n\t\tgetPersistence().cacheResult(guestbooks);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\tpublic static Guestbook create(long guestbookId) {\n\t\treturn getPersistence().create(guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook remove(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().remove(guestbookId);\n\t}\n\n\tpublic static Guestbook updateImpl(Guestbook guestbook) {\n\t\treturn getPersistence().updateImpl(guestbook);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows com.liferay.docs.guestbook.exception.NoSuchGuestbookException {\n\n\t\treturn getPersistence().findByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\tpublic static Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn getPersistence().fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\tpublic static List<Guestbook> findAll() {\n\t\treturn getPersistence().findAll();\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(int start, int end) {\n\t\treturn getPersistence().findAll(start, end);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn getPersistence().findAll(start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\tpublic static List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\treturn getPersistence().findAll(\n\t\t\tstart, end, orderByComparator, retrieveFromCache);\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t */\n\tpublic static void removeAll() {\n\t\tgetPersistence().removeAll();\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\tpublic static int countAll() {\n\t\treturn getPersistence().countAll();\n\t}\n\n\tpublic static GuestbookPersistence getPersistence() {\n\t\treturn _serviceTracker.getService();\n\t}\n\n\tprivate static ServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t_serviceTracker;\n\n\tstatic {\n\t\tBundle bundle = FrameworkUtil.getBundle(GuestbookPersistence.class);\n\n\t\tServiceTracker<GuestbookPersistence, GuestbookPersistence>\n\t\t\tserviceTracker =\n\t\t\t\tnew ServiceTracker<GuestbookPersistence, GuestbookPersistence>(\n\t\t\t\t\tbundle.getBundleContext(), GuestbookPersistence.class,\n\t\t\t\t\tnull);\n\n\t\tserviceTracker.open();\n\n\t\t_serviceTracker = serviceTracker;\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/bnd.bnd",
    "content": "Bundle-Name: guestbook-service\nBundle-SymbolicName: com.liferay.docs.guestbook.service\nBundle-Version: 1.0.0\nLiferay-Require-SchemaVersion: 1.0.0\nLiferay-Service: true\nExport-Package: com.liferay.docs.guestbook.search\n-dsannotations-options: inherit"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.lang\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.portal.aop.api\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.function\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.model.adapter\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.reflect\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.spi\"\n    compileOnly group: \"com.liferay\", name: \"com.liferay.portal.search.api\"\n\tcompileOnly group: \"javax.portlet\", name: \"portlet-api\"\n    compileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.annotation.versioning\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.core\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\tcompileOnly project(\":modules:guestbook:guestbook-api\")\n}\n\nbuildService {\n\tapiDir = \"../guestbook-api/src/main/java\"\n}\n\ngroup = \"com.liferay.docs.guestbook\""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/service.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE service-builder PUBLIC \"-//Liferay//DTD Service Builder 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-service-builder_7_2_0.dtd\">\n\n<service-builder dependency-injector=\"ds\" package-path=\"com.liferay.docs.guestbook\">\n\t<author>Liferay</author>\n\t<namespace>GB</namespace>\n\t\n\t<entity name=\"Guestbook\" local-service=\"true\" uuid=\"true\" remote-service=\"true\">\n\t\t\n\t\t<!-- Guestbook fields -->\n\n\t\t<column name=\"guestbookId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\t\t\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<finder name=\"GroupId\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"groupId\" />\n\t\t</finder>\n\n\t\t<finder name=\"Status\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"status\" />\n\t\t</finder>\n\n\t\t<finder name=\"G_S\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"groupId\" />\n\t\t\t<finder-column name=\"status\" />\n\t\t</finder>\n\n\t\t<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetEntry\" />\n\t\t<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetLink\" />\n\t\t<reference entity=\"WorkflowInstanceLink\" package-path=\"com.liferay.portal\" />\n\n\t</entity>\n\t\n    <entity name=\"GuestbookEntry\" local-service=\"true\" remote-service=\"true\" uuid=\"true\">\n\n\t\t<!-- Guestbook Entry fields -->\n\n\t\t<column name=\"entryId\" primary=\"true\" type=\"long\" />\n\t\t<column name=\"name\" type=\"String\" />\n\t\t<column name=\"email\" type=\"String\" />\n\t\t<column name=\"message\" type=\"String\" />\n\t\t<column name=\"guestbookId\" type=\"long\" />\n\n\t\t<!-- Group instance -->\n\n\t\t<column name=\"groupId\" type=\"long\" />\n\t\t<column name=\"companyId\" type=\"long\" />\n\n\t\t<!-- Audit fields -->\n\n\t\t<column name=\"userId\" type=\"long\" />\n\t\t<column name=\"userName\" type=\"String\" />\n\t\t<column name=\"createDate\" type=\"Date\" />\n\t\t<column name=\"modifiedDate\" type=\"Date\" />\n\n\t\t<!-- Status fields -->\n\t   \n\t\t<column name=\"status\" type=\"int\" />\n\t\t<column name=\"statusByUserId\" type=\"long\" />\n\t\t<column name=\"statusByUserName\" type=\"String\" />\n\t\t<column name=\"statusDate\" type=\"Date\" />\n\n\t\t<order>\n\t\t\t<order-column name=\"createDate\" order-by=\"desc\" />\n\t\t</order>\n\n        <finder name=\"G_G\" return-type=\"Collection\">\n            <finder-column name=\"groupId\" />\n            <finder-column name=\"guestbookId\" />\n        </finder>\n\n\t\t<finder name=\"Status\" return-type=\"Collection\">\n\t\t\t<finder-column name=\"status\" />\n\t\t</finder>\n\n\t   <finder name=\"G_G_S\" return-type=\"Collection\">\n\t\t  <finder-column name=\"groupId\" />\n\t\t  <finder-column name=\"guestbookId\" />\n\t\t  <finder-column name=\"status\" />\n\t   </finder>\n\n\t\t<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetEntry\" />\n\t\t<reference package-path=\"com.liferay.portlet.asset\" entity=\"AssetLink\" />\n\t\t<reference entity=\"WorkflowInstanceLink\" package-path=\"com.liferay.portal\" />\n\n    </entity>\n\n    <exceptions>\n        <exception>GuestbookEntryEmail</exception>\n        <exception>GuestbookEntryMessage</exception>\n        <exception>GuestbookEntryName</exception>\n        <exception>GuestbookName</exception>\n    </exceptions>\n</service-builder>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/internal/security/permission/resource/GuestbookEntryModelResourcePermissionRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.StagedModelPermissionLogic;\nimport com.liferay.portal.kernel.security.permission.resource.WorkflowedModelPermissionLogic;\nimport com.liferay.portal.kernel.service.GroupLocalService;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\nimport com.liferay.portal.kernel.workflow.permission.WorkflowPermission;\n\n@Component(immediate = true)\npublic class GuestbookEntryModelResourcePermissionRegistrar {\n\n @Activate\n\tpublic void activate(BundleContext bundleContext) {\n\t\tDictionary<String, Object> properties = new HashMapDictionary<>();\n\n\t\tproperties.put(\"model.class.name\", GuestbookEntry.class.getName());\n\n\t\t_serviceRegistration = bundleContext.registerService(\n\t\t\tModelResourcePermission.class,\n\t\t\tModelResourcePermissionFactory.create(\n\t\t\t\tGuestbookEntry.class, GuestbookEntry::getEntryId,\n\t\t\t\t_guestbookEntryLocalService::getGuestbookEntry, _portletResourcePermission,\n\t\t\t\t(modelResourcePermission, consumer) -> {\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew StagedModelPermissionLogic<>(\n\t\t\t\t\t\t\t_stagingPermission, GuestbookPortletKeys.GUESTBOOK,\n\t\t\t\t\t\t\tGuestbookEntry::getEntryId));\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew WorkflowedModelPermissionLogic<>(\n\t\t\t\t\t\t\t\t_workflowPermission, modelResourcePermission,\n\t\t\t\t\t\t\t\t_groupLocalService, GuestbookEntry::getEntryId));\n\t\t\t\t}),\n\t\t\tproperties);\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n\t@Reference(target = \"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\")\n\tprivate PortletResourcePermission _portletResourcePermission;\n\n\tprivate ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n\t@Reference\n\tprivate StagingPermission _stagingPermission;\n\n\t@Reference\n\tprivate WorkflowPermission _workflowPermission;\n\t\n\t@Reference\n\tprivate GroupLocalService _groupLocalService;\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/internal/security/permission/resource/GuestbookModelResourcePermissionRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.StagedModelPermissionLogic;\nimport com.liferay.portal.kernel.security.permission.resource.WorkflowedModelPermissionLogic;\nimport com.liferay.portal.kernel.service.GroupLocalService;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\nimport com.liferay.portal.kernel.workflow.permission.WorkflowPermission;\n\n@Component (immediate=true)\npublic class GuestbookModelResourcePermissionRegistrar {\n\n @Activate\n\tpublic void activate(BundleContext bundleContext) {\n\t\tDictionary<String, Object> properties = new HashMapDictionary<>();\n\n\t\tproperties.put(\"model.class.name\", Guestbook.class.getName());\n\n\t\t_serviceRegistration = bundleContext.registerService(\n\t\t\tModelResourcePermission.class,\n\t\t\tModelResourcePermissionFactory.create(\n\t\t\t\tGuestbook.class, Guestbook::getGuestbookId,\n\t\t\t\t_guestbookLocalService::getGuestbook, _portletResourcePermission,\n\t\t\t\t(modelResourcePermission, consumer) -> {\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew StagedModelPermissionLogic<>(\n\t\t\t\t\t\t\t_stagingPermission, GuestbookPortletKeys.GUESTBOOK,\n\t\t\t\t\t\t\tGuestbook::getGuestbookId));\n\t\t\t\t\tconsumer.accept(\n\t\t\t\t\t\tnew WorkflowedModelPermissionLogic<>(\n\t\t\t\t\t\t\t\t_workflowPermission, modelResourcePermission,\n\t\t\t\t\t\t\t\t_groupLocalService, Guestbook::getGuestbookId));\n\t\t\t\t}),\n\t\t\tproperties);\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n\t@Reference(target = \"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\")\n\tprivate PortletResourcePermission _portletResourcePermission;\n\n\tprivate ServiceRegistration<ModelResourcePermission> _serviceRegistration;\n\n\t@Reference\n\tprivate StagingPermission _stagingPermission;\n\n\t@Reference\n\tprivate WorkflowPermission _workflowPermission;\n\t\n\t@Reference\n\tprivate GroupLocalService _groupLocalService;\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/internal/security/permission/resource/GuestbookPortletResourcePermissionRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.internal.security.permission.resource;\n\nimport java.util.Dictionary;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.exportimport.kernel.staging.permission.StagingPermission;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermissionFactory;\nimport com.liferay.portal.kernel.security.permission.resource.StagedPortletPermissionLogic;\nimport com.liferay.portal.kernel.util.HashMapDictionary;\n\n@Component (immediate = true)\npublic class GuestbookPortletResourcePermissionRegistrar {\n\t\n\t\t@Activate\n\tpublic void activate(BundleContext bundleContext) {\n\t\tDictionary<String, Object> properties = new HashMapDictionary<>();\n\n\t\tproperties.put(\"resource.name\", GuestbookConstants.RESOURCE_NAME);\n\n\t\t_serviceRegistration = bundleContext.registerService(\n\t\t\tPortletResourcePermission.class,\n\t\t\tPortletResourcePermissionFactory.create(\n\t\t\t\tGuestbookConstants.RESOURCE_NAME,\n\t\t\t\tnew StagedPortletPermissionLogic(\n\t\t\t\t\t_stagingPermission, GuestbookPortletKeys.GUESTBOOK)),\n\t\t\tproperties);\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\tprivate ServiceRegistration<PortletResourcePermission> _serviceRegistration;\n\n\t@Reference\n\tprivate StagingPermission _stagingPermission;\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @see Guestbook\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookBaseImpl\n\textends GuestbookModelImpl implements Guestbook {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookLocalServiceUtil.addGuestbook(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookLocalServiceUtil.updateGuestbook(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing Guestbook in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookCacheModel\n\timplements CacheModel<Guestbook>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookCacheModel guestbookCacheModel = (GuestbookCacheModel)obj;\n\n\t\tif (guestbookId == guestbookCacheModel.guestbookId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, guestbookId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(27);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic Guestbook toEntityModel() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookImpl.setGuestbookId(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tguestbookImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setName(name);\n\t\t}\n\n\t\tguestbookImpl.setGroupId(groupId);\n\t\tguestbookImpl.setCompanyId(companyId);\n\t\tguestbookImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookImpl.setStatus(status);\n\t\tguestbookImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long guestbookId;\n\tpublic String name;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model base implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This class exists only as a container for the default extended model level methods generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @see GuestbookEntry\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryBaseImpl\n\textends GuestbookEntryModelImpl implements GuestbookEntry {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\t@Override\n\tpublic void persist() {\n\t\tif (this.isNew()) {\n\t\t\tGuestbookEntryLocalServiceUtil.addGuestbookEntry(this);\n\t\t}\n\t\telse {\n\t\t\tGuestbookEntryLocalServiceUtil.updateGuestbookEntry(this);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryCacheModel.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.petra.lang.HashUtil;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.model.CacheModel;\n\nimport java.io.Externalizable;\nimport java.io.IOException;\nimport java.io.ObjectInput;\nimport java.io.ObjectOutput;\n\nimport java.util.Date;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The cache model class for representing GuestbookEntry in entity cache.\n *\n * @author Liferay\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryCacheModel\n\timplements CacheModel<GuestbookEntry>, Externalizable {\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntryCacheModel)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\t(GuestbookEntryCacheModel)obj;\n\n\t\tif (entryId == guestbookEntryCacheModel.entryId) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn HashUtil.hash(0, entryId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBundler sb = new StringBundler(33);\n\n\t\tsb.append(\"{uuid=\");\n\t\tsb.append(uuid);\n\t\tsb.append(\", entryId=\");\n\t\tsb.append(entryId);\n\t\tsb.append(\", name=\");\n\t\tsb.append(name);\n\t\tsb.append(\", email=\");\n\t\tsb.append(email);\n\t\tsb.append(\", message=\");\n\t\tsb.append(message);\n\t\tsb.append(\", guestbookId=\");\n\t\tsb.append(guestbookId);\n\t\tsb.append(\", groupId=\");\n\t\tsb.append(groupId);\n\t\tsb.append(\", companyId=\");\n\t\tsb.append(companyId);\n\t\tsb.append(\", userId=\");\n\t\tsb.append(userId);\n\t\tsb.append(\", userName=\");\n\t\tsb.append(userName);\n\t\tsb.append(\", createDate=\");\n\t\tsb.append(createDate);\n\t\tsb.append(\", modifiedDate=\");\n\t\tsb.append(modifiedDate);\n\t\tsb.append(\", status=\");\n\t\tsb.append(status);\n\t\tsb.append(\", statusByUserId=\");\n\t\tsb.append(statusByUserId);\n\t\tsb.append(\", statusByUserName=\");\n\t\tsb.append(statusByUserName);\n\t\tsb.append(\", statusDate=\");\n\t\tsb.append(statusDate);\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEntityModel() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tif (uuid == null) {\n\t\t\tguestbookEntryImpl.setUuid(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUuid(uuid);\n\t\t}\n\n\t\tguestbookEntryImpl.setEntryId(entryId);\n\n\t\tif (name == null) {\n\t\t\tguestbookEntryImpl.setName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setName(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tguestbookEntryImpl.setEmail(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setEmail(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tguestbookEntryImpl.setMessage(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setMessage(message);\n\t\t}\n\n\t\tguestbookEntryImpl.setGuestbookId(guestbookId);\n\t\tguestbookEntryImpl.setGroupId(groupId);\n\t\tguestbookEntryImpl.setCompanyId(companyId);\n\t\tguestbookEntryImpl.setUserId(userId);\n\n\t\tif (userName == null) {\n\t\t\tguestbookEntryImpl.setUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setUserName(userName);\n\t\t}\n\n\t\tif (createDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setCreateDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setCreateDate(new Date(createDate));\n\t\t}\n\n\t\tif (modifiedDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setModifiedDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setModifiedDate(new Date(modifiedDate));\n\t\t}\n\n\t\tguestbookEntryImpl.setStatus(status);\n\t\tguestbookEntryImpl.setStatusByUserId(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tguestbookEntryImpl.setStatusByUserName(\"\");\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusByUserName(statusByUserName);\n\t\t}\n\n\t\tif (statusDate == Long.MIN_VALUE) {\n\t\t\tguestbookEntryImpl.setStatusDate(null);\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryImpl.setStatusDate(new Date(statusDate));\n\t\t}\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic void readExternal(ObjectInput objectInput) throws IOException {\n\t\tuuid = objectInput.readUTF();\n\n\t\tentryId = objectInput.readLong();\n\t\tname = objectInput.readUTF();\n\t\temail = objectInput.readUTF();\n\t\tmessage = objectInput.readUTF();\n\n\t\tguestbookId = objectInput.readLong();\n\n\t\tgroupId = objectInput.readLong();\n\n\t\tcompanyId = objectInput.readLong();\n\n\t\tuserId = objectInput.readLong();\n\t\tuserName = objectInput.readUTF();\n\t\tcreateDate = objectInput.readLong();\n\t\tmodifiedDate = objectInput.readLong();\n\n\t\tstatus = objectInput.readInt();\n\n\t\tstatusByUserId = objectInput.readLong();\n\t\tstatusByUserName = objectInput.readUTF();\n\t\tstatusDate = objectInput.readLong();\n\t}\n\n\t@Override\n\tpublic void writeExternal(ObjectOutput objectOutput) throws IOException {\n\t\tif (uuid == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(uuid);\n\t\t}\n\n\t\tobjectOutput.writeLong(entryId);\n\n\t\tif (name == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(name);\n\t\t}\n\n\t\tif (email == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(email);\n\t\t}\n\n\t\tif (message == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(message);\n\t\t}\n\n\t\tobjectOutput.writeLong(guestbookId);\n\n\t\tobjectOutput.writeLong(groupId);\n\n\t\tobjectOutput.writeLong(companyId);\n\n\t\tobjectOutput.writeLong(userId);\n\n\t\tif (userName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(userName);\n\t\t}\n\n\t\tobjectOutput.writeLong(createDate);\n\t\tobjectOutput.writeLong(modifiedDate);\n\n\t\tobjectOutput.writeInt(status);\n\n\t\tobjectOutput.writeLong(statusByUserId);\n\n\t\tif (statusByUserName == null) {\n\t\t\tobjectOutput.writeUTF(\"\");\n\t\t}\n\t\telse {\n\t\t\tobjectOutput.writeUTF(statusByUserName);\n\t\t}\n\n\t\tobjectOutput.writeLong(statusDate);\n\t}\n\n\tpublic String uuid;\n\tpublic long entryId;\n\tpublic String name;\n\tpublic String email;\n\tpublic String message;\n\tpublic long guestbookId;\n\tpublic long groupId;\n\tpublic long companyId;\n\tpublic long userId;\n\tpublic String userName;\n\tpublic long createDate;\n\tpublic long modifiedDate;\n\tpublic int status;\n\tpublic long statusByUserId;\n\tpublic String statusByUserName;\n\tpublic long statusDate;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.GuestbookEntry<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookEntryImpl extends GuestbookEntryBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook entry model instance should use the {@link com.liferay.docs.guestbook.model.GuestbookEntry} interface instead.\n\t */\n\tpublic GuestbookEntryImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookEntryModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.GuestbookEntryModel;\nimport com.liferay.docs.guestbook.model.GuestbookEntrySoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.DateUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the GuestbookEntry service. Represents a row in the &quot;GB_GuestbookEntry&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookEntryModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookEntryImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookEntryModelImpl\n\textends BaseModelImpl<GuestbookEntry> implements GuestbookEntryModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook entry model instance should use the <code>GuestbookEntry</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_GuestbookEntry\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"entryId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"email\", Types.VARCHAR},\n\t\t{\"message\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"groupId\", Types.BIGINT}, {\"companyId\", Types.BIGINT},\n\t\t{\"userId\", Types.BIGINT}, {\"userName\", Types.VARCHAR},\n\t\t{\"createDate\", Types.TIMESTAMP}, {\"modifiedDate\", Types.TIMESTAMP},\n\t\t{\"status\", Types.INTEGER}, {\"statusByUserId\", Types.BIGINT},\n\t\t{\"statusByUserName\", Types.VARCHAR}, {\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"entryId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"email\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"message\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_GuestbookEntry (uuid_ VARCHAR(75) null,entryId LONG not null primary key,name VARCHAR(75) null,email VARCHAR(75) null,message VARCHAR(75) null,guestbookId LONG,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_GuestbookEntry\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbookEntry.createDate DESC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_GuestbookEntry.createDate DESC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 4L;\n\n\tpublic static final long STATUS_COLUMN_BITMASK = 8L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 16L;\n\n\tpublic static final long CREATEDATE_COLUMN_BITMASK = 32L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static GuestbookEntry toModel(GuestbookEntrySoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbookEntry model = new GuestbookEntryImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setEntryId(soapModel.getEntryId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setEmail(soapModel.getEmail());\n\t\tmodel.setMessage(soapModel.getMessage());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<GuestbookEntry> toModels(\n\t\tGuestbookEntrySoap[] soapModels) {\n\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> models = new ArrayList<GuestbookEntry>(\n\t\t\tsoapModels.length);\n\n\t\tfor (GuestbookEntrySoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookEntryModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetEntryId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName,\n\t\t\t\tattributeGetterFunction.apply((GuestbookEntry)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<GuestbookEntry, Object>>\n\t\t\tattributeSetterBiConsumers = getAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<GuestbookEntry, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(GuestbookEntry)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<GuestbookEntry, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, GuestbookEntry>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbookEntry.class.getClassLoader(), GuestbookEntry.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<GuestbookEntry> constructor =\n\t\t\t\t(Constructor<GuestbookEntry>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<GuestbookEntry, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<GuestbookEntry, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<GuestbookEntry, Object>>();\n\t\tMap<String, BiConsumer<GuestbookEntry, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<GuestbookEntry, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", GuestbookEntry::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUuid);\n\t\tattributeGetterFunctions.put(\"entryId\", GuestbookEntry::getEntryId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"entryId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setEntryId);\n\t\tattributeGetterFunctions.put(\"name\", GuestbookEntry::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setName);\n\t\tattributeGetterFunctions.put(\"email\", GuestbookEntry::getEmail);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"email\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setEmail);\n\t\tattributeGetterFunctions.put(\"message\", GuestbookEntry::getMessage);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"message\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setMessage);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"guestbookId\", GuestbookEntry::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"groupId\", GuestbookEntry::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", GuestbookEntry::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", GuestbookEntry::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)GuestbookEntry::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", GuestbookEntry::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)GuestbookEntry::setUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"createDate\", GuestbookEntry::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", GuestbookEntry::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", GuestbookEntry::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\",\n\t\t\t(BiConsumer<GuestbookEntry, Integer>)GuestbookEntry::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", GuestbookEntry::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<GuestbookEntry, Long>)\n\t\t\t\tGuestbookEntry::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", GuestbookEntry::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<GuestbookEntry, String>)\n\t\t\t\tGuestbookEntry::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusDate\", GuestbookEntry::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<GuestbookEntry, Date>)GuestbookEntry::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getEntryId() {\n\t\treturn _entryId;\n\t}\n\n\t@Override\n\tpublic void setEntryId(long entryId) {\n\t\t_entryId = entryId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getEmail() {\n\t\tif (_email == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _email;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setEmail(String email) {\n\t\t_email = email;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getMessage() {\n\t\tif (_message == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _message;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setMessage(String message) {\n\t\t_message = message;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_columnBitmask |= GUESTBOOKID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGuestbookId) {\n\t\t\t_setOriginalGuestbookId = true;\n\n\t\t\t_originalGuestbookId = _guestbookId;\n\t\t}\n\n\t\t_guestbookId = guestbookId;\n\t}\n\n\tpublic long getOriginalGuestbookId() {\n\t\treturn _originalGuestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_columnBitmask = -1L;\n\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_columnBitmask |= STATUS_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalStatus) {\n\t\t\t_setOriginalStatus = true;\n\n\t\t\t_originalStatus = _status;\n\t\t}\n\n\t\t_status = status;\n\t}\n\n\tpublic int getOriginalStatus() {\n\t\treturn _originalStatus;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), GuestbookEntry.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookEntryImpl guestbookEntryImpl = new GuestbookEntryImpl();\n\n\t\tguestbookEntryImpl.setUuid(getUuid());\n\t\tguestbookEntryImpl.setEntryId(getEntryId());\n\t\tguestbookEntryImpl.setName(getName());\n\t\tguestbookEntryImpl.setEmail(getEmail());\n\t\tguestbookEntryImpl.setMessage(getMessage());\n\t\tguestbookEntryImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookEntryImpl.setGroupId(getGroupId());\n\t\tguestbookEntryImpl.setCompanyId(getCompanyId());\n\t\tguestbookEntryImpl.setUserId(getUserId());\n\t\tguestbookEntryImpl.setUserName(getUserName());\n\t\tguestbookEntryImpl.setCreateDate(getCreateDate());\n\t\tguestbookEntryImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookEntryImpl.setStatus(getStatus());\n\t\tguestbookEntryImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookEntryImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookEntryImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookEntryImpl.resetOriginalValues();\n\n\t\treturn guestbookEntryImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(GuestbookEntry guestbookEntry) {\n\t\tint value = 0;\n\n\t\tvalue = DateUtil.compareTo(\n\t\t\tgetCreateDate(), guestbookEntry.getCreateDate());\n\n\t\tvalue = value * -1;\n\n\t\tif (value != 0) {\n\t\t\treturn value;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof GuestbookEntry)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)obj;\n\n\t\tlong primaryKey = guestbookEntry.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl = this;\n\n\t\tguestbookEntryModelImpl._originalUuid = guestbookEntryModelImpl._uuid;\n\n\t\tguestbookEntryModelImpl._originalGuestbookId =\n\t\t\tguestbookEntryModelImpl._guestbookId;\n\n\t\tguestbookEntryModelImpl._setOriginalGuestbookId = false;\n\n\t\tguestbookEntryModelImpl._originalGroupId =\n\t\t\tguestbookEntryModelImpl._groupId;\n\n\t\tguestbookEntryModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookEntryModelImpl._originalCompanyId =\n\t\t\tguestbookEntryModelImpl._companyId;\n\n\t\tguestbookEntryModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookEntryModelImpl._setModifiedDate = false;\n\n\t\tguestbookEntryModelImpl._originalStatus =\n\t\t\tguestbookEntryModelImpl._status;\n\n\t\tguestbookEntryModelImpl._setOriginalStatus = false;\n\n\t\tguestbookEntryModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<GuestbookEntry> toCacheModel() {\n\t\tGuestbookEntryCacheModel guestbookEntryCacheModel =\n\t\t\tnew GuestbookEntryCacheModel();\n\n\t\tguestbookEntryCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookEntryCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.entryId = getEntryId();\n\n\t\tguestbookEntryCacheModel.name = getName();\n\n\t\tString name = guestbookEntryCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.name = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.email = getEmail();\n\n\t\tString email = guestbookEntryCacheModel.email;\n\n\t\tif ((email != null) && (email.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.email = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.message = getMessage();\n\n\t\tString message = guestbookEntryCacheModel.message;\n\n\t\tif ((message != null) && (message.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.message = null;\n\t\t}\n\n\t\tguestbookEntryCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookEntryCacheModel.groupId = getGroupId();\n\n\t\tguestbookEntryCacheModel.companyId = getCompanyId();\n\n\t\tguestbookEntryCacheModel.userId = getUserId();\n\n\t\tguestbookEntryCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookEntryCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookEntryCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookEntryCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookEntryCacheModel.status = getStatus();\n\n\t\tguestbookEntryCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookEntryCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookEntryCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookEntryCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookEntryCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookEntryCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookEntryCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<GuestbookEntry, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<GuestbookEntry, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<GuestbookEntry, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((GuestbookEntry)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, GuestbookEntry>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _entryId;\n\tprivate String _name;\n\tprivate String _email;\n\tprivate String _message;\n\tprivate long _guestbookId;\n\tprivate long _originalGuestbookId;\n\tprivate boolean _setOriginalGuestbookId;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate int _originalStatus;\n\tprivate boolean _setOriginalStatus;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate GuestbookEntry _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The extended model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * Helper methods and all application logic should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.model.Guestbook<code> interface.\n * </p>\n *\n * @author Liferay\n */\n@ProviderType\npublic class GuestbookImpl extends GuestbookBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. All methods that expect a guestbook model instance should use the {@link com.liferay.docs.guestbook.model.Guestbook} interface instead.\n\t */\n\tpublic GuestbookImpl() {\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/model/impl/GuestbookModelImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.model.impl;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookModel;\nimport com.liferay.docs.guestbook.model.GuestbookSoap;\nimport com.liferay.expando.kernel.model.ExpandoBridge;\nimport com.liferay.expando.kernel.util.ExpandoBridgeFactoryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.bean.AutoEscapeBeanHandler;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.json.JSON;\nimport com.liferay.portal.kernel.model.CacheModel;\nimport com.liferay.portal.kernel.model.ModelWrapper;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.model.impl.BaseModelImpl;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\n\nimport java.sql.Types;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * The base model implementation for the Guestbook service. Represents a row in the &quot;GB_Guestbook&quot; database table, with each column mapped to a property of this class.\n *\n * <p>\n * This implementation and its corresponding interface </code>GuestbookModel</code> exist only as a container for the default property accessors generated by ServiceBuilder. Helper methods and all application logic should be put in {@link GuestbookImpl}.\n * </p>\n *\n * @author Liferay\n * @see GuestbookImpl\n * @generated\n */\n@JSON(strict = true)\n@ProviderType\npublic class GuestbookModelImpl\n\textends BaseModelImpl<Guestbook> implements GuestbookModel {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. All methods that expect a guestbook model instance should use the <code>Guestbook</code> interface instead.\n\t */\n\tpublic static final String TABLE_NAME = \"GB_Guestbook\";\n\n\tpublic static final Object[][] TABLE_COLUMNS = {\n\t\t{\"uuid_\", Types.VARCHAR}, {\"guestbookId\", Types.BIGINT},\n\t\t{\"name\", Types.VARCHAR}, {\"groupId\", Types.BIGINT},\n\t\t{\"companyId\", Types.BIGINT}, {\"userId\", Types.BIGINT},\n\t\t{\"userName\", Types.VARCHAR}, {\"createDate\", Types.TIMESTAMP},\n\t\t{\"modifiedDate\", Types.TIMESTAMP}, {\"status\", Types.INTEGER},\n\t\t{\"statusByUserId\", Types.BIGINT}, {\"statusByUserName\", Types.VARCHAR},\n\t\t{\"statusDate\", Types.TIMESTAMP}\n\t};\n\n\tpublic static final Map<String, Integer> TABLE_COLUMNS_MAP =\n\t\tnew HashMap<String, Integer>();\n\n\tstatic {\n\t\tTABLE_COLUMNS_MAP.put(\"uuid_\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"guestbookId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"name\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"groupId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"companyId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"userName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"createDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"modifiedDate\", Types.TIMESTAMP);\n\t\tTABLE_COLUMNS_MAP.put(\"status\", Types.INTEGER);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserId\", Types.BIGINT);\n\t\tTABLE_COLUMNS_MAP.put(\"statusByUserName\", Types.VARCHAR);\n\t\tTABLE_COLUMNS_MAP.put(\"statusDate\", Types.TIMESTAMP);\n\t}\n\n\tpublic static final String TABLE_SQL_CREATE =\n\t\t\"create table GB_Guestbook (uuid_ VARCHAR(75) null,guestbookId LONG not null primary key,name VARCHAR(75) null,groupId LONG,companyId LONG,userId LONG,userName VARCHAR(75) null,createDate DATE null,modifiedDate DATE null,status INTEGER,statusByUserId LONG,statusByUserName VARCHAR(75) null,statusDate DATE null)\";\n\n\tpublic static final String TABLE_SQL_DROP = \"drop table GB_Guestbook\";\n\n\tpublic static final String ORDER_BY_JPQL =\n\t\t\" ORDER BY guestbook.guestbookId ASC\";\n\n\tpublic static final String ORDER_BY_SQL =\n\t\t\" ORDER BY GB_Guestbook.guestbookId ASC\";\n\n\tpublic static final String DATA_SOURCE = \"liferayDataSource\";\n\n\tpublic static final String SESSION_FACTORY = \"liferaySessionFactory\";\n\n\tpublic static final String TX_MANAGER = \"liferayTransactionManager\";\n\n\tpublic static final long COMPANYID_COLUMN_BITMASK = 1L;\n\n\tpublic static final long GROUPID_COLUMN_BITMASK = 2L;\n\n\tpublic static final long STATUS_COLUMN_BITMASK = 4L;\n\n\tpublic static final long UUID_COLUMN_BITMASK = 8L;\n\n\tpublic static final long GUESTBOOKID_COLUMN_BITMASK = 16L;\n\n\tpublic static void setEntityCacheEnabled(boolean entityCacheEnabled) {\n\t\t_entityCacheEnabled = entityCacheEnabled;\n\t}\n\n\tpublic static void setFinderCacheEnabled(boolean finderCacheEnabled) {\n\t\t_finderCacheEnabled = finderCacheEnabled;\n\t}\n\n\t/**\n\t * Converts the soap model instance into a normal model instance.\n\t *\n\t * @param soapModel the soap model instance to convert\n\t * @return the normal model instance\n\t */\n\tpublic static Guestbook toModel(GuestbookSoap soapModel) {\n\t\tif (soapModel == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tGuestbook model = new GuestbookImpl();\n\n\t\tmodel.setUuid(soapModel.getUuid());\n\t\tmodel.setGuestbookId(soapModel.getGuestbookId());\n\t\tmodel.setName(soapModel.getName());\n\t\tmodel.setGroupId(soapModel.getGroupId());\n\t\tmodel.setCompanyId(soapModel.getCompanyId());\n\t\tmodel.setUserId(soapModel.getUserId());\n\t\tmodel.setUserName(soapModel.getUserName());\n\t\tmodel.setCreateDate(soapModel.getCreateDate());\n\t\tmodel.setModifiedDate(soapModel.getModifiedDate());\n\t\tmodel.setStatus(soapModel.getStatus());\n\t\tmodel.setStatusByUserId(soapModel.getStatusByUserId());\n\t\tmodel.setStatusByUserName(soapModel.getStatusByUserName());\n\t\tmodel.setStatusDate(soapModel.getStatusDate());\n\n\t\treturn model;\n\t}\n\n\t/**\n\t * Converts the soap model instances into normal model instances.\n\t *\n\t * @param soapModels the soap model instances to convert\n\t * @return the normal model instances\n\t */\n\tpublic static List<Guestbook> toModels(GuestbookSoap[] soapModels) {\n\t\tif (soapModels == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> models = new ArrayList<Guestbook>(soapModels.length);\n\n\t\tfor (GuestbookSoap soapModel : soapModels) {\n\t\t\tmodels.add(toModel(soapModel));\n\t\t}\n\n\t\treturn models;\n\t}\n\n\tpublic GuestbookModelImpl() {\n\t}\n\n\t@Override\n\tpublic long getPrimaryKey() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKey(long primaryKey) {\n\t\tsetGuestbookId(primaryKey);\n\t}\n\n\t@Override\n\tpublic Serializable getPrimaryKeyObj() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setPrimaryKeyObj(Serializable primaryKeyObj) {\n\t\tsetPrimaryKey(((Long)primaryKeyObj).longValue());\n\t}\n\n\t@Override\n\tpublic Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\t@Override\n\tpublic String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getModelAttributes() {\n\t\tMap<String, Object> attributes = new HashMap<String, Object>();\n\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tattributes.put(\n\t\t\t\tattributeName, attributeGetterFunction.apply((Guestbook)this));\n\t\t}\n\n\t\tattributes.put(\"entityCacheEnabled\", isEntityCacheEnabled());\n\t\tattributes.put(\"finderCacheEnabled\", isFinderCacheEnabled());\n\n\t\treturn attributes;\n\t}\n\n\t@Override\n\tpublic void setModelAttributes(Map<String, Object> attributes) {\n\t\tMap<String, BiConsumer<Guestbook, Object>> attributeSetterBiConsumers =\n\t\t\tgetAttributeSetterBiConsumers();\n\n\t\tfor (Map.Entry<String, Object> entry : attributes.entrySet()) {\n\t\t\tString attributeName = entry.getKey();\n\n\t\t\tBiConsumer<Guestbook, Object> attributeSetterBiConsumer =\n\t\t\t\tattributeSetterBiConsumers.get(attributeName);\n\n\t\t\tif (attributeSetterBiConsumer != null) {\n\t\t\t\tattributeSetterBiConsumer.accept(\n\t\t\t\t\t(Guestbook)this, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, Function<Guestbook, Object>>\n\t\tgetAttributeGetterFunctions() {\n\n\t\treturn _attributeGetterFunctions;\n\t}\n\n\tpublic Map<String, BiConsumer<Guestbook, Object>>\n\t\tgetAttributeSetterBiConsumers() {\n\n\t\treturn _attributeSetterBiConsumers;\n\t}\n\n\tprivate static Function<InvocationHandler, Guestbook>\n\t\t_getProxyProviderFunction() {\n\n\t\tClass<?> proxyClass = ProxyUtil.getProxyClass(\n\t\t\tGuestbook.class.getClassLoader(), Guestbook.class,\n\t\t\tModelWrapper.class);\n\n\t\ttry {\n\t\t\tConstructor<Guestbook> constructor =\n\t\t\t\t(Constructor<Guestbook>)proxyClass.getConstructor(\n\t\t\t\t\tInvocationHandler.class);\n\n\t\t\treturn invocationHandler -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn constructor.newInstance(invocationHandler);\n\t\t\t\t}\n\t\t\t\tcatch (ReflectiveOperationException roe) {\n\t\t\t\t\tthrow new InternalError(roe);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcatch (NoSuchMethodException nsme) {\n\t\t\tthrow new InternalError(nsme);\n\t\t}\n\t}\n\n\tprivate static final Map<String, Function<Guestbook, Object>>\n\t\t_attributeGetterFunctions;\n\tprivate static final Map<String, BiConsumer<Guestbook, Object>>\n\t\t_attributeSetterBiConsumers;\n\n\tstatic {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tnew LinkedHashMap<String, Function<Guestbook, Object>>();\n\t\tMap<String, BiConsumer<Guestbook, ?>> attributeSetterBiConsumers =\n\t\t\tnew LinkedHashMap<String, BiConsumer<Guestbook, ?>>();\n\n\t\tattributeGetterFunctions.put(\"uuid\", Guestbook::getUuid);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"uuid\", (BiConsumer<Guestbook, String>)Guestbook::setUuid);\n\t\tattributeGetterFunctions.put(\"guestbookId\", Guestbook::getGuestbookId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"guestbookId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setGuestbookId);\n\t\tattributeGetterFunctions.put(\"name\", Guestbook::getName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"name\", (BiConsumer<Guestbook, String>)Guestbook::setName);\n\t\tattributeGetterFunctions.put(\"groupId\", Guestbook::getGroupId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"groupId\", (BiConsumer<Guestbook, Long>)Guestbook::setGroupId);\n\t\tattributeGetterFunctions.put(\"companyId\", Guestbook::getCompanyId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"companyId\", (BiConsumer<Guestbook, Long>)Guestbook::setCompanyId);\n\t\tattributeGetterFunctions.put(\"userId\", Guestbook::getUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userId\", (BiConsumer<Guestbook, Long>)Guestbook::setUserId);\n\t\tattributeGetterFunctions.put(\"userName\", Guestbook::getUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"userName\", (BiConsumer<Guestbook, String>)Guestbook::setUserName);\n\t\tattributeGetterFunctions.put(\"createDate\", Guestbook::getCreateDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"createDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setCreateDate);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"modifiedDate\", Guestbook::getModifiedDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"modifiedDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setModifiedDate);\n\t\tattributeGetterFunctions.put(\"status\", Guestbook::getStatus);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"status\", (BiConsumer<Guestbook, Integer>)Guestbook::setStatus);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserId\", Guestbook::getStatusByUserId);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserId\",\n\t\t\t(BiConsumer<Guestbook, Long>)Guestbook::setStatusByUserId);\n\t\tattributeGetterFunctions.put(\n\t\t\t\"statusByUserName\", Guestbook::getStatusByUserName);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusByUserName\",\n\t\t\t(BiConsumer<Guestbook, String>)Guestbook::setStatusByUserName);\n\t\tattributeGetterFunctions.put(\"statusDate\", Guestbook::getStatusDate);\n\t\tattributeSetterBiConsumers.put(\n\t\t\t\"statusDate\",\n\t\t\t(BiConsumer<Guestbook, Date>)Guestbook::setStatusDate);\n\n\t\t_attributeGetterFunctions = Collections.unmodifiableMap(\n\t\t\tattributeGetterFunctions);\n\t\t_attributeSetterBiConsumers = Collections.unmodifiableMap(\n\t\t\t(Map)attributeSetterBiConsumers);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUuid() {\n\t\tif (_uuid == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _uuid;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUuid(String uuid) {\n\t\t_columnBitmask |= UUID_COLUMN_BITMASK;\n\n\t\tif (_originalUuid == null) {\n\t\t\t_originalUuid = _uuid;\n\t\t}\n\n\t\t_uuid = uuid;\n\t}\n\n\tpublic String getOriginalUuid() {\n\t\treturn GetterUtil.getString(_originalUuid);\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGuestbookId() {\n\t\treturn _guestbookId;\n\t}\n\n\t@Override\n\tpublic void setGuestbookId(long guestbookId) {\n\t\t_guestbookId = guestbookId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getName() {\n\t\tif (_name == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _name;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\t_name = name;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getGroupId() {\n\t\treturn _groupId;\n\t}\n\n\t@Override\n\tpublic void setGroupId(long groupId) {\n\t\t_columnBitmask |= GROUPID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalGroupId) {\n\t\t\t_setOriginalGroupId = true;\n\n\t\t\t_originalGroupId = _groupId;\n\t\t}\n\n\t\t_groupId = groupId;\n\t}\n\n\tpublic long getOriginalGroupId() {\n\t\treturn _originalGroupId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getCompanyId() {\n\t\treturn _companyId;\n\t}\n\n\t@Override\n\tpublic void setCompanyId(long companyId) {\n\t\t_columnBitmask |= COMPANYID_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalCompanyId) {\n\t\t\t_setOriginalCompanyId = true;\n\n\t\t\t_originalCompanyId = _companyId;\n\t\t}\n\n\t\t_companyId = companyId;\n\t}\n\n\tpublic long getOriginalCompanyId() {\n\t\treturn _originalCompanyId;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getUserId() {\n\t\treturn _userId;\n\t}\n\n\t@Override\n\tpublic void setUserId(long userId) {\n\t\t_userId = userId;\n\t}\n\n\t@Override\n\tpublic String getUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserUuid(String userUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getUserName() {\n\t\tif (_userName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _userName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserName(String userName) {\n\t\t_userName = userName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getCreateDate() {\n\t\treturn _createDate;\n\t}\n\n\t@Override\n\tpublic void setCreateDate(Date createDate) {\n\t\t_createDate = createDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getModifiedDate() {\n\t\treturn _modifiedDate;\n\t}\n\n\tpublic boolean hasSetModifiedDate() {\n\t\treturn _setModifiedDate;\n\t}\n\n\t@Override\n\tpublic void setModifiedDate(Date modifiedDate) {\n\t\t_setModifiedDate = true;\n\n\t\t_modifiedDate = modifiedDate;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic int getStatus() {\n\t\treturn _status;\n\t}\n\n\t@Override\n\tpublic void setStatus(int status) {\n\t\t_columnBitmask |= STATUS_COLUMN_BITMASK;\n\n\t\tif (!_setOriginalStatus) {\n\t\t\t_setOriginalStatus = true;\n\n\t\t\t_originalStatus = _status;\n\t\t}\n\n\t\t_status = status;\n\t}\n\n\tpublic int getOriginalStatus() {\n\t\treturn _originalStatus;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic long getStatusByUserId() {\n\t\treturn _statusByUserId;\n\t}\n\n\t@Override\n\tpublic void setStatusByUserId(long statusByUserId) {\n\t\t_statusByUserId = statusByUserId;\n\t}\n\n\t@Override\n\tpublic String getStatusByUserUuid() {\n\t\ttry {\n\t\t\tUser user = UserLocalServiceUtil.getUserById(getStatusByUserId());\n\n\t\t\treturn user.getUuid();\n\t\t}\n\t\tcatch (PortalException pe) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserUuid(String statusByUserUuid) {\n\t}\n\n\t@JSON\n\t@Override\n\tpublic String getStatusByUserName() {\n\t\tif (_statusByUserName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\telse {\n\t\t\treturn _statusByUserName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setStatusByUserName(String statusByUserName) {\n\t\t_statusByUserName = statusByUserName;\n\t}\n\n\t@JSON\n\t@Override\n\tpublic Date getStatusDate() {\n\t\treturn _statusDate;\n\t}\n\n\t@Override\n\tpublic void setStatusDate(Date statusDate) {\n\t\t_statusDate = statusDate;\n\t}\n\n\t@Override\n\tpublic StagedModelType getStagedModelType() {\n\t\treturn new StagedModelType(\n\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName()));\n\t}\n\n\t@Override\n\tpublic boolean isApproved() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_APPROVED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDenied() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DENIED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDraft() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_DRAFT) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExpired() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_EXPIRED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isInactive() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INACTIVE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isIncomplete() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_INCOMPLETE) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isPending() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_PENDING) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isScheduled() {\n\t\tif (getStatus() == WorkflowConstants.STATUS_SCHEDULED) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic long getColumnBitmask() {\n\t\treturn _columnBitmask;\n\t}\n\n\t@Override\n\tpublic ExpandoBridge getExpandoBridge() {\n\t\treturn ExpandoBridgeFactoryUtil.getExpandoBridge(\n\t\t\tgetCompanyId(), Guestbook.class.getName(), getPrimaryKey());\n\t}\n\n\t@Override\n\tpublic void setExpandoBridgeAttributes(ServiceContext serviceContext) {\n\t\tExpandoBridge expandoBridge = getExpandoBridge();\n\n\t\texpandoBridge.setAttributes(serviceContext);\n\t}\n\n\t@Override\n\tpublic Guestbook toEscapedModel() {\n\t\tif (_escapedModel == null) {\n\t\t\t_escapedModel = _escapedModelProxyProviderFunction.apply(\n\t\t\t\tnew AutoEscapeBeanHandler(this));\n\t\t}\n\n\t\treturn _escapedModel;\n\t}\n\n\t@Override\n\tpublic Object clone() {\n\t\tGuestbookImpl guestbookImpl = new GuestbookImpl();\n\n\t\tguestbookImpl.setUuid(getUuid());\n\t\tguestbookImpl.setGuestbookId(getGuestbookId());\n\t\tguestbookImpl.setName(getName());\n\t\tguestbookImpl.setGroupId(getGroupId());\n\t\tguestbookImpl.setCompanyId(getCompanyId());\n\t\tguestbookImpl.setUserId(getUserId());\n\t\tguestbookImpl.setUserName(getUserName());\n\t\tguestbookImpl.setCreateDate(getCreateDate());\n\t\tguestbookImpl.setModifiedDate(getModifiedDate());\n\t\tguestbookImpl.setStatus(getStatus());\n\t\tguestbookImpl.setStatusByUserId(getStatusByUserId());\n\t\tguestbookImpl.setStatusByUserName(getStatusByUserName());\n\t\tguestbookImpl.setStatusDate(getStatusDate());\n\n\t\tguestbookImpl.resetOriginalValues();\n\n\t\treturn guestbookImpl;\n\t}\n\n\t@Override\n\tpublic int compareTo(Guestbook guestbook) {\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() < primaryKey) {\n\t\t\treturn -1;\n\t\t}\n\t\telse if (getPrimaryKey() > primaryKey) {\n\t\t\treturn 1;\n\t\t}\n\t\telse {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!(obj instanceof Guestbook)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tGuestbook guestbook = (Guestbook)obj;\n\n\t\tlong primaryKey = guestbook.getPrimaryKey();\n\n\t\tif (getPrimaryKey() == primaryKey) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn (int)getPrimaryKey();\n\t}\n\n\t@Override\n\tpublic boolean isEntityCacheEnabled() {\n\t\treturn _entityCacheEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isFinderCacheEnabled() {\n\t\treturn _finderCacheEnabled;\n\t}\n\n\t@Override\n\tpublic void resetOriginalValues() {\n\t\tGuestbookModelImpl guestbookModelImpl = this;\n\n\t\tguestbookModelImpl._originalUuid = guestbookModelImpl._uuid;\n\n\t\tguestbookModelImpl._originalGroupId = guestbookModelImpl._groupId;\n\n\t\tguestbookModelImpl._setOriginalGroupId = false;\n\n\t\tguestbookModelImpl._originalCompanyId = guestbookModelImpl._companyId;\n\n\t\tguestbookModelImpl._setOriginalCompanyId = false;\n\n\t\tguestbookModelImpl._setModifiedDate = false;\n\n\t\tguestbookModelImpl._originalStatus = guestbookModelImpl._status;\n\n\t\tguestbookModelImpl._setOriginalStatus = false;\n\n\t\tguestbookModelImpl._columnBitmask = 0;\n\t}\n\n\t@Override\n\tpublic CacheModel<Guestbook> toCacheModel() {\n\t\tGuestbookCacheModel guestbookCacheModel = new GuestbookCacheModel();\n\n\t\tguestbookCacheModel.uuid = getUuid();\n\n\t\tString uuid = guestbookCacheModel.uuid;\n\n\t\tif ((uuid != null) && (uuid.length() == 0)) {\n\t\t\tguestbookCacheModel.uuid = null;\n\t\t}\n\n\t\tguestbookCacheModel.guestbookId = getGuestbookId();\n\n\t\tguestbookCacheModel.name = getName();\n\n\t\tString name = guestbookCacheModel.name;\n\n\t\tif ((name != null) && (name.length() == 0)) {\n\t\t\tguestbookCacheModel.name = null;\n\t\t}\n\n\t\tguestbookCacheModel.groupId = getGroupId();\n\n\t\tguestbookCacheModel.companyId = getCompanyId();\n\n\t\tguestbookCacheModel.userId = getUserId();\n\n\t\tguestbookCacheModel.userName = getUserName();\n\n\t\tString userName = guestbookCacheModel.userName;\n\n\t\tif ((userName != null) && (userName.length() == 0)) {\n\t\t\tguestbookCacheModel.userName = null;\n\t\t}\n\n\t\tDate createDate = getCreateDate();\n\n\t\tif (createDate != null) {\n\t\t\tguestbookCacheModel.createDate = createDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.createDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tDate modifiedDate = getModifiedDate();\n\n\t\tif (modifiedDate != null) {\n\t\t\tguestbookCacheModel.modifiedDate = modifiedDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.modifiedDate = Long.MIN_VALUE;\n\t\t}\n\n\t\tguestbookCacheModel.status = getStatus();\n\n\t\tguestbookCacheModel.statusByUserId = getStatusByUserId();\n\n\t\tguestbookCacheModel.statusByUserName = getStatusByUserName();\n\n\t\tString statusByUserName = guestbookCacheModel.statusByUserName;\n\n\t\tif ((statusByUserName != null) && (statusByUserName.length() == 0)) {\n\t\t\tguestbookCacheModel.statusByUserName = null;\n\t\t}\n\n\t\tDate statusDate = getStatusDate();\n\n\t\tif (statusDate != null) {\n\t\t\tguestbookCacheModel.statusDate = statusDate.getTime();\n\t\t}\n\t\telse {\n\t\t\tguestbookCacheModel.statusDate = Long.MIN_VALUE;\n\t\t}\n\n\t\treturn guestbookCacheModel;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t4 * attributeGetterFunctions.size() + 2);\n\n\t\tsb.append(\"{\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"=\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\", \");\n\t\t}\n\n\t\tif (sb.index() > 1) {\n\t\t\tsb.setIndex(sb.index() - 1);\n\t\t}\n\n\t\tsb.append(\"}\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toXmlString() {\n\t\tMap<String, Function<Guestbook, Object>> attributeGetterFunctions =\n\t\t\tgetAttributeGetterFunctions();\n\n\t\tStringBundler sb = new StringBundler(\n\t\t\t5 * attributeGetterFunctions.size() + 4);\n\n\t\tsb.append(\"<model><model-name>\");\n\t\tsb.append(getModelClassName());\n\t\tsb.append(\"</model-name>\");\n\n\t\tfor (Map.Entry<String, Function<Guestbook, Object>> entry :\n\t\t\t\tattributeGetterFunctions.entrySet()) {\n\n\t\t\tString attributeName = entry.getKey();\n\t\t\tFunction<Guestbook, Object> attributeGetterFunction =\n\t\t\t\tentry.getValue();\n\n\t\t\tsb.append(\"<column><column-name>\");\n\t\t\tsb.append(attributeName);\n\t\t\tsb.append(\"</column-name><column-value><![CDATA[\");\n\t\t\tsb.append(attributeGetterFunction.apply((Guestbook)this));\n\t\t\tsb.append(\"]]></column-value></column>\");\n\t\t}\n\n\t\tsb.append(\"</model>\");\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final Function<InvocationHandler, Guestbook>\n\t\t_escapedModelProxyProviderFunction = _getProxyProviderFunction();\n\tprivate static boolean _entityCacheEnabled;\n\tprivate static boolean _finderCacheEnabled;\n\n\tprivate String _uuid;\n\tprivate String _originalUuid;\n\tprivate long _guestbookId;\n\tprivate String _name;\n\tprivate long _groupId;\n\tprivate long _originalGroupId;\n\tprivate boolean _setOriginalGroupId;\n\tprivate long _companyId;\n\tprivate long _originalCompanyId;\n\tprivate boolean _setOriginalCompanyId;\n\tprivate long _userId;\n\tprivate String _userName;\n\tprivate Date _createDate;\n\tprivate Date _modifiedDate;\n\tprivate boolean _setModifiedDate;\n\tprivate int _status;\n\tprivate int _originalStatus;\n\tprivate boolean _setOriginalStatus;\n\tprivate long _statusByUserId;\n\tprivate String _statusByUserName;\n\tprivate Date _statusDate;\n\tprivate long _columnBitmask;\n\tprivate Guestbook _escapedModel;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryBatchReindexer.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\npublic interface GuestbookEntryBatchReindexer {\n\t\n\tpublic void reindex(long guestbookId, long companyId);\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryBatchReindexerImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.search.batch.BatchIndexingActionable;\nimport com.liferay.portal.search.indexer.IndexerDocumentBuilder;\nimport com.liferay.portal.search.indexer.IndexerWriter;\n\n@Component(immediate = true, service = GuestbookEntryBatchReindexer.class)\npublic class GuestbookEntryBatchReindexerImpl implements GuestbookEntryBatchReindexer {\n\n    @Override\n    public void reindex(long guestbookId, long companyId) {\n        BatchIndexingActionable batchIndexingActionable =\n    indexerWriter.getBatchIndexingActionable();\n\n        batchIndexingActionable.setAddCriteriaMethod(dynamicQuery -> {\n            Property guestbookIdPropery = PropertyFactoryUtil.forName(\n    \"guestbookId\");\n\n            dynamicQuery.add(guestbookIdPropery.eq(guestbookId));\n        });\n\n        batchIndexingActionable.setCompanyId(companyId);\n\n        batchIndexingActionable.setPerformActionMethod((GuestbookEntry entry) -> {\n            Document document = indexerDocumentBuilder.getDocument(entry);\n\n            batchIndexingActionable.addDocuments(document);\n        });\n\n        batchIndexingActionable.performActions();\n        \n    }\n\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n    protected IndexerDocumentBuilder indexerDocumentBuilder;\n\n    @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n    protected IndexerWriter<GuestbookEntry> indexerWriter;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryKeywordQueryContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.portal.kernel.search.BooleanQuery;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.search.SearchContext;\nimport com.liferay.portal.search.query.QueryHelper;\nimport com.liferay.portal.search.spi.model.query.contributor.KeywordQueryContributor;\nimport com.liferay.portal.search.spi.model.query.contributor.helper.KeywordQueryContributorHelper;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = KeywordQueryContributor.class\n)\npublic class GuestbookEntryKeywordQueryContributor implements KeywordQueryContributor {\n\n    @Override\n    public void contribute(\n        String keywords, BooleanQuery booleanQuery,\n        KeywordQueryContributorHelper keywordQueryContributorHelper) {\n\n        SearchContext searchContext =\n    keywordQueryContributorHelper.getSearchContext();\n\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.TITLE, false);\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.CONTENT, false);\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, \"entryEmail\", false);\n    }\n\n    @Reference\n    protected QueryHelper queryHelper;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryModelDocumentContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.util.LocalizationUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelDocumentContributor;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelDocumentContributor.class\n)\npublic class GuestbookEntryModelDocumentContributor\n    implements ModelDocumentContributor<GuestbookEntry> {\n\n    @Override\n    public void contribute(Document document, GuestbookEntry entry) {\n        try {\n            Locale defaultLocale = PortalUtil.getSiteDefaultLocale(\n    entry.getGroupId());\n\n            document.addDate(Field.MODIFIED_DATE, entry.getModifiedDate());\n            document.addText(\"entryEmail\", entry.getEmail());\n\n            String localizedTitle = LocalizationUtil.getLocalizedName(\n    Field.TITLE, defaultLocale.toString());\n            String localizedContent = LocalizationUtil.getLocalizedName(\n    Field.CONTENT, defaultLocale.toString());\n\n            document.addText(localizedTitle, entry.getName());\n            document.addText(localizedContent, entry.getMessage());\n\n            long guestbookId = entry.getGuestbookId();\n\n            Guestbook guestbook = _guestbookLocalService.getGuestbook(\n    guestbookId);\n\n            String guestbookName = guestbook.getName();\n\n            String localizedGbName = LocalizationUtil.getLocalizedName(\n    Field.NAME, defaultLocale.toString());\n\n            document.addText(localizedGbName, guestbookName);\n        } catch (PortalException pe) {\n            if (_log.isWarnEnabled()) {\n                _log.warn(\"Unable to index entry \" + entry.getEntryId(), pe);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    private static final Log _log = LogFactoryUtil.getLog(\n    GuestbookEntryModelDocumentContributor.class);\n\n    @Reference\n    private GuestbookLocalService _guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryModelIndexerWriterContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.search.batch.BatchIndexingActionable;\nimport com.liferay.portal.search.batch.DynamicQueryBatchIndexingActionableFactory;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor;\nimport com.liferay.portal.search.spi.model.index.contributor.helper.ModelIndexerWriterDocumentHelper;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelIndexerWriterContributor.class\n)\npublic class GuestbookEntryModelIndexerWriterContributor\n    implements ModelIndexerWriterContributor<GuestbookEntry> {\n\n    @Override\n    public void customize(\n        BatchIndexingActionable batchIndexingActionable,\n        ModelIndexerWriterDocumentHelper modelIndexerWriterDocumentHelper) {\n\n        batchIndexingActionable.setPerformActionMethod((GuestbookEntry entry) -> {\n            Document document = modelIndexerWriterDocumentHelper.getDocument(\n    entry);\n\n            batchIndexingActionable.addDocuments(document);\n            \n        });\n    }\n\n    @Override\n    public BatchIndexingActionable getBatchIndexingActionable() {\n        return dynamicQueryBatchIndexingActionableFactory.getBatchIndexingActionable(\n    guestbookEntryLocalService.getIndexableActionableDynamicQuery());\n    }\n\n    @Override\n    public long getCompanyId(GuestbookEntry entry) {\n        return entry.getCompanyId();\n    }\n\n    @Reference\n    protected DynamicQueryBatchIndexingActionableFactory\n    dynamicQueryBatchIndexingActionableFactory;\n\n    @Reference\n    protected GuestbookEntryLocalService guestbookEntryLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntryModelSummaryContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.petra.string.StringPool;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.search.Summary;\nimport com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\",\n        service = ModelSummaryContributor.class\n)\npublic class GuestbookEntryModelSummaryContributor implements ModelSummaryContributor {\n\n    @Override\n    public Summary getSummary(\n        Document document, Locale locale, String snippet) {\n\n        Summary summary = createSummary(document);\n\n        summary.setMaxContentLength(128);\n\n        return summary;\n    }\n\n    private Summary createSummary(Document document) {\n        String prefix = Field.SNIPPET + StringPool.UNDERLINE;\n\n        String title = document.get(prefix + Field.TITLE, Field.CONTENT);\n        String content = document.get(prefix + Field.CONTENT, Field.CONTENT);\n\n        return new Summary(title, content);\n    }\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookEntrySearchRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor;\nimport com.liferay.portal.search.spi.model.registrar.ModelSearchRegistrarHelper;\nimport com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor;\n\n@Component(immediate=true)\npublic class GuestbookEntrySearchRegistrar {\n\n\t@Activate\n\tprotected void activate(BundleContext bundleContext) {\n\n\t\t_serviceRegistration = modelSearchRegistrarHelper.register(\n\t\t\tGuestbookEntry.class, bundleContext, modelSearchDefinition -> {\n\t\t\t\tmodelSearchDefinition.setDefaultSelectedFieldNames(\n\t\t\t\t\tField.COMPANY_ID, Field.ENTRY_CLASS_NAME,\n\t\t\t\t\tField.ENTRY_CLASS_PK, Field.UID, \n\t\t\t\t\tField.SCOPE_GROUP_ID, Field.GROUP_ID);\n\n\t\t\t\tmodelSearchDefinition.setDefaultSelectedLocalizedFieldNames(\n\t\t\t\t\tField.TITLE, Field.CONTENT);\n\n\t\t\t\tmodelSearchDefinition.setModelIndexWriteContributor(\n\t\t\t\t\tmodelIndexWriterContributor);\n\t\t\t\tmodelSearchDefinition.setModelSummaryContributor(\n\t\t\t\t\tmodelSummaryContributor);\n\t\t\t\tmodelSearchDefinition.setSelectAllLocales(true);\n\n\t\t\t});\n\t}\n\n\t@Deactivate\n\tprotected void deactivate() {\n\t\t_serviceRegistration.unregister();\n\t}\n\n\t@Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n\tprotected ModelIndexerWriterContributor<GuestbookEntry> modelIndexWriterContributor;\n\n\t@Reference\n\tprotected ModelSearchRegistrarHelper modelSearchRegistrarHelper;\n\n\t@Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\")\n\tprotected ModelSummaryContributor modelSummaryContributor;\n\n\tprivate ServiceRegistration<?> _serviceRegistration;\n\n    }"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookKeywordQueryContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.portal.kernel.search.BooleanQuery;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.search.SearchContext;\nimport com.liferay.portal.search.query.QueryHelper;\nimport com.liferay.portal.search.spi.model.query.contributor.KeywordQueryContributor;\nimport com.liferay.portal.search.spi.model.query.contributor.helper.KeywordQueryContributorHelper;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = KeywordQueryContributor.class\n)\npublic class GuestbookKeywordQueryContributor\n    implements KeywordQueryContributor {\n\n    @Override\n    public void contribute(\n        String keywords, BooleanQuery booleanQuery,\n        KeywordQueryContributorHelper keywordQueryContributorHelper) {\n\n        SearchContext searchContext =\n    keywordQueryContributorHelper.getSearchContext();\n\n        queryHelper.addSearchLocalizedTerm(\n    booleanQuery, searchContext, Field.TITLE, false);\n    }\n\n    @Reference\n    protected QueryHelper queryHelper;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookModelDocumentContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.util.LocalizationUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelDocumentContributor;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelDocumentContributor.class\n)\npublic class GuestbookModelDocumentContributor\n    implements ModelDocumentContributor<Guestbook> {\n\n    @Override\n    public void contribute(Document document, Guestbook guestbook) {\n        try {\n            document.addDate(Field.MODIFIED_DATE, guestbook.getModifiedDate());\n\n            Locale defaultLocale = PortalUtil.getSiteDefaultLocale(\n    guestbook.getGroupId());\n\n            String localizedTitle = LocalizationUtil.getLocalizedName(\n    Field.TITLE, defaultLocale.toString());\n\n            document.addText(localizedTitle, guestbook.getName());\n        } catch (PortalException pe) {\n            if (_log.isWarnEnabled()) {\n                _log.warn(\n    \"Unable to index guestbook \" + guestbook.getGuestbookId(), pe);\n            }\n        }\n    }\n\n    private static final Log _log = LogFactoryUtil.getLog(\n    GuestbookModelDocumentContributor.class);\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookModelIndexerWriterContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.search.batch.BatchIndexingActionable;\nimport com.liferay.portal.search.batch.DynamicQueryBatchIndexingActionableFactory;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor;\nimport com.liferay.portal.search.spi.model.index.contributor.helper.ModelIndexerWriterDocumentHelper;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelIndexerWriterContributor.class\n)\npublic class GuestbookModelIndexerWriterContributor\n    implements ModelIndexerWriterContributor<Guestbook> {\n\n    @Override\n    public void customize(\n        BatchIndexingActionable batchIndexingActionable,\n        ModelIndexerWriterDocumentHelper modelIndexerWriterDocumentHelper) {\n\n        batchIndexingActionable.setPerformActionMethod((Guestbook guestbook) -> {\n            Document document = modelIndexerWriterDocumentHelper.getDocument(\n    guestbook);\n\n            batchIndexingActionable.addDocuments(document);\n        });\n    }\n\n    @Override\n    public BatchIndexingActionable getBatchIndexingActionable() {\n        return dynamicQueryBatchIndexingActionableFactory.getBatchIndexingActionable(\n    guestbookLocalService.getIndexableActionableDynamicQuery());\n    }\n\n    @Override\n    public long getCompanyId(Guestbook guestbook) {\n        return guestbook.getCompanyId();\n    }\n\n    @Override\n    public void modelIndexed(Guestbook guestbook) {\n        guestbookEntryBatchReindexer.reindex(\n    guestbook.getGuestbookId(), guestbook.getCompanyId());\n    }\n\n    @Reference\n    protected DynamicQueryBatchIndexingActionableFactory\n    dynamicQueryBatchIndexingActionableFactory;\n\n    @Reference\n    protected GuestbookEntryBatchReindexer guestbookEntryBatchReindexer;\n\n    @Reference\n    protected GuestbookLocalService guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookModelSummaryContributor.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport java.util.Locale;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.petra.string.StringPool;\nimport com.liferay.portal.kernel.search.Document;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.kernel.search.Summary;\nimport com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor;\n\n@Component(\n        immediate = true,\n        property = \"indexer.class.name=com.liferay.docs.guestbook.model.Guestbook\",\n        service = ModelSummaryContributor.class\n)\npublic class GuestbookModelSummaryContributor\n    implements ModelSummaryContributor {\n\n    @Override\n    public Summary getSummary(\n        Document document, Locale locale, String snippet) {\n\n        Summary summary = createSummary(document);\n\n        summary.setMaxContentLength(200);\n\n        return summary;\n    }\n\n    private Summary createSummary(Document document) {\n        String prefix = Field.SNIPPET + StringPool.UNDERLINE;\n\n        String title = document.get(prefix + Field.TITLE, Field.TITLE);\n\n        return new Summary(title, StringPool.BLANK);\n    }\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/search/GuestbookSearchRegistrar.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\npackage com.liferay.docs.guestbook.search;\n\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.ServiceRegistration;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.search.Field;\nimport com.liferay.portal.search.spi.model.index.contributor.ModelIndexerWriterContributor;\nimport com.liferay.portal.search.spi.model.registrar.ModelSearchRegistrarHelper;\nimport com.liferay.portal.search.spi.model.result.contributor.ModelSummaryContributor;\n\n@Component(immediate=true)\npublic class GuestbookSearchRegistrar {\n\n        @Activate\n        protected void activate(BundleContext bundleContext) {\n\n            _serviceRegistration = modelSearchRegistrarHelper.register(\n                Guestbook.class, bundleContext, modelSearchDefinition -> {\n                    modelSearchDefinition.setDefaultSelectedFieldNames(\n                        Field.ASSET_TAG_NAMES, Field.COMPANY_ID, Field.CONTENT,\n                        Field.ENTRY_CLASS_NAME, Field.ENTRY_CLASS_PK,\n                        Field.GROUP_ID, Field.MODIFIED_DATE, Field.SCOPE_GROUP_ID,\n                        Field.TITLE, Field.UID);\n\n                    modelSearchDefinition.setModelIndexWriteContributor(\n                        modelIndexWriterContributor);\n                    modelSearchDefinition.setModelSummaryContributor(\n                        modelSummaryContributor);\n                });\n        }\n\n        @Deactivate\n        protected void deactivate() {\n\n            _serviceRegistration.unregister();\n        }\n\n        @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.Guestbook)\")\n        protected ModelIndexerWriterContributor<Guestbook> modelIndexWriterContributor;\n\n        @Reference\n        protected ModelSearchRegistrarHelper modelSearchRegistrarHelper;\n\n        @Reference(target = \"(indexer.class.name=com.liferay.docs.guestbook.model.Guestbook)\")\n        protected ModelSummaryContributor modelSummaryContributor;\n\n        private ServiceRegistration<?> _serviceRegistration;\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookEntryLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookEntryLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook entry to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry addGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\tguestbookEntry.setNew(true);\n\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic GuestbookEntry createGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.create(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(entryId);\n\t}\n\n\t/**\n\t * Deletes the guestbook entry from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws PortalException\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry guestbookEntry)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.remove(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbookEntry.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookEntryPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntry(long entryId) {\n\t\treturn guestbookEntryPersistence.fetchByPrimaryKey(entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchGuestbookEntryByUuidAndGroupId(\n\t\tString uuid, long groupId) {\n\n\t\treturn guestbookEntryPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws PortalException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntry(long entryId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookEntryLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookEntryLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(GuestbookEntry.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"entryId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbookEntry.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<GuestbookEntry>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(GuestbookEntry guestbookEntry)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbookEntry);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(GuestbookEntry.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryLocalService.deleteGuestbookEntry(\n\t\t\t(GuestbookEntry)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbook entries matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbook entries\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbook entries, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntriesByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn guestbookEntryPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook entry matching the UUID and group.\n\t *\n\t * @param uuid the guestbook entry's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook entry\n\t * @throws PortalException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry getGuestbookEntryByUuidAndGroupId(\n\t\t\tString uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookEntryPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> getGuestbookEntries(int start, int end) {\n\t\treturn guestbookEntryPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int getGuestbookEntriesCount() {\n\t\treturn guestbookEntryPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook entry in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t * @return the guestbook entry that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic GuestbookEntry updateGuestbookEntry(GuestbookEntry guestbookEntry) {\n\t\treturn guestbookEntryPersistence.update(guestbookEntry);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryLocalService = (GuestbookEntryLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\tprotected GuestbookEntryLocalService guestbookEntryLocalService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.WorkflowInstanceLinkLocalService\n\t\tworkflowInstanceLinkLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetEntryLocalService\n\t\tassetEntryLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetLinkLocalService\n\t\tassetLinkLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookEntryServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook entry remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookEntryServiceImpl\n * @generated\n */\npublic abstract class GuestbookEntryServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookEntryService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookEntryService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookEntryService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookEntryService = (GuestbookEntryService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookEntryService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn GuestbookEntry.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn GuestbookEntry.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookEntryPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookEntryLocalService\n\t\tguestbookEntryLocalService;\n\n\tprotected GuestbookEntryService guestbookEntryService;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.WorkflowInstanceLinkLocalService\n\t\tworkflowInstanceLinkLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetEntryLocalService\n\t\tassetEntryLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetEntryService\n\t\tassetEntryService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetLinkLocalService\n\t\tassetLinkLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookLocalServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.exportimport.kernel.lar.ExportImportHelperUtil;\nimport com.liferay.exportimport.kernel.lar.ManifestSummary;\nimport com.liferay.exportimport.kernel.lar.PortletDataContext;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandler;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerRegistryUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelDataHandlerUtil;\nimport com.liferay.exportimport.kernel.lar.StagedModelType;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Criterion;\nimport com.liferay.portal.kernel.dao.orm.DefaultActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Disjunction;\nimport com.liferay.portal.kernel.dao.orm.DynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.ExportActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;\nimport com.liferay.portal.kernel.dao.orm.Projection;\nimport com.liferay.portal.kernel.dao.orm.Property;\nimport com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;\nimport com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.PersistedModel;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.BaseLocalServiceImpl;\nimport com.liferay.portal.kernel.service.PersistedModelLocalService;\nimport com.liferay.portal.kernel.transaction.Transactional;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\n\nimport java.io.Serializable;\n\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook local service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookLocalServiceImpl\n * @generated\n */\n@ProviderType\npublic abstract class GuestbookLocalServiceBaseImpl\n\textends BaseLocalServiceImpl\n\timplements GuestbookLocalService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookLocalService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\t/**\n\t * Adds the guestbook to the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was added\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook addGuestbook(Guestbook guestbook) {\n\t\tguestbook.setNew(true);\n\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\t@Transactional(enabled = false)\n\tpublic Guestbook createGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.create(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.remove(guestbookId);\n\t}\n\n\t/**\n\t * Deletes the guestbook from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was removed\n\t */\n\t@Indexable(type = IndexableType.DELETE)\n\t@Override\n\tpublic Guestbook deleteGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.remove(guestbook);\n\t}\n\n\t@Override\n\tpublic DynamicQuery dynamicQuery() {\n\t\tClass<?> clazz = getClass();\n\n\t\treturn DynamicQueryFactoryUtil.forClass(\n\t\t\tGuestbook.class, clazz.getClassLoader());\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns the matching rows.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.findWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns a range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @return the range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end);\n\t}\n\n\t/**\n\t * Performs a dynamic query on the database and returns an ordered range of the matching rows.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param start the lower bound of the range of model instances\n\t * @param end the upper bound of the range of model instances (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching rows\n\t */\n\t@Override\n\tpublic <T> List<T> dynamicQuery(\n\t\tDynamicQuery dynamicQuery, int start, int end,\n\t\tOrderByComparator<T> orderByComparator) {\n\n\t\treturn guestbookPersistence.findWithDynamicQuery(\n\t\t\tdynamicQuery, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(DynamicQuery dynamicQuery) {\n\t\treturn guestbookPersistence.countWithDynamicQuery(dynamicQuery);\n\t}\n\n\t/**\n\t * Returns the number of rows matching the dynamic query.\n\t *\n\t * @param dynamicQuery the dynamic query\n\t * @param projection the projection to apply to the query\n\t * @return the number of rows matching the dynamic query\n\t */\n\t@Override\n\tpublic long dynamicQueryCount(\n\t\tDynamicQuery dynamicQuery, Projection projection) {\n\n\t\treturn guestbookPersistence.countWithDynamicQuery(\n\t\t\tdynamicQuery, projection);\n\t}\n\n\t@Override\n\tpublic Guestbook fetchGuestbook(long guestbookId) {\n\t\treturn guestbookPersistence.fetchByPrimaryKey(guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchGuestbookByUuidAndGroupId(String uuid, long groupId) {\n\t\treturn guestbookPersistence.fetchByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws PortalException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbook(long guestbookId) throws PortalException {\n\t\treturn guestbookPersistence.findByPrimaryKey(guestbookId);\n\t}\n\n\t@Override\n\tpublic ActionableDynamicQuery getActionableDynamicQuery() {\n\t\tActionableDynamicQuery actionableDynamicQuery =\n\t\t\tnew DefaultActionableDynamicQuery();\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\n\t\treturn actionableDynamicQuery;\n\t}\n\n\t@Override\n\tpublic IndexableActionableDynamicQuery\n\t\tgetIndexableActionableDynamicQuery() {\n\n\t\tIndexableActionableDynamicQuery indexableActionableDynamicQuery =\n\t\t\tnew IndexableActionableDynamicQuery();\n\n\t\tindexableActionableDynamicQuery.setBaseLocalService(\n\t\t\tguestbookLocalService);\n\t\tindexableActionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tindexableActionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tindexableActionableDynamicQuery.setPrimaryKeyPropertyName(\n\t\t\t\"guestbookId\");\n\n\t\treturn indexableActionableDynamicQuery;\n\t}\n\n\tprotected void initActionableDynamicQuery(\n\t\tActionableDynamicQuery actionableDynamicQuery) {\n\n\t\tactionableDynamicQuery.setBaseLocalService(guestbookLocalService);\n\t\tactionableDynamicQuery.setClassLoader(getClassLoader());\n\t\tactionableDynamicQuery.setModelClass(Guestbook.class);\n\n\t\tactionableDynamicQuery.setPrimaryKeyPropertyName(\"guestbookId\");\n\t}\n\n\t@Override\n\tpublic ExportActionableDynamicQuery getExportActionableDynamicQuery(\n\t\tfinal PortletDataContext portletDataContext) {\n\n\t\tfinal ExportActionableDynamicQuery exportActionableDynamicQuery =\n\t\t\tnew ExportActionableDynamicQuery() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic long performCount() throws PortalException {\n\t\t\t\t\tManifestSummary manifestSummary =\n\t\t\t\t\t\tportletDataContext.getManifestSummary();\n\n\t\t\t\t\tStagedModelType stagedModelType = getStagedModelType();\n\n\t\t\t\t\tlong modelAdditionCount = super.performCount();\n\n\t\t\t\t\tmanifestSummary.addModelAdditionCount(\n\t\t\t\t\t\tstagedModelType, modelAdditionCount);\n\n\t\t\t\t\tlong modelDeletionCount =\n\t\t\t\t\t\tExportImportHelperUtil.getModelDeletionCount(\n\t\t\t\t\t\t\tportletDataContext, stagedModelType);\n\n\t\t\t\t\tmanifestSummary.addModelDeletionCount(\n\t\t\t\t\t\tstagedModelType, modelDeletionCount);\n\n\t\t\t\t\treturn modelAdditionCount;\n\t\t\t\t}\n\n\t\t\t};\n\n\t\tinitActionableDynamicQuery(exportActionableDynamicQuery);\n\n\t\texportActionableDynamicQuery.setAddCriteriaMethod(\n\t\t\tnew ActionableDynamicQuery.AddCriteriaMethod() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void addCriteria(DynamicQuery dynamicQuery) {\n\t\t\t\t\tCriterion modifiedDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"modifiedDate\");\n\n\t\t\t\t\tCriterion statusDateCriterion =\n\t\t\t\t\t\tportletDataContext.getDateRangeCriteria(\"statusDate\");\n\n\t\t\t\t\tif ((modifiedDateCriterion != null) &&\n\t\t\t\t\t\t(statusDateCriterion != null)) {\n\n\t\t\t\t\t\tDisjunction disjunction =\n\t\t\t\t\t\t\tRestrictionsFactoryUtil.disjunction();\n\n\t\t\t\t\t\tdisjunction.add(modifiedDateCriterion);\n\t\t\t\t\t\tdisjunction.add(statusDateCriterion);\n\n\t\t\t\t\t\tdynamicQuery.add(disjunction);\n\t\t\t\t\t}\n\n\t\t\t\t\tProperty workflowStatusProperty =\n\t\t\t\t\t\tPropertyFactoryUtil.forName(\"status\");\n\n\t\t\t\t\tif (portletDataContext.isInitialPublication()) {\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.ne(\n\t\t\t\t\t\t\t\tWorkflowConstants.STATUS_IN_TRASH));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tStagedModelDataHandler<?> stagedModelDataHandler =\n\t\t\t\t\t\t\tStagedModelDataHandlerRegistryUtil.\n\t\t\t\t\t\t\t\tgetStagedModelDataHandler(\n\t\t\t\t\t\t\t\t\tGuestbook.class.getName());\n\n\t\t\t\t\t\tdynamicQuery.add(\n\t\t\t\t\t\t\tworkflowStatusProperty.in(\n\t\t\t\t\t\t\t\tstagedModelDataHandler.\n\t\t\t\t\t\t\t\t\tgetExportableStatuses()));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\texportActionableDynamicQuery.setCompanyId(\n\t\t\tportletDataContext.getCompanyId());\n\n\t\texportActionableDynamicQuery.setPerformActionMethod(\n\t\t\tnew ActionableDynamicQuery.PerformActionMethod<Guestbook>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void performAction(Guestbook guestbook)\n\t\t\t\t\tthrows PortalException {\n\n\t\t\t\t\tStagedModelDataHandlerUtil.exportStagedModel(\n\t\t\t\t\t\tportletDataContext, guestbook);\n\t\t\t\t}\n\n\t\t\t});\n\t\texportActionableDynamicQuery.setStagedModelType(\n\t\t\tnew StagedModelType(\n\t\t\t\tPortalUtil.getClassNameId(Guestbook.class.getName())));\n\n\t\treturn exportActionableDynamicQuery;\n\t}\n\n\t/**\n\t * @throws PortalException\n\t */\n\t@Override\n\tpublic PersistedModel deletePersistedModel(PersistedModel persistedModel)\n\t\tthrows PortalException {\n\n\t\treturn guestbookLocalService.deleteGuestbook((Guestbook)persistedModel);\n\t}\n\n\t@Override\n\tpublic PersistedModel getPersistedModel(Serializable primaryKeyObj)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByPrimaryKey(primaryKeyObj);\n\t}\n\n\t/**\n\t * Returns all the guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @return the matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId) {\n\n\t\treturn guestbookPersistence.findByUuid_C(uuid, companyId);\n\t}\n\n\t/**\n\t * Returns a range of guestbooks matching the UUID and company.\n\t *\n\t * @param uuid the UUID of the guestbooks\n\t * @param companyId the primary key of the company\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the range of matching guestbooks, or an empty list if no matches were found\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooksByUuidAndCompanyId(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn guestbookPersistence.findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator);\n\t}\n\n\t/**\n\t * Returns the guestbook matching the UUID and group.\n\t *\n\t * @param uuid the guestbook's UUID\n\t * @param groupId the primary key of the group\n\t * @return the matching guestbook\n\t * @throws PortalException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook getGuestbookByUuidAndGroupId(String uuid, long groupId)\n\t\tthrows PortalException {\n\n\t\treturn guestbookPersistence.findByUUID_G(uuid, groupId);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>com.liferay.docs.guestbook.model.impl.GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> getGuestbooks(int start, int end) {\n\t\treturn guestbookPersistence.findAll(start, end);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int getGuestbooksCount() {\n\t\treturn guestbookPersistence.countAll();\n\t}\n\n\t/**\n\t * Updates the guestbook in the database or adds it if it does not yet exist. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbook the guestbook\n\t * @return the guestbook that was updated\n\t */\n\t@Indexable(type = IndexableType.REINDEX)\n\t@Override\n\tpublic Guestbook updateGuestbook(Guestbook guestbook) {\n\t\treturn guestbookPersistence.update(guestbook);\n\t}\n\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookLocalService.class, IdentifiableOSGiService.class,\n\t\t\tPersistedModelLocalService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookLocalService = (GuestbookLocalService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookLocalService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\tprotected GuestbookLocalService guestbookLocalService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.WorkflowInstanceLinkLocalService\n\t\tworkflowInstanceLinkLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetEntryLocalService\n\t\tassetEntryLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetLinkLocalService\n\t\tassetLinkLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/base/GuestbookServiceBaseImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.base;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookService;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.dao.db.DB;\nimport com.liferay.portal.kernel.dao.db.DBManagerUtil;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdate;\nimport com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.module.framework.service.IdentifiableOSGiService;\nimport com.liferay.portal.kernel.service.BaseServiceImpl;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * Provides the base implementation for the guestbook remote service.\n *\n * <p>\n * This implementation exists only as a container for the default service methods generated by ServiceBuilder. All custom service methods should be put in {@link com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl}.\n * </p>\n *\n * @author Liferay\n * @see com.liferay.docs.guestbook.service.impl.GuestbookServiceImpl\n * @generated\n */\npublic abstract class GuestbookServiceBaseImpl\n\textends BaseServiceImpl\n\timplements GuestbookService, AopService, IdentifiableOSGiService {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Use <code>GuestbookService</code> via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code>.\n\t */\n\t@Override\n\tpublic Class<?>[] getAopInterfaces() {\n\t\treturn new Class<?>[] {\n\t\t\tGuestbookService.class, IdentifiableOSGiService.class\n\t\t};\n\t}\n\n\t@Override\n\tpublic void setAopProxy(Object aopProxy) {\n\t\tguestbookService = (GuestbookService)aopProxy;\n\t}\n\n\t/**\n\t * Returns the OSGi service identifier.\n\t *\n\t * @return the OSGi service identifier\n\t */\n\t@Override\n\tpublic String getOSGiServiceIdentifier() {\n\t\treturn GuestbookService.class.getName();\n\t}\n\n\tprotected Class<?> getModelClass() {\n\t\treturn Guestbook.class;\n\t}\n\n\tprotected String getModelClassName() {\n\t\treturn Guestbook.class.getName();\n\t}\n\n\t/**\n\t * Performs a SQL query.\n\t *\n\t * @param sql the sql query\n\t */\n\tprotected void runSQL(String sql) {\n\t\ttry {\n\t\t\tDataSource dataSource = guestbookPersistence.getDataSource();\n\n\t\t\tDB db = DBManagerUtil.getDB();\n\n\t\t\tsql = db.buildSQL(sql);\n\t\t\tsql = PortalUtil.transformSQL(sql);\n\n\t\t\tSqlUpdate sqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(\n\t\t\t\tdataSource, sql);\n\n\t\t\tsqlUpdate.update();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new SystemException(e);\n\t\t}\n\t}\n\n\t@Reference\n\tprotected com.liferay.docs.guestbook.service.GuestbookLocalService\n\t\tguestbookLocalService;\n\n\tprotected GuestbookService guestbookService;\n\n\t@Reference\n\tprotected GuestbookPersistence guestbookPersistence;\n\n\t@Reference\n\tprotected GuestbookEntryPersistence guestbookEntryPersistence;\n\n\t@Reference\n\tprotected com.liferay.counter.kernel.service.CounterLocalService\n\t\tcounterLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameLocalService\n\t\tclassNameLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ClassNameService\n\t\tclassNameService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.ResourceLocalService\n\t\tresourceLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserLocalService\n\t\tuserLocalService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.UserService userService;\n\n\t@Reference\n\tprotected com.liferay.portal.kernel.service.WorkflowInstanceLinkLocalService\n\t\tworkflowInstanceLinkLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetEntryLocalService\n\t\tassetEntryLocalService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetEntryService\n\t\tassetEntryService;\n\n\t@Reference\n\tprotected com.liferay.asset.kernel.service.AssetLinkLocalService\n\t\tassetLinkLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookEntryServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntry</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookEntrySoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookEntryServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceHttp.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the HTTP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The\n * static methods of this class calls the same methods of the service utility.\n * However, the signatures are different because it requires an additional\n * <code>com.liferay.portal.kernel.security.auth.HttpPrincipal</code> parameter.\n *\n * <p>\n * The benefits of using the HTTP utility is that it is fast and allows for\n * tunneling without the cost of serializing to text. The drawback is that it\n * only works with Java.\n * </p>\n *\n * <p>\n * Set the property <b>tunnel.servlet.hosts.allowed</b> in portal.properties to\n * configure security.\n * </p>\n *\n * <p>\n * The HTTP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceSoap\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceHttp {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/http/GuestbookServiceSoap.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.http;\n\nimport org.osgi.annotation.versioning.ProviderType;\n\n/**\n * Provides the SOAP utility for the\n * <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> service\n * utility. The static methods of this class call the same methods of the\n * service utility. However, the signatures are different because it is\n * difficult for SOAP to support certain types.\n *\n * <p>\n * ServiceBuilder follows certain rules in translating the methods. For example,\n * if the method in the service utility returns a <code>java.util.List</code>,\n * that is translated to an array of\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. If the method in the\n * service utility returns a\n * <code>com.liferay.docs.guestbook.model.Guestbook</code>, that is translated to a\n * <code>com.liferay.docs.guestbook.model.GuestbookSoap</code>. Methods that SOAP\n * cannot safely wire are skipped.\n * </p>\n *\n * <p>\n * The benefits of using the SOAP utility is that it is cross platform\n * compatible. SOAP allows different languages like Java, .NET, C++, PHP, and\n * even Perl, to call the generated services. One drawback of SOAP is that it is\n * slow because it needs to serialize all calls into a text format (XML).\n * </p>\n *\n * <p>\n * You can see a list of services at http://localhost:8080/api/axis. Set the\n * property <b>axis.servlet.hosts.allowed</b> in portal.properties to configure\n * security.\n * </p>\n *\n * <p>\n * The SOAP utility is only generated for remote services.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceHttp\n * @generated\n */\n@ProviderType\npublic class GuestbookServiceSoap {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\n\nimport com.liferay.asset.kernel.model.AssetEntry;\nimport com.liferay.asset.kernel.model.AssetLinkConstants;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryEmailException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryMessageException;\nimport com.liferay.docs.guestbook.exception.GuestbookEntryNameException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.ResourceConstants;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.ContentTypes;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\nimport com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;\n\n/**\n * The implementation of the guestbook entry local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry\", service = AopService.class)\npublic class GuestbookEntryLocalServiceImpl extends GuestbookEntryLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalService</code>\n\t * via injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil</code\n\t * >.\n\t */\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry addGuestbookEntry(long userId, long guestbookId, String name,\n\t\t\tString email, String message, ServiceContext serviceContext)\n\t\tthrows PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tlong entryId = counterLocalService.increment();\n\n\t\tGuestbookEntry entry = guestbookEntryPersistence.create(entryId);\n\n\t\tentry.setUuid(serviceContext.getUuid());\n\t\tentry.setUserId(userId);\n\t\tentry.setGroupId(groupId);\n\t\tentry.setCompanyId(user.getCompanyId());\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setCreateDate(serviceContext.getCreateDate(now));\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\t\tentry.setGuestbookId(guestbookId);\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\t\tentry.setStatus(WorkflowConstants.STATUS_DRAFT);\n\t\tentry.setStatusByUserId(userId);\n\t\tentry.setStatusByUserName(user.getFullName());\n\t\tentry.setStatusDate(serviceContext.getModifiedDate(null));\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Calls to other Liferay frameworks go here\n\n\t\tresourceLocalService.addResources(user.getCompanyId(), groupId, userId,\n\t\t\tGuestbookEntry.class.getName(), entryId, false, true, true);\n\n\t\tAssetEntry assetEntry = assetEntryLocalService.updateEntry(userId,\n                      groupId, entry.getCreateDate(), entry.getModifiedDate(),\n                      GuestbookEntry.class.getName(), entryId, entry.getUuid(), 0,\n                      serviceContext.getAssetCategoryIds(),\n                      serviceContext.getAssetTagNames(), true, true, null, null, null, null,\n                      ContentTypes.TEXT_HTML, entry.getMessage(), null, null, null,\n                      null, 0, 0, null);\n\n\t\tassetLinkLocalService.updateLinks(userId, assetEntry.getEntryId(),\n                      serviceContext.getAssetLinkEntryIds(),\n                      AssetLinkConstants.TYPE_RELATED);\n\n\t\tWorkflowHandlerRegistryUtil.startWorkflowInstance(entry.getCompanyId(), \n\t\t\t\tentry.getGroupId(), entry.getUserId(), GuestbookEntry.class.getName(), \n\t\t\t\tentry.getPrimaryKey(), entry, serviceContext);\n\t\treturn entry;\n\t}\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic GuestbookEntry updateGuestbookEntry(long userId, long guestbookId,\n\t\t\tlong entryId, String name, String email, String message,\n\t\t\tServiceContext serviceContext)\n\t\tthrows PortalException, SystemException {\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name, email, message);\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tentry.setUserId(userId);\n\t\tentry.setUserName(user.getFullName());\n\t\tentry.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tentry.setName(name);\n\t\tentry.setEmail(email);\n\t\tentry.setMessage(message);\n\t\tentry.setExpandoBridgeAttributes(serviceContext);\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\t// Integrate with Liferay frameworks here.\n\n        resourceLocalService.updateResources(\n              user.getCompanyId(), serviceContext.getScopeGroupId(), \n              GuestbookEntry.class.getName(), entryId, \n              serviceContext.getModelPermissions());\n\n        AssetEntry assetEntry = assetEntryLocalService.updateEntry(userId,\n                  serviceContext.getScopeGroupId(),\n                  entry.getCreateDate(), entry.getModifiedDate(),\n                  GuestbookEntry.class.getName(), entryId, entry.getUuid(),\n                  0, serviceContext.getAssetCategoryIds(),\n                  serviceContext.getAssetTagNames(), true, true,\n                  entry.getCreateDate(), null, null, null,\n                  ContentTypes.TEXT_HTML, entry.getMessage(), null,\n                  null, null, null, 0, 0,\n                  serviceContext.getAssetPriority());\n\n        assetLinkLocalService.updateLinks(userId, assetEntry.getEntryId(),\n                  serviceContext.getAssetLinkEntryIds(),\n                  AssetLinkConstants.TYPE_RELATED);\n\t\treturn entry;\n\t}\n\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic GuestbookEntry deleteGuestbookEntry(GuestbookEntry entry)\n\t\tthrows PortalException {\n\n\t\tguestbookEntryPersistence.remove(entry);\n\n\t\tresourceLocalService.deleteResource(\n\t\t   entry.getCompanyId(), GuestbookEntry.class.getName(),\n\t\t   ResourceConstants.SCOPE_INDIVIDUAL, entry.getEntryId());\n\n\t\tAssetEntry assetEntry = assetEntryLocalService.fetchEntry(\n                          GuestbookEntry.class.getName(), entry.getEntryId());\n\n\t\tassetLinkLocalService.deleteLinks(assetEntry.getEntryId());\n\n\t\tassetEntryLocalService.deleteEntry(assetEntry);\n\n\t\tworkflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(\n\t\t\tentry.getCompanyId(), entry.getGroupId(),\n\t\t\tGuestbookEntry.class.getName(), entry.getEntryId());\n\n\t\treturn entry;\n\t}\n\n\tpublic GuestbookEntry deleteGuestbookEntry(long entryId) throws PortalException {\n\n\t\tGuestbookEntry entry =\n\t\t\tguestbookEntryPersistence.findByPrimaryKey(entryId);\n\n\t\treturn deleteGuestbookEntry(entry);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end) throws SystemException {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend);\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(long groupId, long guestbookId,\n\t\t\tint start, int end, OrderByComparator<GuestbookEntry> obc) {\n\n\t\treturn guestbookEntryPersistence.findByG_G(groupId, guestbookId, start,\n\t\t\t\tend, obc);\n\t}\n\n\tpublic GuestbookEntry getGuestbookEntry(long entryId) throws PortalException {\n\t\treturn guestbookEntryPersistence.findByPrimaryKey(entryId);\n\t}\n\n\tpublic int getGuestbookEntriesCount(long groupId, long guestbookId) {\n\t\treturn guestbookEntryPersistence.countByG_G(groupId, guestbookId);\n\t}\n\n\tprotected void validate(String name, String email, String entry)\n\t\tthrows PortalException {\n\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookEntryNameException();\n\t\t}\n\n\t\tif (!Validator.isEmailAddress(email)) {\n\t\t\tthrow new GuestbookEntryEmailException();\n\t\t}\n\n\t\tif (Validator.isNull(entry)) {\n\t\t\tthrow new GuestbookEntryMessageException();\n\t\t}\n\t}\n\n     public GuestbookEntry updateStatus(long userId, long guestbookId, long entryId, int status,\n\t\t\tServiceContext serviceContext) throws PortalException,\n\t\t\tSystemException {\n\n\t\tUser user = userLocalService.getUser(userId);\n\t\tGuestbookEntry entry = getGuestbookEntry(entryId);\n\n\t\tentry.setStatus(status);\n\t\tentry.setStatusByUserId(userId);\n\t\tentry.setStatusByUserName(user.getFullName());\n\t\tentry.setStatusDate(new Date());\n\n\t\tguestbookEntryPersistence.update(entry);\n\n\t\tif (status == WorkflowConstants.STATUS_APPROVED) {\n\n\t\t\tassetEntryLocalService.updateVisible(GuestbookEntry.class.getName(),\n\t\t\t\t\tentryId, true);\n\n\t\t} else {\n\n\t\t\tassetEntryLocalService.updateVisible(GuestbookEntry.class.getName(),\n\t\t\t\t\tentryId, false);\n\t\t}\n\n\t\treturn entry;\n\t}\n\n\tpublic List<GuestbookEntry> getGuestbookEntries(\n\t\tlong groupId, long guestbookId, int status, int start, int end)\n\t\tthrows SystemException {\n\n\t\treturn guestbookEntryPersistence.findByG_G_S(\n\t\t\tgroupId, guestbookId, WorkflowConstants.STATUS_APPROVED);\n\t}\n\n\tpublic int getGuestbookEntriesCount(\n\t\tlong groupId, long guestbookId, int status)\n\t\tthrows SystemException {\n\n\t\treturn guestbookEntryPersistence.countByG_G_S(\n\t\t\tgroupId, guestbookId, WorkflowConstants.STATUS_APPROVED);\n\t}\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookEntryServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookEntryServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook entry remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookEntryService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookEntryServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=GuestbookEntry\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookEntryServiceImpl extends GuestbookEntryServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookEntryServiceUtil</code> to access the guestbook entry remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookLocalServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.asset.kernel.model.AssetEntry;\nimport com.liferay.asset.kernel.model.AssetLinkConstants;\nimport com.liferay.docs.guestbook.exception.GuestbookNameException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.base.GuestbookLocalServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.ResourceConstants;\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.search.Indexable;\nimport com.liferay.portal.kernel.search.IndexableType;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.ContentTypes;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\nimport com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;\n\n/**\n * The implementation of the guestbook local service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are\n * added, rerun ServiceBuilder to copy their definitions into the\n * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code>\n * interface.\n *\n * <p>\n * This is a local service. Methods of this service will not have security\n * checks based on the propagated JAAS credentials because this service can only\n * be accessed from within the same VM.\n * </p>\n *\n * @author Liferay\n * @see GuestbookLocalServiceBaseImpl\n */\n@Component(property = \"model.class.name=com.liferay.docs.guestbook.model.Guestbook\", service = AopService.class)\npublic class GuestbookLocalServiceImpl extends GuestbookLocalServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalService</code> via\n\t * injection or a <code>org.osgi.util.tracker.ServiceTracker</code> or use\n\t * <code>com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil</code>.\n\t */\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook addGuestbook(long userId, String name,\n\t\t\tServiceContext serviceContext) throws PortalException {\n\n\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\tUser user = userLocalService.getUserById(userId);\n\n\t\tDate now = new Date();\n\n\t\tvalidate(name);\n\n\t\tlong guestbookId = counterLocalService.increment();\n\n\t\tGuestbook guestbook = guestbookPersistence.create(guestbookId);\n\n\t\tguestbook.setUuid(serviceContext.getUuid());\n\t\tguestbook.setUserId(userId);\n\t\tguestbook.setGroupId(groupId);\n\t\tguestbook.setCompanyId(user.getCompanyId());\n\t\tguestbook.setUserName(user.getFullName());\n\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\tguestbook.setName(name);\n\t\tguestbook.setExpandoBridgeAttributes(serviceContext);\n\t\tguestbook.setStatus(WorkflowConstants.STATUS_DRAFT);\n\t\tguestbook.setStatusByUserId(userId);\n\t\tguestbook.setStatusByUserName(user.getFullName());\n\t\tguestbook.setStatusDate(serviceContext.getModifiedDate(null));\n\n\t\tguestbookPersistence.update(guestbook);\n\n\t\tresourceLocalService.addResources(user.getCompanyId(), groupId, userId,\n\t\t\t\tGuestbook.class.getName(), guestbookId, false, true, true);\n\n\t\tAssetEntry assetEntry = assetEntryLocalService.updateEntry(userId,\n                      groupId, guestbook.getCreateDate(),\n                      guestbook.getModifiedDate(), Guestbook.class.getName(),\n                      guestbookId, guestbook.getUuid(), 0,\n                      serviceContext.getAssetCategoryIds(),\n                      serviceContext.getAssetTagNames(), true, true, null, null, null, null,\n                      ContentTypes.TEXT_HTML, guestbook.getName(), null, null, null,\n                      null, 0, 0, null);\n\n\t\tassetLinkLocalService.updateLinks(userId, assetEntry.getEntryId(),\n                      serviceContext.getAssetLinkEntryIds(),\n                      AssetLinkConstants.TYPE_RELATED);\n\n\t\tWorkflowHandlerRegistryUtil.startWorkflowInstance(guestbook.getCompanyId(), \n\t\t\t\t\tguestbook.getGroupId(), guestbook.getUserId(), Guestbook.class.getName(), \n\t\t\t\t\tguestbook.getPrimaryKey(), guestbook, serviceContext);\n\n\t\treturn guestbook;\n\t}\n\n\t@Indexable(type = IndexableType.REINDEX)\n\tpublic Guestbook updateGuestbook(long userId, long guestbookId,\n        String name, ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Date now = new Date();\n\n            validate(name);\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            User user = userLocalService.getUser(userId);\n\n            guestbook.setUserId(userId);\n            guestbook.setUserName(user.getFullName());\n            guestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n            guestbook.setName(name);\n            guestbook.setExpandoBridgeAttributes(serviceContext);\n\n            guestbookPersistence.update(guestbook);\n\n            resourceLocalService.updateResources(serviceContext.getCompanyId(),\n                    serviceContext.getScopeGroupId(), \n                    Guestbook.class.getName(), guestbookId,\n                    serviceContext.getModelPermissions());\n\n            AssetEntry assetEntry = assetEntryLocalService.updateEntry(guestbook.getUserId(),\n                      guestbook.getGroupId(), guestbook.getCreateDate(),\n                      guestbook.getModifiedDate(), Guestbook.class.getName(),\n                      guestbookId, guestbook.getUuid(), 0,\n                      serviceContext.getAssetCategoryIds(),\n                      serviceContext.getAssetTagNames(), true, true, guestbook.getCreateDate(), \n                      null, null, null, ContentTypes.TEXT_HTML, guestbook.getName(), null, null, \n                      null, null, 0, 0, serviceContext.getAssetPriority());\n\n            assetLinkLocalService.updateLinks(serviceContext.getUserId(),\n                      assetEntry.getEntryId(), serviceContext.getAssetLinkEntryIds(),\n                      AssetLinkConstants.TYPE_RELATED);\n            return guestbook;\n    }\n\n\t@Indexable(type = IndexableType.DELETE)\n\tpublic Guestbook deleteGuestbook(long guestbookId,\n                    ServiceContext serviceContext) throws PortalException,\n                    SystemException {\n\n            Guestbook guestbook = getGuestbook(guestbookId);\n\n            List<GuestbookEntry> entries = _guestbookEntryLocalService.getGuestbookEntries(\n                            serviceContext.getScopeGroupId(), guestbookId);\n\n            for (GuestbookEntry entry : entries) {\n                    _guestbookEntryLocalService.deleteGuestbookEntry(entry.getEntryId());\n            }\n\n            guestbook = deleteGuestbook(guestbook);\n\n\t\t\tresourceLocalService.deleteResource(serviceContext.getCompanyId(),\n\t\t\t\t\tGuestbook.class.getName(), ResourceConstants.SCOPE_INDIVIDUAL,\n\t\t\t\t\tguestbookId);\n\n\t\t\tAssetEntry assetEntry = assetEntryLocalService.fetchEntry(\n                      Guestbook.class.getName(), guestbookId);\n\n\t\t\tassetLinkLocalService.deleteLinks(assetEntry.getEntryId());\n\n\t\t\tassetEntryLocalService.deleteEntry(assetEntry);\n\t\t\t\n\t\t\tworkflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(\n\t\t\t\t\tguestbook.getCompanyId(), guestbook.getGroupId(),\n\t\t\t\t\tGuestbook.class.getName(), guestbook.getGuestbookId());\n\n            return guestbook;\n    }\n\t\n    public Guestbook updateStatus(long userId, long guestbookId, int status,\n\t\t\tServiceContext serviceContext) throws PortalException,\n\t\t\tSystemException {\n\n\t\tUser user = userLocalService.getUser(userId);\n\t\tGuestbook guestbook = getGuestbook(guestbookId);\n\n\t\tguestbook.setStatus(status);\n\t\tguestbook.setStatusByUserId(userId);\n\t\tguestbook.setStatusByUserName(user.getFullName());\n\t\tguestbook.setStatusDate(new Date());\n\n\t\tguestbookPersistence.update(guestbook);\n\n\t\tif (status == WorkflowConstants.STATUS_APPROVED) {\n\n\t\t\tassetEntryLocalService.updateVisible(Guestbook.class.getName(),\n\t\t\t\t\tguestbookId, true);\n\n\t\t} else {\n\n\t\t\tassetEntryLocalService.updateVisible(Guestbook.class.getName(),\n\t\t\t\t\tguestbookId, false);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end,\n\t\t\tOrderByComparator<Guestbook> obc) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end, obc);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int start, int end) {\n\n\t\treturn guestbookPersistence.findByGroupId(groupId, start, end);\n\t}\n\n\tpublic List<Guestbook> getGuestbooks(long groupId, int status)\n\t\tthrows SystemException {\n\t\t\n\t\treturn guestbookPersistence.findByG_S(\n\t\t\tgroupId, WorkflowConstants.STATUS_APPROVED);\n\t}\n\n\tpublic int getGuestbooksCount(long groupId) {\n\n\t\treturn guestbookPersistence.countByGroupId(groupId);\n\t}\n\n\tprotected void validate(String name) throws PortalException {\n\t\tif (Validator.isNull(name)) {\n\t\t\tthrow new GuestbookNameException();\n\t\t}\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookServiceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.impl;\n\nimport com.liferay.docs.guestbook.service.base.GuestbookServiceBaseImpl;\nimport com.liferay.portal.aop.AopService;\n\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * The implementation of the guestbook remote service.\n *\n * <p>\n * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the <code>com.liferay.docs.guestbook.service.GuestbookService</code> interface.\n *\n * <p>\n * This is a remote service. Methods of this service are expected to have security checks based on the propagated JAAS credentials because this service can be accessed remotely.\n * </p>\n *\n * @author Liferay\n * @see GuestbookServiceBaseImpl\n */\n@Component(\n\tproperty = {\n\t\t\"json.web.service.context.name=gb\",\n\t\t\"json.web.service.context.path=Guestbook\"\n\t},\n\tservice = AopService.class\n)\npublic class GuestbookServiceImpl extends GuestbookServiceBaseImpl {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never reference this class directly. Always use <code>com.liferay.docs.guestbook.service.GuestbookServiceUtil</code> to access the guestbook remote service.\n\t */\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookEntryPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookEntryException;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookEntryModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookEntryPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.SQLQuery;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.security.permission.InlineSQLHelperUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook entry service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookEntryPersistence.class)\n@ProviderType\npublic class GuestbookEntryPersistenceImpl\n\textends BasePersistenceImpl<GuestbookEntry>\n\timplements GuestbookEntryPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookEntryUtil</code> to access the guestbook entry persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookEntryImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_First(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_First(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_First(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_Last(\n\t\t\tuuid, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_PrevAndNext(\n\t\t\tlong entryId, String uuid,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbookEntry.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook entry where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof GuestbookEntry) {\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbookEntry.getUuid()) ||\n\t\t\t\t(groupId != guestbookEntry.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<GuestbookEntry> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbookEntry guestbookEntry = list.get(0);\n\n\t\t\t\t\tresult = guestbookEntry;\n\n\t\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (GuestbookEntry)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook entry where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook entry that was removed\n\t */\n\t@Override\n\tpublic GuestbookEntry removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbookEntry);\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif (!uuid.equals(guestbookEntry.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbookEntry.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByUuid_C_PrevAndNext(\n\t\t\tlong entryId, String uuid, long companyId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\ttrue);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, uuid, companyId, orderByComparator,\n\t\t\t\tfalse);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByUuid_C_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, String uuid,\n\t\tlong companyId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbookEntry.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbookEntry.uuid IS NULL OR guestbookEntry.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbookEntry.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByG_G;\n\tprivate FinderPath _finderPathWithoutPaginationFindByG_G;\n\tprivate FinderPath _finderPathCountByG_G;\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(long groupId, long guestbookId) {\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn findByG_G(groupId, guestbookId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByG_G(\n\t\t\tgroupId, guestbookId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {groupId, guestbookId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByG_G;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tgroupId, guestbookId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif ((groupId != guestbookEntry.getGroupId()) ||\n\t\t\t\t\t\t(guestbookId != guestbookEntry.getGuestbookId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_First(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_First(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_First(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_Last(\n\t\t\tlong groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_Last(\n\t\t\tgroupId, guestbookId, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_Last(\n\t\tlong groupId, long guestbookId,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByG_G(groupId, guestbookId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByG_G(\n\t\t\tgroupId, guestbookId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByG_G_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tlong guestbookId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(guestbookId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId) {\n\n\t\treturn filterFindByG_G(\n\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end) {\n\n\t\treturn filterFindByG_G(groupId, guestbookId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_G(\n\t\tlong groupId, long guestbookId, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_G(\n\t\t\t\tgroupId, guestbookId, start, end, orderByComparator);\n\t\t}\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator, true);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_TABLE, orderByComparator, true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookEntryImpl.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookEntryImpl.class);\n\t\t\t}\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(guestbookId);\n\n\t\t\treturn (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\tq, getDialect(), start, end);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] filterFindByG_G_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_G_PrevAndNext(\n\t\t\t\tentryId, groupId, guestbookId, orderByComparator);\n\t\t}\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = filterGetByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = filterGetByG_G_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId,\n\t\t\t\torderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry filterGetByG_G_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tlong guestbookId, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t6 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByFields[i], true));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByFields[i], true));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookEntryImpl.class);\n\t\t}\n\t\telse {\n\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookEntryImpl.class);\n\t\t}\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(guestbookId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t */\n\t@Override\n\tpublic void removeByG_G(long groupId, long guestbookId) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByG_G(\n\t\t\t\t\tgroupId, guestbookId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByG_G(long groupId, long guestbookId) {\n\t\tFinderPath finderPath = _finderPathCountByG_G;\n\n\t\tObject[] finderArgs = new Object[] {groupId, guestbookId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic int filterCountByG_G(long groupId, long guestbookId) {\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn countByG_G(groupId, guestbookId);\n\t\t}\n\n\t\tStringBundler query = new StringBundler(3);\n\n\t\tquery.append(_FILTER_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_GUESTBOOKID_2);\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tq.addScalar(\n\t\t\t\tCOUNT_COLUMN_NAME, com.liferay.portal.kernel.dao.orm.Type.LONG);\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(guestbookId);\n\n\t\t\tLong count = (Long)q.uniqueResult();\n\n\t\t\treturn count.intValue();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprivate static final String _FINDER_COLUMN_G_G_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_G_G_GUESTBOOKID_2 =\n\t\t\"guestbookEntry.guestbookId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByStatus;\n\tprivate FinderPath _finderPathWithoutPaginationFindByStatus;\n\tprivate FinderPath _finderPathCountByStatus;\n\n\t/**\n\t * Returns all the guestbook entries where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByStatus(int status) {\n\t\treturn findByStatus(status, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByStatus(int status, int start, int end) {\n\t\treturn findByStatus(status, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByStatus(status, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByStatus;\n\t\t\tfinderArgs = new Object[] {status};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByStatus;\n\t\t\tfinderArgs = new Object[] {status, start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif ((status != guestbookEntry.getStatus())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_STATUS_STATUS_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByStatus_First(\n\t\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByStatus_First(\n\t\t\tstatus, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByStatus_First(\n\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByStatus(\n\t\t\tstatus, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByStatus_Last(\n\t\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByStatus_Last(\n\t\t\tstatus, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByStatus_Last(\n\t\tint status, OrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByStatus(status);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByStatus(\n\t\t\tstatus, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByStatus_PrevAndNext(\n\t\t\tlong entryId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByStatus_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, status, orderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByStatus_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, status, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByStatus_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, int status,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_STATUS_STATUS_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(status);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where status = &#63; from the database.\n\t *\n\t * @param status the status\n\t */\n\t@Override\n\tpublic void removeByStatus(int status) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByStatus(\n\t\t\t\t\tstatus, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByStatus(int status) {\n\t\tFinderPath finderPath = _finderPathCountByStatus;\n\n\t\tObject[] finderArgs = new Object[] {status};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_STATUS_STATUS_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_STATUS_STATUS_2 =\n\t\t\"guestbookEntry.status = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByG_G_S;\n\tprivate FinderPath _finderPathWithoutPaginationFindByG_G_S;\n\tprivate FinderPath _finderPathCountByG_G_S;\n\n\t/**\n\t * Returns all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @return the matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G_S(\n\t\tlong groupId, long guestbookId, int status) {\n\n\t\treturn findByG_G_S(\n\t\t\tgroupId, guestbookId, status, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\tnull);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end) {\n\n\t\treturn findByG_G_S(groupId, guestbookId, status, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findByG_G_S(\n\t\t\tgroupId, guestbookId, status, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByG_G_S;\n\t\t\tfinderArgs = new Object[] {groupId, guestbookId, status};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByG_G_S;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tgroupId, guestbookId, status, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (GuestbookEntry guestbookEntry : list) {\n\t\t\t\t\tif ((groupId != guestbookEntry.getGroupId()) ||\n\t\t\t\t\t\t(guestbookId != guestbookEntry.getGuestbookId()) ||\n\t\t\t\t\t\t(status != guestbookEntry.getStatus())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t5 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(5);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_S_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_S_GUESTBOOKID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_S_STATUS_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_S_First(\n\t\t\tlong groupId, long guestbookId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_S_First(\n\t\t\tgroupId, guestbookId, status, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(8);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\", status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_S_First(\n\t\tlong groupId, long guestbookId, int status,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tList<GuestbookEntry> list = findByG_G_S(\n\t\t\tgroupId, guestbookId, status, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByG_G_S_Last(\n\t\t\tlong groupId, long guestbookId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByG_G_S_Last(\n\t\t\tgroupId, guestbookId, status, orderByComparator);\n\n\t\tif (guestbookEntry != null) {\n\t\t\treturn guestbookEntry;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(8);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", guestbookId=\");\n\t\tmsg.append(guestbookId);\n\n\t\tmsg.append(\", status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookEntryException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook entry, or <code>null</code> if a matching guestbook entry could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByG_G_S_Last(\n\t\tlong groupId, long guestbookId, int status,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tint count = countByG_G_S(groupId, guestbookId, status);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<GuestbookEntry> list = findByG_G_S(\n\t\t\tgroupId, guestbookId, status, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] findByG_G_S_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = getByG_G_S_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId, status,\n\t\t\t\torderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = getByG_G_S_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId, status,\n\t\t\t\torderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry getByG_G_S_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tlong guestbookId, int status,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t6 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_S_GUESTBOOKID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_S_STATUS_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(guestbookId);\n\n\t\tqPos.add(status);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Returns all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @return the matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_G_S(\n\t\tlong groupId, long guestbookId, int status) {\n\n\t\treturn filterFindByG_G_S(\n\t\t\tgroupId, guestbookId, status, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\tnull);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end) {\n\n\t\treturn filterFindByG_G_S(\n\t\t\tgroupId, guestbookId, status, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries that the user has permissions to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> filterFindByG_G_S(\n\t\tlong groupId, long guestbookId, int status, int start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_G_S(\n\t\t\t\tgroupId, guestbookId, status, start, end, orderByComparator);\n\t\t}\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByFields().length * 2));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(6);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_S_GUESTBOOKID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_S_STATUS_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator, true);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_TABLE, orderByComparator, true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookEntryImpl.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookEntryImpl.class);\n\t\t\t}\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(guestbookId);\n\n\t\t\tqPos.add(status);\n\n\t\t\treturn (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\tq, getDialect(), start, end);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the guestbook entries before and after the current guestbook entry in the ordered set of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param entryId the primary key of the current guestbook entry\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry[] filterFindByG_G_S_PrevAndNext(\n\t\t\tlong entryId, long groupId, long guestbookId, int status,\n\t\t\tOrderByComparator<GuestbookEntry> orderByComparator)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_G_S_PrevAndNext(\n\t\t\t\tentryId, groupId, guestbookId, status, orderByComparator);\n\t\t}\n\n\t\tGuestbookEntry guestbookEntry = findByPrimaryKey(entryId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry[] array = new GuestbookEntryImpl[3];\n\n\t\t\tarray[0] = filterGetByG_G_S_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId, status,\n\t\t\t\torderByComparator, true);\n\n\t\t\tarray[1] = guestbookEntry;\n\n\t\t\tarray[2] = filterGetByG_G_S_PrevAndNext(\n\t\t\t\tsession, guestbookEntry, groupId, guestbookId, status,\n\t\t\t\torderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected GuestbookEntry filterGetByG_G_S_PrevAndNext(\n\t\tSession session, GuestbookEntry guestbookEntry, long groupId,\n\t\tlong guestbookId, int status,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t7 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(6);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_S_GUESTBOOKID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_S_STATUS_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByFields[i], true));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByFields[i], true));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookEntryModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookEntryImpl.class);\n\t\t}\n\t\telse {\n\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookEntryImpl.class);\n\t\t}\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(guestbookId);\n\n\t\tqPos.add(status);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(\n\t\t\t\t\t\tguestbookEntry)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<GuestbookEntry> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t */\n\t@Override\n\tpublic void removeByG_G_S(long groupId, long guestbookId, int status) {\n\t\tfor (GuestbookEntry guestbookEntry :\n\t\t\t\tfindByG_G_S(\n\t\t\t\t\tgroupId, guestbookId, status, QueryUtil.ALL_POS,\n\t\t\t\t\tQueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @return the number of matching guestbook entries\n\t */\n\t@Override\n\tpublic int countByG_G_S(long groupId, long guestbookId, int status) {\n\t\tFinderPath finderPath = _finderPathCountByG_G_S;\n\n\t\tObject[] finderArgs = new Object[] {groupId, guestbookId, status};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_S_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_S_GUESTBOOKID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_G_S_STATUS_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(guestbookId);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries that the user has permission to view where groupId = &#63; and guestbookId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param guestbookId the guestbook ID\n\t * @param status the status\n\t * @return the number of matching guestbook entries that the user has permission to view\n\t */\n\t@Override\n\tpublic int filterCountByG_G_S(long groupId, long guestbookId, int status) {\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn countByG_G_S(groupId, guestbookId, status);\n\t\t}\n\n\t\tStringBundler query = new StringBundler(4);\n\n\t\tquery.append(_FILTER_SQL_COUNT_GUESTBOOKENTRY_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_S_GUESTBOOKID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_G_S_STATUS_2);\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), GuestbookEntry.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tq.addScalar(\n\t\t\t\tCOUNT_COLUMN_NAME, com.liferay.portal.kernel.dao.orm.Type.LONG);\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(guestbookId);\n\n\t\t\tqPos.add(status);\n\n\t\t\tLong count = (Long)q.uniqueResult();\n\n\t\t\treturn count.intValue();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprivate static final String _FINDER_COLUMN_G_G_S_GROUPID_2 =\n\t\t\"guestbookEntry.groupId = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_G_G_S_GUESTBOOKID_2 =\n\t\t\"guestbookEntry.guestbookId = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_G_G_S_STATUS_2 =\n\t\t\"guestbookEntry.status = ?\";\n\n\tpublic GuestbookEntryPersistenceImpl() {\n\t\tsetModelClass(GuestbookEntry.class);\n\n\t\tsetModelImplClass(GuestbookEntryImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook entry in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntry the guestbook entry\n\t */\n\t@Override\n\tpublic void cacheResult(GuestbookEntry guestbookEntry) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {\n\t\t\t\tguestbookEntry.getUuid(), guestbookEntry.getGroupId()\n\t\t\t},\n\t\t\tguestbookEntry);\n\n\t\tguestbookEntry.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbook entries in the entity cache if it is enabled.\n\t *\n\t * @param guestbookEntries the guestbook entries\n\t */\n\t@Override\n\tpublic void cacheResult(List<GuestbookEntry> guestbookEntries) {\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbookEntry);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbook entries.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookEntryImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook entry.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(GuestbookEntry guestbookEntry) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookEntryModelImpl)guestbookEntry, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<GuestbookEntry> guestbookEntries) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (GuestbookEntry guestbookEntry : guestbookEntries) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\t\tguestbookEntry.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache(\n\t\t\t\t(GuestbookEntryModelImpl)guestbookEntry, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookEntryModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook entry with the primary key. Does not add the guestbook entry to the database.\n\t *\n\t * @param entryId the primary key for the new guestbook entry\n\t * @return the new guestbook entry\n\t */\n\t@Override\n\tpublic GuestbookEntry create(long entryId) {\n\t\tGuestbookEntry guestbookEntry = new GuestbookEntryImpl();\n\n\t\tguestbookEntry.setNew(true);\n\t\tguestbookEntry.setPrimaryKey(entryId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbookEntry.setUuid(uuid);\n\n\t\tguestbookEntry.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn remove((Serializable)entryId);\n\t}\n\n\t/**\n\t * Removes the guestbook entry with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry that was removed\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbookEntry guestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\tGuestbookEntryImpl.class, primaryKey);\n\n\t\t\tif (guestbookEntry == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbookEntry);\n\t\t}\n\t\tcatch (NoSuchGuestbookEntryException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected GuestbookEntry removeImpl(GuestbookEntry guestbookEntry) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbookEntry)) {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.get(\n\t\t\t\t\tGuestbookEntryImpl.class,\n\t\t\t\t\tguestbookEntry.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbookEntry != null) {\n\t\t\t\tsession.delete(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbookEntry != null) {\n\t\t\tclearCache(guestbookEntry);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t@Override\n\tpublic GuestbookEntry updateImpl(GuestbookEntry guestbookEntry) {\n\t\tboolean isNew = guestbookEntry.isNew();\n\n\t\tif (!(guestbookEntry instanceof GuestbookEntryModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbookEntry.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(\n\t\t\t\t\tguestbookEntry);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbookEntry proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom GuestbookEntry implementation \" +\n\t\t\t\t\tguestbookEntry.getClass());\n\t\t}\n\n\t\tGuestbookEntryModelImpl guestbookEntryModelImpl =\n\t\t\t(GuestbookEntryModelImpl)guestbookEntry;\n\n\t\tif (Validator.isNull(guestbookEntry.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbookEntry.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbookEntry.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookEntryModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbookEntry.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry.setModifiedDate(\n\t\t\t\t\tserviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbookEntry.isNew()) {\n\t\t\t\tsession.save(guestbookEntry);\n\n\t\t\t\tguestbookEntry.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbookEntry = (GuestbookEntry)session.merge(guestbookEntry);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\targs = new Object[] {guestbookEntryModelImpl.getStatus()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByStatus, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByStatus, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\tguestbookEntryModelImpl.getGuestbookId(),\n\t\t\t\tguestbookEntryModelImpl.getStatus()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByG_G_S, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByG_G_S, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookEntryModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getUuid(),\n\t\t\t\t\tguestbookEntryModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByG_G.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getGuestbookId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByStatus.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalStatus()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByStatus, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByStatus, args);\n\n\t\t\t\targs = new Object[] {guestbookEntryModelImpl.getStatus()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByStatus, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByStatus, args);\n\t\t\t}\n\n\t\t\tif ((guestbookEntryModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByG_G_S.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalGuestbookId(),\n\t\t\t\t\tguestbookEntryModelImpl.getOriginalStatus()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G_S, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G_S, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookEntryModelImpl.getGroupId(),\n\t\t\t\t\tguestbookEntryModelImpl.getGuestbookId(),\n\t\t\t\t\tguestbookEntryModelImpl.getStatus()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_G_S, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_G_S, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tguestbookEntry.getPrimaryKey(), guestbookEntry, false);\n\n\t\tclearUniqueFindersCache(guestbookEntryModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookEntryModelImpl);\n\n\t\tguestbookEntry.resetOriginalValues();\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\tGuestbookEntry guestbookEntry = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbookEntry == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookEntryException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbookEntry;\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or throws a <code>NoSuchGuestbookEntryException</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry\n\t * @throws NoSuchGuestbookEntryException if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry findByPrimaryKey(long entryId)\n\t\tthrows NoSuchGuestbookEntryException {\n\n\t\treturn findByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns the guestbook entry with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param entryId the primary key of the guestbook entry\n\t * @return the guestbook entry, or <code>null</code> if a guestbook entry with the primary key could not be found\n\t */\n\t@Override\n\tpublic GuestbookEntry fetchByPrimaryKey(long entryId) {\n\t\treturn fetchByPrimaryKey((Serializable)entryId);\n\t}\n\n\t/**\n\t * Returns all the guestbook entries.\n\t *\n\t * @return the guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @return the range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end,\n\t\tOrderByComparator<GuestbookEntry> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbook entries.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookEntryModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbook entries\n\t * @param end the upper bound of the range of guestbook entries (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbook entries\n\t */\n\t@Override\n\tpublic List<GuestbookEntry> findAll(\n\t\tint start, int end, OrderByComparator<GuestbookEntry> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<GuestbookEntry> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<GuestbookEntry>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOKENTRY);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOKENTRY;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookEntryModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<GuestbookEntry>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbook entries from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (GuestbookEntry guestbookEntry : findAll()) {\n\t\t\tremove(guestbookEntry);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbook entries.\n\t *\n\t * @return the number of guestbook entries\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOKENTRY);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"entryId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOKENTRY;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookEntryModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook entry persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookEntryModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookEntryModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.COMPANYID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()},\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GUESTBOOKID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByG_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByG_G\",\n\t\t\tnew String[] {Long.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByStatus = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByStatus\",\n\t\t\tnew String[] {\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByStatus = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByStatus\",\n\t\t\tnew String[] {Integer.class.getName()},\n\t\t\tGuestbookEntryModelImpl.STATUS_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByStatus = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByStatus\",\n\t\t\tnew String[] {Integer.class.getName()});\n\n\t\t_finderPathWithPaginationFindByG_G_S = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByG_G_S\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByG_G_S = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookEntryImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByG_G_S\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName()\n\t\t\t},\n\t\t\tGuestbookEntryModelImpl.GROUPID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.GUESTBOOKID_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.STATUS_COLUMN_BITMASK |\n\t\t\tGuestbookEntryModelImpl.CREATEDATE_COLUMN_BITMASK);\n\n\t\t_finderPathCountByG_G_S = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByG_G_S\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName()\n\t\t\t});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookEntryImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.GuestbookEntry\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT guestbookEntry FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT COUNT(guestbookEntry) FROM GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _FILTER_ENTITY_TABLE_FILTER_PK_COLUMN =\n\t\t\"guestbookEntry.entryId\";\n\n\tprivate static final String _FILTER_SQL_SELECT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT DISTINCT {guestbookEntry.*} FROM GB_GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String\n\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_1 =\n\t\t\t\"SELECT {GB_GuestbookEntry.*} FROM (SELECT DISTINCT guestbookEntry.entryId FROM GB_GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String\n\t\t_FILTER_SQL_SELECT_GUESTBOOKENTRY_NO_INLINE_DISTINCT_WHERE_2 =\n\t\t\t\") TEMP_TABLE INNER JOIN GB_GuestbookEntry ON TEMP_TABLE.entryId = GB_GuestbookEntry.entryId\";\n\n\tprivate static final String _FILTER_SQL_COUNT_GUESTBOOKENTRY_WHERE =\n\t\t\"SELECT COUNT(DISTINCT guestbookEntry.entryId) AS COUNT_VALUE FROM GB_GuestbookEntry guestbookEntry WHERE \";\n\n\tprivate static final String _FILTER_ENTITY_ALIAS = \"guestbookEntry\";\n\n\tprivate static final String _FILTER_ENTITY_TABLE = \"GB_GuestbookEntry\";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbookEntry.\";\n\n\tprivate static final String _ORDER_BY_ENTITY_TABLE = \"GB_GuestbookEntry.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No GuestbookEntry exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No GuestbookEntry exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookEntryPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/GuestbookPersistenceImpl.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl;\n\nimport com.liferay.docs.guestbook.exception.NoSuchGuestbookException;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.impl.GuestbookImpl;\nimport com.liferay.docs.guestbook.model.impl.GuestbookModelImpl;\nimport com.liferay.docs.guestbook.service.persistence.GuestbookPersistence;\nimport com.liferay.docs.guestbook.service.persistence.impl.constants.GBPersistenceConstants;\nimport com.liferay.petra.string.StringBundler;\nimport com.liferay.portal.kernel.configuration.Configuration;\nimport com.liferay.portal.kernel.dao.orm.EntityCache;\nimport com.liferay.portal.kernel.dao.orm.FinderCache;\nimport com.liferay.portal.kernel.dao.orm.FinderPath;\nimport com.liferay.portal.kernel.dao.orm.Query;\nimport com.liferay.portal.kernel.dao.orm.QueryPos;\nimport com.liferay.portal.kernel.dao.orm.QueryUtil;\nimport com.liferay.portal.kernel.dao.orm.SQLQuery;\nimport com.liferay.portal.kernel.dao.orm.Session;\nimport com.liferay.portal.kernel.dao.orm.SessionFactory;\nimport com.liferay.portal.kernel.log.Log;\nimport com.liferay.portal.kernel.log.LogFactoryUtil;\nimport com.liferay.portal.kernel.security.permission.InlineSQLHelperUtil;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextThreadLocal;\nimport com.liferay.portal.kernel.service.persistence.CompanyProvider;\nimport com.liferay.portal.kernel.service.persistence.CompanyProviderWrapper;\nimport com.liferay.portal.kernel.service.persistence.impl.BasePersistenceImpl;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.util.OrderByComparator;\nimport com.liferay.portal.kernel.util.ProxyUtil;\nimport com.liferay.portal.kernel.util.SetUtil;\nimport com.liferay.portal.kernel.util.Validator;\nimport com.liferay.portal.kernel.uuid.PortalUUIDUtil;\n\nimport java.io.Serializable;\n\nimport java.lang.reflect.InvocationHandler;\n\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.osgi.annotation.versioning.ProviderType;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Deactivate;\nimport org.osgi.service.component.annotations.Reference;\n\n/**\n * The persistence implementation for the guestbook service.\n *\n * <p>\n * Caching information and settings can be found in <code>portal.properties</code>\n * </p>\n *\n * @author Liferay\n * @generated\n */\n@Component(service = GuestbookPersistence.class)\n@ProviderType\npublic class GuestbookPersistenceImpl\n\textends BasePersistenceImpl<Guestbook> implements GuestbookPersistence {\n\n\t/*\n\t * NOTE FOR DEVELOPERS:\n\t *\n\t * Never modify or reference this class directly. Always use <code>GuestbookUtil</code> to access the guestbook persistence. Modify <code>service.xml</code> and rerun ServiceBuilder to regenerate this class.\n\t */\n\tpublic static final String FINDER_CLASS_NAME_ENTITY =\n\t\tGuestbookImpl.class.getName();\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITH_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List1\";\n\n\tpublic static final String FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION =\n\t\tFINDER_CLASS_NAME_ENTITY + \".List2\";\n\n\tprivate FinderPath _finderPathWithPaginationFindAll;\n\tprivate FinderPath _finderPathWithoutPaginationFindAll;\n\tprivate FinderPath _finderPathCountAll;\n\tprivate FinderPath _finderPathWithPaginationFindByUuid;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid;\n\tprivate FinderPath _finderPathCountByUuid;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid) {\n\t\treturn findByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(String uuid, int start, int end) {\n\t\treturn findByUuid(uuid, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid(uuid, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid(\n\t\tString uuid, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid;\n\t\t\tfinderArgs = new Object[] {uuid, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_First(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_First(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_First(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid(uuid, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_Last(\n\t\t\tString uuid, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_Last(uuid, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_Last(\n\t\tString uuid, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid(uuid);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid(\n\t\t\tuuid, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_PrevAndNext(\n\t\t\tlong guestbookId, String uuid,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t */\n\t@Override\n\tpublic void removeByUuid(String uuid) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid(uuid, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid(String uuid) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid;\n\n\t\tObject[] finderArgs = new Object[] {uuid};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_UUID_2);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_2 =\n\t\t\"guestbook.uuid = ?\";\n\n\tprivate static final String _FINDER_COLUMN_UUID_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '')\";\n\n\tprivate FinderPath _finderPathFetchByUUID_G;\n\tprivate FinderPath _finderPathCountByUUID_G;\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUUID_G(uuid, groupId);\n\n\t\tif (guestbook == null) {\n\t\t\tStringBundler msg = new StringBundler(6);\n\n\t\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\t\tmsg.append(\"uuid=\");\n\t\t\tmsg.append(uuid);\n\n\t\t\tmsg.append(\", groupId=\");\n\t\t\tmsg.append(groupId);\n\n\t\t\tmsg.append(\"}\");\n\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(msg.toString());\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found. Uses the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(String uuid, long groupId) {\n\t\treturn fetchByUUID_G(uuid, groupId, true);\n\t}\n\n\t/**\n\t * Returns the guestbook where uuid = &#63; and groupId = &#63; or returns <code>null</code> if it could not be found, optionally using the finder cache.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUUID_G(\n\t\tString uuid, long groupId, boolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tObject result = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tresult = finderCache.getResult(\n\t\t\t\t_finderPathFetchByUUID_G, finderArgs, this);\n\t\t}\n\n\t\tif (result instanceof Guestbook) {\n\t\t\tGuestbook guestbook = (Guestbook)result;\n\n\t\t\tif (!Objects.equals(uuid, guestbook.getUuid()) ||\n\t\t\t\t(groupId != guestbook.getGroupId())) {\n\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\tif (result == null) {\n\t\t\tStringBundler query = new StringBundler(4);\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tList<Guestbook> list = q.list();\n\n\t\t\t\tif (list.isEmpty()) {\n\t\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t\t_finderPathFetchByUUID_G, finderArgs, list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tGuestbook guestbook = list.get(0);\n\n\t\t\t\t\tresult = guestbook;\n\n\t\t\t\t\tcacheResult(guestbook);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\tif (result instanceof List<?>) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\treturn (Guestbook)result;\n\t\t}\n\t}\n\n\t/**\n\t * Removes the guestbook where uuid = &#63; and groupId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the guestbook that was removed\n\t */\n\t@Override\n\tpublic Guestbook removeByUUID_G(String uuid, long groupId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByUUID_G(uuid, groupId);\n\n\t\treturn remove(guestbook);\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and groupId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUUID_G(String uuid, long groupId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUUID_G;\n\n\t\tObject[] finderArgs = new Object[] {uuid, groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_G_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_G_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_G_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathWithoutPaginationFindByUuid_C;\n\tprivate FinderPath _finderPathCountByUuid_C;\n\n\t/**\n\t * Returns all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(String uuid, long companyId) {\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end) {\n\n\t\treturn findByUuid_C(uuid, companyId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByUuid_C(\n\t\t\tuuid, companyId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByUuid_C(\n\t\tString uuid, long companyId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {uuid, companyId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByUuid_C;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tuuid, companyId, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif (!uuid.equals(guestbook.getUuid()) ||\n\t\t\t\t\t\t(companyId != guestbook.getCompanyId())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_First(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_First(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_First(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByUuid_C_Last(\n\t\t\tString uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByUuid_C_Last(\n\t\t\tuuid, companyId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"uuid=\");\n\t\tmsg.append(uuid);\n\n\t\tmsg.append(\", companyId=\");\n\t\tmsg.append(companyId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByUuid_C_Last(\n\t\tString uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByUuid_C(uuid, companyId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByUuid_C(\n\t\t\tuuid, companyId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByUuid_C_PrevAndNext(\n\t\t\tlong guestbookId, String uuid, long companyId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByUuid_C_PrevAndNext(\n\t\t\t\tsession, guestbook, uuid, companyId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByUuid_C_PrevAndNext(\n\t\tSession session, Guestbook guestbook, String uuid, long companyId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tboolean bindUuid = false;\n\n\t\tif (uuid.isEmpty()) {\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t}\n\t\telse {\n\t\t\tbindUuid = true;\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tif (bindUuid) {\n\t\t\tqPos.add(uuid);\n\t\t}\n\n\t\tqPos.add(companyId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where uuid = &#63; and companyId = &#63; from the database.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t */\n\t@Override\n\tpublic void removeByUuid_C(String uuid, long companyId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByUuid_C(\n\t\t\t\t\tuuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where uuid = &#63; and companyId = &#63;.\n\t *\n\t * @param uuid the uuid\n\t * @param companyId the company ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByUuid_C(String uuid, long companyId) {\n\t\tuuid = Objects.toString(uuid, \"\");\n\n\t\tFinderPath finderPath = _finderPathCountByUuid_C;\n\n\t\tObject[] finderArgs = new Object[] {uuid, companyId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tboolean bindUuid = false;\n\n\t\t\tif (uuid.isEmpty()) {\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_3);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbindUuid = true;\n\n\t\t\t\tquery.append(_FINDER_COLUMN_UUID_C_UUID_2);\n\t\t\t}\n\n\t\t\tquery.append(_FINDER_COLUMN_UUID_C_COMPANYID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tif (bindUuid) {\n\t\t\t\t\tqPos.add(uuid);\n\t\t\t\t}\n\n\t\t\t\tqPos.add(companyId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_2 =\n\t\t\"guestbook.uuid = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_UUID_3 =\n\t\t\"(guestbook.uuid IS NULL OR guestbook.uuid = '') AND \";\n\n\tprivate static final String _FINDER_COLUMN_UUID_C_COMPANYID_2 =\n\t\t\"guestbook.companyId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByGroupId;\n\tprivate FinderPath _finderPathWithoutPaginationFindByGroupId;\n\tprivate FinderPath _finderPathCountByGroupId;\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId) {\n\t\treturn findByGroupId(\n\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(long groupId, int start, int end) {\n\t\treturn findByGroupId(groupId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByGroupId(groupId, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByGroupId;\n\t\t\tfinderArgs = new Object[] {groupId, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif ((groupId != guestbook.getGroupId())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_First(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_First(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_First(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByGroupId(groupId, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByGroupId_Last(\n\t\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByGroupId_Last(groupId, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByGroupId_Last(\n\t\tlong groupId, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByGroupId(groupId);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByGroupId(\n\t\t\tgroupId, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByGroupId_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByGroupId(long groupId) {\n\t\treturn filterFindByGroupId(\n\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end) {\n\n\t\treturn filterFindByGroupId(groupId, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByGroupId(\n\t\tlong groupId, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByGroupId(groupId, start, end, orderByComparator);\n\t\t}\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOK_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator, true);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_TABLE, orderByComparator, true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookImpl.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookImpl.class);\n\t\t\t}\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\treturn (List<Guestbook>)QueryUtil.list(q, getDialect(), start, end);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] filterFindByGroupId_PrevAndNext(\n\t\t\tlong guestbookId, long groupId,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByGroupId_PrevAndNext(\n\t\t\t\tguestbookId, groupId, orderByComparator);\n\t\t}\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = filterGetByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = filterGetByGroupId_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook filterGetByGroupId_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOK_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByFields[i], true));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByFields[i], true));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookImpl.class);\n\t\t}\n\t\telse {\n\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookImpl.class);\n\t\t}\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t */\n\t@Override\n\tpublic void removeByGroupId(long groupId) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByGroupId(\n\t\t\t\t\tgroupId, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByGroupId(long groupId) {\n\t\tFinderPath finderPath = _finderPathCountByGroupId;\n\n\t\tObject[] finderArgs = new Object[] {groupId};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic int filterCountByGroupId(long groupId) {\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn countByGroupId(groupId);\n\t\t}\n\n\t\tStringBundler query = new StringBundler(2);\n\n\t\tquery.append(_FILTER_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_GROUPID_GROUPID_2);\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tq.addScalar(\n\t\t\t\tCOUNT_COLUMN_NAME, com.liferay.portal.kernel.dao.orm.Type.LONG);\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tLong count = (Long)q.uniqueResult();\n\n\t\t\treturn count.intValue();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprivate static final String _FINDER_COLUMN_GROUPID_GROUPID_2 =\n\t\t\"guestbook.groupId = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByStatus;\n\tprivate FinderPath _finderPathWithoutPaginationFindByStatus;\n\tprivate FinderPath _finderPathCountByStatus;\n\n\t/**\n\t * Returns all the guestbooks where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByStatus(int status) {\n\t\treturn findByStatus(status, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByStatus(int status, int start, int end) {\n\t\treturn findByStatus(status, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByStatus(status, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByStatus(\n\t\tint status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByStatus;\n\t\t\tfinderArgs = new Object[] {status};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByStatus;\n\t\t\tfinderArgs = new Object[] {status, start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif ((status != guestbook.getStatus())) {\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t3 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(3);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_STATUS_STATUS_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByStatus_First(\n\t\t\tint status, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByStatus_First(status, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByStatus_First(\n\t\tint status, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByStatus(status, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByStatus_Last(\n\t\t\tint status, OrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByStatus_Last(status, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(4);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByStatus_Last(\n\t\tint status, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByStatus(status);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByStatus(\n\t\t\tstatus, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByStatus_PrevAndNext(\n\t\t\tlong guestbookId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByStatus_PrevAndNext(\n\t\t\t\tsession, guestbook, status, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByStatus_PrevAndNext(\n\t\t\t\tsession, guestbook, status, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByStatus_PrevAndNext(\n\t\tSession session, Guestbook guestbook, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(3);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_STATUS_STATUS_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(status);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where status = &#63; from the database.\n\t *\n\t * @param status the status\n\t */\n\t@Override\n\tpublic void removeByStatus(int status) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByStatus(\n\t\t\t\t\tstatus, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where status = &#63;.\n\t *\n\t * @param status the status\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByStatus(int status) {\n\t\tFinderPath finderPath = _finderPathCountByStatus;\n\n\t\tObject[] finderArgs = new Object[] {status};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(2);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_STATUS_STATUS_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\tprivate static final String _FINDER_COLUMN_STATUS_STATUS_2 =\n\t\t\"guestbook.status = ?\";\n\n\tprivate FinderPath _finderPathWithPaginationFindByG_S;\n\tprivate FinderPath _finderPathWithoutPaginationFindByG_S;\n\tprivate FinderPath _finderPathCountByG_S;\n\n\t/**\n\t * Returns all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByG_S(long groupId, int status) {\n\t\treturn findByG_S(\n\t\t\tgroupId, status, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end) {\n\n\t\treturn findByG_S(groupId, status, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findByG_S(groupId, status, start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of matching guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindByG_S;\n\t\t\tfinderArgs = new Object[] {groupId, status};\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindByG_S;\n\t\t\tfinderArgs = new Object[] {\n\t\t\t\tgroupId, status, start, end, orderByComparator\n\t\t\t};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\n\t\t\tif ((list != null) && !list.isEmpty()) {\n\t\t\t\tfor (Guestbook guestbook : list) {\n\t\t\t\t\tif ((groupId != guestbook.getGroupId()) ||\n\t\t\t\t\t\t(status != guestbook.getStatus())) {\n\n\t\t\t\t\t\tlist = null;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery = new StringBundler(4);\n\t\t\t}\n\n\t\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\t\t\t}\n\t\t\telse if (pagination) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByG_S_First(\n\t\t\tlong groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByG_S_First(\n\t\t\tgroupId, status, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the first guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the first matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByG_S_First(\n\t\tlong groupId, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tList<Guestbook> list = findByG_S(\n\t\t\tgroupId, status, 0, 1, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook\n\t * @throws NoSuchGuestbookException if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByG_S_Last(\n\t\t\tlong groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByG_S_Last(\n\t\t\tgroupId, status, orderByComparator);\n\n\t\tif (guestbook != null) {\n\t\t\treturn guestbook;\n\t\t}\n\n\t\tStringBundler msg = new StringBundler(6);\n\n\t\tmsg.append(_NO_SUCH_ENTITY_WITH_KEY);\n\n\t\tmsg.append(\"groupId=\");\n\t\tmsg.append(groupId);\n\n\t\tmsg.append(\", status=\");\n\t\tmsg.append(status);\n\n\t\tmsg.append(\"}\");\n\n\t\tthrow new NoSuchGuestbookException(msg.toString());\n\t}\n\n\t/**\n\t * Returns the last guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the last matching guestbook, or <code>null</code> if a matching guestbook could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByG_S_Last(\n\t\tlong groupId, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tint count = countByG_S(groupId, status);\n\n\t\tif (count == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<Guestbook> list = findByG_S(\n\t\t\tgroupId, status, count - 1, count, orderByComparator);\n\n\t\tif (!list.isEmpty()) {\n\t\t\treturn list.get(0);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set where groupId = &#63; and status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] findByG_S_PrevAndNext(\n\t\t\tlong guestbookId, long groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = getByG_S_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, status, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = getByG_S_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, status, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook getByG_S_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t5 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(4);\n\t\t}\n\n\t\tquery.append(_SQL_SELECT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByConditionFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tquery.append(_ORDER_BY_ENTITY_ALIAS);\n\t\t\t\tquery.append(orderByFields[i]);\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t}\n\n\t\tString sql = query.toString();\n\n\t\tQuery q = session.createQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(status);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Returns all the guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByG_S(long groupId, int status) {\n\t\treturn filterFindByG_S(\n\t\t\tgroupId, status, QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end) {\n\n\t\treturn filterFindByG_S(groupId, status, start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks that the user has permissions to view where groupId = &#63; and status = &#63;.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic List<Guestbook> filterFindByG_S(\n\t\tlong groupId, int status, int start, int end,\n\t\tOrderByComparator<Guestbook> orderByComparator) {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_S(groupId, status, start, end, orderByComparator);\n\t\t}\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t4 + (orderByComparator.getOrderByFields().length * 2));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOK_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator, true);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_TABLE, orderByComparator, true);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookImpl.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookImpl.class);\n\t\t\t}\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(status);\n\n\t\t\treturn (List<Guestbook>)QueryUtil.list(q, getDialect(), start, end);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the guestbooks before and after the current guestbook in the ordered set of guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param guestbookId the primary key of the current guestbook\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @param orderByComparator the comparator to order the set by (optionally <code>null</code>)\n\t * @return the previous, current, and next guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook[] filterFindByG_S_PrevAndNext(\n\t\t\tlong guestbookId, long groupId, int status,\n\t\t\tOrderByComparator<Guestbook> orderByComparator)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn findByG_S_PrevAndNext(\n\t\t\t\tguestbookId, groupId, status, orderByComparator);\n\t\t}\n\n\t\tGuestbook guestbook = findByPrimaryKey(guestbookId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook[] array = new GuestbookImpl[3];\n\n\t\t\tarray[0] = filterGetByG_S_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, status, orderByComparator, true);\n\n\t\t\tarray[1] = guestbook;\n\n\t\t\tarray[2] = filterGetByG_S_PrevAndNext(\n\t\t\t\tsession, guestbook, groupId, status, orderByComparator, false);\n\n\t\t\treturn array;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprotected Guestbook filterGetByG_S_PrevAndNext(\n\t\tSession session, Guestbook guestbook, long groupId, int status,\n\t\tOrderByComparator<Guestbook> orderByComparator, boolean previous) {\n\n\t\tStringBundler query = null;\n\n\t\tif (orderByComparator != null) {\n\t\t\tquery = new StringBundler(\n\t\t\t\t6 + (orderByComparator.getOrderByConditionFields().length * 3) +\n\t\t\t\t\t(orderByComparator.getOrderByFields().length * 3));\n\t\t}\n\t\telse {\n\t\t\tquery = new StringBundler(5);\n\t\t}\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(_FILTER_SQL_SELECT_GUESTBOOK_WHERE);\n\t\t}\n\t\telse {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1);\n\t\t}\n\n\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\tif (!getDB().isSupportsInlineDistinct()) {\n\t\t\tquery.append(\n\t\t\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2);\n\t\t}\n\n\t\tif (orderByComparator != null) {\n\t\t\tString[] orderByConditionFields =\n\t\t\t\torderByComparator.getOrderByConditionFields();\n\n\t\t\tif (orderByConditionFields.length > 0) {\n\t\t\t\tquery.append(WHERE_AND);\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < orderByConditionFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByConditionFields[i],\n\t\t\t\t\t\t\ttrue));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByConditionFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(WHERE_GREATER_THAN);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(WHERE_LESSER_THAN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tquery.append(ORDER_BY_CLAUSE);\n\n\t\t\tString[] orderByFields = orderByComparator.getOrderByFields();\n\n\t\t\tfor (int i = 0; i < orderByFields.length; i++) {\n\t\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_ALIAS, orderByFields[i], true));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tquery.append(\n\t\t\t\t\t\tgetColumnName(\n\t\t\t\t\t\t\t_ORDER_BY_ENTITY_TABLE, orderByFields[i], true));\n\t\t\t\t}\n\n\t\t\t\tif ((i + 1) < orderByFields.length) {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC_HAS_NEXT);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (orderByComparator.isAscending() ^ previous) {\n\t\t\t\t\t\tquery.append(ORDER_BY_ASC);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tquery.append(ORDER_BY_DESC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tquery.append(GuestbookModelImpl.ORDER_BY_SQL);\n\t\t\t}\n\t\t}\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\tq.setFirstResult(0);\n\t\tq.setMaxResults(2);\n\n\t\tif (getDB().isSupportsInlineDistinct()) {\n\t\t\tq.addEntity(_FILTER_ENTITY_ALIAS, GuestbookImpl.class);\n\t\t}\n\t\telse {\n\t\t\tq.addEntity(_FILTER_ENTITY_TABLE, GuestbookImpl.class);\n\t\t}\n\n\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\tqPos.add(groupId);\n\n\t\tqPos.add(status);\n\n\t\tif (orderByComparator != null) {\n\t\t\tfor (Object orderByConditionValue :\n\t\t\t\t\torderByComparator.getOrderByConditionValues(guestbook)) {\n\n\t\t\t\tqPos.add(orderByConditionValue);\n\t\t\t}\n\t\t}\n\n\t\tList<Guestbook> list = q.list();\n\n\t\tif (list.size() == 2) {\n\t\t\treturn list.get(1);\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Removes all the guestbooks where groupId = &#63; and status = &#63; from the database.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t */\n\t@Override\n\tpublic void removeByG_S(long groupId, int status) {\n\t\tfor (Guestbook guestbook :\n\t\t\t\tfindByG_S(\n\t\t\t\t\tgroupId, status, QueryUtil.ALL_POS, QueryUtil.ALL_POS,\n\t\t\t\t\tnull)) {\n\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbooks\n\t */\n\t@Override\n\tpublic int countByG_S(long groupId, int status) {\n\t\tFinderPath finderPath = _finderPathCountByG_S;\n\n\t\tObject[] finderArgs = new Object[] {groupId, status};\n\n\t\tLong count = (Long)finderCache.getResult(finderPath, finderArgs, this);\n\n\t\tif (count == null) {\n\t\t\tStringBundler query = new StringBundler(3);\n\n\t\t\tquery.append(_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\t\tString sql = query.toString();\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\t\tqPos.add(groupId);\n\n\t\t\t\tqPos.add(status);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t/**\n\t * Returns the number of guestbooks that the user has permission to view where groupId = &#63; and status = &#63;.\n\t *\n\t * @param groupId the group ID\n\t * @param status the status\n\t * @return the number of matching guestbooks that the user has permission to view\n\t */\n\t@Override\n\tpublic int filterCountByG_S(long groupId, int status) {\n\t\tif (!InlineSQLHelperUtil.isEnabled(groupId)) {\n\t\t\treturn countByG_S(groupId, status);\n\t\t}\n\n\t\tStringBundler query = new StringBundler(3);\n\n\t\tquery.append(_FILTER_SQL_COUNT_GUESTBOOK_WHERE);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_GROUPID_2);\n\n\t\tquery.append(_FINDER_COLUMN_G_S_STATUS_2);\n\n\t\tString sql = InlineSQLHelperUtil.replacePermissionCheck(\n\t\t\tquery.toString(), Guestbook.class.getName(),\n\t\t\t_FILTER_ENTITY_TABLE_FILTER_PK_COLUMN, groupId);\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tSQLQuery q = session.createSynchronizedSQLQuery(sql);\n\n\t\t\tq.addScalar(\n\t\t\t\tCOUNT_COLUMN_NAME, com.liferay.portal.kernel.dao.orm.Type.LONG);\n\n\t\t\tQueryPos qPos = QueryPos.getInstance(q);\n\n\t\t\tqPos.add(groupId);\n\n\t\t\tqPos.add(status);\n\n\t\t\tLong count = (Long)q.uniqueResult();\n\n\t\t\treturn count.intValue();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\tprivate static final String _FINDER_COLUMN_G_S_GROUPID_2 =\n\t\t\"guestbook.groupId = ? AND \";\n\n\tprivate static final String _FINDER_COLUMN_G_S_STATUS_2 =\n\t\t\"guestbook.status = ?\";\n\n\tpublic GuestbookPersistenceImpl() {\n\t\tsetModelClass(Guestbook.class);\n\n\t\tsetModelImplClass(GuestbookImpl.class);\n\t\tsetModelPKClass(long.class);\n\n\t\tMap<String, String> dbColumnNames = new HashMap<String, String>();\n\n\t\tdbColumnNames.put(\"uuid\", \"uuid_\");\n\n\t\tsetDBColumnNames(dbColumnNames);\n\t}\n\n\t/**\n\t * Caches the guestbook in the entity cache if it is enabled.\n\t *\n\t * @param guestbook the guestbook\n\t */\n\t@Override\n\tpublic void cacheResult(Guestbook guestbook) {\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook);\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G,\n\t\t\tnew Object[] {guestbook.getUuid(), guestbook.getGroupId()},\n\t\t\tguestbook);\n\n\t\tguestbook.resetOriginalValues();\n\t}\n\n\t/**\n\t * Caches the guestbooks in the entity cache if it is enabled.\n\t *\n\t * @param guestbooks the guestbooks\n\t */\n\t@Override\n\tpublic void cacheResult(List<Guestbook> guestbooks) {\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tif (entityCache.getResult(\n\t\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\t\tguestbook.getPrimaryKey()) == null) {\n\n\t\t\t\tcacheResult(guestbook);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.resetOriginalValues();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clears the cache for all guestbooks.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache() {\n\t\tentityCache.clearCache(GuestbookImpl.class);\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t/**\n\t * Clears the cache for the guestbook.\n\t *\n\t * <p>\n\t * The <code>EntityCache</code> and <code>FinderCache</code> are both cleared by this method.\n\t * </p>\n\t */\n\t@Override\n\tpublic void clearCache(Guestbook guestbook) {\n\t\tentityCache.removeResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey());\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t}\n\n\t@Override\n\tpublic void clearCache(List<Guestbook> guestbooks) {\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\n\t\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tentityCache.removeResult(\n\t\t\t\tentityCacheEnabled, GuestbookImpl.class,\n\t\t\t\tguestbook.getPrimaryKey());\n\n\t\t\tclearUniqueFindersCache((GuestbookModelImpl)guestbook, true);\n\t\t}\n\t}\n\n\tprotected void cacheUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl) {\n\n\t\tObject[] args = new Object[] {\n\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t};\n\n\t\tfinderCache.putResult(\n\t\t\t_finderPathCountByUUID_G, args, Long.valueOf(1), false);\n\t\tfinderCache.putResult(\n\t\t\t_finderPathFetchByUUID_G, args, guestbookModelImpl, false);\n\t}\n\n\tprotected void clearUniqueFindersCache(\n\t\tGuestbookModelImpl guestbookModelImpl, boolean clearCurrent) {\n\n\t\tif (clearCurrent) {\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\n\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t _finderPathFetchByUUID_G.getColumnBitmask()) != 0) {\n\n\t\t\tObject[] args = new Object[] {\n\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUUID_G, args);\n\t\t\tfinderCache.removeResult(_finderPathFetchByUUID_G, args);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a new guestbook with the primary key. Does not add the guestbook to the database.\n\t *\n\t * @param guestbookId the primary key for the new guestbook\n\t * @return the new guestbook\n\t */\n\t@Override\n\tpublic Guestbook create(long guestbookId) {\n\t\tGuestbook guestbook = new GuestbookImpl();\n\n\t\tguestbook.setNew(true);\n\t\tguestbook.setPrimaryKey(guestbookId);\n\n\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\tguestbook.setUuid(uuid);\n\n\t\tguestbook.setCompanyId(companyProvider.getCompanyId());\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(long guestbookId) throws NoSuchGuestbookException {\n\t\treturn remove((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Removes the guestbook with the primary key from the database. Also notifies the appropriate model listeners.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook that was removed\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook remove(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tGuestbook guestbook = (Guestbook)session.get(\n\t\t\t\tGuestbookImpl.class, primaryKey);\n\n\t\t\tif (guestbook == null) {\n\t\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t\t}\n\n\t\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\treturn remove(guestbook);\n\t\t}\n\t\tcatch (NoSuchGuestbookException nsee) {\n\t\t\tthrow nsee;\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\t}\n\n\t@Override\n\tprotected Guestbook removeImpl(Guestbook guestbook) {\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (!session.contains(guestbook)) {\n\t\t\t\tguestbook = (Guestbook)session.get(\n\t\t\t\t\tGuestbookImpl.class, guestbook.getPrimaryKeyObj());\n\t\t\t}\n\n\t\t\tif (guestbook != null) {\n\t\t\t\tsession.delete(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tif (guestbook != null) {\n\t\t\tclearCache(guestbook);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t@Override\n\tpublic Guestbook updateImpl(Guestbook guestbook) {\n\t\tboolean isNew = guestbook.isNew();\n\n\t\tif (!(guestbook instanceof GuestbookModelImpl)) {\n\t\t\tInvocationHandler invocationHandler = null;\n\n\t\t\tif (ProxyUtil.isProxyClass(guestbook.getClass())) {\n\t\t\t\tinvocationHandler = ProxyUtil.getInvocationHandler(guestbook);\n\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Implement ModelWrapper in guestbook proxy \" +\n\t\t\t\t\t\tinvocationHandler.getClass());\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Implement ModelWrapper in custom Guestbook implementation \" +\n\t\t\t\t\tguestbook.getClass());\n\t\t}\n\n\t\tGuestbookModelImpl guestbookModelImpl = (GuestbookModelImpl)guestbook;\n\n\t\tif (Validator.isNull(guestbook.getUuid())) {\n\t\t\tString uuid = PortalUUIDUtil.generate();\n\n\t\t\tguestbook.setUuid(uuid);\n\t\t}\n\n\t\tServiceContext serviceContext =\n\t\t\tServiceContextThreadLocal.getServiceContext();\n\n\t\tDate now = new Date();\n\n\t\tif (isNew && (guestbook.getCreateDate() == null)) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setCreateDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setCreateDate(serviceContext.getCreateDate(now));\n\t\t\t}\n\t\t}\n\n\t\tif (!guestbookModelImpl.hasSetModifiedDate()) {\n\t\t\tif (serviceContext == null) {\n\t\t\t\tguestbook.setModifiedDate(now);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook.setModifiedDate(serviceContext.getModifiedDate(now));\n\t\t\t}\n\t\t}\n\n\t\tSession session = null;\n\n\t\ttry {\n\t\t\tsession = openSession();\n\n\t\t\tif (guestbook.isNew()) {\n\t\t\t\tsession.save(guestbook);\n\n\t\t\t\tguestbook.setNew(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tguestbook = (Guestbook)session.merge(guestbook);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow processException(e);\n\t\t}\n\t\tfinally {\n\t\t\tcloseSession(session);\n\t\t}\n\n\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\n\t\tif (!_columnBitmaskEnabled) {\n\t\t\tfinderCache.clearCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t\t}\n\t\telse if (isNew) {\n\t\t\tObject[] args = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookModelImpl.getUuid(), guestbookModelImpl.getCompanyId()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\targs = new Object[] {guestbookModelImpl.getStatus()};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByStatus, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByStatus, args);\n\n\t\t\targs = new Object[] {\n\t\t\t\tguestbookModelImpl.getGroupId(), guestbookModelImpl.getStatus()\n\t\t\t};\n\n\t\t\tfinderCache.removeResult(_finderPathCountByG_S, args);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindByG_S, args);\n\n\t\t\tfinderCache.removeResult(_finderPathCountAll, FINDER_ARGS_EMPTY);\n\t\t\tfinderCache.removeResult(\n\t\t\t\t_finderPathWithoutPaginationFindAll, FINDER_ARGS_EMPTY);\n\t\t}\n\t\telse {\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getUuid()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByUuid_C.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalUuid(),\n\t\t\t\t\tguestbookModelImpl.getOriginalCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getUuid(),\n\t\t\t\t\tguestbookModelImpl.getCompanyId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByUuid_C, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByUuid_C, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByGroupId.\n\t\t\t\t\t getColumnBitmask()) != 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalGroupId()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getGroupId()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByGroupId, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByGroupId, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByStatus.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalStatus()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByStatus, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByStatus, args);\n\n\t\t\t\targs = new Object[] {guestbookModelImpl.getStatus()};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByStatus, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByStatus, args);\n\t\t\t}\n\n\t\t\tif ((guestbookModelImpl.getColumnBitmask() &\n\t\t\t\t _finderPathWithoutPaginationFindByG_S.getColumnBitmask()) !=\n\t\t\t\t\t 0) {\n\n\t\t\t\tObject[] args = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getOriginalGroupId(),\n\t\t\t\t\tguestbookModelImpl.getOriginalStatus()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_S, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_S, args);\n\n\t\t\t\targs = new Object[] {\n\t\t\t\t\tguestbookModelImpl.getGroupId(),\n\t\t\t\t\tguestbookModelImpl.getStatus()\n\t\t\t\t};\n\n\t\t\t\tfinderCache.removeResult(_finderPathCountByG_S, args);\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathWithoutPaginationFindByG_S, args);\n\t\t\t}\n\t\t}\n\n\t\tentityCache.putResult(\n\t\t\tentityCacheEnabled, GuestbookImpl.class, guestbook.getPrimaryKey(),\n\t\t\tguestbook, false);\n\n\t\tclearUniqueFindersCache(guestbookModelImpl, false);\n\t\tcacheUniqueFindersCache(guestbookModelImpl);\n\n\t\tguestbook.resetOriginalValues();\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>com.liferay.portal.kernel.exception.NoSuchModelException</code> if it could not be found.\n\t *\n\t * @param primaryKey the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(Serializable primaryKey)\n\t\tthrows NoSuchGuestbookException {\n\n\t\tGuestbook guestbook = fetchByPrimaryKey(primaryKey);\n\n\t\tif (guestbook == null) {\n\t\t\tif (_log.isDebugEnabled()) {\n\t\t\t\t_log.debug(_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t\t}\n\n\t\t\tthrow new NoSuchGuestbookException(\n\t\t\t\t_NO_SUCH_ENTITY_WITH_PRIMARY_KEY + primaryKey);\n\t\t}\n\n\t\treturn guestbook;\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or throws a <code>NoSuchGuestbookException</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook\n\t * @throws NoSuchGuestbookException if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook findByPrimaryKey(long guestbookId)\n\t\tthrows NoSuchGuestbookException {\n\n\t\treturn findByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns the guestbook with the primary key or returns <code>null</code> if it could not be found.\n\t *\n\t * @param guestbookId the primary key of the guestbook\n\t * @return the guestbook, or <code>null</code> if a guestbook with the primary key could not be found\n\t */\n\t@Override\n\tpublic Guestbook fetchByPrimaryKey(long guestbookId) {\n\t\treturn fetchByPrimaryKey((Serializable)guestbookId);\n\t}\n\n\t/**\n\t * Returns all the guestbooks.\n\t *\n\t * @return the guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll() {\n\t\treturn findAll(QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);\n\t}\n\n\t/**\n\t * Returns a range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @return the range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(int start, int end) {\n\t\treturn findAll(start, end, null);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator) {\n\n\t\treturn findAll(start, end, orderByComparator, true);\n\t}\n\n\t/**\n\t * Returns an ordered range of all the guestbooks.\n\t *\n\t * <p>\n\t * Useful when paginating results. Returns a maximum of <code>end - start</code> instances. <code>start</code> and <code>end</code> are not primary keys, they are indexes in the result set. Thus, <code>0</code> refers to the first result in the set. Setting both <code>start</code> and <code>end</code> to <code>QueryUtil#ALL_POS</code> will return the full result set. If <code>orderByComparator</code> is specified, then the query will include the given ORDER BY logic. If <code>orderByComparator</code> is absent and pagination is required (<code>start</code> and <code>end</code> are not <code>QueryUtil#ALL_POS</code>), then the query will include the default ORDER BY logic from <code>GuestbookModelImpl</code>. If both <code>orderByComparator</code> and pagination are absent, for performance reasons, the query will not have an ORDER BY clause and the returned result set will be sorted on by the primary key in an ascending order.\n\t * </p>\n\t *\n\t * @param start the lower bound of the range of guestbooks\n\t * @param end the upper bound of the range of guestbooks (not inclusive)\n\t * @param orderByComparator the comparator to order the results by (optionally <code>null</code>)\n\t * @param retrieveFromCache whether to retrieve from the finder cache\n\t * @return the ordered range of guestbooks\n\t */\n\t@Override\n\tpublic List<Guestbook> findAll(\n\t\tint start, int end, OrderByComparator<Guestbook> orderByComparator,\n\t\tboolean retrieveFromCache) {\n\n\t\tboolean pagination = true;\n\t\tFinderPath finderPath = null;\n\t\tObject[] finderArgs = null;\n\n\t\tif ((start == QueryUtil.ALL_POS) && (end == QueryUtil.ALL_POS) &&\n\t\t\t(orderByComparator == null)) {\n\n\t\t\tpagination = false;\n\t\t\tfinderPath = _finderPathWithoutPaginationFindAll;\n\t\t\tfinderArgs = FINDER_ARGS_EMPTY;\n\t\t}\n\t\telse {\n\t\t\tfinderPath = _finderPathWithPaginationFindAll;\n\t\t\tfinderArgs = new Object[] {start, end, orderByComparator};\n\t\t}\n\n\t\tList<Guestbook> list = null;\n\n\t\tif (retrieveFromCache) {\n\t\t\tlist = (List<Guestbook>)finderCache.getResult(\n\t\t\t\tfinderPath, finderArgs, this);\n\t\t}\n\n\t\tif (list == null) {\n\t\t\tStringBundler query = null;\n\t\t\tString sql = null;\n\n\t\t\tif (orderByComparator != null) {\n\t\t\t\tquery = new StringBundler(\n\t\t\t\t\t2 + (orderByComparator.getOrderByFields().length * 2));\n\n\t\t\t\tquery.append(_SQL_SELECT_GUESTBOOK);\n\n\t\t\t\tappendOrderByComparator(\n\t\t\t\t\tquery, _ORDER_BY_ENTITY_ALIAS, orderByComparator);\n\n\t\t\t\tsql = query.toString();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsql = _SQL_SELECT_GUESTBOOK;\n\n\t\t\t\tif (pagination) {\n\t\t\t\t\tsql = sql.concat(GuestbookModelImpl.ORDER_BY_JPQL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(sql);\n\n\t\t\t\tif (!pagination) {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end, false);\n\n\t\t\t\t\tCollections.sort(list);\n\n\t\t\t\t\tlist = Collections.unmodifiableList(list);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist = (List<Guestbook>)QueryUtil.list(\n\t\t\t\t\t\tq, getDialect(), start, end);\n\t\t\t\t}\n\n\t\t\t\tcacheResult(list);\n\n\t\t\t\tfinderCache.putResult(finderPath, finderArgs, list);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(finderPath, finderArgs);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t/**\n\t * Removes all the guestbooks from the database.\n\t *\n\t */\n\t@Override\n\tpublic void removeAll() {\n\t\tfor (Guestbook guestbook : findAll()) {\n\t\t\tremove(guestbook);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of guestbooks.\n\t *\n\t * @return the number of guestbooks\n\t */\n\t@Override\n\tpublic int countAll() {\n\t\tLong count = (Long)finderCache.getResult(\n\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, this);\n\n\t\tif (count == null) {\n\t\t\tSession session = null;\n\n\t\t\ttry {\n\t\t\t\tsession = openSession();\n\n\t\t\t\tQuery q = session.createQuery(_SQL_COUNT_GUESTBOOK);\n\n\t\t\t\tcount = (Long)q.uniqueResult();\n\n\t\t\t\tfinderCache.putResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY, count);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tfinderCache.removeResult(\n\t\t\t\t\t_finderPathCountAll, FINDER_ARGS_EMPTY);\n\n\t\t\t\tthrow processException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcloseSession(session);\n\t\t\t}\n\t\t}\n\n\t\treturn count.intValue();\n\t}\n\n\t@Override\n\tpublic Set<String> getBadColumnNames() {\n\t\treturn _badColumnNames;\n\t}\n\n\t@Override\n\tprotected EntityCache getEntityCache() {\n\t\treturn entityCache;\n\t}\n\n\t@Override\n\tprotected String getPKDBName() {\n\t\treturn \"guestbookId\";\n\t}\n\n\t@Override\n\tprotected String getSelectSQL() {\n\t\treturn _SQL_SELECT_GUESTBOOK;\n\t}\n\n\t@Override\n\tprotected Map<String, Integer> getTableColumnsMap() {\n\t\treturn GuestbookModelImpl.TABLE_COLUMNS_MAP;\n\t}\n\n\t/**\n\t * Initializes the guestbook persistence.\n\t */\n\t@Activate\n\tpublic void activate() {\n\t\tGuestbookModelImpl.setEntityCacheEnabled(entityCacheEnabled);\n\t\tGuestbookModelImpl.setFinderCacheEnabled(finderCacheEnabled);\n\n\t\t_finderPathWithPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findAll\", new String[0]);\n\n\t\t_finderPathWithoutPaginationFindAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathCountAll = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countAll\",\n\t\t\tnew String[0]);\n\n\t\t_finderPathWithPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid\",\n\t\t\tnew String[] {String.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid\",\n\t\t\tnew String[] {String.class.getName()});\n\n\t\t_finderPathFetchByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_ENTITY, \"fetchByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUUID_G = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUUID_G\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {\n\t\t\t\tString.class.getName(), Long.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()},\n\t\t\tGuestbookModelImpl.UUID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.COMPANYID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByUuid_C = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByUuid_C\",\n\t\t\tnew String[] {String.class.getName(), Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByGroupId\",\n\t\t\tnew String[] {Long.class.getName()},\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK);\n\n\t\t_finderPathCountByGroupId = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByGroupId\",\n\t\t\tnew String[] {Long.class.getName()});\n\n\t\t_finderPathWithPaginationFindByStatus = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByStatus\",\n\t\t\tnew String[] {\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), OrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByStatus = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByStatus\",\n\t\t\tnew String[] {Integer.class.getName()},\n\t\t\tGuestbookModelImpl.STATUS_COLUMN_BITMASK);\n\n\t\t_finderPathCountByStatus = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByStatus\",\n\t\t\tnew String[] {Integer.class.getName()});\n\n\t\t_finderPathWithPaginationFindByG_S = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITH_PAGINATION, \"findByG_S\",\n\t\t\tnew String[] {\n\t\t\t\tLong.class.getName(), Integer.class.getName(),\n\t\t\t\tInteger.class.getName(), Integer.class.getName(),\n\t\t\t\tOrderByComparator.class.getName()\n\t\t\t});\n\n\t\t_finderPathWithoutPaginationFindByG_S = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, GuestbookImpl.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"findByG_S\",\n\t\t\tnew String[] {Long.class.getName(), Integer.class.getName()},\n\t\t\tGuestbookModelImpl.GROUPID_COLUMN_BITMASK |\n\t\t\tGuestbookModelImpl.STATUS_COLUMN_BITMASK);\n\n\t\t_finderPathCountByG_S = new FinderPath(\n\t\t\tentityCacheEnabled, finderCacheEnabled, Long.class,\n\t\t\tFINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION, \"countByG_S\",\n\t\t\tnew String[] {Long.class.getName(), Integer.class.getName()});\n\t}\n\n\t@Deactivate\n\tpublic void deactivate() {\n\t\tentityCache.removeCache(GuestbookImpl.class.getName());\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_ENTITY);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITH_PAGINATION);\n\t\tfinderCache.removeCache(FINDER_CLASS_NAME_LIST_WITHOUT_PAGINATION);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tsuper.setConfiguration(configuration);\n\n\t\t_columnBitmaskEnabled = GetterUtil.getBoolean(\n\t\t\tconfiguration.get(\n\t\t\t\t\"value.object.column.bitmask.enabled.com.liferay.docs.guestbook.model.Guestbook\"),\n\t\t\ttrue);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tsuper.setDataSource(dataSource);\n\t}\n\n\t@Override\n\t@Reference(\n\t\ttarget = GBPersistenceConstants.ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER,\n\t\tunbind = \"-\"\n\t)\n\tpublic void setSessionFactory(SessionFactory sessionFactory) {\n\t\tsuper.setSessionFactory(sessionFactory);\n\t}\n\n\tprivate boolean _columnBitmaskEnabled;\n\n\t@Reference(service = CompanyProviderWrapper.class)\n\tprotected CompanyProvider companyProvider;\n\n\t@Reference\n\tprotected EntityCache entityCache;\n\n\t@Reference\n\tprotected FinderCache finderCache;\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK =\n\t\t\"SELECT guestbook FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_SELECT_GUESTBOOK_WHERE =\n\t\t\"SELECT guestbook FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook\";\n\n\tprivate static final String _SQL_COUNT_GUESTBOOK_WHERE =\n\t\t\"SELECT COUNT(guestbook) FROM Guestbook guestbook WHERE \";\n\n\tprivate static final String _FILTER_ENTITY_TABLE_FILTER_PK_COLUMN =\n\t\t\"guestbook.guestbookId\";\n\n\tprivate static final String _FILTER_SQL_SELECT_GUESTBOOK_WHERE =\n\t\t\"SELECT DISTINCT {guestbook.*} FROM GB_Guestbook guestbook WHERE \";\n\n\tprivate static final String\n\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_1 =\n\t\t\t\"SELECT {GB_Guestbook.*} FROM (SELECT DISTINCT guestbook.guestbookId FROM GB_Guestbook guestbook WHERE \";\n\n\tprivate static final String\n\t\t_FILTER_SQL_SELECT_GUESTBOOK_NO_INLINE_DISTINCT_WHERE_2 =\n\t\t\t\") TEMP_TABLE INNER JOIN GB_Guestbook ON TEMP_TABLE.guestbookId = GB_Guestbook.guestbookId\";\n\n\tprivate static final String _FILTER_SQL_COUNT_GUESTBOOK_WHERE =\n\t\t\"SELECT COUNT(DISTINCT guestbook.guestbookId) AS COUNT_VALUE FROM GB_Guestbook guestbook WHERE \";\n\n\tprivate static final String _FILTER_ENTITY_ALIAS = \"guestbook\";\n\n\tprivate static final String _FILTER_ENTITY_TABLE = \"GB_Guestbook\";\n\n\tprivate static final String _ORDER_BY_ENTITY_ALIAS = \"guestbook.\";\n\n\tprivate static final String _ORDER_BY_ENTITY_TABLE = \"GB_Guestbook.\";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_PRIMARY_KEY =\n\t\t\"No Guestbook exists with the primary key \";\n\n\tprivate static final String _NO_SUCH_ENTITY_WITH_KEY =\n\t\t\"No Guestbook exists with the key {\";\n\n\tprivate static final Log _log = LogFactoryUtil.getLog(\n\t\tGuestbookPersistenceImpl.class);\n\n\tprivate static final Set<String> _badColumnNames = SetUtil.fromArray(\n\t\tnew String[] {\"uuid\"});\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/persistence/impl/constants/GBPersistenceConstants.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.service.persistence.impl.constants;\n\nimport com.liferay.petra.string.StringBundler;\n\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.BundleContext;\nimport org.osgi.framework.Constants;\nimport org.osgi.service.component.annotations.Activate;\nimport org.osgi.service.component.annotations.Component;\n\n/**\n * @author Liferay\n * @generated\n */\n@Component(immediate = true, service = {})\npublic class GBPersistenceConstants {\n\n\tpublic static final String BUNDLE_SYMBOLIC_NAME =\n\t\t\"com.liferay.docs.guestbook.service\";\n\n\tpublic static final String ORIGIN_BUNDLE_SYMBOLIC_NAME_FILTER =\n\t\t\"(origin.bundle.symbolic.name=\" + BUNDLE_SYMBOLIC_NAME + \")\";\n\n\t@Activate\n\tprotected void activate(BundleContext bundleContext) {\n\t\tBundle bundle = bundleContext.getBundle();\n\n\t\tif (!BUNDLE_SYMBOLIC_NAME.equals(bundle.getSymbolicName())) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\tStringBundler.concat(\n\t\t\t\t\t\"Incorrect \", Constants.BUNDLE_SYMBOLICNAME, \" for bundle \",\n\t\t\t\t\tbundle.getSymbolicName()));\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/workflow/GuestbookEntryWorkflowHandler.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.workflow;\n\nimport java.io.Serializable;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.security.permission.ResourceActions;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.workflow.BaseWorkflowHandler;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\nimport com.liferay.portal.kernel.workflow.WorkflowHandler;\n\n@Component(immediate = true, service = WorkflowHandler.class)\npublic class GuestbookEntryWorkflowHandler extends BaseWorkflowHandler<GuestbookEntry> {\n\n    @Override\n    public String getClassName() {\n\n        return GuestbookEntry.class.getName();\n\n    }\n\n    @Override\n    public String getType(Locale locale) {\n\n        return _resourceActions.getModelResource(locale, getClassName());\n\n    }\n\n    @Override\n    public GuestbookEntry updateStatus(\n        int status, Map<String, Serializable> workflowContext)\n        throws PortalException {\n\n        long userId = GetterUtil.getLong(\n            (String) workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));\n        long resourcePrimKey = GetterUtil.getLong(\n            (String) workflowContext.get(\n                WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n        ServiceContext serviceContext =\n            (ServiceContext) workflowContext.get(\"serviceContext\");\n\n        long guestbookId =\n            _guestbookEntryLocalService.getGuestbookEntry(resourcePrimKey).getGuestbookId();\n        \n        return _guestbookEntryLocalService.updateStatus(\n            userId, guestbookId, resourcePrimKey, status, serviceContext);\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setGuestbookEntryLocalService(GuestbookEntryLocalService guestbookEntryLocalService) {\n\n        _guestbookEntryLocalService = guestbookEntryLocalService;\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setResourceActions(ResourceActions resourceActions) {\n\n        _resourceActions = resourceActions;\n    }\n\n    private GuestbookEntryLocalService _guestbookEntryLocalService;\n    private ResourceActions _resourceActions;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/workflow/GuestbookWorkflowHandler.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.workflow;\n\nimport java.io.Serializable;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.security.permission.ResourceActions;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.util.GetterUtil;\nimport com.liferay.portal.kernel.workflow.BaseWorkflowHandler;\nimport com.liferay.portal.kernel.workflow.WorkflowConstants;\nimport com.liferay.portal.kernel.workflow.WorkflowHandler;\n\n@Component(immediate = true, service = WorkflowHandler.class)\npublic class GuestbookWorkflowHandler extends BaseWorkflowHandler<Guestbook> {\n\n    @Override\n    public String getClassName() {\n        return Guestbook.class.getName();\n    }\n\n    @Override\n    public String getType(Locale locale) {\n        return _resourceActions.getModelResource(locale, getClassName());\n    }\n\n    @Override\n    public Guestbook updateStatus(\n            int status, Map<String, Serializable> workflowContext)\n        throws PortalException {\n\n        long userId = GetterUtil.getLong(\n            (String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));\n        long resourcePrimKey = GetterUtil.getLong(\n            (String)workflowContext.get(\n                WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n        ServiceContext serviceContext = (ServiceContext)workflowContext.get(\n            \"serviceContext\");\n\n        return _guestbookLocalService.updateStatus(\n            userId, resourcePrimKey, status, serviceContext);\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setResourceActions(ResourceActions resourceActions) {\n\n        _resourceActions = resourceActions;\n    }\n\n    private ResourceActions _resourceActions;\n\n\t@Reference(unbind = \"-\")\n\tprotected void setGuestbookLocalService(\n\t\tGuestbookLocalService guestbookLocalService) {\n\n\t\t_guestbookLocalService = guestbookLocalService;\n\t}\n\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/module-hbm.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">\n\n<hibernate-mapping auto-import=\"false\" default-lazy=\"false\">\n\t<import class=\"com.liferay.docs.guestbook.model.Guestbook\" />\n\t<import class=\"com.liferay.docs.guestbook.model.GuestbookEntry\" />\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookImpl\" table=\"GB_Guestbook\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n\t<class name=\"com.liferay.docs.guestbook.model.impl.GuestbookEntryImpl\" table=\"GB_GuestbookEntry\">\n\t\t<id access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"entryId\" type=\"long\">\n\t\t\t<generator class=\"assigned\" />\n\t\t</id>\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" column=\"uuid_\" name=\"uuid\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"name\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"email\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"message\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"guestbookId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"groupId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"companyId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"userName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"createDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"modifiedDate\" type=\"org.hibernate.type.TimestampType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"status\" type=\"com.liferay.portal.dao.orm.hibernate.IntegerType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserId\" type=\"com.liferay.portal.dao.orm.hibernate.LongType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusByUserName\" type=\"com.liferay.portal.dao.orm.hibernate.StringType\" />\n\t\t<property access=\"com.liferay.portal.dao.orm.hibernate.LiferayPropertyAccessor\" name=\"statusDate\" type=\"org.hibernate.type.TimestampType\" />\n\t</class>\n</hibernate-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/portlet-model-hints.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<model-hints>\n\t<model name=\"com.liferay.docs.guestbook.model.Guestbook\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n\t<model name=\"com.liferay.docs.guestbook.model.GuestbookEntry\">\n\t\t<field name=\"uuid\" type=\"String\" />\n\t\t<field name=\"entryId\" type=\"long\" />\n\t\t<field name=\"name\" type=\"String\" />\n\t\t<field name=\"email\" type=\"String\" />\n\t\t<field name=\"message\" type=\"String\" />\n\t\t<field name=\"guestbookId\" type=\"long\" />\n\t\t<field name=\"groupId\" type=\"long\" />\n\t\t<field name=\"companyId\" type=\"long\" />\n\t\t<field name=\"userId\" type=\"long\" />\n\t\t<field name=\"userName\" type=\"String\" />\n\t\t<field name=\"createDate\" type=\"Date\" />\n\t\t<field name=\"modifiedDate\" type=\"Date\" />\n\t\t<field name=\"status\" type=\"int\" />\n\t\t<field name=\"statusByUserId\" type=\"long\" />\n\t\t<field name=\"statusByUserName\" type=\"String\" />\n\t\t<field name=\"statusDate\" type=\"Date\" />\n\t</model>\n</model-hints>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/resource-actions/default.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action  \nMapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n\n<resource-action-mapping>\n\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n        </portlet-ref>\n        <root>true</root>\n        <permissions>\n            <supports>\n                <action-key>ADD_GUESTBOOK</action-key>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>ADD_ENTRY</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>ADD_GUESTBOOK</action-key>\n                <action-key>ADD_ENTRY</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook.model.Guestbook</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n        </portlet-ref>\n        <permissions>\n            <supports>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>DELETE</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>UPDATE</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>ADD_ENTRY</action-key>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>UPDATE</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n\n    <model-resource>\n        <model-name>com.liferay.docs.guestbook.model.GuestbookEntry</model-name>\n        <portlet-ref>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n        </portlet-ref>\n        <permissions>\n            <supports>\n                <action-key>DELETE</action-key>\n                <action-key>PERMISSIONS</action-key>\n                <action-key>UPDATE</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported>\n                <action-key>UPDATE</action-key>\n            </guest-unsupported>\n        </permissions>\n    </model-resource>\n\n</resource-action-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/indexes.sql",
    "content": "create index IX_A4D0B52D on GB_Guestbook (groupId, status);\ncreate index IX_B946FEA9 on GB_Guestbook (status);\ncreate index IX_9314A9F7 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_EDD4239 on GB_Guestbook (uuid_[$COLUMN_LENGTH:75$], groupId);\n\ncreate index IX_D49B64E3 on GB_GuestbookEntry (groupId, guestbookId, status);\ncreate index IX_D8346035 on GB_GuestbookEntry (groupId, status);\ncreate index IX_4BED4AA1 on GB_GuestbookEntry (status);\ncreate index IX_CC265FEF on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], companyId);\ncreate unique index IX_4A541631 on GB_GuestbookEntry (uuid_[$COLUMN_LENGTH:75$], groupId);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/sequences.sql",
    "content": ""
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/META-INF/sql/tables.sql",
    "content": "create table GB_Guestbook (\n\tuuid_ VARCHAR(75) null,\n\tguestbookId LONG not null primary key,\n\tname VARCHAR(75) null,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);\n\ncreate table GB_GuestbookEntry (\n\tuuid_ VARCHAR(75) null,\n\tentryId LONG not null primary key,\n\tname VARCHAR(75) null,\n\temail VARCHAR(75) null,\n\tmessage VARCHAR(75) null,\n\tguestbookId LONG,\n\tgroupId LONG,\n\tcompanyId LONG,\n\tuserId LONG,\n\tuserName VARCHAR(75) null,\n\tcreateDate DATE null,\n\tmodifiedDate DATE null,\n\tstatus INTEGER,\n\tstatusByUserId LONG,\n\tstatusByUserName VARCHAR(75) null,\n\tstatusDate DATE null\n);"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/portlet.properties",
    "content": "resource.actions.configs=META-INF/resource-actions/default.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/resources/service.properties",
    "content": "##\n## Properties Override\n##\n\n    #\n    # Specify where to get the overridden properties. Updates should not be made\n    # on this file but on the overridden version of this file.\n    #\n    include-and-override=service-ext.properties\n\n##\n## Build\n##\n\n    build.namespace=GB\n    build.number=16\n    build.date=1570484139321"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/.gitignore",
    "content": ".gradle/\n.sass-cache/\nbuild/\ntarget/"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/bnd.bnd",
    "content": "Bundle-Name: guestbook-web\nBundle-SymbolicName: com.liferay.docs.guestbook.portlet\nBundle-Version: 1.0.0\nExport-Package: com.liferay.docs.guestbook.portlet.constants"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/build.gradle",
    "content": "dependencies {\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.portal.kernel\"\n\tcompileOnly group: \"com.liferay.portal\", name: \"com.liferay.util.taglib\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.asset.taglib\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.comment.taglib\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.petra.string\"\n\tcompileOnly group: \"com.liferay\", name: \"com.liferay.application.list.api\"\n\tcompileOnly group: \"javax.portlet\", name: \"portlet-api\"\n\tcompileOnly group: \"javax.servlet\", name: \"javax.servlet-api\"\n\tcompileOnly group: \"jstl\", name: \"jstl\"\n\tcompileOnly group: \"org.osgi\", name: \"org.osgi.service.component.annotations\"\n\n    compileOnly project(\":modules:guestbook:guestbook-api\")\n    compileOnly project(\":modules:guestbook:guestbook-service\")\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/application/list/GuestbookAdminPanelApp.java",
    "content": "package com.liferay.docs.guestbook.application.list;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.application.list.BasePanelApp;\nimport com.liferay.application.list.PanelApp;\nimport com.liferay.application.list.constants.PanelCategoryKeys;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.portal.kernel.model.Portlet;\n\n@Component(\n        immediate = true,\n        property = {\n            \"panel.app.order:Integer=300\",\n            \"panel.category.key=\" + PanelCategoryKeys.SITE_ADMINISTRATION_CONTENT\n        },\n        service = PanelApp.class\n    )\npublic class GuestbookAdminPanelApp extends BasePanelApp {\n\n        @Override\n        public String getPortletId() {\n            return GuestbookPortletKeys.GUESTBOOK_ADMIN;\n        }\n\n        @Override\n        @Reference(\n            target = \"(javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN + \")\",\n            unbind = \"-\"\n        )\n        public void setPortlet(Portlet portlet) {\n            super.setPortlet(portlet);\n        }\n\n    }\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookAdminPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.servlet.SessionErrors;\nimport com.liferay.portal.kernel.servlet.SessionMessages;\nimport com.liferay.portal.kernel.util.ParamUtil;\n@Component(\n        immediate = true,\n        property = {\n                \"com.liferay.portlet.display-category=category.hidden\",\n                \"com.liferay.portlet.scopeable=true\",\n                \"javax.portlet.display-name=Guestbooks\",\n                \"javax.portlet.expiration-cache=0\",\n                \"javax.portlet.init-param.portlet-title-based-navigation=true\",\n                \"javax.portlet.init-param.template-path=/\",\n                \"javax.portlet.init-param.view-template=/guestbook_admin/view.jsp\",\n                \"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK_ADMIN,\n                \"javax.portlet.resource-bundle=content.Language\",\n                \"javax.portlet.security-role-ref=administrator\",\n                \"javax.portlet.supports.mime-type=text/html\",\n                \"com.liferay.portlet.add-default-resource=true\"\n        },\n        service = Portlet.class\n    )\npublic class GuestbookAdminPortlet extends MVCPortlet {\n\npublic void addGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n\n        try {\n            _guestbookLocalService.addGuestbook(\n                serviceContext.getUserId(), name, serviceContext);\n            \n            SessionMessages.add(request, \"guestbookAdded\");\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n            \n            SessionErrors.add(request, pe.getClass().getName());\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n\n    public void updateGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        String name = ParamUtil.getString(request, \"name\");\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.updateGuestbook(\n                serviceContext.getUserId(), guestbookId, name, serviceContext);\n            \n            SessionMessages.add(request,  \"guestbookUpdated\");\n\n        } catch (PortalException pe) {\n        \n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            SessionErrors.add(request, pe.getClass().getName());\n\n            response.setRenderParameter(\n                \"mvcPath\", \"/guestbook_admin/edit_guestbook.jsp\");\n        }\n    }\n    \n    public void deleteGuestbook(ActionRequest request, ActionResponse response)\n        throws PortalException {\n\n        ServiceContext serviceContext = ServiceContextFactory.getInstance(\n            Guestbook.class.getName(), request);\n\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n        try {\n            _guestbookLocalService.deleteGuestbook(guestbookId, serviceContext);\n            \n            SessionMessages.add(request,  \"guestbookDeleted\");\n        }\n        catch (PortalException pe) {\n\n            Logger.getLogger(GuestbookAdminPortlet.class.getName()).log(\n                Level.SEVERE, null, pe);\n\n            SessionErrors.add(request, pe.getClass().getName());\n        }\n    }\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/portlet/GuestbookPortlet.java",
    "content": "package com.liferay.docs.guestbook.portlet;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.ActionRequest;\nimport javax.portlet.ActionResponse;\nimport javax.portlet.Portlet;\nimport javax.portlet.PortletException;\nimport javax.portlet.RenderRequest;\nimport javax.portlet.RenderResponse;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;\nimport com.liferay.portal.kernel.service.ServiceContext;\nimport com.liferay.portal.kernel.service.ServiceContextFactory;\nimport com.liferay.portal.kernel.servlet.SessionErrors;\nimport com.liferay.portal.kernel.servlet.SessionMessages;\nimport com.liferay.portal.kernel.util.ParamUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\n/**\n * @author sezovr\n */\n@Component(immediate = true, property = { \"com.liferay.portlet.display-category=category.social\",\n\t\t\"com.liferay.portlet.header-portlet-css=/css/main.css\", \n\t\t\"com.liferay.portlet.instanceable=false\",\n\t\t\"com.liferay.portlet.scopeable=true\", \n\t\t\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK,\n\t\t\"javax.portlet.display-name=Guestbook\", \n\t\t\"javax.portlet.expiration-cache=0\",\n\t\t\"javax.portlet.init-param.template-path=/\", \n\t\t\"javax.portlet.init-param.view-template=/guestbook/view.jsp\",\n\t\t\"javax.portlet.resource-bundle=content.Language\", \n\t\t\"javax.portlet.security-role-ref=power-user,user\",\n\t\t\"javax.portlet.supports.mime-type=text/html\" }, \n\tservice = Portlet.class)\npublic class GuestbookPortlet extends MVCPortlet {\n\n\tpublic void addEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\n\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(GuestbookEntry.class.getName(), request);\n\n\t\tString userName = ParamUtil.getString(request, \"name\");\n\t\tString email = ParamUtil.getString(request, \"email\");\n\t\tString message = ParamUtil.getString(request, \"message\");\n\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\n\t\tif (entryId > 0) {\n\n\t\t\ttry {\n\n\t\t\t\t_guestbookEntryLocalService.updateGuestbookEntry(serviceContext.getUserId(), guestbookId, entryId, userName,\n\t\t\t\t\t\temail, message, serviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(e);\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\n\t\t} else {\n\n\t\t\ttry {\n\t\t\t\t_guestbookEntryLocalService.addGuestbookEntry(serviceContext.getUserId(), guestbookId, userName, email, message,\n\t\t\t\t\t\tserviceContext);\n\n\t\t\t\tresponse.setRenderParameter(\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t\tSessionMessages.add(request, \"entryAdded\");\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tSessionErrors.add(request, e.getClass().getName());\n\n\t\t\t\tPortalUtil.copyRequestParameters(request, response);\n\n\t\t\t\tresponse.setRenderParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic void deleteEntry(ActionRequest request, ActionResponse response) throws PortalException {\n\t\t\tlong entryId = ParamUtil.getLong(request, \"entryId\");\n\t\t\tlong guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n\n\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\tGuestbookEntry.class.getName(), request);\n\n\t\t\ttry {\n\n\t\t\t\tresponse.setRenderParameter(\n\t\t\t\t\t\"guestbookId\", Long.toString(guestbookId));\n\n\t\t\t\t_guestbookEntryLocalService.deleteGuestbookEntry(entryId);\n\n\t\t\t\tSessionMessages.add(request, \"entryDeleted\");\n\n\t\t\t}\n\n\t\t\tcatch (Exception e) {\n\t\t\t\tLogger.getLogger(GuestbookPortlet.class.getName()).log(\n\t\t\t\t\tLevel.SEVERE, null, e);\n\n\t\t\tSessionErrors.add(request, e.getClass().getName());\n\t\t\t}\n\t}\n\n\t@Override\n\tpublic void render(RenderRequest renderRequest, RenderResponse renderResponse)\n\t\t\tthrows IOException, PortletException {\n\n\t\t\ttry {\n\t\t\t\tServiceContext serviceContext = ServiceContextFactory.getInstance(\n\t\t\t\t\tGuestbook.class.getName(), renderRequest);\n\n\t\t\t\tlong groupId = serviceContext.getScopeGroupId();\n\n\t\t\t\tlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n\t\t\t\tList<Guestbook> guestbooks = _guestbookLocalService.getGuestbooks(\n\t\t\t\t\tgroupId);\n\n\t\t\t\tif (guestbooks.isEmpty()) {\n\t\t\t\t\tGuestbook guestbook = _guestbookLocalService.addGuestbook(\n\t\t\t\t\t\tserviceContext.getUserId(), \"Main\", serviceContext);\n\n\t\t\t\t\tguestbookId = guestbook.getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\tif (guestbookId == 0) {\n\t\t\t\t\tguestbookId = guestbooks.get(0).getGuestbookId();\n\t\t\t\t}\n\n\t\t\t\trenderRequest.setAttribute(\"guestbookId\", guestbookId);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new PortletException(e);\n\t\t\t}\n\n\t\t\tsuper.render(renderRequest, renderResponse);\n\t}\n\n\t@Reference\n\tprivate GuestbookEntryLocalService _guestbookEntryLocalService;\n\n\t@Reference\n\tprivate GuestbookLocalService _guestbookLocalService;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/asset/GuestbookAssetRenderer.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.web.internal.asset;\n\nimport java.util.Locale;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.PortletRequest;\nimport javax.portlet.PortletResponse;\nimport javax.portlet.PortletURL;\nimport javax.portlet.WindowState;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport com.liferay.asset.kernel.model.BaseJSPAssetRenderer;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.petra.string.StringUtil;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.LayoutConstants;\nimport com.liferay.portal.kernel.portlet.LiferayPortletRequest;\nimport com.liferay.portal.kernel.portlet.LiferayPortletResponse;\nimport com.liferay.portal.kernel.portlet.PortletURLFactoryUtil;\nimport com.liferay.portal.kernel.security.permission.ActionKeys;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.util.HtmlUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\n\npublic class GuestbookAssetRenderer extends BaseJSPAssetRenderer<Guestbook>{\n\n    public GuestbookAssetRenderer(Guestbook guestbook, ModelResourcePermission<Guestbook> modelResourcePermission) {\n\n                _guestbook = guestbook;\n                _guestbookModelResourcePermission = modelResourcePermission;\n    }\n    \n    @Override\n    public boolean hasEditPermission(PermissionChecker permissionChecker) \n    {\n        try {\n            return _guestbookModelResourcePermission.contains(\n                permissionChecker, _guestbook, ActionKeys.UPDATE);\n        }\n        catch (Exception e) {\n        }\n\n        return false;\n    }\n\n    @Override\n    public boolean hasViewPermission(PermissionChecker permissionChecker) \n    {\n        try {\n            return _guestbookModelResourcePermission.contains(\n                permissionChecker, _guestbook, ActionKeys.VIEW);\n        }\n        catch (Exception e) {\n        }\n\n        return true;\n    }\n\n    @Override\n    public Guestbook getAssetObject() {\n      return _guestbook;\n    }\n\n    @Override\n    public long getGroupId() {\n      return _guestbook.getGroupId();\n    }\n\n    @Override\n    public long getUserId() {\n\n      return _guestbook.getUserId();\n    }\n\n    @Override\n    public String getUserName() {\n      return _guestbook.getUserName();\n    }\n\n    @Override\n    public String getUuid() {\n      return _guestbook.getUuid();\n    }\n\n    @Override\n    public String getClassName() {\n      return Guestbook.class.getName();\n    }\n\n    @Override\n    public long getClassPK() {\n      return _guestbook.getGuestbookId();\n    }\n\n    @Override\n    public String getSummary(PortletRequest portletRequest, PortletResponse \n      portletResponse) {\n        return \"Name: \" + _guestbook.getName();\n    }\n\n    @Override\n    public String getTitle(Locale locale) {\n      return _guestbook.getName();\n    }\n\n    @Override\n    public boolean include(HttpServletRequest request, HttpServletResponse \n      response, String template) throws Exception {\n        request.setAttribute(\"GUESTBOOK\", _guestbook);\n        request.setAttribute(\"HtmlUtil\", HtmlUtil.getHtml());\n        request.setAttribute(\"StringUtil\", new StringUtil());\n        return super.include(request, response, template);\n    }\n\n    @Override\n    public String getJspPath(HttpServletRequest request, String template) {\n\n        if (template.equals(TEMPLATE_FULL_CONTENT)) {\n          request.setAttribute(\"gb_guestbook\", _guestbook);\n\n          return \"/asset/guestbook/\" + template + \".jsp\";\n        } else {\n          return null;\n        }\n    }\n\n    @Override\n    public PortletURL getURLEdit(LiferayPortletRequest liferayPortletRequest,\n      LiferayPortletResponse liferayPortletResponse) throws Exception {\n\n        PortletURL portletURL = liferayPortletResponse.createLiferayPortletURL(\n            getControlPanelPlid(liferayPortletRequest), GuestbookPortletKeys.GUESTBOOK,\n            PortletRequest.RENDER_PHASE);\n        portletURL.setParameter(\"mvcRenderCommandName\", \"/guestbookwebportlet/edit_guestbook\");\n        portletURL.setParameter(\"guestbookId\", String.valueOf(_guestbook.getGuestbookId()));\n        portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n\n        return portletURL;\n    }\n\n    @Override\n    public String getURLViewInContext(LiferayPortletRequest liferayPortletRequest,\n      LiferayPortletResponse liferayPortletResponse, String noSuchEntryRedirect) throws Exception {\n        try {\n          long plid = PortalUtil.getPlidFromPortletId(_guestbook.getGroupId(),\n              GuestbookPortletKeys.GUESTBOOK);\n\n          PortletURL portletURL;\n          if (plid == LayoutConstants.DEFAULT_PLID) {\n            portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(liferayPortletRequest),\n                GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n          } else {\n            portletURL = PortletURLFactoryUtil.create(liferayPortletRequest,\n                GuestbookPortletKeys.GUESTBOOK, plid, PortletRequest.RENDER_PHASE);\n          }\n\n          portletURL.setParameter(\"mvcRenderCommandName\", \"/guestbookwebportlet/view\");\n          portletURL.setParameter(\"guestbookId\", String.valueOf(_guestbook.getGuestbookId()));\n\n          String currentUrl = PortalUtil.getCurrentURL(liferayPortletRequest);\n\n          portletURL.setParameter(\"redirect\", currentUrl);\n\n          return portletURL.toString();\n\n        } catch (PortalException e) {\n\n            logger.log(Level.SEVERE, e.getMessage());\n\n        } catch (SystemException e) {\n\n            logger.log(Level.SEVERE, e.getMessage());\n\n        }\n\n        return noSuchEntryRedirect;\n    }\n\n    @Override\n    public String getURLView(LiferayPortletResponse liferayPortletResponse, \n    WindowState windowState) throws Exception {\n\n        return super.getURLView(liferayPortletResponse, windowState);\n    }\n\n    private Guestbook _guestbook;\n    private final ModelResourcePermission<Guestbook> _guestbookModelResourcePermission;   \n    private Logger logger = Logger.getLogger(this.getClass().getName());\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/asset/GuestbookAssetRendererFactory.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.web.internal.asset;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.portlet.PortletRequest;\nimport javax.portlet.PortletURL;\nimport javax.servlet.ServletContext;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.asset.kernel.model.AssetRenderer;\nimport com.liferay.asset.kernel.model.AssetRendererFactory;\nimport com.liferay.asset.kernel.model.BaseAssetRendererFactory;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.docs.guestbook.service.GuestbookLocalService;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.LiferayPortletRequest;\nimport com.liferay.portal.kernel.portlet.LiferayPortletResponse;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.theme.ThemeDisplay;\nimport com.liferay.portal.kernel.util.WebKeys;\n\n\n@Component(immediate = true, \n  property = {\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK}, \n  service = AssetRendererFactory.class\n  )\npublic class GuestbookAssetRendererFactory extends \n  BaseAssetRendererFactory<Guestbook> {\n\n  public GuestbookAssetRendererFactory() {\n\tsetClassName(CLASS_NAME);\n\tsetLinkable(_LINKABLE);\n\tsetPortletId(GuestbookPortletKeys.GUESTBOOK); setSearchable(true);\n\tsetSelectable(true); \n  }         \n\t\n    @Override\n    public AssetRenderer<Guestbook> getAssetRenderer(long classPK, int type) \n    throws PortalException {\n      \n      Guestbook guestbook = _guestbookLocalService.getGuestbook(classPK);\n\n      GuestbookAssetRenderer guestbookAssetRenderer = \n      new GuestbookAssetRenderer(guestbook, _guestbookModelResourcePermission);\n\n      guestbookAssetRenderer.setAssetRendererType(type);\n      guestbookAssetRenderer.setServletContext(_servletContext);\n\n      return guestbookAssetRenderer;\n    }\n\n    @Override\n    public String getClassName() {\n    \treturn CLASS_NAME;\n    }\n\n    @Override\n    public String getType() {\n    \treturn TYPE;\n    }\n\n    @Override\n    public PortletURL getURLAdd(LiferayPortletRequest liferayPortletRequest,\n      LiferayPortletResponse liferayPortletResponse, long classTypeId) {\n        PortletURL portletURL = null;\n\n        try {\n          ThemeDisplay themeDisplay = (ThemeDisplay) \n          liferayPortletRequest.getAttribute(WebKeys.THEME_DISPLAY);\n\n          portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(themeDisplay),\n              GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n          portletURL.setParameter(\"mvcRenderCommandName\", \"/guestbookwebportlet/edit_guestbook\");\n          portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n\n          } catch (PortalException e) {\n          \n                logger.log(Level.SEVERE, e.getMessage()); \n                \n          }\n\n        return portletURL;\n    }\n\n    @Override\n    public boolean isLinkable() {\n        return _LINKABLE;\n    }\n\n    @Override\n    public String getIconCssClass() {\n      return \"bookmarks\";\n    }\n\n    @Reference(target = \"(osgi.web.symbolicname=com.liferay.docs.guestbook.portlet)\",\n        unbind = \"-\")\n    public void setServletContext(ServletContext servletContext) {\n        _servletContext = servletContext;\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setGuestbookLocalService(GuestbookLocalService guestbookLocalService) {\n        _guestbookLocalService = guestbookLocalService; \n    }\n\n  private ServletContext _servletContext;\n  private GuestbookLocalService _guestbookLocalService;\n  private static final boolean _LINKABLE = true;\n  public static final String CLASS_NAME = Guestbook.class.getName();\n  public static final String TYPE = \"guestbook\";\n  private Logger logger = Logger.getLogger(this.getClass().getName());\n  private ModelResourcePermission<Guestbook> _guestbookModelResourcePermission;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/asset/GuestbookEntryAssetRenderer.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.web.internal.asset;\n\nimport com.liferay.asset.kernel.model.BaseJSPAssetRenderer;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.exception.SystemException;\nimport com.liferay.portal.kernel.model.LayoutConstants;\nimport com.liferay.portal.kernel.portlet.LiferayPortletRequest;\nimport com.liferay.portal.kernel.portlet.LiferayPortletResponse;\nimport com.liferay.portal.kernel.portlet.PortletURLFactoryUtil;\nimport com.liferay.portal.kernel.security.permission.ActionKeys;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.util.HtmlUtil;\nimport com.liferay.portal.kernel.util.PortalUtil;\nimport com.liferay.petra.string.StringUtil;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport java.util.Locale;\nimport javax.portlet.PortletRequest;\nimport javax.portlet.PortletResponse;\nimport javax.portlet.PortletURL;\nimport javax.portlet.WindowState;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class GuestbookEntryAssetRenderer extends BaseJSPAssetRenderer<GuestbookEntry> {\n\n    public GuestbookEntryAssetRenderer(GuestbookEntry entry, ModelResourcePermission<GuestbookEntry> modelResourcePermission) {\n\n        _entry = entry;\n        _guestbookEntryModelResourcePermission = modelResourcePermission;\n    }\n\n    @Override\n    public boolean hasViewPermission(PermissionChecker permissionChecker) \n    {\n        try {\n            return _guestbookEntryModelResourcePermission.contains(\n                    permissionChecker, _entry, ActionKeys.VIEW);\n        }\n        catch (Exception e) {\n        }\n\n        return true;\n    }\n\n    @Override\n    public GuestbookEntry getAssetObject() {\n        return _entry;\n    }\n\n    @Override\n    public long getGroupId() {\n        return _entry.getGroupId();\n    }\n\n    @Override\n    public long getUserId() {\n\n        return _entry.getUserId();\n    }\n\n    @Override\n    public String getUserName() {\n        return _entry.getUserName();\n    }\n\n    @Override\n    public String getUuid() {\n        return _entry.getUuid();\n    }\n\n    @Override\n    public String getClassName() {\n        return GuestbookEntry.class.getName();\n    }\n\n    @Override\n    public long getClassPK() {\n        return _entry.getEntryId();\n    }\n\n    @Override\n    public String getSummary(PortletRequest portletRequest, \n            PortletResponse portletResponse) {\n        return \"Name: \" + _entry.getName() + \". Message: \" + _entry.getMessage();\n    }\n\n    @Override\n    public String getTitle(Locale locale) {\n        return _entry.getMessage();\n    }\n\n    @Override\n    public boolean include(HttpServletRequest request, \n            HttpServletResponse response, String template) throws Exception {\n        request.setAttribute(\"ENTRY\", _entry);\n        request.setAttribute(\"HtmlUtil\", HtmlUtil.getHtml());\n        request.setAttribute(\"StringUtil\", new StringUtil());\n        return super.include(request, response, template);\n    }\n\n    @Override\n    public String getJspPath(HttpServletRequest request, String template) {\n\n        if (template.equals(TEMPLATE_FULL_CONTENT)) {\n            request.setAttribute(\"gb_entry\", _entry);\n\n            return \"/asset/entry/\" + template + \".jsp\";\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public PortletURL getURLEdit(LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse) throws Exception {\n        PortletURL portletURL = liferayPortletResponse.createLiferayPortletURL(\n                getControlPanelPlid(liferayPortletRequest), GuestbookPortletKeys.GUESTBOOK,\n                PortletRequest.RENDER_PHASE);\n        portletURL.setParameter(\"mvcPath\", \"/guestbook/edit_entry.jsp\");\n        portletURL.setParameter(\"entryId\", String.valueOf(_entry.getEntryId()));\n        portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n\n        return portletURL;\n    }\n\n    @Override\n    public String getURLViewInContext(LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse, String noSuchEntryRedirect) \n                    throws Exception {\n        try {\n            long plid = PortalUtil.getPlidFromPortletId(_entry.getGroupId(),\n                    GuestbookPortletKeys.GUESTBOOK);\n\n            PortletURL portletURL;\n            if (plid == LayoutConstants.DEFAULT_PLID) {\n                portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(liferayPortletRequest),\n                        GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n            } else {\n                portletURL = PortletURLFactoryUtil.create(liferayPortletRequest,\n                        GuestbookPortletKeys.GUESTBOOK, plid, PortletRequest.RENDER_PHASE);\n            }\n\n            portletURL.setParameter(\"mvcPath\", \"/guestbook/view_entry.jsp\");\n            portletURL.setParameter(\"entryId\", String.valueOf(_entry.getEntryId()));\n\n            String currentUrl = PortalUtil.getCurrentURL(liferayPortletRequest);\n\n            portletURL.setParameter(\"redirect\", currentUrl);\n\n            return portletURL.toString();\n\n        } catch (PortalException e) {\n\n        } catch (SystemException e) {\n        }\n\n        return noSuchEntryRedirect;\n    }\n\n    @Override\n    public String getURLView(LiferayPortletResponse liferayPortletResponse, \n            WindowState windowState) throws Exception {\n\n        return super.getURLView(liferayPortletResponse, windowState);\n    }\n\n    @Override\n    public boolean isPrintable() {\n        return true;\n    }\n    private final ModelResourcePermission<GuestbookEntry> _guestbookEntryModelResourcePermission;\n    private GuestbookEntry _entry;\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/asset/GuestbookEntryAssetRendererFactory.java",
    "content": "/**\n * Copyright (c) 2000-present Liferay, Inc. All rights reserved.\n *\n * This library is free software; you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the Free\n * Software Foundation; either version 2.1 of the License, or (at your option)\n * any later version.\n *\n * This library is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\n * details.\n */\n\npackage com.liferay.docs.guestbook.web.internal.asset;\n\nimport com.liferay.asset.kernel.model.AssetRenderer;\nimport com.liferay.asset.kernel.model.AssetRendererFactory;\nimport com.liferay.asset.kernel.model.BaseAssetRendererFactory;\nimport com.liferay.docs.guestbook.constants.GuestbookPortletKeys;\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.docs.guestbook.service.GuestbookEntryLocalService;\nimport com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookEntryPermission;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.portlet.LiferayPortletRequest;\nimport com.liferay.portal.kernel.portlet.LiferayPortletResponse;\nimport com.liferay.portal.kernel.portlet.LiferayPortletURL;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\nimport com.liferay.portal.kernel.theme.ThemeDisplay;\nimport com.liferay.portal.kernel.util.WebKeys;\n\nimport javax.portlet.PortletRequest;\nimport javax.portlet.PortletURL;\nimport javax.portlet.WindowState;\nimport javax.portlet.WindowStateException;\nimport javax.servlet.ServletContext;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\n@Component(\n        immediate = true,\n        property = {\"javax.portlet.name=\" + GuestbookPortletKeys.GUESTBOOK},\n        service = AssetRendererFactory.class\n        )\npublic class GuestbookEntryAssetRendererFactory extends BaseAssetRendererFactory<GuestbookEntry> {\n\n    public GuestbookEntryAssetRendererFactory() {\n        setClassName(CLASS_NAME);\n        setLinkable(_LINKABLE);\n        setPortletId(GuestbookPortletKeys.GUESTBOOK);\n        setSearchable(true);\n        setSelectable(true);\n\n    }\n\n    @Override\n    public AssetRenderer<GuestbookEntry> getAssetRenderer(long classPK, int type)\n            throws PortalException {\n\n        GuestbookEntry entry = _guestbookEntryLocalService.getGuestbookEntry(classPK);\n\n        GuestbookEntryAssetRenderer guestbookEntryAssetRenderer = new GuestbookEntryAssetRenderer(entry, _guestbookEntryModelResourcePermission);\n\n        guestbookEntryAssetRenderer.setAssetRendererType(type);\n        guestbookEntryAssetRenderer.setServletContext(_servletContext);\n\n        return guestbookEntryAssetRenderer;\n    }\n\n    @Override\n    public String getClassName() {\n        return CLASS_NAME;\n    }\n\n    @Override\n    public String getType() {\n        return TYPE;\n    }\n\n    @Override\n    public boolean hasPermission(PermissionChecker permissionChecker,\n            long classPK, String actionId) throws Exception {\n\n        GuestbookEntry entry = _guestbookEntryLocalService.getGuestbookEntry(classPK);\n        return GuestbookEntryPermission.contains(permissionChecker, entry, actionId);\n    }\n\n    @Override\n    public PortletURL getURLAdd(LiferayPortletRequest liferayPortletRequest,\n            LiferayPortletResponse liferayPortletResponse, long classTypeId) {\n\n        PortletURL portletURL = null;\n\n        try {\n            ThemeDisplay themeDisplay = (ThemeDisplay) liferayPortletRequest.getAttribute(WebKeys.THEME_DISPLAY);\n\n            portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(themeDisplay),\n                    GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n            portletURL.setParameter(\"mvcRenderCommandName\", \"/guestbook/edit_entry\");\n            portletURL.setParameter(\"showback\", Boolean.FALSE.toString());\n        } catch (PortalException e) {\n        }\n\n        return portletURL;\n    }\n\n    @Override\n    public PortletURL getURLView(LiferayPortletResponse liferayPortletResponse, WindowState windowState) {\n\n        LiferayPortletURL liferayPortletURL\n        = liferayPortletResponse.createLiferayPortletURL(\n                GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE);\n\n        try {\n            liferayPortletURL.setWindowState(windowState);\n        } catch (WindowStateException wse) {\n\n        }\n        return liferayPortletURL;\n    }\n\n    @Override\n    public boolean isLinkable() {\n        return _LINKABLE;\n    }\n\n    @Override\n    public String getIconCssClass() {\n        return \"pencil\";\n    }\n\n    @Reference(target = \"(osgi.web.symbolicname=com.liferay.docs.guestbook.portlet)\",\n            unbind = \"-\")\n    public void setServletContext (ServletContext servletContext) {\n        _servletContext = servletContext;\n    }\n\n    @Reference(unbind = \"-\")\n    protected void setGuestbookEntryLocalService(GuestbookEntryLocalService guestbookEntryLocalService) {\n        _guestbookEntryLocalService = guestbookEntryLocalService;\n    }\n\n\n    private GuestbookEntryLocalService _guestbookEntryLocalService;\n    private ServletContext _servletContext;\n    private static final boolean _LINKABLE = true;\n    public static final String CLASS_NAME = GuestbookEntry.class.getName();\n    public static final String TYPE = \"entry\";\n\n    private ModelResourcePermission<GuestbookEntry>\n    _guestbookEntryModelResourcePermission;\n\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/security/permission/resource/GuestbookEntryPermission.java",
    "content": "package com.liferay.docs.guestbook.web.internal.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.GuestbookEntry;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n\n@Component(immediate = true)\npublic class GuestbookEntryPermission {\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, GuestbookEntry entry, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookEntryModelResourcePermission.contains(permissionChecker, entry, actionId);\n\t}\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, long entryId, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookEntryModelResourcePermission.contains(permissionChecker, entryId, actionId);\n\t}\n\t\n\t@Reference(\n\t\t\ttarget = \"(model.class.name=com.liferay.docs.guestbook.model.GuestbookEntry)\", \n\t\t\tunbind = \"-\")\n\tprotected void setEntryModelPermission(ModelResourcePermission<GuestbookEntry> modelResourcePermission) {\n\t\t\n\t\t_guestbookEntryModelResourcePermission = modelResourcePermission;\n\t}\n\t\n\tprivate static ModelResourcePermission<GuestbookEntry>_guestbookEntryModelResourcePermission;\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/security/permission/resource/GuestbookModelPermission.java",
    "content": "package com.liferay.docs.guestbook.web.internal.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.model.Guestbook;\nimport com.liferay.portal.kernel.exception.PortalException;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;\n\n@Component(immediate = true)\npublic class GuestbookModelPermission {\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, Guestbook guestbook, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookModelResourcePermission.contains(permissionChecker, guestbook, actionId);\n\t}\n\t\n\tpublic static boolean contains(\n\t\t\tPermissionChecker permissionChecker, long guestbookId, String actionId) throws PortalException {\n\t\t\n\t\treturn _guestbookModelResourcePermission.contains(permissionChecker, guestbookId, actionId);\n\t}\n\t\n\t@Reference(\n\t\t\ttarget = \"(model.class.name=com.liferay.docs.guestbook.model.Guestbook)\", \n\t\t\tunbind = \"-\")\n\tprotected void setEntryModelPermission(ModelResourcePermission<Guestbook> modelResourcePermission) {\n\t\t\n\t\t_guestbookModelResourcePermission = modelResourcePermission;\n\t}\n\t\n\tprivate static ModelResourcePermission<Guestbook>_guestbookModelResourcePermission;\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/java/com/liferay/docs/guestbook/web/internal/security/permission/resource/GuestbookPermission.java",
    "content": "package com.liferay.docs.guestbook.web.internal.security.permission.resource;\n\nimport org.osgi.service.component.annotations.Component;\nimport org.osgi.service.component.annotations.Reference;\n\nimport com.liferay.docs.guestbook.constants.GuestbookConstants;\nimport com.liferay.portal.kernel.security.permission.PermissionChecker;\nimport com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;\n\n@Component(immediate=true)\npublic class GuestbookPermission {\n\n\tpublic static boolean contains(PermissionChecker permissionChecker, long groupId, String actionId) {\n\t\t\n\t\treturn _portletResourcePermission.contains(permissionChecker, groupId, actionId);\n\t\t\n\t}\n\t\n\t@Reference(\n\t\t\ttarget=\"(resource.name=\" + GuestbookConstants.RESOURCE_NAME + \")\", \n\t\t\tunbind=\"-\"\n\t\t\t)\n\tprotected void setPortletResourcePermission(PortletResourcePermission portletResourcePermission) {\n\t\t\n\t\t_portletResourcePermission = portletResourcePermission;\n\t}\n\t\n\tprivate static PortletResourcePermission _portletResourcePermission;\n\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resource-actions/default.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE resource-action-mapping PUBLIC \"-//Liferay//DTD Resource Action  \nMapping 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd\">\n\n<resource-action-mapping>\n\n        <portlet-resource>\n            <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookAdminPortlet</portlet-name>\n            <permissions>\n                <supports>\n                    <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n                    <action-key>CONFIGURATION</action-key>\n                    <action-key>VIEW</action-key>\n                </supports>\n                <site-member-defaults>\n                    <action-key>VIEW</action-key>\n                </site-member-defaults>\n                <guest-defaults>\n                    <action-key>VIEW</action-key>\n                </guest-defaults>\n                <guest-unsupported>\n                    <action-key>ACCESS_IN_CONTROL_PANEL</action-key>\n                    <action-key>CONFIGURATION</action-key>\n                </guest-unsupported>\n            </permissions>\n        </portlet-resource>\n\n    <portlet-resource>\n        <portlet-name>com_liferay_docs_guestbook_portlet_GuestbookPortlet</portlet-name>\n        <permissions>\n            <supports>\n                <action-key>ADD_TO_PAGE</action-key>\n                <action-key>CONFIGURATION</action-key>\n                <action-key>VIEW</action-key>\n            </supports>\n            <site-member-defaults>\n                <action-key>VIEW</action-key>\n            </site-member-defaults>\n            <guest-defaults>\n                <action-key>VIEW</action-key>\n            </guest-defaults>\n            <guest-unsupported />\n        </permissions>\n    </portlet-resource>\n\n</resource-action-mapping>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/asset/entry/full_content.jsp",
    "content": "<%@include file=\"../../init.jsp\"%>\n\n<%\nGuestbookEntry entry = (GuestbookEntry)request.getAttribute(\"gb_entry\");\n\nentry = entry.toEscapedModel();\n%>\n\n<dl>\n\t\t<dt>Guestbook</dt>\n\t\t<dd><%= GuestbookLocalServiceUtil.getGuestbook(entry.getGuestbookId()).getName() %></dd>\n\t\t<dt>Name</dt>\n\t\t<dd><%= entry.getName() %></dd>\n\t\t<dt>Message</dt>\n\t\t<dd><%= entry.getMessage() %></dd>\n</dl>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/asset/guestbook/full_content.jsp",
    "content": "<%@include file=\"../../init.jsp\"%>\n\n<%\nGuestbook guestbook = (Guestbook)request.getAttribute(\"gb_guestbook\");\n\nguestbook = guestbook.toEscapedModel();\n%>\n\n<dl>\n\t\t<dt>Name</dt>\n\t\t<dd><%= guestbook.getName() %></dd>\n</dl>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/css/main.scss",
    "content": ".guestbook-web {\n}"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/edit_entry.jsp",
    "content": "<%@include file=\"../init.jsp\" %>\n\n<% \n\nlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\nGuestbookEntry entry = null;\nif (entryId > 0) {\n  entry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n}\n\nlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n%>\n\n<portlet:renderURL var=\"viewURL\">\n\t<portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\"></portlet:param>\n</portlet:renderURL>\n\n<portlet:actionURL name=\"addEntry\" var=\"addEntryURL\"></portlet:actionURL>\n<%-- Add header here --%>\n\n<liferay-ui:header\n    backURL=\"<%= viewURL.toString() %>\"\n    title=\"<%= entry == null ? \"Add Entry\" : entry.getName() %>\"\n/>\n\n<aui:form action=\"<%= addEntryURL %>\" name=\"<portlet:namespace />fm\">\n\n<aui:model-context bean=\"<%= entry %>\" model=\"<%= GuestbookEntry.class %>\" />\n\n\t<aui:fieldset>\n\n\t\t<aui:input name=\"name\" />\n\t\t<aui:input name=\"email\" />\n\t\t<aui:input name=\"message\" />\n\t\t<aui:input name=\"entryId\" type=\"hidden\" />\n\t\t<aui:input name=\"guestbookId\" type=\"hidden\" value='<%= entry == null ? guestbookId : entry.getGuestbookId() %>'/>\n\n\t</aui:fieldset>\n\n<%-- Add asset categories and links here --%>\n\n<liferay-asset:asset-categories-error />\n<liferay-asset:asset-tags-error />\n\n<liferay-ui:panel defaultState=\"closed\" \n                  extended=\"<%= false %>\" id=\"entryCategorizationPanel\" \n                  persistState=\"<%= true %>\" title=\"categorization\">\n\n    <aui:fieldset>\n       <liferay-asset:asset-categories-selector className=\"<%= GuestbookEntry.class.getName() %>\" classPK=\"<%= entryId %>\" />\n       <liferay-asset:asset-tags-selector className=\"<%= GuestbookEntry.class.getName() %>\" classPK=\"<%= entryId %>\" />\n    </aui:fieldset>\n\n</liferay-ui:panel>\n\n<liferay-ui:panel defaultState=\"closed\" \n                  extended=\"<%= false %>\" \n                  id=\"entryAssetLinksPanel\" \n                  persistState=\"<%= true %>\" \n                  title=\"related-assets\">\n\n    <aui:fieldset collapsed=\"<%= true %>\" collapsible=\"<%= true %>\" label=\"related-assets\">\n            \n        <liferay-asset:input-asset-links\n            className=\"<%= GuestbookEntry.class.getName() %>\"\n            classPK=\"<%= entryId %>\"\n        />\n            \n    </aui:fieldset>\n</liferay-ui:panel>\n\n\t<aui:button-row>\n\n\t\t<aui:button type=\"submit\"></aui:button>\n\t\t<aui:button type=\"cancel\" onClick=\"<%= viewURL.toString() %>\"></aui:button>\n\n\t</aui:button-row>\n</aui:form>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/entry_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\nString mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\nResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);\n\nGuestbookEntry entry = (GuestbookEntry)row.getObject(); \n%>\n\n<liferay-ui:icon-menu>\n\n<portlet:renderURL var=\"viewEntryURL\">\n  <portlet:param name=\"entryId\"\n    value=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n  <portlet:param name=\"mvcPath\"\n    value=\"/guestbook/view_entry.jsp\" />\n</portlet:renderURL>\n\n<liferay-ui:icon message=\"View\" url=\"<%= viewEntryURL.toString() %>\" />\n\t<c:if\n\t\ttest=\"<%= GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.UPDATE) %>\">\n\t\t<portlet:renderURL var=\"editURL\">\n\t\t\t<portlet:param name=\"entryId\"\n\t\t\t\tvalue=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n\t\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n\t\t</portlet:renderURL>\n\n\t\t<liferay-ui:icon image=\"edit\" message=\"Edit\"\n\t\t\turl=\"<%=editURL.toString() %>\" />\n\t</c:if>\n\n\t<c:if\n\ttest=\"<%=GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.PERMISSIONS) %>\">\n\n\t\t<liferay-security:permissionsURL\n\t\t\tmodelResource=\"<%= GuestbookEntry.class.getName() %>\"\n\t\t\tmodelResourceDescription=\"<%= entry.getMessage() %>\"\n\t\t\tresourcePrimKey=\"<%= String.valueOf(entry.getEntryId()) %>\"\n\t\t\tvar=\"permissionsURL\" />\n\t\n\t\t<liferay-ui:icon image=\"permissions\" url=\"<%= permissionsURL %>\" />\n\n\t</c:if>\n\n\t<c:if\n\t\ttest=\"<%=GuestbookEntryPermission.contains(permissionChecker, entry.getEntryId(), ActionKeys.DELETE) %>\">\n\n\t\t<portlet:actionURL name=\"deleteEntry\" var=\"deleteURL\">\n\t\t\t<portlet:param name=\"entryId\"\n\t\t\t\tvalue=\"<%= String.valueOf(entry.getEntryId()) %>\" />\n\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\tvalue=\"<%= String.valueOf(entry.getGuestbookId()) %>\" />\n\t\t</portlet:actionURL>\n\n\t\t<liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\t</c:if>\n\n</liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<liferay-ui:success key=\"entryAdded\" message=\"entry-added\" />\n<liferay-ui:success key=\"entryDeleted\" message=\"entry-deleted\" />\n\n<%\nlong guestbookId = Long.valueOf((Long) renderRequest\n\t\t.getAttribute(\"guestbookId\"));\n%>\n\n<portlet:renderURL var=\"searchURL\">\n\t<portlet:param name=\"mvcPath\" \n\tvalue=\"/guestbook/view_search.jsp\" />\n</portlet:renderURL>\n\n<aui:form action=\"<%=searchURL.toString() %>\" name=\"fm\">\n\n\t<div class=\"row\">\n\t\t<div class=\"col-md-8\">\n\t\t\t<aui:input inlineLabel=\"left\" label=\"\" name=\"keywords\" placeholder=\"search-entries\" size=\"256\" />\n\t\t</div>\n\n\t\t<div class=\"col-md-4\">\n\t\t\t<aui:button type=\"submit\" value=\"search\" />\n\t\t</div>\n\t</div>\n\n</aui:form>\n\n<aui:nav cssClass=\"nav-tabs\">\n\n\t<%\n\t\t\n    \tList<Guestbook> guestbooks = GuestbookLocalServiceUtil\n                .getGuestbooks(scopeGroupId, WorkflowConstants.STATUS_APPROVED);\n\n\t\t\tfor (int i = 0; i < guestbooks.size(); i++) {\n\n\t\t\t\tGuestbook curGuestbook = guestbooks.get(i);\n\t\t\t\tString cssClass = StringPool.BLANK;\n\n\t\t\t\tif (curGuestbook.getGuestbookId() == guestbookId) {\n\t\t\t\t\tcssClass = \"active\";\n\t\t\t\t}\n\n\t\t\t\tif (GuestbookModelPermission.contains(\n\t\t\t\t\tpermissionChecker, curGuestbook.getGuestbookId(), \"VIEW\")) {\n\t\t\t\t\t\t\t\t\t\t\n\t%>\n\n\t<portlet:renderURL var=\"viewPageURL\">\n\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/view.jsp\" />\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(curGuestbook.getGuestbookId())%>\" />\n\t</portlet:renderURL>\n\n\t\t\n\t<aui:nav-item cssClass=\"<%=cssClass%>\" href=\"<%=viewPageURL%>\"\n\t\tlabel=\"<%=HtmlUtil.escape(curGuestbook.getName())%>\" />\n\n\t<%  \n\t\t\t\t}\n\t\t\t\n\t\t\t}\n\t%>\n\n</aui:nav>\n\n<aui:button-row cssClass=\"guestbook-buttons\">\n\n    <c:if test='<%= GuestbookPermission.contains(permissionChecker, scopeGroupId, \"ADD_ENTRY\") %>'>\n\t\t<portlet:renderURL var=\"addEntryURL\">\n\t\t\t<portlet:param name=\"mvcPath\" value=\"/guestbook/edit_entry.jsp\" />\n\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\tvalue=\"<%=String.valueOf(guestbookId)%>\" />\n\t\t</portlet:renderURL>\n\n\t\t<aui:button onClick=\"<%=addEntryURL.toString()%>\" value=\"Add Entry\"></aui:button>\n\t\t\n\t</c:if>\n\n</aui:button-row>\n\n    <liferay-ui:search-container total=\"<%=GuestbookEntryLocalServiceUtil.\n                    getGuestbookEntriesCount(scopeGroupId.longValue(), \n                    guestbookId, WorkflowConstants.STATUS_APPROVED)%>\">\n    <liferay-ui:search-container-results results=\n                    \"<%=GuestbookEntryLocalServiceUtil.getGuestbookEntries(\n                    scopeGroupId.longValue(), guestbookId, \n                    WorkflowConstants.STATUS_APPROVED, \n                    searchContainer.getStart(), searchContainer.getEnd())%>\" />\n\n<liferay-ui:search-container-row\n\tclassName=\"com.liferay.docs.guestbook.model.GuestbookEntry\" modelVar=\"entry\">\n\n\t<liferay-ui:search-container-column-text property=\"message\" />\n\n\t<liferay-ui:search-container-column-text property=\"name\" />\n\n\t<liferay-ui:search-container-column-jsp\n\t\talign=\"right\" \n\t\tpath=\"/guestbook/entry_actions.jsp\" />\n</liferay-ui:search-container-row>\n\n<liferay-ui:search-iterator />\n\n</liferay-ui:search-container>\n"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/view_entry.jsp",
    "content": "<%@ include file=\"../init.jsp\"%>\n\n<%\n\tlong entryId = ParamUtil.getLong(renderRequest, \"entryId\");\n\n\tlong guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n\n\tGuestbookEntry entry = null;\n\n\tentry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n\n\tentryId = entry.getEntryId();\n\n\tentry = entry.toEscapedModel();\n\n\tAssetEntry assetEntry = \n\tAssetEntryLocalServiceUtil.getEntry(GuestbookEntry.class.getName(), \n\t  entry.getEntryId());\n\n\tString currentURL = PortalUtil.getCurrentURL(request);\n\tPortalUtil.addPortletBreadcrumbEntry(request, entry.getMessage(),\n\tcurrentURL);\n\t\n\tPortalUtil.setPageSubtitle(entry.getMessage(), request);\n\tPortalUtil.setPageDescription(entry.getMessage(), request);\n\n\tList<AssetTag> assetTags = \n\tAssetTagLocalServiceUtil.getTags(GuestbookEntry.class.getName(), \n\tentry.getEntryId());\n\tPortalUtil.setPageKeywords(ListUtil.toString(assetTags, \"name\"), \n\trequest);\n%>\n\n<liferay-portlet:renderURL varImpl=\"viewEntryURL\">\n  <portlet:param name=\"mvcPath\"\n\tvalue=\"/guestbook/view_entry.jsp\" />\n  <portlet:param name=\"entryId\" value=\"<%=String.valueOf(entryId)%>\" />\n</liferay-portlet:renderURL>\n\n<liferay-portlet:renderURL varImpl=\"viewURL\">\n  <portlet:param name=\"mvcPath\"\n\tvalue=\"/guestbook/view.jsp\" />\n</liferay-portlet:renderURL>\n\n<liferay-ui:header backURL=\"<%=viewURL.toString()%>\"\n  title=\"<%=entry.getName()%>\" \n/>\n\n<dl>\n  <dt>Guestbook</dt>\n  <dd><%=GuestbookLocalServiceUtil.getGuestbook(entry.getGuestbookId()).getName()%></dd>\n  <dt>Name</dt>\n  <dd><%=entry.getName()%></dd>\n  <dt>Message</dt>\n  <dd><%=entry.getMessage()%></dd>\n</dl>\n\n<c:if test=\"<% themeDisplay.isSignedIn() %>\">\n\n\t<liferay-ui:panel-container extended=\"<%=false%>\"\n\t  id=\"guestbookCollaborationPanelContainer\" persistState=\"<%=true%>\">\n\t  <liferay-ui:panel collapsible=\"<%=true%>\" extended=\"<%=true%>\"\n\t\tid=\"guestbookCollaborationPanel\" persistState=\"<%=true%>\"\n\t\ttitle=\"Collaboration\">\n\t\t\n\t\t<liferay-ui:ratings \n\t\t\tclassName=\"<%=GuestbookEntry.class.getName() %>\" \n\t\t\tclassPK=\"<%=entry.getEntryId() %>\" \n\t\t\ttype=\"stars\" />\n\n\t\t<br/>\n\n\t\t<% \n\t\t\tDiscussion discussion = \n\t\t\tCommentManagerUtil.getDiscussion(user.getUserId(), \n\t\t\tscopeGroupId, GuestbookEntry.class.getName(), \n\t\t\tentry.getEntryId(), new ServiceContextFunction(request));\n\t\t%>\n\n\t\t<c:if test=\"<%= discussion != null %>\">\n\t\t  <h2>\n\t\t\t<strong><liferay-ui:message arguments=\"<%= discussion.getDiscussionCommentsCount() %>\" key='<%= (discussion.getDiscussionCommentsCount() == 1) ? \"x-comment\" : \"x-comments\" %>' /></strong>\n\t      </h2>\n\n\t\t  <liferay-comment:discussion\n\t\t\tclassName=\"<%= GuestbookEntry.class.getName() %>\"\n\t\t\tclassPK=\"<%= entry.getEntryId() %>\"\n\t\t\tdiscussion=\"<%= discussion %>\"\n\t\t\tformName=\"fm2\"\n\t\t\tratingsEnabled=\"true\"\n\t\t\tredirect=\"<%= currentURL %>\"\n\t\t\tuserId=\"<%= entry.getUserId() %>\"\n\t\t\t/>\n\t\t\t\n\t\t</c:if>\t\n\n\t\t</liferay-ui:panel>\n\t</liferay-ui:panel-container>\n\n</c:if>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook/view_search.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\n  String keywords = ParamUtil.getString(request, \"keywords\");\n  long guestbookId = ParamUtil.getLong(renderRequest, \"guestbookId\");\n%>\n<portlet:renderURL var=\"searchURL\">\n\t<portlet:param name=\"mvcPath\" \n\tvalue=\"/guestbook/view_search.jsp\" />\n</portlet:renderURL>\n\n<portlet:renderURL var=\"viewURL\">\n\t<portlet:param \n\t\tname=\"mvcPath\" \n\t\tvalue=\"/guestbook/view.jsp\" \n\t/>\n</portlet:renderURL>\n\n<aui:form action=\"${searchURL}\" name=\"fm\">\n\n\t<liferay-ui:header backURL=\"${viewURL}\" title=\"back\" />\n\n\t<div class=\"row\">\n\t\t<div class=\"col-md-8\">\n\t\t\t<aui:input inlineLabel=\"left\" label=\"\" name=\"keywords\" placeholder=\"search-entries\" size=\"256\" />\n\t\t</div>\n\n\t\t<div class=\"col-md-4\">\n\t\t\t<aui:button type=\"submit\" value=\"search\" />\n\t\t</div>\n\t</div>\n</aui:form>\n\n<%\n\tSearchContext searchContext = SearchContextFactory.getInstance(request);\n\n\tsearchContext.setKeywords(keywords);\n\tsearchContext.setAttribute(\"paginationType\", \"more\");\n\tsearchContext.setStart(0);\n\tsearchContext.setEnd(10);\n\n    Indexer<GuestbookEntry> indexer = IndexerRegistryUtil.getIndexer(GuestbookEntry.class);\n\n    Hits hits = indexer.search(searchContext);\n\n    List<GuestbookEntry> entries = new ArrayList<GuestbookEntry>();\n\n\tfor (int i = 0; i < hits.getDocs().length; i++) {\n\t\t\tDocument doc = hits.doc(i);\n\n\t\t\tlong entryId = GetterUtil\n\t\t\t.getLong(doc.get(Field.ENTRY_CLASS_PK));\n\n\t\t\tGuestbookEntry entry = null;\n\n\t\t\ttry {\n\t\t\t\t\tentry = GuestbookEntryLocalServiceUtil.getGuestbookEntry(entryId);\n\t\t\t} catch (PortalException pe) {\n\t\t\t\t\t_log.error(pe.getLocalizedMessage());\n\t\t\t} catch (SystemException se) {\n\t\t\t\t\t_log.error(se.getLocalizedMessage());\n\t\t\t}\n\n\t\t\tentries.add(entry);\n\t}\n\n\tList<Guestbook> guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);\n\n\tMap<String, String> guestbookMap = new HashMap<String, String>();\n\n\tfor (Guestbook guestbook : guestbooks) {\n\t\t\tguestbookMap.put(Long.toString(guestbook.getGuestbookId()), guestbook.getName());\n\t}\n%>\n\n<liferay-ui:search-container delta=\"10\" \n\temptyResultsMessage=\"no-entries-were-found\" \n\ttotal=\"<%= entries.size() %>\">\n\t\t<liferay-ui:search-container-results\n\t\t\t\tresults=\"<%= entries %>\"\n/>\n\n\t<liferay-ui:search-container-row\n\t\t\tclassName=\"com.liferay.docs.guestbook.model.GuestbookEntry\"\n\t\t\tkeyProperty=\"entryId\" modelVar=\"entry\" escapedModel=\"<%=true%>\">\n\n        <liferay-ui:search-container-column-text name=\"guestbook\"\n            value=\"<%=guestbookMap.get(Long.toString(entry.getGuestbookId()))%>\" />\n\n        <liferay-ui:search-container-column-text property=\"message\" />\n\n        <liferay-ui:search-container-column-text property=\"name\" />\n                \n        <liferay-ui:search-container-column-jsp\n            path=\"/guestbook/entry_actions.jsp\"\n            align=\"right\" />\n   </liferay-ui:search-container-row>\n   \n   <liferay-ui:search-iterator />\n\n</liferay-ui:search-container>\n\n<%!\n\tprivate static Log _log = LogFactoryUtil.getLog(\"html.guestbook.view_search_jsp\");\n%>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/edit_guestbook.jsp",
    "content": "<%@include file = \"../init.jsp\" %>\n\n<%\n        long guestbookId = ParamUtil.getLong(request, \"guestbookId\");\n        \n        Guestbook guestbook = null;\n\n        if (guestbookId > 0) {\n                guestbook = GuestbookLocalServiceUtil.getGuestbook(guestbookId);\n        }\n%>\n\n<portlet:renderURL var=\"viewURL\">\n        <portlet:param name=\"mvcPath\" value=\"/guestbook_admin/view.jsp\" />\n</portlet:renderURL>\n\n<portlet:actionURL name='<%= guestbook == null ? \"addGuestbook\" : \"updateGuestbook\" %>' var=\"editGuestbookURL\" />\n\n<aui:form action=\"<%= editGuestbookURL %>\" name=\"fm\">\n\n        <aui:model-context bean=\"<%= guestbook %>\" model=\"<%= Guestbook.class %>\" />\n\n        <aui:input type=\"hidden\" name=\"guestbookId\"\n            value='<%= guestbook == null ? \"\" : guestbook.getGuestbookId() %>' />\n\n        <aui:fieldset>\n             <aui:input name=\"name\" />\n        </aui:fieldset>\n\n        <aui:button-row>\n             <aui:button type=\"submit\" />\n             <aui:button onClick=\"<%= viewURL %>\" type=\"cancel\"  />\n        </aui:button-row>\n\n    <liferay-asset:asset-categories-error />\n    <liferay-asset:asset-tags-error />\n\n\t<c:if test=\"<%= guestbook != null %>\">\n\t\t<liferay-ui:panel defaultState=\"closed\" extended=\"<%= false %>\"\n\t\t  id=\"guestbookCategorizationPanel\" persistState=\"<%= true %>\"\n\t\t  title=\"categorization\">\n\n\t\t\t<aui:fieldset>\n\t\t\t\t<liferay-asset:asset-categories-selector className=\"<%= Guestbook.class.getName() %>\" classPK=\"<%= guestbook.getGuestbookId() %>\" />\n\t\t\t\t<liferay-asset:asset-tags-selector className=\"<%= Guestbook.class.getName() %>\" classPK=\"<%= guestbook.getGuestbookId() %>\" />\n\t\t\t</aui:fieldset>\n\n\t\t</liferay-ui:panel>\n\t</c:if>\n\n    <liferay-ui:panel defaultState=\"closed\" extended=\"<%= false %>\"\n      id=\"guestbookAssetLinksPanel\" persistState=\"<%= true %>\"\n      title=\"related-assets\">\n      <aui:fieldset>\n        <liferay-asset:input-asset-links\n          className=\"<%= Guestbook.class.getName() %>\"\n          classPK=\"<%= guestbookId %>\" />\n      </aui:fieldset>\n    </liferay-ui:panel>\n\n</aui:form>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/guestbook_actions.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<%\n\tString mvcPath = ParamUtil.getString(request, \"mvcPath\");\n\n\tResultRow row = (ResultRow) request\n\t\t\t\t\t.getAttribute(\"SEARCH_CONTAINER_RESULT_ROW\");\n\n\tGuestbook guestbook = (Guestbook) row.getObject();\n%>\n\n<liferay-ui:icon-menu>\n\n\t<portlet:renderURL var=\"editURL\">\n\t\t<portlet:param name=\"guestbookId\"\n\t\t\tvalue=\"<%=String.valueOf(guestbook.getGuestbookId()) %>\" />\n\t\t<portlet:param name=\"mvcPath\"\n\t\t\tvalue=\"/guestbook_admin/edit_guestbook.jsp\" />\n\t</portlet:renderURL>\n\n\t<liferay-ui:icon image=\"edit\" message=\"Edit\"\n\t\t\turl=\"<%=editURL.toString() %>\" />\n\n\t<portlet:actionURL name=\"deleteGuestbook\" var=\"deleteURL\">\n\t\t\t<portlet:param name=\"guestbookId\"\n\t\t\t\tvalue=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\" />\n\t</portlet:actionURL>\n\n\t<liferay-ui:icon-delete url=\"<%=deleteURL.toString() %>\" />\n\n\t<c:if\n\t\ttest=\"<%=GuestbookModelPermission.contains(permissionChecker, guestbook.getGuestbookId(), ActionKeys.PERMISSIONS) %>\">\n\n\t\t\t<liferay-security:permissionsURL\n\t\t\t\tmodelResource=\"<%= Guestbook.class.getName() %>\"\n\t\t\t\tmodelResourceDescription=\"<%= guestbook.getName() %>\"\n\t\t\t\tresourcePrimKey=\"<%= String.valueOf(guestbook.getGuestbookId()) %>\"\n\t\t\t\tvar=\"permissionsURL\" />\n\t\t\n\t\t\t<liferay-ui:icon image=\"permissions\" url=\"<%= permissionsURL %>\" />\n\n\t</c:if>\n</liferay-ui:icon-menu>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbook_admin/view.jsp",
    "content": "<%@include file=\"../init.jsp\"%>\n\n<liferay-ui:success key=\"guestbookAdded\" message=\"guestbook-added\" />\n<liferay-ui:success key=\"guestbookUpdated\" message=\"guestbook-updated\" />\n<liferay-ui:success key=\"guestbookDeleted\" message=\"guestbook-deleted\" />\n\n<liferay-ui:search-container\n\ttotal=\"<%= GuestbookLocalServiceUtil.getGuestbooksCount(scopeGroupId) %>\">\n\t<liferay-ui:search-container-results\n\t\tresults=\"<%= GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId, \n\t\t\tsearchContainer.getStart(), searchContainer.getEnd()) %>\" />\n\n\t<liferay-ui:search-container-row\n\t\tclassName=\"com.liferay.docs.guestbook.model.Guestbook\" modelVar=\"guestbook\">\n\n\t\t<liferay-ui:search-container-column-text property=\"name\" />\n\t\t\t\t\n\t    <liferay-ui:search-container-column-status property=\"status\" />\n\n\t\t<liferay-ui:search-container-column-jsp\n\t\t\talign=\"right\" \n\t\t\tpath=\"/guestbook_admin/guestbook_actions.jsp\" />\n\t\t\n\t</liferay-ui:search-container-row>\n\n\t<liferay-ui:search-iterator />\n</liferay-ui:search-container>\n\n<aui:button-row cssClass=\"guestbook-admin-buttons\">\n\t<portlet:renderURL var=\"addGuestbookURL\">\n\t\t<portlet:param name=\"mvcPath\"\n\t\t\tvalue=\"/guestbook_admin/edit_guestbook.jsp\" />\n\t\t<portlet:param name=\"redirect\" value=\"<%= \"currentURL\" %>\" />\n\t</portlet:renderURL>\n\t\t\n\t<aui:button onClick=\"<%= addGuestbookURL.toString() %>\"\n\t\tvalue=\"Add Guestbook\" />\n</aui:button-row>"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/init.jsp",
    "content": "<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\"%>\n<%@ taglib uri=\"http://java.sun.com/portlet_2_0\" prefix=\"portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/aui\" prefix=\"aui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/portlet\" prefix=\"liferay-portlet\"%>\n<%@ taglib uri=\"http://liferay.com/tld/theme\" prefix=\"liferay-theme\"%>\n<%@ taglib uri=\"http://liferay.com/tld/ui\" prefix=\"liferay-ui\"%>\n<%@ taglib uri=\"http://liferay.com/tld/frontend\" prefix=\"liferay-frontend\" %>\n<%@ taglib uri=\"http://liferay.com/tld/security\" prefix=\"liferay-security\" %>\n<%@ taglib uri=\"http://liferay.com/tld/asset\" prefix=\"liferay-asset\" %>\n<%@ taglib uri=\"http://liferay.com/tld/comment\" prefix=\"liferay-comment\" %>\n\n<%@ page import=\"java.util.Map\" %> \n<%@ page import=\"java.util.HashMap\" %>\n<%@ page import=\"com.liferay.asset.kernel.service.AssetEntryLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.asset.kernel.service.AssetTagLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.asset.kernel.model.AssetEntry\" %>\n<%@ page import=\"com.liferay.asset.kernel.model.AssetTag\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.ListUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.comment.Discussion\" %>\n<%@ page import=\"com.liferay.portal.kernel.comment.CommentManagerUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.service.ServiceContextFunction\" %>\n<%@ page import=\"java.util.List\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.ParamUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.HtmlUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.petra.string.StringPool\" %>\n<%@ page import=\"com.liferay.portal.kernel.model.PersistedModel\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.SearchEntry\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.ResultRow\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.Guestbook\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookEntryLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil\" %>\n<%@ page import=\"com.liferay.docs.guestbook.model.GuestbookEntry\" %> \n<%@ page import=\"com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookModelPermission\" %>\n<%@ page import=\"com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookPermission\" %>\n<%@ page import=\"com.liferay.docs.guestbook.web.internal.security.permission.resource.GuestbookEntryPermission\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.WebKeys\" %>\n<%@ page import=\"com.liferay.portal.kernel.security.permission.ActionKeys\" %>\n<%@ page import=\"com.liferay.portal.kernel.dao.search.SearchContainer\" %>\n<%@ page import=\"com.liferay.portal.kernel.exception.PortalException\" %>\n<%@ page import=\"com.liferay.portal.kernel.exception.SystemException\" %>\n<%@ page import=\"com.liferay.portal.kernel.language.LanguageUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.log.Log\" %>\n<%@ page import=\"com.liferay.portal.kernel.log.LogFactoryUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Indexer\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.IndexerRegistryUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.SearchContext\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.SearchContextFactory\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Hits\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Document\" %>\n<%@ page import=\"com.liferay.portal.kernel.search.Field\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.GetterUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.Validator\" %>\n<%@ page import=\"com.liferay.portal.kernel.util.PortalUtil\" %>\n<%@ page import=\"com.liferay.portal.kernel.workflow.WorkflowConstants\"%>\n\n<%@ page import=\"java.util.ArrayList\" %>\n<%@ page import=\"java.util.Map\" %>\n<%@ page import=\"java.util.HashMap\" %>\n\n<%@ page import=\"javax.portlet.PortletURL\" %>\n\n<liferay-theme:defineObjects />\n\n<portlet:defineObjects />"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/content/Language.properties",
    "content": "entry-added=Entry added successfully.\nentry-deleted=Entry deleted successfully.\nguestbook-added=Guestbook added successfully.\nguestbook-updated=Guestbook updated successfully.\nguestbook-deleted=Guestbook deleted successfully.\nmodel.resource.com.liferay.docs.guestbook.model.Guestbook=Guestbook\nmodel.resource.com.liferay.docs.guestbook.model.GuestbookEntry=Guestbook Entry"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/portlet.properties",
    "content": "resource.actions.configs=META-INF/resource-actions/default.xml"
  },
  {
    "path": "en/developer/tutorials/code/guestbook/10-workflow/com-liferay-docs-guestbook/settings.gradle",
    "content": "buildscript {\n\tdependencies {\n\t\tclasspath group: \"com.liferay\", name: \"com.liferay.gradle.plugins.workspace\", version: \"2.0.4\"\n\t\tclasspath group: \"net.saliman\", name: \"gradle-properties-plugin\", version: \"1.4.6\"\n\t}\n\n\trepositories {\n\t\tmaven {\n\t\t\turl \"https://repository-cdn.liferay.com/nexus/content/groups/public\"\n\t\t}\n\t}\n}\n\napply plugin: \"net.saliman.properties\"\n\napply plugin: \"com.liferay.workspace\""
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/gulpfile.js",
    "content": "/**\n * © 2017 Liferay, Inc. <https://liferay.com>\n *\n * SPDX-License-Identifier: MIT\n */\n\n'use strict';\n\nvar gulp = require('gulp');\nvar liferayThemeTasks = require('liferay-theme-tasks');\n\nliferayThemeTasks.registerTasks({\n\tgulp,\n});\n"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/liferay-theme.json",
    "content": "{\n  \"LiferayTheme\": {\n    \"deploymentStrategy\": \"LocalAppServer\",\n    \"appServerPath\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761\\\\liferay-ce-portal-7.2.0-ga1\\\\tomcat-9.0.17\",\n    \"deployPath\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761\\\\liferay-ce-portal-7.2.0-ga1\\\\deploy\",\n    \"url\": \"http://localhost:8080\",\n    \"appServerPathPlugin\": \"C:\\\\Users\\\\liferay\\\\opt\\\\Liferay\\\\bundles\\\\liferay-ce-portal-tomcat-7.2.0-ga1-20190531153709761\\\\liferay-ce-portal-7.2.0-ga1\\\\tomcat-9.0.17\\\\webapps\\\\lunar-resort-theme7-2-new-theme\",\n    \"deployed\": true,\n    \"pluginName\": \"lunar-resort-theme7-2-new-theme\"\n  }\n}"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/package.json",
    "content": "{\n\t\"devDependencies\": {\n\t\t\"gulp\": \"3.9.1\",\n\t\t\"liferay-theme-tasks\": \"^9.3.0\",\n\t\t\"compass-mixins\": \"0.12.10\",\n\t\t\"liferay-frontend-common-css\": \"1.0.4\",\n\t\t\"liferay-frontend-theme-styled\": \"4.0.7\",\n\t\t\"liferay-frontend-theme-unstyled\": \"4.0.4\"\n\t},\n\t\"keywords\": [\n\t\t\"liferay-theme\"\n\t],\n\t\"liferayTheme\": {\n\t\t\"baseTheme\": \"styled\",\n\t\t\"fontAwesome\": false,\n\t\t\"screenshot\": \"\",\n\t\t\"templateLanguage\": \"ftl\",\n\t\t\"version\": \"7.2\"\n\t},\n\t\"main\": \"package.json\",\n\t\"name\": \"lunar-resort-theme7-2-new-theme\",\n\t\"version\": \"1.0.0\"\n}\n"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/src/WEB-INF/liferay-look-and-feel.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE look-and-feel PUBLIC \"-//Liferay//DTD Look and Feel 7.2.0//EN\" \"http://www.liferay.com/dtd/liferay-look-and-feel_7_2_0.dtd\">\n\n<look-and-feel>\n\t<compatibility>\n\t\t<version>7.2.0+</version>\n\t</compatibility>\n\t<theme id=\"lunar-resort-theme7-2-new\" name=\"Lunar Resort Theme 7-2-new\">\n\t\t<template-extension>ftl</template-extension>\n\t\t<settings>\n\t\t\t<setting configurable=\"true\" key=\"show-footer\" type=\"checkbox\" value=\"true\" />\n\t\t</settings>\n\t\t<color-scheme id=\"01\" name=\"Default\">\n\t\t\t\t<default-cs>true</default-cs>\n\t\t\t\t<css-class>default</css-class>\n\t\t\t\t<color-scheme-images-path>\n\t\t\t\t\t\t${images-path}/color_schemes/${css-class}\n\t\t\t\t</color-scheme-images-path>\n\t\t</color-scheme>\n\t\t<color-scheme id=\"02\" name=\"Eclipse\">\n\t\t\t<css-class>eclipse</css-class>\n\t\t</color-scheme>\n\t\t<portlet-decorator id=\"barebone\" name=\"Barebone\">\n\t\t\t<portlet-decorator-css-class>portlet-barebone</portlet-decorator-css-class>\n\t\t</portlet-decorator>\n\t\t<portlet-decorator id=\"borderless\" name=\"Borderless\">\n\t\t\t<portlet-decorator-css-class>portlet-borderless</portlet-decorator-css-class>\n\t\t</portlet-decorator>\n\t\t<portlet-decorator id=\"decorate\" name=\"Decorate\">\n\t\t\t<default-portlet-decorator>true</default-portlet-decorator>\n\t\t\t<portlet-decorator-css-class>portlet-decorate</portlet-decorator-css-class>\n\t\t</portlet-decorator>\n\t</theme>\n</look-and-feel>"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/src/WEB-INF/liferay-plugin-package.properties",
    "content": "name=Lunar Resort Theme 7-2-new\nmodule-group-id=liferay\nmodule-incremental-version=1\ntags=\nshort-description=\nlong-description=\nchange-log=\npage-url=http://www.liferay.com\nauthor=Liferay, Inc.\nlicenses=LGPL\nliferay-versions=7.2.0+\n\nresources-importer-developer-mode-enabled=true\n"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/src/css/_colors.scss",
    "content": "$lunar-resort-orange: #dfa356;\n$lunar-resort-blue: #415fa7;\n$lunar-resort-link-teal: #00ccFF;\n$lunar-resort-eclipse-yellow: #dfd456;\n$lunar-resort-eclipse-red: #a75441;"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/src/css/_custom.scss",
    "content": "/* These inject tags are used for dynamically creating imports for themelet styles, you can place them where ever you like in this file. */\n\n/* inject:imports */\n/* endinject */\n\n/* This file allows you to override default styles in one central location for easier upgrade and maintenance. */\n@import 'colors';\n@import \"color_schemes/eclipse\";\n\nbody {\n  \n  a.btn-orange {\n    background-color: $lunar-resort-orange;\n    margin-right: 5px;\n    \n    &:hover {\n      border-color: $white;\n    }\n    \n    @include media-breakpoint-down(sm){\n      width: 100%;\n    }\n  }\n\n  header {\n    background-color: $lunar-resort-blue;\n    \n    .lunar-user a {\n        color: $lunar-resort-link-teal;\n    }\n\n    .user-avatar-link .lexicon-icon {\n      color: $lunar-resort-blue;\n    }\n    \n    li.nav-item {\n      \n      & a.nav-link span {\n        font-size: 1.5em;\n      }\n      \n      &:hover ul.child-menu {\n        background-color: $lunar-resort-blue;\n        display: block;\n        margin-top: -10px;\n      }\n      \n      &.selected {\n        background-color: $white;\n        height: 73px;\n        & a.nav-link {\n          color: $lunar-resort-blue;\n          font-weight: bold;\n          &:hover {\n            color: $lunar-resort-blue;\n            font-weight: normal;\n            padding-left: 9.619px;\n            padding-right: 9.619px;\n          }\n        }\n      }\n      \n      @include media-breakpoint-down(sm){\n        ul.child-menu {\n          display: block;\n        }\n      }\n      \n    }\n  }\n\n  #footer {\n    background-color: $lunar-resort-blue;\n    color: $white;\n    ul{\n      margin-left: auto;\n      margin-right: auto;\n\n      &.navbar-nav {\n        width: 410px;  \n        .nav-item.hover:after {\n          width: auto;\n        }\n        \n        a {\n          color: $white;\n          @include media-breakpoint-down(sm){\n            padding-left: 6px;\n            padding-right: 6px;\n          }\n        }\n      }\n    }\n    \n    #socialMediaWrapper ul {\n      width: 192px;\n      \n      li a {\n        font-size: 2rem;\n      }\n    }\n    \n    p.powered-by a, .contact-info-container a {\n        color: $lunar-resort-link-teal;\n    }\n\n  }\n  \n  &.has-control-menu {\n   header {\n     margin-top: 56px;  \n     @include media-breakpoint-down(sm){\n       margin-top: 48px;\n     }\n   }\n  }\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/src/css/_imports.scss",
    "content": "@import \"bourbon\";\n\n@import \"mixins\";\n\n@import \"compat/mixins\";\n\n@import \"clay/atlas-variables\";"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/src/css/clay.scss",
    "content": "//@import 'liferay-font-awesome/scss/font-awesome';\n//@import 'liferay-font-awesome/scss/glyphicons';\n\n@import \"clay/atlas\";"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/src/css/color_schemes/_eclipse.scss",
    "content": "/* These inject tags are used for dynamically creating imports for themelet styles, you can place them where ever you like in this file. */\n\n/* inject:imports */\n/* endinject */\n\n/* This file allows you to override default styles in one central location for easier upgrade and maintenance. */\n@import '../colors';\n\nbody.eclipse {\n  \n  a.btn-orange {\n    background-color: $lunar-resort-eclipse-yellow;\n  }\n\n  header {\n    background-color: $lunar-resort-eclipse-red;\n\n    .user-avatar-link .lexicon-icon {\n      color: $lunar-resort-eclipse-red;\n    }\n    \n    li.nav-item {\n      \n      ul.child-menu {\n        background-color: $lunar-resort-eclipse-red;\n      }\n      \n      &:hover ul.child-menu {\n        background-color: $lunar-resort-eclipse-red;\n      }\n      \n      &.selected {\n        & a.nav-link {\n          color: $lunar-resort-eclipse-red;\n          &:hover {\n            color: $lunar-resort-eclipse-red;\n          }\n        }\n      }\n    }\n  }\n\n  #footer {\n    background-color: $lunar-resort-eclipse-red;\n  }\n\n}"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/src/templates/footer.ftl",
    "content": "<footer id=\"footer\" role=\"contentinfo\">\n  <div id=\"navbarContactWrapper\" class=\"row mx-0\">\n  <nav id=\"navbarFooter\" class=\"col-12 col-md-6 pt-5\">\n  <div id=\"socialMediaWrapper\" class=\"col-12 col-md-4 text-center mx-auto mb-4\">\n    <h2 class=\"nav-heading\">\n        Follow Us\n    </h2>\n    <div id=\"socialMediaLinks\">\n      <ul class=\"nav flex-row mx-auto\">\n          <li class=\"mx-2\">\n              <div id=\"facebook\"><a class=\"text-white\"\n              href=\"http://www.facebook.com/pages/Liferay/45119213107\" \n              target=\"_blank\"><span class=\"hide\">Facebook</span>\n              <@clay[\"icon\"] symbol=\"social-facebook\" />\n              </a></div>\n          </li>\n          <li class=\"mx-2\">\n              <div id=\"twitter\"><a class=\"text-white\" \n              href=\"http://www.twitter.com/liferay\" \n              target=\"_blank\"><span class=\"hide\">Twitter</span>\n              <@clay[\"icon\"] symbol=\"twitter\" />\n              </a></div>\n          </li>\n          <li class=\"mx-2\">\n              <div id=\"linked-in\"><a class=\"text-white\"\n              href=\"http://www.linkedin.com/company/83609\" \n              target=\"_blank\"><span class=\"hide\">LinkedIn</span>\n              <@clay[\"icon\"] symbol=\"social-linkedin\" />\n              </a></div>\n          </li>\n          <li class=\"mx-2\">\n              <div id=\"youtube\"><a class=\"text-white\"\n              href=\"http://www.youtube.com/user/liferayinc\" \n              target=\"_blank\"><span class=\"hide\">YouTube</span>\n              <@clay[\"icon\"] symbol=\"video\" />\n              </a></div>\n          </li>\n      </ul>\n    </div>\n  </div>\n    <div class=\"text-center mx-auto\">\n      <div class=\"nav text-uppercase\" role=\"menubar\">\n        <#assign preferencesMap = {\"displayDepth\": \"1\", \"portletSetupPortletDecoratorId\": \"barebone\"} />\n\n        <@liferay.navigation_menu\n          default_preferences=freeMarkerPortletPreferences.getPreferences(preferencesMap)\n          instance_id=\"footer_navigation_menu\"\n        />\n      </div>\n    </div>\n  </nav>\n    <div class=\"contact-info-container text-center pt-5 pb-2 col-12 col-md-4 mx-auto mb-4\">\n      <img alt=\"lunar-resort-logo\" height=\"90\" class=\"mb-2\" src=\"${images_folder}/lunar-resort-logo-vertical.png\" />\n      <div id=\"contactTextWrapper\" class=\"row mx-0\">\n        <p class=\"col-12 col-md-6\">\n          123 Mare Nectaris Lane<br>\n          Mare Nectaris, Moon Colony 10010<br>\n        </p>\n        <p class=\"col-12 col-md-6\">\n          Tel: 4-919-843-6666<br>\n          Fax: 4-919-843-6667<br>\n          <a href=\"mailto:info@lunarresort.com\">info@thelunarresort.com</a>\n        </p>\n      </div>\n    </div>\n  </div>\n  <p class=\"powered-by text-center text-white py-3 mb-0\">\n    The Lunar Resort-<@liferay.language key=\"powered-by\" /> <a href=\"http://www.liferay.com\" rel=\"external\">Liferay</a>\n  </p>\n</footer>"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/src/templates/init_custom.ftl",
    "content": "<#assign site_logo_height = 56 />\n<#assign header_css_class = \"navbar navbar-expand-md navbar-dark flex-column flex-md-row bd-navbar\" />\n<#assign logo_css_class = logo_css_class + \" navbar-brand\" />\n\n\n<#assign\n\tshow_footer = getterUtil.getBoolean(themeDisplay.getThemeSetting(\"show-footer\"))\n/>"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/src/templates/navigation.ftl",
    "content": "<div class=\"mx-1 mx-sm-3 order-md-1 lunar-user\">\n\t<@liferay.user_personal_bar />\n</div>\n\n<div class=\"collapse navbar-collapse\" id=\"lunarNav\">\n<nav class=\"${nav_css_class}\" id=\"navigation\" role=\"navigation\">\n\t<h1 class=\"hide-accessible\"><@liferay.language key=\"navigation\" /></h1>\n\n\t<ul aria-label=\"<@liferay.language key=\"site-pages\" />\" class=\"navbar-nav mr-auto\" role=\"menubar\">\n\t\t<#list nav_items as nav_item>\n\t\t\t<#assign\n\t\t\t\tnav_item_attr_has_popup = \"\"\n\t\t\t\tnav_item_css_class = \"nav-item\"\n\t\t\t\tnav_item_layout = nav_item.getLayout()\n\t\t\t\tnav_item_caret = \"\"\n\t\t\t/>\n\n\t\t\t<#if nav_item.isSelected()>\n\t\t\t\t<#assign\n\t\t\t\t\tnav_item_attr_has_popup = \"aria-haspopup='true'\"\n\t\t\t\t\tnav_item_css_class = \"${nav_item_css_class} selected\"\n\t\t\t\t/>\n\t\t\t</#if>\n\t\t\t\n\t\t\t<#if nav_item.hasChildren()>\n\t\t\t\t<#assign\n\t\t\t\t\tnav_item_css_class = \"${nav_item_css_class} dropdown\"\n\t\t\t\t\tnav_item_caret = '<svg class=\"lexicon-icon\"><use xlink:href=\"${images_folder}/lexicon/icons.svg#caret-bottom\" /></svg>'\n\t\t\t\t/>\n\t\t\t</#if>\n\n\t\t\t<li class=\"${nav_item_css_class}\" id=\"layout_${nav_item.getLayoutId()}\" role=\"presentation\">\n\t\t\t\t<a aria-labelledby=\"layout_${nav_item.getLayoutId()}\" class=\"nav-link\" ${nav_item_attr_has_popup} href=\"${nav_item.getURL()}\" ${nav_item.getTarget()} role=\"menuitem\"><span><@liferay_theme[\"layout-icon\"] layout=nav_item_layout /> ${nav_item.getName()}</span> ${nav_item_caret}</a>\n\n\t\t\t\t<#if nav_item.hasChildren()>\n\t\t\t\t\n\t\t\t\t\t<ul class=\"child-menu dropdown-menu\" role=\"menu\">\n\t\t\t\t\t\t<#list nav_item.getChildren() as nav_child>\n\t\t\t\t\t\t\t<#assign\n\t\t\t\t\t\t\t\tnav_child_css_class = \"nav-item\"\n\t\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t\t<#if nav_item.isSelected()>\n\t\t\t\t\t\t\t\t<#assign\n\t\t\t\t\t\t\t\t\tnav_child_css_class = \"nav-item selected\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</#if>\n\n\t\t\t\t\t\t\t<li class=\"${nav_child_css_class}\" id=\"layout_${nav_child.getLayoutId()}\" role=\"presentation\">\n\t\t\t\t\t\t\t\t<a aria-labelledby=\"layout_${nav_child.getLayoutId()}\" class=\"nav-link\" href=\"${nav_child.getURL()}\" ${nav_child.getTarget()} role=\"menuitem\">${nav_child.getName()}</a>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t</#list>\n\t\t\t\t\t</ul>\n\t\t\t\t</#if>\n\t\t\t</li>\n\t\t</#list>\n\t</ul>\n</nav>\n\n<a aria-controls=\"book-now\" class=\"btn text-white btn-orange order-md-2\">\n\t<p class=\"book-now-text mb-0\">Book Now</p>\n</a>\n</div>"
  },
  {
    "path": "en/developer/tutorials/code/lunar-resort-theme/lunar-resort-theme-complete/src/templates/portal_normal.ftl",
    "content": "<!DOCTYPE html>\n\n<#include init />\n\n<html class=\"${root_css_class}\" dir=\"<@liferay.language key=\"lang.dir\" />\" lang=\"${w3c_language_id}\">\n\n<head>\n\t<title>${the_title} - ${company_name}</title>\n\n\t<meta content=\"initial-scale=1.0, width=device-width\" name=\"viewport\" />\n\n\t<@liferay_util[\"include\"] page=top_head_include />\n</head>\n\n<body class=\"${css_class}\">\n\n<@liferay_ui[\"quick-access\"] contentId=\"#main-content\" />\n\n<@liferay_util[\"include\"] page=body_top_include />\n\n<@liferay.control_menu />\n\n<header class=\"${header_css_class}\">\n\t<div class=\"container-fluid\" id=\"banner\" role=\"banner\">\n\t\t<a class=\"${logo_css_class} navbar-brand\" href=\"${site_default_url}\" title=\"<@liferay.language_format arguments=\"${site_name}\" key=\"go-to-x\" />\">\n\t\t\t<img alt=\"${logo_description}\" height=\"56\" src=\"${site_logo}\" />\n\t\t\t<#if show_site_name>\n\t\t\t\t${site_name}\n\t\t\t</#if>\n\t\t</a>\n\n\t\t<#if has_navigation>\n\t\t\t<button aria-controls=\"navigation\" aria-expanded=\"false\" class=\"btn-monospaced ml-auto navbar-toggler\" data-target=\"#lunarNav\" data-toggle=\"collapse\" type=\"button\">\n\t\t\t\t<span class=\"navbar-toggler-icon\"></span>\n\t\t\t</button>\n\t\t\t<#include \"${full_templates_path}/navigation.ftl\" />\n\t\t</#if>\n\t</div>\n</header>\n\n<div class=\"container-fluid mt-0 pt-0 px-0\" id=\"wrapper\">\n\t<section id=\"content\">\n\t\t<h1 class=\"hide-accessible\">${the_title}</h1>\n\n\t\t<#if selectable>\n\t\t\t<@liferay_util[\"include\"] page=content_include />\n\t\t<#else>\n\t\t\t${portletDisplay.recycle()}\n\n\t\t\t${portletDisplay.setTitle(the_title)}\n\n\t\t\t<@liferay_theme[\"wrap-portlet\"] page=\"portlet.ftl\">\n\t\t\t\t<@liferay_util[\"include\"] page=content_include />\n\t\t\t</@>\n\t\t</#if>\n\t</section>\n\n\t<#if show_footer>\n\t\t<#include \"${full_templates_path}/footer.ftl\" />\n\t</#if>\n</div>\n\n<@liferay_util[\"include\"] page=body_bottom_include />\n\n<@liferay_util[\"include\"] page=bottom_include />\n\n</body>\n\n</html>"
  },
  {
    "path": "en/developer/tutorials/drawings/README.markdown",
    "content": "Use this directory to hold any miscellaneous files, such as graphical drawings,\nused in making the articles or images.\n"
  },
  {
    "path": "en/developer/tutorials/drawings/guestbook.xmi",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<XMI xmi.version=\"1.2\" verified=\"false\" timestamp=\"2019-08-27T17:33:53\" xmlns:UML=\"http://schema.omg.org/spec/UML/1.4\">\n <XMI.header>\n  <XMI.documentation>\n   <XMI.exporter>umbrello uml modeller http://umbrello.kde.org</XMI.exporter>\n   <XMI.exporterVersion>1.6.16</XMI.exporterVersion>\n   <XMI.exporterEncoding>UnicodeUTF8</XMI.exporterEncoding>\n  </XMI.documentation>\n  <XMI.metamodel xmi.version=\"1.4\" href=\"UML.xml\" xmi.name=\"UML\"/>\n </XMI.header>\n <XMI.content>\n  <UML:Model isRoot=\"false\" xmi.id=\"m1\" isAbstract=\"false\" name=\"UML Model\" isSpecification=\"false\" isLeaf=\"false\">\n   <UML:Namespace.ownedElement>\n    <UML:Stereotype isRoot=\"false\" xmi.id=\"folder\" visibility=\"public\" isAbstract=\"false\" name=\"folder\" namespace=\"m1\" isSpecification=\"false\" isLeaf=\"false\"/>\n    <UML:Stereotype isRoot=\"false\" xmi.id=\"interface\" visibility=\"public\" isAbstract=\"false\" name=\"interface\" namespace=\"m1\" isSpecification=\"false\" isLeaf=\"false\"/>\n    <UML:Model isRoot=\"false\" xmi.id=\"Logical_View\" visibility=\"public\" isAbstract=\"false\" name=\"Logical View\" namespace=\"m1\" isSpecification=\"false\" isLeaf=\"false\">\n     <UML:Namespace.ownedElement>\n      <UML:Package isRoot=\"false\" xmi.id=\"Datatypes\" visibility=\"public\" isAbstract=\"false\" stereotype=\"folder\" name=\"Datatypes\" namespace=\"Logical_View\" isSpecification=\"false\" isLeaf=\"false\">\n       <UML:Namespace.ownedElement>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uFxxkpTHChKiJ\" visibility=\"public\" isAbstract=\"false\" name=\"char\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"ufeZrdgdIImTs\" visibility=\"public\" isAbstract=\"false\" name=\"int\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uMqpcflospVhz\" visibility=\"public\" isAbstract=\"false\" name=\"float\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"ukkq8tRtTY75P\" visibility=\"public\" isAbstract=\"false\" name=\"double\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uaB8RGWK3cdfG\" visibility=\"public\" isAbstract=\"false\" name=\"bool\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"umBnnAZHifTby\" visibility=\"public\" isAbstract=\"false\" name=\"string\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uQ9ewF219o66n\" visibility=\"public\" isAbstract=\"false\" name=\"unsigned char\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"ufpOKsSTLaXxt\" visibility=\"public\" isAbstract=\"false\" name=\"signed char\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uGl73Z6K2yCWv\" visibility=\"public\" isAbstract=\"false\" name=\"unsigned int\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"ubKowDNTQQR9g\" visibility=\"public\" isAbstract=\"false\" name=\"signed int\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"u7mMnMZuapvmX\" visibility=\"public\" isAbstract=\"false\" name=\"short int\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"u4uXiqlkY8h0U\" visibility=\"public\" isAbstract=\"false\" name=\"unsigned short int\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uhEtAcAF3BKTh\" visibility=\"public\" isAbstract=\"false\" name=\"signed short int\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"u1q6D2001lkiB\" visibility=\"public\" isAbstract=\"false\" name=\"long int\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uD8wHK1TPhWOW\" visibility=\"public\" isAbstract=\"false\" name=\"signed long int\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uTJTyl7jK0GCk\" visibility=\"public\" isAbstract=\"false\" name=\"unsigned long int\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uOT5Fpdaa0ME6\" visibility=\"public\" isAbstract=\"false\" name=\"long double\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"u9QHhHY6pNByq\" visibility=\"public\" isAbstract=\"false\" name=\"wchar_t\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"ufSMWKnE2MeDR\" visibility=\"public\" isAbstract=\"false\" name=\"boolean\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"umHcdFGxhpF4Z\" visibility=\"public\" isAbstract=\"false\" name=\"byte\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"udzAML1UMSHuq\" visibility=\"public\" isAbstract=\"false\" name=\"short\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"ucltgX28DoJai\" visibility=\"public\" isAbstract=\"false\" name=\"long\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uGIdZtfD3RmpK\" visibility=\"public\" isAbstract=\"false\" name=\"String\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uBnaeiYgAyDqM\" visibility=\"public\" isAbstract=\"false\" name=\"Integer\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"u6tk8SyJrCt37\" visibility=\"public\" isAbstract=\"false\" name=\"Character\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uZ2yr2TrjD3GA\" visibility=\"public\" isAbstract=\"false\" name=\"Boolean\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uos4nyGFPvZuP\" visibility=\"public\" isAbstract=\"false\" name=\"Float\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"u0gPojjJf15k8\" visibility=\"public\" isAbstract=\"false\" name=\"Double\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uRZzAHIPLCRuA\" visibility=\"public\" isAbstract=\"false\" name=\"Byte\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"u7z6tAmOM7F6e\" visibility=\"public\" isAbstract=\"false\" name=\"Short\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"ui0ufk0T6rdfJ\" visibility=\"public\" isAbstract=\"false\" name=\"Long\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uVWjZZ48orN7M\" visibility=\"public\" isAbstract=\"false\" name=\"StringBuffer\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uNc9xdgi1iMoa\" visibility=\"public\" isAbstract=\"false\" name=\"StringBuilder\" namespace=\"Datatypes\" isSpecification=\"false\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"u9Da8cRjcrXIz\" visibility=\"public\" isAbstract=\"false\" name=\"Object[][]\" namespace=\"Datatypes\" isSpecification=\"false\" elementReference=\"uDcsaFusy8Y5S\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uSN2mGc2l4oHg\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookEntrySoap[]\" namespace=\"Datatypes\" isSpecification=\"false\" elementReference=\"uHSp7X0pfGrxG\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uKOpKcOEyWLVb\" visibility=\"public\" isAbstract=\"false\" name=\"Class&lt;?>\" namespace=\"Datatypes\" isSpecification=\"false\" elementReference=\"uij0CviuYyYdw\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uFxlNnNgI1t9U\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookSoap[]\" namespace=\"Datatypes\" isSpecification=\"false\" elementReference=\"uYrth6QTT97i2\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uyD4Gl3q9XWmk\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookEntry[]\" namespace=\"Datatypes\" isSpecification=\"false\" elementReference=\"u4d1ZoMYg5mnY\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uMKvVysVu0Dw8\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookEntry[][]\" namespace=\"Datatypes\" isSpecification=\"false\" elementReference=\"u4d1ZoMYg5mnY\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"ufbCUnIy8X4Ut\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookEntrySoap[][]\" namespace=\"Datatypes\" isSpecification=\"false\" elementReference=\"uHSp7X0pfGrxG\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uCtN9ns0qBrY3\" visibility=\"public\" isAbstract=\"false\" name=\"Guestbook[]\" namespace=\"Datatypes\" isSpecification=\"false\" elementReference=\"u5oLyKAa3lM7D\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uLFEAknyUWMcb\" visibility=\"public\" isAbstract=\"false\" name=\"Guestbook[][]\" namespace=\"Datatypes\" isSpecification=\"false\" elementReference=\"u5oLyKAa3lM7D\" isLeaf=\"false\"/>\n        <UML:DataType isRoot=\"false\" xmi.id=\"uIpQxAQ8TJkfg\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookSoap[][]\" namespace=\"Datatypes\" isSpecification=\"false\" elementReference=\"uYrth6QTT97i2\" isLeaf=\"false\"/>\n       </UML:Namespace.ownedElement>\n      </UML:Package>\n      <UML:Package isRoot=\"false\" xmi.id=\"ueCOHn4k55Hul\" visibility=\"public\" isAbstract=\"false\" name=\"com\" namespace=\"Logical_View\" isSpecification=\"false\" isLeaf=\"false\" comment=\"&#xa;Copyright (c) 2000-present Liferay, Inc. All rights reserved.&#xa;&#xa;This library is free software; you can redistribute it and/or modify it under&#xa;the terms of the GNU Lesser General Public License as published by the Free&#xa;Software Foundation; either version 2.1 of the License, or (at your option)&#xa;any later version.&#xa;&#xa;This library is distributed in the hope that it will be useful, but WITHOUT&#xa;ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS&#xa;FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more&#xa;details.&#xa;\">\n       <UML:Namespace.ownedElement>\n        <UML:Package isRoot=\"false\" xmi.id=\"u2heAwPLbeXRO\" visibility=\"public\" isAbstract=\"false\" name=\"liferay\" namespace=\"ueCOHn4k55Hul\" isSpecification=\"false\" isLeaf=\"false\" comment=\"&#xa;Copyright (c) 2000-present Liferay, Inc. All rights reserved.&#xa;&#xa;This library is free software; you can redistribute it and/or modify it under&#xa;the terms of the GNU Lesser General Public License as published by the Free&#xa;Software Foundation; either version 2.1 of the License, or (at your option)&#xa;any later version.&#xa;&#xa;This library is distributed in the hope that it will be useful, but WITHOUT&#xa;ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS&#xa;FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more&#xa;details.&#xa;\">\n         <UML:Namespace.ownedElement>\n          <UML:Package isRoot=\"false\" xmi.id=\"uy5Hu1NT2E04j\" visibility=\"public\" isAbstract=\"false\" name=\"docs\" namespace=\"u2heAwPLbeXRO\" isSpecification=\"false\" isLeaf=\"false\" comment=\"&#xa;Copyright (c) 2000-present Liferay, Inc. All rights reserved.&#xa;&#xa;This library is free software; you can redistribute it and/or modify it under&#xa;the terms of the GNU Lesser General Public License as published by the Free&#xa;Software Foundation; either version 2.1 of the License, or (at your option)&#xa;any later version.&#xa;&#xa;This library is distributed in the hope that it will be useful, but WITHOUT&#xa;ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS&#xa;FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more&#xa;details.&#xa;\">\n           <UML:Namespace.ownedElement>\n            <UML:Package isRoot=\"false\" xmi.id=\"usSXUPnpdKWie\" visibility=\"public\" isAbstract=\"false\" name=\"guestbook\" namespace=\"uy5Hu1NT2E04j\" isSpecification=\"false\" isLeaf=\"false\" comment=\"&#xa;Copyright (c) 2000-present Liferay, Inc. All rights reserved.&#xa;&#xa;This library is free software; you can redistribute it and/or modify it under&#xa;the terms of the GNU Lesser General Public License as published by the Free&#xa;Software Foundation; either version 2.1 of the License, or (at your option)&#xa;any later version.&#xa;&#xa;This library is distributed in the hope that it will be useful, but WITHOUT&#xa;ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS&#xa;FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more&#xa;details.&#xa;\">\n             <UML:Namespace.ownedElement>\n              <UML:Package isRoot=\"false\" xmi.id=\"uuJOr4utQZVql\" visibility=\"public\" isAbstract=\"false\" name=\"model\" namespace=\"usSXUPnpdKWie\" isSpecification=\"false\" isLeaf=\"false\" comment=\"&#xa;Copyright (c) 2000-present Liferay, Inc. All rights reserved.&#xa;&#xa;This library is free software; you can redistribute it and/or modify it under&#xa;the terms of the GNU Lesser General Public License as published by the Free&#xa;Software Foundation; either version 2.1 of the License, or (at your option)&#xa;any later version.&#xa;&#xa;This library is distributed in the hope that it will be useful, but WITHOUT&#xa;ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS&#xa;FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more&#xa;details.&#xa;\">\n               <UML:Namespace.ownedElement>\n                <UML:Package isRoot=\"false\" xmi.id=\"uKJUfYfiEJfLg\" visibility=\"public\" isAbstract=\"false\" name=\"impl\" namespace=\"uuJOr4utQZVql\" isSpecification=\"false\" isLeaf=\"false\" comment=\"&#xa;Copyright (c) 2000-present Liferay, Inc. All rights reserved.&#xa;&#xa;This library is free software; you can redistribute it and/or modify it under&#xa;the terms of the GNU Lesser General Public License as published by the Free&#xa;Software Foundation; either version 2.1 of the License, or (at your option)&#xa;any later version.&#xa;&#xa;This library is distributed in the hope that it will be useful, but WITHOUT&#xa;ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS&#xa;FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more&#xa;details.&#xa;\">\n                 <UML:Namespace.ownedElement>\n                  <UML:Class isRoot=\"false\" xmi.id=\"utY1Nb3lrucXh\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookCacheModel\" namespace=\"uKJUfYfiEJfLg\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:GeneralizableElement.generalization>\n                    <UML:Generalization xmi.idref=\"ulFm2Z0JDGAsZ\"/>\n                   </UML:GeneralizableElement.generalization>\n                   <UML:Classifier.feature>\n                    <UML:Attribute xmi.id=\"uHCW53dkh4ZWn\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"uuid\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ulrcmQVxsrzyS\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"guestbookId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uuW27aoUuzToz\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"name\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uFabxOaxr8Gua\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"groupId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uN3u5hjIkS55S\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"companyId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uLROzf8oemYNm\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"userId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uls2gR5omKY1v\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"userName\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uhDpSdQEdtQMc\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"createDate\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u0A2b6YLV3t8n\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"modifiedDate\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uMozkKcfWhYgf\" visibility=\"public\" type=\"ufeZrdgdIImTs\" name=\"status\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u5qshFd30HoEN\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"statusByUserId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uYduCIVUOeHWh\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserName\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ukAr7j9IwSj1L\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"statusDate\" isSpecification=\"false\"/>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uZ9cj5Z20xfec\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"equals\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uQXpPYRIE0kK4\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                      <UML:Parameter xmi.id=\"u8gHA1lPt8A0h\" visibility=\"private\" value=\"\" type=\"uDcsaFusy8Y5S\" name=\"obj\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uCpAALPkCyHre\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"hashCode\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"upGyWLRGmvPBF\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uV0XJf7lOYbjl\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toString\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uH94qYzFPT3bC\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uwHYWxC9Xb09D\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toEntityModel\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ugScRyrKES7g5\" type=\"u5oLyKAa3lM7D\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uMjSqW0r1IgIA\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"readExternal\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uQRxTu4NNe83r\" visibility=\"private\" value=\"\" type=\"un1gm5DfBo30C\" name=\"objectInput\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ubjZscuiwpAIZ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"writeExternal\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uLWB8fN4hW8rz\" visibility=\"private\" value=\"\" type=\"uWQamAW1ki1ON\" name=\"objectOutput\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                   </UML:Classifier.feature>\n                  </UML:Class>\n                  <UML:Class isRoot=\"false\" xmi.id=\"umW4lWTF8ioUr\" visibility=\"public\" isAbstract=\"true\" name=\"GuestbookEntryBaseImpl\" namespace=\"uKJUfYfiEJfLg\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:GeneralizableElement.generalization>\n                    <UML:Generalization xmi.idref=\"uCZMWklRg5SPU\"/>\n                   </UML:GeneralizableElement.generalization>\n                   <UML:Classifier.feature>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uxfiYotRJAcRB\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"persist\" isSpecification=\"false\" isLeaf=\"false\"/>\n                   </UML:Classifier.feature>\n                  </UML:Class>\n                  <UML:Class isRoot=\"false\" xmi.id=\"uIQBIzAd0DdG0\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookEntryCacheModel\" namespace=\"uKJUfYfiEJfLg\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:GeneralizableElement.generalization>\n                    <UML:Generalization xmi.idref=\"upgj8ltsNi2uo\"/>\n                   </UML:GeneralizableElement.generalization>\n                   <UML:Classifier.feature>\n                    <UML:Attribute xmi.id=\"uQWMBvZilghk8\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"uuid\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ufjwjyR6QK2Xp\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"entryId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u0DDB4973HR2Q\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"name\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u372XSfM3DPOk\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"email\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u9ruqMYkdhypw\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"message\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"unb9OCs36BAvs\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"guestbookId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"usKAqZ6CqtVT8\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"groupId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uDnf2nLi3HhRr\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"companyId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ukCYOxyxmxbK4\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"userId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uXt8vCGRytTKV\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"userName\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ulN9OtzBWfuQL\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"createDate\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"umLojKoxpylG9\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"modifiedDate\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uiSzQQDjlHH0M\" visibility=\"public\" type=\"ufeZrdgdIImTs\" name=\"status\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uGud2ea5cyFZU\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"statusByUserId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uMoRocM7o8sWf\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserName\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uYt2JLkTwWSbv\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"statusDate\" isSpecification=\"false\"/>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uxljtnh0VvYvc\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"equals\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uyZ7W8t8KjM6H\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                      <UML:Parameter xmi.id=\"uU1gW5i16xmJN\" visibility=\"private\" value=\"\" type=\"uDcsaFusy8Y5S\" name=\"obj\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uuzvIn3MKIoRu\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"hashCode\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uVUlHGwpJTUI9\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u8KXyBNoLH6to\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toString\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uTQKRTVopQ6GE\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uszLzyomNI3Ix\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toEntityModel\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uLATltGz5ZXh4\" type=\"u4d1ZoMYg5mnY\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u4fy35L0Rz3FG\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"readExternal\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uGV4o4EXys1x3\" visibility=\"private\" value=\"\" type=\"un1gm5DfBo30C\" name=\"objectInput\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ul7oekPFEdo2X\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"writeExternal\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uhQ5HfcZcfGJJ\" visibility=\"private\" value=\"\" type=\"uWQamAW1ki1ON\" name=\"objectOutput\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                   </UML:Classifier.feature>\n                  </UML:Class>\n                  <UML:Class isRoot=\"false\" xmi.id=\"uhTHyHZ7wFmcr\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookEntryImpl\" namespace=\"uKJUfYfiEJfLg\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:GeneralizableElement.generalization>\n                    <UML:Generalization xmi.idref=\"usaRtS0vwSBSu\"/>\n                   </UML:GeneralizableElement.generalization>\n                   <UML:Classifier.feature>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uhQZ5r2D1dsY5\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"GuestbookEntryImpl\" isSpecification=\"false\" isLeaf=\"false\"/>\n                   </UML:Classifier.feature>\n                  </UML:Class>\n                  <UML:Class isRoot=\"false\" xmi.id=\"ud7IXARPBKg6E\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookEntryModelImpl\" namespace=\"uKJUfYfiEJfLg\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:GeneralizableElement.generalization>\n                    <UML:Generalization xmi.idref=\"u0SEKUtCnZdN0\"/>\n                   </UML:GeneralizableElement.generalization>\n                   <UML:Classifier.feature>\n                    <UML:Attribute xmi.id=\"u9XygGrLjhXtj\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"TABLE_NAME\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u1JEcHsN8UBp5\" visibility=\"public\" type=\"u9Da8cRjcrXIz\" name=\"TABLE_COLUMNS\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uIapkeSys7y4o\" visibility=\"public\" type=\"uQyeogSV0dP7h\" name=\"TABLE_COLUMNS_MAP\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uYE5YOuI73akj\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"TABLE_SQL_CREATE\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uGklXPHwwR6BL\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"TABLE_SQL_DROP\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uichEa6QSnaq9\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"ORDER_BY_JPQL\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uTRMTCUurSqFy\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"ORDER_BY_SQL\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uCgjnwYP2KB9G\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"DATA_SOURCE\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uzklsBwDwnSuw\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"SESSION_FACTORY\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"urfASmgyj0cPN\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"TX_MANAGER\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u71nfMJuDXm4N\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"COMPANYID_COLUMN_BITMASK\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ufFQFqN9y3d9D\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"GROUPID_COLUMN_BITMASK\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ukp0qQZBJl0q0\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"GUESTBOOKID_COLUMN_BITMASK\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uRJQm0fHM02KP\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"UUID_COLUMN_BITMASK\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uoQSZj23DHdfC\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"CREATEDATE_COLUMN_BITMASK\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uNKMcQ27rRdD7\" visibility=\"private\" type=\"uQyeogSV0dP7h\" name=\"_attributeGetterFunctions\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uVfEZMCmXB3lS\" visibility=\"private\" type=\"uQyeogSV0dP7h\" name=\"_attributeSetterBiConsumers\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uhlzDQ184ZF8H\" visibility=\"private\" type=\"uam7nyPRrHTSg\" name=\"_escapedModelProxyProviderFunction\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uXQiBXBBbcF3V\" visibility=\"private\" type=\"ufSMWKnE2MeDR\" name=\"_entityCacheEnabled\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uWzIbBPgQKBhz\" visibility=\"private\" type=\"ufSMWKnE2MeDR\" name=\"_finderCacheEnabled\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uV5GA32s7un6z\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_uuid\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uyG65AME1ol7z\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_originalUuid\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uqqU8tbusr8Hu\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_entryId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uY2VoCF9TQc2w\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_name\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"upvE3P9WNErgU\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_email\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uMIddwifgMgIn\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_message\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uSFVhx6FA8O9T\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_guestbookId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uWXuNBen5Jcyw\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_originalGuestbookId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ufGGNgSyihQrd\" visibility=\"private\" type=\"ufSMWKnE2MeDR\" name=\"_setOriginalGuestbookId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uhm8UA1sfJYeY\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_groupId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uHrGqAyfP80W0\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_originalGroupId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u316bDZgGpqiR\" visibility=\"private\" type=\"ufSMWKnE2MeDR\" name=\"_setOriginalGroupId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u0ORkEPbvheKe\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_companyId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ueqjiFdGMF9vo\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_originalCompanyId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uiBcuvTN8VQH1\" visibility=\"private\" type=\"ufSMWKnE2MeDR\" name=\"_setOriginalCompanyId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uHRJMdUIlkDpX\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_userId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u2L0ulQcr8LWf\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_userName\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uzcG9bJotxvj6\" visibility=\"private\" type=\"u8C1qOpoOXZK5\" name=\"_createDate\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uR9uInTB2nAJB\" visibility=\"private\" type=\"u8C1qOpoOXZK5\" name=\"_modifiedDate\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uBXGH1gJvcQ5E\" visibility=\"private\" type=\"ufSMWKnE2MeDR\" name=\"_setModifiedDate\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ukprpE22zyCyC\" visibility=\"private\" type=\"ufeZrdgdIImTs\" name=\"_status\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"utxkGLQZz8MYd\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_statusByUserId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u4SVAhhAGfLMs\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_statusByUserName\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uVCOmdcGOSlF7\" visibility=\"private\" type=\"u8C1qOpoOXZK5\" name=\"_statusDate\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"usFzEkIWrB6Nf\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_columnBitmask\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uVxBcvogx0gg6\" visibility=\"private\" type=\"u4d1ZoMYg5mnY\" name=\"_escapedModel\" isSpecification=\"false\"/>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uZjnqsKKgA0qg\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setEntityCacheEnabled\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uur3Ovx5GZlWb\" visibility=\"private\" value=\"\" type=\"ufSMWKnE2MeDR\" name=\"entityCacheEnabled\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uIeq6BozeOUNK\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setFinderCacheEnabled\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uZRqgJA0AthyJ\" visibility=\"private\" value=\"\" type=\"ufSMWKnE2MeDR\" name=\"finderCacheEnabled\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ub940XoQ95vZ2\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toModel\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uPpGwCdxCsKt4\" type=\"u4d1ZoMYg5mnY\" kind=\"return\"/>\n                      <UML:Parameter xmi.id=\"uNsVH1OWgRyjs\" visibility=\"private\" value=\"\" type=\"uHSp7X0pfGrxG\" name=\"soapModel\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uTdZsHdKi8HFX\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toModels\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"umASNMcynPMZE\" type=\"uzKRVm8e0nLQP\" kind=\"return\"/>\n                      <UML:Parameter xmi.id=\"uNzwwwR1l14uJ\" visibility=\"private\" value=\"\" type=\"uSN2mGc2l4oHg\" name=\"soapModels\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uweUEqPJ24zE4\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"GuestbookEntryModelImpl\" isSpecification=\"false\" isLeaf=\"false\"/>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uSiaZGUguiDJ2\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uveRMbqP9f8PN\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uMI5kd21vJNUM\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u2qDA90xR4Fmv\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"primaryKey\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ubj5MaICY1Tra\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getPrimaryKeyObj\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u2j4QvIY5Asc4\" type=\"ueqGp2XxhfCAm\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"upEiuirfZeKlG\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setPrimaryKeyObj\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uiEMdTnO049JB\" visibility=\"private\" value=\"\" type=\"ueqGp2XxhfCAm\" name=\"primaryKeyObj\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uAa4BmbjtuReW\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModelClass\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uqfIYxu3wANjt\" type=\"uKOpKcOEyWLVb\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u6gOrKS5J832W\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModelClassName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uJ4dExvvZvebu\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ufwXM3C1aJE1D\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModelAttributes\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"unDO0MMTv9hnB\" type=\"uQyeogSV0dP7h\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uCKIkUumVrEsy\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setModelAttributes\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u8buejTnpBIzp\" visibility=\"private\" value=\"\" type=\"uQyeogSV0dP7h\" name=\"attributes\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uQJcwM9jie1rE\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getAttributeGetterFunctions\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uPD91vEVdHavz\" type=\"uQyeogSV0dP7h\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uAND9PlmH5kog\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getAttributeSetterBiConsumers\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"usEcnNx2jj5cm\" type=\"uQyeogSV0dP7h\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uscnU6mJxUu8d\" isVirtual=\"false\" isInline=\"false\" visibility=\"private\" isAbstract=\"false\" isOverride=\"false\" name=\"_getProxyProviderFunction\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uKL4KZTws01Hs\" type=\"uam7nyPRrHTSg\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uun7QqqWt3Na7\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ucJuUw06T6Qcl\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ueUX2YvEopMHY\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uppdB596OnPoS\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"uuid\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u7JUcdIEJnhNB\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getOriginalUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uMMrZdcr8mRB5\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uZxT7K5bYwGuN\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getEntryId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u05fVkpHPb8Bl\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u3KFxF2mtaag0\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setEntryId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uhkUMtU3oaIEQ\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"entryId\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u2QKvF1qO93Ix\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ufMZSfkN2QXot\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uuxuOD0Hm3K8p\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uxOBAAIMQqMz6\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"name\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ugwrtHQGXcW4V\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getEmail\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u51BeLIQMlg6X\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uoomw6B5FzPb7\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setEmail\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uONB2AXnJGn6r\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"email\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u4o6oKAeSESIi\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getMessage\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"usRJL6Mo35Fr3\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uWyMdBbsWWoUZ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setMessage\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ucAtiHX75HKkT\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"message\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uBLZ9H3QRmQz0\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ukp6BjM1UQDlI\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uJfxvTnlgobps\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uBx1TtK9g17i2\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"guestbookId\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ub4pwUhnv1W7y\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getOriginalGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uOJwrZAbDYR12\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uGCA55VPAfw1Y\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uVhcmEdQ3tKQh\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"udh6dF52f8bq9\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u4mn4lhUTxCzm\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"groupId\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ubN3mhuTI6C3O\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getOriginalGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ufug26BF6a8jB\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uURFKCbIxEM0z\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u1AdikKBDNEMq\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uwopsY95wK8UN\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u9U8Xhtz84eK1\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"companyId\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ufEq5WGpUgqeO\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getOriginalCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uIm64jEweosDa\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uvtlN9XDYccjR\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u5dOhyhpojxWF\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uao4JTZzh4uQo\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"upqd9a25fVyhX\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"userId\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u7PBbfluy0dmC\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uQeLjstjymzxh\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u0uq62hsEKj17\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uPsZMm1hhspnx\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userUuid\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uD0bIIJhJuJ1j\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uGfja9W6CyfhE\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u4CmgCuSO0ecZ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uiQR22H4jc1j5\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userName\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uRGnUXqZquX5C\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uZnN1TcRiXtGT\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uQWka85nDEt6p\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uFp6M6iijHx90\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"createDate\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uWBC38a8Kk7d9\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uhWteOlrJRJ79\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u9MNLdBFvvw2p\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"hasSetModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uQtnIEamu6hvu\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"unv7McW4wnjPf\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u91fCtU7xQp8T\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"modifiedDate\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uYCqIFCmTobGB\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uK1cs3n1U4wg9\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uWSxT7mCxFfaZ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uyodVbtMjU6ia\" visibility=\"private\" value=\"\" type=\"ufeZrdgdIImTs\" name=\"status\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uqnCOi5uoJpEJ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uVVeeBQvgBEUx\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ua5sjdPnUjC2c\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uvROFuhQPWW3Z\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"statusByUserId\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ukpvQGF7KfxPM\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ugT6E8Or1xmb6\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uLG0XyjuFL39u\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"usQoIELVhM9dY\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserUuid\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uG273yXvudhrh\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uIHWbpFYafDOY\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u7UAIkZ6iR3dy\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uTsSQuMmwXvgM\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserName\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uL0vscF2jUnoc\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uf8OXEpLP1uJV\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uh89Epmm9Gnyq\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uzGrTwM6JUbq1\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"statusDate\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uyHxW2p61LHhm\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStagedModelType\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uDL4cz4wK2epZ\" type=\"uAx06XlkZTNOk\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uPVArl6nCmg0q\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isApproved\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uHlUa7QiRlL7N\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uuQpY6AZbflDx\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isDenied\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uQTfSWSYtbPyE\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uQNujTVljHIVt\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isDraft\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uh0iWvAzOItbf\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uMCiS3hvzU0WX\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isExpired\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uZhYkMvUaq5p4\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"unI4zJy10qW5A\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isInactive\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uuj4cWcIeNJ2D\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u5m6PjIQFG4vY\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isIncomplete\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ueEvxaVYi1rK8\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u3JrJgcHJXZ39\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isPending\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uAiaojovapt9V\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uXzmkYvUplJko\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isScheduled\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"usZeL458sthKs\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"udykvZ9f2Nvy7\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getColumnBitmask\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uusEk3C3Wj5Q5\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uDQfkwpY7saAO\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getExpandoBridge\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u9GZCHMZ6MUnK\" type=\"uOy5vwfVBULFm\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uQLoNoXpyr79C\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setExpandoBridgeAttributes\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uuJiVkmajeoBz\" visibility=\"private\" value=\"\" type=\"u6iXBajIxWLgp\" name=\"serviceContext\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u3G3D6JndIQvZ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toEscapedModel\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ufiXp2DmXGKLg\" type=\"u4d1ZoMYg5mnY\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uhXIIfKGbZMpt\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"clone\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uyMa5f9i6fSBJ\" type=\"uDcsaFusy8Y5S\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u2CkA2sdwQgUH\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"compareTo\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uBZM6KDb6XYX9\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                      <UML:Parameter xmi.id=\"uYgFfLLIg5Klo\" visibility=\"private\" value=\"\" type=\"u4d1ZoMYg5mnY\" name=\"guestbookEntry\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"usgyzYTYA3WUF\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"equals\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u9EcCEBZV6fnh\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                      <UML:Parameter xmi.id=\"uW1xY5jmeb2Gq\" visibility=\"private\" value=\"\" type=\"uDcsaFusy8Y5S\" name=\"obj\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ush4qzFaw3GFh\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"hashCode\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uHQOML5boO83W\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uopPelSrk1uQo\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isEntityCacheEnabled\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uXBnKN2PJvV3T\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uSzIi12UCrbJx\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isFinderCacheEnabled\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uVU0bfqXPBNYA\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ujeW7gLuYKVgI\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"resetOriginalValues\" isSpecification=\"false\" isLeaf=\"false\"/>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uq9l4YCWUc6gx\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toCacheModel\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uBjhIuLyIvge4\" type=\"uzWCo8LEDwhWm\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u5uh9hy6dROAo\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toString\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u18kXwMcC6eSV\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uOxjaWrW5bPPV\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toXmlString\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ucqlbFg6pLuHn\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                   </UML:Classifier.feature>\n                  </UML:Class>\n                  <UML:Class isRoot=\"false\" xmi.id=\"uRG8jivcgsDBf\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookImpl\" namespace=\"uKJUfYfiEJfLg\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:GeneralizableElement.generalization>\n                    <UML:Generalization xmi.idref=\"uKxJLwKpIVIuu\"/>\n                   </UML:GeneralizableElement.generalization>\n                   <UML:Classifier.feature>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uhuvD8o1t5IsB\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"GuestbookImpl\" isSpecification=\"false\" isLeaf=\"false\"/>\n                   </UML:Classifier.feature>\n                  </UML:Class>\n                  <UML:Class isRoot=\"false\" xmi.id=\"uRvrJuuRl1rob\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookModelImpl\" namespace=\"uKJUfYfiEJfLg\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:GeneralizableElement.generalization>\n                    <UML:Generalization xmi.idref=\"ukDqUiAtriCzr\"/>\n                   </UML:GeneralizableElement.generalization>\n                   <UML:Classifier.feature>\n                    <UML:Attribute xmi.id=\"uXXmUEtOKBdR8\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"TABLE_NAME\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uz5oLNnJqasqu\" visibility=\"public\" type=\"u9Da8cRjcrXIz\" name=\"TABLE_COLUMNS\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ueQ1G50h3YqPx\" visibility=\"public\" type=\"uQyeogSV0dP7h\" name=\"TABLE_COLUMNS_MAP\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uq3VB2L6nFGnV\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"TABLE_SQL_CREATE\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uPnM8cJMO3rHh\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"TABLE_SQL_DROP\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ums4RZPJ03HQY\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"ORDER_BY_JPQL\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uRvWoIifVaCd3\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"ORDER_BY_SQL\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"usmi2swsHd2aK\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"DATA_SOURCE\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uGuHfntnF4Cgy\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"SESSION_FACTORY\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uwcyycdGSzGP7\" visibility=\"public\" type=\"uGIdZtfD3RmpK\" name=\"TX_MANAGER\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u3bfqXon87cEH\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"COMPANYID_COLUMN_BITMASK\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u5Was8PDiCPdr\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"GROUPID_COLUMN_BITMASK\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"unEZCsjeLkaNx\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"UUID_COLUMN_BITMASK\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"utvrbLTEQdMkV\" visibility=\"public\" type=\"ucltgX28DoJai\" name=\"GUESTBOOKID_COLUMN_BITMASK\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uJ2voT02rU8Va\" visibility=\"private\" type=\"uQyeogSV0dP7h\" name=\"_attributeGetterFunctions\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uzPWou6SUcYg1\" visibility=\"private\" type=\"uQyeogSV0dP7h\" name=\"_attributeSetterBiConsumers\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uMQRkplZGioMm\" visibility=\"private\" type=\"uam7nyPRrHTSg\" name=\"_escapedModelProxyProviderFunction\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uMRsvx4GLZyj8\" visibility=\"private\" type=\"ufSMWKnE2MeDR\" name=\"_entityCacheEnabled\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uixnwRKcH6KJW\" visibility=\"private\" type=\"ufSMWKnE2MeDR\" name=\"_finderCacheEnabled\" ownerScope=\"classifier\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uxQ3p1NHKuws9\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_uuid\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ujna3npI3okFZ\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_originalUuid\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u0azlFuk7WY5z\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_guestbookId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ujM5owoVCU6IZ\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_name\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ultCfhDl5VXK6\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_groupId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u3rO1DRagevJZ\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_originalGroupId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u4ujRnkupnFlj\" visibility=\"private\" type=\"ufSMWKnE2MeDR\" name=\"_setOriginalGroupId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uTxURbLnb1vR3\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_companyId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uKFhFZsy11ZXb\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_originalCompanyId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uMRllmNsRVG1o\" visibility=\"private\" type=\"ufSMWKnE2MeDR\" name=\"_setOriginalCompanyId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"ufvz0vIGTa2nj\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_userId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u5DzNiNw527Ya\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_userName\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uqSmI82p5wjva\" visibility=\"private\" type=\"u8C1qOpoOXZK5\" name=\"_createDate\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u8x2yExbx7BSl\" visibility=\"private\" type=\"u8C1qOpoOXZK5\" name=\"_modifiedDate\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uHpZwLtVC2L1W\" visibility=\"private\" type=\"ufSMWKnE2MeDR\" name=\"_setModifiedDate\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uDVcWyvWkqwYW\" visibility=\"private\" type=\"ufeZrdgdIImTs\" name=\"_status\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uB7IK200eobfI\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_statusByUserId\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"unrzu1eFC5Pxo\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_statusByUserName\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"utKWF5gmv8PL4\" visibility=\"private\" type=\"u8C1qOpoOXZK5\" name=\"_statusDate\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"u7NI5VUHgiL7r\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_columnBitmask\" isSpecification=\"false\"/>\n                    <UML:Attribute xmi.id=\"uFEXnvnJ5pAqx\" visibility=\"private\" type=\"u5oLyKAa3lM7D\" name=\"_escapedModel\" isSpecification=\"false\"/>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ur7Q4ucdP2Xwp\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setEntityCacheEnabled\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uoUaizkRqdFB9\" visibility=\"private\" value=\"\" type=\"ufSMWKnE2MeDR\" name=\"entityCacheEnabled\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u35WxwgnANhhg\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setFinderCacheEnabled\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uHT8Eh5y8m4bq\" visibility=\"private\" value=\"\" type=\"ufSMWKnE2MeDR\" name=\"finderCacheEnabled\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u0l02Mziy2K17\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toModel\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uqo1Rhgb2rNOW\" type=\"u5oLyKAa3lM7D\" kind=\"return\"/>\n                      <UML:Parameter xmi.id=\"uvq2HAlZx4XBQ\" visibility=\"private\" value=\"\" type=\"uYrth6QTT97i2\" name=\"soapModel\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uOdO7DUWspEKx\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toModels\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u6g5Dy1rRvfOI\" type=\"uzKRVm8e0nLQP\" kind=\"return\"/>\n                      <UML:Parameter xmi.id=\"ujawqa3LbdDNV\" visibility=\"private\" value=\"\" type=\"uFxlNnNgI1t9U\" name=\"soapModels\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"utvgGKXIv30tP\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"GuestbookModelImpl\" isSpecification=\"false\" isLeaf=\"false\"/>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uzcjVfbzVZabS\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u8uu671FgmEIK\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ubuoahmQkkeac\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ufcILyuhQqcLB\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"primaryKey\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ujj52ulemYwnW\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getPrimaryKeyObj\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"umBszTLpjkTg8\" type=\"ueqGp2XxhfCAm\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u1ciPO4DzWZRH\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setPrimaryKeyObj\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"utgopZSjdKcGo\" visibility=\"private\" value=\"\" type=\"ueqGp2XxhfCAm\" name=\"primaryKeyObj\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uKpmG6N75tGU1\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModelClass\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u5HntejI1nQSw\" type=\"uKOpKcOEyWLVb\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uAINWWcPlZLUP\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModelClassName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uA6FNXWEtuqei\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ujkk2A5km2ZBH\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModelAttributes\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u2rAVLPSQghUA\" type=\"uQyeogSV0dP7h\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uWmcCzHYPBUdp\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setModelAttributes\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u2jHCJtzMOUVm\" visibility=\"private\" value=\"\" type=\"uQyeogSV0dP7h\" name=\"attributes\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"urn6A5FySwSJ2\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getAttributeGetterFunctions\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uWDtnuu4B2LO8\" type=\"uQyeogSV0dP7h\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"upiFyRkYsQsQb\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getAttributeSetterBiConsumers\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"urxhXX19Xhyvf\" type=\"uQyeogSV0dP7h\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uqa1781FB5biX\" isVirtual=\"false\" isInline=\"false\" visibility=\"private\" isAbstract=\"false\" isOverride=\"false\" name=\"_getProxyProviderFunction\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uNR2zhceAaci5\" type=\"uam7nyPRrHTSg\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uxx3QtH4eb4Jz\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uCCUozrieWwfw\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uBVmtOQBoMDUI\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uMINeaz5RWcP0\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"uuid\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uDD9UxsSEsUAN\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getOriginalUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uamv1SjOhlmoF\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ur7yYa3ELe8cj\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uxpeUsNFX4FYy\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uBir8XZxWtdbd\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uwhsnaY9tL0RE\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"guestbookId\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"umuNHDleDfSzY\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uzYJRT2NBVrdo\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uHKggbnN0OfyF\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uxHGeP9DfgAMl\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"name\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uRko7r3z6F3HM\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uVovHLoaWTZsx\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uV8moWWsdXwuf\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uXUKRuVmdnw02\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"groupId\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uHyibBVbzA2CA\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getOriginalGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uZnPZugOiXg7X\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uM9EBAaIRspme\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uWZkX9SJZyBeU\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u3G9pHjMeXEXp\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uGi6FrKmiBjQ6\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"companyId\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"us7dOpCFz5lYV\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getOriginalCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uJD3IDoD9VDwu\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uAzctSHC99q8h\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uFmAwXjNWr5BE\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"usLw07cCIw68Z\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uErr9VHWXpz4h\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"userId\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uvYXnhJyZDE1T\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uPaPcVTgKv9Qj\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"unLZkgdrNd9Y8\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uunhQ13cU6cIw\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userUuid\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uplilHsQqPQgm\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uem6mU2RkWv0F\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"urtS8bYgCepiX\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uigJIEif4LfOO\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userName\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u4y5Kb5RIO2nc\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uLgJTL5jAK7e1\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u3sIXfXYKB71O\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ufOMRLMbkvgMK\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"createDate\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u2rmPpkpH7tBc\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uw2W3alB61nhB\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ujHi30b57PFzY\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"hasSetModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uVK4EmPrDWdKH\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uyMrM0ODi2AFA\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u70p42kYj9HmK\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"modifiedDate\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u3L2OrZTOhlBL\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uzONu19Eo2oCE\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uLcsCsa4FkWvh\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uJFluRbgUPA4S\" visibility=\"private\" value=\"\" type=\"ufeZrdgdIImTs\" name=\"status\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u5EIGGulNQuA2\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uOVwNAcFO8qT4\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uT4sbjKUWzKqU\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ua3r5EkVWIUIu\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"statusByUserId\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u2E6KmMuoV9hn\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u3afbcgGJeiuq\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uYmPEcwL1NJ8E\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uY4hMckVn4v3i\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserUuid\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"upRQOoQl7qMKE\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uJdzTGKUBSln4\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uBf0fqug7Rv8E\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uR3JfIXQKm85x\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserName\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uCjpyFjhI1tnA\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u214PGyzf6maW\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u3P5vqBcxs7pg\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ukgWX8z6FngfX\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"statusDate\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uu5xHJ1QMGGSV\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStagedModelType\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uIJULnnJwdRiQ\" type=\"uAx06XlkZTNOk\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uQibHjhXba3Ax\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isApproved\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uPoxoyt8CK5pp\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uDPVTCt6sDNJt\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isDenied\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uLfjC66x2eiEJ\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"um5y7IzYmnv3H\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isDraft\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uh7GqWHTM9Ahi\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"umA4Jtd3wlL4C\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isExpired\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uQes5LXKUptjy\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uOFcfQycgiLIj\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isInactive\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"upoX6SR3CrxIX\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"unjoOwoH4rWH6\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isIncomplete\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"un1RQBQvAYpi4\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uuLSZpKbIxeHe\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isPending\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u41XFMGuP6Nfg\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uOv5kntY91n7W\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isScheduled\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uNcoGQhEAZinX\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u4RGhQYFm0di3\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getColumnBitmask\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uB4b1uYCvy2Sp\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"urb5Gr96yVPOd\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getExpandoBridge\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"ucBWm1V7HG22G\" type=\"uOy5vwfVBULFm\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uMIbhKa0VLaIC\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setExpandoBridgeAttributes\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uw2E31P5OnO5s\" visibility=\"private\" value=\"\" type=\"u6iXBajIxWLgp\" name=\"serviceContext\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uWq52Fq5AyR1Y\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toEscapedModel\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"usB1HKvX4TItG\" type=\"u5oLyKAa3lM7D\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uIe82aahonVcq\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"clone\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uC4C4TL03BJUx\" type=\"uDcsaFusy8Y5S\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uXJtOAqLsqtoB\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"compareTo\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u6YBRhih8f2pR\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                      <UML:Parameter xmi.id=\"uCzFabuHqKq1Q\" visibility=\"private\" value=\"\" type=\"u5oLyKAa3lM7D\" name=\"guestbook\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"usCPjuogb7zd0\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"equals\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uYsWMOv85hYwn\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                      <UML:Parameter xmi.id=\"uoY0mSR33Bpm1\" visibility=\"private\" value=\"\" type=\"uDcsaFusy8Y5S\" name=\"obj\" isSpecification=\"false\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uHrfbR5SlxrP7\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"hashCode\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uoptVRQCP0l9A\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uKpRoQyxwA5x1\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isEntityCacheEnabled\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uNbUVcQFYmF92\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u2oZAqiQsYwqq\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isFinderCacheEnabled\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uojpUdqv2kMWP\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uoStfzWq36o14\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"resetOriginalValues\" isSpecification=\"false\" isLeaf=\"false\"/>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uP6XFCHnNvt5y\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toCacheModel\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"uul48tKck8SJy\" type=\"uzWCo8LEDwhWm\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uPM2ELubqWfqF\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toString\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"u7anf6BgjbscM\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                    <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"unvvMBFz3ZGho\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toXmlString\" isSpecification=\"false\" isLeaf=\"false\">\n                     <UML:BehavioralFeature.parameter>\n                      <UML:Parameter xmi.id=\"urwwEliO2z9hD\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                     </UML:BehavioralFeature.parameter>\n                    </UML:Operation>\n                   </UML:Classifier.feature>\n                  </UML:Class>\n                 </UML:Namespace.ownedElement>\n                </UML:Package>\n                <UML:Interface isRoot=\"false\" xmi.id=\"u5oLyKAa3lM7D\" visibility=\"public\" isAbstract=\"false\" stereotype=\"interface\" name=\"Guestbook\" namespace=\"uuJOr4utQZVql\" isSpecification=\"false\" isLeaf=\"false\"/>\n                <UML:Interface isRoot=\"false\" xmi.id=\"u4d1ZoMYg5mnY\" visibility=\"public\" isAbstract=\"false\" stereotype=\"interface\" name=\"GuestbookEntry\" namespace=\"uuJOr4utQZVql\" isSpecification=\"false\" isLeaf=\"false\"/>\n                <UML:Class isRoot=\"false\" xmi.id=\"uHSp7X0pfGrxG\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookEntrySoap\" namespace=\"uuJOr4utQZVql\" isSpecification=\"false\" isLeaf=\"false\">\n                 <UML:GeneralizableElement.generalization>\n                  <UML:Generalization xmi.idref=\"uds9Dy2nEADqO\"/>\n                 </UML:GeneralizableElement.generalization>\n                 <UML:Classifier.feature>\n                  <UML:Attribute xmi.id=\"uwEblEYEsZ92o\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_uuid\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uh60pl6lnN9we\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_entryId\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"u9s1xgr5fmwiN\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_name\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uY9XYdE87cEN6\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_email\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"ugTLQb6jqETuv\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_message\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uuCBG57CzjznO\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_guestbookId\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uR86J4HgddRIb\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_groupId\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uXpO7zP9zJka4\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_companyId\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uLvcgGt4jjOhZ\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_userId\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uRfNOkWUugWSB\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_userName\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uZvfmRXM46tJa\" visibility=\"private\" type=\"u8C1qOpoOXZK5\" name=\"_createDate\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"u9W7xy9IX4D0A\" visibility=\"private\" type=\"u8C1qOpoOXZK5\" name=\"_modifiedDate\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uQdCtpMK1hbsm\" visibility=\"private\" type=\"ufeZrdgdIImTs\" name=\"_status\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"ufiQ6TdEITgCO\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_statusByUserId\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uYikZZEqOVubn\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_statusByUserName\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"u7DXeG3miZjeE\" visibility=\"private\" type=\"u8C1qOpoOXZK5\" name=\"_statusDate\" isSpecification=\"false\"/>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ucMWDIptph9PQ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toSoapModel\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uQA8NFuFrbDDo\" type=\"uHSp7X0pfGrxG\" kind=\"return\"/>\n                    <UML:Parameter xmi.id=\"urKuHUM9IuLj6\" visibility=\"private\" value=\"\" type=\"u4d1ZoMYg5mnY\" name=\"model\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uBPmue7qAIZVv\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toSoapModels\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ubU7daUjoqvID\" type=\"uSN2mGc2l4oHg\" kind=\"return\"/>\n                    <UML:Parameter xmi.id=\"uhiMnoPiAewVr\" visibility=\"private\" value=\"\" type=\"uyD4Gl3q9XWmk\" name=\"models\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uGZVq59rttNLb\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toSoapModels\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uGiBmNWBRLOnB\" type=\"ufbCUnIy8X4Ut\" kind=\"return\"/>\n                    <UML:Parameter xmi.id=\"unkj7NeT5xOSz\" visibility=\"private\" value=\"\" type=\"uMKvVysVu0Dw8\" name=\"models\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uy1k6HOgsgSBr\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toSoapModels\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uEQMlHqKKmRdA\" type=\"uSN2mGc2l4oHg\" kind=\"return\"/>\n                    <UML:Parameter xmi.id=\"ulGZqqVciWxBE\" visibility=\"private\" value=\"\" type=\"uzKRVm8e0nLQP\" name=\"models\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uV7EkZOsRQMza\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"GuestbookEntrySoap\" isSpecification=\"false\" isLeaf=\"false\"/>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uWBoto66x0qau\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u7hsKBUmbHfdU\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uuDS0nKhS5oxo\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"utbnbH0i6s05n\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"pk\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u9YMtIyov3xiP\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uOeoAmNLUKExg\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u4dAgSRqyrMF6\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uJfVQwsQa4f0a\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"uuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"umKjPT1UAA7rJ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getEntryId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uBM4ZO55uupwd\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ukydB3xeHz0DQ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setEntryId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u2acj2H4lhH5s\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"entryId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u8NSNPGStUufJ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u0BmVZcAiTOGZ\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uuKNw1D0fmzVg\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uzNYqvRvix44f\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"name\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"urEq7YdajFL37\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getEmail\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uC90DaIdmH6Pf\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u8LUehTxh4YDK\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setEmail\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uzrjqrGrm7uKG\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"email\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"untSrcn67BHSQ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getMessage\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uWyaG75oLkETa\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ue8sQnVEF2B4i\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setMessage\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uggMKQGTfY4lE\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"message\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u8atb0BelpliU\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u2ESYe387KLCB\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uEEKItKkUnyMO\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uveb4t6n1ZjER\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"guestbookId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uOCt5yXC1zrmD\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uiddel2Q2cO34\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"upZJB6FF7WWpn\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uWxeDlq2pBVto\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"groupId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u0O7FS3OPWbky\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uMzWa5wfFTwrJ\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"usLBlkTznozaz\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uTWJbhV4ImsZh\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"companyId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u11nQisWRe3ys\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u5plnsAsAYohp\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uQqjY7nrlA3Uo\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uKTYq4KfqesHZ\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"userId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uKgcIgYuf25Q3\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uN269AWRMiRuv\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uIIbEHonxTp1a\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u6A56t8Y1psRH\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userName\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uChcrk8upmBWW\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uW5pBSS4q1bdp\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uqBfimqL7CeHW\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uwaf5TXtNU2wW\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"createDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ushePeE1jdRSq\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uGnTHOd1588yF\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uuScwS8lADE0b\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ufVWXDU1USrXZ\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"modifiedDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ugjlBQVKK4MQg\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uwBdW9GoTSPO6\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uoDspfGJXw2Ul\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"utMTmdotvvLzC\" visibility=\"private\" value=\"\" type=\"ufeZrdgdIImTs\" name=\"status\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uhFe7sa3AAe4I\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u8ax4NAOf42T7\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"usfhJBuuDueiX\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uUY1PXqZURBt3\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"statusByUserId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uP5pKa3gntmKm\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uYu3TlZTnMAll\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uv3hNie4bl1dJ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uGg1MKrbvsxRT\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserName\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uLArs98XAkNHS\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uSBn2x2s8Lekj\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uAED53CLSBptp\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uyGcXGAEjWFAl\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"statusDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                 </UML:Classifier.feature>\n                </UML:Class>\n                <UML:Class isRoot=\"false\" xmi.id=\"uYrth6QTT97i2\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookSoap\" namespace=\"uuJOr4utQZVql\" isSpecification=\"false\" isLeaf=\"false\">\n                 <UML:GeneralizableElement.generalization>\n                  <UML:Generalization xmi.idref=\"u5r9FvNzb8SBr\"/>\n                 </UML:GeneralizableElement.generalization>\n                 <UML:Classifier.feature>\n                  <UML:Attribute xmi.id=\"uJwkTAggGemO5\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_uuid\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uTTgP2O95jqS2\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_guestbookId\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"u1TRjZwCvwMWX\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_name\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"ub7DlmfdRVUbN\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_groupId\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uiCVr2jENkbvw\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_companyId\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uZOCh299YAtpQ\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_userId\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uuP8s0fJWyNSl\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_userName\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"ue8zwHXpL05Ip\" visibility=\"private\" type=\"u8C1qOpoOXZK5\" name=\"_createDate\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uzk6uPtGvRiPC\" visibility=\"private\" type=\"u8C1qOpoOXZK5\" name=\"_modifiedDate\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uQlIuJAo2PAh7\" visibility=\"private\" type=\"ufeZrdgdIImTs\" name=\"_status\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uKAzDUwfvSUA6\" visibility=\"private\" type=\"ucltgX28DoJai\" name=\"_statusByUserId\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"udOYR52wjsTCw\" visibility=\"private\" type=\"uGIdZtfD3RmpK\" name=\"_statusByUserName\" isSpecification=\"false\"/>\n                  <UML:Attribute xmi.id=\"uBGdtKsKXePCk\" visibility=\"private\" type=\"u8C1qOpoOXZK5\" name=\"_statusDate\" isSpecification=\"false\"/>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"utjb6whE01QG1\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toSoapModel\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"urfE4uAlE31cx\" type=\"uYrth6QTT97i2\" kind=\"return\"/>\n                    <UML:Parameter xmi.id=\"uott1AeHb1ahT\" visibility=\"private\" value=\"\" type=\"u5oLyKAa3lM7D\" name=\"model\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uKdZAqADDk5fD\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toSoapModels\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uF4S9kqknQ8jG\" type=\"uFxlNnNgI1t9U\" kind=\"return\"/>\n                    <UML:Parameter xmi.id=\"unwdva5bGBGHe\" visibility=\"private\" value=\"\" type=\"uCtN9ns0qBrY3\" name=\"models\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ugaknFw8dJmFD\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toSoapModels\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uWhWN2pFX0vm9\" type=\"uIpQxAQ8TJkfg\" kind=\"return\"/>\n                    <UML:Parameter xmi.id=\"u5yDsrJchSUal\" visibility=\"private\" value=\"\" type=\"uLFEAknyUWMcb\" name=\"models\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"umED6ajFOuB11\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"toSoapModels\" ownerScope=\"classifier\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uO95xn9zlomvg\" type=\"uFxlNnNgI1t9U\" kind=\"return\"/>\n                    <UML:Parameter xmi.id=\"uMx3HxWxojPM8\" visibility=\"private\" value=\"\" type=\"uzKRVm8e0nLQP\" name=\"models\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ub8KRdh13Kl3h\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"GuestbookSoap\" isSpecification=\"false\" isLeaf=\"false\"/>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uwQBcuR2LkM5a\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uCVa8eW2xUIW9\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uAN226SDw1cfa\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u0VHrXvZZBjpb\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"pk\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uDsYIJKOewnwc\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uOfTLkOfFHEJS\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"udQoBUcab9n2R\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u3K1nRnU1YKKf\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"uuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uxaYsf1qlQcsU\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u61RTz9WrOo1a\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uF5v9jko2bK48\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uSZjG30z02fad\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"guestbookId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uUys2yrF5lrqz\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ua6NYg4l5ia4l\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uY8UnGdWURxw0\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uu5eTjI1Gej1p\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"name\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u9WrJ5HgEHQw6\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uTd3rMp1AuVoR\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uqLakIwcgA8bq\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uVc6h49IK1NY4\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"groupId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uYjOzfdnOPvE9\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uZP639CPsDqkp\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uZNIZd2BjmfG6\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u7Hgvvkli8Xfa\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"companyId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u0qyRpkeuixO3\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ubJhDUbYdKcQs\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uW4WLGRU5z4nq\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uMKcpAA7JycKf\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"userId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uRizALQJY3to7\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"usqD8FOZaOXme\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uhxYzySWnAI2A\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uXy5RTUnKXcYP\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userName\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ujaL9xs1oVonQ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ufu4M2p5A4Bkz\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uLqzkFr5ht7bS\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ulqiQOEBTkxhM\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"createDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uLVxcF6b5oP5Q\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uIMEgO7NBdLaI\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"upMFqhihgi0Xz\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uCMaCFSYCUNEp\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"modifiedDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"umAjIafirBnku\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u3y6ymwKaUDnd\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uskcOGs0S2fKU\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u9vqM3cbzdEPa\" visibility=\"private\" value=\"\" type=\"ufeZrdgdIImTs\" name=\"status\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uzvnecMXX3xHw\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"upa9u7h9kHz5Z\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"utJjh76qoTW8T\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uJ72STrIGpPea\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"statusByUserId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uXP0gD1gwIUvI\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u415WyQKcQOkY\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uTd9uz6JsfEYd\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uCKaFSXpcJgtb\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserName\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ubgpSaLRZiHWO\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uk1l8qFU0KqxU\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uqDQrqKUzo17I\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uMvxMpOdzKDJw\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"statusDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                 </UML:Classifier.feature>\n                </UML:Class>\n                <UML:Interface isRoot=\"false\" xmi.id=\"uoLHmwTStXfLg\" visibility=\"public\" isAbstract=\"false\" stereotype=\"interface\" name=\"GuestbookEntryModel\" namespace=\"uuJOr4utQZVql\" isSpecification=\"false\" isLeaf=\"false\">\n                 <UML:Classifier.feature>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uU4tuKJuvVvuZ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"umeMCqRm543iM\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uoWIYqMHe8dEa\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ui9OFd9k7kUUN\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"primaryKey\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uVDI1FZlRr5O7\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u5GvHNAiWwNrs\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u0koFCLbu8lKC\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uw2XhvSAhtrVi\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"uuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"us6rQ2YRs1T6J\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getEntryId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uJSXmA7EGuOA5\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u4EnNuhTmWAuN\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setEntryId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u86RGEyYgyBMF\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"entryId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uq8yGEVlvtFhx\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uwR6YD5DgjvQU\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ugEkrUvshQyMr\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u2xTxCIBhRWJ5\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"name\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ut5Hm8A46EBGk\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getEmail\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uG5e7j5LvbsG6\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u831WOBVYsAx0\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setEmail\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uddxCoUwXVtho\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"email\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uPcxluoSPNYU1\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getMessage\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uC54ilA44TWqy\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uAoEPpTxpmJHu\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setMessage\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uFGq5DDe3uJ7i\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"message\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u5jXyAGFoPZsN\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ucHU5go57biUm\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uA9vmoTO8J2ff\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uS9lipk4sNcsW\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"guestbookId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ux6SnPN4W3IGR\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uCJaHnQ8UPG4p\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uRTi9lSp8Ocds\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"utVdDz2m9hoWV\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"groupId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uvV2hgXLjHMaP\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uEZOHtN96tGkl\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uLHua2IhHHw7s\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uqogUkZustWTU\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"companyId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"usmcNaVcv3VWu\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uzuEh3B3Hc4Ku\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uPyEaYpGRLtyp\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u2mtMuW5B0JpI\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"userId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uRnLIQQdgv7l1\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u8liil2FHcnrB\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uqQDAXkoxaaHp\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uc0mtBIh3FuON\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userUuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uQMwQnaqnHmN1\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uKIkGO9ShcatA\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uBhHpxF4IVjQQ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uuzCzBYTEZBew\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userName\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ueVJ7UD9NcBCl\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"urvs9vVxy93PQ\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ubh4FQAhhPlXx\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"unN9swbKZrZms\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"createDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uTIepu0yKofJw\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uO3kYPcSULUKw\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uqFpjxv0McIqj\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uLJKnOpTWuoLe\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"modifiedDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uaBNz3x750CAy\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uoLvoQWHJtTsO\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uXwZlRkcfvauv\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uk9dD45CgSxUj\" visibility=\"private\" value=\"\" type=\"ufeZrdgdIImTs\" name=\"status\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u4KILkLX88mFu\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uGatslZNUeX7B\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u1Y4trpeKIY8v\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uGZTLkGhTItyQ\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"statusByUserId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u67dTwqOmD8zw\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uWPlonkrbQUgo\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uoYcuA48LlNt2\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"unXhJ5VBE7enH\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserUuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uzI16OWmmOaDo\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"usQOYFDfTfX9u\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ukqT7muDybSIM\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uAexkUZnqGhOw\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserName\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uaL5xWxgmDiGK\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uJB1O5jNWAT1b\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uP5pCWVbsIMGk\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uqxv1Rc0bCMwk\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"statusDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uECpqB8kTbe3f\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isApproved\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uzbxiXI6EKJxX\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uB88lsVtODscu\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isDenied\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uxsJKdlFomArF\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uOPwipt32RqJY\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isDraft\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uaxgYGFksZPt5\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u8OSKuKuDfYFk\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isExpired\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uipc85VCYrFdY\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uThLUiZxMwtEt\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isInactive\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uRT2EfYawyUPW\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"utoC23wXOl9F2\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isIncomplete\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uw2JDOc2YNCqy\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uoujMFmDjIk6g\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isPending\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uyhjraHe80BAo\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uOvpco0w6vZp9\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isScheduled\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"unf2QxTosjxBe\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                 </UML:Classifier.feature>\n                </UML:Interface>\n                <UML:Class isRoot=\"false\" xmi.id=\"ugH8NB1k52llt\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookEntryWrapper\" namespace=\"uuJOr4utQZVql\" isSpecification=\"false\" isLeaf=\"false\">\n                 <UML:GeneralizableElement.generalization>\n                  <UML:Generalization xmi.idref=\"uLKIZ2DLrH4SQ\"/>\n                 </UML:GeneralizableElement.generalization>\n                 <UML:Classifier.feature>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u8O6hIJxUREos\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"GuestbookEntryWrapper\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uiMe8F4noJ1EK\" visibility=\"private\" value=\"\" type=\"u4d1ZoMYg5mnY\" name=\"guestbookEntry\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uJzPgtudgkqKt\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModelAttributes\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"um5qZCCfUtQUL\" type=\"uQyeogSV0dP7h\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uQcX8xmqyNvQe\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setModelAttributes\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u2lejkJboyPDt\" visibility=\"private\" value=\"\" type=\"uQyeogSV0dP7h\" name=\"attributes\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uIUQswGB0gDB2\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u0w5yL2q5Wfmu\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uQgr92MVhIBz4\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uOTx96Wercwsx\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uacJ9M5xMPPDJ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getEmail\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uMAe9X8LUu2Sm\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u6UFrE7PoydN1\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getEntryId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uqLAsVjvPzx3H\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uvOsOFUdQwMjo\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uIdvjwMsUbtfv\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uoNmhT5zdktL6\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uA33at5PSuZSo\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uRDRXndr1dKCM\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getMessage\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uEKMNt9mQ2J7q\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uhaIW9fgXHSHy\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uG4j473jiaeIn\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uY0uBOxMulLXN\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"unEtM55TAY5JH\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uNBh9OW6U51y9\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uZ71bAqR18L5X\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u2K5SQWOXpVei\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uzYCMSzhDKcx2\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"usdmFcyF3GrbH\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uZ12H0aPGhi21\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uCS78owZPKFzo\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"utCYKGBOfWT3n\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uFzinyBVOp5Cc\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u9s5YUKhvybot\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uIHtRpzFynifO\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u9GBFBOPJ6eGo\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u5KCy56i0Zm4k\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u6kYBMT3tKXqb\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uBBjUo05gH9Jv\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uCiqPG82VMbsU\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uXyjrSjaPI4rN\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"urdojtNLD3zLU\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uwwdsTJZZ4yii\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uK3SA0uIJlEPZ\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"umvOdESqRtguY\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isApproved\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"upHnb415E7QF7\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uJgU24ScT7gxU\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isDenied\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"udaIcLCaKpmXa\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uXBKh1Wn1x5vo\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isDraft\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uln7RVuk4JysP\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uVBmuBaZwTI90\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isExpired\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"udewS1WrG65DI\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uGZYmM6rkpmnO\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isInactive\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"us2FynubzwrfP\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u5Ge45zuWJJLY\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isIncomplete\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u1TnChP0l1TKr\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u7od8yTFFfcg1\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isPending\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uNIRdGRDbe7yg\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uHmjDfIDIK2DZ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isScheduled\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ufmtDjFZsEgph\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uaYd3wDHu2oqd\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"persist\" isSpecification=\"false\" isLeaf=\"false\"/>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uEWU7Ub3v2P84\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uywLJWVuGg6A4\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"companyId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uaE1uuw79JPWy\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uC1Nh47yMe3SF\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"createDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"utcDVpJSCL9sz\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setEmail\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u8P2ZiqS78Vkj\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"email\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uBPoaxd5nZX3u\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setEntryId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u4K5CEt3r5RZc\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"entryId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uau7qDH8ChRan\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uqjXoV1U23vyr\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"groupId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u2x2YvaNNF6fV\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ujzfa28tYz9Xz\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"guestbookId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"urrENJFbm5miE\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setMessage\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u1BaYCXrvE7S5\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"message\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uNptH8NKIOq2C\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uOgFDECTPvXhV\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"modifiedDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uCnZF0njRpONP\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"urhghZu3cf3hg\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"name\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uB8d5A0RmoZEp\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ulKqyKlj9pI8X\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"primaryKey\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ur9xSkzrLUTZb\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uh3Ly22P39vSW\" visibility=\"private\" value=\"\" type=\"ufeZrdgdIImTs\" name=\"status\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ucYt1rQJF8v6s\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uWeKNMwqEXUMJ\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"statusByUserId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u7RDAMHuSWoKT\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uCLDIqWtz5pM2\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserName\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ucS79rGp0cW5s\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uNHaUkAGk2ye1\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserUuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uZz2WrDgpb3pO\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uNiBoruqL7IGF\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"statusDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uXUcYBY2U3T0W\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uMgzCM9VcDHSW\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"userId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u4fTagZApKVSa\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uRsfAFZwISWcV\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userName\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uS00r8magyOdV\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u7gPzIfwIwPd5\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userUuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uv29FVPlBnWHi\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uy9jf1zIAixf6\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"uuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uMalGYsXFtfUC\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStagedModelType\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uztHCsy5TOZHr\" type=\"uAx06XlkZTNOk\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u9yMuIS6nseph\" isVirtual=\"false\" isInline=\"false\" visibility=\"protected\" isAbstract=\"false\" isOverride=\"false\" name=\"wrap\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uWCciJ5kVNDDd\" type=\"ugH8NB1k52llt\" kind=\"return\"/>\n                    <UML:Parameter xmi.id=\"uBATyZ0TMXB1Y\" visibility=\"private\" value=\"\" type=\"u4d1ZoMYg5mnY\" name=\"guestbookEntry\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                 </UML:Classifier.feature>\n                </UML:Class>\n                <UML:Interface isRoot=\"false\" xmi.id=\"uUDLwp3yF2eRR\" visibility=\"public\" isAbstract=\"false\" stereotype=\"interface\" name=\"GuestbookModel\" namespace=\"uuJOr4utQZVql\" isSpecification=\"false\" isLeaf=\"false\">\n                 <UML:Classifier.feature>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ub68XfxAagK9u\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ue4U0jlGpHOil\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uzbvgIlHBOx3c\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uv5PNEOQvUHxl\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"primaryKey\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uTc2dBIsBmuzy\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uJFV3IadQuuen\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uwa8Bu61A0tfv\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uZP665QAYCm5E\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"uuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uAvPYHPmxgNi9\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uXWE1aC0PzyCo\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uRcSEx0fpqvV8\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"utBQqfOHvjj0I\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"guestbookId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ucYLZshesecoT\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uY5j0z9LbA0l2\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uwN1EnbagGNkr\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u3xVK472MClDh\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"name\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uVT35TrPlyGfr\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uBJEuBRv8AA4y\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"unyqibPOSGOhG\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uVVMzXJyfFTYX\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"groupId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ukVwl4qu05CCa\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uPBSllzhivLVu\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ur163Ve9bUBCW\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u4b9BulD9U8Je\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"companyId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uknSSMsVu56Sj\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uMPeznxlkg0uL\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uDnjH3NAQFk44\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uc1UvWuYOKq8b\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"userId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u0RKbULUdq3dA\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u5rTMsxjBk12X\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uqciYHkKd7EBl\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u777thU5Xzkre\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userUuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uktwvlIoXecq3\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uZ0gdOWO1mQlB\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"udA58mRCNEVCE\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uE7v9U7oYMT4q\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userName\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uy6D0xva95Eod\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uUivVCCrzrMLD\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uDhZiO3YaZUeB\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uk1c6kpE0SH78\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"createDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u1r7cKdCnO11j\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uiLwWueoRi8zg\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uJndxTpNJw8rx\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ujRzfYbcrP1mA\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"modifiedDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uIkcW6qvM6jCI\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uueBZvTMkyLAT\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uDsaKpgOPNOzy\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uhIn1MhvXLPO0\" visibility=\"private\" value=\"\" type=\"ufeZrdgdIImTs\" name=\"status\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uCbSrooYSdktQ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uYMwqoDeX6Qdx\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uMJdcThxnpafn\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uZINuOZyqBMv2\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"statusByUserId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ubtG6tNxYROfI\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uosNlLTb4JWlC\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u0E3ueYJD3Ltg\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uOfN4KArQwIAh\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserUuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uSSV9Q1oO7kUf\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ujm5p3zly84Fq\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uZIwFm3WaY85N\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u930zE3841CSX\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserName\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u3fGVkd8t5KYd\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uczAF0crOg7Zp\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uIXeQ6LRMm1Yb\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uQRJoln8Ihjf5\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"statusDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uMZN1l9T7RRUw\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isApproved\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uWthav2CCVreN\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uYUqfUdCQc9aN\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isDenied\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uaZni5Zsp7iul\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u1bjlNdoBkFN9\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isDraft\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uMHtPsEHkYCHj\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uStrcwy9YCJGx\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isExpired\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"udl2LxF6F1u5B\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uwpdbWibYvJLO\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isInactive\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ureqyI5PtUHPS\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u0HoVpECn4xx7\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isIncomplete\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"utmtPe7alVvTZ\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u9GCw3hVS6P2W\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isPending\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ulMqbk1aEIcPU\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uNuIB9VXELS0N\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isScheduled\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uSgaXeYKBON0k\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                 </UML:Classifier.feature>\n                </UML:Interface>\n                <UML:Class isRoot=\"false\" xmi.id=\"uWrkLWhzLVhyE\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookWrapper\" namespace=\"uuJOr4utQZVql\" isSpecification=\"false\" isLeaf=\"false\">\n                 <UML:GeneralizableElement.generalization>\n                  <UML:Generalization xmi.idref=\"uBhCfCdodwUj7\"/>\n                 </UML:GeneralizableElement.generalization>\n                 <UML:Classifier.feature>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uBq1cYHKEKIq5\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"GuestbookWrapper\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"udBa8RxPNMJlv\" visibility=\"private\" value=\"\" type=\"u5oLyKAa3lM7D\" name=\"guestbook\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uKOTLK6AOapdw\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModelAttributes\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uwrTsRz7pv4U3\" type=\"uQyeogSV0dP7h\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uiwDtqmh1blVN\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setModelAttributes\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u6TDDTQEXYRq1\" visibility=\"private\" value=\"\" type=\"uQyeogSV0dP7h\" name=\"attributes\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uMZSR3brdhc5d\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uGxKbojKyyhz4\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uzMtroNZJY3yQ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uD5sGdUG11tWk\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uiWgao3Ov3l91\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uZ0djpK85RcMI\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uHydEBiaFygm6\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ugUF2oc3xIH7f\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ugAEPsKQkgSvb\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uzHKrm5gPwKWY\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ukBtpT3yUEhb2\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uZXEQ6SucXvKh\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uEvcrHb43WmxH\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uX3lIcd0T3bWM\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uC5BYIcBua9ky\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uOk3VDnYGQp5Y\" type=\"ufeZrdgdIImTs\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u8QeMKpvBv8vP\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uhGMh7DsmNh8S\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uZasotJj2H14V\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ujxebkFJkxDnr\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u7AwtAxm3Gruw\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusByUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uc7NOqo5QVY44\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uIA4RnmAg4fff\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"usG0hAPOFkfcu\" type=\"u8C1qOpoOXZK5\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uzlyoE5ywtsfz\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uMEAOCMwUVASe\" type=\"ucltgX28DoJai\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ug0uh95zdUGHZ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uUx7HDcuZqAdZ\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u7wBDpcQWlilG\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uvVmSn8dhRdwi\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uWPiwHNpSWETE\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uUxYdDzbG323i\" type=\"uGIdZtfD3RmpK\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ujYM87EByv0P7\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isApproved\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uxIMFR1y4h0hU\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uf9pcA72g57Ex\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isDenied\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uzczqk3wGIXaH\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u7FHra36Ep8rW\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isDraft\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uRxLWB9Gc9hns\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uq1kuZmWQ5bP6\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isExpired\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uj6dQo8eLXooM\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uL53MrzGepNZd\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isInactive\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uUBeK72Z2STAv\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uvAVPKRGuLj7H\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isIncomplete\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uFKbGCqPTeQ1d\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ugSW5Y1LC5VLI\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isPending\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uIAUlLuEQiKU1\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uuZ2FyWv5s9zh\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"isScheduled\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uClnYoNtI2fCu\" type=\"ufSMWKnE2MeDR\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uLgW4kAoOAPfc\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"persist\" isSpecification=\"false\" isLeaf=\"false\"/>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u4He0j66E7AfQ\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCompanyId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uLlfizx3O1OLz\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"companyId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"udlpI9UVzET4d\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setCreateDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uQ7irOkhuljyw\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"createDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uQECn9N2DMGm4\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGroupId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ukcnql63KpoOm\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"groupId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uBqO031flGu8s\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setGuestbookId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u3ltCKwXjdP4y\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"guestbookId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u6TDONgXolmJG\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setModifiedDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uXKisXZlr4KGV\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"modifiedDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uLlogwXI5QXf5\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"umzqSQ50iq75O\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"name\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uw8dHubSWh8mI\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setPrimaryKey\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uZmI5WVd4XoYc\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"primaryKey\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"utDyf2onVS8Wp\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatus\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uQa7N0k5yVmQD\" visibility=\"private\" value=\"\" type=\"ufeZrdgdIImTs\" name=\"status\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ux2yq2eLHEHUG\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uFlrmlp7UvxXS\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"statusByUserId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uJG4xViyzppgd\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"upI7YkUu7KRm1\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserName\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u71ZLZMbZrhFf\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusByUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uKUXsTgBTa58s\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"statusByUserUuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ux8Xx87uiCyNO\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setStatusDate\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uc04ViVynG6lW\" visibility=\"private\" value=\"\" type=\"u8C1qOpoOXZK5\" name=\"statusDate\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uit4kgC6yhCCG\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserId\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uOZ4QDBcxtmlr\" visibility=\"private\" value=\"\" type=\"ucltgX28DoJai\" name=\"userId\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uXxu2XNka7Rw1\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserName\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uJtyHjMrVrGgI\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userName\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ufBV9Yz7U15q6\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUserUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"u2gNN0xWA6Aei\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"userUuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"uolATiN9Spvte\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"setUuid\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"uJSjAuTLtWZP3\" visibility=\"private\" value=\"\" type=\"uGIdZtfD3RmpK\" name=\"uuid\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"ufIuIkJLQOL7r\" isVirtual=\"false\" isInline=\"false\" visibility=\"public\" isAbstract=\"false\" isOverride=\"false\" name=\"getStagedModelType\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ukVPeJcvxZxl7\" type=\"uAx06XlkZTNOk\" kind=\"return\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                  <UML:Operation isRoot=\"false\" isQuery=\"false\" xmi.id=\"u9MUDepuWCUbQ\" isVirtual=\"false\" isInline=\"false\" visibility=\"protected\" isAbstract=\"false\" isOverride=\"false\" name=\"wrap\" isSpecification=\"false\" isLeaf=\"false\">\n                   <UML:BehavioralFeature.parameter>\n                    <UML:Parameter xmi.id=\"ucMt9NqDI8xO3\" type=\"uWrkLWhzLVhyE\" kind=\"return\"/>\n                    <UML:Parameter xmi.id=\"uZDKju3bnYRmi\" visibility=\"private\" value=\"\" type=\"u5oLyKAa3lM7D\" name=\"guestbook\" isSpecification=\"false\"/>\n                   </UML:BehavioralFeature.parameter>\n                  </UML:Operation>\n                 </UML:Classifier.feature>\n                </UML:Class>\n               </UML:Namespace.ownedElement>\n              </UML:Package>\n             </UML:Namespace.ownedElement>\n            </UML:Package>\n           </UML:Namespace.ownedElement>\n          </UML:Package>\n          <UML:Package isRoot=\"false\" xmi.id=\"ujMJ43bkkTInI\" visibility=\"public\" isAbstract=\"false\" name=\"portal\" namespace=\"u2heAwPLbeXRO\" isSpecification=\"false\" isLeaf=\"false\">\n           <UML:Namespace.ownedElement>\n            <UML:Package isRoot=\"false\" xmi.id=\"uHU3MsMBTtx7K\" visibility=\"public\" isAbstract=\"false\" name=\"kernel\" namespace=\"ujMJ43bkkTInI\" isSpecification=\"false\" isLeaf=\"false\">\n             <UML:Namespace.ownedElement>\n              <UML:Package isRoot=\"false\" xmi.id=\"u0YosNbnEUVM2\" visibility=\"public\" isAbstract=\"false\" name=\"model\" namespace=\"uHU3MsMBTtx7K\" isSpecification=\"false\" isLeaf=\"false\">\n               <UML:Namespace.ownedElement>\n                <UML:Class isRoot=\"false\" xmi.id=\"uzWCo8LEDwhWm\" visibility=\"public\" isAbstract=\"false\" name=\"CacheModel\" namespace=\"u0YosNbnEUVM2\" isSpecification=\"false\" isLeaf=\"false\"/>\n                <UML:Package isRoot=\"false\" xmi.id=\"uytz2uAR3ByM1\" visibility=\"public\" isAbstract=\"false\" name=\"impl\" namespace=\"u0YosNbnEUVM2\" isSpecification=\"false\" isLeaf=\"false\">\n                 <UML:Namespace.ownedElement>\n                  <UML:Class isRoot=\"false\" xmi.id=\"uURkF03A2hV3s\" visibility=\"public\" isAbstract=\"false\" name=\"BaseModelImpl\" namespace=\"uytz2uAR3ByM1\" isSpecification=\"false\" isLeaf=\"false\"/>\n                 </UML:Namespace.ownedElement>\n                </UML:Package>\n                <UML:Interface isRoot=\"false\" xmi.id=\"uEJFmMOXICKN7\" visibility=\"public\" isAbstract=\"true\" stereotype=\"interface\" name=\"BaseModel\" namespace=\"u0YosNbnEUVM2\" isSpecification=\"false\" isLeaf=\"false\"/>\n                <UML:Package isRoot=\"false\" xmi.id=\"uHkkJjCLnm1IW\" visibility=\"public\" isAbstract=\"false\" name=\"wrapper\" namespace=\"u0YosNbnEUVM2\" isSpecification=\"false\" isLeaf=\"false\">\n                 <UML:Namespace.ownedElement>\n                  <UML:Class isRoot=\"false\" xmi.id=\"uiusqfusuWgNO\" visibility=\"public\" isAbstract=\"false\" name=\"BaseModelWrapper\" namespace=\"uHkkJjCLnm1IW\" isSpecification=\"false\" isLeaf=\"false\"/>\n                 </UML:Namespace.ownedElement>\n                </UML:Package>\n               </UML:Namespace.ownedElement>\n              </UML:Package>\n              <UML:Package isRoot=\"false\" xmi.id=\"uHlGa2LDTlBSM\" visibility=\"public\" isAbstract=\"false\" name=\"service\" namespace=\"uHU3MsMBTtx7K\" isSpecification=\"false\" isLeaf=\"false\">\n               <UML:Namespace.ownedElement>\n                <UML:Class isRoot=\"false\" xmi.id=\"u6iXBajIxWLgp\" visibility=\"public\" isAbstract=\"false\" name=\"ServiceContext\" namespace=\"uHlGa2LDTlBSM\" isSpecification=\"false\" isLeaf=\"false\"/>\n               </UML:Namespace.ownedElement>\n              </UML:Package>\n              <UML:Package isRoot=\"false\" xmi.id=\"uNdo1u4Mw1VXE\" visibility=\"public\" isAbstract=\"false\" name=\"util\" namespace=\"uHU3MsMBTtx7K\" isSpecification=\"false\" isLeaf=\"false\">\n               <UML:Namespace.ownedElement>\n                <UML:Class isRoot=\"false\" xmi.id=\"uO1HCIi4IK9G9\" visibility=\"public\" isAbstract=\"false\" name=\"Accessor\" namespace=\"uNdo1u4Mw1VXE\" isSpecification=\"false\" isLeaf=\"false\"/>\n               </UML:Namespace.ownedElement>\n              </UML:Package>\n             </UML:Namespace.ownedElement>\n            </UML:Package>\n           </UML:Namespace.ownedElement>\n          </UML:Package>\n          <UML:Package isRoot=\"false\" xmi.id=\"uZRuDLDYHxpPC\" visibility=\"public\" isAbstract=\"false\" name=\"exportimport\" namespace=\"u2heAwPLbeXRO\" isSpecification=\"false\" isLeaf=\"false\">\n           <UML:Namespace.ownedElement>\n            <UML:Package isRoot=\"false\" xmi.id=\"uno0sjQA2tzkt\" visibility=\"public\" isAbstract=\"false\" name=\"kernel\" namespace=\"uZRuDLDYHxpPC\" isSpecification=\"false\" isLeaf=\"false\">\n             <UML:Namespace.ownedElement>\n              <UML:Package isRoot=\"false\" xmi.id=\"uDZ0GlmR1mAUw\" visibility=\"public\" isAbstract=\"false\" name=\"lar\" namespace=\"uno0sjQA2tzkt\" isSpecification=\"false\" isLeaf=\"false\">\n               <UML:Namespace.ownedElement>\n                <UML:Class isRoot=\"false\" xmi.id=\"uAx06XlkZTNOk\" visibility=\"public\" isAbstract=\"false\" name=\"StagedModelType\" namespace=\"uDZ0GlmR1mAUw\" isSpecification=\"false\" isLeaf=\"false\"/>\n               </UML:Namespace.ownedElement>\n              </UML:Package>\n             </UML:Namespace.ownedElement>\n            </UML:Package>\n           </UML:Namespace.ownedElement>\n          </UML:Package>\n          <UML:Package isRoot=\"false\" xmi.id=\"u2WdxlHB2wA0L\" visibility=\"public\" isAbstract=\"false\" name=\"expando\" namespace=\"u2heAwPLbeXRO\" isSpecification=\"false\" isLeaf=\"false\">\n           <UML:Namespace.ownedElement>\n            <UML:Package isRoot=\"false\" xmi.id=\"uX9BaE9REibFl\" visibility=\"public\" isAbstract=\"false\" name=\"kernel\" namespace=\"u2WdxlHB2wA0L\" isSpecification=\"false\" isLeaf=\"false\">\n             <UML:Namespace.ownedElement>\n              <UML:Package isRoot=\"false\" xmi.id=\"uIYdVPsDd9VR6\" visibility=\"public\" isAbstract=\"false\" name=\"model\" namespace=\"uX9BaE9REibFl\" isSpecification=\"false\" isLeaf=\"false\">\n               <UML:Namespace.ownedElement>\n                <UML:Class isRoot=\"false\" xmi.id=\"uOy5vwfVBULFm\" visibility=\"public\" isAbstract=\"false\" name=\"ExpandoBridge\" namespace=\"uIYdVPsDd9VR6\" isSpecification=\"false\" isLeaf=\"false\"/>\n               </UML:Namespace.ownedElement>\n              </UML:Package>\n             </UML:Namespace.ownedElement>\n            </UML:Package>\n           </UML:Namespace.ownedElement>\n          </UML:Package>\n         </UML:Namespace.ownedElement>\n        </UML:Package>\n       </UML:Namespace.ownedElement>\n      </UML:Package>\n      <UML:Generalization child=\"utY1Nb3lrucXh\" parent=\"uzWCo8LEDwhWm\" xmi.id=\"ulFm2Z0JDGAsZ\" visibility=\"public\" discriminator=\"\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\"/>\n      <UML:Class isRoot=\"false\" xmi.id=\"uDcsaFusy8Y5S\" visibility=\"public\" isAbstract=\"false\" name=\"Object\" namespace=\"Logical_View\" isSpecification=\"false\" isLeaf=\"false\"/>\n      <UML:Package isRoot=\"false\" xmi.id=\"uz6SrY7VaXlYI\" visibility=\"public\" isAbstract=\"false\" name=\"java\" namespace=\"Logical_View\" isSpecification=\"false\" isLeaf=\"false\">\n       <UML:Namespace.ownedElement>\n        <UML:Package isRoot=\"false\" xmi.id=\"ux1Akw5ePg46z\" visibility=\"public\" isAbstract=\"false\" name=\"io\" namespace=\"uz6SrY7VaXlYI\" isSpecification=\"false\" isLeaf=\"false\">\n         <UML:Namespace.ownedElement>\n          <UML:Class isRoot=\"false\" xmi.id=\"un1gm5DfBo30C\" visibility=\"public\" isAbstract=\"false\" name=\"ObjectInput\" namespace=\"ux1Akw5ePg46z\" isSpecification=\"false\" isLeaf=\"false\"/>\n          <UML:Class isRoot=\"false\" xmi.id=\"uWQamAW1ki1ON\" visibility=\"public\" isAbstract=\"false\" name=\"ObjectOutput\" namespace=\"ux1Akw5ePg46z\" isSpecification=\"false\" isLeaf=\"false\"/>\n          <UML:Class isRoot=\"false\" xmi.id=\"ueqGp2XxhfCAm\" visibility=\"public\" isAbstract=\"false\" name=\"Serializable\" namespace=\"ux1Akw5ePg46z\" isSpecification=\"false\" isLeaf=\"false\"/>\n         </UML:Namespace.ownedElement>\n        </UML:Package>\n        <UML:Package isRoot=\"false\" xmi.id=\"uScvPQbzBWlSr\" visibility=\"public\" isAbstract=\"false\" name=\"util\" namespace=\"uz6SrY7VaXlYI\" isSpecification=\"false\" isLeaf=\"false\">\n         <UML:Namespace.ownedElement>\n          <UML:Class isRoot=\"false\" xmi.id=\"uQyeogSV0dP7h\" visibility=\"public\" isAbstract=\"false\" name=\"Map\" namespace=\"uScvPQbzBWlSr\" isSpecification=\"false\" isLeaf=\"false\"/>\n          <UML:Class isRoot=\"false\" xmi.id=\"uzKRVm8e0nLQP\" visibility=\"public\" isAbstract=\"false\" name=\"List\" namespace=\"uScvPQbzBWlSr\" isSpecification=\"false\" isLeaf=\"false\"/>\n          <UML:Package isRoot=\"false\" xmi.id=\"uaQeI5mKo30Qt\" visibility=\"public\" isAbstract=\"false\" name=\"function\" namespace=\"uScvPQbzBWlSr\" isSpecification=\"false\" isLeaf=\"false\">\n           <UML:Namespace.ownedElement>\n            <UML:Class isRoot=\"false\" xmi.id=\"uam7nyPRrHTSg\" visibility=\"public\" isAbstract=\"false\" name=\"Function\" namespace=\"uaQeI5mKo30Qt\" isSpecification=\"false\" isLeaf=\"false\"/>\n           </UML:Namespace.ownedElement>\n          </UML:Package>\n          <UML:Class isRoot=\"false\" xmi.id=\"u8C1qOpoOXZK5\" visibility=\"public\" isAbstract=\"false\" name=\"Date\" namespace=\"uScvPQbzBWlSr\" isSpecification=\"false\" isLeaf=\"false\"/>\n         </UML:Namespace.ownedElement>\n        </UML:Package>\n       </UML:Namespace.ownedElement>\n      </UML:Package>\n      <UML:Generalization child=\"umW4lWTF8ioUr\" parent=\"ud7IXARPBKg6E\" xmi.id=\"uCZMWklRg5SPU\" visibility=\"public\" discriminator=\"\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\"/>\n      <UML:Abstraction xmi.id=\"u69i2C3saZxfM\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\" client=\"umW4lWTF8ioUr\" supplier=\"u4d1ZoMYg5mnY\"/>\n      <UML:Generalization child=\"uIQBIzAd0DdG0\" parent=\"uzWCo8LEDwhWm\" xmi.id=\"upgj8ltsNi2uo\" visibility=\"public\" discriminator=\"\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\"/>\n      <UML:Generalization child=\"uhTHyHZ7wFmcr\" parent=\"umW4lWTF8ioUr\" xmi.id=\"usaRtS0vwSBSu\" visibility=\"public\" discriminator=\"\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\"/>\n      <UML:Generalization child=\"ud7IXARPBKg6E\" parent=\"uURkF03A2hV3s\" xmi.id=\"u0SEKUtCnZdN0\" visibility=\"public\" discriminator=\"\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\"/>\n      <UML:Class isRoot=\"false\" xmi.id=\"uij0CviuYyYdw\" visibility=\"public\" isAbstract=\"false\" name=\"Class\" namespace=\"Logical_View\" isSpecification=\"false\" isLeaf=\"false\"/>\n      <UML:Dependency xmi.id=\"u82Cz4xIbDlGV\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\" client=\"ud7IXARPBKg6E\" supplier=\"ulFm2Z0JDGAsZ\"/>\n      <UML:Dependency xmi.id=\"uKaZyCwopZi8F\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\" client=\"ud7IXARPBKg6E\" supplier=\"ulFm2Z0JDGAsZ\"/>\n      <UML:Class isRoot=\"false\" xmi.id=\"ul7aMnLiDXkuY\" visibility=\"public\" isAbstract=\"false\" name=\"GuestbookBaseImpl\" namespace=\"Logical_View\" isSpecification=\"false\" isLeaf=\"false\"/>\n      <UML:Generalization child=\"uRG8jivcgsDBf\" parent=\"ul7aMnLiDXkuY\" xmi.id=\"uKxJLwKpIVIuu\" visibility=\"public\" discriminator=\"\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\"/>\n      <UML:Generalization child=\"uRvrJuuRl1rob\" parent=\"uURkF03A2hV3s\" xmi.id=\"ukDqUiAtriCzr\" visibility=\"public\" discriminator=\"\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\"/>\n      <UML:Dependency xmi.id=\"u8KT9o9QJCucu\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\" client=\"uRvrJuuRl1rob\" supplier=\"ulFm2Z0JDGAsZ\"/>\n      <UML:Dependency xmi.id=\"uuiaHB055Mlqp\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\" client=\"uRvrJuuRl1rob\" supplier=\"ulFm2Z0JDGAsZ\"/>\n      <UML:Abstraction xmi.id=\"uksKxiFpcZ0nd\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\" client=\"u5oLyKAa3lM7D\" supplier=\"uUDLwp3yF2eRR\"/>\n      <UML:Abstraction xmi.id=\"ucqI2i1fuuouV\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\" client=\"u4d1ZoMYg5mnY\" supplier=\"uoLHmwTStXfLg\"/>\n      <UML:Abstraction xmi.id=\"ueALHHoOUuh3y\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\" client=\"uoLHmwTStXfLg\" supplier=\"uEJFmMOXICKN7\"/>\n      <UML:Generalization child=\"uHSp7X0pfGrxG\" parent=\"ueqGp2XxhfCAm\" xmi.id=\"uds9Dy2nEADqO\" visibility=\"public\" discriminator=\"\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\"/>\n      <UML:Generalization child=\"ugH8NB1k52llt\" parent=\"uiusqfusuWgNO\" xmi.id=\"uLKIZ2DLrH4SQ\" visibility=\"public\" discriminator=\"\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\"/>\n      <UML:Abstraction xmi.id=\"uTSB9TXwUyipL\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\" client=\"uUDLwp3yF2eRR\" supplier=\"uEJFmMOXICKN7\"/>\n      <UML:Generalization child=\"uYrth6QTT97i2\" parent=\"ueqGp2XxhfCAm\" xmi.id=\"u5r9FvNzb8SBr\" visibility=\"public\" discriminator=\"\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\"/>\n      <UML:Generalization child=\"uWrkLWhzLVhyE\" parent=\"uiusqfusuWgNO\" xmi.id=\"uBhCfCdodwUj7\" visibility=\"public\" discriminator=\"\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\"/>\n      <UML:Association xmi.id=\"uqRt9fd6MK6Dz\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\">\n       <UML:Association.connection>\n        <UML:AssociationEnd changeability=\"changeable\" xmi.id=\"uUIG9HbgyHNK4\" isNavigable=\"false\" aggregation=\"none\" visibility=\"public\" type=\"uRG8jivcgsDBf\" name=\"\" isSpecification=\"false\"/>\n        <UML:AssociationEnd changeability=\"changeable\" xmi.id=\"u8OCU0cyKTbFP\" isNavigable=\"true\" aggregation=\"none\" visibility=\"public\" type=\"u5oLyKAa3lM7D\" name=\"\" isSpecification=\"false\"/>\n       </UML:Association.connection>\n      </UML:Association>\n      <UML:Association xmi.id=\"uHgwzquwfKbkp\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\">\n       <UML:Association.connection>\n        <UML:AssociationEnd changeability=\"changeable\" xmi.id=\"umWo96MtMaUFH\" isNavigable=\"false\" aggregation=\"none\" visibility=\"public\" type=\"uhTHyHZ7wFmcr\" name=\"\" isSpecification=\"false\"/>\n        <UML:AssociationEnd changeability=\"changeable\" xmi.id=\"u9I8jmBoMsyaE\" isNavigable=\"true\" aggregation=\"none\" visibility=\"public\" type=\"u4d1ZoMYg5mnY\" name=\"\" isSpecification=\"false\"/>\n       </UML:Association.connection>\n      </UML:Association>\n      <UML:Association xmi.id=\"u6we5jZYvpdHh\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\">\n       <UML:Association.connection>\n        <UML:AssociationEnd changeability=\"changeable\" xmi.id=\"uqxeKahdSeo52\" isNavigable=\"false\" aggregation=\"none\" visibility=\"public\" type=\"u4d1ZoMYg5mnY\" name=\"\" isSpecification=\"false\"/>\n        <UML:AssociationEnd changeability=\"changeable\" xmi.id=\"u9wlGfmrH6tY5\" isNavigable=\"true\" aggregation=\"none\" visibility=\"public\" type=\"uhTHyHZ7wFmcr\" name=\"\" isSpecification=\"false\"/>\n       </UML:Association.connection>\n      </UML:Association>\n      <UML:Association xmi.id=\"uP3hL8GMatjrZ\" visibility=\"public\" name=\"\" namespace=\"Logical_View\" isSpecification=\"false\">\n       <UML:Association.connection>\n        <UML:AssociationEnd changeability=\"changeable\" xmi.id=\"uI3vZv65eLqwm\" isNavigable=\"false\" aggregation=\"none\" visibility=\"public\" type=\"uhTHyHZ7wFmcr\" name=\"\" isSpecification=\"false\"/>\n        <UML:AssociationEnd changeability=\"changeable\" xmi.id=\"uF1XWgN8loZdv\" isNavigable=\"true\" aggregation=\"none\" visibility=\"public\" type=\"u4d1ZoMYg5mnY\" name=\"\" isSpecification=\"false\"/>\n       </UML:Association.connection>\n      </UML:Association>\n     </UML:Namespace.ownedElement>\n     <XMI.extension xmi.extender=\"umbrello\">\n      <diagrams resolution=\"96\">\n       <diagram zoom=\"100\" griddotcolor=\"#d3d3d3\" showopsig=\"1\" localid=\"-1\" snapcsgrid=\"0\" name=\"class diagram\" showscope=\"1\" snapx=\"25\" snapy=\"25\" documentation=\"\" linecolor=\"#ff0000\" canvaswidth=\"1117\" showgrid=\"0\" linewidth=\"0\" fillcolor=\"#ffff00\" font=\"Exo 2,11,-1,5,50,0,0,0,0,0,Regular\" snapgrid=\"0\" showops=\"1\" xmi.id=\"uCIrKoQWRUclv\" textcolor=\"#000000\" usefillcolor=\"1\" isopen=\"1\" showstereotype=\"1\" backgroundcolor=\"#ffffff\" showpackage=\"1\" showpubliconly=\"0\" type=\"1\" showatts=\"1\" showattsig=\"1\" canvasheight=\"604\" showattribassocs=\"1\">\n        <widgets>\n         <interfacewidget localid=\"u3FMz5k4FvGXF\" drawascircle=\"0\" showscope=\"1\" autoresize=\"1\" linecolor=\"#ff0000\" showopsigs=\"601\" height=\"45\" linewidth=\"0\" fillcolor=\"#ffff00\" font=\"Exo 2,11,-1,5,50,0,0,0,0,0,Regular\" width=\"120\" xmi.id=\"u4d1ZoMYg5mnY\" textcolor=\"#000000\" usefillcolor=\"1\" showstereotype=\"1\" isinstance=\"0\" usesdiagramusefillcolor=\"0\" y=\"-921\" showattributes=\"0\" showoperations=\"1\" usesdiagramfillcolor=\"0\" showpubliconly=\"0\" showpackage=\"0\" showattsigs=\"601\" x=\"-666\"/>\n         <interfacewidget localid=\"uB0BBCZMAJGHD\" drawascircle=\"0\" showscope=\"1\" autoresize=\"1\" linecolor=\"#ff0000\" showopsigs=\"601\" height=\"45\" linewidth=\"0\" fillcolor=\"#ffff00\" font=\"Exo 2,11,-1,5,50,0,0,0,0,0,Regular\" width=\"86\" xmi.id=\"u5oLyKAa3lM7D\" textcolor=\"#000000\" usefillcolor=\"1\" showstereotype=\"1\" isinstance=\"0\" usesdiagramusefillcolor=\"0\" y=\"-1086\" showattributes=\"0\" showoperations=\"1\" usesdiagramfillcolor=\"0\" showpubliconly=\"0\" showpackage=\"0\" showattsigs=\"601\" x=\"-648\"/>\n         <classwidget localid=\"uq5VB4O7aGsYF\" showscope=\"1\" autoresize=\"1\" linecolor=\"#ff0000\" showopsigs=\"601\" height=\"45\" linewidth=\"0\" fillcolor=\"#ffff00\" font=\"Exo 2,11,-1,5,50,0,0,0,0,0,Regular\" width=\"383\" xmi.id=\"uRG8jivcgsDBf\" textcolor=\"#000000\" usefillcolor=\"1\" showstereotype=\"1\" isinstance=\"0\" usesdiagramusefillcolor=\"0\" y=\"-1000\" showattributes=\"1\" showoperations=\"1\" usesdiagramfillcolor=\"0\" showpubliconly=\"0\" showpackage=\"1\" showattsigs=\"601\" x=\"-806\"/>\n         <classwidget localid=\"umy494r74jJWM\" showscope=\"1\" autoresize=\"1\" linecolor=\"#ff0000\" showopsigs=\"601\" height=\"45\" linewidth=\"0\" fillcolor=\"#ffff00\" font=\"Exo 2,11,-1,5,50,0,0,0,0,0,Regular\" width=\"420\" xmi.id=\"uhTHyHZ7wFmcr\" textcolor=\"#000000\" usefillcolor=\"1\" showstereotype=\"1\" isinstance=\"0\" usesdiagramusefillcolor=\"0\" y=\"-830\" showattributes=\"1\" showoperations=\"1\" usesdiagramfillcolor=\"0\" showpubliconly=\"0\" showpackage=\"1\" showattsigs=\"601\" x=\"-812\"/>\n        </widgets>\n        <messages/>\n        <associations>\n         <assocwidget seqnum=\"\" autoresize=\"1\" totalcountb=\"2\" linecolor=\"#ff0000\" totalcounta=\"2\" linewidth=\"0\" fillcolor=\"none\" font=\"Exo 2,11,-1,5,50,0,0,0,0,0,Regular\" xmi.id=\"uP3hL8GMatjrZ\" textcolor=\"none\" usefillcolor=\"1\" widgetbid=\"u4d1ZoMYg5mnY\" indexa=\"1\" usesdiagramusefillcolor=\"1\" usesdiagramfillcolor=\"1\" type=\"512\" indexb=\"1\" widgetaid=\"uhTHyHZ7wFmcr\">\n          <linepath layout=\"Polyline\">\n           <startpoint starty=\"-830\" startx=\"-606\"/>\n           <endpoint endx=\"-606\" endy=\"-876\"/>\n          </linepath>\n         </assocwidget>\n         <assocwidget seqnum=\"\" autoresize=\"1\" totalcountb=\"2\" linecolor=\"#ff0000\" totalcounta=\"2\" linewidth=\"0\" fillcolor=\"none\" font=\"Exo 2,11,-1,5,50,0,0,0,0,0,Regular\" xmi.id=\"uqRt9fd6MK6Dz\" textcolor=\"none\" usefillcolor=\"1\" widgetbid=\"u5oLyKAa3lM7D\" indexa=\"1\" usesdiagramusefillcolor=\"1\" usesdiagramfillcolor=\"1\" type=\"512\" indexb=\"1\" widgetaid=\"uRG8jivcgsDBf\">\n          <linepath layout=\"Polyline\">\n           <startpoint starty=\"-1000\" startx=\"-605\"/>\n           <endpoint endx=\"-605\" endy=\"-1041\"/>\n          </linepath>\n         </assocwidget>\n        </associations>\n       </diagram>\n       <diagram zoom=\"100\" griddotcolor=\"#d3d3d3\" showopsig=\"1\" localid=\"-1\" snapcsgrid=\"0\" name=\"class diagram_1\" showscope=\"1\" snapx=\"25\" snapy=\"25\" documentation=\"\" linecolor=\"#ff0000\" canvaswidth=\"0\" showgrid=\"0\" linewidth=\"0\" fillcolor=\"#ffff00\" font=\"Exo 2,11,-1,5,50,0,0,0,0,0,Regular\" snapgrid=\"0\" showops=\"1\" xmi.id=\"uOVPrhWgvT2Xh\" textcolor=\"#000000\" usefillcolor=\"1\" isopen=\"0\" showstereotype=\"1\" backgroundcolor=\"#ffffff\" showpackage=\"1\" showpubliconly=\"0\" type=\"1\" showatts=\"1\" showattsig=\"1\" canvasheight=\"0\" showattribassocs=\"1\">\n        <widgets/>\n        <messages/>\n        <associations/>\n       </diagram>\n      </diagrams>\n     </XMI.extension>\n    </UML:Model>\n    <UML:Model isRoot=\"false\" xmi.id=\"Use_Case_View\" visibility=\"public\" isAbstract=\"false\" name=\"Use Case View\" namespace=\"m1\" isSpecification=\"false\" isLeaf=\"false\">\n     <UML:Namespace.ownedElement/>\n    </UML:Model>\n    <UML:Model isRoot=\"false\" xmi.id=\"Component_View\" visibility=\"public\" isAbstract=\"false\" name=\"Component View\" namespace=\"m1\" isSpecification=\"false\" isLeaf=\"false\">\n     <UML:Namespace.ownedElement/>\n    </UML:Model>\n    <UML:Model isRoot=\"false\" xmi.id=\"Deployment_View\" visibility=\"public\" isAbstract=\"false\" name=\"Deployment View\" namespace=\"m1\" isSpecification=\"false\" isLeaf=\"false\">\n     <UML:Namespace.ownedElement/>\n    </UML:Model>\n    <UML:Model isRoot=\"false\" xmi.id=\"Entity_Relationship_Model\" visibility=\"public\" isAbstract=\"false\" name=\"Entity Relationship Model\" namespace=\"m1\" isSpecification=\"false\" isLeaf=\"false\">\n     <UML:Namespace.ownedElement/>\n    </UML:Model>\n   </UML:Namespace.ownedElement>\n  </UML:Model>\n </XMI.content>\n <XMI.extensions xmi.extender=\"umbrello\">\n  <docsettings viewid=\"uCIrKoQWRUclv\" uniqueid=\"uF1XWgN8loZdv\" documentation=\"\"/>\n  <listview>\n   <listitem open=\"1\" type=\"800\" id=\"Views\">\n    <listitem open=\"1\" type=\"821\" id=\"Component_View\"/>\n    <listitem open=\"1\" type=\"827\" id=\"Deployment_View\"/>\n    <listitem open=\"1\" type=\"836\" id=\"Entity_Relationship_Model\"/>\n    <listitem open=\"1\" type=\"801\" id=\"Logical_View\">\n     <listitem open=\"1\" type=\"813\" id=\"uij0CviuYyYdw\"/>\n     <listitem open=\"0\" label=\"class diagram\" type=\"807\" id=\"uCIrKoQWRUclv\"/>\n     <listitem open=\"0\" label=\"class diagram_1\" type=\"807\" id=\"uOVPrhWgvT2Xh\"/>\n     <listitem open=\"1\" type=\"818\" id=\"ueCOHn4k55Hul\">\n      <listitem open=\"1\" type=\"818\" id=\"u2heAwPLbeXRO\">\n       <listitem open=\"1\" type=\"818\" id=\"uy5Hu1NT2E04j\">\n        <listitem open=\"1\" type=\"818\" id=\"usSXUPnpdKWie\">\n         <listitem open=\"1\" type=\"818\" id=\"uuJOr4utQZVql\">\n          <listitem open=\"0\" type=\"817\" id=\"u5oLyKAa3lM7D\"/>\n          <listitem open=\"0\" type=\"817\" id=\"u4d1ZoMYg5mnY\"/>\n          <listitem open=\"0\" type=\"817\" id=\"uoLHmwTStXfLg\">\n           <listitem open=\"0\" type=\"815\" id=\"uvV2hgXLjHMaP\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ueVJ7UD9NcBCl\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ut5Hm8A46EBGk\"/>\n           <listitem open=\"0\" type=\"815\" id=\"us6rQ2YRs1T6J\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ux6SnPN4W3IGR\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u5jXyAGFoPZsN\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uPcxluoSPNYU1\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uTIepu0yKofJw\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uq8yGEVlvtFhx\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uU4tuKJuvVvuZ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uaBNz3x750CAy\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u4KILkLX88mFu\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uzI16OWmmOaDo\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u67dTwqOmD8zw\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uaL5xWxgmDiGK\"/>\n           <listitem open=\"0\" type=\"815\" id=\"usmcNaVcv3VWu\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uQMwQnaqnHmN1\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uRnLIQQdgv7l1\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uVDI1FZlRr5O7\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uECpqB8kTbe3f\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uB88lsVtODscu\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uOPwipt32RqJY\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u8OSKuKuDfYFk\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uThLUiZxMwtEt\"/>\n           <listitem open=\"0\" type=\"815\" id=\"utoC23wXOl9F2\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uoujMFmDjIk6g\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uOvpco0w6vZp9\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uLHua2IhHHw7s\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ubh4FQAhhPlXx\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u831WOBVYsAx0\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u4EnNuhTmWAuN\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uRTi9lSp8Ocds\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uA9vmoTO8J2ff\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uAoEPpTxpmJHu\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uqFpjxv0McIqj\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ugEkrUvshQyMr\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uoWIYqMHe8dEa\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uXwZlRkcfvauv\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u1Y4trpeKIY8v\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ukqT7muDybSIM\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uoYcuA48LlNt2\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uP5pCWVbsIMGk\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uPyEaYpGRLtyp\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uBhHpxF4IVjQQ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uqQDAXkoxaaHp\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u0koFCLbu8lKC\"/>\n          </listitem>\n          <listitem open=\"0\" type=\"813\" id=\"uHSp7X0pfGrxG\">\n           <listitem open=\"0\" type=\"814\" id=\"uXpO7zP9zJka4\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uZvfmRXM46tJa\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uY9XYdE87cEN6\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uh60pl6lnN9we\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uR86J4HgddRIb\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uuCBG57CzjznO\"/>\n           <listitem open=\"0\" type=\"814\" id=\"ugTLQb6jqETuv\"/>\n           <listitem open=\"0\" type=\"814\" id=\"u9W7xy9IX4D0A\"/>\n           <listitem open=\"0\" type=\"814\" id=\"u9s1xgr5fmwiN\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uQdCtpMK1hbsm\"/>\n           <listitem open=\"0\" type=\"814\" id=\"ufiQ6TdEITgCO\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uYikZZEqOVubn\"/>\n           <listitem open=\"0\" type=\"814\" id=\"u7DXeG3miZjeE\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uLvcgGt4jjOhZ\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uRfNOkWUugWSB\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uwEblEYEsZ92o\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u0O7FS3OPWbky\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uChcrk8upmBWW\"/>\n           <listitem open=\"0\" type=\"815\" id=\"urEq7YdajFL37\"/>\n           <listitem open=\"0\" type=\"815\" id=\"umKjPT1UAA7rJ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uOCt5yXC1zrmD\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u8atb0BelpliU\"/>\n           <listitem open=\"0\" type=\"815\" id=\"untSrcn67BHSQ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ushePeE1jdRSq\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u8NSNPGStUufJ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uWBoto66x0qau\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ugjlBQVKK4MQg\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uhFe7sa3AAe4I\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uP5pKa3gntmKm\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uLArs98XAkNHS\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u11nQisWRe3ys\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uKgcIgYuf25Q3\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u9YMtIyov3xiP\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uV7EkZOsRQMza\"/>\n           <listitem open=\"0\" type=\"815\" id=\"usLBlkTznozaz\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uqBfimqL7CeHW\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u8LUehTxh4YDK\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ukydB3xeHz0DQ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"upZJB6FF7WWpn\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uEEKItKkUnyMO\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ue8sQnVEF2B4i\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uuScwS8lADE0b\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uuKNw1D0fmzVg\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uuDS0nKhS5oxo\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uoDspfGJXw2Ul\"/>\n           <listitem open=\"0\" type=\"815\" id=\"usfhJBuuDueiX\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uv3hNie4bl1dJ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uAED53CLSBptp\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uQqjY7nrlA3Uo\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uIIbEHonxTp1a\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u4dAgSRqyrMF6\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ucMWDIptph9PQ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uBPmue7qAIZVv\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uGZVq59rttNLb\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uy1k6HOgsgSBr\"/>\n          </listitem>\n          <listitem open=\"0\" type=\"813\" id=\"ugH8NB1k52llt\">\n           <listitem open=\"0\" type=\"815\" id=\"uIUQswGB0gDB2\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uQgr92MVhIBz4\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uacJ9M5xMPPDJ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u6UFrE7PoydN1\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uvOsOFUdQwMjo\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uoNmhT5zdktL6\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uRDRXndr1dKCM\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uJzPgtudgkqKt\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uhaIW9fgXHSHy\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uY0uBOxMulLXN\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uNBh9OW6U51y9\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uMalGYsXFtfUC\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u2K5SQWOXpVei\"/>\n           <listitem open=\"0\" type=\"815\" id=\"usdmFcyF3GrbH\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uCS78owZPKFzo\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uFzinyBVOp5Cc\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uIHtRpzFynifO\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u5KCy56i0Zm4k\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uBBjUo05gH9Jv\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uXyjrSjaPI4rN\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uwwdsTJZZ4yii\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u8O6hIJxUREos\"/>\n           <listitem open=\"0\" type=\"815\" id=\"umvOdESqRtguY\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uJgU24ScT7gxU\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uXBKh1Wn1x5vo\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uVBmuBaZwTI90\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uGZYmM6rkpmnO\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u5Ge45zuWJJLY\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u7od8yTFFfcg1\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uHmjDfIDIK2DZ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uaYd3wDHu2oqd\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uEWU7Ub3v2P84\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uaE1uuw79JPWy\"/>\n           <listitem open=\"0\" type=\"815\" id=\"utcDVpJSCL9sz\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uBPoaxd5nZX3u\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uau7qDH8ChRan\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u2x2YvaNNF6fV\"/>\n           <listitem open=\"0\" type=\"815\" id=\"urrENJFbm5miE\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uQcX8xmqyNvQe\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uNptH8NKIOq2C\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uCnZF0njRpONP\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uB8d5A0RmoZEp\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ur9xSkzrLUTZb\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ucYt1rQJF8v6s\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u7RDAMHuSWoKT\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ucS79rGp0cW5s\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uZz2WrDgpb3pO\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uXUcYBY2U3T0W\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u4fTagZApKVSa\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uS00r8magyOdV\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uv29FVPlBnWHi\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u9yMuIS6nseph\"/>\n          </listitem>\n          <listitem open=\"0\" type=\"817\" id=\"uUDLwp3yF2eRR\">\n           <listitem open=\"0\" type=\"815\" id=\"ukVwl4qu05CCa\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uy6D0xva95Eod\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uVT35TrPlyGfr\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uAvPYHPmxgNi9\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u1r7cKdCnO11j\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ucYLZshesecoT\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ub68XfxAagK9u\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uIkcW6qvM6jCI\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uCbSrooYSdktQ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uSSV9Q1oO7kUf\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ubtG6tNxYROfI\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u3fGVkd8t5KYd\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uknSSMsVu56Sj\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uktwvlIoXecq3\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u0RKbULUdq3dA\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uTc2dBIsBmuzy\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uMZN1l9T7RRUw\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uYUqfUdCQc9aN\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u1bjlNdoBkFN9\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uStrcwy9YCJGx\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uwpdbWibYvJLO\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u0HoVpECn4xx7\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u9GCw3hVS6P2W\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uNuIB9VXELS0N\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ur163Ve9bUBCW\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uDhZiO3YaZUeB\"/>\n           <listitem open=\"0\" type=\"815\" id=\"unyqibPOSGOhG\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uRcSEx0fpqvV8\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uJndxTpNJw8rx\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uwN1EnbagGNkr\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uzbvgIlHBOx3c\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uDsaKpgOPNOzy\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uMJdcThxnpafn\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uZIwFm3WaY85N\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u0E3ueYJD3Ltg\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uIXeQ6LRMm1Yb\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uDnjH3NAQFk44\"/>\n           <listitem open=\"0\" type=\"815\" id=\"udA58mRCNEVCE\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uqciYHkKd7EBl\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uwa8Bu61A0tfv\"/>\n          </listitem>\n          <listitem open=\"0\" type=\"813\" id=\"uYrth6QTT97i2\">\n           <listitem open=\"0\" type=\"814\" id=\"uiCVr2jENkbvw\"/>\n           <listitem open=\"0\" type=\"814\" id=\"ue8zwHXpL05Ip\"/>\n           <listitem open=\"0\" type=\"814\" id=\"ub7DlmfdRVUbN\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uTTgP2O95jqS2\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uzk6uPtGvRiPC\"/>\n           <listitem open=\"0\" type=\"814\" id=\"u1TRjZwCvwMWX\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uQlIuJAo2PAh7\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uKAzDUwfvSUA6\"/>\n           <listitem open=\"0\" type=\"814\" id=\"udOYR52wjsTCw\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uBGdtKsKXePCk\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uZOCh299YAtpQ\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uuP8s0fJWyNSl\"/>\n           <listitem open=\"0\" type=\"814\" id=\"uJwkTAggGemO5\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uYjOzfdnOPvE9\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ujaL9xs1oVonQ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u9WrJ5HgEHQw6\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uxaYsf1qlQcsU\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uLVxcF6b5oP5Q\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uUys2yrF5lrqz\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uwQBcuR2LkM5a\"/>\n           <listitem open=\"0\" type=\"815\" id=\"umAjIafirBnku\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uzvnecMXX3xHw\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uXP0gD1gwIUvI\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ubgpSaLRZiHWO\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u0qyRpkeuixO3\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uRizALQJY3to7\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uDsYIJKOewnwc\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ub8KRdh13Kl3h\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uZNIZd2BjmfG6\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uLqzkFr5ht7bS\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uqLakIwcgA8bq\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uF5v9jko2bK48\"/>\n           <listitem open=\"0\" type=\"815\" id=\"upMFqhihgi0Xz\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uY8UnGdWURxw0\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uAN226SDw1cfa\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uskcOGs0S2fKU\"/>\n           <listitem open=\"0\" type=\"815\" id=\"utJjh76qoTW8T\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uTd9uz6JsfEYd\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uqDQrqKUzo17I\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uW4WLGRU5z4nq\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uhxYzySWnAI2A\"/>\n           <listitem open=\"0\" type=\"815\" id=\"udQoBUcab9n2R\"/>\n           <listitem open=\"0\" type=\"815\" id=\"utjb6whE01QG1\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uKdZAqADDk5fD\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ugaknFw8dJmFD\"/>\n           <listitem open=\"0\" type=\"815\" id=\"umED6ajFOuB11\"/>\n          </listitem>\n          <listitem open=\"0\" type=\"813\" id=\"uWrkLWhzLVhyE\">\n           <listitem open=\"0\" type=\"815\" id=\"uMZSR3brdhc5d\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uzMtroNZJY3yQ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uiWgao3Ov3l91\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uHydEBiaFygm6\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uKOTLK6AOapdw\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ugAEPsKQkgSvb\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ukBtpT3yUEhb2\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uEvcrHb43WmxH\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ufIuIkJLQOL7r\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uC5BYIcBua9ky\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u8QeMKpvBv8vP\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uZasotJj2H14V\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u7AwtAxm3Gruw\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uIA4RnmAg4fff\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uzlyoE5ywtsfz\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ug0uh95zdUGHZ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u7wBDpcQWlilG\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uWPiwHNpSWETE\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uBq1cYHKEKIq5\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ujYM87EByv0P7\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uf9pcA72g57Ex\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u7FHra36Ep8rW\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uq1kuZmWQ5bP6\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uL53MrzGepNZd\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uvAVPKRGuLj7H\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ugSW5Y1LC5VLI\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uuZ2FyWv5s9zh\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uLgW4kAoOAPfc\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u4He0j66E7AfQ\"/>\n           <listitem open=\"0\" type=\"815\" id=\"udlpI9UVzET4d\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uQECn9N2DMGm4\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uBqO031flGu8s\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uiwDtqmh1blVN\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u6TDONgXolmJG\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uLlogwXI5QXf5\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uw8dHubSWh8mI\"/>\n           <listitem open=\"0\" type=\"815\" id=\"utDyf2onVS8Wp\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ux2yq2eLHEHUG\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uJG4xViyzppgd\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u71ZLZMbZrhFf\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ux8Xx87uiCyNO\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uit4kgC6yhCCG\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uXxu2XNka7Rw1\"/>\n           <listitem open=\"0\" type=\"815\" id=\"ufBV9Yz7U15q6\"/>\n           <listitem open=\"0\" type=\"815\" id=\"uolATiN9Spvte\"/>\n           <listitem open=\"0\" type=\"815\" id=\"u9MUDepuWCUbQ\"/>\n          </listitem>\n          <listitem open=\"1\" type=\"818\" id=\"uKJUfYfiEJfLg\">\n           <listitem open=\"0\" type=\"813\" id=\"utY1Nb3lrucXh\">\n            <listitem open=\"0\" type=\"814\" id=\"uN3u5hjIkS55S\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uhDpSdQEdtQMc\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uZ9cj5Z20xfec\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uFabxOaxr8Gua\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ulrcmQVxsrzyS\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uCpAALPkCyHre\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u0A2b6YLV3t8n\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uuW27aoUuzToz\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uMjSqW0r1IgIA\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uMozkKcfWhYgf\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u5qshFd30HoEN\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uYduCIVUOeHWh\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ukAr7j9IwSj1L\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uwHYWxC9Xb09D\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uV0XJf7lOYbjl\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uLROzf8oemYNm\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uls2gR5omKY1v\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uHCW53dkh4ZWn\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ubjZscuiwpAIZ\"/>\n           </listitem>\n           <listitem open=\"0\" type=\"813\" id=\"umW4lWTF8ioUr\">\n            <listitem open=\"0\" type=\"815\" id=\"uxfiYotRJAcRB\"/>\n           </listitem>\n           <listitem open=\"0\" type=\"813\" id=\"uIQBIzAd0DdG0\">\n            <listitem open=\"0\" type=\"814\" id=\"uDnf2nLi3HhRr\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ulN9OtzBWfuQL\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u372XSfM3DPOk\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ufjwjyR6QK2Xp\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uxljtnh0VvYvc\"/>\n            <listitem open=\"0\" type=\"814\" id=\"usKAqZ6CqtVT8\"/>\n            <listitem open=\"0\" type=\"814\" id=\"unb9OCs36BAvs\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uuzvIn3MKIoRu\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u9ruqMYkdhypw\"/>\n            <listitem open=\"0\" type=\"814\" id=\"umLojKoxpylG9\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u0DDB4973HR2Q\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u4fy35L0Rz3FG\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uiSzQQDjlHH0M\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uGud2ea5cyFZU\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uMoRocM7o8sWf\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uYt2JLkTwWSbv\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uszLzyomNI3Ix\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u8KXyBNoLH6to\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ukCYOxyxmxbK4\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uXt8vCGRytTKV\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uQWMBvZilghk8\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ul7oekPFEdo2X\"/>\n           </listitem>\n           <listitem open=\"1\" type=\"813\" id=\"uhTHyHZ7wFmcr\">\n            <listitem open=\"0\" type=\"815\" id=\"uhQZ5r2D1dsY5\"/>\n           </listitem>\n           <listitem open=\"0\" type=\"813\" id=\"ud7IXARPBKg6E\">\n            <listitem open=\"0\" type=\"814\" id=\"uNKMcQ27rRdD7\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uVfEZMCmXB3lS\"/>\n            <listitem open=\"0\" type=\"814\" id=\"usFzEkIWrB6Nf\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u0ORkEPbvheKe\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uzcG9bJotxvj6\"/>\n            <listitem open=\"0\" type=\"814\" id=\"upvE3P9WNErgU\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uXQiBXBBbcF3V\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uqqU8tbusr8Hu\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uVxBcvogx0gg6\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uhlzDQ184ZF8H\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uWzIbBPgQKBhz\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uscnU6mJxUu8d\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uhm8UA1sfJYeY\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uSFVhx6FA8O9T\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uMIddwifgMgIn\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uR9uInTB2nAJB\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uY2VoCF9TQc2w\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ueqjiFdGMF9vo\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uHrGqAyfP80W0\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uWXuNBen5Jcyw\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uyG65AME1ol7z\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uBXGH1gJvcQ5E\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uiBcuvTN8VQH1\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u316bDZgGpqiR\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ufGGNgSyihQrd\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ukprpE22zyCyC\"/>\n            <listitem open=\"0\" type=\"814\" id=\"utxkGLQZz8MYd\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u4SVAhhAGfLMs\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uVCOmdcGOSlF7\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uHRJMdUIlkDpX\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u2L0ulQcr8LWf\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uV5GA32s7un6z\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uhXIIfKGbZMpt\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u71nfMJuDXm4N\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u2CkA2sdwQgUH\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uoQSZj23DHdfC\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uCgjnwYP2KB9G\"/>\n            <listitem open=\"0\" type=\"815\" id=\"usgyzYTYA3WUF\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uQJcwM9jie1rE\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uAND9PlmH5kog\"/>\n            <listitem open=\"0\" type=\"815\" id=\"udykvZ9f2Nvy7\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uURFKCbIxEM0z\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uRGnUXqZquX5C\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ugwrtHQGXcW4V\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uZxT7K5bYwGuN\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uDQfkwpY7saAO\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uGCA55VPAfw1Y\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uBLZ9H3QRmQz0\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u4o6oKAeSESIi\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ufwXM3C1aJE1D\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uAa4BmbjtuReW\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u6gOrKS5J832W\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uWBC38a8Kk7d9\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u2QKvF1qO93Ix\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ufEq5WGpUgqeO\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ubN3mhuTI6C3O\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ub4pwUhnv1W7y\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u7JUcdIEJnhNB\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uSiaZGUguiDJ2\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ubj5MaICY1Tra\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uyHxW2p61LHhm\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uYCqIFCmTobGB\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uqnCOi5uoJpEJ\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uG273yXvudhrh\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ukpvQGF7KfxPM\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uL0vscF2jUnoc\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uvtlN9XDYccjR\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uD0bIIJhJuJ1j\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u7PBbfluy0dmC\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uun7QqqWt3Na7\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ufFQFqN9y3d9D\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uweUEqPJ24zE4\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ukp0qQZBJl0q0\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ush4qzFaw3GFh\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u9MNLdBFvvw2p\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uPVArl6nCmg0q\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uuQpY6AZbflDx\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uQNujTVljHIVt\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uopPelSrk1uQo\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uMCiS3hvzU0WX\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uSzIi12UCrbJx\"/>\n            <listitem open=\"0\" type=\"815\" id=\"unI4zJy10qW5A\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u5m6PjIQFG4vY\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u3JrJgcHJXZ39\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uXzmkYvUplJko\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uichEa6QSnaq9\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uTRMTCUurSqFy\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ujeW7gLuYKVgI\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uzklsBwDwnSuw\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uwopsY95wK8UN\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uQWka85nDEt6p\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uoomw6B5FzPb7\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uZjnqsKKgA0qg\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u3KFxF2mtaag0\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uQLoNoXpyr79C\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uIeq6BozeOUNK\"/>\n            <listitem open=\"0\" type=\"815\" id=\"udh6dF52f8bq9\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uJfxvTnlgobps\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uWyMdBbsWWoUZ\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uCKIkUumVrEsy\"/>\n            <listitem open=\"0\" type=\"815\" id=\"unv7McW4wnjPf\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uuxuOD0Hm3K8p\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uMI5kd21vJNUM\"/>\n            <listitem open=\"0\" type=\"815\" id=\"upEiuirfZeKlG\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uWSxT7mCxFfaZ\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ua5sjdPnUjC2c\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u7UAIkZ6iR3dy\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uLG0XyjuFL39u\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uh89Epmm9Gnyq\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uao4JTZzh4uQo\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u4CmgCuSO0ecZ\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u0uq62hsEKj17\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ueUX2YvEopMHY\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u1JEcHsN8UBp5\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uIapkeSys7y4o\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u9XygGrLjhXtj\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uYE5YOuI73akj\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uGklXPHwwR6BL\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uq9l4YCWUc6gx\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u3G3D6JndIQvZ\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ub940XoQ95vZ2\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uTdZsHdKi8HFX\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u5uh9hy6dROAo\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uOxjaWrW5bPPV\"/>\n            <listitem open=\"0\" type=\"814\" id=\"urfASmgyj0cPN\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uRJQm0fHM02KP\"/>\n           </listitem>\n           <listitem open=\"1\" type=\"813\" id=\"uRG8jivcgsDBf\">\n            <listitem open=\"0\" type=\"815\" id=\"uhuvD8o1t5IsB\"/>\n           </listitem>\n           <listitem open=\"1\" type=\"813\" id=\"uRvrJuuRl1rob\">\n            <listitem open=\"0\" type=\"814\" id=\"uJ2voT02rU8Va\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uzPWou6SUcYg1\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u7NI5VUHgiL7r\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uTxURbLnb1vR3\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uqSmI82p5wjva\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uMRsvx4GLZyj8\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uFEXnvnJ5pAqx\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uMQRkplZGioMm\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uixnwRKcH6KJW\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uqa1781FB5biX\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ultCfhDl5VXK6\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u0azlFuk7WY5z\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u8x2yExbx7BSl\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ujM5owoVCU6IZ\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uKFhFZsy11ZXb\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u3rO1DRagevJZ\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ujna3npI3okFZ\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uHpZwLtVC2L1W\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uMRllmNsRVG1o\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u4ujRnkupnFlj\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uDVcWyvWkqwYW\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uB7IK200eobfI\"/>\n            <listitem open=\"0\" type=\"814\" id=\"unrzu1eFC5Pxo\"/>\n            <listitem open=\"0\" type=\"814\" id=\"utKWF5gmv8PL4\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ufvz0vIGTa2nj\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u5DzNiNw527Ya\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uxQ3p1NHKuws9\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uIe82aahonVcq\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u3bfqXon87cEH\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uXJtOAqLsqtoB\"/>\n            <listitem open=\"0\" type=\"814\" id=\"usmi2swsHd2aK\"/>\n            <listitem open=\"0\" type=\"815\" id=\"usCPjuogb7zd0\"/>\n            <listitem open=\"0\" type=\"815\" id=\"urn6A5FySwSJ2\"/>\n            <listitem open=\"0\" type=\"815\" id=\"upiFyRkYsQsQb\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u4RGhQYFm0di3\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uM9EBAaIRspme\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u4y5Kb5RIO2nc\"/>\n            <listitem open=\"0\" type=\"815\" id=\"urb5Gr96yVPOd\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uRko7r3z6F3HM\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ur7yYa3ELe8cj\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ujkk2A5km2ZBH\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uKpmG6N75tGU1\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uAINWWcPlZLUP\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u2rmPpkpH7tBc\"/>\n            <listitem open=\"0\" type=\"815\" id=\"umuNHDleDfSzY\"/>\n            <listitem open=\"0\" type=\"815\" id=\"us7dOpCFz5lYV\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uHyibBVbzA2CA\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uDD9UxsSEsUAN\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uzcjVfbzVZabS\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ujj52ulemYwnW\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uu5xHJ1QMGGSV\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u3L2OrZTOhlBL\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u5EIGGulNQuA2\"/>\n            <listitem open=\"0\" type=\"815\" id=\"upRQOoQl7qMKE\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u2E6KmMuoV9hn\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uCjpyFjhI1tnA\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uAzctSHC99q8h\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uplilHsQqPQgm\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uvYXnhJyZDE1T\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uxx3QtH4eb4Jz\"/>\n            <listitem open=\"0\" type=\"814\" id=\"u5Was8PDiCPdr\"/>\n            <listitem open=\"0\" type=\"814\" id=\"utvrbLTEQdMkV\"/>\n            <listitem open=\"0\" type=\"815\" id=\"utvgGKXIv30tP\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uHrfbR5SlxrP7\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ujHi30b57PFzY\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uQibHjhXba3Ax\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uDPVTCt6sDNJt\"/>\n            <listitem open=\"0\" type=\"815\" id=\"um5y7IzYmnv3H\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uKpRoQyxwA5x1\"/>\n            <listitem open=\"0\" type=\"815\" id=\"umA4Jtd3wlL4C\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u2oZAqiQsYwqq\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uOFcfQycgiLIj\"/>\n            <listitem open=\"0\" type=\"815\" id=\"unjoOwoH4rWH6\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uuLSZpKbIxeHe\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uOv5kntY91n7W\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ums4RZPJ03HQY\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uRvWoIifVaCd3\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uoStfzWq36o14\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uGuHfntnF4Cgy\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u3G9pHjMeXEXp\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u3sIXfXYKB71O\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ur7Q4ucdP2Xwp\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uMIbhKa0VLaIC\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u35WxwgnANhhg\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uV8moWWsdXwuf\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uBir8XZxWtdbd\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uWmcCzHYPBUdp\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uyMrM0ODi2AFA\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uHKggbnN0OfyF\"/>\n            <listitem open=\"0\" type=\"815\" id=\"ubuoahmQkkeac\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u1ciPO4DzWZRH\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uLcsCsa4FkWvh\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uT4sbjKUWzKqU\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uBf0fqug7Rv8E\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uYmPEcwL1NJ8E\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u3P5vqBcxs7pg\"/>\n            <listitem open=\"0\" type=\"815\" id=\"usLw07cCIw68Z\"/>\n            <listitem open=\"0\" type=\"815\" id=\"urtS8bYgCepiX\"/>\n            <listitem open=\"0\" type=\"815\" id=\"unLZkgdrNd9Y8\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uBVmtOQBoMDUI\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uz5oLNnJqasqu\"/>\n            <listitem open=\"0\" type=\"814\" id=\"ueQ1G50h3YqPx\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uXXmUEtOKBdR8\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uq3VB2L6nFGnV\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uPnM8cJMO3rHh\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uP6XFCHnNvt5y\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uWq52Fq5AyR1Y\"/>\n            <listitem open=\"0\" type=\"815\" id=\"u0l02Mziy2K17\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uOdO7DUWspEKx\"/>\n            <listitem open=\"0\" type=\"815\" id=\"uPM2ELubqWfqF\"/>\n            <listitem open=\"0\" type=\"815\" id=\"unvvMBFz3ZGho\"/>\n            <listitem open=\"0\" type=\"814\" id=\"uwcyycdGSzGP7\"/>\n            <listitem open=\"0\" type=\"814\" id=\"unEZCsjeLkaNx\"/>\n           </listitem>\n          </listitem>\n         </listitem>\n        </listitem>\n       </listitem>\n       <listitem open=\"1\" type=\"818\" id=\"u2WdxlHB2wA0L\">\n        <listitem open=\"1\" type=\"818\" id=\"uX9BaE9REibFl\">\n         <listitem open=\"1\" type=\"818\" id=\"uIYdVPsDd9VR6\">\n          <listitem open=\"1\" type=\"813\" id=\"uOy5vwfVBULFm\"/>\n         </listitem>\n        </listitem>\n       </listitem>\n       <listitem open=\"1\" type=\"818\" id=\"uZRuDLDYHxpPC\">\n        <listitem open=\"1\" type=\"818\" id=\"uno0sjQA2tzkt\">\n         <listitem open=\"1\" type=\"818\" id=\"uDZ0GlmR1mAUw\">\n          <listitem open=\"1\" type=\"813\" id=\"uAx06XlkZTNOk\"/>\n         </listitem>\n        </listitem>\n       </listitem>\n       <listitem open=\"1\" type=\"818\" id=\"ujMJ43bkkTInI\">\n        <listitem open=\"1\" type=\"818\" id=\"uHU3MsMBTtx7K\">\n         <listitem open=\"1\" type=\"818\" id=\"u0YosNbnEUVM2\">\n          <listitem open=\"1\" type=\"817\" id=\"uEJFmMOXICKN7\"/>\n          <listitem open=\"0\" type=\"813\" id=\"uzWCo8LEDwhWm\"/>\n          <listitem open=\"1\" type=\"818\" id=\"uytz2uAR3ByM1\">\n           <listitem open=\"1\" type=\"813\" id=\"uURkF03A2hV3s\"/>\n          </listitem>\n          <listitem open=\"1\" type=\"818\" id=\"uHkkJjCLnm1IW\">\n           <listitem open=\"1\" type=\"813\" id=\"uiusqfusuWgNO\"/>\n          </listitem>\n         </listitem>\n         <listitem open=\"1\" type=\"818\" id=\"uHlGa2LDTlBSM\">\n          <listitem open=\"1\" type=\"813\" id=\"u6iXBajIxWLgp\"/>\n         </listitem>\n         <listitem open=\"1\" type=\"818\" id=\"uNdo1u4Mw1VXE\">\n          <listitem open=\"1\" type=\"813\" id=\"uO1HCIi4IK9G9\"/>\n         </listitem>\n        </listitem>\n       </listitem>\n      </listitem>\n     </listitem>\n     <listitem open=\"0\" type=\"830\" id=\"Datatypes\">\n      <listitem open=\"0\" type=\"829\" id=\"uaB8RGWK3cdfG\"/>\n      <listitem open=\"1\" type=\"829\" id=\"ufSMWKnE2MeDR\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uZ2yr2TrjD3GA\"/>\n      <listitem open=\"1\" type=\"829\" id=\"umHcdFGxhpF4Z\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uRZzAHIPLCRuA\"/>\n      <listitem open=\"0\" type=\"829\" id=\"uFxxkpTHChKiJ\"/>\n      <listitem open=\"1\" type=\"829\" id=\"u6tk8SyJrCt37\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uKOpKcOEyWLVb\"/>\n      <listitem open=\"0\" type=\"829\" id=\"ukkq8tRtTY75P\"/>\n      <listitem open=\"1\" type=\"829\" id=\"u0gPojjJf15k8\"/>\n      <listitem open=\"0\" type=\"829\" id=\"uMqpcflospVhz\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uos4nyGFPvZuP\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uCtN9ns0qBrY3\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uLFEAknyUWMcb\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uyD4Gl3q9XWmk\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uMKvVysVu0Dw8\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uSN2mGc2l4oHg\"/>\n      <listitem open=\"1\" type=\"829\" id=\"ufbCUnIy8X4Ut\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uFxlNnNgI1t9U\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uIpQxAQ8TJkfg\"/>\n      <listitem open=\"0\" type=\"829\" id=\"ufeZrdgdIImTs\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uBnaeiYgAyDqM\"/>\n      <listitem open=\"1\" type=\"829\" id=\"ucltgX28DoJai\"/>\n      <listitem open=\"1\" type=\"829\" id=\"ui0ufk0T6rdfJ\"/>\n      <listitem open=\"0\" type=\"829\" id=\"uOT5Fpdaa0ME6\"/>\n      <listitem open=\"0\" type=\"829\" id=\"u1q6D2001lkiB\"/>\n      <listitem open=\"1\" type=\"829\" id=\"u9Da8cRjcrXIz\"/>\n      <listitem open=\"1\" type=\"829\" id=\"udzAML1UMSHuq\"/>\n      <listitem open=\"1\" type=\"829\" id=\"u7z6tAmOM7F6e\"/>\n      <listitem open=\"0\" type=\"829\" id=\"u7mMnMZuapvmX\"/>\n      <listitem open=\"0\" type=\"829\" id=\"ufpOKsSTLaXxt\"/>\n      <listitem open=\"0\" type=\"829\" id=\"ubKowDNTQQR9g\"/>\n      <listitem open=\"0\" type=\"829\" id=\"uD8wHK1TPhWOW\"/>\n      <listitem open=\"0\" type=\"829\" id=\"uhEtAcAF3BKTh\"/>\n      <listitem open=\"0\" type=\"829\" id=\"umBnnAZHifTby\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uGIdZtfD3RmpK\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uVWjZZ48orN7M\"/>\n      <listitem open=\"1\" type=\"829\" id=\"uNc9xdgi1iMoa\"/>\n      <listitem open=\"0\" type=\"829\" id=\"uQ9ewF219o66n\"/>\n      <listitem open=\"0\" type=\"829\" id=\"uGl73Z6K2yCWv\"/>\n      <listitem open=\"0\" type=\"829\" id=\"uTJTyl7jK0GCk\"/>\n      <listitem open=\"0\" type=\"829\" id=\"u4uXiqlkY8h0U\"/>\n      <listitem open=\"0\" type=\"829\" id=\"u9QHhHY6pNByq\"/>\n     </listitem>\n     <listitem open=\"1\" type=\"813\" id=\"ul7aMnLiDXkuY\"/>\n     <listitem open=\"1\" type=\"818\" id=\"uz6SrY7VaXlYI\">\n      <listitem open=\"1\" type=\"818\" id=\"ux1Akw5ePg46z\">\n       <listitem open=\"0\" type=\"813\" id=\"un1gm5DfBo30C\"/>\n       <listitem open=\"0\" type=\"813\" id=\"uWQamAW1ki1ON\"/>\n       <listitem open=\"1\" type=\"813\" id=\"ueqGp2XxhfCAm\"/>\n      </listitem>\n      <listitem open=\"1\" type=\"818\" id=\"uScvPQbzBWlSr\">\n       <listitem open=\"1\" type=\"813\" id=\"u8C1qOpoOXZK5\"/>\n       <listitem open=\"1\" type=\"818\" id=\"uaQeI5mKo30Qt\">\n        <listitem open=\"1\" type=\"813\" id=\"uam7nyPRrHTSg\"/>\n       </listitem>\n       <listitem open=\"1\" type=\"813\" id=\"uzKRVm8e0nLQP\"/>\n       <listitem open=\"1\" type=\"813\" id=\"uQyeogSV0dP7h\"/>\n      </listitem>\n     </listitem>\n     <listitem open=\"0\" type=\"813\" id=\"uDcsaFusy8Y5S\"/>\n    </listitem>\n    <listitem open=\"1\" type=\"802\" id=\"Use_Case_View\"/>\n   </listitem>\n  </listview>\n  <codegeneration>\n   <codegenerator language=\"Java\"/>\n  </codegeneration>\n </XMI.extensions>\n</XMI>\n"
  },
  {
    "path": "en/distribute/build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"Distribute\" basedir=\".\">\n\n\t<import file=\"../build.xml\" />\n\n\t<property name=\"purpose.dir\" value=\"distribute\"/>\n\n</project>\n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/00-publishing-your-app-intro.markdown",
    "content": "---\nheader-id: publishing-your-app\n---\n\n# Publishing Your App\n\n[TOC levels=1-4]\n\nAs you develop apps, you may want to sell them to or share them with consumers.\nYou can distribute your apps on the\n[Liferay Marketplace](http://marketplace.liferay.com).\nThe process is straightforward, but must make some decisions along the way and\nprepare your app for publishing on the Marketplace. \n\nThis section's first tutorial explains Liferay Marketplace-related concepts and\ninforms you of decisions to make before starting the publishing process from\nyour portal. Then you'll learn how to get resources, such as your app's icon,\nscreenshots to show off your app, and a comprehensive description of your app,\nready for the publishing process. After that, you'll learn the step-by-step\nprocess of submitting your app to the Marketplace. Lastly, you'll learn how to\nmonitor your app's success on Liferay Marketplace and modify or add new versions\nof your app to the Marketplace. \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/01-planning-your-apps-distribution/00-planning-your-apps-distribution-intro.markdown",
    "content": "---\nheader-id: planning-your-apps-distribution\n---\n\n# Planning Your App's Distribution\n\n[TOC levels=1-4]\n\nWhen you start the formal process of submitting your app to the Marketplace, in\naddition to uploading your app's files you must answer a host of important\nquestions. For example, you must clarify who owns the app, specify pricing for\nthe app, define its licensing scheme (if it's a paid app), associate a person or\ncompany as its owner and maintainer, and specify the versions of Liferay that\nthe app supports. Your answers to these questions help you determine if you must\npackage multiple versions of the app. This tutorial prepares you by explaining\nthe questions and ways you might answer them. Here's what's covered:\n\n- [Selling your app or making it free](/how-to-publish/-/knowledge_base/publish/selling-your-app-or-making-it-free)\n- [Publishing as an individual or on behalf of a company](/how-to-publish/-/knowledge_base/publish/publishing-as-an-individual-or-on-behalf-of-a-company)\n- [Licensing and pricing your app](/how-to-publish/-/knowledge_base/publish/licensing-and-pricing-your-app)\n- [App versioning and target Liferay editions and versions](/how-to-publish/-/knowledge_base/publish/targeting-liferay-editions-and-versions)\n\nThe first tutorial answers this question: Should I sell my app or make it free? \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/01-planning-your-apps-distribution/01-selling-your-app-or-making-it-free.markdown",
    "content": "---\nheader-id: selling-your-app-or-making-it-free\n---\n\n# Selling Your App or Making it Free\n\n[TOC levels=1-4]\n\nDo you want to sell your app on the Marketplace? Or do you want to share\nit freely with anyone on the Marketplace? It's up to you. Most of the content that\nfollows describes options for paid apps (apps you sell). \n\nIf you're selling your app, you must publish using a *Paid App Account*. You\nmust also specify licensing, a price structure, and regional availability for\nyour paid apps.\n\nImportantly, you can't change the app from free to paid or from paid to free\nonce the app is published to the Marketplace. Offering the app in in both\nlicense types requires submitting another app under a different name (title). If\nyou want both free and paid licenses for your app, you must submit the app under\none name for free licenses and under another name for paid licenses. Make sure\nyou select the license type (i.e., free or paid) that's best for your app. \n\nHave you decided who to list as the app's author/owner? Have you decided manages\nthe app once it's on the Marketplace? App ownership options are explained next. \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/01-planning-your-apps-distribution/02-publishing-as-an-individual-or-on-behalf-of-a-company.markdown",
    "content": "---\nheader-id: publishing-as-an-individual-or-on-behalf-of-a-company\n---\n\n# Publishing as an Individual or on Behalf of a Company\n\n[TOC levels=1-4]\n\nYou can publish an app as yourself (an individual) or on behalf of a *company*.\nThis determines who is shown as the app's author and owner. Your selection also\ndetermines who can access the app behind the scenes, once it's published. \n\nThe default option is publishing on behalf of yourself. If you go with this\noption, your name shows as the app's author/owner in the Marketplace. The term\n*personal app* refers to an app published by an individual. That individual is\nthe only one who can manage the personal app. Managing an app includes such\nduties as adding new releases to it, adding new versions of it, and editing its\ndetails. \n\nPublishing on behalf of a company effectively hands the keys over to the\ncompany's administrators. The app shows on the company's Marketplace app\ndevelopment page and in the company's list of apps on the company's public\nprofile page. Company admins have the same permission that an individual author\nhas to manage the app (add new releases, new versions, edit details, etc.). The\ncompany's name alone is shown as the app's author/owner. \n\nYou can\n[register](https://www.liferay.com/marketplace/become-a-developer)\nyourself as a Marketplace Developer or one of your company's administrators can\nregister the company as a Marketplace Developer. You can either register for a\nFree Basic Account or register for a Paid App Developer Account. A Paid App\nDeveloper Account lets you submit paid apps to the Liferay Marketplace for sale\nto customers globally. It enables you to monetize the benefits of the\nMarketplace and the benefits of the Liferay distribution channel. The best part\nis, you can upgrade to a Paid App Developer Account if and when you want to\nsubmit a paid app to the Marketplace! Here's a comparison of the Marketplace\ndeveloper account options: \n\n<div class=\"row-fluid\">\n<div class=\"span4 thumbnail text-center\" style=\"padding:20px;\">\n<img alt=\"\" src=\"../../../images/wrench-hammer-icon.png\" />\n<h3>Free Basic Account</h3>\n\n<hr />\n<ul class=\"text-left\">\n\t<li>No cost to register as a Marketplace developer</li>\n\t<li>Distribute free apps on the Marketplace</li>\n\t<li>Access to developer-only resources (e.g., Liferay Dev Studio, Liferay Developer License)</li>\n\t<li>Participate in Marketplace promotions</li>\n</ul>\n</div>\n\n<div class=\"span4 thumbnail text-center\" style=\"padding:20px;\">\n<img alt=\"\" src=\"../../../images/arrows-icon.png\" />\n<h3>Converting from Basic-to-Paid</h3>\n\n<hr />\n<p style=\"text-align: left;\">When you're ready to submit a paid app to the Marketplace:</p>\n<ol class=\"text-left\">\n\t<li>Agree to the Developer Agreement</li>\n\t<li>Pay $99 annual fee</li>\n\t<li>Submit relevant tax documents (eg., W-9, W-8BEN) to receive payments</li>\n\t<li>Enter PayPal info to receive payments (PayPal account must be a Verified Business Account)</li>\n</ol>\n</div>\n\n<div class=\"span4 thumbnail text-center\" style=\"padding:20px;\">\n<img alt=\"\" src=\"../../../images/wrench-hammer-plus-icon.png\" />\n<h3>Paid App Developer Account</h3>\n\n<hr />\n<ul class=\"text-left\">\n\t<li>Upgrade when you're ready: only pay annual fee at time of paid app submission</li>\n\t<li>Offer paid licensing/support for your apps</li>\n\t<li>Receive 80% of app sales proceeds</li>\n\t<li>Access to detailed transaction history</li>\n\t<li>Paid apps customer management</li>\n\t<li>All the benefits of a Basic Developer Account</li>\n</ul>\n</div>\n</div>\n\nNow that you've determined your app's owner and you've registered an account to\nmanage the app, you can learn about licensing options for paid apps. \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/01-planning-your-apps-distribution/03-licensing-and-pricing-your-app.markdown",
    "content": "---\nheader-id: licensing-and-pricing-your-app\n---\n\n# Licensing and Pricing Your App\n\n[TOC levels=1-4]\n\nYou have significant control over how to price your app. You choose the license\nterm (perpetual vs. annual), choose the license type (standard vs. developer),\ndefine a pricing structure (pricing and bundled discounting based on a license\nunit), and specify regional availability. Even after your app is on the\nMarketplace, you can tweak general pricing or modify regional pricing. Here\nyou'll learn how to do all these things. \n\n## Determining a License Term\n\nHere are the license term options:\n\n- Perpetual license: does not expire.\n\n- Non-perpetual license: must be renewed annually.\n\nImportantly, you can't change your app's license terms once the app is approved.\nReleasing an approved app under a different license term requires submitting\nanother app under a new name (title). So make sure to think through the license\nterm that makes the most sense for your app. \n\n| **Note:** If you are a foreign developer based outside of the United States,\n| non-perpetual license sales (considered to be royalty income) to US customers\n| are subject to a 30% withholding tax. This tax does not apply to perpetual\n| licenses. For more information on licensing terms and fees, please refer to the\n| [FAQ](/faq).\n\n## Determining License Type and License Unit Pricing\n\nLicenses are set to run on a permitted number of Instance Units (defined as a\nsingle installation of the Liferay, which corresponds to one (1) Liferay `.war`\nfile). You can create tiers of bundled options to accommodate the number of\nInstance Units to offer to customers. You can also offer a discounted price for\na bundle of multiple Instance Units.\n\nThere are two types of licenses that you can offer for your app: standard\nlicenses and developer licenses. Standard licenses are intended for production\nserver environments. Developer licenses are limited to 10 unique IP addresses\nand, therefore, should not be used for full-scale production deployments. \n\n![Figure 1: Liferay Marketplace lets you specify different types of licenses and license quantities for pricing.](../../../images/licenseview.png)\n\nYou can also offer subscription services or 30-day trials. Support, maintenance,\nand updates should comprise subscription services.\n\nDepending on how you decide to license your app, you have a few options to\nconsider with regard to subscription services: \n\n**If you're offering a perpetual license...**\n\nYou can offer annually renewable subscription services. During the app\nsubmission process if you choose to *offer subscription services*, you're asked\nto price subscription services on a *per Instance Unit per year* basis. The\nfirst year of subscription services is included with a perpetual license. If a\ncustomer wants to continue receiving services after the first year, the customer\nmust renew subscription services at the price you designate. Customers are\nentitled to support, maintenance, and updates as long as they continue to\nannually renew subscription services. \n\nIf you choose not to offer subscription services, customers are entitled to only\napp updates, if and when updates become available. This type of one-time,\nupfront payment model may work well for less complex apps and themes/templates. \n\n**If you're offering a non-perpetual (annual/renewable) license...**\n\nYou can offer annually renewable subscription services. During the app\nsubmission process if you choose to *offer subscription services*, you should\nbuild the price of subscription services into the annual price of the\nnon-perpetual license; please take this into account as you price non-perpetual\nlicenses. In effect, the customer pays one price annually for both the app\nlicense and subscription services. The customer is entitled to support,\nmaintenance, and updates as long as they continue to renew their non-perpetual\nlicense.\n\nIf you choose not to offer subscription services, customers are entitled to only\napp updates if and when updates become available. They can receive updates as\nlong as they continue to have valid non-perpetual licenses. \n\n## Setting Prices for License Options by Region\n\nYou can specify countries your app will be available in and the app's price in\nin each of those countries. You can make it as simple (single price offered\nglobally) or as granular (different price in each country offered) as you want.\n\n![Figure 2: Liferay Marketplace lets you specify prices for all of your license options and lets you specify them by region.](../../../images/pricing_screenshot.png)\n\nChoose the currency to use with your pricing options. Decide on the renewal cost\nfor any support services you offer. The support services price is per instance,\nso if you specify $100 USD and the customer is running 10 instances, their\nannual support services renewal cost will be $1000. Note: This only applies to\nperpetual licenses. For non-perpetual licenses, include any support services\ncost in the annual license price. \n\nEven after an app has been approved, you can change the currency type and\ncurrency price of its license bundles, and you can modify regional availability\nof license bundles. \n\nAlthough Liferay Marketplace supports major currencies and a broad list of\ncountries, not all currencies and countries are currently available. Additional\ncurrencies and countries may become available at a later time. \n\n## Considering the Liferay Marketplace Fee\n\nBy selling your paid apps on the Liferay Marketplace, you're agreeing to share\napp sales revenue with Liferay. For each app sale, you receive 80% of the sales\nproceeds and Liferay receives 20% of the sales proceeds. We believe this type of\nfee structure is extremely competitive compared to other app marketplaces. \nLiferay uses its share of app sale proceeds to:\n\n- Operate and improve the Liferay Marketplace.\n- Provide developer services, such as payment processing, license tracking, and\nperformance metrics. \n- Continue investing in and growing the Liferay ecosystem.\n\n**Comparison of Liferay's App Revenue Sharing with that of Other App\nMarketplaces**\n\n<table class=\"revenue-sharing-table\">\n<tbody>\n<tr class=\"header\">\n<td></td>\n<td>Share to Developer</td>\n</tr>\n\n<tr class=\"highlight-row\">\n<td>Liferay Marketplace</td>\n<td>80%</td>\n</tr>\n<tr> <td>Atlassian Marketplace</td>\n<td>75%</td>\n</tr>\n\n<tr>\n<td>DNN Store</td>\n<td>75%</td>\n</tr>\n\n<tr>\n<td>Concrete 5</td>\n<td>75%</td>\n</tr>\n\n<tr>\n<td>Apple App Store</td>\n<td>70%</td>\n</tr>\n</tbody>\n</table>\n\nAs you can see, Liferay's fee is reasonable. \n\nYou need a valid PayPal account to receive payment; you're asked to provide this\ninfo when registering for or converting to a Paid App Developer Account.\nPayments are issued no later than 90 days after the transaction. \n\nNow that you've decided on licensing options and pricing, you can concentrate on\nthe Liferay versions your app runs on. \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/01-planning-your-apps-distribution/04-app-versioning-and-target-liferay-editions-and-versions.markdown",
    "content": "---\nheader-id: targeting-liferay-editions-and-versions\n---\n\n# Targeting Liferay Editions and Versions and Versioning your App\n\n[TOC levels=1-4]\n\nThere are multiple versions of Liferay and multiple editions (e.g., community\nand enterprise) for each version. You must decide the versions and editions to\nbuild your app on. And lastly, you must decide how to version your app. This\ntutorial covers these topics. \n\n## Determining Editions and Versions of Liferay to Target\n\nOf course, targeting the widest possible range of Liferay editions and versions\nin an app typically draws larger audiences to the app. And there may be certain\nfeatures in these editions and versions that you want to take advantage of. In\nyour app's plugin\n[packaging properties](/how-to-publish/-/knowledge_base/publish/preparing-your-app),\nspecify packaging directives to indicate the editions the app supports and the\nversion that the app supports. To ensure the widest audience for your app, make\nyour app compatible with both Liferay Digital Experience Platform (DXP) and\nLiferay Portal Community Edition (CE). \n\nYou can prepare a set of app files (including its\n`liferay-plugin-package.properties` file) for each version of Liferay you want\nto support. When uploading your app, you can specify which versions of Liferay\nyour app is compatible with and you can appropriately upload the sets of app\nfiles that are designed for those different versions. The next article in this\nguide explains how to go about\n[specifying packaging directives](/how-to-publish/-/knowledge_base/publish/preparing-your-app). \n\nNote that apps on Liferay Marketplace must be designed for Liferay Portal 6.1\nGA3 or later. That doesn't mean they can't work with prior versions. However,\nonly Liferay Portal 6.1 GA3 and later versions support installing apps directly\nfrom Marketplace and provide safeguards against malicious apps. If you want to\nuse an app with an earlier version of @product@, make sure that version provides\nthe dependencies your app needs. \n\nLastly, you should determine a versioning scheme for your app. How will you\nrefer to the first version of your app, the second version, and so on. \n\n## Decide on a Versioning Scheme\n\nA version of an app represents the functionality of the app at a given point in\ntime. When you first create an app, you give it an initial version (e.g.,\n`1.0`). On updating the app, you increment its version (e.g., from `1.0` to\n`1.1`), and you upload new files representing that version of the app. In some\ncases, you specify additional qualifiers in order to convey a special meaning.\nFor example, you may declare that the version of your app is always in x.y.z\nformat (where you've clearly defined the significance of x, y, and z). Liferay\nversions and official Liferay app versions use this format.\n\nIn any case, you're free to assign your app's version designators any way you\nlike. We recommend that you stick to a well known and easily understandable\nformat, such as `1.0`, `1.1`, `1.2`, and so on. Although you may want to include\nalphabetical characters (e.g., `1.0 Beta 2` or `6.3 Patch 123235-01`), we\ndiscourage it, as such characters may confuse customers as to how your app's\nversions relate to one another.\n\nKeep in mind that the releases of Liferay with which your app works must be\nspecified using Liferay's versioning scheme, as explained in\n[Understanding Liferay's Releases](/docs/7-2/deploy/-/knowledge_base/d/understanding-liferays-releases).\nSee the later section *Specify App Packaging Directives* for details on\nspecifying the releases of Liferay for which your app is designed. \n\nCongratulations on coming up with a sound game plan for your app! The next\narticles explain how to prepare apps for publishing. \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/02-preparing-your-app/00-preparing-your-app-intro.markdown",
    "content": "---\nheader-id: preparing-your-app\n---\n\n# Preparing Your App\n\n[TOC levels=1-4]\n\nAs a Liferay developer, you're undoubtedly already familiar with the concept of\nplugins (portlets, themes, etc.). If you're not familiar with Liferay\nplugins, see \n[Introduction to Liferay Development](/docs/7-2/appdev).\nA *Liferay App* (sometimes just called an *app*) is a collection of one or more\nof these plugins, packaged together to represent the full functionality of an\napplication on the Liferay platform. In addition to the plugins contained within\nan app, apps have metadata such as names, descriptions, versions, and other\ninformation that describe and track the app throughout its life cycle. \n\nMuch like standard Liferay plugins, Liferay apps are also auto-deployable.\nLiferay Marketplace apps are distributed via a special file type with an `.lpkg`\nextension. To deploy these files, drop them into a running Liferay instance's\nauto-deploy folder (`[Liferay_Home]/deploy`), like any other plugin. \n\nAs an app developer, you're not required to create the `.lpkg` files. Instead,\nyour app's individual plugins (WAR files for traditional plugins or JAR files\nfor OSGi modules) are uploaded as part of the publication process, along with\ninformation (name, description, version, icon, etc.) that identifies the app.\nThe publication process is described in detail later.\n\nAt this point in preparing to publish your app, you've developed your app. But\nbefore you start the formal publishing process, you must prepare your app's\nfiles and app metadata. \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/02-preparing-your-app/01-marketplace-app-metadata-guidelines.markdown",
    "content": "---\nheader-id: marketplace-app-metadata-guidelines\n---\n\n# Marketplace App Metadata Guidelines\n\n[TOC levels=1-4]\n\nThe following app metadata guidelines ensure that apps are submitted with\nimportant and necessary supporting information. The metadata that you submit\nwith your app serves both as necessary information for your app's buyers (e.g.,\nyour contact info) and as promotional assets (e.g., description, screenshots,\netc.) that can help drive traffic to and downloads of your app!\n\n![Figure 1: Check out how good your app can look on the Marketplace.](../../../images/dev-portal-app-metadata-guidelines.png)\n\nThink of a good name and description for your app. If you haven't already done\nso, take some screenshots, design an icon, and create a website for your app.\nThe table below helps you address the Marketplace app metadata requirements and\nproduce an appealing app advertisement.\n\n**Marketplace App Metadata Guidelines:**\n\n<style>\n.lego-image {\n\tmax-height: 100%;\n\tmax-width: 100%;\n}\n.max-960 {\n\tmargin: 0 auto;\n\tmax-width: 960px;\n}\n.no-max\n.max-960 {\n\tmax-width: none;\n}\n.metadata-guidelines-table td {\n\tborder-bottom: 1px solid;\n\tborder-top: 1px solid;\n\tpadding: 10px;\n}\n.table-header {\n\tfont-weight: bold;\n}\n.table-header.second {\n\twidth: 70%;\n}\n.left-header {\n\tborder-right: 1px solid;\n}\n</style>\n<div class=\"lego-article metadata-guidelines-table\" id=\"article-33460946\">\n<div class=\"lego-article-content max-960\">\n<div class=\"aui-helper-clearfix lego-section section-1\" >\n<div class=\"aui-w100 block-1 content-column lego-block\" >\n<div class=\"content-column-content\">\n<table>\n\t<thead>\n\t\t<td class=\"table-header left-header\">\n\t\t\tRequired Metadata\n\t\t</td>\n\t\t<td class=\"table-header second\">\n\t\t\tDescription\n\t\t</td>\n\t</thead>\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"table-header left-header\">\n\t\t\t\tApp Name\n\t\t\t</td>\n\t\t\t<td class=\"\">\n\t\t\t\tThis is probably the most important branding element of your\n\t\t\t\tapp, so be creative! Some important things to keep in mind:\n\t\t\t\t<ul>\n\t\t\t\t\t<li>\n\t\t\t\t\t\tIn some views within the Marketplace, app titles longer \n\t\t\t\t\t\tthan 18 characters are shortened with an ellipsis. \n\t\t\t\t\t</li>\n\t\t\t\t\t<li>Titles must not be longer than 50 characters.</li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\tApp title may contain the word \"Liferay\" to describe its\n\t\t\t\t\t\tuse or intent as long as the name does not imply\n\t\t\t\t\t\tofficial certification or validation from Liferay, Inc.\n\t\t\t\t\t\t(For example, names such as \"Exchange Connector for\n\t\t\t\t\t\tLiferay\" or \"Integration Connector Kit for Liferay\"\n\t\t\t\t\t\twould be allowed, while \"Liferay Mail Portlet,\" \"Liferay\n\t\t\t\t\t\tManagement Console,\" or \"Liferay UI Kit\" would not be\n\t\t\t\t\t\tpermissible without explicit approval). Please refer to\n\t\t\t\t\t\tour <a href=\"https://www.liferay.com/trademark\">\n\t\t\t\t\t\ttrademark policy</a> for details.\n\t\t\t\t\t</li>\n\t\t\t\t\t<li>\n\t\t\t\t\t    Please try to conform the app name as closely as\n\t\t\t\t\t    possible to the actual plugin (portlet, module, theme,\n\t\t\t\t\t    etc.) name.\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"table-header left-header\"> Support Information </td>\n\t\t\t<td class=\"\">\n\t\t\t\t<p>\n\t\t\t\t\tPlease include an email address, contact information, and/or\n\t\t\t\t\twebsite URL for the \"Support\" field. If there's an issue\n\t\t\t\t\twith your app, they should be a way to contact you.\n                </p>\n\t\t\t\t<p>\n\t\t\t\t\tIf you choose not to offer Support Services (by unchecking\n\t\t\t\t\t\"Offer Support\" during the app submission process), you\n\t\t\t\t\tmust provide support contact information so that buyers can\n\t\t\t\t\task you general questions about your app.\n                </p>\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"table-header left-header\"> Description </td>\n\t\t\t<td class=\"\">\n                <p>\n\t\t\t\t    Using English as the default language, you must provide a\n\t\t\t\t    description for your app. After providing the description in\n\t\t\t\t    English, you can provide other translations of the text. <p>\n\t\t\t\t    At a minimum, the description should provide a concise\n\t\t\t\t    overview of what the app does. Great descriptions also list\n\t\t\t\t    key functionality and what customers can expect to gain by\n\t\t\t\t    deploying your app. For a good description example, see the\n\t\t\t\t    <a\n\t\t\t\t    href=\"https://web.liferay.com/marketplace/-/mp/application/43707761\">Audience\n\t\t\t\t    Targeting</a> app on the\n\t\t\t\t    <a href=\"https://web.liferay.com/marketplace\">Marketplace</a>.\n                </p>\n\t\t\t\t<p>\n\t\t\t\t\tSpecify any plugin dependencies (plugins that must be\n\t\t\t\t\tinstalled prior to running your app) and environment\n\t\t\t\t\tcompatibilities (compatibility with specific app\n\t\t\t\t\tservers) here, so that potential buyers and the Liferay app\n\t\t\t\t\treview team are aware of these requirements.\n                </p>\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"table-header left-header\"> What's New?* </td>\n\t\t\t<td class=\"\">\n\t\t\t\tUsing English as the default language, describe what's new and\n\t\t\t\timproved in your app. After this, you can provide other \n\t\t\t\ttranslations of the text. This field is shown on updating an app \n\t\t\t\tthat you've already submitted, regardless of whether the app has \n\t\t\t\tbeen published.\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"table-header left-header\">\n\t\t\t\tCompatibility with Future Minor Releases of Liferay\n\t\t\t</td>\n\t\t\t<td class=\"\">\n\t\t\t\tPlease include a \"+\" at the end of the latest version when\n\t\t\t\tspecifying version constraints in your\n\t\t\t\tliferay-plugin-package.properties file (e.g.,\n\t\t\t\t\"liferay-versions=7.2.1+, 7.2.20+\"). This ensures that the app\n\t\t\t\tcontinues to be deployable to future @product@ versions within\n\t\t\t\ta minor release. If, in the future, you discover your app does\n\t\t\t\tNOT work with a particular version, you can modify the list to\n\t\t\t\texclude that version.\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"table-header left-header\">\n\t\t\t\tIncrease Your Potential User Base </td>\n\t\t\t<td class=\"\">\n                <p>\n\t\t\t\t    In most cases, an app compatible with Liferay Portal\n\t\t\t\t    CE also runs on Liferay Digital Experience Platform\n\t\t\t\t    (DXP) (or Liferay Portal EE), and vice versa. Specifying\n\t\t\t\t    compatibility with both DXP/EE and CE versions of Liferay\n\t\t\t\t    ensures a wider audience for your app!\n                </p>\n\t\t\t\t<p>\n\t\t\t\t\tYou can <a\n\t\t\t\t\thref=\"https://www.liferay.com/web/developer/marketplace/license\">request\n\t\t\t\t\ta Developer License </a> to support testing and confirm\n\t\t\t\t\tcompatibility.\n\t\t\t\t</p>\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"table-header left-header\"> Icon </td>\n\t\t\t<td class=\"\">\n\t\t\t\t<p>\n                    App icons must be exactly 90 pixels in both height and\n\t\t\t\t\twidth in PNG, JPG, or GIF format. The image size cannot\n\t\t\t\t\texceed 512kb. Animated images are prohibited.\n                </p>\n\t\t\t\t<p>\n\t\t\t\t\tThe use of the Liferay logo, including alternate versions\n\t\t\t\t\tof the Liferay logo, is permitted only with Liferay's\n\t\t\t\t\texplicit approval. Please refer to our <a\n\t\t\t\t\thref=\"https://www.liferay.com/trademark\">trademark\n\t\t\t\t\tpolicy</a> for details.\n\t\t\t\t</p>\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"table-header left-header\"> Screenshots </td>\n\t\t\t<td class=\"\"> App screenshots must not exceed 1080 pixels\n\t\t\t\tin width x 678 pixels in height and must be in the JPG format.\n\t\t\t\tEach screenshot's file size must not exceed 384KB. Each\n\t\t\t\tscreenshot should be the same size (each is automatically\n\t\t\t\tscaled to match the aspect ratio of the above dimensions), and\n\t\t\t\tit is preferable to name screenshots sequentially, for example\n\t\t\t\tfluffy-puppies-01.jpg, fluffy-puppies-02.jpg, and so on.\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n</div>\n</div>\n</div>\n</div>\n</div>\n\nMake sure your icons, images, descriptions, and tags are free of profanity or \nother offensive material.\n\nDuring the publication process, you upload your app's individual plugin WAR\nfiles and module JAR files along with the app's metadata (name, description,\nversion, icon, etc.).\n\n## Additional Requirements for Themes/Site Templates\n\nThemes and Site Templates must include sample content and optionally link to a\ndemo website that provides context. This ensures a uniform experience for\nMarketplace users: they download  a Theme or Site Template from Marketplace,\ninstall it, go to Sites or Site Templates in the Control Panel, and immediately\nsee the Theme or Site Template in action. \n\n| **Important:** Demo sites must be valid. Demo website, Theme, and Site Template sample content must not plagiarize or infringe on copyrights.\n\nThe\n[Resources Importer](/docs/7-2/frameworks/-/knowledge_base/f/importing-resources-with-a-theme)\nincludes files and web content that imports automatically into Liferay on Theme\nand Site Template deployment. Use the Resources Importer to include files/web\ncontent to provide a sample context for your Theme or Site Template.  The \n[Themes tutorials](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction)\nprovide details. \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/02-preparing-your-app/02-packaging-your-marketplace-app.markdown",
    "content": "---\nheader-id: packaging-your-marketplace-app\n---\n\n# Packaging Your Marketplace App\n\n[TOC levels=1-4]\n\nLiferay apps are \"normal\" Liferay plugins with additional information about\nthem. Therefore, most of the requirements are the same as those that exist for\nother Liferay plugins, as explained in the tutorials on creating\n[Liferay MVC Portlets](/docs/7-2/appdev/-/knowledge_base/a/liferay-mvc-portlet).\n\nIn addition to those requirements, there are some Marketplace-specific ones to\nkeep in mind:\n\n- **Target the Appropriate Java JRE**: Regardless of the tools you use to \n  develop your app, your app's bytecode must be compatible with the target Java\n  JRE for your version of Liferay. Apps are rejected if their bytecode is not\n  compatible with the Java JRE for the intended version of Liferay. In the\n  Compatibility Matrix for your Liferay version, make sure your app's bytecode\n  is compatible with the latest certified Java JDK version.\n\n  If you use any Gradle-based build environment like \n  [Liferay Workspace](/docs/7-2/reference/-/knowledge_base/r/liferay-workspace), make\n  sure your `targetCompatibility` is set to the latest certified Java JDK\n  version. \n\n  Similarly, if you use Maven, you can set the bytecode compatibility like this: \n\n```xml\n<properties>\n    <maven.compiler.source>1.8</maven.compiler.source>\n    <maven.compiler.target>1.8</maven.compiler.target>  \n</properties>\n```\n\n  If you use the \n  [Liferay Plugins SDK](/docs/6-2/tutorials/-/knowledge_base/t/plugins-sdk)\n  to develop your app for Liferay Portal 6.2 or 6.1, you can set the Java\n  version by overriding  the `ant.build.javac.target` property in the Plugins\n  SDK's `build.properties` file. \n\n- **WAR (`.war`) files**:\n    - WAR files must contain a `WEB-INF/liferay-plugin-package.properties` file.\n    - WAR files must not contain any `WEB-INF/liferay-plugin-package.xml` file.\n    - WAR file names must not contain any commas.\n    - WAR file names must conform to the following naming convention:\n\n      *context_name*`-`*plugin_type*`-A.B.C.D.war`\n\n      Where:\n\n    - *context_name*: Alpha-numeric (including `-` and `_`) short name of\n      your app. This name is used as the deployment context, and must not\n      duplicate any other app's context (you'll be warned if you use a\n      context name of any other app on the Marketplace).\n\n    - *plugin_type*: one of the following: `hook`, `layouttpl`,\n      `portlet`, `theme`, or `web`.\n\n    - `A.B.C.D`: The four digit version of your WAR file. Four digits must\n       be used.\n\n        Example: `myapp-portlet-1.0.0.0.war`\n\n- **`WEB-INF/`[`liferay-plugin-package.properties`](https://docs.liferay.com/ce/portal/7.1-latest/propertiesdoc/liferay-plugin-package_7_1_0.properties.html)\n  file:**\n    - Property `recommended.deployment.context` must not be set.\n    - Setting property `security-manager-enabled` to `true` is mandatory for all\n      paid apps that contain WAR-style plugins and that are for 6.1 EE/CE GA3\n      and 6.2 EE/CE; the setting is optional for free apps. Setting this\n      property to `true` enables Liferay's Plugin Security Manager. If you're\n      enabling the security manager, you must also define your Portal Access\n      Control List (PACL) in this file. Read\n      [Plugins Security and PACL](/docs/6-2/tutorials/-/knowledge_base/t/plugin-security-and-pacl)\n      for information on developing secure apps.\n- **Deployment contexts**:\n    - Liferay reserves the right to deny an application if any of its plugin\n      deployment contexts is the same as a context of another plugin in the\n      Marketplace.\n    - Liferay reserves the right to replace app plugin WAR files that have\n      the same deployment context as plugins built by Liferay.\n\nThere are some additional requirements for uploading Liferay DXP 7.x and Liferay\nPortal CE 7.x apps:\n\n- **OSGi modules in JAR (`.jar`) files**:\n    - The manifest file must at minimum contain the following manifest headers:\n        - `Bundle-SymbolicName`: a unique name for the module\n        - `Bundle-Version`: the module's version\n        - `Web-ContextPath`: the servlet context path\n  <!--  - For more information, see \n      [OSGi and Modularity - Modules](/docs/7-1/tutorials/-/knowledge_base/t/osgi-and-modularity). -->\n\n- **Apps containing multiple file types:**\n    - Liferay DXP 7.x and Liferay Portal CE 7.x apps can contain a mix of \n      WAR-based plugins and OSGi JAR-based plugins. Regardless of file type,\n      each plugin must be able to run on Liferay DXP 7.x and Liferay Portal CE\n      7.x. \n\n| **Important:** If you're developing a paid app or want your free app to satisfy\n| Plugin Security Manager on Liferay 6.2 or 6.1 GA3, make sure to specify PACLs\n| for your traditional WAR-style plugins. See the article\n| [Plugin Security and PACL](/docs/6-2/tutorials/-/knowledge_base/t/plugin-security-and-pacl)\n| for details. Give yourself adequate time to develop your app's permission\n| descriptors and time to test your app thoroughly with the security manager\n| enabled.\n\nApps usually consist of multiple plugins (e.g., multiple WAR or JAR files) and \nplugin types. In addition, you may want to consider how to package your app for \nrunning on different Liferay versions. \n\n## Considering Package Variations to Target Different Versions of Liferay\n\nApps can be written to work across many different versions of Liferay. For\nexample, suppose you want to publish version 1.0 of your app, which you're\nsupporting on Liferay Portal 6.1 and 6.2. Due to incompatibilities between these\nLiferay versions, it may be impossible to create a single binary WAR or JAR file\nthat works across both Liferay versions. In this case, you must compile your app\ntwice: once against Liferay Portal 6.1 and once against 6.2, producing two\ndifferent *packages* (also called variations) of your version 1.0 app. Each\npackage has the same functionality, but they're different files. You can upload\nsuch packages to support your app on different Liferay versions. Packages are\nsometimes referred to as files that make up your app. \n\nNow that you've prepared your app's files and specified its metadata, it's\ntime to submit it to Liferay for publishing on the Marketplace! \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/03-submitting-your-app/00-submitting-your-app-intro.markdown",
    "content": "---\nheader-id: submitting-your-app\n---\n\n# Submitting Your App\n\n[TOC levels=1-4]\n\nNow that you have everything in order for publishing your app, you can start the\nMarketplace app submission process. \n\nGo to your *Account Home* on \n[liferay.com](http://www.liferay.com). \nIn the left side navigation panel of your profile page, there are links to pages\nrelated to using apps and developing apps. Links to *Apps* and *Metrics* appear\nin the *Development* section of the navigation panel. You'll use these\nlinks heavily during development, so you may want to bookmark this page too.\nClick *Apps* from the *Development* section to access your app development page. \n\n![Figure 1: Your app development page lists the apps you've developed and enables you to add new apps for publishing to the Marketplace.](../../../images/marketplace-my-app-manager.png) \n\nNow that you know how to get to your app development page, you can submit your\napp! It involves,\n\n- [Specifying your app's initial details](/how-to-publish/-/knowledge_base/publish/specify-your-apps-initial-details)\n- [Uploading your app](/how-to-publish/-/knowledge_base/publish/uploading-your-app)\n- [Creating your licensing and pricing model](/how-to-publish/-/knowledge_base/publish/creating-your-licensing-and-pricing-model)\n- [Submitting your app for review](/how-to-publish/-/knowledge_base/publish/submitting-your-app-for-review)\n\nTo begin the process of publishing your app, click *Add New App*. The app wizard\nappears, so that you can fill in your app's details.\n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/03-submitting-your-app/01-your-apps-initial-details.markdown",
    "content": "---\nheader-id: specify-your-apps-initial-details\n---\n\n# Specifying Your App's Initial Details\n\n[TOC levels=1-4]\n\nFrom this screen you enter your app's basic details. Previous articles\n[Planning Your App's Distribution](/how-to-publish/-/knowledge_base/publish/planning-your-apps-distribution)\nand\n[Preparing Your App](/how-to-publish/-/knowledge_base/publish/preparing-your-app)\nhelped prepare you for filling in these details. \n\nThe top portion of the app wizard's initial screen is shown in the figure below.\n\n![Figure 1: The app wizard lets you add details about your app, an icon, and screen shots. Scroll down further to see options for specifying relevant URLs, adding tags, and specifying the editions of Liferay that your app supports.](../../../images/marketplace-add-app-details.png) \n\nHere are descriptions of this screen's fields:\n\n**App Owner:** Choose to whom the app \"belongs\" once it is uploaded--either\nyourself (personal) or a company. If you'd like to submit on behalf of a\ncompany, click *Create a company* to go to the *Join A Company* page. From this\npage, search to see if your company already exists in the Liferay Marketplace.\nIf you wish to publish your app on behalf of your company, but your company does\nnot yet have a Marketplace profile, register your company by clicking *Register\nMy Company*, filling out the registration form, and submitting the form. \n\n**App Pricing:** Choose whether you want the app to be free or paid. If you\nchoose paid, you'll have the option to specify pricing and licensing details\nlater in the submission process. \n\nImportantly, you can't change the app from free to paid or from paid to free\nonce the app is published to the Marketplace. In order to offer the app in the\nother license type, you must submit another app under a different name (title).\nIf you wish to have both free and paid licenses for your app, you must submit\nthe app under one name for free licenses and submit it under another name for\npaid licenses. Make sure to select the license type (i.e., free or paid) that's\nbest for your app. \n\n**Title:** Name of your application. \n\n**Description:** Like the name says, this is a description of your app. You can\nput anything you want here, but a good guideline is no more than 4-5 paragraphs.\nThis field does not allow any markup tags or other textual adornments--it's just\ntext.\n\n**Localized:** Selecting this displays an *Add Translation* button that lets \nyou denote translations your app provides.\n\n**Icon:** Upload an icon image to represent your app.\n\n**Screen Captures:** Upload one or more screenshots of your app. The screenshots\nyou upload here are displayed in a carousel of rotating images when your app is\nviewed on the Marketplace. \n\n**Category:** Choose the Marketplace category that most accurately describes\nwhat your app does. Users looking for specific types of apps often browse\ncategories by clicking on a specific category name in the main Marketplace home\npage. Having your app listed under the appropriate category helps them find\nyour app.\n\n**Developer Website URL:** This is a URL that should reference the web site\nassociated with the development of the app. For open source apps, this typically\npoints at the project site where the source is maintained. For others, links to\na home page of information about this app would be appropriate.\n\n**Support URL:** This is a URL that should reference a location where your app's\npurchasers or users can get support.\n\n**Documentation URL:** What better way to showcase the amazing capabilities of\nyour app than to have a live, running version of it for potential buyers to see\nand use? This field can house a URL pointing to something as exciting as that\nand/or documentation for using your app. \n\n**API Reference URL:** This is a URL that should reference your app's API \ndocumentation (e.g., Javadoc). \n\n**Source Code URL:** If you'd to provide a link to the source code of your app,\ndo so here.\n\n**Labs:** You can denote an app as experimental by checking the appropriate box.\n\n**Security:** If your app is only for Liferay 7.0 or later, or does not contain \nWAR-style plugins, or does *not* use Liferay's PACL Security Manager, check the\nappropriate box. Otherwise, enable the security manager in your app by including\nthe setting `security-manager-enabled=true` in the\n[`liferay-plugin-package.properties`](http://docs.liferay.com/portal/6.2/propertiesdoc/liferay-plugin-package_6_2_0.properties.html)\nfile in your plugin WAR files. \n\n**Tags:** A set of descriptive words that categorize your app. These tags are\nfree-form and can help potential purchasers find your app through keyword\nsearches, tag clouds, and other search mechanisms. You can click on\n*Suggestions* to let Marketplace suggest some tags for you based on the data\nyou've already entered. Click *Select* to select from existing tags, or you can\nmanually type in new tags. \n\n**EULA:** You can use the default end user license agreement (EULA) or provide\nyour own. There's a link to the minimum terms which custom EULAs must satisfy.\n\nRemember to visit the articles\n[Planning Your App's Distribution](/how-to-publish/-/knowledge_base/publish/planning-your-apps-distribution)\nand\n[Preparing Your App](/how-to-publish/-/knowledge_base/publish/preparing-your-app)\nfor information to help you decide how best to fill in many of these fields. \n\nOnce you've entered all your app's details, click *Next* to get to the screen\nfor uploading your app's files. \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/03-submitting-your-app/02-uploading-your-app.markdown",
    "content": "---\nheader-id: uploading-your-app\n---\n\n# Uploading Your App\n\n[TOC levels=1-4]\n\nOn this screen, you must specify your app's version and upload your app's files. \nNote that the article \n[Planning Your App's Distribution](/how-to-publish/-/knowledge_base/publish/planning-your-apps-distribution) \nhelps you plan your app's versioning scheme. Likewise, the article \n[Preparing Your App](/how-to-publish/-/knowledge_base/publish/preparing-your-app) \nhelps you determine which files to upload. \n\nTo start uploading, click *Browse* and select the files that make up your app. \nEach time you add a file, it automatically begins to upload and its \ncompatibility information is scanned. You must upload at least one file before \nadvancing beyond this screen. Once the files are successfully uploaded, a check \nmark appears next to each plugin, and the plugins are displayed based on their \ncompatibility information. \n\n![Figure 1: Specify a set of files for each Liferay version you wish to support.](../../../images/marketplace-app-version-and-upload-files.png) \n\nIf you selected *Free* for your app pricing, click *Next* to advance to the \nfinal screen. If you selected *Paid*, you'll be presented with additional \noptions for licensing and pricing your app. \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/03-submitting-your-app/03-creating-your-licensing-and-pricing-model.markdown",
    "content": "---\nheader-id: creating-your-licensing-and-pricing-model\n---\n\n# Creating Your Licensing and Pricing Model\n\n[TOC levels=1-4]\n\nCarefully consider which licensing structure best meets your needs. Once your\napp has been approved, these options, except for price updates, cannot be\nchanged. \n\n**Choose a license term:**\n\n![Figure 1: Choosing license terms for Marketplace apps is easy.](../../../images/marketplace-configure-app-license.png)\n\nChoosing *Perpetual* allows the app to continue running without expiration.\nChoosing *Non-Perpetual* expires the app's license one year from the purchase\ndate. Perpetual License also allows you to offer Support Services, which the\ncustomer must renew annually to maintain access to app updates and support. If\nyou choose not to offer Support Services with a Perpetual License, customers\nwill be provided with app updates only, whenever updates are available. \n\nImportantly, you can't change your app's license terms (perpetual or\nnon-perpetual) once the app is approved. In order to release an approved app\nunder a different license term you must submit another app under a new name\n(title). So make sure you think through the license term that makes the most\nsense for your app. \n\n**Creating license options:**\n\n![Figure 2: You can create multiple license options for your Marketplace apps.](../../../images/marketplace-create-license-types.png)\n\nCreating license options allows you to design license bundles and to specify\ndiscounts for customers who purchase more Liferay Instances for your app (a\nLiferay Instance or Instance refers to a single installation of the Liferay\nPortal). Also you can designate different pricing for Standard Licenses vs.\nDeveloper Licenses. You must specify at least one license option, but no more\nthan 10 options per type. You'll price these options on the next page. \n\nYou can add or remove bundles (quantities) of your app's license type even after\nthe app is approved. You must however, always honor any support agreements you\nhave with current customers that are using bundles you've removed.\n\n**Paid support:** You can offer additional paid support services for your app.\nIf you select this option, customers can contact you with support requests and\nare entitled to regular updates. \n\nOnce an app that offers a support subscription offering is approved, you can't\nremove that support subscription offering. You can however, add a support\nsubscription offering to an approved app. \n\n**Offer a trial:** You can offer a free 30-day trial of your app, restricted to\none Instance and 25 users. \n\nWhen you're finished selecting all the options for your license, proceed to the\nnext page to determine the app's pricing and availability.\n\n**Pricing:**\n\n![Figure 3: Liferay makes it easy to price your app's license types and specify their availability to countries around the world.](../../../images/marketplace-app-pricing.png) \n\nBased on your selections from the previous page, you'll have price fields for\neach license option and for any support option you offered. \n\nWhen you have completed your app's pricing and availability, click *Next* to\nadvance to the app preview screen. \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/03-submitting-your-app/04-submitting-your-app-for-review.markdown",
    "content": "---\nheader-id: submitting-your-app-for-review\n---\n\n# Submitting Your App For Review\n\n[TOC levels=1-4]\n\nThe *App Preview* screen lets you preview your app as it will appear on the\nMarketplace. If you want to change things before finally submitting your app,\nclick *Edit* to go back and continue making changes until you're satisfied.\n\nBefore finalizing your app's submission, make sure that you're pleased with the\nvalues you've set for the following app details, as these details can't be\nmodified once the app is approved: \n\n- Free vs. paid\n- License term (perpetual vs. non-perpetual)\n- Support subscription offering (cannot be removed)\n\nOnce you are satisfied, click *Submit for Review*. \n\nAt this point, the Liferay Marketplace staff starts to review your app and test\nit. If you need to make changes to your app during its review or if you're\ncurious about the rigors of the review process, make sure to read the next\nsection of this guide.\n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/04-understanding-the-app-review-process/00-understanding-the-app-review-process-intro.markdown",
    "content": "---\nheader-id: understanding-the-app-review-process\n---\n\n# Understanding the App Review Process\n\n[TOC levels=1-4]\n\nThe Liferay Marketplace app QA/review process begins as soon as you submit your\napp for review. Every third-party app submitted to the Liferay Marketplace is\nreviewed by our team to ensure that certain standards for information are upheld\nand the app installs as expected. *Liferay cannot, however, be a substitute for\nyour own testing and debugging team*. Ultimately, you must test, refine, and\nensure that your app functions as promised and performs as expected.\n\n| **Note:** Liferay is not responsible for the behavior (or misbehavior) of apps\n| on the Marketplace. For details regarding this, consult the *Liferay Marketplace\n| User Agreement*, *Liferay Marketplace Developer Agreement*, and the individual\n| *End User License Agreements* associated with each app.\n\nOnce you've submitted your app for review, your app's status changes as it moves\nthrough the review process. We email you on status changes and provide as much\ndetail as we can if we discover potential issues with your app. Overall, we\ndon't want our app review process to feel like a barrier or a black box. We love\nhaving new apps in the Marketplace, and we try to be as helpful as we can\nthroughout the approval process!\n\n![Figure 1: Liferay informs you at every step during the QA/review process.](../../../images/app_review_process.png)\n\nIf you submit an updated version of a previously approved app, you'll see these\nstatuses: Approved (Version Unsubmitted), Approved (Version Pending), Approved\n(Version Pending QA), and Approved (Version Denied).\n\nThe app review process consists of **Two Major Phases**:\n\n<style>\n.lego-image {\n    max-height: 100%;\n    max-width: 100%;\n}\n.max-960 {\n    margin: 0 auto;\n    max-width: 960px;\n}\n.no-max .max-960 {\n    max-width: none;\n}\n.app-review-process-table {\n    padding-bottom: 40px;\n}\n.app-review-process-table thead td {\n    font-weight: bold;\n}\n.app-review-process-table .first-column {\n    border-left: none;\n}\n.app-review-process-table .second-column {\n    border-right: none;\n    width: 156px;\n}\n.app-review-process-table td {\n    border : 1px solid;\n    padding: 15px;\n}\n</style>\n<div class=\"lego-article app-review-process-table\" id=\"article-33460874\">\n<div class=\"lego-article-content max-960\">\n<div class=\"aui-helper-clearfix lego-section section-1\" >\n<div class=\"aui-w100 block-1 content-column lego-block\" >\n<div class=\"content-column-content\">\n<table>\n    <thead>\n        <td class=\"first-column\"> <strong> Review Phase </strong> </td>\n        <td class=\"second-column\"> <strong> Est. Time Frame </strong> </td>\n    </thead>\n    <tbody>\n        <tr>\n            <td class=\"first-column\">\n                <strong> App metadata review </strong>\n                <p>\n                    Our team\n                    <a href=\"/distribute/how-to-publish/-/knowledge_base/how-to-publish/preparing-your-app#marketplace-app-metadata-guidelines\">\n                    reviews your app's metadata</a>\n                    to confirm that titles, descriptions, images, etc. are\n                    appropriate.\n                </p>\n            </td>\n            <td class=\"second-column\"> ~1 week </td>\n        </tr>\n        <tr>\n            <td class=\"first-column\">\n                <strong> App QA test </strong>\n                <p> Liferay ensures that apps meet a minimal set of\n                    requirements: </p>\n                <ul>\n                    <li>Passes anti-virus scan</li>\n                    <li>Deploys successfully on standard Liferay supported\n                        environments/platforms without errors.</li>\n                    <li>Basic functionality \"smoke\" test.</li>\n                </ul>\n                <p>\n                    Liferay does not do source code review and does not ask for\n                    your source code. Further, Liferay is not responsible for\n                    the behavior (or misbehavior) of apps on the Marketplace.\n                    Please consult the Liferay Marketplace User Agreement,\n                    Liferay Marketplace Developer Agreement, and the individual\n                    End User License Agreements associated with each app.\n                </p>\n            </td>\n            <td class=\"second-column\"> ~1-2 weeks </td>\n        </tr>\n    </tbody>\n</table>\n</div>\n</div>\n</div>\n</div>\n</div>\n\n**Our QA Test Environments** are summarized below. At a minimum, test your app\nagainst these environments prior to submission. If technical reasons prevent\nyour app from running on certain platforms (e.g., app server-specific issues),\nspecify compatibility in the app description and documentation so that our\nreview team can exclude certain test conditions, if necessary.\n\n<style>\ntable td {\n    vertical-align: top;\n}\n.lego-image {\n    max-height: 100%;\n    max-width: 100%;\n}\n.max-960 {\n    margin: 0 auto;\n    max-width: 960px;\n}\n.no-max .max-960 {\n    max-width: none;\n}\n.test-environments-table td {\n    border : 1px solid;\n    padding: 15px;\n}\n.test-environments-table td.empty {\n    border-left: none;\n}\n.test-environments-table thead td {\n    text-align: center;\n}\n.test-environments-table .first-column {\n    border-left: none;\n    font-weight: bold;\n}\n.test-environments-table .third-column {\n    border-right: none;\n}\n</style>\n<div class=\"lego-article test-environments-table\" id=\"article-33460919\">\n<div class=\"lego-article-content max-960\">\n<div class=\"aui-helper-clearfix lego-section section-1\" >\n<div class=\"aui-w100 block-1 content-column lego-block\" >\n<div class=\"content-column-content\">\n<table>\n    <thead>\n        <td class=\"first-column\">\n            <strong>Liferay Version</strong>\n        </td>\n        <td class=\"second-column\">\n            <strong>6.x</strong>\n        </td>\n        <td class=\"third-column\">\n            <strong>7.0.x</strong>\n        </td>\n        <td class=\"fourth-column\">\n            <strong>7.1.x</strong>\n        </td>\n        <td class=\"fifth-column\">\n            <strong>7.2.x</strong>\n        </td>\n        <td class=\"sixth-column\">\n            <strong>7.3.x</strong>\n        </td>\n    </thead>\n    <tbody>\n        <tr>\n            <td class=\"first-column\"> Operating Systems </td>\n            <td class=\"second-column\" colspan=\"5\">Ubuntu 11x and Windows 10 x64</td>\n        </tr>\n        <tr>\n            <td class=\"first-column\"> Database </td>\n            <td class=\"second-column\">MySQL 5.5.x</td>\n            <td class=\"third-column\">MySQL 5.6.x</td>\n            <td class=\"fourth-column\" colspan=\"3\">MySQL 5.7.x</td>\n        </tr>\n        <tr>\n            <td class=\"first-column\"> Application Server * </td>\n            <td class=\"second-column\">Tomcat 7</td>\n            <td class=\"third-column\">Tomcat 8</td>\n            <td class=\"fourth-column\" colspan=\"3\">Tomcat 9</td> \n        </tr>\n        <tr>\n            <td class=\"first-column\"> JDK </td>\n            <td class=\"second-column\">Oracle JDK 6, 7</td>\n            <td class=\"third-column\" colspan=\"3\">Oracle JDK 8 / JDK 11 (7.1 and higher)</td>\n        </tr>\n        <tr>\n            <td class=\"first-column\"> Browser </td>\n            <td class=\"second-column\" colspan=\"5\">Chrome</td>\n        </tr>\n    </tbody>\n</table>\n</div>\n</div>\n</div>\n</div>\n</div>\n\n**\\*** You can request certification on additional, optional application \nservers. For Liferay 7.x, Wildfly 10 is available. For Liferay 6.x, Glassfish\n3.1 and an appropriate JBoss version are available. Please add the request in\nthe app submission panel's *Note to testers* section.\n\nOnce your app is approved by Marketplace staff, you'll get email notification.\nWhen your app is approved, it is made available on Marketplace. The app also\nappears on your public Profile page, which lists all apps that you or your\ncompany developed and published.\n\nIf your app is rejected, you receive an email explaining the reasons for\nrejection. You can then make the requested changes and re-submit the app for\napproval.\n\nAfter you've successfully published your app, you might get all kinds of\nfeedback from users and yourself about what's right and wrong with it. The next\narticle explores how to make changes once you have published your app.\n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/05-managing-your-published-app/00-managing-your-app-intro.markdown",
    "content": "---\nheader-id: managing-your-published-app\n---\n\n# Managing Your Published App\n\n[TOC levels=1-4]\n\nYou've launched your new app on the Marketplace. Congratulations! As you settle\ndown from your launch celebration, you might wonder how your app is performing\nin the Marketplace. Is there a way to monitor how many people are viewing it and\ndownloading it? And what about those features that didn't make this release? How\ndifficult is it to upload your app's next version with those features? Don't\nsweat it. The Liferay Marketplace and app wizard are here to help. \n\nYou can track app performance. Marketplace shows app trends like views,\ndownloads, and purchases. You can upload new versions of your app and make\nchanges to some of its details with the App Wizard. Read on to learn how all of\nthis works. \n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/05-managing-your-published-app/01-tracking-app-performance.markdown",
    "content": "---\nheader-id: tracking-app-performance\n---\n\n# Tracking App Performance\n\n[TOC levels=1-4]\n\nOne of the main reasons for developing and publishing apps on Marketplace is to\ndrive app downloads and adoption. Marketplace gives you detailed reports on\nviews, downloads, and purchases of your app(s). To access these metrics,\nnavigate to *Account Home* &rarr; *Metrics* (under *Development*). \n\n![Figure 1: The App Performance view in Marketplace lets you see how many times your apps have been viewed, downloaded, and purchased over a time interval.](../../../images/marketplace-app-metrics-over-time.png) \n\nThe view shown above is the default metrics view for a developer's apps. Across\nthe bottom is a list of data series options (*Views*, *Downloads*, or\n*Purchases*). At the top, you set a date range. In the middle, a graph shows the\ndata within the date range. Finally, the graphed data is also shown in tabular\nformat, in case you want to know the exact values making up the graph. The\ndifferent types of data available to view are described below.\n\n## Views\n\nWhen someone searches or browses the Marketplace, they click on apps to see\ntheir details. When this occurs for an app, Marketplace records a *View* for\nit. When you select *Views* in the App Metrics screen, the app view count over\ntime appears. The App Metrics screen shows *Views* by default. The number of\npossible views per day per user is unlimited.\n\n## Downloads\n\nMarketplace records a *Download* for your app when someone downloads any package\nof any version of your app. The number of possible downloads per day per user is\nunlimited.\n\n## Purchases\n\nThe Marketplace counts app purchases too. \n\nAs you see areas to improve your app or when necessary changes come up, you can\nmake changes to your published app. The next article describes changes you can\nmake.\n"
  },
  {
    "path": "en/distribute/publish/articles/01-publishing-your-app/05-managing-your-published-app/02-making-changes-to-published-apps.markdown",
    "content": "---\nheader-id: making-changes-to-published-apps\n---\n\n# Making Changes to Published Apps\n\n[TOC levels=1-4]\n\nYou can make these kinds of changes during the life of your published app:\n\n- Edit your app details (e.g., description, icon, etc.)\n- Add new license bundles (quantities)\n- Edit app prices of bundles\n- Modify regional availability\n- Add support for a new version of Liferay\n- Release a new version of your app to fix bugs or offer new functionality\n- Disable your app\n\nYour currently approved app remains available while you're submitting changes\nand while Liferay reviews and tests your app changes. \n\nThe rest of this article describes each kind of change. \n\n## Editing Your App's Metadata\n\nApp metadata includes the name, description, icon, screenshots, and other\ninformation you supplied during the app creation process. Here's how to edit\nyour app's metadata:\n\n1.  Navigate to *Account Home* &rarr; *Development* &rarr; *Apps*. A listing of \n    your published apps appears. \n\n2.  Select the app you wish to edit. This screen shows you what the app looks \n    like on Marketplace. \n\n3.  Click the *Edit* button at the bottom of the preview. Now you can edit \n    details and add new files to the your existing version of your app. The\n    current values as they appear in your app pre-populate the form. \n\n4.  Make any changes as needed on this screen.\n\n5.  Click *Next*. If you don't need to edit any more variations, you can \n    continue clicking *Next* until you reach the final preview screen.\n\n6.  Click *Submit for Review* to submit your changes for review.\n\nAll changes must go through the submission process. If your app revision only\nconsists of metadata changes, it need only go through the \n[*App metadata review*](/how-to-publish/-/knowledge_base/publish/understanding-the-app-review-process)\nphase of the review process. \n\nOnce approved, the changes you request appear for your app on the Marketplace.\n\n## Editing App Prices\n\nYou can change your app's prices, add or remove bundles, and modify regional\navailability for a variety of reasons, whether it's to run a promotional offer,\nor to adjust your pricing model to better account for app demand. Here's how to\nedit app prices:\n\n1.  Navigate to *Company Profile Home* &rarr; *Apps*. A listing of your \n    published apps appears. \n\n2.  Select the app you wish to edit.\n\n3.  At the bottom, click *Edit* &rarr; *Pricing*.\n\n4.  Edit your app's prices.\n\n5.  When you've finished editing your app's prices, click *Next* and save your \n    app. \n\nThese changes do not require Liferay verification process to approve. The\nchanges show immediately. \n\nYou cannot however, modify the following attributes of an approved app: \n\n- Free vs. paid\n- License term (perpetual vs. non-perpetual)\n- Support subscription offering (cannot be removed)\n\nNext, you can consider adding support for new versions of Liferay. \n\n## Adding Support for New Versions of Liferay\n\nIf you must add files to support another Liferay release, the process is similar\nto editing an app.\n\n1.  Navigate to *Account Home* &rarr; *Apps*. A listing of your published apps \n    appears. \n\n2.  Select the app you wish to edit. \n\n3.  Click the *Edit* button to edit that app. \n\n4.  Click *Next* to advance past the details screen (make any necessary \n    changes). The version edit screen appears. \n\n5.  Click *Next* to advance past the version edit screen (you can't actually \n    edit the version number of an already-approved version, but you can edit the\n    \"What's New\" information). The File Upload screen appears.\n\n    This screen should look familiar---it's the same workflow used when you\n    initially created your app! The difference is that you can't edit\n    pre-approved files for specific Liferay releases. You can only add *new*\n    files for a different Liferay release (if you must update existing files,\n    you must create a new version of the app---see the later section on adding\n    versions for details on how to do this).\n\n6.  Upload your new files (ensuring that your new plugins have updated \n    compatibility information: see the\n    [Marketplace App Metadata Guidelines](/how-to-publish/-/knowledge_base/publish/preparing-your-app)\n    for details on versions). \n\n7.  Click *Next*, and observe the newly-added files listed at the bottom of the \n    preview screen.\n\n8.  Click *Submit for Review* to submit your requested change (adding files). \n\nLiferay reviews the files, and once approved, the new package becomes available\nfor download in the Marketplace.\n\n## Releasing a New Version of your App\n\nAs time passes, you may wish to add new functionality to your app or fix a batch\nof bugs. This can be accomplished by releasing a new version of your app. New\nversions offer your users new functionality and bug fixes, and users are\ngenerally encouraged to use the latest version. In addition, when a new version\nof your app becomes available, Marketplace notifies existing users. \n\nNew versions of your apps are created in a similar fashion to creating new apps.\n\n1.  Navigate to *Account Home* &rarr; *Apps*. A listing of your published apps \n    appears. \n\n2.  Select the app you wish to edit. \n\n3.  Click the *Edit* button to edit that app. The Details screen appears.\n\n4.  At the bottom of the Details screen, click the *Add New Version* button. The\n    process of adding a new version begins, starting with the App Details\n    screen. Data from the current version of the app pre-populates the screen. \n\n    You can modify the screen's pre-populated data. Since this is a new version\n    of an existing app, making major changes (such as completely changing the\n    name or description) might unsettle existing users. Uploading new\n    screenshots and refreshing app icons is common. Note that you cannot change\n    the app owner (such as moving from a personally-developed app to a\n    company-developed app).\n\n5.  Click *Next* until you get to the *Add App Version* screen. \n\n6.  Specify a new version name for this version of your app.\n\n7.  Add *What's New* text (optional). Common changes to mention are new features\n    and bug fix information. \n\n8.  Click *Next*. The file upload screen appears. \n\n9.  Upload the files associated with the new version of the app. You must upload\n    all files for all supported Liferay versions again, even if they have not\n    changed since the last version of your app. \n\n10. Continue clicking *Next* until you reach the final preview screen.\n \n11. Click *Submit for Review* to submit the new version of your app for review.\n\nThe Liferay Marketplace staff starts to review your app's new version and test\nit. \n\n## Deactivating Your App\n\nWhen the time comes to retire your app, you can *Deactivate* it. Deactivating an\napp makes it unavailable to new customers, and it doesn't appear in any public\nMarketplace listings. Existing customers that have already downloaded your app\ncan continue downloading the legacy versions of the app they have already\nacquired, but they can't download any versions they've not already received. The\napp remains in your inventory, with all of its history, in case you choose to\nre-activate or reference it.\n\nHere's how to deactivate your app:\n\n1.  Navigate to *Account Home* &rarr; *Apps*. A listing of your published apps \n    appears. \n\n2.  Select the app you wish to deactivate. \n\n3.  Click the *Deactivate* button. \n\nIn this set of articles, we looked at how to create, publish, maintain, and\ntrack Liferay Marketplace apps. You can do this through the App Manager that's\navailable on your *Account Home* page on [liferay.com](http://liferay.com)\n(liferay.com) account required!). We covered requirements for publishing\nvariations of your apps for different versions of Liferay. Next, we showed how\nyou can publish an app on the Marketplace and how you can modify the app as it\nevolves. Finally, we looked at tracking app adoption using view, download, and\ninstall metrics.\n"
  },
  {
    "path": "en/distribute/publish/build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"Marketplace Developer's Guide\" basedir=\".\">\n\t<property name=\"project.dir\" value=\"../../../../liferay-docs\" />\n\n\t<import file=\"../build.xml\" />\n\n\t<property name=\"doc.dir\" value=\"publish\"/>\n\n</project>\n"
  },
  {
    "path": "en/user/articles/01-the-liferay-distinction/01-the-liferay-distinction-intro.markdown",
    "content": "---\nheader-id: the-liferay-distinction\n---\n\n# The Liferay Distinction\n\n[TOC levels=1-4]\n\nYour web presence is a big deal, and the software platform that runs it must be\nup to the task. You must be able to update your site easily, to provide your\npartners with the services they need, and to enable your users and customers to\ninteract with you. It shouldn't be complicated: in today's cloud-based\nenvironment, you should be able to make changes with a click of a button and\ndynamically allocate nodes whenever you need them. \n\n@product@ offers all of this and more. It is a mature, stable, open source\nplatform with a strong heritage of serving some of the biggest sites in the\nworld---and some of the smallest, too. \n\nThe documentation here shows you how to use it. This is the\n**User** section,\nwhich describes the features of an installed @product@, how to configure its\napplications, and how to build your website. \n\nThe **Developer** section is divided into five subsections: \n\n[**Application Development**](/docs/7-2/appdev/-/knowledge_base/a/application-development) shows you how you can build applications using\nindustry standard tools and frameworks on @product@. \n\n[**Tutorials**](/docs/7-2/tutorials/-/knowledge_base/t/developer-tutorials) lead you step-by-step through specific tasks, such as developing\nweb applications, upgrading old applications, creating themes, and more. \n\n[**Frameworks**](/docs/7-2/frameworks/-/knowledge_base/f/frameworks) show you all the Liferay APIs and development frameworks you can\nuse in your applications to streamline development. \n\n[**Customization**](/docs/7-2/customization/-/knowledge_base/c/liferay-customization) explains the myriad of ways @product@ can be customized to\nyour exact specifications. \n\n[**Reference**](/docs/7-2/reference/-/knowledge_base/r/developer-reference) is a collection of material showing developers the options\navailable for various APIs. \n\nThe [**Installation and Upgrades**](https://learn.liferay.com/dxp/latest/en/installation-and-upgrades.html) section shows administrators how to obtain, install, and\nconfigure @product@ on bare metal, virtualized environments, and the cloud. \n\nIf you're coming from an older version, read on to learn what's new in\n@product-ver@. \n"
  },
  {
    "path": "en/user/articles/01-the-liferay-distinction/02-whats-new-72.markdown",
    "content": "---\nheader-id: whats-new-in-72\n---\n\n# What's New in Liferay 7.2!\n\n[TOC levels=1-4]\n\nThe latest version of @product@ delivers powerful tools to help\nbusinesses create and personalize any experience across their solutions,\nleverage the flexibility of a decoupled CMS architecture, and streamline\nbusiness operations. \n\n@product@ meets the needs of today's digital-first business teams to\ncreate experiences rapidly across channels. It equips enterprises with a wide\nvariety of easy-to-use applications and tools to build tailored solutions and\nexperiences on a flexible platform. \n\nFor a full feature list, please read the \n[*Liferay DXP 7.2 Features Overview*](https://www.liferay.com/resource?folderId=3292406&title=Liferay+DXP+7.2+Features+Overview&utm_source=whitepaper&utm_medium=content&utm_content=liferay%20dxp%207.2%20new%20features%20summary)\nor contact sales@liferay.com.\n\n## Key New Features\n\nEnsure your business evolves and stays relevant with new features\ndesigned to support great customer experiences across all stages of the\ncustomer journey. \n\n### Experience Creation\n\nGreat digital experiences are imperative in today's competition for new\nbusiness. Companies also need to make sure they stay relevant after the\nsale, investing in long-term customer relationships that cultivate\nloyalty and repeat business. Here are some new features to help you\nensure you're creating and delivering excellent experiences all the\ntime.\n\n#### Content Authoring\n\n@product-ver@ evolves our content authoring and management\ncapabilities significantly, making it even easier and more intuitive for\nthe non-technical content creator to create and manage engaging content.\n\n![Figure 1: Content authors can build pages out of building blocks called *Fragments*.](../../images/build-pages.png)\n\n#### Site Building\n\nContent pages now allow users to create experiences that have both\napplications and content on the same page, giving businesses increased\nflexibility to create experiences tailored to fit the needs of different\nend users. More tools have been provided to allow for richer layouts and\nfunctionality for the non-technical user to build a visually\nappealing site experience easily. \n\n#### Fragments\n\nThis release expands on the functionality available to create fragments,\nwhich previously required developer involvement. For marketers looking\nto create a fragment for their use case, we provide new functionality that\nallows a non-technical user to create simple fragments easily through the page\neditor itself by dragging and dropping out-of-the-box components into\na container. \n\n![Figure 2: Fragments make it easy to build pages.](../../images/fragments-pages.png)\n\n#### Fragments Toolkit/CLI\n\nImprovements to the web developer's experience has also been made for\n@product-ver@. A set of front-end toolkits---including a CLI tool---is provided\nso web developers can write fragments in their own code editors and\nimport/export them without needing to redeploy.\n\n![Figure 3: Use the Fragments Editor or download the toolkit and use your own tools.](../../images/fragments-toolkit.png)\n\n#### In-Context Editing and Content Previews\n\nAn improved site building interface shows users how different content would\nappear to different visitors. @product@ allows content creators to preview\ncontent in context of how it would look like on a live site. \n\n#### Content Usages\n\nFor structured content that can be mapped and delivered across multiple\nlocations, a new feature has been introduced to allow the content creator to\nview where specific pieces of web content are being used and reused across their\nchannels.\n\n#### A/B Testing (DXP only)\n\nContent creators can use A/B testing to create and customize tests to evaluate\nwhich elements on Content Pages perform better and edit content accordingly.\nLiferay DXP's A/B tests leverage Bayesian statistics to identify the probable\nvalues of lift for a variant, allowing your business to make more informed\ndecisions. Native integration with Liferay Analytics Cloud allows for data\ncollected for the running test to determine a winning variant. \n\n### Personalization\n\nIn @product-ver@, segmentation and personalization capabilities have\nbeen moved into the core product. This allows for a more seamless\nintegration of content creation and personalization functionality and\nhelps streamline the process of creating segments while creating\npersonalized experiences.\n\n#### Session Rules\n\nThere are many different pieces of information available to track a web\nvisitor. Session rules help further identify their distinct digital\nfingerprint per visit and is something that marketers can leverage to\nidentify who is a repeat visitor and who is a new visitor.\n\n#### Rule Builder\n\nBeing able to sort intelligently through vast amounts of data and\nassociated individuals can be tedious; this process should be easy and\nnot have to be curated manually. Marketers should instead be able to\nbuild lists of individuals based on a specific set of criteria using\nrules, leading to time savings and efficiency. The rule builder helps\nenable more effective targeting by reducing the amount of manual work\nneeded to create a specific list of people who fall under certain pieces\nof criteria.\n\n![Figure 4: The Rule Builder provides a drag-and-drop interface that helps you build exactly the criteria you need to target the right information to the right users.](../../images/rule-builder.png)\n\n#### Content Sets\n\nContent sets help address the use case of personalizing a feed of content for\ndifferent groups of people. Users can easily target and personalize multiple\npieces of content in a fragment or widget and have it display accordingly for\nthe visitor.\n\n#### Experiences\n\nThe new Experiences feature allows a user to create different variations\nof a page. All the tools available for creating a page in the layout editor are\nalso available for making these page variations. Non-technical users can easily\ntailor the messaging, images and even widgets on a page.\n\n#### Content Recommendations (DXP only)\n\nContent recommendations leverages interest models generated in Liferay Analytics\nCloud to recommend content on Liferay DXP. The option to do so is available\nthrough Content Sets and automatically filters content based on interest\nkeywords. Liferay Analytics Cloud uses AI to cluster topics and model long-term\ninterest for known and unknown visitors. Keywords are taken from the categories,\ntags, and keywords of content. \n\n### Bulk Management\n\nEnterprises regularly manage large volumes of content and work that gets\ngenerated on their digital properties. The need to manage and accurately\ncategorize these assets can be overwhelming, if not impossible, to maintain and\nuse. Bulk management features provided in @product-ver@ help reduce the amount\nof manual labor through automation and tools that perform bulk actions.\n\n#### Auto-Tagging\n\nUsers can automatically assign the correct metadata for images,\ndocuments and web content. Having rich metadata assigned to the proper\ncontent helps to not only reduce the amount of manual, tedious work, but\nalso improves the searchability of assets. The resulting metadata that\naccumulates helps establish the foundation needed for content\npersonalization efforts and content automation. An Auto-Tagging API lets\ndevelopers extend the functionality to tag any asset with any selected\nauto-tagging service.\n\n#### Bulk Operations\n\nImprovements have been made to make the experience of managing tags,\ncategories, and file operations much easier at scale. \n\n#### Automatic Document Versioning Policies\n\nRefinements to how documents can be versioned are provided in 7.2. The\nfunctionality for document versioning has been expanded to let users define\ntheir own versioning policies and apply them to @product@. This is in addition\nto the existing default versioning system @product@ provides out-of-the-box.\n\n### Business Operations\n\nDigital enterprises today must possess efficient back office organization to\nsupport the delivery of great customer experiences. It is crucial to fulfill\nthese customer interactions and ensure those experiences are delivered and\nsupported in a timely manner. Achieving this kind of efficiency, for a great\ncustomer experience, requires involvement from different departments and\nstakeholders and affects a lot of disparate content and processes. @product-ver@\nhelps enterprises navigate these problems with features to help support\nstreamlining and optimizing those processes and operations.\n\n#### Forms API\n\nReduce the time it takes for IT to deliver custom applications for the\nbusiness. Automate data collection through applications.\n\n#### Workflow Metrics\n\nWorkflow metrics helps users gain insight into how long certain workflow events\ntake to complete. Users can set deadlines on a workflow process's events; these\nconfigurations are called Service Level Agreements (SLAs). Once defined, use\nWorkflow Reports to measure compliance with the SLAs. The analytics and metrics\ngenerated can help you understand the throughput performance of your processes\nin a given timeframe, allowing users to better optimize their processes. \n\n#### Workflow Reports\n\nCut down the time it takes to serve your customers. New reports for workflow\nprocesses help users identify operational bottlenecks and work efficiently to\nincrease revenue goals.\n\n#### Online Document Editing\n\nIntegration with Office 365 and Google Docs allows the creation and editing of\ndocuments stored in @product@. Users can manage documents, presentations, and\nspreadsheets with the power that the suite provides and store them in\n@product@'s document repository for future access. This feature takes advantage\nof the existing permissions system, versioning, and sharing capabilities already\nincluded with @product-ver@. \n\n#### P2P Asset Sharing\n\nIn addition to being able to create and edit documents easily, users can\nnow comment and share documents with other registered users. This allows for\nfaster coordination without the need of assigning custom permissions.\n\n![Figure 5: You can share assets with other users easily.](../../images/share-assets.png)\n\n#### Improved Identity Management Tools\n\nNew improvements have also been made to help tackle tedious tasks of\nhandling personal data erasure requests. Logical hierarchies for assets\nare now in place to help simplify bulk deletion and anonymization during\nthe review process. Filtering and scoping capabilities across @product@ help\nprovide greater context when reviewing personal data, and administrators can be\ninformed when specific applications will delete or anonymize data during the\nauto-anonymization process. All of these improvements help ensure compliance\nwith GDPR and other data protection laws.\n\n### Headless Capabilities\n\nGive your developers the freedom to create any presentation layer and\ncontent creators the ease of traditional content management tools.\n@product-ver@ provides a decoupled architecture to allow for a range\nof approaches towards content management and delivery. Enterprises can choose\na traditional CMS or experience management approach, a full headless approach or\na hybrid approach.\n\n#### OpenAPI\n\nLiferay's API layer supports the [OpenAPI](https://swagger.io/resources/open-api/) specification, the most popular\nopen source framework for REST APIs. This allows for greater\nflexibility, security and ease of integration as the OpenAPI\nspecification is language-agnostic, allows for security audits, and\nclients can understand and consume services without knowledge of server\nimplementation or access to the server code.\n\n#### Headless CMS\n\nEnable developers to manage web content headlessly. Headless capabilities in\nthis version are also provided to help enable omnichannel experiences from\na single source of data. Content delivery APIs allow for developers to deliver\nricher, faster and more responsive user experiences, no matter the device.\nFront-end developers can leverage their native tools and frameworks they're\nalready familiar with to build sites. These APIs provide access to a variety of\nassets and content in @product@.\n\nContent participation APIs are also provided to support user interactions with\ncomments and ratings for web content and assets.\n\n### Developer Experience\n\nIn addition to the new headless capabilities that are provided on\n@product-ver@, improvements to developer tools and the developer\nexperience has been made. These tools help developers accelerate time to\nmarket and tailor the platform for your business needs. \n\n#### Upgrade Tool\n\nA revamped Upgrade Tool simplifies the upgrade process from previous versions of\n@product@. This all-in-one tool allows system administrators to reduce the\ntime spent troubleshooting by helping them plan their upgrade process for\ndatabase migrations, checking properties, and restarting failed upgrades. \n\n#### Front-end Toolkits\n\nFront-end toolkits allow developers to create an application using their\nfavorite JavaScript framework. The toolkit resolves any conflicting JavaScript\npackages and bundles the application properly to deploy it into @product@\nseamlessly.\n\n#### Struts Removed\n\nThough @product@ never used a version of Struts with security vulnerabilities,\nthe Struts library has been removed, and it is no longer a part of the product.\n\n## What's Next\n\nLearn more about how Liferay can help your business take the next step\nin your digital strategy. Request a demo from one of our team members at\n[*liferay.com/request-a-demo*](https://www.liferay.com/request-a-demo?utm_source=whitepaper%20&utm_medium=content&utm_content=liferay%20dxp%207.2%20new%20features%20summary).\n"
  },
  {
    "path": "en/user/articles/05-setting-up/00-intro.markdown",
    "content": "---\nheader-id: setting-up\n---\n\n# Setting Up\n\n[TOC levels=1-4]\n\nIf @product@ is anything, it's configurable. As the core is shrinking\ndue to its increased modularity, it's important that all the applications in\nLiferay are also configurable. \n\nBreaking it down, three types of applications must be configurable:\n\n1.  The platform itself\n2.  Liferay's out-of-the-box applications \n3.  Custom applications\n\nTo this end, Liferay's engineers have made the platform and its applications\nconfigurable, and created a mechanism for developers to make their\n[applications configurable](/docs/7-2/frameworks/-/knowledge_base/f/configurable-applications).\n\n## Where Configuration Happens\n\nLiferay's configuration takes place in the following places: \n\n**User Interface:** configuration through Liferay's UI is stored in a database. The\nvalues set in the UI always override configurations set in properties files.\n\n**Properties files:** properties files that set default behavior may be included in\nthe platform or the modules. Keep in mind that these settings can always be\noverridden by a system administrator in the UI. To find what properties are\nconfigurable this way, visit\n[docs.liferay.com](https://docs.liferay.com/portal/7.2-latest/propertiesdoc).\n\nThe UI location where these configuration options appear depends on the scope\nyou want to affect with the settings you choose.\n\n## Configuration Scope\n\nDepending on the configuration scope of a setting you change, you'll impact the\nplatform and its applications with more or less granularity. At one end of the\nspectrum, you can affect the scope of the whole system. Configurations made at\nthe system scope affect all virtual instances, Sites, and widgets. At the\nopposite end of the spectrum, configurations made at the widget level provide\nconfiguration settings only for that instance of the widget. \n\nTake Language settings, for example. You can set the default language used by\nthe virtual instance. You can also set the default language of a Site. Some\napplications even let you set the default language used by a specific piece of\ncontent. \n\nHere's an overview of the available configuration scopes:\n\n**System:** Configuring Liferay and its applications through System Settings\nprovides system scoped configurations and sets default values for all virtual\ninstances, sites, or widgets.\n\n**Virtual Instance:** Configuring in Instance Settings provides settings that\nact on the specific virtual instance for which they are made, including Sites\nand widgets in the virtual instance.\n\n**Site:** Configurations made at the Site scope, where you select the Site to\nconfigure in the Site selector, provide settings that take place only in that\nSite. Alternate configurations can be made in different Sites.\n\n**Widget Scope:** Configuring a specific widget only provides a\nconfiguration for that particular widget.\n\nScopes in Liferay are hierarchical so that one scope can set the default values\nfor the underlying sub-scopes. For example, making a system-wide configuration\nsets the default values for all virtual instances, sites or widgets of\nthe system. If a different configuration is set at a level with more granularity\n(for example, the widget scope), it takes precedence over the settings made\nat less granular scopes (for example, the virtual instance scope).\n\nThis section contains articles on configuring Liferay at the System and Instance\nscopes:\n\nSystem wide configuration:\n\n- System Settings is the primary location for system configuration.\n\n- Server Administration contains some lower-level server configuration options, such\n  as logging.\n\nSetting up a virtual instance:\n\n- Virtual Instances is where virtual instances are added and\n  edited.\n\n- Instance Settings is the primary location for a virtual instance's\n  configuration.\n\n- Custom Fields is where additional database fields are added to existing\n  virtual instance entities.\n\nAll of these are accessed through the Control Panel. Start by learning to\nconfigure modules system-wide in the System Settings Control Panel app.\n"
  },
  {
    "path": "en/user/articles/05-setting-up/01-system-settings/00-intro.markdown",
    "content": "---\nheader-id: system-wide-settings\n---\n\n# System Wide Settings\n\n[TOC levels=1-4]\n\nIt can be hard to keep track of all the configuration interfaces. The Control\nPanel's Configuration section houses a lot of the higher level (for example,\nsystem and instance scoped) configuration options. This section considers the\nconfiguration options dealing with the *System* scope. Configuration at the\nsystem level affects all the [*Virtual\nInstances*](/docs/7-2/user/-/knowledge_base/u/setting-up-a-virtual-instance) of\nLiferay in the system.\n\n- System Settings is the primary location for system configuration.\n\n- Server Administration contains some lower-level server configuration options,\n    such as logging.\n\nGet started by learning about System Settings.\n"
  },
  {
    "path": "en/user/articles/05-setting-up/01-system-settings/01-system-settings.markdown",
    "content": "---\nheader-id: system-settings\n---\n\n# System Settings\n\n[TOC levels=1-4]\n\n@product@ is modular, meaning it's composed of many applications divided into\neven smaller \"chunks\" of functionality. The applications, and sometimes even\ncode chunks, are configurable at several scopes, as discussed in the\nintroductory article for this section. \n\nIn System Settings, administrators make system scoped configuration changes and\nset system-wide default configurations for other scopes. System Settings is\nlocated in Control Panel &rarr; Configuration &rarr; System Settings. \n\n![Figure 1: System Settings are accessed through the Control Panel.](../../../images/system-settings-categories.png)\n\n## Editing System Configurations\n\nSystem Settings is organized into sections (for example, Content) and categories\n(for example, Workflow) based on the functionality being configured.  There's\nalso a Search bar to make finding configuration entries easier. Search for the\nname of a specific configuration entry, or even a specific field within an\nentry.\n\n![Figure 2: System Settings are organized by section and category.](../../../images/system-settings-nav-search.png)\n\nChanging a configuration isn't difficult: \n\n1.  Find the configuration entry you need, either by searching or browsing the\n    sections and categories.\n\n2.  Open the configuration form for the entry.\n\n3.  Make any changes you'd like, then click *Save*. Your configuration changes\n    are saved and applied throughout the system. \n\n| **Important:** Content generated using templates (e.g., FreeMarker templates and\n| Application Display Templates) is cached. Cached content might not reflect\n| configuration changes until the cache is invalidated (cleared). The\n| [Server Administration &rarr; Resources tab](/docs/7-2/user/-/knowledge_base/u/server-administration-resources)\n| provides cache clearing options.\n\n![Figure 3: After saving changes to a configuration, the actions *Reset Default Values* and *Export* are available for it.](../../../images/system-settings-actions.png)\n\nIf you make some regrettable configuration decisions and can't recall exactly\nwhat you did, start over by clicking the actions button\n(![Actions](../../../images/icon-actions.png)), then clicking *Reset Default\nValues*. \n\n## Configuration Scope\n\nWhile browsing the categories of System Settings, you'll notice that clicking\ninto a topic (for example, Blogs) reveals entries at different scopes. All the\nsettings here act at the system scope. For scopes labeled other than System,\nthese configurations act as defaults. In other words, they identify where the\nsystem-wide configuration is overridden. True system-scoped configurations\n(those under a category's _System Scope_ heading) are not overridden anywhere.\nThere are four values that you'll see under Scope:\n\n- *System Scope:* Any System Settings configuration made for system scoped entries\n  becomes the final value for the application in a system-wide fashion. It\n  affects the whole system and isn't overridden anywhere else. \n\n  ![Figure 4: Some System Settings entries are system scoped.](../../../images/system-settings-system-scope.png)\n\n- *Virtual Instance Scope:* Configuration at the Virtual Instance level is\n  overridden in Instance Settings.\n\n  ![Figure 5: Some System Settings are virtual instance scoped.](../../../images/system-settings-instance-scope.png)\n\n- *Site Scope:* Configuration at this scope is overridden in each site. \n\n  ![Figure 6: Some System Settings are site scoped.](../../../images/system-settings-site-scope.png)\n\n- *Widget Scope:* Configuration at this scope is overridden in each Widget\n  Instance (like the Blogs example below). \n\n  ![Figure 7: Some System Settings entries are widget scoped.](../../../images/system-settings-application-scope.png)\n\nIf a configuration changed in System Settings is also configurable at a\ndifferent scope, the System Settings value acts as a default that can be\noverridden. Once a configuration change is made at a more granular scope, making\na change at the system level doesn't do anything. \n\nFor example, allowing comments is configurable for each Blogs Entry. Set the\ndefault behavior at Control Panel &rarr; Configuration &rarr; System Settings\n&rarr; Blogs. In the Blogs Entry under Widget Scope, disable the *Enable\nComments* checkbox. \n\nNow add a Blog Entry to a Site's Content & Data &rarr; Blogs application. Then\ngo to a public page and add the Blogs Widget to the page. Click the Options\nbutton (![Options](../../../images/icon-app-options.png)) for the widget and\nselect *Configuration*. You'll see the same Enable Comments checkbox, and its\ndefault is now false (unchecked). Checking the box in the Widget Configuration\nscreen breaks its link with the System Settings entry. Changing the System\nSettings configuration has no effect on this widget anymore.\n\n<!--If an application is configurable from Instance Settings and System Settings,\nuse System Settings to configure it whenever possible. If you use Instance\nSettings and later want to revert to using the System Settings default\nconfigurations, use the *Reset Values* button from the Instance Settings entry. \n\n![Figure 8: Some Instance Settings entries have a *Reset Values* button so you can safely revert your configuration changes, falling back to the System Settings defaults.](../../../images/instance-settings-reset-values.png)\n\nThe Reset Values button removes configuration values from the database so you\ncan rely on the defaults that were provided out-of-the-box. If there is no Reset\nValues button for an Instance Settings entry, once you use Instance Settings to\nconfigure the application, you can't later decide to use System Settings. Only\nchanges to Instance Settings are recognized. -->\n\n## Exporting and Importing Configurations\n\nWhat if you change many default configurations in System Settings, and then need\nto make the same changes in another installation? Don't worry, you don't need to\nremember and duplicate every choice you made. The System Settings application\nlets you export a single entry's configurations, or all the settings you made in\nthe System Settings interface. The exported files are deployable to any other\ninstallation of the same version.\n\nTo export a single entry's configurations, click the actions button\n(![Actions](../../../images/icon-actions.png)), then click *Export*. A `.config`\nfile containing your configuration downloads to your system. \n\nTo export all the configuration changes you've made in System Settings, click \nthe System Settings options button \n(![Options](../../../images/icon-options.png)), then click *Export All \nSettings*. The `.config` files for all the entries you edited then download in a \nZIP file. \n\nTo make these configurations active in the destination system, unzip and place\nthe `.config` files in the `[Liferay_Home]/osgi/configs` folder. \n\nNow you know what System Settings is and how to use it. All that's left is to\nexplore the entries to see what configuration options are available. If you\naren't sure what something does, check the documentation for the feature you're\ninterested in, as specific configurations are covered there. \n"
  },
  {
    "path": "en/user/articles/05-setting-up/01-system-settings/02-config-files/00-intro.markdown",
    "content": "---\nheader-id: understanding-system-configuration-files\n---\n\n# Understanding System Configuration Files\n\n[TOC levels=1-4]\n\nThe [System Settings\napplication](/docs/7-2/user/-/knowledge_base/u/system-settings) is convenient\nfor making system-scoped configuration changes and setting default\nconfigurations for other\n[scopes](/docs/7-2/user/-/knowledge_base/u/setting-up#configuration-scope). But\nthere's another supported configuration approach: configuration files. You can\nuse configuration files to transfer configurations from pre-production systems\nto production systems, or between any other @product@ systems. Sometimes\ndevelopers choose to distribute the default configuration for their applications\nvia configuration file. Whatever the reason, configuration files offer another\nconfiguration approach.\n\nConfiguration files use the `.config` property value format defined by the\n[Apache Felix Configuration Admin\nframework](http://felix.apache.org/documentation/subprojects/apache-felix-config-admin.html). \n\n| **Important:** Content generated using templates (e.g., FreeMarker templates and\n| Application Display Templates) is cached. Cached content might not reflect\n| configuration changes until the cache is invalidated (cleared). The\n| [Server Administration &rarr; Resources tab](/docs/7-2/user/-/knowledge_base/u/server-administration-resources)\n| provides cache clearing options.\n| \n| **Note:** The `.cfg` file format is common in OSGi environments, and it's a\n| supported format, but `.config` files are preferable since they allow specifying\n| a property value's type, and allow multi-valued properties. The syntax described\n| in these articles is for `.config` files.\n"
  },
  {
    "path": "en/user/articles/05-setting-up/01-system-settings/02-config-files/01-creating-config-files.markdown",
    "content": "---\nheader-id: creating-configuration-files\n---\n\n# Creating Configuration Files\n\n[TOC levels=1-4]\n\nSystem Settings provides an\n[*Export*](/docs/7-2/user/-/knowledge_base/u/system-settings#exporting-and-importing-configurations)\noption that becomes available once you modify a configuration entry. Exporting\nis the recommended way to create `.config` files: you download a `.config` file\ncontaining the entry's settings in a `key=value` format. @product@ exports an\nentry's total available configuration keys and values, even if only one value\nwas changed. You can export a single configuration entry or the entire set of\nmodified configurations. \n\nTo avoid a file name conflict, name configuration files using a unique \nidentifier. For example, the Journal Service entry, which backs Web Content \nfunctionality, has this file name: \n\n    com.liferay.journal.configuration.JournalServiceConfiguration.config\n\n![Figure 1: The Web Content System Settings entry has the back-end ID `com.liferay.journal.configuration.JournalServiceConfiguration`.](../../../../images/config-web-content-entry.png)\n\n## Key/Value Syntax\n\nThe syntax for all keys and values in a `.config` file is the same: \n\n```properties\nconfigurationName=\"value\"\n```\n\nFor single value configurations without special characters, that's all there is\nto know. Settings with multiple values and certain characters require slight\nmodifications. \n\n## Multi-Value Settings\n\nConfiguration entries can have properties that accept multiple values. For \nexample, a configuration property for specifying supported file extensions needs \nmore than one value. Here's how to write a multi-value setting in a `.config` \nfile: \n\n```properties\nmultiValueSetting=[\"Value 1\",\"Value 2\", ...]\n```\n\nDo not use a space character between values (after the comma). The property\nwon't be loaded.\n\nOpen the Web Content category in System Settings (under the Content section), and\nselect *Web Content* for the virtual instance scope. You'll see what looks like\nmultiple single value entries for *Characters Blacklist*: \n\n![Figure 2: The Web Content System Settings entry has many *Characters Blacklist* fields.](../../../../images/config-web-content-blacklist.png)\n\nIn the configuration file, this is really a single key with an array of \ncomma-separated values: \n\n```properties\ncharactersblacklist=[\"&\",\"'\",\"@\",\"\\\\\",\"]\",\"}\",\":\",\"\\=\",\">\",\"/\",\"<\",\"[\",\"{\",\"%\",\"+\",\"#\",\"`\",\"?\",\"\\\"\",\";\",\"*\",\"~\"]\n```\n\n## Escaping Characters\n\nDouble quotes (`\"`) and equals signs (`=`) must be *escaped* in `.config` files. \nEscaping is using another character to denote that a character shouldn't be used \nin its normal way. Since double quotes and equals signs are already used in \n`.config` files, escaping them tells the framework not to read them the normal \nway, but to pass them through as part of the value. Use a backslash to escape \ncharacters in the `.config` file: \n\n```properties\ncharactersblacklist=[\"&\",\"\\\"\",\"\\=\"]\n```\n\nThis setting illustrates a multi-value setting with a regular, unescaped \ncharacter (`&`), and two escaped ones (`\\\"` and `\\=`). \n\nAlong with the mandatory escaping of double quotes and equals characters, it's\nbeneficial to escape spaces inside values to avoid problems. In this example, a\nbackslash is used before each space character to ensure it's read and processed\nproperly: \n\n```properties\nblacklistBundleSymbolicNames=[\"Liferay\\ Marketplace\",\"Liferay\\ Sharepoint\\ Connector\"]\n```\n\nIf you don't escape spaces yourself, the framework adds the backslash for you \nafter deployment. \n\n## Typed Values\n\nThe `.config` file format supports specifying the type of a configuration value\nby inserting a special type marker character. Because @product@ already knows\nthe correct type for each configuration property, the type characters are only\nuseful for informational purposes. For example, a configuration with a boolean\ntype has *B* just before the value to mark it as a boolean type:\n\n```properties\naddDefaultStructures=B\"true\"\n```\n\nIf you see type markers in `.config` files, you can safely ignore them. The\nexample included above functions identically without the type marker: \n\n```properties\naddDefaultStructures=\"true\"\n```\n\n## Deploying a Configuration File\n\nOnce you have a configuration file, deploy it. It's registered and \nthe targeted configuration values are updated automatically. \n\nTo deploy the `.config` file, place it in your \n[Liferay Home's](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) \n`osgi/configs` folder. To change the configuration further, you can edit the \n`.config` file directly or use System Settings. \n\n## Configuration Files and Clustering\n\nIn a clustered environment, each node needs the same configuration values for\neach entry. For example, all nodes should use the same *Blogs* configuration\nsettings. To accomplish this, deploy copies of the same `.config` file to each node's `[Liferay Home]/osgi/configs` folder.\n"
  },
  {
    "path": "en/user/articles/05-setting-up/01-system-settings/02-config-files/02-factory-configurations.html",
    "content": "<p>&mdash;</p><h2>header-id: factory-configurations</h2><h1>Factory Configurations</h1><p>[TOC levels=1-4]</p><p>Configurations supporting multiple entries are called <em>factory configurations</em>. </p><p>| <strong>Factory Configuration Example:</strong> JAX-WS and JAX-RS web services are | supported. These services must use a CXF Endpoint, which is a context path where | the web services are deployed and accessed. Endpoints can be created via | factory configuration by navigating to the CXF Endpoints entry in System | Settings (System Settings &rarr; Platform &rarr; Web API &rarr; CXF Endpoints). | Click <em>ADD</em>, enter the desired configuration values, then repeat the process to | add as many CXF Endpoint configurations as needed. Creating CXF Endpoint | configurations also creates CXF Endpoints themselves. This is how factory | configurations work.</p><p>If a service is meant to support factory configurations, its System Settings entry has an ADD button. </p><p><img src=\"../../../../images/factory-configuration-entry.png\" alt=\"Figure 1\" /><p class=\"caption\">Figure 1: If a System Settings entry has an ADD button, it&rsquo;s suitable for factory configurations.</p><p>As with single-instance configurations, you can set factory configurations in the System Settings interface (as described in the example above) or via configuration files. Name a standard single-instance configuration file like this: </p>\n<pre><code>my.service.ServiceConfiguration.config\n</code></pre><p>If your service supports factory configurations, use the convention of calling the configuration&rsquo;s first instance <code>-default.config</code>: </p>\n<pre><code>my.service.ServiceConfiguration-default.config\n</code></pre><p>The next instance contains a unique <em>subname</em> (something other than <em>default</em>). It&rsquo;s good practice to use a descriptive name: </p>\n<pre><code>my.service.ServiceConfiguration-port9080.config\n</code></pre><p>To follow the CXF Endpoints example described above, if Liferay&rsquo;s developers had shipped an initial CXF Endpoint <code>.config</code> file with @product@, it would have been named this: </p>\n<pre><code>com.liferay.portal.remote.cxf.common.configuration.CXFEndpointPublisherConfiguration-default.config\n</code></pre><p>If this <code>-default.config</code> configuration specifies a context path for <em>REST</em> web services, and you create another endpoint with a different context path for <em>SOAP</em> web services, your second configuration file could be named: </p>\n<pre><code>com.liferay.portal.remote.cxf.common.configuration.CXFEndpointPublisherConfiguration-soap.config\n</code></pre><p>| <strong>Note:</strong> Some System Settings entries (like the CXF Endpoints entry) don&rsquo;t ship | with a configuration file, so anything you create is the first occurrence. | However, if you configure one and export it to obtain the <code>.config</code> file, it | doesn&rsquo;t use the <code>-default.config</code> naming convention. Instead it&rsquo;s given a | guaranteed unique identifier for its subname, like this: | | com.liferay.portal.remote.cxf.common.configuration.CXFEndpointPublisherConfiguration-a6f67e48-6dca-49c6-bf6b-8fd5e6016b2d.config | | This guarantees that the file has a unique name. If you&rsquo;re exporting the | configuration file for deployment in a separate system, you can rename | the exported file to use a more descriptive subname. If you rename the file and | deploy it to the same system it was exported from, the new subname marks it as | an entirely new configuration. You&rsquo;ll end up with an additional configuration | instance in this case, not just a renamed one.</p><p>| <strong>Warning::</strong> For configuration entries supporting factory configurations, | omitting the subname from a <code>.config</code> file&rsquo;s name causes System Settings to | disallow adding new entries for the configuration (only the configuration entry | targeted by this <code>.config</code> file). This is caused by a known bug. See | <a href=\"https://issues.liferay.com/browse/LPS-76352\">LPS-76352</a> | for more information. Once an improperly named configuration file is deployed, | you can&rsquo;t add any entries for the configuration in question from its System | Settings entry. For example, if you deploy the following file to configure a CXF | Endpoint, not only does this not add a CXF Endpoint, it also prevents you from | adding any CXF Endpoints via System Settings: | | com.liferay.portal.remote.cxf.common.configuration.CXFEndpointPublisherConfiguration.config | | Deploying an erroneous (lacking a subname) <code>.config</code> file doesn&rsquo;t disable | anything permanently. Just rename the file using the proper convention described | above or remove it entirely and start over.</p><p>In many cases, configuration files can be used to force a factory configuration scenario, but not all configurations can be used this way. It&rsquo;s best to stick to the intended use cases. Use System Settings as described above to determine if using factory configurations is a good idea. If not, stick to the single occurrence mode of configuration (specifying only one configuration file for the service). </p>\n"
  },
  {
    "path": "en/user/articles/05-setting-up/01-system-settings/02-config-files/02-factory-configurations.markdown",
    "content": "---\nheader-id: factory-configurations\n---\n\n# Factory Configurations\n\n[TOC levels=1-4]\n\nConfigurations supporting multiple entries are called *factory configurations*. \n\n> Factory Configuration Example: Adding Organization types is supported, and is\n> useful if you need to model real-life hierarchies or enforce hierarchical\n> rules. In Liferay DXP, each Organization type is created via a factory\n> configuration entry in System Settings.\n\nIf a service is meant to support factory configurations, its System Settings\nentry has an ADD button. \n\n![Figure 1: If a System Settings entry has an ADD button, it's suitable for factory configurations.](../../../../images/factory-configuration-entry.png)\n\nAs with single-instance configurations, you can set factory configurations in \nthe System Settings interface (as described in the example above) or via \nconfiguration files. Name a standard single-instance configuration file like\nthis: \n\n    my.service.ServiceConfiguration.config\n\nIf your service supports factory configurations, use the convention of calling\nthe configuration's first instance `-default.config`: \n\n    my.service.ServiceConfiguration-default.config\n\nThe next instance contains a unique *subname* (something other than *default*).\nIt's good practice to use a descriptive name: \n\n    my.service.ServiceConfiguration-port9080.config\n\nIn the Organization type example, the default Organization type (aptly named\n_organization_) is created by a `-default.config` file named\n\n```bash\ncom.liferay.organizations.internal.configuration.OrganizationTypeConfiguration-default.config\n```\n\nFollowing the example from the [Adding a New Organization\nType](../../users-and-permissions/organizations/adding-a-new-organization-type.md)\narticle, you could add the _League_ type with a configuration file named \n\n```bash\ncom.liferay.organizations.internal.configuration.OrganizationTypeConfiguration-league.config\n```\n\nSome System Settings entries that support factory configuration don't ship with\na configuration file for the default instance (e.g., the Anonymous User entry).\nIf you export a factory configuration file to obtain the `.config` file, it\ndoesn't use the `-default.config` naming convention. Instead, whether it's the\nfirst occurrence or an additional one, it's given a guaranteed unique\nidentifier for its subname:\n\n```bash\ncom.liferay.user.associated.data.web.internal.configuration.AnonymousUserConfiguration-6befcd73-7c8b-4597-b396-a18f64f8c308.config\n```\n\nThis guarantees that the file has a unique name. If you're exporting the\nconfiguration file for deployment in a separate system, you can rename the\nexported file to use a more descriptive subname. If you rename the file and\ndeploy it to the same system it was exported from, the new subname marks it as\nan entirely new configuration. You'll end up with an additional configuration\ninstance in this case, not just a renamed one.\n\n| **Warning::** For configuration entries supporting factory configurations,\n| omitting the subname from a `.config` file's name causes System Settings to\n| disallow adding new entries for the configuration (only the configuration entry\n| targeted by this `.config` file). This is caused by a known bug. See\n| [LPS-76352](https://issues.liferay.com/browse/LPS-76352)\n| for more information. Once an improperly named configuration file is deployed,\n| you can't add any entries for the configuration in question from its System\n| Settings entry. For example, if you deploy the following file to configure an Organization Type,\n| not only does this not add an Organiaztion Type, it also prevents you from\n| adding any via System Settings:\n| \n|     com.liferay.organizations.internal.configuration.OrganizationTypeConfiguration.config\n| \n| Deploying an erroneous (lacking a subname) `.config` file doesn't disable\n| anything permanently. Just rename the file using the proper convention described\n| above or remove it entirely and start over.\n\nIn many cases, configuration files can be used to force a factory configuration\nscenario, but not all configurations can be used this way. It's best to stick to\nthe intended use cases. Use System Settings as described above to determine if\nusing factory configurations is a good idea. If not, stick to the single\noccurrence mode of configuration (specifying only one configuration file for the\nservice). \n"
  },
  {
    "path": "en/user/articles/05-setting-up/01-system-settings/03-server-administration/00-intro.html",
    "content": "<p>&mdash;</p><h2>header-id: server-administration</h2><h1>Server Administration</h1><p>[TOC levels=1-4]</p><p>Server Administration lets you manage and monitor your @product@ server. Access the application by clicking <em>Control Panel</em> &rarr; <em>Configuration</em> &rarr; <em>Server Administration</em>. </p><p><img src=\"../../../../images/server-admin-memory.png\" alt=\"Figure 1\" /><p class=\"caption\">Figure 1: The Resources tab of Server Administration shows a graph of your server&rsquo;s memory usage.</p><p>Server Administration&rsquo;s functionality is segmented into these tabs: </p><p><strong>Resources:</strong> View memory usage and perform management tasks like running the garbage collector, clearing the database cache, and more. For more information, see <a href=\"/docs/7-2/user/-/knowledge_base/u/server-administration-resources\">Resources</a>.</p><p><strong>Log Levels:</strong> View and set logging levels. You can make dynamic modifications of log levels for any class hierarchy in @product@. Custom objects not on the list can be added with the <em>Add Category</em> tab. Changes to the log level near the top of the class hierarchy (such as at <code>com.liferay</code>) also change log levels for all the classes under that hierarchy. Modifications unnecessarily high in the hierarchy generate too many messages to be useful. </p><p><strong>Properties:</strong> View JVM and portal properties. This tab has two sub-tabs: System Properties and Portal Properties. The System Properties tab shows an exhaustive list of system properties for the JVM, as well as many @product@ system properties. You can use this information for debugging purposes or to check the currently running configuration. The Portal Properties tab shows an exhaustive list of the current portal property values. For explanations of these properties, see the <a href=\"@platform-ref@/7.1-latest/propertiesdoc/portal.properties.html\">portal properties reference documentation</a>. </p><p><strong>Data Migration:</strong> Migrate documents from one repository to another. For example, you can migrate your documents to a new repository on a different disk or in a <a href=\"/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration\">new format</a>. Here are the steps:</p>\n<ol>\n  <li>Create a backup copy of the Document Library repository and <a href=\"/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation\">@product@ database</a>.</li>\n  <li>Configure the new file store in <em>System Settings</em> &rarr; <em>Platform: File Storage</em>.</li>\n  <li>In this tab (<em>Server Administration</em> &rarr; <em>Data Migration</em>), select the repository hook for the file store you configured and click <em>Execute</em>.</li>\n  <li>Make sure the data migrated correctly.</li>\n  <li><a href=\"/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration\">Configure the new repository</a> as the default.</li>\n  <li>If you used a <code>portal-ext.properties</code> file to configure the repository, restart the server.</li>\n</ol><p><strong>Mail:</strong> Instead of using a <code>portal-ext.properties</code> file to configure a mail server, you can configure a mail server from this tab. If your <a href=\"/docs/7-2/user/-/knowledge_base/u/user-subscriptions-and-mailing-lists\">message boards receive mail</a>, you can connect a POP mail server. If @product@ sends mail (useful for sending notifications to users), you can connect to an SMTP server. Note that if you configure mail server settings here in System Settings, these settings override any mail server settings in your <code>portal-ext.properties</code> file. </p><p><strong>External Services:</strong> Configure external services for generating file previews. For more information, see <a href=\"/docs/7-2/user/-/knowledge_base/u/server-administration-external-services\">the article on External Services</a>.</p><p><strong>Script:</strong> A scripting console for executing migration or management code. The Groovy scripting language is supported out of the box. <!-- See the \nscripting article(/discover/portal/-/knowledge_base/7-2/using-liferays-script-engine)\nfor more information and examples on using the scripting console-->. </p><p><strong>Shutdown:</strong> Schedule a shutdown that notifies logged-in users of the impending shutdown. You can define the number of minutes until the shutdown and a message to display. @product@ displays the message at the top of users&rsquo; pages for the duration of time you specified. When the time expires, all pages display a message saying the portal has been shut down. The server must then be restarted to restore access. </p>\n"
  },
  {
    "path": "en/user/articles/05-setting-up/01-system-settings/03-server-administration/00-intro.markdown",
    "content": "---\nheader-id: server-administration\n---\n\n# Server Administration\n\n[TOC levels=1-4]\n\nServer Administration lets you manage and monitor your @product@ server. Access\nthe application by clicking *Control Panel* &rarr; *Configuration* &rarr;\n*Server Administration*. \n\n![Figure 1: The Resources tab of Server Administration shows a graph of your server's memory usage.](../../../../images/server-admin-memory.png)\n\nServer Administration's functionality is segmented into these tabs: \n\n**Resources:** View memory usage and perform management tasks like running \nthe garbage collector, clearing the database cache, and more. For more \ninformation, see \n[Resources](/docs/7-2/user/-/knowledge_base/u/server-administration-resources).\n\n**Log Levels:** View and set logging levels. You can make dynamic \nmodifications of log levels for any class hierarchy in @product@. Custom \nobjects not on the list can be added with the *Add Category* tab. Changes to \nthe log level near the top of the class hierarchy (such as at `com.liferay`) \nalso change log levels for all the classes under that hierarchy. \nModifications unnecessarily high in the hierarchy generate too many messages \nto be useful. \n\n**Properties:** View JVM and portal properties. This tab has two sub-tabs: \nSystem Properties and Portal Properties. The System Properties tab shows an \nexhaustive list of system properties for the JVM, as well as many @product@ \nsystem properties. You can use this information for debugging purposes or to \ncheck the currently running configuration. The Portal Properties tab shows \nan exhaustive list of the current portal property values. For explanations \nof these properties, see the \n[portal properties reference documentation](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html). \n\n**Data Migration:** Migrate documents from one repository to another. For \nexample, you can migrate your documents to a new repository on a different \ndisk or in a [new format](/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration). Here are the steps:\n\n1.  Create a backup copy of the Document Library repository and\n    [@product@ database](/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation). \n2.  Configure the new file store in *System Settings* &rarr; *Platform: File \n    Storage*. \n3.  In this tab (*Server Administration* &rarr; *Data Migration*), select the \n    repository hook for the file store you configured and click *Execute*. \n4.  Make sure the data migrated correctly. \n5.  [Configure the new\n    repository](/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration)\n    as the default. \n6.  If you used a `portal-ext.properties` file to configure the repository, \n    restart the server. \n\n**Mail:** Instead of using a `portal-ext.properties` file to configure a \nmail server, you can configure a mail server from this tab. If your \n[message boards receive mail](/docs/7-2/user/-/knowledge_base/u/user-subscriptions-and-mailing-lists), \nyou can connect a POP mail server. If @product@ sends mail (useful for \nsending notifications to users), you can connect to an SMTP server. Note \nthat if you configure mail server settings here in System Settings, these \nsettings override any mail server settings in your `portal-ext.properties` \nfile. \n\n**External Services:** Configure external services for generating file \npreviews. For more information, see \n[the article on External Services](/docs/7-2/user/-/knowledge_base/u/server-administration-external-services).\n\n**Script:** A scripting console for executing migration or management code. \nThe Groovy scripting language is supported out of the box. \n\n**Shutdown:** Schedule a shutdown that notifies logged-in users of the\nimpending shutdown. You can define the number of minutes until the shutdown and\na message to display. @product@ displays the message at the top of users' pages\nfor the duration of time you specified. When the time expires, all pages\ndisplay a message saying the portal has been shut down. The server must then be\nrestarted to restore access. \n"
  },
  {
    "path": "en/user/articles/05-setting-up/01-system-settings/03-server-administration/01-server-admin-resources.markdown",
    "content": "---\nheader-id: server-administration-resources\n---\n\n# Server Administration: Resources\n\n[TOC levels=1-4]\n\nThe Server Administration app's Resources tab contains several server wide \nactions that an administrator can execute. These include the following items: \n\n**Run the garbage collector:** Tells the JVM to free memory by running the \ngarbage collector. \n\n**Generate a thread dump:** Generates a thread dump for later scrutiny to \ndetermine the presence and location of any deadlocks. Useful during \nperformance testing, but you must add a logger category for\n`com.liferay.server.admin.web.internal.portlet.action.EditServerMVCActionCommand`\nand set it to `INFO` before executing.\n\n**Clear content cached by this VM:** Clears content stored in the local \ncache. Only local JVM scope Ehcache content is cleared, not clustered \nEhcache. [1](#one)\n\n**Clear content cached across the cluster:** Clears the content of the \nentire clustered Ehcache. [1](#one)\n\n**Clear the database cache:** Clears the database cache. Does not clear any \nEhcache content except database results at the persistence layer. [1](#one)\n\n**Clear the direct servlet cache:** Clears the direct servlet cache. In case \nemergency fixes must be applied, this action allows an administrator to \nclear out the cache manually to force JSPs to reload.\n\nThe direct servlet context optimizes JSP serving performance by caching and \naccessing the generated servlets directly instead of accessing them over the \napplication server's dispatcher chain. This function is only suitable for \ncases where no filter is required for the JSPs; it should be enabled for \nproduction mode to improve performance, but disabled for development mode to \nallow JSP servlets to be reloaded on the fly. See the Direct Servlet Context \nsection of the `portal.properties` file for details. [1](#one)\n\n**Verify database tables of all plugins:** Checks all tables against their \nindexes for data retrieval accuracy. \n\n**Verify membership policies:** Checks that existing Site membership \npolicies were correctly applied and automatically makes updates. If the\n@product@ database is changed manually or is hacked---resulting in a user\nassigned to a Site in violation of a site membership policy---this action\ntriggers the verification methods of all implemented Site membership policies.\nChanges are automatically made to any memberships in violation. \n\n**Reset preview and thumbnail files for Documents and Media:** Regenerates \npreviews of each item in your Documents and Media libraries. \n\n**Clean up permissions:** Removes permissions on the Guest, User, and Power \nUser Roles to simplify the management of User Customizable Pages. The Add To \nPage permission is removed from the Guest and User Roles for all portlets, \nand is reduced in scope for Power Users from portal-wide to User Personal \nSite.\n\n**Clean up portlet preferences:** This action cleans up database entries if \nportlet preferences become orphaned in the @product@ database. \n\n![Figure 1: The Resources tab of Server Administration lets you execute several server maintenance tasks.](../../../../images/server-admin-resources.png)\n\n[<a name=\"one\">1</a>] Caching occurs at multiple levels. Some higher caching \nlayers aren't aware of lower caching layers. Always clear the cache at the\nlowest (most granular) layer possible, even if you've already cleared a higher\nlevel cache.\n"
  },
  {
    "path": "en/user/articles/05-setting-up/01-system-settings/03-server-administration/02-server-admin-external-services.markdown",
    "content": "---\nheader-id: server-administration-external-services\n---\n\n# Server Administration: External Services\n\n[TOC levels=1-4]\n\nUsers can upload and share any type of file via the Documents and Media library, \na customizable and permissions-enabled online repository for files (see \n[publishing files ](/docs/7-2/user/-/knowledge_base/u/publishing-files) \nfor more information). \n[PDFBox](https://pdfbox.apache.org/) \nis included with @product@ and generates automatic previews for certain file\ntypes (mostly PDFs). You can install two additional tools to generate previews\nfor other file types: \n\n-   [**OpenOffice:**](https://www.openoffice.org/) or [**LibreOffice:**](https://www.libreoffice.org/)\n    Convert and generate previews for many types of documents. \n\n-   [**ImageMagick:**](https://www.imagemagick.org/script/index.php) \n    Generate higher-quality image previews for many types of images. \n\n-   [**Xuggler:**](http://www.xuggle.com/xuggler/) \n    Convert and generate previews for audio and video files. \n\nAs of Liferay 7.1, OpenOffice/LibreOffice is configured in OSGi Configuration\nAdmin instead of portal properties. To adjust the these settings, go to *Control\nPanel* &rarr; *Configuration* &rarr; *System Settings* &rarr; *Connectors* &rarr;\n*OpenOffice Integration*. You can also adjust these\nsettings by deploying a\n`com.liferay.document.library.document.conversion.internal.configuration.OpenOfficeConfiguration.config`\nfile to your `[Liferay Home]/osgi/configs` folder. See the\n[Breaking Changes](/docs/7-1/reference/-/knowledge_base/r/breaking-changes#moved-openoffice-properties-to-osgi-configuration)\ndocument for details.\n\nOnce you've installed ImageMagick and Xuggler, you can use the Server\nAdministration app's External Services tab to configure @product@ to use them.\nMake sure to choose the correct versions of these tools for your operating\nsystem. Install the latest stable versions, as older versions may not run\nproperly with @product@. ImageMagick must be installed manually, but you can\ninstall Xuggler from the Control Panel. \n\n| **Tip:** If you're running @product@ on a Linux server and experience a problem\n| enabling Xuggler, check your server's glibc version. For Xuggler to work, you\n| may need to update glibc to version 2.6 or later.\n\n## ImageMagick Configuration\n\nBefore configuring ImageMagick to generate image and PDF previews, install it\nand its dependency, Ghostscript. This differs by operating system: on Linux,\nboth are likely already installed. They are not likely to be installed on\nWindows, but may be on macOS.\n\n1.  Download and install \n    [ImageMagick](https://www.imagemagick.org/script/index.php). \n\n2.  Download and install \n    [Ghostscript](https://www.ghostscript.com/). \n\nOnce installed, you must enable ImageMagick in the Server Administration app's \nExternal Services tab, or in a `portal-ext.properties` file. If using \n`portal-ext.properties`, add the following lines and make sure the search path \npoints to the directories containing the ImageMagick and Ghostscript \nexecutables. You may also need to configure the path for fonts used by \nGhostscript when in macOS or Unix environments. \n\n    imagemagick.enabled=true\n    imagemagick.global.search.path[apple]=/opt/local/bin:/opt/local/share/ghostscript/fonts:/opt/local/share/fonts/urw-fonts\n    imagemagick.global.search.path[unix]=/usr/local/bin:/usr/local/share/ghostscript/fonts:/usr/local/share/fonts/urw-fonts\n    imagemagick.global.search.path[windows]=C:\\\\Program Files\\\\ImageMagick\n\nFollow these steps to instead enable ImageMagick from the Server Administration \napp's External Services tab: \n\n1.  In the *Control Panel*, navigate to *Configuration* &rarr; \n    *Server Administration*, then click the *External Services* tab. \n\n2.  Expand the ImageMagick and Ghostscript section and select *Enabled*. \n\n3.  Verify that the paths to the ImageMagick and Ghostscript executables are \n    correct. \n\n![Figure 1: Enable ImageMagick and Ghostscript, and verify that the paths are correct.](../../../../images/imagemagick-ghostscript.png)\n\n4.  Set the Resource limits to use.\n\n## Xuggler Configuration\n\nFollow these steps to install and configure Xuggler: \n\n1.  In the *Control Panel*, navigate to *Configuration* &rarr; \n    *Server Administration*, then click the *External Services* tab. \n\n2.  In the Xuggler section, select the Xuggler `.jar` file that matches your \n    operating system. Then click *Install*. \n\n3.  Restart your application server. \n\n4.  Enable Xuggler for your portal. There are two ways to do this: \n\n    -   In the Control Panel, navigate to the *Server Administration* &rarr; \n        *External Services* tab, select *Enabled*, then click *Save*. \n    -   Add the following line to your `portal-ext.properties` file and restart \n        your application server: \n\n            xuggler.enabled=true\n\n![Figure 2: Install Xuggler.](../../../../images/xuggler-install.png)\n"
  },
  {
    "path": "en/user/articles/05-setting-up/02-instance-settings/00-intro.markdown",
    "content": "---\nheader-id: setting-up-a-virtual-instance\n---\n\n# Setting Up a Virtual Instance\n\n[TOC levels=1-4]\n\nOnce @product@ is installed, the configuration begins. Recall that \nconfiguration happens at different scopes. Here we're covering configuration at \nthe virtual instance scope. There's an important difference between the system \nscope and the instance scope. The system scope is the highest level scope you \ncan make configurations at. All virtual instances are impacted by configuration \ndone at this scope. The instance scope applies only to one particular virtual \ninstance.\n\nVirtual instances have unique domain names but share a server and database. Each \nvirtual instance can have independent data and configurations. \n\nThe articles in this section cover these topics:\n\n- Adding a virtual instance\n- Configuring a virtual instance\n\nGet started by learning how to add a virtual instance. \n\n"
  },
  {
    "path": "en/user/articles/05-setting-up/02-instance-settings/01-virtual-instances.markdown",
    "content": "---\nheader-id: virtual-instances\n---\n\n# Virtual Instances\n\n[TOC levels=1-4]\n\nHere's a quick scenario: you already have a server hosting a @product@ \ninstallation and a database. It has many \n[Users](/docs/7-2/user/-/knowledge_base/u/users-and-organizations), \n[Sites](/docs/7-2/user/-/knowledge_base/u/building-a-site), \nand specific \n[instance settings](/docs/7-2/user/-/knowledge_base/u/instance-configuration-instance-settings#general). \nIf you require a second similar installation, then adding a *Virtual Instance* \nmight be right for you. \n\nYou can run more than one Virtual Instance on a single server with a shared \ndatabase, but separate data and configurations. Users are directed to the \ncorrect Virtual Instance via its unique domain name. And because Virtual \nInstances share an application server and OSGi container, they also share these \ncustomizations: \n\n-   Custom code deployed by developers and administrators.\n-   [System-scoped configurations](/docs/7-2/user/-/knowledge_base/u/system-settings) \n    (e.g., `.config` files, changes made in *Control Panel* &rarr; \n    *Configuration* &rarr; *System Settings*). \n-   Application server configuration.\n\nAdministrators can manage Virtual Instances in *Control Panel* &rarr; \n*Configuration* &rarr; *System Settings*.\n\n![Figure 1: Add and manage virtual instances of Liferay in the Control Panel's *Configuration* &rarr; *Virtual Instances* section.](../../../images/virtual-instances.png)\n\nFollow these steps to create a Virtual Instance: \n\n1.  Before you create a Virtual Instance, configure its domain name in your \n    network. \n\n2.  Go to *Control Panel* &rarr; *Configuration* &rarr; *Virtual Instances*. \n\n3.  Click the *Add* button \n    (![Add](../../../images/icon-add.png)). \n    This opens the *New Instance* form. \n\n4.  Complete the New Instance form as follows:\n\n**Web ID:** The instance's ID. Using the domain name is a common convention.\n\n**Virtual Host:** The domain name you configured in your network. When users are\ndirected to your server via this domain name, they'll be sent to the Virtual\nInstance that contains their data.\n\n**Mail Domain:** The mail host's domain name for the Virtual Instance. \nEmail notifications are sent from the instance using this domain. \n\n**Max Users:** The maximum number of user accounts the Virtual Instance \nsupports. Enter *0* to support unlimited users. \n\n**Active:** Whether the Virtual Instance is active. Note that inactive \nVirtual Instances aren't accessible to anyone, even the administrator. \n\nClick *Save* when you're done filling out the form. \n\nNow you can navigate to the instance using its domain name. You're brought to \nwhat looks like a clean install of @product@. This is your new Virtual Instance! \nYou can configure it any way you like. The remaining articles in this section \nshow you how to configure an instance's settings. \n"
  },
  {
    "path": "en/user/articles/05-setting-up/02-instance-settings/02-configuring-virtual-instance-settings/01-configuring-virtual-instance-settings-intro.markdown",
    "content": "---\nheader-id: configuring-virtual-instance-settings\n---\n\n# Configuring Virtual Instances\n\n[TOC levels=1-4]\n\nTo access instance settings, open the Control Panel and navigate to\n*Configuration* &rarr; *Instance Settings*. The Instance Settings are organized\ninto three sections: PLATFORM, SECURITY, and CONTENT AND DATA. Here we focus on\nthe instance settings available under the PLATFORM section. The CONTENT AND\nDATA settings are specific to each application, and the SECURITY instance\nsettings are covered in their respective articles, listed below for reference:\n\n- [LDAP](/docs/7-2/deploy/-/knowledge_base/d/configuring-ldap)\n- [OAuth 2](/docs/7-2/deploy/-/knowledge_base/d/oauth2-scopes#creating-the-authorization-page)\n- SSO: \n  - [CAS Server](/docs/7-2/deploy/-/knowledge_base/d/cas-central-authentication-service-single-sign-on-authentication)\n  - [Token based SSO](/docs/7-2/deploy/-/knowledge_base/d/token-based-single-sign-on-authentication)\n  <!-- [Facebook Connect]() Add back once article is available-->\n  - [Open ID Connect](/docs/7-2/deploy/-/knowledge_base/d/authenticating-with-openid-connect#enabling-openid-connect-authentication)\n  - [OpenSSO](/docs/7-2/deploy/-/knowledge_base/d/opensso-single-sign-on-authentication)\n\n  The PLATFORM instance settings are covered in the articles shown below:\n\n  - [Email](/docs/7-2/user/-/knowledge_base/u/email-instance-settings)\n  - [Instance Configuration](/docs/7-2/user/-/knowledge_base/u/instance-configuration-instance-settings)\n  - [User Authentication](/docs/7-2/user/-/knowledge_base/u/user-authentication-instance-settings)\n  - [Users](/docs/7-2/user/-/knowledge_base/u/users-instance-settings)\n  - [More Platform Settings](/docs/7-2/user/-/knowledge_base/u/more-platform-section-instance-settings)\n"
  },
  {
    "path": "en/user/articles/05-setting-up/02-instance-settings/02-configuring-virtual-instance-settings/02-email-instance-settings.markdown",
    "content": "---\nheader-id: email-instance-settings\n---\n\n# Email Instance Settings\n\n[TOC levels=1-4]\n\nThe Email configuration is your one-stop-shop for all your email notification \nneeds. To access the Email configuration settings for your instance, open the \nControl Panel and navigate to *Configuration* &rarr; *Instance Settings* and \nselect the *Email* category under the PLATFORM section. The Email configuration \ncontains six entries:\n\n- Account Created Notification\n- Email Sender\n- Email Verification Notification\n- Mail Host Names\n- Password Changed Notification\n- Password Reset Notification\n\nEach configuration entry is described in the corresponding section below. \n\n## Account Created Notification\n\nThe Account Created Notification entry defines the email templates, with and \nwithout the new User's password included in the body of the text, for the \nmessage sent to notify Users when they create a new account. You can specify \nwhether this email is sent by checking/unchecking the *Enabled* checkbox at the \ntop. Use the template variables listed at the bottom of the configuration under \nthe \"Definition of Terms\" heading to help build your email template. \n\n## Email Sender\n\nThe Email Sender entry specifies the virtual instance's administrative Name and \nAddress for email notifications, declared as the `[$FROM_NAME$]` and \n`[$FROM_ADDRESS$]` variables respectively in the email templates. By default, \nthey are from the [`admin.email.from.name` and `admin.email.from.address`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Admin%20Portlet) [portal properties](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)).\nThis name and email address appear in the *From* field in all email messages sent by the virtual instance. \n\n![Figure 1: Customize the email template for the email messages sent to new Users.](../../../../images/instance-settings-account-created.png)\n\n## Email Verification Notification\n\nThe Email Verification Notification entry defines the email template for the \nmessage sent to Users when asked to verify their Email Address. Use the template \nvariables listed at the bottom of the configuration under the \n\"Definition of Terms\" heading to help build your email template. \n\n## Mail Host Names\n\nThe Mail Host Names entry specifies which mail host names are owned by your \norganization for the virtual instance. Enter one mail host name per line, \nbesides the one specified in the [General tab](). \n\n## Password Changed Notification\n\nThe Password Changed Notification entry defines the email template for the \nmessage sent to notify Users when their password has changed. Use the template \nvariables listed at the bottom of the configuration under the \n\"Definition of Terms\" heading to help build your email template. \n\n## Password Reset Notification\n\nThe Password Reset Notification entry defines the email template for the message \nsent to notify Users when a request has been made to reset their password. Use \nthe template variables listed at the bottom of the configuration under the \n\"Definition of Terms\" heading to help build your email template. \n\n![Figure 2: There are some handy variables available for use in email templates.](../../../../images/instance-settings-definition-of-terms.png)\n"
  },
  {
    "path": "en/user/articles/05-setting-up/02-instance-settings/02-configuring-virtual-instance-settings/03-instance-configuration-settings.markdown",
    "content": "---\nheader-id: instance-configuration-instance-settings\n---\n\n# Instance Configuration Instance Settings\n\n[TOC levels=1-4]\n\nThe Instance Configuration settings define the basic configuration information\nfor the virtual instance, from the appearance to the Terms of Use for your\nUsers to agree to. Access the Instance Configuration settings from the Control\nPanel's *Configuration* &rarr; *Instance Settings* section, and select the\n*Instance Configuration* category under the *PLATFORM* section. The Instance\nConfiguration contains four entries:\n\n- Appearance\n- Contact Information\n- General\n- Terms of Use\n\nEach configuration entry is described in the corresponding section below. \n\n## Appearance\n\nThe Appearance configuration entry defines the default logo and overall look\nand feel for the virtual instance. It's organized into LOGO and LOOK AND FEEL\nsections:\n\n**LOGO:** Change the default logo and check/uncheck the \n*Allow site administrators to use their own logo?* checkbox, enabled by default, \nto specify whether site administrators can upload a logo when they configure a \nsite. When configuring a new logo, be careful to choose an image file that fits \nthe space. Large images might overlap with the navigation. \n\n**LOOK AND FEEL:** Set the default theme(s) for the instance and Control Panel. \n\n## Contact Information\n\nThe Contact Information configuration entry specifies how to contact the\norganization that owns the virtual instance. It's divided into several\nsections:\n\n**ADDRESSES:** Specify the primary, mailing, shipping, P.O. Box, etc. address of \nthe organization. \n\n**PHONE NUMBERS:** Provide the fax, local, etc. phone numbers for the \norganization. \n\n**ADDITIONAL EMAIL ADDRESSES:** Specify any additional email addresses \nassociated with the organization. \n\n**WEBSITES:** Specify the public and/or intranet websites for the organization. \n\nDevelopers can query for this contact information in their applications. Note \nthat you can add and remove additional entries in a section with the plus and \nminus buttons respectively. \n\n## General\n\nThe General entry specifies the virtual instance's configuration information,\nlanding pages, and the associated organization's basic legal information. It\nhas two sections:\n\n**Main Configuration:** Configure this information for the virtual instance:\n\n- Set the name of the entity responsible for running the virtual instance.\n- Set the mail domain.\n- Set the virtual host.\n- Set the URLs to a CDN configured to serve static resources.\n\n**Navigation:** Set a home page for your virtual instance as well as default \nlanding and logout pages. To set these pages, use the page's relative URL that \nfollows the domain. For example, to set the default landing page to \n`http://localhost:8080/web/guest/login`, use `/web/guest/login`. You can use the \nvariable `${liferay:screenName}` as part of the address to redirect Users to \ntheir personal pages upon login. For example, the User `marvin` has this default \nURL to his personal page:\n\n    http://localhost:8080/user/marvin\n\nTo make sure he's directed there on login, place `/user/${liferay:screenName}` \nin the Default Landing Page field. These URLs can also be set at the system \nscope, in a `portal-ext.properties` file:\n\n```properties\ndefault.landing.page.path=\ndefault.logout.page.path=\ncompany.default.home.url=\n```\n\nAll virtual instances share the values specified in the properties file. Changes \nmade in Instance Settings override the values set in the properties file. For \nmore information, see the [Portal Properties documentation](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html).\n\n**Additional Information:** Specify a Legal name, ID, company type, SIC code,\nticker symbol, industry and industry type for the owner of the virtual\ninstance. \n\n## Terms of Use\n\nThe Terms of Use entry contains everything you need to provide a custom Terms of \nUse agreement for your Users. Terms of Use are important when you need them, but \nnot all Sites do. They're not listed first, but they're one of the first things \nto configure for your virtual instance, whether you require them or not. \n\nSince the Terms of Use page is enabled by default, one of your first actions \nshould be to disable or replace the default, placeholder terms. You can disable \nthe requirement for all Users to read the Terms of Use or set the Group ID and \nArticle ID for the Web Content Article that contains your Terms of Use. \"How do \nI find a web content article's Group ID and Article ID,\" you ask? No problem. \nThe Group ID is the ID of the Site the Web Content is associated with. To find \nthe Group/Site ID, follow these steps:\n\n1.  Go to *Site Menu* &rarr; *Configuration* &rarr; *Settings*.\n\n2.  Find the Site ID field in the General tab. Enter it into the Group ID\n    field.\n\n![Figure 1: The Site ID in Site Settings is the Group ID in the terms of Use configuration.](../../../../images/instance-settings-group-id.png)\n\nTo find the Web Content Article's ID, follow these steps:\n\n1.  Go to the *Site Menu* &rarr; *Content & Data* &rarr; *Web Content*. \n\n2.  Click on your Terms of Use article. Its ID appears at the top of the screen, \n    with the Version and Workflow Status. \n\n![Figure 2: The Web Content Article ID is displayed in the edit screen.](../../../../images/instance-settings-wc-id.png)\n\nSave the configuration. All your users signing in for the first time see your \nTerms of Use article. Users must agree to the Terms of Use to register their \nUser Accounts. \n\n![Figure 3: Turn a Web Content Article into the Site's Terms of Use page.](../../../../images/instance-settings-terms-of-use.png)\n"
  },
  {
    "path": "en/user/articles/05-setting-up/02-instance-settings/02-configuring-virtual-instance-settings/04-user-authentication-settings.markdown",
    "content": "---\nheader-id: user-authentication-instance-settings\n---\n\n# User Authentication Instance Settings\n\n[TOC levels=1-4]\n\nThe User Authentication settings define how Users can authenticate, the various \nauthentication methods that are required for them, and the screen names and \nemail addresses that are reserved and can't be registered by Users. \n\nAccess the User Authentication settings in the Control Panel's *Configuration*\n&rarr; *Instance Settings* section, and select the *User Authentication*\ncategory under the *PLATFORM* section. \n\nUser Authentication contains two entries:\n\n- General\n- Reserved Credentials\n\nEach configuration entry is described in the corresponding section below. \n\n![Figure 1: Configure general authentication behavior and settings for external authentication systems.](../../../../images/instance-settings-auth.png)\n\n## General\n\nThe General configuration entry contains several general authentication \nsettings:\n\n-   Authenticate by email address (default), screen name, or User ID (a \n    numerical ID auto-generated in the database---not recommended).\n\n-   Enable/Disable automatic log in. If enabled, a User can check \n    a box which will cause the Site to \"remember\" the login information by \n    placing a cookie on the browser. If disabled, Users must always log in \n    manually.\n\n-   Enable/Disable forgotten password functionality.\n\n-   Enable/Disable request password reset links.\n\n-   Enable/Disable account creation by strangers. If running an Internet \n    site, leave this enabled so visitors can create accounts on your Site.\n\n-   Enable/Disable account creation by those using an email address in the \n    domain of the company running the Site (which is set on the General page of \n    Instance Settings). This is handy if you're using Liferay to host both \n    internal and external web sites. Make sure all internal IDs are created by \n    administrators but external Users can register for IDs themselves.\n\n-   Enable/Disable email address verification. If enabled, \n    Users receive a verification email with a link back to the virtual instance, \n    verifying that the email address they entered is valid.\n\nBy default, all settings except for the last are enabled. User authentication by \nemail address is an important default for the following reasons:\n\n1.  An email address is unique to the User who owns it.\n\n2.  People remember their email addresses. A Users who hasn't logged in for a \n    while could forget their screen name.\n\n3.  If email address isn't used to authenticate, a User might change her email \n    address then forget to update the email address in her profile. If this \n    occurs, no notifications sent by the virtual instance will reach the User. \n    Keeping the email address at the forefront of a User's mind when she logs in \n    helps ensure the User keeps it current. \n\n## Reserved Credentials\n\nThe Reserved Credentials configuration entry specifies the screen names and \nemail addresses Users aren't allowed to use. This prevents Users from creating \nIDs that look like administrative IDs or that have reserved words in their \nnames. \n\nLearn to configure a third party authentication service or set up Single Sign On \n(SSO) in the \n[security documentation](/docs/7-2/deploy/-/knowledge_base/d/securing-product). \n"
  },
  {
    "path": "en/user/articles/05-setting-up/02-instance-settings/02-configuring-virtual-instance-settings/05-users-instance-settings.markdown",
    "content": "---\nheader-id: users-instance-settings\n---\n\n# Users Instance Settings\n\n[TOC levels=1-4]\n\nThe Users configuration defines the look and feel of the Personal Menu, the \ndefault user associations for Users, and defines whether specific fields are \navailable when a new User creates an account. \n\nTo access the Users settings, Open the Control Panel and navigate to \n*Configuration* &rarr; *Instance Settings*, and select the *Users* category \nunder the *PLATFORM* section. \n\nThe Users Instance configuration contains three entries:\n\n- Personal Menu\n- Default User Associations\n- Fields\n\nEach configuration entry is described in the corresponding section below. \n\n## Personal Menu\n\nThe Personal Menu configuration entry specifies whether personal applications\nuse the same look and feel as the current site or if they should use the look\nand feel of the My Dashboard pages instead. You can also specify whether the\npersonal menu appears in the Control Menu (as it did in past versions of\n@product@) by toggling the *Show in Control Menu* option on and off. \n\n## Default User Associations\n\nThe Default User Associations configuration entry defines the default Sites,\nOrganization Sites, Roles, and User Groups you want all new Users assigned to\nautomatically. By default, new Users are only assigned to the Users Role. User\ngroups are handy tools for pre-populating your Users' private Sites with pages,\nassigning Roles and permissions, and managing site membership. If you update\nthis configuration after your Users have created their accounts, don't worry.\nYou can apply the updates to existing Users by checking the *Apply to Existing\nUsers* checkbox. Changes take effect the next time the User signs in. \n\n## Fields\n\nThe Fields configuration entry contains settings for enabling/disabling the \nfields listed below on the Add/Edit User Form:\n\n- Autogeneration of screen names\n- Birthday field\n- Gender field\n"
  },
  {
    "path": "en/user/articles/05-setting-up/02-instance-settings/02-configuring-virtual-instance-settings/06-analytics-infra-localization-third-party-settings.markdown",
    "content": "---\nheader-id: more-platform-section-instance-settings\n---\n\n# More Platform Section Instance Settings\n\n[TOC levels=1-4]\n\nThe Instance Settings also contain settings for Infrastructure and Localization, \nas well as integrations for Analytics and Third Party map APIs. Each \nconfiguration entry is described in the corresponding section below. \n\n## Infrastructure\n\nThe Infrastructure category contains settings to specify which content types are \nvalidated during the Import/Export process and whether to delete temporary LAR \nfiles during the Import/Export process. \n\nTo access these settings, open the Control Panel and navigate to *Configuration* \n&rarr; *Instance Settings* and select *Infrastructure* under the PLATFORM \nheading. \n\n## Localization\n\nThe Localization category contains the configuration for setting the default \ninstance language and time zone for the virtual instance. You can also configure \nthe Language Selector's look and feel from the Widget Scope. \n\nTo access the Localization settings, Open the Control Panel and navigate to \n*Configuration* &rarr; *Instance Settings*, and select the *Localization* \ncategory under the *PLATFORM* section.\n\n## Analytics\n\nThe Analytics category defines the available analytics systems for the virtual \ninstance. Enter an analytics system or remove one of the two pre-configured \noptions (`google` and `piwik`). Activate these systems here, and configure them \nat the [site level](/docs/7-2/user/-/knowledge_base/u/advanced-site-settings#analytics). \n\nTo access Analytics configuration settings, open the Control Panel and navigate \nto *Configuration* &rarr; *Instance Settings*, and select the *Analytics* \ncategory under the *PLATFORM* section. \n\n## Third Party\n\nThe Third Party category specifies the maps API provider for geolocalized assets. \nChoose OpenStreetMap or Google Maps as the maps API provider. \n\nTo access Third Party configuration settings, open the Control Panel and \nnavigate to *Configuration* &rarr; *Instance Settings*, and select the \n*Third Party* category under the *PLATFORM* section. \n"
  },
  {
    "path": "en/user/articles/05-setting-up/03-script-console/00-intro.markdown",
    "content": "---\nheader-id: using-liferays-script-engine\n---\n\n# Using Liferay's Script Engine\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/using-the-script-engine.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n@product@ provides a robust script engine for executing\n[Groovy](http://groovy-lang.org/)\nscripts. You can execute scripts to perform maintenance tasks like data\ncleanup, user maintenance operations, bulk invocations of Liferay's API, or\neven system level operations in the scripting console.\n\nThese tutorials cover the following scripting topics:\n\n- [Invoking Liferay services](/docs/7-2/user/-/knowledge_base/u/invoking-liferay-services-from-scripts)\n\n- [Running scripts from the script console](/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console)\n\n- [Using the script engine with workflow](/docs/7-2/user/-/knowledge_base/u/leveraging-the-script-engine-in-workflow)\n\n- [Script examples](/docs/7-2/user/-/knowledge_base/u/script-examples)\n\nThe most common use of the scripting console is to invoke Liferay's services,\nso that's covered first. Familiarity with Liferay's API makes the scripting\nconsole a useful tool.\n\n"
  },
  {
    "path": "en/user/articles/05-setting-up/03-script-console/01-invoking-liferay-services-from-scripts.markdown",
    "content": "---\nheader-id: invoking-liferay-services-from-scripts\n---\n\n# Invoking Liferay Services From Scripts\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/using-the-script-engine/invoking-liferay-services-from-scripts.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nMany scripting scenarios require invoking Liferay services.\n[Liferay `*ServiceUtil` classes](https://docs.liferay.com/ce/portal/7.2-latest/javadocs/portal-kernel/)\nare the fastest and most convenient way to invoke Liferay\nservices in the\n[script console](/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console).\nYou can use Groovy to invoke Liferay services the same way you would use Java.\nGroovy's syntax facilitates writing concise, elegant scripts.\n\nThis first example illustrates correct syntax for interacting with Liferay\nservices. It uses `UserLocalServiceUtil` to retrieve a list of users and print\ntheir names to Liferay's log. To do this, you could deploy a module with Java\ncode like this:\n\n```groovy\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport java.util.List;\n\n...\n\nint userCount = UserLocalServiceUtil.getUsersCount();\nList<User> users = UserLocalServiceUtil.getUsers(0, userCount);\n\nfor (User user:users) {\n    System.out.println(\"User Name: \" + user.getFullName());\n}\n\n...\n```\n\nOr you could use Groovy---based on Java---and do the whole thing right from the\n[script console](/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console)\nwith the same code:\n\n```groovy\nimport com.liferay.portal.kernel.model.User;\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil;\nimport java.util.List;\n\nint userCount = UserLocalServiceUtil.getUsersCount();\nList<User> users = UserLocalServiceUtil.getUsers(0, userCount);\n\nfor (User user:users) {\n    System.out.println(\"User Name: \" + user.getFullName());\n}\n\nYou can even make the code somewhat Groovier:\n\nimport com.liferay.portal.kernel.service.UserLocalServiceUtil\n\nuserCount = UserLocalServiceUtil.getUsersCount()\nusers = UserLocalServiceUtil.getUsers(0, userCount)\nfor (user in users){\n    System.out.println(\"User Name: \" + user.getFullName())\n}\n```\n\nGroovy scripts that invoke Liferay services are easy to write and execute in the\nscript console.\n\nHow to run scripts is next.\n\n## Related Topics\n\n[Running Scripts From the Script Console](/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console)\n\n[Leveraging the Script Engine in Workflow](/docs/7-2/user/-/knowledge_base/u/leveraging-the-script-engine-in-workflow)\n\n[Script Examples](/docs/7-2/user/-/knowledge_base/u/script-examples)\n"
  },
  {
    "path": "en/user/articles/05-setting-up/03-script-console/02-running-scripts-from-script-console.markdown",
    "content": "---\nheader-id: running-scripts-from-the-script-console\n---\n\n# Running Scripts From the Script Console\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/using-the-script-engine/running-scripts-from-the-script-console.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe script console provides a single view for executing Groovy scripts and\nprinting their output. It has predefined variables that facilitate printing\noutput and working with portlets and users. Here you'll learn these things:\n\n- [How to execute a script in the script console](#running-the-sample-script)\n\n- [The predefined variables available in the script console](#predefined-variables)\n\n- [Tips for running scripts in the script console](#tips)\n\n| **Important:** The script console is for system operations and maintenance and\n| not for end users. Limit script console access to portal administrators.\n\nStart with running the script console's sample script.\n\n## Running the Sample Script\n\nHere's how to run the sample script in the script console:\n\n1.  Sign in as an administrator.\n\n2.  In the Product Menu, navigate to *Control Panel* &rarr; *Configuration*\n    &rarr; *Server Administration*.\n\n3.  Click on *Script*. This is the script console. The default sample script\n    prints the User count to the console output.\n\n    ```groovy\n    // ### Groovy Sample ###\n\n    number = com.liferay.portal.kernel.service.UserLocalServiceUtil.getUsersCount();\n\n    out.println(number);\n    ```\n\n4.  Click *Execute* and check the script console *Output* for the User count.\n\n![Figure 1: The script console's sample Groovy script prints the User count to the console's *Output* section.](../../../images/groovy-script-sample.png)\n\nThe Groovy sample invokes the Liferay service utility `UserLocalServiceUtil` to\nget the user count. Then it uses `out` (a built-in `PrintWriter`) to write the\ncount to the script console. Note that if you use `System.out.println` instead\nof `out.println`, your output is printed to Liferay's log file rather than to\nthe script console.\n\n## Predefined Variables\n\nHere are the predefined variables available to scripts executed in the script\nconsole:\n\n- `out` (`java.io.PrintWriter`)\n- `actionRequest` (`javax.portlet.ActionRequest`)\n- `actionResponse` (`javax.portlet.ActionReponse`)\n- `portletConfig` (`javax.portlet.PortletConfig`)\n- `portletContext` (`javax.portlet.PortletContext`)\n- `preferences` (`javax.portlet.PortletPreferences`)\n- `userInfo` (`java.util.Map<String, String>`)\n\nThis script demonstrates using the `actionRequest` variable to get the portal\ninstance's `Company`:\n\n```groovy\nimport com.liferay.portal.kernel.util.*\n\ncompany = PortalUtil.getCompany(actionRequest)\nout.println(\"Current Company:${company.getName()}\\n\")\n\nout.println(\"User Info:\")\nuserInfo.each {\n        k,v -> out.println(\"${k}:${v}\")\n}\n```\n\n![Figure 2: Here's an example of invoking a Groovy script that uses the predefined `out`, `actionRequest`, and `userInfo` variables to print information about the company and current user.](../../../images/groovy-script-current-user-info.png)\n\n## Tips\n\nKeep these things in mind when using the script console:\n\n- There is no undo.\n- There is no preview.\n- Permissions checking is not enforced for local services.\n- Scripts are executed synchronously. Avoid executing scripts that might take a\n  long time.\n\nFor these reasons, use the script console cautiously. Test your scripts on\nnon-production systems before running them on production.\n\nOf course, Liferay's script engine can be used outside of the script console.\nNext, you'll learn how workflows leverage Liferay's script engine.\n"
  },
  {
    "path": "en/user/articles/05-setting-up/03-script-console/03-leveraging-the-script-engine-in-workflow.markdown",
    "content": "---\nheader-id: leveraging-the-script-engine-in-workflow\n---\n\n# Leveraging the Script Engine in Workflow\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/process-automation/workflow/developer-guide/using-the-script-engine-in-workflow.html?highlight=workflow\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay's Kaleo workflow engine provides a robust system for reviewing and\napproving content in an enterprise environment. Even if you don't leverage\nscripts, it's a powerful and robust workflow solution. Adding scripts takes it\nto the next level. These scripts aren't run from the script console, but are\nembedded in\n[XML workflow definitions](/docs/7-2/reference/-/knowledge_base/r/crafting-xml-workflow-definitions)\nand run during the execution of the workflow.\n\n## Injected Variables\n\nUsually when you're scripting in Groovy, you must define your variables.\n\n```groovy\nKaleoInstanceToken kaleoInstanceToken=new KaleoInstanceToken();\n```\n\nIn workflow scripts, there are several [pre-defined\nvariables](https://github.com/liferay/liferay-portal/blob/7.2.x/modules/apps/portal-workflow/portal-workflow-kaleo-runtime-scripting-impl/src/main/java/com/liferay/portal/workflow/kaleo/runtime/scripting/internal/util/ScriptingContextBuilderImpl.java)\ninjected into your script context, to be called without defining them first.\n\n### Variables that are Always Available\n\nThese variables are available from anywhere that you can run a workflow script:\n\n`kaleoInstanceToken` ([`KaleoInstanceToken`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-workflow/portal-workflow-kaleo-api/src/main/java/com/liferay/portal/workflow/kaleo/model/KaleoInstanceToken.java))\n: A workflow instance and corresponding instance token (the\n`KaleoInstanceToken`) are created each time a User clicks _Submit for\nPublication_. Use the injected token to retrieve its ID, by calling\n`kaleoInstanceToken.getKaleoInstanceTokenId()`. This is often passed as a method\nparameter in a script.\n\n`userId`\n: The `userId` returned is context dependent. Technically, the logic works like\nthis: if the `KaleoTaskInstanceToke.getcompletionUserId()` is null, check\n`KaloeTaskInstanceToken.getUserId()`. If that's null too, call\n`KaleoInstanceToken.getUserId()`. It's the ID of the last User to intervene in\nthe workflow at the time the script is run. In the `created` node, this would be\nthe User that clicked _Submit for Publication_, whereas it's the ID of the\nreviewer upon exit of the `review` node of the Single Approver definition.\n\n`workflowContext` (`Map<String, Serializable>`)\n: The workflow context is full of useful information you can\nuse in your scripts. Usually you'll pass this as a parameter to a method that\nrequires a `WorkflowContext` object, but all of the `WorkflowContext`'s\nattributes are available in the script as well. The workflow context in the\nscript is context dependent. If a call to\n`ExecutionContext.getWorkflowContext()` comes back null, then the workflow\ncontext is obtained by `KaleoInstanceModel.getWorkflowContext()`.\n\n### Variables Injected into Task Nodes\n\nIf a `kaleoTaskInstanceToken` has been created:\n\n`kaleoTaskInstanceToken` ([`KaleoTaskInstanceToken`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-workflow/portal-workflow-kaleo-api/src/main/java/com/liferay/portal/workflow/kaleo/model/KaleoTaskInstanceToken.java))\n: The task's token itself is available in the workflow script. Use it to get its\nID, to use in other useful programmatic workflow activities, like programmatic\nassignment.\n\n`taskName` (`String`)\n: The task's own name is accessible (returns the same as `KaleoTak.getName()`).\n\n`workflowTaskAssignees` (`List<`[`WorkflowTaskAssignee`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/portal-kernel/src/com/liferay/portal/kernel/workflow/WorkflowTaskAssignee.java)`>`)\n: If the script is inside a task node, get a `List` of its assignees.\n\n`kaleoTimerInstanceToken` ([`KaleoTimerInstanceToken`](https://github.com/liferay/liferay-portal/blob/7.2.0-ga1/modules/apps/portal-workflow/portal-workflow-kaleo-api/src/main/java/com/liferay/portal/workflow/kaleo/model/KaleoTimerInstanceToken.java))\n: If a [task timer](/docs/7-2/reference/-/knowledge_base/r/workflow-task-nodes#task-timers)\nexists, use the `kaleoTimerInstanceToken` to get its ID, by calling\n`kaleoTimerInstanceToken.getKaleoTimerInstanceTokenId()`.\n\n## Scripting Examples\n\nThe final step in a workflow runs a script that makes content available for use.\nThe snippet below accesses the Java class associated with the workflow to set\ncontent's status to *approved*.\n\n```xml\n<script>\n    <![CDATA[\n        import com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil;\n        import com.liferay.portal.kernel.workflow.WorkflowConstants;\n\n        WorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus(\"approved\"), workflowContext);\n    ]]>\n</script>\n<script-language>groovy</script-language>\n```\n\nAt virtually any point in a workflow, you can use Liferay's script engine to\naccess workflow APIs or other Liferay APIs. There are a lot of different ways\nyou could use this. Here are a few practical examples:\n\n-  Getting a list of users with a specific workflow-related role\n-  Sending an email to the designated content approver with a list of people to\n   contact if he is unable to review the content\n-  Creating an alert to be displayed in the Alerts portlet for any user assigned\n   to approve content\n\nOf course, before you try any of this, you need to know the appropriate syntax\nfor inserting a script into a workflow. In an XML workflow definition, a script\ncan be used in any XML type that can contain an `actions` tag: those types are\n`<state>`, `<task>`, `<fork>` and `<join>`. Inside of one of those types, format\nyour script like this:\n\n```xml\n<actions>\n    <action>\n        <script>\n            <![CDATA[*the contents of your script*]]>\n        </script>\n        <script-language>*your scripting language of choice*</script-language>\n    </action>\n    ...\n</actions>\n```\n\nHere's an example of a workflow script created in Groovy. This one is used with\na `Condition` statement in Kaleo. It accesses Liferay's asset framework to\ndetermine the category of an asset. The script uses the category to determine\nthe correct approval process automatically.  If the category `legal` has been\napplied to the asset, the asset is sent to the `Legal Review` task upon\nsubmission. Otherwise, the asset is sent to the `Default Review` task.\n\n```xml\n<script>\n    <![CDATA[\n        import com.liferay.portal.kernel.util.GetterUtil;\n        import com.liferay.portal.kernel.workflow.WorkflowConstants;\n        import com.liferay.portal.kernel.workflow.WorkflowHandler;\n        import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;\n        import com.liferay.asset.kernel.model.AssetCategory;\n        import com.liferay.asset.kernel.model.AssetEntry;\n        import com.liferay.asset.kernel.model.AssetRenderer;\n        import com.liferay.asset.kernel.model.AssetRendererFactory;\n        import com.liferay.asset.kernel.service.AssetEntryLocalServiceUtil;\n\n        import java.util.List;\n\n        String className = (String)workflowContext.get(\n            WorkflowConstants.CONTEXT_ENTRY_CLASS_NAME);\n\n        WorkflowHandler workflowHandler =\n            WorkflowHandlerRegistryUtil.getWorkflowHandler(className);\n\n        AssetRendererFactory assetRendererFactory =\n            workflowHandler.getAssetRendererFactory();\n\n        long classPK =\n            GetterUtil.getLong((String)workflowContext.get\n            (WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n        AssetRenderer assetRenderer =\n            workflowHandler.getAssetRenderer(classPK);\n\n        AssetEntry assetEntry = assetRendererFactory.getAssetEntry(\n            assetRendererFactory.getClassName(), assetRenderer.getClassPK());\n\n        List<AssetCategory> assetCategories = assetEntry.getCategories();\n\n        returnValue = \"Default Review\";\n\n        for (AssetCategory assetCategory : assetCategories) {\n            String categoryName = assetCategory.getName();\n\n            if (categoryName.equals(\"legal\")) {\n                returnValue = \"Legal Review\";\n\n                return;\n            }\n        }\n       ]]>\n</script>\n<script-language>groovy</script-language>\n```\n\nWithin a workflow, the next task or state is chosen based on the return value.\nSee some examples of workflow scripts by accessing the\n[embedded workflows](/docs/7-2/user/-/knowledge_base/u/workflow#embedded-workflows)\nand inspecting the XML.\n\n## Calling OSGi Services\n\nHow do you call OSGi services from a workflow script, accounting for the dynamic\nenvironment of the OSGi runtime, where services your script depends on can\ndisappear without notice?\n[Use a service tracker](/docs/7-2/frameworks/-/knowledge_base/f/service-trackers-for-osgi-services).\nThat way you can make sure your code has access to the service it\nneeds, and if not, do something appropriate in response. Here's a little example\ncode to show you how this might look in Groovy:\n\n```groovy\nimport com.liferay.journal.model.JournalArticle;\nimport com.liferay.journal.service.JournalArticleLocalService;\nimport com.liferay.portal.scripting.groovy.internal.GroovyExecutor;\n\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.FrameworkUtil;\nimport org.osgi.util.tracker.ServiceTracker;\n\nServiceTracker<JournalArticleLocalService, JournalArticleLocalService> st;\n\ntry {\n    Bundle bundle = FrameworkUtil.getBundle(GroovyExecutor.class);\n\n    st = new ServiceTracker(bundle.getBundleContext(), JournalArticleLocalService.class, null);\n    st.open();\n\n    JournalArticleLocalService jaService = st.waitForService(500);\n\n    if (jaService == null) {\n        _log.warn(\"The required service 'JournalArticleLocalService' is not available.\");\n    }\n    else {\n        java.util.List<JournalArticle>articles = jaService.getArticles();\n        if (articles != null) {\n            _log.info(\"Article count: \" + articles.size());\n        } else {\n            _log.info(\"no articles\");\n        }\n    }\n}\ncatch(Exception e) {\n    //Handle error appropriately\n}\nfinally {\n    if (st != null) {\n        st.close();\n    }\n}\n```\n\nIf you read the article on [service\ntrackers](/docs/7-2/frameworks/-/knowledge_base/f/service-trackers-for-osgi-services), the only odd\nlooking piece of the above code is the `getBundle` call: why is\n`GroovyExecutor.class` passed as a parameter? The parameter passed to the\n`FrameworkUtil.getBundle` call must be a class from the OSGi bundle executing\nthe workflow script. This is different from the context of a plugin project,\nwhere you'd want to get the bundle hosting the class where you're making the\ncall (using `this.getClass()`, for example). Note that for another scripting\nengine, you must pass in a concrete class from the particular bundle executing\nyour script.\n\nThe combination of Liferay's script and workflow engines is incredibly powerful.\nSince, however, it enables users to execute code, it can be dangerous. When\nconfiguring your permissions, be aware of the potential consequences of poorly\nor maliciously written scripts inside a workflow definition. For more\ninformation on creating workflow definitions, see the [workflow\ndocumentation](/docs/7-2/user/-/knowledge_base/u/workflow).\n\n## Related Topics\n\n[Running Scripts From the Script Console](/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console)\n\n[Script Examples](/docs/7-2/user/-/knowledge_base/u/script-examples)\n"
  },
  {
    "path": "en/user/articles/05-setting-up/03-script-console/04-script-examples.markdown",
    "content": "---\nheader-id: script-examples\n---\n\n# Script Examples\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/using-the-script-engine/script-examples.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nHere are some examples to help you use Liferay's script console. Note: Most of\nthese originated from a [Liferay blog post](https://liferay.dev/blogs/-/blogs/5-tips-to-improve-usage-of-the-liferay-script-console).\n\nThe following scripts are Groovy scripts but they can be adapted to other\nlanguages.\n\n- [Example 1: Presenting New Terms of Use to Users](#example-1-presenting-new-terms-of-use-to-users)\n\n- [Example 2: Embedding HTML Markup in Script Outputs](#example-2-embedding-html-markup-in-script-outputs)\n\n- [Example 3: Show Exceptions in the Script Console](#example-3-show-exceptions-in-the-script-console)\n\n- [Example 4: Implement a Preview Mode](#example-4-implement-a-preview-mode)\n\n- [Example 5: Plan a File Output for Long-Running Scripts](#example-5-plan-a-file-output-for-long-running-scripts)\n\n## Example 1: Presenting New Terms of Use to Users\n\nThis example retrieves user information from the database, makes changes, and\nthen saves the changes in the database. Suppose that your company has\nupdated the\n[terms of use](/docs/7-2/user/-/knowledge_base/u/instance-configuration-instance-settings#terms-of-use)\nand wants present users with the updated terms of use whenever they sign in\nnext. When they agree to the terms of use, a boolean attribute called\n`agreedToTermsOfUse` is set in their user records. As long as the value of this\nvariable is `true`, users aren't presented with the terms of use when they\nsign in. If you set this flag to `false` for each user, each user must\nagree to the terms of use again before they can sign in.\n\n1.  Enter and execute the following code in the script console:\n\n    ```groovy\n    import com.liferay.portal.kernel.service.UserLocalServiceUtil\n\n    userCount = UserLocalServiceUtil.getUsersCount()\n    users = UserLocalServiceUtil.getUsers(0, userCount)\n\n    for (user in users) { println(\"User Name: \" + user.getFullName() + \" -- \" +\n    user.getAgreedToTermsOfUse()) }\n    ```\n\n    This code prints each user's `agreedToTermsOfUse` attribute value.\n\n2.  Replace that with this script:\n\n    ```groovy\n    import com.liferay.portal.kernel.service.UserLocalServiceUtil\n\n    userCount = UserLocalServiceUtil.getUsersCount()\n    users = UserLocalServiceUtil.getUsers(0, userCount)\n\n    long currentUserId = Long.parseLong(userInfo.get(\"liferay.user.id\"))\n\n    for (user in users){\n\n        if(!user.isDefaultUser() && (user.getUserId() != currentUserId)) {\n\n                user.setAgreedToTermsOfUse(false)\n                UserLocalServiceUtil.updateUser(user)\n\n        }\n\n    }\n    ```\n\n    This sets each user's `agreedToTermsOfUse` attribute to `false`. It skips\n    the default user as well as the default admin user that's currently signed\n    in and running the script.\n\n3.  Click *Execute*.\n\n4.  Verify the script updated the records by running the first script again.\n\n    All users (except the default user and your user) have been updated.\n\nYou've enabled the new terms of use agreement for all users to accept.\n\n## Example 2: Embedding HTML Markup in Script Outputs\n\nThe output of the script console is rendered as HTML content. Thus, you can\nembed HTML markup in your output to change its look and feel. Here's an\nexample:\n\n```groovy\nimport com.liferay.portal.kernel.service.*\n\nnumber = com.liferay.portal.kernel.service.UserLocalServiceUtil.getUsersCount();\nout.println(\n        \"\"\"\n                <div style=\"background-color:black; text-align: center\">\n                        <h1 style=\"color: #37A9CC; font-size:xx-large\">${number}</h1>\n                </div>\n        \"\"\");\n```\n\n![Figure 1: Here's an example of invoking a Groovy script that embeds HTML markup in the output of the script.](../../../images/groovy-script-embed-html-markup.png)\n\n## Example 3: Show Exceptions in the Script Console\n\nWhen any exception occurs during script execution, the error message is always\nthe same:\n\n    Your request failed to complete.\n\nThis message gives no detail about the error. To find information about the\nerror and what caused it, you must usually examine the server logs.\n\nYou can, however, use the following technique to make exception details appear in\nthe script console. Wrap your code with a try / catch block and print the\nstacktrace to the console output from the catch clause. Note that even this\ntechnique does not catch script syntax errors. Here's an example:\n\n```groovy\ntry {\n        nullVar = null\n        out.println(nullVar.length())\n} catch(e) {\n        out.println(\"\"\"<div class=\"portlet-msg-error\">${e}</div>\"\"\")\n        e.printStackTrace(out)\n}\n```\n\n![Figure 2: Here's an example of a Groovy script that catches exceptions and prints exception information to the script console.](../../../images/groovy-script-show-exception.png)\n\n## Example 4: Implement a Preview Mode\n\nSince Liferay's script console does not provide an undo feature, it can be\nconvenient to set up a kind of preview mode. The purpose of a preview mode is\nto determine any permanent effects of a script before any information is\nactually saved to the Liferay database. The preview mode consists in using\na `previewMode` flag which determines whether the operations with permanent\neffects should be executed or not. If `previewMode` is `true`, all the data\nthat would be permanently affected by the script is printed instead. Then you\ncan see an outline of the data impacted by the script. If everything is okay,\nswitch the flag so the script can make permanent updates to the database.\n\nHere's an example Groovy script that sets users to inactive. Clearly, you'd\nwant to test this with preview mode before running it:\n\n```groovy\nimport java.util.Calendar\nimport com.liferay.portal.kernel.service.*\nimport com.liferay.portal.kernel.model.*\nimport com.liferay.portal.kernel.dao.orm.*\nimport static com.liferay.portal.kernel.workflow.WorkflowConstants.*\n\n//\n// Deactivate users never logged and created since more than 2 years\n//\n\npreviewMode = true // Update this flag to false to really make changes\n\nCalendar twoYearsAgo = Calendar.getInstance()\ntwoYearsAgo.setTime(new Date())\ntwoYearsAgo.add(Calendar.YEAR, -2)\n\nDynamicQuery query = DynamicQueryFactoryUtil.forClass(User.class)\n        .add(PropertyFactoryUtil.forName(\"lastLoginDate\").isNull())\n        .add(PropertyFactoryUtil.forName(\"createDate\").lt(twoYearsAgo.getTime()))\n\nusers = UserLocalServiceUtil.dynamicQuery(query)\n\nusers.each { u ->\n         if(!u.isDefaultUser() && u.getStatus() != STATUS_INACTIVE) {\n                out.println(u.getEmailAddress())\n                if(!previewMode) {\n                        UserLocalServiceUtil.updateStatus(u.getUserId(), STATUS_INACTIVE)\n                }\n         }\n}\n\nif(previewMode) {\n        out.println('Preview mode is on: switch off the flag and execute '\n                + 'again this script to make changes to the database')\n}\n```\n\n## Example 5: Plan a File Output for Long-Running Scripts\n\n| **Important:** The script console is for system operations and maintenance and\n| not for end users. Limit script console access to portal administrators.\n\nWhen a script has been running for a long time, the console could return an\nerror even though the script can continue running and potentially conclude\nsuccessfully. But it's impossible to know the outcome without the corresponding\noutput!\n\nTo bypass this limitation, you can send the output of the script console to a\nfile instead of to the console itself or to the Liferay log. For example,\nconsider this script:\n\n```groovy\nimport com.liferay.portal.kernel.service.*\nimport com.liferay.portal.kernel.dao.orm.*\n\n// Output management\n\nfinal def SCRIPT_ID = \"MYSCRIPT\"\noutputFile = new File(\"\"\"${System.getProperty(\"liferay.home\")}/scripting/out-${SCRIPT_ID}.txt\"\"\")\noutputFile.getParentFile().mkdirs()\n\ndef trace(message) {\n        out.println(message)\n        outputFile << \"${message}\\n\"\n}\n\n// Main code\n\nusers = UserLocalServiceUtil.getUsers(QueryUtil.ALL_POS, QueryUtil.ALL_POS)\nusers.each { u ->\n        trace(u.getFullName())\n}\n```\n\nThe script above creates a subfolder of\n[Liferay Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\ncalled `scripting` and saves the script output to a file in this folder. After\nrunning the script above, you can read the generated file without direct access\nto the file system. Here's a second script that demonstrates this:\n\n```groovy\nfinal def SCRIPT_ID = \"MYSCRIPT\"\noutputFile = new File(\"\"\"${System.getProperty(\"liferay.home\")}/scripting/out-${SCRIPT_ID}.txt\"\"\")\nout.println(outputFile.text)\n```\n\nOne advantage of using a dedicated output file instead of using a classic logger\nis that it's easier to get the script output data back. Getting the script\noutput data would be more difficult to obtain from the portal log, for example,\nbecause of all the other information there.\n\n## Related Topics\n\n[Running Scripts From the Script Console](/docs/7-2/user/-/knowledge_base/u/running-scripts-from-the-script-console)\n\n[Leveraging the Script Engine in Workflow](/docs/7-2/user/-/knowledge_base/u/leveraging-the-script-engine-in-workflow)\n\n[Using Liferay's Script Engine](/docs/7-2/user/-/knowledge_base/u/using-liferays-script-engine)\n"
  },
  {
    "path": "en/user/articles/05-setting-up/08-custom-fields.markdown",
    "content": "---\nheader-id: custom-fields\n---\n\n# Custom Fields\n\n[TOC levels=1-4]\n\nHave you ever wondered why there's no *Head Circumference* field in the form for\nadding users to @product@? Probably because most sites based on @product@ don't\nneed it. If you're an administrator at the Lunar Resort, however, you certainly\nneed to know your guests' head circumference so you can provide them with\na properly fitting helmet. \n\nMany of @product@'s assets and resources let you add new fields to their edit\nforms. Here's the complete list:\n\n- Blogs Entry\n- Calendar Event\n- Document\n- Documents Folder\n- Knowledge Base Article\n- Knowledge Base Folder\n- Message Boards Category\n- Message Boards Message\n- Organization\n- Page\n- Role\n- Site\n- User\n- User Group\n- Web Content Article\n- Web Content Folder\n- Wiki Page\n\n| **Developer Use Case:** Adding custom fields to @product@ resources affords\n| flexibility to developers. Suppose you must limit the number of users\n| that can be assigned to a particular Role. First an administrator creates a custom\n| field called *max-users* for the Role. A developer then creates a module that\n| inserts logic before a user is added to that Role. If the logic detects that\n| the maximum number of Role users would be exceeded by completing the action, an\n| exception is thrown and the action does not complete.\n\n## Adding Custom Fields\n\nTo add custom fields, find the Custom Fields entry beneath the Control Panel's\nConfiguration heading.\n\nTo add a custom field to one of the listed entities, \n\n1.  Choose a resource by clicking on it.\n\n2.  Click the add (![Add](../../images/icon-add.png)) button.\n\n3.  Choose a field type: Text Area, Input Field, Dropdown, Checkbox, Radio,\n    Geolocation, Date, True/False. \n\n4.  Add a name that's used as a key for accessing the field programmatically.\n\n    | **Note:** The Key you enter here is the name of the new field. It's stored\n    | in the database and used by developers to access the custom field with the\n    | `<liferay-ui:custom-attribute />` tag. It is also used to label the\n    | field in the UI.\n\n4. Choose the Data Type of field and set any advanced properties.\n\n    ![Figure 1: At The Lunar Resort, a Head Circumference field is necessary for all users.](../../images/custom-fields-user-head-circumference.png)\n\n5. Click Save.\n\nThat's it.\n\nOnce you have a custom field for a resource, go find it in the UI of the\nresource. First find the UI location for the resource, and all its custom fields\nare displayed in a Custom Fields panel. For example, consider the Users UI:\n\nNavigate to *Control Panel &rarr; Users and Organizations*. Click on a User to\nopen the Edit User form and scroll down to find your custom field.\n\n![Figure 2: The Custom Fields panel is found at the bottom of the Edit User form.](../../images/custom-fields-panel.png)\n\nYou can also leverage your custom field in Content Pages and Display Page\nTemplates. See \n[Editable Elements](/docs/7-2/user/-/knowledge_base/u/content-page-elements#editable-elements)\nfor more information.\n\n## Editing a Custom Field\n\nYou can't change the key or field type of a custom field, but you can delete it\nand create a new one if necessary. \n\n![Figure 3: The exact Custom Fields configuration options you use depend on the field type you choose.](../../images/custom-fields-configuration.png)\n\nEdit an individual custom field's permissions by clicking the field's kebab menu\n(![Actions](../../images/icon-actions.png)), then *Permissions*.  Permission\ncan be granted or removed for these actions:\n\n- Delete\n- Permissions\n- Update\n- View\n\n![Figure 4: You can delete a custom field, edit it, or configure its permissions.](../../images/custom-fields-edit.png)\n\nCustom fields make many of @product@'s entities extensible directly from the\nadministrative user interface. Use them as is or combine them with some\nback-end code, and you have yet another powerful, flexible feature at your\ndisposal. As they're fond of saying at The Lunar Resort, \"The sky is certainly\nnot the limit.\"\n"
  },
  {
    "path": "en/user/articles/10-managing-users/01-intro.markdown",
    "content": "---\nheader-id: managing-users\n---\n\n# Managing Users\n\n[TOC levels=1-4]\n\nEver heard a retailer advertise as a \"one stop shop\" for anything you want? The\nidea is they have so much stuff that whatever you want is probably there.\nLiferay's Control Panel is like this. Where do you create Users, Organizations,\nor Sites? Where do you configure permissions and plugins and pretty much\nanything else? You do it from the Control Panel.\n\n![Figure 1: Administrators can access the Control Panel from the Product Menu.](../../images/usrmgmt-control-panel.png)\n\nThe Control Panel is divided into six main areas: Users, Sites, Apps,\nConfiguration, and Workflow. The Users section lets you create and\nmanage Users, Organizations, User Groups, Roles, and Password Policies. If\nmonitoring has been enabled, you can also view all the live sessions of your\nUsers.\n\n| **Anonymous User:** *Anonymous Anonymous* is used for the new\n| [Managing User Data](/docs/7-2/user/-/knowledge_base/u/managing-user-data)\n| functionality. Created the first time an administrator clicks *Delete Personal\n| Data* for a User, *Anonymous Anonymous* is a deactivated User assigned\n| [anonymized assets](/docs/7-2/user/-/knowledge_base/u/managing-user-data#anonymizing-data).\n| The Anonymous User is configurable, so the name and configuration details might\n| be different in your virtual instance.\n\nBegin exploring Liferay's User Management functionality by reading about\nadding and editing users. \n\n"
  },
  {
    "path": "en/user/articles/10-managing-users/02-users-and-organizations/01-intro.markdown",
    "content": "---\nheader-id: users-and-organizations\n---\n\n# Users and Organizations\n\n[TOC levels=1-4]\n\n*Users* and *Organizations* are fundamental entities. If your site requires\npeople (even just a set of site administrators) to have accounts to do anything,\nyou need to know about users. If your users are at all divided hierarchically,\nlike into departments, you'll find that organizations are helpful. \n\nYou're probably not surprised to hear that Users and Organizations are managed\nin the Control Panel's *Users and Organizations* section. If it were any\ndifferent, it'd be weird. \n\nConsider the Lunar Resort site. Consider what you'd do if \n\n- An employee leaves the company to join that pesky competitor, Martian Resort\n  and Luxury Spa.\n- An employee joins the resort as a new Mechanical Crew member.\n- An employee is promoted from Crew Supervisor to Department Head and needs the requisite\n  permissions.\n- You need to organize the users by department.\n- A new department is added to the Lunar Resort and the employees need their own\n  internal website.\n- An employee gets married, and their name changes.\n\nThe user tasks listed above are all resolved in the Users and Organizations\nsection of the Control Panel.\n\n## What are Users?\n\nIn case there's any confusion over the term, a User is an entity that can sign\ninto the portal and do something. Generally a User has more privileges, called\nPermissions, than a Guest of your site, who does not sign in. Users are assigned\nRoles, and Roles define the User's privileges.\n\nUnderstanding Users is pretty straightforward. Organizations are a bit trickier,\nbut a smart administrator like you is undoubtedly up to the challenge. Read more\nabout Organizations\n[here](/docs/7-2/user/-/knowledge_base/u/organizations).\n\nThe remaining articles in this section give you guidance on managing (creating,\ndeleting, editing, and more) Users and Organizations.\n"
  },
  {
    "path": "en/user/articles/10-managing-users/02-users-and-organizations/02-managing-users.markdown",
    "content": "---\nheader-id: adding-editing-and-deleting-users\n---\n\n# Adding, Editing, and Deleting Users\n\n[TOC levels=1-4]\n\nAt the root of managing Users is adding, editing, and deleting them. As long as\nyou're the Administrative user, you can do all these things and more. \n\n## Adding Users\n\nHere's how to edit Users: \n\n1.  From the Product Menu, click *Control Panel* &rarr; *Users* &rarr; *Users\nand Organizations*. \n\n2.  In the Users tab, click the *Add* button <!-- ([Add](../../images/icon-add.png)-->.\n\n    ![Figure 1: Add Users from the Users and Organizations section of the Control Panel.](../../../images/usrmgmt-add-user.png)\n\n3.  Fill out the Add User form and click *Save*. At a minimum, provide a Screen\n    Name, First Name, Last Name, and Email Address for the User.\n\n    | Note: Screen names and email addresses are not interchangeable. A screen\n    | name cannot contain an `@` symbol because it is used in the URL to a User's\n    | private page.\n\n    The Add User functionality is split over several independent forms. Saving\n    the first form creates the User, and then you'll see a success message\n    saying\n\n        Success. Your request completed successfully. \n\nAfter submission of the first form, you see a larger form with many sections.\nThe one you're on is the Information section. To the left is a navigation\npane where you can continue configuring the user you're adding by clicking\nthrough the available sections. The options in the left menu change as you\nclick through the tabs at the top. Peruse the sections for the three tabs\n(General, Contact, Preferences) and fill in all the applicable information.\n\n![Figure 2: At a minimum, enter a screen name, email address, and first name to create a new user account. Then you'll be taken to the Information form and can continue configuring the user.](../../../images/add-user-forms-menu.png)\n\nYou don't have to fill anything else out right now. Just note that when the user\naccount was created, a password was automatically generated. If Liferay was\ncorrectly installed and a \n[mail server was set up](/docs/7-2/user/-/knowledge_base/u/server-administration), \nan email message with the User's new password was sent to the User's email\naddress.\n\nIf you haven't set up a mail server, click the *Password* item from the General\nmenu and manually set a password for your new user. Enter the new password\ntwice.\n\n![Figure 3: Enter the password twice to manually set the password for a user. If the Password Policy you're using is configured to allow it, select whether to require the user to reset their password the first time they sign in to the portal.](../../../images/usrmgmt-require-password-reset.png)\n\n## Editing Users\n\nIf you click on *Users and Organizations* in the Control Panel, you'll see your\nown user's account in the list of Users, along with any others. To change\nsomething about a particular user, click the *Actions* button\n(![Actions](../../../images/icon-actions.png)) next to that user.\n\nChoosing *Edit* takes you back to the Edit User page where you can modify any\naspect of the User account including the screen name, email address, first name,\nlast name, Site and Organization memberships, Roles, etc.\n\nChoosing *Permissions* allows you to define which Roles have permissions to edit\nthe User.\n\nChoosing *Manage Pages* allows you to configure the personal pages of a User.\n\nChoosing *Impersonate User* opens another browser window that loads the site as\nif you were the User so you can test your User management on a User to make\nsure you're achieving the desired behavior, without having to repeatedly log\nout of your administrator account and into the User's account.\n\nChoosing *Deactivate* deactivates the user's account. The User is still in\nyour database along with all the rest of your Users, but the account is\ndeactivated, so the User cannot sign in to the portal. You can toggle between\nactive and inactive Users in the Users view. If all the Users are active, this\nfiltering option doesn't appear.\n\nChoosing *Erase Personal Data* \n[deletes the User's personal data](/docs/7-2/user/-/knowledge_base/u/managing-user-data). \n\nChoosing *Export Personal Data* lets you \n[download the User's personal data](/docs-7-2/user/-/knowledge_base/u/exporting-user-data).\n\n![Figure 4: You can choose whether to view active or inactive (deactivated) portal users in the users list found at *Product Menu* &rarr; *Control Panel* &rarr; *Users* &rarr; *Users and Organizations*.](../../../images/usrmgmt-active.png)\n\nMost Users can't perform any of the above actions. In fact, most Users won't\nhave access to the Control Panel at all. You can perform all of the above\nfunctions because you have administrative access.\n\n## Deleting Users\n\nYou must be careful when deleting Users. To guard against accidental deletion\nof Users, a two-step process must be followed: deactivate first, then delete.\n\n1.  Find the User to delete in the Users tab of *Control Panel* &rarr; *Users*\n    &rarr; *Users and Organizations*. If you have a lot of Users, save time by\n    searching for the User.\n\n2.  Click the *Actions* menu for the User and select *Deactivate*. You're asked to\n    confirm that you want to deactivate the User. Click *OK*. \n\n    You'll see a success message and the User disappears, but isn't gone yet.\n\n3.  By default the Users table displays only Active users. Click on *Filter and\n    order* in the top of the table and a dropdown menu appears. Click\n    *Inactive*, and you can see the User you just deactivated. \n\n4.  Click the Actions menu again, and click *Delete* if you really mean to\n    delete the User. Confirm that you want to delete the User, and now the User\n    is gone. This time, it's for real.\n\n| **Deactivated Users:** Deactivating a User means the User can't log in to the\n| portal. He/she has no more permissions in the Sites and pages of the portal than\n| a guest, although the account still exists in the system.\n| \n| Users are reactivated when an administrator finds them in the Users table\n| (be sure you're filtering the table results by Deactivated users), clicks the\n| Actions menu, and selects Activate. There's no confirmation window for\n| activation: they're automatically restored to their former status once Activate\n| is clicked.\n\nNow you understand the basic principles of User administration. There are\nimportant additional topics in the next article that you should consider\nmandatory information for all portal administrators, so do continue reading.\n"
  },
  {
    "path": "en/user/articles/10-managing-users/02-users-and-organizations/03-advanced-user-management.markdown",
    "content": "---\nheader-id: user-management-additional-topics\n---\n\n# User Management: Additional Topics\n\n[TOC levels=1-4]\n\nYou've learned the basics on adding and editing Users, but there are additional\nimportant topics that go beyond the most basic tasks an administrator must\nperform. Read on to learn about these.\n\n## Password Resets\n\nThe Add User functionality includes a *Require Password Reset* checkbox at the\nbottom of the Password form. The default password policy does not even allow\nadministrators to deselect this option. As the administrator, however, you can\nmodify the default password policy so that this box becomes usable.\n\n1.  Navigate to *Password Policies* in Control Panel &rarr; Users.\n\n2.  Click on the *Default Password Policy*.\n\n3.  Deselect the *Change Required* switcher in the Password Changes section. Now\n    you can decide whether users you add must reset their passwords.\n\nSee [Password Policies](/docs/7-2/user/-/knowledge_base/u/password-policies)\nfor more information on editing the default policy or creating your own.\n\n## Adding an Administrative User\n\nIf you're setting things up for the first time, you're likely to be using the\ndefault administrator account, the account of one of those famous Liferay\nAdministrators, *Test Test* or her cousin, *Joe Bloggs*. Because these are\ndefault accounts, hackers know about them, so it's better to set up your own\nadministrator account. Add a user with your information, then give your user\naccount the same administrative rights as the default administrator's account:\n\n1.  Click the *Roles* link in the left navigation pane (in the *Edit User*\n    page's *General* tab). This page of the form shows the Roles to which your\n    account is currently assigned. No Roles appear by default (the User role\n    does not appear since it can't be removed).\n\n2.  Click *Select* under Regular Roles and assign the Administrator Role to your\n    user account. A dialog box pops up with a list of all the regular\n    (portal-scoped) Roles in the portal. Select the Administrator Role from the\n    list (click *Choose*). The dialog box disappears and the Role is added to\n    the list of Roles associated with your account. You are now a portal\n    administrator. Log out and then log back in with your own user account.\n\n| **Power Users:** Users are not assigned the Power User Role by default. The\n| Power User Role grants more permissions than the User Role. If the User Role is\n| sufficient for you, ignore the Power User Role. Alternatively, use it to provide\n| a second level of User permissions and assign it to those Users. If there are\n| certain custom permissions that you'd like all of your portal Users to have, you\n| can grant these permissions to the User Role. You can also customize the default\n| Roles a new User receives via *Default User Associations*. This is covered in\n| [Setting Up a Virtual Instance](/docs/7-2/user/-/knowledge_base/u/setting-up-a-virtual-instance).\n\nIn production, you should always delete or disable the default administrator\naccount to secure your portal.\n\n## Gender\n\nTo collect data on users' genders, enable the binary gender field in the *Add\nUser* form or create a\n[custom field](/docs/7-2/user/-/knowledge_base/u/custom-fields)\nthat meets your needs.\n\nEnable the binary field by including the following in `portal-ext.properties`:\n\n    `field.enable.com.liferay.portal.kernel.model.Contact.male=true`\n\n## User Profile Pictures\n\nUsers have profile pictures. Administrative Users can upload images in the Edit\nUser form, and any User can update her own account information, including image,\nfrom her personal site (*My account* &rarr; *Account Settings*).\n\n![Figure 1: Upload images for user avatars in the Edit User form.](../../../images/usrmgmt-ray-avatar.png)\n\nIf no image is explicitly uploaded for a User's profile picture, a default User\nicon is assigned as the User avatar. By default the User's initials are\ndisplayed (First Name then Last Name) over a random color.\n\n![Figure 2: The default user profile picture is an icon with the user initials over a randomly colored bubble.](../../../images/users-default-user-image.png)\n\nIf the initials-based approach for generating User profile pictures isn't\nsuitable for your portal, disable the inclusion of Users' initials in the\ndefault icons:\n\n1.  Navigate to *Control Panel* &rarr; *Configuration* &rarr; *System Settings*.\n\n2.  In the Platform section, click *Users* &rarr; *User Images*.\n\n3.  Deselect *Use Initials for Default User Portrait*.\n\nNow, instead of the default icon, which is a colorful circle containing the\nuser's initials, the icon is a gray circle containing the approximate shape of a\nhuman being.\n\n![Figure 3: If you disable the default initials-based profile picture, this icon is used instead.](../../../images/user-image-not-initials.png)\n\nThis is just the default. To override it with your own default image:\n\n1.  Create at least one image that is a 100x100 px square. Place it somewhere on\n    the application server's classpath. For example, in Tomcat you could place\n    it in the `tomcat/webapps/ROOT/WEB-INF/classes` folder.\n\n2.  Set the following property in a `portal-ext.properties` file:\n\n        image.default.user.portrait=image-filename-here.png\n\n    This overrides the value of this\n    [portal property](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html):\n\n        image.default.user.portrait=com/liferay/portal/dependencies/user_portrait.png\n\n    | **NOTE:** If you are using the binary field to collect information on users'\n    | genders (see above), then you'll have two default images to override. Set\n    | these properties instead:\n    |\n    |     image.default.user.female.portrait=image-filename.png\n    |     image.default.user.male.portrait=image-filename.png\n\n3.  Restart the application server.\n\n| **Note:** There's a way to adjust which initials are displayed and in what\n| order, so you can make the default user icon (with the user initials) work\n| for your locale. These settings are configured in a\n| [Language Settings module](/docs/7-2/frameworks/-/knowledge_base/f/using-liferays-localization-settings),\n| so kidnap a friendly developer, give him a cup of coffee, and tell him the\n| settings you want to change:\n|\n| `lang.user.default.portrait=initials` sets the type of icon to use for avatars.\n| The default value is *initials*. If set to initials, the next property\n| configures which initials to display, and in what order. Alternatively, specify\n| *image*, which gives you the same non-initials default image shown above.\n|\n| `lang.user.initials.fields=first-name,last-name` determines which initials\n| appear in the user portrait and in what order. The setting here only matters if\n| `lang.user.default.portrait` is set to *initials*.  Valid values are first name,\n| middle name, last name, with first and last name as the defaults.\n\n## Numeric Screen Names\n\nIn prior versions, numeric user screen names were disabled out of the box via\nthe default portal property\n\n    users.screen.name.allow.numeric=false\n\nOther user management systems (LDAP, for example) did not have the same\nrestriction, which made importing users more difficult. Administrators first had\nto set the  above property to `true` before importing and hope that no screen\nnames conflicted with site IDs. In @product-ver@, this property defaults to\n`true` and there's no danger of numeric screen names conflicting with site IDs:\n\n    users.screen.name.allow.numeric=true\n\nThis means you're free to set a user screen name to *24601*, or whatever other\nnumber you can think of, and imports from systems that allow numeric screen\nnames go more smoothly. That's everything you need to know to take advantage of\nthis feature. Keep reading to understand what enabled the change.\n\nBecause users have personal sites, the URL to user *24601*'s personal site is\n\n    http://localhost:8080/web/24601\n\nMeanwhile, a default site URL to cleverly named *Test Site* is\n\n    http://localhost:8080/web/test-site\n\nThere's no conflict here, but two conditions could easily lead to one:\n\n1.  *Test Site*'s group ID matches the number chosen for the user's screen\n    name. Each site has a unique numeric identifier in the database, called\n    group ID. There's nothing stopping it from matching the user's numeric\n    screen name, so it could easily be `24601` just like the hypothetical user\n    above.\n\n2.  A site administrator comes along and changes the site's friendly URL to\n    match its `groupId`. Hello, URL conflict! Now the site's URL matches the\n    user's URL:\n\n        http://localhost:8080/web/24601\n\nThis conflict is no longer possible. In @product-ver@, a site's friendly URL\nis not allowed to be numeric. See for yourself:\n\n1.  Navigate to the site's *Configuration* &rarr; *Site Settings* &rarr; *Site URL*\n    section.\n\n2.  In the Friendly URL section, enter *24601* and save the form. A failure\n    message appears if you don't have a user with the matching screen name:\n\n        The friendly URL may conflict with another page.\n\n    You'll see this failure message if there's an existing conflict with a user\n    screen name:\n\n        Please enter a unique friendly URL. Site [user-first-name user-last-name] has the same friendly URL.\n\nNext, learn about collecting users in organizations.\n"
  },
  {
    "path": "en/user/articles/10-managing-users/02-users-and-organizations/04-organizations.markdown",
    "content": "---\nheader-id: organizations\n---\n\n# Organizations\n\n[TOC levels=1-4]\n\nAn *Organization* groups\n[*Users*](/docs/7-2/user/-/knowledge_base/u/adding-editing-and-deleting-users) \nhierarchically. For example, you can model a company's departments (i.e., Human\nResources and Customer Support) with Organizations. Organizations often have\ntheir own Sites. The *how-to* portion of managing Organizations is in the next\narticle, \n[Managing Organizations](/docs/7-2/user/-/knowledge_base/u/managing-organizations).\nThis article contains important conceptual information on what Organizations are\nand when they're needed.\n\nMany simple portal designs don't use Organizations at all; they only use sites.\nThe main purpose of Organizations is to enable distributed User management.\nPortal administrators can delegate some user management responsibilities to\nOrganization administrators. If you don't anticipate needing to delegate User\nmanagement responsibilities, your portal design probably doesn't need to include\nOrganizations. \n\n| **User Groups and Organizations:** It's easy to confuse User Groups (covered in\n| a separate article) with Organizations since they both group Users. User Groups\n| are an ad hoc collection of Users, organized for a specific function. In the\n| Lunar Resort, if you wanted a group of bloggers, for example, it wouldn't make\n| sense to assign the Sales Department the role of blogging (see the article on\n| Roles if you're not sure what they are). The Sales Department users could blog\n| whenever a new T-shirt design became available in the Lunar Resort store, but\n| they probably wouldn't be as diligent about announcing the new Rover Racing\n| schedule. Instead, creating a User Group containing one individual from each\n| department who is responsible for blogging would make more sense. Read the\n| article on User Groups to learn more about how to use them in your portal.\n\n### When to Use Organizations\n\nTo decide whether your portal design should include Organizations, think about\nits function. A photo-sharing web site could be powered by Sites only. On the\nother hand, Organizations are useful for corporations or educational\ninstitutions since their users can be placed into a hierarchical structure.\nDon't think that Organizations are only for large enterprises, though. Any group\nhierarchy, from large government agencies all the way down to small clubs, can\nbe modeled with Organizations. Also, don't think that you must decide between an\nOrganization-based structure or a Site-based structure for assembling your\nportal's Users. Users can belong both to Organizations and to independent Sites.\nFor example, a corporation or educational institution could create a social\nnetworking site open to all Users, even ones from separate Organizations. \n\nTo illustrate what an Organization is, consider a potential Organization of the\nLunar Resort's Intranet. The company hierarchy has three tiers: The Lunar\nResort, its departments, and divisions within each department.\n\n- Lunar Resort--The top-level Organization.\n\t- Physical Plant Department--Department of users that keep the place running.\n\t\t- Grounds Crew--Users that maintain the grounds.\n\t\t- Janitorial Crew--Users who keep the resort clean.\n\t\t- Mechanical Crew--Users who fix stuff, like lunar rovers.\n\t- Recreation Department--A department that makes sure much fun\n\t  is had by guests of the Lunar Resort.\n        - Golf Instructors--Teach guests how to golf on the moon.\n        - Rover Race Instructors--Teach guests how to drive the lunar rovers.\n        - Lunar Sherpas--Lead guests on moon hikes.\n    - Sales Department--A department of users who sell things to Lunar\n      Resort guests.\n        - Up-sale Group--Make sure guests know how easy it is to improve\n          their stay by spending more money.\n        - Souvenir and Memorabilia Group--Peddle souvenirs to Lunar Resort guests.\n        - Retail Group--Maintain the Lunar Resort store, which\n          contains basic necessities, since guests are coming all the way from Earth.\n    - Sentient Organism Resources Department--Department of Users that hire, fire and\n        regulate intra-company relationships. We'd call it Human Resources, but\n        what's stopping Martians from applying? Nothing!\n\nEach department is a sub-Organization of the resort, and each division is a\nsub-Organization of the department.\n\n### What can Organization Administrators Do?\n\nWhenever you have a collection of Users that fits into a hierarchical structure,\nyou can use Organizations to model those Users. Organization administrators can\nmanage all the Users in their Organization *and* in any sub-Organization.\nReferring to the hierarchy above, for example, an Organization administrator of\nthe Lunar Resort could manage any Users belonging to the resort itself, to any\nof the departments, or to any of a department's subdivisions. An Organization\nAdministrator of the Physical Plant Department can manage any Users belonging to\nthe Physical Plant Department itself, or to the Grounds Crew, the Janitorial\nCrew, or the Mechanical Crew. However, an administrator of the Physical Plant\nDepartment can't manage Users belonging to the Recreation Department or the\nRetail Group organization.\n\nOrganizations and sub-Organization hierarchies can nest to unlimited levels.\nUsers can be members of one or many Organizations. The rights of an Organization\nadministrator apply both to his/her Organization and to any child Organizations.\nMembers of child Organizations are implicit members of their parent\nOrganizations. This means, for example, that members of child Organizations can\naccess the private pages of their parent Organizations. This behavior can be\ncustomized in the \n`Organizations` \n[section of the portal-ext.properties](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Organizations)\nfile where the properties specific to organizations are listed. \n\nSince Organizations are designed for distributed user administration,\nOrganization Administrators have an entirely different set of privileges than\nSite Administrators. Site Administrators are responsible for the pages,\nportlets, content, and membership of their Sites. To this end, they can set the\nmembership type to Open, Restricted, or Private. They can also add Users to or\nremove Users from their Sites but cannot manage the Users themselves. If an\nOrganization has a Site attached to it, the Organization Administrator has the\nsame rights as a Site Administrator for managing the Site's content, but an\nOrganization Site's members are the members of the Organization. Thus\nOrganization administrators have more user management permissions than Site\nadministrators: they can edit users belonging to their Organization or any\nsub-Organization. They cannot add existing portal Users to their organization,\nbut they can create new Users within their Organization. Only portal\nadministrators can add existing users to an Organization.\n\nOrganization Administrators can't access the Control Panel by default, but it's\nnot necessary. In their personal Sites, Organization administrators can click \nthe *My Organizations* link to gain access to any Organizations they manage.\n\n![Figure 1: The My Organizations application lets Organization Administrators manage their organizations in their personal site.](../../../images/orgs-my-organizations.png)\n\n### Organization Roles and Permissions\n\nA huge time-saving benefit of including Organizations into your portal design is\nthat Organization administrators can assign Organization-scoped Roles to members\nof the entire Organization. For example, consider an IT Security group in a\ncorporate setting. You could have a sub-Organization of your IT organization\nthat handles security for all applications company-wide. If you grant the IT\nSecurity Organization the portal administrator Role, all members of the\nOrganization get administrative access to the entire system. Suppose further\nthat a User in this Organization was later hired by the Human Resources\ndepartment. The simple act of removing the User from the IT Security\nOrganization also removes the User's administrative privileges, since the\nprivilege came from the IT Security Organization's Role. By adding the User to\nthe HR Organization, any roles the HR Organization has (such as access to\na benefits system in the portal) are transferred to the User. In this manner,\nyou can design your portal to correspond with your existing organization chart\nand Users' permissions are granted according to their positions in the chart.\n\nOf course, this is only one way to set things up. If you have more complex\nrequirements for permissions within an Organization, you can create custom\nOrganization-scoped Roles to assemble the permissions you wish to grant to\nparticular Users. Alternatively, attach a Site to your Organization and use Site\nTeams to assemble the sets of permissions (see below). See the \n[Roles and Permissions article](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions)\nfor more detail.\n\n### Organization Sites\n\nDoes your Organization need to have its own Site? If an organization has an\nattached Site, the Organization's administrators are treated as the Site\nadministrators. This means that they can manage the pages, portlets, and content\nof the Site as well as the Users of the Organization. Members of an Organization\nwith an attached Site are treated as members of the Organization's Site. This\nmeans that they can access the private pages of the Organization's Site, along\nwith any portlets or content there. Attaching Sites to Organizations allows\nportal administrators to use Organizations to facilitate distributed portal\nadministration, not just distributed User administration. \n\nThat's a lot of information on Organizations. Next, learn how to create and\nmanage Users and Organizations. \n\n"
  },
  {
    "path": "en/user/articles/10-managing-users/02-users-and-organizations/05-managing-organizations.markdown",
    "content": "---\nheader-id: managing-organizations\n---\n\n# Managing Organizations\n\n[TOC levels=1-4]\n\nIf you're not entirely sure what Organizations are or whether you need them, start\n[here](/docs/7-2/user/-/knowledge_base/u/organizations). \nThis article gets right to the practical stuff: how to manage Organizations.\n\n## Adding Organizations\n\nAdd an Organization (perhaps start by adding the *Physical Plant Department*\norganization to the Lunar Resort): \n\n1.  Click *Users and Organizations* from Control Panel &rarr; Users. \n\n2.  Go to the *Organizations* tab and click the *Add* button. Fill out the Name\n    field at a minimum.\n\n3.  If you're creating a child Organization, use the Parent Organization\n    *Select* button to select an Organization in the system to be the direct\n    parent. Click the *Remove* button to remove the currently configured parent.\n\n4.  Click *Save* when finished filling out the Add Organization form.\n\nAs when creating a new user, once you submit the form a success message appears\nand you have access to a new form which lets you enter additional information\nabout the Organization. Organizations can have associated multiple email\naddresses, postal addresses, web sites, and phone numbers. The Services link can\nbe used to indicate the operating hours of the Organization, if any.\n\n| **Tip:** After creating an Organization, assign the desired user to the\n| Organization Owner Role. The Organization Owner can do everything that an\n| organization Administrator can. In addition to their full administrative rights\n| within the Organization, they can do these things:\n| \n| - Appoint other Users to be Organization Administrators\n| - Appoint other Users to be Organization Owners\n| - Remove the memberships of other Organization Administrators or Owners\n| \n| Organization Administrators can't make these Role assignments and can't manage\n| the memberships of other Organization Administrators or Owners.\n\n## Editing Organizations\n\nTo edit an Organization, go to the Users and Organizations section of the\nControl Panel and click the *Organizations* tab. All active Organizations are\nlisted. Click the *Actions* button next to an Organization. This shows a list of\nactions you can perform on this Organization.\n\n- *Edit* lets you specify details about the Organization, including addresses,\n  phone numbers, and email addresses. You can also create a Site for the\n  Organization.\n\n- *Manage Site* lets you create and manage the public and private pages of the\n  Organization's Site. This only appears after a Site has been created for the\n  Organization. \n\n- *Assign Organization Roles* lets you assign Organization-scoped Roles to\n  Users. By default, Organizations are created with three Roles: Organization\n  Administrator, Organization User and Organization Owner. You can assign one or\n  more of these Roles to Users in the Organization. All members of the\n  Organization automatically get the Organization User Role so this Role is\n  hidden when you click Assign Organization Roles.\n\n- *Assign Users* lets you search and select Users to be assigned to this \n  Organization as members.\n\n- *Add User* adds a new User and assigns the User as a member of\n  this Organization.\n\n- *Add Organization* lets you add a child Organization to this\n  Organization. This is how you create hierarchies of Organizations with\n  parent-child relationships.\n\n- *Delete* removes this Organization. Make sure the\n  Organization has no Users in it first. You'll be prompted for confirmation\n  that you want to delete the Organization. If there are Users in the\n  Organization or if there are sub-Organizations, you must remove the Users and\n  delete the sub-Organizations before deleting the parent Organization.\n\nIf you click the Organization name you can view both a list of Users who are\nmembers of this Organization and a list of all the sub-Organizations of this\nOrganization.\n\n## Organization Types\n\nBy default, @product@ only includes the *Organization* type. Configure the\nexisting type or add additional types using the aptly named Organization Type\nentry in System Settings. There are two main reasons to configure Organization\ntypes:\n\n1.  Organizations usually correlate to real-life hierarchical structures.\n    Calling them by their real names is helpful for administrators and Users. In\n    the Major League Baseball (MLB) example, *League*, *Division*, and *Team*\n    Organization types are useful.\n\n2.  Enforce control over which Organizations can be top level Organizations and\n    the type of sub-Organization allowed for each parent Organization type. For\n    example, MLB would not allow Division Organization types to be\n    sub-Organizations of Team Organizations.\n\n![Figure 1: Create new organization types through the System Settings entry called Organization Types.](../../../images/orgs-organization-type.png)\n\nCheck out the configuration options that configure the default *Organization*\ntype and then configure an additional type.\n\nTo add another Organization type called *League*, enter these options into the\nconfiguration form:\n\nName: *League*\n: Adds League to the list of Organization types that appear in the Add\nOrganization menu.\n\nCountry Enabled: *True*\n: Enables the Country selection list field on the form for adding and editing\nLeague types.\n\nCountry Required: *False*\n: Specifies that the *Country* field is not required when adding a League.\n\nRootable: *True*\n: Enables Leagues as a top level Organization. Limit League to sub-Organization\nstatus by excluding this property.\n\nChildren Types: *Division*\n: Specifies Division as the only allowable sub-Organization type for the League\nparent type.\n\nOnce you configure additional Organization types and click Save, you'll find\nyour new type(s) available for selection in the Add Organization form.\n\n![Figure 2: Custom configuration types are available in the Add Organization form.](../../../images/orgs-add-custom-organization.png)\n\nUsers can join or be assigned to Sites when they share a common interest. Users\ncan be assigned to Organizations when they fit into a hierarchical structure.\nUser groups provide a more ad hoc way to group users than sites and\nOrganizations. You'll look at them next.\n\n"
  },
  {
    "path": "en/user/articles/10-managing-users/03-roles-and-permissions/01-intro.markdown",
    "content": "---\nheader-id: roles-and-permissions\n---\n\n# Roles and Permissions\n\n[TOC levels=1-4]\n\nIf a *Role* were to win a Grammy or an Oscar or some other ego-feeding\npopularity contest, it better remember to thank all its *permissions* groupies\nduring the acceptance speech, because they're the ones doing the real work. The\nRole is just the pretty face, so to speak.\n\nRoles collect permissions that define a particular function, according to\na particular scope. Roles collect permissions, and Users are assigned to Roles.\n\n| **Note:** Roles are assigned to Users, but it's tedious to assign each User to\n| a Role intended for lots of Users. Recall that Users are grouped in Sites,\n| Organizations, and User Groups. Implicitly assign regular scoped permissions to\n| Users by assigning a Role directly to one of these User groupings.\n| \n| ![Figure 1: Assign Users to a role, directly or by their association with a Site, Organization, or User Group.](../../../images/roles-assignees.png)\n\nTake a Message Board Administrator Role, for example. A Role with that name\nshould have permissions relevant to the specific Message Board portlets\ndelegated to it. Users with this Role inherit the permissions collected underneath\nthe umbrella of the Role.\n\nIn addition to regular Roles, Site Roles, and Organization Roles, there are also\nTeams. Teams can be created by site administrators within a specific Site. The\npermissions granted to a Team are defined and applied only within the Team's\nsite. The permissions defined by regular, Site, and Organization Roles, by\ncontrast, are defined at the global level, although they are applied to\ndifferent scopes. \n\nRegular role\n: Permissions are defined at the global level and are applied at the global\nscope.\n\nSite role\n: Permissions are defined at the global level and are applied to one specific\nSite.\n\nOrganization role\n: Permissions are defined at the global level and are applied to one specific\nOrganization.\n\nTeam\n: Permissions are defined within a specific Site and are assigned within that\nspecific Site.\n\n| **Note:** Some permissions cannot be handled from the control panel.\n| Asset-level permissions (for instance, permission to edit an individual blog\n| post, or view a folder in the Documents and Media library) are managed from the\n| individual asset. See\n| [Widget Permissions](/docs/7-2/user/-/knowledge_base/u/widget-permissions)\n| for details.\n\n## Deleting Asset Containers\n\nA Web Content Folder contains Web Content articles. The Web Content Folder is\nan asset container, and the Web Content Article is an asset. It's possible to\ngive a Role permission to delete an asset container without giving the Role\npermission to delete individual assets. In that case, beware: if a Role assignee\ndeletes an asset container with individual assets in it, the individual assets\nthemselves are deleted as well.\n\nBesides Web Content Folders, examples of asset containers include Bookmarks\nFolders, Message Boards Categories, Wiki Nodes, and Documents and Media Folders.\n\nYou might not need to create a Role for a certain functionality. Liferay\nprovides many pre-configured Roles for your convenience.\n\n## Default Liferay Roles\n\nIn the Roles Application appears a list of all the Roles in Liferay, by\nscope. \n\nThese are some of the pre-configured regular Roles:\n\n- Guest: The Guest role is assigned to unauthenticated users and grants the\n  lowest-level permissions. \n- User: The User role is assigned to authenticated Users and grants basic\n  permissions (mostly *Add to Page* permissions for their own Sites).\n- Power User: The Power User Role grants more permissions than the User Role.\n  It's an extension point for distinguishing regular Users from\n  more privileged Users. For example, you can set things up so that only\n  Power Users have personal sites.\n- Administrator: The administrator Role grants permission manage the entire\n  portal, including global portal settings and individual Sites, Organizations,\n  and Users.\n\nThese are some of the pre-configured site roles:\n\n- Site Member: The Site Member Role grants basic privileges within a Site, such\n  as permission to visit the Site's private pages.\n- Site Administrator: The Site Administrator Role grants permission to manage\n  *almost* all aspects of a Site including site content, site memberships, and\n  site settings. Site Administrators cannot delete the membership of or remove\n  roles from other Site Administrators or Site Owners. They also *cannot* assign\n  other Users as Site Administrators or Site Owners.\n- Site Owner: The Site Owner Role is the same as the Site Administrator Role\n  except that it grants permission to manage *all* aspects of a Site, including\n  permission to delete the membership of or remove Roles from Site\n  Administrators or other Site Owners. They *can* assign other users as Site\n  Administrators or Site Owners.\n\nThese are some of the pre-configured organization roles:\n\n- Organization User: The Organization User role grants basic privileges within\n  an Organization. If the Organization has an attached Site, the Organization\n  User Role implicitly grants the Site member Role within the attached Site.\n- Organization Administrator: The Organization Administrator Role grants\n  permission to manage *almost* all aspects of an Organization including the\n  Organization's Users and the Organization's Site (if it exists). Organization\n  Administrators cannot delete the membership of or remove Roles from other\n  Organization Administrators or Organization Owners. They also *cannot* assign\n  other Users as Organization Administrators or Organization Owners.\n- Organization Owner: The Organization Owner Role is the same as the\n  Organization Administrator Role except that it grants permission to manage\n  *all* aspects of an Organization, including permission to delete the\n  membership of or remove Roles from Organization Administrators or other\n  Organization Owners. They *can* assign other Users as Organization\n  Administrators or Organization Owners.\n\n| **Tip:** It's easy to overlook the differences between owner type roles and\n| administrator type roles for Sites and Organizations. Site and Organization\n| administrators *cannot* remove the administrator or owner Role from any other\n| administrator or owner, and they *cannot* appoint other Users as site or\n| organization administrators or owners.\n| \n| In contrast, site and organization owners *can* do those things.\n\nRoles, and the permissions granted with their assignment, are foundational\ncomponents in Liferay. Understanding their uses and configuration enhances\nyour ability to configure @product@ to suit your organizational needs.\n"
  },
  {
    "path": "en/user/articles/10-managing-users/03-roles-and-permissions/02-managing-roles.markdown",
    "content": "---\nheader-id: managing-roles\n---\n\n# Managing Roles\n\n[TOC levels=1-4]\n\nYou manage Roles and Permissions in the Control Panel (*Control Panel* &rarr;\n*Users* &rarr; *Roles*). There you'll find an application for creating Roles,\ngranting them permissions, and assigning Users to them. Roles can be scoped by\nportal, Site, or Organization.\n\nDefining a Role's permissions is a topic deserving its own article. Read\n[here](/docs/7-1/user/-/knowledge_base/u/defining-role-permissions) about\ndefining a role's permissions.\n\n![Figure 1: The Roles application lets you add and manage roles for the global (Regular), Site, or Organization scope.](../../../images/roles-app.png)\n\n## Creating Roles\n\nDetermine the scope of the Role you must create. Roles can be scoped globally\n(Regular Roles), to a specific Site (Site Roles), or to an Organization\n(Organization Roles). \n\nTo create a Role:\n\n1.  Click the tab for the proper Role scope, then click the *Add*\n    (![Add](../../../images/icon-add.png)) button. \n\n2.  Enter a title and description. The title field is required but the\n    description is optional. \n\n3.  Enter a Key, if desired. This required field provides a key that can be\n    used to refer to the Role programmatically. It's auto-populated with the\n    title text, but you can override it if desired.\n\n4.  Click *Save*.\n\nNow the Role is present in the database and ready for further configuration.\n\n## Assigning Users to a Role\n\nAssign users to a Role in the Assignees tab of the Add/Edit Role\nform. Roles are assigned to Users, Sites, Organizations, or User Groups.\nHere's how to assign the User Group Manager Role created in the last section to\nUsers:\n\n1.  In the Assignees tab of the Add/Edit Role form, click the second level tab\n    for *Users*.\n\n2.  Click the Add button (![Add](../../../images/icon-add.png)).\n\n3.  Select the Users you want to add to the Role and click *Add*.\n\nIf assigning a group, note that all Users assigned to that group inherit the\nRole as well. \n\nThat's a good start, but your Role isn't worth the database row it occupies\nwithout defining permissions for the Role. Read the next article to learn\nhow.\n"
  },
  {
    "path": "en/user/articles/10-managing-users/03-roles-and-permissions/03-roles-and-permissions.markdown",
    "content": "---\nheader-id: defining-role-permissions\n---\n\n# Defining Role Permissions\n\n[TOC levels=1-4]\n\nRoles collect permissions, so when Users are given a Role, they receive all the\npermissions defined by the Role. \n\nIf you create a Role with permission to access something in the Control Panel,\nkeep in mind that the *View Control Panel Menu* permission is automatically\ngranted.\n\nConsider a Role called User Group Manager. Define the permissions for the User\nGroup Manager Role so that assigned Users can add Users to or remove Users from\nany User Group:\n\n1.  Go to the Control Panel and then click on *Users* &rarr; *Roles*.\n\n2.  On the Regular Roles screen, click *Add* (![Add](../../../images/icon-add.png)).\n\n3.  After naming your Role, click *Save*.\n\n4.  Click on the *Define Permissions* tab.\n\n5.  Drill down in the menu on the left to *Control Panel* &rarr; *Users* &rarr;\n    *User Groups*.\n\n6.  Under the *General Permissions* heading, flag *Access in Control Panel* and\n    *View*. This lets user group managers access the User Groups Control Panel\n    portlet and view existing User Groups.\n\n7.  Since you want User Group managers to be able to view User Groups and\n    assign members to them, also check the *Assign Members* and *View*\n    permissions under the *Resource Permissions* &rarr; *User Group* heading.\n\n8.  There's one last necessary permission you might not think of in association\n    with this Role. In *Control Panel* &rarr; *Users* &rarr; *Users and\n    Organizations*, User Group managers need *View* permission on the User\n    resource. Grant this permission.\n\n9.  Click *Save*.\n\n![Figure 1: When defining permissions on a Role, the Summary view provides a list of permissions that have already been defined for the role. The area on the left side of the screen lets you drill down through various categories of permissions.](../../../images/roles-define-permissions.png)\n\nNow the User Group Manager Role has all the permissions necessary for adding\nUsers to User Groups. After all, User Group managers can view User Groups,\nassign members, and access User Groups in the Control Panel. The permission to\nview Users in the Control Panel was necessary because you must view Users to\nassign them as members of a Role. Without this permission, User Group managers\nsee an empty list if they try to add Users to a Role.\n\n![Figure 2: Users assigned to the User Group Manager Role can't find any users to add unless they have view permissions on the User resource.](../../../images/roles-no-users-found.png)\n\n| **Note:** The Roles application in the Control Panel is not the only place where\n| permissions are configured. You can configure a Role's permissions on a resource\n| at a more granular level. For example, from a particular application instance,\n| click its *Options* (![Options](../../../images/icon-options.png)) menu and\n| select *Permissions*. There you can configure permissions for the resource that\n| overlap with those configured in the Control Panel's Roles application. However,\n| permissions granted or removed in the Control Panel override those made at the\n| more granular level.\n\nThere are three categories of permissions: *Control Panel*, *Site\nAdministration*, and *User*. By default, Users can manage their User accounts\nvia the permissions belonging to the User category. Site Administrators can\naccess the site administration tools belonging to the Site Administration\ncategory. Portal Administrators can access the entire Control Panel. For custom\nRoles, you can mix and match permissions from as many categories as you like.\n\nThe permissions in the Site Administration &rarr; Applications categories govern\nthe content that can be created by portlets such as the Wiki and Message Boards.\nIf you pick one of the portlets from this list, you see options for defining\npermissions on its content. For example, if you pick Message Boards, you see\npermissions for creating categories and threads or deleting and moving topics.\n\nSite application permissions affect the application as a whole. Using the\nMessage Boards as an example, an application permission might define who can add\nthe Message Boards portlet to a page.\n\n![Figure 3: You can fine-tune which actions are defined for a role within a specific application like the Message Boards.](../../../images/roles-message-board-content-permissions.png)\n\nThe Control Panel permissions affect how the Control Panel appears to the User\nin the Control Panel. The Control Panel appears differently for different Users,\ndepending on their permissions. Some Control Panel portlets have a Configuration\nbutton, and you can define who gets to see that. You can also fine-tune who gets\nto see various applications in the Control Panel.\n\nIf you want to change the scope of a permission, click the *Change* link next to\nthe gear icon next to the permission and then choose a new scope. After you\nclick *Save*, you'll see a list of all permissions currently granted to the\nRole. From the Summary view, you can add more permissions or go back to the Role\nApplication default view by clicking on the *Back*\n(![Back](../../../images/icon-back.png)) icon.\n\nSometimes you might find that a certain permission grants more or less access\nthan what you expected---always test your permissions configurations! \n\n## Delegating Social Activities Configuration\n\nThere's a permission that allows Site administrators to delegate responsibility\nfor configuring social activities to other Users. To add this permission to a\nRole, click *Actions* next to the desired Role and select *Define Permissions*.\nFind the *Site Administration* &rarr; *Configuration* &rarr; *Social Activity*\npermissions category. Flag all of the permissions and then click *Save*:\n\n- Access in Site Administration\n- Configuration\n- Permissions\n- Preferences\n- View\n\nOnce these permissions are granted, Role assignees can manage the site's Social\nActivities.\n\nRoles allow portal administrators to define various permissions in whatever\ncombinations they like. This gives you as much flexibility as possible to build\nthe Site you have designed.\n"
  },
  {
    "path": "en/user/articles/10-managing-users/04-managing-user-data/01-intro.markdown",
    "content": "---\nheader-id: managing-user-data\n---\n\n# Managing User Data\n\n[TOC levels=1-4]\n\nInternet users are increasingly and justifiably concerned about how their\npersonal data is processed by the systems they use. The enforcement of GDPR is\na crystallization of these concerns into legislative action. Companies\nprocessing the personal data of EU residents must adopt appropriate measures to\nprotect User data. Of course, legal requirements like those in GDPR only explain\none reason for companies to develop policies for ensuring their users' right to\nprivacy. The market demands site owners show higher levels of responsiveness to\nUser inquiries into how their data is stored and processed.\n\nLiferay is aware of the need for functionality to address User data management,\nand added two important features toward this end:\n\n[Erase and/or anonymize data associated with a User](/docs/7-2/user/-/knowledge_base/u/sanitizing-user-data)\n: Administrative Users go through a step by step process, choosing to erase\ncertain pieces of data and anonymize others.\n\n[Export a User's personal data](/docs/7-2/user/-/knowledge_base/u/exporting-user-data)\n: Export ZIP files containing the data associated with a User.\n\nThese features are tools that get you closer to meeting two of GDPR's\ntechnically challenging requirements, the _right to data portability_ and the\n_right to be forgotten_.\n\n| **Note:** It is Liferay's sincerest hope that through the User Management\n| functionality of @product@, companies processing the personal data of their\n| website's users can satisfy the requirements of GDPR. However, the tools\n| discussed here and anywhere else in the documentation, including those directly\n| aimed at addressing GDPR requirements (as in this article) do not guarantee\n| compliance with the legal requirements of GDPR. Each company or individual whose\n| website processes user personal data and is under the jurisdiction of GDPR must\n| carefully determine the precise steps necessary to ensure they are fully compliant\n| with GDPR.\n\n## Anonymizing Data\n\nDeleting User data is the safest way to honor _right to be forgotten_ requests.\nWhen User data must be preserved, automatic anonymization of the data is in\norder. Users being anonymized must have their identifiers (for example, User ID\nand User Name) removed from content they've interacted with. However,\nportal content usually requires this information for its applications to work\nproperly. Therefore, the User's identifiers must be replaced by something, or\nsomeone. Meet the new User, *Anonymous Anonymous*, identity swapper\n*extraordinaire*. This deactivated User is dedicated to be the User whose\nidentifiers are assigned to anonymized content. This identity swap is an\nimportant step in the anonymization process, but additional manual intervention\nmay be necessary to truly achieve anonymization.\n\n![Figure 1: Anonymized content is presented with the User Anonymous Anonymous's identifying information.](../../../images/users-anonymized-content.png)\n\nHere at Liferay, we've grown fond of *Anonymous Anonymous*. If you'd rather\nstart from scratch or assign an existing User to be the Anonymous User, get rid\nof *Anonymous Anonymous* and configure your own Anonymous User.\n\nThe anonymous user is programmatically created for each instance the first time\nan Administrator clicks *Delete Personal Data* from a User's Actions menu\n(![Actions](../../../images/icon-actions.png)). If you haven't yet done that, no\nAnonymous User exists.\n\nThe easiest way to set up a new User as the Anonymous User is to edit an\nexisting Anonymous User configuration, passing in the new Anonymous User's User\nID. \n\nTo edit an existing configuration:\n\n1.  Go to Control Panel &rarr; Configuration &rarr; System Settings &rarr; Users\n    &rarr; Anonymous User.\n\n2.  Edit the existing configuration, providing a different User ID.\n\n    Get the User ID from Control Panel &rarr; Users &rarr; Users and\n    Organizations. Click on the User and find the User ID in the Information\n    screen of the Edit User application.\n\n3.  Click *Update*.\n\nTo create a new Anonymous User:\n\n1.  [Create a User](/docs/7-2/user/-/knowledge_base/u/adding-editing-and-deleting-users#adding-users)\n    use for data anonymization. Alternatively, you can use an\n    existing User.\n\n2.  If there's already an Anonymous User configured for the instance, there are\n    two ways to remove it: \n \n    Delete the User entirely. Deleting the User simultaneously deletes its\n    configuration as the Anonymous User. Go to Control Panel &rarr; Users &rarr;\n    Users and Organizations. If it's an active User, first deactivate, then\n    delete the User. The default Anonymous Anonymous User is deactivated by\n    default. Simply delete the User in this case. Click the Actions button\n    (![Actions](../../../images/icon-actions.png)) and select *Delete*.\n\n    If you don't want to delete the User, just delete the User's configuration\n    as the Anonymous User. Go to Control Panel &rarr; Configuration &rarr;\n    System Settings &rarr; Users &rarr; Anonymous Users.\n\n3.  Add a new Anonymous User configuration. Click the *Add* button.\n\n4.  Fill out the two fields, Company ID and User ID. \n\n    Get the Company ID from Control Panel &rarr; Configuration &rarr; Virtual\n    Instances. The Instance ID and Company ID are the same.\n\n    Get the User ID from Control Panel &rarr; Users &rarr; Users and\n    Organizations. Click on the User and find the User ID in the Information\n    screen of the Edit User application.\n\nThere can only be one Anonymous User configured for each instance.\n\n![Figure 2: Assign your own Anonymous User from Control Panel &rarr; Configuration &rarr; System Settings &rarr; Users &rarr; Anonymous User.](../../../images/users-anonymous-config.png)\n\n## Manual Anonymization\n\nAnonymizing just the User's identification fields is often not enough. If a User\nnamed Ziltoid Omniscient complains about The Lunar Resort's coffee in a Message\nBoards Message and in it signs the post with _Supreme Leader of Ziltoidea 9_,\nanonymizing this post would remove the User's name (Ziltoid Omnisicent) and\nreplace it with Anonymous Anonymous, but searching the Internet for _Ziltoidea\n9_ quickly reveals that the post was written by \n[Ziltoid the Omniscient](https://en.wikipedia.org/wiki/Ziltoid_the_Omniscient).\nThere can be user-entered personal data within the content of an application.\nYou must manually edit such content to remove identifying details. \n\n![Figure 3: Even though this Message Boards Message (a comment on a blog post in this case) is anonymized, it should be edited to remove User Associated Data from the content of the message.](../../../images/users-partial-anonymization.png)\n\n"
  },
  {
    "path": "en/user/articles/10-managing-users/04-managing-user-data/02-sanitizing-user-data.markdown",
    "content": "---\nheader-id: sanitizing-user-data\n---\n\n# Sanitizing User Data\n\n[TOC levels=1-4]\n\nOne of the technically challenging requirements of the General Data Protection\nRegulation (GDPR) is _the right to be forgotten_. The purpose of this article is\nnot to go into the details of this requirement, but to show you how the personal\ndata erasure functionality can assist you in satisfying this requirement. \n\nA simple way to think of what it means to be _forgotten_ by software is to\nconsider a scenario where a new portal administrator is hired immediately after\na User's right to be forgotten request has been honored. The new portal\nadministrator has access to all of the Site's content and administration\ncapabilities. Despite this, the administrator must not be able to glean\ninformation that could lead her to knowing the identity of the User whose\npersonal data was erased.\n\nConceptually, forgetting a User means two things, at a minimum:\n\n- Erasing the User's identifying information from the system. In @product@, this \n  entails removing the User from database tables and search indexes.\n- Erasing or anonymizing content the User has interacted with so it cannot be\n  tracked to a real person.\n\nUsers can already be deactivated and then deleted, so why add new functionality?\nDeleting removes the User from the table of Users in the database. The User's\ninformation is preserved in other locations, however. In a standard User\ndeletion scenario, all of a User's personally created content is still assigned\nto the User and her identifiers (User ID and User Name) still appear in the UI\nnext to content associated with her. This unintentional preservation of\nuser-identifying data is inadequate for satisfying some of the GDPR requirements\nand is the primary reason why the data erasure functionality was added in\n@product-ver@.\n\n| **Note:** Personal data erasure can help companies in their attempts to satisfy\n| the requirements of GDPR. Using the data erasure tool described here provides no\n| guarantee of compliance with the legal requirements of GDPR. Each company or\n| individual whose website processes user personal data and is under the\n| jurisdiction of GDPR must carefully determine the precise steps necessary to\n| ensure they are fully compliant with GDPR.\n\nTo begin sanitizing a user's data,\n\n1.  Go to Control Panel &rarr; Users &rarr; Users and Organizations.\n\n2.  Click the Actions button for a User (![Actions](../../../images/icon-actions.png)) and\n    select *Delete Personal Data*. If you have not deactivated the user, you\n    will be asked to do so. \n\n    The User's Personal Data Erasure screen appears.\n\n## The Personal Data Erasure Screen\n\nYou can browse all data the user has posted on the system. Click *Personal Site*\nto browse data from that site. \n\n![Figure 1: From here, you can browse all data the user posted on his or her personal Site.](../../../images/users-data-erasure-personal.png)\n\nClick *Regular Sites* to browse any data posted in regular Liferay sites. \n\n![Figure 2: Choose Regular Sites to browse all data posted by the user on administratively-created Sites.](../../../images/users-data-erasure-regular.png)\n\nTo review the user's data, click the item. For example, Pepper seems to have\nposted a blog entry on her personal Site. Clicking that entry reveals the title\nof that blog entry. \n\n![Figure 3: Pepper's blog entry might need review.](../../../images/users-data-erasure-blog.png)\n\nTo review any entry, click it. You're brought to the edit mode of the\napplication (in this case, Blogs), where you can make any changes to the content\nthat are necessary. \n\nTo manage (anonymize or delete) all the items for an application at once: \n\n1.  Click the Actions button (![Actions](../../../images/icon-actions.png)) for\n    the application.\n\n2.  If you're sure all items for an application can be safely deleted, choose\n    *Delete*. \n\n3.  If you're sure simple anonymization is good enough for all of an\n    application's items, choose *Anonymize*.\n\nUse the interface to browse through the Sites, applications, and data. \n\n## Delete the User\n\nOnce all data is reviewed, deleted, edited, and/or anonymized as appropriate,\ndelete the User. A dialog box pops up automatically when you're finished. This\nstep is simple: Click *OK*.\n\n![Figure 4: To finish the data erasure process, delete the User.](../../../images/users-delete-user.png)\n\nNow the User's data is anonymized or deleted, and the User is also deleted.\n"
  },
  {
    "path": "en/user/articles/10-managing-users/04-managing-user-data/03-exporting-user-data.markdown",
    "content": "---\nheader-id: exporting-user-data\n---\n\n# Exporting User Data\n\n[TOC levels=1-4]\n\nUser Management practices must account for the EU's General Data Protection\nRegulation. One of its tenets is that Users have a right to _data portability_.\n\nData portability means that a User has the right to receive their personal data\nin a machine-readable format.\n\n| **Note:** Personal data export can help companies in their attempts to satisfy\n| the requirements of GDPR. Using the export tool described here provides no\n| guarantee of compliance with any GDPR requirement. Each company or individual\n| whose website processes user personal data and is under the jurisdiction of GDPR\n| must carefully determine the precise steps necessary to ensure they are fully\n| compliant with GDPR.\n\nThe Control Panel's User Management system now natively supports the export of\na User's personal data to a ZIP file for download. The data format for the files\ncontaining the data is XML.\n\n## Exporting and Downloading\n\nTo export user data,\n\n1.  Go to *Control Panel* &rarr; *Users* &rarr; *Users and Organizations*.\n\n2.  Find the User and click the Actions button\n    (![Actions](../../../images/icon-actions.png)), then click *Export Personal\n    Data*.\n\n    This opens the User's Export Personal Data screen.\n\n3.  If there are no existing export processes shown, there's only one thing to\n    do: click the *Add* button (![Add](../../../images/icon-add.png)). The tool\n    for exporting the User's data appears.\n\n    ![Figure 1: The Export Personal Data tool lets you export all or some of the User's data.](../../../images/users-export-data.png)\n\n4.  Most of the time you want to export all the available data. Click *Select\n    Items*, and all applications containing User data are selected in the UI.\n\n5.  Click *Export*. You're taken back to the User's Export Personal Data screen,\n    but now there's an export process in the list.\n\n    ![Figure 2: Once User data is successfully exported, the export process is displayed in the User's Export Personal Data list.](../../../images/users-export-processes.png)\n\n6.  Download the data. Click the Actions button\n    (![Actions](../../../images/icon-actions.png)) for the process and select\n    *Download*.\n\n## Examining Exported Data\n\nSo what does the exported data look like?\n\n```xml\n<?xml version=\"1.0\"?>\n\n<model>\n    <model-name>com.liferay.message.boards.model.MBMessage</model-name>\n    <column>\n        <column-name>messageId</column-name>\n        <column-value><![CDATA[38099]]></column-value>\n    </column>\n    <column>\n        <column-name>statusByUserId</column-name>\n        <column-value><![CDATA[38045]]></column-value>\n    </column>\n    <column>\n        <column-name>statusByUserName</column-name>\n        <column-value><![CDATA[Jane Slaughter]]></column-value>\n    </column>\n    <column>\n        <column-name>userId</column-name>\n        <column-value><![CDATA[38045]]></column-value>\n    </column>\n    <column>\n        <column-name>userName</column-name>\n        <column-value><![CDATA[Jane Slaughter]]></column-value>\n    </column>\n    <column>\n        <column-name>subject</column-name>\n        <column-value><![CDATA[Great list. I was thinking of bringing the family,...]]></column-value>\n    </column>\n    <column>\n        <column-name>body</column-name>\n        <column-value><![CDATA[<p>Great list. I was thinking of bringing the family, but I don&#39;t\n  actually believe humans have ever been to the moon, so I guess it\n  would be silly to book a trip! LOL!</p>]]></column-value>\n    </column>\n</model>\n```\n\nIn this example, User Jane Slaughter made a Message Boards Message post, and her\nUser information was recorded in the `MBMessage` model's database table.\n\nThis actually corresponds with a comment on a Blogs Entry:\n\n![Figure 3: A Comment on a blog post is User Associated Data.](../../../images/users-mbmessage.png)\n\nExporting User data informs Site owners and Users about how much personal data the\nsight may have.\n"
  },
  {
    "path": "en/user/articles/10-managing-users/05-user-groups/01-intro.markdown",
    "content": "---\nheader-id: user-groups\n---\n\n# User Groups\n\n[TOC levels=1-4]\n\nA User Group is a list of Users created for a specific purpose. User Groups can\nbe created across the hierarchical boundaries of\n[Organizations](/docs/7-2/user/-/knowledge_base/u/organizations). \nFor example, an administrator could create a Teachers User Group for adding all\nmembers to multiple Sites, assign them all to a\n[Regular Role](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions), \nand create a common set of profile pages for all teachers in the User Group.\nUser Groups are integrated with Roles, Sites, Site Templates, and permissions.\nThis flexibility means that there are many different use cases for User Groups.\nThe articles in this section show you how to work with User Groups to serve the\nmost common use cases. \n\nUser Groups are most often used in these scenarios: \n\n-   **Manage Site membership:** Grant Site membership to all Users in a User\n    Group. Using the previous example, the Teachers user group could be added as\n    a member of the Sites *University Employees* and *Students and Teachers\n    Collaboration*. All users in that User Group would become members of those\n    Sites. \n\n-   **Manage user personal pages:** Provide predefined public or private pages \n    to the users in the user group. For example, the Teachers user group could \n    be created to ensure the home page on all teachers' personal Sites has the \n    same layout and applications. \n\n-   **Collect permissions:** Assign Roles and permissions to a group of Users \n    that don't share an organization. For example, in a university's portal, a \n    user group could be created to group all teachers independently of their \n    departments (organization). This would make it easier to assign one or \n    several Roles at once to all the teachers. \n\nRead on to learn how to manage User Groups. \n"
  },
  {
    "path": "en/user/articles/10-managing-users/05-user-groups/02-creating-user-group.markdown",
    "content": "---\nheader-id: creating-a-user-group\n---\n\n# Creating a User Group\n\n[TOC levels=1-4]\n\nFollow these steps to create a user group: \n\n1.  Open the Menu \n    (![Menu](../../../images/icon-menu.png)) \n    and select *Control Panel* &rarr; *Users* &rarr; *Users Groups*. Any \n    existing user groups appear here in a table. \n\n2.  Click the *Add* button \n    (![Add](../../../images/icon-add.png)). \n    This opens the *New User Group* form. \n\n3.  Give your user group a name and description. \n\n4.  If you want to create My Profile and/or My Dashboard pages for the user \n    group's members, select a Site Template to use from the *My Profile* \n    and *My Dashboard* selector menus. \n\n5.  Click *Save*. The new user group then appears in the table. \n\nNote that new User Groups don't have any Users. The next section shows you how \nto add members to a user group. \n\n![Figure 1: The New User Group form.](../../../images/new-user-group.png)\n\n![Figure 2: The user group you just created now appears in the table.](../../../images/user-groups-table.png)\n\n## Assigning Members to a User Group\n\nFollow these steps to add existing users to a user group: \n\n1.  If you're not already there, open the Menu \n    (![Menu](../../../images/icon-menu.png)) \n    and select *Control Panel* &rarr; *Users* &rarr; *Users Groups*. The \n    available user groups appear here in a table. \n\n2.  Click *Actions*\n    (![Actions](../../../images/icon-actions.png)) \n    &rarr; *Assign Members* for the user group you want to add users to. The \n    group's existing users appear in a table. \n\n3.  Click the *Add* button \n    (![Add](../../../images/icon-add.png)). \n    This opens a list of the users you can select. \n\n4.  Select one or more users from the list, then click *Add*. This adds the \n    selected users to the group, and returns you to the table containing the \n    group's users. The users you added now appear in the table. \n\n![Figure 3: Select the users to add to the user group.](../../../images/user-group-add-users.png)\n"
  },
  {
    "path": "en/user/articles/10-managing-users/05-user-groups/03-user-groups-site-membership.markdown",
    "content": "---\nheader-id: user-groups-and-site-membership\n---\n\n# User Groups and Site Membership\n\n[TOC levels=1-4]\n\nUser Groups are used to manage Site membership. When you assign a User Group \nto a Site, the group's Users become members of that Site. This is one of the \nmain use cases for User Groups. \n\nFollow these steps to assign a user group to a Site:\n\n1.  Open the Menu \n    (![Menu](../../../images/icon-menu.png)), \n    select the Site you want to work in, then open its Site Administration menu. \n\n2.  In the Site Administration menu, select *People* &rarr; *Memberships*. \n    This opens the Site Memberships screen. \n\n    ![Figure 1: Select *Memberships* from the People menu.](../../../images/site-memberships.png)\n\n3.  In Memberships, select the *User Groups* tab. This tab displays any User\n    Groups currently assigned to the Site. \n\n4.  Click the *Add* button \n    (![Add](../../../images/icon-add.png)), \n    select any User Groups you want to assign to the Site, then click *Done*. \n    The user groups you selected now appear in the User Groups tab. \n\n![Figure 2: The User Groups tab in Memberships shows the User Groups currently assigned to the Site.](../../../images/user-groups-site-memberships.png)\n"
  },
  {
    "path": "en/user/articles/10-managing-users/05-user-groups/04-user-group-sites.markdown",
    "content": "---\nheader-id: user-group-sites\n---\n\n# User Group Sites\n\n[TOC levels=1-4]\n\nEach User has a personal Site that consists of public (Profile) and private \n(Dashboard) pages. A *User Group Site* determines the base pages of the User \nGroup members' personal Sites. If the User Group Site pages are added to a \nUser's Profile pages, then the User Group Site is a public Site, accessible to \nanyone with the URL (`http://www.[sitename].com/web/[username]`). If the User \nGroup Site pages are added to the user's Dashboard pages, then the User Group \nSite is a private Site. A mixed approach can also be used, where both private \nand public pages are added for the User Group Site. If Users belong to multiple \nUser Groups, all the pages from those User Group Sites are made part of their \npersonal Sites. \n\n[When creating a user group](/docs/7-2/user/-/knowledge_base/u/creating-a-user-group), \nyou can create the user group Site via the \n[Site Templates](/docs/7-2/user/-/knowledge_base/u/building-sites-from-templates) \navailable for selection in the *My Profile* and *My Dashboard* selector menus. \nYou can also create a User Group Site later, either manually or via a Site \nTemplate. \n\n## Creating User Group Sites From Site Templates\n\nFollow these steps to create a User Group Site from a \n[Site Template](/docs/7-1/user/-/knowledge_base/u/building-sites-from-templates), \nfor a User Group that already exists: \n\n1.  Open the Menu \n    (![Menu](../../../images/icon-menu.png)) \n    and select *Control Panel* &rarr; *Users* &rarr; *User Groups*. User groups\n    appear in a table. \n\n2.  Click *Actions* \n    (![Actions](../../../images/icon-actions.png)) \n    &rarr; *Edit* for the User Group you want to create a Site for. This opens \n    a form that you can use to edit the User Group. Note that this is the same \n    form that appears when \n    [creating a user group](/docs/7-2/user/-/knowledge_base/u/creating-a-user-group). \n\n3.  To use a Site Template to create a public profile for the Users on their \n    *My Profile* Site, select that Site Template from the *My Profile* menu. To \n    use a Site Template to create private pages for the Users on their \n    *My Dashboard* Site, select that Site Template from the *My Dashboard* menu. \n    Note that you can also do both. \n\n4.  Click *Save*. \n\nNow, when one of the group's Users navigates to their *My Profile* or \n*My Dashboard* Sites, the content of those Sites reflect the Site Template(s)\nyou selected. \n\nUser Group Site pages function similarly to regular Site Template pages, with an\nimportant exception: User Group Site pages aren't copied for each user. They're\nshown dynamically along with any custom pages that Users may have on their \npersonal Site. For this reason, Users can't modify pages inherited from the User \nGroup. If needed, the User Group administrator can define certain areas of a \npage as customizable, like with regular Sites. This lets Users add and configure \nwidgets in the specified area of the page. \n\nThis flexibility lets you achieve almost any desired configuration for a User's\npersonal Site without having to modify it directly. When Users are assigned to\na User Group, they'll immediately have access to the User Group's Site pages\nfrom their personal Sites. \n\n| **Note:** Site Templates have an option that propagates changes made to the Site\n| Template. If you use a Site Template with this option enabled, the User Group\n| Sites update automatically when that template changes. If you disable this\n| option but enable it again later, the template's pages are copied to the Users'\n| Sites, overwriting any changes they may have made. For more information on the\n| automatically propagating Site Template changes, see\n| [Site Templates](/docs/7-2/user/-/knowledge_base/u/building-sites-from-templates).\n\n## Creating User Group Sites Manually\n\nYou can create a User Group's Site manually, instead of basing it on a Site\nTemplate. Follow these steps: \n\n1.  Open the Menu \n    (![Menu](../../../images/icon-menu.png)) \n    and select *Control Panel* &rarr; *Users* &rarr; *Users Groups*. User groups \n    appear here.\n\n2.  Click *Actions* \n    (![Actions](../../../images/icon-actions.png)) \n    &rarr; *Manage Pages* for the user group you want to create a Site for. This \n    opens the *Pages* window. Note that this is the same window you use for \n    [creating pages](/docs/7-2/user/-/knowledge_base/u/creating-pages). \n\n3.  Create the public and/or private pages that you want to use for the Users' \n    *My Profile* and/or *My Dashboard* Sites. Public pages you create here \n    become pages on users' *My Profile* Site, while private pages become pages \n    on users' *My Dashboard* Site. \n\nWhen you return to User Groups in the Control Panel, you can access a User \nGroup's public and/or private pages via these links in the User Group's \n*Actions* button \n(![Actions](../../../images/icon-actions.png)): \n\n-   **Go to Profile Pages:** Opens the User Group's public *My Profile* page(s) \n    in a new browser window. \n-   **Go to Dashboard Pages:** Opens the User Group's private *My Dashboard* \n    page(s) in a new browser window. \n\nIn the new window, you can add more pages and portlets and configure Site \nsettings. \n\n## Legacy User Group Sites Behavior\n\nSince the inheritance of User Group Site pages is now dynamic, even if there are\nhundreds of thousands of Users, even millions, there isn't an impact in\nperformance. Versions of Liferay Portal and Liferay DXP prior to 7.0 required\nUser Group pages be copied to each User's personal Site. If you long for the old\ndays, or if you're upgrading from an older version and must keep that\nbehavior, enable it by adding the following line to your `portal-ext.properties`\nfile: \n\n    user.groups.copy.layouts.to.user.personal.site=true\n\nWhen this property is set to `true`, the template pages are copied to a User's\npersonal Site once, and then may be modified by the User. This means that if \nchanges are made to the template pages later, they only affect Users added to\nthe User Group after the change is made. Users with administrative privileges\nover their personal Sites can modify the pages and their content if the *Allow\nSite Administrators to Modify the Pages Associated with This Site Template* box\nhas been checked for the template. When a User is removed from a User Group, the\nassociated pages are removed from the User's personal Site. If a User is removed\nfrom a group and is subsequently added back, the group's template pages are\ncopied to the User's Site a second time. Note that if a User Group's Site is\nbased on a Site Template and an administrator modifies the User Group's Site\nTemplate after users have already been added to the group, those changes only\ntake effect if the *Enable propagation of changes from the Site Template* box\nfor the User Group was checked. \n\n"
  },
  {
    "path": "en/user/articles/10-managing-users/05-user-groups/05-user-group-permissions.markdown",
    "content": "---\nheader-id: configuring-user-group-permissions\n---\n\n# Configuring User Group Permissions\n\n[TOC levels=1-4]\n\nAdministrators commonly create User Groups so the group's Users can take some\nspecific action in a Site. This is done by assigning the permissions for that\naction to a Role, and then assigning that Role to the User Group. This grants\nthe Role's permissions to the User Group's Users. \n\nFollow these steps to grant permissions to Users in a User Group: \n\n1.  [Create the User Group](/docs/7-2/user/-/knowledge_base/u/creating-a-user-group).\n2.  [Assign the User Group to a Site](/docs/7-2/user/-/knowledge_base/u/user-groups-and-site-membership). \n3.  [Create the Site Role and define its permissions](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions). \n4.  Assign the Role to the User Group. \n\nFor instructions on the first three steps, click those links above. This article\nshows you how to assign a Role to a User Group: \n\n1.  Open the Menu \n    (![Menu](../../../images/icon-menu.png)), \n    select the Site to work in, then open its Site Administration menu. \n\n2.  In the Site Administration menu, select *People* &rarr; *Memberships*. \n    This opens the Memberships screen. \n\n    ![Figure 1: Select *Memberships* from the Site Administration menu.](../../../images/site-memberships.png)\n\n3.  In Memberships, select the *User Groups* tab. This tab displays \n    User Groups currently assigned to the Site. \n\n4.  Click the *Actions* button \n    (![Actions](../../../images/icon-actions.png)) \n    for the User Group you want to assign to a Role, and select \n    *Assign Site Roles*. This opens the Assign Site Roles dialog. \n\n    ![Figure 2: Select *Assign Site Roles* for the user group.](../../../images/user-groups-site-role.png)\n\n5.  In the Assign Site Roles dialog, select the Role from the list and click \n    *Done*. \n"
  },
  {
    "path": "en/user/articles/10-managing-users/05-user-groups/06-editing-user-groups.markdown",
    "content": "---\nheader-id: editing-user-groups\n---\n\n# Editing User Groups\n\n[TOC levels=1-4]\n\nYou can access User Groups from *Control Panel* &rarr; *Users* &rarr; *User\nGroups*. Managing membership is the most common action you'll likely perform on\na User Group. \n\n![Figure 1: The user groups appear in a table.](../../../images/user-groups-table.png)\n\nFollow these steps to add/remove users to/from a User Group: \n\n1.  Click the User Group's name or description. Alternatively, you can click the \n    User Group's *Actions* button \n    (![Actions](../../../images/icon-actions.png)) \n    and select *Assign Members*. This presents a list of the User Group's users. \n\n2.  To remove a User from the User Group, click the `X` button next to that \n    User. To remove multiple Users at once, check each User's checkbox and then \n    click the trash icon \n    (![Trash](../../../images/icon-trash.png)) \n    that appears in the Management Bar above the User list. \n\n3.  To add Users to the User Group, click the *Add* button \n    (![Add](../../../images/icon-add.png)). \n    In the dialog that appears, select the Users and click *Add*. \n\n![Figure 2: The list of Users lets you manage the User Group's membership.](../../../images/user-groups-users.png)\n\nOther options are available in each User Group's Actions button \n(![Actions](../../../images/icon-actions.png)): \n\n**Edit:** Modify the User Group's name or description, or choose Site \ntemplates to use for the \n[User Group's Sites](/docs/7-2/user/-/knowledge_base/u/user-group-sites).\n\n**Permissions:** Assign permissions for viewing and managing the User Group. \n\n**User Group Pages Permissions:** Assign permissions for managing the User \nGroup's Site pages. \n\n**Manage Pages:** Manually manage the User Group's Site pages. See the \n[documentation on user group Sites](/docs/7-2/user/-/knowledge_base/u/user-group-sites#creating-user-group-sites-manually)\nfor details. \n\n**Assign Members:** Add/remove Users to/from the User Group. This is \ndescribed in detail above. \n\n**Delete:** Remove the User Group. Note that you can't delete a User Group \nthat contains Users. You must first remove the Users from the group. \n\nIf your User Group has public and private Site pages, the options \n*Go to Profile Pages* and *Go to Dashboard Pages* also appear in your User \nGroup's Actions menu. Clicking one of these links opens that Site in a new \nbrowser window. See the \n[documentation on user group Sites](/docs/7-2/user/-/knowledge_base/u/user-group-sites) \nfor details. \n\n![Figure 3: The Actions menu for a user group.](../../../images/user-groups-actions.png) \n"
  },
  {
    "path": "en/user/articles/10-managing-users/10-password-policies.markdown",
    "content": "---\nheader-id: password-policies\n---\n\n# Password Policies\n\n[TOC levels=1-4]\n\nPassword policies enforce password rules to help users specify secure passwords.\nUse the default policy that ships with Liferay (modified or as is), or create\nyour own policies. Password policies can be assigned to Users or Organizations,\nor set as the default policy used throughout a virtual instance. \n\n## Adding and Configuring Password Policies\n\nTo add or edit password policies, \n\n1.  Navigate to *Control Panel* &rarr; *Users* &rarr; *Password Policies*. \n\n    There's already a default password policy in the system. \n\n2.  Edit this the same way you edit other resources: click *Actions*\n    (![Actions](../../images/icon-actions.png)) and then click *Edit*. \n\n3.  To add a new policy, click the *Add*\n    (![Add](../../images/icon-add.png)) button.\n\n    Provide the *Name* (required), *Description*, and specific configuration options\n    for your new password policy.\n\n![Figure 1: You can create new password policies to suit your needs.](../../images/password-policy-add.png)\n\nThere are several configuration categories.\n\n**Password Changes** \n: Allow or disallow users to change their passwords, and set a time limit on the\nvalidity of password reset links.\n\n**Password Syntax Checking** \n: If enabled, require users to use a certain syntax when choosing a password.\nYou can disallow dictionary words, set a minimum length, and more in this\nsection.\n\n**Password History** \n: If enabled, decide how many passwords to keep in the history, preventing users\nfrom reusing an old password.\n\n**Password Expiration** \n: Decide whether to expire passwords after a specified time. If enabled, specify\nhow long passwords are valid, when and whether to send a warning, and how many\ntimes the user can log in after the password is expired before needing to set a\nnew password (called a *Grace Limit*). \n\n**Lockout** \n: If enabled, set a maximum number of failed authentication attempts before the\naccount is locked, how long the number of attempts is stored, and the lockout\nduration.\n\n**Self Destruct** \n: If enabled, set the time after lockout before Liferay self destructs\ncatastrophically, sending the world into apocalyptic chaos, out of which\nself-aware robots arise and recolonize the world, enslaving the surviving\nremnant of humanity for their own nefarious purposes. We recommend you keep this\ndisabled.\n\nJust making sure you were paying attention; that last one doesn't actually\nexist. \n\nOnce you configure the policy, click *Save* to add it to the list of\nready-to-use password policies.\n\n## Assigning Users to a Password Policy\n\nTo use the default password policy, you don't have to do anything: like its name\nsuggests, it's the default. If you create a new password policy, however, you\nmust assign users to it. To do this click *Actions*\n(![Actions](../../images/icon-actions.png)) &rarr; *Assign Members*.\n\n![Figure 2: Assign members to new password policies to make them take effect.](../../images/password-policy-assign-members.png)\n\nChoose whether to assign users directly or to assign organizations to the\npassword policy, then click *Add* (![Add](../../images/icon-add.png)).\n\nOnce assignments are saved, the password policy is in effect. Did you know you\ncan change the default password policy and configure it using Liferay's\n`portal.properties` file?\n\n## Default Policy Properties\n\nThe Default Password Policy is set as the default and configured in Liferay's\n[portal.properties](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Passwords)\nfile. Find the properties that start with `passwords.default.policy`. To make\nchanges, including changing the default policy, add whichever properties and\nvalues you choose to modify in your `portal-ext.properties` file, as usual.\nRestart the application server and your changes take effect.\n\n```properties\n#\n# Set the properties of the default password policy.\n#\n\n...\npasswords.default.policy.name=Default Password Policy\n...\n```\n\nAs you can see, Password Policies give you a simple yet powerful way to set\npassword rules.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/01-intro.markdown",
    "content": "---\nheader-id: web-experience-management\n---\n\n# Web Experience Management\n\n[TOC levels=1-4]\n\nExperience: consider that word for a moment. Not the type of experience you \ngain with repetition, but the contact or encounter you have with something. \nSuppose you're buying a new phone. It's easy to look at the phone with the \nbiggest screen or the fastest processor and say that it's the best, but what \nwill your experience be? The \"best\" phone might not be the fastest or the \nbiggest; it might be the phone with the longest battery life or the most \ncomprehensive suite of integrated apps. Or it might not be any of those things. \nExperience isn't always something that you can quantify with specs or features.\n\nLiferay takes the experience factor very seriously when it comes to site and \ncontent management. The Web Experience Management suite is focused on providing \nthe best experience for users building websites. When it comes to web \nexperience, just like with your phone, everyone is looking for something \ndifferent. Some smartphone users might love watching videos during their \ncommute with a big beautiful screen, while on the other side, some people might \nbe more excited about a small sleek phone with a great battery life that fits \neasily in their pocket and simply does all the basic communication they need.\n\nLiferay's Content Management is the big beautiful phone and the sleek \nutilitarian one all in one. Marketers will find easy to use tools to build \ncontent without having to write any code or peak under the hood. Developers \nwill find powerful tools like Structures and Templates that enable them to \ncreate dynamic content. And designers will love how Fragments and Content pages \nprovide a way to perfectly realize their designs.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/02-authoring-content.markdown",
    "content": "---\nheader-id: authoring-content-structured-and-inline-content\n---\n\n# Authoring Content: Structured and Inline Content\n\n[TOC levels=1-4]\n\nThe primary goal of Content Management isn't to show off the flashiest new \nfeatures or follow all the latest trends in design, but to provide you with the\ntools you need to create digital content that communicates your message clearly\nand effectively. With this in mind, @product@ offers two core approaches to help\nyou accelerate and simplify creating and organizing content: Structured Web\nContent and Inline Content.\n\n## Structured Web Content\n\nIf you've entered content into a CMS before, you may be familiar with the\nprocess of filling content into various fields like this:\n\n- Title\n- Abstract\n- Text Body\n\nThis is an example of Structured Content. Structured Content is created within \na predefined content structure and then added to pages as needed. The structure \ndefines the fields and then a template defines its styles. The content is then \nsaved, ready to be added to a page later.\n\nThe structure defines what kind of content you are creating and provides \ndifferent types of fields that can be used. A developer could create a format\nfor publishing articles that contains a **Title**, **Header Image**, **Body \nText**, and a **Key Quotation**. The template defines how the elements of the \nstructure are rendered. You could style the elements in the structure with the \n**Title** as large bold text, the **Header Image** as a full page width block \nabove the title, the **Text** as standard text, and the **Key Quotation** as \nlarge font italics with a thin border that displays within the main text \nsection. A content writer or marketer could then create any number of articles, \nall having a uniform style based on the structure.\n\nIn @product@, those articles could be added to pages across the site, or \ndisplayed dynamically with tools like the Asset Publisher and Web Content \nDisplay Pages.\n\n## Inline Content\n\nInline Content is content that is created directly within a page. Rather than \nfilling in fields to create content that's added to a page later, you have\na completed page design where you edit the text and image content. Content\nPages start with a design which is then created with Fragments. Inside the\nFragments, a developer can define where text and images can be placed or\nedited. Marketers and content writers are then free to write or add images\nwithin the page and publish it.\n\nWith content pages, basic HTML and CSS define the primary design, while \nJavaScript and Liferay specific tags can add dynamic behavior to the Content \nPage. After a developer creates the page and a content writer or marketer \nprovides the content, the content exists inline within the page, and is \npublished with it.\n\n## What's Best for Your Use Case?\n\nWhen you step back to look at the big picture, what you see are two different \nparadigms for building pages: content pages, where the content is built into \nthe page; and widget pages, where content and other features can be added, \nremoved, and rearranged as desired.\n\nOften it is helpful to have reusable elements or content that can be moved \naround a page or placed anywhere on a site. For example, you might have a \ncontent based banner which you want to be able to drop onto any various pages \nwith different layouts and styles, or you might have content that uses the same\ntemplate to create similar items for different pages. Structured Content on\nwidget pages is the tool you need to quickly create what you need and manage\nthese cases. Widget pages with structured content are great for some cases, for\nexample:\n\n- Portal pages, where you provide users a gateway into several different\n  services or providing aggregated information.\n\n- Pages where the primary focus is widgets.\n\n- Pages that are based around structured content.\n\nIn other cases, you need to create a page as a complete unit. For example, you \nhave a series of marketing driven landing pages that must match a specific\ndesign and have associated content intended for use on that page or with that\ncampaign. Content Pages provide the best tool for quickly bringing a design to\nlife and empowering marketing with inline content. Content Pages are useful for\npages like:\n\n- Landing pages\n\n- Front pages that provide marketing information or a direct path into the \n  website.\n\n- Pages with multiple variations and small graphical or textual changes across a\n  large number of pages.\n\nMost sites need a little bit of both. Read on to learn more about building\nsites with @product@ and how Content Pages and Structured Content can help you\ndo that.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/01-building-a-site-intro.markdown",
    "content": "---\nheader-id: building-a-site\n---\n\n# Building a Site\n\n[TOC levels=1-4]\n\nA site is a set of pages where content or applications are published. Sites can\nbe independent or serve as an associated organization's website. You can create\nas many different sites as you like within the context of a single Liferay\ninstance.\n\nYou'll start with a tour of the site management user interface. Then you'll\ncreate a custom Lunar Resort Example instance and explore ways to create sites\nand pages for that Liferay instance. Finally, you'll learn how to change various\nsettings for sites and pages to meet your needs. To begin building a site,\ncontinue on to the next section.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/02-site-management/01-site-management-intro.markdown",
    "content": "---\nheader-id: site-management\n---\n\n# Site Management\n\n[TOC levels=1-4]\n\nYou can have many Sites on one Liferay instance, which work together to create\none complete website, or you can simply have one Site which contains all of your\npages and content---or anything in between. In this section you'll look at the\ninterface for creating and managing Sites, create a Site, and learn how to use\nSite Templates for more efficient Site creation.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/02-site-management/02-understanding-site-management.markdown",
    "content": "---\nheader-id: understanding-Site-management\n---\n\n# Understanding Site Management\n\n[TOC levels=1-4]\n\nWhether you're building a large corporate web Site or a small Site for\nfacilitating collaboration among team members, supporting different kinds of\ncollaboration and social scenarios is a must. Liferay's Sites provide three\nmembership types:\n\n**Open:** Users can become members of the Site at any time.\n\n**Restricted:** Users can request Site membership but Site administrators must\napprove requests for users to become members.\n\n**Private:** Users cannot join the Site or request Site membership. Site\nadministrators must manually select users and assign them as Site members.\n\nIn addition to these memberships, when a Site is associated with an\norganization, all the users of that organization are automatically considered\nmembers of the Site.\n\nYou can view all the available open and restricted Sites by adding the My Sites\napplication to a page and accessing the *Available Sites* tab. You can request\naccess to any of the Sites you're not already a member of by selecting the\nSite's *Options* button\n\n(![Options](../../../../images/icon-actions.png)) and clicking *Join*.\n\n## Site Scope\n\nMembers of a Site can be given additional privileges in the Site by using\npermissions. It is also possible to assign different roles within the\nSite to different members. This can be done through *Site Roles*, which are\ndefined equally for all Sites or *Teams* which are unique for each Site. These\nconcepts are discussed later.\n\n@product@ separates Site-scoped information from the Control Panel by placing it\nin the Site menu. From this menu, you can select the specific Site to work on.\nThe Site Administration panel is available for your Site, which includes Build,\nContent, Categorization, Recycle Bin, Members, Configuration, and Publishing.\n\n![Figure 1: Your Site's content resides in the Site Administration menu.](../../../../images/web-content-site-content.png)\n\n## Site Hierarchies\n\nSites can also be organized hierarchically, just like Organizations. The\ndifference between Sites and Organizations, of course, is that Sites organize\npages, content, application data, and users (via Site memberships) whereas\norganizations only group users. Content sharing is available for Sites within\nthe same hierarchy. For instance, if a parent Site has a document type called\n*Lunar Presentation* and all its child Sites should have a copy, the parent\nSite's administrator can enable content sharing to share the document type\nautomatically with its child Sites. Also, content sharing privileges can be set\nto let every Site administrator share content across Sites they manage. You can\nshare the following content across Sites:\n\n- Web Content Structures\n- Web Content Templates\n- Document Types\n- Vocabularies and Categories\n- Widget Templates\n- Data Definitions (Dynamic Data Lists)\n\nPlease refer to the \n[Sites Admin Portlet](https://docs.liferay.com/dxp/portal/7.2-latest/propertiesdoc/portal.properties.html#Sites%20Admin%20Portlet)\nsection of Liferay's `portal.properties` file for a list of relevant\nconfigurable properties. For example, the\n`Sites.content.sharing.with.children.enabled` property can disable content\nsharing between Sites and child Sites, disable it by default while allowing Site\nadministrators to enable it per Site, or to enable it by default while allowing\nadministrators to disable it per Site.\n\nThe Sites Directory application is a configurable app that shows a hierarchy of\nSites and child Sites. It enables users to navigate to any of the displayed\nSites. To use this app to display Site hierarchies, add it to a page, open its\nConfiguration window, and under Display Style, select *List Hierarchy*. The My\nSites Directory application is similar to the Sites Directory application,\nexcept that it lists only the Sites a user belongs to.\n\nEach child Site in the hierarchy has its own administrator, and the Site\nAdministrator role permissions do not flow down to child Sites in the hierarchy.\nIf a Site Administrator creates a child Site, he or she has the same permissions\nin that child Site. This is not, however, because of inheritance. It is only\nbecause creating a Site makes you the Owner of that Site. A Site Administrator\nor a parent Site has no default role in any child Sites created by other Site\nAdministrators. \n\nIf you wanted a user to have administrative access to all Sites in a Site/child\nSite hierarchy, you must create a role based on the Site Administrator role that\nhas the permission *Manage SubSites*. \n\nThe Site Map application helps users navigate a Site. A Site administrator can\nconfigure a root page and a display depth. Just as Sites can have hierarchies,\nso can the pages within a Site. The display depth of the Site Map application\ndetermines how many levels of nested pages to display.\n\n![Figure 2: The Site Map application lets users navigate among pages of a Site organized hierarchically.](../../../../images/site-directory-site-map.png)\n\n## Site Members\n\nAnother useful administrative application is the Site Members application. This\nenables administrators to survey all the users, organizations, and user groups\nthat reside in the Site. Similarly, Liferay provides the Portal Directory\napplication, which functions the same as the Site Members app, but globally\nscoped for all Sites in the instance.\n\n## Page Sets\n\nSites have two categories of pages called page sets. There are two kinds of page\nsets: public pages and private pages. A Site can have only public pages, only\nprivate pages, or both. Private pages can only be accessed by Site members.\nPublic pages can be accessed by anyone, including users who haven't logged in.\nIt's possible to restrict access to pages at the page set level or at the level\nof individual pages through the permissions system. Public pages and private\npages have different URLs and can have different content, applications, themes,\nand layouts.\n\nBuilding a corporate intranet is a typical use case for Sites. A corporate\nintranet could have Sites for all the organizations in the company: Sales,\nMarketing, Information Technology, Human Resources and so on. But what about the\ncorporate health and fitness center? That's something everybody in the company,\nregardless of organization, may want to join. This makes it a good candidate for\nan open and independent Site. Similarly, the home page for a corporate intranet\nshould probably be placed in an open independent Site so any member of the\ninstance can access it.\n\nFor other kinds of websites, you may want to use independent Sites to bring\nusers together who share a common interest. If you were building a photo sharing\nwebsite, you might have independent Sites based on the types of photos people\nwant to share. For example, those who enjoy taking pictures of landscapes could\njoin a Landscapes Site and those who enjoy taking pictures of sunsets could join\na Sunsets Site.\n\nThere is always one default Site, which is also known as the main Site of the\ninstance. This Site does not have its own name but rather takes the name of the\ninstance. By default the instance name is *Liferay* but this value can be\nchanged through the configuration of the setup wizard. The instance name can\nalso be changed at any time through the Control Panel within *Configuration\n&rarr; *Instance Settings*.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/02-site-management/03-adding-sites.markdown",
    "content": "---\nheader-id: adding-sites\n---\n\n# Adding Sites\n\n[TOC levels=1-4]\n\nSites can be created through the Control Panel by a Liferay administrator. The\nControl Panel provides an administrative interface for managing your Liferay\ninstance. There are four main sections of the Liferay Control Panel: Users,\nSites, Apps, and Configuration. In this section, you'll learn how to use the\nControl Panel to manage Sites.  For information about the Apps, Users, and\nConfiguration sections of the Control Panel, see the \n[Using the Liferay Marketplace](/docs/7-2/user/-/knowledge_base/u/using-the-liferay-marketplace),\n[Managing Users](/docs/7-2/user/-/knowledge_base/u/managing-users), and\n[System Wide Settings](/docs/7-2/user/-/knowledge_base/u/system-wide-settings) sections,\nrespectively.\n\n| **Tip:** If you're signed in as an administrator, you can access all Sites by\n| navigating to the Site Administration menu from the Control Panel. To manage a\n| single Site, navigate to the Site by going to the Menu and clicking the *Site\n| Selector* button (![Compass](../../../../images/icon-compass.png)) from the Sites\n| dropdown menu and selecting the appropriate Site name. Once finished, the Site\n| administration options (i.e., Navigation, Content, Members, etc.) for that Site\n| are available.\n\nNow, you'll add a Site for the Lunar Resort.\n\n1.  Navigate to the Control Panel and select *Sites* &rarr; *Sites*.\n\n2.  Click the Add icon (![Add Site](../../../../images/icon-add.png)) at the \n    top right of the page.\n     \n3.  Select a *Blank Site*.\n\n    Any available Site templates appear for you to select. Site templates\n    provide a preconfigured set of pages, applications, and content that can be\n    used as the basis of a Site's public or private page set. To create a Site\n    from scratch, select *Blank Site*. Otherwise, select the name of the Site\n    template you want to use. If you opt to create a Site from a Site template,\n    you have to choose whether to copy the Site template's pages as your new\n    Site's public or private page set. If other Site templates are created, they\n    will appear in the Add menu as they become available. \n\n4.  Name your Site \"The Lunar Resort\"\n \nAfter you enter the name, you will be prompted to enter additional information \nabout the Site and configure certain Site settings.\n\n**Name:** names the Site you wish to create. You also have the option to\ntranslate the name for many different languages. This can be done by selecting\nthe language flag under the Name field, and inserting the name in the selected\nlanguage. Liferay saves the name translation for each language and displays the\ntranslated Site name when that specific language is selected for the instance.\nIf a name translation is not provided, the default instance language's name is\ndisplayed.\n\n**Description:** describes the Site's intended function. The description can\nalso be translated to other languages; see the Name description for more\ninformation on translating the Site's description.\n\n**Active:** determines whether a Site is active or inactive. Inactive Sites are\ninaccessible but can be activated whenever a Site administrator wishes.\n\n**Membership Type:** can be open, restricted, or private. An open Site appears\nin the My Sites app and users can join and leave the Site whenever they want. A\nrestricted Site is the same except users must request membership. A Site\nadministrator must then explicitly grant or deny users' requests to join. A\nprivate Site does not appear in the My Sites app and users must be added to it\nmanually by a Site administrator.\n\n**Allow Manual Membership Management:** determines whether to allow or disallow\nusers to be manually added or removed from the Site. By default, manual Site\nmembership management is enabled. This allows administrators to manually assign\nusers to the Site. It also allows users to join open Sites or request membership\nfrom restricted Sites using the My Sites app. For organization Sites, manual\nSite membership management is disabled, by default. This causes organization\nmembers to be automatically assigned membership following the organization's\nmembership policy. Also, because manual membership management is disabled for\norganization Sites, by default, the *Users* section of *Sites* is unavailable.\nTo activate the *Users* functionality for your organization Site, you'll need to\ncheck *Allow Manual Membership Management* after creating the organization Site\nby navigating to its *Site Settings* menu.\n\n| **Note:** It's possible for Site memberships to be handled automatically by a\n| membership policy. The membership policy can check various pieces of information\n| from each user, such as their first names, last names, birthdays, job titles,\n| organizations, and user groups. Using this information, the Site membership\n| policy can automatically assign members to the Site. If your Site will implement\n| a membership policy, your Site administrators can disallow manual membership\n| management for their Site. When the Allow Manual Membership Management option is\n| disabled, the *Members* section of Site Administration (Site Memberships and\n| Site Teams) is hidden, even from administrators.\n\n**Parent Site:** lets you select a parent Site for the Site that's being\ncreated. Sites can be organized hierarchically. Using hierarchical Sites\nprovides a simplified way to manage Site memberships and Site content sharing.\nFor organizations that have attached Sites, the organization hierarchy should\nmatch the Site hierarchy. When you select a parent Site, an additional option\nappears: *Limit membership to members of the parent Site*. If this option is\nenabled, the Site's membership policy performs a check so that you can only\nassign members to the current Site if they're already members of the parent\nSite.\n\n2.  Set the *Membership Type* as *Restricted*.\n\n3.  Leave the remain defaults and click *Save*.\n\nWhen creating a blank Site or organization Site, the Site is not immediately\nviewable. This is because Sites without a page are impossible to view.\nTherefore, before you can view your Site, you must first create a page for it.\nTo add a page for your temporarily invisible Site, navigate to the *Navigation*\noption from Site Administration. Then add a public page. After adding your\nSite's first page, it renders and your Site is viewable. For more information\nabout adding pages, see the\n[Creating and Managing Pages](/docs/7-2/user/-/knowledge_base/u/creating-and-managing-pages)\nsection.\n\nYou can also categorize your Site template using tags and categories by\nselecting the *Categorization* menu from the bottom of the page. To learn more\nabout using tags and categories in Liferay, see the \n[Organizing Content with Tags and Categories](/docs/7-2/user/-/knowledge_base/u/organizing-content-with-tags-and-categories)\nsection. Lastly, at the top of the page is an additional tab named *Social*.\nThis tab manages whether users of your Site can mention other users. You'll\nlearn about mentioning users later in the Social Collaboration sections.\n\nWhen creating a Site from a Site template, you're asked if you want to copy\nthe pages from the template as public pages or as private pages. By default, the\nSite is linked to the Site template and changes to the Site template propagate\nto any Site based on it. A checkbox appears for unlinking the Site template if\nthe User has permission to do so.\n\nOnce the Site has been created, you should configure its settings to fit your\nneeds. You can learn more about Site Settings in\n[Configuring Sites](/docs/7-2/user/-/knowledge_base/u/configuring-sites).\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/01-adding-pages-to-sites-intro.markdown",
    "content": "---\nheader-id: creating-and-managing-pages\n---\n\n# Adding Pages to Sites\n\n[TOC levels=1-4]\n\nIn the previous section, you learned how to create sites. You may have gathered \nfrom that section that sites aren't particularly useful without pages. In\nfact, sites primarily exist for the sake of organizing pages and content, so \nnow you'll learn about the different types of pages in Liferay, and how to \nselect the best tools based on your use cases. You'll also learn how to manage \npages and use various configuration options.\n\nBefore diving into page creation, you should understand the two major page\ntypes provided in @product-ver@:\n\n[*Content Pages*](/docs/7-2/user/-/knowledge_base/u/creating-content-pages):\nThis new page type is flexible, especially for non-technical users. You can\nbuild pages using content created from pre-defined fragments, which themselves\ncan contain widgets. \n\n[*Widget Pages*](/docs/7-2/user/-/knowledge_base/u/adding-applications-to-a-page):\n@product@'s traditional page type is made up of one or more widgets. There are\nsome use cases (particularly if a page's sole purpose is to host an application)\nto prefer widget pages.\n\nYou should always opt for Content Pages, unless there's a concrete reason\notherwise. Content Pages offer many key features of Widget Pages plus more. Some\nkey features of Content Pages include\n[personalized Experiences](/docs/7-2/user/-/knowledge_base/u/segmentation-and-personalization)\nand\n[A/B Testing](https://help.liferay.com/hc/en-us/articles/360034856751-A-B-Testing)\nFurthermore, Content Pages are easier to use and are more powerful for\nnon-technical users compared to Widget Pages.\n\nWhy would someone prefer Widget Pages? Widget Pages were once the only page type\navailable in earlier versions of @product@, so they're more familiar than\nContent Pages.\n\nAdditionally, there are still a few things that Widget Pages provide that are\nnot possible with Content Pages:\n\n- *Developing an advanced custom layout*: Using Content Pages, authors can\n  create their own page layouts. This prevents developers from creating\n  pre-selectable, custom layouts with FreeMarker like Widget Pages allowed for.\n  Though Content Pages let you create a layout visually (a more user-friendly\n  approach), the programmatic approach of Widget Pages allows for more advanced\n  capabilities.\n\n- *User-Customizable columns*: This was a rarely used feature of Widget Pages\n  that is not provided in Content Pages. If your page requires a\n  user-customizable column, you must use a Widget Page.\n\n- *Using Staging with Page Variations*: Content Pages do not support Staging's\n  Page Variations. This avoids possible confusion with the similar capability to\n  create variations of a page that are used in personalization and A/B Testing.\n\n- *Widget permissions*: You can configure widget permissions on a Widget Page.\n  This is not yet possible for Content Pages; it's planned for a future release.\n\n- *Widget Look & Feel*: On Widget Pages you can access the\n  [Look and Feel](/docs/7-2/user/-/knowledge_base/u/look-and-feel-configuration)\n  tool for widgets, offering fine-grained control over its CSS. This is not\n  available for widgets on Content Pages, since the look and feel of your\n  content is defined in the theme or by using Fragments.\n\nContinue on to learn more about creating pages!\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/02-creating-pages/01-creating-pages-intro.markdown",
    "content": "---\nheader-id: creating-pages\n---\n\n# Creating Pages\n\n[TOC levels=1-4]\n\nAfter you create a Site, you can add new pages and maintain them. You can do\neverything you need with pages from Site Administration.\n\n1.  If you're not currently on the Site you want to edit, click the *Site \n    Selector* button (![Compass](../../../../../images/icon-compass.png)) next to \n    your current Site name in the Menu and select your desired Site.\n\n2.  Go to *Site Administration* &rarr; *Site Builder*.\n\n3.  Click on *Pages*\n \n![Figure 1: The Pages screen lets you edit your Site pages as a whole.](../../../../../images/managing-site-pages.png)\n\nFrom here, you'll create pages and page templates.\n\n| **Note:** Pages are always part of page sets, and page sets are always associated\n| with Sites. Even users' personal pages are part of their personal Sites. All\n| pages belong to one of two types of page sets: public pages and private pages.\n| By default, anyone can access public pages, even non-logged in users (guests).\n| Only users who are members of the Site that owns the pages can access private\n| pages. This means the private pages of an organization's Site are viewable only\n| by Site members and members of the organization.\n\nFrom *Pages* you can do several things:\n\n1.  Click the (![Compass](../../../../../images/icon-add.png)) button in the top\n    right corner to add a new page.\n\n2.  Click options, icons, manage page, or page set settings.\n\n3.  Create child pages by clicking the `+` button next to an existing page.\n\n![Figure 2: Understanding the options on Site Pages.](../../../../../images/site-pages-breakdown.png)\n\nAdding a child page creates child pages in the hierarchy below the page you've \nselected. You can nest pages as deep as you like.\n\n| **Note:** You're not forced to define the page hierarchy in a page's friendly\n| URL. Therefore, child page friendly URLs are not required to include their\n| parent page. For example, a parent page named Parent and its child page named\n| Child could have the URLs *SITE_URL/parent* and *SITE_URL/child*,\n| respectively. The default friendly URL given to a page is based only on the\n| page name and not the hierarchy. If you wish to modify a generated friendly\n| URL, you can do so by following the\n| [Friendly URL](/docs/7-2/user/-/knowledge_base/u/individual-page-settings#name-and-friendly-url)\n| configuration section.\n\nOnce you've clicked the `+` icon to add a page, you're asked to select the type\nof page you are creating. There are two top options followed by other page \ntypes:\n\n**Widget:** Creates a page with a layout template that defines a number of rows\nand columns for adding widgets to your page.\n\n**Content:** Creates a Content Page with inline editing based on Fragments.\n\nBelow those you have other options:\n\n**Full Page Application:** Creates a page that displays a single full page\napplication.\n\n**Page Set:** Creates a container for subpages that is not actually a page\nitself. \n\n**Link to a Page of this Site:** Links to a page within the same Site. This is\noften used to make a page available in multiple parts of a Sites hierarchy.\n\n**Panel:** A page containing any number of applications as selected by an\nadministrator, but only one is displayed at a time. Users select the portlet\nthey want to use from a menu on the left side of the page, and the selected\nportlet takes up the entire page. \n\n**Embedded:** Displays content from another website inside your instance. An\nadministrator can set a URL from the page management interface and that page\nappears in the context and within the navigation of your Liferay instance. \nTo embed an external website, you must provide the protocol in the URL \n(e.g. `https://www.liferay.com/`).\n\n**Link to URL:** Creates a link to any URL. This could be an external page or\na link across Sites in the same Liferay instance.\n\nTo the left, under Collections, you can choose to view the basic page types\nor a collection of page templates. By default, only *Global Templates*\nappears, but additional collections you create appear here as well.\n\n![Figure 3: You must select a page type when adding pages.](../../../../../images/page-types-adding.png)\n\nAfter you've added a page, it may be difficult to track what kind of page you're\ncurrently viewing. The page type appears at the top of the page to help you\ndetermine the administration options you have and where you need to go to\nconfigure the page.\n\n![Figure 4: Here are three different page types as they're displayed in the heading.](../../../../../images/page-type-guide.png)\n\nNow that you know the basics of adding pages, you can start working on the Lunar\nResort Site. If you're not currently on the right Site, navigate to Site\nAdministration in the Menu, select the compass icon next to the current Site\nname, and select the Site you wish to edit.\n\nIf you must ever modify the page you've created for your Site, select\n*Configure* from the Options menu for the page from *Pages*. When\nconfiguring a specific page, you have more options than when you were creating\na new page. You can also read \n[Configuring Sites](/docs/7-2/user/-/knowledge_base/u/configuring-sites).\n\nThere are also configuration options that are only available for individual\npages or page groups only. You'll learn about options available for both\ninstances.\n\nNext, you'll look at creating the main page types you'll use in @product@.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/03-content-pages/01-creating-content-pages-intro.markdown",
    "content": "---\nheader-id: creating-content-pages\n---\n\n# Creating Content Pages\n\n[TOC levels=1-4]\n\nContent Pages provide a new paradigm for creating pages in @product@. They \nempower marketers and content creators to build pages that can be easily \nmanaged and have their content edited in-line and on the fly.\n\nWith Content Pages, you build pages from reusable, extensible elements called \n**Page Fragments**. Page Fragments contain things like images, content, or \nfunctionality and are added to a page to be edited and configured to match your \ndesign. Web Developers can create Page Fragments to expand the options you have \navailable for building pages.\n\nSome Fragments are completely static elements, like an image or text. These can \nbe useful for building a landing page, or realizing a complicated design \nvision. Other Fragments have a number of editable fields which enable \nflexibility and experimentation when creating the perfect page for your needs.\n\n## Creating Page Fragments\n\nThe first thing you need to create a page, are the raw materials: Page \nFragments. Fragments are organized into Collections which describe some aspect \nof the purpose of the Fragments and help keep them organized. For example, if \nyou have a number of Fragments associated with a certain marketing campaign, \nyou could create a Collection for that campaign, so that you have all of those \nFragments readily available when you're working on a page for that campaign.\n\nWith all of the Fragments and Collections that are included in @product-ver@, \nmany projects can be completed using only the out-of-the-box Fragments and \ncapabilities. In many cases, though, you need to develop your own Fragments.\nYou can learn about the Fragment creation process and editing interface in the\n[Creating Page Fragments](/docs/7-2/user/-/knowledge_base/u/creating-page-fragments)\narticle. Developing Fragments is covered in more detail in the\n[Page Fragment development guide](/docs/7-2/frameworks/-/knowledge_base/f/page-fragments).\n\n## Building Content Pages\n\nAfter you've got your materials, you need to arrange them on a page. Content \nPages are built with Page Fragments and Widgets. Widgets work just like they \nalways have, drag them onto the page where you want to use them. Fragments are \nintended to be reusable, so each Fragment is like a single puzzle piece that \ncan fit in many different puzzles. Using the tools you have available, you can \ncreate stunning pages through an intuitive, empowering interface. In the \n[Building Content Pages guide](/docs/7-2/user/-/knowledge_base/u/building-content-pages)\nyou will create Content Pages using a variety of the features and capabilities\nof Content Pages.\n\n## The Content Page Interface\n\nThere are many features of the Content Page Interface---too many to cover in a \npractical exercise. For a complete overview of every nook and cranny of the \nContent Page management interface see the\n[Content Page Interface reference](/docs/7-2/user/-/knowledge_base/u/content-page-management-interface).\n\n## Personalizing Content Pages \n\nWhen you create Content Pages, you can create different **Experiences** for \nusers based on User Segments. You can create a unique Experience on any Content \nPage for any existing User Segment. For more information, see the\n[Content Page Personalization guide](/docs/7-2/user/-/knowledge_base/u/content-page-personalization).\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/03-content-pages/02-content-page-elements.markdown",
    "content": "---\nheader-id: content-page-elements\n---\n\n# Content Page Elements\n\n[TOC levels=1-4]\n\nContent Pages, like Widget Pages, are built by dragging and dropping elements\nonto the page and then configuring the way those elements appear. There are\nthree kinds of elements: \n\n**Sections** are fragments that define a space to place other elements.\nA section fills the entire width of the page. Sections can be thought of as\n*complete* Fragments that serve a purpose by themselves. A large banner image\nwith a text overlay is an example of something you might build as a section.\n\n![Figure 1: A Section named *Banner* being displayed while editing a Content Page.](../../../../../images/content-page-section-example.png)\n\n**Layouts** are special Sections that define spaces where you can add fragments\nor widgets. Each layout you add fills the width of the page. You can add any\nnumber of layouts to the page.\n\n![Figure 2: A 3 Column and 1 Column layout stacked on top of each other.](../../../../../images/content-page-layout-example.png)\n\n**Components** are small design elements or pieces that add functionality to the\npage. A component might be an image with formatting or a block of text with\nstyling pre-applied. Components must be added to the page inside a Layout. If\nyou add a component outside an existing Layout, a one column layout is\nautomatically added to contain the Component. While Sections should be complete\nby themselves, Components work together to build pages piece by piece.\n\n![Figure 3: Here are several of Liferay's out of the box components arranged in the layout you saw previously.](../../../../../images/content-page-component-example.png)\n\n@product@ ships with a plethora of Layouts, Sections, and Components to use to\nbuild pages, and a\n[web developer can create their own Fragments](/docs/7-2/frameworks/-/knowledge_base/f/creating-fragments)\nto add to these. \n\n## Editable Elements\n\nFragments can have editable elements. After a Fragment has been added to a page,\nyou can click on an editable area to provide your own text, image, or links in\nplace of the default defined in the Fragment.\n\nYou can also map these elements to content. You can set the *Content* for the\nelement (web content article, document, or blog) and choose its applicable\n*Field* to display (e.g., title, author name, tags, etc.). You can configure\nthis by selecting the element's *Map* button\n(![Map](../../../../../images/icon-map.png)).\n\n| **Note:** Many mapping improvements were released in Liferay DXP 7.2 SP1+ and\n| Liferay Portal 7.2 GA2+. For example, mapping editables to text/URL fields of\n| existing content and mapping Fragment background images to image fields of\n| existing content. You can also map\n| [custom fields](/docs/7-2/user/-/knowledge_base/u/custom-fields). To ensure\n| you leverage the latest editable element mapping features, upgrade to these\n| versions.\n\nFor more information on developing these elements, see \n[Fragment Specific Tags](/docs/7-2/reference/-/knowledge_base/r/fragment-specific-tags).\n\nNow you'll learn about each editable type.\n\n### Editable Text\n\nEditable text can be plain or rich text. Plain text has no special styling. Rich\ntext enables text styles, typographical emphasis, alignment, and list\nformatting.\n\n![Figure 4: The rich text editor provides a simple WYSIWYG interface with a number of formatting options.](../../../../../images/content-page-rich-text-editor.png)\n\n### Editable Images\n\nEditable image elements allow replacing the image URL or an image from your\nDocuments and Media library. You can provide a link target for the image.\n\nTo edit an image from the Content Page editor,\n\n1.  Click on the image you want to replace.\n\n2.  Click ![Image Properties](../../../../../images/icon-edit.png).\n\n![Figure 5: Editing an image allows you to enter a URL, select an image from Documents and Media or set a link for the image.](../../../../../images/fragment-image-editor.png)\n\nFrom here, you can click *Select* to upload an image from Docs and Media or\ndefine an image URL. Click *Clear* to reset the image. You can also specify an\nimage description.\n\nYou can also specify a background image for a layout from Section Builder. Click\nthe Layout, select *Layout Background Image*, and define the image to display.\n\n| **Note:** Mapping a Layout background image is available in Liferay DXP 7.2\n| SP1+ and Liferay Portal 7.2 GA2+.\n\nFor more information on developing editable images, see \n[Making Images Editable](/docs/7-2/reference/-/knowledge_base/r/fragment-specific-tags#making-images-editable).\n\nYou can also define a link for your image. You'll learn about this next.\n\n### Editable Links\n\nEditable links can be associated with entities that redirect you to a content\ntype or Page (e.g., buttons).\n\nTo edit a link from the content page editor,\n\n1.  Click on the link or button that you want to edit.\n\n2.  Click on ![Edit](../../../../../images/icon-edit.png) to edit the link text.\n\n3.  Click on ![Link](../../../../../images/icon-link.png) to edit the link properties.\n\n4.  Click on ![Map](../../../../../images/icon-map.png) to edit the link mapping\n    (described earlier).\n\nFrom the Link Properties popup, you can define the following link options:\n\n*Manual:* defines a manual link or map it to an existing content field.\n\n- *URL:* sets the link's URL.\n- *Target:* set the link's behavior.\n\n*From Content Field:* \n\n- *Content:* sets the content type.\n- *Field:* sets the field to display for the selected content.\n\nSome of the content fields include\n\n- Categories\n- Tags\n- Display Page URL\n- Description\n- Publish Date\n- Summary\n- Title\n- Last Editor Name\n- Author Name\n- Basic Web Content\n\nFor more information on developing editable links, see \n[Creating Editable Links](/docs/7-2/reference/-/knowledge_base/r/fragment-specific-tags#creating-editable-links).\n\nNext you'll learn about the Content Page Editing Interface.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/03-content-pages/03-content-page-interface.markdown",
    "content": "---\nheader-id: content-page-management-interface\n---\n\n# Content Page Management Interface\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/creating-pages/building-and-managing-content-pages/content-pages-overview.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nUnlike Widget Pages, Content Pages can only be edited through the *Site \nBuilder* and cannot be edited live on the page. Any edits that you make to a \npage are saved as a draft until you publish the page. Subsequent changes \nafter the initial publication are again saved as a draft, without affecting the \nlive page, until the page is published again. To create a Content Page,\n\n1.  Go to *Site Management* &rarr; *Site Builder* &rarr; *Pages*.\n\n2.  Click ![Add](../../../../../images/icon-add.png).\n\n3.  On the next page, select *Content Page* and provide a name for the page.\n\n    You will be brought to the Content Page management interface.\n \n    ![Figure 1: Each Content Page starts as a blank page.](../../../../../images/content-page-edit-blank-page.png)\n\nTo edit an existing Content Page,\n\n1.  Go to *Site Administration* &rarr; *Site Builder* &rarr; *Pages*.\n\n2.  Click *Actions* (![Actions](../../../../../images/icon-staging-bar-options.png))\n    &rarr; *Edit* next to the Content Page you want to edit.\n\nYou can also get to this page by selecting the *Edit* button\n(![Edit](../../../../../images/icon-edit-pencil.png)) from the Control Menu if\nyou're viewing the published Content Page.\n\nOn this page you can view a preview of your page, add Fragments and Widgets, \nand manage the configuration for the page or any Fragments and Widgets \ncurrently residing on the page.\n\nYour tools for building the page are all found on the right side of the page. \nFrom top to bottom, the options are\n\n- [Sections](#sections)\n- [Section Builder](#section-builder)\n- [Widgets](#widgets)\n- [Page Structure](#page-structure)\n- [Look and Feel](#look-and-feel)\n\n### Sections\n\nWhen you open *Sections* you see a list of Collections available. Initially, \nyou only have the **Basic Sections** Collection which is included with the \nproduct. You can open the Collection and drag Sections directly onto the page.\n\n![Figure 2: *Sections* contains Fragments that fully define spaces on your page.](../../../../../images/content-page-sections-editor.png)\n\nOnce a section is added to a page, you can edit its background color,\nbackground image, and spacing. Since these options are available to marketers\nand administrators editing a page, the options are limited, and the color\npalette can be set by the Fragment developer.\n\n![Figure 3: The Section managment tool provide powerful tools, but with the training wheels still on.](../../../../../images/content-page-sections-config.png)\n\n### Section Builder\n\nIn Section Builder, you start with *Layouts* and *Basic Components*. Add \nLayouts to the page to provide a spaces where you can add Components.\n\n![Figure 4: *Sections Builder* contains *Component* Fragments which are intended to be combined to create Sections.](../../../../../images/content-page-section-builder-editor.png)\n\n### Widgets\n\nThe Widgets section functions just like the *Add* menu on a Widget Page. The \nfull list of available widgets is displayed, and you can add them to the page.\n\n![Figure 5: The Widgets section provides a list of Widgets that can be added inside of a Layout.](../../../../../images/content-page-widget-editor.png)\n\nThe main difference is that only the main configuration options are available for widgets on \nContent Pages. Various other configurations like *Look and Feel* are only \navailable for widgets on Widget Pages.\n\n### Page Structure\n\nPage Structure provides a high level view of every Fragment and every field\nwithin each Fragment on the page.\n\n![Figure 6: *Page Structure* shows you a hierarchy of your page.](../../../../../images/content-page-page-structure-editor.png)\n\nClicking on a field in Page Structure will  highlight it on the page. On large\ncomplicated page, this helps you keep on top  of where everything is and also\naccess items that might be hard to click on  directly.\n\n### Look and Feel\n\nClick the *Look and Feel* icon \n(![Look and Feel](../../../../../images/icon-look-and-feel.png)) \nto change the theme or manage other options for the page. These options are \nfully documented in \n[Creating Pages](/docs/7-2/user/-/knowledge_base/u/creating-pages). \n\n### Comments\n\nYou can also comment on any page fragments. This allows discussion and \ncollaboration for teams creating content pages. \n\nComments are disabled by default, but administrators can enable them from \n*Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr; *Pages* \n&rarr; *Content Page Editor*. Select the *Comments Enabled* checkbox and click \n*Update*. This enables content page comments for all instances. To control this \non an instance-by-instance basis, navigate to the same setting in \n*Instance Settings* (instead of System Settings). \n\n![Figure 7: Administrators can enable comments for content pages.](../../../../../images/enable-content-page-comments.png)\n\nIf comments are enabled, you can access them via the *Comments* icon \n(![Comments](../../../../../images/icon-comments.png)). \nThe comments appear for the selected fragment. You can take the following \nactions in the comments UI for a fragment:\n\n-   Add new comments and reply to any existing ones. \n-   Resolve comments by clicking the checkbox for each. Resolving a comment \n    hides it from view, unless *Show Resolved Comments* is selected. \n-   Edit and delete your own comments via the Actions button \n    (![Actions](../../../../../images/icon-actions.png)) \n    for each. \n\nIf you de-select a fragment or enter the comments UI without a fragment selected, \na list of the fragments on the page appears with the number of comments for \neach. Selecting a fragment then shows its comments. \n\n![Figure 8: When creating content pages, you and your team can comment on any fragments.](../../../../../images/content-page-comments.png)\n\n### The Title Bar\n\nThe title bar provides navigation back to the Main Menu, a link to page\nconfiguration, and the ability to search for other pages. The title bar is\ncovered in more detail in \n[Creating Pages](/docs/7-2/user/-/knowledge_base/u/creating-pages). \n\n![Figure 9: The title bar has several tools built into it.](../../../../../images/content-page-edit-title-bar.png)\n\nGreat! Now you know how to use the content page interface!\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/03-content-pages/04-building-content-pages-and-templates.markdown",
    "content": "---\nheader-id: building-content-pages\n---\n\n# Building Content Pages\n\n[TOC levels=1-4]\n\nTo demonstrate Content Page capabilities, create a *Landing Page* for a space \nshuttle tour based on this design:\n\n![Figure 1: You have lots of flexibility when arranging Fragments on a page.](../../../../../images/content-page-design-mockup.png)\n\n## Creating a Content Page\n\nTo create the page, follow these general steps:\n\n1.  Add the Fragments you need to define the basic structure of the page.\n2.  Edit the text for the current page.\n3.  Edit the images.\n4.  Publish the page.\n\nYou'll complete this process next.\n\n1.  From Site Administration for your Site, go to *Site Builder* &rarr; *Pages*.\n\n2.  Click the (![Add](../../../../../images/icon-add.png)) and select *Content\n    Page*.\n\n3.  Set the *Name* as *Space Landing Page* and click *Save*.\n\n    Now you're in Content Page creation. As you work, a draft of the page is\n    automatically saved, but you must click *Publish* to make it available for\n    use.\n\n4.  Open *Sections* &rarr; *Basic Sections* and drag a *Banner Center* Fragment \n    onto the page.\n\n5.  Open *Section Builder* &rarr; *Layouts* and add a 2 Column layout below\n    that.\n    \n6.  Open *Basic Components*.\n\n7.  Add a *Card* to the left column of the layout.\n\n8.  Add a *Paragraph* to the right side, and then an *Image* below that inside \n    the same column.\n\n![Figure 2: You have lots of flexibility when arranging Fragments on a page.](../../../../../images/content-page-creation-step-1.png)\n\nNow that the structure is defined, start editing the text and images.\n\n1.  Click in each text box and edit the text to be relevant to your goal of \n    directing potential customers to space shuttle tours.\n    \n    ![Figure 3: Edit the text and formatting as you see fit.](../../../../../images/content-page-creation-step-2.png)\n\n2.  Click on the main banner image, and then on the \n    ![Edit](../../../../../images/icon-edit-pencil.png) icon.\n\n3.  Select one of the many space flight images that are surely filling up your\n    Documents and Media library, upload a new one, or specify a URL.\n\n![Figure 4: Add some images, and the big picture comes together.](../../../../../images/content-page-creation-step-3.png)\n\nAnytime during Fragment creation, you can remove, duplicate, or configure the\nFragment.\n\n![Figure 5: Add some images, and the big picture comes together.](../../../../../images/content-page-fragment-options.png)\n\nConfiguring a Fragment lets you modify the default options for a provided\nFragment. This also lets you duplicate a Fragment and configure duplicates\ndifferently, so you can reuse base Fragments instead of developing new ones.\nWhen duplicating a Fragment, its configuration and editable elements are also\ncopied.\n\n| **Note:** Fragment configuration and duplication are available in Liferay\n| DXP 7.2 SP1+ and Liferay Portal GA2+.\n\nThis is looking good so far, but the one difference between the design mockup \nand the final result is that the background was black for the original. To \nfinish it up, change the background color for the section to black.\n\n1.  Click on the bottom Section and some additional icons will appear.\n\n    ![Figure 6: You can change the background color, image, or edit spacing and padding for a section. You can also remove it.](../../../../../images/content-page-section-editor.png)\n\n2.   Click on *Background Color* ![Background Color](../../../../../images/icon-color.png), and select Black.\n\n3.  Finally, publish your page.\n\nIn just a few minutes, you used the power of Content Pages and Fragments to go \nfrom nothing to perfectly recreating a page design. To take it to the next \nlevel, head over to the\n[Segmentation and Personalization guide](/docs/7-2/user/-/knowledge_base/u/segmentation-and-personalization).\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/03-content-pages/05-content-page-propagation.markdown",
    "content": "---\nheader-id: propagation-of-changes\n---\n\n# Propagation of Changes\n\n[TOC levels=1-4]\n\nIf you make an update to a Page Fragment or Content Page Template, it doesn't\nautomatically propagate changes, but you can access the *Usages and Propagation*\npage to selectively propagate changes.\n\n1.  From the Site Administration menu, go to *Site Builder* &rarr; *Page \n    Fragments*\n\n2.  Select the Collection containing the changed fragment.\n\n3.  Open the *Actions* menu (![Actions](../../../../../images/icon-actions.png)) for\n    the Fragment and select *View Usages*.\n\nThe *Usages and Propagation* page shows a list of every Page, Page Template, \nand Display Page that uses the selected Page Fragment. You can then selectively\npropagate Fragment changes to any or all of the pages listed. You can use the\nvarious filters and selection options to apply updates to pages quickly.\n\n![Figure 1: Viewing the Usages and Propagation page.](../../../../../images/fragment-usages-and-propagation.png)\n\n| **Note:** Beginning in Liferay DXP 7.2 SP1+ and Liferay Portal 7.2 GA2+, you\n| can propagate changes from global Fragments to their usages on child Sites.\n| See \n| [Creating and Managing Fragments](/docs/7-2/user/-/knowledge_base/u/creating-page-fragments#creating-and-managing-fragments)\n| for more information on global Fragments.\n\nTo update a page or template,\n\n1.  Select the page or pages you want to update by checking the box next to the\n    page name.\n\n2.  Click the *Propagate* icon (\n    ![Propagate](../../../../../images/icon-propagate.png))\n\n    After you propagate changes, visit any affected page to verify there were no\n    unexpected side effects of the changes.\n\n    | **Note:** Developers or others testing a Fragment can enable Fragment change\n    | propagation from the Control Panel. You can learn more about this\n    | [here](/docs/7-2/frameworks/-/knowledge_base/f/managing-fragments-and-collections#propagating-fragment-changes-automatically).\n    | It's recommended to only leverage this functionality during testing, as\n    | automatic propagation on the production environment can cause unintended\n    | consequences.\n\n    Changes to existing `editable` fields are not propagated since this\n    overwrites content currently in content pages. To force propagation to\n    content in an `editable` field, a developer must change the field ID. Any\n    content created in that field no longer appears in the Content Page\n    when the changes are propagated, but it remains in the database and can\n    be retrieved using the old ID.\n\nNext you'll learn how to create your own Page Fragments.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/03-content-pages/06-creating-page-fragments.markdown",
    "content": "---\nheader-id: creating-page-fragments\n---\n\n# Creating Page Fragments\n\n[TOC levels=1-4]\n\nFragments enable you to create rich content. With all of the included Fragments\nand Collections, many projects can be completed using just what ships with\n@product@. Sometimes, though, you must create your own Fragments. Fragments are\nbuilt using HTML, CSS, and JavaScript. For a deeper dive into creating a custom\nFragment, see [Developing Fragments](/docs/7-2/frameworks/-/knowledge_base/f/creating-fragments).\n\nIn this article, you'll learn about the Page Fragments interface.\n\n## Creating and Managing Fragments\n\nTo navigate to the Page Fragments interface,\n\n1.  Go to Site Administration. Make sure the Site where you want to work is \n    selected. If you prefer creating a Fragment that's available for all Sites,\n    navigate to the *Global* Site and create your Fragment there. Global\n    Fragments are inherited by child Sites, so they can only be edited from the\n    Global Site. Any resources the Global Fragment references (e.g., image) from\n    the Global Site is copied to a Site that leverages the Fragment.\n\n    | **Note:** Creating Global Fragments is available in Liferay DXP 7.2\n    | SP1+ and Liferay Portal 7.2 GA2+. To expose this feature in the initial\n    | releases of those versions, however, you must create a `.config` file.\n    | Create the\n    | `com.liferay.fragment.web.internal.configuration.FragmentGlobalPanelAppConfiguration.config`\n    | file and add the `enabled=B\"true\"` property. Then copy it to your\n    | @product@ instance's `osgi/configs` folder.\n\n2.  Select *Site Builder* &rarr; *Page Fragments*\n\n![Figure 1: Here is the Page Fragments page with no custom Fragments or Collections created.](../../../../../images/empty-fragments-page.png)\n\nFragments are organized in *Collections*. The main Page Fragments page shows\navailable Collections (out-of-the-box Fragment Collections to start), allows\nImport and Export, and enables you to create Collections. You can also manage\nthe organization and display of Fragments and Collections you have created.\n\n| **Note:** You cannot edit a default Fragment. If you'd like to provide an\n| edited version of a default Fragment, you can copy it to your custom\n| Collection and edit it there. To do this, navigate to the default Fragment\n| Collection and click the Fragment's *Actions*\n| (![Actions](../../../../../images/icon-actions.png)) &rarr; *Copy To* button.\n| Then select the Collection to copy the default Fragment to. Copying default\n| Fragments is available in Liferay DXP 7.2 SP1+ and Liferay Portal 7.2 GA2+.\n\nTo create a Fragment, you must first create a Collection.\n\n1.  Click *New* &rarr; *Collection* to add a Collection.\n\n2.  Give the Collection a *Name* and *Description* and click *Save*.\n\nCollections help you organize Fragments, and can be used to differentiate \nbetween different types of Fragments or Fragments used by different groups or \ndepartments. Next, create a Fragment inside the Collection you created.\n\n1.  Click on the Collection you created.\n\n2.  Click the *Add* icon (![New](../../../../../images/icon-add.png)) to create\n    a Fragment.\n\n3.  Choose either *Section* or *Component*.\n\n3.  Give it a *Name* and click *Save*.\n\nThe Fragment development environment appears. Each pane in the editor has\na different function:\n\n- The top left pane is for entering HTML.\n- The top right pane is for entering CSS.\n- The bottom left pane is for entering JavaScript.\n- The bottom right pane provides a live preview as you work in the other panes.\n\n![Figure 2: The Fragments editor provides an environment for creating all the parts of a Fragment.](../../../../../images/fragments-editor.png)\n\nIn addition to standard HTML, CSS, and JavaScript, developers can also embed\nwidgets and provide fields containing text and images that can be edited during\nthe final Content Page publication process. Fragment development is covered in\ndepth in \n[Developing Fragments](/docs/7-2/frameworks/-/knowledge_base/f/creating-fragments).\n\nYou can also include resources in your Fragment Collection that your Fragments\ncan reference. This is helpful when exporting/importing Fragments: their\nresources are automatically included. If they're stored somewhere else\n(e.g., Documents and Media), you must export/import them separately. Click the\n*Resources* tab for your Collection and add resources (e.g., image, document,\netc.) there.\n\n![Figure 3: The Resources tab can be selected from the Fragment Collection.](../../../../../images/fragment-resources-tab.png)\n\nOnce added, you can reference resources from your Fragment's code without\nworrying about their availability. You can learn more about doing this in \n[Including Default Resources in Fragments](/docs/7-2/frameworks/-/knowledge_base/f/including-default-resources-in-fragments).\n\nNext you'll learn how to import and export Page Fragments.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/03-content-pages/07-export-import-fragments.markdown",
    "content": "---\nheader-id: exporting-and-importing-fragments\n---\n\n# Exporting and Importing Fragments\n\n[TOC levels=1-4]\n\nOften you'll want to reuse or re-purpose code from Fragments. To do this, export\nyour Fragment Collections.\n\n## Exporting Fragments\n\nThere are two ways to export Fragments:\n\n1.  Export a Collection or set of Collections.\n\n2.  Export some Fragments outside of a Collection.\n\nTo export a single Collection, \n\n1.  Go to *Site Administration* &rarr; *Build* &rarr; *Page Fragments*.\n\n2.  Next to *Collections* click *Actions*\n    (![Actions](../../../../../images/icon-actions.png)) and select *Export*.\n\n3.  Select a Collection or multiple Collections to be exported. Each collection\n    exports in a separate file.\n\n    ![Figure 1: Select Collections to export.](../../../../../images/collections-export.png)\n\nEach Collection `.zip` contains all Collection data and Fragments.\n\nTo export individual Fragments,\n\n1.  Click on the Collection that you want to export.\n\n2.  To export all Fragments in the Collection without exporting any Collection\n    data, click *Actions*\n    (![Actions](../../../../../images/icon-actions.png)) *Export* next to \n    the Collection name. A `.zip` file is generated and downloaded\n    automatically.\n \n    ![Figure 2: Exporting all of the Fragments in a Collection.](../../../../../images/fragments-export-individual.png)\n \n3.  To export a single Fragment, click *Actions* \n    (![Actions](../../../../../images/icon-actions.png)) &rarr; *Export* next\n    to the Fragment. A `.zip` file is generated and downloaded automatically.\n\nNote that if you export a single Fragment or a group of Fragments without\na collection, they must be imported into an existing Collection. \n\nYou can also export Global Collections and single Fragments from your Site.\n\nNow it's time to import your Fragments to where you need them. \n\n## Importing\n\nYou can import a Collection that was created in @product@, a Collection\ncreated using external tools, or Page Fragments without a collection. When you\nfirst import Page Fragments, they aren't available for use until you have\napproved them for use. This is to ensure that there are no errors in any\nimported fragments before they are added to a page.\n\nSee \n[Developing a Fragment Using Desktop Tools](/docs/7-2/frameworks/-/knowledge_base/f/page-fragments-desktop-tools#importing-and-exporting-fragments) for more information on \ncreating and importing Fragments using other tools.\n\n### Importing Collections\n\nTo import a collection, follow these steps:\n\n1.  Go to *Site Administration* &rarr; *Build* &rarr; *Page Fragments*.\n\n2.  Next to *Collections*, click *Actions* (![Actions](../../../../../images/icon-actions.png)) and select *Import*.\n\n    ![Figure 3: Importing and exporting Collections is accessed from a single menu.](../../../../../images/collections-import.png)\n\n3.  On the next screen, click *Choose File* and select the file you want to \n    import.\n\n4.  If you want to replace an existing collection, make sure the box is checked\n    for *Overwrite Existing Files*.\n \n5.  Click *Import* and the collection is uploaded.\n\n### Importing Individual Page Fragments\n\nYou can also import a single Page Fragment or a set that was exported outside of\na collection.\n\n1.  From the root level of the Fragments page, click on an existing Collection\n    where you want to import the Fragments.\n \n2.  From inside the Collection click the *Actions*\n    (![Actions](../../../../../images/icon-actions.png)) button in the top\n    right corner of the page.\n \n3.  Select *Import*.\n\n4.  Drag-and-drop or click *Select* to upload the Fragments `.zip`.\n\n5.  Click *Import*\n\nThe Fragments are imported into the Collection. \n\nExporting and importing fragments is the preferred way to share code or bring it\ninto your Site. \n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/04-widget-pages/01-using-widget-pages intro.markdown",
    "content": "---\nheader-id: adding-applications-to-a-page\n---\n\n# Using Widget Pages\n\n[TOC levels=1-4]\n\nWidget Pages are composed of *widgets*. A widget is any application that you can\nadd to a page. Widget Pages are constructed by adding widgets to the page and\nfilling them with content.\n\nA widget could be a wiki display or a dynamic publishing tool like the Asset\nPublisher. The content you display with widgets could be long-form text or an\nimage gallery, or anything in between. In this section, you'll learn to create\nwidget pages and build content with them.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/04-widget-pages/02-creating-widget-pages.markdown",
    "content": "---\nheader-id: creating-widget-pages\n---\n\n# Creating Widget Pages\n\n[TOC levels=1-4]\n\nWidget Pages are the classic type of page in @product@. They're simple to create\nand fill with content or functionality. You create a blank page, define\na layout, and then add widgets to the layout. Widgets can display content or\nprovide some tool or function for Users.\n\n## Adding a Widget Page\n\nWhen you first start @product@ you get a widget page by default as your home\npage. To create a new widget page,\n\n1.  Go to *Site Administration* &rarr; *Site Builder* &rarr; *Pages*.\n\n2.  Click the *Add* icon (\n    ![Add](../../../../../images/icon-add.png)) in the top right and select \n    *Public Page* to add a new page.\n \n3.  Select *Basic Pages* if it is not selected by default.\n\n4.  Select the *Widget Page* type. \n\n5.  Name the page *Community* and click *Save*.\n \n6.  On the next screen, you can select a Layout Template or manage other \n    options. Leave the defaults and click *Save*.\n\n![Figure 1: Create a page called *Community* with two columns.](../../../../../images/creating-community-page.png)\n\nCreating a page by default also adds it to any Navigation Menus that are \nconfigured to have new pages added to them. If you don't want a new page added \nto a specific Navigation Menu that is listed during page creation, uncheck the \nbox for that menu.\n\nYour new page is now added to the navigation.\n\n1.  Click the logo in the top left of the page to go back to your Site's front\n    page. The page you just created appears in the main navigation.\n\n    ![Figure 2: Your page has been added to the navigation automatically.](../../../../../images/community-page-created.png)\n\n2.  Click on *Community* to go to the page.\n\nCurrently the page is empty. Next you'll add some widgets to give it\nfunctionality.\n\n## Adding Widgets to a Page\n\nTo add a widget to a page, go to the page and click the *Add* button \n(![Add](../../../../../images/icon-add-widget.png)) \nfrom the top menu and select the *Widgets* tab. You can either browse\nthrough the categories of available widgets until you find the one you want, or \nyou can search for widgets by name. Once you've found a widget, click the \n*Add* button to add it to the current page. Once there, you can drag it to a \nnew position. Alternatively, you can drag the widget directly from the Widgets \nmenu to a specific location on the page. Follow the steps below to add some\nCollaboration apps to the Lunar Resort Site.\n\n1.  From the top menu, select *Add* &rarr; *Widgets*.\n\n2.  In the menu that appears, expand the *Collaboration* category.\n\n3.  Drag the *Blogs Aggregator* widget onto the right column of your page.\n\n4.  Next, drag the *Wiki* app to the left column.\n\nSee how easy it is to add applications to your pages? You've added the Wiki\napp and Blogs Aggregator app to a page. \n\n![Figure 3: Your page layout options are virtually limitless with a slew of application and layout combinations.](../../../../../images/app-layout-design.png)\n\nIf the default layout options provided aren't enough, you can create your own.\nFor more information about developing custom layout templates, see the tutorial\n[Layout Templates with the Liferay Theme Generator](/docs/7-2/reference/-/knowledge_base/r/creating-layout-templates-with-the-themes-generator). \n\nNext, you'll look at creating reusable templates for widget pages.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/04-widget-pages/03-creating-widget-pages-from-templates.markdown",
    "content": "---\nheader-id: creating-widget-pages-from-templates\n---\n\n# Creating Widget Pages from Templates\n\n[TOC levels=1-4]\n\nPage templates provide pre-configured pages to reuse. There are two types of\npage templates in @product-ver@: Widget Page templates consist of a portlet\nlayout and configuration. Content Page templates are constructed from\nFragments. You can read about \n[Content Page Templates in this article](/docs/7-2/user/-/knowledge_base/u/building-content-pages).\n\nThree sample layout page templates are installed by default:\n\n- **Search:** Contains a search bar and configuration to display various facets.\n \n- **Wiki:** Provides a page with three applications related to authoring a wiki.\n\n- **Blog:** Provides a page with three applications related to blogging.\n\n![Figure 1: The Blog page template is already available for use along with the Search and Wiki page templates.](../../../../../images/default-page-templates.png)\n\nTo add a new widget page template,\n\n1.  Go to *Site Builder* &rarr; *Pages*.\n\n2.  Select the *Page Templates* tab.\n\n3.  Click *New* and create a collection named *Lunar Resort Templates*.\n\n4.  Click the *Add* icon (![Add Page Template](../../../../../images/icon-add.png))\n    and select *Widget Page Template*.\n\n5.  Enter a name.\n\n6.  Click *Save*.\n\nThe editing page for the template appears. You can add widgets to the page or\naccess page configuration now. The changes you make are instantly applied to\nthe template.\n\nIf you want to edit the template again, \n\n1.  Go back to the *Page Templates* tab.\n\n2.  Click the *Actions* icon (![Actions](../../../../../images/icon-actions.png)).\n\n3.  Click *Configure*.\n\nNote that after a new page template has been created, the default permissions\nonly allow the creator to use the page template. To give other users access to\nit,\n\n1.  Use the Actions menu for the template and select *Permissions*.\n\n2.  In the matrix of Roles and permissions, check the *View* permission for the \n    Roles needed to see the page template in the list of available page\n    templates when creating a new page.\n\nIf you want any user who can create a page to be able to use the page template, \ncheck the *View* permission for the *User* Role.\n\nTo use your template to create a new page,\n\n1.  Go to Site Administration and select the *Pages* option from the \n    *Site Builder* menu dropdown option.\n \n2.  Click the *Add* icon (![Add Page](../../../../../images/icon-add.png)).\n\n3.  Inside the Lunar Resort collection, select the page template that you \n    created.\n\n4.  Enter the name of your page and click *Add*.\n\nPages based on templates can inherit changes from the page template:\n\n![Figure 2: You can choose whether or not to inherit changes made to the page template.](../../../../../images/automatic-application-page-template-changes.png)\n\nBy default, when a Site administrator creates pages based on a page\ntemplate, future changes to the template are automatically propagated to\nthose pages. Site administrators can disable this behavior by disabling the\n*Inherit Changes* selector. Occasionally, propagation for page templates fails\ndue to unintended errors. To learn how to manage a failed page template\npropagation, visit the\n[Propagating Changes from Site Templates to Sites](/docs/7-2/user/-/knowledge_base/u/propagating-changes-from-site-templates-to-sites)\narticle.\n\nIf staging has been enabled, changes to the page template are automatically\npropagated to the staged page. These changes must still be approved before\nthe page is published to live. For this reason, the automatic propagation of\npage template changes to the staged page cannot be turned off and the *Inherit\nChanges* selector does not appear.\n\nYou can read more about staging in the\n[Stagingl](/docs/7-2/user/-/knowledge_base/u/staging) articles.\n\n## Sharing Widget Page Templates\n\n<!-- TODO: check this section. I could not find a way to import/export page\ntemplates. -->\n\nWhen importing pages to a new site or environment, you must also import\ntemplates associated with those pages. Generally templates are included\nautomatically when an associated page is exported, but if not you can export the\ntemplate collection separately so the page can be imported to the new\nenvironment. To export page templates, \n\n1.  Go to *Site Management* &rarr; *Site Builder* &rarr; *Pages*.\n\n2.  Select the *Page Templates* tab.\n\n3.  At the top right of the page, click *Options* \n    (![Options](../../../../../images/icon-options.png)) &rarr; \n    *Export/Import*.\n\n4.  On the *Export/Import* page you can choose to export configuration data and\n    change which Collections and templates are being exported.\n\n5.  When you're done configuring the export, click *Export* and save the \n    exported LAR file.\n\n6.  On the target environment, go to *Site Management* &rarr; *Site Builder*\n    &rarr; *Pages* and select *Page Templates*.\n\n7.  At the top right of the page, click *Options* \n    (![Options](../../../../../images/icon-options.png)) &rarr; \n    *Export/Import*.\n\n8.  Select the *Import* tab.\n\n9.  Upload the LAR with your template data. If the LAR contains additional\n    content you don't want to import, you can deselect it.\n\nOnce the template has been imported, the page can be imported normally to your\nnew environment. For more information on exporting/importing content, visit the\n[Importing/Exporting Sites and Content](/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content)\narticle. \n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/05-building-a-responsive-site/01-building-a-responsive-site-intro.markdown",
    "content": "---\nheader-id: building-a-responsive-site\n---\n\n# Building a Responsive Site\n\n[TOC levels=1-4]\n\nNow more than half of all page views in the world come from mobile devices \nlike phones and tablets. That means that if your pages don't look good on \nmobile devices, your pages don't look good for more than half the people\nlooking at them. @product@ can provide the best experience possible no matter\nwhat device you're using.\n\nMost of the heavy lifting for mobile optimization comes from your developers.\nThemes must be designed to be responsive. Web Content Structures and Templates\nand Page Fragments must be created with code that transitions gracefully to fit\nsmaller screens on mobile devices. But there's work to do for marketers and\nadministrators as well.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/05-building-a-responsive-site/02-built-in-mobile-support.markdown",
    "content": "---\nheader-id: built-in-mobile-support\n---\n\n# Built-in Mobile Support\n\n[TOC levels=1-4]\n\nOut of the box, there are several features that help make your pages look just\nas good and have the same functionality on mobile devices as they do on\na desktop:\n\n-  Liferay Widgets and custom widgets that use Liferay's UI frameworks \n   automatically scale to fit the screen size.\n\n    ![Figure 1: A widget adjusts its size.](../../../../../images/widget-adjustment.png)\n\n-  UI elements like the navigation and Product Menu automatically adjust to\n   remain usable on smaller screens.\n\n    ![Figure 2: The main navigation adjusts its size.](../../../../../images/navigation-adjustment.png)\n\n-  When the screen width is low, Liferay combines columns so that all \n   content remains legible.\n\n    <!--[Figure 3: Columns combine.](../../../../../images/columns-adjustment.png) -->\n    <!--TODO: Update image when there's more time. -->\n \n-  For web developers, Liferay's theme tools provide a number of tools to help\n   ensure optimum mobile performance.\n\nFor most business users, this means that all you need to do to display pages on \nMobile device is to create a page. However, you also have tools available to\nverify that everything displays as intended. The Device Simulator\n(![Simulation](../../../../../images/icon-simulation.png)) is\na powerful tool that shows you how pages look on different devices. \n\n## Using the Device Simulator\n\nWhen creating a page or reviewing a page before it is published, one of your \nmost important tools is the Device Simulator found in the top right corner of \nevery page. The simulator lets you view the current page in a number of \nresolutions based on different display types. There are three predefined \noptions:\n\n**Desktop:** Fixes the width to display the page at full size.\n\n**Tablet:** Puts your page in a box as if it is being displayed on a tablet. It\nalso activates some of Liferay's built-in mobile features.\n\n**Mobile:** Puts your page in an even smaller box to demonstrate how the page\nlooks to your average smartphone user.\n\n![Figure 3: The Simulation panel defines multiple screen sizes.](../../../../../images/device-simulation.png) \n\nThere are also two options available to display \n\n**AutoSize:** Provides another way to view the default behavior where the page\nshrinks and grows based on the width of the browser window.\n \n**Custom:** Lets you enter a specific size for testing and fixes the height and\nwidth of the display.\n\nBecause modern mobile browsers are built on the same technology as desktop\nbrowsers, the behavior you see in the simulator should match the experience of\nusers on mobile devices. In addition to making sure the basic layout looks good\nand that all functionality remains, it's also important to make sure that\nautomatic features---like how columns are combined at lower resolutions---don't\nhave unintended effects.\n\n## Designing Mobile Friendly Pages\n\n@product@ provides the tools you need, but building pages that provide a good \nexperience across all kinds of devices still means working across all levels of\nweb development and publishing. Theme developers must create themes that use\nLiferay's frameworks to scale content well across all kinds of displays.\nDesigners must have multiple screen sizes in mind when designing pages. And\nbefore anything it published it must be thoroughly reviewed to make sure that it\nprovides the best experience to all of your users.\n\nNow that you've learned about Liferay's tools for making your website mobile \nfriendly, let's look at your options for adapting to different types of mobile \ndevices.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/05-building-a-responsive-site/03-mobile-device-rules/01-mobile-device-rules-intro.markdown",
    "content": "---\nheader-id: mobile-device-rules\n---\n\n# Mobile Device Rules\n\n[TOC levels=1-4]\n\nWith mobile device rules, you can alter what gets displayed based on the device\nbeing used to access @product@. For instance, you can configure the look and\nfeel of pages accessed by mobile device users differently from those\naccessed by users on a computer.\n\nWhole Sites or individual pages can be configured for mobile device families.\nA family describes a group of devices. You can set rules that describe\na category of devices, such as all Android devices or all iOS devices. You can\ndefine as many rules in a family as you need to classify all the devices for\nwhich you'd like to define actions. Families can be prioritized to determine\nwhich one applies to a given page request.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/05-building-a-responsive-site/03-mobile-device-rules/02-creating-mobile-device-rules.markdown",
    "content": "---\nheader-id: creating-mobile-device-rules\n---\n\n# Creating Mobile Device Rules\n\n[TOC levels=1-4]\n\nTo configure mobile device rules, you need a way to find out the characteristics\nof the device. While some of the characteristics are provided by the device,\nmost are not. For this reason, there are databases that contain information\nabout thousands of devices. These databases make it possible to learn\ndevice details from the device type. @product@'s Mobile Device Rules can\nconnect to device databases so that you can use their device characteristics in\nyour rules. \n\n| **Important:** For the features described in this article to work, you must\n| install the [Liferay Mobile Device Detection Lite (LMDD)](https://web.liferay.com/marketplace/-/mp/application/92831494) app from Liferay Marketplace.\n| This app provides the device detection database that's required to detect which\n| mobile devices are accessing it. \n\nYou can develop plugins that integrate with other device databases. Even if you\ndon't have a device database, you can still set up mobile device rules. They\nwon't, however, be effective until a database is deployed, because the portal\nwon't have enough information about the devices being used to make page\nrequests.\n\n<!-- Uncomment when the referenced article is published. Jim\nTo learn how to tap into @product@'s Device API, see the \nUsing the Device Recognition API(/develop/tutorials/-/knowledge_base/7-1/using-the-device-recognition-api)\ntutorial.\n-->\n\nTo access the Mobile Device Families administrative page,\n\n1.  Open the *Product Menu*.\n\n2.  Use the *Site Selector* (![Site Selector](../../../../../../images/icon-compass.png))\n    to choose the Site that you want to define Mobile Device Rules for.\n \n3.  Select *Configuration* &rarr; *Mobile Device Families*.\n\nYou can also add families for all Sites by navigating to the Control Panel\n&rarr; *Sites* &rarr; *Sites* &rarr; *Global*. The Mobile Device Families\nadministrative page displays a list of defined families and lets you add more.\nTo add rules, you must first add a family.\n\n1.  Click *Add* button (![Add Family](../../../../../../images/icon-add.png)) to \n    add a *New Device Family*.\n\n2.  Enter a *Name* and *Description*.\n\n3.  Click *Save*.\n\n4.  Click on the name of the Mobile Device Family to access the rules page.\n\n![Figure 1: Create a Mobile Device Family so you can create rules.](../../../../../../images/mobile-device-families.png)\n\nThe rules defined for a family, along with the priorities of the families\nselected for a particular Site or page, determine which family's actions are\napplied to a given request. From the New Classification Rule page for a\nspecific rule set, you can add a rule by specifying an operating system, rule\ntype, physical screen size, and screen resolution. Remember that you can add as\nmany rules to a family as you need in order to classify the devices on which\nyou'd like to take actions.\n\n1.  Click on the *Add* button (![Add Classification Rule](../../../../../../images/icon-add.png)) to add a new rule.\n\n2.  Enter a *Name* and *Description*.\n\n3.  Select the classifications you want for this rule from *Operating System and\n    Type*, *Physical Screen Size*, and *Screen Resolution*.\n\n4.  Click *Save*.\n\nYou'll notice after saving the classification rule that it's characterized as a \n*Simple Rule*. Only Simple Rules are included with @product@, but the rules \nare designed to be extensible, and additional rule types can be added by your \ndevelopers.\n\n![Figure 2: Select the operating system and device type for your rule.](../../../../../../images/mobile-device-editing-rule.png)\n\nOnce you've created some mobile device families and added some rules to them,\nyou're ready to create some actions. The actions defined for a family determine\nwhat happens to a request when the device is detected and the family has been\nfound to apply.\n\nYou can add families to a Site, individual page, or page set from their\nrespective configuration pages. To do it for a Page Set:\n\n1.  Go to *Site Builder* &rarr; *Pages* in your Site.\n\n2.  Click on *Configure* \n    (![Configure](../../../../../../images/icon-gear.png)) for the Public \n    Pages.\n \n3.  Select the *Advanced* tab and open the *Mobile Device Rules* option in the\n    bottom menu.\n\n4.  Click *Select* to open the list of families that can be applied.\n\nFrom the same page, you can access the configuration for an individual page, or\nyou can configure Mobile Device Rules for an entire Site from *Configuration*\n&rarr; *Site Settings*. You can select multiple families for a particular Site\nor page and order them by priority. The families are checked in decreasing order\nof priority: the actions defined by the first family that applies are executed.\n\n![Figure 3: You can select a mobile device family to apply for a Site or page.](../../../../../../images/mobile-device-selection.png)\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/05-building-a-responsive-site/03-mobile-device-rules/03-mobile-device-actions.markdown",
    "content": "---\nheader-id: mobile-device-actions\n---\n\n# Mobile Device Actions\n\n[TOC levels=1-4]\n\nAfter you've created families and applied rules to define those families, you \ncan associate specific actions that occur when a user visits that Site on\na device.\n\nTo add actions to a selected rule group:\n\n1.  Click *Configure* (![Configure](../../../../../../images/icon-gear.png)) for the page, page set, or Site where you\n    have configured a device family. The configuration page appears. \n\n1.  Click the *Advanced* tab.\n\n1.  Decline using the same mobile device rules from the public pages by setting the option to *No*. The device family section appears.\n\n    ![Figure 1: Disable using public pages device rules.](../../../../../../images/configure-mobile-device-rule-for-page.png)\n\n1.  Click *Select* to open the Device Families page.\n\n2.  In the *Mobile Device Rules* section, click *Actions*\n    (![Actions](../../../../../../images/icon-actions.png)) &rarr; *Manage \n    Actions* next to the device family that you wish to add an action for.\n \n3.  Click *Add Action*.\n\n![Figure 2: Getting to the Manage Actions page.](../../../../../../images/manage-mobile-actions.png)\n\nBy default, there are four kinds of actions that can be configured for mobile\nfamilies:\n\n**Layout Template Modification:** Changes the way portlets are arranged on pages\ndelivered to mobile devices. For example, you could have pages with more complex\nlayouts automatically switch to a simpler template if it detects a mobile\ndevice---even if the resolution is theoretically high enough to support the\nstandard layout.\n\n**Theme Modification:** Selects a specific theme for different mobile device\nfamilies. You'd have to have a mobile version of your Site's theme that is\nautomatically applied when a device hits your page.\n\n**URL Redirect:** Sends mobile users to any URL. This can be used to direct\nmobile users to a mobile app download or a mobile version of the page.\n\n**Site Redirect:** Sends mobile users to a different Site on your portal. In\nsome cases, mobile content could be created on a mirror of your Site.\n\n| **Tip:** @product-ver@ was designed from the ground up to be responsive and\n| adapt to any device that might be accessing it. Before creating new themes or\n| forcing a layout template change, you should test how the Site behaves using\n| @product@ out of the box. Certain features, like URL Redirects, can be\n| disruptive and frustrating for users if used improperly.\n\nLike mobile device rules, mobile device actions are extensible. Your developers\ncan define custom actions in addition to the four actions provided by default.\n\nTo review, if you want to configure an action or actions that take place when\nmobile device requests are received, take the following steps:\n\n1.  Create a mobile device family to represent the group of devices for which to\n    define an action or actions.\n\n2.  Define one or more rules for your family that describe the group of devices\n    represented by your family.\n\n3.  Apply your family to an entire page set of a Site (all the public pages of a\n    Site or all the private pages) or to a single page.\n\n4.  Define one or more actions for your family that describe how requests should\n    be handled.\n\n## Mobile Device Rules Example\n\nNow you'll look at an example of using mobile device rules. Suppose you want to\ncreate a rule so that when a Site is accessed by an Android device,\na different layout is used. To set this up, you must follow the same four steps\ndescribed above.\n\nFirst create the Mobile Device Family:\n\n1.  Navigate to the *Mobile Device Families* page of *Site Administration*.\n\n2.  Click *Add Device Family* (![Add Device Family](../../../../../../images/icon-add.png)).\n\n3.  Enter *Android* for the *Name*.\n\n4.  Click *Save*.\n\nNext create a rule for the family:\n\n1.  From the *Mobile Device Families* page, click on *Android*.\n\n2.  Click *Add Classification Rule* (![Add Classification Rule](../../../../../../images/icon-add.png)).\n\n3.  Name the rule *Rule 1*.\n\n4.  Under *Operating System* select *Android* (you can hold\n    <CTRL> or <CMD> to select multiple items).\n\n5.  Under *Device Type* select *All*,\n\n6.  Click *Save*.\n\n    ![Figure 3: Create the Classification rule.](../../../../../../images/example-classification-rule.png)\n\nAs with the previous example, you only need one rule to describe your device\nfamily. Now you must apply the rule to some pages.\n\n1.  Go to *Site Builder* &rarr; *Pages* in Site Administration.\n\n2.  Click on the *Configure* icon for the *Public Pages*\n\n3.  Go to the *Advanced* tab.\n\n3.  Under *Mobile Device Rules*, select the *Android* device\n    family.\n\nNow you must define an action for your Android rule group to use\na different layout.\n\n1.  Click *Actions* &rarr; *Manage Actions* for the *Android* \n    rule.\n \n2.  Click *Add Action*.\n\n3.  Enter the name *Layout Template Modification*, and select the *Layout\n    Template Modification* action type.\n\n4.  Select the *1 Column* layout template. \n\n5.  Click *Save*.\n\n![Figure 4: Create the Actions for Android.](../../../../../../images/example-mobile-action.png)\n\nNow the Liferay Site's pages are presented to Android users with \nthe 1 Column layout template.\n\nMobile Device Rules are a powerful way to manage the way pages and content\nappear on the various devices that access your Site. But remember to consider\nthe power of modern devices and the experience of your users, and use this great\npower responsibly---to help users have a great experience on your website and to\nnot interrupt or negatively impact that experience on whatever device they're\nusing.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/03-adding-pages-to-sites/06-using-full-page-applications/01-using-full-page-applications-intro.markdown",
    "content": "---\nheader-id: using-full-page-applications\n---\n\n# Using Full Page Applications\n\n[TOC levels=1-4]\n\nFull Page Applications are the ideal way to display a Message Board, Wiki, or\nother application that demands a full page.\n\n## Configuring the Page\n\nCreating a Full Page Application starts just like creating any other type of \npage.\n\n1.  Go to *Site Administration* &rarr; *Site Builder* &rarr; *Pages*.\n\n2.  Click the (![Add Page](../../../../../images/icon-add.png)) icon.\n\n3.  Select the *Full Page Application* page type.\n\n4.  Give your page a *Name* and click *Save*.\n\n5.  Set the *Full Page Application* to *Wiki* and click *Save*.\n\n    ![Figure 1: The Full Page Application configuration page.](../../../../../images/full-page-app-configure.png)\n\n6.  Finish the page configuration and click *Save*.\n\n    Out of the box, you can set the Blogs, Wiki, Media Gallery, Message Boards,\n    RSS, Hello Soy Portlet, Documents and Media, Form, or Application\n    Authorization Request to be the sole application for the page. Developers\n    can make their applications Full Page Applications.\n \n7.  Click *Go to Site* in the Site Administration menu, and then click on your\n    page.\n\nNow the page is configured to display the Wiki and only the Wiki. No other \nwidgets can be added to the page, and the Wiki app cannot be removed.\n\n![Figure 2: The Wiki displayed as a Full Page Application.](../../../../../images/single-page-app-wiki.png)\n\nNote that all of the applications that can be added to the page are\nnon-instanceable and the content of whichever application you select is based on\nthe instance for that site. So if you already had data in your Wiki it appears\non this page.\n\nIf you want to configure the application to be scoped to this specific page, you\ncan configure that through the application's settings.\n\n1.  From the page, click the (![Options](../../../../../images/icon-options.png))\n    button for the Wiki and select *Configuration*.\n \n2.  From the *Wiki - Configuration* page, select the *Scope* tab.\n\n3.  Open the *Scope* menu and select *Space Wiki*.\n\n![Figure 3: Configuring the scope.](../../../../../images/configuring-scope.png)\n\nNow the Wiki is scoped for this page, and doesn't share data with the Site or\nglobally scoped Wiki.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/04-liferay-navigation/01-liferay-navigation-intro.markdown",
    "content": "---\nheader-id: managing-site-navigation\n---\n\n# Managing Site Navigation\n\n[TOC levels=1-4]\n\nLiferay provides powerful tools for creating and organizing pages. You can have\nanything from a simple, flat Site navigation to a complex hierarchy with tree of\nsub-pages nested down many levels.\n\n@product-ver@ lets you create Navigation Menus separate from your page\nhierarchy. Now you have the freedom to leave one-off marketing landing pages out\nof the navigation or create multiple navigation menus: a main menu, secondary\nmenus, footer menus, and custom menus for anything that you can dream up.\n\nMenus can differ by page: landing pages can show a simple list of frequently\nvisited pages, and the rest can appear in secondary navigation. You can also\ncreate specific menus for different landing pages to direct users to content\nthat is relevant to them.\n\nGo to *Site Builder* &rarr; *Pages* to view the existing pages or create new\npages. The Site hierarchy as displayed on *Pages* is the main reference for the\norganization of pages on that Site. While Navigation Menus can customize their\norganization and what appears and what doesn't appear, this menu is always the\nprimary reference for the pages on your Site.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/04-liferay-navigation/02-page-hierarchy.markdown",
    "content": "---\nheader-id: page-hierarchy\n---\n\n# Page Hierarchy\n\n[TOC levels=1-4]\n\nUsing the Page Hierarchy, you create public and private pages and organize\nthose pages in whatever order or structure that you see fit.\n\n## Creating a Page\n\nNew pages are created on the *Site Builder* &rarr; *Pages* page in Site \nAdministration. Pages can be created as *Public Pages* which anyone can view, \nor *Private Pages* which can only be viewed by Site Members. To create a new \npage,\n\n1.  Go to *Site Administration* for the Site you want to work on, then\n    *Site Builder* &rarr; *Pages*.\n\n    ![Figure 1: In the default site, initially only the *Home* and the hidden *Search* pages exist in the Public Pages Hierarchy.](../../../../images/default-nav-pages.png)\n\n2.  Click *Add* (![Add Page](../../../../images/icon-add.png)) and select\n    *Public Page*.\n\n3.  Select *Widget Page*.\n\n4.  Set the *Name* as *About Us*.\n\n5.  Click *Save*.\n\n![Figure 2: When you create a page, by default it is added to the site hierarchy.](../../../../images/page-hierarchy-menu.png)\n\nNow that the page is created, it appears in the hierarchy, and you can move or\norganize its position there.\n\n## Organizing Pages\n\nDrag and drop pages to reorder their position in the page hierarchy (and\nsubsequently the default navigation that users see), and to nest them as\nsubpages. The page at the top of the list is the *Home* page that users see\nautomatically when visiting your Site. \n\n1.  Click *Go to Site* to view the organization of the navigation menu. You can\n    see the order of the pages matches the order of the pages from Site\n    Administration.\n\n    ![Figure 3: You can see the order of pages in Site Administration vs. how they appear on the site.](../../../../images/navigation-practical1.png)\n\n2.  Drag the *About Us* page above the *Welcome* page in the list. It\n    automatically becomes the *Home* page.\n\n3.  Click *Go to Site* to see how this affects your menu.\n\n    ![Figure 4: *About Us* is now the home page, and *Welcome* is second in the nav.](../../../../images/navigation-practical2.png)\n\n4.  Drag *About Us* on top of *Welcome* to nest it. \n\n5.  Click *Go to Site* one more time to see how nested pages appear.\n\n![Figure 5: *About Us* is now nested under *Welcome* and appear when you mouse-over *Welcome*.](../../../../images/navigation-practical3.png)\n\nAs you've just demonstrated, organizing pages in the default menu is simple, \nbut very powerful.\n\n### Public and Private Pages\n\nAs noted above, Private Pages work just like Public Pages, except they can be\nviewed only by registered members of a Site. In the default configuration,\nPublic Pages are at the URL `[web-address]/`**web**`/[site-name]` while Private\nPages are at `[web-address]/`**group**`/[site-name]`. Other than the membership\ndistinctions, Public and Private Pages share the same behavior.\n\n## Page Options\n\nWhile managing the default menu, you can also access page options. Clicking on\nthe *Options* icon [Option](../../../../images/icon-options.png) \naccesses several configuration tools:\n\n**View** goes to the selected page on the Site.\n\n**Configure** goes to page configuration.\n\n**Copy Page** creates a new page in the current Site that duplicates the\nselected page.\n\n**Permissions** opens the Permissions dialog.\n\n**Orphan Widgets** clears data related to widgets that have been removed from\nthe page.\n \n**Delete** deletes the page and all its data.\n\nCreating and managing pages using the page hierarchy is simple but very \npowerful. If you need more navigation options, however, Navigation\nMenus provide more flexibility.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/04-liferay-navigation/03-creating-menus.markdown",
    "content": "---\nheader-id: creating-and-managing-navigation-menus\n---\n\n# Creating and Managing Navigation Menus\n\n[TOC levels=1-4]\n\nTo better understand Navigation Menus, it's time to create a new menu.\n\n## Creating a Navigation Menu\n\n1.  Go to *Site Administration* &rarr; *Site Builder* &rarr; *Navigation Menus*.\n\n2.  Click the *Add* button (![Add Page](../../../../images/icon-add.png)) to add\n    a new menu.\n\n3.  Give your menu a name and click *Save*.\n\nOn the next page appears a number of elements that you can add to a menu.\n\n**Page**: Select an existing page from the current Site to add to the navigation\nmenu.\n\n**Submenu**: Create a second level of menu navigation.\n\n**URL**: Create a link to any page anywhere by providing a URL. The link appears\njust like any other option in your menu.\n \nClick on *Page* and you see a view of all the current pages on the current Site.\nSelect a page and click *Add* to add that page to the menu.\n\n| **Note:** When you click on a page, you select that page. Multiple pages can be\n| selected by clicking on each page one at a time. To deselect a page, click on\n| the page again.\n\nNow you see the menu management screen. From here, you can drag and drop menu\nelements to rearrange or nest them. You can also manage options for this menu by\nclicking the gear icon in the top right. Let's add another item to the menu.\n\n1.  Click the `+` icon.\n\n2.  Select *Submenu* in the menu that pops up.\n\n3.  Name your menu *External Links*.\n\n4.  Click *Add*.\n\nClick the `+` button again and select *URL*. You're prompted to enter a page\nname and URL.\n\n1.  Enter a *Name*.\n\n2.  Enter the *URL* for an external Site.\n\n3.  Click *Add*.\n\nDrag the URL item onto the *External Links* submenu. This nests the URL item in\nthe submenu.\n\n![Figure 1: Menus can have a standard page, a submenu, and a URL link in the submenu.](../../../../images/basic-nav-menu.png)\n\nNow that you can see how menus work, you can learn the details.\n\n## Managing Menus\n\nAfter you create a menu, more configuration options appear on the main\nNavigation Menus page. \n\n**Title**: Your menu's name. \n\n**Add New Pages**: Determines if new pages added to the main navigation are\nadded to the menu automatically. \n\n**Marked As**: Can be set as *Primary Navigation*, *Secondary Navigation*, or\n*Social Navigation*.\n \n**Author**: The user that created the menu.\n\n**Create Date**: When the menu was created.\n\nTheme and Fragment developers primarily use the menu types to determine how\na menu should be styled. **Primary Navigation** is the main navigation for\na page. **Secondary Navigation** is a second level of navigation, possibly\na sidebar or a separate menu within a page. **Social Navigation** is for menus\nthat contain links for sharing content on social media or similar tasks.\n\n### Modifying Menus\n\nNext click on the options menu at the far right of your new navigation menu: \n\n![Figure 2: Menus with a standard page, a submenu, and a URL link in the submenu are created for different reasons.](../../../../images/nav-menu-options.png)\n\n**Edit**: Add, remove, or organize menu items.\n\n**Rename**: Change the name of your menu.\n\n**Permissions**: Define who can view, update, delete, and manage the permissions\nfor the menu.\n \n**Mark As**: Change the menu type.\n\n**Delete**: Deletes the menu.\n\nNext you'll learn about the tools for displaying menus on pages.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/04-liferay-navigation/04-displaying-navigation-menus.markdown",
    "content": "---\nheader-id: displaying-navigation-menus\n---\n\n# Displaying Navigation Menus\n\n[TOC levels=1-4]\n\nYou can display Navigation Menus in different ways on your Site. You may want\nto configure different display styles for a main menu, sidebar, and footer menu\nall on one page.\n\n## Navigation Menu Widget\n\nThe Navigation Menu widget lets you add navigation wherever you need it. You\ncan place the widget on a page and then select a menu and style for the menu\nyou are displaying.\n\n1.  Go to a Widget Page, open the Add menu on the right side of the page and \n    add the *Content Management* &rarr; *Navigation Menu* to the page.\n\n2.  Open the *Configuration* menu.\n\nFrom here you can configure three main categories: \n\n- The Navigation Menu to be displayed\n\n- The styling of the menu\n\n- What level of navigation to display\n\n![Figure 1: Configuring the Navigation Menu Widget.](../../../../images/nav-widget-configuration.png)\n\n### Choosing a Navigation Menu\n\nThe Navigation Menu Widget has two ways to select a menu. You can choose to \n*Select Navigation* or *Choose Menu*.\n\n**Select Navigation:** Select from the three main menu types: *Primary\nNavigation*, *Secondary Navigation*, and *Social Navigation*.\n\n**Choose Menu:** Choose any menu that was created for that Site.\n\nOnce you select a menu, you must choose how to display it.\n\n### Display Template\n\nThe *Display Template* option lets you select an\n[Widget Template](/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates)\nfor Navigation Menus. There ten included by default:\n\n**List Menu:** Displays all the items in a vertical list.\n\n**Pills Horizontal:** Displays the items horizontally and uses a button style\nfor highlighting.\n\n**Pills Justified:** Like Pills Horizontal, but pads the items to fill out the\nhorizontal space.\n\n**Pills Stacked:** A vertical version of the pills style.\n\n**Tabs:** Displays the items like navigation tabs.\n\n**Tabs Justified:** Navigation tabs that fill horizontal space.\n\n**Bar Minimally Styled:** A lightweight version of the default display that you\nsee in the embedded menu on your page.\n\n**Bar Minimally Justified Styled:** Like Bar Minimally Styled with horizontal\npadding.\n\n**Bar Default Styled:** The default embedded menu.\n\n**Split Button Dropdowns:** Displays each item as a button with a dropdown \nfor multiple navigation levels.\n\nYou can also add your own custom templates.\n\n<!-- TODO: Provide link for developing widget templates when available.-->\n\n### Menu Items to Show\n\n*Menu Items to Show* configures which pages at what level from the menu are\ndisplayed in the widget. You can choose the starting level, how many levels deep\nto display, and how to display sub-levels.\n\n**Start with Menu Items In:** Choose to start at a specific level of the\nnavigation or a level relative to the current level (above or below).\n\n**Sublevels to Display:** Select the number of levels to display in the\nnavigation, from **1** down to **Infinite**.\n\n**Expand Sublevels:** Choose if hovering your mouse over the navigation reveals\nnavigation levels one at a time automatically or reveal all the levels at\nonce.\n \nNow you can see how there are a variety of customizations and configurations\navailable for navigation menus that you can implement for your Site.\n\n![Figure 2: Navigation menus give you many ways to help users navigate your Site.](../../../../images/navigation-menu-examples.png)\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/05-building-sites-with-templates/01-building-sites-from-templates-intro.markdown",
    "content": "---\nheader-id: building-sites-from-templates\n---\n\n# Building Sites from Templates\n\n[TOC levels=1-4]\n\nSite Templates create a single Site structure that can be used for any new Site.\nThey are created and administered from the Control Panel. In addition to\ncreating multiple Sites with the same design, you can also use them to manage\nchanges across multiple Sites with propagation of changes. Site templates can\ncontain multiple pages, each with its own theme, layout template, applications,\nand app configurations.\n\nSite templates can also contain content just like actual Sites. This allows \nadministrators to use Site Templates to create new Sites that are each created \nwith the same default pages, applications, and content. After they've been \ncreated, these Sites and their pages can be modified by Site administrators. \nUsing Site templates can save Site administrators a lot of work even if each \nSite that was created from a given Site Template ends up being very different.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/05-building-sites-with-templates/02-creating-a-site-template.markdown",
    "content": "---\nheader-id: creating-a-site-template\n---\n\n# Creating a Site Template\n\n[TOC levels=1-4]\n\nSuppose you need to create the following three private Sites for the Lunar\nResort's internal use: Engineering, Marketing, and Legal. These should be\naccessible only to members of these respective departments. You could design\neach Site separately, but you can save yourself time if you create a Site\ntemplate instead.\n\n1.  Go to the Control Panel and click *Sites* &rarr; *Site Templates*. \n\n2.  Click the *Add* icon (![Add Site Template](../../../../images/icon-add.png)) \n    and enter the name *Department* for your template.\n\n3.  Leave the *Active* and *Allow Site administrators to modify pages\n    associated with this Site template...* boxes checked. \n\n4.  Click *Save* to create your Site template.\n\nThe *Active* box must be checked for your template to be usable. If your \ntemplate is still a work in progress, uncheck it to ensure that no one uses it\nuntil it's ready. Checking *Allow Site administrators to modify pages associated\nwith this Site template...* allows Site administrators to modify or remove the\npages and apps that the template introduces to their Sites---if you want the\ntemplates to be completely static, you should uncheck this. \n\nNow it's time to edit your Site template. This example, includes four pages.\n\n| **Note:** This section assumes knowledge of @product@ 7.2 page management. For\n| more information on how to create and manage pages in @product@ 7.2, see the\n| [Adding Pages to Sites article](/docs/7-2/user/-/knowledge_base/u/creating-and-managing-pages).\n\n1.  Click the *Options* icon (![Options](../../../../images/icon-options.png)) \n    and select *Manage*.\n \n    This brings you to the *Pages* page for the Site Template. You already have\n    a home page. Create three more pages.\n\n2.  Create a *Full Page Application* page named *Documents*.\n\n3.  Click *Options* (![Actions](../../../../images/icon-actions.png)) \n    &rarr; *Configure* and set the *Full Page Application* to *Documents and\n    Media*. \n\n4.  Create a page using the Global Page Template *Wiki* and name it *Wiki*.\n\n5.  Create a widget page named *Message Boards*.\n\n6.  Click *Go to Site* in the menu to the left to go to the pages you just \n    created.\n\n7.  On the *Home* page add the Activities, Announcements, and Calendar apps.\n\n8.  On the *Message Boards* page add the Message Boards and Tag Cloud apps.\n\nThe changes you made to your Site template above are completed in real time, so \nthere's no *Save* button.\n\n![Figure 1: You can see the name of the Site template you're currently editing.](../../../../images/editing-site-template.png)\n\nNext, you'll use your Site template to create the Engineering, Marketing and\nLegal Sites.\n\n1.  Go to the Control Panel and click on *Sites* &rarr; *Sites*.\n\n2.  Click the *Add* icon (![Add Site](../../../../images/icon-add.png)) &rarr;\n    *Department*.\n \n3.  Enter *Engineering* for the Site name.\n\n4.  Check the *Create default pages as private (available only to members).\n    If unchecked, they will be public (available to anyone)* option since the\n    Engineering Site is intended for internal use only.\n\n5.  Click *Save*.\n \n6.  In the next section, set the Membership Type to *Private*.\n \n    Recall that private Sites don't appear in the My Sites \n    application so that regular users won't even know that the Engineering Site \n    exists. Also, the only way users can be added to a private Site is via an \n    invitation from a Site administrator.\n \n7.  Leave the *Active* selector enabled so that your Site can be used \n    immediately.\n \n8.  Check the *Create default pages as private (available only to members). If \n    unchecked, they will be public (available to anyone).* option since the \n    Engineering Site is intended for internal use only.\n\n9.  Leave the *Enable propagation of changes from the Site template* box \n    enabled so that the Engineering Site receives updates if the Department \n    Site template is modified.\n\n10.  Click *Save* to create your Engineering Site.\n\n11.  Repeat these steps to create the Marketing and Legal Sites.\n\nThe new Sites have all the pages and apps you created in the Site template. To \nview the pages of the new Sites, click *Sites* &rarr; *Sites* in the Control \nPanel and then click *Actions* &rarr; *Go to Private Pages* next to one of \nyour new Sites. \n\nUsing Site templates streamlines the Site creation process for administrators,\nmaking it easy to create Sites quickly. Now each department of the Lunar Resort\nhas its own Calendar, Documents and Media Library, Wiki, and Message Boards on\ntheir Sites. Although the pages and apps of each department's Site are the same,\neach Site will quickly be filled with department-specific information as users\nadd and share content within the Sites. Also, Site administrators can add new\npages, apps, and content to their Sites, further differentiating each\ndepartment's Site from the others.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/05-building-sites-with-templates/03-managing-site-templates.markdown",
    "content": "---\nheader-id: managing-site-templates\n---\n\n# Managing Site Templates\n\n[TOC levels=1-4]\n\nTo get started, click on *Site Templates* in the Sites section of the Control\nPanel. Here, you can add, manage, or delete Site templates. You can also\nconfigure the permissions of Site templates. As long as a Site is linked to the\nSite template it was created from, changes to the Site template's pages, apps,\nand app configurations are propagated to the Site. Changes to a Site template's\ncontent, however, are not propagated to existing Sites that are linked to the\nSite template. You'll learn about the propagation of changes between Site\ntemplates and Sites in more detail in the section on Site template use cases\nbelow.\n\nTo manage a Site Template's pages,\n\n1.  Click on *Site Templates* in the Control Panel. \n\n2.  Select the *Actions* icon (![Actions](../../../../images/icon-actions.png)) \n    and then *Manage* for an existing template.\n \nIf you open the main Menu on the left side of your screen (if necessary), the\nSite Template is selected in the Site Administration dropdown menu. You're\nprovided similar options as a regular Site, including *Build*, *Content*,\n*Configuration*, and *Publishing*. By default, the Manage Interface opens\n*Build* &rarr; *Pages*. From here, you can add or remove pages from a Site\nTemplate or select themes and layout templates to apply to the Site Template.\nYou can also configure each page to have any theme, any layout template, and any\nnumber of applications, just like a page of a regular Site. As with Site pages,\nyou can organize a Site Template's pages into hierarchies. When you create\na Site using a Site template, the configuration of pages and apps is copied from\nthe template to the Site. By default, all changes made to the Site template are\nautomatically copied to Sites based on that template.\n\n| **Tip:** If you want to publish a piece of web content to many Sites and ensure\n| modifications are applied to all, don't use Site template content for that\n| purpose. Instead, place the content in the global scope and then reference it\n| from a *Web Content Display* application in each Site.\n\nThe Content section offers separate repositories for content related apps\nbased on your Site Template. For instance, by clicking *Polls* from the Content\nsection, you can create a poll question that is only available for that specific\nSite template. Assets created within your template's Content section can\nonly be accessed by Sites using the template.\n\nThe Configuration section includes Widget Templates and\nMobile Device configuration options for your Site Template. Also, nested in the\nConfiguration section is the *Site Template Settings*. This edits the template's\nname and description while also offering boolean options for activating your\nSite template and allowing Site administrators to modify pages associated with\nyour template.\n\nThe following figure displays the form shown when editing the *Department*\ntemplate's settings:\n\n![Figure 1: Site templates have several configurable options including the option to allow Site administrators to modify pages associated with the Site template.](../../../../images/site-template-settings.png)\n\nBy default, the following Site templates are provided:\n\n- **Intranet Site:** Provides a preconfigured Site for an intranet. The Home\n  page displays the activities of the members of the Site, search, a language\n  selector, and a list of the recent content created in the intranet. It also\n  provides two additional pages for Documents and Media and external News\n  obtained through public feeds.\n\n- **Community Site:** Provides a preconfigured Site for building online\n  communities. The Home page of a *community Site* provides message boards,\n  search, a display of a poll and statistics of the activity of community\n  members. The Site will also be created with a page for a wiki.\n\nNow that you know the basics for creating and managing your Site templates,\nyou can learn about propagating changes next.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/05-building-sites-with-templates/04-propagating-changes-from-site-templates.markdown",
    "content": "---\nheader-id: propagating-changes-from-site-templates-to-sites\n---\n\n# Propagating Changes from Site Templates to Sites\n\n[TOC levels=1-4]\n\nSite Template administrators can add, update, or delete Site Template pages.\nChanges made to a Site Template can be propagated to Sites whose page sets are\nlinked to the Site Template. When you create a Site based on a Site Template\nwith the *Enable propagation of changes from the Site template* box checked this\nlink is created. To configure propagation of changes:\n\n1.  Select the Site from the Sites dropdown in the Menu by selecting the\n    *Site Selector* button (![Compass](../../../../images/icon-compass.png)). \n \n2.  Navigate to the *Configuration* &rarr; *Settings* page and uncheck or \n    recheck the *Enable propagation of changes from the Site template* checkbox.\n \nIn this section, you'll learn about the propagation of changes from Site \ntemplates to Sites and discuss the options available to Site administrators and \nSite template administrators.\n\n## Site Template Page Behavior\n\nIf a Site's page set has been created from a Site template and the propagation\nof changes from the Site template is enabled, Site administrators can add new\npages but cannot remove or reorder the pages imported from the Site Template. If\na Site has both pages imported from a Site template and custom Site pages, the\nSite Template pages always appear first in the Site page hierarchy; custom pages\nadded by Site administrators appear after the Site template pages. Only Site\ntemplate administrators can remove, reorder, or add Site template pages. Site\nadministrators can add or remove custom Site pages. They can also reorder custom\nSite pages as long as they're all positioned after the Site template pages. Site\ntemplate administrators cannot add, remove, or reorder custom Site pages.\n\n| **Note:** Pages containing a fragment (e.g., \n| [Content Pages](/docs/7-2/user/-/knowledge_base/u/creating-content-pages))\n| cannot propagate changes after a Site is *first* created based on a site\n| template.\n\nIf a Site administrator changes a page that was imported from a Site Template\nand refreshes the page, the following Information icon\n(![Information](../../../../images/icon-control-menu-information.png)) appears \nin the Control Menu with the following message:\n\n    This page has been changed since the last update from the Site template. No\n    further updates from the Site template will be applied.\n\n![Figure 1: You can click the Information icon to view important information about your Site template.](../../../../images/site-template-update-message.png)\n\n## Merging and Resetting Changes\n\nIf the Site administrator clicks the *Reset Changes* button, changes are\npropagated from the Site template page to the corresponding Site page that was\nimported from the Site template. Clicking the *Reset Changes* button makes two\nkinds of updates to a page. First, changes made by Site administrators to the\nSite page are undone. Second, changes made by Site template administrators to\nthe Site template page are applied to the Site page. Note: clicking the *Reset\nChanges* button only resets one page. If multiple Site pages have been modified\nand you'd like to re-apply the Site template pages to them, you'll need to click\nthe *Reset Changes* button for each page.\n\nSite template administrators can set preferences for apps on Site template\npages. When a Liferay administrator creates a Site from a Site template, the app\npreferences are copied from the Site template's apps, overriding any default app\npreferences. When merging Site template and Site changes (e.g., when resetting),\napp preferences are copied from Site template apps to Site apps. Only global app\npreferences or local app preferences which don't refer to IDs are overwritten.\n\nIn some cases, merging Site template and Site changes fails. For example, if\npages from a Site template cannot be propagated because their friendly URLs are\nin conflict, @product@ could try to continuously merge the Site changes. Instead\nof entering into an infinite loop of merge fails, @product@ stops the merge\nafter several unsuccessful attempts. @product@, however, doesn't stop there:\nyour merge is temporarily paused, you're given an indication of the current\nmerge fail, and then you have the opportunity to fix your merge conflicts. After\nyou've squared away your conflict, navigate to your Site's *Site Administration*\n&rarr; *Configuration* &rarr; *Site Settings* and click the *Reset and\nPropagate* button.\n\n![Figure 2: This type of warning is given when there are friendly URL conflicts with Site template pages.](../../../../images/friendly-url-propagation-failure.png)\n\nThe *Reset and Propagate* button resets the merge fail count and attempts to\npropagate your Site changes again. This process gives you the opportunity to\ndetect and fix a merge fail when problems arise. This helpful process can also\nbe done with page template merges, which follows similar steps.\n\nSite administrators can also add data to Site template applications. For\nexample, Site template administrators can add the Wiki app to a Site template\npage and use the Wiki to create lots of articles. When a Liferay administrator\ncreates a Site from a Site template, data is copied from the Site template's\napps to the Site's apps. The preferences of the Site's apps are updated with the\nIDs of the copied data. For example, if a Site is created from a Site template\nthat has a Wiki app with lots of wiki articles, the wiki articles are copied\nfrom the Site template's scope to the Site's scope and the Site's Wiki app is\nupdated with the IDs of the copied wiki articles.\n\n| **Important:** App data, fragment-based pages, related resources, and\n| permissions on resources are only copied from a Site template to a Site when\n| that Site is *first* created based on the template. No changes made to these\n| entities are propagated to the Site after the Site is created. Neither are\n| such changes propagated to a Site by the *Reset* or *Reset and Propagate*\n| features.\n\nFor example, consider a Site template administrator who includes a Message\nBoards app as part of a Site template. They even create Message Board\ncategories and configures permissions over the actions of the categories. The\nfirst time a Site is created based on the Site template, the categories (app\ndata) and related permissions are copied to the Site. If the Site template\nadministrator adds, removes, or deletes some categories, however, such changes\n*aren't* propagated to the Site. \n\nNow that you've learned how Site templates work, you'll learn how to share Site\ntemplates. \n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/05-building-sites-with-templates/05-sharing-site-templates.markdown",
    "content": "---\nheader-id: sharing-site-templates\n---\n\n# Sharing Site Templates\n\n[TOC levels=1-4]\n\nIf you want to export a Site that uses Site or Page Templates to a different\nenvironment (through a LAR file or remote publication), the templates must be\nexported and imported manually in advance or the import fails.\n\nTo export a Site using a Site Template, use the following process:\n\n1.  Go to the *Control Panel* &rarr; *Sites* &rarr; *Site Templates* menu.\n\n2.  Click the *Actions* icon (![Actions](../../../../images/icon-actions.png))\n    and then *Manage* for the Site template you want to export.\n \n3.  Open the Site Template's management section and click on *Publishing* \n    &rarr; *Export*.\n\n4.  Click the *Add* icon (![Add](../../../../images/icon-add.png)) to create\n    a new Custom Export.\n \n5.  Select the content and pages you want to export and click *Export*.\n\n6.  Click on the *Download* icon for the template that you exported.\n\n7.  In your target environment, go to *Control Panel* &rarr; *Sites* &rarr; \n    *Site Templates* and create a new Site template.\n\n8.  Click *Actions* &rarr; *Import* for that Site template and upload the LAR\n    file containing your Site template's content.\n\nNow the template can be used normally in the new environment. For more \ninformation on exporting/importing content, visit the [Importing/Exporting Pages and Content](/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content) article.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/06-configuring-sites/01-configuring-sites-and-pages-intro.markdown",
    "content": "---\nheader-id: configuring-sites\n---\n\n# Configuring Sites\n\n[TOC levels=1-4]\n\nJust like there's more than one way to cook and an egg --- or eat an egg for \nthat matter --- there's more than one way to build a site. Liferay is created \nwith that in mind, and beyond just creating content and pages, Liferay has a \nwealth of configuration options and tools available to help you create the site \nthat meets your needs and the needs of your users.\n\nIn this section, you'll explore all of Liferay's options for configuring sites, \nand gain a deeper understanding of why you might want to use various options \nand configurations.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/06-configuring-sites/02-configuring-site-settings.markdown",
    "content": "---\nheader-id: configuring-site-settings\n---\n\n# Configuring Site Settings\n\n[TOC levels=1-4]\n\nGeneral settings range from core configuration, like your Site's Membership\nType, to finer details like Documents and Media indexing options.\n\n## Details\n\n*Details* provides the same menu you filled out when first creating your Site.\nThis allows an administrator to change the description and membership type of\na Site.\n\n### Membership Options\n\nThe membership type can be set as open, restricted, or private based on the\nprivacy needs of the Site. Users can join and leave an open Site at will. To\njoin a restricted Site, users must be added by the Site administrator, but they\ncan request membership through the Sites section of the Control Panel.\nA private Site works like a restricted Site but is hidden from users who aren't\nmembers.\n\n### Site Hierarchies\n\nSites can be organized into hierarchies. At the bottom of the Details\nsub-section is the Parent Site section. When you select the parent Site for the\nSite you're currently on, a checkbox appears for limiting membership to members\nof the parent Site.\n\n## Pages\n\nUnder Pages you can view your Site's Public or Private Pates, if any exist. If\nthey don't exist, a *Site Templates* selector appears for creating pages with\na Site Template.\n\n![Figure 1: Selecting a Site Template.](../../../../images/selecting-site-template.png)\n\n## Categorization\n\n*Categorization* helps administrators organize the Site and allows for users to \neasily find your Site and its content through search and navigation. For more \ninformation on using tags and categories, visit the\n[Organizing Content with Tags and Categories](/docs/7-2/user/-/knowledge_base/u/organizing-content-with-tags-and-categories) section.\n\n## Site URL\n\nThe *Friendly URL* option lets you set your Site's URL paths. Friendly URLs are\nused for both public and private pages. The public Site base URL is\n`https://localhost:8080/web`, and the private one is\n`https://localhost:8080/group`. Each friendly URL must be unique.\n\nFor example, setting the friendly URL of your default Site to `/lunar-resort`\nmakes your Site's public home page's URL\n`https://localhost:8080/web/lunar-resort/home`. The private Site's URL is thus\n`https://localhost:8080/group/lunar-resort/home`.\n\nNote that if you add a friendly URL for your instance's home page, you\nshould update your instance's Home URL field so that page requests to\n`http://localhost:8080` redirect properly: \n\n1.  Go to *Configuration* &rarr; *Instance Settings* &rarr; *Instance Configuration* &rarr; *General* in the Control Panel.\n\n2.  Under *Navigation*, in the *Home URL* field enter your home URL (i.e.\n    */web/lunar-resort/home*).\n\n<!-- TODO: Check for Navigation tab. -->\n\nOnce you've entered this setting, page requests to `localhost:8080` redirect\nto the friendly URL of your Liferay instance's new home page.\n\nYou can also configure Virtual Hosts, which connects a domain name to a Site, under *Site URL*. You can use this to define a domain name (i.e., \n`www.lunar-resort.com`) for your Site. This can be a full domain or a \nsubdomain. You can use this to host a number of web sites as separate Sites on \none Liferay server.\n\n![Figure 2: When configuring virtual hosts, the public and private pages of a site can be configured to different domains.](../../../../images/settting-virtual-hosts.png)\n\nFor instance, if you set this up for the Lunar Resort's development network,\nusers in that Site would access `developers.lunar-resort.com`,\nprovided that the Lunar Resort instance's network administrators created the\ndomain name and pointed it to the Liferay server.\n\n1.  With your provider, set the DNS name *developers.lunar-resort.com* to point \n    to your Liferay instance's IP address.\n\n2.  In the Virtual Host tab for the Developers Site, set the URL to\n    *http://developers.lunar-resort.com*\n\nThis helps users quickly access their Site without having to recall an extended \nURL. The *Site URL* option is listed under the General tab.\n\n## Documents and Media\n\nBelow that is *Documents and Media*, which lets you enable/disable\nDirectory Indexing. If on, Site administrators can browse your Site's\nDocuments and Media files and folders. For example, a Site administrator of\na Site called *Lunar Resort* can browse documents at\n`http://localhost:8080/documents/lunar-resort` if this option is enabled.\n\n## Site Template\n\nIf you created your Site using a Site Template, this section appears and\ndisplays information about the link between the Site Template and the Site.\nSpecifically, you can see which Site Template was used and whether or not it\nallows modifications to the pages inherited from it by Site administrators. To\nlearn more about Site Templates and how to create your own, see \n[Building Sites from Templates](/docs/7-2/user/-/knowledge_base/u/building-sites-from-templates).\n\n## Asset Auto Tagging\n\n*Asset Auto Tagging* lets you enable or disable the use of any Asset Auto \nTagging rules on your site. See [Asset Auto Tagging](link) to learn more about setting up auto tagging features.\n\n## Custom Fields\n\n*Custom Fields* only appears if you've created them in Control Panel &rarr;\n*Configuration* &rarr; *Custom Fields*. For more information on Custom Fields,\nsee \n[Custom Fields](/docs/7-2/user/-/knowledge_base/u/setting-up).\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/06-configuring-sites/03-site-social-settings.markdown",
    "content": "---\nheader-id: social-settings-and-languages\n---\n\n# Social Settings and Languages\n\n[TOC levels=1-4]\n\nThe Social tab provides options for managing the social interactions on your\nSite. Languages lets you configure language options and change the default\nlanguage options for the Site.\n\n## Ratings\n\nThe *Ratings* option lets you select the ratings type to use for applications\nlike Documents and Media, Web Content, Comments, etc. Ratings types include\nStars, Likes, and Thumbs. \n\n## Mentions\n\nAt the bottom of the page is *Mentions*, which lets you enable/disable\nMentioning functionality, which is used to *mention* (notify and/or draw\nattention to) friends and colleagues by entering the \"@\" character followed by\ntheir user names. See the \n[Mentioning Users](/docs/7-2/user/-/knowledge_base/u/mentioning-users) article\nfor more information.\n\n## Languages\n\nThe *Languages* tab lets you configure the language options for your Site.\n\n![Figure 1: In the Languages tab, you can configure the site to use the instance's default language or another supported language.](../../../../images/site-language.png)\n\nYou can use the default language or define another supported language as the\ndefault for your Site. \n\n\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/06-configuring-sites/04-advanced-site-settings.markdown",
    "content": "---\nheader-id: advanced-site-settings\n---\n\n# Advanced Site Settings\n\n[TOC levels=1-4]\n\nAdvanced Settings relate to security (like User Roles) or require external\nconfiguration (like creating a Google Analytics account) to use.\n\n### Default User Associations\n\n*Default User Associations* configures Site roles and teams that newly assigned Site \nmembers have by default. If you'd like to learn more about creating roles \nand/or teams, visit the\n[Roles and Permissions](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions)\nand\n[Creating Teams for Advanced Site Membership Management](/docs/7-2/user/-/knowledge_base/u/creating-teams-for-advanced-site-membership-management).\nsections.\n\n## Analytics\n\n@product@ includes built-in support for Google Analytics for analyzing \ntraffic on your Site. Google Analytics provides a snippet of code which you add\nto your pages enable tracking. Adding this code to every page on a Site would\nbe tedious, especially if it's a large Site with a lot of user-generated\ncontent.\n\nThere are two ways to mitigate this problem:\n\n1.  A web developer can hard-code the tracking code into a theme, which embeds\n    it on every page.\n\n2.  An administrator can enter the tracking code in Site settings.\n\nTo use option #2:\n\n1.  Go to *Configuration* &rarr; *Settings* &rarr; *Advanced*.\n\n2.  Expand the *Analytics* section.\n\n3.  Enter your Google Analytics ID.\n\n4.  Click *Save*.\n\n    All the pages in the Site you selected now have the Google Analytics code\n    and can be tracked. \n\n![Figure 1: To set up Google Analytics: sign up, receive an ID, and then enter it into the Google Analytics ID field.](../../../../images/maintaining-google-analytics.png)\n\nTo enable a different analytics service:\n\n1.  Go to *Configuration* in the Control Panel.\n\n2.  Go to *Instance Settings* &rarr; *Analytics*.\n\n3.  Enter the name of any additional service you want to add in the *Analytics*\n    field provided.\n\n4.  Once you have entered the name, go to the *Site Settings* &rarr; *Advanced* \n    &rarr; *Analytics* page for the Site where you wish to add analytics.\n \n5.  Copy the JavaScript tracking code provided by your analytics platform into \n    the corresponding field for your service.\n\nNow all pages on the selected Site contain the tracking script and send\nanalytics data to your analytics platform. \n\n## Maps\n\nThe *Maps* option configures the maps API provider used by your Liferay\ninstance when displaying geolocalized assets. Geolocalized assets can be\ndisplayed for documents, web content articles, DDL records, etc. Maps is\navailable under the Advanced tab. You can read more about Geolocation in\n[Geolocating Assets](/docs/7-2/user/-/knowledge_base/u/geolocating-assets).\n\n### Recycle Bin\n\nThe *Recycle Bin* option enables or disables the Recycle Bin for your Site. You\ncan also regulate the age (in minutes) for which content is able to be stored\nin the Recycle Bin until it is permanently deleted. For a full explanation of\nthe Recycle Bin, see\n[Restoring Deleted Assets](/docs/7-2/user/-/knowledge_base/u/restoring-deleted-assets).\n\n### Content Sharing\n\nIf you select the *Content Sharing* tab from the Advanced tab, you can\nconfigure whether sub-Sites can display content from this Site. Administrators\nof this Site's sub-Sites can use all structures, templates, categories,\nwidget templates, and more from this parent Site. Even if you\ninitially allowed content sharing between the parent Site and its sub-Sites,\nyou can disable this option and immediately revoke content sharing from\nall sub-Sites.\n\nYou can manage this globally by navigating to the Control Panel &rarr;\n*Configuration* &rarr; *Instance Settings* &rarr; *Content & Data* &rarr;\n*Sharing* &rarr; *Content Sharing*. First, you can choose if Site administrators\ncan display content in Sites from other Sites they administer. For example,\nsuppose that a certain User is a Site administrator of two Sites: *Engineering*\nand *Marketing*. The checkbox in the Content Sharing section of Instance\nSettings determines if the Site administrator can display content from the\nMarketing Site in the Engineering Site and vice versa.\n\nYou can also choose if child Sites can display content from parent Sites and\nconfigure the defaults. There are three options:\n\n**Enabled by Default**: Child Sites can display content from parent Sites by\ndefault, but this can be disabled by a Site administrator.\n\n**Disabled by Default**: Child Sites cannot display content from parent Sites by\ndefault, but this can be enabled by a Site administrator.\n\n**Disabled**: Child Sites cannot display content from parent Sites, and this\nbehavior cannot be changed by a Site administrator.\n\nThat covers your Site's advanced settings. You're now equipped to manage all\naspects of your Site's configuration.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/06-configuring-sites/05-customizing-personal-sites.markdown",
    "content": "---\nheader-id: customizing-personal-sites\n---\n\n# Customizing Personal Sites\n\n[TOC levels=1-4]\n\nBy default, newly created users are granted a personal Site.\n\n-  Users function as Site administrators of their personal Sites.\n\n-  Personal Sites are fully customizable but cannot have more than one member. \n\n-  Users can have publicly available content on their Site's Public Pages.\n   This is often used for a user blog.\n \n-  Users can also have Private Pages where they can keep personal information\n   or use Documents and Media to have their own private file repositories.\n\nYou can disable personal Sites by adding the following properties to your\n`portal-ext.properties` file:\n\n```properties\nlayout.user.public.layouts.enabled=false\nlayout.user.private.layouts.enabled=false\n```\n\n| **Note:** The public and private page sets of personal Sites are handled\n| separately. You can leave one page set enabled while disabling the other.\n\nIf you initially had user personal Sites enabled for your instance but then\ndisabled them, existing personal Sites remain on your Liferay instance until the\nnext time users log in, at which point they're removed.\n\nYou can allow users to create personal Sites but not have them automatically\ncreated for new users. To do this, add the following properties to your\n`portal-ext.properties` file:\n\n```properties\nlayout.user.public.layouts.auto.create=false\nlayout.user.private.layouts.auto.create=false\n```\n\nIf the properties `layout.user.public.layouts.enabled`,\n`layout.user.private.layouts.enabled`, `layout.user.public.layouts.auto.create`,\nand `layout.user.private.layouts.auto.create` are all set to `true`, which is\nthe default, users have personal Sites and public and private pages are\ncreated automatically for new users.\n\nThere are a number of portal properties you can use to customize the \nautomatically created pages. You can customize the names of the default pages, \nthe applications that appear on the pages, the themes and layout templates of \nthe default pages, and more. Please refer to the \n[Default User Public Layouts](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Default%20User%20Public%20Layouts) \nand \n[Default User Private Layouts](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Default%20User%20Private%20Layouts)\nsections of the `portal.properties` file for details.\n\n| **Note:** By default, users can modify the pages and applications on their\n| personal Sites. Administrators, however, can customize the modifiable portions\n| of personal Sites through @product@'s permissions system by removing permissions\n| from Roles. To disallow all Liferay users from modifying something, remove the\n| relevant permission from the User Role.\n\nGreat! Now you know how to customize a personal site!\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/06-configuring-sites/06-importing-exporting-pages-and-content.markdown",
    "content": "---\nheader-id: importing-exporting-pages-and-content\n---\n\n# Importing/Exporting Sites and Content\n\n[TOC levels=1-4]\n\nExport/Import lets you backup and restore your Site and app data as a LAR\n(Liferay Archive). There are two primary places Export/Import is used: Sites\nand apps. You can learn more about exporting/importing app data in the\n[Exporting/Importing Widget Content](/docs/7-2/user/-/knowledge_base/u/exporting-importing-widget-data)\nsection. In this section, you'll learn how to export and import content for\nSites.\n\n## Backing Up and Restoring Pages and Their Content\n\nIn *Site Administration* &rarr; *Publishing*, you can find the *Export* and \n*Import* option for pages. If you click on *Export*, you see an interface for\nexporting your public or private pages. The Export feature exports your Site's\ndata as a single LAR file. Similarly, *Import* is a similar interface for\nimporting public or private pages from a LAR file.\n\nWhen importing data into a Site, you should use a newly created Site to avoid\nconflicts between the existing data and the data being imported. When exporting\nSite data, you can specify exactly what data should be included in the LAR:\n\n- Site pages (you can select exactly which ones)\n- Page settings\n- Theme\n- Theme settings\n- Logo\n- Application configurations\n- Application content\n- Archived setups\n- User preferences\n\nA LAR file can be imported into a Site on another Liferay server. You can take\ncontent from a Site in one environment (say, a development or QA environment)\nand move it all to a Site on another server with LARs. You can use LARs to\nimport data onto production servers, but you should not make this a regular\noccurrence. If you want to regularly move pages from one server to another, you\nshould use @product@'s staging environment. See the\n[Staging](/docs/7-2/user/-/knowledge_base/u/staging) section for more details.\n\nYou can export LARs to use them as a backup. If you ever have to restore your\nSite, you must only import the latest LAR file. However, please be careful! If\nthere's content that exists both in the LAR and in the Site that's importing\nthe data, there may be a conflict, and data could be corrupted. If you want to\nrestore a Liferay Site using a LAR file, delete the Site entirely, create a new\nSite with the same name as the old one, and then import the LAR file into the\nnew Site. This way, there's no chance for there to be a data conflict.\n\nSome naming collisions are handled automatically. For example, a collision\noccurs if the LAR you're importing and the Site both have a page with the same\nfriendly URL. @product@ resolves the collision by adding a number to the end of\nthe friendly URL and incrementing until there's no collision. Similarly, if\nimporting a LAR into a Site causes a category name collision, the imported\ncategories are automatically renamed.\n\n| **Note:** LAR files are version dependent. You can't import a LAR file that was\n| exported from one version of Liferay into a Liferay server that's running a\n| different version of Liferay. Also, note that periodically exporting LARs is\n| *not* a complete backup solution; please refer to the\n| [Backing up a Liferay Installation](/docs/7-2/deploy/-/knowledge_base/d/backing-up-a-liferay-installation)\n| section for information on backing up Liferay.\n\n## Page Export Example\n\nHere's how the export process works: \n\n1.  Go the *Site Administration* &rarr; *Publishing*. \n\n2.  Click *Export*.\n\n3.  Click *Add* (![Custom Export](../../../../images/icon-add.png)). \n\n    A *New Custom Export* page loads, so you can choose the pages and \n    content you want to export from your Site. \n\n4.  Enter *Lunar Resort Version 1* for the *Title*.\n\n5.  Under *Pages*, select public or private pages and the settings you want \n    to export.\n \n6.  Under the *Content* category, select *All*.\n\n    Note that if you select one of the *Choose* radio selectors or *Change* \n    links, you're given checkboxes for options to choose. The applications' \n    content can also be selected for export, including the Documents and Media \n    Library, Message Boards, and Web Content assets. You can even export the \n    theme you're using. Finally, you can select whether the permissions for your \n    exported pages and content are included.\n\n    ![Figure 1: You can configure your export options manually by selecting pages, content, and permissions.](../../../../images/export-page-templates.png)\n\n7.  Click *Export*.\n\nOnce you click *Export*, the menu automatically switches to the *Processes* tab,\nwhere you see the status of your exported LAR file. You can select the\n*Download* icon (![Download](../../../../images/icon-download.png)) to download the\nexport to your local machine. Once you have the file, you can copy it to a\nbackup location for safekeeping or import it into another installation of\nLiferay. If you must rebuild or wish to revert back to this version of your\nSite, you can import this file by clicking the *Import* button from the\nPublishing menu, browsing to it, and selecting it. You can also drag a LAR file\ninside the dotted area, which also executes the import process.\n\n## Export Templates\n\nInstead of manually customizing an export process every time you export\npages/content, you can use an Export Template. This provides you the\nconvenience of storing export process settings so they can be reused. If you\nexport pages frequently and usually select the same options to export, you can\ncreate an export template to export with your standard options.\n\nTo create an export template,\n\n1.  Select the *Options* icon (![Options](../../../../images/icon-options.png))\n    from the top right corner of the screen and select *Export Templates*.\n\n2.  Click the *Add* button (![Add Export Template](../../../../images/icon-add.png)).\n\n3.  Assign the template a *Name* and *Description*.\n\n4.  Fill out the configuration options for your export process.\n\n5.  Click *Save*.\n\nYour template is now available to use from the *Export Templates* menu. To use \nthe template,\n\n1.  Click the *Actions* (![Actions](../../../../images/icon-actions.png)) next to the template.\n\n2.  Select *Export*.\n\n    This automatically fills the fields and options for exporting pages and \n    their content.\n\n3.  Give the export a name.\n\n4.  Click *Export* and your LAR file is generated.\n\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/06-configuring-sites/07-styling-widgets-and-assets/01-intro.markdown",
    "content": "---\nheader-id: styling-apps-and-assets\n---\n\n# Styling Apps and Assets\n\n[TOC levels=1-4]\n\nWidget Template define custom display templates used to render widgets. For\nexample, you may want to show blog entries horizontally instead of vertically,\nor list your assets in the asset publisher application in different sizes. In\nthis section, you'll learn about the capabilities of widget templates and how to\ncreate and configure them.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/06-configuring-sites/07-styling-widgets-and-assets/02-styling-widgets-with-widget-templates.markdown",
    "content": "---\nheader-id: styling-widgets-with-widget-templates\n---\n\n# Styling Widgets with Widget Templates\n\n[TOC levels=1-4]\n\nSuppose you're customizing the Lunar Resort Site and want to allow users to use\nFacebook or Twitter to communicate with other interested travelers. You can add\nthis functionality to an existing widget using widget templates: launch a\ntemplate editor, create a custom template, and configure your app to host that\ntemplate. Widget templates let you re-skin your widget and give you ultimate\ncontrol over its appearance and functionality.\n\n## Creating a Widget Template\n\nHere's the process of creating a widget template:\n\n1.  From Site Administration, click the *Site Selector* button \n    (![Compass](../../../../../images/icon-compass.png)) to choose the Site\n    where you want to create the widget template. \n \n2.  Open *Site Builder* &rarr; *Widget Templates*. \n \nIf you selected the Global context, this page shows a list of sample templates\navailable for your apps. These sample templates differ from the default\ntemplates already configured in the apps. If you choose a Site to host your\ntemplate, you must create a custom template for that Site's apps.\n\n![Figure 1: The Site Administration dropdown menu lets you choose the context in which your widget template resides.](../../../../../images/context-selector.png)\n\n3.  Click the *Add* (![Add](../../../../../images/icon-add.png)) button, and\n    you're prompted to select the type of template to create.\n\n- *Asset Publisher*\n- *Blogs*\n- *Breadcrumb*\n- *Categories Navigation*\n- *Language Selector*\n- *Media Gallery*\n- *Navigation Menu*\n- *RSS Publisher*\n- *Site Map*\n- *Tags Navigation*\n- *Wiki*\n\n4.  Enter the name and, optionally, open *Details* to provide a description and\n    a small image to use. You can select the language type for your template.\n \n5.  Within *Details* select a scripting language to use. You can use FreeMarker\n    or Velocity. FreeMarker is recommended. \n \n6.  Use the *Script* section to create the widget template.\n\n7.  Click *Save* when done.\n\n### The Template Editor\n\nOn the left side of the template editor is a palette of common variables used\nfor making templates. This is a great reference when creating your template. To\nplace one of the variables into the template editor, position your text cursor\nwhere you want it placed, and click the variable name.\n\nEach variable also has a tooltip which displays a detailed description. Because\nthere are multiple kinds of widget templates, there are also different variables\nfor each widget template. Thus, each template has a different set of variables\nonly applicable for that specific template. \n\n![Figure 2: Liferay offers a versatile script editor to customize your widget template.](../../../../../images/adt-script-editor.png)\n\nYou can also use the autocomplete feature to add variables to your template. It\ncan be invoked by typing *${* which opens a drop-down menu of available\nvariables. By clicking one of the variables, the editor inserts the variable\ninto the editor.\n\nYou can also embed same-type templates into other templates. For example,\nsuppose you have an existing Wiki widget template and would like to create\nanother similar Wiki widget template. Instead of starting from scratch, you can\nimport the existing Wiki widget template into your new one and build off of it.\nIn other words, you can utilize widget templates as generic templates which\nallow for reusable code to be imported by Velocity or FreeMarker templates in\nthe system.\n\n## Configuring Widget Templates\n\nAfter you've saved your widget template, you can manage it through its *Actions*\n(![Actions](../../../../../images/icon-actions.png)) button. This provides\nseveral options:\n\n- *Edit*: lets you modify the widget template's setup properties.\n- *Permissions*: lets you manage the permissions *Update*, *Permissions*,\n  *Delete*, and *View* for the widget template.\n- *Copy*: creates a copy of the widget template.\n- *Delete*: deletes the widget template.\n\nAdditionally, your widget template generates a static URL and a WebDAV URL.\nThese values access the XML source of your template. You can find these URLs by\nclicking the widget template from the menu and expanding the *Details* section.\nWith the WebDAV URL, Site administrators can add, browse, edit, and delete\nwidget templates on a remote server. If you want to learn more about what the\nWebDAV URL can do, visit the article on\n[WebDAV access](/docs/7-2/user/-/knowledge_base/u/desktop-access-to-documents-and-media). \n\n| **Note:** Embedding widgets into widget templates, although possible, is not recommended\n| because this could cause conflicts with other widgets or unexpected behavior\n| (e.g., embedding a widget that aggregates data to the breadcrumb). If embedding\n| a widget into a widget template is your only option, make sure it does not interfere with\n| other widgets.\n\nNext you must configure the widget to use the new widget template:\n\n1.  Go to the *Configuration* page for the widget you want to modify and open\n    its *Display Settings*.\n\n2.  Under *Display Template*, select your widget template from the drop-down menu.\n\nAlso, you can manage Site-specific display templates for your app: do this by \nclicking the *Manage Display Templates for [SPECIFIC_SITE]* link next to the\n*Display Template* drop-down menu. A window appears with a list of your\nconfigured templates only available for your Site with options to add new\ntemplates or edit existing templates.\n\n![Figure 3: In the *Configuration* menu of an app, you can edit and manage available widget templates.](../../../../../images/adt-configuration.png)\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/06-configuring-sites/07-styling-widgets-and-assets/03-widget-template-example.markdown",
    "content": "---\nheader-id: widget-template-example\n---\n\n# Widget Template Example\n\n[TOC levels=1-4]\n\nNow that you know the general functions of widget templates, you'll create your\nown. This brief demonstration will show you just how easy, yet powerful, widget\ntemplates can be for your Liferay instance.\n\n1.  Add the Media Gallery widget to a page by navigating to *Add*\n    (![Add](../../../../../images/icon-add.png)) &rarr; *Widgets* &rarr;\n    *Content Management* &rarr; *Media Gallery*.\n\n2.  Click the widgets's *Add* button (![Actions](../../../../../images/icon-add.png))\n    &rarr; *Multiple Files Upload* and select two custom photos to display. Then\n    click *Save*, and navigate back to the main application screen.\n\n3.  Notice the default format of the pictures. To change the display template \n    for this widget, navigate to *Options*\n    (![Options](../../../../../images/icon-app-options.png)) &rarr;\n    *Configuration*.\n\n4.  From the *Display Template* drop-down menu, select *Carousel*. Then click\n    *Save*.\n\n\t![Figure 1: After applying the Carousel widget template, your pictures are displayed as a carousel slideshow.](../../../../../images/adt-carousel.png)\n\n\tThe Media Gallery application is transformed into a carousel slideshow. At\n\tthis time, it's perfectly natural to be experiencing \"I can conquer the world\"\n\tfeelings, just as Liferay's mascot, Ray, exudes in the image above. widget\n\ttemplates have that kind of power to transform your site into an enjoyable and\n\tconvenient home for users.\n\nCustomizing the user interface of @product@'s bundled widgets provides the \nultimate customization experience for Liferay users.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/06-configuring-sites/07-styling-widgets-and-assets/04-setting-a-default-widget-template.markdown",
    "content": "---\nheader-id: setting-a-default-widget-template\n---\n\n# Setting a Default Widget Template\n\n[TOC levels=1-4]\n\nYou can change the widget template for an individual widget through its own\nconfiguration, but to configure the default widget template for all widgets of\nthat type, you must go to *System Settings*. In the System Settings you can\nfind a configuration for every widget in @product@. Any widget that supports\nwidget templates has a *Display Style Group ID* and a *Display Style* option.\n\n![Figure 1: The widget template configuration in System Settings lets you change the display style.](../../../../../images/adt-system-settings.png)\n\n\n- **Display Style Group ID:** The Site ID where the widget template is located.\n  For Global templates use *0* for the ID.\n \n- **Display Style:** The widget template's key. \n\nTo enter a Display Style, you first need the *Template Key* for the template\nyou want to use. To get the Template Key, go to the *Application Display\nTemplate* list for a given Site and retrieve it from the widget template\nlisting. Then enter the display style as `ddmTemplate_[template-key]`.\n\n## Default Widget Template Example\n\nFor example, configure the Language Selector widget templates like this:\n\n1.  Open the *Product Menu*.\n\n2.  Using the *Site Selector*, select the *Global* site.\n\n3.  Go to *Site Builder* &rarr; *Widget Templates*\n\n4.  Create a *Language Selector Template*.\n\n5.  Click the *Actions* (![Actions](../../../../../images/icon-actions.png))\n    button for the new widget template.\n\n6.  Open *Details* and find the *Template Key* - `LANGUAGE-ICON-FTL`\n\n![Figure 2: System Settings shows where you can find the Template Key.](../../../../../images/adt-template-key.png)\n\nNow that you have the ID, you can change the template from System Settings.\n\n1.  Go to the *Control Panel* &rarr; *Configuration* &rarr; *System Settings*.\n\n2.  Find *Localization* under the *Platform* heading and select *Language \n    Selector* from the options on the left.\n \n3.  In the *Display Style* field, enter `ddmTemplate_LANGUAGE-ICON-FTL`.\n \nNow any Language Selector widgets are added to a page use the new defaults.\nThis doesn't affect widgets already added to a page and configured.\n\n![Figure 3: You can see the new default configuration.](../../../../../images/adt-new-default.png)\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/07-configuring-pages/01-configuring-pages-intro.markdown",
    "content": "---\nheader-id: customizing-page-options\n---\n\n# Customizing Page Options\n\n[TOC levels=1-4]\n\nEvery page has options to give it a unique configuration. You can handle that \nconfiguration at the individual page level or configure a Page Set of public or \nprivate pages all at once.\n\nWhen you configure options across Sites, Page Sets, and individual pages, there \nis a hierarchy that flows down. For example, you can set a theme at the Site\nlevel that applies to all pages within a Site. You could then set a different\ntheme for the Private Pages set. You can even set a different theme for\na specific page that is different than the master site configuration or the\nconfiguration for its page set.\n\nWhen configuring pages, be aware of your context, so you don't configure an\noption that you intended for the whole site just on one page---or change the\nwhole site's configuration, when you meant to change it for only a specific\npage.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/07-configuring-pages/02-configuring-page-sets/01-configuring-page-sets-intro.markdown",
    "content": "---\nheader-id: configuring-page-sets\n---\n\n# Configuring Page Sets\n\n[TOC levels=1-4]\n\nTo configure options for the entire Page Set, select the *Configure* icon next\nto the Page Set in *Pages*. Options configured for the Page Set apply to all its\npages. Page Set options override options set at the Site level, and\ncustomizations to an individual page override those for the Page Set.\n\n![Figure 1: Selecting the Page Set configuration option.](../../../../../images/configure-page-set.png)\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/07-configuring-pages/02-configuring-page-sets/02-configuring-page-sets.markdown",
    "content": "---\nheader-id: page-set-look-and-feel\n---\n\n# Configuring Page Sets\n\n[TOC levels=1-4]\n\nPage Set configuration starts with the *Look and Feel* tab. Here you have an \ninterface for choosing a theme for the current Site.\n\n![Figure 1: The Look and Feel page set tab.](../../../../../images/page-set-look-and-feel.png)\n\n## Themes\n\nThemes can transform the entire look of your Site. They are created by \ndevelopers and are easily installed using Liferay Marketplace.\n\n![Figure 2: The Look and Feel interface allows you to choose a theme for the current site.](../../../../../images/look-and-feel-pages.png)\n\nYou can apply themes to the entire Site or to individual pages. For the Site,\ngo to *Pages* &rarr; the Site (public or private), and click the Gear icon. For\nindividual pages, click *Configure* &rarr; *Define a specific look and feel for\nthis page* option under the page's *Look and Feel* category. See the \n[Themes](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction) section for\ninformation on creating and developing your own custom themes.\n\n![Figure 3: You can define a specific look and feel for a page.](../../../../../images/define-a-specific-look-and-feel.png)\n\nMany themes include more than one color scheme, which keeps the existing look\nand feel while giving the Site a different flavor. The Color Schemes option\nis not available for the default theme.\n\nThere are a few more configurable settings for your Page Set look and feel. You\ncan switch the bullet style between dots and arrows and you can choose whether\nor not to show maximize/minimize application links by default. The *CSS*\nsection lets you enter custom CSS for tweaking your theme. \n\n## Using a Custom Logo for a Site\n\nBy default, the Liferay logo is used for your Site pages' logo. If you want to\nuse your own logo for a specific Site, select the *Logo* tab from the\n*Configure* interface and browse to the location of your logo. Make sure your\nlogo fits the space in the top left corner of the theme you're using for your\nwebsite. If you don't, you could wind up with a Site that's difficult to\nnavigate, as other page elements are pushed aside to make way for the logo.\n\nIn the logo tab, you can also choose whether or not to display the Site name on\nthe Site. If you check the box labeled *Show Site Name*, the Site name appears\nnext to the logo. This option is enabled by default and cannot be disabled if\nthe *Allow Site Administrators to set their own logo* option is disabled in\n*Instance Settings*. Removing the Site name is not available for the default\nSite---you can configure this only for new Sites and user pages. \n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/07-configuring-pages/02-configuring-page-sets/03-advanced-page-set-options.markdown",
    "content": "---\nheader-id: advanced-page-set-options\n---\n\n# Advanced Page Set Options\n\n[TOC levels=1-4]\n\nThere are some powerful options that should only be used by those with a firm\ncommand of the technology, or they could have major unintended side effects. You\ncan find these options under the *Advanced* tab.\n\n## Executing JavaScript in Site Pages\n\nAt the top of the *Advanced* tab is a JavaScript editor. Code entered here is\nexecuted at the bottom of every page in the Site. If your Site's theme uses\nJavaScript (as is usually the case), it's best to add custom JavaScript code to\nthe theme and *not* here. This way, all your Site's JavaScript code remains in\none place.\n\nThis may be useful if your Site's theme does *not* use JavaScript. In this\ncase, you can place *all* of your Site's JavaScript here.\n\n## Merge Public Pages\n\nIf you have more than one Site on a specific Liferay instance, one of those Sites will be the *Default Site* where visitors will be directed if they access the instance's root URL. By default, visitors will only see the pages of that Site in the navigation. To have another Site's public pages appear in the primary navigation for the Default Site, check the box to *Merge public pages* for that site. Be careful as adding too many pages to the main navigation can make it become unwieldy very quickly.\n\n## Rendering Pages for Mobile Devices\n\n*Mobile Device Rules* lets you configure your page set to have specific\nbehaviors for specific mobile devices or types. Mobile device rules are\ninherited from your Public Pages, but you can define specific rules per page.\nYou can edit the Look and Feel of specific pages for mobile devices, including\nthe theme. This is explained in \n[Mobile Device Rules](/docs/7-2/user/-/knowledge_base/u/mobile-device-rules).\n\n## Robots\n\nThe *Robots* option lets you configure `robots.txt` rules for the domain: both\nits public and private pages. The `robots.txt` file provides instructions to\nsearch engines and other tools that are automatically crawling and indexing your\nSite. Common entries here include defining some pages not to be indexed.\n\n## Notifying Search Engines of Site Pages\n\nThe *Sitemap* option generates a sitemap you can send to some search engines so\nthey can crawl your Site. It uses the industry standard sitemap protocol. \n\nSelect a search engine link to send the sitemap to it. It's only necessary to\ndo this once per Site.\n\nIf you're interested in seeing what is sent to the search engines, select the\n*preview* link to see the generated XML.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/07-configuring-pages/03-configuring-individual-pages/01-configuring-individual-pages-intro.markdown",
    "content": "---\nheader-id: configuring-individual-pages\n---\n\n# Configuring Individual Pages\n\n[TOC levels=1-4]\n\nAfter you've configured your Page Set, you can reconfigure some options at the\nindividual page level. When you decide to customize a single page, options that\nwere not available when initially creating a page appear. You can customize\na page by navigating to *Pages* under *Build* menu and selecting *Options*\n(![Options](../../../../../images/icon-options.png)) &rarr; *Configure*\nnext to the page you want to edit from the navigation tree. Alternatively, you\ncan click the *Configure* icon on the top right of any page.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/07-configuring-pages/03-configuring-individual-pages/02-customizing-pages.markdown",
    "content": "---\nheader-id: individual-page-settings\n---\n\n# Individual Page Settings\n\n[TOC levels=1-4]\n\nOn the Configure page are four tabs: General, SEO, Look and Feel, and \nAdvanced. Options selected here have no effect on the rest of the \nSite; just the page you've selected. Many of these options are the same as\nthose that configure the complete page set, so you can view more details in the \n[Configuring Page Sets](/docs/7-2/user/-/knowledge_base/u/configuring-page-sets) \narticle.\n\nNote that many of the options are localizable, so you can provide translations\nbased on the user's locale.\n\n## General\n\nThe *General* tab lets you configure the basic information and design for the \npage. You can change the *Name*, *Friendly URL*, and *Page Layout*. \n\n### Name and Friendly URL\n\nThe *Name* is the title that appears in the browser's title bar, and how the \npage is identified in the navigation. The *Friendly URL* defines the page's\nlink. It is a best practice to have the URL match the name of the Page, so\nthese two should generally be updated together.\n\n### Page Layout\n\nFor Widget Pages, you can select a Layout Template that defines droppable\nlocations for widgets. Layout templates define a number of sections with\ncolumns and rows. Widgets added to a section expand (or contract) \nhorizontally to fill the space and can be stacked vertically. \n\n![Figure 1: Setting a layout template for your page.](../../../../../images/page-select-layout.png)\n\n## Categorization and SEO\n\nManaging your page's content drastically improves your page's organization and\nuser experience. The Site page's configuration options offers some opportunities\nto organize page content.\n\n### Categorization\n\nThe *Categorization* tab shows the categorization options. These tools help\nadministrators organize the page so users can find your page and its\ncontent through search and navigation. For more information on using tags and\ncategories, see \n[Organizing Content with Tags and Categories](/docs/7-1/user/-/knowledge_base/u/organizing-content-with-tags-and-categories).\n\n### SEO\n\n*SEO* provides several ways to optimize the data the page provides to an indexer\nthat's crawling the page. You can set the various meta tags for description,\nkeywords and robots. There's also a separate Robots section for telling robots\nhow frequently the page is updated and how it should be prioritized. If the page\nis localized, you can select a box to generate canonical links by language. If\nyou want to set some of these settings for the entire Site, you can specify them\nfrom the Sitemaps and Robots tabs of the Manage Site Settings dialog box (see\nbelow).\n\nEach asset (web content article, blog entry, etc.) has a unique URL. From the\nsearch engine's point of view, this makes your pages rank higher since any\nreferences to variations of a specific URL are considered references to the same\npage.\n\nYou can also configure the page to use a custom canonical URL. To do so, set the \n*Use Custom Canonical URL* toggle to *YES*, then enter your desired canonical \nURL in the field that appears. You can define a custom canonical URL for each \nlanguage. If there's no value for a specific language, the canonical URL for \nthat language is controlled by the global/instance-level setting. \n\n![Figure 2: Enter the custom canonical URL that you want to use for the page.](../../../../../images/canonical-url-page.png)\n\nYou can also configure canonical URLs at the global and instance levels. \n\n| **Note:** Any custom canonical URLs set for individual pages take precedent \n| over the global and instance level settings. \n\n**Global:** *Control Panel* &rarr; *Configuration* &rarr; *System Settings* \n&rarr; *Pages* &rarr; *SEO*\n\n**Instance:** *Control Panel* &rarr; *Configuration* &rarr; *Instance Settings* \n&rarr; *Pages* &rarr; *SEO*\n\nNavigate to the level (global/instance) on which you want to configure canonical \nURLs, then choose one of the following in the *Canonical URL* menu: \n\n**Use Default Language URL (default):** When a user visits a page in any \nsupported language, the default language's URL is used as the canonical URL. \n\n**Use Localized URL:** The page's localized URL is used as the canonical URL. \n\n![Figure 3: You can also configure canonical URLs at the global and instance levels.](../../../../../images/canonical-url-system.png)\n\n## Look and Feel\n\n*Look and Feel* lets you set a page-specific theme. You can inherit what you\nalready have configured for your Page Set's theme, or you can define a theme per\npage. See \n[Customizing the Look and Feel of Site Pages](/docs/7-2/user/-/knowledge_base/u/page-set-look-and-feel)\nfor more details.\n\n## Advanced Settings\n\n*Advanced Settings* contains options useful for specific cases. Some of these\nare the same as the options available at the Site or Page Set level, but *Custom\nFields*, *Embedded Widgets*, and *Customization Settings* are unique to the\nindividual page configuration.\n\n### Query String\n\nYou can set a query string to provide parameters to the page. This can become\nuseful to web content templates. You can set a target for the page so that it\npops up in a particularly named window or appears in a frameset. And you\ncan set an icon for the page that appears in the navigation menu.\n\n### Custom Fields\n\n*Custom Fields* lets you edit the custom fields you already have configured for\nthe *Page* resource. If you don't have any custom fields configured in your\nSite, this option doesn't appear. In this case, navigate to the Control\nPanel &rarr; *Custom Fields* located under the *Configuration* tab. These are\nmetadata about the page and can be anything you like, such as author or creation\ndate. For more information on Custom Fields, see \n[Custom Fields](/docs/7-1/user/-/knowledge_base/u/setting-up).\n\n### Embedded Widgets\n\nThis option only appears if you have embedded one or more widgets on\nthe page. \n\nWidgets can be embedded on a page via web content template or fragment. To\nlearn more about this, see \n[Adding Templates](/docs/7-2/user/-/knowledge_base/u/adding-templates).\nYou can embed a widget on a page layout or theme programmatically. If you're\ninterested in learning more about this, visit the\n[Embedding Portlets in Themes](develop/tutorials/-/knowledge_base/7-2/embedding-portlets-in-themes)\ntutorial.\n\n### Customization Settings\n\nThis configuration option in the *Advanced* tab lets you mark specific\nsections of the page you want users to be able to customize. You can learn more\nabout page customizations in \n[Personalizing Pages](/docs/7-2/user/-/knowledge_base/u/personalizing-pages).\n\n### JavaScript\n\nThis shows a JavaScript editor for code that's executed at the bottom of your\npage. If your Site's theme uses JavaScript (as is usually the case), it's best\nto add custom JavaScript code to the theme instead. This way, all your Site's\nJavaScript code remains in one place.\n\nThis configuration option is also available for Page Sets like Public Pages and\nPrivate Pages. Visit \n[Executing JavaScript in Site Pages](/docs/7-2/user/-/knowledge_base/u/advanced-page-set-options#executing-javascript-in-site-pages)\nfor more information on doing this for Page Sets.\n\n### Mobile Device Rules\n\nApply rules for how this page should render for various mobile devices here.\nCreate them by navigating to Site Administration menu and selecting\n*Configuration* &rarr; *Mobile Device Families*.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/07-configuring-pages/03-configuring-individual-pages/03-personalizing-pages.markdown",
    "content": "---\nheader-id: personalizing-pages\n---\n\n# Personalizing Pages\n\n[TOC levels=1-4]\n\nAdministrators can designate pages or sections of Widget Pages as customizable.\nWhen a user visits such a page, a notification appears stating that the user can\ncustomize the page. Users can make customizations only in the sections of pages\ndesignated by administrators. Customizations are based on the rows and columns\nof a page layout. Page customizations are only visible to the user who made the\ncustomizations. By default, Site members can make page customizations but\nnon-Site members and guests can't.\n\n## Enabling Page Customizations\n\nTo enable page customizations as an administrator,\n\n1.  Click *Configure Page* from the *Options* button next to the Page you want\n    to let Site members modify.\n \n2.  Select the *Advanced* tab at the top of the page and expand the\n    *Customization Settings* area.\n\n3.  Click the *Customizable* selector button to activate customizations.\n\n    ![Figure 1: To enable page customizations, click on the *Configure Page* button next to the page, expand the *Customization Settings* area, and click on the *Customizable* button.](../../../../../images/page-customizations.png)\n\n4.  Select the sections of the page that should be customizable.\n \n5.  Enable one or more of the *Customizable* sections so Site members can \n    customize sections of the page. Regions that you've designated as\n    customizable are colored blue.\n\nWhen Site members visit your customizable page, they see an extended Control\nMenu with a notification saying *You can customize this page*. Site members can\ntoggle whether to view or hide the customizable regions. If you toggle the\nselector to view customizable regions, the regions on the page are color-coded\nto help distinguish customizable vs. non-customizable sections of the page.\n\n![Figure 2: Customizable regions are colored green and non-customizable regions are colored red.](../../../../../images/color-coded-customizable-regions.png)\n\n## Customization Permissions\n\nAdministrators must grant users permission to customize pages under the Site\nsection. This can be achieved by assigning permission to a Role, then assigning\nthis Role to the appropriate users. For example, if you want users to be able to\ncustomize your customizable pages, assign the *Customize* permission to the Role\n*User*. If you want Site members to be able to customize their Sites'\ncustomizable pages, accept the default setting. By default, the *Customize*\npermission is assigned to the Role *Site Member*.\n\nThe *Customize* permission also lets users customize the look and feel of\napps and import or export app settings.\n\n## Customizing Pages\n\nWith customization active, Site members can access the Add menu from the top\nright side of the screen when viewing their customizable page, which lets them\nadd apps to the customizable sections of the page. If they click *View Page\nwithout my customizations*, the Add menu disappears.\n\nUsers can make two kinds of customizations to customizable regions:\n\n1.  They can configure applications within the customizable regions.\n\n2.  They can add apps to or remove apps from the customizable regions.\n\n*Reset My Customizations* from the *Options* button restores a user's customized\npage to match the default page, discarding their customizations so they can\nstart anew. \n\n![Figure 3: Customizable areas are highlighted green when organizing apps on the page.](../../../../../images/customizable-regions.png)\n\nUsers can't change a non-instanceable app's configuration inside a customizable\nregion since those apps are tied to the Site where they've been added. \n\n## Viewing Customized Pages\n\nSite members can also choose between viewing their customized page and viewing\nthe default page by selecting the *Options* button\n(![Options](../../../../../images/icon-options.png)) from the Control Menu and\nclicking the *View Page without my customizations* or *View My Customized Page*.\n\nAdministrators of customizable pages have the same two views as Site members:\nthe *default page* view and the *customized page* view. Changes made to the\ndefault page affect all users, whereas changes made to the customized page\naffect only the administrator who made the changes. Changes made by\nadministrators to non-customizable sections in the default view are immediately\napplied for all users. Changes made by administrators to customizable sections,\nhowever, do *not* overwrite users' customizations.\n\n## Customization Example\n\nAs an administrator,\n\n1.  Go to *Configure* for the *Welcome* page.\n\n2.  Go to the *Advanced* tab and activate Customizations.\n\n3.  Set the main column of the Welcome page of the Lunar Resort Site to be\n    customizable.\n\nAs a regular user,\n\n1.  Navigate to the *Welcome* page.\n\n2.  Click *Add* &rarr; *Widgets*.\n\n3.  Locate the *Language Selector* widget and add it to the page.\n\nThe Language Selector application lets users select their language to view\na translation of your Site into their native language. After closing the\nConfiguration dialog box of the Language Selector app, the customized Welcome\npage looks like this:\n\n![Figure 4: In this example, the user added the Language app, and changed the display style from icons to a select box.](../../../../../images/customized-portal-homepage.png)\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/07-configuring-pages/03-configuring-individual-pages/04-page-permissions.markdown",
    "content": "---\nheader-id: changing-page-permissions\n---\n\n# Changing Page Permissions\n\n[TOC levels=1-4]\n\nPublic pages are just that: public. They can be viewed by anybody, logged in or\nnot. And private pages are only private from non-members of the Site. If someone\nhas joined your Site or is a member of your organization, that person can see\nall the private pages. If you want to further protect some content, you can\nmodify the permissions on individual pages in either page group so only certain\nusers can view them.\n\nHere's how to create a page only administrators can see: \n\n1.  Go to your Site's Site Administration dropdown and select *Site Builder*\n    &rarr; *Pages* &rarr; *Private Pages*. \n\n2.  Create a page called *Admin Tips*.\n\n3.  Click *Configure* from the Options button dropdown for the page in the left\n    menu.\n\n4.  Select *Permissions* from the *Options* icon\n    (![Options](../../../../../images/icon-options.png)) in the top right corner of\n    the screen.\n\n4.  Uncheck the *View* and *Add Discussion* permissions next to the Site Member\n    role.\n\n5.  Click the *Save* button.\n\n![Figure 1: The Permissions offer a plethora of options for each role.](../../../../../images/web-content-page-permissions.png)\n\nCongratulations! You've changed the permissions for this page so only Site\nadministrators can view it. Any users you add to this Role can now see the page.\nOther users, even members of this Site, don't have permission to see it.\n\nPages are as flexible as pages you'd create manually without Liferay. Using\na point and click interface, you can define your Site any way you want. You can\ncreate and remove pages, export and import them, set their layouts, define how\nthey are indexed by search engines, and more.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/08-configuring-widgets/01-configuring-widgets-intro.markdown",
    "content": "---\nheader-id: configuring-applications\n---\n\n# Configuring Widgets\n\n[TOC levels=1-4]\n\nJust like siblings have common features inherited from their parents,\nwidgets that ship with @product@ also share common features. These include\nlook and feel, exporting/importing app data, communication, sharing,\npermissions, scoping, and configuration templates. These features work together \nto facilitate information flow within @product@ and provide an enhanced \nexperience for your users. You'll start with look and feel configuration \noptions.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/08-configuring-widgets/02-look-and-feel-configuration.markdown",
    "content": "---\nheader-id: look-and-feel-configuration\n---\n\n# Look and Feel Configuration\n\n[TOC levels=1-4]\n\nTo access the look and feel configuration menu of any widget,\n\n1.  Click *Options* (![Options](../../../../images/icon-app-options.png)) in the top\n    right corner of the widget.\n\n2.  Select *Look and Feel Configuration*.\n\n*Look and Feel Configuration* has six tabs: \n\n- General\n- Text Styles\n- Background Styles\n- Border Styles\n- Margin and Padding\n- Advanced Styling\n\nAfter making customizations, click *Save* and refresh your page to apply your\nchanges. If you don't like the effect of your changes, some tabs have a *Reset*\nbutton to discard changes.\n\n## General Settings\n\nOn the General tab are the following options:\n\n**Use Custom Title** enables changes to your widget's title. The value in the\ntitle box is displayed on widget's decorator. The title is localizable, so you\ncan provide translations of the title for different languages.\n\n**Application Decorators** gives you the choice between three decorators: \n*Barebone*, *Borderless*, and *Decorate*. The Decorate application decorator is\nthe default. Be careful about turning widget borders off; some themes assume\nwidget borders are turned on and may not display correctly with them turned\noff.\n\n![Figure 1: The General tab of the Look and Feel Configuration menu lets you define a custom widget title and select the widget contrast option using decorators.](../../../../images/look-and-feel-portlet-configuration-menu.png)\n\n## Text Styles\n\n*Text Styles* configures the format of the text that appears in the widget. The\noptions include\n\n**Font:** Choose various fonts. You can set the text to bold, italics, or both.\n\n**Size:** Set the font size anywhere from 0.1 em to 12 em, with 0.1 em\nincrements. 1 em is the default.\n\n**Color:** Set to any six digit hex color code. Click on the text box to open\nthe color palette.\n\n**Alignment:** Set to *Left*, *Center*, *Right*, or *Justified*. \n\n**Text Decoration:** Set to *Underline*, *Overline*, or *Strikethrough*. The\ndefault text decoration is *None*.\n\n![Figure 2: The Text Styles tab lets you configure the format of the text that appears in the widget.](../../../../images/look-and-feel-text-styles.png)\n\n**Word Spacing:** Set from -1 em to 0.95 em, with 0.05 em increments. 0 em is\nthe default.\n\n**Line Spacing:** Set from 0 em to 12 em, with 0.1 em increments. 0 em is the\ndefault. \n\n**Letter Spacing:** Set from -10 px to 50 px, with 1 px increments. 0 px is the\ndefault.\n\n## Background Styles\n\nThe Background Styles tab specifies the widget's background color. When you\nselect the text space, you're given a color palette to choose your background\ncolor or you can manually enter any six digit hex color code.\n\n![Figure 3: The Background Styles tab lets you specify the widget's background color.](../../../../images/look-and-feel-background-styles.png)\n\n## Border Styles\n\nThe Border Styles tab, configures your widget's border width, style, and color.\nFor each of these attributes, leave the *Same for All* selector enabled to apply\nthe same settings to top, right, bottom, and left borders.\n\n![Figure 4: The Border Styles tab lets you specify a border width, style, and color for each side of the widget.](../../../../images/look-and-feel-border-styles.png)\n\nFor border width, you can specify any % value, em value, or px value. For\nborder style, you can select Dashed, Double, Dotted, Groove, Hidden, Inset,\nOutset, Ridge, or Solid. For border color, you can enter any six digit hex\ncolor code, just like for the text color and background color. You can also use\nthe color palette.\n\n## Margin and Padding\n\nThe Margin and Padding tab specifies margin and padding lengths for the edges of\nyour widget. Just like for border styles, leave the *Same for All* selector\nenabled to apply the same settings to each side (top, right, bottom, and left)\nof the widget.\n\n![Figure 5: The Margin and Padding tab allows you to specify margin and padding lengths for the sides of your widget.](../../../../images/look-and-feel-margin-and-padding.png)\n\nFor both padding and margin, you can specify any % value, em value, or px\nvalue.\n\n## Advanced Styling\n\nThe Advanced Styling tab displays current information about your widget, \nincluding your widget's Liferay ID and CSS classes.\n\n![Figure 6: The Advanced Styling tab displays your widget's Liferay ID and allows you to enter CSS code to customize the look and feel of your widget.](../../../../images/look-and-feel-advanced-styling.png)\n\nYou can also enter custom CSS class names for your widget and custom CSS code.\nClicking the *Add a CSS rule for just this portlet* or *Add a CSS rule for all\nportlets like this one* links adds the CSS code shells into your custom CSS text\nbox. If you check the *Update my styles as I type* box, your CSS code is applied\ndynamically to your widget so you can see the effects of your edits.\n\nNext, you'll learn about communication between widgets.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/08-configuring-widgets/03-importing-exporting-app-data.markdown",
    "content": "---\nheader-id: exporting-importing-widget-data\n---\n\n# Exporting/Importing Widget Data\n\n[TOC levels=1-4]\n\nYou may need to export data from a specific widget instance, without regard to\ncontent on the rest of the Site. There are many widgets that let you export or\nimport their data individually: \n\n- Blogs\n- Bookmarks\n- Dynamic Data Lists\n- Forms\n- Knowledge Base\n- Message Boards\n- Web Content\n- Wiki\n- And more\n\nExporting widget data produces a `.lar` file that you can save and import into\nanother widget of the same type. To import widget data, you must select a `.lar`\nfile. Be careful not to confuse widget-specific `.lar` files with Site-specific\n`.lar` files. See the\n[Importing/Exporting Pages and Content](/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content)\narticle for information on importing/exporting Site page data.\n\nThere are two ways to export/import widget content. You can navigate to the\nwidget's administrative area located in the Product Menu, or you can visit the\nwidget on its page. Both export/import menus work the same, but the\nadministrative area may hold content different from its widget counterpart\n(e.g., Web Content Admin in Product Menu and Web Content Display widget do not\noffer same content for export/import), so be wary of your selection. \n\nTo export or import data from the widget's administrative area, follow the steps\nbelow.\n\n1.  Navigate to the widget's designated area in the Product Menu. For example,\n    if you plan to export Web Content data, navigate to *Content* &rarr; *Web\n    Content*.\n\n2.  Click the *Options* button (![Options](../../../../images/icon-options.png))\n    from the top right of the page and select *Export/Import*.\n\n3.  Select the *Export* or *Import* tab to begin configuring the respective\n    process.\n\n![Figure 1: You can access a widget's administrative *Export/Import* feature by selecting its Options menu.](../../../../images/admin-app-export-import-feature.png)\n\nTo export or import data from a widget, follow the steps below:\n\n1.  Ensure the widget you're exporting/importing from is available on a page.\n    You can add widgets from the *Add*\n    (![Add](../../../../images/icon-add-app.png)) &rarr; *Widgets* menu.\n\n2.  Select the widget's *Options* button\n    (![Options](../../../../images/icon-app-options.png)) and select\n    *Export/Import*.\n\n3.  Select the *Export* or *Import* tab to begin configuring the respective\n    process.\n\n![Figure 2: You can access a widget's *Export/Import* feature by selecting its Options menu.](../../../../images/widget-export-import-feature.png)\n\nNow that you know how to navigate to the *Export/Import* menus, you can explore\nthe export process.\n\n## Exporting Widget Data\n\nTo export widget data, create a new export process by selecting the *New Export\nProcess* tab (default). You have several export options to configure.\n\nFirst, you can choose to export your widget's configuration settings. This\nexports your customized settings from your widget's *Options* &rarr;\n*Configuration* menu. For some widgets, the configuration export might also\ninclude content. For example, a Web Content Display widget that shows a web\ncontent article also exports the article when exported, even though no content\nis selected. This applies when publishing a Web Content Display widget too; the\nconfigured article is published with the widget.\n\nNext, you can select a *Date Range* of content that you want to export. Content\nadded to your widget within your specified date range is included in the `.lar`\nfile. The following date range choices are available:\n\n**All:** Publishes all content regardless of its creation or last modification\ndate.\n\n**Date Range:** Publishes content based on a specified date range. You can set a\nstart and end date/time window. The content created or modified within that\nwindow of time is published.\n\n**Last...:** Publishes content based on a set amount of time since the current\ntime. For example, you can set the date range to the past 48 hours, starting\nfrom the current time.\n\nBy checking the *Content* box, you can choose specific content you want to\nexport. When you check the *Content* box, more options appear, letting you\nchoose specific kinds of metadata to include. For example, if you have a wiki\npage with referenced content that you don't want, check the *Wiki Pages*\ncheckbox and uncheck the *Referenced Content* checkbox. Another option is the\nselection of content types. Two familiar content types in your Liferay instance\nare *Comments* and *Ratings*. If you want to include these entities in your\n`.lar` file, select *Change* and select them from the checklist. For more\ninformation on managing content types, see the \n[Managing Content Types in Staging](/docs/7-2/user/-/knowledge_base/u/managing-content-types-in-staging)\narticle.\n\nNext, you can choose to export individual deletions. This lets delete operations\nperformed for content types be exported to the LAR file.\n\nFinally, you can choose whether to include permissions for your exported\ncontent. The permissions assigned for the exported widget window are included if\nyou enable the *Export Permissions* selector.\n\nAfter you've exported your widget's data, switch to the *Current and Previous*\ntab to view ongoing export processes and the history of past exports. You can\nalso download the exported `.lar` file from this tab.\n\n## Importing Widget Data\n\nTo import widget data, you can select the LAR using your file explorer or by\ndragging and dropping the file between the dotted lines.\n\n![Figure 3: When importing widget data, you can choose a LAR file using the file explorer or drag and drop the file between the dotted lines.](../../../../images/import-menu.png)\n\nYour LAR file is uploaded and displayed to you for review. Click *Continue*.\n\nNow that you've uploaded and confirmed your LAR file, you're given a similar\nscreen to what you'd be offered during export. Several of these options are\ncovered in great detail in the\n[Importing/Exporting Pages and Content](/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content)\ntutorial. There are some additional options available: *Update Data* and\n*Authorship of the Content*. Here's options and descriptions for each section:\n\n**Update Data**\n\n**Mirror:** All data and content inside the imported LAR is newly created the\nfirst time while maintaining a reference to the source. Subsequent imports from\nthe same source updates entries instead of creating new entries.\n\n**Mirror with overwriting:** Same as the mirror strategy, but if a document or\nan image with the same name is found, it is overwritten.\n\n**Copy as New:** All data and content inside the imported LAR is created as new\nentries within the current Site every time the LAR is imported.\n\n**Authorship of the Content**\n\n**Use the Original Author:** Keep authorship of imported content whenever\npossible. Use the current user as author if the original one is not found.\n\n**Use the Current User as Author:** Assign the current user as the author of\nall imported content.\n\nOnce you've selected the appropriate options, select *Import* and your widget's\ndata is imported and ready for use.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/08-configuring-widgets/04-communication-between-widgets.markdown",
    "content": "---\nheader-id: communication-between-portlet-widgets\n---\n\n# Communication Between Portlet Widgets\n\n[TOC levels=1-4]\n\nPortlet widgets can communicate with each other using public render parameters and\nevents. Public render parameters are easy to use and can be quite powerful.\nSome Liferay portlets provide a configuration UI to help you get the most out\nof this communication mechanism. To access this UI, open your portlet's\nconfiguration window by clicking on the *Options* icon\n(![Options](../../../../images/icon-app-options.png)) and selecting *Configuration*.\nThen click on the *Communication* tab.\n\n![Figure 1: You can configure portlets to communicate with each other using public render parameters.](../../../../images/app-communication-tab.png)\n\n| **Note:** If your widget isn't a portlet, this feature isn't available.\n\nThe screenshot above is for the Wiki, which has six public render\nparameters: `categoryId`, `nodeId`, `nodeName`, `resetCur`, `tag`, and `title`.\nFor each of these parameters, you can configure the portlet to ignore the values\ncoming from other portlets or read the value from another parameter.\n\nWhy might it be useful to ignore the values for certain parameters that come\nfrom other portlets? Consider a common use case for the Wiki application. The \nWiki portlet is often used along with the Tags Navigation portlet so that when a \nuser clicks on a tag of the latter, the Wiki shows a list of pages with that \ntag. An administrator may want the Wiki to show the front page always \nindependently of any tag navigation done through other portlets. Ignoring the\nvalues of the parameter coming from other widgets let this happen.\n\nReading the value of a parameter from another portlet is an advanced but very\npowerful option that allows portlets to communicate with each other even if\ntheir developers didn't intend them to. For example, imagine that the Wiki\nis used to publish information about certain countries, and there's another\nportlet that allows browsing countries for administrative reasons. The second\nportlet has a public render parameter called *country* with the name of the\ncountry. You'd like the Wiki to show the information from the country that's\nselected in the administration portlet. This can be achieved by setting the\nvalue of the title parameter of the Wiki portlet to be read from the country\nparameter of the administration portlet. Cool, isn't it?\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/08-configuring-widgets/05-sharing-applications-with-other-sites.markdown",
    "content": "---\nheader-id: sharing-widgets-with-other-sites\n---\n\n# Sharing Widgets with Other Sites\n\n[TOC levels=1-4]\n\nYou can share widgets with other Sites by embedding an instance of a widget\nrunning on your Site into another website, such as Facebook. This opens up\na whole new avenue of exposure to your web site that you would not have had\notherwise. In fact, this is how all those Facebook games work.\n\n![Figure 1: The Sharing tab in your widget's Configuration menu lets you share your widget in a variety of ways.](../../../../images/collaboration-app-configuration-sharing.png)\n\nTo share one of your widgets, open the *Configuration* dialog box from\nthe widget's *Options* icon (![Options](../../../../images/icon-app-options.png)) and\nselect the *Sharing* tab. There are five sub-tabs under sharing: Any Website,\nFacebook, OpenSocial Gadget, and Netvibes.\n\n## Any Web Site\n\nCopy and paste the provided snippet of JavaScript code into the web site where\nyou want to add the widget. That's all you need to do. When a user loads the\npage on the other website, the code pulls the relevant widget from your \nSite and displays it.\n\n## Facebook\n\nYou can add any widget as a Facebook app. To do this, you must first get a \ndeveloper key. A link for doing this is provided to you in the Facebook tab. \nYou must create the application on Facebook and get the API key and canvas page\nURL from Facebook. Once you've done this, you can copy and paste their values\ninto the Facebook tab. Save the configuration and navigate back to the Facebook\ntab in @product@. You're given the Callback URL, which you can copy and paste\ninto Facebook. When opening your app in Facebook, the correct callback URL is\nused to render the application. You can also enable the *Allow users to add\n[application-name] to Facebook*. Then you can navigate to your app's Options\nmenu and select *Add to Facebook*.\n\n## OpenSocial Gadget\n\nOpenSocial comprises a container and a set of APIs for social networking and\nother web applications. @product@ can serve up applications to be used as \nOpenSocial Gadgets on any OpenSocial-compatible pages.\n\nTo serve a Liferay widget on an OpenSocial platform, copy and paste the provided\ngadget URL and add it to the appropriate configuration page of the OpenSocial\nplatform you're using. Your Liferay instance serves that widget directly onto\nthat platform's page. The URL provided is unique to the specific instance of\nthe widget, so you could serve multiple instances of the same widget as\ndifferent OpenSocial Gadgets.\n\nFrom the Sharing tab in the Configuration menu, you can also enable the selector\n*Allow users to add [application-name] to an OpenSocial platform*. Click *Save*\nand revisit the *Options* button of your widget. A new button appears named\n*Add to an OpenSocial Platform*. When selecting this new button, the URL is\nprovided for sharing the widget to an OpenSocial platform. \n\n## Netvibes\n\nNetvibes offers a similar environment where users can log in, create their own\npersonal dashboard, and add customizable widgets to it. To set up Netvibes\nsupport for a widget, enable the *Allow users to add [application-name] to\nNetvibes pages* selector. You can then use the provided URL to create a custom\nNetvibes widget based on the instance of the Liferay widget that you're using.\n\nNext, you'll learn how to set permissions for Liferay applications.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/08-configuring-widgets/06-widget-permissions.markdown",
    "content": "---\nheader-id: widget-permissions\n---\n\n# Widget Permissions\n\n[TOC levels=1-4]\n\nAll of Liferay's widgets support @product@'s robust, fine-grained permissions\nsystem. Some higher level permissions can be configured in the permissions tab\nof the widget's configuration dialog box. You can grant Roles permission to\n\n- Add a display template\n- Add the widget to a page\n- Configure the app\n- Modify the widget's permissions\n- Modify the widget's preferences\n- View the widget\n\n![Figure 1: Viewing the permissions configuration for a widget.](../../../../images/widget-permissions.png)\n\nTo set these permissions, go to the widget's *Options* icon\n(![Options](../../../../images/icon-app-options.png)) and click select *Permissions*.\nThis shows you a table of Roles. Use the check boxes to grant certain\npermissions to different Roles. Click *Save* after you've made your selections.\n\nBeyond this, specific permissions are generally defined for specific\nwidgets. For example, Message Boards contains a *Ban User* permission. \nThis makes no sense in the context of most other widgets. \nYou'll go over permissions for specific widgets in the sections for those \nwidgets. Next, you'll learn about widget scopes. \n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/08-configuring-widgets/07-widget-scope.markdown",
    "content": "---\nheader-id: widget-scope\n---\n\n# Widget Scope\n\n[TOC levels=1-4]\n\nAs you learned earlier, Roles can be scoped by the instance, by Site, or by an\nOrganization. A Role only takes effect within its scope. For example, a Message\nBoards Administrator Role with complete access to the Message Boards has\ndifferent permissions based on the Role's scope. If it's a global Role, members\nhave permission to administer message boards across the entire installation. If\nit's a Site Role, members only have permission to administer message boards\nwithin the Site where they've been assigned the Role. For Organizations with\nSites, Site Roles are automatically assigned to Organization members based on\nthe Organization Roles they have. For an Organization-scoped Message Boards\nadministrator Role, members only have permission to administer message boards\nwithin the Site of the Organization that assigned the Role to them. \n\nYou've also heard the word *scope* refer to the data set of a widget. By \ndefault, when a widget is added to a page in a Site, it is *scoped* for that \nSite. This means its data belongs to that Site. If the widget is added to a page\nin a different Site, it employs a completely different data set. This enables\nyou to place a Message Boards widget in one Site with one set of categories and\nthreads, and place another Message Boards widget in different Site with\na different set of categories and threads.\n\nScoping by Site means that you can only have one Message Boards widget per \nSite. If you add one Message Boards widget to a page in a Site and add another \nMessage Boards widget to a different page in the same Site, the second Message \nBoards widget contains exactly the same data as the first. This is because, by \ndefault, the Message Boards widget is scoped by Site. Most of @product@'s other \nwidgets also default to being scoped by Site.\n\nTo avoid this limitation, many Liferay widgets can be scoped by page. The data \nsets of page-scoped widget serve a single page, not an entire Site. If you set \nthe scope of a widget to *page* instead of *Site*, you can add any number of \nthese widgets to different pages, and then they have different sets of data.\nThen you can have more than one message board per Site if you wish. Most\nwidgets, however, default to the \"native\" configuration, and have their scopes\nset to the Site where they are placed.\n\nUnless otherwise noted, all widgets support scoping by instance (global), Site\n(default), or page. This grants you some flexibility in how you want to set up\nyour Liferay instance. You can configure the scope of a widget's content with just a few\nsimple steps.\n\n1.  Click the widget's *Options* icon (![Options](../../../../images/icon-app-options.png)).\n\n2.  Select *Configuration*.\n\n3.  Select the *Scope* tab.\n\n4.  Use the drop-down menu to set the scope.\n\n![Figure 1: You can change the scope of your widget's content by navigating to its Configuration menu.](../../../../images/changing-widget-scope.png)\n\nOnce you've created a new scope for widgets, a button (![Scope drop-down icon](../../../../images/icon-cog.png)) with a drop-down menu appears in the *Content & Data* menu for you to select which scope to manage content for. You can choose the default scope or any new scopes you created for your widgets. Your selection changes the content that appears when you manage each type.\n\n![Figure 2: Use the drop-down menu under Content & Data to determine which scope to manage content for.](../../../../images/widget-scope-drop-down-menu.png)\n\nThat's all it takes to change the scope for a particular widget. By setting the \nscope to the current page, you can add as many of these widgets to a Site as \nyou want, provided they are all added to separate pages. \n\nAnother useful feature of Liferay's widgets is Configuration Templates. \n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/08-configuring-widgets/08-configuration-templates.markdown",
    "content": "---\nheader-id: configuration-templates\n---\n\n# Configuration Templates\n\n[TOC levels=1-4]\n\nOnce you've configured a widget, Configuration Templates can save those\nsettings in a reusable template. If someone goes in and changes the settings of\na particular widget, it then becomes easy to revert those changes back to the\noriginal configuration template. Configuration templates are only available for\nwidgets placed on a page. Applications available from the Product Menu do not\nprovide configuration templates.\n\nTo create a configuration template, click the *Options* icon\n(![Options](../../../../images/icon-app-options.png)) from the menu in the widget's\ntitle bar and select *Configuration Templates*. If widget's current settings\nare the ones you want to save, click the *Save Current Configuration as\nTemplate* button. If not, change the settings until it's configured the way you\nwant it, and then click the button.\n\n![Figure 1: Create a configuration template to save your app's configuration settings.](../../../../images/configuration-template.png)\n\nThere is only one field to fill out. Enter a name for your template and click\n*Save*. You should now see your configuration in the list. If you ever need to\nrevert the app to these archived settings, you can click *Actions*\n(![Actions](../../../../images/icon-actions.png)) &rarr; *Apply* next to the\nconfiguration template you want to apply.\n\nUnless otherwise noted, all widgets in @product@ support this feature. This is\nparticularly useful for widgets that have a lot of configuration options, such\nas the Message Boards application. \n\n## Summary\n\nYou've now explored the configuration options available for Liferay widgets.\nYou learned how to customize your widgets, export/import data, communicate\nbetween widgets, take advantage of different scopes, and save configuration\nsettings. You also examined the different uses of social applications like\nFacebook and Netvibes for your Liferay widgets. In all, @product@ gives you an\nabundance of options to leverage the full capability of your widgets.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/09-managing-site-members/01-managing-membership-intro.markdown",
    "content": "---\nheader-id: managing-members-in-your-site\n---\n\n# Managing Members in Your Site\n\n[TOC levels=1-4]\n\nUsers and Sites are important concepts. Sites are where all your content and \npages are stored, and Users access and create that content. While user\nmanagement is covered in depth in our \n[User Management tutorial](/docs/7-2/user/-/knowledge_base/u/managing-users),\nthere are other user configuration options specific to Site Management:\n\n- Adding members to Sites administratively\n- Adding members to Sites automatically\n- Creating Teams of Site members for various functions\n\nThis section of tutorials shows you how to manage and configure these Site\noptions for Users.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/09-managing-site-members/02-adding-members-to-sites.markdown",
    "content": "---\nheader-id: adding-members-to-sites\n---\n\n# Adding Members to Sites\n\n[TOC levels=1-4]\n\nIn [Adding Sites](/docs/7-1/user/-/knowledge_base/u/adding-sites) you \nlearned the difference between Site Membership Types and about public and \nprivate pages within a Site. Now you'll learn how to add users manually to Sites\nand how to provide options for self management. For review, there are a few key\nreasons why Site Membership management is important:\n\n1.  Only Site Members can view a Site's Private Pages.\n\n2.  Site Members have more permissions than guests for many widgets like Message\n    Boards and Wikis that enable them to create content and collaborate on your\n    Site.\n\n3.  Site Members can be associated with Roles that grant Site privileges.\n\n## Administrating Site Membership\n\nAdministrators can manage Site members from that Site's *Site Membership* page.\n\n1.  Open *Site Administration* and select the Site that you want to manage\n    members for.\n \n2.  Click on *People* &rarr; *Memberships*\n\nFrom here you can manage Site Memberships, Organization, and User Group\nassociations. You can learn more about those in the \n[Users and Organizations tutorial](/docs/7-2/user/-/knowledge_base/u/users-and-organizations). Here\nyou see a list of all of the current Users of the Site and you can add or remove\nuser memberships from the Site.\n\n![Figure 1: The current members of the Site as displayed on the *Site Memberships* page. ](../../../../images/orgs-add-organization-site.png)\n\n### Adding Members to a Site\n\nFollow these steps to make an existing user a member of the Site:\n\n1.  Click the *New* (![Add User](../../../../images/icon-add.png)) \n    button in the top right of the screen.\n\n2.  Use *Filter and Order* or the *Search* function to locate the User you want\n    to add to the Site.\n \n3.  Select the User(s) you wish and click *Done*.\n\nOn the *Assign Users to This Site* screen, all Users eligible to be added to the\nSite appear. Deactivated Users do not appear. Site members also appear, but with\na greyed-out checkbox.\n\n![Figure 2: The list of users available to add to the current Site. Note that the current members are visible but cannot be added or removed here. ](../../../../images/assign-users.png)\n\n### Removing User Membership from a Site\n\nThere are two ways to remove a user from a Site. You can remove an individual \nmember like this:\n\n1.  Click the *Actions* (![Actions](../../../../images/icon-actions.png)) icon\n    for the user that you want to remove.\n \n2.  Select *Remove Membership*.\n\n3.  In the pop-up that appears, confirm the removal.\n\n![Figure 3: Selecting to remove a user. ](../../../../images/remove-user.png)\n\nTo remove several users at once, you can do this:\n\n1.  Click the checkbox for each user that you want to remove.\n\n2.  In the menu at the top of the page, click the `X` icon to remove the\n    users from the Site. \n\n3.  In the pop-up that appears, confirm the removal.\n\nRemoved Users lose access to the Site's private pages and membership in any Site\nRoles or Teams they had. \n\n### Assigning Site Roles\n\nRoles grant permissions in @product@. Roles can be assigned for the entire\ninstance or just for one specific Site or Organization. Site Roles assign\npermissions for a specific Site.\n\nYou can use the same interface options that you used to remove Users from the \nSite to assign them to Site Roles. If you select a User or Users and click\n*Assign Site Roles* (either through the Actions menu or the menu at the top),\nyou are taken to the *Assign Site Roles* screen. From here:\n\n1.  Select the Roles that you want to assign to the selected users.\n\n2.  Click *Done*.\n\n![Figure 4: Assigning Site Roles.](../../../../images/assigning-site-roles.png)\n\nSite Roles are created at a global level, but when they're assigned they only\nprovide privileges for the specific Site where they were assigned. Since Roles\nare created at a global level, they cannot be created by Site Administrators\n(since Site Administrators only have Administrator privileges for their Site).\n**Teams** allow Site Administrators to assign permissions to groups of Users\nwithin their Sites. Next, you'll look at more configuration for managing members\nof your Site.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/03-building-a-site/09-managing-site-members/03-creating-teams-to-empower-site-members.markdown",
    "content": "---\nheader-id: creating-teams-for-advanced-site-membership-management\n---\n\n# Creating Teams to Empower Site Members\n\n[TOC levels=1-4]\n\nIf you have an *ad hoc* group of Users who perform the same set of tasks in\na Site, you can organize them into Site Teams, and then assign the team\npermissions for various site-specific functions. Site Teams are the preferred\nmethod for collecting permissions within a single Site. Some common functions to\nassign a Site Team include\n\n- Moderating site Wiki content\n- Managing Message Boards threads\n- Writing blogs\n- Editing a specific page in the site\n\nIf your Site has Message Boards, you might want to enable a subset of the \nSite's members to moderate the categories and threads, and perhaps to ban\nabusive/offensive posters. To do this, you could create a Site Team named\n*Lunar Resort Message Board Moderators*, define the team's permissions in the\nMessage Boards application, and assign the desired Site members to the team. \n\nThe permissions assigned to a Site Team only apply to that Site. The two key\nfeatures of Teams are that they are limited to their Sites and that they empower\nSite Administrators to manage permissions for their Sites since Site\nAdministrators cannot create new Roles.\n\n| **Note:** To create and apply permissions for a group of users to use across\n| multiple Sites or Organizations in your Liferay instance, consider aggregating\n| the Users into a [User Group](/docs/7-2/user/-/knowledge_base/u/user-groups)\n| and assigning the User Group permissions via\n| [Roles](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions).\n\nTo create a team within a Site,\n\n1.  Go to the Site Administration page of your Site.\n\n2.  Select *People* &rarr; *Teams*.\n\n    It's important to note that configuring other Site membership groupings, \n    such as *Users*, *Organizations*, and *User Groups* can be done in the \n    *Site Memberships* app, which is also in the Members tab. You can \n    visit [User Management](/docs/7-2/user/-/knowledge_base/u/managing-users)\n    for more information on how Site memberships work.\n\n3.  Finally, click the *Add Team* icon (![Add Team](../../../../images/icon-add.png)).\n\n    ![Figure 1: Creating teams within your site can foster teamwork and collaboration, as team permissions enable team members to access the same resources and perform the same types of tasks.](../../../../images/creating-a-team.png)\n\n4.  Enter a name and a description and click *Save*. Your new team shows in the list.\n\n5.  To add members, click on the team name link and then select *Add Team \n    Members*.\n\nTo manage a team's permissions, click the *Actions* icon\n(![Actions](../../../../images/icon-actions.png)) and select *Permissions* for\nthat team. Setting permissions for the team assigns those permissions to all the\nteam's members. Only administrators who can edit/manage the team can manage team\npermissions.\n\nIf you created a team whose task is to moderate the Message Boards, for example,\nyou'd want to give the team all the permissions they'd need.\n\n1.  Go to *Site Administration* &rarr; *Content & Data* &rarr; *Message Boards*.\n\n2.  Select *Home Category Permissions* from the *Options* icon\n    (![Options](../../../../images/icon-options.png)) in the top right of the \n    screen.\n\n3.  Find the Team in the Role column and select the appropriate permissions.\n\n![Figure 2: The Lunar Resort Message Board Moderators Site Team has unlimited permissions on the Message Boards application.](../../../../images/site-team-permissions-message-boards.png)\n\nThat's it! It's easy to give groups of Site Users permissions to perform their\ntasks. \n\nThese tutorials have introduced you to @product@ Site management. You've learned\nhow to use @product@ to create multiple Sites with different membership types.\nYou've also seen how easy it is to create and manage Sites and to create and\nmanage pages within Sites. Next, you'll begin working with web content.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/01-intro.markdown",
    "content": "---\nheader-id: managing-content\n---\n\n# Managing Content\n\n[TOC levels=1-4]\n\nThere are two primary ways to manage and display content in @product@\n\n- Web Content\n- Content Sets\n\nThe Web Content framework helps users who are not web developers publish content\nwith a simple point and click interface, while enabling developers to create\ncomplex templates with dynamic elements. \n\nA Content Set defines a list of content, and then that list can be displayed.\n\nContinue on to learn more about managing content!\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/01-managing-web-content-intro.markdown",
    "content": "---\nheader-id: managing-web-content\n---\n\n# Managing Web Content\n\n[TOC levels=1-4]\n\nWeb Content Management (WCM) helps users who are not web developers publish\ncontent with a simple point and click interface, while enabling developers to\ncreate complex templates with dynamic elements. Once these templates have been\ndeployed into @product@, your non-technical users can use them to manage complex\ncontent as easily as they would manage basic content.\n\nIt has these components: \n\n**Web Content Editor:** A complete HTML editor for modifying fonts, adding\ncolor, inserting images, and much more.\n\n**Structure Editor:** Define fields for structured content and more advanced\ndesigns.\n\n**Template Editor:** Import template script files or create your own template to\ninform the system how to display the content within the fields determined by the\nstructure.\n\n**Web Content Display:** Place web content on pages in your Site.\n\n**Asset Publisher:** Aggregate and display different types of content together\nin one view. This is covered in more detail in \n[Publishing Assets](/docs/7-2/user/-/knowledge_base/u/publishing-assets).\n\n**Scheduler:** Schedule when content is reviewed, displayed or removed. This\nis covered in more detail in \n[Scheduling Web Content Publication](/docs/7-2/user/-/knowledge_base/u/scheduling-web-content-publication).\n\n**Workflow Integration:** Run your content through a review process. This is\ncovered in more detail in the\n[Workflow](/docs/7-2/user/-/knowledge_base/u/workflow) section.\n\n**Staging:** Use a separate staging server or stage your content locally so you\ncan keep your changes separate from the live site. This is covered in more\ndetail in the [Staging](/docs/7-2/user/-/knowledge_base/u/staging) section.\n\nThese tools streamline the content creation process for end users and are also\nintegrated with Liferay's services so advanced template developers can use them\nto query for data stored elsewhere on your website.\n\nTo demonstrate @product@'s Web Content Management features, you'll create\nand manage content on Liferay for the ambitious (and fictitious) *Lunar Resort*\nproject. The Lunar Resort project specializes in facilitating lunar vacations.\nIt provides space shuttle transportation from the Earth to the Moon and back,\noffers the use of a state-of-the-art recreational facility enclosed by a large,\ntransparent habitat dome, and even rents out lunar rovers.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/02-publishing-basic-web-content/01-publishing-basic-web-content-intro.markdown",
    "content": "---\nheader-id: publishing-basic-web-content\n---\n\n# Publishing Basic Web Content\n\n[TOC levels=1-4]\n\nWeb Content is one of many different kinds of assets, along with blog posts,\nwiki articles, message board posts, and other kinds of content. Like all of\nthese assets, @product@ handles Web Content using an asset framework that\nincludes categories, tags, comments, ratings and more. Please see\n[Publishing Content Dynamically](/docs/7-2/user/-/knowledge_base/u/publishing-content-dynamically)\nfor more information on Liferay's asset framework.\n\nTo start working with Web Content, publish some basic material using Web\nContent Management's WYSIWYG editor. Then you can cover the editor's features\nin greater depth and learn how to publish the content you create to a page.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/02-publishing-basic-web-content/02-creating-web-content.markdown",
    "content": "---\nheader-id: creating-web-content\n---\n\n# Creating Web Content\n\n[TOC levels=1-4]\n\nTo start you'll create and publish some simple content using the WYSIWYG\neditor to the home page of the Lunar Resort's web site. \n\n1.  Go to *Site Administration* and click the *Site Selector* button\n    (![Compass](../../../../../images/icon-compass.png)).\n\n    Content is created in whichever Site is selected, so always make sure that\n    you're working on the right Site. Content can also be created as Global so\n    that any Site can access it.\n\n    ![Figure 1: You can choose where to create content by navigating to the Site Administration menu and selecting your Site and page scope.](../../../../../images/site-page-scopes.png)\n\n2.  Select the *Lunar Resort* Site.\n\n3.  Open the *Content & Data* section and click on *Web Content*.\n\n    Here you can create web content and organize it into folders.\n\n4.  Click *Add* (![Add Web Content](../../../../../images/icon-add.png)) &rarr; \n    *Basic Web Content* to create a new web content article.\n\n    ![Figure 2: By default, *Basic Web Content* is the only article type available. The next tutorial covers how to create new types.](../../../../../images/web-content-add-menu.png)\n\n5.  Type *Welcome to the Lunar Resort* in the top *Title* field.\n\n6.  In the *Summary* field under the Basic Information tab on the right side,\n    give a short description of the Lunar Resort's facilities (be creative).\n\n7.  In the *Content* field, add the body of your web content article, which\n    you'll dive into next.\n\n8.  Click *Publish*.\n\nThat's all it takes to create Web Content, but there's much more under the \nhood of the web content editor that you'll learn about next.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/02-publishing-basic-web-content/03-using-the-web-content-editor.markdown",
    "content": "---\nheader-id: using-the-wysiwyg-editor\n---\n\n#  Using the Web Content Editor\n\n[TOC levels=1-4]\n\nIn the previous article, you created a simple web content article, but the Web\nContent editor can do much more than make plain text articles.\n\n## Basic Editor Functions\n\nTo explore these options, go back to the article you just created and make it \nbetter:\n\n1.  On the *Web Content* page in *Site Administration*, click on the title of\n    the article.\n\n2.  Highlight the text you entered in the *Content* field.\n\n    A number of controls appear. These let you style the text or provide a link.\n\n3.  Click on the arrows where it says *Normal* to open the *Styles* dropdown \n    and select the *Heading 1* style.\n\nNext, you'll add an image to the article. Whenever you place your cursor in the\n*Content* area, the *Add* icon (\n![WYSIWYG Add](../../../../../images/icon-wysiwyg-add.png)) appears. If you click\non it, controls for inserting an image, video, table, or horizontal line\n(![Controls](../../../../../images/icon-content-insert-controls.png)) appear.\n\n1.  Click *Add* (![WYSIWYG Add](../../../../../images/icon-wysiwyg-add.png)).\n\n2.  Select the icon that depicts a mountain silhouette to insert the image.\n\n3.  In the image file selector, select an image to add to the article.\n\n    Select an image from your computer or from the Site's Documents and Media\n    repository. If you select one from your Documents and Media repository, you\n    can access the \n    [image editor](/docs/7-2/user/-/knowledge_base/u/editing-images) to make\n    changes specifically for your article.\n\n![Figure 1: You can access the image editor through the item selector window.](../../../../../images/image-editor-preview-window.png)\n\nAfter adding an image to the web content article, click it to bring up controls\n(![Image Controls](../../../../../images/icon-wysiwyg-image-controls.png)) for\nformatting it. You can also make it a link.\n\nThe same way you inserted an image in to the article, you can also insert\na table. Click the table to access edit controls, which let you designate\nthe first row and/or column as table headers, and also enable you to add rows,\ncolumns, and cells.\n\nIn addition to images and tables, you can insert a horizontal line as\na separator between between sections. You can also add video by providing a URL.\n\n## Editing the Article Source\n\nIf you need to work directly with the HTML, you can switch to source view. \n\n1.  With your cursor in the *Content* field, select the *Source* icon (\n    ![WYSIWYG Source](../../../../../images/icon-wysiwyg-source.png)) to switch. \n\n2.  Click the regular mode icon (\n    ![Regular Mode](../../../../../images/icon-text.png)\n    ) to go back once you're done editing HTML.\n\nThe HTML editor highlights syntax, and you can switch between a dark and light\ntheme by choosing the moon and sun icons.\n\nIn HTML mode, click on the *Fullscreen* icon\n(![Fullscreen](../../../../../images/icon-enlarge.png)) to access a dual pane view\nthat shows your HTML code on the left and a preview pane on the right. You can\narrange the HTML and preview panes horizontally or vertically.\n\n![Figure 2: You can view how your HTML would render by using the preview pane.](../../../../../images/web-content-editor-html.png)\n\nYou can exit the enlarged editor by clicking the *Done* button at the bottom of\nthe screen.\n\n1.  Add a few short sentences announcing the grand opening of the Lunar Resort. \n\n2.  Click *Save as Draft*.\n\n    Be sure to save your content frequently, because it is not auto-saved. \n\nThe content can be localized in whatever language you want. You'll learn more \nabout localizing your content later.\n\n## Web Content Options\n\nThe right-side menu of the New Web Content form provides options for customizing\nyour web content. It's organized into two tabs: *Properties* (basic \nconfiguration properties) and *Usages* (Where the web content is used on the \nsite). Note that the *Usages* tab is only visible if you're editing existing web \ncontent that's been added to a page. \n\n![Figure 3: New web content can be customized in various ways using the menu located to the right of the editor.](../../../../../images/wcm-menu.png)\n\nThe available properties are listed below:\n\n**Basic Information:** Provide a summary for the web content article.\n\n**Default Template:** Customize the web content article's template if it has \none. To learn more about web content templates, see \n[Designing Uniform Content](/docs/7-2/user/-/knowledge_base/u/designing-uniform-content).\n\n**Display Page Template:** Select a display page template to enhance the styling\nand formatting of your web content.\n\nFor example, if you had a news site with different sections---Sports,\nTechnology, Culture---you could create a display page for each section with\nunique banners, formatting, embedded widgets, or other features. By selecting\na display page, you would ensure that content appears on the page with the\nappropriate features. You'll work through an example of creating a display page\nin the\n[Display Pages for Web Content](/docs/7-2/user/-/knowledge_base/u/display-pages-for-web-content)\ntutorial.\n\nIf a display page template is configured, you can preview what it will look like \nwith the *Preview* (![Preview Template](../../../../../images/icon-preview.png)) \nbutton located next to the selected display page template.\n\n**Featured Image:** Set the image that is used for the web content article's\npreviews. You can set this image from a URL or your computer. If you don't want\na feature image, choose *No Image*.\n\n**Metadata:** Organize web content articles by selecting tags, categories, and\npriority. To learn more about tags and categories, see \n[Organizing Content with Tags and Categories](/docs/7-2/user/-/knowledge_base/u/organizing-content-with-tags-and-categories).\n\n**Friendly URL:** Set the friendly URL where the article can be viewed alone. If\na specific display page is set, the URL links to it.\n\n**Schedule:** Customize the date and time your content publishes and/or expires.\nTo learn more about scheduling content, see \n[Scheduling Web Content Publication](/docs/7-2/user/-/knowledge_base/u/scheduling-web-content-publication).\n\n**Search:** Disabling search for your article removes it from end users' search\nresults. Administrators can still search for it from *Site Administration*\n&rarr; *Content & Data* &rarr; *Web Content*, and the article can still be added\nto pages.\n\n**Related Assets:** Determine relationships between the web content article and\nother assets, even if they don't share any tags and aren't in the same category.\nYou can connect your content to any asset that implements the Related Assets\nfeature. To learn more about defining content relationships and publishing links\nto those related assets, see \n[Defining Content Relationships](/docs/7-2/user/-/knowledge_base/u/defining-content-relationships).\n\n![Figure 4: This blog entry has links to two Related Assets: an article and a message board thread.](../../../../../images/related-assets-link.png)\n\n**Permissions:** Customize who has access to the content. By default, content\nis viewable by Anyone (Guest Role). You can limit viewable permissions by\nselecting any Role from the drop-down or in the list. You can customize \npermissions in more detail by selecting the *More Options* link below the drop \ndown button. \n\nIf your permissions are ignored, you must activate the Web Content Article \npermissions in your System Configuration. This is enabled by default: \n\n1. Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings*.\n\n2. Search or browse for *Web Content*.\n\n3. Check the box labeled *Article View Permissions Check Enabled* under the\n    *Virtual Instance Scope* &rarr; *Web Content* tab.\n\n4. Click *Save*.\n\nOnce it is activated, any permissions you set in the article's configuration are\nchecked before displaying the article.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/02-publishing-basic-web-content/04-displaying-web-content.markdown",
    "content": "---\nheader-id: publishing-web-content\n---\n\n# Publishing Web Content\n\n[TOC levels=1-4]\n\nIn the previous sections, you created and edited an article. Now it's time to \npublish it. \n\n1.  Go to the *Welcome* page of the Lunar Resort Site.\n\n2.  Select the *Add* button (![Add](../../../../../images/icon-add.png)) from\n    the top Control Menu and select the *Widgets* tab.\n\n3.  Find *Web Content Display* and add it to the page.\n\n![Figure 1: Add the Web Content Display app to a page to begin displaying your new web content article.](../../../../../images/add-web-content-display.png)\n\nYou can drag a widget to the position on the page where you want your content \nto appear. You can have as many Web Content Display widgets on a page as you \nneed, which gives you the power to lay out your content exactly the way you \nwant it. \n\nNow select the content to display:\n\n1.  Click *Select Web Content to make it visible* in the bottom of the widget.\n\n2.  Click *Select* under *Web Content*.\n\n3.  Click on the article that you want to display\n\n    If your content does not immediately appear in the list, you can search for \n    the content by title, description, user name, or Site (click the drop-down \n    arrow to see all the options).\n\nSelecting a web content article displays the Web Content Display's\nconfiguration page, where you can choose the User Tools and Content Metadata to\nbe published in the widget. These two entities have the following options to\nchoose from, by default:\n\n- **User Tools**\n    - *Translations*\n    - *Print*\n- **Content Metadata**\n    - *Related Assets*\n    - *Ratings*\n    - *Comments*\n    - *Comment Ratings*\n\nIf you have enabled OpenOffice/LibreOffice integration, you can also enable\ndocument conversion for your content. Then users can download your content in\ntheir format of choice. To enable OpenOffice/LibreOffice integration, go to\n*Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr;\n*Connectors* and check the *Server Enabled* box. Back in the Web Content\nDisplay's configuration page, conversion options are available under the *User\nTools* list.\n\n![Figure 2: Publishing web content is a snap. At a minimum, you only have to select the content you wish to publish. You can also enable lots of optional features to let your users interact with your content.](../../../../../images/web-content-choosing-web-content.png)\n\n**Translations:** Shows the available locales for your content. If you're working \non the page for a particular language, you can select the translation of your \ncontent that goes with your locale.\n\n**Print:** Opens the content in a separate browser window with just the\ncontent---no navigation or other widgets.\n\nBy default, guests cannot leave comments on web content. If you want to allow\nguests to comment on your web content article,\n\n1.  Navigate to the Control Panel &rarr; *Users* &rarr; *Roles*\n\n2.  Select *Guest* &rarr; *Define Permissions*.\n\n3.  From the left menu, select *Site Administration* &rarr; *Content & Data*\n    &rarr; *Web Content*.\n\n4.  Navigate down to the Web Content Article heading and select the *Add \n    Discussion* checkbox. Click *Save*.\n\nGuests can now post comments on your web content article!\n\nYou may decide you want one, some, or none of these features, which is why\nthey're all implemented as simple selector buttons to be enabled or disabled at\nneed. Once you've selected the features you want to include in your Web Content\nDisplay widget, click *Save* and close the configuration window.\n\n## Editing Published Content\n\nIf you must edit published content, you can do it directly from the Web \nContent Display app or from Site Administration. To edit it from\nthe Web Content Display app,\n\n1.  Select the *Options* button \n    (![Options](../../../../../images/icon-app-options.png)) from the widget's top \n    panel.\n\n2.  Select *Edit Web Content* to launch the editor. Select *Edit Template* to\n    launch the template editor for the web content article's template if it has \n    one.\n\nIf you edit the article from Site Administration, you can also view the\narticle's history and use the diff tool to compare versions.\n\n1.  Go to *Content & Data* &rarr; *Web Content* from the Product Menu.\n\n2.  Next to the article, click *Actions* icon \n    (![Actions](../../../../../images/icon-actions.png)) and select *View\n    History*. This shows you \n\nThis shows you all the article's versions and modified/display dates. The diff\ntool compares these versions and highlights the differences between them.\n\n1.  Click *Actions* next to a version of the article you'd like to compare. \n\n2.  Select *Compare to...*.\n\n3.  Select the version with which to compare it.\n\n    The tool provides color coded highlighting to emphasize additions and deletions between the two articles.\n\n![Figure 3: Comparing web content articles is a great feature to use during the Workflow process.](../../../../../images/web-content-diff-feature.png)\n\nWhenever you publish updates to a web content article that's already being\ndisplayed, the content is immediately updated unless you have a workflow\nenabled (see [Workflow](/docs/7-2/user/-/knowledge_base/u/workflow) for\ndetails).\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/02-publishing-basic-web-content/05-other-content-options.markdown",
    "content": "---\nheader-id: other-content-options\n---\n\n# Other Content Options\n\n[TOC levels=1-4]\n\nHere are some options and tools that you can use to enhance your content and\nuser experience.\n\n## Localizing Content\n\nWhen you create a new web content article, you can choose a default language.\nFirst, you must change the system configuration to enable the option to change\nthe default language.\n\n1.  Go to the *Control Panel* &rarr; *Configuration* &rarr; *System Settings*. \n\n2.  Locate *Web Content* &rarr; *Administration* by scrolling or using the \n    search bar.\n\n3.  Check the box labeled *Changeable Default Language*.\n\n4.  Click *Save*.\n\nYou must now add translations for any languages you need. Adding translations\nworks like this:\n\n1.  Open a web content article.\n\n2.  Click the flag icon with a country code on it next to any localizable web \n    content field.\n\n3.  Select a language from the list.\n\nWhen you select a language, all fields in the article switch to the new\nlanguage. To create the new translation, fill in the fields in the selected\nlanguage and publish the article. \n\n![Figure 1: Adding a translation to an article works like adding the default translation.](../../../../../images/web-content-translation.png)\n\n| **Note:** To view localizable fields in a given language, you must have your \n| Portal set to that language. This includes friendly URLs for the web content as \n| well. When you navigate to the localized friendly URL \n| (e.g. `http://localhost:8080/web/guest/-/espanol`), the web content is always \n| displayed in the current language. You can change the language with the \n| [Language Selector app](/docs/7-2/user/-/knowledge_base/u/personalizing-pages#customization-example).\n\nYou can modify the language translation list by inserting `locales.enabled=`\nfollowed by your preferred languages in your `portal-ext.properties` file. For\nexample, `locales.enabled=ar_SA,nl_NL,hi_IN` offers *Arabic (Saudi Arabia)*,\n*Dutch (Netherlands)*, and *Hindi (India)*.\n\n| **Warning:** If you switch your Site's default language (e.g., via friendly\n| URL), but do not have the necessary translations for localizable fields, your\n| Site's language values are used from the old default language. Therefore, you\n| should change the default language of your Site *only* when you have translated\n| values for all localizable entities.\n\nWhen you create a new web content structure, each field you create has a\n*Localizable* checkbox displayed next to it. This enables you to control what\ncan and can't be changed in the translation process. For example, if you don't\nwant images or content titles to be changed when the content is translated, you\ncan make sure those fields aren't localizable. When you follow the steps above\nto localize content, only fields within the structure that had the *Localizable*\nbox checked appear within the translation window.\n\n## Xuggler for Embedding Video\n\nXuggler is a tool which generates video previews and makes it possible to embed\nvideos from your Documents and Media library in web content and elsewhere on the\nsite. To enable Xuggler,\n\n1.  Navigate to the *Control Panel*.\n\n2.  Click on *Configuration* &rarr; *Server Administration* &rarr; *External Services*.\n\n3.  Scroll to the bottom and click *Install* in the *Xuggler* section.\n\n    This downloads the necessary libraries and prompts you to restart the server\n    to enable Xuggler.\n\n4.  After the server restarts, you can enable Xuggler from the same page.\n\nOnce Xuggler has been installed and enabled, you can embed a video or audio \nfile in a web content article the same way you added images previously. \n\n![Figure 2: If you've installed and enabled Xuggler from the *Server Administration* &rarr; *External Tools* section of the Control Panel, you can add audio and video to your web content!](../../../../../images/web-content-audio-video.png)\n\n## XML Format Downloads\n\nTools like the \n[Resource Importer](/docs/7-2/frameworks/-/knowledge_base/f/importing-resources-with-a-theme)\nand Site Initiators can be deployed to build a site almost instantly. Before you\ncan use them to import Web Content, however, you first need to have the content\nexported individually in XML format. To export the content,\n\n1.  Go to *Site Administration* &rarr; *Content & Data* &rarr; *Web Content*.\n\n2.  Start editing the article you want to download.\n\n3.  Click the *Options* icon (![Options](../../../../../images/icon-options.png)) in \n    the top right of the page and select *View Source*.\n\nThis displays the raw XML source of the article. You can copy this content to \nsave into an XML file locally.\n\n![Figure 3: The *View Source* button is available from the *Options* button.](../../../../../images/web-content-download.png)\n\n## Subscribing to Content\n\nAn administrator or web content writer can subscribe to an article or folder to\nfollow changes being made to it. \n\n1.  Go to *Content & Data* &rarr; *Web Content* for your Site.\n\n2.  Click *Actions* \n    (![Actions](../../../../../images/icon-app-options.png)) &rarr; *Subscribe*\n    next to the article or folder you want to follow.\n\nAnytime an asset that you follow is modified, you receive an email notifying you\nof the change.\n\n![Figure 4: Click the Subscribe icon in the web content entity's *Options* menu to begin receiving web content notifications.](../../../../../images/web-content-subscribe.png)\n\nThat's pretty much all there is to basic content creation. Whole sites have\nbeen created this way. But if you want to take advantage of the full power of\n@product@'s WCM, you'll want to use structures and templates or Fragments.\nYou'll cover these topics next.\n\n## Organizing Structure Names\n\nBy default, when you select a structure to add a new Web Content article, the\nstructures are ordered by their IDs, not their names. This can be confusing,\nbut---never fear---there's a configuration property to sort them alphabetically.\n\n![Figure 5: The default ordering for Web Content Structures can yield confusing results.](../../../../../images/web-content-default-order.png)\n\nTo enable this property for Site Administration,\n\n1.  Go to *Configuration* &rarr; *System Settings* &rarr; *Web Content* &rarr; \n    *Web Content Administration*.\n\n2.  Check the box labeled *Journal Browse by Structures Sorted by Name*.\n\n![Figure 6: Web Content Administration will now display structures in alphabetical order.](../../../../../images/web-content-admin-alphabetical.png)\n\nYou can also set this property for the Web Content Display widget. To enable\nthis property for the Web Content Display,\n\n1.  Go to *Configuration* &rarr; *System Settings* &rarr; *Web Content* &rarr; \n    *Web Content Display*.\n\n2.  Check the box labeled *Sort Structures by Name*.\n\nAfter this option is checked, the structures are sorted alphabetically. Note \nthat enabling this property can degrade performance with large structure \nlibraries.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/03-designing-uniform-content/01-designing-structured-content-intro.markdown",
    "content": "---\nheader-id: designing-uniform-content\n---\n\n# Designing Structured Content\n\n[TOC levels=1-4]\n\nIf you've ever launched a web site, you know that as it grows, you can\nexperience growing pains. This is the case especially if you've given lots of\npeople access to the site to make whatever changes they need to make. Without\npreset limitations, users can display content in any order and in any manner\nthey desire (think huge, flashing letters in a font nobody can read). Content\ncan get stale, especially if those responsible for it don't maintain it like\nthey should. And sometimes, content is published that should never have seen the\nlight of day.\n\n<!-- No video at the current time.\n\ndiv class=\"video-link\"\nimg alt=\"video thumbnail\" src=\"../../../../images/vid-struc-temp-thumbnail.png\" />\n/div>\n-->\n\nThankfully, Web Content Management helps you handle all of these situations. You\ncan use *Structures* to define which fields are available to users when they\ncreate content. These can be coupled with *Templates* that define how to display\nthat content. Content won't get stale, because you can take advantage of the\n[Scheduling](/docs/7-2/user/-/knowledge_base/u/scheduling-web-content-publication)\nfeature to determine when content is displayed and when it's removed.\nAdditionally, you can configure @product@'s built-in\n[Workflow](/docs/7-2/user/-/knowledge_base/u/workflow) system to set\nup a review and publishing process so only what you want winds up on the live\nsite. This gives you what you need to run everything from a simple, one-page web\nsite to an enormous, content-rich site.\n\nAll of this starts with structures.\n\n<!--\ndiv class=\"video-tag\" data-name=\"Creating Content with Structures and Templates\">\n  video width=\"100%\" height=\"100%\" controls>\n    source src=\"https://portal.liferay.dev/documents/113763090/113919826/creating-content-with-structures-and-templates.mp4\" type=\"video/mp4\">\n    source src=\"https://portal.liferay.dev/documents/113763090/113919826/creating-content-with-structures-and-templates.webm\" type=\"video/webm\">\n    Your browser does not support HTML5 video.\n  /video>\n/div>\n-->\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/03-designing-uniform-content/02-creating-structured-content/01-creating-structured-web-content-intro.markdown",
    "content": "---\nheader-id: creating-structured-web-content\n---\n\n# Creating Structured Web Content\n\n[TOC levels=1-4]\n\nStructures are the foundation for web content. They determine which fields are\navailable to users as they create new items for display. Structures not only\nimprove manageability for the administrator, they also make it much easier for\nusers to add content quickly. \n\nFor example, say you're managing an online news magazine. All your articles must\ncontain the same types of information: a title, a subtitle, an author, and one\nor more pages of text and images that comprise the body of the article. With\nonly basic content creation, you'd have no way to make sure your users entered\na title, subtitle, and author. You might also get articles that don't match the\nlook and feel of your site. If titles are supposed to be navy blue but they come\nin from your writers manually set to light blue, you must spend time\nreformatting them before they are published.\n\nStructures enforce a format for your content so your writers know exactly what\na complete article or mapped display page needs. Using structures, you can \nprovide a form for your users which spells out exactly what is required and can \nbe formatted automatically using a template or a [display page](/docs/7-2/user/-/knowledge_base/u/display-pages-for-web-content).\n\nYou create a structure by adding form controls such as text fields, text boxes,\nHTML text areas, check boxes, select boxes and multi-selection lists. You can\nadd specialized application fields such as an Image Uploader or Documents and\nMedia right onto the structure. Positioning elements is accomplished by\ndrag-and-drop, making it easy for you to prototype different orders for your\ninput fields. Additionally, elements can be grouped together into blocks which \ncan then be repeatable. Display Page creators can then map these fields to \n[editable page fragments](/docs/7-2/user/-/knowledge_base/u/content-page-elements#editable-elements) \nto use custom styles and formatting. Alternatively, template writers can write a \ntemplate which loops through these blocks and presents your content in \ninnovative ways, such as in sliding navigation bars, content that scrolls with \nthe user, and more.\n\nNext you'll learn how you can create and edit structures through the Manage\nStructures interface.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/03-designing-uniform-content/02-creating-structured-content/02-editing-structures.markdown",
    "content": "---\nheader-id: editing-structures\n---\n\n# Editing Structures\n\n[TOC levels=1-4]\n\nTo start, go to the *Structures* page.\n\n1.  From *Site Administration* go to *Content & Data* &rarr; *Web Content*.\n\n2.  Open the *Structures* tab.\n\nThis page shows you all the web content structures in this Site. You can add\nnew web content structures, edit existing ones, manage the templates associated\nwith a structure, edit the permissions of a structure, and copy or delete\nstructures.\n\n![Figure 1: Structures are not pre-installed. You have to make your own.](../../../../../../images/manage-structures.png)\n\n| Note: When you copy a structure, @product@ generates a unique ID for the copied\n| structure, but every other attribute of the copied structure, including the\n| name, is the same as that of the original. When you copy web content structure,\n| enter a new name for it to avoid confusing it with the original. During the\n| copy process, you're prompted to choose whether to copy any detail templates or\n| list templates associated with the structure. For information on detail\n| templates and list templates, please refer to\n| [Dynamic Data Lists](/docs/7-2/user/-/knowledge_base/u/dynamic-data-lists).\n\n*Basic Web Content*, which you used in previous exercises, lives at the *Global* \nscope so that it is available to all Sites. This structure and template are \nused automatically if a custom structure and template are not added.\n\n## Structure Fields\n\nNow, create a new Structure:\n\n1.  Click *Add* (![Add Structure](../../../../../../images/icon-add.png)).\n\n2.  Give your Structure a name.\n\nStructures are essentially a set of fields organized in a certain way. The\ninterface on this page provides an easy way to add and organize whatever fields\nyou need. Each element that you add has three icon options that you can click:\n\n**Settings:** (![Settings](../../../../../../images/icon-wrench.png)) Changes the\nname and label and set other information about the field, like whether or not\nit is required. \n\n**Delete:** (![Delete](../../../../../../images/icon-trash.png)) Removes the field \nfrom the structure.\n\n**Duplicate:** (![Duplicate](../../../../../../images/icon-wysiwyg-add.png)) Duplicates the \nfield and all its settings and iterates the *Name* to avoid conflicts.\n\nWeb content structures can inherit characteristics from other structures. A \nchild structure inherits all the parent's fields and settings. You can use this \nto make a similar structure to one that already exists. For example, if you \nhave *Sports Article* and you want to create *In-depth Sports Article*, set \n*Sports Article* as the parent and the *In-dept Sports Article* inherits\nall its fields, letting you add new ones for more in-depth information.\n\n| **Note:** Due to import/export operations, it's possible to have both a global\n| and a Site-scoped structure with the same `structureKey`. If this happens, the\n| Site-scoped structure takes precedence, and you can't access the global\n| structure from that Site.\n\nYou can also manually customize a structure's XML in *Source* mode. By default \nthe *View* mode is selected, but you can click the *Source* tab to switch. This \nmethod is for more experienced developers.\n\nTake a moment to add, delete, and rearrange different elements.\n\n![Figure 2: The structure editor gives you many options to customize your Web Content.](../../../../../../images/web-content-structure-editor.png)\n\nThe following fields can be in structures:\n\n**Boolean:** Adds a checkbox onto your structure, which stores either `true`\n(checked) or `false` (unchecked). Template developers can use this as a display\nrule.\n\n**Color:** Adds a way to choose a color.\n\n**Date:** Adds a pre-formatted text field that displays a date picker to assist\nin selecting the desired data. The format for the date is governed by the\ncurrent locale.\n\n**Decimal:** Similar to *Number*, except that it requires a decimal point (.) be\npresent.\n\n**Documents and Media:** Adds an existing uploaded document to attach to the\nstructure. Can also upload documents into the Document Library.\n\n**Geolocation:** Adds a map that displays a configured location. The geolocation\nsystem can work in two ways: letting the system know your current location\n(especially useful on mobile devices) and giving the user directions to a\nanother place.\n\n**HTML:** An area that uses a WYSIWYG editor to enhance the content.\n\n**Image:** Adds the browse image application into your structure. You can\nselect an image from the Documents and Media library or upload an image from\nyour computer's storage. If uploading an image from your personal computer to\nthe web content article, it is only available for that article.\n\n**Integer:** Similar to *Number*, except that it constrains user input to\nwhole numbers.\n\n**Link to Page:** Inserts a link to another page in the same site.\n\n**Number:** Presents a text box that only accepts numbers as inputs, but puts no\nconstraints on the kind of number entered.\n\n**Radio:** Presents the user with a list of options to choose from using radio\nbutton inputs.\n\n**Select:** Presents a selection of options for the user to choose from using a\ncombo box. Can be configured to allow multiple selections, unlike *Radio*.\n\n**Separator:** Adds a horizontal line between fields.\n\n**Text:** Used for items such as titles and headings.\n\n**Text Box:** Used for the body of your content or long descriptions.\n\n**Web Content:** Provides a way to select a web content article.\n\nThese fields provide all you need to model any information type you would\nwant to use as web content. Liferay customers have used structures to model\neverything from articles, to video metadata, to wildlife databases.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/03-designing-uniform-content/02-creating-structured-content/03-configuring-structure-fields.markdown",
    "content": "---\nheader-id: configuring-structure-fields\n---\n\n# Configuring Structure Fields\n\n[TOC levels=1-4]\n\nThere are many options available for configuring each structure field. Some of\nthem relate to how the fields are displayed or how users interact with them, but\nprobably the most important field configuration is the **Name**. When you create\na new field, it has a random name generated that looks like `TextField4882`. In\nmost cases, you should change this to something that is more memorable and more\ndescriptive. If you create a matching template, you don't want to have to \nremember if `TextField4882` was the field for entering an applicant's name or \nannual salary.\n\nPractice this now.\n\n1.  In your structure, add an *HTML* element.\n\n2.  Hover over the field and select the *Configuration* icon \n    (![Configuration](../../../../../../images/icon-wrench.png)).\n\n3.  Change the *Field Label* value to *Instructions* and the *Name* value to\n    *steps*. Now your template writer has a variable by which he or she can\n    refer to this field.\n\nHere's a list of all the configurable settings available for a structure's\nfields:\n\n**Type:** Lists the type of field placed in the definition. This is not editable\nbut is available to reference from a template.\n\n**Field Label:** Sets the text that can be displayed with the field. This is the\nhuman-readable text that the user sees.\n\n**Show Label:** Select *Yes* to display the Field Label.\n\n**Required:** Select *Yes* to mark the field required. If a field is required,\nusers must enter a value for it in order to submit content using this structure.\n\n**Name:** The name of the field internally, automatically generated. Since this\nis the variable name that you can read the data from in a template or display \npage, you should enter a descriptive name.\n\n**Predefined Value:** When a user creates a new web content article based on a\nstructure that has predefined values for various fields, the predefined values\nappear in the form as defaults for those fields.\n\n**Tip:** Each field can have a small help icon, with a tooltip attached that\ndisplays helpful information. If you want to provide text for the tooltip, you\nmay enter it here.\n\n**Indexable:** Select *Yes* to permit your field to be indexed for search.\n\n**Localizable:** Select *Yes* to permit localization for this field.\n\n**Repeatable:** Select *Yes* to make your field repeatable. Users can then add\nas many copies of this field as they need. For example, if you're creating\na structure for articles, you might want a repeatable Author field in case you\nhave multiple authors for a particular article.\n\n**Multiple:** Select *Yes* to enable a multi-selection list (only available for\nthe Select field).\n\n**Options:** Changes the options available for selection. You can add and remove\noptions as well as edit each individual option's display name and value (only\navailable for Radio and Select fields).\n\n**Style:** Changes the line separator's style (only available for Separator).\n\n### Structure Default Values\n\nYou can define Structure Default Values for repeatable values in content created\nfrom that structure. They can also set defaults for Liferay's standard asset\nfields (like tags, categories, and related assets) and the content of the\nstructure fields, while also setting a default template for displaying the\nstructure data.\n\n![Figure 1: You can edit default values via the *Actions* button of the Manage Structures interface.](../../../../../../images/structure-actions.png)\n\nReturning to the newspaper scenario again, suppose you want all sports articles\nto have the same display page (sports page), the same categories, or the same\nset of tags. Instead of adding them for each article or wondering if your users\nare adding them to every web content article, you can add these characteristics\nonce for every sports article by creating default values for the structure.\nCreating default values is not part of creating a new structure, so make sure\nyou have an existing structure.\n\nTo edit a structure's default values:\n\n1.  Go to *Site Administration* &rarr; *Content & Data* &rarr; *Web Content* and\n    click on the *Structures* tab to see the structures list.\n\n2.  Find the *Actions* button (![Actions](../../../../../../images/icon-actions.png)) \n    for the desired structure and select *Edit Default Values* from the menu to \n    view a window like the one below.\n\n    This form manages the structure settings. It duplicates the function of the\n    *Predefined Value* field setting (see above), but is much more convenient\n    for setting or editing a large number of defaults at once.\n\n![Figure 2: You can define values for your structure fields and the standard asset metadata fields.](../../../../../../images/structure-default-values.png)\n\nIf you prefer to reset all your structure's default values, you can select the\n*Reset Values* button. This resets all values for the structure's fields\nand all properties shown in the right panel.\n\n| **Note:** The *Reset Values* button is available for Liferay Portal 7.2 GA2+\n| and Liferay DXP 7.2 SP1+.\n\nEvery new web content you create with this structure is preloaded with the\ndata you inserted. Next, you'll learn about assigning permissions.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/03-designing-uniform-content/02-creating-structured-content/04-structure-settings.markdown",
    "content": "---\nheader-id: structure-settings\n---\n\n# Structure Settings\n\n[TOC levels=1-4]\n\nAfter you have created a structure and configured its fields, there is\nadditional configuration to consider including permissions and remote access to\nmanaging structures.\n\n### Assigning Permissions\n\nPermissions on structures can be set just like any other \n[permission](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions).\nMost users should not be able to edit structures. Structures can be coupled with \ntemplates or used on display pages, which require some web development knowledge \nto create. This is why only trusted developers should be able to create \nstructures. Users, of course, should be able to view structures. The *View* \npermission enables them to make use of the structures to create content.\n\n![Figure 1: You're able to assign structure permissions via the *Actions* button.](../../../../../../images/web-content-structure-permissions.png)\n\nThe best practice for structure permissions is to grant or deny them based on\nRoles.\n\n### WebDAV URL\n\nThe WebDAV URL feature is available for web content structures and templates so\nusers can upload and organize resources from both a web interface and the file\nexplorer of their desktop operating system. With the WebDAV URL, site\nadministrators can add, browse, edit, and delete structures and templates on\na remote server. After you complete your structure, you can access the WebDAV\nURL by re-opening the structure or template and clicking the *Details* section.\nIf you'd like the see WebDAV in action, see \n[WebDAV Access](/docs/7-2/user/-/knowledge_base/u/desktop-access-to-documents-and-media). \n\n| **Note:** Some operating systems require a WebDAV server to be class level 2\n| (to support file locking) before allowing files to be read or written. The\n| Documents and Media library uses a class level 2 WebDAV server but Web Content\n| structures and templates do not. This means that @product@'s Document and Media\n| library supports WebDAV file locking but Web Content structures and templates do\n| not. However, on operating systems which require WebDAV servers to be class\n| level 2, it's possible to avoid the restriction by using third-party WebDAV\n| clients (e.g., [Cyberduck](http://cyberduck.ch)).\n\nNow that you understand how structures work, you're ready to understand another \nkey aspect of @product@'s web content management system: templates.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/03-designing-uniform-content/03-designing-web-content-with-templates/01-designing-web-content-with-templates-intro.markdown",
    "content": "---\nheader-id: designing-web-content-with-templates\n---\n\n# Designing Web Content with Templates\n\n[TOC levels=1-4]\n\nWhile templates aren't required to display web content (i.e. you can use a \n[Display Page Template](https://portal.liferay.dev/docs/7-2/user/-/knowledge_base/u/display-pages-for-web-content) \nto map the structure fields), developers can create templates to display the \nelements of the structure in the markup they want. In essence, templates are \nscripts that tell @product@ how to display content in the structure. Changes to \nthe structure require corresponding changes to any templates that use it, \nbecause new or deleted fields produce errors on the page. Unless a template is \ngeneric and meant to be embedded in other templates, a template must have a \nmatching structure. \n\n@product@ only supports creating web content templates in FreeMarker (FTL) by\ndefault. FreeMarker is the preferred, recommended language, and what you'll use\nin the next example.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/03-designing-uniform-content/03-designing-web-content-with-templates/02-adding-templates.markdown",
    "content": "---\nheader-id: adding-templates\n---\n\n# Adding Templates with Structures\n\n[TOC levels=1-4]\n\nTo better understand templates, now you'll create a structure and an associated\ntemplate. First create the structure:\n\n1.  Go to *Content & Data* &rarr; *Web Content* from Site Administration page\n    and open the *Structures* tab.\n\n2.  Click the *Add* button (![Add Structure](../../../../../../images/icon-add.png)).\n\n3.  Name the structure *News Article* and add the following fields:\n\n\t| Field Type | &nbsp;Field Label | &nbsp;Name |\n\t--------- | ---------- | ---------- |\n\tText  | &nbsp;Title | &nbsp;`title` |\n\tText Box | &nbsp;Abstract | &nbsp;`abstract` |\n\tImage | &nbsp;Image | &nbsp;`image` |\n\tHTML | &nbsp;Body | &nbsp;`body` |\n\n5.  Click *Save*.\n\nNow create the template and connect it to the structure.\n\n1.  From the *Web Content* page, go to the *Templates* tab.\n\n2.  Click the *Add* button (![Add Template](../../../../../../images/icon-add.png)).\n\n3.  Enter the name *News Article*.\n\n4.  Open *Details* and make sure FreeMarker is selected as the script language (it's the default).\n\n5.  Click *Select* under *Structure*.\n\n6.  Choose the *News Article* structure.\n\n7.  In the *Script* area, find the *Fields* label on the left and click on\n    *Title*, *Abstract*, *Image* and *Body* into the editor area. It should\n    look like this:\n\n    ```markup\n    ${title.getData()}\n    ${abstract.getData()}\n    <#if image.getData()?? && image.getData() != \"\"> <img alt=\"${image.getAttribute(\"alt\")}\" data-fileentryid=\"${image.getAttribute(\"fileEntryId\")}\" src=\"${image.getData()}\" /> </#if>\n    ${body.getData()}\n    ```\n\n8.  Next, add heading and `<p>` tags and align the image to center to  style your\n    elements like this:\n\n    ```markup\n    <h1>${title.getData()}</h1>\n    <p>${abstract.getData()}</p>\n    <#if image.getData()?? && image.getData() != \"\"> <img alt=\"${image.getAttribute(\"alt\")}\" data-fileentryid=\"${image.getAttribute(\"fileEntryId\")}\" src=\"${image.getData()}\" align=\"center\" /> </#if>\n    <p>${body.getData()}</p>\n    ```\n\n8. Click *Save*.\n\nTo finish it up, add some content:\n\n1.  Go to the *Web Content* tab.\n\n2.  Click on the *Add* button (![Add](../../../../../../images/icon-add.png)) and\n    select *News Article*.\n \n3. Insert some content and publish!\n\n![Figure 1: The Lunar Resort News Article is shaping up!](../../../../../../images/web-content-structures-templates-completed.png)\n\nAwesome! You created your own web content template!\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/03-designing-uniform-content/03-designing-web-content-with-templates/03-embedding-widgets-in-templates.markdown",
    "content": "---\nheader-id: embedding-widgets-in-templates\n---\n\n# Embedding Widgets in Templates\n\n[TOC levels=1-4]\n\nYou can also embed widgets in web content templates. Core apps and custom apps, \ninstanceable or non-instanceable can be embedded in web content templates. Below\nis an example of embedding a Language widget in FreeMarker:\n\n```markup\n<@liferay_portlet_ext[\"runtime\"] portletName=\"com_liferay_portal_kernel_servlet_taglib_ui_LanguageEntry\" />\n```\n\n| **Warning:** The `theme` variable is no longer injected into the FreeMarker\n| context. For more information about why the theme variable was removed for\n| @product@ 7.0 and suggestions for updating your code, visit the\n| [Taglibs Are No Longer Accessible via the theme Variable in FreeMarker](/docs/7-0/reference/-/knowledge_base/r/breaking-changes#taglibs-are-no-longer-accessible-via-the-theme-variable-in-freemarker)\n| breaking change entry.\n\nIn addition to embedding widgets in templates, you can embed a template within\nanother template. This allows for reusable code, JavaScript library imports,\nscripts, or macros.\n\nBelow is an example of embedding a template in FreeMarker:\n\n```markup\n<#include \"${templatesPath}/[template-key]\" />\n```   \n\nThe *Template Key* can be found when editing a previously published template.\n\n![Figure 1: You can find the Template Key when view the Edit page for a template..](../../../../../../images/adt-template-key.png)\n\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/03-designing-uniform-content/03-designing-web-content-with-templates/04-template-taglibs.markdown",
    "content": "---\nheader-id: using-taglibs-in-templates\n---\n\n# Using Taglibs in Templates\n\n[TOC levels=1-4]\n\nLiferay's taglibs are also accessible to web content administrators developing\nin FreeMarker. There is no need to instantiate these taglibs within your\nFreeMarker template; they're already provided for you automatically. You can\naccess these taglibs by indicating the TLD's file name with underscores.\n\nWhen you're using @product@'s template editor, you can find variables on the \nleft side of the template editor. To place one of the variables onto\nthe template editor,\n\n1.  Position your cursor where you want the variable placed.\n\n2.  Click the variable name.\n\nIf the variable name doesn't give you sufficient information on the variable's \nfunctionality, you can hover your pointer over it for a more detailed \ndescription.\n\n![Figure 1: You can hover your pointer over a variable for a more detailed description.](../../../../../../images/web-content-templates-create.png)\n\nThe interactive template editor is available for the FreeMarker, Velocity, and\nXSL languages. Depending on which language you select, the variable content\nchanges so you're always adding content in the language you've chosen. Another\ncool feature for the template editor is the autocomplete feature. It can be\ninvoked by typing *${* which opens a drop-down menu of available variables. By\nclicking one of the variables, the editor inserts the variable into the template\neditor.\n\n| **Note:** The `utilLocator`, `objectUtil`, and `staticUtil` variables for\n| FreeMarker are disabled by default. These variables are vulnerable to remote\n| code execution and privilege escalation, and should be used with caution, if\n| enabled.\n\nAfter you've saved your template, @product@ provides a WebDAV URL and static \nURL. These values access the XML source of your structure. You can find these \nURLs by returning to your template after it's been saved and expanding the \n*Details* section. For more information on WebDAV and the uses of the WebDAV \nURL, reference the [WebDAV Access](/docs/7-0/user/-/knowledge_base/u/publishing-files#desktop-access-to-documents-and-media)\nsection.\n\nNow that you've created a handsome template and know how to use the template\neditor, it's time to decide who the lucky people are that get to use your new\ntemplate.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/03-designing-uniform-content/03-designing-web-content-with-templates/05-template-permissions.markdown",
    "content": "---\nheader-id: assigning-template-permissions\n---\n\n# Assigning Template Permissions\n\n[TOC levels=1-4]\n\nStructures and Templates provide direct access to Liferay's APIs which makes\nthem powerful, but it also means that they can be dangerous in the wrong hands.\nOnly trusted users should be given access. The recommended practice is to\ncreate two Roles with access to structures and templates:\n\n-  **Content Developers** get full permission to create and edit structures \n   and templates.\n \n-  **Content Creators** only need permission to view the structures and \n   templates so they can use them to create content.\n\nWhen creating the Roles, define them to have global permission for all\nstructures and templates across the entire instance or only for specific\nSites. For more information on creating Roles, see the \n[Roles and Permissions](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions) \narticle.\n\n## Assigning Permissions for Individual Templates\n\nYou can also control access to specific templates separately. To determine who \ncan view and interact with a template,\n\n1.  Go to the *Templates* tab.\n\n2.  Click the *Actions* button (![Actions](../../../../../../images/icon-actions.png))\n    for a template that you created and select *Permissions*.\n\nHere permissions for a template can be set for Roles or Teams. Use this option \nto provide access to templates on a case by case basis for users that shouldn't\nhave access to templates on a larger level granted by a Role.\n\nWhether your Site is small and static or large and dynamic, Liferay's Web\nContent Management system enables you to plan and manage it. With tools such as\nthe WYSIWYG editor, structures and templates, you can quickly add and edit\ncontent. With Web Content Display, you can rapidly select and configure what\ncontent to display. You'll find that managing your site becomes far easier when\nusing @product@'s Web Content Management system.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/04-display-pages-for-web-content/01-display-pages-for-web-content-intro.markdown",
    "content": "---\nheader-id: display-pages-for-web-content\n---\n\n# Display Page Templates for Web Content\n\n[TOC levels=1-4]\n\nDisplay Page Templates provide a new level of control over the look and feel of\nyour content. The templates empower marketers and designers to create stunning\ndesigns for Web Content. They use both Page Fragments and Web Content to provide\nan easy way to create beautiful layouts for displaying articles.\n\nOne great way to use Display Pages Templates is to create standardized formats\nfor articles. If you examine the content on many writing platforms, each article\nfollows a similar format like this:\n\n-  Header Image\n-  Title\n-  Main Body\n-  Highlighted Quote\n-  Footer with links to related articles or other content\n\nDisplay Pages Templates let you create standard, reusable formats like this to \nstreamline the creation of attractive content. Read on to learn more!\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/04-display-pages-for-web-content/02-creating-display-pages.markdown",
    "content": "---\nheader-id: creating-display-pages\n---\n\n# Creating Display Page Templates\n\n[TOC levels=1-4]\n\nDisplay Page Templates are created initially in much the same way as Content\nPages. You  select any number of page fragments and add them to the page.\nDisplay pages differ in that after you add the fragments, you can then map\neditable fields in those fragments to the fields of a web content article. You\ncan learn more about creating Page Fragments in the \n[Creating Content Pages](/docs/7-2/user/-/knowledge_base/u/creating-content-pages)\narticle.\n\nLooking at the example of a template for a long form article, we can see how\nDisplay Page Templates utilize Page Fragments. The article can have an image, a\ntitle (simple style text), a main body (rich text), a highlighted quote (simple\nstyled text), and then a standard footer. Your first step in creating the\nDisplay Page Template is to create a Page Fragment which has all those fields\nformatted the way you want them. Your fragment could have these fields:\n\n-  Editable header\n-  Editable Image\n-  Editable rich text\n-  Editable plain text (with block-quote styling)\n-  Non-editable footer\n\nTo go along with this fragment, you could have a Web Content Structure with \nthese fields:\n\n-  Title (Text box)\n-  Image (Documents and Media image)\n-  Content (Web Content)\n-  Quote (Text area)\n\n![Figure 1: Connecting structure fields to fragment data.](../../../../../images/structure-to-fragment.png)\n\nThe Display Page Template maps the fields from the Web Content Structure to the\nfragment. When the Display Page Template is assigned for an article with that\nStructure, it appears on a display page with the formatting from the fragment.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/02-web-content/04-display-pages-for-web-content/03-display-page-template-example.markdown",
    "content": "---\nheader-id: display-page-template-example\n---\n\n# Display Page Template Example\n\n[TOC levels=1-4]\n\nCreate a new Display Page Template:\n\n1.  Go to *Site Builder* &rarr; *Pages* in Site Administration.\n\n2.  Go to the *Display Page Templates* tab.\n\n3.  Click the *Add* button (![Add](../../../../../images/icon-add.png)).\n\n4.  Name your Display Page *Lunar Resort Display Page Template*, open the \n    *Content Type* dropdown and select *Web Content Article* and \n    *Basic Web Content* for the Subtype, and click *Save*.\n\n![Figure 1: Selecting the Asset type and Subtype.](../../../../../images/display-page-asset-type.png)\n\n![Figure 2: The Display Page Template creation interface.](../../../../../images/create-display-page.png)\n\nTo build the Display Page Template, you can add any number of Fragments---with\nand without editable content---to the page to build your design. Fragments with\neditable content can have their editable fields mapped to be filled by a Web\nContent article. You can also base it on a specific Web Content Structure.\n\n![Figure 3: Editing a Display Page Template with some Fragments added.](../../../../../images/display-page-with-fragments.png)\n\nNotice that the example has an editable title and text body, with a static\nfooter containing graphics and links. After you've added some fragments to the\npage, you can map them like this:\n\n\n1.  Navigate to the *Section Builder* &rarr; *Basic Components* tab in the right\n    menu.\n\n2.  Add a *Heading 1* and *Text* components to the appropriate areas.\n\n3.  Click on an editable text area and click the *Map* button (![Map](../../../../../images/icon-map.png)) \n    in the dialog that appears.\n\n4.  Select a field to map the editable fragment to. The mapped field highlights \n    purple to indicate that it's mapped.\n\n    ![Figure 4: Mapping the editable fragments to structure fields.](../../../../../images/display-page-map-field.png)\n\n5.  Click *Publish* at the top of the page to save your work.\n\n<!--5.  Now check the box that says *Show Editable Areas*. This highlights\nareas that you can map.-->\n\nYou now have a Display Page Template with static graphics and a text area that's \nreplaced with whatever content you add to it.\n\n| **Note:** You can map any data or metadata from a Web Content Article or\n| Structure to a Display Page Template. For the Basic Web Content type, this includes\n| structure-defined fields like Summary, Title, and Content, as well as metadata\n| fields like Publish Date, Categories, and Tags. In a user-defined structure, all\n| user selected fields appear here as well. Custom fields are also available\n| for display if they apply to the content type selected.\n\nFor more information on the right panel and the Fragment options available to\nyou, see \n[Content Page Elements](/docs/7-2/user/-/knowledge_base/u/content-page-elements)\nand\n[Content Page Interface](/docs/7-2/user/-/knowledge_base/u/content-page-management-interface). \nContent Pages and Display Page Templates share the same Page builder\ntools.\n\n## Publishing with Display Page Templates\n\nNow create a short article to display with this display page template:\n\n1.  Go to *Content & Data* &rarr; *Web Content* in Site Administration.\n\n2.  Add a Basic Web Content article.\n\n3.  Name it *Thoughts About Space* and fill in some short content.\n\n4.  Open the *Display Page Template* section and under the dropdown, select\n    *Specific Display Page Template*. Then click *Select*. \n\n5.  Select the *Lunar Resort Display Page Template* and click *Done*.\n\n6.  Click *Publish*.\n\n![Figure 5: Selecting the Asset type and Subtype.](../../../../../images/display-page-creating-content.png)\n\nWhen published, you can view the content at its Friendly URL (you can find the\nFriendly URL while editing a Web Content article under *Friendly URL*) or when\nyou click on the content in an Asset Publisher with *Asset Link Behavior* set to\n*View in Context*.\n\nWhen editing the article, you can preview what the display page template will \nlook like with the *Preview* (![Preview Template](../../../../../images/icon-preview.png)) \nbutton located next to the selected display page template.\n\n![Figure 6: Selecting the Asset type and Subtype.](../../../../../images/display-page-in-context.png)\n\nYou can go back and edit the display page template by navigating to *Site\nAdministration* &rarr; *Site Builder* &rarr; *Pages* &rarr; *Display Page\nTemplates* and clicking *Actions*\n(![Actions](../../../../../images/icon-staging-bar-options.png)) &rarr; *Edit*\nnext to the display page template you want to edit. If you're viewing the\npublished display page, you can also select the *Edit* button\n(![Edit](../../../../../images/icon-edit-pencil.png)) from the Control Menu.\n\nAwesome! You now know how to create and configure a display page using display\npage templates.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/03-content-sets/01-content-sets-intro.markdown",
    "content": "---\nheader-id: content-sets\n---\n\n# Managing Content Sets\n\n[TOC levels=1-4]\n\nA Content Set is exactly what it sounds like: a set of content items. In short, \nan administrator can define a list of content, and then that list can be \ndisplayed. The way that the Content Set is displayed is determined by the \nmethod that is used to display it. For example, if the Content Set is being \nused by a smartwatch app, it could be displayed as a simple list of titles, and \nselecting a title would cause the full article to display on a connected mobile \ndevice. The same Content Set could be displayed in a web browser with the full \ncontent of each article.\n\n| **Note:** In previous versions of @product@, you used the Asset Publisher to \n| define and display either static lists of assets or dynamic lists based on \n| criteria like tags, categories, or asset type. In @product-ver@ Content Sets \n| take the core idea of defining different types of asset lists and expand it. \n| Content Lists are created outside of the context of a specific application or \n| widget and can be used and re-used across different channels and applications.\n\n## Creating and Displaying Content Sets\n\nContent Sets are created through the Site Administration interface. All the \nfeatures for creating and managing Content Sets are contained here. They are \ndisplayed using Liferay's widgets or your own custom applications. Read our\nguides for information on\n[Creating Content Sets](/docs/7-2/user/-/knowledge_base/u/creating-content-sets)\nand\n[Displaying Content Sets](/docs/7-2/user/-/knowledge_base/u/displaying-content-sets)\n\n## Content Set Personalization\n\nContent Sets can have variations driven by @product@'s Personalization engine. \nAfter you create a Content Set, if you have at least one User Segment created, \nyou can create a personalized experience of the Content Set for that Segment.\nTo learn to harness the power of experience personalization for Content Sets, \nsee\n[Content Set Personalization](/docs/7-2/user/-/knowledge_base/u/content-set-personalization).\n\n## Converting Asset Publisher Configurations to Content Sets\n\nYou may have already gone through a great deal of work to create a perfect, \ncurated list of content through the Asset Publisher, but now you want to \ndisplay that list elsewhere without duplicating your work. You can do that with \nContent Sets. Read the\n[Converting Asset Publisher Configuration to Content Sets guide](/docs/7-2/user/-/knowledge_base/u/converting-asset-publisher)\narticle to learn more.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/03-content-sets/02-creating-content-sets.markdown",
    "content": "---\nheader-id: creating-content-sets\n---\n\n# Creating Content Sets\n\n[TOC levels=1-4]\n\nContent Sets are created by content administrators through the Content Sets \ninterface in Site Administration. Content Sets can use either Manual or Dynamic \nselection, and you can create any number of Content Sets, and display them \nthrough the Asset Publisher or custom applications. Content \nSets can also have\n[personalized variations](/docs/7-2/user/-/knowledge_base/u/content-set-personalization)\nwhich provide different experiences for different users based on criteria that\nyou specify. The criteria management is shared with the Asset Publisher, so for\nmore  information on each option, see the official\n[Asset Publisher Documentation](/docs/7-2/user/-/knowledge_base/u/publishing-content-dynamically).\n\n## Creating a Manual Content Set\n\nTo demonstrate the creation of a Manual Content Set, create a Content Set that \ncontains a number of images to be displayed on the Frontpage of the fictitious \nSpace Program website. To prepare for this exercise, upload some appropriate images to *Documents and Media* to use for the Content Set.\n\n1.  Go to *Site Administration* &rarr; *Content & Data* and select *Content \n    Sets*.\n    \n    ![Figure 1: Content Sets is found in the Content & Data section of Site Administration.](../../../../images/content-sets-empty-page.png)\n\n2.  Click ![Add](../../../../images/icon-add.png) and select *Manual Selection*.\n\n3.  Name your Content Set *Space Program Images*.\n\nOn the next screen, you can select the assets to include in the Content Set.\n\n1.  Click *Select* &rarr; *Basic Document*.\n\n    ![Figure 2: You can select the type of asset to add to the Content Set.](../../../../images/content-sets-select-document.png)\n\n2.  Now, check the boxes for each image that you want to add and click *Add*.\n\nNow this Content Set can be displayed anywhere on the site where it was created. You can add or remove items from the set, and it will automatically update it whereever it is displayed. \n\n## Creating a Dynamic Content Set\n\nTo demonstrate the creation of a Dynamic Content Set, create a Content Set that \ncontains a number of varied assets that are tagged as \"trending.\" In order for \nthis to work, you will need some number of existing assets with the appropriate \ntag.\n\n1.  From the *Content Sets* page, click ![Add](../../../../images/icon-add.png)\n    &rarr; *Dynamic Selection*.\n\n2.  Enter *Trending* for the name and click *Save*.\n\nWith Dynamic Content Sets, you can choose the *Source*, *Scope*, *Filter*, and\n*Ordering* for the items in the set.\n\n1.  Leave *Source* as *Any* and *Scope* as *Current Site*\n\n2.  Open *Filter*, make sure it is set to *Contains Any of the following Tags*, \n    and then enter \"trending\" in the *Tags* box.\n    \n    ![Figure 3: Content Sets use the same filter system as the Asset Publisher.](../../../../images/content-set-trending-filter.png)\n\n3.  Open *Ordering* and set it to *Order By*: *Publish Date*, *And Then By*: \n    *Title*.\n\n4.  Click *Save*.\n\nThis will create a Content Set which will contain any items that are currently \ntagged as *trending* and any future items with the *trending* tag will be added \nto the Content Set automatically.\n\nNow that you have your Content Sets created, you can\n[display them on a page](/docs/7-2/user/-/knowledge_base/u/displaying-content-sets).\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/03-content-sets/03-displaying-content-sets.markdown",
    "content": "---\nheader-id: displaying-content-sets\n---\n\n# Displaying Content Sets\n\n[TOC levels=1-4]\n\nContent Sets are primarily displayed through the Asset Publisher. It is \ncurrently the only method to display them out of the box, but you can develop \nyour own external applications or widgets to utilize Content Sets. In [Creating Content Sets](/docs/7-2/user/-/knowledge_base/u/creating-content-sets) you \ncreated two Content Sets. Now display them \non a page.\n\n## Configuring the Asset Publisher for Content Sets\n\nTo display the Content Sets, start with a blank page, and then add the \nnecessary Asset Publishers and configure them to display the Content Sets.\n\n1.  Create a new *Home* page for your site as a Widget Page with a 1 column \n    layout. If you're using a fresh @product@ bundle, you can just remove the \n    *Hello World* widget from the sample *Home* page.\n    \n2.  Open the *Add* menu and add two *Content Management* &rarr; *Asset \n    Publishers* to the page stacked vertically.\n\n3.  Click ![Options](../../../../images/icon-app-options.png) &rarr; \n    *Configuration* for the top Asset Publisher.\n\n4.  Under *Asset Selection* choose *Content Set*.\n\n![Figure 1: The Asset Publisher has a number of options available for selecting its source for content.](../../../../images/content-set-asset-selection.png)\n\n5.  Open *Select Content Set* and click *Select*.\n\n6.  Click on the *Space Program Images* Content Set.\n\n7.  Click *Save*.\n\nNow the images will appear at the top of the page. You can manage the way the \ncontent is displayed---like what metadata appears---or even create a\n*Widget Template* to style the content, but the items which \ndisplay and the order in which they appear are determined by the Content Set.\n\nNow configure the bottom Asset Publisher with the other Content Set.\n\n1.  Click ![Options](../../../../images/icon-app-options.png) &rarr;\n    *Configuration* for the bottom Asset Publisher.\n\n2.  Under *Asset Selection* choose *Content Set*.\n\n3.  Open *Select Content Set* and click *Select*.\n\n6.  Click on the *Trending* Content Set.\n\n![Figure 2: Select the Content Set you want to use.](../../../../images/content-set-select-set.png)\n\n7.  Click *Save*.\n\nAgain, you can manage various display settings, but the items which appear and \ntheir order are determined by the Content Set criteria.\n\n![Figure 3: You can see the results as the standard Asset Publisher output. You can create Widget Templates to add more style and pizzazz here.](../../../../images/content-set-dynamic-results.png)\n\n## Adding Items to an existing Content Set\n\nTo demonstrate both the management of both static and dynamic Content Sets, \nupload a new image, tag it, and add it to the static set manually.\n\n1.  Upload a new image, and under *Categorization* tag it as *trending*.\n\n2.  Without lifting another finger, the image is added to the top of the \n    *Trending* Content List.\n    \n![Figure 4: The result is dynamically added to the Content List wherever it is displayed.](../../../../images/content-set-dynamic-add.png)\n\n3.  To add it to the manual set, go back to *Site Administration* &rarr; \n    *Content & Data* &rarr; *Site Builder*.\n\n4.  Click on *Space Program Images* or select ![Options](../../../../images/icon-options.png) &rarr; *Edit* next to \n    *Space Program Images*.\n    \n5.  Next to *Asset Entries* click *Select* &rarr; *Basic Document*.\n\n6.  Select the new image and click *Add*.\n    \n7.  Navigate back to the *Home* page to see your image added to the list.\n\nContent Sets are a powerful feature which provide one place to easily define \ncontent and other assets to be displayed all over your site. Their reusability \nalso means less repeated work involved in getting great content delivered to \nyour users.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/04-managing-content/03-content-sets/04-converting-asset-publisher.markdown",
    "content": "---\nheader-id: converting-asset-publisher\n---\n\n# Converting Asset Publisher Configurations to Content Sets\n\n[TOC levels=1-4]\n\nIn the previous two guides in this section, you've seen\n[Creating Content Sets](/docs/7-2/user/-/knowledge_base/u/creating-content-sets)\nand\n[Displaying Content Sets](/docs/7-2/user/-/knowledge_base/u/displaying-content-sets) \ndemonstrated. Next, try out converting an existing Asset Publisher\nconfiguration to a Content Set.\n\nIn this case, you have an Asset Publisher on a page, which is configured to \ndisplay images tagged as *trending* in reverse alphabetical order by title. \nThis might not be too hard to reproduce in the *Content Set* creator, but it's \neven easier to create the Content Set definition directly from the Asset \nPublisher.\n\n1.  Go to ![Options](../../../../images/icon-app-options.png) &rarr;\n    *Configuration* for the Asset Publisher.\n\n2.  Click *Create a content set from this configuration*.\n\n    ![Figure 1: You can generate a Content Set directly from the Asset Publisher configuration.](../../../../images/content-set-create-ap.png)\n\n3.  Enter the title and click *Save*.\n\nAnd as quickly as that you have a new Content Set that you can use with Asset\nPublishers anywhere on the site.\n\n![Figure 2: The Content Set is added right alongside any existing sets.](../../../../images/content-set-ap-added.png)\n\nGreat! You converted your Asset Publisher configuration to a Content Set.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/05-organizing-content-with-tags-and-categories/01-organizing-content-with-tags-and-categories-intro.markdown",
    "content": "---\nheader-id: organizing-content-with-tags-and-categories\n---\n\n# Organizing Content with Tags and Categories\n\n[TOC levels=1-4]\n\nTags and categories are two important tools you can use to help organize\ninformation in @product@. These tools help users to easily find the content\nthey're looking for through search or navigation. Tagging and categorizing\nassets is easy. You can tag or categorize an asset at creation time or when\nediting an existing asset. If you click on the *Metadata* section of the\nform when creating or editing an asset, you'll find an interface for adding tags\nand categories. If no categories are available to be added to the asset (e.g.,\nif no categories have been created), the *Select* option doesn't appear.\n\n![Figure 1: Here is the Web Content application's metadata section.](../../../images/web-content-categorization.png)\n\n| **Note:** You'll notice in Figure 1 above that there is also a *Priority* field\n| for web content. This field is not related to categories and tags, but rather,\n| specifies the order in which the web content article is listed when displayed in\n| the Asset Publisher. To learn more about the Asset Publisher, see the\n| [Publishing Assets](/docs/7-2/user/-/knowledge_base/u/publishing-assets)\n| section.\n\nThe Menu (![Menu](../../../images/icon-menu.png)) contains interfaces for\nmanaging tags and categories for each site in @product@. Navigate to the Site\nAdministration menu &rarr; *Categorization*, and you'll find the *Tags* and\n*Categories* options. These options can be used to manage all your site's tags\nand categories. It is important that you both tag and categorize your content\nwhen you enter it. You'll take a closer look at tags and categories next.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/05-organizing-content-with-tags-and-categories/02-tagging-content.markdown",
    "content": "---\nheader-id: tagging-content\n---\n\n# Tagging Content\n\n[TOC levels=1-4]\n\nTags are an important tool that can help organize information and make it easier\nfor users to find the content they want. Tags are all-lowercase words or phrases\nthat you can attach to any content. Tagging content makes your search results\nmore accurate and enables you to use tools like the Asset Publisher to display\ncontent in an organized fashion on a web page. \n\nThere are two ways to create tags: through the administrative console in the\nSite Administration section of the Menu or on the fly as content is created. By\ndefault, tags can be created by regular users and users can apply them to any\nassets they have permission to create or edit.\n\nOnly site administrators can access the *Tags* application in the Content\nsection of the Site Administration area of the Menu. Here, site administrators\ncan create new tags and edit any existing site tags: \n\n1.  Go to the site you want to create tags for and click *Categorization* &rarr;\n    *Tags*.\n \n    From this screen, you can view existing tags and create new ones.\n\n2.  To create a new tag, click the *Add Tag* icon\n    (![Add Tag](../../../images/icon-add.png)) and enter a name for the tag.\n\n![Figure 1: The Add Tag interface is very simple, only requiring the name of your tag.](../../../images/new-tag-interface.png)\n\nThe process for adding tags during content creation is similar. For example, to\ncreate tags for a new web content article:\n\n1.  Go to the *Metadata* dropdown in a New Web Content menu.\n\n2.  Add tags *lunar*, *moon*, and *spectacular*.\n\nOnce you've created the web content with these tags, the web content is\nassociated with those tag words when they are searched or referenced.\n\nTags are not the only instance-wide mechanism for describing content: the next\ntutorial describes categories.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/05-organizing-content-with-tags-and-categories/03-defining-categories-for-content.markdown",
    "content": "---\nheader-id: defining-categories-for-content\n---\n\n# Defining Categories for Content\n\n[TOC levels=1-4]\n\nCategories are similar in concept to tags, but are designed for use by\nadministrators, not regular users. Hierarchies of categories can be created, and\ncategories can be grouped together in *vocabularies*. While tags represent an\n*ad hoc* method for grouping content, categories exist to allow administrators\nto organize content in a more official, hierarchical structure. Think of tags\nlike the index of a book and categories like its table of contents. Both serve\nthe same purpose: to help users find the information they seek.\n\nYou can add properties to categories. Category properties are a way to add\ninformation to specific categories. You can think of category properties as tags\nfor your categories. Structurally, category properties are just like tag\nproperties: they are key-value pairs associated with specific categories that\nprovide information about the categories.\n\nAdding vocabularies and categories is similar to adding tags:\n\n1.  Go to the site where you want to create categories.\n\n2.  Click *Categorization* &rarr; *Categories* to view the Categories \n    application.\n\n![Figure 1: After adding new vocabularies, you'll notice your vocabularies indicate the amount of categories existing beneath them.](../../../images/vocabulary-list.png)\n\nClicking on a vocabulary displays categories that have been created under that\nvocabulary. To create a new vocabulary,\n\n1.  Click on the *Add Vocabulary* button (![Add Vocabulary](../../../images/icon-add.png)).\n\n2.  Enter a name and, optionally, a description.\n\n3.  Click *Save*.\n\nBy default, the *Allow Multiple Categories* option is enabled. This allows \nmultiple categories from the vocabulary to be applied to an asset. If the box \nis disabled, only one category from the vocabulary can be applied to an asset. \nThe *Associated Asset Types* lets you choose which asset types the categories \nof the vocabulary can be applied to and which asset types are *required* to have\nan associated asset from the vocabulary. Finally, you can configure the\npermissions of the vocabulary. By default, guests can view the\nvocabulary but only the owner can delete it, update it, or configure its\npermissions.\n\nCreating new categories is similar to creating new tags except that categories\nmust be added to an existing vocabulary and they can only be created by site\nadministrators. Once created, however, regular users can apply categories to any\nassets they have permission to create or edit. To create a new category:\n\n1.  Click the *Add Category* icon (![Add Category](../../../images/icon-add.png)).\n\nIf you're already viewing a vocabulary:\n\n1.  Select the *Actions* button (![Actions](../../../images/icon-actions.png))\n    next to an existing vocabulary and select *Add Category*.\n\n2.  Enter a name for the new category and, optionally, a description.\n\n3.  Click *Save*.\n\nJust as with tags, you can configure the category's permissions, choosing which\nroles (guest, site member, owner) can view the category, apply it to an asset,\ndelete it, update it, or configure its permissions. By default, categories are\nviewable by guests, and site members can apply categories to assets. \n\nOnce you have created some vocabularies and categories, you can take advantage\nof the full capabilities of categories by creating a nested hierarchy of\ncategories. To nest categories, select the *Actions* button for the category you\nwant to be the parent category. Then select *Add Subcategory*, which adds\na child category to the selected parent.\n\nAfter you've created a hierarchy of categories, they're available to apply to\ncontent: \n\n1.  Click on *Web Content* in the *Content & Data* section of Site\n    Administration and click *Add* &rarr; *Basic Web Content*. \n \n2.  Click on *Metadata* from the right-side menu and click *Select* on the\n    vocabulary you'd like to apply. A dialog box appears with your categories.\n \n3.  Select relevant categories by checking the box next to them, and they'll\n    be applied to the content.\n\nSuppose you're running a Lunar Resort shop called Lunar Fireworks and you have\nmany web content articles describing the colors and types of fireworks you\noffer. The abundance of your articles is overwhelming, and as your shop grows,\nso too does the web content articles you're required to manage. You've decided\nto categorize your web content based on the color and type of firework, so the\narticles are easier to manage.\n\n1.  Go to Site Administration &rarr; *Categorization* &rarr; *Categories* and\n    create vocabularies *Type* and *Color*.\n\n2.  Make sure both vocabularies are only used for web content articles by \n    clicking the *Associated Asset Types* dropdown and selecting *Web Content \n    Article*.\n\n3.  Create categories *Fire* and *Smoke* for the Type vocabulary and *Red*, \n    *Yellow*, and *Blue* categories for the Color vocabulary.\n\n4.  Now navigate to *Content & Data* &rarr; *Web Content* in Site Administration\n    and create an article called *Red Rocket*. This is your best selling\n    product, so make sure to give it a detailed explanation and an awesome\n    picture.\n\n5.  Select the *Metadata* dropdown for your web content article and select the \n    Type &rarr; Fire and Color &rarr; Red categories.\n\nWhen you publish your new web content article for your best selling product,\nit's organized by its type and color. Once you've organized all your articles,\nyou'll always be able to reference the type and color of a firework, just in\ncase you forget.\n\nThere are a few other cool features for vocabularies and categories. A few of\nthem were mentioned already when the *Allow Multiple Categories* and *Required*\nselectors for vocabularies and categories were discussed. The three new\nfeatures are targeted vocabularies, single/multi-valued vocabularies, and\nseparated widgets for every vocabulary. They're in the next tutorial.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/05-organizing-content-with-tags-and-categories/04-targeted-vocabularies.markdown",
    "content": "---\nheader-id: targeted-vocabularies\n---\n\n# Targeted Vocabularies\n\n[TOC levels=1-4]\n\nYou can decide which vocabularies can be applied to an asset type and which\nvocabularies are required for an asset type with Targeted Vocabularies. To\nconfigure these settings, go to the Categories application in Site\nAdministration and select a vocabulary's *Actions* &rarr; *Edit* button. Select\nthe *Associated Asset Types* tab to reveal a dialog box like the one below.\n\n![Figure 1: You can target vocabularies by checking the *Allow Multiple Categories* selector and then selecting the Asset Types.](../../../images/targeted-vocabularies.png)\n\nThe default value for *Associated Asset Types* is *All Asset Types*. You can\nfine tune your choices by using the *+* and *-* buttons, which narrows the scope\nof the vocabulary to specific assets. In the screenshot above, notice that the\nvocabulary is configured to be available for Web Content articles and Blog\nentries, but it is not required. It is mandatory, however, for Bookmark entries.\n\n## Single and Multi-valued Vocabularies\n\nYou can also decide if users can choose one or more categories from the same\nvocabulary to apply to an asset. If a vocabulary is single-valued you can only\nchoose one. If it allows more, you can choose several categories from the\nvocabulary to apply to an asset.\n\n![Figure 2: Multi-valued vocabularies allow multiple categories from the vocabulary to be applied to an asset. Single-valued vocabularies only allow one category from the vocabulary to be applied. Here, the *Dining* and *Nightlife* categories are selected to be applied but the *Scenic Adventures* category is not.](../../../images/multi-valued-vocabularies.png)\n\nYou can configure the single-valued or multi-valued status of a vocabulary\nthrough the Categories application. Edit a vocabulary and deselect the *Allow\nMultiple Categories* selector to create a single-valued vocabulary. Use the\ndefault option to create a multi-valued vocabulary.\n\n## Separated Entries\n\nA third feature of vocabularies and categories is that every vocabulary has its\nown separated entry. These entries appear in the Categorization section of the\nform for editing an asset, and they allow users to easily select appropriate\ncategories for that asset.\n\n![Figure 3: Vocabularies have their own entries, making it easy to select available categories.](../../../images/separated-entries.png)\n\nIt's important to use tags and categories with all your content, so that content\nis easier for users to find. \n\nNext, you'll learn how to geo-locate assets.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/05-organizing-content-with-tags-and-categories/05-geolocating-assets.markdown",
    "content": "---\nheader-id: geolocating-assets\n---\n\n# Geolocating Assets\n\n[TOC levels=1-4]\n\nGeolocation adds the geographic coordinates where an asset was created as\nmetadata to your assets. You can add geolocation metadata to your web content,\nData Lists, and Documents & Media. This feature is provided for you\nout-of-the-box. However, you must first enable it in your assets in order to\nuse it.\n\nLet's examine how you can enable geolocation in your web content.\n\n## Geolocating Web Content\n\nTo use geolocation in your web content, you must create a \n[structure and template](/docs/7-2/user/-/knowledge_base/u/designing-uniform-content)\nthat includes a Geolocation field. \n\n![Figure 1: Add a geolocation field to your structure to enable geolocation in your web content.](../../../images/geo-structure.png)\n\n1.  Create a structure with a Geolocation field like in the image \n    above.\n\n2.  Create a new template and select the structure you just created \n    with the geolocation field.\n\n3.  Scroll down to the *Script* heading and locate the *Fields* section. Here \n    are *Content* and *Geolocation* snippets.\n\n4.  Click on the snippets to add them to the template and *Save*.\n\n![Figure 2: Add the Content and Geolocation snippets to create your web content template quickly.](../../../images/web-content-geolocation-template.png)\n\nTo set your location for the web content, you can share your location with the \nbrowser, type a specific address into the address bar on the map, or even drag \nthe indicator and drop it in any point in the map and the address is\nautomatically updated to reflect the new point. Once the web content is saved, \nthe location is added as metadata to the web content.\n\n![Figure 3: You can enter your location in the address bar, move the indicator to a location, or share your location with the browser.](../../../images/web-content-geo-create.png)\n\n| Note: Depending on your browser settings, you may need to configure it to share your location.\n| \n| ![Figure 4: Make sure your browser is configured to share your location.](../../../images/share-location-dialog.png)\n\n## Geolocating Data Lists\n\nTo use geolocation in your dynamic data lists, you must first create a data\ndefinition that includes a geolocation field.\n\n1.  Open the Product Menu and navigate to *Content & Data* &rarr;\n    *Dynamic Data Lists*. \n\n2.  Click the *Options* menu and select *Manage Data Definitions*.\n\n3.  Click the *Add* button to create a new data definition.\n\n4.  Enter a name, optional description, and parent data definition if you have \n    one.\n \n5.  Scroll down and add a *Geolocation* field to the data definition, along \n    with any other fields you wish to add and *Save*.\n\n6.  Go back to the Dynamic Data Lists screen and click the *Add* button \n    (![add](../../../images/icon-add.png)) to create a new list.\n\n7.  Enter a name and optional description.\n\n8.  Finally, click the *Select* button and choose the newly created data \n    definition.\n\nNow that your data list is complete, you can use the \n[Data List Display portlet](/docs/7-2/user/-/knowledge_base/u/creating-data-lists) \nto display it.\n\n## Geolocating Documents and Media\n\nTo enable geolocation in Documents and Media, you must first create a document\ntype that includes geolocation metadata. You can add geolocation metadata as\npart of a Metadata Set or as part of the new document type. To add geolocation\nmetadata as part of a Metadata Set:\n\n1.  Open the Product Menu and navigate to *Content & Data* &rarr; *Documents and\n    Media*. Open the *Options* menu, and select *Metadata Sets*.\n\n2.  Click the *Add* (![add](../../../images/icon-add.png)) button and enter\n    a name, optional description, and Parent Metadata Set if you have one.\n\n3.  Scroll down and add a *Geolocation* field, along with any additional fields \n    you wish to have, and *Save*.\n\nTo create the new document type with geolocation:\n\n1.  Navigate to *Documents and Media*, open the *Options* menu and select \n    *Document Types*. \n\n2.  Click the *Add* button (![add](../../../images/icon-add.png)) and enter\n    a name and optional description.\n\n3.  Scroll down to the Main Metadata Fields heading and add a *Geolocation* \n    field along with any other fields you wish to have for the document type.\n \n4.  If you are using a Metadata Set, scroll down to the Additional Metadata \n    Fields heading, click the *Select Metadata Set* button.\n\n5.  Choose your Metadata Set with the geolocation metadata and *Save*.\n\n6.  Navigate back to the *Documents and Media* screen and click the *Add*\n    button (![add](../../../images/icon-add.png)) and select your newly created document type.\n\n7.  Fill out the information for the document, and just as with the web \n    content, your location is automatically obtained from the browser and added \n    to your document.\n\nOnce your assets are geolocation enabled, you can use the\n[Asset Publisher](/docs/7-2/user/-/knowledge_base/u/publishing-assets) \nto display the location of the assets on a map, using the map display template.\nCheck out the\n[Configuring Display Settings](/docs/7-2/user/-/knowledge_base/u/configuring-display-settings)\nsection to learn more.\n\n![Figure 5: The Asset Publisher can display your geolocated assets on a map.](../../../images/geo-map.png)\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/06-publishing-content-dynamically/01-publishing-content-dynamically-intro.markdown",
    "content": "---\nheader-id: publishing-content-dynamically\n---\n\n# Publishing Content Dynamically\n\n[TOC levels=1-4]\n\nMost content types are Assets. In the \n[Creating Web Content](/docs/7-2/user/-/knowledge_base/u/creating-web-content) \narticles, you examined the most common type of asset: web content. Other types\nof assets include blog posts, wiki articles, message board posts, bookmarks, and\ndocuments. Developers can define custom asset types that use the asset\nframework, which provides support for tags, categories, vocabularies, comments,\nratings, and asset relationships.\n\nThe Asset Publisher application displays assets. It has many configuration\noptions which you'll cover in this chapter. By default, Asset Publisher displays\nabstracts (previews) of recently published assets with links to their full\nviews. You can configure the Asset Publisher app to display a table of assets,\na list of asset titles, or the full content of assets. You can also make it\ndisplay only certain kinds of assets, and you choose how many items to display\nin a list. \n\nYou might use Asset Publisher to display chosen content types, recent content,\nor content by tags and categories. \n\nThis section covers the following topics:\n\n- Adding relationships between assets\n- Publishing assets\n- Publishing RSS feeds\n- Restoring deleted assets\n\nThe first thing you'll learn about is tagging and categorizing content.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/06-publishing-content-dynamically/02-defining-content-relationships.markdown",
    "content": "---\nheader-id: defining-content-relationships\n---\n\n# Defining Content Relationships\n\n[TOC levels=1-4]\n\nRelated Assets are assets connected to other assets, even if they don't share\nany tags and aren't in the same category. Here you'll focus on how to define\nrelationships between assets so when you begin publishing assets, the Related\nAssets widget can display those relationships. \n\n## Related Assets Widget\n\nBy default, the Related Assets widget displays any related asset of the asset\nselected in the Asset Publisher. If you don't want to show every related asset,\nyou can configure what content relationships to display. To do this, follow\nthese steps: \n\n1.  Go to the Related Assets app and select the *Options* icon\n    (![Options](../../../images/icon-app-options.png)) in the upper right corner\n    of the application and click *Configuration*. \n\n2.  Under the *Setup* &rarr; *Asset Selection* tab, set the type of asset(s) to\n    display using the *Asset Type* menu. The default value is set to *Any*. \n\n3.  You can narrow the scope of the app to display any single category of asset\n    type or select multiple assets from the menu.\n\n    Filter options set minimum requirements for displaying assets by their\n    categories, tags, and custom fields. Ordering and Grouping organizes assets\n    using the same criteria. Display settings customize how the app shows\n    assets: by title, in a table, by abstract, or full content. You can convert\n    assets to different document types like ODT, PDF, and RTF. You can choose to\n    show metadata fields such as author, modification date, tags, and view\n    count. You can even enable RSS subscriptions and customize their display\n    settings. \n\n4.  When you're finished setting the Source and Filter options, click *Save*.\n\nNow that you've configured the Related Assets widget to display specific content\ntypes, you must define the relationships for your assets. Here's a simple\nexample of defining related assets for a web content article and then displaying\nthose related assets.\n\nSuppose you own a gift shop at the Lunar Resort, and you want all your shop's\nassets to appear when an asset is clicked. You must define relationships between\nyour content, so when an asset is clicked, its related assets are appear\nalongside the clicked asset. Here's how to do it: \n\n1.  Create a blog entry explaining your gift shop's new apparel and a photo of\n    the moon, just so consumers are aware that you offer the *only* gift shop on\n    a desolate rock orbiting the Earth!\n\n2.  Create a web content article describing your shop. Once you've given your\n    article a title and some content, open the *Related Assets* dropdown menu.\n    Click *Select*, choose *Blogs Entry*, and select the blog you created. Click\n    *Select* again, choose *Basic Document*, and select the photo of the moon.\n    Click *Publish* to publish your web content article.\n\n3.  Now that those assets are created, you can relate the blog entry and\n    photo to your web content article. Navigate to your article in Site\n    Administration &rarr; *Content & Data* &rarr; *Web Content*.\n\n4.  You've now defined relationship with your three assets. Click the *Add* icon\n    (![Add](../../../images/icon-add.png)) at the top of your page\n    in the Control Menu, select *Widgets*, and add the Related Assets and\n    Asset Publisher widgets to the page. Don't panic: related assets don't\n    appear until you select an asset in the Asset Publisher.\n\n![Figure 1: Select an asset in the Asset Publisher to see its related assets displayed in the Related Assets application.](../../../images/related-assets-app-1.png)\n\nOnce you select an asset, its related assets appear in the Related Assets app,\nas in the image above. If you want more detail, you can place two Related Assets\nwidgets on the page and name one *Related Blogs* and the other *Related\nPhotos*. \n\n![Figure 2: Related Assets applications can be configured to display specific content.](../../../images/related-assets-app-2.png)\n\nNext, you'll learn more about how to use the Asset Publisher.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/06-publishing-content-dynamically/03-using-the-asset-publisher/01-publishing-assets-intro.markdown",
    "content": "---\nheader-id: publishing-assets\n---\n\n# Publishing Assets\n\n[TOC levels=1-4]\n\nAs you create web content, remember that pieces of content are assets, just like\nmessage board entries and blog posts. Since the Asset Publisher publishes\nassets, it excels at publishing mixed content types like images, documents,\nblogs, and of course, web content. This helps in creating a more dynamic web\nsite: you can place user-created wiki entries, blog posts, or message board\nmessages in context with your web content. You'll examine some of the Asset\nPublisher's features next.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/06-publishing-content-dynamically/03-using-the-asset-publisher/02-querying-for-content.markdown",
    "content": "---\nheader-id: querying-for-content\n---\n\n# Querying for Content\n\n[TOC levels=1-4]\n\nThe Asset Publisher works by querying for mixed types of content on the fly.\nSince you can control what and how content is displayed from one location, the\nAsset Publisher helps to \"bubble up\" the most relevant content to your users.\n\nTo get to all the application's options, click the *Options* icon\n(![Options](../../../../images/icon-app-options.png)) in the application's menu.\nIf you click the *Configuration* option and then *Setup* (if necessary), you can\nconfigure the Asset Publisher's settings from the following three areas:\n\n- Asset Selection\n- Display Settings\n- Subscriptions\n\nAsset Selection configures which assets are displayed. You can set asset\nselection to\n\n- *Dynamic*\n- *Manual*\n- *Content Set*\n- *Content Set Provider*\n\nDynamic displays assets based on certain rules or filters. For example, you can\nset the Asset Publisher to display only assets of a certain type or to which\ncertain tags or categories have been applied. Manual asset selection only\ndisplays assets that have been explicitly selected by an administrator. For more\ninformation on Content Sets and their place in the Asset Publisher, see the\n[Managing Content Sets](/docs/7-2/user/-/knowledge_base/u/content-sets)\nsection.\n\nThe Asset Publisher supports a scope that restricts both dynamic and manual\nasset selection. The Asset Publisher can only display assets from its configured\nscope. By default, the Asset Publisher app is scoped to the site of the page to\nwhich it was added. You can, however, customize the scope from the Asset\nSelection section of the Asset Publisher configuration window. To extend your\nAsset Publisher's scope,\n\n1.  Click *Select* under Scope.\n\n2.  Choose *Global* to add the global scope or *Other Site...* to add the scope\n    of another site.\n\nThe Display Settings section of the Asset Publisher configuration window is for\ncustomizing how content is displayed. The Subscription section enables,\ndisables, or configures email subscriptions and RSS subscriptions. In the\nfollowing sections, you'll explore the available configurations for the Asset\nSelection, Display Settings, and Subscriptions sections of the Asset Publisher's\nconfiguration window. You'll start by learning how select content manually.\nYou'll see that it's very similar to using the Web Content Display application\nexcept that you can select assets of any type, not just web content articles. \n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/06-publishing-content-dynamically/03-using-the-asset-publisher/03-selecting-assets.markdown",
    "content": "---\nheader-id: selecting-assets\n---\n\n# Selecting Assets\n\n[TOC levels=1-4]\n\nYou can configure Asset Publisher to select assets manually or dynamically \nthrough various criteria. Within those options there is flexibility in what\nassets are displayed and how they are displayed.\n\n## Selecting Assets Manually\n\nTo enable manual asset selection,\n\n1.  Click the click the *Options* icon\n    (![Options](../../../../images/icon-app-options.png)) in the widget's\n    menu.\n\n2.  Select *Configuration*.\n\n3.  In the Asset Publisher configuration, select *Manual* from the select box\n    beneath *Asset Selection*.\n\nNow you must select a *Scope* and specific *Asset Entries* from that scope to\ndisplay. You can configure multiple scopes, including the global scope, from\nwhich to select assets.\n\n![Figure 1: Selecting assets in the Asset Publisher manually is similar to selecting assets in the Web Content Display application except that you can select assets of any type, not just web content. You can also add scopes to expand the list of assets that are available to be displayed in the Asset Publisher.](../../../../images/web-content-asset-publisher-manual.png)\n\nWhen selecting assets manually, a list of configured scopes appears under the\nScope heading. You can configure the scope like this:\n\n1.  Click the *X* button at the right to remove a scope from the\n    list.\n\n2.  Click the *Select* button to add additional scopes to the Asset\n    Publisher's configuration.\n \n3.  After you've added a scope, a new Select button appears under the Asset\n    Entries heading. A list of assets selected for display appears in the Asset\n    Entries section. You can select assets to be displayed by clicking the\n    appropriate *Select* button. One button appears for each configured scope.\n    By default, these are the available asset types: \n\n       - Blogs Entry\n       - Bookmarks Entry\n       - Bookmarks Folder\n       - Calendar Event\n       - Basic Document\n       - Google Drive Shortcut\n       - Documents Folder\n       - Dynamic Data Lists Record\n       - Knowledge Base Article\n       - Message Boards Message\n       - Basic Web Content\n       - Web Content Folder\n       - Wiki Page\n\n    You can select any number of assets to be displayed. Note, however, that\n    there's a display setting called *Number of Items to Display* that\n    determines the maximum number of items to display (or, if pagination is\n    enabled, the maximum number of items to display per page). The Asset\n    Publisher can mix and match different asset types in the same interface. \n\n4.  When you're done selecting items to display, click *Save*. Any selected\n    assets are added to the list of assets that are displayed by the\n    application. \n \nOnce you have your content selected, you can configure the display types to\nconfigure how the content appears. You'll explore the display settings in more\ndetail after we finish discussing how to select assets for display.\n\nManual asset selection lets you select assets of various types from different\nscopes, but it can be time-consuming to update the assets that should be\ndisplayed. It's often more convenient to use the Asset Publisher to select\ncontent dynamically.\n\n## Selecting Assets Dynamically\n\nThe Asset Publisher's default behavior is to select assets dynamically according\na set of customizable rules. These rules can be combined so that they compliment\neach other to create a nice, refined query for your content. Assets are\nfiltered by permissions automatically, no matter how complicated your asset\nselection rules are. You have the following rule types:\n\n**Scope:** Choose the sites containing the content that should be selected. This\nworks the same way as with manual asset selection: assets can only be displayed\nif they belong to a configured scope. The following scope options are available:\n\n- *Current Site*\n- *Global*\n- *Other Site*\n\nThe Other Site scope option is unavailable for Asset Publisher applications\nconfigured on a page template (e.g., Content Display Page).\n\n**Asset Type:** Choose the asset types you want, from all assets, to only one,\nor any combination in between. For example, you could choose only web content,\nonly wiki entries, or any combination of multiple types.\n\n**Filter:** Add as many filters on tags or categories as you like. You can\nchoose whether the content must contain or must not contain any or all of the\ntags or categories that you enter.\n\n![Figure 2: You can filter by tags and categories, and you can set up as many filter rules as you need.](../../../../images/web-content-asset-publisher-filter.png)\n\nOnce you've set up your filter rules for dynamically selecting content, you can\ndecide how the content is displayed.\n\nIf you've added custom User profile attributes, you can configure the Asset\nPublisher to display assets that match them. This setting retrieves assets that\nhave matching categorization. These categories must be from the global context.\nFor example, suppose a User has a custom field called *Location* with the type\n*Text*. If this attribute is set to *Moon*, you could create a vocabulary\ncalled *Location* and a category for the Location vocabulary called *Moon*.\nThen you could categorize content with *Moon* in the *Location* vocabulary.\nWith this organizational setup, adding an Asset Publisher and specifying\n*Location* as the Asset Publisher's custom user attribute would only display\ncontent that had been categorized as *Moon*. Pretty cool, right?\n\nSee \n[Defining Categories for Content](/docs/7-2/user/-/knowledge_base/u/defining-categories-for-content)\nfor further information. \n\nIn addition, you can use these advanced filters:\n\n-   **Show only assets with** ***Welcome*** **as its display page** displays\n    only assets specifically configured for the *Welcome* page.\n-   **Include tags specified in the URL?** lets you specify tags in the URL for\n    the Asset Publisher to display.\n\nThe *Ordering* section of the Asset Publisher precisely controls how content is\nordered and grouped when displayed. You can order the assets displayed by Asset\nPublisher in ascending or descending order by the following attributes:\n\n- Title\n- Create Date\n- Modified Date\n- Publish Date\n- Expiration Date\n- Priority\n\nSay you have a series of \"How To\" articles that you want displayed in descending\norder based on whether the article was tagged with the *hammer* tag. Or suppose\nyou want a series of video captures to appear in ascending order based on\na category called *birds*. For these use cases, you can configure the ordering\nand grouping settings.\n\nYou can also configure a second ordering. The second ordering is applied to any\nassets for which the first ordering wasn't sufficient. For example, if you\nordered assets by title and there are multiple assets with the same title, the\nsecond ordering takes effect, perhaps the publication date. \n\nYou can establish grouping rules as well as ordering rules. You can group assets\nby type or by vocabulary. For example, suppose you have a vocabulary called\n*Membership Type* with two categories: *Premium* and *Regular*. If you group\nassets by Membership Type, all assets with the Premium category appear in one\ngroup and all assets with the Regular category appear in another group. Grouping\nrules are applied before any ordering rules: they're a way to divide up the\ndisplayed assets into separate lists. The ordering rules are applied separately\nto each group of assets.\n\nNote that ordering rules are only one way to control how your content appears.\nYou can refine the display through many other display settings which you'll\nexamine next.\n\n| **Note:** The following actions have immediate effects in your Asset Publisher:\n| \n| - Change the value of the *Asset Selection* option.\n| - Change the value of the *Scope* option.\n| - Select, add, sort or delete asset entries (only when selecting assets\n|   manually).\n\nOther changes happen after clicking *Save*. Next you'll learn about the Asset\nPublisher's other configuration options.\n\n## Selecting a Content Set\n\n@product-ver@ adds the Content Set feature to create predefined lists of content\nto display in the Asset Publisher. To use a Content Set,\n\n1.  Click the click the *Options* icon\n    (![Options](../../../../images/icon-app-options.png)) in the Asset\n    Publisher's menu.\n\n2.  Select *Configuration* from menu.\n\n3.  Select *Manual* from the select box beneath the *Asset Selection* tab.\n\n4.  Choose the Content Set that you want to use.\n\nFor more information on using Content Sets, see\n[Creating Content Sets](/docs/7-2/user/-/knowledge_base/u/creating-content-sets).\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/06-publishing-content-dynamically/03-using-the-asset-publisher/04-configuring-display-settings.markdown",
    "content": "---\nheader-id: configuring-display-settings\n---\n\n# Configuring Display Settings\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n    <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/displaying-content/using-the-asset-publisher-widget/configuring-display-settings.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nFrom the Asset Publisher's configuration page, open the Setup tab's *Display\nSettings* sub-tab. This section gives you precise control over the display of\nyour assets. There are many options available to configure how you want your\ncontent to appear. Many of these, such as printing, flags, ratings, comments,\ncomment ratings, and social bookmarks work the same way they do in the Web\nContent Display application.\n\n**Display Template**: This selector lets you choose an application display\ntemplate to customize how the Asset Publisher displays assets. These templates\nare in every site by default:\n\n- *Abstracts:* Shows the first 200-500 characters of the content, defined by the\n  **Abstract Length** field. This is the default display template. \n- *Table:* Displays the content in an HTML table which can\n  be styled by a theme developer.\n- *Title List:* Displays the content's title as defined by\n  the user who entered it.\n- *Full Content:* This display template displays the entire content of the entry.\n\nThere's also the *Rich Summary* and *Map* display templates that belong to\nthe global scope. The Rich Summary template provides a summary view of each\nasset along with a *Read More* link to the article's full content. The Map\ntemplate displays\n[geo-localized assets](/docs/7-2/user/-/knowledge_base/u/geolocating-assets)\nin either a Google Map or an Open Street Map provider. The map provider can\nbe configured in Instance Settings, and Site Settings in the Advanced\nsection.\n\n**Abstract Length**: Select the number of characters to display for abstracts.\nThe default is `200`.\n\n**Asset Link Behavior:** The default value is *Show Full Content*, which\ndisplays the full asset in the current Asset Publisher. *View in a Context*\ncauses that asset to be displayed in the application where the asset belongs.\nFor example, a blog entry is displayed in Blogs where it was created. See the\nsection below on display pages for more information.\n\n**Number of Items to Display**: Select the maximum number of assets that can be\ndisplayed by the Asset Publisher. If pagination is enabled, this number\nrepresents the maximum number of assets that can be displayed per page.\n\n**Pagination Type**: This can be set to *None*, *Simple*, or *Regular*. *None*\ndisplays at most the number of assets specified in the **Number of Items to\nDisplay** property. *Simple* adds Previous and Next buttons for browsing through\npages of assets in the Asset Publisher. *Regular* adds more options and\ninformation including First and Last buttons, a dropdown selector for pages,\nthe number of items per page, and the total number of results (assets being\ndisplayed).\n\n**Show Add Content Button**: When selected, an *Add New* button appears that\nlets users add new assets directly from the Asset Publisher application. This\nis checked by default.\n\n**Show Metadata Descriptions:** Enables Metadata descriptions such as\n*Content Related to...* or *Content with tag...* to be displayed with the\npublished assets.\n\n**Show Available Locales:** Since content can be localized, you can have\ndifferent versions of it based on locale. Enabling this option shows the locales\navailable, so users can view the content in their languages.\n\n**Set as the Default Asset Publisher for This Page**: The Asset Publisher app is\nan instanceable app: multiple Asset Publishers can be added to a page and each\nhas an independent configuration. The default Asset Publisher for a page is the\none used to display web content associated with the page.\n\n**Show only assets with Home as its display page template:** Display assets that\nonly exist on the Home page template.\n\n**Include tags specified in the URL:** Incorporate tags specified in the URL.\n\n**Enable ...**: Enable/disable the following options for displayed assets:\n\n- Print\n- Flags\n- Ratings\n- Related Assets\n- Subscribe\n- Comments\n- Comment Ratings\n- View Count Increment\n\nThe Print option adds a *Print* link to the full view of an asset displayed in\nthe Asset Publisher. Clicking *Print* opens a new browser window with a print\nview of the asset. Enabling flags, related assets, ratings, comments, comment\nratings, or social bookmarks add links to the corresponding social features to\nthe view full of the asset in the Asset Publisher.\n\n| **Tip:** An alternate way to add flags, comments, and ratings to a page is\n| through the *Page Flags*, *Page Comments*, and *Page Ratings* applications. Just\n| add the applications in the appropriate location near the asset that should have\n| feedback.\n\n**Metadata:** Select various metadata types to be displayed (see below). For\nexample, you can select tags and categories for display. Upon saving your\nconfiguration, the Asset Publisher displays tags and categories for each\ndisplayed asset. Then users can click on the tags and categories to filter the\ndisplayed assets manually. \n\n![Figure 1: You can configure the Asset Publisher to display various kinds of metadata about the displayed assets.](../../../../images/available-metadata-fields.png)\n\nNext you'll learn about configuring subscriptions for email and RSS through the \nAsset Publisher. \n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/06-publishing-content-dynamically/03-using-the-asset-publisher/05-configuring-asset-publisher-subscriptions.markdown",
    "content": "---\nheader-id: configuring-asset-publisher-subscriptions\n---\n\n# Configuring Asset Publisher Subscriptions\n\n[TOC levels=1-4]\n\nThe Asset Publisher application supports two kinds of subscriptions email\nsubscriptions. To enable subscriptions, click the Asset\nPublisher's Options icon and select *Configuration*. In the configuration\nwindow, open the Setup tab's Subscriptions tab. There you can enable/disable the\n*Enable Email Subscription* selector.\n\n![Figure 1: An email subscription notifies users when new assets are published.](../../../../images/asset-publisher-email.png)\n\nEnabling Email Subscription adds a *Subscribe* link to the Asset Publisher.\nUsers wishing to be notified of newly published assets can click on this link to\nbe added to the subscription list. @product@ periodically checks for new assets\nand sends emails to subscribed users informing them about the new assets. By\ndefault, Liferay performs this check every twenty-four hours.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/06-publishing-content-dynamically/04-publishing-rss-feeds/01-publishing-rss-feeds-intro.markdown",
    "content": "---\nheader-id: publishing-rss-feeds\n---\n\n# Publishing RSS Feeds\n\n[TOC levels=1-4]\n\nRSS is a family of web feed formats used to publish frequently updated works\nsuch as blog entries and news articles. RSS allows users to stay up-to-date with\nyour site's content without actually having to visit your site. Users can use \ntheir own RSS feed readers to aggregate content, and you can also use RSS to \nshare and aggregate content across sites. Next, you'll see how to create RSS \nfeeds from Asset Publisher configurations.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/06-publishing-content-dynamically/04-publishing-rss-feeds/02-configuring-rss-feeds.markdown",
    "content": "---\nheader-id: configuring-rss-feeds\n---\n\n# Configuring RSS Feeds\n\n[TOC levels=1-4]\n\n| **Note:** RSS feeds are deprecated for @product@ 7.2 and are disabled by\n| default. To leverage RSS feeds, you must enable this feature. Go to the\n| Control Panel &rarr; *Configuration* &rarr; *System Settings* &rarr; *Web\n| Content*. Under the *System Scope* &rarr; *Administration* tab, check the\n| *Show Feeds* box. For more information on deprecated apps, see\n| [this article](/docs/7-2/deploy/-/knowledge_base/d/deprecated-apps-in-7-2-what-to-do#web-experience).\n\nTo manage a Liferay site's RSS feeds, navigate to your Site's Site\nAdministration &rarr; *Content & Data* &rarr; *Web Content*. Site administrators\ncan use this Web Content menu option to manage their site's web content,\nincluding web content structures and templates, which you learned in the \n[Creating Web Content](/docs/7-2/user/-/knowledge_base/u/creating-web-content) \nsection. Site administrators can also use this option to manage their site's RSS\nfeeds. To add a new feed:\n\n1.  Go to the *Feeds* tab.\n\n2.  Click the *Add Feed* button.\n\n3.  Enter a *Name*, select a *Target Page*, and select a *Web Content \n    Structure* for the feed.\n\nA feed's target page serves two purposes:\n\n1. The site the target page belongs to determines which web content articles\n   appear in the feed. For example, if the target page belongs to the Marketing\n   site, only web content articles belonging to the Marketing site appear in the\n   feed.\n\n2. The target page is the page where \"orphaned\" web content articles are\n   displayed. Orphaned web content articles have been published in your Site but\n   are not displayed in specific Web Content Display applications. Liferay RSS\n   feeds can provide links to any published web content articles, both orphaned\n   articles and articles that have been configured to be displayed in specific\n   Web Content Display applications. For articles that have been configured to\n   be displayed, the RSS feeds' links point to the Liferay page of that app. For\n   orphaned articles, the RSS feeds' links point to the feed's target page. When\n   users click on such links for orphaned articles, the full content of the\n   orphaned article is displayed on the target page.\n\n![Figure 1: To create a new RSS feed, you only need to specify a name, target page, and web content structure. Of course, you can also configure other features of the feed such as its permissions, web content constraints, and presentation settings.](../../../../images/web-content-new-feed.png)\n\nTo specify a target page, you must enter the target page's friendly URL. Note\nthat friendly URLs don't include the host name. For example, the friendly URL\nof a public page called *Welcome* belonging to a Site called *Marketing* might\nlook like this: `/web/marketing/welcome`. Optionally, you can specify a target\nportlet ID. This would be the portlet ID of a Web Content Display application on\nthe target page in which orphaned web content should be displayed. The\napplication must exist or else the content isn't displayed. The URL field\ncontains the address of your RSS feed. It appears after you've actually created\nthe feed by clicking *Save*.\n\nThe final two sections of the *Add Feed* form are for customizing the web\ncontent articles that appear in your feed.\n\n1. Web Content Constraints selects a web content structure to filter the\n   articles that appear in your feed. This is useful since all web content\n   articles are created using web content structures.\n\n2. Presentation Settings customizes additional details about your feed and how\n   articles are displayed in your feed. Leave the Feed Item Content set to *Web\n   Content Description* if you want a description of each article to appear in\n   your feed. Set it to *Rendered Web Content: Use Default Template* if you want\n   the full content of each article to appear in the feed. Customizing the Feed\n   Type allows you to choose which web feed language to use for your feed. You\n   can choose *Atom 1.0* (the default), *RSS 1.0*, or *RSS 2.0*. Customize the\n   *Maximum Items to Display* to choose the maximum number of articles should\n   appear in your feed at one time. Leave the Order By Column set to *Modified\n   Date* to have articles arranged in order from the last time they were\n   published or modified. You can set the Order by Column to *Display Date* if\n   you want to have articles arranged in order from the time they were\n   configured to be displayed in a specific Web Content Display application.\n   Lastly, you can leave the Order by Type set to *Ascending* to have the oldest\n   articles at the top of the feed or you can set it to *Descending* to have the\n   newest articles at the top of the feed.\n\nWhen you're done configuring your RSS feed, click *Save* to create your feed.\n\nOnce one or more feeds have been created, they'll appear in a list in the Feeds\ntab. You can edit existing feeds using the same form used for creating them. \nThe main difference is that when you edit an existing feed, the URL field is \npopulated. Copy this URL into a new browser tab or window to test your feed. \nFrom the Feeds popup window, you can also customize the permissions of feeds or \ndelete feeds.\n\nIt's possible to completely disable RSS feeds at the instance level. You can do\nthis by setting the `rss.feeds.enabled` property to `false` in your\n`portal-ext.properties` file. By default, it's set to `true`. If you keep the\ndefault, RSS enabled, you can make several other RSS property customizations.\nPlease refer to the\n[RSS section](https://docs.liferay.com/ce/portal/7.2-latest/propertiesdoc/portal.properties.html#RSS)\nof your `portal.properties` file for details.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/06-publishing-content-dynamically/04-publishing-rss-feeds/03-rss-publisher-widget.markdown",
    "content": "---\nheader-id: the-rss-publisher-widget\n---\n\n# The RSS Publisher Widget\n\n[TOC levels=1-4]\n\nThe RSS Publisher widget displays RSS feeds. If you're looking for a web-based\nRSS reader, look no further: just add the RSS Publisher widget to one your\npersonal Site's private pages, and *voila*! You have your own personal RSS reader.\nYou can select the RSS feeds the widget displays and how it displays them. The\nRSS Publisher widget can also be placed on Sites' public or private pages to\nmake feeds available to guests or Site members, respectively. In these cases,\nmake sure that only Site administrators have permission to customize the RSS\nwidget and select feeds to be displayed.\n\n![Figure 1: The RSS Publisher widget lets you display RSS feeds of your choosing.](../../../../images/rss-widget-default-view.png)\n\n**Note:** If you run your server behind a proxy, you must set the appropriate\nJava proxy settings (such as `http.proxyHost=` and `http.proxyPort=`) in your\n`setenv` script or in your `system-ext.properties`. Without these properties,\nthe RSS Publisher widget can't access any RSS feeds.\n\nNote that the RSS Publisher widget is deprecated. In Liferay CE Portal 7.1 GA2+,\nand Liferay DXP 7.1 FP4+, the widget is available from the *Add*\n(![Add](../../../../images/icon-add-app.png))\n&rarr; *Widgets* &rarr; *News* menu. However, the widget is hidden in earlier\nreleases of Liferay CE Portal 7.1 and Liferay DXP 7.1. In these releases, you\nmust therefore make the widget visible via a configuration file. The next\nsection shows you how to do this.\n\n## Using the RSS Publisher Widget\n\nYou can add the RSS Publisher widget to a page from the *Add*\n(![Add](../../../../images/icon-add-app.png))\n&rarr; *Widgets* &rarr; *News* menu. Once you've done so, open the widget's\nConfiguration menu by clicking on the *Options* icon\n(![Options](../../../../images/icon-app-options.png)) at the top-right corner of the\nwidget and selecting *Configuration*.\n\n![Figure 2: The RSS Publisher widget's configuration lets you customize how the widget displays RSS feeds.](../../../../images/rss-widget-config.png)\n\n![Figure 3: You can also use the RSS Publisher widget's configuration to specify which feeds to display.](../../../../images/rss-widget-config-feeds.png)\n\nBy default, the RSS Publisher widget displays one feed. In the *Feeds* section,\nadd or remove a feed via the plus or minus buttons, respectively. To add a feed,\nenter its URL and title in the respective fields. If you leave the *Title* field\nblank, the feed's default title is used (the *Title* field is for custom\ntitles).\n\nIn the top section, use the following toggles to enable/disable the display of\nthe feed's details:\n\n-   Show Feed Title\n-   Show Feed Published Date\n-   Show Feed Description\n-   Show Feed Image\n-   Show Feed Item Author\n\nYou can also select the number of entries and expanded entries that should be\ndisplayed per feed. Expanded entries show more of an article's actual content\nthan regular entries. By default, each feed shows four entries per feed and\neight expanded entries per feed. You can set the feed image alignment to\ncontrol whether feed images appear to the right or left of the text. By default,\nthe feed image alignment is set to *Right*.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/07-restoring-deleted-assets-with-recycle-bin/01-restoring-deleted-assets-intro.markdown",
    "content": "---\nheader-id: restoring-deleted-assets\n---\n\n# Restoring Deleted Assets\n\n[TOC levels=1-4]\n\nHave you ever had that life-altering experience where you deleted an important\nfile and immediately regretted deleting it? The deed is usually followed by a\npalm to the forehead or a sick feeling. Good news! @product@ is here to turn \nthat frown upside down with the *Recycle Bin* feature. With the Recycle Bin, the\n*Move to the Recycle Bin* action replaces *Delete* for certain asset types.\nContent is now temporarily stored in the Recycle Bin. This allows the content to\nbe restored back to its original state. Recycled items can expire after a\ncertain period of time, resulting in their permanent deletion. Before diving\ninto how the Recycle Bin works, you'll look at how to configure it.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/07-restoring-deleted-assets-with-recycle-bin/02-configuring-recycle-bin.markdown",
    "content": "---\nheader-id: configuring-the-recycle-bin\n---\n\n# Configuring the Recycle Bin\n\n[TOC levels=1-4]\n\nThe Recycle Bin supports instance-wide scope or site-specific scope. The\ninstance-wide scope of the Recycle Bin is set by adding the `trash.enabled`\nproperty to your `portal-ext.properties` file. By default, the Recycle Bin is\nenabled instance-wide. You'll go into more detail for adding this property and\nseveral others to your properties file later in the section. First, you'll\nexplore the UI and see what the Recycle Bin can do.\n\nFirst, you'll configure the Recycle Bin for site-specific scoping.\n\n1.  Choose the Site you'd like configure for the Recycle Bin from the Site \n    Administration menu.\n\n2.  Click *Configuration* &rarr; *Settings*.\n\n3.  Next, select the top *Advanced* tab and click *Recycle Bin*. You'll notice \n    a few configurable options.\n\n    **Enable Recycle Bin:** enable and disable settings for the Recycle Bin's\n    site-specific scope.\n\n    **Trash Entries Max Age:** customize the number of minutes a file is kept \n    in the Recycle Bin until its permanent deletion (default is 43200 minutes, \n    or 30 days).\n\n    ![Figure 1: The Recycle Bin offers several configurable options for your site.](../../../images/recycle-bin-site-settings.png)\n\n4.  When you've finished configuring your Recycle Bin settings, click *Save*.\n\n| **Note:** If you disable the Recycle Bin while it's still holding recycled\n| items, the recycled items remain stored and reappear in the Recycle Bin if it is\n| re-enabled.\n\nYou can also configure the Recycle Bin via properties in the `portal.properties`\nfile. Remember that it's a best practice not to edit the `portal.properties`\ndirectly, but to create a separate `portal-ext.properties` file containing the\nproperties to override. There are some additional options not available in the\nGUI that you can set:\n\n`trash.search.limit=500`: set the limit for results used when performing\nsearches in the Recycle Bin (default is 500).\n\n`trash.entry.check.interval=60`: set the interval in minutes for how often the\ntrash handler runs to delete trash entries that have been in the Recycle Bin\nlonger than the maximum age (default is 60).\n\nAlso, as was mentioned earlier, there are properties to enable the Recycle bin\ninstance-wide and set trash entries' maximum age.\n\n`trash.enabled=true`: set this property to *false* to disable the Recycle Bin\nfor all sites in the portal (default is *true*).\n\n`trash.entries.max.age=43200`: set the number of minutes trash entries should be\nheld before being permanently deleted.\n\nVisit the\n[portal.properties](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Trash)\nfile to view all of the configurable properties for the Recycle Bin.\n\nNext, you should make sure permissions are set properly for users who can\nhandle/view the assets in the Recycle Bin. Users who had *View* permissions on\na document when it was recycled can also view that document in the Recycle\nBin. Users who had *Update* or *Delete* permissions on a document when it was\nrecycled can restore the document. \n\nNow that you've successfully configured the Recycle Bin, you'll look at how to\nuse it.\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/07-restoring-deleted-assets-with-recycle-bin/03-using-recycle-bin.markdown",
    "content": "---\nheader-id: using-the-recycle-bin\n---\n\n# Using the Recycle Bin\n\n[TOC levels=1-4]\n\nThe Recycle Bin is temporary storage where assets go when you delete \nthem. You can recycle several different types of assets:\n\n- Blogs\n- Bookmarks\n- Documents and Media\n- Message Boards (and attachments)\n- Web Content\n- Wiki (and attachments)\n\n![Figure 1: The Recycle Bin provides a seamless administrative experience for deleting and removing content.](../../../images/recycle-bin-overview.png)\n\n| **Note:** Attachments added to Wiki and Message Board entries do not go to the\n| Recycle Bin when they are deleted. They can be restored in a similar fashion\n| from the *Removed Attachments* menu within the application.\n\nTo demonstrate using the Recycle Bin let's delete a web content article and \nthen restore it. You'll run through two different methods of restoring the file.\n\n1.  Go to Site Administration and select *Content & Data* &rarr; *Web Content*.\n\n2.  Select the *Add* button (![Add Web Content](../../../images/icon-add.png))\n    and click *Basic Web Content*.\n\n3.  Enter some text for the Title and Content and click *Publish*.\n\n4.  Click the article's *Actions* button\n    (![Actions](../../../images/icon-actions.png)) and click\n    *Move to the Recycle Bin*.\n\n\tNote that the *Delete* button is not listed. @product@ avoids the risk of\n\taccidental deletion of your files by funneling the content through the\n\tRecycle Bin.\n\n5.  After deleting the file, a success message appears, offering an *Undo*\n    option. Click *Undo*. The web content is retrieved from the Recycle Bin and\n    stored in its original place.\n\n6.  Click *Move to the Recycle Bin* again.\n\n7.  Go back to Site Administration and click Recycle Bin from\n    the Content dropdown.\n\n8.  Find your sample web content and click its *Actions* button.\n\n9.  You can restore or delete the content. Select *Restore*.\n\n\t![Figure 2: In the Recycle Bin, you have the option of restoring or permanently deleting the content.](../../../images/recycle-bin-restore.png)\n\n10. Navigate back to the Web Content screen and notice that your sample web\n    content is back to its original place.\n\nThat covers the two general processes of sending and restoring content to/from \nthe Recycle Bin. For other asset types, the Recycle Bin works similarly.\n\nSome applications, such as Web Content and Documents and Media, \nsupport folders for organizing content. You can also send folders to the \nRecycle Bin. Keep in mind that this sends any sub-folders of the deleted folder \nall the files it contains to the Recycle Bin. Folders are restored and deleted \nthe same way as a single file.\n\n*Delete* within the Recycle Bin is the permanent delete button. Once you \nselect this, your file cannot be retrieved and is gone forever. There is also \nan *Empty the Recycle Bin* option accessible from the\n(![Options](../../../images/icon-options.png)) button at the top of the Recycle\nBin screen. This permanently deletes all the files from the Recycle Bin.\n\n## Drag and Drop\n\nYou can also drag and drop items into the Recycle Bin. While you're in the \nControl Panel, select an asset and drag it to the Recycle Bin portlet on the \nControl Panel menu. When you click and begin dragging the asset, a message \nappears near your cursor notifying you of the number of files ready to be \nmoved, and the Recycle Bin is highlighted, showing you where the files can be \ndropped. After you drop the asset onto the Recycle Bin portlet, the asset is \nremoved from its original location and transferred to the Recycle Bin.\n\n![Figure 3: A quick and easy way of disposing your items is the drag and drop method.](../../../images/recycle-bin-drag.png)\n\nAwesome! You now know how to use the Recycle Bin!\n"
  },
  {
    "path": "en/user/articles/100-web-experience-management/07-restoring-deleted-assets-with-recycle-bin/04-recycle-bin-intelligence-and-support.markdown",
    "content": "---\nheader-id: recycle-bin-intelligence-and-support\n---\n\n# Recycle Bin Intelligence and Support\n\n[TOC levels=1-4]\n\nHave you ever wondered what happens to file shortcuts if their linked assets \nare recycled? What if you restore a file that has the same name as another file \ncurrently stored in your site/instance? The Recycle Bin already knows how to \nhandle these types of issues.\n\nWhen documents with shortcuts are moved to the Recycle Bin, the shortcuts are\nremoved. This ensures that all your links and shortcuts work and cuts down on \nmaintenance time and backtracking.\n\nAnother important trait how recycled content is managed with the\n[Staging](/docs/7-2/user/-/knowledge_base/u/staging) framework.\n\nAlthough you there is only one master Recycle Bin for all asset types, when \nstaging is enabled a *Staging* Recycle Bin is created. The original Recycle \nBin, or *Live* Recycle Bin, is still viewable while in staging; however, it is \nnever used.\n\nDuring staging, everything you recycle is sent to the Staging Recycle Bin. This\nprevents staged and unstaged recycled content from mixing. For example, if you \nhave an unstaged document currently on your live site you can enable staging \nand delete that document. If you were to turn staging off and return to the \nlive site, without separate Recycle Bins, the live document would be both on\nyour site and in the Recycle Bin! Because of this, the separate Staging Recycle\nBin is necessary and only used during the staging process. When you publish your\nstaged material, the Staging Recycle Bin content is transferred to the Live\nRecycle Bin.\n\n| **Note:** The Staging Recycle Bin saves its contents until the staged material\n| has been published to the live site. This means that you can turn the staging\n| mode on and off without losing your recycled material.\n\nThe Recycle Bin saves you time by letting you restore content that's been\nrecycled. Instead of recreating or re-uploading content, you'll be tailoring\nyour Liferay instance to fully leverage its capabilities.\n"
  },
  {
    "path": "en/user/articles/110-collaboration/01-intro.markdown",
    "content": "---\nheader-id: collaboration\n---\n\n# Collaboration\n\n[TOC levels=1-4]\n\n@product@ contains an expansive collaboration suite that empowers users to \ncreate content and communities that they couldn't create alone. A robust \ndocument management system is a key component of this suite. As users produce\ndigital assets---documents, videos, audio---they can store and share them using\nthe Documents and Media Library. Documents and Media supports file check in and\ncheck out to prevent conflicting edits from multiple users, and maintains\na version history of those files. It also contains its own repository, and for\nadded flexibility can connect to external repositories. Once files exist in\nDocuments and Media, users can insert them in other content like blog posts and\nwiki articles. \n\n![Figure 1: You can use the Documents and Media Library to manage and use documents in the portal.](../../images/dm-images-in-admin.png)\n\nThe collaboration suite also contains apps that let users share information and \ncreate active communities. The Message Boards app gives users a platform for \ndiscussions. The Blogs app lets users publish their ideas using rich content. \nNotifications keep users informed of what's happening. Social networking apps \nlet users connect and share in ways that bolster friendship and productivity. \nAnd this is just scratching the surface---there are many more apps that help \nusers communicate, produce, and present. \n\n![Figure 2: You can also make your blog entries look great.](../../images/blog-entry-abstract.png)\n\nThe guides that follow show you how to leverage these features, and more, in \ndetail. \n\n![Figure 3: The Message Boards app is fantastic for facilitating discussions.](../../images/message-boards-category-threads.png)\n\n\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/01-intro.markdown",
    "content": "---\nheader-id: managing-documents-and-media\n---\n\n# Managing Documents and Media\n\n[TOC levels=1-4]\n\nThe Documents and Media library stores files on the server using the same type \nof structure that you use to store files locally. It accepts files of any kind, \ncan serve as a virtual shared drive, and can mount and browse external \nrepositories. You can organize documents using customizable document types and \nmetadata sets and display them with automatic document preview generation. Its \ncompanion app, the Media Gallery, displays selected content from the Documents \nand Media library. It can render image, audio, and video files. \n\nLiferay Sync synchronizes Documents and Media folders with local folders on your \ndevices, both your desktop machines and mobile devices. \n\nYou'll get started with Documents and Media by exploring how to publish files. \n\n<!-- Add images once other content with those images is merged in -->\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/02-publishing-files/01-intro.markdown",
    "content": "---\nheader-id: publishing-files\n---\n\n# Publishing Files\n\n[TOC levels=1-4]\n\nAs you create sites, you'll probably want to share files on them. The Documents\nand Media library (Document Library) lets you upload and publish all kinds of\nfiles on your sites. Pictures, videos, spreadsheets, slide presentations, and\nmore can be stored in and shared from the Document Library. Document Library\ninstances can be scoped to a portal instance, site, or page, so you can work\nwith files where they're relevant. \n\nHere, you'll learn how to add files, display them, and collaborate on them. \nYou'll learn how to use both the Documents and Media Library and the Media \nGallery. And lastly, you'll learn how to collaborate on files from within \nseveral environments, including your browser and local desktop file system. \n\n![Figure 1: These documents are awesome.](../../../../images/dm-images-in-admin.png)\n\n![Figure 2: This slideshow rules.](../../../../images/dm-media-gallery-slideshow.png)\n\n![Figure 3: Viewing a file's details is fun.](../../../../images/dm-file-entry-details.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/02-publishing-files/02-adding-files.markdown",
    "content": "---\nheader-id: adding-files-to-a-document-library\n---\n\n# Adding Files to a Document Library\n\n[TOC levels=1-4]\n\nThis article covers the following topics to help you get started adding files to\nyour Document Library: \n\n1.  **Granting File Permissions and Roles:** Determine who can add, view, and \n    update files. Doing this before adding files ensures that only those you \n    wish can access your Document Library. \n\n2.  **Adding Files:** Add specific types of files and their associated \n    metadata to your Document Library. \n\n## Granting File Permissions and Roles\n\nYou should carefully manage who can add, view, and update files. You can store\nfiles of all kinds for various purposes. For example, you may have one set of\nfiles intended for only specific site members and another intended for everyone,\nincluding guests. You can use \n[Roles and Permissions](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions) \nto control access to Document Library files. The Document Library's folder\npermissions also help you organize files. \n\nFollow these steps to create a Role for managing files in your site's Documents\nand Media: \n\n1.  Open the *Menu* (![Product Menu](../../../../images/icon-menu.png)) and \n    navigate to *Control Panel &rarr; Users &rarr; Roles*. \n\n2.  Select the *Site Roles* tab (or *Organization Roles*, for \n    an Organization Role) and then click the *Add* button \n    (![Add](../../../../images/icon-add.png)) to begin creating a role.\n\n3.  Give your Role a name and a description, then click *Save*.\n\n4.  Select your Role's *Define Permissions* tab. In the Role's permission \n    definition screen, navigate to *Site Administration* &rarr; *Content & Data* \n    &rarr; *Documents and Media*. In the *General Permissions* section, select \n    *Access in Site Administration* and click *Save*.\n\n    ![Figure 1: It's often helpful to define a role for specific users to access Documents and Media from Site Administration.](../../../../images/dm-define-role-permissions.png)\n\n5.  Assign this Role to the Users that should manage media. For more \n    information on this and other topics related to Roles, see \n    [Roles and Permissions](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions). \n\n## Using the Add Menu\n\nFollow these steps to add files to your site's Document Library:\n\n1.  Open the *Menu* (![Product Menu](../../../../images/icon-menu.png)), click \n    on your site's name, and navigate to *Content & Data* &rarr; \n    *Documents and Media*. \n    The Documents and Media screen appears and displays the Documents and Media \n    library's *Home* (its root folder). As you add files and folders to the \n    Document Library, they're listed here. \n\n    ![Figure 2: The Documents and Media's *Home* folder starts empty.](../../../../images/dm-admin-home.png)\n\n2.  Click the *Add* icon (![Add](../../../../images/icon-add.png)) and select \n    the type of document to add to the Document Library. You can add documents, \n    folders, and shortcuts much like you would on a desktop file system. You can\n    even configure access to an entirely different repository. The Add menu's\n    options are described below. \n\n    ![Figure 3: The Add menu lets you upload and add all kinds of documents to the library.](../../../../images/dm-admin-add-menu.png)\n\n3.  When you're finished selecting the file to upload and filling out any \n    document type fields that are necessary, click *Publish*. \n\n**File Upload:** Upload a file to the Documents and Media library. \n\n**Folder**: Create a new folder in the Documents and Media library's file \nsystem. \n\n**Multiple Files Upload:** Upload several files at once. You can apply a single \ndescription and document type to all the files. You can also \n[categorize and tag](/docs/7-2/user/-/knowledge_base/u/organizing-content-with-tags-and-categories) \nthe files, and assign them default permissions. \n\n**Repository**: Add access to an external repository. \n\n<!--Uncomment once article is available\nSee \nStore Types \nfor more information. \n-->\n\n**Shortcut**: Create a shortcut to any document that you can view. You \ncan set permissions on the shortcut to specify who can access the \noriginal document via the shortcut. \n\nAny additional items in the Add menu are \n[document types](/docs/7-2/user/-/knowledge_base/u/document-types) \ndescribed by a unique \n[metadata set](/docs/7-2/user/-/knowledge_base/u/metadata-sets). \nWhen you add a document belonging to a document type, a form appears that lets \nyou pick the file to upload and enter the data defined by the document type's \nmetadata set. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/02-publishing-files/03-creating-folders.markdown",
    "content": "---\nheader-id: creating-folders\n---\n\n# Creating Folders\n\n[TOC levels=1-4]\n\nYou'll need folders to organize all but the most limited set of files. Here,\nyou'll learn how to work with folders in a Document Library: \n\n-   [Adding a Folder](#adding-a-folder)\n-   [Document Type Restrictions and Workflow](#document-type-restrictions-and-workflow)\n-   [Setting Folder Permissions](#setting-folder-permissions)\n\n## Adding a Folder\n\nFollow these steps to add a folder: \n\n1.  Open the *Menu* (![Product Menu](../../../../images/icon-menu.png)), click \n    on your site's name, and navigate to *Content & Data* &rarr; \n    *Documents and Media* for your site. The Documents and Media screen appears \n    and displays the Documents and Media library's *Home* (its root folder). \n\n2.  Click the *Add* icon (![Add](../../../../images/icon-add.png)) and select \n    *Folder*. The New Folder form appears. \n\n3.  In the New Folder form, name and describe your folder. Then expand the \n    *Permissions* section.\n\n4.  In the Permissions section, set the folder's permissions. The *Viewable by*\n    menu lets you select who has View permission for the folder:\n\n    -   Anyone (the Guest role; this is the default option)\n    -   Site Members\n    -   Owner\n\n    Click the *More Options* link to choose the other folder permissions for the\n    Guest and Site Member roles. By default, site members can add files,\n    subfolders, shortcuts, and subscribe to changes to the folder's files.\n    Guests don't have any such permissions, which is typically what you want. \n\n    ![Figure 1: Select your folder's permissions.](../../../../images/dm-folder-permissions.png)\n\n5.  To finish creating the folder, click *Save* after making your selections in \n    the Permissions section. \n\nUpon creating the folder, it appears in your Document Library. Opening the \nfolder's *Actions* menu \n(![Actions](../../../../images/icon-actions.png)) presents several options for \nmanaging the folder. The following sections describe some of these options. \n\n![Figure 2: Your new folder appears in the Document Library.](../../../../images/dm-folder.png)\n\n## Document Type Restrictions and Workflow\n\nAfter creating a folder, you can restrict what document types are allowed in it. \nYou can also choose what \n[workflow](/docs/7-2/user/-/knowledge_base/u/workflow) \n(if any) to use for approving files added to or edited in the folder. \n\nFollow these steps to change a folder's document type restrictions and workflow: \n\n1.  Click the folder's *Actions* menu \n    (![Actions](../../../../images/icon-actions.png)) and select *Edit*. \n\n2.  Expand the *Document Type Restrictions and Workflow* section. In this \n    section, choose from the following options:\n\n    -   Use Document Type Restrictions and Workflow of the Parent Folder (the \n        parent folder)\n    -   Define Specific Document Type Restrictions and Workflow for this Folder \n        (the current folder)\n    -   Default Workflow for this Folder (the current folder)\n\n3.  Click *Save* when you're finished. \n\n![Figure 3: You can set the document type restrictions and workflow to use for a folder's files.](../../../../images/dm-restrictions-workflow.png)\n\n## Setting Folder Permissions\n\nWhen creating a folder, you can set some of its permissions via the new folder\nform. Fine tuning a folder's permissions, however, can only be done after\ncreating the folder. \n\nFollow these steps to fine tune a folder's permissions: \n\n1.  Click the folder's *Actions* menu \n    (![Actions](../../../../images/icon-actions.png)) and select *Permissions*. \n    The Permissions window appears. \n\n2.  In the Permissions window, set the permissions you want to use for this\n    folder. The permissions listed below are available for each role.\n\n3.  Click *Save* when you're finished setting permissions. \n\nHere are the permissions you can set: \n\n**Delete**: Move the folder to the Recycle Bin.\n\n**Permissions**: View and modify the folder's permissions.\n\n**Add Subfolder**: Create folders within the folder.\n\n**Add Shortcut**: Create a shortcut (link) to any file in the folder \nthat the role is authorized to view.\n\n**Update**: Edit the folder's attributes and/or move the folder under a \nnew parent folder.\n\n**Add Document**: Add a new file to the folder. \n\n**Subscribe**: Receive email notifications when files are added to or \nmodified in the folder. Note that you can specify the email sender and \ntemplate from the Documents and Media's *Options* \n(![Options](../../../../images/icon-options.png)) &rarr; *Configuration* \nmenu. \n\n**View**: View the folder. \n\n**Access**: Access the folder's contents. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/02-publishing-files/04-dm-management-bar.markdown",
    "content": "---\nheader-id: using-the-documents-and-media-management-bar\n---\n\n# Using the Documents and Media Management Bar\n\n[TOC levels=1-4]\n\nThe Documents and Media *Management Bar* is where people who manage documents \ngo to unwind after a long day at work. Just kidding. The Management Bar, as its \nname implies, contains tools for managing the files and folders in your \nDocument Library. It appears above the files and folders in Documents and Media. \n\n![Figure 1: The Management Bar is a great place to hang out if you're managing documents.](../../../../images/dm-management-bar.png)\n\nIf you've added files or folders to your Document Library, then you're already \nfamiliar with the Management Bar's *Add* button \n(![Add](../../../../images/icon-add.png)). The sections that follow describe the \nrest of the Management Bar. \n\n| **Note:** If a Document Library contains more items than it can display at \n| once, you can use the navigation tool that appears at the bottom of the window \n| to switch your view to another page or configure the page to display more \n| items per page. \n\n## View Types\n\nThe *View Types* button is to the left of the Add button. It lets you choose how \nto display the Document Library's items. The View Types button's icon depends on \nthe selected view type: \n\n**Cards** (![Cards](../../../../images/icon-view-type-cards.png)): Shows a \ncard-like rendering of the item. If the item isn't an image, a generic image for \nthe item's type is displayed. For files, each card also contains the file's \nsuffix (e.g., JPG, PNG, etc.), timestamp, name, and \n[workflow](/docs/7-2/user/-/knowledge_base/u/workflow) \nstatus (e.g., Approved, Draft, etc.). \n\n**List** (![List](../../../../images/icon-view-type-list.png)): Shows the same \ninformation as the Cards view type, in a list with small file renderings. \n\n**Table** (![Table](../../../../images/icon-view-type-table.png)): Shows the \nsame information as the other view types, in a list with no file renderings. \nAlso, the file information is in columns. \n\nThe items in all view types have an Actions menu \n(![Actions](../../../../images/icon-actions.png)). These actions are also\navailable in when viewing each item separately. \n\n![Figure 2: The Cards View type shows items in large card-like renderings.](../../../../images/dm-images-in-admin.png)\n\n## The Info Panel\n\nTo display an info panel with the current folder's details, click the \n*Information* icon\n(![**i**](../../../../images/icon-information-dm.png)). The info panel slides \nout from the right side of the screen and contains the folder's name and number \nof items. It also has these buttons: \n\n**Subscribe** (![Subscribe](../../../../images/icon-star.png)): Get \nnotifications about files added to or modified in the folder. \n\n**Actions** (![Actions](../../../../images/icon-actions.png)): Lists actions you \ncan perform on the current folder. \n\n## Finding and Arranging Items\n\nThe Management Bar also contains tools that help you locate and arrange items in \nthe Document Library. The most prominent of these tools is the *Search* bar, \nwhere you can find files by keywords. \n\nTo the left of the Search bar, the Sort button \n(![Sort](../../../../images/icon-sort.png)) arranges items in ascending \nor descending order. \n\nYou can also arrange items via the *Filter and Order* selector using these\ncriteria: \n\n**All:** Shows all of the current folder's immediate subfolders and files \n(default).\n\n**Mine:** Shows all the current user's files (no matter their folder).\n\n**Document Type:** Shows the files of the selected document type. Upon choosing \nthis option, you must select the document type you want from a popup. \n\nYou can also select from the following criteria for ordering items: \n\n-   Size\n-   Downloads\n-   Modified Date (default)\n-   Create Date\n-   Title\n\n## Selecting Items\n\nThe checkbox on the left-most side of the Management Bar selects all currently \ndisplayed items. Selecting multiple items lets you act on all of them at once. \nYou can also select multiple items individually by using the checkboxes for \neach. When you select one or more items, the Management Bar changes to reflect \nthe actions you can take on the selected items. \n\n![Figure 3: With items selected, the Management Bar changes.](../../../../images/dm-management-bar-actions.png)\n\nHere are the actions you can take on the selected items:\n\n-   Download (![Download](../../../../images/icon-download.png))\n-   Move (![Move](../../../../images/icon-move.png))\n-   Edit Tags\n-   Move to Recycle Bin (![Delete](../../../../images/icon-trash.png))\n\nThe Actions button \n(![Actions](../../../../images/icon-actions.png)) \ncontains all the actions displayed in the Management Bar, plus actions for file \ncheckin and checkout. File checkout and checkin is explained in \n[Checking out and Editing Files](/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files). \n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/02-publishing-files/05-file-previews.markdown",
    "content": "---\nheader-id: viewing-file-previews\n---\n\n# Viewing File Previews\n\n[TOC levels=1-4]\n\nFile previews help users browse and find media efficiently. To view a preview of \na file, click the file's name in the Document Library. If the file is an image,\nthe image appears. If an app is installed that can render a preview of the file\ntype, a representative image of the file appears (e.g., the opening frame of\na video file or a presentation's first slide). If there are no such preview apps\nfor the file, a generic image based on the file type appears. \n\n![Figure 1: File previews let you view and manage a file.](../../../../images/dm-file-entry-details.png)\n\n## File Preview Apps\n\nWhenever possible, @product@ generates previews of documents added to the\nDocument Library. Out of the box, Java-based APIs generate previews. The only\ntool available that is 100% Java and has a compatible license to be distributed\nwith @product@ is \n[PDFBox](https://pdfbox.apache.org). \nA separate thread generates a preview for PDFs when uploaded. This process may\nlast only a few seconds for a small file. The larger the file, the longer it\ntakes. \n\nWhile PDFBox provides a default implementation of image generation for document \npreviews and thumbnails, you must install and configure additional tools to\nharness the full power of document previews. These tools include: \n\n-   [OpenOffice](http://www.openoffice.org) or\n    [LibreOffice](http://www.libreoffice.org): \n    Using one of these in server mode lets you generate thumbnails and previews \n    for supported file types (`.pdf`, `.docx`, `.odt`, `.ppt`, `.odp`, etc.), \n    view documents in your browser, and convert documents. \n\n-   [ImageMagick](http://www.imagemagick.org) (also requires\n    [Ghostscript](http://www.ghostscript.com)): \n    Enables faster and higher-quality previews and conversions. \n\n-   [Xuggler](http://www.xuggle.com/xuggler): \n    Enables audio and video previews, lets you play audio and video files in \n    your browser, and extracts thumbnails from video files. \n\nAfter installing these tools, you can configure them via portal properties in \nthe Control Panel's Server Administration screen, or in a \n`portal-ext.properties` file. To learn how to use these tools, see \n[Configuring @product@](/docs/7-2/user/-/knowledge_base/u/setting-up). \n\nWith these tools installed and configured, a customized viewer displays \nDocuments and Media content, depending on the content type. For example, you can \nview a document with a customized viewer that lets you navigate through the \ndocument's pages. You can also view and play multimedia documents (audio or \nvideo). If the browser supports HTML5, the viewer uses the browser's native \nplayer. Otherwise it falls back to a Flash player. \n\n## Managing Files\n\nYou can also manage a file from its preview. The bar above the preview contains \nthese buttons: \n\n**Info** (![Info](../../../../images/icon-information-dm.png)): \nOpen/close the file's info panel. This panel contains more detailed information \nabout the file. For more information on this, see \n[The Info Panel](#the-info-panel). \n\n**Share**: Share the file with other users. For more information, see \n[Sharing Files](/docs/7-2/user/-/knowledge_base/u/sharing-files). \n\n**Download**: Download the file. \n\n**Actions** (![Actions](../../../../images/icon-actions.png)): \nOpens a menu that lets you perform these actions on the file: \n\n-   **Download**\n\n-   **Edit:** Modify the file's name, description, document type, \n    categorization, and \n    [related assets](/docs/7-2/user/-/knowledge_base/u/defining-content-relationships).\n    You can even upload a new file to replace it. Note that modifying the file\n    increments its version. \n\n-   **Edit with Image Editor:** Edit the image in the Image Editor. The Image \n    Editor is explained in \n    [Editing Images](/docs/7-2/user/-/knowledge_base/u/editing-images). \n\n-   **Checkout/Checkin:** Checkout prevents others from editing the document \n    while you are working on it. Other users can still view the current version \n    of the document, if they have permission. You can check in the document when \n    you're done with it. \n\n-   **Move:** Relocate the file to a different parent folder. \n\n-   **Permissions:** Specify which actions each role can perform on the file. \n\n-   **Move to Recycle Bin:** Move the file from the Documents and Media library \n    to the Recycle Bin. \n\n-   **Share** \n\nAlso note that the *Options* menu \n(![Options](../../../../images/icon-options.png)) at the top-right of the \nscreen contains the same actions as the Actions menu.\n\nThe comments area (below the preview area) lets you comment on and subscribe to \ncomments on the file. \n\n### The Info Panel\n\nAs mentioned above, clicking the *Info* icon \n(![Info](../../../../images/icon-information-dm.png)) opens the info panel. The \ntop of the info panel displays the file's name, version, and \n[workflow status](/docs/7-2/user/-/knowledge_base/u/workflow). \nThere are two tabs in the info panel: Details, and Versions. Details is selected \nby default and shows the following: \n\n**Owner:** The file's owner. \n\n**Download:** A button to download the file. \n\n**Latest Version URL:** A URL to access the newest version of the file. \n\n**WebDAV URL:** A WebDAV URL for accessing the file via a desktop.\n\n**Document Type:** The file's document type. \n\n**Extension:** The file's extension (e.g., JPG, PDF, etc.).\n\n**Size:** The file's size on disk. \n\n**Modified:** The user that last modified the file, and when it was last \nmodified.\n\n**Created:** The user that created the file, and when it was created. \n\n**Ratings:** The file's average user rating. \n\n**Automatically Extracted Metadata:** Any and all metadata automatically \nextracted from the file. When adding new documents or viewing existing \ndocuments, a process is triggered automatically that extracts the file's \nmetadata. The library used by this process is TIKA and it's included out of the\nbox. Depending on your file's type and the metadata written with the file, you\ncan find out all kinds of details. In the case of audio or video files, the\nmedia's duration is displayed.\n\nTo instead view the file's version history, select the *Versions* tab near the \ntop of the info panel. The info panel then changes to list the different \nversions of the file and lets you view, download, remove, and revert to specific \nfile versions. File version history actions are explained in \n[Checking Out and Editing Files](/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files). \n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/02-publishing-files/06-editing-images.markdown",
    "content": "---\nheader-id: editing-images\n---\n\n# Editing Images\n\n[TOC levels=1-4]\n\nEditing and re-uploading images when you only need to apply simple edits is \ntedious. Docs & Media contains a simple built-in image editor for exactly this \nreason. To access the image editor, locate the image you want to edit. Click\nthe Actions icon (![Actions](../../../../images/icon-actions.png)) and select\n*Edit With Image Editor*. \n\nYou can also access the image editor when selecting an image to insert in \ncontent (i.e., via an item selector). Anywhere an image is, you can edit it. For \nexample, you can access the image editor via item selector preview windows in \nblog entries and web content articles. To do this, click the pencil icon \n(![Pencil](../../../../images/icon-edit-pencil.png)) in the bottom-right corner \nof the preview window. \n\n![Figure 1: You can access the image editor through the Documents and Media repository.](../../../../images/image-editor-docs-and-media.png)\n\n![Figure 2: You can also access the image editor through the item selector preview window.](../../../../images/image-editor-preview-window.png)\n\nIf you edit and save the image via the Documents and Media repository, the file \nversion is incremented a minor version (e.g., from version 1.0 to version 1.1). \nYou can view the image's version history (and previous versions) by clicking the \nimage, clicking its *Info* button \n(![**i**](../../../../images/icon-information-dm.png)), and then selecting the \n*Versions* tab. In contrast, if you edit and save an image via an item selector, \na copy of the image is created and saved to the Document Library. \n\nLiferay designed the image editor with quick editing in mind. It offers a \nminimal, user-friendly UI. The main toolbar consists of three buttons, each of \nwhich contains a subset of options: \n\n![Figure 3: The image editor's UI is clear and to the point, offering only what you need.](../../../../images/image-editor-tools.png)\n\n**Transform** (![Transform](../../../../images/icon-transform.png))\n\n-   **Rotate**: Rotate the image to the left or right, in 90 degree increments.\n-   **Resize**: Resize the image. If the lock is closed, the aspect ratio is \n    locked and changing width or height automatically adjusts the other \n    dimension to maintain the aspect ratio. When the lock is opened, the width \n    and height can be changed individually, letting the aspect ratio change \n    (this isn't recommended because the image can become distorted). \n-   **Crop**: Crop the image. \n\n**Adjustment** (![Adjustment](../../../../images/icon-adjustment.png))\n\n-   **Saturation**: Adjust the color saturation. The default value is 50. Values \n    range from 0 (completely desaturated) to 100 (completely saturated). \n-   **Contrast**: Adjust the contrast. The default value is 50. Values range \n    from 0 (no contrast) to 100 (full contrast). \n-   **Brightness**: Adjust the brightness. The default value is 50. Values range \n    from 0 (completely black) to 100 (completely white). \n\n**Filter** (![Wand](../../../../images/icon-wand.png)): Apply a filter to the \nimage.\n\n![Figure 4: Select from a set of preset image filters.](../../../../images/image-editor-filters.png)\n\nUpon editing the image in the editor, you can click the *Cancel* button to \ncancel the changes, or the *Apply* button to apply them. Upon applying the \nchanges, the history bar appears. It lets you undo, redo, or reset the changes. \nUse the Reset button with caution; it resets the image to its original state, \nreverting all changes made in the editor. \n\n![Figure 5: The history bar lets you undo, redo, and reset changes.](../../../../images/image-editor-history-bar.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/02-publishing-files/07-publishing-files.markdown",
    "content": "---\nheader-id: publishing-files-site\n---\n\n# Publishing Files\n\n[TOC levels=1-4]\n\nOnce your Document Library contains files, you may want to publish them in your\nsite. Here are some ways to publish files:\n\n-   Show them in a Documents and Media app.\n-   Display them in a Media Gallery.\n-   Use the Asset Publisher.\n-   Insert them in an asset like a web content article or blog entry.\n\nHere, you'll learn to use the Media Gallery. \n\n## Using the Media Gallery\n\nThe Media Gallery publishes your media files in a simple gallery-like style. It\nshows a large thumbnail of each media file, lets the user download files, and\nhas slideshow capabilities. A common way to use the Media Gallery is to create\na separate page for displaying media and add a Media Gallery widget to it. This\nway, your media takes center stage. \n\nFollow these steps to create a page that contains a Media Gallery widget: \n\n1.  [Create a page](/docs/7-2/user/-/knowledge_base/u/creating-and-managing-pages) \n    and navigate to it in your site. \n\n2.  At the top-right of the screen, click the *Add* icon\n    (![Add](../../../../images/icon-add-app.png)) then navigate to \n    *Widgets* &rarr; *Content Management* and select *Add* next to *Media \n    Gallery* (alternatively, drag the Media Gallery onto your page). The Media \n    Gallery widget appears on the page. \n\n3.  Configure the Media Gallery widget to show your files. By default, it shows \n    files from the Home folder of your site's Documents Library. To choose a \n    different folder, click the widget's Options icon \n    (![Options](../../../../images/icon-app-options.png)) and select \n    *Configuration*. \n\n    The Configuration window appears and shows the *Setup* tab. This tab \n    contains these sections: \n \n    **Display Settings:** Lets you show each file's actions, filter the media \n    types to display, and choose a display template for your media. \n\n    **Folders Listing:** Lets you select a Document Library folder to serve as \n    the root folder from which to display files. The root folder you select \n    becomes the highest-level folder the Media Gallery can access. For example, \n    if you create a subfolder of a parent folder, and then set that subfolder as \n    the Media Gallery's root folder, the Media Gallery can no longer access the \n    parent folder. \n\n    | **Note**: To access the Carousel display template in Media Gallery, your \n    | role must have View access for that template. Since the Carousel template \n    | is in the Global scope, a Global-scope administrator must grant the role \n    | permission to view the template. \n\n    ![Figure 1: You can configure the Media Gallery to use any Documents and Media folder as its root folder.](../../../../images/dm-select-root-folder.png)\n\n4.  Configure the rest of the settings as desired in the Media Gallery app's \n    other configuration tabs: \n\n    **Communication:** Lists public render parameters the widget publishes \n    to other widgets on the page. Other widgets can take action on these \n    parameters. For each shared parameter, you can specify whether to allow \n    communication via the parameter and select which incoming parameter can \n    can populate it. \n\n    **Sharing:** Embed the widget instance as a widget on on any website, \n    Facebook, Netvibes, or as an OpenSocial Gadget. \n\n    **Scope**: Specify the Document Library instance the widget uses: the \n    current site's instance (default), the global instance, or the page's \n    instance. If the page doesn't already have an instance of the widget, you \n    can select *Your Page (Create New)* to create a page-scoped instance for the \n    widget to display. \n\n5.  Click *Save* when you're finished configuring the Media Gallery widget. \n\nThe Media Gallery now shows your files, with images appearing as thumbnails. \nWhen you click a thumbnail, a slideshow appears showing the selected image. \nBelow that image, thumbnails of the folder's other images are displayed. The \nslideshow continues until you click pause or view the last image. Closing the \nslideshow window returns you to the page. \n\n![Figure 2: The Media Gallery renders large thumbnail images of media files.](../../../../images/dm-media-gallery.png)\n\n![Figure 3: The Media Gallery's slideshow provides a nice way to view images.](../../../../images/dm-media-gallery-slideshow.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/02-publishing-files/08-checkout-edit-files.markdown",
    "content": "---\nheader-id: checking-out-and-editing-files\n---\n\n# Checking Out and Editing Files\n\n[TOC levels=1-4]\n\nWhen you check out a document in the Document Library, only you can make changes \nto it until you check it back in. This prevents conflicting edits on the same \ndocument by multiple users. When you check out a file, you can download it, \nreplace it, move it to another Document Library folder, check it in, or cancel \nthe checkout. Checking in a file also increments its version, which lets you \nkeep track of changes. \n\nUnless you're using \n[Liferay Sync](/docs/7-2/user/-/knowledge_base/u/using-liferay-sync-on-your-desktop) \nor a \n[local drive mapped to the file's WebDAV URL](/docs/7-2/user/-/knowledge_base/u/desktop-access-to-documents-and-media), \nfollow these steps to edit a Document Library file from your machine: \n\n1.  Checkout the file by clicking its Actions icon \n    (![Actions](../../../../images/icon-actions.png)) &rarr; *Checkout*. Upon \n    checkout, the file's status changes to Draft and a lock icon appears on the \n    file. \n\n    ![Figure 1: The file on the right in this image is checked out.](../../../../images/dm-file-checked-out.png)\n\n2.  Download the file by clicking its Actions icon \n    (![Actions](../../../../images/icon-actions.png)) &rarr; *Download*. \n\n3.  Edit the file locally. \n\n4.  Return to the Documents and Media Library and click the file's Actions icon \n    (![Actions](../../../../images/icon-actions.png)) &rarr; *Edit*. The file's \n    edit screen appears. \n\n5.  From the file's Edit screen, select the edited local file for upload. \n\n6.  Click *Save and Check In*. In the pop-up that appears, select whether your \n    change is a major or minor version, add any version notes that you need, and \n    click *Save*. \n\n| **Note**: If you edit a file without checking it out, the file's edit screen \n| displays a toggle for *Customize the Version Number Increment and Describe My \n| Changes*. Setting this to *YES* lets you specify the version increment's type \n| and description.\n\nFollow these steps to access a file's version history:\n\n1.  Click the file in the Documents and Media Library.\n\n2.  Click the file's *Info* button \n    (![**i**](../../../../images/icon-information-dm.png)) at the top-right of \n    the screen. This opens the file's info panel. \n\n3.  Select the *Versions* tab in the info panel. \n\nEach file version has an Actions menu \n(![Actions](../../../../images/icon-actions.png)) that you can use to perform \nthe following actions on that file version:\n\n**Download**: Download the selected version of the file to your machine. \n\n**View**: View the file entry screen for the selected version of the file. \n\n**Revert**: Restores the selected file version as a new major file version. Note \nthat this option isn't available for the newest file version. \n\n**Delete Version**: Remove the file version from the Document Library. All \nother file versions remain intact. \n\n![Figure 2: The version history actions let you inspect, delete, and reinstate file versions.](../../../../images/dm-file-version-history.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/02-publishing-files/09-sharing-files.markdown",
    "content": "---\nheader-id: sharing-files\n---\n\n# Sharing Files\n\n[TOC levels=1-4]\n\n@product@'s \n[role-based permissions system](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions) \ndefines which actions users can take on assets, including \n[files](/docs/7-2/user/-/knowledge_base/u/adding-files-to-a-document-library#granting-file-permissions-and-roles). \nAdministrators can let users collaborate on files by assigning the appropriate \nfile permissions to a Role, and then assigning users to that Role. Similarly, \nnon-administrative users can grant permissions to Roles for files they own. \n\nThis Role-based permissions system sometimes falls short. For example, if a Role \nappropriate for file collaboration doesn't exist, an administrator must create \nit and manage its users and permissions. Non-administrative users can't create \nor manage Roles. Also, if a user wants to share a file with one other user, it's \nnot practical for an administrator to create and manage a Role for only two \nusers. \n\n@product@'s sharing feature solves these problems by letting users share \nfiles directly with each other, without involving an administrator. This saves \ntime and effort for everyone. After all, sharing is caring. \n\n| **Note:** Administrators can disable sharing. For instructions on this, see \n| [Configuring Sharing](/docs/7-2/user/-/knowledge_base/u/configuring-sharing). \n\nWhen you share, you grant some of your own permissions for that file to the\nreceiving user. However, there are some important caveats: \n\n-   You can only grant View, Comment, or Update permissions. For example, you \n    can't grant Delete or Override Checkout permissions even if you have those \n    permissions on the file. \n-   You can only grant permissions you have on the file. For example, you can't \n    grant Update permission if you only have View and Comment permissions on the \n    file. \n-   You must grant at least View permission. \n-   Traditional Role-based permissions always take precedence over sharing \n    permissions. So although sharing can extend permissions, it can't remove \n    those granted via Roles in the portal. \n-   By default, the Guest Role has Add Discussion permission. This overlaps with\n    the Comment permission in sharing. Therefore, all users can comment on\n    a file regardless of whether the Comment permission was granted via sharing.\n    Administrators can change this by removing the Add Discussion permission\n    from the Guest Role. \n\nAlso note that the receiving user must be part of the same instance, but doesn't \nhave to be a member of the same Site. \n\n## Sharing Files in Documents and Media\n\nTo share a file, you must own that file or be an administrator. You must share\nfiles via the Documents and Media app in Site Administration or the Documents\nand Media widget on a page. \n\nFollow these steps to share a file: \n\n1.  Using the Documents and Media widget on a page or in Site Administration,\n    navigate to the file you want to share.\n\n    To navigate to the Documents and Media app in Site Administration, open the \n    *Menu* \n    (![Product Menu](../../../../images/icon-menu.png)), \n    click your Site's name, and go to *Content & Data* &rarr; \n    *Documents and Media*. \n\n    To share a file via the Documents and Media widget on a page, actions must \n    be enabled for the widget. Follow these steps to enable actions: \n\n    -   Select *Configuration* from the widget's *Options* menu \n        (![Options](../../../../images/icon-app-options.png)). \n    -   In the *Setup* tab's *Display Settings*, select *Show Actions*. \n    -   Click *Save* and close the Configuration window. \n\n2.  Click the file's *Actions* button \n    (![Actions](../../../../images/icon-actions.png)) \n    and select *Share*. This opens the Share dialog. \n\n    Alternatively, click the file in Documents and Media and then click the \n    *Share* button at the top-right. This opens the same Share dialog. \n\n    ![Figure 1: To share a file, you must fill out the Share dialog as these steps describe.](../../../../images/sharing-file.png)\n\n3.  Enter the name or email address of the user you want to share the file with. \n    To share the file with multiple users, enter each user's email address in a \n    comma delimited list. \n\n4.  To let receiving users also share the file, select \n    *Allow the document to be shared with other users*. Note, however, that \n    administrators can share the file regardless of your selection here. \n\n5.  Select the file permissions to grant to receiving users. Because you can \n    only grant your own permissions for the file, some of these options may be \n    unavailable: \n\n    -   **Update:** View, comment, and update.\n    -   **Comments:** View and comment.\n    -   **View:** View only.\n\n    If you enabled further sharing in the previous step, note that receiving \n    users can only share the file with the permissions you grant here. \n\n6.  Click *Share*. \n\n## Working with Shared Files\n\nYou can access files shared with you in three places: \n\n1.  **The Documents and Media Library:** Files shared with you are visible in \n    their existing Documents and Media locations. For example, if someone shares \n    a file with you that resides in the Documents and Media Library's Home \n    folder, then you can access the file in that folder. \n\n2.  **The Notifications app:** When a file is shared with you, you get a \n    notification in the Notifications app. Clicking the notification takes you \n    to the file in Documents and Media. For information on notifications, see \n    [Managing Notifications and Requests](/docs/7-2/user/-/knowledge_base/u/managing-notifications-and-requests). \n\n    ![Figure 2: The Notifications app contains the notifications that are sent when a user shares a file with you.](../../../../images/sharing-notifications.png)\n\n3.  **The Shared Content app:** This app lists all the content shared with you, \n    and the content you shared. You can access this app from your user menu. \n    Each file has an Actions button \n    (![Actions](../../../../images/icon-actions.png)) \n    for performing permitted actions on the file (e.g., view, comment, update). \n\n    ![Figure 3: The Shared Content app lists the files shared with you, and the files you shared.](../../../../images/shared-content-app.png)\n\n## Managing Shared Files\n\nAfter sharing a file, you can unshare it or modify its permissions on a per-user \nbasis. This can only be done by Administrators, the file's owner, or any user \nwith Update permission and permission to share the file. You can take these \nactions from the file's Info panel in Documents and Media. Follow these steps: \n\n1.  Click the file in Documents and Media, then click the *Info* button \n    (![Info](../../../../images/icon-information-dm.png)) \n    at the top-right. The file's Info panel slides out from the right. \n\n2.  Click the *Manage Collaborators* link. This shows a list of the users you\n    shared the file with and their file permissions. \n\n    ![Figure 4: Click *Manage Collaborators* to open up the list of users you shared the file with.](../../../../images/sharing-info.png)\n\n3.  Make any changes you want to the list of collaborators. To unshare the file \n    with a user, click the `x` icon next to that user. You can also change the \n    file permissions via the selector menu for each user. \n\n4.  Click *Save* and close the dialog. \n\n![Figure 5: The Collaborators dialog lets you unshare a file or change the file permissions for each user.](../../../../images/sharing-collaborators.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/02-publishing-files/10-configuring-sharing.markdown",
    "content": "---\nheader-id: configuring-sharing\n---\n\n# Configuring Sharing\n\n[TOC levels=1-4]\n\nAdministrators can choose whether \n[file sharing](/docs/7-2/user/-/knowledge_base/u/sharing-files) \nis enabled at the global, instance, and Site levels. \n\n## Global Configuration\n\nSharing is enabled globally by default. To configure sharing globally, follow \nthese steps:\n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr; \n    *Sharing*. \n\n2.  Under *SYSTEM SCOPE*, select *Sharing*. \n\n3.  Configure sharing via these settings: \n\n    **Expired Sharing Entries Check Interval:** The interval in minutes for how \n    often expired sharing entries are checked for deletion. \n\n    **Enabled:** Whether sharing is enabled globally. \n\n![Figure 1: Configure sharing globally.](../../../../images/sharing-system.png)\n\nWhen sharing is enabled globally, it's also enabled by default for all portal \ninstances. You can change this from *Virtual Instance Sharing* under \n*VIRTUAL INSTANCE SCOPE*: \n\n**Enabled:** Whether sharing is enabled by default for all instances in the \nportal. \n\n## Instance Configuration\n\nTo enable or disable sharing on a per-instance basis, follow these steps: \n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *Instance Settings* \n    &rarr; *Sharing*. \n\n2.  Select *Virtual Instance Sharing* under *VIRTUAL INSTANCE SCOPE*. \n\n3.  Check or uncheck the *Enabled* checkbox to enable or disable sharing, \n    respectively. \n\n![Figure 2: You can enable or disable sharing for each instance.](../../../../images/sharing-instance.png)\n\n## Site Configuration\n\nTo enable or disable sharing for a Site, follow these steps: \n\n1.  Go to Site Administration (your site's menu) &rarr; *Configuration* &rarr; \n    *Settings*. \n\n2.  Select the *General* tab. \n\n3.  Expand the *Sharing* section and use the toggle to enable or disable sharing \n    for the Site. \n\n![Figure 3: You can enable or disable sharing for each Site.](../../../../images/sharing-toggle.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/02-publishing-files/11-desktop-access.markdown",
    "content": "---\nheader-id: desktop-access-to-documents-and-media\n---\n\n# Desktop Access to Documents and Media\n\n[TOC levels=1-4]\n\nYou can access the Document Library from your desktop file manager via \n[WebDAV](https://en.wikipedia.org/wiki/WebDAV). \nWebDAV is a set of methods based on HTTP that let users create, edit, move, or \ndelete files stored on web servers. WebDAV is supported by most major operating \nsystems and desktop environments, including Linux, macOS, and Windows. Using\nyour file manager via WebDAV doesn't bypass the functionality of the web \ninterface---@product@ increments the version numbers of files edited and \nuploaded via WebDAV. \n\nTo access the Document Library folder from a file browser, you must use your\nlog-in credentials and the WebDAV URL of the folder you want to access. Follow\nthese steps: \n\n1.  Navigate to the Documents and Media app that contains the folder you want to \n    access. Click the folder's Actions icon \n    (![Actions](../../../../images/icon-actions.png)) and select *Access from \n    Desktop*. \n\n    ![Figure 1: Select *Access from Desktop* to get the folder's WebDAV URL.](../../../../images/dm-access-from-desktop-action.png)\n\n2.  Copy the WebDAV URL and follow the instructions for your operating system: \n\n    **Windows:** Map a network drive drive to the WebDAV URL. Enter your \n    credentials when prompted. The Document Library folder appears in the \n    network drive. From your file browser, you can now add, edit, move, or \n    delete files in this folder. \n\n    **macOS:** In the Finder, select *Go* &rarr; *Connect to Server*. In the \n    Server Address field, enter the WebDAV URL of the folder you want to access, \n    then click *Connect* and enter your credentials when prompted. \n\n    **Linux:** In your file manager, you must slightly modify the Document \n    Library folder's WebDAV URL. For KDE's Dolphin, change the URL's protocol to \n    `webdav://` instead of `http://`. For GNOME's Nautilus, change the URL's \n    protocol to `dav://` instead of `http://`. Then press *Enter* and enter your \n    credentials when prompted. \n\nNow you can access the Document Library folder from your desktop file system. If \nyou edit a file in this folder on your file system, the change also shows up in \nthe same Document Library folder in the portal. What's more, the file's minor \nversion is incremented due to the edit. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/02-publishing-files/12-linking-to-google-drive.markdown",
    "content": "---\nheader-id: linking-to-google-drive\n---\n\n# Linking to Google Drive™\n\n[TOC levels=1-4]\n\nYou can create Document Library files that link to files in Google Drive&trade; \nand Google Photos&trade;. This lets you access your Google files from the \nDocument Library. Note that this functionality isn't available by default. To \nenable it, you must complete these steps: \n\n1.  Install the Liferay Plugin for Google Drive&trade; from Liferay Marketplace. \n\n2.  Create and/or configure a Google project capable of communicating with your \n    @product@ instance. The \n    [Google Picker API](https://developers.google.com/picker/) \n    must be enabled for this project. This API lets you select Google files to \n    link to. You must also create the credentials the Google project needs to \n    communicate with your @product@ instance. \n\n3.  Configure your portal to communicate with your Google project. \n\nThis article shows you how to complete these steps and finishes with an example \nof linking to a Google file from the Document Library. \n\n| **Note:** You can also use Google Docs&trade; for online file creation and \n| editing. This doesn't require a plugin and is covered in a \n| [separate section of the documentation](/docs/7-2/user/-/knowledge_base/u/online-file-creation-and-editing-with-google-docs). \n\n| **Important:** The Liferay Plugin for Google Drive&trade; is a Labs \n| application available for Liferay CE Portal and Liferay DXP. Labs apps are \n| experimental and not supported by Liferay. They're released to accelerate the \n| availability of useful and cutting-edge features. This status may change \n| without notice. Use Labs apps at your own discretion. \n\n## Install the App\n\nFirst, you must install the the Liferay Plugin for Google Drive&trade; from \nLiferay Marketplace. This app is available via the following links for Liferay \nCE Portal and Liferay DXP: \n\n-   [Liferay Plugin for Google Drive - CE](https://web.liferay.com/marketplace/-/mp/application/105847499)\n-   [Liferay Plugin for Google Drive - DXP](https://web.liferay.com/marketplace/-/mp/application/98011653)\n\nIf you need help installing apps from Marketplace, see the documentation on \n[using Marketplace](/docs/7-2/user/-/knowledge_base/u/using-the-liferay-marketplace). \n\n## Configure Your Google Project\n\nFollow these steps to create and/or configure your Google project so it can \ncommunicate with your @product@ instance: \n\n1.  Go to the\n    [Google API Console](https://console.developers.google.com). \n    If you don't have a suitable project, \n    [create a new one](https://support.google.com/googleapi/answer/6251787?hl=en&ref_topic=7014522). \n\n2.  Enable the Google Picker API for your project. For instructions, see the \n    Google API Console documentation on \n    [enabling and disabling APIs](https://support.google.com/googleapi/answer/6158841). \n\n3.  Create an OAuth 2 client ID in your Google project. For instructions, see \n    the Google API Console documentation on \n    [setting up OAuth 2.0](https://support.google.com/googleapi/answer/6158849). \n    Enter these values when creating your client ID: \n\n    -   **Application type:** Web application\n    -   **Name:** Google Docs Hook\n    -   **Authorized JavaScript origins**: `[liferay-instance-URL]` (e.g., \n        `http://localhost:8080` is the default for local development machines)\n    -   **Authorized redirect URIs**: `[liferay-instance-URL]/oath2callback` \n\n4.  Create a new API key in your Google project. For instructions, see the \n    Google API Console documentation on \n    [creating API keys](https://support.google.com/googleapi/answer/6158862?hl=en). \n    Be sure to restrict the key to HTTP referrers (web sites), and set it to \n    accept requests from your @product@ instance's URL. \n\nYour new OAuth client ID and public API access key now appear on your Google \nproject's Credentials screen. Keep this screen open to reference these values as \nyou specify them in @product@. \n\n## Configure Your Portal\n\nNow that you have a Google project set up for use with @product@, you must \nconnect your installation to that project. You can do this at two scopes: \n\n1.  Globally, for all instances in your @product@ installation. \n2.  At the instance scope, for one or more instances in your @product@ \n    installation. \n\nYou can override the global configuration for one or more instances by \nconfiguring those instances separately. Similarly, you can configure only the \ninstances you want to connect to your Google project and leave the global \nconfiguration empty. \n\nFollow these steps to configure your @product@ installation to connect to your \nGoogle project: \n\n1.  Note that the configuration options are the same in the global and \n    instance-level configurations. \n\n    To access the global configuration, go to *Control Panel* &rarr; \n    *Configuration* &rarr; *System Settings* &rarr; *Documents and Media*. \n\n    To access the instance-level configuration, go to *Control Panel* &rarr; \n    *Configuration* &rarr; *Instance Settings* &rarr; *Documents and Media*. \n\n2.  Under *VIRTUAL INSTANCE SCOPE*, select *Google Drive*. \n\n3.  Enter your Google project's OAuth 2 client ID and client secret into the \n    *Client ID* and *Client Secret* fields. \n\n4.  In the field *Picker API Key*, enter the API key you created in the previous \n    section. \n\n5.  Click *Save*. \n\n![Figure 1: Enter your Google project's OAuth 2 client ID, OAuth 2 client secret, and Picker API key.](../../../../images/google-drive-system-settings.png)\n\n## Creating Linked Files\n\nWith the preceding configuration steps complete, you can create files in your \nDocument Library that link to files in Google Drive&trade; or images in Google \nPhotos&trade;. Follow these steps to do so:\n\n1.  In your Document Library, click the *Add* button \n    (![Add](../../../../images/icon-add.png)) \n    and select *Google Drive Shortcut*. The *New Google Drive Shortcut* screen \n    appears. \n\n    ![Figure 2: Select *New Google Drive Shortcut* from the *Add* menu in your Document Library.](../../../../images/dm-new-google-drive-shortcut.png)\n\n2.  Click the *Select File* button to open Google's file picker. \n\n3.  Use the file picker to select a file from Google Drive&trade; or Google \n    Photos&trade;. \n\n4.  Click *Publish*. \n\n![Figure 3: You can select files from Google Drive&trade; or your photos.](../../../../images/dm-google-select-a-file.png)\n\nA new file entry appears for the Google document you linked to. You can view the\nfile entry as you would any file entry. The Google document's contents show in \nthe file entry's preview pane. As with any file entry, the *Options* button \n(![Options](../../../../images/icon-options.png)) \ngives you access to the Download, Edit, Move, Permissions, Move to Recycle Bin, \nand Checkin/Checkout/Cancel Checkout options. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/03-metadata-sets/01-metadata-sets-intro.markdown",
    "content": "---\nheader-id: metadata-sets\n---\n\n# Metadata Sets\n\n[TOC levels=1-4]\n\nYou can define metadata fields that users fill out when they create or edit\nDocument Library files. You do this by creating *metadata sets* and then\nassociating them with document types, which wrap Document Library files and thus\napply your metadata fields to the files. Although you apply metadata sets via\ndocument types, metadata sets exist independently and you can apply them to any\nnumber of document types. \n\n## Managing Metadata Sets\n\nTo see the available metadata sets, open the *Menu* \n(![Product Menu](../../../../images/icon-menu.png)), expand your site's menu, and\nnavigate to *Content & Data* &rarr; *Documents and Media*. Then click the \n*Metadata Sets* tab. Any existing sets appear in a table. \n\nTo select a metadata set, select the checkbox to its left. To select all the\nsets, select the checkbox in the Management Bar. With one or more sets selected,\nan `X` icon appears in the Management Bar. Clicking it deletes the selected\nmetadata set(s). Note that metadata sets don't support the Recycle Bin. If you\ndelete a metadata set, it's gone forever. \n\nThe Management Bar also contains other options for managing the metadata sets.\nThe selector menu to the right of the checkbox filters the sets the table\ndisplays (it's set to *All* by default). The *Order by* selector orders the sets\nby Modified Date or ID. The up and down arrows sort the sets in ascending or\ndescending order, respectively. You can also use the Search bar to search for\na set. \n\nIn the table, each metadata set has an Actions button \n(![Actions Menu](../../../../images/icon-actions.png)) for performing the following\nactions on that set: \n\n**Edit**: Edit the set. Alternatively, click the set's name in the table.\n\n**Permissions**: Configure the set's permissions. \n\n**Copy**: Copy the metadata set. \n\n**Delete**: Delete the set. \n\n![Figure 1: The Metadata Sets management window lets you view existing sets and create new ones for applying to document types.](../../../../images/dm-metadata-sets-list.png)\n\n## Creating Metadata Sets\n\nFollow these steps to create a metadata set: \n\n1.  From the *Menu* (![Product Menu](../../../../images/icon-menu.png)), click your\n    Site's name and navigate to *Content & Data* &rarr; *Documents and Media*. \n    Then click the *Metadata Sets* tab. \n\n2.  Click the *Add* button \n    (![Add](../../../../images/icon-add.png)). The New Metadata Set form appears. \n\n3.  Give your metadata set a name. \n\n4.  Open the *Details* section of the form to give your metadata set a \n    description or select a metadata set to extend (both are optional). To \n    select a metadata set to extend, click the *Select* button for *Parent \n    Metadata Set* and then select the metadata set. When a user creates a \n    document of a document type that uses an extended metadata set, the parent \n    metadata set's fields appear above the extended metadata set's. \n\n5.  Add the metadata fields that should be part of this metadata set. To do \n    this, first select the editor's *View* tab and select the *Fields* tab \n    within it. Icons representing the field types are listed on one side and the \n    metadata set's canvas is on the other side. To add a field type to the\n    metadata set, select its icon, drag, and drop it onto the canvas. The field\n    appears on the canvas as it does for users. By dragging a field onto a field \n    that's already on the canvas, you can nest the new field in the existing \n    field. When you mouse over a field on the canvas, the field action icons \n    (![Icons](../../../../images/icon-dm-metadata-actions.png)) \n    appear. Clicking the *+* icon creates a duplicate of the current field and \n    adds it below the current field. Clicking the trash can deletes the field. \n\n    The following metadata fields are available:\n\n    -   **Boolean:** A check box. \n    -   **Color:** Specifies a color. \n    -   **Date:** Enter a date. A valid date format is required for the \n        date field, but you don't have to enter a date manually. When you select \n        the date field a mini-calendar pops up which you can use to select a \n        date. \n    -   **Decimal:** Enter a decimal number. The value is persisted as \n        a `double`. \n    -   **Documents and Media:** Select a file from a Documents and Media \n        library. \n    -   **Geolocation:** Specify a location to associate with the \n        document.\n    -   **HTML:** An area that uses a WYSIWYG editor to enhance the content. \n    -   **Integer:** Enter an integer. The value is persisted as an \n        `int`. \n    -   **Link to Page:** Link to another page in the same site.\n    -   **Number:** Enter a decimal number or an integer. The value is\n        persisted either as a `double` or an `int`, depending on the input's \n        type. \n    -   **Radio:** Displays several clickable options. The default number of \n        options is three but this is customizable. Only one option can be \n        selected at a time.\n    -   **Select:** This is just like the radio field except that the options \n        are hidden and must be accessed from a drop-down menu. \n    -   **Text:** Enter a single line of text.\n    -   **Text Box:** This is just like the text field except you can enter \n        multiple lines of text or separate paragraphs. \n    -   **Web Content:** Select web content.\n\n    ![Figure 2: Add your metadata set's fields to the canvas.](../../../../images/dm-metadata-set-fields.png)\n\n6.  Edit your fields to reflect their intended metadata. For example, a text \n    field's default label is *Text*. If you want to use the text field as a \n    title, for instance, then you should change the field's label to *Title*. To \n    do this, first select the field on the canvas. This automatically selects \n    the *Settings* tab on the left. Alternatively, you can access the Settings \n    tab by clicking the field's wrench icon. To edit a setting value, \n    double-click it in the Settings table and enter the new value. \n\n    Labels, default values, variable names, mouse-over tips, widths, and other \n    settings can be configured for most fields. Some fields have a *Required* \n    setting for specifying whether users must populate the field. If a field's \n    *Repeatable* setting is *Yes*, users can add multiple consecutive instances \n    of the field to the document's metadata. \n\n    Also note that you can translate each of a metadata set's field values to\n    any supported locales. To specify a field value for a translation, select\n    the flag that represents the locale and enter the field value for the\n    locale. \n\n7.  Click *Save* when you're done specifying your new metadata set.\n\n    ![Figure 3: Edit your metadata set's fields to match the metadata that you want each field to hold.](../../../../images/dm-metadata-set-settings.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/04-document-types/01-document-types-intro.markdown",
    "content": "---\nheader-id: document-types\n---\n\n# Document Types\n\n[TOC levels=1-4]\n\nDocument types are made of metadata fields and help users define the purpose of\nDocument Library files. For example, a *Contract* document type may need \nmetadata fields for the effective date, expiration date, contract type, legal \nreviewer, and more. When users create Document Library files of the Contract \ndocument type, they can then populate those metadata fields. Document types also \nhelp you integrate files with other features like search and workflow. Search \nworks on file metadata so users can find files faster. You can also apply \nworkflows to specific document types. And you can more cleanly organize document \nlibraries by designating folders to hold particular document types exclusively. \n\n## Managing Document Types\n\nTo see the available document types, open the *Menu* \n(![Product Menu](../../../../images/icon-menu.png)), \nexpand your site's menu, and navigate to *Content & Data* &rarr; \n*Documents and Media*. Then click the *Document Types* tab. A searchable table \nlists any existing document types. The following actions are available for each \ndocument type via its Actions button \n(![Actions](../../../../images/icon-actions.png)): \n\n**Edit**: Edit the document type. \n\n**Permissions**: Set the document type's permissions. \n\n**Delete**: Delete the document type. Note that document types don't support the \nRecycle Bin. Once you delete a document type, it's gone forever. \n\n![Figure 1: The Document Types management window lets you view existing document types and create new ones.](../../../../images/dm-doc-types-list.png)\n\n## Creating Document Types\n\nFollow these steps to create a document type: \n\n1.  From the *Menu* (![Product Menu](../../../../images/icon-menu.png)), expand \n    your site's menu and navigate to *Content & Data* &rarr; \n    *Documents and Media*. Then click the *Document Types* tab. \n\n2.  Click the *Add* button \n    (![Add](../../../../images/icon-add.png)). The *New Document Type* form \n    appears. \n\n3.  Give your document type a name and a description. \n\n    ![Figure 2: Create your new document type.](../../../../images/dm-doc-types-new.png)\n\n4.  Define the metadata to use with your document type. You do this via these \n    sections in the form: \n\n    **Main Metadata Fields:** These are tied directly to the document type. They \n    can be created only via the form and can't be used with other document \n    types. You create and edit these metadata fields in the form the same way \n    that you do when creating \n    [metadata sets](/docs/7-2/user/-/knowledge_base/u/metadata-sets). \n\n    **Additional Metadata Fields:** Select a metadata set to associate with the \n    document type. Each document type must be associated with one or more \n    metadata set. To differentiate document types that use the same metadata \n    sets, define different main metadata fields. \n\n5.  Define your document type's permissions via the form's *Permissions*\n    section. By default, anyone can view the document type, including site\n    guests. You can restrict its view, update, delete, and permissions\n    configuration to site members or the document type's owner. \n\n6.  Click *Save* when you're finished specifying your new document type. \n\nYour document type is now available when adding a document via the Documents and \nMedia's *Add* menu. When users create new files of the document type, they're \npresented with metadata fields to describe the document. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/05-online-editing-google-docs/01-intro.markdown",
    "content": "---\nheader-id: online-file-creation-and-editing-with-google-docs\n---\n\n# Online File Creation and Editing with Google Docs™\n\n[TOC levels=1-4]\n\nAlthough you can \n[add](/docs/7-2/user/-/knowledge_base/u/adding-files-to-a-document-library#using-the-add-menu) \nand \n[edit](/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files) \nDocuments and Media files via upload and download, @product@ doesn't contain a \nUI that lets you author or edit documents directly. You can, however, create and\nedit Documents and Media files online in Google Docs&trade;, Google\nSheets&trade;, and Google Slides&trade;. \n\n| **Note:** For simplicity and readability, this documentation refers only to \n| Google Docs&trade;. The material, however, also applies to Google \n| Sheets&trade; and Google Slides&trade;. \n\nNote that when you use Google Docs&trade; to create or edit a Documents and \nMedia file, that file isn't permanently stored in Google Docs&trade;. Google \nDocs&trade; is only used for its editing UI. Your edits are then saved to the \nDocuments and Media Library. \n\n![Figure 1: You can create new Google documents in Documents and Media.](../../../../images/google-docs-new.png)\n\n![Figure 2: You can also use Google's document editor to edit existing Documents and Media files.](../../../../images/google-docs-edit.png)\n\n![Figure 3: When using Google's document editor, you can save or discard your changes via the editor's toolbar.](../../../../images/google-docs-save-discard.png)\n\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/05-online-editing-google-docs/02-configuring-gdocs-integration.markdown",
    "content": "---\nheader-id: configuring-google-docs-integration\n---\n\n# Configuring Google Docs™ Integration\n\n[TOC levels=1-4]\n\nBefore you can use Google Docs&trade; to create and edit Documents and Media \nfiles, you must configure @product@ to connect with an application in the \n[Google API Console](https://console.developers.google.com). \n\n| **Note:** You must be an administrator to complete these steps. \n\n## Configure Your Google Project\n\nFirst, you must configure your Google project to use the Google Drive&trade; \nAPI and set up OAuth 2 for use with that project. \n\n1.  Go to the\n    [Google API Console](https://console.developers.google.com). \n    If you don't have a suitable project, \n    [create a new one](https://support.google.com/googleapi/answer/6251787?hl=en&ref_topic=7014522). \n\n2.  Enable the Google Drive&trade; API for your project. For instructions, see \n    the Google API Console documentation on \n    [enabling and disabling APIs](https://support.google.com/googleapi/answer/6158841). \n\n3.  Create an OAuth 2 client ID for your Google project. For instructions, see \n    the Google API Console documentation on \n    [setting up OAuth 2.0](https://support.google.com/googleapi/answer/6158849). \n    Select *Web application* when prompted to select your application type. Take \n    note of the client ID and client secret that appear---you'll need them to \n    configure the portal to use the Google Drive&trade; API. \n\n## Configuring the Portal\n\nNow that you have a Google project set up for use with @product@, you must \nconnect your installation to that project. You can do this at two scopes: \n\n1.  Globally, for all instances in your @product@ installation.\n2.  At the instance scope, for one or more instances in your @product@ \n    installation. \n\nYou can override the global configuration for one or more instances by \nconfiguring those instances separately. Similarly, you can configure only the \ninstances you want to connect to your Google project and leave the global \nconfiguration empty. \n\nFollow these steps to configure your @product@ installation to connect to your \nGoogle project: \n\n1.  Note that the configuration options are the same in the global and \n    instance-level configurations. \n\n    To access the global configuration, go to *Control Panel* &rarr; \n    *Configuration* &rarr; *System Settings* &rarr; *Documents and Media*. \n\n    To access the instance-level configuration, go to *Control Panel* &rarr; \n    *Configuration* &rarr; *Instance Settings* &rarr; *Documents and Media*. \n\n2.  Under *VIRTUAL INSTANCE SCOPE*, select *Google Drive*. \n\n3.  Enter your Google project's OAuth 2 client ID and client secret into the \n    *Client ID* and *Client Secret* fields. \n\n4.  Click *Save*. \n\n| **Note:** To turn this feature off, delete the client ID and client secret \n| values from the form. \n\n| **Note:** You can ignore the *Picker API Key* field. This field is unrelated \n| to the Google Docs&trade; online editing features in @product@. \n\n![Figure 1: Enter your Google project's OAuth 2 client ID and client secret.](../../../../images/google-drive-system-settings.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/05-online-editing-google-docs/03-creating-editing-files-gdocs.markdown",
    "content": "---\nheader-id: creating-and-editing-files-with-google-docs\n---\n\n# Creating and Editing Files with Google Docs™\n\n[TOC levels=1-4]\n\nYou can use Google Docs&trade; to create and edit text documents, spreadsheets,\nor presentations for storage in the Docs and Media library. When you finish your\nGoogle Docs&trade; editing session, your changes are automatically saved in the\nDocuments and Media Library. You can \n\n-   [Create Files](#creating-files)\n-   [Edit Files](#editing-files)\n-   [Manage Multiple Editing Sessions](#multiple-editing-sessions)\n\n## Authentication\n\nThe first time you create or edit a Documents and Media file via Google\nDocs&trade;, you must authenticate with your Google account. This links Google\nDrive&trade; to your portal account, so you only need to do this once. You can\nunlink your account at any time by navigating to User Menu &rarr; *Account\nSettings* &rarr; *General* &rarr; *Apps*, and clicking *Revoke* next to Google\nDrive&trade;. \n\n![Figure 1: You can unlink your Google account from the portal.](../../../../images/google-docs-unlink.png)\n\n## Creating Files\n\nFollow these steps to create a new Documents and Media file via Google \nDocs&trade;:\n\n1.  Open the *Menu* \n    (![Product Menu](../../../../images/icon-menu.png)), \n    click your site's name, and navigate to *Content & Data* &rarr; \n    *Documents and Media*. \n\n2.  Click the *Add* icon \n    (![Add](../../../../images/icon-add.png)) \n    and select the type of Google document to add to the Document Library.\n\n    -   Google Docs&trade;\n    -   Google Slides&trade;\n    -   Google Sheets&trade;\n\n    When you select one of these options, @product@ creates a temporary \n    Documents and Media file and links it to a new Google file. Your browser \n    then redirects you to that Google file so you can create its content. \n\n    Note that some actions on the temporary Documents and Media file can affect \n    its linked Google file. For more information, see [multiple editing\n    sessions](#multiple-editing-sessions). \n\n    ![Figure 2: Select the type of Google document you want to create.](../../../../images/google-docs-new.png)\n\n3.  Use the Google Docs&trade; editor to create your document's content. All \n    Google Docs&trade; features are available except for sharing. \n\n4.  Save or discard your changes by clicking one of these toolbar buttons in the \n    Google Docs&trade; editor: \n\n    **Save and Return to Liferay:** Saves your document as a new file in the \n    Documents and Media Library, deletes the Google file, and returns you to \n    the portal. The saved file's format depends on the type of Google document \n    you selected in step two above: \n\n    -   Google Docs&trade;: Microsoft Word (`.docx`)\n    -   Google Slides&trade;: Microsoft PowerPoint (`.pptx`)\n    -   Google Sheets&trade;: Microsoft Excel (`.xlsx`)\n\n    **Discard Changes:** Returns you to the portal without saving your file in \n    the Documents and Media Library or Google Docs&trade;. \n\n    Note that it's also possible to close the Google Docs&trade; window without \n    clicking either button. In this case, the editing session remains open even \n    though the window that displayed it is closed. For more information, see the \n    section below on \n    [multiple editing sessions](#multiple-editing-sessions). \n\n    ![Figure 3: Save or discard your changes by using the toolbar in the editor.](../../../../images/google-docs-save-discard.png)\n\n## Editing Files\n\nYou can use Google Docs&trade; to edit the following types of Documents and \nMedia files: \n\n-   Text files (`.docx`, `.html`, `.txt`, `.rtf`, `.odt`)\n-   Presentation files (`.pptx`, `.odp`)\n-   Spreadsheet files (`.xlsx`, `.ods`, `.csv`, `.tsv`)\n-   PDF files\n\n| **Note:** Google Docs&trade; doesn't support older, non-XML-based Microsoft \n| Office file types (`.doc`, `.ppt`, `.xls`). \n\nFollow these steps to edit a Documents and Media file in Google Docs&trade;: \n\n1.  Navigate to the file in the Documents and Media Library. \n\n2.  Click the file's Actions icon \n    (![Actions](../../../../images/icon-actions.png)) \n    and select *Edit in Google Docs*. This automatically \n    [checks out](/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files) \n    the file, transfers its content to a new Google Docs&trade; file, and \n    redirects you to that Google Docs&trade; file. \n\n    ![Figure 4: Select *Edit in Google Docs* from the file's Actions menu.](../../../../images/google-docs-edit.png)\n\n3.  Edit the file in Google Docs&trade;. The editing process is exactly the same \n    as described above for creating files. \n\n## Multiple Editing Sessions\n\nWhen you create or edit a Documents and Media file in Google Docs&trade;, you \ncan save or discard your changes by clicking *Save and Return to Liferay* or \n*Discard Changes*, respectively. If you instead close the window without \nclicking either, the editing session still exists. You can access it via the \noriginal file in Documents and Media. If the file didn't exist before (e.g., you \nwere creating a new file), it appears in Documents and Media as a temporary \nfile. \n\nWhen an editing session already exists for a Documents and Media file, the \nfollowing actions are available via that file's Actions icon \n(![Actions](../../../../images/icon-actions.png)): \n\n**Edit in Google Docs:** Resume editing the file in Google Docs&trade;. \n\n**Check in:** Saves the Google file (including any changes) to the Documents and \nMedia file, then deletes the Google file. This is equivalent to clicking \n*Save and Return to Liferay* in a Google Docs&trade; editing window. \n\n**Cancel Checkout:** Deletes the Google file, discarding any changes. This is \nequivalent to clicking *Discard Changes* in a Google Docs&trade; editing window. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/06-online-editing-office-365/01-intro.markdown",
    "content": "---\nheader-id: integration-with-microsoft-office-365\n---\n\n# Integration with Microsoft Office 365™\n\n[TOC levels=1-4]\n\nAlthough you can \n[add](/docs/7-2/user/-/knowledge_base/u/adding-files-to-a-document-library#using-the-add-menu) \nand \n[edit](/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files) \nDocuments and Media files via upload and download, @product@ doesn't contain a \nUI that lets you author or edit documents directly. You can, however, create and \nedit Documents and Media files online in Word&trade;, Excel&trade;, and \nPowerPoint&trade;. \n\nNote that when you use Office 365&trade; to create or edit a Documents and Media \nfile, that file isn't permanently stored in Office 365&trade;. Office 365&trade; \nis only used for its editing UI. Your edits are then saved to the Documents and \nMedia Library. \n\nHere you can learn how to use it. \n\n![Figure 1: You can create new Office 365&trade; documents in Documents and Media.](../../../../images/office365-new.png)\n\n![Figure 2: You can also edit existing Documents and Media files in Office 365&trade;.](../../../../images/office365-edit.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/06-online-editing-office-365/02-configuring-office365-integration.markdown",
    "content": "---\nheader-id: configuring-office-365-integration\n---\n\n# Configuring Office 365™ Integration\n\n[TOC levels=1-4]\n\nBefore you can use Office 365&trade; to create and edit Documents and Media \nfiles, you must configure @product@ to connect with an application in the \n[Azure portal](https://portal.azure.com/). \n\n| **Note:** You must be an administrator to complete these steps. \n\n## Register an Application with the Microsoft Identity Platform\n\nFirst, configure your application with the Microsoft identity platform&trade;. \nTo do so, follow the steps described in \n[Microsoft's documentation](https://docs.microsoft.com/en-gb/graph/auth-register-app-v2). \n\nTo construct a URL for the *Redirect URI* parameter, follow this pattern: \n\n    https://[hostname]/o/document_library/onedrive/oauth2\n\nHere's the minimum permission set needed to use Office 365&trade; integration: \n\n-   Files.Read.All\n-   Files.ReadWrite.All\n\nFor more information about permissions, see \n[Microsoft's documentation](https://docs.microsoft.com/graph/permissions-reference). \n\n## Configuring @product@\n\nNow you must connect your @product@ installation with your Microsoft identity \nplatform&trade; application. You can do this at two scopes: \n\n1.  Globally, for all instances in your @product@ installation.\n2.  At the instance scope, for one or more instances in your @product@ \n    installation. \n\nYou can override the global configuration for one or more instances by \nconfiguring those instances separately. Similarly, you can configure only the \ninstances you want to connect to your application and leave the global \nconfiguration empty. \n\nFollow these steps to configure your @product@ installation to connect to your \napplication: \n\n1.  Note that the configuration options are the same in the global and \n    instance-level configurations. \n\n    To access the global configuration, go to *Control Panel* &rarr; \n    *Configuration* &rarr; *System Settings* &rarr; *Documents and Media*. \n\n    To access the instance-level configuration, go to *Control Panel* &rarr; \n    *Configuration* &rarr; *Instance Settings* &rarr; *Documents and Media*. \n\n2.  Under *VIRTUAL INSTANCE SCOPE*, select *OneDrive*. \n\n3.  Enter your application's OAuth 2 client ID and client secret in the \n    *Client ID* and *Client Secret* fields, respectively. \n\n4.  Enter your tenant ID in the *Tenant* field. To find your tenant ID, see \n    [Microsoft's documentation](https://docs.microsoft.com/onedrive/find-your-office-365-tenant-id). \n\n4.  Click *Save*. \n\n| **Note:** Once enabled, you can disable this feature by deleting the client \n| ID, client secret, and tenant values from the form. \n\n![Figure 1: Enter your application's client ID, client secret, and tenant.](../../../../images/onedrive-system-settings.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/06-online-editing-office-365/03-creating-editing-files-office365.markdown",
    "content": "---\nheader-id: creating-and-editing-documents-and-media-files-with-office-365\n---\n\n# Creating and Editing Documents and Media Files with Office 365™\n\n[TOC levels=1-4]\n\nYou can use Office 365&trade; to create and edit text documents, spreadsheets, \nor presentations for storage in the Documents and Media library. When you finish \nyour Office 365&trade; editing session, you must check in the document to save \nthe changes in the Documents and Media Library. \n\nHere, you'll learn how to do these things:\n\n-   [Authenticate with OneDrive&trade;](#authentication)\n-   [Create Files](#creating-files)\n-   [Edit Files](#editing-files)\n\n## Authentication\n\nThe first time you create or edit a Documents and Media file via Office \n365&trade;, you must authenticate with your Microsoft account. This links \nOneDrive&trade; to your @product@ account, so you only need to do this once. You \ncan unlink your account at any time by navigating to User Menu &rarr; \n*Account Settings* &rarr; *General* &rarr; *Apps*, and clicking *Revoke* next to \nOneDrive&trade;. \n\n![Figure 1: You can unlink your account from the portal.](../../../../images/office365-unlink.png)\n\n## Creating Files\n\nFollow these steps to create a new Documents and Media file via Office \n365&trade;: \n\n1.  Open the *Menu* \n    (![Product Menu](../../../../images/icon-menu.png)), \n    click your Site's name, and navigate to *Content & Data* &rarr; \n    *Documents and Media*. \n\n2.  Click the *Add* icon \n    (![Add](../../../../images/icon-add.png)) \n    and select the type of Office 365&trade; document to add to the Document \n    Library: \n\n    -   Word&trade;\n    -   Excel&trade;\n    -   PowerPoint&trade;\n\n    When you select one of these options, a new window opens for you to enter \n    the document's name. \n\n    ![Figure 2: Select the type of document you want to create.](../../../../images/office365-new.png)\n\n3.  Enter the document's name in the *Title* field, and click *Save*. When you \n    click *Save*, @product@ creates a temporary Documents and Media file and \n    links it to the new Office 365&trade; file. Your browser then opens a new \n    window with that Office 365&trade; file so you can create its content. \n\n    ![Figure 3: Give the document a name.](../../../../images/office365-creation-modal.png)\n\n4.  Use the Office 365&trade; editor to create your document's content. \n\n5.  Save or discard your changes to @product@: \n\n    **Check in:** Saves the Office 365&trade; file to Documents and Media, then \n    deletes the file from Office 365&trade;. The saved file's format depends on \n    the document type you selected in step two above. \n\n    -   Word: Microsoft Word&trade; (`.docx`)\n    -   PowerPoint: Microsoft PowerPoint&trade; (`.pptx`)\n    -   Excel: Microsoft Excel&trade; (`.xlsx`)\n\n    **Cancel Checkout:** Deletes the Office 365&trade; file, discarding any \n    changes. \n\n## Editing Files\n\nYou can use Office 365&trade; to edit the following types of Documents and \nMedia files: \n\n-   Text files (`.doc`, `.docx`, `.docm`, `.dot`, `.dotx`, `.dotm`, `.html`, \n    `.txt`, `.rtf`, `.odt`)\n-   Presentation files (`.ppt`, `.pptx`, `.pptm`, `.pps`, `.ppsx`, `.ppsm`, \n    `.pot`, `.potx`, `.potm`)\n-   Spreadsheet files (`.xls`, `.xlsx`, `.xlsm`, `.xlt`, `.xltx`, `.xltm`, \n    `.ods`, `.csv`, `.tsv`, `.txt`, `.tab`)\n\nFollow these steps to edit a Documents and Media file in Office 365&trade;: \n\n1.  Navigate to the file in the Documents and Media Library. \n\n2.  Click the file's Actions icon \n    (![Actions](../../../../images/icon-actions.png)) \n    and select *Edit in Office 365*. This automatically \n    [checks out](/docs/7-2/user/-/knowledge_base/u/checking-out-and-editing-files) \n    the file, transfers its content to a new Office 365&trade; file, and \n    redirects you to that file Office 365&trade;. \n\n3.  Edit the file in Office 365&trade;. The editing process is exactly the same \n    as described above for creating files. \n\n    ![Figure 4: Select *Edit in Office 365* from the file's Actions menu.](../../../../images/office365-edit.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/02-managing-documents-and-media/07-store-types/01-store-types-intro.markdown",
    "content": "---\nheader-id: store-types\n---\n\n# Store Types\n\n[TOC levels=1-4]\n\nYou can change the file system (the *store*) that the Documents and Media \nlibrary uses to store files. This is configured in the `portal-ext.properties` \nfile by setting the `dl.store.impl=`property. Configuring stores is covered in \n[the Document Repository Configuration guide](/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration). \nHere, you'll consider the ramifications of different stores: \n\n**Simple File System Store:** Uses the file system (local or a mounted share) to \nstore files. This is the default store. \n\n**Advanced File System Store:** Nests files into directories by version, for \nfaster performance and to store more files. \n\n**DBStore (Database Storage)**: Stores files in the @product@ database. The file \n(stored as a blob) size limit is 1 GB. Use the Simple File System Store or \nAdvanced File System Store to store larger files. \n\n**S3Store (Amazon Simple Storage)**: Uses Amazon's cloud-based storage solution. \n\nIf you must move your files from one store to another, use the migration utility \nin *Control Panel* &rarr; *Configuration* &rarr; *Server Administration* &rarr; \n*Data Migration*. \n\n## Simple File System Store\n\nThe Simple File System Store is the default store. It stores Documents and Media\nfiles on the server's file system (local or mounted). This store is heavily\nbound to @product@'s database. The store's default root folder is\n`[Liferay Home]/data/document_library`. You can change this via the\n`dl.store.file.system.root.dir=` property in a `portal-ext.properties` file, or\nin the Control Panel. For instructions on this, see the \n[Document Repository Configuration guide](/docs/7-2/deploy/-/knowledge_base/d/document-repository-configuration).\n\nThe Simple File System Store uses a local folder to store files. You can use the \nfile system for your clustered configuration, but you must make sure the folder \nyou point the store at can handle things like concurrent requests and file \nlocking. You must therefore use a Storage Area Network or a clustered file \nsystem. \n\nThe Simple File System Store creates a folder structure based on primary keys in \n@product@'s database. If, for example, you upload a presentation with the file \nname `workflow.odp` to a folder named `stuff`, the store creates a folder \nstructure like this: \n\n    /companyId/folderId/numericFileEntryName/versionNumber\n\n`companyId`: The site's company ID. \n\n`folderId`: The ID of the Documents and Media folder containing the document. \n\n`numericFileEntryName`: The document's numeric file entry name. \n\n`versionNumber`: The document's version number. \n\n![Figure 1: The Simple File System Store creates a folder structure based on primary keys in @product@'s database.](../../../../images/enterprise-file-system-store.png)\n\n| **Note:** Be careful not to confuse a document's numeric file entry name from \n| its document ID. Each has an independent counter. The numeric file entry name \n| is used in the folder path for storing the document, but the document ID is \n| not. The numeric file entry name can be found in the `name` column of the \n| `DLFileEntry` table in @product@'s database; the document ID can be found in \n| the `fileEntryId` column of the same table. \n\n## Using the Advanced File System Store\n\nThe Advanced File System Store, like the Simple File System Store, saves files \nto the local file system. It uses a slightly different folder structure, \nhowever, and can overcome operating system limitations on the number of files \nstored in a particular folder by programmatically creating a structure that can \nexpand to millions of files. It alphabetically nests the files in folders. This \nalso improves performance, as there are fewer files stored per folder. \n\n![Figure 2: The Advanced File System Store creates a more nested folder structure than the Simple File System Store.](../../../../images/enterprise-adv-file-system-store.png)\n\nThe same rules apply to the Advanced File System Store as apply to the Simple \nFile System Store. To cluster it, you must point the store to a network mounted \nfile system that all the nodes can access. That networked file system must also \nsupport concurrent requests and file locking. Otherwise, you may experience data \ncorruption issues if two users attempt to write to the same file at the same \ntime from two different nodes. \n\nSee the \n[Document Repository Configuration guide](/docs/7-2/deploy/-/knowledge_base/d/using-the-advanced-file-system-store) \nfor instructions on using the Advanced File System Store. \n\n## Using Amazon Simple Storage Service\n\nAmazon's Simple Storage Service (S3) is a cloud-based storage solution that you \ncan use with @product@. It lets you store your documents to the cloud seamlessly \nfrom all nodes. \n\nWhen you sign up for the service, Amazon assigns you unique keys that link you \nto your account. In Amazon's interface, you can create *buckets* of data \noptimized by region. Once you create these to your specifications, follow \n[these instructions](/docs/7-2/deploy/-/knowledge_base/d/using-amazon-simple-storage-service) \nto connect your repository to @product@. \n\nConsult Amazon's S3 documentation for more information. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/01-intro.markdown",
    "content": "---\nheader-id: liferay-sync\n---\n\n# Liferay Sync\n\n[TOC levels=1-4]\n\nLiferay Sync synchronizes files between your @product@ server and users' desktop \nand mobile environments. With Liferay Sync, users can publish and access shared \ndocuments and files from their native environments without using a browser. \nClients for Windows and Mac OS desktops and Android and iOS mobile platforms are \nsupported. As users add and collaborate on documents and files, Liferay Sync\nautomatically synchronizes them across all configured Sync clients and your\nserver. Liferay Sync integrates completely with @product@ so that features such\nas authentication and versioning function in the supported environments. The\nLiferay Sync desktop client stores files locally so they're always available,\neven when users are offline. It automatically synchronizes files upon client\nreconnection. The Liferay Sync mobile client saves storage space on users'\ndevices by downloading only the files they choose. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/02-administering-sync/01-intro.markdown",
    "content": "---\nheader-id: administering-liferay-sync\n---\n\n# Administering Liferay Sync\n\n[TOC levels=1-4]\n\nBefore your users can use Liferay Sync with their Sites, you must install and \nconfigure it on your @product@ server. The articles here walk you through this \nand also discuss important topics like preventing accidental file deletion and \nensuring Sync security. As such, make sure you thoroughly read each article \nbefore letting your users connect to Sync. \n\n| **Note:** To install and configure Liferay Sync on your @product@ server, you \n| must be an administrator. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/02-administering-sync/02-prerequisites.markdown",
    "content": "---\nheader-id: installing-liferay-syncs-prerequisites\n---\n\n# Installing Liferay Sync's Prerequisites\n\n[TOC levels=1-4]\n\nLiferay Sync requires the *Liferay CE Sync Connector* app from \n[Liferay Marketplace](https://web.liferay.com/marketplace). This app enables and\nconfigures Sync in your @product@ instance. For example, you can disable Sync\nacross the instance or on a site-by-site basis. Note that Sync is enabled by\ndefault for all Sites. \n\nFor instructions on installing Marketplace apps, see \n[the Liferay Marketplace documentation](/docs/7-2/user/-/knowledge_base/u/using-the-liferay-marketplace). \n\n| **Note:** The Liferay Sync Security module that Sync requires is included and \n| enabled by default in @product@. You can verify this by ensuring that the \n| `SYNC_DEFAULT` and `SYNC_TOKEN` entries are enabled in *Control Panel* &rarr; \n| *Configuration* &rarr; *Service Access Policy*. \n\nIf you want to use Sync Connector's default settings and are fine with Sync \nbeing enabled for all your Sites, you can skip the articles that follow on\nconfiguring Sync. However, before directing your users to install and configure\nthe Sync desktop and mobile clients, **make sure to read** this guide's articles\non preventing accidental file deletion and ensuring Sync security. You should\nalso **warn your users** about the potential for accidental data loss. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/02-administering-sync/03-configuring-sync.markdown",
    "content": "---\nheader-id: configuring-liferay-sync\n---\n\n# Configuring Liferay Sync\n\n[TOC levels=1-4]\n\nSync Connector lets you manage how, or if, clients connect to your @product@\nserver. You can also configure default file permissions on a per-Site basis, and\nmanage the devices that connect to your @product@ instance. To access Sync\nConnector, select *Control Panel* &rarr; *Configuration* &rarr; *Sync Connector\nAdmin*. \n\nSync Connector Admin has three tabs: \n\n1.  **Settings:** Control Sync's general behavior. These settings apply globally \n    to Sync. \n\n    ![Figure 1: The Control Panel's Configuration section contains Sync Connector Admin.](../../../../images/sync-admin-01.png)\n\n    **Allow the use of Sync?:** Whether Sync is enabled.\n\n    **Allow users to sync their personal Sites?:** Whether users can sync data \n    with their personal Sites. \n\n    **Allow LAN Syncing?:** Whether desktop clients attempt to download \n    updates from other desktop clients on the same local network before \n    downloading from the server. This can help reduce server load and increase \n    data transfer speeds. Note that LAN syncing only works with clients that \n    also enable it. \n\n    **Max Connections:** The maximum number of simultaneous connections each \n    client is allowed per account. For example, if Max Connections is three, a \n    client can simultaneously upload or download up to three files for each \n    account. Note, this setting operates on a per client basis. If Max \n    Connections is set to three and a user has two clients connected to an \n    account (which is possible if Sync is installed on two different \n    machines), then the user is effectively allowed six simultaneous \n    connections. While increasing Max Connections can speed up file transfers \n    it also places a heavier load on the server. *Max Connections* is set to \n    one by default. \n\n    **Poll Interval:** The frequency in seconds that clients automatically \n    check the @product@ instance for updates. For example, if set to ten, \n    connected clients check the instance for updates every ten seconds. The \n    default Poll Interval is five. \n\n    **Max Download Rate:** The maximum transfer rate, in bytes, at which \n    clients can download. A value of 0 specifies no limit. This setting takes \n    precedence over clients' download rate setting. \n\n    **Max Upload Rate:** The maximum transfer rate, in bytes, at which clients \n    can upload. A value of 0 specifies no limit. This setting takes precedence \n    over clients' upload rate setting. \n\n    **Force Security Mode:** Whether to force security mode on mobile clients. \n    Security mode encrypts Sync files on the device and requires a passcode \n    when accessing the Sync mobile app. \n\n2.  **Sites:** Control Sync on a per-Site basis. \n\n    ![Figure 2: Sync Connector Admin's Sites tab lets you manage Sync on a per-Site basis.](../../../../images/sync-admin-02.png)\n \n    For each Site in the @product@ instance, the Sites tab lists each Site's \n    default file permissions (more on this in a moment) and whether Sync is \n    enabled for that Site. Sync is enabled by default for all Sites. To disable \n    Sync for a Site, click the Site's *Actions* button \n    (![Actions](../../../../images/icon-actions.png)) and select \n    *Disable Sync Site*. **Please use caution** when disabling Sync for a Site, \n    as doing so **deletes** files for that Site from the Sync clients. Disabling\n    Sync for a Site, however, doesn't affect the Site's files on the server. \n\n    | **Warning:** Disabling Sync for specific Sites from Sync Connector Admin \n    | can result in data loss across clients. If Sync is disabled for a Site \n    | users are currently syncing, any files in the clients' sync folders for \n    | that Site are automatically deleted from their clients. If a user is \n    | offline when Sync is disabled for a Site, any offline changes or additions \n    | they make are deleted upon client reconnection. \n\n    You can enable Sync for a Site by selecting *Enable Sync Site* from its \n    Actions button. Make sure that each Site for which Sync is enabled has a \n    Documents and Media app on at least one of its pages. If a Site doesn't have \n    the app on any of its pages and users click the *Open Website* link from \n    their Sync menus, the error message *The requested resource was not found* \n    appears. \n\n    The Sites tab also sets default file permissions for files uploaded from\n    Sync clients. The process for setting permissions is nearly the same as for\n    enabling or disabling Sync for Sites. To set the default file permissions\n    for a single Site, click its Actions button and select *Default File\n    Permissions*. This lets you select the default file permissions for that\n    Site. Click *Choose* for the permissions you want to use. \n\n    ![Figure 3: Click *Choose* to select the default file permissions for a Site in Sync.](../../../../images/sync-admin-03.png)\n\n    To set the default file permissions for several Sites, select the checkboxes \n    for the Sites, click the *Default File Permissions* link that appears above \n    the table, and select the permissions you want to use. Default file \n    permissions might behave differently than you'd expect. They control *only* \n    the permissions for new files uploaded through the Sync clients; they don't \n    affect permissions for uploading or restrict document owners (the user who \n    originally uploaded a document) in any way. For example, even if you set a \n    Site's default file permissions to View Only, that Site's users can still \n    upload new documents to the Site. The file's owner has edit permission; the \n    rest of the Site's users have the View Only permission. \n\n3.  **Devices:** View and manage the devices registered with Sync. \n\n    ![Figure 4: Sync Connector Admin's Devices tab lists all the devices Sync has registered.](../../../../images/sync-admin-devices.png)\n\n    Each row in the Devices tab's table represents a device. The *Name* column \n    lists the user that registered the device. The remaining columns list each \n    device's location, client type, client build number, last connection date, \n    and status. Each device's Actions button \n    (![Actions](../../../../images/icon-actions.png)) manages that \n    device. You can change a device's status from Active to Inactive by \n    selecting *Actions* &rarr; *Disable Sync Device*. Inactive devices can't \n    sync. Inactive mobile devices also can't access local Sync files. Once\n    a device is Inactive, you can erase Sync files from it by selecting\n    *Actions* &rarr; *Wipe Sync Device*. This also signs the device out and\n    removes the account from the client. If the device is offline, this happens\n    when it tries to reconnect. The Actions menu also enables or deletes an\n    Inactive device. Deleting a device only removes it from the list of\n    registered devices; it can still reconnect and reregister. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/02-administering-sync/04-file-deletion.markdown",
    "content": "---\nheader-id: preventing-accidental-file-deletion-in-liferay-sync\n---\n\n# Preventing Accidental File Deletion in Liferay Sync\n\n[TOC levels=1-4]\n\nLiferay Sync's power rests in its ability to propagate between @product@ \nand connected Sync clients. When a user deletes a file from a connected \nclient, Sync also deletes the file in the instance and in any other connected \nclients. Likewise, if a user deletes a file in the instance, Sync also deletes \nthe file in all connected clients. In other words, anywhere a user deletes\na file, Sync deletes it *everywhere*. But don't worry: @product@'s Recycle Bin\nis enabled by default and lets you recover deleted files. You can access the\nRecycle Bin from each Site's *Site Administration* menu. \n\n| **Warning:** Liferay Sync automatically propagates file and folder deletion\n| through the @product@ server and in all connected clients. If an instance or\n| Site administrator disables the Recycle Bin, deleted files can't be recovered. \n\n@product@ instance and Site administrators can, of course, disable the Recycle \nBin. Disabling the Recycle Bin in a Site, however, leaves the Site vulnerable to\naccidental file deletions that propagate through Sync. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/02-administering-sync/05-sync-security.markdown",
    "content": "---\nheader-id: ensuring-liferay-sync-security\n---\n\n# Ensuring Liferay Sync Security\n\n[TOC levels=1-4]\n\nAs an administrator, you have a stake in the security of all connections to and\nfrom your servers. As long as @product@ is configured to use HTTPS, Sync clients\nuse user-supplied credentials to communicate securely. Users can only access the\ndocuments and Sites they're permitted to access. To support Security Mode in the\nSync mobile client and securely transmit files, your @product@ server must also\nuse SSL. The next section demonstrates how Sync's permissions work with your\n@product@ instance's permissions. \n\n## Liferay Sync Permissions Demonstration\n\nSync uses @product@'s default permissions to determine files and folders to sync\nwith the user's devices. It can only sync files a user can access. After\ninstalling the desktop Sync client, follow the steps below to test this\nfunctionality. \n\nFirst, enter `classified information` into a new text file and save it on your\ndesktop as `secret.txt`. Then use your browser to sign into @product@\nand create a new user with the user name *secretagent* and the email address\n*secretagent\\@liferay.com*. Give this user a password and then create a new\nprivate Site called *Secret Site*. Create a page on the Site and add the\nDocuments and Media app to it. Then add the secretagent user to the Secret Site\nand grant the *Site Administrator* Role to the user. Log in as secretagent and\nnavigate to the Secret Site. Then upload the `secret.txt` document to the\nDocuments and Media app. Make sure you also have a user that isn't a member of\nthe Secret Site and therefore doesn't have access to any of its documents\nthrough Sync. If you don't have such a user, create one now. \n\nNext, configure your Liferay Sync client to sign in with the secretagent user's\ncredentials and sync with the Secret Site. Open the Liferay Sync menu from the\nsystem tray and select *Preferences*. In the *Accounts* tab, click the plus icon\nat the window's bottom left to add an account. Provide the secretagent user's\ncredentials and uncheck all Sites except the Secret Site. Now confirm that Sync\ndownloaded the `secret.txt` file to your new Sync folder. Open it and check that\nit contains the text `classified information`. Next, use Sync to connect to your\n@product@ instance with the user that doesn't belong to the Secret Site. The\nfile doesn't sync because this user isn't a Site member. \n\nNow go to Sync Connector Admin and set the Secret Site's default file\npermissions to View Only. Create a new user, add it to the Secret Site, and add\nits account in your Liferay Sync client. As with the secretagent user, Sync\ndownloads the `secret.txt` file to this user's local Sync folder because the\nuser is a member of the Secret Site. Now edit and save this file. Even though\nyou can edit and save it locally, the edits aren't synced because the Site's\ndefault file permissions are View Only. After attempting the sync, a red *x*\nappears next to the file in the local Sync folder. Right click the file to see\nthe error. It confirms the user doesn't have the required permissions. \n\n![Figure 1: The upload error occurs because the user only has permission to view files.](../../../../images/sync-file-permissions-error.png)\n\nTo confirm that the error didn't propagate through Sync, open the file in the \nsecretagent user's local Sync folder. It still contains the original text. \nLikewise, the original file remains in the Site's Documents and Media portlet. \nTo get rid of the error in the other user's local Sync folder, return there and \nthen right click the file and select *Download From Server*. This replaces the \nfile with the latest file in the @product@ instance. \n\nNow edit `secret.txt` in the secretagent user's local Sync folder. When you\ncheck the file in the other user's local Sync folder and in the @product@ \ninstance, notice that Sync propagated the edits. The changes were propagated \nbecause the secretagent user owns the file in the instance. Owners can do \nanything with their files, even when the Site's default file permissions are set \nto View Only. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/03-sync-desktop/01-intro.markdown",
    "content": "---\nheader-id: using-liferay-sync-on-your-desktop\n---\n\n# Using Liferay Sync on Your Desktop\n\n[TOC levels=1-4]\n\nLiferay Sync synchronizes files between your @product@ Sites and desktop \ndevices. It lets you work with your files without using a browser. The Sync \nclients also ensure that the files are updated with the latest changes made by \nother users. To use Liferay Sync in your desktop environment, you must install \nthe Sync desktop client. It's currently available for Windows and Mac OS. The \nSync client stores files locally so that they're always available, even when \nyou're offline. Files are automatically synchronized upon your client's\nreconnection to your @product@ server. \n\nOn your desktop devices, Liferay Sync creates a new folder structure that it\nuses to synchronize files. You can treat the files the same as you do any\nothers. Credentials, Sync folder location, and other options are configured in\nthe client. Also, native desktop notification events inform you of what Sync is\ndoing. The native menu and task bar integration keep Sync controls within easy\nreach. \n\nThis guide walks you through setting up and using the Liferay Sync client on\nyour desktop. Before proceeding, check your @product@ instance or Site\nadministrator to ensure that Sync is enabled for your Sites. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/03-sync-desktop/02-install-configure-sync-desktop.markdown",
    "content": "---\nheader-id: installing-and-configuring-the-desktop-liferay-sync-client\n---\n\n# Installing and Configuring the Desktop Liferay Sync Client\n\n[TOC levels=1-4]\n\nYou can download the desktop client from the\n[Liferay customer downloads page](https://customer.liferay.com/downloads). \nNote that you'll need a Liferay account for this. Once you've downloaded the \nappropriate desktop client for your operating system, installing Liferay Sync on \nWindows or Mac OS is straightforward. \n\n## Installing the Liferay Sync Desktop Client\n\nTo install the Liferay Sync client on Windows, you must have administrator\nprivileges. Upon launching the Windows application installer, you're prompted\nto choose an install location. Select an appropriate location and click\n*Install*. Sync automatically starts after the installation finishes. The first\ntime Sync runs, you must configure it to connect and sync with @product@. The\nconfiguration steps are shown below. \n\n| **Note:** You can upgrade previous versions of the desktop Liferay Sync client\n| to version 3.0. When doing so, however, you must set up your account again in\n| the new version of the client. Prior to upgrading, it's typically best to shut\n| down Liferay Sync, backup files from your local Sync folder, and delete that\n| folder. \n\nThe Liferay Sync client for Mac is packaged in a DMG file. Double-clicking on\nthe DMG file mounts it as a disk image and opens a window showing the image's\ncontents. To install Sync, drag the Liferay Sync icon to your Applications\nfolder. Once it's installed, run it from the Applications folder. If you're\nrunning Mac OS X 10.9 or lower, you're prompted for your machine's administrator\ncredentials to install the Finder icon/context menu tool. This prompt only\nappears when installing or upgrading the tool. \n\n![Figure 1: Drag the Liferay Sync icon to the Applications folder.](../../../../images/sync-mac-install.png)\n\nNext, you'll configure the Sync client. \n\n## Configuring the Liferay Sync Desktop Client\n\nNow that you've installed Sync, you're ready to configure it! The configuration \nsteps for Sync on Windows and Mac are identical. \n\n1.  Open Sync and enter your server's address along with your account\n    credentials. Click *Sign In* when you're finished. \n\n    ![Figure 2: The first time you run Liferay Sync, you must tell it how to communicate with your @product@ server.](../../../../images/sync-setup-01.png)\n\n    When connecting to a server via HTTPS, an error appears if the certificate \n    can't be verified. Choosing *Proceed Anyway* bypasses verification and \n    leaves the connection open to compromise. Liferay Sync attempts to read the \n    certificates specified in the Java Control Panel \n    ([see section 20.4.5](https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/jcp.html#A1152831)). \n    If Java isn't installed, you can also put your certificates in \n    `[user.home]/.liferay-sync-3/certificates`. Liferay Sync trusts all \n    certificates in this folder. \n\n    ![Figure 3: When connecting over HTTPS, Liferay Sync produces an error if it can't verify the security certificate. Choosing *Proceed Anyway* bypasses verification and leaves the connection open to compromise.](../../../../images/sync-certificate-error.png)\n\n2.  Select the Sites you want to sync with. You can search for a Site in the \n    *Search* bar above the Site list. If you want to sync all the subfolders of \n    your selected Sites, click *Proceed* and move on to the next step. \n\n    ![Figure 4: Select the Sites you want to sync with. Clicking a Site's gear icon opens another window where you can choose to sync with only specific subfolders in that Site.](../../../../images/sync-setup-02.png) \n\n    To sync only specific folders in a Site, first click the Site's gear icon. \n    In the window that appears, all folders are selected by default. Unselect \n    the folders you don't want to sync with. Unselecting a subfolder causes the \n    parent folder's checkbox to show a minus sign, indicating that you haven't \n    selected all of the parent folder's subfolders. To sync only the documents \n    at the top of a folder's hierarchy (no subfolders), unselect all of that \n    folder's subfolders. You can also do this by clicking the folder's checkbox \n    until the minus sign appears. Click *Select* when you're finished with your \n    selections, and then click *Proceed* to move on to the next step. \n\n    ![Figure 5: Choose the Site's subfolders that you want to sync with. The checkbox with the minus sign indicates that not all of the *registration* folder's subfolders are selected.](../../../../images/sync-select-folders.png)\n\n3.  Specify the local folder to sync. This folder is used exclusively for\n    Sync: Sync creates it and it must not conflict with any existing local\n    folder. The Sync folder's default name is the instance's host name, and its\n    default location is the user's documents folder. For example, since the\n    instance in the following screenshots runs locally at the address\n    `http://localhost:8080/`, Sync creates a Sync folder named *localhost* in\n    the user's documents folder. You can, of course, specify any unique name and\n    location for the Sync folder. Click *Start Syncing* to begin syncing files. \n\n    | **Note:** Syncing to network drives is not supported because Liferay Sync \n    | can't reliably detect local file changes on such drives. \n\n    ![Figure 6: Specify your local Sync folder's name and location.](../../../../images/sync-setup-03.png)\n\n4.  Celebrate! You've successfully set up Liferay Sync! Sync congratulates you \n    on setting it up and begins to sync files from the Sites you selected to \n    your local Sync folder. Note, completing the initial synchronization may \n    take a significant amount of time, depending on the amount of data being \n    transferred. You can safely close the window as syncing continues in the \n    background. To view the local Sync folder, click *Open Folder*. To open \n    Sync's preferences, click the small gray text *advanced setup* near the \n    top-right. \n\n    ![Figure 7: Congratulations, you've successfully set up Liferay Sync!](../../../../images/sync-setup-04.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/03-sync-desktop/03-using-sync-desktop.markdown",
    "content": "---\nheader-id: using-the-liferay-sync-desktop-client\n---\n\n# Using the Liferay Sync Desktop Client\n\n[TOC levels=1-4]\n\nWhen Liferay Sync is running, its icon appears in your task bar (Windows) or menu \nbar (Mac). Clicking this icon opens a menu that lets you work with and manage \nLiferay Sync. \n\n![Figure 1: The Liferay Sync menu in the Windows task bar and Mac menu bar gives you quick access to Sync.](../../../../images/sync-toolbar-01.png)\n\nThe top of this menu shows your Sync status. If all your selected Sites are \nsynced, then your status is *Synced*. \n\nBelow your Sync status, the menu lists three shortcuts for accessing your \nfiles: \n\n**Open Sync Folder:** Select a Site to open its local Sync folder. \n\n**View Website:** Select a Site to view the page in @product@ that contains \nits Documents and Media app. \n\n**Recent Files:** Lists recently created and modified files in the \nrepositories you can access. \n\nNote that if you sync with two or more @product@ instances, Sync shows each at \nthe top of the menu instead of your Sync status. Mouse over each instance to \nreveal a submenu with that instance's Sync status and file shortcuts. \n\n![Figure 2: When you sync with more than one @product@ instance, Sync shows submenus for each.](../../../../images/sync-toolbar-02.png)\n\nFinally, regardless of how many @product@ instances you sync with, the menu lists \nthe following three options: \n\n**Preferences:** Open Sync's preferences. \n\n**Help:** Open Sync's documentation. \n\n**Quit:** Shut down Sync on your machine. \n\nNext, you'll learn how to use Sync's preferences to control how Sync functions \non your machine. \n\n## Using Sync Preferences\n\nYou can use Sync's preferences to add/remove @product@ instances to sync with, \nedit connection settings, and control Sync's basic behavior. Open Sync's \npreferences by clicking the Sync icon in the task bar (Windows) or menu bar \n(Mac OS) and selecting *Preferences*. A preference screen for your instance \naccounts displays. This is the *Accounts* tab in *Preferences*. \n\n![Figure 3: The Preferences menu's *Accounts* tab lets you manage syncing with Sites per account.](../../../../images/sync-preferences-accounts-01.png)\n\nThe *Accounts* tab contains the following options:\n\n**Accounts:** The accounts you sync with. When you select an account, the Sites\nyou have permission to sync with are shown on the right under *Syncing Sites*.\nYou can use the plus, minus, and pencil icons at the bottom of the account list\nto add, delete, or edit an account, respectively. You should use caution when\ndeleting an account from your Sync client, as doing so also deletes any local\nfiles and folders for that account. Adding an account takes you through the\nsame set of steps you used to set up the Sync client. [Click\nhere](/docs/7-2/user/-/knowledge_base/u/installing-and-configuring-the-desktop-liferay-sync-client#configuring-the-liferay-sync-desktop-client) \nfor instructions on this. \n\n**Syncing Sites:** The Sites you have permission to sync with for the selected \naccount. The Sites you currently sync with are shown under *Selected Sites*. \nOther Sites available for syncing are shown under *Unselected Sites*. To \nchange the Sites you sync with, click the *Manage Sites* button. The window \nthat appears lets you select and/or unselect Sites to sync with. This window \nis identical to the one that appeared when you first configured the client. \n[Click here](/docs/7-2/user/-/knowledge_base/u/installing-and-configuring-the-desktop-liferay-sync-client#configuring-the-liferay-sync-desktop-client) \nand see step two for instructions on using it. Use caution when de-selecting\nSites. De-selecting a Site deletes its folder on your machine. \n\n**Location:** The selected account's local Sync folder location. Click the \n*Change* button to change this folder's location. \n\nThe Preferences menu's *General* tab contains settings for the Sync client's \ngeneral behavior. It lists the following options: \n\n**Launch Liferay Sync on startup:** Starts Sync automatically each time your \nmachine starts. \n\n**Show desktop notifications:** Shows a small notification in the corner of \nyour screen when a synced file changes. \n\n**Automatically check for updates:** Automatically check for new client \nversions. You can click the *Check Now* button to check for updates manually. \n\n![Figure 4: The Preferences menu's *General* tab contains settings for Sync's general behavior.](../../../../images/sync-preferences-general-01.png)\n\nFinally, the Preferences menu's *Network* tab controls how Sync transfers data\nwith your @product@ servers. It contains the following options: \n\n**Download Rate:** To limit the rate at which Sync downloads data, select \n*Limit to* and then specify the rate. \n\n**Upload Rate:** To limit the rate at which Sync uploads data, select \n*Limit to* and then specify the rate. \n\n**Enable LAN Syncing:** Whether to download updates from other desktop clients \non the same local network before downloading from the server. This can help \nreduce server load and increase data transfer speeds. Note that LAN syncing \nonly works when enabled in the @product@ instance by the administrator, and in \nother clients. \n\n![Figure 5: The Preferences menu's *Network* tab contains settings for Sync's data transfer behavior.](../../../../images/sync-desktop-prefs-network.png)\n\nNote that your @product@ administrator can also limit the download/upload rate.\nIn this case, @product@'s settings take precedent. For example, if you set\na 5.0 MB/s download rate in the client but @product@'s download limit is 2.0\nMB/s, the latter takes precedence. Also, the client's rate applies across all\nits accounts. For example, if the client connects to three accounts and its\ndownload rate is 5.0 MB/s, then the sum of the download rate for all three\naccounts never exceeds 5.0 MB/s. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/03-sync-desktop/04-sync-folder.markdown",
    "content": "---\nheader-id: using-your-local-liferay-sync-folder\n---\n\n# Using Your Local Liferay Sync Folder\n\n[TOC levels=1-4]\n\nOnce you configure and run Sync, Sync automatically uploads to @product@ any\nfiles you add or modify in your Sync folder. Sync also downloads to your Sync\nfolder any file changes by other users. If you delete a file in your Sync\nfolder, Sync also deletes it from the server and other clients. You should\ntherefore use **extreme caution** when deleting files in your Sync folder. If,\nhowever, you accidentally delete a file, all is not lost! The file can still be\nrecovered from the instance's Recycle Bin, which is enabled by default. Note, if\nthe administrator has disabled the Recycle Bin, recovering deleted files is\nimpossible. \n\n| **Warning:** Deleting a file in your Sync folder also deletes it in the \n| @product@ instance and in other clients. If you accidentally delete a file, it \n| can be recovered from the instance's Recycle Bin. The Recycle Bin is enabled \n| by default. File recovery is, however, impossible if the instance or Site \n| administrator has disabled the Recycle Bin. \n\nYou can run through the following exercise to familiarize yourself with how to\ncreate, edit, download, and upload files with Sync. First, open the Sync folder\nin your file manager and create a new file called `README.txt`. Enter the word\n`test` in this file. Next, make sure you can access this file in your Site. Go\nto the Site you want to sync with and navigate to its Documents and Media app.\nIt lists your `README.txt` file.\n\nDownload the `README.txt` file to a convenient location on your machine. Open \nthe file and check that it still says `test`. Now open the `README.txt` file in \nyour Sync folder and edit it so that it says `second test`. Once the changes are \nsynced, go back to your browser and refresh the page with your Documents and \nMedia app. Click on the `README.txt` file's name, look at the file information \ndisplayed, and check that the file's version number has been incremented. \n\n![Figure 1: Updating a file through Liferay Sync increments the file's version number. You can view a file's version number through the web interface.](../../../../images/sync-file-edit-01.png)\n\nIf you download and open `README.txt` again, it now says `second test`. Your\nedit was uploaded to the Site! You can be confident that this edit was also\ndownloaded by all other Sync clients connected to your Site. \n\nNow delete the `README.txt` file from your local Sync folder. When the changes\nfinish syncing, go back to your browser and refresh the page containing your\nDocuments and Media app. The file is gone! The file is also deleted from the\nlocal Sync folders of all other Sync clients connected to the Site. Remember\nthis very important rule: deleting files in your local Sync folder deletes them\n*everywhere*! Next, you'll learn how to use the Sync client for your mobile\ndevice. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/04-sync-mobile/01-intro.markdown",
    "content": "---\nheader-id: using-liferay-sync-on-your-mobile-device\n---\n\n# Using Liferay Sync on Your Mobile Device\n\n[TOC levels=1-4]\n\nLiferay Sync for Android and iOS contains most of the\n[desktop Sync client](/docs/7-2/user/-/knowledge_base/u/using-liferay-sync-on-your-desktop)'s\nfunctionality. The mobile client can, however, only be connected to one \n@product@ account at a time. Also, mobile Sync doesn't automatically download\nfiles to your device. To save storage space on your device, the Sync mobile app\nlets you choose the files you want to work with. As with the Sync desktop\nclients, the latest versions of Sync on Android and iOS provide a consistent\nuser experience across platforms. While this article details using Sync on\nAndroid, the instructions also apply to Sync on iOS. \n\nYou need to download and install Sync on your Android or iOS device through its \nrespective app store, the same as you do any other mobile app. To find the app, \nsearch Google Play or the App Store for *Liferay*. You can also download Sync \nfrom the \n[Liferay customer downloads page](https://customer.liferay.com/downloads). \nOnce you've installed the Sync app on your device, the rest of the articles in \nthis guide show you how to use it. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/04-sync-mobile/02-connecting-sync-mobile.markdown",
    "content": "---\nheader-id: connecting-liferay-sync-mobile\n---\n\n# Connecting Liferay Sync Mobile\n\n[TOC levels=1-4]\n\nWhen Liferay Sync first starts on your mobile device, press the *Get Started*\nbutton to begin setup. The setup screen asks for your login credentials and your\nserver's address. Once you enter them, press *Sign In*. After signing in, you\nsee a panel that shows your name, a gear icon for accessing the app's settings,\nand navigation options *My Sites* and *My Documents*. My Sites and My Documents\nencompass the Sites in @product@ that you can sync with. My Documents is your\npersonal user Site, while My Sites shows the other Sites with which you can\nsync. No matter how deep you are in the folder hierarchy of a Site, swiping to\nthe right returns you to this panel. If you're in the first level of My Sites\nor My Documents, pressing the location bar at the top slides the screen\nslightly to the right to reveal a compact view of the panel. The following\nscreenshots show both views of the panel. \n\n![Figure 1: This panel lets you access the app's settings, as well as your Sites and documents.](../../../../images/sync-mobile-panel.png)\n\n![Figure 2: Tapping the title bar at the top of My Sites or My Documents opens the main Sync panel's compact view.](../../../../images/sync-mobile-panel-compact.png)\n\nPress the gear icon to access Sync's settings. Settings shows your account\ninformation and an option to sign out of your @product@ instance. Settings also \nlets you toggle *Security Mode*. Security Mode protects files stored on your \ndevice by encrypting them. Using Security Mode requires you to set up a passcode \nto use when accessing the Sync app. Security Mode protects the files on your \ndevice and @product@ instance in the event your device is lost or stolen. You \nshould note, however, downloading and opening files in Security Mode takes \nslightly longer than usual because the @product@ server must use SSL---if it \ndidn't, your files would be transmitting in the open. Below the Security Mode \ntoggle are the app's version and a link to send app feedback to Liferay. \n\n![Figure 3: The Settings screen for the Sync app lets you sign out of your @product@ instance, enable Security Mode, view the app's version, and send feedback.](../../../../images/sync-mobile-settings.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/03-sync/04-sync-mobile/03-sync-mobile-files.markdown",
    "content": "---\nheader-id: managing-files-and-folders-in-liferay-sync-mobile\n---\n\n# Managing Files and Folders in Liferay Sync Mobile\n\n[TOC levels=1-4]\n\nWhether you're working in My Documents or My Sites, you manage files and folders\nthe same way. Pressing a Site or folder shows you a list of its files and\nfolders. It displays each file's size and modification date. You can refresh the\nlist by pulling down from the top of the screen. Your current location in the\nnavigation hierarchy also appears at the top of the screen alongside a plus\nicon. Pressing the plus icon launches an upload screen for adding content in the\ncurrent location. You can add a new folder, upload a file, or launch your\ndevice's camera app to take and upload a picture or video. Pressing the *X* icon\non the upload screen's top right corner cancels any action and returns you to\nthe current file list. \n\n![Figure 1: Sync shows files and folders in a list.](../../../../images/sync-mobile-site.png)\n\nTo download a file to your device, press the file's name in the list. The label\nthat previously showed the file's size and modification date is replaced by a\ndownload progress indicator. When the file finishes downloading, your device\nautomatically opens it in the app you've configured to open files of that type.\nIf you haven't configured your device to use a specific app for that file type,\nyou're presented with a list of apps on your device that can open the file. If\nyour device doesn't have an app that can open the file, Sync tells you to\ninstall one that can. Downloaded files appear in the list with the file size in\nblue instead of gray. For example, the screenshot below shows that\n`LiferayinAction.pdf` is on the device. \n\n![Figure 2: Downloaded files appear in the list with their size in blue.](../../../../images/sync-mobile-file-downloaded.png)\n\nThe Sync mobile app also lets you move, rename, and delete files and folders. To \nthe right of each file and folder in the list is a circle icon with three dots. \nPressing this icon slides open a context menu on the right that lets you move, \nrename, or delete that item. The screenshots below show these options. Note that \nyou should use **extreme caution** when deleting files or folders. Deleting \nfiles or folders in the mobile Sync app also deletes them from @product@ and\nacross any synced clients. Accidentally deleted files can be restored from the\nRecycle Bin, which is enabled by default. If the instance or Site administrator\ndisables the Recycle Bin, however, recovering deleted files is impossible. \n\nWhat if you want to delete a file on your device without also deleting it in the\ninstance? Currently, you can only do this by signing out of your account in the\napp's Settings menu. Doing so removes all downloaded files from your device, but\npreserves them in the instance. If you're on Android, it may be possible to use\na system file browser app to remove downloaded files manually. \n\n| **Warning:** Deleting a file in the mobile Sync app deletes it in @product@ \n| and across any synced clients. If you accidentally delete a file, the instance \n| or Site administrator can restore it from the instance's Recycle Bin. The \n| Recycle Bin is enabled by default. If the instance or Site administrator \n| disables the Recycle Bin, however, recovering deleted files is impossible. \n\nThe context menu also provides additional options for files. A small badge on\nthe file icon's top-right corner indicates the file's version in the @product@ \ninstance. You can also use the context menu to share files you've downloaded. \nPressing the *Share* icon opens a list of your device's apps capable of sharing \nthe file. To close the context menu and return to the list of files and folders, \nswipe to the right. The following screenshot shows the options available in a \nfile's context menu. \n\n![Figure 3: The badge on the file's icon shows the file's version in the @product@ instance. You can also share files that you've downloaded.](../../../../images/sync-mobile-file-actions.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/04-adaptive-media/01-intro.markdown",
    "content": "---\nheader-id: adapting-your-media-across-multiple-devices\n---\n\n# Adapting Your Media Across Multiple Devices\n\n[TOC levels=1-4]\n\nMedia providers must consider differences between devices (phones, laptops,\ntablets, etc.) when delivering content: not only their screen size but also\ntheir bandwidth and processing capabilities. @product@'s Adaptive Media app\nallows administrators to control image quality and dynamically adjusts uploaded\nmedia to best fit the screen being used. \n\n| **Note:** At this time, Adaptive Media only works for images in blog entries \n| and web content articles. \n\nAdaptive Media integrates with Documents and Media, Blogs, and Web Content. It\ngenerates a set of images for use on various screens. When the content is\naccessed, Adaptive Media checks the screen type and resolution and selects\nappropriate the appropriate image. Adaptive Media comes preinstalled in \n@product@. \n\nIn this section, you'll learn how to manage and use Adaptive Media. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/04-adaptive-media/02-installing-adaptive-media.markdown",
    "content": "---\nheader-id: installing-adaptive-media\n---\n\n# Installing Adaptive Media\n\n[TOC levels=1-4]\n\nThe Adaptive Media app is installed in @product@ by default. The following \nsections describe the Adaptive Media app's modules, and how to prepare Adaptive \nMedia to handle animated GIFs. \n\n| **Note:** Since the Adaptive Media app is installed by default, it's updated \n| via Liferay DXP Fix Packs and Liferay Portal CE GA releases. Using \n| [Liferay Marketplace](https://web.liferay.com/marketplace) \n| to update the app causes an error. \n\n## Adaptive Media's Modules\n\nSome modules in the Adaptive Media app are mandatory and must be enabled for \nAdaptive Media to function, while others can be disabled. The Adaptive Media API \nmodules, which export packages for the other modules to consume, are mandatory; \ndisabling one also disables any other modules that depend on it. Here's a list \nof the Adaptive Media API modules: \n\n-   Liferay Adaptive Media API\n-   Liferay Adaptive Media Content Transformer API\n-   Liferay Adaptive Media Image API\n-   Liferay Adaptive Media Image Item Selector API \n\nThe Adaptive Media core modules are also mandatory, and must be enabled to \nensure that Adaptive Media works as expected: \n\n-   Liferay Adaptive Media Document Library\n-   Liferay Adaptive Media Document Library Item Selector Web\n-   Liferay Adaptive Media Document Library Web\n-   Liferay Adaptive Media Image Content Transformer\n-   Liferay Adaptive Media Image Implementation\n-   Liferay Adaptive Media Image Item Selector Implementation\n-   Liferay Adaptive Media Image JS Web\n-   Liferay Adaptive Media Image Service\n-   Liferay Adaptive Media Image Taglib\n-   Liferay Adaptive Media Image Web\n-   Liferay Adaptive Media Item Selector Upload Web\n-   Liferay Adaptive Media Web\n\nThe Adaptive Media Blogs modules, which ensure that images uploaded to  blog \nentries can be processed and adapted, are optional. Here's a list of these \nmodules: \n\n-   Liferay Adaptive Media Blogs Editor Configuration\n-   Liferay Adaptive Media Blogs Item Selector Web\n-   Liferay Adaptive Media Blogs Web\n-   Liferay Adaptive Media Blogs Web Fragment\n\nThe Adaptive Media Journal modules are optional. These modules apply Adaptive \nMedia to web content articles: \n\n-   Liferay Adaptive Media Journal Editor Configuration\n-   Liferay Adaptive Media Journal Web\n\nThere are two more optional modules included in Adaptive Media: \n\n**Liferay Adaptive Media Image Content Transformer Backwards Compatibility:** \nEnsures that content created before the Adaptive Media installation can use\nadapted images without the need to edit that content manually. It transforms\nthe images both at startup and when a user views the content, which can\nnegatively affect performance. We therefore recommend that you run some\nperformance tests before using this module in production. You can disable\nthis module if you don't have old content, are experiencing performance\nproblems, or your old content doesn't need adapted images. \n\n**Liferay Adaptive Media Document Library Thumbnails:** Lets thumbnails in \nDocuments and Media use adapted images. For this to work, you must first \n[migrate the original thumbnails to adapted images](/docs/7-2/user/-/knowledge_base/u/migrating-documents-and-media-thumbnails-to-adaptive-media). \nWe highly recommend that you enable this module, but it's not mandatory. \n\nGreat! Now you know the mandatory and optional modules that come with Adaptive \nMedia. The next section discusses the installation requirements for using \nanimated GIFs with Adaptive Media. If you don't need to use GIFs, you can skip \nahead to the article on adding image resolutions to Adaptive Media. \n\n## Processing Animated GIFs\n\nTo process animated GIFs, Adaptive Media uses an external tool called \n[Gifsicle](https://www.lcdf.org/gifsicle). \nThis tool ensures that the animation works when the GIF is scaled to different \nresolutions. You must manually install Gifsicle on the server and ensure that\nit's on the `PATH`. Once it's installed, you must enable it in Adaptive Media's\n[advanced configuration options](/docs/7-2/user/-/knowledge_base/u/advanced-configuration-options). \n\nIf Gifsicle isn't installed and `image/gif` is included as a supported MIME type\nin the advanced configuration options, Adaptive Media scales only a GIF's single\nframe. This results in a static image in place of the animated GIF. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/04-adaptive-media/03-adding-image-resolutions.markdown",
    "content": "---\nheader-id: adding-image-resolutions\n---\n\n# Adding Image Resolutions\n\n[TOC levels=1-4]\n\nTo use Adaptive Media, you must first define the resolutions for the images\ndelivered to users' devices. Adaptive Media then generates new images scaled to\nfit those resolutions, while maintaining the original aspect ratio.\n\nTo access Adaptive Media settings, open the Control Panel and go to\n*Configuration* &rarr; *Adaptive Media*. Here you can create and manage\nresolutions.\n\n| **Note:** Adaptive Media configurations apply only to the current @product@\n| instance.\n\nOnce you create a resolution, Adaptive Media automatically generates copies of\nnewly uploaded images in that resolution. Images uploaded before you create the\nresolution aren't affected and must be adapted separately (see\n[Generating Missing Adapted Images](/docs/7-2/user/-/knowledge_base/u/managing-image-resolutions#generating-missing-adapted-images)).\n\n![Figure 1: Adaptive Media's image resolutions are listed in a table.](../../../images/adaptive-media-image-resolutions.png)\n\n## Adding a New Image Resolution\n\nThe number of image resolutions required and the values for each depend on the\nuse case. More resolutions may optimize image delivery, but generating\nmore images requires additional computational resources and storage space. To\nstart, we recommend that you create resolutions to cover common device sizes\nlike mobile phones, tablets, laptops, and desktops. If most users use one device\n(e.g., all Intranet users have the same company mobile phone), you can create a\nresolution to target that device.\n\nTo add a new resolution, click the *Add* icon\n(![Add new resolution](../../../images/icon-add.png)) on the Adaptive Media\nconfiguration page and provide the following information:\n\n**Name**: The resolution's name (this must be unique). This can be updated if a\ncustom `Identifier` is defined.\n\n**Max Width**: The generated image's maximum width. If a *Max Height* is given,\nthis field is optional. This value must be at least `1`.\n\n**Maximum Height**: The generated image's maximum height. If a *Max Width* is\ngiven, this field is optional. This value must be at least `1`.\n\n| **Note:** Adaptive Media generates images that fit the Max Width and Max\n| Height, while retaining the original aspect ratio. If you only provide one\n| value (either Max Width or Max Height), the generated image scales\n| proportionally to fit within the specified dimension, while maintaining its\n| original aspect ratio. This ensures that adapted images are not distorted.\n\n**Add a resolution for high density displays (2x):** Defines a scaled up\nresolution for HIDPI displays. Selecting this option creates a new resolution\ndouble the size of the original with the same name and the suffix `-2x`. For\nexample, if the original resolution is `400px` by `300px` (max width by max\nheight), the high density resolution is `800px` by `600px`.\n\n**Identifier:** The resolution's ID. By default, this is automatically generated\nfrom the name. You can specify a custom identifier by selecting the *Custom*\noption and entering a new *ID*. Third party applications can use this ID to\nobtain images for the resolution via Adaptive Media's APIs.\n\n| **Note:** Image resolutions and their identifiers can't be updated if the\n| resolution has been used to adapt images. This prevents inconsistencies in\n| generated images.\n\n![Figure 2: The form for adding a new Adaptive Media resolution.](../../../images/adaptive-media-new-img-resolution.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/04-adaptive-media/04-managing-image-resolutions.markdown",
    "content": "---\nheader-id: managing-image-resolutions\n---\n\n# Managing Image Resolutions\n\n[TOC levels=1-4]\n\nAdaptive Media lets you manage image resolutions and their resulting adapted \nimages. For example, you can disable, enable, edit, and delete resolutions. You \ncan also generate any adapted images that may be missing for a resolution. This \narticle discusses these topics and more. \n\n## Disabling Image Resolutions\n\nDisabling an image resolution prevents it from generating adapted images. Any \nimages uploaded after the resolution is disabled use the most appropriate\nresolution that's still active. Adapted images previously generated by the\ndisabled resolution are still available. \n\nTo disable an image resolution, click its *Actions* menu \n(![Actions](../../../images/icon-actions.png)) and select *Disable*. \n\n## Enabling Image Resolutions\n\nImage resolutions are enabled by default. If you need to enable a disabled \nresolution, click that resolution's *Actions* menu \n(![Actions](../../../images/icon-actions.png)) and select *Enable*. \n\nWhile a resolution is disabled, it doesn't generate adapted images for \nnew image uploads. After enabling a resolution, you should generate the adapted \nimages that weren't generated while it was disabled (see \n[Generating Missing Adapted Images](#generating-missing-adapted-images) \nfor instructions on this). \n\n## Editing Image Resolutions\n\nYou can't edit an image resolution that already has adapted images. This \nprevents odd behavior (of the adapted images---you're still free to be as odd as \nyou want). This is because any changes would only be applied to images uploaded \nafter the edit, creating an inconsistent set of adapted images. Odd indeed. \n\nTherefore, editing an image resolution is only possible if Adaptive Media hasn't\nyet generated adapted images for it. If you must change the values of\na resolution that already has adapted images, you must delete that resolution\nand create a new one with the new values. The next section discusses deleting\nresolutions. \n\n## Deleting Image Resolutions\n\nBe careful when deleting an image resolution, as any adapted images it created\nare irretrievably lost and are not automatically replaced by new image\nresolutions you create.\n\nFollow these steps to delete an image resolution:\n\n1.  Disable the resolution. You can't delete enabled resolutions. This prevents \n    the accidental deletion of image resolutions. \n\n2.  To delete the resolution and all its adapted images, select *Delete* from \n    the resolution's Actions menu \n    (![Actions](../../../images/icon-actions.png)). \n\n## Generating Missing Adapted Images\n\nIf Adaptive Media hasn't generated all the images you need---say, if new images \nwere uploaded before a new image resolution was created or while the resolution\nwas disabled--you must generate the missing images manually.\n\n![Figure 1: The *Adapted Images* column shows the percentage of images that are adapted for each resolution. ](../../../images/adaptive-media-coverage.png)\n\nTo manually generate missing adapted images,\n\n1.  For a single resolution, select *Adapt Remaining* from the resolution's \n    Actions menu \n    (![Actions](../../../images/icon-actions.png)). \n\n2.  For all resolutions at once, select *Adapt All Images* from the Actions menu \n    in the Control Menu at the top of the page. \n\n## The Recycle Bin and Adapted Images\n\nYou can't move adapted images directly to the Recycle Bin. But if the original \nimage is in the Recycle Bin, the corresponding adapted images behave as if they \nare in the Recycle Bin and users can't view them. \n\n| **Note:** URLs that point to adapted images whose original image is in the \n| Recycle Bin return an error code of `404 Not Found`. \n\nIf the original image is restored from the Recycle Bin, the adapted images are \naccessible again. \n\nAwesome! Now you know how to manage image resolutions in Adaptive Media. Next, \nyou'll learn about creating content with adapted images. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/04-adaptive-media/05-creating-content-adapted-images.markdown",
    "content": "---\nheader-id: creating-content-with-adapted-images\n---\n\n# Creating Content with Adapted Images\n\n[TOC levels=1-4]\n\nAdaptive Media is mostly invisible for blog and web content creators. Once\nan image is added to the content, the app works behind the scenes to\ndeliver an adapted image appropriate to the device in use. Content creators\nselect an image when adding it to their content---they don't have to (and \ncan't) select an adapted image. Adaptive Media identifies each adapted image in\nthe content's HTML with a `data-fileentryid` attribute that is replaced with the\nlatest adapted image when the user views the content. This lets Adaptive Media\ndeliver the latest adapted images to your content, even if the content existed\nprior to those images. \n\n| **Note:** If Adaptive Media is uninstalled, the original images are displayed \n| in the blog entries and web content articles.\n\n## Including Adapted Images in Content\n\nSince Adaptive Media delivers the adapted images behind the scenes, content \ncreators should add images to \n[blog entries](/docs/7-2/user/-/knowledge_base/u/publishing-blogs) \nand \n[web content](/docs/7-2/user/-/knowledge_base/u/creating-web-content) \nas usual: by clicking the image button in the editor and then selecting the \nimage in the file selector. \n\nHowever, there are some important caveats. When using the file selector to\ninclude an image for a blog entry, Adaptive Media works only with images added\nfrom the *Blog Images*, *Documents and Media*, and *Upload* tabs. Additionally,\nadapted images can only be applied to a blog entry's content--cover images\nexcluded. Adaptive Media works for images added to a blog entry via drag and\ndrop, as the image is automatically uploaded to the Blog Images repository,\nadapted, and then included in the blog entry's content. You can see this by\ninspecting the HTML and checking that the image contains the `<img>` tag and\n`data-fileentryid` attribute.\n\nFor web content articles, Adaptive Media works only with images added from the\nfile selector's *Documents and Media* tab. Unlike blogs, Adaptive Media doesn't \ndeliver adapted images for images added to web content articles via drag and \ndrop.\n\nFor both blog entries and media content articles, Adaptive Media doesn't work \nwith images added from the file selector's *URL* tab. This is because the image \nis linked directly from the URL and therefore provides no image file for \nAdaptive Media to copy.\n\nNote that you can see the `<img>` tag and `data-fileentryid` attribute in the \nHTML of a blog entry or a web content article while you're writing it. When the \ncontent is displayed, the HTML is automatically replaced and looks similar to \nthis: \n\n    <picture>\n\n        <source media=\"(max-width:850px)\" \n        srcset=\"/o/adaptive-media/image/44147/med/photo.jpeg\">\n\n        <source media=\"(max-width:1200px) and (min-width:850px)\" \n        srcset=\"/o/adaptive-media/image/44147/hd/photo.jpeg\">\n\n        <source media=\"(max-width:2000px) and (min-width:1200px)\" \n        srcset=\"/o/adaptive-media/image/44147/ultra-hd/photo.jpeg\">\n\n        <img src=\"/documents/20140/0/photo.jpeg/1992-9143-85d2-f72ec1ff77a0\">\n\n    </picture>\n\nThis example uses three different images, each with a different resolution. A \n`source` tag defines each of these images. Also note the original image (`img`) \nis used as a fallback in case the adapted images aren't available. \n\n## Using Adapted Images in Structured Web Content\n\nTo use adapted images in \n[structured web content](/docs/7-2/user/-/knowledge_base/u/designing-uniform-content), \ncontent creators must manually include an image field in the web content's \nstructure. Then they can reference that image field in the matching template by \nselecting it on the left side of the editor. Here's an example snippet of an \nimage field named `Imagecrrf` included in a template: \n\n    <#if Imagecrrf.getData()?? && Imagecrrf.getData() !=\"\">\n      <img data-fileentryid=\"${Imagecrrf.getAttribute(\"fileEntryId\")}\" \n      alt=\"${Imagecrrf.getAttribute(\"alt\")}\" src=\"${Imagecrrf.getData()}\" />\n    </#if>\n\nThis snippet includes the `data-fileentryid` attribute to ensure that Adaptive \nMedia replaces the image with an adapted image. If you inspect the resulting web \ncontent's HTML in the editor's code view, you should see a tag like this: \n\n    <img data-fileentryid=\"37308\" \n    src=\"/documents/20143/0/photo.jpeg/85140258-1c9d-89b8-4e45-d79d5e262318?t=1518425\" />\n\nNote the `<img>` tag with a `data-fileentryid` attribute. Adaptive Media uses \nthe file entry ID to replace the `<img>` element automatically with a \n`<picture>` element that contains the available adapted images for each \nresolution (see the `<picture>` example above). \n\n## Staging Adapted Images\n\nAdaptive Media is fully integrated with @product@'s \n[content staging](/docs/7-2/user/-/knowledge_base/u/staging) \nand \n[export/import](/docs/7-2/user/-/knowledge_base/u/exporting-importing-widget-data) \nfunctionality. Adaptive Media includes adapted images in staged content when \npublished, and can update those images to match any new resolutions. \n\nSimilarly, when content that contains adapted images is exported, Adaptive Media \nexports those images in the LAR file. That LAR file can then be imported to \nrestore or transfer that content, along with its adapted images.\n\nAdaptive Media doesn't regenerate adapted images during export/import \nor the publication of staged content. To improve performance, Adaptive Media \ninstead reuses the existing adapted images. \n\nAwesome! Now you know how create content that contains adapted images. You also \nknow how Adaptive Media includes adapted images in the content's HTML. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/04-adaptive-media/06-migrating-dm-thumbnails.markdown",
    "content": "---\nheader-id: migrating-documents-and-media-thumbnails-to-adaptive-media\n---\n\n# Migrating Documents and Media Thumbnails to Adaptive Media\n\n[TOC levels=1-4]\n\n@product@ automatically generates thumbnails for images in Documents and Media.\nOnce you deploy the Adaptive Media app, however, @product@ doesn't display \nthumbnails until you migrate them to Adaptive Media. This article walks you\nthrough this migration process. \n\n| **Note:** You must be a Portal Administrator to perform the actions described\n| here. \n\nYou'll get started by creating image resolutions for the thumbnails in Adaptive \nMedia. \n\n## Adding the Replacement Image Resolutions\n\nTo migrate the existing Documents and Media thumbnails, you must add new image \nresolutions in Adaptive Media that have maximum height and maximum width values \nthat match the values specified in the following portal properties: \n\n    dl.file.entry.thumbnail.max.height\n\n    dl.file.entry.thumbnail.max.width\n\n    dl.file.entry.thumbnail.custom1.max.height\n\n    dl.file.entry.thumbnail.custom1.max.width\n\n    dl.file.entry.thumbnail.custom2.max.height\n\n    dl.file.entry.thumbnail.custom2.max.width\n\n| **Note:** Some of these properties may not be enabled. You need only create\n| image resolutions in Adaptive Media for the enabled properties. \n\nTo create the new Image Resolutions, follow the instructions found in the \n[Adding Image Resolutions](/docs/7-2/user/-/knowledge_base/u/adding-image-resolutions) \nsection of the Adaptive Media user guide. \n\nNow you're ready to to create the Adaptive Media images. \n\n## Creating the Adaptive Media Images\n\nOnce the required image resolutions exist, you can convert the Documents and \nMedia thumbnails to Adaptive Media images. As mentioned in \n[the Adaptive Media installation guide](/docs/7-2/user/-/knowledge_base/u/installing-adaptive-media), \nthe module *Liferay Adaptive Media Document Library Thumbnails* (which is \nincluded in the Adaptive Media app) enables this functionality. \n\nThere are two different ways to migrate the Documents and Media thumbnails to \nAdaptive Media: \n\n**Adapt the images for the thumbnail image resolution:** This scales the \nexisting thumbnails to the values in the Adaptive Media image resolutions, which \ncan take time depending on the number of images. We only recommend this approach \nwhen there isn't a large number of thumbnails to process, or if you prefer to \ngenerate your images from scratch. This approach is covered in more detail in\n[Generating Missing Adapted Images](/docs/7-2/user/-/knowledge_base/u/managing-image-resolutions#generating-missing-adapted-images). \n\n**Execute a migrate process that reuses the existing thumbnails:** This copies \nthe existing thumbnails to Adaptive Media, which performs better because it \navoids the computationally expensive scaling operation. The next section \ndescribes the steps to run this process. \n\n### Running the Migration Process\n\nThe migration process is a set of Gogo console commands. You can learn more\nabout using the Gogo console in \n[the Felix Gogo Shell article](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell). \n\nFollow these steps to migrate your thumbnails from the Gogo console:\n\n1.  Run the `thumbnails:check` command. For each instance, this lists how many \n    thumbnails are pending migration. \n\n2.  Run the `thumbnails:migrate` command. This executes the migration process, \n    which may take a while to finish depending on the number of images. \n\n3.  Run the `thumbnails:cleanUp` command. This deletes all the original \n    Documents and Media thumbnails and updates the count returned by \n    `thumbnails:check`. Therefore, you should **only** run `thumbnails:cleanUp`\n    after running the migrate command and ensuring that the migration ran \n    successfully and no images are pending migration. \n\n| **Note:** If you undeploy Adaptive Media at some point after running the \n| migration process, you must regenerate the Documents and Media thumbnails. \n| To do this, navigate to *Control Panel* &rarr; *Configuration* &rarr; *Server \n| Administration* and click *Execute* next to *Reset preview and thumbnail files \n| for Documents and Media*. \n\nGreat! Now you know how to migrate your Documents and Media thumbnails to \nadapted images. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/04-adaptive-media/07-advanced-configuration.markdown",
    "content": "---\nheader-id: advanced-configuration-options\n---\n\n# Advanced Configuration Options\n\n[TOC levels=1-4]\n\nAdaptive Media's advanced configuration options are available in System \nSettings. Open the Control Panel and go to *Configuration* &rarr; \n*System Settings*, then select *Adaptive Media*. There are two configurations \nlisted under *SYSTEM SCOPE*: \n\n-   Images\n-   Processes\n\nThe *Images* configuration contains the following options: \n\n**Supported MIME Types:** A list of the image MIME types that Adaptive Media \nsupports. If an image is uploaded and its MIME type isn't in this list, Adaptive \nMedia ignores the image. By default, this list contains many common MIME types. \n\n**Gifsicle:** To scale animated GIFs, Adaptive Media uses an external tool \ncalled \n[Gifsicle](https://www.lcdf.org/gifsicle/). \nFirst install Gifsicle on the server, ensure that it's on the `PATH` environment \nvariable, and then click the box next to *Gifsicle Enabled*. If Gifsicle isn't \ninstalled and `image/gif` is included as a supported MIME type, Adaptive Media \nscales only one frame of the GIF, making a static GIF. \n\n**Max Image Size:** Maximum size of the source images that Adaptive Media can \nuse to generate adapted images. Adaptive Media will not generate adapted images \nfor source images larger than this setting. The default value is 10 MB. To \ngenerate adapted images for all source images regardless of size, set this to \n-1. Since generating adapted images from large source images requires \nsignificant amounts of memory, you can specify a lower *Max Image Size* to avoid \nout of memory errors. \n\n![Figure 1: You can configure Gifsicle and the maximum image size for Adaptive Media.](../../../images/adaptive-media-config-01.png)\n\nThe *Processes* configuration is related to Adaptive Media's asynchronous \nprocessing. These values can be modified to improve performance for specific \nscenarios or use cases. The following options are available: \n\n**Max Processes:** The maximum number of processes for generating adapted media. \nThe default value is `5`. \n\n**Core Processes:** The number of processes always available for generating \nadapted media. The default value is `2`. This setting can't exceed the \n*Max processes* setting. \n\n| **Warning:** Larger values for Max Processes and Core Processes may cause out \n| of memory errors, as processing more images at once can consume large amounts \n| of memory. Out of memory errors can also occur if the source images Adaptive \n| Media uses to generate adapted images are large. You can restrict the maximum \n| size of such images via the *Max Image Size* setting in the \n| *Adaptive Media Image* configuration, which is described next. You should run \n| performance tests to optimize these settings for the amount of memory \n| available on your system. \n\n![Figure 2: You can also configure Adaptive Media's image processing resources.](../../../images/adaptive-media-config-02.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/01-intro.markdown",
    "content": "---\nheader-id: collaborating\n---\n\n# Collaborating\n\n[TOC levels=1-4]\n\nYour users can leverage a robust suite of collaboration apps to get things done\nand form extensive communities. These apps provide all the features that you\nwould expect of standalone apps, but these apps are integrated: they share\na common look and feel, security model, and architecture because of Liferay's\ndevelopment framework. You can use them in combination with user registration\nand content management features to build a well-integrated, feature-rich site. \n\nThis guide shows you how to administer and use the collaboration apps: \n\n-   Blogs\n-   Message Boards\n-   Wiki\n-   Alerts and Announcements\n-   Knowledge Base\n-   Bookmarks\n\n![Figure 1: This blog entry looks fascinating.](../../../images/blog-entry-abstract.png)\n\n![Figure 2: This is a great thread.](../../../images/message-boards-participate-in-threads.png)\n\n![Figure 3: The Wiki widget displays your wiki on a Site page.](../../../images/wiki-page-full.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/02-blogs/01-intro.markdown",
    "content": "---\nheader-id: publishing-blogs\n---\n\n# Publishing Blogs\n\n[TOC levels=1-4]\n\nThe Blogs app's editor has a complete set of WYSIWYG controls that appear when \nand where you need them. You can also switch to source mode to edit your \ncontent's HTML code. In source mode, you can work with light text on a dark \nbackground or dark text on a light background. You can even open a dual-screen \nHTML editor to see your code rendered in real time. \n\nThe Blogs app also contains a powerful set of tools for customizing how your \nblogs appear. For example, display templates like Abstract or Full Content let \nyou choose how much of a blog post appears on a page. You can leverage the \nbuilt-in display templates or create your own. You can also add a cover image to\neach of your blog entries. Let's face it---you might not be able to judge a book\nby its cover, but a blog post with a nice cover image is more likely to draw\nreaders. \n\nRead on to learn about Liferay's blogging platform.\n\n![Figure 1: This blog entry looks fascinating.](../../../../images/blog-entry-abstract.png)\n\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/02-blogs/02-adding-blog-entries.markdown",
    "content": "---\nheader-id: adding-blog-entries\n---\n\n# Adding Blog Entries\n\n[TOC levels=1-4]\n\nEach site contains a built-in blog instance, so you can add blog entries to it \nright away. The easiest way to do this is in the Site Administration menu. \nFollow these steps to add a blog entry in Site Administration: \n\n1.  Click the Menu button \n    (![Menu](../../../../images/icon-menu.png)) to open the product menu. Then\n    expand the menu for your site and select *Content & Data* &rarr; *Blogs*. \n    This takes you to the Blogs app for your site. The *Entries* tab is selected \n    by default, which lists the site's blog entries. \n\n2.  Click the *Add* button \n    (![Add](../../../../images/icon-add.png)) to create a new blog entry. This \n    presents the blog entry editor. Note that the same form appears when editing \n    a blog entry. \n\n    ![Figure 1: This screenshot shows some of the blog entry editor's controls.](../../../../images/blogs-new-entry.png)\n\n3.  The first input field, *Drag \\& Drop to Upload*, is for optionally adding \n    a cover image for your entry. By default, an \n    [Asset Publisher](/docs/7-2/user/-/knowledge_base/u/publishing-assets)\n    shows this cover image as part of the blog entry's abstract. You can insert \n    any image you like in this field, either via drag and drop or the *Select \n    File* button. The latter lets you choose an existing image in the blog, an\n    image from Documents and Media, or an image that you upload from your\n    machine. \n\n    If you select an image from Documents and Media, you can make changes to it\n    with the \n    [Image Editor](/docs/7-2/user/-/knowledge_base/u/editing-images). \n    Edits you make are applied automatically to a copy of the image, which you\n    can then use as your cover photo. \n\n    Upon upload, the image appears in the pane. To center the image, drag it \n    into place. You can also add a caption. If you want to select a different \n    image, click the *Change* icon \n    (![Change](../../../../images/icon-change.png)). Clicking the trash \n    can icon removes the image from the blog entry. \n\n4.  Enter a title for your blog entry. You can also add a subtitle if needed. \n\n5.  Enter your blog entry's content in the *Content* field. This field is small\n    at first, but it expands as you add content. The editor displays the editing\n    controls when you need them and hides them from view when you don't. When\n    you select text in your blog post, for example, a bar with context-specific\n    editing controls appears. This keeps your canvas uncluttered so you can\n    focus on writing. You can also add images, videos, and tables in your blog\n    entry's content. See \n    [Using the Blog Entry Editor](/docs/7-2/user/-/knowledge_base/u/using-the-blog-entry-editor)\n    for instructions on creating your blog entry's content. \n\n6.  Expand the *Categorization* panel and associate your blog entry with \n    [tags and/or categories](/docs/7-2/user/-/knowledge_base/u/organizing-content-with-tags-and-categories). \n    Although this is optional, it improves search results for blog entries and \n    gives your users more navigation options. For example, you can add the Tags\n    Navigation app to another column on your blogs page, which lets users browse \n    blog entries by tag. \n\n7.  Expand the *Related Assets* panel and choose any other content in your site \n    that you want to associate with this blog entry. Although this is optional, \n    related assets let you tie together content on your site. For example, you \n    might want to write a blog entry about a discussion that happened on the \n    forums. To link those two assets together, select the forum thread under \n    Related Assets. For more information, see the \n    [related assets documentation](/docs/7-2/user/-/knowledge_base/u/defining-content-relationships).\n\n8.  Expand the *Configuration* panel if you want to customize your blog entry's \n    URL, abstract, or display date. You can also set whether to allow pingbacks \n    for your blog entry. For the URL, the default selection of *Automatic* \n    generates the URL for you based on the blog entry's title. This URL appears \n    in the *Blog Entry URL* text box. Selecting *Custom* lets you enter your own \n    URL. Note that if you change the blog entry's URL after publishing the \n    entry, the original URL redirects to the new URL. \n\n    You can also specify the blog entry's abstract. Enter a 400 character \n    text-only abstract, or a custom abstract that contains a thumbnail image and \n    a manually written description. The *Small Image* section lets you add a \n    small image that appears when blog entries are displayed in list view. Below \n    the abstract section, you can set the entry's display date and time. \n\n    Note that if you're editing an existing blog entry, the *Send Email Entry\n    Updated* toggle appears. Setting this to *YES* sends an email to any\n    subscribers when the blog entry is updated. You can customize this email\n    when \n    [configuring the Blogs app](/docs/7-2/user/-/knowledge_base/u/configuring-the-blogs-app). \n\n    Finally, you can allow *pingbacks* for the blog entry. Pingbacks are XML-RPC\n    requests that are sent automatically when you link to another site. If you\n    link to another site in your blog entry, @product@ sends a pingback to the\n    other site to notify that site that you linked to it. Similarly, if someone\n    links to your blog entry, @product@ can receive a pingback from that\n    site and record the link. \n\n    ![Figure 2: When creating a blog entry, the Configuration panel lets you control when and where the blog entry appears, and what to use for the entry's abstract.](../../../../images/blog-entry-configuration.png)\n\n9.  Expand the *Display Page Template* panel if you want to select a \n    [display page template](/docs/7-2/user/-/knowledge_base/u/display-pages-for-web-content) \n    for displaying your blog entry. The following options are available: \n\n    **Default Display Page Template:** Use the default display page template.\n\n    **Specific Display Page Template:** Click the *Select* button to select the \n    display page template you want to use. \n\n    **No Display Page Template:** Do not use a display page template. \n\n10. Expand the *Permissions* panel to customize your blog entry's permissions.\n    Use the *Viewable by* selector to set who can view the blog entry: \n\n    **Anyone (Guest Role):** Anyone, including guests, can view the entry. \n\n    **Site members:** Only site members can view the entry. \n\n    **Owner:** Only the entry's owner can view the entry. \n\n    Click the *More Options* link to bring up a permissions table that lets you \n    grant or revoke the following permissions for guests and site members: \n\n    **Update Discussion**: Edit another user's comment on the blog entry.\n\n    **Delete**: Move the blog entry to the \n    [Recycle Bin](/docs/7-2/user/-/knowledge_base/u/using-the-recycle-bin).\n\n    **Permissions**: View and modify the blog entry's permissions.\n\n    **Delete Discussion**: Delete any comments on the blog entry.\n\n    **Update**: Edit and modify the blog entry.\n\n    **View**: View the blog entry.\n\n    **Add Discussion**: Comment on the blog entry. \n\n10. Click *Publish* to publish your blog entry. It now appears in the *Entries* \n    tab. \n\n    ![Figure 3: The Blogs app in Site Administration lists the site's blog entries.](../../../../images/blog-entries-site-admin.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/02-blogs/03-blog-entry-editor.markdown",
    "content": "---\nheader-id: using-the-blog-entry-editor\n---\n\n# Using the Blog Entry Editor\n\n[TOC levels=1-4]\n\nWhen you \n[create a new blog entry](/docs/7-2/user/-/knowledge_base/u/adding-blog-entries), \nyou create its content with the blog entry editor. This editor is simple yet \npowerful. Its editing tools are context-aware, only appearing when you need \nthem. They aren't visible while you're writing, which lets you focus on the task \nat hand. But when you select text, for example, a toolbar appears that contains \nbuttons for styling the text. There are similar contextual options for adding \nand editing images, tables, and other types of content. And this is all in the \neditor's text view. You can switch to code view to edit the content's HTML. \nRegardless of which view you use, your entry is automatically saved as a draft \nevery 25 seconds, so a browser crash or network interruption won't cause you to \nlose your work. \n\nThis guide shows you how to use this editor to create and edit blog entries. \n\n![Figure 1: This screenshot shows some of the blog entry editor's controls.](../../../../images/blogs-edit-entry.png)\n\n## Using the Editor's Text View\n\nWhen you create or edit a blog entry, the editor is in text view by default. \nText view is a WYSIWYG editor that lets you enter and edit text and other types \nof content. To enter text, place your cursor in any text field (e.g., Title, \nSubtitle, Content, etc.) and type or paste your text. Note that the Content area \nexpands to fit its contents. \n\nTo style or format text in the Content area, first select the text. A toolbar \nappears above the text that contains the following options, as shown in the \nabove screenshot: \n\n**Text Style:** This selector menu, set to Normal by default, lets you choose \nthe text's style. Normal is typical body text, but you can also select from \ndifferent heading styles, alert or error message styles, code style, and more. \n\n**Typeface:** Select bold, italic, or underline. \n\n**List Style:** Select a numbered or bulleted list. \n\n**Link:** Link the selected text to a specific URL, or to an item in the portal \n(e.g. a file in Documents and Media).\n\n**Twitter:** Generates a link to tweet the selected text. \n\nWhen you park your cursor in the entry's content area, the *Add* icon (`+`) \nappears. If you click this icon, it shows controls for inserting an image, \nvideo, table, or horizontal line \n(![Controls](../../../../images/icon-content-insert-controls.png)). Follow these \ninstructions to insert each: \n\n**Image:** Click the mountain icon, then select or upload an image in the image \nfile selector screen that appears. Alternatively, you can drag-and-drop image \nfiles into the content area. You can also use the \n[built-in Image Editor](/docs/7-2/user/-/knowledge_base/u/editing-images) \nto apply simple edits to an image. Any edits you make are automatically applied \nto a copy of the image. After you add an image to the blog entry, clicking the \nimage brings up controls for justifying it to the right or left side of the \narticle. \n\n**Video:** Click the play icon and insert the video's link. The video then \nappears in your content. \n\n**Table:** Click the table (grid) icon and then choose the number of rows and \ncolumns in your table. When you click inside the table, table editing controls \nappear. They let you designate the first row and/or column as table headers. The \ncontrols also enable you to add rows, columns, and cells. As you type in a cell, \nthe column width automatically adjusts to fit the content. \n\n**Line**: Click the line icon. A simple, lightweight horizontal line then \nappears in your content. Such lines are good for separating sections of content \nin a large blog entry. \n\n## Using the Editor's Code View\n\nTo switch to code view, click the the *Source* icon (`</>`) that appears when \nyou place your cursor in the Content area. The following buttons exist at the \ntop-right of code view: \n\n**Text View** \n(![Text](../../../../images/icon-text.png)): Switch back to text view. \n\n**Dark/Light Theme** \n(![Dark Theme](../../../../images/icon-dark-theme.png) / \n![Light Theme](../../../../images/icon-light-theme.png)): Switch the code \neditor between dark and light theme. \n\n**Fullscreen** \n(![Fullscreen](../../../../images/icon-enlarge.png)): Work in a dual-pane view \nthat shows your HTML code on the left and a preview pane on the right. In this \nview, you can arrange the HTML and preview panes horizontally or vertically. You \ncan also hide the preview pane so the HTML editor takes up the entire window \nspace. \n\n![Figure 2: Editing in code view lets you work with your blog entry's underlying HTML.](../../../../images/blogs-code-view.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/02-blogs/04-managing-blog-entries.markdown",
    "content": "---\nheader-id: managing-blog-entries\n---\n\n# Managing Blog Entries\n\n[TOC levels=1-4]\n\nThe Blogs app in Site Administration helps bloggers and blog administrators \nmanage blog entries. To access this app, click the Menu button \n(![Menu](../../../../images/icon-menu.png)) to open the product menu, then \nexpand the menu for your site and select *Content & Data* &rarr; *Blogs*. The \n*Entries* tab is selected by default, which lists the site's blog entries. \n\n![Figure 1: The Blogs app in Site Administration lists the site's blog entries.](../../../../images/blog-entries-site-admin.png)\n\nYou can use the Management Bar to manage your site's blog entries. If you've \nadded blog entries via your site's Blogs app, then you're already familiar with \nthe Management Bar's *Add* button \n(![Add](../../../../images/icon-add.png)). \n\nThe sections that follow describe how to manage your blog entries. \n\n-   [View Types](#view-types)\n-   [Finding and Arranging Blog Entries](#finding-and-arranging-blog-entries)\n-   [Selecting Blog Entries](#selecting-blog-entries)\n-   [Sharing Blog Entries](#sharing-blog-entries)\n\n## View Types\n\nThe *View Types* button is to the left of the Add button. It lets you choose how \nto display the blog entries in the Blogs app. The View Types button's icon \ndepends on the selected view type: \n\n**Cards** (![Cards](../../../../images/icon-view-type-cards.png)): Shows a \ncard-like rendering of the blog entry, with the author's profile picture. If the \nentry doesn't contain a cover image, a generic rendering of the entry is \ndisplayed. Each card also contains the entry's timestamp, title, \n[workflow](/docs/7-2/user/-/knowledge_base/u/workflow) \nstatus (e.g., Approved, Draft, etc.), and an Actions menu \n(![Actions](../../../../images/icon-actions.png)). \n\n**List** (![List](../../../../images/icon-view-type-list.png)): \nShows the same information as the Cards view type, in a list with the author's \nprofile picture instead of the blog entry's cover image. \n\n**Table** (![Table](../../../../images/icon-view-type-table.png)): Shows the \nsame information as the other view types, in a list with no file renderings. \nAlso, the blog entry information is in columns. \n\n## Finding and Arranging Blog Entries\n\nThe Management Bar also contains tools that help you locate and arrange blog \nentries. The most prominent of these tools is the *Search* bar, where you can \nfind files by keywords. \n\nTo the left of the Search bar, the Sort button \n(![Sort](../../../../images/icon-sort.png)) arranges entries in ascending \nor descending order. \n\nYou can also arrange entries via the *Filter and Order* selector using these\ncriteria: \n\n**All:** Shows all of the site's entries. \n\n**Mine:** Shows only the current user's entries. \n\n**Display Date:** Orders the entries by display date. \n\n## Selecting Blog Entries\n\nThe checkbox on the left-most side of the Management Bar selects all currently \ndisplayed blog entries. Selecting multiple entries lets you act on all of them \nat once. You can also select multiple entries individually by using the \ncheckboxes for each. When you select one or more entries, the Management Bar \nchanges to reflect the actions you can take on the selected entries. \n\nClick the *Trash* button \n(![Trash](../../../../images/icon-trash.png)) to move the selected entries to \nthe Recycle Bin. Unselect the checkbox to return the Management Bar to its \nnormal view. \n\n![Figure 2: With multiple blog entries selected, the management bar changes to reflect the actions you can take on the selected entries.](../../../../images/blog-management-bar-selected.png)\n\n## Sharing Blog Entries\n\nYou can also share blog entries from the Blogs app in Site Administration. \nSharing is enabled by default, as described in \n[Configuring Sharing](/docs/7-2/user/-/knowledge_base/u/configuring-sharing). \nSharing blog entries works the same as \n[sharing files](/docs/7-2/user/-/knowledge_base/u/sharing-files). \n\nWhen sharing is enabled and you have sharing permission, you can share a blog \nentry by clicking its *Actions* menu \n(![Actions](../../../../images/icon-actions.png)) \nand selecting *Share*. \n\n![Figure 3: You can share a blog entry via its Actions menu.](../../../../images/blog-share.png)\n\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/02-blogs/05-configuring-blogs-app.markdown",
    "content": "---\nheader-id: configuring-the-blogs-app\n---\n\n# Configuring the Blogs App\n\n[TOC levels=1-4]\n\nBy configuring the Blogs app in Site Administration, you can control how the app \nbehaves for all blogs in your site. To access this app, click the Menu button \n(![Menu](../../../../images/icon-menu.png)) to open the product menu, then \nexpand the menu for your site and select *Content* &rarr; *Blogs*. The *Options* \nmenu \n(![Options](../../../../images/icon-options.png)) at the top-right of the Blogs \napp lets you configure permissions and notifications, or import/export the app's \ncontent. \n\n![Figure 1: You can configure the options for your site's Blogs app.](../../../../images/blog-instance-options.png)\n\nHere are each of the options available in this menu: \n\n**Entries Permissions:** Configure the permissions that can be applied to the \nBlogs app. You can control which roles can add an entry, configure entry \npermissions, and subscribe to entries. \n\n**Export/Import:** Export or import a LAR file that contains the Blogs app's \ncontent. \n\n**Configuration:** Configure the following options for the Blogs app, in these \ntabs: \n\n-   **Email From:** Define the *From* field in the email messages that users\n    receive from Blogs. \n-   **Entry Added Email:** Define a subject and body for the emails sent when a \n    new blog entry has been added. \n-   **Entry Updated Email:** Define a subject and body for the emails sent when \n    a new blog entry has been updated. \n-   **RSS:** Lets you enable RSS subscription and choose how blogs are displayed \n    to RSS readers. The *Maximum Items to Display* selector lets you choose the \n    total number of RSS feed entries to display on the initial page. You can \n    choose up to one hundred to be displayed. The *Display Style* selector lets \n    you choose between *Full Content*, *Abstract*, and *Title* for the entry \n    display in the RSS feed. Lastly, the *Format* selector lets you choose which \n    format the RSS feed uses to deliver the entries: Atom 1.0, RSS 1.0, or RSS \n    2.0. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/02-blogs/06-displaying-blogs.markdown",
    "content": "---\nheader-id: displaying-blogs\n---\n\n# Displaying Blogs\n\n[TOC levels=1-4]\n\nThe Blogs app in Site Administration lets you \n[add](/7-2/user/-/knowledge_base/u/adding-blog-entries), \n[manage](/7-2/user/-/knowledge_base/u/managing-blog-entries), \nand \n[configure](/docs/7-2/user/-/knowledge_base/u/configuring-the-blogs-app) \nyour site's blogs. You can then display those blogs by adding a separate Blogs \nwidget to a page. Adding the Blogs widget to a site page creates a shared blog \nfor site members. Adding the widget to a user's personal site (dashboard) \ncreates a blog just for that user. The widget works the same way in both cases. \nAnd of course, you can scope a blog to a page to produce a blog instance for \njust that page. \n\nTo add a Blogs widget to a page:\n\n1.  Navigate to the page. \n\n2.  From the *Add* menu \n    (![Add](../../../../images/icon-add-app.png)), open *Widgets* &rarr; \n    *Collaboration*. \n\n3.  Drag a *Blogs* widget onto the page.\n\nBy default, the Blogs widget lists abstracts of the site's recent blog entries. \nThe listing shows each entry's cover image prominently. Each abstract in the \nlisting also shows the number of comments, thumbs up/down ratings, and links to \nshare the entry on Twitter, Facebook, LinkedIn, and other social networking \nsites. Clicking a blog entry lets you view its full content, where you can also \ncomment on the entry. \n\n![Figure 1: Fancy a lunar spelunking trip? This blog entry's abstract lets you know what you're getting into.](../../../../images/blog-entry-abstract.png)\n\nThere are several display options that let you configure the listing to look the \nway you want. To configure the widget, click the *Options* icon \n(![Options](../../../../images/icon-app-options.png)) in its title bar and \nselect *Configuration*. The display settings are in the *Setup* tab:  \n\n**Enable Ratings:** Whether readers can rate blog entries. \n\n**Enable Comments:** Whether readers can comment on blog entries. \n\n**Show View Count:** Whether to show the number views for each entry. \n\n**Social Bookmarks:** The social networking sites that users can share blog \nentries with. Only those in the *Current* column are displayed via the share \nbuttons on each blog entry. To move social networking sites between the \n*Current* and *Available* columns, select the sites and use the arrows between \nthose columns. Similarly, use the up/down arrows beneath the *Current* column to \nreorder the sites as they appear on each blog entry. For more information, see \n[the social bookmarks documentation](/docs/7-2/user/-/knowledge_base/u/using-social-bookmarks). \n\n**Display Style:** The display style for social bookmarks. *Inline* is the \ndefault and displays the social bookmark icons in a row. *Menu* hides them \ninside a single share menu. \n\n**Maximum Items to Display:** The total number of blog entries to display on the \ninitial page. You can select up to 75 to display at once. \n\n**Display Template:** The overall appearance of blog entries in the listing. \n*Abstract* is the default, and is shown in the above screenshot. You can also \nchoose the following: \n\n-   **Full Content:** Displays each blog entry's full content. \n-   **Title:** Displays only the blog entry's title.\n-   **Basic:** A stripped-down version of the Abstract, with less text and no \n    cover image.\n-   **Card:** Displays each blog entry in a card-like rectangle that shows the \n    cover image, title, author, post date, and a few lines of text. \n\nTo select a different widget template or create your own, click \n*Manage Templates*. For more information, see the documentation on \n[widget templates](/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates). \n\n![Figure 2: The Card display template makes your blog posts look like fun little trading cards.](../../../../images/blogs-cards.png)\n\n**Enable Report Inappropriate Content:** Whether to let users flag content as \ninappropriate, which sends an email to administrators. \n\n**Enable Ratings for Comments:** Whether to let readers rate blog entry \ncomments.\n\n**Show Related Assets:** Whether to display related content from other \napps/widgets in blog entries. \n\nThere are also other tabs in *Configuration*: \n\n**Communication:** Lists public render parameters the widget publishes to other \nwidgets on the page. Other apps/widgets can read and take actions on these. For \neach shared parameter, you can specify whether to allow communication using the \nparameter and select which incoming parameter can populate it. \n\n**Sharing:** Embed the widget instance as a widget on any website, Facebook, \nNetvibes, or as an OpenSocial Gadget. \n\n**Scope:** Specify the blog instance the widget displays: the current site's \nblog (default), the global blog, or the page's blog. If the page doesn't already \nhave a blog instance, you can select scope option *\\[Page Name\\] \\(Create New\\)* \nto create a page-scoped blog instance. \n\nWhen you finish setting the options, click *Save* and then close the dialog box. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/02-blogs/07-aggregating-blogs.markdown",
    "content": "---\nheader-id: aggregating-blogs\n---\n\n# Aggregating Blogs\n\n[TOC levels=1-4]\n\nYou can set up a whole web site devoted just to blogging if you wish. The Blogs \nAggregator lets you publish entries from multiple bloggers on one page, giving \nfurther visibility to blog entries. You can add it to a page from the \n*Collaboration* category in the *Add* \n(![Add](../../../../images/icon-add-app.png)) &rarr; *Widgets* menu. \n\nIf you click *Configuration* from the *Options* icon\n(![Options](../../../../images/icon-app-options.png)) in the widget's title bar, \nthe Blogs Aggregator's configuration page appears. The *Setup* tab contains \nthese options: \n\n**Selection Method:** Set how the widget selects blogs for display. You can \nchoose *Users* or *Scope*. If you select Users, the widget aggregates the \nentries of every blogger on your system. To refine the aggregation, you can \nselect an organization by which to filter the users. If you select Scope, the \nwidget aggregates the entries of users in the current scope. This limits the \nentries to members of the site where the widget resides. \n\n**Organization:** The organization whose blogs you want to aggregate.\n\n**Display Style:** Select the overall appearance for blog entries in the widget: \n*Body and Image*, *Body*, *Abstract* (default), *Abstract without Title*, \n*Quote*, *Quote without Title*, and *Title*. \n\n**Maximum Items to Display:** The maximum number of entries the widget displays. \n\n**Enable RSS Subscription:** Whether to enable an RSS feed of the aggregated \nentries. This lets users subscribe to an aggregate feed of all your bloggers. \nBelow this option, you can configure how you want to display the RSS feed: \n\n-   **Maximum Items to Display:** The maximum number of RSS items to display.\n-   **Display Style:** The overall appearance of each entry in the RSS feed: \n    *Abstract*, *Full Content*, or *Title*.\n-   **Format:** The language to use for your RSS feed: *Atom 1.0*, *RSS 1.0*, or \n    *RSS 2.0*. \n-   **Show Tags:** Whether to display each entry's tags. \n\nHere are descriptions for the other tabs in the Blogs Aggregator's \nconfiguration: \n\n**Sharing:** Embed the widget instance as a widget on any website, Facebook, \nNetvibes, or as an OpenSocial Gadget. \n\n**Scope:** Specify the blog instance the widget displays: the current site's \nblog (default), the global blog, or the page's blog. If the page doesn't already \nhave a blog instance, you can select scope option *\\[Page Name\\] \\(Create New\\)* \nto create a page-scoped blog instance. \n\nWhen you finish setting the options, click *Save* and then close the dialog box. \nYou'll notice that the Blogs Aggregator looks very much like the Blogs widget, \nexcept that it shows entries from multiple blogs. \n\n![Figure 1: The Blogs Aggregator lets you display blog entries authored by multiple authors from different sites.](../../../../images/blogs-aggregator.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/02-blogs/08-recent-bloggers.markdown",
    "content": "---\nheader-id: highlighting-recent-bloggers\n---\n\n# Highlighting Recent Bloggers\n\n[TOC levels=1-4]\n\nThe Recent Bloggers widget lets you highlight the work of your site's most \nrecent blog authors. This widget lists each recent author's name, profile \npicture, and number of posts. You can add the Recent Bloggers widget to a page \nfrom the *Collaboration* category in the *Add* \n(![Add](../../../../images/icon-add-app.png)) &rarr; *Widgets* menu. \n\nTo access the widget's configuration options, click *Configuration* from the \n*Options* menu \n(![Options](../../../../images/icon-app-options.png)) in the widget's title bar. \nThe *Setup* tab appears first: \n\n**Selection Method:** Set how the widget selects blogs authors to highlight. You \ncan choose *Users* or *Scope*. If you select Users, the widget aggregates every \nrecent blogger on your system. To refine the aggregation, you can select an \norganization by which to filter the users. If you select Scope, the widget \naggregates the recent bloggers in the current scope. This limits the entries to \nmembers of the site where the widget resides. \n\n**Organization:** The organization whose recent bloggers you want to aggregate. \n\n**Display Style:** Select how the widget displays recent bloggers: \n*User Name and Image*, or *User Name*.\n\n**Maximum Bloggers to Display:** Select the maximum number of recent bloggers \nthe widget displays. \n\nHere are descriptions for the other tabs in the widget's configuration: \n\n**Sharing:** Embed the widget instance as a widget on any website, Facebook, \nNetvibes, or as an OpenSocial Gadget. \n\n**Scope:** Specify the blog instance the widget displays: the current site's \nblog (default), the global blog, or the page's blog. If the page doesn't already \nhave a blog instance, you can select scope option *\\[Page Name\\] \\(Create New\\)* \nto create a page-scoped blog instance. \n\nWhen you're finished setting the options, click *Save*. Then close the dialog\nbox. \n\n![Figure 1: You can show off your site or organization's most recent bloggers from the Recent Bloggers app.](../../../../images/blogs-recent-bloggers.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/03-message-boards/01-intro.markdown",
    "content": "---\nheader-id: creating-forums-with-message-boards\n---\n\n# Creating Forums with Message Boards\n\n[TOC levels=1-4]\n\nAlthough you're likely already familiar with what a modern forum can do, here's\na sampling of what users and administrators can do with @product@'s Message\nBoards app. \n\nUsers can: \n\n-   Start and reply to threads \n-   Mark a thread as a question and select an answer from the replies. \n-   Subscribe to threads \n-   Author posts in BBCode or with the standard WYSIWYG editor \n-   Assign thread priority (e.g., sticky, announcement, etc.)\n-   Attach files to a thread\n-   Rate a thread (e.g., like/dislike)\n-   And more\n\nAdministrators can: \n\n-   Organize threads into categories and subcategories \n-   Scope a message board to a page, a Site, or the entire portal.\n-   Publish threads via RSS\n-   Rank users by the number of messages they post and assign labels to these \n    rankings (e.g., novice, legend, etc.) \n-   Create and modify thread priorities (e.g., sticky, announcement, etc.) \n-   Configure email notifications for thread activity \n-   And more \n\nAs you can see, there's something for everyone! \n\nThe Message Boards app also integrates with the rest of @product@'s features. \nIn many web sites, it's obvious that there's no link between the main Site and \nthe message boards. In some cases, users are even required to register twice: \nonce for the main Site and once for the message boards. Sometimes it's even \nthree times: once for the Site, once for the message boards, and once for the \nshopping cart. By providing a message boards app along with all of the other \napps and widgets, @product@ provides a unique, integrated approach to building \nSites. Administrators can concentrate on building their Site while the \nintegration work is done for them. \n\n![Figure 1: The Message Boards widget lets you explore its categories, interact with message threads, and post new messages.](../../../../images/message-boards-category-threads.png)\n\n![Figure 2: A thread's view displays author information and thread content, for the thread and all replies to the thread.](../../../../images/message-boards-participate-in-threads.png)\n\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/03-message-boards/02-creating-message-boards.markdown",
    "content": "---\nheader-id: creating-message-boards\n---\n\n# Creating Message Boards\n\n[TOC levels=1-4]\n\nYou can create and manage message boards in the Global, Site, and Page scopes. \nRegardless of scope, you manage a message board via the Site Administration \nmenu. The following sections show you how to use this menu to manage message \nboards in each of these scopes. \n\n## Site-scoped Message Boards\n\nBy default, the Message Boards app in Site Administration is scoped to the \ncurrent Site. To administer this message board, open the *Menu* \n(![Menu](../../../../images/icon-menu.png)), expand the menu for your Site, then \nnavigate to *Content & Data* &rarr; *Message Boards*. The Message Boards \nadministration screen then appears. Note that the options available on this \nscreen are the same regardless of scope. The next sections show you how to \nchange scope and then access this screen. \n\n![Figure 1: A Message Board instance starts empty, ready for you to configure for your purposes.](../../../../images/message-boards-administration.png)\n\n## Page-scoped Message Boards\n\nIf you need a page-scoped message board, you must add a Message Boards widget to \nthat page and then set its scope to the page. Follow these steps: \n\n1.  Navigate to the page. \n\n2.  From the *Add* menu \n    (![Add](../../../../images/icon-add-app.png)), open *Widgets* &rarr; \n    *Collaboration*. \n\n3.  Drag a *Message Boards* widget onto the page. \n\n4.  Click the *Options* icon \n    (![Options](../../../../images/icon-app-options.png)) in the widget's title \n    bar and select *Configuration*.\n\n5.  From the *Scope* menu in the *Scope* tab, select the page's name or \n    *PageName (Create New)* if the page scope doesn't exist yet. \n\n6.  Click *Save*, and then close the dialog. \n\nNote that you must still use the Site Administration menu to administer a \npage-scoped Message Boards widget. You do so by setting the Site Administration \nmenu's active scope. Follow these steps to do this: \n\n1.  Open the *Menu* \n    (![Menu](../../../../images/icon-menu.png)), expand the menu for your Site, \n    then expand *Content & Data*. \n\n2.  The current scope appears just below the *Content & Data* heading. \n    *Default Scope* is the current Site. To change this, click the gear icon \n    (![Gear](../../../../images/icon-control-menu-gear.png)) and then select \n    your desired scope. This changes the Site Administration menu to reflect \n    scope you selected. To work in a page's scope, for example, select that \n    page from the gear icon. That page's name then becomes the Site \n    Administration menu's title. \n\n    ![Figure 2: Select the page's scope under the *Content & Data* menu in Site Administration.](../../../../images/mb-site-admin-scope.png)\n\n3.  Select *Message Boards* from the *Content & Data* menu. Any changes you make \n    here apply to the scope that you selected in the previous step. \n\n## Globally-scoped Message Boards\n\nTo manage a message board in the global scope, follow these steps:\n\n1.  Open the *Menu* \n    (![Menu](../../../../images/icon-menu.png)), then click the compass icon \n    (![Compass](../../../../images/icon-compass.png)) on the Site Administration \n    menu. This opens the Select Site dialog. \n\n2.  Select the *My Sites* tab, then select *Global*. This closes the dialog and \n    changes the Site Administration menu's title to *Global*. \n\n3.  Select *Message Boards* from the *Content & Data* menu. Any changes you make \n    here apply to the global scope. \n\n    ![Figure 3: After changing to the global scope, select *Message Boards* from the *Content & Data* menu in Site Administration.](../../../../images/mb-global-scope.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/03-message-boards/03-configuring-message-boards.markdown",
    "content": "---\nheader-id: configuring-message-boards\n---\n\n# Configuring Message Boards\n\n[TOC levels=1-4]\n\nBefore using a message board, configure it to your needs. First, open the\nMessage Boards app in your scope's Site Administration menu, as described \n[earlier](/docs/7-2/user/-/knowledge_base/u/creating-message-boards). \nTo open the message board's configuration screen, click the message board's \n*Options* menu \n(![Options](../../../../images/icon-options.png)) and select *Configuration*. \nThe below sections cover these tabs.\n\n## General Setup\n\nThe *General* tab contains general settings: \n\n**Allow Anonymous Posting:** Choose if users can post anonymously. Use this with\ncaution---anonymous users tend to be mean. \n\n**Subscribe by Default:** Choose if users are subscribed automatically to\nthreads in which they've posted. \n\n**Message Format:** Define the markup language of users' message board posts.\nYou can choose BBCode or HTML. When creating posts, the type of WYSIWYG editor\npresented to users depends on which option is enabled. Both editors have\na *Source* button that lets users view a message's underlying BBCode or HTML.\nUsers can compose messages using either the WYSIWYG or Source view and can\nswitch between views during message composition by clicking the *Source* button.\nFor security reasons, BBCode is preferred. \n\n**Enable Report Inappropriate Content:** Choose if users can report content as\ninappropriate. This sends a message to administrators so they can take action. \n\n**Enable Ratings:** Choose if users can rate posts. \n\n**Thread as Question by Default:** This automatically checks the *Mark as\nquestion* box in the new thread window. Threads marked as questions display\n*waiting for an answer*. Replies to the original message can be marked as an\nanswer. \n\n**Show Recent Posts from Last:** The *Recent Posts* tab shows posts from the\nfollowing timeframes you define here: \n\n-   24 hours\n-   7 days (default) \n-   30 days\n-   365 days \n\nAfter the allotted time has passed, the post expires from *Recent Posts*, but is\nstill accessible everywhere else in the message board. \n\n## Email Setup\n\nUse these tabs to configure how the Message Boards app handles email \nnotifications:\n\n**Email From**: The name and email address that sends email notifications. \nThe default administrator account's name and email address. Default values, \nare from the [`admin.email.from.name` and `admin.email.from.address`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Admin%20Portlet) [portal properties](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)).\nThese were set in the Basic Configuration Wizard when installing @product@.\nMake sure to update this email address to a valid one that can be dedicated to \nnotifications. \n\n**HTML Format:** Support HTML in these emails. \n\n**Definition of Terms:** Shows variables you can use in the email templates\nyou'll define next. \n\n**Message Added Email:** Create a template for email users receive when\na message is added to a topic they subscribe to. \n\n-   **Enabled:** Whether automatic emails are sent to subscribed users.\n-   **Subject:** Choose a prefix for the email's subject line. \n    This lets users set up message filters in their email clients for these \n    notifications. \n-   **Body:** The message body content. Use the variables defined in *Definition\n    of Terms* to customize this content for users. \n-   **Definition of Terms:** Shows variables you can use in the email templates. \n\n**Message Updated Email:** This tab is identical to the Message Added Email \ntab, except it defines the email that users receive when a post is updated. \n\n## Thread Priorities\n\nThe *Thread Priorities* tab defines custom priorities for message threads. This\nlets privileged Roles tag a thread with a certain priority, which highlights it\nfor users. Three priorities are defined by default: \n\n-   Urgent\n-   Sticky\n-   Announcement\n\nTo define a thread priority, enter its name, a URL to its image icon, and a \npriority number. Threads with a higher priority are posted above threads with a \nlower priority. \n\n**Thread Icons**\n\n  &nbsp;**Icon** | **Definition** | \n:-------------------: | :---------------- |\n   ![Urgent](../../../../images/icon-message-boards-urgent.png) |  Urgent |\n   ![Announcement](../../../../images/icon-message-boards-announcement.png) |  Announcement |\n   ![Sticky](../../../../images/icon-message-boards-sticky.png) |  Sticky |\n   ![Question](../../../../images/icon-message-boards-question.png) |  Question |\n\nThe localized language field lets you name the priorities in each locale. You\ncan select the locale, update the priority names for it, and save your updates. \n\n## User Ranks\n\nThe User Ranks tab ranks users by the number of messages they have posted.\nDefault ranks from 0 to 1000 are provided, but you can set custom ranks here as\nwell. \n\nYou can also use this to define message boards labels that appear on user\nprofiles. For example, you can use the message boards label *Moderator* for\nanyone who is a part of any of the Message Boards Administrator groups: the Site\nRole, the Organization, the Organization Role, the regular Role, or the User\nGroup: \n\n    Moderator=organization:Message Boards Administrator\n\n    Moderator=organization-role:Message Boards Administrator\n\n    Moderator=regular-role:Message Boards Administrator\n    \n    Moderator=site-role:Message Boards Administrator\n\n    Moderator=user-group:Message Boards Administrator\n\nAs with thread priority names, the *Localized Language* field localizes rank\nnames. \n\n## RSS\n\nMessage board threads can be published as RSS feeds. The RSS tab\nenables/disables RSS subscriptions and defines how the feeds are generated: \n\n**Maximum Items to Display:** The number of items to display in the feed. \n\n**Display Style:** The feed's appearance. You can publish the full content,\nan abstract, or just the thread title. \n\n**Format:** The feed's format: RSS 1.0, RSS 2.0, or Atom 1.0. \n\nOnce you've finished configuring your message board, make sure to *Save* your\nchanges. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/03-message-boards/04-message-board-permissions.markdown",
    "content": "---\nheader-id: message-board-permissions\n---\n\n# Message Board Permissions\n\n[TOC levels=1-4]\n\nOpen the Message Boards app in your scope's Site Administration menu, as \ndescribed in \n[the article on creating message boards](/docs/7-2/user/-/knowledge_base/u/creating-message-boards). \nThen click the *Options* icon \n(![Options](../../../../images/icon-options.png)) \nand select the *Home Category Permissions* option. This permissions screen is \nfor granting and revoking access to message board functions. \n\nThe permissions enable a Role to perform the following actions: \n\n**Permissions:** View and modify permissions. \n\n**Add File:** Attach a file to a message. \n\n**Ban User:** Forbid a user from participating in the message board. \n\n**Add Category:** Add a new category to the message board. \n\n**Reply to Message:** Respond to an existing message. \n\n**Lock Thread:** Stop any further additions or modifications to a thread's\nmessages. \n\n**Subscribe:** Receive notifications on new and modified posts. \n\n**View:** View all the contents of message threads. \n\n**Add Message:** Post a new thread. \n\n**Move Thread:** Move a thread to a different category or subcategory. \n\n**Update Thread Priority:** Modify a thread's priority. \n\nConfigure the Roles with the permissions you want and *Save* your changes. \n\nAfter adding a Message Boards widget to a page, you can access that widget \ninstance's general permissions. To do so, select the widget's *Options* menu \n(![Options](../../../../images/icon-app-options.png)) and select *Permissions*. \nThis permissions screen lets you control access to the widget instance's \nPermissions, Preferences, and Configuration menus. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/03-message-boards/05-message-board-categories.markdown",
    "content": "---\nheader-id: message-board-categories\n---\n\n# Message Board Categories\n\n[TOC levels=1-4]\n\nMessage Board categories organize threads by topic. This makes it easier to find\nthe right topic for discussion, and can also help discussions stay on topic. For\nexample, a tropical fishkeeping message board may have separate categories for\nfreshwater and saltwater topics. \n\nThis article shows you how to create and manage message board categories.\n\n## Adding Categories\n\nFollow these steps to create a message board category:\n\n1.  Open the Message Boards app in your scope's Site Administration menu, as \n    described in \n    [Creating Message Boards](/docs/7-2/user/-/knowledge_base/u/creating-message-boards).\n\n2.  Click the *Add* icon\n    (![Add](../../../../images/icon-add.png)) and select *Category*. This opens \n    the Add Category form. \n\n    ![Figure 1: You have several options to create a message board category for your needs.](../../../../images/message-boards-add-category.png)\n\n3.  Enter a name and description for the category. \n\n4.  Select the category's *Display Style*. This controls how threads in the \n    category appear. By default, you can choose these display styles: \n\n    **Default:** Classic display style for general purpose discussions. \n\n    **Question:** Threads appear in a question and answer style. \n\n    You can create custom display styles and make them available for selection\n    in this form. You must set the available display styles via the \n    [portal property](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Message Boards Portlet)\n    `message.boards.category.display.styles`. Similarly, you can set the default\n    display style in `message.boards.category.display.styles.default`. \n\n5.  Open the *Mailing List* section of the form and set the mailing list options \n    you want. To enable a mailing list for the category, set the *Active* toggle \n    to *YES*. To enable anonymous emails in the list, set the *Allow Anonymous \n    Emails* toggle to *YES*. The default for both toggles is *NO*. For an \n    explanation of these features, see \n    [the documentation on mailing lists for Message Boards](/docs/7-2/user/-/knowledge_base/u/user-subscriptions-and-mailing-lists#mailing-lists). \n\n6.  Open the *Permissions* section and set the category's permissions. The\n    *Viewable by* selector lets you pick who can view the category: \n\n    -   Anyone (Guest Role)\n    -   Site Members\n    -   Owner\n\n    To show more permissions options, click *More Options*. A table appears with \n    the rest of the category's permissions, which you can assign to the Guest \n    and Site Member roles: \n\n    **Delete:** Remove the category. \n\n    **Permissions:** View and modify permissions. \n\n    **Add File:** Attach a file to any of your messages. \n\n    **Reply to Message:** Respond to existing messages. \n\n    **Lock Thread:** Stop any further additions or modifications to a \n    thread's messages. \n\n    **Update:** Edit the category. \n\n    **Subscribe:** Receive notifications on new and modified posts. \n\n    **View:** View the category. \n\n    **Add Message:** Post a new thread.\n\n    **Move Thread:** Move a thread to a different category or subcategory. \n\n    **Add Subcategory:** Add a new category within this category. \n\n    **Update Thread Priority:** Modify a thread's priority. \n\n    Note that after creating a category, you can revisit its permission options \n    by clicking the category's *Actions* icon\n    (![Actions](../../../../images/icon-actions.png)) and selecting \n    *Permissions*. \n\n7.  Click *Save* when you're finished. Your category now appears in the table. \n\nAs you add categories to a message board, they appear on the message board's \nhome screen. The list displays the category names and the numbers of \nsubcategories, threads, and posts in each one. \n\n![Figure 2: Categories help you organize threads so users can find topical threads that interest them.](../../../../images/message-boards-home.png)\n\n## Adding Subcategories\n\nCategories can contain as many subcategories as you like. If, however, you nest\ncategories too deep, users can have trouble finding them. \n\nFollow these steps to add a subcategory to a category:\n\n1.  Click the category's name in the list, then click the *Add* icon \n    (![Add](../../../../images/icon-add.png)) and select *Category*. The Add \n    Category form appears. \n\n2.  Fill out the Add Category form with the values and settings you want to use \n    for the subcategory. This form is populated with the parent category's \n    properties by default. \n\n3.  Click *Save* when you're finished. Your subcategory now appears in the \n    table. \n\n## Moving and Merging Categories\n\nEach category can have any number of threads, and you can add as many categories\nand subcategories as you wish. You can also move and merge categories. \n\nFollow these steps to move a category or merge it with another: \n\n1.  Click the category's *Actions* icon\n    (![Actions](../../../../images/icon-actions.png)) and select *Move*. \n    This brings up the Move Category form. \n\n2.  Select a new parent category via the *Select* button under the *Parent \n    Category* field. Note that this field is empty for top-level categories. \n\n3.  If you want to merge the category with the selected parent category, select \n    *Merge with Parent Category*. \n\n4.  Click *Move*. \n\n![Figure 3: The Move Category form lets you move and merge categories.](../../../../images/mb-move-merge.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/03-message-boards/06-mb-subscriptions-lists.markdown",
    "content": "---\nheader-id: user-subscriptions-and-mailing-lists\n---\n\n# User Subscriptions and Mailing Lists\n\n[TOC levels=1-4]\n\nThe Message Boards app notifies users of message boards activity via email in \ntwo ways:\n\n-   User subscriptions\n-   Mailing lists\n\n| **Note:** Since multiple sites can use a globally scoped message board, such \n| message boards don't support user subscriptions or mailing lists. Make sure to \n| use a site-scoped or page-scoped message board if you need user subscriptions \n| or a mailing list. \n\n## User Subscriptions\n\nIn the user subscriptions mechanism, the Message Boards app uses its configured \n*Email From* address to send email notifications to subscribed users. The app \ncan also import email replies to message board notifications directly into the\nmessage board. Then, users can interact on the message board via email without\nlogging in to view the message board directly. This is disabled by default. To\nenable it, add the following line to your `portal-ext.properties` file: \n\n    pop.server.notifications.enabled=true\n\nThe user subscription mechanism uses the POP mail protocol. When the Message \nBoards app receives an email reply to a message board notification, it posts \nthat reply to the message board and then deletes it from the mail server. \nDeleting the message from the mail server is the POP protocol's default behavior \nand the Message Boards app assumes that your POP mail server behaves this way. \nMost POP clients offer an option to leave mail on the mail server after it \ndownloads, but you shouldn't exercise this option. If you configure mail to be \nleft on the mail server, the Message Boards app sends copies of each retained \nmessage along with each new email notification it sends to subscribed users. \n\nWhen enabling Message Boards to import replies to email notifications, you must \ndecide whether to handle notifications with a mail server subdomain. By default, \nthe following property setting is specified in the portal properties: \n\n    pop.server.subdomain=events\n\nThis property creates a special MX (mail exchange) subdomain to receive all\nvirtual instance related email (e.g., events.liferay.com). If you don't want to\nuse this approach, unset this value in a `portal-ext.properties` file: \n\n    pop.server.subdomain= \n\nDoing so tells Message Boards to use the *Email From* address specified in the \nMessage Board's configuration to receive message board notification email \nreplies. For example, the *Email From* address could be set to \n*replies\\@liferay.com*. \n\nIf you're not using a mail subdomain, Message Boards parses the message headers \nof emails from the *Email From* address to determine the message board category \nand message ID. If you keep the `pop.server.subdomain=events` default, the email \nnotification address takes the following form:\n\n    mb.[category_id][message_id]@events.liferay.com\n\nIn this case, Message Boards parses the email address to find the category and \nmessage ID. Parsing the email address is safer than parsing message headers, \nsince different email clients treat message headers differently. This is why the \n`events` subdomain is enabled by default. \n\nYou can also configure the interval on which the `POPNotificationListener` runs. \nThe value is set in one minute increments. The default setting is to check for \nnew mail every minute, but you can set it to whatever you like: \n\n    pop.server.notifications.interval=1\n\n| **Note**: Depending on your mail provider, if you use multiple devices to \n| access email through POP, you might need to configure in your POP settings \n| something like Gmail's *recent mode*, which keeps the last 30 days of email\n| available on the server. Then, more than just the first client can receive \n| email. To enable recent mode in Gmail, for example, prefix the value of \n| your POP client's Username or Email field with `recent:`. \n| \n| If you don't use Gmail, IMAP may be a better solution for you. \n\n## Mailing Lists\n\nAlternatively, the Message Boards app can use mailing lists to send email \nnotifications. Any category in a message board can have its own mailing list. \nThe mailing list mechanism, unlike the user subscription mechanism, supports \nboth the POP and the IMAP protocols. POP is the default, but each message \nboard's mailing list is configured independently. If you choose the IMAP \nprotocol for a category's mailing list, make sure to configure the IMAP inbox to \ndelete messages as they are pulled by the email client that sends messages to \nthe users on the mailing list. Otherwise, each email message retained on the \nserver is sent to the mailing list each time there's a new post or update in the \ncategory. \n\nWhen a mailing list is enabled for a message board category, Message Boards \nlistens to the specific email inbox that's configured for the mailing list. \nEnabling the mailing list function lets users on the mailing list reply to the \nnotification messages in their email clients. Message Boards pulls the messages \nfrom the email inbox it's configured to listen to and automatically copies those \nreplies to the appropriate message board thread. \n\nTo enable the mailing list functionality for a category, follow these steps:\n\n1.  Set up a dedicated email address for the category. \n\n2.  Click the category's *Actions* icon\n    (![Actions](../../../../images/icon-actions.png)) and select *Edit*. \n\n3.  In the *Mailing List* section of the form, set the *Active* slider to *YES*. \n    Several options then appear. Fill these out as follows: \n\n    **Email Address:** The email address of the account that receives the \n    messages. \n\n    **Protocol:** Select POP or IMAP.\n\n    **Server Name:** Your mail server's host name. \n\n    **Server Port:** The port on which your mail service is running.\n\n    **Use a Secure Network Connection:** Whether to use an encrypted \n    connection if your server supports it. \n\n    **User Name:** The login name on the mail server. \n\n    **Password:** The password for the account on the server. \n\n    **Read Interval (Minutes):** How often to poll the server looking for new\n    messages to post. \n\n    **Email Address (Outgoing):** The email address originating messages from\n    this category. If you want your users to be able to reply to the categories\n    using email, this should be the same address as the incoming email address. \n\n    **Use Custom Outgoing Server:** Use a different mail server than global\n    default. Fields appear for configuring the server's name, port, user name,\n    password, and secure connection. \n\n4.  If you want to let emails from anonymous users post to the message board \n    category, set the *Allow Anonymous Emails* toggle to *YES*. \n\n5.  Click *Save* when you're finished. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/03-message-boards/07-using-message-boards.markdown",
    "content": "---\nheader-id: using-the-message-boards\n---\n\n# Using the Message Boards\n\n[TOC levels=1-4]\n\nYou can add a Message Boards widget to a page from the *Add* \n(![Add](../../../../images/icon-add-app.png)) menu's *Widgets* &rarr; \n*Collaboration* section. The Message Boards interface is similar to other \nmessage boards that populate the Internet. In any case, it can't hurt to explore \nhow to use @product@'s Message Boards and discover its features. \n\n![Figure 1: The Message Boards widget lets you explore its categories, interact with message threads, and post new messages.](../../../../images/message-boards-category-threads.png)\n\nThreads can be viewed many ways. At the top of the widget is a set of tabs: \n\n**Categories:** The message board's categories. \n\n**Recent Posts:** Posts from all categories, sorted by date.\n\n**My Posts:** The current user's posts. \n\n**My Subscriptions:** Lets users view and manage their thread subscriptions. \n\n**Statistics:** The number of categories, posts, participants, and a list of the \ntop contributors. You can also access this from the same tab in Site \nAdministration's Message Board app. \n\nYou can also use the search bar at the top of the widget to search for threads \nand posts. Although search works on threads and posts within categories, it \ndoesn't work on categories themselves. \n\n## Posting New Threads\n\nFollow these steps to post a new thread: \n\n1.  Click the Message Boards widget's *New Thread* button. Alternatively, click \n    the *Add* button \n    (![Add](../../../../images/icon-add.png)) and select *Thread* in the Message \n    Boards app in Site Administration. Either way, the same *Add Message* form \n    appears. \n\n    ![Figure 2: The Add Message form lets you create a new thread.](../../../../images/message-boards-add-thread.png)\n\n2.  Give your thread a title in the *Subject* field. \n\n3.  Create your thread's content in the *Body* field. This field uses the same \n    editor as the Blogs app, except that it uses BBCode instead of HTML. For \n    further instructions, see the documentation on \n    [using the editor](/docs/7-2/user/-/knowledge_base/u/using-the-blog-entry-editor). \n    Also note that you can \n    [mention](/docs/7-2/user/-/knowledge_base/u/mentioning-users) \n    other users by entering the `@` character and their user name. \n\n4.  If you want to add attachments, open the *Attachments* section and add them \n    via drag and drop or the *Select Files* button. \n\n5.  If you want to associate a tag with the message, open the *Categorization* \n    section and use the *Select* button to select an existing tag. You can also \n    create a new tag by entering it in the *Tags* field and clicking *Add*. See \n    [the documentation on tags](/docs/7-2/user/-/knowledge_base/u/tagging-content) \n    for more information. \n\n6.  If you want to select an existing asset in the portal (e.g., a media file, \n    blog post, etc.) to relate to your thread, open the *Related Assets* section \n    and use the *Select* button to select that asset. \n\n7.  Open the *More Settings* section and select the settings you want to use: \n\n    **Mark as a Question:** Whether to mark this thread as a question. This lets \n    you later select a post in the thread as the answer. \n\n    **Anonymous:** Whether this thread is posted anonymously. \n\n    **Subscribe Me:** Receive notifications for activity on the thread. \n\n    **Priority:** The thread's priority in the Message Board. By default, you \n    can choose *Urgent*, *Sticky*, or *Announcement*. Additional priorities can \n    also be \n    [configured](/docs/7-2/user/-/knowledge_base/u/configuring-message-boards) \n    in the Message Boards app in Site Administration. \n\n    **Allow Pingbacks:** Whether \n    [pingbacks](https://en.wikipedia.org/wiki/Pingback) \n    are allowed for your thread. \n\n8.  Open the *Permissions* section and set the thread's permissions. Possible \n    values in the *Viewable by* selector are\n\n    -   Anyone (Guest Role)\n    -   Site Members\n    -   Owner\n\n    You can also click the *More Options* link to select additional permissions: \n\n    **Delete:** Remove the thread. \n\n    **Permissions:** Grant/revoke thread permissions. \n\n    **Update:** Edit the thread. \n\n    **Subscribe:** Receive notifications for thread activity. \n\n    **View:** View the thread. \n\n    Note that you can revisit the thread's permissions after posting it. To do \n    so, select the thread's Actions menu \n    (![Add](../../../../images/icon-actions.png)) and select *Permissions*. \n\n9.  Click *Publish* to publish the thread. Once it's published, it appears \n    along with the other threads in the category. \n\n## Participating in Message Board Threads\n\nTo find message board threads that interest you, browse a message board's\nCategories or Recent Posts tabs. In the Categories tab, you can view a \ncategory's thread listing by clicking the category's name. Within a category, \nyou can subscribe to an RSS feed and/or emails that inform you about activity in \nthat category. The Recent Posts tab also lists threads, except they're the \nlatest threads across all categories. \n\nClick a thread to view it. Messages appear in a threaded view so that replies\nare aligned under their parent thread. This makes it easy to follow\nconversations. Thread replies are indented under their parent thread. \n\n![Figure 3: A thread's view displays author information and thread content, for the thread and all replies to the thread.](../../../../images/message-boards-participate-in-threads.png)\n\nSubscribing to a thread causes Message Boards to send the user an email whenever\na new message is posted to the thread. If you have enabled the mailing list\nfeature for the thread's category, users can reply to these messages to post\nback to the thread without having to visit your site.\n\nMost threads get more interesting as users reply to them. Follow these steps to \nreply to a message in a thread:\n\n1.  Click the *Reply* button. This opens the quick reply form, which only \n    contains a text field for entering your reply. \n\n2.  Enter your reply in the text field. To access more options for your reply, \n    click the *Advanced Reply* link. This opens the full editor from the \n    add/edit thread form. \n\n3.  Click *Publish* to publish your reply. \n\nIn addition to replying to a message, you can rate it or flag it as \nobjectionable. A message board moderator can evaluate flagged messages and \ndecide how to handle the messages and their authors. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/03-message-boards/08-managing-message-boards.markdown",
    "content": "---\nheader-id: managing-message-boards\n---\n\n# Managing Message Boards\n\n[TOC levels=1-4]\n\nMessage boards can become unwieldy if left unmanaged. The Message Boards in Site \nAdministration facilitates day-to-day thread administration. You may wish to \nassign this function to a \n[Role](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions) \nthat you give to one or more users. This frees you to concentrate on other areas \nof your Site. For example, you can create a Role called \n*Message Board Administrator* scoped to the portal (globally), an Organization,\nor a Site. Members of a global Role can administer Message Boards throughout the\nportal. Members of an Organization or Site-scoped Role can only administer\nMessage Boards in that Organization or Site, respectively. \n\nFollow these steps to create a global Role:\n\n1.  In the Control Panel, select *Users* &rarr; *Roles*. \n\n2.  Select or create the Role.\n\n3.  Select the Role's *Define Permissions* tab and navigate to \n    *Site Administration* &rarr; *Content & Data* &rarr; *Message Boards*. \n    A screen appears that lets you configure Message Board permissions. \n\n    ![Figure 1: Define the permissions you want to use for the message boards administrators.](../../../../images/message-boards-role-permissions.png)\n\n4.  Select the permissions you want message board administrators to have, then \n    click *Save*. \n\n5.  Add users to this Role. \n\n## Locking Threads\n\nYou may encounter threads that you think should be preserved, but stopped. You\ncan halt activity on a thread by selecting *Lock* from the thread's *Actions* \nmenu \n(![Actions](../../../../images/icon-actions.png)). \n\n## Moving Threads\n\nIf someone posts a thread to the wrong category, you can move it to the proper \none. Follow these steps:\n\n1.  Select *Move* from the thread's *Actions* menu \n    (![Actions](../../../../images/icon-actions.png)). This opens the \n    *Move Thread* form. \n\n2.  Click the *Select* button and select the new category. \n\n3.  If you want to add a post explaining the move, select \n    *Add explanation post*. \n\n4.  Click *Move* to move the thread. \n\n## Deleting Threads\n\nSometimes users begin discussing topics that are inappropriate or that reveal\nconfidential information. In this case, administrators can delete the thread \nfrom the message boards. To do so, select *Move to Recycle Bin* from the \nthread's *Actions* menu \n(![Actions](../../../../images/icon-actions.png)). \n\n## Banning Users\n\nUnfortunately, message board users can be abusive. In this case, you can ban the \nuser from the message boards. While viewing any of the user's posts in any \nthread, select the post's *Actions* menu \n(![Actions](../../../../images/icon-actions.png)) and select *Ban this User*. \n\nTo reinstate a banned user, you must use the Message Boards app in Site \nAdministration. Navigate to this app and select the *Banned Users* tab. Select \nthe user's *Actions* menu \n(![Actions](../../../../images/icon-actions.png)) and select *Unban this User*. \n\n## Splitting Threads\n\nSometimes a thread goes on for a while and the discussion completely changes\ninto something else. In this case, you can split the thread where the discussion\ndiverged and create a whole new thread for the new topic. To split a thread at\na certain post, administrators can select that post's *Actions* menu\n(![Actions](../../../../images/icon-actions.png)) and select *Split Thread*.\nThis brings up a form that lets you add an explanation post to the split thread.\nClick *OK* to split the thread. \n\n## Editing Posts\n\nAdministrative users can edit anyone's posts, not just their own. Sometimes \nusers post links to copyrighted material or unsuitable pictures. By editing\nthese posts, you can redact information that shouldn't be posted, or remove\ncontent not conforming to your terms of use. You can also update the thread's\npriority or mark a reply as an answer to a thread's question. \n\nTo edit a post, select its *Actions* menu \n(![Actions](../../../../images/icon-actions.png)) and select *Edit*. \n\n## Post Permissions\n\nPermissions can be set not only on threads, but also on individual posts. You\ncan choose to limit a particular conversation or post to only a select group of \nusers. To do this, select the post's *Actions* menu \n(![Actions](../../../../images/icon-actions.png)) and select *Permissions*. You \ncan then choose which Roles have the following permissions: \n\n-   Delete\n-   Permissions\n-   Subscribe\n-   Update\n-   View \n\nUse this, for example, to let some privileged users post on a certain thread,\nwhile others are only allowed to view it. Other combinations of these\npermissions are also possible. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/04-mentions/01-mentions-intro.markdown",
    "content": "---\nheader-id: mentioning-users\n---\n\n# Mentioning Users\n\n[TOC levels=1-4]\n\nHave you ever wanted to include another user in a discussion on the Message\nBoards? Have you ever wanted to give kudos to a colleague in content you're\nwriting? You can mention (notify and/or draw attention to) other users by \nentering the `@` character in front of each user's user name. \n\nWhen you mention a user, the user receives a site notification next to the\nuser's profile icon and an email, alerting the user with a link to the content.\nYou can mention users in a blog entry, a message boards post, or comments in any \napp that supports comments. A mention also links to the user's home page, so \nreaders can find out more about that user. \n\n![Figure 1: As you enter a user name after the `@` character, links to users that match the text you enter are displayed. Select the user you want to mention and publish your content.](../../../../images/mentions-at-mention-menu.png)\n\nA selector appears after entering the `@` character, listing users that match \nthe name you're entering. In the selector, users are represented by their \nprofile picture, name, and user name. Click the user you want to mention and \nfinish editing your content. \n\nOn publishing the content, mentioned users receive a notification and an email \ninforming them that they've been mentioned. The notification and email indicate \nthe author's name and content type, and contain links to the content. You can \naccess your notifications by selecting *Notifications* from your user menu. \n\n![Figure 2: Your notifications are accessible from your user menu and appear in a list.](../../../../images/mentions-notification-list.png)\n\nMentions are enabled by the Mentions app, which is a part of the Collaboration \nSuite. By default, the Mentions app is enabled globally. However, you can \nenable/disable it globally or per site. For a site to use Mentions, it must be \nenabled for the site's Virtual Instance. \n\nTo access the global Mentions settings for your Virtual Instance, first open the \n*Menu* \n(![Menu](../../../../images/icon-menu.png)) and navigate to *Control Panel* &rarr; \n*Configuration* &rarr; *Instance Settings*. Then click *Community Tools* and \nselect *Mentions* under *VIRTUAL INSTANCE SCOPE*. By default, all users are \nallowed to mention fellow site members and friends. To fine tune these options, \nselect *Define Mentions Capability for Users* and specify the settings you want. \n\n![Figure 3: You can enable or disable the Mentions feature for all of the Virtual Instance's sites.](../../../../images/mentions-global-instance-setting.png)\n\nFor Mentions to be available for a site, the app must be enabled for that site's \nVirtual Instance. Site administrators can enable or disable Mentions for a site. \nA site's Mentions app configuration is accessible from within the *Menu* \n(![Menu](../../../../images/icon-menu.png)). Once in the menu, navigate to \n*[Site Name]* &rarr; *Configuration* &rarr; *Settings*. In the *Social* tab, \nexpand the *Mentions* section. Enable or disable mentions via the toggle labeled \n*Allow Users to Mention Other Users*. \n\n![Figure 4: Mentions can also be enabled or disabled per site.](../../../../images/mentions-site-setting.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/05-wiki/01-intro.markdown",
    "content": "---\nheader-id: working-together-with-the-wiki\n---\n\n# Working Together with the Wiki\n\n[TOC levels=1-4]\n\nWikis are for collaboratively building a collection of information. The most\nfamous wiki on the planet is Wikipedia. It's a full encyclopedia developed\ncollaboratively by users from all over the world, using a wiki. @product@'s wiki\ndoes these things: \n\n-   Creates multiple wikis in a single wiki app instance. \n-   Scopes wikis to a page, a site, or the entire portal. \n-   Creates and edit wikis in \n    [WikiCreole syntax](http://www.wikicreole.org/). \n-   Attaches files to wiki articles. \n-   Associates wiki articles with other assets in the portal. \n-   And more.\n\nAs you can see, @product@'s wiki is flexible and can be configured to fit \nnearly any use case. What's more, it's completely integrated with the portal's \n[user management](/docs/7-2/user/-/knowledge_base/u/managing-users), \n[tagging](/docs/7-2/user/-/knowledge_base/u/tagging-content), and\n[security](/docs/7-2/deploy/-/knowledge_base/d/securing-product) \nfeatures. \n\n![Figure 1: The Wiki widget displays your wiki on a Site page.](../../../../images/wiki-page-full.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/05-wiki/02-getting-started-wiki.markdown",
    "content": "---\nheader-id: getting-started-with-wikis\n---\n\n# Getting Started with Wikis\n\n[TOC levels=1-4]\n\nThe Menu (![Menu](../../../../images/icon-menu.png)) \nis the best place to start working with your wikis. Click the *Menu* \n(![Menu](../../../../images/icon-menu.png)), navigate to your site, and select \nthe *Content & Data* section. If you're updating an existing page-scoped wiki \napp instance, you can select that page scope from the scope menu the Gear icon \n(![Gear](../../../../images/icon-control-menu-gear.png)) \nmakes available. The site's wiki app instance is available in the Default scope. \nOnce you're in the proper content scope, click *Wiki*. The Wiki administration \nscreen lets you add, modify, and delete wiki nodes. A Wiki app instance can \ncontain many wiki nodes. By default, it contains one node: *Main*. \n\n![Figure 1: The Wiki app instance has a wiki node named *Main* with a single front page. You can build on the Main node or click the Add icon to create a new node.](../../../../images/wiki-admin-empty.png)\n\n## Configuring Wikis\n\nBefore adding to your wiki instance, you should configure it. The instance's \ninterfaces for permissions, export and import, configuration, and application \ntemplates are accessible from the Options menu. Click the\n*Options* icon \n(![Options](../../../../images/icon-options.png)) to open this menu.\n\nThe following options are available in this menu: \n\n**Wikis Permissions**: Specify which\n[Roles](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions) can create\nwiki nodes and access the Wikis Permissions screen. For example, if you've\ncreated a specific Role for creating wiki nodes and want to enable that Role to\ncreate new wiki nodes in this wiki application instance, select the Role's\ncheck box in the *Add Node* column and then click *Save*. \n\n**Export / Import**: Import existing wiki content into your wiki app instance,\nor export wiki content to a file. For details, refer to \n[Importing/Exporting Pages and Content](/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content).\n\n**Configuration**: Configure email notifications and RSS feeds. The *Email\nFrom*, *Page Added Email*, and *Page Updated Email* tabs are similar to other\napps' notification email settings tabs; they customize who wiki emails come\nfrom and the format and text of the email sent when a page is added or updated.\nThe *RSS* tab lets you configure RSS feeds. \n\n## Adding Wikis\n\nFollow these steps to create a new wiki node: \n\n1.  Click the *Add* icon \n    (![Add](../../../../images/icon-add.png)) to start creating a new wiki node. \n    The *New Wiki Node* form appears. \n\n2.  Add a name and description for the wiki node. \n\n3.  Open the form's *Permissions* section and define the wiki node's \n    permissions. You can select the following permissions in the *Viewable\n    by* menu:\n\n    -   Anyone (Guest Role)\n    -   Site Members\n    -   Owner\n\n    You can also click the *More Options* link to assign permissions to specific \n    Roles. \n\n4.  Click *Save* when you're done creating the wiki node. \n\n![Figure 2: The New Wiki Node form lets you describe your new node, set view permissions, and set permissions for the Guest and Site Member roles.](../../../../images/wiki-new-wiki-node.png)\n\n## Wiki Node Options\n\nNext to each listed wiki node is an *Actions* menu \n(![Actions](../../../../images/icon-actions.png)). Here are the actions available \nin this menu: \n\n**Edit**: Edit the wiki's name and description.\n\n**Permissions**: Specify which roles can add attachments to wiki pages, add\npages, delete pages, import pages, set permissions on the wiki node, subscribe\nto modifications, update existing pages, and view the wiki node. \n\n**Import Pages**: Import data from other wikis. This lets you migrate from\nanother wiki application to the @product@ wiki. You might want to do this if\nyou're migrating your site from a set of disparate applications (i.e.,\na separate forum, a separate wiki, a separate content management system) to\n@product@, which provides all of these features. Currently, MediaWiki is the\nonly supported wiki.\n\n**RSS**: Subscribe to an RSS feed using Live Bookmarks, Yahoo, Microsoft\nOutlook, or an application on your machine.\n\n**Subscribe**: Subscribe to a wiki node. Any time a wiki page is added or\nupdated, the portal sends you an email notification. \n\n**View Removed Attachments**: Display attachments that have been removed from\nthe wiki node. \n\n**Move to Recycle Bin**: Moves the wiki node to the \n[Recycle Bin](/docs/7-2/user/-/knowledge_base/u/restoring-deleted-assets). \n\n![Figure 3: Each wiki node's Actions menu lists actions you can perform.](../../../../images/wiki-options.png)\n\nBefore opening wiki nodes to contributors, you should consider whether to \nassociate a workflow with them. For example, you could create a workflow that\nrequires an administrator's approval to publish a wiki page modification (add,\nupdate, or delete). You can access your site's default *Wiki Page* workflow from\nwithin the Site Administration Menu, by navigating to *Configuration* &rarr; \n*Workflow* for your site. To learn how to use workflow, see the\n[Workflow](/docs/7-2/user/-/knowledge_base/u/workflow) section. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/05-wiki/03-add-edit-wiki.markdown",
    "content": "---\nheader-id: adding-and-editing-wiki-pages\n---\n\n# Adding and Editing Wiki Pages\n\n[TOC levels=1-4]\n\nWiki nodes initially have no pages. When you navigate into a node for the first \ntime, a default page called *FrontPage* is created automatically. To view the \npage, click the wiki node's name and then click *FrontPage*. The FrontPage \nappears and shows a message that explains the page is empty and needs you to add \ncontent. That message is a link; click it to start editing the page. The wiki \npage editing form then appears. \n\n| **Note:** See the \n| [getting started article](/docs/7-2/user/-/knowledge_base/u/getting-started-with-wikis) \n| for instructions on accessing your wiki nodes. \n\n![Figure 1: Each empty wiki page presents a default message link you can click to edit the page.](../../../../images/wiki-empty-frontpage.png)\n\n![Figure 2: The wiki page editing form lets you create and edit your page's content.](../../../../images/wiki-page-editor.png)\n\nFollow these steps to use the wiki page editing form: \n\n1.  Enter your content in the field that contains the text *Write your content\n    here...*. This is a rich-text, WYSIWYG editor that is almost identical to\n    the one used in the Blogs app. The only difference is that the wiki editor\n    uses Creole instead of HTML as its source. Click the link *Show Syntax\n    Help* if you need help with Creole syntax (e.g., syntax for text styling,\n    header formatting, link creation, etc.). For a detailed explanation of the\n    rest of the editor, see the \n    [Blogs documentation](/docs/7-2/user/-/knowledge_base/u/using-the-blog-entry-editor). \n\n2.  If you want to attach files to the page, open the *Attachments* section of \n    the form and add them via drag and drop or the *Select Files* button. \n\n3.  If you want to associate a tag with the page, open the *Categorization* \n    section and enter a new or existing tag in the *Tags* field. See \n    [the documentation on tags](/docs/7-2/user/-/knowledge_base/u/tagging-content) \n    for more information. \n\n4.  If you want to select an existing asset in the portal (e.g., a media file, \n    blog post, etc.) to relate to the page, open the *Related Assets* section \n    and use the *Select* button to select that asset. \n\n5.  In the form's *Configuration* section, you can set the page to use Creole \n    (default), plain text, or HTML. We recommend that you stick with the Creole \n    format, as it allows for a much cleaner separation of content and code. You \n    can also use the Configuration section to summarize your edit, and specify \n    whether it's a minor edit. \n\n6.  Click *Publish* to publish the page when you're done editing it. \n\nAs is common with wikis in general, if you link to a page that doesn't exist, \nclicking that link opens the new page form with a note stating that the page \ndoesn't exist and that you are creating it. \n\n| **Note**: When you create a page by clicking a link to a page that doesn't \n| exist, the new page is **not** a child of the current page. The page is \n| created at the wiki node's root. From Wiki in Site Administration, you can use \n| the page's Move action to assign it a new parent page. Clicking the Move \n| action brings up a window that lets you select a new parent for the wiki page. \n\nReturn to the wiki node view to see a list of the node's top-level pages. If you \nnavigate to a page that has child pages, its child pages are listed. In these \npage listings, each page's Actions menu \n(![Actions](../../../../images/icon-actions.png)) lists the following actions \nyou can take on the page: \n\n**Edit**: Opens the page in the page editor.\n\n**Permissions**: Lets you determine which roles can view, update, delete,\nsubscribe to, or set permissions on the page, and add, update, or delete page\ndiscussions (comments).\n\n**Copy**: Opens a page editor window with all the content from the source wiki\npage. You're prompted to specify a new title for it. \n\n**Move**: Opens a dialog that lets you rename the page or assign the page to\na new parent page within the wiki node. \n\n**Add Child Page**: Create a new child page of the wiki page. \n\n**Subscribe (or Unsubscribe)**: Subscribes you to (or unsubscribes you from)\nnotifications for the wiki page's modifications. \n\n**Print**: Print the wiki page. \n\n**Move to Recycle Bin**: Moves the wiki page to the Recycle Bin.\n\nEach wiki page has a check box next to it. When you select a page's check box, \nthe Management Bar changes to show an Info icon \n(![Info](../../../../images/icon-information.png)) and Recycle Bin icon \n(![Recycle Bin](../../../../images/icon-trash.png)). To move the selected page \nto the Recycle Bin, click the Recycle Bin icon. To get additional information \nabout the page via an info panel, click the Info icon. The info panel has a star \nicon that you can select to subscribe to the page's modifications. The info \npanel's Details section displays the page's summary, format, version, creation \nand modification dates, number of attachments, and RSS link. \n\nThere are several more features in the wiki node view's Management Bar. The \n*Filter and Order* menu orders the pages by title or modification date \nand filters them by page type. The arrows button sorts the pages in \nascending or descending order. The search bar searches for pages. \n\nThe *View Types* button is next to the Info icon. It lets you choose how to \ndisplay the pages. The View Types button's icon depends on the selected view \ntype: \n\n**List** (![List](../../../../images/icon-view-type-list.png)): Shows the pages\nin a list with an icon representing each page. Each page's entry contains the\nname of its author, when it was last modified, and its\n[workflow](/docs/7-2/user/-/knowledge_base/u/workflow) status (e.g.,\nApproved, Draft, etc.). \n\n**Table** (![Table](../../../../images/icon-view-type-table.png)): Shows the\nsame information as the List view type, in a smaller list with no page icon.\nAlso, the page's information is in columns and includes the revision number. \n\n![Figure 3: The wiki node's view in site administration has features that help you access and learn information about a wiki node's pages.](../../../../images/wiki-node-view-in-admin.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/05-wiki/04-using-wiki-site-pages.markdown",
    "content": "---\nheader-id: using-the-wiki-on-site-pages\n---\n\n# Using the Wiki on Site Pages\n\n[TOC levels=1-4]\n\nYou can use the Wiki on Site pages via the Wiki widget. Follow these steps to \nadd the Wiki widget to a page:\n\n1.  Navigate to the page where you want to place a wiki. \n\n2.  From the *Add* \n    (![Add](../../../../images/icon-add-app.png)) menu, open *Widgets* \n    &rarr; *Wiki* and add a *Wiki* to the page. \n\nYour Site's wiki nodes appear in tabs across the top of the widget. \n\n![Figure 1: Users can interact with your Wiki nodes when you add the Wiki widget to a page.](../../../../images/wiki-page-full.png)\n\nTo view the Wiki widget's configuration options, click its *Options* icon \n(![Options](../../../../images/icon-app-options.png)) and select *Configuration*. \nThe Configuration screen appears with these tabs:\n\n**Setup**: Lets you choose wikis to display and gives you several options for\ndisplaying them. The *Show Related Assets*, *Enable Page Ratings*, *Enable\nComments*, *Enable Ratings for Comments*, and *Enable Highlighting* check boxes\nenable or disable those features for the Wiki. You can set how you want users\nto interact with wiki documents. The *Display Template* selector menu lets you\nchoose the Wiki's \n[Widget Template](/docs/7-2/user/-/knowledge_base/u/styling-widgets-with-widget-templates).\nBelow this, you can set which wiki nodes are visible. For example, you might\nhost two wikis on a given site, exposing one to the public and keeping the\nother private for site members.\n\n**Communication**: Configure communication across portlets, using predefined\npublic render parameters. From here you can modify six public render\nparameters: `categoryId`, `nodeId`, `nodeName`, `resetCur`, `tag`, and `title`.\nYou can perform these actions on each parameter:\n\n-   Ignore the values for this parameter that come from other portlets. For\n    example, the wiki can be used along with the tags navigation app. When\n    a user clicks on a tag in tags navigation, the wiki shows a list of\n    pages with that tag. In some cases, an administrator may want the wiki\n    to show the front page always, independently of any tag navigation done\n    through other portlets. This can be achieved by selecting *Ignore*, so\n    that the values of the parameter coming from those other portlets are\n    ignored.\n\n-   Read the value of a parameter from another app. This is an advanced but \n    very powerful option that lets portlets communicate without prior\n    configuration. For example, imagine that the wiki is used to publish \n    information about certain countries, and a custom app that allows browsing\n    countries for administrative reasons was written and placed on the same\n    page. You could associate to this second app a public render parameter\n    called *country* to designate the name of the country. Using this\n    procedure, you can cause the wiki to show the information from the country\n    being browsed in the other app. You can do this here for the wiki by\n    setting the value for the title parameter to be read from the country\n    parameter of the other app.\n\n**Sharing**: Displays options you're likely to be familiar with such as the\nsections for sharing the Wiki with websites, Facebook, and NetVibes. \n\n**Scope**: Set the wiki's scope. You can select the site-scoped or\nglobal-scoped instance, or select/create an instance for the page. If the page\ndoesn't already have an instance scoped to it, you can click the *[page name]\n(Create New)* menu option to create a page-scoped wiki instance.\n\nOnce you set the wiki's configuration options the way you want them, click \n*Save*. \n\n![Figure 2: Here the user has selected to create a new Wiki instance scoped to the current page named *Welcome*](../../../../images/wiki-app-configuration-scope.png)\n\nThe Wiki's Options menu also contains the usual widget options: \n\n**Look and Feel Configuration:** Set the widget's [look and\nfeel](/docs/7-2/user/-/knowledge_base/u/look-and-feel-configuration).\n\n**Export/Import:** [Export or import widget data](/docs/7-2/user/-/knowledge_base/u/exporting-importing-widget-data).\n\n**Permissions:** Set the widget's permissions.\n\n**Configuration Templates:** Use \n[configuration templates](/docs/7-2/user/-/knowledge_base/u/configuration-templates) to\nstore the widget's current setup or apply an existing archived setup.\n\n**Remove:** Remove the widget from the page. \n\nThe Wiki displays links to all of the Wiki instance's nodes, and provides links \nfor navigating around the wiki. Click on a wiki node's name to begin browsing \nthat node's pages. The following navigation links are listed after the wiki \nnodes:\n\n**FrontPage:** The wiki node's front page article. This is shown by default\nwhen the node is initially selected. \n\n**Recent Changes**: Shows all of the recently updated pages.\n\n**All Pages**: A flat, alphabetical list of all pages currently stored in the\nwiki.\n\n**Orphan Pages**: A list of pages that have no links to them. This can happen\nif you remove a page link without realizing it's the only link to that page.\nThis area lets you review such orphaned wiki pages so that you can re-link or\ndelete them. \n\n**Draft Pages**: A list of unpublished pages. Users can edit pages and save\ntheir changes as drafts. They can come back later to finish their changes and\npublish them. \n\nThe current wiki page's content shows in the wiki's main viewing area. Several \nfeatures display above the wiki page content, depending on which wiki features \nare enabled and your permissions: \n\n**Add Child Page:** Add a wiki page as a child of the current wiki page. \n\n**Edit:** Edit the wiki page (if you have sufficient permissions). \n\n**Details:** View the wiki page's details (if you have sufficient permissions). \nThis is explained further in \n[the documentation on page details](/docs/7-2/user/-/knowledge_base/u/wiki-page-details). \n\n**Print:** Print the wiki page.\n\nAdditional features appear below the wiki page's content. A view counter \ndisplays the wiki page's view count. Ratings and comments also appear if they're \nenabled. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/05-wiki/05-wiki-page-details.markdown",
    "content": "---\nheader-id: wiki-page-details\n---\n\n# Wiki Page Details\n\n[TOC levels=1-4]\n\nWhen viewing a wiki page, you can view its details by clicking *Details* above \nthe page content. Several tabs appear, to give you access to several\ncategories of information about the page.\n\n![Figure 1: Click *Details* to view the wiki page's details.](../../../../images/wiki-page-details-link.png)\n\n![Figure 2: The wiki page's details.](../../../../images/wiki-page-details.png)\n\n## Details\n\nThe Details tab shows page statistics and lets you perform some actions on the \npage: \n\n**Title**: The page title.\n\n**Format**: The page's format (Creole, HTML, MediaWiki, or plain text). \n\n**Latest Version**: The page's latest version. The wiki automatically tracks \npage versions whenever a page is edited.\n\n**Created By**: The user who created the page.\n\n**Last Changed By**: The user who last modified the page.\n\n**Attachments**: The number of attachments to the page.\n\n**RSS Subscription**: An icon that opens a new page where you can subscribe to \nan RSS feed using Live Bookmarks, Yahoo, Microsoft Outlook, or an application \nyou can choose from your machine.\n\n**Email Subscription**: Links that let you to subscribe to or unsubscribe from \nmodifications notifications for the page and the entire wiki node.\n\n**Advanced Actions**: Links that let you modify the page's permissions, make a \ncopy of the page, move (rename) the page, or move the page to the recycle bin. \n\n## History\n\nThe History tab lets you access the page's activities and versions via tabs: \n\n**Activities:** Lists actions performed on the page. Each activity has an icon \nthat represents the type of action, the name of the user, the action's \ndescription, date, and an *Actions* menu \n(![Actions](../../../../images/icon-actions.png)) \nto revert the action or compare its resulting version to that of another action. \n\n**Versions:** Lists all the wiki page's versions. You can revert a page back to \na previous version by selecting *Revert* from that version's *Actions* menu \n(![Actions](../../../../images/icon-actions.png)). You can also compare the\ndifferences between versions by selecting two versions and then clicking the\n*Compare Versions* button. \n\n![Figure 3: The Activities tab displays the actions taken on the wiki page.](../../../../images/wiki-page-history.png)\n\n## Incoming/Outgoing Links\n\nThe tabs *Incoming Links* and *Outgoing Links* list incoming and outgoing links, \nrespectively. These are wiki links to and from the wiki page. You can use this \ntab to examine how this page links to other pages and how other pages link back \nto this page. \n\n## Attachments\n\nThe *Attachments* tab lists the name and size of each file attached to the page.\nYou can attach any file to the wiki. Images are the most common type of file \nattached to a page. Referencing them using the proper WikiCreole syntax renders \nthe image inline, which is a nice way to include illustrations in your wiki \ndocuments. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/05-wiki/06-other-wiki-widgets.markdown",
    "content": "---\nheader-id: other-wiki-widgets\n---\n\n# Other Wiki Widgets\n\n[TOC levels=1-4]\n\nThe widgets that accompany the main Wiki widget help you display and navigate \nparticular wiki nodes. The following widgets are available: \n\n**Page Menu:** Displays a single wiki page's outgoing links. \n\n**Tree Menu:** Displays a wiki's page hierarchy as a tree. \n\n**Wiki Display:** Displays a single wiki node. \n\nYou can find these widgets in the *Add* \n(![Add](../../../../images/icon-add-app.png)) &rarr; *Widgets* &rarr; \n*Wiki* menu. \n\n## Page Menu\n\nThe Page Menu widget displays a wiki page's outgoing links. It answers the \nquestion, \"What wiki pages can I access from this page?\" After adding the Page \nMenu widget to a site page, you must set the wiki page it displays links from. \nFollow these steps to do so: \n\n1.  Click the widget's *Options* icon \n    (![Options](../../../../images/icon-app-options.png)) and select \n    *Configuration*. \n\n2.  In the configuration's *Setup* tab, choose the wiki node then click *Save*. \n\n3.  Still in the configuration's *Setup* tab, select the wiki page then click \n    *Save* and close the configuration dialog. \n\nWhen you click a Page Menu link, the site page's Wiki or Wiki Display widget \ndisplays the wiki page associated with that link. \n\n![Figure 1: The Page Menu widget displays a wiki page's outgoing links.](../../../../images/wiki-page-menu.png)\n\n## Tree Menu\n\nThe Tree Menu widget displays a wiki's page hierarchy as a tree that lets you \nnavigate all the wiki's pages. Much like the Page Menu setup, you configure \nthe Tree Menu widget to focus on a wiki node. You can also configure how deep \nusers can navigate into the page hierarchy. You can set the *Depth* to a value \nfrom 1 to 5, or select *All* to allow navigation to all of the wiki node's \npages. \n\nFollow these steps to configure the Tree Menu widget after adding it to a site \npage:\n\n1.  Click the widget's *Options* icon \n    (![Options](../../../../images/icon-app-options.png)) and select \n    *Configuration*. \n\n2.  In the configuration's *Setup* tab, choose the wiki node then select the \n    depth of wiki pages to display in the hierarchy. \n\n3.  Click *Save* and close the configuration dialog. \n\nIn the Tree Menu, folder icons represent parent wiki pages and document icons\nrepresent child wiki pages at the end of the nodes. When you click a parent wiki \npage or child wiki page, the Wiki or Wiki Display widgets on the site page \ndisplay the respective wiki page.\n\n![Figure 2: The Tree Menu widget displays a wiki node's hierarchy to the configured depth.](../../../../images/wiki-tree-menu.png)\n\n## Wiki Display\n\nThe Wiki Display widget lets you focus user attention on one wiki node. After \nadding the widget to a page, follow these steps to configure it:\n\n1.  Click the widget's *Options* icon \n    (![Options](../../../../images/icon-app-options.png)) and select \n    *Configuration*. \n\n2.  In the configuration's *Setup* tab, choose the wiki node then click *Save*. \n\n3.  Still in the configuration's *Setup* tab, select the wiki page then click \n    *Save* and close the configuration dialog. This page serves as the entry \n    point for the wiki. \n\nThe configuration options and user interface for the Wiki Display are almost\nidentical to that of the Wiki widget. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/06-alerts-announcements/01-alerts-announcements-intro.markdown",
    "content": "---\nheader-id: sending-alerts-and-announcements\n---\n\n# Sending Alerts and Announcements\n\n[TOC levels=1-4]\n\nYou can use the Alerts and Announcements widgets on Site pages to broadcast \nimportant information to users. The Alerts widget is designed for displaying \nhigh-priority information (e.g. planned downtime alerts, security alerts, etc.). \nEach alert is therefore labeled with a red *Important* tag. The Announcements\nwidget displays all other information you want to broadcast on your site. Each\nannouncement therefore lacks the red tag. To separate important alerts from\nmore mundane announcements, you can place the Alerts and Announcements widgets\non different pages. However, you can use either widget to display any\ninformation you wish. Besides the red tag, they function the same. You can also\nscope your alerts and announcements to specific groups of users. \n\n![Figure 1: The Alerts widget provides administrators with an easy way to communicate important information to appropriate groups of users.](../../../../images/alerts-widget.png)\n\nThese widgets have two tabs: \n\n**Unread:** Non-expired alerts/announcements that you haven't read.\n\n**Read:** Alerts/announcements that have expired, or that you've read. \n\nClick an alert/announcement's *Actions* button \n(![Actions](../../../../images/icon-actions.png)) to edit or delete it. \n\n## Creating Alerts and Announcements\n\nThere are two places where you can create alerts and announcements: \n\n1.  The *Announcements and Alerts* app. Access this app at *Control Panel* \n    &rarr; *Configuration* &rarr; *Announcements and Alerts*. Announcements and \n    alerts are in separate tabs in this app. To begin creating an announcement \n    or alert, select the appropriate tab and then click the *Add* button \n    (![Add](../../../../images/icon-add.png)). \n    This app gives administrators a central location to create announcements and \n    alerts that are then displayed on Site pages by the Announcements and Alerts \n    widgets. \n\n2.  The Announcements and Alerts widgets, after adding them to a Site page from \n    the *Add* \n    (![Add](../../../../images/icon-add-app.png)) \n    &rarr; *Widgets* &rarr; *News* menu. To begin creating an announcement or \n    alert, click the widget's *Add Alert* or *Add Announcement* button. \n\nRegardless of where you create the alert or announcement, the form for creating \nit is the same. Follow these steps to complete the form: \n\n1.  Use the *Title* field to give the alert or announcement a title. Then create \n    your content in the field *Write your content here...*. For a detailed \n    explanation of the editor, see the \n    [Blogs documentation](/docs/7-2/user/-/knowledge_base/u/using-the-blog-entry-editor). \n\n    ![Figure 2: Enter your alert or announcement's title and content.](../../../../images/alerts-new-alert.png)\n\n2.  Open the *Configuration* section of the form and set the following options, \n    if desired: \n\n    **Distribution Scope:** The scope where the alert/announcement is displayed. \n    The default *General* scope sends the alert/announcements to everyone. \n    Alternatively, you can select your site or specific roles as the scope. \n\n    **URL:** A URL (optional) to include with the alert/announcement. For \n    example, an announcement about a news story could include a link to the news \n    article. The URL must be valid and begin with `http://` or `https://`. \n\n    **Type:** The alert/announcement type. This can be *General*, *News*, or \n    *Test*. Note that each user can specify a different delivery mechanism for \n    each type of alert/announcement. See \n    [User Configuration](#user-configuration) \n    for details. \n\n    **Priority:** The announcement's priority. This can be *Normal* or \n    *Important*. Note that this is disabled for alerts because alerts are always \n    high priority. \n\n    **Display Date:** The display date of the alert/announcement. This \n    determines when the alert/announcement is sent to users and appears in the \n    widget. By default, the *Display Immediately* box is checked. This sets the \n    display date equal to the creation date. Uncheck this box to enter a custom \n    display date. For example, administrators can create alerts/announcements \n    for display on a later date. This date can be days, weeks, months, or years \n    in the future. Once the *Display Immediately* box is unchecked, clicking the \n    Display Date field opens the date-picker. \n\n    **Expiration Date:** The date and time the alert/announcement expires. Once \n    an alert/announcement expires, the widget displays it in the Read tab. \n    Clicking the Expiration Date field opens the date-picker. \n\n    ![Figure 3: Configure your new alert or announcement.](../../../../images/alerts-new-alert-config.png)\n\n3.  Click *Save* when you're done. Your alert/announcement then appears in the \n    widget. \n\n## User Configuration\n\nUsers can configure how they'd like to receive announcements. \n\n1.  Open your user menu and select *Account Settings*. \n\n2.  On the *Preferences* tab, select *Alerts and Announcements Delivery*. This \n    shows options for customizing the delivery of alerts and announcements. \n\n3.  Select a configuration for each type of alert/announcement (General, News, \n    or Test). For each type, you can enable delivery by email and SMS (text \n    message). Note that the *Website* delivery option is selected and grayed out \n    for each alert type. This means that each alert/announcement is always \n    viewable in its respective widget on a site. \n\n4.  Click *Save* when you're finished. \n\n    ![Figure 4: Each user can choose how they receive alerts and announcements.](../../../../images/alerts-delivery.png)\n\n## Alert and Announcement Roles\n\nYou can also create roles for users to make general announcements. For instance, \nif you want someone specific to have strict control over announcements, give\nthat person an Announcements Role. Follow these steps to create a simple\nAnnouncements Role: \n\n1.  Navigate to *Control Panel* &rarr; *Users* &rarr; *Roles*. \n\n2.  With the *Regular Roles* tab selected, click the *Add* button \n    (![Add](../../../../images/icon-add.png)). This opens the *New Role* form. \n\n3.  Name your Role *Announcements*, give it a description, and click *Save*. \n\n4.  Select the Role's *Define Permissions* tab then grant these permissions: \n\n    -   In *Control Panel* &rarr; *General Permissions*, select *Add General \n        Announcements*. \n    -   In *Site Administration* &rarr; *Applications* &rarr; *Announcements*, \n        select all the resource permissions.\n\n    Click *Save* after selecting each permission. These permissions let the Role \n    add alerts and announcements. \n\nNow you have a simple Announcements Role that can manage your site's general \nannouncements. Of course, you can adjust this Role's permissions as needed. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/07-notifications-requests/01-notifications-requests-intro.markdown",
    "content": "---\nheader-id: managing-notifications-and-requests\n---\n\n# Managing Notifications and Requests\n\n[TOC levels=1-4]\n\nIf you subscribed to a blog or message board, or if someone sent you a\nprivate message, invitation, event reminder, or mentioned you in a post, you\nreceived a notification or request. \n\nTo access notifications and requests, click your user menu and select \n*Notifications*. The *Notifications List* tab is selected by default. This is \nwhere all your notifications appear. Click the *Requests List* tab to view and \nmanage your requests. \n\n![Figure 1: The *Notifications List* section displays all your notifications in a paginated list.](../../../../images/mentions-notification-list.png)\n\n## Managing Notifications\n\nNotifications can pile up after some time, especially if you were away for a few\ndays. The Management Bar gives you several ways to filter and sort your \nnotifications. \n\nThe *Filter and Order* menu gives you the following options for viewing \nnotifications:\n\n**All:** The default option. Displays both read and unread notifications. \n\n**Unread:** Displays notifications that haven't been marked as read. Unread \nnotifications are indicated with a blue border on the left-hand side of the \nnotification. \n\n**Read:** Displays notifications that have been marked as read. \n\n**Date:** Order notifications by date. \n\nBy default, notifications are listed by date in descending order. To sort \nnotifications by ascending order, click the up/down arrow icon in the management \nbar. Clicking the button again reverses the sort. \n\nEach notification's *Actions* menu \n(![Actions](../../../../images/icon-actions.png)) lets you mark the notification as \nread/unread, or delete the notification. \n\n### Managing Multiple Notifications\n\nYou can also manage multiple notifications at once. Select the checkbox next to \nnotifications you want to manage and choose an option from the Management Bar. \nSelect the checkbox above the notifications list to select all notifications on \nthe current page. The Management Bar shows three actions for selected \nnotifications: \n\n-   Mark as Read \n    (![Open Envelope](../../../../images/icon-envelope-open.png))\n-   Mark as Unread \n    (![Closed Envelope](../../../../images/icon-envelope-closed.png))\n-   Delete \n    (![Delete Button](../../../../images/icon-delete.png))\n\n## Managing Requests\n\nWhen you get a request, it appears in the *Requests List* tab. In each request's \n*Actions* menu \n(![Actions](../../../../images/icon-actions.png)), you can click *Confirm* to \naccept, *Ignore* to decline, or *Delete* to remove the request. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/08-knowledge-base/01-intro.markdown",
    "content": "---\nheader-id: using-the-knowledge-base\n---\n\n# Using the Knowledge Base\n\n[TOC levels=1-4]\n\nThe Knowledge Base app can be used to display professional product documentation\nor form complete books or guides. It even lets you import article source files\nwritten in Markdown. It's \n[workflow-enabled](/docs/7-2/user/-/knowledge_base/u/workflow), \nso you can require articles to be approved before publication. Additionally, you \ncan create article templates that help users follow a common outline. \n\nHere are the Knowledge Base's key features: \n\n-   Navigation is built into Knowledge Base Display. \n-   The suggestions interface enables user feedback on articles. \n-   Articles are stored in folders.\n-   Metadata fields exist for the friendly URL, source URL, categorization, and \n    related assets.\n-   The *Edit on GitHub* button \n    (![GitHub](../../../../images/icon-edit-on-github.png)) \n    can take readers to an article's source repository location (if you choose \n    to use it that way). \n-   Markdown source files can be imported to create and update articles. \n\nThe Knowledge Base has several widgets you can add to Site pages: \n\n- Knowledge Base Article \n- Knowledge Base Display \n- Knowledge Base Search \n- Knowledge Base Section \n\n![Figure 1: Knowledge Base Display's navigation and viewing provide a great reading experience.](../../../../images/kb-display.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/08-knowledge-base/02-creating-kb-articles.markdown",
    "content": "---\nheader-id: creating-knowledge-base-articles\n---\n\n# Creating Knowledge Base Articles\n\n[TOC levels=1-4]\n\nThe Knowledge Base app in Site Administration contains everything you need to \ncreate articles in the Knowledge Base. You can create articles by authoring them \nin the app's WYSIWYG editor or by importing them from Markdown files \n(`.markdown`, `.md`) in a ZIP archive. The sections below cover both ways of \ncreating articles. \n\n| **Note:** To access Knowledge Base in Site Administration, a Role must have \n| the permission *Knowledge Base* &rarr; *Access in Site Administration*. To add \n| or act on articles, folders, or suggestions, the Site administrator must grant \n| the appropriate permissions using the Permissions window in Knowledge Base. \n\nTo navigate to the Knowledge Base app, open the Menu \n(![Menu](../../../../images/icon-menu.png)) \nthen go to Site Administration (the menu for your Site) &rarr; *Content & Data* \n&rarr; *Knowledge Base*. The Knowledge Base app has three tabs: \n\n**Articles:** Create and manage articles and folders. \n\n**Templates:** Create and manage templates. \n\n**Suggestions:** Manage user-submitted feedback for articles. \n\nSelect the *Articles* tab, then proceed to the sections below for instructions \non creating articles. \n\n![Figure 1: The Knowledge Base app in Site Administration lets you create Knowledge Base articles.](../../../../images/kb-admin-articles.png)\n\n## Authoring Articles in the Editor\n\nFollow these steps to create an article in the editor: \n\n1.  In the Articles tab, click the *Add* button \n    (![Add](../../../../images/icon-add.png)) \n    and choose *Basic Article* or the name of an available template. This brings \n    up the New Article form. \n\n2.  Enter a title for the article. A URL-safe version of the title you provide \n    is added to the end of the article's friendly URL. You can manage the \n    friendly URL in the *Configuration* section's *Friendly URL* field. \n\n3.  Use the WYSIWYG editor to create the article's content. To view or edit the \n    article's HTML source, click the *Source* button in the editor. The sections \n    below the editor let you add attachments and tags, specify related assets, \n    and set permissions for the article. By default, View permission is granted \n    to the Guest role, meaning anyone can view your article. \n\n    ![Figure 2: You can create and modify a Knowledge Base article's content using the WYSIWYG editor.](../../../../images/kb-admin-new-article.png)\n\n4.  Click *Publish* to submit the article for publication or click \n    *Save as Draft* to continue working on it later. Note that if you've enabled \n    workflow for the Knowledge Base, your article must be approved before \n    publication. \n\nOnce the article is saved, it is converted automatically to HTML for the \nKnowledge Base. Articles are listed in a table in the Articles tab. \n\n## Importing Knowledge Base Articles\n\nYou can also create new Knowledge Base articles by importing them from a ZIP \narchive that contains articles in the Markdown format (`.markdown`, `.md`). For \nexample, you could write articles in your favorite Markdown editor, package them \nin a ZIP file, and then import that ZIP file to create those articles in the \nKnowledge Base. The Knowledge Base can also prioritize articles by their \nfilenames' numerical prefixes. For example, the Knowledge Base would list \n`01-article.markdown` and `02-article.markdown` in ascending order by their \nnumerical prefix (`01`, `02`). For more information on article priority, see \n[Managing Knowledge Base Articles](/docs/7-2/user/-/knowledge_base/u/managing-the-knowledge-base#managing-knowledge-base-articles)\nFor detailed information on the Knowledge Base importer, see the following \ntopics: \n\n-   [Importing Knowledge Base Articles](/docs/7-2/user/-/knowledge_base/u/importing-knowledge-base-articles)\n-   [Knowledge Base ZIP File Requirements](/docs/7-2/user/-/knowledge_base/u/knowledge-base-zip-file-requirements)\n-   [Knowledge Base Importer FAQs](/docs/7-2/user/-/knowledge_base/u/knowledge-base-importer-faqs)\n\n| **Note:** To import articles, your Role must have the permission *Knowledge\n| Base* &rarr; *Resource Permissions: Import Articles*. \n\nFollow these steps to import articles into the Knowledge Base: \n\n1.  In the Articles tab, click the *Add* button \n    (![Add](../../../../images/icon-add.png)) \n    and choose *Import*. This brings up the Import form. \n\n2.  Click *Browse* and select the ZIP file that contains the articles you want \n    to import. \n\n3.  If you want to use the files' numerical prefixes to prioritize the imported \n    articles in the Knowledge Base, select \n    *Apply numerical prefixes of article files as priorities*. \n\n4.  Click *Save* when you're finished. \n\nLike all articles, imported articles are automatically converted to HTML for the \nKnowledge Base and listed in a table with the rest of the articles in the \nArticles tab. \n\n![Figure 3: You can import ZIP files that contain Knowledge Base articles in Markdown format.](../../../../images/kb-admin-import.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/08-knowledge-base/03-managing-kb.markdown",
    "content": "---\nheader-id: managing-the-knowledge-base\n---\n\n# Managing the Knowledge Base\n\n[TOC levels=1-4]\n\nThe Knowledge Base app in Site Administration manages the Knowledge Base. To\nnavigate to this app, open the Menu (![Menu](../../../../images/icon-menu.png))\nthen go to Site Administration (the menu for your site) &rarr; *Content & Data* \n&rarr; *Knowledge Base*. \n\n| **Note:** To access Knowledge Base in Site Administration, a Role must have \n| the permission *Knowledge Base* &rarr; *Access in Site Administration*. To add \n| or act on articles, folders, or suggestions, the site administrator must grant \n| the appropriate permissions using the Permissions window in Knowledge Base. \n\n![Figure 1: You can manage Knowledge Base articles, folders, and suggestions.](../../../../images/kb-admin-articles.png)\n\n## Setting the Knowledge Base's Options\n\nAt the top-right of the Knowledge Base app, the Options menu \n(![Options](../../../../images/icon-options.png)) \ncontains these options: \n\n**Subscribe:** Get notified when Knowledge Base articles are created, \nupdated, or deleted. \n\n**Home Folder Permissions:** Define detailed permissions for the Knowledge \nBase app. You can choose the Roles that can perform the following tasks: \n\n-   Add/delete articles, folders, and templates \n-   Change the Knowledge Base app's permissions \n-   Subscribe to articles\n-   View templates and suggestions\n\n**Export/Import:** Export or import the Knowledge Base app's configuration. \n\n**Configuration:** Configure email notifications for article subscriptions and\nsuggestions. You can also make the Knowledge Base app's articles available via\nRSS (enabled by default), and configure the RSS feed's options. \n\n![Figure 2: The Knowledge Base App's options.](../../../../images/kb-admin-options.png)\n\n## Managing Knowledge Base Articles\n\nEach article also has a *priority* value that determines its position in the \n[Knowledge Base Display widget's navigation](/docs/7-2/user/-/knowledge_base/u/knowledge-base-display). Each\narticle's priority value appears beneath the article's title. The Knowledge Base\nDisplay widget's navigation arranges articles in ascending priority. Priority\n1 is the highest priority. The higher an article's priority, the higher it\nappears in the navigation. Articles are assigned the next lowest priority by\ndefault. This behavior can be changed via \n[Knowledge Base System Settings](/docs/7-2/user/-/knowledge_base/u/knowledge-base-system-settings). \n\nTo assign articles a new priority value, follow these steps: \n\n1.  Select *Move* from the Actions menu \n    (![Actions](../../../../images/icon-actions.png)) \n    next to the article. \n\n2.  Enter a new priority value for the article. \n\n3.  Click *Move* to apply the new priority. \n\nYou can also organize articles with folders. Follow these steps to create a \nfolder:\n\n1.  Click the Add button\n    (![Add](../../../../images/icon-add.png)) \n    and select *Folder*. This opens a form for creating the new folder. \n\n2.  Enter a name and an optional description. \n\n    By default, anyone can view the folder. You can manage this setting along \n    with the other permissions in the form's *Permissions* section. \n\n3.  Click *Save*. The folder is then listed in a table in the Articles tab. \n\nThe text immediately below the *Filter and Order* selector at the top of the \napp shows your position in the folder hierarchy. Click a folder's name in the \nhierarchy to navigate to it. You can also move articles into folders and create \nchild articles. Knowledge Base also supports nested folders.\n\n![Figure 3: This screenshot uses a red box to highlight the text that indicates the current position in the folder hierarchy.](../../../../images/kb-admin-folder-hierarchy.png)\n\nEach folder's Actions menu \n(![Actions](../../../../images/icon-actions.png)) \nlets you perform the following actions on the folder: \n\n**Edit:** Change the folder's name and description.\n\n**Move:** Relocate the folder under a new parent folder or update its priority. \n\n**Delete:** Remove the folder and its articles from the Knowledge Base. \n\n**Permissions:** Grant or revoke the following permissions: add an article to\nthe folder, add a sub-folder to the folder, delete the folder, move the folder,\nset permissions on the folder, edit (update) the folder, and view the folder. \n\nYou can also delete multiple articles or folders at once. To do this, select the \ncheckbox for each item that you want to delete and click the *Delete* button \n(![Delete](../../../../images/icon-delete.png)) \nthat appears in the Management Bar. You can also see the info for selected items \nby clicking the *Info* button \n(![Info](../../../../images/icon-information.png)) \nin the Management Bar. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/08-knowledge-base/04-kb-templates.markdown",
    "content": "---\nheader-id: knowledge-base-templates\n---\n\n# Knowledge Base Templates\n\n[TOC levels=1-4]\n\nTemplates give users a starting point. For example, you can create templates\nthat contain default headers or other content for articles. Templates help\nfoster consistent formatting and content organization for articles. You can\ncreate and manage templates from the Knowledge Base app in Site Administration. \nTo navigate to this app, open the Menu\n(![Menu](../../../../images/icon-menu.png)) and go to Site Administration\n(the menu for your Site) &rarr; *Content & Data* &rarr; *Knowledge Base*. \n\n| **Note:** To access Knowledge Base in Site Administration, a Role must have \n| the permission *Knowledge Base* &rarr; *Access in Site Administration*. To add \n| or act on articles, folders, or suggestions, the Site administrator must grant \n| the appropriate permissions using the Permissions window in Knowledge Base. \n\n![Figure 1: The Knowledge Base app's Templates tab.](../../../../images/kb-admin-templates.png)\n\n## Creating Templates\n\nTo create a new template, follow these steps:\n\n1.  Click the *Templates* tab. \n \n2.  Click the *Add* button \n    (![Add](../../../../images/icon-add.png)). \n    This brings up the *New Template* form. \n\n3.  Enter a title for the template. \n\n4.  Use the WYSIWYG editor to create the template's content. To view or edit the \n    article's HTML source, click the *Source* button (`</>`) in the editor. You \n    can also set the template's permissions via the form's *Permissions* \n    section. \n\n5.  Click *Publish* to finish creating the template. \n\n![Figure 2: The New Template form.](../../../../images/kb-admin-new-template.png)\n\n## Managing Templates\n\nEach template appears in a list in the Templates tab. You can take the following \nactions on each template via its Actions button \n(![Actions](../../../../images/icon-actions.png)):\n\n**View:** Display the template. From here, you can print the template, use \nit to create an article, edit it, modify its permissions, or delete it. \n\n**Edit:** Change the template's title and content. \n\n**Permissions:** Configure the template's permissions. You can choose \nwhether a Role can change permissions, update, view, or delete the template. \n\n**Delete:** Remove the template from the Knowledge Base. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/08-knowledge-base/05-kb-feedback.markdown",
    "content": "---\nheader-id: responding-to-knowledge-base-feedback\n---\n\n# Responding to Knowledge Base Feedback\n\n[TOC levels=1-4]\n\nThe Knowledge Base app's *Suggestions* tab shows user feedback on articles and \nlets you mark progress on addressing the feedback. To navigate to this app, open \nthe Menu \n(![Menu](../../../../images/icon-menu.png)) \nthen go to Site Administration (the menu for your Site) &rarr; *Content & Data* \n&rarr; *Knowledge Base*. \n\n| **Note:** To access Knowledge Base in Site Administration, a Role must have \n| the permission *Knowledge Base* &rarr; *Access in Site Administration*. To add \n| or act on articles, folders, or suggestions, the Site administrator must grant \n| the appropriate permissions using the Permissions window in Knowledge Base. \n\n![Figure 1: The Suggestions tab in Knowledge Base displays each piece of feedback that users leave on Knowledge Base articles.](../../../../images/kb-admin-suggestions.png)\n\nEach suggestion provides the link to the associated article, the user's \nfeedback, the user's name, the feedback's time stamp, and the status on \naddressing the suggestion. You can use each entry's Actions menu \n(![Actions](../../../../images/icon-actions.png)) \nto move the entry between the *New*, *In Progress*, and *Resolved* states.\n\n| **Note:** To view article suggestions, your Role must have the permission \n| *Knowledge Base* &rarr; *Knowledge Base: View Suggestions*. \n| \n| To move suggestions between the *New*, *In Progress*, and *Resolved* states, \n| your Role must have the permission *Knowledge Base* &rarr; *Knowledge Base \n| Article: Update*. Roles assigned this permission can also view and update the\n| state of article suggestions from any of the other Knowledge Base widgets. \n\nWhen you move the suggestion to a different state, an email is sent notifying\nthe user of the change. You can view and configure the automated emails from the \nKnowledge Base app's \n*Options* \n(![Options](../../../../images/icon-options.png)) \n&rarr; *Configuration* menu. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/08-knowledge-base/06-kb-display.markdown",
    "content": "---\nheader-id: knowledge-base-display\n---\n\n# Knowledge Base Display\n\n[TOC levels=1-4]\n\nYou can use the Knowledge Base Display widget to display your published \nKnowledge Base articles. You can customize how this widget displays articles, \nand which ones it displays. To get started, add the widget to the Site page\nyou want to display articles on:\n\n1.  Navigate to the page and open the *Add* menu \n    (![Add](../../../../images/icon-add-app.png)). \n\n2.  Open the *Widgets* &rarr; *Content Management* section, then add *Knowledge \n    Base Display* to the page. \n\nBy default, the Knowledge Base Display widget displays articles from the\nKnowledge Base's Home folder. To change the location of its articles, follow\nthese steps: \n\n1.  Click the widget's *Options* icon \n    (![Options](../../../../images/icon-app-options.png)) \n    and select *Configuration*. This opens the Configuration dialog. \n\n2.  In the *Setup* tab, select the *General* tab then click *Select* in the \n    *Article or Folder* field. This brings up the *Select Entry* form.\n\n3.  Click *Choose* next to the article or folder of articles you want to \n    display. Alternatively, you can click the *Choose This Folder* button at the \n    top of the Select Entry form to select the current folder. \n\n4.  Click *Save* and close the Configuration dialog. \n\n![Figure 1: Select the article or folder of articles that the Knowledge Base Display widget displays.](../../../../images/kb-display-config-article.png)\n \nThe Knowledge Base Display widget's Options icon \n(![Options](../../../../images/icon-app-options.png)) \nalso provides these common configuration options: \n\n-   Look and Feel Configuration \n-   Export/Import \n-   Permissions \n-   Configuration Templates \n\nFor more information on these, see the section on configuring \nwidgets in \n[Web Experience Management](/docs/7-2/user/-/knowledge_base/u/web-experience-management). \n\nThe Knowledge Base Display's navigation menu and display options make it the \nperfect candidate for a full page widget. If you display a folder of articles, \nthe navigation on the left side of the widget displays links to all the folder's \narticles. The viewing area on the right side of the widget displays the folder's \nleading article (the *priority one* article). Click an article in the navigation \nto display it in the viewing area. The currently displayed article's link \nappears in bold in the navigation. You can also move between articles by \nclicking the links with arrows at the bottom of the widget. \n\n![Figure 2: Knowledge Base Display's navigation and viewing provide a great reading experience.](../../../../images/kb-display.png)\n\nKnowledge Base Display can also show article hierarchies. Viewing an article \nthat has child articles expands the navigation tree to show links to the child \narticles. Any expanded nodes collapse when you view a different top level \narticle.\n\nThe links at the top of the widget allow users to perform the following actions \non an article: \n\n-   Subscribe to an RSS feed of the Knowledge Base\n-   Subscribe to the current article\n-   View the current article's history\n-   Print the current article \n\nAdministrators have access to an additional set of links at the top of the \nwidget that lets them perform the following actions:\n\n-   Edit the article\n-   Add a child article\n-   Set the article's permissions\n-   Move the article\n-   Delete the article\n\nBelow the article's content is the rating interface, showing thumbs up/down \nicons. Users can also submit suggestions or comments below the article in the \ntext box labeled *Do you have any suggestions?*. Administrators can \n[view the suggestions and mark progress on them](/docs/7-2/user/-/knowledge_base/u/responding-to-knowledge-base-feedback). \n\nIf the administrator enables the Knowledge Base app's source URL feature (more \non this in a moment) and an article has an assigned source URL, an \n*Edit on GitHub* button \n(![GitHub](../../../../images/icon-edit-on-github.png)) \nappears to the right of the article's title. This button lets users access the \narticle's source in GitHub. You can use this feature to encourage users to \ncontribute fixes or improvements to articles. If you're interested in this \nfeature, you can direct your administrator to follow the instructions in \n[Knowledge Base System Settings](/docs/7-2/user/-/knowledge_base/u/knowledge-base-system-settings). \n\n## Displaying Different Article Sets\n\nAs an administrator, say that you've used folders to aggregate similar articles,\nand you want to provide an easy way for users to switch between these sets of\narticles. The Knowledge Base Display's content folder feature adds a selector to \nthe top of the navigation that lets users switch between article sets.\n \nFollow these steps to set up content folders: \n\n1.  Add a folder in the Knowledge Base app in Site Administration. Then create \n    sub-folders in this folder. These sub-folders are the *content folders*. \n\n2.  Add articles to each content folder.\n\n3.  Select *Configuration* from Knowledge Base Display's *Options* menu \n    (![Options](../../../../images/icon-app-options.png)). \n    In the *Setup* &rarr; *General* tab, select the content folders' parent \n    folder and click *Save*. \n\nThe content selector's values reflect the names of your content folders. Select \none to view its articles. \n\n![Figure 3: Knowledge Base Display's content folder feature lets users switch between different sets of articles.](../../../../images/kb-display-content-selector.png)\n\nYou can also add a common prefix to the names shown in the selector: \n\n1.  Select *Configuration* from Knowledge Base Display's *Options* menu \n    (![Options](../../../../images/icon-app-options.png)). \n    In the Configuration dialog, select the *Setup* &rarr; *Display Settings* \n    tab. \n \n2.  Enter the prefix into the *Content Root Prefix* field and click *Save*. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/08-knowledge-base/07-other-kb-widgets.markdown",
    "content": "---\nheader-id: other-knowledge-base-widgets\n---\n\n# Other Knowledge Base Widgets\n\n[TOC levels=1-4]\n\nThere are other Knowledge Base widgets you can add to Site pages besides \n[Knowledge Base Display](/docs/7-2/user/-/knowledge_base/u/knowledge-base-display): \n\n**Knowledge Base Article:** Display a single article's content.\n\n**Knowledge Base Section:** Publish articles associated with a specific topic \n(section).\n\n**Knowledge Base Search:** Search for articles.\n\nYou can add these widgets from *Add* \n(![Add](../../../../images/icon-add-app.png)) \n&rarr; *Widgets* &rarr; *Content Management*. \n\n## Knowledge Base Article\n\nKnowledge Base Article displays a single article's content. It even shows\nabstracts of child articles. You can add multiple Knowledge Base Article\ninstances to a page, and each one can show a different article. \n\nAfter adding Knowledge Base Article to a page, follow these steps to configure \nthe widget: \n\n1.  Click *Please configure this portlet to make it visible to all users*. This \n    opens the Configuration dialog. \n\n2.  In the *Setup* &rarr; *General* tab, click *Select*, choose an article, \n    click *Save*, and close the Configuration dialog. \n\nYou can change your selection from the widget's *Options* \n(![Options](../../../../images/icon-app-options.png)) \n&rarr; *Configuration* menu. \n\n![Figure 1: The Knowledge Base Article app is great at displaying individual articles.](../../../../images/kb-article.png)\n\nKnowledge Base Article shares the same UI as the Knowledge Base Display to \ndisplay and manage its articles. Refer to the \n[Knowledge Base Display documentation](/docs/7-2/user/-/knowledge_base/u/knowledge-base-display) \nfor a detailed description of the widget's UI. \n\n## Knowledge Base Section\n\n| **Note:** as of Knowledge Base 3.0.0, the Knowledge Base Sections widget is \n| deprecated and replaced by \n| [categories](/docs/7-2/user/-/knowledge_base/u/organizing-content-with-tags-and-categories). \n\nThe Knowledge Base Section widget lets you publish articles associated with a \nspecific topic (section). For example, a news Site might have the sections \n*World*, *Politics*, *Business*, and *Entertainment*. \n\n![Figure 2: The Knowledge Base Section widget.](../../../../images/kb-section.png)\n\nTo use sections, an administrator must first configure the feature in System \nSettings, creating the section names for use in the Knowledge Base Section \nwidget. This process is covered in detail in \n[Knowledge Base System Settings](/docs/7-2/user/-/knowledge_base/u/knowledge-base-system-settings). \nWhen creating or editing a Knowledge Base article, authors can then select the \narticle's section in the *Configuration* &rarr; *Section* field. \n\nYou can add multiple instances of the Knowledge Base Section widget to a page. \nEach widget can display articles from any number of sections. You can configure \nthe widget to display article titles or abstracts. You can also define whether \nto show pagination or section titles. \n\nFollow these steps to configure an instance of the Knowledge Base Section \nwidget:\n\n1.  Select *Configuration* from the Knowledge Base Section widget's *Options* \n    menu (![Options](../../../../images/icon-app-options.png)). \n    This opens the widget's Configuration dialog. \n\n2.  In the Configuration dialog's *Setup* &rarr; *General* tab, select the \n    section or sections that you want to use and click *Save*.\n \n3.  Close the Configuration dialog to see the updates.\n\nThe matching articles are displayed in the app beneath their section heading. \n\n## Knowledge Base Search\n\n| **Note:** as of Knowledge Base 3.0.0, the Knowledge Base Search widget is \n| deprecated and replaced by Liferay Search. \n\nEven though the Knowledge Base can show the structure of its articles, it may be \ndifficult to find exactly what you're looking for by browsing. That's where the \nKnowledge Base Search widget comes in. \n\nEnter your search term and press the *Search* button. The results are displayed\nin a table with the following criteria for each matching article:\n\n-   Title\n-   Author\n-   Create date\n-   Modified date\n-   Number of views\n\nYou can select the criteria to display in the widget's *Options* \n(![Options](../../../../images/icon-app-options.png)) \n*Configuration* dialog. \n\n![Figure 3: The Knowledge Base Search widget lets you search the Knowledge Base for keywords.](../../../../images/kb-search.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/08-knowledge-base/08-kb-import.markdown",
    "content": "---\nheader-id: importing-knowledge-base-articles\n---\n\n# Importing Knowledge Base Articles\n\n[TOC levels=1-4]\n\nAs mentioned earlier, the Knowledge Base app can import articles in bulk. This \nlets you have an offline process where articles are prepared ahead of time \nbefore they're published. Articles are imported into the Knowledge Base as \n[Markdown](http://commonmark.org) \nfiles. Markdown is a text-only file format that is easy to read, yet supports \nall the things you'd need to do to format your articles. \n\n| **Note:** To import articles, your Role must be granted the *Knowledge Base* \n| &rarr; *Resource Permissions: Import Articles* permission. \n\nThe Knowledge Base supports a Markdown dialect known as \n[Multi-Markdown](http://fletcher.github.io/MultiMarkdown-4/). \nThis dialect extends the original Markdown with features like table formatting, \nimage captions, and footnotes. \n\nFor the Knowledge Base to import your Markdown articles, they must adhere to\nthese requirements: \n\n-   All source files must use the `.markdown` or `.md` extensions.\n-   Articles must start with a top-level header (e.g., `# Some Heading ...`). \n-   Each header must have an associated, unique ID for the article's friendly \n    URL title and for anchor tags in the article's sub headers. Here's an \n    example of a top-level header that correctly specifies an ID: \n\n  `# Some Heading  [](id=some-heading)`\n\nHere's Markdown source text for a simple example article: \n\n    # The Moons of Mars [](id=the-moons-of-mars)\n\n    As you look up from your chaise lounge, you're sure to see our neighboring\n    planet Mars. Did you know that Mars has two moons? You might have to break \n    out a pair of binoculars to see them.\n\n    Its two moons are aptly named after the two sons of mythical Roman god Mars.\n    Their names are Phobos and Deimos. \n\nIn the first line above, notice the header's ID assignment\n`id=the-moons-of-mars`. On import, the ID value becomes the Knowledge Base\narticle's URL title. \n\nMarkdown is something of a standard: there's \n[Github Flavored Markdown](https://help.github.com/articles/github-flavored-markdown), \na proposed \n[common Markdown syntax](http://www.commonmark.org), \nforums that support Markdown (reddit, StackExchange, and others), Markdown\neditors, and an \n[IETF draft](https://tools.ietf.org/html/rfc7763) \nfor making it an official Internet media type (text/markdown). Why is there so \nmuch interest in Markdown? \n\n1.  It's readable. Even if you don't know Markdown, you can read it without\n    having to filter out the syntax. \n\n2.  It gets out of a writer's way. You don't have to worry about mousing to\n    various icons to change text into a heading or create bulleted lists. Just \n    start typing. The syntax is very intuitive. \n\n3.  There are tools to convert it to many other formats, though it was designed \n    to convert to HTML. If your articles are in Markdown, it's straightforward \n    to publish them to the web, mobile formats (Kindle, ePub), and print. \n\n4.  Since it's only text, you can use existing tools to collaborate on that\n    text. Using services like GitHub, people can contribute to your articles, \n    and you can see all the changes that have been made to them. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/08-knowledge-base/09-kb-zip-requirements.markdown",
    "content": "---\nheader-id: knowledge-base-zip-file-requirements\n---\n\n# Knowledge Base ZIP File Requirements\n\n[TOC levels=1-4]\n\nThe Knowledge Base importer supports article hierarchies, so Markdown files can \nbe specified anywhere in the ZIP file's directory structure. They can be nested \nin any number of folders. Image files are the only files supported for \nattachments. \n\n| **Note:** Imported articles are independent of the workflow settings. This \n| means that **imported articles are automatically approved.**\n| \n| Only users with the *Import Articles* permission assigned to their Role are \n| able to import articles. This permission can be assigned manually through \n| *Control Panel* &rarr; *Users* &rarr; *Roles*. If you've upgraded from Liferay \n| Portal 6.2, you can also assign this Role to every Role that was already able \n| to add articles with a command from the Gogo shell. \n| \n| Open the\n| [Gogo shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell).\n| Type `knowledgeBase:addImportArticlePermissions` and hit enter. \n\nThe ZIP file's articles are imported in file order (alphanumerically). To \ndesignate an article's priority, add a numeric prefix to its file name. For \nexample, the priorities for articles named `01-file.markdown` and \n`02-file.markdown` become `1.0` and `2.0`.\n\nTo designate an article to be the parent of all other articles in the same \nsource folder, end its file name with `-intro.markdown`. This creates a \nparent-child hierarchy. You can use the prefix `00` for parent articles to place \nthem at the top of the folder's file order. The importer uses the numeric prefix \nof an intro file's folder as its article priority. \n\nHere's the underlying logic for the `00` prefix: \n\n-   A file prefix of `00` for a non-intro file assigns the resulting article's \n    priority to `1.0`.\n-   A file prefix of `00` for a top-level intro file sets the article's priority \n    to the first folder numeric prefix found that is `1.0` or greater. \n\nThis convention lets you specify priorities for top-level (non-child) articles \nin your hierarchy.\n\nWhen importing, keep the checkbox labeled *Apply numerical prefixes of article\nfiles as priorities* selected. If a file doesn't have a prefix, its article gets\nthe next available priority (the highest current priority, plus one). \n\nBelow is an example ZIP file structure that demonstrates the features mentioned \nso far: \n\n**ZIP File Structure Example:**\n\n- `01-winter-events/`\n    - `00-winter-excursions-intro.markdown`\n    - `01-star-dust-snow-shoeing.markdown`\n    - `02-lunar-alpine.markdown`\n\n- `02-summer-events/`\n    - `00-summer-excursions-intro.markdown`\n    - `01-lunar-rock-scrambling.markdown`\n    - `02-extra-terrestrial-mountain-biking.markdown`\n    - `03-lunar-olympics/`\n        - `00-lunar-olympics-intro.markdown`\n        - `01-zero-gravity-diving.markdown`\n\n- `images/`\n    - `some-image.png`\n    - `another-image.jpeg`\n\nThe above ZIP file specifies `00-winter-excursions-intro.markdown` as the parent \nof its neighboring Markdown files: `01-star-dust-snow-shoeing.markdown` and \n`02-lunar-alpine.markdown`. Likewise, `00-lunar-olympics-intro.markdown` is the\nparent of `01-zero-gravity-diving.markdown`. `00-lunar-olympics-intro.markdown` \nis also the peer of `01-lunar-rock-scrambling.markdown` and \n`02-extra-terrestrial-mountain-biking.markdown`, and the child of \n`00-summer-excursions-intro.markdown`.\n\n**ZIP Example's Resulting Relationships and Priorities**\n\n- `01-winter-events/00-winter-excursions-intro.markdown`\n    - **Article:** Winter Excursions\n    - **Relationship:** Peer of *Summer Excursions*\n    - **Priority:** **1.0**\n- `01-winter-events/01-star-dust-snow-shoeing.markdown`\n    - **Article:** Star Dust Snow Shoeing\n    - **Relationship:** Child of *Winter Excursions*\n    - **Priority:** 1.0\n- `01-winter-events/02-lunar-alpine.markdown` \n    - **Article:** Lunar Alpine\n    - **Relationship:** Child of *Winter Excursions*\n    - **Priority:** 2.0\n- `02-summer-events/00-summer-excursions-intro.markdown`\n    - **Article:** Summer Excursions\n    - **Relationship:** Peer of *Winter Excursions*\n    - **Priority:** **2.0**\n- `02-summer-events/01-lunar-rock-scrambling.markdown`\n    - **Article:** Lunar Rock Scrambling\n    - **Relationship:** Child of *Summer Excursions*\n    - **Priority:** 1.0\n- `02-summer-events/02-extra-terrestrial-mountain-biking.markdown`\n    - **Article:** Extra Terrestrial Mountain Biking\n    - **Relationship:** Child of *Summer Excursions*\n    - **Priority:** 2.0\n- `02-summer-events/03-summer-olympics/00-lunar-olympics-intro.markdown`\n    - **Article:** Lunar Olympics\n    - **Relationship:** Child of *Summer Excursions*\n    - **Priority:** 3.0\n- `02-summer-events/03-summer-olympics/01-zero-gravity-diving.markdown`\n    - **Article:** Zero Gravity Diving\n    - **Relationship:** Grandchild of *Summer Excursions*\n    - **Relationship:** Child of *Opening Ceremonies*\n    - **Priority:** 1.0\n\nZIP files must meet the following requirements: \n\n-  Each ZIP file must end in the suffix `.zip`.\n-  Each ZIP file must contain at least one Markdown source file, optionally \n   organized in folders.\n-  All referenced image files must be in a folder named `images` in the ZIP \n   file's root. \n-  Image files must be in a supported format and must use the appropriate file\n   extensions. Supported extensions are `.bmp`,`.gif`,`.jpeg`,`.jpg`, and \n    `.png`. They're specified via an app system setting. For details, see \n   [Knowledge Base System Settings](/docs/7-2/user/-/knowledge_base/u/knowledge-base-system-settings).\n\nOnce you have your article ZIP file, it's time to import it.\n\nFollow these steps to import your ZIP file:\n\n1.  In the Menu \n    (![Menu](../../../../images/icon-menu.png)), \n    navigate to *Site Administration* (the menu for your site) &rarr; *Content* \n    &rarr; *Knowledge Base* &rarr; *Articles*. \n\n2.  Click *Add* \n    (![Add](../../../../images/icon-add.png)) \n    &rarr; *Import* to bring up the importer page. \n\n3.  Browse to the location of your file, and in most cases leave the checkbox \n    for the article priorities checked, and then click *Save*. \n \nYour file is uploaded, and the importer converts each source file's Markdown \ntext to HTML, applying the HTML to the resulting article. Any image files that \nare referenced in an article and included in the ZIP file are imported as \nattachments to the article. \n\n![Figure 1: Selecting *Add* &rarr; *Import* in Knowledge Base brings up the interface for selecting a ZIP file of Markdown source files and images to produce and update articles in your Knowledge Base.](../../../../images/kb-admin-import.png)\n\nIn addition to source files and images, you can configure a base source URL \nsystem setting for the importer that specifies your source file's repository \nlocation. Each article's *Edit on GitHub* button (if enabled) takes the user to \nthe source location. The importer prefixes each file's path with the base source \nURL. This constructs a URL to the article's repository source location; it looks \nlike `[base URL]/[article file path]`. Here's an example base source URL: \n\n    https://github.com/liferay/liferay-docs/blob/master/develop/tutorials\n\nThe source URL constructed from this base URL and article source file \n`folder-1/some-article.markdown` would be:\n\n    https://github.com/liferay/liferay-docs/blob/master/develop/tutorials/folder-1/some-article.markdown\n\nYou specify the base source URL in a file called `.METADATA` in the ZIP file's\nroot folder. The importer treats the `.METADATA` file as a standard Java\nproperties file and uses the base source URL to construct the source URL for\nall of the ZIP file's resulting articles. \n\nTo use the source URL feature, your administrator must enable it via the \n[Knowledge Base System Settings](/docs/7-2/user/-/knowledge_base/u/knowledge-base-system-settings). \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/08-knowledge-base/10-kb-importer-faq.markdown",
    "content": "---\nheader-id: knowledge-base-importer-faqs\n---\n\n# Knowledge Base Importer FAQs\n\n[TOC levels=1-4]\n\n-   **What happens when I import an existing article?** \n\n    The importer checks if the source file's leading header ID (e.g., \n    `# Some Heading  [](id=some-heading)`) matches the URL title of any existing \n    article in the Knowledge Base folder. If a match is found, the importer \n    replaces the article's content with the incoming content converted from the \n    source file. If no match is found, a new article is created. \n\n-   **Do I need to import all of a Knowledge Base folder's articles, even if I \n    only want to create a new article or update a subset of the folder's current\n    articles?** \n\n    No. You can import as many or as few new or modified articles as you like. \n\n-   **Does the importer remove articles?** \n\n    No. The importer only creates and updates articles. It doesn't delete any \n    existing articles. To delete an article, you must manually do so via the \n    Knowledge Base app. \n\n-   **Can I update an article's priority?** \n\n    Yes. You can use the file/folder prefix convention and re-import the article \n    to update its priority. \n\n-   **If I change an article's title, should I also change its header ID?** \n\n    It depends on whether you've already published your article. If it hasn't \n    been published, then there are no public links to it, so it's fine to change \n    the header ID. If the article is already published, you must decide whether \n    it's worth breaking existing links to the article, and worth having search \n    engines rediscover and re-rank your article based on its new friendly URL. \n    The new friendly URL is based on the new header ID. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/08-knowledge-base/11-kb-system-settings.markdown",
    "content": "---\nheader-id: knowledge-base-system-settings\n---\n\n# Knowledge Base System Settings\n\n[TOC levels=1-4]\n\nAdministrators can use the System Settings UI to set the Knowledge Base's global \nconfiguration (across sites). You can access this UI in *Control Panel* &rarr; \n*Configuration* &rarr; *System Settings* &rarr; *Knowledge Base*. There are five \nsections of Knowledge Base configuration settings: \n\n-   Service\n-   Knowledge Base Article\n-   Knowledge Base Display\n-   Knowledge Base Search\n-   Knowledge Base Section\n\nThe *Service* section's settings apply defaults to all the Knowledge Base \nwidgets, and to the Knowledge Base app in Site Administration. The other \nsections apply to specific Knowledge Base widgets and override the \n*Service* defaults. \n\n| **Important:** Advanced configuration of the Knowledge Base application's \n| system settings should only be performed by an @product@ administrator. \n\nThe Knowledge Base has several optional features that are disabled by default,\nbut can be enabled and configured from System Settings. These include source\nURL, import file conventions, new article priority increment, and sections. \n\n## Source URL Settings\n\nThe source URL settings define the source location of Markdown files for import.\nThis should point to a source repository where the files are stored. GitHub is\nassumed as the default. Once defined, the Knowledge Base displays a button\n(default label is *Edit on GitHub*) above each displayed article. Users can\nclick the button to navigate to an article's source location.\n\nThe source URL settings are accessible in the *Service* section of the Knowledge \nBase's System Settings. To enable the source URL, check the *Source URL Enabled* \ncheckbox. \n\nTo change the source URL button's label, specify a new value for the setting \n*Source URL Edit Message Key*. Best practice is to specify the value as a \n[language key](/docs/7-2/customization/-/knowledge_base/c/overriding-language-keys). \nFor example, if you create a language key `edit-on-bitbucket=Edit on Bitbucket`, \nyou can specify that language key as the button's new label: \n\n    edit-on-bitbucket\n\nAlternatively, you can specify the label explicitly: \n\n    Edit on Bitbucket\n\n## Importer File Convention Settings\n\nThese settings define the supported file extensions, the suffix for parent\nfiles, and the image folder's path within the imported ZIP files. These settings \nare accessible in the *Service* section of the Knowledge Base's System Settings. \n\nThe following settings specify the importer's supported file extensions: \n\n**Markdown Importer article extensions:** Sets the supported article extensions.\nThe default values are `.markdown` and `.md`. \n\n**Markdown Importer Image File Extensions:** Sets the supported image file\nextensions. The default values are `.bmp`, `.gif`, `.jpeg`, `.jpg`, and `.png`. \n\nFollow these steps to modify the supported file extensions: \n\n1.  Click the *+* or *-* button next to the setting to add or remove a supported \n    file extension, respectively. \n \n2.  If adding an extension, enter a new value. \n\n3.  Click *Save*.\n\nThese settings define additional article configuration options for the importer: \n \n**Markdown Importer Article Intro:** Sets the parent article's file suffix. \nThe default value is `intro.markdown`. \n\n**Markdown Importer Image Folder:** Sets the image folder path the importer \nlooks for in the ZIP file. The default path is `/images`. \n\n**Article Increment Priority Enabled:** Whether to increment new article \npriorities by `1.0`. To disable this increment so that articles get a flat \nvalue of `1.0`, deselect the checkbox. \n\nAlternatively, you can enable or disable the article increment priority \nfeature for each widget in the corresponding widget's configuration menu in \nSystem Settings. \n\n## Section Names Setting\n\nThe section names setting lets you specify names of arbitrary topics to \nattribute to articles. Using the Knowledge Base Section widget, you can display \none or more *sections* (groups) of articles. To use sections, you must first \ndefine them in the System Settings for the *Knowledge Base Section* widget. \n\nFollow these steps to make new sections available: \n\n1.  Navigate to the *Knowledge Base Section* configuration menu. \n\n2.  Click the plus button next to the *Admin Knowledge Base Article Sections* \n    setting to add a new field for each section you want. \n \n3.  Enter a name for each new section and click *Update*. \n\n![Figure 1: Create the sections you want to use with the Knowledge Base Section widget.](../../../../images/kb-section-setting.png)\n\nOnce your sections are added, you can follow the steps in the \n[Knowledge Base Section documentation](/docs/7-2/user/-/knowledge_base/u/other-knowledge-base-widgets#knowledge-base-section) \nto learn how to use them.\n\n"
  },
  {
    "path": "en/user/articles/110-collaboration/05-collaborating/11-invites/01-invites-intro.markdown",
    "content": "---\nheader-id: inviting-members-to-your-site\n---\n\n# Inviting Members to Your Site\n\n[TOC levels=1-4]\n\nThe Invite Members widget lets site administrators send invitations to join the \nSite. You can add this widget to a page from the \nmenu *Add* \n(![Add](../../../../images/icon-add-app.png)) \n&rarr; *Widgets* &rarr; *Collaboration*. Click the *Invite members* button to \nbring up the interface for sending invitations. \n\n![Figure 1: You can invite users by clicking the add sign next to the user's name.](../../../../images/invite-members-dialog.png)\n\nClick the plus sign next to a User or click the *Add Email Address* button to \nadd a User to the invite list. Users that have already been invited but have not\nyet responded appear with a check mark next to their names. You can also invite\nUsers to the *Site Owner*, *Site Content Reviewer*, and *Site Administrator*\nRoles for your site by selecting that Role under the *Invite to Role* heading.\nOnce you've added all the Users you want to invite and have selected their\nRoles, click the *Send Invitations* button to invite them. For more information\non roles, see the \n[Roles and Permissions documentation](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions). \n\nThe Site invitation shows up under the *Requests List* tab on the User's \n*Notifications* page. The User can then choose to *Confirm* or *Ignore* the \ninvitation.\n\n![Figure 2: You can confirm or ignore the invitation.](../../../../images/invite-members-confirm.png)\n\nWhen Users confirm such invitations, they become Site members assigned to the \nRoles you defined. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/06-assets/01-intro.markdown",
    "content": "---\nheader-id: assets\n---\n\n# Assets\n\n[TOC levels=1-4]\n\nWhen you think of assets, you probably think of things like houses, cars, money, \nor gold bricks. Although @product@ is capable of holding many assets, they\naren't financial assets. Assets in @product@ are any kind of content. For\nexample, images, documents, and web content are all assets. And although such\nassets may not have the tangible value of a gold brick, they could equal or even\nexceed that value when used to support an organization's day-to-day business\nactivities. You might even think of @product@ as your very own Fort Knox (don't \nworry, we won't tell anyone). \n\nHere, you'll learn to configure @product@ to apply \n[tags](/docs/7-2/user/-/knowledge_base/u/tagging-content) \nto assets automatically. \n\n![Figure 1: The tags *freight car* and *electric locomotive* were automatically applied to this image.](../../../images/auto-tagging-images.png)\n\n"
  },
  {
    "path": "en/user/articles/110-collaboration/06-assets/02-configuring-auto-tagging.markdown",
    "content": "---\nheader-id: configuring-asset-auto-tagging\n---\n\n# Configuring Asset Auto Tagging\n\n[TOC levels=1-4]\n\n[Tagging assets](/docs/7-2/user/-/knowledge_base/u/tagging-content) \nis a great way to organize content. Typically, the content creator applies tags\nwhile creating the content. It's also possible, however, to tag content\nautomatically. For example, @product@ can scan an image on upload and\napply tags that describe the image's content. This lets you leverage tags\nwithout requiring content creators to apply them manually. \n\n| **Note:** Auto-tagging currently works only for images, text-based documents, \n| text-based web content, and blog entries. \n\nHere, you'll learn how to configure asset auto-tagging in general. This is \nrequired prior to configuring auto-tagging for specific asset types, which is \ndocumented separately: \n\n-   [Auto Tagging Images](/docs/7-2/user/-/knowledge_base/u/auto-tagging-images)\n-   [Auto Tagging Text](/docs/7-2/user/-/knowledge_base/u/auto-tagging-text)\n\n## Configuration Levels\n\nAuto-tagging is enabled by default. You can configure it at three levels:\n\n**Global (System):** For auto-tagging to function on any level, it must be \nenabled globally. You can also set the default auto-tagging configuration for \nevery portal instance. \n\n**Instance:** When enabled globally, auto-tagging is also enabled by default for \neach portal instance. However, you can override the global auto-tagging \nconfiguration on a per-instance basis. \n\n**Site:** When enabled for an instance, auto-tagging is also enabled by default \nfor all that instance's sites. You can disable it for specific sites. \n\n### Global Configuration\n\nFollow these steps to configure auto tagging globally: \n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr; \n    *Assets*. \n\n2.  Under *SYSTEM SCOPE*, select *Asset Auto Tagging*. \n\n3.  The following settings are available:\n\n    **Enable Auto Tagging of Assets:** Whether asset auto tagging is enabled.\n\n    **Maximum Number of Tags:** The maximum number of tags that can be \n    automatically applied to each asset. The default value of `0` means that \n    there is no limit.\n\n4.  Click *Save* to save your changes. \n\nTo set the default auto-tagging configuration for all instances, select \n*Asset Auto Tagging* under *VIRTUAL INSTANCE SCOPE*. The available settings are \nexactly the same as those in the SYSTEM SCOPE. \n\n![Figure 1: You can configure auto tagging globally in the Assets section of System Settings.](../../../images/auto-tagging-global.png)\n\n### Instance-level Configuration\n\nWhen enabled globally, auto-tagging is also enabled by default for each \ninstance. You can, however, disable or configure it for each instance. \n\nFollow these steps to configure auto tagging on the instance level: \n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *Instance Settings* \n    &rarr; *Assets*. \n\n2.  Under *VIRTUAL INSTANCE SCOPE*, select *Asset Auto Tagging*. \n\n3.  The settings here are identical to those in the global configuration, but \n    apply only to the current instance. \n\n3.  Click *Save* to save your changes. \n\n![Figure 2: You can also configure auto tagging for each instance.](../../../images/auto-tagging-instance.png)\n\n### Site-level Configuration\n\nWhen enabled for an instance, auto-tagging is also enabled by default for all \nthat instance's sites. You can, however, enable or disable it for each site. \n\nFollow these steps to configure auto tagging for a site: \n\n1.  Open the *Menu* \n    (![Product Menu](../../../images/icon-menu.png)), \n    click your site's name, and navigate to *Configuration* &rarr; *Settings*. \n\n2.  In the *General* tab, expand the *Asset Auto Tagging* section. Use the \n    toggle to enable or disable auto tagging for the site. \n\n3.  Click *Save* to save your changes. \n\n![Figure 3: You can enable or disable auto-tagging for a site.](../../../images/auto-tagging-site.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/06-assets/03-auto-tagging-images.markdown",
    "content": "---\nheader-id: auto-tagging-images\n---\n\n# Auto Tagging Images\n\n[TOC levels=1-4]\n\n[Tags](/docs/7-2/user/-/knowledge_base/u/tagging-content) \nhelp you find and organize files, including images. With \n[asset auto tagging enabled](/docs/7-2/user/-/knowledge_base/u/configuring-asset-auto-tagging), \nyou can also enable image auto tagging. Image auto tagging automatically tags \nimages uploaded to the Documents and Media Library. This lets you use tags \nwithout requiring anyone to apply them manually. \n\n| **Note:** Currently, tags applied automatically are English only. \n\nImage auto tagging is disabled by default. To use it, you must do two things: \n\n1.  Ensure that \n    [asset auto tagging](/docs/7-2/user/-/knowledge_base/u/configuring-asset-auto-tagging) \n    is enabled. Although it's enabled by default, administrators can disable it. \n\n2.  Ensure that an image auto tagging provider is enabled. These providers are\n    disabled by default. Here, you'll learn how to enable/disable them. \n\n| **Note:** Prior to Liferay DXP Fix Pack 1 and Liferay Portal CE GA2, you must \n| configure the providers shown here in *Documents and Media* instead of \n| *Assets* (in System/Instance Settings). \n\nThree such providers are available: \n\n[**TensorFlow:**](#configuring-tensorflow-image-auto-tagging) \nAn open-source library that provides machine learning capabilities. TensorFlow \nimage auto-tagging in @product@ is based on \n[TensorFlow's `LabelImage` sample for Java](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/java/src/main/java/org/tensorflow/examples/LabelImage.java), \nand uses the Inception5h model. Use this with caution, since its accuracy is \nlimited. \n\n[**Google Cloud Vision:**](#configuring-google-cloud-vision) \nUses the \n[Google Cloud Vision API](https://cloud.google.com/vision/) \nto automatically tag images. \n\n[**Microsoft Cognitive Services:**](#configuring-microsoft-cognitive-services) \nUses \n[Microsoft Cognitive Services](https://azure.microsoft.com/en-us/services/cognitive-services/) \nto automatically tag images. \n\n![Figure 1: The tags *freight car* and *electric locomotive* were automatically applied to this image.](../../../images/auto-tagging-images.png)\n\n## Configuring TensorFlow Image Auto Tagging\n\nFollow these steps to configure TensorFlow Image Auto Tagging: \n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr; \n    *Assets*. \n\n2.  Under *VIRTUAL INSTANCE SCOPE*, select *TensorFlow Image Auto Tagging*. The \n    following settings are available: \n\n    **Enable TensorFlow Image Auto Tagging:** Check this box to select whether\n    image auto-tagging is enabled by default in any instance that has asset auto\n    tagging enabled. Note that you can override this value for specific\n    instances, as the next section shows. \n\n    **Confidence Threshold:** TensorFlow assigns a confidence level between 0 \n    and 1 for each tag, where 1 is the highest confidence and 0 is the lowest. \n    This field sets the minimum confidence level that TensorFlow needs to apply \n    a tag. Higher values yield fewer tags because TensorFlow needs more \n    confidence before it applies a tag. Likewise, lower values yield more tags. \n\n3.  Click *Save* to save your changes. \n\nYou can override these settings for each instance from *Control Panel* &rarr;\n*Configuration* &rarr; *Instance Settings* &rarr; *Assets*. \n\n![Figure 2: Configure TensorFlow image auto-tagging for your portal instances.](../../../images/auto-tagging-tensorflow.png)\n\nTo optimize performance, you can also control the process that runs TensorFlow \nimage auto tagging: \n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr; \n    *Assets*. \n\n2.  Under *SYSTEM SCOPE*, select *TensorFlow Image Auto Tagging Process*. The \n    following settings are available: \n\n    **Maximum Number of Relaunches:** The maximum number of times the process is \n    allowed to crash before it is permanently disabled. \n\n    **Maximum Number of Relaunches Time Interval:** The time in seconds after \n    which the counter is reset. \n\n3.  Click *Save* to save your changes. \n\n![Figure 3: You can fine tune the process that runs the TensorFlow image auto tagging in the portal.](../../../images/auto-tagging-tensorflow-process.png)\n\n## Configuring Google Cloud Vision\n\nFollow these steps to configure Google Cloud Vision image auto tagging: \n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr; \n    *Assets*. \n\n2.  Under *VIRTUAL INSTANCE SCOPE*, select \n    *Google Cloud Vision Image Auto Tagging*. The following settings are \n    available: \n\n    **API Key:** The API key to use for the Google Cloud Vision API. For more \n    information, see \n    [Google's documentation on API keys](https://cloud.google.com/docs/authentication/api-keys). \n\n    **Enabled:** Whether Google Cloud Vision image auto tagging is enabled. \n\n3.  Click *Save* to save your changes. \n\nYou can override these settings for each instance from *Control Panel* &rarr; \n*Configuration* &rarr; *Instance Settings* &rarr; *Assets*. \n\n![Figure 4: The Google Cloud Vision provider requires an API key.](../../../images/auto-tagging-image-google.png)\n\n## Configuring Microsoft Cognitive Services\n\nFollow these steps to configure Microsoft Cognitive Services image auto tagging: \n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr; \n    *Assets*. \n\n2.  Under *VIRTUAL INSTANCE SCOPE*, select \n    *Microsoft Cognitive Services Image Auto Tagging*. The following settings \n    are available: \n\n    **API Key:** Your \n    [API key](https://azure.microsoft.com/en-us/try/cognitive-services/my-apis/?apiSlug=computer-services) \n    for the Computer Vision API V2. \n\n    **API Endpoint:** The endpoint for the Computer Vision API V2 (e.g., \n    `https://westcentralus.api.cognitive.microsoft.com/vision/v2.0`). \n\n    **Enabled:** Whether Microsoft Cognitive Services image auto tagging is \n    enabled. \n\n    For more information, see the \n    [Microsoft Cognitive Services documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/).\n\n3.  Click *Save* to save your changes. \n\nYou can override these settings for each instance from *Control Panel* &rarr; \n*Configuration* &rarr; *Instance Settings* &rarr; *Assets*. \n\n![Figure 5: The Microsoft Cognitive Services provider requires an API key and an endpoint.](../../../images/auto-tagging-image-microsoft.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/06-assets/04-text-auto-tagging.markdown",
    "content": "---\nheader-id: auto-tagging-text\n---\n\n# Auto Tagging Text\n\n[TOC levels=1-4]\n\nWith \n[asset auto tagging enabled](/docs/7-2/user/-/knowledge_base/u/configuring-asset-auto-tagging), \nyou can also configure text auto tagging. Text auto tagging automatically \n[tags](/docs/7-2/user/-/knowledge_base/u/tagging-content) \ntext-based assets. This lets you use tags without requiring anyone to manually\napply them. \n\n| **Note:** Currently, text auto tagging is only available for text-based \n| documents, text-based web content, and blog entries. Tags applied \n| automatically are English only. \n\nText auto tagging is disabled by default. To use it, you must enable it: \n\n1.  Ensure that \n    [asset auto tagging](/docs/7-2/user/-/knowledge_base/u/configuring-asset-auto-tagging) \n    is enabled. Although it's enabled by default, administrators can disable it. \n\n2.  Ensure that a text auto tagging provider is configured and enabled for the \n    asset types you want to auto tag. You'll learn how to do this here. Note \n    that these providers aren't configured or enabled by default. \n\n| **Note:** Prior to Liferay DXP Fix Pack 1 and Liferay Portal CE GA2, you must \n| enable these providers separately for each content type in System/Instance \n| Settings. For example, you must enable text auto tagging for documents and \n| web content in *Documents and Media* and *Web Content*, respectively. \n\nThere are two text auto-tagging providers in the portal: \n\n[**Google Cloud Natural Language Text Auto Tagging:**](#configuring-google-cloud-natural-language-text-auto-tagging) \nUses the \n[Google Cloud Natural Language API](https://cloud.google.com/natural-language/) \nto analyze and automatically tag portal content. \n\n[**OpenNLP Text Auto Tagging:**](#configuring-opennlp-text-auto-tagging) \nUses the open source \n[Apache OpenNLP](https://opennlp.apache.org/) \nlibrary to analyze and automatically tag portal content. Three models are used: \nlocation name finder, organization finder, and person name finder. Use this \nprovider with caution, as its accuracy may be limited. \n\n## Configuring Google Cloud Natural Language Text Auto Tagging\n\nFollow these steps to configure the auto-tagging provider for the Google Cloud \nNatural Language API: \n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr; \n    *Assets*. \n\n2.  Under *VIRTUAL INSTANCE SCOPE*, select \n    *Google Cloud Natural Language Text Auto Tagging*. The following settings \n    are available: \n\n    **API Key:** The API key to use for the Google Cloud Natural Language API. \n    For more information, see \n    [Google's documentation on API keys](https://cloud.google.com/docs/authentication/api-keys). \n\n    **Classification Endpoint Enabled:** Whether to enable auto tagging of text \n    using the Google Cloud Natural Language API Classification endpoint. \n\n    **Confidence:** Set the classifier's confidence of the category. This number \n    represents how certain the classifier is that this category represents the \n    given text. \n\n    **Entity Endpoint Enabled:** Whether to enable auto tagging of text using \n    the Google Cloud Natural Language API Entity endpoint. \n\n    **Salience:** The salience score for an entity provides information about \n    the importance or centrality of that entity to the entire text. \n\n    **Enable Google Cloud Natural Language Text Auto Tagging For:** The asset \n    types to enable text auto tagging for. Use the menu to select *Document*, \n    *Blogs Entry*, or *Web Content Article*. To add multiple asset types, click \n    the *Add* icon \n    (![Add](../../../images/icon-add.png)) \n    and select the asset type from the menu. You can delete any additional asset \n    types by clicking the Trash icon \n    (![Trash](../../../images/icon-trash-list.png)).\n\n3.  Click *Save* to save your changes. \n\nYou can override these settings for each instance from *Control Panel* &rarr; \n*Configuration* &rarr; *Instance Settings* &rarr; *Assets*. \n\n![Figure 1: Configure Google Cloud Natural Language text auto tagging for your portal instances.](../../../images/auto-tagging-text-google.png)\n\n## Configuring OpenNLP Text Auto Tagging\n\nFollow these steps to configure the OpenNLP Text Auto Tagging provider: \n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings* &rarr; \n    *Assets*. \n\n2.  Under *VIRTUAL INSTANCE SCOPE*, select *OpenNLP Text Auto Tagging*. The \n    following settings are available: \n\n    **Confidence Threshold:** Set the minimum confidence threshold (from 0 to 1, \n    where 1 is the highest confidence) above which tags will be applied. Higher \n    values yield fewer tags because the provider needs more confidence before it \n    applies a tag. Likewise, lower values yield more tags. \n\n    **Enable OpenNLP Text Auto Tagging For:** The asset types to enable text \n    auto tagging for. Use the menu to select *Document*, *Blogs Entry*, or \n    *Web Content Article*. To add multiple asset types, click the *Add* icon \n    (![Add](../../../images/icon-add.png)) \n    and select the asset type from the menu. You can delete any additional asset \n    types by clicking the Trash icon \n    (![Trash](../../../images/icon-trash-list.png)). \n\n3.  Click *Save* to save your changes. \n\nYou can override these settings for each instance from *Control Panel* &rarr; \n*Configuration* &rarr; *Instance Settings* &rarr; *Assets*. \n\n![Figure 2: Configure OpenNLP text auto tagging for your portal instances.](../../../images/auto-tagging-text-open-nlp.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/07-social-network/01-intro.markdown",
    "content": "---\nheader-id: creating-a-social-network\n---\n\n# Creating A Social Network\n\n[TOC levels=1-4]\n\n@product@ contains several features and widgets for leveraging its social\nframework. The Activities widget lets you broadcast user activities on a Site.\nThis is a good way for Site members to see what's going on in their communities.\nWhen placed on a user's private Dashboard, the Activities widget displays the\nactivities of that user's social connections. Users can make those social\nconnections via the Contacts Center app, which lets them establish connections\nand followers throughout the portal. What's more, widgets can be exported as\nOpenSocial gadgets and/or used with Facebook. \n\nThis guide shows you how to do these things, and more. \n\n![Figure 1: The Activities widget shows information about asset-related user activity in the current Site.](../../../images/activities-widget.png)\n\n![Figure 2: Users get a notification that lets them respond to connection requests.](../../../images/connection-request.png)\n\n![Figure 3: The Contacts Center widget lets users make connections.](../../../images/contacts-center.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/07-social-network/02-activities-widget.markdown",
    "content": "---\nheader-id: using-the-activities-widget\n---\n\n# Using the Activities Widget\n\n[TOC levels=1-4]\n\nThe core social widget is Activities. It displays information about user\nactivity on the Site where you added it. User activities tracked include\nupdates to the Documents and Media library, blog posts, message boards posts,\nwiki pages, and bookmarks. @product@ also tracks information about web content\nbut only displays this information if the logged-in user is a Site\nadministrator. This widget provides a summary of recent Site activity. You can\nuse it on a Site's public or private pages to show what Site members have been\nup to, or you can use it on the public or private pages of a user's personal\nSite. When added to a personal Site, the Activities widget shows the activities \nof only that user. \n\nAdd the Activities widget to a page from the *Add* \n(![Add](../../../images/icon-add-app.png)) \n&rarr; *Widgets* &rarr; *Social* menu. \n\n![Figure 1: The Activities widget shows information about asset-related user activity in the current Site.](../../../images/activities-widget.png)\n\nNote that the widget provides links to the assets listed in the feed. These \nlinks don't work, however, unless there's a way to display the assets on the \npage. For example, if you click a link to a blog post in the Activities widget, \nthat page must have a Blogs widget to display that blog post. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/07-social-network/03-connecting-users.markdown",
    "content": "---\nheader-id: connecting-users\n---\n\n# Connecting Users\n\n[TOC levels=1-4]\n\nBy adding the Contacts Center and Activities widgets to users' private \nDashboards, administrators can let users form social connections. The Contacts \nCenter widget lets users form these connection types: \n\n**Connection:** A two-way relationship formed by a user accepting a connection \nrequest from another user. The Activities widget on each user's private \nDashboard displays the activities of the other user. \n\n**Following:** A one-way relationship in which one user follows another user. \nThe followed user's activities appear in the Activities widget on the follower's \nprivate Dashboard. \n\nTo ensure that all users have a page on their private Dashboard that contains \nthe Contacts Center and Activities widgets, you can create a user group and then\ncreate a page via a Site Template. For instructions on this, see the \n[user groups documentation](/docs/7-2/user/-/knowledge_base/u/user-groups). \nThe widgets are in *Add* (![Add](../../../images/icon-add.png)) &rarr; *Widgets*\n&rarr; *Social*. \n\n![Figure 1: The Contacts Center widget lets users make connections.](../../../images/contacts-center.png)\n\nWhen you select a user in the Contacts Center, these buttons appear across the \ntop of the widget: \n\n**Connect:** Send a connection request to the user. \n\n**Disconnect:** Disconnect from the user.\n\n**Follow:** Follow the user.\n\n**Unfollow:** Stop following the user.\n\n**Block:** Block the user. Blocking a user only prevents that user from \nfollowing you or adding you as a connection. A blocked user can still send \nmessages to and view the public profile information of the blocking user.\n\n**Unblock:** Stop blocking the user.\n\n**vCard:** Export the user's vCard and save it as a VCF file. vCard is a \nfile format standard for electronic business cards. \n\nWhen you send a connection request, the user is notified and can confirm or\nignore the request. \n\n![Figure 2: Users get a notification that lets them respond to connection requests.](../../../images/connection-request.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/07-social-network/04-exporting-widgets.markdown",
    "content": "---\nheader-id: exporting-widgets-to-other-websites\n---\n\n# Exporting Widgets To Other Websites\n\n[TOC levels=1-4]\n\nYou can publish its widgets to other websites via OpenSocial. This lets you\nprovide your widget or content in the context of another website. Read on to\nfind out how this is done. \n\n## Sharing OpenSocial Gadgets\n\nOpenSocial consists of a set of APIs for social networking. It may be beneficial\nfor you to share widgets from your server with other sites, such as \n[ighome.com](http://ighome.com) or\n[igoogleportal.com](http://igoogleportal.com). \nThese sites let users customize their own pages with gadgets. Users can share \n@product@ widgets on any OpenSocial-compatible site. @product@ does this by \nsharing the widget's URL with the OpenSocial platform. This URL is unique to the \nuser's specific widget instance. Users can therefore share multiple instances of \nthe same widget as different OpenSocial gadgets. \n\nFollow these steps to share a widget with OpenSocial: \n\n1.  Add the widget you want to share to a site page. \n\n2.  Click the widget's *Options* icon \n    (![Options](../../../images/icon-app-options.png)) \n    and select *Configuration*. \n\n3.  In the *Sharing* tab, open the *OpenSocial Gadget* section. \n\n4.  Set the selector to *YES* for \n    *Allow users to add [widget name] to an OpenSocial platform*. In the \n    *OpenSocial Gadget URL* field, replace `localhost:8080` with the name of \n    your public domain and port. \n\n5.  Click *Save*. \n\n6.  Close the dialog and click the widget's *Options* icon \n    (![Options](../../../images/icon-app-options.png)). \n    There's a new option available named *Add to an OpenSocial Platform*. Select \n    this option to add the widget to a page on an OpenSocial platform. \n\n![Figure 1: You can share widgets via OpenSocial.](../../../images/open-social-sharing.png)\n"
  },
  {
    "path": "en/user/articles/110-collaboration/07-social-network/05-facebook.markdown",
    "content": "---\nheader-id: integrating-with-facebook\n---\n\n# Integrating with Facebook\n\n[TOC levels=1-4]\n\n@product@ provides tools for integrating your portal and its content with \nFacebook. For example, you can use Facebook for authentication and even export \nwidgets as Facebook applications. This article shows you how. \n\n## Facebook Sign On\n\nLike many websites you may visit, any site running on @product@ can use\nFacebook for sign in. This makes it easier for users to sign in to your \nSite, since they don't need to remember another user name and password. \n\n<!--Uncomment once article is available\nSee the\ndeployment documentation\nfor information on using Facebook to sign into @product@. \n-->\n\n## Using Your Widget as Facebook Applications\n\nYou can add any @product@ widget as an app on Facebook. To do this, you must \nfirst get a developer key. A link for doing this is provided to you in the \nFacebook tab in any widget's Configuration screen. You must create the app on \nFacebook and get the key and canvas page URL from Facebook. You can then copy \nand paste their values into the Facebook tab. Once you do that, your widget is \navailable on Facebook. \n\nThis integration lets you make things like Message Boards, Calendars, Wikis, and \nother content on your website available to a much larger audience (unless you \nalready have a billion users on your site, in which case, kudos to you). If \nyou're a developer, you can implement your widget on @product@ and then publish \nit to Facebook. \n"
  },
  {
    "path": "en/user/articles/110-collaboration/07-social-network/06-social-bookmarks.markdown",
    "content": "---\nheader-id: using-social-bookmarks\n---\n\n# Using Social Bookmarks\n\n[TOC levels=1-4]\n\nSocial bookmarks appear below content as buttons for sharing that content on\nsocial networks. For example, social bookmarks appear by default in the Blogs\nwidget below each blog post. For more information on configuring social\nbookmarks in the Blogs widget, see the documentation on \n[displaying blogs](/docs/7-2/user/-/knowledge_base/u/displaying-blogs). \n\nThese social bookmarks are available by default: \n\n-   Twitter\n-   Facebook\n-   LinkedIn\n\n![Figure 1: The default social bookmarks appear in a menu below content.](../../../images/social-bookmarks-menu.png)\n\nYou can install the Social Bookmarks app from Liferay Marketplace. This app is\navailable for \n[Liferay CE Portal](https://web.liferay.com/marketplace/-/mp/application/15194315) \nand \n[Liferay DXP](https://web.liferay.com/marketplace/-/mp/application/15188453). \nIt adds the following social bookmarks: \n\n-   AddThis\n-   Delicious\n-   Digg\n-   Evernote\n-   Reddit\n-   Slashdot\n\nIf you need help installing apps from Liferay Marketplace, see \n[Using the Liferay Marketplace](/docs/7-2/user/-/knowledge_base/u/using-the-liferay-marketplace). \n"
  },
  {
    "path": "en/user/articles/120-search/01-search-intro.markdown",
    "content": "---\nheader-id: search\n---\n\n# Search\n\n[TOC levels=1-4]\n\nSites often feature lots of content split over lots of asset types.  Web content\narticles, documents and media files, and blogs entries are just a few examples.\nMost content types are *assets*. Under the hood, assets use the\n[Asset API](/docs/7-2/frameworks/-/knowledge_base/f/asset-framework) \nand have an Indexer class. Any content that has these features can be\nsearched. \n\n![Figure 1: The Type Facet configuration lists the searchable out-of-the-box asset types.](../../images/search-assets.png)\n\n## Elasticsearch\n\nThe default search engine is Elasticsearch, which is backed by the Lucene\nsearch library. There's an Elasticsearch server embedded in all bundles, which\nis handy for testing and development purposes. Production environments must\ninstall a separate, remote Elasticsearch server (or even better, cluster of\nservers).  For information on how to install Elasticsearch, read the \n[deployment guide](/docs/7-2/deploy/-/knowledge_base/d/elasticsearch).\n\n## Search Features\n\nSearching is simple and straightforward. Find a search bar (there's one embedded\nin every page by default), enter a term, and click *Enter*.\n\n![Figure 2: There's a search bar embedded on all pages by default.](../../images/search-bar.png)\n\nAfter search is triggered, a results page appears. If there are hits to search\nengine documents, they appear as search results in the right hand column. In the\nleft hand column are search facets.\n\n![Figure 3: Results are displayed in the Search Results portlet.](../../images/search-results.png)\n\nThe search bar, search results, and search facets make up three powerful\nfeatures in the search UI.\n\n### Search Bar\n\nThe search bar is simple: it's where you enter *search terms*. Search terms are\nthe text you send to the search engine to match against the documents in the\nindex. \n\n### Search Results and Relevance\n\nThe search term is processed by an algorithm in the search engine, and search\nresults are returned to users in order of relevance. Relevance is determined by\na document's *score*, generated against the search query. The higher the score,\nthe more relevant a document is considered. The particular relevance algorithm\nused is dependent on \n[algorithms provided by the search engine (Elasticsearch by default)](https://www.elastic.co/guide/en/elasticsearch/reference/current/relevance-intro.html#relevance-intro).\n\n### Search Facets\n\nFacets allow users of the Search application to filter search results. Think of\nfacets as buckets that hold similar search results. You might want to see the\nresults in all the buckets, but after scanning the results, you might decide\nthat the results of just one bucket better represent what you want. So what\nfacets are included out of the box?\n\n- Category\n- Folder\n- Site\n- Tag\n- Type\n- User\n- Modified\n- Custom\n\n![Figure 4: *Site* and *Type* are two of the facet sets you'll encounter. They let you drill down to results that contain the search terms you entered.](../../images/search-faceted-search.png)\n\nYou've probably used something similar on any number of sites. You search for an\nitem, are presented with a list of results, and a list of buckets you can click\nto further drill down into the search results, without entering additional\nsearch terms. Search facets work the same way. Facets are, of course,\n[configurable](/docs/7-2/user/-/knowledge_base/u/facets).\n"
  },
  {
    "path": "en/user/articles/120-search/02-what-new-search/01-whats-new-search-intro.markdown",
    "content": "---\nheader-id: whats-new-in-search\n---\n\n# What's New in Search\n\n[TOC levels=1-4]\n\nLots of new and improved search capabilities are present in @product-ver@, from\nnew widgets to new APIs and infrastructure. \n\n## New and Improved Widgets\n\nAdd search widgets by clicking the Add\n(![Add](../../../images/icon-add-widget.png)) icon on the page. Then expand\nthe Widgets &rarr; Search section.\n\n### Custom Filter\n\n**New Widget**\n\nAdd a widget to the page for each of the filters you'd like applied to the\nsearch results. Let search page users see and manipulate the filters or make\nthem invisible and/or immutable (this is just a cool word for \"they can't be\nchanged\").\n\nFor example, add a custom filter to ensure that all returned results have the\nkeyword _street_ in the content field.\n\n### Sort\n\n**New Widget**\n\nThe Sort widget reorders the results based on the value of certain `keyword`\nfields in the index. For example, show results in alphabetic order of the Title\nfield. The default order is determined by the search engine's _Relevance_\ncalculation.\n\nAdd more fields to the sort widget if the default options aren't enough. Click\nthe widget Options (![Options](../../../images/icon-app-options.png)) menu &rarr;\nConfiguration. Enter a human readable label and the `fieldName` to sort by. Just\nmake sure it's a `keyword` field.\n\n### Search Insights\n\n**Improved**\n\nPast versions of Search Insights showed you the full query string sent to the\nsearch engine, but now it also displays the response from the search engine with\nan explanation of the score for each search hit.\n\n## New Search Admin Functionality\n\nThe Search Admin functionality is found in Control Panel &rarr; Configuration\n&rarr; Search.\n\n<!-- COMMENTING: Will be added by FP-1d### DXP Only: Synonyms\n\nAdd a list of synonyms. At search time, the list is parsed to match results for\nsynonymous search keywords.\n-->\n### Search Engine Info\n\nThe displayed Search Engine information is enhanced, showing the client and node\ninformation as well as the vendor and operation mode.\n\n### Field Mappings\n\nThe Field Mappings tab shows the field mappings for all indexes in the search\nengine. \n\n### Indexing Progress\n\nIndexing now displays a progress bar so you can see in the UI when the\nre-indexing action has completed.\n\n## New System Settings\n\nAccess the Search System Settings at Control Panel &rarr; Configuration\n&rarr; System Settings &rarr; Search.\n\nThere's a new entry in the search category: _Title Field Query Builder_. Use it\nto configure how search responds to matches on the Title field of a document.\n\n**Exact Match boost:** Give an additional boost when searched keywords exactly\nmatch the `title` field of a document.\n\n**Maximum Expansions:** Limit the number of documents to return when matching\nsearched keywords to the `title` field as a phrase prefix. See Elasticsearch's\n[Match Phrase Query\ndocumentation](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-match-query-phrase.html)\nfor more information.\n\n## New Infrastructure\n\nThere are some important search infrastructure changes to know about.\n\n### Elasticsearch Support\n\n@product-ver@ supports Elasticsearch 6.5.x through 6.8.x, and 6.5.0 is included\nas the embedded version to use for testing out-of-the-box search behavior. See\nthe \n[deployment guide](/docs/7-2/deploy/-/knowledge_base/d/elasticsearch)\nfor more information.\n\n### Application-Specific Indexes\n\nYou'll notice more search indexes in @product-ver@. That's because you can now\nconfigure application-specific indexes. At the time of this writing, the\nadditional indexes are all related to the DXP Workflow Metrics feature. More\nwill likely appear in future versions, and third party developers can use the\n`portal-search` APIs to create their own indexes. It's under development, so\nvisit the [Search Framework\ndocumentation](/docs/7-2/frameworks/-/knowledge_base/f/search) \nfrequently to discover new search infrastructure changes that expose more\nfunctionality for developers.\n\n### API Enhancements\n\nEnhancements to the [search\nframework](/docs/7-2/frameworks/-/knowledge_base/f/search) APIs include\n\n- Low level indexing and queries\n- Operations directly on indexed documents (no need for the Indexer framework)\n- New Aggregation types\n\n## Multi-Language Search\n\n@product@'s support for multi-language search took a step forward, with\nimprovements to Documents and Media and Web Content. More improvements are\nnecessary in this area and will be prioritized in future releases. See the\n[Multi-Language Search article for more\ninformation](/docs/7-2/user/-/knowledge_base/u/searching-for-localized-content). \n\n"
  },
  {
    "path": "en/user/articles/120-search/03-configuring-a-search-page/01-configuring-a-search-page-intro.markdown",
    "content": "---\nheader-id: configuring-search-pages\n---\n\n# Configuring Search Pages\n\n[TOC levels=1-4]\n\nThere are multiple ways to skin the search cat (disclaimer: no actual cats were\nharmed during the writing of this article). \n\nIf you're unsure which approach to take, use the\n[default](#default-search-pages) configuration. It provides a sensible starting\npoint that can be modified later, as needed. \n\nIf you've been using @product@ for a long time and like the search experience\nyou've always used, use the [legacy approach](#legacy-search-experience). \n\nIf you're in need of a fully customized experience, \n[manually configure](#manual-search-page-configuration) the search experience. \n\nAfter choosing your approach and reading here to get it up and running, find\nthe articles on the [Search\nBar](/docs/7-2/user/-/knowledge_base/u/searching-for-assets#search-bar),\n[Search Facets](/docs/7-2/user/-/knowledge_base/u/facets), and [Search Results](/docs/7-2/user/-/knowledge_base/u/search-results) to understand the full suite of\nconfiguration options. \n\nSearch display pages are where users go to enter search terms and browse search\nresults. \n\n##  Search Page Templates\n\nThe default search page is backed by a Search Page Template, and manually\nconfigured search pages can use the template, too. The template can be used in\ntwo ways:\n\n1.  Enable inheriting changes to the template, if you want the search page to\n    get any updates made to the template at a later date. \n\n2.  Create the page based on the template, but independently configured after\n    the initial creation.\n\nOut of the box, the Search Page Template includes these widgets:\n\n- [Search Bar](/docs/7-2/user/-/knowledge_base/u/searching-for-assets#search-bar)\n\n- [Suggestions](/docs/7-2/user/-/knowledge_base/u/searching-for-assets#search-suggestions)\n\n- [Search Results](/docs/7-2/user/-/knowledge_base/u/search-results)\n\n- [Search Options](/docs/7-2/user/-/knowledge_base/u/configuring-search#widget-scoped-search-configuration)\n\n- [Site Facet](/docs/7-2/user/-/knowledge_base/u/site-facet): This widget is\n    hidden unless the Search Bar is configured to search the _Everything_ scope\n    and results from multiple sites are returned.\n\n- [Type Facet](/docs/7-2/user/-/knowledge_base/u/type-facet)\n\n- [Tag Facet](/docs/7-2/user/-/knowledge_base/u/tag-and-category-facets)\n\n- [Category Facet](/docs/7-2/user/-/knowledge_base/u/tag-and-category-facets)\n\n- [Folder Facet](/docs/7-2/user/-/knowledge_base/u/folder-facet)\n\n- [User Facet](/docs/7-2/user/-/knowledge_base/u/user-facet)\n\n- [Modified Facet](/docs/7-2/user/-/knowledge_base/u/modified-facet)\n\nOut of the box, widgets use the _Barebone_ Application Decorators: unless\nthere's content to render in the widget, the widget body is hidden. The\nheader is displayed if you hover over it. \n\n![Figure 1: At first glance, not much is happening on the search page. But, there's more than meets the eye.](../../../images/search-barebone-widgets.png)\n\nBecause of this, when you visit a search page created from the default search\npage template, you won't see certain widgets fully rendered.\n\nBy contrast, when you add a search widget to a page manually, they use the\n_Borderless_ decorator (by default), which shows more of the widget even when\nthere is no content to display.\n\n## Default Search Pages\n\nUsing the default site and the default theme with the default search settings,\nthe out-of-the-box search experience has two components for end users:\n\n1. A search bar embedded on each page.\n2. A default search page where search requests are routed.\n\nBehind the scenes, The search bar widget points to a hidden search page with the\nfriendly URL `/search`.\n\n![Figure 2: By default, the embedded search bar points to the pre-configured `/search` destination page.](../../../images/search-dest-page.png)\n\nEnter a search term and you're redirected to the default search page, where\nresults are displayed in the Search Results widget.\n\n![Figure 3: The default page is pre-configured with the Search Results widget and the various Facet widgets to provide a full search experience.](../../../images/search-default-page.png)\n\nThe default search page is based on a Search page template, but it doesn't\ninherit changes from the page template by default. That means you can customize\nthe search page directly without changing the template's inheritance\nconfiguration.\n\n![Figure 4: Configure the Search page. By default, it doesn't inherit changes from the page template.](../../../images/search-page-config.png)\n\nIf you require just a few changes to the default page, don't abandon it and\ncreate one manually. Just make the configuration changes you need, including\nadding, configuring, and removing widgets on the page. On the other hand, if you\nwant a clean break from the default search page, starting from scratch is also\nan option.\n\n## Manual Search Page Configuration\n\nIt's reasonable to create the search experience from the ground up. If you're\nworking from a newly created site, it's a necessity. These steps show you how to\nswitch to a manually configured search experience in the default site, but you\ncan skip the step on deleting the default search page if you're starting with\na new site:\n\n1.  Delete the existent search page by navigating to the default site's menu and\n    clicking *Site Builder* &rarr; *Pages*. Click the Search page's Actions menu\n    icon (![Actions](../../../images/icon-actions.png)) and select *Delete*.\n    Confirm you want to delete the page, and it's gone.\n\n    Once deleted, the search bar disappears from your site pages, replaced by a\n    warning message visible only to site administrators:\n\n    ![Figure 5: The search bar is only visible if it points to an existent page.](../../../images/search-bar-warning.png)\n\n2.  Create a new page named whatever you want (_Finders Keepers_, perhaps). Make\n    it hidden or add it to the navigation as you please (the default search\n    page is hidden from the navigation).\n\n    If you want a pre-configured search page, create it from the Search page\n    template. Find the template in the Add Site Page form. It's under *Global\n    Templates*.\n\n    ![Figure 6: There's a handy page template for creating search pages.](../../../images/search-page-template.png)\n\n3.  If you're creating a page not backed by the template, add and configure all\n    the widgets you need. You'll find all the available search widgets in the\n    Add Widget menu's Search section. Lay them out however you want on the page.\n\n4.  Configure the search bar at the top of the page, making sure it points to your\n    new search page's friendly URL (for example, `/finders-keepers`). \n\n    Click the Search Bar widget's Options menu (![Options](../../../images/icon-app-options.png)).\n\n    Click *Configuration* and set the Destination Page to the search page's\n    friendly URL.\n\n    Click *Save*.\n\nNow your search page is up and running.\n\n## Legacy Search Experience\n\nIn prior versions, the search experience was encapsulated in one application,\n*Search*. It was embedded in the default theme, just like the search bar is now.\nIt looked very similar, with only the search bar visible in the default view of\nthe application. Once a search term is entered, the maximized view of the\napplication is presented, with all the search facets and results now in view. It\nlooks a lot like the new search behavior, only its monolithic structure means\nit's difficult to customize. If you liked the old application, it's still\navailable. Enable it with these steps:\n\n1.  Delete the default search page. From the site menu, click *Site Builder*\n    &rarr; *Pages*. Click the Actions menu\n    (![Actions](../../../images/icon-actions.png)) for the Search page and choose\n    *Delete*. \n\n2.  Enable the legacy search application. Go to Control Panel &rarr;\n    Configuration &rarr; System Settings &rarr; Search &rarr; Search Web and\n    check the box for *Classic Search Widget in Front Page*.\n\nNow your portal's search is backed by the legacy Search application, and it's\nembedded on each page in the default theme. To add the legacy Search application\nto a page, open the Add Widget menu, find the Search widget under the Tools\ncategory, and drag and drop it onto the page.\n\nConfigure the portal's search behavior to suit your needs. Here you've seen\nthree distinct search configurations.\n"
  },
  {
    "path": "en/user/articles/120-search/04-searching-for-assets/01-searching-for-assets-intro.markdown",
    "content": "---\nheader-id: searching-for-assets\n---\n\n# Searching for Assets\n\n[TOC levels=1-4]\n\nAs explained in the \n[Search introduction](/docs/7-2/user/-/knowledge_base/u/search), \nall indexed assets can be returned as search results. Developers can create\ntheir own assets, so your installation might have additional asset types beyond\nthe ones included by default. \n\n| **Searching for Users:** When you click an asset in the search results, it's\n| displayed in an Asset Publisher (unless the *View in Context* option is\n| selected in the Search Results portlet). Users are different, though. Think of\n| them as invisible assets, not intended for display in the Asset Publisher\n| application.  While Users appear as search results with other indexed assets,\n| when you click one you're taken to the User's profile page. If public personal\n| pages are disabled, clicking on a User from the list of search results shows\n| you a blank page.\n\n## Search Bar\n\nUsers enter the search context in the search bar. Users enter search terms, hit\nthe *Enter* button (or click the magnifying glass icon), and they're taken to\na [search page](/docs/7-2/user/-/knowledge_base/u/configuring-search-pages)\nwith various search widgets deployed. \n\nIf using the Search Bar in the legacy search portlet, users see a maximized view\nof the search portlet displaying any results and facets that apply. See the\narticle on [configuring search\npages](/docs/7-2/user/-/knowledge_base/u/configuring-search-pages#legacy-search-experience)\nto learn more about these options.\n\n![Figure 1: The default search configuration displays a search bar in its default view, beckoning users to enter the search context.](../../../images/search-bar.png)\n\n### Entering Search Terms\n\nLiferay's search infrastructure supports full text search as implemented by its\nsupported search engines\n([Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/full-text-queries.html)\nand [Solr](http://lucene.apache.org/solr/features.html)).\n\nFull text search compares all the words entered in a search query (for example,\n*space vacation*) to all the words in each index document. A search engine like\nElasticsearch calculates relevance scores to ensure the best results are\nreturned first (like a Blogs Entry titled *Is a vacation in space right for\nyou?*) and lots of matching results are returned (anything with either the\nword *vacation* or *space* is returned). \n\nIn addition to full text search, advanced search syntax is supported. @product@\nrelies on the underlying search engine for this behavior, so consult the\n[Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-query-string-query.html#query-string-syntax)\nor \n[Solr](https://lucene.apache.org/solr/guide/6_6/query-syntax-and-parsing.html)\ndocumentation for the details.\n\n![Figure 2: Search for text in a specific field using Elasticsearch's Query String syntax.](../../../images/search-advanced-syntax.png)\n\n### Matching Exact Phrases: Quoted Search\n\nWhat if users want their search terms (for example, _agile frameworks_) to produce\nonly results with the exact phrase, as typed? In a regular full text search,\nsearching _agile frameworks_ returns search results containing just the terms\n_agile_ and _frameworks_, and hits containing both terms but separated by other\ntext, as well as results with the exact phrase match. To ensure that only hits\nwith the exact phrase are returned, enclose it in quotes: _\"agile frameworks\"_.\n\n![Figure 3: Search for exact phrase matches by enclosing search terms in quotes. If a user searched for _\"agile frameworks\"_, this result would not be returned.](../../../images/search-quoted.png)\n\n### Prefix Searching\n\nIf you're searching in a site for classical musicians, you might search for the\nterm *instrument*. This search returns documents with the full word in them, but\nit also returns variants with *instrument* as the prefix. For example, results\nwith *instruments*, *instrumental*, and *instrumentation* are also returned.\n\n![Figure 4: Searching for *lever* also returns *leverage* and *leveraging*.](../../../images/search-prefix.png)\n\n| **Note:** Prefix searching is available for many fields out of the box, but as\n| with most things related to search behavior, it's more complicated under the\n| hood. The details of the field mapping, including the analyzer used on the field\n| and any transformations performed, determine the final behavior.\n\nAnother way to ensure users see results is through \n[search suggestions](#search-suggestions).\n\n### Configuring the Search Bar\n\nConfigure the Search Bar's behavior via its portlet configuration screen.\n\n![Figure 5: Configure the search bar behavior in its configuration screen.](../../../images/search-bar-configuration.png)\n\n| **Note:** When you configure the globally embedded Search Bar widget at the top\n| of one page, it configures the page-top Search Bar widget on all pages in the\n| site. It also overrides the destination\n| [Search Page's](/docs/7-2/user/-/knowledge_base/u/configuring-search-pages)\n| Search Bar portlet, if they're configured differently. However, it does not\n| override Search Bar widgets manually placed on other pages.\n\nThere are several options:\n\n**Keywords Parameter Name**\n: Edit the parameter name for the keywords entered in the search. For example,\nthe default URL when searching for the keyword term _data_ looks like\nthis: \n\n    http://localhost:8080/web/guest/search?q=data\n\nIf you change the Keywords Parameter Name to _keyword_ it looks like this:\n\n    http://localhost:8080/web/guest/search?keyword=data\n\n**Scope** \n: Choose between three options: This Site (default), Everything, and Let the\nUser Choose. *This Site* means only the assets associated with the site where the\nsearch is executed are searched. Expand the scope of the search to all sites by\nselecting *Everything*. To let users choose which scope they want to search,\nselect *Let the User Choose*.\n\n![Figure 6: Let the user choose which scope the search is executed for.](../../../images/search-scope.png)\n\n**Scope Parameter Name** : Set the URL parameter name for the scope where the\nsearch is taking place. This parameter only appears in the URL if the scope _Let\nthe User Choose_ is selected. The default value is _scope_, so searching for the\nword _data_ produces the default URL of\n\n    http://localhost:8080/web/guest/search?q=data&scope=this-site\n\nChanging _scope_ to _target_ would produce this URL:\n\n    http://localhost:8080/web/guest/search?q=data&target=this-site\n\n**Destination Page**\n: Provide a friendly URL to the \n[search page](/docs/7-2/user/-/knowledge_base/u/configuring-search-pages).\nIf not configured or if it points to a page that doesn't exist, a message\nappears for administrators that the search bar must be configured for it to\nappear to users.\n\n**Use Advanced Search Syntax**\n: If using Elasticsearch, enabling this allows users to enter \n[Query String Syntax](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-query-string-query.html#query-string-syntax) \ninto the Search Bar. If using Solr, consult its documentation for the \n[proper syntax](https://lucene.apache.org/solr/guide/6_6/query-syntax-and-parsing.html).\n\n## Search Suggestions\n\nSuggest search terms to users when their initial queries are suboptimal. Spell\ncheck settings allow administrators to configure the Search application so that\nif a user types a search term that doesn't return many results (for example,\na slightly misspelled werd), the user can be prompted to improve the search. \n\nTo configure the spell check settings, \n\n1.  You must first reindex the spell check indexes. Go to *Control Panel* &rarr;\n    *Configuration* &rarr; *Search*, then click *Execute* next to *Reindex all\n    spell check indexes*.\n\n2.  Add the Suggestions widget to the search page.\n\n3.  Open its configuration screen. Click the widget Options button (![Options](../../../images/icon-app-options.png)) and select *Configuration*.\n\n![Figure 7: Configure the suggestion settings to allow for user input mistakes and help lead users to results.](../../../images/search-suggestions.png)\n\nThere are three main settings here:\n\n**Display \"Did you mean...\" if the number of search results does not meet the\nthreshold.**\n: Present users alternate, spell checked search queries if their search did not\nreturn a minimum number of results (50 by default).\n\n**Display Related Queries**\n: If the number of search results doesn't meet the specified threshold (50 by\ndefault), display up to a maximum number of alternative queries (10 by default).\n\n**Add New Related Queries Based on Successful Queries**\n: Index a user's search query if it produces a minimum number of results (50 by\ndefault), so it can be displayed to users as a suggestion. If the Display\nRelated Queries setting is enabled, it's used as a related query for similar\nsearch queries that don't produce enough results.\n\n"
  },
  {
    "path": "en/user/articles/120-search/05-facets/01-facets-intro.markdown",
    "content": "---\nheader-id: facets\n---\n\n# Facets\n\n[TOC levels=1-4]\n\nEnter a keyword in the Search Bar and click the Search button. The default\nsearch experience redirects to a page with results on the right and a\ncollection of *facets* on the left. \n\n![Figure 1: *Site* and *Type* are two of the facet sets you'll encounter.](../../../images/search-faceted-search.png)\n\nA Facet aggregates search results by some common characteristic, with each facet\nholding search results that share something in common. After scanning the full\nlist of results, a User might decide the results from just one facet are more\nappropriate (for example, all the results from a particular site, or all the\nresults that are Blogs Entries). So what facets are included by default?\n\n- **Site Facet** for filtering results by their site. \n- **Type Facet** for filtering results by the Asset Type.\n- **Tag Facet** for filtering results by Tag.\n- **Category Facet** for filtering results by Category.\n- **Folder Facet** for filtering results by Folder.\n- **User Facet** for filtering results by the content creator.\n- **Modified Facet** for filtering results by the Last Modified Date.\n- **Custom Facet** for filtering results by some other indexed field. See\n    [here](/docs/7-2/user/-/knowledge_base/u/custom-facet) \n    for more information.\n\nEach item in a facet (selected using the checkbox) is called a *Facet Term*\n(*term* for short).\n\nIn this tutorial, you'll explore how facets and their terms are used and how to\nfind a facet's configuration. The remaining articles show the configurations\navailable for each facet.\n\n## Using Facets\n\nIf you're not actually an accomplished oboe player, pretend for a moment. You're\nvisiting a site for classical musicians. You remember reading a great technical\nanalysis of Johann Bach's compositions, but you forgot to bookmark it (or would\nit be a *bachmark*?). You enter the keyword *bach* into the search bar and,\nbecause Johann Bach was a very important and famous composer, you get lots of\nresults: too many, in fact. At first you're discouraged, but you remember that\nthere's a site member who produces most of the site's good technical content,\nwho's named *back2bach*. You see that his name is listed in the User facet, and\nthere aren't many results in the facet count (the number in parentheses next to\nthe facet). You click into the facet and quickly find the content you wanted.\n\n![Figure 2: When presented lots of search results, facets narrow down the results list so users can find relevant content.](../../../images/search-facets1.png)\n\nClicking on a facet narrows down the search results. It's added to the filter\nlist in the search query, and the results list is refined by the selected\nfacets. \n\n![Figure 3: After clicking the *Web Content Article* type in the Asset Types facet, it's the only asset type listed.](../../../images/search-facet-wc.png).\n\n## Multiple Facet Selection\n\nFacet term selections within one facet are additive. Clicking more terms in the\nsame facet expands the search results, because it's processed as if you want to\nsee results matching *Term-1* OR *Term-2*, OR etc. To remove all the term\nselections from a facet, click the *Clear* link. \n\n![Figure 4: Facet terms are additive when applied in the same facet. Any Blogs Entry OR Web Content article matching the keyword is shown here.](../../../images/search-multiple-terms.png)\n\nFacet term selections from different facets are exclusive. Clicking facet terms\nfrom multiple facets narrows the results because they're processed as if you\nwant to see results matching *Facet-1* AND *Facet-2*, AND etc. This is\nintuitive. The facets \n\n![Figure 5: Facet terms selected from different facets are exclusive. These results must be of type Blogs Entry AND be from the User Marvin Smart.](../../../images/search-multiple-facets.png)\n\nConsidering a case where you make two term selections in the Type Facet: (Blogs\nEntry and Web Content Article), and two term selections in the User Facet (James\nJeffries and Marvin Smart). What results are displayed? \n\n*Blogs Entries OR Web Content Articles AND authored by James Jeffries OR Marvin\nSmart*. \n\nIf Marvin and James each created four pieces of content (two blogs and two Web\nContent Articles), all eight would appear in the Search Results. Any Blogs or\nWeb Content created by other Users are not shown, and assets of other Type\ncreated by Marvin and James are not displayed. Content that isn't Blog Entries\nor Web Content Articles created by other Users are obviously not searched.\n\n![Figure 6: Both intra-facet and inter-facet selection is possible.](../../../images/search-facet-selections.png)\n\n| **Note:** The new Search Facet widgets support the multiple selection of facet\n| terms. Multiple facet selection is not supported in the classic Search portlet.\n\n## Facets and Friendly URLs\n\nIn the classic, monolithic Search portlet, URLs like this were not uncommon:\n\n    http://localhost:8080/web/guest/home?_com_liferay_portal_search_web_portlet_SearchPortlet_formDate=1529671834606&p_p_id=com_liferay_portal_search_web_portlet_SearchPortlet&p_p_lifecycle=0&p_p_state=maximized&p_p_mode=view&_com_liferay_portal_search_web_portlet_SearchPortlet_mvcPath=%2Fsearch.jsp&_com_liferay_portal_search_web_portlet_SearchPortlet_redirect=http%3A%2F%2Flocalhost%3A7011%2Fweb%2Fguest%2Fhome%3Fp_p_id%3Dcom_liferay_portal_search_web_portlet_SearchPortlet%26p_p_lifecycle%3D0%26p_p_state%3Dnormal%26p_p_mode%3Dview&_com_liferay_portal_search_web_portlet_SearchPortlet_keywords=test&_com_liferay_portal_search_web_portlet_SearchPortlet_scope=this-site\n\nSearch now uses friendly search URLs for facet filtering. With the default\nsettings, here's the default main search URL when searching for keyword _test_:\n\n    http://localhost:8080/web/guest/search?q=test\n\nSelecting a facet term causes a new parameter to the above URL. For example,\nselecting _Blogs Entry_ from the Type facet results in this URL:\n\n    http://localhost:8080/web/guest/search?q=test&type=com.liferay.blogs.model.BlogsEntry\n\nSelecting another facet term from the same facet category appends the same\nparameter again, but with the newly selected value:\n\n    http://localhost:8080/web/guest/search?q=test&type=com.liferay.blogs.model.BlogsEntry&type=com.liferay.portal.kernel.model.User\n\nThe rest of the facets work the same way. Filtering by the last hour option in\nthe Last Modified facet portlet produces this URL:\n\n    http://localhost:8080/web/guest/search?q=test&modified=past-hour\n\nThe parameter names are configurable for each facet.\n\nNow that you know how facets work, read about configuring each of the included\nfacets.\n"
  },
  {
    "path": "en/user/articles/120-search/05-facets/02-site-facet.markdown",
    "content": "---\nheader-id: site-facet\n---\n\n# Site Facet\n\n[TOC levels=1-4]\n\nThe Site Facet narrows search results down to those existing in a certain site.\nEach Site with content matching the searched keyword appears as a facet term.\n\n![Figure 1: Each Site with matching content is a facet term.](../../../images/search-site-facet.png)\n\nFor the Site Facet to display multiple sites, the Search Bar must be configured\nto search *Everything*. See more about search scope\n[here](/docs/7-2/user/-/knowledge_base/u/searching-for-assets#configuring-the-search-bar).\nIf not searching for Everything, only the current site is searched, and the Site\nFacet has nothing to display. When this occurs, the Site Facet is hidden on the\npage.\n\n| **Note:** Configuring the globally embedded page-top Search Bar to search for\n| Everything not only configures the embedded Search Bar on all pages. It also\n| ensures that the Search Page's Search Bar searches Everything, because the\n| page-top Search Bar's configuration overrides the Search Page's Search Bar\n| configuration. The same does not apply to other Search Bar widgets in the site.\n| Each of these must be configured as desired.\n| \n| If the global Search Bar is disabled, configure the Search Page's Search Bar\n| widget to search for Everything.\n| \n| To configure the search scope,\n| \n| 1.  Open the Search Bar's Options menu\n|     (![Options](../../../images/icon-options.png))\n|     and click *Configuration*.\n| \n| 2.  Set the Scope option to *Everything*.\n| \n| 3.  Click *Save* and close the pop-up.\n\nThe Site Facet contains several configuration options:\n\n![Figure 2: The Site Facet is configurable.](../../../images/search-site-facet-config.png)\n\n**Site Parameter Name**\n: Set the URL parameter name for the Facet. The default is *site*. Searching for\n*lunar resort* and clicking on a site facet produces the URL\n\n        http://localhost:8080/web/guest/search?q=lunar resort&site=20126\n\n**Max Terms**\n: Set the maximum number of facet terms to display, regardless of how many\nmatching terms are found for the facet.\n\n**Frequency Threshold**\n: Set the minimum frequency required for terms to appear in the list of facet\nterms. For example, if the frequency threshold of a facet is set to `3`, a term\nwith two matching results doesn't appear in the term result list.\n\n**Display Frequencies**\n: Choose whether or not to display the term frequencies.\n\n"
  },
  {
    "path": "en/user/articles/120-search/05-facets/03-type-facet.markdown",
    "content": "---\nheader-id: type-facet\n---\n\n# Type Facet\n\n[TOC levels=1-4]\n\nThe Type Facet narrows search results down to those associated with a certain\nAsset Type. Each Type with content matching the searched keyword appears as a\nfacet term.\n\n![Figure 1: Each Asset Type with matching content is a Type Facet term.](../../../images/search-type-facet.png)\n\nBy default, all out of the box Asset Types are included as facet terms:\n\n- Wiki Page\n- Document\n- User\n- Blogs Entry\n- Form Record\n- Documents Folder\n- Dynamic Data Lists Record\n- Web Content Article\n- Message Boards Message\n- Calendar Event\n- Knowledge Base Article\n\nThe Type Facet contains several configuration options:\n\n![Figure 2: The Type Facet is configurable.](../../../images/search-type-facet-config.png)\n\n**Type Parameter Name**\n: Set the URL parameter name for the Facet. The default is *type*. Searching for\n*lunar resort* and clicking on a site facet produces the URL\n\n        http://localhost:8080/web/guest/search?q=lunar resort&type=com.liferay.blogs.model.BlogsEntry\n\n**Frequency Threshold**\n: Set the maximum number of facet terms to display, regardless of how\nmany matching terms are found for the facet.\n\n**Display Frequencies**\n: Choose whether or not to display the term frequencies.\n\n**Current and Available**\n: Add or remove Asset Types from the facet. To remove types, select from the\nCurrent section by clicking and highlighting. Click the right arrow and move the\nAsset Type from *Current* to *Available*. Add Asset Types by moving them to the\nCurrent section.\n\n"
  },
  {
    "path": "en/user/articles/120-search/05-facets/04-tag-category-facets.markdown",
    "content": "---\nheader-id: tag-and-category-facets\n---\n\n# Tag and Category Facets\n\n[TOC levels=1-4]\n\nIf tags or categories were applied to an asset appearing in the result set,\nthey're displayed in the Tag or Category facet, respectively. Like other facets\nwith the Frequency Threshold configuration option, not all tags necessarily\nappear. By default the top 10 tags or categories are listed.\n\n![Figure 1: Each Tag or Category with matching content is a facet term.](../../../images/search-tag-facet.png)\n\nTag and Category Facets contain identical configuration options:\n\n![Figure 2: Tag and Category Facets are configurable.](../../../images/search-tag-facet-config.png)\n\n**Tag/Category Parameter Name**\n: Set the URL parameter name for the Facet. The default is *tag*/*category*.\nSearching for *lunar resort* and clicking on a *moon* Tag Facet term produces\nthe URL\n\n        http://localhost:8080/web/guest/search?q=lunar resort&tag=moon\n\n**Display Style**\n: Choose whether to display the facet terms in Cloud or List style.\n\n**Max Terms**\n: Set the maximum number of facet terms to display, regardless of how many\nmatching terms are found for the facet.\n\n**Frequency Threshold**\n: Set the minimum frequency required for terms to appear in the result list. For\nexample, if the frequency threshold of a facet is set to `3`, a term with two\nmatching results doesn't appear in the term result list.\n\n**Display Frequencies**\n: Choose whether or not to display the term frequencies.\n\n"
  },
  {
    "path": "en/user/articles/120-search/05-facets/05-folder-facet.markdown",
    "content": "---\nheader-id: folder-facet\n---\n\n# Folder Facet\n\n[TOC levels=1-4]\n\nThe Folder Facet narrows search results down to those contained in a certain\nAsset Folder. If you search for *space*, a Folder titled *Space Images* doesn't\nnecessarily show up here. The content inside the folder must match the keyword.\nOnly if its content matches the searched keyword does the Folder appear in the\nFolder Facet.\n\nFolders of these Types appear as Folder Facet terms: \n\n- Documents and Media Folder\n- Web Content Folder\n\n![Figure 1: Each Folder with matching content is a facet term.](../../../images/search-folder-facet.png)\n\nThe Folder Facet contains several configuration options:\n\n![Figure 2: The Folder Facet is configurable.](../../../images/search-folder-facet-config.png)\n\n**Folder Parameter Name**\n: Set the URL parameter name for the Facet. The default is *folder*. Searching for\n*lunar resort* and clicking on a Folder Facet produces the URL\n\n        http://localhost:8080/web/guest/search?q=lunar resort&folder=38716\n\n**Max Terms**\n: Set the maximum number of facet terms to display, regardless of how\nmany matching terms are found for the facet.\n\n**Frequency Threshold**\n: Set the minimum frequency required for terms to appear in the result list. For\nexample, if the frequency threshold of a facet is set to `3`, a term with two\nmatching results doesn't appear in the term result list.\n\n**Display Frequencies**\n: Choose whether or not to display the term frequencies.\n\n"
  },
  {
    "path": "en/user/articles/120-search/05-facets/06-user-facet.markdown",
    "content": "---\nheader-id: user-facet\n---\n\n# User Facet\n\n[TOC levels=1-4]\n\nThe User Facet narrows search results down to those created by a certain User.\n\n![Figure 1: Each User with matching content is a facet term.](../../../images/search-user-facet.png)\n\nThe User Facet contains several configuration options:\n\n![Figure 2: The User Facet is configurable.](../../../images/search-user-facet-config.png)\n\n**User Parameter Name**\n: Set the URL parameter name for the Facet. The default is *user*. Searching for\n*lunar resort* and clicking on a User Facet produces the URL\n\n        http://localhost:8080/web/guest/search?q=lunar resort&user=38716\n\n**Max Terms**\n: Set the maximum number of facet terms to display, regardless of how\nmany matching terms are found for the facet.\n\n**Frequency Threshold**\n: Set the minimum frequency required for terms to appear in the result list. For\nexample, if the frequency threshold of a facet is set to `3`, a term with two\nmatching results doesn't appear in the facet.\n\n**Display Frequencies**\n: Choose whether or not to display the term frequencies.\n"
  },
  {
    "path": "en/user/articles/120-search/05-facets/07-modified-facet.markdown",
    "content": "---\nheader-id: modified-facet\n---\n\n# Modified Facet\n\n[TOC levels=1-4]\n\nThe Modified Facet narrows search results down to those that match the searched\nkeyword and that were created or modified during a certain time period.\n\n![Figure 1: Each time period with matching content is a facet term.](../../../images/search-modified-facet.png)\n\nIn addition to selecting a pre-configured time period, Users can select a Custom\nRange, specifying a From and To date using a date picker:\n\n![Figure 2: Users can include a Custom Range in the Modified Facet.](../../../images/search-modified-facet-custom.png)\n\nThe Modified Facet supports configuration actions:\n\n- Modify existing time ranges\n- Delete existing time ranges\n- Create new time ranges\n\nEdit or create time ranges using a time range alias.\n\nThe available time range aliases include:\n\n    past-hour\n    past-24-hours\n    past-week\n    past-month\n    past-year\n\n![Figure 3: The time ranges are set in the facet's configuration.](../../../images/search-modified-facet-config.png)\n\nEach Range has an alias and a Label.\n\nBy default, all the default ranges end in `*`, which evaluates to *now*. For\nexample, the past-week range is\n\n    [past-week TO *]\n\nYou're not limited to ending Ranges. Instead of the `*`, specify another\ntime range alias as the ending point.\n\nTo set up a range from 12 months ago to one month ago, \n\n1.  Click the plus button in one of the existing ranges.\n\n2.  Give it the label **1-12 Months Ago**.\n\nGive it a Range value of \n\n    [past-year to past-month]\n\nThis gives you lots of flexibility in using alternative time ranges as Modified\nFacet terms.\n"
  },
  {
    "path": "en/user/articles/120-search/05-facets/08-custom-facet.markdown",
    "content": "---\nheader-id: custom-facet\n---\n\n# Custom Facet\n\n[TOC levels=1-4]\n\nAll Facets are configurable, allowing you to narrow down search results based on\na shared characteristic (all Blog Entries, for example). The Custom Facet lets\nyou create entirely new Facets. The first thing to do is enter the Custom\nFacet's configuration screen.\n\n![Figure 1: Custom Facets must be configured first.](../../../images/search-custom-facet-jobtitle.png)\n\nThe screenshot above shows a Custom Facet with the Job Title of each User that\nmatched the search. The next screenshot shows how it was configured.\n\n![Figure 2: Configure a Custom Facet in no time.](../../../images/search-custom-facet-config.png)\n\n**Aggregation Field**\n: Specify the non-analyzed keyword field whose value is used to create the facet\nterms. If the value of the search result's `jobTitle_sortable` field is _upsale\nmanager_, that's what appears in the Custom Facet as a term.\n\n**Custom Heading**\n: Enter a human readable heading for the Custom Facet. \n\n**Custom Parameter Name**\n: Set the URL parameter to use for the facet. With the configuration pictured\nabove, searching for *jane* and clicking on _chief of security_ produces the URL\n\n    http://localhost:8080/web/guest/search?q=jane&jobtitle=chief%20of%20security\n\n**Max Terms**\n: Set the maximum number of facet terms to display, regardless of how\nmany matching terms are found for the facet.\n\n**Frequency Threshold**\n: Set the minimum frequency required for terms to appear in the result list. For\nexample, if the frequency threshold of a facet is set to `3`, a term with two\nmatching results doesn't appear in the term result list.\n\n**Display Frequencies**\n: Choose whether to display the term frequencies.\n\n## Finding Indexed Fields\n\nTo use the Custom Facet, you must know which non-analyzed keyword field to\nspecify. \n\nTo browse the entire list of available fields, inspect the field mappings from\nControl Panel &rarr; Configuration &rarr; Search. Alternatively, use your search\nengine's API. \n\nFor Elasticsearch, access the field mappings from your terminal using CURL to\ncall the [Get Mapping API](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/indices-get-mapping.html):\n\n    curl -X GET \"localhost:9200/_mapping/LiferayDocumentType\"?pretty\n\nSolr uses the [ListFields API](https://lucene.apache.org/solr/guide/6_6/schema-api.html#SchemaAPI-ListFields):\n\n    curl http://localhost:8983/solr/liferay/schema/\n\nHere's a snippet of output from the Elasticsearch example:\n\n    \"ddmStructureKey\": {\n        \"store\": true,\n        \"type\": \"keyword\"\n    },\n    \"ddmTemplateKey\": {\n        \"store\": true,\n        \"type\": \"keyword\"\n    },\n    \"defaultLanguageId\": {\n        \"store\": true,\n        \"type\": \"keyword\"\n    },\n    \"description\": {\n        \"store\": true,\n        \"term_vector\": \"with_positions_offsets\",\n        \"type\": \"text\"\n    },\n    \"discussion\": {\n        \"store\": true,\n        \"type\": \"keyword\"\n    },\n\nUse Custom Fields to aggregate facet terms by shared non-analyzed keyword\nfield values.\n\n"
  },
  {
    "path": "en/user/articles/120-search/06-search-results/01-search-results-intro.markdown",
    "content": "---\nheader-id: search-results\n---\n\n# Search Results\n\n[TOC levels=1-4]\n\nThe ideal search experience involves a User entering a search term, waiting an\ninfinitesimal amount of time, and having the perfectly matching asset delivered\nat the top of a list of other extremely relevant hits. Like this:\n\n![Figure 1: The goal is to return the perfect results to Users searching your site.](../../../images/search-results-perfect.png)\n\nThe developers of an asset control much about how the asset's information is\nstored in the search engine (this process is called *indexing*, and how its\ninformation is searched and returned in the search results.  Developers who\ndislike how a particular asset behaves in search can use an *Indexer Post\nProcessor* to modify the asset's indexing behavior and how search queries are\nconstructed to look up the assets in @product@.\n\nThe Search Results behavior configurable through the UI is covered in this\nsection:\n\n- [Search Results Display Settings](/docs/7-2/user/-/knowledge_base/u/display-settings)\n\n- [Filtering Results](/docs/7-2/user/-/knowledge_base/u/filtering-search-results-with-the-custom-filter-widget) \n\n- [Sorting Results](/docs/7-2/user/-/knowledge_base/u/sorting-search-results-with-the-sort-widget) \n\n- [Search Results Behavior](/docs/7-2/user/-/knowledge_base/u/search-results-behavior)\n\n\n"
  },
  {
    "path": "en/user/articles/120-search/06-search-results/02-display-settings.markdown",
    "content": "---\nheader-id: display-settings\n---\n\n# Display Settings\n\n[TOC levels=1-4]\n\nThe Search Results widget's default display is a paginated list. Each list item\nis a summarized hit to a search query. Click on a specific result to look at it\nin more detail. Configure display options by opening the Search Results options\nmenu (![Options](../../../images/icon-app-options.png)) and selecting\n*Configuration*. \n\n**Enable Highlighting**\n: Highlight the search terms where they appear in the search result's title or\nsummary.\n\n**Display Selected Result in Context**\n: When an asset is clicked, show it in its native application. For example, if\nyou click on a blog post in the search results, you see where the Blogs Entry is\nposted in the Blogs application. Note that you're not in the search context\nafter clicking on a search result. When this option is unchecked, the asset\ndisplays in an Asset Publisher window while still in the search context. If you\nhave the right permissions, you can even edit the content directly from the\nSearch context. Click the back arrow to return to the search results.\n\n**Display Results in Document Form**\n: Display results as search\n[documents](/docs/7-2/frameworks/-/knowledge_base/f/search).  Never use this in\nproduction. Developers use this feature to view search responses in their\nindexed, document-based format. Part of a developer's job when writing search\nindexers is to convert documents (the objects that get indexed) to the actual\nobject and back again. Thus, developers can see how their objects are being\nindexed. Once enabled, click the *Details...* link below the result summary to\nexpand the result's document view.\n\n![Figure 1: Viewing a results document lets you inspect exactly what's being indexed for a particular asset. This is just a small portion of one document.](../../../images/search-results-document.png)\n\nThe next three configurations concern results pagination.\n\n![Figure 2: The number of results per page and the URL parameter names used to control pagination behavior are configurable.](../../../images/search-results-pagination.png)\n\n**Pagination Start Parameter Name**\n: Set the name of the URL parameter for the results page. If the default value\n*start* is preserved, this URL displays when the User navigates to the second\nresults page after searching for *test*:\n\n        http://localhost:8080/web/guest/search?q=test&start=2\n\n**Pagination Delta**\n: Set the number of results to display per results page. Defaults to *20* unless\nyou customized the `search.container.page.default.delta` property in your\n`portal-ext.properties` file.\n\n**Pagination Delta Parameter Name**\n: Set the name of the URL parameter that stores the Pagination Delta value. This\nbecomes visible in the browser if the User changes the number. If the User\nselects 10 results per page and searches for *test*, the Search Page is reloaded\nwith this URL:\n\n        http://localhost:8080/web/guest/search?q=test&delta=10\n\n**Federated Search Key**\n: If this widget is participating in a search on a non-default index, enter the\nkey of the alternate search index. If not set, the widget participates in the\ndefault search, against the default index (`liferay-[comanyId]`. This value is\nusually the name of an application-defined index. \n\n**Fields to Display**\n: If searching an alternate index using the Federated Search Key configuration,\nspecify what fields to search from that index. \n\nFor further reading, check out how to \n[return suggestions for better search terms](/docs/7-2/user/-/knowledge_base/u/searching-for-assets#search-suggestions)\n(for example, \"Did you mean...\") when not enough results are returned initially.\n"
  },
  {
    "path": "en/user/articles/120-search/06-search-results/03-custom-filters.markdown",
    "content": "---\nheader-id: filtering-search-results-with-the-custom-filter-widget\n---\n\n# Filtering Search Results with the Custom Filter Widget\n\n[TOC levels=1-4]\n\nYou often need to exert control over the displayed search results. One viable\napproach is to develop your own search portlets using the @product@ APIs. That\ncan be overkill if you just want to make a slight modification to how the search\nis executed, so many of the out-of-the-box search widgets give you this type of\ncontrol without coding anything (Search Options, Custom Facet, and more). In\n@product-ver@, new widgets have been added: Sort and Custom Filter.\n\nWith Custom Filters, you can contribute queries to the main search query,\nexerting control over the search results. Make the filter widgets visible or\ninvisible to the search Users, and decide if they're changeable or immutable.\n\nTo explore all the options you have with the Custom Filter widget, you need one\non the page.\n\n## Adding and Configuring Custom Filters\n\nTo get started with Custom Filters,\n\n1. Open the Add menu (![Add](../../../images/icon-add-widget.png)) for the page and\n   expand the Widgets section.\n\n2.  From the Search section, drag a Custom Filter onto the page.\n\n![Figure 1: A custom filter has no impact until it's configured.](../../../images/search-custom-filter.png)\n\nCustom filters can do so many things, it's impossible to list them all. What\nfollows is a widget configuration tour. Separate documentation will be written\nto provide a how-to demonstration of Custom Filters.\n\n### Custom Filter Configuration Options\n\nOpen the widget Options menu (![Options](../../../images/icon-app-options.png)) and\nclick _Configuration_.\n\n![Figure 2: Once the Custom Filter is added to the page, mold it like soft clay into the beautiful sculpture you've envisioned.](../../../images/search-custom-filter-configuration.png)\n\n\n**Filter Field (text)**\n: Most often, filters operate on a specific field. Set the name of the indexed\nfield to be filtered (for example, `title`). You won't need this if the Filter\nQuery Type is set to a type that doesn't require a field, such as _Regexp_.\n\n> The Query String and Script queries do not require a Filter Field to be set.\n> All other queries require at least one field. \n<!--Note: Multi Match and Simple Query String take an array of fields accoring to the Elasticsearch docs, but our config doesn't seem to support it afaict.? -->\n\n**Filter Value (text)**\n: For most filters, you must enter a text value here that specifies the\ntext to apply the filter on in the specified field (for example, set a _Match_\nquery to the text _street_ on the `title_en_US` field). Some Filter Query Types\nrequire special notation, as in the case of the _Regexp_ filter. \n\n**Filter Query Type (select list)**\n: Select the query type to filter results by. Available types include Bool,\nExists, Fuzzy, Match, Match Phrase, Match Phrase Prefix, Multi Match, Prefix,\nQuery String, Regexp, Script, Simple Query String, Term, Wildcard. To learn more\nabout these queries, visit the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl.html).\n\n**Occur (select list)**\n: Set the occurrence type for the query being contributed to the search. Options\ninclude Filter, must, must_not, and should. To understand each type, see the\n[Elasticsearch\ndocumentation](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-bool-query.html). \n\n**Query Name (text)**\n: Set the name of the contributed query. This is unnecessary unless this filter\nacts as a parent query to another filter that contributes child clauses; in\nthat case set this filter's Query Name as the child filter's Parent Query Name.\nThis parent/child behavior is only available for filters of type Bool.\n\n**Parent Query Name (text)**\n: When contributing a child clause to a Bool query, set this to match the Query\nName configured in the parent Custom Filter widget. Otherwise, leave it blank.\n\n**Boost (number)**\n: [Boost](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-term-query.html#term-field-params)\nthe score of the results matching this query. Specify any whole or decimal\nnumber here that makes sense. If you always want results matching this at the\ntop, set the Boost value really high (e.g., _1000_).\n\n**Custom Heading (text)**\n: Enter the heading to display for this filter. If not set, the Filter Field's\nvalue is displayed.\n\n**Custom Parameter Name (text)**\n: Specify a URL parameter name for the filter. If not set, the Filter Field's\nvalue is used.\n\n**Invisible (boolean)**\n: If checked, the widget is invisible to regular users. The Filter Value from\nthe configuration is applied by default, but users can still filter for other\nvalues via URL Parameter. Don't worry, you can shut that down if you need to\nwith the Immutable setting (see below).\n\n**Immutable (boolean)**\n: Enable this to ensure that the Filter Value cannot be changed by regular\nusers. The widget becomes invisible to them _and_ filter values set via URL\nparameters are not accepted. The Filter Value set in the widget configuration is\napplied at all times (unless it's disabled).\n\n**Disabled (boolean)**\n: If checked, the query is ignored and doesn't participate in searches. This\ngives you a quick way to stop the filter, but keep the configuration so it can\nbe re-enabled later.\n\n**Federated Search Key (text)**\n: Enter the key of an alternate Search this widget is participating on. If it's\nset, be aware that the default @product@ index isn't searched at all. If not\nset, this widget participates on the default search. Values in this field\ntypically match the name of an application-defined index.\n\nThere are many use cases you can satisfy by incorporating Custom Filters into\nyour search page. Two demonstrative articles are planned to show you some of\nthe filtering capabilities at your disposal:\n\n- _Refine to One_ (or _Needle in a Haystack_) will show you how to add\n  user-operated filters to the page so results can be refined down to just the\n  result they were looking for.\n\n- _Complex filtering_ shows you some more advanced filters and how they work.\n\nCheck out the Custom Filter and see what it adds to your search page.\n\n"
  },
  {
    "path": "en/user/articles/120-search/06-search-results/04-sort-widget.markdown",
    "content": "---\nheader-id: sorting-search-results-with-the-sort-widget\n---\n\n# Sorting Search Results with the Sort Widget\n\n[TOC levels=1-4]\n\nThe Sort widget gives Users configurable control over the order of returned\nresults: no code necessary.\n\nAdd it to a page and begin sorting results.\n\nBy default, results are sorted by the [relevance\nscore](https://www.elastic.co/guide/en/elasticsearch/guide/master/scoring-theory.html)\nreturned by the search engine. Users can choose from one of the out-of-the-box\nalternative sorting strategies, or one configured by a search administrator. \n\nOut of the box, order results in these ways as an alternative to relevance\nsorting:\n\n- alphabetically by Title\n- by the Modified date (oldest first)\n- by the Create date (newest first)\n- by the Create date (oldest first)\n- alphabetically by the User that created each matching asset\n\nIf the out-of-the-box alternatives aren't enough, an administrator can\ncreate additional sort options from the widget's configuration.\n\nIt's also possible to delete unwanted sort options from the widget.\n\n## Adding and Configuring the Sort Widget\n\nTo get started with the Sort widget,\n\n1. Open the Add menu (![Add](../../../images/icon-add-widget.png)) for the page and\n   expand the Widgets section.\n\n2.  From the Search section, drag a Sort widget onto the page.\n\n![Figure 1: Users can re-order search results with the Sort widget.](../../../images/search-sort.png)\n\n### Configuring the Sort Widget\n\nThree things can be done from the Configuration screen:\n\n- Editing existing Sort options\n- Deleting options\n- Adding options\n\n![Figure 2: From the Sort widget's configuration, add, edit, or remove Sort options.](../../../images/search-sort-configuration.png)\n\nTo access the widget configuration screen, open the widget Options menu\n(![Options](../../../images/icon-app-options.png)) and click _Configuration_.\n\nEach Sort option has two fields: _Label_ and _Field_.\n\n**Label**\n: Set the displayed label for the type of sort being configured.\n\n**Field**\n: The `fieldName` of the indexed field to sort. Most of the time\nthis is a\n[keyword](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/keyword.html)\nfield. Other acceptable options are `date` and any \n[numeric datatype](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/number.html).\nThere's even a way for persistent search administrators to coerce `text` fields\ninto behaving with the Sort widget. Keep reading for details.\n\n#### Finding Sortable Fields\n\nTo find the fields available for use in the Sort widget, Users with the proper\npermissions can navigate to *Control Panel* &rarr; *Configuration* &rarr; *Search*.\nFrom there, open the Field Mappings tab and browse the mappings for each index.\nScroll to the `properties` section of the mapping, and find any `keyword` field,\n`date` field, or a field with any numeric datatype. The `type` field is\ninstructive:\n \n    \"type\" : \"keyword\"\n\n    \"type\" : \"date\"\n\n    \"type\" : \"long\"\n\nWhat if you really need to sort by a `text` field? You can do it by adding a new\nversion of the field to the index, with the type `keyword`. Don't worry; you\ndon't need to code anything to do this. From the field mappings screen mentioned\nabove, look at the `firstName` field in the index called `liferay-[companyID]`.\nIn fact, look at the next entry as well:\n\n```json\n\"firstName\" : {\n    \"type\" : \"text\",\n    \"store\" : true\n},\n\"firstName_sortable\" : {\n    \"type\" : \"keyword\",\n    \"store\" : true\n},\n```\n\nThere's a corresponding field with the suffix `_sortable`, and of the correct\ntype for sorting (`keyword`). How did that get there? Via the \n[portal property](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Lucene%20Search) \n\n```properties\nindex.sortable.text.fields=firstName,jobTitle,lastName,name,screenName,title\n```\n\nAll the text fields listed here have a `fieldName_sortable` counterpart\ncreated automatically in the index. To add more, copy this value into a\n[`portal-ext.properties`](/docs/7-2/deploy/-/knowledge_base/d/portal-properties) \nfile into your Liferay Home folder, add any new field\nnames you need to sort by, and restart the server.\n\n#### Adding New Sort Options\n\nTo sort by the new field, use the plus symbol below any option's _Field_\nconfiguration make sure to use the `fieldName_sortable` version of the field in\nthe widget configuration. \n\nTo add a new sort option that's already of the proper datatype, use the plus\nsymbol below any option's _Field_ configuration and fill in the fields. The\norder of options here in the configuration screen matches the order Users see in\nthe select list while configuring the widget for their search.\n\n#### Editing and Deleting Sort Options\n\nTo edit an existing option, edit the text in its configuration section.\n\nTo delete an existing option, use the minus symbol below its _Field_\nconfiguration.\n\n### Controlling the Sort Order\n\nTo control the order for the sort option, add a plus or minus symbol after the\n`fieldName`. Look how it's done for the existing sort options labeled _Created_\nand _Created (oldest first)_ to understand how it works:\n\n**Label:** _Created_\n**Field:** `createDate-`\n\nThe `-` sign following the field name indicates that the order is _descending_.\nChoosing to sort with this brings search results created most recently to\nthe top of the list.\n\n**Label:** _Created (oldest first)_\n**Field:** `createDate+`\n\nThe `+` sign following the field name indicates that the order is _ascending_.\nChoosing to sort with this brings the oldest (by creation date) results to\nthe top of the list.\n"
  },
  {
    "path": "en/user/articles/120-search/06-search-results/05-results-behavior.markdown",
    "content": "---\nheader-id: search-results-behavior\n---\n\n# Search Results Behavior\n\n[TOC levels=1-4]\n\nThe previous article covered ways to display search results. This article\ncovers these additional Search Results concepts and configurations: \n\n- [Filtering search results with facets](#filtering-results-with-facets)\n- [Understanding search results relevance](#search-results-relevance)\n- [The effect of permissions on search results](#permissions-and-search-results)\n- [Search results in the staging environment](#search-and-staging)\n- [Search results summaries](#result-summaries)\n- [Search results term highlighting](#highlighting)\n\n## Filtering Results with Facets\n\nResults are filtered using *facets*. Most users have encountered similar\nfiltering capabilities in other applications, particularly during commerce\nactivities. Users enter a search term, are presented with a list of results and\nsearch facets, which you can think of as buckets that group results together if\nthey share a common characteristic.\n\nAdministrators can configure facets. Read about \n[configuring facets](/docs/7-2/user/-/knowledge_base/u/facets) \nto learn more.\n\n## Search Results Relevance\n\nThe search engine decides which results appear at the top of the list using the\nconcept of *relevance*. Relevance is a score calculated by the search engine.\nThere are numerous factors contributing to the total score of a returned\ndocument, and all of the implementation details of how relevance scoring works\nare algorithms provided by the \n[search engine](https://www.elastic.co/guide/en/elasticsearch/reference/current/relevance-intro.html#relevance-intro).\n\n## Permissions and Search Results\n\nUsers lacking\n[VIEW permission](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions)\non an asset don't see it in the search results. A logged in User with the Site\nAdministrator role likely sees more search results than a guest User to the\nsite. \n\nIn the background, there are two rounds of permissions checks. The first\npermissions check, _pre-filtering_, happens in the search engine's index. It's\nfaster than checking database permissions information, but occasionally the\nsearch index can have stale permissions information. To ensure the search\nengine's index has correct, up-to-date permissions information, a second,\nlast-second permissions check, _post-filtering_, is performed on the results\nprior to their display.\n\n### Initial Permissions Checking\n\nThe first round of search results permissions filtering adds filter clauses to\nthe search query. This ensures that results return from the search engine\npre-filtered, containing results the current User can view.\n\nThis initial permission checking is configurable at *Control Panel* &rarr;\n*Configuration* &rarr; *System Settings* &rarr; *Search* &rarr; *Permission\nChecker*. It includes two system level settings to configure how search\nprocesses User permissions.\n\n**Include Inherited Permissions**\n: Ignore this setting. It's deprecated, no longer used anywhere, and will be\nremoved in a future release.\n\n**Permissions Term Limit**\n: Limits the number of permission search clauses added to the search query\nbefore this level of permission checking is aborted. Permission checking then\nrelies solely on the final permission filtering described below.\n\nThe only reason to limit permissions terms is performance. Users with\nadministrative access to lots of sites and organizations generate many\npermissions terms added to the query. Too many terms in a query can make the\nsearch engine time out.\n\n### Final Permissions Checking\n\nA final round of permission checking happens prior to presenting results in the\nUI. For example, the User searches for *liferay*, and the search engine returns\nall relevant forum posts. As the Search Results iterates through the list of\nrelevant forum posts, it performs one last permission check of the post to\nensure the User can view the post and its categories. If a matching forum post\nexists in a category the User doesn't have permission to view, it isn't\ndisplayed in the list of search results.\n\nThis final round of permission checking is configurable at *Control Panel*\n&rarr; *Configuration* &rarr; *System Settings* &rarr; *Search* &rarr; *Default\nSearch Result Permission Filter*. It includes two settings:\n\n1.  The first setting, Permission Filtered Search Result Accurate Count\n    Threshold, specifies the maximum number of search results to\n    permissions-filter before results are counted. A higher threshold increases\n    count accuracy, but decreases performance. Since results in the currently\n    displayed page are always checked, any value below the search results\n    pagination delta effectively disables this behavior.\n\n2.  The second setting, Search Query Result Window Limit, sets the maximum batch\n    size for each permission checking request. <!-- OR limits the number of\n    results to include in each permission checked request/response cycle to and\n    from the search engine-->. This is again impacted by pagination. For\n    example, if there are 100 results per page, and a User wants to jump all the\n    way to page 200 of the search results, all results between page one and 200\n    must be checked to ensure the User has permission. That's 20,000 results to\n    permissions check. Doing this in one trip to and form the search engine can\n    result in performance issues. Set the maximum batch size for each permission\n    checking request. \n\n## Search and Staging\n\nWith\n[staging](/docs/7-2/user/-/knowledge_base/u/staging-content),\ncontent is placed first in a preview and testing environment before being\npublished for consumption by end Users (on the live site). Content added to the\nsearch index is marked so that the search API can decipher whether an item is\nlive or not. In the live version of the site, only content that's marked for the\nlive site is searchable. \n\nIn the staged version of the site, all content---live or staged---is searchable.\n\n## Result Summaries\n\nA result summary includes the information from a document that the asset's\ndeveloper felt is most useful to end Users searching for the asset. Each asset\ncan have different fields included in the summary. For assets with text content,\na common summary format includes the *title* and some of the *content*, with\ntitle displayed first. The asset type always appears on the second line, and\na snippet of the content that matches the search term is on the last line.\nAssets without content fields, like Documents and Media documents, display the\ndescription instead.\n\nUsers are different. Only the User's full name and the asset type (User) appear\nin User result summaries.\n\n![Figure 1: User summaries contain only the User's full name.](../../../images/search-results-user.png)\n\nFor assets that contain other assets (Web Content and Documents & Media\nfolders) or whose content is not amenable to display (Dynamic Data List Records\nand Calendar Events), it makes more sense to display the title, asset type, and\ndescription in results summaries. There'd never be anything in a content field\nfor these assets.\n\n![Figure 2: Documents and Media and Web Content folders include titles and descriptions in their summaries.](../../../images/search-results-folder.png)\n\nThe asset developer determines which fields are summary-enabled, but there's\nlogic invoked at search time that determines precisely the part of the summary\nfields to display. For example, a `content` field can have a lot of text, but\nthe summary doesn't show it all. Instead, it shows a relevant snippet of the\nfield's text. If the keyword searched for is present in the summary field, that\nportion of the field is used in the summary. In addition, the matching keyword\nis highlighted in the summary.\n\n## Highlighting\n\nBy now you've probably noticed that search terms appearing in the summary are\n<mark>highlighted</mark> by default. If this is undesirable, disable it in the\nwidget configuration screen. \n\n![Figure 3: Some document summaries have lots of highlights if the search term matches text that appears in the summary.](../../../images/search-results-highlight.png)\n\nHighlighting is a helpful visual cue that hints at why the result is returned,\nbut beware. A hit can score well and thus be returned near the top of the\nresults, without having any highlights in the summary. That's because not all\nindexed fields appear in the summary. Consider a User named Arthur C. Clarke. He\nhas an email address of *acc@authors.org*, which is searchable. Because results\nsummaries for Users only contain the full name of the User, searching for Mr.\nClarke by his email address returns the User, but no term is highlighted. \n\n![Figure 4: Results that match the search term won't always have highlights.](../../../images/search-results-no-highlight.png)\n\nThere are additional cases where search results don't have highlighting.\n"
  },
  {
    "path": "en/user/articles/120-search/07-search-insights/01-search-insights-intro.markdown",
    "content": "---\nheader-id: search-insights\n---\n\n# Search Insights\n\n[TOC levels=1-4]\n\n**[Feature intended for testing and development only]**\n\n**[Works with Elasticsearch only]**\n\nAdd the Search Insights Widget to the Search Page to inspect two things: \n\n- The query string that's constructed by the back-end search code when\n    the User enters a keyword \n\n- The response string returned from the search engine\n\n| **Note:** The JSON returned as a request string is pruned from several\n| Elasticsearch query defaults for clarity. To see the full request JSON that\n| Elasticsearch processed, adjust the [Elasticsearch server's\n| logging](https://www.elastic.co/guide/en/elasticsearch/reference/6.x/logging.html).\n\nIn @product-ver@, the Insights widget now adds the response string to\nthe widget's output, and the _Enable Score Explanation_ option (enabled by\ndefault) prints a relevance score explanation for each returned result.\n\nWhen a search query is processed, results are returned. The concept of\n_Relevance_ determines how well results match the query. The Score explanation\nfor returned search documents helps clarify seemingly odd results, letting you\nadjust the relevancy scoring process by making matches in certain fields count\nfor more (_boosting_ the fields is the term for this).\n\n## Inspecting The Search Query String\n\nTo see the Search Insights widget in action, navigate to a Search Page in your\ntest server and add it from the Add menu\n(![Add](../../../images/icon-add-widget.png)).\n\n![Figure 1: The Search Insights widget is helpful during testing and development.](../../../images/search-insights-default.png)\n\nOnce you search for keywords that return Search Results, the Search Insights\nportlet displays the returned query string in all its glory. \n\n![Figure 2: The full query string isn't for the faint of heart. This example is clipped to spare the reader.](../../../images/search-insights-test-search.png)\n\n## Explaining Search Results\n\nTo enable or disable the Explain option,\n\n1.  Open the Search Insight widget's Configuration screen.\n\n2.  There's just one option: _Enable Score Explanation_.  It's a boolean field\n    that's enabled by default.  De-select it to disable the explanation of each\n    result's relevance score.\n\nUnder the hood, the Explain option in the Search Insights widget is exposing an\nElasticsearch API:\n[Explain](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-explain.html).\nSee the Elasticsearch documentation for more details.\n\nHere's an abbreviated portion of the scoring explanation for the Search\nDocument of the Test Test User when the searched keyword was _test_:\n\n    ```json\n    _explanation\":{  \n       \"value\":9.461341,\n       \"description\":\"sum of:\",\n       \"details\":[  \n          {  \n             \"value\":9.461341,\n             \"description\":\"sum of:\",\n             \"details\":[  \n                {  \n                   \"value\":1.0,\n                   \"description\":\"emailAddress:*test*\",\n                   \"details\":[  \n\n                   ]\n                },\n                {  \n                   \"value\":5.0,\n                   \"description\":\"userName:*test*^5.0\",\n                   \"details\":[  \n\n                   ]\n                },\n                {  \n                   \"value\":0.72928625,\n                   \"description\":\"sum of:\",\n                   \"details\":[  \n                      ... \n\n                { \n                   \"value\":1.0027686,\n                   \"description\":\"sum of:\",\n                   \"details\":[  \n                      ...\n                      {  \n                {  \n                   \"value\":0.72928625,\n                   \"description\":\"sum of:\",\n                   \"details\":[  \n                      ...\n                {  \n                   \"value\":1.0,\n                   \"description\":\"screenName:*test*\",\n                   \"details\":[  \n\n                   ]\n                }\n             ]\n          },\n          ...\n       ]\n    }}]}\n    ```\n\nNow you're able to see the entire query string, the response string, and how\neach returned Search Document was scored.\n"
  },
  {
    "path": "en/user/articles/120-search/08-multilanguage-search/01-multilanguage-search-intro.markdown",
    "content": "---\nheader-id: searching-for-localized-content\n---\n\n# Searching for Localized Content\n\n[TOC levels=1-4]\n\n@product@ supports setting a virtual instance-wide [default\nlanguage](/docs/7-2/user/-/knowledge_base/u/more-platform-section-instance-settings#localization)\nand setting a In addition, many out of the box assets [support\ntranslation](/docs/7-2/user/-/knowledge_base/u/other-content-options#localizing-content).\n\nHow an asset's fields are indexed in the search engine plays an important role\nin the end user's experience. Not all assets are indexed in a way that supports\nsearching in a language other than the default language. Even assets that are\ntranslatable might not support searching for the content in that language.\n\nIn short, these assets contain text fields supporting localized search:\n\n| Asset | Fields | Localized Search Approach |\n|-------|--------|---------------------------|\n| Content Page | `title` | 2 |\n| Documents and Media Document | `content` | 3 |\n| Calendar | `name`, `description` | 1 |\n| Calendar Booking | `title`, `description` | 1 |\n| Dynamic Data List Record | `content` | 1 |\n| Form Record | `content` | 1 |\n| Web Content Article | `title`, `content`, `description` |  1 |\n| Asset Category<sup>*</sup> | `title`, `description` |  1 |\n| Asset Tag<sup>*</sup> | `assetTagNames` | 1 |\n| Wiki Page | `title`, `content` |  2 |\n| Blogs Entry | `content`, `title` |  2 |\n| Message Boards Message | `title`, `content` | 2 |\n<!-- | App Builder | `name` | 2 | New with 7.3  -->\n\n<sup>*</sup> Asset tags and categories don't have dedicated documents in the\nindex. Instead, their indexed fields are added to the tagged or categorized\nasset's document.\n\nThere are three localized search approaches represented in the table:\n\n1.  Fully Localized: The asset itself is localizable (in other words,\n    translatable), and its translated fields are indexed into their respected\n    locales.\n\n2.  Fully Localized for Search: Even though the asset itself is not\n    localizable/translatable, its fields are indexed into _all_ the supported\n    locales in the site. This is a new approach, starting with @product-ver@.\n\n3.  Site-Localized for search: The asset's fields are indexed with the site's\n    locale appended.\n\nThere are also assets with text fields and no localization support, meaning that\nthey always index the plain field, without a locale appended (e.g., `title` is\nnot localized, but `title_en_EN` is localized). That means they'll always be\nanalyzed by the default language analyzer, and do not support localized search\nin any capacity.\n\n## What is Localized Search?\n\nIn localized search, fields are indexed with locale information appended (for\nexample, `en_US` for English, making a localized title field indexed as\n`title_en_US`). It's then passed to the proper \n[language analyzer](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/analysis-lang-analyzer.html) \nin the search engine so that the \n[analysis](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/analysis.html) \nprocess is performed properly. Each localization approach is covered below.\n\n## Fully Localized\n\nFully localized search works like this:\n\n1.  One or more of an asset's fields are localizable in the user interface and\n    database (the locale is appended based on the asset creator's language\n    selection).\n\n2.  The fields are indexed with the appended locale and analyzed with the\n    corresponding language analyzer.\n\n3.  At search time, properly indexed and analyzed content is returned according\n    to search engine's relevance algorithms.\n\nThis is the ideal approach for assets that support translation of some or all\nfields outside of the search context.\n\n## Fully Localized for Search\n\nAssets fully localized for search work like this:\n\n1.  The asset's fields are not localizable in the user interface or database.\n\n2.  For at least one text field being indexed, the asset has indexed localized\n    fields for every locale available in the site.\n\n3.  At search time the result document is returned regardless of the search\n    locale, because the content is available in all locales of the site.\n\n## Site-Localized Search\n\nSite-localized search works like this:\n\n1.  The asset's indexed fields are appended with the locale of the site (set in\n    Site Settings) and analyzed with the corresponding language analyzer.\n\n2.  If the site language is changed, reindexing is required to append the proper\n    site locale to the indexed fields and analyze with the corresponding\n    language analyzer.\n\n2.  At search time, if content exists matching the language of the site, it's\n    properly returned according to the search engine's algorithms.\n\nNot all assets support localized search, however. Refer to the table at the\nbeginning of this article for which assets and fields are localized for search.\n\n\n## Assets Supporting Localized Search\n\nWhether an asset supports localized search depends on how the asset was indexed\nin the search engine. At this time, no cohesive pan-asset approach to indexing\nassets for localized search exists. Localized search support is currently\nlimited to the following assets:\n\n### Web Content Articles:\n\n- The `title`, `content`, and `description` fields for each Web Content Article support\n    fully localized search.\n\n    | **Note:** In @product-ver@ the default (non-localized) version of these\n    | fields are not indexed for Web Content Articles. Therefore, any custom\n    | `IndexerPostProcessor`, `ModelDocumentContributor` or\n    | `QueryPreFilterContributor` relying on the presence of fields `title`,\n    | `content` and `description` must be updated to use the localized version\n    | (e.g., `title_en_US`).\n\n- At search time, matching results (with any locale appended) can be\n    returned.\n\n### Categories:\n\n- The `name` and `description` fields support fully localized search.\n\n- At search time, matching results (with any locale appended) can be\n    returned.\n\n### Documents and Media File Entries:\n\n- The `content` field (which contains the content of an uploaded file) supports\n    site-localized search.\n\n- No other fields are indexed with a locale. This means they're always analyzed\n    using the default language analyzer.\n\n### Dynamic Data Mapping Fields:\n\n- Dynamic Data Mapping (DDM) Fields include all form fields created in the Forms\n    application and all fields created in Dynamic Data List Data Definitions and\n    Web Content Structures. \n\n- DDM Fields support fully localized search, with the exception that results can\n    only be returned in the current display locale where the search is taking\n    place.\n\n## Examples\n\nTo see localized search in action, refer to the examples below.\n\n### Fully Localized Search for Web Content Articles\n\n1.  Add a Basic Web Content article in English:\n\n    - Title: _What time is it?_\n    - Summary: _It's soccer time!_\n    - Content: _That's right, it's time for soccer. The 2018 World Cup is behind\n        us, and teams all over the US are gearing up for soccer season. It's\n        never too early to start practicing._\n\n2.  Add a second article in English:\n\n    - Title: _What is the best soccer team ever?_\n    - Summary: _There are many good teams? Which is the best?_\n    - Content: _Here are the 10 best teams in the world: 1. The Lunar Resort's\n        Club Team, Waxing Crescent FC..._\n\n3.  Add a Portuguese (_pt-BR_) translation for each field of the second article:\n\n    - Title: _Qual time de futebol é o melhor de todos os tempos?_\n    - Summary: _Existem muitas boas equipes. Qual é o melhor?_\n    - Content: _Aqui estao as 10 melhores equipes do mundo: 1. Selecao\n        brasileira de Futebol 2. O time do Resort Lunar, Waxing Crescent FC..._\n\n4.  Find a search bar widget and enter _time_ as the keyword.\n\n    The first article is returned, and so is the appropriate translation of the\n    article about soccer teams (because _time_ in Portuguese translates to the\n    English word _team_). Note that if your search context is English, searching\n    for the word _time_ returns the English translation of the Web Content,\n    which does not itself contain the matched keyword. The Portuguese\n    translation contains the matching keyword, while the English translation is\n    returned for English speaking search users.\n\nIn fully localized search, fields are appended with the proper locale, and even\nfields with a locale other than the User's display context are returned if they\ncontain matches to the searched keyword.\n\n### Site-Localized Search for Documents and Media\n\n1.  Create a text file named `search-test.txt` with the following contents: \n\n        Meu time de futebol favorito é o melhor\n\n2. Upload it as a Basic Document to the Documents and Media application.\n\n3.  If your site's language is currently set to English, adding this file \n    appends its content field with the _en\\US_ locale. \n\n4.  Search in the site for the keyword _time_.\n\n    ![Figure 1: Even though the content of this DM File is written in Portuguese, it was appended with the _en_ locale, so it's searchable in an English language site.](../../../images/search-site-localized1.png)\n\n    The file is returned because even though the text in the file is\n    Portuguese, the locale appended to its _content_ field is for English.\n\n5.  Now change the Site's default language to _Portuguese (Brazil)_.\n    Use Site Settings &rarr; Languages to accomplish this.\n\n6. Now search for _time_ in the site, and the document is not returned in the\n   results, because the search is looking for the _pt_ locale.\n\n   ![Figure 2: The uploaded DM File doesn't appear when the site language is changed, because only fields with the site's locale are searched.](../../../images/search-site-localized2.png)\n\n7.  Now go to Control Panel &rarr; Configuration &rarr; Search, and click\n    *Execute* next to _Reindex all search indexes._\n\n8. Search for _time_ in the site's Search Bar again, and now the document is\n   returned in the results, because the content field's locale was changed\n   from _en\\_US_ to _pt\\_BR_ when reindexed.\n\n   ![Figure 3: Once the field is reindexed with the site's locale, it can be returned as a search result in the site.](../../../images/search-site-localized3.png)\n\nIf an asset supports site-localized search, its fields must be reindexed after\nthe site language is changed in order to be returned as search results.\n\n"
  },
  {
    "path": "en/user/articles/120-search/09-search-configuration/01-search-configuration-intro.markdown",
    "content": "---\nheader-id: configuring-search\n---\n\n# Configuring Search\n\n[TOC levels=1-4]\n\n_Configuring Search_ could mean lots of different things: \n\n- System scoped search configuration\n- Reindexing to make sure the search indexes are current with the database\n- Tweaking the search widgets added to pages\n- Creating new Search Pages\n- Configuring the connectors that let @product@ and the search engine\n  communicate\n\nIn fact, _Configuring Search_ means all those things. This is a high level\noverview of what search behavior is configurable out of the box, and\nimportantly, _where_ to find search configuration options.\n\n## System Scoped Search Configuration\n\nSystem scoped search configurations are primarily found in [System\nSettings](/docs/7-2/user/-/knowledge_base/u/system-settings). \n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings*.\n\n2.  Click the *Search* category under the Platform section.\n\n    Alternatively, search for *Search*.\n\n![Figure 1: There are numerous system scoped entries for search in System Settings.](../../../images/search-category-system-settings.png)\n\nThese system scoped configurations are available in System Settings:\n\n### Default Keyword Query\n\nThe Default Keyword Query entry contains one setting:\n\n`disabledEntryClassNames`: The `DefaultKeywordQueryContributor` code\nautomatically adds `description`, `userName`, and `title` fields to the\nkeyword search query. Specify the entry class names\n`DefaultKeywordQueryContributor` should ignore.\n\n### Default Search Result Permission Filter\n\nThe Default Search Result Permission Filter entry allows configuration of\n*post-filtering permission checking* (database permission checking that occurs\nafter the results are returned from the search index). Read\n[here](/docs/7-2/user/-/knowledge_base/u/search-results-behavior#final-permissions-checking)\nfor more information on these settings:\n\n- `permissionFilteredSearchResultAccurateCountThreshold` \n\n- `searchQueryResultWindowLimit`\n\n### Index Status Manager\n\nThe Index Status Manager entry has one setting:\n\n`indexReadOnly`: Suspends all indexing operations and writes to the\nsearch engine. Searches return only the documents already indexed. This is\nuseful for speeding up large data imports, but it should be disabled and a full\nre-index executed once the import is finished.\n\n### Indexer Writer Helper\n\nThe Index Writer Helper entry contains only one valid entry. The second,\n`indexReadOnly`, is deprecated and unused, so setting it has no effect. Use\n`indexReadOnly` from the [Index Status Manager](#index-status-manager) instead.\n\n`indexCommitImmediately`: When *true* (the default), each write request\nforces the search engine to refresh the index reader, potentially flushing\ntransactions to disk. This may negatively impact search engine performance.\nThe default behavior is to commit immediately for index writing on\nindividual assets (e.g. add blog, update blog) but delay commits for bulk\nindex writing operations (e.g.  index all users, index all form entries)\nuntil all entries have been sent to the search engine. Setting this to false\nchanges the behavior for individual index operations, and may cause\napplications like Asset Publisher to exhibit a delayed response when showing\nnewly added content. See the \n[Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/near-real-time.html)\nfor more information.\n\n### Index Registry\n\nConfigure the buffering of index requests:\n\n`buffered`: Disable or configure the buffering of indexing requests. To stop\nthe buffering of index requests, choose *Disabled*. \n\n`bufferedExecutionMode`: Allows administrators to select a different\n`IndexerRequestBufferExecutor`, used to execute a `IndexerRequest`. One\nimplementation of the executor is provided out of the box (_DEFAULT_). When a\ndeveloper creates and deploys a new `IndexerRequestBufferExecutor`\nimplementation, one of the properties they provided is a\n`buffered.execution.mode` which makes the implementation selectable from System\nSettings. \n\n`maximumBufferSize`: If buffering is enabled, set the Maximum Buffer Size so\nthat any additional indexing requests are executed immediately. \n\n`minimumBufferAvailabilityPercentage`: When the capacity of the buffer has\nonly the specified percent of space left, the existing requests in the\nbuffer are executed in one batch and removed from the buffer.\n\n### Index Query Preprocessor\n\nThis entry has one repeatable property (use array syntax if you're defining via \n[OSGi configuration file](/docs/7-2/user/-/knowledge_base/u/creating-configuration-files)):\n\n`fieldNamePatterns`: Fields with names matching the patterns set here are\ntreated as non-analyzed keyword fields. Instead of scored full text queries,\nmatching is performed by non-scored wildcard queries. This is a resource\nintensive operation that degrades search engine performance as indexes grow\nlarger. For substring matching, relying on the \n[ngram tokenizer](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/analysis-ngram-tokenizer.html)\nusually performs better.\n\n### Reindex\n\nThis entry contains only one property:\n\n`indexingBatchSizes`: Set the number of documents indexed\nper batch for model types that support batch indexing. Defaults to 10000. For\nmodels with large documents, decreasing this value may improve stability when\nexecuting a full re-index.\n\n### Engine Helper\n\nThis entry has one repeatable property (use array syntax if you're defining via \n[OSGi configuration file](/docs/7-2/user/-/knowledge_base/u/creating-configuration-files)):\n\n`excludedEntryClassNames`: Exclude an asset type from being\nsearched in the catchall query constructed for the Search application. For\nexample, fields of the Organization asset must be indexed to be searchable\nfrom the Users and Organizations application, but should not be searched in\nthe Search application. Thus, Organizations are added to\n`excludedEntryClassNames`.\n\n### Permission Checker\n\nConfigure *pre-filtering permission checking* (permission checking on the search\nindex) behavior. See\n[here](/docs/7-2/user/-/knowledge_base/u/search-results-behavior#initial-permissions-checking)\nfor more information on these properties:\n\n- `includeInheritedPermission`\n\n- `permissionTermsLimit`\n\n### Title Field Query Builder\n\nConfigure how search responds to matches on the Title field of a document.\n\n**Exact Match boost:** Give an additional boost when searched keywords exactly\nmatch the `title` field of a document.\n\n**Maximum Expansions:** Limit the number of documents to return when matching\nsearched keywords to the `title` field as a phrase prefix. See Elasticsearch's\n[Match Phrase Query\ndocumentation](https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-match-query-phrase.html)\nfor more information.\n\n### Elasticsearch 6\n\nConfigure the connection between @product@ and Elasticsearch 6. See\n[here](/docs/7-2/deploy/-/knowledge_base/d/configuring-the-liferay-elasticsearch-connector)\nfor more information on these properties:\n\n- `clusterName`\n- `operationMode`\n- `indexNamePrefix`\n- `numberOfIndexReplicas`\n- `numberOfIndexShards`\n- `bootstrapMlocakAll`\n- `logExceptionsOnly`\n- `retryOnConflict`\n- `zenDiscoveryUnicastHostsPort`\n- `networkHost`\n- `networkBindHost`\n- `networkPublishHost`\n- `transportTcpPort`\n- `transportAddresses`\n- `clientTransportSniff`\n- `clientTransprtIgnoreClusterName`\n- `clientTransportPingTimeout`\n- `clientTransportNodesSamplerInterval`\n- `HttpEnabled`\n- `HttpCrsEnabled`\n- `HttpCorsAllowOrigin`\n- `HttpCorsConfigurations`\n- `additionalConfigurations`\n- `additionalIndexConfigurations`\n- `overrideTypeMappings`\n- `synchronizedSearch`\n\n### Search Web\n\nThis entry contains one property: `classicSearchPortletInFrontPage`: Revert the\ndefault search experience from using the new Search Widgets to the classic\nSearch Portlet that was standard in past releases. See\n[here](/docs/7-2/user/-/knowledge_base/u/configuring-search-pages#legacy-search-experience)\nfor more information.\n\n### Search Administration\n\nIn *Control Panel* &rarr; *Configuration* &rarr; *Search* there are two\nadministrative UIs: Index Actions and Field Mappings\n\n#### Index Actions\n\nIn Index Actions, re-index one of these:\n\n    - All indexable assets\n    - An individual indexable asset\n    - All spell check indexes\n\n#### Field Mappings\n\nThe Field Mappings tab shows you all field mappings that are effective in the\nsystem, by index. Currently, you can view the mappings, copy them, zoom in or\nout, and view them with a dark theme. Look for added functionality to this UI in\nfuture versions.\n\n### Portal Properties\n\nPortal properties are system scoped configurations as well. The \n[Lucene Search](https://docs.liferay.com/portal/7.2-latest/propertiesdoc/portal.properties.html#Lucene%20Search)\nportal properties configure low level search behavior. Review the properties and\ntheir descriptions and determine if they apply to your search requirements.\n\n## Site Scoped Search Configuration\n\nSearch isn't configurable at the Site Scope by the strict definition of [Site\nScoped Configuration](/docs/7-2/user/-/knowledge_base/u/setting-up#configuration-scope).\nHowever, [Search\nPages](/docs/7-2/user/-/knowledge_base/u/configuring-search-pages) influence\nsite-specific search behavior. Commonly, Search Pages contain search widgets\nconfigured to search for all content within a particular Site.\n\nIn addition, the Header Search (the Search Bar embedded in every Site page by\ndefault), whether populated by the new Search Bar widget or the legacy Search\nportlet, is Site scoped. Only one instance of the Header Search application\nexists per Site, and configuring it in one page context configures it for the\nentire Site. \n\nBecause of the modularity of Search, there are some important configuration\nnuances to be aware of when using the new Search widgets:\n\n- If the Header Search uses the Search Bar widget, its configuration\n  always requires a _destination page_ to be set, where Users are redirected to\n  complete their search activity, interacting with the other Search widgets\n  (Results, Facets, Suggestions etc.). [Search destination\n  pages](/docs/7-2/user/-/knowledge_base/u/configuring-search-pages) are\n  ordinary pages holding the Search widgets. You can have as many pages with\n  Search widgets across the Site as you want.\n\n- Unlike the legacy Search portlet, the new Search Bar widget is instanceable,\n  so one page can contain multiple Search Bar widgets configured differently.\n  All Search Bar instances must point to a Search Page to be effective.\n\n- **Important**: if the destination Search Page has a Search Bar widget\n  instance besides the embedded Header Search, the configurations of the\n  Header Search take precedence over the page's widget instance.\n\n  Conversely, searching from a Search Bar widget instance on other pages honors\n  their configurations, even if they differ from the Header Search\n  configuration. \n\nSee the documentation on [configuring of a Search\nBar](/docs/7-2/user/-/knowledge_base/u/searching-for-assets#configuring-the-search-bar)\nfor more information.\n\n## Widget Scoped Search Configuration\n\nSeveral search widgets are available, and each one has its own configuration\noptions:\n\n**Search Results**\n:  Configure how search results are displayed. Read \n[here](/docs/7-2/user/-/knowledge_base/u/search-results) for more\ninformation.\n\n**Search Bar**\n: Configure the behavior of how search keywords are processed. See\n[here](/docs/7-2/user/-/knowledge_base/u/searching-for-assets#configuring-the-search-bar) \nfor more information.\n\n**Search Facets**\n: Configure each facet's behavior and URL parameters. See\n[here](/docs/7-2/user/-/knowledge_base/u/facets) for more information.\n\n**Search Options**\n: This is a special case, where configuring this widget defines page scoped\nbehavior. Add the Search Option widget to a page and define two booleans for\nthe Search Page: \n\n- Allow Empty Searches: By default, failure to enter a keyword returns no\n  results. Enabling this ensure that _all_ results are returned when no\n  keyword is entered in the Search Bar.\n\n- Basic Facet Selection: By default, facet counts are recalculated after each\n  facet selection. Enable this to turn off facet recounting.\n\n**Search Suggestions**\n: Suggest better queries and spell check queries. See\n[here](/docs/7-2/user/-/knowledge_base/u/searching-for-assets#search-suggestions) \nfor more information.\n\n**Search Insights**\n: Add this to the Search Page to inspect the full query string that's\nconstructed by the back-end search code when the User enters a keyword. Only\nuseful for testing and development.\n\n**Custom Filter**\n: Add a widget to the page for each of the filters you'd like applied to the\nsearch results. Let search page users see and manipulate the filters or make\nthem invisible and/or immutable.\n\n**Sort**\n: Let Users reorder the search results based on the value of certain `keyword`\nfields in the index. For example, show results in alphabetic order of the Title\nfield. The default order is determined by the search engine's _Relevance_\ncalculation.\n\n"
  },
  {
    "path": "en/user/articles/120-search/10-low-level-search-options.markdown",
    "content": "---\nheader-id: low-level-search-options-searching-additional-or-alternate-indexes\n---\n\n# Low Level Search Options: Searching Additional or Alternate Indexes\n\n[TOC levels=1-4]\n\nLow level search is a new concept in @product@ version 7.2: it's a search that\ndoesn't go through the \n[Search and Indexing Framework](/docs/7-2/frameworks/-/knowledge_base/f/model-entity-indexing-framework),\nwhich is infrastructure used for searching documents in the Liferay Index.\n\nA common use case for a low level search is to query an index other than the\n@product@ index. By default, \n[Search Pages](/docs/7-2/user/-/knowledge_base/u/configuring-search-pages) search the\n@product@ index, but you can also search another index, as long as it's in the\nsame Elasticsearch cluster (this feature does not work with Solr). \n\nAdd the Low Level Search Options widget to a search page and configure it to\ndirect the search to the alternate index. To search multiple indexes from the\nsame page, you can add multiple Low Level Search Options widgets and configure\neach one with its own Index Name and Federated Search Key.\n\nSearching alternate indexes is a low level operation that bypasses the @product@\npermission checking mechanisms, presenting whatever results the search engine\nreturns. For this reason, only administrators can add and configure the Low\nLevel Search Options widget. \n\nTo use the Low Level Search Options widget, add it to a Search Page:\n\n1.  Click the Add menu (![Add](../../images/icon-add-widget.png)) on the page to\n    open the Add Widgets menu.\n\n2.  Drag the Low Level Search Options widget (from the Search section), and drop\n    it on the page.\n\nIt doesn't do anything unless you configure it.\n\n## Configuring Low Level Search\n\nThere are several configuration options for the widget. Access them by clicking\nthe widget Options menu (![Options](../../images/icon-app-options.png)) &rarr;\nConfiguration, or by clicking the hypertext URL in the widget body:\n\n_Configure additional low level search options in this page._\n\n![Figure 1: The Low Level Options widget has several configuration options.](../../images/search-lowlvl-options.png)\n\n**Indexes:**\n: Enter the comma-separated names of the alternative indexes to search. Do not\nenter the standard Liferay index name.\n\n**Fields to Return:**\n: Enter the names of the stored fields to be returned from the search engine in\na comma-separated list. Leave it blank to return all stored fields.\n\n**Contributors to Include:**\n: Enter the ids of registered search contributors to be included in this search\nin a comma-separated list of each `SearchRequestContributor`'s Fully Qualified\nClass Name (e.g.,\n`com.liferay.docs.request.contributor.MySearchRequestContributor`). If not set,\nall registered search contributors are applied.\n\n**Contributors to Exclude:**\n: Enter the ids of registered search contributors to be excluded from this\nsearch, in a comma-separated list. If not set, all registered search\ncontributors are applied.\n\n| **Note:** These _Contributors_ are components implementing the\n| `com.liferay.portal.search.spi.searcher.SearchRequestContributor` interface\n| (provided by the `com.liferay.portal.search.spi` artifact), which is an\n| extension point (SPI) that intercepts search requests and adds query parts.\n\n**Federated Search Key:**\n: Enter the key of an alternate search this widget is participating in. If not\nset, this widget participates in the default search. This value is usually the\nname of an application-defined index.\n\n## Example: Searching an Alternate Index \n\n1.  Whether testing on the default search page or creating a new \n    [Search Page](/docs/7-2/user/-/knowledge_base/u/configuring-search-pages), include\n    the following widgets (removing extra widgets simplifies the exercise, but\n    is not required for it to work):\n\n    - Low Level Search Options\n    - Custom Filter\n    - Search Bar\n    - Search Results\n\n2.  Configure all the widgets to participate in an alternate search, by opening\n    the widget's Options menu (![Options](../../images/icon-app-options.png)) and\n    clicking _Configuration_. For each, enter _liferay-0_ in the Federated\n    Search Key setting.\n\n    All the search widgets expected to react appropriately to the alternate\n    search must be configured with the Federated Search Key. The following steps\n    detail additional configuration.\n\n3.  Make an additional configuration in the Low Level Search Options widget,\n    adding the index name of the alternate index:\n\n    Enter at least one index name in the _Indexes_ setting. To follow this\n    example, use _liferay-0_.\n\n4.  Configure the Custom Filter to use the search bar's default query parameter\n    (*q*) and add a query to the search:\n\n    Enter _title_ under field name to add the title field to the query.\n\n    Choose a Filter Query Type (e.g., Match) for the field.\n\n    Since you're overriding the default query to search an alternate index,\n    there's nothing in the query by default. Add any query clauses using the\n    Custom Filter widget(s).\n\nIf you're using _liferay-0_ in your Federated Search Key and Indexes settings,\nsearch for _dynamic_ in the search bar. You'll see results like this:\n\n![Figure 2: Configure the search page to search a different index.](../../images/search-federated.png)\n\nNow you're able to configure the out of the box search widgets to participate in\nsearches against any Elasticsearch index in the cluster.\n"
  },
  {
    "path": "en/user/articles/130-forms/01-forms-intro.markdown",
    "content": "---\nheader-id: forms\n---\n\n# Forms\n\n[TOC levels=1-4]\n\nLiferay Forms gives you robust form building capability. For a complete list of\nthe form fields available, visit the [form fields reference\narticle](/docs/7-2/user/-/knowledge_base/u/form-field-types). \n\nBecause the complexity of use cases for Forms varies from a single input field\nto many pages of fields with different configurations, it makes sense to show\nyou how to build and publish simple forms very quickly, and then show you all\nthe additional features you can use for more complex use cases. Here's\na sampling of the what the Forms application can do:\n\n- Populate a Select or Radio field with a REST Data Provider\n- Make a field appear based on the value of another field\n- Add extra pages to the form\n- Enable CAPTCHA for a form\n- Store results in JSON\n- Enable workflow for the form\n- Redirect to a different URL after a successful form submission\n- Send an email notification to administrators whenever a form is submitted\n- Provide a default value (entered if left alone by the user) or a placeholder\n  value (not entered if left alone by user) for each field\n- Validate fields using a number of different criteria\n- Redirect users to a success page after form submission\n- Define Form Rules to create dynamic form behavior (for example, show or hide a\n  field based on input in another field).\n- Translate form text into any supported language.\n- Create partial forms (with fields and other elements and specific\n  configurations) and save them for reuse.\n- Drag and drop fields onto the form layout.\n- Duplicate a form instead of starting a similar form from scratch.\n\nDespite this long list of more complex options, developing a simple, elegant\nform to suit basic needs takes little effort. The next article covers basic form\nbuilding.\n\n## Forms and Lists\n\nWhen you need a form, what you're really looking for is data. There are two\napplications for building forms to collect precisely the data you need:\n\n1.  [Liferay Forms](/docs/7-2/user/-/knowledge_base/u/forms):\n    The primary form building application is for the simplest one or two\n    question survey to the most complex, multi-page, homeowners insurance\n    application containing rules and lists populated by a REST data provider.\n\n2.  [Dynamic Data Lists (DDL)](/docs/7-2/user/-/knowledge_base/u/dynamic-data-lists):\n    Provides a user interface tool for building reusable form- and list-based\n    applications intended for display on pages, using\n    [templates](/docs/7-2/user/-/knowledge_base/u/using-templates-to-display-forms-and-lists).\n\n| **Kaleo Forms:** If you're a Liferay Digital Enterprise customer, there's a\n| third form building tool called\n| [Kaleo Forms](https://help.liferay.com/hc/en-us/articles/360028821952-Kaleo-Forms).\n| It integrates form building with workflow to create form-based business\n| processes, like a Conference Room Checkout Form, or a Support Ticket Process so\n| support tickets go through the proper channels on their way to resolution. Read\n| more about Kaleo Forms in the\n| workflow [section](https://help.liferay.com/hc/en-us/articles/360028821952-Kaleo-Forms).\n\n## Which Form Builder Should I Use?\n\nLiferay Forms (also referred to as Forms) is a relatively new application, first\nappearing in @product@ version 7.0. If you can use Liferay Forms for your use\ncase, you should.\n\nSo the question \"Which form builder should I use?\" can be restated to \"When\nshould I use Dynamic Data Lists?\"\n\n- Use Dynamic Data Lists (DDL) if you need a way for users to enter data, *and*\n  you need to display the data in the user interface.\n\n- Use DDL if you need to style your lists and forms with templates.\n\n- Use DDL if there's a field type you need that's not included (yet) in Liferay\n  Forms. These are the field types included in DDL that *are not* in Liferay\n  Forms at the time of this writing:\n        - Color\n        - Geolocation\n        - Web Content\n        - Link to Page\n\nIt's important to note that these (and more!) form field types will be\nincluded in future versions of the Liferay Forms application.\n\nWhen all form building features are fully merged into Liferay Forms, the best\nfeatures of DDL, all the new features of Liferay Forms, and all future\nimprovements will be in one application. Now is the time to familiarize yourself\nwith Liferay Forms and begin using it for all your form building needs, except\nfor the narrow use cases described above.\n"
  },
  {
    "path": "en/user/articles/130-forms/02-creating-forms.markdown",
    "content": "---\nheader-id: creating-and-managing-forms\n---\n\n# Creating and Managing Forms\n\n[TOC levels=1-4]\n\nThe Forms widget can do a lot of things really well, but if you just need a\nsimple form, how do you wade through all the features you don't need? Is your\nsimple survey going to make you late for that lunch outing you've been planning\nwith colleagues at that new shawarma place? No!\n\nLet's create a simple yet elegant form, give access to the intended users, and\nget you on your way to lunch.\n\nAt The Lunar Resort, it's important to capture guests' feedback about their stay\nat the resort. After a (hopefully) safe journey home, guests should receive an\nemail with a link to brief survey that prompts them to rate their stay from\na list of selections, and add any additional comments they'd like in an optional\nfield.\n\n![Figure 1: Get feedback from guests of The Lunar Resort.](../../images/forms-guest-survey.png)\n\n## Viewing Forms\n\nWhether creating a form or managing existing forms, it all starts in the same\nplace: the Forms Application in your site's Content section. Access this in the\nMenu, first choosing the site to work in (for example, The Lunar Resort) and\nclicking _Content & Data_  &rarr; _Forms_. The first thing you'll see is a list of\nexisting forms (if there are any). This list is styled by the Display Style\nselector next to the Add button (![Add](../../images/icon-add.png)). By\ndefault, forms are displayed in List format.\n\n![Figure 2: Forms are displayed in List format by default.](../../images/forms-list-view.png)\n\nThere's also a Table format. Change the style for a single site right here in \nthe Forms site menu application, or change the default display style for the\nsystem scope in Control Panel &rarr; Configuration &rarr; System Settings &rarr;\nForms (in the Content section). Click the *Forms* entry and find the Default\nDisplay View property. Click *Update* and your changes are propagated to all\nsites.\n\n## Building a Form\n\nTo add a new form, \n\n1.  Click the *Add* button (![Add](../../images/icon-add.png)). The form builder\n    view appears. \n\n2.  Name the form. Replace *Untitled Form* with *Guest Survey*.\n\n3.  For the description text enter *Tell us how your stay was!*\n\n4.  Add the fields. Click the *Add* button (![Add](../../images/icon-add.png))\n    to open the sidebar if it's not already opened.\n\n    ![Figure 3: You can choose from nine field types when creating forms.](../../images/forms-sidebar.png)\n\n5.  Drag a *Select from List* field onto the form builder and configure it like \n    this:\n\n    **Label:** *Rate your visit to The Lunar Resort.*\n\n    **Help Text:** Leave this blank for now. If you want a subheading for your\n    field to provide additional guidance, this would be useful.\n\n    Turn on the *Required Field* selector. At a minimum, this form must capture\n    whether guests like their stay or not.\n\n    Leave the manual option checked for creating the list of selections. To\n    learn about populating the field with a data provider, read\n    [here](/docs/7-2/user/-/knowledge_base/u/data-providers).\n\n    Add these options:\n\n    - *It was out of this world!*\n    - *I had a good time.*\n    - *I'd rather go to the beach.*\n    - *I'll never come back.*\n\n    Typing in one of the fields automatically adds another blank selection line.\n    Just leave the last one blank when you're done.\n\n6.  To see additional options, click *Properties*. Close the sidebar when\n    finished.\n\n7.  Add a text field, using the same procedure you used for the select\n    field.\n\n    **Label:** *Want to tell us more?*\n\n    **Help Text:** Leave this blank again to give the form a consistent look.\n\n    **My text field has:** Choose *Multiple Lines*. Let guests prattle on about\n    their stay if they want to.\n\n    **Required Field:** Leave this unselected. Only require guests to fill out\n    the select field and leave this one as optional.\n\n8.  Close the sidebar.\n\n9.  In the form builder, you can see the way the fields are laid out on the form\n    page.\n\n    ![Figure 4: The form builder page lets you preview your form layout, add a page to the form, or add some more fields.](../../images/forms-form-builder.png)\n\n10.  When the form is finished, click *Save Form*. It's also auto-saved every\n     minute by default.\n\n11.  Click *Publish Form*. A dedicated URL to the form is generated, but nobody\n     has the URL yet.\n\nNow your form can be added to a page, and Lunar Resort guests can be emailed and\nprovided with a link to the page where the form is displayed.\n\n## Accessing Forms\n\nOnce the form is developed and published, there are two options for getting the\npublished form to targeted users:\n\n1.  Place the Form widget on a site page. This approach lets users navigate to \n    the page in the site.\n\n2. Copy the dedicated form URL and provide it to users (for example, via email).\n   This limits access to the form to only those users who have the direct link.\n\nTo display the form on a site page in The Lunar Resort site:\n\n1.  Add a page to the site (choose Full Page Application for the page type if\n    you only want the form application on the page). Call it *Guest Survey*.\n\n    ![Figure 5: Add a page for guests to view and fill out your new form.](../../images/forms-guest-survey-page.png)\n\n2.  Add the Form widget to the page if you've chosen a Widget Page. If you used\n    a full page application, use the page configuration to choose *Form* from\n    the Full Page Application dropdown.\n\n3.  Once the Form widget is on the page, click *Select Form*, choose the \n    *Guest Survey* form, and click *Save*.\n\n4.  Close the _Form---Configuration_ dialog window and your form is ready for\n    Lunar Resort site users.\n\nTo display the form on a dedicated page accessed only by its link:\n\n1.  In the form builder, click *Publish Form* if you haven't already.\n\n    ![Figure 6: You must first publish a form before you can get a shareable link.](../../images/forms-link-grayed.png)\n\n2.  Once published, click the link icon at the top right of the builder.\n\n    ![Figure 7: Copy the link to your form.](../../images/forms-link.png)\n\n3. Once you get the link out to users, it's showtime.\n\n    ![Figure 8: Lunar Resort guests can use a simple form to record their feelings about the resort.](../../images/forms-guest-survey.png)\n\nNext you'll learn how to view the form entries. Since there aren't any yet, fill\nout and submit the form a few times. \n\nNow you know the basics of creating and managing forms, but this presentation\ndidn't do the Forms application justice. It's much more powerful than hinted at\nhere. The remaining articles in this section immerse you in more advanced form\nbuilding features.\n"
  },
  {
    "path": "en/user/articles/130-forms/03-managing-form-entries.markdown",
    "content": "---\nheader-id: managing-form-entries\n---\n\n# Managing Form Entries\n\n[TOC levels=1-4]\n\nOnce users begin submitting form entries, you'll want to do these things with\nthem:\n\n- [View form entries](#viewing-form-entries)\n- [Export form entries](#exporting-form-entries)\n- [Delete form entries](#deleting-form-entries)\n\nStart by learning how to access and view the entries.\n\n## Viewing Form Entries\n\nWhen users fill out forms, they're generating data. You'll want to see that data\nat some point. \n\n1.  From the Menu, navigate back to the *Content* &rarr; *Forms* section of The\n    Lunar Resort site.\n\n2.  Click the *Actions* (![Actions](../../images/icon-actions.png)) button for\n    the form and select *View Entries*.\n\n    ![Figure 1: You can view the entries right in the Forms application. ](../../images/forms-view-entries.png)\n\n3.  What if you have a lot of form fields and you can't see all the data for\n    each entry in the search container? Just click the *Actions*\n    (![Actions](../../images/icon-actions.png)) button for the entry and select\n    *View*. You're shown all the specifics for that form entry.\n\n    ![Figure 2: You can view a single entry right in the Forms application.](../../images/forms-view-entry.png)\n\nViewing entries is great, but this is serious data we're talking about. You\nmight need to get all the entries into a spreadsheet so you can work with them. \n\n## Exporting Form Entries\n\nSo you need to put your form entries in a spreadsheet to do spreadsheet things\nwith them? No problem.\n\n1.  Navigate to the Forms application in The Lunar Resort site's Content & Data\n    section again.\n\n2.  Click the *Actions* (![Actions](../../images/icon-actions.png)) button and\n    select *Export*. \n\n3.  Choose a File Extension. You can export entries in CSV, JSON, XLS, or XML formats \n    by default. For this example, pick CSV.\n\n4.  Click *Okay*, and open the file or save it locally. Open it with your\n    favorite spreadsheet program and verify your form entries.\n\n![Figure 3: You can export entries as CSV, JSON, XLS, or XML.](../../images/forms-export-csv.png)\n\n| **Note:** The Forms application itself has an *Import/Export* window accessible\n| from the application's Configuration menu\n| (![Configuration](../../images/icon-options.png)). This is how you import and\n| export the application configuration and its data (forms and form entries). The\n| file format for this type of import and export is a LAR file. For more\n| information, see the article on\n| [importing and exporting application content](/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content).\n\nThere's a system level setting to determine whether administrators can export\nentries in CSV format:\n\n1.  Go to Control Panel &rarr; Configuration &rarr; System Settings and click\n    the *Forms* category in the CONTENT AND DATA section.\n\n2.  Click the *Forms* entry under SITE SCOPE.\n\n3.  The CSV Export property has three options:\n\n    - *Enabled* to enable CSV Export without a warning\n    - *Enabled (Show Warning)* to enable CSV Export with the following warning\n        to administrators:\n            This CSV file contains user supplied inputs. \n            Opening a CSV file in a spreadsheet program may be dangerous.\n    - *Disabled* to turn off CSV Export.\n\nOnce you export a batch of form entries, it can make sense to delete them from\nthe database.\n\n## Deleting Form Entries\n\nWhat if you export a form's entries and now you want to remove them from the\nLiferay database? It's easy to delete all of a form's entries at once.\n\n1.  Navigate back to the Forms application In The Lunar Resort Content section.\n\n2.  Click the *Actions* (![Actions](../../images/icon-actions.png)) button next\n    to the Guest Survey form and select *View Entries* again.\n\n3.  Select all entries by checking the box next to *Filter and Order*. An X\n    appears in the top right corner of the Form Entries screen. Click it.\n\n    ![Figure 4: Delete all form entries in one fell swoop.](../../images/forms-delete-entries.png)\n\nIf you just wanted to delete a single entry, select that entry by checking its\nbox; then delete it.\n\nIf you're worried about deleting everything irrecoverably by accident, don't\nworry. You must confirm the deletion in a dialog box that pops up after clicking\nthe trash can.\n\nNow you can create basic forms and manage the entries. Keep reading in this\nsection to learn about the many additional form building features available to\nyou.\n"
  },
  {
    "path": "en/user/articles/130-forms/04-form-fields.markdown",
    "content": "---\nheader-id: form-field-types\n---\n\n# Form Field Types\n\n[TOC levels=1-4]\n\nA form without fields is no form at all. To meet your form-building needs,\nLiferay Forms provides useful and highly configurable field types. \n\n![Figure 1: There are many useful out-of-the-box form field types.](../../images/forms-field-types.png)\n\n**Paragraph:** This is static text on the form. Users do not enter data into \nform text fields. The form creator enters text that form users see displayed on \nthe form. This is useful for longer instructions. \n\n![Figure 2: Use Paragraph fields to enter longer instructions on Form Pages.](../../images/forms-paragraph.png)\n\n**Text Field:** Users enter text into these fields. For example, a Full Name \nfield is a text field. By default, a text field keeps all input on a single line \nof text. To accommodate longer responses, choose the multi-line setting when \nconfiguring the text field \n[as in this example](/docs/7-2/user/-/knowledge_base/u/creating-and-managing-forms#building-a-form). \nYou can put limits on the text users can enter (e.g., numbers from 1-10, email \naddresses, etc.) by using the text field's \n[validation options](/docs/7-2/user/-/knowledge_base/u/validating-text-and-numeric-fields). \n\n![Figure 3: Text fields can be single line or multi-line.](../../images/forms-multiline.png)\n\n**Select from List:** Users select one option (or more, if configured to allow \nit) from a list of choices. Choices are entered manually or are automatically \npopulated by a data provider. For example, a Country of Residence field can be \nselected from list field populated by a Countries of the World data provider. \n\n![Figure 4: Use a select from list field to let Users choose predefined options.](../../images/forms-select-list.png)\n\n**Single Selection:** Using a radio button, users select one option from a list \nof options displayed on the form. \n\n![Figure 5: Single selection fields allow only one selection.](../../images/forms-single-selection.png)\n\n**Date:** Users select a date using a date picker. For example, a Birth Date \nfield uses the Date field type.\n\n![Figure 6: Date fields show a date picker so Users enter a valid date.](../../images/forms-date.png)\n\n**Multiple Selection:** Users select one or more options from check boxes (or \ntoggles, if configured).\n\n![Figure 7: A multiple selection field can use a toggle.](../../images/forms-switcher.png)\n\n**Grid:** Using radio buttons, users select from options laid out in rows and \ncolumns. One selection can be made per row. This is useful when the same \nresponse metric is needed for multiple questions. For example, a product survey \nform might ask users to rate a list of their product's characteristics as \n*Wonderful*, *Pretty Good*, *Not So Good*, or *Awful*. \n\n![Figure 8: Grid fields use the same options (columns) for multiple categories (rows).](../../images/forms-grid.png)\n\n**Numeric:** Users enter numeric data (integers or decimals) into numeric \nfields. Non-number input is not accepted. For example, configure a numeric field \nthat accepts integers to ask users how many pets they have. \n\n![Figure 9: Numeric fields accept only numeric input.](../../images/forms-numeric.png)\n\n**Upload:** Users can select a file from the Documents and Media library or \nupload a file from their local filesystems. \n\n![Figure 10: Upload fields let Users attach files to the form.](../../images/forms-upload.png)\n"
  },
  {
    "path": "en/user/articles/130-forms/04-form-rules/01-intro.markdown",
    "content": "---\nheader-id: form-rules\n---\n\n# Form Rules\n\n[TOC levels=1-4]\n\nChickens don't follow rules well, but dogs do. If you're skeptical, try teaching\nyour chicken to sit on command or herd sheep. Better yet, get a team of chickens\nto pull a sled in the [Iditarod](http://iditarod.com). The Forms application is\nmuch more like the dog than the useful (southwestern omelet anyone?) but\nuntrainable chicken, and it's only getting more trainable as time passes. \n\nForm rules are a good example of the trainable nature of the Forms application.\nWith form rules, you can train your form fields to behave as you wish. There are\nseveral things you can make them do: \n\nShow/hide\n: Based on a predefined condition, set the visibility of a form field.\n\nEnable/disable\n: Use a predefined condition to enable or disable a field.\n\nRequire\n: Use a predefined condition to make a field required.\n\nJump to Page\n: Based on user input, skip over some form pages directly to a relevant\npage. _This rule doesn't appear in the rule builder until a second page is added\nto the form_.\n\nAutofill with Data Provider \n: Use a [data provider](/docs/7-2/user/-/knowledge_base/u/data-providers)\nto populate fields when a condition is met in another field.\n\nCalculate\n: Populate a field with a calculated value using data entered in other fields.\n\nForm rules are for changing fields and form elements by acting on conditions.\n\n*If [condition] do [action].*\n\nIf you're not already familiar with the Forms application, start\n[here](/docs/7-2/user/-/knowledge_base/u/forms). Once you know how to create\nforms, add and configure fields, and then publish forms, come back here and\nlearn about form rules.\n\n## The Anatomy of a Form Rule\n\nEach rule consists of one or more conditions and actions. \n\n*Conditions* determine whether any actions are executed. \n\n*Actions* determine what happens if the condition is met.\n\nRules are stored in the database in JSON format by default.\n\n## Creating Form Rules: Rule Builder\n\nOnce you create a form and lay out its fields, you're ready to set up rules in\nyour form: \n\n1.  Save the form.\n\n2.  Open the Rule Builder by clicking the *Rules* tab at the top of the *Edit\n    Form* screen.\n\n3.  In the rule builder view, you can now begin developing your form rule. Click\n    the *Add* button (![Add](../../../images/icon-add.png)) to get started.\n\n![Figure 1: The Rule Builder gives you a handy interface for creating dynamic form rules.](../../../images/forms-rule-builder.png)\n\nBefore looking at each type of rule condition and action you can use to develop\nrules, consider the *OR* selector box at the right side of the *Condition* (it's\ngrayed out and unusable at first). You can choose *OR* or *AND* here, depending\non what relationship the conditions should have with the action.\n\nOR\n: The action is triggered if *any* of the conditions you specify evaluates to\n*true*\n\nAND\n: The action is triggered only if *all* the conditions you specify evaluate to\n*true*\n\nThis box becomes usable once you click the Add button\n(![Add](../../../images/icon-add.png)) to add an extra condition.\n\n## Conditions\n\nConditions are the gatekeepers of form rules. If the condition's *if statement*\nevaluates to *true*, the action is triggered. If it evaluates to *false*, no\naction happens.\n\nA condition checks whether one field's value\n\n- *Is equal to* a specific value or another field's value.\n- *Is not equal to* a specific value or another field's value.\n- *Contains* a specific value or another field's value.\n- *Does not contain* a specific value or another field's value.\n- *Is empty*. This assumes you want to do something if a field *is* empty.\n- *Is not empty*. This assumes you want to do something as long as a field is\n    *not* empty.\n\nOne exception to this is the User condition, which is the last option in the\nCondition dropdown menu.\n\nThe User condition doesn't act on a field at all. It checks whether a User\nbelongs to a certain role. For example, if the condition\n\nIf `User` *belongs to* `Administrator` \n\nevaluates to *true*, an action is triggered. \n\nA condition is the gateway into a form rule, but actions define what actually\nhappens when the condition evaluates to *true*. The remaining articles discuss\nthe available actions and demonstrate their use.\n"
  },
  {
    "path": "en/user/articles/130-forms/04-form-rules/02-show-hide-rules.markdown",
    "content": "---\nheader-id: action-show-and-hide\n---\n\n# Action: Show and Hide\n\n[TOC levels=1-4]\n\nWith a show and hide rule, you use one or more conditions to determine whether\nto show or hide a field if the condition evaluates to *true*.\n\nTo set this example up, add these fields to a form: \n\n- *I am 18 Years Old or Older*, a required single selection field with two options:\n  *Yes* and *No*.\n\n- *Legal Guardian Email Address*, a text field that accepts valid email\n  addresses (use text field validation to dictate input type).\n\n| **Example:** If you're under 18 years old, you need the approval of a legal\n| guardian to drive a sled in a sled dog race (even if you're racing chickens, not\n| dogs). The form for registering your chicken team asks you the age of the\n| driver. If you enter a number less than 18, the Legal Guardian Email Address\n| field appears.\n| \n| To configure a Show/Hide rule,\n| \n| 1.  Open the Rules tab of the Edit Form page and click the Add\n|     (![Add](../../../images/icon-add.png)) button.\n| \n| 2.  Define the rule:\n|      - If the *I am 18 years old or older* field is equal to the Value *No*, show the\n|        *Legal Guardian Email Address* field.\n| \n|     ![Figure 1: Build form rules quickly by defining your conditions and actions.](../../../images/forms-rule-development.png)\n| \n|      - Save the rule.\n| \n|     ![Figure 2: Once a rule is saved, it is displayed so that you can easily understand what it does.](../../../images/forms-rule-list.png)\n| \n| Now the *Legal Guardian Email Address* field is only displayed in the form if\n| the user selects *No* in the *I am 18 years old or older* field.\n\nShow rules let you keep a field hidden until some condition is met.\n"
  },
  {
    "path": "en/user/articles/130-forms/04-form-rules/03-require-rules.markdown",
    "content": "---\nheader-id: action-require\n---\n\n# Action: Require\n\n[TOC levels=1-4]\n\nUse a require rule to make a field required based on one or more conditions.\n\n| **Example:** If you are following the example, you already set up a _show_ rule,\n| where a *Legal Guardian Email Address* field appears if the user selects *No* in\n| the *I am 18 years old or older* field. You also want to make the *Legal\n| Guardian Email Address* field required.\n| \n| To configure a require rule,\n| \n| 1.  Edit the\n|     [Show Rule](/docs/7-2/user/-/knowledge_base/u/action-show-and-hide)\n|     configured above. Open the Rules tab of the Edit Form page and click the\n|     kebab menu (![Actions](../../../images/icon-actions.png)) for the rule, and\n|     then click *Edit*.\n| 2.  Add an Action to the rule:\n|      - If the *I am 18 years old or older* field is equal to *No*, show the\n|        *Legal Guardian Email Address* field and make it required.\n| \n|     ![Figure 1: Build form rules quickly by defining your conditions and actions.](../../../images/forms-require-rule.png)\n| \n|      - Save the rule.\n| \n|     ![Figure 2: Once a rule is saved, it is displayed so that you can easily understand what it does.](../../../images/forms-require-rule2.png)\n\nRequire rules let you require fields based on input from other fields.\n"
  },
  {
    "path": "en/user/articles/130-forms/04-form-rules/04-enable-rules.markdown",
    "content": "---\nheader-id: action-enable-and-disable\n---\n\n# Action: Enable and Disable\n\n[TOC levels=1-4]\n\nUse an enable/disable rule to make a field editable based on one or more conditions.\n\n| **Example:** Part of the race registration fee pays for dog food. You don't have\n| to feed your chicken team with the provided dog food though. There's a single\n| selection field that asks *Would you like to use the provided dog food?*. If you\n| select *Yes*, you can select how much food, in US pounds, you'll need for your\n| team throughout the race. Since you're racing chickens, you'll select *No*, and\n| the *Amount (US lb.)* field is disabled.\n| \n| To follow the example, add a single selection field *Would you like to use the\n| provided dog food?* with two options: *Yes* and *No*.\n| \n| Add a numeric field called *Amount (US lb.)* and make it an Integer. Use field\n| validation to make sure it's not greater than *100*.\n| \n| To set up the enable/disable rule,\n| \n| 1. Open the Rules tab of the Edit Form page and click the Add\n|    (![Add](../../../images/icon-add.png)) button.\n| \n| 2. Define the rule:\n|     - If the *Would you like to use the provided dog food?* field is equal to\n|       *Yes*, enable the *Amount (US lb.)* field.\n| \n|     ![Figure 1: Build form rules quickly by defining your conditions and actions.](../../../images/forms-enable-rule.png)\n| \n|     - Save the rule.\n| \n|     ![Figure 2: Once a rule is saved, it is displayed so that you can easily understand what it does.](../../../images/forms-enable-rule2.png)\n\nNow users can fill out the amount of dog food they'll need only if they specify\nthat they do indeed want to use the provided food.\n"
  },
  {
    "path": "en/user/articles/130-forms/04-form-rules/05-jump-to-page-rules.markdown",
    "content": "---\nheader-id: action-jump-to-page\n---\n\n# Action: Jump to Page\n\n[TOC levels=1-4]\n\nUse a Jump to Page rule to navigate automatically to a specific page in the form\nbased on one or more conditions. This is useful if some pages don't apply to all\nthe form's users. Even fields marked as required on the skipped pages can be\nsuccessfully skipped using this rule.\n\nThis action doesn't appear in the rule builder unless the form has multiple\npages.\n\nTo follow the example here,\n\n1.  Create a second form page called *Team Information*.\n\n2.  On the _first_ form page, create a single selection field labeled *Are you a\n    returning racer, with the same team?* with two options: *Yes*\n    and *No*.\n\n3.  Create a text field on the _second_ form page called *Animal Name*.\n\n4.  Create a third form page called *Final Confirmation*.\n\n| **Example:** There's a question on the *Team Information* page of the dog sled\n| race registration form that asks *Are you a returning racer with the same team?*\n| If you select *Yes*, when you click the form's *Next* button, you skip to\n| the final page of the form, since there's no need to fill out your animal's name\n| again. Their monogrammed T-shirts will be ready at the start of the race.\n| \n| To configure the Jump to Page rule,\n| \n| 1. Open the Rules tab of the Edit Form page and click the Add\n|    (![Add](../../../images/icon-add.png)) button.\n| \n| 2. Define the rule:\n|     - If field *Are you a returning racer, with the same team?* is equal to\n|         the Value *Yes*, Jump to Page *Final Confirmation*.\n| \n|     ![Figure 1: Build form rules quickly by defining your conditions and actions.](../../../images/forms-jump-to-page.png)\n| \n|     - Save the rule.\n| \n|     ![Figure 2: Once a rule is saved, it is displayed so that you can easily understand what it does.](../../../images/forms-jump-to-page2.png)\n\nOnce the form User fills out the first page and clicks _Next_, the rule\ncondition will evaluate hte answer to the field and either proceed to the next\npage or take the action of skipping to the page inidicatedd in the rule.\n\nIf you use an *is not equal to* condition for form fields on two different\npages, the condition is checked after leaving the page of the first form field,\nand evaluates to *true* since there's a value in the first field and no value in\nthe second field. It's best to use this condition with fields existing on the\nsame page.\n"
  },
  {
    "path": "en/user/articles/130-forms/04-form-rules/06-autofill-rules.markdown",
    "content": "---\nheader-id: action-autofill\n---\n\n# Action: Autofill\n\n[TOC levels=1-4]\n\nAutofill rules let you change the selection options of another field based on\nthe value entered into a related field. A \n[data provider's](/docs/7-2/user/-/knowledge_base/u/data-providers) \noutput is used to populate a field, as long as the condition is met.\n\nBefore configuring an autofill rule, \n[set up a data provider](/docs/7-2/user/-/knowledge_base/u/data-providers). \nThat's how autofilled fields are populated. Pay careful attention to the\ninput and output parameters you choose when setting up the rule.\n\nTo follow this example: \n\n1.  Set up a data provider using the `get-countries` JSON web service. If you're\n    running @product@ at `localhost:8080`, you can access this web service here:\n        \n        http://localhost:8080/api/jsonws?contextName=&signature=%2Fcountry%2Fget-countries-0\n\n    Make sure the output parameter is set to `$..nameCurentValue`. If you're unsure\n    how to do this, first read the article on \n    [Data Providers](/docs/7-2/user/-/knowledge_base/u/data-providers).\n\n2.  On the last form page, add two fields:\n\n    - A Single Selection field called *If I win I'd like my award to be:*, with\n      two choices: *Cash* and  *All Expenses Paid Vacation*.\n\n    - A Select from List field called *Choose a Destination Country*. Under\n      *Create List*, choose *From Autofill*.\n\n| **Example:** Before submitting the race registration, let users decide whether\n| they want a cash prize or an all-expenses-paid vacation. If they choose the\n| vacation, populate the Choose a Destination Country with output from the data\n| provider.\n| \n| To configure an Autofill rule,\n| \n| 1. Open the Rules tab of the Edit Form page and click the Add\n|    (![Add](../../../images/icon-add.png)) button.\n| \n| 2. Define the rule:\n|     - If field *If I win I'd like my award to be* is equal to\n|         the Value *All Expenses Paid Vacation*, Autofill the *Choose a Destination\n|         Country* field from the *countries* data provider (note that you might\n|         have named this differently when setting it up).\n| \n|     ![Figure 1: Build form rules quickly by defining your conditions and actions.](../../../images/forms-autofill.png)\n| \n|     - Save the rule.\n| \n|     ![Figure 2: Once a rule is saved, it is displayed so that you can easily understand what it does.](../../../images/forms-autofill2.png)\n\n## Using Inputs with Autofill\n\nThe above example is simple, using only an Output to autofill a Select from List\nfield if another field has a certain value. Many times, the response from the\nREST provider must be filtered before display in the Select from List field. For\nthis, a Data Provider Input field is required. For example, to configure an\nautofill rule to display the countries of the world filtered by a Region field\n(for example, Americas, Europe, or Oceania),\n\n1.  Create a Data Provider.\n\n    **Name:** `restcountries`\n\n    **URL:** `https://restcountries.eu/rest/v2/region/{region}?fields=name`\n\n    **Inputs:** Fill in the Label (*region*), Parameter (*region*), and Type\n    (*Text*).\n\n    **Outputs:** fill out a  Label (*name*), Path (*$..name*), and Type\n    (*List*).\n\n    To understand more about these values, read the \n    [Data Provider documentation](/docs/7-2/user/-/knowledge_base/u/data-providers).\n\n    ![Figure 3: Create a data provider for the autofill rule.](../../../images/forms-autofill-data-provider.png)\n\n2.  Create a form with these fields:\n\n    **Text:** Use the Label *Region*.\n\n    **Select from List:** Label it *Country*, and choose *From Autofill* under\n    Create List.\n\n    ![Figure 4: Create a form with a text field and a select from list field. These are used to provide the input to the data provider and be autofilled by its output.](../../../images/forms-autofill-input-output-fields.png)\n\n3.  Configure the Autofill rule.\n\n    **Condition:** If *Region* **Is not Empty**\n\n    **Action:** Do **Autofill** From Data Provider `restcountries`, Data\n    Provider's Input: region---*Region*, Data Provider's Output:\n    name---*Country*.\n\n    ![Figure 5: Create the autofill rule. Brag of your prowess.](../../../images/forms-autofill-rule.png)\n\nOnce you're done, publish the form and try it out, by entering a valid region\ninto the Region field, and observing that the options in the Select from List\nField are filtered based on the Region. The\n[restcountries.eu](https://restcountries.eu) service has\nthese regions you can use: Africa, Americas, Asia, Europe, Oceania, and Polar.\n\n![Figure 6: Filter countries by region of the world.](../../../images/forms-autofill-region.gif)\n\nAutofill rules combine the power of data providers and form rules.\n"
  },
  {
    "path": "en/user/articles/130-forms/04-form-rules/07-calculate-rules.markdown",
    "content": "---\nheader-id: action-calculate\n---\n\n# Action: Calculate\n\n[TOC levels=1-4]\n\nCalculate rules let you automatically populate a numeric field by calculating\nits value based on other fields. Calculations are limited to numeric fields.\n\nTo follow the example below:\n\n1.  Create 5 Numeric fields called *Animal Weight 1 (US lb.)*, *Animal Weight 2\n    (US lb.)*, etc.\n\n2.  Create a Numeric field called *Total Food Required (US lb.)*.\n\n| **Example:** A 16-dog sled team can consume 2,000 US lb. of food during the\n| Iditarod. This equates to about 0.25 lb. of food per lb. of animal, if the race\n| lasts ten days. We'll use five numeric fields for animal weight instead of\n| sixteen here, because it's tedious to create sixteen fields, even with the field\n| duplication function. When the form user enters the weight of each animal the\n| Total Food Required field should be calculated based on this simple formula:\n| \n|     Animal Weight 1, Animal Weight 2, ... = AW1, AW2, ...\n|     Total Food Required = TFW\n| \n|     (AW1 + AW2 + AW3 + AW4 + AW5) * 0.25 = TFW\n| \n| To configure a calculate rule:\n| \n| 1. Open the Rules tab of the Edit Form page and click the Add\n|    (![Add](../../../images/icon-add.png)) button.\n| \n| 2. Define the rule:\n|     - If field *Animal Weight 1* is greater than 0, Calculate the sum of the\n|         Animal Weight fields, multiplied by 0.25.\n| \n|     ![Figure 1: Build calculate actions with a handy calculator.](../../../images/forms-calculate-rule.png)\n| \n|     - Save the rule.\n| \n|     ![Figure 2: Once a rule is saved, it is displayed so that you can easily understand what it does.](../../../images/forms-calculate-rule2.png)\n\nThe calculation is defined using the embedded calculator. Use a mix of numeric\nfield values, mathematical operators, and constants to define calculation\nrules.\n"
  },
  {
    "path": "en/user/articles/130-forms/05-element-sets.markdown",
    "content": "---\nheader-id: form-element-sets\n---\n\n# Form Element Sets\n\n[TOC levels=1-4]\n\nIf you're here looking for information on reusable field sets, you're in the\nright place. We call them Element Sets in the Liferay Forms application because\nthese sets include more than just fields: they include the layout and\nconfiguration of the fields as well. In the future, additional styling elements\nwill be available here, too. \n\nElement sets are more like composable Form fragments or reusable Form blocks. \n\nSometimes you might be able to create an entire form by composing existing\nElement Sets. Your colleagues might call you lazy, but we'd call you\nindustrious.\n\n## Creating Element Sets\n\nTo create Element Sets, go to Site Menu &rarr; Content &Data &rarr; Forms. The\nForms view is displayed by default. Click the *Element Sets* tab, and any\nexisting Element Sets appear, just like existing Forms are displayed in the\nForms view. Click the *Add* button (![Add](../../images/icon-add.png)).\n\nHere's the thing. If you know how to \n[create a Form](/docs/7-2/user/-/knowledge_base/u/creating-and-managing-forms), \nyou already know how to create an Element Set. The process is identical. Drag\nand drop elements onto the form builder palette, configuring fields as you go.\n\nWhen you're finished, click *Save*. Element Sets aren't publishable, so there's\nno button for that.\n\n![Figure 1: Creating Element Sets is just like creating Forms. You just can't publish them.](../../images/forms-element-sets.png)\n\nOnce an Element Set is saved, it's instantly available for use, even in the same\nElement Set. That's right, you can use Element Sets to create Element Sets.\n\n## Using Element Sets\n\nTo use an Element Set in a Form:\n\n1.  Open the Form Builder.\n\n2.  If the Add Elements sidebar isn't already displayed, open it by clicking the\n    *Add* button (![Add](../../images/icon-add.png)).\n\n3.  The default view in the Add Elements sidebar is Elements. Instead click\n    *Element Sets*.\n\n4.  Drag the Element Set onto the Form Builder, just like you would any single\n    Form Element.\n\n![Figure 2: Add an Element Set the same way you add other Form Elements, like fields.](../../images/forms-add-element-set.png)\n\nYou probably already guessed that the process is identical for using Element\nSets to build other Element Sets. That's all there is to it. There are just a\ncouple more things to note:\n\n- Once an Element Set is added to a Form, there's no connection with the root\n  Element Set. You're free to move or configure the Fields and Elements however\n  you want.\n\n- Editing an Element Set doesn't retroactively affect the Forms where the\n  Element Set was used. \n\nThink ahead. Are there some common fields you'll commonly need to configure in\nyour Forms? If so, create them as Element Sets once and save yourself\nrepetitive work.\n"
  },
  {
    "path": "en/user/articles/130-forms/06-data-providers.markdown",
    "content": "---\nheader-id: data-providers\n---\n\n# Data Providers\n\n[TOC levels=1-4]\n\nSelect from List fields can hold a lot of options. There are around 200\ncountries on Earth, for example. If you have unoccupied unpaid interns you could\nask them to type each country into the Select from List field manually, or you\ncould auto-populate your select fields using a REST web service. This saves you\n(or your interns) the trouble of typing all those options, and you can rely on\nsomeone else (hopefully a trustworthy expert) to keep the data updated.\n\nWhen setting up a data provider, you're accessing a \n[REST web service](https://en.wikipedia.org/wiki/Representational_state_transfer). \nUse the JSON web services registered in @product@, or any other REST web service\nyou can access. To find a list of the registered JSON web services in @product@,\nnavigate to\n[http://localhost:8080/api/jsonws](http://localhost:8080/api/jsonws)\n(assuming you're running a local server). Browse the available Liferay services.\nMany times, the services useful to you in the Forms application get a list of\nsomething. Find the `get-countries` JSON web service (there are two---use either\none) and click on it, then click *Invoke*. The *Result* tab shows a list of\ncountries using JSON syntax, like this:\n\n    [\n      {\n        \"a2\": \"AF\",\n        \"a3\": \"AFG\",\n        \"countryId\": \"20\",\n        \"idd\": \"093\",\n        \"mvccVersion\": \"0\",\n        \"name\": \"afghanistan\",\n        \"nameCurrentValue\": \"Afghanistan\",\n        \"number\": \"4\"\n      },\n        ...\n\nThat's the record for the country Afghanistan. As you can see in the *URL\nExample* tab, the URL you entered into the data provider form is the same as the\none generated for accessing the `get-countries` JSON web service. Find the URL\nfor any registered JSON web service using this same procedure. \n\nNote the field you want Users to select. With this service, it's most likely\n`nameCurrentValue`, because it contains the full, properly capitalized name of\nthe country.\n\n| *Enabling Access to Data on the Local Network:* By default, you cannot configure\n| data providers to use URLs on the local network. This is a good default for\n| security in a production environment, but makes testing more difficult. To\n| enable local network access from data providers, got to Control Panel &rarr;\n| Configuration &rarr; System Settings &rarr; Data Providers (under Content &\n| Data), and enable _Access Local Network_. You'll need to configure this if you\n| want to follow the basic example in the next section.\n\n## Adding a Basic Data Provider\n\nTo add a *Countries of the World* Data Provider for use in your Forms,\n\n1.  Go to the Forms application.\n\n2.  Click the *Data Providers* tab.\n\n3.  Click the Add button (![Add](../../images/icon-add.png)).\n\n    The REST Data Provider form loads.\n\n4.  Fill out the Name and Description fields.\n\n    Name: `Countries of the World`\n\n5.  Enter the URL and authentication tokens for the REST service. For the\n    `get-countries` service:\n\n    URL: `http://localhost:8080/api/jsonws/country/get-countries/`\n\n    User Name: `adminuser@liferay.com`\n\n    Password:`adminuserpass`\n\n6.  In the Outputs fields, specify which field from the REST service populates\n    your select list. \n\n    Label: `Country Name`\n\n    Path: `$..nameCurrentValue` \n\n    Type: `List`\n\n7.  Save the Data Provider.\n\n![Figure 1: Set up a simple data provider in no time.](../../images/forms-simple-data-provider.png)\n\nWhat's that `$..` before `nameCurrentValue`? It's JsonPath syntax to navigate\nthe JSON data structure and specify the path to the output. Learn more about\nJsonPath\n[here](https://github.com/json-path/JsonPath/blob/master/README.md) and\n[here](http://goessner.net/articles/JsonPath/).\n\n## Using a Data Provider in a Select Field\n\nOnce the Data Provider is configured, use it to populate a Select from List field:\n\n1.  Go to the Form Builder (add a new form or edit an existing one)\n\n2.  Drag a Select from List field onto the form.\n\n3.  In the Create List section, choose *From Data Provider*.\n\n4.  Choose the Data Provider and its Output Parameter:\n\n    Choose a Data Provider: `Countries of the World`\n\n    Choose an Output Parameter: `Country Name`\n\n5.  Publish the form and test it. \n\n![Figure 2: Form users select an option form the list populated by the Data Provider.](../../images/forms-select-data-provider.png)\n\nYour Data Provider is now being used to populate a select field. However, this\nform should be submitted by Guest users, who don't currently have permission to\nsee the list of results from the data provider. Arrgh! Now what? \n\n## Granting Data Provider Permissions\n\nTo configure the data provider's permissions, go to the Forms application (*Site\nAdministration* &rarr; *Content & Data* &rarr; *Forms*). Open the Data Providers\ntab. For the data provider you want to configure, click the Actions button\n(![Actions](../../images/icon-actions.png)), then *Permissions*. \n\nConfigure the permissions you need. If Guests are to fill out the form, they\nneed the *View* permission, or else they won't be able to see the options\nprovided by the data provider. Once you grant permissions, click *Save*.\n\n## Data Provider Configuration\n\nThe above instructions cover adding a basic Data Provider. Knowing more about\neach field in the Data Provider setup form opens up more possibilities.\n\n**URL**\n: The URL of an internal or external REST service endpoint. Consider the REST\nservice at https://restcountries.eu/, which contains a REST API endpoint to find\ncountries by `region`:\n\n    `https://restcountries.eu/rest/v2/region/{region}`\n\nData Provider URLs can take two parameter types: path parameters and\nquery parameters. \n\nPath parameters are part of the URL calling the REST web service, and are added\nusing the pattern `https://service-url.com/service/{path_parameter_name}`: \n\nThe `restcountries.eu` service's `region` endpoint's path parameter is\n`{region}`. Path parameters are mandatory parts of the URL, so make sure you\nspecify an Input (see below) with a _Parameter_ field value matching the path\nparameter from the URL. \n\nQuery parameters are complementary parts of the URL that filter the output of\nthe service call, following the pattern\n`?query_parameter=query_parameter_value`:\n\n    https://restcountries.eu/rest/v2/all?fields=capital\n\nUnlike path parameters, query parameters are optional.\n\n**User Name and Password** \n: Credentials used to authenticate to the REST Web Service, if necessary.\n\n**Cache data on the first request.**\n: If the data is cached, a second load of the select list field is much faster,\nsince a second call to the REST service provider is unnecessary.\n\n**Timeout**\n: The time (in ms) to allow the REST service call to process before aborting the\nrequest, if a response is not returned.\n\n**Inputs**\n: Configure path or query parameters from the REST service to filter the REST\nservice's response. Specify the Label, Parameter, and Type (Text or Number), and\nchoose whether the input is required to use the Data Provider. You can add\nmultiple Inputs. To provide a way for users to specify the input value, use an\n[_Autofill_ Form Rule](/docs/7-2/user/-/knowledge_base/u/action-autofill).\nA User enters input into one field, and their input is sent to the REST service.\nThe REST service's response data is filtered by the input parameter.\n\n**Outputs**\n: The Parameter to display in Select from List or Text fields with autocomplete\nenabled. You can add multiple Outputs. Outputs can be filtered by inputs (see\nabove) but can also be displayed without configuring input filtering. Specify\nthe Label, Path, and Type (Text, Number, or List). The Path field is specified\nin\n[JsonPath syntax](https://github.com/json-path/JsonPath/blob/master/README.md), so it\nmust always start with a `$`. The type of data returned by the Path must match\nthe type you choose in the Type field. Using the `restcountries.eu` service,\nspecify the `name` field as an Output by entering enter `$..name` in the Path\nfield.\n\nIf you have a more complex JsonPath expression to construct (for example, you\nneed the names of all countries with a population over 100\nmillion---`$..[?(@.population>100000000)].name` with the `restcountries.eu`\nservice), consider using an online JsonPath evaluator, like\n[this one](http://jsonpath.herokuapp.com/) or\n[this one](https://jsonpath.com/).\n\n| **Hint:** To display one value to the user, but persist another in the database,\n| enter both into the Paths field, separated by a semicolon:\n| \n|      `$..name;$..numericCode`\n| \n| If this is used with the `restcountries.eu` data provider, the name of the\n| country is displayed to the User, while the numeric country code is stored in\n| the database.\n\n![Figure 3: Set up Data Providers to display data retrieved from a REST service.](../../images/forms-data-provider-configuration.png)\n\n## Troubleshooting Data Provider Errors\n\nTo uncover errors arising from Data Provider failures, \n[configure log levels](/docs/7-2/user/-/knowledge_base/u/server-administration) \nfor these services:\n\n**Category:**\n`com.liferay.dynamic.data.mapping.data.provider.internal.DDMDataProviderInvokerImpl`\n*Level:* WARN \n\n**Category:**\n`com.liferay.dynamic.data.mapping.form.field.type.internal.DDMFormFieldOptionsFactoryImpl`\n*Level:* DEBUG\n\nWith Data Providers, the world's (RESTful) data is at your disposal to use with\nthe Forms application.\n"
  },
  {
    "path": "en/user/articles/130-forms/07-autosave.markdown",
    "content": "---\nheader-id: auto-save\n---\n\n# Auto-Save\n\n[TOC levels=1-4]\n\nLosing progress on a partially created form is bad. Make sure to save your work\nfrequently as you're creating forms. But if you forget to save your work, \nLiferay Forms has your back.\n\nBy default, a form is auto-saved every minute. You won't notice anything in the\nform builder while the back-end auto-saves the form. You can change the\nauto-save duration in *Control Panel* &rarr; *Configuration* &rarr; *System\nSettings* &rarr; *Forms* (in the Content and Data section). To disable\nauto-save, set the interval to *0*. \n\nFor unpublished forms, an auto-save works just like a manual save. For a \npublished form, however, auto-saved data isn't automatically propagated to the \nform. You must click the *Save* button in the form builder to publish the \nchanges. \n\n![Figure 1: Configure the auto-save duration.](../../images/forms-autosave-interval.png)\n"
  },
  {
    "path": "en/user/articles/130-forms/08-translating-forms.markdown",
    "content": "---\nheader-id: translating-forms\n---\n\n# Translating Forms\n\n[TOC levels=1-4]\n\nForms can be translated to any locale that @product@ supports. The form builder \nspecifies a translation of the form's default language. \n\nThe form's default language and the available translations are set in the\n[site's language configuration](/docs/7-2/user/-/knowledge_base/u/social-settings-and-languages#languages).\n\nFollow these steps to create a form translation: \n\n1.  Go to *Site Administration* (your site's menu) &rarr; *Content & Data* &rarr; \n    *Forms* and open the form to translate. \n\n2.  Click the + icon next to the current translation language and choose from\n    the available languages. \n\n    ![Figure 1: Add a translation for the form.](../../images/forms-add-translation.png)\n \n3.  Translate the form's title, field labels, field options, field placeholder\n    text, and any other text visible to the user.\n\n4.  Save and publish the form. \n\n![Figure 2: Translate as much of the form as possible into each language you expect users to need.](../../images/forms-translate2.png)\n\nTo fill out a translated form in a translated language, \n\n1.  Access the form. If a signed-in user accesses the form and a translation is \n    available in the user's language, the user sees the translated form by \n    default.\n\n2.  To see the form in a different language, click the language icon and select\n    a language. \n\n3.  Fill out the form as usual and click *Submit*. \n\n| **Note:** Translations work differently depending on how a User accesses a Form:\n| \n| If [accessed in the Form widget on a @product@\n| page](/docs/7-2/user/-/knowledge_base/u/creating-and-managing-forms#accessing-forms),\n| the Form is displayed in the User's language automatically. If there's no\n| translation available for the User's language, the default language of the Form\n| is displayed.  \n|\n| If accessed via direct\n| [URL](/docs/7-2/user/-/knowledge_base/u/creating-and-managing-forms#accessing-forms),\n| the Form translation must be selected manually.\n\n![Figure 3: Select the form's language.](../../images/forms-translate3.png)\n"
  },
  {
    "path": "en/user/articles/130-forms/09-autocomplete.markdown",
    "content": "---\nheader-id: autocompleting-text-fields\n---\n\n# Autocompleting Text Fields\n\n[TOC levels=1-4]\n\nIt's been scientifically proven that Internet users are lazy (not you, of\ncourse---other Internet users). For example, some users may not fill out your\nform if you make them type the entire title of their country in an employment\napplication. This is especially true if they're filling out the form on their\nmobile devices. Make users' lives easier by configuring autocomplete on a\nform's text fields. \n\nWhy not just use a select field with a data provider to guide user input?\nSometimes a data provider can't encompass all possible field entries. For\nexample, if your data provider doesn't include [mythical countries founded on\nold sea platforms](https://en.wikipedia.org/wiki/Principality_of_Sealand),\nusers the Principality of Sealand can't enter anything into the select field.\nInstead use a text field with autocomplete so users can begin typing their\ncountry's name and then select it from a list when it appears. Autocomplete\ncombines a text field (accepting any response that meets your validation\ncriteria) and common choices to select from. It's a win-win situation. \n\n## Configuring Autocomplete\n\nBefore configuring autocomplete for your text fields, \n[create a form and add a text field](/docs/7-2/user/-/knowledge_base/u/creating-and-managing-forms). \nIf you want the autocomplete options to be populated by a REST data provider,\n[configure one](/docs/7-2/user/-/knowledge_base/u/data-providers) \nbefore creating your form. Now you're ready to configure autocomplete for the\nfield: \n\n1.  In the field configuration sidebar, click the *Properties* tab.\n\n2.  Click the *Autocomplete* switcher so it's enabled.\n\n3.  Select a data provider or create one manually. You can set up a data \n    provider from a \n    [REST service](/docs/7-2/user/-/knowledge_base/u/data-providers), \n    or manually enter the options users should see when they start typing in the \n    text field. \n\n4.  Save and Publish the form. \n\n![Figure 1: You can configure a manual data provider to specify the options users can select from.](../../images/forms-autocomplete-manually.png)\n\nOnce users begin entering text into the field, a selection list of options \nappears. As they enter additional text, the list is refined to include only \noptions that contain the currently entered text. For example, the imaginary \nusers from Sealand (all two of them) begin reluctantly typing their country of\norigin by entering an *S*. They're delighted to see a selection list with a\nbunch of countries containing the letter *S* appear for their selection\nconvenience. If they continue typing and enter *e*, the list is refined to \noptions that have *se* in their name (for example Serbia and Senegal). If they \ncontinue typing and enter *a*, they'll now only see one option, Sealand, if it's \nin your data provider. Selecting it from the list after typing the first three \nletters is much easier than typing the remaining letters. \n\n![Figure 2: When typing in a field with autocomplete, users are presented a list of selections from the configured data provider. The displayed results are filtered to include only selections containing the text entered by the user.](../../images/forms-autocomplete-filtering.png)\n\nWhat will the Forms team think of next? Configuring telepathic connections to \nthe Forms application would be nice. Then users could just think their form\nfield entries into existence. Stay tuned. \n"
  },
  {
    "path": "en/user/articles/130-forms/10-success-pages.markdown",
    "content": "---\nheader-id: form-success-pages\n---\n\n# Form Success Pages\n\n[TOC levels=1-4]\n\nAfter users submit one of your whiz-bang forms, what's next? How will they know\nthey're done and can close the browser window or tab? What if they think their\nsubmission didn't go through and wonder if they need to fill out the whole form\nagain? By default, submitting a form displays the default success message and \nreturns users to the form's now empty first page. Don't leave users feeling \nequally empty. Instead, configure a *Success Page*. A Success Page is a terminal \npage showing users they've finished filling out the form and their submission\nwas successfully received. A Success Page can even urge users to close the\nbrowser window or tab. \n\n![Figure 1: The default success message alerts users when their request completes successfully.](../../images/your-request-completed-successfully.png)\n\nA Success Page is simple. It has a title in bold text and a description beneath \nthe title. A common alternative to using a Success Page is to \n[redirect users to a different page in your Site](/docs/7-2/user/-/knowledge_base/u/redirecting-users). \nWhat should you put in a Success Page? Whatever you want. If you can't think\nof anything important or creative to say, use the default message: \n\n![Figure 2: There's a default Success Page message if you can't think of anything else to say.](../../images/forms-success-page-default.png)\n\nTo configure a Success Page, \n\n1.  Add a form in *Site Administration* (your site's menu) &rarr; *Content &\n    Data* &rarr; *Forms*. \n\n2.  Click on the form page's *Actions* button \n    (![Actions](../../images/icon-actions.png)) \n    and choose *Add Success Page*. \n\n![Figure 3: Add a Success Page using the edit menu for the form page.](../../images/forms-success-page-add.png)\n\nOnce the Success Page is added to your form, fill in the *Title* and \n*Content* fields however you please. When the form is saved and published, \nthe Success Page is live for your form users. \n\n| **Note:** You can't preview the Success Page. Success Pages can only be viewed\n| once a form is submitted, and the *Submit* button isn't available in the form\n| preview. The *Preview Form* link in the form builder only lets you preview the\n| form's regular pages (use the *Next* button to navigate through the form).\n| \n| To see what your Success Page looks like, submit a test entry of the form and\n| then delete it if needed. For more information, see\n| [the documentation on viewing and managing form entries](/docs/7-2/user/-/knowledge_base/u/managing-form-entries#viewing-form-entries).\n"
  },
  {
    "path": "en/user/articles/130-forms/10-workflow-and-forms.markdown",
    "content": "---\nheader-id: sending-form-entries-through-a-workflow\n---\n\n# Workflow and Forms\n\n[TOC levels=1-4]\n\n[The workflow engine](/docs/7-2/user/-/knowledge_base/u/workflow) is for\nsending a submitted asset through a workflow process before it's published.\nMost assets are configured to use workflow at the instance or Site level.\n\n![Figure 1: Workflow is enabled in the Control Panel or in Site Administration for most @product@ assets.](../../images/forms-workflow-configuration.png)\n\nForms are different, so they don't appear in the above image.  There are so\nmany use cases for forms, and there could be so many per site, that a site- or\ninstance-scoped workflow configuration won't serve your needs well. Instead,\nconfigure workflow for *each form* separately.\n\n## Enabling Workflow in a Form\n\nTo enable workflow in a form, \n\n1.  Open the form's editor by opening the Menu, selecting your Site, navigating \n    to *Content &Data* &rarr; *Forms*, and clicking on the form you want.\n\n2.  Click the Options button (![Options](../../images/icon-options.png)) and\n    choose *Settings*.\n\n3.  The Settings window has a *Select a Workflow* drop-down. Find the workflow\n    you want, select it, and then click *Done*.\n\n    ![Figure 2: Enable workflow for each form in its Settings window.](../../images/form-settings.png)\n\n## Testing the Workflow\n\nTest the workflow process:\n\n1.  Add the form to a page.\n\n2.  Click *Submit for Publication* to submit the form entry.\n\nNext go find the form entry in the Forms application:\n\n1.  Go back to the Forms application in the Menu in your Site's *Content &\n    Data* section.\n\n2.  Click the Form's *Actions* button \n    (![Actions](../../images/icon-actions.png)) and select *View Entries*.\n\n    The entry is currently marked *Pending*.\n \nNow [approve the form\nrecord](/docs/7-2/user/-/knowledge_base/u/reviewing-assets):\n\n1.  Navigate to *My Workflow Tasks* in the User Personal Menu.\n\n2.  Click the *Assigned to My Roles* tab.\n\n3.  Click on the form entry.\n\n4.  Click the Actions button (![Actions](../../images/icon-actions.png)) and\n    choose *Assign to Me*.\n\n5.  Click *Done*.\n\n6.  Click the Actions button (![Actions](../../images/icon-actions.png)) again, \n    then click *Approve*.\n\n7.  Click *Done* again.\n\n8.  Navigate back to the View Entries screen for the form, and now the entry is\n    marked as *Approved*. \n\n![Figure 3: Each entry's status is visible in the Forms application's Form Entries screen.](../../images/forms-view-entries-status.png)\n\n"
  },
  {
    "path": "en/user/articles/130-forms/11-duplicating-forms-and-fields.markdown",
    "content": "---\nheader-id: duplicating-forms-and-form-fields\n---\n\n# Duplicating Forms and Form Fields\n\n[TOC levels=1-4]\n\nRepetitive tasks are error prone. Instead of duplicating effort, duplicate\nforms and form fields. \n\nTo duplicate a form, \n\n1.  Go to *Site Administration* (your Site's menu) &rarr; *Content & Data*\n    &rarr; *Forms*. \n\n2.  Click the *Actions* button \n    (![Actions](../../images/icon-actions.png)) \n    for the form to duplicate. \n\n3.  Click *Duplicate*.\n\n![Figure 1: The Duplicate option works the same for forms and form fields.](../../images/forms-duplicate-form.png)\n\nThe form is duplicated and automatically named *Copy of [Original Form Name]*. \nOnce duplicated, you can edit the form however you want. When you duplicate a \nform, all configurations and form rules are duplicated as well. \n\nTo duplicate a form field, \n\n1.  Go to *Site Administration* (your Site's menu) &rarr; *Content & Data*\n    &rarr; *Forms* and open or add a form. \n\n2.  In the Builder view, hover over the form field to duplicate and click the\n    *Copy* icon \n    (![Copy](../../images/icon-copy.png)). \n\n![Figure 2: You can duplicate form fields.](../../images/forms-duplicate-form-field.png)\n\nThe field is duplicated and labeled *Copy of [Original Field Label]*. All the \nform's properties, including its data provider configurations, are copied as\nwell.\n"
  },
  {
    "path": "en/user/articles/130-forms/12-form-pages.markdown",
    "content": "---\nheader-id: form-pages\n---\n\n# Form Pages\n\n[TOC levels=1-4]\n\nAre users more likely to abandon long forms with lots of scrolling? Are they \nmore likely to see a multi-page form and abandon it without a second look, \nassuming that it gets longer and more tedious with every passing page? Such \nusability questions are worth thinking about. If you decide multiple pages are \nappropriate for your form, Liferay Forms supports two pagination styles: \ndefault, and alternate. \n\n![Figure 1: The default pagination style.](../../images/forms-pagination1.png)\n\n![Figure 2: The alternate pagination style as seen in the Form Builder.](../../images/forms-pagination2.png)\n\nTo add a form page, \n\n1.  Go to a form's builder view. \n\n2.  Click the *Actions* button (![Actions](../../images/icon-actions.png)) at\n    the top-right corner of the form, then click *Add New Page*. \n\n![Figure 3: You can add new pages or reset the current page from the Page Actions menu.](../../images/forms-page-actions.png)\n\nYou can also delete form pages, add \n[success pages](/docs/7-2/user/-/knowledge_base/u/form-success-pages), \nand switch pagination modes. \n"
  },
  {
    "path": "en/user/articles/130-forms/13-placeholders-predefined-values.markdown",
    "content": "---\nheader-id: help-text-placeholder-text-and-predefined-values\n---\n\n# Help Text, Placeholder Text, and Predefined Values\n\n[TOC levels=1-4]\n\nForm fields can have help text, placeholder text, and predefined values.\n\n-   **Help Text:** Text that appears as a sub-heading to the field label, but \n    doesn't appear in the field entry area. Enter help text in the Basic tab of \n    the field's sidebar menu.\n\n-   **Placeholder Text:** Text in the field entry area that isn't submitted if \n    the field is left untouched by the user. \n\n-   **Predefined Value:** Text in the field entry area that is submitted if the \n    field is left untouched by the user. \n\nAll form field types can have help text, and all form field types that accept \nuser input can have predefined values. Only text and numeric fields can have \nplaceholder text. \n\nTo enter placeholder text or predefined values, \n\n1.  Open a field's sidebar menu.\n\n2.  Open the *Properties* tab. \n\n![Figure 1: Predefined values and placeholder text are entered in the Properties tab.](../../images/forms-placeholder-predefined-values.png)\n\nFor example, many forms start with a *Full Name* field. You can use help text\nand/or placeholder text to inform users that you need their full names,\nregardless of length. Alternatively, if you're asking users to specify how many\nsandwiches they eat for lunch, a predefined value of *1* probably makes sense. \n\n![Figure 2: The Full Name field here uses help text and placeholder text, while the sandwiches field uses a predefined value.](../../images/forms-help-placeholder-predefined.png)\n\nRemember, placeholder values aren't submitted if the field is left blank, so you \ndon't have to worry about getting a bunch of submissions from \n*Maximillian Aurelius Piroux the 11th*. \n"
  },
  {
    "path": "en/user/articles/130-forms/14-validation.markdown",
    "content": "---\nheader-id: validating-text-and-numeric-fields\n---\n\n# Validating Text and Numeric Fields\n\n[TOC levels=1-4]\n\nValidation ensures that only certain values are entered in a field. Validation\nfunctionality is available for text and numeric fields. \n\nTo enable validation, \n\n1.  Add a Text or Numeric field to a form in the Builder view. \n\n2.  Open the field's *Properties* tab. \n\n3.  Turn on the *Validation* toggle to enable validation and open its \n    configuration options. \n\n![Figure 1: Validate data to ensure you're collecting only useful information.](../../images/forms-text-validation.png)\n\n## Validating Text Fields\n\nValidation for text fields contains several options. You must first choose a \nlist of available conditions to check: \n\n-   If Input *Contains*\n-   If Input *Does Not Contain*\n-   If Input *Is not URL*\n-   If Input *Is not Email*\n-   If Input *Does not Match*\n\nIf the condition isn't met, an error message is displayed to the user. Where you \ngo from there depends on which condition you used. \n\n### If Input Contains/Does Not Contain\n\nWhen you validate text data to check if it contains a certain value, there are \ntwo additional steps to take after selecting the condition: \n\n1.  Enter the text to check for. \n\n2.  Enter an error message so users understand why their submission failed. \n\n![Figure 2: If *Liferay* isn't part of the field's value, an error message is displayed.](../../images/forms-text-val-contains.png)\n\n### If Input Is not URL/Email\n\nChecking for properly formatted URLs and emails is easy. Just choose the \ncondition from the *If Input* drop-down and enter the error message. \n\nValid URLs begin with `http://` or `https://`. Valid emails must contain `@`.\n\n![Figure 3: Use text field validation to make sure users enter a valid email address or URL.](../../images/forms-text-val-email.png)\n\n### If Input Does Not Match\n\nThe *Does Not Match* condition is used for entering \n[regular expressions](https://en.wikipedia.org/wiki/Regular_expression) \nto create custom validation criteria. For example, use this regular expression \nto ensure that ten consecutive numeric digits are entered in a phone number \nfield:\n\n    ^[0-9]{10}$\n\nIf you use regular expression validation, provide some explanatory text (e.g., \nhelp text, placeholder text, and a clear error message) to guide form users in \nentering the proper data.\n\n![Figure 4: Regular expression text validation opens up countless possibilities.](../../images/forms-text-val-regex.png)\n\n## Validating Numeric Fields\n\nNumeric field validation is similar to text field validation, but the conditions \ncompare the value of the number entered to some other value. \n\n![Figure 5: Numeric conditions constrain user-entered numeric data.](../../images/forms-numeric-val2.png)\n\nAvailable conditions to check include\n\n-   Is greater than or equal to\n-   Is greater than\n-   Is not equal to\n-   Is less than or equal to\n-   Is less than\n\nFor example, to make sure users don't enter a number over 10, enable validation\nand use *Is greater than* with a value of *10*. Use the message \n*Please enter 10 or less*. \n\n![Figure 6: Make sure user-entered numeric data is within reasonable bounds. Nobody needs 11 sandwiches for lunch.](../../images/forms-numeric-val1.png)\n\nNote that numeric fields are text fields validated to allow only numeric data \nentry. That's why they're in the Customized Elements section of the form fields \nlist. In addition, the property *My numeric type is* (can be Integer or Decimal) \non the Basic tab of a numeric form is another form of validation. \n"
  },
  {
    "path": "en/user/articles/130-forms/15-captcha.markdown",
    "content": "---\nheader-id: enabling-captcha-on-form-submissions\n---\n\n# Enabling CAPTCHA on Form Submissions\n\n[TOC levels=1-4]\n\n[CAPTCHA](https://en.wikipedia.org/wiki/CAPTCHA) \nprevents a bot from submitting forms. It's often used in \n[login apps](/docs/7-2/deploy/-/knowledge_base/d/logging-into-liferay), \nbut you can also use it in the Forms app. \n\nTo enable CAPTCHA, click the form's *Options* button \n(![Options](../../images/icon-options.png)) \nand select *Settings*. Enable the *Require CAPTCHA* setting, click *Done*, and \nsave the form. That's all there is to it! \n\n![Figure 1: You can enable CAPTCHA for your form in the Form Settings window.](../../images/forms-settings-captcha.png)\n\n![Figure 2: Once you enable CAPTCHA, your form has protection against bot submissions.](../../images/forms-captcha.png)\n"
  },
  {
    "path": "en/user/articles/130-forms/16-form-notifications.markdown",
    "content": "---\nheader-id: form-notifications\n---\n\n# Form Notifications\n\n[TOC levels=1-4]\n\nYou can configure the Forms app to send a notification email each time a form \nentry is submitted.\n\n1.  Open the form's *Form Settings* section by clicking the *Options* button \n    (![Options](../../images/icon-options.png)) \n    and selecting *Settings*. \n\n2.  Click the *Email Notifications* tab, enable the option \n    *Send an Email Notification for Each Entry*, and fill out these fields: \n\n    **From Name:** The sender's name. This could be the Site name, the form \n    name, or anything else informative to the recipient.\n\n    **From Address:** The sender's email address. You can use something like \n    `noreply@example.com`, so that recipients don't try to reply. \n\n    **To Address:** The recipient's email address (e.g., `test@example.com`).\n\n    **Subject:** The email's subject. An informative subject line tells the\n    recipient what happened. For example, An application for employment was\n    submitted in The Lunar Resort*. \n\nNote that if you [enabled workflow for the\nform](/docs/7-2/user/-/knowledge_base/u/sending-form-entries-through-a-workflow)\nand it already sends a notification, you might not need to configure the Forms\napp to generate a notification. \n\n![Figure 1: Configure email notifications each time a form entry is submitted.](../../images/forms-notification-email.png)\n"
  },
  {
    "path": "en/user/articles/130-forms/17-redirecting-users.markdown",
    "content": "---\nheader-id: redirecting-users\n---\n\n# Redirecting Users\n\n[TOC levels=1-4]\n\nWhen users submit a form, you can present them with another page indicating \nsuccess or some other information related to their submission. Sometimes all you \nneed is a \n[success page](/docs/7-2/user/-/knowledge_base/u/form-success-pages), \nbut other times you might want to send users to a specific URL. \n\nWhatever your use case is, follow these steps to set up a redirect URL: \n\n1.  Open the form's *Form Settings* section by clicking the *Options* button \n    (![Options](../../images/icon-options.png)) \n    and select *Settings*. \n\n2.  Enter the redirect URL in the *Redirect URL* field. \n\nThat's it! Now when users submit the form, they're not left wondering what to do \nnext. \n\n![Figure 1: Redirect users after they submit a form.](../../images/forms-redirect.png)\n"
  },
  {
    "path": "en/user/articles/130-forms/18-form-permissions.markdown",
    "content": "---\nheader-id: form-permissions\n---\n\n# Form Permissions\n\n[TOC levels=1-4]\n\nTo access a form's permissions, first navigate to the Forms app in *Site\nAdministration* (your site's menu) &rarr; *Content & Data* &rarr; *Forms*. Then\nclick the form's *Actions* button (![Actions](../../images/icon-actions.png)),\nand select *Permissions*. \n\nBy default, you can grant these permissions for a form: \n\n**Delete:** Delete the form. \n\n**Permissions:** Access and configure the form's permissions. \n\n**Update:** Update form entries. \n\n**Add Form Instance Record:** Submit form entries. \n\n**View:** View the form. \n\n![Figure 1: You can configure a form's permissions.](../../images/forms-form-permissions.png)\n\nNote that guest users can view and fill out forms by default. The *Guest* Role \nhas *View* and *Add Form Instance Record* permissions. \n\n| **Note:** By default, all users inherit the Guest Role's permissions. The Guest\n| Role represents unauthenticated visitors of your Site. If you want to let\n| Guest users submit forms (the default setting), it makes sense that\n| authenticated users can also. To disable automatic inheritance of the Guest\n| Role's permissions, set\n| [this property](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#Permissions)\n| in your `portal-ext.properties` file:\n| \n|     `permissions.check.guest.enabled=false`\n"
  },
  {
    "path": "en/user/articles/130-forms/19-styling-forms.markdown",
    "content": "---\nheader-id: styling-form-pages\n---\n\n# Styling Form Pages\n\n[TOC levels=1-4]\n\nLet's face it: nobody likes an ugly, confusing form. Styling your \n[form pages](/docs/7-2/user/-/knowledge_base/u/form-pages) \nlets you make your forms user friendly. There are two features for styling your\nforms: \n\n1.  Create rows and columns for form fields. \n\n2.  Move fields from one location to another. \n\nSometimes it doesn't make sense to use the default single-column, \nvertically-oriented form. For example, a form with many fields can save space by \nputting them in different columns. You can also use a mixed approach, with each \nrow broken into a different number of columns. The following screenshots show \nexamples of these layouts. \n\n![Figure 1: This is the default single-column, vertically-oriented form.](../../images/forms-form-row.png)\n\n![Figure 2: Putting form fields in multiple columns can give you more space.](../../images/forms-layout-multicolumn.png)\n\n![Figure 3: The first row is in two columns and the second row is in three columns.](../../images/forms-layout-mixed.png)\n\nBy default, dragging a field onto a form page adds a field that occupies an \nentire row. Follow these steps to resize the field to make room for more fields \nin the row (columns): \n\n1.  Hover over the field to reveal its borders: \n\n    ![Figure 4: Form field borders.](../../images/forms-field-borders.png)\n\n2.  Drag the right or left edge of the field to resize it.\n\n    ![Figure 5: After resizing, the field is smaller.](../../images/forms-field-resized.png)\n\n3.  Add a new field to the remaining space in the row. \n\n    ![Figure 6: There are now two fields in the row.](../../images/forms-field-columns.png)\n\nYou can also move fields. To do so, follow these steps: \n\n1.  Hover over the field and the cursor becomes a hand. \n\n2.  Drag the field to an open location in the builder. Available locations are\n    highlighted in blue and outlined with a dotted line. A field moved to a new \n    row fills that entire row. A field moved to an existing row fills that row's \n    remaining space. \n\n    ![Figure 7: You can also move fields on form pages.](../../images/forms-move-field.png)\n"
  },
  {
    "path": "en/user/articles/130-forms/99-ddl/01-intro.markdown",
    "content": "---\nheader-id: dynamic-data-lists\n---\n\n# Dynamic Data Lists\n\n[TOC levels=1-4]\n\nDynamic data lists display forms created from field sets called *data\ndefinitions*. Data definitions consist of a form's field types (e.g., text,\nboolean, date, radio buttons, selector menus, etc.) and those fields' labels and\nsettings. Data definitions effectively serve as data models for a dynamic data\nlist. For example, you could create a data definition with two text fields: one\nfor a user's name, and one for their comments. You could then display a form\nthat gathers user feedback via a dynamic data list that uses that data\ndefinition. \n\nTo summarize: \n\n-   **Data Definitions:** Define a form's fields. \n-   **Dynamic Data Lists:** Display a form based on a data definition. \n\nYou can create one or multiple dynamic data lists from a single data definition.\nThe user data entered for each dynamic data list is kept separate, even if the\ndata definition is shared. For instance, you could use the example data\ndefinition above to create several dynamic data lists, and then place them\nanywhere you need to get feedback from users. Because each dynamic data list's\nform data is separate, you don't need to worry about trying to figure out which\ndynamic data list the user comment came from. \n\nDynamic data lists are flexible. You don't have to restrict dynamic data lists\nto simple input. You could create something as complex as an entire data entry\nsystem for real estate listings, or any other simple list-based application you\ncan dream up.\n\nYou create data definitions and dynamic data lists in from the Site Menu's\nContent &rarr; Dynamic Data Lists application. Creating data definitions and\nlists doesn't require any coding. However, additional formatting can be added\nwith [FreeMarker](https://freemarker.apache.org/) templates. \n\nThese tutorials show you how to create and use data definitions and dynamic\ndata lists: \n\n-   Creating data definitions. \n-   Creating dynamic data lists. \n-   Creating form and display templates. \n\n## System Configuration\n\nThere are two Dynamic Data Lists entries in System Settings. The Dynamic Data\nLists Service entry contains one setting:\n\n**Add Default Structures**\n: This is enabled by default and pre-loads several embedded data definitions to\nbase data lists on. Once loaded on portal startup, these definitions must be\ndeleted manually from the Site Menu &rarr; Dynamic Data Lists application. This\nsetting applies to the first start of a virtual instance.\n\nThe Dynamic Data Lists entry contains three settings:\n\n**Changeable Default Language**\n: If enabled, the default language of a data definition becomes changeable.\n\n**CSV Export**\n: Choose whether DDL records can be exported in CSV format with or without\na warning, or disable this option. Here's what the warning says: \n\n    Warning: This CSV file contains user supplied inputs. Opening a CSV file \n    in a spreadsheet program may be dangerous.\n\n**Default Display View**\n: Choose whether to use a table based default view or a list based default view. \n<!-- I couldn't see this working. Might be non-functional-->\n"
  },
  {
    "path": "en/user/articles/130-forms/99-ddl/02-creating-data-definitions.markdown",
    "content": "---\nheader-id: creating-data-definitions\n---\n\n# Creating Data Definitions\n\n[TOC levels=1-4]\n\nFollow these steps to create a data definition: \n\n1.  Open the Menu \n    (![Menu](../../../images/icon-menu.png)) \n    and expand your site's menu (the Site Administration menu). Then select \n    *Content & Data* &rarr; *Dynamic Data Lists*. This opens the Dynamic Data Lists \n    screen. A table lists any existing dynamic data lists. \n\n2.  Click the *Options* button at the top-right \n    (![Options](../../../images/icon-options.png)) \n    and select *Manage Data Definitions*. The Data Definitions screen appears. A\n    table lists any existing data definitions. Several definitions are embedded\n    for common use cases like contacts, events, inventory, and more. \n\n    ![Figure 1: The Data Definitions screen.](../../../images/ddl-definitions.png)\n\n3.  Click the *Add* button \n    (![Add](../../../images/icon-add.png)) \n    to begin creating a new data definition. This opens the \n    *New Data Definition* form. \n\n4.  Give your data definition a name. Note that the definition's name appears\n    for any users filling out a dynamic data list that uses the definition. Then\n    expand the *Details* section of the form and give it a description. \n\n5.  The Details section of the form also contains the field *Parent Data\n    Definition*. This optional field lets you select an existing data definition\n    (the parent) to form the basis of the new one (the child). The child\n    definition inherits the parent's fields and settings, which you can then\n    customize. When you create a dynamic data list from a child definition, it\n    includes the fields of the parent and child definitions. This lets you use\n    a common definition (the parent) as the basis of a specialized definition\n    (the child). For example, if you were planning a rock climbing trip, you\n    could use the default Events definition as the parent of a Rock Climbing\n    Trip definition that contains fields unique to rock climbing (e.g., climbing\n    equipment availability, altitude, etc.). \n\n    To choose a parent definition, click the *Select* button below the *Parent\n    Data Definition* field and then select an existing definition in the dialog\n    that appears. \n\n    ![Figure 2: After naming your data definition, expand the Details section of the form and give your definition a description and parent definition, if desired.](../../../images/ddl-definition-form-01.png)\n\n6.  Add the data definition's fields in the data definition designer, below the\n    form's Details section. The designer's default *View* tab lets you create\n    the definition in a WYSIWYG editor. You can click the *Source* tab to work\n    with the definition's underlying JSON, but it's much easier to stick with the\n    WYSIWYG editor. \n\n    In the *View* tab select the *Fields* tab. Icons representing the field\n    types are listed on one side and the data definition's canvas is on the\n    other side. To add a field type to the definition, select its icon, drag,\n    and drop it onto the canvas. By dragging a field onto a field that's already\n    on the canvas, you can nest the new field in the existing field. When you\n    mouse over a field on the canvas, the field action icons appear. Clicking\n    the *+* icon creates a duplicate of the current field and adds it below the\n    current field. Clicking the trash can deletes the field. \n\n    The following fields are available:\n\n    -   **Boolean:** A check box. \n    -   **Color:** Specifies a color. \n    -   **Date:** Enter a date. A valid date format is required for the date \n        field, but you don't have to enter a date manually. When you select the \n        date field a mini-calendar pops up which you can use to select a date. \n    -   **Decimal:** Enter a decimal number. The value is persisted as a \n        `double`. \n    -   **Documents and Media:** Select a file from a Documents and Media \n        library. \n    -   **Geolocation:** Associate a location with the User's form entry.\n    -   **HTML:** An area that uses a WYSIWYG editor to write and display HTML\n        content. \n    -   **Integer:** Enter an integer. The value is persisted as an `int`. \n    -   **Link to Page:** Link to another page in the same site.\n    -   **Number:** Enter a decimal number or an integer. The value is\n        persisted either as a `double` or an `int`, depending on the input's \n        type.\n    -   **Radio:** Displays several clickable options. The default number of \n        options is three but this is customizable. Only one option can be \n        selected at a time.\n    -   **Select:** This is just like the radio field except that the options \n        are hidden and must be accessed from a drop-down menu. \n    -   **Text:** Enter a single line of text.\n    -   **Text Box:** This is just like the text field except you can enter \n        multiple lines of text or separate paragraphs. \n    -   **Web Content:** Select web content.\n\n    ![Figure 3: Use the data definition designer to add fields to the data definition.](../../../images/ddl-data-definition-designer.png)\n\n7.  Edit field labels to reflect their intended data. A text field's default\n    label is *Text*. To use the text field as a title, then you should change\n    the field's label to *Title*. First select the field on the canvas. This\n    automatically selects the *Settings* tab on the left. Alternatively, you\n    can access the Settings tab by clicking the field's wrench icon. To edit a\n    setting value, double-click it in the Settings table and enter the new\n    value. The available settings are listed below. \n\n    You can translate each of a data definition's field values to any supported\n    locales. To specify a field value for a translation, select the flag that\n    represents the locale and enter the field value for the locale. \n\n    The following field settings are available. Note that some of these settings \n    are only available for specific field types:\n\n    -   **Type:** The field's type (e.g., text, radio, etc.). This setting can't \n        be edited, but a display template can reference it. \n    -   **Field Label:** The field's display name. \n    -   **Show Label:** Whether the field label is shown. \n    -   **Required:** Whether users must fill out the field (not available for \n        Boolean fields). \n    -   **Name:** The field's internal identifier. You can use this value in a \n        display template to read the field's data. This value is automatically \n        generated, but you can change it if you wish. \n    -   **Predefined Value:** The field's default value. \n    -   **Tip:** Text to display in a tooltip. \n    -   **Indexable:** Whether the field is indexed for search. \n    -   **Localizable:** Whether the field can be translated. \n    -   **Repeatable:** Whether users can make copies of the field. \n    -   **Multiple:** Whether the user can select more than one option. This is \n        only available for Select fields. \n    -   **Options:** The options available for selection in Radio and Select \n        fields. You can add and remove options, and edit each option's display \n        name and value. \n\n    ![Figure 4: Configure the settings for each field in your data definition.](../../../images/ddl-data-definition-settings.png)\n\n8.  Click *Save* when you're done. Your new data definition then appears in the\n    table with the pre-defined ones and any you've already added. \n"
  },
  {
    "path": "en/user/articles/130-forms/99-ddl/03-managing-data-definitions.markdown",
    "content": "---\nheader-id: managing-data-definitions\n---\n\n# Managing Data Definitions\n\n[TOC levels=1-4]\n\nThere are several ways to manage your data definitions. Of course, you can edit\na data definition, but you can also configure its permissions, manage its\ntemplates, copy it, or delete it. \n\nFollow these steps to access your data definitions: \n\n1.  Open the Menu (![Menu](../../../images/icon-menu.png)) and expand your\n    site's menu (the Site Administration menu). Then select *Content & Data*\n    &rarr; *Dynamic Data Lists*. This opens the Dynamic Data Lists screen. A\n    table lists any existing dynamic data lists. \n\n2.  Click the *Options* button at the top-right\n    (![Options](../../../images/icon-options.png)) and select *Manage Data\n    Definitions*. The Data Definitions screen appears.  A table lists the data\n    definitions. \n\n![Figure 1: You can copy an existing data definition, manage its templates, and more.](../../../images/ddl-definitions-actions.png)\n\nYou can manage your data definitions via the *Actions* menu \n(![Actions](../../../images/icon-actions.png)) \nfor each definition: \n\n**Edit:** Edit the data definition. The edit screen uses the same form \nfor \n[creating data definitions](/docs/7-2/user/-/knowledge_base/u/creating-data-definitions). \nNote that if you edit a data definition referenced elsewhere (e.g., by\na dynamic data list or display template), then you must update that reference. \n\n**Manage Templates:** The *Manage Templates* screen creates and manages\ntemplates for the data definition. For details, see \n[Using Templates to Display Forms and Lists](/docs/7-2/user/-/knowledge_base/u/using-templates-to-display-forms-and-lists). \n\n**Permissions:** Configure the data definition's permissions. Note that \nthese permissions are for an individual definition accessed through the\nDynamic Data Lists application in *Site Administration* &rarr; *Content*\n&rarr; *Dynamic Data Lists*. For example, if Site members have View\npermission for a data definition, any Site member who also has a Role that\ncan access the Dynamic Data Lists app and its data definitions can see this\ndefinition listed in the Manage Data Definitions screen. If you don't want\nthis, remove the View permission for Site Member, and Site members won't\nsee your data definition listed with the others. \n\n**Copy:** The *Copy Data Definition* form copies the definition and its\ntemplates. In the form, give the copied definition a new name and description\nand select whether to also copy the original definition's templates. Click\n*Copy* when you're done. The copied definition then appears in the Data\nDefinitions table with existing definitions. You can create new definitions\nbased on existing ones, and then modify the copied one to suit your needs. You\ncan, of course, edit any definition in the portal, but if you copy a definition\ninstead, you can still access the original. \n\n**Delete:** Delete the definition. \n"
  },
  {
    "path": "en/user/articles/130-forms/99-ddl/04-creating-data-lists.markdown",
    "content": "---\nheader-id: creating-data-lists\n---\n\n# Creating Data Lists\n\n[TOC levels=1-4]\n\nThere are two places to create dynamic data lists: \n\n1.  **Site Administration:** Open the Menu and expand your Site's menu (the\n    Site Administration menu). Then select *Content & Data* &rarr; *Dynamic\n    Data Lists*. This opens the Dynamic Data Lists screen. A table contains any\n    existing lists. Click the *Add* button\n    (![Add](../../../images/icon-add.png)) to open *New List* form. \n\n    To add Dynamic Data Lists, you must have permission to access the Dynamic\n    Data Lists app in Site Administration. \n\n2.  **Dynamic Data Lists Display widget:** Navigate to the Site page where you\n    want this widget and add it to the page from *Add*\n    (![Add](../../../images/icon-add-app.png)) &rarr; *Widgets* &rarr;\n    *Collaboration* &rarr; *Dynamic Data Lists Display*. Then click the widget's\n    *Add List* button. This opens the *New List* form. \n\n    To do this, you must have permission to create a new list in the widget. \n\nEither option leads to the New List form: \n\n1.  Give the list a name and a description. \n\n2.  Select the list's data definition: click *Select* under the *Data\n    Definition* field, then click the definition you want to use. \n\n3.  To use a workflow with this list, select it from the *Workflow* field. \n\n4.  To change the list's default permissions, expand the form's *Permissions* \n    section and make your selections. \n\n5.  Click *Save* to finish creating the list. Your new list appears in the table. \n\n    ![Figure 1: The New List form.](../../../images/ddl-add-list.png)\n\n## Creating List Records\n\nBy default, only administrators have permission to create list records. Follow \nthese steps to give other users this permission: \n\n1.  Navigate to *Content & Data* &rarr; *Dynamic Data Lists* in Site Administration. \n\n2.  Click *Actions* \n    (![Actions](../../../images/icon-actions.png)) &rarr; *Permissions* for the\n    list getting the new permissions.\n\n3.  Select *Add Record* for the Roles that should have that permission, then\n    click *Save*. Allow unauthenticated Users to add records by giving Guest the\n    Add Record permission.\n\nCreate new records in a list from the same places you can create the lists\nthemselves: \n\n1.  **Site Administration:** In Site Administration, navigate to *Content & Data* \n    &rarr; *Dynamic Data Lists*. Click a list in the table to view any existing \n    records, then click the *Add* button \n    (![Add](../../../images/icon-add.png)). \n    This opens a form based on the list's data definition, which you can then \n    fill out and submit to create a new record. To do this, you must have \n    permission to access the Dynamic Data Lists app in Site Administration. \n\n2.  **Dynamic Data Lists Display widget:** See above for instructions on adding \n    this widget to a page. You must then configure the widget to display a \n    list's records. \n\n    To configure the widget to display a list's records: \n\n    -   Click the widget's *Select List* button. \n    -   In the dialog that appears, select a list, click *Save*, then close the \n        dialog. The widget then displays the list's existing records. \n\n    To add a record: \n\n    -   Click the widget's *Add* button \n        (![Add](../../../images/icon-add.png)). \n    -   Fill out the form that appears and click *Publish*. \n\nSee the section below for more information on configuring the widget. \n\n![Figure 2: Dynamic Data Lists Display widget.](../../../images/ddl-widget.png)\n\n## Configuring the Dynamic Data Lists Display Widget\n\nThe widget's default display template isn't exciting, but it shows the list's\ncontents, and with permission, add and/or edit list items. To configure the\nwidget, click its *Options* menu\n(![Options](../../../images/icon-app-options.png)) and select *Configuration*.\nThis opens the Configuration dialog, with the *Setup* tab selected by default.\nThe Setup tab contains two other tabs: \n\n**Lists:** Select the list that the widget displays. The currently \ndisplayed list appears at the top of the tab, while the available lists \nappear in a table. To change the widget's list, select the list from the \ntable and click *Save*. \n\n**Optional Configuration:** \n\n**Display Template:** Select the display template for the list.\n\n**Form Template:** Select the form template for the list.\n\n**Editable:** Whether users can add records to the widget's list. \n\n**Form View:** Whether to display the Add Record form by default, instead of\nthe List View. Note that even without this selected, users can still add\nrecords via the widget's *Add* button\n(![Add](../../../images/icon-add.png)). \n\n**Spreadsheet View:** Whether the List View displays each record in a row, with\ncolumns for the record attributes. \n\nWhen finished, click *Save* and close the Configuration dialog. \n\n![Figure 3: The Dynamic Data Lists Display widget's optional configuration.](../../../images/ddl-widget-options.png)\n"
  },
  {
    "path": "en/user/articles/130-forms/99-ddl/05-using-templates.markdown",
    "content": "---\nheader-id: using-templates-to-display-forms-and-lists\n---\n\n# Using Templates to Display Forms and Lists\n\n[TOC levels=1-4]\n\nAfter creating data definitions and lists, you can control how the form appears\nto users, and how the resulting list of records is displayed. You do this by\ncreating templates for each view (form view for displaying the form and display\nview for the list of records) and selecting them in the DDL Display portlet. For\nexample, you might need to create a form with a subset of a data definition's\nfields. Rather than creating a new definition, you can create a template that\ndisplays only the fields you want from the existing definition. You could also\nuse a template to arrange fields differently, and/or with different labels and\nconfiguration options. \n\nData definitions can have as many form and display templates as you care to\ncreate (or none, if you're satisfied with the default templates). You then \nchoose a list's template in the Dynamic Data Lists Display widget. \n\n## Managing Display and Form Templates\n\nSince Display and Form Templates correspond to a particular data definition,\nthey're accessed from the Data Definitions screen of the Dynamic Data Lists\napplication in Site Administration. See the \n[Creating Data Definitions article](/docs/7-2/user/-/knowledge_base/u/creating-data-definitions) \nfor instructions on accessing this screen. \n\nThe Data Definitions screen lists each definition in a table. To start working \nwith a definition's templates, click the definition's Actions button \n(![Actions](../../../images/icon-actions.png)) \nand select *Manage Templates*. This opens a screen that lists the definition's \ntemplates. You can edit, copy, delete, or configure permissions for a \ndefinition via its Actions button \n(![Actions](../../../images/icon-actions.png)). \n\n"
  },
  {
    "path": "en/user/articles/130-forms/99-ddl/06-creating-form-templates.markdown",
    "content": "---\nheader-id: creating-form-templates\n---\n\n# Creating Form Templates\n\n[TOC levels=1-4]\n\nForm templates control how the data entry form appears for a data definition.\nFollow these steps to create a form template for a definition: \n\n1.  Open the Menu (![Menu](../../../images/icon-menu.png)) and expand your\n    Site's menu (the Site Administration menu). Then select *Content & Data*\n    &rarr; *Dynamic Data Lists*. This opens the Dynamic Data Lists screen. A\n    table lists any existing dynamic data lists. \n\n2.  Click the *Options* button at the top-right \n    (![Options](../../../images/icon-options.png)) \n    and select *Manage Data Definitions*. The Data Definitions screen appears. A \n    table lists any existing data definitions. \n\n3.  Click the definition's *Actions* button \n    (![Actions](../../../images/icon-actions.png)) \n    and select *Manage Templates*. This lists the definition's templates. \n\n4.  Click the *Add* button \n    (![Add](../../../images/icon-add.png)) \n    and select *Add Form Template*. This presents the same kind of graphical, \n    drag-and-drop interface used to \n    [create definitions](/docs/7-2/user/-/knowledge_base/u/creating-data-definitions). \n\n5.  Give your template a name, then expand the *Details* section and give it a \n    description. You can also use the *Mode* selector to select which mode the \n    template applies to (*Create* or *Edit*). \n\n6.  Scroll down to the graphical designer in the *View* tab, and make your \n    desired changes. For example, you can move or delete fields, change field \n    labels, and more. \n\n7.  Click *Save* when you're finished. \n\nAlternatively, you can create form templates from the Dynamic Data Lists Display \nwidget: \n\n1.  Follow the instructions in the \n    [Creating Data Lists article](/docs/7-2/user/-/knowledge_base/u/creating-data-lists) for\n    adding and configuring the widget in a Site page. Make sure to configure the\n    widget to show the list you're creating a template for. \n\n2.  Click the widget's *Add Form Template* button. This opens the same form as \n    above for creating a form template for the list's definition. \n"
  },
  {
    "path": "en/user/articles/130-forms/99-ddl/07-creating-display-templates.markdown",
    "content": "---\nheader-id: creating-display-templates\n---\n\n# Creating Display Templates\n\n[TOC levels=1-4]\n\nFor every data definition, you can create as many displays as you need. If\nyou've created a form template that doesn't show all the fields of a particular\ndata definition in the data list's form view, you probably don't want to display\nthose fields in the list view, either. Modify the list view using Display\nTemplates. \n\n| **Note:** If you're familiar with\n| [web content templates](/docs/7-2/user/-/knowledge_base/u/designing-web-content-with-templates),\n| display templates customize the display of a list in the same way. Display\n| templates are written in FreeMarker or Velocity, pulling data from the data\n| definition in the same way that web content templates pull data from their\n| structures. Also similar to web content templates, display templates can be\n| embedded in other display templates. This allows for reusable code, JavaScript\n| library imports, or macros imported by Velocity or FreeMarker templates in the\n| system. Embedding display templates provides a more efficient process when you\n| have a multitude of similar data definitions. Just import an embedded display\n| template and work off of it for your new display template.\n\nAs with \n[form templates](/docs/7-2/user/-/knowledge_base/u/creating-form-templates), \nyou can create display templates from the Dynamic Data Lists app in Site \nAdministration or the Dynamic Data Lists Display widget. \n\nFollow these steps to create a display template from Site Administration: \n\n1.  Open the Menu \n    (![Menu](../../../images/icon-menu.png)) \n    and expand your Site's menu (the Site Administration menu). Then select \n    *Content* &rarr; *Dynamic Data Lists*. This opens the Dynamic Data Lists \n    screen. A table lists any existing dynamic data lists. \n\n2.  Click the *Options* button at the top-right \n    (![Options](../../../images/icon-options.png)) \n    and select *Manage Data Definitions*. The Data Definitions screen appears. A \n    table lists existing data definitions. \n\n3.  Click the definition's *Actions* button \n    (![Options](../../../images/icon-options.png)) \n    and select *Manage Templates*. This lists the definition's templates. \n\n4.  Click the *Add* button \n    (![Add](../../../images/icon-add.png)) \n    and select *Add Display Template*. \n\n5.  Give the template a name, expand the *Details* section of the form, and \n    give it a description. Also in the details section of the form, select the \n    templating language to use from the *Language* selector. You can choose \n    [FreeMarker](https://freemarker.apache.org/index.html), \n    or \n    [Velocity](https://velocity.apache.org/). \n\n6.  In the *Script* section of the form, create your template in the editor \n    using the templating language you chose in the previous step. The palette to \n    the left of the editor contains common variables. Click a variable to insert\n    it in the editor. The editor also autocompletes. In a FreeMarker\n    template, type `${`, which opens an autocomplete list of common variables.\n    Select a variable to insert it in the editor. Alternatively, you can upload\n    a complete script file via the *Browse* button below the editor. \n\n7.  Click *Save* when you're done creating the template. \n\n![Figure 1: Create your display template in the editor.](../../../images/ddl-template-editor.png)\n\nAlternatively, you can use the Dynamic Data Lists Display widget to create \ndisplay templates: \n\n1.  Follow the instructions in the \n    [Creating Data Lists article](/docs/7-2/user/-/knowledge_base/u/creating-data-lists) \n    for adding and configuring the widget in a site page. Make sure to configure\n    the widget to show the list's definition you're making into a template. \n\n2.  Click the widget's *Add Display Template* button. This opens the same form \n    as above for creating a display template for the list's definition. \n\n## Display Template Editor\n\nHelper variables are available in the template editor. These provide access to\nmost of the data that you'll use in creating Display Templates. The variables\nunder the heading Data List Variables let you inject specific information about\nthe data definition the template is being created for:\n\n**Data Definition ID:** `reserved_ddm_structure_id`\n\n**Data List Description:** `reserved_record_set_description`\n\n**Data List ID:** `reserved_record_set_id`\n\n**Data List Name:** `reserved_record_set_name`\n\n**Template ID:** `reserved_ddm_template_id`\n\nInside a template, these variables give the ID for the record set as well as the\nname, description, and data definition. \n\nDisplay the list of records by retrieving them and assigning them to the handy\n`records` variable. Retrieve the list's records from `DDLDisplayTemplateHelper`,\nwhich contains these functions:\n\n    getDocumentLibraryPreviewURL\n\n    getHTMLContent\n\n    getLayoutFriendlyURL\n\n    getRecords\n\n    renderRecordFieldValue\n\n`DDLDisplayTemplateHelper` performs common tasks. Use the `getRecords` method to\naccess a data definition's entries and assign them to a `records` variable: \n\n    <#assign records = ddlDisplayTemplateHelper.getRecords(reserved_record_set_id)>\n\nThis *fetches* the records of the associated data list. You haven't done\nanything with them yet, so your display is still empty. To list all the records,\nuse the *Data List Records* helper in the sidebar of the template editor.\nRemember to place your cursor in the proper place in the template editor window,\nthen click *Data List Records*. This code appears at the cursor: \n\n    <#if records?has_content>\n        <#list records as cur_record>\n            ${cur_record}\n        </#list>\n    </#if>\n\nThis default code snippet spits out everything in the database for the given\ndata definition, which is ugly and practically useless:\n\n    {uuid=52c4ac1c-afe7-963c-49c6-5279b7030a99, recordId=35926, groupId=20126, \n    companyId=20099, userId=20139, userName=Test Test, versionUserId=20139, \n    versionUserName=Test Test, createDate=2018-07-26 14:31:51.056, \n    modifiedDate=2018-07-26 14:31:51.058, DDMStorageId=35927, recordSetId=35922, \n    recordSetVersion=1.0, version=1.0, displayIndex=0, lastPublishDate=null}\n\nHere's a simple example template that uses a list based on the embedded Contacts\ndata definition, and only displays the Company Name and Email fields in\na bulleted list:\n\n    <#assign records = ddlDisplayTemplateHelper.getRecords(reserved_record_set_id)>\n\n        <h1>Here are contacts by company name and email address.</h1>\n\n        <#if records?has_content>\n            <#list records as cur_record>\n                <ul>\n                    <li>\n                        <#-- The below gets the Company field and wraps it in an <em> tag -->\n                        Company Name: <em>${ddlDisplayTemplateHelper.renderRecordFieldValue(cur_record.getDDMFormFieldValues(\"company\")?first, locale)}</em><br /> \n                        <#-- The below gets the Email field  and wraps it in an <em> tag --> \n                        Email: ${ddlDisplayTemplateHelper.renderRecordFieldValue(cur_record.getDDMFormFieldValues(\"email\")?first, locale)} \n                    </li> \n                </ul> \n            </#list> \n        </#if>\n\nHere's what it looks like: \n\n![Figure 2: Extract appropriate display information, rather than spitting out the whole object.](../../../images/ddl-contacts-template.png)\n\nNow you're prepared to make data lists beautiful using Display Templates.\n"
  },
  {
    "path": "en/user/articles/140-workflow/01-workflow-intro.markdown",
    "content": "---\nheader-id: workflow\n---\n\n# Workflow\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/process-automation/workflow/introduction-to-workflow.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay's workflow engine is named *Kaleo*. In Greek, Kaleo means \"called ones,\"\nwhich is appropriate for a workflow engine that calls users to participate in a\nreview process designed for them. Workflow makes it possible to define any\nnumber of simple to complex business processes/workflows, deploy them, and\nmanage them through a portal interface. The processes have knowledge of Users,\nGroups and Roles. You don't have to write a single line of code to accomplish\nthis: it's defined in an XML document. If you're a Liferay Digital Experience\nPlatform (DXP) customer, you also have access to a visual process builder.\n\nThere are several steps to effective workflowing:\n\n- [Designing review processes in XML](/docs/7-2/reference/-/knowledge_base/r/crafting-xml-workflow-definitions)\n\n- (DXP only) [Visually designing review processes](https://help.liferay.com/hc/en-us/articles/360028821892-Workflow-Designer)\n\n- [Uploading workflow definitions](/docs/7-2/user/-/knowledge_base/u/managing-workflows#uploading-workflow-definitions)\n\n- [Activating workflow for enabled assets](/docs/7-2/user/-/knowledge_base/u/activating-workflow)\n\n- [Managing Workflow definitions](/docs/7-2/user/-/knowledge_base/u/managing-workflows)\n\n- [Sending assets through review](/docs/7-2/user/-/knowledge_base/u/reviewing-assets)\n\n- (DXP only) [Using Workflow Metrics](https://help.liferay.com/hc/en-us/articles/360029042071-Workflow-Metrics-The-Service-Level-Agreement-SLA-)\n\nAfter all that, you'll be familiar with using Liferay's workflow engine to set\nup approval processes for any\n[workflow-enabled content](/docs/7-2/user/-/knowledge_base/u/activating-workflow).\n\n## What's New with Workflow?\n\nThere are some noteworthy enhancements to the workflow functionality:\n\n### DXP Feature: Workflow Metrics\n\nFor DXP subscribers, the _Workflow Metrics_ feature was introduced. Configure\none or more Service Level Agreements (SLAs; think of these as deadlines) on a\nworkflow definition's events, and workflow submissions are accordingly tracked\nand reported as on time or overdue.\n\n#### Service Level Agreements (SLAs)\n\nSLAs define the deadlines on a workflow process's events. They're like a\ncontract between the workflow participants and Users submitting workflow items.\n\nSLAs can be formally agreed-upon deadlines between you and your customers, or\ninformally created to meet internal goals, tracking events such as:\n\n- Total time to resolution\n- Time to complete a specific workflow task\n\n![Figure 1: Use Service Level Agreements (SLAs) to define how workflow metrics are reported.](../../images/workflow-add-sla.png)\n\nFor each workflow event to track, set the SLA duration and when the timer should\nbe paused, if at all.\n\n#### Workflow Reports\n\nOnce an SLA is set, workflow submissions that trigger the SLA timer are\nautomatically reported on by the workflow metrics framework, and given the\nstatus _on time_ or _overdue_.\n\n![Figure 2: See Workflow Reports generated based on your SLAs.](../../images/workflow-report.png)\n\nSee the article on\n[Workflow Metrics](https://help.liferay.com/hc/en-us/articles/360029042071-Workflow-Metrics-The-Service-Level-Agreement-SLA-)\nto learn more about SLAs and available reports.\n\n### Control Panel Reorganization\n\nThe Workflow section of the Control Panel is now a top-level section with its\nown subcategories: Process Builder and Submissions (Metrics, too, if you're a\nDXP subscriber). In @product@ 7.1 Workflow was nested under Control Panel\n&rarr; Configuration.\n\n![Figure 3: Workflow has a top-level entry in the Control Panel.](../../images/workflow-menu.png)\n\n### Workflow Definition Permissions: System Settings\n\nThe Workflow System Settings category (Control Panel &rarr; Configuration &rarr;\nSystem Settings) has a new system scoped configuration entry: _Workflow\nDefinitions_. There's just one configuration option, but it's important:\nEnabling it gives administrators permission to publish workflows and scripts.\n\n![Figure 4: Explicit permission must be granted before administrators are allowed to publish and edit workflow definitions.](../../images/workflow-publication-permission.png)\n\nCreate your own workflows from scratch or leverage existing workflows.\n\n## Embedded Workflows\n\nIn addition to the Single Approver definition, there are some workflow\ndefinitions that ship with @product@ but are not pre-installed, since they're\nprimarily included for test cases. They can be found in the Liferay source code\nin\n\n    /modules/apps/portal-workflow/portal-workflow-kaleo-runtime-impl/src/main/resources/META-INF/definitions\n\nThey're also in your Liferay installation. Open your Liferay installation's\n`osgi/portal/com.liferay.portal.workflow.kaleo.runtime.impl.jar`, and then find\nand open the `com.liferay.workflow.kaleo.runtime.impl-[version].jar`. The\ndefinitions are in the `META-INF/definitions` folder.\n"
  },
  {
    "path": "en/user/articles/140-workflow/02-activating-workflow.markdown",
    "content": "---\nheader-id: activating-workflow\n---\n\n# Activating Workflow\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/process-automation/workflow/using-workflows/activating-workflow.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAssets are integrated with the [workflow\nframework](/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework) if\ntheir content is meant to be sent through review processes. Since this is most\noften the case, there are many out of the box assets that integrate with\nworkflow. In this article, learn how to select a workflow for use with each of\nthese workflow-enabled assets.\n\n## Workflow Assets\n\nActivate a workflow for these assets in Control Panel &rarr; Workflow\n&rarr; Process Builder, in the Configuration tab:\n\n- Blogs Entry\n- Calendar Event\n- Comment\n- Knowledge Base Article\n- Message Boards Message\n- Page Revision\n- User\n- Web Content Article\n- Wiki Page\n\nActivate workflow for these assets at the Site level in Site &rarr;\nConfiguration &rarr; Workflow:\n\n- Blogs Entry\n- Calendar Event\n- Comment\n- Knowledge Base Article\n- Message Boards Message\n- Page Revision\n- Web Content Article\n- Wiki Page\n\nWhat's the difference between the Site workflow configuration and the Control\nPanel Workflow configuration? As with most scoped configurations, the higher\nlevel setting (in the Control Panel) sets the default behavior. It's overridden\nat the more granular level (in the Site menu).\n\nUser doesn't appear on the Site list because adding users is strictly a\nportal-wide administrator activity. Only assets that can be added and configured\nat the Site level (for example, those that are accessed from the Site menu) have\nworkflow configuration controls at the Site level.\n\n## Activating Workflow in Applications\n\nSome assets that are workflow-enabled are activated in their respective\napplication:\n\nActivate workflow for Web Content Folders from the folder settings menu:\n\n![Figure 1: Activate workflow on Web Content folders from the folder's edit screen.](../../images/workflow-web-content-folder.png)\n\nActivate workflow for Documents and Media Folders from the folder settings:\n\n![Figure 2: Activate workflow on Documents and Media folders from the folder's edit screen.](../../images/workflow-dm-folder.png)\n\nEnable workflow on Dynamic Data List entries in each list's Add form:\n\n![Figure 3: Activate workflow for each individual Dynamic Data List.](../../images/workflow-ddl.png)\n\nActivate workflow for each individual form's entries from the Form Settings screen:\n\n![Figure 4: Activate workflow on each form's entries from the Form Settings window.](../../images/workflow-form.png)\n\n## Workflow Behavior\n\nMost of the resources listed above behave just as you might expect with workflow\nactivated: The Publish button for the resource's *Add* form is replaced by a\n*Submit for Publication* button, and instead of instant publication, the asset\nhas its status set as *Pending* and must proceed through the workflow before\npublication.\n\n![Figure 5: Instead of a Publish button, a Submit for Publication button appears for workflow-enabled resources.](../../images/submit-for-publication.png)\n\nPage revisions are slightly different. Page revisions only occur in\n[staging environments](/docs/7-2/user/-/knowledge_base/u/staging-content)\nthat have Page Versioning enabled. When a Page Variation or Site Page Variation\nis created, its creator must click *Submit for Publication* at the top of the\npage, and the variation must be approved in the workflow before it can be\npublished to the live Site.\n\n![Figure 6: With workflow enabled on Page Revisions, the Site administrator must submit their page variation for publication before it can go live.](../../images/page-revision-submission.png)\n\n"
  },
  {
    "path": "en/user/articles/140-workflow/03-managing-workflows.markdown",
    "content": "---\nheader-id: managing-workflows\n---\n\n# Managing Workflows\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/process-automation/workflow/designing-and-managing-workflows/managing-workflows.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLike other resources, workflow definitions can be added, edited, and deleted.\nBut that's just the beginning of workflow management.\n\n- Because workflow definitions can be complex works in progress, they can be\n    versioned.\n\n- Unpublished drafts can be saved.\n\n- Because workflow definitions are XML files, they're portable. Thus, they can\n    also be uploaded.\n\nStart by learning basic workflow management.\n\n## Workflow Definition Publication Permissions\n\nUsers with permission to edit or publish workflow definitions can add \n[Groovy scripts](/docs/7-1/user/-/knowledge_base/u/leveraging-the-script-engine-in-workflow) \nto the workflow. Access to the scripting engine means access to the Java Virtual\nMachine (JVM) of the server. Users who publish (or edit) workflow definitions\ncontaining scripts, therefore, can get access to any data within the reach of\nthe JVM, such as data contained in a separate \n[Virtual Instance](/docs/7-1/user/-/knowledge_base/u/virtual-instances) \nof @product@ itself. \n\nBecause of this far-reaching access, permission to create or edit workflow\ndefinitions is limited to Regular Administrators of the Default Virtual\nInstance.\n\nTo grant Users with these Roles the workflow publication access in additional\nVirtual Instances,\n\n1.  Make sure you understand the access you're granting these admins.\n2.  Navigate to Control Panel &rarr; System Settings &rarr; Workflow &rarr;\n    Workflow Definition. \n3.  Check the box for the setting _Allow Administrators to Publish and Edit Workflows_.\n\nThis only applies to Virtual Instances that have been added to the\nsystem. The Default Virtual Instance provides workflow publication access to\nRegular Administrators (via Control Panel &rarr; Configuration &rarr; Workflow),\nand, if running Liferay DXP, to Site Administrators and other Users with access to\nthe Kaleo Forms Admin applications.\n\n## Adding, Editing, and Deleting\n\nTo add a workflow definition,\n\n1.  Go to Control Panel &rarr; Workflow &rarr; Process Builder.\n\n2.  In the default view (Workflows), click the Add button\n    (![Add](../../images/icon-add.png)).\n\n3.  From here you're either\n    [writing an XML definition](/docs/7-2/reference/-/knowledge_base/r/crafting-xml-workflow-definitions),\n    [designing a definition in in the visual designer (DXP only)](https://customer.liferay.com/documentation/7.2/admin/-/official_documentation/portal/workflow-designer),\n    or\n    [uploading an existing definition](#uploading-workflow-definitions).\n\nTo edit a definition,\n\n1.  Go to Control Panel &rarr; Workflow &rarr; Process Builder.\n\n2.  Click the *Actions* button (![Actions](../../images/icon-actions.png))\n    for the workflow, and click *Edit*.\n\nTo delete a definition,\n\n1.  Go to Control Panel &rarr; Workflow &rarr; Process Builder.\n\n2.  Click the *Actions* button (![Actions](../../images/icon-actions.png))\n    for the workflow, and click *Unpublish*.\n\n    A published workflow cannot be deleted, so you must unpublish its workflow\n    definition first.\n\n    You can't unpublish a definition if it's activated for an asset. First\n    dissociate the workflow definition from any assets that use it. See\n    [here](/docs/7-2/user/-/knowledge_base/u/activating-workflow) for more\n    information.\n\n## Uploading Workflow Definitions\n\nIf you have a local XML definition file (perhaps you want to create a new\nworkflow based on one of the\n[embedded workflows](/docs/7-2/user/-/knowledge_base/u/workflow#embedded-workflows)),\nupload it to @product@:\n\n1.  Navigate to Control Panel &rarr; Workflow &rarr; Process Builder.\n\n2.  Click the *Add* button (![Add](../../images/icon-add.png)).\n\n3.  Name the workflow.\n\n4.  In the *Source* tab, click the hyperlink *import a file* in the sentence\n\n    `Write your definition or import a file`\n\n5.  Find the XML file and upload it. Once uploaded, the definition's XML\n    appears in the workflow editor.\n\n6.  If it's ready to publish, click *Publish*. Otherwise, *Save* it and it stays\n    Unpublished.\n\nWhat's the difference between saving and publishing?\n\n## Published Versus Unpublished\n\nThe difference between a published and unpublished workflow is important:\n\n**Published:** Validation is complete, and the workflow can be assigned to\nassets.\n\n**Unpublished:** Validation is not performed on the unpublished workflow, and it\ncannot be assigned to assets until it's published.\n\n## Workflow Versions\n\nYou're making a simple edit to a workflow, when suddenly you remember you have a\nmeeting with your boss. Quickly you save the workflow and hurry off to your\nmeeting. Congratulations! You were promoted to Director of Business\nProductivity! You have no time to edit workflows now, so your colleague must\nfinish editing and publishing the workflow. Unfortunately, in all the excitement\nof your promotion, you forgot what you changed in the workflow. It's best to\nrevert to the prior version and start editing it from scratch.\n\n1.  Open the workflow editor. Go to *Control Panel* &rarr; *Workflow* &rarr;\n    *Process Builder*, and select the workflow from the list (click its title to\n    open the editor).\n\n2.  Click the *Information* button\n    (![Information](../../images/icon-information.png))\n\n3.  There are two information panel sections: Details and Revision\n    History.\n\n    The Details screen shows information about the creation of the workflow and\n    its last modification, and a summary of the total modifications.\n\n    The Revision History screen shows the workflow's current and prior,\n    restorable versions. To view an old workflow or to restore it if you're sure\n    it's the right version, click the *Actions* button\n    (![Actions](../../images/icon-actions.png)) and select either *Preview* or\n    *Restore*.\n\n4.  When you click *Restore* and see the success message, the prior version\n    is the current version of the workflow. You can now edit the restored\n    version of the workflow.\n\n5.  If edits are necessary, edit and click *Update*. This creates another\n    version of the workflow.\n\n![Figure 1: View and restore prior versions of a workflow.](../../images/workflow-revisions.png)\n\nAlternatively, you can refer to the\n[embedded definitions](/docs/7-2/user/-/knowledge_base/u/workflow#embedded-workflows)\nto get workflow definition ideas.\n"
  },
  {
    "path": "en/user/articles/140-workflow/04-reviewing-assets.markdown",
    "content": "---\nheader-id: reviewing-assets\n---\n\n# Reviewing Assets\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/process-automation/workflow/using-workflows/reviewing-assets.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nUser interaction is required at each workflow process Task Node. Where do users\ncomplete tasks? In the *My Workflow Tasks* application of the User menu.\n\n![Figure 1: Users manage workflow tasks from their My Workflow Tasks widget.](../../images/workflow-myworkflow-tasks-menu.png)\n\n## Asset Submission to Workflow\n\nIf an Asset has a workflow activated, when a user clicks Submit for\nPublication, the workflow definition determines the next step. A user assigned\na Role associated to the workflow task receives a Notification indicating that\nthere is a workflow task to complete.\n\n**Workflow Assignees Have Implicit Asset Permissions:** Users with permission\nto execute a workflow task (e.g., Users with the Portal Content Reviewer Role)\nhave full resource action permissions over the assets they can review. These\npermissions apply in the My Workflow Tasks widget in the User's personal page\nand anywhere else actions on the Asset can be performed.\n\nFor example, consider a User with two permissions:\n\n- The Portal Content Reviewer Role enables Users to review workflow\n    submissions and grants edit and delete permissions on the content they're\n    reviewing.\n- Users also have permission to view Web Content Articles in the Site's\n    _Content_ section.\n\nNeither permission explicitly grants the User management permissions on Web\nContent Articles. Users cannot normally edit or delete a Web Content Article,\nfor example. However, if a Web Content Article is sent to the workflow, Users\ncan access the Web Content Article for review (in their _Assigned to Me_ or\n_Assigned to my Roles_ section of My Workflow Tasks), and they can edit or\ndelete the content while reviewing it in the workflow. While it's in the status\n_Pending_, they can also edit or delete the article from Site Administration\n&rarr; Content &rarr; Web Content because of their implicit permissions granted\nby the workflow system. This additional permission is temporary, and the normal\nresource permissions are activated once the Web Content Article exits the\nworkflow process (for example, it's rejected or approved).\n\n![Figure 2: A User with VIEW permission on Web Content cannot manage Approved Articles.](../../images/workflow-approved-permissions.png)\n\n![Figure 3: A User with access to Web Content in the Workflow can manage Pending Articles.](../../images/workflow-pending-permissions.png)\n\n## Assigning the Task\n\nWorkflow Tasks can be completed only by certain users, based on the\n[Assignment](/docs/7-2/reference/-/knowledge_base/r/workflow-task-nodes#assignments).\n\nAll workflow tasks assigned directly to a user are listed in the My Workflow\nTask widget's *Assigned to Me* tab.\n\n![Figure 4: The assets assigned to a user are listed in *Assigned to Me*.](../../images/workflow-assigned-to-me.png)\n\nIf a workflow was assigned to a Role that the user occupies, the workflow's\ntasks appear in the *Assigned to My Roles* tab.\n\n![Figure 5: The Assets assigned to Roles are listed in each associated user's *Assigned to My Roles* tab.](../../images/workflow-assigned-to-my-roles.png)\n\nTo claim a task, the user must move the task into the *Assigned to Me* tab.\n\n1.  Click the Asset's *Actions* button\n    (![Actions](../../images/icon-actions.png)) and select *Assign to Me*.\n\n2.  Add a comment in the pop-up box if necessary, and click *Done*.\n\nAlternatively, assign the task to another user.\n\n1.  Click the Asset's *Actions* button\n    (![Actions](../../images/icon-actions.png)) and select *Assign to ...*.\n\n2.  Select the user to assign the task.\n\n3.  Add a comment in the pop-up box if necessary, and click *Done*.\n\n## Completing the Task\n\nOnce a task is assigned, it's ready to be completed.\n\nThere's a fast way to send an asset along in the review process:\n\n1.  From the *Assigned to Me* tab, click the *Actions* button\n    (![Actions](../../images/icon-actions.png)) and select *Approve* or\n    *Reject*.\n\n    Note that options names in this menu are identical to the workflow\n    definition's\n    [Transition](/docs/7-2/reference/-/knowledge_base/r/workflow-definition-nodes)\n    names. Your menu might have different options than the *Approve* and\n    *Reject* options in the figure below.\n\n2.  Enter a comment if desired and click *Done*.\n\n![Figure 6: Complete Tasks right from the *Assigned to Me* list.](../../images/workflow-complete-task.png)\n\nHere's how to get a closer look at the Asset before sending it along in the\nworkflow:\n\n1.  From the *Assigned to Me* tab, click the title of the Asset to review.\n\n    The Task screen appears showing details about the Asset:\n\n    ![Figure 7: Inspect Assets before completing the Task.](../../images/workflow-task-review.png)\n\n2.  Inspect the Asset to your liking (or even edit it if you have permission)\n    and click the *Actions* button\n    (![Actions](../../images/icon-actions.png)).\n\n3.  Send it along in the workflow by clicking one of the Transition names (for\n    example, *Approve* or *Reject* in the Single Approver Definition).\n\nAnd, you're done. Once you've completed your workflow tasks, kick back and wait\nfor more to come in.\n"
  },
  {
    "path": "en/user/articles/160-segmentation-and-personalization/01-segmentation-intro.markdown",
    "content": "---\nheader-id: segmentation-and-personalization\n---\n\n# Segmentation and Personalization\n\n[TOC levels=1-4]\n\nLiferay's Segmentation and Personalization shows the right content to the \nright people at the right time. It provides the tools you need to manage \ndifferent audiences and dynamically provide personalized experiences for people \nusing your site. For example, if you're creating a campaign to promote new \nfinancial service products, you need a way to display offers to customers who \nare likely to be interested in those offers. You don't want to display \ninformation on a basic free checking account for an \"advanced\" customer who \ncarries a high balance across several types of accounts, but you do want to \nshow that information to a visitor who entered the site through a landing page \nfrom a promotion at a local college. At the same time, you probably don't \nwant to recommend options for optimizing retirement account contributions to \nthe college student, but the other customer might be a great target for that \ncampaign. By using data like user attributes or visitor interactions, you can\ndynamically target relevant content to your site's guests.\n\n## Defining Segments\n\nThe first part of the equation is defining the types of segments that you need. \nYou can create Segments to capture every case. Segments are composed of\ndifferent criteria. In the previous example you might have a segment for *Free\nChecking Account Prospects* that contains criteria based on user data, like\ncustomers that don't currently have an open checking account; or based on user\nbehavior, like visitors who came to the site through specific channels. To learn\nmore about Segmentation options, see the \n[overview of the Segment editor](/docs/7-2/user/-/knowledge_base/u/the-segment-editor), practice\n[creating basic Segments](/docs/7-2/user/-/knowledge_base/u/creating-user-segments),\nor create more complicated \n[Segments with custom fields and session data](/docs/7-2/user/-/knowledge_base/u/creating-segments-with-custom-fields-and-session-data).\n\n## Integrating Segments with Analytics Cloud\n\nThere are two different stories that User Segments can tell. One is targeting\ncontent to specific audiences that encourages engagement and positive user\nexperiences. The other is defining groups of users and visitors to analyze their\nbehavior and interactions with your site. To tell the second story, you must\nintegrate with Analytics Cloud.\n\nAnalytics Cloud is a Liferay service that provides in-depth information on who\nuses your site and how they use it. Analytics Cloud is a key component to fully\nutilizing Segments and Personalization, since it enables you to see the full\npicture of how users and visitors on your site behave and interact with\nboth standard and targeted content. You can learn more about this in \n[Using Analytics Cloud with User Segments](https://help.liferay.com/hc/en-us/articles/360029041751-Using-Analytics-Cloud-With-User-Segments).\n\n## Personalizing Experiences\n\nThe most important piece of the puzzle isn't defining groups or analyzing user \nbehavior. It's the final step of using the data to provide users and site \nvisitors with the best possible experience, and driving campaigns and content \nengagement. If you strategically create segments, you can then use that to \nenhance user experiences, and make sure that users see content targeted to them.\nContent Page Personalization and Content List Personalization are two key\naspects of this.\n\n### Content Page Personalization\n\nContent Page Personalization dynamically changes the page layout and \ncontent based on who is viewing the page. You can create *Experiences* for any \n[Content Page](/docs/7-2/user/-/knowledge_base/u/creating-content-pages) which\nprovide different text, images, widgets,  and even different layouts based on\nthe segment criteria of the user viewing  the page. You can see a step by step\ndemonstration of this in\n[Content Page Personalization](/docs/7-2/user/-/knowledge_base/u/content-page-personalization).\n\n### Content Set Personalization\n\n[Content Sets](/docs/7-2/user/-/knowledge_base/u/content-sets) organize and\ndisplay content. Content Set Personalization provides dynamic selection of\nContent Sets based on User Segments. This means the Content Set which displays\nin a given context is determined by their segment criteria. For example, you\ncould use a content list to display \"featured\" articles at the top of a page.\nThen you could create Segments containing users who should receive more\nspecialized content, rather than the default. Those Segments would then see\ncontent personalized to their interest rather than the default. You can see  a\nstep by step demonstration of this in\n[Content Set Personalization](/docs/7-2/user/-/knowledge_base/u/content-set-personalization).\n"
  },
  {
    "path": "en/user/articles/160-segmentation-and-personalization/02-segment-editor.markdown",
    "content": "---\nheader-id: the-segment-editor\n---\n\n# The Segment Editor\n\n[TOC levels=1-4]\n\nSegmentation and Personalization in @product-ver@ also provides an editor for\ndefining User Segments.\n\n1.  Go to *Site Administration* and select the site that you wish to create\n    segments for.\n\n2.  Click *People* &rarr; *Segments*.\n\n3.  Click the *Add User Segment* button (![Add](../../images/icon-add.png)).\n\nAt the top of the editor you can set the name, view the current members of the\nsegment as it is defined, and choose to *Save* the Segment or *Cancel* to \ndiscard changes.\n\n![Figure 1: The top portion of the Segment Editor has the segment name and its members.](../../images/sp-editor-top.png)\n\nOn the right side of the page, there's a Properties menu with the following\noptions:\n\n- User\n- Organization\n- Session\n\n![Figure 2: You use the Segment Editor to create new Segments.](../../images/sp-segment-editor-full.png)\n\nIn addition to the various properties, there are operations and conjunctions\nthat you use to define criteria.\n\n## User Properties\n\nUser Properties are user attributes you want to capture. This is made up of\nuser metadata as defined in their accounts, but also contains certain group\nmemberships (like Roles and User Groups) as well as information like the date\nthe user profile was last modified.\n\n## Organization Properties\n\nOrganization Properties is a selectable list of Organizations to include in your\nSegment. They contain similar criteria as the User selection, like *Name* and\n*Date Modified*.\n\n## Session Properties\n\nSession Properties contains criteria based on the user's activity, browser, and\nsystem information. You can use this to target the user's device or OS, or for\nactivity-based criteria like if a user entered the website through a specific\ncampaign driven landing page. \n\n## Operations and Conjunctions\n\nThere are a number of different ways to evaluate properties, and different ways\nthat you can relate different fields. For a comprehensive list, see the\n[Defining Segment Criteria Reference](/docs/7-2/reference/-/knowledge_base/r/defining-segmentation-criteria).\n"
  },
  {
    "path": "en/user/articles/160-segmentation-and-personalization/03-creating-segments.markdown",
    "content": "---\nheader-id: creating-user-segments\n---\n\n# Creating User Segments\n\n[TOC levels=1-4]\n\nTo learn how to use segmentation, you'll step through an example for defining\ntwo segments for a site. The segments use user data like the *Job Title* field\nand organization membership for evaluating segments. The first Segment you'll\ncreate, *American Engineers*, uses standard fields as criteria.\n\nTo get started, navigate to the Segments page.\n\n1.  Go to *Site Administration* for the Bank site.\n\n2.  Open the *People* section and select *Segments*.\n\n## Creating a Custom Segment\n\nOn the Segments page, you'll see a list of all the currently available segments,\nif they're available. Create a segment named *American Engineers* for employees\nof your company.\n\n1.  Click the *Add* button (![Add](../../images/icon-add.png)) button.\n\n2.  Click in the top text area and enter the name *American Engineers*.\n\n3.  Open the *User* tab in the right side *Properties* menu and drag the *Job\n    Title* property to the *Conditions* area.\n\n4.  Click on the operator field and set it to *contains*.\n\n5.  Click on the text field and enter *Engineer*.\n\n    ![Figure 1: Setting the comparator to *contains* includes variations of \"Engineer\" like \"Software Engineer\" in the segment.](../../images/sp-set-date.png)\n\n6.  Open the *Organization* properties and drag over an *Organization* field.\n\n7.  Set the comparator between *User Properties* and *Organization Properties* to\n    *And*.\n\n8.  Set the *Organization* field to be *equals* and select the organization.\n\n    ![Figure 2: You can prevent typos by directly selecting Organizations through the interface.](../../images/sp-select-orgs.png)\n\n9.  Click *Save* to save your Segment.\n\nAs you edit, a count of members meeting the criteria appears at the top of the\npage. You can click on *View Members* to see the list. This helps you determine\nif you are correctly defining the Segment.\n\n![Figure 3: You can view the list of Segment members at any time.](../../images/sp-segment-members.png)\n\n## Managing Segments\n\nAfter you create your Segment, you can see it in the list of Segments on the \n*People* &rarr; *Segments* page. From there you can edit the segment, delete \nit, or change the permissions for it. You cannot delete a segment if it's being\nused in an experience. Also, changing permissions only affects who has \naccess to manage the Segment; it doesn't change Segment membership or criteria.\n\n1.  Go to *People* &rarr; *Segments*.\n\n2.  Click the *Actions* button (![Actions](../../images/icon-actions.png)) next\n    to the *American Engineers*.\n\nYou can manage the options here. You can also click on the Segment's name to\nedit it.\n\n![Figure 4: You can edit, delete or manage permissions from the options menu.](../../images/sp-options.png)\n\nNext, you'll define a Custom Field and use it as part of your Segmentation \ncriteria.\n"
  },
  {
    "path": "en/user/articles/160-segmentation-and-personalization/04-creating-segments-with-custom-fields.markdown",
    "content": "---\nheader-id: creating-segments-with-custom-fields-and-session-data\n---\n\n# Creating Segments with Custom Fields and Session Data\n\n[TOC levels=1-4]\n\nNow that you created a segment, you can take things to the next level and use\na Custom Field to define segment criteria.\n\n## Creating a Custom Field\n\nFirst, create a custom field to use for the Segment:\n\n1.  In the *Control Panel* go to *Configuration* &rarr; *Custom Fields*.\n\n2.  Click on *User*.\n\n3.  Click the *Add* button (![Add](../../images/icon-add.png)) button in the top\n    right.\n\n4.  On the next page, click on *Dropdown*.\n\n5.  For the *Field Name* enter *Cardholder Type*.\n\n6.  For values, enter *None,* *Basic*, *Gold*, and *Platinum* on four separate\n    lines.\n\n7.  Click *Save*.\n\nNow any time user is created, they are prompted to enter the *Cardholder Type*,\nand existing users can select it from their user profiles.\n\n![Figure 1: You can easily create custom fields to capture whatever kind of data you need.](../../images/sp-create-custom-field.png)\n\nFor more information on adding Custom Fields, see\n[Adding Custom Fields](/docs/7-2/user/-/knowledge_base/u/custom-fields#adding-custom-fields).\n\n## Defining a Segment with a Custom Field\n\nNext, use a custom field to define another segment. \n\n1.  From Segments Administration, click the *Add* button\n    (![Add](../../images/icon-add.png)).\n\n2.  Click in the text area at the top of the page, enter the name *Premium Card\n    Prospects*.\n\n3.  For User Properties select *Cardholder Type*.\n\n4.  Click on the operator field and set it to *equals*.\n\n5.  Select *Basic* from the select box.\n\n![Figure 2: The custom field you created is seamlessly integrated into segment creation.](../../images/sp-select-custom-field.png)\n\n9.  Click *Save* to save your Segment.\n\nAs you can see, segment criteria can be easily defined using @product@'s\nbuilt-in criteria or your own custom fields. Now, you'll use session data to\nmake this Segment definition even more robust.\n\n## Extending a Segment With Session Data\n\nSo far, you've used criteria derived from user profiles to determine if they\nshould be members of a segment; now it's time to use session data to make your\ncriteria more effective.\n\n| **Note:** For this exercise to work, you must have set a cookie on the \n| specified page.\n\n1.  Click the *Actions* button (![Actions](../../images/icon-actions.png)) next\n    to the *Premium Card Prospects* segment and select *Edit*.\n\n2.  Click the *Session* dropdown from the Properties menu.\n \n3.  Set the comparator for Session Properties to *Or*.\n\n4.  Drag *Cookies* into the Session Properties section.\n\n5.  Change the selection box to *contains*.\n\n6.  Enter *Cards* in the Key text box.\n\nNow any user who has a cookie from visiting the *Cards* page is evaluated as\npart of the *Premium Card Prospects* segment.\n"
  },
  {
    "path": "en/user/articles/160-segmentation-and-personalization/05-personalization-experience-managment.markdown",
    "content": "---\nheader-id: personalization-experience-management\n---\n\n# Personalization Experience Management\n\n[TOC levels=1-4]\n\nThere is no direct location for managing all aspects of Experience \nPersonalization. All the different aspects are managed from whatever you're \npersonalizing. The key integration points of personalization are Content Pages\nand Content Sets. You can see an overview of their personalization options \nbelow.\n\n## Managing Content Page Personalization\n\nBefore you can Personalize Content Pages, you first need to\n[create Content Pages](/docs/7-2/user/-/knowledge_base/u/creating-content-pages).\nWhen you edit a Content Page, you can click on the *Experience* to manage the\noptions for that page.\n\n![Figure 1: You can add, edit, delete, or change priority for Experiences.](../../images/manage-content-page-experience.png)\n\n1.  Go to *Site Administration* &rarr; *Site Builder* &rarr; *Pages*.\n\n2.  Click the *Actions* button ![Actions](../../images/icon-actions.png) &rarr;\n    *Edit* for any Content Page.\n \n3.  Click on the *Default* Experience to manage experiences.\n\nFrom here you have three options:\n\n**![Edit](../../images/icon-edit.png)** changes the name or selected\nSegment for the Experience.\n\n**![Delete](../../images/icon-delete.png)** deletes the Experience.\n\n**![Priority](../../images/icon-priority.png)** changes the priority of the\nExperience. If a user meets the criteria for more than one Experience, the\nhighest ordered one is displayed.\n\nWhen creating a new Experience, you must define the audience by choosing a\nSegment. If your target audience for the Experience is not yet represented by a\nSegment, you can create one from the New Experience interface.\n\n![Figure 2: You can add a new Segment while creating a new Experience.](../../images/add-seg-from-exp.png)\n\n| **Note:** Creating new Segments from the New Experience interface is available\n| in Liferay DXP 7.2 Fix Pack 1+ and Liferay Portal GA2+.\n\nNext you'll learn about managing Content Set personalization.\n\n## Managing Content Set Personalization\n\nManaging Personalization options for Content Sets is similar. First you must \n[create Content Sets](/docs/7-2/user/-/knowledge_base/u/content-sets) then you\ncan personalize them. To create a new Personalized Variation of a Content Set,\n\n1.  Click the *Actions* button ![Actions](../../images/icon-actions.png) &rarr;\n    *Edit* for the Content Set you wish to personalize.\n\n2.  Next to the content set is the message *No Personalized Variations yet*.\n    Click the *New Personalized Variation* button.\n\n3.  Select a Segment from the next page to create a variation for that Segment.\n\n![Figure 3: Select a Segment to create a variation for.](../../images/select-content-set-variation.png)\n\nYou can create a personalized variation for any existing Segment. Each new \nvariation copies the default Content Set, but then essentially functions as its \nown Content Set after that. To edit or manage a Content Set Variation,\n\n1.  Click on the Segment name under *Personalized Variations*.\n\n2.  Click on the *Actions*8 button (![Actions](../../images/icon-actions.png))\n    and you can select *View Content* to preview the content in that set or\n    *Delete* to remove it.\n\n![Figure 4: You can preview or delete a Personalized Variation from the *Actions* menu.](../../images/manage-content-set-segments.png)\n\n## Previewing User Experiences\n\nAs an administrator, when you view a page, you can preview the different \nexperiences that users can have on that page.\n\n1.  Click the *Simulation* button\n    (![Simulation](../../images/icon-simulation.png)) icon from the top of any\n    page.\n \n2.  Select a segment from the *Segments* selection to preview the page as a \n    member of that segment.\n \nViewing the perspective of a segment previews for the administrator any\npersonalizations for Content Pages or Content Sets for that Segment.\n\n![Figure 5: You can preview different experiences from the Preview Panel.](../../images/personalization-segment-preview.png)\n"
  },
  {
    "path": "en/user/articles/160-segmentation-and-personalization/06-content-page-personalization.markdown",
    "content": "---\nheader-id: content-page-personalization\n---\n\n# Content Page Personalization\n\n[TOC levels=1-4]\n\nIn [Creating Segments with Customer Fields and Session Data](/docs/7-2/user/-/knowledge_base/u/creating-segments-with-custom-fields-and-session-data)\nyou created a Segment called *Premium Card Prospects*. Now, you'll use it to\ndemonstrate Content Page Personalization.\n\nIf you're not familiar with Content Pages, see the\n[Creating Content Pages](/docs/7-2/user/-/knowledge_base/u/creating-content-pages)\narticle before you get started here. \n\n## Creating the Default Page\n\nFirst, you need to create the *Credit Cards* page. To do this, create a new \ncontent page, add some fragments to it, and edit the content to match. First \nyou need to build the bones of the page:\n\n1.  Go to *Site Administration* &rarr; *Site Builder* &rarr; *Pages*.\n\n2.  Click the *Add* button (![Add](../../images/icon-add.png)).\n\n3.  Select *Content Page* and name the page *Credit Cards*.\n\n4.  Open the *Sections* &rarr; *Basic Sections* from the build menu on the right\n    side of the screen and add a *Banner* to the page.\n\n5.  Next, click on *Section Builder*, open *Layouts*, and add a three column \n    layout to the page, above the banner.\n\n![Figure 1: Open Layouts from the Section Builder.](../../images/section-builder-layouts.png)\n\n6.  Open the *Basic Components* tab and add a *Card* inside each of the columns.\n\nNext, edit the content (this step is optional):\n\n1.  Click on the text for the cards and the banner to edit it and provide \n    content relevant to someone looking for information about credit cards.\n\n2.  Click on each image and provide an appropriate image.\n\n3.  Click *Publish*.\n\n![Figure 2: Your final result might look something like this.](../../images/personalization-default-content.png)\n\nThis is the default page that anyone visiting the site sees.\n\n## Defining Custom Experiences\n\nNext, define an experience specifically tailored to customers whom you have\nidentified as *Premium Card Prospects* using User Segments.\n\n1.  At the top of the page, for the *Experience* click on *Default* to open\n    the experience selection dialog.\n\n    ![Figure 3: Click on the current experience to create a new one or select a different existing experience.](../../images/select-experience.png)\n\n2.  Click on *New Experience*.\n\n3.  Name it *Card Prospects* and select *Premium Card Prospects* for the \n    *Audience*.\n\n4.  Add a *Banner* fragment above the three columns you added earlier.\n\n5.  Edit the Banner to provide information specifically related to upgrading\n    a card for an existing customer.\n\n6.  Click *Publish*.\n\nThe *Default* version of the page appears for everyone except for those\ndefined as *Premium Card Prospects*, but customers in that segment have\nan experience curated just for them.\n\n![Figure 4: Your final result for the card prospects might look something like this.](../../images/personalization-prospects.png)\n\n| **Note:** When you create a new experience, it copies the *Default*\n| experience at the time that it is created. Any further changes to the\n| *Default* experience do not effect any of experiences for that page.\n"
  },
  {
    "path": "en/user/articles/160-segmentation-and-personalization/07-content-set-personalization.markdown",
    "content": "---\nheader-id: content-set-personalization\n---\n\n# Content Set Personalization\n\n[TOC levels=1-4]\n\nIn [Creating User Segments](/docs/user/7-2/-/knowledge-base/u/creating-user-segments)\nyou created a  Segment called *American Engineers*. Now, you'll use it to\ndemonstrate Content  Set Personalization. For this example, create a Content Set\nto be the  default displayed on the *Home* page. Then you'll modify it to create\na personalized variation containing technical articles for members of the\n*American Engineers* segment.\n\nIf you're not familiar with Content Set, see the\n[Creating Content Sets](/docs/7-2/user/-/knowledge_base/u/creating-content-sets)\narticle before you get started here. \n\n## Creating and Setting the Default Content Set\n\nFirst create the default Content Set and configure it on the Home page using the\nAsset Publisher.\n\n1.  Go to *Site Administration* &rarr; *Content & Data* &rarr; *Content Sets*.\n\n2.  Click the Add button (![Add](../../images/icon-add.png)) and choose *Manual \n    Selection*.\n\n3.  Name it *Home Page Content*.\n\n4.  For the new Content Set, click *Select* next to *Asset Entries* and select\n    *Basic Web Content*.\n\n    ![Figure 1: Click *Select* to add a new Asset Entries.](../../images/create-default-content-set.png)\n\n5.  On the *Select Basic Web Content* page, check the boxes next to the content \n    you want to add and click *Add*.\n\n6.  Navigate to the *Home* page and add an Asset Publisher to the page.\n\n7.  Open *Configuration* for the Asset Publisher.\n\n8.  Under *Asset Selection* select *Content Set*.\n\n9.  Under *Select Content Set* click *Select*, choose *Home Page Content*,\n    and click *Save*.\n\nNow the Content Set that you configured appears in the Asset Publisher on \nthe *Home Page*. Next configure the Content Set for Personalization.\n\n## Personalizing the Content Set\n\nNow create the content set for engineers and configure its display.\n\n1.  Go back to the Content Set from *Site Administration*.\n\n2.  Click *New Personalized Variation* and select the *American Engineers* \n    segment\n\n    ![Figure 2: Create a new Personalized Variation.](../../images/create-personalized-variation.png)\n\n3.  Click *Select* next to *Asset Entries* and select *Basic Web Content*.\n\n4.  Select articles appropriate to an engineering audience and click *Add*.\n\nNow anytime a member of the *American Engineers* segment views this Content Set \nbeing displayed, they see the personalized version and not the default. Test\nthis now, using the *Simulator*.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/01-intro.markdown",
    "content": "---\nheader-id: content-publication-management\n---\n\n# Content Publication Management\n\n[TOC levels=1-4]\n\nToday's enterprises generate an enormous amount of content. You can use advanced\npublishing tools to manage content for a seamless publication experience.\n\nStaging lets you change your Site behind the scenes without affecting the live\nSite. This is done by creating your Site in a temporary staging area (local or\nremote) and then publishing all the changes at once. \n\n<!-- This is the supported way to manage content publication.\n\n@product@ also offers Change Lists, which provide a flexible way to manage\ndifferent site versions, including its assets and how they are displayed. Change\nLists let users group changes into one set and publish those changes when\nthey are ready. This is a beta feature and is not supported at this time. Only\nenable the Change Lists feature for testing purposes. -->\n\nContinue on to begin managing content publication!\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/01-intro.markdown",
    "content": "---\nheader-id: staging\n---\n\n# Staging\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/staging-overview.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nStaging is a tool used to manage content publication. The concept of staging is\nsimple: you can modify your site behind the scenes and then publish all your\nupdates in one shot. You don't want users seeing your web site change before\ntheir eyes as you're modifying it, do you? The staging environment lets you make\nchanges to your site in a specialized *staging area* that's linked to a\nproduction environment. Typically the staging site is used only by content\neditors and site administrators, while the production environment is public.\nContent is published from staging to production all at once.\n\nSite administrators can set up their staging environments locally or remotely.\nWith Local Live staging, your staging environment and live environment are\nhosted on the same server. Remote Live staging has the staging and live\nenvironments on separate servers. You'll learn more about the differences\nbetween these two staging environments and how to enable them for your portal\ninstance.\n\nYou can also leverage the Page Versioning feature. This feature works with both\nLocal Live and Remote Live staging and lets site administrators create multiple\nvariations of staged pages. This allows several different versions of sites and\npages to be developed at the same time. Variations can be created, merged, and\npublished using a Git-like versioning system. In the next section, you'll jump\nin to see how to enable staging.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/02-enabling-staging/01-intro.markdown",
    "content": "---\nheader-id: enabling-staging\n---\n\n# Enabling Staging\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/staging-overview.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nYou have two different ways to set up staging:\n\n- [Local Live](/docs/7-2/user/-/knowledge_base/u/enabling-local-live-staging)\n- [Remote Live](/docs/7-2/user/-/knowledge_base/u/enabling-remote-live-staging)\n\nWhether you enable Local Live or Remote Live staging, the interface for managing\nand publishing staged pages is the same.\n\nLocal Live staging lets you publish site changes quickly, since the staged and\nlive environments are on the same server. It's also easier to switch between the\nstaged and live environments using Local Live staging. Since the staged content,\nhowever, is stored in the same database as the production content, your server\nmust have more resources, and the content isn't as well protected or backed up\nas with Remote Live staging. Also, you can't install new versions of widgets for\ntesting purposes in a Local Live staging environment, since only one version of\nan widget can be installed at any given time on a single Liferay server.\n\nWith Remote Live staging, your staging and live environments are hosted on\nseparate servers, so your data is separated. This lets you deploy new versions\nof widgets and content to your staging environment without interfering with your\nlive environment. Publishing is slower, however, with Remote Live staging since\ndata must be transferred over a network. Of course, you also need more\nhardware to run a separate staging server.\n\nVisit the staging environment article (Local or Remote) that most closely aligns\nwith your goal for staging content.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/02-enabling-staging/02-enabling-local-live-staging.markdown",
    "content": "---\nheader-id: enabling-local-live-staging\n---\n\n# Enabling Local Live Staging\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/configuring-local-live-staging.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLocal Live staging places both your staging environment and your live\nenvironment on the same server. When it's enabled, a clone of the site is\ncreated containing copies of all of the site's existing pages. This means the\nstaging and live environments share the same JVM, database, portlet data\n(depending on which portlets are selected when staging is enabled), and\nconfigurations, such as the properties set in the `portal-ext.properties` file.\nThe cloned site becomes the staging environment and the original site becomes\nthe live environment.\n\nYou can enable local staging for a site by navigating to the *Publishing* &rarr;\n*Staging* menu. To get some hands-on experience with enabling Local Live\nstaging, you can complete a brief example which creates a Local Live staging\nenvironment for your site. \n\n1.  Navigate to the Product Menu (left side) and select *Publishing* &rarr;\n    *Staging*.\n\n2.  Select *Local Live*. You can also enable page versioning and select staged\n    content. For more information on these options, see the\n    [Enabling Page Versioning and Staged Content](/docs/7-2/user/-/knowledge_base/u/enabling-page-versioning-and-staged-content)\n    article.\n\n3.  Click *Save*.\n\nYou've officially begun the staging process!\n\nBecause Local Live staging creates a clone of your site, you should only\nactivate staging on new, clean sites. Having a few pages and some widgets (like\nthose of the example site you created) is no big deal. If you've already created\na large amount of content, however, enabling staging can take a lot of time\nsince it's a resource intensive operation. Also, if you intend to use page\nversioning to track the history of updates to your site, you should enable it as\nearly as possible, *before* your site has many pages and lots of content. Your\nsite's update history isn't saved until you enable page versioning. Page\nversioning requires staging (either Local Live or Remote Live) to be enabled.\n\nIf you ever need to turn off the staging environment, return back to *Staging*\nfrom the Publishing dropdown. For more information on this, see the\n[Disabling Staging](/docs/7-2/user/-/knowledge_base/u/disabling-staging)\narticle.\n\nGreat! Now you're ready to use Local Live Staging.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/02-enabling-staging/03-enabling-remote-live-staging.markdown",
    "content": "---\nheader-id: enabling-remote-live-staging\n---\n\n# Enabling Remote Live Staging\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/configuring-remote-live-staging.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nIn Remote Live staging, a connection is established between the current site and\nanother site on a remote Liferay server. The remote site becomes the live\nenvironment and the current site becomes the staging environment---an instance\nof Liferay used solely for staging. The remote (live) Liferay server and the\nlocal (staging) Liferay server should be completely separate systems. They\nshould not, for example, share the same database. When Remote Live staging is\nenabled, all the necessary information is transferred over the network\nconnecting the two servers. Content creators use the staging server to make\ntheir changes while the live server handles the incoming user traffic. When\nchanges to the site are ready to be published, they are pushed over the network\nto the remote live server. \n\nBefore enabling Remove Live staging, ensure you've configured your Liferay\nserver and remote server appropriately. Follow the\n[Configuring Servers for Remote Live Staging](/docs/7-2/user/-/knowledge_base/u/configuring-servers-for-remote-live-staging)\narticle to do this.\n\nYou can enable remote staging for a site by navigating to the *Publishing*\n&rarr; *Staging* menu. Step through the instructions below to create a Remote\nLive staging environment for your site. \n\n1.  Navigate to the Product Menu (left side) and select *Publishing* &rarr;\n    *Staging*.\n\n2.  Select *Remote Live*. Additional fields appear for Remote Live Connection\n    Settings.\n\n    ![Figure 1: After your remote Liferay server and local Liferay server have been configured to communicate with each other, you have to specify a few Remote Live connection settings.](../../../../images/remote-live-staging-settings.png)\n\n3.  Enter your remote Liferay server's IP address into the Remote Host/IP field.\n    This field should match the host you specified as your\n    `tunnel.servlet.hosts.allowed` property in the `portal-ext.properties` file.\n    If you're configuring an IPv6 address, it must contain brackets when entered\n    into the *Remote Host/IP* field (e.g., *[0:0:0:0:0:0:0:1]*).\n\n    If the remote Liferay server is a cluster, you can set the Remote Host/IP to\n    the load balanced IP address of the cluster to increase the availability of\n    the publishing process. See the\n    [Configuring Remote Staging in a Clustered Environment](/docs/7-2/deploy/-/knowledge_base/d/configuring-remote-staging-in-a-clustered-environment)\n    for details.\n\n4.  Enter the port on which the remote Liferay instance is running into the\n    Remote Port field. You only need to enter a Remote Path Context if a\n    non-root portal servlet context is being used on the remote Liferay server.\n\n5.  Enter the ID of the site on the remote Liferay server that's for the Live\n    environment. If a site hasn't already been prepared on the remote Liferay\n    server, you can log in to the remote Liferay server and create a new blank\n    site.\n\n    After the site has been created, note the site ID so you can enter it into\n    the Remote Site ID field on your local Liferay server. You can find any\n    site's ID by selecting the site's name on the Sites page of the Control\n    Panel.\n \n6.  Check the *Use a Secure Network Connection* field to use HTTPS for the\n    publication of pages from your local (staging) Liferay server to your remote\n    (live) Liferay server.\n\n7.  Decide whether to enable page versioning and select staged content. For more\n    information on these options, see the\n    [Enabling Page Versioning and Staged Content](/docs/7-2/user/-/knowledge_base/u/enabling-page-versioning-and-staged-content)\n    article.\n\n8.  Click *Save*.\n\nYou've officially begun the staging process!\n\nIf you fail to configure your current and remote server properly, you won't be\nable to enable staging and an error message appears. If you have issues,\n[verify you've configured your servers properly](/docs/7-2/user/-/knowledge_base/u/configuring-servers-for-remote-live-staging).\n\nWhen a user publishes changes from the local (staging) server to the remote\n(live) server, @product@ passes the user's email address, screen name, or user\nID to the remote server to perform a permission check. For a publishing\noperation to succeed, the operation must be performed by a user that has\nidentical credentials and permissions on both the local (staging) and the remote\n(live) server. This is true regardless of whether the user attempts to publish\nthe changes immediately or attempts to schedule the publication for later.\n\nIf only a few users should have permission to publish changes from staging to\nproduction, it's easy enough to create a few user accounts on the remote server\nthat match a selected few on the local server. The more user accounts that you\nhave to create, however, the more tedious this job becomes and the more likely\nyou are to make a mistake. And you not only have to create identical user\naccounts, you also have to ensure that these users have identical permissions.\nFor this reason, it's recommended that you use LDAP to copy selected user\naccounts from your local (staging) Liferay server to your remote (live) Liferay\nserver. Liferay's Virtual LDAP Server application, available on Liferay\nMarketplace, makes this easy.\n\nSee the\n[Disabling Staging](/docs/7-2/user/-/knowledge_base/u/disabling-staging)\narticle to learn how to turn off the staging environment.\n\nGreat! Now you're ready to use Remote Live Staging.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/02-enabling-staging/04-configuring-servers-for-remote-live-staging.markdown",
    "content": "---\nheader-id: configuring-servers-for-remote-live-staging\n---\n\n# Configuring Servers for Remote Live Staging\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/configuring-remote-live-staging.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nBefore you can enable Remote Live staging for a site, you must satisfy some\nnecessary requirements:\n\n- Add the remote Liferay server to the current Liferay server's list of allowed\n  servers, and vice versa.\n- Specify an authentication key to be shared by your current and remote server.\n- Enable each Liferay server's tunneling servlet authentication verifier.\n- Update the Tunnel Auth Verifier Configuration of your remote Liferay instance.\n\nFollow the steps below to configure your servers for Remote Live staging.\n\n1.  Add the following lines to your current Liferay server and remote Liferay\n    server's `portal-ext.properties` file:\n\n        tunneling.servlet.shared.secret=[secret]\n        tunneling.servlet.shared.secret.hex=true\n\n    @product@'s use of a pre-shared key between your staging and production\n    environments helps secure the remote publication process. It also removes\n    the need to send the publishing user's password to the remote server for web\n    service authentication. Using a pre-shared key creates an authorization\n    context (permission checker) from the provided email address, screen name,\n    or user ID *without* the user's password.\n\n2.  Specify the values for the servers' `tunneling.servlet.shared.secret`\n    property.\n\n    The values for these properties depend on the chosen configured encryption\n    algorithm, since different encryption algorithms support keys of different\n    lengths. See the\n    [HTTP Tunneling](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html#HTTP%20Tunneling)\n    properties documentation for more information. Note that the following key\n    lengths are supported by the available encryption algorithms:\n\n    - AES: 128, 192, and 256 bit keys\n    - Blowfish: 32 - 448 bit keys\n    - DESede (Triple DES): 56, 112, or 168 bit keys (However, Liferay places an\n      artificial limit on the minimum key length and does not support the 56 bit\n      key length)\n\n    To prevent potential character encoding issues, you can use one of the\n    following two strategies:\n\n    2a. Use hexadecimal encoding (recommended). For example, if your password\n       was *abcdefghijklmnop*, you'd use the following settings in your\n       `portal-ext.properties` file:\n\n        tunneling.servlet.shared.secret=6162636465666768696a6b6c6d6e6f70\n        tunneling.servlet.shared.secret.hex=true\n\n    2b. Use printable ASCII characters (less secure). This degrades the password\n       entropy.\n\n    If you don't use hexadecimal encoding (i.e., if you use the default setting\n    `tunneling.servlet.shared.secret.hex=false`), the\n    `tunneling.servlet.shared.secret` property's value *must* be ASCII\n    compliant.\n\n    Once you've chosen a key, make sure the value of your current server matches\n    the value of your remote server.\n\n    **Important:** Do not share the key with any user. It is used exclusively\n    for communication between staging and production environments. Any user with\n    possession of the key can manage the production server, execute server-side\n    Java code, etc.\n\n3.  Add the following line to your remote Liferay server's\n    `portal-ext.properties` file:\n\n        tunnel.servlet.hosts.allowed=127.0.0.1,SERVER_IP,[STAGING_IP]\n\n    The `[STAGING_IP]` value must be replaced by the staging server's IP\n    addresses. If the server has multiple interfaces, each IP address must also\n    be added, which would show as a source address for the http(s) requests\n    coming from the staging server. The `SERVER_IP` constant can remain set for\n    this property; it's automatically replaced by the Liferay server's IP\n    addresses.\n\n    If you're validating IPv6 addresses, you must configure the app server's JVM\n    to not force the usage of IPv4 addresses. For example, if you're using\n    Tomcat, add the `-Djava.net.preferIPv4Stack=false` attribute in the\n    `$TOMCAT_HOME\\bin\\setenv.[bat|sh]` file.\n\n5.  Update the *TunnelAuthVerfierConfiguration* of your remote Liferay instance.\n    To do this, navigate to the Control Panel &rarr; *Configuration* &rarr;\n    *System Settings* &rarr; *API Authentication* &rarr; *Tunnel\n    *Authentication*. Click */api/liferay/do* and insert the additional IP\n    *addresses you're using in the *Hosts allowed* field. Then select *Update*.\n\n    Alternatively, you can also write this configuration into an OSGi file (e.g.,\n    `osgi/configs/com.liferay.portal.security.auth.verifier.tunnel.module.configuration.TunnelAuthVerifierConfiguration-default.config`)\n    in your @product@ instance:\n\n        enabled=true\n        hostsAllowed=127.0.0.1,SERVER_IP,[Local server IP address]\n        serviceAccessPolicyName=SYSTEM_USER_PASSWORD\n        urlsIncludes=/api/liferay/do\n\n6.  Restart both Liferay servers after making these configuration updates. After\n    restarting, log back in to your local Liferay instance as a site\n    administrator.\n\nThat's all you need to do to configure Remote Live Staging! You can now\n[enable it](/docs/7-1/user/-/knowledge_base/u/enabling-remote-live-staging)!\n\nFor additional information on configuring Remote Live staging, see the topics\nbelow.\n\n## Applying Patches When Using Remote Staging\n\nWhen applying patches to a remote staging environment, you must apply them to\nall your servers. Having servers on different patch levels is not a good\npractice and can lead to import failures and data corruption. It is essential\nthat all servers are updated to the same patch level to ensure remote staging\nworks correctly.\n\n## Configuring Remote Staging's Buffer Size\n\nSimilar to Local Live staging, it is a good idea to turn remote staging on at\nthe beginning of your site's development for good performance. When you're using\nRemote Live staging, and you are publishing a large amount of content, your\npublication could be slow and cause a large amount of network traffic.\n@product@'s system is very fast for the amount of data being transferred over\nthe network. This is because the data transfer is completed piecemeal, instead\nof one large data dump. You can control the size of data transactions by setting\nthe following portal property in your `portal-ext.properties` file:\n\n    staging.remote.transfer.buffer.size\n\nThis property sets the file block sizes for remote staging. If a LAR file used\nfor remote staging exceeds this size, the file will be split into multiple files\nprior to transmission and then reassembled on the remote server. The default\nbuffer size is 10 megabytes.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/02-enabling-staging/05-enabling-page-versioning-and-staged-content.markdown",
    "content": "---\nheader-id: enabling-page-versioning-and-staged-content\n---\n\n# Enabling Page Versioning and Staged Content\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/page-versioning.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nEnabling page versioning for a site lets site administrators work in parallel on\nmultiple versions of the site's pages. Page versioning also maintains a history\nof all updates to the site from the time page versioning was enabled. Site\nadministrators can revert to a previous version of the site at any time. This\nflexibility is very important in cases where a mistake is found and it's\nimportant to publish a fix quickly. \n\nYou can enable page versioning for public pages or private pages on the Staging\nConfiguration page below the menu for selecting your staging environment (Local\nor Remote). If you've already enabled staging, you can navigate to the Product\nMenu &rarr; *Publishing* &rarr; *Staging* and click the Options\n(![Options](../../../../images/icon-options.png)) button and select *Staging\nConfiguration*.\n\n![Figure 1: You can decide to use versioning and choose what content should be staged.](../../../../images/staging-page-versioning-staged-content.png)\n\nYou can also choose content for the staging environment to manage on the Staging\nConfiguration page.\n\nChoosing content to be staged may sound self-explanatory, but content must have\nspecific attributes in @product@ to use it in a staged environment. Content or\nan entity should be site-scoped, so they are always part of a site; otherwise,\nthey are not eligible for staging. For example, page-scoped entities are only\neligible for staging on published pages. When scoped data is on a page (e.g.,\nWeb Content Display widget) and the page is published, the scoped data is\npublished with it.\n\n@product@ by default supports the following content groups for staging:\n\n- Blogs\n- Bookmarks\n- Calendar\n- Documents and Media\n- Dynamic Data Lists\n- Forms\n- Knowledge Base\n- Message Boards\n- Mobile Device Families\n- Polls\n- Web Content\n- Widget Templates\n- Wiki\n\nBefore you activate staging, choose which of these widgets' data you'd\nlike to copy to staging. You'll learn about many of the collaboration widgets\nlisted under the Staged Content heading when you read the\n[Collaboration Suite's](/docs/7-2/user/-/knowledge_base/u/collaboration)\nsection of articles. For now, be aware that you can enable or disable staging\nfor any of these widgets.\n\nWhy might you want to enable staging for some widget types but not others?\nIn the case of collaborative widgets, you probably *don't* want to enable staging\nsince such widgets are designed for user interaction. If their content were\nstaged, you'd have to  publish your site manually whenever somebody posted a\nmessage on the message boards to make that message appear on the live site.\nGenerally, you want web content to be staged because end users aren't creating\nthat kind of content---web content is the stuff you publish to your site. But\nwidgets like the Message Boards or Wiki should *not* be staged. Notice\nwhich widgets are marked for staging by default: if you enable staging and\naccept the defaults, staging is *not* enabled for the collaborative widgets.\n\nThe listed widgets, or content groups, contain one or more specific entity. For\nexample, selecting the Web Content widget does not mean you're only selecting\nweb content itself, but also web content folders.\n\nCertain content types can be linked together and can reference each other on\ndifferent levels. One of the responsibilities of staging is to discover and\nmaintain these references when publishing. Site administrators and content\ncreators have control over the process on different levels: staging can be\nenabled for a content group and a content group can be selected for publication.\n\nDisabled staged content types can cause unintended problems if you're referring\nto them on a staged site. For example, the Asset Publisher portlet and its\npreferences are always staged. If the content types it's set to display are not\nenabled for staging, the Asset Publisher can't access them on a staged site.\nMake sure to plan for the content types you'll need in your staged site.\n\nTurning Staging on and off for individual portlet data could cause data\ninconsistencies between the staging and live sites. Because of this, it's not\npossible to modify the individual portlet configuration once you enable staging.\nIn case you need adjustments later on, you must turn staging off and re-enable\nit with your new configuration.\n\nBesides managing the widget-specific content, @product@ also has several special\ncontent types such as pages or users. For instance, pages are a part of the site\nand can reference other content types, but in a special way. The page references\nwidgets, which means publishing a page also implies publishing its widgets. The\ncontent gives the backbone of the site; however, content alone is useless. To\ndisplay content to the end user, you'll need widgets as the building blocks for\nyour site.\n\nBefore you begin exploring the Staging UI, it's important to understand the\npublishing process for staging to make informed decisions so you use the\nstaging environment efficiently and effectively.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/03-publishing-staged-content-efficiently/01-publishing-staged-content-efficiently-intro.markdown",
    "content": "---\nheader-id: publishing-staged-content-efficiently\n---\n\n# Publishing Staged Content Efficiently\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/understanding-the-publishing-process.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nNow that you understand how staging works, you'll dive deeper into the\npublication process and some prerequisites you should follow before publishing.\nBy understanding how the process works, you can make smart and informed\ndecisions about how you want to publish your site's content.\n\n## Understanding the Publication Process\n\nIn simple terms, a publication is the process where all content, referenced\ncontent, apps and their preferences, pages, etc. are transferred from the\nstaging scope to the live site. If you've enabled remote staging, this process\ninvolves network communication with another remote site. From a low level\nperspective, staging is an equivalence relation where entities are mirrored to\na different location. From a high level perspective, the staging process happens\nin three phases:\n\n1.  **Export:** processes the publication configuration, which defines the\n    site's content and apps. This phase also gathers the obligatory referenced\n    entities that are required on the live site. Then everything according to\n    the publication parameters is processed into the instance's own file format,\n    and that file is stored locally or transferred to the remote live Liferay\n    instance.\n\n2.  **Validation:** determines if it's possible to start the import process.\n    This phase verifies the file's version and its integrity, checks for\n    additional system information like language settings, and validates there is\n    no missing content referenced.\n\n3.  **Import:** makes any necessary updates or additions to the site's content,\n    layouts, and apps according to the publishing parameters. If everything is\n    verified and correct, the staged content is published to your live Site.\n\nThese phases are executed sequentially.\n\nA crucial factor for successfully publishing staged content is data integrity.\nIf anything is not verified during the publication process, the transactional\ndatabase reverts the site back to its original state, discarding the current\npublication. This is a necessary action to safeguard against publishing\nincomplete information, which could break an otherwise well-designed live Site.\n\nIf the file system is not *database-stored* (e.g., DBStore), it's not\ntransactional and isn't reverted if a staging failure occurs. This could\npotentially cause a discrepancy between a file and its reference in the\ndatabase. Because of this, administrators should take great care with staging\nthe document library, making sure that regular backups of both database and file\nsystem are being maintained.\n\nNext, you'll learn about staging best practices and prerequisites to follow for\na seamless staging experience.\n\n## Planning Ahead for Staging\n\nStaging is a complex subsystem that's flexible and scalable. Before advanced\nusers and administrators begin using it for their site, it's important to plan\nahead and remember a few tips for a seamless process. There are several factors\nto evaluate.\n\n- **Content (amount, type, and structure):** Depending on the content in your\n  site, you can turn on staging for only the necessary content types, leaving\n  others turned off to avoid unnecessary work. Publication can also be\n  configured to publish only certain types of content. See the \n  [Managing Content Types](/docs/7-2/user/-/knowledge_base/u/managing-content-types-in-staging)\n  article for more information.\n\n- **Hardware Environment:** You should plan your environment according to your\n  content types. If your site operates on large images and video files, decide\n  if a shared network drive is the best option. Storing many large images in the\n  Document Library usually requires a faster network or local storage. If you're\n  dealing with web content, however, these are usually smaller and take up very\n  little disk space.\n\n- **Customizations and Custom Logic for Your Staging Environment:** Your\n  organization's business logic is most likely implemented in an app, and if you\n  want to support staging for that app, you must \n  [write some code](/docs/7-2/frameworks/-/knowledge_base/f/content-publication-management) \n  to accomplish this. You can also consider changing default UI settings by\n  writing new JSP code if you want your staging environment's look and feel to\n  change.\n\nOnce you've finished planning for your site, you should turn on staging at the\nvery beginning of the site creation process. This allows the site creator to\navoid waiting for huge publications that can take long periods to execute.\nTaking smaller steps throughout the publication process forms an iterative\ncreative process as the site is built from the ground up, where content creators\ncan publish their changes immediately, avoiding long wait times.\n\nHere are some JVM/network configuration recommendations for Staging:\n\n- 4 GB of memory \n\n- 20 MB/s transfer rate minimum (disk)\n\nNow that you know how the staging environment works and how to enable it for\nyour site, you'll begin using it in the next section.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/04-using-the-staging-environment/01-intro.markdown",
    "content": "---\nheader-id: using-the-staging-environment\n---\n\n# Using the Staging Environment\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/staging-ui-reference.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAfter [enabling staging](/docs/7-2/user/-/knowledge_base/u/enabling-staging)\n(either Local Live or Remote Live) for a site, you'll notice additional options\nprovided on the top Control Menu (Staging Bar) and also in the menu to the left.\nThese new menus help you manage staged pages. Most of your page management\noptions have also been removed; now you can't directly edit live pages. You now\nmust use the staging environment to make changes.\n\n<!-- Removed Staging video divs. Put back when it's available for 7.2. -->\n\nClick the *Staging* button to view the staged area. Management options are\nrestored and you can access some new options related to staging. \n\n![Figure 1: You can see the new staging options added to the top and left of your screen.](../../../../images/staging-live-page.png)\n\nTo test out the staging environment, add the Bookmarks widget and then click on\n*Live* from the top menu. Notice that the Bookmarks widget isn't there. That's\nbecause you've staged a change to the page but haven't published that change yet\nto the live site.\n\nNext, you'll learn the basics of staging content.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/04-using-the-staging-environment/02-staging-content.markdown",
    "content": "---\nheader-id: staging-content\n---\n\n# Staging Content\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/managing-data-and-content-types-in-staging.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nWhen you're on the staged site, you have several options in the Staging Bar to\nhelp start your staging conquest.\n\n**Site Pages Variation:** lets you work in parallel on multiple versions of a\nstaged site page. You can choose the site page variation from the dropdown menu\nor manage them from the *Options* icon\n(![Options](../../../../images/icon-staging-bar-options.png)) in the Staging\nBar. See the\n[Using Site Pages Variations](/docs/7-2/user/-/knowledge_base/u/using-multi-and-single-page-variations)\narticle for more information.\n\n**Page Variations:** lets you work in parallel on multiple versions of a staged\npage. You can choose the page variation from the dropdown menu or manage them\nfrom the *Options* icon\n(![Options](../../../../images/icon-staging-bar-options.png)) in the Staging\nBar. See the\n[Using Site Pages Variations](/docs/7-2/user/-/knowledge_base/u/using-multi-and-single-page-variations)\narticle for more information.\n\n**Undo/Redo:** steps back/forward through recent changes to a page, which can\nsave you the time of manually adding or removing apps if you make a mistake. To\naccess *Undo*/*Redo*, select the *Options* icon\n(![Options](../../../../images/icon-staging-bar-options.png)) in the Staging\nBar.\n\n**History:** shows the list of revisions of the page, based on publication\ndates. You can go to any change in the revision history and see how the pages\nlooked at that point. To access *History*, select the *Options* icon\n(![Options](../../../../images/icon-staging-bar-options.png)) in the Staging\nBar.\n\n**Ready for Publication:** After you're done making changes to the staged page,\nclick this button. The status of the page changes from *Draft* to *Ready for\nPublication* and any changes you've made can be published to the Live Site. When\nyou publish a page to live, only the version *Marked as Ready for Publication*\nis published.\n\nNow you'll step through a brief example for using the Staging Bar to stage and\npublish content.\n\n1.  On the staged site, navigate to the Product Menu and select *Content & Data*\n    &rarr; *Web Content*.\n\n2.  Create a Basic Web Content article and save it.\n\n3.  Go back to your staged site's main page and navigate to the *Add*\n    (![Add](../../../../images/icon-add-widget.png)) &rarr; *Widgets* &rarr;\n    *Content Management* menu and drag the *Web Content Display* widget to the\n    page.\n\n4.  Select the web content article you created to display in the new widget.\n\n5.  Select the *Ready for Publication* button to confirm you're ready to publish\n    the content from the staged site to the live site. This prepares the\n    staged content for publication. If workflow is enabled for any new resource,\n    the resource must go through the workflow process before it can be published\n    to the live site.\n\n    ![Figure 1: The staging toolbar indicates whether you're able to publish to the live site.](../../../../images/staging-publish-bar.png)\n\n6.  Click the *Publish to Live* button. A pop-up window appears with\n    configuration options for your publication.\n\n7.  Enter the name of your publication.\n\n8.  Observe the changes listed in the menu. This lists the changed content\n    planned for publication.\n\n    ![Figure 2: The Simple Publication menu displays the changes since last publication and a way to name your publication.](../../../../images/simple-staging-publication.png)\n\n9.  Click the *Publish to Live* button to publish your staged results to the\n    live site.\n\nAwesome! You've created content on the staged site and published it to your live\nsite. It's now available for all your site users to see!\n\n| **Note:** Although publishing content is the more well-known function,\n| publishing a portlet is also a viable option. You can publish portlets\n| residing in the Control Panel and on pages. For example, you can modify a\n| portlet's title and publish the change to live. This is possible because\n| portlet configurations are always staged.\n| \n| To publish a portlet that is on a page, you must publish the page first.\n\nThis example explored the Simple Publication menu. If your publication requires\nmore advanced configuration like specifying specific content, dates, pages,\netc., you should click the *Switch to Advanced Publication* button. You'll\nexplore the more advanced configuration options next.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/04-using-the-staging-environment/03-advanced-publication-with-staging.markdown",
    "content": "---\nheader-id: advanced-publication-with-staging\n---\n\n# Advanced Publication with Staging\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/site-staging-ui-reference.html#advanced-publishing\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nOnce you've finished your changes on the staged site and want to publish them,\nselect the *Publish to Live* button from the Staging Bar. To configure advanced\npublication options, select the *Switch to Advanced Publication* button. Opening\nthe Advanced Publication menu presents options for scheduling a time to publish\nyour content, editing the pages/content to include in the publication, managing\npermissions, etc. This lets you perform advanced editing to your publication\nprocess.\n\n## Date\n\nYou have two options for the Date category:\n\n**Now:** immediately pushes any changes to the live Site.\n\n**Schedule:** set a specific date to publish or to set up recurring publishing.\nYou could use this to publish all changes made during the week every Monday\nmorning without any further intervention.\n\nThese options let you plan staging schedules in advance.\n\n## Pages\n\nThis area of the menu gives you the option to choose which pages to include when\nyou publish. You can choose the page group (Public or Private) to publish by\nselecting the *Change to Public Pages* or *Change to Private Pages*. You cannot\npublish both at the same time; you must complete their publication processes\nseparately if you want to publish both page groups.\n\n![Figure 1: You have several ways to specify the pages you want included in your publication.](../../../../images/staging-advanced-publication.png)\n\nYou can also choose specific pages to publish, and the look and feel of those\npages.\n\n| **Note:** If you're publishing pages with a custom theme, you must check the\n| *Theme Settings* option under the Look and Feel heading for your staging\n| configuration. If it's not checked, the default theme is always applied.\n\nThe *Delete Missing Pages* option under the Look and Feel heading deletes all\npages from the live Site that are not present on the staging Site. You can\nchoose specific pages to publish (and remove) by manually selecting them under\nthe Pages to Publish heading.\n\n| **Note:** The Simple Publication menu displays the number of page deletions\n| tracked by the Staging framework. Keep in mind that this number counts the\n| page deletions on the staging Site, not how many pages will be deleted on the\n| live Site. There could be an inconsistency between the number of page\n| deletions to be published and the actual number of pages present on either of\n| the staging and live Sites. For example, pages that were deleted on the\n| staging Site before they were published.\n\nSee the [Deletions](#deletions) section for more information on content\ndeletion.\n\n## Content\n\nThe Content area lets you select the content to be published. If you choose a\npage to be published from the Pages menu, the portlets and their references are\nalways published, even if you specify differently in the Content section.\n\nThere are other filtering sub-options for certain content types. You first must\nchoose what content to publish based on date. Specifying a date range lets you\nchoose content to publish based on when it was created or last modified. Select\nthe option that best fits your workflow. The available options are described in\nmore detail below:\n\n**All:** publishes all content regardless of its creation or last modification\ndate.\n\n**From Last Publish Date:** publishes content that was created or modified since\nthe last publish date. This is the default option.\n\n**Date Range:** publishes content based on a specified date range. You can set a\nstart and end date/time window. The content created or modified within that\nwindow of time is published.\n\n**Last:** publishes content based on a set amount of time since the current\ntime. For example, you can set the date range to the past 48 hours, starting\nfrom the current time.\n\nUnder the date options are the different types of content that can be published.\nThis list is populated based on the provided date range. For example, if at\nleast one article is created or modified in the given date range, a Web Content\nsection appears in the list, and the number of articles is shown next to the Web\nContent label. Otherwise, the Web Content section is absent.\n\nThe *Categories* content type is not dependent on the date range and is always\nshown in the list.\n\n| **Note:** Since comments and ratings are meant for the end user, they are not\n| supported in staging and can only be added to the live site.\n\nUnchecking the checkbox next to a certain content type excludes it from the\ncurrent publication to the live site.\n\nSome of the content types in the list, like Web Content and Documents and Media,\nhave further filtering options. For instance, when the Web Content section is\npresent and checked, it shows a comma-separated list of related items to be\npublished, including the articles themselves. A sample list of related items for\nweb content might look like this: *Web Content(12), Structures(3), Referenced\nContent, Include Always, Version History*. You can remove items by clicking the\n*Change* button next to the list.\n\nSee the\n[Managing Content Types in Staging](/docs/7-2/user/-/knowledge_base/u/managing-content-types-in-staging)\narticle for more information on managing content during the publication process.\n\n## Deletions\n\nThis portion of the menu lets you delete two things: \n\n- portlet metadata before publishing\n- operations performed for content types\n\nYou have two options to manage for deletions:\n\n**Delete Application Data Before Importing:** all data created by the\napplication is deleted before the import process. Ensure you understand the\nramifications of this option before selecting it. Some applications in other\npages may reference this data. This process cannot be undone. If you are unsure,\ncomplete an export first.\n\n**Replicate Individual Deletions:** operations performed for content in the\nstaging environment are replicated to the target site. This does not include\npage deletions.\n\n## Permissions\n\nThis area lets you include permissions for the pages and portlets when the\nchanges are published. Select the *Publish Permissions* checkbox to enable this\nfunctionality.\n\nOnce you're finished configuring you advanced publication, select *Publish to\nLive* to publish or schedule your publication.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/04-using-the-staging-environment/04-managing-content-types-in-staging.markdown",
    "content": "---\nheader-id: managing-content-types-in-staging\n---\n\n# Managing Content Types in Staging\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/managing-data-and-content-types-in-staging.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nWhen managing content in Staging's Advanced Publication menu, there are several\nfactors to consider when preparing your content for publication. As described in\n[Advanced Publication with Staging](/docs/7-2/user/-/knowledge_base/u/advanced-publication-with-staging),\nyou can navigate to the Content area of the Advanced Publication menu\nto select content you want to publish. There are options attached to each\ncontent group (e.g., Web Content) that you can manage too.\n\n![Figure 1: Click the *Change* button for a content group to manage its specific content.](../../../../images/web-content-version-history-box.png)\n\nYou'll learn about some of these options and their best practices next.\n\n## Referenced Content\n\nThis is represented by\n\n- Structures and templates included in web content.\n- Documents and Media files (e.g., images) included in web content.\n- etc.\n\nYou can exclude some of this content during publication or export to speed up\nthe process. These references are validated during the publication process or an\nimport, so the images must be published or imported first.\n\n## Version History\n\nWeb content tends to be updated frequently, often more so than other kinds of\ncontent. Sometimes this can result in high numbers of versions. If there are\nhundreds of versions, it can take a long time to publish these articles. You\ncan bypass this by choosing to not publish the *Version History* (i.e.,\nthe past versions of the web content articles to be published). If you disable\nthis, only the last **approved** version of each web content article is\npublished to Live. This can significantly speed up the publication process.\n\nYou can set this option globally. If you navigate to the Control Panel &rarr;\n*Configuration* &rarr; *System Settings* &rarr; *Web Content* &rarr; *Virtual\nInstance Scope* &rarr; *Web Content*, you can toggle the *Version History by\nDefault Enabled* checkbox. This sets the default behavior. When publishing\ncontent, it is selected by default, so site administrators must manually uncheck\nthe *Version History* box to publish only the latest approved version of web\ncontent articles. To change the default behavior, enable the checkbox in System\nSettings.\n\n## Previews and Thumbnails\n\nPreviews and thumbnails are generated automatically for documents. Disabling\nthis, though, can greatly increase your publishing speed in some cases. You\nshould be careful about publishing previews and thumbnails to the live Site.\n\nImagine a scenario where a site has approximately 4000 images or documents. If\nthe previews and thumbnails are turned on, this could end up in 28000 physical\nfiles on the disk. If staging is set up to publish the previews and thumbnails,\nthis would mean that instead of taking care of the 4000 images, it would process\nseven times more files! If you still want to use the previews on your live\nenvironment, you can set up that Liferay instance to generate them\nautomatically.\n\nIt depends on your environment for whether you can use the publishing of the\npreviews and thumbnails. Publishing them is a heavy operation, and you must\nalso transfer the LAR file over the network if you use remote staging. If you\ndecide to generate them on the live Site, understand that this could take some\ntime, since it's a CPU intense operation.\n\n## Vocabularies\n\nWhen working within a site, a user may select vocabularies from both the current\nsite as well as the global site. While this doesn't pose an issue when creating\ncontent, it can cause issues when publishing.\n\nFor environments that use both global and local vocabularies, note that global\nvocabularies must be published to the live site through global site staging. One\nway to avoid confusion with vocabularies is to keep all vocabularies local or\nglobal.\n\nIf both must be used, you can resolve the issue by ensuring that dependencies\n(e.g., categories and vocabularies) are published before publishing the site\nthat depends on them (whether the dependencies are local or global).\n\nAssets like tags, categories, structures, templates, widget templates, document\ntypes, and dynamic data lists can also be shared by a parent to its child sites.\nIn this case, ensure that the ancestor's dependencies are published before the\nsite in question.\n\n## Deletions\n\nThe Staging framework gathers deletions (including trashed entities) in a site.\nThese deletions can be published to clean up the live Site. If you plan to\nprocess it later, or if it's not a problem to have lingering data on live,\nthis can be turned off as well to save execution time during the process.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/04-using-the-staging-environment/05-staging-processes-and-templates.markdown",
    "content": "---\nheader-id: staging-processes-and-templates\n---\n\n# Staging Processes and Templates\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/staging-ui-reference.html#staging-page\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nWhen you make a staging publication, it's captured as a staging process and\nstored for future reference. You can manage these processes by navigating to the\n*Staging* option located in the Product Menu's Publishing tab. From there,\nyou'll see a list of staging processes that have been completed. You can\nrelaunch or clear any of these publications by clicking the *Actions* button\n(![Options](../../../../images/icon-actions.png)) next to a process. If you\nclick the *Scheduled* tab from above, you'll find staging processes that you've\nscheduled for future publication dates. \n\n![Figure 1: Your staging processes can be viewed at any time.](../../../../images/staging-processes.png)\n\nIf you find yourself repeatedly creating similar staging processes, you should\nthink about using Publish Templates.\n\nInstead of manually having to customize a publication process every time you're\nlooking to publish pages/content, you can use a publish template. With publish\ntemplates, you can select a custom template and immediately publish with the\noptions you configured.\n\nFollow the steps below to create and use a publish template.\n\n1.  Select the *Options* icon (![Options](../../../../images/icon-options.png))\n    from the top right corner of the Staging screen and select *Publish\n    Templates*.\n\n2.  Click the *Add* button\n    (![Add Publish Template](../../../../images/icon-add.png)) and assign the\n    template a title and description, and then fill out the configuration\n    options as you would during a custom publication process.\n\n3.  Save your publish template. It's available to use from the *Publish\n    Templates* tab in the *Publish to Live* menu's Advanced Publication area.\n\n4.  To use the template, click the *Actions* button\n    (![Actions](../../../../images/icon-actions.png)) next to the template and\n    select *Publish*.\n\n    This automatically sets the options for publishing pages and their content.\n    All you have to do is give the publication process a name. Once you confirm\n    the configuration settings, your staging settings are published.\n\n| **Note:** When staging is enabled, the options available from the *Publishing*\n| tab are modified. When in the Live environment, you can only access the\n| *Export* feature. When in the Staging environment, you can only access the\n| *Import* and *Staging* features. The disabled features for each environment\n| don't make sense in that context. For example, you shouldn't be able to import\n| content when in the live environment; it must be imported into the staged\n| environment and then published before it is available in the live site.\n\nNow you know how to reference stored/scheduled staging processes and create\npublish templates to streamline publication. \n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/05-disabling-staging/01-disabling-staging-intro.markdown",
    "content": "---\nheader-id: disabling-staging\n---\n\n# Disabling Staging\n\n[TOC levels=1-4]\n\nDisabling staging doesn't take a lot of steps, but should not be done lightly.\nIt's important to know the consequences of turning the staging environment off\nso you can decide if your circumstances really warrant it.\n\nThe consequences for disabling Local Live and Remote Live staging are slightly\ndifferent, so you'll learn about both.\n\n## Disabling Local Live Staging\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This material has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/configuring-local-live-staging.html#disabling-local-live-staging\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nConceptually, the live site is the final approved version of your site, whereas\nthe staging site is a temporary workspace containing information that is not\nfinalized.\n\nDisabling local staging completely removes the staging environment, which means\nall content not published to your live site is erased. Therefore, before\ndisabling staging, you must ensure all necessary information on the staged site\nis published or preserved elsewhere.\n\nKeep in mind that draft content types are not published, so they can be lost\ntoo.\n\nWhen you enabled staging there was an initial publication. Disabling staging\ndoes not start a publication; the staging site is deleted. If the staged site\ncontains a large amount of content, however, those deletions could take\na substantial amount of time to process. For this reason, don't disable staging\nwhen your portal instance is busy.\n\n## Disabling Remote Live Staging\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This material has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/configuring-remote-live-staging.html#disabling-remote-live-staging\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nDisabling remote staging does not delete the staged site; it only disables the\nconnection between the live site and remote staging site. This means no data is\ndeleted from the live or remote staging sites after the connection is disabled.\nSince no data is erased and no processes are started, disabling remote staging\nis almost instantaneous.\n\nWhen Remote Live staging is enabled, certain information (e.g., which portlet is\nbeing staged) is recorded on both the live and staged Sites. For this reason,\nwhen you disable remote staging, you must ensure the live Site is still\naccessible so both sides can communicate that it's disabled. Do not shut down\nyour live Site and then attempt to disable remote staging from your staged Site;\nthis results in errors.\n\nIf there's ever a lost network connection between the remote staged Site and the\nlive Site, a message appears, informing you of the error and a way to forcibly\ndisable staging. This is only an option for the staged site; executing this\noption erases the staged site's staging information---not the content. On the\ncontrary, the live site remains in a locked state. A possible workaround is to\ncreate a new live site and import content to it, if necessary.\n\n## Steps to Disable Staging\n\nFollow the steps below to disable Local Live or Remote Live staging:\n\n1.  Navigate to the *Publishing* &rarr; *Staging* option, which is only\n    available from the staged site.\n\n2.  Click the *Options* icon (![Options](../../../../images/icon-options.png))\n    from the upper right corner of the page and select *Staging Configuration*.\n\n3.  Select the *None* radio button and click *Save*.\n\nThat's it! Your staging environment is now turned off.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/06-publishing-single-assets-from-staged-site/01-publishing-single-assets-from-staged-site-intro.markdown",
    "content": "---\nheader-id: publishing-single-assets-from-a-staged-site\n---\n\n# Publishing Single Assets From a Staged Site\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/publishing-single-assets-and-widgets.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nSometimes, stepping through the entire publication process is not necessary and\ncan be overkill. For example,\n\n- What if you created a web content article and want only to publish it and\n  nothing else?\n- What if you want to publish a new folder of articles and their dependencies,\n  but don't want the hassle of the publication process?\n- What if you found a typo in a document and want to fix it quickly and push to\n  the live site?\n\nYou're in luck! You can publish certain single assets from the staged Site to\nthe live Site without creating a staging publication process, from their\nrespective app menus:\n\n- Web Content\n    - Web Content\n    - Folder\n- Documents and Media\n    - Document\n    - Folder\n    - Shortcut\n    - Document Type\n- Blogs\n    - Blog\n- Bookmarks\n    - Bookmark\n    - Folder\n\n**Important:** Single asset publication is not supported for page-scoped\ncontent.\n\n| **Note:** When publishing a Web Content or Bookmarks folder, their respective\n| entries and subfolders are included. Publishing a Documents and Media folder\n| works the same way, but also includes shortcuts.\n\nYou'll step through an example to see how this is done.\n\n1.  Make sure the Staging framework is\n    [enabled](/docs/7-2/user/-/knowledge_base/u/enabling-staging) and you're\n    on the staged Site.\n\n2.  Create a Web Content Article in the Product Menu's *Content & Data* &rarr;\n    *Web Content* menu.\n\n3.  Once you've saved the new Web Content Article, select its *Actions*\n    button (![Actions](../../../../images/icon-actions.png)) next to the article\n    and select *Publish to Live*.\n\n    ![Figure 1: You can publish the single web content article to the live site.](../../../../images/single-asset-publish.png)\n\n4.  You're presented a Process Details page where you can view the progress of\n    your single asset publication request. Ensure the Web Content Article is\n    published successfully.\n\n    | **Note:** Sometimes the publication process doesn't start immediately\n    | (e.g., if there's another publication running). You can check a specific\n    | asset's publication progress by navigating to the *Options*\n    | (![Options](../../../../images/icon-options.png)) &rarr; *Staging* &rarr;\n    | *Current and Previous* tab in its Site Admin app.\n\nThere you have it! If you navigate to your live site's Web Content section, the\nnew article is available.\n\nSimilar to the regular staging publication process, your single asset\npublications also include associated dependencies. For example, if your web\ncontent article contains an image, custom structure, and custom template, they\nare all published together. The same concept applies for folders---if you\npublish a folder containing several web content articles, all the articles and\ntheir associated dependencies are published too.\n\nBy default, only those with permissions to publish widgets can publish single\nassets. Follow the steps below to modify these permissions for a Role:\n\n1.  Navigate to the Control Panel &rarr; *Users* &rarr; *Roles*.\n\n2.  Select the Role you're updating.\n\n3.  Click the *Define Permissions* tab.\n\n4.  In the left menu, navigate to *Control Panel* &rarr; *Sites* &rarr; *Sites*.\n\n5.  Under the Resource Permissions heading, select the *Export/Import\n    Application Info* option.\n\nAlso, make sure the *Publish Staging* permission is granted to the role. This\nis required to publish assets with staging. See the\n[Managing Permissions](/docs/7-2/user/-/knowledge_base/u/managing-permissions)\narticle for more information.\n\nGreat! Now you know how to publish single assets and manage the permissions for\nwho can do it.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/07-organizing-pages-for-staging/01-intro.markdown",
    "content": "---\nheader-id: organizing-pages-for-staging\n---\n\n# Organizing Pages for Staging\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/page-versioning.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nSay you're working on a product-oriented Site where several major changes are\nrequired for a page or a set of pages over a short period of time. You must work\non multiple versions of the Site at the same time to ensure everything has been\nproperly reviewed before it goes live. With staging, you can do this using *Page\nVariations*.\n\nIn this section, you'll explore page variations and how they're useful.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/07-organizing-pages-for-staging/02-using-multi-and-single-page-variations.markdown",
    "content": "---\nheader-id: using-multi-and-single-page-variations\n---\n\n# Using Multi and Single Page Variations\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/page-versioning.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThere are two page variation options available from the\n[Staging Bar](/docs/7-2/user/-/knowledge_base/u/using-the-staging-environment):\n\n- *Site Pages Variation:* Different variations for a set of Site pages. For\n  instance, you could use this if you had three separate pages and wanted to\n  modify these pages while keeping them together as a set.\n- *Page Variations:* Different variations for a single page. A page variation\n  can only exist inside a Site pages variation.\n\nYou must enable page versioning in the Staging Configuration menu before the\nabove options are available in the Staging Bar. You'll learn more about this\nlater.\n\nVariations only affect pages and not the content, which means all the existing\ncontent in your staging Site is shared by all your variations. The content,\nhowever, can be displayed in many different ways for each page variation. For\nexample, in different Site page variations, you can have different logos, a\ndifferent look and feel for your pages, different applications on these pages,\ndifferent configuration of these applications, and even different pages. One\npage can exist in just one Site page variation or in several of them. Modifying\nthe layout type (e.g., Layout, Panel, Embedded, etc.) or friendly URL of a page,\nhowever, **does** affect every Site page variation. For example, if a page\ntemplate is modified, those modifications are propagated to the pages configured\nto inherit changes from the template, overriding Staging's Page Variations and\nSite Pages Variations.\n\n| **Note:** Page templates are not recognized by the Staging framework. This\n| means that existing page templates are not viewable or editable on a staged\n| Site. If they're created on a staged Site, they cannot be preserved once\n| staging is disabled. You can, however, export and import page templates.\n\nYou'll learn about enabling page versioning next.\n\n## Enabling Page Versioning\n\nPage Versioning is enabled on the Staging Configuration screen when first\n[enabling staging](/docs/7-2/user/-/knowledge_base/u/enabling-staging).\n\n![Figure 1: You can enable page versioning for public and/or private pages.](../../../../images/page-versioning.png)\n\nYou can enable page versioning for public and private pages. When page\nversioning is enabled, the page variation options are available in the Staging\nBar. By default, you only have one Site pages variation and page variation which\nare both called *Main Variation*. \n\nIf you did not enable page versioning during the initial setup of your staging\nenvironment, navigate to the Product Menu &rarr; *Publishing* &rarr; *Staging*\n&rarr; *Options* (![Options](../../../../images/icon-options.png)) &rarr;\n*Staging Configuration*. You can enable the page versioning options there.\n\n## Using Site Pages Variations\n\nSite pages variations are useful when you must plan multiple page sets for your\nSite at once. For example, consider this scenario:\n\nIf there were separate teams in your company that needed to create three\ndrastically different page sets for your Site at the same time, they would need\nto create three Site pages variations. For example,\n\n- The marketing team can give your Site a completely different look and feel for\n  the Holidays.\n- The product management team can work on a version that is planned to publish\n  on the first of the year for a new product launch.\n- The developer relations team can work on a version that is planned to publish\n  on the upcoming Hack-a-thon day.\n\nWith this use case, having a Site pages variation for each planned page set\nallows three ideas to be fully planned and implemented before publication.\n\nAnother option for this scenario is to let the product management team\nexperiment with two different ideas for the home page of the Site. This can be\ndone by creating several page variations within the current Site pages variation\nof their product launch Site. You'll learn more about page variations later.\n\nOnce you've\n[created a Site pages variation](/docs/7-2/user/-/knowledge_base/u/creating-multi-and-single-page-variations),\nyou can now navigate to its home page and change the logo, apply a new theme,\nmove applications around, change the order of the pages, configure different\napps, and more. The other variations aren't affected. You can even delete\nexisting pages or add new ones (remember to *Mark as Ready for Publication* when\nyou are finished with your changes). When you delete a page, it is deleted only\nin the current variation. The same happens when you add a new page. If you try\nto access a page that was deleted in the current variation, Liferay informs you\nthis page is not *enabled* in this variation and you must enable it. \n\n![Figure 2: Select the *Enable* button to create a missing page in the current Site pages variation.](../../../../images/enable-unavailable-page.png)\n\nTo publish a variation to the live Site, click on *Publish to Live* in the\nstaging menu and then select *Publish to Live*. Publications can also be\nscheduled independently for different variations. For example, you could have a\nvariation called *Mondays* which is published to the live Site every Monday and\nanother one called *Day 1* which is published to the live Site every first day\nof each month.\n\n## Using Page Variations\n\nYou can also have variations for a single page inside a Site pages variation,\nwhich lets you work in parallel on different versions of a page. For example,\nyou might work on two different proposals for the design of the home page for a\nHolidays Site pages variation. Page variations only exist inside a Site pages\nvariation.\n\nOnce you've\n[created a page variation](/docs/7-2/user/-/knowledge_base/u/creating-multi-and-single-page-variations),\nyou can choose it from the dropdown menu on the Staging Bar. You can always\nswitch between different variations by clicking on them from the Staging Bar.\n\nOnce you've modified the page variation to the way you want, mark it as *Ready\nfor Publication*. Only one page variation can be marked as ready for publication\nand that is the one that gets published to the live Site. To publish a variation\nto the live Site, click on *Publish to Live* in the staging menu and then select\n*Publish to Live*.\n\n## Managing Variation Permissions\n\nIt's also possible to set permissions on each variation, so certain users have\naccess to manage some, but not all variations. To do this,\n\n1.  Navigate to the Staging Bar's *Options* button\n    (![Options](../../../../images/icon-staging-bar-options.png)) and select the\n    variation type you want to configure.\n\n2.  Select the desired variation's *Actions* button\n    (![Actions](../../../../images/icon-actions.png)) and select *Permissions*.\n\n3.  Configure the variation's permissions and then click *Save*.\n\n![Figure 3: Configure the roles that can access and modify your variation.](../../../../images/page-variation-permissions.png)\n\nAwesome! You now know how to manage variation permissions!\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/07-organizing-pages-for-staging/03-creating-multi-and-single-page-variations.markdown",
    "content": "---\nheader-id: creating-multi-and-single-page-variations\n---\n\n# Creating Multi and Single Page Variations\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/page-versioning.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nYou can create two types of page variations:\n\n- Site Pages Variation (multiple pages)\n- Page Variation (single page)\n\nYou can learn more about these variations in the\n[Using Multi and Single Page Variations](/docs/7-2/user/-/knowledge_base/u/using-multi-and-single-page-variations)\narticle. As an example, you'll step through creating a Site pages variation\nnext. Both variation processes, however, are similar.\n\n1.  Select the *Options* icon\n    (![Options](../../../../images/icon-staging-bar-options.png)) in the Staging\n    Bar and select the variation option. For example, select the *Site Pages\n    Variation* option. This brings you to a list of the existing Site page\n    variations for your Site.\n\n2.  Click *Add Site Pages Variation* to create a new one.\n\n    ![Figure 1: When selecting the *Site Pages Variation* link from the Staging Bar, you're able to add and manage your Site pages variations.](../../../../images/staging-page-variations.png)\n\n3.  Set a name and description for your new Site pages variation.\n\n4.  Set how you want your variation created. From the *Copy Pages from Site Page\n    Variation* field, you can copy content from an existing variation to create\n    your new one. There are several options to choose in this selector.\n\n    **All Site Pages Variations:** Creates a new variation that contains the\n    last version marked as ready for publication from any single page existing\n    in any other variation.\n\n    **None (Empty Site Pages Variation):** Creates a new, empty variation.\n\n    **[Existing Variations]:** Creates a new Site page variation that contains\n    only the last version of all the pages that exist in a specific variation\n    (e.g., *Main Variation*). The current variation must be marked as ready for\n    publication.\n\n    The copy option is not available when creating a page variation. A new page\n    variation is a copy of the current Site pages variation.\n\n5.  Click *Add* to create the Site pages variation.\n\n| **Note:** You can rename any variation after it's created, if necessary. For\n| example, edit the Main Variation and change its name to something that makes\n| more sense in your Site, such as *Basic*, *Master*, or *Regular*.\n\nAwesome! Your Site pages variation is created and available for modification.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/07-organizing-pages-for-staging/04-merging-site-pages-variations.markdown",
    "content": "---\nheader-id: merging-site-pages-variations\n---\n\n# Merging Site Pages Variations\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/page-versioning.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nAnother powerful feature of Staging's Page Versioning framework is the\npossibility of *merging* Site Pages Variations. To merge two Site Pages\nVariations, follow the instructions below.\n\n1.  Click the Staging Bar's *Options* button\n    (![Options](../../../../images/icon-staging-bar-options.png)) and select\n    *Site Pages Variation*.\n\n2.  Click the Site Pages Variation's *Actions* button\n    (![Actions](../../../../images/icon-actions.png)) you want to use as the\n    base for merging and select *Merge*.\n\n3.  Select the Site Pages Variation to merge on top of the base Site Pages\n    Variation.\n\n    ![Figure 1: Select the site pages variation you'd like to merge with your base variation.](../../../../images/merge-site-pages-variation.png)\n\n    Merging works like this:\n\n    - New pages that don't exist in the base variation are added.\n    - If a page exists in both Site Pages Variations, and at least one version\n      of the page was marked as ready for publication, the latest version marked\n      as ready is added as a new page variation in the target page of the\n      base variation. Note that older versions or page variations not marked as\n      ready for publication aren't copied. Merging can be executed, however,\n      as many times as needed and creates the needed page variations in the\n      appropriate page of the base site pages variation.\n    - Merging does not affect content and doesn't overwrite anything in the base\n      variation; it adds more versions, pages, and page variations as needed.\n\nGreat! You've merged site pages variations!\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/08-managing-staging-permissions/01-intro.markdown",
    "content": "---\nheader-id: managing-permissions\n---\n\n# Managing Permissions\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/site-building/publishing-tools/staging/managing-staging-permissions.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThe staging environment has many different options for building and managing\na Site and its pages. Sometimes administrators want to limit access to staging's\nsubset of powerful features. A Role with permissions can accomplish this. To\ncreate/modify a Role, complete the following steps:\n\n1.  Navigate to the *Control Panel* &rarr; *Users* &rarr; *Roles*.\n\n2.  To create a new Role, select the *Add* button\n    (![Add](../../../../images/icon-add.png)) and complete the New Role menu.\n    Once you have a new Role created, or you've decided on the Role you want to\n    modify, select the Role's *Actions* icon\n    (![Actions](../../../../images/icon-actions.png)) and select *Edit*.\n\n3. From the top menu, select *Define Permissions*.\n\nThe most obvious permissions for staging are the general permissions that look\nsimilar to the permissions for most Liferay apps. These permissions are in the\n*Site Administration* &rarr; *Publishing* &rarr; *Staging* section of the Define\nPermissions menu. They include\n\n- *Access in Site Administration*\n- *Add to Page*\n- *Configuration*\n- *Permissions*\n- *Preferences*\n- *View*\n\nAlso, there are some Site resource permissions that deal directly with staging.\nThese permissions are in the *Control Panel* &rarr; *Sites* &rarr; *Sites*\nsection in the Define Permissions menu. The relevant Site resource permissions\nrelated to staging are listed below:\n\n**Add Page Variation:** Hides/shows the *Add Page Variation* button on the\nStaging Bar's Manage Page Variations screen.\n\n**Add Site Pages Variation:** Hides/shows the *Add Site Pages Variation* button\non the Staging Bar's Manage Site Page Variations screen.\n\n**Export/Import Application Info:** If the Publish Staging permission is not\ngranted, hides/shows the application level Export/Import menu. The Configuration\npermission for the Export/Import app is also required.\n\n**Export/Import Pages:** If the Publish Staging permission is not granted,\nhides/shows the Export/Import app in the Site Administration menu.\n\n**Manage Staging:** Hides/shows the Staging Configuration menu in the\nSite Administration &rarr; *Publishing* &rarr; *Staging* &rarr; *Options*\n(![Options](../../../../images/icon-options.png)) menu.\n\n**Publish Application Info:** Hides/shows the application level Staging menu.\n\n**Publish Staging:** Hides/shows the *Publish to Live* button on the Staging Bar\nand hides/shows the *Add Staging Process* button\n(![Add](../../../../images/icon-add.png)) in the Site Administration menu's\nStaging app. This permission automatically applies the *Export/Import\nApplication Info*, *Export/Import Pages*, and *Publish Application Info*\npermission functionality regardless of whether they're unselected. \n\n**View Staging:** If Publish Staging, Manage Pages, Manage Staging, or Update\npermissions are not granted, hides/shows the Site Administration menu's Staging\napp.\n\nNotice that some of the permissions above are related to the export/import\nfunctionality. Since these permissions are directly affected by the Publish\nStaging permission, they are important to note. Visit the\n[Importing/Exporting Pages and Content](/docs/7-2/user/-/knowledge_base/u/importing-exporting-pages-and-content)\nsection for more details on importing/exporting Site and page content.\n"
  },
  {
    "path": "en/user/articles/170-content-publication-management/02-staging/09-scheduling-web-content-publication/00-scheduling-web-content-publication-intro.markdown",
    "content": "---\nheader-id: scheduling-web-content-publication\n---\n\n# Scheduling Web Content Publication\n\n[TOC levels=1-4]\n\nLiferay's Web Content framework lets you define when your content goes live. You\ncan determine when the content is displayed, expired, and/or reviewed. This is\nan excellent way to keep your Site current and free from outdated (and perhaps\nincorrect) information.\n\nThe scheduler is built right into the Properties menu your users\naccess when adding web content. To access this menu, click the *Options* gear\n(![Options](../../../../images/icon-gear.png)) and open the Schedule dropdown\nmenu.\n\n![Figure 1: The web content scheduler can be easily accessed from the right panel of the page.](../../../../images/web-content-schedule.png)\n\nThe scheduler offers several configurable options:\n\n**Display Date:** Sets (within a minute) when content will be displayed.\n\n**Expiration Date:** Sets a date to expire the content. The default is one year.\n\n**Never Expire:** Sets your content to never expire.\n\n**Review Date:** Sets a content review date.\n\n**Never Review:** Sets the content to never be reviewed.\n\nAs an example, you'll step through the process of scheduling a web content\narticle.\n\n1.  Navigate to the Product Menu &rarr; *Content & Data* &rarr; *Web Content*.\n\n2.  Create a new web content article by selecting the *Add Web Content* button\n    (![Add](../../../../images/icon-add.png)) &rarr; *Basic Web Content*.\n\n3.  Add content for your web content article.\n\n4.  Select the *Schedule* tab from the web content's Properties menu. Configure\n    the publication schedule.\n\n5.  Click *Publish*. Your web content article is now created and abides by\n    the scheduling parameters you've set.\n    \nWhen you set a Display Date for an existing article it does not affect previous \nversions of the article. If a previous version is published, it remains the \nsame until the new version is scheduled to display. However, the expiration \ndate affects all versions of the article. Once an article has expired, no \nversion of that article appears.\n\n| **Tip:** If you want only the latest version of articles to expire, and not\n| every past version, go to *Control Panel* &rarr; *Configuration* &rarr;\n| *System Settings* &rarr; *Web Content* &rarr; *Virtual Instance Scope* &rarr;\n| *Web Content* and uncheck *Expire All Article Versions Enabled*. This makes\n| the previously approved version of an article appear if the latest version\n| expires.\n\nThe scheduling feature gives you great control in managing when, and for how\nlong, your web content is displayed on your Site. Additionally, you can\ndetermine when your content should be reviewed for accuracy and/or relevance.\nThis makes it possible to manage your growing inventory of content.\n"
  },
  {
    "path": "en/user/articles/200-managing-apps/01-managing-apps-intro.markdown",
    "content": "---\nheader-id: managing-apps\n---\n\n# Managing Apps\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/installing-and-managing-apps/getting-started/installing-and-managing-apps.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nApps run on the platform @product@ provides. The web experience management,\ncollaboration, and business productivity features all consist of apps. Even the\nControl Panel consists of configuration apps. You can also add to or change\nbuilt-in functionality by installing other apps. There are several ways to\nmanage, find, and install apps. This section covers these topics and more.\n\n"
  },
  {
    "path": "en/user/articles/200-managing-apps/02-managing-and-configuring-apps.markdown",
    "content": "---\nheader-id: managing-and-configuring-apps\n---\n\n# Managing and Configuring Apps\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/installing-and-managing-apps/managing-apps.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\n@product@ is a platform for deploying apps that comprise modules and components.\nIt has conveniences for managing apps and app management best practices for\nmaximizing stability. Best practices in production environments involve stopping\nthe server before applying changes, but in cases where this isn't feasible, you\ncan \"auto deploy\" changes several different ways.\n\nThere are two places in the Control Panel where you can manage and configure\napps: the *App Manager* and the *Components* listing. The App Manager manages\napps in the OSGi framework. You can use the App Manager to install, activate,\ndeactivate, and delete apps. You can manage apps at the app and module levels.\n\nThe Components listing views and manages apps at the OSGi component level. It\ndiffers from the App Manager by showing apps by type (portlet, theme, and layout\ntemplate), and setting app permissions. You can use the Components listing to\nactivate and deactivate apps, but it can't install or delete apps.\n\nStart with learning app management best practices in production, or wherever you\nwant to maximize stability.\n\n## Managing Apps in Production\n\nNot all apps are designed to be \"auto deployed\"---deployed while the server is\nrunning. Deploying that way can cause instabilities, such as class loading leaks\nand memory leaks. On production systems, avoid \"auto deploying\" apps and\nconfigurations whenever possible.\n\nIf you're installing an app or a component configuration on a production system\nand stopping the server is feasible, follow these steps:\n\n1.  Stop your server.\n\n2.  Copy your app (`.lpkg`, module `.jar`, or plugin `.war`) to your `[Liferay\n    Home]/deploy` folder, or copy your component configuration (`.config` file)\n    to the `[Liferay Home]/osgi/configs` folder. The [Liferay\n    Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) folder is typically\n    the app server's parent folder.\n\n3.  Start your server.\n\nIf you're running in cluster, follow the instructions for\n[updating a cluster](/docs/7-2/deploy/-/knowledge_base/d/updating-a-cluster).\n\n| **Warning:** Avoid repeatedly \"auto deploying\" new versions of apps that\n| aren't designed for \"auto deployment\".\n\nIf it's not feasible to stop your server or you're app *is* designed for \"auto\ndeployment\", @product@ provides several \"auto deployment\" conveniences. Except\nwhere stopping/starting the server is explicitly mentioned, the practices\ndescribed in the rest of this article and in the following articles involve\n\"auto deployment\".\n\n## Using the App Manager\n\nAccess the App Manager by selecting *Control Panel* &rarr; *Apps* &rarr; *App\nManager*. The App Manager lists your apps. The *Filter and Order* menu lets you\nfilter and order by category, status, or title. Click the up or down arrows to\nperform an ascending or descending sort, respectively. To search for an app or\nmodule, use the search bar. This is often the quickest way to find something.\n\n![Figure 1: The App Manager lets you manage the apps, modules, and components installed in your @product@ instance.](../../images/app-manager.png)\n\nEach item listed in the table contains a description (if available), version,\nand status. Here are the statuses:\n\n-   **Installed:** The item is installed to @product@.\n-   **Resolved:** The item's dependencies are active. Resolved items can\n    typically be activated. Some items, however, can't be  activated and are\n    intended to remain in the Resolved state (e.g., WSDD modules containing\n    SOAP web services).\n-   **Active:** The item is running in @product@.\n\nClicking each item's Actions button (![Actions](../../images/icon-actions.png))\nbrings up a menu that lets you activate, deactivate, or uninstall that item.\n\nTo view an item's contents, click its name in the table. If you click an app,\nthe app's modules are listed. If you click a module, the module's components and\nportlets appear. The component level is as far down as you can go without\ngetting into the source code. At any level in the App Manager, a link trail\nappears that lets you navigate back in the hierarchy.\n\nFor information on using the App Manager to install an app, see  [Installing\nApps Manually](/docs/7-2/user/-/knowledge_base/u/installing-apps-manually).\n\nNext, you'll learn how to use the Components listing.\n\n## Using the Components Listing\n\nAccess the components listing by selecting *Control Panel* &rarr;\n*Configuration* &rarr; *Components*. The components listing first shows a table\ncontaining a list of installed portlets. Select the type of component to\nview---portlets, themes, or layout templates---by clicking the matching tab on\ntop of the table. To configure a component, select its name in the table or\nselect *Edit* from its Actions button\n(![Actions](../../images/icon-actions.png)). Doing either opens the same\nconfiguration screen.\n\n![Figure 2: The components listing lets you manage the portlets, themes, and layout templates installed in your @product@ instance.](../../images/components-list.png)\n\nThe configuration screen lets you view a component's module ID and plugin ID,\nactivate or deactivate the component, and change the component's Add to Page\npermission. The component's module ID and plugin ID appear at the top of the\nscreen. You can activate or deactivate a component by checking or unchecking the\n*Active* checkbox, respectively. To change a component's Add to Page permission\nfor a role, select the role's *Change* button in the permissions table. This\ntakes you to *Control Panel* &rarr; *Users* &rarr; *Roles*, where you can change\nthe component's permissions for the selected role.\n\n![Figure 3: You can activate or deactivate a component, and change its permissions.](../../images/components-configuration.png)\n"
  },
  {
    "path": "en/user/articles/200-managing-apps/03-using-the-liferay-marketplace.markdown",
    "content": "---\nheader-id: using-the-liferay-marketplace\n---\n\n# Using the Liferay Marketplace\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/installing-and-managing-apps/getting-started/using-marketplace.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nLiferay Marketplace is a hub for sharing, browsing, and downloading apps.\nMarketplace leverages the entire Liferay ecosystem to release and share apps in\na user-friendly, one-stop shop.\n\nThere are two ways to access the Marketplace.\n\n1.  **Via the website:** Using your favorite browser, navigate to the\n    Marketplace at\n    [web.liferay.com/marketplace](https://web.liferay.com/marketplace).\n    If you're new to Marketplace, this is the easiest way to access it. You can\n    browse Marketplace without signing in with your liferay.com account.\n\n2.  **Via the Control Panel:** In the Control Panel, navigate to\n    *Apps* &rarr; *Store*. To view Marketplace, you must sign in with your\n    liferay.com account.\n\nNo matter how you access Marketplace, you'll see the same content. Note that to\ndownload apps, you must have a [liferay.com](https://www.liferay.com) account\nand agree to the Marketplace Terms of Use.\n\nHere you'll learn how to,\n\n-   [Find and purchase apps](#finding-and-purchasing-apps)\n\n-   [Manage purchased apps](#managing-purchased-apps)\n\n-   [Renew purchased apps](#renewing-a-purchased-app)\n\nStart with finding and purchasing the apps you want.\n\n## Finding and Purchasing Apps\n\nIf you've used an app store before, Marketplace should be familiar. You'll see\napps in the center of the page, in the following sections:\n\n-   Featured Apps: Liferay features a different set of apps each month.\n\n-   New and Interesting: The latest apps added to Marketplace.\n\n-   Most Viewed in the Past Month: The top 5 most viewed apps in the last month.\n\n-   Themes / Site Templates: Apps that change your Liferay instance's look and\n    feel.\n\n-   App categories: Communication, productivity, security, etc.\n\n-   Weekly Stats: The newest apps, latest apps updated, and trend chart for app\n    downloads and views.\n\nEach section's *See All* link shows more section info. At the top of the\npage, you can search Marketplace by category, @product@ version, and price. To\nbrowse by category, click the *Categories* menu at the top of the page.\n\n![Figure 1: The Liferay Marketplace home page lets you browse and search for apps.](../../images/marketplace-homepage.png)\n\nClick an app to view its details. This includes its description, screenshots,\nprice, latest version, number of downloads, a link to the developer's website, a\nlink to the app's license agreement, and a purchase button (labeled Free or Buy,\ndepending on the price). You can also view the app's version history, read\nreviews, or write your own review.\n\nThe purchase button prompts you to choose a purchase type. You can purchase an\napp for your personal account, or for a Liferay project associated with your\ncompany. If you have the necessary permissions, you can also create a new\nproject for your company. Once you select a purchase type, accept the EULA and\nTerms of Service, and click *Purchase*.\n\n![Figure 2: Click an app to view its details.](../../images/marketplace-app-details.png)\n\nOnce you purchase an app, you can download and install it.\n\n| **Warning:** Not all apps are designed to be \"auto deployed\"---deployed while\n| the server is running. Deploying that way can cause instabilities, such as\n| class loading leaks and memory leaks. On production systems, avoid \"auto\n| deploying\" apps whenever possible. See the\n| [best practices for managing apps in production](/docs/7-2/user/-/knowledge_base/u/managing-and-configuring-apps#managing-apps-in-production).\n\nAn app downloads and installs immediately if you purchase it from the Control\nPanel. If you purchase the app on the Marketplace website, however, your receipt\nis displayed immediately after purchase. To download the app, click the *See\nPurchased* button on the bottom of the receipt, and then click the *App* button\nto start the download. You must then [install the app\nmanually](/docs/7-2/user/-/knowledge_base/u/installing-apps-manually).\nAlternatively, you can use Marketplace from the Control Panel to download and\ninstall the app after purchase on the Marketplace website. The next section\nshows you how to do this.\n\nNote that sometimes administrators disable automatic app installations so they\ncan manage installations manually. In this case, Marketplace apps downloaded\nfrom the Control Panel are placed in the `deploy` folder in [Liferay\nHome](/docs/7-2/deploy/-/knowledge_base/d/liferay-home). Administrators must\nthen manually install the app from this folder. Manual install is also required\nif the server is behind a corporate firewall or otherwise lacks direct\nMarketplace access. Regardless of how the app is downloaded, the manual install\nprocess is the same. For details, see the article [Installing Apps\nManually](/docs/7-2/user/-/knowledge_base/u/installing-apps-manually).\n\n## Managing Purchased Apps\n\n| **Important**: When uninstalling an app or module, make sure to use the same\n| agent you used to install the app. For example, if you installed it with\n| Marketplace, uninstall it with Marketplace. If you installed it with the file\n| system, use the\n| [file system](/docs/7-2/user/-/knowledge_base/u/installing-apps-manually)\n| to uninstall it. If you installed it with the App Manager, however, use\n| [Blacklisting](/docs/7-2/user/-/knowledge_base/u/blacklisting-osgi-bundles-and-components)\n| to uninstall it.\n\nThere are two places to manage your purchased apps:\n\n1.  Your [liferay.com](https://www.liferay.com) account's home page. After\n    signing in, click the user menu at the top-right and select *Account Home*.\n    Note that your home page is distinct from your profile page. Your home page\n    is private, while your profile page is public. On your home page, select\n    *Apps* from the menu on the left to view your projects. Select a project to\n    view its registered apps. Clicking an app lets you view its versions. You\n    can download the version of the app that you need. This is especially useful\n    if you need a previous version of the app, or can't download the app from\n    the Control Panel.\n\n    ![Figure 3: You can manage your purchased apps from your liferay.com account's home page.](../../images/marketplace-project-apps.png)\n\n2.  From the Control Panel. Navigate to *Apps* &rarr; *Purchased* to see your\n    purchased apps. A button next to each app lets you install or uninstall the\n    app. If the app isn't compatible with your @product@ version, *Not\n    Compatible* is displayed in place of the button. Additional compatibility\n    notes are also shown, such as whether a newer version of the app is\n    available. You can also search for an app here by project, category, and\n    title. Clicking the app takes you to its Marketplace entry.\n\n    ![Figure 4: You can also manage your purchased apps from within a running Liferay instance.](../../images/marketplace-purchased.png)\n\n## Renewing a Purchased App\n\nTo continue using a purchased app whose license terms are non-perpetual, you\nmust renew your app subscription, register your server to use the app, and\ngenerate a new activation key to use on your server. Here are the steps:\n\n1.  Go to\n    [https://web.liferay.com/marketplace](https://web.liferay.com/marketplace).\n\n2.  Click your profile picture in the upper right corner and select *Purchased\n    Apps*. The Purchased Apps page appears and shows your app icons organized by\n    project.\n\n3.  Click your app's icon. Your app's details page appears.\n\n4.  Click *Manage Licenses*.\n\n5.  Select *Register New Server*.\n\n6.  Select the most recent *Order ID* (typically the order that has no\n    registered servers).\n\n7.  Fill in your server's details.\n\n8.  Click *Register*.\n\n9.  Click *Download*. The new app activation key to use on your server\n    downloads.\n\n10. Copy the activation key file to your `deploy/` folder in your [`[Liferay\n    Home]`](/docs/7-2/deploy/-/knowledge_base/d/liferay-home).\n\nYou can continue using the application on your server.\n"
  },
  {
    "path": "en/user/articles/200-managing-apps/04-installing-apps-manually.markdown",
    "content": "---\nheader-id: installing-apps-manually\n---\n\n# Installing Apps Manually\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/installing-and-managing-apps/installing-apps.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nBy default, apps you download from the Control Panel via Liferay Marketplace\ninstall automatically. But what if the app you want to install isn't on\nMarketplace? What if all you have is the app's file? In this case, you must\ninstall the app manually. Here you'll learn how to install any app manually.\n\n| **Warning:** Not all apps are designed to be \"auto deployed\"---deployed while\n| the server is running. Deploying that way can cause instabilities, such as\n| class loading leaks and memory leaks. On production systems, avoid \"auto\n| deploying\" apps whenever possible. See the\n| [best practices for managing apps in production](/docs/7-2/user/-/knowledge_base/u/managing-and-configuring-apps#managing-apps-in-production).\n\n| **Important**: When uninstalling an app or module, make sure to use the same\n| agent you used to install the app. For example, if you installed it with\n| Marketplace, uninstall it with\n| [Marketplace](/docs/7-2/user/-/knowledge_base/u/using-the-liferay-marketplace).\n| If you installed it with the file system, use the file system to uninstall it.\n| If you installed it with the App Manager, however, use\n| [Blacklisting](/docs/7-2/user/-/knowledge_base/u/blacklisting-osgi-bundles-and-components)\n| to uninstall it.\n\n## Using the Control Panel to Install Apps\n\nTo install an app manually from the Control Panel, navigate to *Control Panel*\n&rarr; *Apps* &rarr; *App Manager*, and select *Upload* from the options button\n(![Options](../../images/icon-options.png)). In the Upload dialog, choose the\napp on your machine and then click *Install*. When the install completes, close\nthe dialog and you're ready to roll!\n\n## Using Your File System to Install Apps\n\nTo install an app manually on the @product@ server, put the app in the `[Liferay\nHome]/deploy` folder (the [Liferay\nHome](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) folder is typically the\napp server's parent folder). That's it. The auto deploy mechanism takes care of\nthe rest.\n\nYou might now be thinking, \"Whoa there! What do you mean by 'the rest?' What\nexactly happens here? And what if my app server doesn't support auto deploy?\"\nThese are fantastic questions! When you put an app in the `[Liferay\nHome]/deploy` folder, the OSGi container deploys the app to the appropriate\nsubfolder in `[Liferay Home]/osgi`. By default, the following subfolders are\nused for apps matching the indicated file type:\n\n-   `marketplace`: Marketplace LPKG packages\n-   `modules`: OSGi modules\n-   `war`: WAR files\n\nYou can, however, change these subfolders by setting the properties\n`module.framework.base.dir` and `module.framework.auto.deploy.dirs` in a\n[`portal-ext.properties`](/docs/7-2/deploy/-/knowledge_base/d/portal-properties)\nfile. These properties define the `[Liferay Home]/osgi`  folder and its auto\ndeploy subfolders, respectively. The default settings for  these properties in\nthe\n[`portal.properties`](@platform-ref@/7.2-latest/propertiesdoc/portal.properties.html)\nfile are as follows:\n\n```properties\nmodule.framework.base.dir=${liferay.home}/osgi\n\nmodule.framework.auto.deploy.dirs=\\\n    ${module.framework.base.dir}/configs,\\\n    ${module.framework.base.dir}/marketplace,\\\n    ${module.framework.base.dir}/modules,\\\n    ${module.framework.base.dir}/war\n```\n\nNote that the `configs` subfolder isn't for apps: it's for configuration files\n[imported from other @product@ instances](/docs/7-2/user/-/knowledge_base/u/system-settings#exporting-and-importing-configurations).\n\nBut what happens if your app server doesn't support \"hot deploy\"? No problem!\n@product@'s module framework (OSGi) enables auto deploy. Any app server running\n@product@ therefore also supports this auto deploy mechanism.\n\n## Manually Deploying an LPKG App\n\nWhen manually installing an LPKG app, the installation may hang with a server\nlog message like this:\n\n```\n14:00:15,789 INFO  [com.liferay.portal.kernel.deploy.auto.AutoDeployScanner][AutoDeployDir:252] Processing Liferay Push 2.1.0.lpkg\n```\n\nThis happens when LPKG apps have the `restart-required=true` property in their\n`liferay-marketplace.properties` file (inside the LPKG file). This property\nsetting specifies that a server restart is required to complete the\ninstallation.\n"
  },
  {
    "path": "en/user/articles/200-managing-apps/05-app-types.markdown",
    "content": "---\nheader-id: app-types\n---\n\n# App Types\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/installing-and-managing-apps/getting-started/installing-and-managing-apps.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nThere are several different kinds of apps. Some apps can even contain other\napps. The types of apps you can install include:\n\n-   OSGi Modules\n-   Portlets\n-   Web Plugins\n-   Templates\n-   Themes\n\nRead on to learn about these app types.\n\n## OSGi Modules\n\nSince @product@ runs on OSGi, apps can be implemented as OSGi modules. An OSGi\nmodule is a JAR file adapted to run on OSGi. Although it's possible for a single\nmodule to implement a single app, an app typically consists of multiple modules\nthat are packaged together. Also note that apps in OSGi modules aren't required\nto have a UI. For example, @product@ can run OSGi modules that expand built-in\nAPIs without requiring any user interaction. This is crucial for developers that\nmust leverage custom APIs. By providing such an API via one or more OSGi\nmodules, you can let developers leverage your API.\n\nOSGi modules can also contain apps that have a UI: portlets. The next section\ndiscusses these.\n\n## Portlets\n\n[Portlets](/docs/7-2/frameworks/-/knowledge_base/f/portlets) are small web\napplications that run in a portion of a web page. For example, the built-in\nBlogs app is a portlet. Portlet applications, like servlet applications, are a\nJava standard implemented by various portal server vendors. The JSR-168 standard\ndefines the portlet 1.0 specification, the JSR-286 standard defines the portlet\n2.0 specification, and the JSR-362 standard defines the portlet 3.0\nspecification. A Java standard portlet should be deployable on any portlet\ncontainer that supports the standard. Portlets are placed on the page in a\ncertain order by the end user and are served up dynamically by the portal\nserver. This means certain things that apply to servlet-based projects, such as\ncontrol over URLs or access to the `HttpServletRequest` object, don't apply in\nportlet projects because the portal server generates these objects dynamically.\n\nPortlets can be composed of OSGi modules (recommended), or contained in WAR\nfiles. For information on developing portlets see [Web\nFront-ends](/docs/7-2/appdev/-/knowledge_base/a/web-front-ends).\n\n## Web Plugins\n\nWeb plugins are regular Java EE web modules designed to work with @product@. You\ncan integrate with various Enterprise Service Bus (ESB) implementations, as well\nas Single Sign-On implementations, workflow engines, and so on. These are\nimplemented as web modules used by @product@ portlets to provide functionality.\n\n## Templates and Themes\n\n[Templates](/docs/7-2/frameworks/-/knowledge_base/f/layout-templates-intro) and\n[themes](/docs/7-2/frameworks/-/knowledge_base/f/themes-introduction) are\nplugins that change @product@'s appearance. Templates (layout templates) control\nhow you can arrange portlets on a page. They make up a page's body (the large\narea into which you can drag and drop portlets). There are several built-in\nlayout templates. If you have a complex page layout (especially for your home\npage), you may wish to create a custom layout template of your own.\n\nThemes can completely transform @product@'s look and feel. Most organizations\nhave their own look and feel standards that apply to all of their web sites and\napplications. By using a theme plugin, an organization can apply these standards\non @product@. There are many available theme plugins on Liferay's web site and\nmore are being added every day. This makes it easy for theme developers, as they\ncan customize existing themes instead of writing a new one from scratch.\n\n## Liferay Marketplace App Packages\n\nRegardless of app type, each [Liferay\nMarketplace](https://web.liferay.com/marketplace) app is distributed in an LPKG\npackage. The LPKG package contains Marketplace  metadata and the files the app\nneeds to run. Note that it's possible for an LPKG  package to contain multiple\napps. For example, a single LPKG package can contain  several portlets. This is\ncommon in cases where an app requires a Control Panel portlet for\nadministrators, and another portlet for end users.\n"
  },
  {
    "path": "en/user/articles/200-managing-apps/06-blacklisting-osgi-bundles-and-components.markdown",
    "content": "---\nheader-id: blacklisting-osgi-bundles-and-components\n---\n\n# Blacklisting OSGi Bundles and Components\n\n[TOC levels=1-4]\n\n<aside class=\"alert alert-info\">\n  <span class=\"wysiwyg-color-blue120\">This document has been updated and ported to <a href=\"https://learn.liferay.com/dxp/latest/en/system-administration/installing-and-managing-apps/managing-apps/blacklisting-apps.html\">Liferay Learn</a> and is no longer maintained here.</span>\n</aside>\n\nBlacklists are used for good and evil. An evil blacklist penalizes unfairly; a\ngood blacklist protects. @product@'s OSGi bundle and component blacklists are\nfiles that prevent particular bundles from installing and particular components\nfrom enabling. This saves you the trouble of uninstalling and disabling them\nindividually with the Application Manager, Components list, or Gogo shell.\n\n## Blacklisting Bundles\n\n@product@ removes any installed OSGi bundles on the blacklist. Blacklisted bundles\ntherefore can't be installed. The log reports each bundle uninstallation.\n\nFollow these steps to blacklist bundles:\n\n1.  In the Control Panel, navigate to *Configuration* &rarr; *System Settings*\n    &rarr; *Module Container*.\n\n2.  In the Bundle Blacklist screen, add the bundle symbolic names (see the table\n    below) for the Module JARs, LPKG files, or WARs to uninstall. Click the\n    *Save* button when you're finished.\n\n    ![Figure 1: This blacklist uninstalls the `com.liferay.docs.greeting.api` bundle, Liferay Marketplace LPKG, and `classic-theme` WAR.](../../images/bundle-blacklist-configuration.png)\n\n3.  To export the blacklist, click its Actions button\n    (![Actions](../../images/icon-actions.png)) and then click *Export*. The\n    blacklist configuration file then downloads\n    (`com.liferay.portal.bundle.blacklist.internal.BundleBlacklistConfiguration.config`).\n    Here are contents from an example file:\n\n    ```properties\n    blacklistBundleSymbolicNames=[\"com.liferay.docs.greeting.api\",\"Liferay\\ Marketplace\",\"classic-theme\"]\n    ```\n\n4.  Add the bundle symbolic names of any bundles not already listed that you\n    want to prevent from installing.\n\n    **Important**: Configuration values can't contain extra spaces. Extra spaces\n    can short-circuit lists or invalidate the configuration entry.\n\n5.  To deploy the configuration file, copy it into the folder\n    `[Liferay_Home]/osgi/configs`. The [Liferay\n    Home](/docs/7-2/deploy/-/knowledge_base/d/liferay-home) folder is typically\n    the app server's parent folder.\n\n| **Note**: Blacklisting an LPKG uninstalls all of its internal bundles.\n\n**Blacklist Bundle Symbolic Names**\n\n| Type       | Bundle Symbolic Name |\n| ---------- | --------------|\n| Module JAR | `Bundle-SymbolicName` in `bnd.bnd` or `MANIFEST.MF` file |\n| LPKG       | LPKG file name without the `.lpkg` extension |\n| WAR        | Servlet context name in `liferay-plugin-package.properties` file or the WAR file name (minus `.war`), if there is no servlet context name property |\n\n## Reinstalling Blacklisted Bundles\n\nTo reinstall and permit installation of blacklisted OSGi bundles, follow these steps:\n\n1.  Open the configuration file\n    `com.liferay.portal.bundle.blacklist.internal.BundleBlacklistConfiguration.config`.\n\n2.  Remove the symbolic names of the module JARs, LPKGs, or WARs from the\n    `blacklistBundleSymbolicNames` list and save the file.\n\nTo reinstall *all* the blacklisted bundles execute one of these options:\n\n-   Remove the configuration file.\n-   Uninstall the bundle `com.liferay.portal.bundle.blacklist` using the\n    [Application Manager](/docs/7-2/user/-/knowledge_base/u/managing-and-configuring-apps#using-the-app-manager)\n    or\n    [Felix Gogo Shell](/docs/7-2/customization/-/knowledge_base/c/using-the-felix-gogo-shell).\n\n| **Note**: To temporarily reinstall a bundle that's been blacklisted, you can\n| remove its symbolic name from the Bundle Blacklist module in *System Settings*\n| and click the *Update* button. If you want the bundle to install on subsequent\n| server startup, make sure to remove the bundle's symbolic name from any\n| existing blacklist configuration file in the\n| `[Liferay_Home]/osgi/configs` folder.\n\nThe log reports each bundle installation.\n\n## Blacklisting Components\n\nFollow these steps to blacklist components:\n\n1.  In the Control Panel, navigate to *Configuration* &rarr; *System Settings*\n    &rarr; *Module Container*.\n\n2.  In the Component Blacklist screen, add the names of components to disable\n    and click the *Save* button.\n\n![Figure 2: This blacklist disables the components `com.liferay.portal.security.ldap.internal.authenticator.LDAPAuth` and `com.liferay.ip.geocoder.sample.web.internal.portlet.IPGeocoderSamplePortlet`.](../../images/component-blacklist-configuration.png)\n\n3.  To export the blacklist, click on the Component Blacklist module's Actions\n    button (![Actions](../../images/icon-actions.png)) and then click *Export*.\n    The blacklist configuration file then downloads\n    (`com.liferay.portal.component.blacklist.internal.ComponentBlacklistConfiguration.config`).\n    Here are contents from an example file:\n\n    ```properties\n    blacklistComponentNames=[\"com.liferay.portal.security.ldap.internal.authenticator.LDAPAuth\",\"com.liferay.ip.geocoder.sample.web.internal.portlet.IPGeocoderSamplePortlet \"]\n    ```\n\n4.  Add the names of any components not already listed (e.g., components of\n    bundles not yet installed) that you want to prevent from activating.\n\n    **Important**: Configuration values can't contain extra spaces. Extra spaces\n    can short-circuit lists or invalidate the configuration entry.\n\n5.  To deploy the configuration file, copy it into the folder\n    `[Liferay_Home]/osgi/configs`. The Liferay Home folder is typically the app\n    server's parent folder.\n\n## Re-enabling Blacklisted Components\n\nTo re-enable and permit enabling of blacklisted components, follow these steps:\n\n1.  Open the configuration file\n    `com.liferay.portal.component.blacklist.internal.ComponentBlacklistConfiguration.config`.\n\n2.  Remove the names of the components from the `blacklistComponentNames` list\n    and save the file.\n\nTo enable *all* the blacklisted components, remove the configuration file.\n\n| **Note**: To temporarily reactivate a blacklisted component, remove its name\n| from the Component Blacklist Configuration module in System Settings and click\n| *Update*. If you want the component to activate on subsequent server startup,\n| make sure to remove the component's name from any existing component blacklist\n| configuration file in the `[Liferay_Home]/osgi/configs` folder.\n"
  },
  {
    "path": "en/user/articles/220-polls.markdown",
    "content": "---\nheader-id: polls\n---\n\n# Polls\n\n[TOC levels=1-4]\n\nHow can The Lunar Resort stay connected with its earthbound clientèle from\n239,000 miles away? Make them feel really involved and enthusiastic about the\nresort by asking them for feedback. You're not just creating a poll, you're\nmaking connections.\n\nUse Polls to find out what your site visitors are thinking and keep them engaged\nwith your site's content.\n\nTwo applications make and display a poll: the *Polls* application in the Site\nMenu and the *Polls Display* widget you add to a page.\n\n## Creating a Poll\n\nFrom the Site Menu, go to *Content & Data* &rarr; *Polls*. \n\n1.  Click the ![Add](../images/icon-add.png) button and fill out the form. \n\n    ![Figure 1: Besides the Title and the Polls Question, you must enter data for each of the Choices fields when creating a new poll.](../images/polls-add-new-question.png)\n\n    **Title:** (Required) Enter the name of the poll question. \n\n    **Polls Question:** (Required) Enter the text of the poll question. \n\n    **Expiration Date:** Enter the date and time you want the poll to expire. \n\n    **Choices:** Enter at least two options for the poll question. \n\n    **Add Choice:** Enter additional answer options for the poll question. \n\n    **Permissions:** Manage who can view and edit the poll. \n\n2.  Click *Save* to add the poll to the Polls application. \n\nOnce a poll is created, the Polls Display portlet publishes it until it expires\nor is deleted. Set an expiration date for a poll by selecting the day and time\nin the Add Poll form. The default is set to *Never Expire*. \n\nWhen a published poll expires, the poll results are displayed, but users can't\nadd new entries. To remove an expired poll from a page, remove the Poll Display\nportlet or configure it to show another poll question. See the section below for\nmore details about the Polls Display portlet. \n\n*Permissions* are set on individual polls. Use permissions, for example, to\nallow some privileged users to vote on a certain poll question, while others can\nonly view it. \n\nCreating a poll is fairly straightforward. Next, complete the two-step\nprocess and put your poll on a page.\n\n## Adding a Poll to a Page\n\nNow that you have created your poll question, you can present it to your users:\n\n1.  Go to a page and add a Polls Display widget from *Add* &rarr; *Widgets*\n    &rarr; *Content Management*.\n\n2.  Click *Please configure this portlet to make it visible to all users.*\n\n3.  In the dialog box that appears, select the poll to display.\n\n4.  Click *Save*.\n\nOnce the poll question has been placed on the page, perform other tasks using\nthe icons at the bottom of the portlet.\n\n![Figure 2: These buttons provide shortcuts to the widget's configuration, as well as to some of the Polls Application's functionality.](../images/poll-buttons.png)\n\n**Edit Question:** Displays a similar dialog box to the one used to create the\npoll. \n\n**Select Poll:** Displays the same dialog box as Configuration, allowing you to\nchoose different polls from the drop-down menu. \n\n**Add:** Allows you to create a new poll. \n\n## Viewing Poll Results\n\nAll the polls you create appear in the Polls portlet in the Site Menu &rarr;\n*Content & Data* &rarr; *Polls*. When users vote in the poll, the data is \ncollected here. Click on a poll to see a breakdown of the results.\n\n![Figure 3: Selecting a poll in the Polls portlet puts the data at your fingertips.](../images/polls-results.png)\n\nIf you click on one of the listed *Charts*, the portlet generates an\nappropriate visualization of the data.\n\nBelow this is an item called *Charts*. This option shows the poll results\nrepresented in various graphs. The graphs are *Area*, *Horizontal Bar*, *Line*,\n*Pie*, and *Vertical Bar*.\n\n![Figure 4: This is what the vertical bar graph for the Lunar Resort poll results looks like.](../images/polls-results-vertical-bar.png)\n\nThere is also a listing of the users who voted in the poll, how they voted,\nand a time/date stamp of when their votes were cast. Registered users are\nrepresented by name. Guest users have a blank _User_ field.\n"
  },
  {
    "path": "en/user/articles/230-calendar/01-calendar-intro.markdown",
    "content": "---\nheader-id: using-the-calendar\n---\n\n# Using the Calendar\n\n[TOC levels=1-4]\n\nThe Calendar widget is an updated, digitized, 3D-printed sundial. Okay, it's\nreally a tool for storing and sharing scheduled events. It's a personal planner\nfor individual users, a shared calendar for an entire site, or both at the same\ntime. It can be used to create multiple calendars for a single Site or User, to\noverlay the events stored in multiple calendars for simultaneous view, to send\nemail reminders to users, and more.\n\n| **Note:** The calendar supports social activities. Whenever a calendar event\n| is added or updated, a corresponding social activity notification is created. If\n| the event was added or updated in a calendar that the current user has\n| permission to view, the social activity is viewable in the Activities widget.\n\n## Configuring the Calendar\n\nOnce the Calendar widget is on a page, open the\n![Options](../../images/icon-app-options.png) menu in the widget's header and\nclick *Configuration*.\n\n![Figure 1: The Setup &rarr; User Settings tab provides the options you need to get started quickly.](../../images/new-calendar-configuration.png)\n\nFrom the *User Settings* tab, customize the calendar's default view and\nsettings. \n\n**Time Format:** Choose *Locale*, *AM/PM*, or *24 Hour*. _Locale_ is a dynamic\nsetting that chooses whether to display the time in _AM/PM_ or _24 Hour_ format,\nbased on the preferences set by the User's locale. *AM/PM* displays times such\nas 8AM or 11PM. The *24 Hour* time format displays times such as 08:00 and\n23:00.\n\n**Default Duration:** Choose an event duration. When you add a new event\nto the calendar, the time you set here specifies how long events last by\ndefault.\n\n**Default View:** Choose *Day*, *Week*, *Month* or *Agenda*. This sets the\ndefault for when the calendar is first displayed, but the view can be changed\nby clicking the appropriate button at the top-right of the widget.\n\n**Week Starts On:** Choose *Sunday*, *Monday*, or *Saturday*. \n\n**Time Zone:** Choose a time zone or check the *Use Global Time Zone* box.\n\nIf you check *Use Global Time Zone*, the time displayed depends on whether it's\nbeing viewed by a logged-in user or a guest. If a user is logged in, the\nCalendar displays events using the time zone set for the user in *User Personal \nMenu* &rarr; *Account Settings* &rarr; *Preferences* &rarr; *Display Settings* \n&rarr; *Time Zone*. If the Calendar is viewed by a guest or a user who is not \nlogged in, the Calendar displays events using the time zone set by the portal \nadministrator in *Control Panel* &rarr; *Configuration* &rarr; \n*Instance Settings* &rarr; *Platform* &rarr; *Localization* &rarr; \n*VIRTUAL INSTANCE SCOPE* &rarr; *Time Zone*.\n\nFrom the *Display Settings* tab, set the display behavior for the calendar.\n\n**Display Scheduler Only:** By default, the list of calendars and a\nmini-calendar view (used for quickly navigating to a particular date) are\ndisplayed. Check this to display only the scheduler (the large calendar view\nshowing the calendar and scheduled events).\n\n**Display User Events:** Turns off the display of the current, logged in User's\npersonal calendar and events.\n\n**Display Scheduler's Header:** If disabled, removes the ability to toggle\nthrough the calendar views (for example, Day/Week/Month/Agenda) and access to\nthe Add Event button.\n\n**Enabled Views:** If one of the available views is disabled (Day, Week, Month,\nAgenda), it disappears from the scheduler's header.\n\n**Maximum Days to Display:** Set the maximum number of days to display in the\nAgenda view.\n\n**Maximum Events to Display:** Set the maximum number of events to display in\nthe Agenda view.\n\nUse the *RSS* tab to disable RSS subscription or configure the RSS behavior. \n\nEnough with configuration. Next you'll learn how to use it. \n"
  },
  {
    "path": "en/user/articles/230-calendar/02-using-the-calendar-widget.markdown",
    "content": "---\nheader-id: using-the-calendar-widget\n---\n\n# Using the Calendar Widget\n\n[TOC levels=1-4]\n\nThe calendar widget displays a small monthly calendar showing an overview of\nupcoming events. A larger area shows the Scheduler, a more detailed calendar\nwith a number of options: you can set it to to display a *Day*, *Week*, or\n*Month*, or choose a more event-oriented *Agenda* setting.\n\n![Figure 1: The default view is set in configuration, but a user can change it at any time.](../../images/calendar-view.png)\n\nTwo calendars are included by default when the widget is first added to a page:\na personal calendar for the current user and a Site calendar for the current\nSite. These are displayed in the widget's lower left. Next to each calendar is\na colored box: click it to show/hide that calendar's events in the main viewing\narea.\n\n## Adding New Calendars\n\nTo create a new personal calendar,\n\n1.  Click on the arrow to the right of the *My Calendars* header and select\n    *Add Calendar* from the menu. \n\n2.  Fill in the *Add Calendar* form. Give the calendar a name and description,\n    set a time zone, and decide if it's your user's *default calendar*---the one\n    that is shown automatically whenever the widget is displayed. You can also\n    pick a color, which color codes events whenever multiple calendar's events\n    are displayed at once. You can also decide to enable ratings or comments on\n    the calendar's events, and configure permissions.\n\nTo edit an existing calendar instead of adding a new one, select *Manage\nCalendars* from the menu.\n\nTo add or edit a Site calendar, open the menu next to the header with the\nSite's name.\n\n![Figure 2: Personal and Site calendars are shown in the lower left. This image shows calendars belonging to User *Test Test* and Site *Liferay DXP*.](../../images/new-calendar-manage-calendars.png)\n\n## Adding Events to a Calendar\n\nTo add events to a calendar, \n\n1.  Click on any day in the main viewing area to open an event creation pop up.\n    If you've selected the *Day* or *Week* view, you can click on the specific\n    time when your event begins.\n\n    ![Figure 3: When you click anywhere on the calendar, you'll see the event creation pop up appear. Click *Edit* to specify details for your event.](../../images/new-calendar-event-popup.png)\n\n2.  Name your event and assign it to a calendar. Click *Save* to\n    create the event immediately or *Edit* to enter additional information.\n\n    ![Figure 4: You can specify event details such as the event title, start date, end date, description, location, and more.](../../images/new-calendar-event-details.png)\n\n3.  If you clicked *Edit*, complete the *Edit Event* form. Enter start and end\n    times and enter a description. To schedule an event that reoccurs,\n    check the *Repeat* box and fill in the *Repeat* pop up.\n\n    ![Figure 5: The *Repeat* box allows you to specify whether an events repeats daily, weekly, monthly, or yearly, how often it repeats, and when (or if) it ends.](../../images/new-calendar-event-repeat.png)\n\n### Additional Event Functions\n\nAt the bottom of the *Edit Event* form, there are several collapsed sections:\n*Details*, *Invitations*, *Reminders*, *Categorization*, and *Related\nAssets*.\n\n#### Details\n\nIn the Details section, you can move the event to another calendar and enter\na location.\n\n#### Invitations\n\nIn the invitations section, invite Users, Sites, or Calendar Resources (see the\nnext tutorial for more on resources: in brief, a resource is anything you might\nneed for an event---a conference room, a vehicle, etc.). Follow these steps:\n\n1.  Enter the name of an invitee (User, Site, or Resource) in the\n    *Invitations* field. Hit *Enter* to add them to the *Pending* column.\n\n2.  Check the availability of invitees by clicking the arrow next to the their\n    names and selecting *Check Availability*. This displays their calendars\n    (assuming you have permission to view them).\n\nAn automated email is sent to invitees who must navigate to the calendar\nwidget to respond. See below to customize the content of the invitation.\n\nWhen invitees respond to the invitation, their names move to the *Accepted*,\n*Declined*, or *Maybe* columns.\n\n#### Reminders\n\nSchedule up to two email reminders to send to attendees. Reminders translate\nthe time of the event into the recipients own time zone.\n\nSee below to customize the content of the reminder email.\n\n#### Categorization\n\nTag your event or assign it to a category so it appears in appropriate search\nresults and is published by any asset publisher set to publish content assigned\nto the same category.\n\n#### Related Assets\n\nList an asset---such as an agenda or supplementary material for a meeting---as\nrelated to your event. Links to related assets are displayed in the *Event\nDetails* window.\n\n#### Saving and Drafting Changes and Updating Permissions\n\nAt the very bottom of the Edit form is a set of buttons that let you publish the \nchanges, save the changes as a draft, and configure the event's permissions. \n\nGiving a user permission to add, delete, or update discussion allows them to\nmake, edit and remove comments on the event. The *Permissions* permission\nallows a Role to update permissions for the event.\n\n## Customizing Email Notifications\n\nTo customize email notifications for event invitations and reminders,\n\n1.  From the Calendar widget, click on the arrow next to a calendar and select\n    *Calendar Settings*.\n\n    ![Figure 6: Email templates apply to a single calendar and all its events.](../../images/calendar-email-note.png)\n\n2.  Click on the *Notification Templates* tab. Then select either the *Invite\n    Email* or the *Reminder Email* sub-tab.\n\n3.  Edit the email as desired. At the bottom of the screen is a glossary that\n    specifies variables for terms that were set when you created the event. Use\n    these variables to refer to event-specific information, such as the\n    event's name, date or location. It's a good idea to include a link to the\n    event (use the variable `[$EVENT_URL$]`) as users must navigate to\n    the calendar widget in order to respond.\n\nClick *Save*. Now your notifications contain the proper text. \n\nThe next article covers setting up calendar resources and porting data from one\ninstallation to another.\n"
  },
  {
    "path": "en/user/articles/230-calendar/03-calendar-resources-and-porting.markdown",
    "content": "---\nheader-id: calendar-resources-and-porting\n---\n\n# Calendar Resources and Porting\n\n[TOC levels=1-4]\n\nWith calendar resources, you can invite entities other than people to your\nevents. This is beneficial for finding the availability of important resources\nyour event requires, like a conference room, laptop, or, at The Lunar Resort,\nthe Sasquatch Space Suit used to scare guests out on Lunar hikes. \n\nAnother important topic is porting your calendar's data from one installation of\n@product@ to another.\n\n## Calendar Resources\n\nFollow these steps to add a new calendar resource:\n\n1.  Click on the *Resources* tab and click the ![Add](../../images/icon-add.png) \n    button to add a new resource.\n\n    ![Figure 1: Resources are accessed from the tab menu at the top of the widget.](../../images/calendar-resources.png)\n\n2.  Fill in the *New Resource* form. Enter a name, give it a description, and\n    choose whether to set it as active. You can also tag it, assign it to\n    categories, and configure its permissions. Click *Save*.\n\nThe resource has its own calendar that was generated automatically (this is how\nusers can check its availability when creating events). Just as with Users,\nhowever, resources can have more than one calendar. Follow these steps to assign\na new calendar to the resource:\n\n1.  Go to the widget's *Resources* tab, click the\n    ![Options](../../images/icon-actions.png) button next to the resource, and\n    select *View Calendars*.\n\n2.  Click *Add Calendar* and continue just as if you were creating a calendar\n    for a user or a Site.\n\nOnce a resource is created, invite it to your events just as you would an\nattendee.\n\n## Exporting and Importing Calendar Data\n\nLike other Liferay Applications, the calendar allows data to be exported or\nimported as [LAR files](/docs/7-2/user/-/knowledge_base/u/exporting-importing-widget-data). \nAs with all LAR files, data can only be ported between installations of the same \nversion.\n\n1.  From the calendar widget, click the\n    ![Options](../../images/icon-app-options.png) button in the widget header and select\n    *Export/Import*.\n\n2.  Enter a name for the LAR file (or use the default).\n\n    Under *Application*, choose whether to include the widget's configuration\n    in the LAR.\n\n    Under *Content*, choose how much historical data to export and select the\n    content types (calendars, resources, and events) to include. You can\n    also choose whether to include comments and ratings.\n\n    Check the appropriate boxes to select whether to include deletions and\n    permissions in the LAR.\n\n3.  Click *Export*. When a success message displays (this may take a few\n    moments) you can click on the LAR's filename to download it.\n\n    ![Figure 2: This LAR is ready to be downloaded.](../../images/calendar-lar.png)\n\nFollow these steps to import a LAR:\n\n1.  From the calendar widget, click the\n    ![Options](../../images/icon-app-options.png) button in the widget header and select\n    *Export/Import*.\n\n2.  Click the *Import* tab.\n\n3.  Click *Choose File* or else drag-and-drop a LAR into the area surrounded by\n    a dotted line. Click *Continue*.\n\n4.  Decide how much data you want to import:\n\n    Under *Application*, check the box to import the configuration stored in the\n    LAR or leave in unchecked to keep your current configuration.\n\n    Under *Content*, decide which content types (calendars, resources, and\n    events) to import, and whether to include comments and ratings.\n\n    Choose whether to import permissions and deletions, and decide whether to\n    delete your widget's existing data before the import.\n\n5.  In the collapsible *Update Data* section, choose how data will be updated.\n\n    **Mirror:** The data will be imported along with a reference to its\n    source. This allows data to be updated rather than duplicated if the same\n    LAR is imported more than once.\n\n    **Copy as New:** All data is imported as new entries. Repeat imports will\n    produce duplicates.\n\n6.  In the *Authorship of the Content* section, choose whether to keep the\n    original author of the imported content (where available) or to list the\n    current user as the author.\n\n7. Click *Import*.\n\nYour calendar is set up and ready to go! Better check it to see what's next on\nthe agenda.\n"
  },
  {
    "path": "en/user/articles-dxp/10-managing-users/11-auditing-users/01-auditing-users-intro.markdown",
    "content": "---\nheader-id: auditing-users\n---\n\n# Auditing Users\n\n[TOC levels=1-4]\n\nYou've just finished lunch and are ready to get back to work. You have a Site\nyou use to manage your project and before you left, you were about to create a\nfolder in your Documents and Media library for sharing some requirements\ndocumentation. Sitting down at your desk, you navigate to the repository and\nattempt to create the folder.\n\n*You do not have permission to perform this action*, @product@ helpfully tells\nyou. \n\n\"*What?*\" you blurt in surprise. \"This is *my* project!\" \n\n\"Ah, you too?\" asks a co-worker from over the cube wall. \"I lost access to a \nwiki I was updating just a few minutes ago. I was about to enter a support \nticket for it.\" \n\n\"Forget the ticket. Let's go see the admin now,\" you say. \n\nAnd off you go, two floors down and to the far end of the building where, as you\napproach, you can already hear stress in the admin's voice as he tries to \nreassure someone on the phone. \n\n\"Yes, Mr. Jones. Yes, I'll fix it.\" (*Jones? The president of the company?* goes\nthrough your mind.) \"I'll get on it right away, Mr. Jones. It was just a \nmistake; I'll fix it. Thank you, Mr. Jones,\" and he hangs up the phone. \n\n\"Problems?\" you ask the admin, whose name is Harry. He does look rather harried. \n\n\"Yeah, Tom,\" he says. \"Somebody changed a bunch of permissions---it wasn't me. \nI'm assuming you and Dick are here because of the same problem?\" \n\n\"Yup,\" you say. \"I lost access to a document repository folder.\" \n\n\"And I lost access to a wiki,\" Dick says. \n\n\"It was probably due to some Site membership change. Let's take a look at the\nAudit app in the Control Panel and see what happened.\" \n\nSometimes you need to know what Users are doing and exactly who is doing it. If\nyou're a DXP subscriber, you can find this out with the Audit app. In\ncombination with some settings in `portal-ext.properties`, the Audit app shows\nyou all the activity that occurs on your server. You can quickly find out\nwhat changes were made and by whom. If you've delegated permission granting to\nany group of people, this is an essential feature you're likely to use.\n\n"
  },
  {
    "path": "en/user/articles-dxp/10-managing-users/11-auditing-users/02-viewing-user-activity.markdown",
    "content": "---\nheader-id: using-audit-events\n---\n\n# Viewing Audit Events\n\n[TOC levels=1-4]\n\nThe Audit app shows activities in your @product@ installation. Access it by\nnavigating to *Control Panel* &rarr; *Configuration* &rarr; *Audit*. The app\ndisplays a searchable list of captured events. You can browse the list, but\nsearching it is typically faster. \n\nThis figure shows that John Watson logged in and performed some actions on the \nsite. Click an entry to view details about any of these events. \n\n![Figure 1: The Audit app displays the events it captures in a searchable list.](../../../images-dxp/audit-list-events.png)\n\n![Figure 2: Click an event in the list to show its details. The details for this event show that John Watson updated his user account's `prefixId` from `1` to `4`. The `prefixId` represents a name prefix like Dr., Mr., Mrs., or Ms.](../../../images-dxp/audit-detail.png)\n\nAs you can see, depending on how many users you have, this list can get\npopulated very quickly. That's why page view events aren't displayed by default.\nThey'll clutter up your audit report, since they'll definitely be the most \nfrequent event. \n\n**Note:** You can add page view events to your audit report, but keep in mind\nthat doing so adds LOTS of events. If you're a glutton for this kind of\npunishment, add this property to your `portal-ext.properties` file:\n\n```properties\naudit.message.com.liferay.portal.kernel.model.Layout.VIEW=true\n```\n\n@product@'s code refers to pages as *layouts*. Setting this property to `true`\ntherefore records audit events for page views. It's turned off by default\nbecause this is too fine-grained for most installations.\n\nOnce you've added the property, restart your server.\n\n## Finding Audit Events\n\nFinding what you want in a big list of events is like searching for a needle in\na haystack. This is why the Audit app has a robust search mechanism. By default, \nthere's only a single search field. Clicking the *magnifier* icon next to the\nsearch bar, however, reveals an advanced search dialog broken out by various\nfields you can use in your search. \n\nHere are the available search options:\n\n**Match:** Search for matches to *all* the fields you've specified or *any* \nsingle field. \n\n**User ID:** The user ID to search for. This is usually the User who performed\nsome action you'd like to audit.\n\n**User Name:** The user name to search for. This is often easier than \nsearching for a user ID, especially if you don't have access to the database \ncontaining the user ID. \n\n**Resource ID:** The ID of the resource that was modified or viewed in this \naudit record. \n\n**Class Name:** The name of the resource that was modified or viewed in this \naudit record. For example, you could search for user resources to see if \nsomeone modified a user's account. \n\n**Resource Action:** An action performed on the resource. This could be any \nof these: `add`, `assign`, `delete`, `impersonate`, `login`, `login_failure`,\n`logout`, `unassign`, or `update`.\n\n**Session ID:** The session ID to search for. You can use this to correlate \na session ID from your web server logs with activity in @product@. \n\n**Client IP:** The IP address of the client that performed the activity you \nwish to audit. \n\n**Client Host:** The host name of the client that performed the activity you \nwish to audit. \n\n**Server Name:** The name of the server in which the activity occurred. If \nyou're using a cluster, each member of the cluster can be individually \nqueried. \n\n**Server Port:** The server port in which the activity occurred. You need \nthis if you run a vertical cluster of multiple VMs on the same machine. \n\n**Start Date:** The low end of the date range you wish to search for. \n\n**End Date:** The high end of the date range you wish to search. \n\nFor example, to check if someone unassigned a User from a particular Role, you \nmight search for a resource name of *user* and a resource action of *unassign*. \nOnce you have the search results, you can click any of the returned records to \nsee that record's detail page. \n\n![Figure 3: Searching for audit events is easy with the Audit app's advanced search form. You can specify various search criteria to find the types of events you want.](../../../images-dxp/audit-unassign-search.png) \n\n![Figure 4: This record shows that the default administrative user removed the Power User Role from the User Test Test.](../../../images-dxp/audit-unassign-detail.png)\n\nAs you can see, the Audit app shows you what's happening as Users make changes. \nUse this information to troubleshoot problems, determine ownership of particular \nactions, or, as Harry (from the story in the introduction) is about to do, find \nout who made permission changes they weren't supposed to make. \n"
  },
  {
    "path": "en/user/articles-dxp/10-managing-users/11-auditing-users/03-configuring-user-audits.markdown",
    "content": "---\nheader-id: configuring-audits\n---\n\n# Configuring Audits\n\n[TOC levels=1-4]\n\nAudits are enabled by default. The Audit app reports audit events, but you can\nalso report them in @product@'s logs or console, enable them for scheduled jobs,\nor disable them entirely.\n\nThere are two main ways to configure @product@:\n\n1.  Edit a configuration via the Control Panel. This saves the configuration to\n    the database.\n\n2.  Edit a configuration via an OSGi configuration file (`.config` file) in your\n    `[Liferay Home]/osgi/configs` folder.\n\nThese methods apply to each of the audit configuration options explained below.\n\n## Reporting Audit Events in Liferay's Logs and Console\n\nFollow these steps to use the Control Panel to configure the reporting of log\nevents in @product@'s log and console:\n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings* and\n    select *Audit* from the *Security* section.\n\n2.  In the *SYSTEM SCOPE* column on the left, select\n    *Logging Message Audit Message Processor*.\n\n3.  Select the *Enabled* checkbox to report audit events in @product@'s log.\n\n4.  Select the *Output to Console* checkbox to report audit events in the\n    console.\n\n5.  In the *Log Message Format* selector menu, select the format for the audit\n    events (CSV or JSON).\n\n6.  Click *Save* when you're finished.\n\nAlternatively, you can make the same configuration via an OSGi configuration\nfile:\n\n1.  Create a file called `com.liferay.portal.security.audit.router.configuration.LoggingAuditMessageProcessorConfiguration.config`.\n\n2.  Add these properties to the file:\n\n    ```properties\n    enabled=\"true\"\n    logMessageFormat=\"CSV\"\n    #logMessageFormat=\"JSON\"\n    outputToConsole=\"true\"\n    ```\n\n    Note that these are the same options set in the Control Panel. Edit them as\n    you see fit.\n\n3.  Deploy the file to the `[Liferay Home]/osgi/configs` folder. Note that the\n    [Liferay Home folder](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\n    is typically the application server's parent folder.\n\nRegardless of your configuration approach, you must also extend @product@'s\n`log4j-ext.xml` file to configure Log4j (@product@'s logging implementation) to\nlog messages produced by the appropriate class to the appropriate file. To do\nso, create a `portal-log4j-ext.xml` file in\n`[Liferay Home]/tomcat-[version]/webapps/ROOT/WEB-INF/classes/META-INF`\nwith this configuration:\n\n```xml\n<?xml version=\"1.0\"?>\n<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">\n\n<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\">\n\n    <!-- additional audit logging -->\n\n    <appender name=\"auditFile\" class=\"org.apache.log4j.rolling.RollingFileAppender\">\n        <rollingPolicy class=\"org.apache.log4j.rolling.TimeBasedRollingPolicy\">\n            <param name=\"FileNamePattern\" value=\"@liferay.home@/logs/audit.%d{yyyy-MM-dd}.log\" />\n        </rollingPolicy>\n        <layout class=\"org.apache.log4j.EnhancedPatternLayout\">\n            <param name=\"ConversionPattern\" value=\"%d{ABSOLUTE} %-5p [%t][%c{1}:%L] %m%n\" />\n        </layout>\n    </appender>\n\n    <category name=\"com.liferay.portal.security.audit.router.internal.LoggingAuditMessageProcessor\">\n        <priority value=\"INFO\" />\n        <appender-ref ref=\"auditFile\"/>\n    </category>\n</log4j:configuration>\n```\n\nThis configures Log4j to record INFO level messages from the class\n`com.liferay.portal.security.audit.router.internal.LoggingAuditMessageProcessor`\nto a file called `audit.yyyy-MM-dd.log` in the `[Liferay Home]/logs` folder.\nAdjust the audit file properties or log level to your liking.\n\n## Configuring Audit Events for Scheduled Liferay Jobs\n\nBy default, scheduled jobs don't trigger audit events. Follow these steps to\nenable them via the Control Panel:\n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings*, and\n    select *Infrastructure* from the *Platform* section.\n\n2.  In the *SYSTEM SCOPE* column on the left, select *Scheduler Engine Helper*.\n\n3.  Select the checkbox for *Audit scheduler job enabled* and click\n    *Save*.\n\nAlternatively, you can make the same configuration via an OSGi configuration\nfile:\n\n1.  Create a file called\n`com.liferay.portal.scheduler.configuration.SchedulerEngineHelperConfiguration.config`.\n\n2.  Add this property to the file:\n\n    ```properties\n    auditSchedulerJobEnabled=true\n    ```\n\n3.  Deploy the file to the `[Liferay Home]/osgi/configs` folder. Note that the\n    [Liferay Home folder](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\n    is typically the application server's parent folder.\n\nAuditing scheduled jobs is a smart choice if there's a chance someone with a\ndubious competence level would try to schedule jobs, as you'll find out below in\nthe conclusion of our story.\n\n## Enabling or Disabling Audit Events Entirely\n\nAudit events are enabled by default. Follow these steps to disable them via the\nControl Panel:\n\n1.  Go to *Control Panel* &rarr; *Configuration* &rarr; *System Settings* and\n    then click *Audit* in the *Security* section.\n\n2.  Uncheck the *Enabled* box. Note that when auditing is enabled, you can\n    adjust the audit message max queue size from its default value.\n\nAlternatively, you can enable or disable auditing via an OSGi configuration\nfile:\n\n1.  Create a file called\n    `com.liferay.portal.scheduler.configuration.SchedulerEngineHelperConfiguration.config`.\n\n2.  Add these properties to the file. You can adjust their values as desired:\n\n    ```properties\n    enabled=\"true\"\n    auditMessageMaxQueueSize=\"200\"\n    ```\n\n3.  Deploy the file to the `[Liferay Home]/osgi/configs` folder. Note that the\n    [Liferay Home folder](/docs/7-2/deploy/-/knowledge_base/d/liferay-home)\n    is typically the application server's parent folder.\n\n## The End of the Story\n\n\"Okay,\" says Harry, \"let's fire up the audit system and see if we can figure out\nwhat happened.\"\n\nYou and Dick stand behind Harry's chair and watch as he enters a query into a\nform on the Audit app. After clicking *search*, the screen fills up with audit\nevents.\n\n\"Wow, that's a lot of unassign events.\" Harry says. \"And look who the culprit\nis,\" he adds sarcastically.\n\n\"Who's Melvin Dooitrong?\" Dick asks.\n\n\"That's my new intern,\" Harry says. \"He's gonna be sorry.\" Harry pushes out his\nchair and walks down the row of cubes to the end, where a kid no more than 20\nyears old with disheveled hair sits, earbuds in his ears.\n\n\"Hey Melvin,\" Harry says as Melvin turns around to face him. \"Didn't I ask you\nto move that set of users from Site membership to Organization membership?\"\n\n\"Yeah,\" Melvin says, \"I did that already.\"\n\n\"How'd you do it?\"\n\n\"It was going to take a while to do it manually, so I wrote a script and\nexecuted it in the scripting host,\" Melvin replies, matter-of-factly.\n\n\"You did, did you? Well, guess what? Your script removed *everybody* from *all*\nSites.\"\n\n\"*What?*\"\n\n\"Yeah, and now you're going to start adding them back, one by one, manually,\nstarting with Mr. Jones....\"\n\nTom and Dick back away slowly from Melvin's cube as Harry and Melvin continue to\nhave their---let's call it a discussion. One thing is clear: they're having a\nbetter day than Melvin is.\n"
  },
  {
    "path": "en/user/articles-dxp/120-search/12-synonyms.markdown",
    "content": "---\nheader-id: search-tuning-synonym-sets\n---\n\n# Search Tuning: Synonym Sets\n\n[TOC levels=1-4]\n\nStarting with @product-ver@ Service Pack 1, new search tuning features are\navailable for administrative Users: Synonym Sets is one of them.\n\nSynonym Sets are mappings that you (the admin) create, so that if a User\nsearches for a certain keyword or phrase, the synonymous terms in your mapping\nare also searched. Matches to synonyms keywords are scored equally to matches\nwith the exact keyword by the search engine.\n\n**Lunar Resort Use Case:** Multiple content creators at the Lunar Resort write\nblogs about a variety of topics. Consistent terminology is a problem for some\nconcepts. One writer might use the term \"rover\" for the vehicle that travels\nacross the moonscape, while another uses \"lunar cart\" or \"moon ATV\". As the\nportal administrator, you must ensure that the search experience is such that\nsearching for any of those keywords returns all relevant results. Synonym Sets\nare a key ally in this pursuit.\n\n## Requirements and Limitations\n\nSearch tuning features like Synonym Sets are only supported when using\nElasticsearch as the search engine. If you're using Solr, make sure you disable\nthe \n[search tuning features](/docs/7-2/deploy/-/knowledge_base/d/installing-solr#blacklisting-elasticsearch-only-features)\n(Synonym Sets and Result Rankings) when you upgrade your installation to\n@product@ Service Pack 1 (Fix Pack 2).\n\nAs of the initial release (@product-ver@ SP-1), Synonym Sets work with fields\nindexed in two locales: English and Spanish. Thus, the assets supporting\nlocalization out-of-the-box work with Synonym Sets. Technically, this means that\nsynonym searches operate on fields indexed with the `en_*` and `es_*` suffixes.\nRead the \n[multi-language search documentation](/docs/7-2/user/-/knowledge_base/u/searching-for-localized-content) \nto learn which native @product@ assets/fields support localization in the search\nindex. All asset types that index their data into English and Spanish are\nanalyzed with a synonyms-aware analyzer and can be found during a synonym\nsearch.\n\nThe `=>`\n[format](https://www.elastic.co/guide/en/elasticsearch/guide/current/synonym-formats.html)\nsupported in Elasticsearch is not supported through the Synonyms Set UI.\n\n## Creating and Managing Synonym Sets\n\nCreate a synonym set by adding as many synonymous keywords to a set as you\nwant. Once the synonym set is saved, any searches in the same company scope\n(that's any site from the Virtual Instance where the synonyms were configured)\ntake effect.\n\n![Figure 1: Add as many synonymous keywords to a set as you'd like.](../../images/search-synonym-set.png)\n\nTo create a synonym set,\n\n1. Navigate to Control Panel &rarr; Search Tuning &rarr; Synonyms.\n\n2.  Click the Add button (![Add](../../images/icon-add.png)).\n\n3.  Enter the list of synonyms in the set. The input of a synonym is\n    accomplished by clicking _Enter_ or by entering a comma.\n\n4.  When the list is finished, click _Publish_.\n\nThe available synonym sets appear and can be managed in bulk or individually.\nThe management options are to update a synonym set or delete one or more\nsynonym sets.\n\n![Figure 2: Synonym sets can be managed in bulk.](../../images/search-synonym-sets.png) \n\nTo edit or delete a single synonym set, click the Actions button\n(![Actions](../../images/icon-actions.png)) for the synonym set and choose Edit\nor Delete.\n\n## Using Synonym Sets\n\nWhen you have a synonym set defined, the synonyms are ready for use. To test\nthem, find a Search Bar anywhere in the virtual instance and enter a keyword\nfrom one of your synonym sets. Results matching the keyword and any synonym are\nreturned in the Search Results widget.\n\n![Figure 3: The Blogs Entry does not contain the word \"rover\" but it can be matched because of a synonym set mapping \"cart\" as its synonym. The synonym is even highlighted.](../../images/search-synonomous-result.png)\n\n## Known Issues\n\nThere are several [known issues](https://issues.liferay.com/browse/LPS-99658)\nfor Synonym Sets. These are some of the most important ones:\n\n[LPS-100272](https://issues.liferay.com/browse/LPS-100272): \nReindexing permanently deletes all Synonym Sets. Please refer to the ticket for\na way to backup and preserve (restore) Synonym Sets across reindex operations. \n\n[LPS-98126](https://issues.liferay.com/browse/LPS-98126)\nUsers can create duplicate Synonym Set entries and update other Synonym Sets unintentionally.\n\n## Related Resources\n\n* <https://www.elastic.co/guide/en/elasticsearch/guide/current/synonyms.html>\n* <https://www.elastic.co/guide/en/elasticsearch/reference/6.8/analyzer-anatomy.html>\n* <https://www.elastic.co/guide/en/elasticsearch/reference/6.8/analysis-synonym-graph-tokenfilter.html>\n* <http://lucene.apache.org/core/7_7_0/analyzers-common/org/apache/lucene/analysis/en/EnglishPossessiveFilter.html>\n"
  },
  {
    "path": "en/user/articles-dxp/120-search/13-result-rankings.markdown",
    "content": "---\nheader-id: search-tuning-customizing-search-results\n---\n\n# Search Tuning: Customizing Search Results\n\n[TOC levels=1-4]\n\nStarting with @product-ver@ Service Pack 1, new search tuning features are\navailable for administrative Users: Custom Result Rankings is one of them.\n\nResult Rankings provides a brute force method for intervening into the relevance\nscoring of the search engine, by doing these things:\n\n1.  Designate that certain results should appear at the top of the results if\n    they are matched with a certain keyword. This is the idea of _pinning_\n    results to the top of the results list.\n\n2.  By contrast, hide results that shouldn't appear in certain searches at all. \n\n3.  Add results that aren't normally returned by searching a certain keyword.\n\n4.  Re-order pinned results with a drag-and-drop interface.\n\nResult Rankings let you pin, hide, and add search results for a given set of\nkeywords. These rankings apply only to searches using the newer search widgets\n(Search Bar, Search Results, etc.). The rankings you customize do not apply to\nthe legacy Search portlet results or to the individual application search bars.\n\n| **Use Case:** At the Lunar Resort website, visitors often search for activities,\n| entering keywords like \"rover races\", \"atv rentals\", and \"lunar golf\". For all\n| of these, the Lunar Resort always wants a certain [Content\n| Page](/docs/7-2/user/-/knowledge_base/u/creating-content-pages) to appear  at\n| the top of the search results. This is the Activities page in the Lunar Resort\n| where guests can find all of the resort's adventurous offerings, including lunar\n| rover races, ATV rentals, and information about golfing packages. By contrast,\n| the Lunar  Resort does not want the legal liability waiver form to appear during\n| a search for fun activities: that's a bridge to be crossed when guests sign up\n| for the activity. It shouldn't pollute a search for fun activities, even though\n| it contains many of the keywords Users would search for. Result Rankings lets\n| you _pin_ the Activities Content Page to the top of the results and _hide_ the\n| liability waiver Web Content Article.  In addition, a community member wrote a\n| blog favorably reviewing the Lunar Resort, and you want that content added to\n| searches for activities at the resort. This is a prime use case for Result\n| Rankings. \n\n![Figure 1: The Lunar Resort wants to tweak these results: pin the Activities page to the top, and hide the legal content entirely.](../../images/search-result-rankings-todo.png) \n\n## Availability\n\nSearch tuning features like Result Rankings are only supported when using\nElasticsearch as the search engine. If you're using Solr, make sure you disable\nthe \n[search tuning features](/docs/7-2/deploy/-/knowledge_base/d/installing-solr#blacklisting-elasticsearch-only-features)\n(Synonym Sets and Result Rankings) when you upgrade your installation to\n@product@ Service Pack 1 (Fix Pack 2).\n\nResults Rankings was added in @product-ver@ Service Pack 1.\n\n## Requirements and Limitations\n\nResult Ranking entries are configured in a Virtual Instance, but are not applied\nonly to that Virtual Instance. Instead, custom rankings made in one virtual\nInstance are shared across all Virtual Instances in the deployment, and even\nacross separate deployments sharing an Elasticsearch cluster (in a multi-tenant\nscenario). Therefore, Result rankings shouldn't be used when connecting\nmultiple @product@ deployments to the same Elasticsearch cluster unless you\nintend for the same Result rankings to apply to every @product@ deployment. See \n[LPS-101291](https://issues.liferay.com/browse/LPS-101291)\nfor more information.\n\nAn existing Result Ranking cannot be renamed. Renaming requires recreating the\nranking under a different name. See\n[LPS-96357](https://issues.liferay.com/browse/LPS-96357)\nfor more information.\n\n## Creating and Managing Result Rankings\n\nTo manipulate result rankings, create a new _Alias_ containing the\nkeywords/search terms you want to intercept. Perform a search to get results\n(you can also do a separate search if you want to grab results that haven't even\nbeen returned during a natural search for the alias keywords). Once you have the\nresults, choose to pin, hide, re-order, or add results as you please.\n\nTo create a new Result Rankings Alias:\n\n1.  Navigate to Control Panel &rarr; Search Tuning &rarr; Result Rankings.\n\n2.  Click the Add button (![Add](../../images/icon-add.png)).\n\n3.  On the New Ranking screen, enter one of the keywords or search phrases you\n    want to intercept (it can be a phrase, instead of just one word; and don't\n    worry, you can add more later) in the _Search Query_ field. \n\n    Click _Customize Results_.\n\nA search query is executed. The results are displayed and the tools for pinning,\nhiding, and adding results are made available. Re-ordering becomes possible\nafter at least one result is pinned. First, consider whether to add one or more\nAliases.\n\n### Adding Aliases\n\nThe Customize Rankings screen is ready to use, but any intervention only\napplies to the search query you initially entered in the New Ranking screen. To\napply the customized rankings to additional search terms, add them as _Aliases_. \n\n1.  In the Aliases field, enter the search term to add as an Alias.\n\n    **Warning:** Do not use quotes in your alias terms.\n\n2.  To submit the search term as an alias, click Enter or a comma in the Aliases\n    field. You can Add multiple aliases here. \n\n    ![Figure 2: Apply your custom rankings to matched results of additional search terms.](../../images/search-result-rankings-aliases.png)\n\nNote that results not manipulated manually here are returned as usual when the\nalias term is queried for in the Search Bar. \n\nNow you can customize the rankings.\n\n### Activating and Deactivating Aliases\n\n> Available as of Liferay DXP 7.2, SP2\n\nYou can activate or deactivate existing aliases as you have need for them to take effect:\n\n1.  Find the Active alias and open its editor screen.\n\n2. Click the toggle switch currently labeled Active.\n\nDuplicate active aliases are not allowed, but you can deactivate an alias and\nthen create a duplicate. After deactivating an alias, you can only reactivate\nit after deactivating any active duplicates.\n\n### Pinning and Hiding Results\n\nTo pin or hide rankings, hover over the result of interest: two icons appear,\none for pinning and one for hiding. Click the one that applies. Otherwise click\nthe Actions button (![Actions](../../images/icon-actions.png)), and select _Pin\nResult_ or _Hide Result_. Once you select either option, it's applied\nimmediately. A pinned result moves to the top of the list, and a hidden result\ndisappears. Repeat the action as many times as necessary.\n\nIf you're done customizing the results, click _Save_, and it's applied\nimmediately.\n\n![Figure 3: Pin results to the top of the Search Results list.](../../images/search-result-rankings-pinned-result.png)\n\n### Adding Results\n\nTo add a result that was not returned by searching for the first keyword or\nphrase, click the _Add Result_ button and search for whichever asset you want to\npin. \n\n![Figure 4: Add results that aren't normally returned.](../../images/search-result-rankings-add-result.png)\n\nClick _Save_ if you're done customizing results.\n\n### Re-Ordering Pinned Results\n\nTo re-order pinned results (results that are not pinned cannot be re-ordered),\nclick the handle icon, drag the result, and drop it in the preferred location in\nthe list. \n\n![Figure 5: Re-order the pinned rankings if you want to emphasize or de-emphasize certain results.](../../images/search-result-rankings-reorder.png)\n\nOnce finished customizing result rankings, click _Save_.\n\n## Result Rankings Scope and Permissions\n\nBecause configuration of Result rankings happens at the virtual instance scope,\nthere are scoping and permissions behaviors to be aware of.\n\nScope is disregarded for pinned results: Pinned results existing in Site\nA always appear in searches from Site B, even if the Search Bar Scope is set to\n_This Site_. \n\nSearch from Result Rankings is global: When searching for results in Result\nRankings admin, relevant results from all sites are returned.\n\nPermissions are applied as usual: If a User doesn't have permission to see an\nasset, pinning it does not make it appear in the search results for that\nUser.\n\n## Result Rankings Aliases versus Synonyms\n\nSince both are new features without precedent in @product@, there can be\nconfusion over Result Rankings Aliases and Synonyms.\n[Synonyms](/docs/7-2/user/-/knowledge_base/u/search-tuning-synonym-sets) \nexpand the search to include results matched by additional (synonymous)\nkeywords, so more results are returned if there are matches to the synonyms.\n\nResult Rankings Aliases are just keywords that also have the particular\nranking interventions applied to them. Only the searched keyword is matched\nto results, and then, the pins, hides, re-ordering, and additional results take\neffect after that. \n\nThese features don't interact in a predictable way. If you need synonym-like\nbehavior in results rankings, define aliases for the keyword.\n\n## Known Issues\n\nThere are several known  issues and planned improvements for Result Rankings.\nSee [LPS-99540](https://issues.liferay.com/browse/LPS-99540) for the complete\nlist.\n\n"
  },
  {
    "path": "en/user/articles-dxp/140-workflow/05-workflow-metrics-sla.markdown",
    "content": "---\nheader-id: workflow-metrics-the-service-level-agreement-sla\n---\n\n# Workflow Metrics: The Service Level Agreement (SLA)\n\n[TOC levels=1-4]\n\nA brand new feature in Liferay DXP 7.2, _Workflow Metrics_ gives insights into\nthe time certain workflow events take to complete. To use it, set up deadlines\non a workflow process's events. These deadline configurations are referred to as\nSLAs (Service Level Agreements). Once defined, Workflow Reports measure compliance\nwith the SLAs.\n\n| **Requires Elasticsearch:** To use Workflow Metrics, you must be using\n| Elasticsearch to index your @product data. Read\n| [here](/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch) to learn\n| about installing Elasticsearch.\n\nSLAs define the deadlines on a workflow process's events. They're like a\ncontract between the workflow participants and Users submitting workflow items.\n\nWorkflow Reports shows data for all processes with SLAs, including each\nworkflow item's SLA status: on time or overdue.\n\n| **Editing a Workflow with SLAs:** Editing a workflow (e.g., removing nodes, editing a task name) with\n| SLAs defined on it may invalidate the SLA for\n| items already in the workflow/SLA pipeline.\n|\n| **Creating or Editing SLAs for Active Processes:** Editing an SLA's duration or\n| defining a new SLA while items are already in the workflow process causes a\n| recalculation for all instances currently in the workflow. Completed workflow\n| instances are not recalculated.\n\n## Adding SLAs\n\nTo add an SLA,\n\n1.  Go to Control Panel &rarr; Workflow &rarr; Metrics.\n\n2.  Click on the title of the Process. \n\n    The Reports UI for the process is displayed. \n\n3.  If there's no SLA for the process, a warning message stating\n    as much appears. Click the _Add a new SLA_ link from the warning to\n    access the New SLA form directly.\n\n    Alternatively, click the Options (![Options](../../images/icon-options.png))\n    menu and select _SLA Settings_.\n\n    ![Figure 1: Add SLAs to a workflow definition from the Metrics application.](../../images/workflow-add-sla.png)\n\n3.  On the SLAs screen, click the *Add* button\n    (![Add](../../images/icon-add.png)).\n\n4.  In the New SLA form, Give the SLA a Name and Description.\n\n5.  Define the time frame for the SLA, specifying three things:\n\n    - Start: When the item makes it to the event defined here, the SLA timer\n        begins counting.\n    - Pause: If there's an event in the workflow when time should stop counting,\n        enter it here. For the Single Approver workflow, you might choose to\n        pause the SLA timer when the item is in the Update task.\n    - Stop: Choose when the SLA is completed. If the item makes it to the Stop\n        event before the defined SLA duration (the deadline), it's _On Time_\n        according to the SLA. If it fails to make it to the Stop event in the\n        specified duration, it's _Overdue_.\n\n6.  Define the duration (i.e., the deadline) for the SLA. Fill out at least one\n    of the two time boxes.\n\n    **Days:** Enter a whole number of days.\n\n    **Hours:** Enter hours and minutes in the format HH:MM\n\n7.  Once you click *Save*, you'll see the SLA listed on the SLAs screen.\n\n| **System Calendar:** By default, there's an internal calendar that assumes the\n| SLA duration should continue counting all the time: in other words, 24 hours\n| per day, seven days per week. If you need a different calendar format, find a\n| friendly developer to create a custom calendar. Official docs will be written\n| for this extension point, but the basic idea is to implement the\n| `WorkflowMetricsSLACalendar` interface. New implementations of this service are\n| picked up automatically by the Workflow Metrics applications, so it becomes\n| available as soon as the module holding the service implementation is\n| deployed. The interface has three methods to implement:\n|\n| `public Duration getDuration(\n|   LocalDateTime startLocalDateTime, LocalDateTime endLocalDateTime);`\n|\n| `public LocalDateTime getOverdueLocalDateTime(\n|   LocalDateTime nowLocalDateTime, Duration remainingDuration);`\n|\n| `public String getTitle(Locale locale);`\n|\n| See the `DefaultWorkflowMetricsSLACalendar` from the\n| `portal-workflow-metrics-service` module for example code. If you define a new\n| calendar, a new option becomes available in the Add SLA form, allowing you to\n| choose from the default 24/7 calendar or any custom ones you've provided (for\n| example, make the timer run for 9 hours per day, 5 days per week).\n\n![Figure 2: Manage SLAs from the SLAs screen.](../../images/workflow-metrics-sla-list.png)\n\n#### Valid Start and Stop Events\n\nAny workflow task can be used as a start or end parameter for the SLA.\n\nWhen defining the tasks to act as the SLA's Start Events, choose between three\nevents:\n\n- The start node\n- Entry into a task\n- Exit from a task\n\nWhen defining the tasks to act as the SLA's Stop Events, choose between three\nevents:\n\n- Entry into a task\n- Exit from a task\n- The end node\n\nThe SLA can be paused at any task that falls between the start node and the end\nnode, and it's defined by setting the node(s) when the SLA should be paused.\n_The SLA timer is paused the entire time a workflow item is in the specified\nnode_.\n\n#### Durations\n\nDefine the SLA durations in at least one of the available boxes (Days and\nHours). Here are some examples:\n\nExample Duration: 1 day, 24 hours\n: Valid configuration --- Days: _1_\n: Invalid --- Hours: _24:00_. The Hours box must not exceed _23:59_.\n\nExample Duration: 36 hours\n: Valid --- Days: _1_, Hours: _12:00_\n: Invalid --- Days: _1.5_. Only whole numbers are accepted.\n\nExample Duration: 6.5 hours\n: Valid --- Hours: _06:30_\n\nOnce your SLAs are configured, activate the workflow on an asset, stretch\nyour fingers, and get ready for the submissions to roll in if you're one of the\nworkflow assignees. You're on the hook to get those workflow items through the\nprocess within the SLA duration!\n"
  },
  {
    "path": "en/user/articles-dxp/140-workflow/06-workflow-metrics-reports.markdown",
    "content": "---\nheader-id: workflow-metrics-reports\n---\n\n# Workflow Metrics: Reports\n\n[TOC levels=1-4]\n\nAs soon as you enter the Metrics screen (Control Panel &rarr; Workflow &rarr;\nMetrics) you see metrics on each workflow installed in the system.\n\n![Figure 1: In this view, the only process with pending items is the Single Approver.](../../images/workflow-metrics-reports1.png)\n\nA table view of all installed workflow processes shows you how many items are\nOverdue, how many are On Time, and how many are Pending in the workflow process.\n\nThere's more to Metrics than the overview report though. Get more detailed\nreports by clicking on one of the workflow processes.\n\n| **Requires Elasticsearch:** To use Workflow Metrics, you must be using\n| Elasticsearch to index your @product data. Read\n| [here](/docs/7-2/deploy/-/knowledge_base/d/installing-elasticsearch) to learn\n| about installing Elasticsearch.\n\n## Understanding Reports\n\nThe Reports UI has two main views, represented as tabs: _Pending_ (changed to\n_Dashboard_ as of @product-ver@ Service Pack 1---SP1) and\n_Completed_ (changed to _Performance_ in SP1).\n\n_Pending/Dashboard_ items are those currently in the workflow process, and include items\nuntracked by the SLA. This might include items in the paused step of the\nworkflow, or items that are outside the scope of the SLA duration.\n\n_Completed/Performance_ items show any item that has completed processing in the\nworkflow. SP1 includes a new chart on this screen: _Completion Velocity_.\n\nWhen you first click into the metrics for a specific process, you're presented\nwith two valuable reports on pending items: the Pending Items overview and\nWorkload by Step.\n\n![Figure 2: See data on the Pending Items and the Workload by Step for a process.](../../images/workflow-metrics-reports2.png)\n\n### Pending Items\n\nPending Items shows you the overview of items by their SLA status. Drill down by\nclicking on any of the statuses to see the specific items that are enumerated\nin Pending Items.\n\n### Workload by Step\n\nWorkload by Step shows a breakdown of the items that are in each step of the\nworkflow process, by their SLA status (Overdue or On Time).\n\n### Completed Items\n\nClick the *Completed* tab (*Performance* on SP1) to see the items that have\ncompleted the workflow process. Workload by Step data doesn't make sense in this\ncase, because by definition, these items are no longer in any workflow process\nstep. Instead, there's a _Completion Velocity_ chart that shows the performance\nover time.\n\n## Completion Velocity\n\nA line chart shows you the completion rate for the workflow process. The default\ndisplay shows the number of completed workflow instances per day, for the last\n30 days.\n\n![Figure 3: View the completion rate of items in a workflow process over time.](../../images-dxp/workflow-reports-completion-velocity.png)\n\nThe overall completion rate for the time period is displayed in the top right\ncorner of the chart (as _Inst/timeUnit_), while the trend-line is presented in\nthe chart body. The overall performance metric and the chart body are updated\nwhen you select a new time period; the time unit changes depending on the total\ntime period you're measuring. For some of the longer durations, the time unit is\nconfigurable:\n\n**Today**\n: Calculate _Inst/Hour_ from _00:00_, or _12:00 AM_, of the current day until the\ncurrent time (rounded to the nearest whole hour).\n\n**Yesterday**\n: Calculate _Inst/Hour_  From _00:00-23:59_, or _12:00 AM to 11:59 PM_, of the\nprevious day.\n\n**Last 7 Days**\n: Calculate _Inst/Day_. The current day counts as 1 day, so this is from 6 days\nago to the current day.\n\n**Last 30 Days**\n: Calculate the _Inst/Week_ or the _Inst/Day_. The current day counts as 1 day,\nso this is from 29 days ago to the current day.\n\n**Last 90 Days**\n: Calculate the _Inst/Month_, _Inst/Week_, or _Inst/Day_. The current day counts\nas 1 day, so this is from 89 days ago to the current day.\n\n**Last 180 Days**\n: Calculate the _Inst/Month_ or _Inst/Week_. The current day counts as 1 day, so\nthis is from 179 days ago to the current day.\n\n**Last Year**\n: Calculate the _Inst/Month_ or _Inst/Week_. The current day counts as 1 day, so\nthis is from 364 days (365 for a leap year) ago to the current day.\n\n## Items View\n\nHover over the status you're interested in, from either the _Pending_ or the\n_Completed_ tabs (on SP1, these tabs were renamed to _Dashboard_ and\n_Completion_). Click into the All Items screen from the overview report and\na more detailed table appears, including the following columns:\n\n**ID**\n: This is the workflow item's numeric identifier to the system. Importantly, you\ncan click this to enter the Item Detail view.\n\n**Item Subject**\n: This shows a human readable summary of the item, to help administrators\nidentify the item.\n\n**Process Step**\n: This identifies where the item is in the workflow.\n\n**Created By**\n: This shows the user name of the submitting User.\n\n**Creation Date**\n: This shows the date and time the item was submitted to the workflow.\n\nThe All Items view can be filtered so you can find the subset of items you want\nto analyze.\n\n### Filtering by SLA Status\n\nFilter items based on whether they're Overdue, On Time, or Untracked.\n\n![Figure 4: Filter by SLA status: Overdue, On Time, or Untracked.](../../images/workflow-metrics-reports4.png)\n\n**Overdue**\n: Overdue items have breached at least one SLAs defined deadline.\n\n**On Time**\n: On Time items have not breached _any_ SLA deadline.\n\n**Untracked**\n: Untracked items are items in the workflow process that aren't currently under\nthe purview of an SLA. The can be in a task identified as a _Pause_ in the SLA,\nor perhaps outside the scope of the SLA entirely, if the SLA isn't defined for\nthe entire process (Process Begins to Process Ends in the SLA Definition\nscreen).\n\n### Filtering by Process Status and Completion Period\n\nFilter items based on whether they're Pending or Completed in the workflow\nprocess.\n\nIf you filter by the Completed status, you'll get an additional filtering\noption: filter items by the Completion Period. Select from these time periods:\n\n- Today\n- Yesterday\n- Last 7 Days\n- Last 30 Days (default)\n- Last 90 Days\n- Last 180 Days\n- Last Year\n- All Time\n\n![Figure 5: Filter by Process Status and Completion Period.](../../images/workflow-reports-process-status-period.png)\n\n### Filtering by Process Step\n\nFilter items based on where they are in the workflow definition. For example, in\nthe Single Approver workflow process, you can choose to see a report including\nall items in the Review task. This is different for each workflow definition.\n\n### Combining Filters\n\nUse a combination of filters to find just the items you need to see. For\nexample, below are all items in the Single Approver process's Review task that\nhave the status Completed or Pending, whether On time or Overdue. Untracked\nitems aren't shown.\n\n![Figure 6: Combine filters to see just the items you want.](../../images/workflow-metrics-reports13.png)\n\n## Item Details\n\nTo see the metrics for a single workflow process item, click the ID field while\nin the All Items view. A pop-up shows you more detailed information on the item.\n\n![Figure 7: Item Details include SLA status information and whether the item is Resolved or Open.](../../images/workflow-reports-item-detail.png)\n\nFrom here you can view detailed information about the asset and even click *Go\nto Submission Page*, which redirects you to the item's view in the Submissions\nsection of the Control Panel.\n\nThe top of the Item Detail view is important. It shows you the information about\nthe due date for the item in the SLA, and its SLA completion status: _Open_ or\n_Resolved_.\n\n**Open**\n: The defined SLA goals are not yet met. Open items can be of status Overdue or\nOn Time.\n\n**Resolved**\n: The defined SLA goals are completed. Resolved items can be of status Overdue\nor On Time.\n\nFrom the overall metrics of a workflow process down to the details on a single\nitem in the workflow, the new Workflow Metrics functionality gives you insights\ninto the time it takes to _get things done_ in @product@.\n"
  },
  {
    "path": "en/user/articles-dxp/140-workflow/07-workflow-designer/01-intro.markdown",
    "content": "---\nheader-id: workflow-designer\n---\n\n# Workflow Designer\n\n[TOC levels=1-4]\n\nWith the proper permissions, users can publish assets. Even if your enterprise\nhas the greatest employees in the world, many of the items they want to publish\nmust still be reviewed, for a variety of reasons. The Workflow Designer lets you\ndesign workflow definitions so your assets go through a review process before\npublication.\n\nWith the Workflow Designer, you develop workflow definitions using a convenient\ndrag and drop user interface. You don't need to be familiar with writing XML\ndefinitions by hand. Some of the features can be enhanced, however, if you're\nfamiliar with Groovy, a supported Java-based scripting language. All that is to\nsay, don't be scared off when you come to a block of code in these articles.\nJust decide if you need the feature and find someone familiar with Java or\nGroovy to help you out.\n\n| **Note:** By default, only one workflow definition is installed: the Single\n| Approver Workflow definition. What you might not know is that you have access to\n| several others too. Look in `[Liferay_Home]/osgi/marketplace/` and find the\n| `Liferay Forms and Workflow - Liferay Portal Workflow - Impl.lpkg`. Open it,\n| find the `com.liferay.portal.workflow.kaleo.runtime.impl-[version].jar`, and\n| look in its `META-INF/definitions` folder. You'll see the following workflow\n| definitions:\n| \n|     category-specific-definition.xml\n|     legal-marketing-definition.xml\n|     single-approver-definition.xml\n|     single-approver-definition-scripted-assignment.xml\n| \n| To work with any of these definitions in the Workflow Designer, extract them\n| from the JAR file first. Once you have the XML files locally,\n| \n| 1.  Add a new workflow. Go to Control Panel &rarr; Workflow &rarr;\n| Process Builder, and click the Add button\n| (![Add](../../../images/icon-add.png)).\n| \n| 2.  Go to the Source tab.\n| \n| 3.  Click _import a file_ and upload the XML file.\n| \n| 4.  Name the definition appropriately, and click either *Save* (to save it as a\n|     draft) or *Publish* (see below for more information on saving and\n|     publishing).\n| \n| Now you can begin exploring or modifying the definition.\n\nIt's time to start exploring the Workflow Designer and its features.\n"
  },
  {
    "path": "en/user/articles-dxp/140-workflow/07-workflow-designer/02-managing-workflow-with-designer.markdown",
    "content": "---\nheader-id: managing-workflows-with-workflow-designer\n---\n\n# Managing Workflows with the Workflow Designer\n\n[TOC levels=1-4]\n\nThe Workflow Designer gives you an intuitive interface for creating workflow\ndefinitions, from the simplest approval processes to the most complex business\nprocesses you can imagine. It features a drag and drop interface, workflow\ndefinition versioning, and a graphical representation of definitions and their\nnodes. Without the Workflow Designer, you'd have to hand-craft your entire\nworkflow definition in XML. With the Workflow Designer, you might never need to\nlook at a single line of XML. Of course, the Workflow Designer also lets you\ndirectly manipulate the XML (using the *Source* tab) if you find it convenient.\n\n## Adding New Workflow Definitions with the Workflow Designer\n\nAccess the Workflow Designer by going to the Control Panel &rarr; Workflow\n&rarr; Process Builder. Click the Add icon\n(![Add](../../../images/icon-add.png)).\n\nGive the workflow definition a title and you're ready to start designing your\nworkflow.\n\n![Figure 1: The Workflow Designer's graphical interface makes designing workflows intuitive.](../../../images-dxp/workflow-designer-canvas.png)\n\n## Saving and Publishing Workflow Definitions\n\nFirst, look below the canvas to see the buttons that let you *Save* or\n*Publish*. Saving the definition as a draft lets you save your work so it's not\nlost (due to a timeout, for example). It won't be published (and assignable to\nassets), and it won't be considered a version until the Publish button is\nclicked. Each time you save the workflow as a draft, a new revision is added to\nthe Revision history. To see the Revision history and manage workflow versions,\nopen the Info sidebar (![Information](../../../images/icon-information.png)) and\nclick *Revision History*.\n\n![Figure 2: View a list of the current workflows that can be edited in the Workflow Designer.](../../../images-dxp/workflow-designer-definitions.png)\n\n## Adding Nodes\n\nA new workflow is already populated with a start node, an end node, and a\ntransition between them. To make the workflow the way you want it, add nodes to\nthe workflow. \n\n1. *Drag* a node from the *Nodes* palette on the right of the designer and\n   *drop* it on the canvas.\n\n2. You'll see it's not connected to other nodes by a transition, so right now it\n   can't be used in the workflow. Delete the existing transition and then you\n   can make new transitions to direct the *flow* of your workflow (see more\n   about transitions below if you're not sure what they're for or how to use\n   them in the Workflow Designer).\n\nAlternatively, start by deleting the default transition, then click the edge of\nthe start node, drag a new transition from the start node to a blank spot on the\ncanvas, and release it. You're prompted to create a node at that spot, because\nyou can't have a transition without a starting point and an ending point on\na node.\n\n![Figure 3: You can add a node by creating a transition that ends at a blank spot on your Designer canvas.](../../../images-dxp/workflow-designer-add-node.png)\n\nThat's it. Of course, if you drag, say, a *Task* node onto the canvas, it must\nbe configured.\n\n## Node Settings\n\nNow you know how to add nodes to the workflow definition. By default you have\nthree things added to your canvas: a start node, a transition, and an end node.\nThink of the *EndNode* as the point in the workflow where an asset reaches the\n*Approved* status. The *StartNode* is where the asset goes from the *Draft*\nstatus to *Pending*. You might decide to name your nodes to reflect what's\nhappening in each one. To name a node, double click it, and its *Settings*\nappear. Then double click the value of the *Name* property and you can edit the\nname. Click *Save* when you're done. \n\n![Figure 4: You can edit a node's settings.](../../../images-dxp/workflow-designer-node-settings.png)\n\nOf course, there's more you can do besides changing node names. Actions,\nNotifications, and Assignments can be used to make your workflow definition\nuseful and interactive. Keep reading to learn about these features.\n\n## Related Topics\n\n[Kaleo Forms](/docs/7-2/user/-/knowledge_base/u/kaleo-forms)\n\n[Activating Workflow](/docs/7-2/user/-/knowledge_base/u/activating-workflow)\n\n[Liferay's Workflow Framework](/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework)\n\n[Dynamic Data Lists](/docs/7-2/user/-/knowledge_base/u/dynamic-data-lists) \n\n"
  },
  {
    "path": "en/user/articles-dxp/140-workflow/07-workflow-designer/03-workflow-designer-nodes.markdown",
    "content": "---\nheader-id: workflow-definition-nodes\n---\n\n# Workflow Definition Nodes\n\n[TOC levels=1-4]\n\nOnce you know the basics of [creating workflow definitions](/docs/7-2/user/-/knowledge_base/u/workflow-designer)\nwith the workflow Designer, you can get into the details. In this tutorial\nyou'll learn about Actions and Notifications, two important features your\nworkflow nodes can use. You'll also learn how to affect the processing of the\nworkflow using Transitions, Forks, Joins, and Conditions.\n\nThere are several node types you can use in workflow definitions:\n\n- Task nodes\n- Fork and Join nodes\n- Condition nodes\n- Start nodes\n- End nodes\n- State nodes\n\nBecause they're the most complex node, and often the meat of your workflow\ndefinitions, Task nodes are covered [separately](/docs/7-2/user/-/knowledge_base/u/creating-tasks-in-workflow-designer).\n\nFork, Join, and Condition nodes are discussed, along with Transitions, in a\ntutorial on [workflow processing](/docs/7-2/user/-/knowledge_base/u/affecting-the-processing-of-workflow-definitions),\nsince they're used for affecting the processing of the workflow.\n\nThis tutorial discusses State nodes, Start nodes, and End nodes, along with\nActions and Notifications.\n\n## Node Actions and Notifications\n\nAny node can have Actions and Notifications.\n\n### Actions\n\nActions do additional processing before entering the node, after exiting\na node, or once a task node is assigned. They're configured by accessing\na node's Properties tab, then double clicking *Actions*.\n\n![Figure 1: You can add an Action to a Task node.](../../../images-dxp/workflow-designer-action.png)\n\nThe Single Approver workflow contains an Update task with an action written in\nGroovy that sets the status of the asset as *denied*, then sets it to *pending*. \n\n    import com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil;\n    import com.liferay.portal.kernel.workflow.WorkflowConstants;\n\n    WorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus(\"denied\"), workflowContext);\n    WorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus(\"pending\"), workflowContext);\n\nWhy would the action script first set the status to one thing and then to\nanother like that? Because for some assets, the *denied* status sends the asset\ncreator an email notification that the item has been denied.\n\nThe end node in your workflow definition has an action configured on it by\ndefault, on entry to the end node:\n\n    import com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil;\n    import com.liferay.portal.kernel.workflow.WorkflowConstants;\n\n    WorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus(\"approved\"), workflowContext);\n\nThis is a Groovy script that updates the status to *approved*, since that's\nusually the goal of a workflow process.\n\nYou can do something simple like the actions above, or you can be as creative as\nyou'd like.\n\nIt's good to assign a task to a user, and it's even more useful if the user\nis notified about workflow tasks.\n\n### Notifications\n\nNotifications are sent to tell task assignees that the workflow needs attention\nor to update asset creators on the status of the process. They can be sent for\ntasks or any other type of node in the workflow. To set up notifications,\ndouble click on *Notifications* in a node's Properties tab and create\na notification.\n\n![Figure 2: You can send a Notification from a Task node.](../../../images-dxp/workflow-designer-notification.png)\n\nYou must specify the Notification Type, and you can choose User Notification or\nEmail. You can use Freemarker if you need a template, or you can choose to\nwrite a plain text message.\n\n| **Note:** Instant Messenger and Private Message also appear as Notification\n| Type options, but these are non-functional and will be removed in a future\n| version.\n\nHere's a basic Freemarker template that reports the name of the asset creator and\nthe type of asset in the notification:\n\n    ${userName} sent you a ${entryType} for review in the workflow.\n\nYou can also choose to link the sending of the notification to entry into the\nnode (On Entry), when a task is assigned (On Assignment), or when the workflow\nprocessing is leaving a node (On Exit). You can configure multiple notifications\non a node.\n\nCommonly, the assignment and notification settings are teamed up so a user\nreceives a notification when assigned a task in the workflow. To do this,\nchoose *Task Assignees* under Recipient Type when configuring the notification.\n\n| **Note:** The _from name_ and _from address_ of an email notification are\n| configurable via portal properties. Place these settings into a\n| `portal-ext.properties` file, in your Liferay Home folder. Then\n| restart the server:\n| \n|     workflow.email.from.name=\n|     workflow.email.from.address=\n| \n| These can also be set programmatically into the `WorkflowContext`, and the\n| programmatic setting always takes precedence over the system scoped portal\n| property.\n\n## Start and End Nodes\n\nStart and end nodes kick off the workflow processing and bring the asset to its\nfinal, approved state. Often you can use the default start and end nodes without\nmodification. If you want to do some more processing (in the case of a start\nnode), add an action to the node using the Properties tab as described in the\nsection on Actions above.\n\nEnd nodes have a default action that sets the workflow status to Approved using\nthe Groovy scripting language:\n\n    import com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil;\n    import com.liferay.portal.kernel.workflow.WorkflowConstants;\n\n    WorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus(\"approved\"), workflowContext);\n\nAdd more to the action script if you need to do additional processing.\n\nBy default, there's a transition connecting the start node and end node, but\nyou'll probably want to delete it, since most workflows don't proceed straight\nfrom the initial state to approved.\n\n## State Nodes\n\nState nodes can have Notifications and Actions. The default end node added by\nworkflow Designer is a pre-configured state node that sets the workflow status to\nApproved. Perhaps you want to create a node that sets the status to *Expired*.\nYou could create a state node for it by dragging one onto your workflow Designer\ncanvas, then configuring an action in it that sets the status to Expired. Here's\nwhat it would look like in Groovy:\n\n    import com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil;\n    import com.liferay.portal.kernel.workflow.WorkflowConstants;\n\n    WorkflowStatusManagerUtil.updateStatus(WorkflowConstants.getLabelStatus(\"expired\"), workflowContext);\n\nNext, you'll learn to do parallel processing using fork and join nodes.\n\n## Related Topics\n\n[Kaleo Forms](/docs/7-2/user/-/knowledge_base/u/kaleo-forms)\n\n[Activating Workflow](/docs/7-2/user/-/knowledge_base/u/activating-workflow)\n\n[Liferay's Workflow Framework](/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework)\n\n[Dynamic Data Lists](/docs/7-2/user/-/knowledge_base/u/dynamic-data-lists) \n\n"
  },
  {
    "path": "en/user/articles-dxp/140-workflow/07-workflow-designer/04-workflow-designer-workflow-processing.markdown",
    "content": "---\nheader-id: affecting-the-processing-of-workflow-definitions\n---\n\n# Affecting the Processing of Workflow Definitions\n\n[TOC levels=1-4]\n\nWorkflow definitions all contain nodes: a Start Node, and End node, and at least\none Task node. You might know that for the workflow to progress from one node to\nthe other, you need Transitions. In this tutorial you'll learn about using\ntransitions to move the asset through the workflow from node to node, but you'll\nalso learn about some other features that affect the processing of the workflow. \n\n- Transitions\n- Forks\n- Joins\n- Conditions\n\nStart by learning about the ever important Transition.\n\n## Transitions\n\nWhat are transitions? Workflow transitions connect one node to another. On\nexiting the first node, processing continues to the node pointed to by the\ntransition. Every time you create an arrow from one node to another, Workflow\nDesigner creates a transition. \n\n![Figure 1: You connect nodes and direct workflow processing with transitions. The Single Approver workflow has transitions named Submit, Resubmit, Reject, and Approve.](../../../images-dxp/workflow-designer-single-approver.png)\n\nEach node you add has a pop-up menu letting you delete the node. As you\nhover your mouse over the edges of a node, notice your mouse pointer changes to\na cross. The cross indicates you can connect the current node to another node.\nHold down your mouse button and drag the mouse to start drawing your transition\nto another node. If you stop before reaching the edge of the next node, a pop-up\ndisplays node types you can create and connect to on-the-fly. To connect with an\nexisting node, continue dragging the connector to that node.\n\nWhen developing workflows in the Workflow Designer, make sure you go through all the\ntransitions and name them appropriately. By default, these transitions get\nsystem generated names, so rename them to something more human-readable, as\nthey're displayed to workflow users as links that send the item to the next step\nin the workflow.\n\n![Figure 2: In the Single Approver workflow, a user in the Review task can choose to Approve or Reject the asset, which sends the asset either to the EndNode or to the Update task.](../../../images-dxp/workflow-designer-transition-link.png)\n\nTo rename transitions, click on the arrow representing the transition\nand use the Properties tab to set the name just like you do for a node.\n\n## Forks and Joins\n\nSometimes you don't need to wait for one task to be completed before moving on\nto another one. Instead, you want to do two or more things at the same time. To\ndo this, transition to a fork node, make two transitions from the fork to\nyour parallel tasks, and then come back together using a join node.\n\n![Figure 3: Forks and Joins are used to enable parallel processing in the workflow.](../../../images-dxp/workflow-designer-fork-join.png)\n\nWith a regular Join node, for the workflow to proceed beyond the join, the\ntransition from both parallel executions must be invoked. However, if you use a\nJoin XOR node instead, the workflow proceeds as long as the transition from one\nof the parallel executions is invoked.\n\nKeep in mind that you must balance your fork and join nodes. In other words, for\nevery fork, there must be a join that brings the parallel workflow threads back\ntogether.\n\n## Conditions\n\nSometimes you must inspect an asset or its execution context, and depending on\nthe result, send it to the appropriate transition. You need a node for a script\nthat concludes by setting a value to one of your transitions.\n\n![Figure 4: The Category Specific Approval definition starts with a Condition node.](../../../images-dxp/workflow-designer-cat-specific-condition.png)\n\nFrom the *Category Specific Approval* (`category-specific-definition.xml`),\nthis is the script in the condition node that starts the workflow (coming\ndirectly from the start node):\n\n    import com.liferay.asset.kernel.model.AssetCategory;\n    import com.liferay.asset.kernel.model.AssetEntry;\n    import com.liferay.asset.kernel.model.AssetRenderer;\n    import com.liferay.asset.kernel.model.AssetRendererFactory;\n    import com.liferay.asset.kernel.service.AssetEntryLocalServiceUtil;\n    import com.liferay.portal.kernel.util.GetterUtil;\n    import com.liferay.portal.kernel.workflow.WorkflowConstants;\n    import com.liferay.portal.kernel.workflow.WorkflowHandler;\n    import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;\n\n    import java.util.List;\n\n    String className = (String)workflowContext.get(WorkflowConstants.CONTEXT_ENTRY_CLASS_NAME);\n\n    WorkflowHandler workflowHandler = WorkflowHandlerRegistryUtil.getWorkflowHandler(className);\n\n    AssetRendererFactory assetRendererFactory = workflowHandler.getAssetRendererFactory();\n\n    long classPK = GetterUtil.getLong((String)workflowContext.get(WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));\n\n    AssetRenderer assetRenderer = workflowHandler.getAssetRenderer(classPK);\n\n    AssetEntry assetEntry = assetRendererFactory.getAssetEntry(assetRendererFactory.getClassName(), assetRenderer.getClassPK());\n\n    List<AssetCategory> assetCategories = assetEntry.getCategories();\n\n    returnValue = \"Content Review\";\n\n    for (AssetCategory assetCategory : assetCategories) {\n        String categoryName = assetCategory.getName();\n\n        if (categoryName.equals(\"legal\")) {\n            returnValue = \"Legal Review\";\n\n            return;\n        }\n    }\n\nThis example checks the asset category to choose the processing path, whether to\ntransition to the *Legal Review* task or the *Content Review* task.\n\nYou may be wondering what that `returnValue` variable is. It's the variable that\npoints from the condition to a transition, and its value must match a valid\ntransition in the workflow definition. This script looks up the asset in\nquestion, retrieves its [asset category](/docs/7-2/user/-/knowledge_base/u/defining-categories-for-content),\nand sets an initial `returnValue`. Then it checks to see if the asset has been\nmarked with the *legal* category. If not it goes through *Content Review* (the\ncontent-review task in the workflow), and if it does it goes through *Legal\nReview* (the legal-review task in the workflow).\n\nNow you're equipped with the basic knowledge to design beautiful, effective\nworkflows so that your assets can be properly reviewed before they're published\nin your sites.\n\n## Related Topics\n\n[Kaleo Forms](/docs/7-2/user/-/knowledge_base/u/kaleo-forms)\n\n[Activating Workflow](/docs/7-2/user/-/knowledge_base/u/activating-workflow)\n\n[Liferay's Workflow Framework](/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework)\n\n[Dynamic Data Lists](/docs/7-2/user/-/knowledge_base/u/dynamic-data-lists) \n\n"
  },
  {
    "path": "en/user/articles-dxp/140-workflow/07-workflow-designer/05-workflow-designer-tasks.markdown",
    "content": "---\nheader-id: creating-tasks-in-workflow-designer\n---\n\n# Creating Tasks in the Workflow Designer\n\n[TOC levels=1-4]\n\nTask nodes have several parts and are the most complex parts of a workflow\ndefinition. Unlike other workflow nodes, task nodes have Assignments, because a\nUser is expected to *do something* (often approve or reject the submitted asset)\nwhen a workflow process enters the task node: the assignment specifies who that\nUser is. \n\nCommonly, task nodes contain Notifications, Assignments, and Actions (defined in\nscripts). See more about Notifications and Actions in the article on \n[workflow nodes](/docs/7-2/user/-/knowledge_base/u/workflow-definition-nodes). \nTask nodes and their assignments are more complex and deserve their own article\n(this one).\n\nTo get started, drag and drop a task node on your workflow canvas if you haven't\nalready. Open its Properties and give it a name. Then double click *Actions* in\nthe task's Properties pane.\n\nYou can define a notification (often Task Assignee is appropriate), or write a\nGroovy script defining an action that's triggered for your task.\n\nNext learn about creating Assignments for your task nodes. \n\n## Assignments\n\nWorkflow tasks must be completed by a User. You can choose how you want to\nconfigure your assignments. \n\n![Figure 1: You can add an Assignment to a Task node.](../../../images-dxp/workflow-designer-assignment.png)\n\nYou can choose to add assignments to specific Roles, multiple Roles of a Role\nType (Organization, Site, or regular Role types), to the Asset Creator, to\nResource Actions, or to specific Users. Additionally, you can write a script to\ndefine the assignment.\n\nAssigning tasks to Roles, Organizations, or Asset Creators is a straightforward\nconcept, but what does it mean to assign a workflow task to a Resource Action?\nImagine an *UPDATE* resource action. If your workflow definition specifies the\nUPDATE action in an assignment, anyone who has permission to update the type of\nasset being processed in the workflow is assigned to the task. You can configure\nmultiple assignments for a task.\n\n### Resource Action Assignments\n\n*Resource actions* are operations performed by Users on an application or\nentity. For example, a User might have permission to update Message Boards\nMessages. This is called an UPDATE resource action, because the User can update\nthe resource. If you're still uncertain about what resource actions are, refer\nto the developer tutorial on the \n[permission system](/docs/7-2/frameworks/-/knowledge_base/f/defining-application-permissions)\nfor a more detailed explanation.\n\nTo find all the resource actions that have been configured, you need access to\nthe Roles Admin application in the Control Panel (in other words, you need\npermission for the VIEW action on the Roles resource).\n\n- Navigate to Control Panel &rarr; Users &rarr; Roles.\n- Add a new Regular Role. See the \n  [article on managing roles](/docs/7-2/user/-/knowledge_base/u/roles-and-permissions)\n  for more information.\n- Once the Role is added, navigate to the Define Permissions interface for the\n  Role.\n- Find the resource whose action you want to use for defining your workflow\n  assignment.\n\nHow do you go from finding the resource action to using it in the workflow? Use\nthe Workflow Designer's interface for setting up a resource action assignment.\n\nWhen configuring your task node's Assignment, select Resource Actions as the\nAssignment Type, then specify the Resource Actions to use for the assignment\n(for example, UPDATE).\n\n![Figure 2: Configure resource action assignments in the Workflow Designer.](../../../images-dxp/workflow-designer-resource-action-assignment.png)\n\nHere's what the assignment looks like in the Source (Workflow XML) tab:\n\n    <assignments>\n        <resource-actions>\n            <resource-action>UPDATE</resource-action>\n        </resource-actions>\n    </assignments>\n\nAs usual, assign the workflow to the appropriate workflow enabled asset.\n\nNow when the workflow proceeds to the task with the resource action assignment,\nUsers with `UPDATE` permission on the resource (for example, Message Boards\nMessages) are notified of the task and can assign it to themselves (if the\nnotification is set to Task Assignees). Specifically, Users see the tasks in\ntheir *My Workflow Tasks* application under the tab *Assigned to My Roles*.\n\nUse all upper case letters for resource action names. Here are some common\nresource actions:\n\n    UPDATE\n    ADD\n    DELETE\n    VIEW\n    PERMISSIONS\n    SUBSCRIBE\n    ADD_DISCUSSION\n\nYou can determine the probable resource action name from the permissions screen\nfor that resource. For example, in Message Boards, one of the permissions\ndisplayed on that screen is *Add Discussion*. Convert that to all uppercase and\nreplace the space with an underscore, and you have the action name. \n\n### Scripted Assignments\n\nYou can also use a script to manage the assignment. Here's the script for the\nReview task assignment in the Scripted Single Approver workflow definition\n(`single-approver-definition-scripted-assignment.xml`):\n\n    import com.liferay.portal.kernel.model.Group;\n    import com.liferay.portal.kernel.model.Role;\n    import com.liferay.portal.kernel.service.GroupLocalServiceUtil;\n    import com.liferay.portal.kernel.service.RoleLocalServiceUtil;\n    import com.liferay.portal.kernel.util.GetterUtil;\n    import com.liferay.portal.kernel.workflow.WorkflowConstants;\n\n    long companyId = GetterUtil.getLong((String)workflowContext.get(WorkflowConstants.CONTEXT_COMPANY_ID));\n\n    long groupId = GetterUtil.getLong((String)workflowContext.get(WorkflowConstants.CONTEXT_GROUP_ID));\n\n    Group group = GroupLocalServiceUtil.getGroup(groupId);\n\n    roles = new ArrayList<Role>();\n\n    Role adminRole = RoleLocalServiceUtil.getRole(companyId, \"Administrator\");\n\n    roles.add(adminRole);\n\n    if (group.isOrganization()) {\n        Role role = RoleLocalServiceUtil.getRole(companyId, \"Organization Content Reviewer\");\n\n        roles.add(role);\n    }\n    else {\n        Role role = RoleLocalServiceUtil.getRole(companyId, \"Site Content Reviewer\");\n\n        roles.add(role);\n    }\n\n    user = null;\n\t\t\t\t\t\t\nDon't let all that code intimidate you. It's just assigning the task to the\n*Administrator* Role, then checking whether the *group* of the asset is an\nOrganization and assigning it to the *Organization Content Reviewer* Role if it\nis. If it's not, it's assigning the task to the *Site Content Reviewer* Role.\n\nNote the `roles = new ArrayList<Role>();` line above. In a scripted assignment,\nthe `roles` variable is where you specify any Roles the task is assigned to. For\nexample, when `roles.add(adminRole);` is called, the Administrator role is added\nto the assignment.\n\n## Related Topics\n\n[Kaleo Forms](/docs/7-2/user/-/knowledge_base/u/kaleo-forms)\n\n[Activating Workflow](/docs/7-2/user/-/knowledge_base/u/activating-workflow)\n\n[Liferay's Workflow Framework](/docs/7-2/frameworks/-/knowledge_base/f/the-workflow-framework)\n\n[Dynamic Data Lists](/docs/7-2/user/-/knowledge_base/u/dynamic-data-lists) \n\n"
  },
  {
    "path": "en/user/articles-dxp/140-workflow/08-using-kaleo-forms.markdown",
    "content": "---\nheader-id: kaleo-forms\n---\n\n# Kaleo Forms\n\n[TOC levels=1-4]\n\nBusiness processes are often form-based and workflow-driven. They start with \nentered data and progress by sending that data to other people or groups. Then\nit's processed in some way (for example, further data is entered or approval is\ngranted), and the process moves on until completion, when each interested party\nhas seen and manipulated the data. To write an app for each of these processes\nis laborious. It's much better to have a tool for quickly defining a process to\nsuit each use case. The process architect must define both the data that gets\ncollected and the process the data moves through to reach its final state. To\naccomplish this, @product@ already includes the\n[Dynamic Data Lists app](/docs/7-2/user/-/knowledge_base/u/creating-data-definitions)\nfor defining forms, and the \n[Workflow Designer app](/docs/7-2/user/-/knowledge_base/u/workflow-designer) \nfor designing workflows. The Kaleo Forms solution combines the features of these\napps, letting you use a single UI to design an integrated process for sending \nforms through a workflow. \n\n## Creating Kaleo Forms Process\n\nTo start creating a Kaleo Forms Process you need to get to Kaleo Forms Admin: \n\n1.  Go to *Site Administration* (your Site's menu) &rarr; *Content & Data* &rarr; \n    *Kaleo Forms Admin*. The Kaleo Forms app appears with a list of any defined \n    processes. \n\n2.  Click the *Add* button \n    (![Add](../../images/icon-add.png)) \n    to open the New Process Wizard. \n\n3.  Give the process a name and a description and click *Next*. \n\n    ![Figure 1: Add a Kaleo Forms Process to link a form with a workflow definition.](../../images-dxp/kaleo-forms-add.png)\n\n4.  Define the fields that can appear in your process's forms. There are two \n    ways to do this: \n\n    -   Use an existing field set. Click the field set's Actions \n        button \n        (![Actions](../../images/icon-actions.png)) \n        and select *Choose*.\n\n    -   Create a new field set/data definition. Click the \n        *Add Field Set* button. If you need help with this, see the \n        documentation on\n        [creating data definitions](/docs/7-2/user/-/knowledge_base/u/creating-data-definitions). \n        After creating the field set, select it as you would an existing field \n        set. \n\n    Click *Next* to move to the wizard's next step. \n\n    ![Figure 2: Define and choose your form's fields.](../../images-dxp/kaleo-forms-fields.png)\n\n5.  Select a workflow to use for your forms. To do this, click the workflow's \n    *Actions* button \n    (![Actions](../../images/icon-actions.png)) \n    and select *Choose*. \n\n    You can also edit an existing workflow or create a new one: \n\n    -   To edit a workflow, click its *Actions* button \n        (![Actions](../../images/icon-actions.png)) \n        and select *Edit*. \n\n    -   To begin creating a new workflow, click the *Add Workflow* button. \n\n    In either case, you use the same UI to edit/create the workflow. This UI is \n    called \n    [Workflow Designer](/docs/7-2/user/-/knowledge_base/u/workflow-designer). It \n    lets you create your workflow graphically instead of via code. \n\n    Once you select a workflow to use with your forms, click *Next*. \n\n    ![Figure 3: This example workflow has three tasks that happen sequentially.](../../images-dxp/kaleo-forms-spa-order-definition.png)\n\n6.  Select or create a form to use for each workflow task. To do this, click \n    each task's *Actions* button \n    (![Actions](../../images/icon-actions.png)) \n    and select *Assign Form*. On the screen that appears, select an existing \n    form or click the *Add* button \n    (![Add](../../images/icon-add.png)) \n    and to create one. \n\n    Click *Save* when you're finished. Your process is done and appears in Kaleo \n    Forms Admin's table. \n\n    ![Figure 4: Assign a form to each task in the workflow, and for the initial state.](../../images-dxp/kaleo-forms-task-forms.png)\n\n## Adding Records to a Process\n\nTo add records to a process, click it in Kaleo Forms Admin and then click the \n*Add* button \n(![Add](../../images/icon-add.png)). \nThis brings up the form you assigned to the workflow's initial state. Fill it \nout and click *Save*. Once submitting the initial form, the workflow engine then \ntakes over and moves through each task in the workflow. Whatever Users or Roles \nyou assigned to the tasks receive notifications, and the task appears in the \n*Assigned to My Roles* section of the My Workflow Tasks app. A notification also \nappears in the Notifications app. Once in the task, the user views and approves \nthe form or clicks the *Edit* button. At this point, the workflow task forms you \ncreated come into play. Each assigned user fills out the form, saves it, and \nsends it along in the workflow. \n"
  },
  {
    "path": "en/user/articles-dxp/160-segmentation-and-personalization/05-analaytics-cloud-segmentation.markdown",
    "content": "---\nheader-id: using-analytics-cloud-with-user-segments\n---\n\n# Using Analytics Cloud With User Segments\n\n[TOC levels=1-4]\n\nTo use Analytics Cloud with User Segments, you must first connect your DXP data\nsource to Analytics Cloud and enable synchronization of users and analytics. For\nmore information about Analytics Cloud, including instructions for connecting\nit with DXP, see the official\n[Analytics Cloud Documentation](https://learn.liferay.com/analytics-cloud/latest/en/connecting-data-sources/connecting-liferay-dxp-to-analytics-cloud.html).\n\nOnce you're connected to Analytics Cloud, you can create Segments to analyze\nuser behavior.\n\n## Creating a New Segment\n\nSynchronization with Analytics Cloud is not instant, so once you have connected\nAnalytics Cloud and Liferay DXP, you must first wait for the users and data to\nsynchronize. After that completes, you can create Segments in Analytics Cloud to\ncapture data in DXP.\n\nOnly Segments that contain at least one member are synchronized with Liferay\nDXP. This means that empty Segments created with Analytics Cloud are unavailable\nto use on Liferay DXP.\n\nSee Analytics Cloud's documentation on\n[creating Segments](https://help.liferay.com/hc/en-us/articles/360028721412-Creating-User-Segments)\nfor more information.\n\nNext, you can use your new Segment to define behaviors on your server.\n\n## Getting Segment Analytics\n\nAfter you create and sync a Segment in Analytics Cloud, you can view it and\ncustomize it in @product@.\n\n1.  Go to the *Segments* page.\n\n2.  Click on the new Segment.\n\n![Figure 1: When you see Analytics Cloud Segments in the list of Segments, they are marked with the Analytics Cloud icon.](../../images/segments-ac-list-item.png)\n\nAnalytics are based on the criteria that you set on Analytics Cloud,\nbut you can set additional criteria here to use this Segment for\npersonalization in DXP. Changing the Segment criteria here doesn't affect the\ngathered analytics data, unless it is configured in some way that restricts its\nmembers from viewing content that you are using as an Analytics Cloud criteria.\n\nWhen you put it all together to provide personalized experiences and analyze\nuser behavior, you can see the true power of Segmentation."
  },
  {
    "path": "en/user/articles-dxp/160-segmentation-and-personalization/08-recommending-content-based-on-view-history.markdown",
    "content": "---\nheader-id: recommending-content-based-on-user-behavior\n---\n\n# Recommending Content Based on User Behavior\n\n[TOC levels=1-4]\n\n| **Note:** A/B Testing is available for Liferay DXP 7.2 SP1+.\n\nA site's content generates clicks from users. For example, if someone visits\na sporting goods store's site and clicks on several hunting promotional ads, you\ncan deduce an interest in hunting products and can promote this type\nof content when this user visits the site again.\n\nAccomplishing this with\n[Segment-based personalization](/docs/7-2/user/-/knowledge_base/u/creating-user-segments)\nis possible, but that method is really targeted for vertical specific messaging\nor content with a preconceived audience. You may have to create hundreds of\nSegments to target all combinations of customer use cases. Instead, you need an\ninfrastructure that tracks user views and displays the appropriate content based\non behavior. You can accomplish this with *Content Recommendation*.\n\nThis is done by adding tags to content/Pages and monitoring the users who visit\nthem. When a user views a specific content type or Page, its tags are attached\nto that user as *interests*. When the user visits other pages, content that\nmatches their interests is displayed to them. The monitoring process is\nfacilitated by\n[Analytics Cloud](https://help.liferay.com/hc/en-us/articles/360006608732-Generating-New-Business-Using-Analytics),\nso you must have your DXP instance synced with it. If you haven't done this\nyet, start by\n[adding your DXP instance as a data source](https://help.liferay.com/hc/en-us/articles/360006653472-Adding-a-Liferay-DXP-Data-Source).\n\nOnce your DXP instance is synced with Analytics Cloud and you're leveraging\nContent Recommendation, a user's interests are viewable by navigating to the\nleft menu &rarr; *Individuals* &rarr; *Interests*.\n\n![Figure 1: A user's interests are stored and accessible from Analytics Cloud.](../../images-dxp/content-interests.png)\n\nYou can learn more about Analytics Cloud's individual analytics\n[here](https://help.liferay.com/hc/en-us/articles/360006946171-Profiling-Individuals).\n\nTo begin recommending content to users, you must\n\n- Add tags to content and/or Pages.\n- Display content based on user behavior.\n\nYou'll step through these processes next.\n\n## Adding Tags to Track User Behavior\n\nTo track user behavior and accumulate their interests, you must add tags to the\ncontent and Pages they visit. First, you'll add tags to web content and\nconfigure it to be viewable using a Display Page Template.\n\n1.  Go to Site Administration &rarr; *Site Builder* &rarr; *Pages* &rarr;\n    *Display Page Templates*. Select the *Add* button\n    (![Add](../../images-dxp/icon-add.png)), give it a name, and click *Save*.\n\n2.  In the right menu, select *Mapping*\n    (![Mapping](../../images-dxp/icon-mapping.png)), choose the *Web Content\n    Article* content type and choose *Basic Web Content* for the subtype. Then\n    click *Save*.\n\n3.  Add a Fragment to the Display Page and map its field to *Basic Web Content*.\n    For example, click *Section Builder* &rarr; *Basic Components* and drag the\n    *Paragraph* Fragment to the page. Then click the *Map* button and select\n    *Basic Web Content* for its Source and Field.\n\n4.  Publish the Display Page Template.\n\n5.  [Begin creating Basic Web Content](/docs/7-2/user/-/knowledge_base/u/creating-web-content).\n    Before publishing the content, navigate to the *Display Page Template*\n    section and select *Specific Display Page Template* from the selector. Then\n    select the Display Page Template your created previously.\n\n6.  Go to the *Metadata* section in the right menu. Assign tags that\n    characterize the content. These are the tags that are referenced as\n    interests when a user views the content. Then click *Publish*.\n\nNow your web content is mapped to a Display Page, which allows the assigned\ntags to be tracked as interests when the web content is clicked. You can alter\nthis process based on the asset types you want to recommend.\n\nYou can also assign tags to a Page's SEO configuration, which would then be\nassigned to users as interests when they visit the Page. Here's how to do this:\n\n1.  Navigate to Site Administration &rarr; *Site Builder* &rarr; *Pages*.\n\n2.  Click the *Actions* button (![Actions](../../images-dxp/icon-actions.png))\n    &rarr; *Configure* for a Page you want to add tags to.\n\n3.  Select *SEO* &rarr; *Categorization* and add relevant tags to the page. Then\n    click *Save*.\n\nAwesome! Now your content and Pages have tags that are assigned to users as\ninterests when they visit them. These interests are assessed when recommending\ncontent, which you'll learn how to leverage next.\n\n## Displaying Content Based on User Behavior\n\nNow that your Site's users' have their interests tracked using tags, you'll want\nto set up an Asset Publisher to display the content based on their behavior.\n\n1.  Navigate to the Site Administration &rarr; *Content & Data* &rarr; *Content\n    Sets*.\n\n2.  Select the *Add* button (![Add](../../images-dxp/icon-add.png)) and click\n    *Dynamic Selection*. Assign a name and click *Save*.\n\n3.  Under the Content Recommendation tab, enable Content Recommendation. Then\n    save the Content Set.\n\n    ![Figure 2: Enable Content Recommendation for your Content Set.](../../images-dxp/enable-content-recommendation.png)\n\n    For more information on Content Sets, see\n    [Creating Content Sets](/docs/7-2/user/-/knowledge_base/u/creating-content-sets).\n\n4.  Add an Asset Publisher widget to a Page. Navigate to its *Options*\n    (![Options](../../images-dxp/icon-app-options.png)) &rarr; *Configuration*\n    menu and select the *Content Set* asset selection.\n\n5.  Select the Content Set you want to display. Then click *Save*.\n\nIn a realistic scenario, Content Sets have many assets with differing tags. That\nway, content similar to a user's interests is displayed over other content.\n\nGreat! Now when users have accumulated interests based on views, the Asset\nPublisher only shows content based on their interests.\n"
  },
  {
    "path": "en/user/articles-dxp/160-segmentation-and-personalization/09-ab-testing/01-ab-testing-intro.markdown",
    "content": "---\nheader-id: a-b-testing\n---\n\n# A/B Testing\n\n[TOC levels=1-4]\n\n| **Note:** A/B Testing is available for Liferay DXP 7.2 SP1+.\n\n@product@ can help you hone your messages to customers by periodically testing\ndifferent UX and messaging schemes. You can show different versions of your site\nto different users to see which is more effective. \n\n*A/B Testing* lets you maintain a page's current UX and messaging, but provide\nalternative page variants for a select group of visitors. Then the current page\nand page variant(s) are tested based on algorithms to determine which pages\nperform better for a given goal (e.g., bounce rate, clicks, etc.).\n\nFor example, a Marketing team for a bank provides a Content Page advertising\na new credit card. The page has been published for a few weeks, but a redesign\nmight help promote the new credit card better. With A/B Testing, the team can\ncreate a new page variant and display both pages at random to visitors. Then\nthey can analyze the clickthrough rate for the two pages and find which page is\nmore effective. If the new variant is more effective than the original page,\nthey can publish it and remove the old page.\n\nIf improving your site's UX and messaging is something you're interested in,\ncontinue on to learn more!\n"
  },
  {
    "path": "en/user/articles-dxp/160-segmentation-and-personalization/09-ab-testing/02-enabling-ab-testing.markdown",
    "content": "---\nheader-id: enabling-a-b-testing\n---\n\n# Enabling A/B Testing\n\n[TOC levels=1-4]\n\nBefore creating an A/B test, you must ensure some conditions are met:\n\n- You must have @product@ connected to\n  [Analytics Cloud](https://learn.liferay.com/analytics-cloud/latest/en/index.html). To\n  begin,\n  [add a Data Source](https://learn.liferay.com/analytics-cloud/latest/en/connecting-data-sources.html).\n\n- Your page must be a Content Page, since only Content Pages (not Widget Pages)\n  support Experiences for different Segments.\n\n- The Content Page you intend to test must be published.\n\nIf these conditions are met, A/B Testing is automatically enabled for any\nContent Page you navigate to, assuming you have the proper permissions. You'll\nlearn more about how to assign permissions for A/B Testing next.\n\n## Setting A/B Testing Permissions\n\nTo use all the features of A/B Testing, you must have *Update* permissions for\nthe Content Page.\n\nTo assign *Update* permissions for a Content Page,\n\n1.  Navigate to Site Administration &rarr; *Site Builder* &rarr; *Pages*.\n\n2.  Select the Actions button (![Actions](../../../images-dxp/icon-actions.png))\n    next to the Content Page and click *Permissions*.\n\n3.  Enable the *Update* permission for the Role(s) you want to grant A/B Testing\n    access for and click *Save*.\n\nFor more information, see\n[Page Permissions](/docs/7-2/user/-/knowledge_base/u/changing-page-permissions)\nand [Managing Roles](/docs/7-2/user/-/knowledge_base/u/managing-roles).\n"
  },
  {
    "path": "en/user/articles-dxp/160-segmentation-and-personalization/09-ab-testing/03-creating-ab-tests.markdown",
    "content": "---\nheader-id: creating-a-b-tests\n---\n\n# Creating A/B Tests\n\n[TOC levels=1-4]\n\nTo begin leveraging A/B Testing, you must first create an A/B test. You cannot\ncreate a test on an Experience that already has an active test running.\n\nTo create an A/B test,\n\n1.  Navigate to the Content Page you want to test.\n\n2.  Click the A/B Testing (![AB Testing](../../../images-dxp/icon-ab-testing.png))\n    button from the Control Menu.\n\n3.  Choose the Experience you want to test. This option is only available when\n    you have a custom Experience (an Experience other than the default).\n\n    A test can be performed on the Default Experience as well as a personalized\n    Experience mapped to a Segment. When an Experience is being used in an A/B\n    test, it cannot be edited. Deleting a Page/Experience being used in an A/B\n    test also deletes the test for that Page.\n\n4.  Click *Create Test*.\n\n5.  Assign the test a name and description (optional).\n\n6.  Assign the goal you want the test to track. There are two:\n\n    *Bounce Rate*: the percentage of users who don't exhibit any activities on\n    the page (click, scroll, etc.) and then navigate away from the site\n    without visiting another page.\n\n    *Click*: the percentage of users who clicked on the page (per session).\n\n<!--\n    *Scroll Depth*: the average depth users scrolled down on the page.\n\n    *Time On Page*: the average duration users spent on the page.\n-->\n\n![Figure 1: Fill out the form to create your A/B test.](../../../images-dxp/create-ab-test.png)\n\n7.  Click *Save*.\n\nYou now have an A/B test! Notice that the test's status is *Draft*. This means\nit's not yet visible to visitors.\n\n| **Note:** You can only create an A/B test for one page/experience at a time.\n\n| **Note:** The *Control* entity represents the currently published Content\n| Page.\n\n![Figure 2: You now have an A/B test, but there are additional configurations you can apply.](../../../images-dxp/new-ab-test.png)\n\nYou can always edit or delete the new A/B test by clicking the *Actions* button\n(![Actions](../../../images-dxp/icon-actions.png)) in the top right of the A/B\nTest menu. Deleted tests are not recoverable (i.e., not sent to the Recycle\nBin). These options are not available for an active test. You can also, at any\ntime, view your A/B Testing history by selecting the *History* tab. This\ndisplays all completed and terminated A/B tests.\n\nNow it's time to create your test Variant(s). A test Variant is a customization\nof the Experience you want to optimize. An A/B test must contain at least one\nVariant before it can run.\n\nTo create a Variant,\n\n1.  In the A/B Test menu, click *Create Variant*.\n\n2.  Give the Variant a name and click *Save*.\n\n3.  Select the new Variant's Edit button\n    ![Edit](../../../images-dxp/icon-edit.png). The current Control page's\n    content/formatting is copied and displayed as the baseline for the Variant.\n\n4.  Edit the Variant as desired. Then click *Save Variant*.\n\nYou now have a Variant of the Control Page. You can create as many Variants as\nyou want.\n\nIf you selected the Click goal, you must select the clickable element you want\nto target on the Control and Variant pages. If you selected a different goal,\nyou can skip the steps below.\n\nTo configure the Click goal target,\n\n1.  Click *Set Target* under the Click Goal heading of your A/B Test. Any\n    clickable element on the page is highlighted.\n\n    | **Note:** Only links and buttons with an ID attribute can be selected as a\n    | target for the Click goal.\n\n2.  Select the element you want to set as the click target for your Control and\n    Variant pages.\n\n    ![Figure 3: Set the click target to be tracked.](../../../images-dxp/set-click-target.png)\n\nYour Click goal is now set! You can edit the target at any time before starting\nthe test.\n\n![Figure 4: Once the click target is set, you can run the A/B test.](../../../images-dxp/click-goal-set.png)\n\nOnce you're finished creating Variants and configuring goals for your A/B test,\nyou're ready to run the A/B test. You'll learn how to do this next.\n"
  },
  {
    "path": "en/user/articles-dxp/160-segmentation-and-personalization/09-ab-testing/04-running-ab-tests.markdown",
    "content": "---\nheader-id: running-a-b-tests\n---\n\n# Running A/B Tests\n\n[TOC levels=1-4]\n\nOnce you've created and configured your A/B test, you'll want to run it to begin\ngathering data on your Control Page and Variants. To run an A/B test,\n\n1.  Click the *Review and Run Test* button from the A/B Test menu.\n\n2.  Configure how your test should run. There are two configurations:\n\n    *Traffic Split*: the percentage of visitors that are randomly split between\n    the Variants when visiting the Page. A visitor randomly assigned a Variant\n    always sees the same Variant until the test is finished.\n\n    *Confidence Level Required*: the accuracy of the test results (i.e., when\n    the winning Variant truly outperforms the other Variants). Typically you\n    want to have the highest confidence level possible, but this impacts test\n    duration. The higher the required confidence level, the longer it takes to\n    declare a winning Variant. Choose the percentage based on your expectations.\n\n    The *Estimated Time to Declare Winner* field is also displayed. This is the\n    estimated duration the test runs. This is calculated based on the\n    selected traffic split, confidence level, and projected page traffic. Your\n    page's past traffic history is provided by Analytics Cloud.\n\n    ![Figure 1: Configure the final parameters of your A/B test before running it.](../../../images-dxp/run-ab-test.png)\n\n3.  Select *Run*.\n\nYour A/B test is now running!\n\nWhile an A/B test is running, you have two buttons available to help manage the\ntest:\n\n*Terminate Test*: terminates the test. To delete a test, you must terminate it\nfirst.\n\n*View Data in Analytics Cloud*: redirects you to your A/B Testing dashboard\nhosted on Analytics Cloud. Here you can view your test's traffic, reports,\nstatistics, etc. related to your test. See the\n[Monitoring A/B Test Results](/docs/7-2/user/-/knowledge_base/u/monitoring-a-b-test-results)\narticle for more information.\n\nAwesome! You now have a running A/B test accumulating data based on user\ninteractions with your Page. Next, you'll learn how to monitor your A/B test's\nresults.\n"
  },
  {
    "path": "en/user/articles-dxp/160-segmentation-and-personalization/09-ab-testing/05-monitoring-ab-test-results.markdown",
    "content": "---\nheader-id: monitoring-a-b-test-results\n---\n\n# Monitoring A/B Test Results\n\n[TOC levels=1-4]\n\nThe information from your A/B test created in @product@ is automatically\nsynchronized with\n[Analytics Cloud](https://help.liferay.com/hc/en-us/articles/360006608732). The\ntest results used to calculate the winning Variant based on user interaction\ndata is viewable there. You can also view your A/B testing history, meaningful\nstatistics, helpful graphs, etc. from Analytics Cloud.\n\nTo navigate to your test's Analytics Cloud dashboard from @product@, click the\n*View Data in Analytics Cloud* button in the A/B Test sidebar panel while it's\nrunning.\n\n@product@ only displays your test's status (draft, running, etc.) and the\nwinning Variant once the test finishes. For more information on what you can\nview/manage from Analytics Cloud in relation to your A/B test, see the\n[A/B Testing Analytics](https://help.liferay.com/hc/en-us/sections/360001492292-Analyzing-Touchpoints)\narticle.\n\n<!-- Replace link above with specific AC A/B Testing article when available -->\n"
  },
  {
    "path": "en/user/articles-dxp/160-segmentation-and-personalization/09-ab-testing/06-publishing-ab-test-variants.markdown",
    "content": "---\nheader-id: publishing-a-b-test-variants\n---\n\n# Publishing A/B Test Variants\n\n[TOC levels=1-4]\n\nOnce the A/B test has concluded and Analytics Cloud has computed the test\nresults, the status for the test displays as *Winner Declared* (if a winning\nVariant was found) or *No Winner* in the A/B Test sidebar panel. You're\nalso alerted to the result via a notification, which you can view from the User\nMenu.\n\n![Figure 1: If you're satisfied with the A/B test's results, publish the winning Variant.](../../../images-dxp/ab-testing-winner.png)\n\n| **Note:** When the required confidence level is not met during the time\n| duration, there is no winning Variant.\n\nClick the *Publish* button to publish your winning Variant Experience. Once you\npublish a Variant, the A/B test's status is *Completed* and the test is\nfinished.\n\n![Figure 2: Once you've published a Variant, the A/B test is complete.](../../../images-dxp/ab-test-complete.png)\n\nIf you want to publish a Variant that was not declared a winner, select the\nVariant from the A/B Test panel and click *Publish*. You can select *Discard\nTest* to ignore the A/B test recommendations and keep the currently published\nControl Page.\n\nThe most productive Variant is now available to all users who visit the Page.\n\nAwesome! You successfully ran an A/B test and published the Variant that is\nmost effective for your Site's users.\n"
  },
  {
    "path": "en/user/build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"User Guide\" basedir=\".\">\n\t<property name=\"project.dir\" value=\"../../../liferay-docs\" />\n\n\t<import file=\"../build.xml\" />\n\n\t<property name=\"purpose.dir\" value=\".\"/>\n\t<property name=\"doc.dir\" value=\"user\"/>\n\n</project>\n"
  },
  {
    "path": "guidelines/01-creating-docs-for-liferay.markdown",
    "content": "# Creating Docs for Liferay\n\nIt's a straightforward process to create Liferay documentation. The goal\nis to create docs the developers want to read and to keep the style\nconsistent and clear regardless of author. For these reasons, we've\noutlined the process and procedure below.\n\n## Environment\n\nLiferay Documentation is written in Markdown. The file extension should\nbe `.markdown`. Your text editor of choice can be used as long as you use\nthe following conventions:\n\n-   All files are hard-wrapped at 80 columns\n-   Use spaces instead of tabs because of the way Markdown processes\n    indents\n-   Every sentence, including those at the ends of paragraphs, should\n    have a trailing space\n-   Leave one blank space between headings and the paragraphs that\n    follow\n-   Links should start on their own line and not wrap\n\nMarkdown files are created in a folder structure that is parsed by our\nKnowledge Base importer. The idea is to create a reading environment\nlike this:\n\n![Our site has navigation on the left and articles on the right.](images/ldn-screenshot.png)\n\nThere are three areas to notice: the menu on the left, the article on the right,\nand the list of articles at the bottom right. When a user clicks on a topic,\nthat topic opens. There could be one or several tutorials on that topic. The\nfirst of these is the *intro* file. This is marked in the file system by\nappending -intro to the file name (for example,\n`service-builder-intro.markdown`).\n\nNesting is done by folders, and every folder must have an -intro file.\nIn the example above, therefore, you have this folder structure:\n\n![The folder structure defines the outline.](images/folder-structure.png)\n\nWhen a user, therefore, clicks on Tooling, LDN displays\n00-intro.markdown on the right and expands the navigation on the left.\nWhen a user clicks on Liferay IDE, LDN displays the \n`00-liferay-ide-intro.markdown` file and the navigation is further expanded.\n\nNotice that in the file system, the folders are numbered. This is how\nthe topics are ordered on LDN. The topic titles, however, are taken from\nthe heading in the -intro file, so it's important to put the proper\nheadings in those files.\n\n## Structure\n\nAll [Liferay Developer Network](https://dev.liferay.com) articles reside in\nliferay-docs repository folders that are laid out in a similar manner to\nLDN's pages and articles. Each repository folder under [`discover`](discover),\n[`develop`](develop), and [`distribute`](distribute) represents a section of\nLDN's articles. For example, the [`discover/portal`](discover/portal) folder\ncontains Markdown files and images that are the source for LDN's\n[User & Admin&nbsp;&rarr;&nbsp;User Guide](https://dev.liferay.com/discover/portal)\narticles. \n\nThe following tables show the folder locations for articles published to LDN's\nUser & Admin, Developer, and Marketplace sections.\n\n**Source Folders for Liferay Developer Network Articles:**\n\n ![User & Admin](images/discover.png)                                                                                           |\n :------------------------------------------------------------------------------------------------------------------------------------ |\n  &nbsp;[**liferay-docs/discover/**](discover)                                                                                         |\n  &nbsp;&#8226;&nbsp;[`portal/`](discover/portal) &rarr; [Portal](https://dev.liferay.com/discover/portal)                             |\n  &nbsp;&#8226;&nbsp;[`social-office/`](https://github.com/liferay/liferay-docs/tree/6.2.x/discover/social-office) &rarr; [Social Office](https://dev.liferay.com/discover/social-office) |\n  &nbsp;&#8226;&nbsp;[`deployment/`](discover/deployment) &rarr; [Deployment](https://dev.liferay.com/discover/deployment)             |\n  &nbsp;&#8226;&nbsp;[`reference/`](discover/reference) &rarr; [Reference](https://dev.liferay.com/discover/reference)                 |\n  &nbsp;&#8226;&nbsp;[`new-articles/`](discover/new-articles)  (You can submit new articles here for LDN's *User & Admin* section)         |\n\n  ![Developer](images/develop.png) |\n :-------------------------------------------------------------------------------------------------------------------------------------- |\n  &nbsp;[**liferay-docs/develop/**](develop)                                                                                             |\n  &nbsp;&#8226;&nbsp;[`learning-paths/`](develop/learning-paths) &rarr; [Learning Paths](https://dev.liferay.com/develop/learning-paths) |\n  &nbsp;&#8226;&nbsp;[`tutorials/`](develop/tutorials) &rarr; [Tutorials](https://dev.liferay.com/develop/tutorials)                     |\n  &nbsp;&#8226;&nbsp;[`reference/`](develop/reference) &rarr; [Reference](https://dev.liferay.com/develop/reference)                     |\n  &nbsp;&#8226;&nbsp;[`new-articles/`](develop/new-articles)  (You can submit new articles here for LDN's *Developer* section)             |\n\n  ![Marketplace](images/distribute.png)                                                                                             |\n :------------------------------------------------------------------------------------------------------------------------------------------- |\n  &nbsp;[**liferay-docs/distribute/**](distribute)                                                                                            |\n  &nbsp;&#8226;&nbsp;[`distribute/new-articles/`](distribute/new-articles)  (You can submit new articles here for LDN's *Marketplace* section) |\n\n## High Level Procedure \n\nFirst, you need a copy of the repository on your own machine, and to do that,\nyou must [clone](https://help.github.com/articles/fork-a-repo/)\nthe project. For information on how to work with Git projects, see\n[*How do I use Git and GitHub?*](04-faq.markdown#how-do-i-use-git-and-github). \n\n**To add an article** to LDN's [Develop](https://dev.liferay.com/develop)\nsection (as an example), create your article and its images in the\n`liferay-docs/develop/new-articles` folder. Commit your added files and send a\n[pull request](https://help.github.com/articles/using-pull-requests/) to default\nuser `liferay`. For more information on where to place new articles and for\nguidelines on writing them, see\n[*How do I write and submit my own article?*](04-faq.markdown#how-do-i-write-and-submit-my-own-article). \n\n**To modify an article**, you can edit it locally in its repository location, in\na branch created for that purpose. After you've made your changes, commit them\nand submit a [pull request](https://help.github.com/articles/using-pull-requests/) to the\ndefault user `liferay`. \n\n**To preview an article**, invoke one of the `convert.[bat|sh]` scripts\non your Markdown file. The scripts are in the [`bin`](../bin) folder. See\n[*How do I convert my local Markdown to HTML using the provided convert scripts?*](04-faq.markdown#how-do-i-convert-my-local-markdown-to-html-using-the-provided-convert-scripts)\nfor details on using the script. \n\n## Liferay's Doc Standards and Customizations\n\nAs you write documentation, you'll want to be aware of our standards and\ncustomizations. [Click here](02-standards-and-customizations.markdown)\n"
  },
  {
    "path": "guidelines/02-standards-and-customizations.markdown",
    "content": "# Standards and Customizations \n\nLiferay's documentation is currently implemented in Markdown. We chose Markdown\nfor several reasons:\n\n1. It's readable. Even if you don't know Markdown, you can read it without\n   having to filter out the syntax. \n\n2. It gets out of a writer's way. You don't have to worry about mousing to\n   various icons to change text into a heading or create bulleted lists. Just\n   start typing. The syntax is so intuitive, you probably have used it all your\n   life anyway.\n\n3. It's easily convertable to other formats. Using Markdown lets us publish to\n   the web, mobile, and print from the same source files.\n\n4. It's great for collaborating. Using Github, we can easily see what various\n   people have contributed, through the same diffs we'd use in programming. We\n   can merge those changes pretty easily, too, because the format is simple enough\n   that the changes in the diffs happen to be the actual changes made to the text,\n   rather than a bunch of formatting tags.\n\nIn summary, Markdown is, by definition, a text based format that's designed to\nbe as readable as possible. It lets you write in a very natural format which can\nthen be converted to other formats for publication. We have switched to using\nMarkdown for the Liferay 6.1 documentation. It's allowing us to single-source\nour documentation for the web, for ebooks, and for print.\n\n## Ant Target Quick Reference\n\nEach Ant target described in this section is for Liferay's CE docs only (unless\notherwise noted). Most targets have a DXP version that runs on all CE and DXP\ndocs. Append `-dxp` to the targets listed below when checking/editing DXP-only\ndocumentation.\n\nEach target should be executed from a document directory in the `liferay-docs`\nrepo. For example, `/develop/tutorials`, `/discover/portal`, etc.\n\n<!-- compare-with-older-branch/dist-diffs process is broken. Need to fix before\ndocumenting. -Cody -->\n\n-   `add-article-to-temp`: Copies the specified article (e.g., \n    `-Darticle=articles/path/article_name.markdown`) and its supporting\n    `intro.markdown` articles and images, to a folder called `temp` and prepares\n    them for\n    [importing to a Knowledge Base](https://dev.liferay.com/discover/portal/-/knowledge_base/7-0/informing-users-with-the-knowledge-base#importing-articles-from-markdown-source-files).\n    Optionally use this target\n    to add as many articles as you like  to the `temp` folder for putting in the\n    distribution.\n\n    Here's an example command sequence that prepares a ZIP file of two articles and their supporting files:\n\n    1.  `ant clean-temp` &rarr; Deletes the `temp` folder.\n    2.  `ant add-article-to-temp -Darticle=articles\\100-tooling\\02-liferay-ide\\02-creating-a-liferay-workspace-with-liferay-ide.markdown` \n    3.  `ant add-article-to-temp -Darticle=articles\\02-from-liferay-6-to-liferay-7\\07-upgrading-plugins-to-liferay-7\\04-upgrading-portlet-plugins\\02-upgrading-a-servlet-based-portlet.markdown`\n    4.  `ant dist-temp` &rarr; Zips up  `temp` folder's articles and images into a \n        ZIP file in the `dist` folder, for importing to a Knowledge Base portlet.\n        <br><br>\n\n-   `article-to-html`: Converts a Markdown article to HTML. This target requires\n    a `-Darticle` argument, which should point to your Markdown article (e.g.,\n    `ant article-to-html\n    -Darticle=articles/100-tooling/05-maven/01-installing-liferay-maven-artifacts.markdown`).\n    The generated HTML is located in the document directory's `/build` folder.\n\n-   `check-headers`: Checks all Markdown headers to ensure they properly begin\n  with `#` characters.\n\n-   `check`: Runs several targets at once to make sure Markdown articles follow\n    LDN standards to ensure a successful build and publishing process. This\n    target should be executed before every pull request. This target includes\n    the `check-headers`, `check-images`, `check-intros`, `number-headers`, and\n    `number-images` tasks.\n\n-   `check-article-images`: Checks a specific Markdown article's image paths.\n    If an image path (e.g., `../../images/test-pic.png`) does not point to an\n    existing image in the `/images` folder, an error is thrown. This target\n    requires a `-Darticle` argument, which should point to your Markdown article\n    (e.g., `ant check-article-images\n    -Darticle=articles/100-tooling/05-maven/01-installing-liferay-maven-artifacts.markdown`).\n\n-   `check-images`: Checks all Markdown articles' image paths in the folder. If\n    an image path (e.g., `../../images/test-pic.png`) does not point to an\n    existing image in the `/images` folder, an error is thrown.\n\n-   `check-intros`: Checks all directories for an intro file (e.g.,\n    `00-intro.markdown`). If a folder does not have an intro article, an error \n    is thrown.\n\n-   `clean-images`: (no DXP target) Deletes images from the `images` and\n    `images-dxp` folders that are not used in their corresponding Markdown\n    articles.\n\n-   `check-links`: Checks links on the current checked out liferay-docs branch\n    to ensure all links point to existing header IDs (header IDs are used to\n    formulate links). A list of invalid links are listed if any are found.\n\n    This target can be executed before publishing to the live site since it\n    scans the local liferay-docs repo. This also means that links pointing to\n    articles **not** hosted in the current repo (e.g., a link pointing to 7.0\n    documentation residing in an article hosted on the `master` branch) cannot\n    be validated and, therefore, will not be checked.\n\n    The following options are available for the `check-links` task:\n\n    - `-Dapi.links`: Set this to `true` to check Javadoc API links hosted on\n      [docs.liferay.com](https://docs.liferay.com/).\n    - `-Dlegacy.links`: Set this to `true` to check legacy links already\n      published to LDN. This checks all links pointing to articles in the\n      current branch **and** all legacy links hosted on LDN.\n\n-   `dist-article-ce`: Creates a ZIP file for importing the specified article\n    (``-Darticle=...``), its images, and supporting structure to a Knowledge\n    Base.\n\n-   `dist-ce`: Packages all necessary CE resources into a ZIP file deployable to\n    LDN.\n\n-   `dist-dxp`: Packages all necessary DXP resources into a ZIP file deployable \n    to Liferay's Customer Portal.\n\n-   `number-headers`: Numbers all Markdown article headers with a unique header\n    ID. If there are any duplicate IDs, an error is thrown. See the\n    [Assigning Header IDs](#assigning-header-ids) and\n    [Markdown Header ID FAQ](#markdown-header-id-faq) sections for more\n    information.\n\n-   `number-images`: Numbers all Markdown articles' images in the folder. Image\n    numbers (e.g., *\\!\\[Figure x:* ) are replaced with the correct image\n    numbering if they are incorrect. See the\n    [Markdown Image Numbers Tool](#markdown-image-numbers-tool) section for more\n    information.\n\n## Tokens\n\nOur documentation is deployed to two separate sites to display CE and DXP\ndocumentation. Instead of having a completely separate folder structure for both\ndocs, we have all articles that are targeted for both CE and DXP residing in the\n`/articles` folder and DXP-only documentation residing in the `/articles-dxp`\nfolder. Because so many single articles are deployed to two separate sites,\nwe've created tokens that use one type of string when publishing to one site and\nanother different string for the second site. The available tokens are listed\nbelow:\n\n**CE Docs**\n\n- `@product@` = Liferay Portal\n- `@product-ver@` = Liferay Portal CE 7.1\n- `@commerce@` = Emporio\n- `@commerce-ver@` = Emporio 1.0\n- `@ide@` = Dev Studio CE\n- `@app-ref@` = https://docs.liferay.com/ce/apps\n- `@platform-ref@` = https://docs.liferay.com/ce/portal\n\n**DXP Docs**\n\n- `@product@` = Liferay DXP\n- `@product-ver@` = Liferay DXP 7.1\n- `@commerce@` = Liferay Commerce\n- `@commerce-ver@` = Liferay Commerce 1.0\n- `@ide@` = Dev Studio DXP\n- `@app-ref@` = https://docs.liferay.com/dxp/apps\n- `@platform-ref@` = https://docs.liferay.com/dxp/portal\n\n## Syntax Highlighting\n\nThe documentation site supports syntax highlighting. You can format your code\nby surrounding the snippet with three back ticks (e.g., \\`\\`\\`) and assigning it\nto a code alias. For example, a Java code snippet would look like this:\n\n    ```java\n    long fileEntryId = ParamUtil.getLong(actionRequest, \"fileEntryId\");\n    ```\n\nThe available syntax highlighting aliases are listed below:\n\n- ABAP: `abap`\n- Augmented Backus–Naur form: `abnf`\n- ActionScript: `actionscript`\n- Ada: `ada`\n- Apache Configuration: `apacheconf`\n- APL: `apl`\n- AppleScript: `applescript`\n- Arduino: `arduino`\n- ARFF: `arff`\n- AsciiDoc: `asciidoc`, `adoc`\n- 6502 Assembly: `asm6502`\n- ASP.NET (C#): `aspnet`\n- AutoHotkey: `autohotkey`\n- AutoIt: `autoit`\n- Bash: `bash`, `shell`\n- BASIC: `basic`\n- Batch: `batch`\n- Bison: `bison`\n- Backus–Naur form: `bnf`, `rbnf`\n- Brainfuck: `brainfuck`\n- Bro: `bro`\n- C: `c`\n- C#: `csharp`, `cs`, `dotnet`\n- C++: `cpp`\n- CIL: `cil`\n- CoffeeScript: `coffeescript`, `coffee`\n- CMake: `cmake`\n- Clojure: `clojure`\n- Crystal: `crystal`\n- Content-Security-Policy: `csp`\n- CSS Extras: `css-extras`\n- D: `d`\n- Dart: `dart`\n- Diff: `diff`\n- Django/Jinja2: `django`, `jinja2`\n- Docker: `docker`, `dockerfile`\n- Extended Backus–Naur form: `ebnf`\n- Eiffel: `eiffel`\n- EJS: `ejs`\n- Elixir: `elixir`\n- Elm: `elm`\n- ERB: `erb`\n- Erlang: `erlang`\n- F#: `fsharp`\n- Flow: `flow`\n- Fortran: `fortran`\n- G-code: `gcode`\n- GEDCOM: `gedcom`\n- Gherkin: `gherkin`\n- Git: `git`\n- GLSL: `glsl`\n- GameMaker Language: `gml`, `gamemakerlanguage`\n- Go: `go`\n- GraphQL: `graphql`\n- Groovy: `groovy`\n- Haml: `haml`\n- Handlebars: `handlebars`\n- Haskell: `haskell`, `hs`\n- Haxe: `haxe`\n- HCL: `hcl`\n- HTTP: `http`\n- HTTP Public-Key-Pins: `hpkp`\n- HTTP Strict-Transport-Security: `hsts`\n- IchigoJam: `ichigojam`\n- Icon: `icon`\n- Inform 7: `inform7`\n- Ini: `ini`\n- Io: `io`\n- J: `j`\n- Java: `java`\n- JavaDoc: `javadoc`\n- JavaDoc-like: `javadoclike`\n- Java stack trace: `javastacktrace`\n- Jolie: `jolie`\n- JQ: `jq`\n- JSDoc: `jsdoc`\n- JS Extras: `js-extras`\n- JSON: `json`\n- JSONP: `jsonp`\n- JSON5: `json5`\n- Julia: `julia`\n- Keyman: `keyman`\n- Kotlin: `kotlin`\n- LaTeX: `latex`, `tex`, `context`\n- Less: `less`\n- Liquid: `liquid`\n- Lisp: `lisp`, `emacs`, `elisp`, `emacs-lisp`\n- LiveScript: `livescript`\n- LOLCODE: `lolcode`\n- Lua: `lua`\n- Makefile: `makefile`\n- Markdown: `markdown`, `md`\n- Markup templating: `markup-templating`\n- MATLAB: `matlab`\n- MEL: `mel`\n- Mizar: `mizar`\n- Monkey: `monkey`\n- N1QL: `n1ql`\n- N4JS: `n4js`, `n4jsd`\n- Nand To Tetris HDL: `nand2tetris-hdl`\n- NASM: `nasm`\n- nginx: `nginx`\n- Nim: `nim`\n- Nix: `nix`\n- NSIS: `nsis`\n- Objective-C: `objectivec`\n- OCaml: `ocaml`\n- OpenCL: `opencl`\n- Oz: `oz`\n- PARI/GP: `parigp`\n- Parser: `parser`\n- Pascal: `pascal`, `objectpascal`\n- Perl: `perl`\n- PHP: `php`\n- PHPDoc: `phpdoc`\n- PHP Extras: `php-extras`\n- PL/SQL: `plsql`\n- PowerShell: `powershell`\n- Processing: `processing`\n- Prolog: `prolog`\n- .properties: `properties`\n- Protocol Buffers: `protobuf`\n- Pug: `pug`\n- Puppet: `puppet`\n- Pure: `pure`\n- Python: `python`, `py`\n- Q (kdb+ database): `q`\n- Qore: `qore`\n- R: `r`\n- React JSX: `jsx`\n- React TSX: `tsx`\n- Ren'py: `renpy`\n- Reason: `reason`\n- Regex: `regex`\n- reST (reStructuredText): `rest`\n- Rip: `rip`\n- Roboconf: `roboconf`\n- Ruby: `ruby`, `rb`\n- Rust: `rust`\n- SAS: `sas`\n- Sass (Sass): `sass`\n- Sass (Scss): `scss`\n- Scala: `scala`\n- Scheme: `scheme`\n- Shell session: `shell-session`\n- Smalltalk: `smalltalk`\n- Smarty: `smarty`\n- SQL: `sql`\n- Soy (Closure Template): `soy`\n- Stylus: `stylus`\n- Swift: `swift`\n- TAP: `tap`\n- Tcl: `tcl`\n- Textile: `textile`\n- TOML: `toml`\n- Template Toolkit 2: `tt2`\n- Twig: `twig`\n- TypeScript: `typescript`, `ts`\n- T4 Text Templates (C#): `t4-cs`, `t4`\n- T4 Text Templates (VB): `t4-vb`\n- T4 templating: `t4-templating`\n- Vala: `vala`\n- VB.Net: `vbnet`\n- Velocity: `velocity`\n- Verilog: `verilog`\n- VHDL: `vhdl`\n- vim: `vim`\n- Visual Basic: `visual-basic`, `vb`\n- WebAssembly: `wasm`\n- Wiki markup: `wiki`\n- Xeora: `xeora`, `xeoracube`\n- Xojo (REALbasic): `xojo`\n- XQuery: `xquery`\n- YAML: `yaml`, `yml`\n\n## Markdown Image Numbers Tool\n\nWe have a tool that you can call with Ant that numbers the images in a Markdown\nchapter file. While you're writing or editing a Markdown file, you can\njust number all the images in that chapter with a lowercase x. For example, your\nimage tags could take the following form:\n\n    ![Figure x: <image-description>](../../images/<image-name>)\n\nWhen you are finished working on a file, you can call the `number-images` Ant\ntask from the parent directory (e.g., `/develop/tutorials`) of the document you\nare working on:\n\n    ant number-images\n\nIf you're working in a DXP article located in `/articles-dxp`, run the\ncorresponding DXP Ant task:\n\n    ant number-images-dxp\n\nThe DXP task numbers images for articles in `/articles` and `/articles-dxp`.\n\n## Assigning Header IDs\n\nHeader IDs help to assure that each uploaded document's web contents have a\nunique URL. These IDs not only prevent documents from using the same URLs but\nthey also help to preserve web content URLs despite changes to header text in\nrevisions of the document.\n\nOnce you've finished creating headers for your article, number your headers\nusing the Ant task:\n\n    ant number-headers\n\nIf you're working in a DXP article located in `/articles-dxp`, run the\ncorresponding DXP Ant task:\n\n    ant number-headers-dxp\n\nIt will fail if header IDs conflict.\n\nExample - Output from header ID conflict\n\n    number-headers:\n        [java] Numbering headers for files in ../tutorials/articles ...\n        [java] Dup id:liferay-ide file:..\\tutorials\\articles\\100-tooling\\02-liferay-ide\\01-installing-liferay-ide.markdown line:1 (already used by file:..\\tutorials\\articles\\100-tooling\\02-liferay-ide\\00-liferay-ide-intro.markdown)\n        [java] Exception in thread \"main\" java.lang.Exception: FAILURE - Duplicate header IDs exist\n        [java]     at com.liferay.documentation.util.NumberHeadersSiteMain.main(Unknown Source)\n\n    BUILD FAILED\n\nTo resolve a conflict, you *must* be sure to preserve the header ID that existed\nfirst, if that header is a part of the *live* version of the document on\ndev.liferay.com. Then, remove the newer header ID from the other header and run\nthe Ant task again to produce a new unique ID for that header.\n\n## Markdown Tips\n\nBelow are some tips for some constructs that are unique to Liferay\ndocumentation.\n\n### Spaces vs. Tabs\n\nOur standard is the opposite of Liferay's code: we use spaces instead of tabs.\nWhy? Because lists and code blocks in Markdown are much easier to control using\nspaces instead of tabs. Please see the documentation for Markdown for further\ninformation on this, or we provide a good example of it when we talk about lists\nbelow.\n\n### Figures\n\nTo do figures, you should do it this way:\n\n    ![Figure 1: Logging into Liferay Portal is easy.](../../images/logging-into-liferay-portal.png)\n\nIf you're working in a DXP Markdown article, your image should be saved in the\n`/images-dxp` folder and the figure path should reflect that folder:\n\n    ![Figure 1: This diagram breaks down the evaluation process for the weather rule.](../../images-dxp/weather-rule-diagram.png)\n\nUsing this syntax for figure images causes Pandoc to create the following,\neasily styled markup:\n\n\t<div class=\"figure\">\n\t<img src=\"../../images/01-logging-into-liferay-portal.png\" alt=\"Figure 1.1: Logging into Liferay Portal\" />\n\t<p class=\"caption\">Figure 1.1: Logging into Liferay Portal</p>\n\t</div>\n\nWe've duplicated this behavior in the Pegdown parser that we've implemented.\n\n### Inline Images / Icon Images\n\nAn icon's image helps the reader identify the icon in the UI. To use an existing\nicon snapshot, check a document's `images/` folder for files ending in\n*-icon.png*. Follow these steps to include an icon image inline in your\narticle's Markdown text:\n\n1.  Take a snapshot of the icon, if one doesn't already exist in the document's\n    `images/` folder. Please save the snapshot to the `images/` folder and end\n    its name with `-icon.png`.\n2.  Crop the image to remove unrelated content from around the icon.\n3.  Resize the image's height to no greater than 20 pixels. **Important:** Make\n    sure to keep the aspect ratio.\n4. In the Markdown text, include the icon image in parentheses.\n\nInline icon image example in Markdown:\n\n    Click the *Add Blog Entry* icon (![Add Blog Entry](../../images/add-icon.png))\n    to bring up the blog entry editor.\n\nResult shown in a Knowledge Base article:\n\n![Inline icon image](images/render-icon-image-inline.png)\n\n### Right Arrows\n\nWe are in the habit of using right arrows to denote a series of things a user\ncan click on, such as Go To -> Control Panel -> Web Content. Open/LibreOffice\nwould automatically replace the dash and greater-than sign with a right arrow.\n\nWe can do the same in Markdown using the HTML code for this character, which is\n`&rarr;`. I created a SuperAbbrev in jEdit which transforms `rightarrow` into\n`&rarr;`.\n\n### Tables\n\nBecause Pegdown does not support the\n[Pandoc extension table syntax](http://johnmacfarlane.net/pandoc/README.html#tables),\nwe use a table syntax similar to\n[MultiMarkdown](http://fletcher.github.com/peg-multimarkdown/mmd-manual.pdf)\nthat supports the following features:\n\n* Cell content alignment (left, right, or center)\n* Cells containing links, code, images, and text that is plain, bold, italicized, double-quoted, or single-quoted\n* Cells containing strong text, emphasized text, double/single quotes, code, links, and images\n* Left alignment (default) with `:--- `\n* Right alignment with `: ---:`\n* Center alignment with `:---:`\n\nHere is MultiMarkdown-like source for an example table:\n\n    **Table Heading (outside of table)**\n\n\t  Column1 |  &nbsp;Column2 | &nbsp;Type  | &nbsp;Example  |\n\t--------- | :--------------| :---------: | -------------: |\n\t  foo     | bar            | strong       | **powerful** |\n\t  foo     | bar            | italics        | *emphasized* |\n\t  foo     | bar            | double quotes | \"Hey you!\" |\n\t  foo     | bar            | single quotes | 'yes' |\n\t  foo     | bar            | code          | `System.out.println()` |\n\t  foo     | bar            | link          | [Liferay.com](http://liferay.com) |\n\t  foo     | bar            | image         | ![tip](../../images/tip-pen-paper.png)|\n\nHere is the table rendered in Github ...\n\n**Table Heading (outside of table)**\n\n  Column1 |  &nbsp;Column2 | &nbsp;Type  | &nbsp;Example  |\n--------- | :--------------| :---------: | -------------: |\n  foo     | bar            | strong       | **powerful** |\n  foo     | bar            | italics        | *emphasized* |\n  foo     | bar            | double quotes | \"Hey you!\" |\n  foo     | bar            | single quotes | 'yes' |\n  foo     | bar            | code          | `System.out.println()` |\n  foo     | bar            | link          | [Liferay.com](http://liferay.com) |\n  foo     | bar            | image         | ![tip](./images/tip.png)|\n\nTable Limitations:\n\n* Grid tables (tables with grid lines) are not supported\n* Table captions are not supported\n* The period character ( '.') cannot be used in an alignment/divider line\n\nTable Syntax Requirements:\n\n* There must be at least one '|' character per line.\n* The *separator* line must contain only |, -, :, or space characters.\n* Cell content must be on one line only.\n* Columns are separated by the '|' character.\n\nTable Options\n\n* You can pad out cell text using non-breaking spaces (i.e. `&nbsp;`) to the\n  left and/or right of the cell text.\n* You can use a horizontal rule to help separate the end of the table from\n  paragraphs or tables that follow.\n\n---\n\n![important](./images/tip.png) **Important:** - Pandoc does not support\nMultiMarkdown table syntax. If you use Pandoc to build a document for test\npurposes, you'll notice that the table does not get converted as you would\nexpect. If you are using Pandoc to convert a document for a final product (e.g.\nePub), you'll need to temporarily change the table syntax to follow the Pandoc\nextension.\n\n![The example table converted using Pandoc](images/mmdTablePandocHTML.png)\n\n---\n\nNext, let's learn about creating sidebar text.\n\n\n### Sidebars\n\nSidebars appear frequently in our documentation. We place text in sidebars if we\nwant to draw special attention to it or if the text contains ancillary\ninformation that doesn't quite belong in the main text. For example, notes,\ntips, and warnings are often placed in sidebars. To create a sidebar, set off\nyour sidebar text with a begin sidebar token (`+$$$`) and an end sidebar token\n(`$$$`), like so:\n\n    +$$$\n\n    Your sidebar text goes here.\n\n    $$$\n\nSidebars are only rendered on [dev.liferay.com](https://dev.liferay.com). They\nare not rendered on Github.\n\nHere's what a sidebar on [dev.liferay.com](https://dev.liferay.com) looks like:\n\n![liferay-sidebar image](./images/ldn-sidebar.png)\n\nThere's no need to include images in your sidebars. Images for sidebars are\nhandled by Liferay's [dev.liferay.com](https://dev.liferay.com) theme.\n\n**Important:** Make sure that your sidebar tokens have empty lines above and\nbelow them so that they are parsed as independent paragraphs.\n\nLet's look at ordered lists, next.\n\n### Ordered Lists\n\nExplicitly number your lists like so ...\n\n    1. First step.\n    2. Second step.\n    3. Third step.\n\nList items (steps) can have multiple paragraphs, images, code blocks, etc. But\nall text blocks following a step's first paragraph, **must** be indented *4\nspaces* from the start of the step number. Otherwise, the continuous numbering\nis disrupted and the step that follows restarts at `1`.\n\n**Good steps (Markdown source) ...**\n\n    1. First step.\n\n        This paragraph supports step 1.\n\n    2. Second step.\n\n        Another paragraph and an image.\n\n        ![liferay-cube image](./images/liferay-cube.png)\n\n    3. Third step.\n\n        +$$$\n\n        **Note:** A sidebar note. Text is placed in sidebars if it deserves\n        special attention or if it contains ancillary information that doesn't\n        quite belong in the main text. For example, notes, tips, and warnings\n        are often placed in sidebars.\n\n        $$$\n\n    4. Finally! The fourth and final step. Code must be indented 4 spaces more.\n    Let's see a good code block ...\n\n            System.out.println(\"This code is mono-spaced\");\n\n**Resulting HTML from *Good steps***\n\n1. First step.\n\n    This paragraph supports step 1.\n\n2. Second step.\n\n    Another paragraph and an image.\n\n    ![liferay-cube image](./images/liferay-cube.png)\n\n3. Third step.\n\n    ![liferay-sidebar image](./images/ldn-sidebar.png)\n\n4. Finally! The fourth and final step. Code must be indented 4 spaces more.\nLet's see a good code block ...\n\n        System.out.println(\"This code is mono-spaced\");\n\n---\n\nAll is well in the *Good steps*. Let's consider what NOT to do by way of\nexample--the *Bad steps*.\n\n**Bad steps (Markdown source) ...**\n\n    1. First step.\n\n    This paragraph is not indented the full 4 spaces from the step number.\n\n    2. Second step.\n\n        Another paragraph and an image.\n\n    ![liferay-cube image](./images/liferay-cube.png)\n\n    3. Third step.\n\n    +$$$\n    **Note:** This note disrupts continuous numbering. It also won't be rendered\n    as a sidebar.\n    $$$\n\n    4. Finally! The fourth and final step. But the code is not monospace as it\n    needs to be indented 4 more spaces ...\n\n        System.out.println(\"Code should be mono-spaced\");\n\n**Resulting HTML from *Bad steps* ...**\n\n1. First step.\n\nThis paragraph is not indented 4 spaces from the step number.\n\n2. Second step.\n\n    Another paragraph and even the image and notes below are good.\n\n![liferay-cube image](./images/liferay-cube.png)\n\n3. Third step.\n\n+$$$\n**Note:** This sidebar note disrupts continuous numbering because it is not\nindented four spaces. It also is not rendered as a sidebar because there are no\nblank lines below the begin sidebar token or above the end sidebar token.\n$$$\n\n4. Finally! The fourth and final step. But the code is not monospace as it\nneeds to be indented 4 more spaces ...\n\n    System.out.println(\"Code should be mono-spaced\");\n\n---\n\nWell, there you have it--the do's and don'ts of ordered lists.\n\n**Important:** Before you send a pull request, view your Markdown file converted\nto HTML, using your editor's Pegdown converter or by viewing your document\nblob on Github. That way you can be sure any ordered lists you have, preserve\ntheir consinutous numbering.\n\n### Including Video Tutorials\n\nYou may need to include a video tutorial in a developer tutorial or User Guide \narticle. To display video tutorials in the markdown, an Administrator must first \nupload the video tutorial (in MP4 and WEBM formats) to the Documents and Media \nrepository on LDN. Videos are organized into two folders: `/Develop-videos` for \ndeveloper tutorials and `/User-admin-videos` for User Guide articles. The \nvideo's title should be the same for both formats, and include the `.mp4` || \n`.webm` extension in the document name, as shown in the configuration below:\n\n    getting-started-with-liferay-ide.mp4\n\nOnce the videos are uploaded, you can include them in the markdown. First \nadd a thumbnail of the video to the images folder. Thumbnails should be \n250px by 141px (to roughly match the 16x9 aspect ratio). Typically, thumbnails \nare title cards as shown in the figure below:\n\n![Liferay-video-thumbnail image](./images/liferay-video-thumbnail.png)\n\nAdd the following markup after the first paragraph to include the thumbnail:\n\n    <div class=\"video-link\">\n    <img src=\"../../../images/vid-ide-thumbnail.png\" alt=\"video-thumbnail\"/>\n    </div>\n\nThe `<div>` **must use the `video-link` class for the tutorial to render \nproperly.** Finally, you can include the video.\n\nWe use the HTML5 video tag to include multiple sources of our video. Add this \nmarkup to the bottom of the article, just before the *Related Topics* section:\n\n    <div class=\"video-tag\" data-name=\"Getting Started with Liferay IDE\">\n      <video width=\"100%\" height=\"100%\" controls>\n        <source src=\"https://dev.liferay.com/documents/10184/367132/getting-started-with-liferay-ide.mp4\" type=\"video/mp4\">\n        <source src=\"https://dev.liferay.com/documents/10184/367132/getting-started-with-liferay-ide.webm\" type=\"video/webm\">\n        Your browser does not support HTML5 video.\n      </video>\n    </div>\n\nThe wrapping `<div>` tag **must use the `video-tag` class and provide a \n`data-name` value**. The `data-name` attribute is used as the title for the \nvideo player. The `src` attributes point to the video formats. If one format \nisn't supported, the other will be delivered. If neither is supported, the user \nreceives the notification text.\n\nView the video by clicking the thumbnail on the right-side \n(or bottom on mobile) of the article.\n\n**Note:** WEBM format is not native to [Blender](https://www.blender.org/) \n(our video editor of choice), but you can convert an MP4 \n(which is native to Blender) to WEBM with [Handbrake](https://handbrake.fr/). \nSet the export to VP9 with an MKV container and Opus audio. \n\n### Markdown Metadata\n\nOur build process supports conversion from Markdown to html, odt, and epub\nformats using Pandoc. The Liferay Docs README describes the repository contents,\ndirectory structure, and conversion process. We concatenate the individual\nchapter files of a document and insert a markdown title block which pandoc\nparses as bibliographic information. See\n[http://johnmacfarlane.net/pandoc/README.html](http://johnmacfarlane.net/pandoc/README.html)\nfor details. The default title block in build.properties is empty:\n\n\t%\n\t%\n\t%\n\nYou can override the default title block by defining title, author, and date\nfields in your `build.<username>.properties` file. For example, the following\n`build.<username>.properties` entry\n\n\ttitle=Title\n\tauthor=Author(s) (separated by semicolons)\n\tdate=Date\n\nadds the following Markdown text to the beginning of the concatenated file:\n\n\t% Title\n\t% Author(s) (separated by semicolons)\n\t% Date\n\n## Markdown Header ID FAQ\n\nThis FAQ is provided to help answer questions and provide information on how and\nwhy we use IDs for the headers in the Markdown files of our official product\ndocumentation.\n\n### What are the header IDs and why are they important?\n\nHeader IDs were created for the purpose of preserving the URLs of our official\ndocumentation on Liferay.com. Previously, the URLs for our web content were\ndetermined by the heading text of our documents (e.g. the text from \"#\nIntroduction to Liferay Portal\" markdown was used to generate the URL final\nstring in the web content's URL\n[http://www.liferay.com/documentation/liferay-portal/6.1/user-guide/-/ai/introduction-to-liferay](http://www.liferay.com/documentation/liferay-portal/6.1/user-guide/-/ai/introduction-to-liferay)).\nIf the titles of the web content were changed, either by re-import of the\nmarkdown or manual edits via the GUI, the URLs changed too--breaking any links\nto the web content.\n\nIn response to this issue, Liferay Portal and the AssetImporter have been\nimproved so that web content can be referenced by static IDs. Regardless of\nwhether the titles of a web content change, its ID remains the same, preserving\nthe URL of that web content.\n\nThe header IDs are now specified in the markdown source code for our official\ndocuments.\n\nExample 1 - ID to an existing web content:\n\n    # Introduction to Liferay [](id=introduction-to-liferay)\n\nExample 2 - ID for a new section header:\n\n    ## Adding and Updating Assets [](id=adding-and-updating-assets)\n\nEach header, regardless of level, is to have a corresponding ID. This gives us\nthe flexibility to create web content for even the lowliest of subsections if we\nso choose. But no need to worry, those IDs get stripped out of the web content\non import to Liferay.com.\n\nThe naming convention for new headers very closely follows the existing header\ntitle. Uppercase letters are converted to lowercase and spaces are converted to\ndashes.\n\n### How should I specify an ID for a new header?\n\nExecute ant target `number-headers` or `number-headers-dxp` from your document\ndirectory (e.g., `/develop/tutorials`). You can also run `ant check` or `ant\ncheck-dxp` to generate header IDs and to various other checks and tasks.\n\n### What should I do with the ID for an existing header I've modified?\n\n**IMPORTANT:** Do not change the ID of an existing header.  If the header does\nnot have an existing header, then run the `number-headers` target on the\ndocument.\n\n### If I re-order sections or chapters, what do I do with their header IDs?\n\n**IMPORTANT:** Do not change the ID of an existing header. You can however, move\nthe header (along with its ID) around within an article or into a different\narticle.\n\n### If I want to update existing web content and find that its source is missing header IDs, what do I do?\n\nFirst, inform the document owner (e.g. Rich for the User Guide and Jim for the\nDev Guide).\n\nYou'll need to use the last portion of the web content's URL title as the ID for\nheader in the respective markdown source. After updating the IDs in the\nmarkdown, be sure to run ant target `number-headers` to detect any issues with\nthe headers.\n\n### How can I be sure that my header IDs will not be in conflict with other header IDs (e.g., IDs in other documents)?\n\nAnt target `number-headers` detects and reports any issues and/or conflicts\nbetween headers. It will fail if any issues are encountered.\n\nExample - Header ID conflict output\n\n\tnumber-headers:\n        [java] Numbering headers for files in ../tutorials/articles ...\n        [java] Dup id:liferay-ide file:..\\tutorials\\articles\\100-tooling\\02-liferay-ide\\01-installing-liferay-ide.markdown line:1 (already used by file:..\\tutorials\\articles\\100-tooling\\02-liferay-ide\\00-liferay-ide-intro.markdown)\n        [java] Exception in thread \"main\" java.lang.Exception: FAILURE - Duplicate header IDs exist\n        [java]     at com.liferay.documentation.util.NumberHeadersSiteMain.main(Unknown Source)\n\n    BUILD FAILED\n\nTo resolve the above conflict, the author *must* be sure to preserve the header\nID that existed first, if that header is a part of the *live* version of the\ndocument on Liferay.com. Then, the author should remove the newer header ID from\nthe other header and run `ant number-headers` to produce a new unique ID for\nthat header.\n\n### Will the header IDs show in the web content?\n\nNo, they will not show unless possibly there is a syntax error in the header ID\nused in your markdown source.\n\n### Are there safe-guards to prevent upload of documents that have missing or conflicting IDs?\n\nYes, the dependency targets (e.g. `number-headers`) executed by our distribution\ntargets, `dist-ce` and `dist-dxp`, fail and report errors if the documents are\nmissing IDs or have conflicting IDs.\n\n## Appendix: A Sample Markdown Environment\n\nFor liferay.com, we use Pegdown with our own, customized parser, which is\nincluded in this project. You can use this with our `convert.[sh|bat]` script in\nthe `bin` folder to preview your articles.\n\n### Editing Markdown Files\n\nMost programmers have a close relationship with their text editor of choice.\nThis is not an attempt to break that relationship in any way: Markdown is plain\ntext, and you should use whatever tool makes you most effective. One of the\nreasons we chose it is to allow writers to use whatever tools they want.\n\nFor those who are looking for some guidance on a good tool to use, you can use\njEdit. It's a great cross-platform text editor which is highly extensible.\nThough it's written in Java, it can be configured to start in the background\nwhen your machine starts, which makes it pop up as fast as any native editor.\nThis makes it ideal regardless of which platform (Linux, Windows, or Mac) you\nuse.\n\nFor Markdown, jEdit has a Markdown plugin that can render a Markdown document\ninto HTML, and there's also a syntax highlighting mode file that you can\ninstall. The Markdown plugin is available in jEdit's plugin manager, and the\nmode file can be downloaded from\n[https://github.com/peterlynch/jEdit-modes|Github](https://github.com/peterlynch/jEdit-modes) or [http://hasseg.org/blog/post/302/markdown-and-pod-syntax-highlighting-modes-for-jedit](http://hasseg.org/blog/post/302/markdown-and-pod-syntax-highlighting-modes-for-jedit).  \n\nTo install the mode file, copy it into your `.jedit/modes` folder, and edit the\n`catalog` file which is in the same folder. Add this line to the file, between\nthe <MODES> tags:\n\n    <MODE NAME=\"markdown\" FILE=\"markdown.xml\" FILE_NAME_GLOB=\"*.{markdown,md}\" />\n\nSave the file and restart jEdit. While editing, you now have syntax highlighting\nand can preview Markdown files in HTML using the plugin.\n\nIf you're going to be doing diffing and merging using jEdit's jdiff plugin,\nyou'll also need to set the width to be narrower than the 120 character default\nprovided by the Markdown mode file. To do this, go to *Utilities* -> *Global\nOptions* -> *Editing*. Under *Change Settings for Mode*, select *Markdown*, and\nthen change the *Wrap Margin* to 80. Of course, make sure also that *Word Wrap*\nis set to *Soft*.\n\nNow you've got a great environment for editing Markdown files.\n\nNext, you should look at our \n[writer's guidelines](03-writers-guidelines.markdown). \nThis is how we ensure a consistent writing style throughout the documentation. \n\n## Contact Information\n\nRich Sezov (sez11a), Jim Hinkey (jhinkey)\n"
  },
  {
    "path": "guidelines/03-writers-guidelines.markdown",
    "content": "# Liferay Documentation Writer's Guidelines\n\nNow that the documentation is on Github, Liferay and the community can\ncollaborate on documentation. To make this collaboration successful, it is\nimportant that all documentation be written and formatted consistently.\nOtherwise, time is wasted reformatting and wordsmithing the docs for\npublication--time that could be used to create new or improve existing\ndocumentation. \n\nFor these reasons, we ask that those contributing to the documentation adhere to\nthe following editorial conventions. Where there is no guideline below, it's\na good general rule to follow the [Chicago Manual of\nStyle](http://www.chicagomanualofstyle.org). \n\n## Introductions\n\nIntroductions are hard to write: let's get that out of the way right out of the\nbox. Having written many introductions, I feel for you. Sometimes it's better to\nskip the introduction and write the rest of the article and then come back to\nthe introduction when you're done. I get that. If it weren't hard, everybody\nwould do it, so if you're attempting to write one, kudos to you. Because it's\nhard, there are definitely common pitfalls to writing introductions. \n\nAvoid *Choose Your Own Adventure* introductions. If you're not as old as I am,\n[Choose Your Own Adventure](https://en.wikipedia.org/wiki/Choose_Your_Own_Adventure)\nbooks were short novels written in the second person (you), where the reader\nreads along and comes to decision points. For example, a Choose Your Own\nAdventure book might begin like this: \n\n    You were late coming home and really didn't want to be grounded. All your life\n    you've avoided cutting through the forest because of the stories of the beast\n    that lurks there under cover of darkness. But this time, you really don't have a\n    choice: if you get grounded this weekend, you'll miss the dance and your\n    girlfriend will dump you, and you can't let that happen. So with a deep breath,\n    you plunge down the trail into the forest. \n\n    It's actually not so bad. It's quiet and the path is well worn: obviously\n    somebody comes here regularly. But then you run into a problem: the path splits\n    ahead of you, with one path going right and another going left. Which path do\n    you choose? \n\n    If you choose the right path, go to page 100. If you choose the left path, go to\n    page 50. \n\nThis is a great writing style for this genre. The reader knows it's fiction and\nthe situation has been made up. It's horrible, however, for tutorial\nintroductions. In real life, nobody wants to have forecasted for them by\nsomebody who has no idea about them what their situation is. They don't want to\nbe made to feel bad because your introduction assumed they've been through all\nthe tutorials up to now and now they're ready for whatever you plan to tell\nthem. Here's an example of what not to do: \n\n    You've spent some time studying Service Builder (link) and you may even have\n    generated some services. Now you're ready for the next level! \n\nI call this a *Choose Your Own Adventure* introduction. Arrggh! I even had\ntrouble writing that. No, no, no. Don't ever do that; it's so annoying for the\nreader. You have no idea if the reader has spent time studying what you think is\na prerequisite or what his/her situation is at all. There's no reason to\nunnecessarily offend the reader in this way. \n\n## Hierarchical Design \n\nThe official documentation has a hierarchical design. This means that the text\nis divided up into small sections that have headings above them. This breaks up\nthe text and allows the reader to see more easily the overall organization of\nthe documentation. It has the added benefit of allowing the reader to skip to\nthe section he or she wants to read most (because we--even those of us who write\nit--all know that reading documentation is not necessarily everyone's favorite\ntask). \n\nSections should be kept as short as possible, by breaking them up further into\nsubsections. Markdown gives you six levels of headings (the same as HTML), which\nshould be sufficient for all your needs. \n\n## Tutorials \n\nTutorials should be no longer than 1000 words. If you have more material\nthan that, break it up. \n\n### Tutorial Intro \n\nA tutorial should begin with an unlabeled section that serves as your\nintroduction. You can use an anecdote, a story, or industry information to\nprovide your introduction. This introduction, however, must lead to a series of\nbullet points that summarize what the tutorial is about, so the reader can\nglance over the topics to see if the tutorial covers what he/she wants. \n\nDon't end your intro with the bullet points themselves; always provide some\nsummary text after the bullet points. \n\n### Tutorial Body \n\nThe body of the tutorial contains a set of multiple, hierarchical sections. See\nbelow for information on sections. \n\n### Tutorial Summary \n\nThe end of each chapter should contain a summary section that briefly recounts\nwhat was covered in the tutorial. \n\n## Sections \n\nIf you're a programmer, think of sections like linked lists. While each section\nneeds to cover its own material, at the end of a section you should point to\nwhat's coming next. This is called a *segue*, and it generally sounds like this: \n\nYou can see from this how easy it is to do. Next, we'll look at this other\nthing. \n\nSegues are discussed in more detail below. \n\nNever end a section with an image. \n\nNever end a section with code.\n\n### Section Headings and Subheadings \n\nHeadings and Subheadings should follow an outline format. This means that if\nthey're in a hierarchy, there should always be at least two children: otherwise,\nyou don't have a true hierarchy. \n\nBecause of the way Markdown is often parsed, header lines should never break. If\nyou have set your text editor to break lines automatically, and you have a long\nheader, make sure your header does not break. \n\n### Segues\n\nUse segues to introduce new headings and to sum up completed ones. For example: \n\n*...that's how you configure Liferay. Next, we'll look at what it takes to make\nit sing a song. *\n\n*Singing a Song*\n\nYou'd think that making Liferay sing would be hard, but it is in fact easy. All\nyou have to do....\n\n## Text Conventions \n\nThere are several text conventions that help to ensure a consistent\ndocumentation style. Please follow these conventions when entering\ndocumentation. \n\n### Code in Text \n\nProgrammers love to put stuff in quotes when they're doing documentation,\nprobably because it's similar to code. Code looks like this: \n\n\tString myString = \"Hello there!\";\n\nConsequently, many programmers therefore write documentation that looks like\nthis: \n\n    # Set this property to false for easier debugging for development. You can\n    # also disable fast loading by setting the URL parameter \"css_fast_load\" to\n    # \"0\".   \n\n*Please do not do that for documentation.* Use a code style instead. In\nMarkdown, this is done by placing your code inside a left accent character,\nwhich on US keyboards is to the left of the number 1 key, like this: \n\n\t`my code is here`\n\nWe are not writing code; we are writing instructions for human beings to be able\nto write code or use our software. Obviously, if you are documenting something\nin a text file in the product itself, you can't do this, and the example above\nwas taken from Liferay's `portal.properties` file. \n\nThis text was reformatted in the documentation so that it looks like this: \n\nSet this property to `false` for easier debugging for development. You can also\ndisable fast loading by setting the URL parameter `css_fast_load` to `0`. \n\nAlways set off code so that it is clear it's code and not text. This makes it so\nthat people can easily copy/paste your code from the documentation. Of course,\nmake sure your code works. \n\n### File Names \n\nWhen you need to refer to a file name, put the file name in a code font as well. \n\n**Example:** Use the Software Catalog portlet if you have multiple users\nsubmitting portlets into the repository, and if you don't want to worry about\ncreating the `liferay-plugin-repository.xml` file yourself. \n\n### Quotation Marks \n\nIt's much better to set off properties, classes, or code blocks with a code\nstyle instead of quotation marks. This has two benefits: the text is marked so\nthat people pay more attention to it, and there is no ambiguity as to whether or\nnot to include the quotation marks when copying and pasting these values. \n\nProgrammers are used to writing code, not prose. Because of this, programmers\nlike to put periods, commas, and other punctuation outside the quotation marks,\nwhich is incorrect punctuation---but good programming syntax. If you do need to\nuse quotes for something, please remember the punctuation goes *inside* the\nquotes. \n\n### Italics \n\nBelow are some guidelines for when to use italics. \n\n#### Introducing Concepts \n\nIf you're introducing a concept, italicize the concept the first time you use\nit. Thereafter, you don't need the italics. \n\n**Example:** One of the primary ways of extending the functionality of Liferay\nPortal is by the use of *plugins*. Plugins are an umbrella term for installable\n*portlet*, *theme*, *layout template*, and *web module* Java EE .war files.\nThough Liferay comes bundled with a number of functional portlets, themes,\nlayout templates, and web modules, plugins provide a means of extending Liferay\nto be able to do almost anything. \n\n\nNotice the italics on *plugins*, *portlet*, *theme*, *layout template*, and *web\nmodule* the first time the terms are used, but none when the terms are used\nafter they are introduced.\n\n#### UI Elements \n\nWhen you're telling the user to click on something in the UI, italicize it. \n\n**Example:** Click the *Save* button to continue. \n\nIf you're referring to a UI element, but there's no direction for the user,\ncapitalize it, but *don't* italicize it. \n\n**Example:** After clicking the button, the Configuration page appears. \n\nTo provide as much clarity on UI elements as possible, let's combine the above\ntwo rules: \n\n**Example:** On the Configuration page, click the *Add* button. \n\nAs you can see, the UI element that contains direction for the user is\nitalicized, but the UI element that has no direction is not. \n\n### Bold \n\nBold is used sparingly in the documentation. Why is this? Because too much bold\nis distracting. The reader's eye is drawn to bold text more than to anything\nelse. All the headings are bold anyway, so we don't want to use it very much in\nthe body text itself. There is one place where we want to use bold: field Lists. \n\nWhen explaining a form that users can fill out, use bold for the field names. \n\n**Example:**\n\n**Name:** Enter the user's name. \n\n**Address:** Enter the user's address. \n\nNever end a section with a list of form elements, like I was about to do before\nI typed this sentence. \n\n### Bullet Points \n\nBullet points are strange animals. They can be lists of things. They can be\nlists of arguments. Here's a good rule for bullet points: if it's a sentence,\nuse a period. If it's not a sentence, don't. \n\n**Examples:** Don't use a period for bullets that are not sentences, like these: \n\n*  Shoes\n*  Pants\n*  Shirt\n\nUse a period for bullets that are sentences, like these: \n\n*  Roll your mouse over the Dock and click *Sign In*. \n*  Enter your email address and password.\n\nNever end a section with bullet points, like I was about to do before I typed\nthis sentence.\n\n### Trailing Spaces \n\nThe Markdown source files for the documentation should be clean. Every sentence,\nregardless of whether it ends a paragraph or not, should end with one---and only\none---trailing space. This is so that paragraphs can be combined more easily\nduring the editing process, when this becomes necessary. \n\nThere should never be more than one trailing space after a sentence, paragraph,\nor bullet point. \n\n### Spaces vs. Tabs \n\nUnlike the Liferay source code, Liferay documentation should have no tabs in it.\nThis is so that you can more easily differentiate various constructs within\nMarkdown. For example, preformatted text is denoted in Markdown by either one\ntab or four spaces. List levels are denoted by indenting by spaces. If you use\nspaces instead of tabs, you have three list levels to work with before your\nsyntax-highlighting text editor mistakes your outline levels for preformatted\ntext. \n\n### Variable Text (brackets) \n\nSometimes, to avoid putting specific information (like version numbers) in the\ntext, you want to specify that as a \"variable\" of sorts. When you do this,\n*always use straight brackets*, like this: \n\n    Inside the bundle folder, you'll find a `tomcat-[version]` folder. \n\nWhy straight brackets? To avoid confusion with markup languages and also to\navoid accidentally triggering the Markdown parser to pass the text through\n(because it might think it's HTML). \n\n## Images and Captions \n\nEvery image should have a caption, and the caption should be at least one\ndescriptive, complete sentence. In other words, captions that are simple labels\naren't enough. DON'T do something like this for a caption: \n\n*The Liferay setup wizard.*\n\nInstead, do this: \n\n*The Liferay setup wizard makes connecting to your database and configuring a\ndefault administrator easy.*\n\nPicture yourself taking the reader by the hand and leading him or her through\nthe concept you're describing. All of these tips are designed to help you do\nthat.\n\nImages should be in a web-friendly format (.png or .jpg). If you've created a\ndrawing in a vector format (.svg or other), save it in the `drawings` folder for\nthe guide you're working on and save a converted .png or .jpg in the `images`\nfolder. \n\nImage file names should all be lower case, with no spaces in the file names. Use\ndashes (-) to replace spaces in multi-word file names. \n\n## Capitalization \n\nSite: Capitalized when referring to Liferay Sites. When using the term\ngenerically, no capital is needed. \n\nSite Administration: Always Site Administration\n\nPersonal Site: Always Personal Site\n\nRole: Always Role when referring to Liferay Roles. \n\nUser: Always User when referring to Liferay Users. \n\nUser Group: Always User Group\n\nOrganization: Always Organization\n\nControl Menu: Always Control Menu\n\n## Common Documentation and Phraseology Issues \n\nExplain every option, no matter how redundant and stupid it seems. Yes, most\nforms in the product ask for a name and a description, but you must explain them\nanyway. \n\nNever, ever, ever say something is \"self-explanatory.\" It isn't. All you're\ndoing is insulting the reader who doesn't understand the same things you\nunderstand.\n\nRemove the future tense from your documenting vocabulary. When you click on\nsomething, something else *appears*. You don't want to say it will appear, and\nyou especially don't want to say you *are going to* do something else after it\nappears. \n\nNever say something is simple or straightforward. If it were, the reader\nwouldn't be reading documentation on it. \n\n## Know your audience! \n\nYou have at least two types of people to whom you are writing: the person who\nreads the docs cover to cover (rare), and the person who clicks through trying\nto find a specific section that has the information she wants (more common). \n\nIt's better to write for this second type of reader. Many features in the\nproduct overlap (message board threads can be tagged; wiki articles can be\ntagged; blogs can be tagged, etc.), but we can't assume readers have read\neverything. For this reason, don't say things like \"Tagging wiki articles works\nthe same way it does for web content,\" and leave it there. If you say that, use\nit only by way of introduction to how you'll tell readers exactly how to tag\nwiki articles. You can then point readers to the specific place where tagging in\ngeneral is explained. \n\nWith that in mind, another thing you want to avoid is *mind reading*. It's easy\nto fall into making statements like, \"You probably aren't interested in blah\nblah now, but you will be once you finish this section.\" As the writer, you\nactually have no idea what your readers might be thinking, so avoid mind\nreading. \n\nThe Golden Rule says \"Do unto others as you would have them do unto you.\" The\ndoc version is write something you'd want to read. Include all the details you\nwould want if you were newly approaching the topic. \n\n## Be Creative! \n\nUse jokes. Add anecdotes. Write stories. Do whatever you can to keep the text\nfrom being regular, old, dry, boring documentation. One of our authors (Stephen\nKostas) came up with Nosester: a social network for your nose. That's awesome.\nI (Rich Sezov) wrote a story as the intro and summary of the Audit section. Do\nthis sort of thing. It surprises people, makes the text more interesting to\nread, and generally creates goodwill. We don't want Liferay's docs to sound like\neverybody else's. Let's be recognized as much for our cool documentation as for\nour cool product.\n\n## Common Grammatical Mistakes \n\nWe all make grammatical errors sometimes. I'm sure that even J.R.R. Tolkien had\nto correct some grammatical errors before publishing *Lord of the Rings*. What\nseparates the men from the boys (or women from the girls) here, though, is\nwhether or not you are willing to look at your text a second, third, or fourth\ntime and find those grammatical errors to which you are prone. You can then try\nto be more aware of those errors as you write and thereby avoid them. It also\nhelps to read your text after you have written it. This sounds like common\nsense, but you'd be surprised at how many people just write something and send\nit off like it's email. To get good prose, you have to go over it several times. \n\nWhat follows is a list of common grammatical mistakes.\n\n### It's and Its \n\nThis one is easier than it looks, even though most people give up and always use\n*it's*. Why they choose the one with the apostrophe is beyond me, since you have\nto type an extra character. \n\n**It's:** This is short for \"it is.\" So when you're about to write an *its* or\nan *it's*, ask yourself: do I mean \"it is\" here? If so, use the single quote. If\nnot, see below. \n\n**Its**: This is always possessive. *The cat blinked its eyes.* \n\n### Subject Agreement \n\nYour subjects' references must always agree in a sentence. You can't have one be\nsingular and another be plural. For example, you never say, \"If you configure\nthis permission for the user, they cannot access the resource.\" You have to say,\n\"If you configure this permission for the user, he or she cannot access the\nresource.\" Since you're talking about a single user, it's either *he* or *she*\n(or both), but not *they*. Some people, to try to be fair to the genders,\nalternate genders as they go (\"If you configure this permission for the user,\nshe cannot access the resource.\").  That's also correct. Others try to make the\nsentence plural (\"If you configure this permission for users, they cannot access\nthe resource.\"). Again, this is correct. \n\n### Notes on Commas \n\nCommas have several elements which can be confusing. Here are some tips to help\nyou be successful with commas. \n\n#### Oxford Commas\n\nWe are an oxford comma shop. I don't want to hear any arguments about it,\nrequests to review this decision, or examples of other organizations that don't\nuse it. The decision has been made, and it's not changing. \n\nFor those who don't know what an oxford comma is, it's a standard for using\ncommas in a series. If you have a series of three or more things, there is\ndisagreement in the English language writing community on whether the comma\nshould be included for the last item in the series. Excluding the\ncomma can lead to a lack of clarity, as illustrated in this example: \n\n    I want to thank my mentors, my parents and the President for their help. \n\nAre your mentors really your parents and the President? Or did you really mean\nthis: \n\n    I want to thank my mentors, my parents, and the President for their help. \n\nThe oxford comma makes the sentence clear. \n\n#### Conjunctions vs. Compound Verbs \n\nYou only use a comma after a conjunction (i.e., \"and,\" \"or,\" \"but,\" etc.) if it\nis separating two independent clauses. If you have a compound verb, you don't\nneed a comma. So you'd use a comma with \"Click on the button, and the portal\nrefreshes.\" But you wouldn't use a comma with \"Select option three and click\nSave.\" In the first instance, you have two independent clauses; in the second,\nyou have a compound verb (\"select and click\"). One easy way to tell which one\nyou're dealing with is to find the subject(s). If you have two different\nsubjects, you have two independent clauses. If you have one subject sharing two\nverbs, you probably have a compound verb. \n\n\nSo in our first example, our two subjects were *you* and *portal* (the you is\nunderstood as (You) click on the button...). So we need a comma there, because\nwe're separating two independent clauses. \n\nIn our second example, we had one subject: *you*. The subject of the sentence is\ndoing two things: selecting and clicking. We therefore have a compound verb and\ndon't need a comma. \n\n#### Comma Splices, Semicolons, and Em Dashes \n\nMost people I've talked to have no idea what to do with a semicolon (;). Java\nprogrammers, of course, know what to do with it: you end a statement with it!\n(That's a poor attempt at humor.) Seriously, though, many people wind up using a\ncomma in a place where you should use a semicolon, and that is when you're\nseparating two independent clauses (i.e., sentences). Consider the following\nexample: \n\n*Don't try doing it all yourself, it's better to work as a team.* \n\nThat's a comma splice. You're splicing together two complete sentences with a\ncomma: \n\n*Don't try doing it all yourself. It's better to work as a team.* \n\nWhile a comma splice is incorrect punctuation, using a semicolon in the same\nplace is a *correct* drop-in replacement for a comma splice, as a semicolon's\npurpose in life is to separate two independent clauses: \n\n*Don't try doing it all yourself; it's better to work as a team.*\n\nIf you write it this way, your punctuation is correct, and you have just become\none of the few who know how to use a semicolon! \n\nIt's also important to note that the em-dash (---) can also be used in this way: \n\n*Don't try doing it all yourself---it's better to work as a team.*\n\nEm-dashes have no spaces around them and always consist of *three* dash\ncharacters in Markdown. Our Markdown parser converts that into a proper em-dash\ncharacter, but it doesn't convert any of the other variants. \n\n## Terminology\n\nBelow is terminology and how we use it. \n\n**Folder:** Always use folder. Never use directory, directory structure, etc. \n\n**Command line:** Never use Terminal or Command Prompt. We're trying to stay\nOS-agnostic here, people. \n\n**Script:** Always use script; never batch file, batch, etc. And always default\nto the Unix/Mac/Linux way of doing things (`./catalina.sh`, not `catalina` or\n`catalina.bat`) because those environments are used more often by Liferay\ndevelopers and administrators. \n\n## Summary\n\nThese guidelines help us to ensure consistency and clarity within the\ndocumentation. All contributors should be familiar with them and do their best\nto adhere to them before sending a pull request to have their text included in\nour documentation. They are evolving over time, so you may want to review them\nfor changes if you haven't looked at them in a while. \n\nDo you still have questions? Our [FAQ](04-faq.markdown) can point you in the\nright direction. \n\n"
  },
  {
    "path": "guidelines/04-faq.markdown",
    "content": "# FAQ\n\n**Table of Contents**\n\n* [How do I submit a change to an article?](#how-do-i-submit-a-change-to-an-article)\n* [How do I request a new article or a fix to an existing article?](#how-do-i-request-a-new-article-or-a-fix-to-an-existing-article)\n* [How do I use Git and GitHub?](#how-do-i-use-git-and-github)\n* [How do I write and submit my own article?](#how-do-i-write-and-submit-my-own-article)\n* [How do I convert my local Markdown to HTML using the provided convert scripts?](#how-do-i-convert-my-local-markdown-to-html-using-the-provided-convert-scripts)\n\n## How do I submit a change to an article?\n\nSubmitting changes to an article on LDN is straightforward. At the top \nright of each article on LDN is a green *Edit on GitHub* button. Click it:\n\n![Figure 1: Each LDN article has an *Edit on GitHub* button.](images/readme-edit-on-github.png)\n \nYou're then taken to that article on GitHub. To edit it, click the pencil icon \nat the top right of the article. This forks the project and opens up an editor \nin your browser so you can make changes to the file. Make your changes in the \neditor and then put in a title and a description of the changes in the \n*Propose file change* section at the bottom of the screen. When youre finished, \nclick the *Propose file change* button. This takes you to a screen where you can \nsubmit a pull request with your changes.\n\n## How do I request a new article or a fix to an existing article?\n\nTo request new articles or fixes, create a ticket in the JIRA project \n[Liferay Documentation](https://issues.liferay.com/browse/LRDOCS/?selectedTab=com.atlassian.jira.jira-projects-plugin:summary-panel). \nTo create a ticket, click the blue *Create* button at the top of the JIRA page:\n \n![Figure 2: Click the *Create* button to create a new ticket in the Liferay Documentation Project.](images/readme-new-ticket.png)\n \nThis launches the *Create Issue* wizard, which walks you through the steps of \ncreating a new ticket. First, make sure that *PUBLIC - Liferay Documentation* is \nselected in the *Project* menu. Then select the issue type from the *Issue Type* \nmenu. Select *Bug* if you're reporting incorrect information in an existing \narticle. For a new article or addition to an existing article, select \n*Documentation*. Then click *Next*.\n \n![Figure 3: The *Create Issue* wizard guides you in logging your request.](images/readme-new-ticket-info.png)\n\nIn the second step of the *Create Issue* wizard, enter a brief summary of the \nchange or addition. Then select the component type from the *Component/s* menu. \nThe component indicates the type of article your request is for. For example, if \nyou're requesting a change or addition to an LDN tutorial, select *Tutorial*. \nSimilarly, select *Learning Path* if you're requesting a change to an LDN \nLearning Path article. Next, if you're submitting a bug, select the version that \nyour request applies to from the *Affects Version/s* menu. Last but certainly \nnot least, enter a more detailed description of your request in the \n*Description* field. Click *Create* when you're finished.\n \n## How do I use Git and GitHub?\n\nTo use Git and GitHub, you first need to create an account on\n[github.com](https://github.com/). Then read from the following GitHub\ntutorials: \n \n* [Set up Git](https://help.github.com/articles/set-up-git/)\n* [Fork a Repo](https://help.github.com/articles/fork-a-repo/)\n* [Committing Your Changes](http://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository#Committing-Your-Changes)\n* [Using Pull Requests](https://help.github.com/articles/using-pull-requests/)\n* [Be Social](https://help.github.com/articles/be-social/)\n\n## How do I write and submit my own article?\n \nFirst, you should read \n[Creating Docs for Liferay](01-creating-docs-for-liferay.markdown), \n[Standards and Customizations](02-standards-and-customizations.markdown), and\n[Writer's Guidelines](03-writers-guidelines.markdown). These articles describe\nthe tools and writing style that you should use. The structure of your article\nalso depends on what kind of article it is.  If you're writing an LDN developer\ntutorial, you should structure it according to the [tutorial\ntemplate](../develop/tutorial-template.markdown). If you're writing an LDN user\narticle, you should structure it similarly to the \n[articles in the Discover section of LDN](../discover/portal/articles/01-what-is-liferay/01-building-site-with-liferay-web-content.markdown). \nTo submit an article, you must first fork the liferay-docs repository on \nGitHub. Then create a new branch for your article on your forked repository. \nIf your article is a tutorial, place it in the\n[develop/new-articles](../develop/new-articles) folder. If your article is a user\narticle, place it in the\n[discover/new-articles](../discover/new-articles) folder. You should also place any \nimages your article uses in those folders along with it. Once you commit and \npush your changes to your forked liferay-docs repository, submit a pull\nrequest to the default user `liferay`. Alternatively, you can write and submit\nyour article using the web interface on GitHub. Just navigate to the appropriate\n`new-articles` folder for your article type and then click the plus icon in the\nfile path: \n \n![Figure 4: On GitHub, you can click the *plus* icon to add a new file in the file path.](images/readme-new-file-github.png)\n \nThis forks the repository and brings up the web editor where you can name the \nnew file and compose your article. When you're done, enter a description in the \n*Propose new file* section at the bottom of the screen and then click the \n*Propose new file* button.\n \n## How do I convert my local Markdown to HTML using the provided convert scripts?\n\nThere's a Batch and Shell convert script provided so you can use this tool on \nany operating system. These files are located in the [`liferay-docs/bin`](../bin/)\nfolder. To run the convert script, use the following command:\n    \n    convert.bat|sh [Markdown file to convert] [HTML file to be written]\n \nFirst specify the path for either the `convert.bat` or `convert.sh` script. The\nscript's first parameter must be the Markdown file path. The second\nparameter (optional) lets you specify the path of an HTML file to generate.\nAs a working example, the following command would convert an article found in\nthe `discover/new-articles` folder:\n\n    FILE_PATH/convert.bat REPO_PATH/liferay-docs/discover/new-articles/intro.markdown\n\nBy running this command without specifying an HTML file path, an HTML file named\nafter the Markdown file (but having a `.html` suffix) is generated in the\nMarkdown file's folder. When committing new/modified articles, make sure not to\ncommit generated HTML files. It's also important to note that you can run the\nscript from any directory, as long as you provide the script's file path. \n\n"
  },
  {
    "path": "ja/build.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<project name=\"Language Folder - Japanese\" basedir=\".\">\n\t<property name=\"language.dir\" value=\"ja\" />\n\n\t<import file=\"../build-common.xml\" />\n\n</project>"
  },
  {
    "path": "release-site.properties",
    "content": "##\n## Markdown Metadata\n##\n\n    product.abbrev=lp\n    product.community=ce\n    product.enterprise=dxp\n    product.version=7.2\n\n##\n## Tokens\n##\n\n    product.name=Liferay Portal\n    product.name.enterprise=Liferay DXP\n    product.token=product\n\n    product.name.version=Liferay Portal CE 7.2\n    product.name.enterprise.version=Liferay DXP 7.2\n    product.token.version=product-ver\n\n    product.commerce.name=Liferay Commerce\n    product.commerce.name.enterprise=Liferay Commerce\n    product.token.commerce=commerce\n\n    product.commerce.name.version=Liferay Commerce 1.1\n    product.commerce.name.enterprise.version=Liferay Commerce 1.1\n    product.token.commerce.version=commerce-ver\n\n    product.ide.name=Dev Studio CE\n    product.ide.name.enterprise=Dev Studio DXP\n    product.token.ide=ide\n\n    product.app.reference=https://docs.liferay.com/ce/apps\n    product.app.enterprise.reference=https://docs.liferay.com/dxp/apps\n    product.token.app.reference=app-ref\n\n    product.platform.reference=https://docs.liferay.com/ce/portal\n    product.platform.enterprise.reference=https://docs.liferay.com/dxp/portal\n    product.token.platform.reference=platform-ref\n"
  }
]